Update sdk/platform-tools to version 26.0.0.

Command used to update:
rm -rf sdk/platform-tools &&
  wget https://dl.google.com/android/repository/platform-tools-latest-linux.zip &&
  unzip platform-tools-latest-linux.zip -d sdk &&
  rm platform-tools-latest-linux.zip && git add .

Bug: 735481
Change-Id: I7e14e2ca832d73a0613fb5d51d1442247618f584
Reviewed-on: https://chromium-review.googlesource.com/615163
Reviewed-by: John Budorick <jbudorick@chromium.org>
diff --git a/README.chromium b/README.chromium
index 50c8317..a821a71 100644
--- a/README.chromium
+++ b/README.chromium
@@ -3,7 +3,7 @@
 Versions:
   Android SDK Platform API 26
   Android SDK Tools 26.0.2
-  Android SDK Platform-tools 25.0.3
+  Android SDK Platform-tools 26.0.0
   Android SDK Build-tools 26.0.0
   Android SDK Sources 25
   Android Support Library 23.2.1
diff --git a/sdk/platform-tools/NOTICE.txt b/sdk/platform-tools/NOTICE.txt
index 2ca9533..114b172 100644
--- a/sdk/platform-tools/NOTICE.txt
+++ b/sdk/platform-tools/NOTICE.txt
@@ -1,6 +1,29 @@
 Notices for files contained in the tools directory:
 ============================================================
 Notices for file(s):
+/lib64/libtinyxml2.so
+------------------------------------------------------------
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any
+damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any
+purpose, including commercial applications, and to alter it and
+redistribute it freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must
+not claim that you wrote the original software. If you use this
+software in a product, an acknowledgment in the product documentation
+would be appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and
+must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source
+distribution.
+
+============================================================
+Notices for file(s):
 /lib/libfdlibm.a
 /lib64/libfdlibm.a
 ------------------------------------------------------------
@@ -44,40 +67,8 @@
 
 ============================================================
 Notices for file(s):
-/lib/libexpat-host.so
-/lib/libexpat.a
-/lib64/libexpat-host.so
-/lib64/libexpat.a
-------------------------------------------------------------
-Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd
-                               and Clark Cooper
-Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Expat maintainers.
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be included
-in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-============================================================
-Notices for file(s):
 /lib/libcompiler_rt-extras.a
-/lib/libcompiler_rt.a
 /lib64/libcompiler_rt-extras.a
-/lib64/libcompiler_rt.a
 /lib64/libcompiler_rt.so
 ------------------------------------------------------------
 ==============================================================================
@@ -381,333 +372,6 @@
 
 ============================================================
 Notices for file(s):
-/lib64/libbcc.so
-------------------------------------------------------------
-==========================
-NOTICE file for libbcc.git
-==========================
-
-* NOTICE for lib/ExecutionEngine/, lib/ScriptCRT/, include and helper/.
-
-   Copyright (c) 2005-2011, The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-
-
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
-
-
-* NOTICE for runtime/ and lib/CodeGen/.
-  Note: The NOTICE is the same for another git project, external/llvm.git.
-
-==============================================================================
-LLVM Release License
-==============================================================================
-University of Illinois/NCSA
-Open Source License
-
-Copyright (c) 2003-2011 University of Illinois at Urbana-Champaign.
-All rights reserved.
-
-Developed by:
-
-    LLVM Team
-
-    University of Illinois at Urbana-Champaign
-
-    http://llvm.org
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal with
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-of the Software, and to permit persons to whom the Software is furnished to do
-so, subject to the following conditions:
-
-    * Redistributions of source code must retain the above copyright notice,
-      this list of conditions and the following disclaimers.
-
-    * Redistributions in binary form must reproduce the above copyright notice,
-      this list of conditions and the following disclaimers in the
-      documentation and/or other materials provided with the distribution.
-
-    * Neither the names of the LLVM Team, University of Illinois at
-      Urbana-Champaign, nor the names of its contributors may be used to
-      endorse or promote products derived from this Software without specific
-      prior written permission.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
-FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
-CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
-SOFTWARE.
-
-==============================================================================
-Copyrights and Licenses for Third Party Software Distributed with LLVM:
-==============================================================================
-The LLVM software contains code written by third parties.  Such software will
-have its own individual LICENSE.TXT file in the directory in which it appears.
-This file will describe the copyrights, license, and restrictions which apply
-to that code.
-
-The disclaimer of warranty in the University of Illinois Open Source License
-applies to all code in the LLVM Distribution, and nothing in any of the
-other licenses gives permission to use the names of the LLVM Team or the
-University of Illinois to endorse or promote products derived from this
-Software.
-
-The following pieces of software have additional or alternate copyrights,
-licenses, and/or restrictions:
-
-Program             Directory
--------             ---------
-Autoconf            llvm/autoconf
-                    llvm/projects/ModuleMaker/autoconf
-                    llvm/projects/sample/autoconf
-CellSPU backend     llvm/lib/Target/CellSPU/README.txt
-Google Test         llvm/utils/unittest/googletest
-OpenBSD regex       llvm/lib/Support/{reg*, COPYRIGHT.regex}
-
-
-
-* NOTICE for tests/disassem.cpp and tests/disassem.h.
-
-/*      $NetBSD: disassem.c,v 1.14 2003/03/27 16:58:36 mycroft Exp $    */
-
-/*-
- * Copyright (c) 1996 Mark Brinicombe.
- * Copyright (c) 1996 Brini.
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *      This product includes software developed by Brini.
- * 4. The name of the company nor the name of the author may be used to
- *    endorse or promote products derived from this software without specific
- *    prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL BRINI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
- * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * RiscBSD kernel project
- *
- * db_disasm.c
- *
- * Kernel disassembler
- *
- * Created      : 10/02/96
- *
- * Structured after the sparc/sparc/db_disasm.c by David S. Miller &
- * Paul Kranenburg
- *
- * This code is not complete. Not all instructions are disassembled.
- */
-
-============================================================
-Notices for file(s):
 /lib/liblog.a
 /lib/liblog.so
 /lib64/liblog.a
@@ -906,6 +570,170 @@
 
 ============================================================
 Notices for file(s):
+/bin/v8mkpeephole
+/lib/libv8base.a
+/lib64/libv8base.a
+------------------------------------------------------------
+This license applies to all parts of V8 that are not externally
+maintained libraries.  The externally maintained libraries used by V8
+are:
+
+  - PCRE test suite, located in
+    test/mjsunit/third_party/regexp-pcre/regexp-pcre.js.  This is based on the
+    test suite from PCRE-7.3, which is copyrighted by the University
+    of Cambridge and Google, Inc.  The copyright notice and license
+    are embedded in regexp-pcre.js.
+
+  - Layout tests, located in test/mjsunit/third_party/object-keys.  These are
+    based on layout tests from webkit.org which are copyrighted by
+    Apple Computer, Inc. and released under a 3-clause BSD license.
+
+  - Strongtalk assembler, the basis of the files assembler-arm-inl.h,
+    assembler-arm.cc, assembler-arm.h, assembler-ia32-inl.h,
+    assembler-ia32.cc, assembler-ia32.h, assembler-x64-inl.h,
+    assembler-x64.cc, assembler-x64.h, assembler-mips-inl.h,
+    assembler-mips.cc, assembler-mips.h, assembler.cc and assembler.h.
+    This code is copyrighted by Sun Microsystems Inc. and released
+    under a 3-clause BSD license.
+
+  - Valgrind client API header, located at third_party/valgrind/valgrind.h
+    This is release under the BSD license.
+
+These libraries have their own licenses; we recommend you read them,
+as their terms may differ from the terms below.
+
+Further license information can be found in LICENSE files located in 
+sub-directories.
+
+Copyright 2014, the V8 project authors. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+    * Neither the name of Google Inc. nor the names of its
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+========================================================================
+
+fdlibm
+
+Copyright (C) 1993-2004 by Sun Microsystems, Inc. All rights reserved.
+
+Developed at SunSoft, a Sun Microsystems, Inc. business.
+Permission to use, copy, modify, and distribute this
+software is freely granted, provided that this notice
+is preserved.
+
+========================================================================
+
+Strongtalk
+
+Copyright (c) 1994-2006 Sun Microsystems Inc.
+All Rights Reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+- Redistribution in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+- Neither the name of Sun Microsystems or the names of contributors may
+be used to endorse or promote products derived from this software without
+specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The original source code covered by the above license above has been
+modified significantly by Google Inc.
+Copyright 2006-2008 the V8 project authors. All rights reserved.
+
+========================================================================
+
+valgrind
+
+----------------------------------------------------------------
+
+Notice that the following BSD-style license applies to this one
+file (valgrind.h) only.  The rest of Valgrind is licensed under the
+terms of the GNU General Public License, version 2, unless
+otherwise indicated.  See the COPYING file in the source
+distribution for details.
+
+----------------------------------------------------------------
+
+This file is part of Valgrind, a dynamic binary instrumentation
+framework.
+
+Copyright (C) 2000-2007 Julian Seward.  All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+2. The origin of this software must not be misrepresented; you must 
+   not claim that you wrote the original software.  If you use this 
+   software in a product, an acknowledgment in the product 
+   documentation would be appreciated but is not required.
+
+3. Altered source versions must be plainly marked as such, and must
+   not be misrepresented as being the original software.
+
+4. The name of the author may not be used to endorse or promote 
+   products derived from this software without specific prior written 
+   permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+============================================================
+Notices for file(s):
 /framework/org-netbeans-api-visual.jar
 /framework/org-openide-util.jar
 ------------------------------------------------------------
@@ -1353,202 +1181,23 @@
 
 ============================================================
 Notices for file(s):
-/bin/adb
-/lib/libdiagnose_usb.a
-/lib64/libadb.a
-/lib64/libdiagnose_usb.a
+/bin/mdnsd
+/lib/libmdnssd.a
+/lib64/libmdnssd.a
 ------------------------------------------------------------
+The majority of the source code in the mDNSResponder project is licensed
+under the terms of the Apache License, Version 2.0, available from:
+   <http://www.apache.org/licenses/LICENSE-2.0>
 
-   Copyright (c) 2006-2009, The Android Open Source Project
-   Copyright 2006, Brian Swetland <swetland@frotz.net>
+To accommodate license compatibility with the widest possible range
+of client code licenses, the shared library code, which is linked
+at runtime into the same address space as the client using it, is
+licensed under the terms of the "Three-Clause BSD License".
 
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-
-
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
+The Linux Name Service Switch code, contributed by National ICT
+Australia Ltd (NICTA) is licensed under the terms of the NICTA Public
+Software Licence (which is substantially similar to the "Three-Clause
+BSD License", with some additional language pertaining to Australian law).
 
 ============================================================
 Notices for file(s):
@@ -2767,16 +2416,18 @@
 
 ============================================================
 Notices for file(s):
-/bin/conscrypt_generate_constants
+/bin/assemble_vintf
+/bin/hidl-gen
 /framework/guavalib.jar
 /framework/jarjar-apache-ant.jar
 /framework/jarjar-maven-plugin-api.jar
 /framework/jarjar.jar
 /framework/jsilver.jar
-/lib/libconscrypt_openjdk_jni.so
-/lib/libjavacrypto.so
-/lib64/libconscrypt_openjdk_jni.so
-/lib64/libjavacrypto.so
+/framework/objenesis-host.jar
+/lib64/libhidl-gen-ast.so
+/lib64/libhidl-gen-hash.so
+/lib64/libhidl-gen.so
+/lib64/libvintf.so
 ------------------------------------------------------------
 
                                  Apache License
@@ -2983,12 +2634,398 @@
 
 ============================================================
 Notices for file(s):
-/lib/libunwind.a
+/lib64/libcap.so
+------------------------------------------------------------
+Unless otherwise *explicitly* stated, the following text describes the
+licensed conditions under which the contents of this libcap release
+may be used and distributed:
+
+-------------------------------------------------------------------------
+Redistribution and use in source and binary forms of libcap, with
+or without modification, are permitted provided that the following
+conditions are met:
+
+1. Redistributions of source code must retain any existing copyright
+   notice, and this entire permission notice in its entirety,
+   including the disclaimer of warranties.
+
+2. Redistributions in binary form must reproduce all prior and current
+   copyright notices, this list of conditions, and the following
+   disclaimer in the documentation and/or other materials provided
+   with the distribution.
+
+3. The name of any author may not be used to endorse or promote
+   products derived from this software without their specific prior
+   written permission.
+
+ALTERNATIVELY, this product may be distributed under the terms of the
+GNU General Public License (v2.0 - see below), in which case the
+provisions of the GNU GPL are required INSTEAD OF the above
+restrictions.  (This clause is necessary due to a potential conflict
+between the GNU GPL and the restrictions contained in a BSD-style
+copyright.)
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+-------------------------------------------------------------------------
+
+-------------------------
+Full text of gpl-2.0.txt:
+-------------------------
+
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
+
+============================================================
+Notices for file(s):
 /lib/libunwind.so
-/lib/libunwindbacktrace.a
-/lib64/libunwind.a
 /lib64/libunwind.so
-/lib64/libunwindbacktrace.a
 ------------------------------------------------------------
 Permission is hereby granted, free of charge, to any person obtaining
 a copy of this software and associated documentation files (the
@@ -3011,116 +3048,38 @@
 
 ============================================================
 Notices for file(s):
-/lib/libevent-host.so
-/lib64/libevent-host.so
+/lib/libopenjdkjvmti.so
+/lib64/libopenjdkjvmti.so
 ------------------------------------------------------------
-Libevent is available for use under the following license, commonly known
-as the 3-clause (or "modified") BSD license:
+Copyright (C) 2016 The Android Open Source Project
+DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 
-==============================
-Copyright (c) 2000-2007 Niels Provos <provos@citi.umich.edu>
-Copyright (c) 2007-2012 Niels Provos and Nick Mathewson
+This file implements interfaces from the file jvmti.h. This implementation
+is licensed under the same terms as the file jvmti.h.  The
+copyright and license information for the file jvmti.h follows.
 
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-3. The name of the author may not be used to endorse or promote products
-   derived from this software without specific prior written permission.
+Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
+DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
-IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-==============================
+This code is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License version 2 only, as
+published by the Free Software Foundation.  Oracle designates this
+particular file as subject to the "Classpath" exception as provided
+by Oracle in the LICENSE file that accompanied this code.
 
-Portions of Libevent are based on works by others, also made available by
-them under the three-clause BSD license above.  The copyright notices are
-available in the corresponding source files; the license is as above.  Here's
-a list:
+This code is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+version 2 for more details (a copy is included in the LICENSE file that
+accompanied this code).
 
-log.c:
-   Copyright (c) 2000 Dug Song <dugsong@monkey.org>
-   Copyright (c) 1993 The Regents of the University of California.
+You should have received a copy of the GNU General Public License version
+2 along with this work; if not, write to the Free Software Foundation,
+Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 
-strlcpy.c:
-   Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
-
-win32select.c:
-   Copyright (c) 2003 Michael A. Davis <mike@datanerds.net>
-
-evport.c:
-   Copyright (c) 2007 Sun Microsystems
-
-ht-internal.h:
-   Copyright (c) 2002 Christopher Clark
-
-minheap-internal.h:
-   Copyright (c) 2006 Maxim Yegorushkin <maxim.yegorushkin@gmail.com>
-
-==============================
-
-The arc4module is available under the following, sometimes called the
-"OpenBSD" license:
-
-   Copyright (c) 1996, David Mazieres <dm@uun.org>
-   Copyright (c) 2008, Damien Miller <djm@openbsd.org>
-
-   Permission to use, copy, modify, and distribute this software for any
-   purpose with or without fee is hereby granted, provided that the above
-   copyright notice and this permission notice appear in all copies.
-
-   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-
-============================================================
-Notices for file(s):
-/lib/libchrome.so
-/lib64/libchrome.so
-------------------------------------------------------------
-// Copyright 2014 The Chromium Authors. All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//    * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//    * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-//    * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+or visit www.oracle.com if you need additional information or have any
+questions.
 
 ============================================================
 Notices for file(s):
@@ -3160,38 +3119,645 @@
 
 ============================================================
 Notices for file(s):
-/bin/minigzip
-/lib/libz-host.so
-/lib/libz.a
-/lib64/libz-host.so
-/lib64/libz.a
+/bin/conscrypt_generate_constants
+/lib/libconscrypt_openjdk_jni.so
+/lib/libjavacrypto.so
+/lib64/libconscrypt_openjdk_jni.so
+/lib64/libjavacrypto.so
 ------------------------------------------------------------
- (C) 1995-2013 Jean-loup Gailly and Mark Adler
+Copyright 2016 The Android Open Source Project
 
-  This software is provided 'as-is', without any express or implied
-  warranty.  In no event will the authors be held liable for any damages
-  arising from the use of this software.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
 
-  Permission is granted to anyone to use this software for any purpose,
-  including commercial applications, and to alter it and redistribute it
-  freely, subject to the following restrictions:
+     http://www.apache.org/licenses/LICENSE-2.0
 
-  1. The origin of this software must not be misrepresented; you must not
-     claim that you wrote the original software. If you use this software
-     in a product, an acknowledgment in the product documentation would be
-     appreciated but is not required.
-  2. Altered source versions must be plainly marked as such, and must not be
-     misrepresented as being the original software.
-  3. This notice may not be removed or altered from any source distribution.
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
 
-  Jean-loup Gailly        Mark Adler
-  jloup@gzip.org          madler@alumni.caltech.edu
+-----------------------------------------------------------------------
+This product contains a modified portion of `Netty`, a configurable network
+stack in Java, which can be obtained at:
+
+  * LICENSE:
+    * licenses/LICENSE.netty.txt (Apache License 2.0)
+  * HOMEPAGE:
+    * http://netty.io/
+
+This product contains a modified portion of `Apache Harmony`, modular Java runtime,
+which can be obtained at:
+
+  * LICENSE:
+    * licenses/LICENSE.harmony.txt (Apache License 2.0)
+  * HOMEPAGE:
+    * https://harmony.apache.org/
+
+============================================================
+Notices for file(s):
+/lib64/libplatformprotos.so
+------------------------------------------------------------
+   =========================================================================
+   ==  NOTICE file corresponding to the section 4 d of                    ==
+   ==  the Apache License, Version 2.0,                                   ==
+   ==  in this case for the Android-specific code.                        ==
+   =========================================================================
+
+Android Code
+Copyright 2005-2008 The Android Open Source Project
+
+This product includes software developed as part of
+The Android Open Source Project (http://source.android.com).
+
+   =========================================================================
+   ==  NOTICE file corresponding to the section 4 d of                    ==
+   ==  the Apache License, Version 2.0,                                   ==
+   ==  in this case for Apache Commons code.                              ==
+   =========================================================================
+
+Apache Commons
+Copyright 1999-2006 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+   =========================================================================
+   ==  NOTICE file corresponding to the section 4 d of                    ==
+   ==  the Apache License, Version 2.0,                                   ==
+   ==  in this case for Jakarta Commons Logging.                          ==
+   =========================================================================
+
+Jakarta Commons Logging (JCL)
+Copyright 2005,2006 The Apache Software Foundation.
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+   =========================================================================
+   ==  NOTICE file corresponding to the section 4 d of                    ==
+   ==  the Apache License, Version 2.0,                                   ==
+   ==  in this case for the Nuance code.                                  ==
+   =========================================================================
+
+These files are Copyright 2007 Nuance Communications, but released under
+the Apache2 License.
+
+   =========================================================================
+   ==  NOTICE file corresponding to the section 4 d of                    ==
+   ==  the Apache License, Version 2.0,                                   ==
+   ==  in this case for the Media Codecs code.                            ==
+   =========================================================================
+
+Media Codecs
+These files are Copyright 1998 - 2009 PacketVideo, but released under
+the Apache2 License.
+
+   =========================================================================
+   ==  NOTICE file corresponding to the section 4 d of                    ==
+   ==  the Apache License, Version 2.0,                                   ==
+   ==  in this case for the mDnsResponder code.                           ==
+   =========================================================================
+
+mDnsResponder TXTRecord
+This file is Copyright 2004 Apple Computer, Inc.  but released under
+the Apache2 License.
+
+
+   =========================================================================
+   ==  NOTICE file corresponding to the section 4 d of                    ==
+   ==  the Apache License, Version 2.0,                                   ==
+   ==  in this case for the TagSoup code.                                 ==
+   =========================================================================
+
+This file is part of TagSoup and is Copyright 2002-2008 by John Cowan.
+
+TagSoup is licensed under the Apache License,
+Version 2.0.  You may obtain a copy of this license at
+http://www.apache.org/licenses/LICENSE-2.0 .  You may also have
+additional legal rights not granted by this license.
+
+TagSoup is distributed in the hope that it will be useful, but
+unless required by applicable law or agreed to in writing, TagSoup
+is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
+OF ANY KIND, either express or implied; not even the implied warranty
+of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+   =========================================================================
+   ==  NOTICE file corresponding to the section 4 d of                    ==
+   ==  the Apache License, Version 2.0,                                   ==
+   ==  in this case for Additional Codecs code.                           ==
+   =========================================================================
+
+Additional Codecs
+These files are Copyright 2003-2010 VisualOn, but released under
+the Apache2 License.
+
+  =========================================================================
+  ==  NOTICE file corresponding to the section 4 d of                    ==
+  ==  the Apache License, Version 2.0,                                   ==
+  ==  in this case for the Audio Effects code.                           ==
+  =========================================================================
+
+Audio Effects
+These files are Copyright (C) 2004-2010 NXP Software and
+Copyright (C) 2010 The Android Open Source Project, but released under
+the Apache2 License.
+
+
+                               Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+
+
+UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE
+
+Unicode Data Files include all data files under the directories
+http://www.unicode.org/Public/, http://www.unicode.org/reports/,
+and http://www.unicode.org/cldr/data/ . Unicode Software includes any
+source code published in the Unicode Standard or under the directories
+http://www.unicode.org/Public/, http://www.unicode.org/reports/, and
+http://www.unicode.org/cldr/data/.
+
+NOTICE TO USER: Carefully read the following legal agreement. BY
+DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S DATA
+FILES ("DATA FILES"), AND/OR SOFTWARE ("SOFTWARE"), YOU UNEQUIVOCALLY
+ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE TERMS AND CONDITIONS OF
+THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY,
+DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE.
+
+COPYRIGHT AND PERMISSION NOTICE
+
+Copyright © 1991-2008 Unicode, Inc. All rights reserved. Distributed
+under the Terms of Use in http://www.unicode.org/copyright.html.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Unicode data files and any associated documentation (the
+"Data Files") or Unicode software and any associated documentation (the
+"Software") to deal in the Data Files or Software without restriction,
+including without limitation the rights to use, copy, modify, merge,
+publish, distribute, and/or sell copies of the Data Files or Software,
+and to permit persons to whom the Data Files or Software are furnished to
+do so, provided that (a) the above copyright notice(s) and this permission
+notice appear with all copies of the Data Files or Software, (b) both the
+above copyright notice(s) and this permission notice appear in associated
+documentation, and (c) there is clear notice in each modified Data File
+or in the Software as well as in the documentation associated with the
+Data File(s) or Software that the data or software has been modified.
+
+THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
+INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
+OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
+OR PERFORMANCE OF THE DATA FILES OR SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder
+shall not be used in advertising or otherwise to promote the sale, use
+or other dealings in these Data Files or Software without prior written
+authorization of the copyright holder.
+
+============================================================
+Notices for file(s):
+/bin/adb
+/lib/libdiagnose_usb.a
+/lib64/libadb.a
+/lib64/libdiagnose_usb.a
+------------------------------------------------------------
+
+   Copyright (c) 2006-2009, The Android Open Source Project
+   Copyright 2006, Brian Swetland <swetland@frotz.net>
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+------------------------------------------------------------
+libwinpthread license:
+------------------------------------------------------------
+Copyright (c) 2011 mingw-w64 project
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+
+/*
+ * Parts of this library are derived by:
+ *
+ * Posix Threads library for Microsoft Windows
+ *
+ * Use at own risk, there is no implied warranty to this code.
+ * It uses undocumented features of Microsoft Windows that can change
+ * at any time in the future.
+ *
+ * (C) 2010 Lockless Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ *
+ *  * Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *  * Neither the name of Lockless Inc. nor the names of its contributors may be
+ *    used to endorse or promote products derived from this software without
+ *    specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AN
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
 
 ============================================================
 Notices for file(s):
 /bin/llvm-rs-cc
-/lib/libslang.a
-/lib64/libslang.a
 ------------------------------------------------------------
 =========================
 NOTICE file for slang.git
@@ -3535,54 +4101,23 @@
 
 ============================================================
 Notices for file(s):
-/bin/sefcontext_compile
-/lib/libselinux.a
-/lib/libselinux.so
-/lib64/libselinux.a
-/lib64/libselinux.so
-------------------------------------------------------------
-This library (libselinux) is public domain software, i.e. not copyrighted.
-
-Warranty Exclusion
-------------------
-You agree that this software is a
-non-commercially developed program that may contain "bugs" (as that
-term is used in the industry) and that it may not function as intended.
-The software is licensed "as is". NSA makes no, and hereby expressly
-disclaims all, warranties, express, implied, statutory, or otherwise
-with respect to the software, including noninfringement and the implied
-warranties of merchantability and fitness for a particular purpose.
-
-Limitation of Liability
------------------------
-In no event will NSA be liable for any damages, including loss of data,
-lost profits, cost of cover, or other special, incidental,
-consequential, direct or indirect damages arising from the software or
-the use thereof, however caused and on any theory of liability. This
-limitation will apply even if NSA has been advised of the possibility
-of such damage. You acknowledge that this is a reasonable allocation of
-risk.
-
-============================================================
-Notices for file(s):
 /bin/aapt
-/bin/aidl-cpp
 /bin/aidl
 /bin/dexdump
 /bin/dx
 /lib/libaapt.a
-/lib/libaidl-common.a
 /lib/libandroidfw.a
 /lib/libcutils.a
 /lib/libcutils.so
+/lib/libinstrumentation.a
 /lib/libnativehelper.so
 /lib/libsqlite3_android.a
 /lib/libutils.a
 /lib64/libaapt.a
-/lib64/libaidl-common.a
 /lib64/libandroidfw.a
 /lib64/libcutils.a
 /lib64/libcutils.so
+/lib64/libinstrumentation.a
 /lib64/libnativehelper.so
 /lib64/libsqlite3_android.a
 /lib64/libutils.a
@@ -3903,6 +4438,35 @@
 
 ============================================================
 Notices for file(s):
+/lib/libexpat-host.so
+/lib/libexpat.a
+/lib64/libexpat-host.so
+/lib64/libexpat.a
+------------------------------------------------------------
+Copyright (c) 1998-2000 Thai Open Source Software Center Ltd and Clark Cooper
+Copyright (c) 2001-2016 Expat maintainers
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+============================================================
+Notices for file(s):
 /framework/doclava.jar
 ------------------------------------------------------------
  
@@ -3953,10 +4517,41 @@
 
 ============================================================
 Notices for file(s):
+/bin/minigzip
+/lib/libz-host.so
+/lib/libz.a
+/lib64/libz-host.so
+/lib64/libz.a
+------------------------------------------------------------
+ (C) 1995-2017 Jean-loup Gailly and Mark Adler
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  Jean-loup Gailly        Mark Adler
+  jloup@gzip.org          madler@alumni.caltech.edu
+
+============================================================
+Notices for file(s):
 /bin/aprotoc
-/lib/libprotobuf-cpp-lite_static.a
+/framework/host-libprotobuf-java-full.jar
+/framework/host-libprotobuf-java-lite.jar
+/lib64/libprotobuf-cpp-full.so
 /lib64/libprotobuf-cpp-lite.so
-/lib64/libprotobuf-cpp-lite_static.a
+/lib64/libprotoc.so
 ------------------------------------------------------------
 Copyright 2008, Google Inc.
 All rights reserved.
@@ -3995,10 +4590,8 @@
 ============================================================
 Notices for file(s):
 /bin/memory_replay
-/bin/memory_replay_tests32
-/bin/memory_replay_tests64
-/bin/simpleperf
-/lib64/libsimpleperf.a
+/nativetest/memory_replay_tests/memory_replay_tests32
+/nativetest64/memory_replay_tests/memory_replay_tests64
 ------------------------------------------------------------
 
    Copyright (c) 2015, The Android Open Source Project
@@ -4197,11 +4790,12 @@
 /bin/make_ext4fs
 /bin/make_f2fs
 /bin/mkuserimg.sh
-/lib/libext4_utils_host.a
+/lib/libext4_utils.a
 /lib/libf2fs_dlutils_host.a
 /lib/libf2fs_ioutils_host.a
 /lib/libf2fs_utils_host.a
-/lib64/libext4_utils_host.a
+/lib64/libext4_utils.a
+/lib64/libext4_utils.so
 /lib64/libf2fs_dlutils_host.a
 /lib64/libf2fs_ioutils_host.a
 /lib64/libf2fs_utils_host.a
@@ -4536,6 +5130,42 @@
 
 ============================================================
 Notices for file(s):
+/lib/libgtest_host.a
+/lib/libgtest_main_host.a
+/lib64/libgtest_host.a
+/lib64/libgtest_main_host.a
+------------------------------------------------------------
+Copyright 2008, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+    * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+============================================================
+Notices for file(s):
 /lib/libopenjdk.so
 /lib64/libopenjdk.so
 ------------------------------------------------------------
@@ -13284,56 +13914,9 @@
 
 ============================================================
 Notices for file(s):
-/lib/libbz.a
-/lib64/libbz.a
-------------------------------------------------------------
-
---------------------------------------------------------------------------
-
-This program, "bzip2", the associated library "libbzip2", and all
-documentation, are copyright (C) 1996-2010 Julian R Seward.  All
-rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-
-2. The origin of this software must not be misrepresented; you must 
-   not claim that you wrote the original software.  If you use this 
-   software in a product, an acknowledgment in the product 
-   documentation would be appreciated but is not required.
-
-3. Altered source versions must be plainly marked as such, and must
-   not be misrepresented as being the original software.
-
-4. The name of the author may not be used to endorse or promote 
-   products derived from this software without specific prior written 
-   permission.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
-OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
-DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
-GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-Julian Seward, jseward@bzip.org
-bzip2/libbzip2 version 1.0.6 of 6 September 2010
-
---------------------------------------------------------------------------
-
-============================================================
-Notices for file(s):
-/lib/libpcre.a
-/lib64/libpcre.a
+/lib/libpcre2.a
+/lib64/libpcre2.a
+/lib64/libpcre2.so
 ------------------------------------------------------------
 PCRE LICENCE
 ------------
@@ -13430,224 +14013,63 @@
 
 ============================================================
 Notices for file(s):
-/lib64/libdivsufsort.so
-/lib64/libdivsufsort64.so
+/lib64/libtinyalsa.so
 ------------------------------------------------------------
-The MIT License (MIT)
-
-Copyright (c) 2003 Yuta Mori All rights reserved.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-
-============================================================
-Notices for file(s):
-/framework/junit.jar
-------------------------------------------------------------
-Common Public License - v 1.0
-
-
-THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS COMMON PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
-
-
-1. DEFINITIONS
-
-"Contribution" means:
-
-a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and
-b) in the case of each subsequent Contributor:
-i)	 changes to the Program, and
-ii)	 additions to the Program;
-where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program.
-
-"Contributor" means any person or entity that distributes the Program.
-
-
-"Licensed Patents " mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program.
-
-
-"Program" means the Contributions distributed in accordance with this Agreement.
-
-
-"Recipient" means anyone who receives the Program under this Agreement, including all Contributors.
-
-
-2. GRANT OF RIGHTS
-
-a)	Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form.
-b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder.
-c)	Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program.
-d)	Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement.
-3. REQUIREMENTS
-
-A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that:
-
-a)	it complies with the terms and conditions of this Agreement; and
-b)	its license agreement:
-i)	effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose;
-ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits;
-iii)	states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and
-iv)	states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange.
-When the Program is made available in source code form:
-
-a)	it must be made available under this Agreement; and
-b)	a copy of this Agreement must be included with each copy of the Program.
-
-Contributors may not remove or alter any copyright notices contained within the Program.
-
-
-Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution.
-
-
-4. COMMERCIAL DISTRIBUTION
-
-Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense.
-
-
-For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages.
-
-
-5. NO WARRANTY
-
-EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement, including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations.
-
-
-6. DISCLAIMER OF LIABILITY
-
-EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-
-7. GENERAL
-
-If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
-
-
-If Recipient institutes patent litigation against a Contributor with respect to a patent applicable to software (including a cross-claim or counterclaim in a lawsuit), then any patent licenses granted by that Contributor to such Recipient under this Agreement shall terminate as of the date such litigation is filed. In addition, if Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed.
-
-
-All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive.
-
-
-Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. IBM is the initial Agreement Steward. IBM may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved.
-
-
-This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation.
-============================================================
-Notices for file(s):
-/bin/bsdiff_unittest
-------------------------------------------------------------
-This project governed by the following BSD-style licenses. Check each file
-header to known the licenses applying to it:
-
---------------------------------------------------------------------------------
-
-Copyright 2003-2005 Colin Percival
-All rights reserved
+Copyright 2011, The Android Open Source Project
 
 Redistribution and use in source and binary forms, with or without
-modification, are permitted providing that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
+modification, are permitted provided that the following conditions are met:
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+    * Neither the name of The Android Open Source Project nor the names of
+      its contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
 
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
-IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
-DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
-IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
+THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL The Android Open Source Project BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
 
---------------------------------------------------------------------------------
-
-Copyright 2015 The Chromium OS Authors. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
-   * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
-   * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
-   * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 ============================================================
 Notices for file(s):
-/lib/libmodpb64-host.a
-/lib64/libmodpb64-host.a
+/lib/liblz4.so
+/lib64/liblz4.so
 ------------------------------------------------------------
- * MODP_B64 - High performance base64 encoder/decoder
- * Version 1.3 -- 17-Mar-2006
- * http://modp.com/release/base64
- *
- * Copyright (c) 2005, 2006  Nick Galbreath -- nickg [at] modp [dot] com
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *   Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- *
- *   Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- *
- *   Neither the name of the modp.com nor the names of its
- *   contributors may be used to endorse or promote products derived from
- *   this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+LZ4 Library
+Copyright (c) 2011-2016, Yann Collet
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice, this
+  list of conditions and the following disclaimer in the documentation and/or
+  other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 ============================================================
 Notices for file(s):
@@ -13673,13 +14095,11 @@
 ============================================================
 Notices for file(s):
 /lib/libcrypto-host.so
-/lib/libcrypto_static.a
+/lib/libcrypto.a
 /lib/libssl-host.so
-/lib/libssl_static-host.a
 /lib64/libcrypto-host.so
-/lib64/libcrypto_static.a
+/lib64/libcrypto.a
 /lib64/libssl-host.so
-/lib64/libssl_static-host.a
 ------------------------------------------------------------
 
   LICENSE ISSUES
@@ -13811,36 +14231,511 @@
 
 ============================================================
 Notices for file(s):
-/bin/dbus-binding-generator
-/lib/libdbus-binding-gen-host.a
-/lib64/libbrillo.so
-/lib64/libdbus-binding-gen-host.a
+/lib/libusb.a
+/lib64/libusb.a
 ------------------------------------------------------------
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//    * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//    * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-//    * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+		  GNU LESSER GENERAL PUBLIC LICENSE
+		       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+		  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+			    NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+
 
diff --git a/sdk/platform-tools/adb b/sdk/platform-tools/adb
index 3c28311..32d1b28 100755
--- a/sdk/platform-tools/adb
+++ b/sdk/platform-tools/adb
Binary files differ
diff --git a/sdk/platform-tools/api/annotations.zip b/sdk/platform-tools/api/annotations.zip
index 788cbf8..468227c 100644
--- a/sdk/platform-tools/api/annotations.zip
+++ b/sdk/platform-tools/api/annotations.zip
Binary files differ
diff --git a/sdk/platform-tools/api/api-versions.xml b/sdk/platform-tools/api/api-versions.xml
index 16316e3..f218f4c 100644
--- a/sdk/platform-tools/api/api-versions.xml
+++ b/sdk/platform-tools/api/api-versions.xml
@@ -19,10 +19,12 @@
 		<field name="ACCOUNT_MANAGER" since="5" />
 		<field name="ADD_SYSTEM_SERVICE" />
 		<field name="ADD_VOICEMAIL" since="14" />
+		<field name="ANSWER_PHONE_CALLS" since="26" />
 		<field name="AUTHENTICATE_ACCOUNTS" since="5" />
 		<field name="BATTERY_STATS" />
 		<field name="BIND_ACCESSIBILITY_SERVICE" since="16" />
 		<field name="BIND_APPWIDGET" since="3" />
+		<field name="BIND_AUTOFILL_SERVICE" since="26" />
 		<field name="BIND_CARRIER_MESSAGING_SERVICE" since="22" deprecated="23" />
 		<field name="BIND_CARRIER_SERVICES" since="23" />
 		<field name="BIND_CHOOSER_TARGET_SERVICE" since="23" />
@@ -41,6 +43,7 @@
 		<field name="BIND_TELECOM_CONNECTION_SERVICE" since="23" />
 		<field name="BIND_TEXT_SERVICE" since="14" />
 		<field name="BIND_TV_INPUT" since="21" />
+		<field name="BIND_VISUAL_VOICEMAIL_SERVICE" since="26" />
 		<field name="BIND_VOICE_INTERACTION" since="21" />
 		<field name="BIND_VPN_SERVICE" since="14" />
 		<field name="BIND_VR_LISTENER_SERVICE" since="24" />
@@ -90,6 +93,7 @@
 		<field name="INSTALL_LOCATION_PROVIDER" since="4" />
 		<field name="INSTALL_PACKAGES" />
 		<field name="INSTALL_SHORTCUT" since="19" />
+		<field name="INSTANT_APP_FOREGROUND_SERVICE" since="26" />
 		<field name="INTERNAL_SYSTEM_WINDOW" />
 		<field name="INTERNET" />
 		<field name="KILL_BACKGROUND_PROCESSES" since="8" />
@@ -97,6 +101,7 @@
 		<field name="MANAGE_ACCOUNTS" since="5" />
 		<field name="MANAGE_APP_TOKENS" />
 		<field name="MANAGE_DOCUMENTS" since="19" />
+		<field name="MANAGE_OWN_CALLS" since="26" />
 		<field name="MASTER_CLEAR" />
 		<field name="MEDIA_CONTENT_CONTROL" since="19" />
 		<field name="MODIFY_AUDIO_SETTINGS" />
@@ -116,6 +121,7 @@
 		<field name="READ_INPUT_STATE" deprecated="16" />
 		<field name="READ_LOGS" />
 		<field name="READ_OWNER_DATA" />
+		<field name="READ_PHONE_NUMBERS" since="26" />
 		<field name="READ_PHONE_STATE" />
 		<field name="READ_PROFILE" since="14" />
 		<field name="READ_SMS" />
@@ -131,6 +137,9 @@
 		<field name="RECEIVE_WAP_PUSH" />
 		<field name="RECORD_AUDIO" />
 		<field name="REORDER_TASKS" />
+		<field name="REQUEST_COMPANION_RUN_IN_BACKGROUND" since="26" />
+		<field name="REQUEST_COMPANION_USE_DATA_IN_BACKGROUND" since="26" />
+		<field name="REQUEST_DELETE_PACKAGES" since="26" />
 		<field name="REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" since="23" />
 		<field name="REQUEST_INSTALL_PACKAGES" since="23" />
 		<field name="RESTART_PACKAGES" deprecated="16" />
@@ -322,6 +331,7 @@
 		<field name="allowTaskReparenting" />
 		<field name="allowUndo" since="23" />
 		<field name="alpha" since="11" />
+		<field name="alphabeticModifiers" since="26" />
 		<field name="alphabeticShortcut" />
 		<field name="alwaysDrawnWithCache" />
 		<field name="alwaysRetainTaskState" />
@@ -341,6 +351,7 @@
 		<field name="anyDensity" since="4" />
 		<field name="apduServiceBanner" since="19" />
 		<field name="apiKey" />
+		<field name="appCategory" since="26" />
 		<field name="author" since="7" />
 		<field name="authorities" />
 		<field name="autoAdvanceViewId" since="11" />
@@ -348,10 +359,17 @@
 		<field name="autoLink" />
 		<field name="autoMirrored" since="19" />
 		<field name="autoRemoveFromRecents" since="21" />
+		<field name="autoSizeMaxTextSize" since="26" />
+		<field name="autoSizeMinTextSize" since="26" />
+		<field name="autoSizePresetSizes" since="26" />
+		<field name="autoSizeStepGranularity" since="26" />
+		<field name="autoSizeTextType" since="26" />
 		<field name="autoStart" since="7" />
 		<field name="autoText" deprecated="16" />
 		<field name="autoUrlDetect" since="4" />
 		<field name="autoVerify" since="23" />
+		<field name="autofillHints" since="26" />
+		<field name="autofilledHighlight" since="26" />
 		<field name="background" />
 		<field name="backgroundDimAmount" />
 		<field name="backgroundDimEnabled" since="3" />
@@ -399,8 +417,9 @@
 		<field name="canControlMagnification" since="24" />
 		<field name="canPerformGestures" since="24" />
 		<field name="canRecord" since="24" />
-		<field name="canRequestEnhancedWebAccessibility" since="18" />
+		<field name="canRequestEnhancedWebAccessibility" since="18" deprecated="26" />
 		<field name="canRequestFilterKeyEvents" since="18" />
+		<field name="canRequestFingerprintGestures" since="26" />
 		<field name="canRequestTouchExplorationMode" since="18" />
 		<field name="canRetrieveWindowContent" since="14" />
 		<field name="candidatesTextStyleSpans" since="3" />
@@ -412,6 +431,7 @@
 		<field name="centerMedium" />
 		<field name="centerX" />
 		<field name="centerY" />
+		<field name="certDigest" since="26" />
 		<field name="checkBoxPreferenceStyle" />
 		<field name="checkMark" />
 		<field name="checkMarkTint" since="21" />
@@ -450,10 +470,12 @@
 		<field name="colorControlHighlight" since="21" />
 		<field name="colorControlNormal" since="21" />
 		<field name="colorEdgeEffect" since="21" />
+		<field name="colorError" since="26" />
 		<field name="colorFocusedHighlight" since="14" />
 		<field name="colorForeground" />
 		<field name="colorForegroundInverse" />
 		<field name="colorLongPressedHighlight" since="14" />
+		<field name="colorMode" since="26" />
 		<field name="colorMultiSelectHighlight" since="14" />
 		<field name="colorPressedHighlight" since="14" />
 		<field name="colorPrimary" since="21" />
@@ -506,6 +528,7 @@
 		<field name="dayOfWeekBackground" since="21" deprecated="23" />
 		<field name="dayOfWeekTextAppearance" since="21" deprecated="23" />
 		<field name="debuggable" />
+		<field name="defaultFocusHighlightEnabled" since="26" />
 		<field name="defaultHeight" since="24" />
 		<field name="defaultToDeviceProtectedStorage" since="24" />
 		<field name="defaultValue" />
@@ -637,9 +660,17 @@
 		<field name="flipInterval" />
 		<field name="focusable" />
 		<field name="focusableInTouchMode" />
+		<field name="focusedByDefault" since="26" />
 		<field name="focusedMonthDateColor" since="11" deprecated="23" />
+		<field name="font" since="26" />
 		<field name="fontFamily" since="16" />
 		<field name="fontFeatureSettings" since="21" />
+		<field name="fontProviderAuthority" since="26" />
+		<field name="fontProviderCerts" since="26" />
+		<field name="fontProviderPackage" since="26" />
+		<field name="fontProviderQuery" since="26" />
+		<field name="fontStyle" since="26" />
+		<field name="fontWeight" since="26" />
 		<field name="footerDividersEnabled" since="3" />
 		<field name="forceHasOverlappingRendering" since="24" />
 		<field name="foreground" />
@@ -723,6 +754,9 @@
 		<field name="hyphenationFrequency" since="23" />
 		<field name="icon" />
 		<field name="iconPreview" since="3" />
+		<field name="iconSpaceReserved" since="26" />
+		<field name="iconTint" since="26" />
+		<field name="iconTintMode" since="26" />
 		<field name="iconifiedByDefault" since="11" />
 		<field name="id" />
 		<field name="ignoreGravity" />
@@ -739,6 +773,7 @@
 		<field name="imeSubtypeMode" since="11" />
 		<field name="immersive" since="11" />
 		<field name="importantForAccessibility" since="16" />
+		<field name="importantForAutofill" since="26" />
 		<field name="inAnimation" />
 		<field name="includeFontPadding" />
 		<field name="includeInGlobalSearch" since="4" />
@@ -773,17 +808,21 @@
 		<field name="isAsciiCapable" since="19" />
 		<field name="isAuxiliary" since="14" />
 		<field name="isDefault" since="3" />
+		<field name="isFeatureSplit" since="26" />
 		<field name="isGame" since="21" />
 		<field name="isIndicator" />
 		<field name="isModifier" since="3" />
 		<field name="isRepeatable" since="3" />
 		<field name="isScrollContainer" since="3" />
+		<field name="isStatic" since="26" />
 		<field name="isSticky" since="3" />
 		<field name="isolatedProcess" since="16" />
+		<field name="isolatedSplits" since="26" />
 		<field name="itemBackground" />
 		<field name="itemIconDisabledAlpha" />
 		<field name="itemPadding" since="11" />
 		<field name="itemTextAppearance" />
+		<field name="justificationMode" since="26" />
 		<field name="keepScreenOn" />
 		<field name="key" />
 		<field name="keyBackground" since="3" />
@@ -801,6 +840,7 @@
 		<field name="keyWidth" since="3" />
 		<field name="keyboardLayout" since="16" />
 		<field name="keyboardMode" since="3" />
+		<field name="keyboardNavigationCluster" since="26" />
 		<field name="keycode" />
 		<field name="killAfterRestore" since="5" />
 		<field name="label" />
@@ -845,10 +885,12 @@
 		<field name="layout_margin" />
 		<field name="layout_marginBottom" />
 		<field name="layout_marginEnd" since="17" />
+		<field name="layout_marginHorizontal" since="26" />
 		<field name="layout_marginLeft" />
 		<field name="layout_marginRight" />
 		<field name="layout_marginStart" since="17" />
 		<field name="layout_marginTop" />
+		<field name="layout_marginVertical" since="26" />
 		<field name="layout_row" since="14" />
 		<field name="layout_rowSpan" since="14" />
 		<field name="layout_rowWeight" since="21" />
@@ -897,6 +939,7 @@
 		<field name="marqueeRepeatLimit" since="2" />
 		<field name="matchOrder" since="21" />
 		<field name="max" />
+		<field name="maxAspectRatio" since="26" />
 		<field name="maxButtonHeight" since="24" />
 		<field name="maxDate" since="11" />
 		<field name="maxEms" />
@@ -916,6 +959,7 @@
 		<field name="mediaRouteTypes" since="16" />
 		<field name="menuCategory" />
 		<field name="mimeType" />
+		<field name="min" since="26" />
 		<field name="minDate" since="11" />
 		<field name="minEms" />
 		<field name="minHeight" />
@@ -941,6 +985,7 @@
 		<field name="negativeButtonText" />
 		<field name="nestedScrollingEnabled" since="21" />
 		<field name="networkSecurityConfig" since="24" />
+		<field name="nextClusterForward" since="26" />
 		<field name="nextFocusDown" />
 		<field name="nextFocusForward" since="11" />
 		<field name="nextFocusLeft" />
@@ -957,6 +1002,7 @@
 		<field name="numbersSelectorColor" since="21" />
 		<field name="numbersTextColor" since="21" />
 		<field name="numeric" deprecated="16" />
+		<field name="numericModifiers" since="26" />
 		<field name="numericShortcut" />
 		<field name="offset" since="24" />
 		<field name="onClick" since="4" />
@@ -978,11 +1024,13 @@
 		<field name="padding" />
 		<field name="paddingBottom" />
 		<field name="paddingEnd" since="17" />
+		<field name="paddingHorizontal" since="26" />
 		<field name="paddingLeft" />
 		<field name="paddingMode" since="21" />
 		<field name="paddingRight" />
 		<field name="paddingStart" since="17" />
 		<field name="paddingTop" />
+		<field name="paddingVertical" since="26" />
 		<field name="panelBackground" />
 		<field name="panelColorBackground" />
 		<field name="panelColorForeground" />
@@ -1002,6 +1050,7 @@
 		<field name="persistableMode" since="21" />
 		<field name="persistent" />
 		<field name="persistentDrawingCache" />
+		<field name="persistentWhenFeatureAvailable" since="26" />
 		<field name="phoneNumber" deprecated="16" />
 		<field name="pivotX" />
 		<field name="pivotY" />
@@ -1027,6 +1076,7 @@
 		<field name="preferenceStyle" />
 		<field name="presentationTheme" since="17" />
 		<field name="previewImage" since="11" />
+		<field name="primaryContentAlpha" since="26" />
 		<field name="priority" />
 		<field name="privateImeOptions" since="3" />
 		<field name="process" />
@@ -1069,6 +1119,8 @@
 		<field name="ratingBarStyleSmall" />
 		<field name="readPermission" />
 		<field name="recognitionService" since="21" />
+		<field name="recreateOnConfigChanges" since="26" />
+		<field name="recycleEnabled" since="26" />
 		<field name="relinquishTaskIdentity" since="21" />
 		<field name="reparent" since="21" />
 		<field name="reparentWithOverlay" since="21" />
@@ -1082,7 +1134,9 @@
 		<field name="requireDeviceUnlock" since="19" />
 		<field name="required" since="5" />
 		<field name="requiredAccountType" since="18" />
+		<field name="requiredFeature" since="26" />
 		<field name="requiredForAllUsers" since="18" />
+		<field name="requiredNotFeature" since="26" />
 		<field name="requiresFadingEdge" since="14" />
 		<field name="requiresSmallestWidthDp" since="13" />
 		<field name="resizeClip" since="22" />
@@ -1101,6 +1155,7 @@
 		<field name="ringtonePreferenceStyle" />
 		<field name="ringtoneType" />
 		<field name="rotation" since="11" />
+		<field name="rotationAnimation" since="26" />
 		<field name="rotationX" since="11" />
 		<field name="rotationY" since="11" />
 		<field name="roundIcon" since="25" />
@@ -1149,6 +1204,7 @@
 		<field name="searchSuggestSelection" />
 		<field name="searchSuggestThreshold" since="4" />
 		<field name="searchViewStyle" since="21" />
+		<field name="secondaryContentAlpha" since="26" />
 		<field name="secondaryProgress" />
 		<field name="secondaryProgressTint" since="21" />
 		<field name="secondaryProgressTintMode" since="21" />
@@ -1188,6 +1244,7 @@
 		<field name="shownWeekCount" since="11" deprecated="23" />
 		<field name="shrinkColumns" />
 		<field name="singleLine" deprecated="16" />
+		<field name="singleLineTitle" since="26" />
 		<field name="singleUser" since="17" />
 		<field name="slideEdge" since="21" />
 		<field name="smallIcon" since="5" />
@@ -1201,6 +1258,7 @@
 		<field name="spinnerStyle" />
 		<field name="spinnersShown" since="11" />
 		<field name="splitMotionEvents" since="11" />
+		<field name="splitName" since="26" />
 		<field name="splitTrack" since="21" />
 		<field name="spotShadowAlpha" since="21" />
 		<field name="src" />
@@ -1296,6 +1354,8 @@
 		<field name="targetId" since="19" />
 		<field name="targetName" since="21" />
 		<field name="targetPackage" />
+		<field name="targetProcesses" since="26" />
+		<field name="targetSandboxVersion" since="26" />
 		<field name="targetSdkVersion" since="4" />
 		<field name="taskAffinity" />
 		<field name="taskCloseEnterAnimation" />
@@ -1411,6 +1471,7 @@
 		<field name="toYDelta" />
 		<field name="toYScale" />
 		<field name="toolbarStyle" since="21" />
+		<field name="tooltipText" since="26" />
 		<field name="top" />
 		<field name="topBright" />
 		<field name="topDark" />
@@ -1469,6 +1530,7 @@
 		<field name="viewportWidth" since="21" />
 		<field name="visibility" />
 		<field name="visible" />
+		<field name="visibleToInstantApps" since="26" />
 		<field name="vmSafeMode" since="8" />
 		<field name="voiceIcon" since="21" />
 		<field name="voiceLanguage" since="3" />
@@ -1536,6 +1598,7 @@
 		<field name="windowShowAnimation" />
 		<field name="windowShowWallpaper" since="5" />
 		<field name="windowSoftInputMode" since="3" />
+		<field name="windowSplashscreenContent" since="26" />
 		<field name="windowSwipeToDismiss" since="20" />
 		<field name="windowTitleBackgroundStyle" />
 		<field name="windowTitleSize" />
@@ -1785,6 +1848,7 @@
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
 		<field name="accessibilityActionContextClick" since="23" />
+		<field name="accessibilityActionMoveWindow" since="26" />
 		<field name="accessibilityActionScrollDown" since="23" />
 		<field name="accessibilityActionScrollLeft" since="23" />
 		<field name="accessibilityActionScrollRight" since="23" />
@@ -1793,6 +1857,7 @@
 		<field name="accessibilityActionSetProgress" since="24" />
 		<field name="accessibilityActionShowOnScreen" since="23" />
 		<field name="addToDictionary" since="3" />
+		<field name="autofill" since="26" />
 		<field name="background" />
 		<field name="button1" />
 		<field name="button2" />
@@ -1845,6 +1910,7 @@
 		<field name="tabs" />
 		<field name="text1" />
 		<field name="text2" />
+		<field name="textAssist" since="26" />
 		<field name="title" />
 		<field name="toggle" />
 		<field name="undo" since="23" />
@@ -1944,6 +2010,7 @@
 		<field name="no" />
 		<field name="ok" />
 		<field name="paste" />
+		<field name="paste_as_plain_text" since="26" />
 		<field name="search_go" />
 		<field name="selectAll" />
 		<field name="selectTextMode" since="11" />
@@ -2704,12 +2771,28 @@
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
 	</class>
+	<class name="android/accessibilityservice/AccessibilityButtonController" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="isAccessibilityButtonAvailable()Z" />
+		<method name="registerAccessibilityButtonCallback(Landroid/accessibilityservice/AccessibilityButtonController$AccessibilityButtonCallback;)V" />
+		<method name="registerAccessibilityButtonCallback(Landroid/accessibilityservice/AccessibilityButtonController$AccessibilityButtonCallback;Landroid/os/Handler;)V" />
+		<method name="unregisterAccessibilityButtonCallback(Landroid/accessibilityservice/AccessibilityButtonController$AccessibilityButtonCallback;)V" />
+	</class>
+	<class name="android/accessibilityservice/AccessibilityButtonController$AccessibilityButtonCallback" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="onAvailabilityChanged(Landroid/accessibilityservice/AccessibilityButtonController;Z)V" />
+		<method name="onClicked(Landroid/accessibilityservice/AccessibilityButtonController;)V" />
+	</class>
 	<class name="android/accessibilityservice/AccessibilityService" since="4">
 		<extends name="android/app/Service" />
 		<method name="&lt;init>()V" />
 		<method name="disableSelf()V" since="24" />
 		<method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z" since="24" />
 		<method name="findFocus(I)Landroid/view/accessibility/AccessibilityNodeInfo;" since="21" />
+		<method name="getAccessibilityButtonController()Landroid/accessibilityservice/AccessibilityButtonController;" since="26" />
+		<method name="getFingerprintGestureController()Landroid/accessibilityservice/FingerprintGestureController;" since="26" />
 		<method name="getMagnificationController()Landroid/accessibilityservice/AccessibilityService$MagnificationController;" since="24" />
 		<method name="getRootInActiveWindow()Landroid/view/accessibility/AccessibilityNodeInfo;" since="16" />
 		<method name="getServiceInfo()Landroid/accessibilityservice/AccessibilityServiceInfo;" since="16" />
@@ -2801,10 +2884,12 @@
 		<method name="getResolveInfo()Landroid/content/pm/ResolveInfo;" since="14" />
 		<method name="getSettingsActivityName()Ljava/lang/String;" since="14" />
 		<method name="loadDescription(Landroid/content/pm/PackageManager;)Ljava/lang/String;" since="16" />
+		<method name="loadSummary(Landroid/content/pm/PackageManager;)Ljava/lang/CharSequence;" since="26" />
 		<field name="CAPABILITY_CAN_CONTROL_MAGNIFICATION" since="24" />
 		<field name="CAPABILITY_CAN_PERFORM_GESTURES" since="24" />
-		<field name="CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY" since="18" />
+		<field name="CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY" since="18" deprecated="26" />
 		<field name="CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS" since="18" />
+		<field name="CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES" since="26" />
 		<field name="CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION" since="18" />
 		<field name="CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT" since="18" />
 		<field name="CREATOR" />
@@ -2816,10 +2901,13 @@
 		<field name="FEEDBACK_HAPTIC" />
 		<field name="FEEDBACK_SPOKEN" />
 		<field name="FEEDBACK_VISUAL" />
+		<field name="FLAG_ENABLE_ACCESSIBILITY_VOLUME" since="26" />
 		<field name="FLAG_INCLUDE_NOT_IMPORTANT_VIEWS" since="16" />
 		<field name="FLAG_REPORT_VIEW_IDS" since="18" />
-		<field name="FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY" since="18" />
+		<field name="FLAG_REQUEST_ACCESSIBILITY_BUTTON" since="26" />
+		<field name="FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY" since="18" deprecated="26" />
 		<field name="FLAG_REQUEST_FILTER_KEY_EVENTS" since="18" />
+		<field name="FLAG_REQUEST_FINGERPRINT_GESTURES" since="26" />
 		<field name="FLAG_REQUEST_TOUCH_EXPLORATION_MODE" since="16" />
 		<field name="FLAG_RETRIEVE_INTERACTIVE_WINDOWS" since="21" />
 		<field name="eventTypes" />
@@ -2828,6 +2916,23 @@
 		<field name="notificationTimeout" />
 		<field name="packageNames" />
 	</class>
+	<class name="android/accessibilityservice/FingerprintGestureController" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="isGestureDetectionAvailable()Z" />
+		<method name="registerFingerprintGestureCallback(Landroid/accessibilityservice/FingerprintGestureController$FingerprintGestureCallback;Landroid/os/Handler;)V" />
+		<method name="unregisterFingerprintGestureCallback(Landroid/accessibilityservice/FingerprintGestureController$FingerprintGestureCallback;)V" />
+		<field name="FINGERPRINT_GESTURE_SWIPE_DOWN" />
+		<field name="FINGERPRINT_GESTURE_SWIPE_LEFT" />
+		<field name="FINGERPRINT_GESTURE_SWIPE_RIGHT" />
+		<field name="FINGERPRINT_GESTURE_SWIPE_UP" />
+	</class>
+	<class name="android/accessibilityservice/FingerprintGestureController$FingerprintGestureCallback" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="onGestureDetected(I)V" />
+		<method name="onGestureDetectionAvailabilityChanged(Z)V" />
+	</class>
 	<class name="android/accessibilityservice/GestureDescription" since="24">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
@@ -2845,9 +2950,12 @@
 	<class name="android/accessibilityservice/GestureDescription$StrokeDescription" since="24">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>(Landroid/graphics/Path;JJ)V" />
+		<method name="&lt;init>(Landroid/graphics/Path;JJZ)V" since="26" />
+		<method name="continueStroke(Landroid/graphics/Path;JJZ)Landroid/accessibilityservice/GestureDescription$StrokeDescription;" since="26" />
 		<method name="getDuration()J" />
 		<method name="getPath()Landroid/graphics/Path;" />
 		<method name="getStartTime()J" />
+		<method name="willContinue()Z" since="26" />
 	</class>
 	<class name="android/accounts/AbstractAccountAuthenticator" since="5">
 		<extends name="java/lang/Object" />
@@ -2856,12 +2964,16 @@
 		<method name="addAccountFromCredentials(Landroid/accounts/AccountAuthenticatorResponse;Landroid/accounts/Account;Landroid/os/Bundle;)Landroid/os/Bundle;" since="18" />
 		<method name="confirmCredentials(Landroid/accounts/AccountAuthenticatorResponse;Landroid/accounts/Account;Landroid/os/Bundle;)Landroid/os/Bundle;" />
 		<method name="editProperties(Landroid/accounts/AccountAuthenticatorResponse;Ljava/lang/String;)Landroid/os/Bundle;" />
+		<method name="finishSession(Landroid/accounts/AccountAuthenticatorResponse;Ljava/lang/String;Landroid/os/Bundle;)Landroid/os/Bundle;" since="26" />
 		<method name="getAccountCredentialsForCloning(Landroid/accounts/AccountAuthenticatorResponse;Landroid/accounts/Account;)Landroid/os/Bundle;" since="18" />
 		<method name="getAccountRemovalAllowed(Landroid/accounts/AccountAuthenticatorResponse;Landroid/accounts/Account;)Landroid/os/Bundle;" />
 		<method name="getAuthToken(Landroid/accounts/AccountAuthenticatorResponse;Landroid/accounts/Account;Ljava/lang/String;Landroid/os/Bundle;)Landroid/os/Bundle;" />
 		<method name="getAuthTokenLabel(Ljava/lang/String;)Ljava/lang/String;" />
 		<method name="getIBinder()Landroid/os/IBinder;" />
 		<method name="hasFeatures(Landroid/accounts/AccountAuthenticatorResponse;Landroid/accounts/Account;[Ljava/lang/String;)Landroid/os/Bundle;" />
+		<method name="isCredentialsUpdateSuggested(Landroid/accounts/AccountAuthenticatorResponse;Landroid/accounts/Account;Ljava/lang/String;)Landroid/os/Bundle;" since="26" />
+		<method name="startAddAccountSession(Landroid/accounts/AccountAuthenticatorResponse;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Landroid/os/Bundle;)Landroid/os/Bundle;" since="26" />
+		<method name="startUpdateCredentialsSession(Landroid/accounts/AccountAuthenticatorResponse;Landroid/accounts/Account;Ljava/lang/String;Landroid/os/Bundle;)Landroid/os/Bundle;" since="26" />
 		<method name="updateCredentials(Landroid/accounts/AccountAuthenticatorResponse;Landroid/accounts/Account;Ljava/lang/String;Landroid/os/Bundle;)Landroid/os/Bundle;" />
 		<field name="KEY_CUSTOM_TOKEN_EXPIRY" since="23" />
 	</class>
@@ -2893,13 +3005,18 @@
 		<method name="&lt;init>()V" />
 		<method name="addAccount(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Landroid/os/Bundle;Landroid/app/Activity;Landroid/accounts/AccountManagerCallback;Landroid/os/Handler;)Landroid/accounts/AccountManagerFuture;" />
 		<method name="addAccountExplicitly(Landroid/accounts/Account;Ljava/lang/String;Landroid/os/Bundle;)Z" />
+		<method name="addAccountExplicitly(Landroid/accounts/Account;Ljava/lang/String;Landroid/os/Bundle;Ljava/util/Map;)Z" since="26" />
 		<method name="addOnAccountsUpdatedListener(Landroid/accounts/OnAccountsUpdateListener;Landroid/os/Handler;Z)V" />
+		<method name="addOnAccountsUpdatedListener(Landroid/accounts/OnAccountsUpdateListener;Landroid/os/Handler;Z[Ljava/lang/String;)V" since="26" />
 		<method name="blockingGetAuthToken(Landroid/accounts/Account;Ljava/lang/String;Z)Ljava/lang/String;" />
 		<method name="clearPassword(Landroid/accounts/Account;)V" />
 		<method name="confirmCredentials(Landroid/accounts/Account;Landroid/os/Bundle;Landroid/app/Activity;Landroid/accounts/AccountManagerCallback;Landroid/os/Handler;)Landroid/accounts/AccountManagerFuture;" />
 		<method name="editProperties(Ljava/lang/String;Landroid/app/Activity;Landroid/accounts/AccountManagerCallback;Landroid/os/Handler;)Landroid/accounts/AccountManagerFuture;" />
+		<method name="finishSession(Landroid/os/Bundle;Landroid/app/Activity;Landroid/accounts/AccountManagerCallback;Landroid/os/Handler;)Landroid/accounts/AccountManagerFuture;" since="26" />
 		<method name="get(Landroid/content/Context;)Landroid/accounts/AccountManager;" />
+		<method name="getAccountVisibility(Landroid/accounts/Account;Ljava/lang/String;)I" since="26" />
 		<method name="getAccounts()[Landroid/accounts/Account;" />
+		<method name="getAccountsAndVisibilityForPackage(Ljava/lang/String;Ljava/lang/String;)Ljava/util/Map;" since="26" />
 		<method name="getAccountsByType(Ljava/lang/String;)[Landroid/accounts/Account;" />
 		<method name="getAccountsByTypeAndFeatures(Ljava/lang/String;[Ljava/lang/String;Landroid/accounts/AccountManagerCallback;Landroid/os/Handler;)Landroid/accounts/AccountManagerFuture;" />
 		<method name="getAccountsByTypeForPackage(Ljava/lang/String;Ljava/lang/String;)[Landroid/accounts/Account;" since="18" />
@@ -2908,11 +3025,13 @@
 		<method name="getAuthToken(Landroid/accounts/Account;Ljava/lang/String;ZLandroid/accounts/AccountManagerCallback;Landroid/os/Handler;)Landroid/accounts/AccountManagerFuture;" deprecated="16" />
 		<method name="getAuthTokenByFeatures(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Landroid/app/Activity;Landroid/os/Bundle;Landroid/os/Bundle;Landroid/accounts/AccountManagerCallback;Landroid/os/Handler;)Landroid/accounts/AccountManagerFuture;" />
 		<method name="getAuthenticatorTypes()[Landroid/accounts/AuthenticatorDescription;" />
+		<method name="getPackagesAndVisibilityForAccount(Landroid/accounts/Account;)Ljava/util/Map;" since="26" />
 		<method name="getPassword(Landroid/accounts/Account;)Ljava/lang/String;" />
 		<method name="getPreviousName(Landroid/accounts/Account;)Ljava/lang/String;" since="21" />
 		<method name="getUserData(Landroid/accounts/Account;Ljava/lang/String;)Ljava/lang/String;" />
 		<method name="hasFeatures(Landroid/accounts/Account;[Ljava/lang/String;Landroid/accounts/AccountManagerCallback;Landroid/os/Handler;)Landroid/accounts/AccountManagerFuture;" since="8" />
 		<method name="invalidateAuthToken(Ljava/lang/String;Ljava/lang/String;)V" />
+		<method name="isCredentialsUpdateSuggested(Landroid/accounts/Account;Ljava/lang/String;Landroid/accounts/AccountManagerCallback;Landroid/os/Handler;)Landroid/accounts/AccountManagerFuture;" since="26" />
 		<method name="newChooseAccountIntent(Landroid/accounts/Account;Ljava/util/ArrayList;[Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;[Ljava/lang/String;Landroid/os/Bundle;)Landroid/content/Intent;" since="14" deprecated="23" />
 		<method name="newChooseAccountIntent(Landroid/accounts/Account;Ljava/util/List;[Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Landroid/os/Bundle;)Landroid/content/Intent;" since="23" />
 		<method name="notifyAccountAuthenticated(Landroid/accounts/Account;)Z" since="23" />
@@ -2922,10 +3041,14 @@
 		<method name="removeAccountExplicitly(Landroid/accounts/Account;)Z" since="22" />
 		<method name="removeOnAccountsUpdatedListener(Landroid/accounts/OnAccountsUpdateListener;)V" />
 		<method name="renameAccount(Landroid/accounts/Account;Ljava/lang/String;Landroid/accounts/AccountManagerCallback;Landroid/os/Handler;)Landroid/accounts/AccountManagerFuture;" since="21" />
+		<method name="setAccountVisibility(Landroid/accounts/Account;Ljava/lang/String;I)Z" since="26" />
 		<method name="setAuthToken(Landroid/accounts/Account;Ljava/lang/String;Ljava/lang/String;)V" />
 		<method name="setPassword(Landroid/accounts/Account;Ljava/lang/String;)V" />
 		<method name="setUserData(Landroid/accounts/Account;Ljava/lang/String;Ljava/lang/String;)V" />
+		<method name="startAddAccountSession(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Landroid/os/Bundle;Landroid/app/Activity;Landroid/accounts/AccountManagerCallback;Landroid/os/Handler;)Landroid/accounts/AccountManagerFuture;" since="26" />
+		<method name="startUpdateCredentialsSession(Landroid/accounts/Account;Ljava/lang/String;Landroid/os/Bundle;Landroid/app/Activity;Landroid/accounts/AccountManagerCallback;Landroid/os/Handler;)Landroid/accounts/AccountManagerFuture;" since="26" />
 		<method name="updateCredentials(Landroid/accounts/Account;Ljava/lang/String;Landroid/os/Bundle;Landroid/app/Activity;Landroid/accounts/AccountManagerCallback;Landroid/os/Handler;)Landroid/accounts/AccountManagerFuture;" />
+		<field name="ACTION_ACCOUNT_REMOVED" since="26" />
 		<field name="ACTION_AUTHENTICATOR_INTENT" />
 		<field name="AUTHENTICATOR_ATTRIBUTES_NAME" />
 		<field name="AUTHENTICATOR_META_DATA_NAME" />
@@ -2941,6 +3064,8 @@
 		<field name="KEY_ACCOUNT_AUTHENTICATOR_RESPONSE" />
 		<field name="KEY_ACCOUNT_MANAGER_RESPONSE" />
 		<field name="KEY_ACCOUNT_NAME" />
+		<field name="KEY_ACCOUNT_SESSION_BUNDLE" since="26" />
+		<field name="KEY_ACCOUNT_STATUS_TOKEN" since="26" />
 		<field name="KEY_ACCOUNT_TYPE" />
 		<field name="KEY_ANDROID_PACKAGE_NAME" since="14" />
 		<field name="KEY_AUTHENTICATOR_TYPES" />
@@ -2956,7 +3081,14 @@
 		<field name="KEY_LAST_AUTHENTICATED_TIME" since="23" />
 		<field name="KEY_PASSWORD" />
 		<field name="KEY_USERDATA" />
-		<field name="LOGIN_ACCOUNTS_CHANGED_ACTION" />
+		<field name="LOGIN_ACCOUNTS_CHANGED_ACTION" deprecated="26" />
+		<field name="PACKAGE_NAME_KEY_LEGACY_NOT_VISIBLE" since="26" />
+		<field name="PACKAGE_NAME_KEY_LEGACY_VISIBLE" since="26" />
+		<field name="VISIBILITY_NOT_VISIBLE" since="26" />
+		<field name="VISIBILITY_UNDEFINED" since="26" />
+		<field name="VISIBILITY_USER_MANAGED_NOT_VISIBLE" since="26" />
+		<field name="VISIBILITY_USER_MANAGED_VISIBLE" since="26" />
+		<field name="VISIBILITY_VISIBLE" since="26" />
 	</class>
 	<class name="android/accounts/AccountManagerCallback" since="5">
 		<extends name="java/lang/Object" />
@@ -3052,8 +3184,10 @@
 		<extends name="java/lang/Object" />
 		<method name="onAnimationCancel(Landroid/animation/Animator;)V" />
 		<method name="onAnimationEnd(Landroid/animation/Animator;)V" />
+		<method name="onAnimationEnd(Landroid/animation/Animator;Z)V" since="26" />
 		<method name="onAnimationRepeat(Landroid/animation/Animator;)V" />
 		<method name="onAnimationStart(Landroid/animation/Animator;)V" />
+		<method name="onAnimationStart(Landroid/animation/Animator;Z)V" since="26" />
 	</class>
 	<class name="android/animation/Animator$AnimatorPauseListener" since="19">
 		<extends name="java/lang/Object" />
@@ -3077,11 +3211,14 @@
 		<method name="&lt;init>()V" />
 		<method name="clone()Landroid/animation/AnimatorSet;" />
 		<method name="getChildAnimations()Ljava/util/ArrayList;" />
+		<method name="getCurrentPlayTime()J" since="26" />
 		<method name="play(Landroid/animation/Animator;)Landroid/animation/AnimatorSet$Builder;" />
 		<method name="playSequentially(Ljava/util/List;)V" />
 		<method name="playSequentially([Landroid/animation/Animator;)V" />
 		<method name="playTogether(Ljava/util/Collection;)V" />
 		<method name="playTogether([Landroid/animation/Animator;)V" />
+		<method name="reverse()V" since="26" />
+		<method name="setCurrentPlayTime(J)V" since="26" />
 		<method name="setDuration(J)Landroid/animation/AnimatorSet;" />
 	</class>
 	<class name="android/animation/AnimatorSet$Builder" since="11">
@@ -3304,6 +3441,7 @@
 		<extends name="android/animation/Animator" />
 		<method name="&lt;init>()V" />
 		<method name="addUpdateListener(Landroid/animation/ValueAnimator$AnimatorUpdateListener;)V" />
+		<method name="areAnimatorsEnabled()Z" since="26" />
 		<method name="clone()Landroid/animation/ValueAnimator;" />
 		<method name="getAnimatedFraction()F" since="12" />
 		<method name="getAnimatedValue()Ljava/lang/Object;" />
@@ -3487,7 +3625,8 @@
 		<method name="dismissDialog(I)V" deprecated="16" />
 		<method name="dismissKeyboardShortcutsHelper()V" since="24" />
 		<method name="dump(Ljava/lang/String;Ljava/io/FileDescriptor;Ljava/io/PrintWriter;[Ljava/lang/String;)V" since="11" />
-		<method name="enterPictureInPictureMode()V" since="24" />
+		<method name="enterPictureInPictureMode()V" since="24" deprecated="26" />
+		<method name="enterPictureInPictureMode(Landroid/app/PictureInPictureParams;)Z" since="26" />
 		<method name="findViewById(I)Landroid/view/View;" />
 		<method name="finish()V" />
 		<method name="finishActivity(I)V" />
@@ -3512,6 +3651,7 @@
 		<method name="getLayoutInflater()Landroid/view/LayoutInflater;" />
 		<method name="getLoaderManager()Landroid/app/LoaderManager;" since="11" />
 		<method name="getLocalClassName()Ljava/lang/String;" />
+		<method name="getMaxNumPictureInPictureActions()I" since="26" />
 		<method name="getMediaController()Landroid/media/session/MediaController;" since="21" />
 		<method name="getMenuInflater()Landroid/view/MenuInflater;" />
 		<method name="getParent()Landroid/app/Activity;" />
@@ -3529,6 +3669,7 @@
 		<method name="getWindowManager()Landroid/view/WindowManager;" />
 		<method name="hasWindowFocus()Z" since="3" />
 		<method name="invalidateOptionsMenu()V" since="11" />
+		<method name="isActivityTransitionRunning()Z" since="26" />
 		<method name="isChangingConfigurations()Z" since="11" />
 		<method name="isChild()Z" />
 		<method name="isDestroyed()Z" since="17" />
@@ -3565,14 +3706,16 @@
 		<method name="onKeyShortcut(ILandroid/view/KeyEvent;)Z" since="11" />
 		<method name="onLocalVoiceInteractionStarted()V" since="24" />
 		<method name="onLocalVoiceInteractionStopped()V" since="24" />
-		<method name="onMultiWindowModeChanged(Z)V" since="24" />
+		<method name="onMultiWindowModeChanged(Z)V" since="24" deprecated="26" />
+		<method name="onMultiWindowModeChanged(ZLandroid/content/res/Configuration;)V" since="26" />
 		<method name="onNavigateUp()Z" since="16" />
 		<method name="onNavigateUpFromChild(Landroid/app/Activity;)Z" since="16" />
 		<method name="onNewIntent(Landroid/content/Intent;)V" />
 		<method name="onOptionsItemSelected(Landroid/view/MenuItem;)Z" />
 		<method name="onOptionsMenuClosed(Landroid/view/Menu;)V" />
 		<method name="onPause()V" />
-		<method name="onPictureInPictureModeChanged(Z)V" since="24" />
+		<method name="onPictureInPictureModeChanged(Z)V" since="24" deprecated="26" />
+		<method name="onPictureInPictureModeChanged(ZLandroid/content/res/Configuration;)V" since="26" />
 		<method name="onPostCreate(Landroid/os/Bundle;)V" />
 		<method name="onPostCreate(Landroid/os/Bundle;Landroid/os/PersistableBundle;)V" since="21" />
 		<method name="onPostResume()V" />
@@ -3599,7 +3742,7 @@
 		<method name="onTrackballEvent(Landroid/view/MotionEvent;)Z" />
 		<method name="onUserInteraction()V" since="3" />
 		<method name="onUserLeaveHint()V" since="3" />
-		<method name="onVisibleBehindCanceled()V" since="21" />
+		<method name="onVisibleBehindCanceled()V" since="21" deprecated="26" />
 		<method name="openContextMenu(Landroid/view/View;)V" />
 		<method name="openOptionsMenu()V" />
 		<method name="overridePendingTransition(II)V" since="5" />
@@ -3612,7 +3755,7 @@
 		<method name="requestDragAndDropPermissions(Landroid/view/DragEvent;)Landroid/view/DragAndDropPermissions;" since="24" />
 		<method name="requestPermissions([Ljava/lang/String;I)V" since="23" />
 		<method name="requestShowKeyboardShortcuts()V" since="24" />
-		<method name="requestVisibleBehind(Z)Z" since="21" />
+		<method name="requestVisibleBehind(Z)Z" since="21" deprecated="26" />
 		<method name="requestWindowFeature(I)Z" />
 		<method name="runOnUiThread(Ljava/lang/Runnable;)V" />
 		<method name="setActionBar(Landroid/widget/Toolbar;)V" since="21" />
@@ -3632,6 +3775,7 @@
 		<method name="setIntent(Landroid/content/Intent;)V" />
 		<method name="setMediaController(Landroid/media/session/MediaController;)V" since="21" />
 		<method name="setPersistent(Z)V" />
+		<method name="setPictureInPictureParams(Landroid/app/PictureInPictureParams;)V" since="26" />
 		<method name="setProgress(I)V" deprecated="24" />
 		<method name="setProgressBarIndeterminate(Z)V" deprecated="24" />
 		<method name="setProgressBarIndeterminateVisibility(Z)V" deprecated="24" />
@@ -3719,7 +3863,7 @@
 		<method name="getRecentTasks(II)Ljava/util/List;" deprecated="21" />
 		<method name="getRunningAppProcesses()Ljava/util/List;" since="3" />
 		<method name="getRunningServiceControlPanel(Landroid/content/ComponentName;)Landroid/app/PendingIntent;" since="5" />
-		<method name="getRunningServices(I)Ljava/util/List;" />
+		<method name="getRunningServices(I)Ljava/util/List;" deprecated="26" />
 		<method name="getRunningTasks(I)Ljava/util/List;" deprecated="21" />
 		<method name="isInLockTaskMode()Z" since="21" deprecated="23" />
 		<method name="isLowRamDevice()Z" since="19" />
@@ -3804,12 +3948,14 @@
 		<method name="&lt;init>(Ljava/lang/String;I[Ljava/lang/String;)V" />
 		<method name="readFromParcel(Landroid/os/Parcel;)V" />
 		<field name="CREATOR" />
-		<field name="IMPORTANCE_BACKGROUND" />
-		<field name="IMPORTANCE_EMPTY" />
+		<field name="IMPORTANCE_BACKGROUND" deprecated="26" />
+		<field name="IMPORTANCE_CACHED" since="26" />
+		<field name="IMPORTANCE_EMPTY" deprecated="26" />
 		<field name="IMPORTANCE_FOREGROUND" />
 		<field name="IMPORTANCE_FOREGROUND_SERVICE" since="23" />
 		<field name="IMPORTANCE_GONE" since="21" />
 		<field name="IMPORTANCE_PERCEPTIBLE" since="9" />
+		<field name="IMPORTANCE_PERCEPTIBLE_PRE_26" since="26" />
 		<field name="IMPORTANCE_SERVICE" />
 		<field name="IMPORTANCE_TOP_SLEEPING" since="23" />
 		<field name="IMPORTANCE_VISIBLE" />
@@ -3884,6 +4030,7 @@
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
 		<method name="getLaunchBounds()Landroid/graphics/Rect;" since="24" />
+		<method name="getLaunchDisplayId()I" since="26" />
 		<method name="makeBasic()Landroid/app/ActivityOptions;" since="23" />
 		<method name="makeClipRevealAnimation(Landroid/view/View;IIII)Landroid/app/ActivityOptions;" since="23" />
 		<method name="makeCustomAnimation(Landroid/content/Context;II)Landroid/app/ActivityOptions;" />
@@ -3893,7 +4040,9 @@
 		<method name="makeTaskLaunchBehind()Landroid/app/ActivityOptions;" since="21" />
 		<method name="makeThumbnailScaleUpAnimation(Landroid/view/View;Landroid/graphics/Bitmap;II)Landroid/app/ActivityOptions;" />
 		<method name="requestUsageTimeReport(Landroid/app/PendingIntent;)V" since="23" />
+		<method name="setAppVerificationBundle(Landroid/os/Bundle;)Landroid/app/ActivityOptions;" since="26" />
 		<method name="setLaunchBounds(Landroid/graphics/Rect;)Landroid/app/ActivityOptions;" since="24" />
+		<method name="setLaunchDisplayId(I)Landroid/app/ActivityOptions;" since="26" />
 		<method name="toBundle()Landroid/os/Bundle;" />
 		<method name="update(Landroid/app/ActivityOptions;)V" />
 		<field name="EXTRA_USAGE_TIME_REPORT" since="23" />
@@ -4036,6 +4185,7 @@
 		<field name="MODE_ERRORED" />
 		<field name="MODE_IGNORED" />
 		<field name="OPSTR_ADD_VOICEMAIL" since="23" />
+		<field name="OPSTR_ANSWER_PHONE_CALLS" since="26" />
 		<field name="OPSTR_BODY_SENSORS" since="23" />
 		<field name="OPSTR_CALL_PHONE" since="23" />
 		<field name="OPSTR_CAMERA" since="23" />
@@ -4045,11 +4195,14 @@
 		<field name="OPSTR_MOCK_LOCATION" since="23" />
 		<field name="OPSTR_MONITOR_HIGH_POWER_LOCATION" />
 		<field name="OPSTR_MONITOR_LOCATION" />
+		<field name="OPSTR_PICTURE_IN_PICTURE" since="26" />
+		<field name="OPSTR_PROCESS_OUTGOING_CALLS" since="26" />
 		<field name="OPSTR_READ_CALENDAR" since="23" />
 		<field name="OPSTR_READ_CALL_LOG" since="23" />
 		<field name="OPSTR_READ_CELL_BROADCASTS" since="23" />
 		<field name="OPSTR_READ_CONTACTS" since="23" />
 		<field name="OPSTR_READ_EXTERNAL_STORAGE" since="23" />
+		<field name="OPSTR_READ_PHONE_NUMBERS" since="26" />
 		<field name="OPSTR_READ_PHONE_STATE" since="23" />
 		<field name="OPSTR_READ_SMS" since="23" />
 		<field name="OPSTR_RECEIVE_MMS" since="23" />
@@ -4165,6 +4318,13 @@
 		<field name="durationMillis" />
 		<field name="serviceDetails" />
 	</class>
+	<class name="android/app/AuthenticationRequiredException" since="26">
+		<extends name="java/lang/SecurityException" />
+		<implements name="android/os/Parcelable" />
+		<method name="&lt;init>(Ljava/lang/Throwable;Landroid/app/PendingIntent;)V" />
+		<method name="getUserAction()Landroid/app/PendingIntent;" />
+		<field name="CREATOR" />
+	</class>
 	<class name="android/app/AutomaticZenRule" since="24">
 		<extends name="java/lang/Object" />
 		<implements name="android/os/Parcelable" />
@@ -4402,6 +4562,7 @@
 		<method name="getFragmentManager()Landroid/app/FragmentManager;" />
 		<method name="getHost()Ljava/lang/Object;" since="23" />
 		<method name="getId()I" />
+		<method name="getLayoutInflater()Landroid/view/LayoutInflater;" since="26" />
 		<method name="getLoaderManager()Landroid/app/LoaderManager;" />
 		<method name="getParentFragment()Landroid/app/Fragment;" since="17" />
 		<method name="getReenterTransition()Landroid/transition/Transition;" since="21" />
@@ -4426,6 +4587,7 @@
 		<method name="isInLayout()Z" />
 		<method name="isRemoving()Z" />
 		<method name="isResumed()Z" />
+		<method name="isStateSaved()Z" since="26" />
 		<method name="isVisible()Z" />
 		<method name="onActivityCreated(Landroid/os/Bundle;)V" />
 		<method name="onActivityResult(IILandroid/content/Intent;)V" />
@@ -4441,15 +4603,18 @@
 		<method name="onDestroyOptionsMenu()V" />
 		<method name="onDestroyView()V" />
 		<method name="onDetach()V" />
+		<method name="onGetLayoutInflater(Landroid/os/Bundle;)Landroid/view/LayoutInflater;" since="26" />
 		<method name="onHiddenChanged(Z)V" />
 		<method name="onInflate(Landroid/app/Activity;Landroid/util/AttributeSet;Landroid/os/Bundle;)V" since="12" deprecated="23" />
 		<method name="onInflate(Landroid/content/Context;Landroid/util/AttributeSet;Landroid/os/Bundle;)V" since="23" />
 		<method name="onInflate(Landroid/util/AttributeSet;Landroid/os/Bundle;)V" deprecated="16" />
-		<method name="onMultiWindowModeChanged(Z)V" since="24" />
+		<method name="onMultiWindowModeChanged(Z)V" since="24" deprecated="26" />
+		<method name="onMultiWindowModeChanged(ZLandroid/content/res/Configuration;)V" since="26" />
 		<method name="onOptionsItemSelected(Landroid/view/MenuItem;)Z" />
 		<method name="onOptionsMenuClosed(Landroid/view/Menu;)V" />
 		<method name="onPause()V" />
-		<method name="onPictureInPictureModeChanged(Z)V" since="24" />
+		<method name="onPictureInPictureModeChanged(Z)V" since="24" deprecated="26" />
+		<method name="onPictureInPictureModeChanged(ZLandroid/content/res/Configuration;)V" since="26" />
 		<method name="onPrepareOptionsMenu(Landroid/view/Menu;)V" />
 		<method name="onRequestPermissionsResult(I[Ljava/lang/String;[I)V" since="23" />
 		<method name="onResume()V" />
@@ -4458,6 +4623,7 @@
 		<method name="onStop()V" />
 		<method name="onViewCreated(Landroid/view/View;Landroid/os/Bundle;)V" since="13" />
 		<method name="onViewStateRestored(Landroid/os/Bundle;)V" since="17" />
+		<method name="postponeEnterTransition()V" since="26" />
 		<method name="registerForContextMenu(Landroid/view/View;)V" />
 		<method name="requestPermissions([Ljava/lang/String;I)V" since="23" />
 		<method name="setAllowEnterTransitionOverlap(Z)V" since="21" />
@@ -4483,6 +4649,7 @@
 		<method name="startActivityForResult(Landroid/content/Intent;I)V" />
 		<method name="startActivityForResult(Landroid/content/Intent;ILandroid/os/Bundle;)V" since="16" />
 		<method name="startIntentSenderForResult(Landroid/content/IntentSender;ILandroid/content/Intent;IIILandroid/os/Bundle;)V" since="24" />
+		<method name="startPostponedEnterTransition()V" since="26" />
 		<method name="unregisterForContextMenu(Landroid/view/View;)V" />
 	</class>
 	<class name="android/app/Fragment$InstantiationException" since="11">
@@ -4530,11 +4697,13 @@
 		<method name="dispatchDestroy()V" />
 		<method name="dispatchDestroyView()V" />
 		<method name="dispatchLowMemory()V" />
-		<method name="dispatchMultiWindowModeChanged(Z)V" since="24" />
+		<method name="dispatchMultiWindowModeChanged(Z)V" since="24" deprecated="26" />
+		<method name="dispatchMultiWindowModeChanged(ZLandroid/content/res/Configuration;)V" since="26" />
 		<method name="dispatchOptionsItemSelected(Landroid/view/MenuItem;)Z" />
 		<method name="dispatchOptionsMenuClosed(Landroid/view/Menu;)V" />
 		<method name="dispatchPause()V" />
-		<method name="dispatchPictureInPictureModeChanged(Z)V" since="24" />
+		<method name="dispatchPictureInPictureModeChanged(Z)V" since="24" deprecated="26" />
+		<method name="dispatchPictureInPictureModeChanged(ZLandroid/content/res/Configuration;)V" since="26" />
 		<method name="dispatchPrepareOptionsMenu(Landroid/view/Menu;)Z" />
 		<method name="dispatchResume()V" />
 		<method name="dispatchStart()V" />
@@ -4588,8 +4757,11 @@
 		<method name="getBackStackEntryAt(I)Landroid/app/FragmentManager$BackStackEntry;" />
 		<method name="getBackStackEntryCount()I" />
 		<method name="getFragment(Landroid/os/Bundle;Ljava/lang/String;)Landroid/app/Fragment;" />
+		<method name="getFragments()Ljava/util/List;" since="26" />
+		<method name="getPrimaryNavigationFragment()Landroid/app/Fragment;" since="26" />
 		<method name="invalidateOptionsMenu()V" since="14" />
 		<method name="isDestroyed()Z" since="17" />
+		<method name="isStateSaved()Z" since="26" />
 		<method name="popBackStack()V" />
 		<method name="popBackStack(II)V" />
 		<method name="popBackStack(Ljava/lang/String;I)V" />
@@ -4597,8 +4769,10 @@
 		<method name="popBackStackImmediate(II)Z" />
 		<method name="popBackStackImmediate(Ljava/lang/String;I)Z" />
 		<method name="putFragment(Landroid/os/Bundle;Ljava/lang/String;Landroid/app/Fragment;)V" />
+		<method name="registerFragmentLifecycleCallbacks(Landroid/app/FragmentManager$FragmentLifecycleCallbacks;Z)V" since="26" />
 		<method name="removeOnBackStackChangedListener(Landroid/app/FragmentManager$OnBackStackChangedListener;)V" />
 		<method name="saveFragmentInstanceState(Landroid/app/Fragment;)Landroid/app/Fragment$SavedState;" since="13" />
+		<method name="unregisterFragmentLifecycleCallbacks(Landroid/app/FragmentManager$FragmentLifecycleCallbacks;)V" since="26" />
 		<field name="POP_BACK_STACK_INCLUSIVE" />
 	</class>
 	<class name="android/app/FragmentManager$BackStackEntry" since="11">
@@ -4610,6 +4784,24 @@
 		<method name="getId()I" />
 		<method name="getName()Ljava/lang/String;" since="14" />
 	</class>
+	<class name="android/app/FragmentManager$FragmentLifecycleCallbacks" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="onFragmentActivityCreated(Landroid/app/FragmentManager;Landroid/app/Fragment;Landroid/os/Bundle;)V" />
+		<method name="onFragmentAttached(Landroid/app/FragmentManager;Landroid/app/Fragment;Landroid/content/Context;)V" />
+		<method name="onFragmentCreated(Landroid/app/FragmentManager;Landroid/app/Fragment;Landroid/os/Bundle;)V" />
+		<method name="onFragmentDestroyed(Landroid/app/FragmentManager;Landroid/app/Fragment;)V" />
+		<method name="onFragmentDetached(Landroid/app/FragmentManager;Landroid/app/Fragment;)V" />
+		<method name="onFragmentPaused(Landroid/app/FragmentManager;Landroid/app/Fragment;)V" />
+		<method name="onFragmentPreAttached(Landroid/app/FragmentManager;Landroid/app/Fragment;Landroid/content/Context;)V" />
+		<method name="onFragmentPreCreated(Landroid/app/FragmentManager;Landroid/app/Fragment;Landroid/os/Bundle;)V" />
+		<method name="onFragmentResumed(Landroid/app/FragmentManager;Landroid/app/Fragment;)V" />
+		<method name="onFragmentSaveInstanceState(Landroid/app/FragmentManager;Landroid/app/Fragment;Landroid/os/Bundle;)V" />
+		<method name="onFragmentStarted(Landroid/app/FragmentManager;Landroid/app/Fragment;)V" />
+		<method name="onFragmentStopped(Landroid/app/FragmentManager;Landroid/app/Fragment;)V" />
+		<method name="onFragmentViewCreated(Landroid/app/FragmentManager;Landroid/app/Fragment;Landroid/view/View;Landroid/os/Bundle;)V" />
+		<method name="onFragmentViewDestroyed(Landroid/app/FragmentManager;Landroid/app/Fragment;)V" />
+	</class>
 	<class name="android/app/FragmentManager$OnBackStackChangedListener" since="11">
 		<extends name="java/lang/Object" />
 		<method name="onBackStackChanged()V" />
@@ -4639,12 +4831,15 @@
 		<method name="remove(Landroid/app/Fragment;)Landroid/app/FragmentTransaction;" />
 		<method name="replace(ILandroid/app/Fragment;)Landroid/app/FragmentTransaction;" />
 		<method name="replace(ILandroid/app/Fragment;Ljava/lang/String;)Landroid/app/FragmentTransaction;" />
+		<method name="runOnCommit(Ljava/lang/Runnable;)Landroid/app/FragmentTransaction;" since="26" />
 		<method name="setBreadCrumbShortTitle(I)Landroid/app/FragmentTransaction;" />
 		<method name="setBreadCrumbShortTitle(Ljava/lang/CharSequence;)Landroid/app/FragmentTransaction;" />
 		<method name="setBreadCrumbTitle(I)Landroid/app/FragmentTransaction;" />
 		<method name="setBreadCrumbTitle(Ljava/lang/CharSequence;)Landroid/app/FragmentTransaction;" />
 		<method name="setCustomAnimations(II)Landroid/app/FragmentTransaction;" />
 		<method name="setCustomAnimations(IIII)Landroid/app/FragmentTransaction;" since="13" />
+		<method name="setPrimaryNavigationFragment(Landroid/app/Fragment;)Landroid/app/FragmentTransaction;" since="26" />
+		<method name="setReorderingAllowed(Z)Landroid/app/FragmentTransaction;" since="26" />
 		<method name="setTransition(I)Landroid/app/FragmentTransaction;" />
 		<method name="setTransitionStyle(I)Landroid/app/FragmentTransaction;" />
 		<method name="show(Landroid/app/Fragment;)Landroid/app/FragmentTransaction;" />
@@ -4659,9 +4854,11 @@
 	<class name="android/app/Instrumentation" since="1">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
+		<method name="acquireLooperManager(Landroid/os/Looper;)Landroid/os/TestLooperManager;" since="26" />
 		<method name="addMonitor(Landroid/app/Instrumentation$ActivityMonitor;)V" />
 		<method name="addMonitor(Landroid/content/IntentFilter;Landroid/app/Instrumentation$ActivityResult;Z)Landroid/app/Instrumentation$ActivityMonitor;" />
 		<method name="addMonitor(Ljava/lang/String;Landroid/app/Instrumentation$ActivityResult;Z)Landroid/app/Instrumentation$ActivityMonitor;" />
+		<method name="addResults(Landroid/os/Bundle;)V" since="26" />
 		<method name="callActivityOnCreate(Landroid/app/Activity;Landroid/os/Bundle;)V" />
 		<method name="callActivityOnCreate(Landroid/app/Activity;Landroid/os/Bundle;Landroid/os/PersistableBundle;)V" since="21" />
 		<method name="callActivityOnDestroy(Landroid/app/Activity;)V" />
@@ -4686,6 +4883,7 @@
 		<method name="getBinderCounts()Landroid/os/Bundle;" />
 		<method name="getComponentName()Landroid/content/ComponentName;" />
 		<method name="getContext()Landroid/content/Context;" />
+		<method name="getProcessName()Ljava/lang/String;" since="26" />
 		<method name="getTargetContext()Landroid/content/Context;" />
 		<method name="getUiAutomation()Landroid/app/UiAutomation;" since="18" />
 		<method name="getUiAutomation(I)Landroid/app/UiAutomation;" since="24" />
@@ -4727,6 +4925,7 @@
 	</class>
 	<class name="android/app/Instrumentation$ActivityMonitor" since="1">
 		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" since="26" />
 		<method name="&lt;init>(Landroid/content/IntentFilter;Landroid/app/Instrumentation$ActivityResult;Z)V" />
 		<method name="&lt;init>(Ljava/lang/String;Landroid/app/Instrumentation$ActivityResult;Z)V" />
 		<method name="getFilter()Landroid/content/IntentFilter;" />
@@ -4734,6 +4933,7 @@
 		<method name="getLastActivity()Landroid/app/Activity;" />
 		<method name="getResult()Landroid/app/Instrumentation$ActivityResult;" />
 		<method name="isBlocking()Z" />
+		<method name="onStartActivity(Landroid/content/Intent;)Landroid/app/Instrumentation$ActivityResult;" since="26" />
 		<method name="waitForActivity()Landroid/app/Activity;" />
 		<method name="waitForActivityWithTimeout(J)Landroid/app/Activity;" />
 	</class>
@@ -4760,6 +4960,14 @@
 		<method name="isKeyguardLocked()Z" since="16" />
 		<method name="isKeyguardSecure()Z" since="16" />
 		<method name="newKeyguardLock(Ljava/lang/String;)Landroid/app/KeyguardManager$KeyguardLock;" deprecated="16" />
+		<method name="requestDismissKeyguard(Landroid/app/Activity;Landroid/app/KeyguardManager$KeyguardDismissCallback;)V" since="26" />
+	</class>
+	<class name="android/app/KeyguardManager$KeyguardDismissCallback" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="onDismissCancelled()V" />
+		<method name="onDismissError()V" />
+		<method name="onDismissSucceeded()V" />
 	</class>
 	<class name="android/app/KeyguardManager$KeyguardLock" since="1" deprecated="16">
 		<extends name="java/lang/Object" />
@@ -4767,7 +4975,7 @@
 		<method name="disableKeyguard()V" />
 		<method name="reenableKeyguard()V" />
 	</class>
-	<class name="android/app/KeyguardManager$OnKeyguardExitResult" since="1">
+	<class name="android/app/KeyguardManager$OnKeyguardExitResult" since="1" deprecated="26">
 		<extends name="java/lang/Object" />
 		<method name="onKeyguardExitResult(Z)V" />
 	</class>
@@ -4886,12 +5094,21 @@
 		<method name="&lt;init>(ILjava/lang/CharSequence;J)V" deprecated="16" />
 		<method name="&lt;init>(Landroid/os/Parcel;)V" />
 		<method name="clone()Landroid/app/Notification;" since="9" />
+		<method name="getBadgeIconType()I" since="26" />
+		<method name="getChannelId()Ljava/lang/String;" since="26" />
 		<method name="getGroup()Ljava/lang/String;" since="20" />
+		<method name="getGroupAlertBehavior()I" since="26" />
 		<method name="getLargeIcon()Landroid/graphics/drawable/Icon;" since="23" />
+		<method name="getSettingsText()Ljava/lang/CharSequence;" since="26" />
+		<method name="getShortcutId()Ljava/lang/String;" since="26" />
 		<method name="getSmallIcon()Landroid/graphics/drawable/Icon;" since="23" />
 		<method name="getSortKey()Ljava/lang/String;" since="20" />
+		<method name="getTimeoutAfter()J" since="26" />
 		<method name="setLatestEventInfo(Landroid/content/Context;Ljava/lang/CharSequence;Ljava/lang/CharSequence;Landroid/app/PendingIntent;)V" deprecated="16" />
 		<field name="AUDIO_ATTRIBUTES_DEFAULT" since="21" />
+		<field name="BADGE_ICON_LARGE" since="26" />
+		<field name="BADGE_ICON_NONE" since="26" />
+		<field name="BADGE_ICON_SMALL" since="26" />
 		<field name="CATEGORY_ALARM" since="21" />
 		<field name="CATEGORY_CALL" since="21" />
 		<field name="CATEGORY_EMAIL" since="21" />
@@ -4913,16 +5130,22 @@
 		<field name="DEFAULT_LIGHTS" />
 		<field name="DEFAULT_SOUND" />
 		<field name="DEFAULT_VIBRATE" />
+		<field name="EXTRA_AUDIO_CONTENTS_URI" since="26" />
 		<field name="EXTRA_BACKGROUND_IMAGE_URI" since="21" />
 		<field name="EXTRA_BIG_TEXT" since="21" />
+		<field name="EXTRA_CHANNEL_ID" since="26" />
 		<field name="EXTRA_CHRONOMETER_COUNT_DOWN" since="24" />
+		<field name="EXTRA_COLORIZED" since="26" />
 		<field name="EXTRA_COMPACT_ACTIONS" since="21" />
 		<field name="EXTRA_CONVERSATION_TITLE" since="24" />
+		<field name="EXTRA_HISTORIC_MESSAGES" since="26" />
 		<field name="EXTRA_INFO_TEXT" since="19" />
-		<field name="EXTRA_LARGE_ICON" since="19" />
+		<field name="EXTRA_LARGE_ICON" since="19" deprecated="26" />
 		<field name="EXTRA_LARGE_ICON_BIG" since="19" />
 		<field name="EXTRA_MEDIA_SESSION" since="21" />
 		<field name="EXTRA_MESSAGES" since="24" />
+		<field name="EXTRA_NOTIFICATION_ID" since="26" />
+		<field name="EXTRA_NOTIFICATION_TAG" since="26" />
 		<field name="EXTRA_PEOPLE" since="19" />
 		<field name="EXTRA_PICTURE" since="19" />
 		<field name="EXTRA_PROGRESS" since="19" />
@@ -4932,7 +5155,7 @@
 		<field name="EXTRA_SELF_DISPLAY_NAME" since="24" />
 		<field name="EXTRA_SHOW_CHRONOMETER" since="19" />
 		<field name="EXTRA_SHOW_WHEN" since="19" />
-		<field name="EXTRA_SMALL_ICON" since="19" />
+		<field name="EXTRA_SMALL_ICON" since="19" deprecated="26" />
 		<field name="EXTRA_SUB_TEXT" since="19" />
 		<field name="EXTRA_SUMMARY_TEXT" since="19" />
 		<field name="EXTRA_TEMPLATE" since="21" />
@@ -4949,26 +5172,29 @@
 		<field name="FLAG_NO_CLEAR" />
 		<field name="FLAG_ONGOING_EVENT" />
 		<field name="FLAG_ONLY_ALERT_ONCE" />
-		<field name="FLAG_SHOW_LIGHTS" />
+		<field name="FLAG_SHOW_LIGHTS" deprecated="26" />
+		<field name="GROUP_ALERT_ALL" since="26" />
+		<field name="GROUP_ALERT_CHILDREN" since="26" />
+		<field name="GROUP_ALERT_SUMMARY" since="26" />
 		<field name="INTENT_CATEGORY_NOTIFICATION_PREFERENCES" since="21" />
-		<field name="PRIORITY_DEFAULT" since="16" />
-		<field name="PRIORITY_HIGH" since="16" />
-		<field name="PRIORITY_LOW" since="16" />
-		<field name="PRIORITY_MAX" since="16" />
-		<field name="PRIORITY_MIN" since="16" />
+		<field name="PRIORITY_DEFAULT" since="16" deprecated="26" />
+		<field name="PRIORITY_HIGH" since="16" deprecated="26" />
+		<field name="PRIORITY_LOW" since="16" deprecated="26" />
+		<field name="PRIORITY_MAX" since="16" deprecated="26" />
+		<field name="PRIORITY_MIN" since="16" deprecated="26" />
 		<field name="STREAM_DEFAULT" deprecated="21" />
 		<field name="VISIBILITY_PRIVATE" since="21" />
 		<field name="VISIBILITY_PUBLIC" since="21" />
 		<field name="VISIBILITY_SECRET" since="21" />
 		<field name="actions" since="19" />
-		<field name="audioAttributes" since="21" />
+		<field name="audioAttributes" since="21" deprecated="26" />
 		<field name="audioStreamType" deprecated="21" />
 		<field name="bigContentView" since="16" deprecated="24" />
 		<field name="category" since="21" />
 		<field name="color" since="21" />
 		<field name="contentIntent" />
 		<field name="contentView" deprecated="24" />
-		<field name="defaults" />
+		<field name="defaults" deprecated="26" />
 		<field name="deleteIntent" />
 		<field name="extras" since="19" />
 		<field name="flags" />
@@ -4977,16 +5203,16 @@
 		<field name="icon" deprecated="23" />
 		<field name="iconLevel" />
 		<field name="largeIcon" since="11" deprecated="23" />
-		<field name="ledARGB" />
-		<field name="ledOffMS" />
-		<field name="ledOnMS" />
+		<field name="ledARGB" deprecated="26" />
+		<field name="ledOffMS" deprecated="26" />
+		<field name="ledOnMS" deprecated="26" />
 		<field name="number" deprecated="24" />
-		<field name="priority" since="16" />
+		<field name="priority" since="16" deprecated="26" />
 		<field name="publicVersion" since="21" />
-		<field name="sound" />
+		<field name="sound" deprecated="26" />
 		<field name="tickerText" />
 		<field name="tickerView" since="11" deprecated="21" />
-		<field name="vibrate" />
+		<field name="vibrate" deprecated="26" />
 		<field name="visibility" since="21" />
 		<field name="when" />
 	</class>
@@ -4996,6 +5222,7 @@
 		<method name="&lt;init>(ILjava/lang/CharSequence;Landroid/app/PendingIntent;)V" deprecated="23" />
 		<method name="clone()Landroid/app/Notification$Action;" />
 		<method name="getAllowGeneratedReplies()Z" since="24" />
+		<method name="getDataOnlyRemoteInputs()[Landroid/app/RemoteInput;" since="26" />
 		<method name="getExtras()Landroid/os/Bundle;" since="20" />
 		<method name="getIcon()Landroid/graphics/drawable/Icon;" since="23" />
 		<method name="getRemoteInputs()[Landroid/app/RemoteInput;" since="20" />
@@ -5059,7 +5286,8 @@
 	</class>
 	<class name="android/app/Notification$Builder" since="11">
 		<extends name="java/lang/Object" />
-		<method name="&lt;init>(Landroid/content/Context;)V" />
+		<method name="&lt;init>(Landroid/content/Context;)V" deprecated="26" />
+		<method name="&lt;init>(Landroid/content/Context;Ljava/lang/String;)V" since="26" />
 		<method name="addAction(ILjava/lang/CharSequence;Landroid/app/PendingIntent;)Landroid/app/Notification$Builder;" since="16" deprecated="23" />
 		<method name="addAction(Landroid/app/Notification$Action;)Landroid/app/Notification$Builder;" since="20" />
 		<method name="addExtras(Landroid/os/Bundle;)Landroid/app/Notification$Builder;" since="20" />
@@ -5074,9 +5302,12 @@
 		<method name="recoverBuilder(Landroid/content/Context;Landroid/app/Notification;)Landroid/app/Notification$Builder;" since="24" />
 		<method name="setActions([Landroid/app/Notification$Action;)Landroid/app/Notification$Builder;" since="24" />
 		<method name="setAutoCancel(Z)Landroid/app/Notification$Builder;" />
+		<method name="setBadgeIconType(I)Landroid/app/Notification$Builder;" since="26" />
 		<method name="setCategory(Ljava/lang/String;)Landroid/app/Notification$Builder;" since="21" />
+		<method name="setChannelId(Ljava/lang/String;)Landroid/app/Notification$Builder;" since="26" />
 		<method name="setChronometerCountDown(Z)Landroid/app/Notification$Builder;" since="24" />
 		<method name="setColor(I)Landroid/app/Notification$Builder;" since="21" />
+		<method name="setColorized(Z)Landroid/app/Notification$Builder;" since="26" />
 		<method name="setContent(Landroid/widget/RemoteViews;)Landroid/app/Notification$Builder;" deprecated="24" />
 		<method name="setContentInfo(Ljava/lang/CharSequence;)Landroid/app/Notification$Builder;" deprecated="24" />
 		<method name="setContentIntent(Landroid/app/PendingIntent;)Landroid/app/Notification$Builder;" />
@@ -5085,37 +5316,41 @@
 		<method name="setCustomBigContentView(Landroid/widget/RemoteViews;)Landroid/app/Notification$Builder;" since="24" />
 		<method name="setCustomContentView(Landroid/widget/RemoteViews;)Landroid/app/Notification$Builder;" since="24" />
 		<method name="setCustomHeadsUpContentView(Landroid/widget/RemoteViews;)Landroid/app/Notification$Builder;" since="24" />
-		<method name="setDefaults(I)Landroid/app/Notification$Builder;" />
+		<method name="setDefaults(I)Landroid/app/Notification$Builder;" deprecated="26" />
 		<method name="setDeleteIntent(Landroid/app/PendingIntent;)Landroid/app/Notification$Builder;" />
 		<method name="setExtras(Landroid/os/Bundle;)Landroid/app/Notification$Builder;" since="19" />
 		<method name="setFullScreenIntent(Landroid/app/PendingIntent;Z)Landroid/app/Notification$Builder;" />
 		<method name="setGroup(Ljava/lang/String;)Landroid/app/Notification$Builder;" since="20" />
+		<method name="setGroupAlertBehavior(I)Landroid/app/Notification$Builder;" since="26" />
 		<method name="setGroupSummary(Z)Landroid/app/Notification$Builder;" since="20" />
 		<method name="setLargeIcon(Landroid/graphics/Bitmap;)Landroid/app/Notification$Builder;" />
 		<method name="setLargeIcon(Landroid/graphics/drawable/Icon;)Landroid/app/Notification$Builder;" since="23" />
-		<method name="setLights(III)Landroid/app/Notification$Builder;" />
+		<method name="setLights(III)Landroid/app/Notification$Builder;" deprecated="26" />
 		<method name="setLocalOnly(Z)Landroid/app/Notification$Builder;" since="20" />
 		<method name="setNumber(I)Landroid/app/Notification$Builder;" deprecated="24" />
 		<method name="setOngoing(Z)Landroid/app/Notification$Builder;" />
 		<method name="setOnlyAlertOnce(Z)Landroid/app/Notification$Builder;" />
-		<method name="setPriority(I)Landroid/app/Notification$Builder;" since="16" />
+		<method name="setPriority(I)Landroid/app/Notification$Builder;" since="16" deprecated="26" />
 		<method name="setProgress(IIZ)Landroid/app/Notification$Builder;" since="14" />
 		<method name="setPublicVersion(Landroid/app/Notification;)Landroid/app/Notification$Builder;" since="21" />
 		<method name="setRemoteInputHistory([Ljava/lang/CharSequence;)Landroid/app/Notification$Builder;" since="24" />
+		<method name="setSettingsText(Ljava/lang/CharSequence;)Landroid/app/Notification$Builder;" since="26" />
+		<method name="setShortcutId(Ljava/lang/String;)Landroid/app/Notification$Builder;" since="26" />
 		<method name="setShowWhen(Z)Landroid/app/Notification$Builder;" since="17" />
 		<method name="setSmallIcon(I)Landroid/app/Notification$Builder;" />
 		<method name="setSmallIcon(II)Landroid/app/Notification$Builder;" />
 		<method name="setSmallIcon(Landroid/graphics/drawable/Icon;)Landroid/app/Notification$Builder;" since="23" />
 		<method name="setSortKey(Ljava/lang/String;)Landroid/app/Notification$Builder;" since="20" />
-		<method name="setSound(Landroid/net/Uri;)Landroid/app/Notification$Builder;" />
+		<method name="setSound(Landroid/net/Uri;)Landroid/app/Notification$Builder;" deprecated="26" />
 		<method name="setSound(Landroid/net/Uri;I)Landroid/app/Notification$Builder;" deprecated="21" />
-		<method name="setSound(Landroid/net/Uri;Landroid/media/AudioAttributes;)Landroid/app/Notification$Builder;" since="21" />
+		<method name="setSound(Landroid/net/Uri;Landroid/media/AudioAttributes;)Landroid/app/Notification$Builder;" since="21" deprecated="26" />
 		<method name="setStyle(Landroid/app/Notification$Style;)Landroid/app/Notification$Builder;" since="16" />
 		<method name="setSubText(Ljava/lang/CharSequence;)Landroid/app/Notification$Builder;" since="16" />
 		<method name="setTicker(Ljava/lang/CharSequence;)Landroid/app/Notification$Builder;" />
 		<method name="setTicker(Ljava/lang/CharSequence;Landroid/widget/RemoteViews;)Landroid/app/Notification$Builder;" deprecated="21" />
+		<method name="setTimeoutAfter(J)Landroid/app/Notification$Builder;" since="26" />
 		<method name="setUsesChronometer(Z)Landroid/app/Notification$Builder;" since="16" />
-		<method name="setVibrate([J)Landroid/app/Notification$Builder;" />
+		<method name="setVibrate([J)Landroid/app/Notification$Builder;" deprecated="26" />
 		<method name="setVisibility(I)Landroid/app/Notification$Builder;" since="21" />
 		<method name="setWhen(J)Landroid/app/Notification$Builder;" />
 	</class>
@@ -5181,9 +5416,11 @@
 	<class name="android/app/Notification$MessagingStyle" since="24">
 		<extends name="android/app/Notification$Style" />
 		<method name="&lt;init>(Ljava/lang/CharSequence;)V" />
+		<method name="addHistoricMessage(Landroid/app/Notification$MessagingStyle$Message;)Landroid/app/Notification$MessagingStyle;" since="26" />
 		<method name="addMessage(Landroid/app/Notification$MessagingStyle$Message;)Landroid/app/Notification$MessagingStyle;" />
 		<method name="addMessage(Ljava/lang/CharSequence;JLjava/lang/CharSequence;)Landroid/app/Notification$MessagingStyle;" />
 		<method name="getConversationTitle()Ljava/lang/CharSequence;" />
+		<method name="getHistoricMessages()Ljava/util/List;" since="26" />
 		<method name="getMessages()Ljava/util/List;" />
 		<method name="getUserDisplayName()Ljava/lang/CharSequence;" />
 		<method name="setConversationTitle(Ljava/lang/CharSequence;)Landroid/app/Notification$MessagingStyle;" />
@@ -5194,6 +5431,7 @@
 		<method name="&lt;init>(Ljava/lang/CharSequence;JLjava/lang/CharSequence;)V" />
 		<method name="getDataMimeType()Ljava/lang/String;" />
 		<method name="getDataUri()Landroid/net/Uri;" />
+		<method name="getExtras()Landroid/os/Bundle;" since="26" />
 		<method name="getSender()Ljava/lang/CharSequence;" />
 		<method name="getText()Ljava/lang/CharSequence;" />
 		<method name="getTimestamp()J" />
@@ -5224,6 +5462,7 @@
 		<method name="clone()Landroid/app/Notification$WearableExtender;" />
 		<method name="getActions()Ljava/util/List;" />
 		<method name="getBackground()Landroid/graphics/Bitmap;" />
+		<method name="getBridgeTag()Ljava/lang/String;" since="26" />
 		<method name="getContentAction()I" />
 		<method name="getContentIcon()I" />
 		<method name="getContentIconGravity()I" />
@@ -5242,6 +5481,7 @@
 		<method name="getPages()Ljava/util/List;" />
 		<method name="getStartScrollBottom()Z" />
 		<method name="setBackground(Landroid/graphics/Bitmap;)Landroid/app/Notification$WearableExtender;" />
+		<method name="setBridgeTag(Ljava/lang/String;)Landroid/app/Notification$WearableExtender;" since="26" />
 		<method name="setContentAction(I)Landroid/app/Notification$WearableExtender;" />
 		<method name="setContentIcon(I)Landroid/app/Notification$WearableExtender;" />
 		<method name="setContentIconGravity(I)Landroid/app/Notification$WearableExtender;" />
@@ -5268,6 +5508,49 @@
 		<field name="SIZE_XSMALL" />
 		<field name="UNSET_ACTION_INDEX" />
 	</class>
+	<class name="android/app/NotificationChannel" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/os/Parcelable" />
+		<method name="&lt;init>(Ljava/lang/String;Ljava/lang/CharSequence;I)V" />
+		<method name="canBypassDnd()Z" />
+		<method name="canShowBadge()Z" />
+		<method name="enableLights(Z)V" />
+		<method name="enableVibration(Z)V" />
+		<method name="getAudioAttributes()Landroid/media/AudioAttributes;" />
+		<method name="getDescription()Ljava/lang/String;" />
+		<method name="getGroup()Ljava/lang/String;" />
+		<method name="getId()Ljava/lang/String;" />
+		<method name="getImportance()I" />
+		<method name="getLightColor()I" />
+		<method name="getLockscreenVisibility()I" />
+		<method name="getName()Ljava/lang/CharSequence;" />
+		<method name="getSound()Landroid/net/Uri;" />
+		<method name="getVibrationPattern()[J" />
+		<method name="setBypassDnd(Z)V" />
+		<method name="setDescription(Ljava/lang/String;)V" />
+		<method name="setGroup(Ljava/lang/String;)V" />
+		<method name="setImportance(I)V" />
+		<method name="setLightColor(I)V" />
+		<method name="setLockscreenVisibility(I)V" />
+		<method name="setName(Ljava/lang/CharSequence;)V" />
+		<method name="setShowBadge(Z)V" />
+		<method name="setSound(Landroid/net/Uri;Landroid/media/AudioAttributes;)V" />
+		<method name="setVibrationPattern([J)V" />
+		<method name="shouldShowLights()Z" />
+		<method name="shouldVibrate()Z" />
+		<field name="CREATOR" />
+		<field name="DEFAULT_CHANNEL_ID" />
+	</class>
+	<class name="android/app/NotificationChannelGroup" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/os/Parcelable" />
+		<method name="&lt;init>(Ljava/lang/String;Ljava/lang/CharSequence;)V" />
+		<method name="clone()Landroid/app/NotificationChannelGroup;" />
+		<method name="getChannels()Ljava/util/List;" />
+		<method name="getId()Ljava/lang/String;" />
+		<method name="getName()Ljava/lang/CharSequence;" />
+		<field name="CREATOR" />
+	</class>
 	<class name="android/app/NotificationManager" since="1">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
@@ -5276,11 +5559,20 @@
 		<method name="cancel(I)V" />
 		<method name="cancel(Ljava/lang/String;I)V" since="5" />
 		<method name="cancelAll()V" />
+		<method name="createNotificationChannel(Landroid/app/NotificationChannel;)V" since="26" />
+		<method name="createNotificationChannelGroup(Landroid/app/NotificationChannelGroup;)V" since="26" />
+		<method name="createNotificationChannelGroups(Ljava/util/List;)V" since="26" />
+		<method name="createNotificationChannels(Ljava/util/List;)V" since="26" />
+		<method name="deleteNotificationChannel(Ljava/lang/String;)V" since="26" />
+		<method name="deleteNotificationChannelGroup(Ljava/lang/String;)V" since="26" />
 		<method name="getActiveNotifications()[Landroid/service/notification/StatusBarNotification;" since="23" />
 		<method name="getAutomaticZenRule(Ljava/lang/String;)Landroid/app/AutomaticZenRule;" since="24" />
 		<method name="getAutomaticZenRules()Ljava/util/Map;" since="24" />
 		<method name="getCurrentInterruptionFilter()I" since="23" />
 		<method name="getImportance()I" since="24" />
+		<method name="getNotificationChannel(Ljava/lang/String;)Landroid/app/NotificationChannel;" since="26" />
+		<method name="getNotificationChannelGroups()Ljava/util/List;" since="26" />
+		<method name="getNotificationChannels()Ljava/util/List;" since="26" />
 		<method name="getNotificationPolicy()Landroid/app/NotificationManager$Policy;" since="23" />
 		<method name="isNotificationPolicyAccessGranted()Z" since="23" />
 		<method name="notify(ILandroid/app/Notification;)V" />
@@ -5342,6 +5634,7 @@
 		<method name="getCreatorPackage()Ljava/lang/String;" since="17" />
 		<method name="getCreatorUid()I" since="17" />
 		<method name="getCreatorUserHandle()Landroid/os/UserHandle;" since="17" />
+		<method name="getForegroundService(Landroid/content/Context;ILandroid/content/Intent;I)Landroid/app/PendingIntent;" since="26" />
 		<method name="getIntentSender()Landroid/content/IntentSender;" since="4" />
 		<method name="getService(Landroid/content/Context;ILandroid/content/Intent;I)Landroid/app/PendingIntent;" />
 		<method name="getTargetPackage()Ljava/lang/String;" deprecated="17" />
@@ -5371,6 +5664,20 @@
 		<extends name="java/lang/Object" />
 		<method name="onSendFinished(Landroid/app/PendingIntent;Landroid/content/Intent;ILjava/lang/String;Landroid/os/Bundle;)V" />
 	</class>
+	<class name="android/app/PictureInPictureParams" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/os/Parcelable" />
+		<method name="&lt;init>()V" />
+		<field name="CREATOR" />
+	</class>
+	<class name="android/app/PictureInPictureParams$Builder" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="build()Landroid/app/PictureInPictureParams;" />
+		<method name="setActions(Ljava/util/List;)Landroid/app/PictureInPictureParams$Builder;" />
+		<method name="setAspectRatio(Landroid/util/Rational;)Landroid/app/PictureInPictureParams$Builder;" />
+		<method name="setSourceRectHint(Landroid/graphics/Rect;)Landroid/app/PictureInPictureParams$Builder;" />
+	</class>
 	<class name="android/app/Presentation" since="17">
 		<extends name="android/app/Dialog" />
 		<method name="&lt;init>(Landroid/content/Context;Landroid/view/Display;)V" />
@@ -5380,7 +5687,7 @@
 		<method name="onDisplayChanged()V" />
 		<method name="onDisplayRemoved()V" />
 	</class>
-	<class name="android/app/ProgressDialog" since="1">
+	<class name="android/app/ProgressDialog" since="1" deprecated="26">
 		<extends name="android/app/AlertDialog" />
 		<method name="&lt;init>(Landroid/content/Context;)V" />
 		<method name="&lt;init>(Landroid/content/Context;I)V" />
@@ -5406,17 +5713,35 @@
 		<field name="STYLE_HORIZONTAL" />
 		<field name="STYLE_SPINNER" />
 	</class>
+	<class name="android/app/RemoteAction" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/os/Parcelable" />
+		<method name="&lt;init>(Landroid/graphics/drawable/Icon;Ljava/lang/CharSequence;Ljava/lang/CharSequence;Landroid/app/PendingIntent;)V" />
+		<method name="clone()Landroid/app/RemoteAction;" />
+		<method name="dump(Ljava/lang/String;Ljava/io/PrintWriter;)V" />
+		<method name="getActionIntent()Landroid/app/PendingIntent;" />
+		<method name="getContentDescription()Ljava/lang/CharSequence;" />
+		<method name="getIcon()Landroid/graphics/drawable/Icon;" />
+		<method name="getTitle()Ljava/lang/CharSequence;" />
+		<method name="isEnabled()Z" />
+		<method name="setEnabled(Z)V" />
+		<field name="CREATOR" />
+	</class>
 	<class name="android/app/RemoteInput" since="20">
 		<extends name="java/lang/Object" />
 		<implements name="android/os/Parcelable" />
 		<method name="&lt;init>()V" />
+		<method name="addDataResultToIntent(Landroid/app/RemoteInput;Landroid/content/Intent;Ljava/util/Map;)V" since="26" />
 		<method name="addResultsToIntent([Landroid/app/RemoteInput;Landroid/content/Intent;Landroid/os/Bundle;)V" />
 		<method name="getAllowFreeFormInput()Z" />
+		<method name="getAllowedDataTypes()Ljava/util/Set;" since="26" />
 		<method name="getChoices()[Ljava/lang/CharSequence;" />
+		<method name="getDataResultsFromIntent(Landroid/content/Intent;Ljava/lang/String;)Ljava/util/Map;" since="26" />
 		<method name="getExtras()Landroid/os/Bundle;" />
 		<method name="getLabel()Ljava/lang/CharSequence;" />
 		<method name="getResultKey()Ljava/lang/String;" />
 		<method name="getResultsFromIntent(Landroid/content/Intent;)Landroid/os/Bundle;" />
+		<method name="isDataOnly()Z" since="26" />
 		<field name="CREATOR" />
 		<field name="EXTRA_RESULTS_DATA" />
 		<field name="RESULTS_CLIP_LABEL" />
@@ -5427,6 +5752,7 @@
 		<method name="addExtras(Landroid/os/Bundle;)Landroid/app/RemoteInput$Builder;" />
 		<method name="build()Landroid/app/RemoteInput;" />
 		<method name="getExtras()Landroid/os/Bundle;" />
+		<method name="setAllowDataType(Ljava/lang/String;Z)Landroid/app/RemoteInput$Builder;" since="26" />
 		<method name="setAllowFreeFormInput(Z)Landroid/app/RemoteInput$Builder;" />
 		<method name="setChoices([Ljava/lang/CharSequence;)Landroid/app/RemoteInput$Builder;" />
 		<method name="setLabel(Ljava/lang/CharSequence;)Landroid/app/RemoteInput$Builder;" />
@@ -5811,6 +6137,13 @@
 		<field name="FLAG_SYSTEM" since="24" />
 		<field name="WALLPAPER_PREVIEW_META_DATA" since="11" />
 	</class>
+	<class name="android/app/admin/ConnectEvent" since="26">
+		<extends name="android/app/admin/NetworkEvent" />
+		<method name="&lt;init>()V" />
+		<method name="getInetAddress()Ljava/net/InetAddress;" />
+		<method name="getPort()I" />
+		<field name="CREATOR" />
+	</class>
 	<class name="android/app/admin/DeviceAdminInfo" since="8">
 		<extends name="java/lang/Object" />
 		<implements name="android/os/Parcelable" />
@@ -5851,14 +6184,21 @@
 		<method name="onEnabled(Landroid/content/Context;Landroid/content/Intent;)V" />
 		<method name="onLockTaskModeEntering(Landroid/content/Context;Landroid/content/Intent;Ljava/lang/String;)V" since="21" />
 		<method name="onLockTaskModeExiting(Landroid/content/Context;Landroid/content/Intent;)V" since="21" />
-		<method name="onPasswordChanged(Landroid/content/Context;Landroid/content/Intent;)V" />
-		<method name="onPasswordExpiring(Landroid/content/Context;Landroid/content/Intent;)V" since="11" />
-		<method name="onPasswordFailed(Landroid/content/Context;Landroid/content/Intent;)V" />
-		<method name="onPasswordSucceeded(Landroid/content/Context;Landroid/content/Intent;)V" />
+		<method name="onNetworkLogsAvailable(Landroid/content/Context;Landroid/content/Intent;JI)V" since="26" />
+		<method name="onPasswordChanged(Landroid/content/Context;Landroid/content/Intent;)V" deprecated="26" />
+		<method name="onPasswordChanged(Landroid/content/Context;Landroid/content/Intent;Landroid/os/UserHandle;)V" since="26" />
+		<method name="onPasswordExpiring(Landroid/content/Context;Landroid/content/Intent;)V" since="11" deprecated="26" />
+		<method name="onPasswordExpiring(Landroid/content/Context;Landroid/content/Intent;Landroid/os/UserHandle;)V" since="26" />
+		<method name="onPasswordFailed(Landroid/content/Context;Landroid/content/Intent;)V" deprecated="26" />
+		<method name="onPasswordFailed(Landroid/content/Context;Landroid/content/Intent;Landroid/os/UserHandle;)V" since="26" />
+		<method name="onPasswordSucceeded(Landroid/content/Context;Landroid/content/Intent;)V" deprecated="26" />
+		<method name="onPasswordSucceeded(Landroid/content/Context;Landroid/content/Intent;Landroid/os/UserHandle;)V" since="26" />
 		<method name="onProfileProvisioningComplete(Landroid/content/Context;Landroid/content/Intent;)V" since="21" />
 		<method name="onReadyForUserInitialization(Landroid/content/Context;Landroid/content/Intent;)V" since="23" deprecated="24" />
 		<method name="onSecurityLogsAvailable(Landroid/content/Context;Landroid/content/Intent;)V" since="24" />
 		<method name="onSystemUpdatePending(Landroid/content/Context;Landroid/content/Intent;J)V" since="23" />
+		<method name="onUserAdded(Landroid/content/Context;Landroid/content/Intent;Landroid/os/UserHandle;)V" since="26" />
+		<method name="onUserRemoved(Landroid/content/Context;Landroid/content/Intent;Landroid/os/UserHandle;)V" since="26" />
 		<field name="ACTION_DEVICE_ADMIN_DISABLED" />
 		<field name="ACTION_DEVICE_ADMIN_DISABLE_REQUESTED" />
 		<field name="ACTION_DEVICE_ADMIN_ENABLED" />
@@ -5875,6 +6215,10 @@
 		<field name="EXTRA_DISABLE_WARNING" />
 		<field name="EXTRA_LOCK_TASK_PACKAGE" since="21" />
 	</class>
+	<class name="android/app/admin/DeviceAdminService" since="26">
+		<extends name="android/app/Service" />
+		<method name="&lt;init>()V" />
+	</class>
 	<class name="android/app/admin/DevicePolicyManager" since="8">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
@@ -5882,11 +6226,14 @@
 		<method name="addCrossProfileWidgetProvider(Landroid/content/ComponentName;Ljava/lang/String;)Z" since="21" />
 		<method name="addPersistentPreferredActivity(Landroid/content/ComponentName;Landroid/content/IntentFilter;Landroid/content/ComponentName;)V" since="21" />
 		<method name="addUserRestriction(Landroid/content/ComponentName;Ljava/lang/String;)V" since="21" />
+		<method name="bindDeviceAdminServiceAsUser(Landroid/content/ComponentName;Landroid/content/Intent;Landroid/content/ServiceConnection;ILandroid/os/UserHandle;)Z" since="26" />
 		<method name="clearCrossProfileIntentFilters(Landroid/content/ComponentName;)V" since="21" />
-		<method name="clearDeviceOwnerApp(Ljava/lang/String;)V" since="21" />
+		<method name="clearDeviceOwnerApp(Ljava/lang/String;)V" since="21" deprecated="26" />
 		<method name="clearPackagePersistentPreferredActivities(Landroid/content/ComponentName;Ljava/lang/String;)V" since="21" />
-		<method name="clearProfileOwner(Landroid/content/ComponentName;)V" since="24" />
+		<method name="clearProfileOwner(Landroid/content/ComponentName;)V" since="24" deprecated="26" />
+		<method name="clearResetPasswordToken(Landroid/content/ComponentName;)Z" since="26" />
 		<method name="clearUserRestriction(Landroid/content/ComponentName;Ljava/lang/String;)V" since="21" />
+		<method name="createAdminSupportIntent(Ljava/lang/String;)Landroid/content/Intent;" since="26" />
 		<method name="createAndInitializeUser(Landroid/content/ComponentName;Ljava/lang/String;Ljava/lang/String;Landroid/content/ComponentName;Landroid/os/Bundle;)Landroid/os/UserHandle;" since="21" deprecated="23" />
 		<method name="createAndManageUser(Landroid/content/ComponentName;Ljava/lang/String;Landroid/content/ComponentName;Landroid/os/PersistableBundle;I)Landroid/os/UserHandle;" since="24" />
 		<method name="createUser(Landroid/content/ComponentName;Ljava/lang/String;)Landroid/os/UserHandle;" since="21" deprecated="23" />
@@ -5894,20 +6241,25 @@
 		<method name="enableSystemApp(Landroid/content/ComponentName;Ljava/lang/String;)V" since="21" />
 		<method name="getAccountTypesWithManagementDisabled()[Ljava/lang/String;" since="21" />
 		<method name="getActiveAdmins()Ljava/util/List;" />
+		<method name="getAffiliationIds(Landroid/content/ComponentName;)Ljava/util/Set;" since="26" />
 		<method name="getAlwaysOnVpnPackage(Landroid/content/ComponentName;)Ljava/lang/String;" since="24" />
 		<method name="getApplicationRestrictions(Landroid/content/ComponentName;Ljava/lang/String;)Landroid/os/Bundle;" since="21" />
-		<method name="getApplicationRestrictionsManagingPackage(Landroid/content/ComponentName;)Ljava/lang/String;" since="24" />
+		<method name="getApplicationRestrictionsManagingPackage(Landroid/content/ComponentName;)Ljava/lang/String;" since="24" deprecated="26" />
 		<method name="getAutoTimeRequired()Z" since="21" />
+		<method name="getBindDeviceAdminTargetUsers(Landroid/content/ComponentName;)Ljava/util/List;" since="26" />
 		<method name="getBluetoothContactSharingDisabled(Landroid/content/ComponentName;)Z" since="23" />
 		<method name="getCameraDisabled(Landroid/content/ComponentName;)Z" since="14" />
-		<method name="getCertInstallerPackage(Landroid/content/ComponentName;)Ljava/lang/String;" since="23" />
+		<method name="getCertInstallerPackage(Landroid/content/ComponentName;)Ljava/lang/String;" since="23" deprecated="26" />
 		<method name="getCrossProfileCallerIdDisabled(Landroid/content/ComponentName;)Z" since="21" />
 		<method name="getCrossProfileContactsSearchDisabled(Landroid/content/ComponentName;)Z" since="24" />
 		<method name="getCrossProfileWidgetProviders(Landroid/content/ComponentName;)Ljava/util/List;" since="21" />
 		<method name="getCurrentFailedPasswordAttempts()I" />
+		<method name="getDelegatePackages(Landroid/content/ComponentName;Ljava/lang/String;)Ljava/util/List;" since="26" />
+		<method name="getDelegatedScopes(Landroid/content/ComponentName;Ljava/lang/String;)Ljava/util/List;" since="26" />
 		<method name="getDeviceOwnerLockScreenInfo()Ljava/lang/CharSequence;" since="24" />
 		<method name="getInstalledCaCerts(Landroid/content/ComponentName;)Ljava/util/List;" since="21" />
 		<method name="getKeyguardDisabledFeatures(Landroid/content/ComponentName;)I" since="17" />
+		<method name="getLockTaskPackages(Landroid/content/ComponentName;)[Ljava/lang/String;" since="26" />
 		<method name="getLongSupportMessage(Landroid/content/ComponentName;)Ljava/lang/CharSequence;" since="24" />
 		<method name="getMaximumFailedPasswordsForWipe(Landroid/content/ComponentName;)I" />
 		<method name="getMaximumTimeToLock(Landroid/content/ComponentName;)J" />
@@ -5926,10 +6278,13 @@
 		<method name="getPasswordMinimumSymbols(Landroid/content/ComponentName;)I" since="11" />
 		<method name="getPasswordMinimumUpperCase(Landroid/content/ComponentName;)I" since="11" />
 		<method name="getPasswordQuality(Landroid/content/ComponentName;)I" />
+		<method name="getPendingSystemUpdate(Landroid/content/ComponentName;)Landroid/app/admin/SystemUpdateInfo;" since="26" />
 		<method name="getPermissionGrantState(Landroid/content/ComponentName;Ljava/lang/String;Ljava/lang/String;)I" since="23" />
 		<method name="getPermissionPolicy(Landroid/content/ComponentName;)I" since="23" />
 		<method name="getPermittedAccessibilityServices(Landroid/content/ComponentName;)Ljava/util/List;" since="21" />
+		<method name="getPermittedCrossProfileNotificationListeners(Landroid/content/ComponentName;)Ljava/util/List;" since="26" />
 		<method name="getPermittedInputMethods(Landroid/content/ComponentName;)Ljava/util/List;" since="21" />
+		<method name="getRequiredStrongAuthTimeout(Landroid/content/ComponentName;)J" since="26" />
 		<method name="getScreenCaptureDisabled(Landroid/content/ComponentName;)Z" since="21" />
 		<method name="getShortSupportMessage(Landroid/content/ComponentName;)Ljava/lang/CharSequence;" since="24" />
 		<method name="getStorageEncryption(Landroid/content/ComponentName;)Z" since="11" />
@@ -5946,17 +6301,21 @@
 		<method name="isActivePasswordSufficient()Z" />
 		<method name="isAdminActive(Landroid/content/ComponentName;)Z" />
 		<method name="isApplicationHidden(Landroid/content/ComponentName;Ljava/lang/String;)Z" since="21" />
-		<method name="isCallerApplicationRestrictionsManagingPackage()Z" since="24" />
+		<method name="isBackupServiceEnabled(Landroid/content/ComponentName;)Z" since="26" />
+		<method name="isCallerApplicationRestrictionsManagingPackage()Z" since="24" deprecated="26" />
 		<method name="isDeviceOwnerApp(Ljava/lang/String;)Z" since="18" />
 		<method name="isLockTaskPermitted(Ljava/lang/String;)Z" since="21" />
 		<method name="isManagedProfile(Landroid/content/ComponentName;)Z" since="24" />
 		<method name="isMasterVolumeMuted(Landroid/content/ComponentName;)Z" since="21" />
+		<method name="isNetworkLoggingEnabled(Landroid/content/ComponentName;)Z" since="26" />
 		<method name="isPackageSuspended(Landroid/content/ComponentName;Ljava/lang/String;)Z" since="24" />
 		<method name="isProfileOwnerApp(Ljava/lang/String;)Z" since="21" />
 		<method name="isProvisioningAllowed(Ljava/lang/String;)Z" since="24" />
+		<method name="isResetPasswordTokenActive(Landroid/content/ComponentName;)Z" since="26" />
 		<method name="isSecurityLoggingEnabled(Landroid/content/ComponentName;)Z" since="24" />
 		<method name="isUninstallBlocked(Landroid/content/ComponentName;Ljava/lang/String;)Z" since="21" />
 		<method name="lockNow()V" />
+		<method name="lockNow(I)V" since="26" />
 		<method name="reboot(Landroid/content/ComponentName;)V" since="24" />
 		<method name="removeActiveAdmin(Landroid/content/ComponentName;)V" />
 		<method name="removeCrossProfileWidgetProvider(Landroid/content/ComponentName;Ljava/lang/String;)Z" since="21" />
@@ -5964,19 +6323,24 @@
 		<method name="removeUser(Landroid/content/ComponentName;Landroid/os/UserHandle;)Z" since="21" />
 		<method name="requestBugreport(Landroid/content/ComponentName;)Z" since="24" />
 		<method name="resetPassword(Ljava/lang/String;I)Z" />
+		<method name="resetPasswordWithToken(Landroid/content/ComponentName;Ljava/lang/String;[BI)Z" since="26" />
+		<method name="retrieveNetworkLogs(Landroid/content/ComponentName;J)Ljava/util/List;" since="26" />
 		<method name="retrievePreRebootSecurityLogs(Landroid/content/ComponentName;)Ljava/util/List;" since="24" />
 		<method name="retrieveSecurityLogs(Landroid/content/ComponentName;)Ljava/util/List;" since="24" />
 		<method name="setAccountManagementDisabled(Landroid/content/ComponentName;Ljava/lang/String;Z)V" since="21" />
+		<method name="setAffiliationIds(Landroid/content/ComponentName;Ljava/util/Set;)V" since="26" />
 		<method name="setAlwaysOnVpnPackage(Landroid/content/ComponentName;Ljava/lang/String;Z)V" since="24" />
 		<method name="setApplicationHidden(Landroid/content/ComponentName;Ljava/lang/String;Z)Z" since="21" />
 		<method name="setApplicationRestrictions(Landroid/content/ComponentName;Ljava/lang/String;Landroid/os/Bundle;)V" since="21" />
-		<method name="setApplicationRestrictionsManagingPackage(Landroid/content/ComponentName;Ljava/lang/String;)V" since="24" />
+		<method name="setApplicationRestrictionsManagingPackage(Landroid/content/ComponentName;Ljava/lang/String;)V" since="24" deprecated="26" />
 		<method name="setAutoTimeRequired(Landroid/content/ComponentName;Z)V" since="21" />
+		<method name="setBackupServiceEnabled(Landroid/content/ComponentName;Z)V" since="26" />
 		<method name="setBluetoothContactSharingDisabled(Landroid/content/ComponentName;Z)V" since="23" />
 		<method name="setCameraDisabled(Landroid/content/ComponentName;Z)V" since="14" />
-		<method name="setCertInstallerPackage(Landroid/content/ComponentName;Ljava/lang/String;)V" since="23" />
+		<method name="setCertInstallerPackage(Landroid/content/ComponentName;Ljava/lang/String;)V" since="23" deprecated="26" />
 		<method name="setCrossProfileCallerIdDisabled(Landroid/content/ComponentName;Z)V" since="21" />
 		<method name="setCrossProfileContactsSearchDisabled(Landroid/content/ComponentName;Z)V" since="24" />
+		<method name="setDelegatedScopes(Landroid/content/ComponentName;Ljava/lang/String;Ljava/util/List;)V" since="26" />
 		<method name="setDeviceOwnerLockScreenInfo(Landroid/content/ComponentName;Ljava/lang/CharSequence;)V" since="24" />
 		<method name="setGlobalSetting(Landroid/content/ComponentName;Ljava/lang/String;Ljava/lang/String;)V" since="21" />
 		<method name="setKeyguardDisabled(Landroid/content/ComponentName;Z)Z" since="23" />
@@ -5986,6 +6350,7 @@
 		<method name="setMasterVolumeMuted(Landroid/content/ComponentName;Z)V" since="21" />
 		<method name="setMaximumFailedPasswordsForWipe(Landroid/content/ComponentName;I)V" />
 		<method name="setMaximumTimeToLock(Landroid/content/ComponentName;J)V" />
+		<method name="setNetworkLoggingEnabled(Landroid/content/ComponentName;Z)V" since="26" />
 		<method name="setOrganizationColor(Landroid/content/ComponentName;I)V" since="24" />
 		<method name="setOrganizationName(Landroid/content/ComponentName;Ljava/lang/CharSequence;)V" since="24" />
 		<method name="setPackagesSuspended(Landroid/content/ComponentName;[Ljava/lang/String;Z)[Ljava/lang/String;" since="24" />
@@ -6002,10 +6367,13 @@
 		<method name="setPermissionGrantState(Landroid/content/ComponentName;Ljava/lang/String;Ljava/lang/String;I)Z" since="23" />
 		<method name="setPermissionPolicy(Landroid/content/ComponentName;I)V" since="23" />
 		<method name="setPermittedAccessibilityServices(Landroid/content/ComponentName;Ljava/util/List;)Z" since="21" />
+		<method name="setPermittedCrossProfileNotificationListeners(Landroid/content/ComponentName;Ljava/util/List;)Z" since="26" />
 		<method name="setPermittedInputMethods(Landroid/content/ComponentName;Ljava/util/List;)Z" since="21" />
 		<method name="setProfileEnabled(Landroid/content/ComponentName;)V" since="21" />
 		<method name="setProfileName(Landroid/content/ComponentName;Ljava/lang/String;)V" since="21" />
 		<method name="setRecommendedGlobalProxy(Landroid/content/ComponentName;Landroid/net/ProxyInfo;)V" since="21" />
+		<method name="setRequiredStrongAuthTimeout(Landroid/content/ComponentName;J)V" since="26" />
+		<method name="setResetPasswordToken(Landroid/content/ComponentName;[B)Z" since="26" />
 		<method name="setRestrictionsProvider(Landroid/content/ComponentName;Landroid/content/ComponentName;)V" since="21" />
 		<method name="setScreenCaptureDisabled(Landroid/content/ComponentName;Z)V" since="21" />
 		<method name="setSecureSetting(Landroid/content/ComponentName;Ljava/lang/String;Ljava/lang/String;)V" since="21" />
@@ -6022,14 +6390,23 @@
 		<method name="uninstallCaCert(Landroid/content/ComponentName;[B)V" since="21" />
 		<method name="wipeData(I)V" />
 		<field name="ACTION_ADD_DEVICE_ADMIN" />
+		<field name="ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED" since="26" />
+		<field name="ACTION_DEVICE_ADMIN_SERVICE" since="26" />
 		<field name="ACTION_DEVICE_OWNER_CHANGED" since="23" />
 		<field name="ACTION_MANAGED_PROFILE_PROVISIONED" since="23" />
+		<field name="ACTION_PROVISIONING_SUCCESSFUL" since="26" />
 		<field name="ACTION_PROVISION_MANAGED_DEVICE" since="23" />
 		<field name="ACTION_PROVISION_MANAGED_PROFILE" since="21" />
 		<field name="ACTION_SET_NEW_PARENT_PROFILE_PASSWORD" since="24" />
 		<field name="ACTION_SET_NEW_PASSWORD" />
 		<field name="ACTION_START_ENCRYPTION" since="11" />
 		<field name="ACTION_SYSTEM_UPDATE_POLICY_CHANGED" since="23" />
+		<field name="DELEGATION_APP_RESTRICTIONS" since="26" />
+		<field name="DELEGATION_BLOCK_UNINSTALL" since="26" />
+		<field name="DELEGATION_CERT_INSTALL" since="26" />
+		<field name="DELEGATION_ENABLE_SYSTEM_APP" since="26" />
+		<field name="DELEGATION_PACKAGE_ACCESS" since="26" />
+		<field name="DELEGATION_PERMISSION_GRANT" since="26" />
 		<field name="ENCRYPTION_STATUS_ACTIVATING" since="11" />
 		<field name="ENCRYPTION_STATUS_ACTIVE" since="11" />
 		<field name="ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY" since="23" />
@@ -6037,6 +6414,7 @@
 		<field name="ENCRYPTION_STATUS_INACTIVE" since="11" />
 		<field name="ENCRYPTION_STATUS_UNSUPPORTED" since="11" />
 		<field name="EXTRA_ADD_EXPLANATION" />
+		<field name="EXTRA_DELEGATION_SCOPES" since="26" />
 		<field name="EXTRA_DEVICE_ADMIN" />
 		<field name="EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE" since="22" />
 		<field name="EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE" since="21" />
@@ -6047,13 +6425,18 @@
 		<field name="EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION" since="21" />
 		<field name="EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME" since="21" deprecated="23" />
 		<field name="EXTRA_PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM" since="23" />
-		<field name="EXTRA_PROVISIONING_EMAIL_ADDRESS" since="21" />
+		<field name="EXTRA_PROVISIONING_DISCLAIMERS" since="26" />
+		<field name="EXTRA_PROVISIONING_DISCLAIMER_CONTENT" since="26" />
+		<field name="EXTRA_PROVISIONING_DISCLAIMER_HEADER" since="26" />
+		<field name="EXTRA_PROVISIONING_EMAIL_ADDRESS" since="21" deprecated="26" />
+		<field name="EXTRA_PROVISIONING_KEEP_ACCOUNT_ON_MIGRATION" since="26" />
 		<field name="EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED" since="22" />
 		<field name="EXTRA_PROVISIONING_LOCALE" since="21" />
 		<field name="EXTRA_PROVISIONING_LOCAL_TIME" since="21" />
 		<field name="EXTRA_PROVISIONING_LOGO_URI" since="24" />
 		<field name="EXTRA_PROVISIONING_MAIN_COLOR" since="24" />
 		<field name="EXTRA_PROVISIONING_SKIP_ENCRYPTION" since="23" />
+		<field name="EXTRA_PROVISIONING_SKIP_USER_CONSENT" since="26" />
 		<field name="EXTRA_PROVISIONING_TIME_ZONE" since="21" />
 		<field name="EXTRA_PROVISIONING_WIFI_HIDDEN" since="21" />
 		<field name="EXTRA_PROVISIONING_WIFI_PAC_URL" since="21" />
@@ -6063,6 +6446,7 @@
 		<field name="EXTRA_PROVISIONING_WIFI_PROXY_PORT" since="21" />
 		<field name="EXTRA_PROVISIONING_WIFI_SECURITY_TYPE" since="21" />
 		<field name="EXTRA_PROVISIONING_WIFI_SSID" since="21" />
+		<field name="FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY" since="26" />
 		<field name="FLAG_MANAGED_CAN_ACCESS_PARENT" since="21" />
 		<field name="FLAG_PARENT_CAN_ACCESS_MANAGED" since="21" />
 		<field name="KEYGUARD_DISABLE_FEATURES_ALL" since="17" />
@@ -6089,12 +6473,30 @@
 		<field name="PERMISSION_POLICY_AUTO_DENY" since="23" />
 		<field name="PERMISSION_POLICY_AUTO_GRANT" since="23" />
 		<field name="PERMISSION_POLICY_PROMPT" since="23" />
+		<field name="POLICY_DISABLE_CAMERA" since="26" />
+		<field name="POLICY_DISABLE_SCREEN_CAPTURE" since="26" />
 		<field name="RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT" since="23" />
 		<field name="RESET_PASSWORD_REQUIRE_ENTRY" />
 		<field name="SKIP_SETUP_WIZARD" since="24" />
 		<field name="WIPE_EXTERNAL_STORAGE" since="9" />
 		<field name="WIPE_RESET_PROTECTION_DATA" since="22" />
 	</class>
+	<class name="android/app/admin/DnsEvent" since="26">
+		<extends name="android/app/admin/NetworkEvent" />
+		<method name="&lt;init>()V" />
+		<method name="getHostname()Ljava/lang/String;" />
+		<method name="getInetAddresses()Ljava/util/List;" />
+		<method name="getTotalResolvedAddressCount()I" />
+		<field name="CREATOR" />
+	</class>
+	<class name="android/app/admin/NetworkEvent" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/os/Parcelable" />
+		<method name="&lt;init>()V" />
+		<method name="getPackageName()Ljava/lang/String;" />
+		<method name="getTimestamp()J" />
+		<field name="CREATOR" />
+	</class>
 	<class name="android/app/admin/SecurityLog" since="24">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
@@ -6116,6 +6518,17 @@
 		<method name="getTimeNanos()J" />
 		<field name="CREATOR" />
 	</class>
+	<class name="android/app/admin/SystemUpdateInfo" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/os/Parcelable" />
+		<method name="&lt;init>()V" />
+		<method name="getReceivedTime()J" />
+		<method name="getSecurityPatchState()I" />
+		<field name="CREATOR" />
+		<field name="SECURITY_PATCH_STATE_FALSE" />
+		<field name="SECURITY_PATCH_STATE_TRUE" />
+		<field name="SECURITY_PATCH_STATE_UNKNOWN" />
+	</class>
 	<class name="android/app/admin/SystemUpdatePolicy" since="23">
 		<extends name="java/lang/Object" />
 		<implements name="android/os/Parcelable" />
@@ -6152,15 +6565,23 @@
 		<extends name="java/lang/Object" />
 		<implements name="android/os/Parcelable" />
 		<method name="&lt;init>()V" />
+		<method name="getAcquisitionEndTime()J" since="26" />
+		<method name="getAcquisitionStartTime()J" since="26" />
 		<method name="getActivityComponent()Landroid/content/ComponentName;" />
 		<method name="getWindowNodeAt(I)Landroid/app/assist/AssistStructure$WindowNode;" />
 		<method name="getWindowNodeCount()I" />
+		<method name="isHomeActivity()Z" since="26" />
 		<field name="CREATOR" />
 	</class>
 	<class name="android/app/assist/AssistStructure$ViewNode" since="23">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
 		<method name="getAlpha()F" />
+		<method name="getAutofillHints()[Ljava/lang/String;" since="26" />
+		<method name="getAutofillId()Landroid/view/autofill/AutofillId;" since="26" />
+		<method name="getAutofillOptions()[Ljava/lang/CharSequence;" since="26" />
+		<method name="getAutofillType()I" since="26" />
+		<method name="getAutofillValue()Landroid/view/autofill/AutofillValue;" since="26" />
 		<method name="getChildAt(I)Landroid/app/assist/AssistStructure$ViewNode;" />
 		<method name="getChildCount()I" />
 		<method name="getClassName()Ljava/lang/String;" />
@@ -6169,11 +6590,14 @@
 		<method name="getExtras()Landroid/os/Bundle;" />
 		<method name="getHeight()I" />
 		<method name="getHint()Ljava/lang/String;" />
+		<method name="getHtmlInfo()Landroid/view/ViewStructure$HtmlInfo;" since="26" />
 		<method name="getId()I" />
 		<method name="getIdEntry()Ljava/lang/String;" />
 		<method name="getIdPackage()Ljava/lang/String;" />
 		<method name="getIdType()Ljava/lang/String;" />
+		<method name="getInputType()I" since="26" />
 		<method name="getLeft()I" />
+		<method name="getLocaleList()Landroid/os/LocaleList;" since="26" />
 		<method name="getScrollX()I" />
 		<method name="getScrollY()I" />
 		<method name="getText()Ljava/lang/CharSequence;" />
@@ -6188,6 +6612,7 @@
 		<method name="getTop()I" />
 		<method name="getTransformation()Landroid/graphics/Matrix;" />
 		<method name="getVisibility()I" />
+		<method name="getWebDomain()Ljava/lang/String;" since="26" />
 		<method name="getWidth()I" />
 		<method name="isAccessibilityFocused()Z" />
 		<method name="isActivated()Z" />
@@ -6200,6 +6625,7 @@
 		<method name="isFocusable()Z" />
 		<method name="isFocused()Z" />
 		<method name="isLongClickable()Z" />
+		<method name="isOpaque()Z" since="26" />
 		<method name="isSelected()Z" />
 		<field name="TEXT_COLOR_UNDEFINED" />
 		<field name="TEXT_STYLE_BOLD" />
@@ -6256,6 +6682,7 @@
 	<class name="android/app/backup/BackupDataOutput" since="8">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
+		<method name="getQuota()J" since="26" />
 		<method name="writeEntityData([BI)I" />
 		<method name="writeEntityHeader(Ljava/lang/String;I)I" />
 	</class>
@@ -6285,6 +6712,7 @@
 	<class name="android/app/backup/FullBackupDataOutput" since="14">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
+		<method name="getQuota()J" since="26" />
 	</class>
 	<class name="android/app/backup/RestoreObserver" since="8">
 		<extends name="java/lang/Object" />
@@ -6303,6 +6731,8 @@
 		<implements name="android/os/Parcelable" />
 		<method name="&lt;init>()V" />
 		<method name="getBackoffPolicy()I" />
+		<method name="getClipData()Landroid/content/ClipData;" since="26" />
+		<method name="getClipGrantFlags()I" since="26" />
 		<method name="getExtras()Landroid/os/PersistableBundle;" />
 		<method name="getFlexMillis()J" since="24" />
 		<method name="getId()I" />
@@ -6314,19 +6744,23 @@
 		<method name="getMinPeriodMillis()J" since="24" />
 		<method name="getNetworkType()I" />
 		<method name="getService()Landroid/content/ComponentName;" />
+		<method name="getTransientExtras()Landroid/os/Bundle;" since="26" />
 		<method name="getTriggerContentMaxDelay()J" since="24" />
 		<method name="getTriggerContentUpdateDelay()J" since="24" />
 		<method name="getTriggerContentUris()[Landroid/app/job/JobInfo$TriggerContentUri;" since="24" />
 		<method name="isPeriodic()Z" />
 		<method name="isPersisted()Z" />
+		<method name="isRequireBatteryNotLow()Z" since="26" />
 		<method name="isRequireCharging()Z" />
 		<method name="isRequireDeviceIdle()Z" />
+		<method name="isRequireStorageNotLow()Z" since="26" />
 		<field name="BACKOFF_POLICY_EXPONENTIAL" />
 		<field name="BACKOFF_POLICY_LINEAR" />
 		<field name="CREATOR" />
 		<field name="DEFAULT_INITIAL_BACKOFF_MILLIS" />
 		<field name="MAX_BACKOFF_DELAY_MILLIS" />
 		<field name="NETWORK_TYPE_ANY" />
+		<field name="NETWORK_TYPE_METERED" since="26" />
 		<field name="NETWORK_TYPE_NONE" />
 		<field name="NETWORK_TYPE_NOT_ROAMING" since="24" />
 		<field name="NETWORK_TYPE_UNMETERED" />
@@ -6337,6 +6771,7 @@
 		<method name="addTriggerContentUri(Landroid/app/job/JobInfo$TriggerContentUri;)Landroid/app/job/JobInfo$Builder;" since="24" />
 		<method name="build()Landroid/app/job/JobInfo;" />
 		<method name="setBackoffCriteria(JI)Landroid/app/job/JobInfo$Builder;" />
+		<method name="setClipData(Landroid/content/ClipData;I)Landroid/app/job/JobInfo$Builder;" since="26" />
 		<method name="setExtras(Landroid/os/PersistableBundle;)Landroid/app/job/JobInfo$Builder;" />
 		<method name="setMinimumLatency(J)Landroid/app/job/JobInfo$Builder;" />
 		<method name="setOverrideDeadline(J)Landroid/app/job/JobInfo$Builder;" />
@@ -6344,8 +6779,11 @@
 		<method name="setPeriodic(JJ)Landroid/app/job/JobInfo$Builder;" since="24" />
 		<method name="setPersisted(Z)Landroid/app/job/JobInfo$Builder;" />
 		<method name="setRequiredNetworkType(I)Landroid/app/job/JobInfo$Builder;" />
+		<method name="setRequiresBatteryNotLow(Z)Landroid/app/job/JobInfo$Builder;" since="26" />
 		<method name="setRequiresCharging(Z)Landroid/app/job/JobInfo$Builder;" />
 		<method name="setRequiresDeviceIdle(Z)Landroid/app/job/JobInfo$Builder;" />
+		<method name="setRequiresStorageNotLow(Z)Landroid/app/job/JobInfo$Builder;" since="26" />
+		<method name="setTransientExtras(Landroid/os/Bundle;)Landroid/app/job/JobInfo$Builder;" since="26" />
 		<method name="setTriggerContentMaxDelay(J)Landroid/app/job/JobInfo$Builder;" since="24" />
 		<method name="setTriggerContentUpdateDelay(J)Landroid/app/job/JobInfo$Builder;" since="24" />
 	</class>
@@ -6362,8 +6800,13 @@
 		<extends name="java/lang/Object" />
 		<implements name="android/os/Parcelable" />
 		<method name="&lt;init>()V" />
+		<method name="completeWork(Landroid/app/job/JobWorkItem;)V" since="26" />
+		<method name="dequeueWork()Landroid/app/job/JobWorkItem;" since="26" />
+		<method name="getClipData()Landroid/content/ClipData;" since="26" />
+		<method name="getClipGrantFlags()I" since="26" />
 		<method name="getExtras()Landroid/os/PersistableBundle;" />
 		<method name="getJobId()I" />
+		<method name="getTransientExtras()Landroid/os/Bundle;" since="26" />
 		<method name="getTriggeredContentAuthorities()[Ljava/lang/String;" since="24" />
 		<method name="getTriggeredContentUris()[Landroid/net/Uri;" since="24" />
 		<method name="isOverrideDeadlineExpired()Z" />
@@ -6374,6 +6817,7 @@
 		<method name="&lt;init>()V" />
 		<method name="cancel(I)V" />
 		<method name="cancelAll()V" />
+		<method name="enqueue(Landroid/app/job/JobInfo;Landroid/app/job/JobWorkItem;)I" since="26" />
 		<method name="getAllPendingJobs()Ljava/util/List;" />
 		<method name="getPendingJob(I)Landroid/app/job/JobInfo;" since="24" />
 		<method name="schedule(Landroid/app/job/JobInfo;)I" />
@@ -6388,6 +6832,22 @@
 		<method name="onStopJob(Landroid/app/job/JobParameters;)Z" />
 		<field name="PERMISSION_BIND" />
 	</class>
+	<class name="android/app/job/JobServiceEngine" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>(Landroid/app/Service;)V" />
+		<method name="getBinder()Landroid/os/IBinder;" />
+		<method name="jobFinished(Landroid/app/job/JobParameters;Z)V" />
+		<method name="onStartJob(Landroid/app/job/JobParameters;)Z" />
+		<method name="onStopJob(Landroid/app/job/JobParameters;)Z" />
+	</class>
+	<class name="android/app/job/JobWorkItem" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/os/Parcelable" />
+		<method name="&lt;init>(Landroid/content/Intent;)V" />
+		<method name="getDeliveryCount()I" />
+		<method name="getIntent()Landroid/content/Intent;" />
+		<field name="CREATOR" />
+	</class>
 	<class name="android/app/usage/ConfigurationStats" since="21">
 		<extends name="java/lang/Object" />
 		<implements name="android/os/Parcelable" />
@@ -6400,6 +6860,17 @@
 		<method name="getTotalTimeActive()J" />
 		<field name="CREATOR" />
 	</class>
+	<class name="android/app/usage/ExternalStorageStats" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/os/Parcelable" />
+		<method name="&lt;init>()V" />
+		<method name="getAppBytes()J" />
+		<method name="getAudioBytes()J" />
+		<method name="getImageBytes()J" />
+		<method name="getTotalBytes()J" />
+		<method name="getVideoBytes()J" />
+		<field name="CREATOR" />
+	</class>
 	<class name="android/app/usage/NetworkStats" since="23">
 		<extends name="java/lang/Object" />
 		<implements name="java/lang/AutoCloseable" />
@@ -6411,6 +6882,7 @@
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
 		<method name="getEndTimeStamp()J" />
+		<method name="getMetered()I" since="26" />
 		<method name="getRoaming()I" since="24" />
 		<method name="getRxBytes()J" />
 		<method name="getRxPackets()J" />
@@ -6420,6 +6892,9 @@
 		<method name="getTxBytes()J" />
 		<method name="getTxPackets()J" />
 		<method name="getUid()I" />
+		<field name="METERED_ALL" since="26" />
+		<field name="METERED_NO" since="26" />
+		<field name="METERED_YES" since="26" />
 		<field name="ROAMING_ALL" since="24" />
 		<field name="ROAMING_NO" since="24" />
 		<field name="ROAMING_YES" since="24" />
@@ -6449,6 +6924,25 @@
 		<method name="&lt;init>()V" />
 		<method name="onThresholdReached(ILjava/lang/String;)V" />
 	</class>
+	<class name="android/app/usage/StorageStats" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/os/Parcelable" />
+		<method name="&lt;init>()V" />
+		<method name="getAppBytes()J" />
+		<method name="getCacheBytes()J" />
+		<method name="getDataBytes()J" />
+		<field name="CREATOR" />
+	</class>
+	<class name="android/app/usage/StorageStatsManager" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="getFreeBytes(Ljava/util/UUID;)J" />
+		<method name="getTotalBytes(Ljava/util/UUID;)J" />
+		<method name="queryExternalStatsForUser(Ljava/util/UUID;Landroid/os/UserHandle;)Landroid/app/usage/ExternalStorageStats;" />
+		<method name="queryStatsForPackage(Ljava/util/UUID;Ljava/lang/String;Landroid/os/UserHandle;)Landroid/app/usage/StorageStats;" />
+		<method name="queryStatsForUid(Ljava/util/UUID;I)Landroid/app/usage/StorageStats;" />
+		<method name="queryStatsForUser(Ljava/util/UUID;Landroid/os/UserHandle;)Landroid/app/usage/StorageStats;" />
+	</class>
 	<class name="android/app/usage/UsageEvents" since="21">
 		<extends name="java/lang/Object" />
 		<implements name="android/os/Parcelable" />
@@ -6508,6 +7002,7 @@
 		<method name="deleteAllHosts()V" />
 		<method name="deleteAppWidgetId(I)V" />
 		<method name="deleteHost()V" />
+		<method name="getAppWidgetIds()[I" since="26" />
 		<method name="onCreateView(Landroid/content/Context;ILandroid/appwidget/AppWidgetProviderInfo;)Landroid/appwidget/AppWidgetHostView;" />
 		<method name="onProviderChanged(ILandroid/appwidget/AppWidgetProviderInfo;)V" />
 		<method name="onProvidersChanged()V" since="17" />
@@ -6526,6 +7021,7 @@
 		<method name="getErrorView()Landroid/view/View;" />
 		<method name="prepareView(Landroid/view/View;)V" />
 		<method name="setAppWidget(ILandroid/appwidget/AppWidgetProviderInfo;)V" />
+		<method name="setExecutor(Ljava/util/concurrent/Executor;)V" since="26" />
 		<method name="updateAppWidget(Landroid/widget/RemoteViews;)V" />
 		<method name="updateAppWidgetOptions(Landroid/os/Bundle;)V" since="16" />
 		<method name="updateAppWidgetSize(Landroid/os/Bundle;IIII)V" since="16" />
@@ -6541,12 +7037,15 @@
 		<method name="getAppWidgetInfo(I)Landroid/appwidget/AppWidgetProviderInfo;" />
 		<method name="getAppWidgetOptions(I)Landroid/os/Bundle;" since="16" />
 		<method name="getInstalledProviders()Ljava/util/List;" />
+		<method name="getInstalledProvidersForPackage(Ljava/lang/String;Landroid/os/UserHandle;)Ljava/util/List;" since="26" />
 		<method name="getInstalledProvidersForProfile(Landroid/os/UserHandle;)Ljava/util/List;" since="21" />
 		<method name="getInstance(Landroid/content/Context;)Landroid/appwidget/AppWidgetManager;" />
+		<method name="isRequestPinAppWidgetSupported()Z" since="26" />
 		<method name="notifyAppWidgetViewDataChanged(II)V" since="11" />
 		<method name="notifyAppWidgetViewDataChanged([II)V" since="11" />
 		<method name="partiallyUpdateAppWidget(ILandroid/widget/RemoteViews;)V" since="11" />
 		<method name="partiallyUpdateAppWidget([ILandroid/widget/RemoteViews;)V" since="11" />
+		<method name="requestPinAppWidget(Landroid/content/ComponentName;Landroid/os/Bundle;Landroid/app/PendingIntent;)Z" since="26" />
 		<method name="updateAppWidget(ILandroid/widget/RemoteViews;)V" />
 		<method name="updateAppWidget(Landroid/content/ComponentName;Landroid/widget/RemoteViews;)V" />
 		<method name="updateAppWidget([ILandroid/widget/RemoteViews;)V" />
@@ -6565,6 +7064,7 @@
 		<field name="EXTRA_APPWIDGET_IDS" />
 		<field name="EXTRA_APPWIDGET_OLD_IDS" since="21" />
 		<field name="EXTRA_APPWIDGET_OPTIONS" since="16" />
+		<field name="EXTRA_APPWIDGET_PREVIEW" since="26" />
 		<field name="EXTRA_APPWIDGET_PROVIDER" since="16" />
 		<field name="EXTRA_APPWIDGET_PROVIDER_PROFILE" since="21" />
 		<field name="EXTRA_CUSTOM_EXTRAS" />
@@ -6645,6 +7145,7 @@
 		<method name="getBluetoothLeScanner()Landroid/bluetooth/le/BluetoothLeScanner;" since="21" />
 		<method name="getBondedDevices()Ljava/util/Set;" />
 		<method name="getDefaultAdapter()Landroid/bluetooth/BluetoothAdapter;" />
+		<method name="getLeMaximumAdvertisingDataLength()I" since="26" />
 		<method name="getName()Ljava/lang/String;" />
 		<method name="getProfileConnectionState(I)I" since="14" />
 		<method name="getProfileProxy(Landroid/content/Context;Landroid/bluetooth/BluetoothProfile$ServiceListener;I)Z" since="11" />
@@ -6654,6 +7155,10 @@
 		<method name="getState()I" />
 		<method name="isDiscovering()Z" />
 		<method name="isEnabled()Z" />
+		<method name="isLe2MPhySupported()Z" since="26" />
+		<method name="isLeCodedPhySupported()Z" since="26" />
+		<method name="isLeExtendedAdvertisingSupported()Z" since="26" />
+		<method name="isLePeriodicAdvertisingSupported()Z" since="26" />
 		<method name="isMultipleAdvertisementSupported()Z" since="21" />
 		<method name="isOffloadedFilteringSupported()Z" since="21" />
 		<method name="isOffloadedScanBatchingSupported()Z" since="21" />
@@ -7025,6 +7530,8 @@
 		<method name="&lt;init>()V" />
 		<method name="connectGatt(Landroid/content/Context;ZLandroid/bluetooth/BluetoothGattCallback;)Landroid/bluetooth/BluetoothGatt;" since="18" />
 		<method name="connectGatt(Landroid/content/Context;ZLandroid/bluetooth/BluetoothGattCallback;I)Landroid/bluetooth/BluetoothGatt;" since="23" />
+		<method name="connectGatt(Landroid/content/Context;ZLandroid/bluetooth/BluetoothGattCallback;II)Landroid/bluetooth/BluetoothGatt;" since="26" />
+		<method name="connectGatt(Landroid/content/Context;ZLandroid/bluetooth/BluetoothGattCallback;IILandroid/os/Handler;)Landroid/bluetooth/BluetoothGatt;" since="26" />
 		<method name="createBond()Z" since="19" />
 		<method name="createInsecureRfcommSocketToServiceRecord(Ljava/util/UUID;)Landroid/bluetooth/BluetoothSocket;" since="10" />
 		<method name="createRfcommSocketToServiceRecord(Ljava/util/UUID;)Landroid/bluetooth/BluetoothSocket;" />
@@ -7066,6 +7573,15 @@
 		<field name="EXTRA_UUID" since="15" />
 		<field name="PAIRING_VARIANT_PASSKEY_CONFIRMATION" since="19" />
 		<field name="PAIRING_VARIANT_PIN" since="19" />
+		<field name="PHY_LE_1M" since="26" />
+		<field name="PHY_LE_1M_MASK" since="26" />
+		<field name="PHY_LE_2M" since="26" />
+		<field name="PHY_LE_2M_MASK" since="26" />
+		<field name="PHY_LE_CODED" since="26" />
+		<field name="PHY_LE_CODED_MASK" since="26" />
+		<field name="PHY_OPTION_NO_PREFERRED" since="26" />
+		<field name="PHY_OPTION_S2" since="26" />
+		<field name="PHY_OPTION_S8" since="26" />
 		<field name="TRANSPORT_AUTO" since="23" />
 		<field name="TRANSPORT_BREDR" since="23" />
 		<field name="TRANSPORT_LE" since="23" />
@@ -7087,10 +7603,12 @@
 		<method name="getServices()Ljava/util/List;" />
 		<method name="readCharacteristic(Landroid/bluetooth/BluetoothGattCharacteristic;)Z" />
 		<method name="readDescriptor(Landroid/bluetooth/BluetoothGattDescriptor;)Z" />
+		<method name="readPhy()V" since="26" />
 		<method name="readRemoteRssi()Z" />
 		<method name="requestConnectionPriority(I)Z" since="21" />
 		<method name="requestMtu(I)Z" since="21" />
 		<method name="setCharacteristicNotification(Landroid/bluetooth/BluetoothGattCharacteristic;Z)Z" />
+		<method name="setPreferredPhy(III)V" since="26" />
 		<method name="writeCharacteristic(Landroid/bluetooth/BluetoothGattCharacteristic;)Z" />
 		<method name="writeDescriptor(Landroid/bluetooth/BluetoothGattDescriptor;)Z" />
 		<field name="CONNECTION_PRIORITY_BALANCED" since="21" />
@@ -7117,6 +7635,8 @@
 		<method name="onDescriptorRead(Landroid/bluetooth/BluetoothGatt;Landroid/bluetooth/BluetoothGattDescriptor;I)V" />
 		<method name="onDescriptorWrite(Landroid/bluetooth/BluetoothGatt;Landroid/bluetooth/BluetoothGattDescriptor;I)V" />
 		<method name="onMtuChanged(Landroid/bluetooth/BluetoothGatt;II)V" since="21" />
+		<method name="onPhyRead(Landroid/bluetooth/BluetoothGatt;III)V" since="26" />
+		<method name="onPhyUpdate(Landroid/bluetooth/BluetoothGatt;III)V" since="26" />
 		<method name="onReadRemoteRssi(Landroid/bluetooth/BluetoothGatt;II)V" />
 		<method name="onReliableWriteCompleted(Landroid/bluetooth/BluetoothGatt;I)V" />
 		<method name="onServicesDiscovered(Landroid/bluetooth/BluetoothGatt;I)V" />
@@ -7207,8 +7727,10 @@
 		<method name="getService(Ljava/util/UUID;)Landroid/bluetooth/BluetoothGattService;" />
 		<method name="getServices()Ljava/util/List;" />
 		<method name="notifyCharacteristicChanged(Landroid/bluetooth/BluetoothDevice;Landroid/bluetooth/BluetoothGattCharacteristic;Z)Z" />
+		<method name="readPhy(Landroid/bluetooth/BluetoothDevice;)V" since="26" />
 		<method name="removeService(Landroid/bluetooth/BluetoothGattService;)Z" />
 		<method name="sendResponse(Landroid/bluetooth/BluetoothDevice;III[B)Z" />
+		<method name="setPreferredPhy(Landroid/bluetooth/BluetoothDevice;III)V" since="26" />
 	</class>
 	<class name="android/bluetooth/BluetoothGattServerCallback" since="18">
 		<extends name="java/lang/Object" />
@@ -7221,6 +7743,8 @@
 		<method name="onExecuteWrite(Landroid/bluetooth/BluetoothDevice;IZ)V" />
 		<method name="onMtuChanged(Landroid/bluetooth/BluetoothDevice;I)V" since="22" />
 		<method name="onNotificationSent(Landroid/bluetooth/BluetoothDevice;I)V" since="21" />
+		<method name="onPhyRead(Landroid/bluetooth/BluetoothDevice;III)V" since="26" />
+		<method name="onPhyUpdate(Landroid/bluetooth/BluetoothDevice;III)V" since="26" />
 		<method name="onServiceAdded(ILandroid/bluetooth/BluetoothGattService;)V" />
 	</class>
 	<class name="android/bluetooth/BluetoothGattService" since="18">
@@ -7416,20 +7940,115 @@
 		<method name="setTimeout(I)Landroid/bluetooth/le/AdvertiseSettings$Builder;" />
 		<method name="setTxPowerLevel(I)Landroid/bluetooth/le/AdvertiseSettings$Builder;" />
 	</class>
+	<class name="android/bluetooth/le/AdvertisingSet" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="enableAdvertising(ZII)V" />
+		<method name="setAdvertisingData(Landroid/bluetooth/le/AdvertiseData;)V" />
+		<method name="setAdvertisingParameters(Landroid/bluetooth/le/AdvertisingSetParameters;)V" />
+		<method name="setPeriodicAdvertisingData(Landroid/bluetooth/le/AdvertiseData;)V" />
+		<method name="setPeriodicAdvertisingEnabled(Z)V" />
+		<method name="setPeriodicAdvertisingParameters(Landroid/bluetooth/le/PeriodicAdvertisingParameters;)V" />
+		<method name="setScanResponseData(Landroid/bluetooth/le/AdvertiseData;)V" />
+	</class>
+	<class name="android/bluetooth/le/AdvertisingSetCallback" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="onAdvertisingDataSet(Landroid/bluetooth/le/AdvertisingSet;I)V" />
+		<method name="onAdvertisingEnabled(Landroid/bluetooth/le/AdvertisingSet;ZI)V" />
+		<method name="onAdvertisingParametersUpdated(Landroid/bluetooth/le/AdvertisingSet;II)V" />
+		<method name="onAdvertisingSetStarted(Landroid/bluetooth/le/AdvertisingSet;II)V" />
+		<method name="onAdvertisingSetStopped(Landroid/bluetooth/le/AdvertisingSet;)V" />
+		<method name="onPeriodicAdvertisingDataSet(Landroid/bluetooth/le/AdvertisingSet;I)V" />
+		<method name="onPeriodicAdvertisingEnabled(Landroid/bluetooth/le/AdvertisingSet;ZI)V" />
+		<method name="onPeriodicAdvertisingParametersUpdated(Landroid/bluetooth/le/AdvertisingSet;I)V" />
+		<method name="onScanResponseDataSet(Landroid/bluetooth/le/AdvertisingSet;I)V" />
+		<field name="ADVERTISE_FAILED_ALREADY_STARTED" />
+		<field name="ADVERTISE_FAILED_DATA_TOO_LARGE" />
+		<field name="ADVERTISE_FAILED_FEATURE_UNSUPPORTED" />
+		<field name="ADVERTISE_FAILED_INTERNAL_ERROR" />
+		<field name="ADVERTISE_FAILED_TOO_MANY_ADVERTISERS" />
+		<field name="ADVERTISE_SUCCESS" />
+	</class>
+	<class name="android/bluetooth/le/AdvertisingSetParameters" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/os/Parcelable" />
+		<method name="&lt;init>()V" />
+		<method name="getInterval()I" />
+		<method name="getPrimaryPhy()I" />
+		<method name="getSecondaryPhy()I" />
+		<method name="getTxPowerLevel()I" />
+		<method name="includeTxPower()Z" />
+		<method name="isAnonymous()Z" />
+		<method name="isConnectable()Z" />
+		<method name="isLegacy()Z" />
+		<method name="isScannable()Z" />
+		<field name="CREATOR" />
+		<field name="INTERVAL_HIGH" />
+		<field name="INTERVAL_LOW" />
+		<field name="INTERVAL_MAX" />
+		<field name="INTERVAL_MEDIUM" />
+		<field name="INTERVAL_MIN" />
+		<field name="TX_POWER_HIGH" />
+		<field name="TX_POWER_LOW" />
+		<field name="TX_POWER_MAX" />
+		<field name="TX_POWER_MEDIUM" />
+		<field name="TX_POWER_MIN" />
+		<field name="TX_POWER_ULTRA_LOW" />
+	</class>
+	<class name="android/bluetooth/le/AdvertisingSetParameters$Builder" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="build()Landroid/bluetooth/le/AdvertisingSetParameters;" />
+		<method name="setAnonymous(Z)Landroid/bluetooth/le/AdvertisingSetParameters$Builder;" />
+		<method name="setConnectable(Z)Landroid/bluetooth/le/AdvertisingSetParameters$Builder;" />
+		<method name="setIncludeTxPower(Z)Landroid/bluetooth/le/AdvertisingSetParameters$Builder;" />
+		<method name="setInterval(I)Landroid/bluetooth/le/AdvertisingSetParameters$Builder;" />
+		<method name="setLegacyMode(Z)Landroid/bluetooth/le/AdvertisingSetParameters$Builder;" />
+		<method name="setPrimaryPhy(I)Landroid/bluetooth/le/AdvertisingSetParameters$Builder;" />
+		<method name="setScannable(Z)Landroid/bluetooth/le/AdvertisingSetParameters$Builder;" />
+		<method name="setSecondaryPhy(I)Landroid/bluetooth/le/AdvertisingSetParameters$Builder;" />
+		<method name="setTxPowerLevel(I)Landroid/bluetooth/le/AdvertisingSetParameters$Builder;" />
+	</class>
 	<class name="android/bluetooth/le/BluetoothLeAdvertiser" since="21">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
 		<method name="startAdvertising(Landroid/bluetooth/le/AdvertiseSettings;Landroid/bluetooth/le/AdvertiseData;Landroid/bluetooth/le/AdvertiseCallback;)V" />
 		<method name="startAdvertising(Landroid/bluetooth/le/AdvertiseSettings;Landroid/bluetooth/le/AdvertiseData;Landroid/bluetooth/le/AdvertiseData;Landroid/bluetooth/le/AdvertiseCallback;)V" />
+		<method name="startAdvertisingSet(Landroid/bluetooth/le/AdvertisingSetParameters;Landroid/bluetooth/le/AdvertiseData;Landroid/bluetooth/le/AdvertiseData;Landroid/bluetooth/le/PeriodicAdvertisingParameters;Landroid/bluetooth/le/AdvertiseData;IILandroid/bluetooth/le/AdvertisingSetCallback;)V" since="26" />
+		<method name="startAdvertisingSet(Landroid/bluetooth/le/AdvertisingSetParameters;Landroid/bluetooth/le/AdvertiseData;Landroid/bluetooth/le/AdvertiseData;Landroid/bluetooth/le/PeriodicAdvertisingParameters;Landroid/bluetooth/le/AdvertiseData;IILandroid/bluetooth/le/AdvertisingSetCallback;Landroid/os/Handler;)V" since="26" />
+		<method name="startAdvertisingSet(Landroid/bluetooth/le/AdvertisingSetParameters;Landroid/bluetooth/le/AdvertiseData;Landroid/bluetooth/le/AdvertiseData;Landroid/bluetooth/le/PeriodicAdvertisingParameters;Landroid/bluetooth/le/AdvertiseData;Landroid/bluetooth/le/AdvertisingSetCallback;)V" since="26" />
+		<method name="startAdvertisingSet(Landroid/bluetooth/le/AdvertisingSetParameters;Landroid/bluetooth/le/AdvertiseData;Landroid/bluetooth/le/AdvertiseData;Landroid/bluetooth/le/PeriodicAdvertisingParameters;Landroid/bluetooth/le/AdvertiseData;Landroid/bluetooth/le/AdvertisingSetCallback;Landroid/os/Handler;)V" since="26" />
 		<method name="stopAdvertising(Landroid/bluetooth/le/AdvertiseCallback;)V" />
+		<method name="stopAdvertisingSet(Landroid/bluetooth/le/AdvertisingSetCallback;)V" since="26" />
 	</class>
 	<class name="android/bluetooth/le/BluetoothLeScanner" since="21">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
 		<method name="flushPendingScanResults(Landroid/bluetooth/le/ScanCallback;)V" />
 		<method name="startScan(Landroid/bluetooth/le/ScanCallback;)V" />
+		<method name="startScan(Ljava/util/List;Landroid/bluetooth/le/ScanSettings;Landroid/app/PendingIntent;)I" since="26" />
 		<method name="startScan(Ljava/util/List;Landroid/bluetooth/le/ScanSettings;Landroid/bluetooth/le/ScanCallback;)V" />
+		<method name="stopScan(Landroid/app/PendingIntent;)V" since="26" />
 		<method name="stopScan(Landroid/bluetooth/le/ScanCallback;)V" />
+		<field name="EXTRA_CALLBACK_TYPE" since="26" />
+		<field name="EXTRA_ERROR_CODE" since="26" />
+		<field name="EXTRA_LIST_SCAN_RESULT" since="26" />
+	</class>
+	<class name="android/bluetooth/le/PeriodicAdvertisingParameters" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/os/Parcelable" />
+		<method name="&lt;init>()V" />
+		<method name="getIncludeTxPower()Z" />
+		<method name="getInterval()I" />
+		<field name="CREATOR" />
+	</class>
+	<class name="android/bluetooth/le/PeriodicAdvertisingParameters$Builder" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="build()Landroid/bluetooth/le/PeriodicAdvertisingParameters;" />
+		<method name="setIncludeTxPower(Z)Landroid/bluetooth/le/PeriodicAdvertisingParameters$Builder;" />
+		<method name="setInterval(I)Landroid/bluetooth/le/PeriodicAdvertisingParameters$Builder;" />
 	</class>
 	<class name="android/bluetooth/le/ScanCallback" since="21">
 		<extends name="java/lang/Object" />
@@ -7488,18 +8107,35 @@
 	<class name="android/bluetooth/le/ScanResult" since="21">
 		<extends name="java/lang/Object" />
 		<implements name="android/os/Parcelable" />
-		<method name="&lt;init>(Landroid/bluetooth/BluetoothDevice;Landroid/bluetooth/le/ScanRecord;IJ)V" />
+		<method name="&lt;init>(Landroid/bluetooth/BluetoothDevice;IIIIIIILandroid/bluetooth/le/ScanRecord;J)V" since="26" />
+		<method name="&lt;init>(Landroid/bluetooth/BluetoothDevice;Landroid/bluetooth/le/ScanRecord;IJ)V" deprecated="26" />
+		<method name="getAdvertisingSid()I" since="26" />
+		<method name="getDataStatus()I" since="26" />
 		<method name="getDevice()Landroid/bluetooth/BluetoothDevice;" />
+		<method name="getPeriodicAdvertisingInterval()I" since="26" />
+		<method name="getPrimaryPhy()I" since="26" />
 		<method name="getRssi()I" />
 		<method name="getScanRecord()Landroid/bluetooth/le/ScanRecord;" />
+		<method name="getSecondaryPhy()I" since="26" />
 		<method name="getTimestampNanos()J" />
+		<method name="getTxPower()I" since="26" />
+		<method name="isConnectable()Z" since="26" />
+		<method name="isLegacy()Z" since="26" />
 		<field name="CREATOR" />
+		<field name="DATA_COMPLETE" since="26" />
+		<field name="DATA_TRUNCATED" since="26" />
+		<field name="PERIODIC_INTERVAL_NOT_PRESENT" since="26" />
+		<field name="PHY_UNUSED" since="26" />
+		<field name="SID_NOT_PRESENT" since="26" />
+		<field name="TX_POWER_NOT_PRESENT" since="26" />
 	</class>
 	<class name="android/bluetooth/le/ScanSettings" since="21">
 		<extends name="java/lang/Object" />
 		<implements name="android/os/Parcelable" />
 		<method name="&lt;init>()V" />
 		<method name="getCallbackType()I" />
+		<method name="getLegacy()Z" since="26" />
+		<method name="getPhy()I" since="26" />
 		<method name="getReportDelayMillis()J" />
 		<method name="getScanMode()I" />
 		<method name="getScanResultType()I" />
@@ -7512,6 +8148,7 @@
 		<field name="MATCH_NUM_FEW_ADVERTISEMENT" since="23" />
 		<field name="MATCH_NUM_MAX_ADVERTISEMENT" since="23" />
 		<field name="MATCH_NUM_ONE_ADVERTISEMENT" since="23" />
+		<field name="PHY_LE_ALL_SUPPORTED" since="26" />
 		<field name="SCAN_MODE_BALANCED" />
 		<field name="SCAN_MODE_LOW_LATENCY" />
 		<field name="SCAN_MODE_LOW_POWER" />
@@ -7522,11 +8159,89 @@
 		<method name="&lt;init>()V" />
 		<method name="build()Landroid/bluetooth/le/ScanSettings;" />
 		<method name="setCallbackType(I)Landroid/bluetooth/le/ScanSettings$Builder;" since="23" />
+		<method name="setLegacy(Z)Landroid/bluetooth/le/ScanSettings$Builder;" since="26" />
 		<method name="setMatchMode(I)Landroid/bluetooth/le/ScanSettings$Builder;" since="23" />
 		<method name="setNumOfMatches(I)Landroid/bluetooth/le/ScanSettings$Builder;" since="23" />
+		<method name="setPhy(I)Landroid/bluetooth/le/ScanSettings$Builder;" since="26" />
 		<method name="setReportDelay(J)Landroid/bluetooth/le/ScanSettings$Builder;" />
 		<method name="setScanMode(I)Landroid/bluetooth/le/ScanSettings$Builder;" />
 	</class>
+	<class name="android/companion/AssociationRequest" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/os/Parcelable" />
+		<method name="&lt;init>()V" />
+		<field name="CREATOR" />
+	</class>
+	<class name="android/companion/AssociationRequest$Builder" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="addDeviceFilter(Landroid/companion/DeviceFilter;)Landroid/companion/AssociationRequest$Builder;" />
+		<method name="build()Landroid/companion/AssociationRequest;" />
+		<method name="setSingleDevice(Z)Landroid/companion/AssociationRequest$Builder;" />
+	</class>
+	<class name="android/companion/BluetoothDeviceFilter" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/companion/DeviceFilter" />
+		<method name="&lt;init>()V" />
+		<field name="CREATOR" />
+	</class>
+	<class name="android/companion/BluetoothDeviceFilter$Builder" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="addServiceUuid(Landroid/os/ParcelUuid;Landroid/os/ParcelUuid;)Landroid/companion/BluetoothDeviceFilter$Builder;" />
+		<method name="build()Landroid/companion/BluetoothDeviceFilter;" />
+		<method name="setAddress(Ljava/lang/String;)Landroid/companion/BluetoothDeviceFilter$Builder;" />
+		<method name="setNamePattern(Ljava/util/regex/Pattern;)Landroid/companion/BluetoothDeviceFilter$Builder;" />
+	</class>
+	<class name="android/companion/BluetoothLeDeviceFilter" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/companion/DeviceFilter" />
+		<method name="&lt;init>()V" />
+		<method name="getRenamePrefixLengthLimit()I" />
+		<field name="CREATOR" />
+	</class>
+	<class name="android/companion/BluetoothLeDeviceFilter$Builder" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="build()Landroid/companion/BluetoothLeDeviceFilter;" />
+		<method name="setNamePattern(Ljava/util/regex/Pattern;)Landroid/companion/BluetoothLeDeviceFilter$Builder;" />
+		<method name="setRawDataFilter([B[B)Landroid/companion/BluetoothLeDeviceFilter$Builder;" />
+		<method name="setRenameFromBytes(Ljava/lang/String;Ljava/lang/String;IILjava/nio/ByteOrder;)Landroid/companion/BluetoothLeDeviceFilter$Builder;" />
+		<method name="setRenameFromName(Ljava/lang/String;Ljava/lang/String;II)Landroid/companion/BluetoothLeDeviceFilter$Builder;" />
+		<method name="setScanFilter(Landroid/bluetooth/le/ScanFilter;)Landroid/companion/BluetoothLeDeviceFilter$Builder;" />
+	</class>
+	<class name="android/companion/CompanionDeviceManager" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="associate(Landroid/companion/AssociationRequest;Landroid/companion/CompanionDeviceManager$Callback;Landroid/os/Handler;)V" />
+		<method name="disassociate(Ljava/lang/String;)V" />
+		<method name="getAssociations()Ljava/util/List;" />
+		<method name="hasNotificationAccess(Landroid/content/ComponentName;)Z" />
+		<method name="requestNotificationAccess(Landroid/content/ComponentName;)V" />
+		<field name="EXTRA_DEVICE" />
+	</class>
+	<class name="android/companion/CompanionDeviceManager$Callback" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="onDeviceFound(Landroid/content/IntentSender;)V" />
+		<method name="onFailure(Ljava/lang/CharSequence;)V" />
+	</class>
+	<class name="android/companion/DeviceFilter" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/os/Parcelable" />
+	</class>
+	<class name="android/companion/WifiDeviceFilter" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/companion/DeviceFilter" />
+		<method name="&lt;init>()V" />
+		<field name="CREATOR" />
+	</class>
+	<class name="android/companion/WifiDeviceFilter$Builder" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="build()Landroid/companion/WifiDeviceFilter;" />
+		<method name="setNamePattern(Ljava/util/regex/Pattern;)Landroid/companion/WifiDeviceFilter$Builder;" />
+	</class>
 	<class name="android/content/AbstractThreadedSyncAdapter" since="5">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>(Landroid/content/Context;Z)V" />
@@ -7630,6 +8345,7 @@
 		<method name="&lt;init>(Landroid/content/ClipDescription;Landroid/content/ClipData$Item;)V" />
 		<method name="&lt;init>(Ljava/lang/CharSequence;[Ljava/lang/String;Landroid/content/ClipData$Item;)V" />
 		<method name="addItem(Landroid/content/ClipData$Item;)V" />
+		<method name="addItem(Landroid/content/ContentResolver;Landroid/content/ClipData$Item;)V" since="26" />
 		<method name="getDescription()Landroid/content/ClipDescription;" />
 		<method name="getItemAt(I)Landroid/content/ClipData$Item;" />
 		<method name="getItemCount()I" />
@@ -7667,6 +8383,7 @@
 		<method name="getLabel()Ljava/lang/CharSequence;" />
 		<method name="getMimeType(I)Ljava/lang/String;" />
 		<method name="getMimeTypeCount()I" />
+		<method name="getTimestamp()J" since="26" />
 		<method name="hasMimeType(Ljava/lang/String;)Z" />
 		<method name="setExtras(Landroid/os/PersistableBundle;)V" since="24" />
 		<field name="CREATOR" />
@@ -7760,8 +8477,10 @@
 		<method name="openPipeHelper(Landroid/net/Uri;Ljava/lang/String;Landroid/os/Bundle;Ljava/lang/Object;Landroid/content/ContentProvider$PipeDataWriter;)Landroid/os/ParcelFileDescriptor;" since="11" />
 		<method name="openTypedAssetFile(Landroid/net/Uri;Ljava/lang/String;Landroid/os/Bundle;)Landroid/content/res/AssetFileDescriptor;" since="11" />
 		<method name="openTypedAssetFile(Landroid/net/Uri;Ljava/lang/String;Landroid/os/Bundle;Landroid/os/CancellationSignal;)Landroid/content/res/AssetFileDescriptor;" since="19" />
+		<method name="query(Landroid/net/Uri;[Ljava/lang/String;Landroid/os/Bundle;Landroid/os/CancellationSignal;)Landroid/database/Cursor;" since="26" />
 		<method name="query(Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;" />
 		<method name="query(Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;Landroid/os/CancellationSignal;)Landroid/database/Cursor;" since="16" />
+		<method name="refresh(Landroid/net/Uri;Landroid/os/Bundle;Landroid/os/CancellationSignal;)Z" since="26" />
 		<method name="setPathPermissions([Landroid/content/pm/PathPermission;)V" since="4" />
 		<method name="setReadPermission(Ljava/lang/String;)V" />
 		<method name="setWritePermission(Ljava/lang/String;)V" />
@@ -7792,8 +8511,10 @@
 		<method name="openFile(Landroid/net/Uri;Ljava/lang/String;Landroid/os/CancellationSignal;)Landroid/os/ParcelFileDescriptor;" since="19" />
 		<method name="openTypedAssetFileDescriptor(Landroid/net/Uri;Ljava/lang/String;Landroid/os/Bundle;)Landroid/content/res/AssetFileDescriptor;" since="11" />
 		<method name="openTypedAssetFileDescriptor(Landroid/net/Uri;Ljava/lang/String;Landroid/os/Bundle;Landroid/os/CancellationSignal;)Landroid/content/res/AssetFileDescriptor;" since="19" />
+		<method name="query(Landroid/net/Uri;[Ljava/lang/String;Landroid/os/Bundle;Landroid/os/CancellationSignal;)Landroid/database/Cursor;" since="26" />
 		<method name="query(Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;" />
 		<method name="query(Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;Landroid/os/CancellationSignal;)Landroid/database/Cursor;" since="16" />
+		<method name="refresh(Landroid/net/Uri;Landroid/os/Bundle;Landroid/os/CancellationSignal;)Z" since="26" />
 		<method name="release()Z" deprecated="24" />
 		<method name="uncanonicalize(Landroid/net/Uri;)Landroid/net/Uri;" since="19" />
 		<method name="update(Landroid/net/Uri;Landroid/content/ContentValues;Ljava/lang/String;[Ljava/lang/String;)I" />
@@ -7894,8 +8615,10 @@
 		<method name="openOutputStream(Landroid/net/Uri;Ljava/lang/String;)Ljava/io/OutputStream;" since="3" />
 		<method name="openTypedAssetFileDescriptor(Landroid/net/Uri;Ljava/lang/String;Landroid/os/Bundle;)Landroid/content/res/AssetFileDescriptor;" since="11" />
 		<method name="openTypedAssetFileDescriptor(Landroid/net/Uri;Ljava/lang/String;Landroid/os/Bundle;Landroid/os/CancellationSignal;)Landroid/content/res/AssetFileDescriptor;" since="19" />
+		<method name="query(Landroid/net/Uri;[Ljava/lang/String;Landroid/os/Bundle;Landroid/os/CancellationSignal;)Landroid/database/Cursor;" since="26" />
 		<method name="query(Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;" />
 		<method name="query(Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;Landroid/os/CancellationSignal;)Landroid/database/Cursor;" since="16" />
+		<method name="refresh(Landroid/net/Uri;Landroid/os/Bundle;Landroid/os/CancellationSignal;)Z" since="26" />
 		<method name="registerContentObserver(Landroid/net/Uri;ZLandroid/database/ContentObserver;)V" />
 		<method name="releasePersistableUriPermission(Landroid/net/Uri;I)V" since="19" />
 		<method name="removePeriodicSync(Landroid/accounts/Account;Ljava/lang/String;Landroid/os/Bundle;)V" since="8" />
@@ -7914,9 +8637,22 @@
 		<field name="ANY_CURSOR_ITEM_TYPE" since="21" />
 		<field name="CURSOR_DIR_BASE_TYPE" />
 		<field name="CURSOR_ITEM_BASE_TYPE" />
+		<field name="EXTRA_HONORED_ARGS" since="26" />
+		<field name="EXTRA_REFRESH_SUPPORTED" since="26" />
 		<field name="EXTRA_SIZE" since="21" />
+		<field name="EXTRA_TOTAL_COUNT" since="26" />
 		<field name="NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS" since="24" />
 		<field name="NOTIFY_SYNC_TO_NETWORK" since="24" />
+		<field name="QUERY_ARG_LIMIT" since="26" />
+		<field name="QUERY_ARG_OFFSET" since="26" />
+		<field name="QUERY_ARG_SORT_COLLATION" since="26" />
+		<field name="QUERY_ARG_SORT_COLUMNS" since="26" />
+		<field name="QUERY_ARG_SORT_DIRECTION" since="26" />
+		<field name="QUERY_ARG_SQL_SELECTION" since="26" />
+		<field name="QUERY_ARG_SQL_SELECTION_ARGS" since="26" />
+		<field name="QUERY_ARG_SQL_SORT_ORDER" since="26" />
+		<field name="QUERY_SORT_DIRECTION_ASCENDING" since="26" />
+		<field name="QUERY_SORT_DIRECTION_DESCENDING" since="26" />
 		<field name="SCHEME_ANDROID_RESOURCE" />
 		<field name="SCHEME_CONTENT" />
 		<field name="SCHEME_FILE" />
@@ -7993,6 +8729,7 @@
 		<method name="checkUriPermission(Landroid/net/Uri;Ljava/lang/String;Ljava/lang/String;III)I" />
 		<method name="clearWallpaper()V" deprecated="16" />
 		<method name="createConfigurationContext(Landroid/content/res/Configuration;)Landroid/content/Context;" since="17" />
+		<method name="createContextForSplit(Ljava/lang/String;)Landroid/content/Context;" since="26" />
 		<method name="createDeviceProtectedStorageContext()Landroid/content/Context;" since="24" />
 		<method name="createDisplayContext(Landroid/view/Display;)Landroid/content/Context;" since="17" />
 		<method name="createPackageContext(Ljava/lang/String;I)Landroid/content/Context;" />
@@ -8064,10 +8801,13 @@
 		<method name="peekWallpaper()Landroid/graphics/drawable/Drawable;" deprecated="16" />
 		<method name="registerComponentCallbacks(Landroid/content/ComponentCallbacks;)V" since="14" />
 		<method name="registerReceiver(Landroid/content/BroadcastReceiver;Landroid/content/IntentFilter;)Landroid/content/Intent;" />
+		<method name="registerReceiver(Landroid/content/BroadcastReceiver;Landroid/content/IntentFilter;I)Landroid/content/Intent;" since="26" />
 		<method name="registerReceiver(Landroid/content/BroadcastReceiver;Landroid/content/IntentFilter;Ljava/lang/String;Landroid/os/Handler;)Landroid/content/Intent;" />
+		<method name="registerReceiver(Landroid/content/BroadcastReceiver;Landroid/content/IntentFilter;Ljava/lang/String;Landroid/os/Handler;I)Landroid/content/Intent;" since="26" />
 		<method name="removeStickyBroadcast(Landroid/content/Intent;)V" deprecated="21" />
 		<method name="removeStickyBroadcastAsUser(Landroid/content/Intent;Landroid/os/UserHandle;)V" since="17" deprecated="21" />
 		<method name="revokeUriPermission(Landroid/net/Uri;I)V" />
+		<method name="revokeUriPermission(Ljava/lang/String;Landroid/net/Uri;I)V" since="26" />
 		<method name="sendBroadcast(Landroid/content/Intent;)V" />
 		<method name="sendBroadcast(Landroid/content/Intent;Ljava/lang/String;)V" />
 		<method name="sendBroadcastAsUser(Landroid/content/Intent;Landroid/os/UserHandle;)V" since="17" />
@@ -8086,6 +8826,7 @@
 		<method name="startActivities([Landroid/content/Intent;Landroid/os/Bundle;)V" since="16" />
 		<method name="startActivity(Landroid/content/Intent;)V" />
 		<method name="startActivity(Landroid/content/Intent;Landroid/os/Bundle;)V" since="16" />
+		<method name="startForegroundService(Landroid/content/Intent;)Landroid/content/ComponentName;" since="26" />
 		<method name="startInstrumentation(Landroid/content/ComponentName;Ljava/lang/String;Landroid/os/Bundle;)Z" />
 		<method name="startIntentSender(Landroid/content/IntentSender;Landroid/content/Intent;III)V" since="5" />
 		<method name="startIntentSender(Landroid/content/IntentSender;Landroid/content/Intent;IIILandroid/os/Bundle;)V" since="16" />
@@ -8116,6 +8857,7 @@
 		<field name="CAPTIONING_SERVICE" since="19" />
 		<field name="CARRIER_CONFIG_SERVICE" since="23" />
 		<field name="CLIPBOARD_SERVICE" />
+		<field name="COMPANION_DEVICE_SERVICE" since="26" />
 		<field name="CONNECTIVITY_SERVICE" />
 		<field name="CONSUMER_IR_SERVICE" since="19" />
 		<field name="CONTEXT_IGNORE_SECURITY" />
@@ -8151,15 +8893,18 @@
 		<field name="NSD_SERVICE" since="16" />
 		<field name="POWER_SERVICE" />
 		<field name="PRINT_SERVICE" since="19" />
+		<field name="RECEIVER_VISIBLE_TO_INSTANT_APPS" since="26" />
 		<field name="RESTRICTIONS_SERVICE" since="21" />
 		<field name="SEARCH_SERVICE" />
 		<field name="SENSOR_SERVICE" />
 		<field name="SHORTCUT_SERVICE" since="25" />
 		<field name="STORAGE_SERVICE" since="9" />
+		<field name="STORAGE_STATS_SERVICE" since="26" />
 		<field name="SYSTEM_HEALTH_SERVICE" since="24" />
 		<field name="TELECOM_SERVICE" since="21" />
 		<field name="TELEPHONY_SERVICE" />
 		<field name="TELEPHONY_SUBSCRIPTION_SERVICE" since="22" />
+		<field name="TEXT_CLASSIFICATION_SERVICE" since="26" />
 		<field name="TEXT_SERVICES_MANAGER_SERVICE" since="14" />
 		<field name="TV_INPUT_SERVICE" since="21" />
 		<field name="UI_MODE_SERVICE" since="8" />
@@ -8168,6 +8913,7 @@
 		<field name="USER_SERVICE" since="17" />
 		<field name="VIBRATOR_SERVICE" />
 		<field name="WALLPAPER_SERVICE" />
+		<field name="WIFI_AWARE_SERVICE" since="26" />
 		<field name="WIFI_P2P_SERVICE" since="14" />
 		<field name="WIFI_SERVICE" />
 		<field name="WINDOW_SERVICE" />
@@ -8355,6 +9101,7 @@
 		<method name="readFromParcel(Landroid/os/Parcel;)V" />
 		<method name="removeCategory(Ljava/lang/String;)V" />
 		<method name="removeExtra(Ljava/lang/String;)V" />
+		<method name="removeFlags(I)V" since="26" />
 		<method name="replaceExtras(Landroid/content/Intent;)Landroid/content/Intent;" since="3" />
 		<method name="replaceExtras(Landroid/os/Bundle;)Landroid/content/Intent;" since="3" />
 		<method name="resolveActivity(Landroid/content/pm/PackageManager;)Landroid/content/ComponentName;" />
@@ -8397,6 +9144,7 @@
 		<field name="ACTION_CALL" />
 		<field name="ACTION_CALL_BUTTON" />
 		<field name="ACTION_CAMERA_BUTTON" />
+		<field name="ACTION_CARRIER_SETUP" since="26" />
 		<field name="ACTION_CHOOSER" />
 		<field name="ACTION_CLOSE_SYSTEM_DIALOGS" />
 		<field name="ACTION_CONFIGURATION_CHANGED" />
@@ -8405,8 +9153,8 @@
 		<field name="ACTION_DATE_CHANGED" />
 		<field name="ACTION_DEFAULT" />
 		<field name="ACTION_DELETE" />
-		<field name="ACTION_DEVICE_STORAGE_LOW" />
-		<field name="ACTION_DEVICE_STORAGE_OK" />
+		<field name="ACTION_DEVICE_STORAGE_LOW" deprecated="26" />
+		<field name="ACTION_DEVICE_STORAGE_OK" deprecated="26" />
 		<field name="ACTION_DIAL" />
 		<field name="ACTION_DOCK_EVENT" since="5" />
 		<field name="ACTION_DREAMING_STARTED" since="17" />
@@ -8537,8 +9285,10 @@
 		<field name="CATEGORY_SELECTED_ALTERNATIVE" />
 		<field name="CATEGORY_TAB" />
 		<field name="CATEGORY_TEST" />
+		<field name="CATEGORY_TYPED_OPENABLE" since="26" />
 		<field name="CATEGORY_UNIT_TEST" />
 		<field name="CATEGORY_VOICE" since="23" />
+		<field name="CATEGORY_VR_HOME" since="26" />
 		<field name="CREATOR" />
 		<field name="EXTRA_ALARM_COUNT" />
 		<field name="EXTRA_ALLOW_MULTIPLE" since="18" />
@@ -8560,6 +9310,8 @@
 		<field name="EXTRA_CHOOSER_TARGETS" since="24" />
 		<field name="EXTRA_CHOSEN_COMPONENT" since="22" />
 		<field name="EXTRA_CHOSEN_COMPONENT_INTENT_SENDER" since="22" />
+		<field name="EXTRA_COMPONENT_NAME" since="26" />
+		<field name="EXTRA_CONTENT_ANNOTATIONS" since="26" />
 		<field name="EXTRA_DATA_REMOVED" since="3" />
 		<field name="EXTRA_DOCK_STATE" since="5" />
 		<field name="EXTRA_DOCK_STATE_CAR" since="5" />
@@ -8570,6 +9322,7 @@
 		<field name="EXTRA_DONT_KILL_APP" />
 		<field name="EXTRA_EMAIL" />
 		<field name="EXTRA_EXCLUDE_COMPONENTS" since="24" />
+		<field name="EXTRA_FROM_STORAGE" since="26" />
 		<field name="EXTRA_HTML_TEXT" since="16" />
 		<field name="EXTRA_INDEX" since="24" />
 		<field name="EXTRA_INITIAL_INTENTS" since="5" />
@@ -8584,6 +9337,7 @@
 		<field name="EXTRA_PHONE_NUMBER" />
 		<field name="EXTRA_PROCESS_TEXT" since="23" />
 		<field name="EXTRA_PROCESS_TEXT_READONLY" since="23" />
+		<field name="EXTRA_QUICK_VIEW_FEATURES" since="26" />
 		<field name="EXTRA_QUIET_MODE" since="24" />
 		<field name="EXTRA_REFERRER" since="17" />
 		<field name="EXTRA_REFERRER_NAME" since="22" />
@@ -8595,10 +9349,10 @@
 		<field name="EXTRA_RESTRICTIONS_LIST" since="18" />
 		<field name="EXTRA_RESULT_RECEIVER" since="23" />
 		<field name="EXTRA_RETURN_RESULT" since="14" />
-		<field name="EXTRA_SHORTCUT_ICON" />
-		<field name="EXTRA_SHORTCUT_ICON_RESOURCE" />
-		<field name="EXTRA_SHORTCUT_INTENT" />
-		<field name="EXTRA_SHORTCUT_NAME" />
+		<field name="EXTRA_SHORTCUT_ICON" deprecated="26" />
+		<field name="EXTRA_SHORTCUT_ICON_RESOURCE" deprecated="26" />
+		<field name="EXTRA_SHORTCUT_INTENT" deprecated="26" />
+		<field name="EXTRA_SHORTCUT_NAME" deprecated="26" />
 		<field name="EXTRA_SHUTDOWN_USERSPACE_ONLY" since="19" />
 		<field name="EXTRA_STREAM" />
 		<field name="EXTRA_SUBJECT" />
@@ -8648,6 +9402,7 @@
 		<field name="FLAG_RECEIVER_NO_ABORT" since="19" />
 		<field name="FLAG_RECEIVER_REGISTERED_ONLY" />
 		<field name="FLAG_RECEIVER_REPLACE_PENDING" since="8" />
+		<field name="FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS" since="26" />
 		<field name="METADATA_DOCK_HOME" since="5" />
 		<field name="URI_ALLOW_UNSAFE" since="22" />
 		<field name="URI_ANDROID_APP_SCHEME" since="22" />
@@ -8845,6 +9600,15 @@
 		<field name="extras" />
 		<field name="period" />
 	</class>
+	<class name="android/content/QuickViewConstants" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<field name="FEATURE_DOWNLOAD" />
+		<field name="FEATURE_EDIT" />
+		<field name="FEATURE_PRINT" />
+		<field name="FEATURE_SEND" />
+		<field name="FEATURE_VIEW" />
+	</class>
 	<class name="android/content/ReceiverCallNotAllowedException" since="1">
 		<extends name="android/util/AndroidRuntimeException" />
 		<method name="&lt;init>(Ljava/lang/String;)V" />
@@ -8943,6 +9707,7 @@
 	</class>
 	<class name="android/content/ServiceConnection" since="1">
 		<extends name="java/lang/Object" />
+		<method name="onBindingDied(Landroid/content/ComponentName;)V" since="26" />
 		<method name="onServiceConnected(Landroid/content/ComponentName;Landroid/os/IBinder;)V" />
 		<method name="onServiceDisconnected(Landroid/content/ComponentName;)V" />
 	</class>
@@ -9097,6 +9862,10 @@
 		<method name="&lt;init>(Landroid/content/pm/ActivityInfo;)V" />
 		<method name="dump(Landroid/util/Printer;Ljava/lang/String;)V" />
 		<method name="getThemeResource()I" />
+		<field name="COLOR_MODE_DEFAULT" since="26" />
+		<field name="COLOR_MODE_HDR" since="26" />
+		<field name="COLOR_MODE_WIDE_COLOR_GAMUT" since="26" />
+		<field name="CONFIG_COLOR_MODE" since="26" />
 		<field name="CONFIG_DENSITY" since="17" />
 		<field name="CONFIG_FONT_SCALE" />
 		<field name="CONFIG_KEYBOARD" />
@@ -9157,6 +9926,7 @@
 		<field name="SCREEN_ORIENTATION_USER_LANDSCAPE" since="18" />
 		<field name="SCREEN_ORIENTATION_USER_PORTRAIT" since="18" />
 		<field name="UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW" since="14" />
+		<field name="colorMode" since="26" />
 		<field name="configChanges" />
 		<field name="documentLaunchMode" since="21" />
 		<field name="flags" />
@@ -9190,7 +9960,17 @@
 		<method name="&lt;init>()V" />
 		<method name="&lt;init>(Landroid/content/pm/ApplicationInfo;)V" />
 		<method name="dump(Landroid/util/Printer;Ljava/lang/String;)V" />
+		<method name="getCategoryTitle(Landroid/content/Context;I)Ljava/lang/CharSequence;" since="26" />
 		<method name="loadDescription(Landroid/content/pm/PackageManager;)Ljava/lang/CharSequence;" />
+		<field name="CATEGORY_AUDIO" since="26" />
+		<field name="CATEGORY_GAME" since="26" />
+		<field name="CATEGORY_IMAGE" since="26" />
+		<field name="CATEGORY_MAPS" since="26" />
+		<field name="CATEGORY_NEWS" since="26" />
+		<field name="CATEGORY_PRODUCTIVITY" since="26" />
+		<field name="CATEGORY_SOCIAL" since="26" />
+		<field name="CATEGORY_UNDEFINED" since="26" />
+		<field name="CATEGORY_VIDEO" since="26" />
 		<field name="CREATOR" />
 		<field name="FLAG_ALLOW_BACKUP" since="8" />
 		<field name="FLAG_ALLOW_CLEAR_USER_DATA" />
@@ -9204,7 +9984,7 @@
 		<field name="FLAG_HAS_CODE" />
 		<field name="FLAG_INSTALLED" since="17" />
 		<field name="FLAG_IS_DATA_ONLY" since="17" />
-		<field name="FLAG_IS_GAME" since="21" />
+		<field name="FLAG_IS_GAME" since="21" deprecated="26" />
 		<field name="FLAG_KILL_AFTER_RESTORE" since="8" />
 		<field name="FLAG_LARGE_HEAP" since="11" />
 		<field name="FLAG_MULTIARCH" since="21" />
@@ -9225,6 +10005,7 @@
 		<field name="FLAG_USES_CLEARTEXT_TRAFFIC" since="23" />
 		<field name="FLAG_VM_SAFE_MODE" since="8" />
 		<field name="backupAgentName" since="8" />
+		<field name="category" since="26" />
 		<field name="className" />
 		<field name="compatibleWidthLimitDp" since="13" />
 		<field name="dataDir" />
@@ -9242,8 +10023,10 @@
 		<field name="requiresSmallestWidthDp" since="13" />
 		<field name="sharedLibraryFiles" />
 		<field name="sourceDir" />
+		<field name="splitNames" since="26" />
 		<field name="splitPublicSourceDirs" since="21" />
 		<field name="splitSourceDirs" since="21" />
+		<field name="storageUuid" since="26" />
 		<field name="targetSdkVersion" since="4" />
 		<field name="taskAffinity" />
 		<field name="theme" />
@@ -9256,6 +10039,14 @@
 		<method name="&lt;init>(Landroid/content/pm/PackageManager;)V" />
 		<method name="compare(Landroid/content/pm/ApplicationInfo;Landroid/content/pm/ApplicationInfo;)I" />
 	</class>
+	<class name="android/content/pm/ChangedPackages" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/os/Parcelable" />
+		<method name="&lt;init>(ILjava/util/List;)V" />
+		<method name="getPackageNames()Ljava/util/List;" />
+		<method name="getSequenceNumber()I" />
+		<field name="CREATOR" />
+	</class>
 	<class name="android/content/pm/ComponentInfo" since="1">
 		<extends name="android/content/pm/PackageItemInfo" />
 		<method name="&lt;init>()V" />
@@ -9271,6 +10062,7 @@
 		<field name="enabled" />
 		<field name="exported" />
 		<field name="processName" />
+		<field name="splitName" since="26" />
 	</class>
 	<class name="android/content/pm/ConfigurationInfo" since="3">
 		<extends name="java/lang/Object" />
@@ -9332,9 +10124,11 @@
 		<field name="handleProfiling" />
 		<field name="publicSourceDir" />
 		<field name="sourceDir" />
+		<field name="splitNames" since="26" />
 		<field name="splitPublicSourceDirs" since="21" />
 		<field name="splitSourceDirs" since="21" />
 		<field name="targetPackage" />
+		<field name="targetProcesses" since="26" />
 	</class>
 	<class name="android/content/pm/LabeledIntent" since="5">
 		<extends name="android/content/Intent" />
@@ -9366,7 +10160,12 @@
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
 		<method name="getActivityList(Ljava/lang/String;Landroid/os/UserHandle;)Ljava/util/List;" />
+		<method name="getApplicationInfo(Ljava/lang/String;ILandroid/os/UserHandle;)Landroid/content/pm/ApplicationInfo;" since="26" />
+		<method name="getPinItemRequest(Landroid/content/Intent;)Landroid/content/pm/LauncherApps$PinItemRequest;" since="26" />
+		<method name="getProfiles()Ljava/util/List;" since="26" />
 		<method name="getShortcutBadgedIconDrawable(Landroid/content/pm/ShortcutInfo;I)Landroid/graphics/drawable/Drawable;" since="25" />
+		<method name="getShortcutConfigActivityIntent(Landroid/content/pm/LauncherActivityInfo;)Landroid/content/IntentSender;" since="26" />
+		<method name="getShortcutConfigActivityList(Ljava/lang/String;Landroid/os/UserHandle;)Ljava/util/List;" since="26" />
 		<method name="getShortcutIconDrawable(Landroid/content/pm/ShortcutInfo;I)Landroid/graphics/drawable/Drawable;" since="25" />
 		<method name="getShortcuts(Landroid/content/pm/LauncherApps$ShortcutQuery;Landroid/os/UserHandle;)Ljava/util/List;" since="25" />
 		<method name="hasShortcutHostPermission()Z" since="25" />
@@ -9381,6 +10180,9 @@
 		<method name="startShortcut(Landroid/content/pm/ShortcutInfo;Landroid/graphics/Rect;Landroid/os/Bundle;)V" since="25" />
 		<method name="startShortcut(Ljava/lang/String;Ljava/lang/String;Landroid/graphics/Rect;Landroid/os/Bundle;Landroid/os/UserHandle;)V" since="25" />
 		<method name="unregisterCallback(Landroid/content/pm/LauncherApps$Callback;)V" />
+		<field name="ACTION_CONFIRM_PIN_APPWIDGET" since="26" />
+		<field name="ACTION_CONFIRM_PIN_SHORTCUT" since="26" />
+		<field name="EXTRA_PIN_ITEM_REQUEST" since="26" />
 	</class>
 	<class name="android/content/pm/LauncherApps$Callback" since="21">
 		<extends name="java/lang/Object" />
@@ -9394,6 +10196,21 @@
 		<method name="onPackagesUnsuspended([Ljava/lang/String;Landroid/os/UserHandle;)V" since="24" />
 		<method name="onShortcutsChanged(Ljava/lang/String;Ljava/util/List;Landroid/os/UserHandle;)V" since="25" />
 	</class>
+	<class name="android/content/pm/LauncherApps$PinItemRequest" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/os/Parcelable" />
+		<method name="&lt;init>()V" />
+		<method name="accept()Z" />
+		<method name="accept(Landroid/os/Bundle;)Z" />
+		<method name="getAppWidgetProviderInfo(Landroid/content/Context;)Landroid/appwidget/AppWidgetProviderInfo;" />
+		<method name="getExtras()Landroid/os/Bundle;" />
+		<method name="getRequestType()I" />
+		<method name="getShortcutInfo()Landroid/content/pm/ShortcutInfo;" />
+		<method name="isValid()Z" />
+		<field name="CREATOR" />
+		<field name="REQUEST_TYPE_APPWIDGET" />
+		<field name="REQUEST_TYPE_SHORTCUT" />
+	</class>
 	<class name="android/content/pm/LauncherApps$ShortcutQuery" since="25">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
@@ -9454,13 +10271,16 @@
 		<method name="openSession(I)Landroid/content/pm/PackageInstaller$Session;" />
 		<method name="registerSessionCallback(Landroid/content/pm/PackageInstaller$SessionCallback;)V" />
 		<method name="registerSessionCallback(Landroid/content/pm/PackageInstaller$SessionCallback;Landroid/os/Handler;)V" />
+		<method name="uninstall(Landroid/content/pm/VersionedPackage;Landroid/content/IntentSender;)V" since="26" />
 		<method name="uninstall(Ljava/lang/String;Landroid/content/IntentSender;)V" />
 		<method name="unregisterSessionCallback(Landroid/content/pm/PackageInstaller$SessionCallback;)V" />
 		<method name="updateSessionAppIcon(ILandroid/graphics/Bitmap;)V" />
 		<method name="updateSessionAppLabel(ILjava/lang/CharSequence;)V" />
+		<field name="ACTION_SESSION_COMMITTED" since="26" />
 		<field name="ACTION_SESSION_DETAILS" />
 		<field name="EXTRA_OTHER_PACKAGE_NAME" />
 		<field name="EXTRA_PACKAGE_NAME" />
+		<field name="EXTRA_SESSION" since="26" />
 		<field name="EXTRA_SESSION_ID" />
 		<field name="EXTRA_STATUS" />
 		<field name="EXTRA_STATUS_MESSAGE" />
@@ -9505,10 +10325,12 @@
 		<method name="getAppIcon()Landroid/graphics/Bitmap;" />
 		<method name="getAppLabel()Ljava/lang/CharSequence;" />
 		<method name="getAppPackageName()Ljava/lang/String;" />
+		<method name="getInstallReason()I" since="26" />
 		<method name="getInstallerPackageName()Ljava/lang/String;" />
 		<method name="getProgress()F" />
 		<method name="getSessionId()I" />
 		<method name="isActive()Z" />
+		<method name="isSealed()Z" since="26" />
 		<field name="CREATOR" />
 	</class>
 	<class name="android/content/pm/PackageInstaller$SessionParams" since="21">
@@ -9519,6 +10341,7 @@
 		<method name="setAppLabel(Ljava/lang/CharSequence;)V" />
 		<method name="setAppPackageName(Ljava/lang/String;)V" />
 		<method name="setInstallLocation(I)V" />
+		<method name="setInstallReason(I)V" since="26" />
 		<method name="setOriginatingUid(I)V" since="24" />
 		<method name="setOriginatingUri(Landroid/net/Uri;)V" />
 		<method name="setReferrerUri(Landroid/net/Uri;)V" />
@@ -9563,10 +10386,12 @@
 		<method name="addPermission(Landroid/content/pm/PermissionInfo;)Z" />
 		<method name="addPermissionAsync(Landroid/content/pm/PermissionInfo;)Z" since="8" />
 		<method name="addPreferredActivity(Landroid/content/IntentFilter;I[Landroid/content/ComponentName;Landroid/content/ComponentName;)V" deprecated="16" />
+		<method name="canRequestPackageInstalls()Z" since="26" />
 		<method name="canonicalToCurrentPackageNames([Ljava/lang/String;)[Ljava/lang/String;" since="8" />
 		<method name="checkPermission(Ljava/lang/String;Ljava/lang/String;)I" />
 		<method name="checkSignatures(II)I" since="5" />
 		<method name="checkSignatures(Ljava/lang/String;Ljava/lang/String;)I" />
+		<method name="clearInstantAppCookie()V" since="26" />
 		<method name="clearPackagePreferredActivities(Ljava/lang/String;)V" />
 		<method name="currentToCanonicalPackageNames([Ljava/lang/String;)[Ljava/lang/String;" since="8" />
 		<method name="extendVerificationTimeout(IIJ)V" since="17" />
@@ -9587,12 +10412,15 @@
 		<method name="getApplicationLabel(Landroid/content/pm/ApplicationInfo;)Ljava/lang/CharSequence;" />
 		<method name="getApplicationLogo(Landroid/content/pm/ApplicationInfo;)Landroid/graphics/drawable/Drawable;" since="9" />
 		<method name="getApplicationLogo(Ljava/lang/String;)Landroid/graphics/drawable/Drawable;" since="9" />
+		<method name="getChangedPackages(I)Landroid/content/pm/ChangedPackages;" since="26" />
 		<method name="getComponentEnabledSetting(Landroid/content/ComponentName;)I" />
 		<method name="getDefaultActivityIcon()Landroid/graphics/drawable/Drawable;" />
 		<method name="getDrawable(Ljava/lang/String;ILandroid/content/pm/ApplicationInfo;)Landroid/graphics/drawable/Drawable;" />
 		<method name="getInstalledApplications(I)Ljava/util/List;" />
 		<method name="getInstalledPackages(I)Ljava/util/List;" />
 		<method name="getInstallerPackageName(Ljava/lang/String;)Ljava/lang/String;" since="5" />
+		<method name="getInstantAppCookie()[B" since="26" />
+		<method name="getInstantAppCookieMaxBytes()I" since="26" />
 		<method name="getInstrumentationInfo(Landroid/content/ComponentName;I)Landroid/content/pm/InstrumentationInfo;" />
 		<method name="getLaunchIntentForPackage(Ljava/lang/String;)Landroid/content/Intent;" since="3" />
 		<method name="getLeanbackLaunchIntentForPackage(Ljava/lang/String;)Landroid/content/Intent;" since="21" />
@@ -9600,6 +10428,7 @@
 		<method name="getPackageArchiveInfo(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;" />
 		<method name="getPackageGids(Ljava/lang/String;)[I" />
 		<method name="getPackageGids(Ljava/lang/String;I)[I" since="24" />
+		<method name="getPackageInfo(Landroid/content/pm/VersionedPackage;I)Landroid/content/pm/PackageInfo;" since="26" />
 		<method name="getPackageInfo(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;" />
 		<method name="getPackageInstaller()Landroid/content/pm/PackageInstaller;" since="21" />
 		<method name="getPackageUid(Ljava/lang/String;I)I" since="24" />
@@ -9615,6 +10444,7 @@
 		<method name="getResourcesForApplication(Landroid/content/pm/ApplicationInfo;)Landroid/content/res/Resources;" />
 		<method name="getResourcesForApplication(Ljava/lang/String;)Landroid/content/res/Resources;" />
 		<method name="getServiceInfo(Landroid/content/ComponentName;I)Landroid/content/pm/ServiceInfo;" />
+		<method name="getSharedLibraries(I)Ljava/util/List;" since="26" />
 		<method name="getSystemAvailableFeatures()[Landroid/content/pm/FeatureInfo;" since="5" />
 		<method name="getSystemSharedLibraryNames()[Ljava/lang/String;" since="3" />
 		<method name="getText(Ljava/lang/String;ILandroid/content/pm/ApplicationInfo;)Ljava/lang/CharSequence;" />
@@ -9626,6 +10456,8 @@
 		<method name="hasSystemFeature(Ljava/lang/String;I)Z" since="24" />
 		<method name="installPackage(Landroid/net/Uri;)V" />
 		<method name="installPackage(Landroid/net/Uri;Landroid/content/pm/IPackageInstallObserver;I)V" />
+		<method name="isInstantApp()Z" since="26" />
+		<method name="isInstantApp(Ljava/lang/String;)Z" since="26" />
 		<method name="isPermissionRevokedByPolicy(Ljava/lang/String;Ljava/lang/String;)Z" since="23" />
 		<method name="isSafeMode()Z" since="3" />
 		<method name="queryBroadcastReceivers(Landroid/content/Intent;I)Ljava/util/List;" />
@@ -9641,9 +10473,11 @@
 		<method name="resolveActivity(Landroid/content/Intent;I)Landroid/content/pm/ResolveInfo;" />
 		<method name="resolveContentProvider(Ljava/lang/String;I)Landroid/content/pm/ProviderInfo;" />
 		<method name="resolveService(Landroid/content/Intent;I)Landroid/content/pm/ResolveInfo;" />
+		<method name="setApplicationCategoryHint(Ljava/lang/String;I)V" since="26" />
 		<method name="setApplicationEnabledSetting(Ljava/lang/String;II)V" />
 		<method name="setComponentEnabledSetting(Landroid/content/ComponentName;II)V" />
 		<method name="setInstallerPackageName(Ljava/lang/String;Ljava/lang/String;)V" since="11" />
+		<method name="updateInstantAppCookie([B)V" since="26" />
 		<method name="verifyPendingInstall(II)V" since="14" />
 		<field name="COMPONENT_ENABLED_STATE_DEFAULT" />
 		<field name="COMPONENT_ENABLED_STATE_DISABLED" />
@@ -9653,10 +10487,12 @@
 		<field name="DONT_KILL_APP" />
 		<field name="EXTRA_VERIFICATION_ID" since="14" />
 		<field name="EXTRA_VERIFICATION_RESULT" since="17" />
+		<field name="FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS" since="26" />
 		<field name="FEATURE_APP_WIDGETS" since="18" />
 		<field name="FEATURE_AUDIO_LOW_LATENCY" since="9" />
 		<field name="FEATURE_AUDIO_OUTPUT" since="21" />
 		<field name="FEATURE_AUDIO_PRO" since="23" />
+		<field name="FEATURE_AUTOFILL" since="26" />
 		<field name="FEATURE_AUTOMOTIVE" since="23" />
 		<field name="FEATURE_BACKUP" since="20" />
 		<field name="FEATURE_BLUETOOTH" since="8" />
@@ -9671,9 +10507,11 @@
 		<field name="FEATURE_CAMERA_FLASH" since="7" />
 		<field name="FEATURE_CAMERA_FRONT" since="9" />
 		<field name="FEATURE_CAMERA_LEVEL_FULL" since="21" />
+		<field name="FEATURE_COMPANION_DEVICE_SETUP" since="26" />
 		<field name="FEATURE_CONNECTION_SERVICE" since="21" />
 		<field name="FEATURE_CONSUMER_IR" since="19" />
 		<field name="FEATURE_DEVICE_ADMIN" since="19" />
+		<field name="FEATURE_EMBEDDED" since="26" />
 		<field name="FEATURE_ETHERNET" since="24" />
 		<field name="FEATURE_FAKETOUCH" since="11" />
 		<field name="FEATURE_FAKETOUCH_MULTITOUCH_DISTINCT" since="13" />
@@ -9685,6 +10523,7 @@
 		<field name="FEATURE_HOME_SCREEN" since="18" />
 		<field name="FEATURE_INPUT_METHODS" since="18" />
 		<field name="FEATURE_LEANBACK" since="21" />
+		<field name="FEATURE_LEANBACK_ONLY" since="26" />
 		<field name="FEATURE_LIVE_TV" since="21" />
 		<field name="FEATURE_LIVE_WALLPAPER" since="7" />
 		<field name="FEATURE_LOCATION" since="8" />
@@ -9727,13 +10566,16 @@
 		<field name="FEATURE_USB_ACCESSORY" since="12" />
 		<field name="FEATURE_USB_HOST" since="12" />
 		<field name="FEATURE_VERIFIED_BOOT" since="21" />
+		<field name="FEATURE_VR_HEADTRACKING" since="26" />
 		<field name="FEATURE_VR_MODE" since="24" />
 		<field name="FEATURE_VR_MODE_HIGH_PERFORMANCE" since="24" />
+		<field name="FEATURE_VULKAN_HARDWARE_COMPUTE" since="26" />
 		<field name="FEATURE_VULKAN_HARDWARE_LEVEL" since="24" />
 		<field name="FEATURE_VULKAN_HARDWARE_VERSION" since="24" />
 		<field name="FEATURE_WATCH" since="20" />
 		<field name="FEATURE_WEBVIEW" since="20" />
 		<field name="FEATURE_WIFI" since="8" />
+		<field name="FEATURE_WIFI_AWARE" since="26" />
 		<field name="FEATURE_WIFI_DIRECT" since="14" />
 		<field name="FORWARD_LOCK_PACKAGE" />
 		<field name="GET_ACTIVITIES" />
@@ -9776,6 +10618,11 @@
 		<field name="INSTALL_PARSE_FAILED_NOT_APK" />
 		<field name="INSTALL_PARSE_FAILED_NO_CERTIFICATES" />
 		<field name="INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION" />
+		<field name="INSTALL_REASON_DEVICE_RESTORE" since="26" />
+		<field name="INSTALL_REASON_DEVICE_SETUP" since="26" />
+		<field name="INSTALL_REASON_POLICY" since="26" />
+		<field name="INSTALL_REASON_UNKNOWN" since="26" />
+		<field name="INSTALL_REASON_USER" since="26" />
 		<field name="INSTALL_SUCCEEDED" />
 		<field name="MATCH_ALL" since="23" />
 		<field name="MATCH_DEFAULT_ONLY" />
@@ -9799,13 +10646,14 @@
 		<field name="SIGNATURE_UNKNOWN_PACKAGE" />
 		<field name="VERIFICATION_ALLOW" since="14" />
 		<field name="VERIFICATION_REJECT" since="14" />
+		<field name="VERSION_CODE_HIGHEST" since="26" />
 	</class>
 	<class name="android/content/pm/PackageManager$NameNotFoundException" since="1">
 		<extends name="android/util/AndroidException" />
 		<method name="&lt;init>()V" />
 		<method name="&lt;init>(Ljava/lang/String;)V" />
 	</class>
-	<class name="android/content/pm/PackageStats" since="1">
+	<class name="android/content/pm/PackageStats" since="1" deprecated="26">
 		<extends name="java/lang/Object" />
 		<implements name="android/os/Parcelable" />
 		<method name="&lt;init>(Landroid/content/pm/PackageStats;)V" />
@@ -9859,6 +10707,7 @@
 		<field name="PROTECTION_FLAG_PRE23" since="23" />
 		<field name="PROTECTION_FLAG_PREINSTALLED" since="23" />
 		<field name="PROTECTION_FLAG_PRIVILEGED" since="23" />
+		<field name="PROTECTION_FLAG_RUNTIME_ONLY" since="26" />
 		<field name="PROTECTION_FLAG_SETUP" since="24" />
 		<field name="PROTECTION_FLAG_SYSTEM" since="16" deprecated="23" />
 		<field name="PROTECTION_FLAG_VERIFIER" since="23" />
@@ -9906,6 +10755,7 @@
 		<field name="filter" />
 		<field name="icon" />
 		<field name="isDefault" />
+		<field name="isInstantAppAvailable" since="26" />
 		<field name="labelRes" />
 		<field name="match" />
 		<field name="nonLocalizedLabel" />
@@ -9936,6 +10786,21 @@
 		<field name="flags" since="14" />
 		<field name="permission" />
 	</class>
+	<class name="android/content/pm/SharedLibraryInfo" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/os/Parcelable" />
+		<method name="&lt;init>()V" />
+		<method name="getDeclaringPackage()Landroid/content/pm/VersionedPackage;" />
+		<method name="getDependentPackages()Ljava/util/List;" />
+		<method name="getName()Ljava/lang/String;" />
+		<method name="getType()I" />
+		<method name="getVersion()I" />
+		<field name="CREATOR" />
+		<field name="TYPE_BUILTIN" />
+		<field name="TYPE_DYNAMIC" />
+		<field name="TYPE_STATIC" />
+		<field name="VERSION_UNDEFINED" />
+	</class>
 	<class name="android/content/pm/ShortcutInfo" since="25">
 		<extends name="java/lang/Object" />
 		<implements name="android/os/Parcelable" />
@@ -9981,6 +10846,7 @@
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
 		<method name="addDynamicShortcuts(Ljava/util/List;)Z" />
+		<method name="createShortcutResultIntent(Landroid/content/pm/ShortcutInfo;)Landroid/content/Intent;" since="26" />
 		<method name="disableShortcuts(Ljava/util/List;)V" />
 		<method name="disableShortcuts(Ljava/util/List;Ljava/lang/CharSequence;)V" />
 		<method name="enableShortcuts(Ljava/util/List;)V" />
@@ -9991,9 +10857,11 @@
 		<method name="getMaxShortcutCountPerActivity()I" />
 		<method name="getPinnedShortcuts()Ljava/util/List;" />
 		<method name="isRateLimitingActive()Z" />
+		<method name="isRequestPinShortcutSupported()Z" since="26" />
 		<method name="removeAllDynamicShortcuts()V" />
 		<method name="removeDynamicShortcuts(Ljava/util/List;)V" />
 		<method name="reportShortcutUsed(Ljava/lang/String;)V" />
+		<method name="requestPinShortcut(Landroid/content/pm/ShortcutInfo;Landroid/content/IntentSender;)Z" since="26" />
 		<method name="setDynamicShortcuts(Ljava/util/List;)Z" />
 		<method name="updateShortcuts(Ljava/util/List;)Z" />
 	</class>
@@ -10008,6 +10876,14 @@
 		<method name="toCharsString()Ljava/lang/String;" />
 		<field name="CREATOR" />
 	</class>
+	<class name="android/content/pm/VersionedPackage" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/os/Parcelable" />
+		<method name="&lt;init>(Ljava/lang/String;I)V" />
+		<method name="getPackageName()Ljava/lang/String;" />
+		<method name="getVersionCode()I" />
+		<field name="CREATOR" />
+	</class>
 	<class name="android/content/res/AssetFileDescriptor" since="1">
 		<extends name="java/lang/Object" />
 		<implements name="android/os/Parcelable" since="3" />
@@ -10085,7 +10961,9 @@
 		<method name="getLayoutDirection()I" since="17" />
 		<method name="getLocales()Landroid/os/LocaleList;" since="24" />
 		<method name="isLayoutSizeAtLeast(I)Z" since="11" />
+		<method name="isScreenHdr()Z" since="26" />
 		<method name="isScreenRound()Z" since="23" />
+		<method name="isScreenWideColorGamut()Z" since="26" />
 		<method name="needNewResources(II)Z" />
 		<method name="readFromParcel(Landroid/os/Parcel;)V" since="8" />
 		<method name="setLayoutDirection(Ljava/util/Locale;)V" since="17" />
@@ -10094,6 +10972,16 @@
 		<method name="setTo(Landroid/content/res/Configuration;)V" since="8" />
 		<method name="setToDefaults()V" />
 		<method name="updateFrom(Landroid/content/res/Configuration;)I" />
+		<field name="COLOR_MODE_HDR_MASK" since="26" />
+		<field name="COLOR_MODE_HDR_NO" since="26" />
+		<field name="COLOR_MODE_HDR_SHIFT" since="26" />
+		<field name="COLOR_MODE_HDR_UNDEFINED" since="26" />
+		<field name="COLOR_MODE_HDR_YES" since="26" />
+		<field name="COLOR_MODE_UNDEFINED" since="26" />
+		<field name="COLOR_MODE_WIDE_COLOR_GAMUT_MASK" since="26" />
+		<field name="COLOR_MODE_WIDE_COLOR_GAMUT_NO" since="26" />
+		<field name="COLOR_MODE_WIDE_COLOR_GAMUT_UNDEFINED" since="26" />
+		<field name="COLOR_MODE_WIDE_COLOR_GAMUT_YES" since="26" />
 		<field name="CREATOR" />
 		<field name="DENSITY_DPI_UNDEFINED" since="17" />
 		<field name="HARDKEYBOARDHIDDEN_NO" since="3" />
@@ -10157,7 +11045,9 @@
 		<field name="UI_MODE_TYPE_NORMAL" since="8" />
 		<field name="UI_MODE_TYPE_TELEVISION" since="13" />
 		<field name="UI_MODE_TYPE_UNDEFINED" since="8" />
+		<field name="UI_MODE_TYPE_VR_HEADSET" since="26" />
 		<field name="UI_MODE_TYPE_WATCH" since="20" />
+		<field name="colorMode" since="26" />
 		<field name="densityDpi" since="17" />
 		<field name="fontScale" />
 		<field name="hardKeyboardHidden" since="3" />
@@ -10213,6 +11103,7 @@
 		<method name="getDrawable(ILandroid/content/res/Resources$Theme;)Landroid/graphics/drawable/Drawable;" since="21" />
 		<method name="getDrawableForDensity(II)Landroid/graphics/drawable/Drawable;" since="15" deprecated="22" />
 		<method name="getDrawableForDensity(IILandroid/content/res/Resources$Theme;)Landroid/graphics/drawable/Drawable;" since="21" />
+		<method name="getFont(I)Landroid/graphics/Typeface;" since="26" />
 		<method name="getFraction(III)F" since="3" />
 		<method name="getIdentifier(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I" />
 		<method name="getIntArray(I)[I" />
@@ -10279,6 +11170,7 @@
 		<method name="getDimensionPixelSize(II)I" />
 		<method name="getDrawable(I)Landroid/graphics/drawable/Drawable;" />
 		<method name="getFloat(IF)F" />
+		<method name="getFont(I)Landroid/graphics/Typeface;" since="26" />
 		<method name="getFraction(IIIF)F" />
 		<method name="getIndex(I)I" />
 		<method name="getIndexCount()I" />
@@ -11328,10 +12220,14 @@
 		<method name="copyPixelsFromBuffer(Ljava/nio/Buffer;)V" since="3" />
 		<method name="copyPixelsToBuffer(Ljava/nio/Buffer;)V" />
 		<method name="createBitmap(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;" />
+		<method name="createBitmap(IILandroid/graphics/Bitmap$Config;Z)Landroid/graphics/Bitmap;" since="26" />
+		<method name="createBitmap(IILandroid/graphics/Bitmap$Config;ZLandroid/graphics/ColorSpace;)Landroid/graphics/Bitmap;" since="26" />
 		<method name="createBitmap(Landroid/graphics/Bitmap;)Landroid/graphics/Bitmap;" />
 		<method name="createBitmap(Landroid/graphics/Bitmap;IIII)Landroid/graphics/Bitmap;" />
 		<method name="createBitmap(Landroid/graphics/Bitmap;IIIILandroid/graphics/Matrix;Z)Landroid/graphics/Bitmap;" />
 		<method name="createBitmap(Landroid/util/DisplayMetrics;IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;" since="17" />
+		<method name="createBitmap(Landroid/util/DisplayMetrics;IILandroid/graphics/Bitmap$Config;Z)Landroid/graphics/Bitmap;" since="26" />
+		<method name="createBitmap(Landroid/util/DisplayMetrics;IILandroid/graphics/Bitmap$Config;ZLandroid/graphics/ColorSpace;)Landroid/graphics/Bitmap;" since="26" />
 		<method name="createBitmap(Landroid/util/DisplayMetrics;[IIIIILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;" since="17" />
 		<method name="createBitmap(Landroid/util/DisplayMetrics;[IIILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;" since="17" />
 		<method name="createBitmap([IIIIILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;" />
@@ -11342,6 +12238,7 @@
 		<method name="extractAlpha(Landroid/graphics/Paint;[I)Landroid/graphics/Bitmap;" />
 		<method name="getAllocationByteCount()I" since="19" />
 		<method name="getByteCount()I" since="12" />
+		<method name="getColorSpace()Landroid/graphics/ColorSpace;" since="26" />
 		<method name="getConfig()Landroid/graphics/Bitmap$Config;" />
 		<method name="getDensity()I" since="4" />
 		<method name="getGenerationId()I" since="12" />
@@ -11393,6 +12290,8 @@
 		<field name="ALPHA_8" />
 		<field name="ARGB_4444" />
 		<field name="ARGB_8888" />
+		<field name="HARDWARE" since="26" />
+		<field name="RGBA_F16" since="26" />
 		<field name="RGB_565" />
 	</class>
 	<class name="android/graphics/BitmapFactory" since="1">
@@ -11421,6 +12320,7 @@
 		<field name="inJustDecodeBounds" />
 		<field name="inMutable" since="11" />
 		<field name="inPreferQualityOverSpeed" since="10" deprecated="24" />
+		<field name="inPreferredColorSpace" since="26" />
 		<field name="inPreferredConfig" />
 		<field name="inPremultiplied" since="19" />
 		<field name="inPurgeable" since="4" deprecated="21" />
@@ -11430,6 +12330,8 @@
 		<field name="inTargetDensity" since="4" />
 		<field name="inTempStorage" />
 		<field name="mCancel" deprecated="24" />
+		<field name="outColorSpace" since="26" />
+		<field name="outConfig" since="26" />
 		<field name="outHeight" />
 		<field name="outMimeType" />
 		<field name="outWidth" />
@@ -11487,15 +12389,20 @@
 		<method name="&lt;init>()V" />
 		<method name="&lt;init>(Landroid/graphics/Bitmap;)V" />
 		<method name="&lt;init>(Ljavax/microedition/khronos/opengles/GL;)V" />
+		<method name="clipOutPath(Landroid/graphics/Path;)Z" since="26" />
+		<method name="clipOutRect(FFFF)Z" since="26" />
+		<method name="clipOutRect(IIII)Z" since="26" />
+		<method name="clipOutRect(Landroid/graphics/Rect;)Z" since="26" />
+		<method name="clipOutRect(Landroid/graphics/RectF;)Z" since="26" />
 		<method name="clipPath(Landroid/graphics/Path;)Z" />
-		<method name="clipPath(Landroid/graphics/Path;Landroid/graphics/Region$Op;)Z" />
+		<method name="clipPath(Landroid/graphics/Path;Landroid/graphics/Region$Op;)Z" deprecated="26" />
 		<method name="clipRect(FFFF)Z" />
-		<method name="clipRect(FFFFLandroid/graphics/Region$Op;)Z" />
+		<method name="clipRect(FFFFLandroid/graphics/Region$Op;)Z" deprecated="26" />
 		<method name="clipRect(IIII)Z" />
 		<method name="clipRect(Landroid/graphics/Rect;)Z" />
-		<method name="clipRect(Landroid/graphics/Rect;Landroid/graphics/Region$Op;)Z" />
+		<method name="clipRect(Landroid/graphics/Rect;Landroid/graphics/Region$Op;)Z" deprecated="26" />
 		<method name="clipRect(Landroid/graphics/RectF;)Z" />
-		<method name="clipRect(Landroid/graphics/RectF;Landroid/graphics/Region$Op;)Z" />
+		<method name="clipRect(Landroid/graphics/RectF;Landroid/graphics/Region$Op;)Z" deprecated="26" />
 		<method name="clipRegion(Landroid/graphics/Region;)Z" deprecated="21" />
 		<method name="clipRegion(Landroid/graphics/Region;Landroid/graphics/Region$Op;)Z" deprecated="21" />
 		<method name="concat(Landroid/graphics/Matrix;)V" />
@@ -11565,15 +12472,15 @@
 		<method name="rotate(F)V" />
 		<method name="rotate(FFF)V" />
 		<method name="save()I" />
-		<method name="save(I)I" />
+		<method name="save(I)I" deprecated="26" />
 		<method name="saveLayer(FFFFLandroid/graphics/Paint;)I" since="21" />
-		<method name="saveLayer(FFFFLandroid/graphics/Paint;I)I" />
+		<method name="saveLayer(FFFFLandroid/graphics/Paint;I)I" deprecated="26" />
 		<method name="saveLayer(Landroid/graphics/RectF;Landroid/graphics/Paint;)I" since="21" />
-		<method name="saveLayer(Landroid/graphics/RectF;Landroid/graphics/Paint;I)I" />
+		<method name="saveLayer(Landroid/graphics/RectF;Landroid/graphics/Paint;I)I" deprecated="26" />
 		<method name="saveLayerAlpha(FFFFI)I" since="21" />
-		<method name="saveLayerAlpha(FFFFII)I" />
+		<method name="saveLayerAlpha(FFFFII)I" deprecated="26" />
 		<method name="saveLayerAlpha(Landroid/graphics/RectF;I)I" since="21" />
-		<method name="saveLayerAlpha(Landroid/graphics/RectF;II)I" />
+		<method name="saveLayerAlpha(Landroid/graphics/RectF;II)I" deprecated="26" />
 		<method name="scale(FF)V" />
 		<method name="scale(FFFF)V" />
 		<method name="setBitmap(Landroid/graphics/Bitmap;)V" />
@@ -11584,11 +12491,11 @@
 		<method name="skew(FF)V" />
 		<method name="translate(FF)V" />
 		<field name="ALL_SAVE_FLAG" />
-		<field name="CLIP_SAVE_FLAG" />
-		<field name="CLIP_TO_LAYER_SAVE_FLAG" />
-		<field name="FULL_COLOR_LAYER_SAVE_FLAG" />
-		<field name="HAS_ALPHA_LAYER_SAVE_FLAG" />
-		<field name="MATRIX_SAVE_FLAG" />
+		<field name="CLIP_SAVE_FLAG" deprecated="26" />
+		<field name="CLIP_TO_LAYER_SAVE_FLAG" deprecated="26" />
+		<field name="FULL_COLOR_LAYER_SAVE_FLAG" deprecated="26" />
+		<field name="HAS_ALPHA_LAYER_SAVE_FLAG" deprecated="26" />
+		<field name="MATRIX_SAVE_FLAG" deprecated="26" />
 	</class>
 	<class name="android/graphics/Canvas$EdgeType" since="1">
 		<extends name="java/lang/Enum" />
@@ -11611,15 +12518,58 @@
 		<method name="HSVToColor(I[F)I" />
 		<method name="HSVToColor([F)I" />
 		<method name="RGBToHSV(III[F)V" />
+		<method name="alpha()F" since="26" />
 		<method name="alpha(I)I" />
+		<method name="alpha(J)F" since="26" />
+		<method name="argb(FFFF)I" since="26" />
 		<method name="argb(IIII)I" />
+		<method name="blue()F" since="26" />
 		<method name="blue(I)I" />
+		<method name="blue(J)F" since="26" />
+		<method name="colorSpace(J)Landroid/graphics/ColorSpace;" since="26" />
 		<method name="colorToHSV(I[F)V" />
+		<method name="convert(FFFFLandroid/graphics/ColorSpace$Connector;)J" since="26" />
+		<method name="convert(FFFFLandroid/graphics/ColorSpace;Landroid/graphics/ColorSpace;)J" since="26" />
+		<method name="convert(ILandroid/graphics/ColorSpace;)J" since="26" />
+		<method name="convert(JLandroid/graphics/ColorSpace$Connector;)J" since="26" />
+		<method name="convert(JLandroid/graphics/ColorSpace;)J" since="26" />
+		<method name="convert(Landroid/graphics/ColorSpace;)Landroid/graphics/Color;" since="26" />
+		<method name="getColorSpace()Landroid/graphics/ColorSpace;" since="26" />
+		<method name="getComponent(I)F" since="26" />
+		<method name="getComponentCount()I" since="26" />
+		<method name="getComponents()[F" since="26" />
+		<method name="getComponents([F)[F" since="26" />
+		<method name="getModel()Landroid/graphics/ColorSpace$Model;" since="26" />
+		<method name="green()F" since="26" />
 		<method name="green(I)I" />
+		<method name="green(J)F" since="26" />
+		<method name="isInColorSpace(JLandroid/graphics/ColorSpace;)Z" since="26" />
+		<method name="isSrgb()Z" since="26" />
+		<method name="isSrgb(J)Z" since="26" />
+		<method name="isWideGamut()Z" since="26" />
+		<method name="isWideGamut(J)Z" since="26" />
+		<method name="luminance()F" since="26" />
 		<method name="luminance(I)F" since="24" />
+		<method name="luminance(J)F" since="26" />
+		<method name="pack()J" since="26" />
+		<method name="pack(FFF)J" since="26" />
+		<method name="pack(FFFF)J" since="26" />
+		<method name="pack(FFFFLandroid/graphics/ColorSpace;)J" since="26" />
+		<method name="pack(I)J" since="26" />
 		<method name="parseColor(Ljava/lang/String;)I" />
+		<method name="red()F" since="26" />
 		<method name="red(I)I" />
+		<method name="red(J)F" since="26" />
+		<method name="rgb(FFF)I" since="26" />
 		<method name="rgb(III)I" />
+		<method name="toArgb()I" since="26" />
+		<method name="toArgb(J)I" since="26" />
+		<method name="valueOf(FFF)Landroid/graphics/Color;" since="26" />
+		<method name="valueOf(FFFF)Landroid/graphics/Color;" since="26" />
+		<method name="valueOf(FFFFLandroid/graphics/ColorSpace;)Landroid/graphics/Color;" since="26" />
+		<method name="valueOf(I)Landroid/graphics/Color;" since="26" />
+		<method name="valueOf(J)Landroid/graphics/Color;" since="26" />
+		<method name="valueOf([FLandroid/graphics/ColorSpace;)Landroid/graphics/Color;" since="26" />
 		<field name="BLACK" />
 		<field name="BLUE" />
 		<field name="CYAN" />
@@ -11635,7 +12585,7 @@
 	</class>
 	<class name="android/graphics/ColorFilter" since="1">
 		<extends name="java/lang/Object" />
-		<method name="&lt;init>()V" />
+		<method name="&lt;init>()V" deprecated="26" />
 	</class>
 	<class name="android/graphics/ColorMatrix" since="1">
 		<extends name="java/lang/Object" />
@@ -11659,6 +12609,135 @@
 		<extends name="android/graphics/ColorFilter" />
 		<method name="&lt;init>(Landroid/graphics/ColorMatrix;)V" />
 		<method name="&lt;init>([F)V" />
+		<method name="getColorMatrix(Landroid/graphics/ColorMatrix;)V" since="26" />
+	</class>
+	<class name="android/graphics/ColorSpace" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="adapt(Landroid/graphics/ColorSpace;[F)Landroid/graphics/ColorSpace;" />
+		<method name="adapt(Landroid/graphics/ColorSpace;[FLandroid/graphics/ColorSpace$Adaptation;)Landroid/graphics/ColorSpace;" />
+		<method name="connect(Landroid/graphics/ColorSpace;)Landroid/graphics/ColorSpace$Connector;" />
+		<method name="connect(Landroid/graphics/ColorSpace;Landroid/graphics/ColorSpace$RenderIntent;)Landroid/graphics/ColorSpace$Connector;" />
+		<method name="connect(Landroid/graphics/ColorSpace;Landroid/graphics/ColorSpace;)Landroid/graphics/ColorSpace$Connector;" />
+		<method name="connect(Landroid/graphics/ColorSpace;Landroid/graphics/ColorSpace;Landroid/graphics/ColorSpace$RenderIntent;)Landroid/graphics/ColorSpace$Connector;" />
+		<method name="fromXyz(FFF)[F" />
+		<method name="fromXyz([F)[F" />
+		<method name="get(Landroid/graphics/ColorSpace$Named;)Landroid/graphics/ColorSpace;" />
+		<method name="getComponentCount()I" />
+		<method name="getId()I" />
+		<method name="getMaxValue(I)F" />
+		<method name="getMinValue(I)F" />
+		<method name="getModel()Landroid/graphics/ColorSpace$Model;" />
+		<method name="getName()Ljava/lang/String;" />
+		<method name="isSrgb()Z" />
+		<method name="isWideGamut()Z" />
+		<method name="match([FLandroid/graphics/ColorSpace$Rgb$TransferParameters;)Landroid/graphics/ColorSpace;" />
+		<method name="toXyz(FFF)[F" />
+		<method name="toXyz([F)[F" />
+		<field name="ILLUMINANT_A" />
+		<field name="ILLUMINANT_B" />
+		<field name="ILLUMINANT_C" />
+		<field name="ILLUMINANT_D50" />
+		<field name="ILLUMINANT_D55" />
+		<field name="ILLUMINANT_D60" />
+		<field name="ILLUMINANT_D65" />
+		<field name="ILLUMINANT_D75" />
+		<field name="ILLUMINANT_E" />
+		<field name="MAX_ID" />
+		<field name="MIN_ID" />
+	</class>
+	<class name="android/graphics/ColorSpace$Adaptation" since="26">
+		<extends name="java/lang/Enum" />
+		<method name="valueOf(Ljava/lang/String;)Landroid/graphics/ColorSpace$Adaptation;" />
+		<method name="values()[Landroid/graphics/ColorSpace$Adaptation;" />
+		<field name="BRADFORD" />
+		<field name="CIECAT02" />
+		<field name="VON_KRIES" />
+	</class>
+	<class name="android/graphics/ColorSpace$Connector" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="getDestination()Landroid/graphics/ColorSpace;" />
+		<method name="getRenderIntent()Landroid/graphics/ColorSpace$RenderIntent;" />
+		<method name="getSource()Landroid/graphics/ColorSpace;" />
+		<method name="transform(FFF)[F" />
+		<method name="transform([F)[F" />
+	</class>
+	<class name="android/graphics/ColorSpace$Model" since="26">
+		<extends name="java/lang/Enum" />
+		<method name="getComponentCount()I" />
+		<method name="valueOf(Ljava/lang/String;)Landroid/graphics/ColorSpace$Model;" />
+		<method name="values()[Landroid/graphics/ColorSpace$Model;" />
+		<field name="CMYK" />
+		<field name="LAB" />
+		<field name="RGB" />
+		<field name="XYZ" />
+	</class>
+	<class name="android/graphics/ColorSpace$Named" since="26">
+		<extends name="java/lang/Enum" />
+		<method name="valueOf(Ljava/lang/String;)Landroid/graphics/ColorSpace$Named;" />
+		<method name="values()[Landroid/graphics/ColorSpace$Named;" />
+		<field name="ACES" />
+		<field name="ACESCG" />
+		<field name="ADOBE_RGB" />
+		<field name="BT2020" />
+		<field name="BT709" />
+		<field name="CIE_LAB" />
+		<field name="CIE_XYZ" />
+		<field name="DCI_P3" />
+		<field name="DISPLAY_P3" />
+		<field name="EXTENDED_SRGB" />
+		<field name="LINEAR_EXTENDED_SRGB" />
+		<field name="LINEAR_SRGB" />
+		<field name="NTSC_1953" />
+		<field name="PRO_PHOTO_RGB" />
+		<field name="SMPTE_C" />
+		<field name="SRGB" />
+	</class>
+	<class name="android/graphics/ColorSpace$RenderIntent" since="26">
+		<extends name="java/lang/Enum" />
+		<method name="valueOf(Ljava/lang/String;)Landroid/graphics/ColorSpace$RenderIntent;" />
+		<method name="values()[Landroid/graphics/ColorSpace$RenderIntent;" />
+		<field name="ABSOLUTE" />
+		<field name="PERCEPTUAL" />
+		<field name="RELATIVE" />
+		<field name="SATURATION" />
+	</class>
+	<class name="android/graphics/ColorSpace$Rgb" since="26">
+		<extends name="android/graphics/ColorSpace" />
+		<method name="&lt;init>(Ljava/lang/String;[FD)V" />
+		<method name="&lt;init>(Ljava/lang/String;[FLandroid/graphics/ColorSpace$Rgb$TransferParameters;)V" />
+		<method name="&lt;init>(Ljava/lang/String;[FLjava/util/function/DoubleUnaryOperator;Ljava/util/function/DoubleUnaryOperator;)V" />
+		<method name="&lt;init>(Ljava/lang/String;[F[FD)V" />
+		<method name="&lt;init>(Ljava/lang/String;[F[FLandroid/graphics/ColorSpace$Rgb$TransferParameters;)V" />
+		<method name="&lt;init>(Ljava/lang/String;[F[FLjava/util/function/DoubleUnaryOperator;Ljava/util/function/DoubleUnaryOperator;FF)V" />
+		<method name="fromLinear(FFF)[F" />
+		<method name="fromLinear([F)[F" />
+		<method name="getEotf()Ljava/util/function/DoubleUnaryOperator;" />
+		<method name="getInverseTransform()[F" />
+		<method name="getInverseTransform([F)[F" />
+		<method name="getOetf()Ljava/util/function/DoubleUnaryOperator;" />
+		<method name="getPrimaries()[F" />
+		<method name="getPrimaries([F)[F" />
+		<method name="getTransferParameters()Landroid/graphics/ColorSpace$Rgb$TransferParameters;" />
+		<method name="getTransform()[F" />
+		<method name="getTransform([F)[F" />
+		<method name="getWhitePoint()[F" />
+		<method name="getWhitePoint([F)[F" />
+		<method name="toLinear(FFF)[F" />
+		<method name="toLinear([F)[F" />
+	</class>
+	<class name="android/graphics/ColorSpace$Rgb$TransferParameters" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>(DDDDD)V" />
+		<method name="&lt;init>(DDDDDDD)V" />
+		<field name="a" />
+		<field name="b" />
+		<field name="c" />
+		<field name="d" />
+		<field name="e" />
+		<field name="f" />
+		<field name="g" />
 	</class>
 	<class name="android/graphics/ComposePathEffect" since="1">
 		<extends name="android/graphics/PathEffect" />
@@ -11744,6 +12823,8 @@
 	<class name="android/graphics/LightingColorFilter" since="1">
 		<extends name="android/graphics/ColorFilter" />
 		<method name="&lt;init>(II)V" />
+		<method name="getColorAdd()I" since="26" />
+		<method name="getColorMultiply()I" since="26" />
 	</class>
 	<class name="android/graphics/LinearGradient" since="1">
 		<extends name="android/graphics/Shader" />
@@ -11898,6 +12979,7 @@
 		<method name="getFontMetricsInt()Landroid/graphics/Paint$FontMetricsInt;" />
 		<method name="getFontMetricsInt(Landroid/graphics/Paint$FontMetricsInt;)I" />
 		<method name="getFontSpacing()F" />
+		<method name="getFontVariationSettings()Ljava/lang/String;" since="26" />
 		<method name="getHinting()I" since="14" />
 		<method name="getLetterSpacing()F" since="21" />
 		<method name="getMaskFilter()Landroid/graphics/MaskFilter;" />
@@ -11956,6 +13038,7 @@
 		<method name="setFilterBitmap(Z)V" />
 		<method name="setFlags(I)V" />
 		<method name="setFontFeatureSettings(Ljava/lang/String;)V" since="21" />
+		<method name="setFontVariationSettings(Ljava/lang/String;)Z" since="26" />
 		<method name="setHinting(I)V" since="14" />
 		<method name="setLetterSpacing(F)V" since="21" />
 		<method name="setLinearText(Z)V" deprecated="16" />
@@ -12065,6 +13148,7 @@
 		<method name="addRoundRect(FFFF[FLandroid/graphics/Path$Direction;)V" since="21" />
 		<method name="addRoundRect(Landroid/graphics/RectF;FFLandroid/graphics/Path$Direction;)V" />
 		<method name="addRoundRect(Landroid/graphics/RectF;[FLandroid/graphics/Path$Direction;)V" />
+		<method name="approximate(F)[F" since="26" />
 		<method name="arcTo(FFFFFFZ)V" since="21" />
 		<method name="arcTo(Landroid/graphics/RectF;FF)V" />
 		<method name="arcTo(Landroid/graphics/RectF;FFZ)V" />
@@ -12175,9 +13259,11 @@
 		<field name="LA_88" deprecated="16" />
 		<field name="L_8" deprecated="19" />
 		<field name="OPAQUE" />
+		<field name="RGBA_1010102" since="26" />
 		<field name="RGBA_4444" deprecated="16" />
 		<field name="RGBA_5551" deprecated="16" />
 		<field name="RGBA_8888" />
+		<field name="RGBA_F16" since="26" />
 		<field name="RGBX_8888" />
 		<field name="RGB_332" deprecated="16" />
 		<field name="RGB_565" />
@@ -12407,7 +13493,7 @@
 	</class>
 	<class name="android/graphics/Shader" since="1">
 		<extends name="java/lang/Object" />
-		<method name="&lt;init>()V" />
+		<method name="&lt;init>()V" deprecated="26" />
 		<method name="getLocalMatrix(Landroid/graphics/Matrix;)Z" />
 		<method name="setLocalMatrix(Landroid/graphics/Matrix;)V" />
 	</class>
@@ -12427,10 +13513,12 @@
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>(I)V" />
 		<method name="&lt;init>(IZ)V" since="19" />
+		<method name="&lt;init>(Z)V" since="26" />
 		<method name="attachToGLContext(I)V" since="16" />
 		<method name="detachFromGLContext()V" since="16" />
 		<method name="getTimestamp()J" since="14" />
 		<method name="getTransformMatrix([F)V" />
+		<method name="isReleased()Z" since="26" />
 		<method name="release()V" since="14" />
 		<method name="releaseTexImage()V" since="19" />
 		<method name="setDefaultBufferSize(II)V" since="15" />
@@ -12474,6 +13562,20 @@
 		<field name="SANS_SERIF" />
 		<field name="SERIF" />
 	</class>
+	<class name="android/graphics/Typeface$Builder" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>(Landroid/content/res/AssetManager;Ljava/lang/String;)V" />
+		<method name="&lt;init>(Ljava/io/File;)V" />
+		<method name="&lt;init>(Ljava/io/FileDescriptor;)V" />
+		<method name="&lt;init>(Ljava/lang/String;)V" />
+		<method name="build()Landroid/graphics/Typeface;" />
+		<method name="setFallback(Ljava/lang/String;)Landroid/graphics/Typeface$Builder;" />
+		<method name="setFontVariationSettings(Ljava/lang/String;)Landroid/graphics/Typeface$Builder;" />
+		<method name="setFontVariationSettings([Landroid/graphics/fonts/FontVariationAxis;)Landroid/graphics/Typeface$Builder;" />
+		<method name="setItalic(Z)Landroid/graphics/Typeface$Builder;" />
+		<method name="setTtcIndex(I)Landroid/graphics/Typeface$Builder;" />
+		<method name="setWeight(I)Landroid/graphics/Typeface$Builder;" />
+	</class>
 	<class name="android/graphics/Xfermode" since="1">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
@@ -12488,6 +13590,16 @@
 		<method name="getYuvData()[B" />
 		<method name="getYuvFormat()I" />
 	</class>
+	<class name="android/graphics/drawable/AdaptiveIconDrawable" since="26">
+		<extends name="android/graphics/drawable/Drawable" />
+		<implements name="android/graphics/drawable/Drawable$Callback" />
+		<method name="&lt;init>(Landroid/graphics/drawable/Drawable;Landroid/graphics/drawable/Drawable;)V" />
+		<method name="getBackground()Landroid/graphics/drawable/Drawable;" />
+		<method name="getExtraInsetFraction()F" />
+		<method name="getForeground()Landroid/graphics/drawable/Drawable;" />
+		<method name="getIconMask()Landroid/graphics/Path;" />
+		<method name="setOpacity(I)V" />
+	</class>
 	<class name="android/graphics/drawable/Animatable" since="4">
 		<extends name="java/lang/Object" />
 		<method name="isRunning()Z" />
@@ -12763,6 +13875,7 @@
 		<extends name="java/lang/Object" />
 		<implements name="android/os/Parcelable" />
 		<method name="&lt;init>()V" />
+		<method name="createWithAdaptiveBitmap(Landroid/graphics/Bitmap;)Landroid/graphics/drawable/Icon;" since="26" />
 		<method name="createWithBitmap(Landroid/graphics/Bitmap;)Landroid/graphics/drawable/Icon;" />
 		<method name="createWithContentUri(Landroid/net/Uri;)Landroid/graphics/drawable/Icon;" />
 		<method name="createWithContentUri(Ljava/lang/String;)Landroid/graphics/drawable/Icon;" />
@@ -12786,6 +13899,8 @@
 		<extends name="android/graphics/drawable/Drawable" />
 		<extends name="android/graphics/drawable/DrawableWrapper" since="23" />
 		<implements name="android/graphics/drawable/Drawable$Callback" />
+		<method name="&lt;init>(Landroid/graphics/drawable/Drawable;F)V" since="26" />
+		<method name="&lt;init>(Landroid/graphics/drawable/Drawable;FFFF)V" since="26" />
 		<method name="&lt;init>(Landroid/graphics/drawable/Drawable;I)V" />
 		<method name="&lt;init>(Landroid/graphics/drawable/Drawable;IIII)V" />
 		<method name="getDrawable()Landroid/graphics/drawable/Drawable;" since="19" />
@@ -12946,10 +14061,14 @@
 	<class name="android/graphics/drawable/shapes/ArcShape" since="1">
 		<extends name="android/graphics/drawable/shapes/RectShape" />
 		<method name="&lt;init>(FF)V" />
+		<method name="clone()Landroid/graphics/drawable/shapes/ArcShape;" since="26" />
+		<method name="getStartAngle()F" since="26" />
+		<method name="getSweepAngle()F" since="26" />
 	</class>
 	<class name="android/graphics/drawable/shapes/OvalShape" since="1">
 		<extends name="android/graphics/drawable/shapes/RectShape" />
 		<method name="&lt;init>()V" />
+		<method name="clone()Landroid/graphics/drawable/shapes/OvalShape;" since="26" />
 	</class>
 	<class name="android/graphics/drawable/shapes/PathShape" since="1">
 		<extends name="android/graphics/drawable/shapes/Shape" />
@@ -12980,6 +14099,14 @@
 		<method name="onResize(FF)V" />
 		<method name="resize(FF)V" />
 	</class>
+	<class name="android/graphics/fonts/FontVariationAxis" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>(Ljava/lang/String;F)V" />
+		<method name="fromFontVariationSettings(Ljava/lang/String;)[Landroid/graphics/fonts/FontVariationAxis;" />
+		<method name="getStyleValue()F" />
+		<method name="getTag()Ljava/lang/String;" />
+		<method name="toFontVariationSettings([Landroid/graphics/fonts/FontVariationAxis;)Ljava/lang/String;" />
+	</class>
 	<class name="android/graphics/pdf/PdfDocument" since="19">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
@@ -13307,11 +14434,43 @@
 		<method name="getY()F" />
 		<method name="getZ()F" />
 	</class>
+	<class name="android/hardware/HardwareBuffer" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/os/Parcelable" />
+		<implements name="java/lang/AutoCloseable" />
+		<method name="&lt;init>()V" />
+		<method name="create(IIIIJ)Landroid/hardware/HardwareBuffer;" />
+		<method name="getFormat()I" />
+		<method name="getHeight()I" />
+		<method name="getLayers()I" />
+		<method name="getUsage()J" />
+		<method name="getWidth()I" />
+		<method name="isClosed()Z" />
+		<field name="BLOB" />
+		<field name="CREATOR" />
+		<field name="RGBA_1010102" />
+		<field name="RGBA_8888" />
+		<field name="RGBA_FP16" />
+		<field name="RGBX_8888" />
+		<field name="RGB_565" />
+		<field name="RGB_888" />
+		<field name="USAGE_CPU_READ_OFTEN" />
+		<field name="USAGE_CPU_READ_RARELY" />
+		<field name="USAGE_CPU_WRITE_OFTEN" />
+		<field name="USAGE_CPU_WRITE_RARELY" />
+		<field name="USAGE_GPU_COLOR_OUTPUT" />
+		<field name="USAGE_GPU_DATA_BUFFER" />
+		<field name="USAGE_GPU_SAMPLED_IMAGE" />
+		<field name="USAGE_PROTECTED_CONTENT" />
+		<field name="USAGE_SENSOR_DIRECT_DATA" />
+		<field name="USAGE_VIDEO_ENCODE" />
+	</class>
 	<class name="android/hardware/Sensor" since="3">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
 		<method name="getFifoMaxEventCount()I" since="19" />
 		<method name="getFifoReservedEventCount()I" since="19" />
+		<method name="getHighestDirectReportRateLevel()I" since="26" />
 		<method name="getId()I" since="24" />
 		<method name="getMaxDelay()I" since="21" />
 		<method name="getMaximumRange()F" />
@@ -13325,6 +14484,7 @@
 		<method name="getVendor()Ljava/lang/String;" />
 		<method name="getVersion()I" />
 		<method name="isAdditionalInfoSupported()Z" since="24" />
+		<method name="isDirectChannelTypeSupported(I)Z" since="26" />
 		<method name="isDynamicSensor()Z" since="24" />
 		<method name="isWakeUpSensor()Z" since="21" />
 		<field name="REPORTING_MODE_CONTINUOUS" since="21" />
@@ -13332,6 +14492,7 @@
 		<field name="REPORTING_MODE_ON_CHANGE" since="21" />
 		<field name="REPORTING_MODE_SPECIAL_TRIGGER" since="21" />
 		<field name="STRING_TYPE_ACCELEROMETER" since="20" />
+		<field name="STRING_TYPE_ACCELEROMETER_UNCALIBRATED" since="26" />
 		<field name="STRING_TYPE_AMBIENT_TEMPERATURE" since="20" />
 		<field name="STRING_TYPE_GAME_ROTATION_VECTOR" since="20" />
 		<field name="STRING_TYPE_GEOMAGNETIC_ROTATION_VECTOR" since="20" />
@@ -13342,6 +14503,7 @@
 		<field name="STRING_TYPE_HEART_RATE" since="20" />
 		<field name="STRING_TYPE_LIGHT" since="20" />
 		<field name="STRING_TYPE_LINEAR_ACCELERATION" since="20" />
+		<field name="STRING_TYPE_LOW_LATENCY_OFFBODY_DETECT" since="26" />
 		<field name="STRING_TYPE_MAGNETIC_FIELD" since="20" />
 		<field name="STRING_TYPE_MAGNETIC_FIELD_UNCALIBRATED" since="20" />
 		<field name="STRING_TYPE_MOTION_DETECT" since="24" />
@@ -13357,6 +14519,7 @@
 		<field name="STRING_TYPE_STEP_DETECTOR" since="20" />
 		<field name="STRING_TYPE_TEMPERATURE" since="20" deprecated="20" />
 		<field name="TYPE_ACCELEROMETER" />
+		<field name="TYPE_ACCELEROMETER_UNCALIBRATED" since="26" />
 		<field name="TYPE_ALL" />
 		<field name="TYPE_AMBIENT_TEMPERATURE" since="14" />
 		<field name="TYPE_DEVICE_PRIVATE_BASE" since="24" />
@@ -13369,6 +14532,7 @@
 		<field name="TYPE_HEART_RATE" since="20" />
 		<field name="TYPE_LIGHT" />
 		<field name="TYPE_LINEAR_ACCELERATION" since="9" />
+		<field name="TYPE_LOW_LATENCY_OFFBODY_DETECT" since="26" />
 		<field name="TYPE_MAGNETIC_FIELD" />
 		<field name="TYPE_MAGNETIC_FIELD_UNCALIBRATED" since="18" />
 		<field name="TYPE_MOTION_DETECT" since="24" />
@@ -13400,6 +14564,18 @@
 		<field name="serial" />
 		<field name="type" />
 	</class>
+	<class name="android/hardware/SensorDirectChannel" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/nio/channels/Channel" />
+		<method name="&lt;init>()V" />
+		<method name="configure(Landroid/hardware/Sensor;I)I" />
+		<field name="RATE_FAST" />
+		<field name="RATE_NORMAL" />
+		<field name="RATE_STOP" />
+		<field name="RATE_VERY_FAST" />
+		<field name="TYPE_HARDWARE_BUFFER" />
+		<field name="TYPE_MEMORY_FILE" />
+	</class>
 	<class name="android/hardware/SensorEvent" since="3">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
@@ -13433,6 +14609,8 @@
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
 		<method name="cancelTriggerSensor(Landroid/hardware/TriggerEventListener;Landroid/hardware/Sensor;)Z" since="18" />
+		<method name="createDirectChannel(Landroid/hardware/HardwareBuffer;)Landroid/hardware/SensorDirectChannel;" since="26" />
+		<method name="createDirectChannel(Landroid/os/MemoryFile;)Landroid/hardware/SensorDirectChannel;" since="26" />
 		<method name="flush(Landroid/hardware/SensorEventListener;)Z" since="19" />
 		<method name="getAltitude(FF)F" since="9" />
 		<method name="getAngleChange([F[F[F)V" since="9" />
@@ -13559,6 +14737,7 @@
 		<method name="abortCaptures()V" />
 		<method name="capture(Landroid/hardware/camera2/CaptureRequest;Landroid/hardware/camera2/CameraCaptureSession$CaptureCallback;Landroid/os/Handler;)I" />
 		<method name="captureBurst(Ljava/util/List;Landroid/hardware/camera2/CameraCaptureSession$CaptureCallback;Landroid/os/Handler;)I" />
+		<method name="finalizeOutputConfigurations(Ljava/util/List;)V" since="26" />
 		<method name="getDevice()Landroid/hardware/camera2/CameraDevice;" />
 		<method name="getInputSurface()Landroid/view/Surface;" since="23" />
 		<method name="isReprocessable()Z" since="23" />
@@ -13582,6 +14761,7 @@
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
 		<method name="onActive(Landroid/hardware/camera2/CameraCaptureSession;)V" />
+		<method name="onCaptureQueueEmpty(Landroid/hardware/camera2/CameraCaptureSession;)V" since="26" />
 		<method name="onClosed(Landroid/hardware/camera2/CameraCaptureSession;)V" />
 		<method name="onConfigureFailed(Landroid/hardware/camera2/CameraCaptureSession;)V" />
 		<method name="onConfigured(Landroid/hardware/camera2/CameraCaptureSession;)V" />
@@ -13976,6 +15156,7 @@
 		<field name="CONTROL_AWB_REGIONS" />
 		<field name="CONTROL_CAPTURE_INTENT" />
 		<field name="CONTROL_EFFECT_MODE" />
+		<field name="CONTROL_ENABLE_ZSL" since="26" />
 		<field name="CONTROL_MODE" />
 		<field name="CONTROL_POST_RAW_SENSITIVITY_BOOST" since="24" />
 		<field name="CONTROL_SCENE_MODE" />
@@ -14056,6 +15237,7 @@
 		<field name="CONTROL_AWB_STATE" />
 		<field name="CONTROL_CAPTURE_INTENT" />
 		<field name="CONTROL_EFFECT_MODE" />
+		<field name="CONTROL_ENABLE_ZSL" since="26" />
 		<field name="CONTROL_MODE" />
 		<field name="CONTROL_POST_RAW_SENSITIVITY_BOOST" since="24" />
 		<field name="CONTROL_SCENE_MODE" />
@@ -14201,9 +15383,13 @@
 		<extends name="java/lang/Object" />
 		<implements name="android/os/Parcelable" />
 		<method name="&lt;init>(ILandroid/view/Surface;)V" />
+		<method name="&lt;init>(Landroid/util/Size;Ljava/lang/Class;)V" since="26" />
 		<method name="&lt;init>(Landroid/view/Surface;)V" />
+		<method name="addSurface(Landroid/view/Surface;)V" since="26" />
+		<method name="enableSurfaceSharing()V" since="26" />
 		<method name="getSurface()Landroid/view/Surface;" />
 		<method name="getSurfaceGroupId()I" />
+		<method name="getSurfaces()Ljava/util/List;" since="26" />
 		<field name="CREATOR" />
 		<field name="SURFACE_GROUP_ID_NONE" />
 	</class>
@@ -14507,6 +15693,7 @@
 		<method name="getSerial()Ljava/lang/String;" />
 		<method name="releaseInterface(Landroid/hardware/usb/UsbInterface;)Z" />
 		<method name="requestWait()Landroid/hardware/usb/UsbRequest;" />
+		<method name="requestWait(J)Landroid/hardware/usb/UsbRequest;" since="26" />
 		<method name="setConfiguration(Landroid/hardware/usb/UsbConfiguration;)Z" since="21" />
 		<method name="setInterface(Landroid/hardware/usb/UsbInterface;)Z" since="21" />
 	</class>
@@ -14564,7 +15751,8 @@
 		<method name="getClientData()Ljava/lang/Object;" />
 		<method name="getEndpoint()Landroid/hardware/usb/UsbEndpoint;" />
 		<method name="initialize(Landroid/hardware/usb/UsbDeviceConnection;Landroid/hardware/usb/UsbEndpoint;)Z" />
-		<method name="queue(Ljava/nio/ByteBuffer;I)Z" />
+		<method name="queue(Ljava/nio/ByteBuffer;)Z" since="26" />
+		<method name="queue(Ljava/nio/ByteBuffer;I)Z" deprecated="26" />
 		<method name="setClientData(Ljava/lang/Object;)V" />
 	</class>
 	<class name="android/icu/lang/UCharacter" since="24">
@@ -14732,6 +15920,10 @@
 		<field name="CONTROL" />
 		<field name="CR" />
 		<field name="EXTEND" />
+		<field name="E_BASE" since="26" />
+		<field name="E_BASE_GAZ" since="26" />
+		<field name="E_MODIFIER" since="26" />
+		<field name="GLUE_AFTER_ZWJ" since="26" />
 		<field name="L" />
 		<field name="LF" />
 		<field name="LV" />
@@ -14742,6 +15934,7 @@
 		<field name="SPACING_MARK" />
 		<field name="T" />
 		<field name="V" />
+		<field name="ZWJ" since="26" />
 	</class>
 	<class name="android/icu/lang/UCharacter$HangulSyllableType" since="24">
 		<extends name="java/lang/Object" />
@@ -14754,6 +15947,9 @@
 	</class>
 	<class name="android/icu/lang/UCharacter$JoiningGroup" since="24">
 		<extends name="java/lang/Object" />
+		<field name="AFRICAN_FEH" since="26" />
+		<field name="AFRICAN_NOON" since="26" />
+		<field name="AFRICAN_QAF" since="26" />
 		<field name="AIN" />
 		<field name="ALAPH" />
 		<field name="ALEF" />
@@ -14867,6 +16063,8 @@
 		<field name="CONDITIONAL_JAPANESE_STARTER" />
 		<field name="CONTINGENT_BREAK" />
 		<field name="EXCLAMATION" />
+		<field name="E_BASE" since="26" />
+		<field name="E_MODIFIER" since="26" />
 		<field name="GLUE" />
 		<field name="H2" />
 		<field name="H3" />
@@ -14893,6 +16091,7 @@
 		<field name="SURROGATE" />
 		<field name="UNKNOWN" />
 		<field name="WORD_JOINER" />
+		<field name="ZWJ" since="26" />
 		<field name="ZWSPACE" />
 	</class>
 	<class name="android/icu/lang/UCharacter$NumericType" since="24">
@@ -14927,6 +16126,8 @@
 		<method name="getID()I" />
 		<method name="getInstance(I)Landroid/icu/lang/UCharacter$UnicodeBlock;" />
 		<method name="of(I)Landroid/icu/lang/UCharacter$UnicodeBlock;" />
+		<field name="ADLAM" since="26" />
+		<field name="ADLAM_ID" since="26" />
 		<field name="AEGEAN_NUMBERS" />
 		<field name="AEGEAN_NUMBERS_ID" />
 		<field name="AHOM" />
@@ -14975,6 +16176,8 @@
 		<field name="BATAK_ID" />
 		<field name="BENGALI" />
 		<field name="BENGALI_ID" />
+		<field name="BHAIKSUKI" since="26" />
+		<field name="BHAIKSUKI_ID" since="26" />
 		<field name="BLOCK_ELEMENTS" />
 		<field name="BLOCK_ELEMENTS_ID" />
 		<field name="BOPOMOFO" />
@@ -15064,6 +16267,8 @@
 		<field name="CYRILLIC_EXTENDED_A_ID" />
 		<field name="CYRILLIC_EXTENDED_B" />
 		<field name="CYRILLIC_EXTENDED_B_ID" />
+		<field name="CYRILLIC_EXTENDED_C" since="26" />
+		<field name="CYRILLIC_EXTENDED_C_ID" since="26" />
 		<field name="CYRILLIC_ID" />
 		<field name="CYRILLIC_SUPPLEMENT" />
 		<field name="CYRILLIC_SUPPLEMENTARY" />
@@ -15117,6 +16322,8 @@
 		<field name="GEORGIAN_SUPPLEMENT_ID" />
 		<field name="GLAGOLITIC" />
 		<field name="GLAGOLITIC_ID" />
+		<field name="GLAGOLITIC_SUPPLEMENT" since="26" />
+		<field name="GLAGOLITIC_SUPPLEMENT_ID" since="26" />
 		<field name="GOTHIC" />
 		<field name="GOTHIC_ID" />
 		<field name="GRANTHA" />
@@ -15155,6 +16362,8 @@
 		<field name="HIRAGANA_ID" />
 		<field name="IDEOGRAPHIC_DESCRIPTION_CHARACTERS" />
 		<field name="IDEOGRAPHIC_DESCRIPTION_CHARACTERS_ID" />
+		<field name="IDEOGRAPHIC_SYMBOLS_AND_PUNCTUATION" since="26" />
+		<field name="IDEOGRAPHIC_SYMBOLS_AND_PUNCTUATION_ID" since="26" />
 		<field name="IMPERIAL_ARAMAIC" />
 		<field name="IMPERIAL_ARAMAIC_ID" />
 		<field name="INSCRIPTIONAL_PAHLAVI" />
@@ -15239,6 +16448,8 @@
 		<field name="MANDAIC_ID" />
 		<field name="MANICHAEAN" />
 		<field name="MANICHAEAN_ID" />
+		<field name="MARCHEN" since="26" />
+		<field name="MARCHEN_ID" since="26" />
 		<field name="MATHEMATICAL_ALPHANUMERIC_SYMBOLS" />
 		<field name="MATHEMATICAL_ALPHANUMERIC_SYMBOLS_ID" />
 		<field name="MATHEMATICAL_OPERATORS" />
@@ -15273,6 +16484,8 @@
 		<field name="MODI_ID" />
 		<field name="MONGOLIAN" />
 		<field name="MONGOLIAN_ID" />
+		<field name="MONGOLIAN_SUPPLEMENT" since="26" />
+		<field name="MONGOLIAN_SUPPLEMENT_ID" since="26" />
 		<field name="MRO" />
 		<field name="MRO_ID" />
 		<field name="MULTANI" />
@@ -15287,6 +16500,8 @@
 		<field name="MYANMAR_ID" />
 		<field name="NABATAEAN" />
 		<field name="NABATAEAN_ID" />
+		<field name="NEWA" since="26" />
+		<field name="NEWA_ID" since="26" />
 		<field name="NEW_TAI_LUE" />
 		<field name="NEW_TAI_LUE_ID" />
 		<field name="NKO" />
@@ -15318,6 +16533,8 @@
 		<field name="ORIYA_ID" />
 		<field name="ORNAMENTAL_DINGBATS" />
 		<field name="ORNAMENTAL_DINGBATS_ID" />
+		<field name="OSAGE" since="26" />
+		<field name="OSAGE_ID" since="26" />
 		<field name="OSMANYA" />
 		<field name="OSMANYA_ID" />
 		<field name="PAHAWH_HMONG" />
@@ -15420,6 +16637,10 @@
 		<field name="TAKRI_ID" />
 		<field name="TAMIL" />
 		<field name="TAMIL_ID" />
+		<field name="TANGUT" since="26" />
+		<field name="TANGUT_COMPONENTS" since="26" />
+		<field name="TANGUT_COMPONENTS_ID" since="26" />
+		<field name="TANGUT_ID" since="26" />
 		<field name="TELUGU" />
 		<field name="TELUGU_ID" />
 		<field name="THAANA" />
@@ -15466,7 +16687,11 @@
 		<field name="DOUBLE_QUOTE" />
 		<field name="EXTEND" />
 		<field name="EXTENDNUMLET" />
+		<field name="E_BASE" since="26" />
+		<field name="E_BASE_GAZ" since="26" />
+		<field name="E_MODIFIER" since="26" />
 		<field name="FORMAT" />
+		<field name="GLUE_AFTER_ZWJ" since="26" />
 		<field name="HEBREW_LETTER" />
 		<field name="KATAKANA" />
 		<field name="LF" />
@@ -15478,6 +16703,7 @@
 		<field name="OTHER" />
 		<field name="REGIONAL_INDICATOR" />
 		<field name="SINGLE_QUOTE" />
+		<field name="ZWJ" since="26" />
 	</class>
 	<class name="android/icu/lang/UCharacterCategory" since="24">
 		<extends name="java/lang/Object" />
@@ -15702,6 +16928,7 @@
 		<method name="hasScript(II)Z" />
 		<method name="isCased(I)Z" />
 		<method name="isRightToLeft(I)Z" />
+		<field name="ADLAM" since="26" />
 		<field name="AFAKA" />
 		<field name="AHOM" />
 		<field name="ANATOLIAN_HIEROGLYPHS" />
@@ -15713,6 +16940,7 @@
 		<field name="BASSA_VAH" />
 		<field name="BATAK" />
 		<field name="BENGALI" />
+		<field name="BHAIKSUKI" since="26" />
 		<field name="BLISSYMBOLS" />
 		<field name="BOOK_PAHLAVI" />
 		<field name="BOPOMOFO" />
@@ -15751,6 +16979,7 @@
 		<field name="HAN" />
 		<field name="HANGUL" />
 		<field name="HANUNOO" />
+		<field name="HAN_WITH_BOPOMOFO" since="26" />
 		<field name="HARAPPAN_INDUS" />
 		<field name="HATRAN" />
 		<field name="HEBREW" />
@@ -15761,6 +16990,7 @@
 		<field name="INSCRIPTIONAL_PAHLAVI" />
 		<field name="INSCRIPTIONAL_PARTHIAN" />
 		<field name="INVALID_CODE" />
+		<field name="JAMO" since="26" />
 		<field name="JAPANESE" />
 		<field name="JAVANESE" />
 		<field name="JURCHEN" />
@@ -15794,6 +17024,7 @@
 		<field name="MANDAEAN" />
 		<field name="MANDAIC" />
 		<field name="MANICHAEAN" />
+		<field name="MARCHEN" since="26" />
 		<field name="MATHEMATICAL_NOTATION" />
 		<field name="MAYAN_HIEROGLYPHS" />
 		<field name="MEITEI_MAYEK" />
@@ -15810,6 +17041,7 @@
 		<field name="MYANMAR" />
 		<field name="NABATAEAN" />
 		<field name="NAKHI_GEBA" />
+		<field name="NEWA" since="26" />
 		<field name="NEW_TAI_LUE" />
 		<field name="NKO" />
 		<field name="NUSHU" />
@@ -15824,6 +17056,7 @@
 		<field name="OL_CHIKI" />
 		<field name="ORIYA" />
 		<field name="ORKHON" />
+		<field name="OSAGE" since="26" />
 		<field name="OSMANYA" />
 		<field name="PAHAWH_HMONG" />
 		<field name="PALMYRENE" />
@@ -15849,6 +17082,7 @@
 		<field name="SUNDANESE" />
 		<field name="SYLOTI_NAGRI" />
 		<field name="SYMBOLS" />
+		<field name="SYMBOLS_EMOJI" since="26" />
 		<field name="SYRIAC" />
 		<field name="TAGALOG" />
 		<field name="TAGBANWA" />
@@ -16371,6 +17605,8 @@
 		<method name="values()[Landroid/icu/text/DateFormat$BooleanAttribute;" />
 		<field name="PARSE_ALLOW_NUMERIC" />
 		<field name="PARSE_ALLOW_WHITESPACE" />
+		<field name="PARSE_MULTIPLE_PATTERNS_FOR_MATCH" since="26" />
+		<field name="PARSE_PARTIAL_LITERAL_MATCH" since="26" />
 	</class>
 	<class name="android/icu/text/DateFormat$Field" since="24">
 		<extends name="java/text/Format$Field" />
@@ -16749,6 +17985,16 @@
 		<method name="hasErrors()Z" />
 		<method name="isTransitionalDifferent()Z" />
 	</class>
+	<class name="android/icu/text/ListFormatter" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="format(Ljava/util/Collection;)Ljava/lang/String;" />
+		<method name="format([Ljava/lang/Object;)Ljava/lang/String;" />
+		<method name="getInstance()Landroid/icu/text/ListFormatter;" />
+		<method name="getInstance(Landroid/icu/util/ULocale;)Landroid/icu/text/ListFormatter;" />
+		<method name="getInstance(Ljava/util/Locale;)Landroid/icu/text/ListFormatter;" />
+		<method name="getPatternForNumItems(I)Ljava/lang/String;" />
+	</class>
 	<class name="android/icu/text/LocaleDisplayNames" since="24">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
@@ -16760,6 +18006,8 @@
 		<method name="getInstance(Ljava/util/Locale;)Landroid/icu/text/LocaleDisplayNames;" />
 		<method name="getInstance(Ljava/util/Locale;[Landroid/icu/text/DisplayContext;)Landroid/icu/text/LocaleDisplayNames;" />
 		<method name="getLocale()Landroid/icu/util/ULocale;" />
+		<method name="getUiList(Ljava/util/Set;ZLjava/util/Comparator;)Ljava/util/List;" since="26" />
+		<method name="getUiListCompareWholeItems(Ljava/util/Set;Ljava/util/Comparator;)Ljava/util/List;" since="26" />
 		<method name="keyDisplayName(Ljava/lang/String;)Ljava/lang/String;" />
 		<method name="keyValueDisplayName(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;" />
 		<method name="languageDisplayName(Ljava/lang/String;)Ljava/lang/String;" />
@@ -16778,9 +18026,19 @@
 		<field name="DIALECT_NAMES" />
 		<field name="STANDARD_NAMES" />
 	</class>
+	<class name="android/icu/text/LocaleDisplayNames$UiListItem" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>(Landroid/icu/util/ULocale;Landroid/icu/util/ULocale;Ljava/lang/String;Ljava/lang/String;)V" />
+		<method name="getComparator(Ljava/util/Comparator;Z)Ljava/util/Comparator;" />
+		<field name="minimized" />
+		<field name="modified" />
+		<field name="nameInDisplayLocale" />
+		<field name="nameInSelf" />
+	</class>
 	<class name="android/icu/text/MeasureFormat" since="24">
 		<extends name="android/icu/text/UFormat" />
 		<method name="&lt;init>()V" />
+		<method name="formatMeasurePerUnit(Landroid/icu/util/Measure;Landroid/icu/util/MeasureUnit;Ljava/lang/StringBuilder;Ljava/text/FieldPosition;)Ljava/lang/StringBuilder;" since="26" />
 		<method name="formatMeasures(Ljava/lang/StringBuilder;Ljava/text/FieldPosition;[Landroid/icu/util/Measure;)Ljava/lang/StringBuilder;" />
 		<method name="formatMeasures([Landroid/icu/util/Measure;)Ljava/lang/String;" />
 		<method name="getCurrencyFormat()Landroid/icu/text/MeasureFormat;" />
@@ -17054,6 +18312,7 @@
 		<field name="PERCENTSTYLE" />
 		<field name="PLURALCURRENCYSTYLE" />
 		<field name="SCIENTIFICSTYLE" />
+		<field name="STANDARDCURRENCYSTYLE" since="26" />
 	</class>
 	<class name="android/icu/text/NumberFormat$Field" since="24">
 		<extends name="java/text/Format$Field" />
@@ -17245,6 +18504,15 @@
 		<method name="setStrengthDefault()V" />
 		<method name="setUpperCaseFirst(Z)V" />
 	</class>
+	<class name="android/icu/text/ScientificNumberFormatter" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="format(Ljava/lang/Object;)Ljava/lang/String;" />
+		<method name="getMarkupInstance(Landroid/icu/text/DecimalFormat;Ljava/lang/String;Ljava/lang/String;)Landroid/icu/text/ScientificNumberFormatter;" />
+		<method name="getMarkupInstance(Landroid/icu/util/ULocale;Ljava/lang/String;Ljava/lang/String;)Landroid/icu/text/ScientificNumberFormatter;" />
+		<method name="getSuperscriptInstance(Landroid/icu/text/DecimalFormat;)Landroid/icu/text/ScientificNumberFormatter;" />
+		<method name="getSuperscriptInstance(Landroid/icu/util/ULocale;)Landroid/icu/text/ScientificNumberFormatter;" />
+	</class>
 	<class name="android/icu/text/SearchIterator" since="24">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>(Ljava/text/CharacterIterator;Landroid/icu/text/BreakIterator;)V" />
@@ -17732,9 +19000,9 @@
 		<method name="computeGregorianFields(I)V" />
 		<method name="computeGregorianMonthStart(II)I" />
 		<method name="computeJulianDay()I" />
-		<method name="computeMillisInDay()I" />
+		<method name="computeMillisInDay()I" deprecated="26" />
 		<method name="computeTime()V" />
-		<method name="computeZoneOffset(JI)I" />
+		<method name="computeZoneOffset(JI)I" deprecated="26" />
 		<method name="fieldDifference(Ljava/util/Date;I)I" />
 		<method name="fieldName(I)Ljava/lang/String;" />
 		<method name="floorDivide(II)I" />
@@ -17833,7 +19101,7 @@
 		<field name="AM_PM" />
 		<field name="APRIL" />
 		<field name="AUGUST" />
-		<field name="BASE_FIELD_COUNT" />
+		<field name="BASE_FIELD_COUNT" deprecated="26" />
 		<field name="DATE" />
 		<field name="DAY_OF_MONTH" />
 		<field name="DAY_OF_WEEK" />
@@ -17861,7 +19129,7 @@
 		<field name="MARCH" />
 		<field name="MAXIMUM" />
 		<field name="MAX_DATE" />
-		<field name="MAX_FIELD_COUNT" />
+		<field name="MAX_FIELD_COUNT" deprecated="26" />
 		<field name="MAX_JULIAN" />
 		<field name="MAX_MILLIS" />
 		<field name="MAY" />
@@ -18004,6 +19272,33 @@
 		<method name="getFromDate()J" />
 		<method name="getToDate()J" />
 	</class>
+	<class name="android/icu/util/EthiopicCalendar" since="26">
+		<extends name="android/icu/util/CECalendar" />
+		<method name="&lt;init>()V" />
+		<method name="&lt;init>(III)V" />
+		<method name="&lt;init>(IIIIII)V" />
+		<method name="&lt;init>(Landroid/icu/util/TimeZone;)V" />
+		<method name="&lt;init>(Landroid/icu/util/TimeZone;Landroid/icu/util/ULocale;)V" />
+		<method name="&lt;init>(Landroid/icu/util/TimeZone;Ljava/util/Locale;)V" />
+		<method name="&lt;init>(Landroid/icu/util/ULocale;)V" />
+		<method name="&lt;init>(Ljava/util/Date;)V" />
+		<method name="&lt;init>(Ljava/util/Locale;)V" />
+		<method name="isAmeteAlemEra()Z" />
+		<method name="setAmeteAlemEra(Z)V" />
+		<field name="GENBOT" />
+		<field name="HAMLE" />
+		<field name="HEDAR" />
+		<field name="MEGABIT" />
+		<field name="MESKEREM" />
+		<field name="MIAZIA" />
+		<field name="NEHASSE" />
+		<field name="PAGUMEN" />
+		<field name="SENE" />
+		<field name="TAHSAS" />
+		<field name="TEKEMT" />
+		<field name="TER" />
+		<field name="YEKATIT" />
+	</class>
 	<class name="android/icu/util/Freezable" since="24">
 		<extends name="java/lang/Object" />
 		<implements name="java/lang/Cloneable" />
@@ -18169,6 +19464,7 @@
 		<field name="CELSIUS" />
 		<field name="CENTILITER" />
 		<field name="CENTIMETER" />
+		<field name="CENTURY" since="26" />
 		<field name="CUBIC_CENTIMETER" />
 		<field name="CUBIC_FOOT" />
 		<field name="CUBIC_INCH" />
@@ -18177,6 +19473,7 @@
 		<field name="CUBIC_MILE" />
 		<field name="CUBIC_YARD" />
 		<field name="CUP" />
+		<field name="CUP_METRIC" since="26" />
 		<field name="DAY" />
 		<field name="DECILITER" />
 		<field name="DECIMETER" />
@@ -18188,6 +19485,7 @@
 		<field name="FOOT" />
 		<field name="FURLONG" />
 		<field name="GALLON" />
+		<field name="GENERIC_TEMPERATURE" since="26" />
 		<field name="GIGABIT" />
 		<field name="GIGABYTE" />
 		<field name="GIGAHERTZ" />
@@ -18215,8 +19513,10 @@
 		<field name="KILOMETER_PER_HOUR" />
 		<field name="KILOWATT" />
 		<field name="KILOWATT_HOUR" />
+		<field name="KNOT" since="26" />
 		<field name="LIGHT_YEAR" />
 		<field name="LITER" />
+		<field name="LITER_PER_100KILOMETERS" since="26" />
 		<field name="LITER_PER_KILOMETER" />
 		<field name="LUX" />
 		<field name="MEGABIT" />
@@ -18234,6 +19534,7 @@
 		<field name="MILE" />
 		<field name="MILE_PER_GALLON" />
 		<field name="MILE_PER_HOUR" />
+		<field name="MILE_SCANDINAVIAN" since="26" />
 		<field name="MILLIAMPERE" />
 		<field name="MILLIBAR" />
 		<field name="MILLIGRAM" />
@@ -18253,10 +19554,12 @@
 		<field name="PARSEC" />
 		<field name="PICOMETER" />
 		<field name="PINT" />
+		<field name="PINT_METRIC" since="26" />
 		<field name="POUND" />
 		<field name="POUND_PER_SQUARE_INCH" />
 		<field name="QUART" />
 		<field name="RADIAN" />
+		<field name="REVOLUTION_ANGLE" since="26" />
 		<field name="SECOND" />
 		<field name="SQUARE_CENTIMETER" />
 		<field name="SQUARE_FOOT" />
@@ -18365,6 +19668,8 @@
 		<field name="SHORT_COMMONLY_USED" />
 		<field name="SHORT_GENERIC" />
 		<field name="SHORT_GMT" />
+		<field name="TIMEZONE_ICU" since="26" />
+		<field name="TIMEZONE_JDK" since="26" />
 		<field name="UNKNOWN_ZONE" />
 		<field name="UNKNOWN_ZONE_ID" />
 	</class>
@@ -18518,6 +19823,36 @@
 		<field name="DISPLAY" />
 		<field name="FORMAT" />
 	</class>
+	<class name="android/icu/util/UniversalTimeScale" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="bigDecimalFrom(DI)Landroid/icu/math/BigDecimal;" />
+		<method name="bigDecimalFrom(JI)Landroid/icu/math/BigDecimal;" />
+		<method name="bigDecimalFrom(Landroid/icu/math/BigDecimal;I)Landroid/icu/math/BigDecimal;" />
+		<method name="from(JI)J" />
+		<method name="getTimeScaleValue(II)J" />
+		<method name="toBigDecimal(JI)Landroid/icu/math/BigDecimal;" />
+		<method name="toBigDecimal(Landroid/icu/math/BigDecimal;I)Landroid/icu/math/BigDecimal;" />
+		<method name="toLong(JI)J" />
+		<field name="DB2_TIME" />
+		<field name="DOTNET_DATE_TIME" />
+		<field name="EPOCH_OFFSET_PLUS_1_VALUE" />
+		<field name="EPOCH_OFFSET_VALUE" />
+		<field name="EXCEL_TIME" />
+		<field name="FROM_MAX_VALUE" />
+		<field name="FROM_MIN_VALUE" />
+		<field name="ICU4C_TIME" />
+		<field name="JAVA_TIME" />
+		<field name="MAC_OLD_TIME" />
+		<field name="MAC_TIME" />
+		<field name="MAX_SCALE" />
+		<field name="TO_MAX_VALUE" />
+		<field name="TO_MIN_VALUE" />
+		<field name="UNITS_VALUE" />
+		<field name="UNIX_MICROSECONDS_TIME" />
+		<field name="UNIX_TIME" />
+		<field name="WINDOWS_FILE_TIME" />
+	</class>
 	<class name="android/icu/util/ValueIterator" since="24">
 		<extends name="java/lang/Object" />
 		<method name="next(Landroid/icu/util/ValueIterator$Element;)Z" />
@@ -18573,6 +19908,7 @@
 		<field name="UNICODE_6_3" />
 		<field name="UNICODE_7_0" />
 		<field name="UNICODE_8_0" />
+		<field name="UNICODE_9_0" since="26" />
 	</class>
 	<class name="android/inputmethodservice/AbstractInputMethodService" since="3">
 		<extends name="android/app/Service" />
@@ -18936,6 +20272,7 @@
 		<method name="getAccumulatedDeltaRangeMeters()D" />
 		<method name="getAccumulatedDeltaRangeState()I" />
 		<method name="getAccumulatedDeltaRangeUncertaintyMeters()D" />
+		<method name="getAutomaticGainControlLevelDb()D" since="26" />
 		<method name="getCarrierCycles()J" />
 		<method name="getCarrierFrequencyHz()F" />
 		<method name="getCarrierPhase()D" />
@@ -18951,6 +20288,7 @@
 		<method name="getState()I" />
 		<method name="getSvid()I" />
 		<method name="getTimeOffsetNanos()D" />
+		<method name="hasAutomaticGainControlLevelDb()Z" since="26" />
 		<method name="hasCarrierCycles()Z" />
 		<method name="hasCarrierFrequencyHz()Z" />
 		<method name="hasCarrierPhase()Z" />
@@ -18973,11 +20311,13 @@
 		<field name="STATE_GAL_E1C_2ND_CODE_LOCK" />
 		<field name="STATE_GLO_STRING_SYNC" />
 		<field name="STATE_GLO_TOD_DECODED" />
+		<field name="STATE_GLO_TOD_KNOWN" since="26" />
 		<field name="STATE_MSEC_AMBIGUOUS" />
 		<field name="STATE_SBAS_SYNC" />
 		<field name="STATE_SUBFRAME_SYNC" />
 		<field name="STATE_SYMBOL_SYNC" />
 		<field name="STATE_TOW_DECODED" />
+		<field name="STATE_TOW_KNOWN" since="26" />
 		<field name="STATE_UNKNOWN" />
 	</class>
 	<class name="android/location/GnssMeasurementsEvent" since="24">
@@ -19035,12 +20375,14 @@
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
 		<method name="getAzimuthDegrees(I)F" />
+		<method name="getCarrierFrequencyHz(I)F" since="26" />
 		<method name="getCn0DbHz(I)F" />
 		<method name="getConstellationType(I)I" />
 		<method name="getElevationDegrees(I)F" />
 		<method name="getSatelliteCount()I" />
 		<method name="getSvid(I)I" />
 		<method name="hasAlmanacData(I)Z" />
+		<method name="hasCarrierFrequencyHz(I)Z" since="26" />
 		<method name="hasEphemerisData(I)Z" />
 		<method name="usedInFix(I)Z" />
 		<field name="CONSTELLATION_BEIDOU" />
@@ -19103,34 +20445,43 @@
 		<method name="getAccuracy()F" />
 		<method name="getAltitude()D" />
 		<method name="getBearing()F" />
+		<method name="getBearingAccuracyDegrees()F" since="26" />
 		<method name="getElapsedRealtimeNanos()J" since="17" />
 		<method name="getExtras()Landroid/os/Bundle;" />
 		<method name="getLatitude()D" />
 		<method name="getLongitude()D" />
 		<method name="getProvider()Ljava/lang/String;" />
 		<method name="getSpeed()F" />
+		<method name="getSpeedAccuracyMetersPerSecond()F" since="26" />
 		<method name="getTime()J" />
+		<method name="getVerticalAccuracyMeters()F" since="26" />
 		<method name="hasAccuracy()Z" />
 		<method name="hasAltitude()Z" />
 		<method name="hasBearing()Z" />
+		<method name="hasBearingAccuracy()Z" since="26" />
 		<method name="hasSpeed()Z" />
+		<method name="hasSpeedAccuracy()Z" since="26" />
+		<method name="hasVerticalAccuracy()Z" since="26" />
 		<method name="isFromMockProvider()Z" since="18" />
-		<method name="removeAccuracy()V" />
-		<method name="removeAltitude()V" />
-		<method name="removeBearing()V" />
-		<method name="removeSpeed()V" />
+		<method name="removeAccuracy()V" deprecated="26" />
+		<method name="removeAltitude()V" deprecated="26" />
+		<method name="removeBearing()V" deprecated="26" />
+		<method name="removeSpeed()V" deprecated="26" />
 		<method name="reset()V" />
 		<method name="set(Landroid/location/Location;)V" />
 		<method name="setAccuracy(F)V" />
 		<method name="setAltitude(D)V" />
 		<method name="setBearing(F)V" />
+		<method name="setBearingAccuracyDegrees(F)V" since="26" />
 		<method name="setElapsedRealtimeNanos(J)V" since="17" />
 		<method name="setExtras(Landroid/os/Bundle;)V" />
 		<method name="setLatitude(D)V" />
 		<method name="setLongitude(D)V" />
 		<method name="setProvider(Ljava/lang/String;)V" />
 		<method name="setSpeed(F)V" />
+		<method name="setSpeedAccuracyMetersPerSecond(F)V" since="26" />
 		<method name="setTime(J)V" />
+		<method name="setVerticalAccuracyMeters(F)V" since="26" />
 		<field name="CREATOR" />
 		<field name="FORMAT_DEGREES" />
 		<field name="FORMAT_MINUTES" />
@@ -19248,6 +20599,7 @@
 		<method name="getContentType()I" />
 		<method name="getFlags()I" />
 		<method name="getUsage()I" />
+		<method name="getVolumeControlStream()I" since="26" />
 		<field name="CONTENT_TYPE_MOVIE" />
 		<field name="CONTENT_TYPE_MUSIC" />
 		<field name="CONTENT_TYPE_SONIFICATION" />
@@ -19256,11 +20608,12 @@
 		<field name="CREATOR" />
 		<field name="FLAG_AUDIBILITY_ENFORCED" />
 		<field name="FLAG_HW_AV_SYNC" />
-		<field name="FLAG_LOW_LATENCY" since="24" />
+		<field name="FLAG_LOW_LATENCY" since="24" deprecated="26" />
 		<field name="USAGE_ALARM" />
 		<field name="USAGE_ASSISTANCE_ACCESSIBILITY" />
 		<field name="USAGE_ASSISTANCE_NAVIGATION_GUIDANCE" />
 		<field name="USAGE_ASSISTANCE_SONIFICATION" />
+		<field name="USAGE_ASSISTANT" since="26" />
 		<field name="USAGE_GAME" />
 		<field name="USAGE_MEDIA" />
 		<field name="USAGE_NOTIFICATION" />
@@ -19322,9 +20675,30 @@
 		<field name="TYPE_UNKNOWN" />
 		<field name="TYPE_USB_ACCESSORY" />
 		<field name="TYPE_USB_DEVICE" />
+		<field name="TYPE_USB_HEADSET" since="26" />
 		<field name="TYPE_WIRED_HEADPHONES" />
 		<field name="TYPE_WIRED_HEADSET" />
 	</class>
+	<class name="android/media/AudioFocusRequest" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="acceptsDelayedFocusGain()Z" />
+		<method name="getAudioAttributes()Landroid/media/AudioAttributes;" />
+		<method name="getFocusGain()I" />
+		<method name="willPauseWhenDucked()Z" />
+	</class>
+	<class name="android/media/AudioFocusRequest$Builder" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>(I)V" />
+		<method name="&lt;init>(Landroid/media/AudioFocusRequest;)V" />
+		<method name="build()Landroid/media/AudioFocusRequest;" />
+		<method name="setAcceptsDelayedFocusGain(Z)Landroid/media/AudioFocusRequest$Builder;" />
+		<method name="setAudioAttributes(Landroid/media/AudioAttributes;)Landroid/media/AudioFocusRequest$Builder;" />
+		<method name="setFocusGain(I)Landroid/media/AudioFocusRequest$Builder;" />
+		<method name="setOnAudioFocusChangeListener(Landroid/media/AudioManager$OnAudioFocusChangeListener;)Landroid/media/AudioFocusRequest$Builder;" />
+		<method name="setOnAudioFocusChangeListener(Landroid/media/AudioManager$OnAudioFocusChangeListener;Landroid/os/Handler;)Landroid/media/AudioFocusRequest$Builder;" />
+		<method name="setWillPauseWhenDucked(Z)Landroid/media/AudioFocusRequest$Builder;" />
+	</class>
 	<class name="android/media/AudioFormat" since="3">
 		<extends name="java/lang/Object" />
 		<implements name="android/os/Parcelable" since="24" />
@@ -19402,12 +20776,14 @@
 	<class name="android/media/AudioManager" since="1">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
-		<method name="abandonAudioFocus(Landroid/media/AudioManager$OnAudioFocusChangeListener;)I" since="8" />
+		<method name="abandonAudioFocus(Landroid/media/AudioManager$OnAudioFocusChangeListener;)I" since="8" deprecated="26" />
+		<method name="abandonAudioFocusRequest(Landroid/media/AudioFocusRequest;)I" since="26" />
 		<method name="adjustStreamVolume(III)V" />
 		<method name="adjustSuggestedStreamVolume(III)V" />
 		<method name="adjustVolume(II)V" />
 		<method name="dispatchMediaKeyEvent(Landroid/view/KeyEvent;)V" since="19" />
 		<method name="generateAudioSessionId()I" since="21" />
+		<method name="getActivePlaybackConfigurations()Ljava/util/List;" since="26" />
 		<method name="getActiveRecordingConfigurations()Ljava/util/List;" since="24" />
 		<method name="getDevices(I)[Landroid/media/AudioDeviceInfo;" since="23" />
 		<method name="getMode()I" />
@@ -19418,7 +20794,7 @@
 		<method name="getStreamMaxVolume(I)I" />
 		<method name="getStreamVolume(I)I" />
 		<method name="getVibrateSetting(I)I" deprecated="16" />
-		<method name="isBluetoothA2dpOn()Z" since="3" />
+		<method name="isBluetoothA2dpOn()Z" since="3" deprecated="26" />
 		<method name="isBluetoothScoAvailableOffCall()Z" since="8" />
 		<method name="isBluetoothScoOn()Z" />
 		<method name="isMicrophoneMute()Z" />
@@ -19431,12 +20807,14 @@
 		<method name="playSoundEffect(I)V" />
 		<method name="playSoundEffect(IF)V" since="3" />
 		<method name="registerAudioDeviceCallback(Landroid/media/AudioDeviceCallback;Landroid/os/Handler;)V" since="23" />
+		<method name="registerAudioPlaybackCallback(Landroid/media/AudioManager$AudioPlaybackCallback;Landroid/os/Handler;)V" since="26" />
 		<method name="registerAudioRecordingCallback(Landroid/media/AudioManager$AudioRecordingCallback;Landroid/os/Handler;)V" since="24" />
 		<method name="registerMediaButtonEventReceiver(Landroid/app/PendingIntent;)V" since="18" deprecated="21" />
 		<method name="registerMediaButtonEventReceiver(Landroid/content/ComponentName;)V" since="8" deprecated="21" />
 		<method name="registerRemoteControlClient(Landroid/media/RemoteControlClient;)V" since="14" deprecated="21" />
 		<method name="registerRemoteController(Landroid/media/RemoteController;)Z" since="19" deprecated="21" />
-		<method name="requestAudioFocus(Landroid/media/AudioManager$OnAudioFocusChangeListener;II)I" since="8" />
+		<method name="requestAudioFocus(Landroid/media/AudioFocusRequest;)I" since="26" />
+		<method name="requestAudioFocus(Landroid/media/AudioManager$OnAudioFocusChangeListener;II)I" since="8" deprecated="26" />
 		<method name="setBluetoothA2dpOn(Z)V" since="3" deprecated="16" />
 		<method name="setBluetoothScoOn(Z)V" />
 		<method name="setMicrophoneMute(Z)V" />
@@ -19455,6 +20833,7 @@
 		<method name="stopBluetoothSco()V" since="8" />
 		<method name="unloadSoundEffects()V" />
 		<method name="unregisterAudioDeviceCallback(Landroid/media/AudioDeviceCallback;)V" since="23" />
+		<method name="unregisterAudioPlaybackCallback(Landroid/media/AudioManager$AudioPlaybackCallback;)V" since="26" />
 		<method name="unregisterAudioRecordingCallback(Landroid/media/AudioManager$AudioRecordingCallback;)V" since="24" />
 		<method name="unregisterMediaButtonEventReceiver(Landroid/app/PendingIntent;)V" since="18" deprecated="21" />
 		<method name="unregisterMediaButtonEventReceiver(Landroid/content/ComponentName;)V" since="8" deprecated="21" />
@@ -19478,6 +20857,8 @@
 		<field name="AUDIOFOCUS_LOSS" since="8" />
 		<field name="AUDIOFOCUS_LOSS_TRANSIENT" since="8" />
 		<field name="AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK" since="8" />
+		<field name="AUDIOFOCUS_NONE" since="26" />
+		<field name="AUDIOFOCUS_REQUEST_DELAYED" since="26" />
 		<field name="AUDIOFOCUS_REQUEST_FAILED" since="8" />
 		<field name="AUDIOFOCUS_REQUEST_GRANTED" since="8" />
 		<field name="AUDIO_SESSION_ID_GENERATE" since="21" />
@@ -19536,6 +20917,7 @@
 		<field name="SCO_AUDIO_STATE_CONNECTING" since="14" />
 		<field name="SCO_AUDIO_STATE_DISCONNECTED" since="8" />
 		<field name="SCO_AUDIO_STATE_ERROR" since="8" />
+		<field name="STREAM_ACCESSIBILITY" since="26" />
 		<field name="STREAM_ALARM" />
 		<field name="STREAM_DTMF" since="5" />
 		<field name="STREAM_MUSIC" />
@@ -19551,6 +20933,11 @@
 		<field name="VIBRATE_TYPE_NOTIFICATION" deprecated="16" />
 		<field name="VIBRATE_TYPE_RINGER" deprecated="16" />
 	</class>
+	<class name="android/media/AudioManager$AudioPlaybackCallback" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="onPlaybackConfigChanged(Ljava/util/List;)V" />
+	</class>
 	<class name="android/media/AudioManager$AudioRecordingCallback" since="24">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
@@ -19560,6 +20947,13 @@
 		<extends name="java/lang/Object" />
 		<method name="onAudioFocusChange(I)V" />
 	</class>
+	<class name="android/media/AudioPlaybackConfiguration" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/os/Parcelable" />
+		<method name="&lt;init>()V" />
+		<method name="getAudioAttributes()Landroid/media/AudioAttributes;" />
+		<field name="CREATOR" />
+	</class>
 	<class name="android/media/AudioRecord" since="3">
 		<extends name="java/lang/Object" />
 		<implements name="android/media/AudioRouting" since="24" />
@@ -19662,8 +21056,9 @@
 	<class name="android/media/AudioTrack" since="3">
 		<extends name="java/lang/Object" />
 		<implements name="android/media/AudioRouting" since="24" />
-		<method name="&lt;init>(IIIIII)V" />
-		<method name="&lt;init>(IIIIIII)V" since="9" />
+		<implements name="android/media/VolumeAutomation" since="26" />
+		<method name="&lt;init>(IIIIII)V" deprecated="26" />
+		<method name="&lt;init>(IIIIIII)V" since="9" deprecated="26" />
 		<method name="&lt;init>(Landroid/media/AudioAttributes;Landroid/media/AudioFormat;III)V" since="21" />
 		<method name="addOnRoutingChangedListener(Landroid/media/AudioTrack$OnRoutingChangedListener;Landroid/os/Handler;)V" since="23" deprecated="24" />
 		<method name="attachAuxEffect(I)I" since="9" />
@@ -19681,6 +21076,7 @@
 		<method name="getNativeFrameCount()I" deprecated="19" />
 		<method name="getNativeOutputSampleRate(I)I" />
 		<method name="getNotificationMarkerPosition()I" />
+		<method name="getPerformanceMode()I" since="26" />
 		<method name="getPlayState()I" />
 		<method name="getPlaybackHeadPosition()I" />
 		<method name="getPlaybackParams()Landroid/media/PlaybackParams;" since="23" />
@@ -19726,6 +21122,9 @@
 		<field name="ERROR_INVALID_OPERATION" />
 		<field name="MODE_STATIC" />
 		<field name="MODE_STREAM" />
+		<field name="PERFORMANCE_MODE_LOW_LATENCY" since="26" />
+		<field name="PERFORMANCE_MODE_NONE" since="26" />
+		<field name="PERFORMANCE_MODE_POWER_SAVING" since="26" />
 		<field name="PLAYSTATE_PAUSED" />
 		<field name="PLAYSTATE_PLAYING" />
 		<field name="PLAYSTATE_STOPPED" />
@@ -19743,6 +21142,7 @@
 		<method name="setAudioAttributes(Landroid/media/AudioAttributes;)Landroid/media/AudioTrack$Builder;" />
 		<method name="setAudioFormat(Landroid/media/AudioFormat;)Landroid/media/AudioTrack$Builder;" />
 		<method name="setBufferSizeInBytes(I)Landroid/media/AudioTrack$Builder;" />
+		<method name="setPerformanceMode(I)Landroid/media/AudioTrack$Builder;" since="26" />
 		<method name="setSessionId(I)Landroid/media/AudioTrack$Builder;" />
 		<method name="setTransferMode(I)Landroid/media/AudioTrack$Builder;" />
 	</class>
@@ -19835,8 +21235,11 @@
 		<method name="getAttributeInt(Ljava/lang/String;I)I" />
 		<method name="getLatLong([F)Z" />
 		<method name="getThumbnail()[B" />
+		<method name="getThumbnailBitmap()Landroid/graphics/Bitmap;" since="26" />
+		<method name="getThumbnailBytes()[B" since="26" />
 		<method name="getThumbnailRange()[J" since="24" />
 		<method name="hasThumbnail()Z" />
+		<method name="isThumbnailCompressed()Z" since="26" />
 		<method name="saveAttributes()V" />
 		<method name="setAttribute(Ljava/lang/String;Ljava/lang/String;)V" />
 		<field name="ORIENTATION_FLIP_HORIZONTAL" />
@@ -19864,8 +21267,10 @@
 		<field name="TAG_DATETIME" />
 		<field name="TAG_DATETIME_DIGITIZED" since="23" />
 		<field name="TAG_DATETIME_ORIGINAL" since="24" />
+		<field name="TAG_DEFAULT_CROP_SIZE" since="26" />
 		<field name="TAG_DEVICE_SETTING_DESCRIPTION" since="24" />
 		<field name="TAG_DIGITAL_ZOOM_RATIO" since="24" />
+		<field name="TAG_DNG_VERSION" since="26" />
 		<field name="TAG_EXIF_VERSION" since="24" />
 		<field name="TAG_EXPOSURE_BIAS_VALUE" since="24" />
 		<field name="TAG_EXPOSURE_INDEX" since="24" />
@@ -19929,7 +21334,12 @@
 		<field name="TAG_MAX_APERTURE_VALUE" since="24" />
 		<field name="TAG_METERING_MODE" since="24" />
 		<field name="TAG_MODEL" />
+		<field name="TAG_NEW_SUBFILE_TYPE" since="26" />
 		<field name="TAG_OECF" since="24" />
+		<field name="TAG_ORF_ASPECT_FRAME" since="26" />
+		<field name="TAG_ORF_PREVIEW_IMAGE_LENGTH" since="26" />
+		<field name="TAG_ORF_PREVIEW_IMAGE_START" since="26" />
+		<field name="TAG_ORF_THUMBNAIL_IMAGE" since="26" />
 		<field name="TAG_ORIENTATION" />
 		<field name="TAG_PHOTOMETRIC_INTERPRETATION" since="24" />
 		<field name="TAG_PIXEL_X_DIMENSION" since="24" />
@@ -19940,6 +21350,12 @@
 		<field name="TAG_RELATED_SOUND_FILE" since="24" />
 		<field name="TAG_RESOLUTION_UNIT" since="24" />
 		<field name="TAG_ROWS_PER_STRIP" since="24" />
+		<field name="TAG_RW2_ISO" since="26" />
+		<field name="TAG_RW2_JPG_FROM_RAW" since="26" />
+		<field name="TAG_RW2_SENSOR_BOTTOM_BORDER" since="26" />
+		<field name="TAG_RW2_SENSOR_LEFT_BORDER" since="26" />
+		<field name="TAG_RW2_SENSOR_RIGHT_BORDER" since="26" />
+		<field name="TAG_RW2_SENSOR_TOP_BORDER" since="26" />
 		<field name="TAG_SAMPLES_PER_PIXEL" since="24" />
 		<field name="TAG_SATURATION" since="24" />
 		<field name="TAG_SCENE_CAPTURE_TYPE" since="24" />
@@ -19952,6 +21368,7 @@
 		<field name="TAG_SPECTRAL_SENSITIVITY" since="24" />
 		<field name="TAG_STRIP_BYTE_COUNTS" since="24" />
 		<field name="TAG_STRIP_OFFSETS" since="24" />
+		<field name="TAG_SUBFILE_TYPE" since="26" />
 		<field name="TAG_SUBJECT_AREA" since="24" />
 		<field name="TAG_SUBJECT_DISTANCE" since="24" />
 		<field name="TAG_SUBJECT_DISTANCE_RANGE" since="24" />
@@ -20084,9 +21501,68 @@
 		<field name="START_VIDEO_RECORDING" />
 		<field name="STOP_VIDEO_RECORDING" />
 	</class>
+	<class name="android/media/MediaCas" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/lang/AutoCloseable" />
+		<method name="&lt;init>(I)V" />
+		<method name="enumeratePlugins()[Landroid/media/MediaCas$PluginDescriptor;" />
+		<method name="isSystemIdSupported(I)Z" />
+		<method name="openSession()Landroid/media/MediaCas$Session;" />
+		<method name="processEmm([B)V" />
+		<method name="processEmm([BII)V" />
+		<method name="provision(Ljava/lang/String;)V" />
+		<method name="refreshEntitlements(I[B)V" />
+		<method name="sendEvent(II[B)V" />
+		<method name="setEventListener(Landroid/media/MediaCas$EventListener;Landroid/os/Handler;)V" />
+		<method name="setPrivateData([B)V" />
+	</class>
+	<class name="android/media/MediaCas$EventListener" since="26">
+		<extends name="java/lang/Object" />
+		<method name="onEvent(Landroid/media/MediaCas;II[B)V" />
+	</class>
+	<class name="android/media/MediaCas$PluginDescriptor" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="getName()Ljava/lang/String;" />
+		<method name="getSystemId()I" />
+	</class>
+	<class name="android/media/MediaCas$Session" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/lang/AutoCloseable" />
+		<method name="&lt;init>(Landroid/media/MediaCas;)V" />
+		<method name="processEcm([B)V" />
+		<method name="processEcm([BII)V" />
+		<method name="setPrivateData([B)V" />
+	</class>
+	<class name="android/media/MediaCasException" since="26">
+		<extends name="java/lang/Exception" />
+		<method name="&lt;init>()V" />
+	</class>
+	<class name="android/media/MediaCasException$DeniedByServerException" since="26">
+		<extends name="android/media/MediaCasException" />
+		<method name="&lt;init>()V" />
+	</class>
+	<class name="android/media/MediaCasException$NotProvisionedException" since="26">
+		<extends name="android/media/MediaCasException" />
+		<method name="&lt;init>()V" />
+	</class>
+	<class name="android/media/MediaCasException$ResourceBusyException" since="26">
+		<extends name="android/media/MediaCasException" />
+		<method name="&lt;init>()V" />
+	</class>
+	<class name="android/media/MediaCasException$UnsupportedCasException" since="26">
+		<extends name="android/media/MediaCasException" />
+		<method name="&lt;init>()V" />
+	</class>
+	<class name="android/media/MediaCasStateException" since="26">
+		<extends name="java/lang/IllegalStateException" />
+		<method name="&lt;init>()V" />
+		<method name="getDiagnosticInfo()Ljava/lang/String;" />
+	</class>
 	<class name="android/media/MediaCodec" since="16">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
+		<method name="configure(Landroid/media/MediaFormat;Landroid/view/Surface;ILandroid/media/MediaDescrambler;)V" since="26" />
 		<method name="configure(Landroid/media/MediaFormat;Landroid/view/Surface;Landroid/media/MediaCrypto;I)V" />
 		<method name="createByCodecName(Ljava/lang/String;)Landroid/media/MediaCodec;" />
 		<method name="createDecoderByType(Ljava/lang/String;)Landroid/media/MediaCodec;" />
@@ -20101,6 +21577,7 @@
 		<method name="getInputBuffers()[Ljava/nio/ByteBuffer;" deprecated="21" />
 		<method name="getInputFormat()Landroid/media/MediaFormat;" since="21" />
 		<method name="getInputImage(I)Landroid/media/Image;" since="21" />
+		<method name="getMetrics()Landroid/os/PersistableBundle;" since="26" />
 		<method name="getName()Ljava/lang/String;" since="18" />
 		<method name="getOutputBuffer(I)Ljava/nio/ByteBuffer;" since="21" />
 		<method name="getOutputBuffers()[Ljava/nio/ByteBuffer;" deprecated="21" />
@@ -20126,6 +21603,7 @@
 		<field name="BUFFER_FLAG_CODEC_CONFIG" />
 		<field name="BUFFER_FLAG_END_OF_STREAM" />
 		<field name="BUFFER_FLAG_KEY_FRAME" since="21" />
+		<field name="BUFFER_FLAG_PARTIAL_FRAME" since="26" />
 		<field name="BUFFER_FLAG_SYNC_FRAME" deprecated="21" />
 		<field name="CONFIGURE_FLAG_ENCODE" />
 		<field name="CRYPTO_MODE_AES_CBC" since="24" />
@@ -20197,6 +21675,20 @@
 		<method name="getSkipBlocks()I" />
 		<method name="set(II)V" />
 	</class>
+	<class name="android/media/MediaCodec$MetricsConstants" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<field name="CODEC" />
+		<field name="ENCODER" />
+		<field name="HEIGHT" />
+		<field name="MIME_TYPE" />
+		<field name="MODE" />
+		<field name="MODE_AUDIO" />
+		<field name="MODE_VIDEO" />
+		<field name="ROTATION" />
+		<field name="SECURE" />
+		<field name="WIDTH" />
+	</class>
 	<class name="android/media/MediaCodec$OnFrameRenderedListener" since="23">
 		<extends name="java/lang/Object" />
 		<method name="onFrameRendered(Landroid/media/MediaCodec;JJ)V" />
@@ -20285,6 +21777,7 @@
 		<field name="COLOR_TI_FormatYUV420PackedSemiPlanar" deprecated="23" />
 		<field name="FEATURE_AdaptivePlayback" since="19" />
 		<field name="FEATURE_IntraRefresh" since="24" />
+		<field name="FEATURE_PartialFrame" since="26" />
 		<field name="FEATURE_SecurePlayback" since="21" />
 		<field name="FEATURE_TunneledPlayback" since="21" />
 		<field name="colorFormats" />
@@ -20295,6 +21788,7 @@
 		<method name="&lt;init>()V" />
 		<field name="AACObjectELD" />
 		<field name="AACObjectERLC" />
+		<field name="AACObjectERScalable" since="26" />
 		<field name="AACObjectHE" />
 		<field name="AACObjectHE_PS" />
 		<field name="AACObjectLC" />
@@ -20511,6 +22005,14 @@
 		<method name="getSize()J" />
 		<method name="readAt(J[BII)I" />
 	</class>
+	<class name="android/media/MediaDescrambler" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/lang/AutoCloseable" />
+		<method name="&lt;init>(I)V" />
+		<method name="descramble(Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;Landroid/media/MediaCodec$CryptoInfo;)I" />
+		<method name="requiresSecureDecoderComponent(Ljava/lang/String;)Z" />
+		<method name="setMediaCasSession(Landroid/media/MediaCas$Session;)V" />
+	</class>
 	<class name="android/media/MediaDescription" since="21">
 		<extends name="java/lang/Object" />
 		<implements name="android/os/Parcelable" />
@@ -20523,7 +22025,15 @@
 		<method name="getMediaUri()Landroid/net/Uri;" since="23" />
 		<method name="getSubtitle()Ljava/lang/CharSequence;" />
 		<method name="getTitle()Ljava/lang/CharSequence;" />
+		<field name="BT_FOLDER_TYPE_ALBUMS" since="26" />
+		<field name="BT_FOLDER_TYPE_ARTISTS" since="26" />
+		<field name="BT_FOLDER_TYPE_GENRES" since="26" />
+		<field name="BT_FOLDER_TYPE_MIXED" since="26" />
+		<field name="BT_FOLDER_TYPE_PLAYLISTS" since="26" />
+		<field name="BT_FOLDER_TYPE_TITLES" since="26" />
+		<field name="BT_FOLDER_TYPE_YEARS" since="26" />
 		<field name="CREATOR" />
+		<field name="EXTRA_BT_FOLDER_TYPE" since="26" />
 	</class>
 	<class name="android/media/MediaDescription$Builder" since="21">
 		<extends name="java/lang/Object" />
@@ -20565,7 +22075,7 @@
 		<method name="setOnKeyStatusChangeListener(Landroid/media/MediaDrm$OnKeyStatusChangeListener;Landroid/os/Handler;)V" since="23" />
 		<method name="setPropertyByteArray(Ljava/lang/String;[B)V" />
 		<method name="setPropertyString(Ljava/lang/String;Ljava/lang/String;)V" />
-		<field name="EVENT_KEY_EXPIRED" />
+		<field name="EVENT_KEY_EXPIRED" deprecated="26" />
 		<field name="EVENT_KEY_REQUIRED" />
 		<field name="EVENT_PROVISION_REQUIRED" deprecated="23" />
 		<field name="EVENT_SESSION_RECLAIMED" since="23" />
@@ -20644,7 +22154,9 @@
 		<method name="&lt;init>()V" />
 		<method name="advance()Z" />
 		<method name="getCachedDuration()J" />
+		<method name="getCasInfo(I)Landroid/media/MediaExtractor$CasInfo;" since="26" />
 		<method name="getDrmInitData()Landroid/media/DrmInitData;" since="24" />
+		<method name="getMetrics()Landroid/os/PersistableBundle;" since="26" />
 		<method name="getPsshInfo()Ljava/util/Map;" since="18" />
 		<method name="getSampleCryptoInfo(Landroid/media/MediaCodec$CryptoInfo;)Z" />
 		<method name="getSampleFlags()I" />
@@ -20664,13 +22176,28 @@
 		<method name="setDataSource(Ljava/io/FileDescriptor;JJ)V" />
 		<method name="setDataSource(Ljava/lang/String;)V" />
 		<method name="setDataSource(Ljava/lang/String;Ljava/util/Map;)V" />
+		<method name="setMediaCas(Landroid/media/MediaCas;)V" since="26" />
 		<method name="unselectTrack(I)V" />
 		<field name="SAMPLE_FLAG_ENCRYPTED" />
+		<field name="SAMPLE_FLAG_PARTIAL_FRAME" since="26" />
 		<field name="SAMPLE_FLAG_SYNC" />
 		<field name="SEEK_TO_CLOSEST_SYNC" />
 		<field name="SEEK_TO_NEXT_SYNC" />
 		<field name="SEEK_TO_PREVIOUS_SYNC" />
 	</class>
+	<class name="android/media/MediaExtractor$CasInfo" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="getSession()Landroid/media/MediaCas$Session;" />
+		<method name="getSystemId()I" />
+	</class>
+	<class name="android/media/MediaExtractor$MetricsConstants" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<field name="FORMAT" />
+		<field name="MIME_TYPE" />
+		<field name="TRACKS" />
+	</class>
 	<class name="android/media/MediaFormat" since="16">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
@@ -20731,6 +22258,7 @@
 		<field name="KEY_IS_FORCED_SUBTITLE" since="19" />
 		<field name="KEY_I_FRAME_INTERVAL" />
 		<field name="KEY_LANGUAGE" since="19" />
+		<field name="KEY_LATENCY" since="26" />
 		<field name="KEY_LEVEL" since="23" />
 		<field name="KEY_MAX_HEIGHT" since="19" />
 		<field name="KEY_MAX_INPUT_SIZE" />
@@ -20762,6 +22290,7 @@
 		<field name="MIMETYPE_AUDIO_OPUS" since="21" />
 		<field name="MIMETYPE_AUDIO_QCELP" since="21" />
 		<field name="MIMETYPE_AUDIO_RAW" since="21" />
+		<field name="MIMETYPE_AUDIO_SCRAMBLED" since="26" />
 		<field name="MIMETYPE_AUDIO_VORBIS" since="21" />
 		<field name="MIMETYPE_TEXT_CEA_608" since="21" />
 		<field name="MIMETYPE_TEXT_VTT" since="21" />
@@ -20772,6 +22301,7 @@
 		<field name="MIMETYPE_VIDEO_MPEG2" since="21" />
 		<field name="MIMETYPE_VIDEO_MPEG4" since="21" />
 		<field name="MIMETYPE_VIDEO_RAW" since="21" />
+		<field name="MIMETYPE_VIDEO_SCRAMBLED" since="26" />
 		<field name="MIMETYPE_VIDEO_VP8" since="21" />
 		<field name="MIMETYPE_VIDEO_VP9" since="21" />
 	</class>
@@ -20797,6 +22327,7 @@
 		<field name="METADATA_KEY_ARTIST" />
 		<field name="METADATA_KEY_ART_URI" />
 		<field name="METADATA_KEY_AUTHOR" />
+		<field name="METADATA_KEY_BT_FOLDER_TYPE" since="26" />
 		<field name="METADATA_KEY_COMPILATION" />
 		<field name="METADATA_KEY_COMPOSER" />
 		<field name="METADATA_KEY_DATE" />
@@ -20809,6 +22340,7 @@
 		<field name="METADATA_KEY_DURATION" />
 		<field name="METADATA_KEY_GENRE" />
 		<field name="METADATA_KEY_MEDIA_ID" />
+		<field name="METADATA_KEY_MEDIA_URI" since="26" />
 		<field name="METADATA_KEY_NUM_TRACKS" />
 		<field name="METADATA_KEY_RATING" />
 		<field name="METADATA_KEY_TITLE" />
@@ -20894,6 +22426,7 @@
 	</class>
 	<class name="android/media/MediaMuxer" since="18">
 		<extends name="java/lang/Object" />
+		<method name="&lt;init>(Ljava/io/FileDescriptor;I)V" since="26" />
 		<method name="&lt;init>(Ljava/lang/String;I)V" />
 		<method name="addTrack(Landroid/media/MediaFormat;)I" />
 		<method name="release()V" />
@@ -20906,11 +22439,13 @@
 	<class name="android/media/MediaMuxer$OutputFormat" since="18">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
+		<field name="MUXER_OUTPUT_3GPP" since="26" />
 		<field name="MUXER_OUTPUT_MPEG_4" />
 		<field name="MUXER_OUTPUT_WEBM" since="21" />
 	</class>
 	<class name="android/media/MediaPlayer" since="1">
 		<extends name="java/lang/Object" />
+		<implements name="android/media/VolumeAutomation" since="26" />
 		<method name="&lt;init>()V" />
 		<method name="addTimedTextSource(Landroid/content/Context;Landroid/net/Uri;Ljava/lang/String;)V" since="16" />
 		<method name="addTimedTextSource(Ljava/io/FileDescriptor;JJLjava/lang/String;)V" since="16" />
@@ -20925,7 +22460,11 @@
 		<method name="deselectTrack(I)V" since="16" />
 		<method name="getAudioSessionId()I" since="9" />
 		<method name="getCurrentPosition()I" />
+		<method name="getDrmInfo()Landroid/media/MediaPlayer$DrmInfo;" since="26" />
+		<method name="getDrmPropertyString(Ljava/lang/String;)Ljava/lang/String;" since="26" />
 		<method name="getDuration()I" />
+		<method name="getKeyRequest([B[BLjava/lang/String;ILjava/util/Map;)Landroid/media/MediaDrm$KeyRequest;" since="26" />
+		<method name="getMetrics()Landroid/os/PersistableBundle;" since="26" />
 		<method name="getPlaybackParams()Landroid/media/PlaybackParams;" since="23" />
 		<method name="getSelectedTrack(I)I" since="21" />
 		<method name="getSyncParams()Landroid/media/SyncParams;" since="23" />
@@ -20938,26 +22477,38 @@
 		<method name="pause()V" />
 		<method name="prepare()V" />
 		<method name="prepareAsync()V" />
+		<method name="prepareDrm(Ljava/util/UUID;)V" since="26" />
+		<method name="provideKeyResponse([B[B)[B" since="26" />
 		<method name="release()V" />
+		<method name="releaseDrm()V" since="26" />
 		<method name="reset()V" />
+		<method name="restoreKeys([B)V" since="26" />
 		<method name="seekTo(I)V" />
+		<method name="seekTo(JI)V" since="26" />
 		<method name="selectTrack(I)V" since="16" />
 		<method name="setAudioAttributes(Landroid/media/AudioAttributes;)V" since="21" />
 		<method name="setAudioSessionId(I)V" since="9" />
-		<method name="setAudioStreamType(I)V" />
+		<method name="setAudioStreamType(I)V" deprecated="26" />
 		<method name="setAuxEffectSendLevel(F)V" since="9" />
 		<method name="setDataSource(Landroid/content/Context;Landroid/net/Uri;)V" />
 		<method name="setDataSource(Landroid/content/Context;Landroid/net/Uri;Ljava/util/Map;)V" since="14" />
+		<method name="setDataSource(Landroid/content/Context;Landroid/net/Uri;Ljava/util/Map;Ljava/util/List;)V" since="26" />
 		<method name="setDataSource(Landroid/content/res/AssetFileDescriptor;)V" since="24" />
 		<method name="setDataSource(Landroid/media/MediaDataSource;)V" since="23" />
 		<method name="setDataSource(Ljava/io/FileDescriptor;)V" />
 		<method name="setDataSource(Ljava/io/FileDescriptor;JJ)V" />
 		<method name="setDataSource(Ljava/lang/String;)V" />
 		<method name="setDisplay(Landroid/view/SurfaceHolder;)V" />
+		<method name="setDrmPropertyString(Ljava/lang/String;Ljava/lang/String;)V" since="26" />
 		<method name="setLooping(Z)V" />
 		<method name="setNextMediaPlayer(Landroid/media/MediaPlayer;)V" since="16" />
 		<method name="setOnBufferingUpdateListener(Landroid/media/MediaPlayer$OnBufferingUpdateListener;)V" />
 		<method name="setOnCompletionListener(Landroid/media/MediaPlayer$OnCompletionListener;)V" />
+		<method name="setOnDrmConfigHelper(Landroid/media/MediaPlayer$OnDrmConfigHelper;)V" since="26" />
+		<method name="setOnDrmInfoListener(Landroid/media/MediaPlayer$OnDrmInfoListener;)V" since="26" />
+		<method name="setOnDrmInfoListener(Landroid/media/MediaPlayer$OnDrmInfoListener;Landroid/os/Handler;)V" since="26" />
+		<method name="setOnDrmPreparedListener(Landroid/media/MediaPlayer$OnDrmPreparedListener;)V" since="26" />
+		<method name="setOnDrmPreparedListener(Landroid/media/MediaPlayer$OnDrmPreparedListener;Landroid/os/Handler;)V" since="26" />
 		<method name="setOnErrorListener(Landroid/media/MediaPlayer$OnErrorListener;)V" />
 		<method name="setOnInfoListener(Landroid/media/MediaPlayer$OnInfoListener;)V" since="3" />
 		<method name="setOnPreparedListener(Landroid/media/MediaPlayer$OnPreparedListener;)V" />
@@ -20981,6 +22532,7 @@
 		<field name="MEDIA_ERROR_TIMED_OUT" since="17" />
 		<field name="MEDIA_ERROR_UNKNOWN" />
 		<field name="MEDIA_ERROR_UNSUPPORTED" since="17" />
+		<field name="MEDIA_INFO_AUDIO_NOT_PLAYING" since="26" />
 		<field name="MEDIA_INFO_BAD_INTERLEAVING" since="3" />
 		<field name="MEDIA_INFO_BUFFERING_END" since="9" />
 		<field name="MEDIA_INFO_BUFFERING_START" since="9" />
@@ -20989,12 +22541,47 @@
 		<field name="MEDIA_INFO_SUBTITLE_TIMED_OUT" since="19" />
 		<field name="MEDIA_INFO_UNKNOWN" since="3" />
 		<field name="MEDIA_INFO_UNSUPPORTED_SUBTITLE" since="19" />
+		<field name="MEDIA_INFO_VIDEO_NOT_PLAYING" since="26" />
 		<field name="MEDIA_INFO_VIDEO_RENDERING_START" since="17" />
 		<field name="MEDIA_INFO_VIDEO_TRACK_LAGGING" since="3" />
 		<field name="MEDIA_MIMETYPE_TEXT_SUBRIP" since="16" />
+		<field name="PREPARE_DRM_STATUS_PREPARATION_ERROR" since="26" />
+		<field name="PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR" since="26" />
+		<field name="PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR" since="26" />
+		<field name="PREPARE_DRM_STATUS_SUCCESS" since="26" />
+		<field name="SEEK_CLOSEST" since="26" />
+		<field name="SEEK_CLOSEST_SYNC" since="26" />
+		<field name="SEEK_NEXT_SYNC" since="26" />
+		<field name="SEEK_PREVIOUS_SYNC" since="26" />
 		<field name="VIDEO_SCALING_MODE_SCALE_TO_FIT" since="16" />
 		<field name="VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING" since="16" />
 	</class>
+	<class name="android/media/MediaPlayer$DrmInfo" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="getPssh()Ljava/util/Map;" />
+		<method name="getSupportedSchemes()[Ljava/util/UUID;" />
+	</class>
+	<class name="android/media/MediaPlayer$MetricsConstants" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<field name="CODEC_AUDIO" />
+		<field name="CODEC_VIDEO" />
+		<field name="DURATION" />
+		<field name="ERRORS" />
+		<field name="ERROR_CODE" />
+		<field name="FRAMES" />
+		<field name="FRAMES_DROPPED" />
+		<field name="HEIGHT" />
+		<field name="MIME_TYPE_AUDIO" />
+		<field name="MIME_TYPE_VIDEO" />
+		<field name="PLAYING" />
+		<field name="WIDTH" />
+	</class>
+	<class name="android/media/MediaPlayer$NoDrmSchemeException" since="26">
+		<extends name="android/media/MediaDrmException" />
+		<method name="&lt;init>(Ljava/lang/String;)V" />
+	</class>
 	<class name="android/media/MediaPlayer$OnBufferingUpdateListener" since="1">
 		<extends name="java/lang/Object" />
 		<method name="onBufferingUpdate(Landroid/media/MediaPlayer;I)V" />
@@ -21003,6 +22590,18 @@
 		<extends name="java/lang/Object" />
 		<method name="onCompletion(Landroid/media/MediaPlayer;)V" />
 	</class>
+	<class name="android/media/MediaPlayer$OnDrmConfigHelper" since="26">
+		<extends name="java/lang/Object" />
+		<method name="onDrmConfig(Landroid/media/MediaPlayer;)V" />
+	</class>
+	<class name="android/media/MediaPlayer$OnDrmInfoListener" since="26">
+		<extends name="java/lang/Object" />
+		<method name="onDrmInfo(Landroid/media/MediaPlayer;Landroid/media/MediaPlayer$DrmInfo;)V" />
+	</class>
+	<class name="android/media/MediaPlayer$OnDrmPreparedListener" since="26">
+		<extends name="java/lang/Object" />
+		<method name="onDrmPrepared(Landroid/media/MediaPlayer;I)V" />
+	</class>
 	<class name="android/media/MediaPlayer$OnErrorListener" since="1">
 		<extends name="java/lang/Object" />
 		<method name="onError(Landroid/media/MediaPlayer;II)Z" />
@@ -21031,6 +22630,14 @@
 		<extends name="java/lang/Object" />
 		<method name="onVideoSizeChanged(Landroid/media/MediaPlayer;II)V" />
 	</class>
+	<class name="android/media/MediaPlayer$ProvisioningNetworkErrorException" since="26">
+		<extends name="android/media/MediaDrmException" />
+		<method name="&lt;init>(Ljava/lang/String;)V" />
+	</class>
+	<class name="android/media/MediaPlayer$ProvisioningServerErrorException" since="26">
+		<extends name="android/media/MediaDrmException" />
+		<method name="&lt;init>(Ljava/lang/String;)V" />
+	</class>
 	<class name="android/media/MediaPlayer$TrackInfo" since="16">
 		<extends name="java/lang/Object" />
 		<implements name="android/os/Parcelable" />
@@ -21050,6 +22657,7 @@
 		<method name="&lt;init>()V" />
 		<method name="getAudioSourceMax()I" since="4" />
 		<method name="getMaxAmplitude()I" />
+		<method name="getMetrics()Landroid/os/PersistableBundle;" since="26" />
 		<method name="getSurface()Landroid/view/Surface;" since="21" />
 		<method name="pause()V" since="24" />
 		<method name="prepare()V" />
@@ -21069,9 +22677,12 @@
 		<method name="setLocation(FF)V" since="14" />
 		<method name="setMaxDuration(I)V" since="3" />
 		<method name="setMaxFileSize(J)V" since="3" />
+		<method name="setNextOutputFile(Ljava/io/File;)V" since="26" />
+		<method name="setNextOutputFile(Ljava/io/FileDescriptor;)V" since="26" />
 		<method name="setOnErrorListener(Landroid/media/MediaRecorder$OnErrorListener;)V" since="3" />
 		<method name="setOnInfoListener(Landroid/media/MediaRecorder$OnInfoListener;)V" since="3" />
 		<method name="setOrientationHint(I)V" since="9" />
+		<method name="setOutputFile(Ljava/io/File;)V" since="26" />
 		<method name="setOutputFile(Ljava/io/FileDescriptor;)V" since="3" />
 		<method name="setOutputFile(Ljava/lang/String;)V" />
 		<method name="setOutputFormat(I)V" />
@@ -21079,6 +22690,7 @@
 		<method name="setProfile(Landroid/media/CamcorderProfile;)V" since="8" />
 		<method name="setVideoEncoder(I)V" since="3" />
 		<method name="setVideoEncodingBitRate(I)V" since="8" />
+		<method name="setVideoEncodingProfileLevel(II)V" since="26" />
 		<method name="setVideoFrameRate(I)V" since="3" />
 		<method name="setVideoSize(II)V" since="3" />
 		<method name="setVideoSource(I)V" since="3" />
@@ -21087,7 +22699,9 @@
 		<field name="MEDIA_ERROR_SERVER_DIED" since="17" />
 		<field name="MEDIA_RECORDER_ERROR_UNKNOWN" since="3" />
 		<field name="MEDIA_RECORDER_INFO_MAX_DURATION_REACHED" since="3" />
+		<field name="MEDIA_RECORDER_INFO_MAX_FILESIZE_APPROACHING" since="26" />
 		<field name="MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED" since="3" />
+		<field name="MEDIA_RECORDER_INFO_NEXT_OUTPUT_FILE_STARTED" since="26" />
 		<field name="MEDIA_RECORDER_INFO_UNKNOWN" since="3" />
 	</class>
 	<class name="android/media/MediaRecorder$AudioEncoder" since="1">
@@ -21115,6 +22729,26 @@
 		<field name="VOICE_RECOGNITION" since="7" />
 		<field name="VOICE_UPLINK" since="4" />
 	</class>
+	<class name="android/media/MediaRecorder$MetricsConstants" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<field name="AUDIO_BITRATE" />
+		<field name="AUDIO_CHANNELS" />
+		<field name="AUDIO_SAMPLERATE" />
+		<field name="AUDIO_TIMESCALE" />
+		<field name="CAPTURE_FPS" />
+		<field name="CAPTURE_FPS_ENABLE" />
+		<field name="FRAMERATE" />
+		<field name="HEIGHT" />
+		<field name="MOVIE_TIMESCALE" />
+		<field name="ROTATION" />
+		<field name="VIDEO_BITRATE" />
+		<field name="VIDEO_IFRAME_INTERVAL" />
+		<field name="VIDEO_LEVEL" />
+		<field name="VIDEO_PROFILE" />
+		<field name="VIDEO_TIMESCALE" />
+		<field name="WIDTH" />
+	</class>
 	<class name="android/media/MediaRecorder$OnErrorListener" since="3">
 		<extends name="java/lang/Object" />
 		<method name="onError(Landroid/media/MediaRecorder;II)V" />
@@ -21130,6 +22764,7 @@
 		<field name="AMR_NB" since="10" />
 		<field name="AMR_WB" since="10" />
 		<field name="DEFAULT" />
+		<field name="MPEG_2_TS" since="26" />
 		<field name="MPEG_4" />
 		<field name="RAW_AMR" since="3" deprecated="16" />
 		<field name="THREE_GPP" />
@@ -21704,6 +23339,10 @@
 		<extends name="android/media/MediaDrmException" />
 		<method name="&lt;init>(Ljava/lang/String;)V" />
 	</class>
+	<class name="android/media/VolumeAutomation" since="26">
+		<extends name="java/lang/Object" />
+		<method name="createVolumeShaper(Landroid/media/VolumeShaper$Configuration;)Landroid/media/VolumeShaper;" />
+	</class>
 	<class name="android/media/VolumeProvider" since="21">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>(III)V" />
@@ -21717,6 +23356,54 @@
 		<field name="VOLUME_CONTROL_FIXED" />
 		<field name="VOLUME_CONTROL_RELATIVE" />
 	</class>
+	<class name="android/media/VolumeShaper" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/lang/AutoCloseable" />
+		<method name="&lt;init>()V" />
+		<method name="apply(Landroid/media/VolumeShaper$Operation;)V" />
+		<method name="getVolume()F" />
+		<method name="replace(Landroid/media/VolumeShaper$Configuration;Landroid/media/VolumeShaper$Operation;Z)V" />
+	</class>
+	<class name="android/media/VolumeShaper$Configuration" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/os/Parcelable" />
+		<method name="&lt;init>()V" />
+		<method name="getDuration()J" />
+		<method name="getInterpolatorType()I" />
+		<method name="getMaximumCurvePoints()I" />
+		<method name="getTimes()[F" />
+		<method name="getVolumes()[F" />
+		<field name="CREATOR" />
+		<field name="CUBIC_RAMP" />
+		<field name="INTERPOLATOR_TYPE_CUBIC" />
+		<field name="INTERPOLATOR_TYPE_CUBIC_MONOTONIC" />
+		<field name="INTERPOLATOR_TYPE_LINEAR" />
+		<field name="INTERPOLATOR_TYPE_STEP" />
+		<field name="LINEAR_RAMP" />
+		<field name="SCURVE_RAMP" />
+		<field name="SINE_RAMP" />
+	</class>
+	<class name="android/media/VolumeShaper$Configuration$Builder" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="&lt;init>(Landroid/media/VolumeShaper$Configuration;)V" />
+		<method name="build()Landroid/media/VolumeShaper$Configuration;" />
+		<method name="invertVolumes()Landroid/media/VolumeShaper$Configuration$Builder;" />
+		<method name="reflectTimes()Landroid/media/VolumeShaper$Configuration$Builder;" />
+		<method name="scaleToEndVolume(F)Landroid/media/VolumeShaper$Configuration$Builder;" />
+		<method name="scaleToStartVolume(F)Landroid/media/VolumeShaper$Configuration$Builder;" />
+		<method name="setCurve([F[F)Landroid/media/VolumeShaper$Configuration$Builder;" />
+		<method name="setDuration(J)Landroid/media/VolumeShaper$Configuration$Builder;" />
+		<method name="setInterpolatorType(I)Landroid/media/VolumeShaper$Configuration$Builder;" />
+	</class>
+	<class name="android/media/VolumeShaper$Operation" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/os/Parcelable" />
+		<method name="&lt;init>()V" />
+		<field name="CREATOR" />
+		<field name="PLAY" />
+		<field name="REVERSE" />
+	</class>
 	<class name="android/media/audiofx/AcousticEchoCanceler" since="16">
 		<extends name="android/media/audiofx/AudioEffect" />
 		<method name="&lt;init>()V" />
@@ -22354,8 +24041,8 @@
 		<method name="setQueueTitle(Ljava/lang/CharSequence;)V" />
 		<method name="setRatingType(I)V" since="22" />
 		<method name="setSessionActivity(Landroid/app/PendingIntent;)V" />
-		<field name="FLAG_HANDLES_MEDIA_BUTTONS" />
-		<field name="FLAG_HANDLES_TRANSPORT_CONTROLS" />
+		<field name="FLAG_HANDLES_MEDIA_BUTTONS" deprecated="26" />
+		<field name="FLAG_HANDLES_TRANSPORT_CONTROLS" deprecated="26" />
 	</class>
 	<class name="android/media/session/MediaSession$Callback" since="21">
 		<extends name="java/lang/Object" />
@@ -22508,17 +24195,30 @@
 		<method name="buildChannelUriForPassthroughInput(Ljava/lang/String;)Landroid/net/Uri;" />
 		<method name="buildChannelsUriForInput(Ljava/lang/String;)Landroid/net/Uri;" />
 		<method name="buildInputId(Landroid/content/ComponentName;)Ljava/lang/String;" />
+		<method name="buildPreviewProgramUri(J)Landroid/net/Uri;" since="26" />
+		<method name="buildPreviewProgramsUriForChannel(J)Landroid/net/Uri;" since="26" />
+		<method name="buildPreviewProgramsUriForChannel(Landroid/net/Uri;)Landroid/net/Uri;" since="26" />
 		<method name="buildProgramUri(J)Landroid/net/Uri;" />
 		<method name="buildProgramsUriForChannel(J)Landroid/net/Uri;" />
 		<method name="buildProgramsUriForChannel(JJJ)Landroid/net/Uri;" />
 		<method name="buildProgramsUriForChannel(Landroid/net/Uri;)Landroid/net/Uri;" />
 		<method name="buildProgramsUriForChannel(Landroid/net/Uri;JJ)Landroid/net/Uri;" />
 		<method name="buildRecordedProgramUri(J)Landroid/net/Uri;" since="24" />
+		<method name="buildWatchNextProgramUri(J)Landroid/net/Uri;" since="26" />
 		<method name="isChannelUri(Landroid/net/Uri;)Z" since="24" />
 		<method name="isChannelUriForPassthroughInput(Landroid/net/Uri;)Z" since="24" />
 		<method name="isChannelUriForTunerInput(Landroid/net/Uri;)Z" since="24" />
 		<method name="isProgramUri(Landroid/net/Uri;)Z" since="24" />
+		<method name="requestChannelBrowsable(Landroid/content/Context;J)V" since="26" />
+		<field name="ACTION_INITIALIZE_PROGRAMS" since="26" />
+		<field name="ACTION_PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT" since="26" />
+		<field name="ACTION_PREVIEW_PROGRAM_BROWSABLE_DISABLED" since="26" />
+		<field name="ACTION_REQUEST_CHANNEL_BROWSABLE" since="26" />
+		<field name="ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED" since="26" />
 		<field name="AUTHORITY" />
+		<field name="EXTRA_CHANNEL_ID" since="26" />
+		<field name="EXTRA_PREVIEW_PROGRAM_ID" since="26" />
+		<field name="EXTRA_WATCH_NEXT_PROGRAM_ID" since="26" />
 	</class>
 	<class name="android/media/tv/TvContract$BaseTvColumns" since="21">
 		<extends name="java/lang/Object" />
@@ -22535,6 +24235,7 @@
 		<field name="COLUMN_APP_LINK_INTENT_URI" since="23" />
 		<field name="COLUMN_APP_LINK_POSTER_ART_URI" since="23" />
 		<field name="COLUMN_APP_LINK_TEXT" since="23" />
+		<field name="COLUMN_BROWSABLE" since="26" />
 		<field name="COLUMN_DESCRIPTION" />
 		<field name="COLUMN_DISPLAY_NAME" />
 		<field name="COLUMN_DISPLAY_NUMBER" />
@@ -22544,11 +24245,14 @@
 		<field name="COLUMN_INTERNAL_PROVIDER_FLAG2" since="23" />
 		<field name="COLUMN_INTERNAL_PROVIDER_FLAG3" since="23" />
 		<field name="COLUMN_INTERNAL_PROVIDER_FLAG4" since="23" />
+		<field name="COLUMN_INTERNAL_PROVIDER_ID" since="26" />
+		<field name="COLUMN_LOCKED" since="26" />
 		<field name="COLUMN_NETWORK_AFFILIATION" />
 		<field name="COLUMN_ORIGINAL_NETWORK_ID" />
 		<field name="COLUMN_SEARCHABLE" />
 		<field name="COLUMN_SERVICE_ID" />
 		<field name="COLUMN_SERVICE_TYPE" />
+		<field name="COLUMN_TRANSIENT" since="26" />
 		<field name="COLUMN_TRANSPORT_STREAM_ID" />
 		<field name="COLUMN_TYPE" />
 		<field name="COLUMN_VERSION_NUMBER" />
@@ -22580,6 +24284,7 @@
 		<field name="TYPE_NTSC" />
 		<field name="TYPE_OTHER" />
 		<field name="TYPE_PAL" />
+		<field name="TYPE_PREVIEW" since="26" />
 		<field name="TYPE_SECAM" />
 		<field name="TYPE_S_DMB" />
 		<field name="TYPE_T_DMB" />
@@ -22605,6 +24310,90 @@
 		<method name="&lt;init>()V" />
 		<field name="CONTENT_DIRECTORY" />
 	</class>
+	<class name="android/media/tv/TvContract$PreviewPrograms" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/media/tv/TvContract$BaseTvColumns" />
+		<method name="&lt;init>()V" />
+		<field name="ASPECT_RATIO_16_9" />
+		<field name="ASPECT_RATIO_1_1" />
+		<field name="ASPECT_RATIO_2_3" />
+		<field name="ASPECT_RATIO_3_2" />
+		<field name="ASPECT_RATIO_4_3" />
+		<field name="AVAILABILITY_AVAILABLE" />
+		<field name="AVAILABILITY_FREE_WITH_SUBSCRIPTION" />
+		<field name="AVAILABILITY_PAID_CONTENT" />
+		<field name="COLUMN_AUDIO_LANGUAGE" />
+		<field name="COLUMN_AUTHOR" />
+		<field name="COLUMN_AVAILABILITY" />
+		<field name="COLUMN_BROWSABLE" />
+		<field name="COLUMN_CANONICAL_GENRE" />
+		<field name="COLUMN_CHANNEL_ID" />
+		<field name="COLUMN_CONTENT_ID" />
+		<field name="COLUMN_CONTENT_RATING" />
+		<field name="COLUMN_DURATION_MILLIS" />
+		<field name="COLUMN_EPISODE_DISPLAY_NUMBER" />
+		<field name="COLUMN_EPISODE_TITLE" />
+		<field name="COLUMN_INTENT_URI" />
+		<field name="COLUMN_INTERACTION_COUNT" />
+		<field name="COLUMN_INTERACTION_TYPE" />
+		<field name="COLUMN_INTERNAL_PROVIDER_DATA" />
+		<field name="COLUMN_INTERNAL_PROVIDER_FLAG1" />
+		<field name="COLUMN_INTERNAL_PROVIDER_FLAG2" />
+		<field name="COLUMN_INTERNAL_PROVIDER_FLAG3" />
+		<field name="COLUMN_INTERNAL_PROVIDER_FLAG4" />
+		<field name="COLUMN_INTERNAL_PROVIDER_ID" />
+		<field name="COLUMN_ITEM_COUNT" />
+		<field name="COLUMN_LAST_PLAYBACK_POSITION_MILLIS" />
+		<field name="COLUMN_LIVE" />
+		<field name="COLUMN_LOGO_URI" />
+		<field name="COLUMN_LONG_DESCRIPTION" />
+		<field name="COLUMN_OFFER_PRICE" />
+		<field name="COLUMN_POSTER_ART_ASPECT_RATIO" />
+		<field name="COLUMN_POSTER_ART_URI" />
+		<field name="COLUMN_PREVIEW_VIDEO_URI" />
+		<field name="COLUMN_RELEASE_DATE" />
+		<field name="COLUMN_REVIEW_RATING" />
+		<field name="COLUMN_REVIEW_RATING_STYLE" />
+		<field name="COLUMN_SEARCHABLE" />
+		<field name="COLUMN_SEASON_DISPLAY_NUMBER" />
+		<field name="COLUMN_SEASON_TITLE" />
+		<field name="COLUMN_SHORT_DESCRIPTION" />
+		<field name="COLUMN_STARTING_PRICE" />
+		<field name="COLUMN_THUMBNAIL_ASPECT_RATIO" />
+		<field name="COLUMN_THUMBNAIL_URI" />
+		<field name="COLUMN_TITLE" />
+		<field name="COLUMN_TRANSIENT" />
+		<field name="COLUMN_TYPE" />
+		<field name="COLUMN_VERSION_NUMBER" />
+		<field name="COLUMN_VIDEO_HEIGHT" />
+		<field name="COLUMN_VIDEO_WIDTH" />
+		<field name="COLUMN_WEIGHT" />
+		<field name="CONTENT_ITEM_TYPE" />
+		<field name="CONTENT_TYPE" />
+		<field name="CONTENT_URI" />
+		<field name="INTERACTION_TYPE_FANS" />
+		<field name="INTERACTION_TYPE_FOLLOWERS" />
+		<field name="INTERACTION_TYPE_LIKES" />
+		<field name="INTERACTION_TYPE_LISTENS" />
+		<field name="INTERACTION_TYPE_THUMBS" />
+		<field name="INTERACTION_TYPE_VIEWERS" />
+		<field name="INTERACTION_TYPE_VIEWS" />
+		<field name="REVIEW_RATING_STYLE_PERCENTAGE" />
+		<field name="REVIEW_RATING_STYLE_STARS" />
+		<field name="REVIEW_RATING_STYLE_THUMBS_UP_DOWN" />
+		<field name="TYPE_ALBUM" />
+		<field name="TYPE_ARTIST" />
+		<field name="TYPE_CHANNEL" />
+		<field name="TYPE_CLIP" />
+		<field name="TYPE_EVENT" />
+		<field name="TYPE_MOVIE" />
+		<field name="TYPE_PLAYLIST" />
+		<field name="TYPE_STATION" />
+		<field name="TYPE_TRACK" />
+		<field name="TYPE_TV_EPISODE" />
+		<field name="TYPE_TV_SEASON" />
+		<field name="TYPE_TV_SERIES" />
+	</class>
 	<class name="android/media/tv/TvContract$Programs" since="21">
 		<extends name="java/lang/Object" />
 		<implements name="android/media/tv/TvContract$BaseTvColumns" />
@@ -22626,6 +24415,8 @@
 		<field name="COLUMN_LONG_DESCRIPTION" />
 		<field name="COLUMN_POSTER_ART_URI" />
 		<field name="COLUMN_RECORDING_PROHIBITED" since="24" />
+		<field name="COLUMN_REVIEW_RATING" since="26" />
+		<field name="COLUMN_REVIEW_RATING_STYLE" since="26" />
 		<field name="COLUMN_SEARCHABLE" since="23" />
 		<field name="COLUMN_SEASON_DISPLAY_NUMBER" since="24" />
 		<field name="COLUMN_SEASON_NUMBER" deprecated="24" />
@@ -22640,6 +24431,9 @@
 		<field name="CONTENT_ITEM_TYPE" />
 		<field name="CONTENT_TYPE" />
 		<field name="CONTENT_URI" />
+		<field name="REVIEW_RATING_STYLE_PERCENTAGE" since="26" />
+		<field name="REVIEW_RATING_STYLE_STARS" since="26" />
+		<field name="REVIEW_RATING_STYLE_THUMBS_UP_DOWN" since="26" />
 	</class>
 	<class name="android/media/tv/TvContract$Programs$Genres" since="21">
 		<extends name="java/lang/Object" />
@@ -22689,6 +24483,8 @@
 		<field name="COLUMN_RECORDING_DATA_URI" />
 		<field name="COLUMN_RECORDING_DURATION_MILLIS" />
 		<field name="COLUMN_RECORDING_EXPIRE_TIME_UTC_MILLIS" />
+		<field name="COLUMN_REVIEW_RATING" since="26" />
+		<field name="COLUMN_REVIEW_RATING_STYLE" since="26" />
 		<field name="COLUMN_SEARCHABLE" />
 		<field name="COLUMN_SEASON_DISPLAY_NUMBER" />
 		<field name="COLUMN_SEASON_TITLE" />
@@ -22702,13 +24498,104 @@
 		<field name="CONTENT_ITEM_TYPE" />
 		<field name="CONTENT_TYPE" />
 		<field name="CONTENT_URI" />
+		<field name="REVIEW_RATING_STYLE_PERCENTAGE" since="26" />
+		<field name="REVIEW_RATING_STYLE_STARS" since="26" />
+		<field name="REVIEW_RATING_STYLE_THUMBS_UP_DOWN" since="26" />
+	</class>
+	<class name="android/media/tv/TvContract$WatchNextPrograms" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/media/tv/TvContract$BaseTvColumns" />
+		<method name="&lt;init>()V" />
+		<field name="ASPECT_RATIO_16_9" />
+		<field name="ASPECT_RATIO_1_1" />
+		<field name="ASPECT_RATIO_2_3" />
+		<field name="ASPECT_RATIO_3_2" />
+		<field name="ASPECT_RATIO_4_3" />
+		<field name="AVAILABILITY_AVAILABLE" />
+		<field name="AVAILABILITY_FREE_WITH_SUBSCRIPTION" />
+		<field name="AVAILABILITY_PAID_CONTENT" />
+		<field name="COLUMN_AUDIO_LANGUAGE" />
+		<field name="COLUMN_AUTHOR" />
+		<field name="COLUMN_AVAILABILITY" />
+		<field name="COLUMN_BROWSABLE" />
+		<field name="COLUMN_CANONICAL_GENRE" />
+		<field name="COLUMN_CONTENT_ID" />
+		<field name="COLUMN_CONTENT_RATING" />
+		<field name="COLUMN_DURATION_MILLIS" />
+		<field name="COLUMN_EPISODE_DISPLAY_NUMBER" />
+		<field name="COLUMN_EPISODE_TITLE" />
+		<field name="COLUMN_INTENT_URI" />
+		<field name="COLUMN_INTERACTION_COUNT" />
+		<field name="COLUMN_INTERACTION_TYPE" />
+		<field name="COLUMN_INTERNAL_PROVIDER_DATA" />
+		<field name="COLUMN_INTERNAL_PROVIDER_FLAG1" />
+		<field name="COLUMN_INTERNAL_PROVIDER_FLAG2" />
+		<field name="COLUMN_INTERNAL_PROVIDER_FLAG3" />
+		<field name="COLUMN_INTERNAL_PROVIDER_FLAG4" />
+		<field name="COLUMN_INTERNAL_PROVIDER_ID" />
+		<field name="COLUMN_ITEM_COUNT" />
+		<field name="COLUMN_LAST_ENGAGEMENT_TIME_UTC_MILLIS" />
+		<field name="COLUMN_LAST_PLAYBACK_POSITION_MILLIS" />
+		<field name="COLUMN_LIVE" />
+		<field name="COLUMN_LOGO_URI" />
+		<field name="COLUMN_LONG_DESCRIPTION" />
+		<field name="COLUMN_OFFER_PRICE" />
+		<field name="COLUMN_POSTER_ART_ASPECT_RATIO" />
+		<field name="COLUMN_POSTER_ART_URI" />
+		<field name="COLUMN_PREVIEW_VIDEO_URI" />
+		<field name="COLUMN_RELEASE_DATE" />
+		<field name="COLUMN_REVIEW_RATING" />
+		<field name="COLUMN_REVIEW_RATING_STYLE" />
+		<field name="COLUMN_SEARCHABLE" />
+		<field name="COLUMN_SEASON_DISPLAY_NUMBER" />
+		<field name="COLUMN_SEASON_TITLE" />
+		<field name="COLUMN_SHORT_DESCRIPTION" />
+		<field name="COLUMN_STARTING_PRICE" />
+		<field name="COLUMN_THUMBNAIL_ASPECT_RATIO" />
+		<field name="COLUMN_THUMBNAIL_URI" />
+		<field name="COLUMN_TITLE" />
+		<field name="COLUMN_TRANSIENT" />
+		<field name="COLUMN_TYPE" />
+		<field name="COLUMN_VERSION_NUMBER" />
+		<field name="COLUMN_VIDEO_HEIGHT" />
+		<field name="COLUMN_VIDEO_WIDTH" />
+		<field name="COLUMN_WATCH_NEXT_TYPE" />
+		<field name="CONTENT_ITEM_TYPE" />
+		<field name="CONTENT_TYPE" />
+		<field name="CONTENT_URI" />
+		<field name="INTERACTION_TYPE_FANS" />
+		<field name="INTERACTION_TYPE_FOLLOWERS" />
+		<field name="INTERACTION_TYPE_LIKES" />
+		<field name="INTERACTION_TYPE_LISTENS" />
+		<field name="INTERACTION_TYPE_THUMBS" />
+		<field name="INTERACTION_TYPE_VIEWERS" />
+		<field name="INTERACTION_TYPE_VIEWS" />
+		<field name="REVIEW_RATING_STYLE_PERCENTAGE" />
+		<field name="REVIEW_RATING_STYLE_STARS" />
+		<field name="REVIEW_RATING_STYLE_THUMBS_UP_DOWN" />
+		<field name="TYPE_ALBUM" />
+		<field name="TYPE_ARTIST" />
+		<field name="TYPE_CHANNEL" />
+		<field name="TYPE_CLIP" />
+		<field name="TYPE_EVENT" />
+		<field name="TYPE_MOVIE" />
+		<field name="TYPE_PLAYLIST" />
+		<field name="TYPE_STATION" />
+		<field name="TYPE_TRACK" />
+		<field name="TYPE_TV_EPISODE" />
+		<field name="TYPE_TV_SEASON" />
+		<field name="TYPE_TV_SERIES" />
+		<field name="WATCH_NEXT_TYPE_CONTINUE" />
+		<field name="WATCH_NEXT_TYPE_NEW" />
+		<field name="WATCH_NEXT_TYPE_NEXT" />
+		<field name="WATCH_NEXT_TYPE_WATCHLIST" />
 	</class>
 	<class name="android/media/tv/TvInputInfo" since="21">
 		<extends name="java/lang/Object" />
 		<implements name="android/os/Parcelable" />
 		<method name="&lt;init>()V" />
 		<method name="canRecord()Z" since="24" />
-		<method name="createSettingsIntent()Landroid/content/Intent;" />
+		<method name="createSettingsIntent()Landroid/content/Intent;" deprecated="26" />
 		<method name="createSetupIntent()Landroid/content/Intent;" />
 		<method name="getExtras()Landroid/os/Bundle;" since="24" />
 		<method name="getId()Ljava/lang/String;" />
@@ -22757,6 +24644,7 @@
 		<field name="ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED" />
 		<field name="ACTION_QUERY_CONTENT_RATING_SYSTEMS" />
 		<field name="ACTION_SETUP_INPUTS" since="24" />
+		<field name="ACTION_VIEW_RECORDING_SCHEDULES" since="26" />
 		<field name="INPUT_STATE_CONNECTED" />
 		<field name="INPUT_STATE_CONNECTED_STANDBY" />
 		<field name="INPUT_STATE_DISCONNECTED" />
@@ -23217,6 +25105,7 @@
 		<method name="getBoundNetworkForProcess()Landroid/net/Network;" since="23" />
 		<method name="getDefaultProxy()Landroid/net/ProxyInfo;" since="23" />
 		<method name="getLinkProperties(Landroid/net/Network;)Landroid/net/LinkProperties;" since="21" />
+		<method name="getMultipathPreference(Landroid/net/Network;)I" since="26" />
 		<method name="getNetworkCapabilities(Landroid/net/Network;)Landroid/net/NetworkCapabilities;" since="21" />
 		<method name="getNetworkInfo(I)Landroid/net/NetworkInfo;" deprecated="23" />
 		<method name="getNetworkInfo(Landroid/net/Network;)Landroid/net/NetworkInfo;" since="21" />
@@ -23227,8 +25116,10 @@
 		<method name="isDefaultNetworkActive()Z" since="21" />
 		<method name="isNetworkTypeValid(I)Z" deprecated="23" />
 		<method name="registerDefaultNetworkCallback(Landroid/net/ConnectivityManager$NetworkCallback;)V" since="24" />
+		<method name="registerDefaultNetworkCallback(Landroid/net/ConnectivityManager$NetworkCallback;Landroid/os/Handler;)V" since="26" />
 		<method name="registerNetworkCallback(Landroid/net/NetworkRequest;Landroid/app/PendingIntent;)V" since="23" />
 		<method name="registerNetworkCallback(Landroid/net/NetworkRequest;Landroid/net/ConnectivityManager$NetworkCallback;)V" since="21" />
+		<method name="registerNetworkCallback(Landroid/net/NetworkRequest;Landroid/net/ConnectivityManager$NetworkCallback;Landroid/os/Handler;)V" since="26" />
 		<method name="releaseNetworkRequest(Landroid/app/PendingIntent;)V" since="22" />
 		<method name="removeDefaultNetworkActiveListener(Landroid/net/ConnectivityManager$OnNetworkActiveListener;)V" since="21" />
 		<method name="reportBadNetwork(Landroid/net/Network;)V" since="21" deprecated="23" />
@@ -23236,6 +25127,9 @@
 		<method name="requestBandwidthUpdate(Landroid/net/Network;)Z" since="23" />
 		<method name="requestNetwork(Landroid/net/NetworkRequest;Landroid/app/PendingIntent;)V" since="22" />
 		<method name="requestNetwork(Landroid/net/NetworkRequest;Landroid/net/ConnectivityManager$NetworkCallback;)V" since="21" />
+		<method name="requestNetwork(Landroid/net/NetworkRequest;Landroid/net/ConnectivityManager$NetworkCallback;I)V" since="26" />
+		<method name="requestNetwork(Landroid/net/NetworkRequest;Landroid/net/ConnectivityManager$NetworkCallback;Landroid/os/Handler;)V" since="26" />
+		<method name="requestNetwork(Landroid/net/NetworkRequest;Landroid/net/ConnectivityManager$NetworkCallback;Landroid/os/Handler;I)V" since="26" />
 		<method name="requestRouteToHost(II)Z" deprecated="21" />
 		<method name="setNetworkPreference(I)V" deprecated="21" />
 		<method name="setProcessDefaultNetwork(Landroid/net/Network;)Z" since="21" deprecated="23" />
@@ -23259,6 +25153,9 @@
 		<field name="EXTRA_NO_CONNECTIVITY" />
 		<field name="EXTRA_OTHER_NETWORK_INFO" />
 		<field name="EXTRA_REASON" />
+		<field name="MULTIPATH_PREFERENCE_HANDOVER" since="26" />
+		<field name="MULTIPATH_PREFERENCE_PERFORMANCE" since="26" />
+		<field name="MULTIPATH_PREFERENCE_RELIABILITY" since="26" />
 		<field name="RESTRICT_BACKGROUND_STATUS_DISABLED" since="24" />
 		<field name="RESTRICT_BACKGROUND_STATUS_ENABLED" since="24" />
 		<field name="RESTRICT_BACKGROUND_STATUS_WHITELISTED" since="24" />
@@ -23282,6 +25179,7 @@
 		<method name="onLinkPropertiesChanged(Landroid/net/Network;Landroid/net/LinkProperties;)V" />
 		<method name="onLosing(Landroid/net/Network;I)V" />
 		<method name="onLost(Landroid/net/Network;)V" />
+		<method name="onUnavailable()V" since="26" />
 	</class>
 	<class name="android/net/ConnectivityManager$OnNetworkActiveListener" since="21">
 		<extends name="java/lang/Object" />
@@ -23455,6 +25353,7 @@
 		<field name="TRANSPORT_ETHERNET" />
 		<field name="TRANSPORT_VPN" />
 		<field name="TRANSPORT_WIFI" />
+		<field name="TRANSPORT_WIFI_AWARE" since="26" />
 	</class>
 	<class name="android/net/NetworkInfo" since="1">
 		<extends name="java/lang/Object" />
@@ -23519,8 +25418,13 @@
 		<method name="build()Landroid/net/NetworkRequest;" />
 		<method name="removeCapability(I)Landroid/net/NetworkRequest$Builder;" />
 		<method name="removeTransportType(I)Landroid/net/NetworkRequest$Builder;" />
+		<method name="setNetworkSpecifier(Landroid/net/NetworkSpecifier;)Landroid/net/NetworkRequest$Builder;" since="26" />
 		<method name="setNetworkSpecifier(Ljava/lang/String;)Landroid/net/NetworkRequest$Builder;" />
 	</class>
+	<class name="android/net/NetworkSpecifier" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+	</class>
 	<class name="android/net/ParseException" since="1">
 		<extends name="java/lang/RuntimeException" />
 		<method name="&lt;init>()V" />
@@ -23596,6 +25500,7 @@
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
 		<method name="clearThreadStatsTag()V" since="14" />
+		<method name="getAndSetThreadStatsTag(I)I" since="26" />
 		<method name="getMobileRxBytes()J" />
 		<method name="getMobileRxPackets()J" />
 		<method name="getMobileTxBytes()J" />
@@ -24207,7 +26112,9 @@
 		<extends name="java/lang/Object" />
 		<implements name="android/os/Parcelable" />
 		<method name="&lt;init>()V" />
+		<method name="getHttpProxy()Landroid/net/ProxyInfo;" since="26" />
 		<method name="isPasspoint()Z" since="23" />
+		<method name="setHttpProxy(Landroid/net/ProxyInfo;)V" since="26" />
 		<field name="BSSID" />
 		<field name="FQDN" since="21" />
 		<field name="SSID" />
@@ -24218,9 +26125,10 @@
 		<field name="allowedProtocols" />
 		<field name="enterpriseConfig" since="18" />
 		<field name="hiddenSSID" />
+		<field name="isHomeProviderNetwork" since="26" />
 		<field name="networkId" />
 		<field name="preSharedKey" />
-		<field name="priority" />
+		<field name="priority" deprecated="26" />
 		<field name="providerFriendlyName" since="23" />
 		<field name="roamingConsortiumIds" since="23" />
 		<field name="status" />
@@ -24291,6 +26199,7 @@
 		<method name="getCaCertificate()Ljava/security/cert/X509Certificate;" />
 		<method name="getCaCertificates()[Ljava/security/cert/X509Certificate;" since="24" />
 		<method name="getClientCertificate()Ljava/security/cert/X509Certificate;" />
+		<method name="getClientCertificateChain()[Ljava/security/cert/X509Certificate;" since="26" />
 		<method name="getDomainSuffixMatch()Ljava/lang/String;" since="23" />
 		<method name="getEapMethod()I" />
 		<method name="getIdentity()Ljava/lang/String;" />
@@ -24304,6 +26213,7 @@
 		<method name="setCaCertificate(Ljava/security/cert/X509Certificate;)V" />
 		<method name="setCaCertificates([Ljava/security/cert/X509Certificate;)V" since="24" />
 		<method name="setClientKeyEntry(Ljava/security/PrivateKey;Ljava/security/cert/X509Certificate;)V" />
+		<method name="setClientKeyEntryWithCertificateChain(Ljava/security/PrivateKey;[Ljava/security/cert/X509Certificate;)V" since="26" />
 		<method name="setDomainSuffixMatch(Ljava/lang/String;)V" since="23" />
 		<method name="setEapMethod(I)V" />
 		<method name="setIdentity(Ljava/lang/String;)V" />
@@ -24330,11 +26240,14 @@
 	<class name="android/net/wifi/WifiEnterpriseConfig$Phase2" since="18">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
+		<field name="AKA" since="26" />
+		<field name="AKA_PRIME" since="26" />
 		<field name="GTC" />
 		<field name="MSCHAP" />
 		<field name="MSCHAPV2" />
 		<field name="NONE" />
 		<field name="PAP" />
+		<field name="SIM" since="26" />
 	</class>
 	<class name="android/net/wifi/WifiInfo" since="1">
 		<extends name="java/lang/Object" />
@@ -24358,6 +26271,7 @@
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
 		<method name="addNetwork(Landroid/net/wifi/WifiConfiguration;)I" />
+		<method name="addOrUpdatePasspointConfiguration(Landroid/net/wifi/hotspot2/PasspointConfiguration;)V" since="26" />
 		<method name="calculateSignalLevel(II)I" />
 		<method name="cancelWps(Landroid/net/wifi/WifiManager$WpsCallback;)V" since="21" />
 		<method name="compareSignalLevel(II)I" />
@@ -24370,6 +26284,7 @@
 		<method name="getConfiguredNetworks()Ljava/util/List;" />
 		<method name="getConnectionInfo()Landroid/net/wifi/WifiInfo;" />
 		<method name="getDhcpInfo()Landroid/net/DhcpInfo;" deprecated="18" />
+		<method name="getPasspointConfigurations()Ljava/util/List;" since="26" />
 		<method name="getScanResults()Ljava/util/List;" />
 		<method name="getWifiState()I" />
 		<method name="is5GHzBandSupported()Z" since="21" />
@@ -24380,14 +26295,16 @@
 		<method name="isScanAlwaysAvailable()Z" since="18" />
 		<method name="isTdlsSupported()Z" since="21" />
 		<method name="isWifiEnabled()Z" />
-		<method name="pingSupplicant()Z" />
+		<method name="pingSupplicant()Z" deprecated="26" />
 		<method name="reassociate()Z" />
 		<method name="reconnect()Z" />
 		<method name="removeNetwork(I)Z" />
-		<method name="saveConfiguration()Z" />
+		<method name="removePasspointConfiguration(Ljava/lang/String;)V" since="26" />
+		<method name="saveConfiguration()Z" deprecated="26" />
 		<method name="setTdlsEnabled(Ljava/net/InetAddress;Z)V" since="19" />
 		<method name="setTdlsEnabledWithMacAddress(Ljava/lang/String;Z)V" since="19" />
 		<method name="setWifiEnabled(Z)Z" />
+		<method name="startLocalOnlyHotspot(Landroid/net/wifi/WifiManager$LocalOnlyHotspotCallback;Landroid/os/Handler;)V" since="26" />
 		<method name="startScan()Z" />
 		<method name="startWps(Landroid/net/wifi/WpsInfo;Landroid/net/wifi/WifiManager$WpsCallback;)V" since="21" />
 		<method name="updateNetwork(Landroid/net/wifi/WifiConfiguration;)I" />
@@ -24425,6 +26342,23 @@
 		<field name="WPS_TKIP_ONLY_PROHIBITED" since="21" />
 		<field name="WPS_WEP_PROHIBITED" since="21" />
 	</class>
+	<class name="android/net/wifi/WifiManager$LocalOnlyHotspotCallback" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="onFailed(I)V" />
+		<method name="onStarted(Landroid/net/wifi/WifiManager$LocalOnlyHotspotReservation;)V" />
+		<method name="onStopped()V" />
+		<field name="ERROR_GENERIC" />
+		<field name="ERROR_INCOMPATIBLE_MODE" />
+		<field name="ERROR_NO_CHANNEL" />
+		<field name="ERROR_TETHERING_DISALLOWED" />
+	</class>
+	<class name="android/net/wifi/WifiManager$LocalOnlyHotspotReservation" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/lang/AutoCloseable" />
+		<method name="&lt;init>(Landroid/net/wifi/WifiManager;)V" />
+		<method name="getWifiConfiguration()Landroid/net/wifi/WifiConfiguration;" />
+	</class>
 	<class name="android/net/wifi/WifiManager$MulticastLock" since="4">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>(Landroid/net/wifi/WifiManager;)V" />
@@ -24464,6 +26398,211 @@
 		<field name="pin" />
 		<field name="setup" />
 	</class>
+	<class name="android/net/wifi/aware/AttachCallback" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="onAttachFailed()V" />
+		<method name="onAttached(Landroid/net/wifi/aware/WifiAwareSession;)V" />
+	</class>
+	<class name="android/net/wifi/aware/Characteristics" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/os/Parcelable" />
+		<method name="&lt;init>()V" />
+		<method name="getMaxMatchFilterLength()I" />
+		<method name="getMaxServiceNameLength()I" />
+		<method name="getMaxServiceSpecificInfoLength()I" />
+		<field name="CREATOR" />
+	</class>
+	<class name="android/net/wifi/aware/DiscoverySession" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/lang/AutoCloseable" />
+		<method name="&lt;init>()V" />
+		<method name="createNetworkSpecifierOpen(Landroid/net/wifi/aware/PeerHandle;)Landroid/net/NetworkSpecifier;" />
+		<method name="createNetworkSpecifierPassphrase(Landroid/net/wifi/aware/PeerHandle;Ljava/lang/String;)Landroid/net/NetworkSpecifier;" />
+		<method name="sendMessage(Landroid/net/wifi/aware/PeerHandle;I[B)V" />
+	</class>
+	<class name="android/net/wifi/aware/DiscoverySessionCallback" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="onMessageReceived(Landroid/net/wifi/aware/PeerHandle;[B)V" />
+		<method name="onMessageSendFailed(I)V" />
+		<method name="onMessageSendSucceeded(I)V" />
+		<method name="onPublishStarted(Landroid/net/wifi/aware/PublishDiscoverySession;)V" />
+		<method name="onServiceDiscovered(Landroid/net/wifi/aware/PeerHandle;[BLjava/util/List;)V" />
+		<method name="onSessionConfigFailed()V" />
+		<method name="onSessionConfigUpdated()V" />
+		<method name="onSessionTerminated()V" />
+		<method name="onSubscribeStarted(Landroid/net/wifi/aware/SubscribeDiscoverySession;)V" />
+	</class>
+	<class name="android/net/wifi/aware/IdentityChangedListener" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="onIdentityChanged([B)V" />
+	</class>
+	<class name="android/net/wifi/aware/PeerHandle" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+	</class>
+	<class name="android/net/wifi/aware/PublishConfig" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/os/Parcelable" />
+		<method name="&lt;init>()V" />
+		<field name="CREATOR" />
+		<field name="PUBLISH_TYPE_SOLICITED" />
+		<field name="PUBLISH_TYPE_UNSOLICITED" />
+	</class>
+	<class name="android/net/wifi/aware/PublishConfig$Builder" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="build()Landroid/net/wifi/aware/PublishConfig;" />
+		<method name="setMatchFilter(Ljava/util/List;)Landroid/net/wifi/aware/PublishConfig$Builder;" />
+		<method name="setPublishType(I)Landroid/net/wifi/aware/PublishConfig$Builder;" />
+		<method name="setServiceName(Ljava/lang/String;)Landroid/net/wifi/aware/PublishConfig$Builder;" />
+		<method name="setServiceSpecificInfo([B)Landroid/net/wifi/aware/PublishConfig$Builder;" />
+		<method name="setTerminateNotificationEnabled(Z)Landroid/net/wifi/aware/PublishConfig$Builder;" />
+		<method name="setTtlSec(I)Landroid/net/wifi/aware/PublishConfig$Builder;" />
+	</class>
+	<class name="android/net/wifi/aware/PublishDiscoverySession" since="26">
+		<extends name="android/net/wifi/aware/DiscoverySession" />
+		<method name="&lt;init>()V" />
+		<method name="updatePublish(Landroid/net/wifi/aware/PublishConfig;)V" />
+	</class>
+	<class name="android/net/wifi/aware/SubscribeConfig" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/os/Parcelable" />
+		<method name="&lt;init>()V" />
+		<field name="CREATOR" />
+		<field name="SUBSCRIBE_TYPE_ACTIVE" />
+		<field name="SUBSCRIBE_TYPE_PASSIVE" />
+	</class>
+	<class name="android/net/wifi/aware/SubscribeConfig$Builder" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="build()Landroid/net/wifi/aware/SubscribeConfig;" />
+		<method name="setMatchFilter(Ljava/util/List;)Landroid/net/wifi/aware/SubscribeConfig$Builder;" />
+		<method name="setServiceName(Ljava/lang/String;)Landroid/net/wifi/aware/SubscribeConfig$Builder;" />
+		<method name="setServiceSpecificInfo([B)Landroid/net/wifi/aware/SubscribeConfig$Builder;" />
+		<method name="setSubscribeType(I)Landroid/net/wifi/aware/SubscribeConfig$Builder;" />
+		<method name="setTerminateNotificationEnabled(Z)Landroid/net/wifi/aware/SubscribeConfig$Builder;" />
+		<method name="setTtlSec(I)Landroid/net/wifi/aware/SubscribeConfig$Builder;" />
+	</class>
+	<class name="android/net/wifi/aware/SubscribeDiscoverySession" since="26">
+		<extends name="android/net/wifi/aware/DiscoverySession" />
+		<method name="&lt;init>()V" />
+		<method name="updateSubscribe(Landroid/net/wifi/aware/SubscribeConfig;)V" />
+	</class>
+	<class name="android/net/wifi/aware/WifiAwareManager" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="attach(Landroid/net/wifi/aware/AttachCallback;Landroid/net/wifi/aware/IdentityChangedListener;Landroid/os/Handler;)V" />
+		<method name="attach(Landroid/net/wifi/aware/AttachCallback;Landroid/os/Handler;)V" />
+		<method name="getCharacteristics()Landroid/net/wifi/aware/Characteristics;" />
+		<method name="isAvailable()Z" />
+		<field name="ACTION_WIFI_AWARE_STATE_CHANGED" />
+		<field name="WIFI_AWARE_DATA_PATH_ROLE_INITIATOR" />
+		<field name="WIFI_AWARE_DATA_PATH_ROLE_RESPONDER" />
+	</class>
+	<class name="android/net/wifi/aware/WifiAwareSession" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/lang/AutoCloseable" />
+		<method name="&lt;init>()V" />
+		<method name="createNetworkSpecifierOpen(I[B)Landroid/net/NetworkSpecifier;" />
+		<method name="createNetworkSpecifierPassphrase(I[BLjava/lang/String;)Landroid/net/NetworkSpecifier;" />
+		<method name="publish(Landroid/net/wifi/aware/PublishConfig;Landroid/net/wifi/aware/DiscoverySessionCallback;Landroid/os/Handler;)V" />
+		<method name="subscribe(Landroid/net/wifi/aware/SubscribeConfig;Landroid/net/wifi/aware/DiscoverySessionCallback;Landroid/os/Handler;)V" />
+	</class>
+	<class name="android/net/wifi/hotspot2/ConfigParser" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="parsePasspointConfig(Ljava/lang/String;[B)Landroid/net/wifi/hotspot2/PasspointConfiguration;" />
+	</class>
+	<class name="android/net/wifi/hotspot2/PasspointConfiguration" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/os/Parcelable" />
+		<method name="&lt;init>()V" />
+		<method name="&lt;init>(Landroid/net/wifi/hotspot2/PasspointConfiguration;)V" />
+		<method name="getCredential()Landroid/net/wifi/hotspot2/pps/Credential;" />
+		<method name="getHomeSp()Landroid/net/wifi/hotspot2/pps/HomeSp;" />
+		<method name="setCredential(Landroid/net/wifi/hotspot2/pps/Credential;)V" />
+		<method name="setHomeSp(Landroid/net/wifi/hotspot2/pps/HomeSp;)V" />
+		<field name="CREATOR" />
+	</class>
+	<class name="android/net/wifi/hotspot2/omadm/PpsMoParser" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="parseMoText(Ljava/lang/String;)Landroid/net/wifi/hotspot2/PasspointConfiguration;" />
+	</class>
+	<class name="android/net/wifi/hotspot2/pps/Credential" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/os/Parcelable" />
+		<method name="&lt;init>()V" />
+		<method name="&lt;init>(Landroid/net/wifi/hotspot2/pps/Credential;)V" />
+		<method name="getCaCertificate()Ljava/security/cert/X509Certificate;" />
+		<method name="getCertCredential()Landroid/net/wifi/hotspot2/pps/Credential$CertificateCredential;" />
+		<method name="getClientCertificateChain()[Ljava/security/cert/X509Certificate;" />
+		<method name="getClientPrivateKey()Ljava/security/PrivateKey;" />
+		<method name="getRealm()Ljava/lang/String;" />
+		<method name="getSimCredential()Landroid/net/wifi/hotspot2/pps/Credential$SimCredential;" />
+		<method name="getUserCredential()Landroid/net/wifi/hotspot2/pps/Credential$UserCredential;" />
+		<method name="setCaCertificate(Ljava/security/cert/X509Certificate;)V" />
+		<method name="setCertCredential(Landroid/net/wifi/hotspot2/pps/Credential$CertificateCredential;)V" />
+		<method name="setClientCertificateChain([Ljava/security/cert/X509Certificate;)V" />
+		<method name="setClientPrivateKey(Ljava/security/PrivateKey;)V" />
+		<method name="setRealm(Ljava/lang/String;)V" />
+		<method name="setSimCredential(Landroid/net/wifi/hotspot2/pps/Credential$SimCredential;)V" />
+		<method name="setUserCredential(Landroid/net/wifi/hotspot2/pps/Credential$UserCredential;)V" />
+		<field name="CREATOR" />
+	</class>
+	<class name="android/net/wifi/hotspot2/pps/Credential$CertificateCredential" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/os/Parcelable" />
+		<method name="&lt;init>()V" />
+		<method name="&lt;init>(Landroid/net/wifi/hotspot2/pps/Credential$CertificateCredential;)V" />
+		<method name="getCertSha256Fingerprint()[B" />
+		<method name="getCertType()Ljava/lang/String;" />
+		<method name="setCertSha256Fingerprint([B)V" />
+		<method name="setCertType(Ljava/lang/String;)V" />
+		<field name="CREATOR" />
+	</class>
+	<class name="android/net/wifi/hotspot2/pps/Credential$SimCredential" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/os/Parcelable" />
+		<method name="&lt;init>()V" />
+		<method name="&lt;init>(Landroid/net/wifi/hotspot2/pps/Credential$SimCredential;)V" />
+		<method name="getEapType()I" />
+		<method name="getImsi()Ljava/lang/String;" />
+		<method name="setEapType(I)V" />
+		<method name="setImsi(Ljava/lang/String;)V" />
+		<field name="CREATOR" />
+	</class>
+	<class name="android/net/wifi/hotspot2/pps/Credential$UserCredential" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/os/Parcelable" />
+		<method name="&lt;init>()V" />
+		<method name="&lt;init>(Landroid/net/wifi/hotspot2/pps/Credential$UserCredential;)V" />
+		<method name="getEapType()I" />
+		<method name="getNonEapInnerMethod()Ljava/lang/String;" />
+		<method name="getPassword()Ljava/lang/String;" />
+		<method name="getUsername()Ljava/lang/String;" />
+		<method name="setEapType(I)V" />
+		<method name="setNonEapInnerMethod(Ljava/lang/String;)V" />
+		<method name="setPassword(Ljava/lang/String;)V" />
+		<method name="setUsername(Ljava/lang/String;)V" />
+		<field name="CREATOR" />
+	</class>
+	<class name="android/net/wifi/hotspot2/pps/HomeSp" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/os/Parcelable" />
+		<method name="&lt;init>()V" />
+		<method name="&lt;init>(Landroid/net/wifi/hotspot2/pps/HomeSp;)V" />
+		<method name="getFqdn()Ljava/lang/String;" />
+		<method name="getFriendlyName()Ljava/lang/String;" />
+		<method name="getRoamingConsortiumOis()[J" />
+		<method name="setFqdn(Ljava/lang/String;)V" />
+		<method name="setFriendlyName(Ljava/lang/String;)V" />
+		<method name="setRoamingConsortiumOis([J)V" />
+		<field name="CREATOR" />
+	</class>
 	<class name="android/net/wifi/p2p/WifiP2pConfig" since="14">
 		<extends name="java/lang/Object" />
 		<implements name="android/os/Parcelable" />
@@ -25012,7 +27151,7 @@
 		<method name="eglCreateContext(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLConfig;Landroid/opengl/EGLContext;[II)Landroid/opengl/EGLContext;" />
 		<method name="eglCreatePbufferFromClientBuffer(Landroid/opengl/EGLDisplay;IILandroid/opengl/EGLConfig;[II)Landroid/opengl/EGLSurface;" />
 		<method name="eglCreatePbufferSurface(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLConfig;[II)Landroid/opengl/EGLSurface;" />
-		<method name="eglCreatePixmapSurface(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLConfig;I[II)Landroid/opengl/EGLSurface;" />
+		<method name="eglCreatePixmapSurface(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLConfig;I[II)Landroid/opengl/EGLSurface;" deprecated="26" />
 		<method name="eglCreateWindowSurface(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLConfig;Ljava/lang/Object;[II)Landroid/opengl/EGLSurface;" />
 		<method name="eglDestroyContext(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLContext;)Z" />
 		<method name="eglDestroySurface(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLSurface;)Z" />
@@ -25173,6 +27312,7 @@
 		<field name="EGL_CONTEXT_MAJOR_VERSION_KHR" />
 		<field name="EGL_CONTEXT_MINOR_VERSION_KHR" />
 		<field name="EGL_OPENGL_ES3_BIT_KHR" />
+		<field name="EGL_RECORDABLE_ANDROID" since="26" />
 	</class>
 	<class name="android/opengl/EGLObjectHandle" since="17">
 		<extends name="java/lang/Object" />
@@ -28005,6 +30145,7 @@
 		<field name="BATTERY_PROPERTY_CURRENT_AVERAGE" since="21" />
 		<field name="BATTERY_PROPERTY_CURRENT_NOW" since="21" />
 		<field name="BATTERY_PROPERTY_ENERGY_COUNTER" since="21" />
+		<field name="BATTERY_PROPERTY_STATUS" since="26" />
 		<field name="BATTERY_STATUS_CHARGING" />
 		<field name="BATTERY_STATUS_DISCHARGING" />
 		<field name="BATTERY_STATUS_FULL" />
@@ -28040,6 +30181,7 @@
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
 		<method name="getRadioVersion()Ljava/lang/String;" since="14" />
+		<method name="getSerial()Ljava/lang/String;" since="26" />
 		<field name="BOARD" />
 		<field name="BOOTLOADER" since="8" />
 		<field name="BRAND" />
@@ -28055,7 +30197,7 @@
 		<field name="MODEL" />
 		<field name="PRODUCT" />
 		<field name="RADIO" since="8" deprecated="16" />
-		<field name="SERIAL" since="9" />
+		<field name="SERIAL" since="9" deprecated="26" />
 		<field name="SUPPORTED_32_BIT_ABIS" since="21" />
 		<field name="SUPPORTED_64_BIT_ABIS" since="21" />
 		<field name="SUPPORTED_ABIS" since="21" />
@@ -28107,6 +30249,7 @@
 		<field name="M" since="23" />
 		<field name="N" since="24" />
 		<field name="N_MR1" since="25" />
+		<field name="O" since="26" />
 	</class>
 	<class name="android/os/Bundle" since="1">
 		<extends name="android/os/BaseBundle" since="21" />
@@ -28120,6 +30263,7 @@
 		<method name="&lt;init>(Ljava/lang/ClassLoader;)V" />
 		<method name="clear()V" />
 		<method name="containsKey(Ljava/lang/String;)Z" />
+		<method name="deepCopy()Landroid/os/Bundle;" since="26" />
 		<method name="get(Ljava/lang/String;)Ljava/lang/Object;" />
 		<method name="getBinder(Ljava/lang/String;)Landroid/os/IBinder;" since="18" />
 		<method name="getBoolean(Ljava/lang/String;)Z" />
@@ -28875,6 +31019,7 @@
 		<method name="getType()I" />
 		<method name="match(Ljava/lang/String;)Z" />
 		<field name="CREATOR" />
+		<field name="PATTERN_ADVANCED_GLOB" since="26" />
 		<field name="PATTERN_LITERAL" />
 		<field name="PATTERN_PREFIX" />
 		<field name="PATTERN_SIMPLE_GLOB" />
@@ -28886,6 +31031,7 @@
 		<method name="&lt;init>()V" />
 		<method name="&lt;init>(I)V" />
 		<method name="&lt;init>(Landroid/os/PersistableBundle;)V" />
+		<method name="deepCopy()Landroid/os/PersistableBundle;" since="26" />
 		<method name="getPersistableBundle(Ljava/lang/String;)Landroid/os/PersistableBundle;" />
 		<method name="putPersistableBundle(Ljava/lang/String;Landroid/os/PersistableBundle;)V" />
 		<field name="CREATOR" />
@@ -28968,6 +31114,15 @@
 		<field name="THREAD_PRIORITY_URGENT_AUDIO" />
 		<field name="THREAD_PRIORITY_URGENT_DISPLAY" />
 	</class>
+	<class name="android/os/ProxyFileDescriptorCallback" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="onFsync()V" />
+		<method name="onGetSize()J" />
+		<method name="onRead(JI[B)I" />
+		<method name="onRelease()V" />
+		<method name="onWrite(JI[B)I" />
+	</class>
 	<class name="android/os/RecoverySystem" since="8">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
@@ -28987,7 +31142,9 @@
 		<method name="finishBroadcast()V" />
 		<method name="getBroadcastCookie(I)Ljava/lang/Object;" since="4" />
 		<method name="getBroadcastItem(I)Landroid/os/IInterface;" />
+		<method name="getRegisteredCallbackCookie(I)Ljava/lang/Object;" since="26" />
 		<method name="getRegisteredCallbackCount()I" since="17" />
+		<method name="getRegisteredCallbackItem(I)Landroid/os/IInterface;" since="26" />
 		<method name="kill()V" />
 		<method name="onCallbackDied(Landroid/os/IInterface;)V" />
 		<method name="onCallbackDied(Landroid/os/IInterface;Ljava/lang/Object;)V" since="4" />
@@ -29052,6 +31209,7 @@
 		<method name="detectDiskWrites()Landroid/os/StrictMode$ThreadPolicy$Builder;" />
 		<method name="detectNetwork()Landroid/os/StrictMode$ThreadPolicy$Builder;" />
 		<method name="detectResourceMismatches()Landroid/os/StrictMode$ThreadPolicy$Builder;" since="23" />
+		<method name="detectUnbufferedIo()Landroid/os/StrictMode$ThreadPolicy$Builder;" since="26" />
 		<method name="penaltyDeath()Landroid/os/StrictMode$ThreadPolicy$Builder;" />
 		<method name="penaltyDeathOnNetwork()Landroid/os/StrictMode$ThreadPolicy$Builder;" since="11" />
 		<method name="penaltyDialog()Landroid/os/StrictMode$ThreadPolicy$Builder;" />
@@ -29064,6 +31222,7 @@
 		<method name="permitDiskWrites()Landroid/os/StrictMode$ThreadPolicy$Builder;" />
 		<method name="permitNetwork()Landroid/os/StrictMode$ThreadPolicy$Builder;" />
 		<method name="permitResourceMismatches()Landroid/os/StrictMode$ThreadPolicy$Builder;" since="23" />
+		<method name="permitUnbufferedIo()Landroid/os/StrictMode$ThreadPolicy$Builder;" since="26" />
 	</class>
 	<class name="android/os/StrictMode$VmPolicy" since="9">
 		<extends name="java/lang/Object" />
@@ -29078,10 +31237,12 @@
 		<method name="detectActivityLeaks()Landroid/os/StrictMode$VmPolicy$Builder;" since="11" />
 		<method name="detectAll()Landroid/os/StrictMode$VmPolicy$Builder;" />
 		<method name="detectCleartextNetwork()Landroid/os/StrictMode$VmPolicy$Builder;" since="23" />
+		<method name="detectContentUriWithoutPermission()Landroid/os/StrictMode$VmPolicy$Builder;" since="26" />
 		<method name="detectFileUriExposure()Landroid/os/StrictMode$VmPolicy$Builder;" since="18" />
 		<method name="detectLeakedClosableObjects()Landroid/os/StrictMode$VmPolicy$Builder;" since="11" />
 		<method name="detectLeakedRegistrationObjects()Landroid/os/StrictMode$VmPolicy$Builder;" since="16" />
 		<method name="detectLeakedSqlLiteObjects()Landroid/os/StrictMode$VmPolicy$Builder;" />
+		<method name="detectUntaggedSockets()Landroid/os/StrictMode$VmPolicy$Builder;" since="26" />
 		<method name="penaltyDeath()Landroid/os/StrictMode$VmPolicy$Builder;" />
 		<method name="penaltyDeathOnCleartextNetwork()Landroid/os/StrictMode$VmPolicy$Builder;" since="23" />
 		<method name="penaltyDeathOnFileUriExposure()Landroid/os/StrictMode$VmPolicy$Builder;" since="24" />
@@ -29099,6 +31260,17 @@
 		<method name="sleep(J)V" />
 		<method name="uptimeMillis()J" />
 	</class>
+	<class name="android/os/TestLooperManager" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="execute(Landroid/os/Message;)V" />
+		<method name="getMessageQueue()Landroid/os/MessageQueue;" />
+		<method name="hasMessages(Landroid/os/Handler;Ljava/lang/Object;I)Z" />
+		<method name="hasMessages(Landroid/os/Handler;Ljava/lang/Object;Ljava/lang/Runnable;)Z" />
+		<method name="next()Landroid/os/Message;" />
+		<method name="recycle(Landroid/os/Message;)V" />
+		<method name="release()V" />
+	</class>
 	<class name="android/os/TokenWatcher" since="1">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>(Landroid/os/Handler;Ljava/lang/String;)V" />
@@ -29159,9 +31331,13 @@
 		<method name="setUserRestrictions(Landroid/os/Bundle;Landroid/os/UserHandle;)V" since="18" deprecated="21" />
 		<method name="supportsMultipleUsers()Z" since="24" />
 		<field name="ALLOW_PARENT_PROFILE_APP_LINKING" since="23" />
+		<field name="DISALLOW_ADD_MANAGED_PROFILE" since="26" />
 		<field name="DISALLOW_ADD_USER" since="21" />
 		<field name="DISALLOW_ADJUST_VOLUME" since="21" />
 		<field name="DISALLOW_APPS_CONTROL" since="21" />
+		<field name="DISALLOW_AUTOFILL" since="26" />
+		<field name="DISALLOW_BLUETOOTH" since="26" />
+		<field name="DISALLOW_BLUETOOTH_SHARING" since="26" />
 		<field name="DISALLOW_CONFIG_BLUETOOTH" since="18" />
 		<field name="DISALLOW_CONFIG_CELL_BROADCASTS" since="21" />
 		<field name="DISALLOW_CONFIG_CREDENTIALS" since="18" />
@@ -29182,6 +31358,7 @@
 		<field name="DISALLOW_NETWORK_RESET" since="23" />
 		<field name="DISALLOW_OUTGOING_BEAM" since="22" />
 		<field name="DISALLOW_OUTGOING_CALLS" since="21" />
+		<field name="DISALLOW_REMOVE_MANAGED_PROFILE" since="26" />
 		<field name="DISALLOW_REMOVE_USER" since="18" />
 		<field name="DISALLOW_SAFE_BOOT" since="23" />
 		<field name="DISALLOW_SET_USER_ICON" since="24" />
@@ -29196,15 +31373,28 @@
 		<field name="USER_CREATION_FAILED_NOT_PERMITTED" since="24" />
 		<field name="USER_CREATION_FAILED_NO_MORE_USERS" since="24" />
 	</class>
+	<class name="android/os/VibrationEffect" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/os/Parcelable" />
+		<method name="&lt;init>()V" />
+		<method name="createOneShot(JI)Landroid/os/VibrationEffect;" />
+		<method name="createWaveform([JI)Landroid/os/VibrationEffect;" />
+		<method name="createWaveform([J[II)Landroid/os/VibrationEffect;" />
+		<field name="CREATOR" />
+		<field name="DEFAULT_AMPLITUDE" />
+	</class>
 	<class name="android/os/Vibrator" since="1">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
 		<method name="cancel()V" />
+		<method name="hasAmplitudeControl()Z" since="26" />
 		<method name="hasVibrator()Z" since="11" />
-		<method name="vibrate(J)V" />
-		<method name="vibrate(JLandroid/media/AudioAttributes;)V" since="21" />
-		<method name="vibrate([JI)V" />
-		<method name="vibrate([JILandroid/media/AudioAttributes;)V" since="21" />
+		<method name="vibrate(J)V" deprecated="26" />
+		<method name="vibrate(JLandroid/media/AudioAttributes;)V" since="21" deprecated="26" />
+		<method name="vibrate(Landroid/os/VibrationEffect;)V" since="26" />
+		<method name="vibrate(Landroid/os/VibrationEffect;Landroid/media/AudioAttributes;)V" since="26" />
+		<method name="vibrate([JI)V" deprecated="26" />
+		<method name="vibrate([JILandroid/media/AudioAttributes;)V" since="21" deprecated="26" />
 	</class>
 	<class name="android/os/WorkSource" since="9">
 		<extends name="java/lang/Object" />
@@ -29305,7 +31495,7 @@
 		<field name="MEASUREMENT_BLUETOOTH_TX_MS" />
 		<field name="MEASUREMENT_BLUETOOTH_TX_PACKETS" />
 		<field name="MEASUREMENT_BUTTON_USER_ACTIVITY_COUNT" />
-		<field name="MEASUREMENT_CPU_POWER_MAMS" />
+		<field name="MEASUREMENT_CPU_POWER_MAMS" deprecated="26" />
 		<field name="MEASUREMENT_MOBILE_IDLE_MS" />
 		<field name="MEASUREMENT_MOBILE_POWER_MAMS" />
 		<field name="MEASUREMENT_MOBILE_RX_BYTES" />
@@ -29376,15 +31566,29 @@
 	<class name="android/os/storage/StorageManager" since="9">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
+		<method name="allocateBytes(Ljava/io/FileDescriptor;J)V" since="26" />
+		<method name="allocateBytes(Ljava/util/UUID;J)V" since="26" />
+		<method name="getAllocatableBytes(Ljava/util/UUID;)J" since="26" />
+		<method name="getCacheQuotaBytes(Ljava/util/UUID;)J" since="26" />
+		<method name="getCacheSizeBytes(Ljava/util/UUID;)J" since="26" />
 		<method name="getMountedObbPath(Ljava/lang/String;)Ljava/lang/String;" />
 		<method name="getPrimaryStorageVolume()Landroid/os/storage/StorageVolume;" since="24" />
 		<method name="getStorageVolume(Ljava/io/File;)Landroid/os/storage/StorageVolume;" since="24" />
 		<method name="getStorageVolumes()Ljava/util/List;" since="24" />
+		<method name="getUuidForPath(Ljava/io/File;)Ljava/util/UUID;" since="26" />
+		<method name="isCacheBehaviorGroup(Ljava/io/File;)Z" since="26" />
+		<method name="isCacheBehaviorTombstone(Ljava/io/File;)Z" since="26" />
 		<method name="isEncrypted(Ljava/io/File;)Z" since="24" />
 		<method name="isObbMounted(Ljava/lang/String;)Z" />
 		<method name="mountObb(Ljava/lang/String;Ljava/lang/String;Landroid/os/storage/OnObbStateChangeListener;)Z" />
+		<method name="openProxyFileDescriptor(ILandroid/os/ProxyFileDescriptorCallback;Landroid/os/Handler;)Landroid/os/ParcelFileDescriptor;" since="26" />
+		<method name="setCacheBehaviorGroup(Ljava/io/File;Z)V" since="26" />
+		<method name="setCacheBehaviorTombstone(Ljava/io/File;Z)V" since="26" />
 		<method name="unmountObb(Ljava/lang/String;ZLandroid/os/storage/OnObbStateChangeListener;)Z" />
 		<field name="ACTION_MANAGE_STORAGE" since="25" />
+		<field name="EXTRA_REQUESTED_BYTES" since="26" />
+		<field name="EXTRA_UUID" since="26" />
+		<field name="UUID_DEFAULT" since="26" />
 	</class>
 	<class name="android/os/storage/StorageVolume" since="24">
 		<extends name="java/lang/Object" />
@@ -29518,12 +31722,14 @@
 		<method name="getOnPreferenceChangeListener()Landroid/preference/Preference$OnPreferenceChangeListener;" />
 		<method name="getOnPreferenceClickListener()Landroid/preference/Preference$OnPreferenceClickListener;" />
 		<method name="getOrder()I" />
+		<method name="getParent()Landroid/preference/PreferenceGroup;" since="26" />
 		<method name="getPersistedBoolean(Z)Z" />
 		<method name="getPersistedFloat(F)F" />
 		<method name="getPersistedInt(I)I" />
 		<method name="getPersistedLong(J)J" />
 		<method name="getPersistedString(Ljava/lang/String;)Ljava/lang/String;" />
 		<method name="getPersistedStringSet(Ljava/util/Set;)Ljava/util/Set;" since="24" />
+		<method name="getPreferenceDataStore()Landroid/preference/PreferenceDataStore;" since="26" />
 		<method name="getPreferenceManager()Landroid/preference/PreferenceManager;" />
 		<method name="getSharedPreferences()Landroid/content/SharedPreferences;" />
 		<method name="getShouldDisableView()Z" />
@@ -29534,8 +31740,11 @@
 		<method name="getWidgetLayoutResource()I" />
 		<method name="hasKey()Z" />
 		<method name="isEnabled()Z" />
+		<method name="isIconSpaceReserved()Z" since="26" />
 		<method name="isPersistent()Z" />
+		<method name="isRecycleEnabled()Z" since="26" />
 		<method name="isSelectable()Z" />
+		<method name="isSingleLineTitle()Z" since="26" />
 		<method name="notifyChanged()V" />
 		<method name="notifyDependencyChange(Z)V" />
 		<method name="notifyHierarchyChanged()V" />
@@ -29566,6 +31775,7 @@
 		<method name="setFragment(Ljava/lang/String;)V" since="11" />
 		<method name="setIcon(I)V" since="11" />
 		<method name="setIcon(Landroid/graphics/drawable/Drawable;)V" since="11" />
+		<method name="setIconSpaceReserved(Z)V" since="26" />
 		<method name="setIntent(Landroid/content/Intent;)V" />
 		<method name="setKey(Ljava/lang/String;)V" />
 		<method name="setLayoutResource(I)V" />
@@ -29573,8 +31783,11 @@
 		<method name="setOnPreferenceClickListener(Landroid/preference/Preference$OnPreferenceClickListener;)V" />
 		<method name="setOrder(I)V" />
 		<method name="setPersistent(Z)V" />
+		<method name="setPreferenceDataStore(Landroid/preference/PreferenceDataStore;)V" since="26" />
+		<method name="setRecycleEnabled(Z)V" since="26" />
 		<method name="setSelectable(Z)V" />
 		<method name="setShouldDisableView(Z)V" />
+		<method name="setSingleLineTitle(Z)V" since="26" />
 		<method name="setSummary(I)V" />
 		<method name="setSummary(Ljava/lang/CharSequence;)V" />
 		<method name="setTitle(I)V" />
@@ -29671,6 +31884,21 @@
 		<method name="&lt;init>(Landroid/content/Context;Landroid/util/AttributeSet;I)V" />
 		<method name="&lt;init>(Landroid/content/Context;Landroid/util/AttributeSet;II)V" since="21" />
 	</class>
+	<class name="android/preference/PreferenceDataStore" since="26">
+		<extends name="java/lang/Object" />
+		<method name="getBoolean(Ljava/lang/String;Z)Z" />
+		<method name="getFloat(Ljava/lang/String;F)F" />
+		<method name="getInt(Ljava/lang/String;I)I" />
+		<method name="getLong(Ljava/lang/String;J)J" />
+		<method name="getString(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;" />
+		<method name="getStringSet(Ljava/lang/String;Ljava/util/Set;)Ljava/util/Set;" />
+		<method name="putBoolean(Ljava/lang/String;Z)V" />
+		<method name="putFloat(Ljava/lang/String;F)V" />
+		<method name="putInt(Ljava/lang/String;I)V" />
+		<method name="putLong(Ljava/lang/String;J)V" />
+		<method name="putString(Ljava/lang/String;Ljava/lang/String;)V" />
+		<method name="putStringSet(Ljava/lang/String;Ljava/util/Set;)V" />
+	</class>
 	<class name="android/preference/PreferenceFragment" since="11">
 		<extends name="android/app/Fragment" />
 		<method name="&lt;init>()V" />
@@ -29712,6 +31940,7 @@
 		<method name="findPreference(Ljava/lang/CharSequence;)Landroid/preference/Preference;" />
 		<method name="getDefaultSharedPreferences(Landroid/content/Context;)Landroid/content/SharedPreferences;" />
 		<method name="getDefaultSharedPreferencesName(Landroid/content/Context;)Ljava/lang/String;" since="24" />
+		<method name="getPreferenceDataStore()Landroid/preference/PreferenceDataStore;" since="26" />
 		<method name="getSharedPreferences()Landroid/content/SharedPreferences;" />
 		<method name="getSharedPreferencesMode()I" />
 		<method name="getSharedPreferencesName()Ljava/lang/String;" />
@@ -29719,6 +31948,7 @@
 		<method name="isStorageDeviceProtected()Z" since="24" />
 		<method name="setDefaultValues(Landroid/content/Context;IZ)V" />
 		<method name="setDefaultValues(Landroid/content/Context;Ljava/lang/String;IIZ)V" />
+		<method name="setPreferenceDataStore(Landroid/preference/PreferenceDataStore;)V" since="26" />
 		<method name="setSharedPreferencesMode(I)V" />
 		<method name="setSharedPreferencesName(Ljava/lang/String;)V" />
 		<method name="setStorageDefault()V" since="24" />
@@ -30009,6 +32239,8 @@
 		<extends name="java/lang/Object" />
 		<implements name="android/os/Parcelable" />
 		<method name="&lt;init>()V" />
+		<method name="getAdvancedIntOption(Ljava/lang/String;)I" since="26" />
+		<method name="getAdvancedStringOption(Ljava/lang/String;)Ljava/lang/String;" since="26" />
 		<method name="getAttributes()Landroid/print/PrintAttributes;" />
 		<method name="getCopies()I" />
 		<method name="getCreationTime()J" />
@@ -30017,6 +32249,7 @@
 		<method name="getPages()[Landroid/print/PageRange;" />
 		<method name="getPrinterId()Landroid/print/PrinterId;" />
 		<method name="getState()I" />
+		<method name="hasAdvancedOption(Ljava/lang/String;)Z" since="26" />
 		<field name="CREATOR" />
 		<field name="STATE_BLOCKED" />
 		<field name="STATE_CANCELED" />
@@ -30153,9 +32386,11 @@
 		<method name="onDisconnected()V" />
 		<method name="onPrintJobQueued(Landroid/printservice/PrintJob;)V" />
 		<method name="onRequestCancelPrintJob(Landroid/printservice/PrintJob;)V" />
+		<field name="EXTRA_CAN_SELECT_PRINTER" since="26" />
 		<field name="EXTRA_PRINTER_INFO" since="21" />
 		<field name="EXTRA_PRINT_DOCUMENT_INFO" since="23" />
 		<field name="EXTRA_PRINT_JOB_INFO" />
+		<field name="EXTRA_SELECT_PRINTER" since="26" />
 		<field name="SERVICE_INTERFACE" />
 		<field name="SERVICE_META_DATA" />
 	</class>
@@ -30183,6 +32418,7 @@
 		<field name="ACTION_SET_ALARM" />
 		<field name="ACTION_SET_TIMER" since="19" />
 		<field name="ACTION_SHOW_ALARMS" since="19" />
+		<field name="ACTION_SHOW_TIMERS" since="26" />
 		<field name="ACTION_SNOOZE_ALARM" since="23" />
 		<field name="ALARM_SEARCH_MODE_ALL" since="23" />
 		<field name="ALARM_SEARCH_MODE_LABEL" since="23" />
@@ -30647,8 +32883,10 @@
 		<field name="DURATION" />
 		<field name="EXTRA_CALL_TYPE_FILTER" since="21" />
 		<field name="FEATURES" since="21" />
+		<field name="FEATURES_HD_CALL" since="26" />
 		<field name="FEATURES_PULLED_EXTERNALLY" since="25" />
 		<field name="FEATURES_VIDEO" since="21" />
+		<field name="FEATURES_WIFI" since="26" />
 		<field name="GEOCODED_LOCATION" since="21" />
 		<field name="INCOMING_TYPE" />
 		<field name="IS_READ" since="14" />
@@ -31611,6 +33849,7 @@
 		<method name="notifyDirectoryChange(Landroid/content/ContentResolver;)V" />
 		<field name="ACCOUNT_NAME" />
 		<field name="ACCOUNT_TYPE" />
+		<field name="CALLER_PACKAGE_PARAM_KEY" since="26" />
 		<field name="CONTENT_ITEM_TYPE" />
 		<field name="CONTENT_TYPE" />
 		<field name="CONTENT_URI" />
@@ -31749,6 +33988,7 @@
 	<class name="android/provider/ContactsContract$PhoneLookup" since="5">
 		<extends name="java/lang/Object" />
 		<implements name="android/provider/BaseColumns" />
+		<implements name="android/provider/ContactsContract$ContactNameColumns" since="26" />
 		<implements name="android/provider/ContactsContract$ContactOptionsColumns" />
 		<implements name="android/provider/ContactsContract$ContactsColumns" />
 		<implements name="android/provider/ContactsContract$PhoneLookupColumns" />
@@ -31822,6 +34062,7 @@
 		<method name="&lt;init>()V" />
 		<field name="CONTENT_TYPE" />
 		<field name="CONTENT_URI" />
+		<field name="DATABASE_CREATION_TIMESTAMP" since="26" />
 		<field name="STATUS" />
 		<field name="STATUS_BUSY" />
 		<field name="STATUS_EMPTY" />
@@ -32061,7 +34302,10 @@
 		<method name="buildTreeDocumentUri(Ljava/lang/String;Ljava/lang/String;)Landroid/net/Uri;" since="21" />
 		<method name="copyDocument(Landroid/content/ContentResolver;Landroid/net/Uri;Landroid/net/Uri;)Landroid/net/Uri;" since="24" />
 		<method name="createDocument(Landroid/content/ContentResolver;Landroid/net/Uri;Ljava/lang/String;Ljava/lang/String;)Landroid/net/Uri;" since="21" />
+		<method name="createWebLinkIntent(Landroid/content/ContentResolver;Landroid/net/Uri;Landroid/os/Bundle;)Landroid/content/IntentSender;" since="26" />
 		<method name="deleteDocument(Landroid/content/ContentResolver;Landroid/net/Uri;)Z" />
+		<method name="ejectRoot(Landroid/content/ContentResolver;Landroid/net/Uri;)V" since="26" />
+		<method name="findDocumentPath(Landroid/content/ContentResolver;Landroid/net/Uri;)Landroid/provider/DocumentsContract$Path;" since="26" />
 		<method name="getDocumentId(Landroid/net/Uri;)Ljava/lang/String;" />
 		<method name="getDocumentThumbnail(Landroid/content/ContentResolver;Landroid/net/Uri;Landroid/graphics/Point;Landroid/os/CancellationSignal;)Landroid/graphics/Bitmap;" />
 		<method name="getRootId(Landroid/net/Uri;)Ljava/lang/String;" />
@@ -32072,9 +34316,11 @@
 		<method name="moveDocument(Landroid/content/ContentResolver;Landroid/net/Uri;Landroid/net/Uri;Landroid/net/Uri;)Landroid/net/Uri;" since="24" />
 		<method name="removeDocument(Landroid/content/ContentResolver;Landroid/net/Uri;Landroid/net/Uri;)Z" since="24" />
 		<method name="renameDocument(Landroid/content/ContentResolver;Landroid/net/Uri;Ljava/lang/String;)Landroid/net/Uri;" since="21" />
+		<field name="ACTION_DOCUMENT_SETTINGS" since="26" />
 		<field name="EXTRA_ERROR" />
 		<field name="EXTRA_EXCLUDE_SELF" since="23" />
 		<field name="EXTRA_INFO" />
+		<field name="EXTRA_INITIAL_URI" since="26" />
 		<field name="EXTRA_LOADING" />
 		<field name="EXTRA_ORIENTATION" since="24" />
 		<field name="EXTRA_PROMPT" since="23" />
@@ -32099,11 +34345,21 @@
 		<field name="FLAG_SUPPORTS_MOVE" since="24" />
 		<field name="FLAG_SUPPORTS_REMOVE" since="24" />
 		<field name="FLAG_SUPPORTS_RENAME" since="21" />
+		<field name="FLAG_SUPPORTS_SETTINGS" since="26" />
 		<field name="FLAG_SUPPORTS_THUMBNAIL" />
 		<field name="FLAG_SUPPORTS_WRITE" />
 		<field name="FLAG_VIRTUAL_DOCUMENT" since="24" />
+		<field name="FLAG_WEB_LINKABLE" since="26" />
 		<field name="MIME_TYPE_DIR" />
 	</class>
+	<class name="android/provider/DocumentsContract$Path" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/os/Parcelable" />
+		<method name="&lt;init>(Ljava/lang/String;Ljava/util/List;)V" />
+		<method name="getPath()Ljava/util/List;" />
+		<method name="getRootId()Ljava/lang/String;" />
+		<field name="CREATOR" />
+	</class>
 	<class name="android/provider/DocumentsContract$Root" since="19">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
@@ -32118,16 +34374,21 @@
 		<field name="COLUMN_TITLE" />
 		<field name="FLAG_LOCAL_ONLY" />
 		<field name="FLAG_SUPPORTS_CREATE" />
+		<field name="FLAG_SUPPORTS_EJECT" since="26" />
 		<field name="FLAG_SUPPORTS_IS_CHILD" since="21" />
 		<field name="FLAG_SUPPORTS_RECENTS" />
 		<field name="FLAG_SUPPORTS_SEARCH" />
+		<field name="MIME_TYPE_ITEM" since="26" />
 	</class>
 	<class name="android/provider/DocumentsProvider" since="19">
 		<extends name="android/content/ContentProvider" />
 		<method name="&lt;init>()V" />
 		<method name="copyDocument(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;" since="24" />
 		<method name="createDocument(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;" />
+		<method name="createWebLinkIntent(Ljava/lang/String;Landroid/os/Bundle;)Landroid/content/IntentSender;" since="26" />
 		<method name="deleteDocument(Ljava/lang/String;)V" />
+		<method name="ejectRoot(Ljava/lang/String;)V" since="26" />
+		<method name="findDocumentPath(Ljava/lang/String;Ljava/lang/String;)Landroid/provider/DocumentsContract$Path;" since="26" />
 		<method name="getDocumentStreamTypes(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;" since="24" />
 		<method name="getDocumentType(Ljava/lang/String;)Ljava/lang/String;" />
 		<method name="isChildDocument(Ljava/lang/String;Ljava/lang/String;)Z" since="21" />
@@ -32135,6 +34396,7 @@
 		<method name="openDocument(Ljava/lang/String;Ljava/lang/String;Landroid/os/CancellationSignal;)Landroid/os/ParcelFileDescriptor;" />
 		<method name="openDocumentThumbnail(Ljava/lang/String;Landroid/graphics/Point;Landroid/os/CancellationSignal;)Landroid/content/res/AssetFileDescriptor;" />
 		<method name="openTypedDocument(Ljava/lang/String;Ljava/lang/String;Landroid/os/Bundle;Landroid/os/CancellationSignal;)Landroid/content/res/AssetFileDescriptor;" since="24" />
+		<method name="queryChildDocuments(Ljava/lang/String;[Ljava/lang/String;Landroid/os/Bundle;)Landroid/database/Cursor;" since="26" />
 		<method name="queryChildDocuments(Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;" />
 		<method name="queryDocument(Ljava/lang/String;[Ljava/lang/String;)Landroid/database/Cursor;" />
 		<method name="queryRecentDocuments(Ljava/lang/String;[Ljava/lang/String;)Landroid/database/Cursor;" />
@@ -32144,6 +34406,69 @@
 		<method name="renameDocument(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;" since="21" />
 		<method name="revokeDocumentPermission(Ljava/lang/String;)V" since="21" />
 	</class>
+	<class name="android/provider/FontRequest" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V" />
+		<method name="&lt;init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;)V" />
+		<method name="getCertificates()Ljava/util/List;" />
+		<method name="getProviderAuthority()Ljava/lang/String;" />
+		<method name="getProviderPackage()Ljava/lang/String;" />
+		<method name="getQuery()Ljava/lang/String;" />
+	</class>
+	<class name="android/provider/FontsContract" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="buildTypeface(Landroid/content/Context;Landroid/os/CancellationSignal;[Landroid/provider/FontsContract$FontInfo;)Landroid/graphics/Typeface;" />
+		<method name="fetchFonts(Landroid/content/Context;Landroid/os/CancellationSignal;Landroid/provider/FontRequest;)Landroid/provider/FontsContract$FontFamilyResult;" />
+		<method name="requestFonts(Landroid/content/Context;Landroid/provider/FontRequest;Landroid/os/Handler;Landroid/os/CancellationSignal;Landroid/provider/FontsContract$FontRequestCallback;)V" />
+	</class>
+	<class name="android/provider/FontsContract$Columns" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/provider/BaseColumns" />
+		<method name="&lt;init>()V" />
+		<field name="FILE_ID" />
+		<field name="ITALIC" />
+		<field name="RESULT_CODE" />
+		<field name="RESULT_CODE_FONT_NOT_FOUND" />
+		<field name="RESULT_CODE_FONT_UNAVAILABLE" />
+		<field name="RESULT_CODE_MALFORMED_QUERY" />
+		<field name="RESULT_CODE_OK" />
+		<field name="TTC_INDEX" />
+		<field name="VARIATION_SETTINGS" />
+		<field name="WEIGHT" />
+	</class>
+	<class name="android/provider/FontsContract$FontFamilyResult" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="getFonts()[Landroid/provider/FontsContract$FontInfo;" />
+		<method name="getStatusCode()I" />
+		<field name="STATUS_OK" />
+		<field name="STATUS_REJECTED" />
+		<field name="STATUS_UNEXPECTED_DATA_PROVIDED" />
+		<field name="STATUS_WRONG_CERTIFICATES" />
+	</class>
+	<class name="android/provider/FontsContract$FontInfo" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="getAxes()[Landroid/graphics/fonts/FontVariationAxis;" />
+		<method name="getResultCode()I" />
+		<method name="getTtcIndex()I" />
+		<method name="getUri()Landroid/net/Uri;" />
+		<method name="getWeight()I" />
+		<method name="isItalic()Z" />
+	</class>
+	<class name="android/provider/FontsContract$FontRequestCallback" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="onTypefaceRequestFailed(I)V" />
+		<method name="onTypefaceRetrieved(Landroid/graphics/Typeface;)V" />
+		<field name="FAIL_REASON_FONT_LOAD_ERROR" />
+		<field name="FAIL_REASON_FONT_NOT_FOUND" />
+		<field name="FAIL_REASON_FONT_UNAVAILABLE" />
+		<field name="FAIL_REASON_MALFORMED_QUERY" />
+		<field name="FAIL_REASON_PROVIDER_NOT_FOUND" />
+		<field name="FAIL_REASON_WRONG_CERTIFICATES" />
+	</class>
 	<class name="android/provider/LiveFolders" since="3" deprecated="14">
 		<extends name="java/lang/Object" />
 		<implements name="android/provider/BaseColumns" />
@@ -32165,6 +34490,7 @@
 	<class name="android/provider/MediaStore" since="1">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
+		<method name="getDocumentUri(Landroid/content/Context;Landroid/net/Uri;)Landroid/net/Uri;" since="26" />
 		<method name="getMediaScannerUri()Landroid/net/Uri;" />
 		<method name="getVersion(Landroid/content/Context;)Ljava/lang/String;" since="12" />
 		<field name="ACTION_IMAGE_CAPTURE" since="3" />
@@ -32530,10 +34856,12 @@
 		<field name="ACTION_APPLICATION_DETAILS_SETTINGS" since="9" />
 		<field name="ACTION_APPLICATION_DEVELOPMENT_SETTINGS" since="3" />
 		<field name="ACTION_APPLICATION_SETTINGS" />
+		<field name="ACTION_APP_NOTIFICATION_SETTINGS" since="26" />
 		<field name="ACTION_BATTERY_SAVER_SETTINGS" since="22" />
 		<field name="ACTION_BLUETOOTH_SETTINGS" />
 		<field name="ACTION_CAPTIONING_SETTINGS" since="19" />
 		<field name="ACTION_CAST_SETTINGS" since="21" />
+		<field name="ACTION_CHANNEL_NOTIFICATION_SETTINGS" since="26" />
 		<field name="ACTION_DATA_ROAMING_SETTINGS" since="3" />
 		<field name="ACTION_DATE_SETTINGS" />
 		<field name="ACTION_DEVICE_INFO_SETTINGS" since="8" />
@@ -32552,18 +34880,21 @@
 		<field name="ACTION_MANAGE_APPLICATIONS_SETTINGS" since="3" />
 		<field name="ACTION_MANAGE_DEFAULT_APPS_SETTINGS" since="24" />
 		<field name="ACTION_MANAGE_OVERLAY_PERMISSION" since="23" />
+		<field name="ACTION_MANAGE_UNKNOWN_APP_SOURCES" since="26" />
 		<field name="ACTION_MANAGE_WRITE_SETTINGS" since="23" />
 		<field name="ACTION_MEMORY_CARD_SETTINGS" since="3" />
 		<field name="ACTION_NETWORK_OPERATOR_SETTINGS" since="3" />
 		<field name="ACTION_NFCSHARING_SETTINGS" since="14" />
 		<field name="ACTION_NFC_PAYMENT_SETTINGS" since="19" />
 		<field name="ACTION_NFC_SETTINGS" since="16" />
+		<field name="ACTION_NIGHT_DISPLAY_SETTINGS" since="26" />
 		<field name="ACTION_NOTIFICATION_LISTENER_SETTINGS" since="22" />
 		<field name="ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS" since="23" />
 		<field name="ACTION_PRINT_SETTINGS" since="19" />
 		<field name="ACTION_PRIVACY_SETTINGS" since="5" />
 		<field name="ACTION_QUICK_LAUNCH_SETTINGS" since="3" />
 		<field name="ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" since="23" />
+		<field name="ACTION_REQUEST_SET_AUTOFILL_SERVICE" since="26" />
 		<field name="ACTION_SEARCH_SETTINGS" since="8" />
 		<field name="ACTION_SECURITY_SETTINGS" />
 		<field name="ACTION_SETTINGS" />
@@ -32582,11 +34913,14 @@
 		<field name="ACTION_WIFI_IP_SETTINGS" since="3" />
 		<field name="ACTION_WIFI_SETTINGS" />
 		<field name="ACTION_WIRELESS_SETTINGS" />
+		<field name="ACTION_ZEN_MODE_PRIORITY_SETTINGS" since="26" />
 		<field name="AUTHORITY" />
 		<field name="EXTRA_ACCOUNT_TYPES" since="18" />
 		<field name="EXTRA_AIRPLANE_MODE_ENABLED" since="23" />
+		<field name="EXTRA_APP_PACKAGE" since="26" />
 		<field name="EXTRA_AUTHORITIES" since="8" />
 		<field name="EXTRA_BATTERY_SAVER_MODE_ENABLED" since="23" />
+		<field name="EXTRA_CHANNEL_ID" since="26" />
 		<field name="EXTRA_DO_NOT_DISTURB_MODE_ENABLED" since="23" />
 		<field name="EXTRA_DO_NOT_DISTURB_MODE_MINUTES" since="23" />
 		<field name="EXTRA_INPUT_METHOD_ID" since="11" />
@@ -32632,7 +34966,7 @@
 		<field name="RADIO_CELL" />
 		<field name="RADIO_NFC" />
 		<field name="RADIO_WIFI" />
-		<field name="SHOW_PROCESSES" deprecated="25" />
+		<field name="SHOW_PROCESSES" deprecated="26" />
 		<field name="STAY_ON_WHILE_PLUGGED_IN" />
 		<field name="SYS_PROP_SETTING_VERSION" />
 		<field name="TRANSITION_ANIMATION_SCALE" />
@@ -32642,7 +34976,7 @@
 		<field name="WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN" since="23" />
 		<field name="WIFI_MAX_DHCP_RETRY_COUNT" />
 		<field name="WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS" />
-		<field name="WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON" />
+		<field name="WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON" deprecated="26" />
 		<field name="WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY" />
 		<field name="WIFI_NUM_OPEN_NETWORKS_KEPT" />
 		<field name="WIFI_ON" />
@@ -32681,7 +35015,7 @@
 		<method name="setLocationProviderEnabled(Landroid/content/ContentResolver;Ljava/lang/String;Z)V" since="8" deprecated="19" />
 		<field name="ACCESSIBILITY_DISPLAY_INVERSION_ENABLED" since="21" />
 		<field name="ACCESSIBILITY_ENABLED" since="4" />
-		<field name="ACCESSIBILITY_SPEAK_PASSWORD" since="15" />
+		<field name="ACCESSIBILITY_SPEAK_PASSWORD" since="15" deprecated="26" />
 		<field name="ADB_ENABLED" deprecated="17" />
 		<field name="ALLOWED_GEOLOCATION_ORIGINS" since="8" />
 		<field name="ALLOW_MOCK_LOCATION" deprecated="23" />
@@ -33108,6 +35442,17 @@
 		<field name="RETRY_INDEX" />
 		<field name="SUBSCRIPTION_ID" since="22" />
 	</class>
+	<class name="android/provider/Telephony$ServiceStateTable" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="getUriForSubscriptionId(I)Landroid/net/Uri;" />
+		<method name="getUriForSubscriptionIdAndField(ILjava/lang/String;)Landroid/net/Uri;" />
+		<field name="AUTHORITY" />
+		<field name="CONTENT_URI" />
+		<field name="IS_MANUAL_NETWORK_SELECTION" />
+		<field name="VOICE_OPERATOR_NUMERIC" />
+		<field name="VOICE_REG_STATE" />
+	</class>
 	<class name="android/provider/Telephony$Sms" since="19">
 		<extends name="java/lang/Object" />
 		<implements name="android/provider/BaseColumns" />
@@ -33320,6 +35665,8 @@
 		<implements name="android/provider/OpenableColumns" />
 		<method name="&lt;init>()V" />
 		<method name="buildSourceUri(Ljava/lang/String;)Landroid/net/Uri;" />
+		<field name="ARCHIVED" since="26" />
+		<field name="BACKED_UP" since="26" />
 		<field name="CONTENT_URI" />
 		<field name="DATE" />
 		<field name="DELETED" since="23" />
@@ -33327,6 +35674,7 @@
 		<field name="DIR_TYPE" />
 		<field name="DURATION" />
 		<field name="HAS_CONTENT" />
+		<field name="IS_OMTP_VOICEMAIL" since="26" />
 		<field name="IS_READ" />
 		<field name="ITEM_TYPE" />
 		<field name="LAST_MODIFIED" since="24" />
@@ -33334,6 +35682,7 @@
 		<field name="NUMBER" />
 		<field name="PHONE_ACCOUNT_COMPONENT_NAME" since="23" />
 		<field name="PHONE_ACCOUNT_ID" since="23" />
+		<field name="RESTORED" since="26" />
 		<field name="SOURCE_DATA" />
 		<field name="SOURCE_PACKAGE" />
 		<field name="TRANSCRIPTION" since="21" />
@@ -34838,8 +37187,13 @@
 		<method name="getPrivateKey(Landroid/content/Context;Ljava/lang/String;)Ljava/security/PrivateKey;" />
 		<method name="isBoundKeyAlgorithm(Ljava/lang/String;)Z" since="18" deprecated="23" />
 		<method name="isKeyAlgorithmSupported(Ljava/lang/String;)Z" since="18" />
-		<field name="ACTION_STORAGE_CHANGED" since="16" />
+		<field name="ACTION_KEYCHAIN_CHANGED" since="26" />
+		<field name="ACTION_KEY_ACCESS_CHANGED" since="26" />
+		<field name="ACTION_STORAGE_CHANGED" since="16" deprecated="26" />
+		<field name="ACTION_TRUST_STORE_CHANGED" since="26" />
 		<field name="EXTRA_CERTIFICATE" />
+		<field name="EXTRA_KEY_ACCESSIBLE" since="26" />
+		<field name="EXTRA_KEY_ALIAS" since="26" />
 		<field name="EXTRA_NAME" />
 		<field name="EXTRA_PKCS12" />
 	</class>
@@ -35073,6 +37427,130 @@
 		<method name="&lt;init>(Ljava/lang/String;)V" />
 		<method name="&lt;init>(Ljava/lang/String;Ljava/lang/Throwable;)V" />
 	</class>
+	<class name="android/service/autofill/AutofillService" since="26">
+		<extends name="android/app/Service" />
+		<method name="&lt;init>()V" />
+		<method name="getFillEventHistory()Landroid/service/autofill/FillEventHistory;" />
+		<method name="onConnected()V" />
+		<method name="onDisconnected()V" />
+		<method name="onFillRequest(Landroid/service/autofill/FillRequest;Landroid/os/CancellationSignal;Landroid/service/autofill/FillCallback;)V" />
+		<method name="onSaveRequest(Landroid/service/autofill/SaveRequest;Landroid/service/autofill/SaveCallback;)V" />
+		<field name="SERVICE_INTERFACE" />
+		<field name="SERVICE_META_DATA" />
+	</class>
+	<class name="android/service/autofill/Dataset" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/os/Parcelable" />
+		<method name="&lt;init>()V" />
+		<field name="CREATOR" />
+	</class>
+	<class name="android/service/autofill/Dataset$Builder" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="&lt;init>(Landroid/widget/RemoteViews;)V" />
+		<method name="build()Landroid/service/autofill/Dataset;" />
+		<method name="setAuthentication(Landroid/content/IntentSender;)Landroid/service/autofill/Dataset$Builder;" />
+		<method name="setId(Ljava/lang/String;)Landroid/service/autofill/Dataset$Builder;" />
+		<method name="setValue(Landroid/view/autofill/AutofillId;Landroid/view/autofill/AutofillValue;)Landroid/service/autofill/Dataset$Builder;" />
+		<method name="setValue(Landroid/view/autofill/AutofillId;Landroid/view/autofill/AutofillValue;Landroid/widget/RemoteViews;)Landroid/service/autofill/Dataset$Builder;" />
+	</class>
+	<class name="android/service/autofill/FillCallback" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="onFailure(Ljava/lang/CharSequence;)V" />
+		<method name="onSuccess(Landroid/service/autofill/FillResponse;)V" />
+	</class>
+	<class name="android/service/autofill/FillContext" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/os/Parcelable" />
+		<method name="&lt;init>()V" />
+		<method name="getRequestId()I" />
+		<method name="getStructure()Landroid/app/assist/AssistStructure;" />
+		<field name="CREATOR" />
+	</class>
+	<class name="android/service/autofill/FillEventHistory" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/os/Parcelable" />
+		<method name="&lt;init>()V" />
+		<method name="getClientState()Landroid/os/Bundle;" />
+		<method name="getEvents()Ljava/util/List;" />
+		<field name="CREATOR" />
+	</class>
+	<class name="android/service/autofill/FillEventHistory$Event" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="getDatasetId()Ljava/lang/String;" />
+		<method name="getType()I" />
+		<field name="TYPE_AUTHENTICATION_SELECTED" />
+		<field name="TYPE_DATASET_AUTHENTICATION_SELECTED" />
+		<field name="TYPE_DATASET_SELECTED" />
+		<field name="TYPE_SAVE_SHOWN" />
+	</class>
+	<class name="android/service/autofill/FillRequest" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/os/Parcelable" />
+		<method name="&lt;init>()V" />
+		<method name="getClientState()Landroid/os/Bundle;" />
+		<method name="getFillContexts()Ljava/util/List;" />
+		<method name="getFlags()I" />
+		<method name="getId()I" />
+		<field name="CREATOR" />
+		<field name="FLAG_MANUAL_REQUEST" />
+	</class>
+	<class name="android/service/autofill/FillResponse" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/os/Parcelable" />
+		<method name="&lt;init>()V" />
+		<field name="CREATOR" />
+	</class>
+	<class name="android/service/autofill/FillResponse$Builder" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="addDataset(Landroid/service/autofill/Dataset;)Landroid/service/autofill/FillResponse$Builder;" />
+		<method name="build()Landroid/service/autofill/FillResponse;" />
+		<method name="setAuthentication([Landroid/view/autofill/AutofillId;Landroid/content/IntentSender;Landroid/widget/RemoteViews;)Landroid/service/autofill/FillResponse$Builder;" />
+		<method name="setClientState(Landroid/os/Bundle;)Landroid/service/autofill/FillResponse$Builder;" />
+		<method name="setIgnoredIds([Landroid/view/autofill/AutofillId;)Landroid/service/autofill/FillResponse$Builder;" />
+		<method name="setSaveInfo(Landroid/service/autofill/SaveInfo;)Landroid/service/autofill/FillResponse$Builder;" />
+	</class>
+	<class name="android/service/autofill/SaveCallback" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="onFailure(Ljava/lang/CharSequence;)V" />
+		<method name="onSuccess()V" />
+	</class>
+	<class name="android/service/autofill/SaveInfo" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/os/Parcelable" />
+		<method name="&lt;init>()V" />
+		<field name="CREATOR" />
+		<field name="FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE" />
+		<field name="NEGATIVE_BUTTON_STYLE_CANCEL" />
+		<field name="NEGATIVE_BUTTON_STYLE_REJECT" />
+		<field name="SAVE_DATA_TYPE_ADDRESS" />
+		<field name="SAVE_DATA_TYPE_CREDIT_CARD" />
+		<field name="SAVE_DATA_TYPE_EMAIL_ADDRESS" />
+		<field name="SAVE_DATA_TYPE_GENERIC" />
+		<field name="SAVE_DATA_TYPE_PASSWORD" />
+		<field name="SAVE_DATA_TYPE_USERNAME" />
+	</class>
+	<class name="android/service/autofill/SaveInfo$Builder" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>(I[Landroid/view/autofill/AutofillId;)V" />
+		<method name="build()Landroid/service/autofill/SaveInfo;" />
+		<method name="setDescription(Ljava/lang/CharSequence;)Landroid/service/autofill/SaveInfo$Builder;" />
+		<method name="setFlags(I)Landroid/service/autofill/SaveInfo$Builder;" />
+		<method name="setNegativeAction(ILandroid/content/IntentSender;)Landroid/service/autofill/SaveInfo$Builder;" />
+		<method name="setOptionalIds([Landroid/view/autofill/AutofillId;)Landroid/service/autofill/SaveInfo$Builder;" />
+	</class>
+	<class name="android/service/autofill/SaveRequest" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/os/Parcelable" />
+		<method name="&lt;init>()V" />
+		<method name="getClientState()Landroid/os/Bundle;" />
+		<method name="getFillContexts()Ljava/util/List;" />
+		<field name="CREATOR" />
+	</class>
 	<class name="android/service/carrier/CarrierIdentifier" since="23">
 		<extends name="java/lang/Object" />
 		<implements name="android/os/Parcelable" />
@@ -35261,6 +37739,8 @@
 		<method name="onRequestConditions(I)V" />
 		<method name="onSubscribe(Landroid/net/Uri;)V" />
 		<method name="onUnsubscribe(Landroid/net/Uri;)V" />
+		<method name="requestRebind(Landroid/content/ComponentName;)V" since="26" />
+		<method name="requestUnbind()V" since="26" />
 		<field name="EXTRA_RULE_ID" />
 		<field name="META_DATA_CONFIGURATION_ACTIVITY" />
 		<field name="META_DATA_RULE_INSTANCE_LIMIT" />
@@ -35279,20 +37759,28 @@
 		<method name="getCurrentInterruptionFilter()I" since="21" />
 		<method name="getCurrentListenerHints()I" since="21" />
 		<method name="getCurrentRanking()Landroid/service/notification/NotificationListenerService$RankingMap;" since="21" />
+		<method name="getNotificationChannelGroups(Ljava/lang/String;Landroid/os/UserHandle;)Ljava/util/List;" since="26" />
+		<method name="getNotificationChannels(Ljava/lang/String;Landroid/os/UserHandle;)Ljava/util/List;" since="26" />
+		<method name="getSnoozedNotifications()[Landroid/service/notification/StatusBarNotification;" since="26" />
 		<method name="onInterruptionFilterChanged(I)V" since="21" />
 		<method name="onListenerConnected()V" since="21" />
 		<method name="onListenerDisconnected()V" since="24" />
 		<method name="onListenerHintsChanged(I)V" since="21" />
+		<method name="onNotificationChannelGroupModified(Ljava/lang/String;Landroid/os/UserHandle;Landroid/app/NotificationChannelGroup;I)V" since="26" />
+		<method name="onNotificationChannelModified(Ljava/lang/String;Landroid/os/UserHandle;Landroid/app/NotificationChannel;I)V" since="26" />
 		<method name="onNotificationPosted(Landroid/service/notification/StatusBarNotification;)V" />
 		<method name="onNotificationPosted(Landroid/service/notification/StatusBarNotification;Landroid/service/notification/NotificationListenerService$RankingMap;)V" since="21" />
 		<method name="onNotificationRankingUpdate(Landroid/service/notification/NotificationListenerService$RankingMap;)V" since="21" />
 		<method name="onNotificationRemoved(Landroid/service/notification/StatusBarNotification;)V" />
 		<method name="onNotificationRemoved(Landroid/service/notification/StatusBarNotification;Landroid/service/notification/NotificationListenerService$RankingMap;)V" since="21" />
+		<method name="onNotificationRemoved(Landroid/service/notification/StatusBarNotification;Landroid/service/notification/NotificationListenerService$RankingMap;I)V" since="26" />
 		<method name="requestInterruptionFilter(I)V" since="21" />
 		<method name="requestListenerHints(I)V" since="21" />
 		<method name="requestRebind(Landroid/content/ComponentName;)V" since="24" />
 		<method name="requestUnbind()V" since="24" />
 		<method name="setNotificationsShown([Ljava/lang/String;)V" since="23" />
+		<method name="snoozeNotification(Ljava/lang/String;J)V" since="26" />
+		<method name="updateNotificationChannel(Ljava/lang/String;Landroid/os/UserHandle;Landroid/app/NotificationChannel;)V" since="26" />
 		<field name="HINT_HOST_DISABLE_CALL_EFFECTS" since="24" />
 		<field name="HINT_HOST_DISABLE_EFFECTS" since="21" />
 		<field name="HINT_HOST_DISABLE_NOTIFICATION_EFFECTS" since="24" />
@@ -35301,6 +37789,28 @@
 		<field name="INTERRUPTION_FILTER_NONE" since="21" />
 		<field name="INTERRUPTION_FILTER_PRIORITY" since="21" />
 		<field name="INTERRUPTION_FILTER_UNKNOWN" since="23" />
+		<field name="NOTIFICATION_CHANNEL_OR_GROUP_ADDED" since="26" />
+		<field name="NOTIFICATION_CHANNEL_OR_GROUP_DELETED" since="26" />
+		<field name="NOTIFICATION_CHANNEL_OR_GROUP_UPDATED" since="26" />
+		<field name="REASON_APP_CANCEL" since="26" />
+		<field name="REASON_APP_CANCEL_ALL" since="26" />
+		<field name="REASON_CANCEL" since="26" />
+		<field name="REASON_CANCEL_ALL" since="26" />
+		<field name="REASON_CHANNEL_BANNED" since="26" />
+		<field name="REASON_CLICK" since="26" />
+		<field name="REASON_ERROR" since="26" />
+		<field name="REASON_GROUP_OPTIMIZATION" since="26" />
+		<field name="REASON_GROUP_SUMMARY_CANCELED" since="26" />
+		<field name="REASON_LISTENER_CANCEL" since="26" />
+		<field name="REASON_LISTENER_CANCEL_ALL" since="26" />
+		<field name="REASON_PACKAGE_BANNED" since="26" />
+		<field name="REASON_PACKAGE_CHANGED" since="26" />
+		<field name="REASON_PACKAGE_SUSPENDED" since="26" />
+		<field name="REASON_PROFILE_TURNED_OFF" since="26" />
+		<field name="REASON_SNOOZED" since="26" />
+		<field name="REASON_TIMEOUT" since="26" />
+		<field name="REASON_UNAUTOBUNDLED" since="26" />
+		<field name="REASON_USER_STOPPED" since="26" />
 		<field name="SERVICE_INTERFACE" />
 		<field name="SUPPRESSED_EFFECT_SCREEN_OFF" since="24" />
 		<field name="SUPPRESSED_EFFECT_SCREEN_ON" since="24" />
@@ -35308,6 +37818,8 @@
 	<class name="android/service/notification/NotificationListenerService$Ranking" since="21">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
+		<method name="canShowBadge()Z" since="26" />
+		<method name="getChannel()Landroid/app/NotificationChannel;" since="26" />
 		<method name="getImportance()I" since="24" />
 		<method name="getImportanceExplanation()Ljava/lang/CharSequence;" since="24" />
 		<method name="getKey()Ljava/lang/String;" />
@@ -35329,7 +37841,7 @@
 		<extends name="java/lang/Object" />
 		<implements name="android/os/Parcelable" />
 		<method name="&lt;init>(Landroid/os/Parcel;)V" />
-		<method name="&lt;init>(Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;IIILandroid/app/Notification;Landroid/os/UserHandle;J)V" />
+		<method name="&lt;init>(Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;IIILandroid/app/Notification;Landroid/os/UserHandle;J)V" deprecated="26" />
 		<method name="clone()Landroid/service/notification/StatusBarNotification;" />
 		<method name="getGroupKey()Ljava/lang/String;" since="21" />
 		<method name="getId()I" />
@@ -35485,6 +37997,7 @@
 		<method name="onHandleScreenshot(Landroid/graphics/Bitmap;)V" since="23" />
 		<method name="onHide()V" since="23" />
 		<method name="onLockscreenShown()V" since="23" />
+		<method name="onPrepareShow(Landroid/os/Bundle;I)V" since="26" />
 		<method name="onRequestAbortVoice(Landroid/service/voice/VoiceInteractionSession$AbortVoiceRequest;)V" since="23" />
 		<method name="onRequestCommand(Landroid/service/voice/VoiceInteractionSession$CommandRequest;)V" since="23" />
 		<method name="onRequestCompleteVoice(Landroid/service/voice/VoiceInteractionSession$CompleteVoiceRequest;)V" since="23" />
@@ -35497,7 +38010,9 @@
 		<method name="setDisabledShowContext(I)V" since="23" />
 		<method name="setKeepAwake(Z)V" since="23" />
 		<method name="setTheme(I)V" since="23" />
+		<method name="setUiEnabled(Z)V" since="26" />
 		<method name="show(Landroid/os/Bundle;I)V" since="23" />
+		<method name="startAssistantActivity(Landroid/content/Intent;)V" since="26" />
 		<method name="startVoiceActivity(Landroid/content/Intent;)V" since="23" />
 		<field name="SHOW_SOURCE_ACTIVITY" since="24" />
 		<field name="SHOW_SOURCE_APPLICATION" since="23" />
@@ -35718,6 +38233,7 @@
 		<method name="getMaxBufferSize()I" />
 		<method name="hasFinished()Z" since="21" />
 		<method name="hasStarted()Z" since="21" />
+		<method name="rangeStart(III)V" since="26" />
 		<method name="start(III)I" />
 	</class>
 	<class name="android/speech/tts/SynthesisRequest" since="14">
@@ -35867,6 +38383,7 @@
 		<method name="onDone(Ljava/lang/String;)V" />
 		<method name="onError(Ljava/lang/String;)V" deprecated="21" />
 		<method name="onError(Ljava/lang/String;I)V" since="21" />
+		<method name="onRangeStart(Ljava/lang/String;III)V" since="26" />
 		<method name="onStart(Ljava/lang/String;)V" />
 		<method name="onStop(Ljava/lang/String;Z)V" since="23" />
 	</class>
@@ -35931,13 +38448,16 @@
 		<method name="getsockname(Ljava/io/FileDescriptor;)Ljava/net/SocketAddress;" />
 		<method name="gettid()I" />
 		<method name="getuid()I" />
+		<method name="getxattr(Ljava/lang/String;Ljava/lang/String;)[B" since="26" />
 		<method name="if_indextoname(I)Ljava/lang/String;" />
+		<method name="if_nametoindex(Ljava/lang/String;)I" since="26" />
 		<method name="inet_pton(ILjava/lang/String;)Ljava/net/InetAddress;" />
 		<method name="isatty(Ljava/io/FileDescriptor;)Z" />
 		<method name="kill(II)V" />
 		<method name="lchown(Ljava/lang/String;II)V" />
 		<method name="link(Ljava/lang/String;Ljava/lang/String;)V" />
 		<method name="listen(Ljava/io/FileDescriptor;I)V" />
+		<method name="listxattr(Ljava/lang/String;)[Ljava/lang/String;" since="26" />
 		<method name="lseek(Ljava/io/FileDescriptor;JI)J" />
 		<method name="lstat(Ljava/lang/String;)Landroid/system/StructStat;" />
 		<method name="mincore(JJ[B)V" />
@@ -35964,6 +38484,7 @@
 		<method name="recvfrom(Ljava/io/FileDescriptor;Ljava/nio/ByteBuffer;ILjava/net/InetSocketAddress;)I" />
 		<method name="recvfrom(Ljava/io/FileDescriptor;[BIIILjava/net/InetSocketAddress;)I" />
 		<method name="remove(Ljava/lang/String;)V" />
+		<method name="removexattr(Ljava/lang/String;Ljava/lang/String;)V" since="26" />
 		<method name="rename(Ljava/lang/String;Ljava/lang/String;)V" />
 		<method name="sendfile(Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;Landroid/util/MutableLong;J)J" />
 		<method name="sendto(Ljava/io/FileDescriptor;Ljava/nio/ByteBuffer;ILjava/net/InetAddress;I)I" />
@@ -35973,7 +38494,9 @@
 		<method name="seteuid(I)V" />
 		<method name="setgid(I)V" />
 		<method name="setsid()I" />
+		<method name="setsockoptInt(Ljava/io/FileDescriptor;III)V" since="26" />
 		<method name="setuid(I)V" />
+		<method name="setxattr(Ljava/lang/String;Ljava/lang/String;[BI)V" since="26" />
 		<method name="shutdown(Ljava/io/FileDescriptor;I)V" />
 		<method name="socket(III)Ljava/io/FileDescriptor;" />
 		<method name="socketpair(IIILjava/io/FileDescriptor;Ljava/io/FileDescriptor;)V" />
@@ -36384,6 +38907,7 @@
 		<field name="S_IXOTH" />
 		<field name="S_IXUSR" />
 		<field name="TCP_NODELAY" />
+		<field name="TCP_USER_TIMEOUT" since="26" />
 		<field name="WCONTINUED" />
 		<field name="WEXITED" />
 		<field name="WNOHANG" />
@@ -36544,9 +39068,11 @@
 		<method name="getDetails()Landroid/telecom/Call$Details;" />
 		<method name="getParent()Landroid/telecom/Call;" />
 		<method name="getRemainingPostDialSequence()Ljava/lang/String;" />
+		<method name="getRttCall()Landroid/telecom/Call$RttCall;" since="26" />
 		<method name="getState()I" />
 		<method name="getVideoCall()Landroid/telecom/InCallService$VideoCall;" />
 		<method name="hold()V" />
+		<method name="isRttActive()Z" since="26" />
 		<method name="mergeConference()V" />
 		<method name="phoneAccountSelected(Landroid/telecom/PhoneAccountHandle;Z)V" />
 		<method name="playDtmfTone(C)V" />
@@ -36558,13 +39084,17 @@
 		<method name="reject(ZLjava/lang/String;)V" />
 		<method name="removeExtras(Ljava/util/List;)V" since="25" />
 		<method name="removeExtras([Ljava/lang/String;)V" since="25" />
+		<method name="respondToRttRequest(IZ)V" since="26" />
 		<method name="sendCallEvent(Ljava/lang/String;Landroid/os/Bundle;)V" since="25" />
+		<method name="sendRttRequest()V" since="26" />
 		<method name="splitFromConference()V" />
 		<method name="stopDtmfTone()V" />
+		<method name="stopRtt()V" since="26" />
 		<method name="swapConference()V" />
 		<method name="unhold()V" />
 		<method name="unregisterCallback(Landroid/telecom/Call$Callback;)V" />
 		<field name="AVAILABLE_PHONE_ACCOUNTS" />
+		<field name="EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS" since="26" />
 		<field name="STATE_ACTIVE" />
 		<field name="STATE_CONNECTING" />
 		<field name="STATE_DIALING" />
@@ -36587,6 +39117,10 @@
 		<method name="onDetailsChanged(Landroid/telecom/Call;Landroid/telecom/Call$Details;)V" />
 		<method name="onParentChanged(Landroid/telecom/Call;Landroid/telecom/Call;)V" />
 		<method name="onPostDialWait(Landroid/telecom/Call;Ljava/lang/String;)V" />
+		<method name="onRttInitiationFailure(Landroid/telecom/Call;I)V" since="26" />
+		<method name="onRttModeChanged(Landroid/telecom/Call;I)V" since="26" />
+		<method name="onRttRequest(Landroid/telecom/Call;I)V" since="26" />
+		<method name="onRttStatusChanged(Landroid/telecom/Call;ZLandroid/telecom/Call$RttCall;)V" since="26" />
 		<method name="onStateChanged(Landroid/telecom/Call;I)V" />
 		<method name="onVideoCallChanged(Landroid/telecom/Call;Landroid/telecom/InCallService$VideoCall;)V" />
 	</class>
@@ -36602,6 +39136,7 @@
 		<method name="getCallerDisplayName()Ljava/lang/String;" />
 		<method name="getCallerDisplayNamePresentation()I" />
 		<method name="getConnectTimeMillis()J" />
+		<method name="getCreationTimeMillis()J" since="26" />
 		<method name="getDisconnectCause()Landroid/telecom/DisconnectCause;" />
 		<method name="getExtras()Landroid/os/Bundle;" />
 		<method name="getGatewayInfo()Landroid/telecom/GatewayInfo;" />
@@ -36638,8 +39173,20 @@
 		<field name="PROPERTY_HAS_CDMA_VOICE_PRIVACY" since="25" />
 		<field name="PROPERTY_HIGH_DEF_AUDIO" />
 		<field name="PROPERTY_IS_EXTERNAL_CALL" since="25" />
+		<field name="PROPERTY_SELF_MANAGED" since="26" />
 		<field name="PROPERTY_WIFI" />
 	</class>
+	<class name="android/telecom/Call$RttCall" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="getRttAudioMode()I" />
+		<method name="read()Ljava/lang/String;" />
+		<method name="setRttMode(I)V" />
+		<method name="write(Ljava/lang/String;)V" />
+		<field name="RTT_MODE_FULL" />
+		<field name="RTT_MODE_HCO" />
+		<field name="RTT_MODE_VCO" />
+	</class>
 	<class name="android/telecom/CallAudioState" since="23">
 		<extends name="java/lang/Object" />
 		<implements name="android/os/Parcelable" />
@@ -36768,6 +39315,7 @@
 		<method name="onReject()V" />
 		<method name="onReject(Ljava/lang/String;)V" since="24" />
 		<method name="onSeparate()V" />
+		<method name="onShowIncomingCallUi()V" since="26" />
 		<method name="onStateChanged(I)V" />
 		<method name="onStopDtmfTone()V" />
 		<method name="onUnhold()V" />
@@ -36779,6 +39327,7 @@
 		<method name="setActive()V" />
 		<method name="setAddress(Landroid/net/Uri;I)V" />
 		<method name="setAudioModeIsVoip(Z)V" />
+		<method name="setAudioRoute(I)V" since="26" />
 		<method name="setCallerDisplayName(Ljava/lang/String;I)V" />
 		<method name="setConferenceableConnections(Ljava/util/List;)V" />
 		<method name="setConferenceables(Ljava/util/List;)V" />
@@ -36822,11 +39371,13 @@
 		<field name="EVENT_CALL_MERGE_FAILED" since="25" />
 		<field name="EVENT_CALL_PULL_FAILED" since="25" />
 		<field name="EXTRA_ANSWERING_DROPS_FG_CALL" since="25" />
+		<field name="EXTRA_ANSWERING_DROPS_FG_CALL_APP_NAME" since="26" />
 		<field name="EXTRA_CALL_SUBJECT" />
 		<field name="EXTRA_CHILD_ADDRESS" />
 		<field name="EXTRA_LAST_FORWARDED_NUMBER" />
 		<field name="PROPERTY_HAS_CDMA_VOICE_PRIVACY" since="25" />
 		<field name="PROPERTY_IS_EXTERNAL_CALL" since="25" />
+		<field name="PROPERTY_SELF_MANAGED" since="26" />
 		<field name="STATE_ACTIVE" />
 		<field name="STATE_DIALING" />
 		<field name="STATE_DISCONNECTED" />
@@ -36836,6 +39387,15 @@
 		<field name="STATE_PULLING_CALL" since="25" />
 		<field name="STATE_RINGING" />
 	</class>
+	<class name="android/telecom/Connection$RttModifyStatus" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<field name="SESSION_MODIFY_REQUEST_FAIL" />
+		<field name="SESSION_MODIFY_REQUEST_INVALID" />
+		<field name="SESSION_MODIFY_REQUEST_REJECTED_BY_REMOTE" />
+		<field name="SESSION_MODIFY_REQUEST_SUCCESS" />
+		<field name="SESSION_MODIFY_REQUEST_TIMED_OUT" />
+	</class>
 	<class name="android/telecom/Connection$VideoProvider" since="23">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
@@ -36857,6 +39417,7 @@
 		<method name="receiveSessionModifyResponse(ILandroid/telecom/VideoProfile;Landroid/telecom/VideoProfile;)V" />
 		<method name="setCallDataUsage(J)V" />
 		<field name="SESSION_EVENT_CAMERA_FAILURE" />
+		<field name="SESSION_EVENT_CAMERA_PERMISSION_ERROR" since="26" />
 		<field name="SESSION_EVENT_CAMERA_READY" />
 		<field name="SESSION_EVENT_RX_PAUSE" />
 		<field name="SESSION_EVENT_RX_RESUME" />
@@ -36891,7 +39452,9 @@
 		<method name="getAllConnections()Ljava/util/Collection;" />
 		<method name="onConference(Landroid/telecom/Connection;Landroid/telecom/Connection;)V" />
 		<method name="onCreateIncomingConnection(Landroid/telecom/PhoneAccountHandle;Landroid/telecom/ConnectionRequest;)Landroid/telecom/Connection;" />
+		<method name="onCreateIncomingConnectionFailed(Landroid/telecom/PhoneAccountHandle;Landroid/telecom/ConnectionRequest;)V" since="26" />
 		<method name="onCreateOutgoingConnection(Landroid/telecom/PhoneAccountHandle;Landroid/telecom/ConnectionRequest;)Landroid/telecom/Connection;" />
+		<method name="onCreateOutgoingConnectionFailed(Landroid/telecom/PhoneAccountHandle;Landroid/telecom/ConnectionRequest;)V" since="26" />
 		<method name="onRemoteConferenceAdded(Landroid/telecom/RemoteConference;)V" />
 		<method name="onRemoteExistingConnectionAdded(Landroid/telecom/RemoteConnection;)V" />
 		<field name="SERVICE_INTERFACE" />
@@ -37001,7 +39564,10 @@
 		<field name="CAPABILITY_CALL_SUBJECT" />
 		<field name="CAPABILITY_CONNECTION_MANAGER" />
 		<field name="CAPABILITY_PLACE_EMERGENCY_CALLS" />
+		<field name="CAPABILITY_RTT" since="26" />
+		<field name="CAPABILITY_SELF_MANAGED" since="26" />
 		<field name="CAPABILITY_SIM_SUBSCRIPTION" />
+		<field name="CAPABILITY_SUPPORTS_VIDEO_CALLING" since="26" />
 		<field name="CAPABILITY_VIDEO_CALLING" />
 		<field name="CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE" since="24" />
 		<field name="CREATOR" />
@@ -37169,6 +39735,8 @@
 	<class name="android/telecom/TelecomManager" since="21">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
+		<method name="acceptRingingCall()V" since="26" />
+		<method name="acceptRingingCall(I)V" since="26" />
 		<method name="addNewIncomingCall(Landroid/telecom/PhoneAccountHandle;Landroid/os/Bundle;)V" since="23" />
 		<method name="cancelMissedCallsNotification()V" />
 		<method name="createManageBlockedNumbersIntent()Landroid/content/Intent;" since="24" />
@@ -37178,11 +39746,15 @@
 		<method name="getDefaultOutgoingPhoneAccount(Ljava/lang/String;)Landroid/telecom/PhoneAccountHandle;" since="23" />
 		<method name="getLine1Number(Landroid/telecom/PhoneAccountHandle;)Ljava/lang/String;" since="23" />
 		<method name="getPhoneAccount(Landroid/telecom/PhoneAccountHandle;)Landroid/telecom/PhoneAccount;" since="23" />
+		<method name="getSelfManagedPhoneAccounts()Ljava/util/List;" since="26" />
 		<method name="getSimCallManager()Landroid/telecom/PhoneAccountHandle;" since="23" />
 		<method name="getVoiceMailNumber(Landroid/telecom/PhoneAccountHandle;)Ljava/lang/String;" since="23" />
 		<method name="handleMmi(Ljava/lang/String;)Z" />
 		<method name="handleMmi(Ljava/lang/String;Landroid/telecom/PhoneAccountHandle;)Z" since="23" />
 		<method name="isInCall()Z" />
+		<method name="isInManagedCall()Z" since="26" />
+		<method name="isIncomingCallPermitted(Landroid/telecom/PhoneAccountHandle;)Z" since="26" />
+		<method name="isOutgoingCallPermitted(Landroid/telecom/PhoneAccountHandle;)Z" since="26" />
 		<method name="isVoiceMailNumber(Landroid/telecom/PhoneAccountHandle;Ljava/lang/String;)Z" since="23" />
 		<method name="placeCall(Landroid/net/Uri;Landroid/os/Bundle;)V" since="23" />
 		<method name="registerPhoneAccount(Landroid/telecom/PhoneAccount;)V" since="23" />
@@ -37193,7 +39765,9 @@
 		<field name="ACTION_CHANGE_PHONE_ACCOUNTS" since="23" />
 		<field name="ACTION_CONFIGURE_PHONE_ACCOUNT" since="23" />
 		<field name="ACTION_DEFAULT_DIALER_CHANGED" since="23" />
-		<field name="ACTION_INCOMING_CALL" since="23" />
+		<field name="ACTION_INCOMING_CALL" since="23" deprecated="26" />
+		<field name="ACTION_PHONE_ACCOUNT_REGISTERED" since="26" />
+		<field name="ACTION_PHONE_ACCOUNT_UNREGISTERED" since="26" />
 		<field name="ACTION_SHOW_CALL_ACCESSIBILITY_SETTINGS" since="23" />
 		<field name="ACTION_SHOW_CALL_SETTINGS" />
 		<field name="ACTION_SHOW_MISSED_CALLS_NOTIFICATION" since="24" />
@@ -37207,15 +39781,18 @@
 		<field name="EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME" since="23" />
 		<field name="EXTRA_INCOMING_CALL_ADDRESS" since="23" />
 		<field name="EXTRA_INCOMING_CALL_EXTRAS" since="23" />
+		<field name="EXTRA_INCOMING_VIDEO_STATE" since="26" />
 		<field name="EXTRA_NOTIFICATION_COUNT" since="24" />
 		<field name="EXTRA_NOTIFICATION_PHONE_NUMBER" since="24" />
 		<field name="EXTRA_OUTGOING_CALL_EXTRAS" since="23" />
 		<field name="EXTRA_PHONE_ACCOUNT_HANDLE" since="23" />
+		<field name="EXTRA_START_CALL_WITH_RTT" since="26" />
 		<field name="EXTRA_START_CALL_WITH_SPEAKERPHONE" />
 		<field name="EXTRA_START_CALL_WITH_VIDEO_STATE" since="23" />
 		<field name="GATEWAY_ORIGINAL_ADDRESS" />
 		<field name="GATEWAY_PROVIDER_PACKAGE" />
 		<field name="METADATA_INCLUDE_EXTERNAL_CALLS" since="25" />
+		<field name="METADATA_INCLUDE_SELF_MANAGED_CALLS" since="26" />
 		<field name="METADATA_IN_CALL_SERVICE_RINGING" since="24" />
 		<field name="METADATA_IN_CALL_SERVICE_UI" since="23" />
 		<field name="PRESENTATION_ALLOWED" />
@@ -37263,6 +39840,7 @@
 		<method name="getConfigForSubId(I)Landroid/os/PersistableBundle;" />
 		<method name="notifyConfigChangedForSubId(I)V" />
 		<field name="ACTION_CARRIER_CONFIG_CHANGED" />
+		<field name="DATA_CYCLE_THRESHOLD_DISABLED" since="26" />
 		<field name="KEY_ADDITIONAL_CALL_SETTING_BOOL" />
 		<field name="KEY_ALLOW_ADDING_APNS_BOOL" since="24" />
 		<field name="KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL" since="25" />
@@ -37274,7 +39852,9 @@
 		<field name="KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL" since="24" />
 		<field name="KEY_APN_EXPAND_BOOL" />
 		<field name="KEY_AUTO_RETRY_ENABLED_BOOL" />
+		<field name="KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY" since="26" />
 		<field name="KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL" />
+		<field name="KEY_CARRIER_DATA_CALL_PERMANENT_FAILURE_STRINGS" since="26" />
 		<field name="KEY_CARRIER_FORCE_DISABLE_ETWS_CMAS_TEST_BOOL" since="24" />
 		<field name="KEY_CARRIER_IMS_GBA_REQUIRED_BOOL" since="24" />
 		<field name="KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL" since="24" />
@@ -37285,12 +39865,15 @@
 		<field name="KEY_CARRIER_SETTINGS_ENABLE_BOOL" />
 		<field name="KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL" since="24" />
 		<field name="KEY_CARRIER_VOLTE_AVAILABLE_BOOL" />
+		<field name="KEY_CARRIER_VOLTE_PROVISIONED_BOOL" since="26" />
 		<field name="KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL" />
 		<field name="KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL" />
 		<field name="KEY_CARRIER_VT_AVAILABLE_BOOL" />
-		<field name="KEY_CARRIER_VVM_PACKAGE_NAME_STRING" />
+		<field name="KEY_CARRIER_VVM_PACKAGE_NAME_STRING" deprecated="26" />
+		<field name="KEY_CARRIER_VVM_PACKAGE_NAME_STRING_ARRAY" since="26" />
 		<field name="KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL" />
 		<field name="KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL" since="24" />
+		<field name="KEY_CDMA_3WAYCALL_FLASH_DELAY_INT" since="26" />
 		<field name="KEY_CDMA_DTMF_TONE_DELAY_INT" since="24" />
 		<field name="KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY" />
 		<field name="KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY" />
@@ -37298,13 +39881,19 @@
 		<field name="KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING" since="24" />
 		<field name="KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING" since="24" />
 		<field name="KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING" since="24" />
+		<field name="KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING" since="26" />
 		<field name="KEY_CSP_ENABLED_BOOL" />
+		<field name="KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG" since="26" />
+		<field name="KEY_DATA_WARNING_THRESHOLD_BYTES_LONG" since="26" />
 		<field name="KEY_DEFAULT_SIM_CALL_MANAGER_STRING" />
+		<field name="KEY_DEFAULT_VM_NUMBER_STRING" since="26" />
+		<field name="KEY_DIAL_STRING_REPLACE_STRING_ARRAY" since="26" />
 		<field name="KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL" />
 		<field name="KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL" since="25" />
 		<field name="KEY_DTMF_TYPE_ENABLED_BOOL" />
 		<field name="KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT" since="24" />
 		<field name="KEY_EDITABLE_ENHANCED_4G_LTE_BOOL" since="24" />
+		<field name="KEY_EDITABLE_VOICEMAIL_NUMBER_BOOL" since="26" />
 		<field name="KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL" />
 		<field name="KEY_FORCE_HOME_NETWORK_BOOL" />
 		<field name="KEY_GSM_DTMF_TONE_DELAY_INT" since="24" />
@@ -37312,11 +39901,15 @@
 		<field name="KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY" />
 		<field name="KEY_HAS_IN_CALL_NOISE_SUPPRESSION_BOOL" />
 		<field name="KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL" />
+		<field name="KEY_HIDE_ENHANCED_4G_LTE_BOOL" since="26" />
 		<field name="KEY_HIDE_IMS_APN_BOOL" since="24" />
 		<field name="KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL" since="24" />
 		<field name="KEY_HIDE_SIM_LOCK_SETTINGS_BOOL" />
 		<field name="KEY_IGNORE_SIM_NETWORK_LOCKED_EVENTS_BOOL" />
+		<field name="KEY_IMS_CONFERENCE_SIZE_LIMIT_INT" since="26" />
 		<field name="KEY_IMS_DTMF_TONE_DELAY_INT" since="24" />
+		<field name="KEY_IS_IMS_CONFERENCE_SIZE_ENFORCED_BOOL" since="26" />
+		<field name="KEY_MDN_IS_ADDITIONAL_VOICEMAIL_NUMBER_BOOL" since="26" />
 		<field name="KEY_MMS_ALIAS_ENABLED_BOOL" />
 		<field name="KEY_MMS_ALIAS_MAX_CHARS_INT" />
 		<field name="KEY_MMS_ALIAS_MIN_CHARS_INT" />
@@ -37348,14 +39941,21 @@
 		<field name="KEY_MMS_UA_PROF_TAG_NAME_STRING" />
 		<field name="KEY_MMS_UA_PROF_URL_STRING" />
 		<field name="KEY_MMS_USER_AGENT_STRING" />
+		<field name="KEY_MONTHLY_DATA_CYCLE_DAY_INT" since="26" />
+		<field name="KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY" since="26" />
 		<field name="KEY_OPERATOR_SELECTION_EXPAND_BOOL" />
 		<field name="KEY_PREFER_2G_BOOL" />
+		<field name="KEY_RCS_CONFIG_SERVER_URL_STRING" since="26" />
 		<field name="KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL" since="24" />
+		<field name="KEY_RESTART_RADIO_ON_PDP_FAIL_REGULAR_DEACTIVATION_BOOL" since="26" />
 		<field name="KEY_SHOW_APN_SETTING_CDMA_BOOL" />
 		<field name="KEY_SHOW_CDMA_CHOICES_BOOL" />
 		<field name="KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL" since="24" />
 		<field name="KEY_SHOW_ONSCREEN_DIAL_BUTTON_BOOL" />
+		<field name="KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL" since="26" />
 		<field name="KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL" />
+		<field name="KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL" since="26" />
+		<field name="KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL" since="26" />
 		<field name="KEY_SUPPORT_CONFERENCE_CALL_BOOL" since="24" />
 		<field name="KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL" />
 		<field name="KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL" />
@@ -37367,9 +39967,13 @@
 		<field name="KEY_VOICE_PRIVACY_DISABLE_UI_BOOL" />
 		<field name="KEY_VOLTE_REPLACEMENT_RAT_INT" />
 		<field name="KEY_VVM_CELLULAR_DATA_REQUIRED_BOOL" since="24" />
+		<field name="KEY_VVM_CLIENT_PREFIX_STRING" since="26" />
 		<field name="KEY_VVM_DESTINATION_NUMBER_STRING" />
+		<field name="KEY_VVM_DISABLED_CAPABILITIES_STRING_ARRAY" since="26" />
+		<field name="KEY_VVM_LEGACY_MODE_ENABLED_BOOL" since="26" />
 		<field name="KEY_VVM_PORT_NUMBER_INT" />
 		<field name="KEY_VVM_PREFETCH_BOOL" since="24" />
+		<field name="KEY_VVM_SSL_ENABLED_BOOL" since="26" />
 		<field name="KEY_VVM_TYPE_STRING" />
 		<field name="KEY_WORLD_PHONE_BOOL" />
 	</class>
@@ -37492,12 +40096,17 @@
 		<extends name="android/telephony/CellSignalStrength" />
 		<implements name="android/os/Parcelable" />
 		<method name="&lt;init>()V" />
+		<method name="getTimingAdvance()I" since="26" />
 		<field name="CREATOR" />
 	</class>
 	<class name="android/telephony/CellSignalStrengthLte" since="17">
 		<extends name="android/telephony/CellSignalStrength" />
 		<implements name="android/os/Parcelable" />
 		<method name="&lt;init>()V" />
+		<method name="getCqi()I" since="26" />
+		<method name="getRsrp()I" since="26" />
+		<method name="getRsrq()I" since="26" />
+		<method name="getRssnr()I" since="26" />
 		<method name="getTimingAdvance()I" />
 		<field name="CREATOR" />
 	</class>
@@ -37666,6 +40275,7 @@
 	<class name="android/telephony/SmsManager" since="4">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
+		<method name="createAppSpecificSmsToken(Landroid/app/PendingIntent;)Ljava/lang/String;" since="26" />
 		<method name="divideMessage(Ljava/lang/String;)Ljava/util/ArrayList;" />
 		<method name="downloadMultimediaMessage(Landroid/content/Context;Ljava/lang/String;Landroid/net/Uri;Landroid/os/Bundle;Landroid/app/PendingIntent;)V" since="21" />
 		<method name="getCarrierConfigValues()Landroid/os/Bundle;" since="21" />
@@ -37824,8 +40434,11 @@
 		<method name="getDefaultVoiceSubscriptionId()I" since="24" />
 		<method name="isNetworkRoaming(I)Z" />
 		<method name="removeOnSubscriptionsChangedListener(Landroid/telephony/SubscriptionManager$OnSubscriptionsChangedListener;)V" />
+		<field name="ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED" since="26" />
+		<field name="ACTION_DEFAULT_SUBSCRIPTION_CHANGED" since="26" />
 		<field name="DATA_ROAMING_DISABLE" />
 		<field name="DATA_ROAMING_ENABLE" />
+		<field name="EXTRA_SUBSCRIPTION_INDEX" since="26" />
 		<field name="INVALID_SUBSCRIPTION_ID" since="24" />
 	</class>
 	<class name="android/telephony/SubscriptionManager$OnSubscriptionsChangedListener" since="22">
@@ -37837,34 +40450,45 @@
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
 		<method name="canChangeDtmfToneLength()Z" since="23" />
+		<method name="createForPhoneAccountHandle(Landroid/telecom/PhoneAccountHandle;)Landroid/telephony/TelephonyManager;" since="26" />
 		<method name="createForSubscriptionId(I)Landroid/telephony/TelephonyManager;" since="24" />
 		<method name="getAllCellInfo()Ljava/util/List;" since="17" />
 		<method name="getCallState()I" />
-		<method name="getCellLocation()Landroid/telephony/CellLocation;" />
+		<method name="getCarrierConfig()Landroid/os/PersistableBundle;" since="26" />
+		<method name="getCellLocation()Landroid/telephony/CellLocation;" deprecated="26" />
 		<method name="getDataActivity()I" />
 		<method name="getDataNetworkType()I" since="24" />
 		<method name="getDataState()I" />
-		<method name="getDeviceId()Ljava/lang/String;" />
-		<method name="getDeviceId(I)Ljava/lang/String;" since="23" />
+		<method name="getDeviceId()Ljava/lang/String;" deprecated="26" />
+		<method name="getDeviceId(I)Ljava/lang/String;" since="23" deprecated="26" />
 		<method name="getDeviceSoftwareVersion()Ljava/lang/String;" />
+		<method name="getForbiddenPlmns()[Ljava/lang/String;" since="26" />
 		<method name="getGroupIdLevel1()Ljava/lang/String;" since="18" />
 		<method name="getIccAuthentication(IILjava/lang/String;)Ljava/lang/String;" since="24" />
+		<method name="getImei()Ljava/lang/String;" since="26" />
+		<method name="getImei(I)Ljava/lang/String;" since="26" />
 		<method name="getLine1Number()Ljava/lang/String;" />
+		<method name="getMeid()Ljava/lang/String;" since="26" />
+		<method name="getMeid(I)Ljava/lang/String;" since="26" />
 		<method name="getMmsUAProfUrl()Ljava/lang/String;" since="19" />
 		<method name="getMmsUserAgent()Ljava/lang/String;" since="19" />
 		<method name="getNeighboringCellInfo()Ljava/util/List;" since="3" deprecated="23" />
 		<method name="getNetworkCountryIso()Ljava/lang/String;" />
 		<method name="getNetworkOperator()Ljava/lang/String;" />
 		<method name="getNetworkOperatorName()Ljava/lang/String;" />
+		<method name="getNetworkSpecifier()Ljava/lang/String;" since="26" />
 		<method name="getNetworkType()I" />
 		<method name="getPhoneCount()I" since="23" />
 		<method name="getPhoneType()I" />
+		<method name="getServiceState()Landroid/telephony/ServiceState;" since="26" />
 		<method name="getSimCountryIso()Ljava/lang/String;" />
 		<method name="getSimOperator()Ljava/lang/String;" />
 		<method name="getSimOperatorName()Ljava/lang/String;" />
 		<method name="getSimSerialNumber()Ljava/lang/String;" />
 		<method name="getSimState()I" />
+		<method name="getSimState(I)I" since="26" />
 		<method name="getSubscriberId()Ljava/lang/String;" />
+		<method name="getVisualVoicemailPackageName()Ljava/lang/String;" since="26" />
 		<method name="getVoiceMailAlphaTag()Ljava/lang/String;" />
 		<method name="getVoiceMailNumber()Ljava/lang/String;" />
 		<method name="getVoiceNetworkType()I" since="24" />
@@ -37873,9 +40497,12 @@
 		<method name="hasIccCard()Z" since="5" />
 		<method name="iccCloseLogicalChannel(I)Z" since="21" />
 		<method name="iccExchangeSimIO(IIIIILjava/lang/String;)[B" since="21" />
-		<method name="iccOpenLogicalChannel(Ljava/lang/String;)Landroid/telephony/IccOpenLogicalChannelResponse;" since="21" />
+		<method name="iccOpenLogicalChannel(Ljava/lang/String;)Landroid/telephony/IccOpenLogicalChannelResponse;" since="21" deprecated="26" />
+		<method name="iccOpenLogicalChannel(Ljava/lang/String;I)Landroid/telephony/IccOpenLogicalChannelResponse;" since="26" />
 		<method name="iccTransmitApduBasicChannel(IIIIILjava/lang/String;)Ljava/lang/String;" since="21" />
 		<method name="iccTransmitApduLogicalChannel(IIIIIILjava/lang/String;)Ljava/lang/String;" since="21" />
+		<method name="isConcurrentVoiceAndDataSupported()Z" since="26" />
+		<method name="isDataEnabled()Z" since="26" />
 		<method name="isHearingAidCompatibilitySupported()Z" since="23" />
 		<method name="isNetworkRoaming()Z" />
 		<method name="isSmsCapable()Z" since="21" />
@@ -37884,14 +40511,22 @@
 		<method name="isVoicemailVibrationEnabled(Landroid/telecom/PhoneAccountHandle;)Z" since="24" />
 		<method name="isWorldPhone()Z" since="23" />
 		<method name="listen(Landroid/telephony/PhoneStateListener;I)V" />
+		<method name="sendDialerSpecialCode(Ljava/lang/String;)V" since="26" />
 		<method name="sendEnvelopeWithStatus(Ljava/lang/String;)Ljava/lang/String;" since="21" />
+		<method name="sendUssdRequest(Ljava/lang/String;Landroid/telephony/TelephonyManager$UssdResponseCallback;Landroid/os/Handler;)V" since="26" />
+		<method name="sendVisualVoicemailSms(Ljava/lang/String;ILjava/lang/String;Landroid/app/PendingIntent;)V" since="26" />
+		<method name="setDataEnabled(Z)V" since="26" />
 		<method name="setLine1NumberForDisplay(Ljava/lang/String;Ljava/lang/String;)Z" since="22" />
 		<method name="setOperatorBrandOverride(Ljava/lang/String;)Z" since="22" />
 		<method name="setPreferredNetworkTypeToGlobal()Z" since="22" />
+		<method name="setVisualVoicemailSmsFilterSettings(Landroid/telephony/VisualVoicemailSmsFilterSettings;)V" since="26" />
 		<method name="setVoiceMailNumber(Ljava/lang/String;Ljava/lang/String;)Z" since="22" />
+		<method name="setVoicemailRingtoneUri(Landroid/telecom/PhoneAccountHandle;Landroid/net/Uri;)V" since="26" />
+		<method name="setVoicemailVibrationEnabled(Landroid/telecom/PhoneAccountHandle;Z)V" since="26" />
 		<field name="ACTION_CONFIGURE_VOICEMAIL" since="23" />
 		<field name="ACTION_PHONE_STATE_CHANGED" since="3" />
 		<field name="ACTION_RESPOND_VIA_MESSAGE" since="18" />
+		<field name="ACTION_SHOW_VOICEMAIL_NOTIFICATION" since="26" />
 		<field name="APPTYPE_CSIM" since="24" />
 		<field name="APPTYPE_ISIM" since="24" />
 		<field name="APPTYPE_RUIM" since="24" />
@@ -37911,11 +40546,18 @@
 		<field name="DATA_CONNECTING" />
 		<field name="DATA_DISCONNECTED" />
 		<field name="DATA_SUSPENDED" />
+		<field name="EXTRA_CALL_VOICEMAIL_INTENT" since="26" />
+		<field name="EXTRA_HIDE_PUBLIC_SETTINGS" since="26" />
 		<field name="EXTRA_INCOMING_NUMBER" since="3" />
+		<field name="EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT" since="26" />
+		<field name="EXTRA_NOTIFICATION_COUNT" since="26" />
+		<field name="EXTRA_PHONE_ACCOUNT_HANDLE" since="26" />
 		<field name="EXTRA_STATE" since="3" />
 		<field name="EXTRA_STATE_IDLE" since="3" />
 		<field name="EXTRA_STATE_OFFHOOK" since="3" />
 		<field name="EXTRA_STATE_RINGING" since="3" />
+		<field name="EXTRA_VOICEMAIL_NUMBER" since="26" />
+		<field name="METADATA_HIDE_VOICEMAIL_SETTINGS_MENU" since="26" />
 		<field name="NETWORK_TYPE_1xRTT" since="4" />
 		<field name="NETWORK_TYPE_CDMA" since="4" />
 		<field name="NETWORK_TYPE_EDGE" />
@@ -37940,14 +40582,69 @@
 		<field name="PHONE_TYPE_NONE" />
 		<field name="PHONE_TYPE_SIP" since="11" />
 		<field name="SIM_STATE_ABSENT" />
+		<field name="SIM_STATE_CARD_IO_ERROR" since="26" />
+		<field name="SIM_STATE_CARD_RESTRICTED" since="26" />
 		<field name="SIM_STATE_NETWORK_LOCKED" />
+		<field name="SIM_STATE_NOT_READY" since="26" />
+		<field name="SIM_STATE_PERM_DISABLED" since="26" />
 		<field name="SIM_STATE_PIN_REQUIRED" />
 		<field name="SIM_STATE_PUK_REQUIRED" />
 		<field name="SIM_STATE_READY" />
 		<field name="SIM_STATE_UNKNOWN" />
+		<field name="USSD_ERROR_SERVICE_UNAVAIL" since="26" />
+		<field name="USSD_RETURN_FAILURE" since="26" />
 		<field name="VVM_TYPE_CVVM" since="23" />
 		<field name="VVM_TYPE_OMTP" since="23" />
 	</class>
+	<class name="android/telephony/TelephonyManager$UssdResponseCallback" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="onReceiveUssdResponse(Landroid/telephony/TelephonyManager;Ljava/lang/String;Ljava/lang/CharSequence;)V" />
+		<method name="onReceiveUssdResponseFailed(Landroid/telephony/TelephonyManager;Ljava/lang/String;I)V" />
+	</class>
+	<class name="android/telephony/VisualVoicemailService" since="26">
+		<extends name="android/app/Service" />
+		<method name="&lt;init>()V" />
+		<method name="onCellServiceConnected(Landroid/telephony/VisualVoicemailService$VisualVoicemailTask;Landroid/telecom/PhoneAccountHandle;)V" />
+		<method name="onSimRemoved(Landroid/telephony/VisualVoicemailService$VisualVoicemailTask;Landroid/telecom/PhoneAccountHandle;)V" />
+		<method name="onSmsReceived(Landroid/telephony/VisualVoicemailService$VisualVoicemailTask;Landroid/telephony/VisualVoicemailSms;)V" />
+		<method name="onStopped(Landroid/telephony/VisualVoicemailService$VisualVoicemailTask;)V" />
+		<field name="SERVICE_INTERFACE" />
+	</class>
+	<class name="android/telephony/VisualVoicemailService$VisualVoicemailTask" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="finish()V" />
+	</class>
+	<class name="android/telephony/VisualVoicemailSms" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/os/Parcelable" />
+		<method name="&lt;init>()V" />
+		<method name="getFields()Landroid/os/Bundle;" />
+		<method name="getMessageBody()Ljava/lang/String;" />
+		<method name="getPhoneAccountHandle()Landroid/telecom/PhoneAccountHandle;" />
+		<method name="getPrefix()Ljava/lang/String;" />
+		<field name="CREATOR" />
+	</class>
+	<class name="android/telephony/VisualVoicemailSmsFilterSettings" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/os/Parcelable" />
+		<method name="&lt;init>()V" />
+		<field name="CREATOR" />
+		<field name="DESTINATION_PORT_ANY" />
+		<field name="DESTINATION_PORT_DATA_SMS" />
+		<field name="clientPrefix" />
+		<field name="destinationPort" />
+		<field name="originatingNumbers" />
+	</class>
+	<class name="android/telephony/VisualVoicemailSmsFilterSettings$Builder" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="build()Landroid/telephony/VisualVoicemailSmsFilterSettings;" />
+		<method name="setClientPrefix(Ljava/lang/String;)Landroid/telephony/VisualVoicemailSmsFilterSettings$Builder;" />
+		<method name="setDestinationPort(I)Landroid/telephony/VisualVoicemailSmsFilterSettings$Builder;" />
+		<method name="setOriginatingNumbers(Ljava/util/List;)Landroid/telephony/VisualVoicemailSmsFilterSettings$Builder;" />
+	</class>
 	<class name="android/telephony/cdma/CdmaCellLocation" since="5">
 		<extends name="android/telephony/CellLocation" />
 		<method name="&lt;init>()V" />
@@ -38407,7 +41104,7 @@
 		<extends name="android/content/res/Resources" />
 		<method name="&lt;init>()V" />
 	</class>
-	<class name="android/test/suitebuilder/TestMethod" since="1">
+	<class name="android/test/suitebuilder/TestMethod" since="1" deprecated="26">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>(Ljava/lang/String;Ljava/lang/Class;)V" since="3" />
 		<method name="&lt;init>(Ljava/lang/reflect/Method;Ljava/lang/Class;)V" />
@@ -38418,7 +41115,7 @@
 		<method name="getEnclosingClassname()Ljava/lang/String;" />
 		<method name="getName()Ljava/lang/String;" />
 	</class>
-	<class name="android/test/suitebuilder/TestSuiteBuilder" since="1">
+	<class name="android/test/suitebuilder/TestSuiteBuilder" since="1" deprecated="26">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>(Ljava/lang/Class;)V" />
 		<method name="&lt;init>(Ljava/lang/String;Ljava/lang/ClassLoader;)V" />
@@ -38431,7 +41128,7 @@
 		<method name="includePackages([Ljava/lang/String;)Landroid/test/suitebuilder/TestSuiteBuilder;" />
 		<method name="named(Ljava/lang/String;)Landroid/test/suitebuilder/TestSuiteBuilder;" />
 	</class>
-	<class name="android/test/suitebuilder/TestSuiteBuilder$FailedToCreateTests" since="1">
+	<class name="android/test/suitebuilder/TestSuiteBuilder$FailedToCreateTests" since="1" deprecated="26">
 		<extends name="junit/framework/TestCase" />
 		<method name="&lt;init>(Ljava/lang/Exception;)V" />
 		<method name="testSuiteConstructionFailed()V" />
@@ -38463,7 +41160,7 @@
 		<method name="&lt;init>()V" />
 		<method name="make(Ljava/lang/CharSequence;[CII)Landroid/text/AlteredCharSequence;" />
 	</class>
-	<class name="android/text/AndroidCharacter" since="1">
+	<class name="android/text/AndroidCharacter" since="1" deprecated="26">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
 		<method name="getDirectionalities([C[BI)V" />
@@ -38499,8 +41196,13 @@
 		<method name="getInstance(Ljava/util/Locale;)Landroid/text/BidiFormatter;" />
 		<method name="getInstance(Z)Landroid/text/BidiFormatter;" />
 		<method name="getStereoReset()Z" />
+		<method name="isRtl(Ljava/lang/CharSequence;)Z" since="26" />
 		<method name="isRtl(Ljava/lang/String;)Z" />
 		<method name="isRtlContext()Z" />
+		<method name="unicodeWrap(Ljava/lang/CharSequence;)Ljava/lang/CharSequence;" since="26" />
+		<method name="unicodeWrap(Ljava/lang/CharSequence;Landroid/text/TextDirectionHeuristic;)Ljava/lang/CharSequence;" since="26" />
+		<method name="unicodeWrap(Ljava/lang/CharSequence;Landroid/text/TextDirectionHeuristic;Z)Ljava/lang/CharSequence;" since="26" />
+		<method name="unicodeWrap(Ljava/lang/CharSequence;Z)Ljava/lang/CharSequence;" since="26" />
 		<method name="unicodeWrap(Ljava/lang/String;)Ljava/lang/String;" />
 		<method name="unicodeWrap(Ljava/lang/String;Landroid/text/TextDirectionHeuristic;)Ljava/lang/String;" />
 		<method name="unicodeWrap(Ljava/lang/String;Landroid/text/TextDirectionHeuristic;Z)Ljava/lang/String;" />
@@ -38720,6 +41422,8 @@
 		<field name="HYPHENATION_FREQUENCY_FULL" since="23" />
 		<field name="HYPHENATION_FREQUENCY_NONE" since="23" />
 		<field name="HYPHENATION_FREQUENCY_NORMAL" since="23" />
+		<field name="JUSTIFICATION_MODE_INTER_WORD" since="26" />
+		<field name="JUSTIFICATION_MODE_NONE" since="26" />
 	</class>
 	<class name="android/text/Layout$Alignment" since="1">
 		<extends name="java/lang/Enum" />
@@ -38908,6 +41612,7 @@
 		<method name="setHyphenationFrequency(I)Landroid/text/StaticLayout$Builder;" />
 		<method name="setIncludePad(Z)Landroid/text/StaticLayout$Builder;" />
 		<method name="setIndents([I[I)Landroid/text/StaticLayout$Builder;" />
+		<method name="setJustificationMode(I)Landroid/text/StaticLayout$Builder;" since="26" />
 		<method name="setLineSpacing(FF)Landroid/text/StaticLayout$Builder;" />
 		<method name="setMaxLines(I)Landroid/text/StaticLayout$Builder;" />
 		<method name="setText(Ljava/lang/CharSequence;)Landroid/text/StaticLayout$Builder;" />
@@ -38943,7 +41648,7 @@
 	<class name="android/text/TextUtils" since="1">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
-		<method name="commaEllipsize(Ljava/lang/CharSequence;Landroid/text/TextPaint;FLjava/lang/String;Ljava/lang/String;)Ljava/lang/CharSequence;" />
+		<method name="commaEllipsize(Ljava/lang/CharSequence;Landroid/text/TextPaint;FLjava/lang/String;Ljava/lang/String;)Ljava/lang/CharSequence;" deprecated="26" />
 		<method name="concat([Ljava/lang/CharSequence;)Ljava/lang/CharSequence;" />
 		<method name="copySpansFrom(Landroid/text/Spanned;IILjava/lang/Class;Landroid/text/Spannable;I)V" />
 		<method name="dumpSpans(Ljava/lang/CharSequence;Landroid/util/Printer;Ljava/lang/String;)V" since="3" />
@@ -38974,6 +41679,7 @@
 		<method name="lastIndexOf(Ljava/lang/CharSequence;C)I" />
 		<method name="lastIndexOf(Ljava/lang/CharSequence;CI)I" />
 		<method name="lastIndexOf(Ljava/lang/CharSequence;CII)I" />
+		<method name="listEllipsize(Landroid/content/Context;Ljava/util/List;Ljava/lang/String;Landroid/text/TextPaint;FI)Ljava/lang/CharSequence;" since="26" />
 		<method name="regionMatches(Ljava/lang/CharSequence;ILjava/lang/CharSequence;II)Z" />
 		<method name="replace(Ljava/lang/CharSequence;[Ljava/lang/String;[Ljava/lang/CharSequence;)Ljava/lang/CharSequence;" />
 		<method name="split(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;" />
@@ -39220,15 +41926,19 @@
 	</class>
 	<class name="android/text/method/DateKeyListener" since="1">
 		<extends name="android/text/method/NumberKeyListener" />
-		<method name="&lt;init>()V" />
-		<method name="getInstance()Landroid/text/method/DateKeyListener;" />
-		<field name="CHARACTERS" />
+		<method name="&lt;init>()V" deprecated="26" />
+		<method name="&lt;init>(Ljava/util/Locale;)V" since="26" />
+		<method name="getInstance()Landroid/text/method/DateKeyListener;" deprecated="26" />
+		<method name="getInstance(Ljava/util/Locale;)Landroid/text/method/DateKeyListener;" since="26" />
+		<field name="CHARACTERS" deprecated="26" />
 	</class>
 	<class name="android/text/method/DateTimeKeyListener" since="1">
 		<extends name="android/text/method/NumberKeyListener" />
-		<method name="&lt;init>()V" />
-		<method name="getInstance()Landroid/text/method/DateTimeKeyListener;" />
-		<field name="CHARACTERS" />
+		<method name="&lt;init>()V" deprecated="26" />
+		<method name="&lt;init>(Ljava/util/Locale;)V" since="26" />
+		<method name="getInstance()Landroid/text/method/DateTimeKeyListener;" deprecated="26" />
+		<method name="getInstance(Ljava/util/Locale;)Landroid/text/method/DateTimeKeyListener;" since="26" />
+		<field name="CHARACTERS" deprecated="26" />
 	</class>
 	<class name="android/text/method/DialerKeyListener" since="1">
 		<extends name="android/text/method/NumberKeyListener" />
@@ -39238,11 +41948,15 @@
 	</class>
 	<class name="android/text/method/DigitsKeyListener" since="1">
 		<extends name="android/text/method/NumberKeyListener" />
-		<method name="&lt;init>()V" />
-		<method name="&lt;init>(ZZ)V" />
-		<method name="getInstance()Landroid/text/method/DigitsKeyListener;" />
+		<method name="&lt;init>()V" deprecated="26" />
+		<method name="&lt;init>(Ljava/util/Locale;)V" since="26" />
+		<method name="&lt;init>(Ljava/util/Locale;ZZ)V" since="26" />
+		<method name="&lt;init>(ZZ)V" deprecated="26" />
+		<method name="getInstance()Landroid/text/method/DigitsKeyListener;" deprecated="26" />
 		<method name="getInstance(Ljava/lang/String;)Landroid/text/method/DigitsKeyListener;" />
-		<method name="getInstance(ZZ)Landroid/text/method/DigitsKeyListener;" />
+		<method name="getInstance(Ljava/util/Locale;)Landroid/text/method/DigitsKeyListener;" since="26" />
+		<method name="getInstance(Ljava/util/Locale;ZZ)Landroid/text/method/DigitsKeyListener;" since="26" />
+		<method name="getInstance(ZZ)Landroid/text/method/DigitsKeyListener;" deprecated="26" />
 	</class>
 	<class name="android/text/method/HideReturnsTransformationMethod" since="1">
 		<extends name="android/text/method/ReplacementTransformationMethod" />
@@ -39375,9 +42089,11 @@
 	</class>
 	<class name="android/text/method/TimeKeyListener" since="1">
 		<extends name="android/text/method/NumberKeyListener" />
-		<method name="&lt;init>()V" />
-		<method name="getInstance()Landroid/text/method/TimeKeyListener;" />
-		<field name="CHARACTERS" />
+		<method name="&lt;init>()V" deprecated="26" />
+		<method name="&lt;init>(Ljava/util/Locale;)V" since="26" />
+		<method name="getInstance()Landroid/text/method/TimeKeyListener;" deprecated="26" />
+		<method name="getInstance(Ljava/util/Locale;)Landroid/text/method/TimeKeyListener;" since="26" />
+		<field name="CHARACTERS" deprecated="26" />
 	</class>
 	<class name="android/text/method/Touch" since="1">
 		<extends name="java/lang/Object" />
@@ -40148,6 +42864,11 @@
 		<method name="inflateTransition(I)Landroid/transition/Transition;" />
 		<method name="inflateTransitionManager(ILandroid/view/ViewGroup;)Landroid/transition/TransitionManager;" />
 	</class>
+	<class name="android/transition/TransitionListenerAdapter" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/transition/Transition$TransitionListener" />
+		<method name="&lt;init>()V" />
+	</class>
 	<class name="android/transition/TransitionManager" since="19">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
@@ -40428,6 +43149,63 @@
 		<method name="set(Ljava/lang/Object;Ljava/lang/Float;)V" />
 		<method name="setValue(Ljava/lang/Object;F)V" />
 	</class>
+	<class name="android/util/Half" since="26">
+		<extends name="java/lang/Number" />
+		<implements name="java/lang/Comparable" />
+		<method name="&lt;init>(D)V" />
+		<method name="&lt;init>(F)V" />
+		<method name="&lt;init>(Ljava/lang/String;)V" />
+		<method name="&lt;init>(S)V" />
+		<method name="abs(S)S" />
+		<method name="ceil(S)S" />
+		<method name="compare(SS)I" />
+		<method name="compareTo(Landroid/util/Half;)I" />
+		<method name="copySign(SS)S" />
+		<method name="equals(SS)Z" />
+		<method name="floor(S)S" />
+		<method name="getExponent(S)I" />
+		<method name="getSign(S)I" />
+		<method name="getSignificand(S)I" />
+		<method name="greater(SS)Z" />
+		<method name="greaterEquals(SS)Z" />
+		<method name="halfToIntBits(S)I" />
+		<method name="halfToRawIntBits(S)I" />
+		<method name="halfToShortBits(S)S" />
+		<method name="halfValue()S" />
+		<method name="hashCode(S)I" />
+		<method name="intBitsToHalf(I)S" />
+		<method name="isInfinite(S)Z" />
+		<method name="isNaN()Z" />
+		<method name="isNaN(S)Z" />
+		<method name="isNormalized(S)Z" />
+		<method name="less(SS)Z" />
+		<method name="lessEquals(SS)Z" />
+		<method name="max(SS)S" />
+		<method name="min(SS)S" />
+		<method name="parseHalf(Ljava/lang/String;)S" />
+		<method name="round(S)S" />
+		<method name="toFloat(S)F" />
+		<method name="toHalf(F)S" />
+		<method name="toHexString(S)Ljava/lang/String;" />
+		<method name="toString(S)Ljava/lang/String;" />
+		<method name="trunc(S)S" />
+		<method name="valueOf(F)Landroid/util/Half;" />
+		<method name="valueOf(Ljava/lang/String;)Landroid/util/Half;" />
+		<method name="valueOf(S)Landroid/util/Half;" />
+		<field name="EPSILON" />
+		<field name="LOWEST_VALUE" />
+		<field name="MAX_EXPONENT" />
+		<field name="MAX_VALUE" />
+		<field name="MIN_EXPONENT" />
+		<field name="MIN_NORMAL" />
+		<field name="MIN_VALUE" />
+		<field name="NEGATIVE_INFINITY" />
+		<field name="NEGATIVE_ZERO" />
+		<field name="NaN" />
+		<field name="POSITIVE_INFINITY" />
+		<field name="POSITIVE_ZERO" />
+		<field name="SIZE" />
+	</class>
 	<class name="android/util/IntProperty" since="24">
 		<extends name="android/util/Property" />
 		<method name="&lt;init>(Ljava/lang/String;)V" />
@@ -41060,7 +43838,9 @@
 		<method name="getSupportedModes()[Landroid/view/Display$Mode;" since="23" />
 		<method name="getSupportedRefreshRates()[F" since="21" deprecated="23" />
 		<method name="getWidth()I" deprecated="16" />
+		<method name="isHdr()Z" since="26" />
 		<method name="isValid()Z" since="17" />
+		<method name="isWideColorGamut()Z" since="26" />
 		<field name="DEFAULT_DISPLAY" />
 		<field name="FLAG_PRESENTATION" since="19" />
 		<field name="FLAG_PRIVATE" since="19" />
@@ -41074,6 +43854,7 @@
 		<field name="STATE_OFF" since="20" />
 		<field name="STATE_ON" since="20" />
 		<field name="STATE_UNKNOWN" since="20" />
+		<field name="STATE_VR" since="26" />
 	</class>
 	<class name="android/view/Display$HdrCapabilities" since="24">
 		<extends name="java/lang/Object" />
@@ -41131,6 +43912,7 @@
 		<method name="findNearestTouchable(Landroid/view/ViewGroup;III[I)Landroid/view/View;" />
 		<method name="findNextFocus(Landroid/view/ViewGroup;Landroid/view/View;I)Landroid/view/View;" />
 		<method name="findNextFocusFromRect(Landroid/view/ViewGroup;Landroid/graphics/Rect;I)Landroid/view/View;" />
+		<method name="findNextKeyboardNavigationCluster(Landroid/view/View;Landroid/view/View;I)Landroid/view/View;" since="26" />
 		<method name="getInstance()Landroid/view/FocusFinder;" />
 	</class>
 	<class name="android/view/FrameMetrics" since="24">
@@ -41142,11 +43924,13 @@
 		<field name="DRAW_DURATION" />
 		<field name="FIRST_DRAW_FRAME" />
 		<field name="INPUT_HANDLING_DURATION" />
+		<field name="INTENDED_VSYNC_TIMESTAMP" since="26" />
 		<field name="LAYOUT_MEASURE_DURATION" />
 		<field name="SWAP_BUFFERS_DURATION" />
 		<field name="SYNC_DURATION" />
 		<field name="TOTAL_DURATION" />
 		<field name="UNKNOWN_DELAY_DURATION" />
+		<field name="VSYNC_TIMESTAMP" since="26" />
 	</class>
 	<class name="android/view/FrameStats" since="21">
 		<extends name="java/lang/Object" />
@@ -41307,6 +44091,8 @@
 		<field name="SOURCE_JOYSTICK" since="12" />
 		<field name="SOURCE_KEYBOARD" />
 		<field name="SOURCE_MOUSE" />
+		<field name="SOURCE_MOUSE_RELATIVE" since="26" />
+		<field name="SOURCE_ROTARY_ENCODER" since="26" />
 		<field name="SOURCE_STYLUS" since="14" />
 		<field name="SOURCE_TOUCHPAD" />
 		<field name="SOURCE_TOUCHSCREEN" />
@@ -41887,6 +44673,7 @@
 		<field name="FLAG_APPEND_TO_GROUP" />
 		<field name="FLAG_PERFORM_NO_CLOSE" />
 		<field name="NONE" />
+		<field name="SUPPORTED_MODIFIERS_MASK" since="26" />
 	</class>
 	<class name="android/view/MenuInflater" since="1">
 		<extends name="java/lang/Object" />
@@ -41899,17 +44686,23 @@
 		<method name="expandActionView()Z" since="14" />
 		<method name="getActionProvider()Landroid/view/ActionProvider;" since="14" />
 		<method name="getActionView()Landroid/view/View;" since="11" />
+		<method name="getAlphabeticModifiers()I" since="26" />
 		<method name="getAlphabeticShortcut()C" />
+		<method name="getContentDescription()Ljava/lang/CharSequence;" since="26" />
 		<method name="getGroupId()I" />
 		<method name="getIcon()Landroid/graphics/drawable/Drawable;" />
+		<method name="getIconTintList()Landroid/content/res/ColorStateList;" since="26" />
+		<method name="getIconTintMode()Landroid/graphics/PorterDuff$Mode;" since="26" />
 		<method name="getIntent()Landroid/content/Intent;" />
 		<method name="getItemId()I" />
 		<method name="getMenuInfo()Landroid/view/ContextMenu$ContextMenuInfo;" />
+		<method name="getNumericModifiers()I" since="26" />
 		<method name="getNumericShortcut()C" />
 		<method name="getOrder()I" />
 		<method name="getSubMenu()Landroid/view/SubMenu;" />
 		<method name="getTitle()Ljava/lang/CharSequence;" />
 		<method name="getTitleCondensed()Ljava/lang/CharSequence;" />
+		<method name="getTooltipText()Ljava/lang/CharSequence;" since="26" />
 		<method name="hasSubMenu()Z" />
 		<method name="isActionViewExpanded()Z" since="14" />
 		<method name="isCheckable()Z" />
@@ -41920,21 +44713,28 @@
 		<method name="setActionView(I)Landroid/view/MenuItem;" since="11" />
 		<method name="setActionView(Landroid/view/View;)Landroid/view/MenuItem;" since="11" />
 		<method name="setAlphabeticShortcut(C)Landroid/view/MenuItem;" />
+		<method name="setAlphabeticShortcut(CI)Landroid/view/MenuItem;" since="26" />
 		<method name="setCheckable(Z)Landroid/view/MenuItem;" />
 		<method name="setChecked(Z)Landroid/view/MenuItem;" />
+		<method name="setContentDescription(Ljava/lang/CharSequence;)Landroid/view/MenuItem;" since="26" />
 		<method name="setEnabled(Z)Landroid/view/MenuItem;" />
 		<method name="setIcon(I)Landroid/view/MenuItem;" />
 		<method name="setIcon(Landroid/graphics/drawable/Drawable;)Landroid/view/MenuItem;" />
+		<method name="setIconTintList(Landroid/content/res/ColorStateList;)Landroid/view/MenuItem;" since="26" />
+		<method name="setIconTintMode(Landroid/graphics/PorterDuff$Mode;)Landroid/view/MenuItem;" since="26" />
 		<method name="setIntent(Landroid/content/Intent;)Landroid/view/MenuItem;" />
 		<method name="setNumericShortcut(C)Landroid/view/MenuItem;" />
+		<method name="setNumericShortcut(CI)Landroid/view/MenuItem;" since="26" />
 		<method name="setOnActionExpandListener(Landroid/view/MenuItem$OnActionExpandListener;)Landroid/view/MenuItem;" since="14" />
 		<method name="setOnMenuItemClickListener(Landroid/view/MenuItem$OnMenuItemClickListener;)Landroid/view/MenuItem;" />
 		<method name="setShortcut(CC)Landroid/view/MenuItem;" />
+		<method name="setShortcut(CCII)Landroid/view/MenuItem;" since="26" />
 		<method name="setShowAsAction(I)V" since="11" />
 		<method name="setShowAsActionFlags(I)Landroid/view/MenuItem;" since="14" />
 		<method name="setTitle(I)Landroid/view/MenuItem;" />
 		<method name="setTitle(Ljava/lang/CharSequence;)Landroid/view/MenuItem;" />
 		<method name="setTitleCondensed(Ljava/lang/CharSequence;)Landroid/view/MenuItem;" />
+		<method name="setTooltipText(Ljava/lang/CharSequence;)Landroid/view/MenuItem;" since="26" />
 		<method name="setVisible(Z)Landroid/view/MenuItem;" />
 		<field name="SHOW_AS_ACTION_ALWAYS" since="11" />
 		<field name="SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW" since="14" />
@@ -42096,6 +44896,7 @@
 		<field name="AXIS_RX" since="12" />
 		<field name="AXIS_RY" since="12" />
 		<field name="AXIS_RZ" since="12" />
+		<field name="AXIS_SCROLL" since="26" />
 		<field name="AXIS_SIZE" since="12" />
 		<field name="AXIS_THROTTLE" since="12" />
 		<field name="AXIS_TILT" since="14" />
@@ -42179,7 +44980,11 @@
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
 		<method name="request(Landroid/view/Surface;Landroid/graphics/Bitmap;Landroid/view/PixelCopy$OnPixelCopyFinishedListener;Landroid/os/Handler;)V" />
+		<method name="request(Landroid/view/Surface;Landroid/graphics/Rect;Landroid/graphics/Bitmap;Landroid/view/PixelCopy$OnPixelCopyFinishedListener;Landroid/os/Handler;)V" since="26" />
 		<method name="request(Landroid/view/SurfaceView;Landroid/graphics/Bitmap;Landroid/view/PixelCopy$OnPixelCopyFinishedListener;Landroid/os/Handler;)V" />
+		<method name="request(Landroid/view/SurfaceView;Landroid/graphics/Rect;Landroid/graphics/Bitmap;Landroid/view/PixelCopy$OnPixelCopyFinishedListener;Landroid/os/Handler;)V" since="26" />
+		<method name="request(Landroid/view/Window;Landroid/graphics/Bitmap;Landroid/view/PixelCopy$OnPixelCopyFinishedListener;Landroid/os/Handler;)V" since="26" />
+		<method name="request(Landroid/view/Window;Landroid/graphics/Rect;Landroid/graphics/Bitmap;Landroid/view/PixelCopy$OnPixelCopyFinishedListener;Landroid/os/Handler;)V" since="26" />
 		<field name="ERROR_DESTINATION_INVALID" />
 		<field name="ERROR_SOURCE_INVALID" />
 		<field name="ERROR_SOURCE_NO_DATA" />
@@ -42344,6 +45149,7 @@
 		<method name="isCreating()Z" />
 		<method name="lockCanvas()Landroid/graphics/Canvas;" />
 		<method name="lockCanvas(Landroid/graphics/Rect;)Landroid/graphics/Canvas;" />
+		<method name="lockHardwareCanvas()Landroid/graphics/Canvas;" since="26" />
 		<method name="removeCallback(Landroid/view/SurfaceHolder$Callback;)V" />
 		<method name="setFixedSize(II)V" />
 		<method name="setFormat(I)V" />
@@ -42371,6 +45177,7 @@
 		<extends name="java/lang/Object" />
 		<implements name="android/view/SurfaceHolder$Callback" />
 		<method name="surfaceRedrawNeeded(Landroid/view/SurfaceHolder;)V" />
+		<method name="surfaceRedrawNeededAsync(Landroid/view/SurfaceHolder;Ljava/lang/Runnable;)V" since="26" />
 	</class>
 	<class name="android/view/SurfaceView" since="1">
 		<extends name="android/view/View" />
@@ -42445,13 +45252,17 @@
 		<method name="&lt;init>(Landroid/content/Context;Landroid/util/AttributeSet;I)V" />
 		<method name="&lt;init>(Landroid/content/Context;Landroid/util/AttributeSet;II)V" since="21" />
 		<method name="addChildrenForAccessibility(Ljava/util/ArrayList;)V" since="16" />
+		<method name="addExtraDataToAccessibilityNodeInfo(Landroid/view/accessibility/AccessibilityNodeInfo;Ljava/lang/String;Landroid/os/Bundle;)V" since="26" />
 		<method name="addFocusables(Ljava/util/ArrayList;I)V" />
 		<method name="addFocusables(Ljava/util/ArrayList;II)V" since="4" />
+		<method name="addKeyboardNavigationClusters(Ljava/util/Collection;I)V" since="26" />
 		<method name="addOnAttachStateChangeListener(Landroid/view/View$OnAttachStateChangeListener;)V" since="12" />
 		<method name="addOnLayoutChangeListener(Landroid/view/View$OnLayoutChangeListener;)V" since="11" />
 		<method name="addTouchables(Ljava/util/ArrayList;)V" />
 		<method name="animate()Landroid/view/ViewPropertyAnimator;" since="12" />
 		<method name="announceForAccessibility(Ljava/lang/CharSequence;)V" since="16" />
+		<method name="autofill(Landroid/util/SparseArray;)V" since="26" />
+		<method name="autofill(Landroid/view/autofill/AutofillValue;)V" since="26" />
 		<method name="awakenScrollBars()Z" since="5" />
 		<method name="awakenScrollBars(I)Z" since="5" />
 		<method name="awakenScrollBars(IZ)Z" since="5" />
@@ -42484,6 +45295,7 @@
 		<method name="createContextMenu(Landroid/view/ContextMenu;)V" />
 		<method name="destroyDrawingCache()V" />
 		<method name="dispatchApplyWindowInsets(Landroid/view/WindowInsets;)Landroid/view/WindowInsets;" since="20" />
+		<method name="dispatchCapturedPointerEvent(Landroid/view/MotionEvent;)Z" since="26" />
 		<method name="dispatchConfigurationChanged(Landroid/content/res/Configuration;)V" since="8" />
 		<method name="dispatchDisplayHint(I)V" since="8" />
 		<method name="dispatchDragEvent(Landroid/view/DragEvent;)Z" since="11" />
@@ -42502,7 +45314,9 @@
 		<method name="dispatchNestedPrePerformAccessibilityAction(ILandroid/os/Bundle;)Z" since="22" />
 		<method name="dispatchNestedPreScroll(II[I[I)Z" since="21" />
 		<method name="dispatchNestedScroll(IIII[I)Z" since="21" />
+		<method name="dispatchPointerCaptureChanged(Z)V" since="26" />
 		<method name="dispatchPopulateAccessibilityEvent(Landroid/view/accessibility/AccessibilityEvent;)Z" since="4" />
+		<method name="dispatchProvideAutofillStructure(Landroid/view/ViewStructure;I)V" since="26" />
 		<method name="dispatchProvideStructure(Landroid/view/ViewStructure;)V" since="23" />
 		<method name="dispatchRestoreInstanceState(Landroid/util/SparseArray;)V" />
 		<method name="dispatchSaveInstanceState(Landroid/util/SparseArray;)V" />
@@ -42539,6 +45353,10 @@
 		<method name="getAlpha()F" since="11" />
 		<method name="getAnimation()Landroid/view/animation/Animation;" />
 		<method name="getApplicationWindowToken()Landroid/os/IBinder;" />
+		<method name="getAutofillHints()[Ljava/lang/String;" since="26" />
+		<method name="getAutofillId()Landroid/view/autofill/AutofillId;" since="26" />
+		<method name="getAutofillType()I" since="26" />
+		<method name="getAutofillValue()Landroid/view/autofill/AutofillValue;" since="26" />
 		<method name="getBackground()Landroid/graphics/drawable/Drawable;" />
 		<method name="getBackgroundTintList()Landroid/content/res/ColorStateList;" since="21" />
 		<method name="getBackgroundTintMode()Landroid/graphics/PorterDuff$Mode;" since="21" />
@@ -42553,6 +45371,7 @@
 		<method name="getContentDescription()Ljava/lang/CharSequence;" since="4" />
 		<method name="getContext()Landroid/content/Context;" />
 		<method name="getContextMenuInfo()Landroid/view/ContextMenu$ContextMenuInfo;" />
+		<method name="getDefaultFocusHighlightEnabled()Z" since="26" />
 		<method name="getDefaultSize(II)I" />
 		<method name="getDisplay()Landroid/view/Display;" since="17" />
 		<method name="getDrawableState()[I" />
@@ -42565,6 +45384,7 @@
 		<method name="getElevation()F" since="21" />
 		<method name="getFilterTouchesWhenObscured()Z" since="9" />
 		<method name="getFitsSystemWindows()Z" since="16" />
+		<method name="getFocusable()I" since="26" />
 		<method name="getFocusables(I)Ljava/util/ArrayList;" />
 		<method name="getFocusedRect(Landroid/graphics/Rect;)V" />
 		<method name="getForeground()Landroid/graphics/drawable/Drawable;" since="23" />
@@ -42581,6 +45401,7 @@
 		<method name="getHorizontalScrollbarHeight()I" />
 		<method name="getId()I" />
 		<method name="getImportantForAccessibility()I" since="16" />
+		<method name="getImportantForAutofill()I" since="26" />
 		<method name="getKeepScreenOn()Z" />
 		<method name="getKeyDispatcherState()Landroid/view/KeyEvent$DispatcherState;" since="5" />
 		<method name="getLabelFor()I" since="17" />
@@ -42601,6 +45422,7 @@
 		<method name="getMeasuredWidthAndState()I" since="11" />
 		<method name="getMinimumHeight()I" since="16" />
 		<method name="getMinimumWidth()I" since="16" />
+		<method name="getNextClusterForwardId()I" since="26" />
 		<method name="getNextFocusDownId()I" />
 		<method name="getNextFocusForwardId()I" since="11" />
 		<method name="getNextFocusLeftId()I" />
@@ -42649,6 +45471,7 @@
 		<method name="getTag(I)Ljava/lang/Object;" since="4" />
 		<method name="getTextAlignment()I" since="17" />
 		<method name="getTextDirection()I" since="17" />
+		<method name="getTooltipText()Ljava/lang/CharSequence;" since="26" />
 		<method name="getTop()I" />
 		<method name="getTopFadingEdgeStrength()F" />
 		<method name="getTopPaddingOffset()I" since="2" />
@@ -42673,11 +45496,13 @@
 		<method name="getX()F" since="11" />
 		<method name="getY()F" since="11" />
 		<method name="getZ()F" since="21" />
+		<method name="hasExplicitFocusable()Z" since="26" />
 		<method name="hasFocus()Z" />
 		<method name="hasFocusable()Z" />
 		<method name="hasNestedScrollingParent()Z" since="21" />
 		<method name="hasOnClickListeners()Z" since="15" />
 		<method name="hasOverlappingRendering()Z" since="16" />
+		<method name="hasPointerCapture()Z" since="26" />
 		<method name="hasTransientState()Z" since="16" />
 		<method name="hasWindowFocus()Z" />
 		<method name="inflate(Landroid/content/Context;ILandroid/view/ViewGroup;)Landroid/view/View;" />
@@ -42699,15 +45524,18 @@
 		<method name="isFocusable()Z" />
 		<method name="isFocusableInTouchMode()Z" />
 		<method name="isFocused()Z" />
+		<method name="isFocusedByDefault()Z" since="26" />
 		<method name="isHapticFeedbackEnabled()Z" since="3" />
 		<method name="isHardwareAccelerated()Z" since="11" />
 		<method name="isHorizontalFadingEdgeEnabled()Z" />
 		<method name="isHorizontalScrollBarEnabled()Z" />
 		<method name="isHovered()Z" since="14" />
 		<method name="isImportantForAccessibility()Z" since="21" />
+		<method name="isImportantForAutofill()Z" since="26" />
 		<method name="isInEditMode()Z" since="3" />
 		<method name="isInLayout()Z" since="18" />
 		<method name="isInTouchMode()Z" />
+		<method name="isKeyboardNavigationCluster()Z" since="26" />
 		<method name="isLaidOut()Z" since="19" />
 		<method name="isLayoutDirectionResolved()Z" since="19" />
 		<method name="isLayoutRequested()Z" />
@@ -42730,6 +45558,7 @@
 		<method name="isVerticalFadingEdgeEnabled()Z" />
 		<method name="isVerticalScrollBarEnabled()Z" />
 		<method name="jumpDrawablesToCurrentState()V" since="11" />
+		<method name="keyboardNavigationClusterSearch(Landroid/view/View;I)Landroid/view/View;" since="26" />
 		<method name="layout(IIII)V" />
 		<method name="measure(II)V" />
 		<method name="mergeDrawableStates([I[I)[I" />
@@ -42740,6 +45569,7 @@
 		<method name="onApplyWindowInsets(Landroid/view/WindowInsets;)Landroid/view/WindowInsets;" since="20" />
 		<method name="onAttachedToWindow()V" />
 		<method name="onCancelPendingInputEvents()V" since="19" />
+		<method name="onCapturedPointerEvent(Landroid/view/MotionEvent;)Z" since="26" />
 		<method name="onCheckIsTextEditor()Z" since="3" />
 		<method name="onConfigurationChanged(Landroid/content/res/Configuration;)V" since="8" />
 		<method name="onCreateContextMenu(Landroid/view/ContextMenu;)V" />
@@ -42765,7 +45595,10 @@
 		<method name="onLayout(ZIIII)V" />
 		<method name="onMeasure(II)V" />
 		<method name="onOverScrolled(IIZZ)V" since="9" />
+		<method name="onPointerCaptureChange(Z)V" since="26" />
 		<method name="onPopulateAccessibilityEvent(Landroid/view/accessibility/AccessibilityEvent;)V" since="14" />
+		<method name="onProvideAutofillStructure(Landroid/view/ViewStructure;I)V" since="26" />
+		<method name="onProvideAutofillVirtualStructure(Landroid/view/ViewStructure;I)V" since="26" />
 		<method name="onProvideStructure(Landroid/view/ViewStructure;)V" since="23" />
 		<method name="onProvideVirtualStructure(Landroid/view/ViewStructure;)V" since="23" />
 		<method name="onResolvePointerIcon(Landroid/view/MotionEvent;I)Landroid/view/PointerIcon;" since="24" />
@@ -42805,6 +45638,7 @@
 		<method name="postOnAnimation(Ljava/lang/Runnable;)V" since="16" />
 		<method name="postOnAnimationDelayed(Ljava/lang/Runnable;J)V" since="16" />
 		<method name="refreshDrawableState()V" />
+		<method name="releasePointerCapture()V" since="26" />
 		<method name="removeCallbacks(Ljava/lang/Runnable;)Z" />
 		<method name="removeOnAttachStateChangeListener(Landroid/view/View$OnAttachStateChangeListener;)V" since="12" />
 		<method name="removeOnLayoutChangeListener(Landroid/view/View$OnLayoutChangeListener;)V" since="11" />
@@ -42815,11 +45649,13 @@
 		<method name="requestFocus(ILandroid/graphics/Rect;)Z" />
 		<method name="requestFocusFromTouch()Z" />
 		<method name="requestLayout()V" />
+		<method name="requestPointerCapture()V" since="26" />
 		<method name="requestRectangleOnScreen(Landroid/graphics/Rect;)Z" />
 		<method name="requestRectangleOnScreen(Landroid/graphics/Rect;Z)Z" />
 		<method name="requestUnbufferedDispatch(Landroid/view/MotionEvent;)V" since="21" />
 		<method name="resolveSize(II)I" />
 		<method name="resolveSizeAndState(III)I" since="11" />
+		<method name="restoreDefaultFocus()Z" since="26" />
 		<method name="restoreHierarchyState(Landroid/util/SparseArray;)V" />
 		<method name="saveHierarchyState(Landroid/util/SparseArray;)V" />
 		<method name="scrollBy(II)V" />
@@ -42831,6 +45667,7 @@
 		<method name="setActivated(Z)V" since="11" />
 		<method name="setAlpha(F)V" since="11" />
 		<method name="setAnimation(Landroid/view/animation/Animation;)V" />
+		<method name="setAutofillHints([Ljava/lang/String;)V" since="26" />
 		<method name="setBackground(Landroid/graphics/drawable/Drawable;)V" since="16" />
 		<method name="setBackgroundColor(I)V" />
 		<method name="setBackgroundDrawable(Landroid/graphics/drawable/Drawable;)V" deprecated="16" />
@@ -42844,6 +45681,7 @@
 		<method name="setClipToOutline(Z)V" since="21" />
 		<method name="setContentDescription(Ljava/lang/CharSequence;)V" since="4" />
 		<method name="setContextClickable(Z)V" since="23" />
+		<method name="setDefaultFocusHighlightEnabled(Z)V" since="26" />
 		<method name="setDrawingCacheBackgroundColor(I)V" />
 		<method name="setDrawingCacheEnabled(Z)V" />
 		<method name="setDrawingCacheQuality(I)V" />
@@ -42853,8 +45691,10 @@
 		<method name="setFadingEdgeLength(I)V" />
 		<method name="setFilterTouchesWhenObscured(Z)V" since="9" />
 		<method name="setFitsSystemWindows(Z)V" since="14" />
+		<method name="setFocusable(I)V" since="26" />
 		<method name="setFocusable(Z)V" />
 		<method name="setFocusableInTouchMode(Z)V" />
+		<method name="setFocusedByDefault(Z)V" since="26" />
 		<method name="setForeground(Landroid/graphics/drawable/Drawable;)V" since="23" />
 		<method name="setForegroundGravity(I)V" since="23" />
 		<method name="setForegroundTintList(Landroid/content/res/ColorStateList;)V" since="23" />
@@ -42866,7 +45706,9 @@
 		<method name="setHovered(Z)V" since="14" />
 		<method name="setId(I)V" />
 		<method name="setImportantForAccessibility(I)V" since="16" />
+		<method name="setImportantForAutofill(I)V" since="26" />
 		<method name="setKeepScreenOn(Z)V" />
+		<method name="setKeyboardNavigationCluster(Z)V" since="26" />
 		<method name="setLabelFor(I)V" since="17" />
 		<method name="setLayerPaint(Landroid/graphics/Paint;)V" since="17" />
 		<method name="setLayerType(ILandroid/graphics/Paint;)V" since="11" />
@@ -42878,12 +45720,14 @@
 		<method name="setMinimumHeight(I)V" />
 		<method name="setMinimumWidth(I)V" />
 		<method name="setNestedScrollingEnabled(Z)V" since="21" />
+		<method name="setNextClusterForwardId(I)V" since="26" />
 		<method name="setNextFocusDownId(I)V" />
 		<method name="setNextFocusForwardId(I)V" since="11" />
 		<method name="setNextFocusLeftId(I)V" />
 		<method name="setNextFocusRightId(I)V" />
 		<method name="setNextFocusUpId(I)V" />
 		<method name="setOnApplyWindowInsetsListener(Landroid/view/View$OnApplyWindowInsetsListener;)V" since="20" />
+		<method name="setOnCapturedPointerListener(Landroid/view/View$OnCapturedPointerListener;)V" since="26" />
 		<method name="setOnClickListener(Landroid/view/View$OnClickListener;)V" />
 		<method name="setOnContextClickListener(Landroid/view/View$OnContextClickListener;)V" since="23" />
 		<method name="setOnCreateContextMenuListener(Landroid/view/View$OnCreateContextMenuListener;)V" />
@@ -42931,6 +45775,7 @@
 		<method name="setTag(Ljava/lang/Object;)V" />
 		<method name="setTextAlignment(I)V" since="17" />
 		<method name="setTextDirection(I)V" since="17" />
+		<method name="setTooltipText(Ljava/lang/CharSequence;)V" since="26" />
 		<method name="setTop(I)V" since="11" />
 		<method name="setTouchDelegate(Landroid/view/TouchDelegate;)V" />
 		<method name="setTransitionName(Ljava/lang/String;)V" since="21" />
@@ -42964,6 +45809,25 @@
 		<field name="ACCESSIBILITY_LIVE_REGION_NONE" since="19" />
 		<field name="ACCESSIBILITY_LIVE_REGION_POLITE" since="19" />
 		<field name="ALPHA" since="14" />
+		<field name="AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS" since="26" />
+		<field name="AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE" since="26" />
+		<field name="AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY" since="26" />
+		<field name="AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH" since="26" />
+		<field name="AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR" since="26" />
+		<field name="AUTOFILL_HINT_CREDIT_CARD_NUMBER" since="26" />
+		<field name="AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE" since="26" />
+		<field name="AUTOFILL_HINT_EMAIL_ADDRESS" since="26" />
+		<field name="AUTOFILL_HINT_NAME" since="26" />
+		<field name="AUTOFILL_HINT_PASSWORD" since="26" />
+		<field name="AUTOFILL_HINT_PHONE" since="26" />
+		<field name="AUTOFILL_HINT_POSTAL_ADDRESS" since="26" />
+		<field name="AUTOFILL_HINT_POSTAL_CODE" since="26" />
+		<field name="AUTOFILL_HINT_USERNAME" since="26" />
+		<field name="AUTOFILL_TYPE_DATE" since="26" />
+		<field name="AUTOFILL_TYPE_LIST" since="26" />
+		<field name="AUTOFILL_TYPE_NONE" since="26" />
+		<field name="AUTOFILL_TYPE_TEXT" since="26" />
+		<field name="AUTOFILL_TYPE_TOGGLE" since="26" />
 		<field name="DRAG_FLAG_GLOBAL" since="24" />
 		<field name="DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION" since="24" />
 		<field name="DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION" since="24" />
@@ -42984,8 +45848,10 @@
 		<field name="ENABLED_WINDOW_FOCUSED_STATE_SET" />
 		<field name="FIND_VIEWS_WITH_CONTENT_DESCRIPTION" since="14" />
 		<field name="FIND_VIEWS_WITH_TEXT" since="14" />
+		<field name="FOCUSABLE" since="26" />
 		<field name="FOCUSABLES_ALL" since="4" />
 		<field name="FOCUSABLES_TOUCH_MODE" since="4" />
+		<field name="FOCUSABLE_AUTO" since="26" />
 		<field name="FOCUSED_SELECTED_STATE_SET" />
 		<field name="FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET" />
 		<field name="FOCUSED_STATE_SET" />
@@ -43002,6 +45868,11 @@
 		<field name="IMPORTANT_FOR_ACCESSIBILITY_NO" since="16" />
 		<field name="IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS" since="19" />
 		<field name="IMPORTANT_FOR_ACCESSIBILITY_YES" since="16" />
+		<field name="IMPORTANT_FOR_AUTOFILL_AUTO" since="26" />
+		<field name="IMPORTANT_FOR_AUTOFILL_NO" since="26" />
+		<field name="IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS" since="26" />
+		<field name="IMPORTANT_FOR_AUTOFILL_YES" since="26" />
+		<field name="IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS" since="26" />
 		<field name="INVISIBLE" />
 		<field name="KEEP_SCREEN_ON" />
 		<field name="LAYER_TYPE_HARDWARE" since="11" />
@@ -43015,6 +45886,7 @@
 		<field name="MEASURED_SIZE_MASK" since="11" />
 		<field name="MEASURED_STATE_MASK" since="11" />
 		<field name="MEASURED_STATE_TOO_SMALL" since="11" />
+		<field name="NOT_FOCUSABLE" since="26" />
 		<field name="NO_ID" />
 		<field name="OVER_SCROLL_ALWAYS" since="9" />
 		<field name="OVER_SCROLL_IF_CONTENT_SCROLLS" since="9" />
@@ -43070,6 +45942,7 @@
 		<field name="SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN" since="16" />
 		<field name="SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION" since="16" />
 		<field name="SYSTEM_UI_FLAG_LAYOUT_STABLE" since="16" />
+		<field name="SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR" since="26" />
 		<field name="SYSTEM_UI_FLAG_LIGHT_STATUS_BAR" since="23" />
 		<field name="SYSTEM_UI_FLAG_LOW_PROFILE" since="14" />
 		<field name="SYSTEM_UI_FLAG_VISIBLE" since="14" />
@@ -43103,6 +45976,7 @@
 	<class name="android/view/View$AccessibilityDelegate" since="14">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
+		<method name="addExtraDataToAccessibilityNodeInfo(Landroid/view/View;Landroid/view/accessibility/AccessibilityNodeInfo;Ljava/lang/String;Landroid/os/Bundle;)V" since="26" />
 		<method name="dispatchPopulateAccessibilityEvent(Landroid/view/View;Landroid/view/accessibility/AccessibilityEvent;)Z" />
 		<method name="getAccessibilityNodeProvider(Landroid/view/View;)Landroid/view/accessibility/AccessibilityNodeProvider;" since="16" />
 		<method name="onInitializeAccessibilityEvent(Landroid/view/View;Landroid/view/accessibility/AccessibilityEvent;)V" />
@@ -43148,6 +46022,10 @@
 		<method name="onViewAttachedToWindow(Landroid/view/View;)V" />
 		<method name="onViewDetachedFromWindow(Landroid/view/View;)V" />
 	</class>
+	<class name="android/view/View$OnCapturedPointerListener" since="26">
+		<extends name="java/lang/Object" />
+		<method name="onCapturedPointer(Landroid/view/View;Landroid/view/MotionEvent;)Z" />
+	</class>
 	<class name="android/view/View$OnClickListener" since="1">
 		<extends name="java/lang/Object" />
 		<method name="onClick(Landroid/view/View;)V" />
@@ -43225,6 +46103,7 @@
 		<method name="getScaledDoubleTapSlop()I" since="3" />
 		<method name="getScaledEdgeSlop()I" since="3" />
 		<method name="getScaledFadingEdgeLength()I" since="3" />
+		<method name="getScaledHorizontalScrollFactor()F" since="26" />
 		<method name="getScaledMaximumDrawingCacheSize()I" since="3" />
 		<method name="getScaledMaximumFlingVelocity()I" since="4" />
 		<method name="getScaledMinimumFlingVelocity()I" since="3" />
@@ -43233,6 +46112,7 @@
 		<method name="getScaledPagingTouchSlop()I" since="8" />
 		<method name="getScaledScrollBarSize()I" since="3" />
 		<method name="getScaledTouchSlop()I" since="3" />
+		<method name="getScaledVerticalScrollFactor()F" since="26" />
 		<method name="getScaledWindowTouchSlop()I" since="3" />
 		<method name="getScrollBarFadeDuration()I" since="5" />
 		<method name="getScrollBarSize()I" deprecated="16" />
@@ -43511,13 +46391,15 @@
 		<method name="getParentForAccessibility()Landroid/view/ViewParent;" since="16" />
 		<method name="getTextAlignment()I" since="19" />
 		<method name="getTextDirection()I" since="19" />
-		<method name="invalidateChild(Landroid/view/View;Landroid/graphics/Rect;)V" />
-		<method name="invalidateChildInParent([ILandroid/graphics/Rect;)Landroid/view/ViewParent;" />
+		<method name="invalidateChild(Landroid/view/View;Landroid/graphics/Rect;)V" deprecated="26" />
+		<method name="invalidateChildInParent([ILandroid/graphics/Rect;)Landroid/view/ViewParent;" deprecated="26" />
 		<method name="isLayoutDirectionResolved()Z" since="19" />
 		<method name="isLayoutRequested()Z" />
 		<method name="isTextAlignmentResolved()Z" since="19" />
 		<method name="isTextDirectionResolved()Z" since="19" />
+		<method name="keyboardNavigationClusterSearch(Landroid/view/View;I)Landroid/view/View;" since="26" />
 		<method name="notifySubtreeAccessibilityStateChanged(Landroid/view/View;Landroid/view/View;I)V" since="19" />
+		<method name="onDescendantInvalidated(Landroid/view/View;Landroid/view/View;)V" since="26" />
 		<method name="onNestedFling(Landroid/view/View;FFZ)Z" since="21" />
 		<method name="onNestedPreFling(Landroid/view/View;FF)Z" since="21" />
 		<method name="onNestedPrePerformAccessibilityAction(Landroid/view/View;ILandroid/os/Bundle;)Z" since="22" />
@@ -43586,6 +46468,7 @@
 		<method name="addChildCount(I)I" />
 		<method name="asyncCommit()V" />
 		<method name="asyncNewChild(I)Landroid/view/ViewStructure;" />
+		<method name="getAutofillId()Landroid/view/autofill/AutofillId;" since="26" />
 		<method name="getChildCount()I" />
 		<method name="getExtras()Landroid/os/Bundle;" />
 		<method name="getHint()Ljava/lang/CharSequence;" />
@@ -43594,9 +46477,16 @@
 		<method name="getTextSelectionStart()I" />
 		<method name="hasExtras()Z" />
 		<method name="newChild(I)Landroid/view/ViewStructure;" />
+		<method name="newHtmlInfoBuilder(Ljava/lang/String;)Landroid/view/ViewStructure$HtmlInfo$Builder;" since="26" />
 		<method name="setAccessibilityFocused(Z)V" />
 		<method name="setActivated(Z)V" />
 		<method name="setAlpha(F)V" />
+		<method name="setAutofillHints([Ljava/lang/String;)V" since="26" />
+		<method name="setAutofillId(Landroid/view/autofill/AutofillId;)V" since="26" />
+		<method name="setAutofillId(Landroid/view/autofill/AutofillId;I)V" since="26" />
+		<method name="setAutofillOptions([Ljava/lang/CharSequence;)V" since="26" />
+		<method name="setAutofillType(I)V" since="26" />
+		<method name="setAutofillValue(Landroid/view/autofill/AutofillValue;)V" since="26" />
 		<method name="setCheckable(Z)V" />
 		<method name="setChecked(Z)V" />
 		<method name="setChildCount(I)V" />
@@ -43604,14 +46494,19 @@
 		<method name="setClickable(Z)V" />
 		<method name="setContentDescription(Ljava/lang/CharSequence;)V" />
 		<method name="setContextClickable(Z)V" />
+		<method name="setDataIsSensitive(Z)V" since="26" />
 		<method name="setDimens(IIIIII)V" />
 		<method name="setElevation(F)V" />
 		<method name="setEnabled(Z)V" />
 		<method name="setFocusable(Z)V" />
 		<method name="setFocused(Z)V" />
 		<method name="setHint(Ljava/lang/CharSequence;)V" />
+		<method name="setHtmlInfo(Landroid/view/ViewStructure$HtmlInfo;)V" since="26" />
 		<method name="setId(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V" />
+		<method name="setInputType(I)V" since="26" />
+		<method name="setLocaleList(Landroid/os/LocaleList;)V" since="26" />
 		<method name="setLongClickable(Z)V" />
+		<method name="setOpaque(Z)V" since="26" />
 		<method name="setSelected(Z)V" />
 		<method name="setText(Ljava/lang/CharSequence;)V" />
 		<method name="setText(Ljava/lang/CharSequence;II)V" />
@@ -43619,6 +46514,19 @@
 		<method name="setTextStyle(FIII)V" />
 		<method name="setTransformation(Landroid/graphics/Matrix;)V" />
 		<method name="setVisibility(I)V" />
+		<method name="setWebDomain(Ljava/lang/String;)V" since="26" />
+	</class>
+	<class name="android/view/ViewStructure$HtmlInfo" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="getAttributes()Ljava/util/List;" />
+		<method name="getTag()Ljava/lang/String;" />
+	</class>
+	<class name="android/view/ViewStructure$HtmlInfo$Builder" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="addAttribute(Ljava/lang/String;Ljava/lang/String;)Landroid/view/ViewStructure$HtmlInfo$Builder;" />
+		<method name="build()Landroid/view/ViewStructure$HtmlInfo;" />
 	</class>
 	<class name="android/view/ViewStub" since="1">
 		<extends name="android/view/View" />
@@ -43712,6 +46620,7 @@
 		<method name="getAllowReturnTransitionOverlap()Z" since="21" />
 		<method name="getAttributes()Landroid/view/WindowManager$LayoutParams;" />
 		<method name="getCallback()Landroid/view/Window$Callback;" />
+		<method name="getColorMode()I" since="26" />
 		<method name="getContainer()Landroid/view/Window;" />
 		<method name="getContentScene()Landroid/transition/Scene;" since="21" />
 		<method name="getContext()Landroid/content/Context;" />
@@ -43768,6 +46677,7 @@
 		<method name="setChildDrawable(ILandroid/graphics/drawable/Drawable;)V" />
 		<method name="setChildInt(II)V" />
 		<method name="setClipToOutline(Z)V" since="22" />
+		<method name="setColorMode(I)V" since="26" />
 		<method name="setContainer(Landroid/view/Window;)V" />
 		<method name="setContentView(I)V" />
 		<method name="setContentView(Landroid/view/View;)V" />
@@ -43872,6 +46782,7 @@
 		<method name="onMenuItemSelected(ILandroid/view/MenuItem;)Z" />
 		<method name="onMenuOpened(ILandroid/view/Menu;)Z" />
 		<method name="onPanelClosed(ILandroid/view/Menu;)V" />
+		<method name="onPointerCaptureChanged(Z)V" since="26" />
 		<method name="onPreparePanel(ILandroid/view/View;Landroid/view/Menu;)Z" />
 		<method name="onProvideKeyboardShortcuts(Ljava/util/List;Landroid/view/Menu;I)V" since="24" />
 		<method name="onSearchRequested()Z" />
@@ -43967,8 +46878,10 @@
 		<method name="&lt;init>(Landroid/os/Parcel;)V" />
 		<method name="copyFrom(Landroid/view/WindowManager$LayoutParams;)I" />
 		<method name="debug(Ljava/lang/String;)Ljava/lang/String;" />
+		<method name="getColorMode()I" since="26" />
 		<method name="getTitle()Ljava/lang/CharSequence;" />
 		<method name="mayUseInputMethod(I)Z" since="3" />
+		<method name="setColorMode(I)V" since="26" />
 		<method name="setTitle(Ljava/lang/CharSequence;)V" />
 		<field name="ALPHA_CHANGED" />
 		<field name="ANIMATION_CHANGED" />
@@ -43985,7 +46898,7 @@
 		<field name="FLAG_ALT_FOCUSABLE_IM" since="3" />
 		<field name="FLAG_BLUR_BEHIND" deprecated="16" />
 		<field name="FLAG_DIM_BEHIND" />
-		<field name="FLAG_DISMISS_KEYGUARD" since="5" />
+		<field name="FLAG_DISMISS_KEYGUARD" since="5" deprecated="26" />
 		<field name="FLAG_DITHER" deprecated="17" />
 		<field name="FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS" since="21" />
 		<field name="FLAG_FORCE_NOT_FULLSCREEN" />
@@ -44026,6 +46939,7 @@
 		<field name="ROTATION_ANIMATION_CROSSFADE" since="18" />
 		<field name="ROTATION_ANIMATION_JUMPCUT" since="18" />
 		<field name="ROTATION_ANIMATION_ROTATE" since="18" />
+		<field name="ROTATION_ANIMATION_SEAMLESS" since="26" />
 		<field name="SCREEN_BRIGHTNESS_CHANGED" since="3" />
 		<field name="SCREEN_ORIENTATION_CHANGED" since="3" />
 		<field name="SOFT_INPUT_ADJUST_NOTHING" since="11" />
@@ -44047,6 +46961,7 @@
 		<field name="TYPE_APPLICATION" />
 		<field name="TYPE_APPLICATION_ATTACHED_DIALOG" since="3" />
 		<field name="TYPE_APPLICATION_MEDIA" />
+		<field name="TYPE_APPLICATION_OVERLAY" since="26" />
 		<field name="TYPE_APPLICATION_PANEL" />
 		<field name="TYPE_APPLICATION_STARTING" />
 		<field name="TYPE_APPLICATION_SUB_PANEL" />
@@ -44057,17 +46972,17 @@
 		<field name="TYPE_INPUT_METHOD_DIALOG" since="3" />
 		<field name="TYPE_KEYGUARD" />
 		<field name="TYPE_KEYGUARD_DIALOG" />
-		<field name="TYPE_PHONE" />
-		<field name="TYPE_PRIORITY_PHONE" />
+		<field name="TYPE_PHONE" deprecated="26" />
+		<field name="TYPE_PRIORITY_PHONE" deprecated="26" />
 		<field name="TYPE_PRIVATE_PRESENTATION" since="19" />
 		<field name="TYPE_SEARCH_BAR" />
 		<field name="TYPE_STATUS_BAR" />
 		<field name="TYPE_STATUS_BAR_PANEL" />
-		<field name="TYPE_SYSTEM_ALERT" />
+		<field name="TYPE_SYSTEM_ALERT" deprecated="26" />
 		<field name="TYPE_SYSTEM_DIALOG" />
-		<field name="TYPE_SYSTEM_ERROR" />
-		<field name="TYPE_SYSTEM_OVERLAY" />
-		<field name="TYPE_TOAST" />
+		<field name="TYPE_SYSTEM_ERROR" deprecated="26" />
+		<field name="TYPE_SYSTEM_OVERLAY" deprecated="26" />
+		<field name="TYPE_TOAST" deprecated="26" />
 		<field name="TYPE_WALLPAPER" since="5" />
 		<field name="alpha" />
 		<field name="buttonBrightness" since="8" />
@@ -44190,7 +47105,9 @@
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
 		<method name="addAccessibilityStateChangeListener(Landroid/view/accessibility/AccessibilityManager$AccessibilityStateChangeListener;)Z" since="14" />
+		<method name="addAccessibilityStateChangeListener(Landroid/view/accessibility/AccessibilityManager$AccessibilityStateChangeListener;Landroid/os/Handler;)V" since="26" />
 		<method name="addTouchExplorationStateChangeListener(Landroid/view/accessibility/AccessibilityManager$TouchExplorationStateChangeListener;)Z" since="19" />
+		<method name="addTouchExplorationStateChangeListener(Landroid/view/accessibility/AccessibilityManager$TouchExplorationStateChangeListener;Landroid/os/Handler;)V" since="26" />
 		<method name="getAccessibilityServiceList()Ljava/util/List;" deprecated="16" />
 		<method name="getEnabledAccessibilityServiceList(I)Ljava/util/List;" since="14" />
 		<method name="getInstalledAccessibilityServiceList()Ljava/util/List;" since="14" />
@@ -44224,6 +47141,7 @@
 		<method name="focusSearch(I)Landroid/view/accessibility/AccessibilityNodeInfo;" since="16" />
 		<method name="getActionList()Ljava/util/List;" since="21" />
 		<method name="getActions()I" deprecated="21" />
+		<method name="getAvailableExtraData()Ljava/util/List;" since="26" />
 		<method name="getBoundsInParent(Landroid/graphics/Rect;)V" />
 		<method name="getBoundsInScreen(Landroid/graphics/Rect;)V" />
 		<method name="getChild(I)Landroid/view/accessibility/AccessibilityNodeInfo;" />
@@ -44235,6 +47153,7 @@
 		<method name="getDrawingOrder()I" since="24" />
 		<method name="getError()Ljava/lang/CharSequence;" since="21" />
 		<method name="getExtras()Landroid/os/Bundle;" since="19" />
+		<method name="getHintText()Ljava/lang/CharSequence;" since="26" />
 		<method name="getInputType()I" since="19" />
 		<method name="getLabelFor()Landroid/view/accessibility/AccessibilityNodeInfo;" since="17" />
 		<method name="getLabeledBy()Landroid/view/accessibility/AccessibilityNodeInfo;" since="17" />
@@ -44269,6 +47188,7 @@
 		<method name="isPassword()Z" />
 		<method name="isScrollable()Z" />
 		<method name="isSelected()Z" />
+		<method name="isShowingHintText()Z" since="26" />
 		<method name="isVisibleToUser()Z" since="16" />
 		<method name="obtain()Landroid/view/accessibility/AccessibilityNodeInfo;" />
 		<method name="obtain(Landroid/view/View;)Landroid/view/accessibility/AccessibilityNodeInfo;" />
@@ -44278,11 +47198,13 @@
 		<method name="performAction(ILandroid/os/Bundle;)Z" since="16" />
 		<method name="recycle()V" />
 		<method name="refresh()Z" since="18" />
+		<method name="refreshWithExtraData(Ljava/lang/String;Landroid/os/Bundle;)Z" since="26" />
 		<method name="removeAction(I)V" since="21" deprecated="21" />
 		<method name="removeAction(Landroid/view/accessibility/AccessibilityNodeInfo$AccessibilityAction;)Z" since="21" />
 		<method name="removeChild(Landroid/view/View;)Z" since="21" />
 		<method name="removeChild(Landroid/view/View;I)Z" since="21" />
 		<method name="setAccessibilityFocused(Z)V" since="16" />
+		<method name="setAvailableExtraData(Ljava/util/List;)V" since="26" />
 		<method name="setBoundsInParent(Landroid/graphics/Rect;)V" />
 		<method name="setBoundsInScreen(Landroid/graphics/Rect;)V" />
 		<method name="setCanOpenPopup(Z)V" since="19" />
@@ -44302,6 +47224,7 @@
 		<method name="setError(Ljava/lang/CharSequence;)V" since="21" />
 		<method name="setFocusable(Z)V" />
 		<method name="setFocused(Z)V" />
+		<method name="setHintText(Ljava/lang/CharSequence;)V" since="26" />
 		<method name="setImportantForAccessibility(Z)V" since="24" />
 		<method name="setInputType(I)V" since="19" />
 		<method name="setLabelFor(Landroid/view/View;)V" since="17" />
@@ -44320,6 +47243,7 @@
 		<method name="setRangeInfo(Landroid/view/accessibility/AccessibilityNodeInfo$RangeInfo;)V" since="19" />
 		<method name="setScrollable(Z)V" />
 		<method name="setSelected(Z)V" />
+		<method name="setShowingHintText(Z)V" since="26" />
 		<method name="setSource(Landroid/view/View;)V" />
 		<method name="setSource(Landroid/view/View;I)V" since="16" />
 		<method name="setText(Ljava/lang/CharSequence;)V" />
@@ -44335,6 +47259,8 @@
 		<field name="ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN" since="18" />
 		<field name="ACTION_ARGUMENT_HTML_ELEMENT_STRING" since="16" />
 		<field name="ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT" since="16" />
+		<field name="ACTION_ARGUMENT_MOVE_WINDOW_X" since="26" />
+		<field name="ACTION_ARGUMENT_MOVE_WINDOW_Y" since="26" />
 		<field name="ACTION_ARGUMENT_PROGRESS_VALUE" since="24" />
 		<field name="ACTION_ARGUMENT_ROW_INT" since="23" />
 		<field name="ACTION_ARGUMENT_SELECTION_END_INT" since="18" />
@@ -44362,6 +47288,9 @@
 		<field name="ACTION_SET_SELECTION" since="18" />
 		<field name="ACTION_SET_TEXT" since="21" />
 		<field name="CREATOR" />
+		<field name="EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH" since="26" />
+		<field name="EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX" since="26" />
+		<field name="EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY" since="26" />
 		<field name="FOCUS_ACCESSIBILITY" since="16" />
 		<field name="FOCUS_INPUT" since="16" />
 		<field name="MOVEMENT_GRANULARITY_CHARACTER" since="16" />
@@ -44388,6 +47317,7 @@
 		<field name="ACTION_EXPAND" />
 		<field name="ACTION_FOCUS" />
 		<field name="ACTION_LONG_CLICK" />
+		<field name="ACTION_MOVE_WINDOW" since="26" />
 		<field name="ACTION_NEXT_AT_MOVEMENT_GRANULARITY" />
 		<field name="ACTION_NEXT_HTML_ELEMENT" />
 		<field name="ACTION_PASTE" />
@@ -44446,6 +47376,7 @@
 	<class name="android/view/accessibility/AccessibilityNodeProvider" since="16">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
+		<method name="addExtraDataToAccessibilityNodeInfo(ILandroid/view/accessibility/AccessibilityNodeInfo;Ljava/lang/String;Landroid/os/Bundle;)V" since="26" />
 		<method name="createAccessibilityNodeInfo(I)Landroid/view/accessibility/AccessibilityNodeInfo;" />
 		<method name="findAccessibilityNodeInfosByText(Ljava/lang/String;I)Ljava/util/List;" />
 		<method name="findFocus(I)Landroid/view/accessibility/AccessibilityNodeInfo;" since="19" />
@@ -44519,6 +47450,7 @@
 		<method name="isAccessibilityFocused()Z" />
 		<method name="isActive()Z" />
 		<method name="isFocused()Z" />
+		<method name="isInPictureInPictureMode()Z" since="26" />
 		<method name="obtain()Landroid/view/accessibility/AccessibilityWindowInfo;" />
 		<method name="obtain(Landroid/view/accessibility/AccessibilityWindowInfo;)Landroid/view/accessibility/AccessibilityWindowInfo;" />
 		<method name="recycle()V" />
@@ -44859,6 +47791,62 @@
 		<method name="&lt;init>(IFIFIFIF)V" />
 		<method name="&lt;init>(Landroid/content/Context;Landroid/util/AttributeSet;)V" />
 	</class>
+	<class name="android/view/autofill/AutofillId" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/os/Parcelable" />
+		<method name="&lt;init>()V" />
+		<field name="CREATOR" />
+	</class>
+	<class name="android/view/autofill/AutofillManager" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="cancel()V" />
+		<method name="commit()V" />
+		<method name="disableAutofillServices()V" />
+		<method name="hasEnabledAutofillServices()Z" />
+		<method name="isAutofillSupported()Z" />
+		<method name="isEnabled()Z" />
+		<method name="notifyValueChanged(Landroid/view/View;)V" />
+		<method name="notifyValueChanged(Landroid/view/View;ILandroid/view/autofill/AutofillValue;)V" />
+		<method name="notifyViewEntered(Landroid/view/View;)V" />
+		<method name="notifyViewEntered(Landroid/view/View;ILandroid/graphics/Rect;)V" />
+		<method name="notifyViewExited(Landroid/view/View;)V" />
+		<method name="notifyViewExited(Landroid/view/View;I)V" />
+		<method name="registerCallback(Landroid/view/autofill/AutofillManager$AutofillCallback;)V" />
+		<method name="requestAutofill(Landroid/view/View;)V" />
+		<method name="requestAutofill(Landroid/view/View;ILandroid/graphics/Rect;)V" />
+		<method name="unregisterCallback(Landroid/view/autofill/AutofillManager$AutofillCallback;)V" />
+		<field name="EXTRA_ASSIST_STRUCTURE" />
+		<field name="EXTRA_AUTHENTICATION_RESULT" />
+		<field name="EXTRA_CLIENT_STATE" />
+	</class>
+	<class name="android/view/autofill/AutofillManager$AutofillCallback" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="onAutofillEvent(Landroid/view/View;I)V" />
+		<method name="onAutofillEvent(Landroid/view/View;II)V" />
+		<field name="EVENT_INPUT_HIDDEN" />
+		<field name="EVENT_INPUT_SHOWN" />
+		<field name="EVENT_INPUT_UNAVAILABLE" />
+	</class>
+	<class name="android/view/autofill/AutofillValue" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="android/os/Parcelable" />
+		<method name="&lt;init>()V" />
+		<method name="forDate(J)Landroid/view/autofill/AutofillValue;" />
+		<method name="forList(I)Landroid/view/autofill/AutofillValue;" />
+		<method name="forText(Ljava/lang/CharSequence;)Landroid/view/autofill/AutofillValue;" />
+		<method name="forToggle(Z)Landroid/view/autofill/AutofillValue;" />
+		<method name="getDateValue()J" />
+		<method name="getListValue()I" />
+		<method name="getTextValue()Ljava/lang/CharSequence;" />
+		<method name="getToggleValue()Z" />
+		<method name="isDate()Z" />
+		<method name="isList()Z" />
+		<method name="isText()Z" />
+		<method name="isToggle()Z" />
+		<field name="CREATOR" />
+	</class>
 	<class name="android/view/inputmethod/BaseInputConnection" since="3">
 		<extends name="java/lang/Object" />
 		<implements name="android/view/inputmethod/InputConnection" />
@@ -44944,6 +47932,7 @@
 		<field name="IME_FLAG_NO_ENTER_ACTION" />
 		<field name="IME_FLAG_NO_EXTRACT_UI" />
 		<field name="IME_FLAG_NO_FULLSCREEN" since="11" />
+		<field name="IME_FLAG_NO_PERSONALIZED_LEARNING" since="26" />
 		<field name="IME_MASK_ACTION" />
 		<field name="IME_NULL" />
 		<field name="actionId" />
@@ -45197,6 +48186,61 @@
 		<method name="setSubtypeMode(Ljava/lang/String;)Landroid/view/inputmethod/InputMethodSubtype$InputMethodSubtypeBuilder;" />
 		<method name="setSubtypeNameResId(I)Landroid/view/inputmethod/InputMethodSubtype$InputMethodSubtypeBuilder;" />
 	</class>
+	<class name="android/view/textclassifier/TextClassification" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="getConfidenceScore(Ljava/lang/String;)F" />
+		<method name="getEntity(I)Ljava/lang/String;" />
+		<method name="getEntityCount()I" />
+		<method name="getIcon()Landroid/graphics/drawable/Drawable;" />
+		<method name="getIntent()Landroid/content/Intent;" />
+		<method name="getLabel()Ljava/lang/CharSequence;" />
+		<method name="getOnClickListener()Landroid/view/View$OnClickListener;" />
+		<method name="getText()Ljava/lang/String;" />
+	</class>
+	<class name="android/view/textclassifier/TextClassification$Builder" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="build()Landroid/view/textclassifier/TextClassification;" />
+		<method name="setEntityType(Ljava/lang/String;F)Landroid/view/textclassifier/TextClassification$Builder;" />
+		<method name="setIcon(Landroid/graphics/drawable/Drawable;)Landroid/view/textclassifier/TextClassification$Builder;" />
+		<method name="setIntent(Landroid/content/Intent;)Landroid/view/textclassifier/TextClassification$Builder;" />
+		<method name="setLabel(Ljava/lang/String;)Landroid/view/textclassifier/TextClassification$Builder;" />
+		<method name="setOnClickListener(Landroid/view/View$OnClickListener;)Landroid/view/textclassifier/TextClassification$Builder;" />
+		<method name="setText(Ljava/lang/String;)Landroid/view/textclassifier/TextClassification$Builder;" />
+	</class>
+	<class name="android/view/textclassifier/TextClassificationManager" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="getTextClassifier()Landroid/view/textclassifier/TextClassifier;" />
+		<method name="setTextClassifier(Landroid/view/textclassifier/TextClassifier;)V" />
+	</class>
+	<class name="android/view/textclassifier/TextClassifier" since="26">
+		<extends name="java/lang/Object" />
+		<method name="classifyText(Ljava/lang/CharSequence;IILandroid/os/LocaleList;)Landroid/view/textclassifier/TextClassification;" />
+		<method name="suggestSelection(Ljava/lang/CharSequence;IILandroid/os/LocaleList;)Landroid/view/textclassifier/TextSelection;" />
+		<field name="NO_OP" />
+		<field name="TYPE_ADDRESS" />
+		<field name="TYPE_EMAIL" />
+		<field name="TYPE_OTHER" />
+		<field name="TYPE_PHONE" />
+		<field name="TYPE_URL" />
+	</class>
+	<class name="android/view/textclassifier/TextSelection" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="getConfidenceScore(Ljava/lang/String;)F" />
+		<method name="getEntity(I)Ljava/lang/String;" />
+		<method name="getEntityCount()I" />
+		<method name="getSelectionEndIndex()I" />
+		<method name="getSelectionStartIndex()I" />
+	</class>
+	<class name="android/view/textclassifier/TextSelection$Builder" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>(II)V" />
+		<method name="build()Landroid/view/textclassifier/TextSelection;" />
+		<method name="setEntityType(Ljava/lang/String;F)Landroid/view/textclassifier/TextSelection$Builder;" />
+	</class>
 	<class name="android/view/textservice/SentenceSuggestionsInfo" since="16">
 		<extends name="java/lang/Object" />
 		<implements name="android/os/Parcelable" />
@@ -45532,6 +48576,12 @@
 		<method name="getEmbeddedView(ILandroid/content/Context;)Landroid/view/View;" />
 		<method name="getFullScreenView(ILandroid/content/Context;)Landroid/view/View;" />
 	</class>
+	<class name="android/webkit/RenderProcessGoneDetail" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="didCrash()Z" />
+		<method name="rendererPriorityAtExit()I" />
+	</class>
 	<class name="android/webkit/ServiceWorkerClient" since="24">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
@@ -45778,8 +48828,9 @@
 		<method name="getPluginState()Landroid/webkit/WebSettings$PluginState;" since="8" deprecated="18" />
 		<method name="getPluginsEnabled()Z" deprecated="16" />
 		<method name="getPluginsPath()Ljava/lang/String;" deprecated="16" />
+		<method name="getSafeBrowsingEnabled()Z" since="26" />
 		<method name="getSansSerifFontFamily()Ljava/lang/String;" />
-		<method name="getSaveFormData()Z" />
+		<method name="getSaveFormData()Z" deprecated="26" />
 		<method name="getSavePassword()Z" deprecated="18" />
 		<method name="getSerifFontFamily()Ljava/lang/String;" />
 		<method name="getStandardFontFamily()Ljava/lang/String;" />
@@ -45833,8 +48884,9 @@
 		<method name="setPluginsEnabled(Z)V" deprecated="16" />
 		<method name="setPluginsPath(Ljava/lang/String;)V" deprecated="16" />
 		<method name="setRenderPriority(Landroid/webkit/WebSettings$RenderPriority;)V" deprecated="18" />
+		<method name="setSafeBrowsingEnabled(Z)V" since="26" />
 		<method name="setSansSerifFontFamily(Ljava/lang/String;)V" />
-		<method name="setSaveFormData(Z)V" />
+		<method name="setSaveFormData(Z)V" deprecated="26" />
 		<method name="setSavePassword(Z)V" deprecated="18" />
 		<method name="setSerifFontFamily(Ljava/lang/String;)V" />
 		<method name="setStandardFontFamily(Ljava/lang/String;)V" />
@@ -45983,17 +49035,22 @@
 		<method name="freeMemory()V" since="7" deprecated="19" />
 		<method name="getCertificate()Landroid/net/http/SslCertificate;" />
 		<method name="getContentHeight()I" />
+		<method name="getCurrentWebViewPackage()Landroid/content/pm/PackageInfo;" since="26" />
 		<method name="getFavicon()Landroid/graphics/Bitmap;" />
 		<method name="getHitTestResult()Landroid/webkit/WebView$HitTestResult;" />
-		<method name="getHttpAuthUsernamePassword(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;" />
+		<method name="getHttpAuthUsernamePassword(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;" deprecated="26" />
 		<method name="getOriginalUrl()Ljava/lang/String;" since="3" />
 		<method name="getPluginList()Landroid/webkit/PluginList;" />
 		<method name="getProgress()I" />
+		<method name="getRendererPriorityWaivedWhenNotVisible()Z" since="26" />
+		<method name="getRendererRequestedPriority()I" since="26" />
 		<method name="getScale()F" deprecated="17" />
 		<method name="getSettings()Landroid/webkit/WebSettings;" />
 		<method name="getTitle()Ljava/lang/String;" />
 		<method name="getUrl()Ljava/lang/String;" />
 		<method name="getVisibleTitleHeight()I" since="11" deprecated="16" />
+		<method name="getWebChromeClient()Landroid/webkit/WebChromeClient;" since="26" />
+		<method name="getWebViewClient()Landroid/webkit/WebViewClient;" since="26" />
 		<method name="getZoomControls()Landroid/view/View;" />
 		<method name="goBack()V" />
 		<method name="goBackOrForward(I)V" />
@@ -46031,11 +49088,12 @@
 		<method name="setDownloadListener(Landroid/webkit/DownloadListener;)V" />
 		<method name="setFindListener(Landroid/webkit/WebView$FindListener;)V" since="16" />
 		<method name="setHorizontalScrollbarOverlay(Z)V" deprecated="23" />
-		<method name="setHttpAuthUsernamePassword(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V" />
+		<method name="setHttpAuthUsernamePassword(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V" deprecated="26" />
 		<method name="setInitialScale(I)V" />
 		<method name="setMapTrackballToArrowKeys(Z)V" deprecated="17" />
 		<method name="setNetworkAvailable(Z)V" since="3" />
 		<method name="setPictureListener(Landroid/webkit/WebView$PictureListener;)V" deprecated="16" />
+		<method name="setRendererPriorityPolicy(IZ)V" since="26" />
 		<method name="setVerticalScrollbarOverlay(Z)V" deprecated="23" />
 		<method name="setWebChromeClient(Landroid/webkit/WebChromeClient;)V" />
 		<method name="setWebContentsDebuggingEnabled(Z)V" since="19" />
@@ -46045,6 +49103,9 @@
 		<method name="zoomBy(F)V" since="21" />
 		<method name="zoomIn()Z" />
 		<method name="zoomOut()Z" />
+		<field name="RENDERER_PRIORITY_BOUND" since="26" />
+		<field name="RENDERER_PRIORITY_IMPORTANT" since="26" />
+		<field name="RENDERER_PRIORITY_WAIVED" since="26" />
 		<field name="SCHEME_GEO" />
 		<field name="SCHEME_MAILTO" />
 		<field name="SCHEME_TEL" />
@@ -46101,6 +49162,7 @@
 		<method name="onReceivedHttpError(Landroid/webkit/WebView;Landroid/webkit/WebResourceRequest;Landroid/webkit/WebResourceResponse;)V" since="23" />
 		<method name="onReceivedLoginRequest(Landroid/webkit/WebView;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V" since="12" />
 		<method name="onReceivedSslError(Landroid/webkit/WebView;Landroid/webkit/SslErrorHandler;Landroid/net/http/SslError;)V" since="8" />
+		<method name="onRenderProcessGone(Landroid/webkit/WebView;Landroid/webkit/RenderProcessGoneDetail;)Z" since="26" />
 		<method name="onScaleChanged(Landroid/webkit/WebView;FF)V" />
 		<method name="onTooManyRedirects(Landroid/webkit/WebView;Landroid/os/Message;Landroid/os/Message;)V" deprecated="16" />
 		<method name="onUnhandledInputEvent(Landroid/webkit/WebView;Landroid/view/InputEvent;)V" since="21" />
@@ -46123,19 +49185,22 @@
 		<field name="ERROR_TIMEOUT" since="5" />
 		<field name="ERROR_TOO_MANY_REQUESTS" since="5" />
 		<field name="ERROR_UNKNOWN" since="5" />
+		<field name="ERROR_UNSAFE_RESOURCE" since="26" />
 		<field name="ERROR_UNSUPPORTED_AUTH_SCHEME" since="5" />
 		<field name="ERROR_UNSUPPORTED_SCHEME" since="5" />
 	</class>
 	<class name="android/webkit/WebViewDatabase" since="1">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
-		<method name="clearFormData()V" />
+		<method name="clearFormData()V" deprecated="26" />
 		<method name="clearHttpAuthUsernamePassword()V" />
 		<method name="clearUsernamePassword()V" deprecated="18" />
+		<method name="getHttpAuthUsernamePassword(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;" since="26" />
 		<method name="getInstance(Landroid/content/Context;)Landroid/webkit/WebViewDatabase;" />
-		<method name="hasFormData()Z" />
+		<method name="hasFormData()Z" deprecated="26" />
 		<method name="hasHttpAuthUsernamePassword()Z" />
 		<method name="hasUsernamePassword()Z" deprecated="18" />
+		<method name="setHttpAuthUsernamePassword(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V" since="26" />
 		<field name="LOGTAG" />
 	</class>
 	<class name="android/webkit/WebViewFragment" since="11">
@@ -46339,6 +49404,7 @@
 	</class>
 	<class name="android/widget/Adapter" since="1">
 		<extends name="java/lang/Object" />
+		<method name="getAutofillOptions()[Ljava/lang/CharSequence;" since="26" />
 		<method name="getCount()I" />
 		<method name="getItem(I)Ljava/lang/Object;" />
 		<method name="getItemId(I)J" />
@@ -46656,6 +49722,7 @@
 		<method name="getFormat()Ljava/lang/String;" />
 		<method name="getOnChronometerTickListener()Landroid/widget/Chronometer$OnChronometerTickListener;" since="3" />
 		<method name="isCountDown()Z" since="24" />
+		<method name="isTheFinalCountDown()Z" since="26" />
 		<method name="setBase(J)V" />
 		<method name="setCountDown(Z)V" since="24" />
 		<method name="setFormat(Ljava/lang/String;)V" />
@@ -46751,6 +49818,7 @@
 		<method name="setFirstDayOfWeek(I)V" since="21" />
 		<method name="setMaxDate(J)V" since="11" />
 		<method name="setMinDate(J)V" since="11" />
+		<method name="setOnDateChangedListener(Landroid/widget/DatePicker$OnDateChangedListener;)V" since="26" />
 		<method name="setSpinnersShown(Z)V" since="11" deprecated="24" />
 		<method name="updateDate(III)V" />
 	</class>
@@ -46758,7 +49826,7 @@
 		<extends name="java/lang/Object" />
 		<method name="onDateChanged(Landroid/widget/DatePicker;III)V" />
 	</class>
-	<class name="android/widget/DialerFilter" since="1">
+	<class name="android/widget/DialerFilter" since="1" deprecated="26">
 		<extends name="android/widget/RelativeLayout" />
 		<method name="&lt;init>(Landroid/content/Context;)V" />
 		<method name="&lt;init>(Landroid/content/Context;Landroid/util/AttributeSet;)V" />
@@ -47437,8 +50505,8 @@
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>(Landroid/content/Context;)V" />
 		<method name="&lt;init>(Landroid/content/Context;Landroid/view/animation/Interpolator;)V" />
-		<method name="&lt;init>(Landroid/content/Context;Landroid/view/animation/Interpolator;FF)V" />
-		<method name="&lt;init>(Landroid/content/Context;Landroid/view/animation/Interpolator;FFZ)V" since="11" />
+		<method name="&lt;init>(Landroid/content/Context;Landroid/view/animation/Interpolator;FF)V" deprecated="26" />
+		<method name="&lt;init>(Landroid/content/Context;Landroid/view/animation/Interpolator;FFZ)V" since="11" deprecated="26" />
 		<method name="abortAnimation()V" />
 		<method name="computeScrollOffset()Z" />
 		<method name="fling(IIIIIIII)V" />
@@ -47570,6 +50638,7 @@
 		<method name="getIndeterminateTintMode()Landroid/graphics/PorterDuff$Mode;" since="21" />
 		<method name="getInterpolator()Landroid/view/animation/Interpolator;" />
 		<method name="getMax()I" />
+		<method name="getMin()I" since="26" />
 		<method name="getProgress()I" />
 		<method name="getProgressBackgroundTintList()Landroid/content/res/ColorStateList;" since="21" />
 		<method name="getProgressBackgroundTintMode()Landroid/graphics/PorterDuff$Mode;" since="21" />
@@ -47581,6 +50650,7 @@
 		<method name="getSecondaryProgressTintMode()Landroid/graphics/PorterDuff$Mode;" since="21" />
 		<method name="incrementProgressBy(I)V" />
 		<method name="incrementSecondaryProgressBy(I)V" />
+		<method name="isAnimating()Z" since="26" />
 		<method name="isIndeterminate()Z" />
 		<method name="setIndeterminate(Z)V" />
 		<method name="setIndeterminateDrawable(Landroid/graphics/drawable/Drawable;)V" />
@@ -47590,6 +50660,7 @@
 		<method name="setInterpolator(Landroid/content/Context;I)V" />
 		<method name="setInterpolator(Landroid/view/animation/Interpolator;)V" />
 		<method name="setMax(I)V" />
+		<method name="setMin(I)V" since="26" />
 		<method name="setProgress(I)V" />
 		<method name="setProgress(IZ)V" since="24" />
 		<method name="setProgressBackgroundTintList(Landroid/content/res/ColorStateList;)V" since="21" />
@@ -48278,6 +51349,11 @@
 		<method name="endBatchEdit()V" since="3" />
 		<method name="extractText(Landroid/view/inputmethod/ExtractedTextRequest;Landroid/view/inputmethod/ExtractedText;)Z" since="3" />
 		<method name="getAutoLinkMask()I" />
+		<method name="getAutoSizeMaxTextSize()I" since="26" />
+		<method name="getAutoSizeMinTextSize()I" since="26" />
+		<method name="getAutoSizeStepGranularity()I" since="26" />
+		<method name="getAutoSizeTextAvailableSizes()[I" since="26" />
+		<method name="getAutoSizeTextType()I" since="26" />
 		<method name="getBreakStrategy()I" since="23" />
 		<method name="getCompoundDrawablePadding()I" />
 		<method name="getCompoundDrawableTintList()Landroid/content/res/ColorStateList;" since="23" />
@@ -48303,6 +51379,7 @@
 		<method name="getExtendedPaddingTop()I" />
 		<method name="getFilters()[Landroid/text/InputFilter;" />
 		<method name="getFontFeatureSettings()Ljava/lang/String;" since="21" />
+		<method name="getFontVariationSettings()Ljava/lang/String;" since="26" />
 		<method name="getFreezesText()Z" />
 		<method name="getGravity()I" />
 		<method name="getHighlightColor()I" since="16" />
@@ -48316,6 +51393,7 @@
 		<method name="getIncludeFontPadding()Z" since="16" />
 		<method name="getInputExtras(Z)Landroid/os/Bundle;" since="3" />
 		<method name="getInputType()I" since="3" />
+		<method name="getJustificationMode()I" since="26" />
 		<method name="getKeyListener()Landroid/text/method/KeyListener;" />
 		<method name="getLayout()Landroid/text/Layout;" />
 		<method name="getLetterSpacing()F" since="21" />
@@ -48348,6 +51426,7 @@
 		<method name="getShadowRadius()F" since="16" />
 		<method name="getShowSoftInputOnFocus()Z" since="21" />
 		<method name="getText()Ljava/lang/CharSequence;" />
+		<method name="getTextClassifier()Landroid/view/textclassifier/TextClassifier;" since="26" />
 		<method name="getTextColor(Landroid/content/Context;Landroid/content/res/TypedArray;I)I" />
 		<method name="getTextColors()Landroid/content/res/ColorStateList;" />
 		<method name="getTextColors(Landroid/content/Context;Landroid/content/res/TypedArray;)Landroid/content/res/ColorStateList;" />
@@ -48387,6 +51466,9 @@
 		<method name="resolveTextDirection()V" since="14" />
 		<method name="setAllCaps(Z)V" since="14" />
 		<method name="setAutoLinkMask(I)V" />
+		<method name="setAutoSizeTextTypeUniformWithConfiguration(IIII)V" since="26" />
+		<method name="setAutoSizeTextTypeUniformWithPresetSizes([II)V" since="26" />
+		<method name="setAutoSizeTextTypeWithDefaults(I)V" since="26" />
 		<method name="setBreakStrategy(I)V" since="23" />
 		<method name="setCompoundDrawablePadding(I)V" />
 		<method name="setCompoundDrawableTintList(Landroid/content/res/ColorStateList;)V" since="23" />
@@ -48409,6 +51491,7 @@
 		<method name="setExtractedText(Landroid/view/inputmethod/ExtractedText;)V" since="3" />
 		<method name="setFilters([Landroid/text/InputFilter;)V" />
 		<method name="setFontFeatureSettings(Ljava/lang/String;)V" since="21" />
+		<method name="setFontVariationSettings(Ljava/lang/String;)Z" since="26" />
 		<method name="setFrame(IIII)Z" />
 		<method name="setFreezesText(Z)V" />
 		<method name="setGravity(I)V" />
@@ -48426,6 +51509,7 @@
 		<method name="setIncludeFontPadding(Z)V" />
 		<method name="setInputExtras(I)V" since="3" />
 		<method name="setInputType(I)V" since="3" />
+		<method name="setJustificationMode(I)V" since="26" />
 		<method name="setKeyListener(Landroid/text/method/KeyListener;)V" />
 		<method name="setLetterSpacing(F)V" since="21" />
 		<method name="setLineSpacing(FF)V" />
@@ -48462,6 +51546,7 @@
 		<method name="setText([CII)V" />
 		<method name="setTextAppearance(I)V" since="23" />
 		<method name="setTextAppearance(Landroid/content/Context;I)V" deprecated="23" />
+		<method name="setTextClassifier(Landroid/view/textclassifier/TextClassifier;)V" since="26" />
 		<method name="setTextColor(I)V" />
 		<method name="setTextColor(Landroid/content/res/ColorStateList;)V" />
 		<method name="setTextIsSelectable(Z)V" since="11" />
@@ -48476,6 +51561,8 @@
 		<method name="setTypeface(Landroid/graphics/Typeface;)V" />
 		<method name="setTypeface(Landroid/graphics/Typeface;I)V" />
 		<method name="setWidth(I)V" />
+		<field name="AUTO_SIZE_TEXT_TYPE_NONE" since="26" />
+		<field name="AUTO_SIZE_TEXT_TYPE_UNIFORM" since="26" />
 	</class>
 	<class name="android/widget/TextView$BufferType" since="1">
 		<extends name="java/lang/Enum" />
@@ -48517,6 +51604,7 @@
 		<method name="setIs24HourView(Ljava/lang/Boolean;)V" />
 		<method name="setMinute(I)V" since="23" />
 		<method name="setOnTimeChangedListener(Landroid/widget/TimePicker$OnTimeChangedListener;)V" />
+		<method name="validateInput()Z" since="26" />
 	</class>
 	<class name="android/widget/TimePicker$OnTimeChangedListener" since="1">
 		<extends name="java/lang/Object" />
@@ -48659,6 +51747,8 @@
 		<method name="addSubtitleSource(Ljava/io/InputStream;Landroid/media/MediaFormat;)V" since="19" />
 		<method name="resolveAdjustedSize(II)I" />
 		<method name="resume()V" since="8" />
+		<method name="setAudioAttributes(Landroid/media/AudioAttributes;)V" since="26" />
+		<method name="setAudioFocusRequest(I)V" since="26" />
 		<method name="setMediaController(Landroid/widget/MediaController;)V" />
 		<method name="setOnCompletionListener(Landroid/media/MediaPlayer$OnCompletionListener;)V" />
 		<method name="setOnErrorListener(Landroid/media/MediaPlayer$OnErrorListener;)V" />
@@ -48716,7 +51806,7 @@
 		<implements name="android/widget/ListAdapter" />
 		<method name="getWrappedAdapter()Landroid/widget/ListAdapter;" />
 	</class>
-	<class name="android/widget/ZoomButton" since="1">
+	<class name="android/widget/ZoomButton" since="1" deprecated="26">
 		<extends name="android/widget/ImageButton" />
 		<implements name="android/view/View$OnLongClickListener" />
 		<method name="&lt;init>(Landroid/content/Context;)V" />
@@ -48725,7 +51815,7 @@
 		<method name="&lt;init>(Landroid/content/Context;Landroid/util/AttributeSet;II)V" since="21" />
 		<method name="setZoomSpeed(J)V" />
 	</class>
-	<class name="android/widget/ZoomButtonsController" since="4">
+	<class name="android/widget/ZoomButtonsController" since="4" deprecated="26">
 		<extends name="java/lang/Object" />
 		<implements name="android/view/View$OnTouchListener" />
 		<method name="&lt;init>(Landroid/view/View;)V" />
@@ -48758,7 +51848,7 @@
 		<method name="setZoomSpeed(J)V" />
 		<method name="show()V" />
 	</class>
-	<class name="com/android/internal/util/Predicate" since="1">
+	<class name="com/android/internal/util/Predicate" since="1" deprecated="26">
 		<extends name="java/lang/Object" />
 		<method name="apply(Ljava/lang/Object;)Z" />
 	</class>
@@ -49086,6 +52176,8 @@
 		<field name="OP_INT_TO_FLOAT" />
 		<field name="OP_INT_TO_LONG" />
 		<field name="OP_INT_TO_SHORT" />
+		<field name="OP_INVOKE_CUSTOM" since="26" />
+		<field name="OP_INVOKE_CUSTOM_RANGE" since="26" />
 		<field name="OP_INVOKE_DIRECT" />
 		<field name="OP_INVOKE_DIRECT_EMPTY" deprecated="16" />
 		<field name="OP_INVOKE_DIRECT_JUMBO" since="11" />
@@ -49093,6 +52185,8 @@
 		<field name="OP_INVOKE_INTERFACE" />
 		<field name="OP_INVOKE_INTERFACE_JUMBO" since="11" />
 		<field name="OP_INVOKE_INTERFACE_RANGE" />
+		<field name="OP_INVOKE_POLYMORPHIC" since="26" />
+		<field name="OP_INVOKE_POLYMORPHIC_RANGE" since="26" />
 		<field name="OP_INVOKE_STATIC" />
 		<field name="OP_INVOKE_STATIC_JUMBO" since="11" />
 		<field name="OP_INVOKE_STATIC_RANGE" />
@@ -49289,16 +52383,20 @@
 		<extends name="java/lang/ClassLoader" />
 		<method name="&lt;init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)V" />
 	</class>
-	<class name="dalvik/system/DexFile" since="1">
+	<class name="dalvik/system/DexFile" since="1" deprecated="26">
 		<extends name="java/lang/Object" />
-		<method name="&lt;init>(Ljava/io/File;)V" />
-		<method name="&lt;init>(Ljava/lang/String;)V" />
+		<method name="&lt;init>(Ljava/io/File;)V" deprecated="26" />
+		<method name="&lt;init>(Ljava/lang/String;)V" deprecated="26" />
 		<method name="close()V" />
 		<method name="entries()Ljava/util/Enumeration;" />
 		<method name="getName()Ljava/lang/String;" />
 		<method name="isDexOptNeeded(Ljava/lang/String;)Z" />
 		<method name="loadClass(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/Class;" />
-		<method name="loadDex(Ljava/lang/String;Ljava/lang/String;I)Ldalvik/system/DexFile;" since="3" />
+		<method name="loadDex(Ljava/lang/String;Ljava/lang/String;I)Ldalvik/system/DexFile;" since="3" deprecated="26" />
+	</class>
+	<class name="dalvik/system/InMemoryDexClassLoader" since="26">
+		<extends name="dalvik/system/BaseDexClassLoader" />
+		<method name="&lt;init>(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V" />
 	</class>
 	<class name="dalvik/system/PathClassLoader" since="1">
 		<extends name="dalvik/system/BaseDexClassLoader" since="14" />
@@ -49805,6 +52903,7 @@
 		<method name="setReadable(ZZ)Z" since="9" />
 		<method name="setWritable(Z)Z" since="9" />
 		<method name="setWritable(ZZ)Z" since="9" />
+		<method name="toPath()Ljava/nio/file/Path;" since="26" />
 		<method name="toURI()Ljava/net/URI;" />
 		<method name="toURL()Ljava/net/URL;" deprecated="16" />
 		<field name="pathSeparator" />
@@ -50423,8 +53522,12 @@
 	</class>
 	<class name="java/lang/AbstractStringBuilder" since="1">
 		<extends name="java/lang/Object" />
+		<implements name="java/lang/Appendable" since="26" />
 		<implements name="java/lang/CharSequence" since="24" />
 		<method name="&lt;init>()V" />
+		<method name="append(C)Ljava/lang/AbstractStringBuilder;" since="26" />
+		<method name="append(Ljava/lang/CharSequence;)Ljava/lang/AbstractStringBuilder;" since="26" />
+		<method name="append(Ljava/lang/CharSequence;II)Ljava/lang/AbstractStringBuilder;" since="26" />
 		<method name="capacity()I" />
 		<method name="charAt(I)C" />
 		<method name="codePointAt(I)I" />
@@ -50505,6 +53608,13 @@
 		<field name="TRUE" />
 		<field name="TYPE" />
 	</class>
+	<class name="java/lang/BootstrapMethodError" since="26">
+		<extends name="java/lang/LinkageError" />
+		<method name="&lt;init>()V" />
+		<method name="&lt;init>(Ljava/lang/String;)V" />
+		<method name="&lt;init>(Ljava/lang/String;Ljava/lang/Throwable;)V" />
+		<method name="&lt;init>(Ljava/lang/Throwable;)V" />
+	</class>
 	<class name="java/lang/Byte" since="1">
 		<extends name="java/lang/Number" />
 		<implements name="java/lang/Comparable" />
@@ -50517,6 +53627,8 @@
 		<method name="parseByte(Ljava/lang/String;)B" />
 		<method name="parseByte(Ljava/lang/String;I)B" />
 		<method name="toString(B)Ljava/lang/String;" />
+		<method name="toUnsignedInt(B)I" since="26" />
+		<method name="toUnsignedLong(B)J" since="26" />
 		<method name="valueOf(B)Ljava/lang/Byte;" />
 		<method name="valueOf(Ljava/lang/String;)Ljava/lang/Byte;" />
 		<method name="valueOf(Ljava/lang/String;I)Ljava/lang/Byte;" />
@@ -50706,6 +53818,8 @@
 		<field name="ANCIENT_GREEK_NUMBERS" since="19" />
 		<field name="ANCIENT_SYMBOLS" since="19" />
 		<field name="ARABIC" />
+		<field name="ARABIC_EXTENDED_A" since="26" />
+		<field name="ARABIC_MATHEMATICAL_ALPHABETIC_SYMBOLS" since="26" />
 		<field name="ARABIC_PRESENTATION_FORMS_A" />
 		<field name="ARABIC_PRESENTATION_FORMS_B" />
 		<field name="ARABIC_SUPPLEMENT" since="19" />
@@ -50728,6 +53842,7 @@
 		<field name="BUHID" />
 		<field name="BYZANTINE_MUSICAL_SYMBOLS" />
 		<field name="CARIAN" since="19" />
+		<field name="CHAKMA" since="26" />
 		<field name="CHAM" since="19" />
 		<field name="CHEROKEE" />
 		<field name="CJK_COMPATIBILITY" />
@@ -50833,6 +53948,10 @@
 		<field name="MATHEMATICAL_ALPHANUMERIC_SYMBOLS" />
 		<field name="MATHEMATICAL_OPERATORS" />
 		<field name="MEETEI_MAYEK" since="19" />
+		<field name="MEETEI_MAYEK_EXTENSIONS" since="26" />
+		<field name="MEROITIC_CURSIVE" since="26" />
+		<field name="MEROITIC_HIEROGLYPHS" since="26" />
+		<field name="MIAO" since="26" />
 		<field name="MISCELLANEOUS_MATHEMATICAL_SYMBOLS_A" />
 		<field name="MISCELLANEOUS_MATHEMATICAL_SYMBOLS_B" />
 		<field name="MISCELLANEOUS_SYMBOLS" />
@@ -50868,12 +53987,15 @@
 		<field name="RUNIC" />
 		<field name="SAMARITAN" since="19" />
 		<field name="SAURASHTRA" since="19" />
+		<field name="SHARADA" since="26" />
 		<field name="SHAVIAN" />
 		<field name="SINHALA" />
 		<field name="SMALL_FORM_VARIANTS" />
+		<field name="SORA_SOMPENG" since="26" />
 		<field name="SPACING_MODIFIER_LETTERS" />
 		<field name="SPECIALS" />
 		<field name="SUNDANESE" since="19" />
+		<field name="SUNDANESE_SUPPLEMENT" since="26" />
 		<field name="SUPERSCRIPTS_AND_SUBSCRIPTS" />
 		<field name="SUPPLEMENTAL_ARROWS_A" />
 		<field name="SUPPLEMENTAL_ARROWS_B" />
@@ -50891,6 +54013,7 @@
 		<field name="TAI_THAM" since="19" />
 		<field name="TAI_VIET" since="19" />
 		<field name="TAI_XUAN_JING_SYMBOLS" />
+		<field name="TAKRI" since="26" />
 		<field name="TAMIL" />
 		<field name="TELUGU" />
 		<field name="THAANA" />
@@ -50930,6 +54053,7 @@
 		<field name="BUHID" />
 		<field name="CANADIAN_ABORIGINAL" />
 		<field name="CARIAN" />
+		<field name="CHAKMA" since="26" />
 		<field name="CHAM" />
 		<field name="CHEROKEE" />
 		<field name="COMMON" />
@@ -50974,6 +54098,9 @@
 		<field name="MALAYALAM" />
 		<field name="MANDAIC" />
 		<field name="MEETEI_MAYEK" />
+		<field name="MEROITIC_CURSIVE" since="26" />
+		<field name="MEROITIC_HIEROGLYPHS" since="26" />
+		<field name="MIAO" since="26" />
 		<field name="MONGOLIAN" />
 		<field name="MYANMAR" />
 		<field name="NEW_TAI_LUE" />
@@ -50992,8 +54119,10 @@
 		<field name="RUNIC" />
 		<field name="SAMARITAN" />
 		<field name="SAURASHTRA" />
+		<field name="SHARADA" since="26" />
 		<field name="SHAVIAN" />
 		<field name="SINHALA" />
+		<field name="SORA_SOMPENG" since="26" />
 		<field name="SUNDANESE" />
 		<field name="SYLOTI_NAGRI" />
 		<field name="SYRIAC" />
@@ -51002,6 +54131,7 @@
 		<field name="TAI_LE" />
 		<field name="TAI_THAM" />
 		<field name="TAI_VIET" />
+		<field name="TAKRI" since="26" />
 		<field name="TAMIL" />
 		<field name="TELUGU" />
 		<field name="THAANA" />
@@ -51059,6 +54189,7 @@
 		<method name="getSigners()[Ljava/lang/Object;" />
 		<method name="getSimpleName()Ljava/lang/String;" />
 		<method name="getSuperclass()Ljava/lang/Class;" />
+		<method name="getTypeName()Ljava/lang/String;" since="26" />
 		<method name="isAnnotation()Z" />
 		<method name="isAnonymousClass()Z" />
 		<method name="isArray()Z" />
@@ -51071,6 +54202,7 @@
 		<method name="isPrimitive()Z" />
 		<method name="isSynthetic()Z" />
 		<method name="newInstance()Ljava/lang/Object;" />
+		<method name="toGenericString()Ljava/lang/String;" since="26" />
 	</class>
 	<class name="java/lang/ClassCastException" since="1">
 		<extends name="java/lang/RuntimeException" />
@@ -51341,7 +54473,9 @@
 		<method name="bitCount(I)I" />
 		<method name="compare(II)I" since="19" />
 		<method name="compareTo(Ljava/lang/Integer;)I" />
+		<method name="compareUnsigned(II)I" since="26" />
 		<method name="decode(Ljava/lang/String;)Ljava/lang/Integer;" />
+		<method name="divideUnsigned(II)I" since="26" />
 		<method name="getInteger(Ljava/lang/String;)Ljava/lang/Integer;" />
 		<method name="getInteger(Ljava/lang/String;I)Ljava/lang/Integer;" />
 		<method name="getInteger(Ljava/lang/String;Ljava/lang/Integer;)Ljava/lang/Integer;" />
@@ -51354,6 +54488,9 @@
 		<method name="numberOfTrailingZeros(I)I" />
 		<method name="parseInt(Ljava/lang/String;)I" />
 		<method name="parseInt(Ljava/lang/String;I)I" />
+		<method name="parseUnsignedInt(Ljava/lang/String;)I" since="26" />
+		<method name="parseUnsignedInt(Ljava/lang/String;I)I" since="26" />
+		<method name="remainderUnsigned(II)I" since="26" />
 		<method name="reverse(I)I" />
 		<method name="reverseBytes(I)I" />
 		<method name="rotateLeft(II)I" />
@@ -51365,6 +54502,9 @@
 		<method name="toOctalString(I)Ljava/lang/String;" />
 		<method name="toString(I)Ljava/lang/String;" />
 		<method name="toString(II)Ljava/lang/String;" />
+		<method name="toUnsignedLong(I)J" since="26" />
+		<method name="toUnsignedString(I)Ljava/lang/String;" since="26" />
+		<method name="toUnsignedString(II)Ljava/lang/String;" since="26" />
 		<method name="valueOf(I)Ljava/lang/Integer;" />
 		<method name="valueOf(Ljava/lang/String;)Ljava/lang/Integer;" />
 		<method name="valueOf(Ljava/lang/String;I)Ljava/lang/Integer;" />
@@ -51406,7 +54546,9 @@
 		<method name="bitCount(J)I" />
 		<method name="compare(JJ)I" since="19" />
 		<method name="compareTo(Ljava/lang/Long;)I" />
+		<method name="compareUnsigned(JJ)I" since="26" />
 		<method name="decode(Ljava/lang/String;)Ljava/lang/Long;" />
+		<method name="divideUnsigned(JJ)J" since="26" />
 		<method name="getLong(Ljava/lang/String;)Ljava/lang/Long;" />
 		<method name="getLong(Ljava/lang/String;J)Ljava/lang/Long;" />
 		<method name="getLong(Ljava/lang/String;Ljava/lang/Long;)Ljava/lang/Long;" />
@@ -51419,6 +54561,9 @@
 		<method name="numberOfTrailingZeros(J)I" />
 		<method name="parseLong(Ljava/lang/String;)J" />
 		<method name="parseLong(Ljava/lang/String;I)J" />
+		<method name="parseUnsignedLong(Ljava/lang/String;)J" since="26" />
+		<method name="parseUnsignedLong(Ljava/lang/String;I)J" since="26" />
+		<method name="remainderUnsigned(JJ)J" since="26" />
 		<method name="reverse(J)J" />
 		<method name="reverseBytes(J)J" />
 		<method name="rotateLeft(JI)J" />
@@ -51430,6 +54575,8 @@
 		<method name="toOctalString(J)Ljava/lang/String;" />
 		<method name="toString(J)Ljava/lang/String;" />
 		<method name="toString(JI)Ljava/lang/String;" />
+		<method name="toUnsignedString(J)Ljava/lang/String;" since="26" />
+		<method name="toUnsignedString(JI)Ljava/lang/String;" since="26" />
 		<method name="valueOf(J)Ljava/lang/Long;" />
 		<method name="valueOf(Ljava/lang/String;)Ljava/lang/Long;" />
 		<method name="valueOf(Ljava/lang/String;I)Ljava/lang/Long;" />
@@ -51615,11 +54762,14 @@
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
 		<method name="destroy()V" />
+		<method name="destroyForcibly()Ljava/lang/Process;" since="26" />
 		<method name="exitValue()I" />
 		<method name="getErrorStream()Ljava/io/InputStream;" />
 		<method name="getInputStream()Ljava/io/InputStream;" />
 		<method name="getOutputStream()Ljava/io/OutputStream;" />
+		<method name="isAlive()Z" since="26" />
 		<method name="waitFor()I" />
+		<method name="waitFor(JLjava/util/concurrent/TimeUnit;)Z" since="26" />
 	</class>
 	<class name="java/lang/ProcessBuilder" since="1">
 		<extends name="java/lang/Object" />
@@ -51631,10 +54781,41 @@
 		<method name="directory()Ljava/io/File;" />
 		<method name="directory(Ljava/io/File;)Ljava/lang/ProcessBuilder;" />
 		<method name="environment()Ljava/util/Map;" />
+		<method name="inheritIO()Ljava/lang/ProcessBuilder;" since="26" />
+		<method name="redirectError()Ljava/lang/ProcessBuilder$Redirect;" since="26" />
+		<method name="redirectError(Ljava/io/File;)Ljava/lang/ProcessBuilder;" since="26" />
+		<method name="redirectError(Ljava/lang/ProcessBuilder$Redirect;)Ljava/lang/ProcessBuilder;" since="26" />
 		<method name="redirectErrorStream()Z" />
 		<method name="redirectErrorStream(Z)Ljava/lang/ProcessBuilder;" />
+		<method name="redirectInput()Ljava/lang/ProcessBuilder$Redirect;" since="26" />
+		<method name="redirectInput(Ljava/io/File;)Ljava/lang/ProcessBuilder;" since="26" />
+		<method name="redirectInput(Ljava/lang/ProcessBuilder$Redirect;)Ljava/lang/ProcessBuilder;" since="26" />
+		<method name="redirectOutput()Ljava/lang/ProcessBuilder$Redirect;" since="26" />
+		<method name="redirectOutput(Ljava/io/File;)Ljava/lang/ProcessBuilder;" since="26" />
+		<method name="redirectOutput(Ljava/lang/ProcessBuilder$Redirect;)Ljava/lang/ProcessBuilder;" since="26" />
 		<method name="start()Ljava/lang/Process;" />
 	</class>
+	<class name="java/lang/ProcessBuilder$Redirect" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="appendTo(Ljava/io/File;)Ljava/lang/ProcessBuilder$Redirect;" />
+		<method name="file()Ljava/io/File;" />
+		<method name="from(Ljava/io/File;)Ljava/lang/ProcessBuilder$Redirect;" />
+		<method name="to(Ljava/io/File;)Ljava/lang/ProcessBuilder$Redirect;" />
+		<method name="type()Ljava/lang/ProcessBuilder$Redirect$Type;" />
+		<field name="INHERIT" />
+		<field name="PIPE" />
+	</class>
+	<class name="java/lang/ProcessBuilder$Redirect$Type" since="26">
+		<extends name="java/lang/Enum" />
+		<method name="valueOf(Ljava/lang/String;)Ljava/lang/ProcessBuilder$Redirect$Type;" />
+		<method name="values()[Ljava/lang/ProcessBuilder$Redirect$Type;" />
+		<field name="APPEND" />
+		<field name="INHERIT" />
+		<field name="PIPE" />
+		<field name="READ" />
+		<field name="WRITE" />
+	</class>
 	<class name="java/lang/Readable" since="1">
 		<extends name="java/lang/Object" />
 		<method name="read(Ljava/nio/CharBuffer;)I" />
@@ -51761,6 +54942,8 @@
 		<method name="parseShort(Ljava/lang/String;I)S" />
 		<method name="reverseBytes(S)S" />
 		<method name="toString(S)Ljava/lang/String;" />
+		<method name="toUnsignedInt(S)I" since="26" />
+		<method name="toUnsignedLong(S)J" since="26" />
 		<method name="valueOf(Ljava/lang/String;)Ljava/lang/Short;" />
 		<method name="valueOf(Ljava/lang/String;I)Ljava/lang/Short;" />
 		<method name="valueOf(S)Ljava/lang/Short;" />
@@ -51904,6 +55087,8 @@
 		<method name="indexOf(Ljava/lang/String;I)I" />
 		<method name="intern()Ljava/lang/String;" />
 		<method name="isEmpty()Z" since="9" />
+		<method name="join(Ljava/lang/CharSequence;Ljava/lang/Iterable;)Ljava/lang/String;" since="26" />
+		<method name="join(Ljava/lang/CharSequence;[Ljava/lang/CharSequence;)Ljava/lang/String;" since="26" />
 		<method name="lastIndexOf(I)I" />
 		<method name="lastIndexOf(II)I" />
 		<method name="lastIndexOf(Ljava/lang/String;)I" />
@@ -52173,6 +55358,7 @@
 		<method name="initialValue()Ljava/lang/Object;" />
 		<method name="remove()V" />
 		<method name="set(Ljava/lang/Object;)V" />
+		<method name="withInitial(Ljava/util/function/Supplier;)Ljava/lang/ThreadLocal;" since="26" />
 	</class>
 	<class name="java/lang/Throwable" since="1">
 		<extends name="java/lang/Object" />
@@ -52271,6 +55457,8 @@
 		<field name="PACKAGE" />
 		<field name="PARAMETER" />
 		<field name="TYPE" />
+		<field name="TYPE_PARAMETER" since="26" />
+		<field name="TYPE_USE" since="26" />
 	</class>
 	<class name="java/lang/annotation/IncompleteAnnotationException" since="1">
 		<extends name="java/lang/RuntimeException" />
@@ -52282,6 +55470,10 @@
 		<extends name="java/lang/Object" />
 		<implements name="java/lang/annotation/Annotation" />
 	</class>
+	<class name="java/lang/annotation/Native" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/lang/annotation/Annotation" />
+	</class>
 	<class name="java/lang/annotation/Repeatable" since="24">
 		<extends name="java/lang/Object" />
 		<implements name="java/lang/annotation/Annotation" />
@@ -52305,6 +55497,167 @@
 		<implements name="java/lang/annotation/Annotation" />
 		<method name="value()[Ljava/lang/annotation/ElementType;" />
 	</class>
+	<class name="java/lang/invoke/CallSite" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="dynamicInvoker()Ljava/lang/invoke/MethodHandle;" />
+		<method name="getTarget()Ljava/lang/invoke/MethodHandle;" />
+		<method name="setTarget(Ljava/lang/invoke/MethodHandle;)V" />
+		<method name="type()Ljava/lang/invoke/MethodType;" />
+	</class>
+	<class name="java/lang/invoke/ConstantCallSite" since="26">
+		<extends name="java/lang/invoke/CallSite" />
+		<method name="&lt;init>(Ljava/lang/invoke/MethodHandle;)V" />
+		<method name="&lt;init>(Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;)V" />
+	</class>
+	<class name="java/lang/invoke/LambdaConversionException" since="26">
+		<extends name="java/lang/Exception" />
+		<method name="&lt;init>()V" />
+		<method name="&lt;init>(Ljava/lang/String;)V" />
+		<method name="&lt;init>(Ljava/lang/String;Ljava/lang/Throwable;)V" />
+		<method name="&lt;init>(Ljava/lang/String;Ljava/lang/Throwable;ZZ)V" />
+		<method name="&lt;init>(Ljava/lang/Throwable;)V" />
+	</class>
+	<class name="java/lang/invoke/MethodHandle" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="asCollector(Ljava/lang/Class;I)Ljava/lang/invoke/MethodHandle;" />
+		<method name="asFixedArity()Ljava/lang/invoke/MethodHandle;" />
+		<method name="asSpreader(Ljava/lang/Class;I)Ljava/lang/invoke/MethodHandle;" />
+		<method name="asType(Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;" />
+		<method name="asVarargsCollector(Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle;" />
+		<method name="bindTo(Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle;" />
+		<method name="invoke([Ljava/lang/Object;)Ljava/lang/Object;" />
+		<method name="invokeExact([Ljava/lang/Object;)Ljava/lang/Object;" />
+		<method name="invokeWithArguments(Ljava/util/List;)Ljava/lang/Object;" />
+		<method name="invokeWithArguments([Ljava/lang/Object;)Ljava/lang/Object;" />
+		<method name="isVarargsCollector()Z" />
+		<method name="type()Ljava/lang/invoke/MethodType;" />
+	</class>
+	<class name="java/lang/invoke/MethodHandleInfo" since="26">
+		<extends name="java/lang/Object" />
+		<method name="getDeclaringClass()Ljava/lang/Class;" />
+		<method name="getMethodType()Ljava/lang/invoke/MethodType;" />
+		<method name="getModifiers()I" />
+		<method name="getName()Ljava/lang/String;" />
+		<method name="getReferenceKind()I" />
+		<method name="isVarArgs()Z" />
+		<method name="refKindIsField(I)Z" />
+		<method name="refKindIsValid(I)Z" />
+		<method name="refKindName(I)Ljava/lang/String;" />
+		<method name="referenceKindToString(I)Ljava/lang/String;" />
+		<method name="reflectAs(Ljava/lang/Class;Ljava/lang/invoke/MethodHandles$Lookup;)Ljava/lang/reflect/Member;" />
+		<method name="toString(ILjava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/String;" />
+		<field name="REF_getField" />
+		<field name="REF_getStatic" />
+		<field name="REF_invokeInterface" />
+		<field name="REF_invokeSpecial" />
+		<field name="REF_invokeStatic" />
+		<field name="REF_invokeVirtual" />
+		<field name="REF_newInvokeSpecial" />
+		<field name="REF_putField" />
+		<field name="REF_putStatic" />
+	</class>
+	<class name="java/lang/invoke/MethodHandles" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="arrayElementGetter(Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle;" />
+		<method name="arrayElementSetter(Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle;" />
+		<method name="catchException(Ljava/lang/invoke/MethodHandle;Ljava/lang/Class;Ljava/lang/invoke/MethodHandle;)Ljava/lang/invoke/MethodHandle;" />
+		<method name="collectArguments(Ljava/lang/invoke/MethodHandle;ILjava/lang/invoke/MethodHandle;)Ljava/lang/invoke/MethodHandle;" />
+		<method name="constant(Ljava/lang/Class;Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle;" />
+		<method name="dropArguments(Ljava/lang/invoke/MethodHandle;ILjava/util/List;)Ljava/lang/invoke/MethodHandle;" />
+		<method name="dropArguments(Ljava/lang/invoke/MethodHandle;I[Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle;" />
+		<method name="exactInvoker(Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;" />
+		<method name="explicitCastArguments(Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;" />
+		<method name="filterArguments(Ljava/lang/invoke/MethodHandle;I[Ljava/lang/invoke/MethodHandle;)Ljava/lang/invoke/MethodHandle;" />
+		<method name="filterReturnValue(Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)Ljava/lang/invoke/MethodHandle;" />
+		<method name="foldArguments(Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)Ljava/lang/invoke/MethodHandle;" />
+		<method name="guardWithTest(Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)Ljava/lang/invoke/MethodHandle;" />
+		<method name="identity(Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle;" />
+		<method name="insertArguments(Ljava/lang/invoke/MethodHandle;I[Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle;" />
+		<method name="invoker(Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;" />
+		<method name="lookup()Ljava/lang/invoke/MethodHandles$Lookup;" />
+		<method name="permuteArguments(Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;[I)Ljava/lang/invoke/MethodHandle;" />
+		<method name="publicLookup()Ljava/lang/invoke/MethodHandles$Lookup;" />
+		<method name="reflectAs(Ljava/lang/Class;Ljava/lang/invoke/MethodHandle;)Ljava/lang/reflect/Member;" />
+		<method name="spreadInvoker(Ljava/lang/invoke/MethodType;I)Ljava/lang/invoke/MethodHandle;" />
+		<method name="throwException(Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle;" />
+	</class>
+	<class name="java/lang/invoke/MethodHandles$Lookup" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="bind(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;" />
+		<method name="findConstructor(Ljava/lang/Class;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;" />
+		<method name="findGetter(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle;" />
+		<method name="findSetter(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle;" />
+		<method name="findSpecial(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle;" />
+		<method name="findStatic(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;" />
+		<method name="findStaticGetter(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle;" />
+		<method name="findStaticSetter(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle;" />
+		<method name="findVirtual(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;" />
+		<method name="in(Ljava/lang/Class;)Ljava/lang/invoke/MethodHandles$Lookup;" />
+		<method name="lookupClass()Ljava/lang/Class;" />
+		<method name="lookupModes()I" />
+		<method name="revealDirect(Ljava/lang/invoke/MethodHandle;)Ljava/lang/invoke/MethodHandleInfo;" />
+		<method name="unreflect(Ljava/lang/reflect/Method;)Ljava/lang/invoke/MethodHandle;" />
+		<method name="unreflectConstructor(Ljava/lang/reflect/Constructor;)Ljava/lang/invoke/MethodHandle;" />
+		<method name="unreflectGetter(Ljava/lang/reflect/Field;)Ljava/lang/invoke/MethodHandle;" />
+		<method name="unreflectSetter(Ljava/lang/reflect/Field;)Ljava/lang/invoke/MethodHandle;" />
+		<method name="unreflectSpecial(Ljava/lang/reflect/Method;Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle;" />
+		<field name="PACKAGE" />
+		<field name="PRIVATE" />
+		<field name="PROTECTED" />
+		<field name="PUBLIC" />
+	</class>
+	<class name="java/lang/invoke/MethodType" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/io/Serializable" />
+		<method name="&lt;init>()V" />
+		<method name="appendParameterTypes(Ljava/util/List;)Ljava/lang/invoke/MethodType;" />
+		<method name="appendParameterTypes([Ljava/lang/Class;)Ljava/lang/invoke/MethodType;" />
+		<method name="changeParameterType(ILjava/lang/Class;)Ljava/lang/invoke/MethodType;" />
+		<method name="changeReturnType(Ljava/lang/Class;)Ljava/lang/invoke/MethodType;" />
+		<method name="dropParameterTypes(II)Ljava/lang/invoke/MethodType;" />
+		<method name="erase()Ljava/lang/invoke/MethodType;" />
+		<method name="fromMethodDescriptorString(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/invoke/MethodType;" />
+		<method name="generic()Ljava/lang/invoke/MethodType;" />
+		<method name="genericMethodType(I)Ljava/lang/invoke/MethodType;" />
+		<method name="genericMethodType(IZ)Ljava/lang/invoke/MethodType;" />
+		<method name="hasPrimitives()Z" />
+		<method name="hasWrappers()Z" />
+		<method name="insertParameterTypes(ILjava/util/List;)Ljava/lang/invoke/MethodType;" />
+		<method name="insertParameterTypes(I[Ljava/lang/Class;)Ljava/lang/invoke/MethodType;" />
+		<method name="methodType(Ljava/lang/Class;)Ljava/lang/invoke/MethodType;" />
+		<method name="methodType(Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/invoke/MethodType;" />
+		<method name="methodType(Ljava/lang/Class;Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/invoke/MethodType;" />
+		<method name="methodType(Ljava/lang/Class;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodType;" />
+		<method name="methodType(Ljava/lang/Class;Ljava/util/List;)Ljava/lang/invoke/MethodType;" />
+		<method name="methodType(Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/invoke/MethodType;" />
+		<method name="parameterArray()[Ljava/lang/Class;" />
+		<method name="parameterCount()I" />
+		<method name="parameterList()Ljava/util/List;" />
+		<method name="parameterType(I)Ljava/lang/Class;" />
+		<method name="returnType()Ljava/lang/Class;" />
+		<method name="toMethodDescriptorString()Ljava/lang/String;" />
+		<method name="unwrap()Ljava/lang/invoke/MethodType;" />
+		<method name="wrap()Ljava/lang/invoke/MethodType;" />
+	</class>
+	<class name="java/lang/invoke/MutableCallSite" since="26">
+		<extends name="java/lang/invoke/CallSite" />
+		<method name="&lt;init>(Ljava/lang/invoke/MethodHandle;)V" />
+		<method name="&lt;init>(Ljava/lang/invoke/MethodType;)V" />
+	</class>
+	<class name="java/lang/invoke/VolatileCallSite" since="26">
+		<extends name="java/lang/invoke/CallSite" />
+		<method name="&lt;init>(Ljava/lang/invoke/MethodHandle;)V" />
+		<method name="&lt;init>(Ljava/lang/invoke/MethodType;)V" />
+	</class>
+	<class name="java/lang/invoke/WrongMethodTypeException" since="26">
+		<extends name="java/lang/RuntimeException" />
+		<method name="&lt;init>()V" />
+		<method name="&lt;init>(Ljava/lang/String;)V" />
+	</class>
 	<class name="java/lang/ref/PhantomReference" since="1">
 		<extends name="java/lang/ref/Reference" />
 		<method name="&lt;init>(Ljava/lang/Object;Ljava/lang/ref/ReferenceQueue;)V" />
@@ -52379,6 +55732,7 @@
 	</class>
 	<class name="java/lang/reflect/Constructor" since="1">
 		<extends name="java/lang/reflect/AccessibleObject" />
+		<extends name="java/lang/reflect/Executable" since="26" />
 		<implements name="java/lang/reflect/GenericDeclaration" />
 		<implements name="java/lang/reflect/Member" />
 		<method name="&lt;init>()V" />
@@ -52391,6 +55745,21 @@
 		<method name="newInstance([Ljava/lang/Object;)Ljava/lang/Object;" />
 		<method name="toGenericString()Ljava/lang/String;" />
 	</class>
+	<class name="java/lang/reflect/Executable" since="26">
+		<extends name="java/lang/reflect/AccessibleObject" />
+		<implements name="java/lang/reflect/GenericDeclaration" />
+		<implements name="java/lang/reflect/Member" />
+		<method name="&lt;init>()V" />
+		<method name="getExceptionTypes()[Ljava/lang/Class;" />
+		<method name="getGenericExceptionTypes()[Ljava/lang/reflect/Type;" />
+		<method name="getGenericParameterTypes()[Ljava/lang/reflect/Type;" />
+		<method name="getParameterAnnotations()[[Ljava/lang/annotation/Annotation;" />
+		<method name="getParameterCount()I" />
+		<method name="getParameterTypes()[Ljava/lang/Class;" />
+		<method name="getParameters()[Ljava/lang/reflect/Parameter;" />
+		<method name="isVarArgs()Z" />
+		<method name="toGenericString()Ljava/lang/String;" />
+	</class>
 	<class name="java/lang/reflect/Field" since="1">
 		<extends name="java/lang/reflect/AccessibleObject" />
 		<implements name="java/lang/reflect/Member" />
@@ -52425,11 +55794,13 @@
 	</class>
 	<class name="java/lang/reflect/GenericDeclaration" since="1">
 		<extends name="java/lang/Object" />
+		<implements name="java/lang/reflect/AnnotatedElement" since="26" />
 		<method name="getTypeParameters()[Ljava/lang/reflect/TypeVariable;" />
 	</class>
 	<class name="java/lang/reflect/GenericSignatureFormatError" since="1">
 		<extends name="java/lang/ClassFormatError" />
 		<method name="&lt;init>()V" />
+		<method name="&lt;init>(Ljava/lang/String;)V" since="26" />
 	</class>
 	<class name="java/lang/reflect/InvocationHandler" since="1">
 		<extends name="java/lang/Object" />
@@ -52458,6 +55829,7 @@
 	</class>
 	<class name="java/lang/reflect/Method" since="1">
 		<extends name="java/lang/reflect/AccessibleObject" />
+		<extends name="java/lang/reflect/Executable" since="26" />
 		<implements name="java/lang/reflect/GenericDeclaration" />
 		<implements name="java/lang/reflect/Member" />
 		<method name="&lt;init>()V" />
@@ -52495,6 +55867,7 @@
 		<method name="isTransient(I)Z" />
 		<method name="isVolatile(I)Z" />
 		<method name="methodModifiers()I" since="19" />
+		<method name="parameterModifiers()I" since="26" />
 		<method name="toString(I)Ljava/lang/String;" />
 		<field name="ABSTRACT" />
 		<field name="FINAL" />
@@ -52509,6 +55882,20 @@
 		<field name="TRANSIENT" />
 		<field name="VOLATILE" />
 	</class>
+	<class name="java/lang/reflect/Parameter" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/lang/reflect/AnnotatedElement" />
+		<method name="&lt;init>()V" />
+		<method name="getDeclaringExecutable()Ljava/lang/reflect/Executable;" />
+		<method name="getModifiers()I" />
+		<method name="getName()Ljava/lang/String;" />
+		<method name="getParameterizedType()Ljava/lang/reflect/Type;" />
+		<method name="getType()Ljava/lang/Class;" />
+		<method name="isImplicit()Z" />
+		<method name="isNamePresent()Z" />
+		<method name="isSynthetic()Z" />
+		<method name="isVarArgs()Z" />
+	</class>
 	<class name="java/lang/reflect/ParameterizedType" since="1">
 		<extends name="java/lang/Object" />
 		<implements name="java/lang/reflect/Type" />
@@ -53849,6 +57236,10 @@
 		<method name="wrap([S)Ljava/nio/ShortBuffer;" />
 		<method name="wrap([SII)Ljava/nio/ShortBuffer;" />
 	</class>
+	<class name="java/nio/channels/AcceptPendingException" since="26">
+		<extends name="java/lang/IllegalStateException" />
+		<method name="&lt;init>()V" />
+	</class>
 	<class name="java/nio/channels/AlreadyBoundException" since="24">
 		<extends name="java/lang/IllegalStateException" />
 		<method name="&lt;init>()V" />
@@ -53857,10 +57248,89 @@
 		<extends name="java/lang/IllegalStateException" />
 		<method name="&lt;init>()V" />
 	</class>
+	<class name="java/nio/channels/AsynchronousByteChannel" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/nio/channels/AsynchronousChannel" />
+		<method name="read(Ljava/nio/ByteBuffer;)Ljava/util/concurrent/Future;" />
+		<method name="read(Ljava/nio/ByteBuffer;Ljava/lang/Object;Ljava/nio/channels/CompletionHandler;)V" />
+		<method name="write(Ljava/nio/ByteBuffer;)Ljava/util/concurrent/Future;" />
+		<method name="write(Ljava/nio/ByteBuffer;Ljava/lang/Object;Ljava/nio/channels/CompletionHandler;)V" />
+	</class>
+	<class name="java/nio/channels/AsynchronousChannel" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/nio/channels/Channel" />
+	</class>
+	<class name="java/nio/channels/AsynchronousChannelGroup" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>(Ljava/nio/channels/spi/AsynchronousChannelProvider;)V" />
+		<method name="awaitTermination(JLjava/util/concurrent/TimeUnit;)Z" />
+		<method name="isShutdown()Z" />
+		<method name="isTerminated()Z" />
+		<method name="provider()Ljava/nio/channels/spi/AsynchronousChannelProvider;" />
+		<method name="shutdown()V" />
+		<method name="shutdownNow()V" />
+		<method name="withCachedThreadPool(Ljava/util/concurrent/ExecutorService;I)Ljava/nio/channels/AsynchronousChannelGroup;" />
+		<method name="withFixedThreadPool(ILjava/util/concurrent/ThreadFactory;)Ljava/nio/channels/AsynchronousChannelGroup;" />
+		<method name="withThreadPool(Ljava/util/concurrent/ExecutorService;)Ljava/nio/channels/AsynchronousChannelGroup;" />
+	</class>
 	<class name="java/nio/channels/AsynchronousCloseException" since="1">
 		<extends name="java/nio/channels/ClosedChannelException" />
 		<method name="&lt;init>()V" />
 	</class>
+	<class name="java/nio/channels/AsynchronousFileChannel" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/nio/channels/AsynchronousChannel" />
+		<method name="&lt;init>()V" />
+		<method name="force(Z)V" />
+		<method name="lock()Ljava/util/concurrent/Future;" />
+		<method name="lock(JJZ)Ljava/util/concurrent/Future;" />
+		<method name="lock(JJZLjava/lang/Object;Ljava/nio/channels/CompletionHandler;)V" />
+		<method name="lock(Ljava/lang/Object;Ljava/nio/channels/CompletionHandler;)V" />
+		<method name="open(Ljava/nio/file/Path;Ljava/util/Set;Ljava/util/concurrent/ExecutorService;[Ljava/nio/file/attribute/FileAttribute;)Ljava/nio/channels/AsynchronousFileChannel;" />
+		<method name="open(Ljava/nio/file/Path;[Ljava/nio/file/OpenOption;)Ljava/nio/channels/AsynchronousFileChannel;" />
+		<method name="read(Ljava/nio/ByteBuffer;J)Ljava/util/concurrent/Future;" />
+		<method name="read(Ljava/nio/ByteBuffer;JLjava/lang/Object;Ljava/nio/channels/CompletionHandler;)V" />
+		<method name="size()J" />
+		<method name="truncate(J)Ljava/nio/channels/AsynchronousFileChannel;" />
+		<method name="tryLock()Ljava/nio/channels/FileLock;" />
+		<method name="tryLock(JJZ)Ljava/nio/channels/FileLock;" />
+		<method name="write(Ljava/nio/ByteBuffer;J)Ljava/util/concurrent/Future;" />
+		<method name="write(Ljava/nio/ByteBuffer;JLjava/lang/Object;Ljava/nio/channels/CompletionHandler;)V" />
+	</class>
+	<class name="java/nio/channels/AsynchronousServerSocketChannel" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/nio/channels/AsynchronousChannel" />
+		<implements name="java/nio/channels/NetworkChannel" />
+		<method name="&lt;init>(Ljava/nio/channels/spi/AsynchronousChannelProvider;)V" />
+		<method name="accept()Ljava/util/concurrent/Future;" />
+		<method name="accept(Ljava/lang/Object;Ljava/nio/channels/CompletionHandler;)V" />
+		<method name="bind(Ljava/net/SocketAddress;)Ljava/nio/channels/AsynchronousServerSocketChannel;" />
+		<method name="bind(Ljava/net/SocketAddress;I)Ljava/nio/channels/AsynchronousServerSocketChannel;" />
+		<method name="open()Ljava/nio/channels/AsynchronousServerSocketChannel;" />
+		<method name="open(Ljava/nio/channels/AsynchronousChannelGroup;)Ljava/nio/channels/AsynchronousServerSocketChannel;" />
+		<method name="provider()Ljava/nio/channels/spi/AsynchronousChannelProvider;" />
+		<method name="setOption(Ljava/net/SocketOption;Ljava/lang/Object;)Ljava/nio/channels/AsynchronousServerSocketChannel;" />
+	</class>
+	<class name="java/nio/channels/AsynchronousSocketChannel" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/nio/channels/AsynchronousByteChannel" />
+		<implements name="java/nio/channels/NetworkChannel" />
+		<method name="&lt;init>(Ljava/nio/channels/spi/AsynchronousChannelProvider;)V" />
+		<method name="bind(Ljava/net/SocketAddress;)Ljava/nio/channels/AsynchronousSocketChannel;" />
+		<method name="connect(Ljava/net/SocketAddress;)Ljava/util/concurrent/Future;" />
+		<method name="connect(Ljava/net/SocketAddress;Ljava/lang/Object;Ljava/nio/channels/CompletionHandler;)V" />
+		<method name="getRemoteAddress()Ljava/net/SocketAddress;" />
+		<method name="open()Ljava/nio/channels/AsynchronousSocketChannel;" />
+		<method name="open(Ljava/nio/channels/AsynchronousChannelGroup;)Ljava/nio/channels/AsynchronousSocketChannel;" />
+		<method name="provider()Ljava/nio/channels/spi/AsynchronousChannelProvider;" />
+		<method name="read(Ljava/nio/ByteBuffer;JLjava/util/concurrent/TimeUnit;Ljava/lang/Object;Ljava/nio/channels/CompletionHandler;)V" />
+		<method name="read([Ljava/nio/ByteBuffer;IIJLjava/util/concurrent/TimeUnit;Ljava/lang/Object;Ljava/nio/channels/CompletionHandler;)V" />
+		<method name="setOption(Ljava/net/SocketOption;Ljava/lang/Object;)Ljava/nio/channels/AsynchronousSocketChannel;" />
+		<method name="shutdownInput()Ljava/nio/channels/AsynchronousSocketChannel;" />
+		<method name="shutdownOutput()Ljava/nio/channels/AsynchronousSocketChannel;" />
+		<method name="write(Ljava/nio/ByteBuffer;JLjava/util/concurrent/TimeUnit;Ljava/lang/Object;Ljava/nio/channels/CompletionHandler;)V" />
+		<method name="write([Ljava/nio/ByteBuffer;IIJLjava/util/concurrent/TimeUnit;Ljava/lang/Object;Ljava/nio/channels/CompletionHandler;)V" />
+	</class>
 	<class name="java/nio/channels/ByteChannel" since="1">
 		<extends name="java/lang/Object" />
 		<implements name="java/nio/channels/ReadableByteChannel" />
@@ -53880,7 +57350,9 @@
 		<method name="&lt;init>()V" />
 		<method name="newChannel(Ljava/io/InputStream;)Ljava/nio/channels/ReadableByteChannel;" />
 		<method name="newChannel(Ljava/io/OutputStream;)Ljava/nio/channels/WritableByteChannel;" />
+		<method name="newInputStream(Ljava/nio/channels/AsynchronousByteChannel;)Ljava/io/InputStream;" since="26" />
 		<method name="newInputStream(Ljava/nio/channels/ReadableByteChannel;)Ljava/io/InputStream;" />
+		<method name="newOutputStream(Ljava/nio/channels/AsynchronousByteChannel;)Ljava/io/OutputStream;" since="26" />
 		<method name="newOutputStream(Ljava/nio/channels/WritableByteChannel;)Ljava/io/OutputStream;" />
 		<method name="newReader(Ljava/nio/channels/ReadableByteChannel;Ljava/lang/String;)Ljava/io/Reader;" />
 		<method name="newReader(Ljava/nio/channels/ReadableByteChannel;Ljava/nio/charset/CharsetDecoder;I)Ljava/io/Reader;" />
@@ -53899,6 +57371,11 @@
 		<extends name="java/lang/IllegalStateException" />
 		<method name="&lt;init>()V" />
 	</class>
+	<class name="java/nio/channels/CompletionHandler" since="26">
+		<extends name="java/lang/Object" />
+		<method name="completed(Ljava/lang/Object;Ljava/lang/Object;)V" />
+		<method name="failed(Ljava/lang/Throwable;Ljava/lang/Object;)V" />
+	</class>
 	<class name="java/nio/channels/ConnectionPendingException" since="1">
 		<extends name="java/lang/IllegalStateException" />
 		<method name="&lt;init>()V" />
@@ -53907,6 +57384,7 @@
 		<extends name="java/nio/channels/spi/AbstractSelectableChannel" />
 		<implements name="java/nio/channels/ByteChannel" />
 		<implements name="java/nio/channels/GatheringByteChannel" />
+		<implements name="java/nio/channels/MulticastChannel" since="26" />
 		<implements name="java/nio/channels/NetworkChannel" since="24" />
 		<implements name="java/nio/channels/ScatteringByteChannel" />
 		<method name="&lt;init>(Ljava/nio/channels/spi/SelectorProvider;)V" />
@@ -53933,6 +57411,8 @@
 		<method name="lock()Ljava/nio/channels/FileLock;" />
 		<method name="lock(JJZ)Ljava/nio/channels/FileLock;" />
 		<method name="map(Ljava/nio/channels/FileChannel$MapMode;JJ)Ljava/nio/MappedByteBuffer;" />
+		<method name="open(Ljava/nio/file/Path;Ljava/util/Set;[Ljava/nio/file/attribute/FileAttribute;)Ljava/nio/channels/FileChannel;" since="26" />
+		<method name="open(Ljava/nio/file/Path;[Ljava/nio/file/OpenOption;)Ljava/nio/channels/FileChannel;" since="26" />
 		<method name="position()J" />
 		<method name="position(J)Ljava/nio/channels/FileChannel;" />
 		<method name="read(Ljava/nio/ByteBuffer;J)I" />
@@ -53954,6 +57434,7 @@
 	<class name="java/nio/channels/FileLock" since="1">
 		<extends name="java/lang/Object" />
 		<implements name="java/lang/AutoCloseable" since="19" />
+		<method name="&lt;init>(Ljava/nio/channels/AsynchronousFileChannel;JJZ)V" since="26" />
 		<method name="&lt;init>(Ljava/nio/channels/FileChannel;JJZ)V" />
 		<method name="acquiredBy()Ljava/nio/channels/Channel;" since="24" />
 		<method name="channel()Ljava/nio/channels/FileChannel;" />
@@ -53978,14 +57459,40 @@
 		<extends name="java/lang/IllegalStateException" />
 		<method name="&lt;init>()V" />
 	</class>
+	<class name="java/nio/channels/IllegalChannelGroupException" since="26">
+		<extends name="java/lang/IllegalArgumentException" />
+		<method name="&lt;init>()V" />
+	</class>
 	<class name="java/nio/channels/IllegalSelectorException" since="1">
 		<extends name="java/lang/IllegalArgumentException" />
 		<method name="&lt;init>()V" />
 	</class>
+	<class name="java/nio/channels/InterruptedByTimeoutException" since="26">
+		<extends name="java/io/IOException" />
+		<method name="&lt;init>()V" />
+	</class>
 	<class name="java/nio/channels/InterruptibleChannel" since="1">
 		<extends name="java/lang/Object" />
 		<implements name="java/nio/channels/Channel" />
 	</class>
+	<class name="java/nio/channels/MembershipKey" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="block(Ljava/net/InetAddress;)Ljava/nio/channels/MembershipKey;" />
+		<method name="channel()Ljava/nio/channels/MulticastChannel;" />
+		<method name="drop()V" />
+		<method name="group()Ljava/net/InetAddress;" />
+		<method name="isValid()Z" />
+		<method name="networkInterface()Ljava/net/NetworkInterface;" />
+		<method name="sourceAddress()Ljava/net/InetAddress;" />
+		<method name="unblock(Ljava/net/InetAddress;)Ljava/nio/channels/MembershipKey;" />
+	</class>
+	<class name="java/nio/channels/MulticastChannel" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/nio/channels/NetworkChannel" />
+		<method name="join(Ljava/net/InetAddress;Ljava/net/NetworkInterface;)Ljava/nio/channels/MembershipKey;" />
+		<method name="join(Ljava/net/InetAddress;Ljava/net/NetworkInterface;Ljava/net/InetAddress;)Ljava/nio/channels/MembershipKey;" />
+	</class>
 	<class name="java/nio/channels/NetworkChannel" since="24">
 		<extends name="java/lang/Object" />
 		<implements name="java/nio/channels/Channel" />
@@ -54038,6 +57545,10 @@
 		<implements name="java/nio/channels/ScatteringByteChannel" />
 		<method name="&lt;init>(Ljava/nio/channels/spi/SelectorProvider;)V" />
 	</class>
+	<class name="java/nio/channels/ReadPendingException" since="26">
+		<extends name="java/lang/IllegalStateException" />
+		<method name="&lt;init>()V" />
+	</class>
 	<class name="java/nio/channels/ReadableByteChannel" since="1">
 		<extends name="java/lang/Object" />
 		<implements name="java/nio/channels/Channel" />
@@ -54117,6 +57628,10 @@
 		<method name="setOption(Ljava/net/SocketOption;Ljava/lang/Object;)Ljava/nio/channels/ServerSocketChannel;" since="24" />
 		<method name="socket()Ljava/net/ServerSocket;" />
 	</class>
+	<class name="java/nio/channels/ShutdownChannelGroupException" since="26">
+		<extends name="java/lang/IllegalStateException" />
+		<method name="&lt;init>()V" />
+	</class>
 	<class name="java/nio/channels/SocketChannel" since="1">
 		<extends name="java/nio/channels/spi/AbstractSelectableChannel" />
 		<implements name="java/nio/channels/ByteChannel" />
@@ -54150,6 +57665,10 @@
 		<implements name="java/nio/channels/Channel" />
 		<method name="write(Ljava/nio/ByteBuffer;)I" />
 	</class>
+	<class name="java/nio/channels/WritePendingException" since="26">
+		<extends name="java/lang/IllegalStateException" />
+		<method name="&lt;init>()V" />
+	</class>
 	<class name="java/nio/channels/spi/AbstractInterruptibleChannel" since="1">
 		<extends name="java/lang/Object" />
 		<implements name="java/nio/channels/Channel" />
@@ -54179,6 +57698,15 @@
 		<method name="implCloseSelector()V" />
 		<method name="register(Ljava/nio/channels/spi/AbstractSelectableChannel;ILjava/lang/Object;)Ljava/nio/channels/SelectionKey;" />
 	</class>
+	<class name="java/nio/channels/spi/AsynchronousChannelProvider" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="openAsynchronousChannelGroup(ILjava/util/concurrent/ThreadFactory;)Ljava/nio/channels/AsynchronousChannelGroup;" />
+		<method name="openAsynchronousChannelGroup(Ljava/util/concurrent/ExecutorService;I)Ljava/nio/channels/AsynchronousChannelGroup;" />
+		<method name="openAsynchronousServerSocketChannel(Ljava/nio/channels/AsynchronousChannelGroup;)Ljava/nio/channels/AsynchronousServerSocketChannel;" />
+		<method name="openAsynchronousSocketChannel(Ljava/nio/channels/AsynchronousChannelGroup;)Ljava/nio/channels/AsynchronousSocketChannel;" />
+		<method name="provider()Ljava/nio/channels/spi/AsynchronousChannelProvider;" />
+	</class>
 	<class name="java/nio/channels/spi/SelectorProvider" since="1">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
@@ -54332,6 +57860,626 @@
 		<method name="charsetForName(Ljava/lang/String;)Ljava/nio/charset/Charset;" />
 		<method name="charsets()Ljava/util/Iterator;" />
 	</class>
+	<class name="java/nio/file/AccessDeniedException" since="26">
+		<extends name="java/nio/file/FileSystemException" />
+		<method name="&lt;init>(Ljava/lang/String;)V" />
+		<method name="&lt;init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V" />
+	</class>
+	<class name="java/nio/file/AccessMode" since="26">
+		<extends name="java/lang/Enum" />
+		<method name="valueOf(Ljava/lang/String;)Ljava/nio/file/AccessMode;" />
+		<method name="values()[Ljava/nio/file/AccessMode;" />
+		<field name="EXECUTE" />
+		<field name="READ" />
+		<field name="WRITE" />
+	</class>
+	<class name="java/nio/file/AtomicMoveNotSupportedException" since="26">
+		<extends name="java/nio/file/FileSystemException" />
+		<method name="&lt;init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V" />
+	</class>
+	<class name="java/nio/file/ClosedDirectoryStreamException" since="26">
+		<extends name="java/lang/IllegalStateException" />
+		<method name="&lt;init>()V" />
+	</class>
+	<class name="java/nio/file/ClosedFileSystemException" since="26">
+		<extends name="java/lang/IllegalStateException" />
+		<method name="&lt;init>()V" />
+	</class>
+	<class name="java/nio/file/ClosedWatchServiceException" since="26">
+		<extends name="java/lang/IllegalStateException" />
+		<method name="&lt;init>()V" />
+	</class>
+	<class name="java/nio/file/CopyOption" since="26">
+		<extends name="java/lang/Object" />
+	</class>
+	<class name="java/nio/file/DirectoryIteratorException" since="26">
+		<extends name="java/util/ConcurrentModificationException" />
+		<method name="&lt;init>(Ljava/io/IOException;)V" />
+		<method name="getCause()Ljava/io/IOException;" />
+	</class>
+	<class name="java/nio/file/DirectoryNotEmptyException" since="26">
+		<extends name="java/nio/file/FileSystemException" />
+		<method name="&lt;init>(Ljava/lang/String;)V" />
+	</class>
+	<class name="java/nio/file/DirectoryStream" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/io/Closeable" />
+		<implements name="java/lang/Iterable" />
+	</class>
+	<class name="java/nio/file/DirectoryStream$Filter" since="26">
+		<extends name="java/lang/Object" />
+		<method name="accept(Ljava/lang/Object;)Z" />
+	</class>
+	<class name="java/nio/file/FileAlreadyExistsException" since="26">
+		<extends name="java/nio/file/FileSystemException" />
+		<method name="&lt;init>(Ljava/lang/String;)V" />
+		<method name="&lt;init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V" />
+	</class>
+	<class name="java/nio/file/FileStore" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="getAttribute(Ljava/lang/String;)Ljava/lang/Object;" />
+		<method name="getFileStoreAttributeView(Ljava/lang/Class;)Ljava/nio/file/attribute/FileStoreAttributeView;" />
+		<method name="getTotalSpace()J" />
+		<method name="getUnallocatedSpace()J" />
+		<method name="getUsableSpace()J" />
+		<method name="isReadOnly()Z" />
+		<method name="name()Ljava/lang/String;" />
+		<method name="supportsFileAttributeView(Ljava/lang/Class;)Z" />
+		<method name="supportsFileAttributeView(Ljava/lang/String;)Z" />
+		<method name="type()Ljava/lang/String;" />
+	</class>
+	<class name="java/nio/file/FileSystem" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/io/Closeable" />
+		<method name="&lt;init>()V" />
+		<method name="getFileStores()Ljava/lang/Iterable;" />
+		<method name="getPath(Ljava/lang/String;[Ljava/lang/String;)Ljava/nio/file/Path;" />
+		<method name="getPathMatcher(Ljava/lang/String;)Ljava/nio/file/PathMatcher;" />
+		<method name="getRootDirectories()Ljava/lang/Iterable;" />
+		<method name="getSeparator()Ljava/lang/String;" />
+		<method name="getUserPrincipalLookupService()Ljava/nio/file/attribute/UserPrincipalLookupService;" />
+		<method name="isOpen()Z" />
+		<method name="isReadOnly()Z" />
+		<method name="newWatchService()Ljava/nio/file/WatchService;" />
+		<method name="provider()Ljava/nio/file/spi/FileSystemProvider;" />
+		<method name="supportedFileAttributeViews()Ljava/util/Set;" />
+	</class>
+	<class name="java/nio/file/FileSystemAlreadyExistsException" since="26">
+		<extends name="java/lang/RuntimeException" />
+		<method name="&lt;init>()V" />
+		<method name="&lt;init>(Ljava/lang/String;)V" />
+	</class>
+	<class name="java/nio/file/FileSystemException" since="26">
+		<extends name="java/io/IOException" />
+		<method name="&lt;init>(Ljava/lang/String;)V" />
+		<method name="&lt;init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V" />
+		<method name="getFile()Ljava/lang/String;" />
+		<method name="getOtherFile()Ljava/lang/String;" />
+		<method name="getReason()Ljava/lang/String;" />
+	</class>
+	<class name="java/nio/file/FileSystemLoopException" since="26">
+		<extends name="java/nio/file/FileSystemException" />
+		<method name="&lt;init>(Ljava/lang/String;)V" />
+	</class>
+	<class name="java/nio/file/FileSystemNotFoundException" since="26">
+		<extends name="java/lang/RuntimeException" />
+		<method name="&lt;init>()V" />
+		<method name="&lt;init>(Ljava/lang/String;)V" />
+	</class>
+	<class name="java/nio/file/FileSystems" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="getDefault()Ljava/nio/file/FileSystem;" />
+		<method name="getFileSystem(Ljava/net/URI;)Ljava/nio/file/FileSystem;" />
+		<method name="newFileSystem(Ljava/net/URI;Ljava/util/Map;)Ljava/nio/file/FileSystem;" />
+		<method name="newFileSystem(Ljava/net/URI;Ljava/util/Map;Ljava/lang/ClassLoader;)Ljava/nio/file/FileSystem;" />
+		<method name="newFileSystem(Ljava/nio/file/Path;Ljava/lang/ClassLoader;)Ljava/nio/file/FileSystem;" />
+	</class>
+	<class name="java/nio/file/FileVisitOption" since="26">
+		<extends name="java/lang/Enum" />
+		<method name="valueOf(Ljava/lang/String;)Ljava/nio/file/FileVisitOption;" />
+		<method name="values()[Ljava/nio/file/FileVisitOption;" />
+		<field name="FOLLOW_LINKS" />
+	</class>
+	<class name="java/nio/file/FileVisitResult" since="26">
+		<extends name="java/lang/Enum" />
+		<method name="valueOf(Ljava/lang/String;)Ljava/nio/file/FileVisitResult;" />
+		<method name="values()[Ljava/nio/file/FileVisitResult;" />
+		<field name="CONTINUE" />
+		<field name="SKIP_SIBLINGS" />
+		<field name="SKIP_SUBTREE" />
+		<field name="TERMINATE" />
+	</class>
+	<class name="java/nio/file/FileVisitor" since="26">
+		<extends name="java/lang/Object" />
+		<method name="postVisitDirectory(Ljava/lang/Object;Ljava/io/IOException;)Ljava/nio/file/FileVisitResult;" />
+		<method name="preVisitDirectory(Ljava/lang/Object;Ljava/nio/file/attribute/BasicFileAttributes;)Ljava/nio/file/FileVisitResult;" />
+		<method name="visitFile(Ljava/lang/Object;Ljava/nio/file/attribute/BasicFileAttributes;)Ljava/nio/file/FileVisitResult;" />
+		<method name="visitFileFailed(Ljava/lang/Object;Ljava/io/IOException;)Ljava/nio/file/FileVisitResult;" />
+	</class>
+	<class name="java/nio/file/Files" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="copy(Ljava/io/InputStream;Ljava/nio/file/Path;[Ljava/nio/file/CopyOption;)J" />
+		<method name="copy(Ljava/nio/file/Path;Ljava/io/OutputStream;)J" />
+		<method name="copy(Ljava/nio/file/Path;Ljava/nio/file/Path;[Ljava/nio/file/CopyOption;)Ljava/nio/file/Path;" />
+		<method name="createDirectories(Ljava/nio/file/Path;[Ljava/nio/file/attribute/FileAttribute;)Ljava/nio/file/Path;" />
+		<method name="createDirectory(Ljava/nio/file/Path;[Ljava/nio/file/attribute/FileAttribute;)Ljava/nio/file/Path;" />
+		<method name="createFile(Ljava/nio/file/Path;[Ljava/nio/file/attribute/FileAttribute;)Ljava/nio/file/Path;" />
+		<method name="createLink(Ljava/nio/file/Path;Ljava/nio/file/Path;)Ljava/nio/file/Path;" />
+		<method name="createSymbolicLink(Ljava/nio/file/Path;Ljava/nio/file/Path;[Ljava/nio/file/attribute/FileAttribute;)Ljava/nio/file/Path;" />
+		<method name="createTempDirectory(Ljava/lang/String;[Ljava/nio/file/attribute/FileAttribute;)Ljava/nio/file/Path;" />
+		<method name="createTempDirectory(Ljava/nio/file/Path;Ljava/lang/String;[Ljava/nio/file/attribute/FileAttribute;)Ljava/nio/file/Path;" />
+		<method name="createTempFile(Ljava/lang/String;Ljava/lang/String;[Ljava/nio/file/attribute/FileAttribute;)Ljava/nio/file/Path;" />
+		<method name="createTempFile(Ljava/nio/file/Path;Ljava/lang/String;Ljava/lang/String;[Ljava/nio/file/attribute/FileAttribute;)Ljava/nio/file/Path;" />
+		<method name="delete(Ljava/nio/file/Path;)V" />
+		<method name="deleteIfExists(Ljava/nio/file/Path;)Z" />
+		<method name="exists(Ljava/nio/file/Path;[Ljava/nio/file/LinkOption;)Z" />
+		<method name="find(Ljava/nio/file/Path;ILjava/util/function/BiPredicate;[Ljava/nio/file/FileVisitOption;)Ljava/util/stream/Stream;" />
+		<method name="getAttribute(Ljava/nio/file/Path;Ljava/lang/String;[Ljava/nio/file/LinkOption;)Ljava/lang/Object;" />
+		<method name="getFileAttributeView(Ljava/nio/file/Path;Ljava/lang/Class;[Ljava/nio/file/LinkOption;)Ljava/nio/file/attribute/FileAttributeView;" />
+		<method name="getFileStore(Ljava/nio/file/Path;)Ljava/nio/file/FileStore;" />
+		<method name="getLastModifiedTime(Ljava/nio/file/Path;[Ljava/nio/file/LinkOption;)Ljava/nio/file/attribute/FileTime;" />
+		<method name="getOwner(Ljava/nio/file/Path;[Ljava/nio/file/LinkOption;)Ljava/nio/file/attribute/UserPrincipal;" />
+		<method name="getPosixFilePermissions(Ljava/nio/file/Path;[Ljava/nio/file/LinkOption;)Ljava/util/Set;" />
+		<method name="isDirectory(Ljava/nio/file/Path;[Ljava/nio/file/LinkOption;)Z" />
+		<method name="isExecutable(Ljava/nio/file/Path;)Z" />
+		<method name="isHidden(Ljava/nio/file/Path;)Z" />
+		<method name="isReadable(Ljava/nio/file/Path;)Z" />
+		<method name="isRegularFile(Ljava/nio/file/Path;[Ljava/nio/file/LinkOption;)Z" />
+		<method name="isSameFile(Ljava/nio/file/Path;Ljava/nio/file/Path;)Z" />
+		<method name="isSymbolicLink(Ljava/nio/file/Path;)Z" />
+		<method name="isWritable(Ljava/nio/file/Path;)Z" />
+		<method name="lines(Ljava/nio/file/Path;)Ljava/util/stream/Stream;" />
+		<method name="lines(Ljava/nio/file/Path;Ljava/nio/charset/Charset;)Ljava/util/stream/Stream;" />
+		<method name="list(Ljava/nio/file/Path;)Ljava/util/stream/Stream;" />
+		<method name="move(Ljava/nio/file/Path;Ljava/nio/file/Path;[Ljava/nio/file/CopyOption;)Ljava/nio/file/Path;" />
+		<method name="newBufferedReader(Ljava/nio/file/Path;)Ljava/io/BufferedReader;" />
+		<method name="newBufferedReader(Ljava/nio/file/Path;Ljava/nio/charset/Charset;)Ljava/io/BufferedReader;" />
+		<method name="newBufferedWriter(Ljava/nio/file/Path;Ljava/nio/charset/Charset;[Ljava/nio/file/OpenOption;)Ljava/io/BufferedWriter;" />
+		<method name="newBufferedWriter(Ljava/nio/file/Path;[Ljava/nio/file/OpenOption;)Ljava/io/BufferedWriter;" />
+		<method name="newByteChannel(Ljava/nio/file/Path;Ljava/util/Set;[Ljava/nio/file/attribute/FileAttribute;)Ljava/nio/channels/SeekableByteChannel;" />
+		<method name="newByteChannel(Ljava/nio/file/Path;[Ljava/nio/file/OpenOption;)Ljava/nio/channels/SeekableByteChannel;" />
+		<method name="newDirectoryStream(Ljava/nio/file/Path;)Ljava/nio/file/DirectoryStream;" />
+		<method name="newDirectoryStream(Ljava/nio/file/Path;Ljava/lang/String;)Ljava/nio/file/DirectoryStream;" />
+		<method name="newDirectoryStream(Ljava/nio/file/Path;Ljava/nio/file/DirectoryStream$Filter;)Ljava/nio/file/DirectoryStream;" />
+		<method name="newInputStream(Ljava/nio/file/Path;[Ljava/nio/file/OpenOption;)Ljava/io/InputStream;" />
+		<method name="newOutputStream(Ljava/nio/file/Path;[Ljava/nio/file/OpenOption;)Ljava/io/OutputStream;" />
+		<method name="notExists(Ljava/nio/file/Path;[Ljava/nio/file/LinkOption;)Z" />
+		<method name="probeContentType(Ljava/nio/file/Path;)Ljava/lang/String;" />
+		<method name="readAllBytes(Ljava/nio/file/Path;)[B" />
+		<method name="readAllLines(Ljava/nio/file/Path;)Ljava/util/List;" />
+		<method name="readAllLines(Ljava/nio/file/Path;Ljava/nio/charset/Charset;)Ljava/util/List;" />
+		<method name="readAttributes(Ljava/nio/file/Path;Ljava/lang/Class;[Ljava/nio/file/LinkOption;)Ljava/nio/file/attribute/BasicFileAttributes;" />
+		<method name="readAttributes(Ljava/nio/file/Path;Ljava/lang/String;[Ljava/nio/file/LinkOption;)Ljava/util/Map;" />
+		<method name="readSymbolicLink(Ljava/nio/file/Path;)Ljava/nio/file/Path;" />
+		<method name="setAttribute(Ljava/nio/file/Path;Ljava/lang/String;Ljava/lang/Object;[Ljava/nio/file/LinkOption;)Ljava/nio/file/Path;" />
+		<method name="setLastModifiedTime(Ljava/nio/file/Path;Ljava/nio/file/attribute/FileTime;)Ljava/nio/file/Path;" />
+		<method name="setOwner(Ljava/nio/file/Path;Ljava/nio/file/attribute/UserPrincipal;)Ljava/nio/file/Path;" />
+		<method name="setPosixFilePermissions(Ljava/nio/file/Path;Ljava/util/Set;)Ljava/nio/file/Path;" />
+		<method name="size(Ljava/nio/file/Path;)J" />
+		<method name="walk(Ljava/nio/file/Path;I[Ljava/nio/file/FileVisitOption;)Ljava/util/stream/Stream;" />
+		<method name="walk(Ljava/nio/file/Path;[Ljava/nio/file/FileVisitOption;)Ljava/util/stream/Stream;" />
+		<method name="walkFileTree(Ljava/nio/file/Path;Ljava/nio/file/FileVisitor;)Ljava/nio/file/Path;" />
+		<method name="walkFileTree(Ljava/nio/file/Path;Ljava/util/Set;ILjava/nio/file/FileVisitor;)Ljava/nio/file/Path;" />
+		<method name="write(Ljava/nio/file/Path;Ljava/lang/Iterable;Ljava/nio/charset/Charset;[Ljava/nio/file/OpenOption;)Ljava/nio/file/Path;" />
+		<method name="write(Ljava/nio/file/Path;Ljava/lang/Iterable;[Ljava/nio/file/OpenOption;)Ljava/nio/file/Path;" />
+		<method name="write(Ljava/nio/file/Path;[B[Ljava/nio/file/OpenOption;)Ljava/nio/file/Path;" />
+	</class>
+	<class name="java/nio/file/InvalidPathException" since="26">
+		<extends name="java/lang/IllegalArgumentException" />
+		<method name="&lt;init>(Ljava/lang/String;Ljava/lang/String;)V" />
+		<method name="&lt;init>(Ljava/lang/String;Ljava/lang/String;I)V" />
+		<method name="getIndex()I" />
+		<method name="getInput()Ljava/lang/String;" />
+		<method name="getReason()Ljava/lang/String;" />
+	</class>
+	<class name="java/nio/file/LinkOption" since="26">
+		<extends name="java/lang/Enum" />
+		<implements name="java/nio/file/CopyOption" />
+		<implements name="java/nio/file/OpenOption" />
+		<method name="valueOf(Ljava/lang/String;)Ljava/nio/file/LinkOption;" />
+		<method name="values()[Ljava/nio/file/LinkOption;" />
+		<field name="NOFOLLOW_LINKS" />
+	</class>
+	<class name="java/nio/file/LinkPermission" since="26">
+		<extends name="java/security/BasicPermission" />
+		<method name="&lt;init>(Ljava/lang/String;)V" />
+		<method name="&lt;init>(Ljava/lang/String;Ljava/lang/String;)V" />
+	</class>
+	<class name="java/nio/file/NoSuchFileException" since="26">
+		<extends name="java/nio/file/FileSystemException" />
+		<method name="&lt;init>(Ljava/lang/String;)V" />
+		<method name="&lt;init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V" />
+	</class>
+	<class name="java/nio/file/NotDirectoryException" since="26">
+		<extends name="java/nio/file/FileSystemException" />
+		<method name="&lt;init>(Ljava/lang/String;)V" />
+	</class>
+	<class name="java/nio/file/NotLinkException" since="26">
+		<extends name="java/nio/file/FileSystemException" />
+		<method name="&lt;init>(Ljava/lang/String;)V" />
+		<method name="&lt;init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V" />
+	</class>
+	<class name="java/nio/file/OpenOption" since="26">
+		<extends name="java/lang/Object" />
+	</class>
+	<class name="java/nio/file/Path" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/lang/Comparable" />
+		<implements name="java/lang/Iterable" />
+		<implements name="java/nio/file/Watchable" />
+		<method name="compareTo(Ljava/nio/file/Path;)I" />
+		<method name="endsWith(Ljava/lang/String;)Z" />
+		<method name="endsWith(Ljava/nio/file/Path;)Z" />
+		<method name="getFileName()Ljava/nio/file/Path;" />
+		<method name="getFileSystem()Ljava/nio/file/FileSystem;" />
+		<method name="getName(I)Ljava/nio/file/Path;" />
+		<method name="getNameCount()I" />
+		<method name="getParent()Ljava/nio/file/Path;" />
+		<method name="getRoot()Ljava/nio/file/Path;" />
+		<method name="isAbsolute()Z" />
+		<method name="normalize()Ljava/nio/file/Path;" />
+		<method name="relativize(Ljava/nio/file/Path;)Ljava/nio/file/Path;" />
+		<method name="resolve(Ljava/lang/String;)Ljava/nio/file/Path;" />
+		<method name="resolve(Ljava/nio/file/Path;)Ljava/nio/file/Path;" />
+		<method name="resolveSibling(Ljava/lang/String;)Ljava/nio/file/Path;" />
+		<method name="resolveSibling(Ljava/nio/file/Path;)Ljava/nio/file/Path;" />
+		<method name="startsWith(Ljava/lang/String;)Z" />
+		<method name="startsWith(Ljava/nio/file/Path;)Z" />
+		<method name="subpath(II)Ljava/nio/file/Path;" />
+		<method name="toAbsolutePath()Ljava/nio/file/Path;" />
+		<method name="toFile()Ljava/io/File;" />
+		<method name="toRealPath([Ljava/nio/file/LinkOption;)Ljava/nio/file/Path;" />
+		<method name="toUri()Ljava/net/URI;" />
+	</class>
+	<class name="java/nio/file/PathMatcher" since="26">
+		<extends name="java/lang/Object" />
+		<method name="matches(Ljava/nio/file/Path;)Z" />
+	</class>
+	<class name="java/nio/file/Paths" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="get(Ljava/lang/String;[Ljava/lang/String;)Ljava/nio/file/Path;" />
+		<method name="get(Ljava/net/URI;)Ljava/nio/file/Path;" />
+	</class>
+	<class name="java/nio/file/ProviderMismatchException" since="26">
+		<extends name="java/lang/IllegalArgumentException" />
+		<method name="&lt;init>()V" />
+		<method name="&lt;init>(Ljava/lang/String;)V" />
+	</class>
+	<class name="java/nio/file/ProviderNotFoundException" since="26">
+		<extends name="java/lang/RuntimeException" />
+		<method name="&lt;init>()V" />
+		<method name="&lt;init>(Ljava/lang/String;)V" />
+	</class>
+	<class name="java/nio/file/ReadOnlyFileSystemException" since="26">
+		<extends name="java/lang/UnsupportedOperationException" />
+		<method name="&lt;init>()V" />
+	</class>
+	<class name="java/nio/file/SecureDirectoryStream" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/nio/file/DirectoryStream" />
+		<method name="deleteDirectory(Ljava/lang/Object;)V" />
+		<method name="deleteFile(Ljava/lang/Object;)V" />
+		<method name="getFileAttributeView(Ljava/lang/Class;)Ljava/nio/file/attribute/FileAttributeView;" />
+		<method name="getFileAttributeView(Ljava/lang/Object;Ljava/lang/Class;[Ljava/nio/file/LinkOption;)Ljava/nio/file/attribute/FileAttributeView;" />
+		<method name="move(Ljava/lang/Object;Ljava/nio/file/SecureDirectoryStream;Ljava/lang/Object;)V" />
+		<method name="newByteChannel(Ljava/lang/Object;Ljava/util/Set;[Ljava/nio/file/attribute/FileAttribute;)Ljava/nio/channels/SeekableByteChannel;" />
+		<method name="newDirectoryStream(Ljava/lang/Object;[Ljava/nio/file/LinkOption;)Ljava/nio/file/SecureDirectoryStream;" />
+	</class>
+	<class name="java/nio/file/SimpleFileVisitor" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/nio/file/FileVisitor" />
+		<method name="&lt;init>()V" />
+	</class>
+	<class name="java/nio/file/StandardCopyOption" since="26">
+		<extends name="java/lang/Enum" />
+		<implements name="java/nio/file/CopyOption" />
+		<method name="valueOf(Ljava/lang/String;)Ljava/nio/file/StandardCopyOption;" />
+		<method name="values()[Ljava/nio/file/StandardCopyOption;" />
+		<field name="ATOMIC_MOVE" />
+		<field name="COPY_ATTRIBUTES" />
+		<field name="REPLACE_EXISTING" />
+	</class>
+	<class name="java/nio/file/StandardOpenOption" since="26">
+		<extends name="java/lang/Enum" />
+		<implements name="java/nio/file/OpenOption" />
+		<method name="valueOf(Ljava/lang/String;)Ljava/nio/file/StandardOpenOption;" />
+		<method name="values()[Ljava/nio/file/StandardOpenOption;" />
+		<field name="APPEND" />
+		<field name="CREATE" />
+		<field name="CREATE_NEW" />
+		<field name="DELETE_ON_CLOSE" />
+		<field name="DSYNC" />
+		<field name="READ" />
+		<field name="SPARSE" />
+		<field name="SYNC" />
+		<field name="TRUNCATE_EXISTING" />
+		<field name="WRITE" />
+	</class>
+	<class name="java/nio/file/StandardWatchEventKinds" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<field name="ENTRY_CREATE" />
+		<field name="ENTRY_DELETE" />
+		<field name="ENTRY_MODIFY" />
+		<field name="OVERFLOW" />
+	</class>
+	<class name="java/nio/file/WatchEvent" since="26">
+		<extends name="java/lang/Object" />
+		<method name="context()Ljava/lang/Object;" />
+		<method name="count()I" />
+		<method name="kind()Ljava/nio/file/WatchEvent$Kind;" />
+	</class>
+	<class name="java/nio/file/WatchEvent$Kind" since="26">
+		<extends name="java/lang/Object" />
+		<method name="name()Ljava/lang/String;" />
+		<method name="type()Ljava/lang/Class;" />
+	</class>
+	<class name="java/nio/file/WatchEvent$Modifier" since="26">
+		<extends name="java/lang/Object" />
+		<method name="name()Ljava/lang/String;" />
+	</class>
+	<class name="java/nio/file/WatchKey" since="26">
+		<extends name="java/lang/Object" />
+		<method name="cancel()V" />
+		<method name="isValid()Z" />
+		<method name="pollEvents()Ljava/util/List;" />
+		<method name="reset()Z" />
+		<method name="watchable()Ljava/nio/file/Watchable;" />
+	</class>
+	<class name="java/nio/file/WatchService" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/io/Closeable" />
+		<method name="poll()Ljava/nio/file/WatchKey;" />
+		<method name="poll(JLjava/util/concurrent/TimeUnit;)Ljava/nio/file/WatchKey;" />
+		<method name="take()Ljava/nio/file/WatchKey;" />
+	</class>
+	<class name="java/nio/file/Watchable" since="26">
+		<extends name="java/lang/Object" />
+		<method name="register(Ljava/nio/file/WatchService;[Ljava/nio/file/WatchEvent$Kind;)Ljava/nio/file/WatchKey;" />
+		<method name="register(Ljava/nio/file/WatchService;[Ljava/nio/file/WatchEvent$Kind;[Ljava/nio/file/WatchEvent$Modifier;)Ljava/nio/file/WatchKey;" />
+	</class>
+	<class name="java/nio/file/attribute/AclEntry" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="flags()Ljava/util/Set;" />
+		<method name="newBuilder()Ljava/nio/file/attribute/AclEntry$Builder;" />
+		<method name="newBuilder(Ljava/nio/file/attribute/AclEntry;)Ljava/nio/file/attribute/AclEntry$Builder;" />
+		<method name="permissions()Ljava/util/Set;" />
+		<method name="principal()Ljava/nio/file/attribute/UserPrincipal;" />
+		<method name="type()Ljava/nio/file/attribute/AclEntryType;" />
+	</class>
+	<class name="java/nio/file/attribute/AclEntry$Builder" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="build()Ljava/nio/file/attribute/AclEntry;" />
+		<method name="setFlags(Ljava/util/Set;)Ljava/nio/file/attribute/AclEntry$Builder;" />
+		<method name="setFlags([Ljava/nio/file/attribute/AclEntryFlag;)Ljava/nio/file/attribute/AclEntry$Builder;" />
+		<method name="setPermissions(Ljava/util/Set;)Ljava/nio/file/attribute/AclEntry$Builder;" />
+		<method name="setPermissions([Ljava/nio/file/attribute/AclEntryPermission;)Ljava/nio/file/attribute/AclEntry$Builder;" />
+		<method name="setPrincipal(Ljava/nio/file/attribute/UserPrincipal;)Ljava/nio/file/attribute/AclEntry$Builder;" />
+		<method name="setType(Ljava/nio/file/attribute/AclEntryType;)Ljava/nio/file/attribute/AclEntry$Builder;" />
+	</class>
+	<class name="java/nio/file/attribute/AclEntryFlag" since="26">
+		<extends name="java/lang/Enum" />
+		<method name="valueOf(Ljava/lang/String;)Ljava/nio/file/attribute/AclEntryFlag;" />
+		<method name="values()[Ljava/nio/file/attribute/AclEntryFlag;" />
+		<field name="DIRECTORY_INHERIT" />
+		<field name="FILE_INHERIT" />
+		<field name="INHERIT_ONLY" />
+		<field name="NO_PROPAGATE_INHERIT" />
+	</class>
+	<class name="java/nio/file/attribute/AclEntryPermission" since="26">
+		<extends name="java/lang/Enum" />
+		<method name="valueOf(Ljava/lang/String;)Ljava/nio/file/attribute/AclEntryPermission;" />
+		<method name="values()[Ljava/nio/file/attribute/AclEntryPermission;" />
+		<field name="ADD_FILE" />
+		<field name="ADD_SUBDIRECTORY" />
+		<field name="APPEND_DATA" />
+		<field name="DELETE" />
+		<field name="DELETE_CHILD" />
+		<field name="EXECUTE" />
+		<field name="LIST_DIRECTORY" />
+		<field name="READ_ACL" />
+		<field name="READ_ATTRIBUTES" />
+		<field name="READ_DATA" />
+		<field name="READ_NAMED_ATTRS" />
+		<field name="SYNCHRONIZE" />
+		<field name="WRITE_ACL" />
+		<field name="WRITE_ATTRIBUTES" />
+		<field name="WRITE_DATA" />
+		<field name="WRITE_NAMED_ATTRS" />
+		<field name="WRITE_OWNER" />
+	</class>
+	<class name="java/nio/file/attribute/AclEntryType" since="26">
+		<extends name="java/lang/Enum" />
+		<method name="valueOf(Ljava/lang/String;)Ljava/nio/file/attribute/AclEntryType;" />
+		<method name="values()[Ljava/nio/file/attribute/AclEntryType;" />
+		<field name="ALARM" />
+		<field name="ALLOW" />
+		<field name="AUDIT" />
+		<field name="DENY" />
+	</class>
+	<class name="java/nio/file/attribute/AclFileAttributeView" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/nio/file/attribute/FileOwnerAttributeView" />
+		<method name="getAcl()Ljava/util/List;" />
+		<method name="setAcl(Ljava/util/List;)V" />
+	</class>
+	<class name="java/nio/file/attribute/AttributeView" since="26">
+		<extends name="java/lang/Object" />
+		<method name="name()Ljava/lang/String;" />
+	</class>
+	<class name="java/nio/file/attribute/BasicFileAttributeView" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/nio/file/attribute/FileAttributeView" />
+		<method name="readAttributes()Ljava/nio/file/attribute/BasicFileAttributes;" />
+		<method name="setTimes(Ljava/nio/file/attribute/FileTime;Ljava/nio/file/attribute/FileTime;Ljava/nio/file/attribute/FileTime;)V" />
+	</class>
+	<class name="java/nio/file/attribute/BasicFileAttributes" since="26">
+		<extends name="java/lang/Object" />
+		<method name="creationTime()Ljava/nio/file/attribute/FileTime;" />
+		<method name="fileKey()Ljava/lang/Object;" />
+		<method name="isDirectory()Z" />
+		<method name="isOther()Z" />
+		<method name="isRegularFile()Z" />
+		<method name="isSymbolicLink()Z" />
+		<method name="lastAccessTime()Ljava/nio/file/attribute/FileTime;" />
+		<method name="lastModifiedTime()Ljava/nio/file/attribute/FileTime;" />
+		<method name="size()J" />
+	</class>
+	<class name="java/nio/file/attribute/DosFileAttributeView" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/nio/file/attribute/BasicFileAttributeView" />
+		<method name="readAttributes()Ljava/nio/file/attribute/DosFileAttributes;" />
+		<method name="setArchive(Z)V" />
+		<method name="setHidden(Z)V" />
+		<method name="setReadOnly(Z)V" />
+		<method name="setSystem(Z)V" />
+	</class>
+	<class name="java/nio/file/attribute/DosFileAttributes" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/nio/file/attribute/BasicFileAttributes" />
+		<method name="isArchive()Z" />
+		<method name="isHidden()Z" />
+		<method name="isReadOnly()Z" />
+		<method name="isSystem()Z" />
+	</class>
+	<class name="java/nio/file/attribute/FileAttribute" since="26">
+		<extends name="java/lang/Object" />
+		<method name="name()Ljava/lang/String;" />
+		<method name="value()Ljava/lang/Object;" />
+	</class>
+	<class name="java/nio/file/attribute/FileAttributeView" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/nio/file/attribute/AttributeView" />
+	</class>
+	<class name="java/nio/file/attribute/FileOwnerAttributeView" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/nio/file/attribute/FileAttributeView" />
+		<method name="getOwner()Ljava/nio/file/attribute/UserPrincipal;" />
+		<method name="setOwner(Ljava/nio/file/attribute/UserPrincipal;)V" />
+	</class>
+	<class name="java/nio/file/attribute/FileStoreAttributeView" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/nio/file/attribute/AttributeView" />
+	</class>
+	<class name="java/nio/file/attribute/FileTime" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/lang/Comparable" />
+		<method name="&lt;init>()V" />
+		<method name="compareTo(Ljava/nio/file/attribute/FileTime;)I" />
+		<method name="from(JLjava/util/concurrent/TimeUnit;)Ljava/nio/file/attribute/FileTime;" />
+		<method name="from(Ljava/time/Instant;)Ljava/nio/file/attribute/FileTime;" />
+		<method name="fromMillis(J)Ljava/nio/file/attribute/FileTime;" />
+		<method name="to(Ljava/util/concurrent/TimeUnit;)J" />
+		<method name="toInstant()Ljava/time/Instant;" />
+		<method name="toMillis()J" />
+	</class>
+	<class name="java/nio/file/attribute/GroupPrincipal" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/nio/file/attribute/UserPrincipal" />
+	</class>
+	<class name="java/nio/file/attribute/PosixFileAttributeView" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/nio/file/attribute/BasicFileAttributeView" />
+		<implements name="java/nio/file/attribute/FileOwnerAttributeView" />
+		<method name="readAttributes()Ljava/nio/file/attribute/PosixFileAttributes;" />
+		<method name="setGroup(Ljava/nio/file/attribute/GroupPrincipal;)V" />
+		<method name="setPermissions(Ljava/util/Set;)V" />
+	</class>
+	<class name="java/nio/file/attribute/PosixFileAttributes" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/nio/file/attribute/BasicFileAttributes" />
+		<method name="group()Ljava/nio/file/attribute/GroupPrincipal;" />
+		<method name="owner()Ljava/nio/file/attribute/UserPrincipal;" />
+		<method name="permissions()Ljava/util/Set;" />
+	</class>
+	<class name="java/nio/file/attribute/PosixFilePermission" since="26">
+		<extends name="java/lang/Enum" />
+		<method name="valueOf(Ljava/lang/String;)Ljava/nio/file/attribute/PosixFilePermission;" />
+		<method name="values()[Ljava/nio/file/attribute/PosixFilePermission;" />
+		<field name="GROUP_EXECUTE" />
+		<field name="GROUP_READ" />
+		<field name="GROUP_WRITE" />
+		<field name="OTHERS_EXECUTE" />
+		<field name="OTHERS_READ" />
+		<field name="OTHERS_WRITE" />
+		<field name="OWNER_EXECUTE" />
+		<field name="OWNER_READ" />
+		<field name="OWNER_WRITE" />
+	</class>
+	<class name="java/nio/file/attribute/PosixFilePermissions" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="asFileAttribute(Ljava/util/Set;)Ljava/nio/file/attribute/FileAttribute;" />
+		<method name="fromString(Ljava/lang/String;)Ljava/util/Set;" />
+		<method name="toString(Ljava/util/Set;)Ljava/lang/String;" />
+	</class>
+	<class name="java/nio/file/attribute/UserDefinedFileAttributeView" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/nio/file/attribute/FileAttributeView" />
+		<method name="delete(Ljava/lang/String;)V" />
+		<method name="list()Ljava/util/List;" />
+		<method name="read(Ljava/lang/String;Ljava/nio/ByteBuffer;)I" />
+		<method name="size(Ljava/lang/String;)I" />
+		<method name="write(Ljava/lang/String;Ljava/nio/ByteBuffer;)I" />
+	</class>
+	<class name="java/nio/file/attribute/UserPrincipal" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/security/Principal" />
+	</class>
+	<class name="java/nio/file/attribute/UserPrincipalLookupService" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="lookupPrincipalByGroupName(Ljava/lang/String;)Ljava/nio/file/attribute/GroupPrincipal;" />
+		<method name="lookupPrincipalByName(Ljava/lang/String;)Ljava/nio/file/attribute/UserPrincipal;" />
+	</class>
+	<class name="java/nio/file/attribute/UserPrincipalNotFoundException" since="26">
+		<extends name="java/io/IOException" />
+		<method name="&lt;init>(Ljava/lang/String;)V" />
+		<method name="getName()Ljava/lang/String;" />
+	</class>
+	<class name="java/nio/file/spi/FileSystemProvider" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="checkAccess(Ljava/nio/file/Path;[Ljava/nio/file/AccessMode;)V" />
+		<method name="copy(Ljava/nio/file/Path;Ljava/nio/file/Path;[Ljava/nio/file/CopyOption;)V" />
+		<method name="createDirectory(Ljava/nio/file/Path;[Ljava/nio/file/attribute/FileAttribute;)V" />
+		<method name="createLink(Ljava/nio/file/Path;Ljava/nio/file/Path;)V" />
+		<method name="createSymbolicLink(Ljava/nio/file/Path;Ljava/nio/file/Path;[Ljava/nio/file/attribute/FileAttribute;)V" />
+		<method name="delete(Ljava/nio/file/Path;)V" />
+		<method name="deleteIfExists(Ljava/nio/file/Path;)Z" />
+		<method name="getFileAttributeView(Ljava/nio/file/Path;Ljava/lang/Class;[Ljava/nio/file/LinkOption;)Ljava/nio/file/attribute/FileAttributeView;" />
+		<method name="getFileStore(Ljava/nio/file/Path;)Ljava/nio/file/FileStore;" />
+		<method name="getFileSystem(Ljava/net/URI;)Ljava/nio/file/FileSystem;" />
+		<method name="getPath(Ljava/net/URI;)Ljava/nio/file/Path;" />
+		<method name="getScheme()Ljava/lang/String;" />
+		<method name="installedProviders()Ljava/util/List;" />
+		<method name="isHidden(Ljava/nio/file/Path;)Z" />
+		<method name="isSameFile(Ljava/nio/file/Path;Ljava/nio/file/Path;)Z" />
+		<method name="move(Ljava/nio/file/Path;Ljava/nio/file/Path;[Ljava/nio/file/CopyOption;)V" />
+		<method name="newAsynchronousFileChannel(Ljava/nio/file/Path;Ljava/util/Set;Ljava/util/concurrent/ExecutorService;[Ljava/nio/file/attribute/FileAttribute;)Ljava/nio/channels/AsynchronousFileChannel;" />
+		<method name="newByteChannel(Ljava/nio/file/Path;Ljava/util/Set;[Ljava/nio/file/attribute/FileAttribute;)Ljava/nio/channels/SeekableByteChannel;" />
+		<method name="newDirectoryStream(Ljava/nio/file/Path;Ljava/nio/file/DirectoryStream$Filter;)Ljava/nio/file/DirectoryStream;" />
+		<method name="newFileChannel(Ljava/nio/file/Path;Ljava/util/Set;[Ljava/nio/file/attribute/FileAttribute;)Ljava/nio/channels/FileChannel;" />
+		<method name="newFileSystem(Ljava/net/URI;Ljava/util/Map;)Ljava/nio/file/FileSystem;" />
+		<method name="newFileSystem(Ljava/nio/file/Path;Ljava/util/Map;)Ljava/nio/file/FileSystem;" />
+		<method name="newInputStream(Ljava/nio/file/Path;[Ljava/nio/file/OpenOption;)Ljava/io/InputStream;" />
+		<method name="newOutputStream(Ljava/nio/file/Path;[Ljava/nio/file/OpenOption;)Ljava/io/OutputStream;" />
+		<method name="readAttributes(Ljava/nio/file/Path;Ljava/lang/Class;[Ljava/nio/file/LinkOption;)Ljava/nio/file/attribute/BasicFileAttributes;" />
+		<method name="readAttributes(Ljava/nio/file/Path;Ljava/lang/String;[Ljava/nio/file/LinkOption;)Ljava/util/Map;" />
+		<method name="readSymbolicLink(Ljava/nio/file/Path;)Ljava/nio/file/Path;" />
+		<method name="setAttribute(Ljava/nio/file/Path;Ljava/lang/String;Ljava/lang/Object;[Ljava/nio/file/LinkOption;)V" />
+	</class>
+	<class name="java/nio/file/spi/FileTypeDetector" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="probeContentType(Ljava/nio/file/Path;)Ljava/lang/String;" />
+	</class>
 	<class name="java/security/AccessControlContext" since="1">
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>(Ljava/security/AccessControlContext;Ljava/security/DomainCombiner;)V" />
@@ -54496,6 +58644,13 @@
 		<extends name="java/lang/Object" />
 		<method name="combine([Ljava/security/ProtectionDomain;[Ljava/security/ProtectionDomain;)[Ljava/security/ProtectionDomain;" />
 	</class>
+	<class name="java/security/DomainLoadStoreParameter" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/security/KeyStore$LoadStoreParameter" />
+		<method name="&lt;init>(Ljava/net/URI;Ljava/util/Map;)V" />
+		<method name="getConfiguration()Ljava/net/URI;" />
+		<method name="getProtectionParams()Ljava/util/Map;" />
+	</class>
 	<class name="java/security/GeneralSecurityException" since="1">
 		<extends name="java/lang/Exception" />
 		<method name="&lt;init>()V" />
@@ -54696,6 +58851,12 @@
 	</class>
 	<class name="java/security/KeyStore$Entry" since="1">
 		<extends name="java/lang/Object" />
+		<method name="getAttributes()Ljava/util/Set;" since="26" />
+	</class>
+	<class name="java/security/KeyStore$Entry$Attribute" since="26">
+		<extends name="java/lang/Object" />
+		<method name="getName()Ljava/lang/String;" />
+		<method name="getValue()Ljava/lang/String;" />
 	</class>
 	<class name="java/security/KeyStore$LoadStoreParameter" since="1">
 		<extends name="java/lang/Object" />
@@ -54706,12 +58867,16 @@
 		<implements name="java/security/KeyStore$ProtectionParameter" />
 		<implements name="javax/security/auth/Destroyable" />
 		<method name="&lt;init>([C)V" />
+		<method name="&lt;init>([CLjava/lang/String;Ljava/security/spec/AlgorithmParameterSpec;)V" since="26" />
 		<method name="getPassword()[C" />
+		<method name="getProtectionAlgorithm()Ljava/lang/String;" since="26" />
+		<method name="getProtectionParameters()Ljava/security/spec/AlgorithmParameterSpec;" since="26" />
 	</class>
 	<class name="java/security/KeyStore$PrivateKeyEntry" since="1">
 		<extends name="java/lang/Object" />
 		<implements name="java/security/KeyStore$Entry" />
 		<method name="&lt;init>(Ljava/security/PrivateKey;[Ljava/security/cert/Certificate;)V" />
+		<method name="&lt;init>(Ljava/security/PrivateKey;[Ljava/security/cert/Certificate;Ljava/util/Set;)V" since="26" />
 		<method name="getCertificate()Ljava/security/cert/Certificate;" />
 		<method name="getCertificateChain()[Ljava/security/cert/Certificate;" />
 		<method name="getPrivateKey()Ljava/security/PrivateKey;" />
@@ -54723,12 +58888,14 @@
 		<extends name="java/lang/Object" />
 		<implements name="java/security/KeyStore$Entry" />
 		<method name="&lt;init>(Ljavax/crypto/SecretKey;)V" />
+		<method name="&lt;init>(Ljavax/crypto/SecretKey;Ljava/util/Set;)V" since="26" />
 		<method name="getSecretKey()Ljavax/crypto/SecretKey;" />
 	</class>
 	<class name="java/security/KeyStore$TrustedCertificateEntry" since="1">
 		<extends name="java/lang/Object" />
 		<implements name="java/security/KeyStore$Entry" />
 		<method name="&lt;init>(Ljava/security/cert/Certificate;)V" />
+		<method name="&lt;init>(Ljava/security/cert/Certificate;Ljava/util/Set;)V" since="26" />
 		<method name="getTrustedCertificate()Ljava/security/cert/Certificate;" />
 	</class>
 	<class name="java/security/KeyStoreException" since="1">
@@ -54805,6 +58972,13 @@
 		<method name="&lt;init>()V" />
 		<method name="&lt;init>(Ljava/lang/String;)V" />
 	</class>
+	<class name="java/security/PKCS12Attribute" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/security/KeyStore$Entry$Attribute" />
+		<method name="&lt;init>(Ljava/lang/String;Ljava/lang/String;)V" />
+		<method name="&lt;init>([B)V" />
+		<method name="getEncoded()[B" />
+	</class>
 	<class name="java/security/Permission" since="1">
 		<extends name="java/lang/Object" />
 		<implements name="java/io/Serializable" />
@@ -54860,10 +59034,12 @@
 	<class name="java/security/Principal" since="1">
 		<extends name="java/lang/Object" />
 		<method name="getName()Ljava/lang/String;" />
+		<method name="implies(Ljavax/security/auth/Subject;)Z" since="26" />
 	</class>
 	<class name="java/security/PrivateKey" since="1">
 		<extends name="java/lang/Object" />
 		<implements name="java/security/Key" />
+		<implements name="javax/security/auth/Destroyable" since="26" />
 		<field name="serialVersionUID" />
 	</class>
 	<class name="java/security/PrivilegedAction" since="1">
@@ -54941,6 +59117,7 @@
 		<method name="getInstance(Ljava/lang/String;)Ljava/security/SecureRandom;" />
 		<method name="getInstance(Ljava/lang/String;Ljava/lang/String;)Ljava/security/SecureRandom;" />
 		<method name="getInstance(Ljava/lang/String;Ljava/security/Provider;)Ljava/security/SecureRandom;" />
+		<method name="getInstanceStrong()Ljava/security/SecureRandom;" since="26" />
 		<method name="getProvider()Ljava/security/Provider;" />
 		<method name="getSeed(I)[B" />
 		<method name="setSeed([B)V" />
@@ -55309,6 +59486,7 @@
 		<method name="getType()Ljava/lang/String;" />
 		<method name="verify(Ljava/security/PublicKey;)V" />
 		<method name="verify(Ljava/security/PublicKey;Ljava/lang/String;)V" />
+		<method name="verify(Ljava/security/PublicKey;Ljava/security/Provider;)V" since="26" />
 		<method name="writeReplace()Ljava/lang/Object;" />
 	</class>
 	<class name="java/security/cert/Certificate$CertificateRep" since="1">
@@ -55558,6 +59736,7 @@
 		<method name="getVersion()I" />
 		<method name="verify(Ljava/security/PublicKey;)V" />
 		<method name="verify(Ljava/security/PublicKey;Ljava/lang/String;)V" />
+		<method name="verify(Ljava/security/PublicKey;Ljava/security/Provider;)V" since="26" />
 	</class>
 	<class name="java/security/cert/X509CRLEntry" since="1">
 		<extends name="java/lang/Object" />
@@ -55887,6 +60066,7 @@
 		<method name="&lt;init>(Ljava/lang/String;)V" />
 		<method name="getDigestAlgorithm()Ljava/lang/String;" />
 		<field name="SHA1" />
+		<field name="SHA224" since="26" />
 		<field name="SHA256" />
 		<field name="SHA384" />
 		<field name="SHA512" />
@@ -57711,6 +61891,1500 @@
 		<method name="&lt;init>(Ljava/lang/String;III)V" />
 		<method name="setText(Ljava/lang/String;)V" />
 	</class>
+	<class name="java/time/Clock" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="fixed(Ljava/time/Instant;Ljava/time/ZoneId;)Ljava/time/Clock;" />
+		<method name="getZone()Ljava/time/ZoneId;" />
+		<method name="instant()Ljava/time/Instant;" />
+		<method name="millis()J" />
+		<method name="offset(Ljava/time/Clock;Ljava/time/Duration;)Ljava/time/Clock;" />
+		<method name="system(Ljava/time/ZoneId;)Ljava/time/Clock;" />
+		<method name="systemDefaultZone()Ljava/time/Clock;" />
+		<method name="systemUTC()Ljava/time/Clock;" />
+		<method name="tick(Ljava/time/Clock;Ljava/time/Duration;)Ljava/time/Clock;" />
+		<method name="tickMinutes(Ljava/time/ZoneId;)Ljava/time/Clock;" />
+		<method name="tickSeconds(Ljava/time/ZoneId;)Ljava/time/Clock;" />
+		<method name="withZone(Ljava/time/ZoneId;)Ljava/time/Clock;" />
+	</class>
+	<class name="java/time/DateTimeException" since="26">
+		<extends name="java/lang/RuntimeException" />
+		<method name="&lt;init>(Ljava/lang/String;)V" />
+		<method name="&lt;init>(Ljava/lang/String;Ljava/lang/Throwable;)V" />
+	</class>
+	<class name="java/time/DayOfWeek" since="26">
+		<extends name="java/lang/Enum" />
+		<implements name="java/time/temporal/TemporalAccessor" />
+		<implements name="java/time/temporal/TemporalAdjuster" />
+		<method name="from(Ljava/time/temporal/TemporalAccessor;)Ljava/time/DayOfWeek;" />
+		<method name="getDisplayName(Ljava/time/format/TextStyle;Ljava/util/Locale;)Ljava/lang/String;" />
+		<method name="getValue()I" />
+		<method name="minus(J)Ljava/time/DayOfWeek;" />
+		<method name="of(I)Ljava/time/DayOfWeek;" />
+		<method name="plus(J)Ljava/time/DayOfWeek;" />
+		<method name="valueOf(Ljava/lang/String;)Ljava/time/DayOfWeek;" />
+		<method name="values()[Ljava/time/DayOfWeek;" />
+		<field name="FRIDAY" />
+		<field name="MONDAY" />
+		<field name="SATURDAY" />
+		<field name="SUNDAY" />
+		<field name="THURSDAY" />
+		<field name="TUESDAY" />
+		<field name="WEDNESDAY" />
+	</class>
+	<class name="java/time/Duration" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/io/Serializable" />
+		<implements name="java/lang/Comparable" />
+		<implements name="java/time/temporal/TemporalAmount" />
+		<method name="&lt;init>()V" />
+		<method name="abs()Ljava/time/Duration;" />
+		<method name="between(Ljava/time/temporal/Temporal;Ljava/time/temporal/Temporal;)Ljava/time/Duration;" />
+		<method name="compareTo(Ljava/time/Duration;)I" />
+		<method name="dividedBy(J)Ljava/time/Duration;" />
+		<method name="from(Ljava/time/temporal/TemporalAmount;)Ljava/time/Duration;" />
+		<method name="getNano()I" />
+		<method name="getSeconds()J" />
+		<method name="isNegative()Z" />
+		<method name="isZero()Z" />
+		<method name="minus(JLjava/time/temporal/TemporalUnit;)Ljava/time/Duration;" />
+		<method name="minus(Ljava/time/Duration;)Ljava/time/Duration;" />
+		<method name="minusDays(J)Ljava/time/Duration;" />
+		<method name="minusHours(J)Ljava/time/Duration;" />
+		<method name="minusMillis(J)Ljava/time/Duration;" />
+		<method name="minusMinutes(J)Ljava/time/Duration;" />
+		<method name="minusNanos(J)Ljava/time/Duration;" />
+		<method name="minusSeconds(J)Ljava/time/Duration;" />
+		<method name="multipliedBy(J)Ljava/time/Duration;" />
+		<method name="negated()Ljava/time/Duration;" />
+		<method name="of(JLjava/time/temporal/TemporalUnit;)Ljava/time/Duration;" />
+		<method name="ofDays(J)Ljava/time/Duration;" />
+		<method name="ofHours(J)Ljava/time/Duration;" />
+		<method name="ofMillis(J)Ljava/time/Duration;" />
+		<method name="ofMinutes(J)Ljava/time/Duration;" />
+		<method name="ofNanos(J)Ljava/time/Duration;" />
+		<method name="ofSeconds(J)Ljava/time/Duration;" />
+		<method name="ofSeconds(JJ)Ljava/time/Duration;" />
+		<method name="parse(Ljava/lang/CharSequence;)Ljava/time/Duration;" />
+		<method name="plus(JLjava/time/temporal/TemporalUnit;)Ljava/time/Duration;" />
+		<method name="plus(Ljava/time/Duration;)Ljava/time/Duration;" />
+		<method name="plusDays(J)Ljava/time/Duration;" />
+		<method name="plusHours(J)Ljava/time/Duration;" />
+		<method name="plusMillis(J)Ljava/time/Duration;" />
+		<method name="plusMinutes(J)Ljava/time/Duration;" />
+		<method name="plusNanos(J)Ljava/time/Duration;" />
+		<method name="plusSeconds(J)Ljava/time/Duration;" />
+		<method name="toDays()J" />
+		<method name="toHours()J" />
+		<method name="toMillis()J" />
+		<method name="toMinutes()J" />
+		<method name="toNanos()J" />
+		<method name="withNanos(I)Ljava/time/Duration;" />
+		<method name="withSeconds(J)Ljava/time/Duration;" />
+		<field name="ZERO" />
+	</class>
+	<class name="java/time/Instant" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/io/Serializable" />
+		<implements name="java/lang/Comparable" />
+		<implements name="java/time/temporal/Temporal" />
+		<implements name="java/time/temporal/TemporalAdjuster" />
+		<method name="&lt;init>()V" />
+		<method name="atOffset(Ljava/time/ZoneOffset;)Ljava/time/OffsetDateTime;" />
+		<method name="atZone(Ljava/time/ZoneId;)Ljava/time/ZonedDateTime;" />
+		<method name="compareTo(Ljava/time/Instant;)I" />
+		<method name="from(Ljava/time/temporal/TemporalAccessor;)Ljava/time/Instant;" />
+		<method name="getEpochSecond()J" />
+		<method name="getNano()I" />
+		<method name="isAfter(Ljava/time/Instant;)Z" />
+		<method name="isBefore(Ljava/time/Instant;)Z" />
+		<method name="minus(JLjava/time/temporal/TemporalUnit;)Ljava/time/Instant;" />
+		<method name="minus(Ljava/time/temporal/TemporalAmount;)Ljava/time/Instant;" />
+		<method name="minusMillis(J)Ljava/time/Instant;" />
+		<method name="minusNanos(J)Ljava/time/Instant;" />
+		<method name="minusSeconds(J)Ljava/time/Instant;" />
+		<method name="now()Ljava/time/Instant;" />
+		<method name="now(Ljava/time/Clock;)Ljava/time/Instant;" />
+		<method name="ofEpochMilli(J)Ljava/time/Instant;" />
+		<method name="ofEpochSecond(J)Ljava/time/Instant;" />
+		<method name="ofEpochSecond(JJ)Ljava/time/Instant;" />
+		<method name="parse(Ljava/lang/CharSequence;)Ljava/time/Instant;" />
+		<method name="plus(JLjava/time/temporal/TemporalUnit;)Ljava/time/Instant;" />
+		<method name="plus(Ljava/time/temporal/TemporalAmount;)Ljava/time/Instant;" />
+		<method name="plusMillis(J)Ljava/time/Instant;" />
+		<method name="plusNanos(J)Ljava/time/Instant;" />
+		<method name="plusSeconds(J)Ljava/time/Instant;" />
+		<method name="toEpochMilli()J" />
+		<method name="truncatedTo(Ljava/time/temporal/TemporalUnit;)Ljava/time/Instant;" />
+		<method name="with(Ljava/time/temporal/TemporalAdjuster;)Ljava/time/Instant;" />
+		<method name="with(Ljava/time/temporal/TemporalField;J)Ljava/time/Instant;" />
+		<field name="EPOCH" />
+		<field name="MAX" />
+		<field name="MIN" />
+	</class>
+	<class name="java/time/LocalDate" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/io/Serializable" />
+		<implements name="java/time/chrono/ChronoLocalDate" />
+		<implements name="java/time/temporal/Temporal" />
+		<implements name="java/time/temporal/TemporalAdjuster" />
+		<method name="&lt;init>()V" />
+		<method name="atStartOfDay()Ljava/time/LocalDateTime;" />
+		<method name="atStartOfDay(Ljava/time/ZoneId;)Ljava/time/ZonedDateTime;" />
+		<method name="atTime(II)Ljava/time/LocalDateTime;" />
+		<method name="atTime(III)Ljava/time/LocalDateTime;" />
+		<method name="atTime(IIII)Ljava/time/LocalDateTime;" />
+		<method name="atTime(Ljava/time/LocalTime;)Ljava/time/LocalDateTime;" />
+		<method name="atTime(Ljava/time/OffsetTime;)Ljava/time/OffsetDateTime;" />
+		<method name="from(Ljava/time/temporal/TemporalAccessor;)Ljava/time/LocalDate;" />
+		<method name="getChronology()Ljava/time/chrono/IsoChronology;" />
+		<method name="getDayOfMonth()I" />
+		<method name="getDayOfWeek()Ljava/time/DayOfWeek;" />
+		<method name="getDayOfYear()I" />
+		<method name="getMonth()Ljava/time/Month;" />
+		<method name="getMonthValue()I" />
+		<method name="getYear()I" />
+		<method name="minus(JLjava/time/temporal/TemporalUnit;)Ljava/time/LocalDate;" />
+		<method name="minus(Ljava/time/temporal/TemporalAmount;)Ljava/time/LocalDate;" />
+		<method name="minusDays(J)Ljava/time/LocalDate;" />
+		<method name="minusMonths(J)Ljava/time/LocalDate;" />
+		<method name="minusWeeks(J)Ljava/time/LocalDate;" />
+		<method name="minusYears(J)Ljava/time/LocalDate;" />
+		<method name="now()Ljava/time/LocalDate;" />
+		<method name="now(Ljava/time/Clock;)Ljava/time/LocalDate;" />
+		<method name="now(Ljava/time/ZoneId;)Ljava/time/LocalDate;" />
+		<method name="of(III)Ljava/time/LocalDate;" />
+		<method name="of(ILjava/time/Month;I)Ljava/time/LocalDate;" />
+		<method name="ofEpochDay(J)Ljava/time/LocalDate;" />
+		<method name="ofYearDay(II)Ljava/time/LocalDate;" />
+		<method name="parse(Ljava/lang/CharSequence;)Ljava/time/LocalDate;" />
+		<method name="parse(Ljava/lang/CharSequence;Ljava/time/format/DateTimeFormatter;)Ljava/time/LocalDate;" />
+		<method name="plus(JLjava/time/temporal/TemporalUnit;)Ljava/time/LocalDate;" />
+		<method name="plus(Ljava/time/temporal/TemporalAmount;)Ljava/time/LocalDate;" />
+		<method name="plusDays(J)Ljava/time/LocalDate;" />
+		<method name="plusMonths(J)Ljava/time/LocalDate;" />
+		<method name="plusWeeks(J)Ljava/time/LocalDate;" />
+		<method name="plusYears(J)Ljava/time/LocalDate;" />
+		<method name="until(Ljava/time/chrono/ChronoLocalDate;)Ljava/time/Period;" />
+		<method name="with(Ljava/time/temporal/TemporalAdjuster;)Ljava/time/LocalDate;" />
+		<method name="with(Ljava/time/temporal/TemporalField;J)Ljava/time/LocalDate;" />
+		<method name="withDayOfMonth(I)Ljava/time/LocalDate;" />
+		<method name="withDayOfYear(I)Ljava/time/LocalDate;" />
+		<method name="withMonth(I)Ljava/time/LocalDate;" />
+		<method name="withYear(I)Ljava/time/LocalDate;" />
+		<field name="MAX" />
+		<field name="MIN" />
+	</class>
+	<class name="java/time/LocalDateTime" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/io/Serializable" />
+		<implements name="java/time/chrono/ChronoLocalDateTime" />
+		<implements name="java/time/temporal/Temporal" />
+		<implements name="java/time/temporal/TemporalAdjuster" />
+		<method name="&lt;init>()V" />
+		<method name="atOffset(Ljava/time/ZoneOffset;)Ljava/time/OffsetDateTime;" />
+		<method name="atZone(Ljava/time/ZoneId;)Ljava/time/ZonedDateTime;" />
+		<method name="from(Ljava/time/temporal/TemporalAccessor;)Ljava/time/LocalDateTime;" />
+		<method name="getDayOfMonth()I" />
+		<method name="getDayOfWeek()Ljava/time/DayOfWeek;" />
+		<method name="getDayOfYear()I" />
+		<method name="getHour()I" />
+		<method name="getMinute()I" />
+		<method name="getMonth()Ljava/time/Month;" />
+		<method name="getMonthValue()I" />
+		<method name="getNano()I" />
+		<method name="getSecond()I" />
+		<method name="getYear()I" />
+		<method name="minus(JLjava/time/temporal/TemporalUnit;)Ljava/time/LocalDateTime;" />
+		<method name="minus(Ljava/time/temporal/TemporalAmount;)Ljava/time/LocalDateTime;" />
+		<method name="minusDays(J)Ljava/time/LocalDateTime;" />
+		<method name="minusHours(J)Ljava/time/LocalDateTime;" />
+		<method name="minusMinutes(J)Ljava/time/LocalDateTime;" />
+		<method name="minusMonths(J)Ljava/time/LocalDateTime;" />
+		<method name="minusNanos(J)Ljava/time/LocalDateTime;" />
+		<method name="minusSeconds(J)Ljava/time/LocalDateTime;" />
+		<method name="minusWeeks(J)Ljava/time/LocalDateTime;" />
+		<method name="minusYears(J)Ljava/time/LocalDateTime;" />
+		<method name="now()Ljava/time/LocalDateTime;" />
+		<method name="now(Ljava/time/Clock;)Ljava/time/LocalDateTime;" />
+		<method name="now(Ljava/time/ZoneId;)Ljava/time/LocalDateTime;" />
+		<method name="of(IIIII)Ljava/time/LocalDateTime;" />
+		<method name="of(IIIIII)Ljava/time/LocalDateTime;" />
+		<method name="of(IIIIIII)Ljava/time/LocalDateTime;" />
+		<method name="of(ILjava/time/Month;III)Ljava/time/LocalDateTime;" />
+		<method name="of(ILjava/time/Month;IIII)Ljava/time/LocalDateTime;" />
+		<method name="of(ILjava/time/Month;IIIII)Ljava/time/LocalDateTime;" />
+		<method name="of(Ljava/time/LocalDate;Ljava/time/LocalTime;)Ljava/time/LocalDateTime;" />
+		<method name="ofEpochSecond(JILjava/time/ZoneOffset;)Ljava/time/LocalDateTime;" />
+		<method name="ofInstant(Ljava/time/Instant;Ljava/time/ZoneId;)Ljava/time/LocalDateTime;" />
+		<method name="parse(Ljava/lang/CharSequence;)Ljava/time/LocalDateTime;" />
+		<method name="parse(Ljava/lang/CharSequence;Ljava/time/format/DateTimeFormatter;)Ljava/time/LocalDateTime;" />
+		<method name="plus(JLjava/time/temporal/TemporalUnit;)Ljava/time/LocalDateTime;" />
+		<method name="plus(Ljava/time/temporal/TemporalAmount;)Ljava/time/LocalDateTime;" />
+		<method name="plusDays(J)Ljava/time/LocalDateTime;" />
+		<method name="plusHours(J)Ljava/time/LocalDateTime;" />
+		<method name="plusMinutes(J)Ljava/time/LocalDateTime;" />
+		<method name="plusMonths(J)Ljava/time/LocalDateTime;" />
+		<method name="plusNanos(J)Ljava/time/LocalDateTime;" />
+		<method name="plusSeconds(J)Ljava/time/LocalDateTime;" />
+		<method name="plusWeeks(J)Ljava/time/LocalDateTime;" />
+		<method name="plusYears(J)Ljava/time/LocalDateTime;" />
+		<method name="toLocalDate()Ljava/time/LocalDate;" />
+		<method name="truncatedTo(Ljava/time/temporal/TemporalUnit;)Ljava/time/LocalDateTime;" />
+		<method name="with(Ljava/time/temporal/TemporalAdjuster;)Ljava/time/LocalDateTime;" />
+		<method name="with(Ljava/time/temporal/TemporalField;J)Ljava/time/LocalDateTime;" />
+		<method name="withDayOfMonth(I)Ljava/time/LocalDateTime;" />
+		<method name="withDayOfYear(I)Ljava/time/LocalDateTime;" />
+		<method name="withHour(I)Ljava/time/LocalDateTime;" />
+		<method name="withMinute(I)Ljava/time/LocalDateTime;" />
+		<method name="withMonth(I)Ljava/time/LocalDateTime;" />
+		<method name="withNano(I)Ljava/time/LocalDateTime;" />
+		<method name="withSecond(I)Ljava/time/LocalDateTime;" />
+		<method name="withYear(I)Ljava/time/LocalDateTime;" />
+		<field name="MAX" />
+		<field name="MIN" />
+	</class>
+	<class name="java/time/LocalTime" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/io/Serializable" />
+		<implements name="java/lang/Comparable" />
+		<implements name="java/time/temporal/Temporal" />
+		<implements name="java/time/temporal/TemporalAdjuster" />
+		<method name="&lt;init>()V" />
+		<method name="atDate(Ljava/time/LocalDate;)Ljava/time/LocalDateTime;" />
+		<method name="atOffset(Ljava/time/ZoneOffset;)Ljava/time/OffsetTime;" />
+		<method name="compareTo(Ljava/time/LocalTime;)I" />
+		<method name="format(Ljava/time/format/DateTimeFormatter;)Ljava/lang/String;" />
+		<method name="from(Ljava/time/temporal/TemporalAccessor;)Ljava/time/LocalTime;" />
+		<method name="getHour()I" />
+		<method name="getMinute()I" />
+		<method name="getNano()I" />
+		<method name="getSecond()I" />
+		<method name="isAfter(Ljava/time/LocalTime;)Z" />
+		<method name="isBefore(Ljava/time/LocalTime;)Z" />
+		<method name="minus(JLjava/time/temporal/TemporalUnit;)Ljava/time/LocalTime;" />
+		<method name="minus(Ljava/time/temporal/TemporalAmount;)Ljava/time/LocalTime;" />
+		<method name="minusHours(J)Ljava/time/LocalTime;" />
+		<method name="minusMinutes(J)Ljava/time/LocalTime;" />
+		<method name="minusNanos(J)Ljava/time/LocalTime;" />
+		<method name="minusSeconds(J)Ljava/time/LocalTime;" />
+		<method name="now()Ljava/time/LocalTime;" />
+		<method name="now(Ljava/time/Clock;)Ljava/time/LocalTime;" />
+		<method name="now(Ljava/time/ZoneId;)Ljava/time/LocalTime;" />
+		<method name="of(II)Ljava/time/LocalTime;" />
+		<method name="of(III)Ljava/time/LocalTime;" />
+		<method name="of(IIII)Ljava/time/LocalTime;" />
+		<method name="ofNanoOfDay(J)Ljava/time/LocalTime;" />
+		<method name="ofSecondOfDay(J)Ljava/time/LocalTime;" />
+		<method name="parse(Ljava/lang/CharSequence;)Ljava/time/LocalTime;" />
+		<method name="parse(Ljava/lang/CharSequence;Ljava/time/format/DateTimeFormatter;)Ljava/time/LocalTime;" />
+		<method name="plus(JLjava/time/temporal/TemporalUnit;)Ljava/time/LocalTime;" />
+		<method name="plus(Ljava/time/temporal/TemporalAmount;)Ljava/time/LocalTime;" />
+		<method name="plusHours(J)Ljava/time/LocalTime;" />
+		<method name="plusMinutes(J)Ljava/time/LocalTime;" />
+		<method name="plusNanos(J)Ljava/time/LocalTime;" />
+		<method name="plusSeconds(J)Ljava/time/LocalTime;" />
+		<method name="toNanoOfDay()J" />
+		<method name="toSecondOfDay()I" />
+		<method name="truncatedTo(Ljava/time/temporal/TemporalUnit;)Ljava/time/LocalTime;" />
+		<method name="with(Ljava/time/temporal/TemporalAdjuster;)Ljava/time/LocalTime;" />
+		<method name="with(Ljava/time/temporal/TemporalField;J)Ljava/time/LocalTime;" />
+		<method name="withHour(I)Ljava/time/LocalTime;" />
+		<method name="withMinute(I)Ljava/time/LocalTime;" />
+		<method name="withNano(I)Ljava/time/LocalTime;" />
+		<method name="withSecond(I)Ljava/time/LocalTime;" />
+		<field name="MAX" />
+		<field name="MIDNIGHT" />
+		<field name="MIN" />
+		<field name="NOON" />
+	</class>
+	<class name="java/time/Month" since="26">
+		<extends name="java/lang/Enum" />
+		<implements name="java/time/temporal/TemporalAccessor" />
+		<implements name="java/time/temporal/TemporalAdjuster" />
+		<method name="firstDayOfYear(Z)I" />
+		<method name="firstMonthOfQuarter()Ljava/time/Month;" />
+		<method name="from(Ljava/time/temporal/TemporalAccessor;)Ljava/time/Month;" />
+		<method name="getDisplayName(Ljava/time/format/TextStyle;Ljava/util/Locale;)Ljava/lang/String;" />
+		<method name="getValue()I" />
+		<method name="length(Z)I" />
+		<method name="maxLength()I" />
+		<method name="minLength()I" />
+		<method name="minus(J)Ljava/time/Month;" />
+		<method name="of(I)Ljava/time/Month;" />
+		<method name="plus(J)Ljava/time/Month;" />
+		<method name="valueOf(Ljava/lang/String;)Ljava/time/Month;" />
+		<method name="values()[Ljava/time/Month;" />
+		<field name="APRIL" />
+		<field name="AUGUST" />
+		<field name="DECEMBER" />
+		<field name="FEBRUARY" />
+		<field name="JANUARY" />
+		<field name="JULY" />
+		<field name="JUNE" />
+		<field name="MARCH" />
+		<field name="MAY" />
+		<field name="NOVEMBER" />
+		<field name="OCTOBER" />
+		<field name="SEPTEMBER" />
+	</class>
+	<class name="java/time/MonthDay" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/io/Serializable" />
+		<implements name="java/lang/Comparable" />
+		<implements name="java/time/temporal/TemporalAccessor" />
+		<implements name="java/time/temporal/TemporalAdjuster" />
+		<method name="&lt;init>()V" />
+		<method name="atYear(I)Ljava/time/LocalDate;" />
+		<method name="compareTo(Ljava/time/MonthDay;)I" />
+		<method name="format(Ljava/time/format/DateTimeFormatter;)Ljava/lang/String;" />
+		<method name="from(Ljava/time/temporal/TemporalAccessor;)Ljava/time/MonthDay;" />
+		<method name="getDayOfMonth()I" />
+		<method name="getMonth()Ljava/time/Month;" />
+		<method name="getMonthValue()I" />
+		<method name="isAfter(Ljava/time/MonthDay;)Z" />
+		<method name="isBefore(Ljava/time/MonthDay;)Z" />
+		<method name="isValidYear(I)Z" />
+		<method name="now()Ljava/time/MonthDay;" />
+		<method name="now(Ljava/time/Clock;)Ljava/time/MonthDay;" />
+		<method name="now(Ljava/time/ZoneId;)Ljava/time/MonthDay;" />
+		<method name="of(II)Ljava/time/MonthDay;" />
+		<method name="of(Ljava/time/Month;I)Ljava/time/MonthDay;" />
+		<method name="parse(Ljava/lang/CharSequence;)Ljava/time/MonthDay;" />
+		<method name="parse(Ljava/lang/CharSequence;Ljava/time/format/DateTimeFormatter;)Ljava/time/MonthDay;" />
+		<method name="with(Ljava/time/Month;)Ljava/time/MonthDay;" />
+		<method name="withDayOfMonth(I)Ljava/time/MonthDay;" />
+		<method name="withMonth(I)Ljava/time/MonthDay;" />
+	</class>
+	<class name="java/time/OffsetDateTime" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/io/Serializable" />
+		<implements name="java/lang/Comparable" />
+		<implements name="java/time/temporal/Temporal" />
+		<implements name="java/time/temporal/TemporalAdjuster" />
+		<method name="&lt;init>()V" />
+		<method name="atZoneSameInstant(Ljava/time/ZoneId;)Ljava/time/ZonedDateTime;" />
+		<method name="atZoneSimilarLocal(Ljava/time/ZoneId;)Ljava/time/ZonedDateTime;" />
+		<method name="compareTo(Ljava/time/OffsetDateTime;)I" />
+		<method name="format(Ljava/time/format/DateTimeFormatter;)Ljava/lang/String;" />
+		<method name="from(Ljava/time/temporal/TemporalAccessor;)Ljava/time/OffsetDateTime;" />
+		<method name="getDayOfMonth()I" />
+		<method name="getDayOfWeek()Ljava/time/DayOfWeek;" />
+		<method name="getDayOfYear()I" />
+		<method name="getHour()I" />
+		<method name="getMinute()I" />
+		<method name="getMonth()Ljava/time/Month;" />
+		<method name="getMonthValue()I" />
+		<method name="getNano()I" />
+		<method name="getOffset()Ljava/time/ZoneOffset;" />
+		<method name="getSecond()I" />
+		<method name="getYear()I" />
+		<method name="isAfter(Ljava/time/OffsetDateTime;)Z" />
+		<method name="isBefore(Ljava/time/OffsetDateTime;)Z" />
+		<method name="isEqual(Ljava/time/OffsetDateTime;)Z" />
+		<method name="minus(JLjava/time/temporal/TemporalUnit;)Ljava/time/OffsetDateTime;" />
+		<method name="minus(Ljava/time/temporal/TemporalAmount;)Ljava/time/OffsetDateTime;" />
+		<method name="minusDays(J)Ljava/time/OffsetDateTime;" />
+		<method name="minusHours(J)Ljava/time/OffsetDateTime;" />
+		<method name="minusMinutes(J)Ljava/time/OffsetDateTime;" />
+		<method name="minusMonths(J)Ljava/time/OffsetDateTime;" />
+		<method name="minusNanos(J)Ljava/time/OffsetDateTime;" />
+		<method name="minusSeconds(J)Ljava/time/OffsetDateTime;" />
+		<method name="minusWeeks(J)Ljava/time/OffsetDateTime;" />
+		<method name="minusYears(J)Ljava/time/OffsetDateTime;" />
+		<method name="now()Ljava/time/OffsetDateTime;" />
+		<method name="now(Ljava/time/Clock;)Ljava/time/OffsetDateTime;" />
+		<method name="now(Ljava/time/ZoneId;)Ljava/time/OffsetDateTime;" />
+		<method name="of(IIIIIIILjava/time/ZoneOffset;)Ljava/time/OffsetDateTime;" />
+		<method name="of(Ljava/time/LocalDate;Ljava/time/LocalTime;Ljava/time/ZoneOffset;)Ljava/time/OffsetDateTime;" />
+		<method name="of(Ljava/time/LocalDateTime;Ljava/time/ZoneOffset;)Ljava/time/OffsetDateTime;" />
+		<method name="ofInstant(Ljava/time/Instant;Ljava/time/ZoneId;)Ljava/time/OffsetDateTime;" />
+		<method name="parse(Ljava/lang/CharSequence;)Ljava/time/OffsetDateTime;" />
+		<method name="parse(Ljava/lang/CharSequence;Ljava/time/format/DateTimeFormatter;)Ljava/time/OffsetDateTime;" />
+		<method name="plus(JLjava/time/temporal/TemporalUnit;)Ljava/time/OffsetDateTime;" />
+		<method name="plus(Ljava/time/temporal/TemporalAmount;)Ljava/time/OffsetDateTime;" />
+		<method name="plusDays(J)Ljava/time/OffsetDateTime;" />
+		<method name="plusHours(J)Ljava/time/OffsetDateTime;" />
+		<method name="plusMinutes(J)Ljava/time/OffsetDateTime;" />
+		<method name="plusMonths(J)Ljava/time/OffsetDateTime;" />
+		<method name="plusNanos(J)Ljava/time/OffsetDateTime;" />
+		<method name="plusSeconds(J)Ljava/time/OffsetDateTime;" />
+		<method name="plusWeeks(J)Ljava/time/OffsetDateTime;" />
+		<method name="plusYears(J)Ljava/time/OffsetDateTime;" />
+		<method name="timeLineOrder()Ljava/util/Comparator;" />
+		<method name="toEpochSecond()J" />
+		<method name="toInstant()Ljava/time/Instant;" />
+		<method name="toLocalDate()Ljava/time/LocalDate;" />
+		<method name="toLocalDateTime()Ljava/time/LocalDateTime;" />
+		<method name="toLocalTime()Ljava/time/LocalTime;" />
+		<method name="toOffsetTime()Ljava/time/OffsetTime;" />
+		<method name="toZonedDateTime()Ljava/time/ZonedDateTime;" />
+		<method name="truncatedTo(Ljava/time/temporal/TemporalUnit;)Ljava/time/OffsetDateTime;" />
+		<method name="with(Ljava/time/temporal/TemporalAdjuster;)Ljava/time/OffsetDateTime;" />
+		<method name="with(Ljava/time/temporal/TemporalField;J)Ljava/time/OffsetDateTime;" />
+		<method name="withDayOfMonth(I)Ljava/time/OffsetDateTime;" />
+		<method name="withDayOfYear(I)Ljava/time/OffsetDateTime;" />
+		<method name="withHour(I)Ljava/time/OffsetDateTime;" />
+		<method name="withMinute(I)Ljava/time/OffsetDateTime;" />
+		<method name="withMonth(I)Ljava/time/OffsetDateTime;" />
+		<method name="withNano(I)Ljava/time/OffsetDateTime;" />
+		<method name="withOffsetSameInstant(Ljava/time/ZoneOffset;)Ljava/time/OffsetDateTime;" />
+		<method name="withOffsetSameLocal(Ljava/time/ZoneOffset;)Ljava/time/OffsetDateTime;" />
+		<method name="withSecond(I)Ljava/time/OffsetDateTime;" />
+		<method name="withYear(I)Ljava/time/OffsetDateTime;" />
+		<field name="MAX" />
+		<field name="MIN" />
+	</class>
+	<class name="java/time/OffsetTime" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/io/Serializable" />
+		<implements name="java/lang/Comparable" />
+		<implements name="java/time/temporal/Temporal" />
+		<implements name="java/time/temporal/TemporalAdjuster" />
+		<method name="&lt;init>()V" />
+		<method name="atDate(Ljava/time/LocalDate;)Ljava/time/OffsetDateTime;" />
+		<method name="compareTo(Ljava/time/OffsetTime;)I" />
+		<method name="format(Ljava/time/format/DateTimeFormatter;)Ljava/lang/String;" />
+		<method name="from(Ljava/time/temporal/TemporalAccessor;)Ljava/time/OffsetTime;" />
+		<method name="getHour()I" />
+		<method name="getMinute()I" />
+		<method name="getNano()I" />
+		<method name="getOffset()Ljava/time/ZoneOffset;" />
+		<method name="getSecond()I" />
+		<method name="isAfter(Ljava/time/OffsetTime;)Z" />
+		<method name="isBefore(Ljava/time/OffsetTime;)Z" />
+		<method name="isEqual(Ljava/time/OffsetTime;)Z" />
+		<method name="minus(JLjava/time/temporal/TemporalUnit;)Ljava/time/OffsetTime;" />
+		<method name="minus(Ljava/time/temporal/TemporalAmount;)Ljava/time/OffsetTime;" />
+		<method name="minusHours(J)Ljava/time/OffsetTime;" />
+		<method name="minusMinutes(J)Ljava/time/OffsetTime;" />
+		<method name="minusNanos(J)Ljava/time/OffsetTime;" />
+		<method name="minusSeconds(J)Ljava/time/OffsetTime;" />
+		<method name="now()Ljava/time/OffsetTime;" />
+		<method name="now(Ljava/time/Clock;)Ljava/time/OffsetTime;" />
+		<method name="now(Ljava/time/ZoneId;)Ljava/time/OffsetTime;" />
+		<method name="of(IIIILjava/time/ZoneOffset;)Ljava/time/OffsetTime;" />
+		<method name="of(Ljava/time/LocalTime;Ljava/time/ZoneOffset;)Ljava/time/OffsetTime;" />
+		<method name="ofInstant(Ljava/time/Instant;Ljava/time/ZoneId;)Ljava/time/OffsetTime;" />
+		<method name="parse(Ljava/lang/CharSequence;)Ljava/time/OffsetTime;" />
+		<method name="parse(Ljava/lang/CharSequence;Ljava/time/format/DateTimeFormatter;)Ljava/time/OffsetTime;" />
+		<method name="plus(JLjava/time/temporal/TemporalUnit;)Ljava/time/OffsetTime;" />
+		<method name="plus(Ljava/time/temporal/TemporalAmount;)Ljava/time/OffsetTime;" />
+		<method name="plusHours(J)Ljava/time/OffsetTime;" />
+		<method name="plusMinutes(J)Ljava/time/OffsetTime;" />
+		<method name="plusNanos(J)Ljava/time/OffsetTime;" />
+		<method name="plusSeconds(J)Ljava/time/OffsetTime;" />
+		<method name="toLocalTime()Ljava/time/LocalTime;" />
+		<method name="truncatedTo(Ljava/time/temporal/TemporalUnit;)Ljava/time/OffsetTime;" />
+		<method name="with(Ljava/time/temporal/TemporalAdjuster;)Ljava/time/OffsetTime;" />
+		<method name="with(Ljava/time/temporal/TemporalField;J)Ljava/time/OffsetTime;" />
+		<method name="withHour(I)Ljava/time/OffsetTime;" />
+		<method name="withMinute(I)Ljava/time/OffsetTime;" />
+		<method name="withNano(I)Ljava/time/OffsetTime;" />
+		<method name="withOffsetSameInstant(Ljava/time/ZoneOffset;)Ljava/time/OffsetTime;" />
+		<method name="withOffsetSameLocal(Ljava/time/ZoneOffset;)Ljava/time/OffsetTime;" />
+		<method name="withSecond(I)Ljava/time/OffsetTime;" />
+		<field name="MAX" />
+		<field name="MIN" />
+	</class>
+	<class name="java/time/Period" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/io/Serializable" />
+		<implements name="java/time/chrono/ChronoPeriod" />
+		<method name="&lt;init>()V" />
+		<method name="between(Ljava/time/LocalDate;Ljava/time/LocalDate;)Ljava/time/Period;" />
+		<method name="from(Ljava/time/temporal/TemporalAmount;)Ljava/time/Period;" />
+		<method name="getChronology()Ljava/time/chrono/IsoChronology;" />
+		<method name="getDays()I" />
+		<method name="getMonths()I" />
+		<method name="getYears()I" />
+		<method name="minus(Ljava/time/temporal/TemporalAmount;)Ljava/time/Period;" />
+		<method name="minusDays(J)Ljava/time/Period;" />
+		<method name="minusMonths(J)Ljava/time/Period;" />
+		<method name="minusYears(J)Ljava/time/Period;" />
+		<method name="multipliedBy(I)Ljava/time/Period;" />
+		<method name="negated()Ljava/time/Period;" />
+		<method name="normalized()Ljava/time/Period;" />
+		<method name="of(III)Ljava/time/Period;" />
+		<method name="ofDays(I)Ljava/time/Period;" />
+		<method name="ofMonths(I)Ljava/time/Period;" />
+		<method name="ofWeeks(I)Ljava/time/Period;" />
+		<method name="ofYears(I)Ljava/time/Period;" />
+		<method name="parse(Ljava/lang/CharSequence;)Ljava/time/Period;" />
+		<method name="plus(Ljava/time/temporal/TemporalAmount;)Ljava/time/Period;" />
+		<method name="plusDays(J)Ljava/time/Period;" />
+		<method name="plusMonths(J)Ljava/time/Period;" />
+		<method name="plusYears(J)Ljava/time/Period;" />
+		<method name="toTotalMonths()J" />
+		<method name="withDays(I)Ljava/time/Period;" />
+		<method name="withMonths(I)Ljava/time/Period;" />
+		<method name="withYears(I)Ljava/time/Period;" />
+		<field name="ZERO" />
+	</class>
+	<class name="java/time/Year" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/io/Serializable" />
+		<implements name="java/lang/Comparable" />
+		<implements name="java/time/temporal/Temporal" />
+		<implements name="java/time/temporal/TemporalAdjuster" />
+		<method name="&lt;init>()V" />
+		<method name="atDay(I)Ljava/time/LocalDate;" />
+		<method name="atMonth(I)Ljava/time/YearMonth;" />
+		<method name="atMonth(Ljava/time/Month;)Ljava/time/YearMonth;" />
+		<method name="atMonthDay(Ljava/time/MonthDay;)Ljava/time/LocalDate;" />
+		<method name="compareTo(Ljava/time/Year;)I" />
+		<method name="format(Ljava/time/format/DateTimeFormatter;)Ljava/lang/String;" />
+		<method name="from(Ljava/time/temporal/TemporalAccessor;)Ljava/time/Year;" />
+		<method name="getValue()I" />
+		<method name="isAfter(Ljava/time/Year;)Z" />
+		<method name="isBefore(Ljava/time/Year;)Z" />
+		<method name="isLeap()Z" />
+		<method name="isLeap(J)Z" />
+		<method name="isValidMonthDay(Ljava/time/MonthDay;)Z" />
+		<method name="length()I" />
+		<method name="minus(JLjava/time/temporal/TemporalUnit;)Ljava/time/Year;" />
+		<method name="minus(Ljava/time/temporal/TemporalAmount;)Ljava/time/Year;" />
+		<method name="minusYears(J)Ljava/time/Year;" />
+		<method name="now()Ljava/time/Year;" />
+		<method name="now(Ljava/time/Clock;)Ljava/time/Year;" />
+		<method name="now(Ljava/time/ZoneId;)Ljava/time/Year;" />
+		<method name="of(I)Ljava/time/Year;" />
+		<method name="parse(Ljava/lang/CharSequence;)Ljava/time/Year;" />
+		<method name="parse(Ljava/lang/CharSequence;Ljava/time/format/DateTimeFormatter;)Ljava/time/Year;" />
+		<method name="plus(JLjava/time/temporal/TemporalUnit;)Ljava/time/Year;" />
+		<method name="plus(Ljava/time/temporal/TemporalAmount;)Ljava/time/Year;" />
+		<method name="plusYears(J)Ljava/time/Year;" />
+		<method name="with(Ljava/time/temporal/TemporalAdjuster;)Ljava/time/Year;" />
+		<method name="with(Ljava/time/temporal/TemporalField;J)Ljava/time/Year;" />
+		<field name="MAX_VALUE" />
+		<field name="MIN_VALUE" />
+	</class>
+	<class name="java/time/YearMonth" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/io/Serializable" />
+		<implements name="java/lang/Comparable" />
+		<implements name="java/time/temporal/Temporal" />
+		<implements name="java/time/temporal/TemporalAdjuster" />
+		<method name="&lt;init>()V" />
+		<method name="atDay(I)Ljava/time/LocalDate;" />
+		<method name="atEndOfMonth()Ljava/time/LocalDate;" />
+		<method name="compareTo(Ljava/time/YearMonth;)I" />
+		<method name="format(Ljava/time/format/DateTimeFormatter;)Ljava/lang/String;" />
+		<method name="from(Ljava/time/temporal/TemporalAccessor;)Ljava/time/YearMonth;" />
+		<method name="getMonth()Ljava/time/Month;" />
+		<method name="getMonthValue()I" />
+		<method name="getYear()I" />
+		<method name="isAfter(Ljava/time/YearMonth;)Z" />
+		<method name="isBefore(Ljava/time/YearMonth;)Z" />
+		<method name="isLeapYear()Z" />
+		<method name="isValidDay(I)Z" />
+		<method name="lengthOfMonth()I" />
+		<method name="lengthOfYear()I" />
+		<method name="minus(JLjava/time/temporal/TemporalUnit;)Ljava/time/YearMonth;" />
+		<method name="minus(Ljava/time/temporal/TemporalAmount;)Ljava/time/YearMonth;" />
+		<method name="minusMonths(J)Ljava/time/YearMonth;" />
+		<method name="minusYears(J)Ljava/time/YearMonth;" />
+		<method name="now()Ljava/time/YearMonth;" />
+		<method name="now(Ljava/time/Clock;)Ljava/time/YearMonth;" />
+		<method name="now(Ljava/time/ZoneId;)Ljava/time/YearMonth;" />
+		<method name="of(II)Ljava/time/YearMonth;" />
+		<method name="of(ILjava/time/Month;)Ljava/time/YearMonth;" />
+		<method name="parse(Ljava/lang/CharSequence;)Ljava/time/YearMonth;" />
+		<method name="parse(Ljava/lang/CharSequence;Ljava/time/format/DateTimeFormatter;)Ljava/time/YearMonth;" />
+		<method name="plus(JLjava/time/temporal/TemporalUnit;)Ljava/time/YearMonth;" />
+		<method name="plus(Ljava/time/temporal/TemporalAmount;)Ljava/time/YearMonth;" />
+		<method name="plusMonths(J)Ljava/time/YearMonth;" />
+		<method name="plusYears(J)Ljava/time/YearMonth;" />
+		<method name="with(Ljava/time/temporal/TemporalAdjuster;)Ljava/time/YearMonth;" />
+		<method name="with(Ljava/time/temporal/TemporalField;J)Ljava/time/YearMonth;" />
+		<method name="withMonth(I)Ljava/time/YearMonth;" />
+		<method name="withYear(I)Ljava/time/YearMonth;" />
+	</class>
+	<class name="java/time/ZoneId" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/io/Serializable" />
+		<method name="&lt;init>()V" />
+		<method name="from(Ljava/time/temporal/TemporalAccessor;)Ljava/time/ZoneId;" />
+		<method name="getAvailableZoneIds()Ljava/util/Set;" />
+		<method name="getDisplayName(Ljava/time/format/TextStyle;Ljava/util/Locale;)Ljava/lang/String;" />
+		<method name="getId()Ljava/lang/String;" />
+		<method name="getRules()Ljava/time/zone/ZoneRules;" />
+		<method name="normalized()Ljava/time/ZoneId;" />
+		<method name="of(Ljava/lang/String;)Ljava/time/ZoneId;" />
+		<method name="of(Ljava/lang/String;Ljava/util/Map;)Ljava/time/ZoneId;" />
+		<method name="ofOffset(Ljava/lang/String;Ljava/time/ZoneOffset;)Ljava/time/ZoneId;" />
+		<method name="systemDefault()Ljava/time/ZoneId;" />
+		<field name="SHORT_IDS" />
+	</class>
+	<class name="java/time/ZoneOffset" since="26">
+		<extends name="java/time/ZoneId" />
+		<implements name="java/lang/Comparable" />
+		<implements name="java/time/temporal/TemporalAccessor" />
+		<implements name="java/time/temporal/TemporalAdjuster" />
+		<method name="&lt;init>()V" />
+		<method name="compareTo(Ljava/time/ZoneOffset;)I" />
+		<method name="from(Ljava/time/temporal/TemporalAccessor;)Ljava/time/ZoneOffset;" />
+		<method name="getTotalSeconds()I" />
+		<method name="of(Ljava/lang/String;)Ljava/time/ZoneOffset;" />
+		<method name="ofHours(I)Ljava/time/ZoneOffset;" />
+		<method name="ofHoursMinutes(II)Ljava/time/ZoneOffset;" />
+		<method name="ofHoursMinutesSeconds(III)Ljava/time/ZoneOffset;" />
+		<method name="ofTotalSeconds(I)Ljava/time/ZoneOffset;" />
+		<field name="MAX" />
+		<field name="MIN" />
+		<field name="UTC" />
+	</class>
+	<class name="java/time/ZonedDateTime" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/io/Serializable" />
+		<implements name="java/time/chrono/ChronoZonedDateTime" />
+		<implements name="java/time/temporal/Temporal" />
+		<method name="&lt;init>()V" />
+		<method name="from(Ljava/time/temporal/TemporalAccessor;)Ljava/time/ZonedDateTime;" />
+		<method name="getDayOfMonth()I" />
+		<method name="getDayOfWeek()Ljava/time/DayOfWeek;" />
+		<method name="getDayOfYear()I" />
+		<method name="getHour()I" />
+		<method name="getMinute()I" />
+		<method name="getMonth()Ljava/time/Month;" />
+		<method name="getMonthValue()I" />
+		<method name="getNano()I" />
+		<method name="getSecond()I" />
+		<method name="getYear()I" />
+		<method name="minus(JLjava/time/temporal/TemporalUnit;)Ljava/time/ZonedDateTime;" />
+		<method name="minus(Ljava/time/temporal/TemporalAmount;)Ljava/time/ZonedDateTime;" />
+		<method name="minusDays(J)Ljava/time/ZonedDateTime;" />
+		<method name="minusHours(J)Ljava/time/ZonedDateTime;" />
+		<method name="minusMinutes(J)Ljava/time/ZonedDateTime;" />
+		<method name="minusMonths(J)Ljava/time/ZonedDateTime;" />
+		<method name="minusNanos(J)Ljava/time/ZonedDateTime;" />
+		<method name="minusSeconds(J)Ljava/time/ZonedDateTime;" />
+		<method name="minusWeeks(J)Ljava/time/ZonedDateTime;" />
+		<method name="minusYears(J)Ljava/time/ZonedDateTime;" />
+		<method name="now()Ljava/time/ZonedDateTime;" />
+		<method name="now(Ljava/time/Clock;)Ljava/time/ZonedDateTime;" />
+		<method name="now(Ljava/time/ZoneId;)Ljava/time/ZonedDateTime;" />
+		<method name="of(IIIIIIILjava/time/ZoneId;)Ljava/time/ZonedDateTime;" />
+		<method name="of(Ljava/time/LocalDate;Ljava/time/LocalTime;Ljava/time/ZoneId;)Ljava/time/ZonedDateTime;" />
+		<method name="of(Ljava/time/LocalDateTime;Ljava/time/ZoneId;)Ljava/time/ZonedDateTime;" />
+		<method name="ofInstant(Ljava/time/Instant;Ljava/time/ZoneId;)Ljava/time/ZonedDateTime;" />
+		<method name="ofInstant(Ljava/time/LocalDateTime;Ljava/time/ZoneOffset;Ljava/time/ZoneId;)Ljava/time/ZonedDateTime;" />
+		<method name="ofLocal(Ljava/time/LocalDateTime;Ljava/time/ZoneId;Ljava/time/ZoneOffset;)Ljava/time/ZonedDateTime;" />
+		<method name="ofStrict(Ljava/time/LocalDateTime;Ljava/time/ZoneOffset;Ljava/time/ZoneId;)Ljava/time/ZonedDateTime;" />
+		<method name="parse(Ljava/lang/CharSequence;)Ljava/time/ZonedDateTime;" />
+		<method name="parse(Ljava/lang/CharSequence;Ljava/time/format/DateTimeFormatter;)Ljava/time/ZonedDateTime;" />
+		<method name="plus(JLjava/time/temporal/TemporalUnit;)Ljava/time/ZonedDateTime;" />
+		<method name="plus(Ljava/time/temporal/TemporalAmount;)Ljava/time/ZonedDateTime;" />
+		<method name="plusDays(J)Ljava/time/ZonedDateTime;" />
+		<method name="plusHours(J)Ljava/time/ZonedDateTime;" />
+		<method name="plusMinutes(J)Ljava/time/ZonedDateTime;" />
+		<method name="plusMonths(J)Ljava/time/ZonedDateTime;" />
+		<method name="plusNanos(J)Ljava/time/ZonedDateTime;" />
+		<method name="plusSeconds(J)Ljava/time/ZonedDateTime;" />
+		<method name="plusWeeks(J)Ljava/time/ZonedDateTime;" />
+		<method name="plusYears(J)Ljava/time/ZonedDateTime;" />
+		<method name="toLocalDate()Ljava/time/LocalDate;" />
+		<method name="toLocalDateTime()Ljava/time/LocalDateTime;" />
+		<method name="toOffsetDateTime()Ljava/time/OffsetDateTime;" />
+		<method name="truncatedTo(Ljava/time/temporal/TemporalUnit;)Ljava/time/ZonedDateTime;" />
+		<method name="with(Ljava/time/temporal/TemporalAdjuster;)Ljava/time/ZonedDateTime;" />
+		<method name="with(Ljava/time/temporal/TemporalField;J)Ljava/time/ZonedDateTime;" />
+		<method name="withDayOfMonth(I)Ljava/time/ZonedDateTime;" />
+		<method name="withDayOfYear(I)Ljava/time/ZonedDateTime;" />
+		<method name="withEarlierOffsetAtOverlap()Ljava/time/ZonedDateTime;" />
+		<method name="withFixedOffsetZone()Ljava/time/ZonedDateTime;" />
+		<method name="withHour(I)Ljava/time/ZonedDateTime;" />
+		<method name="withLaterOffsetAtOverlap()Ljava/time/ZonedDateTime;" />
+		<method name="withMinute(I)Ljava/time/ZonedDateTime;" />
+		<method name="withMonth(I)Ljava/time/ZonedDateTime;" />
+		<method name="withNano(I)Ljava/time/ZonedDateTime;" />
+		<method name="withSecond(I)Ljava/time/ZonedDateTime;" />
+		<method name="withYear(I)Ljava/time/ZonedDateTime;" />
+		<method name="withZoneSameInstant(Ljava/time/ZoneId;)Ljava/time/ZonedDateTime;" />
+		<method name="withZoneSameLocal(Ljava/time/ZoneId;)Ljava/time/ZonedDateTime;" />
+	</class>
+	<class name="java/time/chrono/AbstractChronology" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/time/chrono/Chronology" />
+		<method name="&lt;init>()V" />
+	</class>
+	<class name="java/time/chrono/ChronoLocalDate" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/lang/Comparable" />
+		<implements name="java/time/temporal/Temporal" />
+		<implements name="java/time/temporal/TemporalAdjuster" />
+		<method name="atTime(Ljava/time/LocalTime;)Ljava/time/chrono/ChronoLocalDateTime;" />
+		<method name="compareTo(Ljava/time/chrono/ChronoLocalDate;)I" />
+		<method name="format(Ljava/time/format/DateTimeFormatter;)Ljava/lang/String;" />
+		<method name="from(Ljava/time/temporal/TemporalAccessor;)Ljava/time/chrono/ChronoLocalDate;" />
+		<method name="getChronology()Ljava/time/chrono/Chronology;" />
+		<method name="getEra()Ljava/time/chrono/Era;" />
+		<method name="isAfter(Ljava/time/chrono/ChronoLocalDate;)Z" />
+		<method name="isBefore(Ljava/time/chrono/ChronoLocalDate;)Z" />
+		<method name="isEqual(Ljava/time/chrono/ChronoLocalDate;)Z" />
+		<method name="isLeapYear()Z" />
+		<method name="lengthOfMonth()I" />
+		<method name="lengthOfYear()I" />
+		<method name="minus(JLjava/time/temporal/TemporalUnit;)Ljava/time/chrono/ChronoLocalDate;" />
+		<method name="minus(Ljava/time/temporal/TemporalAmount;)Ljava/time/chrono/ChronoLocalDate;" />
+		<method name="plus(JLjava/time/temporal/TemporalUnit;)Ljava/time/chrono/ChronoLocalDate;" />
+		<method name="plus(Ljava/time/temporal/TemporalAmount;)Ljava/time/chrono/ChronoLocalDate;" />
+		<method name="timeLineOrder()Ljava/util/Comparator;" />
+		<method name="toEpochDay()J" />
+		<method name="until(Ljava/time/chrono/ChronoLocalDate;)Ljava/time/chrono/ChronoPeriod;" />
+		<method name="with(Ljava/time/temporal/TemporalAdjuster;)Ljava/time/chrono/ChronoLocalDate;" />
+		<method name="with(Ljava/time/temporal/TemporalField;J)Ljava/time/chrono/ChronoLocalDate;" />
+	</class>
+	<class name="java/time/chrono/ChronoLocalDateImpl" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/io/Serializable" />
+		<implements name="java/time/chrono/ChronoLocalDate" />
+		<implements name="java/time/temporal/Temporal" />
+		<implements name="java/time/temporal/TemporalAdjuster" />
+		<method name="&lt;init>()V" />
+	</class>
+	<class name="java/time/chrono/ChronoLocalDateTime" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/lang/Comparable" />
+		<implements name="java/time/temporal/Temporal" />
+		<implements name="java/time/temporal/TemporalAdjuster" />
+		<method name="atZone(Ljava/time/ZoneId;)Ljava/time/chrono/ChronoZonedDateTime;" />
+		<method name="compareTo(Ljava/time/chrono/ChronoLocalDateTime;)I" />
+		<method name="format(Ljava/time/format/DateTimeFormatter;)Ljava/lang/String;" />
+		<method name="from(Ljava/time/temporal/TemporalAccessor;)Ljava/time/chrono/ChronoLocalDateTime;" />
+		<method name="getChronology()Ljava/time/chrono/Chronology;" />
+		<method name="isAfter(Ljava/time/chrono/ChronoLocalDateTime;)Z" />
+		<method name="isBefore(Ljava/time/chrono/ChronoLocalDateTime;)Z" />
+		<method name="isEqual(Ljava/time/chrono/ChronoLocalDateTime;)Z" />
+		<method name="minus(JLjava/time/temporal/TemporalUnit;)Ljava/time/chrono/ChronoLocalDateTime;" />
+		<method name="minus(Ljava/time/temporal/TemporalAmount;)Ljava/time/chrono/ChronoLocalDateTime;" />
+		<method name="plus(JLjava/time/temporal/TemporalUnit;)Ljava/time/chrono/ChronoLocalDateTime;" />
+		<method name="plus(Ljava/time/temporal/TemporalAmount;)Ljava/time/chrono/ChronoLocalDateTime;" />
+		<method name="timeLineOrder()Ljava/util/Comparator;" />
+		<method name="toEpochSecond(Ljava/time/ZoneOffset;)J" />
+		<method name="toInstant(Ljava/time/ZoneOffset;)Ljava/time/Instant;" />
+		<method name="toLocalDate()Ljava/time/chrono/ChronoLocalDate;" />
+		<method name="toLocalTime()Ljava/time/LocalTime;" />
+		<method name="with(Ljava/time/temporal/TemporalAdjuster;)Ljava/time/chrono/ChronoLocalDateTime;" />
+		<method name="with(Ljava/time/temporal/TemporalField;J)Ljava/time/chrono/ChronoLocalDateTime;" />
+	</class>
+	<class name="java/time/chrono/ChronoPeriod" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/time/temporal/TemporalAmount" />
+		<method name="between(Ljava/time/chrono/ChronoLocalDate;Ljava/time/chrono/ChronoLocalDate;)Ljava/time/chrono/ChronoPeriod;" />
+		<method name="getChronology()Ljava/time/chrono/Chronology;" />
+		<method name="isNegative()Z" />
+		<method name="isZero()Z" />
+		<method name="minus(Ljava/time/temporal/TemporalAmount;)Ljava/time/chrono/ChronoPeriod;" />
+		<method name="multipliedBy(I)Ljava/time/chrono/ChronoPeriod;" />
+		<method name="negated()Ljava/time/chrono/ChronoPeriod;" />
+		<method name="normalized()Ljava/time/chrono/ChronoPeriod;" />
+		<method name="plus(Ljava/time/temporal/TemporalAmount;)Ljava/time/chrono/ChronoPeriod;" />
+	</class>
+	<class name="java/time/chrono/ChronoZonedDateTime" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/lang/Comparable" />
+		<implements name="java/time/temporal/Temporal" />
+		<method name="compareTo(Ljava/time/chrono/ChronoZonedDateTime;)I" />
+		<method name="format(Ljava/time/format/DateTimeFormatter;)Ljava/lang/String;" />
+		<method name="from(Ljava/time/temporal/TemporalAccessor;)Ljava/time/chrono/ChronoZonedDateTime;" />
+		<method name="getChronology()Ljava/time/chrono/Chronology;" />
+		<method name="getOffset()Ljava/time/ZoneOffset;" />
+		<method name="getZone()Ljava/time/ZoneId;" />
+		<method name="isAfter(Ljava/time/chrono/ChronoZonedDateTime;)Z" />
+		<method name="isBefore(Ljava/time/chrono/ChronoZonedDateTime;)Z" />
+		<method name="isEqual(Ljava/time/chrono/ChronoZonedDateTime;)Z" />
+		<method name="minus(JLjava/time/temporal/TemporalUnit;)Ljava/time/chrono/ChronoZonedDateTime;" />
+		<method name="minus(Ljava/time/temporal/TemporalAmount;)Ljava/time/chrono/ChronoZonedDateTime;" />
+		<method name="plus(JLjava/time/temporal/TemporalUnit;)Ljava/time/chrono/ChronoZonedDateTime;" />
+		<method name="plus(Ljava/time/temporal/TemporalAmount;)Ljava/time/chrono/ChronoZonedDateTime;" />
+		<method name="timeLineOrder()Ljava/util/Comparator;" />
+		<method name="toEpochSecond()J" />
+		<method name="toInstant()Ljava/time/Instant;" />
+		<method name="toLocalDate()Ljava/time/chrono/ChronoLocalDate;" />
+		<method name="toLocalDateTime()Ljava/time/chrono/ChronoLocalDateTime;" />
+		<method name="toLocalTime()Ljava/time/LocalTime;" />
+		<method name="with(Ljava/time/temporal/TemporalAdjuster;)Ljava/time/chrono/ChronoZonedDateTime;" />
+		<method name="with(Ljava/time/temporal/TemporalField;J)Ljava/time/chrono/ChronoZonedDateTime;" />
+		<method name="withEarlierOffsetAtOverlap()Ljava/time/chrono/ChronoZonedDateTime;" />
+		<method name="withLaterOffsetAtOverlap()Ljava/time/chrono/ChronoZonedDateTime;" />
+		<method name="withZoneSameInstant(Ljava/time/ZoneId;)Ljava/time/chrono/ChronoZonedDateTime;" />
+		<method name="withZoneSameLocal(Ljava/time/ZoneId;)Ljava/time/chrono/ChronoZonedDateTime;" />
+	</class>
+	<class name="java/time/chrono/Chronology" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/lang/Comparable" />
+		<method name="compareTo(Ljava/time/chrono/Chronology;)I" />
+		<method name="date(III)Ljava/time/chrono/ChronoLocalDate;" />
+		<method name="date(Ljava/time/chrono/Era;III)Ljava/time/chrono/ChronoLocalDate;" />
+		<method name="date(Ljava/time/temporal/TemporalAccessor;)Ljava/time/chrono/ChronoLocalDate;" />
+		<method name="dateEpochDay(J)Ljava/time/chrono/ChronoLocalDate;" />
+		<method name="dateNow()Ljava/time/chrono/ChronoLocalDate;" />
+		<method name="dateNow(Ljava/time/Clock;)Ljava/time/chrono/ChronoLocalDate;" />
+		<method name="dateNow(Ljava/time/ZoneId;)Ljava/time/chrono/ChronoLocalDate;" />
+		<method name="dateYearDay(II)Ljava/time/chrono/ChronoLocalDate;" />
+		<method name="dateYearDay(Ljava/time/chrono/Era;II)Ljava/time/chrono/ChronoLocalDate;" />
+		<method name="eraOf(I)Ljava/time/chrono/Era;" />
+		<method name="eras()Ljava/util/List;" />
+		<method name="from(Ljava/time/temporal/TemporalAccessor;)Ljava/time/chrono/Chronology;" />
+		<method name="getAvailableChronologies()Ljava/util/Set;" />
+		<method name="getCalendarType()Ljava/lang/String;" />
+		<method name="getDisplayName(Ljava/time/format/TextStyle;Ljava/util/Locale;)Ljava/lang/String;" />
+		<method name="getId()Ljava/lang/String;" />
+		<method name="isLeapYear(J)Z" />
+		<method name="localDateTime(Ljava/time/temporal/TemporalAccessor;)Ljava/time/chrono/ChronoLocalDateTime;" />
+		<method name="of(Ljava/lang/String;)Ljava/time/chrono/Chronology;" />
+		<method name="ofLocale(Ljava/util/Locale;)Ljava/time/chrono/Chronology;" />
+		<method name="period(III)Ljava/time/chrono/ChronoPeriod;" />
+		<method name="prolepticYear(Ljava/time/chrono/Era;I)I" />
+		<method name="range(Ljava/time/temporal/ChronoField;)Ljava/time/temporal/ValueRange;" />
+		<method name="resolveDate(Ljava/util/Map;Ljava/time/format/ResolverStyle;)Ljava/time/chrono/ChronoLocalDate;" />
+		<method name="zonedDateTime(Ljava/time/Instant;Ljava/time/ZoneId;)Ljava/time/chrono/ChronoZonedDateTime;" />
+		<method name="zonedDateTime(Ljava/time/temporal/TemporalAccessor;)Ljava/time/chrono/ChronoZonedDateTime;" />
+	</class>
+	<class name="java/time/chrono/Era" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/time/temporal/TemporalAccessor" />
+		<implements name="java/time/temporal/TemporalAdjuster" />
+		<method name="getDisplayName(Ljava/time/format/TextStyle;Ljava/util/Locale;)Ljava/lang/String;" />
+		<method name="getValue()I" />
+	</class>
+	<class name="java/time/chrono/HijrahChronology" since="26">
+		<extends name="java/time/chrono/AbstractChronology" />
+		<implements name="java/io/Serializable" />
+		<method name="&lt;init>()V" />
+		<method name="date(III)Ljava/time/chrono/HijrahDate;" />
+		<method name="date(Ljava/time/chrono/Era;III)Ljava/time/chrono/HijrahDate;" />
+		<method name="date(Ljava/time/temporal/TemporalAccessor;)Ljava/time/chrono/HijrahDate;" />
+		<method name="dateEpochDay(J)Ljava/time/chrono/HijrahDate;" />
+		<method name="dateNow()Ljava/time/chrono/HijrahDate;" />
+		<method name="dateNow(Ljava/time/Clock;)Ljava/time/chrono/HijrahDate;" />
+		<method name="dateNow(Ljava/time/ZoneId;)Ljava/time/chrono/HijrahDate;" />
+		<method name="dateYearDay(II)Ljava/time/chrono/HijrahDate;" />
+		<method name="dateYearDay(Ljava/time/chrono/Era;II)Ljava/time/chrono/HijrahDate;" />
+		<method name="eraOf(I)Ljava/time/chrono/HijrahEra;" />
+		<method name="resolveDate(Ljava/util/Map;Ljava/time/format/ResolverStyle;)Ljava/time/chrono/HijrahDate;" />
+		<field name="INSTANCE" />
+	</class>
+	<class name="java/time/chrono/HijrahDate" since="26">
+		<extends name="java/time/chrono/ChronoLocalDateImpl" />
+		<method name="&lt;init>()V" />
+		<method name="from(Ljava/time/temporal/TemporalAccessor;)Ljava/time/chrono/HijrahDate;" />
+		<method name="getChronology()Ljava/time/chrono/HijrahChronology;" />
+		<method name="getEra()Ljava/time/chrono/HijrahEra;" />
+		<method name="minus(JLjava/time/temporal/TemporalUnit;)Ljava/time/chrono/HijrahDate;" />
+		<method name="minus(Ljava/time/temporal/TemporalAmount;)Ljava/time/chrono/HijrahDate;" />
+		<method name="now()Ljava/time/chrono/HijrahDate;" />
+		<method name="now(Ljava/time/Clock;)Ljava/time/chrono/HijrahDate;" />
+		<method name="now(Ljava/time/ZoneId;)Ljava/time/chrono/HijrahDate;" />
+		<method name="of(III)Ljava/time/chrono/HijrahDate;" />
+		<method name="plus(JLjava/time/temporal/TemporalUnit;)Ljava/time/chrono/HijrahDate;" />
+		<method name="plus(Ljava/time/temporal/TemporalAmount;)Ljava/time/chrono/HijrahDate;" />
+		<method name="with(Ljava/time/temporal/TemporalAdjuster;)Ljava/time/chrono/HijrahDate;" />
+		<method name="with(Ljava/time/temporal/TemporalField;J)Ljava/time/chrono/HijrahDate;" />
+		<method name="withVariant(Ljava/time/chrono/HijrahChronology;)Ljava/time/chrono/HijrahDate;" />
+	</class>
+	<class name="java/time/chrono/HijrahEra" since="26">
+		<extends name="java/lang/Enum" />
+		<implements name="java/time/chrono/Era" />
+		<method name="of(I)Ljava/time/chrono/HijrahEra;" />
+		<method name="valueOf(Ljava/lang/String;)Ljava/time/chrono/HijrahEra;" />
+		<method name="values()[Ljava/time/chrono/HijrahEra;" />
+		<field name="AH" />
+	</class>
+	<class name="java/time/chrono/IsoChronology" since="26">
+		<extends name="java/time/chrono/AbstractChronology" />
+		<implements name="java/io/Serializable" />
+		<method name="&lt;init>()V" />
+		<method name="date(III)Ljava/time/LocalDate;" />
+		<method name="date(Ljava/time/chrono/Era;III)Ljava/time/LocalDate;" />
+		<method name="date(Ljava/time/temporal/TemporalAccessor;)Ljava/time/LocalDate;" />
+		<method name="dateEpochDay(J)Ljava/time/LocalDate;" />
+		<method name="dateNow()Ljava/time/LocalDate;" />
+		<method name="dateNow(Ljava/time/Clock;)Ljava/time/LocalDate;" />
+		<method name="dateNow(Ljava/time/ZoneId;)Ljava/time/LocalDate;" />
+		<method name="dateYearDay(II)Ljava/time/LocalDate;" />
+		<method name="dateYearDay(Ljava/time/chrono/Era;II)Ljava/time/LocalDate;" />
+		<method name="eraOf(I)Ljava/time/chrono/IsoEra;" />
+		<method name="localDateTime(Ljava/time/temporal/TemporalAccessor;)Ljava/time/LocalDateTime;" />
+		<method name="period(III)Ljava/time/Period;" />
+		<method name="resolveDate(Ljava/util/Map;Ljava/time/format/ResolverStyle;)Ljava/time/LocalDate;" />
+		<method name="zonedDateTime(Ljava/time/Instant;Ljava/time/ZoneId;)Ljava/time/ZonedDateTime;" />
+		<method name="zonedDateTime(Ljava/time/temporal/TemporalAccessor;)Ljava/time/ZonedDateTime;" />
+		<field name="INSTANCE" />
+	</class>
+	<class name="java/time/chrono/IsoEra" since="26">
+		<extends name="java/lang/Enum" />
+		<implements name="java/time/chrono/Era" />
+		<method name="of(I)Ljava/time/chrono/IsoEra;" />
+		<method name="valueOf(Ljava/lang/String;)Ljava/time/chrono/IsoEra;" />
+		<method name="values()[Ljava/time/chrono/IsoEra;" />
+		<field name="BCE" />
+		<field name="CE" />
+	</class>
+	<class name="java/time/chrono/JapaneseChronology" since="26">
+		<extends name="java/time/chrono/AbstractChronology" />
+		<implements name="java/io/Serializable" />
+		<method name="&lt;init>()V" />
+		<method name="date(III)Ljava/time/chrono/JapaneseDate;" />
+		<method name="date(Ljava/time/chrono/Era;III)Ljava/time/chrono/JapaneseDate;" />
+		<method name="date(Ljava/time/temporal/TemporalAccessor;)Ljava/time/chrono/JapaneseDate;" />
+		<method name="dateEpochDay(J)Ljava/time/chrono/JapaneseDate;" />
+		<method name="dateNow()Ljava/time/chrono/JapaneseDate;" />
+		<method name="dateNow(Ljava/time/Clock;)Ljava/time/chrono/JapaneseDate;" />
+		<method name="dateNow(Ljava/time/ZoneId;)Ljava/time/chrono/JapaneseDate;" />
+		<method name="dateYearDay(II)Ljava/time/chrono/JapaneseDate;" />
+		<method name="dateYearDay(Ljava/time/chrono/Era;II)Ljava/time/chrono/JapaneseDate;" />
+		<method name="eraOf(I)Ljava/time/chrono/JapaneseEra;" />
+		<method name="resolveDate(Ljava/util/Map;Ljava/time/format/ResolverStyle;)Ljava/time/chrono/JapaneseDate;" />
+		<field name="INSTANCE" />
+	</class>
+	<class name="java/time/chrono/JapaneseDate" since="26">
+		<extends name="java/time/chrono/ChronoLocalDateImpl" />
+		<method name="&lt;init>()V" />
+		<method name="from(Ljava/time/temporal/TemporalAccessor;)Ljava/time/chrono/JapaneseDate;" />
+		<method name="getChronology()Ljava/time/chrono/JapaneseChronology;" />
+		<method name="getEra()Ljava/time/chrono/JapaneseEra;" />
+		<method name="minus(JLjava/time/temporal/TemporalUnit;)Ljava/time/chrono/JapaneseDate;" />
+		<method name="minus(Ljava/time/temporal/TemporalAmount;)Ljava/time/chrono/JapaneseDate;" />
+		<method name="now()Ljava/time/chrono/JapaneseDate;" />
+		<method name="now(Ljava/time/Clock;)Ljava/time/chrono/JapaneseDate;" />
+		<method name="now(Ljava/time/ZoneId;)Ljava/time/chrono/JapaneseDate;" />
+		<method name="of(III)Ljava/time/chrono/JapaneseDate;" />
+		<method name="of(Ljava/time/chrono/JapaneseEra;III)Ljava/time/chrono/JapaneseDate;" />
+		<method name="plus(JLjava/time/temporal/TemporalUnit;)Ljava/time/chrono/JapaneseDate;" />
+		<method name="plus(Ljava/time/temporal/TemporalAmount;)Ljava/time/chrono/JapaneseDate;" />
+		<method name="with(Ljava/time/temporal/TemporalAdjuster;)Ljava/time/chrono/JapaneseDate;" />
+		<method name="with(Ljava/time/temporal/TemporalField;J)Ljava/time/chrono/JapaneseDate;" />
+	</class>
+	<class name="java/time/chrono/JapaneseEra" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/io/Serializable" />
+		<implements name="java/time/chrono/Era" />
+		<method name="&lt;init>()V" />
+		<method name="of(I)Ljava/time/chrono/JapaneseEra;" />
+		<method name="valueOf(Ljava/lang/String;)Ljava/time/chrono/JapaneseEra;" />
+		<method name="values()[Ljava/time/chrono/JapaneseEra;" />
+		<field name="HEISEI" />
+		<field name="MEIJI" />
+		<field name="SHOWA" />
+		<field name="TAISHO" />
+	</class>
+	<class name="java/time/chrono/MinguoChronology" since="26">
+		<extends name="java/time/chrono/AbstractChronology" />
+		<implements name="java/io/Serializable" />
+		<method name="&lt;init>()V" />
+		<method name="date(III)Ljava/time/chrono/MinguoDate;" />
+		<method name="date(Ljava/time/chrono/Era;III)Ljava/time/chrono/MinguoDate;" />
+		<method name="date(Ljava/time/temporal/TemporalAccessor;)Ljava/time/chrono/MinguoDate;" />
+		<method name="dateEpochDay(J)Ljava/time/chrono/MinguoDate;" />
+		<method name="dateNow()Ljava/time/chrono/MinguoDate;" />
+		<method name="dateNow(Ljava/time/Clock;)Ljava/time/chrono/MinguoDate;" />
+		<method name="dateNow(Ljava/time/ZoneId;)Ljava/time/chrono/MinguoDate;" />
+		<method name="dateYearDay(II)Ljava/time/chrono/MinguoDate;" />
+		<method name="dateYearDay(Ljava/time/chrono/Era;II)Ljava/time/chrono/MinguoDate;" />
+		<method name="eraOf(I)Ljava/time/chrono/MinguoEra;" />
+		<method name="resolveDate(Ljava/util/Map;Ljava/time/format/ResolverStyle;)Ljava/time/chrono/MinguoDate;" />
+		<field name="INSTANCE" />
+	</class>
+	<class name="java/time/chrono/MinguoDate" since="26">
+		<extends name="java/time/chrono/ChronoLocalDateImpl" />
+		<method name="&lt;init>()V" />
+		<method name="from(Ljava/time/temporal/TemporalAccessor;)Ljava/time/chrono/MinguoDate;" />
+		<method name="getChronology()Ljava/time/chrono/MinguoChronology;" />
+		<method name="getEra()Ljava/time/chrono/MinguoEra;" />
+		<method name="minus(JLjava/time/temporal/TemporalUnit;)Ljava/time/chrono/MinguoDate;" />
+		<method name="minus(Ljava/time/temporal/TemporalAmount;)Ljava/time/chrono/MinguoDate;" />
+		<method name="now()Ljava/time/chrono/MinguoDate;" />
+		<method name="now(Ljava/time/Clock;)Ljava/time/chrono/MinguoDate;" />
+		<method name="now(Ljava/time/ZoneId;)Ljava/time/chrono/MinguoDate;" />
+		<method name="of(III)Ljava/time/chrono/MinguoDate;" />
+		<method name="plus(JLjava/time/temporal/TemporalUnit;)Ljava/time/chrono/MinguoDate;" />
+		<method name="plus(Ljava/time/temporal/TemporalAmount;)Ljava/time/chrono/MinguoDate;" />
+		<method name="with(Ljava/time/temporal/TemporalAdjuster;)Ljava/time/chrono/MinguoDate;" />
+		<method name="with(Ljava/time/temporal/TemporalField;J)Ljava/time/chrono/MinguoDate;" />
+	</class>
+	<class name="java/time/chrono/MinguoEra" since="26">
+		<extends name="java/lang/Enum" />
+		<implements name="java/time/chrono/Era" />
+		<method name="of(I)Ljava/time/chrono/MinguoEra;" />
+		<method name="valueOf(Ljava/lang/String;)Ljava/time/chrono/MinguoEra;" />
+		<method name="values()[Ljava/time/chrono/MinguoEra;" />
+		<field name="BEFORE_ROC" />
+		<field name="ROC" />
+	</class>
+	<class name="java/time/chrono/ThaiBuddhistChronology" since="26">
+		<extends name="java/time/chrono/AbstractChronology" />
+		<implements name="java/io/Serializable" />
+		<method name="&lt;init>()V" />
+		<method name="date(III)Ljava/time/chrono/ThaiBuddhistDate;" />
+		<method name="date(Ljava/time/chrono/Era;III)Ljava/time/chrono/ThaiBuddhistDate;" />
+		<method name="date(Ljava/time/temporal/TemporalAccessor;)Ljava/time/chrono/ThaiBuddhistDate;" />
+		<method name="dateEpochDay(J)Ljava/time/chrono/ThaiBuddhistDate;" />
+		<method name="dateNow()Ljava/time/chrono/ThaiBuddhistDate;" />
+		<method name="dateNow(Ljava/time/Clock;)Ljava/time/chrono/ThaiBuddhistDate;" />
+		<method name="dateNow(Ljava/time/ZoneId;)Ljava/time/chrono/ThaiBuddhistDate;" />
+		<method name="dateYearDay(II)Ljava/time/chrono/ThaiBuddhistDate;" />
+		<method name="dateYearDay(Ljava/time/chrono/Era;II)Ljava/time/chrono/ThaiBuddhistDate;" />
+		<method name="eraOf(I)Ljava/time/chrono/ThaiBuddhistEra;" />
+		<method name="resolveDate(Ljava/util/Map;Ljava/time/format/ResolverStyle;)Ljava/time/chrono/ThaiBuddhistDate;" />
+		<field name="INSTANCE" />
+	</class>
+	<class name="java/time/chrono/ThaiBuddhistDate" since="26">
+		<extends name="java/time/chrono/ChronoLocalDateImpl" />
+		<method name="&lt;init>()V" />
+		<method name="from(Ljava/time/temporal/TemporalAccessor;)Ljava/time/chrono/ThaiBuddhistDate;" />
+		<method name="getChronology()Ljava/time/chrono/ThaiBuddhistChronology;" />
+		<method name="getEra()Ljava/time/chrono/ThaiBuddhistEra;" />
+		<method name="minus(JLjava/time/temporal/TemporalUnit;)Ljava/time/chrono/ThaiBuddhistDate;" />
+		<method name="minus(Ljava/time/temporal/TemporalAmount;)Ljava/time/chrono/ThaiBuddhistDate;" />
+		<method name="now()Ljava/time/chrono/ThaiBuddhistDate;" />
+		<method name="now(Ljava/time/Clock;)Ljava/time/chrono/ThaiBuddhistDate;" />
+		<method name="now(Ljava/time/ZoneId;)Ljava/time/chrono/ThaiBuddhistDate;" />
+		<method name="of(III)Ljava/time/chrono/ThaiBuddhistDate;" />
+		<method name="plus(JLjava/time/temporal/TemporalUnit;)Ljava/time/chrono/ThaiBuddhistDate;" />
+		<method name="plus(Ljava/time/temporal/TemporalAmount;)Ljava/time/chrono/ThaiBuddhistDate;" />
+		<method name="with(Ljava/time/temporal/TemporalAdjuster;)Ljava/time/chrono/ThaiBuddhistDate;" />
+		<method name="with(Ljava/time/temporal/TemporalField;J)Ljava/time/chrono/ThaiBuddhistDate;" />
+	</class>
+	<class name="java/time/chrono/ThaiBuddhistEra" since="26">
+		<extends name="java/lang/Enum" />
+		<implements name="java/time/chrono/Era" />
+		<method name="of(I)Ljava/time/chrono/ThaiBuddhistEra;" />
+		<method name="valueOf(Ljava/lang/String;)Ljava/time/chrono/ThaiBuddhistEra;" />
+		<method name="values()[Ljava/time/chrono/ThaiBuddhistEra;" />
+		<field name="BE" />
+		<field name="BEFORE_BE" />
+	</class>
+	<class name="java/time/format/DateTimeFormatter" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="format(Ljava/time/temporal/TemporalAccessor;)Ljava/lang/String;" />
+		<method name="formatTo(Ljava/time/temporal/TemporalAccessor;Ljava/lang/Appendable;)V" />
+		<method name="getChronology()Ljava/time/chrono/Chronology;" />
+		<method name="getDecimalStyle()Ljava/time/format/DecimalStyle;" />
+		<method name="getLocale()Ljava/util/Locale;" />
+		<method name="getResolverFields()Ljava/util/Set;" />
+		<method name="getResolverStyle()Ljava/time/format/ResolverStyle;" />
+		<method name="getZone()Ljava/time/ZoneId;" />
+		<method name="ofLocalizedDate(Ljava/time/format/FormatStyle;)Ljava/time/format/DateTimeFormatter;" />
+		<method name="ofLocalizedDateTime(Ljava/time/format/FormatStyle;)Ljava/time/format/DateTimeFormatter;" />
+		<method name="ofLocalizedDateTime(Ljava/time/format/FormatStyle;Ljava/time/format/FormatStyle;)Ljava/time/format/DateTimeFormatter;" />
+		<method name="ofLocalizedTime(Ljava/time/format/FormatStyle;)Ljava/time/format/DateTimeFormatter;" />
+		<method name="ofPattern(Ljava/lang/String;)Ljava/time/format/DateTimeFormatter;" />
+		<method name="ofPattern(Ljava/lang/String;Ljava/util/Locale;)Ljava/time/format/DateTimeFormatter;" />
+		<method name="parse(Ljava/lang/CharSequence;)Ljava/time/temporal/TemporalAccessor;" />
+		<method name="parse(Ljava/lang/CharSequence;Ljava/text/ParsePosition;)Ljava/time/temporal/TemporalAccessor;" />
+		<method name="parse(Ljava/lang/CharSequence;Ljava/time/temporal/TemporalQuery;)Ljava/lang/Object;" />
+		<method name="parseBest(Ljava/lang/CharSequence;[Ljava/time/temporal/TemporalQuery;)Ljava/time/temporal/TemporalAccessor;" />
+		<method name="parseUnresolved(Ljava/lang/CharSequence;Ljava/text/ParsePosition;)Ljava/time/temporal/TemporalAccessor;" />
+		<method name="parsedExcessDays()Ljava/time/temporal/TemporalQuery;" />
+		<method name="parsedLeapSecond()Ljava/time/temporal/TemporalQuery;" />
+		<method name="toFormat()Ljava/text/Format;" />
+		<method name="toFormat(Ljava/time/temporal/TemporalQuery;)Ljava/text/Format;" />
+		<method name="withChronology(Ljava/time/chrono/Chronology;)Ljava/time/format/DateTimeFormatter;" />
+		<method name="withDecimalStyle(Ljava/time/format/DecimalStyle;)Ljava/time/format/DateTimeFormatter;" />
+		<method name="withLocale(Ljava/util/Locale;)Ljava/time/format/DateTimeFormatter;" />
+		<method name="withResolverFields(Ljava/util/Set;)Ljava/time/format/DateTimeFormatter;" />
+		<method name="withResolverFields([Ljava/time/temporal/TemporalField;)Ljava/time/format/DateTimeFormatter;" />
+		<method name="withResolverStyle(Ljava/time/format/ResolverStyle;)Ljava/time/format/DateTimeFormatter;" />
+		<method name="withZone(Ljava/time/ZoneId;)Ljava/time/format/DateTimeFormatter;" />
+		<field name="BASIC_ISO_DATE" />
+		<field name="ISO_DATE" />
+		<field name="ISO_DATE_TIME" />
+		<field name="ISO_INSTANT" />
+		<field name="ISO_LOCAL_DATE" />
+		<field name="ISO_LOCAL_DATE_TIME" />
+		<field name="ISO_LOCAL_TIME" />
+		<field name="ISO_OFFSET_DATE" />
+		<field name="ISO_OFFSET_DATE_TIME" />
+		<field name="ISO_OFFSET_TIME" />
+		<field name="ISO_ORDINAL_DATE" />
+		<field name="ISO_TIME" />
+		<field name="ISO_WEEK_DATE" />
+		<field name="ISO_ZONED_DATE_TIME" />
+		<field name="RFC_1123_DATE_TIME" />
+	</class>
+	<class name="java/time/format/DateTimeFormatterBuilder" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="append(Ljava/time/format/DateTimeFormatter;)Ljava/time/format/DateTimeFormatterBuilder;" />
+		<method name="appendChronologyId()Ljava/time/format/DateTimeFormatterBuilder;" />
+		<method name="appendChronologyText(Ljava/time/format/TextStyle;)Ljava/time/format/DateTimeFormatterBuilder;" />
+		<method name="appendFraction(Ljava/time/temporal/TemporalField;IIZ)Ljava/time/format/DateTimeFormatterBuilder;" />
+		<method name="appendInstant()Ljava/time/format/DateTimeFormatterBuilder;" />
+		<method name="appendInstant(I)Ljava/time/format/DateTimeFormatterBuilder;" />
+		<method name="appendLiteral(C)Ljava/time/format/DateTimeFormatterBuilder;" />
+		<method name="appendLiteral(Ljava/lang/String;)Ljava/time/format/DateTimeFormatterBuilder;" />
+		<method name="appendLocalized(Ljava/time/format/FormatStyle;Ljava/time/format/FormatStyle;)Ljava/time/format/DateTimeFormatterBuilder;" />
+		<method name="appendLocalizedOffset(Ljava/time/format/TextStyle;)Ljava/time/format/DateTimeFormatterBuilder;" />
+		<method name="appendOffset(Ljava/lang/String;Ljava/lang/String;)Ljava/time/format/DateTimeFormatterBuilder;" />
+		<method name="appendOffsetId()Ljava/time/format/DateTimeFormatterBuilder;" />
+		<method name="appendOptional(Ljava/time/format/DateTimeFormatter;)Ljava/time/format/DateTimeFormatterBuilder;" />
+		<method name="appendPattern(Ljava/lang/String;)Ljava/time/format/DateTimeFormatterBuilder;" />
+		<method name="appendText(Ljava/time/temporal/TemporalField;)Ljava/time/format/DateTimeFormatterBuilder;" />
+		<method name="appendText(Ljava/time/temporal/TemporalField;Ljava/time/format/TextStyle;)Ljava/time/format/DateTimeFormatterBuilder;" />
+		<method name="appendText(Ljava/time/temporal/TemporalField;Ljava/util/Map;)Ljava/time/format/DateTimeFormatterBuilder;" />
+		<method name="appendValue(Ljava/time/temporal/TemporalField;)Ljava/time/format/DateTimeFormatterBuilder;" />
+		<method name="appendValue(Ljava/time/temporal/TemporalField;I)Ljava/time/format/DateTimeFormatterBuilder;" />
+		<method name="appendValue(Ljava/time/temporal/TemporalField;IILjava/time/format/SignStyle;)Ljava/time/format/DateTimeFormatterBuilder;" />
+		<method name="appendValueReduced(Ljava/time/temporal/TemporalField;III)Ljava/time/format/DateTimeFormatterBuilder;" />
+		<method name="appendValueReduced(Ljava/time/temporal/TemporalField;IILjava/time/chrono/ChronoLocalDate;)Ljava/time/format/DateTimeFormatterBuilder;" />
+		<method name="appendZoneId()Ljava/time/format/DateTimeFormatterBuilder;" />
+		<method name="appendZoneOrOffsetId()Ljava/time/format/DateTimeFormatterBuilder;" />
+		<method name="appendZoneRegionId()Ljava/time/format/DateTimeFormatterBuilder;" />
+		<method name="appendZoneText(Ljava/time/format/TextStyle;)Ljava/time/format/DateTimeFormatterBuilder;" />
+		<method name="appendZoneText(Ljava/time/format/TextStyle;Ljava/util/Set;)Ljava/time/format/DateTimeFormatterBuilder;" />
+		<method name="getLocalizedDateTimePattern(Ljava/time/format/FormatStyle;Ljava/time/format/FormatStyle;Ljava/time/chrono/Chronology;Ljava/util/Locale;)Ljava/lang/String;" />
+		<method name="optionalEnd()Ljava/time/format/DateTimeFormatterBuilder;" />
+		<method name="optionalStart()Ljava/time/format/DateTimeFormatterBuilder;" />
+		<method name="padNext(I)Ljava/time/format/DateTimeFormatterBuilder;" />
+		<method name="padNext(IC)Ljava/time/format/DateTimeFormatterBuilder;" />
+		<method name="parseCaseInsensitive()Ljava/time/format/DateTimeFormatterBuilder;" />
+		<method name="parseCaseSensitive()Ljava/time/format/DateTimeFormatterBuilder;" />
+		<method name="parseDefaulting(Ljava/time/temporal/TemporalField;J)Ljava/time/format/DateTimeFormatterBuilder;" />
+		<method name="parseLenient()Ljava/time/format/DateTimeFormatterBuilder;" />
+		<method name="parseStrict()Ljava/time/format/DateTimeFormatterBuilder;" />
+		<method name="toFormatter()Ljava/time/format/DateTimeFormatter;" />
+		<method name="toFormatter(Ljava/util/Locale;)Ljava/time/format/DateTimeFormatter;" />
+	</class>
+	<class name="java/time/format/DateTimeParseException" since="26">
+		<extends name="java/time/DateTimeException" />
+		<method name="&lt;init>(Ljava/lang/String;Ljava/lang/CharSequence;I)V" />
+		<method name="&lt;init>(Ljava/lang/String;Ljava/lang/CharSequence;ILjava/lang/Throwable;)V" />
+		<method name="getErrorIndex()I" />
+		<method name="getParsedString()Ljava/lang/String;" />
+	</class>
+	<class name="java/time/format/DecimalStyle" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="getAvailableLocales()Ljava/util/Set;" />
+		<method name="getDecimalSeparator()C" />
+		<method name="getNegativeSign()C" />
+		<method name="getPositiveSign()C" />
+		<method name="getZeroDigit()C" />
+		<method name="of(Ljava/util/Locale;)Ljava/time/format/DecimalStyle;" />
+		<method name="ofDefaultLocale()Ljava/time/format/DecimalStyle;" />
+		<method name="withDecimalSeparator(C)Ljava/time/format/DecimalStyle;" />
+		<method name="withNegativeSign(C)Ljava/time/format/DecimalStyle;" />
+		<method name="withPositiveSign(C)Ljava/time/format/DecimalStyle;" />
+		<method name="withZeroDigit(C)Ljava/time/format/DecimalStyle;" />
+		<field name="STANDARD" />
+	</class>
+	<class name="java/time/format/FormatStyle" since="26">
+		<extends name="java/lang/Enum" />
+		<method name="valueOf(Ljava/lang/String;)Ljava/time/format/FormatStyle;" />
+		<method name="values()[Ljava/time/format/FormatStyle;" />
+		<field name="FULL" />
+		<field name="LONG" />
+		<field name="MEDIUM" />
+		<field name="SHORT" />
+	</class>
+	<class name="java/time/format/ResolverStyle" since="26">
+		<extends name="java/lang/Enum" />
+		<method name="valueOf(Ljava/lang/String;)Ljava/time/format/ResolverStyle;" />
+		<method name="values()[Ljava/time/format/ResolverStyle;" />
+		<field name="LENIENT" />
+		<field name="SMART" />
+		<field name="STRICT" />
+	</class>
+	<class name="java/time/format/SignStyle" since="26">
+		<extends name="java/lang/Enum" />
+		<method name="valueOf(Ljava/lang/String;)Ljava/time/format/SignStyle;" />
+		<method name="values()[Ljava/time/format/SignStyle;" />
+		<field name="ALWAYS" />
+		<field name="EXCEEDS_PAD" />
+		<field name="NEVER" />
+		<field name="NORMAL" />
+		<field name="NOT_NEGATIVE" />
+	</class>
+	<class name="java/time/format/TextStyle" since="26">
+		<extends name="java/lang/Enum" />
+		<method name="asNormal()Ljava/time/format/TextStyle;" />
+		<method name="asStandalone()Ljava/time/format/TextStyle;" />
+		<method name="isStandalone()Z" />
+		<method name="valueOf(Ljava/lang/String;)Ljava/time/format/TextStyle;" />
+		<method name="values()[Ljava/time/format/TextStyle;" />
+		<field name="FULL" />
+		<field name="FULL_STANDALONE" />
+		<field name="NARROW" />
+		<field name="NARROW_STANDALONE" />
+		<field name="SHORT" />
+		<field name="SHORT_STANDALONE" />
+	</class>
+	<class name="java/time/temporal/ChronoField" since="26">
+		<extends name="java/lang/Enum" />
+		<implements name="java/time/temporal/TemporalField" />
+		<method name="checkValidIntValue(J)I" />
+		<method name="checkValidValue(J)J" />
+		<method name="valueOf(Ljava/lang/String;)Ljava/time/temporal/ChronoField;" />
+		<method name="values()[Ljava/time/temporal/ChronoField;" />
+		<field name="ALIGNED_DAY_OF_WEEK_IN_MONTH" />
+		<field name="ALIGNED_DAY_OF_WEEK_IN_YEAR" />
+		<field name="ALIGNED_WEEK_OF_MONTH" />
+		<field name="ALIGNED_WEEK_OF_YEAR" />
+		<field name="AMPM_OF_DAY" />
+		<field name="CLOCK_HOUR_OF_AMPM" />
+		<field name="CLOCK_HOUR_OF_DAY" />
+		<field name="DAY_OF_MONTH" />
+		<field name="DAY_OF_WEEK" />
+		<field name="DAY_OF_YEAR" />
+		<field name="EPOCH_DAY" />
+		<field name="ERA" />
+		<field name="HOUR_OF_AMPM" />
+		<field name="HOUR_OF_DAY" />
+		<field name="INSTANT_SECONDS" />
+		<field name="MICRO_OF_DAY" />
+		<field name="MICRO_OF_SECOND" />
+		<field name="MILLI_OF_DAY" />
+		<field name="MILLI_OF_SECOND" />
+		<field name="MINUTE_OF_DAY" />
+		<field name="MINUTE_OF_HOUR" />
+		<field name="MONTH_OF_YEAR" />
+		<field name="NANO_OF_DAY" />
+		<field name="NANO_OF_SECOND" />
+		<field name="OFFSET_SECONDS" />
+		<field name="PROLEPTIC_MONTH" />
+		<field name="SECOND_OF_DAY" />
+		<field name="SECOND_OF_MINUTE" />
+		<field name="YEAR" />
+		<field name="YEAR_OF_ERA" />
+	</class>
+	<class name="java/time/temporal/ChronoUnit" since="26">
+		<extends name="java/lang/Enum" />
+		<implements name="java/time/temporal/TemporalUnit" />
+		<method name="valueOf(Ljava/lang/String;)Ljava/time/temporal/ChronoUnit;" />
+		<method name="values()[Ljava/time/temporal/ChronoUnit;" />
+		<field name="CENTURIES" />
+		<field name="DAYS" />
+		<field name="DECADES" />
+		<field name="ERAS" />
+		<field name="FOREVER" />
+		<field name="HALF_DAYS" />
+		<field name="HOURS" />
+		<field name="MICROS" />
+		<field name="MILLENNIA" />
+		<field name="MILLIS" />
+		<field name="MINUTES" />
+		<field name="MONTHS" />
+		<field name="NANOS" />
+		<field name="SECONDS" />
+		<field name="WEEKS" />
+		<field name="YEARS" />
+	</class>
+	<class name="java/time/temporal/IsoFields" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<field name="DAY_OF_QUARTER" />
+		<field name="QUARTER_OF_YEAR" />
+		<field name="QUARTER_YEARS" />
+		<field name="WEEK_BASED_YEAR" />
+		<field name="WEEK_BASED_YEARS" />
+		<field name="WEEK_OF_WEEK_BASED_YEAR" />
+	</class>
+	<class name="java/time/temporal/JulianFields" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<field name="JULIAN_DAY" />
+		<field name="MODIFIED_JULIAN_DAY" />
+		<field name="RATA_DIE" />
+	</class>
+	<class name="java/time/temporal/Temporal" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/time/temporal/TemporalAccessor" />
+		<method name="isSupported(Ljava/time/temporal/TemporalUnit;)Z" />
+		<method name="minus(JLjava/time/temporal/TemporalUnit;)Ljava/time/temporal/Temporal;" />
+		<method name="minus(Ljava/time/temporal/TemporalAmount;)Ljava/time/temporal/Temporal;" />
+		<method name="plus(JLjava/time/temporal/TemporalUnit;)Ljava/time/temporal/Temporal;" />
+		<method name="plus(Ljava/time/temporal/TemporalAmount;)Ljava/time/temporal/Temporal;" />
+		<method name="until(Ljava/time/temporal/Temporal;Ljava/time/temporal/TemporalUnit;)J" />
+		<method name="with(Ljava/time/temporal/TemporalAdjuster;)Ljava/time/temporal/Temporal;" />
+		<method name="with(Ljava/time/temporal/TemporalField;J)Ljava/time/temporal/Temporal;" />
+	</class>
+	<class name="java/time/temporal/TemporalAccessor" since="26">
+		<extends name="java/lang/Object" />
+		<method name="get(Ljava/time/temporal/TemporalField;)I" />
+		<method name="getLong(Ljava/time/temporal/TemporalField;)J" />
+		<method name="isSupported(Ljava/time/temporal/TemporalField;)Z" />
+		<method name="query(Ljava/time/temporal/TemporalQuery;)Ljava/lang/Object;" />
+		<method name="range(Ljava/time/temporal/TemporalField;)Ljava/time/temporal/ValueRange;" />
+	</class>
+	<class name="java/time/temporal/TemporalAdjuster" since="26">
+		<extends name="java/lang/Object" />
+		<method name="adjustInto(Ljava/time/temporal/Temporal;)Ljava/time/temporal/Temporal;" />
+	</class>
+	<class name="java/time/temporal/TemporalAdjusters" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="dayOfWeekInMonth(ILjava/time/DayOfWeek;)Ljava/time/temporal/TemporalAdjuster;" />
+		<method name="firstDayOfMonth()Ljava/time/temporal/TemporalAdjuster;" />
+		<method name="firstDayOfNextMonth()Ljava/time/temporal/TemporalAdjuster;" />
+		<method name="firstDayOfNextYear()Ljava/time/temporal/TemporalAdjuster;" />
+		<method name="firstDayOfYear()Ljava/time/temporal/TemporalAdjuster;" />
+		<method name="firstInMonth(Ljava/time/DayOfWeek;)Ljava/time/temporal/TemporalAdjuster;" />
+		<method name="lastDayOfMonth()Ljava/time/temporal/TemporalAdjuster;" />
+		<method name="lastDayOfYear()Ljava/time/temporal/TemporalAdjuster;" />
+		<method name="lastInMonth(Ljava/time/DayOfWeek;)Ljava/time/temporal/TemporalAdjuster;" />
+		<method name="next(Ljava/time/DayOfWeek;)Ljava/time/temporal/TemporalAdjuster;" />
+		<method name="nextOrSame(Ljava/time/DayOfWeek;)Ljava/time/temporal/TemporalAdjuster;" />
+		<method name="ofDateAdjuster(Ljava/util/function/UnaryOperator;)Ljava/time/temporal/TemporalAdjuster;" />
+		<method name="previous(Ljava/time/DayOfWeek;)Ljava/time/temporal/TemporalAdjuster;" />
+		<method name="previousOrSame(Ljava/time/DayOfWeek;)Ljava/time/temporal/TemporalAdjuster;" />
+	</class>
+	<class name="java/time/temporal/TemporalAmount" since="26">
+		<extends name="java/lang/Object" />
+		<method name="addTo(Ljava/time/temporal/Temporal;)Ljava/time/temporal/Temporal;" />
+		<method name="get(Ljava/time/temporal/TemporalUnit;)J" />
+		<method name="getUnits()Ljava/util/List;" />
+		<method name="subtractFrom(Ljava/time/temporal/Temporal;)Ljava/time/temporal/Temporal;" />
+	</class>
+	<class name="java/time/temporal/TemporalField" since="26">
+		<extends name="java/lang/Object" />
+		<method name="adjustInto(Ljava/time/temporal/Temporal;J)Ljava/time/temporal/Temporal;" />
+		<method name="getBaseUnit()Ljava/time/temporal/TemporalUnit;" />
+		<method name="getDisplayName(Ljava/util/Locale;)Ljava/lang/String;" />
+		<method name="getFrom(Ljava/time/temporal/TemporalAccessor;)J" />
+		<method name="getRangeUnit()Ljava/time/temporal/TemporalUnit;" />
+		<method name="isDateBased()Z" />
+		<method name="isSupportedBy(Ljava/time/temporal/TemporalAccessor;)Z" />
+		<method name="isTimeBased()Z" />
+		<method name="range()Ljava/time/temporal/ValueRange;" />
+		<method name="rangeRefinedBy(Ljava/time/temporal/TemporalAccessor;)Ljava/time/temporal/ValueRange;" />
+		<method name="resolve(Ljava/util/Map;Ljava/time/temporal/TemporalAccessor;Ljava/time/format/ResolverStyle;)Ljava/time/temporal/TemporalAccessor;" />
+	</class>
+	<class name="java/time/temporal/TemporalQueries" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="chronology()Ljava/time/temporal/TemporalQuery;" />
+		<method name="localDate()Ljava/time/temporal/TemporalQuery;" />
+		<method name="localTime()Ljava/time/temporal/TemporalQuery;" />
+		<method name="offset()Ljava/time/temporal/TemporalQuery;" />
+		<method name="precision()Ljava/time/temporal/TemporalQuery;" />
+		<method name="zone()Ljava/time/temporal/TemporalQuery;" />
+		<method name="zoneId()Ljava/time/temporal/TemporalQuery;" />
+	</class>
+	<class name="java/time/temporal/TemporalQuery" since="26">
+		<extends name="java/lang/Object" />
+		<method name="queryFrom(Ljava/time/temporal/TemporalAccessor;)Ljava/lang/Object;" />
+	</class>
+	<class name="java/time/temporal/TemporalUnit" since="26">
+		<extends name="java/lang/Object" />
+		<method name="addTo(Ljava/time/temporal/Temporal;J)Ljava/time/temporal/Temporal;" />
+		<method name="between(Ljava/time/temporal/Temporal;Ljava/time/temporal/Temporal;)J" />
+		<method name="getDuration()Ljava/time/Duration;" />
+		<method name="isDateBased()Z" />
+		<method name="isDurationEstimated()Z" />
+		<method name="isSupportedBy(Ljava/time/temporal/Temporal;)Z" />
+		<method name="isTimeBased()Z" />
+	</class>
+	<class name="java/time/temporal/UnsupportedTemporalTypeException" since="26">
+		<extends name="java/time/DateTimeException" />
+		<method name="&lt;init>(Ljava/lang/String;)V" />
+		<method name="&lt;init>(Ljava/lang/String;Ljava/lang/Throwable;)V" />
+	</class>
+	<class name="java/time/temporal/ValueRange" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/io/Serializable" />
+		<method name="&lt;init>()V" />
+		<method name="checkValidIntValue(JLjava/time/temporal/TemporalField;)I" />
+		<method name="checkValidValue(JLjava/time/temporal/TemporalField;)J" />
+		<method name="getLargestMinimum()J" />
+		<method name="getMaximum()J" />
+		<method name="getMinimum()J" />
+		<method name="getSmallestMaximum()J" />
+		<method name="isFixed()Z" />
+		<method name="isIntValue()Z" />
+		<method name="isValidIntValue(J)Z" />
+		<method name="isValidValue(J)Z" />
+		<method name="of(JJ)Ljava/time/temporal/ValueRange;" />
+		<method name="of(JJJ)Ljava/time/temporal/ValueRange;" />
+		<method name="of(JJJJ)Ljava/time/temporal/ValueRange;" />
+	</class>
+	<class name="java/time/temporal/WeekFields" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/io/Serializable" />
+		<method name="&lt;init>()V" />
+		<method name="dayOfWeek()Ljava/time/temporal/TemporalField;" />
+		<method name="getFirstDayOfWeek()Ljava/time/DayOfWeek;" />
+		<method name="getMinimalDaysInFirstWeek()I" />
+		<method name="of(Ljava/time/DayOfWeek;I)Ljava/time/temporal/WeekFields;" />
+		<method name="of(Ljava/util/Locale;)Ljava/time/temporal/WeekFields;" />
+		<method name="weekBasedYear()Ljava/time/temporal/TemporalField;" />
+		<method name="weekOfMonth()Ljava/time/temporal/TemporalField;" />
+		<method name="weekOfWeekBasedYear()Ljava/time/temporal/TemporalField;" />
+		<method name="weekOfYear()Ljava/time/temporal/TemporalField;" />
+		<field name="ISO" />
+		<field name="SUNDAY_START" />
+		<field name="WEEK_BASED_YEARS" />
+	</class>
+	<class name="java/time/zone/ZoneOffsetTransition" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/io/Serializable" />
+		<implements name="java/lang/Comparable" />
+		<method name="&lt;init>()V" />
+		<method name="compareTo(Ljava/time/zone/ZoneOffsetTransition;)I" />
+		<method name="getDateTimeAfter()Ljava/time/LocalDateTime;" />
+		<method name="getDateTimeBefore()Ljava/time/LocalDateTime;" />
+		<method name="getDuration()Ljava/time/Duration;" />
+		<method name="getInstant()Ljava/time/Instant;" />
+		<method name="getOffsetAfter()Ljava/time/ZoneOffset;" />
+		<method name="getOffsetBefore()Ljava/time/ZoneOffset;" />
+		<method name="isGap()Z" />
+		<method name="isOverlap()Z" />
+		<method name="isValidOffset(Ljava/time/ZoneOffset;)Z" />
+		<method name="of(Ljava/time/LocalDateTime;Ljava/time/ZoneOffset;Ljava/time/ZoneOffset;)Ljava/time/zone/ZoneOffsetTransition;" />
+		<method name="toEpochSecond()J" />
+	</class>
+	<class name="java/time/zone/ZoneOffsetTransitionRule" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/io/Serializable" />
+		<method name="&lt;init>()V" />
+		<method name="createTransition(I)Ljava/time/zone/ZoneOffsetTransition;" />
+		<method name="getDayOfMonthIndicator()I" />
+		<method name="getDayOfWeek()Ljava/time/DayOfWeek;" />
+		<method name="getLocalTime()Ljava/time/LocalTime;" />
+		<method name="getMonth()Ljava/time/Month;" />
+		<method name="getOffsetAfter()Ljava/time/ZoneOffset;" />
+		<method name="getOffsetBefore()Ljava/time/ZoneOffset;" />
+		<method name="getStandardOffset()Ljava/time/ZoneOffset;" />
+		<method name="getTimeDefinition()Ljava/time/zone/ZoneOffsetTransitionRule$TimeDefinition;" />
+		<method name="isMidnightEndOfDay()Z" />
+		<method name="of(Ljava/time/Month;ILjava/time/DayOfWeek;Ljava/time/LocalTime;ZLjava/time/zone/ZoneOffsetTransitionRule$TimeDefinition;Ljava/time/ZoneOffset;Ljava/time/ZoneOffset;Ljava/time/ZoneOffset;)Ljava/time/zone/ZoneOffsetTransitionRule;" />
+	</class>
+	<class name="java/time/zone/ZoneOffsetTransitionRule$TimeDefinition" since="26">
+		<extends name="java/lang/Enum" />
+		<method name="createDateTime(Ljava/time/LocalDateTime;Ljava/time/ZoneOffset;Ljava/time/ZoneOffset;)Ljava/time/LocalDateTime;" />
+		<method name="valueOf(Ljava/lang/String;)Ljava/time/zone/ZoneOffsetTransitionRule$TimeDefinition;" />
+		<method name="values()[Ljava/time/zone/ZoneOffsetTransitionRule$TimeDefinition;" />
+		<field name="STANDARD" />
+		<field name="UTC" />
+		<field name="WALL" />
+	</class>
+	<class name="java/time/zone/ZoneRules" since="26">
+		<extends name="java/lang/Object" />
+		<implements name="java/io/Serializable" />
+		<method name="&lt;init>()V" />
+		<method name="getDaylightSavings(Ljava/time/Instant;)Ljava/time/Duration;" />
+		<method name="getOffset(Ljava/time/Instant;)Ljava/time/ZoneOffset;" />
+		<method name="getOffset(Ljava/time/LocalDateTime;)Ljava/time/ZoneOffset;" />
+		<method name="getStandardOffset(Ljava/time/Instant;)Ljava/time/ZoneOffset;" />
+		<method name="getTransition(Ljava/time/LocalDateTime;)Ljava/time/zone/ZoneOffsetTransition;" />
+		<method name="getTransitionRules()Ljava/util/List;" />
+		<method name="getTransitions()Ljava/util/List;" />
+		<method name="getValidOffsets(Ljava/time/LocalDateTime;)Ljava/util/List;" />
+		<method name="isDaylightSavings(Ljava/time/Instant;)Z" />
+		<method name="isFixedOffset()Z" />
+		<method name="isValidOffset(Ljava/time/LocalDateTime;Ljava/time/ZoneOffset;)Z" />
+		<method name="nextTransition(Ljava/time/Instant;)Ljava/time/zone/ZoneOffsetTransition;" />
+		<method name="of(Ljava/time/ZoneOffset;)Ljava/time/zone/ZoneRules;" />
+		<method name="of(Ljava/time/ZoneOffset;Ljava/time/ZoneOffset;Ljava/util/List;Ljava/util/List;Ljava/util/List;)Ljava/time/zone/ZoneRules;" />
+		<method name="previousTransition(Ljava/time/Instant;)Ljava/time/zone/ZoneOffsetTransition;" />
+	</class>
+	<class name="java/time/zone/ZoneRulesException" since="26">
+		<extends name="java/time/DateTimeException" />
+		<method name="&lt;init>(Ljava/lang/String;)V" />
+		<method name="&lt;init>(Ljava/lang/String;Ljava/lang/Throwable;)V" />
+	</class>
 	<class name="java/util/AbstractCollection" since="1">
 		<extends name="java/lang/Object" />
 		<implements name="java/util/Collection" />
@@ -57936,6 +63610,36 @@
 		<method name="toString([S)Ljava/lang/String;" />
 		<method name="toString([Z)Ljava/lang/String;" />
 	</class>
+	<class name="java/util/Base64" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="getDecoder()Ljava/util/Base64$Decoder;" />
+		<method name="getEncoder()Ljava/util/Base64$Encoder;" />
+		<method name="getMimeDecoder()Ljava/util/Base64$Decoder;" />
+		<method name="getMimeEncoder()Ljava/util/Base64$Encoder;" />
+		<method name="getMimeEncoder(I[B)Ljava/util/Base64$Encoder;" />
+		<method name="getUrlDecoder()Ljava/util/Base64$Decoder;" />
+		<method name="getUrlEncoder()Ljava/util/Base64$Encoder;" />
+	</class>
+	<class name="java/util/Base64$Decoder" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="decode(Ljava/lang/String;)[B" />
+		<method name="decode(Ljava/nio/ByteBuffer;)Ljava/nio/ByteBuffer;" />
+		<method name="decode([B)[B" />
+		<method name="decode([B[B)I" />
+		<method name="wrap(Ljava/io/InputStream;)Ljava/io/InputStream;" />
+	</class>
+	<class name="java/util/Base64$Encoder" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="encode(Ljava/nio/ByteBuffer;)Ljava/nio/ByteBuffer;" />
+		<method name="encode([B)[B" />
+		<method name="encode([B[B)I" />
+		<method name="encodeToString([B)Ljava/lang/String;" />
+		<method name="withoutPadding()Ljava/util/Base64$Encoder;" />
+		<method name="wrap(Ljava/io/OutputStream;)Ljava/io/OutputStream;" />
+	</class>
 	<class name="java/util/BitSet" since="1">
 		<extends name="java/lang/Object" />
 		<implements name="java/io/Serializable" />
@@ -57993,7 +63697,9 @@
 		<method name="get(I)I" />
 		<method name="getActualMaximum(I)I" />
 		<method name="getActualMinimum(I)I" />
+		<method name="getAvailableCalendarTypes()Ljava/util/Set;" since="26" />
 		<method name="getAvailableLocales()[Ljava/util/Locale;" />
+		<method name="getCalendarType()Ljava/lang/String;" since="26" />
 		<method name="getDisplayName(IILjava/util/Locale;)Ljava/lang/String;" since="9" />
 		<method name="getDisplayNames(IILjava/util/Locale;)Ljava/util/Map;" since="9" />
 		<method name="getFirstDayOfWeek()I" />
@@ -58028,6 +63734,7 @@
 		<method name="setTimeInMillis(J)V" />
 		<method name="setTimeZone(Ljava/util/TimeZone;)V" />
 		<method name="setWeekDate(III)V" since="24" />
+		<method name="toInstant()Ljava/time/Instant;" since="26" />
 		<field name="ALL_STYLES" since="9" />
 		<field name="AM" />
 		<field name="AM_PM" />
@@ -58050,12 +63757,16 @@
 		<field name="JULY" />
 		<field name="JUNE" />
 		<field name="LONG" since="9" />
+		<field name="LONG_FORMAT" since="26" />
+		<field name="LONG_STANDALONE" since="26" />
 		<field name="MARCH" />
 		<field name="MAY" />
 		<field name="MILLISECOND" />
 		<field name="MINUTE" />
 		<field name="MONDAY" />
 		<field name="MONTH" />
+		<field name="NARROW_FORMAT" since="26" />
+		<field name="NARROW_STANDALONE" since="26" />
 		<field name="NOVEMBER" />
 		<field name="OCTOBER" />
 		<field name="PM" />
@@ -58063,6 +63774,8 @@
 		<field name="SECOND" />
 		<field name="SEPTEMBER" />
 		<field name="SHORT" since="9" />
+		<field name="SHORT_FORMAT" since="26" />
+		<field name="SHORT_STANDALONE" since="26" />
 		<field name="SUNDAY" />
 		<field name="THURSDAY" />
 		<field name="TUESDAY" />
@@ -58078,6 +63791,24 @@
 		<field name="isTimeSet" />
 		<field name="time" />
 	</class>
+	<class name="java/util/Calendar$Builder" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>()V" />
+		<method name="build()Ljava/util/Calendar;" />
+		<method name="set(II)Ljava/util/Calendar$Builder;" />
+		<method name="setCalendarType(Ljava/lang/String;)Ljava/util/Calendar$Builder;" />
+		<method name="setDate(III)Ljava/util/Calendar$Builder;" />
+		<method name="setFields([I)Ljava/util/Calendar$Builder;" />
+		<method name="setInstant(J)Ljava/util/Calendar$Builder;" />
+		<method name="setInstant(Ljava/util/Date;)Ljava/util/Calendar$Builder;" />
+		<method name="setLenient(Z)Ljava/util/Calendar$Builder;" />
+		<method name="setLocale(Ljava/util/Locale;)Ljava/util/Calendar$Builder;" />
+		<method name="setTimeOfDay(III)Ljava/util/Calendar$Builder;" />
+		<method name="setTimeOfDay(IIII)Ljava/util/Calendar$Builder;" />
+		<method name="setTimeZone(Ljava/util/TimeZone;)Ljava/util/Calendar$Builder;" />
+		<method name="setWeekDate(III)Ljava/util/Calendar$Builder;" />
+		<method name="setWeekDefinition(II)Ljava/util/Calendar$Builder;" />
+	</class>
 	<class name="java/util/Collection" since="1">
 		<extends name="java/lang/Object" />
 		<implements name="java/lang/Iterable" />
@@ -58107,6 +63838,9 @@
 		<method name="checkedCollection(Ljava/util/Collection;Ljava/lang/Class;)Ljava/util/Collection;" />
 		<method name="checkedList(Ljava/util/List;Ljava/lang/Class;)Ljava/util/List;" />
 		<method name="checkedMap(Ljava/util/Map;Ljava/lang/Class;Ljava/lang/Class;)Ljava/util/Map;" />
+		<method name="checkedNavigableMap(Ljava/util/NavigableMap;Ljava/lang/Class;Ljava/lang/Class;)Ljava/util/NavigableMap;" since="26" />
+		<method name="checkedNavigableSet(Ljava/util/NavigableSet;Ljava/lang/Class;)Ljava/util/NavigableSet;" since="26" />
+		<method name="checkedQueue(Ljava/util/Queue;Ljava/lang/Class;)Ljava/util/Queue;" since="26" />
 		<method name="checkedSet(Ljava/util/Set;Ljava/lang/Class;)Ljava/util/Set;" />
 		<method name="checkedSortedMap(Ljava/util/SortedMap;Ljava/lang/Class;Ljava/lang/Class;)Ljava/util/SortedMap;" />
 		<method name="checkedSortedSet(Ljava/util/SortedSet;Ljava/lang/Class;)Ljava/util/SortedSet;" />
@@ -58117,7 +63851,11 @@
 		<method name="emptyList()Ljava/util/List;" />
 		<method name="emptyListIterator()Ljava/util/ListIterator;" since="19" />
 		<method name="emptyMap()Ljava/util/Map;" />
+		<method name="emptyNavigableMap()Ljava/util/NavigableMap;" since="26" />
+		<method name="emptyNavigableSet()Ljava/util/NavigableSet;" since="26" />
 		<method name="emptySet()Ljava/util/Set;" />
+		<method name="emptySortedMap()Ljava/util/SortedMap;" since="26" />
+		<method name="emptySortedSet()Ljava/util/SortedSet;" since="26" />
 		<method name="enumeration(Ljava/util/Collection;)Ljava/util/Enumeration;" />
 		<method name="fill(Ljava/util/List;Ljava/lang/Object;)V" />
 		<method name="frequency(Ljava/util/Collection;Ljava/lang/Object;)I" />
@@ -58146,12 +63884,16 @@
 		<method name="synchronizedCollection(Ljava/util/Collection;)Ljava/util/Collection;" />
 		<method name="synchronizedList(Ljava/util/List;)Ljava/util/List;" />
 		<method name="synchronizedMap(Ljava/util/Map;)Ljava/util/Map;" />
+		<method name="synchronizedNavigableMap(Ljava/util/NavigableMap;)Ljava/util/NavigableMap;" since="26" />
+		<method name="synchronizedNavigableSet(Ljava/util/NavigableSet;)Ljava/util/NavigableSet;" since="26" />
 		<method name="synchronizedSet(Ljava/util/Set;)Ljava/util/Set;" />
 		<method name="synchronizedSortedMap(Ljava/util/SortedMap;)Ljava/util/SortedMap;" />
 		<method name="synchronizedSortedSet(Ljava/util/SortedSet;)Ljava/util/SortedSet;" />
 		<method name="unmodifiableCollection(Ljava/util/Collection;)Ljava/util/Collection;" />
 		<method name="unmodifiableList(Ljava/util/List;)Ljava/util/List;" />
 		<method name="unmodifiableMap(Ljava/util/Map;)Ljava/util/Map;" />
+		<method name="unmodifiableNavigableMap(Ljava/util/NavigableMap;)Ljava/util/NavigableMap;" since="26" />
+		<method name="unmodifiableNavigableSet(Ljava/util/NavigableSet;)Ljava/util/NavigableSet;" since="26" />
 		<method name="unmodifiableSet(Ljava/util/Set;)Ljava/util/Set;" />
 		<method name="unmodifiableSortedMap(Ljava/util/SortedMap;)Ljava/util/SortedMap;" />
 		<method name="unmodifiableSortedSet(Ljava/util/SortedSet;)Ljava/util/SortedSet;" />
@@ -58216,6 +63958,7 @@
 		<method name="after(Ljava/util/Date;)Z" />
 		<method name="before(Ljava/util/Date;)Z" />
 		<method name="compareTo(Ljava/util/Date;)I" />
+		<method name="from(Ljava/time/Instant;)Ljava/util/Date;" since="26" />
 		<method name="getDate()I" deprecated="16" />
 		<method name="getDay()I" deprecated="16" />
 		<method name="getHours()I" deprecated="16" />
@@ -58234,6 +63977,7 @@
 		<method name="setTime(J)V" />
 		<method name="setYear(I)V" deprecated="16" />
 		<method name="toGMTString()Ljava/lang/String;" deprecated="16" />
+		<method name="toInstant()Ljava/time/Instant;" since="26" />
 		<method name="toLocaleString()Ljava/lang/String;" deprecated="16" />
 	</class>
 	<class name="java/util/Deque" since="9">
@@ -58399,9 +64143,11 @@
 		<method name="&lt;init>(Ljava/util/Locale;)V" />
 		<method name="&lt;init>(Ljava/util/TimeZone;)V" />
 		<method name="&lt;init>(Ljava/util/TimeZone;Ljava/util/Locale;)V" />
+		<method name="from(Ljava/time/ZonedDateTime;)Ljava/util/GregorianCalendar;" since="26" />
 		<method name="getGregorianChange()Ljava/util/Date;" />
 		<method name="isLeapYear(I)Z" />
 		<method name="setGregorianChange(Ljava/util/Date;)V" />
+		<method name="toZonedDateTime()Ljava/time/ZonedDateTime;" since="26" />
 		<field name="AD" />
 		<field name="BC" />
 	</class>
@@ -58577,6 +64323,10 @@
 		<method name="&lt;init>(Ljava/lang/String;)V" />
 		<method name="&lt;init>(Ljava/lang/String;Ljava/lang/String;)V" />
 		<method name="&lt;init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V" />
+		<method name="filter(Ljava/util/List;Ljava/util/Collection;)Ljava/util/List;" since="26" />
+		<method name="filter(Ljava/util/List;Ljava/util/Collection;Ljava/util/Locale$FilteringMode;)Ljava/util/List;" since="26" />
+		<method name="filterTags(Ljava/util/List;Ljava/util/Collection;)Ljava/util/List;" since="26" />
+		<method name="filterTags(Ljava/util/List;Ljava/util/Collection;Ljava/util/Locale$FilteringMode;)Ljava/util/List;" since="26" />
 		<method name="forLanguageTag(Ljava/lang/String;)Ljava/util/Locale;" since="21" />
 		<method name="getAvailableLocales()[Ljava/util/Locale;" />
 		<method name="getCountry()Ljava/lang/String;" />
@@ -58604,8 +64354,12 @@
 		<method name="getUnicodeLocaleKeys()Ljava/util/Set;" since="21" />
 		<method name="getUnicodeLocaleType(Ljava/lang/String;)Ljava/lang/String;" since="21" />
 		<method name="getVariant()Ljava/lang/String;" />
+		<method name="hasExtensions()Z" since="26" />
+		<method name="lookup(Ljava/util/List;Ljava/util/Collection;)Ljava/util/Locale;" since="26" />
+		<method name="lookupTag(Ljava/util/List;Ljava/util/Collection;)Ljava/lang/String;" since="26" />
 		<method name="setDefault(Ljava/util/Locale$Category;Ljava/util/Locale;)V" since="24" />
 		<method name="setDefault(Ljava/util/Locale;)V" />
+		<method name="stripExtensions()Ljava/util/Locale;" since="26" />
 		<method name="toLanguageTag()Ljava/lang/String;" since="21" />
 		<field name="CANADA" />
 		<field name="CANADA_FRENCH" />
@@ -58656,6 +64410,28 @@
 		<field name="DISPLAY" />
 		<field name="FORMAT" />
 	</class>
+	<class name="java/util/Locale$FilteringMode" since="26">
+		<extends name="java/lang/Enum" />
+		<method name="valueOf(Ljava/lang/String;)Ljava/util/Locale$FilteringMode;" />
+		<method name="values()[Ljava/util/Locale$FilteringMode;" />
+		<field name="AUTOSELECT_FILTERING" />
+		<field name="EXTENDED_FILTERING" />
+		<field name="IGNORE_EXTENDED_RANGES" />
+		<field name="MAP_EXTENDED_RANGES" />
+		<field name="REJECT_EXTENDED_RANGES" />
+	</class>
+	<class name="java/util/Locale$LanguageRange" since="26">
+		<extends name="java/lang/Object" />
+		<method name="&lt;init>(Ljava/lang/String;)V" />
+		<method name="&lt;init>(Ljava/lang/String;D)V" />
+		<method name="getRange()Ljava/lang/String;" />
+		<method name="getWeight()D" />
+		<method name="mapEquivalents(Ljava/util/List;Ljava/util/Map;)Ljava/util/List;" />
+		<method name="parse(Ljava/lang/String;)Ljava/util/List;" />
+		<method name="parse(Ljava/lang/String;Ljava/util/Map;)Ljava/util/List;" />
+		<field name="MAX_WEIGHT" />
+		<field name="MIN_WEIGHT" />
+	</class>
 	<class name="java/util/LongSummaryStatistics" since="24">
 		<extends name="java/lang/Object" />
 		<implements name="java/util/function/IntConsumer" />
@@ -58961,6 +64737,7 @@
 		<method name="clearCache()V" since="9" />
 		<method name="clearCache(Ljava/lang/ClassLoader;)V" since="9" />
 		<method name="containsKey(Ljava/lang/String;)Z" since="9" />
+		<method name="getBaseBundleName()Ljava/lang/String;" since="26" />
 		<method name="getBundle(Ljava/lang/String;)Ljava/util/ResourceBundle;" />
 		<method name="getBundle(Ljava/lang/String;Ljava/util/Locale;)Ljava/util/ResourceBundle;" />
 		<method name="getBundle(Ljava/lang/String;Ljava/util/Locale;Ljava/lang/ClassLoader;)Ljava/util/ResourceBundle;" />
@@ -59009,6 +64786,8 @@
 		<method name="&lt;init>(Ljava/lang/String;)V" />
 		<method name="&lt;init>(Ljava/nio/channels/ReadableByteChannel;)V" />
 		<method name="&lt;init>(Ljava/nio/channels/ReadableByteChannel;Ljava/lang/String;)V" />
+		<method name="&lt;init>(Ljava/nio/file/Path;)V" since="26" />
+		<method name="&lt;init>(Ljava/nio/file/Path;Ljava/lang/String;)V" since="26" />
 		<method name="close()V" />
 		<method name="delimiter()Ljava/util/regex/Pattern;" />
 		<method name="findInLine(Ljava/lang/String;)Ljava/lang/String;" />
@@ -59289,12 +65068,14 @@
 		<method name="getOffset(J)I" />
 		<method name="getRawOffset()I" />
 		<method name="getTimeZone(Ljava/lang/String;)Ljava/util/TimeZone;" />
+		<method name="getTimeZone(Ljava/time/ZoneId;)Ljava/util/TimeZone;" since="26" />
 		<method name="hasSameRules(Ljava/util/TimeZone;)Z" />
 		<method name="inDaylightTime(Ljava/util/Date;)Z" />
 		<method name="observesDaylightTime()Z" since="24" />
 		<method name="setDefault(Ljava/util/TimeZone;)V" />
 		<method name="setID(Ljava/lang/String;)V" />
 		<method name="setRawOffset(I)V" />
+		<method name="toZoneId()Ljava/time/ZoneId;" since="26" />
 		<method name="useDaylightTime()Z" />
 		<field name="LONG" />
 		<field name="SHORT" />
@@ -60901,13 +66682,13 @@
 		<method name="&lt;init>(Ljava/lang/String;)V" />
 		<field name="CLASS_PATH" />
 		<field name="CONTENT_TYPE" />
-		<field name="EXTENSION_INSTALLATION" />
+		<field name="EXTENSION_INSTALLATION" deprecated="26" />
 		<field name="EXTENSION_LIST" />
 		<field name="EXTENSION_NAME" />
 		<field name="IMPLEMENTATION_TITLE" />
-		<field name="IMPLEMENTATION_URL" />
+		<field name="IMPLEMENTATION_URL" deprecated="26" />
 		<field name="IMPLEMENTATION_VENDOR" />
-		<field name="IMPLEMENTATION_VENDOR_ID" />
+		<field name="IMPLEMENTATION_VENDOR_ID" deprecated="26" />
 		<field name="IMPLEMENTATION_VERSION" />
 		<field name="MAIN_CLASS" />
 		<field name="MANIFEST_VERSION" />
@@ -60975,11 +66756,11 @@
 	</class>
 	<class name="java/util/jar/Pack200$Packer" since="1">
 		<extends name="java/lang/Object" />
-		<method name="addPropertyChangeListener(Ljava/beans/PropertyChangeListener;)V" since="3" />
+		<method name="addPropertyChangeListener(Ljava/beans/PropertyChangeListener;)V" since="3" deprecated="26" />
 		<method name="pack(Ljava/util/jar/JarFile;Ljava/io/OutputStream;)V" />
 		<method name="pack(Ljava/util/jar/JarInputStream;Ljava/io/OutputStream;)V" />
 		<method name="properties()Ljava/util/SortedMap;" />
-		<method name="removePropertyChangeListener(Ljava/beans/PropertyChangeListener;)V" since="3" />
+		<method name="removePropertyChangeListener(Ljava/beans/PropertyChangeListener;)V" since="3" deprecated="26" />
 		<field name="CLASS_ATTRIBUTE_PFX" />
 		<field name="CODE_ATTRIBUTE_PFX" />
 		<field name="DEFLATE_HINT" />
@@ -61002,9 +66783,9 @@
 	</class>
 	<class name="java/util/jar/Pack200$Unpacker" since="1">
 		<extends name="java/lang/Object" />
-		<method name="addPropertyChangeListener(Ljava/beans/PropertyChangeListener;)V" since="3" />
+		<method name="addPropertyChangeListener(Ljava/beans/PropertyChangeListener;)V" since="3" deprecated="26" />
 		<method name="properties()Ljava/util/SortedMap;" />
-		<method name="removePropertyChangeListener(Ljava/beans/PropertyChangeListener;)V" since="3" />
+		<method name="removePropertyChangeListener(Ljava/beans/PropertyChangeListener;)V" since="3" deprecated="26" />
 		<method name="unpack(Ljava/io/File;Ljava/util/jar/JarOutputStream;)V" />
 		<method name="unpack(Ljava/io/InputStream;Ljava/util/jar/JarOutputStream;)V" />
 		<field name="DEFLATE_HINT" />
@@ -61091,7 +66872,7 @@
 		<extends name="java/lang/Object" />
 		<method name="&lt;init>()V" />
 		<method name="addLogger(Ljava/util/logging/Logger;)Z" />
-		<method name="addPropertyChangeListener(Ljava/beans/PropertyChangeListener;)V" since="3" />
+		<method name="addPropertyChangeListener(Ljava/beans/PropertyChangeListener;)V" since="3" deprecated="26" />
 		<method name="checkAccess()V" />
 		<method name="getLogManager()Ljava/util/logging/LogManager;" />
 		<method name="getLogger(Ljava/lang/String;)Ljava/util/logging/Logger;" />
@@ -61100,7 +66881,7 @@
 		<method name="getProperty(Ljava/lang/String;)Ljava/lang/String;" />
 		<method name="readConfiguration()V" />
 		<method name="readConfiguration(Ljava/io/InputStream;)V" />
-		<method name="removePropertyChangeListener(Ljava/beans/PropertyChangeListener;)V" since="3" />
+		<method name="removePropertyChangeListener(Ljava/beans/PropertyChangeListener;)V" since="3" deprecated="26" />
 		<method name="reset()V" />
 		<field name="LOGGING_MXBEAN_NAME" />
 	</class>
@@ -61138,14 +66919,18 @@
 		<method name="&lt;init>(Ljava/lang/String;Ljava/lang/String;)V" />
 		<method name="addHandler(Ljava/util/logging/Handler;)V" />
 		<method name="config(Ljava/lang/String;)V" />
+		<method name="config(Ljava/util/function/Supplier;)V" since="26" />
 		<method name="entering(Ljava/lang/String;Ljava/lang/String;)V" />
 		<method name="entering(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)V" />
 		<method name="entering(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V" />
 		<method name="exiting(Ljava/lang/String;Ljava/lang/String;)V" />
 		<method name="exiting(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)V" />
 		<method name="fine(Ljava/lang/String;)V" />
+		<method name="fine(Ljava/util/function/Supplier;)V" since="26" />
 		<method name="finer(Ljava/lang/String;)V" />
+		<method name="finer(Ljava/util/function/Supplier;)V" since="26" />
 		<method name="finest(Ljava/lang/String;)V" />
+		<method name="finest(Ljava/util/function/Supplier;)V" since="26" />
 		<method name="getAnonymousLogger()Ljava/util/logging/Logger;" />
 		<method name="getAnonymousLogger(Ljava/lang/String;)Ljava/util/logging/Logger;" />
 		<method name="getFilter()Ljava/util/logging/Filter;" />
@@ -61160,28 +66945,38 @@
 		<method name="getResourceBundleName()Ljava/lang/String;" />
 		<method name="getUseParentHandlers()Z" />
 		<method name="info(Ljava/lang/String;)V" />
+		<method name="info(Ljava/util/function/Supplier;)V" since="26" />
 		<method name="isLoggable(Ljava/util/logging/Level;)Z" />
 		<method name="log(Ljava/util/logging/Level;Ljava/lang/String;)V" />
 		<method name="log(Ljava/util/logging/Level;Ljava/lang/String;Ljava/lang/Object;)V" />
 		<method name="log(Ljava/util/logging/Level;Ljava/lang/String;Ljava/lang/Throwable;)V" />
 		<method name="log(Ljava/util/logging/Level;Ljava/lang/String;[Ljava/lang/Object;)V" />
+		<method name="log(Ljava/util/logging/Level;Ljava/lang/Throwable;Ljava/util/function/Supplier;)V" since="26" />
+		<method name="log(Ljava/util/logging/Level;Ljava/util/function/Supplier;)V" since="26" />
 		<method name="log(Ljava/util/logging/LogRecord;)V" />
 		<method name="logp(Ljava/util/logging/Level;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V" />
 		<method name="logp(Ljava/util/logging/Level;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)V" />
 		<method name="logp(Ljava/util/logging/Level;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)V" />
 		<method name="logp(Ljava/util/logging/Level;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V" />
-		<method name="logrb(Ljava/util/logging/Level;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V" />
-		<method name="logrb(Ljava/util/logging/Level;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)V" />
-		<method name="logrb(Ljava/util/logging/Level;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)V" />
-		<method name="logrb(Ljava/util/logging/Level;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V" />
+		<method name="logp(Ljava/util/logging/Level;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;Ljava/util/function/Supplier;)V" since="26" />
+		<method name="logp(Ljava/util/logging/Level;Ljava/lang/String;Ljava/lang/String;Ljava/util/function/Supplier;)V" since="26" />
+		<method name="logrb(Ljava/util/logging/Level;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V" deprecated="26" />
+		<method name="logrb(Ljava/util/logging/Level;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)V" deprecated="26" />
+		<method name="logrb(Ljava/util/logging/Level;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)V" deprecated="26" />
+		<method name="logrb(Ljava/util/logging/Level;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V" deprecated="26" />
+		<method name="logrb(Ljava/util/logging/Level;Ljava/lang/String;Ljava/lang/String;Ljava/util/ResourceBundle;Ljava/lang/String;Ljava/lang/Throwable;)V" since="26" />
+		<method name="logrb(Ljava/util/logging/Level;Ljava/lang/String;Ljava/lang/String;Ljava/util/ResourceBundle;Ljava/lang/String;[Ljava/lang/Object;)V" since="26" />
 		<method name="removeHandler(Ljava/util/logging/Handler;)V" />
 		<method name="setFilter(Ljava/util/logging/Filter;)V" />
 		<method name="setLevel(Ljava/util/logging/Level;)V" />
 		<method name="setParent(Ljava/util/logging/Logger;)V" />
+		<method name="setResourceBundle(Ljava/util/ResourceBundle;)V" since="26" />
 		<method name="setUseParentHandlers(Z)V" />
 		<method name="severe(Ljava/lang/String;)V" />
+		<method name="severe(Ljava/util/function/Supplier;)V" since="26" />
 		<method name="throwing(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)V" />
 		<method name="warning(Ljava/lang/String;)V" />
+		<method name="warning(Ljava/util/function/Supplier;)V" since="26" />
 		<field name="GLOBAL_LOGGER_NAME" since="9" />
 		<field name="global" deprecated="16" />
 	</class>
@@ -61342,8 +67137,10 @@
 		<method name="&lt;init>()V" />
 		<method name="appendReplacement(Ljava/lang/StringBuffer;Ljava/lang/String;)Ljava/util/regex/Matcher;" />
 		<method name="appendTail(Ljava/lang/StringBuffer;)Ljava/lang/StringBuffer;" />
+		<method name="end(Ljava/lang/String;)I" since="26" />
 		<method name="find()Z" />
 		<method name="find(I)Z" />
+		<method name="group(Ljava/lang/String;)Ljava/lang/String;" since="26" />
 		<method name="hasAnchoringBounds()Z" />
 		<method name="hasTransparentBounds()Z" />
 		<method name="hitEnd()Z" />
@@ -61359,6 +67156,7 @@
 		<method name="requireEnd()Z" />
 		<method name="reset()Ljava/util/regex/Matcher;" />
 		<method name="reset(Ljava/lang/CharSequence;)Ljava/util/regex/Matcher;" />
+		<method name="start(Ljava/lang/String;)I" since="26" />
 		<method name="toMatchResult()Ljava/util/regex/MatchResult;" />
 		<method name="useAnchoringBounds(Z)Ljava/util/regex/Matcher;" />
 		<method name="usePattern(Ljava/util/regex/Pattern;)Ljava/util/regex/Matcher;" />
@@ -61688,12 +67486,14 @@
 		<extends name="java/lang/Object" />
 		<implements name="java/util/zip/Checksum" />
 		<method name="&lt;init>()V" />
+		<method name="update(Ljava/nio/ByteBuffer;)V" since="26" />
 		<method name="update([B)V" />
 	</class>
 	<class name="java/util/zip/CRC32" since="1">
 		<extends name="java/lang/Object" />
 		<implements name="java/util/zip/Checksum" />
 		<method name="&lt;init>()V" />
+		<method name="update(Ljava/nio/ByteBuffer;)V" since="26" />
 		<method name="update([B)V" />
 	</class>
 	<class name="java/util/zip/CheckedInputStream" since="1">
@@ -61841,7 +67641,10 @@
 		<method name="getComment()Ljava/lang/String;" />
 		<method name="getCompressedSize()J" />
 		<method name="getCrc()J" />
+		<method name="getCreationTime()Ljava/nio/file/attribute/FileTime;" since="26" />
 		<method name="getExtra()[B" />
+		<method name="getLastAccessTime()Ljava/nio/file/attribute/FileTime;" since="26" />
+		<method name="getLastModifiedTime()Ljava/nio/file/attribute/FileTime;" since="26" />
 		<method name="getMethod()I" />
 		<method name="getName()Ljava/lang/String;" />
 		<method name="getSize()J" />
@@ -61850,7 +67653,10 @@
 		<method name="setComment(Ljava/lang/String;)V" />
 		<method name="setCompressedSize(J)V" />
 		<method name="setCrc(J)V" />
+		<method name="setCreationTime(Ljava/nio/file/attribute/FileTime;)Ljava/util/zip/ZipEntry;" since="26" />
 		<method name="setExtra([B)V" />
+		<method name="setLastAccessTime(Ljava/nio/file/attribute/FileTime;)Ljava/util/zip/ZipEntry;" since="26" />
+		<method name="setLastModifiedTime(Ljava/nio/file/attribute/FileTime;)Ljava/util/zip/ZipEntry;" since="26" />
 		<method name="setMethod(I)V" />
 		<method name="setSize(J)V" />
 		<method name="setTime(J)V" />
@@ -61922,6 +67728,7 @@
 		<method name="getInputStream(Ljava/util/zip/ZipEntry;)Ljava/io/InputStream;" />
 		<method name="getName()Ljava/lang/String;" />
 		<method name="size()I" />
+		<method name="stream()Ljava/util/stream/Stream;" since="26" />
 		<field name="CENATT" since="21" />
 		<field name="CENATX" since="21" />
 		<field name="CENCOM" since="21" />
@@ -62313,6 +68120,7 @@
 	<class name="javax/crypto/SecretKey" since="1">
 		<extends name="java/lang/Object" />
 		<implements name="java/security/Key" />
+		<implements name="javax/security/auth/Destroyable" since="26" />
 		<field name="serialVersionUID" />
 	</class>
 	<class name="javax/crypto/SecretKeyFactory" since="1">
@@ -62457,7 +68265,9 @@
 		<extends name="java/lang/Object" />
 		<implements name="java/security/spec/AlgorithmParameterSpec" />
 		<method name="&lt;init>([BI)V" />
+		<method name="&lt;init>([BILjava/security/spec/AlgorithmParameterSpec;)V" since="26" />
 		<method name="getIterationCount()I" />
+		<method name="getParameterSpec()Ljava/security/spec/AlgorithmParameterSpec;" since="26" />
 		<method name="getSalt()[B" />
 	</class>
 	<class name="javax/crypto/spec/PSource" since="1">
@@ -62509,7 +68319,7 @@
 		<method name="eglCopyBuffers(Ljavax/microedition/khronos/egl/EGLDisplay;Ljavax/microedition/khronos/egl/EGLSurface;Ljava/lang/Object;)Z" />
 		<method name="eglCreateContext(Ljavax/microedition/khronos/egl/EGLDisplay;Ljavax/microedition/khronos/egl/EGLConfig;Ljavax/microedition/khronos/egl/EGLContext;[I)Ljavax/microedition/khronos/egl/EGLContext;" />
 		<method name="eglCreatePbufferSurface(Ljavax/microedition/khronos/egl/EGLDisplay;Ljavax/microedition/khronos/egl/EGLConfig;[I)Ljavax/microedition/khronos/egl/EGLSurface;" />
-		<method name="eglCreatePixmapSurface(Ljavax/microedition/khronos/egl/EGLDisplay;Ljavax/microedition/khronos/egl/EGLConfig;Ljava/lang/Object;[I)Ljavax/microedition/khronos/egl/EGLSurface;" />
+		<method name="eglCreatePixmapSurface(Ljavax/microedition/khronos/egl/EGLDisplay;Ljavax/microedition/khronos/egl/EGLConfig;Ljava/lang/Object;[I)Ljavax/microedition/khronos/egl/EGLSurface;" deprecated="26" />
 		<method name="eglCreateWindowSurface(Ljavax/microedition/khronos/egl/EGLDisplay;Ljavax/microedition/khronos/egl/EGLConfig;Ljava/lang/Object;[I)Ljavax/microedition/khronos/egl/EGLSurface;" />
 		<method name="eglDestroyContext(Ljavax/microedition/khronos/egl/EGLDisplay;Ljavax/microedition/khronos/egl/EGLContext;)Z" />
 		<method name="eglDestroySurface(Ljavax/microedition/khronos/egl/EGLDisplay;Ljavax/microedition/khronos/egl/EGLSurface;)Z" />
diff --git a/sdk/platform-tools/dmtracedump b/sdk/platform-tools/dmtracedump
index 0c062c3..86f0630 100755
--- a/sdk/platform-tools/dmtracedump
+++ b/sdk/platform-tools/dmtracedump
Binary files differ
diff --git a/sdk/platform-tools/etc1tool b/sdk/platform-tools/etc1tool
index 434513c..6579bb0 100755
--- a/sdk/platform-tools/etc1tool
+++ b/sdk/platform-tools/etc1tool
Binary files differ
diff --git a/sdk/platform-tools/fastboot b/sdk/platform-tools/fastboot
index b147d23..fd8bb1e 100755
--- a/sdk/platform-tools/fastboot
+++ b/sdk/platform-tools/fastboot
Binary files differ
diff --git a/sdk/platform-tools/hprof-conv b/sdk/platform-tools/hprof-conv
index ca5c224..24a91f8 100755
--- a/sdk/platform-tools/hprof-conv
+++ b/sdk/platform-tools/hprof-conv
Binary files differ
diff --git a/sdk/platform-tools/lib64/libc++.so b/sdk/platform-tools/lib64/libc++.so
index fa84063..a72b89d 100755
--- a/sdk/platform-tools/lib64/libc++.so
+++ b/sdk/platform-tools/lib64/libc++.so
Binary files differ
diff --git a/sdk/platform-tools/package.xml b/sdk/platform-tools/package.xml
deleted file mode 100644
index 28769e4..0000000
--- a/sdk/platform-tools/package.xml
+++ /dev/null
@@ -1,145 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="yes"?><ns2:repository xmlns:ns2="http://schemas.android.com/repository/android/common/01" xmlns:ns3="http://schemas.android.com/sdk/android/repo/addon2/01" xmlns:ns4="http://schemas.android.com/sdk/android/repo/sys-img2/01" xmlns:ns5="http://schemas.android.com/repository/android/generic/01" xmlns:ns6="http://schemas.android.com/sdk/android/repo/repository2/01"><license id="license-882247AE" type="text">Terms and Conditions
-
-This is the Android Software Development Kit License Agreement
-
-1. Introduction
-
-1.1 The Android Software Development Kit (referred to in the License Agreement as the "SDK" and specifically including the Android system files, packaged APIs, and Google APIs add-ons) is licensed to you subject to the terms of the License Agreement. The License Agreement forms a legally binding contract between you and Google in relation to your use of the SDK.
-
-1.2 "Android" means the Android software stack for devices, as made available under the Android Open Source Project, which is located at the following URL: http://source.android.com/, as updated from time to time.
-
-1.3 A "compatible implementation" means any Android device that (i) complies with the Android Compatibility Definition document, which can be found at the Android compatibility website (http://source.android.com/compatibility) and which may be updated from time to time; and (ii) successfully passes the Android Compatibility Test Suite (CTS).
-
-1.4 "Google" means Google Inc., a Delaware corporation with principal place of business at 1600 Amphitheatre Parkway, Mountain View, CA 94043, United States.
-
-
-2. Accepting the License Agreement
-
-2.1 In order to use the SDK, you must first agree to the License Agreement. You may not use the SDK if you do not accept the License Agreement.
-
-2.2 By clicking to accept, you hereby agree to the terms of the License Agreement.
-
-2.3 You may not use the SDK and may not accept the License Agreement if you are a person barred from receiving the SDK under the laws of the United States or other countries, including the country in which you are resident or from which you use the SDK.
-
-2.4 If you are agreeing to be bound by the License Agreement on behalf of your employer or other entity, you represent and warrant that you have full legal authority to bind your employer or such entity to the License Agreement. If you do not have the requisite authority, you may not accept the License Agreement or use the SDK on behalf of your employer or other entity.
-
-
-3. SDK License from Google
-
-3.1 Subject to the terms of the License Agreement, Google grants you a limited, worldwide, royalty-free, non-assignable, non-exclusive, and non-sublicensable license to use the SDK solely to develop applications for compatible implementations of Android.
-
-3.2 You may not use this SDK to develop applications for other platforms (including non-compatible implementations of Android) or to develop another SDK. You are of course free to develop applications for other platforms, including non-compatible implementations of Android, provided that this SDK is not used for that purpose.
-
-3.3 You agree that Google or third parties own all legal right, title and interest in and to the SDK, including any Intellectual Property Rights that subsist in the SDK. "Intellectual Property Rights" means any and all rights under patent law, copyright law, trade secret law, trademark law, and any and all other proprietary rights. Google reserves all rights not expressly granted to you.
-
-3.4 You may not use the SDK for any purpose not expressly permitted by the License Agreement.  Except to the extent required by applicable third party licenses, you may not: (a) copy (except for backup purposes), modify, adapt, redistribute, decompile, reverse engineer, disassemble, or create derivative works of the SDK or any part of the SDK; or (b) load any part of the SDK onto a mobile handset or any other hardware device except a personal computer, combine any part of the SDK with other software, or distribute any software or device incorporating a part of the SDK.
-
-3.5 Use, reproduction and distribution of components of the SDK licensed under an open source software license are governed solely by the terms of that open source software license and not the License Agreement.
-
-3.6 You agree that the form and nature of the SDK that Google provides may change without prior notice to you and that future versions of the SDK may be incompatible with applications developed on previous versions of the SDK. You agree that Google may stop (permanently or temporarily) providing the SDK (or any features within the SDK) to you or to users generally at Google's sole discretion, without prior notice to you.
-
-3.7 Nothing in the License Agreement gives you a right to use any of Google's trade names, trademarks, service marks, logos, domain names, or other distinctive brand features.
-
-3.8 You agree that you will not remove, obscure, or alter any proprietary rights notices (including copyright and trademark notices) that may be affixed to or contained within the SDK.
-
-
-4. Use of the SDK by You
-
-4.1 Google agrees that it obtains no right, title or interest from you (or your licensors) under the License Agreement in or to any software applications that you develop using the SDK, including any intellectual property rights that subsist in those applications.
-
-4.2 You agree to use the SDK and write applications only for purposes that are permitted by (a) the License Agreement and (b) any applicable law, regulation or generally accepted practices or guidelines in the relevant jurisdictions (including any laws regarding the export of data or software to and from the United States or other relevant countries).
-
-4.3 You agree that if you use the SDK to develop applications for general public users, you will protect the privacy and legal rights of those users. If the users provide you with user names, passwords, or other login information or personal information, you must make the users aware that the information will be available to your application, and you must provide legally adequate privacy notice and protection for those users. If your application stores personal or sensitive information provided by users, it must do so securely. If the user provides your application with Google Account information, your application may only use that information to access the user's Google Account when, and for the limited purposes for which, the user has given you permission to do so.
-
-4.4 You agree that you will not engage in any activity with the SDK, including the development or distribution of an application, that interferes with, disrupts, damages, or accesses in an unauthorized manner the servers, networks, or other properties or services of any third party including, but not limited to, Google or any mobile communications carrier.
-
-4.5 You agree that you are solely responsible for (and that Google has no responsibility to you or to any third party for) any data, content, or resources that you create, transmit or display through Android and/or applications for Android, and for the consequences of your actions (including any loss or damage which Google may suffer) by doing so.
-
-4.6 You agree that you are solely responsible for (and that Google has no responsibility to you or to any third party for) any breach of your obligations under the License Agreement, any applicable third party contract or Terms of Service, or any applicable law or regulation, and for the consequences (including any loss or damage which Google or any third party may suffer) of any such breach.
-
-5. Your Developer Credentials
-
-5.1 You agree that you are responsible for maintaining the confidentiality of any developer credentials that may be issued to you by Google or which you may choose yourself and that you will be solely responsible for all applications that are developed under your developer credentials.
-
-6. Privacy and Information
-
-6.1 In order to continually innovate and improve the SDK, Google may collect certain usage statistics from the software including but not limited to a unique identifier, associated IP address, version number of the software, and information on which tools and/or services in the SDK are being used and how they are being used. Before any of this information is collected, the SDK will notify you and seek your consent. If you withhold consent, the information will not be collected.
-
-6.2 The data collected is examined in the aggregate to improve the SDK and is maintained in accordance with Google's Privacy Policy.
-
-
-7. Third Party Applications
-
-7.1 If you use the SDK to run applications developed by a third party or that access data, content or resources provided by a third party, you agree that Google is not responsible for those applications, data, content, or resources. You understand that all data, content or resources which you may access through such third party applications are the sole responsibility of the person from which they originated and that Google is not liable for any loss or damage that you may experience as a result of the use or access of any of those third party applications, data, content, or resources.
-
-7.2 You should be aware the data, content, and resources presented to you through such a third party application may be protected by intellectual property rights which are owned by the providers (or by other persons or companies on their behalf). You may not modify, rent, lease, loan, sell, distribute or create derivative works based on these data, content, or resources (either in whole or in part) unless you have been specifically given permission to do so by the relevant owners.
-
-7.3 You acknowledge that your use of such third party applications, data, content, or resources may be subject to separate terms between you and the relevant third party. In that case, the License Agreement does not affect your legal relationship with these third parties.
-
-
-8. Using Android APIs
-
-8.1 Google Data APIs
-
-8.1.1 If you use any API to retrieve data from Google, you acknowledge that the data may be protected by intellectual property rights which are owned by Google or those parties that provide the data (or by other persons or companies on their behalf). Your use of any such API may be subject to additional Terms of Service. You may not modify, rent, lease, loan, sell, distribute or create derivative works based on this data (either in whole or in part) unless allowed by the relevant Terms of Service.
-
-8.1.2 If you use any API to retrieve a user's data from Google, you acknowledge and agree that you shall retrieve data only with the user's explicit consent and only when, and for the limited purposes for which, the user has given you permission to do so.
-
-
-9. Terminating the License Agreement
-
-9.1 The License Agreement will continue to apply until terminated by either you or Google as set out below.
-
-9.2 If you want to terminate the License Agreement, you may do so by ceasing your use of the SDK and any relevant developer credentials.
-
-9.3 Google may at any time, terminate the License Agreement with you if:
-(A) you have breached any provision of the License Agreement; or
-(B) Google is required to do so by law; or
-(C) the partner with whom Google offered certain parts of SDK (such as APIs) to you has terminated its relationship with Google or ceased to offer certain parts of the SDK to you; or
-(D) Google decides to no longer provide the SDK or certain parts of the SDK to users in the country in which you are resident or from which you use the service, or the provision of the SDK or certain SDK services to you by Google is, in Google's sole discretion, no longer commercially viable.
-
-9.4 When the License Agreement comes to an end, all of the legal rights, obligations and liabilities that you and Google have benefited from, been subject to (or which have accrued over time whilst the License Agreement has been in force) or which are expressed to continue indefinitely, shall be unaffected by this cessation, and the provisions of paragraph 14.7 shall continue to apply to such rights, obligations and liabilities indefinitely.
-
-
-10. DISCLAIMER OF WARRANTIES
-
-10.1 YOU EXPRESSLY UNDERSTAND AND AGREE THAT YOUR USE OF THE SDK IS AT YOUR SOLE RISK AND THAT THE SDK IS PROVIDED "AS IS" AND "AS AVAILABLE" WITHOUT WARRANTY OF ANY KIND FROM GOOGLE.
-
-10.2 YOUR USE OF THE SDK AND ANY MATERIAL DOWNLOADED OR OTHERWISE OBTAINED THROUGH THE USE OF THE SDK IS AT YOUR OWN DISCRETION AND RISK AND YOU ARE SOLELY RESPONSIBLE FOR ANY DAMAGE TO YOUR COMPUTER SYSTEM OR OTHER DEVICE OR LOSS OF DATA THAT RESULTS FROM SUCH USE.
-
-10.3 GOOGLE FURTHER EXPRESSLY DISCLAIMS ALL WARRANTIES AND CONDITIONS OF ANY KIND, WHETHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
-
-
-11. LIMITATION OF LIABILITY
-
-11.1 YOU EXPRESSLY UNDERSTAND AND AGREE THAT GOOGLE, ITS SUBSIDIARIES AND AFFILIATES, AND ITS LICENSORS SHALL NOT BE LIABLE TO YOU UNDER ANY THEORY OF LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL OR EXEMPLARY DAMAGES THAT MAY BE INCURRED BY YOU, INCLUDING ANY LOSS OF DATA, WHETHER OR NOT GOOGLE OR ITS REPRESENTATIVES HAVE BEEN ADVISED OF OR SHOULD HAVE BEEN AWARE OF THE POSSIBILITY OF ANY SUCH LOSSES ARISING.
-
-
-12. Indemnification
-
-12.1 To the maximum extent permitted by law, you agree to defend, indemnify and hold harmless Google, its affiliates and their respective directors, officers, employees and agents from and against any and all claims, actions, suits or proceedings, as well as any and all losses, liabilities, damages, costs and expenses (including reasonable attorneys fees) arising out of or accruing from (a) your use of the SDK, (b) any application you develop on the SDK that infringes any copyright, trademark, trade secret, trade dress, patent or other intellectual property right of any person or defames any person or violates their rights of publicity or privacy, and (c) any non-compliance by you with the License Agreement.
-
-
-13. Changes to the License Agreement
-
-13.1 Google may make changes to the License Agreement as it distributes new versions of the SDK. When these changes are made, Google will make a new version of the License Agreement available on the website where the SDK is made available.
-
-
-14. General Legal Terms
-
-14.1 The License Agreement constitutes the whole legal agreement between you and Google and governs your use of the SDK (excluding any services which Google may provide to you under a separate written agreement), and completely replaces any prior agreements between you and Google in relation to the SDK.
-
-14.2 You agree that if Google does not exercise or enforce any legal right or remedy which is contained in the License Agreement (or which Google has the benefit of under any applicable law), this will not be taken to be a formal waiver of Google's rights and that those rights or remedies will still be available to Google.
-
-14.3 If any court of law, having the jurisdiction to decide on this matter, rules that any provision of the License Agreement is invalid, then that provision will be removed from the License Agreement without affecting the rest of the License Agreement. The remaining provisions of the License Agreement will continue to be valid and enforceable.
-
-14.4 You acknowledge and agree that each member of the group of companies of which Google is the parent shall be third party beneficiaries to the License Agreement and that such other companies shall be entitled to directly enforce, and rely upon, any provision of the License Agreement that confers a benefit on (or rights in favor of) them. Other than this, no other person or company shall be third party beneficiaries to the License Agreement.
-
-14.5 EXPORT RESTRICTIONS. THE SDK IS SUBJECT TO UNITED STATES EXPORT LAWS AND REGULATIONS. YOU MUST COMPLY WITH ALL DOMESTIC AND INTERNATIONAL EXPORT LAWS AND REGULATIONS THAT APPLY TO THE SDK. THESE LAWS INCLUDE RESTRICTIONS ON DESTINATIONS, END USERS AND END USE.
-
-14.6 The rights granted in the License Agreement may not be assigned or transferred by either you or Google without the prior written approval of the other party. Neither you nor Google shall be permitted to delegate their responsibilities or obligations under the License Agreement without the prior written approval of the other party.
-
-14.7 The License Agreement, and your relationship with Google under the License Agreement, shall be governed by the laws of the State of California without regard to its conflict of laws provisions. You and Google agree to submit to the exclusive jurisdiction of the courts located within the county of Santa Clara, California to resolve any legal matter arising from the License Agreement. Notwithstanding this, you agree that Google shall still be allowed to apply for injunctive remedies (or an equivalent type of urgent legal relief) in any jurisdiction.
-
-
-November 20, 2015</license><localPackage path="platform-tools" obsolete="false"><type-details xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ns5:genericDetailsType"/><revision><major>25</major><minor>0</minor><micro>3</micro></revision><display-name>Android SDK Platform-Tools 25.0.3</display-name><uses-license ref="license-882247AE"/></localPackage></ns2:repository>
\ No newline at end of file
diff --git a/sdk/platform-tools/source.properties b/sdk/platform-tools/source.properties
index 92f972f..eb71ef1 100644
--- a/sdk/platform-tools/source.properties
+++ b/sdk/platform-tools/source.properties
@@ -1,7 +1,3 @@
-### Android Tool: Source of this archive.
-#Thu Mar 16 09:57:20 PDT 2017
-Archive.HostOs=linux
-Pkg.License=Terms and Conditions\n\nThis is the Android Software Development Kit License Agreement\n\n1. Introduction\n\n1.1 The Android Software Development Kit (referred to in the License Agreement as the "SDK" and specifically including the Android system files, packaged APIs, and Google APIs add-ons) is licensed to you subject to the terms of the License Agreement. The License Agreement forms a legally binding contract between you and Google in relation to your use of the SDK.\n\n1.2 "Android" means the Android software stack for devices, as made available under the Android Open Source Project, which is located at the following URL\: http\://source.android.com/, as updated from time to time.\n\n1.3 A "compatible implementation" means any Android device that (i) complies with the Android Compatibility Definition document, which can be found at the Android compatibility website (http\://source.android.com/compatibility) and which may be updated from time to time; and (ii) successfully passes the Android Compatibility Test Suite (CTS).\n\n1.4 "Google" means Google Inc., a Delaware corporation with principal place of business at 1600 Amphitheatre Parkway, Mountain View, CA 94043, United States.\n\n\n2. Accepting the License Agreement\n\n2.1 In order to use the SDK, you must first agree to the License Agreement. You may not use the SDK if you do not accept the License Agreement.\n\n2.2 By clicking to accept, you hereby agree to the terms of the License Agreement.\n\n2.3 You may not use the SDK and may not accept the License Agreement if you are a person barred from receiving the SDK under the laws of the United States or other countries, including the country in which you are resident or from which you use the SDK.\n\n2.4 If you are agreeing to be bound by the License Agreement on behalf of your employer or other entity, you represent and warrant that you have full legal authority to bind your employer or such entity to the License Agreement. If you do not have the requisite authority, you may not accept the License Agreement or use the SDK on behalf of your employer or other entity.\n\n\n3. SDK License from Google\n\n3.1 Subject to the terms of the License Agreement, Google grants you a limited, worldwide, royalty-free, non-assignable, non-exclusive, and non-sublicensable license to use the SDK solely to develop applications for compatible implementations of Android.\n\n3.2 You may not use this SDK to develop applications for other platforms (including non-compatible implementations of Android) or to develop another SDK. You are of course free to develop applications for other platforms, including non-compatible implementations of Android, provided that this SDK is not used for that purpose.\n\n3.3 You agree that Google or third parties own all legal right, title and interest in and to the SDK, including any Intellectual Property Rights that subsist in the SDK. "Intellectual Property Rights" means any and all rights under patent law, copyright law, trade secret law, trademark law, and any and all other proprietary rights. Google reserves all rights not expressly granted to you.\n\n3.4 You may not use the SDK for any purpose not expressly permitted by the License Agreement.  Except to the extent required by applicable third party licenses, you may not\: (a) copy (except for backup purposes), modify, adapt, redistribute, decompile, reverse engineer, disassemble, or create derivative works of the SDK or any part of the SDK; or (b) load any part of the SDK onto a mobile handset or any other hardware device except a personal computer, combine any part of the SDK with other software, or distribute any software or device incorporating a part of the SDK.\n\n3.5 Use, reproduction and distribution of components of the SDK licensed under an open source software license are governed solely by the terms of that open source software license and not the License Agreement.\n\n3.6 You agree that the form and nature of the SDK that Google provides may change without prior notice to you and that future versions of the SDK may be incompatible with applications developed on previous versions of the SDK. You agree that Google may stop (permanently or temporarily) providing the SDK (or any features within the SDK) to you or to users generally at Google's sole discretion, without prior notice to you.\n\n3.7 Nothing in the License Agreement gives you a right to use any of Google's trade names, trademarks, service marks, logos, domain names, or other distinctive brand features.\n\n3.8 You agree that you will not remove, obscure, or alter any proprietary rights notices (including copyright and trademark notices) that may be affixed to or contained within the SDK.\n\n\n4. Use of the SDK by You\n\n4.1 Google agrees that it obtains no right, title or interest from you (or your licensors) under the License Agreement in or to any software applications that you develop using the SDK, including any intellectual property rights that subsist in those applications.\n\n4.2 You agree to use the SDK and write applications only for purposes that are permitted by (a) the License Agreement and (b) any applicable law, regulation or generally accepted practices or guidelines in the relevant jurisdictions (including any laws regarding the export of data or software to and from the United States or other relevant countries).\n\n4.3 You agree that if you use the SDK to develop applications for general public users, you will protect the privacy and legal rights of those users. If the users provide you with user names, passwords, or other login information or personal information, you must make the users aware that the information will be available to your application, and you must provide legally adequate privacy notice and protection for those users. If your application stores personal or sensitive information provided by users, it must do so securely. If the user provides your application with Google Account information, your application may only use that information to access the user's Google Account when, and for the limited purposes for which, the user has given you permission to do so.\n\n4.4 You agree that you will not engage in any activity with the SDK, including the development or distribution of an application, that interferes with, disrupts, damages, or accesses in an unauthorized manner the servers, networks, or other properties or services of any third party including, but not limited to, Google or any mobile communications carrier.\n\n4.5 You agree that you are solely responsible for (and that Google has no responsibility to you or to any third party for) any data, content, or resources that you create, transmit or display through Android and/or applications for Android, and for the consequences of your actions (including any loss or damage which Google may suffer) by doing so.\n\n4.6 You agree that you are solely responsible for (and that Google has no responsibility to you or to any third party for) any breach of your obligations under the License Agreement, any applicable third party contract or Terms of Service, or any applicable law or regulation, and for the consequences (including any loss or damage which Google or any third party may suffer) of any such breach.\n\n5. Your Developer Credentials\n\n5.1 You agree that you are responsible for maintaining the confidentiality of any developer credentials that may be issued to you by Google or which you may choose yourself and that you will be solely responsible for all applications that are developed under your developer credentials.\n\n6. Privacy and Information\n\n6.1 In order to continually innovate and improve the SDK, Google may collect certain usage statistics from the software including but not limited to a unique identifier, associated IP address, version number of the software, and information on which tools and/or services in the SDK are being used and how they are being used. Before any of this information is collected, the SDK will notify you and seek your consent. If you withhold consent, the information will not be collected.\n\n6.2 The data collected is examined in the aggregate to improve the SDK and is maintained in accordance with Google's Privacy Policy.\n\n\n7. Third Party Applications\n\n7.1 If you use the SDK to run applications developed by a third party or that access data, content or resources provided by a third party, you agree that Google is not responsible for those applications, data, content, or resources. You understand that all data, content or resources which you may access through such third party applications are the sole responsibility of the person from which they originated and that Google is not liable for any loss or damage that you may experience as a result of the use or access of any of those third party applications, data, content, or resources.\n\n7.2 You should be aware the data, content, and resources presented to you through such a third party application may be protected by intellectual property rights which are owned by the providers (or by other persons or companies on their behalf). You may not modify, rent, lease, loan, sell, distribute or create derivative works based on these data, content, or resources (either in whole or in part) unless you have been specifically given permission to do so by the relevant owners.\n\n7.3 You acknowledge that your use of such third party applications, data, content, or resources may be subject to separate terms between you and the relevant third party. In that case, the License Agreement does not affect your legal relationship with these third parties.\n\n\n8. Using Android APIs\n\n8.1 Google Data APIs\n\n8.1.1 If you use any API to retrieve data from Google, you acknowledge that the data may be protected by intellectual property rights which are owned by Google or those parties that provide the data (or by other persons or companies on their behalf). Your use of any such API may be subject to additional Terms of Service. You may not modify, rent, lease, loan, sell, distribute or create derivative works based on this data (either in whole or in part) unless allowed by the relevant Terms of Service.\n\n8.1.2 If you use any API to retrieve a user's data from Google, you acknowledge and agree that you shall retrieve data only with the user's explicit consent and only when, and for the limited purposes for which, the user has given you permission to do so.\n\n\n9. Terminating the License Agreement\n\n9.1 The License Agreement will continue to apply until terminated by either you or Google as set out below.\n\n9.2 If you want to terminate the License Agreement, you may do so by ceasing your use of the SDK and any relevant developer credentials.\n\n9.3 Google may at any time, terminate the License Agreement with you if\:\n(A) you have breached any provision of the License Agreement; or\n(B) Google is required to do so by law; or\n(C) the partner with whom Google offered certain parts of SDK (such as APIs) to you has terminated its relationship with Google or ceased to offer certain parts of the SDK to you; or\n(D) Google decides to no longer provide the SDK or certain parts of the SDK to users in the country in which you are resident or from which you use the service, or the provision of the SDK or certain SDK services to you by Google is, in Google's sole discretion, no longer commercially viable.\n\n9.4 When the License Agreement comes to an end, all of the legal rights, obligations and liabilities that you and Google have benefited from, been subject to (or which have accrued over time whilst the License Agreement has been in force) or which are expressed to continue indefinitely, shall be unaffected by this cessation, and the provisions of paragraph 14.7 shall continue to apply to such rights, obligations and liabilities indefinitely.\n\n\n10. DISCLAIMER OF WARRANTIES\n\n10.1 YOU EXPRESSLY UNDERSTAND AND AGREE THAT YOUR USE OF THE SDK IS AT YOUR SOLE RISK AND THAT THE SDK IS PROVIDED "AS IS" AND "AS AVAILABLE" WITHOUT WARRANTY OF ANY KIND FROM GOOGLE.\n\n10.2 YOUR USE OF THE SDK AND ANY MATERIAL DOWNLOADED OR OTHERWISE OBTAINED THROUGH THE USE OF THE SDK IS AT YOUR OWN DISCRETION AND RISK AND YOU ARE SOLELY RESPONSIBLE FOR ANY DAMAGE TO YOUR COMPUTER SYSTEM OR OTHER DEVICE OR LOSS OF DATA THAT RESULTS FROM SUCH USE.\n\n10.3 GOOGLE FURTHER EXPRESSLY DISCLAIMS ALL WARRANTIES AND CONDITIONS OF ANY KIND, WHETHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.\n\n\n11. LIMITATION OF LIABILITY\n\n11.1 YOU EXPRESSLY UNDERSTAND AND AGREE THAT GOOGLE, ITS SUBSIDIARIES AND AFFILIATES, AND ITS LICENSORS SHALL NOT BE LIABLE TO YOU UNDER ANY THEORY OF LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL OR EXEMPLARY DAMAGES THAT MAY BE INCURRED BY YOU, INCLUDING ANY LOSS OF DATA, WHETHER OR NOT GOOGLE OR ITS REPRESENTATIVES HAVE BEEN ADVISED OF OR SHOULD HAVE BEEN AWARE OF THE POSSIBILITY OF ANY SUCH LOSSES ARISING.\n\n\n12. Indemnification\n\n12.1 To the maximum extent permitted by law, you agree to defend, indemnify and hold harmless Google, its affiliates and their respective directors, officers, employees and agents from and against any and all claims, actions, suits or proceedings, as well as any and all losses, liabilities, damages, costs and expenses (including reasonable attorneys fees) arising out of or accruing from (a) your use of the SDK, (b) any application you develop on the SDK that infringes any copyright, trademark, trade secret, trade dress, patent or other intellectual property right of any person or defames any person or violates their rights of publicity or privacy, and (c) any non-compliance by you with the License Agreement.\n\n\n13. Changes to the License Agreement\n\n13.1 Google may make changes to the License Agreement as it distributes new versions of the SDK. When these changes are made, Google will make a new version of the License Agreement available on the website where the SDK is made available.\n\n\n14. General Legal Terms\n\n14.1 The License Agreement constitutes the whole legal agreement between you and Google and governs your use of the SDK (excluding any services which Google may provide to you under a separate written agreement), and completely replaces any prior agreements between you and Google in relation to the SDK.\n\n14.2 You agree that if Google does not exercise or enforce any legal right or remedy which is contained in the License Agreement (or which Google has the benefit of under any applicable law), this will not be taken to be a formal waiver of Google's rights and that those rights or remedies will still be available to Google.\n\n14.3 If any court of law, having the jurisdiction to decide on this matter, rules that any provision of the License Agreement is invalid, then that provision will be removed from the License Agreement without affecting the rest of the License Agreement. The remaining provisions of the License Agreement will continue to be valid and enforceable.\n\n14.4 You acknowledge and agree that each member of the group of companies of which Google is the parent shall be third party beneficiaries to the License Agreement and that such other companies shall be entitled to directly enforce, and rely upon, any provision of the License Agreement that confers a benefit on (or rights in favor of) them. Other than this, no other person or company shall be third party beneficiaries to the License Agreement.\n\n14.5 EXPORT RESTRICTIONS. THE SDK IS SUBJECT TO UNITED STATES EXPORT LAWS AND REGULATIONS. YOU MUST COMPLY WITH ALL DOMESTIC AND INTERNATIONAL EXPORT LAWS AND REGULATIONS THAT APPLY TO THE SDK. THESE LAWS INCLUDE RESTRICTIONS ON DESTINATIONS, END USERS AND END USE.\n\n14.6 The rights granted in the License Agreement may not be assigned or transferred by either you or Google without the prior written approval of the other party. Neither you nor Google shall be permitted to delegate their responsibilities or obligations under the License Agreement without the prior written approval of the other party.\n\n14.7 The License Agreement, and your relationship with Google under the License Agreement, shall be governed by the laws of the State of California without regard to its conflict of laws provisions. You and Google agree to submit to the exclusive jurisdiction of the courts located within the county of Santa Clara, California to resolve any legal matter arising from the License Agreement. Notwithstanding this, you agree that Google shall still be allowed to apply for injunctive remedies (or an equivalent type of urgent legal relief) in any jurisdiction.\n\n\nNovember 20, 2015
-Pkg.LicenseRef=android-sdk-license
-Pkg.Revision=25.0.3
-Pkg.SourceUrl=https\://dl.google.com/android/repository/repository-11.xml
+Pkg.UserSrc=false
+Pkg.Revision=26.0.0
+#Pkg.Revision=26.0.0 rc2
diff --git a/sdk/platform-tools/sqlite3 b/sdk/platform-tools/sqlite3
index dca436b..db7992c 100755
--- a/sdk/platform-tools/sqlite3
+++ b/sdk/platform-tools/sqlite3
Binary files differ
diff --git a/sdk/platform-tools/systrace/UPSTREAM_REVISION b/sdk/platform-tools/systrace/UPSTREAM_REVISION
index 19f2117..43123c5 100644
--- a/sdk/platform-tools/systrace/UPSTREAM_REVISION
+++ b/sdk/platform-tools/systrace/UPSTREAM_REVISION
@@ -1 +1 @@
-be85e5fd4bf5793777543eded8f53b47c4241386
+c69b78719398903e6916f572bd7383afbbb2cdb9
diff --git a/sdk/platform-tools/systrace/catapult/common/battor/battor/battor_binary_dependencies.json b/sdk/platform-tools/systrace/catapult/common/battor/battor/battor_binary_dependencies.json
index f4b2871..a47d7ec 100644
--- a/sdk/platform-tools/systrace/catapult/common/battor/battor/battor_binary_dependencies.json
+++ b/sdk/platform-tools/systrace/catapult/common/battor/battor/battor_binary_dependencies.json
@@ -1,46 +1,114 @@
 {
   "config_type": "BaseConfig",
   "dependencies": {
+    "avrdude_binary": {
+      "cloud_storage_base_folder": "binary_dependencies/battor",
+      "cloud_storage_bucket": "chromium-telemetry",
+      "file_info": {
+        "darwin_x86_64": {
+          "cloud_storage_hash": "6de6324c279ea75c79c68cab4c2ddcc68da1b286",
+          "download_path": "../bin/darwin/x86_64/avrdude",
+          "local_paths": [
+            "../bin/override/avrdude"
+          ]
+        },
+        "linux2_x86_64": {
+          "cloud_storage_hash": "db29526605f6f95a75ab33f4060b8c330152de69",
+          "download_path": "../bin/linux2/x86_64/avrdude",
+          "local_paths": [
+            "../bin/override/avrdude"
+          ]
+        },
+        "win32_AMD64": {
+          "cloud_storage_hash": "517aa73b093e254007076cf5ac7afb94151df2ed",
+          "download_path": "../bin/win/x86_64/avrdude.exe",
+          "local_paths": [
+            "../bin/override/avrdude.exe"
+          ]
+        },
+        "win32_x86": {
+          "cloud_storage_hash": "517aa73b093e254007076cf5ac7afb94151df2ed",
+          "download_path": "../bin/win/x86_64/avrdude.exe",
+          "local_paths": [
+            "../bin/override/avrdude.exe"
+          ]
+        },
+        "win_AMD64": {
+          "cloud_storage_hash": "517aa73b093e254007076cf5ac7afb94151df2ed",
+          "download_path": "../bin/win/x86_64/avrdude.exe",
+          "local_paths": [
+            "../bin/override/avrdude.exe"
+          ]
+        }
+      }
+    },
+    "avrdude_config": {
+      "cloud_storage_base_folder": "binary_dependencies/battor",
+      "cloud_storage_bucket": "chromium-telemetry",
+      "file_info": {
+        "default": {
+          "cloud_storage_hash": "ccdfa12743429b8b92b61a20163d6311ab55a4fa",
+          "download_path": "../bin/battor/avrdude.conf",
+          "local_paths": [
+            "../bin/override/avrdude.conf"
+          ]
+        }
+      }
+    },
     "battor_agent_binary": {
       "cloud_storage_base_folder": "binary_dependencies/battor",
       "cloud_storage_bucket": "chromium-telemetry",
       "file_info": {
-        "linux2_x86_64": {
-          "cloud_storage_hash": "136ed6d947e7edcf82e212f6a0cdddffbbe9a8d5",
-          "download_path": "../bin/linux2/x86_64/battor_agent",
-          "local_paths": [
-            "../bin/override/battor_agent"
-          ]
-        },
         "darwin_x86_64": {
-          "cloud_storage_hash": "6923bb851fe57f5880d5dac78add6939134e5514",
+          "cloud_storage_hash": "ea35fa0b56cf1e2e774218498b244be167c2d61c",
           "download_path": "../bin/darwin/x86_64/battor_agent",
           "local_paths": [
             "../bin/override/battor_agent"
           ]
         },
+        "linux2_x86_64": {
+          "cloud_storage_hash": "7280345d3c31e1185c03492082a98b2eb4dccaae",
+          "download_path": "../bin/linux2/x86_64/battor_agent",
+          "local_paths": [
+            "../bin/override/battor_agent"
+          ]
+        },
         "win32_AMD64": {
-          "cloud_storage_hash": "65e728f774c25958ccff2ec4613ca387413e26b5",
+          "cloud_storage_hash": "e1b3630f94bbacb58b2d254543ee94073f072dc0",
           "download_path": "../bin/win/AMD64/battor_agent.exe",
           "local_paths": [
             "../bin/override/battor_agent.exe"
           ]
         },
         "win32_x86": {
-          "cloud_storage_hash": "65e728f774c25958ccff2ec4613ca387413e26b5",
+          "cloud_storage_hash": "e1b3630f94bbacb58b2d254543ee94073f072dc0",
           "download_path": "../bin/win/x86_64/battor_agent.exe",
           "local_paths": [
             "../bin/override/battor_agent.exe"
           ]
         },
         "win_AMD64": {
-          "cloud_storage_hash": "65e728f774c25958ccff2ec4613ca387413e26b5",
+          "cloud_storage_hash": "e1b3630f94bbacb58b2d254543ee94073f072dc0",
           "download_path": "../bin/win/AMD64/battor_agent.exe",
           "local_paths": [
             "../bin/override/battor_agent.exe"
           ]
         }
       }
+    },
+    "battor_firmware": {
+      "cloud_storage_base_folder": "binary_dependencies/battor",
+      "cloud_storage_bucket": "chromium-telemetry",
+      "file_info": {
+        "default": {
+          "cloud_storage_hash": "da987f879c341bf2f928dc0f65cc6477c2d32bbc",
+          "download_path": "../bin/battor/battor_firmware.hex",
+          "local_paths": [
+            "../bin/override/battor_firmware.hex"
+          ],
+          "version_in_cs": "3c3ce4d"
+        }
+      }
     }
   }
 }
diff --git a/sdk/platform-tools/systrace/catapult/common/battor/battor/battor_error.py b/sdk/platform-tools/systrace/catapult/common/battor/battor/battor_error.py
index 148611e..3ea6efc 100644
--- a/sdk/platform-tools/systrace/catapult/common/battor/battor/battor_error.py
+++ b/sdk/platform-tools/systrace/catapult/common/battor/battor/battor_error.py
@@ -4,5 +4,5 @@
 
 from devil import base_error
 
-class BattorError(base_error.BaseError):
+class BattOrError(base_error.BaseError):
   pass
diff --git a/sdk/platform-tools/systrace/catapult/common/battor/battor/battor_wrapper.py b/sdk/platform-tools/systrace/catapult/common/battor/battor/battor_wrapper.py
index f1f37d4..88b258a 100644
--- a/sdk/platform-tools/systrace/catapult/common/battor/battor/battor_wrapper.py
+++ b/sdk/platform-tools/systrace/catapult/common/battor/battor/battor_wrapper.py
@@ -14,6 +14,7 @@
 import time
 
 from battor import battor_error
+import py_utils
 from py_utils import cloud_storage
 import dependency_manager
 from devil.utils import battor_device_mapping
@@ -23,6 +24,9 @@
 from serial.tools import list_ports
 
 
+DEFAULT_SHELL_CLOSE_TIMEOUT_S = 60
+
+
 def IsBattOrConnected(test_platform, android_device=None,
                       android_device_map=None, android_device_file=None):
   """Returns True if BattOr is detected."""
@@ -33,7 +37,7 @@
 
     if not android_device_map:
       device_tree = find_usb_devices.GetBusNumberToDeviceTreeMap()
-      if len(battor_device_mapping.GetBattorList(device_tree)) == 1:
+      if len(battor_device_mapping.GetBattOrList(device_tree)) == 1:
         return True
       if android_device_file:
         android_device_map = battor_device_mapping.ReadSerialMapFile(
@@ -41,7 +45,7 @@
       else:
         try:
           android_device_map = battor_device_mapping.GenerateSerialMap()
-        except battor_error.BattorError:
+        except battor_error.BattOrError:
           return False
 
     # If neither if statement above is triggered, it means that an
@@ -64,21 +68,29 @@
 
   elif test_platform == 'linux':
     device_tree = find_usb_devices.GetBusNumberToDeviceTreeMap(fast=True)
-    return bool(battor_device_mapping.GetBattorList(device_tree))
+    return bool(battor_device_mapping.GetBattOrList(device_tree))
 
   return False
 
 
-class BattorWrapper(object):
+class BattOrWrapper(object):
   """A class for communicating with a BattOr in python."""
+  _EXIT_CMD = 'Exit'
+  _GET_FIRMWARE_GIT_HASH_CMD = 'GetFirmwareGitHash'
   _START_TRACING_CMD = 'StartTracing'
   _STOP_TRACING_CMD = 'StopTracing'
   _SUPPORTS_CLOCKSYNC_CMD = 'SupportsExplicitClockSync'
   _RECORD_CLOCKSYNC_CMD = 'RecordClockSyncMarker'
   _SUPPORTED_PLATFORMS = ['android', 'chromeos', 'linux', 'mac', 'win']
 
+  _SUPPORTED_AUTOFLASHING_PLATFORMS = ['linux', 'mac', 'win']
+  _BATTOR_PARTNO = 'x192a3u'
+  _BATTOR_PROGRAMMER = 'avr109'
+  _BATTOR_BAUDRATE = '115200'
+
   def __init__(self, target_platform, android_device=None, battor_path=None,
-               battor_map_file=None, battor_map=None, serial_log_bucket=None):
+               battor_map_file=None, battor_map=None, serial_log_bucket=None,
+               autoflash=True):
     """Constructor.
 
     Args:
@@ -95,24 +107,25 @@
       _battor_agent_binary: Path to the BattOr agent binary used to communicate
         with the BattOr.
       _tracing: A bool saying if tracing has been started.
-      _battor_shell: A subprocess running the bator_agent_binary
+      _battor_shell: A subprocess running the battor_agent_binary
       _trace_results_path: Path to BattOr trace results file.
       _serial_log_bucket: Cloud storage bucket to which BattOr agent serial logs
         are uploaded on failure.
       _serial_log_file: Temp file for the BattOr agent serial log.
     """
-    self._battor_path = self._GetBattorPath(target_platform, android_device,
+    self._battor_path = self._GetBattOrPath(target_platform, android_device,
         battor_path, battor_map_file, battor_map)
     config = os.path.join(
         os.path.dirname(os.path.abspath(__file__)),
         'battor_binary_dependencies.json')
 
-    dm = dependency_manager.DependencyManager(
+    self._dm = dependency_manager.DependencyManager(
         [dependency_manager.BaseConfig(config)])
-    self._battor_agent_binary = dm.FetchPath(
+    self._battor_agent_binary = self._dm.FetchPath(
         'battor_agent_binary', '%s_%s' % (sys.platform, platform.machine()))
-    self._serial_log_bucket = serial_log_bucket
 
+    self._autoflash = autoflash
+    self._serial_log_bucket = serial_log_bucket
     self._tracing = False
     self._battor_shell = None
     self._trace_results_path = None
@@ -120,9 +133,42 @@
     self._stop_tracing_time = None
     self._trace_results = None
     self._serial_log_file = None
+    self._target_platform = target_platform
+    self._git_hash = None
 
     atexit.register(self.KillBattOrShell)
 
+  def _FlashBattOr(self):
+    assert self._battor_shell, (
+        'Must start shell before attempting to flash BattOr')
+
+    try:
+      device_git_hash = self.GetFirmwareGitHash()
+      battor_firmware, cs_git_hash = self._dm.FetchPathWithVersion(
+          'battor_firmware', 'default')
+      if cs_git_hash != device_git_hash:
+        logging.info(
+            'Flashing BattOr with old firmware version <%s> with new '
+            'version <%s>.', device_git_hash, cs_git_hash)
+        avrdude_config = self._dm.FetchPath('avrdude_config', 'default')
+        self.StopShell()
+        return self.FlashFirmware(battor_firmware, avrdude_config)
+      return False
+    except ValueError:
+      logging.exception('Git hash returned from BattOr was not as expected: %s'
+                        % self._git_hash)
+
+    finally:
+      if not self._battor_shell:
+        # TODO(charliea): Once we understand why BattOrs are crashing, remove
+        # this log.
+        # http://crbug.com/699581
+        logging.info('_FlashBattOr serial log:')
+        self._UploadSerialLogToCloudStorage()
+        self._serial_log_file = None
+
+        self.StartShell()
+
   def KillBattOrShell(self):
     if self._battor_shell:
       logging.critical('BattOr shell was not properly closed. Killing now.')
@@ -130,29 +176,45 @@
 
   def GetShellReturnCode(self):
     """Gets the return code of the BattOr agent shell."""
-    # TODO(rnephew): Get rid of logging after crbug.com/645106 is fixed.
-    logging.critical('Finding return code for BattOr shell.')
     rc = self._battor_shell.poll()
-    logging.critical('Found return code: %s' % rc)
     return rc
 
   def StartShell(self):
     """Start BattOr binary shell."""
     assert not self._battor_shell, 'Attempting to start running BattOr shell.'
+
     battor_cmd = [self._battor_agent_binary]
     if self._serial_log_bucket:
-      self._serial_log_file = tempfile.NamedTemporaryFile()
+      # Create and immediately close a temp file in order to get a filename
+      # for the serial log.
+      self._serial_log_file = tempfile.NamedTemporaryFile(delete=False)
+      self._serial_log_file.close()
       battor_cmd.append('--battor-serial-log=%s' % self._serial_log_file.name)
     if self._battor_path:
       battor_cmd.append('--battor-path=%s' % self._battor_path)
     self._battor_shell = self._StartShellImpl(battor_cmd)
     assert self.GetShellReturnCode() is None, 'Shell failed to start.'
 
+  def StopShell(self, timeout=None):
+    """Stop BattOr binary shell."""
+    assert self._battor_shell, 'Attempting to stop a non-running BattOr shell.'
+    assert not self._tracing, 'Attempting to stop a BattOr shell while tracing.'
+    timeout = timeout if timeout else DEFAULT_SHELL_CLOSE_TIMEOUT_S
+
+    self._SendBattOrCommand(self._EXIT_CMD, check_return=False)
+    try:
+      py_utils.WaitFor(lambda: self.GetShellReturnCode() != None, timeout)
+    except py_utils.TimeoutException:
+      self.KillBattOrShell()
+    finally:
+      self._battor_shell = None
+
   def StartTracing(self):
     """Start tracing on the BattOr."""
     assert self._battor_shell, 'Must start shell before tracing'
     assert not self._tracing, 'Tracing already started.'
-    self._SendBattorCommand(self._START_TRACING_CMD)
+    self._FlashBattOr()
+    self._SendBattOrCommand(self._START_TRACING_CMD)
     self._tracing = True
     self._start_tracing_time = int(time.time())
 
@@ -163,7 +225,7 @@
     temp_file = tempfile.NamedTemporaryFile(delete=False)
     self._trace_results_path = temp_file.name
     temp_file.close()
-    self._SendBattorCommand(
+    self._SendBattOrCommand(
         '%s %s' % (self._STOP_TRACING_CMD, self._trace_results_path),
         check_return=False)
     self._tracing = False
@@ -180,8 +242,11 @@
     if timeout is None:
       timeout = self._stop_tracing_time - self._start_tracing_time
 
-    if self.GetShellReturnCode() == 1:
-      self._UploadSerialLogToCloudStorage()
+    # TODO(charliea): Once we understand why BattOrs are crashing, only do
+    # this on failure.
+    # http://crbug.com/699581
+    logging.info('CollectTraceData serial log:')
+    self._UploadSerialLogToCloudStorage()
 
     with open(self._trace_results_path) as results:
       self._trace_results = results.read()
@@ -191,20 +256,20 @@
 
   def SupportsExplicitClockSync(self):
     """Returns if BattOr supports Clock Sync events."""
-    return bool(int(self._SendBattorCommand(self._SUPPORTS_CLOCKSYNC_CMD,
+    return bool(int(self._SendBattOrCommand(self._SUPPORTS_CLOCKSYNC_CMD,
                                             check_return=False)))
 
   def RecordClockSyncMarker(self, sync_id):
     """Record clock sync event on BattOr."""
     if not isinstance(sync_id, basestring):
       raise TypeError('sync_id must be a string.')
-    self._SendBattorCommand('%s %s' % (self._RECORD_CLOCKSYNC_CMD, sync_id))
+    self._SendBattOrCommand('%s %s' % (self._RECORD_CLOCKSYNC_CMD, sync_id))
 
-  def _GetBattorPath(self, target_platform, android_device=None,
+  def _GetBattOrPath(self, target_platform, android_device=None,
                      battor_path=None, battor_map_file=None, battor_map=None):
     """Determines most likely path to the correct BattOr."""
     if target_platform not in self._SUPPORTED_PLATFORMS:
-      raise battor_error.BattorError(
+      raise battor_error.BattOrError(
           '%s is an unsupported platform.' % target_platform)
     if target_platform in ['win']:
       # Right now, the BattOr agent binary isn't able to automatically detect
@@ -214,7 +279,7 @@
       for (port, desc, _) in serial.tools.list_ports.comports():
         if 'USB Serial Port' in desc:
           return port
-      raise battor_error.BattorError(
+      raise battor_error.BattOrError(
           'Could not find BattOr attached to machine.')
     if target_platform in ['mac']:
       for (port, desc, _) in serial.tools.list_ports.comports():
@@ -225,26 +290,26 @@
       device_tree = find_usb_devices.GetBusNumberToDeviceTreeMap(fast=True)
       if battor_path:
         if not isinstance(battor_path, basestring):
-          raise battor_error.BattorError(
+          raise battor_error.BattOrError(
               'An invalid BattOr path was specified.')
         return battor_path
 
       if target_platform == 'android':
         if not android_device:
-          raise battor_error.BattorError(
+          raise battor_error.BattOrError(
               'Must specify device for Android platform.')
         if not battor_map_file and not battor_map:
           # No map was passed, so must create one.
           battor_map = battor_device_mapping.GenerateSerialMap()
 
-        return battor_device_mapping.GetBattorPathFromPhoneSerial(
+        return battor_device_mapping.GetBattOrPathFromPhoneSerial(
             str(android_device), serial_map_file=battor_map_file,
             serial_map=battor_map)
 
       # Not Android and no explicitly passed BattOr.
-      battors = battor_device_mapping.GetBattorList(device_tree)
+      battors = battor_device_mapping.GetBattOrList(device_tree)
       if len(battors) != 1:
-        raise battor_error.BattorError(
+        raise battor_error.BattOrError(
             'For non-Android platforms, exactly one BattOr must be '
             'attached unless address is explicitly given.')
       return '/dev/%s' % battors.pop()
@@ -252,19 +317,20 @@
     raise NotImplementedError(
         'BattOr Wrapper not implemented for given platform')
 
-  def _SendBattorCommandImpl(self, cmd):
+  def _SendBattOrCommandImpl(self, cmd):
     """Sends command to the BattOr."""
     self._battor_shell.stdin.write('%s\n' % cmd)
     self._battor_shell.stdin.flush()
     return self._battor_shell.stdout.readline()
 
-  def _SendBattorCommand(self, cmd, check_return=True):
-    status = self._SendBattorCommandImpl(cmd)
+  def _SendBattOrCommand(self, cmd, check_return=True):
+    status = self._SendBattOrCommandImpl(cmd)
 
     if check_return and not 'Done.' in status:
+      self.KillBattOrShell()
       self._UploadSerialLogToCloudStorage()
       self._serial_log_file = None
-      raise battor_error.BattorError(
+      raise battor_error.BattOrError(
           'BattOr did not complete command \'%s\' correctly.\n'
           'Outputted: %s' % (cmd, status))
     return status
@@ -290,3 +356,60 @@
     except cloud_storage.PermissionError as e:
       logging.error('Cannot upload BattOr serial log file to cloud storage due '
                     'to permission error: %s' % e.message)
+
+  def GetFirmwareGitHash(self):
+    """Gets the git hash for the BattOr firmware.
+
+    Returns: Git hash for firmware currently on the BattOr.
+        Also sets self._git_hash to this value.
+
+    Raises: ValueException if the git hash is not in hex.
+    """
+    assert self._battor_shell, ('Must start shell before getting firmware git '
+                                'hash')
+    self._git_hash = self._SendBattOrCommand(self._GET_FIRMWARE_GIT_HASH_CMD,
+                                       check_return=False).strip()
+    # We expect the git hash to be a valid 6 character hexstring. This will
+    # throw a ValueError exception otherwise.
+    int(self._git_hash, 16)
+    return self._git_hash
+
+  def FlashFirmware(self, hex_path, avrdude_config_path):
+    """Flashes the BattOr using an avrdude config at config_path with the new
+       firmware at hex_path.
+    """
+    assert not self._battor_shell, 'Cannot flash BattOr with open shell'
+    if self._target_platform not in self._SUPPORTED_AUTOFLASHING_PLATFORMS:
+      logging.critical('Flashing firmware on this platform is not supported.')
+      return False
+
+    avrdude_binary = self._dm.FetchPath(
+        'avrdude_binary', '%s_%s' % (sys.platform, platform.machine()))
+    # Sanitize hex file path for windows. It contains <drive>:/ which avrdude
+    # is not capable of handling.
+    _, hex_path = os.path.splitdrive(hex_path)
+    avr_cmd = [
+        avrdude_binary,
+        '-e',  # Specify to erase data on chip.
+        '-p', self._BATTOR_PARTNO,  # Specify AVR device.
+        # Specify which microcontroller programmer to use.
+        '-c', self._BATTOR_PROGRAMMER,
+        '-b', self._BATTOR_BAUDRATE,  # Specify the baud rate to communicate at.
+        '-P', self._battor_path,  # Serial path to the battor.
+        # Command to execute with hex file and path to hex file.
+        '-U', 'flash:w:%s' % hex_path,
+        '-C', avrdude_config_path, # AVRdude config file path.
+        '2>&1'  # All output goes to stderr for some reason.
+    ]
+    try:
+      subprocess.check_output(avr_cmd)
+    except subprocess.CalledProcessError as e:
+      raise BattOrFlashError('BattOr flash failed with return code %s.'
+                             % e.returncode)
+
+    self._git_hash = None
+    return True
+
+
+class BattOrFlashError(Exception):
+  pass
diff --git a/sdk/platform-tools/systrace/catapult/common/battor/battor/battor_wrapper_devicetest.py b/sdk/platform-tools/systrace/catapult/common/battor/battor/battor_wrapper_devicetest.py
index b1ce240..9823787 100644
--- a/sdk/platform-tools/systrace/catapult/common/battor/battor/battor_wrapper_devicetest.py
+++ b/sdk/platform-tools/systrace/catapult/common/battor/battor/battor_wrapper_devicetest.py
@@ -20,7 +20,7 @@
 
 _SUPPORTED_CQ_PLATFORMS = ['win', 'linux', 'mac']
 
-class BattorWrapperDeviceTest(unittest.TestCase):
+class BattOrWrapperDeviceTest(unittest.TestCase):
   def setUp(self):
     test_platform = platform.system()
     self._battor_list = None
@@ -29,7 +29,7 @@
     elif 'Linux' in test_platform:
       self._platform = 'linux'
       device_tree  = find_usb_devices.GetBusNumberToDeviceTreeMap()
-      self._battor_list = battor_device_mapping.GetBattorList(device_tree)
+      self._battor_list = battor_device_mapping.GetBattOrList(device_tree)
     elif 'Darwin' in test_platform:
       self._platform = 'mac'
 
@@ -49,10 +49,18 @@
 
     battor_path = (None if not self._battor_list
                    else '/dev/%s' % self._battor_list[0])
-    battor = battor_wrapper.BattorWrapper(self._platform,
+    battor = battor_wrapper.BattOrWrapper(self._platform,
                                           battor_path=battor_path)
     try:
       battor.StartShell()
+      self.assertTrue(isinstance(battor.GetFirmwareGitHash(), basestring))
+      # We expect the git hash to be a valid 6 character hexstring. This will
+      # throw a ValueError exception otherwise.
+      int(battor.GetFirmwareGitHash(), 16)
+      self.assertTrue(len(battor.GetFirmwareGitHash()) == 7)
+      battor.StopShell()
+
+      battor.StartShell()
       battor.StartTracing()
       # TODO(rnephew): This sleep is required for now because crbug.com/602266
       # causes the BattOr to crash when the trace time is too short. Once that
@@ -65,7 +73,7 @@
 
       # Below is a work around for crbug.com/603309. On this short of a trace, 5
       # seconds is enough to ensure that the trace will finish flushing to the
-      # file. The process is then killed so that BattorWrapper knows that the
+      # file. The process is then killed so that BattOrWrapper knows that the
       # process has been closed after tracing stops.
       if self._platform == 'win':
         time.sleep(5)
@@ -76,6 +84,7 @@
         battor._battor_shell.kill()
         battor._battor_shell = None
       raise
+
     self.assertTrue('# BattOr' in results[0])
     self.assertTrue('# voltage_range' in results[1])
     self.assertTrue('# current_range' in results[2])
diff --git a/sdk/platform-tools/systrace/catapult/common/battor/battor/battor_wrapper_unittest.py b/sdk/platform-tools/systrace/catapult/common/battor/battor/battor_wrapper_unittest.py
index f946755..862c4d3 100644
--- a/sdk/platform-tools/systrace/catapult/common/battor/battor/battor_wrapper_unittest.py
+++ b/sdk/platform-tools/systrace/catapult/common/battor/battor/battor_wrapper_unittest.py
@@ -4,6 +4,8 @@
 
 import dependency_manager
 import logging
+import mock
+import subprocess
 import unittest
 
 from battor import battor_error
@@ -18,11 +20,15 @@
 class DependencyManagerMock(object):
   def __init__(self, _):
     self._fetch_return = 'path'
+    self._version_return = 'cbaa843'
 
   def FetchPath(self, _, *unused):
     del unused
     return self._fetch_return
 
+  def FetchPathWithVersion(self, _, *unused):
+    del unused
+    return self._fetch_return, self._version_return
 
 class PopenMock(object):
   def __init__(self, *unused):
@@ -31,6 +37,9 @@
   def poll(self):
     pass
 
+  def kill(self):
+    pass
+
 
 class IsBattOrConnectedTest(unittest.TestCase):
   def setUp(self):
@@ -55,8 +64,8 @@
     find_usb_devices.GetBusNumberToDeviceTreeMap = lambda fast=None: None
 
     self._get_battor_list_return = []
-    self._get_battor_list = battor_device_mapping.GetBattorList
-    battor_device_mapping.GetBattorList = lambda x: self._get_battor_list_return
+    self._get_battor_list = battor_device_mapping.GetBattOrList
+    battor_device_mapping.GetBattOrList = lambda x: self._get_battor_list_return
 
   def tearDown(self):
     serial.tools.list_ports.comports = self._comports
@@ -64,16 +73,16 @@
     battor_device_mapping.ReadSerialMapFile = self._read_serial_map_file
     find_usb_devices.GetBusNumberToDeviceTreeMap = (
         self._get_bus_number_to_device_tree_map)
-    battor_device_mapping.GetBattorList = self._get_battor_list
+    battor_device_mapping.GetBattOrList = self._get_battor_list
 
   def forceException(self):
     raise NotImplementedError
 
-  def testAndroidWithBattor(self):
+  def testAndroidWithBattOr(self):
     self._generate_serial_map_return = {'abc': '123'}
     self.assertTrue(battor_wrapper.IsBattOrConnected('android', 'abc'))
 
-  def testAndroidWithoutMatchingBattor(self):
+  def testAndroidWithoutMatchingBattOr(self):
     self._generate_serial_map_return = {'notabc': 'not123'}
     self.assertFalse(battor_wrapper.IsBattOrConnected('android', 'abc'))
 
@@ -100,160 +109,172 @@
       battor_wrapper.IsBattOrConnected('android', android_device='abc',
                                       android_device_file='file'))
 
-  def testLinuxWithBattor(self):
+  def testLinuxWithBattOr(self):
     self._get_battor_list_return = ['battor']
     self.assertTrue(battor_wrapper.IsBattOrConnected('linux'))
 
-  def testLinuxWithoutBattor(self):
+  def testLinuxWithoutBattOr(self):
     self._get_battor_list_return = []
     self.assertFalse(battor_wrapper.IsBattOrConnected('linux'))
 
-  def testMacWithBattor(self):
+  def testMacWithBattOr(self):
     self._serial_tools_return = [('/dev/tty.usbserial-MAA', 'BattOr v3.3', '')]
     self.assertTrue(battor_wrapper.IsBattOrConnected('mac'))
 
-  def testMacWithoutBattor(self):
+  def testMacWithoutBattOr(self):
     self._serial_tools_return = [('/dev/tty.usbserial-MAA', 'not_one', '')]
     self.assertFalse(battor_wrapper.IsBattOrConnected('mac'))
 
-  def testWinWithBattor(self):
+  def testWinWithBattOr(self):
     self._serial_tools_return = [('COM4', 'USB Serial Port', '')]
     self.assertTrue(battor_wrapper.IsBattOrConnected('win'))
 
-  def testWinWithoutBattor(self):
+  def testWinWithoutBattOr(self):
     self._get_battor_list_return = []
     self.assertFalse(battor_wrapper.IsBattOrConnected('win'))
 
 
-class BattorWrapperTest(unittest.TestCase):
+class BattOrWrapperTest(unittest.TestCase):
   def setUp(self):
     self._battor = None
     self._is_battor = True
     self._battor_list = ['battor1']
     self._should_pass = True
     self._fake_map = {'battor1': 'device1'}
+    self._fake_return_code = None
+    self._fake_battor_return = 'Done.\n'
 
     self._get_battor_path_from_phone_serial = (
-        battor_device_mapping.GetBattorPathFromPhoneSerial)
+        battor_device_mapping.GetBattOrPathFromPhoneSerial)
     self._get_bus_number_to_device_tree_map = (
         find_usb_devices.GetBusNumberToDeviceTreeMap)
     self._dependency_manager = dependency_manager.DependencyManager
-    self._get_battor_list = battor_device_mapping.GetBattorList
-    self._is_battor = battor_device_mapping.IsBattor
+    self._get_battor_list = battor_device_mapping.GetBattOrList
+    self._is_battor = battor_device_mapping.IsBattOr
     self._generate_serial_map = battor_device_mapping.GenerateSerialMap
     self._serial_tools = serial.tools.list_ports.comports
 
-
-    battor_device_mapping.GetBattorPathFromPhoneSerial = (
+    battor_device_mapping.GetBattOrPathFromPhoneSerial = (
         lambda x, serial_map_file=None, serial_map=None: x + '_battor')
     find_usb_devices.GetBusNumberToDeviceTreeMap = lambda fast=False: True
     dependency_manager.DependencyManager = DependencyManagerMock
-    battor_device_mapping.GetBattorList = lambda x: self._battor_list
-    battor_device_mapping.IsBattor = lambda x, y: self._is_battor
+    battor_device_mapping.GetBattOrList = lambda x: self._battor_list
+    battor_device_mapping.IsBattOr = lambda x, y: self._is_battor
     battor_device_mapping.GenerateSerialMap = lambda: self._fake_map
     serial.tools.list_ports.comports = lambda: [('COM4', 'USB Serial Port', '')]
 
+    self._subprocess_check_output_code = 0
+    def subprocess_check_output_mock(*unused):
+      if self._subprocess_check_output_code != 0:
+        raise subprocess.CalledProcessError(None, None)
+      return 0
+    self._subprocess_check_output = subprocess.check_output
+    subprocess.check_output = subprocess_check_output_mock
+
   def tearDown(self):
-    battor_device_mapping.GetBattorPathFromPhoneSerial = (
+    battor_device_mapping.GetBattOrPathFromPhoneSerial = (
         self._get_battor_path_from_phone_serial)
     find_usb_devices.GetBusNumberToDeviceTreeMap = (
         self._get_bus_number_to_device_tree_map)
     dependency_manager.DependencyManager = self._dependency_manager
-    battor_device_mapping.GetBattorList = self._get_battor_list
-    battor_device_mapping.IsBattor = self._is_battor
+    battor_device_mapping.GetBattOrList = self._get_battor_list
+    battor_device_mapping.IsBattOr = self._is_battor
     battor_device_mapping.GenerateSerialMap = self._generate_serial_map
     serial.tools.list_ports.comports = self._serial_tools
+    subprocess.check_output = self._subprocess_check_output
 
-  def _DefaultBattorReplacements(self):
+  def _DefaultBattOrReplacements(self):
+    battor_wrapper.DEFAULT_SHELL_CLOSE_TIMEOUT_S = .1
     self._battor._StartShellImpl = lambda *unused: PopenMock()
-    self._battor.GetShellReturnCode = lambda *unused: None
-    self._battor._SendBattorCommandImpl = lambda x: 'Done.\n'
-    self._battor._StopTracingImpl = lambda *unused: ('Done.\n', None)
+    self._battor.GetShellReturnCode = lambda *unused: self._fake_return_code
+    self._battor._SendBattOrCommandImpl = lambda x: self._fake_battor_return
+    self._battor._StopTracingImpl = lambda *unused: (self._fake_battor_return,
+                                                     None)
 
   def testBadPlatform(self):
-    with self.assertRaises(battor_error.BattorError):
-      self._battor = battor_wrapper.BattorWrapper('unknown')
+    with self.assertRaises(battor_error.BattOrError):
+      self._battor = battor_wrapper.BattOrWrapper('unknown')
 
-  def testInitAndroidWithBattor(self):
-    self._battor = battor_wrapper.BattorWrapper('android', android_device='abc')
+  def testInitAndroidWithBattOr(self):
+    self._battor = battor_wrapper.BattOrWrapper('android', android_device='abc')
     self.assertEquals(self._battor._battor_path, 'abc_battor')
 
-  def testInitAndroidWithoutBattor(self):
+  def testInitAndroidWithoutBattOr(self):
     self._battor_list = []
     self._fake_map = {}
-    battor_device_mapping.GetBattorPathFromPhoneSerial = (
+    battor_device_mapping.GetBattOrPathFromPhoneSerial = (
         self._get_battor_path_from_phone_serial)
-    with self.assertRaises(battor_error.BattorError):
-      self._battor = battor_wrapper.BattorWrapper('android',
+    with self.assertRaises(battor_error.BattOrError):
+      self._battor = battor_wrapper.BattOrWrapper('android',
                                                   android_device='abc')
 
-  def testInitBattorPathIsBattor(self):
+  def testInitBattOrPathIsBattOr(self):
     battor_path = 'battor/path/here'
-    self._battor = battor_wrapper.BattorWrapper(
+    self._battor = battor_wrapper.BattOrWrapper(
         'android', android_device='abc', battor_path=battor_path)
     self.assertEquals(self._battor._battor_path, battor_path)
 
-  def testInitNonAndroidWithBattor(self):
-    self._battor = battor_wrapper.BattorWrapper('win')
+  def testInitNonAndroidWithBattOr(self):
+    self._battor = battor_wrapper.BattOrWrapper('win')
     self.assertEquals(self._battor._battor_path, 'COM4')
 
-  def testInitNonAndroidWithMultipleBattor(self):
+  def testInitNonAndroidWithMultipleBattOr(self):
     self._battor_list.append('battor2')
-    with self.assertRaises(battor_error.BattorError):
-      self._battor = battor_wrapper.BattorWrapper('linux')
+    with self.assertRaises(battor_error.BattOrError):
+      self._battor = battor_wrapper.BattOrWrapper('linux')
 
-  def testInitNonAndroidWithoutBattor(self):
+  def testInitNonAndroidWithoutBattOr(self):
     self._battor_list = []
     serial.tools.list_ports.comports = lambda: [('COM4', 'None', '')]
-    with self.assertRaises(battor_error.BattorError):
-      self._battor = battor_wrapper.BattorWrapper('win')
+    with self.assertRaises(battor_error.BattOrError):
+      self._battor = battor_wrapper.BattOrWrapper('win')
 
   def testStartShellPass(self):
-    self._battor = battor_wrapper.BattorWrapper('win')
-    self._DefaultBattorReplacements()
+    self._battor = battor_wrapper.BattOrWrapper('win')
+    self._DefaultBattOrReplacements()
     self._battor.StartShell()
     self.assertIsNotNone(self._battor._battor_shell)
 
   def testStartShellDoubleStart(self):
-    self._battor = battor_wrapper.BattorWrapper('win')
-    self._DefaultBattorReplacements()
+    self._battor = battor_wrapper.BattOrWrapper('win')
+    self._DefaultBattOrReplacements()
     self._battor.StartShell()
     with self.assertRaises(AssertionError):
       self._battor.StartShell()
 
   def testStartShellFail(self):
-    self._battor = battor_wrapper.BattorWrapper('win')
-    self._DefaultBattorReplacements()
+    self._battor = battor_wrapper.BattOrWrapper('win')
+    self._DefaultBattOrReplacements()
     self._battor.GetShellReturnCode = lambda *unused: 1
     with self.assertRaises(AssertionError):
       self._battor.StartShell()
 
   def testStartTracingPass(self):
-    self._battor = battor_wrapper.BattorWrapper('win')
-    self._DefaultBattorReplacements()
+    self._battor = battor_wrapper.BattOrWrapper('win')
+    self._DefaultBattOrReplacements()
     self._battor.StartShell()
     self._battor.StartTracing()
     self.assertTrue(self._battor._tracing)
 
   def testStartTracingDoubleStart(self):
-    self._battor = battor_wrapper.BattorWrapper('win')
-    self._DefaultBattorReplacements()
+    self._battor = battor_wrapper.BattOrWrapper('win')
+    self._DefaultBattOrReplacements()
     self._battor.StartShell()
     self._battor.StartTracing()
     with self.assertRaises(AssertionError):
       self._battor.StartTracing()
 
   def testStartTracingCommandFails(self):
-    self._battor = battor_wrapper.BattorWrapper('win')
-    self._DefaultBattorReplacements()
-    self._battor._SendBattorCommandImpl = lambda *unused: 'Fail.\n'
+    self._battor = battor_wrapper.BattOrWrapper('win')
+    self._DefaultBattOrReplacements()
+    self._battor._SendBattOrCommandImpl = lambda *unused: 'Fail.\n'
     self._battor.StartShell()
-    with self.assertRaises(battor_error.BattorError):
+    with self.assertRaises(battor_error.BattOrError):
       self._battor.StartTracing()
 
   def testStopTracingPass(self):
-    self._battor = battor_wrapper.BattorWrapper('win')
-    self._DefaultBattorReplacements()
+    self._battor = battor_wrapper.BattOrWrapper('win')
+    self._DefaultBattOrReplacements()
     self._battor.StartShell()
     self._battor.StartTracing()
     self._battor.GetShellReturnCode = lambda *unused: 0
@@ -261,11 +282,89 @@
     self.assertFalse(self._battor._tracing)
 
   def testStopTracingNotRunning(self):
-    self._battor = battor_wrapper.BattorWrapper('win')
-    self._DefaultBattorReplacements()
+    self._battor = battor_wrapper.BattOrWrapper('win')
+    self._DefaultBattOrReplacements()
     with self.assertRaises(AssertionError):
       self._battor.StopTracing()
 
+  def testFlashFirmwarePass(self):
+    self._battor = battor_wrapper.BattOrWrapper('linux')
+    self._DefaultBattOrReplacements()
+    self.assertTrue(self._battor.FlashFirmware('hex_path', 'config_path'))
+
+  def testFlashFirmwareFail(self):
+    self._battor = battor_wrapper.BattOrWrapper('linux')
+    self._DefaultBattOrReplacements()
+    self._subprocess_check_output_code = 1
+    with self.assertRaises(battor_wrapper.BattOrFlashError):
+      self._battor.FlashFirmware('hex_path', 'config_path')
+
+  def testFlashFirmwarePlatformNotSupported(self):
+    self._battor = battor_wrapper.BattOrWrapper('win')
+    self._DefaultBattOrReplacements()
+    self._battor._target_platform = 'unsupported_platform'
+    self.assertFalse(self._battor.FlashFirmware('hex_path', 'config_path'))
+
+  def testFlashFirmwareShellRunning(self):
+    self._battor = battor_wrapper.BattOrWrapper('linux')
+    self._DefaultBattOrReplacements()
+    self._battor.StartShell()
+    with self.assertRaises(AssertionError):
+      self._battor.FlashFirmware('hex_path', 'config_path')
+
+  def testGetFirmwareGitHashNotRunning(self):
+    self._battor = battor_wrapper.BattOrWrapper('win')
+    self._DefaultBattOrReplacements()
+    with self.assertRaises(AssertionError):
+      self._battor.GetFirmwareGitHash()
+
+  def testGetFirmwareGitHashPass(self):
+    self._battor = battor_wrapper.BattOrWrapper('win')
+    self._DefaultBattOrReplacements()
+    self._battor.StartShell()
+    self._battor.GetFirmwareGitHash = lambda: 'cbaa843'
+    self.assertTrue(isinstance(self._battor.GetFirmwareGitHash(), basestring))
+
+  def testStopShellPass(self):
+    self._battor = battor_wrapper.BattOrWrapper('win')
+    self._DefaultBattOrReplacements()
+    self._battor.StartShell()
+    self._fake_return_code = 0
+    self._battor.StopShell()
+    self.assertIsNone(self._battor._battor_shell)
+
+  @mock.patch('time.sleep', mock.Mock)
+  def testStopShellTimeOutAndKill(self):
+    self._battor = battor_wrapper.BattOrWrapper('win')
+    self._DefaultBattOrReplacements()
+    self._battor.StartShell()
+    self._battor.StopShell()
+    self.assertIsNone(self._battor._battor_shell)
+
+  def testStopShellNotStarted(self):
+    self._battor = battor_wrapper.BattOrWrapper('win')
+    self._DefaultBattOrReplacements()
+    with self.assertRaises(AssertionError):
+      self._battor.StopShell()
+
+  @mock.patch('time.sleep', mock.Mock)
+  def testFlashBattOrSameGitHash(self):
+    self._battor = battor_wrapper.BattOrWrapper('linux')
+    self._DefaultBattOrReplacements()
+    self._battor.StartShell()
+    self._battor.GetFirmwareGitHash = lambda: 'cbaa843'
+    dependency_manager.DependencyManager._version_return = 'cbaa843'
+    self.assertFalse(self._battor._FlashBattOr())
+
+  @mock.patch('time.sleep', mock.Mock)
+  def testFlashBattOrDifferentGitHash(self):
+    self._battor = battor_wrapper.BattOrWrapper('linux')
+    self._DefaultBattOrReplacements()
+    self._battor.StartShell()
+    self._battor.GetFirmwareGitHash = lambda: 'bazz732'
+    dependency_manager.DependencyManager._version_return = 'cbaa843'
+    self.assertTrue(self._battor._FlashBattOr())
+
 
 if __name__ == '__main__':
   logging.getLogger().setLevel(logging.DEBUG)
diff --git a/sdk/platform-tools/systrace/catapult/common/battor/bin/upload_battor_binaries.py b/sdk/platform-tools/systrace/catapult/common/battor/bin/upload_battor_binaries.py
index 71387b9..8901cfc 100755
--- a/sdk/platform-tools/systrace/catapult/common/battor/bin/upload_battor_binaries.py
+++ b/sdk/platform-tools/systrace/catapult/common/battor/bin/upload_battor_binaries.py
@@ -14,7 +14,8 @@
 
 
 _SUPPORTED_ARCHS = [
-    'linux2_x86_64', 'darwin_x86_64', 'win_AMD64', 'win32_AMD64', 'win32_x86'
+    'linux2_x86_64', 'darwin_x86_64', 'win_AMD64', 'win32_AMD64', 'win32_x86',
+    'default'
 ]
 _DEFAULT_DEP = 'battor_agent_binary'
 _DEFAULT_CONFIG = os.path.join(os.path.dirname(__file__), '..', 'battor',
diff --git a/sdk/platform-tools/systrace/catapult/common/eslint/LICENSE b/sdk/platform-tools/systrace/catapult/common/eslint/LICENSE
new file mode 100644
index 0000000..f943447
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/common/eslint/LICENSE
@@ -0,0 +1,20 @@
+ESLint
+Copyright jQuery Foundation and other contributors, https://jquery.org/
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/common/eslint/README.md b/sdk/platform-tools/systrace/catapult/common/eslint/README.md
new file mode 100644
index 0000000..8ba5b63
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/common/eslint/README.md
@@ -0,0 +1,5 @@
+This directory contains the Catapult eslint config, custom Catapult eslint rules,
+and tests for those rules.
+
+Some of our custom rules are modified versions of those included with eslint, as
+suggested in https://goo.gl/uAxFHq.
diff --git a/sdk/platform-tools/systrace/catapult/common/eslint/bin/run_eslint b/sdk/platform-tools/systrace/catapult/common/eslint/bin/run_eslint
new file mode 100755
index 0000000..933415b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/common/eslint/bin/run_eslint
@@ -0,0 +1,54 @@
+#!/usr/bin/env python
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import argparse
+import os
+import sys
+
+
+_CATAPULT_PATH = os.path.abspath(
+    os.path.join(os.path.dirname(__file__),
+                 os.path.pardir, os.path.pardir, os.path.pardir))
+
+
+_ESLINT_PATH = os.path.abspath(
+    os.path.join(os.path.dirname(__file__), os.path.pardir))
+
+
+DIRECTORIES_TO_LINT = [
+  os.path.join(_CATAPULT_PATH, 'dashboard', 'dashboard'),
+  os.path.join(_CATAPULT_PATH, 'tracing', 'tracing')
+]
+
+
+def _AddToPathIfNeeded(path):
+  if path not in sys.path:
+    sys.path.insert(0, path)
+
+
+if __name__ == '__main__':
+  _AddToPathIfNeeded(_ESLINT_PATH)
+  import eslint
+
+  parser = argparse.ArgumentParser(
+      description='Wrapper script to run eslint on Catapult code')
+  parser.add_argument('--paths', '-p', default=None, nargs='+', metavar='PATH',
+                      help='List of paths to lint')
+  parser.add_argument('--all', default=None, action='store_true',
+                      help='Runs eslint on all applicable Catapult code')
+  parser.add_argument('--extra-args', default=None, type=str,
+                      help='A string of extra arguments to pass to eslint')
+
+  args = parser.parse_args(sys.argv[1:])
+  if ((args.paths is not None and args.all is not None) or
+      (args.paths is None and args.all is None)):
+    print 'Either --paths or --all must be used, but not both.\n'
+    parser.print_help()
+    sys.exit(1)
+
+  paths = DIRECTORIES_TO_LINT if args.all else args.paths
+  success, output = eslint.RunEslint(paths, extra_args=args.extra_args)
+  print output
+  sys.exit(not success)
diff --git a/sdk/platform-tools/systrace/catapult/common/eslint/bin/run_tests b/sdk/platform-tools/systrace/catapult/common/eslint/bin/run_tests
new file mode 100755
index 0000000..db10679
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/common/eslint/bin/run_tests
@@ -0,0 +1,35 @@
+#!/usr/bin/env python
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import sys
+
+
+_CATAPULT_PATH = os.path.abspath(
+    os.path.join(os.path.dirname(__file__),
+    os.path.pardir, os.path.pardir, os.path.pardir))
+
+
+_ESLINT_PATH = os.path.abspath(
+    os.path.join(os.path.dirname(__file__), os.path.pardir))
+
+
+def _RunTestsOrDie(top_level_dir):
+  exit_code = run_with_typ.Run(top_level_dir, path=[_ESLINT_PATH])
+  if exit_code:
+    sys.exit(exit_code)
+
+
+def _AddToPathIfNeeded(path):
+  if path not in sys.path:
+    sys.path.insert(0, path)
+
+
+if __name__ == '__main__':
+  _AddToPathIfNeeded(_CATAPULT_PATH)
+
+  from catapult_build import run_with_typ
+
+  _RunTestsOrDie(os.path.join(_ESLINT_PATH, 'eslint'))
diff --git a/sdk/platform-tools/systrace/catapult/common/eslint/eslint/__init__.py b/sdk/platform-tools/systrace/catapult/common/eslint/eslint/__init__.py
new file mode 100644
index 0000000..082178a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/common/eslint/eslint/__init__.py
@@ -0,0 +1,68 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import subprocess
+import sys
+
+
+_CATAPULT_PATH = os.path.join(
+    os.path.dirname(os.path.abspath(__file__)),
+    os.path.pardir, os.path.pardir, os.path.pardir)
+
+
+def _AddToPathIfNeeded(path):
+  if path not in sys.path:
+    sys.path.insert(0, path)
+
+
+def _UpdateSysPathIfNeeded():
+  _AddToPathIfNeeded(os.path.join(_CATAPULT_PATH, 'common', 'node_runner'))
+  _AddToPathIfNeeded(os.path.join(_CATAPULT_PATH, 'common', 'py_utils'))
+
+
+_UpdateSysPathIfNeeded()
+
+
+import py_utils
+from node_runner import node_util
+
+
+BASE_ESLINT_CMD = [
+  node_util.GetNodePath(),
+  os.path.join(node_util.GetNodeModulesPath(), 'eslint', 'bin', 'eslint.js'),
+  '--color'
+]
+
+
+DEFAULT_ESLINT_RULES_DIR = os.path.join(
+    py_utils.GetCatapultDir(), 'common', 'eslint', 'rules')
+
+
+def _CreateEslintCommand(rulesdir, extra_args):
+  eslint_cmd = BASE_ESLINT_CMD + [
+      '--rulesdir', rulesdir, '--ext', '.js,.html'
+  ]
+  if extra_args:
+    eslint_cmd.extend(extra_args.strip().split(' '))
+  return eslint_cmd
+
+
+def RunEslint(paths, rules_dir=DEFAULT_ESLINT_RULES_DIR, extra_args=None):
+  """Runs eslint on a list of paths.
+
+  Args:
+    paths: A list of paths to run eslint on.
+    rules_dir: A directory of custom eslint rules.
+    extra_args: A string to append to the end of the eslint command.
+  """
+  if type(paths) is not list or len(paths) == 0:
+    raise ValueError('Must specify a non-empty list of paths to lint.')
+
+  try:
+    eslint_cmd = _CreateEslintCommand(rules_dir, extra_args)
+    return True, subprocess.check_output(eslint_cmd + paths,
+                                         stderr=subprocess.STDOUT).rstrip()
+  except subprocess.CalledProcessError as e:
+    return False, e.output.rstrip()
diff --git a/sdk/platform-tools/systrace/catapult/common/eslint/eslint/smoke_test.py b/sdk/platform-tools/systrace/catapult/common/eslint/eslint/smoke_test.py
new file mode 100644
index 0000000..9a0f442
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/common/eslint/eslint/smoke_test.py
@@ -0,0 +1,36 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import eslint
+import os
+import tempfile
+import unittest
+
+
+_TEMP_FILE_CONTENTS = '''<!DOCTYPE html>
+<!--
+Copyright 2016 The Chromium Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+<script>
+// This should cause a linter error because we require camelCase.
+var non_camel_case = 0;
+</script>
+'''
+
+
+class SmokeTest(unittest.TestCase):
+  def testEslintFindsError(self):
+    try:
+      tmp_file =  tempfile.NamedTemporaryFile(
+          delete=False, dir=os.path.dirname(__file__), suffix=".html")
+      tmp_file.write(_TEMP_FILE_CONTENTS)
+      tmp_file.close()
+
+      success, output = eslint.RunEslint([tmp_file.name])
+      self.assertFalse(success)
+      self.assertTrue('is not in camel case' in output)
+    finally:
+      os.remove(tmp_file.name)
diff --git a/sdk/platform-tools/systrace/catapult/common/eslint/rules/catapult-camelcase.js b/sdk/platform-tools/systrace/catapult/common/eslint/rules/catapult-camelcase.js
new file mode 100644
index 0000000..bf31052
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/common/eslint/rules/catapult-camelcase.js
@@ -0,0 +1,154 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+/* eslint-disable */
+
+/**
+ * @fileoverview Rule to flag non-camelcased identifiers
+ * @author Nicholas C. Zakas
+ */
+
+'use strict';
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+    meta: {
+        docs: {
+            description: "enforce Catapult camelcase naming convention",
+            category: "Stylistic Issues",
+            recommended: false
+        },
+
+        schema: [
+            {
+                type: "object",
+                properties: {
+                    properties: {
+                        enum: ["always", "never"]
+                    }
+                },
+                additionalProperties: false
+            }
+        ]
+    },
+
+    create(context) {
+
+        //--------------------------------------------------------------------------
+        // Helpers
+        //--------------------------------------------------------------------------
+
+        // contains reported nodes to avoid reporting twice on destructuring with shorthand notation
+        var reported = [];
+
+        /**
+         * Checks if a string contains an underscore and isn't all upper-case
+         * @param {string} name The string to check.
+         * @returns {boolean} if the string is underscored
+         * @private
+         */
+        function isUnderscored(name) {
+
+            // if there's an underscore, it might be A_VARANT, which is okay
+            return name.indexOf("_") > -1 && name !== name.toUpperCase();
+        }
+
+        /**
+         * Reports an AST node as a rule violation.
+         * @param {ASTNode} node The node to report.
+         * @returns {void}
+         * @private
+         */
+        function report(node) {
+            if (reported.indexOf(node) < 0) {
+                reported.push(node);
+                context.report(node, "Identifier '{{name}}' is not in camel case.", { name: node.name });
+            }
+        }
+
+        var options = context.options[0] || {};
+        let properties = options.properties || "";
+
+        if (properties !== "always" && properties !== "never") {
+            properties = "always";
+        }
+
+        return {
+
+            Identifier(node) {
+
+                /*
+                 * Leading and trailing underscores are commonly used to flag
+                 * private/protected identifiers, strip them.
+                 *
+                 * NOTE: This has four Catapult-specific style exceptions:
+                 *
+                 *   - The prefix opt_
+                 *   - The prefix g_
+                 *   - The suffix _smallerIsBetter
+                 *   - The suffix _biggerIsBetter
+                 */
+                var name = node.name.replace(/(?:^opt_)|^(?:^g_)|^_+|_+$|(?:_smallerIsBetter)$|(?:_biggerIsBetter)$/g, ""),
+                    effectiveParent = (node.parent.type === "MemberExpression") ? node.parent.parent : node.parent;
+
+                // MemberExpressions get special rules
+                if (node.parent.type === "MemberExpression") {
+
+                    // "never" check properties
+                    if (properties === "never") {
+                        return;
+                    }
+
+                    // Always report underscored object names
+                    if (node.parent.object.type === "Identifier" &&
+                            node.parent.object.name === node.name &&
+                            isUnderscored(name)) {
+                        report(node);
+
+                    // Report AssignmentExpressions only if they are the left side of the assignment
+                    } else if (effectiveParent.type === "AssignmentExpression" &&
+                            isUnderscored(name) &&
+                            (effectiveParent.right.type !== "MemberExpression" ||
+                            effectiveParent.left.type === "MemberExpression" &&
+                            effectiveParent.left.property.name === node.name)) {
+                        report(node);
+                    }
+
+                // Properties have their own rules
+                } else if (node.parent.type === "Property") {
+
+                    // "never" check properties
+                    if (properties === "never") {
+                        return;
+                    }
+
+                    if (node.parent.parent && node.parent.parent.type === "ObjectPattern" &&
+                            node.parent.key === node && node.parent.value !== node) {
+                        return;
+                    }
+
+                    if (isUnderscored(name) && effectiveParent.type !== "CallExpression") {
+                        report(node);
+                    }
+
+                // Check if it's an import specifier
+                } else if (["ImportSpecifier", "ImportNamespaceSpecifier", "ImportDefaultSpecifier"].indexOf(node.parent.type) >= 0) {
+
+                    // Report only if the local imported identifier is underscored
+                    if (node.parent.local && node.parent.local.name === node.name && isUnderscored(name)) {
+                        report(node);
+                    }
+
+                // Report anything that is underscored that isn't a CallExpression
+                } else if (isUnderscored(name) && effectiveParent.type !== "CallExpression") {
+                    report(node);
+                }
+            }
+
+        };
+
+    }
+};
diff --git a/sdk/platform-tools/systrace/catapult/common/eslint/tests/catapult-camelcase.js b/sdk/platform-tools/systrace/catapult/common/eslint/tests/catapult-camelcase.js
new file mode 100644
index 0000000..f0bdb37
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/common/eslint/tests/catapult-camelcase.js
@@ -0,0 +1,324 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+/* eslint-disable */
+
+/**
+ * @fileoverview Tests for camelcase rule.
+ * @author Nicholas C. Zakas
+ */
+
+'use strict';
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+var rule = require("../rules/catapult-camelcase"),
+    RuleTester = require("../../node_runner/node_runner/node_modules/eslint/lib/testers/rule-tester");
+
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
+var ruleTester = new RuleTester();
+
+ruleTester.run("camelcase", rule, {
+    valid: [
+        "firstName = \"Nicholas\"",
+        "FIRST_NAME = \"Nicholas\"",
+        "__myPrivateVariable = \"Patrick\"",
+        "myPrivateVariable_ = \"Patrick\"",
+        "function doSomething(){}",
+        "do_something()",
+        "foo.do_something()",
+        "var foo = bar.baz_boom;",
+        "var foo = bar.baz_boom.something;",
+        "foo.boom_pow.qux = bar.baz_boom.something;",
+        "if (bar.baz_boom) {}",
+        "var obj = { key: foo.bar_baz };",
+        "var arr = [foo.bar_baz];",
+        "[foo.bar_baz]",
+        "var arr = [foo.bar_baz.qux];",
+        "[foo.bar_baz.nesting]",
+        "if (foo.bar_baz === boom.bam_pow) { [foo.baz_boom] }",
+        // These tests are for Catapult-specific exceptions.
+        "opt_firstName = \"Nicholas\"",
+        "g_firstName = \"Nicholas\"",
+        "sizeInBytes_smallerIsBetter = \"Nicholas\"",
+        "sizeInBytes_biggerIsBetter = \"Nicholas\"",
+        {
+            code: "var o = {key: 1}",
+            options: [{properties: "always"}]
+        },
+        {
+            code: "var o = {bar_baz: 1}",
+            options: [{properties: "never"}]
+        },
+        {
+            code: "obj.a_b = 2;",
+            options: [{properties: "never"}]
+        },
+        {
+            code: "var obj = {\n a_a: 1 \n};\n obj.a_b = 2;",
+            options: [{properties: "never"}]
+        },
+        {
+            code: "obj.foo_bar = function(){};",
+            options: [{properties: "never"}]
+        },
+        {
+            code: "var { category_id: category } = query;",
+            parserOptions: { ecmaVersion: 6 }
+        },
+        {
+            code: "var { category_id: category } = query;",
+            parserOptions: { ecmaVersion: 6 },
+            options: [{properties: "never"}]
+        },
+        {
+            code: "import { camelCased } from \"external module\";",
+            parserOptions: { ecmaVersion: 6, sourceType: "module" }
+        },
+        {
+            code: "import { no_camelcased as camelCased } from \"external-module\";",
+            parserOptions: { ecmaVersion: 6, sourceType: "module" }
+        },
+        {
+            code: "import { no_camelcased as camelCased, anoterCamelCased } from \"external-module\";",
+            parserOptions: { ecmaVersion: 6, sourceType: "module" }
+        }
+    ],
+    invalid: [
+        {
+            code: "first_name = \"Nicholas\"",
+            errors: [
+                {
+                    message: "Identifier 'first_name' is not in camel case.",
+                    type: "Identifier"
+                }
+            ]
+        },
+        {
+            code: "__private_first_name = \"Patrick\"",
+            errors: [
+                {
+                    message: "Identifier '__private_first_name' is not in camel case.",
+                    type: "Identifier"
+                }
+            ]
+        },
+        {
+            code: "function foo_bar(){}",
+            errors: [
+                {
+                    message: "Identifier 'foo_bar' is not in camel case.",
+                    type: "Identifier"
+                }
+            ]
+        },
+        {
+            code: "obj.foo_bar = function(){};",
+            errors: [
+                {
+                    message: "Identifier 'foo_bar' is not in camel case.",
+                    type: "Identifier"
+                }
+            ]
+        },
+        {
+            code: "bar_baz.foo = function(){};",
+            errors: [
+                {
+                    message: "Identifier 'bar_baz' is not in camel case.",
+                    type: "Identifier"
+                }
+            ]
+        },
+        {
+            code: "[foo_bar.baz]",
+            errors: [
+                {
+                    message: "Identifier 'foo_bar' is not in camel case.",
+                    type: "Identifier"
+                }
+            ]
+        },
+        {
+            code: "if (foo.bar_baz === boom.bam_pow) { [foo_bar.baz] }",
+            errors: [
+                {
+                    message: "Identifier 'foo_bar' is not in camel case.",
+                    type: "Identifier"
+                }
+            ]
+        },
+        {
+            code: "foo.bar_baz = boom.bam_pow",
+            errors: [
+                {
+                    message: "Identifier 'bar_baz' is not in camel case.",
+                    type: "Identifier"
+                }
+            ]
+        },
+        {
+            code: "var foo = { bar_baz: boom.bam_pow }",
+            errors: [
+                {
+                    message: "Identifier 'bar_baz' is not in camel case.",
+                    type: "Identifier"
+                }
+            ]
+        },
+        {
+            code: "foo.qux.boom_pow = { bar: boom.bam_pow }",
+            errors: [
+                {
+                    message: "Identifier 'boom_pow' is not in camel case.",
+                    type: "Identifier"
+                }
+            ]
+        },
+        {
+            code: "var o = {bar_baz: 1}",
+            options: [{properties: "always"}],
+            errors: [
+                {
+                    message: "Identifier 'bar_baz' is not in camel case.",
+                    type: "Identifier"
+                }
+            ]
+        },
+        {
+            code: "obj.a_b = 2;",
+            options: [{properties: "always"}],
+            errors: [
+                {
+                    message: "Identifier 'a_b' is not in camel case.",
+                    type: "Identifier"
+                }
+            ]
+        },
+        {
+            code: "obj.a_b = 2;",
+            options: [{properties: "always"}],
+            errors: [
+                {
+                    message: "Identifier 'a_b' is not in camel case.",
+                    type: "Identifier"
+                }
+            ]
+        },
+        {
+            code: "var { category_id: category_id } = query;",
+            parserOptions: { ecmaVersion: 6 },
+            errors: [
+                {
+                    message: "Identifier 'category_id' is not in camel case.",
+                    type: "Identifier"
+                }
+            ]
+        },
+        {
+            code: "var { category_id } = query;",
+            parserOptions: { ecmaVersion: 6 },
+            errors: [
+                {
+                    message: "Identifier 'category_id' is not in camel case.",
+                    type: "Identifier"
+                }
+            ]
+        },
+        {
+            code: "import no_camelcased from \"external-module\";",
+            parserOptions: { ecmaVersion: 6, sourceType: "module" },
+            errors: [
+                {
+                    message: "Identifier 'no_camelcased' is not in camel case.",
+                    type: "Identifier"
+                }
+            ]
+        },
+        {
+            code: "import * as no_camelcased from \"external-module\";",
+            parserOptions: { ecmaVersion: 6, sourceType: "module" },
+            errors: [
+                {
+                    message: "Identifier 'no_camelcased' is not in camel case.",
+                    type: "Identifier"
+                }
+            ]
+        },
+        {
+            code: "import { no_camelcased } from \"external-module\";",
+            parserOptions: { ecmaVersion: 6, sourceType: "module" },
+            errors: [
+                {
+                    message: "Identifier 'no_camelcased' is not in camel case.",
+                    type: "Identifier"
+                }
+            ]
+        },
+        {
+            code: "import { no_camelcased as no_camel_cased } from \"external module\";",
+            parserOptions: { ecmaVersion: 6, sourceType: "module" },
+            errors: [
+                {
+                    message: "Identifier 'no_camel_cased' is not in camel case.",
+                    type: "Identifier"
+                }
+            ]
+        },
+        {
+            code: "import { camelCased as no_camel_cased } from \"external module\";",
+            parserOptions: { ecmaVersion: 6, sourceType: "module" },
+            errors: [
+                {
+                    message: "Identifier 'no_camel_cased' is not in camel case.",
+                    type: "Identifier"
+                }
+            ]
+        },
+        {
+            code: "import { camelCased, no_camelcased } from \"external-module\";",
+            parserOptions: { ecmaVersion: 6, sourceType: "module" },
+            errors: [
+                {
+                    message: "Identifier 'no_camelcased' is not in camel case.",
+                    type: "Identifier"
+                }
+            ]
+        },
+        {
+            code: "import { no_camelcased as camelCased, another_no_camelcased } from \"external-module\";",
+            parserOptions: { ecmaVersion: 6, sourceType: "module" },
+            errors: [
+                {
+                    message: "Identifier 'another_no_camelcased' is not in camel case.",
+                    type: "Identifier"
+                }
+            ]
+        },
+        {
+            code: "import camelCased, { no_camelcased } from \"external-module\";",
+            parserOptions: { ecmaVersion: 6, sourceType: "module" },
+            errors: [
+                {
+                    message: "Identifier 'no_camelcased' is not in camel case.",
+                    type: "Identifier"
+                }
+            ]
+        },
+        {
+            code: "import no_camelcased, { another_no_camelcased as camelCased } from \"external-module\";",
+            parserOptions: { ecmaVersion: 6, sourceType: "module" },
+            errors: [
+                {
+                    message: "Identifier 'no_camelcased' is not in camel case.",
+                    type: "Identifier"
+                }
+            ]
+        }
+    ]
+});
diff --git a/sdk/platform-tools/systrace/catapult/common/node_runner/bin/test_node_for_smoke b/sdk/platform-tools/systrace/catapult/common/node_runner/bin/test_node_for_smoke
deleted file mode 100755
index b145858..0000000
--- a/sdk/platform-tools/systrace/catapult/common/node_runner/bin/test_node_for_smoke
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/usr/bin/env python
-# Copyright 2016 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import os
-import sys
-
-_NODE_RUNNER_PATH = os.path.abspath(
-    os.path.join(os.path.dirname(__file__), '..'))
-
-
-_PY_UTILS_PATH = os.path.abspath(
-    os.path.join(_NODE_RUNNER_PATH, '..', 'py_utils'))
-
-
-def _AddToPathIfNeeded(path):
-  if path not in sys.path:
-    sys.path.insert(0, path)
-
-
-if __name__ == '__main__':
-  _AddToPathIfNeeded(_NODE_RUNNER_PATH)
-  _AddToPathIfNeeded(_PY_UTILS_PATH)
-
-  from node_runner import node_util
-  print node_util.RunEslint()
-  sys.exit(0)
diff --git a/sdk/platform-tools/systrace/catapult/common/node_runner/node_runner/node_binaries.json b/sdk/platform-tools/systrace/catapult/common/node_runner/node_runner/node_binaries.json
index de4ede1..4245249 100644
--- a/sdk/platform-tools/systrace/catapult/common/node_runner/node_runner/node_binaries.json
+++ b/sdk/platform-tools/systrace/catapult/common/node_runner/node_runner/node_binaries.json
@@ -6,22 +6,46 @@
       "cloud_storage_bucket": "chromium-telemetry",
       "file_info": {
         "linux_x86_64": {
-          "cloud_storage_hash": "da9f3781488844a966106822ef3dbdbe2377ff13",
+          "cloud_storage_hash": "5750e968975e7f5ab8cb694f5e92a34a890e129d",
           "download_path": "bin/node/node-linux64.zip",
-          "path_within_archive": "node-v6.5.0-linux-x64/bin/node",
-          "version_in_cs": "6.5.0"
+          "path_within_archive": "node-v6.7.0-linux-x64/bin/node",
+          "version_in_cs": "6.7.0"
         },
         "mac_x86_64": {
-          "cloud_storage_hash": "05a772e1cb5f2167394586b5a1f9bdc7c0c1b376",
+          "cloud_storage_hash": "1af7c221e530165af8a6ab8ff7ccb1f2dd54036d",
           "download_path": "bin/node/node-mac64.zip",
-          "path_within_archive": "node-v6.5.0-darwin-x64/bin/node",
-          "version_in_cs": "6.5.0"
+          "path_within_archive": "node-v6.7.0-darwin-x64/bin/node",
+          "version_in_cs": "6.7.0"
         },
         "win_AMD64": {
-          "cloud_storage_hash": "0046368de2ffb3d474847238dfc82c3f7995810a",
+          "cloud_storage_hash": "23f21bfb2edf874a8b6bdb6c1acb408bc7edeced",
           "download_path": "bin/node/node-win64.zip",
-          "path_within_archive": "node-v6.5.0-win-x64/node.exe",
-          "version_in_cs": "6.5.0"
+          "path_within_archive": "node-v6.7.0-win-x64/node.exe",
+          "version_in_cs": "6.7.0"
+        }
+      }
+    },
+    "npm": {
+      "cloud_storage_base_folder": "binary_dependencies",
+      "cloud_storage_bucket": "chromium-telemetry",
+      "file_info": {
+        "linux_x86_64": {
+          "cloud_storage_hash": "5750e968975e7f5ab8cb694f5e92a34a890e129d",
+          "download_path": "bin/node/node-linux64.zip",
+          "path_within_archive": "node-v6.7.0-linux-x64/lib/node_modules/npm/bin/npm-cli.js",
+          "version_in_cs": "6.7.0"
+        },
+        "mac_x86_64": {
+          "cloud_storage_hash": "1af7c221e530165af8a6ab8ff7ccb1f2dd54036d",
+          "download_path": "bin/node/node-mac64.zip",
+          "path_within_archive": "node-v6.7.0-darwin-x64/lib/node_modules/npm/bin/npm-cli.js",
+          "version_in_cs": "6.7.0"
+        },
+        "win_AMD64": {
+          "cloud_storage_hash": "23f21bfb2edf874a8b6bdb6c1acb408bc7edeced",
+          "download_path": "bin/node/node-win64.zip",
+          "path_within_archive": "node-v6.7.0-win-x64\\node_modules\\npm\\bin\\npm-cli.js",
+          "version_in_cs": "6.7.0"
         }
       }
     }
diff --git a/sdk/platform-tools/systrace/catapult/common/node_runner/node_runner/node_util.py b/sdk/platform-tools/systrace/catapult/common/node_runner/node_runner/node_util.py
index 4ec4490..05d0084 100644
--- a/sdk/platform-tools/systrace/catapult/common/node_runner/node_runner/node_util.py
+++ b/sdk/platform-tools/systrace/catapult/common/node_runner/node_runner/node_util.py
@@ -24,9 +24,7 @@
     self.arch_name = dependency_util.GetArchForCurrentDesktopPlatform(
         self.os_name)
     self.node_path = self.bm.FetchPath('node', self.os_name, self.arch_name)
-    self.npm_path = os.path.abspath(os.path.join(
-        os.path.dirname(self.node_path), '..', 'lib', 'node_modules', 'npm',
-        'bin', 'npm-cli.js'))
+    self.npm_path = self.bm.FetchPath('npm', self.os_name, self.arch_name)
 
     self.node_initialized = False
 
@@ -60,14 +58,3 @@
     # Escape path on Windows because it's very long and must be passed to NTFS.
     path = u'\\\\?\\' + path
   return path
-
-
-def RunEslint(filenames=None):
-  cmd = [GetNodePath(), os.path.join(
-      GetNodeModulesPath(), 'eslint', 'bin', 'eslint.js'), '--color']
-  if filenames:
-    cmd += filenames
-  try:
-    return subprocess.check_output(cmd, stderr=subprocess.STDOUT)
-  except subprocess.CalledProcessError as e:
-    return e.output
diff --git a/sdk/platform-tools/systrace/catapult/common/node_runner/node_runner/package.json b/sdk/platform-tools/systrace/catapult/common/node_runner/node_runner/package.json
index 797c1d5..27d0325 100644
--- a/sdk/platform-tools/systrace/catapult/common/node_runner/node_runner/package.json
+++ b/sdk/platform-tools/systrace/catapult/common/node_runner/node_runner/package.json
@@ -15,8 +15,8 @@
   "gypfile": false,
   "private": true,
   "dependencies": {
-    "eslint": "^3.4.0",
+    "eslint": "^3.14.1",
     "eslint-config-google": "^0.6.0",
-    "eslint-plugin-html": "^1.5.2"
+    "eslint-plugin-html": "^2.0.0"
   }
 }
diff --git a/sdk/platform-tools/systrace/catapult/common/py_utils/py_utils/__init__.py b/sdk/platform-tools/systrace/catapult/common/py_utils/py_utils/__init__.py
index e508170..3f26311 100644
--- a/sdk/platform-tools/systrace/catapult/common/py_utils/py_utils/__init__.py
+++ b/sdk/platform-tools/systrace/catapult/common/py_utils/py_utils/__init__.py
@@ -5,8 +5,10 @@
 # found in the LICENSE file.
 
 import functools
+import inspect
 import os
 import sys
+import time
 
 
 def GetCatapultDir():
@@ -85,3 +87,54 @@
       print '%s timed out.' % func.__name__
       return False
   return RunWithTimeout
+
+
+MIN_POLL_INTERVAL_IN_SECONDS = 0.1
+MAX_POLL_INTERVAL_IN_SECONDS = 5
+OUTPUT_INTERVAL_IN_SECONDS = 300
+
+def WaitFor(condition, timeout):
+  """Waits for up to |timeout| secs for the function |condition| to return True.
+
+  Polling frequency is (elapsed_time / 10), with a min of .1s and max of 5s.
+
+  Returns:
+    Result of |condition| function (if present).
+  """
+  def GetConditionString():
+    if condition.__name__ == '<lambda>':
+      try:
+        return inspect.getsource(condition).strip()
+      except IOError:
+        pass
+    return condition.__name__
+
+  # Do an initial check to see if its true.
+  res = condition()
+  if res:
+    return res
+  start_time = time.time()
+  last_output_time = start_time
+  elapsed_time = time.time() - start_time
+  while elapsed_time < timeout:
+    res = condition()
+    if res:
+      return res
+    now = time.time()
+    elapsed_time = now - start_time
+    last_output_elapsed_time = now - last_output_time
+    if last_output_elapsed_time > OUTPUT_INTERVAL_IN_SECONDS:
+      last_output_time = time.time()
+    poll_interval = min(max(elapsed_time / 10., MIN_POLL_INTERVAL_IN_SECONDS),
+                        MAX_POLL_INTERVAL_IN_SECONDS)
+    time.sleep(poll_interval)
+  raise TimeoutException('Timed out while waiting %ds for %s.' %
+                         (timeout, GetConditionString()))
+
+class TimeoutException(Exception):
+  """The operation failed to complete because of a timeout.
+
+  It is possible that waiting for a longer period of time would result in a
+  successful operation.
+  """
+  pass
diff --git a/sdk/platform-tools/systrace/catapult/common/py_utils/py_utils/chrome_binaries.json b/sdk/platform-tools/systrace/catapult/common/py_utils/py_utils/chrome_binaries.json
index a3f5b51..bb0b2e1 100644
--- a/sdk/platform-tools/systrace/catapult/common/py_utils/py_utils/chrome_binaries.json
+++ b/sdk/platform-tools/systrace/catapult/common/py_utils/py_utils/chrome_binaries.json
@@ -6,22 +6,22 @@
       "cloud_storage_bucket": "chrome-telemetry",
       "file_info": {
         "mac_x86_64": {
-          "cloud_storage_hash": "26e8d9d1c26bdb32664d16768a5439ec96868590",
+          "cloud_storage_hash": "ab33866d00fb0c9d6543c20a21da5f047ba6a7b6",
           "download_path": "bin/reference_builds/chrome-mac64.zip",
           "path_within_archive": "chrome-mac/Google Chrome.app/Contents/MacOS/Google Chrome",
-          "version_in_cs": "51.0.2702.0"
+          "version_in_cs": "58.0.3004.0"
         },
         "win_AMD64": {
-          "cloud_storage_hash": "b0e70644a0c0cd399e0dcdfdef7583ae2c3a913e",
-          "download_path": "bin\\reference_build\\chrome-win64.zip",
-          "path_within_archive": "chrome-win64\\chrome.exe",
-          "version_in_cs": "51.0.2702.0"
+          "cloud_storage_hash": "2348f9bcf421fa4739493a12b4c8e3210a528d84",
+          "download_path": "bin\\reference_build\\chrome-win64-pgo.zip",
+          "path_within_archive": "chrome-win64-pgo\\chrome.exe",
+          "version_in_cs": "58.0.3004.0"
         },
         "win_x86": {
-          "cloud_storage_hash": "3e9804166f3291726112410dbd8de51016b8d988",
-          "download_path": "bin\\reference_build\\chrome-win32.zip",
-          "path_within_archive": "chrome-win32\\chrome.exe",
-          "version_in_cs": "51.0.2702.0"
+          "cloud_storage_hash": "421c59cfbc02bee9b74f68869af3a930b5988e71",
+          "download_path": "bin\\reference_build\\chrome-win32-pgo.zip",
+          "path_within_archive": "chrome-win32-pgo\\chrome.exe",
+          "version_in_cs": "58.0.3004.0"
         }
       }
     },
@@ -29,49 +29,11 @@
       "cloud_storage_base_folder": "binary_dependencies",
       "cloud_storage_bucket": "chrome-telemetry",
       "file_info": {
-        "android_k_arm64-v8a": {
-          "cloud_storage_hash": "c43f09d30523bb0612f65e80b2b993e34ea68d4a",
-          "download_path": "bin/reference_build/android_k_arm64-v8a/ChromeStable.apk",
-          "version_in_cs": "51.0.2700.2"
-        },
-        "android_k_armeabi-v7a": {
-          "cloud_storage_hash": "87588c19bd1634fd2a9e21aedd5ad31fc47e10db",
-          "download_path": "bin/reference_build/android_k_armeabi-v7a/ChromeStable.apk",
-          "version_in_cs": "51.0.2700.2"
-        },
-        "android_l_arm64-v8a": {
-          "cloud_storage_hash": "fd54f185354e614b94e201d1d72de41b86f550b4",
-          "download_path": "bin/reference_build/android_l_arm64-v8a/ChromeStable.apk",
-          "version_in_cs": "51.0.2700.2"
-        },
-        "android_l_armeabi-v7a": {
-          "cloud_storage_hash": "4a332a47651f986e65ddc3b736c682dc3f7eaa0a",
-          "download_path": "bin/reference_build/android_l_armeabi-v7a/ChromeStable.apk",
-          "version_in_cs": "51.0.2700.2"
-        },
         "linux_x86_64": {
-          "cloud_storage_hash": "814040060381e2ca6d6e570e7cb0a880ae239096",
+          "cloud_storage_hash": "d61c3c5a81dc5e0a896589ab3d72a253e4b9cc90",
           "download_path": "bin/reference_build/chrome-linux64.zip",
           "path_within_archive": "chrome-precise64/chrome",
-          "version_in_cs": "51.0.2700.0"
-        },
-        "mac_x86_64": {
-          "cloud_storage_hash": "ae6d92afaf57f77e0bc39b9179610f528042ba76",
-          "download_path": "bin/reference_builds/chrome-mac64.zip",
-          "path_within_archive": "chrome-mac/Google Chrome.app/Contents/MacOS/Google Chrome",
-          "version_in_cs": "51.0.2700.0"
-        },
-        "win_AMD64": {
-          "cloud_storage_hash": "dbce7907a3b7840d6fd9d016e47ffdc1f675c70e",
-          "download_path": "bin\\reference_build\\chrome-win64.zip",
-          "path_within_archive": "chrome-win64\\chrome.exe",
-          "version_in_cs": "51.0.2700.0"
-        },
-        "win_x86": {
-          "cloud_storage_hash": "93c4267434aad8fd2b9336490fd00e4e4f4c100e",
-          "download_path": "bin\\reference_build\\chrome-win32.zip",
-          "path_within_archive": "chrome-win32\\chrome.exe",
-          "version_in_cs": "51.0.2700.0"
+          "version_in_cs": "58.0.3000.4"
         }
       }
     },
@@ -79,79 +41,44 @@
       "cloud_storage_base_folder": "binary_dependencies",
       "cloud_storage_bucket": "chrome-telemetry",
       "file_info": {
-        "android_k_arm64-v8a": {
-          "cloud_storage_hash": "08f7e4dcdeba512043bfe7fe714c571bccc45954",
-          "download_path": "bin/reference_build/android_k_arm64-v8a/ChromeStable.apk",
-          "version_in_cs": "48.0.2564.95"
-        },
         "android_k_armeabi-v7a": {
-          "cloud_storage_hash": "798efed0a7ae1460fe297ba1bd16308585c9e6ca",
+          "cloud_storage_hash": "2f0629a395974793c3a25e6c2dc971487742bd49",
           "download_path": "bin/reference_build/android_k_armeabi-v7a/ChromeStable.apk",
-          "version_in_cs": "48.0.2564.95"
+          "version_in_cs": "56.0.2924.87"
         },
         "android_l_arm64-v8a": {
-          "cloud_storage_hash": "e054040d12a20d6c7c4b847d46e254821b4d4532",
+          "cloud_storage_hash": "03306b04e49ed3b0c4c29da84a128d76659624f2",
           "download_path": "bin/reference_build/android_l_arm64-v8a/ChromeStable.apk",
-          "version_in_cs": "48.0.2564.95"
+          "version_in_cs": "56.0.2924.87"
         },
         "android_l_armeabi-v7a": {
-          "cloud_storage_hash": "1438bb54959d9fbf891616392959b778df9e62f9",
+          "cloud_storage_hash": "2f0629a395974793c3a25e6c2dc971487742bd49",
           "download_path": "bin/reference_build/android_l_armeabi-v7a/ChromeStable.apk",
-          "version_in_cs": "48.0.2564.95"
+          "version_in_cs": "56.0.2924.87"
         },
         "linux_x86_64": {
-          "cloud_storage_hash": "22ff02efcda4579b8e87a3f87d3293f257595bc8",
+          "cloud_storage_hash": "07dd594d89c8350978ed368b55204d7ee3641001",
           "download_path": "bin/reference_build/chrome-linux64.zip",
           "path_within_archive": "chrome-precise64/chrome",
-          "version_in_cs": "52.0.2743.116"
+          "version_in_cs": "56.0.2924.87"
         },
         "mac_x86_64": {
-          "cloud_storage_hash": "8854cfea0bb9d111a977ed71832ce99a291c253b",
+          "cloud_storage_hash": "e2e1ac31913ab6976084375540e17bbaa2401820",
           "download_path": "bin/reference_builds/chrome-mac64.zip",
           "path_within_archive": "chrome-mac/Google Chrome.app/Contents/MacOS/Google Chrome",
-          "version_in_cs": "52.0.2743.116"
+          "version_in_cs": "56.0.2924.87"
         },
         "win_AMD64": {
-          "cloud_storage_hash": "ebd5fd9ac05655e7f82a26fe46bfb1bc454866b2",
-          "download_path": "bin\\reference_build\\chrome-win64.zip",
-          "path_within_archive": "chrome-win64\\chrome.exe",
-          "version_in_cs": "52.0.2743.116"
+          "cloud_storage_hash": "d6624303bceff81503db78c4ad17d1ebd1af7b68",
+          "download_path": "bin\\reference_build\\chrome-win64-pgo.zip",
+          "path_within_archive": "chrome-win64-pgo\\chrome.exe",
+          "version_in_cs": "56.0.2924.87"
         },
         "win_x86": {
-          "cloud_storage_hash": "9284d1e7a1a69a95c64ede83ad1f3f0f928c9831",
-          "download_path": "bin\\reference_build\\chrome-win32.zip",
-          "path_within_archive": "chrome-win32\\chrome.exe",
-          "version_in_cs": "52.0.2743.116"
-        }
-      }
-    },
-    "reference_build": {
-      "cloud_storage_base_folder": "binary_dependencies",
-      "cloud_storage_bucket": "chrome-telemetry",
-      "file_info": {
-        "linux_x86_64": {
-          "cloud_storage_hash": "80656d2479eb101486a9dd229cbde54e12a04206",
-          "download_path": "bin/reference_build/chrome-linux64.zip",
-          "path_within_archive": "chrome-precise64/chrome",
-          "version_in_cs": "49.0.2623.112"
-        },
-        "mac_x86_64": {
-          "cloud_storage_hash": "950b1f2a99a8c9b2bced6563ae4b50b2b77669f8",
-          "download_path": "bin/reference_builds/chrome-mac64.zip",
-          "path_within_archive": "chrome-mac/Google Chrome.app/Contents/MacOS/Google Chrome",
-          "version_in_cs": "49.0.2623.112"
-        },
-        "win_AMD64": {
-          "cloud_storage_hash": "e61079394532bf3aeea0afa3e38cfdb700d03363",
-          "download_path": "bin\\reference_build\\chrome-win64.zip",
-          "path_within_archive": "chrome-win64\\chrome.exe",
-          "version_in_cs": "49.0.2623.112"
-        },
-        "win_x86": {
-          "cloud_storage_hash": "28ca3c790aad3e9045fa297913f7131cc51b17ac",
-          "download_path": "bin\\reference_build\\chrome-win32.zip",
-          "path_within_archive": "chrome-win32\\chrome.exe",
-          "version_in_cs": "49.0.2623.112"
+          "cloud_storage_hash": "2b89d4571eeac4d80d4a1fac7c92db3f7e0bd565",
+          "download_path": "bin\\reference_build\\chrome-win32-pgo.zip",
+          "path_within_archive": "chrome-win32-pgo\\chrome.exe",
+          "version_in_cs": "56.0.2924.87"
         }
       }
     }
diff --git a/sdk/platform-tools/systrace/catapult/common/py_utils/py_utils/cloud_storage.py b/sdk/platform-tools/systrace/catapult/common/py_utils/py_utils/cloud_storage.py
index 408fd4d..7bc9a19 100644
--- a/sdk/platform-tools/systrace/catapult/common/py_utils/py_utils/cloud_storage.py
+++ b/sdk/platform-tools/systrace/catapult/common/py_utils/py_utils/cloud_storage.py
@@ -12,9 +12,9 @@
 import shutil
 import stat
 import subprocess
+import re
 import sys
 import tempfile
-import time
 
 import py_utils
 from py_utils import lock
@@ -24,6 +24,8 @@
 # TODO(nedn, jbudorick): figure out a way to get rid of this ugly hack.
 from py_utils import cloud_storage_global_lock  # pylint: disable=unused-import
 
+logger = logging.getLogger(__name__)  # pylint: disable=invalid-name
+
 
 PUBLIC_BUCKET = 'chromium-telemetry'
 PARTNER_BUCKET = 'chrome-partner-telemetry'
@@ -54,6 +56,9 @@
 # calls that invoke cloud storage network io will throw exceptions.
 DISABLE_CLOUD_STORAGE_IO = 'DISABLE_CLOUD_STORAGE_IO'
 
+# The maximum number of seconds to wait to acquire the pseudo lock for a cloud
+# storage file before raising an exception.
+LOCK_ACQUISITION_TIMEOUT = 10
 
 
 class CloudStorageError(Exception):
@@ -145,29 +150,35 @@
   stdout, stderr = gsutil.communicate()
 
   if gsutil.returncode:
-    if stderr.startswith((
-        'You are attempting to access protected data with no configured',
-        'Failure: No handler was ready to authenticate.')):
-      raise CredentialsError()
-    if ('status=403' in stderr or 'status 403' in stderr or
-        '403 Forbidden' in stderr):
-      raise PermissionError()
-    if (stderr.startswith('InvalidUriError') or 'No such object' in stderr or
-        'No URLs matched' in stderr or 'One or more URLs matched no' in stderr):
-      raise NotFoundError(stderr)
-    if '500 Internal Server Error' in stderr:
-      raise ServerError(stderr)
-    raise CloudStorageError(stderr)
+    raise GetErrorObjectForCloudStorageStderr(stderr)
 
   return stdout
 
 
+def GetErrorObjectForCloudStorageStderr(stderr):
+  if (stderr.startswith((
+      'You are attempting to access protected data with no configured',
+      'Failure: No handler was ready to authenticate.')) or
+      re.match('.*401.*does not have .* access to .*', stderr)):
+    return CredentialsError()
+  if ('status=403' in stderr or 'status 403' in stderr or
+      '403 Forbidden' in stderr or
+      re.match('.*403.*does not have .* access to .*', stderr)):
+    return PermissionError()
+  if (stderr.startswith('InvalidUriError') or 'No such object' in stderr or
+      'No URLs matched' in stderr or 'One or more URLs matched no' in stderr):
+    return NotFoundError(stderr)
+  if '500 Internal Server Error' in stderr:
+    return ServerError(stderr)
+  return CloudStorageError(stderr)
+
+
 def IsNetworkIOEnabled():
   """Returns true if cloud storage is enabled."""
   disable_cloud_storage_env_val = os.getenv(DISABLE_CLOUD_STORAGE_IO)
 
   if disable_cloud_storage_env_val and disable_cloud_storage_env_val != '1':
-    logging.error(
+    logger.error(
         'Unsupported value of environment variable '
         'DISABLE_CLOUD_STORAGE_IO. Expected None or \'1\' but got %s.',
         disable_cloud_storage_env_val)
@@ -192,7 +203,7 @@
 def Move(bucket1, bucket2, remote_path):
   url1 = 'gs://%s/%s' % (bucket1, remote_path)
   url2 = 'gs://%s/%s' % (bucket2, remote_path)
-  logging.info('Moving %s to %s', url1, url2)
+  logger.info('Moving %s to %s', url1, url2)
   _RunCommand(['mv', url1, url2])
 
 
@@ -210,13 +221,13 @@
   """
   url1 = 'gs://%s/%s' % (bucket_from, remote_path_from)
   url2 = 'gs://%s/%s' % (bucket_to, remote_path_to)
-  logging.info('Copying %s to %s', url1, url2)
+  logger.info('Copying %s to %s', url1, url2)
   _RunCommand(['cp', url1, url2])
 
 
 def Delete(bucket, remote_path):
   url = 'gs://%s/%s' % (bucket, remote_path)
-  logging.info('Deleting %s', url)
+  logger.info('Deleting %s', url)
   _RunCommand(['rm', url])
 
 
@@ -234,30 +245,88 @@
   pseudo_lock_path = '%s.pseudo_lock' % base_path
   _CreateDirectoryIfNecessary(os.path.dirname(pseudo_lock_path))
 
-  # We need to make sure that there is no other process which is acquiring the
-  # lock on |base_path| and has not finished before proceeding further to create
-  # the |pseudo_lock_path|. Otherwise, |pseudo_lock_path| may be deleted by
-  # that other process after we create it in this process.
-  while os.path.exists(pseudo_lock_path):
-    time.sleep(0.1)
+  # Make sure that we guard the creation, acquisition, release, and removal of
+  # the pseudo lock all with the same guard (_CLOUD_STORAGE_GLOBAL_LOCK).
+  # Otherwise, we can get nasty interleavings that result in multiple processes
+  # thinking they have an exclusive lock, like:
+  #
+  # (Process 1) Create and acquire the pseudo lock
+  # (Process 1) Release the pseudo lock
+  # (Process 1) Release the file lock
+  # (Process 2) Open and acquire the existing pseudo lock
+  # (Process 1) Delete the (existing) pseudo lock
+  # (Process 3) Create and acquire a new pseudo lock
+  #
+  # Using the same guard for creation and removal of the pseudo lock guarantees
+  # that all processes are referring to the same lock.
+  pseudo_lock_fd = None
+  pseudo_lock_fd_return = []
+  py_utils.WaitFor(lambda: _AttemptPseudoLockAcquisition(pseudo_lock_path,
+                                                         pseudo_lock_fd_return),
+                   LOCK_ACQUISITION_TIMEOUT)
+  pseudo_lock_fd = pseudo_lock_fd_return[0]
 
-  # Guard the creation & acquiring lock of |pseudo_lock_path| by the global lock
-  # to make sure that there is no race condition on creating the file.
-  with open(_CLOUD_STORAGE_GLOBAL_LOCK) as global_file:
-    with lock.FileLock(global_file, lock.LOCK_EX):
-      fd = open(pseudo_lock_path, 'w')
-      lock.AcquireFileLock(fd, lock.LOCK_EX)
   try:
     yield
   finally:
-    lock.ReleaseFileLock(fd)
-    try:
-      fd.close()
-      os.remove(pseudo_lock_path)
-    except OSError:
-      # We don't care if the pseudo-lock gets removed elsewhere before we have
-      # a chance to do so.
-      pass
+    py_utils.WaitFor(lambda: _AttemptPseudoLockRelease(pseudo_lock_fd),
+                     LOCK_ACQUISITION_TIMEOUT)
+
+def _AttemptPseudoLockAcquisition(pseudo_lock_path, pseudo_lock_fd_return):
+  """Try to acquire the lock and return a boolean indicating whether the attempt
+  was successful. If the attempt was successful, pseudo_lock_fd_return, which
+  should be an empty array, will be modified to contain a single entry: the file
+  descriptor of the (now acquired) lock file.
+
+  This whole operation is guarded with the global cloud storage lock, which
+  prevents race conditions that might otherwise cause multiple processes to
+  believe they hold the same pseudo lock (see _FileLock for more details).
+  """
+  pseudo_lock_fd = None
+  try:
+    with open(_CLOUD_STORAGE_GLOBAL_LOCK) as global_file:
+      with lock.FileLock(global_file, lock.LOCK_EX | lock.LOCK_NB):
+        # Attempt to acquire the lock in a non-blocking manner. If we block,
+        # then we'll cause deadlock because another process will be unable to
+        # acquire the cloud storage global lock in order to release the pseudo
+        # lock.
+        pseudo_lock_fd = open(pseudo_lock_path, 'w')
+        lock.AcquireFileLock(pseudo_lock_fd, lock.LOCK_EX | lock.LOCK_NB)
+        pseudo_lock_fd_return.append(pseudo_lock_fd)
+        return True
+  except (lock.LockException, IOError):
+    # We failed to acquire either the global cloud storage lock or the pseudo
+    # lock.
+    if pseudo_lock_fd:
+      pseudo_lock_fd.close()
+    return False
+
+
+def _AttemptPseudoLockRelease(pseudo_lock_fd):
+  """Try to release the pseudo lock and return a boolean indicating whether
+  the release was succesful.
+
+  This whole operation is guarded with the global cloud storage lock, which
+  prevents race conditions that might otherwise cause multiple processes to
+  believe they hold the same pseudo lock (see _FileLock for more details).
+  """
+  pseudo_lock_path = pseudo_lock_fd.name
+  try:
+    with open(_CLOUD_STORAGE_GLOBAL_LOCK) as global_file:
+      with lock.FileLock(global_file, lock.LOCK_EX | lock.LOCK_NB):
+        lock.ReleaseFileLock(pseudo_lock_fd)
+        pseudo_lock_fd.close()
+        try:
+          os.remove(pseudo_lock_path)
+        except OSError:
+          # We don't care if the pseudo lock gets removed elsewhere before
+          # we have a chance to do so.
+          pass
+        return True
+  except (lock.LockException, IOError):
+    # We failed to acquire the global cloud storage lock and are thus unable to
+    # release the pseudo lock.
+    return False
 
 
 def _CreateDirectoryIfNecessary(directory):
@@ -267,7 +336,7 @@
 
 def _GetLocked(bucket, remote_path, local_path):
   url = 'gs://%s/%s' % (bucket, remote_path)
-  logging.info('Downloading %s to %s', url, local_path)
+  logger.info('Downloading %s to %s', url, local_path)
   _CreateDirectoryIfNecessary(os.path.dirname(local_path))
   with tempfile.NamedTemporaryFile(
       dir=os.path.dirname(local_path),
@@ -278,7 +347,7 @@
       try:
         _RunCommand(['cp', url, partial_download_path.name])
       except ServerError:
-        logging.info('Cloud Storage server error, retrying download')
+        logger.info('Cloud Storage server error, retrying download')
         _RunCommand(['cp', url, partial_download_path.name])
       shutil.move(partial_download_path.name, local_path)
     finally:
@@ -305,7 +374,7 @@
     command_and_args += ['-a', 'public-read']
     extra_info = ' (publicly readable)'
   command_and_args += [local_path, url]
-  logging.info('Uploading %s to %s%s', local_path, url, extra_info)
+  logger.info('Uploading %s to %s%s', local_path, url, extra_info)
   _RunCommand(command_and_args)
   return 'https://console.developers.google.com/m/cloudstorage/b/%s/o/%s' % (
       bucket, remote_path)
@@ -344,7 +413,7 @@
   with _FileLock(file_path):
     hash_path = file_path + '.sha1'
     if not os.path.exists(hash_path):
-      logging.warning('Hash file not found: %s', hash_path)
+      logger.warning('Hash file not found: %s', hash_path)
       return False
 
     expected_hash = ReadHash(hash_path)
diff --git a/sdk/platform-tools/systrace/catapult/common/py_utils/py_utils/cloud_storage_unittest.py b/sdk/platform-tools/systrace/catapult/common/py_utils/py_utils/cloud_storage_unittest.py
index ce03867..a513b26 100644
--- a/sdk/platform-tools/systrace/catapult/common/py_utils/py_utils/cloud_storage_unittest.py
+++ b/sdk/platform-tools/systrace/catapult/common/py_utils/py_utils/cloud_storage_unittest.py
@@ -3,7 +3,9 @@
 # found in the LICENSE file.
 
 import os
+import shutil
 import sys
+import tempfile
 import unittest
 
 import mock
@@ -11,7 +13,10 @@
 
 import py_utils
 from py_utils import cloud_storage
+from py_utils import lock
 
+_CLOUD_STORAGE_GLOBAL_LOCK_PATH = os.path.join(
+    os.path.dirname(__file__), 'cloud_storage_global_lock.py')
 
 def _FakeReadHash(_):
   return 'hashthis!'
@@ -25,7 +30,7 @@
   return 'omgnewhash'
 
 
-class CloudStorageUnitTest(fake_filesystem_unittest.TestCase):
+class CloudStorageFakeFsUnitTest(fake_filesystem_unittest.TestCase):
 
   def setUp(self):
     self.original_environ = os.environ.copy()
@@ -236,3 +241,44 @@
       cloud_storage.Insert('bucket', 'foo', file_path)
     with self.assertRaises(cloud_storage.CloudStorageIODisabled):
       cloud_storage.GetFilesInDirectoryIfChanged(dir_path, 'bucket')
+
+
+class CloudStorageRealFsUnitTest(unittest.TestCase):
+
+  def setUp(self):
+    self.original_environ = os.environ.copy()
+    os.environ['DISABLE_CLOUD_STORAGE_IO'] = ''
+
+  def tearDown(self):
+    os.environ = self.original_environ
+
+  @mock.patch('py_utils.cloud_storage.LOCK_ACQUISITION_TIMEOUT', .005)
+  def testGetPseudoLockUnavailableCausesTimeout(self):
+    with tempfile.NamedTemporaryFile(suffix='.pseudo_lock') as pseudo_lock_fd:
+      with lock.FileLock(pseudo_lock_fd, lock.LOCK_EX | lock.LOCK_NB):
+        with self.assertRaises(py_utils.TimeoutException):
+          file_path = pseudo_lock_fd.name.replace('.pseudo_lock', '')
+          cloud_storage.GetIfChanged(file_path, cloud_storage.PUBLIC_BUCKET)
+
+  @mock.patch('py_utils.cloud_storage.LOCK_ACQUISITION_TIMEOUT', .005)
+  def testGetGlobalLockUnavailableCausesTimeout(self):
+    with open(_CLOUD_STORAGE_GLOBAL_LOCK_PATH) as global_lock_fd:
+      with lock.FileLock(global_lock_fd, lock.LOCK_EX | lock.LOCK_NB):
+        tmp_dir = tempfile.mkdtemp()
+        try:
+          file_path = os.path.join(tmp_dir, 'foo')
+          with self.assertRaises(py_utils.TimeoutException):
+            cloud_storage.GetIfChanged(file_path, cloud_storage.PUBLIC_BUCKET)
+        finally:
+          shutil.rmtree(tmp_dir)
+
+
+class CloudStorageErrorHandlingTest(unittest.TestCase):
+  def runTest(self):
+    self.assertIsInstance(cloud_storage.GetErrorObjectForCloudStorageStderr(
+        'ServiceException: 401 Anonymous users does not have '
+        'storage.objects.get access to object chrome-partner-telemetry'),
+                          cloud_storage.CredentialsError)
+    self.assertIsInstance(cloud_storage.GetErrorObjectForCloudStorageStderr(
+        '403 Caller does not have storage.objects.list access to bucket '
+        'chrome-telemetry'), cloud_storage.PermissionError)
diff --git a/sdk/platform-tools/systrace/catapult/common/py_utils/py_utils/contextlib_ext.py b/sdk/platform-tools/systrace/catapult/common/py_utils/py_utils/contextlib_ext.py
new file mode 100644
index 0000000..922d27d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/common/py_utils/py_utils/contextlib_ext.py
@@ -0,0 +1,33 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+class _OptionalContextManager(object):
+
+  def __init__(self, manager, condition):
+    self._manager = manager
+    self._condition = condition
+
+  def __enter__(self):
+    if self._condition:
+      return self._manager.__enter__()
+    return None
+
+  def __exit__(self, exc_type, exc_val, exc_tb):
+    if self._condition:
+      return self._manager.__exit__(exc_type, exc_val, exc_tb)
+    return None
+
+
+def Optional(manager, condition):
+  """Wraps the provided context manager and runs it if condition is True.
+
+  Args:
+    manager: A context manager to conditionally run.
+    condition: If true, runs the given context manager.
+  Returns:
+    A context manager that conditionally executes the given manager.
+  """
+  return _OptionalContextManager(manager, condition)
+
diff --git a/sdk/platform-tools/systrace/catapult/common/py_utils/py_utils/contextlib_ext_unittest.py b/sdk/platform-tools/systrace/catapult/common/py_utils/py_utils/contextlib_ext_unittest.py
new file mode 100644
index 0000000..b83e7e5
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/common/py_utils/py_utils/contextlib_ext_unittest.py
@@ -0,0 +1,34 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from py_utils import contextlib_ext
+
+
+class OptionalUnittest(unittest.TestCase):
+
+  class SampleContextMgr(object):
+
+    def __init__(self):
+      self.entered = False
+      self.exited = False
+
+    def __enter__(self):
+      self.entered = True
+
+    def __exit__(self, exc_type, exc_val, exc_tb):
+      self.exited = True
+
+  def testConditionTrue(self):
+    c = self.SampleContextMgr()
+    with contextlib_ext.Optional(c, True):
+      self.assertTrue(c.entered)
+    self.assertTrue(c.exited)
+
+  def testConditionFalse(self):
+    c = self.SampleContextMgr()
+    with contextlib_ext.Optional(c, False):
+      self.assertFalse(c.entered)
+    self.assertFalse(c.exited)
diff --git a/sdk/platform-tools/systrace/catapult/common/py_utils/py_utils/py_utils_unittest.py b/sdk/platform-tools/systrace/catapult/common/py_utils/py_utils/py_utils_unittest.py
index e045bca..e614a45 100644
--- a/sdk/platform-tools/systrace/catapult/common/py_utils/py_utils/py_utils_unittest.py
+++ b/sdk/platform-tools/systrace/catapult/common/py_utils/py_utils/py_utils_unittest.py
@@ -21,3 +21,35 @@
 
 def _GetFileInTestDir(file_name):
   return os.path.join(os.path.dirname(__file__), 'test_data', file_name)
+
+
+class WaitForTest(unittest.TestCase):
+
+  def testWaitForTrue(self):
+    def ReturnTrue():
+      return True
+    self.assertTrue(py_utils.WaitFor(ReturnTrue, .1))
+
+  def testWaitForFalse(self):
+    def ReturnFalse():
+      return False
+
+    with self.assertRaises(py_utils.TimeoutException):
+      py_utils.WaitFor(ReturnFalse, .1)
+
+  def testWaitForEventuallyTrue(self):
+    # Use list to pass to inner function in order to allow modifying the
+    # variable from the outer scope.
+    c = [0]
+    def ReturnCounterBasedValue():
+      c[0] += 1
+      return c[0] > 2
+
+    self.assertTrue(py_utils.WaitFor(ReturnCounterBasedValue, .5))
+
+  def testWaitForTrueLambda(self):
+    self.assertTrue(py_utils.WaitFor(lambda: True, .1))
+
+  def testWaitForFalseLambda(self):
+    with self.assertRaises(py_utils.TimeoutException):
+      py_utils.WaitFor(lambda: False, .1)
diff --git a/sdk/platform-tools/systrace/catapult/common/py_utils/py_utils/tempfile_ext.py b/sdk/platform-tools/systrace/catapult/common/py_utils/py_utils/tempfile_ext.py
new file mode 100644
index 0000000..b095530
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/common/py_utils/py_utils/tempfile_ext.py
@@ -0,0 +1,25 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+import contextlib
+import shutil
+import tempfile
+
+
+@contextlib.contextmanager
+def NamedTemporaryDirectory(suffix='', prefix='tmp', dir=None):
+  """A context manager that manages a temporary directory.
+
+  This is a context manager version of tempfile.mkdtemp. The arguments to this
+  function are the same as the arguments for that one.
+  """
+  # This uses |dir| as a parameter name for consistency with mkdtemp.
+  # pylint: disable=redefined-builtin
+
+  d = tempfile.mkdtemp(suffix=suffix, prefix=prefix, dir=dir)
+  try:
+    yield d
+  finally:
+    shutil.rmtree(d)
diff --git a/sdk/platform-tools/systrace/catapult/common/py_utils/py_utils/tempfile_ext_unittest.py b/sdk/platform-tools/systrace/catapult/common/py_utils/py_utils/tempfile_ext_unittest.py
new file mode 100644
index 0000000..6844623
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/common/py_utils/py_utils/tempfile_ext_unittest.py
@@ -0,0 +1,39 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+
+from py_utils import tempfile_ext
+from pyfakefs import fake_filesystem_unittest
+
+
+class NamedTemporaryDirectoryTest(fake_filesystem_unittest.TestCase):
+
+  def setUp(self):
+    self.setUpPyfakefs()
+
+  def tearDown(self):
+    self.tearDownPyfakefs()
+
+  def testBasic(self):
+    with tempfile_ext.NamedTemporaryDirectory() as d:
+      self.assertTrue(os.path.exists(d))
+      self.assertTrue(os.path.isdir(d))
+    self.assertFalse(os.path.exists(d))
+
+  def testSuffix(self):
+    test_suffix = 'foo'
+    with tempfile_ext.NamedTemporaryDirectory(suffix=test_suffix) as d:
+      self.assertTrue(os.path.basename(d).endswith(test_suffix))
+
+  def testPrefix(self):
+    test_prefix = 'bar'
+    with tempfile_ext.NamedTemporaryDirectory(prefix=test_prefix) as d:
+      self.assertTrue(os.path.basename(d).startswith(test_prefix))
+
+  def testDir(self):
+    test_dir = '/baz'
+    self.fs.CreateDirectory(test_dir)
+    with tempfile_ext.NamedTemporaryDirectory(dir=test_dir) as d:
+      self.assertEquals(test_dir, os.path.dirname(d))
diff --git a/sdk/platform-tools/systrace/catapult/dependency_manager/bin/update b/sdk/platform-tools/systrace/catapult/dependency_manager/bin/update
new file mode 100755
index 0000000..c2ca1df
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/dependency_manager/bin/update
@@ -0,0 +1,37 @@
+#! /usr/bin/env python
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import argparse
+import os
+import sys
+
+sys.path.append(
+    os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
+from dependency_manager import base_config
+
+
+def UpdateDependency(dependency, platform, path, config):
+  c = base_config.BaseConfig(config, writable=True)
+  c.AddCloudStorageDependencyUpdateJob(
+      dependency, platform, path, version=None, execute_job=True)
+
+
+def main(raw_args):
+  parser = argparse.ArgumentParser()
+  parser.add_argument('--config', required=True, type=os.path.realpath,
+                      help='Path to the dependency configuration file.')
+  parser.add_argument('--dependency', required=True,
+                      help='Dependency name.')
+  parser.add_argument('--path', required=True, type=os.path.realpath,
+                      help='Path to the new dependency.')
+  parser.add_argument('--platform', required=True,
+                      help='Platform to update.')
+  args = parser.parse_args(raw_args)
+  UpdateDependency(args.dependency, args.platform, args.path, args.config)
+  return 0
+
+
+if __name__ == '__main__':
+  sys.exit(main(sys.argv[1:]))
diff --git a/sdk/platform-tools/systrace/catapult/devil/PRESUBMIT.py b/sdk/platform-tools/systrace/catapult/devil/PRESUBMIT.py
index edec3e1..289a5c6 100644
--- a/sdk/platform-tools/systrace/catapult/devil/PRESUBMIT.py
+++ b/sdk/platform-tools/systrace/catapult/devil/PRESUBMIT.py
@@ -26,21 +26,17 @@
     'PYTHONPATH': ':'.join([J(), J('..')]),
   })
 
-  return input_api.canned_checks.RunUnitTests(
-      input_api,
-      output_api,
-      unit_tests=[
-          J('devil_env_test.py'),
-          J('android', 'battery_utils_test.py'),
-          J('android', 'device_utils_test.py'),
-          J('android', 'fastboot_utils_test.py'),
-          J('android', 'md5sum_test.py'),
-          J('android', 'logcat_monitor_test.py'),
-          J('android', 'tools', 'script_common_test.py'),
-          J('utils', 'cmd_helper_test.py'),
-          J('utils', 'timeout_retry_unittest.py'),
-      ],
-      env=test_env)
+  message_type = (output_api.PresubmitError if input_api.is_committing
+                  else output_api.PresubmitPromptWarning)
+
+  return input_api.RunTests([
+      input_api.Command(
+          name='devil/bin/run_py_tests',
+          cmd=[
+            input_api.os_path.join(
+                input_api.PresubmitLocalPath(), 'bin', 'run_py_tests')],
+          kwargs={'env': test_env},
+          message=message_type)])
 
 
 def _EnsureNoPylibUse(input_api, output_api):
diff --git a/sdk/platform-tools/systrace/catapult/devil/README.md b/sdk/platform-tools/systrace/catapult/devil/README.md
new file mode 100644
index 0000000..852ac37
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/devil/README.md
@@ -0,0 +1,37 @@
+<!-- Copyright 2015 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file.
+-->
+## devil
+
+😈
+
+devil is a library used by the Chromium developers to interact with Android
+devices. It currently supports SDK level 16 and above.
+
+## Interfaces
+
+devil provides python APIs:
+  - [`devil.android.adb_wrapper`](docs/adb_wrapper.md) provides a thin wrapper
+    around the adb binary. Most functions and methods have direct analogues on
+    the adb command-line.
+  - [`devil.android.device_utils`](docs/device_utils.md) provides higher-level
+    functionality built on top of `adb_wrapper`. **This is the primary
+    mechanism through which chromium's scripts interact with devices.**
+
+## Utilities
+
+devil also provides command-line utilities:
+ - [`devil/utils/markdown.py`](docs/markdown.md) generated markdown
+   documentation for python modules.
+
+## Constraints and Caveats
+
+devil is used with python 2.7. Its compatibility with python 3 has not been
+tested, and neither achieving nor maintaining said compatibility is currently
+a priority.
+
+## Contributing
+
+Please see the [contributor's guide](https://github.com/catapult-project/catapult/blob/master/CONTRIBUTING.md).
+
diff --git a/sdk/platform-tools/systrace/catapult/devil/bin/generate_md_docs b/sdk/platform-tools/systrace/catapult/devil/bin/generate_md_docs
new file mode 100755
index 0000000..634e14a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/devil/bin/generate_md_docs
@@ -0,0 +1,45 @@
+#!/usr/bin/env python
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import sys
+
+_DEVIL_PATH = os.path.abspath(os.path.join(
+    os.path.dirname(__file__), '..'))
+_DEVIL_URL = (
+    'https://github.com/catapult-project/catapult/blob/master/devil/')
+
+sys.path.append(_DEVIL_PATH)
+from devil.utils import cmd_helper
+
+_FILES_TO_DOC = {
+  'devil/android/sdk/adb_wrapper.py': 'docs/adb_wrapper.md',
+  'devil/android/device_utils.py': 'docs/device_utils.md',
+  'devil/utils/markdown.py': 'docs/markdown.md',
+}
+
+_MARKDOWN_SCRIPT = os.path.join(_DEVIL_PATH, 'devil', 'utils', 'markdown.py')
+
+def main():
+  failed = False
+  for k, v in _FILES_TO_DOC.iteritems():
+    module_path = os.path.join(_DEVIL_PATH, k)
+    module_link = _DEVIL_URL + k
+    doc_path = os.path.join(_DEVIL_PATH, v)
+
+    status, stdout = cmd_helper.GetCmdStatusAndOutput(
+        [sys.executable, _MARKDOWN_SCRIPT, module_path,
+         '--module-link', module_link])
+    if status:
+      logging.error('Failed to update doc for %s' % module_path)
+      failed = True
+    else:
+      with open(doc_path, 'w') as doc_file:
+        doc_file.write(stdout)
+
+  return 1 if failed else 0
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/README.md b/sdk/platform-tools/systrace/catapult/devil/devil/README.md
deleted file mode 100644
index b3eb5d0..0000000
--- a/sdk/platform-tools/systrace/catapult/devil/devil/README.md
+++ /dev/null
@@ -1,17 +0,0 @@
-<!-- Copyright 2015 The Chromium Authors. All rights reserved.
-     Use of this source code is governed by a BSD-style license that can be
-     found in the LICENSE file.
--->
-devil
-=====
-
-devil is a library used by the Chromium developers to interact with Android
-devices. It currently supports SDK level 16 and above.
-
-😈
-
-Contributing
-============
-
-Please see the [contributor's guide](https://github.com/catapult-project/catapult/blob/master/CONTRIBUTING.md).
-
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/__init__.py b/sdk/platform-tools/systrace/catapult/devil/devil/__init__.py
index 50b23df..7de59c9 100644
--- a/sdk/platform-tools/systrace/catapult/devil/devil/__init__.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/__init__.py
@@ -1,3 +1,7 @@
 # Copyright 2015 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
+
+import logging
+
+logging.getLogger('devil').addHandler(logging.NullHandler())
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/apk_helper.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/apk_helper.py
index 61eeda0..1a9b8c5 100644
--- a/sdk/platform-tools/systrace/catapult/devil/devil/android/apk_helper.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/apk_helper.py
@@ -4,8 +4,10 @@
 
 """Module containing utilities for apk packages."""
 
+import itertools
 import re
 
+from devil import base_error
 from devil.android.sdk import aapt
 
 
@@ -54,16 +56,22 @@
 
     m = _MANIFEST_ELEMENT_RE.match(line[len(indent) * indent_depth:])
     if m:
-      if not m.group(1) in node:
-        node[m.group(1)] = {}
-      node_stack += [node[m.group(1)]]
+      manifest_key = m.group(1)
+      if manifest_key in node:
+        node[manifest_key] += [{}]
+      else:
+        node[manifest_key] = [{}]
+      node_stack += [node[manifest_key][-1]]
       continue
 
     m = _MANIFEST_ATTRIBUTE_RE.match(line[len(indent) * indent_depth:])
     if m:
-      if not m.group(1) in node:
-        node[m.group(1)] = []
-      node[m.group(1)].append(m.group(2) or m.group(3))
+      manifest_key = m.group(1)
+      if manifest_key in node:
+        raise base_error.BaseError(
+            "A single attribute should have one key and one value")
+      else:
+        node[manifest_key] = m.group(2) or m.group(3)
       continue
 
   return parsed_manifest
@@ -84,8 +92,8 @@
     manifest_info = self._GetManifest()
     try:
       activity = (
-          manifest_info['manifest']['application']['activity']
-              ['android:name'][0])
+          manifest_info['manifest'][0]['application'][0]['activity'][0]
+              ['android:name'])
     except KeyError:
       return None
     if '.' not in activity:
@@ -97,24 +105,34 @@
   def GetInstrumentationName(
       self, default='android.test.InstrumentationTestRunner'):
     """Returns the name of the Instrumentation in the apk."""
-    manifest_info = self._GetManifest()
+    all_instrumentations = self.GetAllInstrumentations(default=default)
+    if len(all_instrumentations) != 1:
+      raise base_error.BaseError(
+          'There is more than one instrumentation. Expected one.')
+    else:
+      return all_instrumentations[0]['android:name']
+
+  def GetAllInstrumentations(
+      self, default='android.test.InstrumentationTestRunner'):
+    """Returns a list of all Instrumentations in the apk."""
     try:
-      return manifest_info['manifest']['instrumentation']['android:name'][0]
+      return self._GetManifest()['manifest'][0]['instrumentation']
     except KeyError:
-      return default
+      return [{'android:name': default}]
 
   def GetPackageName(self):
     """Returns the package name of the apk."""
     manifest_info = self._GetManifest()
     try:
-      return manifest_info['manifest']['package'][0]
+      return manifest_info['manifest'][0]['package']
     except KeyError:
       raise Exception('Failed to determine package name of %s' % self._apk_path)
 
   def GetPermissions(self):
     manifest_info = self._GetManifest()
     try:
-      return manifest_info['manifest']['uses-permission']['android:name']
+      return [p['android:name'] for
+              p in manifest_info['manifest'][0]['uses-permission']]
     except KeyError:
       return []
 
@@ -122,7 +140,7 @@
     """Returns the name of the split of the apk."""
     manifest_info = self._GetManifest()
     try:
-      return manifest_info['manifest']['split'][0]
+      return manifest_info['manifest'][0]['split']
     except KeyError:
       return None
 
@@ -130,8 +148,12 @@
     """Returns whether any services exist that use isolatedProcess=true."""
     manifest_info = self._GetManifest()
     try:
-      services = manifest_info['manifest']['application']['service']
-      return any(int(v, 0) for v in services['android:isolatedProcess'])
+      applications = manifest_info['manifest'][0].get('application', [])
+      services = itertools.chain(
+          *(application.get('service', []) for application in applications))
+      return any(
+          int(s.get('android:isolatedProcess', '0'), 0)
+          for s in services)
     except KeyError:
       return False
 
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/apk_helper_test.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/apk_helper_test.py
new file mode 100755
index 0000000..f7d077d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/apk_helper_test.py
@@ -0,0 +1,169 @@
+#! /usr/bin/env python
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from devil import base_error
+from devil import devil_env
+from devil.android import apk_helper
+from devil.utils import mock_calls
+
+with devil_env.SysPath(devil_env.PYMOCK_PATH):
+  import mock  # pylint: disable=import-error
+
+
+_MANIFEST_DUMP = """N: android=http://schemas.android.com/apk/res/android
+  E: manifest (line=1)
+    A: package="org.chromium.abc" (Raw: "org.chromium.abc")
+    A: split="random_split" (Raw: "random_split")
+    E: uses-permission (line=2)
+      A: android:name(0x01010003)="android.permission.INTERNET" (Raw: "android.permission.INTERNET")
+    E: uses-permission (line=3)
+      A: android:name(0x01010003)="android.permission.READ_EXTERNAL_STORAGE" (Raw: "android.permission.READ_EXTERNAL_STORAGE")
+    E: uses-permission (line=4)
+      A: android:name(0x01010003)="android.permission.ACCESS_FINE_LOCATION" (Raw: "android.permission.ACCESS_FINE_LOCATION")
+    E: application (line=5)
+      E: activity (line=6)
+        A: android:name(0x01010003)="org.chromium.ActivityName" (Raw: "org.chromium.ActivityName")
+        A: android:exported(0x01010010)=(type 0x12)0xffffffff
+      E: service (line=7)
+        A: android:name(0x01010001)="org.chromium.RandomService" (Raw: "org.chromium.RandomService")
+        A: android:isolatedProcess(0x01010888)=(type 0x12)0xffffffff
+    E: instrumentation (line=8)
+      A: android:label(0x01010001)="abc" (Raw: "abc")
+      A: android:name(0x01010003)="org.chromium.RandomJUnit4TestRunner" (Raw: "org.chromium.RandomJUnit4TestRunner")
+      A: android:targetPackage(0x01010021)="org.chromium.random_package" (Raw:"org.chromium.random_pacakge")
+      A: junit4=(type 0x12)0xffffffff (Raw: "true")
+    E: instrumentation (line=9)
+      A: android:label(0x01010001)="abc" (Raw: "abc")
+      A: android:name(0x01010003)="org.chromium.RandomTestRunner" (Raw: "org.chromium.RandomTestRunner")
+      A: android:targetPackage(0x01010021)="org.chromium.random_package" (Raw:"org.chromium.random_pacakge")
+"""
+
+_NO_ISOLATED_SERVICES = """N: android=http://schemas.android.com/apk/res/android
+  E: manifest (line=1)
+    A: package="org.chromium.abc" (Raw: "org.chromium.abc")
+    E: application (line=5)
+      E: activity (line=6)
+        A: android:name(0x01010003)="org.chromium.ActivityName" (Raw: "org.chromium.ActivityName")
+        A: android:exported(0x01010010)=(type 0x12)0xffffffff
+      E: service (line=7)
+        A: android:name(0x01010001)="org.chromium.RandomService" (Raw: "org.chromium.RandomService")
+"""
+
+_NO_SERVICES = """N: android=http://schemas.android.com/apk/res/android
+  E: manifest (line=1)
+    A: package="org.chromium.abc" (Raw: "org.chromium.abc")
+    E: application (line=5)
+      E: activity (line=6)
+        A: android:name(0x01010003)="org.chromium.ActivityName" (Raw: "org.chromium.ActivityName")
+        A: android:exported(0x01010010)=(type 0x12)0xffffffff
+"""
+
+_NO_APPLICATION = """N: android=http://schemas.android.com/apk/res/android
+  E: manifest (line=1)
+    A: package="org.chromium.abc" (Raw: "org.chromium.abc")
+"""
+
+_SINGLE_INSTRUMENTATION_MANIFEST_DUMP = """N: android=http://schemas.android.com/apk/res/android
+  E: manifest (line=1)
+    A: package="org.chromium.xyz" (Raw: "org.chromium.xyz")
+    E: instrumentation (line=8)
+      A: android:label(0x01010001)="xyz" (Raw: "xyz")
+      A: android:name(0x01010003)="org.chromium.RandomTestRunner" (Raw: "org.chromium.RandomTestRunner")
+      A: android:targetPackage(0x01010021)="org.chromium.random_package" (Raw:"org.chromium.random_pacakge")
+"""
+
+_SINGLE_J4_INSTRUMENTATION_MANIFEST_DUMP = """N: android=http://schemas.android.com/apk/res/android
+  E: manifest (line=1)
+    A: package="org.chromium.xyz" (Raw: "org.chromium.xyz")
+    E: instrumentation (line=8)
+      A: android:label(0x01010001)="xyz" (Raw: "xyz")
+      A: android:name(0x01010003)="org.chromium.RandomJ4TestRunner" (Raw: "org.chromium.RandomJ4TestRunner")
+      A: android:targetPackage(0x01010021)="org.chromium.random_package" (Raw:"org.chromium.random_pacakge")
+      A: junit4=(type 0x12)0xffffffff (Raw: "true")
+"""
+
+
+def _MockAaptDump(manifest_dump):
+  return mock.patch(
+      'devil.android.sdk.aapt.Dump',
+      mock.Mock(side_effect=None, return_value=manifest_dump.split('\n')))
+
+class ApkHelperTest(mock_calls.TestCase):
+
+  def testGetInstrumentationName(self):
+    with _MockAaptDump(_MANIFEST_DUMP):
+      helper = apk_helper.ApkHelper("")
+      with self.assertRaises(base_error.BaseError):
+        helper.GetInstrumentationName()
+
+  def testGetActivityName(self):
+    with _MockAaptDump(_MANIFEST_DUMP):
+      helper = apk_helper.ApkHelper("")
+      self.assertEquals(
+          helper.GetActivityName(), 'org.chromium.ActivityName')
+
+  def testGetAllInstrumentations(self):
+    with _MockAaptDump(_MANIFEST_DUMP):
+      helper = apk_helper.ApkHelper("")
+      all_instrumentations = helper.GetAllInstrumentations()
+      self.assertEquals(len(all_instrumentations), 2)
+      self.assertEquals(all_instrumentations[0]['android:name'],
+                        'org.chromium.RandomJUnit4TestRunner')
+      self.assertEquals(all_instrumentations[1]['android:name'],
+                        'org.chromium.RandomTestRunner')
+
+  def testGetPackageName(self):
+    with _MockAaptDump(_MANIFEST_DUMP):
+      helper = apk_helper.ApkHelper("")
+      self.assertEquals(helper.GetPackageName(), 'org.chromium.abc')
+
+  def testGetPermssions(self):
+    with _MockAaptDump(_MANIFEST_DUMP):
+      helper = apk_helper.ApkHelper("")
+      all_permissions = helper.GetPermissions()
+      self.assertEquals(len(all_permissions), 3)
+      self.assertTrue('android.permission.INTERNET' in all_permissions)
+      self.assertTrue(
+          'android.permission.READ_EXTERNAL_STORAGE' in all_permissions)
+      self.assertTrue(
+          'android.permission.ACCESS_FINE_LOCATION' in all_permissions)
+
+  def testGetSplitName(self):
+    with _MockAaptDump(_MANIFEST_DUMP):
+      helper = apk_helper.ApkHelper("")
+      self.assertEquals(helper.GetSplitName(), 'random_split')
+
+  def testHasIsolatedProcesses_noApplication(self):
+    with _MockAaptDump(_NO_APPLICATION):
+      helper = apk_helper.ApkHelper("")
+      self.assertFalse(helper.HasIsolatedProcesses())
+
+  def testHasIsolatedProcesses_noServices(self):
+    with _MockAaptDump(_NO_SERVICES):
+      helper = apk_helper.ApkHelper("")
+      self.assertFalse(helper.HasIsolatedProcesses())
+
+  def testHasIsolatedProcesses_oneNotIsolatedProcess(self):
+    with _MockAaptDump(_NO_ISOLATED_SERVICES):
+      helper = apk_helper.ApkHelper("")
+      self.assertFalse(helper.HasIsolatedProcesses())
+
+  def testHasIsolatedProcesses_oneIsolatedProcess(self):
+    with _MockAaptDump(_MANIFEST_DUMP):
+      helper = apk_helper.ApkHelper("")
+      self.assertTrue(helper.HasIsolatedProcesses())
+
+  def testGetSingleInstrumentationName(self):
+    with _MockAaptDump(_SINGLE_INSTRUMENTATION_MANIFEST_DUMP):
+      helper = apk_helper.ApkHelper("")
+      self.assertEquals('org.chromium.RandomTestRunner',
+                        helper.GetInstrumentationName())
+
+  def testGetSingleJUnit4InstrumentationName(self):
+    with _MockAaptDump(_SINGLE_J4_INSTRUMENTATION_MANIFEST_DUMP):
+      helper = apk_helper.ApkHelper("")
+      self.assertEquals('org.chromium.RandomJ4TestRunner',
+                        helper.GetInstrumentationName())
+
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/battery_utils.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/battery_utils.py
index 7a735f3..3b225aa 100644
--- a/sdk/platform-tools/systrace/catapult/devil/devil/android/battery_utils.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/battery_utils.py
@@ -17,14 +17,15 @@
 from devil.android.sdk import version_codes
 from devil.utils import timeout_retry
 
+logger = logging.getLogger(__name__)
+
 _DEFAULT_TIMEOUT = 30
 _DEFAULT_RETRIES = 3
 
 
 _DEVICE_PROFILES = [
   {
-    'name': 'Nexus 4',
-    'witness_file': '/sys/module/pm8921_charger/parameters/disabled',
+    'name': ['Nexus 4'],
     'enable_command': (
         'echo 0 > /sys/module/pm8921_charger/parameters/disabled && '
         'dumpsys battery reset'),
@@ -36,12 +37,11 @@
     'current': None,
   },
   {
-    'name': 'Nexus 5',
+    'name': ['Nexus 5'],
     # Nexus 5
     # Setting the HIZ bit of the bq24192 causes the charger to actually ignore
     # energy coming from USB. Setting the power_supply offline just updates the
     # Android system to reflect that.
-    'witness_file': '/sys/kernel/debug/bq24192/INPUT_SRC_CONT',
     'enable_command': (
         'echo 0x4A > /sys/kernel/debug/bq24192/INPUT_SRC_CONT && '
         'chmod 644 /sys/class/power_supply/usb/online && '
@@ -57,8 +57,7 @@
     'current': None,
   },
   {
-    'name': 'Nexus 6',
-    'witness_file': None,
+    'name': ['Nexus 6'],
     'enable_command': (
         'echo 1 > /sys/class/power_supply/battery/charging_enabled && '
         'dumpsys battery reset'),
@@ -71,8 +70,7 @@
     'current': '/sys/class/power_supply/max170xx_battery/current_now',
   },
   {
-    'name': 'Nexus 9',
-    'witness_file': None,
+    'name': ['Nexus 9'],
     'enable_command': (
         'echo Disconnected > '
         '/sys/bus/i2c/drivers/bq2419x/0-006b/input_cable_state && '
@@ -86,8 +84,7 @@
     'current': '/sys/class/power_supply/battery/current_now',
   },
   {
-    'name': 'Nexus 10',
-    'witness_file': None,
+    'name': ['Nexus 10'],
     'enable_command': None,
     'disable_command': None,
     'charge_counter': None,
@@ -96,8 +93,7 @@
 
   },
   {
-    'name': 'Nexus 5X',
-    'witness_file': None,
+    'name': ['Nexus 5X'],
     'enable_command': (
         'echo 1 > /sys/class/power_supply/battery/charging_enabled && '
         'dumpsys battery reset'),
@@ -108,6 +104,42 @@
     'voltage': None,
     'current': None,
   },
+  { # Galaxy s5
+    'name': ['SM-G900H'],
+    'enable_command': (
+        'chmod 644 /sys/class/power_supply/battery/test_mode && '
+        'chmod 644 /sys/class/power_supply/sec-charger/current_now && '
+        'echo 0 > /sys/class/power_supply/battery/test_mode && '
+        'echo 9999 > /sys/class/power_supply/sec-charger/current_now &&'
+        'dumpsys battery reset'),
+    'disable_command': (
+        'chmod 644 /sys/class/power_supply/battery/test_mode && '
+        'chmod 644 /sys/class/power_supply/sec-charger/current_now && '
+        'echo 1 > /sys/class/power_supply/battery/test_mode && '
+        'echo 0 > /sys/class/power_supply/sec-charger/current_now && '
+        'dumpsys battery set ac 0 && dumpsys battery set usb 0'),
+    'charge_counter': None,
+    'voltage': '/sys/class/power_supply/sec-fuelgauge/voltage_now',
+    'current': '/sys/class/power_supply/sec-charger/current_now',
+  },
+  { # Galaxy s6, Galaxy s6, Galaxy s6 edge
+    'name': ['SM-G920F', 'SM-G920V', 'SM-G925V'],
+    'enable_command': (
+        'chmod 644 /sys/class/power_supply/battery/test_mode && '
+        'chmod 644 /sys/class/power_supply/max77843-charger/current_now && '
+        'echo 0 > /sys/class/power_supply/battery/test_mode && '
+        'echo 9999 > /sys/class/power_supply/max77843-charger/current_now &&'
+        'dumpsys battery reset'),
+    'disable_command': (
+        'chmod 644 /sys/class/power_supply/battery/test_mode && '
+        'chmod 644 /sys/class/power_supply/max77843-charger/current_now && '
+        'echo 1 > /sys/class/power_supply/battery/test_mode && '
+        'echo 0 > /sys/class/power_supply/max77843-charger/current_now && '
+        'dumpsys battery set ac 0 && dumpsys battery set usb 0'),
+    'charge_counter': None,
+    'voltage': '/sys/class/power_supply/max77843-fuelgauge/voltage_now',
+    'current': '/sys/class/power_supply/max77843-charger/current_now',
+  },
 ]
 
 # The list of useful dumpsys columns.
@@ -214,8 +246,8 @@
     if package not in self._cache['uids']:
       self.GetPowerData()
       if package not in self._cache['uids']:
-        logging.warning('No UID found for %s. Can\'t get network data.',
-                        package)
+        logger.warning('No UID found for %s. Can\'t get network data.',
+                       package)
         return None
 
     network_data_path = '/proc/uid_stat/%s/' % self._cache['uids'][package]
@@ -224,12 +256,12 @@
     # If ReadFile throws exception, it means no network data usage file for
     # package has been recorded. Return 0 sent and 0 received.
     except device_errors.AdbShellCommandFailedError:
-      logging.warning('No sent data found for package %s', package)
+      logger.warning('No sent data found for package %s', package)
       send_data = 0
     try:
       recv_data = int(self._device.ReadFile(network_data_path + 'tcp_rcv'))
     except device_errors.AdbShellCommandFailedError:
-      logging.warning('No received data found for package %s', package)
+      logger.warning('No received data found for package %s', package)
       recv_data = 0
     return (send_data, recv_data)
 
@@ -311,10 +343,10 @@
         ['dumpsys', 'battery'], check_return=True)[1:]:
       # If usb charging has been disabled, an extra line of header exists.
       if 'UPDATES STOPPED' in line:
-        logging.warning('Dumpsys battery not receiving updates. '
-                        'Run dumpsys battery reset if this is in error.')
+        logger.warning('Dumpsys battery not receiving updates. '
+                       'Run dumpsys battery reset if this is in error.')
       elif ':' not in line:
-        logging.warning('Unknown line found in dumpsys battery: "%s"', line)
+        logger.warning('Unknown line found in dumpsys battery: "%s"', line)
       else:
         k, v = line.split(':', 1)
         result[k.strip()] = v.strip()
@@ -428,12 +460,12 @@
       raise ValueError('Discharge amount(%s) must be between 1 and 99'
                        % percent)
     if battery_level is None:
-      logging.warning('Unable to find current battery level. Cannot discharge.')
+      logger.warning('Unable to find current battery level. Cannot discharge.')
       return
     # Do not discharge if it would make battery level too low.
     if percent >= battery_level - 10:
-      logging.warning('Battery is too low or discharge amount requested is too '
-                      'high. Cannot discharge phone %s percent.', percent)
+      logger.warning('Battery is too low or discharge amount requested is too '
+                     'high. Cannot discharge phone %s percent.', percent)
       return
 
     self._HardwareSetCharging(False)
@@ -441,7 +473,7 @@
     def device_discharged():
       self._HardwareSetCharging(True)
       current_level = int(self.GetBatteryInfo().get('level'))
-      logging.info('current battery level: %s', current_level)
+      logger.info('current battery level: %s', current_level)
       if battery_level - current_level >= percent:
         return True
       self._HardwareSetCharging(False)
@@ -466,10 +498,10 @@
     def device_charged():
       battery_level = self.GetBatteryInfo().get('level')
       if battery_level is None:
-        logging.warning('Unable to find current battery level.')
+        logger.warning('Unable to find current battery level.')
         battery_level = 100
       else:
-        logging.info('current battery level: %s', battery_level)
+        logger.info('current battery level: %s', battery_level)
         battery_level = int(battery_level)
 
       # Use > so that it will not reset if charge is going down.
@@ -497,21 +529,21 @@
     def cool_device():
       temp = self.GetBatteryInfo().get('temperature')
       if temp is None:
-        logging.warning('Unable to find current battery temperature.')
+        logger.warning('Unable to find current battery temperature.')
         temp = 0
       else:
-        logging.info('Current battery temperature: %s', temp)
+        logger.info('Current battery temperature: %s', temp)
       if int(temp) <= target_temp:
         return True
       else:
-        if self._cache['profile']['name'] == 'Nexus 5':
+        if 'Nexus 5' in self._cache['profile']['name']:
           self._DischargeDevice(1)
         return False
 
     self._DiscoverDeviceProfile()
     self.EnableBatteryUpdates()
-    logging.info('Waiting for the device to cool down to %s (0.1 C)',
-                 target_temp)
+    logger.info('Waiting for the device to cool down to %s (0.1 C)',
+                target_temp)
     timeout_retry.WaitFor(cool_device, wait_period=wait_period)
 
   @decorators.WithTimeoutAndRetriesFromInstance()
@@ -525,7 +557,7 @@
       retries: number of retries
     """
     if self.GetCharging() == enabled:
-      logging.warning('Device charging already in expected state: %s', enabled)
+      logger.warning('Device charging already in expected state: %s', enabled)
       return
 
     self._DiscoverDeviceProfile()
@@ -533,15 +565,15 @@
       if self._cache['profile']['enable_command']:
         self._HardwareSetCharging(enabled)
       else:
-        logging.info('Unable to enable charging via hardware. '
-                     'Falling back to software enabling.')
+        logger.info('Unable to enable charging via hardware. '
+                    'Falling back to software enabling.')
         self.EnableBatteryUpdates()
     else:
       if self._cache['profile']['enable_command']:
         self._ClearPowerData()
         self._HardwareSetCharging(enabled)
       else:
-        logging.info('Unable to disable charging via hardware. '
+        logger.info('Unable to disable charging via hardware. '
                      'Falling back to software disabling.')
         self.DisableBatteryUpdates()
 
@@ -613,8 +645,8 @@
         but fails.
     """
     if self._device.build_version_sdk < version_codes.LOLLIPOP:
-      logging.warning('Dumpsys power data only available on 5.0 and above. '
-                      'Cannot clear power data.')
+      logger.warning('Dumpsys power data only available on 5.0 and above. '
+                     'Cannot clear power data.')
       return False
 
     self._device.RunShellCommand(
@@ -653,12 +685,11 @@
     if 'profile' in self._cache:
       return True
     for profile in _DEVICE_PROFILES:
-      if self._device.product_model == profile['name']:
+      if self._device.product_model in profile['name']:
         self._cache['profile'] = profile
         return True
     self._cache['profile'] = {
-        'name': None,
-        'witness_file': None,
+        'name': [],
         'enable_command': None,
         'disable_command': None,
         'charge_counter': None,
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/battery_utils_test.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/battery_utils_test.py
index 9fbd127..beaba3b 100755
--- a/sdk/platform-tools/systrace/catapult/devil/devil/android/battery_utils_test.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/battery_utils_test.py
@@ -616,13 +616,13 @@
     with self.patch_call(self.call.device.product_model,
                          return_value='Nexus 4'):
       self.battery._DiscoverDeviceProfile()
-      self.assertEqual(self.battery._cache['profile']['name'], "Nexus 4")
+      self.assertListEqual(self.battery._cache['profile']['name'], ["Nexus 4"])
 
   def testDiscoverDeviceProfile_unknown(self):
     with self.patch_call(self.call.device.product_model,
                          return_value='Other'):
       self.battery._DiscoverDeviceProfile()
-      self.assertEqual(self.battery._cache['profile']['name'], None)
+      self.assertListEqual(self.battery._cache['profile']['name'], [])
 
 
 class BatteryUtilsClearPowerData(BatteryUtilsTest):
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/constants/chrome.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/constants/chrome.py
index 006764f..dca04bd 100644
--- a/sdk/platform-tools/systrace/catapult/devil/devil/android/constants/chrome.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/constants/chrome.py
@@ -12,46 +12,46 @@
     'chrome_document': PackageInfo(
         'com.google.android.apps.chrome.document',
         'com.google.android.apps.chrome.document.ChromeLauncherActivity',
-        '/data/local/chrome-command-line',
+        'chrome-command-line',
         'chrome_devtools_remote'),
     'chrome': PackageInfo(
         'com.google.android.apps.chrome',
         'com.google.android.apps.chrome.Main',
-        '/data/local/chrome-command-line',
+        'chrome-command-line',
         'chrome_devtools_remote'),
     'chrome_beta': PackageInfo(
         'com.chrome.beta',
         'com.google.android.apps.chrome.Main',
-        '/data/local/chrome-command-line',
+        'chrome-command-line',
         'chrome_devtools_remote'),
     'chrome_stable': PackageInfo(
         'com.android.chrome',
         'com.google.android.apps.chrome.Main',
-        '/data/local/chrome-command-line',
+        'chrome-command-line',
         'chrome_devtools_remote'),
     'chrome_dev': PackageInfo(
         'com.chrome.dev',
         'com.google.android.apps.chrome.Main',
-        '/data/local/chrome-command-line',
+        'chrome-command-line',
         'chrome_devtools_remote'),
     'chrome_canary': PackageInfo(
         'com.chrome.canary',
         'com.google.android.apps.chrome.Main',
-        '/data/local/chrome-command-line',
+        'chrome-command-line',
         'chrome_devtools_remote'),
     'chrome_work': PackageInfo(
         'com.chrome.work',
         'com.google.android.apps.chrome.Main',
-        '/data/local/chrome-command-line',
+        'chrome-command-line',
         'chrome_devtools_remote'),
     'chromium': PackageInfo(
         'org.chromium.chrome',
         'com.google.android.apps.chrome.Main',
-        '/data/local/chrome-command-line',
+        'chrome-command-line',
         'chrome_devtools_remote'),
     'content_shell': PackageInfo(
         'org.chromium.content_shell_apk',
         '.ContentShellActivity',
-        '/data/local/tmp/content-shell-command-line',
+        'content-shell-command-line',
         'content_shell_devtools_remote'),
 }
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/device_blacklist.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/device_blacklist.py
index e8055a7..010e996 100644
--- a/sdk/platform-tools/systrace/catapult/devil/devil/android/device_blacklist.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/device_blacklist.py
@@ -8,6 +8,8 @@
 import threading
 import time
 
+logger = logging.getLogger(__name__)
+
 
 class Blacklist(object):
 
@@ -30,11 +32,11 @@
         with open(self._path, 'r') as f:
           blacklist = json.load(f)
       except (IOError, ValueError) as e:
-        logging.warning('Unable to read blacklist: %s', str(e))
+        logger.warning('Unable to read blacklist: %s', str(e))
         os.remove(self._path)
 
       if not isinstance(blacklist, dict):
-        logging.warning('Ignoring %s: %s (a dict was expected instead)',
+        logger.warning('Ignoring %s: %s (a dict was expected instead)',
                         self._path, blacklist)
         blacklist = dict()
 
@@ -63,7 +65,7 @@
         'reason': reason,
     }
     device_dicts = {device: event_info for device in devices}
-    logging.info('Adding %s to blacklist %s for reason: %s',
+    logger.info('Adding %s to blacklist %s for reason: %s',
                  ','.join(devices), self._path, reason)
     with self._blacklist_lock:
       blacklist = self.Read()
@@ -72,7 +74,7 @@
 
   def Reset(self):
     """Erases the blacklist file if it exists."""
-    logging.info('Resetting blacklist %s', self._path)
+    logger.info('Resetting blacklist %s', self._path)
     with self._blacklist_lock:
       if os.path.exists(self._path):
         os.remove(self._path)
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/device_errors.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/device_errors.py
index 67faa48..568e497 100644
--- a/sdk/platform-tools/systrace/catapult/devil/devil/android/device_errors.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/device_errors.py
@@ -15,11 +15,19 @@
   """Exception for command failures."""
 
   def __init__(self, message, device_serial=None):
-    if device_serial is not None:
-      message = '(device: %s) %s' % (device_serial, message)
+    device_leader = '(device: %s)' % device_serial
+    if device_serial is not None and not message.startswith(device_leader):
+      message = '%s %s' % (device_leader, message)
     self.device_serial = device_serial
     super(CommandFailedError, self).__init__(message)
 
+  def __eq__(self, other):
+    return (super(CommandFailedError, self).__eq__(other)
+            and self.device_serial == other.device_serial)
+
+  def __ne__(self, other):
+    return not self == other
+
 
 class _BaseCommandFailedError(CommandFailedError):
   """Base Exception for adb and fastboot command failures."""
@@ -42,6 +50,27 @@
       message = ''.join(message)
     super(_BaseCommandFailedError, self).__init__(message, device_serial)
 
+  def __eq__(self, other):
+    return (super(_BaseCommandFailedError, self).__eq__(other)
+            and self.args == other.args
+            and self.output == other.output
+            and self.status == other.status)
+
+  def __ne__(self, other):
+    return not self == other
+
+  def __reduce__(self):
+    """Support pickling."""
+    result = [None, None, None, None, None]
+    super_result = super(_BaseCommandFailedError, self).__reduce__()
+    for i in range(len(super_result)):
+      result[i] = super_result[i]
+
+    # Update the args used to reconstruct this exception.
+    result[1] = (
+        self.args, self.output, self.status, self.device_serial, self.message)
+    return tuple(result)
+
 
 class AdbCommandFailedError(_BaseCommandFailedError):
   """Exception for adb command failures."""
@@ -91,6 +120,17 @@
     super(AdbShellCommandFailedError, self).__init__(
       ['shell', command], output, status, device_serial, message)
 
+  def __reduce__(self):
+    """Support pickling."""
+    result = [None, None, None, None, None]
+    super_result = super(AdbShellCommandFailedError, self).__reduce__()
+    for i in range(len(super_result)):
+      result[i] = super_result[i]
+
+    # Update the args used to reconstruct this exception.
+    result[1] = (self.command, self.output, self.status, self.device_serial)
+    return tuple(result)
+
 
 class CommandTimeoutError(base_error.BaseError):
   """Exception for command timeouts."""
@@ -105,9 +145,9 @@
 class NoDevicesError(base_error.BaseError):
   """Exception for having no devices attached."""
 
-  def __init__(self):
+  def __init__(self, msg=None):
     super(NoDevicesError, self).__init__(
-        'No devices attached.', is_infra_error=True)
+        msg or 'No devices attached.', is_infra_error=True)
 
 
 class MultipleDevicesError(base_error.BaseError):
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/device_errors_test.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/device_errors_test.py
new file mode 100755
index 0000000..68a4f16
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/device_errors_test.py
@@ -0,0 +1,72 @@
+#! /usr/bin/env python
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import pickle
+import sys
+import unittest
+
+from devil.android import device_errors
+
+
+class DeviceErrorsTest(unittest.TestCase):
+
+  def assertIsPicklable(self, original):
+    pickled = pickle.dumps(original)
+    reconstructed = pickle.loads(pickled)
+    self.assertEquals(original, reconstructed)
+
+  def testPicklable_AdbCommandFailedError(self):
+    original = device_errors.AdbCommandFailedError(
+        ['these', 'are', 'adb', 'args'], 'adb failure output', status=':(',
+        device_serial='0123456789abcdef')
+    self.assertIsPicklable(original)
+
+  def testPicklable_AdbShellCommandFailedError(self):
+    original = device_errors.AdbShellCommandFailedError(
+        'foo', 'erroneous foo output', '1', device_serial='0123456789abcdef')
+    self.assertIsPicklable(original)
+
+  def testPicklable_CommandFailedError(self):
+    original = device_errors.CommandFailedError(
+        'sample command failed')
+    self.assertIsPicklable(original)
+
+  def testPicklable_CommandTimeoutError(self):
+    original = device_errors.CommandTimeoutError(
+        'My fake command timed out :(')
+    self.assertIsPicklable(original)
+
+  def testPicklable_DeviceChargingError(self):
+    original = device_errors.DeviceChargingError(
+        'Fake device failed to charge')
+    self.assertIsPicklable(original)
+
+  def testPicklable_DeviceUnreachableError(self):
+    original = device_errors.DeviceUnreachableError
+    self.assertIsPicklable(original)
+
+  def testPicklable_FastbootCommandFailedError(self):
+    original = device_errors.FastbootCommandFailedError(
+        ['these', 'are', 'fastboot', 'args'], 'fastboot failure output',
+        status=':(', device_serial='0123456789abcdef')
+    self.assertIsPicklable(original)
+
+  def testPicklable_MultipleDevicesError(self):
+    # TODO(jbudorick): Implement this after implementing a stable DeviceUtils
+    # fake. https://github.com/catapult-project/catapult/issues/3145
+    pass
+
+  def testPicklable_NoAdbError(self):
+    original = device_errors.NoAdbError()
+    self.assertIsPicklable(original)
+
+  def testPicklable_NoDevicesError(self):
+    original = device_errors.NoDevicesError()
+    self.assertIsPicklable(original)
+
+
+
+if __name__ == '__main__':
+  sys.exit(unittest.main())
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/device_list.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/device_list.py
index 36a03b1..0fbb0f1 100644
--- a/sdk/platform-tools/systrace/catapult/devil/devil/android/device_list.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/device_list.py
@@ -8,6 +8,8 @@
 import logging
 import os
 
+logger = logging.getLogger(__name__)
+
 
 def GetPersistentDeviceList(file_name):
   """Returns a list of devices.
@@ -18,7 +20,7 @@
   Returns: List of device serial numbers that were on the bot.
   """
   if not os.path.isfile(file_name):
-    logging.warning('Device file %s doesn\'t exist.', file_name)
+    logger.warning("Device file %s doesn't exist.", file_name)
     return []
 
   try:
@@ -26,11 +28,11 @@
       devices = json.load(f)
     if not isinstance(devices, list) or not all(isinstance(d, basestring)
                                                 for d in devices):
-      logging.warning('Unrecognized device file format: %s', devices)
+      logger.warning('Unrecognized device file format: %s', devices)
       return []
     return [d for d in devices if d != '(error)']
   except ValueError:
-    logging.exception(
+    logger.exception(
         'Error reading device file %s. Falling back to old format.', file_name)
 
   # TODO(bpastene) Remove support for old unstructured file format.
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/device_temp_file.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/device_temp_file.py
index 75488c5..4d0c7ad 100644
--- a/sdk/platform-tools/systrace/catapult/devil/devil/android/device_temp_file.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/device_temp_file.py
@@ -26,7 +26,14 @@
       suffix: The suffix of the name of the temp file.
       prefix: The prefix of the name of the temp file.
       dir: The directory on the device where to place the temp file.
+    Raises:
+      ValueError if any of suffix, prefix, or dir are None.
     """
+    if None in (dir, prefix, suffix):
+      m = 'Provided None path component. (dir: %s, prefix: %s, suffix: %s)' % (
+          dir, prefix, suffix)
+      raise ValueError(m)
+
     self._adb = adb
     # Python's random module use 52-bit numbers according to its docs.
     random_hex = hex(random.randint(0, 2 ** 52))[2:]
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/device_utils.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/device_utils.py
index 1a99650..7ba1b51 100644
--- a/sdk/platform-tools/systrace/catapult/devil/devil/android/device_utils.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/device_utils.py
@@ -16,6 +16,7 @@
 import multiprocessing
 import os
 import posixpath
+import pprint
 import re
 import shutil
 import stat
@@ -49,6 +50,8 @@
 from devil.utils import timeout_retry
 from devil.utils import zip_utils
 
+logger = logging.getLogger(__name__)
+
 _DEFAULT_TIMEOUT = 30
 _DEFAULT_RETRIES = 3
 
@@ -70,14 +73,25 @@
 
 # Not all permissions can be set.
 _PERMISSIONS_BLACKLIST = [
+    'android.permission.ACCESS_LOCATION_EXTRA_COMMANDS',
     'android.permission.ACCESS_MOCK_LOCATION',
     'android.permission.ACCESS_NETWORK_STATE',
+    'android.permission.ACCESS_NOTIFICATION_POLICY',
     'android.permission.ACCESS_WIFI_STATE',
     'android.permission.AUTHENTICATE_ACCOUNTS',
     'android.permission.BLUETOOTH',
     'android.permission.BLUETOOTH_ADMIN',
+    'android.permission.BROADCAST_STICKY',
+    'android.permission.CHANGE_NETWORK_STATE',
+    'android.permission.CHANGE_WIFI_MULTICAST_STATE',
+    'android.permission.CHANGE_WIFI_STATE',
+    'android.permission.DISABLE_KEYGUARD',
     'android.permission.DOWNLOAD_WITHOUT_NOTIFICATION',
+    'android.permission.EXPAND_STATUS_BAR',
+    'android.permission.GET_PACKAGE_SIZE',
+    'android.permission.INSTALL_SHORTCUT',
     'android.permission.INTERNET',
+    'android.permission.KILL_BACKGROUND_PROCESSES',
     'android.permission.MANAGE_ACCOUNTS',
     'android.permission.MODIFY_AUDIO_SETTINGS',
     'android.permission.NFC',
@@ -85,8 +99,16 @@
     'android.permission.READ_SYNC_STATS',
     'android.permission.RECEIVE_BOOT_COMPLETED',
     'android.permission.RECORD_VIDEO',
+    'android.permission.REORDER_TASKS',
+    'android.permission.REQUEST_INSTALL_PACKAGES',
     'android.permission.RUN_INSTRUMENTATION',
+    'android.permission.SET_ALARM',
+    'android.permission.SET_TIME_ZONE',
+    'android.permission.SET_WALLPAPER',
+    'android.permission.SET_WALLPAPER_HINTS',
+    'android.permission.TRANSMIT_IR',
     'android.permission.USE_CREDENTIALS',
+    'android.permission.USE_FINGERPRINT',
     'android.permission.VIBRATE',
     'android.permission.WAKE_LOCK',
     'android.permission.WRITE_SYNC_SETTINGS',
@@ -94,6 +116,7 @@
     'com.android.browser.permission.WRITE_HISTORY_BOOKMARKS',
     'com.android.launcher.permission.INSTALL_SHORTCUT',
     'com.chrome.permission.DEVICE_EXTRAS',
+    'com.google.android.apps.now.CURRENT_ACCOUNT_ACCESS',
     'com.google.android.c2dm.permission.RECEIVE',
     'com.google.android.providers.gsf.permission.READ_GSERVICES',
     'com.sec.enterprise.knox.MDM_CONTENT_PROVIDER',
@@ -144,6 +167,17 @@
     ('s', stat.S_ISGID),
     ('t', stat.S_ISVTX),
 ]
+_SELINUX_MODE = {
+    'enforcing': True,
+    'permissive': False,
+    'disabled': None
+}
+# Some devices require different logic for checking if root is necessary
+_SPECIAL_ROOT_DEVICE_LIST = [
+    'marlin',
+    'sailfish',
+]
+
 
 @decorators.WithExplicitTimeoutAndRetries(
     _DEFAULT_TIMEOUT, _DEFAULT_RETRIES)
@@ -184,7 +218,7 @@
   adb_wrapper.AdbWrapper.KillServer()
   if not timeout_retry.WaitFor(adb_killed, wait_period=1, max_tries=5):
     # TODO(perezju): raise an exception after fixng http://crbug.com/442319
-    logging.warning('Failed to kill adb server')
+    logger.warning('Failed to kill adb server')
   adb_wrapper.AdbWrapper.StartServer()
   if not timeout_retry.WaitFor(adb_started, wait_period=1, max_tries=5):
     raise device_errors.CommandFailedError('Failed to start adb server')
@@ -293,6 +327,11 @@
 
     self._ClearCache()
 
+  @property
+  def serial(self):
+    """Returns the device serial."""
+    return self.adb.GetDeviceSerial()
+
   def __eq__(self, other):
     """Checks whether |other| refers to the same device as |self|.
 
@@ -302,7 +341,7 @@
     Returns:
       Whether |other| refers to the same device as |self|.
     """
-    return self.adb.GetDeviceSerial() == str(other)
+    return self.serial == str(other)
 
   def __lt__(self, other):
     """Compares two instances of DeviceUtils.
@@ -314,11 +353,11 @@
     Returns:
       Whether |self| is less than |other|.
     """
-    return self.adb.GetDeviceSerial() < other.adb.GetDeviceSerial()
+    return self.serial < other.serial
 
   def __str__(self):
     """Returns the device serial."""
-    return self.adb.GetDeviceSerial()
+    return self.serial
 
   @decorators.WithTimeoutAndRetriesFromInstance()
   def IsOnline(self, timeout=None, retries=None):
@@ -337,7 +376,7 @@
     try:
       return self.adb.GetState() == 'device'
     except base_error.BaseError as exc:
-      logging.info('Failed to get state: %s', exc)
+      logger.info('Failed to get state: %s', exc)
       return False
 
   @decorators.WithTimeoutAndRetriesFromInstance()
@@ -356,6 +395,8 @@
       DeviceUnreachableError on missing device.
     """
     try:
+      if self.product_name in _SPECIAL_ROOT_DEVICE_LIST:
+        return self.GetProp('service.adb.root') == '1'
       self.RunShellCommand(['ls', '/root'], check_return=True)
       return True
     except device_errors.AdbCommandFailedError:
@@ -379,9 +420,14 @@
       DeviceUnreachableError on missing device.
     """
     if 'needs_su' not in self._cache:
+      cmd = '%s && ! ls /root' % self._Su('ls /root')
+      if self.product_name in _SPECIAL_ROOT_DEVICE_LIST:
+        if self.HasRoot():
+          self._cache['needs_su'] = False
+          return False
+        cmd = 'which which && which su'
       try:
-        self.RunShellCommand(
-            '%s && ! ls /root' % self._Su('ls /root'), check_return=True,
+        self.RunShellCommand(cmd, shell=True, check_return=True,
             timeout=self._default_timeout if timeout is DEFAULT else timeout,
             retries=self._default_retries if retries is DEFAULT else retries)
         self._cache['needs_su'] = True
@@ -389,6 +435,7 @@
         self._cache['needs_su'] = False
     return self._cache['needs_su']
 
+
   def _Su(self, command):
     if self.build_version_sdk >= version_codes.MARSHMALLOW:
       return 'su 0 %s' % command
@@ -489,15 +536,11 @@
     apks = []
     for line in output:
       if not line.startswith('package:'):
-        # KitKat on x86 may show following warnings that is safe to ignore.
-        if (line.startswith('WARNING: linker: libdvm.so has text relocations.')
-            and version_codes.KITKAT <= self.build_version_sdk
-            and self.build_version_sdk <= version_codes.KITKAT_WATCH
-            and self.product_cpu_abi == 'x86'):
-          continue
-        raise device_errors.CommandFailedError(
-            'pm path returned: %r' % '\n'.join(output), str(self))
+        continue
       apks.append(line[len('package:'):])
+    if not apks and output:
+      raise device_errors.CommandFailedError(
+          'pm path returned: %r' % '\n'.join(output), str(self))
     self._cache['package_apk_paths'][package] = list(apks)
     return apks
 
@@ -531,19 +574,19 @@
       package: Name of the package.
 
     Returns:
-      The package's data directory, or None if the package doesn't exist on the
-      device.
+      The package's data directory.
+    Raises:
+      CommandFailedError if the package's data directory can't be found,
+        whether because it's not installed or otherwise.
     """
-    try:
-      output = self._RunPipedShellCommand(
-          'pm dump %s | grep dataDir=' % cmd_helper.SingleQuote(package))
-      for line in output:
-        _, _, dataDir = line.partition('dataDir=')
-        if dataDir:
-          return dataDir
-    except device_errors.CommandFailedError:
-      logging.exception('Could not find data directory for %s', package)
-    return None
+    output = self._RunPipedShellCommand(
+        'pm dump %s | grep dataDir=' % cmd_helper.SingleQuote(package))
+    for line in output:
+      _, _, dataDir = line.partition('dataDir=')
+      if dataDir:
+        return dataDir
+    raise device_errors.CommandFailedError(
+        'Could not find data directory for %s', package)
 
   @decorators.WithTimeoutAndRetriesFromInstance()
   def WaitUntilFullyBooted(self, wifi=False, timeout=None, retries=None):
@@ -693,7 +736,13 @@
       all_apks += split_select.SelectSplits(
         self, base_apk.path, split_apks, allow_cached_props=allow_cached_props)
       if len(all_apks) == 1:
-        logging.warning('split-select did not select any from %s', split_apks)
+        logger.warning('split-select did not select any from %s', split_apks)
+
+    missing_apks = [apk for apk in all_apks if not os.path.exists(apk)]
+    if missing_apks:
+      raise device_errors.CommandFailedError(
+          'Attempted to install non-existent apks: %s'
+              % pprint.pformat(missing_apks))
 
     package_name = base_apk.GetPackageName()
     device_apk_paths = self._GetApplicationPathsInternal(package_name)
@@ -703,11 +752,11 @@
     if not device_apk_paths:
       apks_to_install = all_apks
     elif len(device_apk_paths) > 1 and not split_apks:
-      logging.warning(
+      logger.warning(
           'Installing non-split APK when split APK was previously installed')
       apks_to_install = all_apks
     elif len(device_apk_paths) == 1 and split_apks:
-      logging.warning(
+      logger.warning(
           'Installing split APK when non-split APK was previously installed')
       apks_to_install = all_apks
     else:
@@ -715,7 +764,7 @@
         apks_to_install, host_checksums = (
             self._ComputeStaleApks(package_name, all_apks))
       except EnvironmentError as e:
-        logging.warning('Error calculating md5: %s', e)
+        logger.warning('Error calculating md5: %s', e)
         apks_to_install, host_checksums = all_apks, None
       if apks_to_install and not reinstall:
         self.Uninstall(package_name)
@@ -783,26 +832,30 @@
       raise device_errors.DeviceVersionError(
           ('Requires SDK level %s, device is SDK level %s' %
            (required_sdk_level, self.build_version_sdk)),
-           device_serial=self.adb.GetDeviceSerial())
+           device_serial=self.serial)
 
   @decorators.WithTimeoutAndRetriesFromInstance()
-  def RunShellCommand(self, cmd, check_return=False, cwd=None, env=None,
-                      as_root=False, single_line=False, large_output=False,
-                      raw_output=False, timeout=None, retries=None):
+  def RunShellCommand(self, cmd, shell=False, check_return=False, cwd=None,
+                      env=None, run_as=None, as_root=False, single_line=False,
+                      large_output=False, raw_output=False, timeout=None,
+                      retries=None):
     """Run an ADB shell command.
 
-    The command to run |cmd| should be a sequence of program arguments or else
-    a single string.
+    The command to run |cmd| should be a sequence of program arguments
+    (preferred) or a single string with a shell script to run.
 
     When |cmd| is a sequence, it is assumed to contain the name of the command
     to run followed by its arguments. In this case, arguments are passed to the
-    command exactly as given, without any further processing by the shell. This
-    allows to easily pass arguments containing spaces or special characters
-    without having to worry about getting quoting right. Whenever possible, it
-    is recomended to pass |cmd| as a sequence.
+    command exactly as given, preventing any further processing by the shell.
+    This allows callers to easily pass arguments with spaces or special
+    characters without having to worry about quoting rules. Whenever possible,
+    it is recomended to pass |cmd| as a sequence.
 
-    When |cmd| is given as a string, it will be interpreted and run by the
-    shell on the device.
+    When |cmd| is passed as a single string, |shell| should be set to True.
+    The command will be interpreted and run by the shell on the device,
+    allowing the use of shell features such as pipes, wildcards, or variables.
+    Failing to set shell=True will issue a warning, but this will be changed
+    to a hard failure in the future (see: catapult:#3242).
 
     This behaviour is consistent with that of command runners in cmd_helper as
     well as Python's own subprocess.Popen.
@@ -811,12 +864,15 @@
       have switched to the new behaviour.
 
     Args:
-      cmd: A string with the full command to run on the device, or a sequence
-        containing the command and its arguments.
+      cmd: A sequence containing the command to run and its arguments, or a
+        string with a shell script to run (should also set shell=True).
+      shell: A boolean indicating whether shell features may be used in |cmd|.
       check_return: A boolean indicating whether or not the return code should
         be checked.
       cwd: The device directory in which the command should be run.
       env: The environment variables with which the command should be run.
+      run_as: A string containing the package as which the command should be
+        run.
       as_root: A boolean indicating whether the shell command should be run
         with root privileges.
       single_line: A boolean indicating if only a single line of output is
@@ -865,16 +921,16 @@
       else:
         with device_temp_file.DeviceTempFile(self.adb, suffix='.sh') as script:
           self._WriteFileWithPush(script.name, cmd)
-          logging.info('Large shell command will be run from file: %s ...',
-                       cmd[:self._MAX_ADB_COMMAND_LENGTH])
+          logger.info('Large shell command will be run from file: %s ...',
+                      cmd[:self._MAX_ADB_COMMAND_LENGTH])
           return handle_check_return('sh %s' % script.name_quoted)
 
     def handle_large_output(cmd, large_output_mode):
       if large_output_mode:
         with device_temp_file.DeviceTempFile(self.adb) as large_output_file:
           cmd = '( %s )>%s' % (cmd, large_output_file.name)
-          logging.debug('Large output mode enabled. Will write output to '
-                        'device and read results from file.')
+          logger.debug('Large output mode enabled. Will write output to '
+                       'device and read results from file.')
           handle_large_command(cmd)
           return self.ReadFile(large_output_file.name, force_pull=True)
       else:
@@ -882,21 +938,30 @@
           return handle_large_command(cmd)
         except device_errors.AdbCommandFailedError as exc:
           if exc.status is None:
-            logging.error(_FormatPartialOutputError(exc.output))
-            logging.warning('Attempting to run in large_output mode.')
-            logging.warning('Use RunShellCommand(..., large_output=True) for '
-                            'shell commands that expect a lot of output.')
+            logger.error(_FormatPartialOutputError(exc.output))
+            logger.warning('Attempting to run in large_output mode.')
+            logger.warning('Use RunShellCommand(..., large_output=True) for '
+                           'shell commands that expect a lot of output.')
             return handle_large_output(cmd, True)
           else:
             raise
 
-    if not isinstance(cmd, basestring):
+    if isinstance(cmd, basestring):
+      if not shell:
+        logging.warning(
+            'The command to run should preferably be passed as a sequence of'
+            ' args. If shell features are needed (pipes, wildcards, variables)'
+            ' clients should explicitly set shell=True.')
+    else:
       cmd = ' '.join(cmd_helper.SingleQuote(s) for s in cmd)
     if env:
       env = ' '.join(env_quote(k, v) for k, v in env.iteritems())
       cmd = '%s %s' % (env, cmd)
     if cwd:
       cmd = 'cd %s && %s' % (cmd_helper.SingleQuote(cwd), cmd)
+    if run_as:
+      cmd = 'run-as %s sh -c %s' % (cmd_helper.SingleQuote(run_as),
+                                    cmd_helper.SingleQuote(cmd))
     if as_root and self.NeedsSU():
       # "su -c sh -c" allows using shell features in |cmd|
       cmd = self._Su('sh -c %s' % cmd_helper.SingleQuote(cmd))
@@ -922,15 +987,15 @@
     PIPESTATUS_LEADER = 'PIPESTATUS: '
 
     script += '; echo "%s${PIPESTATUS[@]}"' % PIPESTATUS_LEADER
-    kwargs['check_return'] = True
+    kwargs.update(shell=True, check_return=True)
     output = self.RunShellCommand(script, **kwargs)
     pipestatus_line = output[-1]
 
     if not pipestatus_line.startswith(PIPESTATUS_LEADER):
-      logging.error('Pipe exit statuses of shell script missing.')
+      logger.error('Pipe exit statuses of shell script missing.')
       raise device_errors.AdbShellCommandFailedError(
           script, output, status=None,
-          device_serial=self.adb.GetDeviceSerial())
+          device_serial=self.serial)
 
     output = output[:-1]
     statuses = [
@@ -938,7 +1003,7 @@
     if any(statuses):
       raise device_errors.AdbShellCommandFailedError(
           script, output, status=statuses,
-          device_serial=self.adb.GetDeviceSerial())
+          device_serial=self.serial)
     return output
 
   @decorators.WithTimeoutAndRetriesFromInstance()
@@ -982,11 +1047,11 @@
         raise device_errors.CommandFailedError(
             'No process "%s"' % process_name, str(self))
 
-    logging.info(
+    logger.info(
         'KillAll(%r, ...) attempting to kill the following:', process_name)
     for name, ids in procs_pids.iteritems():
       for i in ids:
-        logging.info('  %05s %s', str(i), name)
+        logger.info('  %05s %s', str(i), name)
 
     cmd = ['kill', '-%d' % signum] + sorted(pids)
     self.RunShellCommand(cmd, as_root=as_root, check_return=True)
@@ -1054,7 +1119,7 @@
     package = component.split('/')[0]
     shell_snippet = 'p=%s;%s' % (package,
                                  cmd_helper.ShrinkToSnippet(cmd, 'p', package))
-    return self.RunShellCommand(shell_snippet, check_return=True,
+    return self.RunShellCommand(shell_snippet, shell=True, check_return=True,
                                 large_output=True)
 
   @decorators.WithTimeoutAndRetriesFromInstance()
@@ -1126,7 +1191,7 @@
       DeviceUnreachableError on missing device.
     """
     cmd = 'p=%s;if [[ "$(ps)" = *$p* ]]; then am force-stop $p; fi'
-    self.RunShellCommand(cmd % package, check_return=True)
+    self.RunShellCommand(cmd % package, shell=True, check_return=True)
 
   @decorators.WithTimeoutAndRetriesFromInstance()
   def ClearApplicationState(
@@ -1203,6 +1268,7 @@
     cache_commit_funcs = []
     for h, d in host_device_tuples:
       assert os.path.isabs(h) and posixpath.isabs(d)
+      h = os.path.realpath(h)
       changed_files, up_to_date_files, stale_files, cache_commit_func = (
           self._GetChangedAndStaleFiles(h, d, delete_device_stale))
       all_changed_files += changed_files
@@ -1273,7 +1339,7 @@
           calculate_host_checksums,
           calculate_device_checksums))
     except EnvironmentError as e:
-      logging.warning('Error calculating md5: %s', e)
+      logger.warning('Error calculating md5: %s', e)
       return ([(host_path, device_path)], [], [], lambda: 0)
 
     to_push = []
@@ -1369,7 +1435,7 @@
           install_commands.InstallCommands(self)
         self._commands_installed = True
       except device_errors.CommandFailedError as e:
-        logging.warning('unzip not available: %s', str(e))
+        logger.warning('unzip not available: %s', str(e))
         self._commands_installed = False
     return self._commands_installed
 
@@ -1431,7 +1497,7 @@
           quoted_dirs = ' '.join(cmd_helper.SingleQuote(d) for d in dirs)
           self.RunShellCommand(
               'unzip %s&&chmod -R 777 %s' % (device_temp.name, quoted_dirs),
-              as_root=True,
+              shell=True, as_root=True,
               env={'PATH': '%s:$PATH' % install_commands.BIN_DIR},
               check_return=True)
       finally:
@@ -1476,8 +1542,11 @@
     paths = device_paths
     if isinstance(paths, basestring):
       paths = (paths,)
-    condition = ' -a '.join('-e %s' % cmd_helper.SingleQuote(p) for p in paths)
-    cmd = 'test %s' % condition
+    if not paths:
+      return True
+    cmd = ['test', '-e', paths[0]]
+    for p in paths[1:]:
+      cmd.extend(['-a', '-e', p])
     try:
       self.RunShellCommand(cmd, as_root=as_root, check_return=True,
                            timeout=timeout, retries=retries)
@@ -1486,6 +1555,33 @@
       return False
 
   @decorators.WithTimeoutAndRetriesFromInstance()
+  def RemovePath(self, device_path, force=False, recursive=False,
+                 as_root=False, timeout=None, retries=None):
+    """Removes the given path(s) from the device.
+
+    Args:
+      device_path: A string containing the absolute path to the file on the
+                   device, or an iterable of paths to check.
+      force: Whether to remove the path(s) with force (-f).
+      recursive: Whether to remove any directories in the path(s) recursively.
+      as_root: Whether root permissions should be use to remove the given
+               path(s).
+      timeout: timeout in seconds
+      retries: number of retries
+    """
+    args = ['rm']
+    if force:
+      args.append('-f')
+    if recursive:
+      args.append('-r')
+    if isinstance(device_path, basestring):
+      args.append(device_path)
+    else:
+      args.extend(device_path)
+    self.RunShellCommand(args, as_root=as_root, check_return=True)
+
+
+  @decorators.WithTimeoutAndRetriesFromInstance()
   def PullFile(self, device_path, host_path, timeout=None, retries=None):
     """Pull a file from the device.
 
@@ -1556,7 +1652,7 @@
         cmd = 'SRC=%s DEST=%s;cp "$SRC" "$DEST" && chmod 666 "$DEST"' % (
             cmd_helper.SingleQuote(device_path),
             cmd_helper.SingleQuote(device_temp.name))
-        self.RunShellCommand(cmd, as_root=True, check_return=True)
+        self.RunShellCommand(cmd, shell=True, as_root=True, check_return=True)
         return self._ReadFileWithPull(device_temp.name)
     else:
       return self._ReadFileWithPull(device_path)
@@ -1594,7 +1690,7 @@
       # a shell command rather than pushing a file.
       cmd = 'echo -n %s > %s' % (cmd_helper.SingleQuote(contents),
                                  cmd_helper.SingleQuote(device_path))
-      self.RunShellCommand(cmd, as_root=as_root, check_return=True)
+      self.RunShellCommand(cmd, shell=True, as_root=as_root, check_return=True)
     elif as_root and self.NeedsSU():
       # Adb does not allow to "push with su", so we first push to a temp file
       # on a safe location, and then copy it to the desired location with su.
@@ -1625,7 +1721,7 @@
         if m.group('filename') not in ['.', '..']:
           entries.append(m.groupdict())
       else:
-        logging.info('Skipping: %s', line)
+        logger.info('Skipping: %s', line)
 
     return entries
 
@@ -1946,7 +2042,8 @@
           'echo "%s">$c &&' % token +
           'getprop'
       )
-      output = self.RunShellCommand(cmd, check_return=True, large_output=True)
+      output = self.RunShellCommand(
+          cmd, shell=True, check_return=True, large_output=True)
       # Error-checking for this existing is done in GetExternalStoragePath().
       self._cache['external_storage'] = output[0]
       self._cache['prev_token'] = output[1]
@@ -2044,13 +2141,14 @@
     return self.GetProp('ro.product.cpu.abi', cache=True)
 
   @decorators.WithTimeoutAndRetriesFromInstance()
-  def GetPids(self, process_name, timeout=None, retries=None):
-    """Returns the PIDs of processes with the given name.
+  def GetPids(self, process_name=None, timeout=None, retries=None):
+    """Returns the PIDs of processes containing the given name as substring.
 
     Note that the |process_name| is often the package name.
 
     Args:
       process_name: A string containing the process name to get the PIDs for.
+                    If missing returns PIDs for all processes.
       timeout: timeout in seconds
       retries: number of retries
 
@@ -2064,8 +2162,17 @@
     """
     procs_pids = collections.defaultdict(list)
     try:
-      ps_output = self._RunPipedShellCommand(
-          'ps | grep -F %s' % cmd_helper.SingleQuote(process_name))
+      ps_cmd = 'ps'
+      # ps behavior was changed in Android above N, http://crbug.com/686716
+      if (self.build_version_sdk >= version_codes.NOUGAT_MR1
+          and self.build_id[0] > 'N'):
+        ps_cmd = 'ps -e'
+      if process_name:
+        ps_output = self._RunPipedShellCommand(
+            '%s | grep -F %s' % (ps_cmd, cmd_helper.SingleQuote(process_name)))
+      else:
+        ps_output = self.RunShellCommand(
+            ps_cmd.split(), check_return=True, large_output=True)
     except device_errors.AdbShellCommandFailedError as e:
       if e.status and isinstance(e.status, list) and not e.status[0]:
         # If ps succeeded but grep failed, there were no processes with the
@@ -2074,16 +2181,91 @@
       else:
         raise
 
+    process_name = process_name or ''
     for line in ps_output:
       try:
         ps_data = line.split()
-        if process_name in ps_data[-1]:
-          pid, process = ps_data[1], ps_data[-1]
+        pid, process = ps_data[1], ps_data[-1]
+        if process_name in process and pid != 'PID':
           procs_pids[process].append(pid)
       except IndexError:
         pass
     return procs_pids
 
+  def GetApplicationPids(self, process_name, at_most_one=False, **kwargs):
+    """Returns the PID or PIDs of a given process name.
+
+    Note that the |process_name|, often the package name, must match exactly.
+
+    Args:
+      process_name: A string containing the process name to get the PIDs for.
+      at_most_one: A boolean indicating that at most one PID is expected to
+                   be found.
+      timeout: timeout in seconds
+      retries: number of retries
+
+    Returns:
+      A list of the PIDs for the named process. If at_most_one=True returns
+      the single PID found or None otherwise.
+
+    Raises:
+      CommandFailedError if at_most_one=True and more than one PID is found
+          for the named process.
+      CommandTimeoutError on timeout.
+      DeviceUnreachableError on missing device.
+    """
+    pids = self.GetPids(process_name, **kwargs).get(process_name, [])
+    if at_most_one:
+      if len(pids) > 1:
+        raise device_errors.CommandFailedError(
+            'Expected a single process but found PIDs: %s.' % ', '.join(pids),
+            device_serial=str(self))
+      return pids[0] if pids else None
+    else:
+      return pids
+
+  @decorators.WithTimeoutAndRetriesFromInstance()
+  def GetEnforce(self, timeout=None, retries=None):
+    """Get the current mode of SELinux.
+
+    Args:
+      timeout: timeout in seconds
+      retries: number of retries
+
+    Returns:
+      True (enforcing), False (permissive), or None (disabled).
+
+    Raises:
+      CommandFailedError on failure.
+      CommandTimeoutError on timeout.
+      DeviceUnreachableError on missing device.
+    """
+    output = self.RunShellCommand(
+        ['getenforce'], check_return=True, single_line=True).lower()
+    if output not in _SELINUX_MODE:
+      raise device_errors.CommandFailedError(
+          'Unexpected getenforce output: %s' % output)
+    return _SELINUX_MODE[output]
+
+  @decorators.WithTimeoutAndRetriesFromInstance()
+  def SetEnforce(self, enabled, timeout=None, retries=None):
+    """Modify the mode SELinux is running in.
+
+    Args:
+      enabled: a boolean indicating whether to put SELinux in encorcing mode
+               (if True), or permissive mode (otherwise).
+      timeout: timeout in seconds
+      retries: number of retries
+
+    Raises:
+      CommandFailedError on failure.
+      CommandTimeoutError on timeout.
+      DeviceUnreachableError on missing device.
+    """
+    self.RunShellCommand(
+        ['setenforce', '1' if int(enabled) else '0'], as_root=True,
+        check_return=True)
+
   @decorators.WithTimeoutAndRetriesFromInstance()
   def TakeScreenshot(self, host_path=None, timeout=None, retries=None):
     """Takes a screenshot of the device.
@@ -2105,7 +2287,7 @@
     """
     if not host_path:
       host_path = os.path.abspath('screenshot-%s-%s.png' % (
-          self.adb.GetDeviceSerial(), _GetTimeStamp()))
+          self.serial, _GetTimeStamp()))
     with device_temp_file.DeviceTempFile(self.adb, suffix='.png') as device_tmp:
       self.RunShellCommand(['/system/bin/screencap', '-p', device_tmp.name],
                            check_return=True)
@@ -2134,12 +2316,12 @@
     try:
       result.update(self._GetMemoryUsageForPidFromSmaps(pid))
     except device_errors.CommandFailedError:
-      logging.exception('Error getting memory usage from smaps')
+      logger.exception('Error getting memory usage from smaps')
 
     try:
       result.update(self._GetMemoryUsageForPidFromStatus(pid))
     except device_errors.CommandFailedError:
-      logging.exception('Error getting memory usage from status')
+      logger.exception('Error getting memory usage from status')
 
     return result
 
@@ -2166,13 +2348,13 @@
     if not match:
       return None
     package = match.group(2)
-    logging.warning('Trying to dismiss %s dialog for %s', *match.groups())
+    logger.warning('Trying to dismiss %s dialog for %s', *match.groups())
     self.SendKeyEvent(keyevent.KEYCODE_DPAD_RIGHT)
     self.SendKeyEvent(keyevent.KEYCODE_DPAD_RIGHT)
     self.SendKeyEvent(keyevent.KEYCODE_ENTER)
     match = _FindFocusedWindow()
     if match:
-      logging.error('Still showing a %s dialog for %s', *match.groups())
+      logger.error('Still showing a %s dialog for %s', *match.groups())
     return package
 
   def _GetMemoryUsageForPidFromSmaps(self, pid):
@@ -2257,7 +2439,7 @@
     self._EnsureCacheInitialized()
     given_token = obj.get('token')
     if not given_token or self._cache['prev_token'] != given_token:
-      logging.warning('Stale cache detected. Not using it.')
+      logger.warning('Stale cache detected. Not using it.')
       return False
 
     self._cache['package_apk_paths'] = obj.get('package_apk_paths', {})
@@ -2342,7 +2524,7 @@
       A device serial, or a list of device serials (optional).
 
     Returns:
-      A list of one or more DeviceUtils instances.
+      A list of DeviceUtils instances.
 
     Raises:
       NoDevicesError: Raised when no non-blacklisted devices exist and
@@ -2370,7 +2552,7 @@
 
     def blacklisted(serial):
       if serial in blacklisted_devices:
-        logging.warning('Device %s is blacklisted.', serial)
+        logger.warning('Device %s is blacklisted.', serial)
         return True
       return False
 
@@ -2390,10 +2572,11 @@
 
   @decorators.WithTimeoutAndRetriesFromInstance()
   def RestartAdbd(self, timeout=None, retries=None):
-    logging.info('Restarting adbd on device.')
+    logger.info('Restarting adbd on device.')
     with device_temp_file.DeviceTempFile(self.adb, suffix='.sh') as script:
       self.WriteFile(script.name, _RESTART_ADBD_SCRIPT)
-      self.RunShellCommand(['source', script.name], as_root=True)
+      self.RunShellCommand(
+          ['source', script.name], check_return=True, as_root=True)
       self.adb.WaitForDevice()
 
   @decorators.WithTimeoutAndRetriesFromInstance()
@@ -2402,19 +2585,19 @@
     # the permission model.
     if not permissions or self.build_version_sdk < version_codes.MARSHMALLOW:
       return
-    logging.info('Setting permissions for %s.', package)
+    logger.info('Setting permissions for %s.', package)
     permissions = [p for p in permissions if p not in _PERMISSIONS_BLACKLIST]
     if ('android.permission.WRITE_EXTERNAL_STORAGE' in permissions
         and 'android.permission.READ_EXTERNAL_STORAGE' not in permissions):
       permissions.append('android.permission.READ_EXTERNAL_STORAGE')
     cmd = '&&'.join('pm grant %s %s' % (package, p) for p in permissions)
     if cmd:
-      output = self.RunShellCommand(cmd, check_return=True)
+      output = self.RunShellCommand(cmd, shell=True, check_return=True)
       if output:
-        logging.warning('Possible problem when granting permissions. Blacklist '
-                        'may need to be updated.')
+        logger.warning('Possible problem when granting permissions. Blacklist '
+                       'may need to be updated.')
         for line in output:
-          logging.warning('  %s', line)
+          logger.warning('  %s', line)
 
   @decorators.WithTimeoutAndRetriesFromInstance()
   def IsScreenOn(self, timeout=None, retries=None):
@@ -2460,7 +2643,7 @@
       return self.IsScreenOn() == on
 
     if screen_test():
-      logging.info('Screen already in expected state.')
+      logger.info('Screen already in expected state.')
       return
-    self.RunShellCommand('input keyevent 26')
+    self.SendKeyEvent(keyevent.KEYCODE_POWER)
     timeout_retry.WaitFor(screen_test, wait_period=1)
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/device_utils_devicetest.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/device_utils_devicetest.py
index 54ab7db..e69cc90 100755
--- a/sdk/platform-tools/systrace/catapult/devil/devil/android/device_utils_devicetest.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/device_utils_devicetest.py
@@ -9,6 +9,7 @@
 """
 
 import os
+import posixpath
 import sys
 import tempfile
 import unittest
@@ -87,12 +88,12 @@
     device_file_path = "%s/%s" % (_DEVICE_DIR, file_name)
     self.adb.Push(host_file_path, device_file_path)
     self.device.PushChangedFiles([(host_file_path, device_file_path)])
-    result = self.device.RunShellCommand(['cat', device_file_path],
-                                         single_line=True)
+    result = self.device.RunShellCommand(
+        ['cat', device_file_path], check_return=True, single_line=True)
     self.assertEqual(_OLD_CONTENTS, result)
 
     cmd_helper.RunCmd(['rm', host_file_path])
-    self.device.RunShellCommand(['rm', '-rf', _DEVICE_DIR])
+    self.device.RemovePath(_DEVICE_DIR, recursive=True, force=True)
 
   def testPushChangedFiles_singleFileChange(self):
     (host_file_path, file_name) = self._MakeTempFile(_OLD_CONTENTS)
@@ -102,12 +103,12 @@
     with open(host_file_path, 'w') as f:
       f.write(_NEW_CONTENTS)
     self.device.PushChangedFiles([(host_file_path, device_file_path)])
-    result = self.device.RunShellCommand(['cat', device_file_path],
-                                         single_line=True)
+    result = self.device.RunShellCommand(
+        ['cat', device_file_path], check_return=True, single_line=True)
     self.assertEqual(_NEW_CONTENTS, result)
 
     cmd_helper.RunCmd(['rm', host_file_path])
-    self.device.RunShellCommand(['rm', '-rf', _DEVICE_DIR])
+    self.device.RemovePath(_DEVICE_DIR, recursive=True, force=True)
 
   def testDeleteFiles(self):
     host_tmp_dir = tempfile.mkdtemp()
@@ -120,11 +121,11 @@
     cmd_helper.RunCmd(['rm', host_file_path])
     self.device.PushChangedFiles([(host_tmp_dir, _DEVICE_DIR)],
                                  delete_device_stale=True)
-    result = self.device.RunShellCommand(['ls', _DEVICE_DIR], single_line=True)
-    self.assertEqual('', result)
+    filenames = self.device.ListDirectory(_DEVICE_DIR)
+    self.assertEqual([], filenames)
 
     cmd_helper.RunCmd(['rm', '-rf', host_tmp_dir])
-    self.device.RunShellCommand(['rm', '-rf', _DEVICE_DIR])
+    self.device.RemovePath(_DEVICE_DIR, recursive=True, force=True)
 
   def testPushAndDeleteFiles_noSubDir(self):
     host_tmp_dir = tempfile.mkdtemp()
@@ -144,14 +145,15 @@
 
     self.device.PushChangedFiles([(host_tmp_dir, _DEVICE_DIR)],
                                    delete_device_stale=True)
-    result = self.device.RunShellCommand(['cat', device_file_path1],
-                                         single_line=True)
+    result = self.device.RunShellCommand(
+        ['cat', device_file_path1], check_return=True, single_line=True)
     self.assertEqual(_NEW_CONTENTS, result)
-    result = self.device.RunShellCommand(['ls', _DEVICE_DIR], single_line=True)
-    self.assertEqual(file_name1, result)
 
-    self.device.RunShellCommand(['rm', '-rf', _DEVICE_DIR])
+    filenames = self.device.ListDirectory(_DEVICE_DIR)
+    self.assertEqual([file_name1], filenames)
+
     cmd_helper.RunCmd(['rm', '-rf', host_tmp_dir])
+    self.device.RemovePath(_DEVICE_DIR, recursive=True, force=True)
 
   def testPushAndDeleteFiles_SubDir(self):
     host_tmp_dir = tempfile.mkdtemp()
@@ -187,31 +189,31 @@
 
     self.device.PushChangedFiles([(host_tmp_dir, _DEVICE_DIR)],
                                    delete_device_stale=True)
-    result = self.device.RunShellCommand(['cat', device_file_path1],
-                                         single_line=True)
+    result = self.device.RunShellCommand(
+        ['cat', device_file_path1], check_return=True, single_line=True)
     self.assertEqual(_NEW_CONTENTS, result)
 
-    result = self.device.RunShellCommand(['ls', _DEVICE_DIR])
-    self.assertIn(file_name1, result)
-    self.assertIn(_SUB_DIR1, result)
-    self.assertIn(_SUB_DIR, result)
-    self.assertEqual(3, len(result))
+    filenames = self.device.ListDirectory(_DEVICE_DIR)
+    self.assertIn(file_name1, filenames)
+    self.assertIn(_SUB_DIR1, filenames)
+    self.assertIn(_SUB_DIR, filenames)
+    self.assertEqual(3, len(filenames))
 
-    result = self.device.RunShellCommand(['cat', device_file_path3],
-                                      single_line=True)
+    result = self.device.RunShellCommand(
+        ['cat', device_file_path3], check_return=True, single_line=True)
     self.assertEqual(_OLD_CONTENTS, result)
 
-    result = self.device.RunShellCommand(["ls", "%s/%s/%s"
-                                          % (_DEVICE_DIR, _SUB_DIR, _SUB_DIR2)],
-                                         single_line=True)
-    self.assertEqual('', result)
+    filenames = self.device.ListDirectory(
+        posixpath.join(_DEVICE_DIR, _SUB_DIR, _SUB_DIR2))
+    self.assertEqual([], filenames)
 
-    self.device.RunShellCommand(['rm', '-rf', _DEVICE_DIR])
     cmd_helper.RunCmd(['rm', '-rf', host_tmp_dir])
+    self.device.RemovePath(_DEVICE_DIR, recursive=True, force=True)
 
   def testRestartAdbd(self):
     def get_adbd_pid():
-      ps_output = self.device.RunShellCommand(['ps'])
+      # TODO(catapult:#3215): Migrate to device.GetPids().
+      ps_output = self.device.RunShellCommand(['ps'], check_return=True)
       for ps_line in ps_output:
         if 'adbd' in ps_line:
           return ps_line.split()[1]
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/device_utils_test.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/device_utils_test.py
index 18fda54..2490209 100755
--- a/sdk/platform-tools/systrace/catapult/devil/devil/android/device_utils_test.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/device_utils_test.py
@@ -22,6 +22,7 @@
 from devil.android import device_utils
 from devil.android.sdk import adb_wrapper
 from devil.android.sdk import intent
+from devil.android.sdk import keyevent
 from devil.android.sdk import version_codes
 from devil.utils import cmd_helper
 from devil.utils import mock_calls
@@ -194,7 +195,8 @@
     props = props or []
     ret = [sdcard, 'TOKEN'] + props
     return (self.call.device.RunShellCommand(
-        AnyStringWith('getprop'), check_return=True, large_output=True), ret)
+        AnyStringWith('getprop'),
+        shell=True, check_return=True, large_output=True), ret)
 
 
 class DeviceUtilsEqTest(DeviceUtilsTest):
@@ -282,11 +284,30 @@
 class DeviceUtilsHasRootTest(DeviceUtilsTest):
 
   def testHasRoot_true(self):
-    with self.assertCall(self.call.adb.Shell('ls /root'), 'foo\n'):
+    with self.patch_call(self.call.device.product_name,
+                          return_value='notasailfish'), (
+        self.assertCall(self.call.adb.Shell('ls /root'), 'foo\n')):
+      self.assertTrue(self.device.HasRoot())
+
+  def testhasRootSpecial_true(self):
+    with self.patch_call(self.call.device.product_name,
+                         return_value='sailfish'), (
+        self.assertCall(self.call.adb.Shell('getprop service.adb.root'),
+                        '1\n')):
       self.assertTrue(self.device.HasRoot())
 
   def testHasRoot_false(self):
-    with self.assertCall(self.call.adb.Shell('ls /root'), self.ShellError()):
+    with self.patch_call(self.call.device.product_name,
+                         return_value='notasailfish'), (
+        self.assertCall(self.call.adb.Shell('ls /root'),
+                        self.ShellError())):
+      self.assertFalse(self.device.HasRoot())
+
+  def testHasRootSpecial_false(self):
+    with self.patch_call(self.call.device.product_name,
+                         return_value='sailfish'), (
+        self.assertCall(self.call.adb.Shell('getprop service.adb.root'),
+                        '\n')):
       self.assertFalse(self.device.HasRoot())
 
 
@@ -361,6 +382,15 @@
       self.assertEquals([],
           self.device._GetApplicationPathsInternal('not.installed.app'))
 
+  def testGetApplicationPathsInternal_garbageFirstLine(self):
+    with self.assertCalls(
+        (self.call.device.GetProp('ro.build.version.sdk', cache=True), '19'),
+        (self.call.device.RunShellCommand(
+            ['pm', 'path', 'android'], check_return=True),
+         ['garbage first line'])):
+      with self.assertRaises(device_errors.CommandFailedError):
+        self.device._GetApplicationPathsInternal('android')
+
   def testGetApplicationPathsInternal_fails(self):
     with self.assertCalls(
         (self.call.device.GetProp('ro.build.version.sdk', cache=True), '19'),
@@ -417,7 +447,8 @@
         self.call.device._RunPipedShellCommand(
             'pm dump foo.bar.baz | grep dataDir='),
         self.ShellError()):
-      self.assertIsNone(self.device.GetApplicationDataDirectory('foo.bar.baz'))
+      with self.assertRaises(device_errors.CommandFailedError):
+        self.device.GetApplicationDataDirectory('foo.bar.baz')
 
 
 @mock.patch('time.sleep', mock.Mock())
@@ -614,6 +645,7 @@
   def testInstall_noPriorInstall(self):
     with self.patch_call(self.call.device.build_version_sdk, return_value=23):
       with self.assertCalls(
+          (mock.call.os.path.exists('/fake/test/app.apk'), True),
           (self.call.device._GetApplicationPathsInternal('test.package'), []),
           self.call.adb.Install('/fake/test/app.apk', reinstall=False,
                                 allow_downgrade=False),
@@ -623,6 +655,7 @@
   def testInstall_permissionsPreM(self):
     with self.patch_call(self.call.device.build_version_sdk, return_value=20):
       with self.assertCalls(
+          (mock.call.os.path.exists('/fake/test/app.apk'), True),
           (self.call.device._GetApplicationPathsInternal('test.package'), []),
           (self.call.adb.Install('/fake/test/app.apk', reinstall=False,
                                  allow_downgrade=False))):
@@ -631,6 +664,7 @@
   def testInstall_findPermissions(self):
     with self.patch_call(self.call.device.build_version_sdk, return_value=23):
       with self.assertCalls(
+          (mock.call.os.path.exists('/fake/test/app.apk'), True),
           (self.call.device._GetApplicationPathsInternal('test.package'), []),
           (self.call.adb.Install('/fake/test/app.apk', reinstall=False,
                                  allow_downgrade=False)),
@@ -639,6 +673,7 @@
 
   def testInstall_passPermissions(self):
     with self.assertCalls(
+        (mock.call.os.path.exists('/fake/test/app.apk'), True),
         (self.call.device._GetApplicationPathsInternal('test.package'), []),
         (self.call.adb.Install('/fake/test/app.apk', reinstall=False,
                                allow_downgrade=False)),
@@ -648,6 +683,7 @@
 
   def testInstall_differentPriorInstall(self):
     with self.assertCalls(
+        (mock.call.os.path.exists('/fake/test/app.apk'), True),
         (self.call.device._GetApplicationPathsInternal('test.package'),
          ['/fake/data/app/test.package.apk']),
         (self.call.device._ComputeStaleApks('test.package',
@@ -661,6 +697,7 @@
 
   def testInstall_differentPriorInstall_reinstall(self):
     with self.assertCalls(
+        (mock.call.os.path.exists('/fake/test/app.apk'), True),
         (self.call.device._GetApplicationPathsInternal('test.package'),
          ['/fake/data/app/test.package.apk']),
         (self.call.device._ComputeStaleApks('test.package',
@@ -673,6 +710,7 @@
 
   def testInstall_identicalPriorInstall_reinstall(self):
     with self.assertCalls(
+        (mock.call.os.path.exists('/fake/test/app.apk'), True),
         (self.call.device._GetApplicationPathsInternal('test.package'),
          ['/fake/data/app/test.package.apk']),
         (self.call.device._ComputeStaleApks('test.package',
@@ -682,8 +720,15 @@
       self.device.Install(DeviceUtilsInstallTest.mock_apk,
           reinstall=True, retries=0, permissions=[])
 
+  def testInstall_missingApk(self):
+    with self.assertCalls(
+        (mock.call.os.path.exists('/fake/test/app.apk'), False)):
+      with self.assertRaises(device_errors.CommandFailedError):
+        self.device.Install(DeviceUtilsInstallTest.mock_apk, retries=0)
+
   def testInstall_fails(self):
     with self.assertCalls(
+        (mock.call.os.path.exists('/fake/test/app.apk'), True),
         (self.call.device._GetApplicationPathsInternal('test.package'), []),
         (self.call.adb.Install('/fake/test/app.apk', reinstall=False,
                                allow_downgrade=False),
@@ -693,6 +738,7 @@
 
   def testInstall_downgrade(self):
     with self.assertCalls(
+        (mock.call.os.path.exists('/fake/test/app.apk'), True),
         (self.call.device._GetApplicationPathsInternal('test.package'),
          ['/fake/data/app/test.package.apk']),
         (self.call.device._ComputeStaleApks('test.package',
@@ -716,6 +762,8 @@
             ['split1.apk', 'split2.apk', 'split3.apk'],
             allow_cached_props=False),
          ['split2.apk']),
+        (mock.call.os.path.exists('base.apk'), True),
+        (mock.call.os.path.exists('split2.apk'), True),
         (self.call.device._GetApplicationPathsInternal('test.package'), []),
         (self.call.adb.InstallMultiple(
             ['base.apk', 'split2.apk'], partial=None, reinstall=False,
@@ -731,6 +779,8 @@
             ['split1.apk', 'split2.apk', 'split3.apk'],
             allow_cached_props=False),
          ['split2.apk']),
+        (mock.call.os.path.exists('base.apk'), True),
+        (mock.call.os.path.exists('split2.apk'), True),
         (self.call.device._GetApplicationPathsInternal('test.package'),
          ['base-on-device.apk', 'split2-on-device.apk']),
         (self.call.device._ComputeStaleApks('test.package',
@@ -751,6 +801,8 @@
             ['split1.apk', 'split2.apk', 'split3.apk'],
             allow_cached_props=False),
          ['split2.apk']),
+        (mock.call.os.path.exists('base.apk'), True),
+        (mock.call.os.path.exists('split2.apk'), True),
         (self.call.device._GetApplicationPathsInternal('test.package'),
          ['base-on-device.apk', 'split2-on-device.apk']),
         (self.call.device._ComputeStaleApks('test.package',
@@ -764,6 +816,21 @@
                                   reinstall=True, permissions=[], retries=0,
                                   allow_downgrade=True)
 
+  def testInstallSplitApk_missingSplit(self):
+    with self.assertCalls(
+        (self.call.device._CheckSdkLevel(21)),
+        (mock.call.devil.android.sdk.split_select.SelectSplits(
+            self.device, 'base.apk',
+            ['split1.apk', 'split2.apk', 'split3.apk'],
+            allow_cached_props=False),
+         ['split2.apk']),
+        (mock.call.os.path.exists('base.apk'), True),
+        (mock.call.os.path.exists('split2.apk'), False)):
+      with self.assertRaises(device_errors.CommandFailedError):
+        self.device.InstallSplitApk(DeviceUtilsInstallSplitApkTest.mock_apk,
+            ['split1.apk', 'split2.apk', 'split3.apk'], permissions=[],
+            retries=0)
+
 
 class DeviceUtilsUninstallTest(DeviceUtilsTest):
 
@@ -803,38 +870,47 @@
 
   def testRunShellCommand_commandAsList(self):
     with self.assertCall(self.call.adb.Shell('pm list packages'), ''):
-      self.device.RunShellCommand(['pm', 'list', 'packages'])
+      self.device.RunShellCommand(
+          ['pm', 'list', 'packages'], check_return=True)
 
   def testRunShellCommand_commandAsListQuoted(self):
     with self.assertCall(self.call.adb.Shell("echo 'hello world' '$10'"), ''):
-      self.device.RunShellCommand(['echo', 'hello world', '$10'])
+      self.device.RunShellCommand(
+          ['echo', 'hello world', '$10'], check_return=True)
 
   def testRunShellCommand_commandAsString(self):
     with self.assertCall(self.call.adb.Shell('echo "$VAR"'), ''):
-      self.device.RunShellCommand('echo "$VAR"')
+      self.device.RunShellCommand(
+          'echo "$VAR"', shell=True, check_return=True)
 
   def testNewRunShellImpl_withEnv(self):
     with self.assertCall(
         self.call.adb.Shell('VAR=some_string echo "$VAR"'), ''):
-      self.device.RunShellCommand('echo "$VAR"', env={'VAR': 'some_string'})
+      self.device.RunShellCommand(
+          'echo "$VAR"', shell=True, check_return=True,
+          env={'VAR': 'some_string'})
 
   def testNewRunShellImpl_withEnvQuoted(self):
     with self.assertCall(
         self.call.adb.Shell('PATH="$PATH:/other/path" run_this'), ''):
-      self.device.RunShellCommand('run_this', env={'PATH': '$PATH:/other/path'})
+      self.device.RunShellCommand(
+          ['run_this'], check_return=True, env={'PATH': '$PATH:/other/path'})
 
   def testNewRunShellImpl_withEnv_failure(self):
     with self.assertRaises(KeyError):
-      self.device.RunShellCommand('some_cmd', env={'INVALID NAME': 'value'})
+      self.device.RunShellCommand(
+          ['some_cmd'], check_return=True, env={'INVALID NAME': 'value'})
 
   def testNewRunShellImpl_withCwd(self):
     with self.assertCall(self.call.adb.Shell('cd /some/test/path && ls'), ''):
-      self.device.RunShellCommand('ls', cwd='/some/test/path')
+      self.device.RunShellCommand(
+          ['ls'], check_return=True, cwd='/some/test/path')
 
   def testNewRunShellImpl_withCwdQuoted(self):
     with self.assertCall(
         self.call.adb.Shell("cd '/some test/path with/spaces' && ls"), ''):
-      self.device.RunShellCommand('ls', cwd='/some test/path with/spaces')
+      self.device.RunShellCommand(
+          ['ls'], check_return=True, cwd='/some test/path with/spaces')
 
   def testRunShellCommand_withHugeCmd(self):
     payload = 'hi! ' * 1024
@@ -844,10 +920,11 @@
           self.adb, suffix='.sh'), MockTempFile('/sdcard/temp-123.sh')),
       self.call.device._WriteFileWithPush('/sdcard/temp-123.sh', expected_cmd),
       (self.call.adb.Shell('sh /sdcard/temp-123.sh'), payload + '\n')):
-      self.assertEquals([payload],
-                        self.device.RunShellCommand(['echo', payload]))
+      self.assertEquals(
+          [payload],
+          self.device.RunShellCommand(['echo', payload], check_return=True))
 
-  def testRunShellCommand_withHugeCmdAndSU(self):
+  def testRunShellCommand_withHugeCmdAndSu(self):
     payload = 'hi! ' * 1024
     expected_cmd_without_su = """sh -c 'echo '"'"'%s'"'"''""" % payload
     expected_cmd = 'su -c %s' % expected_cmd_without_su
@@ -860,7 +937,8 @@
       (self.call.adb.Shell('sh /sdcard/temp-123.sh'), payload + '\n')):
       self.assertEquals(
           [payload],
-          self.device.RunShellCommand(['echo', payload], as_root=True))
+          self.device.RunShellCommand(
+              ['echo', payload], check_return=True, as_root=True))
 
   def testRunShellCommand_withSu(self):
     expected_cmd_without_su = "sh -c 'setprop service.adb.root 0'"
@@ -869,71 +947,112 @@
         (self.call.device.NeedsSU(), True),
         (self.call.device._Su(expected_cmd_without_su), expected_cmd),
         (self.call.adb.Shell(expected_cmd), '')):
-      self.device.RunShellCommand('setprop service.adb.root 0', as_root=True)
+      self.device.RunShellCommand(
+          ['setprop', 'service.adb.root', '0'],
+          check_return=True, as_root=True)
+
+  def testRunShellCommand_withRunAs(self):
+    expected_cmd_without_run_as = "sh -c 'mkdir -p files'"
+    expected_cmd = (
+        'run-as org.devil.test_package %s' % expected_cmd_without_run_as)
+    with self.assertCall(self.call.adb.Shell(expected_cmd), ''):
+      self.device.RunShellCommand(
+          ['mkdir', '-p', 'files'],
+          check_return=True, run_as='org.devil.test_package')
+
+  def testRunShellCommand_withRunAsAndSu(self):
+    expected_cmd_with_nothing = "sh -c 'mkdir -p files'"
+    expected_cmd_with_run_as = (
+        'run-as org.devil.test_package %s' % expected_cmd_with_nothing)
+    expected_cmd_without_su = (
+        'sh -c %s' % cmd_helper.SingleQuote(expected_cmd_with_run_as))
+    expected_cmd = 'su -c %s' % expected_cmd_without_su
+    with self.assertCalls(
+        (self.call.device.NeedsSU(), True),
+        (self.call.device._Su(expected_cmd_without_su), expected_cmd),
+        (self.call.adb.Shell(expected_cmd), '')):
+      self.device.RunShellCommand(
+          ['mkdir', '-p', 'files'],
+          check_return=True, run_as='org.devil.test_package',
+          as_root=True)
 
   def testRunShellCommand_manyLines(self):
     cmd = 'ls /some/path'
     with self.assertCall(self.call.adb.Shell(cmd), 'file1\nfile2\nfile3\n'):
-      self.assertEquals(['file1', 'file2', 'file3'],
-                        self.device.RunShellCommand(cmd))
+      self.assertEquals(
+          ['file1', 'file2', 'file3'],
+          self.device.RunShellCommand(cmd.split(), check_return=True))
 
   def testRunShellCommand_manyLinesRawOutput(self):
     cmd = 'ls /some/path'
     with self.assertCall(self.call.adb.Shell(cmd), '\rfile1\nfile2\r\nfile3\n'):
-      self.assertEquals('\rfile1\nfile2\r\nfile3\n',
-                        self.device.RunShellCommand(cmd, raw_output=True))
+      self.assertEquals(
+          '\rfile1\nfile2\r\nfile3\n',
+          self.device.RunShellCommand(
+              cmd.split(), check_return=True, raw_output=True))
 
   def testRunShellCommand_singleLine_success(self):
     cmd = 'echo $VALUE'
     with self.assertCall(self.call.adb.Shell(cmd), 'some value\n'):
-      self.assertEquals('some value',
-                        self.device.RunShellCommand(cmd, single_line=True))
+      self.assertEquals(
+          'some value',
+          self.device.RunShellCommand(
+              cmd, shell=True, check_return=True, single_line=True))
 
   def testRunShellCommand_singleLine_successEmptyLine(self):
     cmd = 'echo $VALUE'
     with self.assertCall(self.call.adb.Shell(cmd), '\n'):
-      self.assertEquals('',
-                        self.device.RunShellCommand(cmd, single_line=True))
+      self.assertEquals(
+          '',
+          self.device.RunShellCommand(
+              cmd, shell=True, check_return=True, single_line=True))
 
   def testRunShellCommand_singleLine_successWithoutEndLine(self):
     cmd = 'echo -n $VALUE'
     with self.assertCall(self.call.adb.Shell(cmd), 'some value'):
-      self.assertEquals('some value',
-                        self.device.RunShellCommand(cmd, single_line=True))
+      self.assertEquals(
+          'some value',
+          self.device.RunShellCommand(
+              cmd, shell=True, check_return=True, single_line=True))
 
   def testRunShellCommand_singleLine_successNoOutput(self):
     cmd = 'echo -n $VALUE'
     with self.assertCall(self.call.adb.Shell(cmd), ''):
-      self.assertEquals('',
-                        self.device.RunShellCommand(cmd, single_line=True))
+      self.assertEquals(
+          '',
+          self.device.RunShellCommand(
+              cmd, shell=True, check_return=True, single_line=True))
 
   def testRunShellCommand_singleLine_failTooManyLines(self):
     cmd = 'echo $VALUE'
     with self.assertCall(self.call.adb.Shell(cmd),
                          'some value\nanother value\n'):
       with self.assertRaises(device_errors.CommandFailedError):
-        self.device.RunShellCommand(cmd, single_line=True)
+        self.device.RunShellCommand(
+            cmd, shell=True, check_return=True, single_line=True)
 
   def testRunShellCommand_checkReturn_success(self):
     cmd = 'echo $ANDROID_DATA'
     output = '/data\n'
     with self.assertCall(self.call.adb.Shell(cmd), output):
-      self.assertEquals([output.rstrip()],
-                        self.device.RunShellCommand(cmd, check_return=True))
+      self.assertEquals(
+          [output.rstrip()],
+          self.device.RunShellCommand(cmd, shell=True, check_return=True))
 
   def testRunShellCommand_checkReturn_failure(self):
     cmd = 'ls /root'
     output = 'opendir failed, Permission denied\n'
     with self.assertCall(self.call.adb.Shell(cmd), self.ShellError(output)):
       with self.assertRaises(device_errors.AdbCommandFailedError):
-        self.device.RunShellCommand(cmd, check_return=True)
+        self.device.RunShellCommand(cmd.split(), check_return=True)
 
   def testRunShellCommand_checkReturn_disabled(self):
     cmd = 'ls /root'
     output = 'opendir failed, Permission denied\n'
     with self.assertCall(self.call.adb.Shell(cmd), self.ShellError(output)):
-      self.assertEquals([output.rstrip()],
-                        self.device.RunShellCommand(cmd, check_return=False))
+      self.assertEquals(
+          [output.rstrip()],
+          self.device.RunShellCommand(cmd.split(), check_return=False))
 
   def testRunShellCommand_largeOutput_enabled(self):
     cmd = 'echo $VALUE'
@@ -948,13 +1067,13 @@
       self.assertEquals(
           ['something'],
           self.device.RunShellCommand(
-              cmd, large_output=True, check_return=True))
+              cmd, shell=True, large_output=True, check_return=True))
 
   def testRunShellCommand_largeOutput_disabledNoTrigger(self):
     cmd = 'something'
     with self.assertCall(self.call.adb.Shell(cmd), self.ShellError('')):
       with self.assertRaises(device_errors.AdbCommandFailedError):
-        self.device.RunShellCommand(cmd, check_return=True)
+        self.device.RunShellCommand([cmd], check_return=True)
 
   def testRunShellCommand_largeOutput_disabledTrigger(self):
     cmd = 'echo $VALUE'
@@ -967,8 +1086,9 @@
         (self.call.adb.Shell(cmd_redirect)),
         (self.call.device.ReadFile(mock.ANY, force_pull=True),
          'something')):
-      self.assertEquals(['something'],
-                        self.device.RunShellCommand(cmd, check_return=True))
+      self.assertEquals(
+          ['something'],
+          self.device.RunShellCommand(cmd, shell=True, check_return=True))
 
 
 class DeviceUtilsRunPipedShellCommandTest(DeviceUtilsTest):
@@ -977,7 +1097,7 @@
     with self.assertCall(
         self.call.device.RunShellCommand(
             'ps | grep foo; echo "PIPESTATUS: ${PIPESTATUS[@]}"',
-            check_return=True),
+            shell=True, check_return=True),
         ['This line contains foo', 'PIPESTATUS: 0 0']):
       self.assertEquals(['This line contains foo'],
                         self.device._RunPipedShellCommand('ps | grep foo'))
@@ -986,7 +1106,7 @@
     with self.assertCall(
         self.call.device.RunShellCommand(
             'ps | grep foo; echo "PIPESTATUS: ${PIPESTATUS[@]}"',
-            check_return=True),
+            shell=True, check_return=True),
         ['PIPESTATUS: 1 0']):
       with self.assertRaises(device_errors.AdbShellCommandFailedError) as ec:
         self.device._RunPipedShellCommand('ps | grep foo')
@@ -996,7 +1116,7 @@
     with self.assertCall(
         self.call.device.RunShellCommand(
             'ps | grep foo; echo "PIPESTATUS: ${PIPESTATUS[@]}"',
-            check_return=True),
+            shell=True, check_return=True),
         ['PIPESTATUS: 0 1']):
       with self.assertRaises(device_errors.AdbShellCommandFailedError) as ec:
         self.device._RunPipedShellCommand('ps | grep foo')
@@ -1006,7 +1126,7 @@
     with self.assertCall(
         self.call.device.RunShellCommand(
             'ps | grep foo; echo "PIPESTATUS: ${PIPESTATUS[@]}"',
-            check_return=True),
+            shell=True, check_return=True),
         ['foo.bar'] * 256 + ['foo.ba']):
       with self.assertRaises(device_errors.AdbShellCommandFailedError) as ec:
         self.device._RunPipedShellCommand('ps | grep foo')
@@ -1265,7 +1385,7 @@
     with self.assertCalls(
         self.call.device.RunShellCommand(
             'p=test.package;am instrument "$p"/.TestInstrumentation',
-            check_return=True, large_output=True)):
+            shell=True, check_return=True, large_output=True)):
       self.device.StartInstrumentation(
           'test.package/.TestInstrumentation',
           finish=False, raw=False, extras=None)
@@ -1274,7 +1394,7 @@
     with self.assertCalls(
         (self.call.device.RunShellCommand(
             'p=test.package;am instrument -w "$p"/.TestInstrumentation',
-            check_return=True, large_output=True),
+            shell=True, check_return=True, large_output=True),
          ['OK (1 test)'])):
       output = self.device.StartInstrumentation(
           'test.package/.TestInstrumentation',
@@ -1285,7 +1405,7 @@
     with self.assertCalls(
         self.call.device.RunShellCommand(
             'p=test.package;am instrument -r "$p"/.TestInstrumentation',
-            check_return=True, large_output=True)):
+            shell=True, check_return=True, large_output=True)):
       self.device.StartInstrumentation(
           'test.package/.TestInstrumentation',
           finish=False, raw=True, extras=None)
@@ -1295,7 +1415,7 @@
         self.call.device.RunShellCommand(
             'p=test.package;am instrument -e "$p".foo Foo -e bar \'Val \'"$p" '
             '"$p"/.TestInstrumentation',
-            check_return=True, large_output=True)):
+            shell=True, check_return=True, large_output=True)):
       self.device.StartInstrumentation(
           'test.package/.TestInstrumentation',
           finish=False, raw=False, extras={'test.package.foo': 'Foo',
@@ -1540,7 +1660,7 @@
             '/test/temp/file/tmp.zip', '/test/sdcard/foo123.zip'),
         self.call.device.RunShellCommand(
             'unzip /test/sdcard/foo123.zip&&chmod -R 777 /test/dir',
-            as_root=True,
+            shell=True, as_root=True,
             env={'PATH': '/data/local/tmp/bin:$PATH'},
             check_return=True)):
       self.assertTrue(self.device._PushChangedFilesZipped(test_files,
@@ -1561,7 +1681,7 @@
   def testPathExists_pathExists(self):
     with self.assertCall(
         self.call.device.RunShellCommand(
-            "test -e '/path/file exists'",
+            ['test', '-e', '/path/file exists'],
             as_root=False, check_return=True, timeout=10, retries=0),
         []):
       self.assertTrue(self.device.PathExists('/path/file exists'))
@@ -1569,7 +1689,7 @@
   def testPathExists_multiplePathExists(self):
     with self.assertCall(
         self.call.device.RunShellCommand(
-            "test -e '/path 1' -a -e /path2",
+            ['test', '-e', '/path 1', '-a', '-e', '/path2'],
             as_root=False, check_return=True, timeout=10, retries=0),
         []):
       self.assertTrue(self.device.PathExists(('/path 1', '/path2')))
@@ -1577,7 +1697,7 @@
   def testPathExists_pathDoesntExist(self):
     with self.assertCall(
         self.call.device.RunShellCommand(
-            "test -e /path/file.not.exists",
+            ['test', '-e', '/path/file.not.exists'],
             as_root=False, check_return=True, timeout=10, retries=0),
         self.ShellError()):
       self.assertFalse(self.device.PathExists('/path/file.not.exists'))
@@ -1585,7 +1705,7 @@
   def testPathExists_asRoot(self):
     with self.assertCall(
         self.call.device.RunShellCommand(
-            "test -e /root/path/exists",
+            ['test', '-e', '/root/path/exists'],
             as_root=True, check_return=True, timeout=10, retries=0),
         self.ShellError()):
       self.assertFalse(
@@ -1594,12 +1714,51 @@
   def testFileExists_pathDoesntExist(self):
     with self.assertCall(
         self.call.device.RunShellCommand(
-            "test -e /path/file.not.exists",
+            ['test', '-e', '/path/file.not.exists'],
             as_root=False, check_return=True, timeout=10, retries=0),
         self.ShellError()):
       self.assertFalse(self.device.FileExists('/path/file.not.exists'))
 
 
+class DeviceUtilsRemovePathTest(DeviceUtilsTest):
+
+  def testRemovePath_regular(self):
+    with self.assertCall(
+        self.call.device.RunShellCommand(
+            ['rm', 'some file'], as_root=False, check_return=True),
+        []):
+      self.device.RemovePath('some file')
+
+  def testRemovePath_withForce(self):
+    with self.assertCall(
+        self.call.device.RunShellCommand(
+            ['rm', '-f', 'some file'], as_root=False, check_return=True),
+        []):
+      self.device.RemovePath('some file', force=True)
+
+  def testRemovePath_recursively(self):
+    with self.assertCall(
+        self.call.device.RunShellCommand(
+            ['rm', '-r', '/remove/this/dir'], as_root=False, check_return=True),
+        []):
+      self.device.RemovePath('/remove/this/dir', recursive=True)
+
+  def testRemovePath_withRoot(self):
+    with self.assertCall(
+        self.call.device.RunShellCommand(
+            ['rm', 'some file'], as_root=True, check_return=True),
+        []):
+      self.device.RemovePath('some file', as_root=True)
+
+  def testRemovePath_manyPaths(self):
+    with self.assertCall(
+        self.call.device.RunShellCommand(
+            ['rm', 'eeny', 'meeny', 'miny', 'moe'],
+            as_root=False, check_return=True),
+        []):
+      self.device.RemovePath(['eeny', 'meeny', 'miny', 'moe'])
+
+
 class DeviceUtilsPullFileTest(DeviceUtilsTest):
 
   def testPullFile_existsOnDevice(self):
@@ -1719,7 +1878,7 @@
         self.call.device.RunShellCommand(
             'SRC=/this/big/file/can.be.read.with.su DEST=/sdcard/tmp/on.device;'
             'cp "$SRC" "$DEST" && chmod 666 "$DEST"',
-            as_root=True, check_return=True),
+            shell=True, as_root=True, check_return=True),
         (self.call.device._ReadFileWithPull('/sdcard/tmp/on.device'),
          contents)):
       self.assertEqual(
@@ -2044,7 +2203,8 @@
     self.assertIsNone(self.device._cache['token'])
     with self.assertCall(
         self.call.device.RunShellCommand(
-            AnyStringWith('getprop'), check_return=True, large_output=True),
+            AnyStringWith('getprop'),
+            shell=True, check_return=True, large_output=True),
         ['/sdcard', 'TOKEN']):
       self.device._EnsureCacheInitialized()
     self.assertIsNotNone(self.device._cache['token'])
@@ -2053,7 +2213,8 @@
     self.assertIsNone(self.device._cache['token'])
     with self.assertCall(
         self.call.device.RunShellCommand(
-            AnyStringWith('getprop'), check_return=True, large_output=True),
+            AnyStringWith('getprop'),
+            shell=True, check_return=True, large_output=True),
         self.TimeoutError()):
       with self.assertRaises(device_errors.CommandTimeoutError):
         self.device._EnsureCacheInitialized()
@@ -2122,57 +2283,202 @@
 
 
 class DeviceUtilsGetPidsTest(DeviceUtilsTest):
+  def setUp(self):
+    super(DeviceUtilsGetPidsTest, self).setUp()
+    self.sample_output = [
+        'USER  PID     PPID  VSIZE RSS   WCHAN          PC  NAME',
+        'user  1001    100   1024  1024  ffffffff 00000000 one.match',
+        'user  1002    100   1024  1024  ffffffff 00000000 two.match',
+        'user  1003    100   1024  1024  ffffffff 00000000 three.match',
+        'user  1234    100   1024  1024  ffffffff 00000000 my$process',
+        'user  1000    100   1024  1024  ffffffff 00000000 foo',
+        'user  1236    100   1024  1024  ffffffff 00000000 foo',
+    ]
+
+  def _grepOutput(self, substring):
+    return [line for line in self.sample_output if substring in line]
+
+  def testGetPids_sdkGreaterThanNougatMR1(self):
+    with self.patch_call(self.call.device.build_version_sdk,
+                         return_value=(version_codes.NOUGAT_MR1 + 1)):
+      with self.patch_call(self.call.device.build_id,
+                           return_value='ZZZ99Z'):
+        with self.assertCall(
+            self.call.device._RunPipedShellCommand(
+                'ps -e | grep -F example.process'), []):
+          self.device.GetPids('example.process')
 
   def testGetPids_noMatches(self):
-    with self.assertCall(
-        self.call.device._RunPipedShellCommand('ps | grep -F does.not.match'),
-        []):
-      self.assertEqual({}, self.device.GetPids('does.not.match'))
+    with self.patch_call(self.call.device.build_version_sdk,
+                         return_value=version_codes.LOLLIPOP):
+      with self.assertCall(
+          self.call.device._RunPipedShellCommand('ps | grep -F does.not.match'),
+          self._grepOutput('does.not.match')):
+        self.assertEqual({}, self.device.GetPids('does.not.match'))
 
   def testGetPids_oneMatch(self):
-    with self.assertCall(
-        self.call.device._RunPipedShellCommand('ps | grep -F one.match'),
-        ['user  1001    100   1024 1024   ffffffff 00000000 one.match']):
-      self.assertEqual(
-          {'one.match': ['1001']},
-          self.device.GetPids('one.match'))
+    with self.patch_call(self.call.device.build_version_sdk,
+                         return_value=version_codes.LOLLIPOP):
+      with self.assertCall(
+          self.call.device._RunPipedShellCommand('ps | grep -F one.match'),
+          self._grepOutput('one.match')):
+        self.assertEqual(
+            {'one.match': ['1001']},
+            self.device.GetPids('one.match'))
 
   def testGetPids_multipleMatches(self):
-    with self.assertCall(
-        self.call.device._RunPipedShellCommand('ps | grep -F match'),
-        ['user  1001    100   1024 1024   ffffffff 00000000 one.match',
-         'user  1002    100   1024 1024   ffffffff 00000000 two.match',
-         'user  1003    100   1024 1024   ffffffff 00000000 three.match']):
-      self.assertEqual(
-          {'one.match': ['1001'],
-           'two.match': ['1002'],
-           'three.match': ['1003']},
-          self.device.GetPids('match'))
-
-  def testGetPids_exactMatch(self):
-    with self.assertCall(
-        self.call.device._RunPipedShellCommand('ps | grep -F exact.match'),
-        ['user  1000    100   1024 1024   ffffffff 00000000 not.exact.match',
-         'user  1234    100   1024 1024   ffffffff 00000000 exact.match']):
-      self.assertEqual(
-          {'not.exact.match': ['1000'], 'exact.match': ['1234']},
-          self.device.GetPids('exact.match'))
+    with self.patch_call(self.call.device.build_version_sdk,
+                         return_value=version_codes.LOLLIPOP):
+      with self.assertCall(
+          self.call.device._RunPipedShellCommand('ps | grep -F match'),
+          self._grepOutput('match')):
+        self.assertEqual(
+            {'one.match': ['1001'],
+             'two.match': ['1002'],
+             'three.match': ['1003']},
+            self.device.GetPids('match'))
 
   def testGetPids_quotable(self):
-    with self.assertCall(
-        self.call.device._RunPipedShellCommand("ps | grep -F 'my$process'"),
-        ['user  1234    100   1024 1024   ffffffff 00000000 my$process']):
-      self.assertEqual(
-          {'my$process': ['1234']}, self.device.GetPids('my$process'))
+    with self.patch_call(self.call.device.build_version_sdk,
+                         return_value=version_codes.LOLLIPOP):
+      with self.assertCall(
+          self.call.device._RunPipedShellCommand("ps | grep -F 'my$process'"),
+          self._grepOutput('my$process')):
+        self.assertEqual(
+            {'my$process': ['1234']}, self.device.GetPids('my$process'))
 
   def testGetPids_multipleInstances(self):
-    with self.assertCall(
-        self.call.device._RunPipedShellCommand('ps | grep -F foo'),
-        ['user  1000    100   1024 1024   ffffffff 00000000 foo',
-         'user  1234    100   1024 1024   ffffffff 00000000 foo']):
-      self.assertEqual(
-          {'foo': ['1000', '1234']},
-          self.device.GetPids('foo'))
+    with self.patch_call(self.call.device.build_version_sdk,
+                         return_value=version_codes.LOLLIPOP):
+      with self.assertCall(
+          self.call.device._RunPipedShellCommand('ps | grep -F foo'),
+          self._grepOutput('foo')):
+        self.assertEqual(
+            {'foo': ['1000', '1236']},
+            self.device.GetPids('foo'))
+
+  def testGetPids_allProcesses(self):
+    with self.patch_call(self.call.device.build_version_sdk,
+                         return_value=version_codes.LOLLIPOP):
+      with self.assertCall(
+          self.call.device.RunShellCommand(
+              ['ps'], check_return=True, large_output=True),
+          self.sample_output):
+        self.assertEqual(
+            {'one.match': ['1001'],
+             'two.match': ['1002'],
+             'three.match': ['1003'],
+             'my$process': ['1234'],
+             'foo': ['1000', '1236']},
+            self.device.GetPids())
+
+  def testGetApplicationPids_notFound(self):
+    with self.patch_call(self.call.device.build_version_sdk,
+                         return_value=version_codes.LOLLIPOP):
+      with self.assertCall(
+          self.call.device._RunPipedShellCommand('ps | grep -F match'),
+          self._grepOutput('match')):
+        # No PIDs found, process name should be exact match.
+        self.assertEqual([], self.device.GetApplicationPids('match'))
+
+  def testGetApplicationPids_foundOne(self):
+    with self.patch_call(self.call.device.build_version_sdk,
+                         return_value=version_codes.LOLLIPOP):
+      with self.assertCall(
+          self.call.device._RunPipedShellCommand('ps | grep -F one.match'),
+          self._grepOutput('one.match')):
+        self.assertEqual(['1001'], self.device.GetApplicationPids('one.match'))
+
+  def testGetApplicationPids_foundMany(self):
+    with self.patch_call(self.call.device.build_version_sdk,
+                         return_value=version_codes.LOLLIPOP):
+      with self.assertCall(
+          self.call.device._RunPipedShellCommand('ps | grep -F foo'),
+          self._grepOutput('foo')):
+        self.assertEqual(
+            ['1000', '1236'],
+            self.device.GetApplicationPids('foo'))
+
+  def testGetApplicationPids_atMostOneNotFound(self):
+    with self.patch_call(self.call.device.build_version_sdk,
+                         return_value=version_codes.LOLLIPOP):
+      with self.assertCall(
+          self.call.device._RunPipedShellCommand('ps | grep -F match'),
+          self._grepOutput('match')):
+        # No PIDs found, process name should be exact match.
+        self.assertEqual(
+            None,
+            self.device.GetApplicationPids('match', at_most_one=True))
+
+  def testGetApplicationPids_atMostOneFound(self):
+    with self.patch_call(self.call.device.build_version_sdk,
+                         return_value=version_codes.LOLLIPOP):
+      with self.assertCall(
+          self.call.device._RunPipedShellCommand('ps | grep -F one.match'),
+          self._grepOutput('one.match')):
+        self.assertEqual(
+            '1001',
+            self.device.GetApplicationPids('one.match', at_most_one=True))
+
+  def testGetApplicationPids_atMostOneFoundTooMany(self):
+    with self.patch_call(self.call.device.build_version_sdk,
+                         return_value=version_codes.LOLLIPOP):
+      with self.assertRaises(device_errors.CommandFailedError):
+        with self.assertCall(
+            self.call.device._RunPipedShellCommand('ps | grep -F foo'),
+            self._grepOutput('foo')):
+          self.device.GetApplicationPids('foo', at_most_one=True)
+
+
+class DeviceUtilsGetSetEnforce(DeviceUtilsTest):
+
+  def testGetEnforce_Enforcing(self):
+    with self.assertCall(self.call.adb.Shell('getenforce'), 'Enforcing'):
+      self.assertEqual(True, self.device.GetEnforce())
+
+  def testGetEnforce_Permissive(self):
+    with self.assertCall(self.call.adb.Shell('getenforce'), 'Permissive'):
+      self.assertEqual(False, self.device.GetEnforce())
+
+  def testGetEnforce_Disabled(self):
+    with self.assertCall(self.call.adb.Shell('getenforce'), 'Disabled'):
+      self.assertEqual(None, self.device.GetEnforce())
+
+  def testSetEnforce_Enforcing(self):
+    with self.assertCalls(
+        (self.call.device.NeedsSU(), False),
+        (self.call.adb.Shell('setenforce 1'), '')):
+      self.device.SetEnforce(enabled=True)
+
+  def testSetEnforce_Permissive(self):
+    with self.assertCalls(
+        (self.call.device.NeedsSU(), False),
+        (self.call.adb.Shell('setenforce 0'), '')):
+      self.device.SetEnforce(enabled=False)
+
+  def testSetEnforce_EnforcingWithInt(self):
+    with self.assertCalls(
+        (self.call.device.NeedsSU(), False),
+        (self.call.adb.Shell('setenforce 1'), '')):
+      self.device.SetEnforce(enabled=1)
+
+  def testSetEnforce_PermissiveWithInt(self):
+    with self.assertCalls(
+        (self.call.device.NeedsSU(), False),
+        (self.call.adb.Shell('setenforce 0'), '')):
+      self.device.SetEnforce(enabled=0)
+
+  def testSetEnforce_EnforcingWithStr(self):
+    with self.assertCalls(
+        (self.call.device.NeedsSU(), False),
+        (self.call.adb.Shell('setenforce 1'), '')):
+      self.device.SetEnforce(enabled='1')
+
+  def testSetEnforce_PermissiveWithStr(self):
+    with self.assertCalls(
+        (self.call.device.NeedsSU(), False),
+        (self.call.adb.Shell('setenforce 0'), '')):
+      self.device.SetEnforce(enabled='0')  # Not recommended but it works!
 
 
 class DeviceUtilsTakeScreenshotTest(DeviceUtilsTest):
@@ -2429,7 +2735,7 @@
             self.adb, suffix='.sh'), MockTempFile(mock_temp_file)),
         self.call.device.WriteFile(mock.ANY, mock.ANY),
         (self.call.device.RunShellCommand(
-            ['source', mock_temp_file], as_root=True)),
+            ['source', mock_temp_file], check_return=True, as_root=True)),
         self.call.adb.WaitForDevice()):
       self.device.RestartAdbd()
 
@@ -2450,7 +2756,7 @@
                          return_value=version_codes.MARSHMALLOW):
       with self.assertCalls(
           (self.call.device.RunShellCommand(
-              permissions_cmd, check_return=True), [])):
+              permissions_cmd, shell=True, check_return=True), [])):
         self.device.GrantPermissions('package', ['p1'])
 
   def testGrantPermissions_multiple(self):
@@ -2459,7 +2765,7 @@
                          return_value=version_codes.MARSHMALLOW):
       with self.assertCalls(
           (self.call.device.RunShellCommand(
-              permissions_cmd, check_return=True), [])):
+              permissions_cmd, shell=True, check_return=True), [])):
         self.device.GrantPermissions('package', ['p1', 'p2'])
 
   def testGrantPermissions_WriteExtrnalStorage(self):
@@ -2470,7 +2776,7 @@
                          return_value=version_codes.MARSHMALLOW):
       with self.assertCalls(
           (self.call.device.RunShellCommand(
-              permissions_cmd, check_return=True), [])):
+              permissions_cmd, shell=True, check_return=True), [])):
         self.device.GrantPermissions(
             'package', ['android.permission.WRITE_EXTERNAL_STORAGE'])
 
@@ -2542,7 +2848,7 @@
   def testSetScreen_on(self):
     with self.assertCalls(
         (self.call.device.IsScreenOn(), False),
-        (self.call.device.RunShellCommand('input keyevent 26'), []),
+        (self.call.device.SendKeyEvent(keyevent.KEYCODE_POWER), None),
         (self.call.device.IsScreenOn(), True)):
       self.device.SetScreen(True)
 
@@ -2550,7 +2856,7 @@
   def testSetScreen_off(self):
     with self.assertCalls(
         (self.call.device.IsScreenOn(), True),
-        (self.call.device.RunShellCommand('input keyevent 26'), []),
+        (self.call.device.SendKeyEvent(keyevent.KEYCODE_POWER), None),
         (self.call.device.IsScreenOn(), False)):
       self.device.SetScreen(False)
 
@@ -2558,7 +2864,7 @@
   def testSetScreen_slow(self):
     with self.assertCalls(
         (self.call.device.IsScreenOn(), True),
-        (self.call.device.RunShellCommand('input keyevent 26'), []),
+        (self.call.device.SendKeyEvent(keyevent.KEYCODE_POWER), None),
         (self.call.device.IsScreenOn(), True),
         (self.call.device.IsScreenOn(), True),
         (self.call.device.IsScreenOn(), False)):
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/fastboot_utils.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/fastboot_utils.py
index c5e8a49..3bd3ee8 100644
--- a/sdk/platform-tools/systrace/catapult/devil/devil/android/fastboot_utils.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/fastboot_utils.py
@@ -17,6 +17,8 @@
 from devil.android.sdk import fastboot
 from devil.utils import timeout_retry
 
+logger = logging.getLogger(__name__)
+
 _DEFAULT_TIMEOUT = 30
 _DEFAULT_RETRIES = 3
 _FASTBOOT_REBOOT_TIMEOUT = 10 * _DEFAULT_TIMEOUT
@@ -163,11 +165,11 @@
             elif board_name:
               return False
             else:
-              logging.warning('No board type found in %s.',
-                              self._BOARD_VERIFICATION_FILE)
+              logger.warning('No board type found in %s.',
+                             self._BOARD_VERIFICATION_FILE)
     else:
-      logging.warning('%s not found. Unable to use it to verify device.',
-                      self._BOARD_VERIFICATION_FILE)
+      logger.warning('%s not found. Unable to use it to verify device.',
+                     self._BOARD_VERIFICATION_FILE)
 
     zip_regex = re.compile(r'.*%s.*\.zip' % re.escape(self._board))
     for f in files:
@@ -192,9 +194,9 @@
     """
     if not self._VerifyBoard(directory):
       if force:
-        logging.warning('Could not verify build is meant to be installed on '
-                        'the current device type, but force flag is set. '
-                        'Flashing device. Possibly dangerous operation.')
+        logger.warning('Could not verify build is meant to be installed on '
+                       'the current device type, but force flag is set. '
+                       'Flashing device. Possibly dangerous operation.')
       else:
         raise device_errors.CommandFailedError(
             'Could not verify build is meant to be installed on the current '
@@ -205,10 +207,10 @@
     partitions = flash_image_files.keys()
     for partition in partitions:
       if _KNOWN_PARTITIONS[partition].get('wipe_only') and not wipe:
-        logging.info(
+        logger.info(
             'Not flashing in wipe mode. Skipping partition %s.', partition)
       else:
-        logging.info(
+        logger.info(
             'Flashing %s with %s', partition, flash_image_files[partition])
         self.fastboot.Flash(partition, flash_image_files[partition])
         if _KNOWN_PARTITIONS[partition].get('restart', False):
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/flag_changer.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/flag_changer.py
index 4267f11..b2ee8b1 100644
--- a/sdk/platform-tools/systrace/catapult/devil/devil/android/flag_changer.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/flag_changer.py
@@ -2,9 +2,58 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import contextlib
 import logging
+import posixpath
+import re
 
-from devil.android import device_errors
+from devil.android.sdk import version_codes
+
+
+logger = logging.getLogger(__name__)
+
+
+_CMDLINE_DIR = '/data/local/tmp'
+_CMDLINE_DIR_LEGACY = '/data/local'
+_RE_NEEDS_QUOTING = re.compile(r'[^\w-]')  # Not in: alphanumeric or hyphens.
+_QUOTES = '"\''  # Either a single or a double quote.
+_ESCAPE = '\\'  # A backslash.
+
+
+@contextlib.contextmanager
+def CustomCommandLineFlags(device, cmdline_name, flags):
+  """Context manager to change Chrome's command line temporarily.
+
+  Example:
+
+      with flag_changer.TemporaryCommandLineFlags(device, name, flags):
+        # Launching Chrome will use the provided flags.
+
+      # Previous set of flags on the device is now restored.
+
+  Args:
+    device: A DeviceUtils instance.
+    cmdline_name: Name of the command line file where to store flags.
+    flags: A sequence of command line flags to set.
+  """
+  # On Android N and above, we need to temporarily set SELinux to permissive
+  # so that Chrome is allowed to read the command line file.
+  # TODO(crbug.com/699082): Remove when a solution to avoid this is implemented.
+  needs_permissive = (
+      device.build_version_sdk >= version_codes.NOUGAT and
+      device.GetEnforce())
+  if needs_permissive:
+    device.SetEnforce(enabled=False)
+  try:
+    changer = FlagChanger(device, cmdline_name)
+    try:
+      changer.ReplaceFlags(flags)
+      yield
+    finally:
+      changer.Restore()
+  finally:
+    if needs_permissive:
+      device.SetEnforce(enabled=True)
 
 
 class FlagChanger(object):
@@ -20,26 +69,42 @@
 
     Args:
       device: A DeviceUtils instance.
-      cmdline_file: Path to the command line file on the device.
+      cmdline_file: Name of the command line file where to store flags.
     """
     self._device = device
 
-    # Unrooted devices have limited access to the file system.
-    # Place files in /data/local/tmp/ rather than /data/local/
-    if not device.HasRoot() and not '/data/local/tmp/' in cmdline_file:
-      self._cmdline_file = cmdline_file.replace('/data/local/',
-                                                '/data/local/tmp/')
-    else:
-      self._cmdline_file = cmdline_file
+    if posixpath.sep in cmdline_file:
+      raise ValueError(
+          'cmdline_file should be a file name only, do not include path'
+          ' separators in: %s' % cmdline_file)
+    self._cmdline_path = posixpath.join(_CMDLINE_DIR, cmdline_file)
 
-    stored_flags = ''
-    if self._device.PathExists(self._cmdline_file):
-      try:
-        stored_flags = self._device.ReadFile(self._cmdline_file).strip()
-      except device_errors.CommandFailedError:
-        pass
+    cmdline_path_legacy = posixpath.join(_CMDLINE_DIR_LEGACY, cmdline_file)
+    if self._device.PathExists(cmdline_path_legacy):
+      logging.warning(
+            'Removing legacy command line file %r.', cmdline_path_legacy)
+      self._device.RemovePath(cmdline_path_legacy, as_root=True)
+
+    self._state_stack = [None]  # Actual state is set by GetCurrentFlags().
+    self.GetCurrentFlags()
+
+  def GetCurrentFlags(self):
+    """Read the current flags currently stored in the device.
+
+    Also updates the internal state of the flag_changer.
+
+    Returns:
+      A list of flags.
+    """
+    if self._device.PathExists(self._cmdline_path):
+      command_line = self._device.ReadFile(self._cmdline_path).strip()
+    else:
+      command_line = ''
+    flags = _ParseFlags(command_line)
+
     # Store the flags as a set to facilitate adding and removing flags.
-    self._state_stack = [set(self._TokenizeFlags(stored_flags))]
+    self._state_stack[-1] = set(flags)
+    return flags
 
   def ReplaceFlags(self, flags):
     """Replaces the flags in the command line with the ones provided.
@@ -50,10 +115,13 @@
       flags: A sequence of command line flags to set, eg. ['--single-process'].
              Note: this should include flags only, not the name of a command
              to run (ie. there is no need to start the sequence with 'chrome').
+
+    Returns:
+      A list with the flags now stored on the device.
     """
     new_flags = set(flags)
     self._state_stack.append(new_flags)
-    self._UpdateCommandLineFile()
+    return self._UpdateCommandLineFile()
 
   def AddFlags(self, flags):
     """Appends flags to the command line if they aren't already there.
@@ -62,8 +130,11 @@
 
     Args:
       flags: A sequence of flags to add on, eg. ['--single-process'].
+
+    Returns:
+      A list with the flags now stored on the device.
     """
-    self.PushFlags(add=flags)
+    return self.PushFlags(add=flags)
 
   def RemoveFlags(self, flags):
     """Removes flags from the command line, if they exist.
@@ -78,8 +149,11 @@
              that we expect a complete match when removing flags; if you want
              to remove a switch with a value, you must use the exact string
              used to add it in the first place.
+
+    Returns:
+      A list with the flags now stored on the device.
     """
-    self.PushFlags(remove=flags)
+    return self.PushFlags(remove=flags)
 
   def PushFlags(self, add=None, remove=None):
     """Appends and removes flags to/from the command line if they aren't already
@@ -92,91 +166,135 @@
               expect a complete match when removing flags; if you want to remove
               a switch with a value, you must use the exact string used to add
               it in the first place.
+
+    Returns:
+      A list with the flags now stored on the device.
     """
     new_flags = self._state_stack[-1].copy()
     if add:
       new_flags.update(add)
     if remove:
       new_flags.difference_update(remove)
-    self.ReplaceFlags(new_flags)
+    return self.ReplaceFlags(new_flags)
 
   def Restore(self):
     """Restores the flags to their state prior to the last AddFlags or
        RemoveFlags call.
+
+    Returns:
+      A list with the flags now stored on the device.
     """
     # The initial state must always remain on the stack.
     assert len(self._state_stack) > 1, (
       "Mismatch between calls to Add/RemoveFlags and Restore")
     self._state_stack.pop()
-    self._UpdateCommandLineFile()
+    return self._UpdateCommandLineFile()
 
   def _UpdateCommandLineFile(self):
-    """Writes out the command line to the file, or removes it if empty."""
-    current_flags = list(self._state_stack[-1])
-    logging.info('Current flags: %s', current_flags)
-    # Root is not required to write to /data/local/tmp/.
-    use_root = '/data/local/tmp/' not in self._cmdline_file
-    if current_flags:
-      # The first command line argument doesn't matter as we are not actually
-      # launching the chrome executable using this command line.
-      cmd_line = ' '.join(['_'] + current_flags)
-      self._device.WriteFile(
-          self._cmdline_file, cmd_line, as_root=use_root)
-      file_contents = self._device.ReadFile(
-          self._cmdline_file, as_root=use_root).rstrip()
-      assert file_contents == cmd_line, (
-          'Failed to set the command line file at %s' % self._cmdline_file)
-    else:
-      self._device.RunShellCommand('rm ' + self._cmdline_file,
-                                   as_root=use_root)
-      assert not self._device.FileExists(self._cmdline_file), (
-          'Failed to remove the command line file at %s' % self._cmdline_file)
+    """Writes out the command line to the file, or removes it if empty.
 
-  @staticmethod
-  def _TokenizeFlags(line):
-    """Changes the string containing the command line into a list of flags.
-
-    Follows similar logic to CommandLine.java::tokenizeQuotedArguments:
-    * Flags are split using whitespace, unless the whitespace is within a
-      pair of quotation marks.
-    * Unlike the Java version, we keep the quotation marks around switch
-      values since we need them to re-create the file when new flags are
-      appended.
-
-    Args:
-      line: A string containing the entire command line.  The first token is
-            assumed to be the program name.
+    Returns:
+      A list with the flags now stored on the device.
     """
-    if not line:
-      return []
-
-    tokenized_flags = []
-    current_flag = ""
-    within_quotations = False
-
-    # Move through the string character by character and build up each flag
-    # along the way.
-    for c in line.strip():
-      if c is '"':
-        if len(current_flag) > 0 and current_flag[-1] == '\\':
-          # Last char was a backslash; pop it, and treat this " as a literal.
-          current_flag = current_flag[0:-1] + '"'
-        else:
-          within_quotations = not within_quotations
-          current_flag += c
-      elif not within_quotations and (c is ' ' or c is '\t'):
-        if current_flag is not "":
-          tokenized_flags.append(current_flag)
-          current_flag = ""
-      else:
-        current_flag += c
-
-    # Tack on the last flag.
-    if not current_flag:
-      if within_quotations:
-        logging.warn('Unterminated quoted argument: ' + line)
+    command_line = _SerializeFlags(self._state_stack[-1])
+    if command_line is not None:
+      self._device.WriteFile(self._cmdline_path, command_line)
     else:
-      tokenized_flags.append(current_flag)
+      self._device.RemovePath(self._cmdline_path, force=True)
 
-    # Return everything but the program name.
-    return tokenized_flags[1:]
+    current_flags = self.GetCurrentFlags()
+    logger.info('Flags now set on the device: %s', current_flags)
+    return current_flags
+
+
+def _ParseFlags(line):
+  """Parse the string containing the command line into a list of flags.
+
+  It's a direct port of CommandLine.java::tokenizeQuotedArguments.
+
+  The first token is assumed to be the (unused) program name and stripped off
+  from the list of flags.
+
+  Args:
+    line: A string containing the entire command line.  The first token is
+          assumed to be the program name.
+
+  Returns:
+     A list of flags, with quoting removed.
+  """
+  flags = []
+  current_quote = None
+  current_flag = None
+
+  for c in line:
+    # Detect start or end of quote block.
+    if (current_quote is None and c in _QUOTES) or c == current_quote:
+      if current_flag is not None and current_flag[-1] == _ESCAPE:
+        # Last char was a backslash; pop it, and treat c as a literal.
+        current_flag = current_flag[:-1] + c
+      else:
+        current_quote = c if current_quote is None else None
+    elif current_quote is None and c.isspace():
+      if current_flag is not None:
+        flags.append(current_flag)
+        current_flag = None
+    else:
+      if current_flag is None:
+        current_flag = ''
+      current_flag += c
+
+  if current_flag is not None:
+    if current_quote is not None:
+      logger.warning('Unterminated quoted argument: ' + current_flag)
+    flags.append(current_flag)
+
+  # Return everything but the program name.
+  return flags[1:]
+
+
+def _SerializeFlags(flags):
+  """Serialize a sequence of flags into a command line string.
+
+  Args:
+    flags: A sequence of strings with individual flags.
+
+  Returns:
+    A line with the command line contents to save; or None if the sequence of
+    flags is empty.
+  """
+  if flags:
+    # The first command line argument doesn't matter as we are not actually
+    # launching the chrome executable using this command line.
+    args = ['_']
+    args.extend(_QuoteFlag(f) for f in flags)
+    return ' '.join(args)
+  else:
+    return None
+
+
+def _QuoteFlag(flag):
+  """Validate and quote a single flag.
+
+  Args:
+    A string with the flag to quote.
+
+  Returns:
+    A string with the flag quoted so that it can be parsed by the algorithm
+    in _ParseFlags; or None if the flag does not appear to be valid.
+  """
+  if '=' in flag:
+    key, value = flag.split('=', 1)
+  else:
+    key, value = flag, None
+
+  if not flag or _RE_NEEDS_QUOTING.search(key):
+    # Probably not a valid flag, but quote the whole thing so it can be
+    # parsed back correctly.
+    return '"%s"' % flag.replace('"', r'\"')
+
+  if value is None:
+    return key
+
+  if _RE_NEEDS_QUOTING.search(value):
+    value = '"%s"' % value.replace('"', r'\"')
+  return '='.join([key, value])
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/flag_changer_devicetest.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/flag_changer_devicetest.py
new file mode 100644
index 0000000..b75504b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/flag_changer_devicetest.py
@@ -0,0 +1,88 @@
+#!/usr/bin/env python
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""
+Unit tests for the contents of flag_changer.py.
+The test will invoke real devices
+"""
+
+import os
+import posixpath
+import sys
+import unittest
+
+if __name__ == '__main__':
+  sys.path.append(
+      os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', )))
+
+from devil.android import device_test_case
+from devil.android import device_utils
+from devil.android import flag_changer
+from devil.android.sdk import adb_wrapper
+
+
+_CMDLINE_FILE = 'dummy-command-line'
+
+
+class FlagChangerTest(device_test_case.DeviceTestCase):
+
+  def setUp(self):
+    super(FlagChangerTest, self).setUp()
+    self.adb = adb_wrapper.AdbWrapper(self.serial)
+    self.adb.WaitForDevice()
+    self.device = device_utils.DeviceUtils(
+        self.adb, default_timeout=10, default_retries=0)
+    # pylint: disable=protected-access
+    self.cmdline_path = posixpath.join(flag_changer._CMDLINE_DIR, _CMDLINE_FILE)
+    self.cmdline_path_legacy = posixpath.join(
+        flag_changer._CMDLINE_DIR_LEGACY, _CMDLINE_FILE)
+
+  def tearDown(self):
+    super(FlagChangerTest, self).tearDown()
+    self.device.RemovePath(
+        [self.cmdline_path, self.cmdline_path_legacy], force=True, as_root=True)
+
+  def testFlagChanger_restoreFlags(self):
+    if not self.device.HasRoot():
+      self.skipTest('Test needs a rooted device')
+
+    # Write some custom chrome command line flags.
+    self.device.WriteFile(
+        self.cmdline_path, 'chrome --some --old --flags')
+
+    # Write some more flags on a command line file in the legacy location.
+    self.device.WriteFile(
+        self.cmdline_path_legacy, 'some --stray --flags', as_root=True)
+    self.assertTrue(self.device.PathExists(self.cmdline_path_legacy))
+
+    changer = flag_changer.FlagChanger(self.device, _CMDLINE_FILE)
+
+    # Legacy command line file is removed, ensuring Chrome picks up the
+    # right file.
+    self.assertFalse(self.device.PathExists(self.cmdline_path_legacy))
+
+    # Write some new files, and check they are set.
+    new_flags = ['--my', '--new', '--flags=with special value']
+    self.assertItemsEqual(
+        changer.ReplaceFlags(new_flags),
+        new_flags)
+
+    # Restore and go back to the old flags.
+    self.assertItemsEqual(
+        changer.Restore(),
+        ['--some', '--old', '--flags'])
+
+  def testFlagChanger_removeFlags(self):
+    self.device.RemovePath(self.cmdline_path, force=True)
+    self.assertFalse(self.device.PathExists(self.cmdline_path))
+
+    with flag_changer.CustomCommandLineFlags(
+        self.device, _CMDLINE_FILE, ['--some', '--flags']):
+      self.assertTrue(self.device.PathExists(self.cmdline_path))
+
+    self.assertFalse(self.device.PathExists(self.cmdline_path))
+
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/flag_changer_test.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/flag_changer_test.py
new file mode 100755
index 0000000..5342cf4
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/flag_changer_test.py
@@ -0,0 +1,135 @@
+#!/usr/bin/env python
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import posixpath
+import unittest
+
+from devil.android import flag_changer
+
+
+_CMDLINE_FILE = 'chrome-command-line'
+
+
+class _FakeDevice(object):
+  def __init__(self):
+    self.build_type = 'user'
+    self.has_root = True
+    self.file_system = {}
+
+  def HasRoot(self):
+    return self.has_root
+
+  def PathExists(self, filepath):
+    return filepath in self.file_system
+
+  def RemovePath(self, path, **_kwargs):
+    self.file_system.pop(path)
+
+  def WriteFile(self, path, contents, **_kwargs):
+    self.file_system[path] = contents
+
+  def ReadFile(self, path, **_kwargs):
+    return self.file_system[path]
+
+
+class FlagChangerTest(unittest.TestCase):
+  def setUp(self):
+    self.device = _FakeDevice()
+    # pylint: disable=protected-access
+    self.cmdline_path = posixpath.join(flag_changer._CMDLINE_DIR, _CMDLINE_FILE)
+    self.cmdline_path_legacy = posixpath.join(
+        flag_changer._CMDLINE_DIR_LEGACY, _CMDLINE_FILE)
+
+  def testFlagChanger_removeLegacyCmdLine(self):
+    self.device.WriteFile(self.cmdline_path_legacy, 'chrome --old --stuff')
+    self.assertTrue(self.device.PathExists(self.cmdline_path_legacy))
+
+    changer = flag_changer.FlagChanger(self.device, 'chrome-command-line')
+    self.assertEquals(
+        changer._cmdline_path,  # pylint: disable=protected-access
+        self.cmdline_path)
+    self.assertFalse(self.device.PathExists(self.cmdline_path_legacy))
+
+  def testFlagChanger_mustBeFileName(self):
+    with self.assertRaises(ValueError):
+      flag_changer.FlagChanger(self.device, '/data/local/chrome-command-line')
+
+
+class ParseSerializeFlagsTest(unittest.TestCase):
+  def _testQuoteFlag(self, flag, expected_quoted_flag):
+    # Start with an unquoted flag, check that it's quoted as expected.
+    # pylint: disable=protected-access
+    quoted_flag = flag_changer._QuoteFlag(flag)
+    self.assertEqual(quoted_flag, expected_quoted_flag)
+    # Check that it survives a round-trip.
+    parsed_flags = flag_changer._ParseFlags('_ %s' % quoted_flag)
+    self.assertEqual(len(parsed_flags), 1)
+    self.assertEqual(flag, parsed_flags[0])
+
+  def testQuoteFlag_simple(self):
+    self._testQuoteFlag('--simple-flag', '--simple-flag')
+
+  def testQuoteFlag_withSimpleValue(self):
+    self._testQuoteFlag('--key=value', '--key=value')
+
+  def testQuoteFlag_withQuotedValue1(self):
+    self._testQuoteFlag('--key=valueA valueB', '--key="valueA valueB"')
+
+  def testQuoteFlag_withQuotedValue2(self):
+    self._testQuoteFlag(
+        '--key=this "should" work', r'--key="this \"should\" work"')
+
+  def testQuoteFlag_withQuotedValue3(self):
+    self._testQuoteFlag(
+        "--key=this is 'fine' too", '''--key="this is 'fine' too"''')
+
+  def testQuoteFlag_withQuotedValue4(self):
+    self._testQuoteFlag(
+        "--key='I really want to keep these quotes'",
+        '''--key="'I really want to keep these quotes'"''')
+
+  def testQuoteFlag_withQuotedValue5(self):
+    self._testQuoteFlag(
+        "--this is a strange=flag", '"--this is a strange=flag"')
+
+  def testQuoteFlag_withEmptyValue(self):
+    self._testQuoteFlag('--some-flag=', '--some-flag=')
+
+  def _testParseCmdLine(self, command_line, expected_flags):
+    # Start with a command line, check that flags are parsed as expected.
+    # pylint: disable=protected-access
+    flags = flag_changer._ParseFlags(command_line)
+    self.assertItemsEqual(flags, expected_flags)
+
+    # Check that flags survive a round-trip.
+    # Note: Although new_command_line and command_line may not match, they
+    # should describe the same set of flags.
+    new_command_line = flag_changer._SerializeFlags(flags)
+    new_flags = flag_changer._ParseFlags(new_command_line)
+    self.assertItemsEqual(new_flags, expected_flags)
+
+  def testParseCmdLine_simple(self):
+    self._testParseCmdLine(
+        'chrome --foo --bar="a b" --baz=true --fine="ok"',
+        ['--foo', '--bar=a b', '--baz=true', '--fine=ok'])
+
+  def testParseCmdLine_withFancyQuotes(self):
+    self._testParseCmdLine(
+        r'''_ --foo="this 'is' ok"
+              --bar='this \'is\' too'
+              --baz="this \'is\' tricky"
+        ''',
+        ["--foo=this 'is' ok",
+         "--bar=this 'is' too",
+         r"--baz=this \'is\' tricky"])
+
+  def testParseCmdLine_withUnterminatedQuote(self):
+    self._testParseCmdLine(
+        '_ --foo --bar="I forgot something',
+        ['--foo', '--bar=I forgot something'])
+
+
+if __name__ == '__main__':
+  unittest.main(verbosity=2)
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/forwarder.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/forwarder.py
index 99e8343..244f555 100644
--- a/sdk/platform-tools/systrace/catapult/devil/devil/android/forwarder.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/forwarder.py
@@ -13,14 +13,55 @@
 from devil import devil_env
 from devil.android import device_errors
 from devil.android.constants import file_system
+from devil.android.sdk import adb_wrapper
 from devil.android.valgrind_tools import base_tool
 from devil.utils import cmd_helper
 
+logger = logging.getLogger(__name__)
+
+# If passed as the device port, this will tell the forwarder to allocate
+# a dynamic port on the device. The actual port can then be retrieved with
+# Forwarder.DevicePortForHostPort.
+DYNAMIC_DEVICE_PORT = 0
+
 
 def _GetProcessStartTime(pid):
   return psutil.Process(pid).create_time
 
 
+def _LogMapFailureDiagnostics(device):
+  # The host forwarder daemon logs to /tmp/host_forwarder_log, so print the end
+  # of that.
+  try:
+    with open('/tmp/host_forwarder_log') as host_forwarder_log:
+      logger.info('Last 50 lines of the host forwarder daemon log:')
+      for line in host_forwarder_log.read().splitlines()[-50:]:
+        logger.info('    %s', line)
+  except Exception: # pylint: disable=broad-except
+    # Grabbing the host forwarder log is best-effort. Ignore all errors.
+    logger.warning('Failed to get the contents of host_forwarder_log.')
+
+  # The device forwarder daemon logs to the logcat, so print the end of that.
+  try:
+    logger.info('Last 50 lines of logcat:')
+    for logcat_line in device.adb.Logcat(dump=True)[-50:]:
+      logger.info('    %s', logcat_line)
+  except device_errors.CommandFailedError:
+    # Grabbing the device forwarder log is also best-effort. Ignore all errors.
+    logger.warning('Failed to get the contents of the logcat.')
+
+  # Log alive device forwarders.
+  try:
+    ps_out = device.RunShellCommand(['ps'], check_return=True)
+    logger.info('Currently running device_forwarders:')
+    for line in ps_out:
+      if 'device_forwarder' in line:
+        logger.info('    %s', line)
+  except device_errors.CommandFailedError:
+    logger.warning('Failed to list currently running device_forwarder '
+                   'instances.')
+
+
 class _FileLock(object):
   """With statement-aware implementation of a file lock.
 
@@ -61,6 +102,8 @@
   # Defined in host_forwarder_main.cc
   _HOST_FORWARDER_LOG = '/tmp/host_forwarder_log'
 
+  _TIMEOUT = 60  # seconds
+
   _instance = None
 
   @staticmethod
@@ -87,17 +130,21 @@
       instance._InitDeviceLocked(device, tool)
 
       device_serial = str(device)
-      redirection_commands = [
-          ['--adb=' + devil_env.config.FetchPath('adb'),
+      map_arg_lists = [
+          ['--adb=' + adb_wrapper.AdbWrapper.GetAdbPath(),
            '--serial-id=' + device_serial,
            '--map', str(device_port), str(host_port)]
           for device_port, host_port in port_pairs]
-      logging.info('Forwarding using commands: %s', redirection_commands)
+      logger.info('Forwarding using commands: %s', map_arg_lists)
 
-      for redirection_command in redirection_commands:
+      for map_arg_list in map_arg_lists:
         try:
-          (exit_code, output) = cmd_helper.GetCmdStatusAndOutput(
-              [instance._host_forwarder_path] + redirection_command)
+          map_cmd = [instance._host_forwarder_path] + map_arg_list
+          (exit_code, output) = cmd_helper.GetCmdStatusAndOutputWithTimeout(
+              map_cmd, Forwarder._TIMEOUT)
+        except cmd_helper.TimeoutError as e:
+          raise HostForwarderError(
+              '`%s` timed out:\n%s' % (' '.join(map_cmd), e.output))
         except OSError as e:
           if e.errno == 2:
             raise HostForwarderError(
@@ -105,16 +152,22 @@
                 'Make sure you have built host_forwarder.')
           else: raise
         if exit_code != 0:
-          Forwarder._KillDeviceLocked(device, tool)
-          # Log alive forwarders
-          ps_out = device.RunShellCommand(['ps'])
-          logging.info('Currently running device_forwarders:')
-          for line in ps_out:
-            if 'device_forwarder' in line:
-              logging.info('    %s', line)
+          try:
+            instance._KillDeviceLocked(device, tool)
+          except device_errors.CommandFailedError:
+            # We don't want the failure to kill the device forwarder to
+            # supersede the original failure to map.
+            logging.warning(
+                'Failed to kill the device forwarder after map failure: %s',
+                str(e))
+          _LogMapFailureDiagnostics(device)
+          formatted_output = ('\n'.join(output) if isinstance(output, list)
+                              else output)
           raise HostForwarderError(
-              '%s exited with %d:\n%s' % (instance._host_forwarder_path,
-                                          exit_code, '\n'.join(output)))
+              '`%s` exited with %d:\n%s' % (
+                  ' '.join(map_cmd),
+                  exit_code,
+                  formatted_output))
         tokens = output.split(':')
         if len(tokens) != 2:
           raise HostForwarderError(
@@ -125,8 +178,8 @@
         serial_with_port = (device_serial, device_port)
         instance._device_to_host_port_map[serial_with_port] = host_port
         instance._host_to_device_port_map[host_port] = serial_with_port
-        logging.info('Forwarding device port: %d to host port: %d.',
-                     device_port, host_port)
+        logger.info('Forwarding device port: %d to host port: %d.',
+                    device_port, host_port)
 
   @staticmethod
   def UnmapDevicePort(device_port, device):
@@ -148,27 +201,48 @@
       port_pairs: A list of tuples (device_port, host_port) to unmap.
     """
     with _FileLock(Forwarder._LOCK_PATH):
-      if not Forwarder._instance:
-        return
-      adb_serial = str(device)
-      if adb_serial not in Forwarder._instance._initialized_devices:
-        return
-      port_map = Forwarder._GetInstanceLocked(
-          None)._device_to_host_port_map
-      for (device_serial, device_port) in port_map.keys():
-        if adb_serial == device_serial:
-          Forwarder._UnmapDevicePortLocked(device_port, device)
-      # There are no more ports mapped, kill the device_forwarder.
+      instance = Forwarder._GetInstanceLocked(None)
+      unmap_all_cmd = [
+          instance._host_forwarder_path,
+          '--adb=%s' % adb_wrapper.AdbWrapper.GetAdbPath(),
+          '--serial-id=%s' % device.serial,
+          '--unmap-all'
+      ]
+      try:
+        exit_code, output = cmd_helper.GetCmdStatusAndOutputWithTimeout(
+            unmap_all_cmd, Forwarder._TIMEOUT)
+      except cmd_helper.TimeoutError as e:
+        raise HostForwarderError(
+            '`%s` timed out:\n%s' % (' '.join(unmap_all_cmd), e.output))
+      if exit_code != 0:
+        error_msg = [
+            '`%s` exited with %d' % (' '.join(unmap_all_cmd), exit_code)]
+        if isinstance(output, list):
+          error_msg += output
+        else:
+          error_msg += [output]
+        raise HostForwarderError('\n'.join(error_msg))
+
+      # Clean out any entries from the device & host map.
+      device_map = instance._device_to_host_port_map
+      host_map = instance._host_to_device_port_map
+      for device_serial_and_port, host_port in device_map.items():
+        device_serial = device_serial_and_port[0]
+        if device_serial == device.serial:
+          del device_map[device_serial_and_port]
+          del host_map[host_port]
+
+      # Kill the device forwarder.
       tool = base_tool.BaseTool()
-      Forwarder._KillDeviceLocked(device, tool)
+      instance._KillDeviceLocked(device, tool)
 
   @staticmethod
   def DevicePortForHostPort(host_port):
     """Returns the device port that corresponds to a given host port."""
     with _FileLock(Forwarder._LOCK_PATH):
-      _, device_port = Forwarder._GetInstanceLocked(
+      serial_and_port = Forwarder._GetInstanceLocked(
           None)._host_to_device_port_map.get(host_port)
-      return device_port
+      return serial_and_port[1] if serial_and_port else None
 
   @staticmethod
   def RemoveHostLog():
@@ -225,22 +299,32 @@
     serial = str(device)
     serial_with_port = (serial, device_port)
     if not serial_with_port in instance._device_to_host_port_map:
-      logging.error('Trying to unmap non-forwarded port %d', device_port)
+      logger.error('Trying to unmap non-forwarded port %d', device_port)
       return
-    redirection_command = ['--adb=' + devil_env.config.FetchPath('adb'),
-                           '--serial-id=' + serial,
-                           '--unmap', str(device_port)]
-    logging.info('Undo forwarding using command: %s', redirection_command)
-    (exit_code, output) = cmd_helper.GetCmdStatusAndOutput(
-        [instance._host_forwarder_path] + redirection_command)
-    if exit_code != 0:
-      logging.error(
-          '%s exited with %d:\n%s',
-          instance._host_forwarder_path, exit_code, '\n'.join(output))
+
     host_port = instance._device_to_host_port_map[serial_with_port]
     del instance._device_to_host_port_map[serial_with_port]
     del instance._host_to_device_port_map[host_port]
 
+    unmap_cmd = [
+        instance._host_forwarder_path,
+        '--adb=%s' % adb_wrapper.AdbWrapper.GetAdbPath(),
+        '--serial-id=%s' % serial,
+        '--unmap', str(device_port)
+    ]
+    try:
+      (exit_code, output) = cmd_helper.GetCmdStatusAndOutputWithTimeout(
+          unmap_cmd, Forwarder._TIMEOUT)
+    except cmd_helper.TimeoutError as e:
+      raise HostForwarderError(
+          '`%s` timed out:\n%s' % (' '.join(unmap_cmd), e.output))
+    if exit_code != 0:
+      logger.error(
+          '`%s` exited with %d:\n%s',
+          ' '.join(unmap_cmd),
+          exit_code,
+          '\n'.join(output) if isinstance(output, list) else output)
+
   @staticmethod
   def _GetPidForLock():
     """Returns the PID used for host_forwarder initialization.
@@ -290,9 +374,9 @@
     if device_serial in self._initialized_devices:
       return
     try:
-      Forwarder._KillDeviceLocked(device, tool)
+      self._KillDeviceLocked(device, tool)
     except device_errors.CommandFailedError:
-      logging.warning('Failed to kill device forwarder. Rebooting.')
+      logger.warning('Failed to kill device forwarder. Rebooting.')
       device.Reboot()
     forwarder_device_path_on_host = devil_env.config.FetchPath(
         'forwarder_device', device=device)
@@ -304,30 +388,59 @@
         forwarder_device_path_on_host,
         forwarder_device_path_on_device)])
 
-    cmd = '%s %s' % (tool.GetUtilWrapper(), Forwarder._DEVICE_FORWARDER_PATH)
+    cmd = [Forwarder._DEVICE_FORWARDER_PATH]
+    wrapper = tool.GetUtilWrapper()
+    if wrapper:
+      cmd.insert(0, wrapper)
     device.RunShellCommand(
         cmd, env={'LD_LIBRARY_PATH': Forwarder._DEVICE_FORWARDER_FOLDER},
         check_return=True)
     self._initialized_devices.add(device_serial)
 
+  @staticmethod
+  def KillHost():
+    """Kills the forwarder process running on the host."""
+    with _FileLock(Forwarder._LOCK_PATH):
+      Forwarder._GetInstanceLocked(None)._KillHostLocked()
+
   def _KillHostLocked(self):
     """Kills the forwarder process running on the host.
 
     Note that the global lock must be acquired before calling this method.
     """
-    logging.info('Killing host_forwarder.')
-    (exit_code, output) = cmd_helper.GetCmdStatusAndOutput(
-        [self._host_forwarder_path, '--kill-server'])
-    if exit_code != 0:
-      (exit_code, output) = cmd_helper.GetCmdStatusAndOutput(
-          ['pkill', '-9', 'host_forwarder'])
+    logger.info('Killing host_forwarder.')
+    try:
+      kill_cmd = [self._host_forwarder_path, '--kill-server']
+      (exit_code, _o) = cmd_helper.GetCmdStatusAndOutputWithTimeout(
+          kill_cmd, Forwarder._TIMEOUT)
       if exit_code != 0:
-        raise HostForwarderError(
-            '%s exited with %d:\n%s' % (self._host_forwarder_path, exit_code,
-                                        '\n'.join(output)))
+        kill_cmd = ['pkill', '-9', 'host_forwarder']
+        (exit_code, output) = cmd_helper.GetCmdStatusAndOutputWithTimeout(
+            kill_cmd, Forwarder._TIMEOUT)
+        if exit_code != 0:
+          raise HostForwarderError(
+              '%s exited with %d:\n%s' % (
+                  self._host_forwarder_path,
+                  exit_code,
+                  '\n'.join(output) if isinstance(output, list) else output))
+    except cmd_helper.TimeoutError as e:
+      raise HostForwarderError(
+          '`%s` timed out:\n%s' % (' '.join(kill_cmd), e.output))
 
   @staticmethod
-  def _KillDeviceLocked(device, tool):
+  def KillDevice(device, tool=None):
+    """Kills the forwarder process running on the device.
+
+    Args:
+      device: Instance of DeviceUtils for talking to the device.
+      tool: Wrapper tool (e.g. valgrind) that can be used to execute the device
+            forwarder (see valgrind_tools.py).
+    """
+    with _FileLock(Forwarder._LOCK_PATH):
+      Forwarder._GetInstanceLocked(None)._KillDeviceLocked(
+          device, tool or base_tool.BaseTool())
+
+  def _KillDeviceLocked(self, device, tool):
     """Kills the forwarder process running on the device.
 
     Note that the global lock must be acquired before calling this method.
@@ -337,13 +450,15 @@
       tool: Wrapper tool (e.g. valgrind) that can be used to execute the device
             forwarder (see valgrind_tools.py).
     """
-    logging.info('Killing device_forwarder.')
-    Forwarder._instance._initialized_devices.discard(str(device))
+    logger.info('Killing device_forwarder.')
+    self._initialized_devices.discard(device.serial)
     if not device.FileExists(Forwarder._DEVICE_FORWARDER_PATH):
       return
 
-    cmd = '%s %s --kill-server' % (tool.GetUtilWrapper(),
-                                   Forwarder._DEVICE_FORWARDER_PATH)
+    cmd = [Forwarder._DEVICE_FORWARDER_PATH, '--kill-server']
+    wrapper = tool.GetUtilWrapper()
+    if wrapper:
+      cmd.insert(0, wrapper)
     device.RunShellCommand(
         cmd, env={'LD_LIBRARY_PATH': Forwarder._DEVICE_FORWARDER_FOLDER},
         check_return=True)
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/install_commands.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/install_commands.py
index 5a06bf3..c8da869 100644
--- a/sdk/platform-tools/systrace/catapult/devil/devil/android/install_commands.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/install_commands.py
@@ -42,7 +42,8 @@
         '%s not found. Please build chromium_commands.'
         % chromium_commands_jar_path)
 
-  device.RunShellCommand(['mkdir', BIN_DIR, _FRAMEWORK_DIR])
+  device.RunShellCommand(
+      ['mkdir', '-p', BIN_DIR, _FRAMEWORK_DIR], check_return=True)
   for command, main_class in _COMMANDS.iteritems():
     shell_command = _SHELL_COMMAND_FORMAT % (
         file_system.TEST_EXECUTABLE_DIR, main_class)
@@ -54,4 +55,3 @@
   device.adb.Push(
       chromium_commands_jar_path,
       '%s/chromium_commands.jar' % _FRAMEWORK_DIR)
-
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/logcat_monitor.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/logcat_monitor.py
index bf30d40..0aece87 100644
--- a/sdk/platform-tools/systrace/catapult/devil/devil/android/logcat_monitor.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/logcat_monitor.py
@@ -18,10 +18,13 @@
 from devil.android.sdk import adb_wrapper
 from devil.utils import reraiser_thread
 
+logger = logging.getLogger(__name__)
+
 
 class LogcatMonitor(object):
 
-  _RECORD_THREAD_JOIN_WAIT = 2.0
+  _RECORD_ITER_TIMEOUT = 2.0
+  _RECORD_THREAD_JOIN_WAIT = 5.0
   _WAIT_TIME = 0.2
   _THREADTIME_RE_FORMAT = (
       r'(?P<date>\S*) +(?P<time>\S*) +(?P<proc_id>%s) +(?P<thread_id>%s) +'
@@ -89,7 +92,7 @@
     if isinstance(failure_regex, basestring):
       failure_regex = re.compile(failure_regex)
 
-    logging.debug('Waiting %d seconds for "%s"', timeout, success_regex.pattern)
+    logger.debug('Waiting %d seconds for "%s"', timeout, success_regex.pattern)
 
     # NOTE This will continue looping until:
     #  - success_regex matches a line, in which case the match object is
@@ -162,10 +165,16 @@
       # Write the log with line buffering so the consumer sees each individual
       # line.
       for data in self._adb.Logcat(filter_specs=self._filter_specs,
-                                   logcat_format='threadtime'):
+                                   logcat_format='threadtime',
+                                   iter_timeout=self._RECORD_ITER_TIMEOUT):
+        if self._stop_recording_event.isSet():
+          return
+
+        if data is None:
+          # Logcat can yield None if the iter_timeout is hit.
+          continue
+
         with self._record_file_lock:
-          if self._stop_recording_event.isSet():
-            return
           if self._record_file and not self._record_file.closed:
             self._record_file.write(data + '\n')
 
@@ -232,7 +241,7 @@
     """Closes logcat recording file in case |Close| was never called."""
     with self._record_file_lock:
       if self._record_file:
-        logging.warning(
+        logger.warning(
             'Need to call |Close| on the logcat monitor when done!')
         self._record_file.close()
 
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/md5sum.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/md5sum.py
index 5270646..6dece9e 100644
--- a/sdk/platform-tools/systrace/catapult/devil/devil/android/md5sum.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/md5sum.py
@@ -89,7 +89,7 @@
   # Note: ":" is equivalent to "true".
   md5sum_script += ';:'
   try:
-    out = device.RunShellCommand(md5sum_script, check_return=True)
+    out = device.RunShellCommand(md5sum_script, shell=True, check_return=True)
   except device_errors.AdbShellCommandFailedError as e:
     # Push the binary only if it is found to not exist
     # (faster than checking up-front).
@@ -103,10 +103,10 @@
         device.adb.Push(md5sum_dist_path, MD5SUM_DEVICE_LIB_PATH)
       else:
         mkdir_cmd = 'a=%s;[[ -e $a ]] || mkdir $a' % MD5SUM_DEVICE_LIB_PATH
-        device.RunShellCommand(mkdir_cmd, check_return=True)
+        device.RunShellCommand(mkdir_cmd, shell=True, check_return=True)
         device.adb.Push(md5sum_dist_bin_path, MD5SUM_DEVICE_BIN_PATH)
 
-      out = device.RunShellCommand(md5sum_script, check_return=True)
+      out = device.RunShellCommand(md5sum_script, shell=True, check_return=True)
     else:
       raise
 
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/perf/cache_control.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/perf/cache_control.py
index 7bd0a4e..27782b5 100644
--- a/sdk/platform-tools/systrace/catapult/devil/devil/android/perf/cache_control.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/perf/cache_control.py
@@ -11,6 +11,5 @@
 
   def DropRamCaches(self):
     """Drops the filesystem ram caches for performance testing."""
-    self._device.RunShellCommand('sync', as_root=True)
+    self._device.RunShellCommand(['sync'], check_return=True, as_root=True)
     self._device.WriteFile(CacheControl._DROP_CACHES, '3', as_root=True)
-
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/perf/perf_control.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/perf/perf_control.py
index 383b4fb..06a5db6 100644
--- a/sdk/platform-tools/systrace/catapult/devil/devil/android/perf/perf_control.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/perf/perf_control.py
@@ -8,12 +8,16 @@
 
 from devil.android import device_errors
 
+logger = logging.getLogger(__name__)
+
 
 class PerfControl(object):
   """Provides methods for setting the performance mode of a device."""
+
+  _AVAILABLE_GOVERNORS_REL_PATH = 'cpufreq/scaling_available_governors'
+  _CPU_FILE_PATTERN = re.compile(r'^cpu\d+$')
   _CPU_PATH = '/sys/devices/system/cpu'
   _KERNEL_MAX = '/sys/devices/system/cpu/kernel_max'
-  _CPU_FILE_PATTERN = re.compile(r'^cpu\d+$')
 
   def __init__(self, device):
     self._device = device
@@ -23,19 +27,25 @@
         if self._CPU_FILE_PATTERN.match(filename)]
     assert self._cpu_files, 'Failed to detect CPUs.'
     self._cpu_file_list = ' '.join(self._cpu_files)
-    logging.info('CPUs found: %s', self._cpu_file_list)
+    logger.info('CPUs found: %s', self._cpu_file_list)
+
     self._have_mpdecision = self._device.FileExists('/system/bin/mpdecision')
 
+    raw = self._ReadEachCpuFile(self._AVAILABLE_GOVERNORS_REL_PATH)
+    self._available_governors = [
+        (cpu, raw_governors.strip().split() if not exit_code else None)
+        for cpu, raw_governors, exit_code in raw]
+
   def SetHighPerfMode(self):
     """Sets the highest stable performance mode for the device."""
     try:
       self._device.EnableRoot()
     except device_errors.CommandFailedError:
       message = 'Need root for performance mode. Results may be NOISY!!'
-      logging.warning(message)
+      logger.warning(message)
       # Add an additional warning at exit, such that it's clear that any results
       # may be different/noisy (due to the lack of intended performance mode).
-      atexit.register(logging.warning, message)
+      atexit.register(logger.warning, message)
       return
 
     product_model = self._device.product_model
@@ -43,22 +53,22 @@
     if 'Nexus 4' == product_model:
       self._ForceAllCpusOnline(True)
       if not self._AllCpusAreOnline():
-        logging.warning('Failed to force CPUs online. Results may be NOISY!')
-      self._SetScalingGovernorInternal('performance')
+        logger.warning('Failed to force CPUs online. Results may be NOISY!')
+      self.SetScalingGovernor('performance')
     elif 'Nexus 5' == product_model:
       self._ForceAllCpusOnline(True)
       if not self._AllCpusAreOnline():
-        logging.warning('Failed to force CPUs online. Results may be NOISY!')
-      self._SetScalingGovernorInternal('performance')
+        logger.warning('Failed to force CPUs online. Results may be NOISY!')
+      self.SetScalingGovernor('performance')
       self._SetScalingMaxFreq(1190400)
       self._SetMaxGpuClock(200000000)
     else:
-      self._SetScalingGovernorInternal('performance')
+      self.SetScalingGovernor('performance')
 
   def SetPerfProfilingMode(self):
     """Enables all cores for reliable perf profiling."""
     self._ForceAllCpusOnline(True)
-    self._SetScalingGovernorInternal('performance')
+    self.SetScalingGovernor('performance')
     if not self._AllCpusAreOnline():
       if not self._device.HasRoot():
         raise RuntimeError('Need root to force CPUs online.')
@@ -82,7 +92,7 @@
         'Nexus 7': 'interactive',
         'Nexus 10': 'interactive'
     }.get(product_model, 'ondemand')
-    self._SetScalingGovernorInternal(governor_mode)
+    self.SetScalingGovernor(governor_mode)
     self._ForceAllCpusOnline(False)
 
   def GetCpuInfo(self):
@@ -101,22 +111,63 @@
         'done'
     ])
     output = self._device.RunShellCommand(
-        script, cwd=self._CPU_PATH, check_return=True, as_root=True)
+        script, cwd=self._CPU_PATH, check_return=True, as_root=True, shell=True)
     output = '\n'.join(output).split('%~%')
     return zip(self._cpu_files, output[0::2], (int(c) for c in output[1::2]))
 
   def _WriteEachCpuFile(self, path, value):
+    self._ConditionallyWriteEachCpuFile(path, value, condition='true')
+
+  def _ConditionallyWriteEachCpuFile(self, path, value, condition):
+    template = (
+        '{condition} && test -e "$CPU/{path}" && echo {value} > "$CPU/{path}"')
     results = self._ForEachCpu(
-        'test -e "$CPU/{path}" && echo {value} > "$CPU/{path}"'.format(
-            path=path, value=value))
+        template.format(path=path, value=value, condition=condition))
     cpus = ' '.join(cpu for (cpu, _, status) in results if status == 0)
     if cpus:
-      logging.info('Successfully set %s to %r on: %s', path, value, cpus)
+      logger.info('Successfully set %s to %r on: %s', path, value, cpus)
     else:
-      logging.warning('Failed to set %s to %r on any cpus', path, value)
+      logger.warning('Failed to set %s to %r on any cpus', path, value)
 
-  def _SetScalingGovernorInternal(self, value):
-    self._WriteEachCpuFile('cpufreq/scaling_governor', value)
+  def _ReadEachCpuFile(self, path):
+    return self._ForEachCpu(
+        'cat "$CPU/{path}"'.format(path=path))
+
+  def SetScalingGovernor(self, value):
+    """Sets the scaling governor to the given value on all possible CPUs.
+
+    This does not attempt to set a governor to a value not reported as available
+    on the corresponding CPU.
+
+    Args:
+      value: [string] The new governor value.
+    """
+    condition = 'test -e "{path}" && grep -q {value} {path}'.format(
+        path=('${CPU}/%s' % self._AVAILABLE_GOVERNORS_REL_PATH),
+        value=value)
+    self._ConditionallyWriteEachCpuFile(
+        'cpufreq/scaling_governor', value, condition)
+
+  def GetScalingGovernor(self):
+    """Gets the currently set governor for each CPU.
+
+    Returns:
+      An iterable of 2-tuples, each containing the cpu and the current
+      governor.
+    """
+    raw = self._ReadEachCpuFile('cpufreq/scaling_governor')
+    return [
+        (cpu, raw_governor.strip() if not exit_code else None)
+        for cpu, raw_governor, exit_code in raw]
+
+  def ListAvailableGovernors(self):
+    """Returns the list of available governors for each CPU.
+
+    Returns:
+      An iterable of 2-tuples, each containing the cpu and a list of available
+      governors for that cpu.
+    """
+    return self._available_governors
 
   def _SetScalingMaxFreq(self, value):
     self._WriteEachCpuFile('cpufreq/scaling_max_freq', '%d' % value)
@@ -153,7 +204,7 @@
       self._device.RunShellCommand(cmd, check_return=True, as_root=True)
 
     if not self._have_mpdecision and not self._AllCpusAreOnline():
-      logging.warning('Unexpected cpu hot plugging detected.')
+      logger.warning('Unexpected cpu hot plugging detected.')
 
     if force_online:
       self._ForEachCpu('echo 1 > "$CPU/online"')
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/perf/surface_stats_collector.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/perf/surface_stats_collector.py
index 49372ad..25079f3 100644
--- a/sdk/platform-tools/systrace/catapult/devil/devil/android/perf/surface_stats_collector.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/perf/surface_stats_collector.py
@@ -105,15 +105,17 @@
     # The command returns nothing if it is supported, otherwise returns many
     # lines of result just like 'dumpsys SurfaceFlinger'.
     results = self._device.RunShellCommand(
-        'dumpsys SurfaceFlinger --latency-clear SurfaceView')
+        ['dumpsys', 'SurfaceFlinger', '--latency-clear', 'SurfaceView'],
+        check_return=True)
     return not len(results)
 
   def GetSurfaceFlingerPid(self):
-    results = self._device.RunShellCommand('ps | grep surfaceflinger')
-    if not results:
+    pids_dict = self._device.GetPids('surfaceflinger')
+    if not pids_dict:
       raise Exception('Unable to get surface flinger process id')
-    pid = results[0].split()[1]
-    return pid
+    # TODO(cataput:#3378): Do more strict checks in GetPids when possible.
+    # For now it just returns the first pid found of some matching process.
+    return pids_dict.popitem()[1][0]
 
   def _GetSurfaceFlingerFrameData(self):
     """Returns collected SurfaceFlinger frame timing data.
@@ -156,7 +158,8 @@
     # the activity's main window are not updated when the main web content is
     # composited into a SurfaceView.
     results = self._device.RunShellCommand(
-        'dumpsys SurfaceFlinger --latency SurfaceView')
+        ['dumpsys', 'SurfaceFlinger', '--latency', 'SurfaceView'],
+        check_return=True)
     if not len(results):
       return (None, None)
 
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/perf/thermal_throttle.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/perf/thermal_throttle.py
index 362a9d4..546a92e 100644
--- a/sdk/platform-tools/systrace/catapult/devil/devil/android/perf/thermal_throttle.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/perf/thermal_throttle.py
@@ -4,6 +4,8 @@
 
 import logging
 
+logger = logging.getLogger(__name__)
+
 
 class OmapThrottlingDetector(object):
   """Class to detect and track thermal throttling on an OMAP 4."""
@@ -101,32 +103,33 @@
     for line in log:
       if self._detector.BecameThrottled(line):
         if not self._throttled:
-          logging.warning('>>> Device %s thermally throttled', serial_number)
+          logger.warning('>>> Device %s thermally throttled', serial_number)
         self._throttled = True
         has_been_throttled = True
       elif self._detector.BecameUnthrottled(line):
         if self._throttled:
-          logging.warning('>>> Device %s thermally unthrottled', serial_number)
+          logger.warning('>>> Device %s thermally unthrottled', serial_number)
         self._throttled = False
         has_been_throttled = True
       temperature = self._detector.GetThrottlingTemperature(line)
       if temperature is not None:
-        logging.info(u'Device %s thermally throttled at %3.1f%sC',
-                     serial_number, temperature, degree_symbol)
+        logger.info(u'Device %s thermally throttled at %3.1f%sC',
+                    serial_number, temperature, degree_symbol)
 
-    if logging.getLogger().isEnabledFor(logging.DEBUG):
+    if logger.isEnabledFor(logging.DEBUG):
       # Print current temperature of CPU SoC.
       temperature = self._detector.GetCurrentTemperature()
       if temperature is not None:
-        logging.debug(u'Current SoC temperature of %s = %3.1f%sC',
-                      serial_number, temperature, degree_symbol)
+        logger.debug(u'Current SoC temperature of %s = %3.1f%sC',
+                     serial_number, temperature, degree_symbol)
 
       # Print temperature of battery, to give a system temperature
-      dumpsys_log = self._device.RunShellCommand('dumpsys battery')
+      dumpsys_log = self._device.RunShellCommand(
+          ['dumpsys', 'battery'], check_return=True)
       for line in dumpsys_log:
         if 'temperature' in line:
           btemp = float([s for s in line.split() if s.isdigit()][0]) / 10.0
-          logging.debug(u'Current battery temperature of %s = %3.1f%sC',
-                        serial_number, btemp, degree_symbol)
+          logger.debug(u'Current battery temperature of %s = %3.1f%sC',
+                       serial_number, btemp, degree_symbol)
 
     return has_been_throttled
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/ports.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/ports.py
index 6384d74..1d4e5f2 100644
--- a/sdk/platform-tools/systrace/catapult/devil/devil/android/ports.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/ports.py
@@ -12,6 +12,8 @@
 import socket
 import traceback
 
+logger = logging.getLogger(__name__)
+
 # The net test server is started from port 10201.
 _TEST_SERVER_PORT_FIRST = 10201
 _TEST_SERVER_PORT_LAST = 30000
@@ -36,7 +38,7 @@
       fp.write('%d' % _TEST_SERVER_PORT_FIRST)
     return True
   except Exception:  # pylint: disable=broad-except
-    logging.exception('Error while resetting port allocation')
+    logger.exception('Error while resetting port allocation')
   return False
 
 
@@ -68,16 +70,16 @@
         fp.seek(0, os.SEEK_SET)
         fp.write('%d' % (port + 1))
   except Exception:  # pylint: disable=broad-except
-    logging.exception('Error while allocating port')
+    logger.exception('Error while allocating port')
   finally:
     if fp_lock:
       fcntl.flock(fp_lock, fcntl.LOCK_UN)
       fp_lock.close()
   if port:
-    logging.info('Allocate port %d for test server.', port)
+    logger.info('Allocate port %d for test server.', port)
   else:
-    logging.error('Could not allocate port for test server. '
-                  'List of ports tried: %s', str(ports_tried))
+    logger.error('Could not allocate port for test server. '
+                 'List of ports tried: %s', str(ports_tried))
   return port
 
 
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/sdk/adb_compatibility_devicetest.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/sdk/adb_compatibility_devicetest.py
index 49a4971..cbe2a1b 100644
--- a/sdk/platform-tools/systrace/catapult/devil/devil/android/sdk/adb_compatibility_devicetest.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/sdk/adb_compatibility_devicetest.py
@@ -83,7 +83,7 @@
       adb_wrapper.AdbWrapper.StartServer()
 
     adb_pids = _hostAdbPids()
-    self.assertEqual(1, len(adb_pids))
+    self.assertGreaterEqual(len(adb_pids), 1)
 
     kill_server_status, _ = cmd_helper.GetCmdStatusAndOutput(
         [adb_wrapper.AdbWrapper.GetAdbPath(), 'kill-server'])
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/sdk/adb_wrapper.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/sdk/adb_wrapper.py
index cbc5ed5..7f6b8d9 100644
--- a/sdk/platform-tools/systrace/catapult/devil/devil/android/sdk/adb_wrapper.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/sdk/adb_wrapper.py
@@ -27,6 +27,8 @@
 with devil_env.SysPath(devil_env.DEPENDENCY_MANAGER_PATH):
   import dependency_manager  # pylint: disable=import-error
 
+logger = logging.getLogger(__name__)
+
 
 ADB_KEYS_FILE = '/data/misc/adb/adb_keys'
 
@@ -36,8 +38,8 @@
 _ADB_VERSION_RE = re.compile(r'Android Debug Bridge version (\d+\.\d+\.\d+)')
 _EMULATOR_RE = re.compile(r'^emulator-[0-9]+$')
 _READY_STATE = 'device'
-_VERITY_DISABLE_RE = re.compile('Verity (already)? disabled')
-_VERITY_ENABLE_RE = re.compile('Verity (already)? enabled')
+_VERITY_DISABLE_RE = re.compile(r'Verity (already )?disabled')
+_VERITY_ENABLE_RE = re.compile(r'Verity (already )?enabled')
 
 
 def VerifyLocalFileExists(path):
@@ -279,18 +281,21 @@
                            device_serial=self._device_serial,
                            check_error=check_error)
 
-  def _IterRunDeviceAdbCmd(self, args, timeout):
+  def _IterRunDeviceAdbCmd(self, args, iter_timeout, timeout):
     """Runs an adb command and returns an iterator over its output lines.
 
     Args:
       args: A list of arguments to adb.
-      timeout: Timeout in seconds.
+      iter_timeout: Timeout for each iteration in seconds.
+      timeout: Timeout for the entire command in seconds.
 
     Yields:
       The output of the command line by line.
     """
     return cmd_helper.IterCmdOutputLines(
-      self._BuildAdbCmd(args, self._device_serial), timeout=timeout)
+        self._BuildAdbCmd(args, self._device_serial),
+        iter_timeout=iter_timeout,
+        timeout=timeout)
 
   def __eq__(self, other):
     """Consider instances equal if they refer to the same device.
@@ -319,7 +324,7 @@
   def IsServerOnline(cls):
     status, output = cmd_helper.GetCmdStatusAndOutput(['pgrep', 'adb'])
     output = [int(x) for x in output.split()]
-    logging.info('PIDs for adb found: %r', output)
+    logger.info('PIDs for adb found: %r', output)
     return status == 0
   # pylint: enable=unused-argument
 
@@ -451,7 +456,9 @@
       VerifyLocalFileExists(local)
     except IOError:
       raise device_errors.AdbCommandFailedError(
-          cmd, 'File not found on host: %s' % local, device_serial=str(self))
+          cmd,
+          'File pulled from the device did not arrive on the host: %s' % local,
+          device_serial=str(self))
 
   def Shell(self, command, expect_status=0, timeout=DEFAULT_TIMEOUT,
             retries=DEFAULT_RETRIES):
@@ -485,7 +492,7 @@
       try:
         status = int(output[output_end + 1:])
       except ValueError:
-        logging.warning('exit status of shell command %r missing.', command)
+        logger.warning('exit status of shell command %r missing.', command)
         raise device_errors.AdbShellCommandFailedError(
             command, output, status=None, device_serial=self._device_serial)
       output = output[:output_end]
@@ -546,8 +553,8 @@
           device_serial=self._device_serial)
 
   def Logcat(self, clear=False, dump=False, filter_specs=None,
-             logcat_format=None, ring_buffer=None, timeout=None,
-             retries=DEFAULT_RETRIES):
+             logcat_format=None, ring_buffer=None, iter_timeout=None,
+             timeout=None, retries=DEFAULT_RETRIES):
     """Get an iterable over the logcat output.
 
     Args:
@@ -560,6 +567,9 @@
       ring_buffer: If set, a list of alternate ring buffers to request.
         Options include "main", "system", "radio", "events", "crash" or "all".
         The default is equivalent to ["main", "system", "crash"].
+      iter_timeout: If set and neither clear nor dump is set, the number of
+        seconds to wait between iterations. If no line is found before the
+        given number of seconds elapses, the iterable will yield None.
       timeout: (optional) If set, timeout per try in seconds. If clear or dump
         is set, defaults to DEFAULT_TIMEOUT.
       retries: (optional) If clear or dump is set, the number of retries to
@@ -585,7 +595,7 @@
       cmd.extend(filter_specs)
 
     if use_iter:
-      return self._IterRunDeviceAdbCmd(cmd, timeout)
+      return self._IterRunDeviceAdbCmd(cmd, iter_timeout, timeout)
     else:
       timeout = timeout if timeout is not None else DEFAULT_TIMEOUT
       return self._RunDeviceAdbCmd(cmd, timeout, retries).splitlines()
@@ -635,7 +645,20 @@
     Args:
       timeout: (optional) Timeout per try in seconds.
       retries: (optional) Number of retries to attempt.
+    Returns:
+      The output of adb forward --list as a string.
     """
+    if (distutils.version.LooseVersion(self.Version()) >=
+        distutils.version.LooseVersion('1.0.36')):
+      # Starting in 1.0.36, this can occasionally fail with a protocol fault.
+      # As this interrupts all connections with all devices, we instead just
+      # return an empty list. This may give clients an inaccurate result, but
+      # that's usually better than crashing the adb server.
+
+      # TODO(jbudorick): Determine an appropriate upper version bound for this
+      # once b/31811775 is fixed.
+      return ''
+
     return self._RunDeviceAdbCmd(['forward', '--list'], timeout, retries)
 
   def JDWP(self, timeout=DEFAULT_TIMEOUT, retries=DEFAULT_RETRIES):
@@ -730,7 +753,7 @@
       cmd.append('-k')
     cmd.append(package)
     output = self._RunDeviceAdbCmd(cmd, timeout, retries)
-    if 'Failure' in output:
+    if 'Failure' in output or 'Exception' in output:
       raise device_errors.AdbCommandFailedError(
           cmd, output, device_serial=self._device_serial)
 
@@ -871,14 +894,14 @@
   def DisableVerity(self, timeout=DEFAULT_TIMEOUT, retries=DEFAULT_RETRIES):
     """Disable Marshmallow's Verity security feature"""
     output = self._RunDeviceAdbCmd(['disable-verity'], timeout, retries)
-    if output and _VERITY_DISABLE_RE.search(output):
+    if output and not _VERITY_DISABLE_RE.search(output):
       raise device_errors.AdbCommandFailedError(
           ['disable-verity'], output, device_serial=self._device_serial)
 
   def EnableVerity(self, timeout=DEFAULT_TIMEOUT, retries=DEFAULT_RETRIES):
     """Enable Marshmallow's Verity security feature"""
     output = self._RunDeviceAdbCmd(['enable-verity'], timeout, retries)
-    if output and _VERITY_ENABLE_RE.search(output):
+    if output and not _VERITY_ENABLE_RE.search(output):
       raise device_errors.AdbCommandFailedError(
           ['enable-verity'], output, device_serial=self._device_serial)
 
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/sdk/adb_wrapper_test.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/sdk/adb_wrapper_test.py
new file mode 100755
index 0000000..ef08661
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/sdk/adb_wrapper_test.py
@@ -0,0 +1,59 @@
+#!/usr/bin/env python
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Unit tests for some APIs with conditional logic in adb_wrapper.py
+"""
+
+import unittest
+
+from devil import devil_env
+from devil.android import device_errors
+from devil.android.sdk import adb_wrapper
+
+with devil_env.SysPath(devil_env.PYMOCK_PATH):
+  import mock  # pylint: disable=import-error
+
+
+class AdbWrapperTest(unittest.TestCase):
+  def setUp(self):
+    self.adb = adb_wrapper.AdbWrapper('ABC12345678')
+
+  def _MockRunDeviceAdbCmd(self, return_value):
+    return mock.patch.object(
+        self.adb,
+        '_RunDeviceAdbCmd',
+        mock.Mock(side_effect=None, return_value=return_value))
+
+  def testDisableVerityWhenDisabled(self):
+    with self._MockRunDeviceAdbCmd('Verity already disabled on /system'):
+      self.adb.DisableVerity()
+
+  def testDisableVerityWhenEnabled(self):
+    with self._MockRunDeviceAdbCmd(
+        'Verity disabled on /system\nNow reboot your device for settings to '
+        'take effect'):
+      self.adb.DisableVerity()
+
+  def testEnableVerityWhenEnabled(self):
+    with self._MockRunDeviceAdbCmd('Verity already enabled on /system'):
+      self.adb.EnableVerity()
+
+  def testEnableVerityWhenDisabled(self):
+    with self._MockRunDeviceAdbCmd(
+        'Verity enabled on /system\nNow reboot your device for settings to '
+        'take effect'):
+      self.adb.EnableVerity()
+
+  def testFailEnableVerity(self):
+    with self._MockRunDeviceAdbCmd('error: closed'):
+      self.assertRaises(
+          device_errors.AdbCommandFailedError, self.adb.EnableVerity)
+
+  def testFailDisableVerity(self):
+    with self._MockRunDeviceAdbCmd('error: closed'):
+      self.assertRaises(
+          device_errors.AdbCommandFailedError, self.adb.DisableVerity)
+
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/sdk/gce_adb_wrapper.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/sdk/gce_adb_wrapper.py
index a85d2bd..71600f4 100644
--- a/sdk/platform-tools/systrace/catapult/devil/devil/android/sdk/gce_adb_wrapper.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/sdk/gce_adb_wrapper.py
@@ -18,6 +18,8 @@
 from devil.android.sdk import adb_wrapper
 from devil.utils import cmd_helper
 
+logger = logging.getLogger(__name__)
+
 
 class GceAdbWrapper(adb_wrapper.AdbWrapper):
 
@@ -113,7 +115,7 @@
     try:
       adb_wrapper.VerifyLocalFileExists(local)
     except (subprocess.CalledProcessError, IOError):
-      logging.exception('Error when pulling files from android instance.')
+      logger.exception('Error when pulling files from android instance.')
       raise device_errors.AdbCommandFailedError(
           cmd, 'File not reachable on host: %s' % local,
           device_serial=str(self))
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/sdk/keyevent.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/sdk/keyevent.py
index 40f9416..657dc96 100644
--- a/sdk/platform-tools/systrace/catapult/devil/devil/android/sdk/keyevent.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/sdk/keyevent.py
@@ -22,6 +22,8 @@
 
 KEYCODE_DPAD_RIGHT = 22
 
+KEYCODE_POWER = 26
+
 KEYCODE_A = 29
 KEYCODE_B = 30
 KEYCODE_C = 31
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/sdk/shared_prefs.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/sdk/shared_prefs.py
index 50ff5c6..2fa2e6a 100644
--- a/sdk/platform-tools/systrace/catapult/devil/devil/android/sdk/shared_prefs.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/sdk/shared_prefs.py
@@ -11,8 +11,12 @@
 import logging
 import posixpath
 
+from devil.android import device_errors
+from devil.android.sdk import version_codes
 from xml.etree import ElementTree
 
+logger = logging.getLogger(__name__)
+
 
 _XML_DECLARATION = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
 
@@ -268,6 +272,19 @@
         ['mkdir', '-p', posixpath.dirname(self.path)],
         as_root=True, check_return=True)
     self._device.WriteFile(self.path, str(self), as_root=True)
+    # Creating the directory/file can cause issues with SELinux if they did
+    # not already exist. As a workaround, apply the package's security context
+    # to the shared_prefs directory, which mimics the behavior of a file
+    # created by the app itself
+    if self._device.build_version_sdk >= version_codes.MARSHMALLOW:
+      security_context = self._GetSecurityContext(self.package)
+      if security_context == None:
+        raise device_errors.CommandFailedError(
+            'Failed to get security context for %s' % self.package)
+      self._device.RunShellCommand(
+          ['chcon', '-R', security_context,
+           '/data/data/%s/shared_prefs' % self.package],
+          as_root=True, check_return=True)
     self._device.KillAll(self.package, exact=True, as_root=True, quiet=True)
     self._changed = False
 
@@ -388,4 +405,16 @@
     if old_value != value:
       pref.set(value)
       self._changed = True
-      logging.info('Setting property: %s', pref)
+      logger.info('Setting property: %s', pref)
+
+  def _GetSecurityContext(self, package):
+    for line in self._device.RunShellCommand(['ls', '-Z', '/data/data/'],
+                                             as_root=True, check_return=True):
+      split_line = line.split()
+      # ls -Z output differs between Android versions, but the package is
+      # always last and the context always starts with "u:object"
+      if split_line[-1] == package:
+        for column in split_line:
+          if column.startswith('u:object'):
+            return column
+    return None
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/sdk/shared_prefs_test.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/sdk/shared_prefs_test.py
index ff3b9a1..4c31c56 100755
--- a/sdk/platform-tools/systrace/catapult/devil/devil/android/sdk/shared_prefs_test.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/sdk/shared_prefs_test.py
@@ -13,6 +13,7 @@
 from devil import devil_env
 from devil.android import device_utils
 from devil.android.sdk import shared_prefs
+from devil.android.sdk import version_codes
 
 with devil_env.SysPath(devil_env.PYMOCK_PATH):
   import mock  # pylint: disable=import-error
@@ -97,6 +98,8 @@
     self.assertTrue(prefs.changed)
 
   def testCommit(self):
+    type(self.device).build_version_sdk = mock.PropertyMock(
+        return_value=version_codes.LOLLIPOP_MR1)
     prefs = shared_prefs.SharedPrefs(
         self.device, 'com.some.package', 'other_prefs.xml')
     self.assertFalse(self.device.FileExists(prefs.path))  # file does not exist
@@ -131,6 +134,8 @@
     self.assertEquals(self.device.WriteFile.call_args_list, [])  # did not write
 
   def testAsContextManager_readAndWrite(self):
+    type(self.device).build_version_sdk = mock.PropertyMock(
+        return_value=version_codes.LOLLIPOP_MR1)
     with shared_prefs.SharedPrefs(
         self.device, 'com.some.package', 'prefs.xml') as prefs:
       prefs.SetBoolean('featureEnabled', True)
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/sdk/version_codes.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/sdk/version_codes.py
index 410379b..3f03cba 100644
--- a/sdk/platform-tools/systrace/catapult/devil/devil/android/sdk/version_codes.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/sdk/version_codes.py
@@ -15,4 +15,6 @@
 LOLLIPOP = 21
 LOLLIPOP_MR1 = 22
 MARSHMALLOW = 23
+NOUGAT = 24
+NOUGAT_MR1 = 25
 
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/settings.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/settings.py
index 2249c2a..886b266 100644
--- a/sdk/platform-tools/systrace/catapult/devil/devil/android/settings.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/settings.py
@@ -4,6 +4,8 @@
 
 import logging
 
+logger = logging.getLogger(__name__)
+
 _LOCK_SCREEN_SETTINGS_PATH = '/data/system/locksettings.db'
 _ALTERNATE_LOCK_SCREEN_SETTINGS_PATH = (
     '/data/data/com.android.providers.settings/databases/settings.db')
@@ -139,7 +141,8 @@
     # Example row:
     # 'Row: 0 _id=13, name=logging_id2, value=-1fccbaa546705b05'
     for row in self._device.RunShellCommand(
-        'content query --uri content://%s' % self._table, as_root=True):
+        ['content', 'query', '--uri', 'content://%s' % self._table],
+        check_return=True, as_root=True):
       fields = row.split(', ')
       key = None
       value = None
@@ -157,33 +160,29 @@
 
   def __getitem__(self, key):
     return self._device.RunShellCommand(
-        'content query --uri content://%s --where "name=\'%s\'" '
-        '--projection value' % (self._table, key), as_root=True).strip()
+        ['content', 'query', '--uri', 'content://%s' % self._table,
+         '--where', "name='%s'" % key],
+        check_return=True, as_root=True).strip()
 
   def __setitem__(self, key, value):
     if key in self:
       self._device.RunShellCommand(
-          'content update --uri content://%s '
-          '--bind value:%s:%s --where "name=\'%s\'"' % (
-              self._table,
-              self._GetTypeBinding(value), value, key),
-          as_root=True)
+          ['content', 'update', '--uri', 'content://%s' % self._table,
+           '--bind', 'value:%s:%s' % (self._GetTypeBinding(value), value),
+           '--where', "name='%s'" % key],
+          check_return=True, as_root=True)
     else:
       self._device.RunShellCommand(
-          'content insert --uri content://%s '
-          '--bind name:%s:%s --bind value:%s:%s' % (
-              self._table,
-              self._GetTypeBinding(key), key,
-              self._GetTypeBinding(value), value),
-          as_root=True)
+          ['content', 'insert', '--uri', 'content://%s' % self._table,
+           '--bind', 'name:%s:%s' % (self._GetTypeBinding(key), key),
+           '--bind', 'value:%s:%s' % (self._GetTypeBinding(value), value)],
+          check_return=True, as_root=True)
 
   def __delitem__(self, key):
     self._device.RunShellCommand(
-        'content delete --uri content://%s '
-        '--bind name:%s:%s' % (
-            self._table,
-            self._GetTypeBinding(key), key),
-        as_root=True)
+        ['content', 'delete', '--uri', 'content://%s' % self._table,
+         '--bind', 'name:%s:%s' % (self._GetTypeBinding(key), key)],
+        check_return=True, as_root=True)
 
 
 def ConfigureContentSettings(device, desired_settings):
@@ -205,9 +204,9 @@
     settings = ContentSettings(table, device)
     for key, value in key_value:
       settings[key] = value
-    logging.info('\n%s %s', table, (80 - len(table)) * '-')
+    logger.info('\n%s %s', table, (80 - len(table)) * '-')
     for key, value in sorted(settings.iteritems()):
-      logging.info('\t%s: %s', key, value)
+      logger.info('\t%s: %s', key, value)
 
 
 def SetLockScreenSettings(device):
@@ -230,8 +229,8 @@
     Exception if the setting was not properly set.
   """
   if device.build_type not in _COMPATIBLE_BUILD_TYPES:
-    logging.warning('Unable to disable lockscreen on %s builds.',
-                    device.build_type)
+    logger.warning('Unable to disable lockscreen on %s builds.',
+                   device.build_type)
     return
 
   def get_lock_settings(table):
@@ -251,7 +250,7 @@
     columns = ['name', 'value']
     generate_values = lambda k, v: [k, v]
   else:
-    logging.warning('Unable to find database file to set lock screen settings.')
+    logger.warning('Unable to find database file to set lock screen settings.')
     return
 
   for table, key, value in locksettings:
@@ -268,8 +267,7 @@
       'columns': ', '.join(columns),
       'values': ', '.join(["'%s'" % value for value in values])
     }
-    output_msg = device.RunShellCommand('sqlite3 %s "%s"' % (db, cmd),
-                                        as_root=True)
+    output_msg = device.RunShellCommand(
+        ['sqlite3', db, cmd], check_return=True, as_root=True)
     if output_msg:
-      logging.info(' '.join(output_msg))
-
+      logger.info(' '.join(output_msg))
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/tools/adb_run_shell_cmd.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/tools/adb_run_shell_cmd.py
index a826ab1..77b67e8 100755
--- a/sdk/platform-tools/systrace/catapult/devil/devil/android/tools/adb_run_shell_cmd.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/tools/adb_run_shell_cmd.py
@@ -5,8 +5,14 @@
 
 import argparse
 import json
+import os
 import sys
 
+if __name__ == '__main__':
+  sys.path.append(
+      os.path.abspath(os.path.join(os.path.dirname(__file__),
+                                   '..', '..', '..')))
+
 from devil.android import device_blacklist
 from devil.android import device_utils
 from devil.utils import run_tests_helper
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/tools/cpufreq.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/tools/cpufreq.py
new file mode 100755
index 0000000..97deaf0
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/tools/cpufreq.py
@@ -0,0 +1,87 @@
+#! /usr/bin/env python
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""A script to manipulate device CPU frequency."""
+
+import argparse
+import os
+import pprint
+import sys
+
+if __name__ == '__main__':
+  sys.path.append(
+      os.path.abspath(os.path.join(os.path.dirname(__file__),
+                                   '..', '..', '..')))
+
+from devil import devil_env
+from devil.android import device_utils
+from devil.android.perf import perf_control
+from devil.utils import run_tests_helper
+
+
+def SetScalingGovernor(device, args):
+  p = perf_control.PerfControl(device)
+  p.SetScalingGovernor(args.governor)
+
+
+def GetScalingGovernor(device, _args):
+  p = perf_control.PerfControl(device)
+  for cpu, governor in p.GetScalingGovernor():
+    print '%s %s: %s' % (str(device), cpu, governor)
+
+
+def ListAvailableGovernors(device, _args):
+  p = perf_control.PerfControl(device)
+  for cpu, governors in p.ListAvailableGovernors():
+    print '%s %s: %s' % (str(device), cpu, pprint.pformat(governors))
+
+
+def main(raw_args):
+  parser = argparse.ArgumentParser()
+  parser.add_argument(
+      '--adb-path',
+      help='ADB binary path.')
+  parser.add_argument(
+      '--device', dest='devices', action='append', default=[],
+      help='Devices for which the governor should be set. Defaults to all.')
+  parser.add_argument(
+      '-v', '--verbose', action='count',
+      help='Log more.')
+
+  subparsers = parser.add_subparsers()
+
+  set_governor = subparsers.add_parser('set-governor')
+  set_governor.add_argument(
+      'governor',
+      help='Desired CPU governor.')
+  set_governor.set_defaults(func=SetScalingGovernor)
+
+  get_governor = subparsers.add_parser('get-governor')
+  get_governor.set_defaults(func=GetScalingGovernor)
+
+  list_governors = subparsers.add_parser('list-governors')
+  list_governors.set_defaults(func=ListAvailableGovernors)
+
+  args = parser.parse_args(raw_args)
+
+  run_tests_helper.SetLogLevel(args.verbose)
+
+  devil_dynamic_config = devil_env.EmptyConfig()
+  if args.adb_path:
+    devil_dynamic_config['dependencies'].update(
+        devil_env.LocalConfigItem(
+            'adb', devil_env.GetPlatform(), args.adb_path))
+  devil_env.config.Initialize(configs=[devil_dynamic_config])
+
+  devices = device_utils.DeviceUtils.HealthyDevices(device_arg=args.devices)
+
+  parallel_devices = device_utils.DeviceUtils.parallel(devices)
+  parallel_devices.pMap(args.func, args)
+
+  return 0
+
+
+if __name__ == '__main__':
+  sys.exit(main(sys.argv[1:]))
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/tools/device_monitor.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/tools/device_monitor.py
new file mode 100755
index 0000000..49214a9
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/tools/device_monitor.py
@@ -0,0 +1,231 @@
+#!/usr/bin/env python
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Launches a daemon to monitor android device temperatures & status.
+
+This script will repeatedly poll the given devices for their temperatures and
+status every 60 seconds and dump the stats to file on the host.
+"""
+
+import argparse
+import collections
+import json
+import logging
+import logging.handlers
+import os
+import re
+import socket
+import sys
+import time
+
+if __name__ == '__main__':
+  sys.path.append(
+      os.path.abspath(os.path.join(os.path.dirname(__file__),
+                                   '..', '..', '..')))
+
+from devil import devil_env
+from devil.android import battery_utils
+from devil.android import device_blacklist
+from devil.android import device_errors
+from devil.android import device_utils
+
+
+# Various names of sensors used to measure cpu temp
+CPU_TEMP_SENSORS = [
+  # most nexus devices
+  'tsens_tz_sensor0',
+  # android one
+  'mtktscpu',
+  # nexus 9
+  'CPU-therm',
+]
+
+DEVICE_FILE_VERSION = 1
+# TODO(bpastene): Remove the old file once sysmon has been updated to read the
+# new status file.
+DEVICE_FILES = [
+    os.path.join(os.path.expanduser('~'), 'android_device_status.json'),
+    os.path.join(
+        os.path.expanduser('~'), '.android',
+        '%s__android_device_status.json' % socket.gethostname().split('.')[0]
+    ),
+]
+
+MEM_INFO_REGEX = re.compile(r'.*?\:\s*(\d+)\s*kB') # ex: 'MemTotal:   185735 kB'
+
+
+def get_device_status(device):
+  """Polls the given device for various info.
+
+    Returns: A dict of the following format:
+    {
+      'battery': {
+        'level': 100,
+        'temperature': 123
+      },
+      'build': {
+        'build.id': 'ABC12D',
+        'product.device': 'chickenofthesea'
+      },
+      'mem': {
+        'avail': 1000000,
+        'total': 1234567,
+      },
+      'processes': 123,
+      'state': 'good',
+      'temp': {
+        'some_sensor': 30
+      },
+      'uptime': 1234.56,
+    }
+  """
+  status = collections.defaultdict(dict)
+
+  # Battery
+  battery = battery_utils.BatteryUtils(device)
+  battery_info = battery.GetBatteryInfo()
+  try:
+    level = int(battery_info.get('level'))
+  except (KeyError, TypeError, ValueError):
+    level = None
+  if level and level >= 0 and level <= 100:
+    status['battery']['level'] = level
+  try:
+    temperature = int(battery_info.get('temperature'))
+  except (KeyError, TypeError, ValueError):
+    temperature = None
+  if temperature:
+    status['battery']['temperature'] = temperature
+
+  # Build
+  status['build']['build.id'] = device.build_id
+  status['build']['product.device'] = device.build_product
+
+  # Memory
+  mem_info = ''
+  try:
+    mem_info = device.ReadFile('/proc/meminfo')
+  except device_errors.AdbShellCommandFailedError:
+    logging.exception('Unable to read /proc/meminfo')
+  for line in mem_info.splitlines():
+    match = MEM_INFO_REGEX.match(line)
+    if match:
+      try:
+        value = int(match.group(1))
+      except ValueError:
+        continue
+      key = line.split(':')[0].strip()
+      if 'MemTotal' == key:
+        status['mem']['total'] = value
+      elif 'MemFree' == key:
+        status['mem']['free'] = value
+
+  # Process
+  try:
+    # TODO(catapult:#3215): Migrate to device.GetPids()
+    lines = device.RunShellCommand(['ps'], check_return=True)
+    status['processes'] = len(lines) - 1 # Ignore the header row.
+  except device_errors.AdbShellCommandFailedError:
+    logging.exception('Unable to count process list.')
+
+  # CPU Temps
+  # Find a thermal sensor that matches one in CPU_TEMP_SENSORS and read its
+  # temperature.
+  files = []
+  try:
+    files = device.RunShellCommand(
+        'grep -lE "%s" /sys/class/thermal/thermal_zone*/type' % '|'.join(
+            CPU_TEMP_SENSORS), shell=True, check_return=True)
+  except device_errors.AdbShellCommandFailedError:
+    logging.exception('Unable to list thermal sensors.')
+  for f in files:
+    try:
+      sensor_name = device.ReadFile(f).strip()
+      temp = float(device.ReadFile(f[:-4] + 'temp').strip()) # s/type^/temp
+      status['temp'][sensor_name] = temp
+    except (device_errors.AdbShellCommandFailedError, ValueError):
+      logging.exception('Unable to read thermal sensor %s', f)
+
+  # Uptime
+  try:
+    uptimes = device.ReadFile('/proc/uptime').split()
+    status['uptime'] = float(uptimes[0]) # Take the first field (actual uptime)
+  except (device_errors.AdbShellCommandFailedError, ValueError):
+    logging.exception('Unable to read /proc/uptime')
+
+  status['state'] = 'available'
+  return status
+
+
+def get_all_status(blacklist):
+  status_dict = {
+      'version': DEVICE_FILE_VERSION,
+      'devices': {},
+  }
+
+  healthy_devices = device_utils.DeviceUtils.HealthyDevices(blacklist)
+  parallel_devices = device_utils.DeviceUtils.parallel(healthy_devices)
+  results = parallel_devices.pMap(get_device_status).pGet(None)
+
+  status_dict['devices'] = {
+      device.serial: result for device, result in zip(healthy_devices, results)
+  }
+
+  if blacklist:
+    for device, reason in blacklist.Read().iteritems():
+      status_dict['devices'][device] = {
+          'state': reason.get('reason', 'blacklisted')}
+
+  status_dict['timestamp'] = time.time()
+  return status_dict
+
+
+def main(argv):
+  """Launches the device monitor.
+
+  Polls the devices for their battery and cpu temperatures and scans the
+  blacklist file every 60 seconds and dumps the data to DEVICE_FILE.
+  """
+
+  parser = argparse.ArgumentParser(
+      description='Launches the device monitor.')
+  parser.add_argument('--adb-path', help='Path to adb binary.')
+  parser.add_argument('--blacklist-file', help='Path to device blacklist file.')
+  args = parser.parse_args(argv)
+
+  logger = logging.getLogger()
+  logger.setLevel(logging.DEBUG)
+  handler = logging.handlers.RotatingFileHandler(
+      '/tmp/device_monitor.log', maxBytes=10 * 1024 * 1024, backupCount=5)
+  fmt = logging.Formatter('%(asctime)s %(levelname)s %(message)s',
+                          datefmt='%y%m%d %H:%M:%S')
+  handler.setFormatter(fmt)
+  logger.addHandler(handler)
+
+  devil_dynamic_config = devil_env.EmptyConfig()
+  if args.adb_path:
+    devil_dynamic_config['dependencies'].update(
+        devil_env.LocalConfigItem(
+            'adb', devil_env.GetPlatform(), args.adb_path))
+
+  devil_env.config.Initialize(configs=[devil_dynamic_config])
+
+  blacklist = (device_blacklist.Blacklist(args.blacklist_file)
+               if args.blacklist_file else None)
+
+  logging.info('Device monitor running with pid %d, adb: %s, blacklist: %s',
+               os.getpid(), args.adb_path, args.blacklist_file)
+  while True:
+    start = time.time()
+    status_dict = get_all_status(blacklist)
+    for device_file in DEVICE_FILES:
+      with open(device_file, 'wb') as f:
+        json.dump(status_dict, f, indent=2, sort_keys=True)
+    logging.info('Got status of all devices in %.2fs.', time.time() - start)
+    time.sleep(60)
+
+
+if __name__ == '__main__':
+  sys.exit(main(sys.argv[1:]))
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/tools/device_monitor_test.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/tools/device_monitor_test.py
new file mode 100755
index 0000000..e39e324
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/tools/device_monitor_test.py
@@ -0,0 +1,168 @@
+#!/usr/bin/env python
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import sys
+import unittest
+
+if __name__ == '__main__':
+  sys.path.append(
+        os.path.abspath(os.path.join(os.path.dirname(__file__),
+                                           '..', '..', '..')))
+
+from devil import devil_env
+from devil.android import device_errors
+from devil.android import device_utils
+from devil.android.tools import device_monitor
+
+with devil_env.SysPath(devil_env.PYMOCK_PATH):
+  import mock  # pylint: disable=import-error
+
+
+class DeviceMonitorTest(unittest.TestCase):
+
+  def setUp(self):
+    self.device = mock.Mock(spec=device_utils.DeviceUtils,
+        serial='device_cereal', build_id='abc123', build_product='clownfish')
+    self.file_contents = {
+        '/proc/meminfo': """
+                         MemTotal:        1234567 kB
+                         MemFree:         1000000 kB
+                         MemUsed:          234567 kB
+                         """,
+        '/sys/class/thermal/thermal_zone0/type': 'CPU-therm',
+        '/sys/class/thermal/thermal_zone0/temp': '30',
+        '/proc/uptime': '12345 99999',
+    }
+    self.device.ReadFile = mock.MagicMock(
+        side_effect=lambda file_name: self.file_contents[file_name])
+
+    self.cmd_outputs = {
+        'ps': ['headers', 'p1', 'p2', 'p3', 'p4', 'p5'],
+        'grep': ['/sys/class/thermal/thermal_zone0/type'],
+    }
+
+    def mock_run_shell(cmd, **_kwargs):
+      args = cmd.split() if isinstance(cmd, basestring) else cmd
+      try:
+        return self.cmd_outputs[args[0]]
+      except KeyError:
+        raise device_errors.AdbShellCommandFailedError(cmd, None, None)
+
+    self.device.RunShellCommand = mock.MagicMock(side_effect=mock_run_shell)
+
+    self.battery = mock.Mock()
+    self.battery.GetBatteryInfo = mock.MagicMock(
+        return_value={'level': '80', 'temperature': '123'})
+
+    self.expected_status = {
+      'device_cereal': {
+        'processes': 5,
+        'temp': {
+          'CPU-therm': 30.0
+         },
+         'battery': {
+           'temperature': 123,
+           'level': 80
+         },
+         'uptime': 12345.0,
+         'mem': {
+           'total': 1234567,
+           'free': 1000000
+         },
+         'build': {
+           'build.id': 'abc123',
+           'product.device': 'clownfish',
+         },
+         'state': 'available',
+      }
+    }
+
+  @mock.patch('devil.android.battery_utils.BatteryUtils')
+  @mock.patch('devil.android.device_utils.DeviceUtils.HealthyDevices')
+  def test_getStats(self, get_devices, get_battery):
+    get_devices.return_value = [self.device]
+    get_battery.return_value = self.battery
+
+    status = device_monitor.get_all_status(None)
+    self.assertEquals(self.expected_status, status['devices'])
+
+  @mock.patch('devil.android.battery_utils.BatteryUtils')
+  @mock.patch('devil.android.device_utils.DeviceUtils.HealthyDevices')
+  def test_getStatsNoBattery(self, get_devices, get_battery):
+    get_devices.return_value = [self.device]
+    get_battery.return_value = self.battery
+    broken_battery_info = mock.Mock()
+    broken_battery_info.GetBatteryInfo = mock.MagicMock(
+        return_value={'level': '-1', 'temperature': 'not_a_number'})
+    get_battery.return_value = broken_battery_info
+
+    # Should be same status dict but without battery stats.
+    expected_status_no_battery = self.expected_status.copy()
+    expected_status_no_battery['device_cereal'].pop('battery')
+
+    status = device_monitor.get_all_status(None)
+    self.assertEquals(expected_status_no_battery, status['devices'])
+
+  @mock.patch('devil.android.battery_utils.BatteryUtils')
+  @mock.patch('devil.android.device_utils.DeviceUtils.HealthyDevices')
+  def test_getStatsNoPs(self, get_devices, get_battery):
+    get_devices.return_value = [self.device]
+    get_battery.return_value = self.battery
+    del self.cmd_outputs['ps']  # Throw exception on run shell ps command.
+
+    # Should be same status dict but without process stats.
+    expected_status_no_ps = self.expected_status.copy()
+    expected_status_no_ps['device_cereal'].pop('processes')
+
+    status = device_monitor.get_all_status(None)
+    self.assertEquals(expected_status_no_ps, status['devices'])
+
+  @mock.patch('devil.android.battery_utils.BatteryUtils')
+  @mock.patch('devil.android.device_utils.DeviceUtils.HealthyDevices')
+  def test_getStatsNoSensors(self, get_devices, get_battery):
+    get_devices.return_value = [self.device]
+    get_battery.return_value = self.battery
+    del self.cmd_outputs['grep']  # Throw exception on run shell grep command.
+
+    # Should be same status dict but without temp stats.
+    expected_status_no_temp = self.expected_status.copy()
+    expected_status_no_temp['device_cereal'].pop('temp')
+
+    status = device_monitor.get_all_status(None)
+    self.assertEquals(expected_status_no_temp, status['devices'])
+
+  @mock.patch('devil.android.battery_utils.BatteryUtils')
+  @mock.patch('devil.android.device_utils.DeviceUtils.HealthyDevices')
+  def test_getStatsWithBlacklist(self, get_devices, get_battery):
+    get_devices.return_value = [self.device]
+    get_battery.return_value = self.battery
+    blacklist = mock.Mock()
+    blacklist.Read = mock.MagicMock(
+        return_value={'bad_device': {'reason': 'offline'}})
+
+    # Should be same status dict but with extra blacklisted device.
+    expected_status = self.expected_status.copy()
+    expected_status['bad_device'] = {'state': 'offline'}
+
+    status = device_monitor.get_all_status(blacklist)
+    self.assertEquals(expected_status, status['devices'])
+
+  @mock.patch('devil.android.battery_utils.BatteryUtils')
+  @mock.patch('devil.android.device_utils.DeviceUtils.HealthyDevices')
+  def test_brokenTempValue(self, get_devices, get_battery):
+    self.file_contents['/sys/class/thermal/thermal_zone0/temp'] = 'n0t a numb3r'
+    get_devices.return_value = [self.device]
+    get_battery.return_value = self.battery
+
+    expected_status_no_temp = self.expected_status.copy()
+    expected_status_no_temp['device_cereal'].pop('temp')
+
+    status = device_monitor.get_all_status(None)
+    self.assertEquals(self.expected_status, status['devices'])
+
+
+if __name__ == '__main__':
+  sys.exit(unittest.main())
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/tools/device_recovery.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/tools/device_recovery.py
index a7844e2..57857b1 100755
--- a/sdk/platform-tools/systrace/catapult/devil/devil/android/tools/device_recovery.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/tools/device_recovery.py
@@ -26,6 +26,8 @@
 from devil.utils import reset_usb  # pylint: disable=unused-import
 from devil.utils import run_tests_helper
 
+logger = logging.getLogger(__name__)
+
 
 def KillAllAdb():
   def get_all_adb():
@@ -39,15 +41,15 @@
   for sig in [signal.SIGTERM, signal.SIGQUIT, signal.SIGKILL]:
     for p in get_all_adb():
       try:
-        logging.info('kill %d %d (%s [%s])', sig, p.pid, p.name,
-                     ' '.join(p.cmdline))
+        logger.info('kill %d %d (%s [%s])', sig, p.pid, p.name,
+                    ' '.join(p.cmdline))
         p.send_signal(sig)
       except (psutil.NoSuchProcess, psutil.AccessDenied):
         pass
   for p in get_all_adb():
     try:
-      logging.error('Unable to kill %d (%s [%s])', p.pid, p.name,
-                    ' '.join(p.cmdline))
+      logger.error('Unable to kill %d (%s [%s])', p.pid, p.name,
+                   ' '.join(p.cmdline))
     except (psutil.NoSuchProcess, psutil.AccessDenied):
       pass
 
@@ -55,7 +57,7 @@
 def RecoverDevice(device, blacklist, should_reboot=lambda device: True):
   if device_status.IsBlacklisted(device.adb.GetDeviceSerial(),
                                  blacklist):
-    logging.debug('%s is blacklisted, skipping recovery.', str(device))
+    logger.debug('%s is blacklisted, skipping recovery.', str(device))
     return
 
   if should_reboot(device):
@@ -63,26 +65,31 @@
       device.WaitUntilFullyBooted(retries=0)
     except (device_errors.CommandTimeoutError,
             device_errors.CommandFailedError):
-      logging.exception('Failure while waiting for %s. '
-                        'Attempting to recover.', str(device))
+      logger.exception('Failure while waiting for %s. '
+                       'Attempting to recover.', str(device))
     try:
       try:
         device.Reboot(block=False, timeout=5, retries=0)
       except device_errors.CommandTimeoutError:
-        logging.warning('Timed out while attempting to reboot %s normally.'
-                        'Attempting alternative reboot.', str(device))
+        logger.warning('Timed out while attempting to reboot %s normally.'
+                       'Attempting alternative reboot.', str(device))
         # The device drops offline before we can grab the exit code, so
         # we don't check for status.
-        device.adb.Root()
-        device.adb.Shell('echo b > /proc/sysrq-trigger', expect_status=None,
-                         timeout=5, retries=0)
+        try:
+          device.adb.Root()
+        finally:
+          # We are already in a failure mode, attempt to reboot regardless of
+          # what device.adb.Root() returns. If the sysrq reboot fails an
+          # exception willbe thrown at that level.
+          device.adb.Shell('echo b > /proc/sysrq-trigger', expect_status=None,
+                           timeout=5, retries=0)
     except device_errors.CommandFailedError:
-      logging.exception('Failed to reboot %s.', str(device))
+      logger.exception('Failed to reboot %s.', str(device))
       if blacklist:
         blacklist.Extend([device.adb.GetDeviceSerial()],
                          reason='reboot_failure')
     except device_errors.CommandTimeoutError:
-      logging.exception('Timed out while rebooting %s.', str(device))
+      logger.exception('Timed out while rebooting %s.', str(device))
       if blacklist:
         blacklist.Extend([device.adb.GetDeviceSerial()],
                          reason='reboot_timeout')
@@ -91,12 +98,12 @@
       device.WaitUntilFullyBooted(
           retries=0, timeout=device.REBOOT_DEFAULT_TIMEOUT)
     except device_errors.CommandFailedError:
-      logging.exception('Failure while waiting for %s.', str(device))
+      logger.exception('Failure while waiting for %s.', str(device))
       if blacklist:
         blacklist.Extend([device.adb.GetDeviceSerial()],
                          reason='reboot_failure')
     except device_errors.CommandTimeoutError:
-      logging.exception('Timed out while waiting for %s.', str(device))
+      logger.exception('Timed out while waiting for %s.', str(device))
       if blacklist:
         blacklist.Extend([device.adb.GetDeviceSerial()],
                          reason='reboot_timeout')
@@ -124,15 +131,15 @@
       status['serial'] for status in statuses
       if status['blacklisted']))
 
-  logging.debug('Should restart USB for:')
+  logger.debug('Should restart USB for:')
   for d in should_restart_usb:
-    logging.debug('  %s', d)
-  logging.debug('Should restart ADB for:')
+    logger.debug('  %s', d)
+  logger.debug('Should restart ADB for:')
   for d in should_restart_adb:
-    logging.debug('  %s', d)
-  logging.debug('Should reboot:')
+    logger.debug('  %s', d)
+  logger.debug('Should reboot:')
   for d in should_reboot_device:
-    logging.debug('  %s', d)
+    logger.debug('  %s', d)
 
   if blacklist:
     blacklist.Reset()
@@ -146,20 +153,20 @@
       if enable_usb_reset:
         reset_usb.reset_android_usb(serial)
       else:
-        logging.warning('USB reset disabled for %s (crbug.com/642914)',
-                        serial)
+        logger.warning('USB reset disabled for %s (crbug.com/642914)',
+                       serial)
     except IOError:
-      logging.exception('Unable to reset USB for %s.', serial)
+      logger.exception('Unable to reset USB for %s.', serial)
       if blacklist:
         blacklist.Extend([serial], reason='USB failure')
     except device_errors.DeviceUnreachableError:
-      logging.exception('Unable to reset USB for %s.', serial)
+      logger.exception('Unable to reset USB for %s.', serial)
       if blacklist:
         blacklist.Extend([serial], reason='offline')
 
   device_utils.DeviceUtils.parallel(devices).pMap(
       RecoverDevice, blacklist,
-      should_reboot=lambda device: device in should_reboot_device)
+      should_reboot=lambda device: device.serial in should_reboot_device)
 
 
 def main():
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/tools/device_status.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/tools/device_status.py
index dbfccb6..167d66c 100755
--- a/sdk/platform-tools/systrace/catapult/devil/devil/android/tools/device_status.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/tools/device_status.py
@@ -27,6 +27,8 @@
 from devil.utils import lsusb
 from devil.utils import run_tests_helper
 
+logger = logging.getLogger(__name__)
+
 _RE_DEVICE_ID = re.compile(r'Device ID = (\d+)')
 
 
@@ -42,7 +44,7 @@
     battery_level = int(battery_info.get('level', 100))
 
     if battery_level < 15:
-      logging.error('Critically low battery level (%d)', battery_level)
+      logger.error('Critically low battery level (%d)', battery_level)
       battery = battery_utils.BatteryUtils(device)
       if not battery.GetCharging():
         battery.SetCharging(True)
@@ -50,8 +52,8 @@
         blacklist.Extend([device.adb.GetDeviceSerial()], reason='low_battery')
 
   except device_errors.CommandFailedError:
-    logging.exception('Failed to get battery information for %s',
-                      str(device))
+    logger.exception('Failed to get battery information for %s',
+                     str(device))
 
   return battery_info
 
@@ -65,7 +67,7 @@
       if m:
         imei_slice = m.group(1)[-6:]
   except device_errors.CommandFailedError:
-    logging.exception('Failed to get IMEI slice for %s', str(device))
+    logger.exception('Failed to get IMEI slice for %s', str(device))
 
   return imei_slice
 
@@ -129,7 +131,7 @@
 
           if (device.product_name == 'mantaray' and
               battery_info.get('AC powered', None) != 'true'):
-            logging.error('Mantaray device not connected to AC power.')
+            logger.error('Mantaray device not connected to AC power.')
 
           device_status.update({
             'ro.build.product': build_product,
@@ -142,14 +144,14 @@
           })
 
         except device_errors.CommandFailedError:
-          logging.exception('Failure while getting device status for %s.',
-                            str(device))
+          logger.exception('Failure while getting device status for %s.',
+                           str(device))
           if blacklist:
             blacklist.Extend([serial], reason='status_check_failure')
 
         except device_errors.CommandTimeoutError:
-          logging.exception('Timeout while getting device status for %s.',
-                            str(device))
+          logger.exception('Timeout while getting device status for %s.',
+                           str(device))
           if blacklist:
             blacklist.Extend([serial], reason='status_check_timeout')
 
@@ -169,23 +171,23 @@
 def _LogStatuses(statuses):
   # Log the state of all devices.
   for status in statuses:
-    logging.info(status['serial'])
+    logger.info(status['serial'])
     adb_status = status.get('adb_status')
     blacklisted = status.get('blacklisted')
-    logging.info('  USB status: %s',
-                 'online' if status.get('usb_status') else 'offline')
-    logging.info('  ADB status: %s', adb_status)
-    logging.info('  Blacklisted: %s', str(blacklisted))
+    logger.info('  USB status: %s',
+                'online' if status.get('usb_status') else 'offline')
+    logger.info('  ADB status: %s', adb_status)
+    logger.info('  Blacklisted: %s', str(blacklisted))
     if adb_status == 'device' and not blacklisted:
-      logging.info('  Device type: %s', status.get('ro.build.product'))
-      logging.info('  OS build: %s', status.get('ro.build.id'))
-      logging.info('  OS build fingerprint: %s',
-                   status.get('ro.build.fingerprint'))
-      logging.info('  Battery state:')
+      logger.info('  Device type: %s', status.get('ro.build.product'))
+      logger.info('  OS build: %s', status.get('ro.build.id'))
+      logger.info('  OS build fingerprint: %s',
+                  status.get('ro.build.fingerprint'))
+      logger.info('  Battery state:')
       for k, v in status.get('battery', {}).iteritems():
-        logging.info('    %s: %s', k, v)
-      logging.info('  IMEI slice: %s', status.get('imei_slice'))
-      logging.info('  WiFi IP: %s', status.get('wifi_ip'))
+        logger.info('    %s: %s', k, v)
+      logger.info('  IMEI slice: %s', status.get('imei_slice'))
+      logger.info('  WiFi IP: %s', status.get('wifi_ip'))
 
 
 def _WriteBuildbotFile(file_path, statuses):
@@ -224,13 +226,13 @@
       if os.path.exists(path):
         expected_devices.update(device_list.GetPersistentDeviceList(path))
       else:
-        logging.warning('Could not find known devices file: %s', path)
+        logger.warning('Could not find known devices file: %s', path)
   except IOError:
-    logging.warning('Problem reading %s, skipping.', path)
+    logger.warning('Problem reading %s, skipping.', path)
 
-  logging.info('Expected devices:')
+  logger.info('Expected devices:')
   for device in expected_devices:
-    logging.info('  %s', device)
+    logger.info('  %s', device)
   return expected_devices
 
 
@@ -303,7 +305,7 @@
 
   # If all devices failed, or if there are no devices, it's an infra error.
   if not live_devices:
-    logging.error('No available devices.')
+    logger.error('No available devices.')
   return 0 if live_devices else exit_codes.INFRA
 
 
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/tools/flash_device.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/tools/flash_device.py
index 50ed696..d13c1df 100755
--- a/sdk/platform-tools/systrace/catapult/devil/devil/android/tools/flash_device.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/tools/flash_device.py
@@ -18,6 +18,8 @@
 from devil.constants import exit_codes
 from devil.utils import run_tests_helper
 
+logger = logging.getLogger(__name__)
+
 
 def main():
   parser = argparse.ArgumentParser()
@@ -35,9 +37,9 @@
   if args.blacklist_file:
     blacklist = device_blacklist.Blacklist(args.blacklist_file).Read()
     if blacklist:
-      logging.critical('Device(s) in blacklist, not flashing devices:')
+      logger.critical('Device(s) in blacklist, not flashing devices:')
       for key in blacklist:
-        logging.critical('  %s', key)
+        logger.critical('  %s', key)
       return exit_codes.INFRA
 
   flashed_devices = []
@@ -49,18 +51,18 @@
       fastboot.FlashDevice(args.build_path, wipe=args.wipe)
       flashed_devices.append(device)
     except Exception:  # pylint: disable=broad-except
-      logging.exception('Device %s failed to flash.', str(device))
+      logger.exception('Device %s failed to flash.', str(device))
       failed_devices.append(device)
 
   devices = script_common.GetDevices(args.devices, args.blacklist_file)
   device_utils.DeviceUtils.parallel(devices).pMap(flash)
 
   if flashed_devices:
-    logging.info('The following devices were flashed:')
-    logging.info('  %s', ' '.join(str(d) for d in flashed_devices))
+    logger.info('The following devices were flashed:')
+    logger.info('  %s', ' '.join(str(d) for d in flashed_devices))
   if failed_devices:
-    logging.critical('The following devices failed to flash:')
-    logging.critical('  %s', ' '.join(str(d) for d in failed_devices))
+    logger.critical('The following devices failed to flash:')
+    logger.critical('  %s', ' '.join(str(d) for d in failed_devices))
     return exit_codes.INFRA
   return 0
 
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/tools/provision_devices.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/tools/provision_devices.py
index d9bff5d..7374290 100755
--- a/sdk/platform-tools/systrace/catapult/devil/devil/android/tools/provision_devices.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/tools/provision_devices.py
@@ -39,6 +39,7 @@
 from devil.android import settings
 from devil.android.constants import chrome
 from devil.android.sdk import adb_wrapper
+from devil.android.sdk import intent
 from devil.android.sdk import keyevent
 from devil.android.sdk import version_codes
 from devil.android.tools import script_common
@@ -46,7 +47,10 @@
 from devil.utils import run_tests_helper
 from devil.utils import timeout_retry
 
-_SYSTEM_WEBVIEW_PATHS = ['/system/app/webview', '/system/app/WebViewGoogle']
+logger = logging.getLogger(__name__)
+
+_SYSTEM_APP_DIRECTORIES = ['/system/app/', '/system/priv-app/']
+_SYSTEM_WEBVIEW_NAMES = ['webview', 'WebViewGoogle']
 _CHROME_PACKAGE_REGEX = re.compile('.*chrom.*')
 _TOMBSTONE_REGEX = re.compile('tombstone.*')
 
@@ -81,11 +85,19 @@
     output_device_blacklist=None,
     reboot_timeout=None,
     remove_system_webview=False,
+    system_app_remove_list=None,
     wipe=True):
   blacklist = (device_blacklist.Blacklist(blacklist_file)
                if blacklist_file
                else None)
-  devices = script_common.GetDevices(devices, blacklist)
+  system_app_remove_list = system_app_remove_list or []
+  try:
+    devices = script_common.GetDevices(devices, blacklist)
+  except device_errors.NoDevicesError:
+    logging.error('No available devices to provision.')
+    if blacklist:
+      logging.error('Local device blacklist: %s', blacklist.Read())
+    raise
   devices = [d for d in devices
              if not emulators or d.adb.is_emulator]
   parallel_devices = device_utils.DeviceUtils.parallel(devices)
@@ -113,7 +125,11 @@
         lambda d: WaitForCharge(d, min_battery_level)))
 
   if remove_system_webview:
-    steps.append(ProvisionStep(RemoveSystemWebView))
+    system_app_remove_list.extend(_SYSTEM_WEBVIEW_NAMES)
+
+  if system_app_remove_list:
+    steps.append(ProvisionStep(
+        lambda d: RemoveSystemApps(d, system_app_remove_list)))
 
   steps.append(ProvisionStep(SetDate))
   steps.append(ProvisionStep(CheckExternalStorage))
@@ -141,7 +157,7 @@
       try:
         device.WaitUntilFullyBooted(timeout=reboot_timeout, retries=0)
       except device_errors.CommandTimeoutError:
-        logging.error('Device did not finish booting. Will try to reboot.')
+        logger.error('Device did not finish booting. Will try to reboot.')
         device.Reboot(timeout=reboot_timeout)
       step.cmd(device)
       if step.reboot:
@@ -149,14 +165,14 @@
         device.adb.WaitForDevice()
 
   except device_errors.CommandTimeoutError:
-    logging.exception('Timed out waiting for device %s. Adding to blacklist.',
-                      str(device))
+    logger.exception('Timed out waiting for device %s. Adding to blacklist.',
+                     str(device))
     if blacklist:
       blacklist.Extend([str(device)], reason='provision_timeout')
 
   except device_errors.CommandFailedError:
-    logging.exception('Failed to provision device %s. Adding to blacklist.',
-                      str(device))
+    logger.exception('Failed to provision device %s. Adding to blacklist.',
+                     str(device))
     if blacklist:
       blacklist.Extend([str(device)], reason='provision_failure')
 
@@ -168,7 +184,7 @@
 
     package = "com.google.android.gms"
     version_name = device.GetApplicationVersion(package)
-    logging.info("Version name for %s is %s", package, version_name)
+    logger.info("Version name for %s is %s", package, version_name)
   else:
     WipeDevice(device, adb_key_files)
 
@@ -195,8 +211,9 @@
       _UninstallIfMatch(device, _CHROME_PACKAGE_REGEX,
                         chrome.PACKAGE_INFO['chrome_stable'].package)
       device.RunShellCommand('rm -rf %s/*' % device.GetExternalStoragePath(),
-                             check_return=True)
-      device.RunShellCommand('rm -rf /data/local/tmp/*', check_return=True)
+                             shell=True, check_return=True)
+      device.RunShellCommand('rm -rf /data/local/tmp/*',
+                             shell=True, check_return=True)
     else:
       device.EnableRoot()
       _UninstallIfMatch(device, _CHROME_PACKAGE_REGEX,
@@ -209,17 +226,18 @@
       _WipeFileOrDir(device, '/data/local/.config/')
       _WipeFileOrDir(device, '/data/local/tmp/')
       device.RunShellCommand('rm -rf %s/*' % device.GetExternalStoragePath(),
-                             check_return=True)
+                             shell=True, check_return=True)
   except device_errors.CommandFailedError:
-    logging.exception('Possible failure while wiping the device. '
-                      'Attempting to continue.')
+    logger.exception('Possible failure while wiping the device. '
+                     'Attempting to continue.')
 
 
 def _UninstallIfMatch(device, pattern, app_to_keep):
-  installed_packages = device.RunShellCommand(['pm', 'list', 'packages'])
+  installed_packages = device.RunShellCommand(
+      ['pm', 'list', 'packages'], check_return=True)
   installed_system_packages = [
-      pkg.split(':')[1] for pkg in device.RunShellCommand(['pm', 'list',
-                                                           'packages', '-s'])]
+      pkg.split(':')[1] for pkg in device.RunShellCommand(
+          ['pm', 'list', 'packages', '-s'], check_return=True)]
   for package_output in installed_packages:
     package = package_output.split(":")[1]
     if pattern.match(package) and not package == app_to_keep:
@@ -268,11 +286,11 @@
             adb_public_keys = f.readlines()
           adb_keys_set.update(adb_public_keys)
         except IOError:
-          logging.warning('Unable to find adb keys file %s.', adb_key_file)
+          logger.warning('Unable to find adb keys file %s.', adb_key_file)
       _WriteAdbKeysFile(device, '\n'.join(adb_keys_set))
   except device_errors.CommandFailedError:
-    logging.exception('Possible failure while wiping the device. '
-                      'Attempting to continue.')
+    logger.exception('Possible failure while wiping the device. '
+                     'Attempting to continue.')
 
 
 def _WriteAdbKeysFile(device, adb_keys_string):
@@ -291,12 +309,12 @@
   try:
     device.EnableRoot()
   except device_errors.CommandFailedError as e:
-    logging.warning(str(e))
+    logger.warning(str(e))
 
   if not device.IsUserBuild():
     _ConfigureLocalProperties(device, enable_java_debug)
   else:
-    logging.warning('Cannot configure properties in user builds.')
+    logger.warning('Cannot configure properties in user builds.')
   settings.ConfigureContentSettings(
       device, settings.DETERMINISTIC_DEVICE_SETTINGS)
   if disable_location:
@@ -334,28 +352,43 @@
                          check_return=True)
 
 
-def RemoveSystemWebView(device):
-  if any(device.PathExists(p) for p in _SYSTEM_WEBVIEW_PATHS):
-    logging.info('System WebView exists and needs to be removed')
-    if device.HasRoot():
-      # Disabled Marshmallow's Verity security feature
-      if device.build_version_sdk >= version_codes.MARSHMALLOW:
-        device.adb.DisableVerity()
-        device.Reboot()
-        device.WaitUntilFullyBooted()
-        device.EnableRoot()
+def _RemoveSystemApp(device, system_app):
+  found_paths = []
+  for directory in _SYSTEM_APP_DIRECTORIES:
+    path = os.path.join(directory, system_app)
+    if device.PathExists(path):
+      found_paths.append(path)
+  if not found_paths:
+    logger.warning('Could not find install location for system app %s',
+                   system_app)
+  device.RemovePath(found_paths, force=True, recursive=True)
 
-      # This is required, e.g., to replace the system webview on a device.
-      device.adb.Remount()
-      device.RunShellCommand(['stop'], check_return=True)
-      device.RunShellCommand(['rm', '-rf'] + _SYSTEM_WEBVIEW_PATHS,
-                             check_return=True)
-      device.RunShellCommand(['start'], check_return=True)
-    else:
-      logging.warning('Cannot remove system webview from a non-rooted device')
+def RemoveSystemApps(device, system_app_remove_list):
+  """Attempts to remove the provided system apps from the given device.
+
+  Arguments:
+    device: The device to remove the system apps from.
+    system_app_remove_list: A list of app names to remove, e.g.
+        ['WebViewGoogle', 'GoogleVrCore']
+  """
+  device.EnableRoot()
+  if device.HasRoot():
+    # Disable Marshmallow's Verity security feature
+    if device.build_version_sdk >= version_codes.MARSHMALLOW:
+      logger.info('Disabling Verity on %s', device.serial)
+      device.adb.DisableVerity()
+      device.Reboot()
+      device.WaitUntilFullyBooted()
+      device.EnableRoot()
+
+    device.adb.Remount()
+    device.RunShellCommand(['stop'], check_return=True)
+    for system_app in system_app_remove_list:
+      _RemoveSystemApp(device, system_app)
+    device.RunShellCommand(['start'], check_return=True)
   else:
-    logging.info('System WebView already removed')
-
+    raise device_errors.CommandFailedError(
+        'Failed to remove system apps from non-rooted device', str(device))
 
 
 def _ConfigureLocalProperties(device, java_debug=True):
@@ -380,7 +413,7 @@
         ['chmod', '644', device.LOCAL_PROPERTIES_PATH],
         as_root=True, check_return=True)
   except device_errors.CommandFailedError:
-    logging.exception('Failed to configure local properties.')
+    logger.exception('Failed to configure local properties.')
 
 
 def FinishProvisioning(device):
@@ -404,7 +437,7 @@
     battery = battery_utils.BatteryUtils(device)
     battery.LetBatteryCoolToTemperature(max_battery_temp)
   except device_errors.CommandFailedError:
-    logging.exception('Unable to let battery cool to specified temperature.')
+    logger.exception('Unable to let battery cool to specified temperature.')
 
 
 def SetDate(device):
@@ -426,16 +459,17 @@
 
     get_date_command.append('+"%Y%m%d.%H%M%S"')
     device_time = device.RunShellCommand(
-        get_date_command, as_root=True, single_line=True).replace('"', '')
+        get_date_command, check_return=True,
+        as_root=True, single_line=True).replace('"', '')
     device_time = datetime.datetime.strptime(device_time, "%Y%m%d.%H%M%S")
     correct_time = datetime.datetime.strptime(strgmtime, date_format)
     tdelta = (correct_time - device_time).seconds
     if tdelta <= 1:
-      logging.info('Date/time successfully set on %s', device)
+      logger.info('Date/time successfully set on %s', device)
       return True
     else:
-      logging.error('Date mismatch. Device: %s Correct: %s',
-                    device_time.isoformat(), correct_time.isoformat())
+      logger.error('Date mismatch. Device: %s Correct: %s',
+                   device_time.isoformat(), correct_time.isoformat())
       return False
 
   # Sometimes the date is not set correctly on the devices. Retry on failure.
@@ -447,12 +481,15 @@
         _set_and_verify_date, wait_period=1, max_tries=2):
       raise device_errors.CommandFailedError(
           'Failed to set date & time.', device_serial=str(device))
+    device.EnableRoot()
+    device.BroadcastIntent(
+        intent.Intent(action='android.intent.action.TIME_SET'))
 
 
 def LogDeviceProperties(device):
-  props = device.RunShellCommand('getprop', check_return=True)
+  props = device.RunShellCommand(['getprop'], check_return=True)
   for prop in props:
-    logging.info('  %s', prop)
+    logger.info('  %s', prop)
 
 
 def CheckExternalStorage(device):
@@ -466,7 +503,7 @@
         device.adb, suffix='.sh', dir=device.GetExternalStoragePath()) as f:
       device.WriteFile(f.name, 'test')
   except device_errors.CommandFailedError:
-    logging.info('External storage not writable. Remounting / as RW')
+    logger.info('External storage not writable. Remounting / as RW')
     device.RunShellCommand(['mount', '-o', 'remount,rw', '/'],
                            check_return=True, as_root=True)
     device.EnableRoot()
@@ -488,25 +525,16 @@
   parser = argparse.ArgumentParser(
       description='Provision Android devices with settings required for bots.')
   parser.add_argument(
-      '-d', '--device', metavar='SERIAL', action='append', dest='devices',
-      help='the serial number of the device to be provisioned '
-           '(the default is to provision all devices attached)')
+      '--adb-key-files', type=str, nargs='+',
+      help='list of adb keys to push to device')
   parser.add_argument(
       '--adb-path',
       help='Absolute path to the adb binary to use.')
   parser.add_argument('--blacklist-file', help='Device blacklist JSON file.')
   parser.add_argument(
-      '--skip-wipe', action='store_true', default=False,
-      help="don't wipe device data during provisioning")
-  parser.add_argument(
-      '--reboot-timeout', metavar='SECS', type=int,
-      help='when wiping the device, max number of seconds to'
-           ' wait after each reboot '
-           '(default: %s)' % _DEFAULT_TIMEOUTS.HELP_TEXT)
-  parser.add_argument(
-      '--min-battery-level', type=int, metavar='NUM',
-      help='wait for the device to reach this minimum battery'
-           ' level before trying to continue')
+      '-d', '--device', metavar='SERIAL', action='append', dest='devices',
+      help='the serial number of the device to be provisioned '
+           '(the default is to provision all devices attached)')
   parser.add_argument(
       '--disable-location', action='store_true',
       help='disable Google location services on devices')
@@ -524,23 +552,52 @@
       '--disable-system-chrome', action='store_true',
       help='Disable the system chrome from devices.')
   parser.add_argument(
-      '--remove-system-webview', action='store_true',
-      help='Remove the system webview from devices.')
-  parser.add_argument(
-      '--adb-key-files', type=str, nargs='+',
-      help='list of adb keys to push to device')
-  parser.add_argument(
-      '-v', '--verbose', action='count', default=1,
-      help='Log more information.')
+      '--emulators', action='store_true',
+      help='provision only emulators and ignore usb devices '
+           '(this will not wipe emulators)')
   parser.add_argument(
       '--max-battery-temp', type=int, metavar='NUM',
       help='Wait for the battery to have this temp or lower.')
   parser.add_argument(
+      '--min-battery-level', type=int, metavar='NUM',
+      help='wait for the device to reach this minimum battery'
+           ' level before trying to continue')
+  parser.add_argument(
       '--output-device-blacklist',
       help='Json file to output the device blacklist.')
   parser.add_argument(
-      '--emulators', action='store_true',
-      help='provision only emulators and ignore usb devices')
+      '--reboot-timeout', metavar='SECS', type=int,
+      help='when wiping the device, max number of seconds to'
+           ' wait after each reboot '
+           '(default: %s)' % _DEFAULT_TIMEOUTS.HELP_TEXT)
+  parser.add_argument(
+      '--remove-system-apps', nargs='*', dest='system_app_remove_list',
+      help='the names of system apps to remove')
+  parser.add_argument(
+      '--remove-system-webview', action='store_true',
+      help='Remove the system webview from devices.')
+  parser.add_argument(
+      '--skip-wipe', action='store_true', default=False,
+      help='do not wipe device data during provisioning')
+  parser.add_argument(
+      '-v', '--verbose', action='count', default=1,
+      help='Log more information.')
+
+  # No-op arguments for compatibility with build/android/provision_devices.py.
+  # TODO(jbudorick): Remove these once all callers have stopped using them.
+  parser.add_argument(
+      '--chrome-specific-wipe', action='store_true',
+      help=argparse.SUPPRESS)
+  parser.add_argument(
+      '--phase', action='append',
+      help=argparse.SUPPRESS)
+  parser.add_argument(
+      '-r', '--auto-reconnect', action='store_true',
+      help=argparse.SUPPRESS)
+  parser.add_argument(
+      '-t', '--target',
+      help=argparse.SUPPRESS)
+
   args = parser.parse_args(raw_args)
 
   run_tests_helper.SetLogLevel(args.verbose)
@@ -569,8 +626,10 @@
         output_device_blacklist=args.output_device_blacklist,
         reboot_timeout=args.reboot_timeout,
         remove_system_webview=args.remove_system_webview,
-        wipe=not args.skip_wipe)
+        system_app_remove_list=args.system_app_remove_list,
+        wipe=not args.skip_wipe and not args.emulators)
   except (device_errors.DeviceUnreachableError, device_errors.NoDevicesError):
+    logging.exception('Unable to provision local devices.')
     return exit_codes.INFRA
 
 
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/tools/screenshot.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/tools/screenshot.py
index 326bb16..a264c4f 100755
--- a/sdk/platform-tools/systrace/catapult/devil/devil/android/tools/screenshot.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/tools/screenshot.py
@@ -16,6 +16,8 @@
 from devil.android import device_utils
 from devil.android.tools import script_common
 
+logger = logging.getLogger(__name__)
+
 
 def main():
   # Parse options.
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/tools/script_common.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/tools/script_common.py
index 8e462c3..f91ad5e 100644
--- a/sdk/platform-tools/systrace/catapult/devil/devil/android/tools/script_common.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/tools/script_common.py
@@ -9,11 +9,11 @@
 
 def GetDevices(requested_devices, blacklist_file):
   if not isinstance(blacklist_file, device_blacklist.Blacklist):
-    blacklist = (device_blacklist.Blacklist(blacklist_file)
-                 if blacklist_file
-                 else None)
+    blacklist_file = (device_blacklist.Blacklist(blacklist_file)
+                      if blacklist_file
+                      else None)
 
-  devices = device_utils.DeviceUtils.HealthyDevices(blacklist)
+  devices = device_utils.DeviceUtils.HealthyDevices(blacklist_file)
   if not devices:
     raise device_errors.NoDevicesError()
   elif requested_devices:
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/tools/video_recorder.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/tools/video_recorder.py
index bcc9a75..a91e649 100755
--- a/sdk/platform-tools/systrace/catapult/devil/devil/android/tools/video_recorder.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/tools/video_recorder.py
@@ -22,6 +22,8 @@
 from devil.utils import reraiser_thread
 from devil.utils import timeout_retry
 
+logger = logging.getLogger(__name__)
+
 
 class VideoRecorder(object):
   """Records a screen capture video from an Android Device (KitKat or newer)."""
@@ -85,7 +87,7 @@
     """Stop recording video."""
     if not self._device.KillAll('screenrecord', signum=device_signal.SIGINT,
                                 quiet=True):
-      logging.warning('Nothing to kill: screenrecord was not running')
+      logger.warning('Nothing to kill: screenrecord was not running')
     self._recorder_thread.join()
 
   def Pull(self, host_file=None):
@@ -105,7 +107,7 @@
             time.strftime('%Y%m%dT%H%M%S', time.localtime())))
     host_file_name = os.path.abspath(host_file_name)
     self._device.PullFile(self._device_file, host_file_name)
-    self._device.RunShellCommand('rm -f "%s"' % self._device_file)
+    self._device.RemovePath(self._device_file, force=True)
     return host_file_name
 
 
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/android/tools/wait_for_devices.py b/sdk/platform-tools/systrace/catapult/devil/devil/android/tools/wait_for_devices.py
new file mode 100755
index 0000000..4bde2cd
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/android/tools/wait_for_devices.py
@@ -0,0 +1,50 @@
+#!/usr/bin/env python
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Waits for the given devices to be available."""
+
+import argparse
+import os
+import sys
+
+if __name__ == '__main__':
+  sys.path.append(
+      os.path.abspath(os.path.join(os.path.dirname(__file__),
+                                   '..', '..', '..')))
+
+from devil import devil_env
+from devil.android import device_utils
+from devil.utils import run_tests_helper
+
+
+def main(raw_args):
+  parser = argparse.ArgumentParser()
+  parser.add_argument('-v', '--verbose', action='count', help='Log more.')
+  parser.add_argument('-t', '--timeout', default=30, type=int,
+                      help='Seconds to wait for the devices.')
+  parser.add_argument('--adb-path', help='ADB binary to use.')
+  parser.add_argument('device_serials', nargs='*', metavar='SERIAL',
+                      help='Serials of the devices to wait for.')
+
+  args = parser.parse_args(raw_args)
+
+  run_tests_helper.SetLogLevel(args.verbose)
+
+  devil_dynamic_config = devil_env.EmptyConfig()
+  if args.adb_path:
+    devil_dynamic_config['dependencies'].update(
+        devil_env.LocalConfigItem(
+            'adb', devil_env.GetPlatform(), args.adb_path))
+  devil_env.config.Initialize(configs=[devil_dynamic_config])
+
+  devices = device_utils.DeviceUtils.HealthyDevices(
+      device_arg=args.device_serials)
+  parallel_devices = device_utils.DeviceUtils.parallel(devices)
+  parallel_devices.WaitUntilFullyBooted(timeout=args.timeout)
+  return 0
+
+
+if __name__ == '__main__':
+  sys.exit(main(sys.argv[1:]))
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/base_error.py b/sdk/platform-tools/systrace/catapult/devil/devil/base_error.py
index dadf4da..4b89661 100644
--- a/sdk/platform-tools/systrace/catapult/devil/devil/base_error.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/base_error.py
@@ -10,6 +10,13 @@
     super(BaseError, self).__init__(message)
     self._is_infra_error = is_infra_error
 
+  def __eq__(self, other):
+    return (self.message == other.message
+            and self.is_infra_error == other.is_infra_error)
+
+  def __ne__(self, other):
+    return not self == other
+
   @property
   def is_infra_error(self):
     """Property to indicate if error was caused by an infrastructure issue."""
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/devil_dependencies.json b/sdk/platform-tools/systrace/catapult/devil/devil/devil_dependencies.json
index d16e792..e552293 100644
--- a/sdk/platform-tools/systrace/catapult/devil/devil/devil_dependencies.json
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/devil_dependencies.json
@@ -2,8 +2,8 @@
   "config_type": "BaseConfig",
   "dependencies": {
     "aapt": {
-      "cloud_storage_bucket": "chromium-telemetry",
       "cloud_storage_base_folder": "binary_dependencies",
+      "cloud_storage_bucket": "chromium-telemetry",
       "file_info": {
         "linux2_x86_64": {
           "cloud_storage_hash": "16ba3180141a2489d7ec99b39fd6e3434a9a373f",
@@ -12,8 +12,8 @@
       }
     },
     "adb": {
-      "cloud_storage_bucket": "chromium-telemetry",
       "cloud_storage_base_folder": "binary_dependencies",
+      "cloud_storage_bucket": "chromium-telemetry",
       "file_info": {
         "linux2_x86_64": {
           "cloud_storage_hash": "8bd43e3930f6eec643d5dc64cab9e5bb4ddf4909",
@@ -22,8 +22,8 @@
       }
     },
     "android_build_tools_libc++": {
-      "cloud_storage_bucket": "chromium-telemetry",
       "cloud_storage_base_folder": "binary_dependencies",
+      "cloud_storage_bucket": "chromium-telemetry",
       "file_info": {
         "linux2_x86_64": {
           "cloud_storage_hash": "91cdce1e3bd81b2ac1fd380013896d0e2cdb40a0",
@@ -32,8 +32,8 @@
       }
     },
     "chromium_commands": {
-      "cloud_storage_bucket": "chromium-telemetry",
       "cloud_storage_base_folder": "binary_dependencies",
+      "cloud_storage_bucket": "chromium-telemetry",
       "file_info": {
         "linux2_x86_64": {
           "cloud_storage_hash": "4e22f641e4757309510e8d9f933f5aa504574ab6",
@@ -42,8 +42,8 @@
       }
     },
     "dexdump": {
-      "cloud_storage_bucket": "chromium-telemetry",
       "cloud_storage_base_folder": "binary_dependencies",
+      "cloud_storage_bucket": "chromium-telemetry",
       "file_info": {
         "linux2_x86_64": {
           "cloud_storage_hash": "acfb10f7a868baf9bcf446a2d9f8ed6b5d52c3c6",
@@ -52,8 +52,8 @@
       }
     },
     "fastboot": {
-      "cloud_storage_bucket": "chromium-telemetry",
       "cloud_storage_base_folder": "binary_dependencies",
+      "cloud_storage_bucket": "chromium-telemetry",
       "file_info": {
         "linux2_x86_64": {
           "cloud_storage_hash": "db9728166f182800eb9d09e9f036d56e105e8235",
@@ -62,41 +62,41 @@
       }
     },
     "forwarder_device": {
-      "cloud_storage_bucket": "chromium-telemetry",
       "cloud_storage_base_folder": "binary_dependencies",
+      "cloud_storage_bucket": "chromium-telemetry",
       "file_info": {
-        "android_armeabi-v7a": {
-          "cloud_storage_hash": "de54e23327cc04ef7009fe697227617c7eeb1b2e",
-          "download_path": "../bin/deps/android/armeabi-v7a/bin/forwarder_device"
-        },
         "android_arm64-v8a": {
-          "cloud_storage_hash": "67b496f6d3ec6371393c2a8f4f403fdb27f5d091",
+          "cloud_storage_hash": "90cc60cefb497512d95d774045146754c477b433",
           "download_path": "../bin/deps/android/arm64-v8a/bin/forwarder_device"
+        },
+        "android_armeabi-v7a": {
+          "cloud_storage_hash": "3a5ade8fbaffea3b0670bffaa845288db5e4d567",
+          "download_path": "../bin/deps/android/armeabi-v7a/bin/forwarder_device"
         }
       }
     },
     "forwarder_host": {
-      "cloud_storage_bucket": "chromium-telemetry",
       "cloud_storage_base_folder": "binary_dependencies",
+      "cloud_storage_bucket": "chromium-telemetry",
       "file_info": {
         "linux2_x86_64": {
-          "cloud_storage_hash": "02a1854adc15cc9e438f4ecfad2af058bd44fa7e",
+          "cloud_storage_hash": "63653293098d7fe17aa115b147c8d9aa253b0912",
           "download_path": "../bin/deps/linux2/x86_64/forwarder_host"
         }
       }
     },
     "md5sum_device": {
-      "cloud_storage_bucket": "chromium-telemetry",
       "cloud_storage_base_folder": "binary_dependencies",
+      "cloud_storage_bucket": "chromium-telemetry",
       "file_info": {
+        "android_arm64-v8a": {
+          "cloud_storage_hash": "4e7d2dedd9c6321fdc152b06869e09a3c5817904",
+          "download_path": "../bin/deps/android/arm64-v8a/bin/md5sum_device"
+        },
         "android_armeabi-v7a": {
           "cloud_storage_hash": "39fd90af0f8828202b687f7128393759181c5e2e",
           "download_path": "../bin/deps/android/armeabi-v7a/bin/md5sum_device"
         },
-        "android_arm64-v8a": {
-          "cloud_storage_hash": "4e7d2dedd9c6321fdc152b06869e09a3c5817904",
-          "download_path": "../bin/deps/andorid/arm64-v8a/bin/md5sum_device"
-        },
         "android_x86": {
           "cloud_storage_hash": "d5cf42ab5986a69c31c0177b0df499d6bf708df6",
           "download_path": "../bin/deps/android/x86/bin/md5sum_device"
@@ -104,8 +104,8 @@
       }
     },
     "md5sum_host": {
-      "cloud_storage_bucket": "chromium-telemetry",
       "cloud_storage_base_folder": "binary_dependencies",
+      "cloud_storage_bucket": "chromium-telemetry",
       "file_info": {
         "linux2_x86_64": {
           "cloud_storage_hash": "4db5bd5e9bea8880d8bf2caa59d0efb0acc19f74",
@@ -114,8 +114,8 @@
       }
     },
     "split-select": {
-      "cloud_storage_bucket": "chromium-telemetry",
       "cloud_storage_base_folder": "binary_dependencies",
+      "cloud_storage_bucket": "chromium-telemetry",
       "file_info": {
         "linux2_x86_64": {
           "cloud_storage_hash": "abb9753a8d3efeea4144e328933931729e01571c",
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/devil_env.py b/sdk/platform-tools/systrace/catapult/devil/devil/devil_env.py
index 6b64766..aa4fe1e 100644
--- a/sdk/platform-tools/systrace/catapult/devil/devil/devil_env.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/devil_env.py
@@ -4,6 +4,7 @@
 
 import contextlib
 import json
+import logging
 import os
 import platform
 import sys
@@ -85,6 +86,8 @@
   def __init__(self):
     self._dm_init_lock = threading.Lock()
     self._dm = None
+    self._logging_init_lock = threading.Lock()
+    self._logging_initialized = False
 
   def Initialize(self, configs=None, config_files=None):
     """Initialize devil's environment from configuration files.
@@ -138,6 +141,32 @@
       self._dm = dependency_manager.DependencyManager(
           [dependency_manager.BaseConfig(c) for c in config_files])
 
+  def InitializeLogging(self, log_level, formatter=None, handler=None):
+    if self._logging_initialized:
+      return
+
+    with self._logging_init_lock:
+      if self._logging_initialized:
+        return
+
+      formatter = formatter or logging.Formatter(
+          '%(threadName)-4s  %(message)s')
+      handler = handler or logging.StreamHandler(sys.stdout)
+      handler.setFormatter(formatter)
+
+      devil_logger = logging.getLogger('devil')
+      devil_logger.setLevel(log_level)
+      devil_logger.propagate = False
+      devil_logger.addHandler(handler)
+
+      import py_utils.cloud_storage
+      lock_logger = py_utils.cloud_storage.logger
+      lock_logger.setLevel(log_level)
+      lock_logger.propagate = False
+      lock_logger.addHandler(handler)
+
+      self._logging_initialized = True
+
   def FetchPath(self, dependency, arch=None, device=None):
     if self._dm is None:
       self.Initialize()
@@ -150,9 +179,13 @@
       self.Initialize()
     return self._dm.LocalPath(dependency, GetPlatform(arch, device))
 
+  def PrefetchPaths(self, dependencies=None, arch=None, device=None):
+    return self._dm.PrefetchPaths(
+        GetPlatform(arch, device), dependencies=dependencies)
+
 
 def GetPlatform(arch=None, device=None):
-  if device:
+  if arch or device:
     return 'android_%s' % (arch or device.product_cpu_abi)
   return '%s_%s' % (sys.platform, platform.machine())
 
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/utils/battor_device_mapping.py b/sdk/platform-tools/systrace/catapult/devil/devil/utils/battor_device_mapping.py
index 5624036..8cabb83 100755
--- a/sdk/platform-tools/systrace/catapult/devil/devil/utils/battor_device_mapping.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/utils/battor_device_mapping.py
@@ -61,7 +61,7 @@
 create the JSON file manually. (In this case, hubs would not be necessary
 and the physical ports connected would be irrelevant.)
 
-Step (2) is conducted through the function GetBattorPathFromPhoneSerial,
+Step (2) is conducted through the function GetBattOrPathFromPhoneSerial,
 which takes a serial number mapping generated via step (1) and a phone
 serial number, then gets the corresponding BattOr serial number from the
 map and determines its BattOr path (e.g. /dev/ttyUSB0). Since BattOr paths
@@ -70,7 +70,7 @@
 determine the BattOr path every time before connecting to the BattOr.
 
 Note that if there is only one BattOr connected to the system, then
-GetBattorPathFromPhoneSerial will always return that BattOr and will ignore
+GetBattOrPathFromPhoneSerial will always return that BattOr and will ignore
 the mapping file. Thus, if the user never has more than one BattOr connected
 to the system, the user will not need to generate mapping files.
 '''
@@ -84,20 +84,20 @@
 from devil.utils import usb_hubs
 
 
-def GetBattorList(device_tree_map):
+def GetBattOrList(device_tree_map):
   return [x for x in find_usb_devices.GetTTYList()
-          if IsBattor(x, device_tree_map)]
+          if IsBattOr(x, device_tree_map)]
 
 
-def IsBattor(tty_string, device_tree_map):
+def IsBattOr(tty_string, device_tree_map):
   (bus, device) = find_usb_devices.GetBusDeviceFromTTY(tty_string)
   node = device_tree_map[bus].FindDeviceNumber(device)
   return '0403:6001' in node.desc
 
 
-def GetBattorSerialNumbers(device_tree_map):
+def GetBattOrSerialNumbers(device_tree_map):
   for x in find_usb_devices.GetTTYList():
-    if IsBattor(x, device_tree_map):
+    if IsBattOr(x, device_tree_map):
       (bus, device) = find_usb_devices.GetBusDeviceFromTTY(x)
       devnode = device_tree_map[bus].FindDeviceNumber(device)
       yield devnode.serial
@@ -169,7 +169,7 @@
   devtree = find_usb_devices.GetBusNumberToDeviceTreeMap()
 
   # List of serial numbers in the system that represent BattOrs.
-  battor_serials = list(GetBattorSerialNumbers(devtree))
+  battor_serials = list(GetBattOrSerialNumbers(devtree))
 
   # If there's only one BattOr in the system, then a serial number ma
   # is not necessary.
@@ -198,12 +198,12 @@
     for (port, serial) in hub.iteritems():
       if serial in battor_serials:
         if port_to_devices[port].battor is not None:
-          raise battor_error.BattorError('Multiple BattOrs on same port number')
+          raise battor_error.BattOrError('Multiple BattOrs on same port number')
         else:
           port_to_devices[port].battor = serial
       else:
         if port_to_devices[port].phone is not None:
-          raise battor_error.BattorError('Multiple phones on same port number')
+          raise battor_error.BattOrError('Multiple phones on same port number')
         else:
           port_to_devices[port].phone = serial
 
@@ -214,7 +214,7 @@
     if pair.phone is None:
       continue
     if pair.battor is None:
-      raise battor_error.BattorError(
+      raise battor_error.BattOrError(
           'Phone detected with no corresponding BattOr')
     result[pair.phone] = pair.battor
   return result
@@ -228,7 +228,7 @@
   try:
     battor_serial = serial_map[serial]
   except KeyError:
-    raise battor_error.BattorError('Serial number not found in serial map.')
+    raise battor_error.BattOrError('Serial number not found in serial map.')
   for tree in devtree.values():
     for node in tree.AllNodes():
       if isinstance(node, find_usb_devices.USBDeviceNode):
@@ -238,12 +238,12 @@
           try:
             return bus_device_to_tty[bus_device]
           except KeyError:
-            raise battor_error.BattorError(
+            raise battor_error.BattOrError(
                 'Device with given serial number not a BattOr '
                 '(does not have TTY path)')
 
 
-def GetBattorPathFromPhoneSerial(serial, serial_map=None,
+def GetBattOrPathFromPhoneSerial(serial, serial_map=None,
                                  serial_map_file=None):
   """Gets the TTY path (e.g. '/dev/ttyUSB0')  to communicate with the BattOr.
 
@@ -273,17 +273,17 @@
 
   Raises:
     ValueError: If serial number is not given.
-    BattorError: If BattOr not found or unexpected USB topology.
+    BattOrError: If BattOr not found or unexpected USB topology.
   """
   # If there's only one BattOr connected to the system, just use that one.
   # This allows for use on, e.g., a developer's workstation with no hubs.
   devtree = find_usb_devices.GetBusNumberToDeviceTreeMap()
-  all_battors = GetBattorList(devtree)
+  all_battors = GetBattOrList(devtree)
   if len(all_battors) == 1:
     return '/dev/' + all_battors[0]
 
   if not serial:
-    raise battor_error.BattorError(
+    raise battor_error.BattOrError(
         'Two or more BattOrs connected, no serial provided')
 
   if serial_map and serial_map_file:
@@ -295,13 +295,13 @@
   tty_string = _PhoneToPathMap(serial, serial_map, devtree)
 
   if not tty_string:
-    raise battor_error.BattorError(
+    raise battor_error.BattOrError(
         'No device with given serial number detected.')
 
-  if IsBattor(tty_string, devtree):
+  if IsBattOr(tty_string, devtree):
     return '/dev/' + tty_string
   else:
-    raise battor_error.BattorError(
+    raise battor_error.BattOrError(
         'Device with given serial number is not a BattOr.')
 
 if __name__ == '__main__':
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/utils/cmd_helper.py b/sdk/platform-tools/systrace/catapult/devil/devil/utils/cmd_helper.py
index c220052..06c105f 100644
--- a/sdk/platform-tools/systrace/catapult/devil/devil/utils/cmd_helper.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/utils/cmd_helper.py
@@ -12,6 +12,7 @@
 import string
 import StringIO
 import subprocess
+import sys
 import time
 
 # fcntl is not available on Windows.
@@ -20,6 +21,8 @@
 except ImportError:
   fcntl = None
 
+logger = logging.getLogger(__name__)
+
 _SafeShellChars = frozenset(string.ascii_letters + string.digits + '@%_-+=:,./')
 
 
@@ -91,10 +94,15 @@
 
 
 def Popen(args, stdout=None, stderr=None, shell=None, cwd=None, env=None):
+  # preexec_fn isn't supported on windows.
+  if sys.platform == 'win32':
+    preexec_fn = None
+  else:
+    preexec_fn = lambda: signal.signal(signal.SIGPIPE, signal.SIG_DFL)
+
   return subprocess.Popen(
       args=args, cwd=cwd, stdout=stdout, stderr=stderr,
-      shell=shell, close_fds=True, env=env,
-      preexec_fn=lambda: signal.signal(signal.SIGPIPE, signal.SIG_DFL))
+      shell=shell, close_fds=True, env=env, preexec_fn=preexec_fn)
 
 
 def Call(args, stdout=None, stderr=None, shell=None, cwd=None, env=None):
@@ -116,7 +124,7 @@
   Returns:
     Return code from the command execution.
   """
-  logging.info(str(args) + ' ' + (cwd or ''))
+  logger.info(str(args) + ' ' + (cwd or ''))
   return Call(args, cwd=cwd)
 
 
@@ -150,7 +158,7 @@
     cwd = ''
   else:
     cwd = ':' + cwd
-  logging.info('[host]%s> %s', cwd, args)
+  logger.info('[host]%s> %s', cwd, args)
   return args
 
 
@@ -172,9 +180,9 @@
       args, cwd=cwd, shell=shell)
 
   if stderr:
-    logging.critical('STDERR: %s', stderr)
-  logging.debug('STDOUT: %s%s', stdout[:4096].rstrip(),
-                '<truncated>' if len(stdout) > 4096 else '')
+    logger.critical('STDERR: %s', stderr)
+  logger.debug('STDOUT: %s%s', stdout[:4096].rstrip(),
+               '<truncated>' if len(stdout) > 4096 else '')
   return (status, stdout)
 
 
@@ -211,8 +219,30 @@
     return self._output
 
 
-def _IterProcessStdout(process, timeout=None, buffer_size=4096,
-                       poll_interval=1):
+def _IterProcessStdout(process, iter_timeout=None, timeout=None,
+                       buffer_size=4096, poll_interval=1):
+  """Iterate over a process's stdout.
+
+  This is intentionally not public.
+
+  Args:
+    process: The process in question.
+    iter_timeout: An optional length of time, in seconds, to wait in
+      between each iteration. If no output is received in the given
+      time, this generator will yield None.
+    timeout: An optional length of time, in seconds, during which
+      the process must finish. If it fails to do so, a TimeoutError
+      will be raised.
+    buffer_size: The maximum number of bytes to read (and thus yield) at once.
+    poll_interval: The length of time to wait in calls to `select.select`.
+      If iter_timeout is set, the remaining length of time in the iteration
+      may take precedence.
+  Raises:
+    TimeoutError: if timeout is set and the process does not complete.
+  Yields:
+    basestrings of data or None.
+  """
+
   assert fcntl, 'fcntl module is required'
   try:
     # Enable non-blocking reads from the child's stdout.
@@ -221,10 +251,24 @@
     fcntl.fcntl(child_fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
 
     end_time = (time.time() + timeout) if timeout else None
+    iter_end_time = (time.time() + iter_timeout) if iter_timeout else None
+
     while True:
       if end_time and time.time() > end_time:
         raise TimeoutError()
-      read_fds, _, _ = select.select([child_fd], [], [], poll_interval)
+      if iter_end_time and time.time() > iter_end_time:
+        yield None
+        iter_end_time = time.time() + iter_timeout
+
+      if iter_end_time:
+        iter_aware_poll_interval = min(
+            poll_interval,
+            max(0, iter_end_time - time.time()))
+      else:
+        iter_aware_poll_interval = poll_interval
+
+      read_fds, _, _ = select.select(
+          [child_fd], [], [], iter_aware_poll_interval)
       if child_fd in read_fds:
         data = os.read(child_fd, buffer_size)
         if not data:
@@ -234,9 +278,10 @@
         break
   finally:
     try:
-      # Make sure the process doesn't stick around if we fail with an
-      # exception.
-      process.kill()
+      if process.returncode is None:
+        # Make sure the process doesn't stick around if we fail with an
+        # exception.
+        process.kill()
     except OSError:
       pass
     process.wait()
@@ -259,6 +304,8 @@
 
   Returns:
     The 2-tuple (exit code, output).
+  Raises:
+    TimeoutError on timeout.
   """
   _ValidateAndLogCommand(args, cwd, shell)
   output = StringIO.StringIO()
@@ -273,25 +320,26 @@
     raise TimeoutError(output.getvalue())
 
   str_output = output.getvalue()
-  logging.debug('STDOUT+STDERR: %s%s', str_output[:4096].rstrip(),
-                '<truncated>' if len(str_output) > 4096 else '')
+  logger.debug('STDOUT+STDERR: %s%s', str_output[:4096].rstrip(),
+               '<truncated>' if len(str_output) > 4096 else '')
   return process.returncode, str_output
 
 
-def IterCmdOutputLines(args, timeout=None, cwd=None, shell=False,
-                       check_status=True):
+def IterCmdOutputLines(args, iter_timeout=None, timeout=None, cwd=None,
+                       shell=False, check_status=True):
   """Executes a subprocess and continuously yields lines from its output.
 
   Args:
     args: List of arguments to the program, the program to execute is the first
       element.
+    iter_timeout: Timeout for each iteration, in seconds.
+    timeout: Timeout for the entire command, in seconds.
     cwd: If not None, the subprocess's current directory will be changed to
       |cwd| before it's executed.
     shell: Whether to execute args as a shell command. Must be True if args
       is a string and False if args is a sequence.
     check_status: A boolean indicating whether to check the exit status of the
       process after all output has been read.
-
   Yields:
     The output of the subprocess, line by line.
 
@@ -302,14 +350,44 @@
   cmd = _ValidateAndLogCommand(args, cwd, shell)
   process = Popen(args, cwd=cwd, shell=shell, stdout=subprocess.PIPE,
                   stderr=subprocess.STDOUT)
+  return _IterCmdOutputLines(
+      process, cmd, iter_timeout=iter_timeout, timeout=timeout,
+      check_status=check_status)
+
+def _IterCmdOutputLines(process, cmd, iter_timeout=None, timeout=None,
+                        check_status=True):
   buffer_output = ''
-  for data in _IterProcessStdout(process, timeout=timeout):
+
+  iter_end = None
+  cur_iter_timeout = None
+  if iter_timeout:
+    iter_end = time.time() + iter_timeout
+    cur_iter_timeout = iter_timeout
+
+  for data in _IterProcessStdout(process, iter_timeout=cur_iter_timeout,
+                                 timeout=timeout):
+    if iter_timeout:
+      # Check whether the current iteration has timed out.
+      cur_iter_timeout = iter_end - time.time()
+      if data is None or cur_iter_timeout < 0:
+        yield None
+        iter_end = time.time() + iter_timeout
+        continue
+    else:
+      assert data is not None, (
+          'Iteration received no data despite no iter_timeout being set. '
+          'cmd: %s' % cmd)
+
+    # Construct lines to yield from raw data.
     buffer_output += data
     has_incomplete_line = buffer_output[-1] not in '\r\n'
     lines = buffer_output.splitlines()
     buffer_output = lines.pop() if has_incomplete_line else ''
     for line in lines:
       yield line
+      if iter_timeout:
+        iter_end = time.time() + iter_timeout
+
   if buffer_output:
     yield buffer_output
   if check_status and process.returncode:
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/utils/cmd_helper_test.py b/sdk/platform-tools/systrace/catapult/devil/devil/utils/cmd_helper_test.py
index a04f1ad..783c413 100755
--- a/sdk/platform-tools/systrace/catapult/devil/devil/utils/cmd_helper_test.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/utils/cmd_helper_test.py
@@ -7,9 +7,14 @@
 
 import unittest
 import subprocess
+import time
 
+from devil import devil_env
 from devil.utils import cmd_helper
 
+with devil_env.SysPath(devil_env.PYMOCK_PATH):
+  import mock  # pylint: disable=import-error
+
 
 class CmdHelperSingleQuoteTest(unittest.TestCase):
 
@@ -84,35 +89,173 @@
         cmd_helper.ShrinkToSnippet(['foo', ' barbar '], 'a', 'bar'))
 
 
+_DEFAULT = 'DEFAULT'
+
+
+class _ProcessOutputEvent(object):
+
+  def __init__(self, select_fds=_DEFAULT, read_contents=None, ts=_DEFAULT):
+    self.select_fds = select_fds
+    self.read_contents = read_contents
+    self.ts = ts
+
+
+class _MockProcess(object):
+
+  def __init__(self, output_sequence=None, return_value=0):
+
+    # Arbitrary.
+    fake_stdout_fileno = 25
+
+    self.mock_proc = mock.MagicMock(spec=subprocess.Popen)
+    self.mock_proc.stdout = mock.MagicMock()
+    self.mock_proc.stdout.fileno = mock.MagicMock(
+        return_value=fake_stdout_fileno)
+    self.mock_proc.returncode = None
+
+    self._return_value = return_value
+
+    # This links the behavior of os.read, select.select, time.time, and
+    # <process>.poll. The output sequence can be thought of as a list of
+    # return values for select.select with corresponding return values for
+    # the other calls at any time between that select call and the following
+    # one. We iterate through the sequence only on calls to select.select.
+    #
+    # os.read is a special case, though, where we only return a given chunk
+    # of data *once* after a given call to select.
+
+    if not output_sequence:
+      output_sequence = []
+
+    # Use an leading element to make the iteration logic work.
+    initial_seq_element = _ProcessOutputEvent(
+        _DEFAULT, '',
+        output_sequence[0].ts if output_sequence else _DEFAULT)
+    output_sequence.insert(0, initial_seq_element)
+
+    for o in output_sequence:
+      if o.select_fds == _DEFAULT:
+        if o.read_contents is None:
+          o.select_fds = []
+        else:
+          o.select_fds = [fake_stdout_fileno]
+      if o.ts == _DEFAULT:
+        o.ts = time.time()
+    self._output_sequence = output_sequence
+
+    self._output_seq_index = 0
+    self._read_flags = [False] * len(output_sequence)
+
+    def read_side_effect(*_args, **_kwargs):
+      if self._read_flags[self._output_seq_index]:
+        return None
+      self._read_flags[self._output_seq_index] = True
+      return self._output_sequence[self._output_seq_index].read_contents
+
+    def select_side_effect(*_args, **_kwargs):
+      if self._output_seq_index is None:
+        self._output_seq_index = 0
+      else:
+        self._output_seq_index += 1
+      return (self._output_sequence[self._output_seq_index].select_fds,
+              None, None)
+
+    def time_side_effect(*_args, **_kwargs):
+      return self._output_sequence[self._output_seq_index].ts
+
+    def poll_side_effect(*_args, **_kwargs):
+      if self._output_seq_index >= len(self._output_sequence) - 1:
+        self.mock_proc.returncode = self._return_value
+      return self.mock_proc.returncode
+
+    mock_read = mock.MagicMock(side_effect=read_side_effect)
+    mock_select = mock.MagicMock(side_effect=select_side_effect)
+    mock_time = mock.MagicMock(side_effect=time_side_effect)
+    self.mock_proc.poll = mock.MagicMock(side_effect=poll_side_effect)
+
+    # Set up but *do not start* the mocks.
+    self._mocks = [
+      mock.patch('fcntl.fcntl'),
+      mock.patch('os.read', new=mock_read),
+      mock.patch('select.select', new=mock_select),
+      mock.patch('time.time', new=mock_time),
+    ]
+
+  def __enter__(self):
+    for m in self._mocks:
+      m.__enter__()
+    return self.mock_proc
+
+  def __exit__(self, exc_type, exc_val, exc_tb):
+    for m in reversed(self._mocks):
+      m.__exit__(exc_type, exc_val, exc_tb)
+
+
 class CmdHelperIterCmdOutputLinesTest(unittest.TestCase):
   """Test IterCmdOutputLines with some calls to the unix 'seq' command."""
 
+  # This calls _IterCmdOutputLines rather than IterCmdOutputLines s.t. it
+  # can mock the process.
+  # pylint: disable=protected-access
+
+  _SIMPLE_OUTPUT_SEQUENCE = [
+    _ProcessOutputEvent(read_contents='1\n2\n'),
+  ]
+
   def testIterCmdOutputLines_success(self):
-    for num, line in enumerate(
-        cmd_helper.IterCmdOutputLines(['seq', '10']), 1):
-      self.assertEquals(num, int(line))
+    with _MockProcess(
+        output_sequence=self._SIMPLE_OUTPUT_SEQUENCE) as mock_proc:
+      for num, line in enumerate(
+          cmd_helper._IterCmdOutputLines(mock_proc, 'mock_proc'), 1):
+        self.assertEquals(num, int(line))
 
   def testIterCmdOutputLines_exitStatusFail(self):
     with self.assertRaises(subprocess.CalledProcessError):
-      for num, line in enumerate(
-          cmd_helper.IterCmdOutputLines('seq 10 && false', shell=True), 1):
-        self.assertEquals(num, int(line))
-      # after reading all the output we get an exit status of 1
+      with _MockProcess(output_sequence=self._SIMPLE_OUTPUT_SEQUENCE,
+                        return_value=1) as mock_proc:
+        for num, line in enumerate(
+            cmd_helper._IterCmdOutputLines(mock_proc, 'mock_proc'), 1):
+          self.assertEquals(num, int(line))
+        # after reading all the output we get an exit status of 1
 
   def testIterCmdOutputLines_exitStatusIgnored(self):
-    for num, line in enumerate(
-        cmd_helper.IterCmdOutputLines('seq 10 && false', shell=True,
-                                      check_status=False), 1):
-      self.assertEquals(num, int(line))
+    with _MockProcess(output_sequence=self._SIMPLE_OUTPUT_SEQUENCE,
+                      return_value=1) as mock_proc:
+      for num, line in enumerate(
+          cmd_helper._IterCmdOutputLines(
+              mock_proc, 'mock_proc', check_status=False),
+          1):
+        self.assertEquals(num, int(line))
 
   def testIterCmdOutputLines_exitStatusSkipped(self):
-    for num, line in enumerate(
-        cmd_helper.IterCmdOutputLines('seq 10 && false', shell=True), 1):
-      self.assertEquals(num, int(line))
-      # no exception will be raised because we don't attempt to read past
-      # the end of the output and, thus, the status never gets checked
-      if num == 10:
-        break
+    with _MockProcess(output_sequence=self._SIMPLE_OUTPUT_SEQUENCE,
+                      return_value=1) as mock_proc:
+      for num, line in enumerate(
+          cmd_helper._IterCmdOutputLines(mock_proc, 'mock_proc'), 1):
+        self.assertEquals(num, int(line))
+        # no exception will be raised because we don't attempt to read past
+        # the end of the output and, thus, the status never gets checked
+        if num == 2:
+          break
+
+  def testIterCmdOutputLines_delay(self):
+    output_sequence = [
+      _ProcessOutputEvent(read_contents='1\n2\n', ts=1),
+      _ProcessOutputEvent(read_contents=None, ts=2),
+      _ProcessOutputEvent(read_contents='Awake', ts=10),
+    ]
+    with _MockProcess(output_sequence=output_sequence) as mock_proc:
+      for num, line in enumerate(
+          cmd_helper._IterCmdOutputLines(mock_proc, 'mock_proc',
+                                         iter_timeout=5), 1):
+        if num <= 2:
+          self.assertEquals(num, int(line))
+        elif num == 3:
+          self.assertEquals(None, line)
+        elif num == 4:
+          self.assertEquals('Awake', line)
+        else:
+          self.fail()
 
 
 if __name__ == '__main__':
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/utils/find_usb_devices_test.py b/sdk/platform-tools/systrace/catapult/devil/devil/utils/find_usb_devices_test.py
index c99e716..e8b00c8 100755
--- a/sdk/platform-tools/systrace/catapult/devil/devil/utils/find_usb_devices_test.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/utils/find_usb_devices_test.py
@@ -82,14 +82,14 @@
 
 T:  Bus=02 Lev=00 Prnt=00 Port=01 Cnt=00 Dev#= 20 Spd=000 MxCh=00
 T:  Bus=02 Lev=00 Prnt=20 Port=00 Cnt=00 Dev#= 21 Spd=000 MxCh=00
-S:  SerialNumber=Battor0
+S:  SerialNumber=BattOr0
 T:  Bus=02 Lev=00 Prnt=20 Port=02 Cnt=00 Dev#= 22 Spd=000 MxCh=00
-S:  SerialNumber=Battor1
+S:  SerialNumber=BattOr1
 T:  Bus=02 Lev=00 Prnt=20 Port=03 Cnt=00 Dev#= 23 Spd=000 MxCh=00
 T:  Bus=02 Lev=00 Prnt=23 Port=01 Cnt=00 Dev#= 24 Spd=000 MxCh=00
-S:  SerialNumber=Battor2
+S:  SerialNumber=BattOr2
 T:  Bus=02 Lev=00 Prnt=23 Port=03 Cnt=00 Dev#= 25 Spd=000 MxCh=00
-S:  SerialNumber=Battor3
+S:  SerialNumber=BattOr3
 T:  Bus=02 Lev=00 Prnt=23 Port=02 Cnt=00 Dev#= 26 Spd=000 MxCh=00
 
 T:  Bus=02 Lev=00 Prnt=00 Port=02 Cnt=00 Dev#=100 Spd=000 MxCh=00
@@ -213,14 +213,14 @@
     lsusb.raw_lsusb = mock.Mock(
         return_value=RAW_LSUSB_OUTPUT)
 
-  def testIsBattor(self):
+  def testIsBattOr(self):
     bd = find_usb_devices.GetBusNumberToDeviceTreeMap()
-    self.assertTrue(battor_device_mapping.IsBattor('ttyUSB3', bd))
-    self.assertFalse(battor_device_mapping.IsBattor('ttyUSB5', bd))
+    self.assertTrue(battor_device_mapping.IsBattOr('ttyUSB3', bd))
+    self.assertFalse(battor_device_mapping.IsBattOr('ttyUSB5', bd))
 
-  def testGetBattors(self):
+  def testGetBattOrs(self):
     bd = find_usb_devices.GetBusNumberToDeviceTreeMap()
-    self.assertEquals(battor_device_mapping.GetBattorList(bd),
+    self.assertEquals(battor_device_mapping.GetBattOrList(bd),
                           ['ttyUSB0', 'ttyUSB1', 'ttyUSB2',
                            'ttyUSB3', 'ttyUSB4'])
 
@@ -247,10 +247,10 @@
   def testGetSerialMapping(self):
     pp = find_usb_devices.GetAllPhysicalPortToSerialMaps([TEST_HUB])
     result = list(pp)
-    self.assertEquals(result[0], {7:'Battor0',
-                                  5:'Battor1',
-                                  3:'Battor2',
-                                  1:'Battor3'})
+    self.assertEquals(result[0], {7:'BattOr0',
+                                  5:'BattOr1',
+                                  3:'BattOr2',
+                                  1:'BattOr3'})
     self.assertEquals(result[1], {})
 
   def testFastDeviceDescriptions(self):
@@ -289,31 +289,31 @@
     dev_battor_p7_h1_t0 = bd[2].FindDeviceNumber(21)
     self.assertEquals(dev_foo.serial, 'FooSerial')
     self.assertEquals(dev_bar.serial, 'BarSerial')
-    self.assertEquals(dev_battor_p7_h1_t0.serial, 'Battor0')
+    self.assertEquals(dev_battor_p7_h1_t0.serial, 'BattOr0')
 
-  def testBattorDictMapping(self):
-    map_dict = {'Phone1':'Battor1', 'Phone2':'Battor2', 'Phone3':'Battor3'}
-    a1 = battor_device_mapping.GetBattorPathFromPhoneSerial(
+  def testBattOrDictMapping(self):
+    map_dict = {'Phone1':'BattOr1', 'Phone2':'BattOr2', 'Phone3':'BattOr3'}
+    a1 = battor_device_mapping.GetBattOrPathFromPhoneSerial(
              'Phone1', serial_map=map_dict)
-    a2 = battor_device_mapping.GetBattorPathFromPhoneSerial(
+    a2 = battor_device_mapping.GetBattOrPathFromPhoneSerial(
              'Phone2', serial_map=map_dict)
-    a3 = battor_device_mapping.GetBattorPathFromPhoneSerial(
+    a3 = battor_device_mapping.GetBattOrPathFromPhoneSerial(
              'Phone3', serial_map=map_dict)
     self.assertEquals(a1, '/dev/ttyUSB1')
     self.assertEquals(a2, '/dev/ttyUSB2')
     self.assertEquals(a3, '/dev/ttyUSB3')
 
-  def testBattorDictFromFileMapping(self):
+  def testBattOrDictFromFileMapping(self):
     try:
-      map_dict = {'Phone1':'Battor1', 'Phone2':'Battor2', 'Phone3':'Battor3'}
+      map_dict = {'Phone1':'BattOr1', 'Phone2':'BattOr2', 'Phone3':'BattOr3'}
       curr_dir = os.path.dirname(os.path.realpath(__file__))
       filename = os.path.join(curr_dir, 'test', 'data', 'test_write_map.json')
       battor_device_mapping.WriteSerialMapFile(filename, map_dict)
-      a1 = battor_device_mapping.GetBattorPathFromPhoneSerial(
+      a1 = battor_device_mapping.GetBattOrPathFromPhoneSerial(
                'Phone1', serial_map_file=filename)
-      a2 = battor_device_mapping.GetBattorPathFromPhoneSerial(
+      a2 = battor_device_mapping.GetBattOrPathFromPhoneSerial(
                'Phone2', serial_map_file=filename)
-      a3 = battor_device_mapping.GetBattorPathFromPhoneSerial(
+      a3 = battor_device_mapping.GetBattOrPathFromPhoneSerial(
                'Phone3', serial_map_file=filename)
     finally:
       os.remove(filename)
@@ -326,16 +326,16 @@
     map_dict = battor_device_mapping.ReadSerialMapFile(
         os.path.join(curr_dir, 'test', 'data', 'test_serial_map.json'))
     self.assertEquals(len(map_dict.keys()), 3)
-    self.assertEquals(map_dict['Phone1'], 'Battor1')
-    self.assertEquals(map_dict['Phone2'], 'Battor2')
-    self.assertEquals(map_dict['Phone3'], 'Battor3')
+    self.assertEquals(map_dict['Phone1'], 'BattOr1')
+    self.assertEquals(map_dict['Phone2'], 'BattOr2')
+    self.assertEquals(map_dict['Phone3'], 'BattOr3')
 
 original_PPTSM = find_usb_devices.GetAllPhysicalPortToSerialMaps
 original_PPTTM = find_usb_devices.GetAllPhysicalPortToTTYMaps
-original_GBL = battor_device_mapping.GetBattorList
+original_GBL = battor_device_mapping.GetBattOrList
 original_GBNDM = find_usb_devices.GetBusNumberToDeviceTreeMap
-original_IB = battor_device_mapping.IsBattor
-original_GBSM = battor_device_mapping.GetBattorSerialNumbers
+original_IB = battor_device_mapping.IsBattOr
+original_GBSM = battor_device_mapping.GetBattOrSerialNumbers
 
 def setup_battor_test(serial, tty, battor, bser=None):
   serial_mapper = mock.Mock(return_value=serial)
@@ -346,19 +346,19 @@
   battor_serials = mock.Mock(return_value=bser)
   find_usb_devices.GetAllPhysicalPortToSerialMaps = serial_mapper
   find_usb_devices.GetAllPhysicalPortToTTYMaps = tty_mapper
-  battor_device_mapping.GetBattorList = battor_lister
+  battor_device_mapping.GetBattOrList = battor_lister
   find_usb_devices.GetBusNumberToDeviceTreeMap = devtree
-  battor_device_mapping.IsBattor = is_battor
-  battor_device_mapping.GetBattorSerialNumbers = battor_serials
+  battor_device_mapping.IsBattOr = is_battor
+  battor_device_mapping.GetBattOrSerialNumbers = battor_serials
 
-class BattorMappingTest(unittest.TestCase):
+class BattOrMappingTest(unittest.TestCase):
   def tearDown(self):
     find_usb_devices.GetAllPhysicalPortToSerialMaps = original_PPTSM
     find_usb_devices.GetAllPhysicalPortToTTYMaps = original_PPTTM
-    battor_device_mapping.GetBattorList = original_GBL
+    battor_device_mapping.GetBattOrList = original_GBL
     find_usb_devices.GetBusNumberToDeviceTreeMap = original_GBNDM
-    battor_device_mapping.IsBattor = original_IB
-    battor_device_mapping.GetBattorSerialNumbers = original_GBSM
+    battor_device_mapping.IsBattOr = original_IB
+    battor_device_mapping.GetBattOrSerialNumbers = original_GBSM
 
   def test_generate_serial_map(self):
     setup_battor_test([{1:'Phn1', 2:'Phn2', 3:'Phn3'},
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/utils/lsusb.py b/sdk/platform-tools/systrace/catapult/devil/devil/utils/lsusb.py
index d6306df..6cbf256 100644
--- a/sdk/platform-tools/systrace/catapult/devil/devil/utils/lsusb.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/utils/lsusb.py
@@ -7,6 +7,8 @@
 
 from devil.utils import cmd_helper
 
+logger = logging.getLogger(__name__)
+
 _COULDNT_OPEN_ERROR_RE = re.compile(r'Couldn\'t open device.*')
 _INDENTATION_RE = re.compile(r'^( *)')
 _LSUSB_BUS_DEVICE_RE = re.compile(r'^Bus (\d{3}) Device (\d{3}): (.*)')
@@ -22,7 +24,56 @@
   device = {'bus': bus_id, 'device': dev_id}
   depth_stack = [device]
 
-  # TODO(jbudorick): Add documentation for parsing.
+  # This builds a nested dict -- a tree, basically -- that corresponds
+  # to the lsusb output. It looks first for a line containing
+  #
+  #   "Bus <bus number> Device <device number>: ..."
+  #
+  # and uses that to create the root node. It then parses all remaining
+  # lines as a tree, with the indentation level determining the
+  # depth of the new node.
+  #
+  # This expects two kinds of lines:
+  #   - "groups", which take the form
+  #       "<Group name>:"
+  #     and typically have children, and
+  #   - "entries", which take the form
+  #       "<entry name>   <entry value>  <possible entry description>"
+  #     and typically do not have children (but can).
+  #
+  # This maintains a stack containing all current ancestor nodes in
+  # order to add new nodes to the proper place in the tree.
+  # The stack is added to when a new node is parsed. Nodes are removed
+  # from the stack when they are either at the same indentation level as
+  # or a deeper indentation level than the current line.
+  #
+  # e.g. the following lsusb output:
+  #
+  # Bus 123 Device 456: School bus
+  # Device Descriptor:
+  #   bDeviceClass 5 Actual School Bus
+  #   Configuration Descriptor:
+  #     bLength 20 Rows
+  #
+  # would produce the following dict:
+  #
+  # {
+  #   'bus': 123,
+  #   'device': 456,
+  #   'desc': 'School bus',
+  #   'Device Descriptor': {
+  #     'bDeviceClass': {
+  #       '_value': '5',
+  #       '_desc': 'Actual School Bus',
+  #     },
+  #     'Configuration Descriptor': {
+  #       'bLength': {
+  #         '_value': '20',
+  #         '_desc': 'Rows',
+  #       },
+  #     },
+  #   }
+  # }
   for line in raw_output.splitlines():
     # Ignore blank lines.
     if not line:
@@ -34,24 +85,29 @@
     m = _LSUSB_BUS_DEVICE_RE.match(line)
     if m:
       if m.group(1) != bus_id:
-        logging.warning(
+        logger.warning(
             'Expected bus_id value: %r, seen %r', bus_id, m.group(1))
       if m.group(2) != dev_id:
-        logging.warning(
+        logger.warning(
             'Expected dev_id value: %r, seen %r', dev_id, m.group(2))
       device['desc'] = m.group(3)
       continue
 
+    # Skip any lines that aren't indented, as they're not part of the
+    # device descriptor.
     indent_match = _INDENTATION_RE.match(line)
     if not indent_match:
       continue
 
+    # Determine the indentation depth.
     depth = 1 + len(indent_match.group(1)) / 2
     if depth > len(depth_stack):
-      logging.error(
+      logger.error(
           'lsusb parsing error: unexpected indentation: "%s"', line)
       continue
 
+    # Pop everything off the depth stack that isn't a parent of
+    # this element.
     while depth < len(depth_stack):
       depth_stack.pop()
 
@@ -74,7 +130,7 @@
       depth_stack.append(new_entry)
       continue
 
-    logging.error('lsusb parsing error: unrecognized line: "%s"', line)
+    logger.error('lsusb parsing error: unrecognized line: "%s"', line)
 
   return device
 
@@ -92,7 +148,7 @@
         devices.append(_lsusbv_on_device(bus_num, dev_num))
       except cmd_helper.TimeoutError:
         # Will be blacklisted if it is in expected device file, but times out.
-        logging.info('lsusb -v %s:%s timed out.', bus_num, dev_num)
+        logger.info('lsusb -v %s:%s timed out.', bus_num, dev_num)
   return devices
 
 def raw_lsusb():
@@ -104,6 +160,15 @@
   except KeyError:
     return None
 
+def _is_android_device(device):
+  try:
+    # Hubs are not android devices.
+    if device['Device Descriptor']['bDeviceClass']['_value'] == '9':
+      return False
+  except KeyError:
+    pass
+  return get_lsusb_serial(device) is not None
+
 def get_android_devices():
-  return [serial for serial in (get_lsusb_serial(d) for d in lsusb())
-          if serial]
+  android_devices = (d for d in lsusb() if _is_android_device(d))
+  return [get_lsusb_serial(d) for d in android_devices]
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/utils/markdown.py b/sdk/platform-tools/systrace/catapult/devil/devil/utils/markdown.py
index cb2dc2b..54e7ed5 100755
--- a/sdk/platform-tools/systrace/catapult/devil/devil/utils/markdown.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/utils/markdown.py
@@ -1,8 +1,69 @@
+#! /usr/bin/env python
 # Copyright 2016 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
 import argparse
+import imp
+import os
+import re
+import sys
+import textwrap
+import types
+
+# A markdown code block template: https://goo.gl/9EsyRi
+_CODE_BLOCK_FORMAT = '''```{language}
+{code}
+```
+'''
+
+_DEVIL_ROOT = os.path.abspath(os.path.join(
+    os.path.dirname(__file__), '..', '..'))
+
+
+def md_bold(raw_text):
+  """Returns markdown-formatted bold text."""
+  return '**%s**' % md_escape(raw_text, characters='*')
+
+
+def md_code(raw_text, language):
+  """Returns a markdown-formatted code block in the given language."""
+  return _CODE_BLOCK_FORMAT.format(
+      language=language or '',
+      code=md_escape(raw_text, characters='`'))
+
+
+def md_escape(raw_text, characters='*_'):
+  """Escapes * and _."""
+  def escape_char(m):
+    return '\\%s' % m.group(0)
+  pattern = '[%s]' % re.escape(characters)
+  return re.sub(pattern, escape_char, raw_text)
+
+
+def md_heading(raw_text, level):
+  """Returns markdown-formatted heading."""
+  adjusted_level = min(max(level, 0), 6)
+  return '%s%s%s' % (
+      '#' * adjusted_level, ' ' if adjusted_level > 0 else '', raw_text)
+
+
+def md_inline_code(raw_text):
+  """Returns markdown-formatted inline code."""
+  return '`%s`' % md_escape(raw_text, characters='`')
+
+
+def md_italic(raw_text):
+  """Returns markdown-formatted italic text."""
+  return '*%s*' % md_escape(raw_text, characters='*')
+
+
+def md_link(link_text, link_target):
+  """returns a markdown-formatted link."""
+  return '[%s](%s)' % (
+      md_escape(link_text, characters=']'),
+      md_escape(link_target, characters=')'))
+
 
 class MarkdownHelpFormatter(argparse.HelpFormatter):
   """A really bare-bones argparse help formatter that generates valid markdown.
@@ -25,28 +86,25 @@
   def _format_usage(self, usage, actions, groups, prefix):
     usage_text = super(MarkdownHelpFormatter, self)._format_usage(
         usage, actions, groups, prefix)
-    return '\n```\n%s\n```\n\n' % usage_text
+    return md_code(usage_text, language=None)
 
   #override
   def format_help(self):
-    self._root_section.heading = '# %s' % self._prog
+    self._root_section.heading = md_heading(self._prog, level=1)
     return super(MarkdownHelpFormatter, self).format_help()
 
   #override
   def start_section(self, heading):
-    super(MarkdownHelpFormatter, self).start_section('## **%s**' % heading)
+    super(MarkdownHelpFormatter, self).start_section(
+        md_heading(heading, level=2))
 
   #override
   def _format_action(self, action):
     lines = []
     action_header = self._format_action_invocation(action)
-    lines.append('### **%s** ' % action_header)
+    lines.append(md_heading(action_header, level=3))
     if action.help:
-      lines.append('')
-      lines.append('```')
-      help_text = self._expand_help(action)
-      lines.extend(self._split_lines(help_text, 80))
-      lines.append('```')
+      lines.append(md_code(self._expand_help(action), language=None))
     lines.extend(['', ''])
     return '\n'.join(lines)
 
@@ -69,6 +127,194 @@
 
 
 def add_md_help_argument(parser):
+  """Adds --md-help to the given argparse.ArgumentParser.
+
+  Running a script with --md-help will print the help text for that script
+  as valid markdown.
+
+  Args:
+    parser: The ArgumentParser to which --md-help should be added.
+  """
   parser.add_argument('--md-help', action=MarkdownHelpAction,
                       help='print Markdown-formatted help text and exit.')
 
+
+def load_module_from_path(module_path):
+  """Load a module given only the path name.
+
+  Also loads package modules as necessary.
+
+  Args:
+    module_path: An absolute path to a python module.
+  Returns:
+    The module object for the given path.
+  """
+  module_names = [os.path.splitext(os.path.basename(module_path))[0]]
+  d = os.path.dirname(module_path)
+
+  while os.path.exists(os.path.join(d, '__init__.py')):
+    module_names.append(os.path.basename(d))
+    d = os.path.dirname(d)
+
+  d = [d]
+
+  module = None
+  full_module_name = ''
+  for package_name in reversed(module_names):
+    if module:
+      d = module.__path__
+      full_module_name += '.'
+    r = imp.find_module(package_name, d)
+    full_module_name += package_name
+    module = imp.load_module(full_module_name, *r)
+  return module
+
+
+def md_module(module_obj, module_path=None, module_link=None):
+  """Write markdown documentation for a class.
+
+  Documents public classes and functions.
+
+  Args:
+    class_obj: a types.TypeType object for the class that should be
+      documented.
+  Returns:
+    A list of markdown-formatted lines.
+  """
+  def should_doc(name):
+    return (type(module_obj.__dict__[name]) != types.ModuleType
+            and not name.startswith('_'))
+
+  stuff_to_doc = sorted(
+    obj for name, obj in module_obj.__dict__.iteritems()
+    if should_doc(name))
+
+  classes_to_doc = []
+  functions_to_doc = []
+
+  for s in stuff_to_doc:
+    if type(s) == types.TypeType:
+      classes_to_doc.append(s)
+    elif type(s) == types.FunctionType:
+      functions_to_doc.append(s)
+
+  command = ['devil/utils/markdown.py']
+  if module_link:
+    command.extend(['--module-link', module_link])
+  if module_path:
+    command.append(os.path.relpath(module_path, _DEVIL_ROOT))
+
+  heading_text = module_obj.__name__
+  if module_link:
+    heading_text = md_link(heading_text, module_link)
+
+  content = [
+      md_heading(heading_text, level=1),
+      '',
+      md_italic('This page was autogenerated by %s'
+          % md_inline_code(' '.join(command))),
+      '',
+  ]
+
+  for c in classes_to_doc:
+    content += md_class(c)
+  for f in functions_to_doc:
+    content += md_function(f)
+
+  print '\n'.join(content)
+
+  return 0
+
+
+def md_class(class_obj):
+  """Write markdown documentation for a class.
+
+  Documents public methods. Does not currently document subclasses.
+
+  Args:
+    class_obj: a types.TypeType object for the class that should be
+      documented.
+  Returns:
+    A list of markdown-formatted lines.
+  """
+  content = [md_heading(md_escape(class_obj.__name__), level=2)]
+  content.append('')
+  if class_obj.__doc__:
+    content.extend(md_docstring(class_obj.__doc__))
+
+  def should_doc(name, obj):
+    return (type(obj) == types.FunctionType
+            and (name.startswith('__') or not name.startswith('_')))
+
+  methods_to_doc = sorted(
+      obj for name, obj in class_obj.__dict__.iteritems()
+      if should_doc(name, obj))
+
+  for m in methods_to_doc:
+    content.extend(md_function(m, class_obj=class_obj))
+
+  return content
+
+
+def md_docstring(docstring):
+  """Write a markdown-formatted docstring.
+
+  Returns:
+    A list of markdown-formatted lines.
+  """
+  content = []
+  lines = textwrap.dedent(docstring).splitlines()
+  content.append(md_escape(lines[0]))
+  lines = lines[1:]
+  while lines and (not lines[0] or lines[0].isspace()):
+    lines = lines[1:]
+
+  if not all(l.isspace() for l in lines):
+    content.append(md_code('\n'.join(lines), language=None))
+    content.append('')
+  return content
+
+
+def md_function(func_obj, class_obj=None):
+  """Write markdown documentation for a function.
+
+  Args:
+    func_obj: a types.FunctionType object for the function that should be
+      documented.
+  Returns:
+    A list of markdown-formatted lines.
+  """
+  if class_obj:
+    heading_text = '%s.%s' % (class_obj.__name__, func_obj.__name__)
+  else:
+    heading_text = func_obj.__name__
+  content = [md_heading(md_escape(heading_text), level=3)]
+  content.append('')
+
+  if func_obj.__doc__:
+    content.extend(md_docstring(func_obj.__doc__))
+
+  return content
+
+
+def main(raw_args):
+  """Write markdown documentation for the module at the provided path.
+
+  Args:
+    raw_args: the raw command-line args. Usually sys.argv[1:].
+  Returns:
+    An integer exit code. 0 for success, non-zero for failure.
+  """
+  parser = argparse.ArgumentParser()
+  parser.add_argument('--module-link')
+  parser.add_argument('module_path', type=os.path.realpath)
+  args = parser.parse_args(raw_args)
+
+  return md_module(
+      load_module_from_path(args.module_path),
+      module_link=args.module_link)
+
+
+if __name__ == '__main__':
+  sys.exit(main(sys.argv[1:]))
+
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/utils/markdown_test.py b/sdk/platform-tools/systrace/catapult/devil/devil/utils/markdown_test.py
new file mode 100755
index 0000000..323776c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/utils/markdown_test.py
@@ -0,0 +1,121 @@
+#! /usr/bin/env python
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import sys
+import textwrap
+import unittest
+
+if __name__ == '__main__':
+  sys.path.append(
+      os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..')))
+
+from devil.utils import markdown
+
+
+class MarkdownTest(unittest.TestCase):
+
+  def testBold(self):
+    raw = 'foo'
+    self.assertEquals('**foo**', markdown.md_bold(raw))
+
+  def testBoldContainsStars(self):
+    raw = '*foo*'
+    self.assertEquals('**\\*foo\\***', markdown.md_bold(raw))
+
+  def testCode(self):
+    raw = textwrap.dedent("""\
+        class MarkdownTest(unittest.TestCase):
+          def testCode(self):
+            pass""")
+
+    expected = textwrap.dedent("""\
+        ```python
+        class MarkdownTest(unittest.TestCase):
+          def testCode(self):
+            pass
+        ```
+        """)
+    actual = markdown.md_code(raw, language='python')
+    self.assertEquals(expected, actual)
+
+  def testCodeContainsTicks(self):
+    raw = textwrap.dedent("""\
+        This is sample markdown.
+        ```c
+        // This is a sample code block.
+        int main(int argc, char** argv) {
+          return 0;
+        }
+        ```""")
+
+    expected = textwrap.dedent("""\
+        ```
+        This is sample markdown.
+        \\`\\`\\`c
+        // This is a sample code block.
+        int main(int argc, char** argv) {
+          return 0;
+        }
+        \\`\\`\\`
+        ```
+        """)
+    actual = markdown.md_code(raw, language=None)
+    self.assertEquals(expected, actual)
+
+  def testEscape(self):
+    raw = 'text_with_underscores *and stars*'
+    expected = 'text\\_with\\_underscores \\*and stars\\*'
+    actual = markdown.md_escape(raw)
+    self.assertEquals(expected, actual)
+
+  def testHeading1(self):
+    raw = 'Heading 1'
+    self.assertEquals('# Heading 1', markdown.md_heading(raw, level=1))
+
+  def testHeading5(self):
+    raw = 'Heading 5'
+    self.assertEquals('##### Heading 5', markdown.md_heading(raw, level=5))
+
+  def testHeading10(self):
+    raw = 'Heading 10'
+    self.assertEquals('###### Heading 10', markdown.md_heading(raw, level=10))
+
+  def testInlineCode(self):
+    raw = 'devil.utils.markdown_test'
+    self.assertEquals(
+        '`devil.utils.markdown_test`', markdown.md_inline_code(raw))
+
+  def testInlineCodeContainsTicks(self):
+    raw = 'this contains `backticks`'
+    self.assertEquals(
+        '`this contains \\`backticks\\``', markdown.md_inline_code(raw))
+
+  def testItalic(self):
+    raw = 'bar'
+    self.assertEquals('*bar*', markdown.md_italic(raw))
+
+  def testItalicContainsStars(self):
+    raw = '*bar*'
+    self.assertEquals('*\\*bar\\**', markdown.md_italic(raw))
+
+  def testLink(self):
+    link_text = 'Devil home'
+    link_target = (
+        'https://github.com/catapult-project/catapult/tree/master/devil')
+    expected = (
+        '[Devil home]'
+        '(https://github.com/catapult-project/catapult/tree/master/devil)')
+    self.assertEquals(expected, markdown.md_link(link_text, link_target))
+
+  def testLinkTextContainsBracket(self):
+    link_text = 'foo [] bar'
+    link_target = 'https://www.google.com'
+    expected = '[foo [\\] bar](https://www.google.com)'
+    self.assertEquals(expected, markdown.md_link(link_text, link_target))
+
+
+if __name__ == '__main__':
+  unittest.main(verbosity=2)
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/utils/reset_usb.py b/sdk/platform-tools/systrace/catapult/devil/devil/utils/reset_usb.py
index 3f3b30a..0335227 100755
--- a/sdk/platform-tools/systrace/catapult/devil/devil/utils/reset_usb.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/utils/reset_usb.py
@@ -6,13 +6,21 @@
 import argparse
 import fcntl
 import logging
+import os
 import re
 import sys
 
+if __name__ == '__main__':
+  sys.path.append(
+      os.path.abspath(os.path.join(os.path.dirname(__file__),
+                                   '..', '..')))
+
 from devil.android import device_errors
 from devil.utils import lsusb
 from devil.utils import run_tests_helper
 
+logger = logging.getLogger(__name__)
+
 _INDENTATION_RE = re.compile(r'^( *)')
 _LSUSB_BUS_DEVICE_RE = re.compile(r'^Bus (\d{3}) Device (\d{3}):')
 _LSUSB_ENTRY_RE = re.compile(r'^ *([^ ]+) +([^ ]+) *([^ ].*)?$')
@@ -25,7 +33,7 @@
   """Reset the USB device with the given bus and device."""
   usb_file_path = '/dev/bus/usb/%03d/%03d' % (bus, device)
   with open(usb_file_path, 'w') as usb_file:
-    logging.debug('fcntl.ioctl(%s, %d)', usb_file_path, _USBDEVFS_RESET)
+    logger.debug('fcntl.ioctl(%s, %d)', usb_file_path, _USBDEVFS_RESET)
     fcntl.ioctl(usb_file, _USBDEVFS_RESET)
 
 
@@ -45,7 +53,8 @@
     reset_usb(bus, device)
   else:
     raise device_errors.DeviceUnreachableError(
-        'Unable to determine bus or device for device %s' % serial)
+        'Unable to determine bus(%s) or device(%s) for device %s'
+         % (bus, device, serial))
 
 
 def reset_all_android_devices():
@@ -63,13 +72,15 @@
         reset_usb(bus, device)
         serial = lsusb.get_lsusb_serial(device_info)
         if serial:
-          logging.info('Reset USB device (bus: %03d, device: %03d, serial: %s)',
+          logger.info(
+              'Reset USB device (bus: %03d, device: %03d, serial: %s)',
               bus, device, serial)
         else:
-          logging.info('Reset USB device (bus: %03d, device: %03d)',
+          logger.info(
+              'Reset USB device (bus: %03d, device: %03d)',
               bus, device)
       except IOError:
-        logging.error(
+        logger.error(
             'Failed to reset USB device (bus: %03d, device: %03d)',
             bus, device)
 
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/utils/signal_handler.py b/sdk/platform-tools/systrace/catapult/devil/devil/utils/signal_handler.py
index 566bef9..1230f8d 100644
--- a/sdk/platform-tools/systrace/catapult/devil/devil/utils/signal_handler.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/utils/signal_handler.py
@@ -7,6 +7,23 @@
 
 
 @contextlib.contextmanager
+def SignalHandler(signalnum, handler):
+  """Sets the signal handler for the given signal in the wrapped context.
+
+  Args:
+    signum: The signal for which a handler should be added.
+    additional_handler: The handler to add.
+  """
+  existing_handler = signal.getsignal(signalnum)
+
+  try:
+    signal.signal(signalnum, handler)
+    yield
+  finally:
+    signal.signal(signalnum, existing_handler)
+
+
+@contextlib.contextmanager
 def AddSignalHandler(signalnum, additional_handler):
   """Adds a signal handler for the given signal in the wrapped context.
 
@@ -24,7 +41,8 @@
       existing_handler(signum, frame)
     additional_handler(signum, frame)
 
-  signal.signal(signalnum, handler)
-  yield
-  signal.signal(signalnum, existing_handler)
-
+  try:
+    signal.signal(signalnum, handler)
+    yield
+  finally:
+    signal.signal(signalnum, existing_handler)
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/utils/test/data/test_serial_map.json b/sdk/platform-tools/systrace/catapult/devil/devil/utils/test/data/test_serial_map.json
index 452df3f..f068281 100644
--- a/sdk/platform-tools/systrace/catapult/devil/devil/utils/test/data/test_serial_map.json
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/utils/test/data/test_serial_map.json
@@ -1 +1 @@
-[{"phone": "Phone1", "battor": "Battor1"}, {"phone": "Phone2", "battor": "Battor2"}, {"phone": "Phone3", "battor": "Battor3"}]
+[{"phone": "Phone1", "battor": "BattOr1"}, {"phone": "Phone2", "battor": "BattOr2"}, {"phone": "Phone3", "battor": "BattOr3"}]
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/utils/timeout_retry.py b/sdk/platform-tools/systrace/catapult/devil/devil/utils/timeout_retry.py
index e4b5ff8..d230462 100644
--- a/sdk/platform-tools/systrace/catapult/devil/devil/utils/timeout_retry.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/utils/timeout_retry.py
@@ -12,6 +12,8 @@
 from devil.utils import reraiser_thread
 from devil.utils import watchdog_timer
 
+logger = logging.getLogger(__name__)
+
 
 class TimeoutRetryThreadGroup(reraiser_thread.ReraiserThreadGroup):
 
@@ -102,7 +104,7 @@
     if timeout_thread_group:
       # pylint: disable=no-member
       msg.append('(%.1fs)' % timeout_thread_group.GetElapsedTime())
-    logging.info(' '.join(msg))
+    logger.info(' '.join(msg))
     if result:
       return result
     if timeout_thread_group:
@@ -156,7 +158,7 @@
         thread_group.JoinAll(watcher=thread_group.GetWatcher(), timeout=60,
                              error_log_func=error_log_func)
         if thread_group.IsAlive():
-          logging.info('Still working on %s', desc)
+          logger.info('Still working on %s', desc)
         else:
           return thread_group.GetAllReturnValues()[0]
     except reraiser_thread.TimeoutError as e:
diff --git a/sdk/platform-tools/systrace/catapult/devil/devil/utils/zip_utils.py b/sdk/platform-tools/systrace/catapult/devil/devil/utils/zip_utils.py
index d799463..eaa6a2d 100644
--- a/sdk/platform-tools/systrace/catapult/devil/devil/utils/zip_utils.py
+++ b/sdk/platform-tools/systrace/catapult/devil/devil/utils/zip_utils.py
@@ -6,6 +6,8 @@
 import os
 import zipfile
 
+logger = logging.getLogger(__name__)
+
 
 def WriteToZipFile(zip_file, path, arc_path):
   """Recursively write |path| to |zip_file| as |arc_path|.
@@ -18,14 +20,14 @@
   if os.path.isdir(path):
     for dir_path, _, file_names in os.walk(path):
       dir_arc_path = os.path.join(arc_path, os.path.relpath(dir_path, path))
-      logging.debug('dir:  %s -> %s', dir_path, dir_arc_path)
+      logger.debug('dir:  %s -> %s', dir_path, dir_arc_path)
       zip_file.write(dir_path, dir_arc_path, zipfile.ZIP_STORED)
       for f in file_names:
         file_path = os.path.join(dir_path, f)
         file_arc_path = os.path.join(dir_arc_path, f)
-        logging.debug('file: %s -> %s', file_path, file_arc_path)
+        logger.debug('file: %s -> %s', file_path, file_arc_path)
         zip_file.write(file_path, file_arc_path, zipfile.ZIP_DEFLATED)
   else:
-    logging.debug('file: %s -> %s', path, arc_path)
+    logger.debug('file: %s -> %s', path, arc_path)
     zip_file.write(path, arc_path, zipfile.ZIP_DEFLATED)
 
diff --git a/sdk/platform-tools/systrace/catapult/devil/docs/adb_wrapper.md b/sdk/platform-tools/systrace/catapult/devil/docs/adb_wrapper.md
new file mode 100644
index 0000000..a8dc3b0
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/devil/docs/adb_wrapper.md
@@ -0,0 +1,388 @@
+# [devil.android.sdk.adb_wrapper](https://github.com/catapult-project/catapult/blob/master/devil/devil/android/sdk/adb_wrapper.py)
+
+*This page was autogenerated by `devil/utils/markdown.py --module-link https://github.com/catapult-project/catapult/blob/master/devil/devil/android/sdk/adb_wrapper.py`*
+
+## DeviceStat
+
+DeviceStat(st\_mode, st\_size, st\_time)
+### DeviceStat.\_\_repr\_\_
+
+Return a nicely formatted representation string
+### DeviceStat.\_\_getnewargs\_\_
+
+Return self as a plain tuple.  Used by copy and pickle.
+### DeviceStat.\_\_getstate\_\_
+
+Exclude the OrderedDict from pickling
+## AdbWrapper
+
+A wrapper around a local Android Debug Bridge executable.
+### AdbWrapper.GetDeviceSerial
+
+Gets the device serial number associated with this object.
+```
+    Returns:
+      Device serial number as a string.
+```
+
+
+### AdbWrapper.Push
+
+Pushes a file from the host to the device.
+```
+    Args:
+      local: Path on the host filesystem.
+      remote: Path on the device filesystem.
+      timeout: (optional) Timeout per try in seconds.
+      retries: (optional) Number of retries to attempt.
+```
+
+
+### AdbWrapper.Pull
+
+Pulls a file from the device to the host.
+```
+    Args:
+      remote: Path on the device filesystem.
+      local: Path on the host filesystem.
+      timeout: (optional) Timeout per try in seconds.
+      retries: (optional) Number of retries to attempt.
+```
+
+
+### AdbWrapper.Shell
+
+Runs a shell command on the device.
+```
+    Args:
+      command: A string with the shell command to run.
+      expect_status: (optional) Check that the command's exit status matches
+        this value. Default is 0. If set to None the test is skipped.
+      timeout: (optional) Timeout per try in seconds.
+      retries: (optional) Number of retries to attempt.
+
+    Returns:
+      The output of the shell command as a string.
+
+    Raises:
+      device_errors.AdbCommandFailedError: If the exit status doesn't match
+        |expect_status|.
+```
+
+
+### AdbWrapper.IterShell
+
+Runs a shell command and returns an iterator over its output lines.
+```
+    Args:
+      command: A string with the shell command to run.
+      timeout: Timeout in seconds.
+
+    Yields:
+      The output of the command line by line.
+```
+
+
+### AdbWrapper.Ls
+
+List the contents of a directory on the device.
+```
+    Args:
+      path: Path on the device filesystem.
+      timeout: (optional) Timeout per try in seconds.
+      retries: (optional) Number of retries to attempt.
+
+    Returns:
+      A list of pairs (filename, stat) for each file found in the directory,
+      where the stat object has the properties: st_mode, st_size, and st_time.
+
+    Raises:
+      AdbCommandFailedError if |path| does not specify a valid and accessible
+          directory in the device, or the output of "adb ls" command is less
+          than four columns
+```
+
+
+### AdbWrapper.Logcat
+
+Get an iterable over the logcat output.
+```
+    Args:
+      clear: If true, clear the logcat.
+      dump: If true, dump the current logcat contents.
+      filter_specs: If set, a list of specs to filter the logcat.
+      logcat_format: If set, the format in which the logcat should be output.
+        Options include "brief", "process", "tag", "thread", "raw", "time",
+        "threadtime", and "long"
+      ring_buffer: If set, a list of alternate ring buffers to request.
+        Options include "main", "system", "radio", "events", "crash" or "all".
+        The default is equivalent to ["main", "system", "crash"].
+      iter_timeout: If set and neither clear nor dump is set, the number of
+        seconds to wait between iterations. If no line is found before the
+        given number of seconds elapses, the iterable will yield None.
+      timeout: (optional) If set, timeout per try in seconds. If clear or dump
+        is set, defaults to DEFAULT_TIMEOUT.
+      retries: (optional) If clear or dump is set, the number of retries to
+        attempt. Otherwise, does nothing.
+
+    Yields:
+      logcat output line by line.
+```
+
+
+### AdbWrapper.Forward
+
+Forward socket connections from the local socket to the remote socket.
+```
+    Sockets are specified by one of:
+      tcp:<port>
+      localabstract:<unix domain socket name>
+      localreserved:<unix domain socket name>
+      localfilesystem:<unix domain socket name>
+      dev:<character device name>
+      jdwp:<process pid> (remote only)
+
+    Args:
+      local: The host socket.
+      remote: The device socket.
+      allow_rebind: A boolean indicating whether adb may rebind a local socket;
+        otherwise, the default, an exception is raised if the local socket is
+        already being forwarded.
+      timeout: (optional) Timeout per try in seconds.
+      retries: (optional) Number of retries to attempt.
+```
+
+
+### AdbWrapper.ForwardRemove
+
+Remove a forward socket connection.
+```
+    Args:
+      local: The host socket.
+      timeout: (optional) Timeout per try in seconds.
+      retries: (optional) Number of retries to attempt.
+```
+
+
+### AdbWrapper.ForwardList
+
+List all currently forwarded socket connections.
+```
+    Args:
+      timeout: (optional) Timeout per try in seconds.
+      retries: (optional) Number of retries to attempt.
+    Returns:
+      The output of adb forward --list as a string.
+```
+
+
+### AdbWrapper.JDWP
+
+List of PIDs of processes hosting a JDWP transport.
+```
+    Args:
+      timeout: (optional) Timeout per try in seconds.
+      retries: (optional) Number of retries to attempt.
+
+    Returns:
+      A list of PIDs as strings.
+```
+
+
+### AdbWrapper.Install
+
+Install an apk on the device.
+```
+    Args:
+      apk_path: Host path to the APK file.
+      forward_lock: (optional) If set forward-locks the app.
+      allow_downgrade: (optional) If set, allows for downgrades.
+      reinstall: (optional) If set reinstalls the app, keeping its data.
+      sd_card: (optional) If set installs on the SD card.
+      timeout: (optional) Timeout per try in seconds.
+      retries: (optional) Number of retries to attempt.
+```
+
+
+### AdbWrapper.InstallMultiple
+
+Install an apk with splits on the device.
+```
+    Args:
+      apk_paths: Host path to the APK file.
+      forward_lock: (optional) If set forward-locks the app.
+      reinstall: (optional) If set reinstalls the app, keeping its data.
+      sd_card: (optional) If set installs on the SD card.
+      allow_downgrade: (optional) Allow versionCode downgrade.
+      partial: (optional) Package ID if apk_paths doesn't include all .apks.
+      timeout: (optional) Timeout per try in seconds.
+      retries: (optional) Number of retries to attempt.
+```
+
+
+### AdbWrapper.Uninstall
+
+Remove the app |package| from the device.
+```
+    Args:
+      package: The package to uninstall.
+      keep_data: (optional) If set keep the data and cache directories.
+      timeout: (optional) Timeout per try in seconds.
+      retries: (optional) Number of retries to attempt.
+```
+
+
+### AdbWrapper.Backup
+
+Write an archive of the device's data to |path|.
+```
+    Args:
+      path: Local path to store the backup file.
+      packages: List of to packages to be backed up.
+      apk: (optional) If set include the .apk files in the archive.
+      shared: (optional) If set buckup the device's SD card.
+      nosystem: (optional) If set exclude system applications.
+      include_all: (optional) If set back up all installed applications and
+        |packages| is optional.
+      timeout: (optional) Timeout per try in seconds.
+      retries: (optional) Number of retries to attempt.
+```
+
+
+### AdbWrapper.Restore
+
+Restore device contents from the backup archive.
+```
+    Args:
+      path: Host path to the backup archive.
+      timeout: (optional) Timeout per try in seconds.
+      retries: (optional) Number of retries to attempt.
+```
+
+
+### AdbWrapper.WaitForDevice
+
+Block until the device is online.
+```
+    Args:
+      timeout: (optional) Timeout per try in seconds.
+      retries: (optional) Number of retries to attempt.
+```
+
+
+### AdbWrapper.GetState
+
+Get device state.
+```
+    Args:
+      timeout: (optional) Timeout per try in seconds.
+      retries: (optional) Number of retries to attempt.
+
+    Returns:
+      One of 'offline', 'bootloader', or 'device'.
+```
+
+
+### AdbWrapper.GetDevPath
+
+Gets the device path.
+```
+    Args:
+      timeout: (optional) Timeout per try in seconds.
+      retries: (optional) Number of retries to attempt.
+
+    Returns:
+      The device path (e.g. usb:3-4)
+```
+
+
+### AdbWrapper.Remount
+
+Remounts the /system partition on the device read-write.
+### AdbWrapper.Reboot
+
+Reboots the device.
+```
+    Args:
+      to_bootloader: (optional) If set reboots to the bootloader.
+      timeout: (optional) Timeout per try in seconds.
+      retries: (optional) Number of retries to attempt.
+```
+
+
+### AdbWrapper.Root
+
+Restarts the adbd daemon with root permissions, if possible.
+```
+    Args:
+      timeout: (optional) Timeout per try in seconds.
+      retries: (optional) Number of retries to attempt.
+```
+
+
+### AdbWrapper.Emu
+
+Runs an emulator console command.
+```
+    See http://developer.android.com/tools/devices/emulator.html#console
+
+    Args:
+      cmd: The command to run on the emulator console.
+      timeout: (optional) Timeout per try in seconds.
+      retries: (optional) Number of retries to attempt.
+
+    Returns:
+      The output of the emulator console command.
+```
+
+
+### AdbWrapper.DisableVerity
+
+Disable Marshmallow's Verity security feature
+### AdbWrapper.EnableVerity
+
+Enable Marshmallow's Verity security feature
+### AdbWrapper.\_\_init\_\_
+
+Initializes the AdbWrapper.
+```
+    Args:
+      device_serial: The device serial number as a string.
+```
+
+
+### AdbWrapper.\_\_eq\_\_
+
+Consider instances equal if they refer to the same device.
+```
+    Args:
+      other: The instance to compare equality with.
+
+    Returns:
+      True if the instances are considered equal, false otherwise.
+```
+
+
+### AdbWrapper.\_\_str\_\_
+
+The string representation of an instance.
+```
+    Returns:
+      The device serial number as a string.
+```
+
+
+### AdbWrapper.\_\_repr\_\_
+
+### VerifyLocalFileExists
+
+Verifies a local file exists.
+```
+  Args:
+    path: Path to the local file.
+
+  Raises:
+    IOError: If the file doesn't exist.
+```
+
+
diff --git a/sdk/platform-tools/systrace/catapult/devil/docs/device_utils.md b/sdk/platform-tools/systrace/catapult/devil/docs/device_utils.md
new file mode 100644
index 0000000..b281b26
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/devil/docs/device_utils.md
@@ -0,0 +1,1041 @@
+# [devil.android.device_utils](https://github.com/catapult-project/catapult/blob/master/devil/devil/android/device_utils.py)
+
+*This page was autogenerated by `devil/utils/markdown.py --module-link https://github.com/catapult-project/catapult/blob/master/devil/devil/android/device_utils.py`*
+
+## DeviceUtils
+
+### DeviceUtils.\_\_init\_\_
+
+DeviceUtils constructor.
+```
+    Args:
+      device: Either a device serial, an existing AdbWrapper instance, or an
+        an existing AndroidCommands instance.
+      enable_device_files_cache: For PushChangedFiles(), cache checksums of
+        pushed files rather than recomputing them on a subsequent call.
+      default_timeout: An integer containing the default number of seconds to
+        wait for an operation to complete if no explicit value is provided.
+      default_retries: An integer containing the default number or times an
+        operation should be retried on failure if no explicit value is provided.
+```
+
+
+### DeviceUtils.\_\_eq\_\_
+
+Checks whether |other| refers to the same device as |self|.
+```
+    Args:
+      other: The object to compare to. This can be a basestring, an instance
+        of adb_wrapper.AdbWrapper, or an instance of DeviceUtils.
+    Returns:
+      Whether |other| refers to the same device as |self|.
+```
+
+
+### DeviceUtils.\_\_lt\_\_
+
+Compares two instances of DeviceUtils.
+```
+    This merely compares their serial numbers.
+
+    Args:
+      other: The instance of DeviceUtils to compare to.
+    Returns:
+      Whether |self| is less than |other|.
+```
+
+
+### DeviceUtils.\_\_str\_\_
+
+Returns the device serial.
+### DeviceUtils.NeedsSU
+
+Checks whether 'su' is needed to access protected resources.
+```
+    Args:
+      timeout: timeout in seconds
+      retries: number of retries
+
+    Returns:
+      True if 'su' is available on the device and is needed to to access
+        protected resources; False otherwise if either 'su' is not available
+        (e.g. because the device has a user build), or not needed (because adbd
+        already has root privileges).
+
+    Raises:
+      CommandTimeoutError on timeout.
+      DeviceUnreachableError on missing device.
+```
+
+
+### DeviceUtils.IsOnline
+
+Checks whether the device is online.
+```
+    Args:
+      timeout: timeout in seconds
+      retries: number of retries
+
+    Returns:
+      True if the device is online, False otherwise.
+
+    Raises:
+      CommandTimeoutError on timeout.
+```
+
+
+### DeviceUtils.HasRoot
+
+Checks whether or not adbd has root privileges.
+```
+    Args:
+      timeout: timeout in seconds
+      retries: number of retries
+
+    Returns:
+      True if adbd has root privileges, False otherwise.
+
+    Raises:
+      CommandTimeoutError on timeout.
+      DeviceUnreachableError on missing device.
+```
+
+
+### DeviceUtils.EnableRoot
+
+Restarts adbd with root privileges.
+```
+    Args:
+      timeout: timeout in seconds
+      retries: number of retries
+
+    Raises:
+      CommandFailedError if root could not be enabled.
+      CommandTimeoutError on timeout.
+```
+
+
+### DeviceUtils.IsUserBuild
+
+Checks whether or not the device is running a user build.
+```
+    Args:
+      timeout: timeout in seconds
+      retries: number of retries
+
+    Returns:
+      True if the device is running a user build, False otherwise (i.e. if
+        it's running a userdebug build).
+
+    Raises:
+      CommandTimeoutError on timeout.
+      DeviceUnreachableError on missing device.
+```
+
+
+### DeviceUtils.GetExternalStoragePath
+
+Get the device's path to its SD card.
+```
+    Args:
+      timeout: timeout in seconds
+      retries: number of retries
+
+    Returns:
+      The device's path to its SD card.
+
+    Raises:
+      CommandFailedError if the external storage path could not be determined.
+      CommandTimeoutError on timeout.
+      DeviceUnreachableError on missing device.
+```
+
+
+### DeviceUtils.GetApplicationPaths
+
+Get the paths of the installed apks on the device for the given package.
+```
+    Args:
+      package: Name of the package.
+
+    Returns:
+      List of paths to the apks on the device for the given package.
+```
+
+
+### DeviceUtils.GetApplicationVersion
+
+Get the version name of a package installed on the device.
+```
+    Args:
+      package: Name of the package.
+
+    Returns:
+      A string with the version name or None if the package is not found
+      on the device.
+```
+
+
+### DeviceUtils.GetApplicationDataDirectory
+
+Get the data directory on the device for the given package.
+```
+    Args:
+      package: Name of the package.
+
+    Returns:
+      The package's data directory.
+    Raises:
+      CommandFailedError if the package's data directory can't be found,
+        whether because it's not installed or otherwise.
+```
+
+
+### DeviceUtils.WaitUntilFullyBooted
+
+Wait for the device to fully boot.
+```
+    This means waiting for the device to boot, the package manager to be
+    available, and the SD card to be ready. It can optionally mean waiting
+    for wifi to come up, too.
+
+    Args:
+      wifi: A boolean indicating if we should wait for wifi to come up or not.
+      timeout: timeout in seconds
+      retries: number of retries
+
+    Raises:
+      CommandFailedError on failure.
+      CommandTimeoutError if one of the component waits times out.
+      DeviceUnreachableError if the device becomes unresponsive.
+```
+
+
+### DeviceUtils.Reboot
+
+Reboot the device.
+```
+    Args:
+      block: A boolean indicating if we should wait for the reboot to complete.
+      wifi: A boolean indicating if we should wait for wifi to be enabled after
+        the reboot. The option has no effect unless |block| is also True.
+      timeout: timeout in seconds
+      retries: number of retries
+
+    Raises:
+      CommandTimeoutError on timeout.
+      DeviceUnreachableError on missing device.
+```
+
+
+### DeviceUtils.Install
+
+Install an APK.
+```
+    Noop if an identical APK is already installed.
+
+    Args:
+      apk: An ApkHelper instance or string containing the path to the APK.
+      allow_downgrade: A boolean indicating if we should allow downgrades.
+      reinstall: A boolean indicating if we should keep any existing app data.
+      permissions: Set of permissions to set. If not set, finds permissions with
+          apk helper. To set no permissions, pass [].
+      timeout: timeout in seconds
+      retries: number of retries
+
+    Raises:
+      CommandFailedError if the installation fails.
+      CommandTimeoutError if the installation times out.
+      DeviceUnreachableError on missing device.
+```
+
+
+### DeviceUtils.InstallSplitApk
+
+Install a split APK.
+```
+    Noop if all of the APK splits are already installed.
+
+    Args:
+      base_apk: An ApkHelper instance or string containing the path to the base
+          APK.
+      split_apks: A list of strings of paths of all of the APK splits.
+      allow_downgrade: A boolean indicating if we should allow downgrades.
+      reinstall: A boolean indicating if we should keep any existing app data.
+      allow_cached_props: Whether to use cached values for device properties.
+      permissions: Set of permissions to set. If not set, finds permissions with
+          apk helper. To set no permissions, pass [].
+      timeout: timeout in seconds
+      retries: number of retries
+
+    Raises:
+      CommandFailedError if the installation fails.
+      CommandTimeoutError if the installation times out.
+      DeviceUnreachableError on missing device.
+      DeviceVersionError if device SDK is less than Android L.
+```
+
+
+### DeviceUtils.Uninstall
+
+Remove the app |package\_name| from the device.
+```
+    This is a no-op if the app is not already installed.
+
+    Args:
+      package_name: The package to uninstall.
+      keep_data: (optional) Whether to keep the data and cache directories.
+      timeout: Timeout in seconds.
+      retries: Number of retries.
+
+    Raises:
+      CommandFailedError if the uninstallation fails.
+      CommandTimeoutError if the uninstallation times out.
+      DeviceUnreachableError on missing device.
+```
+
+
+### DeviceUtils.RunShellCommand
+
+Run an ADB shell command.
+```
+    The command to run |cmd| should be a sequence of program arguments or else
+    a single string.
+
+    When |cmd| is a sequence, it is assumed to contain the name of the command
+    to run followed by its arguments. In this case, arguments are passed to the
+    command exactly as given, without any further processing by the shell. This
+    allows to easily pass arguments containing spaces or special characters
+    without having to worry about getting quoting right. Whenever possible, it
+    is recomended to pass |cmd| as a sequence.
+
+    When |cmd| is given as a string, it will be interpreted and run by the
+    shell on the device.
+
+    This behaviour is consistent with that of command runners in cmd_helper as
+    well as Python's own subprocess.Popen.
+
+    TODO(perezju) Change the default of |check_return| to True when callers
+      have switched to the new behaviour.
+
+    Args:
+      cmd: A string with the full command to run on the device, or a sequence
+        containing the command and its arguments.
+      check_return: A boolean indicating whether or not the return code should
+        be checked.
+      cwd: The device directory in which the command should be run.
+      env: The environment variables with which the command should be run.
+      run_as: A string containing the package as which the command should be
+        run.
+      as_root: A boolean indicating whether the shell command should be run
+        with root privileges.
+      single_line: A boolean indicating if only a single line of output is
+        expected.
+      large_output: Uses a work-around for large shell command output. Without
+        this large output will be truncated.
+      raw_output: Whether to only return the raw output
+          (no splitting into lines).
+      timeout: timeout in seconds
+      retries: number of retries
+
+    Returns:
+      If single_line is False, the output of the command as a list of lines,
+      otherwise, a string with the unique line of output emmited by the command
+      (with the optional newline at the end stripped).
+
+    Raises:
+      AdbCommandFailedError if check_return is True and the exit code of
+        the command run on the device is non-zero.
+      CommandFailedError if single_line is True but the output contains two or
+        more lines.
+      CommandTimeoutError on timeout.
+      DeviceUnreachableError on missing device.
+```
+
+
+### DeviceUtils.KillAll
+
+Kill all processes with the given name on the device.
+```
+    Args:
+      process_name: A string containing the name of the process to kill.
+      exact: A boolean indicating whether to kill all processes matching
+             the string |process_name| exactly, or all of those which contain
+             |process_name| as a substring. Defaults to False.
+      signum: An integer containing the signal number to send to kill. Defaults
+              to SIGKILL (9).
+      as_root: A boolean indicating whether the kill should be executed with
+               root privileges.
+      blocking: A boolean indicating whether we should wait until all processes
+                with the given |process_name| are dead.
+      quiet: A boolean indicating whether to ignore the fact that no processes
+             to kill were found.
+      timeout: timeout in seconds
+      retries: number of retries
+
+    Returns:
+      The number of processes attempted to kill.
+
+    Raises:
+      CommandFailedError if no process was killed and |quiet| is False.
+      CommandTimeoutError on timeout.
+      DeviceUnreachableError on missing device.
+```
+
+
+### DeviceUtils.StartActivity
+
+Start package's activity on the device.
+```
+    Args:
+      intent_obj: An Intent object to send.
+      blocking: A boolean indicating whether we should wait for the activity to
+                finish launching.
+      trace_file_name: If present, a string that both indicates that we want to
+                       profile the activity and contains the path to which the
+                       trace should be saved.
+      force_stop: A boolean indicating whether we should stop the activity
+                  before starting it.
+      timeout: timeout in seconds
+      retries: number of retries
+
+    Raises:
+      CommandFailedError if the activity could not be started.
+      CommandTimeoutError on timeout.
+      DeviceUnreachableError on missing device.
+```
+
+
+### DeviceUtils.StartInstrumentation
+
+### DeviceUtils.BroadcastIntent
+
+Send a broadcast intent.
+```
+    Args:
+      intent: An Intent to broadcast.
+      timeout: timeout in seconds
+      retries: number of retries
+
+    Raises:
+      CommandTimeoutError on timeout.
+      DeviceUnreachableError on missing device.
+```
+
+
+### DeviceUtils.GoHome
+
+Return to the home screen and obtain launcher focus.
+```
+    This command launches the home screen and attempts to obtain
+    launcher focus until the timeout is reached.
+
+    Args:
+      timeout: timeout in seconds
+      retries: number of retries
+
+    Raises:
+      CommandTimeoutError on timeout.
+      DeviceUnreachableError on missing device.
+```
+
+
+### DeviceUtils.ForceStop
+
+Close the application.
+```
+    Args:
+      package: A string containing the name of the package to stop.
+      timeout: timeout in seconds
+      retries: number of retries
+
+    Raises:
+      CommandTimeoutError on timeout.
+      DeviceUnreachableError on missing device.
+```
+
+
+### DeviceUtils.ClearApplicationState
+
+Clear all state for the given package.
+```
+    Args:
+      package: A string containing the name of the package to stop.
+      permissions: List of permissions to set after clearing data.
+      timeout: timeout in seconds
+      retries: number of retries
+
+    Raises:
+      CommandTimeoutError on timeout.
+      DeviceUnreachableError on missing device.
+```
+
+
+### DeviceUtils.SendKeyEvent
+
+Sends a keycode to the device.
+```
+    See the devil.android.sdk.keyevent module for suitable keycode values.
+
+    Args:
+      keycode: A integer keycode to send to the device.
+      timeout: timeout in seconds
+      retries: number of retries
+
+    Raises:
+      CommandTimeoutError on timeout.
+      DeviceUnreachableError on missing device.
+```
+
+
+### DeviceUtils.PushChangedFiles
+
+Push files to the device, skipping files that don't need updating.
+```
+    When a directory is pushed, it is traversed recursively on the host and
+    all files in it are pushed to the device as needed.
+    Additionally, if delete_device_stale option is True,
+    files that exist on the device but don't exist on the host are deleted.
+
+    Args:
+      host_device_tuples: A list of (host_path, device_path) tuples, where
+        |host_path| is an absolute path of a file or directory on the host
+        that should be minimially pushed to the device, and |device_path| is
+        an absolute path of the destination on the device.
+      timeout: timeout in seconds
+      retries: number of retries
+      delete_device_stale: option to delete stale files on device
+
+    Raises:
+      CommandFailedError on failure.
+      CommandTimeoutError on timeout.
+      DeviceUnreachableError on missing device.
+```
+
+
+### DeviceUtils.FileExists
+
+Checks whether the given file exists on the device.
+```
+    Arguments are the same as PathExists.
+```
+
+
+### DeviceUtils.PathExists
+
+Checks whether the given path(s) exists on the device.
+```
+    Args:
+      device_path: A string containing the absolute path to the file on the
+                   device, or an iterable of paths to check.
+      as_root: Whether root permissions should be use to check for the existence
+               of the given path(s).
+      timeout: timeout in seconds
+      retries: number of retries
+
+    Returns:
+      True if the all given paths exist on the device, False otherwise.
+
+    Raises:
+      CommandTimeoutError on timeout.
+      DeviceUnreachableError on missing device.
+```
+
+
+### DeviceUtils.RemovePath
+
+Removes the given path(s) from the device.
+```
+    Args:
+      device_path: A string containing the absolute path to the file on the
+                   device, or an iterable of paths to check.
+      force: Whether to remove the path(s) with force (-f).
+      recursive: Whether to remove any directories in the path(s) recursively.
+      as_root: Whether root permissions should be use to remove the given
+               path(s).
+      timeout: timeout in seconds
+      retries: number of retries
+```
+
+
+### DeviceUtils.PullFile
+
+Pull a file from the device.
+```
+    Args:
+      device_path: A string containing the absolute path of the file to pull
+                   from the device.
+      host_path: A string containing the absolute path of the destination on
+                 the host.
+      timeout: timeout in seconds
+      retries: number of retries
+
+    Raises:
+      CommandFailedError on failure.
+      CommandTimeoutError on timeout.
+```
+
+
+### DeviceUtils.ReadFile
+
+Reads the contents of a file from the device.
+```
+    Args:
+      device_path: A string containing the absolute path of the file to read
+                   from the device.
+      as_root: A boolean indicating whether the read should be executed with
+               root privileges.
+      force_pull: A boolean indicating whether to force the operation to be
+          performed by pulling a file from the device. The default is, when the
+          contents are short, to retrieve the contents using cat instead.
+      timeout: timeout in seconds
+      retries: number of retries
+
+    Returns:
+      The contents of |device_path| as a string. Contents are intepreted using
+      universal newlines, so the caller will see them encoded as '
+'. Also,
+      all lines will be terminated.
+
+    Raises:
+      AdbCommandFailedError if the file can't be read.
+      CommandTimeoutError on timeout.
+      DeviceUnreachableError on missing device.
+```
+
+
+### DeviceUtils.WriteFile
+
+Writes |contents| to a file on the device.
+```
+    Args:
+      device_path: A string containing the absolute path to the file to write
+          on the device.
+      contents: A string containing the data to write to the device.
+      as_root: A boolean indicating whether the write should be executed with
+          root privileges (if available).
+      force_push: A boolean indicating whether to force the operation to be
+          performed by pushing a file to the device. The default is, when the
+          contents are short, to pass the contents using a shell script instead.
+      timeout: timeout in seconds
+      retries: number of retries
+
+    Raises:
+      CommandFailedError if the file could not be written on the device.
+      CommandTimeoutError on timeout.
+      DeviceUnreachableError on missing device.
+```
+
+
+### DeviceUtils.ListDirectory
+
+List all files on a device directory.
+```
+    Mirroring os.listdir (and most client expectations) the resulting list
+    does not include the special entries '.' and '..' even if they are present
+    in the directory.
+
+    Args:
+      device_path: A string containing the path of the directory on the device
+                   to list.
+      as_root: A boolean indicating whether the to use root privileges to list
+               the directory contents.
+      timeout: timeout in seconds
+      retries: number of retries
+
+    Returns:
+      A list of filenames for all entries contained in the directory.
+
+    Raises:
+      AdbCommandFailedError if |device_path| does not specify a valid and
+          accessible directory in the device.
+      CommandTimeoutError on timeout.
+      DeviceUnreachableError on missing device.
+```
+
+
+### DeviceUtils.StatDirectory
+
+List file and stat info for all entries on a device directory.
+```
+    Implementation notes: this is currently implemented by parsing the output
+    of 'ls -a -l' on the device. Whether possible and convenient, we attempt to
+    make parsing strict and return values mirroring those of the standard |os|
+    and |stat| Python modules.
+
+    Mirroring os.listdir (and most client expectations) the resulting list
+    does not include the special entries '.' and '..' even if they are present
+    in the directory.
+
+    Args:
+      device_path: A string containing the path of the directory on the device
+                   to list.
+      as_root: A boolean indicating whether the to use root privileges to list
+               the directory contents.
+      timeout: timeout in seconds
+      retries: number of retries
+
+    Returns:
+      A list of dictionaries, each containing the following keys:
+        filename: A string with the file name.
+        st_mode: File permissions, use the stat module to interpret these.
+        st_nlink: Number of hard links (may be missing).
+        st_owner: A string with the user name of the owner.
+        st_group: A string with the group name of the owner.
+        st_rdev_pair: Device type as (major, minior) (only if inode device).
+        st_size: Size of file, in bytes (may be missing for non-regular files).
+        st_mtime: Time of most recent modification, in seconds since epoch
+          (although resolution is in minutes).
+        symbolic_link_to: If entry is a symbolic link, path where it points to;
+          missing otherwise.
+
+    Raises:
+      AdbCommandFailedError if |device_path| does not specify a valid and
+          accessible directory in the device.
+      CommandTimeoutError on timeout.
+      DeviceUnreachableError on missing device.
+```
+
+
+### DeviceUtils.StatPath
+
+Get the stat attributes of a file or directory on the device.
+```
+    Args:
+      device_path: A string containing the path of a file or directory from
+                   which to get attributes.
+      as_root: A boolean indicating whether the to use root privileges to
+               access the file information.
+      timeout: timeout in seconds
+      retries: number of retries
+
+    Returns:
+      A dictionary with the stat info collected; see StatDirectory for details.
+
+    Raises:
+      CommandFailedError if device_path cannot be found on the device.
+      CommandTimeoutError on timeout.
+      DeviceUnreachableError on missing device.
+```
+
+
+### DeviceUtils.FileSize
+
+Get the size of a file on the device.
+```
+    Note: This is implemented by parsing the output of the 'ls' command on
+    the device. On some Android versions, when passing a directory or special
+    file, the size is *not* reported and this function will throw an exception.
+
+    Args:
+      device_path: A string containing the path of a file on the device.
+      as_root: A boolean indicating whether the to use root privileges to
+               access the file information.
+      timeout: timeout in seconds
+      retries: number of retries
+
+    Returns:
+      The size of the file in bytes.
+
+    Raises:
+      CommandFailedError if device_path cannot be found on the device, or
+        its size cannot be determited for some reason.
+      CommandTimeoutError on timeout.
+      DeviceUnreachableError on missing device.
+```
+
+
+### DeviceUtils.GetLanguage
+
+Returns the language setting on the device.
+```
+    Args:
+      cache: Whether to use cached properties when available.
+```
+
+
+### DeviceUtils.SetJavaAsserts
+
+Enables or disables Java asserts.
+```
+    Args:
+      enabled: A boolean indicating whether Java asserts should be enabled
+               or disabled.
+      timeout: timeout in seconds
+      retries: number of retries
+
+    Returns:
+      True if the device-side property changed and a restart is required as a
+      result, False otherwise.
+
+    Raises:
+      CommandTimeoutError on timeout.
+```
+
+
+### DeviceUtils.GetCountry
+
+Returns the country setting on the device.
+```
+    Args:
+      cache: Whether to use cached properties when available.
+```
+
+
+### DeviceUtils.GetProp
+
+Gets a property from the device.
+```
+    Args:
+      property_name: A string containing the name of the property to get from
+                     the device.
+      cache: Whether to use cached properties when available.
+      timeout: timeout in seconds
+      retries: number of retries
+
+    Returns:
+      The value of the device's |property_name| property.
+
+    Raises:
+      CommandTimeoutError on timeout.
+```
+
+
+### DeviceUtils.SetProp
+
+Sets a property on the device.
+```
+    Args:
+      property_name: A string containing the name of the property to set on
+                     the device.
+      value: A string containing the value to set to the property on the
+             device.
+      check: A boolean indicating whether to check that the property was
+             successfully set on the device.
+      timeout: timeout in seconds
+      retries: number of retries
+
+    Raises:
+      CommandFailedError if check is true and the property was not correctly
+        set on the device (e.g. because it is not rooted).
+      CommandTimeoutError on timeout.
+```
+
+
+### DeviceUtils.GetABI
+
+Gets the device main ABI.
+```
+    Args:
+      timeout: timeout in seconds
+      retries: number of retries
+
+    Returns:
+      The device's main ABI name.
+
+    Raises:
+      CommandTimeoutError on timeout.
+```
+
+
+### DeviceUtils.GetPids
+
+Returns the PIDs of processes with the given name.
+```
+    Note that the |process_name| is often the package name.
+
+    Args:
+      process_name: A string containing the process name to get the PIDs for.
+      timeout: timeout in seconds
+      retries: number of retries
+
+    Returns:
+      A dict mapping process name to a list of PIDs for each process that
+      contained the provided |process_name|.
+
+    Raises:
+      CommandTimeoutError on timeout.
+      DeviceUnreachableError on missing device.
+```
+
+
+### DeviceUtils.GetEnforce
+
+Get the current mode of SELinux.
+```
+    Args:
+      timeout: timeout in seconds
+      retries: number of retries
+
+    Returns:
+      True (enforcing), False (permissive), or None (disabled).
+
+    Raises:
+      CommandFailedError on failure.
+      CommandTimeoutError on timeout.
+      DeviceUnreachableError on missing device.
+```
+
+
+### DeviceUtils.SetEnforce
+
+Modify the mode SELinux is running in.
+```
+    Args:
+      enabled: a boolean indicating whether to put SELinux in encorcing mode
+               (if True), or permissive mode (otherwise).
+      timeout: timeout in seconds
+      retries: number of retries
+
+    Raises:
+      CommandFailedError on failure.
+      CommandTimeoutError on timeout.
+      DeviceUnreachableError on missing device.
+```
+
+
+### DeviceUtils.TakeScreenshot
+
+Takes a screenshot of the device.
+```
+    Args:
+      host_path: A string containing the path on the host to save the
+                 screenshot to. If None, a file name in the current
+                 directory will be generated.
+      timeout: timeout in seconds
+      retries: number of retries
+
+    Returns:
+      The name of the file on the host to which the screenshot was saved.
+
+    Raises:
+      CommandFailedError on failure.
+      CommandTimeoutError on timeout.
+      DeviceUnreachableError on missing device.
+```
+
+
+### DeviceUtils.GetMemoryUsageForPid
+
+Gets the memory usage for the given PID.
+```
+    Args:
+      pid: PID of the process.
+      timeout: timeout in seconds
+      retries: number of retries
+
+    Returns:
+      A dict containing memory usage statistics for the PID. May include:
+        Size, Rss, Pss, Shared_Clean, Shared_Dirty, Private_Clean,
+        Private_Dirty, VmHWM
+
+    Raises:
+      CommandTimeoutError on timeout.
+```
+
+
+### DeviceUtils.DismissCrashDialogIfNeeded
+
+Dismiss the error/ANR dialog if present.
+```
+    Returns: Name of the crashed package if a dialog is focused,
+             None otherwise.
+```
+
+
+### DeviceUtils.GetLogcatMonitor
+
+Returns a new LogcatMonitor associated with this device.
+```
+    Parameters passed to this function are passed directly to
+    |logcat_monitor.LogcatMonitor| and are documented there.
+```
+
+
+### DeviceUtils.GetClientCache
+
+Returns client cache.
+### DeviceUtils.LoadCacheData
+
+Initializes the cache from data created using DumpCacheData.
+```
+    The cache is used only if its token matches the one found on the device.
+    This prevents a stale cache from being used (which can happen when sharing
+    devices).
+
+    Args:
+      data: A previously serialized cache (string).
+      timeout: timeout in seconds
+      retries: number of retries
+
+    Returns:
+      Whether the cache was loaded.
+```
+
+
+### DeviceUtils.DumpCacheData
+
+Dumps the current cache state to a string.
+```
+    Args:
+      timeout: timeout in seconds
+      retries: number of retries
+
+    Returns:
+      A serialized cache as a string.
+```
+
+
+### DeviceUtils.RestartAdbd
+
+### DeviceUtils.GrantPermissions
+
+### DeviceUtils.IsScreenOn
+
+Determines if screen is on.
+```
+    Dumpsys input_method exposes screen on/off state. Below is an explination of
+    the states.
+
+    Pre-L:
+      On: mScreenOn=true
+      Off: mScreenOn=false
+    L+:
+      On: mInteractive=true
+      Off: mInteractive=false
+
+    Returns:
+      True if screen is on, false if it is off.
+
+    Raises:
+      device_errors.CommandFailedError: If screen state cannot be found.
+```
+
+
+### DeviceUtils.SetScreen
+
+Turns screen on and off.
+```
+    Args:
+      on: bool to decide state to switch to. True = on False = off.
+```
+
+
+### GetAVDs
+
+Returns a list of Android Virtual Devices.
+```
+  Returns:
+    A list containing the configured AVDs.
+```
+
+
+### RestartServer
+
+Restarts the adb server.
+```
+  Raises:
+    CommandFailedError if we fail to kill or restart the server.
+```
+
+
diff --git a/sdk/platform-tools/systrace/catapult/devil/docs/markdown.md b/sdk/platform-tools/systrace/catapult/devil/docs/markdown.md
new file mode 100644
index 0000000..957dba7
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/devil/docs/markdown.md
@@ -0,0 +1,139 @@
+# [devil.utils.markdown](https://github.com/catapult-project/catapult/blob/master/devil/devil/utils/markdown.py)
+
+*This page was autogenerated by `devil/utils/markdown.py --module-link https://github.com/catapult-project/catapult/blob/master/devil/devil/utils/markdown.py`*
+
+## MarkdownHelpAction
+
+### MarkdownHelpAction.\_\_init\_\_
+
+### MarkdownHelpAction.\_\_call\_\_
+
+## MarkdownHelpFormatter
+
+A really bare-bones argparse help formatter that generates valid markdown.
+```
+  This will generate something like:
+
+  usage
+
+  # **section heading**:
+
+  ## **--argument-one**
+
+  \`\`\`
+  argument-one help text
+  \`\`\`
+
+```
+
+
+### MarkdownHelpFormatter.format\_help
+
+### MarkdownHelpFormatter.start\_section
+
+### md\_bold
+
+Returns markdown-formatted bold text.
+### md\_code
+
+Returns a markdown-formatted code block in the given language.
+### md\_escape
+
+Escapes \* and \_.
+### md\_heading
+
+Returns markdown-formatted heading.
+### md\_inline\_code
+
+Returns markdown-formatted inline code.
+### md\_italic
+
+Returns markdown-formatted italic text.
+### md\_link
+
+returns a markdown-formatted link.
+### add\_md\_help\_argument
+
+Adds --md-help to the given argparse.ArgumentParser.
+```
+  Running a script with --md-help will print the help text for that script
+  as valid markdown.
+
+  Args:
+    parser: The ArgumentParser to which --md-help should be added.
+```
+
+
+### load\_module\_from\_path
+
+Load a module given only the path name.
+```
+  Also loads package modules as necessary.
+
+  Args:
+    module_path: An absolute path to a python module.
+  Returns:
+    The module object for the given path.
+```
+
+
+### md\_module
+
+Write markdown documentation for a class.
+```
+  Documents public classes and functions.
+
+  Args:
+    class_obj: a types.TypeType object for the class that should be
+      documented.
+  Returns:
+    A list of markdown-formatted lines.
+```
+
+
+### md\_class
+
+Write markdown documentation for a class.
+```
+  Documents public methods. Does not currently document subclasses.
+
+  Args:
+    class_obj: a types.TypeType object for the class that should be
+      documented.
+  Returns:
+    A list of markdown-formatted lines.
+```
+
+
+### md\_docstring
+
+Write a markdown-formatted docstring.
+```
+  Returns:
+    A list of markdown-formatted lines.
+```
+
+
+### md\_function
+
+Write markdown documentation for a function.
+```
+  Args:
+    func_obj: a types.FunctionType object for the function that should be
+      documented.
+  Returns:
+    A list of markdown-formatted lines.
+```
+
+
+### main
+
+Write markdown documentation for the module at the provided path.
+```
+  Args:
+    raw_args: the raw command-line args. Usually sys.argv[1:].
+  Returns:
+    An integer exit code. 0 for success, non-zero for failure.
+```
+
+
diff --git a/sdk/platform-tools/systrace/catapult/systrace/bin/adb_profile_chrome_startup b/sdk/platform-tools/systrace/catapult/systrace/bin/adb_profile_chrome_startup
index fed3416..6bd3270 100755
--- a/sdk/platform-tools/systrace/catapult/systrace/bin/adb_profile_chrome_startup
+++ b/sdk/platform-tools/systrace/catapult/systrace/bin/adb_profile_chrome_startup
@@ -13,21 +13,22 @@
 _SYSTRACE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
 sys.path.append(_SYSTRACE_DIR)
 
-from profile_chrome import atrace_tracing_agent
 from profile_chrome import chrome_startup_tracing_agent
 from profile_chrome import flags
 from profile_chrome import profiler
 from profile_chrome import ui
+from systrace import util
+from systrace.tracing_agents import atrace_agent
 
 _CATAPULT_DIR = os.path.join(
     os.path.dirname(os.path.abspath(__file__)), '..', '..')
 sys.path.append(os.path.join(_CATAPULT_DIR, 'devil'))
 
 from devil.android import device_utils
+from devil.android.sdk import adb_wrapper
 
 
-_CHROME_STARTUP_MODULES = [atrace_tracing_agent,
-                           chrome_startup_tracing_agent]
+_CHROME_STARTUP_MODULES = [atrace_agent, chrome_startup_tracing_agent]
 _DEFAULT_CHROME_CATEGORIES = '_DEFAULT_CHROME_CATEGORIES'
 
 
@@ -37,9 +38,10 @@
                                  'Android systrace. See http://dev.chromium.org'
                                  '/developers/how-tos/trace-event-profiling-'
                                  'tool for detailed instructions for '
-                                 'profiling.')
+                                 'profiling.', conflict_handler='resolve')
+  parser = util.get_main_options(parser)
 
-  browsers = sorted(profiler.GetSupportedBrowsers().keys())
+  browsers = sorted(util.get_supported_browsers().keys())
   parser.add_option('-b', '--browser', help='Select among installed browsers. '
                     'One of ' + ', '.join(browsers) + ', "stable" is used by '
                     'default.', type='choice', choices=browsers,
@@ -53,7 +55,7 @@
                     default=5, metavar='N', type='int', dest='trace_time')
 
   parser.add_option_group(chrome_startup_tracing_agent.add_options(parser))
-  parser.add_option_group(atrace_tracing_agent.add_options(parser))
+  parser.add_option_group(atrace_agent.add_options(parser))
   parser.add_option_group(flags.OutputOptions(parser))
 
   return parser
@@ -63,6 +65,14 @@
   parser = _CreateOptionParser()
   options, _ = parser.parse_args()
 
+  if not options.device_serial_number:
+    devices = [a.GetDeviceSerial() for a in adb_wrapper.AdbWrapper.Devices()]
+    if len(devices) == 0:
+      raise RuntimeError('No ADB devices connected.')
+    elif len(devices) >= 2:
+      raise RuntimeError('Multiple devices connected, serial number required')
+    options.device_serial_number = devices[0]
+
   if options.verbose:
     logging.getLogger().setLevel(logging.DEBUG)
 
@@ -71,7 +81,7 @@
     logging.error('Exactly 1 device must be attached.')
     return 1
   device = devices[0]
-  package_info = profiler.GetSupportedBrowsers()[options.browser]
+  package_info = util.get_supported_browsers()[options.browser]
 
   options.device = device
   options.package_info = package_info
@@ -84,8 +94,8 @@
   options.chrome_categories = _DEFAULT_CHROME_CATEGORIES
 
   if options.atrace_categories in ['list', 'help']:
-    ui.PrintMessage('\n'.join(
-        atrace_tracing_agent.AtraceAgent.GetCategories(device)))
+    atrace_agent.list_categories(atrace_agent.get_config(options))
+    print '\n'
     return 0
   result = profiler.CaptureProfile(options,
                                    options.trace_time,
diff --git a/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/agents_unittest.py b/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/agents_unittest.py
deleted file mode 100644
index 8a22381..0000000
--- a/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/agents_unittest.py
+++ /dev/null
@@ -1,45 +0,0 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import unittest
-
-from profile_chrome import profiler
-
-from devil.android import device_utils
-from devil.android.sdk import intent
-from devil.android.sdk import keyevent
-
-
-class BaseAgentTest(unittest.TestCase):
-  def setUp(self):
-    devices = device_utils.DeviceUtils.HealthyDevices()
-    self.browser = 'stable'
-    self.package_info = profiler.GetSupportedBrowsers()[self.browser]
-    self.device = devices[0]
-
-    curr_browser = self.GetChromeProcessID()
-    if curr_browser == None:
-      self.StartBrowser()
-
-  def StartBrowser(self):
-    # Turn on the device screen.
-    self.device.SetScreen(True)
-
-    # Unlock device.
-    self.device.SendKeyEvent(keyevent.KEYCODE_MENU)
-
-    # Start browser.
-    self.device.StartActivity(
-      intent.Intent(activity=self.package_info.activity,
-                    package=self.package_info.package,
-                    data='about:blank',
-                    extras={'create_new_tab': True}),
-      blocking=True, force_stop=True)
-
-  def GetChromeProcessID(self):
-    chrome_processes = self.device.GetPids(self.package_info.package)
-    if (self.package_info.package in chrome_processes and
-        len(chrome_processes[self.package_info.package]) > 0):
-      return chrome_processes[self.package_info.package][0]
-    return None
diff --git a/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/atrace_tracing_agent.py b/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/atrace_tracing_agent.py
deleted file mode 100644
index c98906f..0000000
--- a/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/atrace_tracing_agent.py
+++ /dev/null
@@ -1,164 +0,0 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import optparse
-import py_utils
-import threading
-import zlib
-
-from devil.utils import cmd_helper
-
-from systrace import trace_result
-from systrace import tracing_agents
-
-
-_ATRACE_OPTIONS = [
-    # Compress the trace before sending it over USB.
-    '-z',
-    # Use a large trace buffer to increase the polling interval.
-    '-b', '16384'
-]
-
-# Interval in seconds for sampling atrace data.
-_ATRACE_INTERVAL = 15
-
-# If a custom list of categories is not specified, traces will include
-# these categories (if available on the device).
-_DEFAULT_CATEGORIES = 'sched gfx view dalvik webview input disk am wm'.split()
-
-_TRACING_ON_PATH = '/sys/kernel/debug/tracing/tracing_on'
-
-
-class AtraceAgent(tracing_agents.TracingAgent):
-  def __init__(self, device, ring_buffer):
-    tracing_agents.TracingAgent.__init__(self)
-    self._device = device
-    self._ring_buffer = ring_buffer
-    self._done = threading.Event()
-    self._thread = None
-    self._trace_data = None
-    self._categories = None
-
-  def __repr__(self):
-    return 'atrace'
-
-  @staticmethod
-  def GetCategories(device):
-    return device.RunShellCommand('atrace --list_categories')
-
-  @py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
-  def StartAgentTracing(self, config, timeout=None):
-    self._categories = _ComputeAtraceCategories(config)
-    self._thread = threading.Thread(target=self._CollectData)
-    self._thread.start()
-    return True
-
-  @py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
-  def StopAgentTracing(self, timeout=None):
-    self._done.set()
-    return True
-
-  @py_utils.Timeout(tracing_agents.GET_RESULTS_TIMEOUT)
-  def GetResults(self, timeout=None):
-    self._thread.join()
-    self._thread = None
-    return trace_result.TraceResult('systemTraceEvents', self._trace_data)
-
-  def SupportsExplicitClockSync(self):
-    return False
-
-  def RecordClockSyncMarker(self, sync_id, did_record_sync_marker_callback):
-    # pylint: disable=unused-argument
-    assert self.SupportsExplicitClockSync(), ('Clock sync marker cannot be '
-        'recorded since explicit clock sync is not supported.')
-
-  def IsTracingOn(self):
-    result = self._RunAdbShellCommand(['cat', _TRACING_ON_PATH])
-    return result.strip() == '1'
-
-  def _RunAdbShellCommand(self, command):
-    # We use a separate interface to adb because the one from AndroidCommands
-    # isn't re-entrant.
-    # TODO(jbudorick) Look at providing a way to unhandroll this once the
-    #                 adb rewrite has fully landed.
-    device_param = (['-s', str(self._device)] if str(self._device) else [])
-    cmd = ['adb'] + device_param + ['shell'] + command
-    return cmd_helper.GetCmdOutput(cmd)
-
-  def _RunATraceCommand(self, command):
-    cmd = ['atrace', '--%s' % command] + _ATRACE_OPTIONS + self._categories
-    return self._RunAdbShellCommand(cmd)
-
-  def _ForceStopAtrace(self):
-    # atrace on pre-M Android devices cannot be stopped asynchronously
-    # correctly. Use synchronous mode to force stop.
-    cmd = ['atrace', '-t', '0']
-    return self._RunAdbShellCommand(cmd)
-
-  def _CollectData(self):
-    trace_data = []
-    self._RunATraceCommand('async_start')
-    try:
-      while not self._done.is_set():
-        self._done.wait(_ATRACE_INTERVAL)
-        if not self._ring_buffer or self._done.is_set():
-          trace_data.append(
-              self._DecodeTraceData(self._RunATraceCommand('async_dump')))
-    finally:
-      trace_data.append(
-          self._DecodeTraceData(self._RunATraceCommand('async_stop')))
-      if self.IsTracingOn():
-        self._ForceStopAtrace()
-    self._trace_data = ''.join([zlib.decompress(d) for d in trace_data])
-
-  @staticmethod
-  def _DecodeTraceData(trace_data):
-    try:
-      trace_start = trace_data.index('TRACE:')
-    except ValueError:
-      raise RuntimeError('Atrace start marker not found')
-    trace_data = trace_data[trace_start + 6:]
-
-    # Collapse CRLFs that are added by adb shell.
-    if trace_data.startswith('\r\n'):
-      trace_data = trace_data.replace('\r\n', '\n')
-
-    # Skip the initial newline.
-    return trace_data[1:]
-
-
-class AtraceConfig(tracing_agents.TracingConfig):
-  def __init__(self, atrace_categories, device, ring_buffer):
-    tracing_agents.TracingConfig.__init__(self)
-    self.atrace_categories = atrace_categories
-    self.device = device
-    self.ring_buffer = ring_buffer
-
-
-def try_create_agent(config):
-  if config.atrace_categories:
-    return AtraceAgent(config.device, config.ring_buffer)
-  return None
-
-def add_options(parser):
-  atrace_opts = optparse.OptionGroup(parser, 'Atrace tracing options')
-  atrace_opts.add_option('-s', '--systrace', help='Capture a systrace with '
-                           'the chosen comma-delimited systrace categories. You'
-                           ' can also capture a combined Chrome + systrace by '
-                           'enabling both types of categories. Use "list" to '
-                           'see the available categories. Systrace is disabled'
-                           ' by default. Note that in this case, Systrace is '
-                           'synonymous with Atrace.',
-                           metavar='ATRACE_CATEGORIES',
-                           dest='atrace_categories', default='')
-  return atrace_opts
-
-def get_config(options):
-  return AtraceConfig(options.atrace_categories, options.device,
-                      options.ring_buffer)
-
-def _ComputeAtraceCategories(config):
-  if not config.atrace_categories:
-    return _DEFAULT_CATEGORIES
-  return config.atrace_categories.split(',')
diff --git a/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/atrace_tracing_agent_unittest.py b/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/atrace_tracing_agent_unittest.py
deleted file mode 100644
index 24f3c7e..0000000
--- a/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/atrace_tracing_agent_unittest.py
+++ /dev/null
@@ -1,36 +0,0 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-from profile_chrome import agents_unittest
-from profile_chrome import atrace_tracing_agent
-from systrace import decorators
-
-
-class AtraceAgentTest(agents_unittest.BaseAgentTest):
-  @decorators.ClientOnlyTest
-  def testGetCategories(self):
-    categories = \
-        atrace_tracing_agent.AtraceAgent.GetCategories(self.device)
-    self.assertTrue(categories)
-    assert 'gfx' in ' '.join(categories)
-
-  # TODO(washingtonp): This test throws an error on the Trybot servers when
-  # running profile_chrome's atrace agent. Once systrace uses profile_chrome's
-  # agent instead, this test may not longer need to be disabled.
-  @decorators.Disabled
-  def testTracing(self):
-    categories = 'gfx,input,view'
-    ring_buffer = False
-    agent = atrace_tracing_agent.AtraceAgent(self.device,
-                                             ring_buffer)
-
-    try:
-      agent.StartAgentTracing(atrace_tracing_agent.AtraceConfig(categories,
-          self.device, ring_buffer))
-    finally:
-      agent.StopAgentTracing()
-    result = agent.GetResults()
-
-    self.assertFalse(agent.IsTracingOn())
-    self.assertTrue('CPU#' in result.raw_data)
diff --git a/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/chrome_startup_tracing_agent_unittest.py b/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/chrome_startup_tracing_agent_unittest.py
index 85732da..289e52d 100644
--- a/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/chrome_startup_tracing_agent_unittest.py
+++ b/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/chrome_startup_tracing_agent_unittest.py
@@ -5,8 +5,8 @@
 import json
 
 from profile_chrome import chrome_startup_tracing_agent
-from profile_chrome import agents_unittest
 from systrace import decorators
+from systrace.tracing_agents import agents_unittest
 
 
 class ChromeAgentTest(agents_unittest.BaseAgentTest):
diff --git a/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/chrome_tracing_agent.py b/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/chrome_tracing_agent.py
index 656a559..1e8895b 100644
--- a/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/chrome_tracing_agent.py
+++ b/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/chrome_tracing_agent.py
@@ -14,7 +14,7 @@
 from systrace import tracing_agents
 
 
-_DEFAULT_CHROME_CATEGORIES = '_DEFAULT_CHROME_CATEGORIES'
+DEFAULT_CHROME_CATEGORIES = '_DEFAULT_CHROME_CATEGORIES'
 _HEAP_PROFILE_MMAP_PROPERTY = 'heapprof.mmap'
 
 
@@ -163,8 +163,7 @@
                          'Chrome\'s default categories. Chrome tracing can be '
                          'disabled with "--categories=\'\'". Use "list" to '
                          'see the available categories.',
-                         metavar='CHROME_CATEGORIES', dest='chrome_categories',
-                         default=_DEFAULT_CHROME_CATEGORIES)
+                         metavar='CHROME_CATEGORIES', dest='chrome_categories')
   chrome_opts.add_option('--trace-cc',
                          help='Deprecated, use --trace-frame-viewer.',
                          action='store_true')
diff --git a/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/chrome_tracing_agent_unittest.py b/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/chrome_tracing_agent_unittest.py
index dc38759..25c701a 100644
--- a/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/chrome_tracing_agent_unittest.py
+++ b/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/chrome_tracing_agent_unittest.py
@@ -5,8 +5,8 @@
 import json
 
 from profile_chrome import chrome_tracing_agent
-from profile_chrome import agents_unittest
 from systrace import decorators
+from systrace.tracing_agents import agents_unittest
 
 
 class ChromeAgentTest(agents_unittest.BaseAgentTest):
diff --git a/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/ddms_tracing_agent.py b/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/ddms_tracing_agent.py
index 276a3b9..9d041b9 100644
--- a/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/ddms_tracing_agent.py
+++ b/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/ddms_tracing_agent.py
@@ -27,7 +27,8 @@
     return 'ddms profile'
 
   def _SupportsSampling(self):
-    for line in self._device.RunShellCommand('am --help'):
+    for line in self._device.RunShellCommand(
+        ['am', '--help'], check_return=True):
       if re.match(r'.*am profile start.*--sampling', line):
         return True
     return False
@@ -36,16 +37,17 @@
   def StartAgentTracing(self, config, timeout=None):
     self._output_file = (
         '/data/local/tmp/ddms-profile-%s' % util.GetTraceTimestamp())
-    cmd = 'am profile start '
+    cmd = ['am', 'profile', 'start']
     if self._supports_sampling:
-      cmd += '--sampling %d ' % _DDMS_SAMPLING_FREQUENCY_US
-    cmd += '%s %s' % (self._package, self._output_file)
-    self._device.RunShellCommand(cmd)
+      cmd.extend(['--sampling', str(_DDMS_SAMPLING_FREQUENCY_US)])
+    cmd.extend([self._package, self._output_file])
+    self._device.RunShellCommand(cmd, check_return=True)
     return True
 
   @py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
   def StopAgentTracing(self, timeout=None):
-    self._device.RunShellCommand('am profile stop %s' % self._package)
+    self._device.RunShellCommand(
+        ['am', 'profile', 'stop', self._package], check_return=True)
     return True
 
   @py_utils.Timeout(tracing_agents.GET_RESULTS_TIMEOUT)
diff --git a/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/ddms_tracing_agent_unittest.py b/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/ddms_tracing_agent_unittest.py
index c3878d0..c7269bb 100644
--- a/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/ddms_tracing_agent_unittest.py
+++ b/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/ddms_tracing_agent_unittest.py
@@ -2,9 +2,9 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from profile_chrome import agents_unittest
 from profile_chrome import ddms_tracing_agent
 from systrace import decorators
+from systrace.tracing_agents import agents_unittest
 
 
 class DdmsAgentTest(agents_unittest.BaseAgentTest):
diff --git a/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/main.py b/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/main.py
index b448f92..6cd815c 100755
--- a/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/main.py
+++ b/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/main.py
@@ -15,14 +15,16 @@
 from profile_chrome import flags
 from profile_chrome import perf_tracing_agent
 from profile_chrome import profiler
-from profile_chrome import atrace_tracing_agent
 from profile_chrome import ui
+from systrace import util
+from systrace.tracing_agents import atrace_agent
 
 from devil.android import device_utils
+from devil.android.sdk import adb_wrapper
 
 
 _PROFILE_CHROME_AGENT_MODULES = [chrome_tracing_agent, ddms_tracing_agent,
-                                 perf_tracing_agent, atrace_tracing_agent]
+                                 perf_tracing_agent, atrace_agent]
 
 
 def _CreateOptionParser():
@@ -30,7 +32,9 @@
                                  'from Android browsers. See http://dev.'
                                  'chromium.org/developers/how-tos/trace-event-'
                                  'profiling-tool for detailed instructions for '
-                                 'profiling.')
+                                 'profiling.', conflict_handler='resolve')
+
+  parser = util.get_main_options(parser)
 
   timed_options = optparse.OptionGroup(parser, 'Timed tracing')
   timed_options.add_option('-t', '--time', help='Profile for N seconds and '
@@ -49,7 +53,7 @@
 
   parser.add_option_group(flags.OutputOptions(parser))
 
-  browsers = sorted(profiler.GetSupportedBrowsers().keys())
+  browsers = sorted(util.get_supported_browsers().keys())
   parser.add_option('-b', '--browser', help='Select among installed browsers. '
                     'One of ' + ', '.join(browsers) + ', "stable" is used by '
                     'default.', type='choice', choices=browsers,
@@ -58,10 +62,6 @@
                     action='store_true')
   parser.add_option('-z', '--compress', help='Compress the resulting trace '
                     'with gzip. ', action='store_true')
-  parser.add_option('-d', '--device', help='The Android device ID to use, '
-                    'defaults to the value of ANDROID_SERIAL environment '
-                    'variable. If not specified, only 0 or 1 connected '
-                    'devices are supported.', dest='device_serial_number')
 
   # Add options from profile_chrome agents.
   for module in _PROFILE_CHROME_AGENT_MODULES:
@@ -82,27 +82,28 @@
 When in doubt, just try out --trace-frame-viewer.
 """)
 
+  logging.basicConfig()
+
   if options.verbose:
     logging.getLogger().setLevel(logging.DEBUG)
 
+  if not options.device_serial_number:
+    devices = [a.GetDeviceSerial() for a in adb_wrapper.AdbWrapper.Devices()]
+    if len(devices) == 0:
+      raise RuntimeError('No ADB devices connected.')
+    elif len(devices) >= 2:
+      raise RuntimeError('Multiple devices connected, serial number required')
+    options.device_serial_number = devices[0]
   device = device_utils.DeviceUtils.HealthyDevices(device_arg=
       options.device_serial_number)[0]
-  package_info = profiler.GetSupportedBrowsers()[options.browser]
+  package_info = util.get_supported_browsers()[options.browser]
 
   options.device = device
   options.package_info = package_info
 
-  # Add options that are present in Systrace but not in profile_chrome (since
-  # they both use the same tracing controller).
-  # TODO(washingtonp): Once Systrace uses all of the profile_chrome agents,
-  # manually setting these options will no longer be necessary and should be
-  # removed.
-  options.list_categories = None
-  options.link_assets = None
-  options.asset_dir = None
-  options.timeout = None
-  options.collection_timeout = None
-  options.target = None
+  # Include Chrome categories by default in profile_chrome.
+  if not options.chrome_categories:
+    options.chrome_categories = chrome_tracing_agent.DEFAULT_CHROME_CATEGORIES
 
   if options.chrome_categories in ['list', 'help']:
     ui.PrintMessage('Collecting record categories list...', eol='')
@@ -124,8 +125,8 @@
     return 0
 
   if options.atrace_categories in ['list', 'help']:
-    ui.PrintMessage('\n'.join(
-        atrace_tracing_agent.AtraceAgent.GetCategories(device)))
+    atrace_agent.list_categories(atrace_agent.get_config(options))
+    print '\n'
     return 0
 
   if (perf_tracing_agent.PerfProfilerAgent.IsSupported() and
@@ -138,7 +139,8 @@
     ui.PrintMessage('Time interval or continuous tracing should be specified.')
     return 1
 
-  if options.chrome_categories and 'webview' in options.atrace_categories:
+  if (options.chrome_categories and options.atrace_categories and
+      'webview' in options.atrace_categories):
     logging.warning('Using the "webview" category in atrace together with '
                     'Chrome tracing results in duplicate trace events.')
 
diff --git a/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/perf_tracing_agent.py b/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/perf_tracing_agent.py
index dc03bb8..65831df 100644
--- a/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/perf_tracing_agent.py
+++ b/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/perf_tracing_agent.py
@@ -119,7 +119,8 @@
   @classmethod
   def GetCategories(cls, device):
     perf_binary = cls._PrepareDevice(device)
-    return device.RunShellCommand('%s list' % perf_binary)
+    # Perf binary returns non-zero exit status on "list" command.
+    return device.RunShellCommand([perf_binary, 'list'], check_return=False)
 
   @py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
   def StartAgentTracing(self, config, timeout=None):
@@ -239,7 +240,6 @@
                      'the available sample types.', action='callback',
                      default='', callback=_OptionalValueCallback('cycles'),
                      metavar='PERF_CATEGORIES', dest='perf_categories')
-  parser.add_option_group(options)
   return options
 
 def get_config(options):
diff --git a/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/perf_tracing_agent_unittest.py b/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/perf_tracing_agent_unittest.py
index 8f8a7f7..d8ebe3a 100644
--- a/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/perf_tracing_agent_unittest.py
+++ b/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/perf_tracing_agent_unittest.py
@@ -4,10 +4,10 @@
 
 import json
 
-from profile_chrome import agents_unittest
 from profile_chrome import perf_tracing_agent
 from profile_chrome import ui
 from systrace import decorators
+from systrace.tracing_agents import agents_unittest
 
 
 class PerfProfilerAgentTest(agents_unittest.BaseAgentTest):
diff --git a/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/profiler.py b/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/profiler.py
index a52faf1..1a4009c 100644
--- a/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/profiler.py
+++ b/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/profiler.py
@@ -4,7 +4,6 @@
 
 import time
 
-from devil.android.constants import chrome
 from profile_chrome import chrome_startup_tracing_agent
 from profile_chrome import chrome_tracing_agent
 from profile_chrome import ui
@@ -15,7 +14,7 @@
 
 def _GetResults(trace_results, controller, output, compress, write_json,
                 interval):
-  ui.PrintMessage('Downloading...', eol='')
+  ui.PrintMessage('Downloading...')
 
   # Wait for the trace file to get written.
   time.sleep(1)
@@ -36,10 +35,10 @@
   result = None
   trace_results = output_generator.MergeTraceResultsIfNeeded(trace_results)
   if not write_json:
-    print 'Writing trace HTML'
+    ui.PrintMessage('Writing trace HTML...')
     html_file = trace_results[0].source_name + '.html'
     result = output_generator.GenerateHTMLOutput(trace_results, html_file)
-    print '\nWrote file://%s\n' % result
+    ui.PrintMessage('\nWrote file://%s' % result)
   elif compress and len(trace_results) == 1:
     result = output or trace_results[0].source_name + '.gz'
     util.WriteDataToCompressedFile(trace_results[0].raw_data, result)
@@ -59,19 +58,6 @@
   return result
 
 
-def GetSupportedBrowsers():
-  """Returns the package names of all supported browsers."""
-  # Add aliases for backwards compatibility.
-  supported_browsers = {
-    'stable': chrome.PACKAGE_INFO['chrome_stable'],
-    'beta': chrome.PACKAGE_INFO['chrome_beta'],
-    'dev': chrome.PACKAGE_INFO['chrome_dev'],
-    'build': chrome.PACKAGE_INFO['chrome'],
-  }
-  supported_browsers.update(chrome.PACKAGE_INFO)
-  return supported_browsers
-
-
 def CaptureProfile(options, interval, modules, output=None,
                    compress=False, write_json=False):
   """Records a profiling trace saves the result to a file.
@@ -102,15 +88,17 @@
     result = controller.StartTracing()
     trace_type = controller.GetTraceType()
     if not result:
-      print 'Trace starting failed.'
+      ui.PrintMessage('Trace starting failed.')
     if interval:
       ui.PrintMessage(('Capturing %d-second %s. Press Enter to stop early...' %
                      (interval, trace_type)), eol='')
       ui.WaitForEnter(interval)
     else:
       ui.PrintMessage('Capturing %s. Press Enter to stop...' % trace_type,
-                     eol='')
+                      eol='')
       raw_input()
+
+    ui.PrintMessage('Stopping...')
     all_results = controller.StopTracing()
   finally:
     if interval:
diff --git a/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/profiler_unittest.py b/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/profiler_unittest.py
index cd7af95..9b35753 100644
--- a/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/profiler_unittest.py
+++ b/sdk/platform-tools/systrace/catapult/systrace/profile_chrome/profiler_unittest.py
@@ -18,7 +18,7 @@
   def setUp(self):
     ui.EnableTestMode()
     self._tracing_options = tracing_controller.TracingControllerConfig(None,
-        None, None, None, None, None, None, None, None, None)
+        None, None, None, None, None, None, None, None)
 
   @decorators.ClientOnlyTest
   def testCaptureBasicProfile(self):
diff --git a/sdk/platform-tools/systrace/catapult/systrace/systrace/output_generator.py b/sdk/platform-tools/systrace/catapult/systrace/systrace/output_generator.py
index 6f09d1e..f0ff06a 100644
--- a/sdk/platform-tools/systrace/catapult/systrace/systrace/output_generator.py
+++ b/sdk/platform-tools/systrace/catapult/systrace/systrace/output_generator.py
@@ -143,7 +143,7 @@
   for candidate in merge_candidates[1:]:
     json_data = candidate.raw_data
     for key, value in json_data.items():
-      if not str(key) in merged_data or str(key) in json_data:
+      if not str(key) in merged_data or not merged_data[str(key)]:
         merged_data[str(key)] = value
 
   return ([trace_result.TraceResult('merged-data', json.dumps(merged_data))]
diff --git a/sdk/platform-tools/systrace/catapult/systrace/systrace/run_systrace.py b/sdk/platform-tools/systrace/catapult/systrace/systrace/run_systrace.py
index 815daa7..1bf9e8d 100755
--- a/sdk/platform-tools/systrace/catapult/systrace/systrace/run_systrace.py
+++ b/sdk/platform-tools/systrace/catapult/systrace/systrace/run_systrace.py
@@ -26,7 +26,6 @@
 import optparse
 import os
 import time
-from distutils.spawn import find_executable
 
 _SYSTRACE_DIR = os.path.abspath(
     os.path.join(os.path.dirname(__file__), os.path.pardir))
@@ -41,6 +40,7 @@
 from devil import devil_env
 from devil.android.sdk import adb_wrapper
 from systrace import systrace_runner
+from systrace import util
 from systrace.tracing_agents import atrace_agent
 from systrace.tracing_agents import atrace_from_file_agent
 from systrace.tracing_agents import battor_trace_agent
@@ -50,12 +50,6 @@
                battor_trace_agent, ftrace_agent]
 
 
-def _get_default_serial():
-  if 'ANDROID_SERIAL' in os.environ:
-    return os.environ['ANDROID_SERIAL']
-  return None
-
-
 def parse_options(argv):
   """Parses and checks the command-line options.
 
@@ -65,53 +59,13 @@
   """
   usage = 'Usage: %prog [options] [category1 [category2 ...]]'
   desc = 'Example: %prog -b 32768 -t 15 gfx input view sched freq'
-  parser = optparse.OptionParser(usage=usage, description=desc)
-  parser.add_option('-o', dest='output_file', help='write trace output to FILE',
-                    default=None, metavar='FILE')
-  parser.add_option('-t', '--time', dest='trace_time', type='int',
-                    help='trace for N seconds', metavar='N')
+  parser = optparse.OptionParser(usage=usage, description=desc,
+                                 conflict_handler='resolve')
+  parser = util.get_main_options(parser)
+
   parser.add_option('-l', '--list-categories', dest='list_categories',
                     default=False, action='store_true',
                     help='list the available categories and exit')
-  parser.add_option('-j', '--json', dest='write_json',
-                    default=False, action='store_true',
-                    help='write a JSON file')
-  parser.add_option('--link-assets', dest='link_assets', default=False,
-                    action='store_true',
-                    help='(deprecated)')
-  parser.add_option('--from-file', dest='from_file', action='store',
-                    help='read the trace from a file (compressed) rather than'
-                    'running a live trace')
-  parser.add_option('--asset-dir', dest='asset_dir', default='trace-viewer',
-                    type='string', help='(deprecated)')
-  parser.add_option('-e', '--serial', dest='device_serial_number',
-                    default=_get_default_serial(),
-                    type='string', help='adb device serial number')
-  parser.add_option('--target', dest='target', default='android', type='string',
-                    help='chose tracing target (android or linux)')
-  parser.add_option('--timeout', dest='timeout', type='int',
-                    help='timeout for start and stop tracing (seconds)')
-  parser.add_option('--collection-timeout', dest='collection_timeout',
-                    type='int', help='timeout for data collection (seconds)')
-
-  atrace_ftrace_options = optparse.OptionGroup(parser,
-                                               'Atrace and Ftrace options')
-  atrace_ftrace_options.add_option('-b', '--buf-size', dest='trace_buf_size',
-                                   type='int', help='use a trace buffer size '
-                                   ' of N KB', metavar='N')
-  atrace_ftrace_options.add_option('--no-fix-threads', dest='fix_threads',
-                                   default=True, action='store_false',
-                                   help='don\'t fix missing or truncated '
-                                   'thread names')
-  atrace_ftrace_options.add_option('--no-fix-tgids', dest='fix_tgids',
-                                   default=True, action='store_false',
-                                   help='Do not run extra commands to restore'
-                                   ' missing thread to thread group id '
-                                   'mappings.')
-  atrace_ftrace_options.add_option('--no-fix-circular', dest='fix_circular',
-                                   default=True, action='store_false',
-                                   help='don\'t fix truncated circular traces')
-  parser.add_option_group(atrace_ftrace_options)
 
   # Add the other agent parsing options to the parser. For Systrace on the
   # command line, all agents are added. For Android, only the compatible agents
@@ -137,10 +91,27 @@
 
   return (options, categories)
 
+def find_adb():
+  """Finds adb on the path.
+
+  This method is provided to avoid the issue of diskutils.spawn's
+  find_executable which first searches the current directory before
+  searching $PATH. That behavior results in issues where systrace.py
+  uses a different adb than the one in the path.
+  """
+  paths = os.environ['PATH'].split(os.pathsep)
+  executable = 'adb'
+  if sys.platform == 'win32':
+    executable = executable + '.exe'
+  for p in paths:
+    f = os.path.join(p, executable)
+    if os.path.isfile(f):
+      return f
+  return None
 
 def initialize_devil():
   """Initialize devil to use adb from $PATH"""
-  adb_path = find_executable('adb')
+  adb_path = find_adb()
   if adb_path is None:
     print >> sys.stderr, "Unable to find adb, is it in your path?"
     sys.exit(1)
@@ -174,6 +145,10 @@
       raise RuntimeError('Categories are only valid for atrace/ftrace. Target '
                          'platform must be either Android or Linux.')
 
+  # Include atrace categories by default in Systrace.
+  if options.target == 'android' and not options.atrace_categories:
+    options.atrace_categories = atrace_agent.DEFAULT_CATEGORIES
+
   if options.target == 'android' and not options.from_file:
     initialize_devil()
     if not options.device_serial_number:
diff --git a/sdk/platform-tools/systrace/catapult/systrace/systrace/systrace_trace_viewer.html b/sdk/platform-tools/systrace/catapult/systrace/systrace/systrace_trace_viewer.html
index 776ec40..b651314 100644
--- a/sdk/platform-tools/systrace/catapult/systrace/systrace/systrace_trace_viewer.html
+++ b/sdk/platform-tools/systrace/catapult/systrace/systrace/systrace_trace_viewer.html
@@ -126,8 +126,6 @@
       }
 
       table {
-        font-size: 12px;
-
         flex: 1 1 auto;
         align-self: stretch;
         border-collapse: separate;
@@ -141,10 +139,14 @@
         vertical-align: top;
       }
 
-      tr:focus,
-      td:focus {
-        outline: 1px dotted rgba(0,0,0,0.1);
-        outline-offset: 0;
+      table > tbody:focus {
+        outline: none;
+      }
+      table > tbody:focus[selection-mode="row"] > tr[selected],
+      table > tbody:focus[selection-mode="cell"] > tr > td[selected],
+      table > tbody:focus > tr.empty-row > td {
+        outline: 1px dotted #666666;
+        outline-offset: -1px;
       }
 
       button.toggle-button {
@@ -231,16 +233,19 @@
         border-top: 1px solid #ffffff;
       }
 
-      expand-button {
-        -webkit-user-select: none;
-        display: inline-block;
-        cursor: pointer;
-        font-size: 9px;
-        min-width: 8px;
-        max-width: 8px;
+      :host([zebra]) table tbody tr:nth-child(even) {
+        background-color: #f4f4f4;
       }
 
-      .button-expanded {
+      expand-button {
+        -webkit-user-select: none;
+        cursor: pointer;
+        margin-right: 3px;
+        font-size: smaller;
+        height: 1rem;
+      }
+
+      expand-button.button-expanded {
         transform: rotate(90deg);
       }
     </style>
@@ -297,22 +302,22 @@
   <template>
     <style>
     :host {
-      display: block;
+      display: flex;
+      flex-direction: row;
+      justify-content: flex-end;
       position: relative;
       /* Limit the sparkline's negative z-index to the span only. */
       isolation: isolate;
     }
-    #content.right-align {
-      text-align: right;
-      position: relative;
-      display: block;
+
+    :host(.left-align) {
+      justify-content: flex-start;
     }
-    #significance.better, #content.better {
-      color: green;
+
+    :host(.inline) {
+      display: inline-flex;
     }
-    #significance.worse, #content.worse {
-      color: red;
-    }
+
     #sparkline {
       width: 0%;
       position: absolute;
@@ -342,19 +347,85 @@
       background-color: hsla(0, 100%, 88%, .75);
       border-color: hsl(0, 100%, 80%);
     }
-    #warning {
-      margin-left: 4px;
-      font-size: 66%;
+
+    #content, #significance, #warning {
+      flex-grow: 0;
     }
-    #significance {
-      font-family: "Noto Color Emoji", "Apple Color Emoji", "Segoe UI Emoji", Times, Symbola, Aegyptus, Code2000, Code2001, Code2002, Musica, serif, LastResort;
-      font-size: 13pt;
+    #content.better {
+      color: green;
+    }
+    #content.worse {
+      color: red;
+    }
+
+    #significance svg {
+      margin-left: 4px;
+      display: none;
+      height: 1em;
+      vertical-align: text-top;
+      stroke-width: 4;
+      fill: rgba(0, 0, 0, 0);
+    }
+    #significance #insignificant {
+      stroke: black;
+    }
+    #significance #significantly_better {
+      stroke: green;
+    }
+    #significance #significantly_worse {
+      stroke: red;
+    }
+
+    #warning {
+      display: none;
+      margin-left: 4px;
+      height: 1em;
+      vertical-align: text-top;
+      stroke-width: 0;
+    }
+    #warning path {
+      fill: rgb(255, 185, 185);
+    }
+    #warning rect {
+      fill: red;
     }
     </style>
+
     <span id="sparkline"></span>
+
     <span id="content"></span>
-    <span id="significance"></span>
-    <span id="warning" style="display:none">⚠</span>
+
+    <span id="significance">
+      
+      <svg id="insignificant" viewBox="0 0 128 128">
+        <circle cx="64" cy="64" r="60"></circle>
+        <circle cx="44" cy="44" r="4"></circle>
+        <circle cx="84" cy="44" r="4"></circle>
+        <line x1="36" x2="92" y1="80" y2="80"></line>
+      </svg>
+
+      
+      <svg id="significantly_better" viewBox="0 0 128 128">
+        <circle cx="64" cy="64" r="60"></circle>
+        <circle cx="44" cy="44" r="4"></circle>
+        <circle cx="84" cy="44" r="4"></circle>
+        <path d="M 28 64 Q 64 128 100 64"></path>
+      </svg>
+
+      
+      <svg id="significantly_worse" viewBox="0 0 128 128">
+        <circle cx="64" cy="64" r="60"></circle>
+        <circle cx="44" cy="44" r="4"></circle>
+        <circle cx="84" cy="44" r="4"></circle>
+        <path d="M 36 96 Q 64 48 92 96"></path>
+      </svg>
+    </span>
+
+    <svg id="warning" viewBox="0 0 128 128">
+      <path d="M 64 0 L 128 128 L 0 128 L 64 0"></path>
+      <rect height="84" width="8" x="60" y="0"></rect>
+      <rect height="24" width="8" x="60" y="100"></rect>
+    </svg>
   </template>
 </dom-module><dom-module id="tr-ui-a-generic-object-view">
   <template>
@@ -432,8 +503,8 @@
       padding: 0 3px 0 3px;
     }
 
-    :host(.info-bar-hidden) {
-      display: none;
+    :host([hidden]) {
+      display: none !important;
     }
 
     #message { flex: 1 1 auto; }
@@ -862,6 +933,7 @@
       display: flex;
       flex-direction: row;
       align-items: center;
+      font-size: 12px;
     }
     </style>
     <tr-ui-b-table id="table"></tr-ui-b-table>
@@ -871,11 +943,13 @@
     <style>
     :host {
       display: flex;
+      flex: 0 1;
       flex-direction: column;
     }
     #table {
-      flex: 1 1 auto;
+      flex: 0 1 auto;
       align-self: stretch;
+      font-size: 12px;
     }
     </style>
     <tr-ui-b-table id="table">
@@ -891,6 +965,9 @@
     #heading {
       flex: 0 0 auto;
     }
+    tr-ui-b-table {
+      font-size: 12px;
+    }
     </style>
 
     <div id="heading">
@@ -903,6 +980,11 @@
 .tr-ui-e-chrome-gpu-state-snapshot-view{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAAZiS0dEAEwATABMYqp3KAAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB90JCQsBMCH7ZqYAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAUElEQVRYw+3WwQkAIAiF4Vc0hTO5/wiuURvYIcQOv1cRPhDlDXffSsrMsrYiQi/zU80FAACAVX3nt3lWAABA/x+ovnPyAAAA5AHyAAAA3wMOd34Xd+lsglgAAAAASUVORK5CYII=);display:-webkit-flex;overflow:auto}.tr-ui-e-chrome-gpu-state-snapshot-view img{display:block;margin:16px auto 16px auto}
 </style><dom-module id="tr-ui-a-layout-tree-sub-view">
   <template>
+    <style>
+    tr-ui-b-table {
+      font-size: 12px;
+    }
+    </style>
     <div id="content"></div>
   </template>
 </dom-module><dom-module id="tr-ui-e-s-frame-data-side-panel">
@@ -916,6 +998,7 @@
     table-container {
       display: flex;
       overflow: auto;
+      font-size: 12px;
     }
     </style>
     <div>
@@ -950,10 +1033,6 @@
     <label id="label"></label>
   </template>
 </dom-module><style>
-  * /deep/ .chart-base #title {
-    font-size: 16pt;
-  }
-
   * /deep/ .chart-base {
     -webkit-user-select: none;
     cursor: default;
@@ -965,10 +1044,6 @@
     shape-rendering: crispEdges;
     stroke: #000;
   }
-
-  * /deep/ .chart-base .legend body {
-    margin: 0;
-  }
 </style><template id="chart-base-template">
   <svg> 
     <g id="chart-area" xmlns="http://www.w3.org/2000/svg">
@@ -985,8 +1060,6 @@
   * /deep/ .chart-base-2d #brushes {
     fill: rgb(213, 236, 229)
   }
-</style><style>
-* /deep/ .line-chart .line{fill:none;stroke-width:1.5px}* /deep/ .line-chart #brushes>rect{fill:rgb(192,192,192)}
 </style><dom-module id="tr-ui-e-s-input-latency-side-panel">
   <template>
     <style>
@@ -1010,34 +1083,7 @@
     <toolbar id="toolbar"></toolbar>
     <result-area id="result_area"></result-area>
   </template>
-</dom-module><style>
-* /deep/ .pie-chart .arc-text{font-size:8pt}* /deep/ .pie-chart .label{font-size:10pt}* /deep/ .pie-chart polyline{fill:none;stroke:black}
-</style><dom-module id="tr-ui-e-s-time-summary-side-panel">
-  <template>
-    <style>
-    :host {
-      flex-direction: column;
-      display: flex;
-    }
-    toolbar {
-      flex: 0 0 auto;
-      border-bottom: 1px solid black;
-      display: flex;
-    }
-    result-area {
-      flex: 1 1 auto;
-      display: block;
-      min-height: 0;
-      overflow-y: auto;
-    }
-    </style>
-
-    <toolbar id="toolbar"></toolbar>
-    <result-area id="result_area"></result-area>
-  </template>
-</dom-module><style>
-.tr-ui-e-system-stats-snapshot-view .subhead{font-size:small;padding-bottom:10px}.tr-ui-e-system-stats-snapshot-view ul{background-position:0 5px;background-repeat:no-repeat;cursor:pointer;font-family:monospace;list-style:none;margin:0;padding-left:15px}.tr-ui-e-system-stats-snapshot-view li{background-position:0 5px;background-repeat:no-repeat;cursor:pointer;list-style:none;margin:0;padding-left:15px}
-</style><dom-module id="tr-ui-heading">
+</dom-module><dom-module id="tr-ui-b-heading">
   <template>
     <style>
     :host {
@@ -1081,13 +1127,86 @@
 .object-instance-track{height:18px}
 </style><style>
 .tr-ui-e-system-stats-instance-track{height:500px}.tr-ui-e-system-stats-instance-track ul{list-style:none;list-style-position:outside;margin:0;overflow:hidden}
-</style><dom-module id="tr-ui-e-v8-runtime-call-stats-table">
+</style><style>
+.tr-ui-e-system-stats-snapshot-view .subhead{font-size:small;padding-bottom:10px}.tr-ui-e-system-stats-snapshot-view ul{background-position:0 5px;background-repeat:no-repeat;cursor:pointer;font-family:monospace;list-style:none;margin:0;padding-left:15px}.tr-ui-e-system-stats-snapshot-view li{background-position:0 5px;background-repeat:no-repeat;cursor:pointer;list-style:none;margin:0;padding-left:15px}
+</style><dom-module id="tr-ui-e-v8-gc-objects-stats-table">
+  <template>
+    <style>
+    tr-ui-b-table {
+      flex: 0 0 auto;
+      align-self: stretch;
+      margin-top: 1em;
+      font-size: 12px;
+    }
+    .diff {
+      display: inline-block;
+      margin-top: 1em;
+      margin-left: 0.8em;
+    }
+    </style>
+    <div class="diff" id="diffOption">
+      Diff
+    </div>
+    <tr-ui-b-table id="diffTable"></tr-ui-b-table>
+    <tr-ui-b-table id="table"></tr-ui-b-table>
+  </template>
+</dom-module><dom-module id="tr-ui-e-multi-v8-gc-stats-thread-slice-sub-view">
+  <template>
+    <style>
+    </style>
+    <tr-ui-e-v8-gc-objects-stats-table id="gcObjectsStats">
+    </tr-ui-e-v8-gc-objects-stats-table>
+  </template>
+</dom-module><dom-module id="tr-ui-e-v8-ic-stats-table">
+  <template>
+    <style>
+    tr-ui-b-table {
+      flex: 0 0 auto;
+      align-self: stretch;
+      margin-top: 1em;
+      font-size: 12px;
+    }
+    #total {
+      margin-top: 1em;
+      margin-left: 0.8em;
+    }
+    #groupOption {
+      display: inline-block;
+      margin-top: 1em;
+      margin-left: 0.8em;
+    }
+    </style>
+    <div style="padding-right: 200px">
+      <div style="float:right;  border-style: solid; border-width: 1px; padding:20px">
+        0 uninitialized<br/>
+        . premonomorphic<br/>
+        1 monomorphic<br/>
+        ^ recompute handler<br/>
+        P polymorphic<br/>
+        N megamorphic<br/>
+        G generic
+      </div>
+    </div>
+    <div id="total">
+    </div>
+    <div id="groupOption">
+      Group Key
+    </div>
+    <tr-ui-b-table id="table"></tr-ui-b-table>
+  </template>
+</dom-module><dom-module id="tr-ui-e-multi-v8-ic-stats-thread-slice-sub-view">
+  <template>
+    <tr-ui-e-v8-ic-stats-table id="table">
+    </tr-ui-e-v8-ic-stats-table>
+  </template>
+</dom-module><dom-module id="tr-ui-e-v8-runtime-call-stats-table">
   <template>
     <style>
     #table {
       flex: 0 0 auto;
       align-self: stretch;
       margin-top: 1em;
+      font-size: 12px;
     }
     </style>
     <tr-ui-b-table id="table"></tr-ui-b-table>
@@ -1097,6 +1216,16 @@
     <tr-ui-a-multi-thread-slice-sub-view id="content"></tr-ui-a-multi-thread-slice-sub-view>
     <tr-ui-e-v8-runtime-call-stats-table id="runtimeCallStats"></tr-ui-e-v8-runtime-call-stats-table>
   </template>
+</dom-module><dom-module id="tr-ui-e-single-v8-gc-stats-thread-slice-sub-view">
+  <template>
+    <tr-ui-a-single-event-sub-view id="content"></tr-ui-a-single-event-sub-view>
+    <tr-ui-e-v8-gc-objects-stats-table id="gcObjectsStats"></tr-ui-e-v8-gc-objects-stats-table>
+  </template>
+</dom-module><dom-module id="tr-ui-e-single-v8-ic-stats-thread-slice-sub-view">
+  <template>
+    <tr-ui-e-v8-ic-stats-table id="table">
+    </tr-ui-e-v8-ic-stats-table>
+  </template>
 </dom-module><dom-module id="tr-ui-e-single-v8-thread-slice-sub-view">
   <template>
     <tr-ui-a-single-thread-slice-sub-view id="content"></tr-ui-a-single-thread-slice-sub-view>
@@ -1112,11 +1241,110 @@
     #table {
       flex: 1 1 auto;
       align-self: stretch;
+      font-size: 12px;
     }
     </style>
     <tr-ui-b-table id="table">
     </tr-ui-b-table>
   </template>
+</dom-module><dom-module id="tr-ui-b-tab-view">
+  <template>
+    <style>
+      :host {
+        display: flex;
+        flex-direction: column;
+      }
+
+      #selection_description, #tabs {
+        font-size: 12px;
+      }
+
+      #selection_description {
+        display: inline-block;
+        font-weight: bold;
+        margin: 9px 0px 4px 20px;
+      }
+
+      #tabs {
+        flex: 0 0 auto;
+        border-top: 1px solid #8e8e8e;
+        border-bottom: 1px solid #8e8e8e;
+        background-color: #ececec;
+        overflow: hidden;
+        margin: 0;
+      }
+
+      #tabs input[type=radio] {
+        display: none;
+      }
+
+      #tabs tab label {
+        cursor: pointer;
+        display: inline-block;
+        border: 1px solid #ececec;
+        margin: 5px 0px 0px 15px;
+        padding: 3px 10px 3px 10px;
+      }
+
+      #tabs tab label span {
+        font-weight: bold;
+      }
+
+      #tabs:focus input[type=radio]:checked ~ label {
+        outline: dotted 1px #8e8e8e;
+        outline-offset: -2px;
+      }
+
+      #tabs input[type=radio]:checked ~ label {
+        background-color: white;
+        border: 1px solid #8e8e8e;
+        border-bottom: 1px solid white;
+      }
+
+      #subView {
+        flex: 1 1 auto;
+        overflow: auto;
+      }
+    </style>
+    <div hidden="[[tabsHidden]]" id="tabs">
+      <label id="selection_description">[[label_]]</label>
+      <template is="dom-repeat" items="[[subViews_]]">
+        <tab>
+          <input checked="[[isChecked_(item)]]" id$="[[computeRadioId_(item)]]" name="tabs" on-change="onTabChanged_" type="radio"/>
+          <label for$="[[computeRadioId_(item)]]">
+            <template if="[[item.tabIcon]]" is="dom-if">
+              <span style$="[[item.tabIcon.style]]">[[item.tabIcon.text]]</span>
+            </template>
+            [[item.tabLabel]]
+          </label>
+        </tab>
+      </template>
+    </div>
+    <div id="subView"></div>
+    <content>
+    </content>
+  </template>
+</dom-module><dom-module id="tr-ui-a-memory-dump-heap-details-breakdown-view">
+  <template>
+    <tr-ui-b-tab-view id="tabs"></tr-ui-b-tab-view>
+  </template>
+</dom-module><dom-module id="tr-ui-a-memory-dump-heap-details-breakdown-view-tab">
+  <template>
+    <tr-v-ui-scalar-context-controller></tr-v-ui-scalar-context-controller>
+    <tr-ui-b-info-bar hidden="" id="info"></tr-ui-b-info-bar>
+    <tr-ui-b-table id="table"></tr-ui-b-table>
+  </template>
+</dom-module><dom-module id="tr-ui-a-memory-dump-heap-details-path-view">
+  <template>
+    <style>
+      :host {
+        display: flex;
+        flex-direction: column;
+      }
+    </style>
+    <tr-v-ui-scalar-context-controller></tr-v-ui-scalar-context-controller>
+    <tr-ui-b-table id="table"></tr-ui-b-table>
+  </template>
 </dom-module><dom-module id="tr-ui-a-memory-dump-heap-details-pane">
   <template>
     <style>
@@ -1139,7 +1367,7 @@
       #label {
         flex: 1 1 auto;
         padding: 8px;
-        font-size:  15px;
+        font-size: 15px;
         font-weight: bold;
       }
 
@@ -1163,10 +1391,24 @@
         text-align: center;
       }
 
-      #table {
+      #split_view {
         display: none;  /* Hide until memory allocator dumps are set. */
         flex: 1 0 auto;
         align-self: stretch;
+        flex-direction: row;
+      }
+
+      #path_view {
+        width: 50%;
+      }
+
+      #breakdown_view {
+        flex: 1 1 auto;
+        width: 0;
+      }
+
+      #path_view, #breakdown_view {
+        overflow-x: auto;  /* Show scrollbar if necessary. */
       }
     </style>
     <div id="header">
@@ -1177,10 +1419,18 @@
       </div>
     </div>
     <div id="contents">
-      <tr-ui-b-info-bar class="info-bar-hidden" id="info_bar">
+      <tr-ui-b-info-bar hidden="" id="info_bar">
       </tr-ui-b-info-bar>
+
       <div id="info_text">No heap dump selected</div>
-      <tr-ui-b-table id="table"></tr-ui-b-table>
+
+      <div id="split_view">
+        <tr-ui-a-memory-dump-heap-details-path-view id="path_view">
+        </tr-ui-a-memory-dump-heap-details-path-view>
+        <tr-ui-b-drag-handle id="drag_handle"></tr-ui-b-drag-handle>
+        <tr-ui-a-memory-dump-heap-details-breakdown-view id="breakdown_view">
+        </tr-ui-a-memory-dump-heap-details-breakdown-view>
+      </div>
     </div>
   </template>
 </dom-module><dom-module id="tr-ui-a-memory-dump-allocator-details-pane">
@@ -1220,6 +1470,7 @@
         display: none;  /* Hide until memory allocator dumps are set. */
         flex: 1 0 auto;
         align-self: stretch;
+        font-size: 12px;
       }
     </style>
     <div id="label">Component details</div>
@@ -1265,6 +1516,7 @@
         display: none;  /* Hide until memory dumps are set. */
         flex: 1 0 auto;
         align-self: stretch;
+        font-size: 12px;
       }
     </style>
     <div id="label">Memory maps</div>
@@ -1310,6 +1562,11 @@
         font-weight: bold;
       }
 
+      #label a {
+        font-weight: normal;
+        float: right;
+      }
+
       #contents {
         flex: 1 0 auto;
         align-self: stretch;
@@ -1328,11 +1585,12 @@
         display: none;  /* Hide until memory dumps are set. */
         flex: 1 0 auto;
         align-self: stretch;
+        font-size: 12px;
       }
     </style>
     <tr-ui-b-view-specific-brushing-state id="state" view-id="analysis.memory_dump_overview_pane">
     </tr-ui-b-view-specific-brushing-state>
-    <div id="label">Overview</div>
+    <div id="label">Overview <a href="https://chromium.googlesource.com/chromium/src/+/master/docs/memory-infra">Help</a></div>
     <div id="contents">
       <div id="info_text">No memory memory dumps selected</div>
       <tr-ui-b-table id="table"></tr-ui-b-table>
@@ -1388,6 +1646,11 @@
   </template>
 </dom-module><dom-module id="tr-ui-a-container-memory-dump-sub-view">
   <template>
+    <style>
+    tr-ui-b-table {
+      font-size: 12px;
+    }
+    </style>
     <div id="content"></div>
   </template>
 </dom-module><dom-module id="tr-ui-a-counter-sample-sub-view">
@@ -1397,33 +1660,11 @@
       display: flex;
       flex-direction: column;
     }
-    </style>
-    <tr-ui-b-table id="table"></tr-ui-b-table>
-  </template>
-</dom-module><dom-module id="tr-ui-a-multi-event-details-table">
-  <template>
-    <style>
-    :host {
-      display: flex;
-      flex-direction: column;
-    }
-    #table {
-      flex: 1 1 auto;
-      align-self: stretch;
-    }
-
-    #titletable {
-      font-weight: bold;
-    }
-
-    #title-info {
+    tr-ui-b-table {
       font-size: 12px;
     }
     </style>
-    <tr-ui-b-table id="titletable">
-    </tr-ui-b-table>
-    <tr-ui-b-table id="table">
-    </tr-ui-b-table>
+    <tr-ui-b-table id="table"></tr-ui-b-table>
   </template>
 </dom-module><dom-module id="tr-ui-a-multi-event-summary-table">
   <template>
@@ -1434,6 +1675,7 @@
     #table {
       flex: 1 1 auto;
       align-self: stretch;
+      font-size: 12px;
     }
     </style>
     <tr-ui-b-table id="table">
@@ -1449,12 +1691,170 @@
     #table {
       flex: 1 1 auto;
       align-self: stretch;
+      font-size: 12px;
     }
     </style>
     <tr-ui-b-table id="table">
     </tr-ui-b-table>
     
   </template>
+</dom-module><dom-module id="tr-ui-b-radio-picker">
+  <template>
+    <style>
+    :host([vertical]) #container {
+      flex-direction: column;
+    }
+    :host(:not[vertical]) #container {
+      flex-direction: row;
+    }
+    #container {
+      display: flex;
+    }
+    #container > div {
+      padding-left: 1em;
+      padding-bottom: 0.5em;
+    }
+    </style>
+    <div id="container"></div>
+  </template>
+</dom-module><dom-module id="tr-v-ui-breakdown-span">
+  <template>
+    <style>
+    :host {
+      display: flex;
+      flex-direction: column;
+    }
+    #table_container {
+      display: flex;
+      flex: 0 0 auto;
+    }
+    #table {
+      max-height: 150px;
+      overflow-y: auto;
+    }
+    </style>
+
+    <div id="empty">(empty)</div>
+    <div id="table_container">
+      <div id="container"></div>
+      <span>
+        <tr-ui-b-table id="table"></tr-ui-b-table>
+      </span>
+    </div>
+  </template>
+</dom-module><dom-module id="tr-v-ui-buildbot-info-span">
+  <template>
+    <tr-ui-b-table id="table"></tr-ui-b-table>
+  </template>
+</dom-module><dom-module id="tr-v-ui-collected-related-event-set-span">
+  
+</dom-module><dom-module id="tr-v-ui-device-info-span">
+  <template>
+    <tr-ui-b-table id="table"></tr-ui-b-table>
+  </template>
+</dom-module><dom-module id="tr-v-ui-generic-diagnostic-span">
+  <template>
+    <tr-ui-a-generic-object-view id="generic"></tr-ui-a-generic-object-view>
+  </template>
+
+  
+</dom-module><dom-module id="tr-v-ui-merged-buildbot-info-span">
+  <template>
+    <tr-ui-b-table id="table"></tr-ui-b-table>
+  </template>
+</dom-module><dom-module id="tr-v-ui-merged-device-info-span">
+  <template>
+    <tr-ui-b-table id="table"></tr-ui-b-table>
+  </template>
+</dom-module><dom-module id="tr-v-ui-merged-revision-info-span">
+  <template>
+    <tr-ui-b-table id="table"></tr-ui-b-table>
+  </template>
+</dom-module><dom-module id="tr-v-ui-merged-telemetry-info-span">
+  <template>
+    <style>
+    #hide, #table {
+      display: none;
+    }
+    </style>
+    <button id="show" on-click="onShow_">Show</button>
+    <button id="hide" on-click="onHide_">Hide</button>
+    <tr-ui-b-table id="table"></tr-ui-b-table>
+  </template>
+</dom-module><dom-module id="tr-v-ui-related-event-set-span">
+  
+</dom-module><dom-module id="tr-v-ui-related-histogram-map-span">
+  <template>
+    <tr-ui-b-table id="table"></tr-ui-b-table>
+  </template>
+</dom-module><dom-module id="tr-v-ui-related-histogram-set-span">
+  
+</dom-module><dom-module id="tr-v-ui-revision-info-span">
+  <template>
+    <tr-ui-b-table id="table"></tr-ui-b-table>
+  </template>
+</dom-module><dom-module id="tr-v-ui-scalar-diagnostic-span">
+  <template>
+    <tr-v-ui-scalar-span id="scalar"></tr-v-ui-scalar-span>
+  </template>
+
+  
+</dom-module><dom-module id="tr-v-ui-telemetry-info-span">
+  <template>
+    <tr-ui-b-table id="table"></tr-ui-b-table>
+  </template>
+</dom-module><dom-module id="tr-v-ui-unmergeable-diagnostic-set-span">
+  
+</dom-module><dom-module id="tr-v-ui-diagnostic-map-table">
+  <template>
+    <tr-ui-b-table id="table"></tr-ui-b-table>
+  </template>
+
+  
+</dom-module><dom-module name="tr-v-ui-scalar-map-table">
+  <template>
+    <style>
+    </style>
+    <tr-ui-b-table id="table"></tr-ui-b-table>
+  </template>
+</dom-module><dom-module id="tr-v-ui-histogram-span">
+  <template>
+    <style>
+    #container {
+      display: flex;
+      flex-direction: row;
+      justify-content: space-between;
+    }
+    #chart {
+      flex-grow: 1;
+      display: none;
+    }
+    #drag_handle, #sample_diagnostics_container {
+      display: none;
+    }
+    #chart svg {
+      display: block;
+    }
+    </style>
+
+    <div id="container">
+      <div id="chart"></div>
+      <div id="stats_container">
+        <tr-v-ui-scalar-map-table id="stats"></tr-v-ui-scalar-map-table>
+      </div>
+    </div>
+    <tr-ui-b-drag-handle id="drag_handle"></tr-ui-b-drag-handle>
+
+    <tr-v-ui-diagnostic-map-table id="histogram_diagnostics"></tr-v-ui-diagnostic-map-table>
+
+    <div id="sample_diagnostics_container">
+      <div id="merge_sample_diagnostics_container">
+        <input checked="" id="merge_sample_diagnostics" on-change="updateDiagnostics_" type="checkbox"/>
+        <label for="merge_sample_diagnostics">Merge Sample Diagnostics</label>
+      </div>
+      <tr-v-ui-diagnostic-map-table id="sample_diagnostics"></tr-v-ui-diagnostic-map-table>
+    </div>
+  </template>
 </dom-module><dom-module id="tr-ui-a-multi-event-sub-view">
   <template>
     <style>
@@ -1472,6 +1872,10 @@
       flex: 0 0 auto;
       align-self: stretch;
     }
+    #histogramContainer {
+      display: flex;
+    }
+
     tr-ui-a-multi-event-summary-table {
       border-bottom: 1px solid #aaa;
     }
@@ -1485,7 +1889,18 @@
       border-bottom: 1px solid #aaa;
     }
     </style>
-    <div id="content"></div>
+    <div id="content">
+      <tr-ui-a-multi-event-summary-table id="eventSummaryTable">
+      </tr-ui-a-multi-event-summary-table>
+      <tr-ui-a-selection-summary-table id="selectionSummaryTable">
+      </tr-ui-a-selection-summary-table>
+      <tr-ui-b-radio-picker id="radioPicker">
+      </tr-ui-b-radio-picker>
+      <div id="histogramContainer">
+        <tr-v-ui-histogram-span id="histogramSpan">
+        </tr-v-ui-histogram-span>
+      </div>
+    </div>
   </template>
 </dom-module><dom-module id="tr-ui-a-related-events">
   <template>
@@ -1497,6 +1912,7 @@
     #table {
       flex: 1 1 auto;
       align-self: stretch;
+      font-size: 12px;
     }
     </style>
     <tr-ui-b-table id="table"></tr-ui-b-table>
@@ -1558,6 +1974,7 @@
     <style>
     :host {
       display: flex;
+      font-size: 12px;
     }
     </style>
     <tr-ui-b-table id="content"></tr-ui-b-table>
@@ -1568,6 +1985,11 @@
   </template>
 </dom-module><dom-module id="tr-ui-a-power-sample-summary-table">
   <template>
+    <style>
+    tr-ui-b-table {
+      font-size: 12px;
+    }
+    </style>
     <tr-ui-b-table id="table"></tr-ui-b-table>
   </template>
 </dom-module><dom-module id="tr-ui-a-multi-power-sample-sub-view">
@@ -1613,6 +2035,9 @@
       margin: 1px;
       margin-right: 2px;
     }
+    tr-ui-b-table {
+      font-size: 12px;
+    }
     </style>
     <div id="control">
       Sample View Option
@@ -1655,6 +2080,7 @@
     #table {
       flex: 1 1 auto;
       align-self: stretch;
+      font-size: 12px;
     }
     </style>
     <tr-ui-b-table id="table"></tr-ui-b-table>
@@ -1859,6 +2285,7 @@
     <style>
     :host {
       display: flex;
+      font-size: 12px;
     }
     </style>
     <tr-ui-b-table id="table"></tr-ui-b-table>
@@ -1876,6 +2303,7 @@
     <style>
     :host {
       display: flex;
+      font-size: 12px;
     }
     </style>
     <tr-ui-b-table id="content"></tr-ui-b-table>
@@ -1987,78 +2415,6 @@
       <tr-ui-a-user-expectation-related-samples-table id="relatedSamples"></tr-ui-a-user-expectation-related-samples-table>
     </div>
   </template>
-</dom-module><dom-module id="tr-ui-b-tab-view">
-  <template>
-    <style>
-      :host {
-        display: flex;
-        flex-direction: column;
-      }
-
-      #selection_description, #tabs {
-        font-size: 12px;
-      }
-
-      #selection_description {
-        display: inline-block;
-        font-weight: bold;
-        margin: 9px 0px 4px 20px;
-      }
-
-      #tabs {
-        flex: 0 0 auto;
-        border-top: 1px solid #8e8e8e;
-        border-bottom: 1px solid #8e8e8e;
-        background-color: #ececec;
-        overflow: hidden;
-        margin: 0;
-      }
-
-      #tabs input[type=radio] {
-        display: none;
-      }
-
-      #tabs tab label {
-        cursor: pointer;
-        display: inline-block;
-        border: 1px solid #ececec;
-        margin: 5px 0px 0px 15px;
-        padding: 3px 10px 3px 10px;
-      }
-
-      #tabs tab label span {
-        font-weight: bold;
-      }
-
-      #tabs input[type=radio]:checked ~ label {
-        background-color: white;
-        border: 1px solid #8e8e8e;
-        border-bottom: 1px solid white;
-      }
-
-      #subView {
-        flex: 1 1 auto;
-        overflow: auto;
-      }
-    </style>
-    <div hidden="[[tabsHidden]]" id="tabs">
-      <label id="selection_description">[[label_]]</label>
-      <template is="dom-repeat" items="[[subViews_]]">
-        <tab>
-          <input checked$="[[isChecked_(item)]]" id$="[[computeRadioId_(item)]]" name="tabs" on-change="onTabChanged_" type="radio"/>
-          <label for$="[[computeRadioId_(item)]]">
-            <template if="[[item.tabIcon]]" is="dom-if">
-              <span style$="[[item.tabIcon.style]]">[[item.tabIcon.text]]</span>
-            </template>
-            [[item.tabLabel]]
-          </label>
-        </tab>
-      </template>
-    </div>
-    <div id="subView"></div>
-    <content>
-    </content>
-  </template>
 </dom-module><dom-module id="tr-ui-a-analysis-view">
   <template>
     <style>
@@ -2208,7 +2564,13 @@
   -webkit-box-flex: 1;
 }
 </style><style>
-.ruler-track{height:12px}.ruler-track.tall-mode{height:30px}
+.x-axis-track {
+  height: 12px;
+}
+
+.x-axis-track.tall-mode {
+  height: 30px;
+}
 </style><dom-module id="tr-ui-timeline-track-view">
   <template>
     <style>
@@ -2293,8 +2655,6 @@
         border-bottom: 2px solid rgba(0, 0, 0, 0.5);
         border-right: 2px solid rgba(0, 0, 0, 0.5);
         border-radius: 50%;
-
-        animation: spin 1s linear infinite;
       }
       @keyframes spin { 100% { transform: rotate(360deg); } }
     </style>
@@ -2346,27 +2706,33 @@
         -webkit-user-select: text;
         color: #777;
       }
+      #promptContainer {
+        display: flex;
+      }
+      #promptMark {
+        width: 1em;
+        color: #468;
+      }
       #prompt {
-        -webkit-user-select: text;
-        -webkit-user-modify: read-write-plaintext-only;
+        flex: 1;
+        width: 100%;
+        border: none !important;
+        background-color: inherit !important;
+        font: inherit !important;
         text-overflow: clip !important;
         text-decoration: none !important;
       }
       #prompt:focus {
         outline: none;
       }
-      #prompt br {
-        display: none;
-      }
-      #prompt ::before {
-        content: ">";
-        color: #468;
-      }
     </style>
 
     <div class="root hidden" id="root" on-focus="onConsoleFocus" tabindex="0">
       <div id="history"></div>
-      <div id="prompt" on-blur="onConsoleBlur" on-keydown="promptKeyDown" on-keypress="promptKeyPress"></div>
+      <div id="promptContainer">
+        <span id="promptMark">&gt;</span>
+        <input id="prompt" on-blur="onConsoleBlur" on-keydown="promptKeyDown" on-keypress="promptKeyPress" type="text"/>
+       </div>
     </div>
   </template>
 </dom-module><dom-module id="tr-ui-side-panel-container">
@@ -2655,22 +3021,6 @@
       </div>
     </div>
   </template>
-</dom-module><dom-module id="tr-v-ui-array-of-numbers-span">
-  <template>
-  </template>
-</dom-module><dom-module id="tr-v-ui-generic-table-view">
-  <template>
-    <style>
-    :host {
-    display: flex;
-    }
-    #table {
-      flex: 1 1 auto;
-      align-self: stretch;
-    }
-    </style>
-    <tr-ui-b-table id="table"></tr-ui-b-table>
-  </template>
 </dom-module><dom-module id="tr-ui-timeline-view-metadata-overlay">
   <template>
     <style>
@@ -2680,7 +3030,7 @@
       overflow: auto;
     }
     </style>
-    <tr-v-ui-generic-table-view id="gtv"></tr-v-ui-generic-table-view>
+    <tr-ui-b-table id="table"></tr-ui-b-table>
   </template>
 </dom-module><dom-module id="tr-ui-timeline-view">
   <template>
@@ -2794,6 +3144,7 @@
     }
     #table {
       flex: 1 1 auto;
+      font-size: 12px;
     }
     </style>
     <tr-ui-b-table id="table"></tr-ui-b-table>
@@ -2801,88 +3152,34 @@
 </dom-module><dom-module id="tr-ui-b-grouping-table-groupby-picker">
   <template>
     <style>
-    :host {
-      display: flex;
-      align-items: center;
-    }
-
-    :host(:not([vertical])), :host(:not([vertical])) groups {
-      flex-direction: row;
-    }
-
-    :host([vertical]), :host([vertical]) groups {
-      flex-direction: column;
-    }
-
-    groups {
-      -webkit-user-select: none;
+    #container {
       display: flex;
     }
-
-    possible-group {
-      display: span;
-      padding-right: 10px;
-      padding-left: 10px;
+    #container *:not(:first-child) {
+      padding-left: 3px;
+      border-left: 1px solid black;
+      margin-left: 3px;
     }
     </style>
 
-    <groups id="groups"></groups>
-    <tr-ui-b-dropdown id="add_group"></tr-ui-b-dropdown>
+    <div id="container"></div>
   </template>
 </dom-module><dom-module id="tr-ui-b-grouping-table-groupby-picker-group">
   <template>
     <style>
     :host {
       white-space: nowrap;
-      border: 3px solid white;
-      background-color: #dddddd;
-      cursor: move;
     }
-
-    :host(:not([vertical])) {
-      display: inline;
-    }
-
-    :host([vertical]) {
-      display: block;
-    }
-
-    :host(:not([vertical]).drop-before) {
-      border-left: 3px solid black;
-    }
-
-    :host([vertical].drop-before) {
-      border-top: 3px solid black;
-    }
-
-    :host(:not([vertical]).drop-after) {
-      border-right: 3px solid black;
-    }
-
-    :host([vertical].drop-after) {
-      border-bottom: 3px solid black;
-    }
-
-    :host([dragging]) {
-      opacity: 0.5;
-    }
-
-    #remove {
-      visibility: hidden;
-      padding-left: 3px;
-      width: 20px;
-      height: 20px;
-      cursor: auto;
-    }
-
-    #key {
-      padding-right: 3px;
+    #left, #right {
+      user-select: none;
+      cursor: pointer;
     }
     </style>
 
-    
-    <span id="remove" on-click="remove_">×</span>
-    <span id="key"></span>
+    <span id="left" on-click="moveLeft_">◀</span>
+    <input id="enabled" on-change="onEnableChanged_" type="checkbox"/>
+    <label for="enabled" id="label"></label>
+    <span id="right" on-click="moveRight_">▶</span>
   </template>
 </dom-module><dom-module id="tr-ui-sp-file-size-stats-side-panel">
   <template>
@@ -2918,54 +3215,183 @@
       <tr-ui-b-grouping-table id="table"></tr-ui-b-grouping-table>
     </table-container>
   </template>
-</dom-module><dom-module id="tr-v-ui-breakdown-span">
-  <template>
-    <div id="container"></div>
-    <tr-ui-b-drag-handle id="drag_handle"></tr-ui-b-drag-handle>
-  </template>
-</dom-module><dom-module id="tr-v-ui-generic-diagnostic-span">
-  <template>
-    <tr-ui-a-generic-object-view id="generic"></tr-ui-a-generic-object-view>
-  </template>
-
-  
-</dom-module><dom-module id="tr-v-ui-iteration-info-span">
-  <template>
-    <tr-ui-b-table id="table"></tr-ui-b-table>
-  </template>
-</dom-module><dom-module id="tr-v-ui-related-event-set-span">
-  
-</dom-module><dom-module id="tr-v-ui-related-value-map-span">
-  
-</dom-module><dom-module id="tr-v-ui-related-value-set-span">
-  
-</dom-module><dom-module id="tr-v-ui-diagnostic-map-table">
-  <template>
-    <tr-ui-b-table id="table"></tr-ui-b-table>
-  </template>
-
-  
-</dom-module><dom-module name="tr-v-ui-numeric-stats-span">
-  <template>
-    <tr-ui-b-table id="stats"></tr-ui-b-table>
-  </template>
-</dom-module><dom-module id="tr-v-ui-histogram-span">
+</dom-module><dom-module id="tr-v-ui-histogram-set-table-cell">
   <template>
     <style>
-      #container {
-        display: flex;
-        flex-direction: row;
-      }
+    #histogram_container {
+      display: flex;
+      flex-direction: row;
+    }
+
+    #missing, #empty, #unmergeable, #scalar {
+      flex-grow: 1;
+    }
+
+    #open_histogram, #close_histogram {
+      height: 1em;
+    }
+
+    #open_histogram {
+      margin-left: 4px;
+      stroke-width: 0;
+      stroke: blue;
+      fill: blue;
+    }
+    :host(:hover) #open_histogram {
+      background: blue;
+      stroke: white;
+      fill: white;
+    }
+
+    #scalar {
+      flex-grow: 1;
+      white-space: nowrap;
+    }
+
+    #histogram {
+      flex-grow: 1;
+    }
+
+    #close_histogram line {
+      stroke-width: 18;
+      stroke: black;
+    }
+    #close_histogram:hover {
+      background: black;
+    }
+    #close_histogram:hover line {
+      stroke: white;
+    }
+
+    #overview_container {
+      display: none;
+    }
     </style>
 
-    <div id="container">
-      <div id="chart"></div>
-      <tr-v-ui-numeric-stats-span id="stats"></tr-v-ui-numeric-stats-span>
+    <div id="histogram_container">
+      <span id="missing">(missing)</span>
+      <span id="empty">(empty)</span>
+      <span id="unmergeable">(unmergeable)</span>
+
+      <tr-v-ui-scalar-span id="scalar" on-click="openHistogram_"></tr-v-ui-scalar-span>
+
+      <svg id="open_histogram" on-click="openHistogram_" viewBox="0 0 128 128">
+        <rect height="16" width="32" x="16" y="24"></rect>
+        <rect height="16" width="96" x="16" y="56"></rect>
+        <rect height="16" width="64" x="16" y="88"></rect>
+      </svg>
+
+      <span id="histogram"></span>
+
+      <svg id="close_histogram" on-click="closeHistogram_" viewBox="0 0 128 128">
+        <line x1="28" x2="100" y1="28" y2="100"></line>
+        <line x1="28" x2="100" y1="100" y2="28"></line>
+      </svg>
     </div>
 
-    <tr-v-ui-diagnostic-map-table id="diagnostic_map_table"></tr-v-ui-diagnostic-map-table>
+    <div id="overview_container">
+    </div>
   </template>
-</dom-module><dom-module id="tr-v-ui-value-set-table">
+</dom-module><dom-module id="tr-v-ui-histogram-set-table-name-cell">
+  <template>
+    <style>
+    #name_container {
+      display: flex;
+    }
+
+    #name {
+      overflow: hidden;
+      white-space: nowrap;
+      text-overflow: ellipsis;
+    }
+
+    #show_overview, #hide_overview {
+      height: 1em;
+      margin-left: 5px;
+    }
+
+    #show_overview {
+      stroke: blue;
+      stroke-width: 16;
+    }
+
+    #show_overview:hover {
+      background: blue;
+      stroke: white;
+    }
+
+    #hide_overview {
+      display: none;
+      stroke-width: 18;
+      stroke: black;
+    }
+
+    #hide_overview:hover {
+      background: black;
+      stroke: white;
+    }
+
+    #open_histograms, #close_histograms {
+      height: 1em;
+    }
+
+    #open_histograms {
+      margin-left: 4px;
+      stroke-width: 0;
+      stroke: blue;
+      fill: blue;
+    }
+    :host(:hover) #open_histograms {
+      background: blue;
+      stroke: white;
+      fill: white;
+    }
+
+    #close_histograms line {
+      stroke-width: 18;
+      stroke: black;
+    }
+    #close_histograms:hover {
+      background: black;
+    }
+    #close_histograms:hover line {
+      stroke: white;
+    }
+
+    #overview_container {
+      display: none;
+    }
+    </style>
+
+    <div id="name_container">
+      <span id="name"></span>
+      <svg id="show_overview" on-click="showOverview_" viewBox="0 0 128 128">
+        <line x1="19" x2="49" y1="109" y2="49"></line>
+        <line x1="49" x2="79" y1="49" y2="79"></line>
+        <line x1="79" x2="109" y1="79" y2="19"></line>
+      </svg>
+
+      <svg id="hide_overview" on-click="hideOverview_" viewBox="0 0 128 128">
+        <line x1="28" x2="100" y1="28" y2="100"></line>
+        <line x1="28" x2="100" y1="100" y2="28"></line>
+      </svg>
+
+      <svg id="open_histograms" on-click="openHistograms_" viewBox="0 0 128 128">
+        <rect height="16" width="32" x="16" y="24"></rect>
+        <rect height="16" width="96" x="16" y="56"></rect>
+        <rect height="16" width="64" x="16" y="88"></rect>
+      </svg>
+
+      <svg id="close_histograms" on-click="closeHistograms_" viewBox="0 0 128 128">
+        <line x1="28" x2="100" y1="28" y2="100"></line>
+        <line x1="28" x2="100" y1="100" y2="28"></line>
+      </svg>
+    </div>
+
+    <div id="overview_container">
+    </div>
+  </template>
+</dom-module><dom-module id="tr-v-ui-histogram-set-table">
   <template>
     <style>
     :host {
@@ -2981,15 +3407,22 @@
       margin-top: 5px;
       display: flex;
       min-height: 0px;
-      overflow-y: auto;
+      overflow: auto;
     }
 
-    #histogram {
+    #help {
       display: none;
+      margin-left: 20px;
     }
 
     #zero {
       color: red;
+      /* histogram-set-table is used by both metrics-side-panel and results2.html.
+       * This font-size rule has no effect in results2.html, but improves
+       * legibility in the metrics-side-panel, which sets font-size in order to
+       * make this table denser.
+       */
+      font-size: initial;
     }
 
     #search {
@@ -3001,21 +3434,71 @@
       white-space: nowrap;
     }
 
+    #show_overview, #hide_overview {
+      height: 1em;
+      margin-right: 20px;
+    }
+
+    #show_overview {
+      stroke: blue;
+      stroke-width: 16;
+    }
+
+    #show_overview:hover {
+      background: blue;
+      stroke: white;
+    }
+
+    #hide_overview {
+      display: none;
+      stroke-width: 18;
+      stroke: black;
+    }
+
+    #hide_overview:hover {
+      background: black;
+      stroke: white;
+    }
+
     #reference_column_container * {
       margin-right: 20px;
     }
+
+    #statistic_container * {
+      margin-right: 20px;
+    }
+
+    #download_csv {
+      margin-right: 20px;
+    }
     </style>
 
-    <div id="zero">zero values</div>
+    <div id="zero">zero Histograms</div>
 
     <div id="container">
       <div id="controls">
         <input id="search" on-keyup="onSearch_" placeholder="Find Histogram name"/>
 
+        <svg id="show_overview" on-click="showOverview_" viewBox="0 0 128 128">
+          <line x1="19" x2="49" y1="109" y2="49"></line>
+          <line x1="49" x2="79" y1="49" y2="79"></line>
+          <line x1="79" x2="109" y1="79" y2="19"></line>
+        </svg>
+        <svg id="hide_overview" on-click="hideOverview_" viewBox="0 0 128 128">
+          <line x1="28" x2="100" y1="28" y2="100"></line>
+          <line x1="28" x2="100" y1="100" y2="28"></line>
+        </svg>
+
         <span id="reference_column_container"></span>
 
+        <span id="statistic_container"></span>
+
+        <button id="download_csv" on-click="downloadCSV_">⬇ CSV</button>
+
         <input id="show_all" on-change="onShowAllChange_" title="When unchecked, less important histograms are hidden." type="checkbox"/>
         <label for="show_all" title="When unchecked, less important histograms are hidden.">Show all</label>
+
+        <a id="help">Help</a>
       </div>
 
       <tr-ui-b-grouping-table-groupby-picker id="picker">
@@ -3024,18 +3507,17 @@
       <table-container>
         <tr-ui-b-table id="table">
       </tr-ui-b-table></table-container>
-      <tr-v-ui-histogram-span id="histogram">
-    </tr-v-ui-histogram-span></div>
+    </div>
   </template>
-</dom-module><dom-module id="tr-v-ui-value-set-view">
+</dom-module><dom-module id="tr-v-ui-histogram-set-view">
   <template>
     <style>
-      :host {
-        font-family: sans-serif;
-      };
+    :host {
+      font-family: sans-serif;
+    }
     </style>
 
-    <tr-v-ui-value-set-table id="valueSetTable"></tr-v-ui-value-set-table>
+    <tr-v-ui-histogram-set-table id="histograms"></tr-v-ui-histogram-set-table>
   </template>
 </dom-module><dom-module id="tr-ui-sp-metrics-side-panel">
   <template>
@@ -3047,10 +3529,15 @@
     div#error {
       color: red;
     }
+    #results {
+      font-size: 12px;
+    }
     </style>
 
     <top-left-controls id="top_left_controls"></top-left-controls>
-    <tr-v-ui-value-set-view id="results"></tr-v-ui-value-set-view>
+
+    <tr-v-ui-histogram-set-view id="results"></tr-v-ui-histogram-set-view>
+
     <div id="error"></div>
   </template>
 </dom-module><dom-module id="tr-ui-e-s-alerts-side-panel">
@@ -3064,6 +3551,9 @@
       flex-direction: column;
       display: flex;
     }
+    tr-ui-b-table {
+      font-size: 12px;
+    }
     </style>
 
     <div id="content">
@@ -3082,8 +3572,8 @@
  * Do not edit directly.
  */
 
-'use strict';if(window.Polymer)
-throw new Error('Cannot proceed. Polymer already present.');window.Polymer={};window.Polymer.dom='shadow';(function(){function resolve(){document.body.removeAttribute('unresolved');}
+'use strict';if(window.Polymer){throw new Error('Cannot proceed. Polymer already present.');}
+window.Polymer={};window.Polymer.dom='shadow';(function(){function resolve(){document.body.removeAttribute('unresolved');}
 if(window.WebComponents){addEventListener('WebComponentsReady',resolve);}else{if(document.readyState==='interactive'||document.readyState==='complete'){resolve();}else{addEventListener('DOMContentLoaded',resolve);}}}());window.Polymer={Settings:function(){var settings=window.Polymer||{};var parts=location.search.slice(1).split('&');for(var i=0,o;i<parts.length&&(o=parts[i]);i++){o=o.split('=');o[0]&&(settings[o[0]]=o[1]||true);}
 settings.wantShadow=settings.dom==='shadow';settings.hasShadow=Boolean(Element.prototype.createShadowRoot);settings.nativeShadow=settings.hasShadow&&!window.ShadowDOMPolyfill;settings.useShadow=settings.wantShadow&&settings.hasShadow;settings.hasNativeImports=Boolean('import'in document.createElement('link'));settings.useNativeImports=settings.hasNativeImports;settings.useNativeCustomElements=!window.CustomElements||window.CustomElements.useNative;settings.useNativeShadow=settings.useShadow&&settings.nativeShadow;settings.usePolyfillProto=!settings.useNativeCustomElements&&!Object.__proto__;settings.hasNativeCSSProperties=!navigator.userAgent.match('AppleWebKit/601')&&window.CSS&&CSS.supports&&CSS.supports('box-shadow','0 0 0 var(--foo)');settings.useNativeCSSProperties=settings.hasNativeCSSProperties&&settings.lazyRegister&&settings.useNativeCSSProperties;return settings;}()};(function(){var userPolymer=window.Polymer;window.Polymer=function(prototype){if(typeof prototype==='function'){prototype=prototype.prototype;}
 if(!prototype){prototype={};}
@@ -3607,230 +4097,148 @@
 if(this.if!=this._lastIf){this.fire('dom-change');this._lastIf=this.if;}},_ensureInstance:function(){var parentNode=Polymer.dom(this).parentNode;if(parentNode){var parent=Polymer.dom(parentNode);if(!this._instance){this._instance=this.stamp();var root=this._instance.root;parent.insertBefore(root,this);}else{var c$=this._instance._children;if(c$&&c$.length){var lastChild=Polymer.dom(this).previousSibling;if(lastChild!==c$[c$.length-1]){for(var i=0,n;i<c$.length&&(n=c$[i]);i++){parent.insertBefore(n,this);}}}}}},_teardownInstance:function(){if(this._instance){var c$=this._instance._children;if(c$&&c$.length){var parent=Polymer.dom(Polymer.dom(c$[0]).parentNode);for(var i=0,n;i<c$.length&&(n=c$[i]);i++){parent.removeChild(n);}}
 this._instance=null;}},_showHideChildren:function(){var hidden=this.__hideTemplateChildren__||!this.if;if(this._instance){this._instance._showHideChildren(hidden);}},_forwardParentProp:function(prop,value){if(this._instance){this._instance[prop]=value;}},_forwardParentPath:function(path,value){if(this._instance){this._instance._notifyPath(path,value,true);}}});Polymer({is:'dom-bind',extends:'template',_template:null,created:function(){var self=this;Polymer.RenderStatus.whenReady(function(){if(document.readyState=='loading'){document.addEventListener('DOMContentLoaded',function(){self._markImportsReady();});}else{self._markImportsReady();}});},_ensureReady:function(){if(!this._readied){this._readySelf();}},_markImportsReady:function(){this._importsReady=true;this._ensureReady();},_registerFeatures:function(){this._prepConstructor();},_insertChildren:function(){var parentDom=Polymer.dom(Polymer.dom(this).parentNode);parentDom.insertBefore(this.root,this);},_removeChildren:function(){if(this._children){for(var i=0;i<this._children.length;i++){this.root.appendChild(this._children[i]);}}},_initFeatures:function(){},_scopeElementClass:function(element,selector){if(this.dataHost){return this.dataHost._scopeElementClass(element,selector);}else{return selector;}},_prepConfigure:function(){var config={};for(var prop in this._propertyEffects){config[prop]=this[prop];}
 var setupConfigure=this._setupConfigure;this._setupConfigure=function(){setupConfigure.call(this,config);};},attached:function(){if(this._importsReady){this.render();}},detached:function(){this._removeChildren();},render:function(){this._ensureReady();if(!this._children){this._template=this;this._prepAnnotations();this._prepEffects();this._prepBehaviors();this._prepConfigure();this._prepBindings();this._prepPropertyInfo();Polymer.Base._initFeatures.call(this);this._children=Polymer.TreeApi.arrayCopyChildNodes(this.root);}
-this._insertChildren();this.fire('dom-change');}});'use strict';if(!Polymer.Settings.useNativeShadow){tr.b.showPanic('Polymer error','base only works in shadow mode');}'use strict';var global=this;this.tr=(function(){if(global.tr){console.warn('Base was multiply initialized. First init wins.');return global.tr;}
-function exportPath(name){var parts=name.split('.');var cur=global;for(var part;parts.length&&(part=parts.shift());){if(part in cur){cur=cur[part];}else{cur=cur[part]={};}}
-return cur;};function isExported(name){var parts=name.split('.');var cur=global;for(var part;parts.length&&(part=parts.shift());){if(part in cur){cur=cur[part];}else{return false;}}
+this._insertChildren();this.fire('dom-change');}});'use strict';if(!Polymer.Settings.useNativeShadow){tr.b.showPanic('Polymer error','base only works in shadow mode');}'use strict';var global=this.window||this.global;this.tr=(function(){if(global.tr)return global.tr;function exportPath(name){var parts=name.split('.');var cur=global;for(var part;parts.length&&(part=parts.shift());){if(part in cur){cur=cur[part];}else{cur=cur[part]={};}}
+return cur;}
+function isExported(name){var parts=name.split('.');var cur=global;for(var part;parts.length&&(part=parts.shift());){if(part in cur){cur=cur[part];}else{return false;}}
 return true;}
-function isDefined(name){var parts=name.split('.');var curObject=global;for(var i=0;i<parts.length;i++){var partName=parts[i];var nextObject=curObject[partName];if(nextObject===undefined)
-return false;curObject=nextObject;}
+function isDefined(name){var parts=name.split('.');var curObject=global;for(var i=0;i<parts.length;i++){var partName=parts[i];var nextObject=curObject[partName];if(nextObject===undefined)return false;curObject=nextObject;}
 return true;}
-var panicElement=undefined;var rawPanicMessages=[];function showPanicElementIfNeeded(){if(panicElement)
-return;var panicOverlay=document.createElement('div');panicOverlay.style.backgroundColor='white';panicOverlay.style.border='3px solid red';panicOverlay.style.boxSizing='border-box';panicOverlay.style.color='black';panicOverlay.style.display='-webkit-flex';panicOverlay.style.height='100%';panicOverlay.style.left=0;panicOverlay.style.padding='8px';panicOverlay.style.position='fixed';panicOverlay.style.top=0;panicOverlay.style.webkitFlexDirection='column';panicOverlay.style.width='100%';panicElement=document.createElement('div');panicElement.style.webkitFlex='1 1 auto';panicElement.style.overflow='auto';panicOverlay.appendChild(panicElement);if(!document.body){setTimeout(function(){document.body.appendChild(panicOverlay);},150);}else{document.body.appendChild(panicOverlay);}}
-function showPanic(panicTitle,panicDetails){if(tr.isHeadless){if(panicDetails instanceof Error)
-throw panicDetails;throw new Error('Panic: '+panicTitle+':\n'+panicDetails);}
-if(panicDetails instanceof Error)
-panicDetails=panicDetails.stack;showPanicElementIfNeeded();var panicMessageEl=document.createElement('div');panicMessageEl.innerHTML='<h2 id="message"></h2>'+'<pre id="details"></pre>';panicMessageEl.querySelector('#message').textContent=panicTitle;panicMessageEl.querySelector('#details').textContent=panicDetails;panicElement.appendChild(panicMessageEl);rawPanicMessages.push({title:panicTitle,details:panicDetails});}
+var panicElement=undefined;var rawPanicMessages=[];function showPanicElementIfNeeded(){if(panicElement)return;var panicOverlay=document.createElement('div');panicOverlay.style.backgroundColor='white';panicOverlay.style.border='3px solid red';panicOverlay.style.boxSizing='border-box';panicOverlay.style.color='black';panicOverlay.style.display='-webkit-flex';panicOverlay.style.height='100%';panicOverlay.style.left=0;panicOverlay.style.padding='8px';panicOverlay.style.position='fixed';panicOverlay.style.top=0;panicOverlay.style.webkitFlexDirection='column';panicOverlay.style.width='100%';panicElement=document.createElement('div');panicElement.style.webkitFlex='1 1 auto';panicElement.style.overflow='auto';panicOverlay.appendChild(panicElement);if(!document.body){setTimeout(function(){document.body.appendChild(panicOverlay);},150);}else{document.body.appendChild(panicOverlay);}}
+function showPanic(panicTitle,panicDetails){if(tr.isHeadless){if(panicDetails instanceof Error)throw panicDetails;throw new Error('Panic: '+panicTitle+':\n'+panicDetails);}
+if(panicDetails instanceof Error){panicDetails=panicDetails.stack;}
+showPanicElementIfNeeded();var panicMessageEl=document.createElement('div');panicMessageEl.innerHTML='<h2 id="message"></h2>'+'<pre id="details"></pre>';panicMessageEl.querySelector('#message').textContent=panicTitle;panicMessageEl.querySelector('#details').textContent=panicDetails;panicElement.appendChild(panicMessageEl);rawPanicMessages.push({title:panicTitle,details:panicDetails});}
 function hasPanic(){return rawPanicMessages.length!==0;}
 function getPanicText(){return rawPanicMessages.map(function(msg){return msg.title;}).join(', ');}
-function exportTo(namespace,fn){var obj=exportPath(namespace);var exports=fn();for(var propertyName in exports){var propertyDescriptor=Object.getOwnPropertyDescriptor(exports,propertyName);if(propertyDescriptor)
-Object.defineProperty(obj,propertyName,propertyDescriptor);}};function initialize(){if(global.isVinn){tr.isVinn=true;}else if(global.process&&global.process.versions.node){tr.isNode=true;}else{tr.isVinn=false;tr.isNode=false;tr.doc=document;tr.isMac=/Mac/.test(navigator.platform);tr.isWindows=/Win/.test(navigator.platform);tr.isChromeOS=/CrOS/.test(navigator.userAgent);tr.isLinux=/Linux/.test(navigator.userAgent);}
+function exportTo(namespace,fn){var obj=exportPath(namespace);var exports=fn();for(var propertyName in exports){var propertyDescriptor=Object.getOwnPropertyDescriptor(exports,propertyName);if(propertyDescriptor){Object.defineProperty(obj,propertyName,propertyDescriptor);}}}
+function initialize(){if(global.isVinn){tr.isVinn=true;}else if(global.process&&global.process.versions.node){tr.isNode=true;}else{tr.isVinn=false;tr.isNode=false;tr.doc=document;tr.isMac=/Mac/.test(navigator.platform);tr.isWindows=/Win/.test(navigator.platform);tr.isChromeOS=/CrOS/.test(navigator.userAgent);tr.isLinux=/Linux/.test(navigator.userAgent);}
 tr.isHeadless=tr.isVinn||tr.isNode;}
-return{initialize:initialize,exportTo:exportTo,isExported:isExported,isDefined:isDefined,showPanic:showPanic,hasPanic:hasPanic,getPanicText:getPanicText};})();tr.initialize();'use strict';tr.exportTo('tr.b',function(){function EventTarget(){}
-EventTarget.decorate=function(target){for(var k in EventTarget.prototype){if(k=='decorate')
-continue;var v=EventTarget.prototype[k];if(typeof v!=='function')
-continue;target[k]=v;}};EventTarget.prototype={addEventListener:function(type,handler){if(!this.listeners_)
-this.listeners_=Object.create(null);if(!(type in this.listeners_)){this.listeners_[type]=[handler];}else{var handlers=this.listeners_[type];if(handlers.indexOf(handler)<0)
-handlers.push(handler);}},removeEventListener:function(type,handler){if(!this.listeners_)
-return;if(type in this.listeners_){var handlers=this.listeners_[type];var index=handlers.indexOf(handler);if(index>=0){if(handlers.length==1)
-delete this.listeners_[type];else
-handlers.splice(index,1);}}},dispatchEvent:function(event){if(!this.listeners_)
-return true;var self=this;event.__defineGetter__('target',function(){return self;});var realPreventDefault=event.preventDefault;event.preventDefault=function(){realPreventDefault.call(this);this.rawReturnValue=false;};var type=event.type;var prevented=0;if(type in this.listeners_){var handlers=this.listeners_[type].concat();for(var i=0,handler;handler=handlers[i];i++){if(handler.handleEvent)
-prevented|=handler.handleEvent.call(handler,event)===false;else
-prevented|=handler.call(this,event)===false;}}
-return!prevented&&event.rawReturnValue;},hasEventListener:function(type){return this.listeners_[type]!==undefined;}};var EventTargetHelper={decorate:function(target){for(var k in EventTargetHelper){if(k=='decorate')
-continue;var v=EventTargetHelper[k];if(typeof v!=='function')
-continue;target[k]=v;}
-target.listenerCounts_={};},addEventListener:function(type,listener,useCapture){this.__proto__.addEventListener.call(this,type,listener,useCapture);if(this.listenerCounts_[type]===undefined)
-this.listenerCounts_[type]=0;this.listenerCounts_[type]++;},removeEventListener:function(type,listener,useCapture){this.__proto__.removeEventListener.call(this,type,listener,useCapture);this.listenerCounts_[type]--;},hasEventListener:function(type){return this.listenerCounts_[type]>0;}};return{EventTarget:EventTarget,EventTargetHelper:EventTargetHelper};});'use strict';tr.exportTo('tr.b',function(){function RegisteredTypeInfo(constructor,metadata){this.constructor=constructor;this.metadata=metadata;};var BASIC_REGISTRY_MODE='BASIC_REGISTRY_MODE';var TYPE_BASED_REGISTRY_MODE='TYPE_BASED_REGISTRY_MODE';var ALL_MODES={BASIC_REGISTRY_MODE:true,TYPE_BASED_REGISTRY_MODE:true};function ExtensionRegistryOptions(mode){if(mode===undefined)
-throw new Error('Mode is required');if(!ALL_MODES[mode])
-throw new Error('Not a mode.');this.mode_=mode;this.defaultMetadata_={};this.defaultConstructor_=undefined;this.defaultTypeInfo_=undefined;this.frozen_=false;}
-ExtensionRegistryOptions.prototype={freeze:function(){if(this.frozen_)
-throw new Error('Frozen');this.frozen_=true;},get mode(){return this.mode_;},get defaultMetadata(){return this.defaultMetadata_;},set defaultMetadata(defaultMetadata){if(this.frozen_)
-throw new Error('Frozen');this.defaultMetadata_=defaultMetadata;this.defaultTypeInfo_=undefined;},get defaultConstructor(){return this.defaultConstructor_;},set defaultConstructor(defaultConstructor){if(this.frozen_)
-throw new Error('Frozen');this.defaultConstructor_=defaultConstructor;this.defaultTypeInfo_=undefined;},get defaultTypeInfo(){if(this.defaultTypeInfo_===undefined&&this.defaultConstructor_){this.defaultTypeInfo_=new RegisteredTypeInfo(this.defaultConstructor,this.defaultMetadata);}
-return this.defaultTypeInfo_;},validateConstructor:function(constructor){if(!this.mandatoryBaseClass)
-return;var curProto=constructor.prototype.__proto__;var ok=false;while(curProto){if(curProto===this.mandatoryBaseClass.prototype){ok=true;break;}
+return{initialize,exportTo,isExported,isDefined,showPanic,hasPanic,getPanicText,};})();tr.initialize();'use strict';tr.exportTo('tr.b',function(){function EventTarget(){}
+EventTarget.decorate=function(target){for(var k in EventTarget.prototype){if(k==='decorate')continue;var v=EventTarget.prototype[k];if(typeof v!=='function')continue;target[k]=v;}};EventTarget.prototype={addEventListener:function(type,handler){if(!this.listeners_){this.listeners_=Object.create(null);}
+if(!(type in this.listeners_)){this.listeners_[type]=[handler];}else{var handlers=this.listeners_[type];if(handlers.indexOf(handler)<0){handlers.push(handler);}}},removeEventListener:function(type,handler){if(!this.listeners_)return;if(type in this.listeners_){var handlers=this.listeners_[type];var index=handlers.indexOf(handler);if(index>=0){if(handlers.length===1){delete this.listeners_[type];}else{handlers.splice(index,1);}}}},dispatchEvent:function(event){if(!this.listeners_)return true;event.__defineGetter__('target',()=>this);var realPreventDefault=event.preventDefault;event.preventDefault=function(){realPreventDefault.call(this);this.rawReturnValue=false;};var type=event.type;var prevented=0;if(type in this.listeners_){var handlers=this.listeners_[type].concat();for(var i=0,handler;handler=handlers[i];i++){if(handler.handleEvent){prevented|=handler.handleEvent.call(handler,event)===false;}else{prevented|=handler.call(this,event)===false;}}}
+return!prevented&&event.rawReturnValue;},hasEventListener:function(type){return(this.listeners_!==undefined&&this.listeners_[type]!==undefined);}};return{EventTarget,};});'use strict';tr.exportTo('tr.b',function(){function RegisteredTypeInfo(constructor,metadata){this.constructor=constructor;this.metadata=metadata;}
+var BASIC_REGISTRY_MODE='BASIC_REGISTRY_MODE';var TYPE_BASED_REGISTRY_MODE='TYPE_BASED_REGISTRY_MODE';var ALL_MODES={BASIC_REGISTRY_MODE:true,TYPE_BASED_REGISTRY_MODE:true};function ExtensionRegistryOptions(mode){if(mode===undefined){throw new Error('Mode is required');}
+if(!ALL_MODES[mode]){throw new Error('Not a mode.');}
+this.mode_=mode;this.defaultMetadata_={};this.defaultConstructor_=undefined;this.defaultTypeInfo_=undefined;this.frozen_=false;}
+ExtensionRegistryOptions.prototype={freeze:function(){if(this.frozen_){throw new Error('Frozen');}
+this.frozen_=true;},get mode(){return this.mode_;},get defaultMetadata(){return this.defaultMetadata_;},set defaultMetadata(defaultMetadata){if(this.frozen_){throw new Error('Frozen');}
+this.defaultMetadata_=defaultMetadata;this.defaultTypeInfo_=undefined;},get defaultConstructor(){return this.defaultConstructor_;},set defaultConstructor(defaultConstructor){if(this.frozen_){throw new Error('Frozen');}
+this.defaultConstructor_=defaultConstructor;this.defaultTypeInfo_=undefined;},get defaultTypeInfo(){if(this.defaultTypeInfo_===undefined&&this.defaultConstructor_){this.defaultTypeInfo_=new RegisteredTypeInfo(this.defaultConstructor,this.defaultMetadata);}
+return this.defaultTypeInfo_;},validateConstructor:function(constructor){if(!this.mandatoryBaseClass)return;var curProto=constructor.prototype.__proto__;var ok=false;while(curProto){if(curProto===this.mandatoryBaseClass.prototype){ok=true;break;}
 curProto=curProto.__proto__;}
-if(!ok)
-throw new Error(constructor+'must be subclass of '+registry);}};return{BASIC_REGISTRY_MODE:BASIC_REGISTRY_MODE,TYPE_BASED_REGISTRY_MODE:TYPE_BASED_REGISTRY_MODE,ExtensionRegistryOptions:ExtensionRegistryOptions,RegisteredTypeInfo:RegisteredTypeInfo};});'use strict';tr.exportTo('tr.b',function(){var Event;if(tr.isHeadless){function HeadlessEvent(type,opt_bubbles,opt_preventable){this.type=type;this.bubbles=(opt_bubbles!==undefined?!!opt_bubbles:false);this.cancelable=(opt_preventable!==undefined?!!opt_preventable:false);this.defaultPrevented=false;this.cancelBubble=false;};HeadlessEvent.prototype={preventDefault:function(){this.defaultPrevented=true;},stopPropagation:function(){this.cancelBubble=true;}};Event=HeadlessEvent;}else{function TrEvent(type,opt_bubbles,opt_preventable){var e=tr.doc.createEvent('Event');e.initEvent(type,!!opt_bubbles,!!opt_preventable);e.__proto__=global.Event.prototype;return e;};TrEvent.prototype={__proto__:global.Event.prototype};Event=TrEvent;}
-function dispatchSimpleEvent(target,type,opt_bubbles,opt_cancelable,opt_fields){var e=new tr.b.Event(type,opt_bubbles,opt_cancelable);if(opt_fields){tr.b.iterItems(opt_fields,function(name,value){e[name]=value;});}
+if(!ok){throw new Error(constructor+'must be subclass of '+registry);}}};return{BASIC_REGISTRY_MODE,TYPE_BASED_REGISTRY_MODE,ExtensionRegistryOptions,RegisteredTypeInfo,};});'use strict';tr.exportTo('tr.b',function(){var Event;if(tr.isHeadless){function HeadlessEvent(type,opt_bubbles,opt_preventable){this.type=type;this.bubbles=(opt_bubbles!==undefined?!!opt_bubbles:false);this.cancelable=(opt_preventable!==undefined?!!opt_preventable:false);this.defaultPrevented=false;this.cancelBubble=false;}
+HeadlessEvent.prototype={preventDefault:function(){this.defaultPrevented=true;},stopPropagation:function(){this.cancelBubble=true;}};Event=HeadlessEvent;}else{function TrEvent(type,opt_bubbles,opt_preventable){var e=tr.doc.createEvent('Event');e.initEvent(type,!!opt_bubbles,!!opt_preventable);e.__proto__=global.Event.prototype;return e;}
+TrEvent.prototype={__proto__:global.Event.prototype};Event=TrEvent;}
+function dispatchSimpleEvent(target,type,opt_bubbles,opt_cancelable,opt_fields){var e=new tr.b.Event(type,opt_bubbles,opt_cancelable);if(opt_fields){for(var[name,value]of Object.entries(opt_fields)){e[name]=value;}}
 return target.dispatchEvent(e);}
-return{Event:Event,dispatchSimpleEvent:dispatchSimpleEvent};});'use strict';tr.exportTo('tr.b',function(){var RegisteredTypeInfo=tr.b.RegisteredTypeInfo;var ExtensionRegistryOptions=tr.b.ExtensionRegistryOptions;function decorateBasicExtensionRegistry(registry,extensionRegistryOptions){var savedStateStack=[];registry.registeredTypeInfos_=[];registry.register=function(constructor,opt_metadata){if(registry.findIndexOfRegisteredConstructor(constructor)!==undefined)
-throw new Error('Handler already registered for '+constructor);extensionRegistryOptions.validateConstructor(constructor);var metadata={};for(var k in extensionRegistryOptions.defaultMetadata)
-metadata[k]=extensionRegistryOptions.defaultMetadata[k];if(opt_metadata){for(var k in opt_metadata)
-metadata[k]=opt_metadata[k];}
-var typeInfo=new RegisteredTypeInfo(constructor,metadata);var e=new tr.b.Event('will-register');e.typeInfo=typeInfo;registry.dispatchEvent(e);registry.registeredTypeInfos_.push(typeInfo);e=new tr.b.Event('registry-changed');registry.dispatchEvent(e);};registry.pushCleanStateBeforeTest=function(){savedStateStack.push(registry.registeredTypeInfos_);registry.registeredTypeInfos_=[];var e=new tr.b.Event('registry-changed');registry.dispatchEvent(e);};registry.popCleanStateAfterTest=function(){registry.registeredTypeInfos_=savedStateStack[0];savedStateStack.splice(0,1);var e=new tr.b.Event('registry-changed');registry.dispatchEvent(e);};registry.findIndexOfRegisteredConstructor=function(constructor){for(var i=0;i<registry.registeredTypeInfos_.length;i++)
-if(registry.registeredTypeInfos_[i].constructor==constructor)
-return i;return undefined;};registry.unregister=function(constructor){var foundIndex=registry.findIndexOfRegisteredConstructor(constructor);if(foundIndex===undefined)
-throw new Error(constructor+' not registered');registry.registeredTypeInfos_.splice(foundIndex,1);var e=new tr.b.Event('registry-changed');registry.dispatchEvent(e);};registry.getAllRegisteredTypeInfos=function(){return registry.registeredTypeInfos_;};registry.findTypeInfo=function(constructor){var foundIndex=this.findIndexOfRegisteredConstructor(constructor);if(foundIndex!==undefined)
-return this.registeredTypeInfos_[foundIndex];return undefined;};registry.findTypeInfoMatching=function(predicate,opt_this){opt_this=opt_this?opt_this:undefined;for(var i=0;i<registry.registeredTypeInfos_.length;++i){var typeInfo=registry.registeredTypeInfos_[i];if(predicate.call(opt_this,typeInfo))
-return typeInfo;}
-return extensionRegistryOptions.defaultTypeInfo;};registry.findTypeInfoWithName=function(name){if(typeof(name)!=='string')
-throw new Error('Name is not a string.');var typeInfo=registry.findTypeInfoMatching(function(ti){return ti.constructor.name===name;});if(typeInfo)
-return typeInfo;return undefined;};}
-return{_decorateBasicExtensionRegistry:decorateBasicExtensionRegistry};});'use strict';tr.exportTo('tr.b',function(){var categoryPartsFor={};function getCategoryParts(category){var parts=categoryPartsFor[category];if(parts!==undefined)
-return parts;parts=category.split(',');categoryPartsFor[category]=parts;return parts;}
-return{getCategoryParts:getCategoryParts};});'use strict';tr.exportTo('tr.b',function(){var getCategoryParts=tr.b.getCategoryParts;var RegisteredTypeInfo=tr.b.RegisteredTypeInfo;var ExtensionRegistryOptions=tr.b.ExtensionRegistryOptions;function decorateTypeBasedExtensionRegistry(registry,extensionRegistryOptions){var savedStateStack=[];registry.registeredTypeInfos_=[];registry.categoryPartToTypeInfoMap_=new Map();registry.typeNameToTypeInfoMap_=new Map();registry.register=function(constructor,metadata){extensionRegistryOptions.validateConstructor(constructor);var typeInfo=new RegisteredTypeInfo(constructor,metadata||extensionRegistryOptions.defaultMetadata);typeInfo.typeNames=[];typeInfo.categoryParts=[];if(metadata&&metadata.typeName)
-typeInfo.typeNames.push(metadata.typeName);if(metadata&&metadata.typeNames){typeInfo.typeNames.push.apply(typeInfo.typeNames,metadata.typeNames);}
+return{Event,dispatchSimpleEvent,};});'use strict';tr.exportTo('tr.b',function(){var RegisteredTypeInfo=tr.b.RegisteredTypeInfo;var ExtensionRegistryOptions=tr.b.ExtensionRegistryOptions;function decorateBasicExtensionRegistry(registry,extensionRegistryOptions){var savedStateStack=[];registry.registeredTypeInfos_=[];registry.register=function(constructor,opt_metadata){if(registry.findIndexOfRegisteredConstructor(constructor)!==undefined){throw new Error('Handler already registered for '+constructor);}
+extensionRegistryOptions.validateConstructor(constructor);var metadata={};for(var k in extensionRegistryOptions.defaultMetadata){metadata[k]=extensionRegistryOptions.defaultMetadata[k];}
+if(opt_metadata){for(var k in opt_metadata){metadata[k]=opt_metadata[k];}}
+var typeInfo=new RegisteredTypeInfo(constructor,metadata);var e=new tr.b.Event('will-register');e.typeInfo=typeInfo;registry.dispatchEvent(e);registry.registeredTypeInfos_.push(typeInfo);e=new tr.b.Event('registry-changed');registry.dispatchEvent(e);};registry.pushCleanStateBeforeTest=function(){savedStateStack.push(registry.registeredTypeInfos_);registry.registeredTypeInfos_=[];var e=new tr.b.Event('registry-changed');registry.dispatchEvent(e);};registry.popCleanStateAfterTest=function(){registry.registeredTypeInfos_=savedStateStack[0];savedStateStack.splice(0,1);var e=new tr.b.Event('registry-changed');registry.dispatchEvent(e);};registry.findIndexOfRegisteredConstructor=function(constructor){for(var i=0;i<registry.registeredTypeInfos_.length;i++){if(registry.registeredTypeInfos_[i].constructor===constructor){return i;}}
+return undefined;};registry.unregister=function(constructor){var foundIndex=registry.findIndexOfRegisteredConstructor(constructor);if(foundIndex===undefined){throw new Error(constructor+' not registered');}
+registry.registeredTypeInfos_.splice(foundIndex,1);var e=new tr.b.Event('registry-changed');registry.dispatchEvent(e);};registry.getAllRegisteredTypeInfos=function(){return registry.registeredTypeInfos_;};registry.findTypeInfo=function(constructor){var foundIndex=this.findIndexOfRegisteredConstructor(constructor);if(foundIndex!==undefined){return this.registeredTypeInfos_[foundIndex];}
+return undefined;};registry.findTypeInfoMatching=function(predicate,opt_this){opt_this=opt_this?opt_this:undefined;for(var i=0;i<registry.registeredTypeInfos_.length;++i){var typeInfo=registry.registeredTypeInfos_[i];if(predicate.call(opt_this,typeInfo)){return typeInfo;}}
+return extensionRegistryOptions.defaultTypeInfo;};registry.findTypeInfoWithName=function(name){if(typeof(name)!=='string'){throw new Error('Name is not a string.');}
+var typeInfo=registry.findTypeInfoMatching(function(ti){return ti.constructor.name===name;});if(typeInfo)return typeInfo;return undefined;};}
+return{_decorateBasicExtensionRegistry:decorateBasicExtensionRegistry};});'use strict';tr.exportTo('tr.b',function(){var categoryPartsFor={};function getCategoryParts(category){var parts=categoryPartsFor[category];if(parts!==undefined)return parts;parts=category.split(',');categoryPartsFor[category]=parts;return parts;}
+return{getCategoryParts,};});'use strict';tr.exportTo('tr.b',function(){var getCategoryParts=tr.b.getCategoryParts;var RegisteredTypeInfo=tr.b.RegisteredTypeInfo;var ExtensionRegistryOptions=tr.b.ExtensionRegistryOptions;function decorateTypeBasedExtensionRegistry(registry,extensionRegistryOptions){var savedStateStack=[];registry.registeredTypeInfos_=[];registry.categoryPartToTypeInfoMap_=new Map();registry.typeNameToTypeInfoMap_=new Map();registry.register=function(constructor,metadata){extensionRegistryOptions.validateConstructor(constructor);var typeInfo=new RegisteredTypeInfo(constructor,metadata||extensionRegistryOptions.defaultMetadata);typeInfo.typeNames=[];typeInfo.categoryParts=[];if(metadata&&metadata.typeName){typeInfo.typeNames.push(metadata.typeName);}
+if(metadata&&metadata.typeNames){typeInfo.typeNames.push.apply(typeInfo.typeNames,metadata.typeNames);}
 if(metadata&&metadata.categoryParts){typeInfo.categoryParts.push.apply(typeInfo.categoryParts,metadata.categoryParts);}
-if(typeInfo.typeNames.length===0&&typeInfo.categoryParts.length===0)
-throw new Error('typeName or typeNames must be provided');typeInfo.typeNames.forEach(function(typeName){if(registry.typeNameToTypeInfoMap_.has(typeName))
-throw new Error('typeName '+typeName+' already registered');});typeInfo.categoryParts.forEach(function(categoryPart){if(registry.categoryPartToTypeInfoMap_.has(categoryPart)){throw new Error('categoryPart '+categoryPart+' already registered');}});var e=new tr.b.Event('will-register');e.typeInfo=typeInfo;registry.dispatchEvent(e);typeInfo.typeNames.forEach(function(typeName){registry.typeNameToTypeInfoMap_.set(typeName,typeInfo);});typeInfo.categoryParts.forEach(function(categoryPart){registry.categoryPartToTypeInfoMap_.set(categoryPart,typeInfo);});registry.registeredTypeInfos_.push(typeInfo);var e=new tr.b.Event('registry-changed');registry.dispatchEvent(e);};registry.pushCleanStateBeforeTest=function(){savedStateStack.push({registeredTypeInfos:registry.registeredTypeInfos_,typeNameToTypeInfoMap:registry.typeNameToTypeInfoMap_,categoryPartToTypeInfoMap:registry.categoryPartToTypeInfoMap_});registry.registeredTypeInfos_=[];registry.typeNameToTypeInfoMap_=new Map();registry.categoryPartToTypeInfoMap_=new Map();var e=new tr.b.Event('registry-changed');registry.dispatchEvent(e);};registry.popCleanStateAfterTest=function(){var state=savedStateStack[0];savedStateStack.splice(0,1);registry.registeredTypeInfos_=state.registeredTypeInfos;registry.typeNameToTypeInfoMap_=state.typeNameToTypeInfoMap;registry.categoryPartToTypeInfoMap_=state.categoryPartToTypeInfoMap;var e=new tr.b.Event('registry-changed');registry.dispatchEvent(e);};registry.unregister=function(constructor){var typeInfoIndex=-1;for(var i=0;i<registry.registeredTypeInfos_.length;i++){if(registry.registeredTypeInfos_[i].constructor==constructor){typeInfoIndex=i;break;}}
-if(typeInfoIndex===-1)
-throw new Error(constructor+' not registered');var typeInfo=registry.registeredTypeInfos_[typeInfoIndex];registry.registeredTypeInfos_.splice(typeInfoIndex,1);typeInfo.typeNames.forEach(function(typeName){registry.typeNameToTypeInfoMap_.delete(typeName);});typeInfo.categoryParts.forEach(function(categoryPart){registry.categoryPartToTypeInfoMap_.delete(categoryPart);});var e=new tr.b.Event('registry-changed');registry.dispatchEvent(e);};registry.getTypeInfo=function(category,typeName){if(category){var categoryParts=getCategoryParts(category);for(var i=0;i<categoryParts.length;i++){var categoryPart=categoryParts[i];var typeInfo=registry.categoryPartToTypeInfoMap_.get(categoryPart);if(typeInfo!==undefined)
-return typeInfo;}}
-var typeInfo=registry.typeNameToTypeInfoMap_.get(typeName);if(typeInfo!==undefined)
-return typeInfo;return extensionRegistryOptions.defaultTypeInfo;};registry.getConstructor=function(category,typeName){var typeInfo=registry.getTypeInfo(category,typeName);if(typeInfo)
-return typeInfo.constructor;return undefined;};}
-return{_decorateTypeBasedExtensionRegistry:decorateTypeBasedExtensionRegistry};});'use strict';tr.exportTo('tr.b',function(){function asArray(x){var values=[];if(x[Symbol.iterator])
-for(var value of x)
-values.push(value);else
-for(var i=0;i<x.length;i++)
-values.push(x[i]);return values;}
-function getOnlyElement(iterable){var iterator=iterable[Symbol.iterator]();var firstIteration=iterator.next();if(firstIteration.done)
-throw new Error('getOnlyElement was passed an empty iterable.');var secondIteration=iterator.next();if(!secondIteration.done)
-throw new Error('getOnlyElement was passed an iterable with multiple elements.');return firstIteration.value;}
-function getFirstElement(iterable){var iterator=iterable[Symbol.iterator]();var result=iterator.next();if(result.done)
-throw new Error('getFirstElement was passed an empty iterable.');return result.value;}
-function compareArrays(x,y,elementCmp){var minLength=Math.min(x.length,y.length);for(var i=0;i<minLength;i++){var tmp=elementCmp(x[i],y[i]);if(tmp)
-return tmp;}
-if(x.length==y.length)
-return 0;if(x[i]===undefined)
-return-1;return 1;}
-function comparePossiblyUndefinedValues(x,y,cmp,opt_this){if(x!==undefined&&y!==undefined)
-return cmp.call(opt_this,x,y);if(x!==undefined)
-return-1;if(y!==undefined)
-return 1;return 0;}
-function compareNumericWithNaNs(x,y){if(!isNaN(x)&&!isNaN(y))
-return x-y;if(isNaN(x))
-return 1;if(isNaN(y))
-return-1;return 0;}
-function concatenateArrays(){var values=[];for(var i=0;i<arguments.length;i++){if(!(arguments[i]instanceof Array))
-throw new Error('Arguments '+i+'is not an array');values.push.apply(values,arguments[i]);}
+if(typeInfo.typeNames.length===0&&typeInfo.categoryParts.length===0){throw new Error('typeName or typeNames must be provided');}
+typeInfo.typeNames.forEach(function(typeName){if(registry.typeNameToTypeInfoMap_.has(typeName)){throw new Error('typeName '+typeName+' already registered');}});typeInfo.categoryParts.forEach(function(categoryPart){if(registry.categoryPartToTypeInfoMap_.has(categoryPart)){throw new Error('categoryPart '+categoryPart+' already registered');}});var e=new tr.b.Event('will-register');e.typeInfo=typeInfo;registry.dispatchEvent(e);typeInfo.typeNames.forEach(function(typeName){registry.typeNameToTypeInfoMap_.set(typeName,typeInfo);});typeInfo.categoryParts.forEach(function(categoryPart){registry.categoryPartToTypeInfoMap_.set(categoryPart,typeInfo);});registry.registeredTypeInfos_.push(typeInfo);var e=new tr.b.Event('registry-changed');registry.dispatchEvent(e);};registry.pushCleanStateBeforeTest=function(){savedStateStack.push({registeredTypeInfos:registry.registeredTypeInfos_,typeNameToTypeInfoMap:registry.typeNameToTypeInfoMap_,categoryPartToTypeInfoMap:registry.categoryPartToTypeInfoMap_});registry.registeredTypeInfos_=[];registry.typeNameToTypeInfoMap_=new Map();registry.categoryPartToTypeInfoMap_=new Map();var e=new tr.b.Event('registry-changed');registry.dispatchEvent(e);};registry.popCleanStateAfterTest=function(){var state=savedStateStack[0];savedStateStack.splice(0,1);registry.registeredTypeInfos_=state.registeredTypeInfos;registry.typeNameToTypeInfoMap_=state.typeNameToTypeInfoMap;registry.categoryPartToTypeInfoMap_=state.categoryPartToTypeInfoMap;var e=new tr.b.Event('registry-changed');registry.dispatchEvent(e);};registry.unregister=function(constructor){var typeInfoIndex=-1;for(var i=0;i<registry.registeredTypeInfos_.length;i++){if(registry.registeredTypeInfos_[i].constructor===constructor){typeInfoIndex=i;break;}}
+if(typeInfoIndex===-1){throw new Error(constructor+' not registered');}
+var typeInfo=registry.registeredTypeInfos_[typeInfoIndex];registry.registeredTypeInfos_.splice(typeInfoIndex,1);typeInfo.typeNames.forEach(function(typeName){registry.typeNameToTypeInfoMap_.delete(typeName);});typeInfo.categoryParts.forEach(function(categoryPart){registry.categoryPartToTypeInfoMap_.delete(categoryPart);});var e=new tr.b.Event('registry-changed');registry.dispatchEvent(e);};registry.getTypeInfo=function(category,typeName){if(category){var categoryParts=getCategoryParts(category);for(var i=0;i<categoryParts.length;i++){var categoryPart=categoryParts[i];var typeInfo=registry.categoryPartToTypeInfoMap_.get(categoryPart);if(typeInfo!==undefined)return typeInfo;}}
+var typeInfo=registry.typeNameToTypeInfoMap_.get(typeName);if(typeInfo!==undefined)return typeInfo;return extensionRegistryOptions.defaultTypeInfo;};registry.getConstructor=function(category,typeName){var typeInfo=registry.getTypeInfo(category,typeName);if(typeInfo)return typeInfo.constructor;return undefined;};}
+return{_decorateTypeBasedExtensionRegistry:decorateTypeBasedExtensionRegistry};});'use strict';tr.exportTo('tr.b',function(){function asArray(x){var values=[];if(x[Symbol.iterator]){for(var value of x){values.push(value);}}else{for(var i=0;i<x.length;i++){values.push(x[i]);}}
 return values;}
+function getOnlyElement(iterable){var iterator=iterable[Symbol.iterator]();var firstIteration=iterator.next();if(firstIteration.done){throw new Error('getOnlyElement was passed an empty iterable.');}
+var secondIteration=iterator.next();if(!secondIteration.done){throw new Error('getOnlyElement was passed an iterable with multiple elements.');}
+return firstIteration.value;}
+function getFirstElement(iterable){var iterator=iterable[Symbol.iterator]();var result=iterator.next();if(result.done){throw new Error('getFirstElement was passed an empty iterable.');}
+return result.value;}
+function compareArrays(x,y,elementCmp){var minLength=Math.min(x.length,y.length);for(var i=0;i<minLength;i++){var tmp=elementCmp(x[i],y[i]);if(tmp)return tmp;}
+if(x.length===y.length)return 0;if(x[i]===undefined)return-1;return 1;}
+function comparePossiblyUndefinedValues(x,y,cmp,opt_this){if(x!==undefined&&y!==undefined){return cmp.call(opt_this,x,y);}
+if(x!==undefined)return-1;if(y!==undefined)return 1;return 0;}
 function concatenateObjects(){var result={};for(var i=0;i<arguments.length;i++){var object=arguments[i];for(var j in object){result[j]=object[j];}}
 return result;}
-function cloneDictionary(dict){var clone={};for(var k in dict){clone[k]=dict[k];}
-return clone;}
-function dictionaryKeys(dict){var keys=[];for(var key in dict)
-keys.push(key);return keys;}
-function dictionaryValues(dict){var values=[];for(var key in dict)
-values.push(dict[key]);return values;}
-function dictionaryLength(dict){var n=0;for(var key in dict)
-n++;return n;}
-function dictionaryContainsValue(dict,value){for(var key in dict)
-if(dict[key]===value)
-return true;return false;}
-function every(iterable,predicate){for(var x of iterable)
-if(!predicate(x))
-return false;return true;}
-function group(ary,callback,opt_this,opt_arrayConstructor){var arrayConstructor=opt_arrayConstructor||Array;var results={};for(var element of ary){var key=callback.call(opt_this,element);if(!(key in results))
-results[key]=new arrayConstructor();results[key].push(element);}
+function dictionaryValues(dict){var values=[];for(var key in dict){values.push(dict[key]);}
+return values;}
+function dictionaryLength(dict){var n=0;for(var key in dict){n++;}
+return n;}
+function dictionaryContainsValue(dict,value){for(var key in dict){if(dict[key]===value){return true;}}
+return false;}
+function group(ary,callback,opt_this,opt_arrayConstructor){var arrayConstructor=opt_arrayConstructor||Array;var results={};for(var element of ary){var key=callback.call(opt_this,element);if(!(key in results)){results[key]=new arrayConstructor();}
+results[key].push(element);}
 return results;}
 function groupIntoMap(ary,callback,opt_this,opt_arrayConstructor){var arrayConstructor=opt_arrayConstructor||Array;var results=new Map();for(var element of ary){var key=callback.call(opt_this,element);var items=results.get(key);if(items===undefined){items=new arrayConstructor();results.set(key,items);}
 items.push(element);}
 return results;}
-function iterItems(dict,fn,opt_this){opt_this=opt_this||this;var keys=Object.keys(dict);for(var i=0;i<keys.length;i++){var key=keys[i];fn.call(opt_this,key,dict[key]);}}
 function mapItems(dict,fn,opt_this){opt_this=opt_this||this;var result={};var keys=Object.keys(dict);for(var i=0;i<keys.length;i++){var key=keys[i];result[key]=fn.call(opt_this,key,dict[key]);}
 return result;}
-function filterItems(dict,predicate,opt_this){opt_this=opt_this||this;var result={};var keys=Object.keys(dict);for(var i=0;i<keys.length;i++){var key=keys[i];var value=dict[key];if(predicate.call(opt_this,key,value))
-result[key]=value;}
+function filterItems(dict,predicate,opt_this){opt_this=opt_this||this;var result={};var keys=Object.keys(dict);for(var i=0;i<keys.length;i++){var key=keys[i];var value=dict[key];if(predicate.call(opt_this,key,value)){result[key]=value;}}
 return result;}
-function iterObjectFieldsRecursively(object,func){if(!(object instanceof Object))
-return;if(object instanceof Array){for(var i=0;i<object.length;i++){func(object,i,object[i]);iterObjectFieldsRecursively(object[i],func);}
+function iterObjectFieldsRecursively(object,func){if(!(object instanceof Object))return;if(object instanceof Array){for(var i=0;i<object.length;i++){func(object,i,object[i]);iterObjectFieldsRecursively(object[i],func);}
 return;}
 for(var key in object){var value=object[key];func(object,key,value);iterObjectFieldsRecursively(value,func);}}
-function invertArrayOfDicts(array,opt_dictGetter,opt_this){opt_this=opt_this||this;var result={};for(var i=0;i<array.length;i++){var item=array[i];if(item===undefined)
-continue;var dict=opt_dictGetter?opt_dictGetter.call(opt_this,item):item;if(dict===undefined)
-continue;for(var key in dict){var valueList=result[key];if(valueList===undefined)
-result[key]=valueList=new Array(array.length);valueList[i]=dict[key];}}
+function invertArrayOfDicts(array,opt_dictGetter,opt_this){opt_this=opt_this||this;var result={};for(var i=0;i<array.length;i++){var item=array[i];if(item===undefined)continue;var dict=opt_dictGetter?opt_dictGetter.call(opt_this,item):item;if(dict===undefined)continue;for(var key in dict){var valueList=result[key];if(valueList===undefined){result[key]=valueList=new Array(array.length);}
+valueList[i]=dict[key];}}
 return result;}
 function arrayToDict(array,valueToKeyFn,opt_this){opt_this=opt_this||this;var result={};var length=array.length;for(var i=0;i<length;i++){var value=array[i];var key=valueToKeyFn.call(opt_this,value);result[key]=value;}
 return result;}
 function identity(d){return d;}
-function findFirstIndexInArray(ary,opt_func,opt_this){var func=opt_func||identity;for(var i=0;i<ary.length;i++){if(func.call(opt_this,ary[i],i))
-return i;}
+function findFirstIndexInArray(ary,opt_func,opt_this){var func=opt_func||identity;for(var i=0;i<ary.length;i++){if(func.call(opt_this,ary[i],i))return i;}
 return-1;}
-function findFirstInArray(ary,opt_func,opt_this){var i=findFirstIndexInArray(ary,opt_func,opt_func);if(i===-1)
-return undefined;return ary[i];}
-function findFirstKeyInDictMatching(dict,opt_func,opt_this){var func=opt_func||identity;for(var key in dict){if(func.call(opt_this,key,dict[key]))
-return key;}
+function findFirstInArray(ary,opt_func,opt_this){var i=findFirstIndexInArray(ary,opt_func,opt_func);if(i===-1)return undefined;return ary[i];}
+function findFirstKeyInDictMatching(dict,opt_func,opt_this){var func=opt_func||identity;for(var key in dict){if(func.call(opt_this,key,dict[key])){return key;}}
 return undefined;}
-function mapValues(map){var values=[];for(var value of map.values())
-values.push(value);return values;}
-function iterMapItems(map,fn,opt_this){opt_this=opt_this||this;for(var key of map.keys())
-fn.call(opt_this,key,map.get(key));}
-return{asArray:asArray,concatenateArrays:concatenateArrays,concatenateObjects:concatenateObjects,compareArrays:compareArrays,comparePossiblyUndefinedValues:comparePossiblyUndefinedValues,compareNumericWithNaNs:compareNumericWithNaNs,cloneDictionary:cloneDictionary,dictionaryLength:dictionaryLength,dictionaryKeys:dictionaryKeys,dictionaryValues:dictionaryValues,dictionaryContainsValue:dictionaryContainsValue,every:every,getOnlyElement:getOnlyElement,getFirstElement:getFirstElement,group:group,groupIntoMap:groupIntoMap,iterItems:iterItems,mapItems:mapItems,filterItems:filterItems,iterObjectFieldsRecursively:iterObjectFieldsRecursively,invertArrayOfDicts:invertArrayOfDicts,arrayToDict:arrayToDict,identity:identity,findFirstIndexInArray:findFirstIndexInArray,findFirstInArray:findFirstInArray,findFirstKeyInDictMatching:findFirstKeyInDictMatching,mapValues:mapValues,iterMapItems:iterMapItems};});'use strict';tr.exportTo('tr.b',function(){function decorateExtensionRegistry(registry,registryOptions){if(registry.register)
-throw new Error('Already has registry');registryOptions.freeze();if(registryOptions.mode==tr.b.BASIC_REGISTRY_MODE){tr.b._decorateBasicExtensionRegistry(registry,registryOptions);}else if(registryOptions.mode==tr.b.TYPE_BASED_REGISTRY_MODE){tr.b._decorateTypeBasedExtensionRegistry(registry,registryOptions);}else{throw new Error('Unrecognized mode');}
-if(registry.addEventListener===undefined)
-tr.b.EventTarget.decorate(registry);}
-return{decorateExtensionRegistry:decorateExtensionRegistry};});'use strict';tr.exportTo('tr.importer',function(){function Importer(){}
-Importer.prototype={__proto__:Object.prototype,get importerName(){return'Importer';},isTraceDataContainer:function(){return false;},extractSubtraces:function(){return[];},importClockSyncMarkers:function(){},importEvents:function(){},importSampleData:function(){},finalizeImport:function(){}};var options=new tr.b.ExtensionRegistryOptions(tr.b.BASIC_REGISTRY_MODE);options.defaultMetadata={};options.mandatoryBaseClass=Importer;tr.b.decorateExtensionRegistry(Importer,options);Importer.findImporterFor=function(eventData){var typeInfo=Importer.findTypeInfoMatching(function(ti){return ti.constructor.canImport(eventData);});if(typeInfo)
-return typeInfo.constructor;return undefined;};return{Importer:Importer};});'use strict';tr.exportTo('tr.e.importer.gcloud_trace',function(){function GcloudTraceImporter(model,eventData){this.importPriority=2;this.eventData_=eventData;}
-GcloudTraceImporter.canImport=function(eventData){if(typeof(eventData)!=='string'&&!(eventData instanceof String))
-return false;var normalizedEventData=eventData.slice(0,20).replace(/\s/g,'');if(normalizedEventData.length<14)
-return false;return normalizedEventData.slice(0,14)=='{"projectId":"';};GcloudTraceImporter.prototype={__proto__:tr.importer.Importer.prototype,get importerName(){return'GcloudTraceImporter';},extractSubtraces:function(){var traceEvents=this.createEventsForTrace();return traceEvents?[traceEvents]:[];},createEventsForTrace:function(){var events=[];var trace=JSON.parse(this.eventData_);var spanLength=trace.spans.length;for(var i=0;i<spanLength;i++){events.push(this.createEventForSpan(trace.traceId,trace.spans[i]));}
+function mapValues(map){var values=[];for(var value of map.values()){values.push(value);}
+return values;}
+function setsEqual(a,b){if(!(a instanceof Set)||!(b instanceof Set))return false;if(a.size!==b.size)return false;return Array.from(a).every(x=>b.has(x));}
+return{asArray,concatenateObjects,compareArrays,comparePossiblyUndefinedValues,dictionaryLength,dictionaryValues,dictionaryContainsValue,getOnlyElement,getFirstElement,group,groupIntoMap,mapItems,filterItems,iterObjectFieldsRecursively,invertArrayOfDicts,arrayToDict,identity,findFirstIndexInArray,findFirstInArray,findFirstKeyInDictMatching,mapValues,setsEqual,};});'use strict';tr.exportTo('tr.b',function(){function decorateExtensionRegistry(registry,registryOptions){if(registry.register){throw new Error('Already has registry');}
+registryOptions.freeze();if(registryOptions.mode===tr.b.BASIC_REGISTRY_MODE){tr.b._decorateBasicExtensionRegistry(registry,registryOptions);}else if(registryOptions.mode===tr.b.TYPE_BASED_REGISTRY_MODE){tr.b._decorateTypeBasedExtensionRegistry(registry,registryOptions);}else{throw new Error('Unrecognized mode');}
+if(registry.addEventListener===undefined){tr.b.EventTarget.decorate(registry);}}
+return{decorateExtensionRegistry,};});'use strict';tr.exportTo('tr.importer',function(){function Importer(){}
+Importer.prototype={__proto__:Object.prototype,get importerName(){return'Importer';},isTraceDataContainer:function(){return false;},extractSubtraces:function(){return[];},importClockSyncMarkers:function(){},importEvents:function(){},importSampleData:function(){},finalizeImport:function(){}};var options=new tr.b.ExtensionRegistryOptions(tr.b.BASIC_REGISTRY_MODE);options.defaultMetadata={};options.mandatoryBaseClass=Importer;tr.b.decorateExtensionRegistry(Importer,options);Importer.findImporterFor=function(eventData){var typeInfo=Importer.findTypeInfoMatching(function(ti){return ti.constructor.canImport(eventData);});if(typeInfo){return typeInfo.constructor;}
+return undefined;};return{Importer,};});'use strict';tr.exportTo('tr.e.importer.gcloud_trace',function(){function GcloudTraceImporter(model,eventData){this.importPriority=2;this.eventData_=eventData;}
+GcloudTraceImporter.canImport=function(eventData){if(typeof(eventData)!=='string'&&!(eventData instanceof String)){return false;}
+var normalizedEventData=eventData.slice(0,20).replace(/\s/g,'');if(normalizedEventData.length<14)return false;return normalizedEventData.slice(0,14)==='{"projectId":"';};GcloudTraceImporter.prototype={__proto__:tr.importer.Importer.prototype,get importerName(){return'GcloudTraceImporter';},extractSubtraces:function(){var traceEvents=this.createEventsForTrace();return traceEvents?[traceEvents]:[];},createEventsForTrace:function(){var events=[];var trace=JSON.parse(this.eventData_);var spanLength=trace.spans.length;for(var i=0;i<spanLength;i++){events.push(this.createEventForSpan(trace.traceId,trace.spans[i]));}
 return{'traceEvents':events};},createEventForSpan:function(traceId,span){var newArgs={};if(span.labels){newArgs=JSON.parse(JSON.stringify(span.labels));}
 newArgs['Span ID']=span.spanId;newArgs['Start Time']=span.startTime;newArgs['End Time']=span.endTime;if(span.parentSpanId){newArgs['Parent Span ID']=span.parentSpanId;}
-return{name:span.name,args:newArgs,pid:traceId,ts:Date.parse(span.startTime)*1000,dur:(Date.parse(span.endTime)-Date.parse(span.startTime))*1000,cat:'tracespan',tid:traceId,ph:'X'};}};tr.importer.Importer.register(GcloudTraceImporter);return{GcloudTraceImporter:GcloudTraceImporter};});'use strict';tr.exportTo('tr.b',function(){function convertEventsToRanges(events){return events.map(function(event){return tr.b.Range.fromExplicitRange(event.start,event.end);});}
-function mergeRanges(inRanges,mergeThreshold,mergeFunction){var remainingEvents=inRanges.slice();remainingEvents.sort(function(x,y){return x.min-y.min;});if(remainingEvents.length<=1){var merged=[];if(remainingEvents.length==1){merged.push(mergeFunction(remainingEvents));}
+return{name:span.name,args:newArgs,pid:traceId,ts:Date.parse(span.startTime)*1000,dur:(Date.parse(span.endTime)-Date.parse(span.startTime))*1000,cat:'tracespan',tid:traceId,ph:'X'};}};tr.importer.Importer.register(GcloudTraceImporter);return{GcloudTraceImporter,};});'use strict';tr.exportTo('tr.b.math',function(){function convertEventsToRanges(events){return events.map(function(event){return tr.b.math.Range.fromExplicitRange(event.start,event.end);});}
+function mergeRanges(inRanges,mergeThreshold,mergeFunction){var remainingEvents=inRanges.slice();remainingEvents.sort(function(x,y){return x.min-y.min;});if(remainingEvents.length<=1){var merged=[];if(remainingEvents.length===1){merged.push(mergeFunction(remainingEvents));}
 return merged;}
 var mergedEvents=[];var currentMergeBuffer=[];var rightEdge;function beginMerging(){currentMergeBuffer.push(remainingEvents[0]);remainingEvents.splice(0,1);rightEdge=currentMergeBuffer[0].max;}
-function flushCurrentMergeBuffer(){if(currentMergeBuffer.length==0)
-return;mergedEvents.push(mergeFunction(currentMergeBuffer));currentMergeBuffer=[];if(remainingEvents.length!=0)
-beginMerging();}
+function flushCurrentMergeBuffer(){if(currentMergeBuffer.length===0)return;mergedEvents.push(mergeFunction(currentMergeBuffer));currentMergeBuffer=[];if(remainingEvents.length!==0)beginMerging();}
 beginMerging();while(remainingEvents.length){var currentEvent=remainingEvents[0];var distanceFromRightEdge=currentEvent.min-rightEdge;if(distanceFromRightEdge<mergeThreshold){rightEdge=Math.max(rightEdge,currentEvent.max);remainingEvents.splice(0,1);currentMergeBuffer.push(currentEvent);continue;}
 flushCurrentMergeBuffer();}
 flushCurrentMergeBuffer();return mergedEvents;}
-function findEmptyRangesBetweenRanges(inRanges,opt_totalRange){if(opt_totalRange&&opt_totalRange.isEmpty)
-opt_totalRange=undefined;var emptyRanges=[];if(!inRanges.length){if(opt_totalRange)
-emptyRanges.push(opt_totalRange);return emptyRanges;}
-inRanges=inRanges.slice();inRanges.sort(function(x,y){return x.min-y.min;});if(opt_totalRange&&(opt_totalRange.min<inRanges[0].min)){emptyRanges.push(tr.b.Range.fromExplicitRange(opt_totalRange.min,inRanges[0].min));}
-inRanges.forEach(function(range,index){for(var otherIndex=0;otherIndex<inRanges.length;++otherIndex){if(index===otherIndex)
-continue;var other=inRanges[otherIndex];if(other.min>range.max){emptyRanges.push(tr.b.Range.fromExplicitRange(range.max,other.min));return;}
+function findEmptyRangesBetweenRanges(inRanges,opt_totalRange){if(opt_totalRange&&opt_totalRange.isEmpty)opt_totalRange=undefined;var emptyRanges=[];if(!inRanges.length){if(opt_totalRange)emptyRanges.push(opt_totalRange);return emptyRanges;}
+inRanges=inRanges.slice();inRanges.sort(function(x,y){return x.min-y.min;});if(opt_totalRange&&(opt_totalRange.min<inRanges[0].min)){emptyRanges.push(tr.b.math.Range.fromExplicitRange(opt_totalRange.min,inRanges[0].min));}
+inRanges.forEach(function(range,index){for(var otherIndex=0;otherIndex<inRanges.length;++otherIndex){if(index===otherIndex)continue;var other=inRanges[otherIndex];if(other.min>range.max){emptyRanges.push(tr.b.math.Range.fromExplicitRange(range.max,other.min));return;}
 if(other.max>range.max){return;}}
-if(opt_totalRange&&(range.max<opt_totalRange.max)){emptyRanges.push(tr.b.Range.fromExplicitRange(range.max,opt_totalRange.max));}});return emptyRanges;}
-return{convertEventsToRanges:convertEventsToRanges,findEmptyRangesBetweenRanges:findEmptyRangesBetweenRanges,mergeRanges:mergeRanges};});!function(t,n){if("object"==typeof exports&&"object"==typeof module)module.exports=n();else if("function"==typeof define&&define.amd)define(n);else{var r=n();for(var a in r)("object"==typeof exports?exports:t)[a]=r[a]}}(this,function(){return function(t){function n(a){if(r[a])return r[a].exports;var e=r[a]={exports:{},id:a,loaded:!1};return t[a].call(e.exports,e,e.exports,n),e.loaded=!0,e.exports}var r={};return n.m=t,n.c=r,n.p="",n(0)}([function(t,n,r){n.glMatrix=r(1),n.mat2=r(2),n.mat2d=r(3),n.mat3=r(4),n.mat4=r(5),n.quat=r(6),n.vec2=r(9),n.vec3=r(7),n.vec4=r(8)},function(t,n,r){var a={};a.EPSILON=1e-6,a.ARRAY_TYPE="undefined"!=typeof Float32Array?Float32Array:Array,a.RANDOM=Math.random,a.setMatrixArrayType=function(t){GLMAT_ARRAY_TYPE=t};var e=Math.PI/180;a.toRadian=function(t){return t*e},t.exports=a},function(t,n,r){var a=r(1),e={};e.create=function(){var t=new a.ARRAY_TYPE(4);return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t},e.clone=function(t){var n=new a.ARRAY_TYPE(4);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n},e.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t},e.identity=function(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t},e.transpose=function(t,n){if(t===n){var r=n[1];t[1]=n[2],t[2]=r}else t[0]=n[0],t[1]=n[2],t[2]=n[1],t[3]=n[3];return t},e.invert=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=r*u-e*a;return o?(o=1/o,t[0]=u*o,t[1]=-a*o,t[2]=-e*o,t[3]=r*o,t):null},e.adjoint=function(t,n){var r=n[0];return t[0]=n[3],t[1]=-n[1],t[2]=-n[2],t[3]=r,t},e.determinant=function(t){return t[0]*t[3]-t[2]*t[1]},e.multiply=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=r[0],c=r[1],f=r[2],s=r[3];return t[0]=a*i+u*c,t[1]=e*i+o*c,t[2]=a*f+u*s,t[3]=e*f+o*s,t},e.mul=e.multiply,e.rotate=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=Math.sin(r),c=Math.cos(r);return t[0]=a*c+u*i,t[1]=e*c+o*i,t[2]=a*-i+u*c,t[3]=e*-i+o*c,t},e.scale=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=r[0],c=r[1];return t[0]=a*i,t[1]=e*i,t[2]=u*c,t[3]=o*c,t},e.fromRotation=function(t,n){var r=Math.sin(n),a=Math.cos(n);return t[0]=a,t[1]=r,t[2]=-r,t[3]=a,t},e.fromScaling=function(t,n){return t[0]=n[0],t[1]=0,t[2]=0,t[3]=n[1],t},e.str=function(t){return"mat2("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+")"},e.frob=function(t){return Math.sqrt(Math.pow(t[0],2)+Math.pow(t[1],2)+Math.pow(t[2],2)+Math.pow(t[3],2))},e.LDU=function(t,n,r,a){return t[2]=a[2]/a[0],r[0]=a[0],r[1]=a[1],r[3]=a[3]-t[2]*r[1],[t,n,r]},t.exports=e},function(t,n,r){var a=r(1),e={};e.create=function(){var t=new a.ARRAY_TYPE(6);return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t[4]=0,t[5]=0,t},e.clone=function(t){var n=new a.ARRAY_TYPE(6);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n[4]=t[4],n[5]=t[5],n},e.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[4]=n[4],t[5]=n[5],t},e.identity=function(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t[4]=0,t[5]=0,t},e.invert=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],c=r*u-a*e;return c?(c=1/c,t[0]=u*c,t[1]=-a*c,t[2]=-e*c,t[3]=r*c,t[4]=(e*i-u*o)*c,t[5]=(a*o-r*i)*c,t):null},e.determinant=function(t){return t[0]*t[3]-t[1]*t[2]},e.multiply=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],c=n[5],f=r[0],s=r[1],h=r[2],M=r[3],l=r[4],v=r[5];return t[0]=a*f+u*s,t[1]=e*f+o*s,t[2]=a*h+u*M,t[3]=e*h+o*M,t[4]=a*l+u*v+i,t[5]=e*l+o*v+c,t},e.mul=e.multiply,e.rotate=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],c=n[5],f=Math.sin(r),s=Math.cos(r);return t[0]=a*s+u*f,t[1]=e*s+o*f,t[2]=a*-f+u*s,t[3]=e*-f+o*s,t[4]=i,t[5]=c,t},e.scale=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],c=n[5],f=r[0],s=r[1];return t[0]=a*f,t[1]=e*f,t[2]=u*s,t[3]=o*s,t[4]=i,t[5]=c,t},e.translate=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],c=n[5],f=r[0],s=r[1];return t[0]=a,t[1]=e,t[2]=u,t[3]=o,t[4]=a*f+u*s+i,t[5]=e*f+o*s+c,t},e.fromRotation=function(t,n){var r=Math.sin(n),a=Math.cos(n);return t[0]=a,t[1]=r,t[2]=-r,t[3]=a,t[4]=0,t[5]=0,t},e.fromScaling=function(t,n){return t[0]=n[0],t[1]=0,t[2]=0,t[3]=n[1],t[4]=0,t[5]=0,t},e.fromTranslation=function(t,n){return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t[4]=n[0],t[5]=n[1],t},e.str=function(t){return"mat2d("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+", "+t[4]+", "+t[5]+")"},e.frob=function(t){return Math.sqrt(Math.pow(t[0],2)+Math.pow(t[1],2)+Math.pow(t[2],2)+Math.pow(t[3],2)+Math.pow(t[4],2)+Math.pow(t[5],2)+1)},t.exports=e},function(t,n,r){var a=r(1),e={};e.create=function(){var t=new a.ARRAY_TYPE(9);return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=1,t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},e.fromMat4=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[4],t[4]=n[5],t[5]=n[6],t[6]=n[8],t[7]=n[9],t[8]=n[10],t},e.clone=function(t){var n=new a.ARRAY_TYPE(9);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n[4]=t[4],n[5]=t[5],n[6]=t[6],n[7]=t[7],n[8]=t[8],n},e.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[4]=n[4],t[5]=n[5],t[6]=n[6],t[7]=n[7],t[8]=n[8],t},e.identity=function(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=1,t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},e.transpose=function(t,n){if(t===n){var r=n[1],a=n[2],e=n[5];t[1]=n[3],t[2]=n[6],t[3]=r,t[5]=n[7],t[6]=a,t[7]=e}else t[0]=n[0],t[1]=n[3],t[2]=n[6],t[3]=n[1],t[4]=n[4],t[5]=n[7],t[6]=n[2],t[7]=n[5],t[8]=n[8];return t},e.invert=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],c=n[6],f=n[7],s=n[8],h=s*o-i*f,M=-s*u+i*c,l=f*u-o*c,v=r*h+a*M+e*l;return v?(v=1/v,t[0]=h*v,t[1]=(-s*a+e*f)*v,t[2]=(i*a-e*o)*v,t[3]=M*v,t[4]=(s*r-e*c)*v,t[5]=(-i*r+e*u)*v,t[6]=l*v,t[7]=(-f*r+a*c)*v,t[8]=(o*r-a*u)*v,t):null},e.adjoint=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],c=n[6],f=n[7],s=n[8];return t[0]=o*s-i*f,t[1]=e*f-a*s,t[2]=a*i-e*o,t[3]=i*c-u*s,t[4]=r*s-e*c,t[5]=e*u-r*i,t[6]=u*f-o*c,t[7]=a*c-r*f,t[8]=r*o-a*u,t},e.determinant=function(t){var n=t[0],r=t[1],a=t[2],e=t[3],u=t[4],o=t[5],i=t[6],c=t[7],f=t[8];return n*(f*u-o*c)+r*(-f*e+o*i)+a*(c*e-u*i)},e.multiply=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],c=n[5],f=n[6],s=n[7],h=n[8],M=r[0],l=r[1],v=r[2],m=r[3],p=r[4],d=r[5],A=r[6],R=r[7],w=r[8];return t[0]=M*a+l*o+v*f,t[1]=M*e+l*i+v*s,t[2]=M*u+l*c+v*h,t[3]=m*a+p*o+d*f,t[4]=m*e+p*i+d*s,t[5]=m*u+p*c+d*h,t[6]=A*a+R*o+w*f,t[7]=A*e+R*i+w*s,t[8]=A*u+R*c+w*h,t},e.mul=e.multiply,e.translate=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],c=n[5],f=n[6],s=n[7],h=n[8],M=r[0],l=r[1];return t[0]=a,t[1]=e,t[2]=u,t[3]=o,t[4]=i,t[5]=c,t[6]=M*a+l*o+f,t[7]=M*e+l*i+s,t[8]=M*u+l*c+h,t},e.rotate=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],c=n[5],f=n[6],s=n[7],h=n[8],M=Math.sin(r),l=Math.cos(r);return t[0]=l*a+M*o,t[1]=l*e+M*i,t[2]=l*u+M*c,t[3]=l*o-M*a,t[4]=l*i-M*e,t[5]=l*c-M*u,t[6]=f,t[7]=s,t[8]=h,t},e.scale=function(t,n,r){var a=r[0],e=r[1];return t[0]=a*n[0],t[1]=a*n[1],t[2]=a*n[2],t[3]=e*n[3],t[4]=e*n[4],t[5]=e*n[5],t[6]=n[6],t[7]=n[7],t[8]=n[8],t},e.fromTranslation=function(t,n){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=1,t[5]=0,t[6]=n[0],t[7]=n[1],t[8]=1,t},e.fromRotation=function(t,n){var r=Math.sin(n),a=Math.cos(n);return t[0]=a,t[1]=r,t[2]=0,t[3]=-r,t[4]=a,t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},e.fromScaling=function(t,n){return t[0]=n[0],t[1]=0,t[2]=0,t[3]=0,t[4]=n[1],t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},e.fromMat2d=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=0,t[3]=n[2],t[4]=n[3],t[5]=0,t[6]=n[4],t[7]=n[5],t[8]=1,t},e.fromQuat=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=r+r,i=a+a,c=e+e,f=r*o,s=a*o,h=a*i,M=e*o,l=e*i,v=e*c,m=u*o,p=u*i,d=u*c;return t[0]=1-h-v,t[3]=s-d,t[6]=M+p,t[1]=s+d,t[4]=1-f-v,t[7]=l-m,t[2]=M-p,t[5]=l+m,t[8]=1-f-h,t},e.normalFromMat4=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],c=n[6],f=n[7],s=n[8],h=n[9],M=n[10],l=n[11],v=n[12],m=n[13],p=n[14],d=n[15],A=r*i-a*o,R=r*c-e*o,w=r*f-u*o,q=a*c-e*i,Y=a*f-u*i,g=e*f-u*c,y=s*m-h*v,x=s*p-M*v,P=s*d-l*v,E=h*p-M*m,T=h*d-l*m,b=M*d-l*p,D=A*b-R*T+w*E+q*P-Y*x+g*y;return D?(D=1/D,t[0]=(i*b-c*T+f*E)*D,t[1]=(c*P-o*b-f*x)*D,t[2]=(o*T-i*P+f*y)*D,t[3]=(e*T-a*b-u*E)*D,t[4]=(r*b-e*P+u*x)*D,t[5]=(a*P-r*T-u*y)*D,t[6]=(m*g-p*Y+d*q)*D,t[7]=(p*w-v*g-d*R)*D,t[8]=(v*Y-m*w+d*A)*D,t):null},e.str=function(t){return"mat3("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+", "+t[4]+", "+t[5]+", "+t[6]+", "+t[7]+", "+t[8]+")"},e.frob=function(t){return Math.sqrt(Math.pow(t[0],2)+Math.pow(t[1],2)+Math.pow(t[2],2)+Math.pow(t[3],2)+Math.pow(t[4],2)+Math.pow(t[5],2)+Math.pow(t[6],2)+Math.pow(t[7],2)+Math.pow(t[8],2))},t.exports=e},function(t,n,r){var a=r(1),e={};e.create=function(){var t=new a.ARRAY_TYPE(16);return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=1,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=1,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},e.clone=function(t){var n=new a.ARRAY_TYPE(16);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n[4]=t[4],n[5]=t[5],n[6]=t[6],n[7]=t[7],n[8]=t[8],n[9]=t[9],n[10]=t[10],n[11]=t[11],n[12]=t[12],n[13]=t[13],n[14]=t[14],n[15]=t[15],n},e.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[4]=n[4],t[5]=n[5],t[6]=n[6],t[7]=n[7],t[8]=n[8],t[9]=n[9],t[10]=n[10],t[11]=n[11],t[12]=n[12],t[13]=n[13],t[14]=n[14],t[15]=n[15],t},e.identity=function(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=1,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=1,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},e.transpose=function(t,n){if(t===n){var r=n[1],a=n[2],e=n[3],u=n[6],o=n[7],i=n[11];t[1]=n[4],t[2]=n[8],t[3]=n[12],t[4]=r,t[6]=n[9],t[7]=n[13],t[8]=a,t[9]=u,t[11]=n[14],t[12]=e,t[13]=o,t[14]=i}else t[0]=n[0],t[1]=n[4],t[2]=n[8],t[3]=n[12],t[4]=n[1],t[5]=n[5],t[6]=n[9],t[7]=n[13],t[8]=n[2],t[9]=n[6],t[10]=n[10],t[11]=n[14],t[12]=n[3],t[13]=n[7],t[14]=n[11],t[15]=n[15];return t},e.invert=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],c=n[6],f=n[7],s=n[8],h=n[9],M=n[10],l=n[11],v=n[12],m=n[13],p=n[14],d=n[15],A=r*i-a*o,R=r*c-e*o,w=r*f-u*o,q=a*c-e*i,Y=a*f-u*i,g=e*f-u*c,y=s*m-h*v,x=s*p-M*v,P=s*d-l*v,E=h*p-M*m,T=h*d-l*m,b=M*d-l*p,D=A*b-R*T+w*E+q*P-Y*x+g*y;return D?(D=1/D,t[0]=(i*b-c*T+f*E)*D,t[1]=(e*T-a*b-u*E)*D,t[2]=(m*g-p*Y+d*q)*D,t[3]=(M*Y-h*g-l*q)*D,t[4]=(c*P-o*b-f*x)*D,t[5]=(r*b-e*P+u*x)*D,t[6]=(p*w-v*g-d*R)*D,t[7]=(s*g-M*w+l*R)*D,t[8]=(o*T-i*P+f*y)*D,t[9]=(a*P-r*T-u*y)*D,t[10]=(v*Y-m*w+d*A)*D,t[11]=(h*w-s*Y-l*A)*D,t[12]=(i*x-o*E-c*y)*D,t[13]=(r*E-a*x+e*y)*D,t[14]=(m*R-v*q-p*A)*D,t[15]=(s*q-h*R+M*A)*D,t):null},e.adjoint=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],c=n[6],f=n[7],s=n[8],h=n[9],M=n[10],l=n[11],v=n[12],m=n[13],p=n[14],d=n[15];return t[0]=i*(M*d-l*p)-h*(c*d-f*p)+m*(c*l-f*M),t[1]=-(a*(M*d-l*p)-h*(e*d-u*p)+m*(e*l-u*M)),t[2]=a*(c*d-f*p)-i*(e*d-u*p)+m*(e*f-u*c),t[3]=-(a*(c*l-f*M)-i*(e*l-u*M)+h*(e*f-u*c)),t[4]=-(o*(M*d-l*p)-s*(c*d-f*p)+v*(c*l-f*M)),t[5]=r*(M*d-l*p)-s*(e*d-u*p)+v*(e*l-u*M),t[6]=-(r*(c*d-f*p)-o*(e*d-u*p)+v*(e*f-u*c)),t[7]=r*(c*l-f*M)-o*(e*l-u*M)+s*(e*f-u*c),t[8]=o*(h*d-l*m)-s*(i*d-f*m)+v*(i*l-f*h),t[9]=-(r*(h*d-l*m)-s*(a*d-u*m)+v*(a*l-u*h)),t[10]=r*(i*d-f*m)-o*(a*d-u*m)+v*(a*f-u*i),t[11]=-(r*(i*l-f*h)-o*(a*l-u*h)+s*(a*f-u*i)),t[12]=-(o*(h*p-M*m)-s*(i*p-c*m)+v*(i*M-c*h)),t[13]=r*(h*p-M*m)-s*(a*p-e*m)+v*(a*M-e*h),t[14]=-(r*(i*p-c*m)-o*(a*p-e*m)+v*(a*c-e*i)),t[15]=r*(i*M-c*h)-o*(a*M-e*h)+s*(a*c-e*i),t},e.determinant=function(t){var n=t[0],r=t[1],a=t[2],e=t[3],u=t[4],o=t[5],i=t[6],c=t[7],f=t[8],s=t[9],h=t[10],M=t[11],l=t[12],v=t[13],m=t[14],p=t[15],d=n*o-r*u,A=n*i-a*u,R=n*c-e*u,w=r*i-a*o,q=r*c-e*o,Y=a*c-e*i,g=f*v-s*l,y=f*m-h*l,x=f*p-M*l,P=s*m-h*v,E=s*p-M*v,T=h*p-M*m;return d*T-A*E+R*P+w*x-q*y+Y*g},e.multiply=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],c=n[5],f=n[6],s=n[7],h=n[8],M=n[9],l=n[10],v=n[11],m=n[12],p=n[13],d=n[14],A=n[15],R=r[0],w=r[1],q=r[2],Y=r[3];return t[0]=R*a+w*i+q*h+Y*m,t[1]=R*e+w*c+q*M+Y*p,t[2]=R*u+w*f+q*l+Y*d,t[3]=R*o+w*s+q*v+Y*A,R=r[4],w=r[5],q=r[6],Y=r[7],t[4]=R*a+w*i+q*h+Y*m,t[5]=R*e+w*c+q*M+Y*p,t[6]=R*u+w*f+q*l+Y*d,t[7]=R*o+w*s+q*v+Y*A,R=r[8],w=r[9],q=r[10],Y=r[11],t[8]=R*a+w*i+q*h+Y*m,t[9]=R*e+w*c+q*M+Y*p,t[10]=R*u+w*f+q*l+Y*d,t[11]=R*o+w*s+q*v+Y*A,R=r[12],w=r[13],q=r[14],Y=r[15],t[12]=R*a+w*i+q*h+Y*m,t[13]=R*e+w*c+q*M+Y*p,t[14]=R*u+w*f+q*l+Y*d,t[15]=R*o+w*s+q*v+Y*A,t},e.mul=e.multiply,e.translate=function(t,n,r){var a,e,u,o,i,c,f,s,h,M,l,v,m=r[0],p=r[1],d=r[2];return n===t?(t[12]=n[0]*m+n[4]*p+n[8]*d+n[12],t[13]=n[1]*m+n[5]*p+n[9]*d+n[13],t[14]=n[2]*m+n[6]*p+n[10]*d+n[14],t[15]=n[3]*m+n[7]*p+n[11]*d+n[15]):(a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],c=n[5],f=n[6],s=n[7],h=n[8],M=n[9],l=n[10],v=n[11],t[0]=a,t[1]=e,t[2]=u,t[3]=o,t[4]=i,t[5]=c,t[6]=f,t[7]=s,t[8]=h,t[9]=M,t[10]=l,t[11]=v,t[12]=a*m+i*p+h*d+n[12],t[13]=e*m+c*p+M*d+n[13],t[14]=u*m+f*p+l*d+n[14],t[15]=o*m+s*p+v*d+n[15]),t},e.scale=function(t,n,r){var a=r[0],e=r[1],u=r[2];return t[0]=n[0]*a,t[1]=n[1]*a,t[2]=n[2]*a,t[3]=n[3]*a,t[4]=n[4]*e,t[5]=n[5]*e,t[6]=n[6]*e,t[7]=n[7]*e,t[8]=n[8]*u,t[9]=n[9]*u,t[10]=n[10]*u,t[11]=n[11]*u,t[12]=n[12],t[13]=n[13],t[14]=n[14],t[15]=n[15],t},e.rotate=function(t,n,r,e){var u,o,i,c,f,s,h,M,l,v,m,p,d,A,R,w,q,Y,g,y,x,P,E,T,b=e[0],D=e[1],L=e[2],_=Math.sqrt(b*b+D*D+L*L);return Math.abs(_)<a.EPSILON?null:(_=1/_,b*=_,D*=_,L*=_,u=Math.sin(r),o=Math.cos(r),i=1-o,c=n[0],f=n[1],s=n[2],h=n[3],M=n[4],l=n[5],v=n[6],m=n[7],p=n[8],d=n[9],A=n[10],R=n[11],w=b*b*i+o,q=D*b*i+L*u,Y=L*b*i-D*u,g=b*D*i-L*u,y=D*D*i+o,x=L*D*i+b*u,P=b*L*i+D*u,E=D*L*i-b*u,T=L*L*i+o,t[0]=c*w+M*q+p*Y,t[1]=f*w+l*q+d*Y,t[2]=s*w+v*q+A*Y,t[3]=h*w+m*q+R*Y,t[4]=c*g+M*y+p*x,t[5]=f*g+l*y+d*x,t[6]=s*g+v*y+A*x,t[7]=h*g+m*y+R*x,t[8]=c*P+M*E+p*T,t[9]=f*P+l*E+d*T,t[10]=s*P+v*E+A*T,t[11]=h*P+m*E+R*T,n!==t&&(t[12]=n[12],t[13]=n[13],t[14]=n[14],t[15]=n[15]),t)},e.rotateX=function(t,n,r){var a=Math.sin(r),e=Math.cos(r),u=n[4],o=n[5],i=n[6],c=n[7],f=n[8],s=n[9],h=n[10],M=n[11];return n!==t&&(t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[12]=n[12],t[13]=n[13],t[14]=n[14],t[15]=n[15]),t[4]=u*e+f*a,t[5]=o*e+s*a,t[6]=i*e+h*a,t[7]=c*e+M*a,t[8]=f*e-u*a,t[9]=s*e-o*a,t[10]=h*e-i*a,t[11]=M*e-c*a,t},e.rotateY=function(t,n,r){var a=Math.sin(r),e=Math.cos(r),u=n[0],o=n[1],i=n[2],c=n[3],f=n[8],s=n[9],h=n[10],M=n[11];return n!==t&&(t[4]=n[4],t[5]=n[5],t[6]=n[6],t[7]=n[7],t[12]=n[12],t[13]=n[13],t[14]=n[14],t[15]=n[15]),t[0]=u*e-f*a,t[1]=o*e-s*a,t[2]=i*e-h*a,t[3]=c*e-M*a,t[8]=u*a+f*e,t[9]=o*a+s*e,t[10]=i*a+h*e,t[11]=c*a+M*e,t},e.rotateZ=function(t,n,r){var a=Math.sin(r),e=Math.cos(r),u=n[0],o=n[1],i=n[2],c=n[3],f=n[4],s=n[5],h=n[6],M=n[7];return n!==t&&(t[8]=n[8],t[9]=n[9],t[10]=n[10],t[11]=n[11],t[12]=n[12],t[13]=n[13],t[14]=n[14],t[15]=n[15]),t[0]=u*e+f*a,t[1]=o*e+s*a,t[2]=i*e+h*a,t[3]=c*e+M*a,t[4]=f*e-u*a,t[5]=s*e-o*a,t[6]=h*e-i*a,t[7]=M*e-c*a,t},e.fromTranslation=function(t,n){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=1,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=1,t[11]=0,t[12]=n[0],t[13]=n[1],t[14]=n[2],t[15]=1,t},e.fromScaling=function(t,n){return t[0]=n[0],t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=n[1],t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=n[2],t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},e.fromRotation=function(t,n,r){var e,u,o,i=r[0],c=r[1],f=r[2],s=Math.sqrt(i*i+c*c+f*f);return Math.abs(s)<a.EPSILON?null:(s=1/s,i*=s,c*=s,f*=s,e=Math.sin(n),u=Math.cos(n),o=1-u,t[0]=i*i*o+u,t[1]=c*i*o+f*e,t[2]=f*i*o-c*e,t[3]=0,t[4]=i*c*o-f*e,t[5]=c*c*o+u,t[6]=f*c*o+i*e,t[7]=0,t[8]=i*f*o+c*e,t[9]=c*f*o-i*e,t[10]=f*f*o+u,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t)},e.fromXRotation=function(t,n){var r=Math.sin(n),a=Math.cos(n);return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=a,t[6]=r,t[7]=0,t[8]=0,t[9]=-r,t[10]=a,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},e.fromYRotation=function(t,n){var r=Math.sin(n),a=Math.cos(n);return t[0]=a,t[1]=0,t[2]=-r,t[3]=0,t[4]=0,t[5]=1,t[6]=0,t[7]=0,t[8]=r,t[9]=0,t[10]=a,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},e.fromZRotation=function(t,n){var r=Math.sin(n),a=Math.cos(n);return t[0]=a,t[1]=r,t[2]=0,t[3]=0,t[4]=-r,t[5]=a,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=1,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},e.fromRotationTranslation=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=a+a,c=e+e,f=u+u,s=a*i,h=a*c,M=a*f,l=e*c,v=e*f,m=u*f,p=o*i,d=o*c,A=o*f;return t[0]=1-(l+m),t[1]=h+A,t[2]=M-d,t[3]=0,t[4]=h-A,t[5]=1-(s+m),t[6]=v+p,t[7]=0,t[8]=M+d,t[9]=v-p,t[10]=1-(s+l),t[11]=0,t[12]=r[0],t[13]=r[1],t[14]=r[2],t[15]=1,t},e.fromRotationTranslationScale=function(t,n,r,a){var e=n[0],u=n[1],o=n[2],i=n[3],c=e+e,f=u+u,s=o+o,h=e*c,M=e*f,l=e*s,v=u*f,m=u*s,p=o*s,d=i*c,A=i*f,R=i*s,w=a[0],q=a[1],Y=a[2];return t[0]=(1-(v+p))*w,t[1]=(M+R)*w,t[2]=(l-A)*w,t[3]=0,t[4]=(M-R)*q,t[5]=(1-(h+p))*q,t[6]=(m+d)*q,t[7]=0,t[8]=(l+A)*Y,t[9]=(m-d)*Y,t[10]=(1-(h+v))*Y,t[11]=0,t[12]=r[0],t[13]=r[1],t[14]=r[2],t[15]=1,t},e.fromRotationTranslationScaleOrigin=function(t,n,r,a,e){var u=n[0],o=n[1],i=n[2],c=n[3],f=u+u,s=o+o,h=i+i,M=u*f,l=u*s,v=u*h,m=o*s,p=o*h,d=i*h,A=c*f,R=c*s,w=c*h,q=a[0],Y=a[1],g=a[2],y=e[0],x=e[1],P=e[2];return t[0]=(1-(m+d))*q,t[1]=(l+w)*q,t[2]=(v-R)*q,t[3]=0,t[4]=(l-w)*Y,t[5]=(1-(M+d))*Y,t[6]=(p+A)*Y,t[7]=0,t[8]=(v+R)*g,t[9]=(p-A)*g,t[10]=(1-(M+m))*g,t[11]=0,t[12]=r[0]+y-(t[0]*y+t[4]*x+t[8]*P),t[13]=r[1]+x-(t[1]*y+t[5]*x+t[9]*P),t[14]=r[2]+P-(t[2]*y+t[6]*x+t[10]*P),t[15]=1,t},e.fromQuat=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=r+r,i=a+a,c=e+e,f=r*o,s=a*o,h=a*i,M=e*o,l=e*i,v=e*c,m=u*o,p=u*i,d=u*c;return t[0]=1-h-v,t[1]=s+d,t[2]=M-p,t[3]=0,t[4]=s-d,t[5]=1-f-v,t[6]=l+m,t[7]=0,t[8]=M+p,t[9]=l-m,t[10]=1-f-h,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},e.frustum=function(t,n,r,a,e,u,o){var i=1/(r-n),c=1/(e-a),f=1/(u-o);return t[0]=2*u*i,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=2*u*c,t[6]=0,t[7]=0,t[8]=(r+n)*i,t[9]=(e+a)*c,t[10]=(o+u)*f,t[11]=-1,t[12]=0,t[13]=0,t[14]=o*u*2*f,t[15]=0,t},e.perspective=function(t,n,r,a,e){var u=1/Math.tan(n/2),o=1/(a-e);return t[0]=u/r,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=u,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=(e+a)*o,t[11]=-1,t[12]=0,t[13]=0,t[14]=2*e*a*o,t[15]=0,t},e.perspectiveFromFieldOfView=function(t,n,r,a){var e=Math.tan(n.upDegrees*Math.PI/180),u=Math.tan(n.downDegrees*Math.PI/180),o=Math.tan(n.leftDegrees*Math.PI/180),i=Math.tan(n.rightDegrees*Math.PI/180),c=2/(o+i),f=2/(e+u);return t[0]=c,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=f,t[6]=0,t[7]=0,t[8]=-((o-i)*c*.5),t[9]=(e-u)*f*.5,t[10]=a/(r-a),t[11]=-1,t[12]=0,t[13]=0,t[14]=a*r/(r-a),t[15]=0,t},e.ortho=function(t,n,r,a,e,u,o){var i=1/(n-r),c=1/(a-e),f=1/(u-o);return t[0]=-2*i,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=-2*c,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=2*f,t[11]=0,t[12]=(n+r)*i,t[13]=(e+a)*c,t[14]=(o+u)*f,t[15]=1,t},e.lookAt=function(t,n,r,u){var o,i,c,f,s,h,M,l,v,m,p=n[0],d=n[1],A=n[2],R=u[0],w=u[1],q=u[2],Y=r[0],g=r[1],y=r[2];return Math.abs(p-Y)<a.EPSILON&&Math.abs(d-g)<a.EPSILON&&Math.abs(A-y)<a.EPSILON?e.identity(t):(M=p-Y,l=d-g,v=A-y,m=1/Math.sqrt(M*M+l*l+v*v),M*=m,l*=m,v*=m,o=w*v-q*l,i=q*M-R*v,c=R*l-w*M,m=Math.sqrt(o*o+i*i+c*c),m?(m=1/m,o*=m,i*=m,c*=m):(o=0,i=0,c=0),f=l*c-v*i,s=v*o-M*c,h=M*i-l*o,m=Math.sqrt(f*f+s*s+h*h),m?(m=1/m,f*=m,s*=m,h*=m):(f=0,s=0,h=0),t[0]=o,t[1]=f,t[2]=M,t[3]=0,t[4]=i,t[5]=s,t[6]=l,t[7]=0,t[8]=c,t[9]=h,t[10]=v,t[11]=0,t[12]=-(o*p+i*d+c*A),t[13]=-(f*p+s*d+h*A),t[14]=-(M*p+l*d+v*A),t[15]=1,t)},e.str=function(t){return"mat4("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+", "+t[4]+", "+t[5]+", "+t[6]+", "+t[7]+", "+t[8]+", "+t[9]+", "+t[10]+", "+t[11]+", "+t[12]+", "+t[13]+", "+t[14]+", "+t[15]+")"},e.frob=function(t){return Math.sqrt(Math.pow(t[0],2)+Math.pow(t[1],2)+Math.pow(t[2],2)+Math.pow(t[3],2)+Math.pow(t[4],2)+Math.pow(t[5],2)+Math.pow(t[6],2)+Math.pow(t[7],2)+Math.pow(t[8],2)+Math.pow(t[9],2)+Math.pow(t[10],2)+Math.pow(t[11],2)+Math.pow(t[12],2)+Math.pow(t[13],2)+Math.pow(t[14],2)+Math.pow(t[15],2))},t.exports=e},function(t,n,r){var a=r(1),e=r(4),u=r(7),o=r(8),i={};i.create=function(){var t=new a.ARRAY_TYPE(4);return t[0]=0,t[1]=0,t[2]=0,t[3]=1,t},i.rotationTo=function(){var t=u.create(),n=u.fromValues(1,0,0),r=u.fromValues(0,1,0);return function(a,e,o){var c=u.dot(e,o);return-.999999>c?(u.cross(t,n,e),u.length(t)<1e-6&&u.cross(t,r,e),u.normalize(t,t),i.setAxisAngle(a,t,Math.PI),a):c>.999999?(a[0]=0,a[1]=0,a[2]=0,a[3]=1,a):(u.cross(t,e,o),a[0]=t[0],a[1]=t[1],a[2]=t[2],a[3]=1+c,i.normalize(a,a))}}(),i.setAxes=function(){var t=e.create();return function(n,r,a,e){return t[0]=a[0],t[3]=a[1],t[6]=a[2],t[1]=e[0],t[4]=e[1],t[7]=e[2],t[2]=-r[0],t[5]=-r[1],t[8]=-r[2],i.normalize(n,i.fromMat3(n,t))}}(),i.clone=o.clone,i.fromValues=o.fromValues,i.copy=o.copy,i.set=o.set,i.identity=function(t){return t[0]=0,t[1]=0,t[2]=0,t[3]=1,t},i.setAxisAngle=function(t,n,r){r=.5*r;var a=Math.sin(r);return t[0]=a*n[0],t[1]=a*n[1],t[2]=a*n[2],t[3]=Math.cos(r),t},i.add=o.add,i.multiply=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=r[0],c=r[1],f=r[2],s=r[3];return t[0]=a*s+o*i+e*f-u*c,t[1]=e*s+o*c+u*i-a*f,t[2]=u*s+o*f+a*c-e*i,t[3]=o*s-a*i-e*c-u*f,t},i.mul=i.multiply,i.scale=o.scale,i.rotateX=function(t,n,r){r*=.5;var a=n[0],e=n[1],u=n[2],o=n[3],i=Math.sin(r),c=Math.cos(r);return t[0]=a*c+o*i,t[1]=e*c+u*i,t[2]=u*c-e*i,t[3]=o*c-a*i,t},i.rotateY=function(t,n,r){r*=.5;var a=n[0],e=n[1],u=n[2],o=n[3],i=Math.sin(r),c=Math.cos(r);return t[0]=a*c-u*i,t[1]=e*c+o*i,t[2]=u*c+a*i,t[3]=o*c-e*i,t},i.rotateZ=function(t,n,r){r*=.5;var a=n[0],e=n[1],u=n[2],o=n[3],i=Math.sin(r),c=Math.cos(r);return t[0]=a*c+e*i,t[1]=e*c-a*i,t[2]=u*c+o*i,t[3]=o*c-u*i,t},i.calculateW=function(t,n){var r=n[0],a=n[1],e=n[2];return t[0]=r,t[1]=a,t[2]=e,t[3]=Math.sqrt(Math.abs(1-r*r-a*a-e*e)),t},i.dot=o.dot,i.lerp=o.lerp,i.slerp=function(t,n,r,a){var e,u,o,i,c,f=n[0],s=n[1],h=n[2],M=n[3],l=r[0],v=r[1],m=r[2],p=r[3];return u=f*l+s*v+h*m+M*p,0>u&&(u=-u,l=-l,v=-v,m=-m,p=-p),1-u>1e-6?(e=Math.acos(u),o=Math.sin(e),i=Math.sin((1-a)*e)/o,c=Math.sin(a*e)/o):(i=1-a,c=a),t[0]=i*f+c*l,t[1]=i*s+c*v,t[2]=i*h+c*m,t[3]=i*M+c*p,t},i.sqlerp=function(){var t=i.create(),n=i.create();return function(r,a,e,u,o,c){return i.slerp(t,a,o,c),i.slerp(n,e,u,c),i.slerp(r,t,n,2*c*(1-c)),r}}(),i.invert=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=r*r+a*a+e*e+u*u,i=o?1/o:0;return t[0]=-r*i,t[1]=-a*i,t[2]=-e*i,t[3]=u*i,t},i.conjugate=function(t,n){return t[0]=-n[0],t[1]=-n[1],t[2]=-n[2],t[3]=n[3],t},i.length=o.length,i.len=i.length,i.squaredLength=o.squaredLength,i.sqrLen=i.squaredLength,i.normalize=o.normalize,i.fromMat3=function(t,n){var r,a=n[0]+n[4]+n[8];if(a>0)r=Math.sqrt(a+1),t[3]=.5*r,r=.5/r,t[0]=(n[5]-n[7])*r,t[1]=(n[6]-n[2])*r,t[2]=(n[1]-n[3])*r;else{var e=0;n[4]>n[0]&&(e=1),n[8]>n[3*e+e]&&(e=2);var u=(e+1)%3,o=(e+2)%3;r=Math.sqrt(n[3*e+e]-n[3*u+u]-n[3*o+o]+1),t[e]=.5*r,r=.5/r,t[3]=(n[3*u+o]-n[3*o+u])*r,t[u]=(n[3*u+e]+n[3*e+u])*r,t[o]=(n[3*o+e]+n[3*e+o])*r}return t},i.str=function(t){return"quat("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+")"},t.exports=i},function(t,n,r){var a=r(1),e={};e.create=function(){var t=new a.ARRAY_TYPE(3);return t[0]=0,t[1]=0,t[2]=0,t},e.clone=function(t){var n=new a.ARRAY_TYPE(3);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n},e.fromValues=function(t,n,r){var e=new a.ARRAY_TYPE(3);return e[0]=t,e[1]=n,e[2]=r,e},e.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t},e.set=function(t,n,r,a){return t[0]=n,t[1]=r,t[2]=a,t},e.add=function(t,n,r){return t[0]=n[0]+r[0],t[1]=n[1]+r[1],t[2]=n[2]+r[2],t},e.subtract=function(t,n,r){return t[0]=n[0]-r[0],t[1]=n[1]-r[1],t[2]=n[2]-r[2],t},e.sub=e.subtract,e.multiply=function(t,n,r){return t[0]=n[0]*r[0],t[1]=n[1]*r[1],t[2]=n[2]*r[2],t},e.mul=e.multiply,e.divide=function(t,n,r){return t[0]=n[0]/r[0],t[1]=n[1]/r[1],t[2]=n[2]/r[2],t},e.div=e.divide,e.min=function(t,n,r){return t[0]=Math.min(n[0],r[0]),t[1]=Math.min(n[1],r[1]),t[2]=Math.min(n[2],r[2]),t},e.max=function(t,n,r){return t[0]=Math.max(n[0],r[0]),t[1]=Math.max(n[1],r[1]),t[2]=Math.max(n[2],r[2]),t},e.scale=function(t,n,r){return t[0]=n[0]*r,t[1]=n[1]*r,t[2]=n[2]*r,t},e.scaleAndAdd=function(t,n,r,a){return t[0]=n[0]+r[0]*a,t[1]=n[1]+r[1]*a,t[2]=n[2]+r[2]*a,t},e.distance=function(t,n){var r=n[0]-t[0],a=n[1]-t[1],e=n[2]-t[2];return Math.sqrt(r*r+a*a+e*e)},e.dist=e.distance,e.squaredDistance=function(t,n){var r=n[0]-t[0],a=n[1]-t[1],e=n[2]-t[2];return r*r+a*a+e*e},e.sqrDist=e.squaredDistance,e.length=function(t){var n=t[0],r=t[1],a=t[2];return Math.sqrt(n*n+r*r+a*a)},e.len=e.length,e.squaredLength=function(t){var n=t[0],r=t[1],a=t[2];return n*n+r*r+a*a},e.sqrLen=e.squaredLength,e.negate=function(t,n){return t[0]=-n[0],t[1]=-n[1],t[2]=-n[2],t},e.inverse=function(t,n){return t[0]=1/n[0],t[1]=1/n[1],t[2]=1/n[2],t},e.normalize=function(t,n){var r=n[0],a=n[1],e=n[2],u=r*r+a*a+e*e;return u>0&&(u=1/Math.sqrt(u),t[0]=n[0]*u,t[1]=n[1]*u,t[2]=n[2]*u),t},e.dot=function(t,n){return t[0]*n[0]+t[1]*n[1]+t[2]*n[2]},e.cross=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=r[0],i=r[1],c=r[2];return t[0]=e*c-u*i,t[1]=u*o-a*c,t[2]=a*i-e*o,t},e.lerp=function(t,n,r,a){var e=n[0],u=n[1],o=n[2];return t[0]=e+a*(r[0]-e),t[1]=u+a*(r[1]-u),t[2]=o+a*(r[2]-o),t},e.hermite=function(t,n,r,a,e,u){var o=u*u,i=o*(2*u-3)+1,c=o*(u-2)+u,f=o*(u-1),s=o*(3-2*u);return t[0]=n[0]*i+r[0]*c+a[0]*f+e[0]*s,t[1]=n[1]*i+r[1]*c+a[1]*f+e[1]*s,t[2]=n[2]*i+r[2]*c+a[2]*f+e[2]*s,t},e.bezier=function(t,n,r,a,e,u){var o=1-u,i=o*o,c=u*u,f=i*o,s=3*u*i,h=3*c*o,M=c*u;return t[0]=n[0]*f+r[0]*s+a[0]*h+e[0]*M,t[1]=n[1]*f+r[1]*s+a[1]*h+e[1]*M,t[2]=n[2]*f+r[2]*s+a[2]*h+e[2]*M,t},e.random=function(t,n){n=n||1;var r=2*a.RANDOM()*Math.PI,e=2*a.RANDOM()-1,u=Math.sqrt(1-e*e)*n;return t[0]=Math.cos(r)*u,t[1]=Math.sin(r)*u,t[2]=e*n,t},e.transformMat4=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=r[3]*a+r[7]*e+r[11]*u+r[15];return o=o||1,t[0]=(r[0]*a+r[4]*e+r[8]*u+r[12])/o,t[1]=(r[1]*a+r[5]*e+r[9]*u+r[13])/o,t[2]=(r[2]*a+r[6]*e+r[10]*u+r[14])/o,t},e.transformMat3=function(t,n,r){var a=n[0],e=n[1],u=n[2];return t[0]=a*r[0]+e*r[3]+u*r[6],t[1]=a*r[1]+e*r[4]+u*r[7],t[2]=a*r[2]+e*r[5]+u*r[8],t},e.transformQuat=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=r[0],i=r[1],c=r[2],f=r[3],s=f*a+i*u-c*e,h=f*e+c*a-o*u,M=f*u+o*e-i*a,l=-o*a-i*e-c*u;return t[0]=s*f+l*-o+h*-c-M*-i,t[1]=h*f+l*-i+M*-o-s*-c,t[2]=M*f+l*-c+s*-i-h*-o,t},e.rotateX=function(t,n,r,a){var e=[],u=[];return e[0]=n[0]-r[0],e[1]=n[1]-r[1],e[2]=n[2]-r[2],u[0]=e[0],u[1]=e[1]*Math.cos(a)-e[2]*Math.sin(a),u[2]=e[1]*Math.sin(a)+e[2]*Math.cos(a),t[0]=u[0]+r[0],t[1]=u[1]+r[1],t[2]=u[2]+r[2],t},e.rotateY=function(t,n,r,a){var e=[],u=[];return e[0]=n[0]-r[0],e[1]=n[1]-r[1],e[2]=n[2]-r[2],u[0]=e[2]*Math.sin(a)+e[0]*Math.cos(a),u[1]=e[1],u[2]=e[2]*Math.cos(a)-e[0]*Math.sin(a),t[0]=u[0]+r[0],t[1]=u[1]+r[1],t[2]=u[2]+r[2],t},e.rotateZ=function(t,n,r,a){var e=[],u=[];return e[0]=n[0]-r[0],e[1]=n[1]-r[1],e[2]=n[2]-r[2],u[0]=e[0]*Math.cos(a)-e[1]*Math.sin(a),u[1]=e[0]*Math.sin(a)+e[1]*Math.cos(a),u[2]=e[2],t[0]=u[0]+r[0],t[1]=u[1]+r[1],t[2]=u[2]+r[2],t},e.forEach=function(){var t=e.create();return function(n,r,a,e,u,o){var i,c;for(r||(r=3),a||(a=0),c=e?Math.min(e*r+a,n.length):n.length,i=a;c>i;i+=r)t[0]=n[i],t[1]=n[i+1],t[2]=n[i+2],u(t,t,o),n[i]=t[0],n[i+1]=t[1],n[i+2]=t[2];return n}}(),e.angle=function(t,n){var r=e.fromValues(t[0],t[1],t[2]),a=e.fromValues(n[0],n[1],n[2]);e.normalize(r,r),e.normalize(a,a);var u=e.dot(r,a);return u>1?0:Math.acos(u)},e.str=function(t){return"vec3("+t[0]+", "+t[1]+", "+t[2]+")"},t.exports=e},function(t,n,r){var a=r(1),e={};e.create=function(){var t=new a.ARRAY_TYPE(4);return t[0]=0,t[1]=0,t[2]=0,t[3]=0,t},e.clone=function(t){var n=new a.ARRAY_TYPE(4);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n},e.fromValues=function(t,n,r,e){var u=new a.ARRAY_TYPE(4);return u[0]=t,u[1]=n,u[2]=r,u[3]=e,u},e.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t},e.set=function(t,n,r,a,e){return t[0]=n,t[1]=r,t[2]=a,t[3]=e,t},e.add=function(t,n,r){return t[0]=n[0]+r[0],t[1]=n[1]+r[1],t[2]=n[2]+r[2],t[3]=n[3]+r[3],t},e.subtract=function(t,n,r){return t[0]=n[0]-r[0],t[1]=n[1]-r[1],t[2]=n[2]-r[2],t[3]=n[3]-r[3],t},e.sub=e.subtract,e.multiply=function(t,n,r){return t[0]=n[0]*r[0],t[1]=n[1]*r[1],t[2]=n[2]*r[2],t[3]=n[3]*r[3],t},e.mul=e.multiply,e.divide=function(t,n,r){return t[0]=n[0]/r[0],t[1]=n[1]/r[1],t[2]=n[2]/r[2],t[3]=n[3]/r[3],t},e.div=e.divide,e.min=function(t,n,r){return t[0]=Math.min(n[0],r[0]),t[1]=Math.min(n[1],r[1]),t[2]=Math.min(n[2],r[2]),t[3]=Math.min(n[3],r[3]),t},e.max=function(t,n,r){return t[0]=Math.max(n[0],r[0]),t[1]=Math.max(n[1],r[1]),t[2]=Math.max(n[2],r[2]),t[3]=Math.max(n[3],r[3]),t},e.scale=function(t,n,r){return t[0]=n[0]*r,t[1]=n[1]*r,t[2]=n[2]*r,t[3]=n[3]*r,t},e.scaleAndAdd=function(t,n,r,a){return t[0]=n[0]+r[0]*a,t[1]=n[1]+r[1]*a,t[2]=n[2]+r[2]*a,t[3]=n[3]+r[3]*a,t},e.distance=function(t,n){var r=n[0]-t[0],a=n[1]-t[1],e=n[2]-t[2],u=n[3]-t[3];return Math.sqrt(r*r+a*a+e*e+u*u)},e.dist=e.distance,e.squaredDistance=function(t,n){var r=n[0]-t[0],a=n[1]-t[1],e=n[2]-t[2],u=n[3]-t[3];return r*r+a*a+e*e+u*u},e.sqrDist=e.squaredDistance,e.length=function(t){var n=t[0],r=t[1],a=t[2],e=t[3];return Math.sqrt(n*n+r*r+a*a+e*e)},e.len=e.length,e.squaredLength=function(t){var n=t[0],r=t[1],a=t[2],e=t[3];return n*n+r*r+a*a+e*e},e.sqrLen=e.squaredLength,e.negate=function(t,n){return t[0]=-n[0],t[1]=-n[1],t[2]=-n[2],t[3]=-n[3],t},e.inverse=function(t,n){return t[0]=1/n[0],t[1]=1/n[1],t[2]=1/n[2],t[3]=1/n[3],t},e.normalize=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=r*r+a*a+e*e+u*u;return o>0&&(o=1/Math.sqrt(o),t[0]=r*o,t[1]=a*o,t[2]=e*o,t[3]=u*o),t},e.dot=function(t,n){return t[0]*n[0]+t[1]*n[1]+t[2]*n[2]+t[3]*n[3]},e.lerp=function(t,n,r,a){var e=n[0],u=n[1],o=n[2],i=n[3];return t[0]=e+a*(r[0]-e),t[1]=u+a*(r[1]-u),t[2]=o+a*(r[2]-o),t[3]=i+a*(r[3]-i),t},e.random=function(t,n){return n=n||1,t[0]=a.RANDOM(),t[1]=a.RANDOM(),t[2]=a.RANDOM(),t[3]=a.RANDOM(),e.normalize(t,t),e.scale(t,t,n),t},e.transformMat4=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3];return t[0]=r[0]*a+r[4]*e+r[8]*u+r[12]*o,t[1]=r[1]*a+r[5]*e+r[9]*u+r[13]*o,t[2]=r[2]*a+r[6]*e+r[10]*u+r[14]*o,t[3]=r[3]*a+r[7]*e+r[11]*u+r[15]*o,t},e.transformQuat=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=r[0],i=r[1],c=r[2],f=r[3],s=f*a+i*u-c*e,h=f*e+c*a-o*u,M=f*u+o*e-i*a,l=-o*a-i*e-c*u;return t[0]=s*f+l*-o+h*-c-M*-i,t[1]=h*f+l*-i+M*-o-s*-c,t[2]=M*f+l*-c+s*-i-h*-o,t[3]=n[3],t},e.forEach=function(){var t=e.create();return function(n,r,a,e,u,o){var i,c;for(r||(r=4),a||(a=0),c=e?Math.min(e*r+a,n.length):n.length,i=a;c>i;i+=r)t[0]=n[i],t[1]=n[i+1],t[2]=n[i+2],t[3]=n[i+3],u(t,t,o),n[i]=t[0],n[i+1]=t[1],n[i+2]=t[2],n[i+3]=t[3];return n}}(),e.str=function(t){return"vec4("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+")"},t.exports=e},function(t,n,r){var a=r(1),e={};e.create=function(){var t=new a.ARRAY_TYPE(2);return t[0]=0,t[1]=0,t},e.clone=function(t){var n=new a.ARRAY_TYPE(2);return n[0]=t[0],n[1]=t[1],n},e.fromValues=function(t,n){var r=new a.ARRAY_TYPE(2);return r[0]=t,r[1]=n,r},e.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t},e.set=function(t,n,r){return t[0]=n,t[1]=r,t},e.add=function(t,n,r){return t[0]=n[0]+r[0],t[1]=n[1]+r[1],t},e.subtract=function(t,n,r){return t[0]=n[0]-r[0],t[1]=n[1]-r[1],t},e.sub=e.subtract,e.multiply=function(t,n,r){return t[0]=n[0]*r[0],t[1]=n[1]*r[1],t},e.mul=e.multiply,e.divide=function(t,n,r){return t[0]=n[0]/r[0],t[1]=n[1]/r[1],t},e.div=e.divide,e.min=function(t,n,r){return t[0]=Math.min(n[0],r[0]),t[1]=Math.min(n[1],r[1]),t},e.max=function(t,n,r){return t[0]=Math.max(n[0],r[0]),t[1]=Math.max(n[1],r[1]),t},e.scale=function(t,n,r){return t[0]=n[0]*r,t[1]=n[1]*r,t},e.scaleAndAdd=function(t,n,r,a){return t[0]=n[0]+r[0]*a,t[1]=n[1]+r[1]*a,t},e.distance=function(t,n){var r=n[0]-t[0],a=n[1]-t[1];return Math.sqrt(r*r+a*a)},e.dist=e.distance,e.squaredDistance=function(t,n){var r=n[0]-t[0],a=n[1]-t[1];return r*r+a*a},e.sqrDist=e.squaredDistance,e.length=function(t){var n=t[0],r=t[1];return Math.sqrt(n*n+r*r)},e.len=e.length,e.squaredLength=function(t){var n=t[0],r=t[1];return n*n+r*r},e.sqrLen=e.squaredLength,e.negate=function(t,n){return t[0]=-n[0],t[1]=-n[1],t},e.inverse=function(t,n){return t[0]=1/n[0],t[1]=1/n[1],t},e.normalize=function(t,n){var r=n[0],a=n[1],e=r*r+a*a;return e>0&&(e=1/Math.sqrt(e),t[0]=n[0]*e,t[1]=n[1]*e),t},e.dot=function(t,n){return t[0]*n[0]+t[1]*n[1]},e.cross=function(t,n,r){var a=n[0]*r[1]-n[1]*r[0];return t[0]=t[1]=0,t[2]=a,t},e.lerp=function(t,n,r,a){var e=n[0],u=n[1];return t[0]=e+a*(r[0]-e),t[1]=u+a*(r[1]-u),t},e.random=function(t,n){n=n||1;var r=2*a.RANDOM()*Math.PI;return t[0]=Math.cos(r)*n,t[1]=Math.sin(r)*n,t},e.transformMat2=function(t,n,r){var a=n[0],e=n[1];return t[0]=r[0]*a+r[2]*e,t[1]=r[1]*a+r[3]*e,t},e.transformMat2d=function(t,n,r){var a=n[0],e=n[1];return t[0]=r[0]*a+r[2]*e+r[4],t[1]=r[1]*a+r[3]*e+r[5],t},e.transformMat3=function(t,n,r){var a=n[0],e=n[1];return t[0]=r[0]*a+r[3]*e+r[6],t[1]=r[1]*a+r[4]*e+r[7],t},e.transformMat4=function(t,n,r){var a=n[0],e=n[1];return t[0]=r[0]*a+r[4]*e+r[12],t[1]=r[1]*a+r[5]*e+r[13],t},e.forEach=function(){var t=e.create();return function(n,r,a,e,u,o){var i,c;for(r||(r=2),a||(a=0),c=e?Math.min(e*r+a,n.length):n.length,i=a;c>i;i+=r)t[0]=n[i],t[1]=n[i+1],u(t,t,o),n[i]=t[0],n[i+1]=t[1];return n}}(),e.str=function(t){return"vec2("+t[0]+", "+t[1]+")"},t.exports=e}])});'use strict';(function(global){if(tr.isNode){var glMatrixAbsPath=HTMLImportsLoader.hrefToAbsolutePath('/gl-matrix-min.js');var glMatrixModule=require(glMatrixAbsPath);for(var exportName in glMatrixModule){global[exportName]=glMatrixModule[exportName];}}})(this);'use strict';tr.exportTo('tr.b',function(){function approximately(x,y,delta){if(delta===undefined)
-delta=1e-9;return Math.abs(x-y)<delta;}
+if(opt_totalRange&&(range.max<opt_totalRange.max)){emptyRanges.push(tr.b.math.Range.fromExplicitRange(range.max,opt_totalRange.max));}});return emptyRanges;}
+return{convertEventsToRanges,findEmptyRangesBetweenRanges,mergeRanges,};});!function(t,n){if("object"==typeof exports&&"object"==typeof module)module.exports=n();else if("function"==typeof define&&define.amd)define(n);else{var r=n();for(var a in r)("object"==typeof exports?exports:t)[a]=r[a]}}(this,function(){return function(t){function n(a){if(r[a])return r[a].exports;var e=r[a]={exports:{},id:a,loaded:!1};return t[a].call(e.exports,e,e.exports,n),e.loaded=!0,e.exports}var r={};return n.m=t,n.c=r,n.p="",n(0)}([function(t,n,r){n.glMatrix=r(1),n.mat2=r(2),n.mat2d=r(3),n.mat3=r(4),n.mat4=r(5),n.quat=r(6),n.vec2=r(9),n.vec3=r(7),n.vec4=r(8)},function(t,n,r){var a={};a.EPSILON=1e-6,a.ARRAY_TYPE="undefined"!=typeof Float32Array?Float32Array:Array,a.RANDOM=Math.random,a.setMatrixArrayType=function(t){GLMAT_ARRAY_TYPE=t};var e=Math.PI/180;a.toRadian=function(t){return t*e},t.exports=a},function(t,n,r){var a=r(1),e={};e.create=function(){var t=new a.ARRAY_TYPE(4);return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t},e.clone=function(t){var n=new a.ARRAY_TYPE(4);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n},e.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t},e.identity=function(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t},e.transpose=function(t,n){if(t===n){var r=n[1];t[1]=n[2],t[2]=r}else t[0]=n[0],t[1]=n[2],t[2]=n[1],t[3]=n[3];return t},e.invert=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=r*u-e*a;return o?(o=1/o,t[0]=u*o,t[1]=-a*o,t[2]=-e*o,t[3]=r*o,t):null},e.adjoint=function(t,n){var r=n[0];return t[0]=n[3],t[1]=-n[1],t[2]=-n[2],t[3]=r,t},e.determinant=function(t){return t[0]*t[3]-t[2]*t[1]},e.multiply=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=r[0],c=r[1],f=r[2],s=r[3];return t[0]=a*i+u*c,t[1]=e*i+o*c,t[2]=a*f+u*s,t[3]=e*f+o*s,t},e.mul=e.multiply,e.rotate=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=Math.sin(r),c=Math.cos(r);return t[0]=a*c+u*i,t[1]=e*c+o*i,t[2]=a*-i+u*c,t[3]=e*-i+o*c,t},e.scale=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=r[0],c=r[1];return t[0]=a*i,t[1]=e*i,t[2]=u*c,t[3]=o*c,t},e.fromRotation=function(t,n){var r=Math.sin(n),a=Math.cos(n);return t[0]=a,t[1]=r,t[2]=-r,t[3]=a,t},e.fromScaling=function(t,n){return t[0]=n[0],t[1]=0,t[2]=0,t[3]=n[1],t},e.str=function(t){return"mat2("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+")"},e.frob=function(t){return Math.sqrt(Math.pow(t[0],2)+Math.pow(t[1],2)+Math.pow(t[2],2)+Math.pow(t[3],2))},e.LDU=function(t,n,r,a){return t[2]=a[2]/a[0],r[0]=a[0],r[1]=a[1],r[3]=a[3]-t[2]*r[1],[t,n,r]},t.exports=e},function(t,n,r){var a=r(1),e={};e.create=function(){var t=new a.ARRAY_TYPE(6);return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t[4]=0,t[5]=0,t},e.clone=function(t){var n=new a.ARRAY_TYPE(6);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n[4]=t[4],n[5]=t[5],n},e.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[4]=n[4],t[5]=n[5],t},e.identity=function(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t[4]=0,t[5]=0,t},e.invert=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],c=r*u-a*e;return c?(c=1/c,t[0]=u*c,t[1]=-a*c,t[2]=-e*c,t[3]=r*c,t[4]=(e*i-u*o)*c,t[5]=(a*o-r*i)*c,t):null},e.determinant=function(t){return t[0]*t[3]-t[1]*t[2]},e.multiply=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],c=n[5],f=r[0],s=r[1],h=r[2],M=r[3],l=r[4],v=r[5];return t[0]=a*f+u*s,t[1]=e*f+o*s,t[2]=a*h+u*M,t[3]=e*h+o*M,t[4]=a*l+u*v+i,t[5]=e*l+o*v+c,t},e.mul=e.multiply,e.rotate=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],c=n[5],f=Math.sin(r),s=Math.cos(r);return t[0]=a*s+u*f,t[1]=e*s+o*f,t[2]=a*-f+u*s,t[3]=e*-f+o*s,t[4]=i,t[5]=c,t},e.scale=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],c=n[5],f=r[0],s=r[1];return t[0]=a*f,t[1]=e*f,t[2]=u*s,t[3]=o*s,t[4]=i,t[5]=c,t},e.translate=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],c=n[5],f=r[0],s=r[1];return t[0]=a,t[1]=e,t[2]=u,t[3]=o,t[4]=a*f+u*s+i,t[5]=e*f+o*s+c,t},e.fromRotation=function(t,n){var r=Math.sin(n),a=Math.cos(n);return t[0]=a,t[1]=r,t[2]=-r,t[3]=a,t[4]=0,t[5]=0,t},e.fromScaling=function(t,n){return t[0]=n[0],t[1]=0,t[2]=0,t[3]=n[1],t[4]=0,t[5]=0,t},e.fromTranslation=function(t,n){return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t[4]=n[0],t[5]=n[1],t},e.str=function(t){return"mat2d("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+", "+t[4]+", "+t[5]+")"},e.frob=function(t){return Math.sqrt(Math.pow(t[0],2)+Math.pow(t[1],2)+Math.pow(t[2],2)+Math.pow(t[3],2)+Math.pow(t[4],2)+Math.pow(t[5],2)+1)},t.exports=e},function(t,n,r){var a=r(1),e={};e.create=function(){var t=new a.ARRAY_TYPE(9);return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=1,t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},e.fromMat4=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[4],t[4]=n[5],t[5]=n[6],t[6]=n[8],t[7]=n[9],t[8]=n[10],t},e.clone=function(t){var n=new a.ARRAY_TYPE(9);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n[4]=t[4],n[5]=t[5],n[6]=t[6],n[7]=t[7],n[8]=t[8],n},e.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[4]=n[4],t[5]=n[5],t[6]=n[6],t[7]=n[7],t[8]=n[8],t},e.identity=function(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=1,t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},e.transpose=function(t,n){if(t===n){var r=n[1],a=n[2],e=n[5];t[1]=n[3],t[2]=n[6],t[3]=r,t[5]=n[7],t[6]=a,t[7]=e}else t[0]=n[0],t[1]=n[3],t[2]=n[6],t[3]=n[1],t[4]=n[4],t[5]=n[7],t[6]=n[2],t[7]=n[5],t[8]=n[8];return t},e.invert=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],c=n[6],f=n[7],s=n[8],h=s*o-i*f,M=-s*u+i*c,l=f*u-o*c,v=r*h+a*M+e*l;return v?(v=1/v,t[0]=h*v,t[1]=(-s*a+e*f)*v,t[2]=(i*a-e*o)*v,t[3]=M*v,t[4]=(s*r-e*c)*v,t[5]=(-i*r+e*u)*v,t[6]=l*v,t[7]=(-f*r+a*c)*v,t[8]=(o*r-a*u)*v,t):null},e.adjoint=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],c=n[6],f=n[7],s=n[8];return t[0]=o*s-i*f,t[1]=e*f-a*s,t[2]=a*i-e*o,t[3]=i*c-u*s,t[4]=r*s-e*c,t[5]=e*u-r*i,t[6]=u*f-o*c,t[7]=a*c-r*f,t[8]=r*o-a*u,t},e.determinant=function(t){var n=t[0],r=t[1],a=t[2],e=t[3],u=t[4],o=t[5],i=t[6],c=t[7],f=t[8];return n*(f*u-o*c)+r*(-f*e+o*i)+a*(c*e-u*i)},e.multiply=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],c=n[5],f=n[6],s=n[7],h=n[8],M=r[0],l=r[1],v=r[2],m=r[3],p=r[4],d=r[5],A=r[6],R=r[7],w=r[8];return t[0]=M*a+l*o+v*f,t[1]=M*e+l*i+v*s,t[2]=M*u+l*c+v*h,t[3]=m*a+p*o+d*f,t[4]=m*e+p*i+d*s,t[5]=m*u+p*c+d*h,t[6]=A*a+R*o+w*f,t[7]=A*e+R*i+w*s,t[8]=A*u+R*c+w*h,t},e.mul=e.multiply,e.translate=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],c=n[5],f=n[6],s=n[7],h=n[8],M=r[0],l=r[1];return t[0]=a,t[1]=e,t[2]=u,t[3]=o,t[4]=i,t[5]=c,t[6]=M*a+l*o+f,t[7]=M*e+l*i+s,t[8]=M*u+l*c+h,t},e.rotate=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],c=n[5],f=n[6],s=n[7],h=n[8],M=Math.sin(r),l=Math.cos(r);return t[0]=l*a+M*o,t[1]=l*e+M*i,t[2]=l*u+M*c,t[3]=l*o-M*a,t[4]=l*i-M*e,t[5]=l*c-M*u,t[6]=f,t[7]=s,t[8]=h,t},e.scale=function(t,n,r){var a=r[0],e=r[1];return t[0]=a*n[0],t[1]=a*n[1],t[2]=a*n[2],t[3]=e*n[3],t[4]=e*n[4],t[5]=e*n[5],t[6]=n[6],t[7]=n[7],t[8]=n[8],t},e.fromTranslation=function(t,n){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=1,t[5]=0,t[6]=n[0],t[7]=n[1],t[8]=1,t},e.fromRotation=function(t,n){var r=Math.sin(n),a=Math.cos(n);return t[0]=a,t[1]=r,t[2]=0,t[3]=-r,t[4]=a,t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},e.fromScaling=function(t,n){return t[0]=n[0],t[1]=0,t[2]=0,t[3]=0,t[4]=n[1],t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},e.fromMat2d=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=0,t[3]=n[2],t[4]=n[3],t[5]=0,t[6]=n[4],t[7]=n[5],t[8]=1,t},e.fromQuat=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=r+r,i=a+a,c=e+e,f=r*o,s=a*o,h=a*i,M=e*o,l=e*i,v=e*c,m=u*o,p=u*i,d=u*c;return t[0]=1-h-v,t[3]=s-d,t[6]=M+p,t[1]=s+d,t[4]=1-f-v,t[7]=l-m,t[2]=M-p,t[5]=l+m,t[8]=1-f-h,t},e.normalFromMat4=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],c=n[6],f=n[7],s=n[8],h=n[9],M=n[10],l=n[11],v=n[12],m=n[13],p=n[14],d=n[15],A=r*i-a*o,R=r*c-e*o,w=r*f-u*o,q=a*c-e*i,Y=a*f-u*i,g=e*f-u*c,y=s*m-h*v,x=s*p-M*v,P=s*d-l*v,E=h*p-M*m,T=h*d-l*m,b=M*d-l*p,D=A*b-R*T+w*E+q*P-Y*x+g*y;return D?(D=1/D,t[0]=(i*b-c*T+f*E)*D,t[1]=(c*P-o*b-f*x)*D,t[2]=(o*T-i*P+f*y)*D,t[3]=(e*T-a*b-u*E)*D,t[4]=(r*b-e*P+u*x)*D,t[5]=(a*P-r*T-u*y)*D,t[6]=(m*g-p*Y+d*q)*D,t[7]=(p*w-v*g-d*R)*D,t[8]=(v*Y-m*w+d*A)*D,t):null},e.str=function(t){return"mat3("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+", "+t[4]+", "+t[5]+", "+t[6]+", "+t[7]+", "+t[8]+")"},e.frob=function(t){return Math.sqrt(Math.pow(t[0],2)+Math.pow(t[1],2)+Math.pow(t[2],2)+Math.pow(t[3],2)+Math.pow(t[4],2)+Math.pow(t[5],2)+Math.pow(t[6],2)+Math.pow(t[7],2)+Math.pow(t[8],2))},t.exports=e},function(t,n,r){var a=r(1),e={};e.create=function(){var t=new a.ARRAY_TYPE(16);return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=1,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=1,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},e.clone=function(t){var n=new a.ARRAY_TYPE(16);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n[4]=t[4],n[5]=t[5],n[6]=t[6],n[7]=t[7],n[8]=t[8],n[9]=t[9],n[10]=t[10],n[11]=t[11],n[12]=t[12],n[13]=t[13],n[14]=t[14],n[15]=t[15],n},e.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[4]=n[4],t[5]=n[5],t[6]=n[6],t[7]=n[7],t[8]=n[8],t[9]=n[9],t[10]=n[10],t[11]=n[11],t[12]=n[12],t[13]=n[13],t[14]=n[14],t[15]=n[15],t},e.identity=function(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=1,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=1,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},e.transpose=function(t,n){if(t===n){var r=n[1],a=n[2],e=n[3],u=n[6],o=n[7],i=n[11];t[1]=n[4],t[2]=n[8],t[3]=n[12],t[4]=r,t[6]=n[9],t[7]=n[13],t[8]=a,t[9]=u,t[11]=n[14],t[12]=e,t[13]=o,t[14]=i}else t[0]=n[0],t[1]=n[4],t[2]=n[8],t[3]=n[12],t[4]=n[1],t[5]=n[5],t[6]=n[9],t[7]=n[13],t[8]=n[2],t[9]=n[6],t[10]=n[10],t[11]=n[14],t[12]=n[3],t[13]=n[7],t[14]=n[11],t[15]=n[15];return t},e.invert=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],c=n[6],f=n[7],s=n[8],h=n[9],M=n[10],l=n[11],v=n[12],m=n[13],p=n[14],d=n[15],A=r*i-a*o,R=r*c-e*o,w=r*f-u*o,q=a*c-e*i,Y=a*f-u*i,g=e*f-u*c,y=s*m-h*v,x=s*p-M*v,P=s*d-l*v,E=h*p-M*m,T=h*d-l*m,b=M*d-l*p,D=A*b-R*T+w*E+q*P-Y*x+g*y;return D?(D=1/D,t[0]=(i*b-c*T+f*E)*D,t[1]=(e*T-a*b-u*E)*D,t[2]=(m*g-p*Y+d*q)*D,t[3]=(M*Y-h*g-l*q)*D,t[4]=(c*P-o*b-f*x)*D,t[5]=(r*b-e*P+u*x)*D,t[6]=(p*w-v*g-d*R)*D,t[7]=(s*g-M*w+l*R)*D,t[8]=(o*T-i*P+f*y)*D,t[9]=(a*P-r*T-u*y)*D,t[10]=(v*Y-m*w+d*A)*D,t[11]=(h*w-s*Y-l*A)*D,t[12]=(i*x-o*E-c*y)*D,t[13]=(r*E-a*x+e*y)*D,t[14]=(m*R-v*q-p*A)*D,t[15]=(s*q-h*R+M*A)*D,t):null},e.adjoint=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],c=n[6],f=n[7],s=n[8],h=n[9],M=n[10],l=n[11],v=n[12],m=n[13],p=n[14],d=n[15];return t[0]=i*(M*d-l*p)-h*(c*d-f*p)+m*(c*l-f*M),t[1]=-(a*(M*d-l*p)-h*(e*d-u*p)+m*(e*l-u*M)),t[2]=a*(c*d-f*p)-i*(e*d-u*p)+m*(e*f-u*c),t[3]=-(a*(c*l-f*M)-i*(e*l-u*M)+h*(e*f-u*c)),t[4]=-(o*(M*d-l*p)-s*(c*d-f*p)+v*(c*l-f*M)),t[5]=r*(M*d-l*p)-s*(e*d-u*p)+v*(e*l-u*M),t[6]=-(r*(c*d-f*p)-o*(e*d-u*p)+v*(e*f-u*c)),t[7]=r*(c*l-f*M)-o*(e*l-u*M)+s*(e*f-u*c),t[8]=o*(h*d-l*m)-s*(i*d-f*m)+v*(i*l-f*h),t[9]=-(r*(h*d-l*m)-s*(a*d-u*m)+v*(a*l-u*h)),t[10]=r*(i*d-f*m)-o*(a*d-u*m)+v*(a*f-u*i),t[11]=-(r*(i*l-f*h)-o*(a*l-u*h)+s*(a*f-u*i)),t[12]=-(o*(h*p-M*m)-s*(i*p-c*m)+v*(i*M-c*h)),t[13]=r*(h*p-M*m)-s*(a*p-e*m)+v*(a*M-e*h),t[14]=-(r*(i*p-c*m)-o*(a*p-e*m)+v*(a*c-e*i)),t[15]=r*(i*M-c*h)-o*(a*M-e*h)+s*(a*c-e*i),t},e.determinant=function(t){var n=t[0],r=t[1],a=t[2],e=t[3],u=t[4],o=t[5],i=t[6],c=t[7],f=t[8],s=t[9],h=t[10],M=t[11],l=t[12],v=t[13],m=t[14],p=t[15],d=n*o-r*u,A=n*i-a*u,R=n*c-e*u,w=r*i-a*o,q=r*c-e*o,Y=a*c-e*i,g=f*v-s*l,y=f*m-h*l,x=f*p-M*l,P=s*m-h*v,E=s*p-M*v,T=h*p-M*m;return d*T-A*E+R*P+w*x-q*y+Y*g},e.multiply=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],c=n[5],f=n[6],s=n[7],h=n[8],M=n[9],l=n[10],v=n[11],m=n[12],p=n[13],d=n[14],A=n[15],R=r[0],w=r[1],q=r[2],Y=r[3];return t[0]=R*a+w*i+q*h+Y*m,t[1]=R*e+w*c+q*M+Y*p,t[2]=R*u+w*f+q*l+Y*d,t[3]=R*o+w*s+q*v+Y*A,R=r[4],w=r[5],q=r[6],Y=r[7],t[4]=R*a+w*i+q*h+Y*m,t[5]=R*e+w*c+q*M+Y*p,t[6]=R*u+w*f+q*l+Y*d,t[7]=R*o+w*s+q*v+Y*A,R=r[8],w=r[9],q=r[10],Y=r[11],t[8]=R*a+w*i+q*h+Y*m,t[9]=R*e+w*c+q*M+Y*p,t[10]=R*u+w*f+q*l+Y*d,t[11]=R*o+w*s+q*v+Y*A,R=r[12],w=r[13],q=r[14],Y=r[15],t[12]=R*a+w*i+q*h+Y*m,t[13]=R*e+w*c+q*M+Y*p,t[14]=R*u+w*f+q*l+Y*d,t[15]=R*o+w*s+q*v+Y*A,t},e.mul=e.multiply,e.translate=function(t,n,r){var a,e,u,o,i,c,f,s,h,M,l,v,m=r[0],p=r[1],d=r[2];return n===t?(t[12]=n[0]*m+n[4]*p+n[8]*d+n[12],t[13]=n[1]*m+n[5]*p+n[9]*d+n[13],t[14]=n[2]*m+n[6]*p+n[10]*d+n[14],t[15]=n[3]*m+n[7]*p+n[11]*d+n[15]):(a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],c=n[5],f=n[6],s=n[7],h=n[8],M=n[9],l=n[10],v=n[11],t[0]=a,t[1]=e,t[2]=u,t[3]=o,t[4]=i,t[5]=c,t[6]=f,t[7]=s,t[8]=h,t[9]=M,t[10]=l,t[11]=v,t[12]=a*m+i*p+h*d+n[12],t[13]=e*m+c*p+M*d+n[13],t[14]=u*m+f*p+l*d+n[14],t[15]=o*m+s*p+v*d+n[15]),t},e.scale=function(t,n,r){var a=r[0],e=r[1],u=r[2];return t[0]=n[0]*a,t[1]=n[1]*a,t[2]=n[2]*a,t[3]=n[3]*a,t[4]=n[4]*e,t[5]=n[5]*e,t[6]=n[6]*e,t[7]=n[7]*e,t[8]=n[8]*u,t[9]=n[9]*u,t[10]=n[10]*u,t[11]=n[11]*u,t[12]=n[12],t[13]=n[13],t[14]=n[14],t[15]=n[15],t},e.rotate=function(t,n,r,e){var u,o,i,c,f,s,h,M,l,v,m,p,d,A,R,w,q,Y,g,y,x,P,E,T,b=e[0],D=e[1],L=e[2],_=Math.sqrt(b*b+D*D+L*L);return Math.abs(_)<a.EPSILON?null:(_=1/_,b*=_,D*=_,L*=_,u=Math.sin(r),o=Math.cos(r),i=1-o,c=n[0],f=n[1],s=n[2],h=n[3],M=n[4],l=n[5],v=n[6],m=n[7],p=n[8],d=n[9],A=n[10],R=n[11],w=b*b*i+o,q=D*b*i+L*u,Y=L*b*i-D*u,g=b*D*i-L*u,y=D*D*i+o,x=L*D*i+b*u,P=b*L*i+D*u,E=D*L*i-b*u,T=L*L*i+o,t[0]=c*w+M*q+p*Y,t[1]=f*w+l*q+d*Y,t[2]=s*w+v*q+A*Y,t[3]=h*w+m*q+R*Y,t[4]=c*g+M*y+p*x,t[5]=f*g+l*y+d*x,t[6]=s*g+v*y+A*x,t[7]=h*g+m*y+R*x,t[8]=c*P+M*E+p*T,t[9]=f*P+l*E+d*T,t[10]=s*P+v*E+A*T,t[11]=h*P+m*E+R*T,n!==t&&(t[12]=n[12],t[13]=n[13],t[14]=n[14],t[15]=n[15]),t)},e.rotateX=function(t,n,r){var a=Math.sin(r),e=Math.cos(r),u=n[4],o=n[5],i=n[6],c=n[7],f=n[8],s=n[9],h=n[10],M=n[11];return n!==t&&(t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[12]=n[12],t[13]=n[13],t[14]=n[14],t[15]=n[15]),t[4]=u*e+f*a,t[5]=o*e+s*a,t[6]=i*e+h*a,t[7]=c*e+M*a,t[8]=f*e-u*a,t[9]=s*e-o*a,t[10]=h*e-i*a,t[11]=M*e-c*a,t},e.rotateY=function(t,n,r){var a=Math.sin(r),e=Math.cos(r),u=n[0],o=n[1],i=n[2],c=n[3],f=n[8],s=n[9],h=n[10],M=n[11];return n!==t&&(t[4]=n[4],t[5]=n[5],t[6]=n[6],t[7]=n[7],t[12]=n[12],t[13]=n[13],t[14]=n[14],t[15]=n[15]),t[0]=u*e-f*a,t[1]=o*e-s*a,t[2]=i*e-h*a,t[3]=c*e-M*a,t[8]=u*a+f*e,t[9]=o*a+s*e,t[10]=i*a+h*e,t[11]=c*a+M*e,t},e.rotateZ=function(t,n,r){var a=Math.sin(r),e=Math.cos(r),u=n[0],o=n[1],i=n[2],c=n[3],f=n[4],s=n[5],h=n[6],M=n[7];return n!==t&&(t[8]=n[8],t[9]=n[9],t[10]=n[10],t[11]=n[11],t[12]=n[12],t[13]=n[13],t[14]=n[14],t[15]=n[15]),t[0]=u*e+f*a,t[1]=o*e+s*a,t[2]=i*e+h*a,t[3]=c*e+M*a,t[4]=f*e-u*a,t[5]=s*e-o*a,t[6]=h*e-i*a,t[7]=M*e-c*a,t},e.fromTranslation=function(t,n){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=1,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=1,t[11]=0,t[12]=n[0],t[13]=n[1],t[14]=n[2],t[15]=1,t},e.fromScaling=function(t,n){return t[0]=n[0],t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=n[1],t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=n[2],t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},e.fromRotation=function(t,n,r){var e,u,o,i=r[0],c=r[1],f=r[2],s=Math.sqrt(i*i+c*c+f*f);return Math.abs(s)<a.EPSILON?null:(s=1/s,i*=s,c*=s,f*=s,e=Math.sin(n),u=Math.cos(n),o=1-u,t[0]=i*i*o+u,t[1]=c*i*o+f*e,t[2]=f*i*o-c*e,t[3]=0,t[4]=i*c*o-f*e,t[5]=c*c*o+u,t[6]=f*c*o+i*e,t[7]=0,t[8]=i*f*o+c*e,t[9]=c*f*o-i*e,t[10]=f*f*o+u,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t)},e.fromXRotation=function(t,n){var r=Math.sin(n),a=Math.cos(n);return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=a,t[6]=r,t[7]=0,t[8]=0,t[9]=-r,t[10]=a,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},e.fromYRotation=function(t,n){var r=Math.sin(n),a=Math.cos(n);return t[0]=a,t[1]=0,t[2]=-r,t[3]=0,t[4]=0,t[5]=1,t[6]=0,t[7]=0,t[8]=r,t[9]=0,t[10]=a,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},e.fromZRotation=function(t,n){var r=Math.sin(n),a=Math.cos(n);return t[0]=a,t[1]=r,t[2]=0,t[3]=0,t[4]=-r,t[5]=a,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=1,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},e.fromRotationTranslation=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=a+a,c=e+e,f=u+u,s=a*i,h=a*c,M=a*f,l=e*c,v=e*f,m=u*f,p=o*i,d=o*c,A=o*f;return t[0]=1-(l+m),t[1]=h+A,t[2]=M-d,t[3]=0,t[4]=h-A,t[5]=1-(s+m),t[6]=v+p,t[7]=0,t[8]=M+d,t[9]=v-p,t[10]=1-(s+l),t[11]=0,t[12]=r[0],t[13]=r[1],t[14]=r[2],t[15]=1,t},e.fromRotationTranslationScale=function(t,n,r,a){var e=n[0],u=n[1],o=n[2],i=n[3],c=e+e,f=u+u,s=o+o,h=e*c,M=e*f,l=e*s,v=u*f,m=u*s,p=o*s,d=i*c,A=i*f,R=i*s,w=a[0],q=a[1],Y=a[2];return t[0]=(1-(v+p))*w,t[1]=(M+R)*w,t[2]=(l-A)*w,t[3]=0,t[4]=(M-R)*q,t[5]=(1-(h+p))*q,t[6]=(m+d)*q,t[7]=0,t[8]=(l+A)*Y,t[9]=(m-d)*Y,t[10]=(1-(h+v))*Y,t[11]=0,t[12]=r[0],t[13]=r[1],t[14]=r[2],t[15]=1,t},e.fromRotationTranslationScaleOrigin=function(t,n,r,a,e){var u=n[0],o=n[1],i=n[2],c=n[3],f=u+u,s=o+o,h=i+i,M=u*f,l=u*s,v=u*h,m=o*s,p=o*h,d=i*h,A=c*f,R=c*s,w=c*h,q=a[0],Y=a[1],g=a[2],y=e[0],x=e[1],P=e[2];return t[0]=(1-(m+d))*q,t[1]=(l+w)*q,t[2]=(v-R)*q,t[3]=0,t[4]=(l-w)*Y,t[5]=(1-(M+d))*Y,t[6]=(p+A)*Y,t[7]=0,t[8]=(v+R)*g,t[9]=(p-A)*g,t[10]=(1-(M+m))*g,t[11]=0,t[12]=r[0]+y-(t[0]*y+t[4]*x+t[8]*P),t[13]=r[1]+x-(t[1]*y+t[5]*x+t[9]*P),t[14]=r[2]+P-(t[2]*y+t[6]*x+t[10]*P),t[15]=1,t},e.fromQuat=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=r+r,i=a+a,c=e+e,f=r*o,s=a*o,h=a*i,M=e*o,l=e*i,v=e*c,m=u*o,p=u*i,d=u*c;return t[0]=1-h-v,t[1]=s+d,t[2]=M-p,t[3]=0,t[4]=s-d,t[5]=1-f-v,t[6]=l+m,t[7]=0,t[8]=M+p,t[9]=l-m,t[10]=1-f-h,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},e.frustum=function(t,n,r,a,e,u,o){var i=1/(r-n),c=1/(e-a),f=1/(u-o);return t[0]=2*u*i,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=2*u*c,t[6]=0,t[7]=0,t[8]=(r+n)*i,t[9]=(e+a)*c,t[10]=(o+u)*f,t[11]=-1,t[12]=0,t[13]=0,t[14]=o*u*2*f,t[15]=0,t},e.perspective=function(t,n,r,a,e){var u=1/Math.tan(n/2),o=1/(a-e);return t[0]=u/r,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=u,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=(e+a)*o,t[11]=-1,t[12]=0,t[13]=0,t[14]=2*e*a*o,t[15]=0,t},e.perspectiveFromFieldOfView=function(t,n,r,a){var e=Math.tan(n.upDegrees*Math.PI/180),u=Math.tan(n.downDegrees*Math.PI/180),o=Math.tan(n.leftDegrees*Math.PI/180),i=Math.tan(n.rightDegrees*Math.PI/180),c=2/(o+i),f=2/(e+u);return t[0]=c,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=f,t[6]=0,t[7]=0,t[8]=-((o-i)*c*.5),t[9]=(e-u)*f*.5,t[10]=a/(r-a),t[11]=-1,t[12]=0,t[13]=0,t[14]=a*r/(r-a),t[15]=0,t},e.ortho=function(t,n,r,a,e,u,o){var i=1/(n-r),c=1/(a-e),f=1/(u-o);return t[0]=-2*i,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=-2*c,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=2*f,t[11]=0,t[12]=(n+r)*i,t[13]=(e+a)*c,t[14]=(o+u)*f,t[15]=1,t},e.lookAt=function(t,n,r,u){var o,i,c,f,s,h,M,l,v,m,p=n[0],d=n[1],A=n[2],R=u[0],w=u[1],q=u[2],Y=r[0],g=r[1],y=r[2];return Math.abs(p-Y)<a.EPSILON&&Math.abs(d-g)<a.EPSILON&&Math.abs(A-y)<a.EPSILON?e.identity(t):(M=p-Y,l=d-g,v=A-y,m=1/Math.sqrt(M*M+l*l+v*v),M*=m,l*=m,v*=m,o=w*v-q*l,i=q*M-R*v,c=R*l-w*M,m=Math.sqrt(o*o+i*i+c*c),m?(m=1/m,o*=m,i*=m,c*=m):(o=0,i=0,c=0),f=l*c-v*i,s=v*o-M*c,h=M*i-l*o,m=Math.sqrt(f*f+s*s+h*h),m?(m=1/m,f*=m,s*=m,h*=m):(f=0,s=0,h=0),t[0]=o,t[1]=f,t[2]=M,t[3]=0,t[4]=i,t[5]=s,t[6]=l,t[7]=0,t[8]=c,t[9]=h,t[10]=v,t[11]=0,t[12]=-(o*p+i*d+c*A),t[13]=-(f*p+s*d+h*A),t[14]=-(M*p+l*d+v*A),t[15]=1,t)},e.str=function(t){return"mat4("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+", "+t[4]+", "+t[5]+", "+t[6]+", "+t[7]+", "+t[8]+", "+t[9]+", "+t[10]+", "+t[11]+", "+t[12]+", "+t[13]+", "+t[14]+", "+t[15]+")"},e.frob=function(t){return Math.sqrt(Math.pow(t[0],2)+Math.pow(t[1],2)+Math.pow(t[2],2)+Math.pow(t[3],2)+Math.pow(t[4],2)+Math.pow(t[5],2)+Math.pow(t[6],2)+Math.pow(t[7],2)+Math.pow(t[8],2)+Math.pow(t[9],2)+Math.pow(t[10],2)+Math.pow(t[11],2)+Math.pow(t[12],2)+Math.pow(t[13],2)+Math.pow(t[14],2)+Math.pow(t[15],2))},t.exports=e},function(t,n,r){var a=r(1),e=r(4),u=r(7),o=r(8),i={};i.create=function(){var t=new a.ARRAY_TYPE(4);return t[0]=0,t[1]=0,t[2]=0,t[3]=1,t},i.rotationTo=function(){var t=u.create(),n=u.fromValues(1,0,0),r=u.fromValues(0,1,0);return function(a,e,o){var c=u.dot(e,o);return-.999999>c?(u.cross(t,n,e),u.length(t)<1e-6&&u.cross(t,r,e),u.normalize(t,t),i.setAxisAngle(a,t,Math.PI),a):c>.999999?(a[0]=0,a[1]=0,a[2]=0,a[3]=1,a):(u.cross(t,e,o),a[0]=t[0],a[1]=t[1],a[2]=t[2],a[3]=1+c,i.normalize(a,a))}}(),i.setAxes=function(){var t=e.create();return function(n,r,a,e){return t[0]=a[0],t[3]=a[1],t[6]=a[2],t[1]=e[0],t[4]=e[1],t[7]=e[2],t[2]=-r[0],t[5]=-r[1],t[8]=-r[2],i.normalize(n,i.fromMat3(n,t))}}(),i.clone=o.clone,i.fromValues=o.fromValues,i.copy=o.copy,i.set=o.set,i.identity=function(t){return t[0]=0,t[1]=0,t[2]=0,t[3]=1,t},i.setAxisAngle=function(t,n,r){r=.5*r;var a=Math.sin(r);return t[0]=a*n[0],t[1]=a*n[1],t[2]=a*n[2],t[3]=Math.cos(r),t},i.add=o.add,i.multiply=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=r[0],c=r[1],f=r[2],s=r[3];return t[0]=a*s+o*i+e*f-u*c,t[1]=e*s+o*c+u*i-a*f,t[2]=u*s+o*f+a*c-e*i,t[3]=o*s-a*i-e*c-u*f,t},i.mul=i.multiply,i.scale=o.scale,i.rotateX=function(t,n,r){r*=.5;var a=n[0],e=n[1],u=n[2],o=n[3],i=Math.sin(r),c=Math.cos(r);return t[0]=a*c+o*i,t[1]=e*c+u*i,t[2]=u*c-e*i,t[3]=o*c-a*i,t},i.rotateY=function(t,n,r){r*=.5;var a=n[0],e=n[1],u=n[2],o=n[3],i=Math.sin(r),c=Math.cos(r);return t[0]=a*c-u*i,t[1]=e*c+o*i,t[2]=u*c+a*i,t[3]=o*c-e*i,t},i.rotateZ=function(t,n,r){r*=.5;var a=n[0],e=n[1],u=n[2],o=n[3],i=Math.sin(r),c=Math.cos(r);return t[0]=a*c+e*i,t[1]=e*c-a*i,t[2]=u*c+o*i,t[3]=o*c-u*i,t},i.calculateW=function(t,n){var r=n[0],a=n[1],e=n[2];return t[0]=r,t[1]=a,t[2]=e,t[3]=Math.sqrt(Math.abs(1-r*r-a*a-e*e)),t},i.dot=o.dot,i.lerp=o.lerp,i.slerp=function(t,n,r,a){var e,u,o,i,c,f=n[0],s=n[1],h=n[2],M=n[3],l=r[0],v=r[1],m=r[2],p=r[3];return u=f*l+s*v+h*m+M*p,0>u&&(u=-u,l=-l,v=-v,m=-m,p=-p),1-u>1e-6?(e=Math.acos(u),o=Math.sin(e),i=Math.sin((1-a)*e)/o,c=Math.sin(a*e)/o):(i=1-a,c=a),t[0]=i*f+c*l,t[1]=i*s+c*v,t[2]=i*h+c*m,t[3]=i*M+c*p,t},i.sqlerp=function(){var t=i.create(),n=i.create();return function(r,a,e,u,o,c){return i.slerp(t,a,o,c),i.slerp(n,e,u,c),i.slerp(r,t,n,2*c*(1-c)),r}}(),i.invert=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=r*r+a*a+e*e+u*u,i=o?1/o:0;return t[0]=-r*i,t[1]=-a*i,t[2]=-e*i,t[3]=u*i,t},i.conjugate=function(t,n){return t[0]=-n[0],t[1]=-n[1],t[2]=-n[2],t[3]=n[3],t},i.length=o.length,i.len=i.length,i.squaredLength=o.squaredLength,i.sqrLen=i.squaredLength,i.normalize=o.normalize,i.fromMat3=function(t,n){var r,a=n[0]+n[4]+n[8];if(a>0)r=Math.sqrt(a+1),t[3]=.5*r,r=.5/r,t[0]=(n[5]-n[7])*r,t[1]=(n[6]-n[2])*r,t[2]=(n[1]-n[3])*r;else{var e=0;n[4]>n[0]&&(e=1),n[8]>n[3*e+e]&&(e=2);var u=(e+1)%3,o=(e+2)%3;r=Math.sqrt(n[3*e+e]-n[3*u+u]-n[3*o+o]+1),t[e]=.5*r,r=.5/r,t[3]=(n[3*u+o]-n[3*o+u])*r,t[u]=(n[3*u+e]+n[3*e+u])*r,t[o]=(n[3*o+e]+n[3*e+o])*r}return t},i.str=function(t){return"quat("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+")"},t.exports=i},function(t,n,r){var a=r(1),e={};e.create=function(){var t=new a.ARRAY_TYPE(3);return t[0]=0,t[1]=0,t[2]=0,t},e.clone=function(t){var n=new a.ARRAY_TYPE(3);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n},e.fromValues=function(t,n,r){var e=new a.ARRAY_TYPE(3);return e[0]=t,e[1]=n,e[2]=r,e},e.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t},e.set=function(t,n,r,a){return t[0]=n,t[1]=r,t[2]=a,t},e.add=function(t,n,r){return t[0]=n[0]+r[0],t[1]=n[1]+r[1],t[2]=n[2]+r[2],t},e.subtract=function(t,n,r){return t[0]=n[0]-r[0],t[1]=n[1]-r[1],t[2]=n[2]-r[2],t},e.sub=e.subtract,e.multiply=function(t,n,r){return t[0]=n[0]*r[0],t[1]=n[1]*r[1],t[2]=n[2]*r[2],t},e.mul=e.multiply,e.divide=function(t,n,r){return t[0]=n[0]/r[0],t[1]=n[1]/r[1],t[2]=n[2]/r[2],t},e.div=e.divide,e.min=function(t,n,r){return t[0]=Math.min(n[0],r[0]),t[1]=Math.min(n[1],r[1]),t[2]=Math.min(n[2],r[2]),t},e.max=function(t,n,r){return t[0]=Math.max(n[0],r[0]),t[1]=Math.max(n[1],r[1]),t[2]=Math.max(n[2],r[2]),t},e.scale=function(t,n,r){return t[0]=n[0]*r,t[1]=n[1]*r,t[2]=n[2]*r,t},e.scaleAndAdd=function(t,n,r,a){return t[0]=n[0]+r[0]*a,t[1]=n[1]+r[1]*a,t[2]=n[2]+r[2]*a,t},e.distance=function(t,n){var r=n[0]-t[0],a=n[1]-t[1],e=n[2]-t[2];return Math.sqrt(r*r+a*a+e*e)},e.dist=e.distance,e.squaredDistance=function(t,n){var r=n[0]-t[0],a=n[1]-t[1],e=n[2]-t[2];return r*r+a*a+e*e},e.sqrDist=e.squaredDistance,e.length=function(t){var n=t[0],r=t[1],a=t[2];return Math.sqrt(n*n+r*r+a*a)},e.len=e.length,e.squaredLength=function(t){var n=t[0],r=t[1],a=t[2];return n*n+r*r+a*a},e.sqrLen=e.squaredLength,e.negate=function(t,n){return t[0]=-n[0],t[1]=-n[1],t[2]=-n[2],t},e.inverse=function(t,n){return t[0]=1/n[0],t[1]=1/n[1],t[2]=1/n[2],t},e.normalize=function(t,n){var r=n[0],a=n[1],e=n[2],u=r*r+a*a+e*e;return u>0&&(u=1/Math.sqrt(u),t[0]=n[0]*u,t[1]=n[1]*u,t[2]=n[2]*u),t},e.dot=function(t,n){return t[0]*n[0]+t[1]*n[1]+t[2]*n[2]},e.cross=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=r[0],i=r[1],c=r[2];return t[0]=e*c-u*i,t[1]=u*o-a*c,t[2]=a*i-e*o,t},e.lerp=function(t,n,r,a){var e=n[0],u=n[1],o=n[2];return t[0]=e+a*(r[0]-e),t[1]=u+a*(r[1]-u),t[2]=o+a*(r[2]-o),t},e.hermite=function(t,n,r,a,e,u){var o=u*u,i=o*(2*u-3)+1,c=o*(u-2)+u,f=o*(u-1),s=o*(3-2*u);return t[0]=n[0]*i+r[0]*c+a[0]*f+e[0]*s,t[1]=n[1]*i+r[1]*c+a[1]*f+e[1]*s,t[2]=n[2]*i+r[2]*c+a[2]*f+e[2]*s,t},e.bezier=function(t,n,r,a,e,u){var o=1-u,i=o*o,c=u*u,f=i*o,s=3*u*i,h=3*c*o,M=c*u;return t[0]=n[0]*f+r[0]*s+a[0]*h+e[0]*M,t[1]=n[1]*f+r[1]*s+a[1]*h+e[1]*M,t[2]=n[2]*f+r[2]*s+a[2]*h+e[2]*M,t},e.random=function(t,n){n=n||1;var r=2*a.RANDOM()*Math.PI,e=2*a.RANDOM()-1,u=Math.sqrt(1-e*e)*n;return t[0]=Math.cos(r)*u,t[1]=Math.sin(r)*u,t[2]=e*n,t},e.transformMat4=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=r[3]*a+r[7]*e+r[11]*u+r[15];return o=o||1,t[0]=(r[0]*a+r[4]*e+r[8]*u+r[12])/o,t[1]=(r[1]*a+r[5]*e+r[9]*u+r[13])/o,t[2]=(r[2]*a+r[6]*e+r[10]*u+r[14])/o,t},e.transformMat3=function(t,n,r){var a=n[0],e=n[1],u=n[2];return t[0]=a*r[0]+e*r[3]+u*r[6],t[1]=a*r[1]+e*r[4]+u*r[7],t[2]=a*r[2]+e*r[5]+u*r[8],t},e.transformQuat=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=r[0],i=r[1],c=r[2],f=r[3],s=f*a+i*u-c*e,h=f*e+c*a-o*u,M=f*u+o*e-i*a,l=-o*a-i*e-c*u;return t[0]=s*f+l*-o+h*-c-M*-i,t[1]=h*f+l*-i+M*-o-s*-c,t[2]=M*f+l*-c+s*-i-h*-o,t},e.rotateX=function(t,n,r,a){var e=[],u=[];return e[0]=n[0]-r[0],e[1]=n[1]-r[1],e[2]=n[2]-r[2],u[0]=e[0],u[1]=e[1]*Math.cos(a)-e[2]*Math.sin(a),u[2]=e[1]*Math.sin(a)+e[2]*Math.cos(a),t[0]=u[0]+r[0],t[1]=u[1]+r[1],t[2]=u[2]+r[2],t},e.rotateY=function(t,n,r,a){var e=[],u=[];return e[0]=n[0]-r[0],e[1]=n[1]-r[1],e[2]=n[2]-r[2],u[0]=e[2]*Math.sin(a)+e[0]*Math.cos(a),u[1]=e[1],u[2]=e[2]*Math.cos(a)-e[0]*Math.sin(a),t[0]=u[0]+r[0],t[1]=u[1]+r[1],t[2]=u[2]+r[2],t},e.rotateZ=function(t,n,r,a){var e=[],u=[];return e[0]=n[0]-r[0],e[1]=n[1]-r[1],e[2]=n[2]-r[2],u[0]=e[0]*Math.cos(a)-e[1]*Math.sin(a),u[1]=e[0]*Math.sin(a)+e[1]*Math.cos(a),u[2]=e[2],t[0]=u[0]+r[0],t[1]=u[1]+r[1],t[2]=u[2]+r[2],t},e.forEach=function(){var t=e.create();return function(n,r,a,e,u,o){var i,c;for(r||(r=3),a||(a=0),c=e?Math.min(e*r+a,n.length):n.length,i=a;c>i;i+=r)t[0]=n[i],t[1]=n[i+1],t[2]=n[i+2],u(t,t,o),n[i]=t[0],n[i+1]=t[1],n[i+2]=t[2];return n}}(),e.angle=function(t,n){var r=e.fromValues(t[0],t[1],t[2]),a=e.fromValues(n[0],n[1],n[2]);e.normalize(r,r),e.normalize(a,a);var u=e.dot(r,a);return u>1?0:Math.acos(u)},e.str=function(t){return"vec3("+t[0]+", "+t[1]+", "+t[2]+")"},t.exports=e},function(t,n,r){var a=r(1),e={};e.create=function(){var t=new a.ARRAY_TYPE(4);return t[0]=0,t[1]=0,t[2]=0,t[3]=0,t},e.clone=function(t){var n=new a.ARRAY_TYPE(4);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n},e.fromValues=function(t,n,r,e){var u=new a.ARRAY_TYPE(4);return u[0]=t,u[1]=n,u[2]=r,u[3]=e,u},e.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t},e.set=function(t,n,r,a,e){return t[0]=n,t[1]=r,t[2]=a,t[3]=e,t},e.add=function(t,n,r){return t[0]=n[0]+r[0],t[1]=n[1]+r[1],t[2]=n[2]+r[2],t[3]=n[3]+r[3],t},e.subtract=function(t,n,r){return t[0]=n[0]-r[0],t[1]=n[1]-r[1],t[2]=n[2]-r[2],t[3]=n[3]-r[3],t},e.sub=e.subtract,e.multiply=function(t,n,r){return t[0]=n[0]*r[0],t[1]=n[1]*r[1],t[2]=n[2]*r[2],t[3]=n[3]*r[3],t},e.mul=e.multiply,e.divide=function(t,n,r){return t[0]=n[0]/r[0],t[1]=n[1]/r[1],t[2]=n[2]/r[2],t[3]=n[3]/r[3],t},e.div=e.divide,e.min=function(t,n,r){return t[0]=Math.min(n[0],r[0]),t[1]=Math.min(n[1],r[1]),t[2]=Math.min(n[2],r[2]),t[3]=Math.min(n[3],r[3]),t},e.max=function(t,n,r){return t[0]=Math.max(n[0],r[0]),t[1]=Math.max(n[1],r[1]),t[2]=Math.max(n[2],r[2]),t[3]=Math.max(n[3],r[3]),t},e.scale=function(t,n,r){return t[0]=n[0]*r,t[1]=n[1]*r,t[2]=n[2]*r,t[3]=n[3]*r,t},e.scaleAndAdd=function(t,n,r,a){return t[0]=n[0]+r[0]*a,t[1]=n[1]+r[1]*a,t[2]=n[2]+r[2]*a,t[3]=n[3]+r[3]*a,t},e.distance=function(t,n){var r=n[0]-t[0],a=n[1]-t[1],e=n[2]-t[2],u=n[3]-t[3];return Math.sqrt(r*r+a*a+e*e+u*u)},e.dist=e.distance,e.squaredDistance=function(t,n){var r=n[0]-t[0],a=n[1]-t[1],e=n[2]-t[2],u=n[3]-t[3];return r*r+a*a+e*e+u*u},e.sqrDist=e.squaredDistance,e.length=function(t){var n=t[0],r=t[1],a=t[2],e=t[3];return Math.sqrt(n*n+r*r+a*a+e*e)},e.len=e.length,e.squaredLength=function(t){var n=t[0],r=t[1],a=t[2],e=t[3];return n*n+r*r+a*a+e*e},e.sqrLen=e.squaredLength,e.negate=function(t,n){return t[0]=-n[0],t[1]=-n[1],t[2]=-n[2],t[3]=-n[3],t},e.inverse=function(t,n){return t[0]=1/n[0],t[1]=1/n[1],t[2]=1/n[2],t[3]=1/n[3],t},e.normalize=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=r*r+a*a+e*e+u*u;return o>0&&(o=1/Math.sqrt(o),t[0]=r*o,t[1]=a*o,t[2]=e*o,t[3]=u*o),t},e.dot=function(t,n){return t[0]*n[0]+t[1]*n[1]+t[2]*n[2]+t[3]*n[3]},e.lerp=function(t,n,r,a){var e=n[0],u=n[1],o=n[2],i=n[3];return t[0]=e+a*(r[0]-e),t[1]=u+a*(r[1]-u),t[2]=o+a*(r[2]-o),t[3]=i+a*(r[3]-i),t},e.random=function(t,n){return n=n||1,t[0]=a.RANDOM(),t[1]=a.RANDOM(),t[2]=a.RANDOM(),t[3]=a.RANDOM(),e.normalize(t,t),e.scale(t,t,n),t},e.transformMat4=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3];return t[0]=r[0]*a+r[4]*e+r[8]*u+r[12]*o,t[1]=r[1]*a+r[5]*e+r[9]*u+r[13]*o,t[2]=r[2]*a+r[6]*e+r[10]*u+r[14]*o,t[3]=r[3]*a+r[7]*e+r[11]*u+r[15]*o,t},e.transformQuat=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=r[0],i=r[1],c=r[2],f=r[3],s=f*a+i*u-c*e,h=f*e+c*a-o*u,M=f*u+o*e-i*a,l=-o*a-i*e-c*u;return t[0]=s*f+l*-o+h*-c-M*-i,t[1]=h*f+l*-i+M*-o-s*-c,t[2]=M*f+l*-c+s*-i-h*-o,t[3]=n[3],t},e.forEach=function(){var t=e.create();return function(n,r,a,e,u,o){var i,c;for(r||(r=4),a||(a=0),c=e?Math.min(e*r+a,n.length):n.length,i=a;c>i;i+=r)t[0]=n[i],t[1]=n[i+1],t[2]=n[i+2],t[3]=n[i+3],u(t,t,o),n[i]=t[0],n[i+1]=t[1],n[i+2]=t[2],n[i+3]=t[3];return n}}(),e.str=function(t){return"vec4("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+")"},t.exports=e},function(t,n,r){var a=r(1),e={};e.create=function(){var t=new a.ARRAY_TYPE(2);return t[0]=0,t[1]=0,t},e.clone=function(t){var n=new a.ARRAY_TYPE(2);return n[0]=t[0],n[1]=t[1],n},e.fromValues=function(t,n){var r=new a.ARRAY_TYPE(2);return r[0]=t,r[1]=n,r},e.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t},e.set=function(t,n,r){return t[0]=n,t[1]=r,t},e.add=function(t,n,r){return t[0]=n[0]+r[0],t[1]=n[1]+r[1],t},e.subtract=function(t,n,r){return t[0]=n[0]-r[0],t[1]=n[1]-r[1],t},e.sub=e.subtract,e.multiply=function(t,n,r){return t[0]=n[0]*r[0],t[1]=n[1]*r[1],t},e.mul=e.multiply,e.divide=function(t,n,r){return t[0]=n[0]/r[0],t[1]=n[1]/r[1],t},e.div=e.divide,e.min=function(t,n,r){return t[0]=Math.min(n[0],r[0]),t[1]=Math.min(n[1],r[1]),t},e.max=function(t,n,r){return t[0]=Math.max(n[0],r[0]),t[1]=Math.max(n[1],r[1]),t},e.scale=function(t,n,r){return t[0]=n[0]*r,t[1]=n[1]*r,t},e.scaleAndAdd=function(t,n,r,a){return t[0]=n[0]+r[0]*a,t[1]=n[1]+r[1]*a,t},e.distance=function(t,n){var r=n[0]-t[0],a=n[1]-t[1];return Math.sqrt(r*r+a*a)},e.dist=e.distance,e.squaredDistance=function(t,n){var r=n[0]-t[0],a=n[1]-t[1];return r*r+a*a},e.sqrDist=e.squaredDistance,e.length=function(t){var n=t[0],r=t[1];return Math.sqrt(n*n+r*r)},e.len=e.length,e.squaredLength=function(t){var n=t[0],r=t[1];return n*n+r*r},e.sqrLen=e.squaredLength,e.negate=function(t,n){return t[0]=-n[0],t[1]=-n[1],t},e.inverse=function(t,n){return t[0]=1/n[0],t[1]=1/n[1],t},e.normalize=function(t,n){var r=n[0],a=n[1],e=r*r+a*a;return e>0&&(e=1/Math.sqrt(e),t[0]=n[0]*e,t[1]=n[1]*e),t},e.dot=function(t,n){return t[0]*n[0]+t[1]*n[1]},e.cross=function(t,n,r){var a=n[0]*r[1]-n[1]*r[0];return t[0]=t[1]=0,t[2]=a,t},e.lerp=function(t,n,r,a){var e=n[0],u=n[1];return t[0]=e+a*(r[0]-e),t[1]=u+a*(r[1]-u),t},e.random=function(t,n){n=n||1;var r=2*a.RANDOM()*Math.PI;return t[0]=Math.cos(r)*n,t[1]=Math.sin(r)*n,t},e.transformMat2=function(t,n,r){var a=n[0],e=n[1];return t[0]=r[0]*a+r[2]*e,t[1]=r[1]*a+r[3]*e,t},e.transformMat2d=function(t,n,r){var a=n[0],e=n[1];return t[0]=r[0]*a+r[2]*e+r[4],t[1]=r[1]*a+r[3]*e+r[5],t},e.transformMat3=function(t,n,r){var a=n[0],e=n[1];return t[0]=r[0]*a+r[3]*e+r[6],t[1]=r[1]*a+r[4]*e+r[7],t},e.transformMat4=function(t,n,r){var a=n[0],e=n[1];return t[0]=r[0]*a+r[4]*e+r[12],t[1]=r[1]*a+r[5]*e+r[13],t},e.forEach=function(){var t=e.create();return function(n,r,a,e,u,o){var i,c;for(r||(r=2),a||(a=0),c=e?Math.min(e*r+a,n.length):n.length,i=a;c>i;i+=r)t[0]=n[i],t[1]=n[i+1],u(t,t,o),n[i]=t[0],n[i+1]=t[1];return n}}(),e.str=function(t){return"vec2("+t[0]+", "+t[1]+")"},t.exports=e}])});'use strict';(function(global){if(tr.isNode){var glMatrixAbsPath=HTMLImportsLoader.hrefToAbsolutePath('/gl-matrix-min.js');var glMatrixModule=require(glMatrixAbsPath);for(var exportName in glMatrixModule){global[exportName]=glMatrixModule[exportName];}}})(this);'use strict';tr.exportTo('tr.b.math',function(){var PREFERRED_NUMBER_SERIES_MULTIPLIERS=[1,2,5,10];function approximately(x,y,delta){if(delta===undefined)delta=1e-9;return Math.abs(x-y)<delta;}
 function clamp(x,lo,hi){return Math.min(Math.max(x,lo),hi);}
 function lerp(percentage,lo,hi){var range=hi-lo;return lo+percentage*range;}
 function normalize(value,lo,hi){return(value-lo)/(hi-lo);}
 function deg2rad(deg){return(Math.PI*deg)/180.0;}
 function erf(x){var sign=(x>=0)?1:-1;x=Math.abs(x);var a1=0.254829592;var a2=-0.284496736;var a3=1.421413741;var a4=-1.453152027;var a5=1.061405429;var p=0.3275911;var t=1.0/(1.0+p*x);var y=1.0-(((((a5*t+a4)*t)+a3)*t+a2)*t+a1)*t*Math.exp(-x*x);return sign*y;}
-var tmp_vec2=vec2.create();var tmp_vec2b=vec2.create();var tmp_vec4=vec4.create();var tmp_mat2d=mat2d.create();vec2.createFromArray=function(arr){if(arr.length!=2)
-throw new Error('Should be length 2');var v=vec2.create();vec2.set(v,arr[0],arr[1]);return v;};vec2.createXY=function(x,y){var v=vec2.create();vec2.set(v,x,y);return v;};vec2.toString=function(a){return'['+a[0]+', '+a[1]+']';};vec2.addTwoScaledUnitVectors=function(out,u1,scale1,u2,scale2){vec2.scale(tmp_vec2,u1,scale1);vec2.scale(tmp_vec2b,u2,scale2);vec2.add(out,tmp_vec2,tmp_vec2b);};vec2.interpolatePiecewiseFunction=function(points,x){if(x<points[0][0])
-return points[0][1];for(var i=1;i<points.length;++i){if(x<points[i][0]){var percent=normalize(x,points[i-1][0],points[i][0]);return lerp(percent,points[i-1][1],points[i][1]);}}
-return points[points.length-1][1];};vec3.createXYZ=function(x,y,z){var v=vec3.create();vec3.set(v,x,y,z);return v;};vec3.toString=function(a){return'vec3('+a[0]+', '+a[1]+', '+a[2]+')';};mat2d.translateXY=function(out,x,y){vec2.set(tmp_vec2,x,y);mat2d.translate(out,out,tmp_vec2);};mat2d.scaleXY=function(out,x,y){vec2.set(tmp_vec2,x,y);mat2d.scale(out,out,tmp_vec2);};vec4.unitize=function(out,a){out[0]=a[0]/a[3];out[1]=a[1]/a[3];out[2]=a[2]/a[3];out[3]=1;return out;};vec2.copyFromVec4=function(out,a){vec4.unitize(tmp_vec4,a);vec2.copy(out,tmp_vec4);};return{approximately:approximately,clamp:clamp,lerp:lerp,normalize:normalize,deg2rad:deg2rad,erf:erf};});'use strict';tr.exportTo('tr.b',function(){function Range(){this.isEmpty_=true;this.min_=undefined;this.max_=undefined;}
-Range.prototype={__proto__:Object.prototype,reset:function(){this.isEmpty_=true;this.min_=undefined;this.max_=undefined;},get isEmpty(){return this.isEmpty_;},addRange:function(range){if(range.isEmpty)
-return;this.addValue(range.min);this.addValue(range.max);},addValue:function(value){if(this.isEmpty_){this.max_=value;this.min_=value;this.isEmpty_=false;return;}
-this.max_=Math.max(this.max_,value);this.min_=Math.min(this.min_,value);},set min(min){this.isEmpty_=false;this.min_=min;},get min(){if(this.isEmpty_)
-return undefined;return this.min_;},get max(){if(this.isEmpty_)
-return undefined;return this.max_;},set max(max){this.isEmpty_=false;this.max_=max;},get range(){if(this.isEmpty_)
-return undefined;return this.max_-this.min_;},get center(){return(this.min_+this.max_)*0.5;},get duration(){if(this.isEmpty_)
-return 0;return this.max_-this.min_;},normalize:function(x){return tr.b.normalize(x,this.min,this.max);},lerp:function(x){return tr.b.lerp(x,this.min,this.max);},equals:function(that){if(this.isEmpty&&that.isEmpty)
-return true;if(this.isEmpty!=that.isEmpty)
-return false;return(tr.b.approximately(this.min,that.min)&&tr.b.approximately(this.max,that.max));},containsExplicitRangeInclusive:function(min,max){if(this.isEmpty)
-return false;return this.min_<=min&&max<=this.max_;},containsExplicitRangeExclusive:function(min,max){if(this.isEmpty)
-return false;return this.min_<min&&max<this.max_;},intersectsExplicitRangeInclusive:function(min,max){if(this.isEmpty)
-return false;return this.min_<=max&&min<=this.max_;},intersectsExplicitRangeExclusive:function(min,max){if(this.isEmpty)
-return false;return this.min_<max&&min<this.max_;},containsRangeInclusive:function(range){if(range.isEmpty)
-return false;return this.containsExplicitRangeInclusive(range.min_,range.max_);},containsRangeExclusive:function(range){if(range.isEmpty)
-return false;return this.containsExplicitRangeExclusive(range.min_,range.max_);},intersectsRangeInclusive:function(range){if(range.isEmpty)
-return false;return this.intersectsExplicitRangeInclusive(range.min_,range.max_);},intersectsRangeExclusive:function(range){if(range.isEmpty)
-return false;return this.intersectsExplicitRangeExclusive(range.min_,range.max_);},findExplicitIntersectionDuration:function(min,max){var min=Math.max(this.min,min);var max=Math.min(this.max,max);if(max<min)
-return 0;return max-min;},findIntersection:function(range){if(this.isEmpty||range.isEmpty)
-return new Range();var min=Math.max(this.min,range.min);var max=Math.min(this.max,range.max);if(max<min)
-return new Range();return Range.fromExplicitRange(min,max);},toJSON:function(){if(this.isEmpty_)
-return{isEmpty:true};return{isEmpty:false,max:this.max,min:this.min};},filterArray:function(array,opt_keyFunc,opt_this){if(this.isEmpty_)
-return[];function binSearch(test){var i0=0;var i1=array.length;while(i0<i1){var i=Math.trunc((i0+i1)/2);if(test(i))
-i1=i;else
-i0=i+1;}
+var tmpVec2=vec2.create();var tmpVec2b=vec2.create();var tmpVec4=vec4.create();var tmpMat2d=mat2d.create();vec2.createFromArray=function(arr){if(arr.length!==2)throw new Error('Should be length 2');var v=vec2.create();vec2.set(v,arr[0],arr[1]);return v;};vec2.createXY=function(x,y){var v=vec2.create();vec2.set(v,x,y);return v;};vec2.toString=function(a){return'['+a[0]+', '+a[1]+']';};vec2.addTwoScaledUnitVectors=function(out,u1,scale1,u2,scale2){vec2.scale(tmpVec2,u1,scale1);vec2.scale(tmpVec2b,u2,scale2);vec2.add(out,tmpVec2,tmpVec2b);};vec2.interpolatePiecewiseFunction=function(points,x){if(x<points[0][0])return points[0][1];for(var i=1;i<points.length;++i){if(x<points[i][0]){var percent=normalize(x,points[i-1][0],points[i][0]);return lerp(percent,points[i-1][1],points[i][1]);}}
+return points[points.length-1][1];};vec3.createXYZ=function(x,y,z){var v=vec3.create();vec3.set(v,x,y,z);return v;};vec3.toString=function(a){return'vec3('+a[0]+', '+a[1]+', '+a[2]+')';};mat2d.translateXY=function(out,x,y){vec2.set(tmpVec2,x,y);mat2d.translate(out,out,tmpVec2);};mat2d.scaleXY=function(out,x,y){vec2.set(tmpVec2,x,y);mat2d.scale(out,out,tmpVec2);};vec4.unitize=function(out,a){out[0]=a[0]/a[3];out[1]=a[1]/a[3];out[2]=a[2]/a[3];out[3]=1;return out;};vec2.copyFromVec4=function(out,a){vec4.unitize(tmpVec4,a);vec2.copy(out,tmpVec4);};function logOrLog10(x,base){if(base===10)return Math.log10(x);return Math.log(x)/Math.log(base);}
+function lesserPower(x,opt_base){var base=opt_base||10;return Math.pow(base,Math.floor(logOrLog10(x,base)));}
+function greaterPower(x,opt_base){var base=opt_base||10;return Math.pow(base,Math.ceil(logOrLog10(x,base)));}
+function lesserWholeNumber(x){if(x===0)return 0;const pow10=(x<0)?-lesserPower(-x):lesserPower(x);return pow10*Math.floor(x/pow10);}
+function greaterWholeNumber(x){if(x===0)return 0;const pow10=(x<0)?-lesserPower(-x):lesserPower(x);return pow10*Math.ceil(x/pow10);}
+function preferredNumberLargerThanMin(min){var absMin=Math.abs(min);var conservativeGuess=tr.b.math.lesserPower(absMin);var minPreferedNumber=undefined;for(var multiplier of PREFERRED_NUMBER_SERIES_MULTIPLIERS){var tightenedGuess=conservativeGuess*multiplier;if(tightenedGuess>=absMin){minPreferedNumber=tightenedGuess;break;}}
+if(minPreferedNumber===undefined){throw new Error('Could not compute preferred number for '+min);}
+if(min<0)minPreferedNumber*=-1;return minPreferedNumber;}
+return{approximately,clamp,lerp,normalize,deg2rad,erf,lesserPower,greaterPower,lesserWholeNumber,greaterWholeNumber,preferredNumberLargerThanMin,};});'use strict';tr.exportTo('tr.b.math',function(){function Range(){this.isEmpty_=true;this.min_=undefined;this.max_=undefined;}
+Range.prototype={__proto__:Object.prototype,reset:function(){this.isEmpty_=true;this.min_=undefined;this.max_=undefined;},get isEmpty(){return this.isEmpty_;},addRange:function(range){if(range.isEmpty)return;this.addValue(range.min);this.addValue(range.max);},addValue:function(value){if(this.isEmpty_){this.max_=value;this.min_=value;this.isEmpty_=false;return;}
+this.max_=Math.max(this.max_,value);this.min_=Math.min(this.min_,value);},set min(min){this.isEmpty_=false;this.min_=min;},get min(){if(this.isEmpty_)return undefined;return this.min_;},get max(){if(this.isEmpty_)return undefined;return this.max_;},set max(max){this.isEmpty_=false;this.max_=max;},get range(){if(this.isEmpty_)return undefined;return this.max_-this.min_;},get center(){return(this.min_+this.max_)*0.5;},get duration(){if(this.isEmpty_)return 0;return this.max_-this.min_;},enclosingPowers(opt_base){if(this.isEmpty)return new Range();return Range.fromExplicitRange(tr.b.math.lesserPower(this.min_,opt_base),tr.b.math.greaterPower(this.max_,opt_base));},normalize:function(x){return tr.b.math.normalize(x,this.min,this.max);},lerp:function(x){return tr.b.math.lerp(x,this.min,this.max);},clamp:function(x){return tr.b.math.clamp(x,this.min,this.max);},equals:function(that){if(this.isEmpty&&that.isEmpty)return true;if(this.isEmpty!==that.isEmpty)return false;return(tr.b.math.approximately(this.min,that.min)&&tr.b.math.approximately(this.max,that.max));},containsExplicitRangeInclusive:function(min,max){if(this.isEmpty)return false;return this.min_<=min&&max<=this.max_;},containsExplicitRangeExclusive:function(min,max){if(this.isEmpty)return false;return this.min_<min&&max<this.max_;},intersectsExplicitRangeInclusive:function(min,max){if(this.isEmpty)return false;return this.min_<=max&&min<=this.max_;},intersectsExplicitRangeExclusive:function(min,max){if(this.isEmpty)return false;return this.min_<max&&min<this.max_;},containsRangeInclusive:function(range){if(range.isEmpty)return false;return this.containsExplicitRangeInclusive(range.min_,range.max_);},containsRangeExclusive:function(range){if(range.isEmpty)return false;return this.containsExplicitRangeExclusive(range.min_,range.max_);},intersectsRangeInclusive:function(range){if(range.isEmpty)return false;return this.intersectsExplicitRangeInclusive(range.min_,range.max_);},intersectsRangeExclusive:function(range){if(range.isEmpty)return false;return this.intersectsExplicitRangeExclusive(range.min_,range.max_);},findExplicitIntersectionDuration:function(min,max){var min=Math.max(this.min,min);var max=Math.min(this.max,max);if(max<min)return 0;return max-min;},findIntersection:function(range){if(this.isEmpty||range.isEmpty)return new Range();var min=Math.max(this.min,range.min);var max=Math.min(this.max,range.max);if(max<min)return new Range();return Range.fromExplicitRange(min,max);},toJSON:function(){if(this.isEmpty_)return{isEmpty:true};return{isEmpty:false,max:this.max,min:this.min};},filterArray:function(array,opt_keyFunc,opt_this){if(this.isEmpty_)return[];function binSearch(test){var i0=0;var i1=array.length;while(i0<i1){var i=Math.trunc((i0+i1)/2);if(test(i)){i1=i;}else{i0=i+1;}}
 return i1;}
 var keyFunc=opt_keyFunc||tr.b.identity;function getValue(index){return keyFunc.call(opt_this,array[index]);}
-var first=binSearch(function(i){return this.min_===undefined||this.min_<=getValue(i);}.bind(this));var last=binSearch(function(i){return this.max_!==undefined&&this.max_<getValue(i);}.bind(this));return array.slice(first,last);}};Range.fromDict=function(d){if(d.isEmpty===true){return new Range();}else if(d.isEmpty===false){var range=new Range();range.min=d.min;range.max=d.max;return range;}else{throw new Error('Not a range');}};Range.fromExplicitRange=function(min,max){var range=new Range();range.min=min;range.max=max;return range;};Range.compareByMinTimes=function(a,b){if(!a.isEmpty&&!b.isEmpty)
-return a.min_-b.min_;if(a.isEmpty&&!b.isEmpty)
-return-1;if(!a.isEmpty&&b.isEmpty)
-return 1;return 0;};Range.PERCENT_RANGE=Range.fromExplicitRange(0,1);Object.freeze(Range.PERCENT_RANGE);return{Range:Range};});'use strict';(function(exports){var rank={standard:function(array,key){array=array.sort(function(a,b){var x=a[key];var y=b[key];return((x<y)?-1:((x>y)?1:0));});for(var i=1;i<array.length+1;i++){array[i-1]['rank']=i;}
+var first=binSearch(function(i){return this.min_===undefined||this.min_<=getValue(i);}.bind(this));var last=binSearch(function(i){return this.max_!==undefined&&this.max_<getValue(i);}.bind(this));return array.slice(first,last);}};Range.fromDict=function(d){if(d.isEmpty===true)return new Range();if(d.isEmpty===false){var range=new Range();range.min=d.min;range.max=d.max;return range;}
+throw new Error('Not a range');};Range.fromExplicitRange=function(min,max){var range=new Range();range.min=min;range.max=max;return range;};Range.compareByMinTimes=function(a,b){if(!a.isEmpty&&!b.isEmpty)return a.min_-b.min_;if(a.isEmpty&&!b.isEmpty)return-1;if(!a.isEmpty&&b.isEmpty)return 1;return 0;};Range.PERCENT_RANGE=Range.fromExplicitRange(0,1);Object.freeze(Range.PERCENT_RANGE);return{Range,};});'use strict';(function(exports){var rank={standard:function(array,key){array=array.sort(function(a,b){var x=a[key];var y=b[key];return((x<y)?-1:((x>y)?1:0));});for(var i=1;i<array.length+1;i++){array[i-1]['rank']=i;}
 return array;},fractional:function(array,key){array=this.standard(array,key);var pos=0;while(pos<array.length){var sum=0;var i=0;for(i=0;array[pos+i+1]&&(array[pos+i][key]===array[pos+i+1][key]);i++){sum+=array[pos+i]['rank'];}
 sum+=array[pos+i]['rank'];var endPos=pos+i+1;for(pos;pos<endPos;pos++){array[pos]['rank']=sum/(i+1);}
 pos=endPos;}
@@ -3848,1588 +4256,1180 @@
 exports.test=function(x,y,alt,corr){alt=typeof alt!=='undefined'?alt:'two-sided';corr=typeof corr!=='undefined'?corr:true;var nx=x.length,ny=y.length,f=1,u,mu,std,z,p;u=statistic(x,y);if(corr){mu=(nx*ny/2)+0.5;}else{mu=nx*ny/2;}
 std=Math.sqrt(u.tcf*nx*ny*(nx+ny+1)/12);if(alt=='less'){z=(u.ux-mu)/std;}else if(alt=='greater'){z=(u.uy-mu)/std;}else if(alt=='two-sided'){z=Math.abs((u.big-mu)/std);}else{console.log('Unknown alternative argument');}
 if(alt=='two-sided'){f=2;}
-p=dnorm(-z,0,1)*f;return{U:u.small,p:p};}})(typeof exports==='undefined'?this['mannwhitneyu']={}:exports);'use strict';(function(global){if(tr.isNode){var mwuAbsPath=HTMLImportsLoader.hrefToAbsolutePath('/mannwhitneyu.js');var mwuModule=require(mwuAbsPath);for(var exportName in mwuModule){global[exportName]=mwuModule[exportName];}}})(this);'use strict';tr.exportTo('tr.b',function(){var identity=x=>x;var Statistics={};Statistics.divideIfPossibleOrZero=function(numerator,denominator){if(denominator===0)
-return 0;return numerator/denominator;};Statistics.sum=function(ary,opt_func,opt_this){var func=opt_func||identity;var ret=0;var i=0;for(var elt of ary)
-ret+=func.call(opt_this,elt,i++);return ret;};Statistics.mean=function(ary,opt_func,opt_this){var func=opt_func||identity;var sum=0;var i=0;for(var elt of ary)
-sum+=func.call(opt_this,elt,i++);if(i===0)
-return undefined;return sum/i;};Statistics.geometricMean=function(ary,opt_func,opt_this){var func=opt_func||identity;var i=0;var logsum=0;for(var elt of ary){var x=func.call(opt_this,elt,i++);if(x<=0)
-return 0;logsum+=Math.log(Math.abs(x));}
-if(i===0)
-return 1;return Math.exp(logsum/i);};Statistics.weightedMean=function(ary,weightCallback,opt_valueCallback,opt_this){var valueCallback=opt_valueCallback||identity;var numerator=0;var denominator=0;var i=-1;for(var elt of ary){i++;var value=valueCallback.call(opt_this,elt,i);if(value===undefined)
-continue;var weight=weightCallback.call(opt_this,elt,i,value);numerator+=weight*value;denominator+=weight;}
-if(denominator===0)
-return undefined;return numerator/denominator;};Statistics.variance=function(ary,opt_func,opt_this){if(ary.length===0)
-return undefined;if(ary.length===1)
-return 0;var func=opt_func||identity;var mean=Statistics.mean(ary,func,opt_this);var sumOfSquaredDistances=Statistics.sum(ary,function(d,i){var v=func.call(this,d,i)-mean;return v*v;},opt_this);return sumOfSquaredDistances/(ary.length-1);};Statistics.stddev=function(ary,opt_func,opt_this){if(ary.length==0)
-return undefined;return Math.sqrt(Statistics.variance(ary,opt_func,opt_this));};Statistics.max=function(ary,opt_func,opt_this){var func=opt_func||identity;var ret=-Infinity;var i=0;for(var elt of ary)
-ret=Math.max(ret,func.call(opt_this,elt,i++));return ret;};Statistics.min=function(ary,opt_func,opt_this){var func=opt_func||identity;var ret=Infinity;var i=0;for(var elt of ary)
-ret=Math.min(ret,func.call(opt_this,elt,i++));return ret;};Statistics.range=function(ary,opt_func,opt_this){var func=opt_func||identity;var ret=new tr.b.Range();var i=0;for(var elt of ary)
-ret.addValue(func.call(opt_this,elt,i++));return ret;};Statistics.percentile=function(ary,percent,opt_func,opt_this){if(!(percent>=0&&percent<=1))
-throw new Error('percent must be [0,1]');var func=opt_func||identity;var tmp=new Array(ary.length);var i=0;for(var elt of ary)
-tmp[i]=func.call(opt_this,elt,i++);tmp.sort((a,b)=>a-b);var idx=Math.floor((ary.length-1)*percent);return tmp[idx];};Statistics.normalizeSamples=function(samples){if(samples.length===0){return{normalized_samples:samples,scale:1.0};}
-samples=samples.slice().sort(function(a,b){return a-b;});var low=Math.min.apply(null,samples);var high=Math.max.apply(null,samples);var new_low=0.5/samples.length;var new_high=(samples.length-0.5)/samples.length;if(high-low===0.0){samples=Array.apply(null,new Array(samples.length)).map(function(){return 0.5;});return{normalized_samples:samples,scale:1.0};}
-var scale=(new_high-new_low)/(high-low);for(var i=0;i<samples.length;i++){samples[i]=(samples[i]-low)*scale+new_low;}
-return{normalized_samples:samples,scale:scale};};Statistics.discrepancy=function(samples,opt_location_count){if(samples.length===0)
-return 0.0;var max_local_discrepancy=0;var inv_sample_count=1.0/samples.length;var locations=[];var count_less=[];var count_less_equal=[];if(opt_location_count!==undefined){var sample_index=0;for(var i=0;i<opt_location_count;i++){var location=i/(opt_location_count-1);locations.push(location);while(sample_index<samples.length&&samples[sample_index]<location){sample_index+=1;}
-count_less.push(sample_index);while(sample_index<samples.length&&samples[sample_index]<=location){sample_index+=1;}
-count_less_equal.push(sample_index);}}else{if(samples[0]>0.0){locations.push(0.0);count_less.push(0);count_less_equal.push(0);}
-for(var i=0;i<samples.length;i++){locations.push(samples[i]);count_less.push(i);count_less_equal.push(i+1);}
-if(samples[-1]<1.0){locations.push(1.0);count_less.push(samples.length);count_less_equal.push(samples.length);}}
-var max_diff=0;var min_diff=0;for(var i=1;i<locations.length;i++){var length=locations[i]-locations[i-1];var count_closed=count_less_equal[i]-count_less[i-1];var count_open=count_less[i]-count_less_equal[i-1];var count_closed_increment=count_less_equal[i]-count_less_equal[i-1];var count_open_increment=count_less[i]-count_less[i-1];max_diff=Math.max(count_closed_increment*inv_sample_count-length+max_diff,count_closed*inv_sample_count-length);min_diff=Math.min(count_open_increment*inv_sample_count-length+min_diff,count_open*inv_sample_count-length);max_local_discrepancy=Math.max(max_diff,-min_diff,max_local_discrepancy);}
-return max_local_discrepancy;};Statistics.timestampsDiscrepancy=function(timestamps,opt_absolute,opt_location_count){if(timestamps.length===0)
-return 0.0;if(opt_absolute===undefined)
-opt_absolute=true;if(Array.isArray(timestamps[0])){var range_discrepancies=timestamps.map(function(r){return Statistics.timestampsDiscrepancy(r);});return Math.max.apply(null,range_discrepancies);}
-var s=Statistics.normalizeSamples(timestamps);var samples=s.normalized_samples;var sample_scale=s.scale;var discrepancy=Statistics.discrepancy(samples,opt_location_count);var inv_sample_count=1.0/samples.length;if(opt_absolute===true){discrepancy/=sample_scale;}else{discrepancy=tr.b.clamp((discrepancy-inv_sample_count)/(1.0-inv_sample_count),0.0,1.0);}
-return discrepancy;};Statistics.durationsDiscrepancy=function(durations,opt_absolute,opt_location_count){if(durations.length===0)
-return 0.0;var timestamps=durations.reduce(function(prev,curr,index,array){prev.push(prev[prev.length-1]+curr);return prev;},[0]);return Statistics.timestampsDiscrepancy(timestamps,opt_absolute,opt_location_count);};Statistics.uniformlySampleStream=function(samples,streamLength,newElement,numSamples){if(streamLength<=numSamples){if(samples.length>=streamLength)
-samples[streamLength-1]=newElement;else
-samples.push(newElement);return;}
-var probToKeep=numSamples/streamLength;if(Math.random()>probToKeep)
-return;var index=Math.floor(Math.random()*numSamples);samples[index]=newElement;};Statistics.mergeSampledStreams=function(samplesA,streamLengthA,samplesB,streamLengthB,numSamples){if(streamLengthB<numSamples){var nbElements=Math.min(streamLengthB,samplesB.length);for(var i=0;i<nbElements;++i){Statistics.uniformlySampleStream(samplesA,streamLengthA+i+1,samplesB[i],numSamples);}
+p=dnorm(-z,0,1)*f;return{U:u.small,p:p};}})(typeof exports==='undefined'?this['mannwhitneyu']={}:exports);'use strict';(function(global){if(tr.isNode){var mwuAbsPath=HTMLImportsLoader.hrefToAbsolutePath('/mannwhitneyu.js');var mwuModule=require(mwuAbsPath);for(var exportName in mwuModule){global[exportName]=mwuModule[exportName];}}})(this);'use strict';tr.exportTo('tr.b.math',function(){var identity=x=>x;var Statistics={};Statistics.divideIfPossibleOrZero=function(numerator,denominator){if(denominator===0)return 0;return numerator/denominator;};Statistics.sum=function(ary,opt_func,opt_this){var func=opt_func||identity;var ret=0;var i=0;for(var elt of ary){ret+=func.call(opt_this,elt,i++);}
+return ret;};Statistics.mean=function(ary,opt_func,opt_this){var func=opt_func||identity;var sum=0;var i=0;for(var elt of ary){sum+=func.call(opt_this,elt,i++);}
+if(i===0)return undefined;return sum/i;};Statistics.geometricMean=function(ary,opt_func,opt_this){var func=opt_func||identity;var i=0;var logsum=0;for(var elt of ary){var x=func.call(opt_this,elt,i++);if(x<=0)return 0;logsum+=Math.log(Math.abs(x));}
+if(i===0)return 1;return Math.exp(logsum/i);};Statistics.weightedMean=function(ary,weightCallback,opt_valueCallback,opt_this){var valueCallback=opt_valueCallback||identity;var numerator=0;var denominator=0;var i=-1;for(var elt of ary){i++;var value=valueCallback.call(opt_this,elt,i);if(value===undefined)continue;var weight=weightCallback.call(opt_this,elt,i,value);numerator+=weight*value;denominator+=weight;}
+if(denominator===0)return undefined;return numerator/denominator;};Statistics.variance=function(ary,opt_func,opt_this){if(ary.length===0)return undefined;if(ary.length===1)return 0;var func=opt_func||identity;var mean=Statistics.mean(ary,func,opt_this);var sumOfSquaredDistances=Statistics.sum(ary,function(d,i){var v=func.call(this,d,i)-mean;return v*v;},opt_this);return sumOfSquaredDistances/(ary.length-1);};Statistics.stddev=function(ary,opt_func,opt_this){if(ary.length===0)return undefined;return Math.sqrt(Statistics.variance(ary,opt_func,opt_this));};Statistics.max=function(ary,opt_func,opt_this){var func=opt_func||identity;var ret=-Infinity;var i=0;for(var elt of ary){ret=Math.max(ret,func.call(opt_this,elt,i++));}
+return ret;};Statistics.min=function(ary,opt_func,opt_this){var func=opt_func||identity;var ret=Infinity;var i=0;for(var elt of ary){ret=Math.min(ret,func.call(opt_this,elt,i++));}
+return ret;};Statistics.range=function(ary,opt_func,opt_this){var func=opt_func||identity;var ret=new tr.b.math.Range();var i=0;for(var elt of ary){ret.addValue(func.call(opt_this,elt,i++));}
+return ret;};Statistics.percentile=function(ary,percent,opt_func,opt_this){if(!(percent>=0&&percent<=1)){throw new Error('percent must be [0,1]');}
+var func=opt_func||identity;var tmp=new Array(ary.length);var i=0;for(var elt of ary){tmp[i]=func.call(opt_this,elt,i++);}
+tmp.sort((a,b)=>a-b);var idx=Math.floor((ary.length-1)*percent);return tmp[idx];};Statistics.normalizeSamples=function(samples){if(samples.length===0){return{normalized_samples:samples,scale:1.0};}
+samples=samples.slice().sort(function(a,b){return a-b;});var low=Math.min.apply(null,samples);var high=Math.max.apply(null,samples);var newLow=0.5/samples.length;var newHigh=(samples.length-0.5)/samples.length;if(high-low===0.0){samples=Array.apply(null,new Array(samples.length)).map(function(){return 0.5;});return{normalized_samples:samples,scale:1.0};}
+var scale=(newHigh-newLow)/(high-low);for(var i=0;i<samples.length;i++){samples[i]=(samples[i]-low)*scale+newLow;}
+return{normalized_samples:samples,scale:scale};};Statistics.discrepancy=function(samples,opt_locationCount){if(samples.length===0)return 0.0;var maxLocalDiscrepancy=0;var invSampleCount=1.0/samples.length;var locations=[];var countLess=[];var countLessEqual=[];if(opt_locationCount!==undefined){var sampleIndex=0;for(var i=0;i<opt_locationCount;i++){var location=i/(opt_locationCount-1);locations.push(location);while(sampleIndex<samples.length&&samples[sampleIndex]<location){sampleIndex+=1;}
+countLess.push(sampleIndex);while(sampleIndex<samples.length&&samples[sampleIndex]<=location){sampleIndex+=1;}
+countLessEqual.push(sampleIndex);}}else{if(samples[0]>0.0){locations.push(0.0);countLess.push(0);countLessEqual.push(0);}
+for(var i=0;i<samples.length;i++){locations.push(samples[i]);countLess.push(i);countLessEqual.push(i+1);}
+if(samples[-1]<1.0){locations.push(1.0);countLess.push(samples.length);countLessEqual.push(samples.length);}}
+var maxDiff=0;var minDiff=0;for(var i=1;i<locations.length;i++){var length=locations[i]-locations[i-1];var countClosed=countLessEqual[i]-countLess[i-1];var countOpen=countLess[i]-countLessEqual[i-1];var countClosedIncrement=countLessEqual[i]-countLessEqual[i-1];var countOpenIncrement=countLess[i]-countLess[i-1];maxDiff=Math.max(countClosedIncrement*invSampleCount-length+maxDiff,countClosed*invSampleCount-length);minDiff=Math.min(countOpenIncrement*invSampleCount-length+minDiff,countOpen*invSampleCount-length);maxLocalDiscrepancy=Math.max(maxDiff,-minDiff,maxLocalDiscrepancy);}
+return maxLocalDiscrepancy;};Statistics.timestampsDiscrepancy=function(timestamps,opt_absolute,opt_locationCount){if(timestamps.length===0)return 0.0;if(opt_absolute===undefined)opt_absolute=true;if(Array.isArray(timestamps[0])){var rangeDiscrepancies=timestamps.map(function(r){return Statistics.timestampsDiscrepancy(r);});return Math.max.apply(null,rangeDiscrepancies);}
+var s=Statistics.normalizeSamples(timestamps);var samples=s.normalized_samples;var sampleScale=s.scale;var discrepancy=Statistics.discrepancy(samples,opt_locationCount);var invSampleCount=1.0/samples.length;if(opt_absolute===true){discrepancy/=sampleScale;}else{discrepancy=tr.b.math.clamp((discrepancy-invSampleCount)/(1.0-invSampleCount),0.0,1.0);}
+return discrepancy;};Statistics.durationsDiscrepancy=function(durations,opt_absolute,opt_locationCount){if(durations.length===0)return 0.0;var timestamps=durations.reduce(function(prev,curr,index,array){prev.push(prev[prev.length-1]+curr);return prev;},[0]);return Statistics.timestampsDiscrepancy(timestamps,opt_absolute,opt_locationCount);};Statistics.uniformlySampleArray=function(samples,count){if(samples.length<=count){return samples;}
+while(samples.length>count){var i=parseInt(Math.random()*samples.length);samples.splice(i,1);}
+return samples;};Statistics.uniformlySampleStream=function(samples,streamLength,newElement,numSamples){if(streamLength<=numSamples){if(samples.length>=streamLength){samples[streamLength-1]=newElement;}else{samples.push(newElement);}
+return;}
+var probToKeep=numSamples/streamLength;if(Math.random()>probToKeep)return;var index=Math.floor(Math.random()*numSamples);samples[index]=newElement;};Statistics.mergeSampledStreams=function(samplesA,streamLengthA,samplesB,streamLengthB,numSamples){if(streamLengthB<numSamples){var nbElements=Math.min(streamLengthB,samplesB.length);for(var i=0;i<nbElements;++i){Statistics.uniformlySampleStream(samplesA,streamLengthA+i+1,samplesB[i],numSamples);}
 return;}
 if(streamLengthA<numSamples){var nbElements=Math.min(streamLengthA,samplesA.length);var tempSamples=samplesB.slice();for(var i=0;i<nbElements;++i){Statistics.uniformlySampleStream(tempSamples,streamLengthB+i+1,samplesA[i],numSamples);}
 for(var i=0;i<tempSamples.length;++i){samplesA[i]=tempSamples[i];}
 return;}
 var nbElements=Math.min(numSamples,samplesB.length);var probOfSwapping=streamLengthB/(streamLengthA+streamLengthB);for(var i=0;i<nbElements;++i){if(Math.random()<probOfSwapping){samplesA[i]=samplesB[i];}}};function Distribution(){}
-Distribution.prototype={computeDensity:function(x){throw Error('Not implemented');},computePercentile:function(x){throw Error('Not implemented');},computeComplementaryPercentile:function(x){return 1-this.computePercentile(x);},get mean(){throw Error('Not implemented');},get mode(){throw Error('Not implemented');},get median(){throw Error('Not implemented');},get standardDeviation(){throw Error('Not implemented');},get variance(){throw Error('Not implemented');}};Statistics.UniformDistribution=function(opt_range){if(!opt_range)
-opt_range=tr.b.Range.fromExplicitRange(0,1);this.range=opt_range;};Statistics.UniformDistribution.prototype={__proto__:Distribution.prototype,computeDensity:function(x){return 1/this.range.range;},computePercentile:function(x){return tr.b.normalize(x,this.range.min,this.range.max);},get mean(){return this.range.center;},get mode(){return undefined;},get median(){return this.mean;},get standardDeviation(){return Math.sqrt(this.variance);},get variance(){return Math.pow(this.range.range,2)/12;}};Statistics.NormalDistribution=function(opt_mean,opt_variance){this.mean_=opt_mean||0;this.variance_=opt_variance||1;this.standardDeviation_=Math.sqrt(this.variance_);};Statistics.NormalDistribution.prototype={__proto__:Distribution.prototype,computeDensity:function(x){var scale=(1.0/(this.standardDeviation*Math.sqrt(2.0*Math.PI)));var exponent=-Math.pow(x-this.mean,2)/(2.0*this.variance);return scale*Math.exp(exponent);},computePercentile:function(x){var standardizedX=((x-this.mean)/Math.sqrt(2.0*this.variance));return(1.0+tr.b.erf(standardizedX))/2.0;},get mean(){return this.mean_;},get median(){return this.mean;},get mode(){return this.mean;},get standardDeviation(){return this.standardDeviation_;},get variance(){return this.variance_;}};Statistics.LogNormalDistribution=function(opt_location,opt_shape){this.normalDistribution_=new Statistics.NormalDistribution(opt_location,Math.pow(opt_shape||1,2));};Statistics.LogNormalDistribution.prototype={__proto__:Statistics.NormalDistribution.prototype,computeDensity:function(x){return this.normalDistribution_.computeDensity(Math.log(x))/x;},computePercentile:function(x){return this.normalDistribution_.computePercentile(Math.log(x));},get mean(){return Math.exp(this.normalDistribution_.mean+
+Distribution.prototype={computeDensity:function(x){throw Error('Not implemented');},computePercentile:function(x){throw Error('Not implemented');},computeComplementaryPercentile:function(x){return 1-this.computePercentile(x);},get mean(){throw Error('Not implemented');},get mode(){throw Error('Not implemented');},get median(){throw Error('Not implemented');},get standardDeviation(){throw Error('Not implemented');},get variance(){throw Error('Not implemented');}};Statistics.UniformDistribution=function(opt_range){if(!opt_range)opt_range=tr.b.math.Range.fromExplicitRange(0,1);this.range=opt_range;};Statistics.UniformDistribution.prototype={__proto__:Distribution.prototype,computeDensity:function(x){return 1/this.range.range;},computePercentile:function(x){return tr.b.math.normalize(x,this.range.min,this.range.max);},get mean(){return this.range.center;},get mode(){return undefined;},get median(){return this.mean;},get standardDeviation(){return Math.sqrt(this.variance);},get variance(){return Math.pow(this.range.range,2)/12;}};Statistics.NormalDistribution=function(opt_mean,opt_variance){this.mean_=opt_mean||0;this.variance_=opt_variance||1;this.standardDeviation_=Math.sqrt(this.variance_);};Statistics.NormalDistribution.prototype={__proto__:Distribution.prototype,computeDensity:function(x){var scale=(1.0/(this.standardDeviation*Math.sqrt(2.0*Math.PI)));var exponent=-Math.pow(x-this.mean,2)/(2.0*this.variance);return scale*Math.exp(exponent);},computePercentile:function(x){var standardizedX=((x-this.mean)/Math.sqrt(2.0*this.variance));return(1.0+tr.b.math.erf(standardizedX))/2.0;},get mean(){return this.mean_;},get median(){return this.mean;},get mode(){return this.mean;},get standardDeviation(){return this.standardDeviation_;},get variance(){return this.variance_;}};Statistics.LogNormalDistribution=function(opt_location,opt_shape){this.normalDistribution_=new Statistics.NormalDistribution(opt_location,Math.pow(opt_shape||1,2));};Statistics.LogNormalDistribution.prototype={__proto__:Statistics.NormalDistribution.prototype,computeDensity:function(x){return this.normalDistribution_.computeDensity(Math.log(x))/x;},computePercentile:function(x){return this.normalDistribution_.computePercentile(Math.log(x));},get mean(){return Math.exp(this.normalDistribution_.mean+
 (this.normalDistribution_.variance/2));},get variance(){var nm=this.normalDistribution_.mean;var nv=this.normalDistribution_.variance;return(Math.exp(2*(nm+nv))-
 Math.exp(2*nm+nv));},get standardDeviation(){return Math.sqrt(this.variance);},get median(){return Math.exp(this.normalDistribution_.mean);},get mode(){return Math.exp(this.normalDistribution_.mean-
 this.normalDistribution_.variance);}};Statistics.LogNormalDistribution.fromMedianAndDiminishingReturns=function(median,diminishingReturns){diminishingReturns=Math.log(diminishingReturns/median);var shape=Math.sqrt(1-3*diminishingReturns-
-Math.sqrt(Math.pow(diminishingReturns-3,2)-8))/2;var location=Math.log(median);return new Statistics.LogNormalDistribution(location,shape);};Statistics.mwu=mannwhitneyu;return{Statistics:Statistics};});'use strict';var GREEK_SMALL_LETTER_MU=String.fromCharCode(956);tr.exportTo('tr.b',function(){var UnitScale={};function defineUnitScale(name,prefixes){if(UnitScale[name]!==undefined)
-throw new Error('Unit scale \''+name+'\' already exists');if(prefixes.AUTO!==undefined){throw new Error('\'AUTO\' unit prefix will be added automatically '+'for unit scale \''+name+'\'');}
-prefixes.AUTO=tr.b.dictionaryValues(prefixes);prefixes.AUTO.sort((a,b)=>a.value-b.value);UnitScale[name]=prefixes;}
-function convertUnit(value,fromPrefix,toPrefix){if(value===undefined)
-return undefined;return value*(fromPrefix.value/toPrefix.value);}
-defineUnitScale('Binary',{NONE:{value:Math.pow(1024,0),symbol:''},KIBI:{value:Math.pow(1024,1),symbol:'Ki'},MEBI:{value:Math.pow(1024,2),symbol:'Mi'},GIBI:{value:Math.pow(1024,3),symbol:'Gi'},TEBI:{value:Math.pow(1024,4),symbol:'Ti'}});defineUnitScale('Metric',{NANO:{value:1e-9,symbol:'n'},MICRO:{value:1e-6,symbol:GREEK_SMALL_LETTER_MU},MILLI:{value:1e-3,symbol:'m'},NONE:{value:1,symbol:''},KILO:{value:1e3,symbol:'k'},MEGA:{value:1e6,symbol:'M'},GIGA:{value:1e9,symbol:'G'}});return{UnitScale:UnitScale,convertUnit:convertUnit};});'use strict';tr.exportTo('tr.b',function(){var msDisplayMode={scale:1e-3,suffix:'ms',roundedLess:function(a,b){return Math.round(a*1000)<Math.round(b*1000);},formatSpec:{unit:'s',unitPrefix:tr.b.UnitScale.Metric.MILLI,minimumFractionDigits:3,}};var nsDisplayMode={scale:1e-9,suffix:'ns',roundedLess:function(a,b){return Math.round(a*1000000)<Math.round(b*1000000);},formatSpec:{unit:'s',unitPrefix:tr.b.UnitScale.Metric.NANO,maximumFractionDigits:0}};var TimeDisplayModes={ns:nsDisplayMode,ms:msDisplayMode};return{TimeDisplayModes:TimeDisplayModes};});'use strict';tr.exportTo('tr.b',function(){var TimeDisplayModes=tr.b.TimeDisplayModes;var PLUS_MINUS_SIGN=String.fromCharCode(177);function max(a,b){if(a===undefined)
-return b;if(b===undefined)
-return a;return a.scale>b.scale?a:b;}
-var ImprovementDirection={DONT_CARE:0,BIGGER_IS_BETTER:1,SMALLER_IS_BETTER:2};function Unit(unitName,jsonName,basePrefix,isDelta,improvementDirection,formatSpec){this.unitName=unitName;this.jsonName=jsonName;this.basePrefix=basePrefix;this.isDelta=isDelta;this.improvementDirection=improvementDirection;this.formatSpec_=formatSpec;this.baseUnit=undefined;this.correspondingDeltaUnit=undefined;}
-Unit.prototype={asJSON:function(){return this.jsonName;},format:function(value,opt_context){var context=opt_context||{};var formatSpec=this.formatSpec_;if(typeof formatSpec==='function')
-formatSpec=formatSpec();function resolveProperty(propertyName){if(propertyName in context)
-return context[propertyName];else if(propertyName in formatSpec)
-return formatSpec[propertyName];else
-return undefined;}
-var signString='';if(value<0){signString='-';value=-value;}else if(this.isDelta){signString=value===0?PLUS_MINUS_SIGN:'+';}
-var unitString='';if(formatSpec.unit){if(formatSpec.unitHasPrecedingSpace!==false)
-unitString+=' ';var unitPrefix=resolveProperty('unitPrefix');if(unitPrefix!==undefined){var selectedPrefix;if(unitPrefix instanceof Array){var i=0;while(i<unitPrefix.length-1&&value/unitPrefix[i+1].value>=1){i++;}
-selectedPrefix=unitPrefix[i];}else{selectedPrefix=unitPrefix;}
-unitString+=selectedPrefix.symbol||'';value=tr.b.convertUnit(value,this.basePrefix,selectedPrefix);}else{value=tr.b.convertUnit(value,this.basePrefix,tr.b.UnitScale.Metric.NONE);}
-unitString+=formatSpec.unit;}
-var minimumFractionDigits=resolveProperty('minimumFractionDigits');var maximumFractionDigits=resolveProperty('maximumFractionDigits');if(minimumFractionDigits>maximumFractionDigits){if('minimumFractionDigits'in context&&!('maximumFractionDigits'in context)){maximumFractionDigits=minimumFractionDigits;}else if('maximumFractionDigits'in context&&!('minimumFractionDigits'in context)){minimumFractionDigits=maximumFractionDigits;}}
-var numberString=value.toLocaleString(undefined,{minimumFractionDigits:minimumFractionDigits,maximumFractionDigits:maximumFractionDigits});return signString+numberString+unitString;}};Unit.reset=function(){Unit.currentTimeDisplayMode=TimeDisplayModes.ms;};Unit.timestampFromUs=function(us){return tr.b.convertUnit(us,tr.b.UnitScale.Metric.MICRO,tr.b.UnitScale.Metric.MILLI);};Object.defineProperty(Unit,'currentTimeDisplayMode',{get:function(){return Unit.currentTimeDisplayMode_;},set:function(value){if(Unit.currentTimeDisplayMode_===value)
-return;Unit.currentTimeDisplayMode_=value;Unit.dispatchEvent(new tr.b.Event('display-mode-changed'));}});Unit.didPreferredTimeDisplayUnitChange=function(){var largest=undefined;var els=tr.b.findDeepElementsMatching(document.body,'tr-v-ui-preferred-display-unit');els.forEach(function(el){largest=max(largest,el.preferredTimeDisplayMode);});Unit.currentDisplayUnit=largest===undefined?TimeDisplayModes.ms:largest;};Unit.byName={};Unit.byJSONName={};Unit.fromJSON=function(object){var u=Unit.byJSONName[object];if(u){return u;}
-throw new Error('Unrecognized unit');};Unit.define=function(params){var definedUnits=[];tr.b.iterItems(ImprovementDirection,function(_,improvementDirection){var regularUnit=Unit.defineUnitVariant_(params,false,improvementDirection);var deltaUnit=Unit.defineUnitVariant_(params,true,improvementDirection);regularUnit.correspondingDeltaUnit=deltaUnit;deltaUnit.correspondingDeltaUnit=deltaUnit;definedUnits.push(regularUnit,deltaUnit);});var baseUnit=Unit.byName[params.baseUnitName];definedUnits.forEach(u=>u.baseUnit=baseUnit);};Unit.defineUnitVariant_=function(params,isDelta,improvementDirection){var nameSuffix=isDelta?'Delta':'';switch(improvementDirection){case ImprovementDirection.DONT_CARE:break;case ImprovementDirection.BIGGER_IS_BETTER:nameSuffix+='_biggerIsBetter';break;case ImprovementDirection.SMALLER_IS_BETTER:nameSuffix+='_smallerIsBetter';break;default:throw new Error('Unknown improvement direction: '+improvementDirection);}
-var unitName=params.baseUnitName+nameSuffix;var jsonName=params.baseJsonName+nameSuffix;if(Unit.byName[unitName]!==undefined)
-throw new Error('Unit \''+unitName+'\' already exists');if(Unit.byJSONName[jsonName]!==undefined)
-throw new Error('JSON unit \''+jsonName+'\' alread exists');var basePrefix=params.basePrefix?params.basePrefix:tr.b.UnitScale.Metric.NONE;var unit=new Unit(unitName,jsonName,basePrefix,isDelta,improvementDirection,params.formatSpec);Unit.byName[unitName]=unit;Unit.byJSONName[jsonName]=unit;return unit;};tr.b.EventTarget.decorate(Unit);Unit.reset();Unit.define({baseUnitName:'timeDurationInMs',baseJsonName:'ms',basePrefix:tr.b.UnitScale.Metric.MILLI,formatSpec:function(){return Unit.currentTimeDisplayMode_.formatSpec;}});Unit.define({baseUnitName:'timeStampInMs',baseJsonName:'tsMs',basePrefix:tr.b.UnitScale.Metric.MILLI,formatSpec:function(){return Unit.currentTimeDisplayMode_.formatSpec;}});Unit.define({baseUnitName:'normalizedPercentage',baseJsonName:'n%',formatSpec:{unit:'%',unitPrefix:{value:0.01},unitHasPrecedingSpace:false,minimumFractionDigits:3,maximumFractionDigits:3}});Unit.define({baseUnitName:'sizeInBytes',baseJsonName:'sizeInBytes',formatSpec:{unit:'B',unitPrefix:tr.b.UnitScale.Binary.AUTO,minimumFractionDigits:1,maximumFractionDigits:1}});Unit.define({baseUnitName:'energyInJoules',baseJsonName:'J',formatSpec:{unit:'J',minimumFractionDigits:3}});Unit.define({baseUnitName:'powerInWatts',baseJsonName:'W',formatSpec:{unit:'W',minimumFractionDigits:3}});Unit.define({baseUnitName:'unitlessNumber',baseJsonName:'unitless',formatSpec:{minimumFractionDigits:3,maximumFractionDigits:3}});Unit.define({baseUnitName:'count',baseJsonName:'count',formatSpec:{minimumFractionDigits:0,maximumFractionDigits:0}});return{ImprovementDirection:ImprovementDirection,Unit:Unit};});'use strict';tr.exportTo('tr.c',function(){function Auditor(model){this.model_=model;}
-Auditor.prototype={__proto__:Object.prototype,get model(){return this.model_;},runAnnotate:function(){},installUserFriendlyCategoryDriverIfNeeded:function(){},runAudit:function(){}};var options=new tr.b.ExtensionRegistryOptions(tr.b.BASIC_REGISTRY_MODE);options.defaultMetadata={};options.mandatoryBaseClass=Auditor;tr.b.decorateExtensionRegistry(Auditor,options);return{Auditor:Auditor};});'use strict';tr.exportTo('tr.b',function(){function clamp01(value){return Math.max(0,Math.min(1,value));}
+Math.sqrt(Math.pow(diminishingReturns-3,2)-8))/2;var location=Math.log(median);return new Statistics.LogNormalDistribution(location,shape);};Statistics.DEFAULT_ALPHA=0.01;Statistics.MAX_SUGGESTED_SAMPLE_SIZE=20;Statistics.Significance={SIGNIFICANT:'REJECT',INSIGNIFICANT:'FAIL_TO_REJECT',NEED_MORE_DATA:'NEED_MORE_DATA',DONT_CARE:'DONT_CARE',};Statistics.mwu=function(a,b,opt_alpha,opt_reqSampleSize){var result=mannwhitneyu.test(a,b);var alpha=opt_alpha||Statistics.DEFAULT_ALPHA;if(result.p<alpha){result.significance=Statistics.Significance.SIGNIFICANT;}else if(opt_reqSampleSize&&(a.length<opt_reqSampleSize||b.length<opt_reqSampleSize)){result.significance=Statistics.Significance.NEED_MORE_DATA;}else{result.significance=Statistics.Significance.INSIGNIFICANT;}
+return result;};return{Statistics,};});'use strict';var GREEK_SMALL_LETTER_MU=String.fromCharCode(956);tr.exportTo('tr.b',function(){var SECONDS_IN_A_MINUTE=60;var SECONDS_IN_AN_HOUR=SECONDS_IN_A_MINUTE*60;var SECONDS_IN_A_DAY=SECONDS_IN_AN_HOUR*24;var SECONDS_IN_A_WEEK=SECONDS_IN_A_DAY*7;var SECONDS_IN_A_YEAR=SECONDS_IN_A_DAY*365.2422;var SECONDS_IN_A_MONTH=SECONDS_IN_A_YEAR/12;var UnitPrefixScale={};var UnitScale={};function defineUnitPrefixScale(name,prefixes){if(UnitPrefixScale[name]!==undefined){throw new Error('Unit prefix scale \''+name+'\' already exists');}
+if(prefixes.AUTO!==undefined){throw new Error('The \'AUTO\' unit prefix is not supported for unit'+'prefix scales and cannot be added to scale \''+name+'\'');}
+UnitPrefixScale[name]=prefixes;}
+UnitScale.defineUnitScale=function(name,unitScale){if(UnitScale[name]!==undefined){throw new Error('Unit scale \''+name+'\' already exists');}
+if(unitScale.AUTO!==undefined){throw new Error('\'AUTO\' unit scale will be added automatically '+'for unit scale \''+name+'\'');}
+unitScale.AUTO=tr.b.dictionaryValues(unitScale);unitScale.AUTO.sort((a,b)=>a.value-b.value);if(name)UnitScale[name]=unitScale;return unitScale;};UnitScale.defineUnitScaleFromPrefixScale=function(baseSymbol,baseName,prefixScale,opt_scaleName){if(baseSymbol===undefined){throw new Error('Cannot create UnitScale with undefined baseSymbol.');}
+if(!baseName){throw new Error('Cannot create UnitScale without a baseName.');}
+if(!prefixScale){throw new Error('Cannot create UnitScale without a prefix scale.');}
+var unitScale={};for(var curPrefix of Object.keys(prefixScale)){var curScale=prefixScale[curPrefix];if(curScale.symbol===undefined||!curScale.value){throw new Error(`Cannot convert PrefixScale with malformed prefix ${curScale}.`);}
+var name=curPrefix==='NONE'?baseName:`${curPrefix}_${baseName}`;unitScale[name]={value:curScale.value,symbol:curScale.symbol+baseSymbol,baseSymbol:baseSymbol};}
+return UnitScale.defineUnitScale(opt_scaleName,unitScale);};function convertUnit(value,fromScale,toScale){if(value===undefined)return undefined;var fromScaleBase=fromScale.baseSymbol;var toScaleBase=toScale.baseSymbol;if(fromScaleBase!==undefined&&toScaleBase!==undefined&&fromScaleBase!==toScaleBase){throw new Error('Cannot convert between units with different base symbols.');}
+return value*(fromScale.value/toScale.value);}
+defineUnitPrefixScale('BINARY',{NONE:{value:Math.pow(1024,0),symbol:''},KIBI:{value:Math.pow(1024,1),symbol:'Ki'},MEBI:{value:Math.pow(1024,2),symbol:'Mi'},GIBI:{value:Math.pow(1024,3),symbol:'Gi'},TEBI:{value:Math.pow(1024,4),symbol:'Ti'}});defineUnitPrefixScale('METRIC',{NANO:{value:1e-9,symbol:'n'},MICRO:{value:1e-6,symbol:GREEK_SMALL_LETTER_MU},MILLI:{value:1e-3,symbol:'m'},NONE:{value:1,symbol:''},KILO:{value:1e3,symbol:'k'},MEGA:{value:1e6,symbol:'M'},GIGA:{value:1e9,symbol:'G'}});UnitScale.defineUnitScale('TIME',{NANO_SEC:{value:1e-9,symbol:'ns',baseSymbol:'s'},MICRO_SEC:{value:1e-6,symbol:GREEK_SMALL_LETTER_MU+'s',baseSymbol:'s'},MILLI_SEC:{value:1e-3,symbol:'ms',baseSymbol:'s'},SEC:{value:1,symbol:'s',baseSymbol:'s'},MINUTE:{value:SECONDS_IN_A_MINUTE,symbol:'min',baseSymbol:'s'},HOUR:{value:SECONDS_IN_AN_HOUR,symbol:'hr',baseSymbol:'s'},DAY:{value:SECONDS_IN_A_DAY,symbol:'days',baseSymbol:'s'},WEEK:{value:SECONDS_IN_A_WEEK,symbol:'weeks',baseSymbol:'s'},MONTH:{value:SECONDS_IN_A_MONTH,symbol:'months',baseSymbol:'s'},YEAR:{value:SECONDS_IN_A_YEAR,symbol:'years',baseSymbol:'s'}});UnitScale.defineUnitScaleFromPrefixScale('B','BYTE',UnitPrefixScale.BINARY,'MEMORY');return{UnitPrefixScale,UnitScale,convertUnit,};});'use strict';tr.exportTo('tr.b',function(){var msDisplayMode={scale:1e-3,suffix:'ms',roundedLess:function(a,b){return Math.round(a*1000)<Math.round(b*1000);},formatSpec:{unitScale:[tr.b.UnitScale.TIME.MILLI_SEC],minimumFractionDigits:3,}};var nsDisplayMode={scale:1e-9,suffix:'ns',roundedLess:function(a,b){return Math.round(a*1000000)<Math.round(b*1000000);},formatSpec:{unitScale:[tr.b.UnitScale.TIME.NANO_SEC],maximumFractionDigits:0}};var TimeDisplayModes={ns:nsDisplayMode,ms:msDisplayMode};return{TimeDisplayModes,};});'use strict';tr.exportTo('tr.b',function(){function _iterateElementDeeplyImpl(element,cb,thisArg,includeElement){if(includeElement&&cb.call(thisArg,element))return true;if(element.root&&element.root!==element&&_iterateElementDeeplyImpl(element.root,cb,thisArg,false)){return true;}
+var children=Polymer.dom(element).children;for(var i=0;i<children.length;i++){if(_iterateElementDeeplyImpl(children[i],cb,thisArg,true)){return true;}}
+return false;}
+function iterateElementDeeply(element,cb,thisArg){_iterateElementDeeplyImpl(element,cb,thisArg,false);}
+function findDeepElementMatchingPredicate(element,predicate){var foundElement=undefined;function matches(element){var match=predicate(element);if(!match){return false;}
+foundElement=element;return true;}
+iterateElementDeeply(element,matches);return foundElement;}
+function findDeepElementsMatchingPredicate(element,predicate){var foundElements=[];function matches(element){var match=predicate(element);if(match){foundElements.push(element);}
+return false;}
+iterateElementDeeply(element,matches);return foundElements;}
+function findDeepElementMatching(element,selector){return findDeepElementMatchingPredicate(element,function(element){return element.matches(selector);});}
+function findDeepElementsMatching(element,selector){return findDeepElementsMatchingPredicate(element,function(element){return element.matches(selector);});}
+function findDeepElementWithTextContent(element,re){return findDeepElementMatchingPredicate(element,function(element){if(element.children.length!==0)return false;return re.test(Polymer.dom(element).textContent);});}
+return{iterateElementDeeply,findDeepElementMatching,findDeepElementsMatching,findDeepElementMatchingPredicate,findDeepElementsMatchingPredicate,findDeepElementWithTextContent,};});'use strict';tr.exportTo('tr.b',function(){var TimeDisplayModes=tr.b.TimeDisplayModes;var PLUS_MINUS_SIGN=String.fromCharCode(177);var CACHED_FORMATTERS={};function getNumberFormatter(minSpec,maxSpec,minCtx,maxCtx){var key=minSpec+'-'+maxSpec+'-'+minCtx+'-'+maxCtx;var formatter=CACHED_FORMATTERS[key];if(formatter===undefined){var minimumFractionDigits=minCtx!==undefined?minCtx:minSpec;var maximumFractionDigits=maxCtx!==undefined?maxCtx:maxSpec;if(minimumFractionDigits>maximumFractionDigits){if(minCtx!==undefined&&maxCtx===undefined){maximumFractionDigits=minimumFractionDigits;}else if(minCtx===undefined&&maxCtx!==undefined){minimumFractionDigits=maximumFractionDigits;}}
+formatter=new Intl.NumberFormat(undefined,{minimumFractionDigits,maximumFractionDigits,});CACHED_FORMATTERS[key]=formatter;}
+return formatter;}
+function max(a,b){if(a===undefined)return b;if(b===undefined)return a;return a.scale>b.scale?a:b;}
+var ImprovementDirection={DONT_CARE:0,BIGGER_IS_BETTER:1,SMALLER_IS_BETTER:2};function Unit(unitName,jsonName,scaleBaseUnit,isDelta,improvementDirection,formatSpec){this.unitName=unitName;this.jsonName=jsonName;this.scaleBaseUnit=scaleBaseUnit;this.isDelta=isDelta;this.improvementDirection=improvementDirection;this.formatSpec_=formatSpec;this.baseUnit=undefined;this.correspondingDeltaUnit=undefined;}
+Unit.prototype={asJSON:function(){return this.jsonName;},getUnitScale_:function(opt_context){var formatSpec=this.formatSpec_;var formatSpecWasFunction=false;if(typeof formatSpec==='function'){formatSpecWasFunction=true;formatSpec=formatSpec();}
+var context=opt_context||{};var scale=undefined;if(context.unitScale){scale=context.unitScale;}else if(context.unitPrefix){var symbol=formatSpec.baseSymbol?formatSpec.baseSymbol:this.scaleBaseUnit.baseSymbol;scale=tr.b.UnitScale.defineUnitScaleFromPrefixScale(symbol,symbol,[context.unitPrefix]).AUTO;}else{scale=formatSpec.unitScale;if(!scale){scale=[{value:1,symbol:formatSpec.baseSymbol||'',baseSymbol:formatSpec.baseSymbol||''}];if(!formatSpecWasFunction)formatSpec.unitScale=scale;}}
+if(!(scale instanceof Array)){throw new Error('Unit has a malformed unit scale.');}
+return scale;},get unitString(){var scale=this.getUnitScale_();if(!scale){throw new Error('A UnitScale could not be found for Unit '+this.unitName);}
+return scale[0].baseSymbol||scale[0].symbol;},format:function(value,opt_context){var signString='';if(value<0){signString='-';value=-value;}else if(this.isDelta){signString=value===0?PLUS_MINUS_SIGN:'+';}
+var context=opt_context||{};var scale=this.getUnitScale_(context);var deltaValue=context.deltaValue===undefined?value:context.deltaValue;deltaValue=Math.abs(deltaValue)*this.scaleBaseUnit.value;var i=0;while(i<scale.length-1&&deltaValue/scale[i+1].value>=1){i++;}
+var selectedSubUnit=scale[i];var formatSpec=this.formatSpec_;if(typeof formatSpec==='function')formatSpec=formatSpec();var unitString='';if(selectedSubUnit.symbol){if(!formatSpec.avoidSpacePrecedingUnit)unitString=' ';unitString+=selectedSubUnit.symbol;}
+value=tr.b.convertUnit(value,this.scaleBaseUnit,selectedSubUnit);var numberString=getNumberFormatter(formatSpec.minimumFractionDigits,formatSpec.maximumFractionDigits,context.minimumFractionDigits,context.maximumFractionDigits).format(value);return signString+numberString+unitString;}};Unit.reset=function(){Unit.currentTimeDisplayMode=TimeDisplayModes.ms;};Unit.timestampFromUs=function(us){return tr.b.convertUnit(us,tr.b.UnitPrefixScale.METRIC.MICRO,tr.b.UnitPrefixScale.METRIC.MILLI);};Object.defineProperty(Unit,'currentTimeDisplayMode',{get:function(){return Unit.currentTimeDisplayMode_;},set:function(value){if(Unit.currentTimeDisplayMode_===value)return;Unit.currentTimeDisplayMode_=value;Unit.dispatchEvent(new tr.b.Event('display-mode-changed'));}});Unit.didPreferredTimeDisplayUnitChange=function(){var largest=undefined;var els=tr.b.findDeepElementsMatching(document.body,'tr-v-ui-preferred-display-unit');els.forEach(function(el){largest=max(largest,el.preferredTimeDisplayMode);});Unit.currentTimeDisplayMode=largest===undefined?TimeDisplayModes.ms:largest;};Unit.byName={};Unit.byJSONName={};Unit.fromJSON=function(object){var u=Unit.byJSONName[object];if(u){return u;}
+throw new Error('Unrecognized unit');};Unit.define=function(params){var definedUnits=[];for(var improvementDirection of Object.values(ImprovementDirection)){var regularUnit=Unit.defineUnitVariant_(params,false,improvementDirection);var deltaUnit=Unit.defineUnitVariant_(params,true,improvementDirection);regularUnit.correspondingDeltaUnit=deltaUnit;deltaUnit.correspondingDeltaUnit=deltaUnit;definedUnits.push(regularUnit,deltaUnit);}
+var baseUnit=Unit.byName[params.baseUnitName];definedUnits.forEach(u=>u.baseUnit=baseUnit);};Unit.nameSuffixForImprovementDirection=function(improvementDirection){switch(improvementDirection){case ImprovementDirection.DONT_CARE:return'';case ImprovementDirection.BIGGER_IS_BETTER:return'_biggerIsBetter';case ImprovementDirection.SMALLER_IS_BETTER:return'_smallerIsBetter';default:throw new Error('Unknown improvement direction: '+improvementDirection);}};Unit.defineUnitVariant_=function(params,isDelta,improvementDirection){var nameSuffix=isDelta?'Delta':'';nameSuffix+=Unit.nameSuffixForImprovementDirection(improvementDirection);var unitName=params.baseUnitName+nameSuffix;var jsonName=params.baseJsonName+nameSuffix;if(Unit.byName[unitName]!==undefined){throw new Error('Unit \''+unitName+'\' already exists');}
+if(Unit.byJSONName[jsonName]!==undefined){throw new Error('JSON unit \''+jsonName+'\' alread exists');}
+var scaleBaseUnit=params.scaleBaseUnit;if(!scaleBaseUnit){var formatSpec=params.formatSpec;if(typeof formatSpec==='function')formatSpec=formatSpec();var baseSymbol=formatSpec.unitScale?formatSpec.unitScale[0].baseSymbol:(formatSpec.baseSymbol||'');scaleBaseUnit={value:1,symbol:baseSymbol,baseSymbol:baseSymbol};}
+var unit=new Unit(unitName,jsonName,scaleBaseUnit,isDelta,improvementDirection,params.formatSpec);Unit.byName[unitName]=unit;Unit.byJSONName[jsonName]=unit;return unit;};tr.b.EventTarget.decorate(Unit);Unit.reset();Unit.define({baseUnitName:'timeInMsAutoFormat',baseJsonName:'msBestFitFormat',scaleBaseUnit:tr.b.UnitScale.TIME.MILLI_SEC,formatSpec:{unitScale:tr.b.UnitScale.TIME.AUTO,minimumFractionDigits:0,maximumFractionDigits:3}});Unit.define({baseUnitName:'timeDurationInMs',baseJsonName:'ms',scaleBaseUnit:tr.b.UnitScale.TIME.MILLI_SEC,formatSpec:function(){return Unit.currentTimeDisplayMode_.formatSpec;}});Unit.define({baseUnitName:'timeStampInMs',baseJsonName:'tsMs',scaleBaseUnit:tr.b.UnitScale.TIME.MILLI_SEC,formatSpec:function(){return Unit.currentTimeDisplayMode_.formatSpec;}});Unit.define({baseUnitName:'normalizedPercentage',baseJsonName:'n%',formatSpec:{unitScale:[{value:0.01,symbol:'%'}],avoidSpacePrecedingUnit:true,minimumFractionDigits:3,maximumFractionDigits:3}});Unit.define({baseUnitName:'sizeInBytes',baseJsonName:'sizeInBytes',formatSpec:{unitScale:tr.b.UnitScale.MEMORY.AUTO,minimumFractionDigits:1,maximumFractionDigits:1}});Unit.define({baseUnitName:'energyInJoules',baseJsonName:'J',formatSpec:{baseSymbol:'J',minimumFractionDigits:3}});Unit.define({baseUnitName:'powerInWatts',baseJsonName:'W',formatSpec:{baseSymbol:'W',minimumFractionDigits:3}});Unit.define({baseUnitName:'unitlessNumber',baseJsonName:'unitless',formatSpec:{minimumFractionDigits:3,maximumFractionDigits:3}});Unit.define({baseUnitName:'count',baseJsonName:'count',formatSpec:{minimumFractionDigits:0,maximumFractionDigits:0}});Unit.define({baseUnitName:'sigma',baseJsonName:'sigma',formatSpec:{baseSymbol:String.fromCharCode(963),minimumFractionDigits:1,maximumFractionDigits:1}});return{ImprovementDirection,Unit,};});'use strict';tr.exportTo('tr.b',function(){class Scalar{constructor(unit,value){if(!(unit instanceof tr.b.Unit)){throw new Error('Expected Unit');}
+if(!(typeof(value)==='number')){throw new Error('Expected value to be number');}
+this.unit=unit;this.value=value;}
+asDict(){return{unit:this.unit.asJSON(),value:tr.b.numberToJson(this.value),};}
+toString(){return this.unit.format(this.value);}
+static fromDict(d){return new Scalar(tr.b.Unit.fromJSON(d.unit),tr.b.numberFromJson(d.value));}}
+return{Scalar,};});'use strict';tr.exportTo('tr.c',function(){function Auditor(model){this.model_=model;}
+Auditor.prototype={__proto__:Object.prototype,get model(){return this.model_;},runAnnotate:function(){},installUserFriendlyCategoryDriverIfNeeded:function(){},runAudit:function(){}};var options=new tr.b.ExtensionRegistryOptions(tr.b.BASIC_REGISTRY_MODE);options.defaultMetadata={};options.mandatoryBaseClass=Auditor;tr.b.decorateExtensionRegistry(Auditor,options);return{Auditor,};});'use strict';tr.exportTo('tr.b',function(){function clamp01(value){return Math.max(0,Math.min(1,value));}
 function Color(opt_r,opt_g,opt_b,opt_a){this.r=Math.floor(opt_r)||0;this.g=Math.floor(opt_g)||0;this.b=Math.floor(opt_b)||0;this.a=opt_a;}
-Color.fromString=function(str){var tmp;var values;if(str.substr(0,4)=='rgb('){tmp=str.substr(4,str.length-5);values=tmp.split(',').map(function(v){return v.replace(/^\s+/,'','g');});if(values.length!=3)
-throw new Error('Malformatted rgb-expression');return new Color(parseInt(values[0]),parseInt(values[1]),parseInt(values[2]));}else if(str.substr(0,5)=='rgba('){tmp=str.substr(5,str.length-6);values=tmp.split(',').map(function(v){return v.replace(/^\s+/,'','g');});if(values.length!=4)
-throw new Error('Malformatted rgb-expression');return new Color(parseInt(values[0]),parseInt(values[1]),parseInt(values[2]),parseFloat(values[3]));}else if(str[0]=='#'&&str.length==7){return new Color(parseInt(str.substr(1,2),16),parseInt(str.substr(3,2),16),parseInt(str.substr(5,2),16));}else{throw new Error('Unrecognized string format.');}};Color.lerp=function(a,b,percent){if(a.a!==undefined&&b.a!==undefined)
-return Color.lerpRGBA(a,b,percent);return Color.lerpRGB(a,b,percent);};Color.lerpRGB=function(a,b,percent){return new Color(((b.r-a.r)*percent)+a.r,((b.g-a.g)*percent)+a.g,((b.b-a.b)*percent)+a.b);};Color.lerpRGBA=function(a,b,percent){return new Color(((b.r-a.r)*percent)+a.r,((b.g-a.g)*percent)+a.g,((b.b-a.b)*percent)+a.b,((b.a-a.a)*percent)+a.a);};Color.fromDict=function(dict){return new Color(dict.r,dict.g,dict.b,dict.a);};Color.fromHSLExplicit=function(h,s,l,a){var r,g,b;function hue2rgb(p,q,t){if(t<0)t+=1;if(t>1)t-=1;if(t<1/6)return p+(q-p)*6*t;if(t<1/2)return q;if(t<2/3)return p+(q-p)*(2/3-t)*6;return p;}
+Color.fromString=function(str){var tmp;var values;if(str.substr(0,4)==='rgb('){tmp=str.substr(4,str.length-5);values=tmp.split(',').map(function(v){return v.replace(/^\s+/,'','g');});if(values.length!==3){throw new Error('Malformatted rgb-expression');}
+return new Color(parseInt(values[0]),parseInt(values[1]),parseInt(values[2]));}
+if(str.substr(0,5)==='rgba('){tmp=str.substr(5,str.length-6);values=tmp.split(',').map(function(v){return v.replace(/^\s+/,'','g');});if(values.length!==4){throw new Error('Malformatted rgb-expression');}
+return new Color(parseInt(values[0]),parseInt(values[1]),parseInt(values[2]),parseFloat(values[3]));}
+if(str[0]==='#'&&str.length===7){return new Color(parseInt(str.substr(1,2),16),parseInt(str.substr(3,2),16),parseInt(str.substr(5,2),16));}
+throw new Error('Unrecognized string format.');};Color.lerp=function(a,b,percent){if(a.a!==undefined&&b.a!==undefined){return Color.lerpRGBA(a,b,percent);}
+return Color.lerpRGB(a,b,percent);};Color.lerpRGB=function(a,b,percent){return new Color(((b.r-a.r)*percent)+a.r,((b.g-a.g)*percent)+a.g,((b.b-a.b)*percent)+a.b);};Color.lerpRGBA=function(a,b,percent){return new Color(((b.r-a.r)*percent)+a.r,((b.g-a.g)*percent)+a.g,((b.b-a.b)*percent)+a.b,((b.a-a.a)*percent)+a.a);};Color.fromDict=function(dict){return new Color(dict.r,dict.g,dict.b,dict.a);};Color.fromHSLExplicit=function(h,s,l,a){var r;var g;var b;function hue2rgb(p,q,t){if(t<0)t+=1;if(t>1)t-=1;if(t<1/6)return p+(q-p)*6*t;if(t<1/2)return q;if(t<2/3)return p+(q-p)*(2/3-t)*6;return p;}
 if(s===0){r=g=b=l;}else{var q=l<0.5?l*(1+s):l+s-l*s;var p=2*l-q;r=hue2rgb(p,q,h+1/3);g=hue2rgb(p,q,h);b=hue2rgb(p,q,h-1/3);}
-return new Color(Math.floor(r*255),Math.floor(g*255),Math.floor(b*255),a);}
-Color.fromHSL=function(hsl){return Color.fromHSLExplicit(hsl.h,hsl.s,hsl.l,hsl.a);}
-Color.prototype={clone:function(){var c=new Color();c.r=this.r;c.g=this.g;c.b=this.b;c.a=this.a;return c;},blendOver:function(bgColor){var oneMinusThisAlpha=1-this.a;var outA=this.a+bgColor.a*oneMinusThisAlpha;var bgBlend=(bgColor.a*oneMinusThisAlpha)/bgColor.a;return new Color(this.r*this.a+bgColor.r*bgBlend,this.g*this.a+bgColor.g*bgBlend,this.b*this.a+bgColor.b*bgBlend,outA);},brighten:function(opt_k){var k;k=opt_k||0.45;return new Color(Math.min(255,this.r+Math.floor(this.r*k)),Math.min(255,this.g+Math.floor(this.g*k)),Math.min(255,this.b+Math.floor(this.b*k)),this.a);},lighten:function(k,opt_max_l){var max_l=opt_max_l!==undefined?opt_max_l:1.0;var hsl=this.toHSL();hsl.l=clamp01(hsl.l+k);return Color.fromHSL(hsl);},darken:function(opt_k){var k;if(opt_k!==undefined)
-k=opt_k;else
-k=0.45;return new Color(Math.min(255,this.r-Math.floor(this.r*k)),Math.min(255,this.g-Math.floor(this.g*k)),Math.min(255,this.b-Math.floor(this.b*k)),this.a);},desaturate:function(opt_desaturateFactor){var desaturateFactor;if(opt_desaturateFactor!==undefined)
-desaturateFactor=opt_desaturateFactor;else
-desaturateFactor=1;var hsl=this.toHSL();hsl.s=clamp01(hsl.s*(1-desaturateFactor));return Color.fromHSL(hsl);},withAlpha:function(a){return new Color(this.r,this.g,this.b,a);},toString:function(){if(this.a!==undefined){return'rgba('+
+return new Color(Math.floor(r*255),Math.floor(g*255),Math.floor(b*255),a);};Color.fromHSL=function(hsl){return Color.fromHSLExplicit(hsl.h,hsl.s,hsl.l,hsl.a);};Color.prototype={clone:function(){var c=new Color();c.r=this.r;c.g=this.g;c.b=this.b;c.a=this.a;return c;},blendOver:function(bgColor){var oneMinusThisAlpha=1-this.a;var outA=this.a+bgColor.a*oneMinusThisAlpha;var bgBlend=(bgColor.a*oneMinusThisAlpha)/bgColor.a;return new Color(this.r*this.a+bgColor.r*bgBlend,this.g*this.a+bgColor.g*bgBlend,this.b*this.a+bgColor.b*bgBlend,outA);},brighten:function(opt_k){var k;k=opt_k||0.45;return new Color(Math.min(255,this.r+Math.floor(this.r*k)),Math.min(255,this.g+Math.floor(this.g*k)),Math.min(255,this.b+Math.floor(this.b*k)),this.a);},lighten:function(k,opt_maxL){var maxL=opt_maxL!==undefined?opt_maxL:1.0;var hsl=this.toHSL();hsl.l=Math.min(hsl.l+k,maxL);return Color.fromHSL(hsl);},darken:function(opt_k){var k;if(opt_k!==undefined){k=opt_k;}else{k=0.45;}
+return new Color(Math.min(255,this.r-Math.floor(this.r*k)),Math.min(255,this.g-Math.floor(this.g*k)),Math.min(255,this.b-Math.floor(this.b*k)),this.a);},desaturate:function(opt_desaturateFactor){var desaturateFactor;if(opt_desaturateFactor!==undefined){desaturateFactor=opt_desaturateFactor;}else{desaturateFactor=1;}
+var hsl=this.toHSL();hsl.s=clamp01(hsl.s*(1-desaturateFactor));return Color.fromHSL(hsl);},withAlpha:function(a){return new Color(this.r,this.g,this.b,a);},toString:function(){if(this.a!==undefined){return'rgba('+
 this.r+','+this.g+','+
 this.b+','+this.a+')';}
-return'rgb('+this.r+','+this.g+','+this.b+')';},toHSL:function(){var r=this.r/255;var g=this.g/255;var b=this.b/255;var max=Math.max(r,g,b);var min=Math.min(r,g,b);var h,s;var l=(max+min)/2;if(min===max){h=0;s=0;}else{var delta=max-min;if(l>0.5)
-s=delta/(2-max-min);else
-s=delta/(max+min);if(r===max){h=(g-b)/delta;if(g<b)
-h+=6;}else if(g===max){h=2+((b-r)/delta);}else{h=4+((r-g)/delta);}
+return'rgb('+this.r+','+this.g+','+this.b+')';},toHSL:function(){var r=this.r/255;var g=this.g/255;var b=this.b/255;var max=Math.max(r,g,b);var min=Math.min(r,g,b);var h;var s;var l=(max+min)/2;if(min===max){h=0;s=0;}else{var delta=max-min;if(l>0.5){s=delta/(2-max-min);}else{s=delta/(max+min);}
+if(r===max){h=(g-b)/delta;if(g<b)h+=6;}else if(g===max){h=2+((b-r)/delta);}else{h=4+((r-g)/delta);}
 h/=6;}
 return{h:h,s:s,l:l,a:this.a};},toStringWithAlphaOverride:function(alpha){return'rgba('+
 this.r+','+this.g+','+
-this.b+','+alpha+')';}};return{Color:Color};});'use strict';tr.exportTo('tr.b',function(){var generalPurposeColors=[new tr.b.Color(122,98,135),new tr.b.Color(150,83,105),new tr.b.Color(44,56,189),new tr.b.Color(99,86,147),new tr.b.Color(104,129,107),new tr.b.Color(130,178,55),new tr.b.Color(87,109,147),new tr.b.Color(111,145,88),new tr.b.Color(81,152,131),new tr.b.Color(142,91,111),new tr.b.Color(81,163,70),new tr.b.Color(148,94,86),new tr.b.Color(144,89,118),new tr.b.Color(83,150,97),new tr.b.Color(105,94,139),new tr.b.Color(89,144,122),new tr.b.Color(105,119,128),new tr.b.Color(96,128,137),new tr.b.Color(145,88,145),new tr.b.Color(88,145,144),new tr.b.Color(90,100,143),new tr.b.Color(121,97,136),new tr.b.Color(111,160,73),new tr.b.Color(112,91,142),new tr.b.Color(86,147,86),new tr.b.Color(63,100,170),new tr.b.Color(81,152,107),new tr.b.Color(60,164,173),new tr.b.Color(143,72,161),new tr.b.Color(159,74,86)];var reservedColorsByName={thread_state_uninterruptible:new tr.b.Color(182,125,143),thread_state_iowait:new tr.b.Color(255,140,0),thread_state_running:new tr.b.Color(126,200,148),thread_state_runnable:new tr.b.Color(133,160,210),thread_state_sleeping:new tr.b.Color(240,240,240),thread_state_unknown:new tr.b.Color(199,155,125),background_memory_dump:new tr.b.Color(0,180,180),light_memory_dump:new tr.b.Color(0,0,180),detailed_memory_dump:new tr.b.Color(180,0,180),generic_work:new tr.b.Color(125,125,125),good:new tr.b.Color(0,125,0),bad:new tr.b.Color(180,125,0),terrible:new tr.b.Color(180,0,0),black:new tr.b.Color(0,0,0),rail_response:new tr.b.Color(67,135,253),rail_animation:new tr.b.Color(244,74,63),rail_idle:new tr.b.Color(238,142,0),rail_load:new tr.b.Color(13,168,97),startup:new tr.b.Color(230,230,0),used_memory_column:new tr.b.Color(0,0,255),older_used_memory_column:new tr.b.Color(153,204,255),tracing_memory_column:new tr.b.Color(153,153,153),heap_dump_stack_frame:new tr.b.Color(128,128,128),heap_dump_object_type:new tr.b.Color(0,0,255),cq_build_running:new tr.b.Color(255,255,119),cq_build_passed:new tr.b.Color(153,238,102),cq_build_failed:new tr.b.Color(238,136,136),cq_build_abandoned:new tr.b.Color(187,187,187),cq_build_attempt_runnig:new tr.b.Color(222,222,75),cq_build_attempt_passed:new tr.b.Color(103,218,35),cq_build_attempt_failed:new tr.b.Color(197,81,81)};var numGeneralPurposeColorIds=generalPurposeColors.length;var numReservedColorIds=tr.b.dictionaryLength(reservedColorsByName);var numColorsPerVariant=numGeneralPurposeColorIds+numReservedColorIds;function ColorScheme(){}
-var paletteBase=[];paletteBase.push.apply(paletteBase,generalPurposeColors);paletteBase.push.apply(paletteBase,tr.b.dictionaryValues(reservedColorsByName));ColorScheme.colors=[];ColorScheme.properties={};ColorScheme.properties={numColorsPerVariant:numColorsPerVariant};function pushVariant(func){var variantColors=paletteBase.map(func);ColorScheme.colors.push.apply(ColorScheme.colors,variantColors);}
-pushVariant(function(c){return c;});ColorScheme.properties.brightenedOffsets=[];ColorScheme.properties.brightenedOffsets.push(ColorScheme.colors.length);pushVariant(function(c){return c.lighten(0.3,0.9);});ColorScheme.properties.brightenedOffsets.push(ColorScheme.colors.length);pushVariant(function(c){return c.lighten(0.48,0.9);});ColorScheme.properties.brightenedOffsets.push(ColorScheme.colors.length);pushVariant(function(c){return c.lighten(0.65,0.9);});ColorScheme.properties.dimmedOffsets=[];ColorScheme.properties.dimmedOffsets.push(ColorScheme.colors.length);pushVariant(function(c){return c.desaturate();});ColorScheme.properties.dimmedOffsets.push(ColorScheme.colors.length);pushVariant(function(c){return c.desaturate(0.5);});ColorScheme.properties.dimmedOffsets.push(ColorScheme.colors.length);pushVariant(function(c){return c.desaturate(0.3);});ColorScheme.colorsAsStrings=ColorScheme.colors.map(function(c){return c.toString();});var reservedColorNameToIdMap=(function(){var m=new Map();var i=generalPurposeColors.length;tr.b.iterItems(reservedColorsByName,function(key,value){m.set(key,i++);});return m;})();ColorScheme.getColorIdForReservedName=function(name){var id=reservedColorNameToIdMap.get(name);if(id===undefined)
-throw new Error('Unrecognized color ')+name;return id;};ColorScheme.getColorForReservedNameAsString=function(reservedName){var id=ColorScheme.getColorIdForReservedName(reservedName);return ColorScheme.colorsAsStrings[id];};ColorScheme.getStringHash=function(name){var hash=0;for(var i=0;i<name.length;++i)
-hash=(hash+37*hash+11*name.charCodeAt(i))%0xFFFFFFFF;return hash;};var stringColorIdCache=new Map();ColorScheme.getColorIdForGeneralPurposeString=function(string){if(stringColorIdCache.get(string)===undefined){var hash=ColorScheme.getStringHash(string);stringColorIdCache.set(string,hash%numGeneralPurposeColorIds);}
-return stringColorIdCache.get(string);};return{ColorScheme:ColorScheme};});'use strict';tr.exportTo('tr.model',function(){var ColorScheme=tr.b.ColorScheme;function EventInfo(title,description,docLinks){this.title=title;this.description=description;this.docLinks=docLinks;this.colorId=ColorScheme.getColorIdForGeneralPurposeString(title);}
-return{EventInfo:EventInfo};});'use strict';tr.exportTo('tr.b',function(){var nextGUID=1;var UUID4_PATTERN='xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';var GUID={allocateSimple:function(){return nextGUID++;},getLastSimpleGuid:function(){return nextGUID-1;},allocateUUID4:function(){return UUID4_PATTERN.replace(/[xy]/g,function(c){var r=parseInt(Math.random()*16);if(c==='y')
-r=(r&3)+8;return r.toString(16);});}};return{GUID:GUID};});'use strict';tr.exportTo('tr.model',function(){function EventRegistry(){}
-var options=new tr.b.ExtensionRegistryOptions(tr.b.BASIC_REGISTRY_MODE);tr.b.decorateExtensionRegistry(EventRegistry,options);EventRegistry.addEventListener('will-register',function(e){var metadata=e.typeInfo.metadata;if(metadata.name===undefined)
-throw new Error('Registered events must provide name metadata');if(metadata.pluralName===undefined)
-throw new Error('Registered events must provide pluralName metadata');if(metadata.subTypes===undefined){metadata.subTypes={};var options=new tr.b.ExtensionRegistryOptions(tr.b.TYPE_BASED_REGISTRY_MODE);options.mandatoryBaseClass=e.typeInfo.constructor;options.defaultConstructor=e.typeInfo.constructor;tr.b.decorateExtensionRegistry(metadata.subTypes,options);}else{if(!metadata.subTypes.register)
-throw new Error('metadata.subTypes must be an extension registry.');}
+this.b+','+alpha+')';}};return{Color,};});'use strict';tr.exportTo('tr.b',function(){var generalPurposeColors=[new tr.b.Color(122,98,135),new tr.b.Color(150,83,105),new tr.b.Color(44,56,189),new tr.b.Color(99,86,147),new tr.b.Color(104,129,107),new tr.b.Color(130,178,55),new tr.b.Color(87,109,147),new tr.b.Color(111,145,88),new tr.b.Color(81,152,131),new tr.b.Color(142,91,111),new tr.b.Color(81,163,70),new tr.b.Color(148,94,86),new tr.b.Color(144,89,118),new tr.b.Color(83,150,97),new tr.b.Color(105,94,139),new tr.b.Color(89,144,122),new tr.b.Color(105,119,128),new tr.b.Color(96,128,137),new tr.b.Color(145,88,145),new tr.b.Color(88,145,144),new tr.b.Color(90,100,143),new tr.b.Color(121,97,136),new tr.b.Color(111,160,73),new tr.b.Color(112,91,142),new tr.b.Color(86,147,86),new tr.b.Color(63,100,170),new tr.b.Color(81,152,107),new tr.b.Color(60,164,173),new tr.b.Color(143,72,161),new tr.b.Color(159,74,86)];var reservedColorsByName={thread_state_uninterruptible:new tr.b.Color(182,125,143),thread_state_iowait:new tr.b.Color(255,140,0),thread_state_running:new tr.b.Color(126,200,148),thread_state_runnable:new tr.b.Color(133,160,210),thread_state_sleeping:new tr.b.Color(240,240,240),thread_state_unknown:new tr.b.Color(199,155,125),background_memory_dump:new tr.b.Color(0,180,180),light_memory_dump:new tr.b.Color(0,0,180),detailed_memory_dump:new tr.b.Color(180,0,180),vsync_highlight_color:new tr.b.Color(0,0,255),generic_work:new tr.b.Color(125,125,125),good:new tr.b.Color(0,125,0),bad:new tr.b.Color(180,125,0),terrible:new tr.b.Color(180,0,0),black:new tr.b.Color(0,0,0),grey:new tr.b.Color(221,221,221),white:new tr.b.Color(255,255,255),yellow:new tr.b.Color(255,255,0),olive:new tr.b.Color(100,100,0),rail_response:new tr.b.Color(67,135,253),rail_animation:new tr.b.Color(244,74,63),rail_idle:new tr.b.Color(238,142,0),rail_load:new tr.b.Color(13,168,97),startup:new tr.b.Color(230,230,0),heap_dump_stack_frame:new tr.b.Color(128,128,128),heap_dump_object_type:new tr.b.Color(0,0,255),heap_dump_child_node_arrow:new tr.b.Color(204,102,0),cq_build_running:new tr.b.Color(255,255,119),cq_build_passed:new tr.b.Color(153,238,102),cq_build_failed:new tr.b.Color(238,136,136),cq_build_abandoned:new tr.b.Color(187,187,187),cq_build_attempt_runnig:new tr.b.Color(222,222,75),cq_build_attempt_passed:new tr.b.Color(103,218,35),cq_build_attempt_failed:new tr.b.Color(197,81,81)};var numGeneralPurposeColorIds=generalPurposeColors.length;var numReservedColorIds=tr.b.dictionaryLength(reservedColorsByName);var numColorsPerVariant=numGeneralPurposeColorIds+numReservedColorIds;function ColorScheme(){}
+var paletteBase=[];paletteBase.push.apply(paletteBase,generalPurposeColors);paletteBase.push.apply(paletteBase,tr.b.dictionaryValues(reservedColorsByName));ColorScheme.colors=[];ColorScheme.properties={};ColorScheme.properties={numColorsPerVariant,};function pushVariant(func){var variantColors=paletteBase.map(func);ColorScheme.colors.push.apply(ColorScheme.colors,variantColors);}
+pushVariant(function(c){return c;});ColorScheme.properties.brightenedOffsets=[];ColorScheme.properties.brightenedOffsets.push(ColorScheme.colors.length);pushVariant(function(c){return c.lighten(0.3,0.8);});ColorScheme.properties.brightenedOffsets.push(ColorScheme.colors.length);pushVariant(function(c){return c.lighten(0.48,0.85);});ColorScheme.properties.brightenedOffsets.push(ColorScheme.colors.length);pushVariant(function(c){return c.lighten(0.65,0.9);});ColorScheme.properties.dimmedOffsets=[];ColorScheme.properties.dimmedOffsets.push(ColorScheme.colors.length);pushVariant(function(c){return c.desaturate();});ColorScheme.properties.dimmedOffsets.push(ColorScheme.colors.length);pushVariant(function(c){return c.desaturate(0.5);});ColorScheme.properties.dimmedOffsets.push(ColorScheme.colors.length);pushVariant(function(c){return c.desaturate(0.3);});ColorScheme.colorsAsStrings=ColorScheme.colors.map(function(c){return c.toString();});var reservedColorNameToIdMap=(function(){var m=new Map();var i=generalPurposeColors.length;for(var key of Object.keys(reservedColorsByName)){m.set(key,i++);}
+return m;})();ColorScheme.getColorIdForReservedName=function(name){var id=reservedColorNameToIdMap.get(name);if(id===undefined){throw new Error('Unrecognized color '+name);}
+return id;};ColorScheme.getColorForReservedNameAsString=function(reservedName){var id=ColorScheme.getColorIdForReservedName(reservedName);return ColorScheme.colorsAsStrings[id];};ColorScheme.getStringHash=function(name){var hash=0;for(var i=0;i<name.length;++i){hash=(hash+37*hash+11*name.charCodeAt(i))%0xFFFFFFFF;}
+return hash;};var stringColorIdCache=new Map();ColorScheme.getColorIdForGeneralPurposeString=function(string){if(stringColorIdCache.get(string)===undefined){var hash=ColorScheme.getStringHash(string);stringColorIdCache.set(string,hash%numGeneralPurposeColorIds);}
+return stringColorIdCache.get(string);};return{ColorScheme,};});'use strict';tr.exportTo('tr.model',function(){var ColorScheme=tr.b.ColorScheme;function EventInfo(title,description,docLinks){this.title=title;this.description=description;this.docLinks=docLinks;this.colorId=ColorScheme.getColorIdForGeneralPurposeString(title);}
+return{EventInfo,};});'use strict';tr.exportTo('tr.b',function(){var nextGUID=1;var UUID4_PATTERN='xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';var GUID={allocateSimple:function(){return nextGUID++;},getLastSimpleGuid:function(){return nextGUID-1;},allocateUUID4:function(){return UUID4_PATTERN.replace(/[xy]/g,function(c){var r=parseInt(Math.random()*16);if(c==='y')r=(r&3)+8;return r.toString(16);});}};return{GUID,};});'use strict';tr.exportTo('tr.model',function(){function EventRegistry(){}
+var options=new tr.b.ExtensionRegistryOptions(tr.b.BASIC_REGISTRY_MODE);tr.b.decorateExtensionRegistry(EventRegistry,options);EventRegistry.addEventListener('will-register',function(e){var metadata=e.typeInfo.metadata;if(metadata.name===undefined){throw new Error('Registered events must provide name metadata');}
+if(metadata.pluralName===undefined){throw new Error('Registered events must provide pluralName metadata');}
+if(metadata.subTypes===undefined){metadata.subTypes={};var options=new tr.b.ExtensionRegistryOptions(tr.b.TYPE_BASED_REGISTRY_MODE);options.mandatoryBaseClass=e.typeInfo.constructor;options.defaultConstructor=e.typeInfo.constructor;tr.b.decorateExtensionRegistry(metadata.subTypes,options);}else{if(!metadata.subTypes.register){throw new Error('metadata.subTypes must be an extension registry.');}}
 e.typeInfo.constructor.subTypes=metadata.subTypes;});var eventsByTypeName=undefined;EventRegistry.getEventTypeInfoByTypeName=function(typeName){if(eventsByTypeName===undefined){eventsByTypeName={};EventRegistry.getAllRegisteredTypeInfos().forEach(function(typeInfo){eventsByTypeName[typeInfo.metadata.name]=typeInfo;});}
 return eventsByTypeName[typeName];};EventRegistry.addEventListener('registry-changed',function(){eventsByTypeName=undefined;});function convertCamelCaseToTitleCase(name){var result=name.replace(/[A-Z]/g,' $&');result=result.charAt(0).toUpperCase()+result.slice(1);return result;}
-EventRegistry.getUserFriendlySingularName=function(typeName){var typeInfo=EventRegistry.getEventTypeInfoByTypeName(typeName);var str=typeInfo.metadata.name;return convertCamelCaseToTitleCase(str);};EventRegistry.getUserFriendlyPluralName=function(typeName){var typeInfo=EventRegistry.getEventTypeInfoByTypeName(typeName);var str=typeInfo.metadata.pluralName;return convertCamelCaseToTitleCase(str);};return{EventRegistry:EventRegistry};});'use strict';tr.exportTo('tr.model',function(){var EventRegistry=tr.model.EventRegistry;var RequestSelectionChangeEvent=tr.b.Event.bind(undefined,'requestSelectionChange',true,false);function EventSet(opt_events){this.bounds_=new tr.b.Range();this.events_=new Set();if(opt_events){if(opt_events instanceof Array){for(var event of opt_events)
-this.push(event);}else if(opt_events instanceof EventSet){this.addEventSet(opt_events);}else{this.push(opt_events);}}}
-EventSet.prototype={__proto__:Object.prototype,get bounds(){return this.bounds_;},get duration(){if(this.bounds_.isEmpty)
-return 0;return this.bounds_.max-this.bounds_.min;},get length(){return this.events_.size;},get guid(){return this.guid_;},*[Symbol.iterator](){for(var event of this.events_)
-yield event;},clear:function(){this.bounds_=new tr.b.Range();this.events_.clear();},push:function(event){if(event.guid==undefined)
-throw new Error('Event must have a GUID');if(!this.events_.has(event)){this.events_.add(event);if(event.addBoundsToRange)
-if(this.bounds_!==undefined)
-event.addBoundsToRange(this.bounds_);}
-return event;},contains:function(event){if(this.events_.has(event))
-return event;else
-return undefined;},addEventSet:function(eventSet){for(var event of eventSet)
-this.push(event);},intersectionIsEmpty:function(otherEventSet){return!this.some(event=>otherEventSet.contains(event));},equals:function(that){if(this.length!==that.length)
-return false;return this.every(event=>that.contains(event));},sortEvents:function(compare){var ary=this.toArray();ary.sort(compare);this.clear();for(var event of ary)
-this.push(event);},getEventsOrganizedByBaseType:function(opt_pruneEmpty){var allTypeInfos=EventRegistry.getAllRegisteredTypeInfos();var events=this.getEventsOrganizedByCallback(function(event){var maxEventIndex=-1;var maxEventTypeInfo=undefined;allTypeInfos.forEach(function(eventTypeInfo,eventIndex){if(!(event instanceof eventTypeInfo.constructor))
-return;if(eventIndex>maxEventIndex){maxEventIndex=eventIndex;maxEventTypeInfo=eventTypeInfo;}});if(maxEventIndex==-1){console.log(event);throw new Error('Unrecognized event type');}
-return maxEventTypeInfo.metadata.name;});if(!opt_pruneEmpty){allTypeInfos.forEach(function(eventTypeInfo){if(events[eventTypeInfo.metadata.name]===undefined)
-events[eventTypeInfo.metadata.name]=new EventSet();});}
-return events;},getEventsOrganizedByTitle:function(){return this.getEventsOrganizedByCallback(function(event){if(event.title===undefined)
-throw new Error('An event didn\'t have a title!');return event.title;});},getEventsOrganizedByCallback:function(cb,opt_this){var groupedEvents=tr.b.group(this,cb,opt_this||this);return tr.b.mapItems(groupedEvents,(_,events)=>new EventSet(events));},enumEventsOfType:function(type,func){for(var event of this)
-if(event instanceof type)
-func(event);},get userFriendlyName(){if(this.length===0){throw new Error('Empty event set');}
-var eventsByBaseType=this.getEventsOrganizedByBaseType(true);var eventTypeName=tr.b.dictionaryKeys(eventsByBaseType)[0];if(this.length===1){var tmp=EventRegistry.getUserFriendlySingularName(eventTypeName);return tr.b.getOnlyElement(this.events_).userFriendlyName;}
+EventRegistry.getUserFriendlySingularName=function(typeName){var typeInfo=EventRegistry.getEventTypeInfoByTypeName(typeName);var str=typeInfo.metadata.name;return convertCamelCaseToTitleCase(str);};EventRegistry.getUserFriendlyPluralName=function(typeName){var typeInfo=EventRegistry.getEventTypeInfoByTypeName(typeName);var str=typeInfo.metadata.pluralName;return convertCamelCaseToTitleCase(str);};return{EventRegistry,};});'use strict';tr.exportTo('tr.model',function(){var EventRegistry=tr.model.EventRegistry;var RequestSelectionChangeEvent=tr.b.Event.bind(undefined,'requestSelectionChange',true,false);function EventSet(opt_events){this.bounds_=new tr.b.math.Range();this.events_=new Set();this.guid_=tr.b.GUID.allocateSimple();if(opt_events){if(opt_events instanceof Array){for(var event of opt_events){this.push(event);}}else if(opt_events instanceof EventSet){this.addEventSet(opt_events);}else{this.push(opt_events);}}}
+EventSet.prototype={__proto__:Object.prototype,get bounds(){return this.bounds_;},get duration(){if(this.bounds_.isEmpty)return 0;return this.bounds_.max-this.bounds_.min;},get length(){return this.events_.size;},get guid(){return this.guid_;},*[Symbol.iterator](){for(var event of this.events_){yield event;}},clear:function(){this.bounds_=new tr.b.math.Range();this.events_.clear();},push:function(...events){var numPushed;for(var event of events){if(event.guid===undefined){throw new Error('Event must have a GUID');}
+if(!this.events_.has(event)){this.events_.add(event);if(event.addBoundsToRange){if(this.bounds_!==undefined){event.addBoundsToRange(this.bounds_);}}}
+numPushed++;}
+return numPushed;},contains:function(event){if(this.events_.has(event))return event;return undefined;},addEventSet:function(eventSet){for(var event of eventSet){this.push(event);}},intersectionIsEmpty:function(otherEventSet){return!this.some(event=>otherEventSet.contains(event));},equals:function(that){if(this.length!==that.length)return false;return this.every(event=>that.contains(event));},sortEvents:function(compare){var ary=this.toArray();ary.sort(compare);this.clear();for(var event of ary){this.push(event);}},getEventsOrganizedByBaseType:function(opt_pruneEmpty){var allTypeInfos=EventRegistry.getAllRegisteredTypeInfos();var events=this.getEventsOrganizedByCallback(function(event){var maxEventIndex=-1;var maxEventTypeInfo=undefined;allTypeInfos.forEach(function(eventTypeInfo,eventIndex){if(!(event instanceof eventTypeInfo.constructor))return;if(eventIndex>maxEventIndex){maxEventIndex=eventIndex;maxEventTypeInfo=eventTypeInfo;}});if(maxEventIndex===-1){throw new Error(`Unrecognized event type:${event.constructor.name}`);}
+return maxEventTypeInfo.metadata.name;});if(!opt_pruneEmpty){allTypeInfos.forEach(function(eventTypeInfo){if(events[eventTypeInfo.metadata.name]===undefined){events[eventTypeInfo.metadata.name]=new EventSet();}});}
+return events;},getEventsOrganizedByTitle:function(){return this.getEventsOrganizedByCallback(function(event){if(event.title===undefined){throw new Error('An event didn\'t have a title!');}
+return event.title;});},getEventsOrganizedByCallback:function(cb,opt_this){var groupedEvents=tr.b.group(this,cb,opt_this||this);return tr.b.mapItems(groupedEvents,(_,events)=>new EventSet(events));},enumEventsOfType:function(type,func){for(var event of this){if(event instanceof type){func(event);}}},get userFriendlyName(){if(this.length===0){throw new Error('Empty event set');}
+var eventsByBaseType=this.getEventsOrganizedByBaseType(true);var eventTypeName=Object.keys(eventsByBaseType)[0];if(this.length===1){var tmp=EventRegistry.getUserFriendlySingularName(eventTypeName);return tr.b.getOnlyElement(this.events_).userFriendlyName;}
 var numEventTypes=tr.b.dictionaryLength(eventsByBaseType);if(numEventTypes!==1){return this.length+' events of various types';}
-var tmp=EventRegistry.getUserFriendlyPluralName(eventTypeName);return this.length+' '+tmp;},filter:function(fn,opt_this){var res=new EventSet();for(var event of this)
-if(fn.call(opt_this,event))
-res.push(event);return res;},toArray:function(){var ary=[];for(var event of this)
-ary.push(event);return ary;},forEach:function(fn,opt_this){for(var event of this)
-fn.call(opt_this,event);},map:function(fn,opt_this){var res=[];for(var event of this)
-res.push(fn.call(opt_this,event));return res;},every:function(fn,opt_this){for(var event of this)
-if(!fn.call(opt_this,event))
-return false;return true;},some:function(fn,opt_this){for(var event of this)
-if(fn.call(opt_this,event))
-return true;return false;},asDict:function(){var stable_ids=[];for(var event of this)
-stable_ids.push(event.stableId);return{'events':stable_ids};},asSet:function(){return this.events_;}};EventSet.IMMUTABLE_EMPTY_SET=(function(){var s=new EventSet();s.push=function(){throw new Error('Cannot push to an immutable event set');};s.addEventSet=function(){throw new Error('Cannot add to an immutable event set');};Object.freeze(s);return s;})();return{EventSet:EventSet,RequestSelectionChangeEvent:RequestSelectionChangeEvent};});'use strict';tr.exportTo('tr.model',function(){var ColorScheme=tr.b.ColorScheme;var SelectionState={NONE:0,SELECTED:ColorScheme.properties.brightenedOffsets[0],HIGHLIGHTED:ColorScheme.properties.brightenedOffsets[1],DIMMED:ColorScheme.properties.dimmedOffsets[0],BRIGHTENED0:ColorScheme.properties.brightenedOffsets[0],BRIGHTENED1:ColorScheme.properties.brightenedOffsets[1],BRIGHTENED2:ColorScheme.properties.brightenedOffsets[2],DIMMED0:ColorScheme.properties.dimmedOffsets[0],DIMMED1:ColorScheme.properties.dimmedOffsets[1],DIMMED2:ColorScheme.properties.dimmedOffsets[2]};var brighteningLevels=[SelectionState.NONE,SelectionState.BRIGHTENED0,SelectionState.BRIGHTENED1,SelectionState.BRIGHTENED2];SelectionState.getFromBrighteningLevel=function(level){return brighteningLevels[level];}
-var dimmingLevels=[SelectionState.DIMMED0,SelectionState.DIMMED1,SelectionState.DIMMED2];SelectionState.getFromDimmingLevel=function(level){return dimmingLevels[level];}
-return{SelectionState:SelectionState};});'use strict';tr.exportTo('tr.model',function(){var SelectionState=tr.model.SelectionState;function SelectableItem(modelItem){this.modelItem_=modelItem;}
-SelectableItem.prototype={get modelItem(){return this.modelItem_;},get selected(){return this.selectionState===SelectionState.SELECTED;},addToSelection:function(selection){var modelItem=this.modelItem_;if(!modelItem)
-return;selection.push(modelItem);},addToTrackMap:function(eventToTrackMap,track){var modelItem=this.modelItem_;if(!modelItem)
-return;eventToTrackMap.addEvent(modelItem,track);}};return{SelectableItem:SelectableItem};});'use strict';tr.exportTo('tr.model',function(){var SelectableItem=tr.model.SelectableItem;var SelectionState=tr.model.SelectionState;var IMMUTABLE_EMPTY_SET=tr.model.EventSet.IMMUTABLE_EMPTY_SET;function Event(){SelectableItem.call(this,this);this.guid_=tr.b.GUID.allocateSimple();this.selectionState=SelectionState.NONE;this.info=undefined;}
-Event.prototype={__proto__:SelectableItem.prototype,get guid(){return this.guid_;},get stableId(){return undefined;},associatedAlerts:IMMUTABLE_EMPTY_SET,addAssociatedAlert:function(alert){if(this.associatedAlerts===IMMUTABLE_EMPTY_SET)
-this.associatedAlerts=new tr.model.EventSet();this.associatedAlerts.push(alert);},addBoundsToRange:function(range){}};return{Event:Event};});'use strict';tr.exportTo('tr.model',function(){function TimedEvent(start){tr.model.Event.call(this);this.start=start;this.duration=0;this.cpuStart=undefined;this.cpuDuration=undefined;this.contexts=Object.freeze([]);}
-TimedEvent.prototype={__proto__:tr.model.Event.prototype,get end(){return this.start+this.duration;},addBoundsToRange:function(range){range.addValue(this.start);range.addValue(this.end);},bounds:function(that,opt_precisionUnit){if(opt_precisionUnit===undefined)
-opt_precisionUnit=tr.b.TimeDisplayModes.ms;var startsBefore=opt_precisionUnit.roundedLess(that.start,this.start);var endsAfter=opt_precisionUnit.roundedLess(this.end,that.end);return!startsBefore&&!endsAfter;}};return{TimedEvent:TimedEvent};});'use strict';tr.exportTo('tr.model',function(){function Alert(info,start,opt_associatedEvents,opt_args){tr.model.TimedEvent.call(this,start);this.info=info;this.args=opt_args||{};this.associatedEvents=new tr.model.EventSet(opt_associatedEvents);this.associatedEvents.forEach(function(event){event.addAssociatedAlert(this);},this);}
+var tmp=EventRegistry.getUserFriendlyPluralName(eventTypeName);return this.length+' '+tmp;},filter:function(fn,opt_this){var res=new EventSet();for(var event of this){if(fn.call(opt_this,event)){res.push(event);}}
+return res;},toArray:function(){var ary=[];for(var event of this){ary.push(event);}
+return ary;},forEach:function(fn,opt_this){for(var event of this){fn.call(opt_this,event);}},map:function(fn,opt_this){var res=[];for(var event of this){res.push(fn.call(opt_this,event));}
+return res;},every:function(fn,opt_this){for(var event of this){if(!fn.call(opt_this,event)){return false;}}
+return true;},some:function(fn,opt_this){for(var event of this){if(fn.call(opt_this,event)){return true;}}
+return false;},asDict:function(){var stableIds=[];for(var event of this){stableIds.push(event.stableId);}
+return{'events':stableIds};},asSet:function(){return this.events_;}};EventSet.IMMUTABLE_EMPTY_SET=(function(){var s=new EventSet();s.push=function(){throw new Error('Cannot push to an immutable event set');};s.addEventSet=function(){throw new Error('Cannot add to an immutable event set');};Object.freeze(s);return s;})();return{EventSet,RequestSelectionChangeEvent,};});'use strict';tr.exportTo('tr.model',function(){var ColorScheme=tr.b.ColorScheme;var SelectionState={NONE:0,SELECTED:ColorScheme.properties.brightenedOffsets[0],HIGHLIGHTED:ColorScheme.properties.brightenedOffsets[1],DIMMED:ColorScheme.properties.dimmedOffsets[0],BRIGHTENED0:ColorScheme.properties.brightenedOffsets[0],BRIGHTENED1:ColorScheme.properties.brightenedOffsets[1],BRIGHTENED2:ColorScheme.properties.brightenedOffsets[2],DIMMED0:ColorScheme.properties.dimmedOffsets[0],DIMMED1:ColorScheme.properties.dimmedOffsets[1],DIMMED2:ColorScheme.properties.dimmedOffsets[2]};var brighteningLevels=[SelectionState.NONE,SelectionState.BRIGHTENED0,SelectionState.BRIGHTENED1,SelectionState.BRIGHTENED2];SelectionState.getFromBrighteningLevel=function(level){return brighteningLevels[level];};var dimmingLevels=[SelectionState.DIMMED0,SelectionState.DIMMED1,SelectionState.DIMMED2];SelectionState.getFromDimmingLevel=function(level){return dimmingLevels[level];};return{SelectionState,};});'use strict';tr.exportTo('tr.model',function(){var SelectionState=tr.model.SelectionState;function SelectableItem(modelItem){this.modelItem_=modelItem;}
+SelectableItem.prototype={get modelItem(){return this.modelItem_;},get selected(){return this.selectionState===SelectionState.SELECTED;},addToSelection:function(selection){var modelItem=this.modelItem_;if(!modelItem)return;selection.push(modelItem);},addToTrackMap:function(eventToTrackMap,track){var modelItem=this.modelItem_;if(!modelItem)return;eventToTrackMap.addEvent(modelItem,track);}};return{SelectableItem,};});'use strict';tr.exportTo('tr.model',function(){var SelectableItem=tr.model.SelectableItem;var SelectionState=tr.model.SelectionState;var IMMUTABLE_EMPTY_SET=tr.model.EventSet.IMMUTABLE_EMPTY_SET;function Event(){SelectableItem.call(this,this);this.guid_=tr.b.GUID.allocateSimple();this.selectionState=SelectionState.NONE;this.info=undefined;}
+Event.prototype={__proto__:SelectableItem.prototype,get guid(){return this.guid_;},get stableId(){return undefined;},get range(){var range=new tr.b.math.Range();this.addBoundsToRange(range);return range;},associatedAlerts:IMMUTABLE_EMPTY_SET,addAssociatedAlert:function(alert){if(this.associatedAlerts===IMMUTABLE_EMPTY_SET){this.associatedAlerts=new tr.model.EventSet();}
+this.associatedAlerts.push(alert);},addBoundsToRange:function(range){}};return{Event,};});'use strict';tr.exportTo('tr.model',function(){function TimedEvent(start){tr.model.Event.call(this);this.start=start;this.duration=0;this.cpuStart=undefined;this.cpuDuration=undefined;this.contexts=Object.freeze([]);}
+TimedEvent.prototype={__proto__:tr.model.Event.prototype,get end(){return this.start+this.duration;},addBoundsToRange:function(range){range.addValue(this.start);range.addValue(this.end);},bounds:function(that,opt_precisionUnit){if(opt_precisionUnit===undefined){opt_precisionUnit=tr.b.TimeDisplayModes.ms;}
+var startsBefore=opt_precisionUnit.roundedLess(that.start,this.start);var endsAfter=opt_precisionUnit.roundedLess(this.end,that.end);return!startsBefore&&!endsAfter;}};return{TimedEvent,};});'use strict';tr.exportTo('tr.model',function(){function Alert(info,start,opt_associatedEvents,opt_args){tr.model.TimedEvent.call(this,start);this.info=info;this.args=opt_args||{};this.associatedEvents=new tr.model.EventSet(opt_associatedEvents);this.associatedEvents.forEach(function(event){event.addAssociatedAlert(this);},this);}
 Alert.prototype={__proto__:tr.model.TimedEvent.prototype,get title(){return this.info.title;},get colorId(){return this.info.colorId;},get userFriendlyName(){return'Alert '+this.title+' at '+
-tr.b.Unit.byName.timeStampInMs.format(this.start);}};tr.model.EventRegistry.register(Alert,{name:'alert',pluralName:'alerts'});return{Alert:Alert};});'use strict';tr.exportTo('tr.model',function(){var ColorScheme=tr.b.ColorScheme;var Statistics=tr.b.Statistics;var FRAME_PERF_CLASS={GOOD:'good',BAD:'bad',TERRIBLE:'terrible',NEUTRAL:'generic_work'};function Frame(associatedEvents,threadTimeRanges,opt_args){tr.model.Event.call(this);this.threadTimeRanges=threadTimeRanges;this.associatedEvents=new tr.model.EventSet(associatedEvents);this.args=opt_args||{};this.title='Frame';this.start=Statistics.min(threadTimeRanges,function(x){return x.start;});this.end=Statistics.max(threadTimeRanges,function(x){return x.end;});this.totalDuration=Statistics.sum(threadTimeRanges,function(x){return x.end-x.start;});this.perfClass=FRAME_PERF_CLASS.NEUTRAL;};Frame.prototype={__proto__:tr.model.Event.prototype,set perfClass(perfClass){this.colorId=ColorScheme.getColorIdForReservedName(perfClass);this.perfClass_=perfClass;},get perfClass(){return this.perfClass_;},shiftTimestampsForward:function(amount){this.start+=amount;this.end+=amount;for(var i=0;i<this.threadTimeRanges.length;i++){this.threadTimeRanges[i].start+=amount;this.threadTimeRanges[i].end+=amount;}},addBoundsToRange:function(range){range.addValue(this.start);range.addValue(this.end);}};tr.model.EventRegistry.register(Frame,{name:'frame',pluralName:'frames'});return{Frame:Frame,FRAME_PERF_CLASS:FRAME_PERF_CLASS};});'use strict';tr.exportTo('tr.b',function(){function findLowIndexInSortedArray(ary,mapFn,loVal){if(ary.length==0)
-return 1;var low=0;var high=ary.length-1;var i,comparison;var hitPos=-1;while(low<=high){i=Math.floor((low+high)/2);comparison=mapFn(ary[i])-loVal;if(comparison<0){low=i+1;continue;}else if(comparison>0){high=i-1;continue;}else{hitPos=i;high=i-1;}}
-return hitPos!=-1?hitPos:low;}
-function findHighIndexInSortedArray(ary,mapFn,loVal,hiVal){var lo=loVal||0;var hi=hiVal!==undefined?hiVal:ary.length;while(lo<hi){var mid=(lo+hi)>>1;if(mapFn(ary[mid])>=0)
-lo=mid+1;else
-hi=mid;}
+tr.b.Unit.byName.timeStampInMs.format(this.start);}};tr.model.EventRegistry.register(Alert,{name:'alert',pluralName:'alerts'});return{Alert,};});'use strict';tr.exportTo('tr.model',function(){var ColorScheme=tr.b.ColorScheme;var Statistics=tr.b.math.Statistics;var FRAME_PERF_CLASS={GOOD:'good',BAD:'bad',TERRIBLE:'terrible',NEUTRAL:'generic_work'};function Frame(associatedEvents,threadTimeRanges,opt_args){tr.model.Event.call(this);this.threadTimeRanges=threadTimeRanges;this.associatedEvents=new tr.model.EventSet(associatedEvents);this.args=opt_args||{};this.title='Frame';this.start=Statistics.min(threadTimeRanges,function(x){return x.start;});this.end=Statistics.max(threadTimeRanges,function(x){return x.end;});this.totalDuration=Statistics.sum(threadTimeRanges,function(x){return x.end-x.start;});this.perfClass=FRAME_PERF_CLASS.NEUTRAL;}
+Frame.prototype={__proto__:tr.model.Event.prototype,set perfClass(perfClass){this.colorId=ColorScheme.getColorIdForReservedName(perfClass);this.perfClass_=perfClass;},get perfClass(){return this.perfClass_;},shiftTimestampsForward:function(amount){this.start+=amount;this.end+=amount;for(var i=0;i<this.threadTimeRanges.length;i++){this.threadTimeRanges[i].start+=amount;this.threadTimeRanges[i].end+=amount;}},addBoundsToRange:function(range){range.addValue(this.start);range.addValue(this.end);}};tr.model.EventRegistry.register(Frame,{name:'frame',pluralName:'frames'});return{Frame,FRAME_PERF_CLASS,};});'use strict';tr.exportTo('tr.b.math',function(){function findLowIndexInSortedArray(ary,mapFn,loVal){if(ary.length===0)return 1;var low=0;var high=ary.length-1;var i;var comparison;var hitPos=-1;while(low<=high){i=Math.floor((low+high)/2);comparison=mapFn(ary[i])-loVal;if(comparison<0){low=i+1;continue;}else if(comparison>0){high=i-1;continue;}else{hitPos=i;high=i-1;}}
+return hitPos!==-1?hitPos:low;}
+function findHighIndexInSortedArray(ary,mapFn,loVal,hiVal){var lo=loVal||0;var hi=hiVal!==undefined?hiVal:ary.length;while(lo<hi){var mid=(lo+hi)>>1;if(mapFn(ary[mid])>=0){lo=mid+1;}else{hi=mid;}}
 return hi;}
-function findIndexInSortedIntervals(ary,mapLoFn,mapWidthFn,loVal){var first=findLowIndexInSortedArray(ary,mapLoFn,loVal);if(first==0){if(loVal>=mapLoFn(ary[0])&&loVal<mapLoFn(ary[0])+mapWidthFn(ary[0],0)){return 0;}else{return-1;}}else if(first<ary.length){if(loVal>=mapLoFn(ary[first])&&loVal<mapLoFn(ary[first])+mapWidthFn(ary[first],first)){return first;}else if(loVal>=mapLoFn(ary[first-1])&&loVal<mapLoFn(ary[first-1])+
-mapWidthFn(ary[first-1],first-1)){return first-1;}else{return ary.length;}}else if(first==ary.length){if(loVal>=mapLoFn(ary[first-1])&&loVal<mapLoFn(ary[first-1])+
-mapWidthFn(ary[first-1],first-1)){return first-1;}else{return ary.length;}}else{return ary.length;}}
-function findIndexInSortedClosedIntervals(ary,mapLoFn,mapHiFn,val){var i=findLowIndexInSortedArray(ary,mapLoFn,val);if(i===0){if(val>=mapLoFn(ary[0],0)&&val<=mapHiFn(ary[0],0)){return 0;}else{return-1;}}else if(i<ary.length){if(val>=mapLoFn(ary[i-1],i-1)&&val<=mapHiFn(ary[i-1],i-1)){return i-1;}else if(val>=mapLoFn(ary[i],i)&&val<=mapHiFn(ary[i],i)){return i;}else{return ary.length;}}else if(i==ary.length){if(val>=mapLoFn(ary[i-1],i-1)&&val<=mapHiFn(ary[i-1],i-1)){return i-1;}else{return ary.length;}}else{return ary.length;}}
-function iterateOverIntersectingIntervals(ary,mapLoFn,mapWidthFn,loVal,hiVal,cb){if(ary.length==0)
-return;if(loVal>hiVal)return;var i=findLowIndexInSortedArray(ary,mapLoFn,loVal);if(i==-1){return;}
+function findIndexInSortedIntervals(ary,mapLoFn,mapWidthFn,loVal){var first=findLowIndexInSortedArray(ary,mapLoFn,loVal);if(first===0){if(loVal>=mapLoFn(ary[0])&&loVal<mapLoFn(ary[0])+mapWidthFn(ary[0],0)){return 0;}
+return-1;}
+if(first<ary.length){if(loVal>=mapLoFn(ary[first])&&loVal<mapLoFn(ary[first])+mapWidthFn(ary[first],first)){return first;}
+if(loVal>=mapLoFn(ary[first-1])&&loVal<mapLoFn(ary[first-1])+
+mapWidthFn(ary[first-1],first-1)){return first-1;}
+return ary.length;}
+if(first===ary.length){if(loVal>=mapLoFn(ary[first-1])&&loVal<mapLoFn(ary[first-1])+
+mapWidthFn(ary[first-1],first-1)){return first-1;}
+return ary.length;}
+return ary.length;}
+function findIndexInSortedClosedIntervals(ary,mapLoFn,mapHiFn,val){var i=findLowIndexInSortedArray(ary,mapLoFn,val);if(i===0){if(val>=mapLoFn(ary[0],0)&&val<=mapHiFn(ary[0],0)){return 0;}
+return-1;}
+if(i<ary.length){if(val>=mapLoFn(ary[i-1],i-1)&&val<=mapHiFn(ary[i-1],i-1)){return i-1;}
+if(val>=mapLoFn(ary[i],i)&&val<=mapHiFn(ary[i],i)){return i;}
+return ary.length;}
+if(i===ary.length){if(val>=mapLoFn(ary[i-1],i-1)&&val<=mapHiFn(ary[i-1],i-1)){return i-1;}
+return ary.length;}
+return ary.length;}
+function iterateOverIntersectingIntervals(ary,mapLoFn,mapWidthFn,loVal,hiVal,cb){if(ary.length===0)return;if(loVal>hiVal)return;var i=findLowIndexInSortedArray(ary,mapLoFn,loVal);if(i===-1){return;}
 if(i>0){var hi=mapLoFn(ary[i-1])+mapWidthFn(ary[i-1],i-1);if(hi>=loVal){cb(ary[i-1],i-1);}}
-if(i==ary.length){return;}
-for(var n=ary.length;i<n;i++){var lo=mapLoFn(ary[i]);if(lo>=hiVal)
-break;cb(ary[i],i);}}
+if(i===ary.length){return;}
+for(var n=ary.length;i<n;i++){var lo=mapLoFn(ary[i]);if(lo>=hiVal)break;cb(ary[i],i);}}
 function getIntersectingIntervals(ary,mapLoFn,mapWidthFn,loVal,hiVal){var tmp=[];iterateOverIntersectingIntervals(ary,mapLoFn,mapWidthFn,loVal,hiVal,function(d){tmp.push(d);});return tmp;}
-function findClosestElementInSortedArray(ary,mapFn,val,maxDiff){if(ary.length===0)
-return null;var aftIdx=findLowIndexInSortedArray(ary,mapFn,val);var befIdx=aftIdx>0?aftIdx-1:0;if(aftIdx===ary.length)
-aftIdx-=1;var befDiff=Math.abs(val-mapFn(ary[befIdx]));var aftDiff=Math.abs(val-mapFn(ary[aftIdx]));if(befDiff>maxDiff&&aftDiff>maxDiff)
-return null;var idx=befDiff<aftDiff?befIdx:aftIdx;return ary[idx];}
-function findClosestIntervalInSortedIntervals(ary,mapLoFn,mapHiFn,val,maxDiff){if(ary.length===0)
-return null;var idx=findLowIndexInSortedArray(ary,mapLoFn,val);if(idx>0)
-idx-=1;var hiInt=ary[idx];var loInt=hiInt;if(val>mapHiFn(hiInt)&&idx+1<ary.length)
-loInt=ary[idx+1];var loDiff=Math.abs(val-mapLoFn(loInt));var hiDiff=Math.abs(val-mapHiFn(hiInt));if(loDiff>maxDiff&&hiDiff>maxDiff)
-return null;if(loDiff<hiDiff)
-return loInt;else
-return hiInt;}
-return{findLowIndexInSortedArray:findLowIndexInSortedArray,findHighIndexInSortedArray:findHighIndexInSortedArray,findIndexInSortedIntervals:findIndexInSortedIntervals,findIndexInSortedClosedIntervals:findIndexInSortedClosedIntervals,iterateOverIntersectingIntervals:iterateOverIntersectingIntervals,getIntersectingIntervals:getIntersectingIntervals,findClosestElementInSortedArray:findClosestElementInSortedArray,findClosestIntervalInSortedIntervals:findClosestIntervalInSortedIntervals};});'use strict';tr.exportTo('tr.model.helpers',function(){var Frame=tr.model.Frame;var Statistics=tr.b.Statistics;var UI_DRAW_TYPE={NONE:'none',LEGACY:'legacy',MARSHMALLOW:'marshmallow'};var UI_THREAD_DRAW_NAMES={'performTraversals':UI_DRAW_TYPE.LEGACY,'Choreographer#doFrame':UI_DRAW_TYPE.MARSHMALLOW};var RENDER_THREAD_DRAW_NAME='DrawFrame';var RENDER_THREAD_INDEP_DRAW_NAME='doFrame';var RENDER_THREAD_QUEUE_NAME='queueBuffer';var RENDER_THREAD_SWAP_NAME='eglSwapBuffers';var THREAD_SYNC_NAME='syncFrameState';function getSlicesForThreadTimeRanges(threadTimeRanges){var ret=[];threadTimeRanges.forEach(function(threadTimeRange){var slices=[];threadTimeRange.thread.sliceGroup.iterSlicesInTimeRange(function(slice){slices.push(slice);},threadTimeRange.start,threadTimeRange.end);ret.push.apply(ret,slices);});return ret;}
+function findClosestElementInSortedArray(ary,mapFn,val,maxDiff){if(ary.length===0)return null;var aftIdx=findLowIndexInSortedArray(ary,mapFn,val);var befIdx=aftIdx>0?aftIdx-1:0;if(aftIdx===ary.length)aftIdx-=1;var befDiff=Math.abs(val-mapFn(ary[befIdx]));var aftDiff=Math.abs(val-mapFn(ary[aftIdx]));if(befDiff>maxDiff&&aftDiff>maxDiff)return null;var idx=befDiff<aftDiff?befIdx:aftIdx;return ary[idx];}
+function findClosestIntervalInSortedIntervals(ary,mapLoFn,mapHiFn,val,maxDiff){if(ary.length===0)return null;var idx=findLowIndexInSortedArray(ary,mapLoFn,val);if(idx>0)idx-=1;var hiInt=ary[idx];var loInt=hiInt;if(val>mapHiFn(hiInt)&&idx+1<ary.length){loInt=ary[idx+1];}
+var loDiff=Math.abs(val-mapLoFn(loInt));var hiDiff=Math.abs(val-mapHiFn(hiInt));if(loDiff>maxDiff&&hiDiff>maxDiff)return null;if(loDiff<hiDiff)return loInt;return hiInt;}
+return{findLowIndexInSortedArray,findHighIndexInSortedArray,findIndexInSortedIntervals,findIndexInSortedClosedIntervals,iterateOverIntersectingIntervals,getIntersectingIntervals,findClosestElementInSortedArray,findClosestIntervalInSortedIntervals,};});'use strict';tr.exportTo('tr.model.helpers',function(){var Frame=tr.model.Frame;var Statistics=tr.b.math.Statistics;var UI_DRAW_TYPE={NONE:'none',LEGACY:'legacy',MARSHMALLOW:'marshmallow'};var UI_THREAD_DRAW_NAMES={'performTraversals':UI_DRAW_TYPE.LEGACY,'Choreographer#doFrame':UI_DRAW_TYPE.MARSHMALLOW};var RENDER_THREAD_DRAW_NAME='DrawFrame';var RENDER_THREAD_INDEP_DRAW_NAME='doFrame';var RENDER_THREAD_QUEUE_NAME='queueBuffer';var RENDER_THREAD_SWAP_NAME='eglSwapBuffers';var THREAD_SYNC_NAME='syncFrameState';function getSlicesForThreadTimeRanges(threadTimeRanges){var ret=[];threadTimeRanges.forEach(function(threadTimeRange){var slices=[];threadTimeRange.thread.sliceGroup.iterSlicesInTimeRange(function(slice){slices.push(slice);},threadTimeRange.start,threadTimeRange.end);ret.push.apply(ret,slices);});return ret;}
 function makeFrame(threadTimeRanges,surfaceFlinger){var args={};if(surfaceFlinger&&surfaceFlinger.hasVsyncs){var start=Statistics.min(threadTimeRanges,function(threadTimeRanges){return threadTimeRanges.start;});args['deadline']=surfaceFlinger.getFrameDeadline(start);args['frameKickoff']=surfaceFlinger.getFrameKickoff(start);}
 var events=getSlicesForThreadTimeRanges(threadTimeRanges);return new Frame(events,threadTimeRanges,args);}
-function findOverlappingDrawFrame(renderThread,uiDrawSlice){if(!renderThread)
-return undefined;var overlappingDrawFrame;var slices=tr.b.iterateOverIntersectingIntervals(renderThread.sliceGroup.slices,function(range){return range.start},function(range){return range.end},uiDrawSlice.start,uiDrawSlice.end,function(rtDrawSlice){if(rtDrawSlice.title==RENDER_THREAD_DRAW_NAME){var rtSyncSlice=rtDrawSlice.findDescendentSlice(THREAD_SYNC_NAME);if(rtSyncSlice&&rtSyncSlice.start>=uiDrawSlice.start&&rtSyncSlice.end<=uiDrawSlice.end){overlappingDrawFrame=rtDrawSlice;}}});return overlappingDrawFrame;}
-function getPreTraversalWorkRanges(uiThread){if(!uiThread)
-return[];var preFrameEvents=[];uiThread.sliceGroup.slices.forEach(function(slice){if(slice.title=='obtainView'||slice.title=='setupListItem'||slice.title=='deliverInputEvent'||slice.title=='RV Scroll')
-preFrameEvents.push(slice);});uiThread.asyncSliceGroup.slices.forEach(function(slice){if(slice.title=='deliverInputEvent')
-preFrameEvents.push(slice);});return tr.b.mergeRanges(tr.b.convertEventsToRanges(preFrameEvents),3,function(events){return{start:events[0].min,end:events[events.length-1].max};});}
-function getFrameStartTime(traversalStart,preTraversalWorkRanges){var preTraversalWorkRange=tr.b.findClosestIntervalInSortedIntervals(preTraversalWorkRanges,function(range){return range.start},function(range){return range.end},traversalStart,3);if(preTraversalWorkRange)
-return preTraversalWorkRange.start;return traversalStart;}
-function getRtFrameEndTime(rtDrawSlice){console.log(rtDrawSlice);var rtQueueSlice=rtDrawSlice.findDescendentSlice(RENDER_THREAD_QUEUE_NAME);if(rtQueueSlice){return rtQueueSlice.end;}
+function findOverlappingDrawFrame(renderThread,uiDrawSlice){if(!renderThread)return undefined;var overlappingDrawFrame;var slices=tr.b.math.iterateOverIntersectingIntervals(renderThread.sliceGroup.slices,function(range){return range.start;},function(range){return range.end;},uiDrawSlice.start,uiDrawSlice.end,function(rtDrawSlice){if(rtDrawSlice.title===RENDER_THREAD_DRAW_NAME){var rtSyncSlice=rtDrawSlice.findDescendentSlice(THREAD_SYNC_NAME);if(rtSyncSlice&&rtSyncSlice.start>=uiDrawSlice.start&&rtSyncSlice.end<=uiDrawSlice.end){overlappingDrawFrame=rtDrawSlice;}}});return overlappingDrawFrame;}
+function getPreTraversalWorkRanges(uiThread){if(!uiThread)return[];var preFrameEvents=[];uiThread.sliceGroup.slices.forEach(function(slice){if(slice.title==='obtainView'||slice.title==='setupListItem'||slice.title==='deliverInputEvent'||slice.title==='RV Scroll'){preFrameEvents.push(slice);}});uiThread.asyncSliceGroup.slices.forEach(function(slice){if(slice.title==='deliverInputEvent'){preFrameEvents.push(slice);}});return tr.b.math.mergeRanges(tr.b.math.convertEventsToRanges(preFrameEvents),3,function(events){return{start:events[0].min,end:events[events.length-1].max};});}
+function getFrameStartTime(traversalStart,preTraversalWorkRanges){var preTraversalWorkRange=tr.b.math.findClosestIntervalInSortedIntervals(preTraversalWorkRanges,function(range){return range.start;},function(range){return range.end;},traversalStart,3);if(preTraversalWorkRange){return preTraversalWorkRange.start;}
+return traversalStart;}
+function getRtFrameEndTime(rtDrawSlice){var rtQueueSlice=rtDrawSlice.findDescendentSlice(RENDER_THREAD_QUEUE_NAME);if(rtQueueSlice){return rtQueueSlice.end;}
 var rtSwapSlice=rtDrawSlice.findDescendentSlice(RENDER_THREAD_SWAP_NAME);if(rtSwapSlice){return rtSwapSlice.end;}
 return rtDrawSlice.end;}
-function getUiThreadDrivenFrames(app){if(!app.uiThread)
-return[];var preTraversalWorkRanges=[];if(app.uiDrawType==UI_DRAW_TYPE.LEGACY)
-preTraversalWorkRanges=getPreTraversalWorkRanges(app.uiThread);var frames=[];app.uiThread.sliceGroup.slices.forEach(function(slice){if(!(slice.title in UI_THREAD_DRAW_NAMES)){return;}
+function getUiThreadDrivenFrames(app){if(!app.uiThread)return[];var preTraversalWorkRanges=[];if(app.uiDrawType===UI_DRAW_TYPE.LEGACY){preTraversalWorkRanges=getPreTraversalWorkRanges(app.uiThread);}
+var frames=[];app.uiThread.sliceGroup.slices.forEach(function(slice){if(!(slice.title in UI_THREAD_DRAW_NAMES)){return;}
 var threadTimeRanges=[];var uiThreadTimeRange={thread:app.uiThread,start:getFrameStartTime(slice.start,preTraversalWorkRanges),end:slice.end};threadTimeRanges.push(uiThreadTimeRange);var rtDrawSlice=findOverlappingDrawFrame(app.renderThread,slice);if(rtDrawSlice){var rtSyncSlice=rtDrawSlice.findDescendentSlice(THREAD_SYNC_NAME);if(rtSyncSlice){uiThreadTimeRange.end=Math.min(uiThreadTimeRange.end,rtSyncSlice.start);}
 threadTimeRanges.push({thread:app.renderThread,start:rtDrawSlice.start,end:getRtFrameEndTime(rtDrawSlice)});}
 frames.push(makeFrame(threadTimeRanges,app.surfaceFlinger));});return frames;}
-function getRenderThreadDrivenFrames(app){if(!app.renderThread)
-return[];var frames=[];app.renderThread.sliceGroup.getSlicesOfName(RENDER_THREAD_INDEP_DRAW_NAME).forEach(function(slice){var threadTimeRanges=[{thread:app.renderThread,start:slice.start,end:slice.end}];frames.push(makeFrame(threadTimeRanges,app.surfaceFlinger));});return frames;}
-function getUiDrawType(uiThread){if(!uiThread)
-return UI_DRAW_TYPE.NONE;var slices=uiThread.sliceGroup.slices;for(var i=0;i<slices.length;i++){if(slices[i].title in UI_THREAD_DRAW_NAMES){return UI_THREAD_DRAW_NAMES[slices[i].title];}}
+function getRenderThreadDrivenFrames(app){if(!app.renderThread)return[];var frames=[];app.renderThread.sliceGroup.getSlicesOfName(RENDER_THREAD_INDEP_DRAW_NAME).forEach(function(slice){var threadTimeRanges=[{thread:app.renderThread,start:slice.start,end:slice.end}];frames.push(makeFrame(threadTimeRanges,app.surfaceFlinger));});return frames;}
+function getUiDrawType(uiThread){if(!uiThread){return UI_DRAW_TYPE.NONE;}
+var slices=uiThread.sliceGroup.slices;for(var i=0;i<slices.length;i++){if(slices[i].title in UI_THREAD_DRAW_NAMES){return UI_THREAD_DRAW_NAMES[slices[i].title];}}
 return UI_DRAW_TYPE.NONE;}
-function getInputSamples(process){var samples=undefined;for(var counterName in process.counters){if(/^android\.aq\:pending/.test(counterName)&&process.counters[counterName].numSeries==1){samples=process.counters[counterName].series[0].samples;break;}}
-if(!samples)
-return[];var inputSamples=[];var lastValue=0;samples.forEach(function(sample){if(sample.value>lastValue){inputSamples.push(sample);}
+function getInputSamples(process){var samples=undefined;for(var counterName in process.counters){if(/^android\.aq\:pending/.test(counterName)&&process.counters[counterName].numSeries===1){samples=process.counters[counterName].series[0].samples;break;}}
+if(!samples)return[];var inputSamples=[];var lastValue=0;samples.forEach(function(sample){if(sample.value>lastValue){inputSamples.push(sample);}
 lastValue=sample.value;});return inputSamples;}
-function getAnimationAsyncSlices(uiThread){if(!uiThread)
-return[];var slices=[];for(var slice of uiThread.asyncSliceGroup.getDescendantEvents()){if(/^animator\:/.test(slice.title))
-slices.push(slice);}
+function getAnimationAsyncSlices(uiThread){if(!uiThread)return[];var slices=[];for(var slice of uiThread.asyncSliceGroup.getDescendantEvents()){if(/^animator\:/.test(slice.title)){slices.push(slice);}}
 return slices;}
 function AndroidApp(process,uiThread,renderThread,surfaceFlinger,uiDrawType){this.process=process;this.uiThread=uiThread;this.renderThread=renderThread;this.surfaceFlinger=surfaceFlinger;this.uiDrawType=uiDrawType;this.frames_=undefined;this.inputs_=undefined;}
-AndroidApp.createForProcessIfPossible=function(process,surfaceFlinger){var uiThread=process.getThread(process.pid);var uiDrawType=getUiDrawType(uiThread);if(uiDrawType==UI_DRAW_TYPE.NONE){uiThread=undefined;}
-var renderThreads=process.findAllThreadsNamed('RenderThread');var renderThread=renderThreads.length==1?renderThreads[0]:undefined;if(uiThread||renderThread){return new AndroidApp(process,uiThread,renderThread,surfaceFlinger,uiDrawType);}};AndroidApp.prototype={getFrames:function(){if(!this.frames_){var uiFrames=getUiThreadDrivenFrames(this);var rtFrames=getRenderThreadDrivenFrames(this);this.frames_=uiFrames.concat(rtFrames);this.frames_.sort(function(a,b){a.end-b.end});}
+AndroidApp.createForProcessIfPossible=function(process,surfaceFlinger){var uiThread=process.getThread(process.pid);var uiDrawType=getUiDrawType(uiThread);if(uiDrawType===UI_DRAW_TYPE.NONE){uiThread=undefined;}
+var renderThreads=process.findAllThreadsNamed('RenderThread');var renderThread=(renderThreads.length===1?renderThreads[0]:undefined);if(uiThread||renderThread){return new AndroidApp(process,uiThread,renderThread,surfaceFlinger,uiDrawType);}};AndroidApp.prototype={getFrames:function(){if(!this.frames_){var uiFrames=getUiThreadDrivenFrames(this);var rtFrames=getRenderThreadDrivenFrames(this);this.frames_=uiFrames.concat(rtFrames);this.frames_.sort(function(a,b){a.end-b.end;});}
 return this.frames_;},getInputSamples:function(){if(!this.inputs_){this.inputs_=getInputSamples(this.process);}
 return this.inputs_;},getAnimationAsyncSlices:function(){if(!this.animations_){this.animations_=getAnimationAsyncSlices(this.uiThread);}
-return this.animations_;}};return{AndroidApp:AndroidApp};});'use strict';tr.exportTo('tr.model.helpers',function(){var findLowIndexInSortedArray=tr.b.findLowIndexInSortedArray;var VSYNC_SF_NAME='android.VSYNC-sf';var VSYNC_APP_NAME='android.VSYNC-app';var VSYNC_FALLBACK_NAME='android.VSYNC';var TIMESTAMP_FUDGE_MS=0.01;function getVsyncTimestamps(process,counterName){var vsync=process.counters[counterName];if(!vsync)
-vsync=process.counters[VSYNC_FALLBACK_NAME];if(vsync&&vsync.numSeries==1&&vsync.numSamples>1)
-return vsync.series[0].timestamps;return undefined;}
-function AndroidSurfaceFlinger(process,thread){this.process=process;this.thread=thread;this.appVsync_=undefined;this.sfVsync_=undefined;this.appVsyncTimestamps_=getVsyncTimestamps(process,VSYNC_APP_NAME);this.sfVsyncTimestamps_=getVsyncTimestamps(process,VSYNC_SF_NAME);this.deadlineDelayMs_=this.appVsyncTimestamps_!==this.sfVsyncTimestamps_?5:TIMESTAMP_FUDGE_MS;};AndroidSurfaceFlinger.createForProcessIfPossible=function(process){var mainThread=process.getThread(process.pid);if(mainThread&&mainThread.name&&/surfaceflinger/.test(mainThread.name))
-return new AndroidSurfaceFlinger(process,mainThread);var primaryThreads=process.findAllThreadsNamed('SurfaceFlinger');if(primaryThreads.length==1)
-return new AndroidSurfaceFlinger(process,primaryThreads[0]);return undefined;};AndroidSurfaceFlinger.prototype={get hasVsyncs(){return!!this.appVsyncTimestamps_&&!!this.sfVsyncTimestamps_;},getFrameKickoff:function(timestamp){if(!this.hasVsyncs)
-throw new Error('cannot query vsync info without vsyncs');var firstGreaterIndex=findLowIndexInSortedArray(this.appVsyncTimestamps_,function(x){return x;},timestamp+TIMESTAMP_FUDGE_MS);if(firstGreaterIndex<1)
-return undefined;return this.appVsyncTimestamps_[firstGreaterIndex-1];},getFrameDeadline:function(timestamp){if(!this.hasVsyncs)
-throw new Error('cannot query vsync info without vsyncs');var firstGreaterIndex=findLowIndexInSortedArray(this.sfVsyncTimestamps_,function(x){return x;},timestamp+this.deadlineDelayMs_);if(firstGreaterIndex>=this.sfVsyncTimestamps_.length)
-return undefined;return this.sfVsyncTimestamps_[firstGreaterIndex];}};return{AndroidSurfaceFlinger:AndroidSurfaceFlinger};});'use strict';tr.exportTo('tr.model.helpers',function(){var AndroidApp=tr.model.helpers.AndroidApp;var AndroidSurfaceFlinger=tr.model.helpers.AndroidSurfaceFlinger;var IMPORTANT_SURFACE_FLINGER_SLICES={'doComposition':true,'updateTexImage':true,'postFramebuffer':true};var IMPORTANT_UI_THREAD_SLICES={'Choreographer#doFrame':true,'performTraversals':true,'deliverInputEvent':true};var IMPORTANT_RENDER_THREAD_SLICES={'doFrame':true};function iterateImportantThreadSlices(thread,important,callback){if(!thread)
-return;thread.sliceGroup.slices.forEach(function(slice){if(slice.title in important)
-callback(slice);});}
+return this.animations_;}};return{AndroidApp,};});'use strict';tr.exportTo('tr.model.helpers',function(){var findLowIndexInSortedArray=tr.b.math.findLowIndexInSortedArray;var VSYNC_SF_NAME='android.VSYNC-sf';var VSYNC_APP_NAME='android.VSYNC-app';var VSYNC_FALLBACK_NAME='android.VSYNC';var TIMESTAMP_FUDGE_MS=0.01;function getVsyncTimestamps(process,counterName){var vsync=process.counters[counterName];if(!vsync){vsync=process.counters[VSYNC_FALLBACK_NAME];}
+if(vsync&&vsync.numSeries===1&&vsync.numSamples>1){return vsync.series[0].timestamps;}
+return undefined;}
+function AndroidSurfaceFlinger(process,thread){this.process=process;this.thread=thread;this.appVsync_=undefined;this.sfVsync_=undefined;this.appVsyncTimestamps_=getVsyncTimestamps(process,VSYNC_APP_NAME);this.sfVsyncTimestamps_=getVsyncTimestamps(process,VSYNC_SF_NAME);this.deadlineDelayMs_=this.appVsyncTimestamps_!==this.sfVsyncTimestamps_?5:TIMESTAMP_FUDGE_MS;}
+AndroidSurfaceFlinger.createForProcessIfPossible=function(process){var mainThread=process.getThread(process.pid);if(mainThread&&mainThread.name&&/surfaceflinger/.test(mainThread.name)){return new AndroidSurfaceFlinger(process,mainThread);}
+var primaryThreads=process.findAllThreadsNamed('SurfaceFlinger');if(primaryThreads.length===1){return new AndroidSurfaceFlinger(process,primaryThreads[0]);}
+return undefined;};AndroidSurfaceFlinger.prototype={get hasVsyncs(){return!!this.appVsyncTimestamps_&&!!this.sfVsyncTimestamps_;},getFrameKickoff:function(timestamp){if(!this.hasVsyncs){throw new Error('cannot query vsync info without vsyncs');}
+var firstGreaterIndex=findLowIndexInSortedArray(this.appVsyncTimestamps_,function(x){return x;},timestamp+TIMESTAMP_FUDGE_MS);if(firstGreaterIndex<1)return undefined;return this.appVsyncTimestamps_[firstGreaterIndex-1];},getFrameDeadline:function(timestamp){if(!this.hasVsyncs){throw new Error('cannot query vsync info without vsyncs');}
+var firstGreaterIndex=findLowIndexInSortedArray(this.sfVsyncTimestamps_,function(x){return x;},timestamp+this.deadlineDelayMs_);if(firstGreaterIndex>=this.sfVsyncTimestamps_.length){return undefined;}
+return this.sfVsyncTimestamps_[firstGreaterIndex];}};return{AndroidSurfaceFlinger,};});'use strict';tr.exportTo('tr.model.helpers',function(){var AndroidApp=tr.model.helpers.AndroidApp;var AndroidSurfaceFlinger=tr.model.helpers.AndroidSurfaceFlinger;var IMPORTANT_SURFACE_FLINGER_SLICES={'doComposition':true,'updateTexImage':true,'postFramebuffer':true};var IMPORTANT_UI_THREAD_SLICES={'Choreographer#doFrame':true,'performTraversals':true,'deliverInputEvent':true};var IMPORTANT_RENDER_THREAD_SLICES={'doFrame':true};function iterateImportantThreadSlices(thread,important,callback){if(!thread)return;thread.sliceGroup.slices.forEach(function(slice){if(slice.title in important){callback(slice);}});}
 function AndroidModelHelper(model){this.model=model;this.apps=[];this.surfaceFlinger=undefined;var processes=model.getAllProcesses();for(var i=0;i<processes.length&&!this.surfaceFlinger;i++){this.surfaceFlinger=AndroidSurfaceFlinger.createForProcessIfPossible(processes[i]);}
-model.getAllProcesses().forEach(function(process){var app=AndroidApp.createForProcessIfPossible(process,this.surfaceFlinger);if(app)
-this.apps.push(app);},this);};AndroidModelHelper.guid=tr.b.GUID.allocateSimple();AndroidModelHelper.supportsModel=function(model){return true;};AndroidModelHelper.prototype={iterateImportantSlices:function(callback){if(this.surfaceFlinger){iterateImportantThreadSlices(this.surfaceFlinger.thread,IMPORTANT_SURFACE_FLINGER_SLICES,callback);}
-this.apps.forEach(function(app){iterateImportantThreadSlices(app.uiThread,IMPORTANT_UI_THREAD_SLICES,callback);iterateImportantThreadSlices(app.renderThread,IMPORTANT_RENDER_THREAD_SLICES,callback);});}};return{AndroidModelHelper:AndroidModelHelper};});'use strict';tr.exportTo('tr.model',function(){function Slice(category,title,colorId,start,args,opt_duration,opt_cpuStart,opt_cpuDuration,opt_argsStripped,opt_bind_id){if(new.target){throw new Error("Can't instantiate pure virtual class Slice");}
-tr.model.TimedEvent.call(this,start);this.category=category||'';this.title=title;this.colorId=colorId;this.args=args;this.startStackFrame=undefined;this.endStackFrame=undefined;this.didNotFinish=false;this.inFlowEvents=[];this.outFlowEvents=[];this.subSlices=[];this.selfTime=undefined;this.cpuSelfTime=undefined;this.important=false;this.parentContainer=undefined;this.argsStripped=false;this.bind_id_=opt_bind_id;this.parentSlice=undefined;this.isTopLevel=false;if(opt_duration!==undefined)
-this.duration=opt_duration;if(opt_cpuStart!==undefined)
-this.cpuStart=opt_cpuStart;if(opt_cpuDuration!==undefined)
-this.cpuDuration=opt_cpuDuration;if(opt_argsStripped!==undefined)
-this.argsStripped=true;}
+model.getAllProcesses().forEach(function(process){var app=AndroidApp.createForProcessIfPossible(process,this.surfaceFlinger);if(app){this.apps.push(app);}},this);}
+AndroidModelHelper.guid=tr.b.GUID.allocateSimple();AndroidModelHelper.supportsModel=function(model){return true;};AndroidModelHelper.prototype={iterateImportantSlices:function(callback){if(this.surfaceFlinger){iterateImportantThreadSlices(this.surfaceFlinger.thread,IMPORTANT_SURFACE_FLINGER_SLICES,callback);}
+this.apps.forEach(function(app){iterateImportantThreadSlices(app.uiThread,IMPORTANT_UI_THREAD_SLICES,callback);iterateImportantThreadSlices(app.renderThread,IMPORTANT_RENDER_THREAD_SLICES,callback);});}};return{AndroidModelHelper,};});'use strict';tr.exportTo('tr.model',function(){function Slice(category,title,colorId,start,args,opt_duration,opt_cpuStart,opt_cpuDuration,opt_argsStripped,opt_bindId){if(new.target){throw new Error('Can\'t instantiate pure virtual class Slice');}
+tr.model.TimedEvent.call(this,start);this.category=category||'';this.title=title;this.colorId=colorId;this.args=args;this.startStackFrame=undefined;this.endStackFrame=undefined;this.didNotFinish=false;this.inFlowEvents=[];this.outFlowEvents=[];this.subSlices=[];this.selfTime=undefined;this.cpuSelfTime=undefined;this.important=false;this.parentContainer=undefined;this.argsStripped=false;this.bind_id_=opt_bindId;this.parentSlice=undefined;this.isTopLevel=false;if(opt_duration!==undefined){this.duration=opt_duration;}
+if(opt_cpuStart!==undefined){this.cpuStart=opt_cpuStart;}
+if(opt_cpuDuration!==undefined){this.cpuDuration=opt_cpuDuration;}
+if(opt_argsStripped!==undefined){this.argsStripped=true;}}
 Slice.prototype={__proto__:tr.model.TimedEvent.prototype,get analysisTypeName(){return this.title;},get userFriendlyName(){return'Slice '+this.title+' at '+
 tr.b.Unit.byName.timeStampInMs.format(this.start);},get stableId(){var parentSliceGroup=this.parentContainer.sliceGroup;return parentSliceGroup.stableId+'.'+
-parentSliceGroup.slices.indexOf(this);},findDescendentSlice:function(targetTitle){if(!this.subSlices)
-return undefined;for(var i=0;i<this.subSlices.length;i++){if(this.subSlices[i].title==targetTitle)
-return this.subSlices[i];var slice=this.subSlices[i].findDescendentSlice(targetTitle);if(slice)return slice;}
-return undefined;},get mostTopLevelSlice(){var curSlice=this;while(curSlice.parentSlice)
-curSlice=curSlice.parentSlice;return curSlice;},getProcess:function(){var thread=this.parentContainer;if(thread&&thread.getProcess)
-return thread.getProcess();return undefined;},get model(){var process=this.getProcess();if(process!==undefined)
-return this.getProcess().model;return undefined;},findTopmostSlicesRelativeToThisSlice:function*(eventPredicate){if(eventPredicate(this)){yield this;return;}
-for(var s of this.subSlices)
-yield*s.findTopmostSlicesRelativeToThisSlice(eventPredicate);},iterateAllSubsequentSlices:function(callback,opt_this){var parentStack=[];var started=false;var topmostSlice=this.mostTopLevelSlice;parentStack.push(topmostSlice);while(parentStack.length!==0){var curSlice=parentStack.pop();if(started)
-callback.call(opt_this,curSlice);else
-started=(curSlice.guid===this.guid);for(var i=curSlice.subSlices.length-1;i>=0;i--){parentStack.push(curSlice.subSlices[i]);}}},get subsequentSlices(){var res=[];this.iterateAllSubsequentSlices(function(subseqSlice){res.push(subseqSlice);});return res;},enumerateAllAncestors:function*(){var curSlice=this;while(curSlice.parentSlice){curSlice=curSlice.parentSlice;yield curSlice;}},get ancestorSlices(){var res=[];for(var slice of this.enumerateAllAncestors())
-res.push(slice);return res;},iterateEntireHierarchy:function(callback,opt_this){var mostTopLevelSlice=this.mostTopLevelSlice;callback.call(opt_this,mostTopLevelSlice);mostTopLevelSlice.iterateAllSubsequentSlices(callback,opt_this);},get entireHierarchy(){var res=[];this.iterateEntireHierarchy(function(slice){res.push(slice);});return res;},get ancestorAndSubsequentSlices(){var res=[];res.push(this);for(var aSlice of this.enumerateAllAncestors())
-res.push(aSlice);this.iterateAllSubsequentSlices(function(sSlice){res.push(sSlice);});return res;},enumerateAllDescendents:function*(){for(var slice of this.subSlices)
-yield slice;for(var slice of this.subSlices)
-yield*slice.enumerateAllDescendents();},get descendentSlices(){var res=[];for(var slice of this.enumerateAllDescendents())
-res.push(slice);return res;}};return{Slice:Slice};});'use strict';tr.exportTo('tr.model',function(){var Slice=tr.model.Slice;var SCHEDULING_STATE={DEBUG:'Debug',EXIT_DEAD:'Exit Dead',RUNNABLE:'Runnable',RUNNING:'Running',SLEEPING:'Sleeping',STOPPED:'Stopped',TASK_DEAD:'Task Dead',UNINTR_SLEEP:'Uninterruptible Sleep',UNINTR_SLEEP_WAKE_KILL:'Uninterruptible Sleep | WakeKill',UNINTR_SLEEP_WAKING:'Uninterruptible Sleep | Waking',UNINTR_SLEEP_IO:'Uninterruptible Sleep - Block I/O',UNINTR_SLEEP_WAKE_KILL_IO:'Uninterruptible Sleep | WakeKill - Block I/O',UNINTR_SLEEP_WAKING_IO:'Uninterruptible Sleep | Waking - Block I/O',UNKNOWN:'UNKNOWN',WAKE_KILL:'Wakekill',WAKING:'Waking',ZOMBIE:'Zombie'};function ThreadTimeSlice(thread,schedulingState,cat,start,args,opt_duration){Slice.call(this,cat,schedulingState,this.getColorForState_(schedulingState),start,args,opt_duration);this.thread=thread;this.schedulingState=schedulingState;this.cpuOnWhichThreadWasRunning=undefined;}
-ThreadTimeSlice.prototype={__proto__:Slice.prototype,getColorForState_:function(state){var getColorIdForReservedName=tr.b.ColorScheme.getColorIdForReservedName;switch(state){case SCHEDULING_STATE.RUNNABLE:return getColorIdForReservedName('thread_state_runnable');case SCHEDULING_STATE.RUNNING:return getColorIdForReservedName('thread_state_running');case SCHEDULING_STATE.SLEEPING:return getColorIdForReservedName('thread_state_sleeping');case SCHEDULING_STATE.DEBUG:case SCHEDULING_STATE.EXIT_DEAD:case SCHEDULING_STATE.STOPPED:case SCHEDULING_STATE.TASK_DEAD:case SCHEDULING_STATE.UNINTR_SLEEP:case SCHEDULING_STATE.UNINTR_SLEEP_WAKE_KILL:case SCHEDULING_STATE.UNINTR_SLEEP_WAKING:case SCHEDULING_STATE.UNKNOWN:case SCHEDULING_STATE.WAKE_KILL:case SCHEDULING_STATE.WAKING:case SCHEDULING_STATE.ZOMBIE:return getColorIdForReservedName('thread_state_uninterruptible');case SCHEDULING_STATE.UNINTR_SLEEP_IO:case SCHEDULING_STATE.UNINTR_SLEEP_WAKE_KILL_IO:case SCHEDULING_STATE.UNINTR_SLEEP_WAKING_IO:return getColorIdForReservedName('thread_state_iowait');default:return getColorIdForReservedName('thread_state_unknown');}},get analysisTypeName(){return'tr.ui.analysis.ThreadTimeSlice';},getAssociatedCpuSlice:function(){if(!this.cpuOnWhichThreadWasRunning)
-return undefined;var cpuSlices=this.cpuOnWhichThreadWasRunning.slices;for(var i=0;i<cpuSlices.length;i++){var cpuSlice=cpuSlices[i];if(cpuSlice.start!==this.start)
-continue;if(cpuSlice.duration!==this.duration)
-continue;return cpuSlice;}
-return undefined;},getCpuSliceThatTookCpu:function(){if(this.cpuOnWhichThreadWasRunning)
-return undefined;var curIndex=this.thread.indexOfTimeSlice(this);var cpuSliceWhenLastRunning;while(curIndex>=0){var curSlice=this.thread.timeSlices[curIndex];if(!curSlice.cpuOnWhichThreadWasRunning){curIndex--;continue;}
+parentSliceGroup.slices.indexOf(this);},get bindId(){return this.bind_id_;},findDescendentSlice:function(targetTitle){if(!this.subSlices){return undefined;}
+for(var i=0;i<this.subSlices.length;i++){if(this.subSlices[i].title===targetTitle){return this.subSlices[i];}
+var slice=this.subSlices[i].findDescendentSlice(targetTitle);if(slice)return slice;}
+return undefined;},get mostTopLevelSlice(){if(!this.parentSlice)return this;return this.parentSlice.mostTopLevelSlice;},getProcess:function(){var thread=this.parentContainer;if(thread&&thread.getProcess){return thread.getProcess();}
+return undefined;},get model(){var process=this.getProcess();if(process!==undefined){return this.getProcess().model;}
+return undefined;},findTopmostSlicesRelativeToThisSlice:function*(eventPredicate){if(eventPredicate(this)){yield this;return;}
+for(var s of this.subSlices){yield*s.findTopmostSlicesRelativeToThisSlice(eventPredicate);}},iterateAllSubsequentSlices:function(callback,opt_this){var parentStack=[];var started=false;var topmostSlice=this.mostTopLevelSlice;parentStack.push(topmostSlice);while(parentStack.length!==0){var curSlice=parentStack.pop();if(started){callback.call(opt_this,curSlice);}else{started=(curSlice.guid===this.guid);}
+for(var i=curSlice.subSlices.length-1;i>=0;i--){parentStack.push(curSlice.subSlices[i]);}}},get subsequentSlices(){var res=[];this.iterateAllSubsequentSlices(function(subseqSlice){res.push(subseqSlice);});return res;},enumerateAllAncestors:function*(){let curSlice=this.parentSlice;while(curSlice){yield curSlice;curSlice=curSlice.parentSlice;}},get ancestorSlices(){return Array.from(this.enumerateAllAncestors());},iterateEntireHierarchy:function(callback,opt_this){var mostTopLevelSlice=this.mostTopLevelSlice;callback.call(opt_this,mostTopLevelSlice);mostTopLevelSlice.iterateAllSubsequentSlices(callback,opt_this);},get entireHierarchy(){var res=[];this.iterateEntireHierarchy(function(slice){res.push(slice);});return res;},get ancestorAndSubsequentSlices(){var res=[];res.push(this);for(var aSlice of this.enumerateAllAncestors()){res.push(aSlice);}
+this.iterateAllSubsequentSlices(function(sSlice){res.push(sSlice);});return res;},enumerateAllDescendents:function*(){for(var slice of this.subSlices){yield slice;}
+for(var slice of this.subSlices){yield*slice.enumerateAllDescendents();}},get descendentSlices(){var res=[];for(var slice of this.enumerateAllDescendents()){res.push(slice);}
+return res;}};return{Slice,};});'use strict';tr.exportTo('tr.model',function(){var Slice=tr.model.Slice;var SCHEDULING_STATE={DEBUG:'Debug',EXIT_DEAD:'Exit Dead',RUNNABLE:'Runnable',RUNNING:'Running',SLEEPING:'Sleeping',STOPPED:'Stopped',TASK_DEAD:'Task Dead',UNINTR_SLEEP:'Uninterruptible Sleep',UNINTR_SLEEP_WAKE_KILL:'Uninterruptible Sleep | WakeKill',UNINTR_SLEEP_WAKING:'Uninterruptible Sleep | Waking',UNINTR_SLEEP_IO:'Uninterruptible Sleep - Block I/O',UNINTR_SLEEP_WAKE_KILL_IO:'Uninterruptible Sleep | WakeKill - Block I/O',UNINTR_SLEEP_WAKING_IO:'Uninterruptible Sleep | Waking - Block I/O',UNKNOWN:'UNKNOWN',WAKE_KILL:'Wakekill',WAKING:'Waking',ZOMBIE:'Zombie'};function ThreadTimeSlice(thread,schedulingState,cat,start,args,opt_duration){Slice.call(this,cat,schedulingState,this.getColorForState_(schedulingState),start,args,opt_duration);this.thread=thread;this.schedulingState=schedulingState;this.cpuOnWhichThreadWasRunning=undefined;}
+ThreadTimeSlice.prototype={__proto__:Slice.prototype,getColorForState_:function(state){var getColorIdForReservedName=tr.b.ColorScheme.getColorIdForReservedName;switch(state){case SCHEDULING_STATE.RUNNABLE:return getColorIdForReservedName('thread_state_runnable');case SCHEDULING_STATE.RUNNING:return getColorIdForReservedName('thread_state_running');case SCHEDULING_STATE.SLEEPING:return getColorIdForReservedName('thread_state_sleeping');case SCHEDULING_STATE.DEBUG:case SCHEDULING_STATE.EXIT_DEAD:case SCHEDULING_STATE.STOPPED:case SCHEDULING_STATE.TASK_DEAD:case SCHEDULING_STATE.UNINTR_SLEEP:case SCHEDULING_STATE.UNINTR_SLEEP_WAKE_KILL:case SCHEDULING_STATE.UNINTR_SLEEP_WAKING:case SCHEDULING_STATE.UNKNOWN:case SCHEDULING_STATE.WAKE_KILL:case SCHEDULING_STATE.WAKING:case SCHEDULING_STATE.ZOMBIE:return getColorIdForReservedName('thread_state_uninterruptible');case SCHEDULING_STATE.UNINTR_SLEEP_IO:case SCHEDULING_STATE.UNINTR_SLEEP_WAKE_KILL_IO:case SCHEDULING_STATE.UNINTR_SLEEP_WAKING_IO:return getColorIdForReservedName('thread_state_iowait');default:return getColorIdForReservedName('thread_state_unknown');}},get analysisTypeName(){return'tr.ui.analysis.ThreadTimeSlice';},getAssociatedCpuSlice:function(){if(!this.cpuOnWhichThreadWasRunning)return undefined;var cpuSlices=this.cpuOnWhichThreadWasRunning.slices;for(var i=0;i<cpuSlices.length;i++){var cpuSlice=cpuSlices[i];if(cpuSlice.start!==this.start)continue;if(cpuSlice.duration!==this.duration)continue;return cpuSlice;}
+return undefined;},getCpuSliceThatTookCpu:function(){if(this.cpuOnWhichThreadWasRunning)return undefined;var curIndex=this.thread.indexOfTimeSlice(this);var cpuSliceWhenLastRunning;while(curIndex>=0){var curSlice=this.thread.timeSlices[curIndex];if(!curSlice.cpuOnWhichThreadWasRunning){curIndex--;continue;}
 cpuSliceWhenLastRunning=curSlice.getAssociatedCpuSlice();break;}
-if(!cpuSliceWhenLastRunning)
-return undefined;var cpu=cpuSliceWhenLastRunning.cpu;var indexOfSliceOnCpuWhenLastRunning=cpu.indexOf(cpuSliceWhenLastRunning);var nextRunningSlice=cpu.slices[indexOfSliceOnCpuWhenLastRunning+1];if(!nextRunningSlice)
-return undefined;if(Math.abs(nextRunningSlice.start-cpuSliceWhenLastRunning.end)<0.00001)
-return nextRunningSlice;return undefined;}};tr.model.EventRegistry.register(ThreadTimeSlice,{name:'threadTimeSlice',pluralName:'threadTimeSlices'});return{ThreadTimeSlice:ThreadTimeSlice,SCHEDULING_STATE:SCHEDULING_STATE};});'use strict';tr.exportTo('tr.model',function(){var CompoundEventSelectionState={NOT_SELECTED:0,EVENT_SELECTED:0x1,SOME_ASSOCIATED_EVENTS_SELECTED:0x2,ALL_ASSOCIATED_EVENTS_SELECTED:0x4,EVENT_AND_SOME_ASSOCIATED_SELECTED:0x1|0x2,EVENT_AND_ALL_ASSOCIATED_SELECTED:0x1|0x4};return{CompoundEventSelectionState:CompoundEventSelectionState};});'use strict';tr.exportTo('tr.model.um',function(){var CompoundEventSelectionState=tr.model.CompoundEventSelectionState;function UserExpectation(parentModel,initiatorTitle,start,duration){tr.model.TimedEvent.call(this,start);this.associatedEvents=new tr.model.EventSet();this.duration=duration;this.initiatorTitle_=initiatorTitle;this.parentModel=parentModel;this.typeInfo_=undefined;this.sourceEvents=new tr.model.EventSet();}
-UserExpectation.prototype={__proto__:tr.model.TimedEvent.prototype,computeCompoundEvenSelectionState:function(selection){var cess=CompoundEventSelectionState.NOT_SELECTED;if(selection.contains(this))
-cess|=CompoundEventSelectionState.EVENT_SELECTED;if(this.associatedEvents.intersectionIsEmpty(selection))
-return cess;var allContained=this.associatedEvents.every(function(event){return selection.contains(event);});if(allContained)
-cess|=CompoundEventSelectionState.ALL_ASSOCIATED_EVENTS_SELECTED;else
-cess|=CompoundEventSelectionState.SOME_ASSOCIATED_EVENTS_SELECTED;return cess;},get associatedSamples(){var samples=new tr.model.EventSet();this.associatedEvents.forEach(function(event){if(event instanceof tr.model.ThreadSlice)
-samples.addEventSet(event.overlappingSamples);});return samples;},get userFriendlyName(){return this.title+' User Expectation at '+
+if(!cpuSliceWhenLastRunning)return undefined;var cpu=cpuSliceWhenLastRunning.cpu;var indexOfSliceOnCpuWhenLastRunning=cpu.indexOf(cpuSliceWhenLastRunning);var nextRunningSlice=cpu.slices[indexOfSliceOnCpuWhenLastRunning+1];if(!nextRunningSlice)return undefined;if(Math.abs(nextRunningSlice.start-cpuSliceWhenLastRunning.end)<0.00001){return nextRunningSlice;}
+return undefined;}};tr.model.EventRegistry.register(ThreadTimeSlice,{name:'threadTimeSlice',pluralName:'threadTimeSlices'});return{ThreadTimeSlice,SCHEDULING_STATE,};});'use strict';tr.exportTo('tr.model',function(){var CompoundEventSelectionState={NOT_SELECTED:0,EVENT_SELECTED:0x1,SOME_ASSOCIATED_EVENTS_SELECTED:0x2,ALL_ASSOCIATED_EVENTS_SELECTED:0x4,EVENT_AND_SOME_ASSOCIATED_SELECTED:0x1|0x2,EVENT_AND_ALL_ASSOCIATED_SELECTED:0x1|0x4};return{CompoundEventSelectionState,};});'use strict';tr.exportTo('tr.model.um',function(){var CompoundEventSelectionState=tr.model.CompoundEventSelectionState;function UserExpectation(parentModel,initiatorType,start,duration){tr.model.TimedEvent.call(this,start);this.associatedEvents=new tr.model.EventSet();this.duration=duration;this.initiatorType_=initiatorType;this.parentModel=parentModel;this.typeInfo_=undefined;this.sourceEvents=new tr.model.EventSet();}
+var INITIATOR_TYPE={KEYBOARD:'Keyboard',MOUSE:'Mouse',MOUSE_WHEEL:'MouseWheel',TAP:'Tap',PINCH:'Pinch',FLING:'Fling',TOUCH:'Touch',SCROLL:'Scroll',CSS:'CSS',WEBGL:'WebGL',VIDEO:'Video'};UserExpectation.prototype={__proto__:tr.model.TimedEvent.prototype,computeCompoundEvenSelectionState:function(selection){var cess=CompoundEventSelectionState.NOT_SELECTED;if(selection.contains(this)){cess|=CompoundEventSelectionState.EVENT_SELECTED;}
+if(this.associatedEvents.intersectionIsEmpty(selection)){return cess;}
+var allContained=this.associatedEvents.every(function(event){return selection.contains(event);});if(allContained){cess|=CompoundEventSelectionState.ALL_ASSOCIATED_EVENTS_SELECTED;}else{cess|=CompoundEventSelectionState.SOME_ASSOCIATED_EVENTS_SELECTED;}
+return cess;},get associatedSamples(){var samples=new tr.model.EventSet();this.associatedEvents.forEach(function(event){if(event instanceof tr.model.ThreadSlice){samples.addEventSet(event.overlappingSamples);}});return samples;},get userFriendlyName(){return this.title+' User Expectation at '+
 tr.b.Unit.byName.timeStampInMs.format(this.start);},get stableId(){return('UserExpectation.'+this.guid);},get typeInfo(){if(!this.typeInfo_){this.typeInfo_=UserExpectation.subTypes.findTypeInfo(this.constructor);}
-if(!this.typeInfo_)
-throw new Error('Unregistered UserExpectation');return this.typeInfo_;},get colorId(){return this.typeInfo.metadata.colorId;},get stageTitle(){return this.typeInfo.metadata.stageTitle;},get initiatorTitle(){return this.initiatorTitle_;},get title(){if(!this.initiatorTitle)
-return this.stageTitle;return this.initiatorTitle+' '+this.stageTitle;},get totalCpuMs(){var cpuMs=0;this.associatedEvents.forEach(function(event){if(event.cpuSelfTime)
-cpuMs+=event.cpuSelfTime;});return cpuMs;}};var subTypes={};var options=new tr.b.ExtensionRegistryOptions(tr.b.BASIC_REGISTRY_MODE);tr.b.decorateExtensionRegistry(subTypes,options);subTypes.addEventListener('will-register',function(e){var metadata=e.typeInfo.metadata;if(metadata.stageTitle===undefined){throw new Error('Registered UserExpectations must provide '+'stageTitle');}
-if(metadata.colorId===undefined){throw new Error('Registered UserExpectations must provide '+'colorId');}});tr.model.EventRegistry.register(UserExpectation,{name:'userExpectation',pluralName:'userExpectations',subTypes:subTypes});return{UserExpectation:UserExpectation};});'use strict';tr.exportTo('tr.model.um',function(){function ResponseExpectation(parentModel,initiatorTitle,start,duration,opt_isAnimationBegin){tr.model.um.UserExpectation.call(this,parentModel,initiatorTitle,start,duration);this.isAnimationBegin=opt_isAnimationBegin||false;}
-ResponseExpectation.prototype={__proto__:tr.model.um.UserExpectation.prototype,constructor:ResponseExpectation};tr.model.um.UserExpectation.subTypes.register(ResponseExpectation,{stageTitle:'Response',colorId:tr.b.ColorScheme.getColorIdForReservedName('rail_response')});return{ResponseExpectation:ResponseExpectation};});'use strict';tr.exportTo('tr.v.d',function(){class Diagnostic{asDict(){var result={type:this.constructor.name};this.asDictInto_(result);return result;}
-asDictInto_(d){throw new Error('Abstract virtual method');}
-static fromDict(d){var typeInfo=Diagnostic.findTypeInfoWithName(d.type);if(!typeInfo)
-throw new Error('Unrecognized diagnostic type: '+d.type);return typeInfo.constructor.fromDict(d);}}
-var options=new tr.b.ExtensionRegistryOptions(tr.b.BASIC_REGISTRY_MODE);options.defaultMetadata={};options.mandatoryBaseClass=Diagnostic;tr.b.decorateExtensionRegistry(Diagnostic,options);Diagnostic.addEventListener('will-register',function(e){var constructor=e.typeInfo.constructor;if(!(constructor.fromDict instanceof Function)||(constructor.fromDict===Diagnostic.fromDict)||(constructor.fromDict.length!==1)){throw new Error('Diagnostics must define fromDict(d)');}});return{Diagnostic:Diagnostic};});'use strict';tr.exportTo('tr.v.d',function(){function ValueRef(guid){this.guid=guid;}
-return{ValueRef:ValueRef};});'use strict';tr.exportTo('tr.v.d',function(){class RelatedValueMap extends tr.v.d.Diagnostic{constructor(){super();this.valuesByName_=new Map();}
-get(name){return this.valuesByName_.get(name);}
-set(name,value){if(!(value instanceof tr.v.Histogram)&&!(value instanceof tr.v.d.ValueRef))
-throw new Error('Must be instanceof Histogram or ValueRef: '+value);this.valuesByName_.set(name,value);}
-add(value){this.set(value.name,value);}
-get length(){return this.valuesByName_.size;}*[Symbol.iterator](){for(var pair of this.valuesByName_)
-yield pair;}
-resolve(valueSet,opt_required){for(var[name,value]of this){if(!(value instanceof tr.v.d.ValueRef))
-continue;var guid=value.guid;value=valueSet.lookup(guid);if(value instanceof tr.v.Histogram)
-this.valuesByName_.set(name,value);else if(opt_required)
-throw new Error('Unable to find Histogram '+guid);}}
-asDictInto_(d){d.values={};for(var[name,value]of this)
-d.values[name]=value.guid;}
-static fromDict(d){var map=new RelatedValueMap();tr.b.iterItems(d.values,function(name,guid){map.set(name,new tr.v.d.ValueRef(guid));});return map;}}
-tr.v.d.Diagnostic.register(RelatedValueMap,{elementName:'tr-v-ui-related-value-map-span'});return{RelatedValueMap:RelatedValueMap};});'use strict';tr.exportTo('tr.v.d',function(){class Breakdown extends tr.v.d.Diagnostic{constructor(){super();this.values_=new Map();this.colorScheme=undefined;}
-set(name,value){if(typeof name!=='string'||typeof value!=='number'){throw new Error('Breakdown maps from strings to numbers');}
-this.values_.set(name,value);}
-get(name){return this.values_.get(name)||0;}*[Symbol.iterator](){for(var pair of this.values_)
-yield pair;}
-asDictInto_(d){d.values={};for(var[name,value]of this)
-d.values[name]=value;if(this.colorScheme)
-d.colorScheme=this.colorScheme;}
-static fromDict(d){var breakdown=new Breakdown();tr.b.iterItems(d.values,(name,value)=>breakdown.set(name,value));if(d.colorScheme)
-breakdown.colorScheme=d.colorScheme;return breakdown;}}
-tr.v.d.Diagnostic.register(Breakdown,{elementName:'tr-v-ui-breakdown-span'});return{Breakdown:Breakdown};});'use strict';tr.exportTo('tr.v.d',function(){class Generic extends tr.v.d.Diagnostic{constructor(value){super();this.value=value;}
-asDictInto_(d){d.value=this.value;}
-static fromDict(d){return new Generic(d.value);}}
-tr.v.d.Diagnostic.register(Generic,{elementName:'tr-v-ui-generic-diagnostic-span'});return{Generic:Generic};});'use strict';tr.exportTo('tr.b',function(){function addSingletonGetter(ctor){ctor.getInstance=function(){return ctor.instance_||(ctor.instance_=new ctor());};}
-function deepCopy(value){if(!(value instanceof Object)){if(value===undefined||value===null)
-return value;if(typeof value=='string')
-return value.substring();if(typeof value=='boolean')
-return value;if(typeof value=='number')
-return value;throw new Error('Unrecognized: '+typeof value);}
-var object=value;if(object instanceof Array){var res=new Array(object.length);for(var i=0;i<object.length;i++)
-res[i]=deepCopy(object[i]);return res;}
-if(object.__proto__!=Object.prototype)
-throw new Error('Can only clone simple types');var res={};for(var key in object){res[key]=deepCopy(object[key]);}
-return res;}
-function normalizeException(e){if(e===undefined||e===null){return{typeName:'UndefinedError',message:'Unknown: null or undefined exception',stack:'Unknown'};}
-if(typeof(e)=='string'){return{typeName:'StringError',message:e,stack:[e]};}
-var typeName;if(e.name){typeName=e.name;}else if(e.constructor){if(e.constructor.name){typeName=e.constructor.name;}else{typeName='AnonymousError';}}else{typeName='ErrorWithNoConstructor';}
-var msg=e.message?e.message:'Unknown';return{typeName:typeName,message:msg,stack:e.stack?e.stack:[msg]};}
-function stackTraceAsString(){return new Error().stack+'';}
-function stackTrace(){var stack=stackTraceAsString();stack=stack.split('\n');return stack.slice(2);}
-function getUsingPath(path,from_dict){var parts=path.split('.');var cur=from_dict;for(var part;parts.length&&(part=parts.shift());){if(!parts.length){return cur[part];}else if(part in cur){cur=cur[part];}else{return undefined;}}
-return undefined;}
-function formatDate(date){return date.toISOString().replace('T',' ').slice(0,19);}
-return{addSingletonGetter:addSingletonGetter,deepCopy:deepCopy,normalizeException:normalizeException,stackTrace:stackTrace,stackTraceAsString:stackTraceAsString,formatDate:formatDate,getUsingPath:getUsingPath};});'use strict';tr.exportTo('tr.v.d',function(){class IterationInfo extends tr.v.d.Diagnostic{constructor(opt_info){super();this.benchmarkName_='';this.benchmarkStart_=undefined;this.label_='';this.osVersion_='';this.productVersion_='';this.storyDisplayName_='';this.storyGroupingKeys_={};this.storyRepeatCounter_=0;this.storyUrl_='';this.storysetRepeatCounter_=0;if(opt_info)
-this.addInfo(opt_info);}
-addInfo(info){if(info.benchmarkName)
-this.benchmarkName_=info.benchmarkName;if(info.benchmarkStartMs)
-this.benchmarkStart_=new Date(info.benchmarkStartMs);if(info.label)
-this.label_=info.label;if(info.storyDisplayName)
-this.storyDisplayName_=info.storyDisplayName;if(info.storyGroupingKeys)
-this.storyGroupingKeys_=info.storyGroupingKeys;if(info.storyRepeatCounter)
-this.storyRepeatCounter_=info.storyRepeatCounter;if(info.storyUrl)
-this.storyUrl_=info.storyUrl;if(info.storysetRepeatCounter)
-this.storysetRepeatCounter_=info.storysetRepeatCounter;if(info['os-version'])
-this.osVersion_=info['os-version'];if(info['product-version'])
-this.productVersion_=info['product-version'];}
-addToValue(value){value.diagnostics.set(IterationInfo.NAME,this);}
-static getFromValue(value){return value.diagnostics.get(IterationInfo.NAME);}
-asDictInto_(d){d.benchmarkName=this.benchmarkName;if(this.benchmarkStart)
-d.benchmarkStartMs=this.benchmarkStart.getTime();d.label=this.label;d.storyDisplayName=this.storyDisplayName;d.storyGroupingKeys=this.storyGroupingKeys;d.storyRepeatCounter=this.storyRepeatCounter;d.storyUrl=this.storyUrl;d.storysetRepeatCounter=this.storysetRepeatCounter;d['os-version']=this.osVersion;d['product-version']=this.productVersion;}
-static fromDict(d){var info=new IterationInfo();info.addInfo(d);return info;}
-get displayLabel(){if(this.label)
-return this.label;return this.benchmarkName+' '+this.benchmarkStartString;}
-get osVersion(){return this.osVersion_;}
-get productVersion(){return this.productVersion_;}
-get benchmarkName(){return this.benchmarkName_;}
-get label(){return this.label_;}
-get storyGroupingKeys(){return this.storyGroupingKeys_;}
-get storyDisplayName(){return this.storyDisplayName_;}
-get storyUrl(){return this.storyUrl_;}
-get storyRepeatCounter(){return this.storyRepeatCounter_;}
-get storyRepeatCounterLabel(){return'story repeat '+this.storyRepeatCounter;}
-get storysetRepeatCounter(){return this.storysetRepeatCounter_;}
-get storysetRepeatCounterLabel(){return'storyset repeat '+this.storysetRepeatCounter;}
-get benchmarkStart(){return this.benchmarkStart_;}
-get benchmarkStartString(){if(this.benchmarkStart_===undefined)
-return'';return tr.b.formatDate(this.benchmarkStart);}
-static getField(value,fieldName,defaultValue){var iteration=tr.v.d.IterationInfo.getFromValue(value);if(!(iteration instanceof tr.v.d.IterationInfo)||!iteration[fieldName]){return defaultValue;}
-return iteration[fieldName];}
-static getStoryGroupingKeyLabel(value,storyGroupingKey){var iteration=tr.v.d.IterationInfo.getFromValue(value);if(!(iteration instanceof tr.v.d.IterationInfo))
-return storyGroupingKey+': undefined';return storyGroupingKey+': '+
-iteration.storyGroupingKeys[storyGroupingKey];}}
-IterationInfo.NAME='iteration';tr.v.d.Diagnostic.register(IterationInfo,{elementName:'tr-v-ui-iteration-info-span'});return{IterationInfo:IterationInfo};});'use strict';tr.exportTo('tr.v.d',function(){class EventRef{constructor(event){this.stableId=event.stableId;this.title=event.title;this.start=event.start;this.duration=event.duration;this.end=this.start+this.duration;this.guid=tr.b.GUID.allocateSimple();}}
-return{EventRef:EventRef};});'use strict';tr.exportTo('tr.v.d',function(){class RelatedEventSet extends tr.v.d.Diagnostic{constructor(opt_events){super();this.eventsByStableId_=new Map();if(opt_events){if(opt_events instanceof tr.model.EventSet||opt_events instanceof Array){for(var event of opt_events)
-this.add(event);}else{this.add(opt_events);}}}
-add(event){this.eventsByStableId_.set(event.stableId,event);}
-has(event){return this.eventsByStableId_.has(event.stableId);}
-get length(){return this.eventsByStableId_.size;}*[Symbol.iterator](){for(var[stableId,event]of this.eventsByStableId_)
-yield event;}
-resolve(model,opt_required){for(var[stableId,event]of this.eventsByStableId_){if(!(event instanceof tr.v.d.EventRef))
-continue;event=model.getEventByStableId(stableId);if(event instanceof tr.model.Event)
-this.eventsByStableId_.set(stableId,event);else if(opt_required)
-throw new Error('Unable to find Event '+stableId);}}
-asDictInto_(d){d.events=[];for(var event of this){d.events.push({stableId:event.stableId,title:event.title,start:event.start,duration:event.duration});}}
-static fromDict(d){return new RelatedEventSet(d.events.map(event=>new tr.v.d.EventRef(event)));}}
-tr.v.d.Diagnostic.register(RelatedEventSet,{elementName:'tr-v-ui-related-event-set-span'});return{RelatedEventSet:RelatedEventSet};});'use strict';tr.exportTo('tr.v.d',function(){var COLOR_SCHEME_CHROME_USER_FRIENDLY_CATEGORY_DRIVER='ChromeUserFriendlyCategory';class RelatedHistogramBreakdown extends tr.v.d.RelatedValueMap{constructor(){super();this.colorScheme=undefined;}
-set(name,value){if(!(value instanceof tr.v.d.ValueRef)){if(!(value instanceof tr.v.Histogram)){throw new Error('RelatedHistogramBreakdown can only contain Histograms');}
-if(value.name.indexOf(name)!==(value.name.length-name.length)){throw new Error('RelatedHistogramBreakdown name must be a suffix of value.name');}
-if((this.length>0)&&(value.unit!==tr.b.getFirstElement(this)[1].unit)){throw new Error('Units mismatch',tr.b.getFirstElement(this)[1].unit,value.unit);}}
-tr.v.d.RelatedValueMap.prototype.set.call(this,name,value);}
-asDictInto_(d){tr.v.d.RelatedValueMap.prototype.asDictInto_.call(this,d);if(this.colorScheme)
-d.colorScheme=this.colorScheme;}
-static fromDict(d){var diagnostic=new RelatedHistogramBreakdown();tr.b.iterItems(d.values,function(name,guid){diagnostic.set(name,new tr.v.d.ValueRef(guid));});if(d.colorScheme)
-diagnostic.colorScheme=d.colorScheme;return diagnostic;}
-static buildFromEvents(values,namePrefix,events,categoryForEvent,unit,opt_sampleForEvent,opt_binBoundaries,opt_this){var sampleForEvent=opt_sampleForEvent||((event)=>event.cpuSelfTime);var diagnostic=new RelatedHistogramBreakdown();for(var event of events){var sample=sampleForEvent.call(opt_this,event);if(sample===undefined)
-continue;var eventCategory=categoryForEvent.call(opt_this,event);var value=diagnostic.get(eventCategory);if(value===undefined){value=new tr.v.Histogram(namePrefix+eventCategory,unit,opt_binBoundaries);values.addHistogram(value);diagnostic.set(eventCategory,value);}
-value.addSample(sample,{relatedEvents:new tr.v.d.RelatedEventSet([event])});}
-return diagnostic;}}
-tr.v.d.Diagnostic.register(RelatedHistogramBreakdown,{elementName:'tr-v-ui-breakdown-span'});return{COLOR_SCHEME_CHROME_USER_FRIENDLY_CATEGORY_DRIVER:COLOR_SCHEME_CHROME_USER_FRIENDLY_CATEGORY_DRIVER,RelatedHistogramBreakdown:RelatedHistogramBreakdown};});'use strict';tr.exportTo('tr.v.d',function(){class RelatedValueSet extends tr.v.d.Diagnostic{constructor(opt_values){super();this.valuesByGuid_=new Map();if(opt_values)
-for(var value of opt_values)
-this.add(value);}
-add(value){if(!(value instanceof tr.v.Histogram)&&!(value instanceof tr.v.d.ValueRef))
-throw new Error('Must be instanceof Histogram or ValueRef: '+value);if(this.valuesByGuid_.get(value.guid))
-throw new Error('Tried to add same value twice');this.valuesByGuid_.set(value.guid,value);}
-has(value){return this.valuesByGuid_.has(value.guid);}
-get length(){return this.valuesByGuid_.size;}*[Symbol.iterator](){for(var[guid,value]of this.valuesByGuid_)
-yield value;}
-resolve(valueSet,opt_required){for(var[guid,value]of this.valuesByGuid_){if(!(value instanceof tr.v.d.ValueRef))
-continue;value=valueSet.lookup(guid);if(value instanceof tr.v.Histogram)
-this.valuesByGuid_.set(guid,value);else if(opt_required)
-throw new Error('Unable to find Histogram '+guid);}}
-asDictInto_(d){d.guids=[];for(var value of this)
-d.guids.push(value.guid);}
-static fromDict(d){return new RelatedValueSet(d.guids.map(guid=>new tr.v.d.ValueRef(guid)));}}
-tr.v.d.Diagnostic.register(RelatedValueSet,{elementName:'tr-v-ui-related-value-set-span'});return{RelatedValueSet:RelatedValueSet,};});'use strict';tr.exportTo('tr.v.d',function(){class DiagnosticMap extends Map{set(name,diagnostic){if(typeof(name)!=='string')
-throw new Error('name must be string, not '+name);if(!(diagnostic instanceof tr.v.d.Diagnostic))
-throw new Error('Must be instanceof Diagnostic: '+diagnostic);Map.prototype.set.call(this,name,diagnostic);}
-addDicts(dict){tr.b.iterItems(dict,function(name,diagnosticDict){this.set(name,tr.v.d.Diagnostic.fromDict(diagnosticDict));},this);}
-asDict(){var dict={};for(var[name,diagnostic]of this){dict[name]=diagnostic.asDict();}
-return dict;}
-static fromDict(d){var diagnostics=new DiagnosticMap();diagnostics.addDicts(d);return diagnostics;}
-static fromObject(obj){var diagnostics=new DiagnosticMap();tr.b.iterItems(obj,function(name,diagnostic){diagnostics.set(name,diagnostic);});return diagnostics;}}
-return{DiagnosticMap:DiagnosticMap};});'use strict';tr.exportTo('tr.v',function(){var MAX_DIAGNOSTIC_MAPS=16;class NumericBase{constructor(unit){if(!(unit instanceof tr.b.Unit))
-throw new Error('Expected provided unit to be instance of Unit');this.unit=unit;}
-asDict(){var d={unit:this.unit.asJSON()};this.asDictInto_(d);return d;}
-static fromDict(d){if(d.type==='scalar')
-return ScalarNumeric.fromDict(d);throw new Error('Not implemented');}}
-class ScalarNumeric extends NumericBase{constructor(unit,value){if(!(unit instanceof tr.b.Unit))
-throw new Error('Expected Unit');if(!(typeof(value)=='number'))
-throw new Error('Expected value to be number');super(unit);this.value=value;}
-asDictInto_(d){d.type='scalar';if(this.value===Infinity)
-d.value='Infinity';else if(this.value===-Infinity)
-d.value='-Infinity';else if(isNaN(this.value))
-d.value='NaN';else
-d.value=this.value;}
-toString(){return this.unit.format(this.value);}
-static fromDict(d){if(typeof(d.value)==='string'){if(d.value==='-Infinity'){d.value=-Infinity;}else if(d.value==='Infinity'){d.value=Infinity;}else if(d.value==='NaN'){d.value=NaN;}}
-return new ScalarNumeric(tr.b.Unit.fromJSON(d.unit),d.value);}}
-return{NumericBase:NumericBase,ScalarNumeric:ScalarNumeric};});'use strict';tr.exportTo('tr.e.audits',function(){var SCHEDULING_STATE=tr.model.SCHEDULING_STATE;var Auditor=tr.c.Auditor;var AndroidModelHelper=tr.model.helpers.AndroidModelHelper;var ColorScheme=tr.b.ColorScheme;var Statistics=tr.b.Statistics;var FRAME_PERF_CLASS=tr.model.FRAME_PERF_CLASS;var Alert=tr.model.Alert;var EventInfo=tr.model.EventInfo;var ScalarNumeric=tr.v.ScalarNumeric;var timeDurationInMs=tr.b.Unit.byName.timeDurationInMs;var EXPECTED_FRAME_TIME_MS=16.67;function getStart(e){return e.start;}
+if(!this.typeInfo_){throw new Error('Unregistered UserExpectation');}
+return this.typeInfo_;},get colorId(){return this.typeInfo.metadata.colorId;},get stageTitle(){return this.typeInfo.metadata.stageTitle;},get initiatorType(){return this.initiatorType_;},get title(){if(!this.initiatorType){return this.stageTitle;}
+return this.initiatorType+' '+this.stageTitle;},get totalCpuMs(){var cpuMs=0;this.associatedEvents.forEach(function(event){if(event.cpuSelfTime){cpuMs+=event.cpuSelfTime;}});return cpuMs;}};var subTypes={};var options=new tr.b.ExtensionRegistryOptions(tr.b.BASIC_REGISTRY_MODE);tr.b.decorateExtensionRegistry(subTypes,options);subTypes.addEventListener('will-register',function(e){var metadata=e.typeInfo.metadata;if(metadata.stageTitle===undefined){throw new Error('Registered UserExpectations must provide '+'stageTitle');}
+if(metadata.colorId===undefined){throw new Error('Registered UserExpectations must provide '+'colorId');}});tr.model.EventRegistry.register(UserExpectation,{name:'userExpectation',pluralName:'userExpectations',subTypes:subTypes});return{UserExpectation,INITIATOR_TYPE,};});'use strict';tr.exportTo('tr.model.um',function(){function ResponseExpectation(parentModel,initiatorTitle,start,duration,opt_isAnimationBegin){tr.model.um.UserExpectation.call(this,parentModel,initiatorTitle,start,duration);this.isAnimationBegin=opt_isAnimationBegin||false;}
+ResponseExpectation.prototype={__proto__:tr.model.um.UserExpectation.prototype,constructor:ResponseExpectation};tr.model.um.UserExpectation.subTypes.register(ResponseExpectation,{stageTitle:'Response',colorId:tr.b.ColorScheme.getColorIdForReservedName('rail_response')});return{ResponseExpectation,};});'use strict';tr.exportTo('tr.e.audits',function(){var SCHEDULING_STATE=tr.model.SCHEDULING_STATE;var Auditor=tr.c.Auditor;var AndroidModelHelper=tr.model.helpers.AndroidModelHelper;var ColorScheme=tr.b.ColorScheme;var Statistics=tr.b.math.Statistics;var FRAME_PERF_CLASS=tr.model.FRAME_PERF_CLASS;var Alert=tr.model.Alert;var EventInfo=tr.model.EventInfo;var Scalar=tr.b.Scalar;var timeDurationInMs=tr.b.Unit.byName.timeDurationInMs;var EXPECTED_FRAME_TIME_MS=16.67;function getStart(e){return e.start;}
 function getDuration(e){return e.duration;}
 function getCpuDuration(e){return(e.cpuDuration!==undefined)?e.cpuDuration:e.duration;}
 function frameIsActivityStart(frame){return frame.associatedEvents.any(x=>x.title==='activityStart');}
 function frameMissedDeadline(frame){return frame.args['deadline']&&frame.args['deadline']<frame.end;}
 function DocLinkBuilder(){this.docLinks=[];}
-DocLinkBuilder.prototype={addAppVideo:function(name,videoId){this.docLinks.push({label:'Video Link',textContent:('Android Performance Patterns: '+name),href:'https://www.youtube.com/watch?list=PLWz5rJ2EKKc9CBxr3BVjPTPoDPLdPIFCE&v='+videoId});return this;},addDacRef:function(name,link){this.docLinks.push({label:'Doc Link',textContent:(name+' documentation'),href:'https://developer.android.com/reference/'+link});return this;},build:function(){return this.docLinks;}};function AndroidAuditor(model){Auditor.call(this,model);var helper=model.getOrCreateHelper(AndroidModelHelper);if(helper.apps.length||helper.surfaceFlinger)
-this.helper=helper;}
-AndroidAuditor.viewAlphaAlertInfo_=new EventInfo('Inefficient View alpha usage','Setting an alpha between 0 and 1 has significant performance costs, if one of the fast alpha paths is not used.',new DocLinkBuilder().addAppVideo('Hidden Cost of Transparency','wIy8g8yNhNk').addDacRef('View#setAlpha()','android/view/View.html#setAlpha(float)').build());AndroidAuditor.saveLayerAlertInfo_=new EventInfo('Expensive rendering with Canvas#saveLayer()','Canvas#saveLayer() incurs extremely high rendering cost. They disrupt the rendering pipeline when drawn, forcing a flush of drawing content. Instead use View hardware layers, or static Bitmaps. This enables the offscreen buffers to be reused in between frames, and avoids the disruptive render target switch.',new DocLinkBuilder().addAppVideo('Hidden Cost of Transparency','wIy8g8yNhNk').addDacRef('Canvas#saveLayerAlpha()','android/graphics/Canvas.html#saveLayerAlpha(android.graphics.RectF, int, int)').build());AndroidAuditor.getSaveLayerAlerts_=function(frame){var badAlphaRegEx=/^(.+) alpha caused (unclipped )?saveLayer (\d+)x(\d+)$/;var saveLayerRegEx=/^(unclipped )?saveLayer (\d+)x(\d+)$/;var ret=[];var events=[];frame.associatedEvents.forEach(function(slice){var match=badAlphaRegEx.exec(slice.title);if(match){var args={'view name':match[1],width:parseInt(match[3]),height:parseInt(match[4])};ret.push(new Alert(AndroidAuditor.viewAlphaAlertInfo_,slice.start,[slice],args));}else if(saveLayerRegEx.test(slice.title))
-events.push(slice);},this);if(events.length>ret.length){var unclippedSeen=Statistics.sum(events,function(slice){return saveLayerRegEx.exec(slice.title)[1]?1:0;});var clippedSeen=events.length-unclippedSeen;var earliestStart=Statistics.min(events,function(slice){return slice.start;});var args={'Unclipped saveLayer count (especially bad!)':unclippedSeen,'Clipped saveLayer count':clippedSeen};events.push(frame);ret.push(new Alert(AndroidAuditor.saveLayerAlertInfo_,earliestStart,events,args));}
-return ret;};AndroidAuditor.pathAlertInfo_=new EventInfo('Path texture churn','Paths are drawn with a mask texture, so when a path is modified / newly drawn, that texture must be generated and uploaded to the GPU. Ensure that you cache paths between frames and do not unnecessarily call Path#reset(). You can cut down on this cost by sharing Path object instances between drawables/views.');AndroidAuditor.getPathAlert_=function(frame){var uploadRegEx=/^Generate Path Texture$/;var events=frame.associatedEvents.filter(function(event){return event.title=='Generate Path Texture';});var start=Statistics.min(events,getStart);var duration=Statistics.sum(events,getDuration);if(duration<3)
-return undefined;events.push(frame);return new Alert(AndroidAuditor.pathAlertInfo_,start,events,{'Time spent':new ScalarNumeric(timeDurationInMs,duration)});};AndroidAuditor.uploadAlertInfo_=new EventInfo('Expensive Bitmap uploads','Bitmaps that have been modified / newly drawn must be uploaded to the GPU. Since this is expensive if the total number of pixels uploaded is large, reduce the amount of Bitmap churn in this animation/context, per frame.');AndroidAuditor.getUploadAlert_=function(frame){var uploadRegEx=/^Upload (\d+)x(\d+) Texture$/;var events=[];var start=Number.POSITIVE_INFINITY;var duration=0;var pixelsUploaded=0;frame.associatedEvents.forEach(function(event){var match=uploadRegEx.exec(event.title);if(match){events.push(event);start=Math.min(start,event.start);duration+=event.duration;pixelsUploaded+=parseInt(match[1])*parseInt(match[2]);}});if(events.length==0||duration<3)
-return undefined;var mPixels=(pixelsUploaded/1000000).toFixed(2)+' million';var args={'Pixels uploaded':mPixels,'Time spent':new ScalarNumeric(timeDurationInMs,duration)};events.push(frame);return new Alert(AndroidAuditor.uploadAlertInfo_,start,events,args);};AndroidAuditor.ListViewInflateAlertInfo_=new EventInfo('Inflation during ListView recycling','ListView item recycling involved inflating views. Ensure your Adapter#getView() recycles the incoming View, instead of constructing a new one.');AndroidAuditor.ListViewBindAlertInfo_=new EventInfo('Inefficient ListView recycling/rebinding','ListView recycling taking too much time per frame. Ensure your Adapter#getView() binds data efficiently.');AndroidAuditor.getListViewAlert_=function(frame){var events=frame.associatedEvents.filter(function(event){return event.title==='obtainView'||event.title==='setupListItem';});var duration=Statistics.sum(events,getCpuDuration);if(events.length==0||duration<3)
-return undefined;var hasInflation=false;for(var event of events)
-if(event.findDescendentSlice('inflate'))
-hasInflation=true;var start=Statistics.min(events,getStart);var args={'Time spent':new ScalarNumeric(timeDurationInMs,duration)};args['ListView items '+(hasInflation?'inflated':'rebound')]=events.length/2;var eventInfo=hasInflation?AndroidAuditor.ListViewInflateAlertInfo_:AndroidAuditor.ListViewBindAlertInfo_;events.push(frame);return new Alert(eventInfo,start,events,args);};AndroidAuditor.measureLayoutAlertInfo_=new EventInfo('Expensive measure/layout pass','Measure/Layout took a significant time, contributing to jank. Avoid triggering layout during animations.',new DocLinkBuilder().addAppVideo('Invalidations, Layouts, and Performance','we6poP0kw6E').build());AndroidAuditor.getMeasureLayoutAlert_=function(frame){var events=frame.associatedEvents.filter(function(event){return event.title==='measure'||event.title==='layout';});var duration=Statistics.sum(events,getCpuDuration);if(events.length==0||duration<3)
-return undefined;var start=Statistics.min(events,getStart);events.push(frame);return new Alert(AndroidAuditor.measureLayoutAlertInfo_,start,events,{'Time spent':new ScalarNumeric(timeDurationInMs,duration)});};AndroidAuditor.viewDrawAlertInfo_=new EventInfo('Long View#draw()','Recording the drawing commands of invalidated Views took a long time. Avoid significant work in View or Drawable custom drawing, especially allocations or drawing to Bitmaps.',new DocLinkBuilder().addAppVideo('Invalidations, Layouts, and Performance','we6poP0kw6E').addAppVideo('Avoiding Allocations in onDraw()','HAK5acHQ53E').build());AndroidAuditor.getViewDrawAlert_=function(frame){var slice=undefined;for(var event of frame.associatedEvents){if(event.title==='getDisplayList'||event.title==='Record View#draw()'){slice=event;break;}}
-if(!slice||getCpuDuration(slice)<3)
-return undefined;return new Alert(AndroidAuditor.viewDrawAlertInfo_,slice.start,[slice,frame],{'Time spent':new ScalarNumeric(timeDurationInMs,getCpuDuration(slice))});};AndroidAuditor.blockingGcAlertInfo_=new EventInfo('Blocking Garbage Collection','Blocking GCs are caused by object churn, and made worse by having large numbers of objects in the heap. Avoid allocating objects during animations/scrolling, and recycle Bitmaps to avoid triggering garbage collection.',new DocLinkBuilder().addAppVideo('Garbage Collection in Android','pzfzz50W5Uo').addAppVideo('Avoiding Allocations in onDraw()','HAK5acHQ53E').build());AndroidAuditor.getBlockingGcAlert_=function(frame){var events=frame.associatedEvents.filter(function(event){return event.title=='DVM Suspend'||event.title=='GC: Wait For Concurrent';});var blockedDuration=Statistics.sum(events,getDuration);if(blockedDuration<3)
-return undefined;var start=Statistics.min(events,getStart);events.push(frame);return new Alert(AndroidAuditor.blockingGcAlertInfo_,start,events,{'Blocked duration':new ScalarNumeric(timeDurationInMs,blockedDuration)});};AndroidAuditor.lockContentionAlertInfo_=new EventInfo('Lock contention','UI thread lock contention is caused when another thread holds a lock that the UI thread is trying to use. UI thread progress is blocked until the lock is released. Inspect locking done within the UI thread, and ensure critical sections are short.');AndroidAuditor.getLockContentionAlert_=function(frame){var events=frame.associatedEvents.filter(function(event){return/^Lock Contention on /.test(event.title);});var blockedDuration=Statistics.sum(events,getDuration);if(blockedDuration<1)
-return undefined;var start=Statistics.min(events,getStart);events.push(frame);return new Alert(AndroidAuditor.lockContentionAlertInfo_,start,events,{'Blocked duration':new ScalarNumeric(timeDurationInMs,blockedDuration)});};AndroidAuditor.schedulingAlertInfo_=new EventInfo('Scheduling delay','Work to produce this frame was descheduled for several milliseconds, contributing to jank. Ensure that code on the UI thread doesn\'t block on work being done on other threads, and that background threads (doing e.g. network or bitmap loading) are running at android.os.Process#THREAD_PRIORITY_BACKGROUND or lower so they are less likely to interrupt the UI thread. These background threads should show up with a priority number of 130 or higher in the scheduling section under the Kernel process.');AndroidAuditor.getSchedulingAlert_=function(frame){var totalDuration=0;var totalStats={};frame.threadTimeRanges.forEach(function(ttr){var stats=ttr.thread.getSchedulingStatsForRange(ttr.start,ttr.end);tr.b.iterItems(stats,function(key,value){if(!(key in totalStats))
-totalStats[key]=0;totalStats[key]+=value;totalDuration+=value;});});if(!(SCHEDULING_STATE.RUNNING in totalStats)||totalDuration==0||totalDuration-totalStats[SCHEDULING_STATE.RUNNING]<3)
-return;var args={};tr.b.iterItems(totalStats,function(key,value){if(key===SCHEDULING_STATE.RUNNABLE)
-key='Not scheduled, but runnable';else if(key===SCHEDULING_STATE.UNINTR_SLEEP)
-key='Blocking I/O delay';args[key]=new ScalarNumeric(timeDurationInMs,value);});return new Alert(AndroidAuditor.schedulingAlertInfo_,frame.start,[frame],args);};AndroidAuditor.prototype={__proto__:Auditor.prototype,renameAndSort_:function(){this.model.kernel.important=false;this.model.getAllProcesses().forEach(function(process){if(this.helper.surfaceFlinger&&process==this.helper.surfaceFlinger.process){if(!process.name)
-process.name='SurfaceFlinger';process.sortIndex=Number.NEGATIVE_INFINITY;process.important=false;return;}
-var uiThread=process.getThread(process.pid);if(!process.name&&uiThread&&uiThread.name){if(/^ndroid\./.test(uiThread.name))
-uiThread.name='a'+uiThread.name;process.name=uiThread.name;uiThread.name='UI Thread';}
-process.sortIndex=0;for(var tid in process.threads){process.sortIndex-=process.threads[tid].sliceGroup.slices.length;}},this);this.model.getAllThreads().forEach(function(thread){if(thread.tid==thread.parent.pid)
-thread.sortIndex=-3;if(thread.name=='RenderThread')
-thread.sortIndex=-2;if(/^hwuiTask/.test(thread.name))
-thread.sortIndex=-1;});},pushFramesAndJudgeJank_:function(){var badFramesObserved=0;var framesObserved=0;var surfaceFlinger=this.helper.surfaceFlinger;this.helper.apps.forEach(function(app){app.process.frames=app.getFrames();app.process.frames.forEach(function(frame){if(frame.totalDuration>EXPECTED_FRAME_TIME_MS*2){badFramesObserved+=2;frame.perfClass=FRAME_PERF_CLASS.TERRIBLE;}else if(frame.totalDuration>EXPECTED_FRAME_TIME_MS||frameMissedDeadline(frame)){badFramesObserved++;frame.perfClass=FRAME_PERF_CLASS.BAD;}else{frame.perfClass=FRAME_PERF_CLASS.GOOD;}});framesObserved+=app.process.frames.length;});if(framesObserved){var portionBad=badFramesObserved/framesObserved;if(portionBad>0.3)
-this.model.faviconHue='red';else if(portionBad>0.05)
-this.model.faviconHue='yellow';else
-this.model.faviconHue='green';}},pushEventInfo_:function(){var appAnnotator=new AppAnnotator();this.helper.apps.forEach(function(app){if(app.uiThread)
-appAnnotator.applyEventInfos(app.uiThread.sliceGroup);if(app.renderThread)
-appAnnotator.applyEventInfos(app.renderThread.sliceGroup);});},runAnnotate:function(){if(!this.helper)
-return;this.renameAndSort_();this.pushFramesAndJudgeJank_();this.pushEventInfo_();this.helper.iterateImportantSlices(function(slice){slice.important=true;});},runAudit:function(){if(!this.helper)
-return;var alerts=this.model.alerts;this.helper.apps.forEach(function(app){app.getFrames().forEach(function(frame){alerts.push.apply(alerts,AndroidAuditor.getSaveLayerAlerts_(frame));if(frame.perfClass==FRAME_PERF_CLASS.NEUTRAL||frame.perfClass==FRAME_PERF_CLASS.GOOD)
-return;var alert=AndroidAuditor.getPathAlert_(frame);if(alert)
-alerts.push(alert);var alert=AndroidAuditor.getUploadAlert_(frame);if(alert)
-alerts.push(alert);var alert=AndroidAuditor.getListViewAlert_(frame);if(alert)
-alerts.push(alert);var alert=AndroidAuditor.getMeasureLayoutAlert_(frame);if(alert)
-alerts.push(alert);var alert=AndroidAuditor.getViewDrawAlert_(frame);if(alert)
-alerts.push(alert);var alert=AndroidAuditor.getBlockingGcAlert_(frame);if(alert)
-alerts.push(alert);var alert=AndroidAuditor.getLockContentionAlert_(frame);if(alert)
-alerts.push(alert);var alert=AndroidAuditor.getSchedulingAlert_(frame);if(alert)
-alerts.push(alert);});},this);this.addRenderingInteractionRecords();this.addInputInteractionRecords();},addRenderingInteractionRecords:function(){var events=[];this.helper.apps.forEach(function(app){events.push.apply(events,app.getAnimationAsyncSlices());events.push.apply(events,app.getFrames());});var mergerFunction=function(events){var ir=new tr.model.um.ResponseExpectation(this.model,'Rendering',events[0].min,events[events.length-1].max-events[0].min);this.model.userModel.expectations.push(ir);}.bind(this);tr.b.mergeRanges(tr.b.convertEventsToRanges(events),30,mergerFunction);},addInputInteractionRecords:function(){var inputSamples=[];this.helper.apps.forEach(function(app){inputSamples.push.apply(inputSamples,app.getInputSamples());});var mergerFunction=function(events){var ir=new tr.model.um.ResponseExpectation(this.model,'Input',events[0].min,events[events.length-1].max-events[0].min);this.model.userModel.expectations.push(ir);}.bind(this);var inputRanges=inputSamples.map(function(sample){return tr.b.Range.fromExplicitRange(sample.timestamp,sample.timestamp);});tr.b.mergeRanges(inputRanges,30,mergerFunction);}};Auditor.register(AndroidAuditor);function AppAnnotator(){this.titleInfoLookup=new Map();this.titleParentLookup=new Map();this.build_();}
-AppAnnotator.prototype={build_:function(){var registerEventInfo=function(dict){this.titleInfoLookup.set(dict.title,new EventInfo(dict.title,dict.description,dict.docLinks));if(dict.parents)
-this.titleParentLookup.set(dict.title,dict.parents);}.bind(this);registerEventInfo({title:'inflate',description:'Constructing a View hierarchy from pre-processed XML via LayoutInflater#layout. This includes constructing all of the View objects in the hierarchy, and applying styled attributes.'});registerEventInfo({title:'obtainView',description:'Adapter#getView() called to bind content to a recycled View that is being presented.'});registerEventInfo({title:'setupListItem',description:'Attached a newly-bound, recycled View to its parent ListView.'});registerEventInfo({title:'setupGridItem',description:'Attached a newly-bound, recycled View to its parent GridView.'});var choreographerLinks=new DocLinkBuilder().addDacRef('Choreographer','android/view/Choreographer.html').build();registerEventInfo({title:'Choreographer#doFrame',docLinks:choreographerLinks,description:'Choreographer executes frame callbacks for inputs, animations, and rendering traversals. When this work is done, a frame will be presented to the user.'});registerEventInfo({title:'input',parents:['Choreographer#doFrame'],docLinks:choreographerLinks,description:'Input callbacks are processed. This generally encompasses dispatching input to Views, as well as any work the Views do to process this input/gesture.'});registerEventInfo({title:'animation',parents:['Choreographer#doFrame'],docLinks:choreographerLinks,description:'Animation callbacks are processed. This is generally minimal work, as animations determine progress for the frame, and push new state to animated objects (such as setting View properties).'});registerEventInfo({title:'traversals',parents:['Choreographer#doFrame'],docLinks:choreographerLinks,description:'Primary draw traversals. This is the primary traversal of the View hierarchy, including layout and draw passes.'});var traversalParents=['Choreographer#doFrame','performTraversals'];var layoutLinks=new DocLinkBuilder().addDacRef('View#Layout','android/view/View.html#Layout').build();registerEventInfo({title:'performTraversals',description:'A drawing traversal of the View hierarchy, comprised of all layout and drawing needed to produce the frame.'});registerEventInfo({title:'measure',parents:traversalParents,docLinks:layoutLinks,description:'First of two phases in view hierarchy layout. Views are asked to size themselves according to constraints supplied by their parent. Some ViewGroups may measure a child more than once to help satisfy their own constraints. Nesting ViewGroups that measure children more than once can lead to excessive and repeated work.'});registerEventInfo({title:'layout',parents:traversalParents,docLinks:layoutLinks,description:'Second of two phases in view hierarchy layout, repositioning content and child Views into their new locations.'});var drawString='Draw pass over the View hierarchy. Every invalidated View will have its drawing commands recorded. On Android versions prior to Lollipop, this would also include the issuing of draw commands to the GPU. Starting with Lollipop, it only includes the recording of commands, and syncing that information to the RenderThread.';registerEventInfo({title:'draw',parents:traversalParents,description:drawString});var recordString='Every invalidated View\'s drawing commands are recorded. Each will have View#draw() called, and is passed a Canvas that will record and store its drawing commands until it is next invalidated/rerecorded.';registerEventInfo({title:'getDisplayList',parents:['draw'],description:recordString});registerEventInfo({title:'Record View#draw()',parents:['draw'],description:recordString});registerEventInfo({title:'drawDisplayList',parents:['draw'],description:'Execution of recorded draw commands to generate a frame. This represents the actual formation and issuing of drawing commands to the GPU. On Android L and higher devices, this work is done on a dedicated RenderThread, instead of on the UI Thread.'});registerEventInfo({title:'DrawFrame',description:'RenderThread portion of the standard UI/RenderThread split frame. This represents the actual formation and issuing of drawing commands to the GPU.'});registerEventInfo({title:'doFrame',description:'RenderThread animation frame. Represents drawing work done by the RenderThread on a frame where the UI thread did not produce new drawing content.'});registerEventInfo({title:'syncFrameState',description:'Sync stage between the UI thread and the RenderThread, where the UI thread hands off a frame (including information about modified Views). Time in this method primarily consists of uploading modified Bitmaps to the GPU. After this sync is completed, the UI thread is unblocked, and the RenderThread starts to render the frame.'});registerEventInfo({title:'flush drawing commands',description:'Issuing the now complete drawing commands to the GPU.'});registerEventInfo({title:'eglSwapBuffers',description:'Complete GPU rendering of the frame.'});registerEventInfo({title:'RV Scroll',description:'RecyclerView is calculating a scroll. If there are too many of these in Systrace, some Views inside RecyclerView might be causing it. Try to avoid using EditText, focusable views or handle them with care.'});registerEventInfo({title:'RV OnLayout',description:'OnLayout has been called by the View system. If this shows up too many times in Systrace, make sure the children of RecyclerView do not update themselves directly. This will cause a full re-layout but when it happens via the Adapter notifyItemChanged, RecyclerView can avoid full layout calculation.'});registerEventInfo({title:'RV FullInvalidate',description:'NotifyDataSetChanged or equal has been called. If this is taking a long time, try sending granular notify adapter changes instead of just calling notifyDataSetChanged or setAdapter / swapAdapter. Adding stable ids to your adapter might help.'});registerEventInfo({title:'RV PartialInvalidate',description:'RecyclerView is rebinding a View. If this is taking a lot of time, consider optimizing your layout or make sure you are not doing extra operations in onBindViewHolder call.'});registerEventInfo({title:'RV OnBindView',description:'RecyclerView is rebinding a View. If this is taking a lot of time, consider optimizing your layout or make sure you are not doing extra operations in onBindViewHolder call.'});registerEventInfo({title:'RV CreateView',description:'RecyclerView is creating a new View. If too many of these are present: 1) There might be a problem in Recycling (e.g. custom Animations that set transient state and prevent recycling or ItemAnimator not implementing the contract properly. See Adapter#onFailedToRecycleView(ViewHolder). 2) There may be too many item view types. Try merging them. 3) There might be too many itemChange animations and not enough space in RecyclerPool. Try increasing your pool size and item cache size.'});registerEventInfo({title:'eglSwapBuffers',description:'The CPU has finished producing drawing commands, and is flushing drawing work to the GPU, and posting that buffer to the consumer (which is often SurfaceFlinger window composition). Once this is completed, the GPU can produce the frame content without any involvement from the CPU.'});},applyEventInfosRecursive_:function(parentNames,slice){var checkExpectedParentNames=function(expectedParentNames){if(!expectedParentNames)
-return true;return expectedParentNames.some(function(name){return parentNames.has(name);});};if(this.titleInfoLookup.has(slice.title)){if(checkExpectedParentNames(this.titleParentLookup.get(slice.title)))
-slice.info=this.titleInfoLookup.get(slice.title);}
-if(slice.subSlices.length>0){if(!parentNames.has(slice.title))
-parentNames.set(slice.title,0);parentNames.set(slice.title,parentNames.get(slice.title)+1);slice.subSlices.forEach(function(subSlice){this.applyEventInfosRecursive_(parentNames,subSlice);},this);parentNames.set(slice.title,parentNames.get(slice.title)-1);if(parentNames.get(slice.title)==0)
-delete parentNames[slice.title];}},applyEventInfos:function(sliceGroup){sliceGroup.topLevelSlices.forEach(function(slice){this.applyEventInfosRecursive_(new Map(),slice);},this);}};return{AndroidAuditor:AndroidAuditor};});'use strict';tr.exportTo('tr.model',function(){function ObjectSnapshot(objectInstance,ts,args){tr.model.Event.call(this);this.objectInstance=objectInstance;this.ts=ts;this.args=args;}
+DocLinkBuilder.prototype={addAppVideo:function(name,videoId){this.docLinks.push({label:'Video Link',textContent:('Android Performance Patterns: '+name),href:'https://www.youtube.com/watch?list=PLWz5rJ2EKKc9CBxr3BVjPTPoDPLdPIFCE&v='+videoId});return this;},addDacRef:function(name,link){this.docLinks.push({label:'Doc Link',textContent:(name+' documentation'),href:'https://developer.android.com/reference/'+link});return this;},build:function(){return this.docLinks;}};function AndroidAuditor(model){Auditor.call(this,model);var helper=model.getOrCreateHelper(AndroidModelHelper);if(helper.apps.length||helper.surfaceFlinger){this.helper=helper;}}
+AndroidAuditor.viewAlphaAlertInfo_=new EventInfo('Inefficient View alpha usage','Setting an alpha between 0 and 1 has significant performance costs, if one of the fast alpha paths is not used.',new DocLinkBuilder().addAppVideo('Hidden Cost of Transparency','wIy8g8yNhNk').addDacRef('View#setAlpha()','android/view/View.html#setAlpha(float)').build());AndroidAuditor.saveLayerAlertInfo_=new EventInfo('Expensive rendering with Canvas#saveLayer()','Canvas#saveLayer() incurs extremely high rendering cost. They disrupt the rendering pipeline when drawn, forcing a flush of drawing content. Instead use View hardware layers, or static Bitmaps. This enables the offscreen buffers to be reused in between frames, and avoids the disruptive render target switch.',new DocLinkBuilder().addAppVideo('Hidden Cost of Transparency','wIy8g8yNhNk').addDacRef('Canvas#saveLayerAlpha()','android/graphics/Canvas.html#saveLayerAlpha(android.graphics.RectF, int, int)').build());AndroidAuditor.getSaveLayerAlerts_=function(frame){var badAlphaRegEx=/^(.+) alpha caused (unclipped )?saveLayer (\d+)x(\d+)$/;var saveLayerRegEx=/^(unclipped )?saveLayer (\d+)x(\d+)$/;var ret=[];var events=[];frame.associatedEvents.forEach(function(slice){var match=badAlphaRegEx.exec(slice.title);if(match){var args={'view name':match[1],'width':parseInt(match[3]),'height':parseInt(match[4])};ret.push(new Alert(AndroidAuditor.viewAlphaAlertInfo_,slice.start,[slice],args));}else if(saveLayerRegEx.test(slice.title)){events.push(slice);}},this);if(events.length>ret.length){var unclippedSeen=Statistics.sum(events,function(slice){return saveLayerRegEx.exec(slice.title)[1]?1:0;});var clippedSeen=events.length-unclippedSeen;var earliestStart=Statistics.min(events,function(slice){return slice.start;});var args={'Unclipped saveLayer count (especially bad!)':unclippedSeen,'Clipped saveLayer count':clippedSeen};events.push(frame);ret.push(new Alert(AndroidAuditor.saveLayerAlertInfo_,earliestStart,events,args));}
+return ret;};AndroidAuditor.pathAlertInfo_=new EventInfo('Path texture churn','Paths are drawn with a mask texture, so when a path is modified / newly drawn, that texture must be generated and uploaded to the GPU. Ensure that you cache paths between frames and do not unnecessarily call Path#reset(). You can cut down on this cost by sharing Path object instances between drawables/views.');AndroidAuditor.getPathAlert_=function(frame){var uploadRegEx=/^Generate Path Texture$/;var events=frame.associatedEvents.filter(function(event){return event.title==='Generate Path Texture';});var start=Statistics.min(events,getStart);var duration=Statistics.sum(events,getDuration);if(duration<3)return undefined;events.push(frame);return new Alert(AndroidAuditor.pathAlertInfo_,start,events,{'Time spent':new Scalar(timeDurationInMs,duration)});};AndroidAuditor.uploadAlertInfo_=new EventInfo('Expensive Bitmap uploads','Bitmaps that have been modified / newly drawn must be uploaded to the GPU. Since this is expensive if the total number of pixels uploaded is large, reduce the amount of Bitmap churn in this animation/context, per frame.');AndroidAuditor.getUploadAlert_=function(frame){var uploadRegEx=/^Upload (\d+)x(\d+) Texture$/;var events=[];var start=Number.POSITIVE_INFINITY;var duration=0;var pixelsUploaded=0;frame.associatedEvents.forEach(function(event){var match=uploadRegEx.exec(event.title);if(match){events.push(event);start=Math.min(start,event.start);duration+=event.duration;pixelsUploaded+=parseInt(match[1])*parseInt(match[2]);}});if(events.length===0||duration<3)return undefined;var mPixels=(pixelsUploaded/1000000).toFixed(2)+' million';var args={'Pixels uploaded':mPixels,'Time spent':new Scalar(timeDurationInMs,duration)};events.push(frame);return new Alert(AndroidAuditor.uploadAlertInfo_,start,events,args);};AndroidAuditor.ListViewInflateAlertInfo_=new EventInfo('Inflation during ListView recycling','ListView item recycling involved inflating views. Ensure your Adapter#getView() recycles the incoming View, instead of constructing a new one.');AndroidAuditor.ListViewBindAlertInfo_=new EventInfo('Inefficient ListView recycling/rebinding','ListView recycling taking too much time per frame. Ensure your Adapter#getView() binds data efficiently.');AndroidAuditor.getListViewAlert_=function(frame){var events=frame.associatedEvents.filter(function(event){return event.title==='obtainView'||event.title==='setupListItem';});var duration=Statistics.sum(events,getCpuDuration);if(events.length===0||duration<3)return undefined;var hasInflation=false;for(var event of events){if(event.findDescendentSlice('inflate')){hasInflation=true;}}
+var start=Statistics.min(events,getStart);var args={'Time spent':new Scalar(timeDurationInMs,duration)};args['ListView items '+(hasInflation?'inflated':'rebound')]=events.length/2;var eventInfo=hasInflation?AndroidAuditor.ListViewInflateAlertInfo_:AndroidAuditor.ListViewBindAlertInfo_;events.push(frame);return new Alert(eventInfo,start,events,args);};AndroidAuditor.measureLayoutAlertInfo_=new EventInfo('Expensive measure/layout pass','Measure/Layout took a significant time, contributing to jank. Avoid triggering layout during animations.',new DocLinkBuilder().addAppVideo('Invalidations, Layouts, and Performance','we6poP0kw6E').build());AndroidAuditor.getMeasureLayoutAlert_=function(frame){var events=frame.associatedEvents.filter(function(event){return event.title==='measure'||event.title==='layout';});var duration=Statistics.sum(events,getCpuDuration);if(events.length===0||duration<3)return undefined;var start=Statistics.min(events,getStart);events.push(frame);return new Alert(AndroidAuditor.measureLayoutAlertInfo_,start,events,{'Time spent':new Scalar(timeDurationInMs,duration)});};AndroidAuditor.viewDrawAlertInfo_=new EventInfo('Long View#draw()','Recording the drawing commands of invalidated Views took a long time. Avoid significant work in View or Drawable custom drawing, especially allocations or drawing to Bitmaps.',new DocLinkBuilder().addAppVideo('Invalidations, Layouts, and Performance','we6poP0kw6E').addAppVideo('Avoiding Allocations in onDraw()','HAK5acHQ53E').build());AndroidAuditor.getViewDrawAlert_=function(frame){var slice=undefined;for(var event of frame.associatedEvents){if(event.title==='getDisplayList'||event.title==='Record View#draw()'){slice=event;break;}}
+if(!slice||getCpuDuration(slice)<3)return undefined;return new Alert(AndroidAuditor.viewDrawAlertInfo_,slice.start,[slice,frame],{'Time spent':new Scalar(timeDurationInMs,getCpuDuration(slice))});};AndroidAuditor.blockingGcAlertInfo_=new EventInfo('Blocking Garbage Collection','Blocking GCs are caused by object churn, and made worse by having large numbers of objects in the heap. Avoid allocating objects during animations/scrolling, and recycle Bitmaps to avoid triggering garbage collection.',new DocLinkBuilder().addAppVideo('Garbage Collection in Android','pzfzz50W5Uo').addAppVideo('Avoiding Allocations in onDraw()','HAK5acHQ53E').build());AndroidAuditor.getBlockingGcAlert_=function(frame){var events=frame.associatedEvents.filter(function(event){return event.title==='DVM Suspend'||event.title==='GC: Wait For Concurrent';});var blockedDuration=Statistics.sum(events,getDuration);if(blockedDuration<3)return undefined;var start=Statistics.min(events,getStart);events.push(frame);return new Alert(AndroidAuditor.blockingGcAlertInfo_,start,events,{'Blocked duration':new Scalar(timeDurationInMs,blockedDuration)});};AndroidAuditor.lockContentionAlertInfo_=new EventInfo('Lock contention','UI thread lock contention is caused when another thread holds a lock that the UI thread is trying to use. UI thread progress is blocked until the lock is released. Inspect locking done within the UI thread, and ensure critical sections are short.');AndroidAuditor.getLockContentionAlert_=function(frame){var events=frame.associatedEvents.filter(function(event){return/^Lock Contention on /.test(event.title);});var blockedDuration=Statistics.sum(events,getDuration);if(blockedDuration<1)return undefined;var start=Statistics.min(events,getStart);events.push(frame);return new Alert(AndroidAuditor.lockContentionAlertInfo_,start,events,{'Blocked duration':new Scalar(timeDurationInMs,blockedDuration)});};AndroidAuditor.schedulingAlertInfo_=new EventInfo('Scheduling delay','Work to produce this frame was descheduled for several milliseconds, contributing to jank. Ensure that code on the UI thread doesn\'t block on work being done on other threads, and that background threads (doing e.g. network or bitmap loading) are running at android.os.Process#THREAD_PRIORITY_BACKGROUND or lower so they are less likely to interrupt the UI thread. These background threads should show up with a priority number of 130 or higher in the scheduling section under the Kernel process.');AndroidAuditor.getSchedulingAlert_=function(frame){var totalDuration=0;var totalStats={};for(var ttr of frame.threadTimeRanges){var stats=ttr.thread.getSchedulingStatsForRange(ttr.start,ttr.end);for(var[key,value]of Object.entries(stats)){if(!(key in totalStats)){totalStats[key]=0;}
+totalStats[key]+=value;totalDuration+=value;}}
+if(!(SCHEDULING_STATE.RUNNING in totalStats)||totalDuration===0||totalDuration-totalStats[SCHEDULING_STATE.RUNNING]<3){return;}
+var args={};for(var[key,value]of Object.entries(totalStats)){if(key===SCHEDULING_STATE.RUNNABLE){key='Not scheduled, but runnable';}else if(key===SCHEDULING_STATE.UNINTR_SLEEP){key='Blocking I/O delay';}
+args[key]=new Scalar(timeDurationInMs,value);}
+return new Alert(AndroidAuditor.schedulingAlertInfo_,frame.start,[frame],args);};AndroidAuditor.prototype={__proto__:Auditor.prototype,renameAndSort_:function(){this.model.kernel.important=false;this.model.getAllProcesses().forEach(function(process){if(this.helper.surfaceFlinger&&process===this.helper.surfaceFlinger.process){if(!process.name){process.name='SurfaceFlinger';}
+process.sortIndex=Number.NEGATIVE_INFINITY;process.important=false;return;}
+var uiThread=process.getThread(process.pid);if(!process.name&&uiThread&&uiThread.name){if(/^ndroid\./.test(uiThread.name)){uiThread.name='a'+uiThread.name;}
+process.name=uiThread.name;uiThread.name='UI Thread';}
+process.sortIndex=0;for(var tid in process.threads){process.sortIndex-=process.threads[tid].sliceGroup.slices.length;}},this);this.model.getAllThreads().forEach(function(thread){if(thread.tid===thread.parent.pid){thread.sortIndex=-3;}
+if(thread.name==='RenderThread'){thread.sortIndex=-2;}
+if(/^hwuiTask/.test(thread.name)){thread.sortIndex=-1;}});},pushFramesAndJudgeJank_:function(){var badFramesObserved=0;var framesObserved=0;var surfaceFlinger=this.helper.surfaceFlinger;this.helper.apps.forEach(function(app){app.process.frames=app.getFrames();app.process.frames.forEach(function(frame){if(frame.totalDuration>EXPECTED_FRAME_TIME_MS*2){badFramesObserved+=2;frame.perfClass=FRAME_PERF_CLASS.TERRIBLE;}else if(frame.totalDuration>EXPECTED_FRAME_TIME_MS||frameMissedDeadline(frame)){badFramesObserved++;frame.perfClass=FRAME_PERF_CLASS.BAD;}else{frame.perfClass=FRAME_PERF_CLASS.GOOD;}});framesObserved+=app.process.frames.length;});if(framesObserved){var portionBad=badFramesObserved/framesObserved;if(portionBad>0.3){this.model.faviconHue='red';}else if(portionBad>0.05){this.model.faviconHue='yellow';}else{this.model.faviconHue='green';}}},pushEventInfo_:function(){var appAnnotator=new AppAnnotator();this.helper.apps.forEach(function(app){if(app.uiThread){appAnnotator.applyEventInfos(app.uiThread.sliceGroup);}
+if(app.renderThread){appAnnotator.applyEventInfos(app.renderThread.sliceGroup);}});},runAnnotate:function(){if(!this.helper)return;this.renameAndSort_();this.pushFramesAndJudgeJank_();this.pushEventInfo_();this.helper.iterateImportantSlices(function(slice){slice.important=true;});},runAudit:function(){if(!this.helper)return;var alerts=this.model.alerts;this.helper.apps.forEach(function(app){app.getFrames().forEach(function(frame){alerts.push.apply(alerts,AndroidAuditor.getSaveLayerAlerts_(frame));if(frame.perfClass===FRAME_PERF_CLASS.NEUTRAL||frame.perfClass===FRAME_PERF_CLASS.GOOD){return;}
+var alert=AndroidAuditor.getPathAlert_(frame);if(alert)alerts.push(alert);var alert=AndroidAuditor.getUploadAlert_(frame);if(alert)alerts.push(alert);var alert=AndroidAuditor.getListViewAlert_(frame);if(alert)alerts.push(alert);var alert=AndroidAuditor.getMeasureLayoutAlert_(frame);if(alert)alerts.push(alert);var alert=AndroidAuditor.getViewDrawAlert_(frame);if(alert)alerts.push(alert);var alert=AndroidAuditor.getBlockingGcAlert_(frame);if(alert)alerts.push(alert);var alert=AndroidAuditor.getLockContentionAlert_(frame);if(alert)alerts.push(alert);var alert=AndroidAuditor.getSchedulingAlert_(frame);if(alert)alerts.push(alert);});},this);this.addRenderingInteractionRecords();this.addInputInteractionRecords();},addRenderingInteractionRecords:function(){var events=[];this.helper.apps.forEach(function(app){events.push.apply(events,app.getAnimationAsyncSlices());events.push.apply(events,app.getFrames());});var mergerFunction=function(events){var ir=new tr.model.um.ResponseExpectation(this.model,'Rendering',events[0].min,events[events.length-1].max-events[0].min);this.model.userModel.expectations.push(ir);}.bind(this);tr.b.math.mergeRanges(tr.b.math.convertEventsToRanges(events),30,mergerFunction);},addInputInteractionRecords:function(){var inputSamples=[];this.helper.apps.forEach(function(app){inputSamples.push.apply(inputSamples,app.getInputSamples());});var mergerFunction=function(events){var ir=new tr.model.um.ResponseExpectation(this.model,'Input',events[0].min,events[events.length-1].max-events[0].min);this.model.userModel.expectations.push(ir);}.bind(this);var inputRanges=inputSamples.map(function(sample){return tr.b.math.Range.fromExplicitRange(sample.timestamp,sample.timestamp);});tr.b.math.mergeRanges(inputRanges,30,mergerFunction);}};Auditor.register(AndroidAuditor);function AppAnnotator(){this.titleInfoLookup=new Map();this.titleParentLookup=new Map();this.build_();}
+AppAnnotator.prototype={build_:function(){var registerEventInfo=function(dict){this.titleInfoLookup.set(dict.title,new EventInfo(dict.title,dict.description,dict.docLinks));if(dict.parents){this.titleParentLookup.set(dict.title,dict.parents);}}.bind(this);registerEventInfo({title:'inflate',description:'Constructing a View hierarchy from pre-processed XML via LayoutInflater#layout. This includes constructing all of the View objects in the hierarchy, and applying styled attributes.'});registerEventInfo({title:'obtainView',description:'Adapter#getView() called to bind content to a recycled View that is being presented.'});registerEventInfo({title:'setupListItem',description:'Attached a newly-bound, recycled View to its parent ListView.'});registerEventInfo({title:'setupGridItem',description:'Attached a newly-bound, recycled View to its parent GridView.'});var choreographerLinks=new DocLinkBuilder().addDacRef('Choreographer','android/view/Choreographer.html').build();registerEventInfo({title:'Choreographer#doFrame',docLinks:choreographerLinks,description:'Choreographer executes frame callbacks for inputs, animations, and rendering traversals. When this work is done, a frame will be presented to the user.'});registerEventInfo({title:'input',parents:['Choreographer#doFrame'],docLinks:choreographerLinks,description:'Input callbacks are processed. This generally encompasses dispatching input to Views, as well as any work the Views do to process this input/gesture.'});registerEventInfo({title:'animation',parents:['Choreographer#doFrame'],docLinks:choreographerLinks,description:'Animation callbacks are processed. This is generally minimal work, as animations determine progress for the frame, and push new state to animated objects (such as setting View properties).'});registerEventInfo({title:'traversals',parents:['Choreographer#doFrame'],docLinks:choreographerLinks,description:'Primary draw traversals. This is the primary traversal of the View hierarchy, including layout and draw passes.'});var traversalParents=['Choreographer#doFrame','performTraversals'];var layoutLinks=new DocLinkBuilder().addDacRef('View#Layout','android/view/View.html#Layout').build();registerEventInfo({title:'performTraversals',description:'A drawing traversal of the View hierarchy, comprised of all layout and drawing needed to produce the frame.'});registerEventInfo({title:'measure',parents:traversalParents,docLinks:layoutLinks,description:'First of two phases in view hierarchy layout. Views are asked to size themselves according to constraints supplied by their parent. Some ViewGroups may measure a child more than once to help satisfy their own constraints. Nesting ViewGroups that measure children more than once can lead to excessive and repeated work.'});registerEventInfo({title:'layout',parents:traversalParents,docLinks:layoutLinks,description:'Second of two phases in view hierarchy layout, repositioning content and child Views into their new locations.'});var drawString='Draw pass over the View hierarchy. Every invalidated View will have its drawing commands recorded. On Android versions prior to Lollipop, this would also include the issuing of draw commands to the GPU. Starting with Lollipop, it only includes the recording of commands, and syncing that information to the RenderThread.';registerEventInfo({title:'draw',parents:traversalParents,description:drawString});var recordString='Every invalidated View\'s drawing commands are recorded. Each will have View#draw() called, and is passed a Canvas that will record and store its drawing commands until it is next invalidated/rerecorded.';registerEventInfo({title:'getDisplayList',parents:['draw'],description:recordString});registerEventInfo({title:'Record View#draw()',parents:['draw'],description:recordString});registerEventInfo({title:'drawDisplayList',parents:['draw'],description:'Execution of recorded draw commands to generate a frame. This represents the actual formation and issuing of drawing commands to the GPU. On Android L and higher devices, this work is done on a dedicated RenderThread, instead of on the UI Thread.'});registerEventInfo({title:'DrawFrame',description:'RenderThread portion of the standard UI/RenderThread split frame. This represents the actual formation and issuing of drawing commands to the GPU.'});registerEventInfo({title:'doFrame',description:'RenderThread animation frame. Represents drawing work done by the RenderThread on a frame where the UI thread did not produce new drawing content.'});registerEventInfo({title:'syncFrameState',description:'Sync stage between the UI thread and the RenderThread, where the UI thread hands off a frame (including information about modified Views). Time in this method primarily consists of uploading modified Bitmaps to the GPU. After this sync is completed, the UI thread is unblocked, and the RenderThread starts to render the frame.'});registerEventInfo({title:'flush drawing commands',description:'Issuing the now complete drawing commands to the GPU.'});registerEventInfo({title:'eglSwapBuffers',description:'Complete GPU rendering of the frame.'});registerEventInfo({title:'RV Scroll',description:'RecyclerView is calculating a scroll. If there are too many of these in Systrace, some Views inside RecyclerView might be causing it. Try to avoid using EditText, focusable views or handle them with care.'});registerEventInfo({title:'RV OnLayout',description:'OnLayout has been called by the View system. If this shows up too many times in Systrace, make sure the children of RecyclerView do not update themselves directly. This will cause a full re-layout but when it happens via the Adapter notifyItemChanged, RecyclerView can avoid full layout calculation.'});registerEventInfo({title:'RV FullInvalidate',description:'NotifyDataSetChanged or equal has been called. If this is taking a long time, try sending granular notify adapter changes instead of just calling notifyDataSetChanged or setAdapter / swapAdapter. Adding stable ids to your adapter might help.'});registerEventInfo({title:'RV PartialInvalidate',description:'RecyclerView is rebinding a View. If this is taking a lot of time, consider optimizing your layout or make sure you are not doing extra operations in onBindViewHolder call.'});registerEventInfo({title:'RV OnBindView',description:'RecyclerView is rebinding a View. If this is taking a lot of time, consider optimizing your layout or make sure you are not doing extra operations in onBindViewHolder call.'});registerEventInfo({title:'RV CreateView',description:'RecyclerView is creating a new View. If too many of these are present: 1) There might be a problem in Recycling (e.g. custom Animations that set transient state and prevent recycling or ItemAnimator not implementing the contract properly. See Adapter#onFailedToRecycleView(ViewHolder). 2) There may be too many item view types. Try merging them. 3) There might be too many itemChange animations and not enough space in RecyclerPool. Try increasing your pool size and item cache size.'});registerEventInfo({title:'eglSwapBuffers',description:'The CPU has finished producing drawing commands, and is flushing drawing work to the GPU, and posting that buffer to the consumer (which is often SurfaceFlinger window composition). Once this is completed, the GPU can produce the frame content without any involvement from the CPU.'});},applyEventInfosRecursive_:function(parentNames,slice){var checkExpectedParentNames=function(expectedParentNames){if(!expectedParentNames)return true;return expectedParentNames.some(function(name){return parentNames.has(name);});};if(this.titleInfoLookup.has(slice.title)){if(checkExpectedParentNames(this.titleParentLookup.get(slice.title))){slice.info=this.titleInfoLookup.get(slice.title);}}
+if(slice.subSlices.length>0){if(!parentNames.has(slice.title)){parentNames.set(slice.title,0);}
+parentNames.set(slice.title,parentNames.get(slice.title)+1);slice.subSlices.forEach(function(subSlice){this.applyEventInfosRecursive_(parentNames,subSlice);},this);parentNames.set(slice.title,parentNames.get(slice.title)-1);if(parentNames.get(slice.title)===0){delete parentNames[slice.title];}}},applyEventInfos:function(sliceGroup){sliceGroup.topLevelSlices.forEach(function(slice){this.applyEventInfosRecursive_(new Map(),slice);},this);}};return{AndroidAuditor,};});'use strict';tr.exportTo('tr.model',function(){function ObjectSnapshot(objectInstance,ts,args){tr.model.Event.call(this);this.objectInstance=objectInstance;this.ts=ts;this.args=args;}
 ObjectSnapshot.prototype={__proto__:tr.model.Event.prototype,preInitialize:function(){},initialize:function(){},referencedAt:function(item,object,field){},addBoundsToRange:function(range){range.addValue(this.ts);},get userFriendlyName(){return'Snapshot of '+
 this.objectInstance.typeName+' '+
 this.objectInstance.id+' @ '+
-tr.b.Unit.byName.timeStampInMs.format(this.ts);}};tr.model.EventRegistry.register(ObjectSnapshot,{name:'objectSnapshot',pluralName:'objectSnapshots'});return{ObjectSnapshot:ObjectSnapshot};});'use strict';tr.exportTo('tr.model',function(){var ObjectSnapshot=tr.model.ObjectSnapshot;function ObjectInstance(parent,scopedId,category,name,creationTs,opt_baseTypeName){tr.model.Event.call(this);this.parent=parent;this.scopedId=scopedId;this.category=category;this.baseTypeName=opt_baseTypeName?opt_baseTypeName:name;this.name=name;this.creationTs=creationTs;this.creationTsWasExplicit=false;this.deletionTs=Number.MAX_VALUE;this.deletionTsWasExplicit=false;this.colorId=0;this.bounds=new tr.b.Range();this.snapshots=[];this.hasImplicitSnapshots=false;}
-ObjectInstance.prototype={__proto__:tr.model.Event.prototype,get typeName(){return this.name;},addBoundsToRange:function(range){range.addRange(this.bounds);},addSnapshot:function(ts,args,opt_name,opt_baseTypeName){if(ts<this.creationTs)
-throw new Error('Snapshots must be >= instance.creationTs');if(ts>=this.deletionTs)
-throw new Error('Snapshots cannot be added after '+'an objects deletion timestamp.');var lastSnapshot;if(this.snapshots.length>0){lastSnapshot=this.snapshots[this.snapshots.length-1];if(lastSnapshot.ts==ts)
-throw new Error('Snapshots already exists at this time!');if(ts<lastSnapshot.ts){throw new Error('Snapshots must be added in increasing timestamp order');}}
-if(opt_name&&(this.name!=opt_name)){if(!opt_baseTypeName)
-throw new Error('Must provide base type name for name update');if(this.baseTypeName!=opt_baseTypeName)
-throw new Error('Cannot update type name: base types dont match');this.name=opt_name;}
-var snapshotConstructor=tr.model.ObjectSnapshot.subTypes.getConstructor(this.category,this.name);var snapshot=new snapshotConstructor(this,ts,args);this.snapshots.push(snapshot);return snapshot;},wasDeleted:function(ts){var lastSnapshot;if(this.snapshots.length>0){lastSnapshot=this.snapshots[this.snapshots.length-1];if(lastSnapshot.ts>ts)
-throw new Error('Instance cannot be deleted at ts='+
-ts+'. A snapshot exists that is older.');}
-this.deletionTs=ts;this.deletionTsWasExplicit=true;},preInitialize:function(){for(var i=0;i<this.snapshots.length;i++)
-this.snapshots[i].preInitialize();},initialize:function(){for(var i=0;i<this.snapshots.length;i++)
-this.snapshots[i].initialize();},isAliveAt:function(ts){if(ts<this.creationTs&&this.creationTsWasExplicit)
-return false;if(ts>this.deletionTs)
-return false;return true;},getSnapshotAt:function(ts){if(ts<this.creationTs){if(this.creationTsWasExplicit)
-throw new Error('ts must be within lifetime of this instance');return this.snapshots[0];}
-if(ts>this.deletionTs)
-throw new Error('ts must be within lifetime of this instance');var snapshots=this.snapshots;var i=tr.b.findIndexInSortedIntervals(snapshots,function(snapshot){return snapshot.ts;},function(snapshot,i){if(i==snapshots.length-1)
-return snapshots[i].objectInstance.deletionTs;return snapshots[i+1].ts-snapshots[i].ts;},ts);if(i<0){return this.snapshots[0];}
-if(i>=this.snapshots.length)
-return this.snapshots[this.snapshots.length-1];return this.snapshots[i];},updateBounds:function(){this.bounds.reset();this.bounds.addValue(this.creationTs);if(this.deletionTs!=Number.MAX_VALUE)
-this.bounds.addValue(this.deletionTs);else if(this.snapshots.length>0)
-this.bounds.addValue(this.snapshots[this.snapshots.length-1].ts);},shiftTimestampsForward:function(amount){this.creationTs+=amount;if(this.deletionTs!=Number.MAX_VALUE)
-this.deletionTs+=amount;this.snapshots.forEach(function(snapshot){snapshot.ts+=amount;});},get userFriendlyName(){return this.typeName+' object '+this.scopedId;}};tr.model.EventRegistry.register(ObjectInstance,{name:'objectInstance',pluralName:'objectInstances'});return{ObjectInstance:ObjectInstance};});'use strict';tr.exportTo('tr.e.chrome',function(){var ObjectSnapshot=tr.model.ObjectSnapshot;var ObjectInstance=tr.model.ObjectInstance;function BlameContextSnapshot(){ObjectSnapshot.apply(this,arguments);}
-BlameContextSnapshot.prototype={__proto__:ObjectSnapshot.prototype,get parentContext(){if(this.args.parent instanceof BlameContextSnapshot)
-return this.args.parent;return undefined;},get userFriendlyName(){return'BlameContext';}};function BlameContextInstance(){ObjectInstance.apply(this,arguments);}
-BlameContextInstance.prototype={__proto__:ObjectInstance.prototype,get blameContextType(){throw new Error('Not implemented');}};return{BlameContextSnapshot:BlameContextSnapshot,BlameContextInstance:BlameContextInstance};});'use strict';tr.exportTo('tr.e.chrome',function(){var BlameContextSnapshot=tr.e.chrome.BlameContextSnapshot;var BlameContextInstance=tr.e.chrome.BlameContextInstance;function FrameTreeNodeSnapshot(){BlameContextSnapshot.apply(this,arguments);}
-FrameTreeNodeSnapshot.prototype={__proto__:BlameContextSnapshot.prototype,get renderFrame(){if(this.args.renderFrame instanceof tr.e.chrome.RenderFrameSnapshot)
-return this.args.renderFrame;return undefined;},get url(){return this.args.url;},get userFriendlyName(){return'FrameTreeNode';}};tr.model.ObjectSnapshot.subTypes.register(FrameTreeNodeSnapshot,{typeName:'FrameTreeNode'});function FrameTreeNodeInstance(){BlameContextInstance.apply(this,arguments);}
-FrameTreeNodeInstance.prototype={__proto__:BlameContextInstance.prototype,get blameContextType(){return'Frame';}};tr.model.ObjectInstance.subTypes.register(FrameTreeNodeInstance,{typeName:'FrameTreeNode'});return{FrameTreeNodeSnapshot:FrameTreeNodeSnapshot,FrameTreeNodeInstance:FrameTreeNodeInstance};});'use strict';tr.exportTo('tr.e.chrome',function(){var BlameContextSnapshot=tr.e.chrome.BlameContextSnapshot;var BlameContextInstance=tr.e.chrome.BlameContextInstance;function RenderFrameSnapshot(){BlameContextSnapshot.apply(this,arguments);}
-RenderFrameSnapshot.prototype={__proto__:BlameContextSnapshot.prototype,referencedAt:function(item,object,field){if(item instanceof tr.e.chrome.FrameTreeNodeSnapshot&&object===item.args&&field==='renderFrame'){this.args.frameTreeNode=item;}},get frameTreeNode(){if(this.args.frameTreeNode instanceof tr.e.chrome.FrameTreeNodeSnapshot)
-return this.args.frameTreeNode;return undefined;},get url(){if(this.frameTreeNode)
-return this.frameTreeNode.url;return undefined;},get userFriendlyName(){return'RenderFrame';}};tr.model.ObjectSnapshot.subTypes.register(RenderFrameSnapshot,{typeName:'RenderFrame'});function RenderFrameInstance(){BlameContextInstance.apply(this,arguments);}
-RenderFrameInstance.prototype={__proto__:BlameContextInstance.prototype,get blameContextType(){return'Frame';}};tr.model.ObjectInstance.subTypes.register(RenderFrameInstance,{typeName:'RenderFrame'});return{RenderFrameSnapshot:RenderFrameSnapshot,RenderFrameInstance:RenderFrameInstance};});'use strict';tr.exportTo('tr.e.chrome',function(){var BlameContextSnapshot=tr.e.chrome.BlameContextSnapshot;var BlameContextInstance=tr.e.chrome.BlameContextInstance;function TopLevelSnapshot(){BlameContextSnapshot.apply(this,arguments);}
+tr.b.Unit.byName.timeStampInMs.format(this.ts);}};tr.model.EventRegistry.register(ObjectSnapshot,{name:'objectSnapshot',pluralName:'objectSnapshots'});return{ObjectSnapshot,};});'use strict';tr.exportTo('tr.model',function(){var ObjectSnapshot=tr.model.ObjectSnapshot;function ObjectInstance(parent,scopedId,category,name,creationTs,opt_baseTypeName){tr.model.Event.call(this);this.parent=parent;this.scopedId=scopedId;this.category=category;this.baseTypeName=opt_baseTypeName?opt_baseTypeName:name;this.name=name;this.creationTs=creationTs;this.creationTsWasExplicit=false;this.deletionTs=Number.MAX_VALUE;this.deletionTsWasExplicit=false;this.colorId=0;this.bounds=new tr.b.math.Range();this.snapshots=[];this.hasImplicitSnapshots=false;}
+ObjectInstance.prototype={__proto__:tr.model.Event.prototype,get typeName(){return this.name;},addBoundsToRange:function(range){range.addRange(this.bounds);},addSnapshot:function(ts,args,opt_name,opt_baseTypeName){if(ts<this.creationTs){throw new Error('Snapshots must be >= instance.creationTs');}
+if(ts>=this.deletionTs){throw new Error('Snapshots cannot be added after '+'an objects deletion timestamp.');}
+var lastSnapshot;if(this.snapshots.length>0){lastSnapshot=this.snapshots[this.snapshots.length-1];if(lastSnapshot.ts===ts){throw new Error('Snapshots already exists at this time!');}
+if(ts<lastSnapshot.ts){throw new Error('Snapshots must be added in increasing timestamp order');}}
+if(opt_name&&(this.name!==opt_name)){if(!opt_baseTypeName){throw new Error('Must provide base type name for name update');}
+if(this.baseTypeName!==opt_baseTypeName){throw new Error('Cannot update type name: base types dont match');}
+this.name=opt_name;}
+var snapshotConstructor=tr.model.ObjectSnapshot.subTypes.getConstructor(this.category,this.name);var snapshot=new snapshotConstructor(this,ts,args);this.snapshots.push(snapshot);return snapshot;},wasDeleted:function(ts){var lastSnapshot;if(this.snapshots.length>0){lastSnapshot=this.snapshots[this.snapshots.length-1];if(lastSnapshot.ts>ts){throw new Error('Instance cannot be deleted at ts='+
+ts+'. A snapshot exists that is older.');}}
+this.deletionTs=ts;this.deletionTsWasExplicit=true;},preInitialize:function(){for(var i=0;i<this.snapshots.length;i++){this.snapshots[i].preInitialize();}},initialize:function(){for(var i=0;i<this.snapshots.length;i++){this.snapshots[i].initialize();}},isAliveAt:function(ts){if(ts<this.creationTs&&this.creationTsWasExplicit){return false;}
+if(ts>this.deletionTs){return false;}
+return true;},getSnapshotAt:function(ts){if(ts<this.creationTs){if(this.creationTsWasExplicit){throw new Error('ts must be within lifetime of this instance');}
+return this.snapshots[0];}
+if(ts>this.deletionTs){throw new Error('ts must be within lifetime of this instance');}
+var snapshots=this.snapshots;var i=tr.b.math.findIndexInSortedIntervals(snapshots,function(snapshot){return snapshot.ts;},function(snapshot,i){if(i===snapshots.length-1){return snapshots[i].objectInstance.deletionTs;}
+return snapshots[i+1].ts-snapshots[i].ts;},ts);if(i<0){return this.snapshots[0];}
+if(i>=this.snapshots.length){return this.snapshots[this.snapshots.length-1];}
+return this.snapshots[i];},updateBounds:function(){this.bounds.reset();this.bounds.addValue(this.creationTs);if(this.deletionTs!==Number.MAX_VALUE){this.bounds.addValue(this.deletionTs);}else if(this.snapshots.length>0){this.bounds.addValue(this.snapshots[this.snapshots.length-1].ts);}},shiftTimestampsForward:function(amount){this.creationTs+=amount;if(this.deletionTs!==Number.MAX_VALUE){this.deletionTs+=amount;}
+this.snapshots.forEach(function(snapshot){snapshot.ts+=amount;});},get userFriendlyName(){return this.typeName+' object '+this.scopedId;}};tr.model.EventRegistry.register(ObjectInstance,{name:'objectInstance',pluralName:'objectInstances'});return{ObjectInstance,};});'use strict';tr.exportTo('tr.e.chrome',function(){var ObjectSnapshot=tr.model.ObjectSnapshot;var ObjectInstance=tr.model.ObjectInstance;function BlameContextSnapshot(){ObjectSnapshot.apply(this,arguments);}
+BlameContextSnapshot.prototype={__proto__:ObjectSnapshot.prototype,get parentContext(){if(this.args.parent instanceof BlameContextSnapshot){return this.args.parent;}
+return undefined;},get userFriendlyName(){return'BlameContext';}};function BlameContextInstance(){ObjectInstance.apply(this,arguments);}
+BlameContextInstance.prototype={__proto__:ObjectInstance.prototype,get blameContextType(){throw new Error('Not implemented');}};return{BlameContextSnapshot,BlameContextInstance,};});'use strict';tr.exportTo('tr.e.chrome',function(){var BlameContextSnapshot=tr.e.chrome.BlameContextSnapshot;var BlameContextInstance=tr.e.chrome.BlameContextInstance;function FrameTreeNodeSnapshot(){BlameContextSnapshot.apply(this,arguments);}
+FrameTreeNodeSnapshot.prototype={__proto__:BlameContextSnapshot.prototype,get renderFrame(){if(this.args.renderFrame instanceof tr.e.chrome.RenderFrameSnapshot){return this.args.renderFrame;}
+return undefined;},get url(){return this.args.url;},get userFriendlyName(){return'FrameTreeNode';}};tr.model.ObjectSnapshot.subTypes.register(FrameTreeNodeSnapshot,{typeName:'FrameTreeNode'});function FrameTreeNodeInstance(){BlameContextInstance.apply(this,arguments);}
+FrameTreeNodeInstance.prototype={__proto__:BlameContextInstance.prototype,get blameContextType(){return'Frame';}};tr.model.ObjectInstance.subTypes.register(FrameTreeNodeInstance,{typeName:'FrameTreeNode'});return{FrameTreeNodeSnapshot,FrameTreeNodeInstance,};});'use strict';tr.exportTo('tr.e.chrome',function(){var BlameContextSnapshot=tr.e.chrome.BlameContextSnapshot;var BlameContextInstance=tr.e.chrome.BlameContextInstance;function RenderFrameSnapshot(){BlameContextSnapshot.apply(this,arguments);}
+RenderFrameSnapshot.prototype={__proto__:BlameContextSnapshot.prototype,referencedAt:function(item,object,field){if(item instanceof tr.e.chrome.FrameTreeNodeSnapshot&&object===item.args&&field==='renderFrame'){this.args.frameTreeNode=item;}},get frameTreeNode(){if(this.args.frameTreeNode instanceof
+tr.e.chrome.FrameTreeNodeSnapshot){return this.args.frameTreeNode;}
+return undefined;},get url(){if(this.frameTreeNode){return this.frameTreeNode.url;}
+return undefined;},get userFriendlyName(){return'RenderFrame';}};tr.model.ObjectSnapshot.subTypes.register(RenderFrameSnapshot,{typeName:'RenderFrame'});function RenderFrameInstance(){BlameContextInstance.apply(this,arguments);}
+RenderFrameInstance.prototype={__proto__:BlameContextInstance.prototype,get blameContextType(){return'Frame';}};tr.model.ObjectInstance.subTypes.register(RenderFrameInstance,{typeName:'RenderFrame'});return{RenderFrameSnapshot,RenderFrameInstance,};});'use strict';tr.exportTo('tr.e.chrome',function(){var BlameContextSnapshot=tr.e.chrome.BlameContextSnapshot;var BlameContextInstance=tr.e.chrome.BlameContextInstance;function TopLevelSnapshot(){BlameContextSnapshot.apply(this,arguments);}
 TopLevelSnapshot.prototype={__proto__:BlameContextSnapshot.prototype,get userFriendlyName(){return'TopLevel';}};tr.model.ObjectSnapshot.subTypes.register(TopLevelSnapshot,{typeName:'TopLevel'});function TopLevelInstance(){BlameContextInstance.apply(this,arguments);}
-TopLevelInstance.prototype={__proto__:BlameContextInstance.prototype,get blameContextType(){return'TopLevel';}};tr.model.ObjectInstance.subTypes.register(TopLevelInstance,{typeName:'TopLevel'});return{TopLevelSnapshot:TopLevelSnapshot,TopLevelInstance:TopLevelInstance};});'use strict';tr.exportTo('tr.model',function(){function AsyncSlice(category,title,colorId,start,args,duration,opt_isTopLevel,opt_cpuStart,opt_cpuDuration,opt_argsStripped){tr.model.TimedEvent.call(this,start);this.category=category||'';this.originalTitle=title;this.title=title;this.colorId=colorId;this.args=args;this.startStackFrame=undefined;this.endStackFrame=undefined;this.didNotFinish=false;this.important=false;this.subSlices=[];this.parentContainer_=undefined;this.id=undefined;this.startThread=undefined;this.endThread=undefined;this.cpuStart=undefined;this.cpuDuration=undefined;this.argsStripped=false;this.startStackFrame=undefined;this.endStackFrame=undefined;this.duration=duration;this.isTopLevel=(opt_isTopLevel===true);if(opt_cpuStart!==undefined)
-this.cpuStart=opt_cpuStart;if(opt_cpuDuration!==undefined)
-this.cpuDuration=opt_cpuDuration;if(opt_argsStripped!==undefined)
-this.argsStripped=opt_argsStripped;}
-AsyncSlice.prototype={__proto__:tr.model.TimedEvent.prototype,get analysisTypeName(){return this.title;},get parentContainer(){return this.parentContainer_;},set parentContainer(parentContainer){this.parentContainer_=parentContainer;for(var i=0;i<this.subSlices.length;i++){var subSlice=this.subSlices[i];if(subSlice.parentContainer===undefined)
-subSlice.parentContainer=parentContainer;}},get viewSubGroupTitle(){return this.title;},get userFriendlyName(){return'Async slice '+this.title+' at '+
+TopLevelInstance.prototype={__proto__:BlameContextInstance.prototype,get blameContextType(){return'TopLevel';}};tr.model.ObjectInstance.subTypes.register(TopLevelInstance,{typeName:'TopLevel'});return{TopLevelSnapshot,TopLevelInstance,};});'use strict';tr.exportTo('tr.model',function(){function AsyncSlice(category,title,colorId,start,args,duration,opt_isTopLevel,opt_cpuStart,opt_cpuDuration,opt_argsStripped){tr.model.TimedEvent.call(this,start);this.category=category||'';this.originalTitle=title;this.title=title;this.colorId=colorId;this.args=args;this.startStackFrame=undefined;this.endStackFrame=undefined;this.didNotFinish=false;this.important=false;this.subSlices=[];this.parentContainer_=undefined;this.id=undefined;this.startThread=undefined;this.endThread=undefined;this.cpuStart=undefined;this.cpuDuration=undefined;this.argsStripped=false;this.startStackFrame=undefined;this.endStackFrame=undefined;this.duration=duration;this.isTopLevel=(opt_isTopLevel===true);if(opt_cpuStart!==undefined){this.cpuStart=opt_cpuStart;}
+if(opt_cpuDuration!==undefined){this.cpuDuration=opt_cpuDuration;}
+if(opt_argsStripped!==undefined){this.argsStripped=opt_argsStripped;}}
+AsyncSlice.prototype={__proto__:tr.model.TimedEvent.prototype,get analysisTypeName(){return this.title;},get parentContainer(){return this.parentContainer_;},set parentContainer(parentContainer){this.parentContainer_=parentContainer;for(var i=0;i<this.subSlices.length;i++){var subSlice=this.subSlices[i];if(subSlice.parentContainer===undefined){subSlice.parentContainer=parentContainer;}}},get viewSubGroupTitle(){return this.title;},get userFriendlyName(){return'Async slice '+this.title+' at '+
 tr.b.Unit.byName.timeStampInMs.format(this.start);},get stableId(){var parentAsyncSliceGroup=this.parentContainer.asyncSliceGroup;return parentAsyncSliceGroup.stableId+'.'+
 parentAsyncSliceGroup.slices.indexOf(this);},findTopmostSlicesRelativeToThisSlice:function*(eventPredicate,opt_this){if(eventPredicate(this)){yield this;return;}
-for(var s of this.subSlices)
-yield*s.findTopmostSlicesRelativeToThisSlice(eventPredicate);},findDescendentSlice:function(targetTitle){if(!this.subSlices)
-return undefined;for(var i=0;i<this.subSlices.length;i++){if(this.subSlices[i].title==targetTitle)
-return this.subSlices[i];var slice=this.subSlices[i].findDescendentSlice(targetTitle);if(slice)return slice;}
-return undefined;},enumerateAllDescendents:function*(){for(var slice of this.subSlices)
-yield slice;for(var slice of this.subSlices)
-yield*slice.enumerateAllDescendents();},compareTo:function(that){return this.title.localeCompare(that.title);}};tr.model.EventRegistry.register(AsyncSlice,{name:'asyncSlice',pluralName:'asyncSlices'});return{AsyncSlice:AsyncSlice};});'use strict';tr.exportTo('tr.model.helpers',function(){var MAIN_FRAMETIME_TYPE='main_frametime_type';var IMPL_FRAMETIME_TYPE='impl_frametime_type';var MAIN_RENDERING_STATS='BenchmarkInstrumentation::MainThreadRenderingStats';var IMPL_RENDERING_STATS='BenchmarkInstrumentation::ImplThreadRenderingStats';function getSlicesIntersectingRange(rangeOfInterest,slices){var slicesInFilterRange=[];for(var i=0;i<slices.length;i++){var slice=slices[i];if(rangeOfInterest.intersectsExplicitRangeInclusive(slice.start,slice.end))
-slicesInFilterRange.push(slice);}
+for(var s of this.subSlices){yield*s.findTopmostSlicesRelativeToThisSlice(eventPredicate);}},findDescendentSlice:function(targetTitle){if(!this.subSlices)return undefined;for(var i=0;i<this.subSlices.length;i++){if(this.subSlices[i].title===targetTitle){return this.subSlices[i];}
+var slice=this.subSlices[i].findDescendentSlice(targetTitle);if(slice)return slice;}
+return undefined;},enumerateAllDescendents:function*(){for(var slice of this.subSlices){yield slice;}
+for(var slice of this.subSlices){yield*slice.enumerateAllDescendents();}},compareTo:function(that){return this.title.localeCompare(that.title);}};tr.model.EventRegistry.register(AsyncSlice,{name:'asyncSlice',pluralName:'asyncSlices'});return{AsyncSlice,};});'use strict';tr.exportTo('tr.model.helpers',function(){var MAIN_FRAMETIME_TYPE='main_frametime_type';var IMPL_FRAMETIME_TYPE='impl_frametime_type';var MAIN_RENDERING_STATS='BenchmarkInstrumentation::MainThreadRenderingStats';var IMPL_RENDERING_STATS='BenchmarkInstrumentation::ImplThreadRenderingStats';function getSlicesIntersectingRange(rangeOfInterest,slices){var slicesInFilterRange=[];for(var i=0;i<slices.length;i++){var slice=slices[i];if(rangeOfInterest.intersectsExplicitRangeInclusive(slice.start,slice.end)){slicesInFilterRange.push(slice);}}
 return slicesInFilterRange;}
-function ChromeProcessHelper(modelHelper,process){this.modelHelper=modelHelper;this.process=process;}
-ChromeProcessHelper.prototype={get pid(){return this.process.pid;},getFrameEventsInRange:function(frametimeType,range){var titleToGet=(frametimeType===MAIN_FRAMETIME_TYPE?MAIN_RENDERING_STATS:IMPL_RENDERING_STATS);var frameEvents=[];for(var event of this.process.getDescendantEvents())
-if(event.title===titleToGet)
-if(range.intersectsExplicitRangeInclusive(event.start,event.end))
-frameEvents.push(event);frameEvents.sort(function(a,b){return a.start-b.start});return frameEvents;}};function getFrametimeDataFromEvents(frameEvents){var frametimeData=[];for(var i=1;i<frameEvents.length;i++){var diff=frameEvents[i].start-frameEvents[i-1].start;frametimeData.push({'x':frameEvents[i].start,'frametime':diff});}
+function ChromeProcessHelper(modelHelper,process){this.modelHelper=modelHelper;this.process=process;this.telemetryInternalRanges_=undefined;}
+ChromeProcessHelper.prototype={get pid(){return this.process.pid;},isTelemetryInternalEvent:function(slice){if(this.telemetryInternalRanges_===undefined){this.findTelemetryInternalRanges_();}
+for(var range of this.telemetryInternalRanges_){if(range.containsExplicitRangeInclusive(slice.start,slice.end)){return true;}}
+return false;},findTelemetryInternalRanges_:function(){this.telemetryInternalRanges_=[];var start=0;for(var thread of Object.values(this.process.threads)){for(var slice of thread.asyncSliceGroup.getDescendantEvents()){if(/^telemetry\.internal\.[^.]*\.start$/.test(slice.title)){start=slice.start;}else if(/^telemetry\.internal\.[^.]*\.end$/.test(slice.title)&&start!==undefined){this.telemetryInternalRanges_.push(tr.b.math.Range.fromExplicitRange(start,slice.end));start=undefined;}}}},getFrameEventsInRange:function(frametimeType,range){var titleToGet=(frametimeType===MAIN_FRAMETIME_TYPE?MAIN_RENDERING_STATS:IMPL_RENDERING_STATS);var frameEvents=[];for(var event of this.process.getDescendantEvents()){if(event.title===titleToGet){if(range.intersectsExplicitRangeInclusive(event.start,event.end)){frameEvents.push(event);}}}
+frameEvents.sort(function(a,b){return a.start-b.start;});return frameEvents;}};function getFrametimeDataFromEvents(frameEvents){var frametimeData=[];for(var i=1;i<frameEvents.length;i++){var diff=frameEvents[i].start-frameEvents[i-1].start;frametimeData.push({'x':frameEvents[i].start,'frametime':diff});}
 return frametimeData;}
-return{ChromeProcessHelper:ChromeProcessHelper,MAIN_FRAMETIME_TYPE:MAIN_FRAMETIME_TYPE,IMPL_FRAMETIME_TYPE:IMPL_FRAMETIME_TYPE,MAIN_RENDERING_STATS:MAIN_RENDERING_STATS,IMPL_RENDERING_STATS:IMPL_RENDERING_STATS,getSlicesIntersectingRange:getSlicesIntersectingRange,getFrametimeDataFromEvents:getFrametimeDataFromEvents};});'use strict';tr.exportTo('tr.model.helpers',function(){function ChromeBrowserHelper(modelHelper,process){tr.model.helpers.ChromeProcessHelper.call(this,modelHelper,process);this.mainThread_=process.findAtMostOneThreadNamed('CrBrowserMain');if(!process.name)
-process.name=ChromeBrowserHelper.PROCESS_NAME;}
-ChromeBrowserHelper.PROCESS_NAME='Browser';ChromeBrowserHelper.isBrowserProcess=function(process){return!!process.findAtMostOneThreadNamed('CrBrowserMain');};ChromeBrowserHelper.prototype={__proto__:tr.model.helpers.ChromeProcessHelper.prototype,get browserName(){var hasInProcessRendererThread=this.process.findAllThreadsNamed('Chrome_InProcRendererThread').length>0;return hasInProcessRendererThread?'webview':'chrome';},get rendererHelpers(){return this.modelHelper.rendererHelpers;},getLoadingEventsInRange:function(rangeOfInterest){return this.getAllAsyncSlicesMatching(function(slice){return slice.title.indexOf('WebContentsImpl Loading')===0&&rangeOfInterest.intersectsExplicitRangeInclusive(slice.start,slice.end);});},getCommitProvisionalLoadEventsInRange:function(rangeOfInterest){return this.getAllAsyncSlicesMatching(function(slice){return slice.title==='RenderFrameImpl::didCommitProvisionalLoad'&&rangeOfInterest.intersectsExplicitRangeInclusive(slice.start,slice.end);});},get hasLatencyEvents(){var hasLatency=false;for(var thread of this.modelHelper.model.getAllThreads())
-for(var event of thread.getDescendantEvents()){if(!event.isTopLevel)
-continue;if(!(event instanceof tr.e.cc.InputLatencyAsyncSlice))
-continue;hasLatency=true;}
-return hasLatency;},getLatencyEventsInRange:function(rangeOfInterest){return this.getAllAsyncSlicesMatching(function(slice){return(slice.title.indexOf('InputLatency')===0)&&rangeOfInterest.intersectsExplicitRangeInclusive(slice.start,slice.end);});},getAllAsyncSlicesMatching:function(pred,opt_this){var events=[];this.iterAllThreads(function(thread){for(var slice of thread.getDescendantEvents())
-if(pred.call(opt_this,slice))
-events.push(slice);});return events;},getAllNetworkEventsInRange:function(rangeOfInterest){var networkEvents=[];this.modelHelper.model.getAllThreads().forEach(function(thread){thread.asyncSliceGroup.slices.forEach(function(slice){var match=false;if(slice.category=='net'||slice.category=='disabled-by-default-netlog'||slice.category=='netlog'){match=true;}
-if(!match)
-return;if(rangeOfInterest.intersectsExplicitRangeInclusive(slice.start,slice.end))
-networkEvents.push(slice);});});return networkEvents;},iterAllThreads:function(func,opt_this){tr.b.iterItems(this.process.threads,function(tid,thread){func.call(opt_this,thread);});tr.b.iterItems(this.rendererHelpers,function(pid,rendererHelper){var rendererProcess=rendererHelper.process;tr.b.iterItems(rendererProcess.threads,function(tid,thread){func.call(opt_this,thread);});},this);}};return{ChromeBrowserHelper:ChromeBrowserHelper};});'use strict';tr.exportTo('tr.model.helpers',function(){function ChromeGpuHelper(modelHelper,process){tr.model.helpers.ChromeProcessHelper.call(this,modelHelper,process);this.mainThread_=process.findAtMostOneThreadNamed('CrGpuMain');if(!process.name)
-process.name=ChromeGpuHelper.PROCESS_NAME;};ChromeGpuHelper.PROCESS_NAME='GPU Process';ChromeGpuHelper.isGpuProcess=function(process){if(process.findAtMostOneThreadNamed('CrBrowserMain')||process.findAtMostOneThreadNamed('CrRendererMain'))
-return false;return process.findAtMostOneThreadNamed('CrGpuMain');};ChromeGpuHelper.prototype={__proto__:tr.model.helpers.ChromeProcessHelper.prototype,get mainThread(){return this.mainThread_;}};return{ChromeGpuHelper:ChromeGpuHelper};});'use strict';tr.exportTo('tr.b',function(){function SinebowColorGenerator(opt_a,opt_brightness){this.a_=(opt_a===undefined)?1:opt_a;this.brightness_=(opt_brightness===undefined)?1:opt_brightness;this.colorIndex_=0;this.keyToColor={};}
-SinebowColorGenerator.prototype={colorForKey:function(key){if(!this.keyToColor[key])
-this.keyToColor[key]=this.nextColor();return this.keyToColor[key];},nextColor:function(){var components=SinebowColorGenerator.nthColor(this.colorIndex_++);return tr.b.Color.fromString(SinebowColorGenerator.calculateColor(components[0],components[1],components[2],this.a_,this.brightness_));}};SinebowColorGenerator.PHI=(1+Math.sqrt(5))/2;SinebowColorGenerator.sinebow_=function(h){h+=0.5;h=-h;var r=Math.sin(Math.PI*h);var g=Math.sin(Math.PI*(h+1/3));var b=Math.sin(Math.PI*(h+2/3));r*=r;g*=g;b*=b;var y=2*(0.2989*r+0.5870*g+0.1140*b);r/=y;g/=y;b/=y;return[256*r,256*g,256*b];};SinebowColorGenerator.nthColor=function(n){return SinebowColorGenerator.sinebow_(n*this.PHI);};SinebowColorGenerator.calculateColor=function(r,g,b,a,brightness){if(brightness<=1){r*=brightness;g*=brightness;b*=brightness;}else{r=tr.b.lerp(tr.b.normalize(brightness,1,2),r,255);g=tr.b.lerp(tr.b.normalize(brightness,1,2),g,255);b=tr.b.lerp(tr.b.normalize(brightness,1,2),b,255);}
-r=Math.round(r);g=Math.round(g);b=Math.round(b);return'rgba('+r+','+g+','+b+', '+a+')';};return{SinebowColorGenerator:SinebowColorGenerator};});'use strict';tr.exportTo('tr.e.chrome',function(){var SAME_AS_PARENT='same-as-parent';var TITLES_FOR_USER_FRIENDLY_CATEGORY={composite:['CompositingInputsUpdater::update','ThreadProxy::SetNeedsUpdateLayers','LayerTreeHost::UpdateLayers::CalcDrawProps','UpdateLayerTree'],gc:['minorGC','majorGC','MajorGC','MinorGC','V8.GCScavenger','V8.GCIncrementalMarking','V8.GCIdleNotification','V8.GCContext','V8.GCCompactor','V8GCController::traceDOMWrappers'],iframe_creation:['WebLocalFrameImpl::createChildframe'],imageDecode:['Decode Image','ImageFrameGenerator::decode','ImageFrameGenerator::decodeAndScale'],input:['HitTest','ScrollableArea::scrollPositionChanged','EventHandler::handleMouseMoveEvent'],layout:['FrameView::invalidateTree','FrameView::layout','FrameView::performLayout','FrameView::performPostLayoutTasks','FrameView::performPreLayoutTasks','Layer::updateLayerPositionsAfterLayout','Layout','LayoutView::hitTest','ResourceLoadPriorityOptimizer::updateAllImageResourcePriorities','WebViewImpl::layout'],parseHTML:['ParseHTML','HTMLDocumentParser::didReceiveParsedChunkFromBackgroundParser','HTMLDocumentParser::processParsedChunkFromBackgroundParser'],raster:['DisplayListRasterSource::PerformSolidColorAnalysis','Picture::Raster','RasterBufferImpl::Playback','RasterTask','RasterizerTaskImpl::RunOnWorkerThread','SkCanvas::drawImageRect()','SkCanvas::drawPicture()','SkCanvas::drawTextBlob()','TileTaskWorkerPool::PlaybackToMemory'],record:['ContentLayerDelegate::paintContents','DeprecatedPaintLayerCompositor::updateIfNeededRecursive','DeprecatedPaintLayerCompositor::updateLayerPositionsAfterLayout','Paint','Picture::Record','PictureLayer::Update','RenderLayer::updateLayerPositionsAfterLayout'],style:['CSSParserImpl::parseStyleSheet.parse','CSSParserImpl::parseStyleSheet.tokenize','Document::updateStyle','Document::updateStyleInvalidationIfNeeded','ParseAuthorStyleSheet','RuleSet::addRulesFromSheet','StyleElement::processStyleSheet','StyleEngine::createResolver','StyleSheetContents::parseAuthorStyleSheet','UpdateLayoutTree'],script_parse_and_compile:['v8.parseOnBackground','V8.ScriptCompiler'],script_execute:['V8.Execute','WindowProxy::initialize'],resource_loading:['ResourceFetcher::requestResource','ResourceDispatcher::OnReceivedData','ResourceDispatcher::OnRequestComplete','ResourceDispatcher::OnReceivedResponse','Resource::appendData'],renderer_misc:['DecodeFont','ThreadState::completeSweep'],v8_runtime:[],[SAME_AS_PARENT]:['SyncChannel::Send']};var COLOR_FOR_USER_FRIENDLY_CATEGORY=new tr.b.SinebowColorGenerator();var USER_FRIENDLY_CATEGORY_FOR_TITLE=new Map();for(var category in TITLES_FOR_USER_FRIENDLY_CATEGORY){TITLES_FOR_USER_FRIENDLY_CATEGORY[category].forEach(function(title){USER_FRIENDLY_CATEGORY_FOR_TITLE.set(title,category);});}
+return{ChromeProcessHelper,MAIN_FRAMETIME_TYPE,IMPL_FRAMETIME_TYPE,MAIN_RENDERING_STATS,IMPL_RENDERING_STATS,getSlicesIntersectingRange,getFrametimeDataFromEvents,};});'use strict';tr.exportTo('tr.model.helpers',function(){function ChromeBrowserHelper(modelHelper,process){tr.model.helpers.ChromeProcessHelper.call(this,modelHelper,process);this.mainThread_=process.findAtMostOneThreadNamed('CrBrowserMain');if(!process.name){process.name=ChromeBrowserHelper.PROCESS_NAME;}}
+ChromeBrowserHelper.PROCESS_NAME='Browser';ChromeBrowserHelper.isBrowserProcess=function(process){return!!process.findAtMostOneThreadNamed('CrBrowserMain');};ChromeBrowserHelper.prototype={__proto__:tr.model.helpers.ChromeProcessHelper.prototype,get browserName(){var hasInProcessRendererThread=this.process.findAllThreadsNamed('Chrome_InProcRendererThread').length>0;return hasInProcessRendererThread?'webview':'chrome';},get mainThread(){return this.mainThread_;},get rendererHelpers(){return this.modelHelper.rendererHelpers;},getLoadingEventsInRange:function(rangeOfInterest){return this.getAllAsyncSlicesMatching(function(slice){return slice.title.indexOf('WebContentsImpl Loading')===0&&rangeOfInterest.intersectsExplicitRangeInclusive(slice.start,slice.end);});},getCommitProvisionalLoadEventsInRange:function(rangeOfInterest){return this.getAllAsyncSlicesMatching(function(slice){return slice.title==='RenderFrameImpl::didCommitProvisionalLoad'&&rangeOfInterest.intersectsExplicitRangeInclusive(slice.start,slice.end);});},get hasLatencyEvents(){var hasLatency=false;for(var thread of this.modelHelper.model.getAllThreads()){for(var event of thread.getDescendantEvents()){if(!event.isTopLevel)continue;if(!(event instanceof tr.e.cc.InputLatencyAsyncSlice)){continue;}
+hasLatency=true;}}
+return hasLatency;},getLatencyEventsInRange:function(rangeOfInterest){return this.getAllAsyncSlicesMatching(function(slice){return(slice.title.indexOf('InputLatency')===0)&&rangeOfInterest.intersectsExplicitRangeInclusive(slice.start,slice.end);});},getAllAsyncSlicesMatching:function(pred,opt_this){var events=[];this.iterAllThreads(function(thread){for(var slice of thread.getDescendantEvents()){if(pred.call(opt_this,slice)){events.push(slice);}}});return events;},getAllNetworkEventsInRange:function(rangeOfInterest){let networkEvents=[];for(const thread of
+Object.values(this.modelHelper.model.getAllThreads())){networkEvents=networkEvents.concat(thread.getNetworkEventsInRange(rangeOfInterest));}
+return networkEvents;},iterAllThreads:function(func,opt_this){for(var thread of Object.values(this.process.threads)){func.call(opt_this,thread);}
+for(var rendererHelper of Object.values(this.rendererHelpers)){var rendererProcess=rendererHelper.process;for(var thread of Object.values(rendererProcess.threads)){func.call(opt_this,thread);}}}};return{ChromeBrowserHelper,};});'use strict';tr.exportTo('tr.model.helpers',function(){function ChromeGpuHelper(modelHelper,process){tr.model.helpers.ChromeProcessHelper.call(this,modelHelper,process);this.mainThread_=process.findAtMostOneThreadNamed('CrGpuMain');if(!process.name){process.name=ChromeGpuHelper.PROCESS_NAME;}}
+ChromeGpuHelper.PROCESS_NAME='GPU Process';ChromeGpuHelper.isGpuProcess=function(process){if(process.findAtMostOneThreadNamed('CrBrowserMain')||process.findAtMostOneThreadNamed('CrRendererMain')){return false;}
+return process.findAtMostOneThreadNamed('CrGpuMain');};ChromeGpuHelper.prototype={__proto__:tr.model.helpers.ChromeProcessHelper.prototype,get mainThread(){return this.mainThread_;}};return{ChromeGpuHelper,};});'use strict';tr.exportTo('tr.b',function(){function SinebowColorGenerator(opt_a,opt_brightness){this.a_=(opt_a===undefined)?1:opt_a;this.brightness_=(opt_brightness===undefined)?1:opt_brightness;this.colorIndex_=0;this.keyToColor={};}
+SinebowColorGenerator.prototype={colorForKey:function(key){if(!this.keyToColor[key]){this.keyToColor[key]=this.nextColor();}
+return this.keyToColor[key];},nextColor:function(){var components=SinebowColorGenerator.nthColor(this.colorIndex_++);return tr.b.Color.fromString(SinebowColorGenerator.calculateColor(components[0],components[1],components[2],this.a_,this.brightness_));}};SinebowColorGenerator.PHI=(1+Math.sqrt(5))/2;SinebowColorGenerator.sinebow_=function(h){h+=0.5;h=-h;var r=Math.sin(Math.PI*h);var g=Math.sin(Math.PI*(h+1/3));var b=Math.sin(Math.PI*(h+2/3));r*=r;g*=g;b*=b;var y=2*(0.2989*r+0.5870*g+0.1140*b);r/=y;g/=y;b/=y;return[256*r,256*g,256*b];};SinebowColorGenerator.nthColor=function(n){return SinebowColorGenerator.sinebow_(n*this.PHI);};SinebowColorGenerator.calculateColor=function(r,g,b,a,brightness){if(brightness<=1){r*=brightness;g*=brightness;b*=brightness;}else{r=tr.b.math.lerp(tr.b.math.normalize(brightness,1,2),r,255);g=tr.b.math.lerp(tr.b.math.normalize(brightness,1,2),g,255);b=tr.b.math.lerp(tr.b.math.normalize(brightness,1,2),b,255);}
+r=Math.round(r);g=Math.round(g);b=Math.round(b);return'rgba('+r+','+g+','+b+', '+a+')';};return{SinebowColorGenerator,};});'use strict';tr.exportTo('tr.e.chrome',function(){var SAME_AS_PARENT='same-as-parent';var TITLES_FOR_USER_FRIENDLY_CATEGORY={composite:['CompositingInputsUpdater::update','ThreadProxy::SetNeedsUpdateLayers','LayerTreeHost::UpdateLayers::CalcDrawProps','UpdateLayerTree'],gc:['minorGC','majorGC','MajorGC','MinorGC','V8.GCScavenger','V8.GCIncrementalMarking','V8.GCIdleNotification','V8.GCContext','V8.GCCompactor','V8GCController::traceDOMWrappers'],iframe_creation:['WebLocalFrameImpl::createChildframe'],imageDecode:['Decode Image','ImageFrameGenerator::decode','ImageFrameGenerator::decodeAndScale'],input:['HitTest','ScrollableArea::scrollPositionChanged','EventHandler::handleMouseMoveEvent'],layout:['FrameView::invalidateTree','FrameView::layout','FrameView::performLayout','FrameView::performPostLayoutTasks','FrameView::performPreLayoutTasks','Layer::updateLayerPositionsAfterLayout','Layout','LayoutView::hitTest','ResourceLoadPriorityOptimizer::updateAllImageResourcePriorities','WebViewImpl::layout'],parseHTML:['ParseHTML','HTMLDocumentParser::didReceiveParsedChunkFromBackgroundParser','HTMLDocumentParser::processParsedChunkFromBackgroundParser','HTMLDocumentParser::processTokenizedChunkFromBackgroundParser',],raster:['DisplayListRasterSource::PerformSolidColorAnalysis','Picture::Raster','RasterBufferImpl::Playback','RasterTask','RasterizerTaskImpl::RunOnWorkerThread','SkCanvas::drawImageRect()','SkCanvas::drawPicture()','SkCanvas::drawTextBlob()','TileTaskWorkerPool::PlaybackToMemory'],record:['ContentLayerDelegate::paintContents','DeprecatedPaintLayerCompositor::updateIfNeededRecursive','DeprecatedPaintLayerCompositor::updateLayerPositionsAfterLayout','Paint','Picture::Record','PictureLayer::Update','RenderLayer::updateLayerPositionsAfterLayout'],style:['CSSParserImpl::parseStyleSheet.parse','CSSParserImpl::parseStyleSheet.tokenize','Document::updateStyle','Document::updateStyleInvalidationIfNeeded','ParseAuthorStyleSheet','RuleSet::addRulesFromSheet','StyleElement::processStyleSheet','StyleEngine::createResolver','StyleSheetContents::parseAuthorStyleSheet','UpdateLayoutTree'],script_parse_and_compile:['v8.parseOnBackground','V8.ScriptCompiler'],script_execute:['V8.Execute','WindowProxy::initialize'],resource_loading:['ResourceFetcher::requestResource','ResourceDispatcher::OnReceivedData','ResourceDispatcher::OnRequestComplete','ResourceDispatcher::OnReceivedResponse','Resource::appendData'],renderer_misc:['DecodeFont','ThreadState::completeSweep'],v8_runtime:[],[SAME_AS_PARENT]:['SyncChannel::Send']};var COLOR_FOR_USER_FRIENDLY_CATEGORY=new tr.b.SinebowColorGenerator();var USER_FRIENDLY_CATEGORY_FOR_TITLE=new Map();for(var category in TITLES_FOR_USER_FRIENDLY_CATEGORY){TITLES_FOR_USER_FRIENDLY_CATEGORY[category].forEach(function(title){USER_FRIENDLY_CATEGORY_FOR_TITLE.set(title,category);});}
 var USER_FRIENDLY_CATEGORY_FOR_EVENT_CATEGORY={netlog:'net',overhead:'overhead',startup:'startup',gpu:'gpu'};function ChromeUserFriendlyCategoryDriver(){}
-ChromeUserFriendlyCategoryDriver.fromEvent=function(event){var userFriendlyCategory=USER_FRIENDLY_CATEGORY_FOR_TITLE.get(event.title);if(userFriendlyCategory){if(userFriendlyCategory==SAME_AS_PARENT){if(event.parentSlice)
-return ChromeUserFriendlyCategoryDriver.fromEvent(event.parentSlice);}else{return userFriendlyCategory;}}
-var eventCategoryParts=tr.b.getCategoryParts(event.category);for(var i=0;i<eventCategoryParts.length;++i){var eventCategory=eventCategoryParts[i];userFriendlyCategory=USER_FRIENDLY_CATEGORY_FOR_EVENT_CATEGORY[eventCategory];if(userFriendlyCategory)
-return userFriendlyCategory;}
-return'other';};ChromeUserFriendlyCategoryDriver.getColor=function(ufc){return COLOR_FOR_USER_FRIENDLY_CATEGORY.colorForKey(ufc);};ChromeUserFriendlyCategoryDriver.ALL_TITLES=['other'];for(var category in TITLES_FOR_USER_FRIENDLY_CATEGORY){if(category===SAME_AS_PARENT)
-continue;ChromeUserFriendlyCategoryDriver.ALL_TITLES.push(category);}
+ChromeUserFriendlyCategoryDriver.fromEvent=function(event){var userFriendlyCategory=USER_FRIENDLY_CATEGORY_FOR_TITLE.get(event.title);if(userFriendlyCategory){if(userFriendlyCategory===SAME_AS_PARENT){if(event.parentSlice){return ChromeUserFriendlyCategoryDriver.fromEvent(event.parentSlice);}}else{return userFriendlyCategory;}}
+var eventCategoryParts=tr.b.getCategoryParts(event.category);for(var i=0;i<eventCategoryParts.length;++i){var eventCategory=eventCategoryParts[i];userFriendlyCategory=USER_FRIENDLY_CATEGORY_FOR_EVENT_CATEGORY[eventCategory];if(userFriendlyCategory){return userFriendlyCategory;}}
+return'other';};ChromeUserFriendlyCategoryDriver.getColor=function(ufc){return COLOR_FOR_USER_FRIENDLY_CATEGORY.colorForKey(ufc);};ChromeUserFriendlyCategoryDriver.ALL_TITLES=['other'];for(var category in TITLES_FOR_USER_FRIENDLY_CATEGORY){if(category===SAME_AS_PARENT)continue;ChromeUserFriendlyCategoryDriver.ALL_TITLES.push(category);}
 for(var category of tr.b.dictionaryValues(USER_FRIENDLY_CATEGORY_FOR_EVENT_CATEGORY)){ChromeUserFriendlyCategoryDriver.ALL_TITLES.push(category);}
-ChromeUserFriendlyCategoryDriver.ALL_TITLES.sort();for(var category of ChromeUserFriendlyCategoryDriver.ALL_TITLES)
-ChromeUserFriendlyCategoryDriver.getColor(category);return{ChromeUserFriendlyCategoryDriver:ChromeUserFriendlyCategoryDriver};});'use strict';tr.exportTo('tr.model.helpers',function(){function ChromeRendererHelper(modelHelper,process){tr.model.helpers.ChromeProcessHelper.call(this,modelHelper,process);this.mainThread_=process.findAtMostOneThreadNamed('CrRendererMain');this.compositorThread_=process.findAtMostOneThreadNamed('Compositor');this.rasterWorkerThreads_=process.findAllThreadsMatching(function(t){if(t.name===undefined)
-return false;if(t.name.indexOf('CompositorTileWorker')===0)
-return true;if(t.name.indexOf('CompositorRasterWorker')===0)
-return true;return false;});this.isChromeTracingUI_=process.labels!==undefined&&process.labels.length===1&&process.labels[0]==='chrome://tracing';if(!process.name)
-process.name=ChromeRendererHelper.PROCESS_NAME;};ChromeRendererHelper.PROCESS_NAME='Renderer';ChromeRendererHelper.isRenderProcess=function(process){if(process.findAtMostOneThreadNamed('CrRendererMain'))
-return true;if(process.findAtMostOneThreadNamed('Compositor'))
-return true;return false;};ChromeRendererHelper.prototype={__proto__:tr.model.helpers.ChromeProcessHelper.prototype,get mainThread(){return this.mainThread_;},get compositorThread(){return this.compositorThread_;},get rasterWorkerThreads(){return this.rasterWorkerThreads_;},get isChromeTracingUI(){return this.isChromeTracingUI_;},generateTimeBreakdownTree:function(start,end){if(this.mainThread===null)
-return;var breakdownMap={};var range=tr.b.Range.fromExplicitRange(start,end);for(var title of
-tr.e.chrome.ChromeUserFriendlyCategoryDriver.ALL_TITLES){breakdownMap[title]={total:0,events:{}};}
-breakdownMap['idle']={total:0,events:{}};var totalIdleTime=end-start;for(var event of this.mainThread.getDescendantEvents()){if(!range.intersectsExplicitRangeExclusive(event.start,event.end))
-continue;if(event.selfTime===undefined)
-continue;var title=tr.e.chrome.ChromeUserFriendlyCategoryDriver.fromEvent(event);var wallTimeIntersectionRatio=0;if(event.duration>0){wallTimeIntersectionRatio=range.findExplicitIntersectionDuration(event.start,event.end)/event.duration;}
-var v8_runtime=event.args['runtime-call-stat'];if(v8_runtime!==undefined){try{var v8_runtime_object=JSON.parse(v8_runtime);for(var runtime_call in v8_runtime_object){if(v8_runtime_object[runtime_call].length==2){if(breakdownMap['v8_runtime'].events[runtime_call]===undefined){breakdownMap['v8_runtime'].events[runtime_call]=0;}
-var runtime_time=v8_runtime_object[runtime_call][1]*wallTimeIntersectionRatio/1000;breakdownMap['v8_runtime'].total+=runtime_time;breakdownMap['v8_runtime'].events[runtime_call]+=runtime_time;}}}catch(e){console.warn(e);}}
-var approximatedSelfTimeContribution=event.selfTime*wallTimeIntersectionRatio;breakdownMap[title].total+=approximatedSelfTimeContribution;if(breakdownMap[title].events[event.title]===undefined)
-breakdownMap[title].events[event.title]=0;breakdownMap[title].events[event.title]+=approximatedSelfTimeContribution;totalIdleTime-=approximatedSelfTimeContribution;}
-breakdownMap['idle'].total=totalIdleTime;return breakdownMap;}};return{ChromeRendererHelper:ChromeRendererHelper};});'use strict';tr.exportTo('tr.model.helpers',function(){function findChromeBrowserProcesses(model){return model.getAllProcesses(tr.model.helpers.ChromeBrowserHelper.isBrowserProcess);}
+ChromeUserFriendlyCategoryDriver.ALL_TITLES.sort();for(var category of ChromeUserFriendlyCategoryDriver.ALL_TITLES){ChromeUserFriendlyCategoryDriver.getColor(category);}
+return{ChromeUserFriendlyCategoryDriver,};});'use strict';tr.exportTo('tr.model.helpers',function(){function ChromeRendererHelper(modelHelper,process){tr.model.helpers.ChromeProcessHelper.call(this,modelHelper,process);this.mainThread_=process.findAtMostOneThreadNamed('CrRendererMain')||process.findAtMostOneThreadNamed('Chrome_InProcRendererThread');this.compositorThread_=process.findAtMostOneThreadNamed('Compositor');this.rasterWorkerThreads_=process.findAllThreadsMatching(function(t){if(t.name===undefined)return false;if(t.name.indexOf('CompositorTileWorker')===0)return true;if(t.name.indexOf('CompositorRasterWorker')===0)return true;return false;});if(!process.name){process.name=ChromeRendererHelper.PROCESS_NAME;}}
+function generateTimeBreakdownTree_(mainThread,rangeStart,rangeEnd,getEventStart,getEventDuration,getEventSelfTime){if(mainThread===null)return;var breakdownTree={};var range=tr.b.math.Range.fromExplicitRange(rangeStart,rangeEnd);for(var title of
+tr.e.chrome.ChromeUserFriendlyCategoryDriver.ALL_TITLES){breakdownTree[title]={total:0,events:{}};}
+for(var event of mainThread.getDescendantEvents()){var eventStart=getEventStart(event);var eventDuration=getEventDuration(event);var eventSelfTime=getEventSelfTime(event);var eventEnd=eventStart+eventDuration;if(!range.intersectsExplicitRangeExclusive(eventStart,eventEnd)){continue;}
+if(eventSelfTime===undefined)continue;var title=tr.e.chrome.ChromeUserFriendlyCategoryDriver.fromEvent(event);var timeIntersectionRatio=0;if(eventDuration>0){timeIntersectionRatio=range.findExplicitIntersectionDuration(eventStart,eventEnd)/eventDuration;}
+var v8Runtime=event.args['runtime-call-stat'];if(v8Runtime!==undefined){var v8RuntimeObject=JSON.parse(v8Runtime);for(var runtimeCall in v8RuntimeObject){if(v8RuntimeObject[runtimeCall].length===2){if(breakdownTree['v8_runtime'].events[runtimeCall]===undefined){breakdownTree['v8_runtime'].events[runtimeCall]=0;}
+var runtimeTime=tr.b.Unit.timestampFromUs(v8RuntimeObject[runtimeCall][1]*timeIntersectionRatio);breakdownTree['v8_runtime'].total+=runtimeTime;breakdownTree['v8_runtime'].events[runtimeCall]+=runtimeTime;}}}
+var approximatedSelfTimeContribution=eventSelfTime*timeIntersectionRatio;breakdownTree[title].total+=approximatedSelfTimeContribution;if(breakdownTree[title].events[event.title]===undefined){breakdownTree[title].events[event.title]=0;}
+breakdownTree[title].events[event.title]+=approximatedSelfTimeContribution;}
+return breakdownTree;}
+ChromeRendererHelper.PROCESS_NAME='Renderer';ChromeRendererHelper.isRenderProcess=function(process){if(process.findAtMostOneThreadNamed('CrRendererMain'))return true;if(process.findAtMostOneThreadNamed('Compositor'))return true;return false;};ChromeRendererHelper.isTracingProcess=function(process){return process.labels!==undefined&&process.labels.length===1&&process.labels[0]==='chrome://tracing';};ChromeRendererHelper.prototype={__proto__:tr.model.helpers.ChromeProcessHelper.prototype,get mainThread(){return this.mainThread_;},get compositorThread(){return this.compositorThread_;},get rasterWorkerThreads(){return this.rasterWorkerThreads_;},get isChromeTracingUI(){return ChromeRendererHelper.isTracingProcess(this.process);},generateWallClockTimeBreakdownTree:function(start,end){function getEventStart(e){return e.start;}
+function getEventDuration(e){return e.duration;}
+function getEventSelfTime(e){return e.selfTime;}
+var breakdownTree=generateTimeBreakdownTree_(this.mainThread,start,end,getEventStart,getEventDuration,getEventSelfTime);var idleTotal=end-start;for(let cat in breakdownTree){idleTotal-=breakdownTree[cat].total;}
+breakdownTree['idle']={total:idleTotal,events:{}};return breakdownTree;},generateCpuTimeBreakdownTree:function(cpuStart,cpuEnd){function getEventStart(e){return e.cpuStart;}
+function getEventDuration(e){return e.cpuDuration;}
+function getEventSelfTime(e){return e.cpuSelfTime;}
+return generateTimeBreakdownTree_(this.mainThread,cpuStart,cpuEnd,getEventStart,getEventDuration,getEventSelfTime);},getAllNetworkEventsInRange:function(rangeOfInterest){const networkEvents=[];for(const thread of Object.values(this.process.threads)){const events=thread.getNetworkEventsInRange(rangeOfInterest);for(const event of events){networkEvents.push(event);}}
+return networkEvents;}};return{ChromeRendererHelper,};});'use strict';tr.exportTo('tr.model.helpers',function(){function findChromeBrowserProcesses(model){return model.getAllProcesses(tr.model.helpers.ChromeBrowserHelper.isBrowserProcess);}
 function findChromeRenderProcesses(model){return model.getAllProcesses(tr.model.helpers.ChromeRendererHelper.isRenderProcess);}
-function findChromeGpuProcess(model){var gpuProcesses=model.getAllProcesses(tr.model.helpers.ChromeGpuHelper.isGpuProcess);if(gpuProcesses.length!==1)
-return undefined;return gpuProcesses[0];}
+function findChromeGpuProcess(model){var gpuProcesses=model.getAllProcesses(tr.model.helpers.ChromeGpuHelper.isGpuProcess);if(gpuProcesses.length!==1)return undefined;return gpuProcesses[0];}
 function ChromeModelHelper(model){this.model_=model;var browserProcesses=findChromeBrowserProcesses(model);this.browserHelpers_=browserProcesses.map(p=>new tr.model.helpers.ChromeBrowserHelper(this,p));var gpuProcess=findChromeGpuProcess(model);if(gpuProcess){this.gpuHelper_=new tr.model.helpers.ChromeGpuHelper(this,gpuProcess);}else{this.gpuHelper_=undefined;}
-var rendererProcesses_=findChromeRenderProcesses(model);this.rendererHelpers_={};rendererProcesses_.forEach(function(renderProcess){var rendererHelper=new tr.model.helpers.ChromeRendererHelper(this,renderProcess);this.rendererHelpers_[rendererHelper.pid]=rendererHelper;},this);}
-ChromeModelHelper.guid=tr.b.GUID.allocateSimple();ChromeModelHelper.supportsModel=function(model){if(findChromeBrowserProcesses(model).length)
-return true;if(findChromeRenderProcesses(model).length)
-return true;return false;};ChromeModelHelper.prototype={get pid(){throw new Error('woah');},get process(){throw new Error('woah');},get model(){return this.model_;},get browserProcess(){if(this.browserHelper===undefined)
-return undefined;return this.browserHelper.process;},get browserHelper(){return this.browserHelpers_[0];},get browserHelpers(){return this.browserHelpers_;},get gpuHelper(){return this.gpuHelper_;},get rendererHelpers(){return this.rendererHelpers_;}};return{ChromeModelHelper:ChromeModelHelper};});'use strict';tr.exportTo('tr.e.cc',function(){var AsyncSlice=tr.model.AsyncSlice;var EventSet=tr.model.EventSet;var UI_COMP_NAME='INPUT_EVENT_LATENCY_UI_COMPONENT';var ORIGINAL_COMP_NAME='INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT';var BEGIN_COMP_NAME='INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT';var END_COMP_NAME='INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT';var MAIN_RENDERER_THREAD_NAME='CrRendererMain';var COMPOSITOR_THREAD_NAME='Compositor';var POSTTASK_FLOW_EVENT='disabled-by-default-toplevel.flow';var IPC_FLOW_EVENT='disabled-by-default-ipc.flow';var INPUT_EVENT_TYPE_NAMES={CHAR:'Char',CLICK:'GestureClick',CONTEXT_MENU:'ContextMenu',FLING_CANCEL:'GestureFlingCancel',FLING_START:'GestureFlingStart',KEY_DOWN:'KeyDown',KEY_DOWN_RAW:'RawKeyDown',KEY_UP:'KeyUp',LATENCY_SCROLL_UPDATE:'ScrollUpdate',MOUSE_DOWN:'MouseDown',MOUSE_ENTER:'MouseEnter',MOUSE_LEAVE:'MouseLeave',MOUSE_MOVE:'MouseMove',MOUSE_UP:'MouseUp',MOUSE_WHEEL:'MouseWheel',PINCH_BEGIN:'GesturePinchBegin',PINCH_END:'GesturePinchEnd',PINCH_UPDATE:'GesturePinchUpdate',SCROLL_BEGIN:'GestureScrollBegin',SCROLL_END:'GestureScrollEnd',SCROLL_UPDATE:'GestureScrollUpdate',SCROLL_UPDATE_RENDERER:'ScrollUpdate',SHOW_PRESS:'GestureShowPress',TAP:'GestureTap',TAP_CANCEL:'GestureTapCancel',TAP_DOWN:'GestureTapDown',TOUCH_CANCEL:'TouchCancel',TOUCH_END:'TouchEnd',TOUCH_MOVE:'TouchMove',TOUCH_START:'TouchStart',UNKNOWN:'UNKNOWN'};function InputLatencyAsyncSlice(){AsyncSlice.apply(this,arguments);this.associatedEvents_=new EventSet();this.typeName_=undefined;if(!this.isLegacyEvent)
-this.determineModernTypeName_();}
-InputLatencyAsyncSlice.prototype={__proto__:AsyncSlice.prototype,get isLegacyEvent(){return this.title==='InputLatency';},get typeName(){if(!this.typeName_)
-this.determineLegacyTypeName_();return this.typeName_;},checkTypeName_:function(){if(!this.typeName_)
-throw'Unable to determine typeName';var found=false;for(var type_name in INPUT_EVENT_TYPE_NAMES){if(this.typeName===INPUT_EVENT_TYPE_NAMES[type_name]){found=true;break;}}
-if(!found)
-this.typeName_=INPUT_EVENT_TYPE_NAMES.UNKNOWN;},determineModernTypeName_:function(){var lastColonIndex=this.title.lastIndexOf(':');if(lastColonIndex<0)
-return;var characterAfterLastColonIndex=lastColonIndex+1;this.typeName_=this.title.slice(characterAfterLastColonIndex);this.checkTypeName_();},determineLegacyTypeName_:function(){for(var subSlice of this.enumerateAllDescendents()){var subSliceIsAInputLatencyAsyncSlice=(subSlice instanceof InputLatencyAsyncSlice);if(!subSliceIsAInputLatencyAsyncSlice)
-continue;if(!subSlice.typeName)
-continue;if(this.typeName_&&subSlice.typeName_){var subSliceHasDifferentTypeName=(this.typeName_!==subSlice.typeName_);if(subSliceHasDifferentTypeName){throw'InputLatencyAsyncSlice.determineLegacyTypeName_() '+' found multiple typeNames';}}
+var rendererProcesses_=findChromeRenderProcesses(model);this.rendererHelpers_={};rendererProcesses_.forEach(function(renderProcess){var rendererHelper=new tr.model.helpers.ChromeRendererHelper(this,renderProcess);this.rendererHelpers_[rendererHelper.pid]=rendererHelper;},this);this.chromeBounds_=undefined;}
+ChromeModelHelper.guid=tr.b.GUID.allocateSimple();ChromeModelHelper.supportsModel=function(model){if(findChromeBrowserProcesses(model).length)return true;if(findChromeRenderProcesses(model).length)return true;return false;};ChromeModelHelper.prototype={get pid(){throw new Error('woah');},get process(){throw new Error('woah');},get model(){return this.model_;},get browserProcess(){if(this.browserHelper===undefined)return undefined;return this.browserHelper.process;},get browserHelper(){return this.browserHelpers_[0];},get browserHelpers(){return this.browserHelpers_;},get gpuHelper(){return this.gpuHelper_;},get rendererHelpers(){return this.rendererHelpers_;},get rendererWithLargestPid(){var largestPid=-1;for(var pid in this.rendererHelpers){var rendererHelper=this.rendererHelpers[pid];if(rendererHelper.isChromeTracingUI)continue;if(pid>largestPid)largestPid=pid;}
+if(largestPid===-1)return undefined;return this.rendererHelpers[largestPid];},get chromeBounds(){if(!this.chromeBounds_){this.chromeBounds_=new tr.b.math.Range();for(var browserHelper of
+tr.b.dictionaryValues(this.browserHelpers)){this.chromeBounds_.addRange(browserHelper.process.bounds);}}
+if(this.chromeBounds_.isEmpty){return undefined;}
+return this.chromeBounds_;}};return{ChromeModelHelper,};});'use strict';tr.exportTo('tr.e.cc',function(){var AsyncSlice=tr.model.AsyncSlice;var EventSet=tr.model.EventSet;var UI_COMP_NAME='INPUT_EVENT_LATENCY_UI_COMPONENT';var ORIGINAL_COMP_NAME='INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT';var BEGIN_COMP_NAME='INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT';var END_COMP_NAME='INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT';var MAIN_RENDERER_THREAD_NAME='CrRendererMain';var COMPOSITOR_THREAD_NAME='Compositor';var POSTTASK_FLOW_EVENT='disabled-by-default-toplevel.flow';var IPC_FLOW_EVENT='disabled-by-default-ipc.flow';var INPUT_EVENT_TYPE_NAMES={CHAR:'Char',CLICK:'GestureClick',CONTEXT_MENU:'ContextMenu',FLING_CANCEL:'GestureFlingCancel',FLING_START:'GestureFlingStart',KEY_DOWN:'KeyDown',KEY_DOWN_RAW:'RawKeyDown',KEY_UP:'KeyUp',LATENCY_SCROLL_UPDATE:'ScrollUpdate',MOUSE_DOWN:'MouseDown',MOUSE_ENTER:'MouseEnter',MOUSE_LEAVE:'MouseLeave',MOUSE_MOVE:'MouseMove',MOUSE_UP:'MouseUp',MOUSE_WHEEL:'MouseWheel',PINCH_BEGIN:'GesturePinchBegin',PINCH_END:'GesturePinchEnd',PINCH_UPDATE:'GesturePinchUpdate',SCROLL_BEGIN:'GestureScrollBegin',SCROLL_END:'GestureScrollEnd',SCROLL_UPDATE:'GestureScrollUpdate',SCROLL_UPDATE_RENDERER:'ScrollUpdate',SHOW_PRESS:'GestureShowPress',TAP:'GestureTap',TAP_CANCEL:'GestureTapCancel',TAP_DOWN:'GestureTapDown',TOUCH_CANCEL:'TouchCancel',TOUCH_END:'TouchEnd',TOUCH_MOVE:'TouchMove',TOUCH_START:'TouchStart',UNKNOWN:'UNKNOWN'};function InputLatencyAsyncSlice(){AsyncSlice.apply(this,arguments);this.associatedEvents_=new EventSet();this.typeName_=undefined;if(!this.isLegacyEvent){this.determineModernTypeName_();}}
+InputLatencyAsyncSlice.prototype={__proto__:AsyncSlice.prototype,get isLegacyEvent(){return this.title==='InputLatency';},get typeName(){if(!this.typeName_){this.determineLegacyTypeName_();}
+return this.typeName_;},checkTypeName_:function(){if(!this.typeName_){throw new Error('Unable to determine typeName');}
+var found=false;for(var typeName in INPUT_EVENT_TYPE_NAMES){if(this.typeName===INPUT_EVENT_TYPE_NAMES[typeName]){found=true;break;}}
+if(!found){this.typeName_=INPUT_EVENT_TYPE_NAMES.UNKNOWN;}},determineModernTypeName_:function(){var lastColonIndex=this.title.lastIndexOf(':');if(lastColonIndex<0)return;var characterAfterLastColonIndex=lastColonIndex+1;this.typeName_=this.title.slice(characterAfterLastColonIndex);this.checkTypeName_();},determineLegacyTypeName_:function(){for(var subSlice of this.enumerateAllDescendents()){var subSliceIsAInputLatencyAsyncSlice=(subSlice instanceof InputLatencyAsyncSlice);if(!subSliceIsAInputLatencyAsyncSlice)continue;if(!subSlice.typeName)continue;if(this.typeName_&&subSlice.typeName_){var subSliceHasDifferentTypeName=(this.typeName_!==subSlice.typeName_);if(subSliceHasDifferentTypeName){throw new Error('InputLatencyAsyncSlice.determineLegacyTypeName_() '+' found multiple typeNames');}}
 this.typeName_=subSlice.typeName_;}
-if(!this.typeName_)
-throw'InputLatencyAsyncSlice.determineLegacyTypeName_() failed';this.checkTypeName_();},getRendererHelper:function(sourceSlices){var traceModel=this.startThread.parent.model;var modelHelper=traceModel.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(!modelHelper)
-return undefined;var mainThread=undefined;var compositorThread=undefined;for(var i in sourceSlices){if(sourceSlices[i].parentContainer.name===MAIN_RENDERER_THREAD_NAME)
-mainThread=sourceSlices[i].parentContainer;else if(sourceSlices[i].parentContainer.name===COMPOSITOR_THREAD_NAME)
-compositorThread=sourceSlices[i].parentContainer;if(mainThread&&compositorThread)
-break;}
-var rendererHelpers=modelHelper.rendererHelpers;var pids=Object.keys(rendererHelpers);for(var i=0;i<pids.length;i++){var pid=pids[i];var rendererHelper=rendererHelpers[pid];if(rendererHelper.mainThread===mainThread||rendererHelper.compositorThread===compositorThread)
-return rendererHelper;}
-return undefined;},addEntireSliceHierarchy:function(slice){this.associatedEvents_.push(slice);slice.iterateAllSubsequentSlices(function(subsequentSlice){this.associatedEvents_.push(subsequentSlice);},this);},addDirectlyAssociatedEvents:function(flowEvents){var slices=[];flowEvents.forEach(function(flowEvent){this.associatedEvents_.push(flowEvent);var newSource=flowEvent.startSlice.mostTopLevelSlice;if(slices.indexOf(newSource)===-1)
-slices.push(newSource);},this);var lastFlowEvent=flowEvents[flowEvents.length-1];var lastSource=lastFlowEvent.endSlice.mostTopLevelSlice;if(slices.indexOf(lastSource)===-1)
-slices.push(lastSource);return slices;},addScrollUpdateEvents:function(rendererHelper){if(!rendererHelper||!rendererHelper.compositorThread)
-return;var compositorThread=rendererHelper.compositorThread;var gestureScrollUpdateStart=this.start;var gestureScrollUpdateEnd=this.end;var allCompositorAsyncSlices=compositorThread.asyncSliceGroup.slices;for(var i in allCompositorAsyncSlices){var slice=allCompositorAsyncSlices[i];if(slice.title!=='Latency::ScrollUpdate')
-continue;var parentId=slice.args.data.INPUT_EVENT_LATENCY_FORWARD_SCROLL_UPDATE_TO_MAIN_COMPONENT.sequence_number;if(parentId===undefined){if(slice.start<gestureScrollUpdateStart||slice.start>=gestureScrollUpdateEnd)
-continue;}else{if(parseInt(parentId)!==parseInt(this.id))
-continue;}
-slice.associatedEvents.forEach(function(event){this.associatedEvents_.push(event);},this);break;}},belongToOtherInputs:function(slice,flowEvents){var fromOtherInputs=false;slice.iterateEntireHierarchy(function(subsequentSlice){if(fromOtherInputs)
-return;subsequentSlice.inFlowEvents.forEach(function(inflow){if(fromOtherInputs)
-return;if(inflow.category.indexOf('input')>-1){if(flowEvents.indexOf(inflow)===-1)
-fromOtherInputs=true;}},this);},this);return fromOtherInputs;},triggerOtherInputs:function(event,flowEvents){if(event.outFlowEvents===undefined||event.outFlowEvents.length===0)
-return false;var flow=event.outFlowEvents[0];if(flow.category!==POSTTASK_FLOW_EVENT||!flow.endSlice)
-return false;var endSlice=flow.endSlice;if(this.belongToOtherInputs(endSlice.mostTopLevelSlice,flowEvents))
-return true;return false;},followSubsequentSlices:function(event,queue,visited,flowEvents){var stopFollowing=false;var inputAck=false;event.iterateAllSubsequentSlices(function(slice){if(stopFollowing)
-return;if(slice.title==='TaskQueueManager::RunTask')
-return;if(slice.title==='ThreadProxy::ScheduledActionSendBeginMainFrame')
-return;if(slice.title==='Scheduler::ScheduleBeginImplFrameDeadline'){if(this.triggerOtherInputs(slice,flowEvents))
-return;}
-if(slice.title==='CompositorImpl::PostComposite'){if(this.triggerOtherInputs(slice,flowEvents))
-return;}
-if(slice.title==='InputRouterImpl::ProcessInputEventAck')
-inputAck=true;if(inputAck&&slice.title==='InputRouterImpl::FilterAndSendWebInputEvent')
-stopFollowing=true;this.followCurrentSlice(slice,queue,visited);},this);},followCurrentSlice:function(event,queue,visited){event.outFlowEvents.forEach(function(outflow){if((outflow.category===POSTTASK_FLOW_EVENT||outflow.category===IPC_FLOW_EVENT)&&outflow.endSlice){this.associatedEvents_.push(outflow);var nextEvent=outflow.endSlice.mostTopLevelSlice;if(!visited.contains(nextEvent)){visited.push(nextEvent);queue.push(nextEvent);}}},this);},backtraceFromDraw:function(beginImplFrame,visited){var pendingEventQueue=[];pendingEventQueue.push(beginImplFrame.mostTopLevelSlice);while(pendingEventQueue.length!==0){var event=pendingEventQueue.pop();this.addEntireSliceHierarchy(event);event.inFlowEvents.forEach(function(inflow){if(inflow.category===POSTTASK_FLOW_EVENT&&inflow.startSlice){var nextEvent=inflow.startSlice.mostTopLevelSlice;if(!visited.contains(nextEvent)){visited.push(nextEvent);pendingEventQueue.push(nextEvent);}}},this);}},sortRasterizerSlices:function(rasterWorkerThreads,sortedRasterizerSlices){rasterWorkerThreads.forEach(function(rasterizer){Array.prototype.push.apply(sortedRasterizerSlices,rasterizer.sliceGroup.slices);},this);sortedRasterizerSlices.sort(function(a,b){if(a.start!==b.start)
-return a.start-b.start;return a.guid-b.guid;});},addRasterizationEvents:function(prepareTiles,rendererHelper,visited,flowEvents,sortedRasterizerSlices){if(!prepareTiles.args.prepare_tiles_id)
-return;if(!rendererHelper||!rendererHelper.rasterWorkerThreads)
-return;var rasterWorkerThreads=rendererHelper.rasterWorkerThreads;var prepare_tile_id=prepareTiles.args.prepare_tiles_id;var pendingEventQueue=[];if(sortedRasterizerSlices.length===0)
-this.sortRasterizerSlices(rasterWorkerThreads,sortedRasterizerSlices);var numFinishedTasks=0;var RASTER_TASK_TITLE='RasterizerTaskImpl::RunOnWorkerThread';var IMAGEDECODE_TASK_TITLE='ImageDecodeTaskImpl::RunOnWorkerThread';var FINISHED_TASK_TITLE='TaskSetFinishedTaskImpl::RunOnWorkerThread';for(var i=0;i<sortedRasterizerSlices.length;i++){var task=sortedRasterizerSlices[i];if(task.title===RASTER_TASK_TITLE||task.title===IMAGEDECODE_TASK_TITLE){if(task.args.source_prepare_tiles_id===prepare_tile_id)
-this.addEntireSliceHierarchy(task.mostTopLevelSlice);}else if(task.title===FINISHED_TASK_TITLE){if(task.start>prepareTiles.start){pendingEventQueue.push(task.mostTopLevelSlice);if(++numFinishedTasks===3)
-break;}}}
-while(pendingEventQueue.length!=0){var event=pendingEventQueue.pop();this.addEntireSliceHierarchy(event);this.followSubsequentSlices(event,pendingEventQueue,visited,flowEvents);}},addOtherCausallyRelatedEvents:function(rendererHelper,sourceSlices,flowEvents,sortedRasterizerSlices){var pendingEventQueue=[];var visitedEvents=new EventSet();var beginImplFrame=undefined;var prepareTiles=undefined;var sortedRasterizerSlices=[];sourceSlices.forEach(function(sourceSlice){if(!visitedEvents.contains(sourceSlice)){visitedEvents.push(sourceSlice);pendingEventQueue.push(sourceSlice);}},this);while(pendingEventQueue.length!=0){var event=pendingEventQueue.pop();this.addEntireSliceHierarchy(event);this.followCurrentSlice(event,pendingEventQueue,visitedEvents);this.followSubsequentSlices(event,pendingEventQueue,visitedEvents,flowEvents);var COMPOSITOR_PREPARE_TILES='TileManager::PrepareTiles';prepareTiles=event.findDescendentSlice(COMPOSITOR_PREPARE_TILES);if(prepareTiles)
-this.addRasterizationEvents(prepareTiles,rendererHelper,visitedEvents,flowEvents,sortedRasterizerSlices);var COMPOSITOR_ON_BIFD='Scheduler::OnBeginImplFrameDeadline';beginImplFrame=event.findDescendentSlice(COMPOSITOR_ON_BIFD);if(beginImplFrame)
-this.backtraceFromDraw(beginImplFrame,visitedEvents);}
-var INPUT_GSU='InputLatency::GestureScrollUpdate';if(this.title===INPUT_GSU)
-this.addScrollUpdateEvents(rendererHelper);},get associatedEvents(){if(this.associatedEvents_.length!==0)
-return this.associatedEvents_;var modelIndices=this.startThread.parent.model.modelIndices;var flowEvents=modelIndices.getFlowEventsWithId(this.id);if(flowEvents.length===0)
-return this.associatedEvents_;var sourceSlices=this.addDirectlyAssociatedEvents(flowEvents);var rendererHelper=this.getRendererHelper(sourceSlices);this.addOtherCausallyRelatedEvents(rendererHelper,sourceSlices,flowEvents);return this.associatedEvents_;},get inputLatency(){if(!('data'in this.args))
-return undefined;var data=this.args.data;if(!(END_COMP_NAME in data))
-return undefined;var latency=0;var endTime=data[END_COMP_NAME].time;if(ORIGINAL_COMP_NAME in data){latency=endTime-data[ORIGINAL_COMP_NAME].time;}else if(UI_COMP_NAME in data){latency=endTime-data[UI_COMP_NAME].time;}else if(BEGIN_COMP_NAME in data){latency=endTime-data[BEGIN_COMP_NAME].time;}else{throw new Error('No valid begin latency component');}
-return latency;}};var eventTypeNames=['Char','ContextMenu','GestureClick','GestureFlingCancel','GestureFlingStart','GestureScrollBegin','GestureScrollEnd','GestureScrollUpdate','GestureShowPress','GestureTap','GestureTapCancel','GestureTapDown','GesturePinchBegin','GesturePinchEnd','GesturePinchUpdate','KeyDown','KeyUp','MouseDown','MouseEnter','MouseLeave','MouseMove','MouseUp','MouseWheel','RawKeyDown','ScrollUpdate','TouchCancel','TouchEnd','TouchMove','TouchStart'];var allTypeNames=['InputLatency'];eventTypeNames.forEach(function(eventTypeName){allTypeNames.push('InputLatency:'+eventTypeName);allTypeNames.push('InputLatency::'+eventTypeName);});AsyncSlice.subTypes.register(InputLatencyAsyncSlice,{typeNames:allTypeNames,categoryParts:['latencyInfo']});return{InputLatencyAsyncSlice:InputLatencyAsyncSlice,INPUT_EVENT_TYPE_NAMES:INPUT_EVENT_TYPE_NAMES};});'use strict';tr.exportTo('tr.model',function(){return{BROWSER_PROCESS_PID_REF:-1,OBJECT_DEFAULT_SCOPE:'ptr'};});'use strict';tr.exportTo('tr.e.audits',function(){var Auditor=tr.c.Auditor;function ChromeAuditor(model){Auditor.call(this,model);var modelHelper=this.model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(modelHelper&&modelHelper.browserHelper){this.modelHelper=modelHelper;}else{this.modelHelper=undefined;}};ChromeAuditor.prototype={__proto__:Auditor.prototype,runAnnotate:function(){if(!this.modelHelper)
-return;for(var pid in this.modelHelper.rendererHelpers){var rendererHelper=this.modelHelper.rendererHelpers[pid];if(rendererHelper.isChromeTracingUI)
-rendererHelper.process.important=false;}},installUserFriendlyCategoryDriverIfNeeded:function(){this.model.addUserFriendlyCategoryDriver(tr.e.chrome.ChromeUserFriendlyCategoryDriver);},runAudit:function(){if(!this.modelHelper)
-return;this.model.replacePIDRefsInPatchups(tr.model.BROWSER_PROCESS_PID_REF,this.modelHelper.browserProcess.pid);this.model.applyObjectRefPatchups();}};Auditor.register(ChromeAuditor);return{ChromeAuditor:ChromeAuditor};});'use strict';tr.exportTo('tr.e.chrome',function(){var KNOWN_PROPERTIES={absX:1,absY:1,address:1,anonymous:1,childNeeds:1,children:1,classNames:1,col:1,colSpan:1,float:1,height:1,htmlId:1,name:1,posChildNeeds:1,positioned:1,positionedMovement:1,relX:1,relY:1,relativePositioned:1,row:1,rowSpan:1,selfNeeds:1,stickyPositioned:1,tag:1,width:1};function LayoutObject(snapshot,args){this.snapshot_=snapshot;this.id_=args.address;this.name_=args.name;this.childLayoutObjects_=[];this.otherProperties_={};this.tag_=args.tag;this.relativeRect_=tr.b.Rect.fromXYWH(args.relX,args.relY,args.width,args.height);this.absoluteRect_=tr.b.Rect.fromXYWH(args.absX,args.absY,args.width,args.height);this.isFloat_=args.float;this.isStickyPositioned_=args.stickyPositioned;this.isPositioned_=args.positioned;this.isRelativePositioned_=args.relativePositioned;this.isAnonymous_=args.anonymous;this.htmlId_=args.htmlId;this.classNames_=args.classNames;this.needsLayoutReasons_=[];if(args.selfNeeds)
-this.needsLayoutReasons_.push('self');if(args.childNeeds)
-this.needsLayoutReasons_.push('child');if(args.posChildNeeds)
-this.needsLayoutReasons_.push('positionedChild');if(args.positionedMovement)
-this.needsLayoutReasons_.push('positionedMovement');this.tableRow_=args.row;this.tableCol_=args.col;this.tableRowSpan_=args.rowSpan;this.tableColSpan_=args.colSpan;if(args.children){args.children.forEach(function(child){this.childLayoutObjects_.push(new LayoutObject(snapshot,child));}.bind(this));}
-for(var property in args){if(!KNOWN_PROPERTIES[property])
-this.otherProperties_[property]=args[property];}}
-LayoutObject.prototype={get snapshot(){return this.snapshot_;},get id(){return this.id_;},get name(){return this.name_;},get tag(){return this.tag_;},get relativeRect(){return this.relativeRect_;},get absoluteRect(){return this.absoluteRect_;},get isPositioned(){return this.isPositioned_;},get isFloat(){return this.isFloat_;},get isStickyPositioned(){return this.isStickyPositioned_;},get isRelativePositioned(){return this.isRelativePositioned_;},get isAnonymous(){return this.isAnonymous_;},get tableRow(){return this.tableRow_;},get tableCol(){return this.tableCol_;},get tableRowSpan(){return this.tableRowSpan_;},get tableColSpan(){return this.tableColSpan_;},get htmlId(){return this.htmlId_;},get classNames(){return this.classNames_;},get needsLayoutReasons(){return this.needsLayoutReasons_;},get hasChildLayoutObjects(){return this.childLayoutObjects_.length>0;},get childLayoutObjects(){return this.childLayoutObjects_;},traverseTree:function(cb,opt_this){cb.call(opt_this,this);if(!this.hasChildLayoutObjects)
-return;this.childLayoutObjects.forEach(function(child){child.traverseTree(cb,opt_this);});},get otherPropertyNames(){var names=[];for(var name in this.otherProperties_){names.push(name);}
-return names;},getProperty:function(name){return this.otherProperties_[name];},get previousSnapshotLayoutObject(){if(!this.snapshot.previousSnapshot)
-return undefined;return this.snapshot.previousSnapshot.getLayoutObjectById(this.id);},get nextSnapshotLayoutObject(){if(!this.snapshot.nextSnapshot)
-return undefined;return this.snapshot.nextSnapshot.getLayoutObjectById(this.id);}};return{LayoutObject:LayoutObject};});'use strict';tr.exportTo('tr.e.chrome',function(){var ObjectSnapshot=tr.model.ObjectSnapshot;var ObjectInstance=tr.model.ObjectInstance;function LayoutTreeInstance(){ObjectInstance.apply(this,arguments);}
+if(!this.typeName_){throw new Error('InputLatencyAsyncSlice.determineLegacyTypeName_() failed');}
+this.checkTypeName_();},getRendererHelper:function(sourceSlices){var traceModel=this.startThread.parent.model;var modelHelper=traceModel.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(!modelHelper)return undefined;var mainThread=undefined;var compositorThread=undefined;for(var i in sourceSlices){if(sourceSlices[i].parentContainer.name===MAIN_RENDERER_THREAD_NAME){mainThread=sourceSlices[i].parentContainer;}else if(sourceSlices[i].parentContainer.name===COMPOSITOR_THREAD_NAME){compositorThread=sourceSlices[i].parentContainer;}
+if(mainThread&&compositorThread)break;}
+var rendererHelpers=modelHelper.rendererHelpers;var pids=Object.keys(rendererHelpers);for(var i=0;i<pids.length;i++){var pid=pids[i];var rendererHelper=rendererHelpers[pid];if(rendererHelper.mainThread===mainThread||rendererHelper.compositorThread===compositorThread){return rendererHelper;}}
+return undefined;},addEntireSliceHierarchy:function(slice){this.associatedEvents_.push(slice);slice.iterateAllSubsequentSlices(function(subsequentSlice){this.associatedEvents_.push(subsequentSlice);},this);},addDirectlyAssociatedEvents:function(flowEvents){var slices=[];flowEvents.forEach(function(flowEvent){this.associatedEvents_.push(flowEvent);var newSource=flowEvent.startSlice.mostTopLevelSlice;if(slices.indexOf(newSource)===-1){slices.push(newSource);}},this);var lastFlowEvent=flowEvents[flowEvents.length-1];var lastSource=lastFlowEvent.endSlice.mostTopLevelSlice;if(slices.indexOf(lastSource)===-1){slices.push(lastSource);}
+return slices;},addScrollUpdateEvents:function(rendererHelper){if(!rendererHelper||!rendererHelper.compositorThread){return;}
+var compositorThread=rendererHelper.compositorThread;var gestureScrollUpdateStart=this.start;var gestureScrollUpdateEnd=this.end;var allCompositorAsyncSlices=compositorThread.asyncSliceGroup.slices;for(var i in allCompositorAsyncSlices){var slice=allCompositorAsyncSlices[i];if(slice.title!=='Latency::ScrollUpdate')continue;var parentId=slice.args.data.INPUT_EVENT_LATENCY_FORWARD_SCROLL_UPDATE_TO_MAIN_COMPONENT.sequence_number;if(parentId===undefined){if(slice.start<gestureScrollUpdateStart||slice.start>=gestureScrollUpdateEnd){continue;}}else{if(parseInt(parentId)!==parseInt(this.id)){continue;}}
+slice.associatedEvents.forEach(function(event){this.associatedEvents_.push(event);},this);break;}},belongToOtherInputs:function(slice,flowEvents){var fromOtherInputs=false;slice.iterateEntireHierarchy(function(subsequentSlice){if(fromOtherInputs)return;subsequentSlice.inFlowEvents.forEach(function(inflow){if(fromOtherInputs)return;if(inflow.category.indexOf('input')>-1){if(flowEvents.indexOf(inflow)===-1){fromOtherInputs=true;}}},this);},this);return fromOtherInputs;},triggerOtherInputs:function(event,flowEvents){if(event.outFlowEvents===undefined||event.outFlowEvents.length===0){return false;}
+var flow=event.outFlowEvents[0];if(flow.category!==POSTTASK_FLOW_EVENT||!flow.endSlice){return false;}
+var endSlice=flow.endSlice;if(this.belongToOtherInputs(endSlice.mostTopLevelSlice,flowEvents)){return true;}
+return false;},followSubsequentSlices:function(event,queue,visited,flowEvents){var stopFollowing=false;var inputAck=false;event.iterateAllSubsequentSlices(function(slice){if(stopFollowing)return;if(slice.title==='TaskQueueManager::RunTask')return;if(slice.title==='ThreadProxy::ScheduledActionSendBeginMainFrame'){return;}
+if(slice.title==='Scheduler::ScheduleBeginImplFrameDeadline'){if(this.triggerOtherInputs(slice,flowEvents))return;}
+if(slice.title==='CompositorImpl::PostComposite'){if(this.triggerOtherInputs(slice,flowEvents))return;}
+if(slice.title==='InputRouterImpl::ProcessInputEventAck'){inputAck=true;}
+if(inputAck&&slice.title==='InputRouterImpl::FilterAndSendWebInputEvent'){stopFollowing=true;}
+this.followCurrentSlice(slice,queue,visited);},this);},followCurrentSlice:function(event,queue,visited){event.outFlowEvents.forEach(function(outflow){if((outflow.category===POSTTASK_FLOW_EVENT||outflow.category===IPC_FLOW_EVENT)&&outflow.endSlice){this.associatedEvents_.push(outflow);var nextEvent=outflow.endSlice.mostTopLevelSlice;if(!visited.contains(nextEvent)){visited.push(nextEvent);queue.push(nextEvent);}}},this);},backtraceFromDraw:function(beginImplFrame,visited){var pendingEventQueue=[];pendingEventQueue.push(beginImplFrame.mostTopLevelSlice);while(pendingEventQueue.length!==0){var event=pendingEventQueue.pop();this.addEntireSliceHierarchy(event);event.inFlowEvents.forEach(function(inflow){if(inflow.category===POSTTASK_FLOW_EVENT&&inflow.startSlice){var nextEvent=inflow.startSlice.mostTopLevelSlice;if(!visited.contains(nextEvent)){visited.push(nextEvent);pendingEventQueue.push(nextEvent);}}},this);}},sortRasterizerSlices:function(rasterWorkerThreads,sortedRasterizerSlices){rasterWorkerThreads.forEach(function(rasterizer){Array.prototype.push.apply(sortedRasterizerSlices,rasterizer.sliceGroup.slices);},this);sortedRasterizerSlices.sort(function(a,b){if(a.start!==b.start){return a.start-b.start;}
+return a.guid-b.guid;});},addRasterizationEvents:function(prepareTiles,rendererHelper,visited,flowEvents,sortedRasterizerSlices){if(!prepareTiles.args.prepare_tiles_id)return;if(!rendererHelper||!rendererHelper.rasterWorkerThreads){return;}
+var rasterWorkerThreads=rendererHelper.rasterWorkerThreads;var prepareTileId=prepareTiles.args.prepare_tiles_id;var pendingEventQueue=[];if(sortedRasterizerSlices.length===0){this.sortRasterizerSlices(rasterWorkerThreads,sortedRasterizerSlices);}
+var numFinishedTasks=0;var RASTER_TASK_TITLE='RasterizerTaskImpl::RunOnWorkerThread';var IMAGEDECODE_TASK_TITLE='ImageDecodeTaskImpl::RunOnWorkerThread';var FINISHED_TASK_TITLE='TaskSetFinishedTaskImpl::RunOnWorkerThread';for(var i=0;i<sortedRasterizerSlices.length;i++){var task=sortedRasterizerSlices[i];if(task.title===RASTER_TASK_TITLE||task.title===IMAGEDECODE_TASK_TITLE){if(task.args.source_prepare_tiles_id===prepareTileId){this.addEntireSliceHierarchy(task.mostTopLevelSlice);}}else if(task.title===FINISHED_TASK_TITLE){if(task.start>prepareTiles.start){pendingEventQueue.push(task.mostTopLevelSlice);if(++numFinishedTasks===3)break;}}}
+while(pendingEventQueue.length!==0){var event=pendingEventQueue.pop();this.addEntireSliceHierarchy(event);this.followSubsequentSlices(event,pendingEventQueue,visited,flowEvents);}},addOtherCausallyRelatedEvents:function(rendererHelper,sourceSlices,flowEvents,sortedRasterizerSlices){var pendingEventQueue=[];var visitedEvents=new EventSet();var beginImplFrame=undefined;var prepareTiles=undefined;var sortedRasterizerSlices=[];sourceSlices.forEach(function(sourceSlice){if(!visitedEvents.contains(sourceSlice)){visitedEvents.push(sourceSlice);pendingEventQueue.push(sourceSlice);}},this);while(pendingEventQueue.length!==0){var event=pendingEventQueue.pop();this.addEntireSliceHierarchy(event);this.followCurrentSlice(event,pendingEventQueue,visitedEvents);this.followSubsequentSlices(event,pendingEventQueue,visitedEvents,flowEvents);var COMPOSITOR_PREPARE_TILES='TileManager::PrepareTiles';prepareTiles=event.findDescendentSlice(COMPOSITOR_PREPARE_TILES);if(prepareTiles){this.addRasterizationEvents(prepareTiles,rendererHelper,visitedEvents,flowEvents,sortedRasterizerSlices);}
+var COMPOSITOR_ON_BIFD='Scheduler::OnBeginImplFrameDeadline';beginImplFrame=event.findDescendentSlice(COMPOSITOR_ON_BIFD);if(beginImplFrame){this.backtraceFromDraw(beginImplFrame,visitedEvents);}}
+var INPUT_GSU='InputLatency::GestureScrollUpdate';if(this.title===INPUT_GSU){this.addScrollUpdateEvents(rendererHelper);}},get associatedEvents(){if(this.associatedEvents_.length!==0){return this.associatedEvents_;}
+var modelIndices=this.startThread.parent.model.modelIndices;var flowEvents=modelIndices.getFlowEventsWithId(this.id);if(flowEvents.length===0){return this.associatedEvents_;}
+var sourceSlices=this.addDirectlyAssociatedEvents(flowEvents);var rendererHelper=this.getRendererHelper(sourceSlices);this.addOtherCausallyRelatedEvents(rendererHelper,sourceSlices,flowEvents);return this.associatedEvents_;},get inputLatency(){if(!('data'in this.args))return undefined;var data=this.args.data;if(!(END_COMP_NAME in data))return undefined;var latency=0;var endTime=data[END_COMP_NAME].time;if(ORIGINAL_COMP_NAME in data){latency=endTime-data[ORIGINAL_COMP_NAME].time;}else if(UI_COMP_NAME in data){latency=endTime-data[UI_COMP_NAME].time;}else if(BEGIN_COMP_NAME in data){latency=endTime-data[BEGIN_COMP_NAME].time;}else{throw new Error('No valid begin latency component');}
+return latency;}};var eventTypeNames=['Char','ContextMenu','GestureClick','GestureFlingCancel','GestureFlingStart','GestureScrollBegin','GestureScrollEnd','GestureScrollUpdate','GestureShowPress','GestureTap','GestureTapCancel','GestureTapDown','GesturePinchBegin','GesturePinchEnd','GesturePinchUpdate','KeyDown','KeyUp','MouseDown','MouseEnter','MouseLeave','MouseMove','MouseUp','MouseWheel','RawKeyDown','ScrollUpdate','TouchCancel','TouchEnd','TouchMove','TouchStart'];var allTypeNames=['InputLatency'];eventTypeNames.forEach(function(eventTypeName){allTypeNames.push('InputLatency:'+eventTypeName);allTypeNames.push('InputLatency::'+eventTypeName);});AsyncSlice.subTypes.register(InputLatencyAsyncSlice,{typeNames:allTypeNames,categoryParts:['latencyInfo']});return{InputLatencyAsyncSlice,INPUT_EVENT_TYPE_NAMES,};});'use strict';tr.exportTo('tr.model',function(){return{BROWSER_PROCESS_PID_REF:-1,OBJECT_DEFAULT_SCOPE:'ptr',LOCAL_ID_PHASES:new Set(['N','D','O','(',')'])};});'use strict';tr.exportTo('tr.e.audits',function(){var Auditor=tr.c.Auditor;function ChromeAuditor(model){Auditor.call(this,model);var modelHelper=this.model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(modelHelper&&modelHelper.browserHelper){this.modelHelper=modelHelper;}else{this.modelHelper=undefined;}}
+ChromeAuditor.prototype={__proto__:Auditor.prototype,runAnnotate:function(){if(!this.modelHelper)return;for(var pid in this.modelHelper.rendererHelpers){var rendererHelper=this.modelHelper.rendererHelpers[pid];if(rendererHelper.isChromeTracingUI){rendererHelper.process.important=false;}}},installUserFriendlyCategoryDriverIfNeeded:function(){this.model.addUserFriendlyCategoryDriver(tr.e.chrome.ChromeUserFriendlyCategoryDriver);},runAudit:function(){if(!this.modelHelper)return;this.model.replacePIDRefsInPatchups(tr.model.BROWSER_PROCESS_PID_REF,this.modelHelper.browserProcess.pid);this.model.applyObjectRefPatchups();}};Auditor.register(ChromeAuditor);return{ChromeAuditor,};});'use strict';tr.exportTo('tr.e.chrome',function(){var KNOWN_PROPERTIES={absX:1,absY:1,address:1,anonymous:1,childNeeds:1,children:1,classNames:1,col:1,colSpan:1,float:1,height:1,htmlId:1,name:1,posChildNeeds:1,positioned:1,positionedMovement:1,relX:1,relY:1,relativePositioned:1,row:1,rowSpan:1,selfNeeds:1,stickyPositioned:1,tag:1,width:1};function LayoutObject(snapshot,args){this.snapshot_=snapshot;this.id_=args.address;this.name_=args.name;this.childLayoutObjects_=[];this.otherProperties_={};this.tag_=args.tag;this.relativeRect_=tr.b.math.Rect.fromXYWH(args.relX,args.relY,args.width,args.height);this.absoluteRect_=tr.b.math.Rect.fromXYWH(args.absX,args.absY,args.width,args.height);this.isFloat_=args.float;this.isStickyPositioned_=args.stickyPositioned;this.isPositioned_=args.positioned;this.isRelativePositioned_=args.relativePositioned;this.isAnonymous_=args.anonymous;this.htmlId_=args.htmlId;this.classNames_=args.classNames;this.needsLayoutReasons_=[];if(args.selfNeeds){this.needsLayoutReasons_.push('self');}
+if(args.childNeeds){this.needsLayoutReasons_.push('child');}
+if(args.posChildNeeds){this.needsLayoutReasons_.push('positionedChild');}
+if(args.positionedMovement){this.needsLayoutReasons_.push('positionedMovement');}
+this.tableRow_=args.row;this.tableCol_=args.col;this.tableRowSpan_=args.rowSpan;this.tableColSpan_=args.colSpan;if(args.children){args.children.forEach(function(child){this.childLayoutObjects_.push(new LayoutObject(snapshot,child));}.bind(this));}
+for(var property in args){if(!KNOWN_PROPERTIES[property]){this.otherProperties_[property]=args[property];}}}
+LayoutObject.prototype={get snapshot(){return this.snapshot_;},get id(){return this.id_;},get name(){return this.name_;},get tag(){return this.tag_;},get relativeRect(){return this.relativeRect_;},get absoluteRect(){return this.absoluteRect_;},get isPositioned(){return this.isPositioned_;},get isFloat(){return this.isFloat_;},get isStickyPositioned(){return this.isStickyPositioned_;},get isRelativePositioned(){return this.isRelativePositioned_;},get isAnonymous(){return this.isAnonymous_;},get tableRow(){return this.tableRow_;},get tableCol(){return this.tableCol_;},get tableRowSpan(){return this.tableRowSpan_;},get tableColSpan(){return this.tableColSpan_;},get htmlId(){return this.htmlId_;},get classNames(){return this.classNames_;},get needsLayoutReasons(){return this.needsLayoutReasons_;},get hasChildLayoutObjects(){return this.childLayoutObjects_.length>0;},get childLayoutObjects(){return this.childLayoutObjects_;},traverseTree:function(cb,opt_this){cb.call(opt_this,this);if(!this.hasChildLayoutObjects)return;this.childLayoutObjects.forEach(function(child){child.traverseTree(cb,opt_this);});},get otherPropertyNames(){var names=[];for(var name in this.otherProperties_){names.push(name);}
+return names;},getProperty:function(name){return this.otherProperties_[name];},get previousSnapshotLayoutObject(){if(!this.snapshot.previousSnapshot)return undefined;return this.snapshot.previousSnapshot.getLayoutObjectById(this.id);},get nextSnapshotLayoutObject(){if(!this.snapshot.nextSnapshot)return undefined;return this.snapshot.nextSnapshot.getLayoutObjectById(this.id);}};return{LayoutObject,};});'use strict';tr.exportTo('tr.e.chrome',function(){var ObjectSnapshot=tr.model.ObjectSnapshot;var ObjectInstance=tr.model.ObjectInstance;function LayoutTreeInstance(){ObjectInstance.apply(this,arguments);}
 LayoutTreeInstance.prototype={__proto__:ObjectInstance.prototype,};ObjectInstance.subTypes.register(LayoutTreeInstance,{typeName:'LayoutTree'});function LayoutTreeSnapshot(){ObjectSnapshot.apply(this,arguments);this.rootLayoutObject=new tr.e.chrome.LayoutObject(this,this.args);}
-LayoutTreeSnapshot.prototype={__proto__:ObjectSnapshot.prototype,};ObjectSnapshot.subTypes.register(LayoutTreeSnapshot,{typeName:'LayoutTree'});return{LayoutTreeInstance:LayoutTreeInstance,LayoutTreeSnapshot:LayoutTreeSnapshot};});'use strict';tr.exportTo('tr.b',function(){function Base64(){}
-function b64ToUint6(nChr){if(nChr>64&&nChr<91)
-return nChr-65;if(nChr>96&&nChr<123)
-return nChr-71;if(nChr>47&&nChr<58)
-return nChr+4;if(nChr===43)
-return 62;if(nChr===47)
-return 63;return 0;}
-Base64.getDecodedBufferLength=function(input){return input.length*3+1>>2;};Base64.EncodeArrayBufferToString=function(input){var binary='';var bytes=new Uint8Array(input);var len=bytes.byteLength;for(var i=0;i<len;i++)
-binary+=String.fromCharCode(bytes[i]);return btoa(binary);};Base64.DecodeToTypedArray=function(input,output){var nInLen=input.length;var nOutLen=nInLen*3+1>>2;var nMod3=0;var nMod4=0;var nUint24=0;var nOutIdx=0;if(nOutLen>output.byteLength)
-throw new Error('Output buffer too small to decode.');for(var nInIdx=0;nInIdx<nInLen;nInIdx++){nMod4=nInIdx&3;nUint24|=b64ToUint6(input.charCodeAt(nInIdx))<<18-6*nMod4;if(nMod4===3||nInLen-nInIdx===1){for(nMod3=0;nMod3<3&&nOutIdx<nOutLen;nMod3++,nOutIdx++){output.setUint8(nOutIdx,nUint24>>>(16>>>nMod3&24)&255);}
+LayoutTreeSnapshot.prototype={__proto__:ObjectSnapshot.prototype,};ObjectSnapshot.subTypes.register(LayoutTreeSnapshot,{typeName:'LayoutTree'});return{LayoutTreeInstance,LayoutTreeSnapshot,};});'use strict';tr.exportTo('tr.model',function(){function EventContainer(){this.guid_=tr.b.GUID.allocateSimple();this.important=true;this.bounds_=new tr.b.math.Range();}
+EventContainer.prototype={get guid(){return this.guid_;},get stableId(){throw new Error('Not implemented');},get bounds(){return this.bounds_;},updateBounds:function(){throw new Error('Not implemented');},shiftTimestampsForward:function(amount){throw new Error('Not implemented');},childEvents:function*(){},getDescendantEvents:function*(){yield*this.childEvents();for(var container of this.childEventContainers()){yield*container.getDescendantEvents();}},childEventContainers:function*(){},getDescendantEventContainers:function*(){yield this;for(var container of this.childEventContainers()){yield*container.getDescendantEventContainers();}},findTopmostSlicesInThisContainer:function*(eventPredicate,opt_this){},findTopmostSlices:function*(eventPredicate){for(var ec of this.getDescendantEventContainers()){yield*ec.findTopmostSlicesInThisContainer(eventPredicate);}},findTopmostSlicesNamed:function*(name){yield*this.findTopmostSlices(e=>e.title===name);}};return{EventContainer,};});'use strict';tr.exportTo('tr.model',function(){var Event=tr.model.Event;var EventRegistry=tr.model.EventRegistry;class ResourceUsageSample extends Event{constructor(series,start,usage){super();this.series_=series;this.start_=start;this.usage_=usage;}
+get series(){return this.series_;}
+get start(){return this.start_;}
+set start(value){this.start_=value;}
+get usage(){return this.usage_;}
+set usage(value){this.usage_=value;}
+addBoundsToRange(range){range.addValue(this.start);}}
+EventRegistry.register(ResourceUsageSample,{name:'resourceUsageSample',pluralName:'resourceUsageSamples'});return{ResourceUsageSample,};});'use strict';tr.exportTo('tr.model',function(){var ResourceUsageSample=tr.model.ResourceUsageSample;class ResourceUsageSeries extends tr.model.EventContainer{constructor(device){super();this.device_=device;this.samples_=[];}
+get device(){return this.device_;}
+get samples(){return this.samples_;}
+get stableId(){return this.device_.stableId+'.ResourceUsageSeries';}
+addUsageSample(ts,val){var sample=new ResourceUsageSample(this,ts,val);this.samples_.push(sample);return sample;}
+computeResourceTimeConsumedInMs(start,end){var measurementRange=tr.b.math.Range.fromExplicitRange(start,end);var resourceTimeInMs=0;var startIndex=tr.b.math.findLowIndexInSortedArray(this.samples,x=>x.start,start)-1;var endIndex=tr.b.math.findLowIndexInSortedArray(this.samples,x=>x.start,end);if(startIndex<0)startIndex=0;for(var i=startIndex;i<endIndex;i++){var sample=this.samples[i];var nextSample=this.samples[i+1];var sampleRange=new tr.b.math.Range();sampleRange.addValue(sample.start);sampleRange.addValue(nextSample?nextSample.start:sample.start);var intersectionRangeInMs=measurementRange.findIntersection(sampleRange);resourceTimeInMs+=intersectionRangeInMs.duration*sample.usage;}
+return resourceTimeInMs;}
+getSamplesWithinRange(start,end){var startIndex=tr.b.math.findLowIndexInSortedArray(this.samples,x=>x.start,start);var endIndex=tr.b.math.findLowIndexInSortedArray(this.samples,x=>x.start,end);return this.samples.slice(startIndex,endIndex);}
+shiftTimestampsForward(amount){for(var i=0;i<this.samples_.length;++i){this.samples_[i].start+=amount;}}
+updateBounds(){this.bounds.reset();if(this.samples_.length===0)return;this.bounds.addValue(this.samples_[0].start);this.bounds.addValue(this.samples_[this.samples_.length-1].start);}*childEvents(){yield*this.samples_;}}
+return{ResourceUsageSeries,};});'use strict';tr.exportTo('tr.e.audits',function(){class CpuUsageAuditor extends tr.c.Auditor{constructor(model){super();this.model_=model;}
+runAnnotate(){this.model_.device.cpuUsageSeries=this.computeCpuUsageSeries_(this.model_.bounds.min,this.model_.bounds.max,this.computeCpuUsage_());}
+computeBinSize_(start,end){var MIN_BIN_SIZE_MS=1.0;var MAX_NUM_BINS=100000;var interval=end-start;var binSize=MIN_BIN_SIZE_MS;while(binSize*MAX_NUM_BINS<interval)binSize*=2;return binSize;}
+computeCpuUsageSeries_(start,end,usageRecords){var series=new tr.model.ResourceUsageSeries();if(start===undefined)return series;var binSize=this.computeBinSize_(start,end);var numBins=Math.ceil((end-start)/binSize);var arr=new Array(numBins).fill(0);for(var record of usageRecords){var firstIndex=Math.ceil((record.start-start)/binSize);var lastIndex=Math.floor((record.end-start)/binSize);for(var i=firstIndex;i<=lastIndex;i++)arr[i]+=record.usage;}
+for(var i=0;i<numBins;i++){series.addUsageSample(start+(i*binSize),arr[i]);}
+return series;}
+computeCpuUsage_(){var model=this.model_;var result=[];for(var pid in model.processes){for(var e of model.processes[pid].getDescendantEvents()){if(!(e instanceof tr.model.ThreadSlice)||e.duration===0||e.cpuDuration===undefined){continue;}
+if(e.selfTime===0||e.selfTime===undefined||e.cpuSelfTime===undefined){continue;}
+var usage=tr.b.math.clamp(e.cpuSelfTime/e.selfTime,0,1);var lastTime=e.start;for(var subslice of e.subSlices){result.push({usage,start:lastTime,end:subslice.start});lastTime=subslice.end;}
+result.push({usage,start:lastTime,end:e.end});}}
+return result;}}
+tr.c.Auditor.register(CpuUsageAuditor);return{CpuUsageAuditor:CpuUsageAuditor};});'use strict';tr.exportTo('tr.b',function(){function Base64(){}
+function b64ToUint6(nChr){if(nChr>64&&nChr<91)return nChr-65;if(nChr>96&&nChr<123)return nChr-71;if(nChr>47&&nChr<58)return nChr+4;if(nChr===43)return 62;if(nChr===47)return 63;return 0;}
+Base64.getDecodedBufferLength=function(input){return input.length*3+1>>2;};Base64.EncodeArrayBufferToString=function(input){var binary='';var bytes=new Uint8Array(input);var len=bytes.byteLength;for(var i=0;i<len;i++){binary+=String.fromCharCode(bytes[i]);}
+return btoa(binary);};Base64.DecodeToTypedArray=function(input,output){var nInLen=input.length;var nOutLen=nInLen*3+1>>2;var nMod3=0;var nMod4=0;var nUint24=0;var nOutIdx=0;if(nOutLen>output.byteLength){throw new Error('Output buffer too small to decode.');}
+for(var nInIdx=0;nInIdx<nInLen;nInIdx++){nMod4=nInIdx&3;nUint24|=b64ToUint6(input.charCodeAt(nInIdx))<<18-6*nMod4;if(nMod4===3||nInLen-nInIdx===1){for(nMod3=0;nMod3<3&&nOutIdx<nOutLen;nMod3++,nOutIdx++){output.setUint8(nOutIdx,nUint24>>>(16>>>nMod3&24)&255);}
 nUint24=0;}}
-return nOutIdx-1;};Base64.btoa=function(input){return btoa(input);};Base64.atob=function(input){return atob(input);};return{Base64:Base64};});'use strict';tr.exportTo('tr.e.importer.etw',function(){function Parser(importer){this.importer=importer;this.model=importer.model;}
-Parser.prototype={__proto__:Object.prototype};var options=new tr.b.ExtensionRegistryOptions(tr.b.BASIC_REGISTRY_MODE);options.mandatoryBaseClass=Parser;tr.b.decorateExtensionRegistry(Parser,options);return{Parser:Parser};});'use strict';tr.exportTo('tr.e.importer.etw',function(){var Parser=tr.e.importer.etw.Parser;var guid='68FDD900-4A3E-11D1-84F4-0000F80464E3';var kEventTraceHeaderOpcode=0;function EventTraceParser(importer){Parser.call(this,importer);importer.registerEventHandler(guid,kEventTraceHeaderOpcode,EventTraceParser.prototype.decodeHeader.bind(this));}
-EventTraceParser.prototype={__proto__:Parser.prototype,decodeFields:function(header,decoder){if(header.version!=2)
-throw new Error('Incompatible EventTrace event version.');var bufferSize=decoder.decodeUInt32();var version=decoder.decodeUInt32();var providerVersion=decoder.decodeUInt32();var numberOfProcessors=decoder.decodeUInt32();var endTime=decoder.decodeUInt64ToString();var timerResolution=decoder.decodeUInt32();var maxFileSize=decoder.decodeUInt32();var logFileMode=decoder.decodeUInt32();var buffersWritten=decoder.decodeUInt32();var startBuffers=decoder.decodeUInt32();var pointerSize=decoder.decodeUInt32();var eventsLost=decoder.decodeUInt32();var cpuSpeed=decoder.decodeUInt32();var loggerName=decoder.decodeUInteger(header.is64);var logFileName=decoder.decodeUInteger(header.is64);var timeZoneInformation=decoder.decodeTimeZoneInformation();var padding=decoder.decodeUInt32();var bootTime=decoder.decodeUInt64ToString();var perfFreq=decoder.decodeUInt64ToString();var startTime=decoder.decodeUInt64ToString();var reservedFlags=decoder.decodeUInt32();var buffersLost=decoder.decodeUInt32();var sessionNameString=decoder.decodeW16String();var logFileNameString=decoder.decodeW16String();return{bufferSize:bufferSize,version:version,providerVersion:providerVersion,numberOfProcessors:numberOfProcessors,endTime:endTime,timerResolution:timerResolution,maxFileSize:maxFileSize,logFileMode:logFileMode,buffersWritten:buffersWritten,startBuffers:startBuffers,pointerSize:pointerSize,eventsLost:eventsLost,cpuSpeed:cpuSpeed,loggerName:loggerName,logFileName:logFileName,timeZoneInformation:timeZoneInformation,bootTime:bootTime,perfFreq:perfFreq,startTime:startTime,reservedFlags:reservedFlags,buffersLost:buffersLost,sessionNameString:sessionNameString,logFileNameString:logFileNameString};},decodeHeader:function(header,decoder){var fields=this.decodeFields(header,decoder);return true;}};Parser.register(EventTraceParser);return{EventTraceParser:EventTraceParser};});'use strict';tr.exportTo('tr.e.importer.etw',function(){var Parser=tr.e.importer.etw.Parser;var guid='3D6FA8D0-FE05-11D0-9DDA-00C04FD7BA7C';var kProcessStartOpcode=1;var kProcessEndOpcode=2;var kProcessDCStartOpcode=3;var kProcessDCEndOpcode=4;var kProcessDefunctOpcode=39;function ProcessParser(importer){Parser.call(this,importer);importer.registerEventHandler(guid,kProcessStartOpcode,ProcessParser.prototype.decodeStart.bind(this));importer.registerEventHandler(guid,kProcessEndOpcode,ProcessParser.prototype.decodeEnd.bind(this));importer.registerEventHandler(guid,kProcessDCStartOpcode,ProcessParser.prototype.decodeDCStart.bind(this));importer.registerEventHandler(guid,kProcessDCEndOpcode,ProcessParser.prototype.decodeDCEnd.bind(this));importer.registerEventHandler(guid,kProcessDefunctOpcode,ProcessParser.prototype.decodeDefunct.bind(this));}
-ProcessParser.prototype={__proto__:Parser.prototype,decodeFields:function(header,decoder){if(header.version>5)
-throw new Error('Incompatible Process event version.');var pageDirectoryBase;if(header.version==1)
-pageDirectoryBase=decoder.decodeUInteger(header.is64);var uniqueProcessKey;if(header.version>=2)
-uniqueProcessKey=decoder.decodeUInteger(header.is64);var processId=decoder.decodeUInt32();var parentId=decoder.decodeUInt32();var sessionId;var exitStatus;if(header.version>=1){sessionId=decoder.decodeUInt32();exitStatus=decoder.decodeInt32();}
-var directoryTableBase;if(header.version>=3)
-directoryTableBase=decoder.decodeUInteger(header.is64);var flags;if(header.version>=4)
-flags=decoder.decodeUInt32();var userSID=decoder.decodeSID(header.is64);var imageFileName;if(header.version>=1)
-imageFileName=decoder.decodeString();var commandLine;if(header.version>=2)
-commandLine=decoder.decodeW16String();var packageFullName;var applicationId;if(header.version>=4){packageFullName=decoder.decodeW16String();applicationId=decoder.decodeW16String();}
-var exitTime;if(header.version==5&&header.opcode==kProcessDefunctOpcode)
-exitTime=decoder.decodeUInt64ToString();return{pageDirectoryBase:pageDirectoryBase,uniqueProcessKey:uniqueProcessKey,processId:processId,parentId:parentId,sessionId:sessionId,exitStatus:exitStatus,directoryTableBase:directoryTableBase,flags:flags,userSID:userSID,imageFileName:imageFileName,commandLine:commandLine,packageFullName:packageFullName,applicationId:applicationId,exitTime:exitTime};},decodeStart:function(header,decoder){var fields=this.decodeFields(header,decoder);var process=this.model.getOrCreateProcess(fields.processId);if(process.hasOwnProperty('has_ended')){throw new Error('Process clash detected.');}
-process.name=fields.imageFileName;return true;},decodeEnd:function(header,decoder){var fields=this.decodeFields(header,decoder);var process=this.model.getOrCreateProcess(fields.processId);process.has_ended=true;return true;},decodeDCStart:function(header,decoder){var fields=this.decodeFields(header,decoder);var process=this.model.getOrCreateProcess(fields.processId);if(process.hasOwnProperty('has_ended'))
-throw new Error('Process clash detected.');process.name=fields.imageFileName;return true;},decodeDCEnd:function(header,decoder){var fields=this.decodeFields(header,decoder);var process=this.model.getOrCreateProcess(fields.processId);process.has_ended=true;return true;},decodeDefunct:function(header,decoder){var fields=this.decodeFields(header,decoder);return true;}};Parser.register(ProcessParser);return{ProcessParser:ProcessParser};});'use strict';tr.exportTo('tr.e.importer.etw',function(){var Parser=tr.e.importer.etw.Parser;var guid='3D6FA8D1-FE05-11D0-9DDA-00C04FD7BA7C';var kThreadStartOpcode=1;var kThreadEndOpcode=2;var kThreadDCStartOpcode=3;var kThreadDCEndOpcode=4;var kThreadCSwitchOpcode=36;function ThreadParser(importer){Parser.call(this,importer);importer.registerEventHandler(guid,kThreadStartOpcode,ThreadParser.prototype.decodeStart.bind(this));importer.registerEventHandler(guid,kThreadEndOpcode,ThreadParser.prototype.decodeEnd.bind(this));importer.registerEventHandler(guid,kThreadDCStartOpcode,ThreadParser.prototype.decodeDCStart.bind(this));importer.registerEventHandler(guid,kThreadDCEndOpcode,ThreadParser.prototype.decodeDCEnd.bind(this));importer.registerEventHandler(guid,kThreadCSwitchOpcode,ThreadParser.prototype.decodeCSwitch.bind(this));}
-ThreadParser.prototype={__proto__:Parser.prototype,decodeFields:function(header,decoder){if(header.version>3)
-throw new Error('Incompatible Thread event version.');var processId=decoder.decodeUInt32();var threadId=decoder.decodeUInt32();var stackBase;var stackLimit;var userStackBase;var userStackLimit;var affinity;var startAddr;var win32StartAddr;var tebBase;var subProcessTag;var basePriority;var pagePriority;var ioPriority;var threadFlags;var waitMode;if(header.version==1){if(header.opcode==kThreadStartOpcode||header.opcode==kThreadDCStartOpcode){stackBase=decoder.decodeUInteger(header.is64);stackLimit=decoder.decodeUInteger(header.is64);userStackBase=decoder.decodeUInteger(header.is64);userStackLimit=decoder.decodeUInteger(header.is64);startAddr=decoder.decodeUInteger(header.is64);win32StartAddr=decoder.decodeUInteger(header.is64);waitMode=decoder.decodeInt8();decoder.skip(3);}}else{stackBase=decoder.decodeUInteger(header.is64);stackLimit=decoder.decodeUInteger(header.is64);userStackBase=decoder.decodeUInteger(header.is64);userStackLimit=decoder.decodeUInteger(header.is64);if(header.version==2)
-startAddr=decoder.decodeUInteger(header.is64);else
-affinity=decoder.decodeUInteger(header.is64);win32StartAddr=decoder.decodeUInteger(header.is64);tebBase=decoder.decodeUInteger(header.is64);subProcessTag=decoder.decodeUInt32();if(header.version==3){basePriority=decoder.decodeUInt8();pagePriority=decoder.decodeUInt8();ioPriority=decoder.decodeUInt8();threadFlags=decoder.decodeUInt8();}}
-return{processId:processId,threadId:threadId,stackBase:stackBase,stackLimit:stackLimit,userStackBase:userStackBase,userStackLimit:userStackLimit,affinity:affinity,startAddr:startAddr,win32StartAddr:win32StartAddr,tebBase:tebBase,subProcessTag:subProcessTag,waitMode:waitMode,basePriority:basePriority,pagePriority:pagePriority,ioPriority:ioPriority,threadFlags:threadFlags};},decodeCSwitchFields:function(header,decoder){if(header.version!=2)
-throw new Error('Incompatible Thread event version.');var newThreadId=decoder.decodeUInt32();var oldThreadId=decoder.decodeUInt32();var newThreadPriority=decoder.decodeInt8();var oldThreadPriority=decoder.decodeInt8();var previousCState=decoder.decodeUInt8();var spareByte=decoder.decodeInt8();var oldThreadWaitReason=decoder.decodeInt8();var oldThreadWaitMode=decoder.decodeInt8();var oldThreadState=decoder.decodeInt8();var oldThreadWaitIdealProcessor=decoder.decodeInt8();var newThreadWaitTime=decoder.decodeUInt32();var reserved=decoder.decodeUInt32();return{newThreadId:newThreadId,oldThreadId:oldThreadId,newThreadPriority:newThreadPriority,oldThreadPriority:oldThreadPriority,previousCState:previousCState,spareByte:spareByte,oldThreadWaitReason:oldThreadWaitReason,oldThreadWaitMode:oldThreadWaitMode,oldThreadState:oldThreadState,oldThreadWaitIdealProcessor:oldThreadWaitIdealProcessor,newThreadWaitTime:newThreadWaitTime,reserved:reserved};},decodeStart:function(header,decoder){var fields=this.decodeFields(header,decoder);this.importer.createThreadIfNeeded(fields.processId,fields.threadId);return true;},decodeEnd:function(header,decoder){var fields=this.decodeFields(header,decoder);this.importer.removeThreadIfPresent(fields.threadId);return true;},decodeDCStart:function(header,decoder){var fields=this.decodeFields(header,decoder);this.importer.createThreadIfNeeded(fields.processId,fields.threadId);return true;},decodeDCEnd:function(header,decoder){var fields=this.decodeFields(header,decoder);this.importer.removeThreadIfPresent(fields.threadId);return true;},decodeCSwitch:function(header,decoder){var fields=this.decodeCSwitchFields(header,decoder);var cpu=this.importer.getOrCreateCpu(header.cpu);var new_thread=this.importer.getThreadFromWindowsTid(fields.newThreadId);var new_thread_name;if(new_thread&&new_thread.userFriendlyName){new_thread_name=new_thread.userFriendlyName;}else{var new_process_id=this.importer.getPidFromWindowsTid(fields.newThreadId);var new_process=this.model.getProcess(new_process_id);var new_process_name;if(new_process)
-new_process_name=new_process.name;else
-new_process_name='Unknown process';new_thread_name=new_process_name+' (tid '+fields.newThreadId+')';}
-cpu.switchActiveThread(header.timestamp,{},fields.newThreadId,new_thread_name,fields);return true;}};Parser.register(ThreadParser);return{ThreadParser:ThreadParser};});'use strict';tr.exportTo('tr.b',function(){function max(a,b){if(a===undefined)
-return b;if(b===undefined)
-return a;return Math.max(a,b);}
+return nOutIdx-1;};Base64.btoa=function(input){return btoa(input);};Base64.atob=function(input){return atob(input);};return{Base64,};});'use strict';tr.exportTo('tr.e.importer.etw',function(){function Parser(importer){this.importer=importer;this.model=importer.model;}
+Parser.prototype={__proto__:Object.prototype};var options=new tr.b.ExtensionRegistryOptions(tr.b.BASIC_REGISTRY_MODE);options.mandatoryBaseClass=Parser;tr.b.decorateExtensionRegistry(Parser,options);return{Parser,};});'use strict';tr.exportTo('tr.e.importer.etw',function(){var Parser=tr.e.importer.etw.Parser;var guid='68FDD900-4A3E-11D1-84F4-0000F80464E3';var kEventTraceHeaderOpcode=0;function EventTraceParser(importer){Parser.call(this,importer);importer.registerEventHandler(guid,kEventTraceHeaderOpcode,EventTraceParser.prototype.decodeHeader.bind(this));}
+EventTraceParser.prototype={__proto__:Parser.prototype,decodeFields:function(header,decoder){if(header.version!==2){throw new Error('Incompatible EventTrace event version.');}
+var bufferSize=decoder.decodeUInt32();var version=decoder.decodeUInt32();var providerVersion=decoder.decodeUInt32();var numberOfProcessors=decoder.decodeUInt32();var endTime=decoder.decodeUInt64ToString();var timerResolution=decoder.decodeUInt32();var maxFileSize=decoder.decodeUInt32();var logFileMode=decoder.decodeUInt32();var buffersWritten=decoder.decodeUInt32();var startBuffers=decoder.decodeUInt32();var pointerSize=decoder.decodeUInt32();var eventsLost=decoder.decodeUInt32();var cpuSpeed=decoder.decodeUInt32();var loggerName=decoder.decodeUInteger(header.is64);var logFileName=decoder.decodeUInteger(header.is64);var timeZoneInformation=decoder.decodeTimeZoneInformation();var padding=decoder.decodeUInt32();var bootTime=decoder.decodeUInt64ToString();var perfFreq=decoder.decodeUInt64ToString();var startTime=decoder.decodeUInt64ToString();var reservedFlags=decoder.decodeUInt32();var buffersLost=decoder.decodeUInt32();var sessionNameString=decoder.decodeW16String();var logFileNameString=decoder.decodeW16String();return{bufferSize:bufferSize,version:version,providerVersion:providerVersion,numberOfProcessors:numberOfProcessors,endTime:endTime,timerResolution:timerResolution,maxFileSize:maxFileSize,logFileMode:logFileMode,buffersWritten:buffersWritten,startBuffers:startBuffers,pointerSize:pointerSize,eventsLost:eventsLost,cpuSpeed:cpuSpeed,loggerName:loggerName,logFileName:logFileName,timeZoneInformation:timeZoneInformation,bootTime:bootTime,perfFreq:perfFreq,startTime:startTime,reservedFlags:reservedFlags,buffersLost:buffersLost,sessionNameString:sessionNameString,logFileNameString:logFileNameString};},decodeHeader:function(header,decoder){var fields=this.decodeFields(header,decoder);return true;}};Parser.register(EventTraceParser);return{EventTraceParser,};});'use strict';tr.exportTo('tr.e.importer.etw',function(){var Parser=tr.e.importer.etw.Parser;var guid='3D6FA8D0-FE05-11D0-9DDA-00C04FD7BA7C';var kProcessStartOpcode=1;var kProcessEndOpcode=2;var kProcessDCStartOpcode=3;var kProcessDCEndOpcode=4;var kProcessDefunctOpcode=39;function ProcessParser(importer){Parser.call(this,importer);importer.registerEventHandler(guid,kProcessStartOpcode,ProcessParser.prototype.decodeStart.bind(this));importer.registerEventHandler(guid,kProcessEndOpcode,ProcessParser.prototype.decodeEnd.bind(this));importer.registerEventHandler(guid,kProcessDCStartOpcode,ProcessParser.prototype.decodeDCStart.bind(this));importer.registerEventHandler(guid,kProcessDCEndOpcode,ProcessParser.prototype.decodeDCEnd.bind(this));importer.registerEventHandler(guid,kProcessDefunctOpcode,ProcessParser.prototype.decodeDefunct.bind(this));}
+ProcessParser.prototype={__proto__:Parser.prototype,decodeFields:function(header,decoder){if(header.version>5){throw new Error('Incompatible Process event version.');}
+var pageDirectoryBase;if(header.version===1){pageDirectoryBase=decoder.decodeUInteger(header.is64);}
+var uniqueProcessKey;if(header.version>=2){uniqueProcessKey=decoder.decodeUInteger(header.is64);}
+var processId=decoder.decodeUInt32();var parentId=decoder.decodeUInt32();var sessionId;var exitStatus;if(header.version>=1){sessionId=decoder.decodeUInt32();exitStatus=decoder.decodeInt32();}
+var directoryTableBase;if(header.version>=3){directoryTableBase=decoder.decodeUInteger(header.is64);}
+var flags;if(header.version>=4){flags=decoder.decodeUInt32();}
+var userSID=decoder.decodeSID(header.is64);var imageFileName;if(header.version>=1){imageFileName=decoder.decodeString();}
+var commandLine;if(header.version>=2){commandLine=decoder.decodeW16String();}
+var packageFullName;var applicationId;if(header.version>=4){packageFullName=decoder.decodeW16String();applicationId=decoder.decodeW16String();}
+var exitTime;if(header.version===5&&header.opcode===kProcessDefunctOpcode){exitTime=decoder.decodeUInt64ToString();}
+return{pageDirectoryBase:pageDirectoryBase,uniqueProcessKey:uniqueProcessKey,processId:processId,parentId:parentId,sessionId:sessionId,exitStatus:exitStatus,directoryTableBase:directoryTableBase,flags:flags,userSID:userSID,imageFileName:imageFileName,commandLine:commandLine,packageFullName:packageFullName,applicationId:applicationId,exitTime:exitTime};},decodeStart:function(header,decoder){var fields=this.decodeFields(header,decoder);var process=this.model.getOrCreateProcess(fields.processId);if(process.hasOwnProperty('has_ended')){throw new Error('Process clash detected.');}
+process.name=fields.imageFileName;return true;},decodeEnd:function(header,decoder){var fields=this.decodeFields(header,decoder);var process=this.model.getOrCreateProcess(fields.processId);process.has_ended=true;return true;},decodeDCStart:function(header,decoder){var fields=this.decodeFields(header,decoder);var process=this.model.getOrCreateProcess(fields.processId);if(process.hasOwnProperty('has_ended')){throw new Error('Process clash detected.');}
+process.name=fields.imageFileName;return true;},decodeDCEnd:function(header,decoder){var fields=this.decodeFields(header,decoder);var process=this.model.getOrCreateProcess(fields.processId);process.has_ended=true;return true;},decodeDefunct:function(header,decoder){var fields=this.decodeFields(header,decoder);return true;}};Parser.register(ProcessParser);return{ProcessParser,};});'use strict';tr.exportTo('tr.e.importer.etw',function(){var Parser=tr.e.importer.etw.Parser;var guid='3D6FA8D1-FE05-11D0-9DDA-00C04FD7BA7C';var kThreadStartOpcode=1;var kThreadEndOpcode=2;var kThreadDCStartOpcode=3;var kThreadDCEndOpcode=4;var kThreadCSwitchOpcode=36;function ThreadParser(importer){Parser.call(this,importer);importer.registerEventHandler(guid,kThreadStartOpcode,ThreadParser.prototype.decodeStart.bind(this));importer.registerEventHandler(guid,kThreadEndOpcode,ThreadParser.prototype.decodeEnd.bind(this));importer.registerEventHandler(guid,kThreadDCStartOpcode,ThreadParser.prototype.decodeDCStart.bind(this));importer.registerEventHandler(guid,kThreadDCEndOpcode,ThreadParser.prototype.decodeDCEnd.bind(this));importer.registerEventHandler(guid,kThreadCSwitchOpcode,ThreadParser.prototype.decodeCSwitch.bind(this));}
+ThreadParser.prototype={__proto__:Parser.prototype,decodeFields:function(header,decoder){if(header.version>3){throw new Error('Incompatible Thread event version.');}
+var processId=decoder.decodeUInt32();var threadId=decoder.decodeUInt32();var stackBase;var stackLimit;var userStackBase;var userStackLimit;var affinity;var startAddr;var win32StartAddr;var tebBase;var subProcessTag;var basePriority;var pagePriority;var ioPriority;var threadFlags;var waitMode;if(header.version===1){if(header.opcode===kThreadStartOpcode||header.opcode===kThreadDCStartOpcode){stackBase=decoder.decodeUInteger(header.is64);stackLimit=decoder.decodeUInteger(header.is64);userStackBase=decoder.decodeUInteger(header.is64);userStackLimit=decoder.decodeUInteger(header.is64);startAddr=decoder.decodeUInteger(header.is64);win32StartAddr=decoder.decodeUInteger(header.is64);waitMode=decoder.decodeInt8();decoder.skip(3);}}else{stackBase=decoder.decodeUInteger(header.is64);stackLimit=decoder.decodeUInteger(header.is64);userStackBase=decoder.decodeUInteger(header.is64);userStackLimit=decoder.decodeUInteger(header.is64);if(header.version===2){startAddr=decoder.decodeUInteger(header.is64);}else{affinity=decoder.decodeUInteger(header.is64);}
+win32StartAddr=decoder.decodeUInteger(header.is64);tebBase=decoder.decodeUInteger(header.is64);subProcessTag=decoder.decodeUInt32();if(header.version===3){basePriority=decoder.decodeUInt8();pagePriority=decoder.decodeUInt8();ioPriority=decoder.decodeUInt8();threadFlags=decoder.decodeUInt8();}}
+return{processId:processId,threadId:threadId,stackBase:stackBase,stackLimit:stackLimit,userStackBase:userStackBase,userStackLimit:userStackLimit,affinity:affinity,startAddr:startAddr,win32StartAddr:win32StartAddr,tebBase:tebBase,subProcessTag:subProcessTag,waitMode:waitMode,basePriority:basePriority,pagePriority:pagePriority,ioPriority:ioPriority,threadFlags:threadFlags};},decodeCSwitchFields:function(header,decoder){if(header.version!==2){throw new Error('Incompatible Thread event version.');}
+var newThreadId=decoder.decodeUInt32();var oldThreadId=decoder.decodeUInt32();var newThreadPriority=decoder.decodeInt8();var oldThreadPriority=decoder.decodeInt8();var previousCState=decoder.decodeUInt8();var spareByte=decoder.decodeInt8();var oldThreadWaitReason=decoder.decodeInt8();var oldThreadWaitMode=decoder.decodeInt8();var oldThreadState=decoder.decodeInt8();var oldThreadWaitIdealProcessor=decoder.decodeInt8();var newThreadWaitTime=decoder.decodeUInt32();var reserved=decoder.decodeUInt32();return{newThreadId:newThreadId,oldThreadId:oldThreadId,newThreadPriority:newThreadPriority,oldThreadPriority:oldThreadPriority,previousCState:previousCState,spareByte:spareByte,oldThreadWaitReason:oldThreadWaitReason,oldThreadWaitMode:oldThreadWaitMode,oldThreadState:oldThreadState,oldThreadWaitIdealProcessor:oldThreadWaitIdealProcessor,newThreadWaitTime:newThreadWaitTime,reserved:reserved};},decodeStart:function(header,decoder){var fields=this.decodeFields(header,decoder);this.importer.createThreadIfNeeded(fields.processId,fields.threadId);return true;},decodeEnd:function(header,decoder){var fields=this.decodeFields(header,decoder);this.importer.removeThreadIfPresent(fields.threadId);return true;},decodeDCStart:function(header,decoder){var fields=this.decodeFields(header,decoder);this.importer.createThreadIfNeeded(fields.processId,fields.threadId);return true;},decodeDCEnd:function(header,decoder){var fields=this.decodeFields(header,decoder);this.importer.removeThreadIfPresent(fields.threadId);return true;},decodeCSwitch:function(header,decoder){var fields=this.decodeCSwitchFields(header,decoder);var cpu=this.importer.getOrCreateCpu(header.cpu);var newThread=this.importer.getThreadFromWindowsTid(fields.newThreadId);var newThreadName;if(newThread&&newThread.userFriendlyName){newThreadName=newThread.userFriendlyName;}else{var newProcessId=this.importer.getPidFromWindowsTid(fields.newThreadId);var newProcess=this.model.getProcess(newProcessId);var newProcessName;if(newProcess){newProcessName=newProcess.name;}else{newProcessName='Unknown process';}
+newThreadName=newProcessName+' (tid '+fields.newThreadId+')';}
+cpu.switchActiveThread(header.timestamp,{},fields.newThreadId,newThreadName,fields);return true;}};Parser.register(ThreadParser);return{ThreadParser,};});'use strict';tr.exportTo('tr.b',function(){function max(a,b){if(a===undefined)return b;if(b===undefined)return a;return Math.max(a,b);}
 function IntervalTree(beginPositionCb,endPositionCb){this.beginPositionCb_=beginPositionCb;this.endPositionCb_=endPositionCb;this.root_=undefined;this.size_=0;}
-IntervalTree.prototype={insert:function(datum){var startPosition=this.beginPositionCb_(datum);var endPosition=this.endPositionCb_(datum);var node=new IntervalTreeNode(datum,startPosition,endPosition);this.size_++;this.root_=this.insertNode_(this.root_,node);this.root_.colour=Colour.BLACK;return datum;},insertNode_:function(root,node){if(root===undefined)
-return node;if(root.leftNode&&root.leftNode.isRed&&root.rightNode&&root.rightNode.isRed)
-this.flipNodeColour_(root);if(node.key<root.key)
-root.leftNode=this.insertNode_(root.leftNode,node);else if(node.key===root.key)
-root.merge(node);else
-root.rightNode=this.insertNode_(root.rightNode,node);if(root.rightNode&&root.rightNode.isRed&&(root.leftNode===undefined||!root.leftNode.isRed))
-root=this.rotateLeft_(root);if(root.leftNode&&root.leftNode.isRed&&root.leftNode.leftNode&&root.leftNode.leftNode.isRed)
-root=this.rotateRight_(root);return root;},rotateRight_:function(node){var sibling=node.leftNode;node.leftNode=sibling.rightNode;sibling.rightNode=node;sibling.colour=node.colour;node.colour=Colour.RED;return sibling;},rotateLeft_:function(node){var sibling=node.rightNode;node.rightNode=sibling.leftNode;sibling.leftNode=node;sibling.colour=node.colour;node.colour=Colour.RED;return sibling;},flipNodeColour_:function(node){node.colour=this.flipColour_(node.colour);node.leftNode.colour=this.flipColour_(node.leftNode.colour);node.rightNode.colour=this.flipColour_(node.rightNode.colour);},flipColour_:function(colour){return colour===Colour.RED?Colour.BLACK:Colour.RED;},updateHighValues:function(){this.updateHighValues_(this.root_);},updateHighValues_:function(node){if(node===undefined)
-return undefined;node.maxHighLeft=this.updateHighValues_(node.leftNode);node.maxHighRight=this.updateHighValues_(node.rightNode);return max(max(node.maxHighLeft,node.highValue),node.maxHighRight);},validateFindArguments_:function(queryLow,queryHigh){if(queryLow===undefined||queryHigh===undefined)
-throw new Error('queryLow and queryHigh must be defined');if((typeof queryLow!=='number')||(typeof queryHigh!=='number'))
-throw new Error('queryLow and queryHigh must be numbers');},findIntersection:function(queryLow,queryHigh){this.validateFindArguments_(queryLow,queryHigh);if(this.root_===undefined)
-return[];var ret=[];this.root_.appendIntersectionsInto_(ret,queryLow,queryHigh);return ret;},get size(){return this.size_;},get root(){return this.root_;},dump_:function(){if(this.root_===undefined)
-return[];return this.root_.dump();}};var Colour={RED:'red',BLACK:'black'};function IntervalTreeNode(datum,lowValue,highValue){this.lowValue_=lowValue;this.data_=[{datum:datum,high:highValue,low:lowValue}];this.colour_=Colour.RED;this.parentNode_=undefined;this.leftNode_=undefined;this.rightNode_=undefined;this.maxHighLeft_=undefined;this.maxHighRight_=undefined;}
-IntervalTreeNode.prototype={appendIntersectionsInto_:function(ret,queryLow,queryHigh){if(this.lowValue_>=queryHigh){if(!this.leftNode_)
-return;return this.leftNode_.appendIntersectionsInto_(ret,queryLow,queryHigh);}
+IntervalTree.prototype={insert:function(datum){var startPosition=this.beginPositionCb_(datum);var endPosition=this.endPositionCb_(datum);var node=new IntervalTreeNode(datum,startPosition,endPosition);this.size_++;this.root_=this.insertNode_(this.root_,node);this.root_.colour=Colour.BLACK;return datum;},insertNode_:function(root,node){if(root===undefined)return node;if(root.leftNode&&root.leftNode.isRed&&root.rightNode&&root.rightNode.isRed){this.flipNodeColour_(root);}
+if(node.key<root.key){root.leftNode=this.insertNode_(root.leftNode,node);}else if(node.key===root.key){root.merge(node);}else{root.rightNode=this.insertNode_(root.rightNode,node);}
+if(root.rightNode&&root.rightNode.isRed&&(root.leftNode===undefined||!root.leftNode.isRed)){root=this.rotateLeft_(root);}
+if(root.leftNode&&root.leftNode.isRed&&root.leftNode.leftNode&&root.leftNode.leftNode.isRed){root=this.rotateRight_(root);}
+return root;},rotateRight_:function(node){var sibling=node.leftNode;node.leftNode=sibling.rightNode;sibling.rightNode=node;sibling.colour=node.colour;node.colour=Colour.RED;return sibling;},rotateLeft_:function(node){var sibling=node.rightNode;node.rightNode=sibling.leftNode;sibling.leftNode=node;sibling.colour=node.colour;node.colour=Colour.RED;return sibling;},flipNodeColour_:function(node){node.colour=this.flipColour_(node.colour);node.leftNode.colour=this.flipColour_(node.leftNode.colour);node.rightNode.colour=this.flipColour_(node.rightNode.colour);},flipColour_:function(colour){return colour===Colour.RED?Colour.BLACK:Colour.RED;},updateHighValues:function(){this.updateHighValues_(this.root_);},updateHighValues_:function(node){if(node===undefined)return undefined;node.maxHighLeft=this.updateHighValues_(node.leftNode);node.maxHighRight=this.updateHighValues_(node.rightNode);return max(max(node.maxHighLeft,node.highValue),node.maxHighRight);},validateFindArguments_:function(queryLow,queryHigh){if(queryLow===undefined||queryHigh===undefined){throw new Error('queryLow and queryHigh must be defined');}
+if((typeof queryLow!=='number')||(typeof queryHigh!=='number')){throw new Error('queryLow and queryHigh must be numbers');}},findIntersection:function(queryLow,queryHigh){this.validateFindArguments_(queryLow,queryHigh);if(this.root_===undefined)return[];var ret=[];this.root_.appendIntersectionsInto_(ret,queryLow,queryHigh);return ret;},get size(){return this.size_;},get root(){return this.root_;},dump_:function(){if(this.root_===undefined)return[];return this.root_.dump();}};var Colour={RED:'red',BLACK:'black'};function IntervalTreeNode(datum,lowValue,highValue){this.lowValue_=lowValue;this.data_=[{datum:datum,high:highValue,low:lowValue}];this.colour_=Colour.RED;this.parentNode_=undefined;this.leftNode_=undefined;this.rightNode_=undefined;this.maxHighLeft_=undefined;this.maxHighRight_=undefined;}
+IntervalTreeNode.prototype={appendIntersectionsInto_:function(ret,queryLow,queryHigh){if(this.lowValue_>=queryHigh){if(!this.leftNode_)return;return this.leftNode_.appendIntersectionsInto_(ret,queryLow,queryHigh);}
 if(this.maxHighLeft_>queryLow){this.leftNode_.appendIntersectionsInto_(ret,queryLow,queryHigh);}
-if(this.highValue>queryLow){for(var i=(this.data.length-1);i>=0;--i){if(this.data[i].high<queryLow)
-break;ret.push(this.data[i].datum);}}
-if(this.rightNode_){this.rightNode_.appendIntersectionsInto_(ret,queryLow,queryHigh);}},get colour(){return this.colour_;},set colour(colour){this.colour_=colour;},get key(){return this.lowValue_;},get lowValue(){return this.lowValue_;},get highValue(){return this.data_[this.data_.length-1].high;},set leftNode(left){this.leftNode_=left;},get leftNode(){return this.leftNode_;},get hasLeftNode(){return this.leftNode_!==undefined;},set rightNode(right){this.rightNode_=right;},get rightNode(){return this.rightNode_;},get hasRightNode(){return this.rightNode_!==undefined;},set parentNode(parent){this.parentNode_=parent;},get parentNode(){return this.parentNode_;},get isRootNode(){return this.parentNode_===undefined;},set maxHighLeft(high){this.maxHighLeft_=high;},get maxHighLeft(){return this.maxHighLeft_;},set maxHighRight(high){this.maxHighRight_=high;},get maxHighRight(){return this.maxHighRight_;},get data(){return this.data_;},get isRed(){return this.colour_===Colour.RED;},merge:function(node){for(var i=0;i<node.data.length;i++)
-this.data_.push(node.data[i]);this.data_.sort(function(a,b){return a.high-b.high;});},dump:function(){var ret={};if(this.leftNode_)
-ret['left']=this.leftNode_.dump();ret['data']=this.data_.map(function(d){return[d.low,d.high];});if(this.rightNode_)
-ret['right']=this.rightNode_.dump();return ret;}};return{IntervalTree:IntervalTree};});'use strict';tr.exportTo('tr.b',function(){var tmpVec2s=[];for(var i=0;i<8;i++)
-tmpVec2s[i]=vec2.create();var tmpVec2a=vec4.create();var tmpVec4a=vec4.create();var tmpVec4b=vec4.create();var tmpMat4=mat4.create();var tmpMat4b=mat4.create();var p00=vec2.createXY(0,0);var p10=vec2.createXY(1,0);var p01=vec2.createXY(0,1);var p11=vec2.createXY(1,1);var lerpingVecA=vec2.create();var lerpingVecB=vec2.create();function lerpVec2(out,a,b,amt){vec2.scale(lerpingVecA,a,amt);vec2.scale(lerpingVecB,b,1-amt);vec2.add(out,lerpingVecA,lerpingVecB);vec2.normalize(out,out);return out;}
+if(this.highValue>queryLow){for(var i=(this.data.length-1);i>=0;--i){if(this.data[i].high<queryLow)break;ret.push(this.data[i].datum);}}
+if(this.rightNode_){this.rightNode_.appendIntersectionsInto_(ret,queryLow,queryHigh);}},get colour(){return this.colour_;},set colour(colour){this.colour_=colour;},get key(){return this.lowValue_;},get lowValue(){return this.lowValue_;},get highValue(){return this.data_[this.data_.length-1].high;},set leftNode(left){this.leftNode_=left;},get leftNode(){return this.leftNode_;},get hasLeftNode(){return this.leftNode_!==undefined;},set rightNode(right){this.rightNode_=right;},get rightNode(){return this.rightNode_;},get hasRightNode(){return this.rightNode_!==undefined;},set parentNode(parent){this.parentNode_=parent;},get parentNode(){return this.parentNode_;},get isRootNode(){return this.parentNode_===undefined;},set maxHighLeft(high){this.maxHighLeft_=high;},get maxHighLeft(){return this.maxHighLeft_;},set maxHighRight(high){this.maxHighRight_=high;},get maxHighRight(){return this.maxHighRight_;},get data(){return this.data_;},get isRed(){return this.colour_===Colour.RED;},merge:function(node){for(var i=0;i<node.data.length;i++){this.data_.push(node.data[i]);}
+this.data_.sort(function(a,b){return a.high-b.high;});},dump:function(){var ret={};if(this.leftNode_){ret['left']=this.leftNode_.dump();}
+ret['data']=this.data_.map(function(d){return[d.low,d.high];});if(this.rightNode_){ret['right']=this.rightNode_.dump();}
+return ret;}};return{IntervalTree,};});'use strict';tr.exportTo('tr.b.math',function(){var tmpVec2s=[];for(var i=0;i<8;i++){tmpVec2s[i]=vec2.create();}
+var tmpVec2a=vec4.create();var tmpVec4a=vec4.create();var tmpVec4b=vec4.create();var tmpMat4=mat4.create();var tmpMat4b=mat4.create();var p00=vec2.createXY(0,0);var p10=vec2.createXY(1,0);var p01=vec2.createXY(0,1);var p11=vec2.createXY(1,1);var lerpingVecA=vec2.create();var lerpingVecB=vec2.create();function lerpVec2(out,a,b,amt){vec2.scale(lerpingVecA,a,amt);vec2.scale(lerpingVecB,b,1-amt);vec2.add(out,lerpingVecA,lerpingVecB);vec2.normalize(out,out);return out;}
 function Quad(){this.p1=vec2.create();this.p2=vec2.create();this.p3=vec2.create();this.p4=vec2.create();}
-Quad.fromXYWH=function(x,y,w,h){var q=new Quad();vec2.set(q.p1,x,y);vec2.set(q.p2,x+w,y);vec2.set(q.p3,x+w,y+h);vec2.set(q.p4,x,y+h);return q;}
-Quad.fromRect=function(r){return new Quad.fromXYWH(r.x,r.y,r.width,r.height);}
-Quad.from4Vecs=function(p1,p2,p3,p4){var q=new Quad();vec2.set(q.p1,p1[0],p1[1]);vec2.set(q.p2,p2[0],p2[1]);vec2.set(q.p3,p3[0],p3[1]);vec2.set(q.p4,p4[0],p4[1]);return q;}
-Quad.from8Array=function(arr){if(arr.length!=8)
-throw new Error('Array must be 8 long');var q=new Quad();q.p1[0]=arr[0];q.p1[1]=arr[1];q.p2[0]=arr[2];q.p2[1]=arr[3];q.p3[0]=arr[4];q.p3[1]=arr[5];q.p4[0]=arr[6];q.p4[1]=arr[7];return q;};Quad.prototype={pointInside:function(point){return pointInImplicitQuad(point,this.p1,this.p2,this.p3,this.p4);},boundingRect:function(){var x0=Math.min(this.p1[0],this.p2[0],this.p3[0],this.p4[0]);var y0=Math.min(this.p1[1],this.p2[1],this.p3[1],this.p4[1]);var x1=Math.max(this.p1[0],this.p2[0],this.p3[0],this.p4[0]);var y1=Math.max(this.p1[1],this.p2[1],this.p3[1],this.p4[1]);return new tr.b.Rect.fromXYWH(x0,y0,x1-x0,y1-y0);},clone:function(){var q=new Quad();vec2.copy(q.p1,this.p1);vec2.copy(q.p2,this.p2);vec2.copy(q.p3,this.p3);vec2.copy(q.p4,this.p4);return q;},scale:function(s){var q=new Quad();this.scaleFast(q,s);return q;},scaleFast:function(dstQuad,s){vec2.copy(dstQuad.p1,this.p1,s);vec2.copy(dstQuad.p2,this.p2,s);vec2.copy(dstQuad.p3,this.p3,s);vec2.copy(dstQuad.p3,this.p3,s);},isRectangle:function(){var bounds=this.boundingRect();return(bounds.x==this.p1[0]&&bounds.y==this.p1[1]&&bounds.width==this.p2[0]-this.p1[0]&&bounds.y==this.p2[1]&&bounds.width==this.p3[0]-this.p1[0]&&bounds.height==this.p3[1]-this.p2[1]&&bounds.x==this.p4[0]&&bounds.height==this.p4[1]-this.p2[1]);},projectUnitRect:function(rect){var q=new Quad();this.projectUnitRectFast(q,rect);return q;},projectUnitRectFast:function(dstQuad,rect){var v12=tmpVec2s[0];var v14=tmpVec2s[1];var v23=tmpVec2s[2];var v43=tmpVec2s[3];var l12,l14,l23,l43;vec2.sub(v12,this.p2,this.p1);l12=vec2.length(v12);vec2.scale(v12,v12,1/l12);vec2.sub(v14,this.p4,this.p1);l14=vec2.length(v14);vec2.scale(v14,v14,1/l14);vec2.sub(v23,this.p3,this.p2);l23=vec2.length(v23);vec2.scale(v23,v23,1/l23);vec2.sub(v43,this.p3,this.p4);l43=vec2.length(v43);vec2.scale(v43,v43,1/l43);var b12=tmpVec2s[0];var b14=tmpVec2s[1];var b23=tmpVec2s[2];var b43=tmpVec2s[3];lerpVec2(b12,v12,v43,rect.y);lerpVec2(b43,v12,v43,1-rect.bottom);lerpVec2(b14,v14,v23,rect.x);lerpVec2(b23,v14,v23,1-rect.right);vec2.addTwoScaledUnitVectors(tmpVec2a,b12,l12*rect.x,b14,l14*rect.y);vec2.add(dstQuad.p1,this.p1,tmpVec2a);vec2.addTwoScaledUnitVectors(tmpVec2a,b12,l12*-(1.0-rect.right),b23,l23*rect.y);vec2.add(dstQuad.p2,this.p2,tmpVec2a);vec2.addTwoScaledUnitVectors(tmpVec2a,b43,l43*-(1.0-rect.right),b23,l23*-(1.0-rect.bottom));vec2.add(dstQuad.p3,this.p3,tmpVec2a);vec2.addTwoScaledUnitVectors(tmpVec2a,b43,l43*rect.left,b14,l14*-(1.0-rect.bottom));vec2.add(dstQuad.p4,this.p4,tmpVec2a);},toString:function(){return'Quad('+
+Quad.fromXYWH=function(x,y,w,h){var q=new Quad();vec2.set(q.p1,x,y);vec2.set(q.p2,x+w,y);vec2.set(q.p3,x+w,y+h);vec2.set(q.p4,x,y+h);return q;};Quad.fromRect=function(r){return new Quad.fromXYWH(r.x,r.y,r.width,r.height);};Quad.from4Vecs=function(p1,p2,p3,p4){var q=new Quad();vec2.set(q.p1,p1[0],p1[1]);vec2.set(q.p2,p2[0],p2[1]);vec2.set(q.p3,p3[0],p3[1]);vec2.set(q.p4,p4[0],p4[1]);return q;};Quad.from8Array=function(arr){if(arr.length!==8){throw new Error('Array must be 8 long');}
+var q=new Quad();q.p1[0]=arr[0];q.p1[1]=arr[1];q.p2[0]=arr[2];q.p2[1]=arr[3];q.p3[0]=arr[4];q.p3[1]=arr[5];q.p4[0]=arr[6];q.p4[1]=arr[7];return q;};Quad.prototype={pointInside:function(point){return pointInImplicitQuad(point,this.p1,this.p2,this.p3,this.p4);},boundingRect:function(){var x0=Math.min(this.p1[0],this.p2[0],this.p3[0],this.p4[0]);var y0=Math.min(this.p1[1],this.p2[1],this.p3[1],this.p4[1]);var x1=Math.max(this.p1[0],this.p2[0],this.p3[0],this.p4[0]);var y1=Math.max(this.p1[1],this.p2[1],this.p3[1],this.p4[1]);return new tr.b.math.Rect.fromXYWH(x0,y0,x1-x0,y1-y0);},clone:function(){var q=new Quad();vec2.copy(q.p1,this.p1);vec2.copy(q.p2,this.p2);vec2.copy(q.p3,this.p3);vec2.copy(q.p4,this.p4);return q;},scale:function(s){var q=new Quad();this.scaleFast(q,s);return q;},scaleFast:function(dstQuad,s){vec2.copy(dstQuad.p1,this.p1,s);vec2.copy(dstQuad.p2,this.p2,s);vec2.copy(dstQuad.p3,this.p3,s);vec2.copy(dstQuad.p3,this.p3,s);},isRectangle:function(){var bounds=this.boundingRect();return(bounds.x===this.p1[0]&&bounds.y===this.p1[1]&&bounds.width===this.p2[0]-this.p1[0]&&bounds.y===this.p2[1]&&bounds.width===this.p3[0]-this.p1[0]&&bounds.height===this.p3[1]-this.p2[1]&&bounds.x===this.p4[0]&&bounds.height===this.p4[1]-this.p2[1]);},projectUnitRect:function(rect){var q=new Quad();this.projectUnitRectFast(q,rect);return q;},projectUnitRectFast:function(dstQuad,rect){var v12=tmpVec2s[0];var v14=tmpVec2s[1];var v23=tmpVec2s[2];var v43=tmpVec2s[3];var l12;var l14;var l23;var l43;vec2.sub(v12,this.p2,this.p1);l12=vec2.length(v12);vec2.scale(v12,v12,1/l12);vec2.sub(v14,this.p4,this.p1);l14=vec2.length(v14);vec2.scale(v14,v14,1/l14);vec2.sub(v23,this.p3,this.p2);l23=vec2.length(v23);vec2.scale(v23,v23,1/l23);vec2.sub(v43,this.p3,this.p4);l43=vec2.length(v43);vec2.scale(v43,v43,1/l43);var b12=tmpVec2s[0];var b14=tmpVec2s[1];var b23=tmpVec2s[2];var b43=tmpVec2s[3];lerpVec2(b12,v12,v43,rect.y);lerpVec2(b43,v12,v43,1-rect.bottom);lerpVec2(b14,v14,v23,rect.x);lerpVec2(b23,v14,v23,1-rect.right);vec2.addTwoScaledUnitVectors(tmpVec2a,b12,l12*rect.x,b14,l14*rect.y);vec2.add(dstQuad.p1,this.p1,tmpVec2a);vec2.addTwoScaledUnitVectors(tmpVec2a,b12,l12*-(1.0-rect.right),b23,l23*rect.y);vec2.add(dstQuad.p2,this.p2,tmpVec2a);vec2.addTwoScaledUnitVectors(tmpVec2a,b43,l43*-(1.0-rect.right),b23,l23*-(1.0-rect.bottom));vec2.add(dstQuad.p3,this.p3,tmpVec2a);vec2.addTwoScaledUnitVectors(tmpVec2a,b43,l43*rect.left,b14,l14*-(1.0-rect.bottom));vec2.add(dstQuad.p4,this.p4,tmpVec2a);},toString:function(){return'Quad('+
 vec2.toString(this.p1)+', '+
 vec2.toString(this.p2)+', '+
 vec2.toString(this.p3)+', '+
 vec2.toString(this.p4)+')';}};function sign(p1,p2,p3){return(p1[0]-p3[0])*(p2[1]-p3[1])-
 (p2[0]-p3[0])*(p1[1]-p3[1]);}
-function pointInTriangle2(pt,p1,p2,p3){var b1=sign(pt,p1,p2)<0.0;var b2=sign(pt,p2,p3)<0.0;var b3=sign(pt,p3,p1)<0.0;return((b1==b2)&&(b2==b3));}
+function pointInTriangle2(pt,p1,p2,p3){var b1=sign(pt,p1,p2)<0.0;var b2=sign(pt,p2,p3)<0.0;var b3=sign(pt,p3,p1)<0.0;return((b1===b2)&&(b2===b3));}
 function pointInImplicitQuad(point,p1,p2,p3,p4){return pointInTriangle2(point,p1,p2,p3)||pointInTriangle2(point,p1,p3,p4);}
-return{pointInTriangle2:pointInTriangle2,pointInImplicitQuad:pointInImplicitQuad,Quad:Quad};});'use strict';tr.exportTo('tr.b',function(){var ESTIMATED_IDLE_PERIOD_LENGTH_MILLISECONDS=10;var REQUEST_IDLE_CALLBACK_TIMEOUT_MILLISECONDS=100;var recordRAFStacks=false;var pendingPreAFs=[];var pendingRAFs=[];var pendingIdleCallbacks=[];var currentRAFDispatchList=undefined;var rafScheduled=false;var idleWorkScheduled=false;function scheduleRAF(){if(rafScheduled)
-return;rafScheduled=true;if(tr.isHeadless){Promise.resolve().then(function(){processRequests(false,0);},function(e){console.log(e.stack);throw e;});}else{if(window.requestAnimationFrame){window.requestAnimationFrame(processRequests.bind(this,false));}else{var delta=Date.now()-window.performance.now();window.webkitRequestAnimationFrame(function(domTimeStamp){processRequests(false,domTimeStamp-delta);});}}}
+return{pointInTriangle2,pointInImplicitQuad,Quad,};});'use strict';tr.exportTo('tr.b',function(){function addSingletonGetter(ctor){ctor.getInstance=function(){return ctor.instance_||(ctor.instance_=new ctor());};}
+function deepCopy(value){if(!(value instanceof Object)){if(value===undefined||value===null)return value;if(typeof value==='string')return value.substring();if(typeof value==='boolean')return value;if(typeof value==='number')return value;throw new Error('Unrecognized: '+typeof value);}
+var object=value;if(object instanceof Array){var res=new Array(object.length);for(var i=0;i<object.length;i++){res[i]=deepCopy(object[i]);}
+return res;}
+if(object.__proto__!==Object.prototype){throw new Error('Can only clone simple types');}
+var res={};for(var key in object){res[key]=deepCopy(object[key]);}
+return res;}
+function normalizeException(e){if(e===undefined||e===null){return{typeName:'UndefinedError',message:'Unknown: null or undefined exception',stack:'Unknown'};}
+if(typeof(e)==='string'){return{typeName:'StringError',message:e,stack:[e]};}
+var typeName;if(e.name){typeName=e.name;}else if(e.constructor){if(e.constructor.name){typeName=e.constructor.name;}else{typeName='AnonymousError';}}else{typeName='ErrorWithNoConstructor';}
+var msg=e.message?e.message:'Unknown';return{typeName:typeName,message:msg,stack:e.stack?e.stack:[msg]};}
+function stackTraceAsString(){return new Error().stack+'';}
+function stackTrace(){var stack=stackTraceAsString();stack=stack.split('\n');return stack.slice(2);}
+function getUsingPath(path,fromDict){var parts=path.split('.');var cur=fromDict;for(var part;parts.length&&(part=parts.shift());){if(!parts.length){return cur[part];}else if(part in cur){cur=cur[part];}else{return undefined;}}
+return undefined;}
+function formatDate(date){return date.toISOString().replace('T',' ').slice(0,19);}
+function numberToJson(n){if(isNaN(n))return'NaN';if(n===Infinity)return'Infinity';if(n===-Infinity)return'-Infinity';return n;}
+function numberFromJson(n){if(n==='NaN'||n===null)return NaN;if(n==='Infinity')return Infinity;if(n==='-Infinity')return-Infinity;return n;}
+function runLengthEncoding(ary){let encodedArray=[];for(let element of ary){if(encodedArray.length===0||encodedArray[encodedArray.length-1].value!==element){encodedArray.push({value:element,count:1,});}else{encodedArray[encodedArray.length-1].count+=1;}}
+return encodedArray;}
+return{addSingletonGetter,deepCopy,normalizeException,stackTrace,stackTraceAsString,formatDate,numberToJson,numberFromJson,getUsingPath,runLengthEncoding,};});'use strict';tr.exportTo('tr.b',function(){var ESTIMATED_IDLE_PERIOD_LENGTH_MILLISECONDS=10;var REQUEST_IDLE_CALLBACK_TIMEOUT_MILLISECONDS=100;var recordRAFStacks=false;var pendingPreAFs=[];var pendingRAFs=[];var pendingIdleCallbacks=[];var currentRAFDispatchList=undefined;var rafScheduled=false;var idleWorkScheduled=false;function scheduleRAF(){if(rafScheduled)return;rafScheduled=true;if(tr.isHeadless){Promise.resolve().then(function(){processRequests(false,0);},function(e){throw e;});}else{if(window.requestAnimationFrame){window.requestAnimationFrame(processRequests.bind(this,false));}else{var delta=Date.now()-window.performance.now();window.webkitRequestAnimationFrame(function(domTimeStamp){processRequests(false,domTimeStamp-delta);});}}}
 function nativeRequestIdleCallbackSupported(){return!tr.isHeadless&&window.requestIdleCallback;}
-function scheduleIdleWork(){if(idleWorkScheduled)
-return;if(!nativeRequestIdleCallbackSupported()){scheduleRAF();return;}
+function scheduleIdleWork(){if(idleWorkScheduled)return;if(!nativeRequestIdleCallbackSupported()){scheduleRAF();return;}
 idleWorkScheduled=true;window.requestIdleCallback(function(deadline,didTimeout){processIdleWork(false,deadline);},{timeout:REQUEST_IDLE_CALLBACK_TIMEOUT_MILLISECONDS});}
-function onAnimationFrameError(e,opt_stack){console.log(e.stack);if(tr.isHeadless)
-throw e;if(opt_stack)
-console.log(opt_stack);if(e.message)
-console.error(e.message,e.stack);else
-console.error(e);}
+function onAnimationFrameError(e,opt_stack){console.log(e.stack);if(tr.isHeadless)throw e;if(opt_stack)console.log(opt_stack);if(e.message){console.error(e.message,e.stack);}else{console.error(e);}}
 function runTask(task,frameBeginTime){try{task.callback.call(task.context,frameBeginTime);}catch(e){tr.b.onAnimationFrameError(e,task.stack);}}
-function processRequests(forceAllTasksToRun,frameBeginTime){rafScheduled=false;var currentPreAFs=pendingPreAFs;currentRAFDispatchList=pendingRAFs;pendingPreAFs=[];pendingRAFs=[];var hasRAFTasks=currentPreAFs.length||currentRAFDispatchList.length;for(var i=0;i<currentPreAFs.length;i++)
-runTask(currentPreAFs[i],frameBeginTime);while(currentRAFDispatchList.length>0)
-runTask(currentRAFDispatchList.shift(),frameBeginTime);currentRAFDispatchList=undefined;if((!hasRAFTasks&&!nativeRequestIdleCallbackSupported())||forceAllTasksToRun){var rafCompletionDeadline=frameBeginTime+ESTIMATED_IDLE_PERIOD_LENGTH_MILLISECONDS;processIdleWork(forceAllTasksToRun,{timeRemaining:function(){return rafCompletionDeadline-window.performance.now();}});}
-if(pendingIdleCallbacks.length>0)
-scheduleIdleWork();}
+function processRequests(forceAllTasksToRun,frameBeginTime){rafScheduled=false;var currentPreAFs=pendingPreAFs;currentRAFDispatchList=pendingRAFs;pendingPreAFs=[];pendingRAFs=[];var hasRAFTasks=currentPreAFs.length||currentRAFDispatchList.length;for(var i=0;i<currentPreAFs.length;i++){runTask(currentPreAFs[i],frameBeginTime);}
+while(currentRAFDispatchList.length>0){runTask(currentRAFDispatchList.shift(),frameBeginTime);}
+currentRAFDispatchList=undefined;if((!hasRAFTasks&&!nativeRequestIdleCallbackSupported())||forceAllTasksToRun){var rafCompletionDeadline=frameBeginTime+ESTIMATED_IDLE_PERIOD_LENGTH_MILLISECONDS;processIdleWork(forceAllTasksToRun,{timeRemaining:function(){return rafCompletionDeadline-window.performance.now();}});}
+if(pendingIdleCallbacks.length>0)scheduleIdleWork();}
 function processIdleWork(forceAllTasksToRun,deadline){idleWorkScheduled=false;while(pendingIdleCallbacks.length>0){runTask(pendingIdleCallbacks.shift());if(!forceAllTasksToRun&&(tr.isHeadless||deadline.timeRemaining()<=0)){break;}}
-if(pendingIdleCallbacks.length>0)
-scheduleIdleWork();}
-function getStack_(){if(!recordRAFStacks)
-return'';var stackLines=tr.b.stackTrace();stackLines.shift();return stackLines.join('\n');}
+if(pendingIdleCallbacks.length>0)scheduleIdleWork();}
+function getStack_(){if(!recordRAFStacks)return'';var stackLines=tr.b.stackTrace();stackLines.shift();return stackLines.join('\n');}
 function requestPreAnimationFrame(callback,opt_this){pendingPreAFs.push({callback:callback,context:opt_this||global,stack:getStack_()});scheduleRAF();}
 function requestAnimationFrameInThisFrameIfPossible(callback,opt_this){if(!currentRAFDispatchList){requestAnimationFrame(callback,opt_this);return;}
 currentRAFDispatchList.push({callback:callback,context:opt_this||global,stack:getStack_()});return;}
 function requestAnimationFrame(callback,opt_this){pendingRAFs.push({callback:callback,context:opt_this||global,stack:getStack_()});scheduleRAF();}
+function animationFrame(){return new Promise(resolve=>requestAnimationFrame(resolve));}
 function requestIdleCallback(callback,opt_this){pendingIdleCallbacks.push({callback:callback,context:opt_this||global,stack:getStack_()});scheduleIdleWork();}
-function forcePendingRAFTasksToRun(frameBeginTime){if(!rafScheduled)
-return;processRequests(false,frameBeginTime);}
-function forceAllPendingTasksToRunForTest(){if(!rafScheduled&&!idleWorkScheduled)
-return;processRequests(true,0);}
-return{onAnimationFrameError:onAnimationFrameError,requestPreAnimationFrame:requestPreAnimationFrame,requestAnimationFrame:requestAnimationFrame,requestAnimationFrameInThisFrameIfPossible:requestAnimationFrameInThisFrameIfPossible,requestIdleCallback:requestIdleCallback,forcePendingRAFTasksToRun:forcePendingRAFTasksToRun,forceAllPendingTasksToRunForTest:forceAllPendingTasksToRunForTest};});'use strict';tr.exportTo('tr.b',function(){var Base64=tr.b.Base64;function computeUserTimingMarkName(groupName,functionName,opt_args){if(groupName===undefined)
-throw new Error('getMeasureString should have group name');if(functionName===undefined)
-throw new Error('getMeasureString should have function name');var userTimingMarkName=groupName+':'+functionName;if(opt_args!==undefined){userTimingMarkName+='/';userTimingMarkName+=Base64.btoa(JSON.stringify(opt_args));}
+function forcePendingRAFTasksToRun(frameBeginTime){if(!rafScheduled)return;processRequests(false,frameBeginTime);}
+function forceAllPendingTasksToRunForTest(){if(!rafScheduled&&!idleWorkScheduled)return;processRequests(true,0);}
+function timeout(ms){return new Promise(resolve=>window.setTimeout(resolve,ms));}
+function idle(){return new Promise(resolve=>requestIdleCallback(resolve));}
+return{animationFrame,forceAllPendingTasksToRunForTest,forcePendingRAFTasksToRun,idle,onAnimationFrameError,requestAnimationFrame,requestAnimationFrameInThisFrameIfPossible,requestIdleCallback,requestPreAnimationFrame,timeout,};});'use strict';tr.exportTo('tr.b',function(){var Base64=tr.b.Base64;function computeUserTimingMarkName(groupName,functionName,opt_args){if(groupName===undefined){throw new Error('getMeasureString should have group name');}
+if(functionName===undefined){throw new Error('getMeasureString should have function name');}
+var userTimingMarkName=groupName+':'+functionName;if(opt_args!==undefined){userTimingMarkName+='/';userTimingMarkName+=Base64.btoa(JSON.stringify(opt_args));}
 return userTimingMarkName;}
 function Timing(){}
 Timing.nextMarkNumber=0;Timing.mark=function(groupName,functionName,opt_args){if(tr.isHeadless){return{end:function(){}};}
-var userTimingMarkName=computeUserTimingMarkName(groupName,functionName,opt_args);var markBeginName='tvcm.mark'+Timing.nextMarkNumber++;var markEndName='tvcm.mark'+Timing.nextMarkNumber++;window.performance.mark(markBeginName);return{end:function(){window.performance.mark(markEndName);window.performance.measure(userTimingMarkName,markBeginName,markEndName);}};};Timing.wrap=function(groupName,callback,opt_args){if(groupName===undefined)
-throw new Error('Timing.wrap should have group name');if(callback.name==='')
-throw new Error('Anonymous function is not allowed');return Timing.wrapNamedFunction(groupName,callback.name,callback,opt_args);};Timing.wrapNamedFunction=function(groupName,functionName,callback,opt_args){function timedNamedFunction(){var markedTime=Timing.mark(groupName,functionName,opt_args);try{callback.apply(this,arguments);}finally{markedTime.end();}}
+var userTimingMarkName=computeUserTimingMarkName(groupName,functionName,opt_args);var markBeginName='tvcm.mark'+Timing.nextMarkNumber++;var markEndName='tvcm.mark'+Timing.nextMarkNumber++;window.performance.mark(markBeginName);return{end:function(){window.performance.mark(markEndName);window.performance.measure(userTimingMarkName,markBeginName,markEndName);}};};Timing.wrap=function(groupName,callback,opt_args){if(groupName===undefined){throw new Error('Timing.wrap should have group name');}
+if(callback.name===''){throw new Error('Anonymous function is not allowed');}
+return Timing.wrapNamedFunction(groupName,callback.name,callback,opt_args);};Timing.wrapNamedFunction=function(groupName,functionName,callback,opt_args){function timedNamedFunction(){var markedTime=Timing.mark(groupName,functionName,opt_args);try{callback.apply(this,arguments);}finally{markedTime.end();}}
 return timedNamedFunction;};function TimedNamedPromise(groupName,name,executor,opt_args){var markedTime=Timing.mark(groupName,name,opt_args);var promise=new Promise(executor);promise.then(function(result){markedTime.end();return result;},function(e){markedTime.end();throw e;});return promise;}
-return{_computeUserTimingMarkName:computeUserTimingMarkName,TimedNamedPromise:TimedNamedPromise,Timing:Timing};});'use strict';tr.exportTo('tr.b',function(){var Timing=tr.b.Timing;function Task(runCb,thisArg){if(runCb!==undefined&&thisArg===undefined)
-throw new Error('Almost certainly, you meant to pass a thisArg.');this.runCb_=runCb;this.thisArg_=thisArg;this.afterTask_=undefined;this.subTasks_=[];}
-Task.prototype={get name(){return this.runCb_.name;},subTask:function(cb,thisArg){if(cb instanceof Task)
-this.subTasks_.push(cb);else
-this.subTasks_.push(new Task(cb,thisArg));return this.subTasks_[this.subTasks_.length-1];},run:function(){if(this.runCb_!==undefined)
-this.runCb_.call(this.thisArg_,this);var subTasks=this.subTasks_;this.subTasks_=undefined;if(!subTasks.length)
-return this.afterTask_;for(var i=1;i<subTasks.length;i++)
-subTasks[i-1].afterTask_=subTasks[i];subTasks[subTasks.length-1].afterTask_=this.afterTask_;return subTasks[0];},after:function(cb,thisArg){if(this.afterTask_)
-throw new Error('Has an after task already');if(cb instanceof Task)
-this.afterTask_=cb;else
-this.afterTask_=new Task(cb,thisArg);return this.afterTask_;},timedAfter:function(groupName,cb,thisArg,opt_args){if(cb.name==='')
-throw new Error('Anonymous Task is not allowed');return this.namedTimedAfter(groupName,cb.name,cb,thisArg,opt_args);},namedTimedAfter:function(groupName,name,cb,thisArg,opt_args){if(this.afterTask_)
-throw new Error('Has an after task already');var realTask;if(cb instanceof Task)
-realTask=cb;else
-realTask=new Task(cb,thisArg);this.afterTask_=new Task(function(task){var markedTask=Timing.mark(groupName,name,opt_args);task.subTask(realTask,thisArg);task.subTask(function(){markedTask.end();},thisArg);},thisArg);return this.afterTask_;},enqueue:function(cb,thisArg){var lastTask=this;while(lastTask.afterTask_)
-lastTask=lastTask.afterTask_;return lastTask.after(cb,thisArg);}};Task.RunSynchronously=function(task){var curTask=task;while(curTask)
-curTask=curTask.run();}
-Task.RunWhenIdle=function(task){return new Promise(function(resolve,reject){var curTask=task;function runAnother(){try{curTask=curTask.run();}catch(e){reject(e);console.error(e.stack);return;}
-if(curTask){tr.b.requestIdleCallback(runAnother);return;}
+return{_computeUserTimingMarkName:computeUserTimingMarkName,TimedNamedPromise,Timing,};});'use strict';tr.exportTo('tr.b',function(){var Timing=tr.b.Timing;function Task(runCb,thisArg){if(runCb!==undefined&&thisArg===undefined&&runCb.prototype!==undefined){throw new Error('Almost certainly you meant to pass a bound callback '+'or thisArg.');}
+this.runCb_=runCb;this.thisArg_=thisArg;this.afterTask_=undefined;this.subTasks_=[];this.updatesUi_=false;}
+Task.prototype={get name(){return this.runCb_.name;},set updatesUi(value){this.updatesUi_=value;},subTask:function(cb,thisArg){if(cb instanceof Task){this.subTasks_.push(cb);}else{this.subTasks_.push(new Task(cb,thisArg));}
+return this.subTasks_[this.subTasks_.length-1];},run:function(){if(this.runCb_!==undefined)this.runCb_.call(this.thisArg_,this);var subTasks=this.subTasks_;this.subTasks_=undefined;if(!subTasks.length)return this.afterTask_;for(var i=1;i<subTasks.length;i++){subTasks[i-1].afterTask_=subTasks[i];}
+subTasks[subTasks.length-1].afterTask_=this.afterTask_;return subTasks[0];},after:function(cb,thisArg){if(this.afterTask_){throw new Error('Has an after task already');}
+if(cb instanceof Task){this.afterTask_=cb;}else{this.afterTask_=new Task(cb,thisArg);}
+return this.afterTask_;},timedAfter:function(groupName,cb,thisArg,opt_args){if(cb.name===''){throw new Error('Anonymous Task is not allowed');}
+return this.namedTimedAfter(groupName,cb.name,cb,thisArg,opt_args);},namedTimedAfter:function(groupName,name,cb,thisArg,opt_args){if(this.afterTask_){throw new Error('Has an after task already');}
+var realTask;if(cb instanceof Task){realTask=cb;}else{realTask=new Task(cb,thisArg);}
+this.afterTask_=new Task(function(task){var markedTask=Timing.mark(groupName,name,opt_args);task.subTask(realTask,thisArg);task.subTask(function(){markedTask.end();},thisArg);},thisArg);return this.afterTask_;},enqueue:function(cb,thisArg){if(!this.afterTask_)return this.after(cb,thisArg);return this.afterTask_.enqueue(cb,thisArg);}};Task.RunSynchronously=function(task){var curTask=task;while(curTask){curTask=curTask.run();}};Task.RunWhenIdle=function(task){return new Promise(function(resolve,reject){var curTask=task;function runAnother(){try{curTask=curTask.run();}catch(e){reject(e);return;}
+if(curTask){if(curTask.updatesUi_){tr.b.requestAnimationFrameInThisFrameIfPossible(runAnother);}else{tr.b.requestIdleCallback(runAnother);}
+return;}
 resolve();}
-tr.b.requestIdleCallback(runAnother);});}
-return{Task:Task};});'use strict';tr.exportTo('tr.c',function(){function makeCaseInsensitiveRegex(pattern){pattern=pattern.replace(/[.*+?^${}()|[\]\\]/g,'\\$&');return new RegExp(pattern,'i');}
+tr.b.requestIdleCallback(runAnother);});};return{Task,};});'use strict';tr.exportTo('tr.c',function(){function makeCaseInsensitiveRegex(pattern){pattern=pattern.replace(/[.*+?^${}()|[\]\\]/g,'\\$&');return new RegExp(pattern,'i');}
 function Filter(){}
-Filter.prototype={__proto__:Object.prototype,matchCounter:function(counter){return true;},matchCpu:function(cpu){return true;},matchProcess:function(process){return true;},matchSlice:function(slice){return true;},matchThread:function(thread){return true;}};function TitleOrCategoryFilter(text){Filter.call(this);this.regex_=makeCaseInsensitiveRegex(text);if(!text.length)
-throw new Error('Filter text is empty.');}
-TitleOrCategoryFilter.prototype={__proto__:Filter.prototype,matchSlice:function(slice){if(slice.title===undefined&&slice.category===undefined)
-return false;return this.regex_.test(slice.title)||(!!slice.category&&this.regex_.test(slice.category));}};function ExactTitleFilter(text){Filter.call(this);this.text_=text;if(!text.length)
-throw new Error('Filter text is empty.');}
+Filter.prototype={__proto__:Object.prototype,matchCounter:function(counter){return true;},matchCpu:function(cpu){return true;},matchProcess:function(process){return true;},matchSlice:function(slice){return true;},matchThread:function(thread){return true;}};function TitleOrCategoryFilter(text){Filter.call(this);this.regex_=makeCaseInsensitiveRegex(text);if(!text.length){throw new Error('Filter text is empty.');}}
+TitleOrCategoryFilter.prototype={__proto__:Filter.prototype,matchSlice:function(slice){if(slice.title===undefined&&slice.category===undefined){return false;}
+return this.regex_.test(slice.title)||(!!slice.category&&this.regex_.test(slice.category));}};function ExactTitleFilter(text){Filter.call(this);this.text_=text;if(!text.length){throw new Error('Filter text is empty.');}}
 ExactTitleFilter.prototype={__proto__:Filter.prototype,matchSlice:function(slice){return slice.title===this.text_;}};function FullTextFilter(text){Filter.call(this);this.regex_=makeCaseInsensitiveRegex(text);this.titleOrCategoryFilter_=new TitleOrCategoryFilter(text);}
-FullTextFilter.prototype={__proto__:Filter.prototype,matchObject_:function(obj){for(var key in obj){if(!obj.hasOwnProperty(key))
-continue;if(this.regex_.test(key))
-return true;if(this.regex_.test(obj[key]))
-return true;}
-return false;},matchSlice:function(slice){if(this.titleOrCategoryFilter_.matchSlice(slice))
-return true;return this.matchObject_(slice.args);}};return{Filter:Filter,TitleOrCategoryFilter:TitleOrCategoryFilter,ExactTitleFilter:ExactTitleFilter,FullTextFilter:FullTextFilter};});'use strict';tr.exportTo('tr.model',function(){var ClockDomainId={BATTOR:'BATTOR',UNKNOWN_CHROME_LEGACY:'UNKNOWN_CHROME_LEGACY',LINUX_CLOCK_MONOTONIC:'LINUX_CLOCK_MONOTONIC',LINUX_FTRACE_GLOBAL:'LINUX_FTRACE_GLOBAL',MAC_MACH_ABSOLUTE_TIME:'MAC_MACH_ABSOLUTE_TIME',WIN_ROLLOVER_PROTECTED_TIME_GET_TIME:'WIN_ROLLOVER_PROTECTED_TIME_GET_TIME',WIN_QPC:'WIN_QPC',TELEMETRY:'TELEMETRY'};var POSSIBLE_CHROME_CLOCK_DOMAINS=new Set([ClockDomainId.UNKNOWN_CHROME_LEGACY,ClockDomainId.LINUX_CLOCK_MONOTONIC,ClockDomainId.MAC_MACH_ABSOLUTE_TIME,ClockDomainId.WIN_ROLLOVER_PROTECTED_TIME_GET_TIME,ClockDomainId.WIN_QPC]);var BATTOR_FAST_SYNC_THRESHOLD_MS=3;function ClockSyncManager(){this.domainsSeen_=new Set();this.markersBySyncId_=new Map();this.transformerMapByDomainId_={};}
+FullTextFilter.prototype={__proto__:Filter.prototype,matchObject_:function(obj){for(var key in obj){if(!obj.hasOwnProperty(key))continue;if(this.regex_.test(key))return true;if(this.regex_.test(obj[key]))return true;}
+return false;},matchSlice:function(slice){if(this.titleOrCategoryFilter_.matchSlice(slice))return true;return this.matchObject_(slice.args);}};return{Filter,TitleOrCategoryFilter,ExactTitleFilter,FullTextFilter,};});'use strict';tr.exportTo('tr.model',function(){var ClockDomainId={BATTOR:'BATTOR',UNKNOWN_CHROME_LEGACY:'UNKNOWN_CHROME_LEGACY',LINUX_CLOCK_MONOTONIC:'LINUX_CLOCK_MONOTONIC',LINUX_FTRACE_GLOBAL:'LINUX_FTRACE_GLOBAL',MAC_MACH_ABSOLUTE_TIME:'MAC_MACH_ABSOLUTE_TIME',WIN_ROLLOVER_PROTECTED_TIME_GET_TIME:'WIN_ROLLOVER_PROTECTED_TIME_GET_TIME',WIN_QPC:'WIN_QPC',TELEMETRY:'TELEMETRY'};var POSSIBLE_CHROME_CLOCK_DOMAINS=new Set([ClockDomainId.UNKNOWN_CHROME_LEGACY,ClockDomainId.LINUX_CLOCK_MONOTONIC,ClockDomainId.MAC_MACH_ABSOLUTE_TIME,ClockDomainId.WIN_ROLLOVER_PROTECTED_TIME_GET_TIME,ClockDomainId.WIN_QPC]);var BATTOR_FAST_SYNC_THRESHOLD_MS=3;function ClockSyncManager(){this.domainsSeen_=new Set();this.markersBySyncId_=new Map();this.transformerMapByDomainId_={};}
 ClockSyncManager.prototype={addClockSyncMarker:function(domainId,syncId,startTs,opt_endTs){this.onDomainSeen_(domainId);if(tr.b.dictionaryValues(ClockDomainId).indexOf(domainId)<0){throw new Error('"'+domainId+'" is not in the list of known '+'clock domain IDs.');}
 if(this.modelDomainId_){throw new Error('Cannot add new clock sync markers after getting '+'a model time transformer.');}
 var marker=new ClockSyncMarker(domainId,startTs,opt_endTs);if(!this.markersBySyncId_.has(syncId)){this.markersBySyncId_.set(syncId,[marker]);return;}
 var markers=this.markersBySyncId_.get(syncId);if(markers.length===2){throw new Error('Clock sync with ID "'+syncId+'" is already '+'complete - cannot add a third clock sync marker to it.');}
-if(markers[0].domainId===domainId)
-throw new Error('A clock domain cannot sync with itself.');markers.push(marker);this.onSyncCompleted_(markers[0],marker);},get markersBySyncId(){return this.markersBySyncId_;},get domainsSeen(){return this.domainsSeen_;},getModelTimeTransformer:function(domainId){return this.getModelTimeTransformerRaw_(domainId).fn;},getModelTimeTransformerError:function(domainId){return this.getModelTimeTransformerRaw_(domainId).error;},getModelTimeTransformerRaw_:function(domainId){this.onDomainSeen_(domainId);if(!this.modelDomainId_)
-this.selectModelDomainId_();var transformer=this.getTransformerBetween_(domainId,this.modelDomainId_);if(!transformer){throw new Error('No clock sync markers exist pairing clock domain "'+
-domainId+'" '+'with model clock domain "'+
-this.modelDomainId_+'".');}
-return transformer;},getTransformerBetween_:function(fromDomainId,toDomainId){var visitedDomainIds=new Set();var queue=[{domainId:fromDomainId,transformer:Transformer.IDENTITY}];while(queue.length>0){queue.sort((domain1,domain2)=>domain1.transformer.error-domain2.transformer.error);var current=queue.shift();if(current.domainId===toDomainId)
-return current.transformer;if(visitedDomainIds.has(current.domainId))
-continue;visitedDomainIds.add(current.domainId);var outgoingTransformers=this.transformerMapByDomainId_[current.domainId];if(!outgoingTransformers)
-continue;for(var outgoingDomainId in outgoingTransformers){var toNextDomainTransformer=outgoingTransformers[outgoingDomainId];var toCurrentDomainTransformer=current.transformer;queue.push({domainId:outgoingDomainId,transformer:Transformer.compose(toNextDomainTransformer,toCurrentDomainTransformer)});}}
+if(markers[0].domainId===domainId){throw new Error('A clock domain cannot sync with itself.');}
+markers.push(marker);this.onSyncCompleted_(markers[0],marker);},get markersBySyncId(){return this.markersBySyncId_;},get domainsSeen(){return this.domainsSeen_;},getModelTimeTransformer:function(domainId){this.onDomainSeen_(domainId);if(!this.modelDomainId_){this.selectModelDomainId_();}
+return this.getTimeTransformerRaw_(domainId,this.modelDomainId_).fn;},getTimeTransformerError:function(fromDomainId,toDomainId){this.onDomainSeen_(fromDomainId);this.onDomainSeen_(toDomainId);return this.getTimeTransformerRaw_(fromDomainId,toDomainId).error;},getTimeTransformerRaw_:function(fromDomainId,toDomainId){var transformer=this.getTransformerBetween_(fromDomainId,toDomainId);if(!transformer){throw new Error('No clock sync markers exist pairing clock domain "'+
+fromDomainId+'" '+'with target clock domain "'+
+toDomainId+'".');}
+return transformer;},getTransformerBetween_:function(fromDomainId,toDomainId){var visitedDomainIds=new Set();var queue=[{domainId:fromDomainId,transformer:Transformer.IDENTITY}];while(queue.length>0){queue.sort((domain1,domain2)=>domain1.transformer.error-domain2.transformer.error);var current=queue.shift();if(current.domainId===toDomainId){return current.transformer;}
+if(visitedDomainIds.has(current.domainId)){continue;}
+visitedDomainIds.add(current.domainId);var outgoingTransformers=this.transformerMapByDomainId_[current.domainId];if(!outgoingTransformers)continue;for(var outgoingDomainId in outgoingTransformers){var toNextDomainTransformer=outgoingTransformers[outgoingDomainId];var toCurrentDomainTransformer=current.transformer;queue.push({domainId:outgoingDomainId,transformer:Transformer.compose(toNextDomainTransformer,toCurrentDomainTransformer)});}}
 return undefined;},selectModelDomainId_:function(){this.ensureAllDomainsAreConnected_();for(var chromeDomainId of POSSIBLE_CHROME_CLOCK_DOMAINS){if(this.domainsSeen_.has(chromeDomainId)){this.modelDomainId_=chromeDomainId;return;}}
 var domainsSeenArray=Array.from(this.domainsSeen_);domainsSeenArray.sort();this.modelDomainId_=domainsSeenArray[0];},ensureAllDomainsAreConnected_:function(){var firstDomainId=undefined;for(var domainId of this.domainsSeen_){if(!firstDomainId){firstDomainId=domainId;continue;}
 if(!this.getTransformerBetween_(firstDomainId,domainId)){throw new Error('Unable to select a master clock domain because no '+'path can be found from "'+firstDomainId+'" to "'+domainId+'".');}}
-return true;},onDomainSeen_:function(domainId){if(domainId===ClockDomainId.UNKNOWN_CHROME_LEGACY&&!this.domainsSeen_.has(ClockDomainId.UNKNOWN_CHROME_LEGACY)){for(var chromeDomainId of POSSIBLE_CHROME_CLOCK_DOMAINS){if(chromeDomainId===ClockDomainId.UNKNOWN_CHROME_LEGACY)
-continue;this.collapseDomains_(ClockDomainId.UNKNOWN_CHROME_LEGACY,chromeDomainId);}}
-this.domainsSeen_.add(domainId);},onSyncCompleted_:function(marker1,marker2){var forwardTransformer=Transformer.fromMarkers(marker1,marker2);var backwardTransformer=Transformer.fromMarkers(marker2,marker1);var existingTransformer=this.getOrCreateTransformerMap_(marker1.domainId)[marker2.domainId];if(!existingTransformer||forwardTransformer.error<existingTransformer.error){this.getOrCreateTransformerMap_(marker1.domainId)[marker2.domainId]=forwardTransformer;this.getOrCreateTransformerMap_(marker2.domainId)[marker1.domainId]=backwardTransformer;}},collapseDomains_:function(domain1Id,domain2Id){this.getOrCreateTransformerMap_(domain1Id)[domain2Id]=this.getOrCreateTransformerMap_(domain2Id)[domain1Id]=Transformer.IDENTITY;},getOrCreateTransformerMap_:function(domainId){if(!this.transformerMapByDomainId_[domainId])
-this.transformerMapByDomainId_[domainId]={};return this.transformerMapByDomainId_[domainId];}};function ClockSyncMarker(domainId,startTs,opt_endTs){this.domainId=domainId;this.startTs=startTs;this.endTs=opt_endTs===undefined?startTs:opt_endTs;}
+return true;},onDomainSeen_:function(domainId){if(domainId===ClockDomainId.UNKNOWN_CHROME_LEGACY&&!this.domainsSeen_.has(ClockDomainId.UNKNOWN_CHROME_LEGACY)){for(var chromeDomainId of POSSIBLE_CHROME_CLOCK_DOMAINS){if(chromeDomainId===ClockDomainId.UNKNOWN_CHROME_LEGACY){continue;}
+this.collapseDomains_(ClockDomainId.UNKNOWN_CHROME_LEGACY,chromeDomainId);}}
+this.domainsSeen_.add(domainId);},onSyncCompleted_:function(marker1,marker2){var forwardTransformer=Transformer.fromMarkers(marker1,marker2);var backwardTransformer=Transformer.fromMarkers(marker2,marker1);var existingTransformer=this.getOrCreateTransformerMap_(marker1.domainId)[marker2.domainId];if(!existingTransformer||forwardTransformer.error<existingTransformer.error){this.getOrCreateTransformerMap_(marker1.domainId)[marker2.domainId]=forwardTransformer;this.getOrCreateTransformerMap_(marker2.domainId)[marker1.domainId]=backwardTransformer;}},collapseDomains_:function(domain1Id,domain2Id){this.getOrCreateTransformerMap_(domain1Id)[domain2Id]=this.getOrCreateTransformerMap_(domain2Id)[domain1Id]=Transformer.IDENTITY;},getOrCreateTransformerMap_:function(domainId){if(!this.transformerMapByDomainId_[domainId]){this.transformerMapByDomainId_[domainId]={};}
+return this.transformerMapByDomainId_[domainId];},computeDotGraph:function(){let dotString='graph {\n';const domainsSeen=[...this.domainsSeen_].sort();for(const domainId of domainsSeen){dotString+=`${domainId}[shape=box]\n`;}
+const markersBySyncIdEntries=[...this.markersBySyncId_.entries()].sort(([syncId1,markers1],[syncId2,markers2])=>syncId1.localeCompare(syncId2));for(const[syncId,markers]of markersBySyncIdEntries){const sortedMarkers=markers.sort((a,b)=>a.domainId.localeCompare(b.domainId));for(const m of markers){dotString+=`"${syncId}"--${m.domainId}`;dotString+=`[label="[${m.startTs}, ${m.endTs}]"]\n`;}}
+dotString+='}';return dotString;}};function ClockSyncMarker(domainId,startTs,opt_endTs){this.domainId=domainId;this.startTs=startTs;this.endTs=opt_endTs===undefined?startTs:opt_endTs;}
 ClockSyncMarker.prototype={get duration(){return this.endTs-this.startTs;},get ts(){return this.startTs+this.duration/2;}};function Transformer(fn,error){this.fn=fn;this.error=error;}
-Transformer.IDENTITY=new Transformer(tr.b.identity,0);Transformer.compose=function(aToB,bToC){return new Transformer((ts)=>bToC.fn(aToB.fn(ts)),aToB.error+bToC.error);};Transformer.fromMarkers=function(fromMarker,toMarker){var fromTs=fromMarker.ts,toTs=toMarker.ts;if(fromMarker.domainId===ClockDomainId.BATTOR&&toMarker.duration>BATTOR_FAST_SYNC_THRESHOLD_MS){toTs=toMarker.startTs;}else if(toMarker.domainId===ClockDomainId.BATTOR&&fromMarker.duration>BATTOR_FAST_SYNC_THRESHOLD_MS){fromTs=fromMarker.startTs;}
-var tsShift=toTs-fromTs;return new Transformer((ts)=>ts+tsShift,fromMarker.duration+toMarker.duration);};return{ClockDomainId:ClockDomainId,ClockSyncManager:ClockSyncManager};});'use strict';tr.exportTo('tr.model',function(){function EventContainer(){this.guid_=tr.b.GUID.allocateSimple();this.important=true;this.bounds_=new tr.b.Range();}
-EventContainer.prototype={get guid(){return this.guid_;},get stableId(){throw new Error('Not implemented');},get bounds(){return this.bounds_;},updateBounds:function(){throw new Error('Not implemented');},shiftTimestampsForward:function(amount){throw new Error('Not implemented');},childEvents:function*(){},getDescendantEvents:function*(){yield*this.childEvents();for(var container of this.childEventContainers())
-yield*container.getDescendantEvents();},childEventContainers:function*(){},getDescendantEventContainers:function*(){yield this;for(var container of this.childEventContainers())
-yield*container.getDescendantEventContainers();},findTopmostSlicesInThisContainer:function*(eventPredicate,opt_this){},findTopmostSlices:function*(eventPredicate){for(var ec of this.getDescendantEventContainers())
-yield*ec.findTopmostSlicesInThisContainer(eventPredicate);},findTopmostSlicesNamed:function*(name){yield*this.findTopmostSlices(e=>e.title===name);}};return{EventContainer:EventContainer};});'use strict';tr.exportTo('tr.model',function(){var Event=tr.model.Event;var EventRegistry=tr.model.EventRegistry;function PowerSample(series,start,powerInW){Event.call(this);this.series_=series;this.start_=start;this.powerInW_=powerInW;}
-PowerSample.prototype={__proto__:Event.prototype,get series(){return this.series_;},get start(){return this.start_;},set start(value){this.start_=value;},get powerInW(){return this.powerInW_;},set powerInW(value){this.powerInW_=value;},addBoundsToRange:function(range){range.addValue(this.start);}};EventRegistry.register(PowerSample,{name:'powerSample',pluralName:'powerSamples'});return{PowerSample:PowerSample};});'use strict';tr.exportTo('tr.model',function(){var PowerSample=tr.model.PowerSample;function PowerSeries(device){tr.model.EventContainer.call(this);this.device_=device;this.samples_=[];}
-PowerSeries.prototype={__proto__:tr.model.EventContainer.prototype,get device(){return this.device_;},get samples(){return this.samples_;},get stableId(){return this.device_.stableId+'.PowerSeries';},addPowerSample:function(ts,val){var sample=new PowerSample(this,ts,val);this.samples_.push(sample);return sample;},getEnergyConsumedInJ:function(start,end){var measurementRange=tr.b.Range.fromExplicitRange(start,end);var energyConsumedInJ=0;var startIndex=tr.b.findLowIndexInSortedArray(this.samples,x=>x.start,start)-1;var endIndex=tr.b.findLowIndexInSortedArray(this.samples,x=>x.start,end);if(startIndex<0)
-startIndex=0;for(var i=startIndex;i<endIndex;i++){var sample=this.samples[i];var nextSample=this.samples[i+1];var sampleRange=new tr.b.Range();sampleRange.addValue(sample.start);sampleRange.addValue(nextSample?nextSample.start:sample.start);var intersectionRangeInMs=measurementRange.findIntersection(sampleRange);var durationInS=tr.b.convertUnit(intersectionRangeInMs.duration,tr.b.UnitScale.Metric.MILLI,tr.b.UnitScale.Metric.NONE);energyConsumedInJ+=durationInS*sample.powerInW;}
-return energyConsumedInJ;},getSamplesWithinRange:function(start,end){var startIndex=tr.b.findLowIndexInSortedArray(this.samples,x=>x.start,start);var endIndex=tr.b.findLowIndexInSortedArray(this.samples,x=>x.start,end);return this.samples.slice(startIndex,endIndex);},shiftTimestampsForward:function(amount){for(var i=0;i<this.samples_.length;++i)
-this.samples_[i].start+=amount;},updateBounds:function(){this.bounds.reset();if(this.samples_.length===0)
-return;this.bounds.addValue(this.samples_[0].start);this.bounds.addValue(this.samples_[this.samples_.length-1].start);},childEvents:function*(){yield*this.samples_;},};return{PowerSeries:PowerSeries};});'use strict';tr.exportTo('tr.model',function(){function Device(model){if(!model)
-throw new Error('Must provide a model.');tr.model.EventContainer.call(this);this.powerSeries_=undefined;this.vSyncTimestamps_=[];};Device.compare=function(x,y){return x.guid-y.guid;};Device.prototype={__proto__:tr.model.EventContainer.prototype,compareTo:function(that){return Device.compare(this,that);},get userFriendlyName(){return'Device';},get userFriendlyDetails(){return'Device';},get stableId(){return'Device';},getSettingsKey:function(){return'device';},get powerSeries(){return this.powerSeries_;},set powerSeries(powerSeries){this.powerSeries_=powerSeries;},get vSyncTimestamps(){return this.vSyncTimestamps_;},set vSyncTimestamps(value){this.vSyncTimestamps_=value;},updateBounds:function(){this.bounds.reset();for(var child of this.childEventContainers()){child.updateBounds();this.bounds.addRange(child.bounds);}},shiftTimestampsForward:function(amount){for(var child of this.childEventContainers()){child.shiftTimestampsForward(amount);}
-for(var i=0;i<this.vSyncTimestamps_.length;i++)
-this.vSyncTimestamps_[i]+=amount;},addCategoriesToDict:function(categoriesDict){},childEventContainers:function*(){if(this.powerSeries_)
-yield this.powerSeries_;}};return{Device:Device};});'use strict';tr.exportTo('tr.model',function(){function FlowEvent(category,id,title,colorId,start,args,opt_duration){tr.model.TimedEvent.call(this,start);this.category=category||'';this.title=title;this.colorId=colorId;this.start=start;this.args=args;this.id=id;this.startSlice=undefined;this.endSlice=undefined;this.startStackFrame=undefined;this.endStackFrame=undefined;if(opt_duration!==undefined)
-this.duration=opt_duration;}
+Transformer.IDENTITY=new Transformer(tr.b.identity,0);Transformer.compose=function(aToB,bToC){return new Transformer((ts)=>bToC.fn(aToB.fn(ts)),aToB.error+bToC.error);};Transformer.fromMarkers=function(fromMarker,toMarker){var fromTs=fromMarker.ts;var toTs=toMarker.ts;if(fromMarker.domainId===ClockDomainId.BATTOR&&toMarker.duration>BATTOR_FAST_SYNC_THRESHOLD_MS){toTs=toMarker.startTs;}else if(toMarker.domainId===ClockDomainId.BATTOR&&fromMarker.duration>BATTOR_FAST_SYNC_THRESHOLD_MS){fromTs=fromMarker.startTs;}
+var tsShift=toTs-fromTs;return new Transformer((ts)=>ts+tsShift,fromMarker.duration+toMarker.duration);};return{ClockDomainId,ClockSyncManager,};});'use strict';tr.exportTo('tr.model',function(){function CounterSample(series,timestamp,value){tr.model.Event.call(this);this.series_=series;this.timestamp_=timestamp;this.value_=value;}
+CounterSample.groupByTimestamp=function(samples){var samplesByTimestamp=tr.b.group(samples,function(sample){return sample.timestamp;});var timestamps=Object.keys(samplesByTimestamp);timestamps.sort();var groups=[];for(var i=0;i<timestamps.length;i++){var ts=timestamps[i];var group=samplesByTimestamp[ts];group.sort(function(x,y){return x.series.seriesIndex-y.series.seriesIndex;});groups.push(group);}
+return groups;};CounterSample.prototype={__proto__:tr.model.Event.prototype,get series(){return this.series_;},get timestamp(){return this.timestamp_;},get value(){return this.value_;},set timestamp(timestamp){this.timestamp_=timestamp;},addBoundsToRange:function(range){range.addValue(this.timestamp);},getSampleIndex:function(){return tr.b.math.findLowIndexInSortedArray(this.series.timestamps,function(x){return x;},this.timestamp_);},get userFriendlyName(){return'Counter sample from '+this.series_.title+' at '+
+tr.b.Unit.byName.timeStampInMs.format(this.timestamp);}};tr.model.EventRegistry.register(CounterSample,{name:'counterSample',pluralName:'counterSamples'});return{CounterSample,};});'use strict';tr.exportTo('tr.model',function(){var CounterSample=tr.model.CounterSample;function CounterSeries(name,color){tr.model.EventContainer.call(this);this.name_=name;this.color_=color;this.timestamps_=[];this.samples_=[];this.counter=undefined;this.seriesIndex=undefined;}
+CounterSeries.prototype={__proto__:tr.model.EventContainer.prototype,get length(){return this.timestamps_.length;},get name(){return this.name_;},get color(){return this.color_;},get samples(){return this.samples_;},get timestamps(){return this.timestamps_;},getSample:function(idx){return this.samples_[idx];},getTimestamp:function(idx){return this.timestamps_[idx];},addCounterSample:function(ts,val){var sample=new CounterSample(this,ts,val);this.addSample(sample);return sample;},addSample:function(sample){this.timestamps_.push(sample.timestamp);this.samples_.push(sample);},getStatistics:function(sampleIndices){var sum=0;var min=Number.MAX_VALUE;var max=-Number.MAX_VALUE;for(var i=0;i<sampleIndices.length;++i){var sample=this.getSample(sampleIndices[i]).value;sum+=sample;min=Math.min(sample,min);max=Math.max(sample,max);}
+return{min:min,max:max,avg:(sum/sampleIndices.length),start:this.getSample(sampleIndices[0]).value,end:this.getSample(sampleIndices.length-1).value};},shiftTimestampsForward:function(amount){for(var i=0;i<this.timestamps_.length;++i){this.timestamps_[i]+=amount;this.samples_[i].timestamp=this.timestamps_[i];}},childEvents:function*(){yield*this.samples_;},childEventContainers:function*(){}};return{CounterSeries,};});'use strict';tr.exportTo('tr.model',function(){function Counter(parent,id,category,name){tr.model.EventContainer.call(this);this.parent_=parent;this.id_=id;this.category_=category||'';this.name_=name;this.series_=[];this.totals=[];}
+Counter.prototype={__proto__:tr.model.EventContainer.prototype,get parent(){return this.parent_;},get id(){return this.id_;},get category(){return this.category_;},get name(){return this.name_;},childEvents:function*(){},childEventContainers:function*(){yield*this.series;},set timestamps(arg){throw new Error('Bad counter API. No cookie.');},set seriesNames(arg){throw new Error('Bad counter API. No cookie.');},set seriesColors(arg){throw new Error('Bad counter API. No cookie.');},set samples(arg){throw new Error('Bad counter API. No cookie.');},addSeries:function(series){series.counter=this;series.seriesIndex=this.series_.length;this.series_.push(series);return series;},getSeries:function(idx){return this.series_[idx];},get series(){return this.series_;},get numSeries(){return this.series_.length;},get numSamples(){if(this.series_.length===0)return 0;return this.series_[0].length;},get timestamps(){if(this.series_.length===0)return[];return this.series_[0].timestamps;},getSampleStatistics:function(sampleIndices){sampleIndices.sort();var ret=[];this.series_.forEach(function(series){ret.push(series.getStatistics(sampleIndices));});return ret;},shiftTimestampsForward:function(amount){for(var i=0;i<this.series_.length;++i){this.series_[i].shiftTimestampsForward(amount);}},updateBounds:function(){this.totals=[];this.maxTotal=0;this.bounds.reset();if(this.series_.length===0)return;var firstSeries=this.series_[0];var lastSeries=this.series_[this.series_.length-1];this.bounds.addValue(firstSeries.getTimestamp(0));this.bounds.addValue(lastSeries.getTimestamp(lastSeries.length-1));var numSeries=this.numSeries;this.maxTotal=-Infinity;for(var i=0;i<firstSeries.length;++i){var total=0;this.series_.forEach(function(series){total+=series.getSample(i).value;this.totals.push(total);}.bind(this));this.maxTotal=Math.max(total,this.maxTotal);}}};Counter.compare=function(x,y){var tmp=x.parent.compareTo(y.parent);if(tmp!==0)return tmp;var tmp=x.name.localeCompare(y.name);if(tmp===0)return x.tid-y.tid;return tmp;};return{Counter,};});'use strict';tr.exportTo('tr.model',function(){var Slice=tr.model.Slice;function CpuSlice(cat,title,colorId,start,args,opt_duration){Slice.apply(this,arguments);this.threadThatWasRunning=undefined;this.cpu=undefined;}
+CpuSlice.prototype={__proto__:Slice.prototype,get analysisTypeName(){return'tr.ui.analysis.CpuSlice';},getAssociatedTimeslice:function(){if(!this.threadThatWasRunning){return undefined;}
+var timeSlices=this.threadThatWasRunning.timeSlices;for(var i=0;i<timeSlices.length;i++){var timeSlice=timeSlices[i];if(timeSlice.start!==this.start){continue;}
+if(timeSlice.duration!==this.duration){continue;}
+return timeSlice;}
+return undefined;}};tr.model.EventRegistry.register(CpuSlice,{name:'cpuSlice',pluralName:'cpuSlices'});return{CpuSlice,};});'use strict';tr.exportTo('tr.model',function(){function TimeToObjectInstanceMap(createObjectInstanceFunction,parent,scopedId){this.createObjectInstanceFunction_=createObjectInstanceFunction;this.parent=parent;this.scopedId=scopedId;this.instances=[];}
+TimeToObjectInstanceMap.prototype={idWasCreated:function(category,name,ts){if(this.instances.length===0){this.instances.push(this.createObjectInstanceFunction_(this.parent,this.scopedId,category,name,ts));this.instances[0].creationTsWasExplicit=true;return this.instances[0];}
+var lastInstance=this.instances[this.instances.length-1];if(ts<lastInstance.deletionTs){throw new Error('Mutation of the TimeToObjectInstanceMap must be '+'done in ascending timestamp order.');}
+lastInstance=this.createObjectInstanceFunction_(this.parent,this.scopedId,category,name,ts);lastInstance.creationTsWasExplicit=true;this.instances.push(lastInstance);return lastInstance;},addSnapshot:function(category,name,ts,args,opt_baseTypeName){if(this.instances.length===0){this.instances.push(this.createObjectInstanceFunction_(this.parent,this.scopedId,category,name,ts,opt_baseTypeName));}
+var i=tr.b.math.findIndexInSortedIntervals(this.instances,function(inst){return inst.creationTs;},function(inst){return inst.deletionTs-inst.creationTs;},ts);var instance;if(i<0){instance=this.instances[0];if(ts>instance.deletionTs||instance.creationTsWasExplicit){throw new Error('At the provided timestamp, no instance was still alive');}
+if(instance.snapshots.length!==0){throw new Error('Cannot shift creationTs forward, '+'snapshots have been added. First snap was at ts='+
+instance.snapshots[0].ts+' and creationTs was '+
+instance.creationTs);}
+instance.creationTs=ts;}else if(i>=this.instances.length){instance=this.instances[this.instances.length-1];if(ts>=instance.deletionTs){instance=this.createObjectInstanceFunction_(this.parent,this.scopedId,category,name,ts,opt_baseTypeName);this.instances.push(instance);}else{var lastValidIndex;for(var i=this.instances.length-1;i>=0;i--){var tmp=this.instances[i];if(ts>=tmp.deletionTs)break;if(tmp.creationTsWasExplicit===false&&tmp.snapshots.length===0){lastValidIndex=i;}}
+if(lastValidIndex===undefined){throw new Error('Cannot add snapshot. No instance was alive that was mutable.');}
+instance=this.instances[lastValidIndex];instance.creationTs=ts;}}else{instance=this.instances[i];}
+return instance.addSnapshot(ts,args,name,opt_baseTypeName);},get lastInstance(){if(this.instances.length===0)return undefined;return this.instances[this.instances.length-1];},idWasDeleted:function(category,name,ts){if(this.instances.length===0){this.instances.push(this.createObjectInstanceFunction_(this.parent,this.scopedId,category,name,ts));}
+var lastInstance=this.instances[this.instances.length-1];if(ts<lastInstance.creationTs){throw new Error('Cannot delete an id before it was created');}
+if(lastInstance.deletionTs===Number.MAX_VALUE){lastInstance.wasDeleted(ts);return lastInstance;}
+if(ts<lastInstance.deletionTs){throw new Error('id was already deleted earlier.');}
+lastInstance=this.createObjectInstanceFunction_(this.parent,this.scopedId,category,name,ts);this.instances.push(lastInstance);lastInstance.wasDeleted(ts);return lastInstance;},getInstanceAt:function(ts){var i=tr.b.math.findIndexInSortedIntervals(this.instances,function(inst){return inst.creationTs;},function(inst){return inst.deletionTs-inst.creationTs;},ts);if(i<0){if(this.instances[0].creationTsWasExplicit){return undefined;}
+return this.instances[0];}else if(i>=this.instances.length){return undefined;}
+return this.instances[i];}};return{TimeToObjectInstanceMap,};});'use strict';tr.exportTo('tr.model',function(){var ObjectInstance=tr.model.ObjectInstance;var ObjectSnapshot=tr.model.ObjectSnapshot;function ObjectCollection(parent){tr.model.EventContainer.call(this);this.parent=parent;this.instanceMapsByScopedId_={};this.instancesByTypeName_={};this.createObjectInstance_=this.createObjectInstance_.bind(this);}
+ObjectCollection.prototype={__proto__:tr.model.EventContainer.prototype,childEvents:function*(){for(var instance of this.getAllObjectInstances()){yield instance;yield*instance.snapshots;}},createObjectInstance_:function(parent,scopedId,category,name,creationTs,opt_baseTypeName){var constructor=tr.model.ObjectInstance.subTypes.getConstructor(category,name);var instance=new constructor(parent,scopedId,category,name,creationTs,opt_baseTypeName);var typeName=instance.typeName;var instancesOfTypeName=this.instancesByTypeName_[typeName];if(!instancesOfTypeName){instancesOfTypeName=[];this.instancesByTypeName_[typeName]=instancesOfTypeName;}
+instancesOfTypeName.push(instance);return instance;},getOrCreateInstanceMap_:function(scopedId){var dict;if(scopedId.scope in this.instanceMapsByScopedId_){dict=this.instanceMapsByScopedId_[scopedId.scope];}else{dict={};this.instanceMapsByScopedId_[scopedId.scope]=dict;}
+var instanceMap=dict[scopedId.id];if(instanceMap)return instanceMap;instanceMap=new tr.model.TimeToObjectInstanceMap(this.createObjectInstance_,this.parent,scopedId);dict[scopedId.id]=instanceMap;return instanceMap;},idWasCreated:function(scopedId,category,name,ts){var instanceMap=this.getOrCreateInstanceMap_(scopedId);return instanceMap.idWasCreated(category,name,ts);},addSnapshot:function(scopedId,category,name,ts,args,opt_baseTypeName){var instanceMap=this.getOrCreateInstanceMap_(scopedId);var snapshot=instanceMap.addSnapshot(category,name,ts,args,opt_baseTypeName);if(snapshot.objectInstance.category!==category){var msg='Added snapshot name='+name+' with cat='+category+' impossible. It instance was created/snapshotted with cat='+
+snapshot.objectInstance.category+' name='+
+snapshot.objectInstance.name;throw new Error(msg);}
+if(opt_baseTypeName&&snapshot.objectInstance.baseTypeName!==opt_baseTypeName){throw new Error('Could not add snapshot with baseTypeName='+
+opt_baseTypeName+'. It '+'was previously created with name='+
+snapshot.objectInstance.baseTypeName);}
+if(snapshot.objectInstance.name!==name){throw new Error('Could not add snapshot with name='+name+'. It '+'was previously created with name='+
+snapshot.objectInstance.name);}
+return snapshot;},idWasDeleted:function(scopedId,category,name,ts){var instanceMap=this.getOrCreateInstanceMap_(scopedId);var deletedInstance=instanceMap.idWasDeleted(category,name,ts);if(!deletedInstance)return;if(deletedInstance.category!==category){var msg='Deleting object '+deletedInstance.name+' with a different category '+'than when it was created. It previous had cat='+
+deletedInstance.category+' but the delete command '+'had cat='+category;throw new Error(msg);}
+if(deletedInstance.baseTypeName!==name){throw new Error('Deletion requested for name='+
+name+' could not proceed: '+'An existing object with baseTypeName='+
+deletedInstance.baseTypeName+' existed.');}},autoDeleteObjects:function(maxTimestamp){for(var imapById of Object.values(this.instanceMapsByScopedId_)){for(var i2imap of Object.values(imapById)){var lastInstance=i2imap.lastInstance;if(lastInstance.deletionTs!==Number.MAX_VALUE)continue;i2imap.idWasDeleted(lastInstance.category,lastInstance.name,maxTimestamp);lastInstance.deletionTsWasExplicit=false;}}},getObjectInstanceAt:function(scopedId,ts){var instanceMap;if(scopedId.scope in this.instanceMapsByScopedId_){instanceMap=this.instanceMapsByScopedId_[scopedId.scope][scopedId.id];}
+if(!instanceMap)return undefined;return instanceMap.getInstanceAt(ts);},getSnapshotAt:function(scopedId,ts){var instance=this.getObjectInstanceAt(scopedId,ts);if(!instance)return undefined;return instance.getSnapshotAt(ts);},iterObjectInstances:function(iter,opt_this){opt_this=opt_this||this;for(var imapById of Object.values(this.instanceMapsByScopedId_)){for(var i2imap of Object.values(imapById)){i2imap.instances.forEach(iter,opt_this);}}},getAllObjectInstances:function(){var instances=[];this.iterObjectInstances(function(i){instances.push(i);});return instances;},getAllInstancesNamed:function(name){return this.instancesByTypeName_[name];},getAllInstancesByTypeName:function(){return this.instancesByTypeName_;},preInitializeAllObjects:function(){this.iterObjectInstances(function(instance){instance.preInitialize();});},initializeAllObjects:function(){this.iterObjectInstances(function(instance){instance.initialize();});},initializeInstances:function(){this.iterObjectInstances(function(instance){instance.initialize();});},updateBounds:function(){this.bounds.reset();this.iterObjectInstances(function(instance){instance.updateBounds();this.bounds.addRange(instance.bounds);},this);},shiftTimestampsForward:function(amount){this.iterObjectInstances(function(instance){instance.shiftTimestampsForward(amount);});},addCategoriesToDict:function(categoriesDict){this.iterObjectInstances(function(instance){categoriesDict[instance.category]=true;});}};return{ObjectCollection,};});'use strict';tr.exportTo('tr.model',function(){function AsyncSliceGroup(parentContainer,opt_name){tr.model.EventContainer.call(this);this.parentContainer_=parentContainer;this.slices=[];this.name_=opt_name;this.viewSubGroups_=undefined;}
+AsyncSliceGroup.prototype={__proto__:tr.model.EventContainer.prototype,get parentContainer(){return this.parentContainer_;},get model(){return this.parentContainer_.parent.model;},get stableId(){return this.parentContainer_.stableId+'.AsyncSliceGroup';},getSettingsKey:function(){if(!this.name_)return undefined;var parentKey=this.parentContainer_.getSettingsKey();if(!parentKey)return undefined;return parentKey+'.'+this.name_;},push:function(slice){slice.parentContainer=this.parentContainer;this.slices.push(slice);return slice;},get length(){return this.slices.length;},shiftTimestampsForward:function(amount){for(var sI=0;sI<this.slices.length;sI++){var slice=this.slices[sI];slice.start=(slice.start+amount);var shiftSubSlices=function(subSlices){if(subSlices===undefined||subSlices.length===0)return;for(var sJ=0;sJ<subSlices.length;sJ++){subSlices[sJ].start+=amount;shiftSubSlices(subSlices[sJ].subSlices);}};shiftSubSlices(slice.subSlices);}},updateBounds:function(){this.bounds.reset();for(var i=0;i<this.slices.length;i++){this.bounds.addValue(this.slices[i].start);this.bounds.addValue(this.slices[i].end);}},get viewSubGroups(){if(this.viewSubGroups_===undefined){var prefix='';if(this.name!==undefined){prefix=this.name+'.';}else{prefix='';}
+var subGroupsByTitle={};for(var i=0;i<this.slices.length;++i){var slice=this.slices[i];var subGroupTitle=slice.viewSubGroupTitle;if(!subGroupsByTitle[subGroupTitle]){subGroupsByTitle[subGroupTitle]=new AsyncSliceGroup(this.parentContainer_,prefix+subGroupTitle);}
+subGroupsByTitle[subGroupTitle].push(slice);}
+this.viewSubGroups_=tr.b.dictionaryValues(subGroupsByTitle);this.viewSubGroups_.sort(function(a,b){return a.slices[0].compareTo(b.slices[0]);});}
+return this.viewSubGroups_;},findTopmostSlicesInThisContainer:function*(eventPredicate,opt_this){for(var slice of this.slices){if(slice.isTopLevel){yield*slice.findTopmostSlicesRelativeToThisSlice(eventPredicate,opt_this);}}},childEvents:function*(){for(var slice of this.slices){yield slice;if(slice.subSlices){yield*slice.subSlices;}}},childEventContainers:function*(){}};return{AsyncSliceGroup,};});'use strict';tr.exportTo('tr.model',function(){var Slice=tr.model.Slice;function ThreadSlice(cat,title,colorId,start,args,opt_duration,opt_cpuStart,opt_cpuDuration,opt_argsStripped,opt_bindId){Slice.call(this,cat,title,colorId,start,args,opt_duration,opt_cpuStart,opt_cpuDuration,opt_argsStripped,opt_bindId);this.subSlices=[];}
+ThreadSlice.prototype={__proto__:Slice.prototype,get overlappingSamples(){var samples=new tr.model.EventSet();if(!this.parentContainer||!this.parentContainer.samples){return samples;}
+this.parentContainer.samples.forEach(function(sample){if(this.start<=sample.start&&sample.start<=this.end){samples.push(sample);}},this);return samples;}};tr.model.EventRegistry.register(ThreadSlice,{name:'slice',pluralName:'slices'});return{ThreadSlice,};});'use strict';tr.exportTo('tr.model',function(){var ColorScheme=tr.b.ColorScheme;var ThreadSlice=tr.model.ThreadSlice;function getSliceLo(s){return s.start;}
+function getSliceHi(s){return s.end;}
+function SliceGroup(parentContainer,opt_sliceConstructor,opt_name){tr.model.EventContainer.call(this);this.parentContainer_=parentContainer;var sliceConstructor=opt_sliceConstructor||ThreadSlice;this.sliceConstructor=sliceConstructor;this.sliceConstructorSubTypes=this.sliceConstructor.subTypes;if(!this.sliceConstructorSubTypes){throw new Error('opt_sliceConstructor must have a subtype registry.');}
+this.openPartialSlices_=[];this.slices=[];this.topLevelSlices=[];this.haveTopLevelSlicesBeenBuilt=false;this.name_=opt_name;if(this.model===undefined){throw new Error('SliceGroup must have model defined.');}}
+SliceGroup.prototype={__proto__:tr.model.EventContainer.prototype,get parentContainer(){return this.parentContainer_;},get model(){return this.parentContainer_.model;},get stableId(){return this.parentContainer_.stableId+'.SliceGroup';},getSettingsKey:function(){if(!this.name_)return undefined;var parentKey=this.parentContainer_.getSettingsKey();if(!parentKey)return undefined;return parentKey+'.'+this.name;},get length(){return this.slices.length;},pushSlice:function(slice){this.haveTopLevelSlicesBeenBuilt=false;slice.parentContainer=this.parentContainer_;this.slices.push(slice);return slice;},pushSlices:function(slices){this.haveTopLevelSlicesBeenBuilt=false;slices.forEach(function(slice){slice.parentContainer=this.parentContainer_;this.slices.push(slice);},this);},beginSlice:function(category,title,ts,opt_args,opt_tts,opt_argsStripped,opt_colorId){if(this.openPartialSlices_.length){var prevSlice=this.openPartialSlices_[this.openPartialSlices_.length-1];if(ts<prevSlice.start){throw new Error('Slices must be added in increasing timestamp order');}}
+var colorId=opt_colorId||ColorScheme.getColorIdForGeneralPurposeString(title);var sliceConstructorSubTypes=this.sliceConstructorSubTypes;var sliceType=sliceConstructorSubTypes.getConstructor(category,title);var slice=new sliceType(category,title,colorId,ts,opt_args?opt_args:{},null,opt_tts,undefined,opt_argsStripped);this.openPartialSlices_.push(slice);slice.didNotFinish=true;this.pushSlice(slice);return slice;},isTimestampValidForBeginOrEnd:function(ts){if(!this.openPartialSlices_.length)return true;var top=this.openPartialSlices_[this.openPartialSlices_.length-1];return ts>=top.start;},get openSliceCount(){return this.openPartialSlices_.length;},get mostRecentlyOpenedPartialSlice(){if(!this.openPartialSlices_.length)return undefined;return this.openPartialSlices_[this.openPartialSlices_.length-1];},endSlice:function(ts,opt_tts,opt_colorId){if(!this.openSliceCount){throw new Error('endSlice called without an open slice');}
+var slice=this.openPartialSlices_[this.openSliceCount-1];this.openPartialSlices_.splice(this.openSliceCount-1,1);if(ts<slice.start){throw new Error('Slice '+slice.title+' end time is before its start.');}
+slice.duration=ts-slice.start;slice.didNotFinish=false;slice.colorId=opt_colorId||slice.colorId;if(opt_tts&&slice.cpuStart!==undefined){slice.cpuDuration=opt_tts-slice.cpuStart;}
+return slice;},pushCompleteSlice:function(category,title,ts,duration,tts,cpuDuration,opt_args,opt_argsStripped,opt_colorId,opt_bindId){var colorId=opt_colorId||ColorScheme.getColorIdForGeneralPurposeString(title);var sliceConstructorSubTypes=this.sliceConstructorSubTypes;var sliceType=sliceConstructorSubTypes.getConstructor(category,title);var slice=new sliceType(category,title,colorId,ts,opt_args?opt_args:{},duration,tts,cpuDuration,opt_argsStripped,opt_bindId);if(duration===undefined){slice.didNotFinish=true;}
+this.pushSlice(slice);return slice;},autoCloseOpenSlices:function(){this.updateBounds();var maxTimestamp=this.bounds.max;for(var sI=0;sI<this.slices.length;sI++){var slice=this.slices[sI];if(slice.didNotFinish){slice.duration=maxTimestamp-slice.start;}}
+this.openPartialSlices_=[];},shiftTimestampsForward:function(amount){for(var sI=0;sI<this.slices.length;sI++){var slice=this.slices[sI];slice.start=(slice.start+amount);}},updateBounds:function(){this.bounds.reset();for(var i=0;i<this.slices.length;i++){this.bounds.addValue(this.slices[i].start);this.bounds.addValue(this.slices[i].end);}},copySlice:function(slice){var sliceConstructorSubTypes=this.sliceConstructorSubTypes;var sliceType=sliceConstructorSubTypes.getConstructor(slice.category,slice.title);var newSlice=new sliceType(slice.category,slice.title,slice.colorId,slice.start,slice.args,slice.duration,slice.cpuStart,slice.cpuDuration);newSlice.didNotFinish=slice.didNotFinish;return newSlice;},findTopmostSlicesInThisContainer:function*(eventPredicate,opt_this){if(!this.haveTopLevelSlicesBeenBuilt){throw new Error('Nope');}
+for(var s of this.topLevelSlices){yield*s.findTopmostSlicesRelativeToThisSlice(eventPredicate);}},childEvents:function*(){yield*this.slices;},childEventContainers:function*(){},getSlicesOfName:function(title){var slices=[];for(var i=0;i<this.slices.length;i++){if(this.slices[i].title===title){slices.push(this.slices[i]);}}
+return slices;},iterSlicesInTimeRange:function(callback,start,end){var ret=[];tr.b.math.iterateOverIntersectingIntervals(this.topLevelSlices,function(s){return s.start;},function(s){return s.duration;},start,end,function(topLevelSlice){callback(topLevelSlice);for(var slice of topLevelSlice.enumerateAllDescendents()){callback(slice);}});return ret;},findFirstSlice:function(){if(!this.haveTopLevelSlicesBeenBuilt){throw new Error('Nope');}
+if(0===this.slices.length)return undefined;return this.slices[0];},findSliceAtTs:function(ts){if(!this.haveTopLevelSlicesBeenBuilt)throw new Error('Nope');var i=tr.b.math.findIndexInSortedClosedIntervals(this.topLevelSlices,getSliceLo,getSliceHi,ts);if(i===-1||i===this.topLevelSlices.length){return undefined;}
+var curSlice=this.topLevelSlices[i];while(true){var i=tr.b.math.findIndexInSortedClosedIntervals(curSlice.subSlices,getSliceLo,getSliceHi,ts);if(i===-1||i===curSlice.subSlices.length){return curSlice;}
+curSlice=curSlice.subSlices[i];}},findNextSliceAfter:function(ts,refGuid){var i=tr.b.math.findLowIndexInSortedArray(this.slices,getSliceLo,ts);if(i===this.slices.length){return undefined;}
+for(;i<this.slices.length;i++){var slice=this.slices[i];if(slice.start>ts)return slice;if(slice.guid<=refGuid)continue;return slice;}
+return undefined;},hasCpuDuration_:function(){if(this.slices.some(function(slice){return slice.cpuDuration!==undefined;}))return true;return false;},createSubSlices:function(){this.haveTopLevelSlicesBeenBuilt=true;this.createSubSlicesImpl_();if(!this.hasCpuDuration_()&&this.parentContainer.timeSlices){this.addCpuTimeToSubslices_(this.parentContainer.timeSlices);}
+this.slices.forEach(function(slice){var selfTime=slice.duration;for(var i=0;i<slice.subSlices.length;i++){selfTime-=slice.subSlices[i].duration;}
+slice.selfTime=selfTime;if(slice.cpuDuration===undefined)return;var cpuSelfTime=slice.cpuDuration;for(var i=0;i<slice.subSlices.length;i++){if(slice.subSlices[i].cpuDuration!==undefined){cpuSelfTime-=slice.subSlices[i].cpuDuration;}}
+slice.cpuSelfTime=cpuSelfTime;});},createSubSlicesImpl_:function(){var precisionUnit=this.model.intrinsicTimeUnit;function addSliceIfBounds(parent,child){if(parent.bounds(child,precisionUnit)){child.parentSlice=parent;if(parent.subSlices===undefined){parent.subSlices=[];}
+parent.subSlices.push(child);return true;}
+return false;}
+if(!this.slices.length)return;var ops=[];for(var i=0;i<this.slices.length;i++){if(this.slices[i].subSlices){this.slices[i].subSlices.splice(0,this.slices[i].subSlices.length);}
+ops.push(i);}
+var originalSlices=this.slices;ops.sort(function(ix,iy){var x=originalSlices[ix];var y=originalSlices[iy];if(x.start!==y.start){return x.start-y.start;}
+return ix-iy;});var slices=new Array(this.slices.length);for(var i=0;i<ops.length;i++){slices[i]=originalSlices[ops[i]];}
+var rootSlice=slices[0];this.topLevelSlices=[];this.topLevelSlices.push(rootSlice);rootSlice.isTopLevel=true;for(var i=1;i<slices.length;i++){var slice=slices[i];while(rootSlice!==undefined&&(!addSliceIfBounds(rootSlice,slice))){rootSlice=rootSlice.parentSlice;}
+if(rootSlice===undefined){this.topLevelSlices.push(slice);slice.isTopLevel=true;}
+rootSlice=slice;}
+this.slices=slices;},addCpuTimeToSubslices_:function(timeSlices){var SCHEDULING_STATE=tr.model.SCHEDULING_STATE;var sliceIdx=0;timeSlices.forEach(function(timeSlice){if(timeSlice.schedulingState===SCHEDULING_STATE.RUNNING){while(sliceIdx<this.topLevelSlices.length){if(this.addCpuTimeToSubslice_(this.topLevelSlices[sliceIdx],timeSlice)){sliceIdx++;}else{break;}}}},this);},addCpuTimeToSubslice_:function(slice,timeSlice){if(slice.start>timeSlice.end||slice.end<timeSlice.start){return slice.end<=timeSlice.end;}
+var duration=timeSlice.duration;if(slice.start>timeSlice.start){duration-=slice.start-timeSlice.start;}
+if(timeSlice.end>slice.end){duration-=timeSlice.end-slice.end;}
+if(slice.cpuDuration){slice.cpuDuration+=duration;}else{slice.cpuDuration=duration;}
+for(var i=0;i<slice.subSlices.length;i++){this.addCpuTimeToSubslice_(slice.subSlices[i],timeSlice);}
+return slice.end<=timeSlice.end;}};SliceGroup.merge=function(groupA,groupB){if(groupA.openPartialSlices_.length>0){throw new Error('groupA has open partial slices');}
+if(groupB.openPartialSlices_.length>0){throw new Error('groupB has open partial slices');}
+if(groupA.parentContainer!==groupB.parentContainer){throw new Error('Different parent threads. Cannot merge');}
+if(groupA.sliceConstructor!==groupB.sliceConstructor){throw new Error('Different slice constructors. Cannot merge');}
+var result=new SliceGroup(groupA.parentContainer,groupA.sliceConstructor,groupA.name_);var slicesA=groupA.slices;var slicesB=groupB.slices;var idxA=0;var idxB=0;var openA=[];var openB=[];var splitOpenSlices=function(when){for(var i=0;i<openB.length;i++){var oldSlice=openB[i];var oldEnd=oldSlice.end;if(when<oldSlice.start||oldEnd<when){throw new Error('slice should not be split');}
+var newSlice=result.copySlice(oldSlice);newSlice.start=when;newSlice.duration=oldEnd-when;if(newSlice.title.indexOf(' (cont.)')===-1){newSlice.title+=' (cont.)';}
+oldSlice.duration=when-oldSlice.start;openB[i]=newSlice;result.pushSlice(newSlice);}};var closeOpenSlices=function(upTo){while(openA.length>0||openB.length>0){var nextA=openA[openA.length-1];var nextB=openB[openB.length-1];var endA=nextA&&nextA.end;var endB=nextB&&nextB.end;if((endA===undefined||endA>upTo)&&(endB===undefined||endB>upTo)){return;}
+if(endB===undefined||endA<endB){splitOpenSlices(endA);openA.pop();}else{openB.pop();}}};while(idxA<slicesA.length||idxB<slicesB.length){var sA=slicesA[idxA];var sB=slicesB[idxB];var nextSlice;var isFromB;if(sA===undefined||(sB!==undefined&&sA.start>sB.start)){nextSlice=result.copySlice(sB);isFromB=true;idxB++;}else{nextSlice=result.copySlice(sA);isFromB=false;idxA++;}
+closeOpenSlices(nextSlice.start);result.pushSlice(nextSlice);if(isFromB){openB.push(nextSlice);}else{splitOpenSlices(nextSlice.start);openA.push(nextSlice);}}
+closeOpenSlices();return result;};return{SliceGroup,};});'use strict';tr.exportTo('tr.model',function(){var AsyncSlice=tr.model.AsyncSlice;var AsyncSliceGroup=tr.model.AsyncSliceGroup;var SliceGroup=tr.model.SliceGroup;var ThreadSlice=tr.model.ThreadSlice;var ThreadTimeSlice=tr.model.ThreadTimeSlice;function Thread(parent,tid){if(!parent){throw new Error('Parent must be provided.');}
+tr.model.EventContainer.call(this);this.parent=parent;this.sortIndex=0;this.tid=tid;this.name=undefined;this.samples_=undefined;this.sliceGroup=new SliceGroup(this,ThreadSlice,'slices');this.timeSlices=undefined;this.kernelSliceGroup=new SliceGroup(this,ThreadSlice,'kernel-slices');this.asyncSliceGroup=new AsyncSliceGroup(this,'async-slices');}
+Thread.prototype={__proto__:tr.model.EventContainer.prototype,get model(){return this.parent.model;},get stableId(){return this.parent.stableId+'.'+this.tid;},compareTo:function(that){return Thread.compare(this,that);},childEventContainers:function*(){if(this.sliceGroup.length){yield this.sliceGroup;}
+if(this.kernelSliceGroup.length){yield this.kernelSliceGroup;}
+if(this.asyncSliceGroup.length){yield this.asyncSliceGroup;}},childEvents:function*(){if(this.timeSlices){yield*this.timeSlices;}},iterateAllPersistableObjects:function(cb){cb(this);if(this.sliceGroup.length){cb(this.sliceGroup);}
+this.asyncSliceGroup.viewSubGroups.forEach(cb);},shiftTimestampsForward:function(amount){this.sliceGroup.shiftTimestampsForward(amount);if(this.timeSlices){for(var i=0;i<this.timeSlices.length;i++){var slice=this.timeSlices[i];slice.start+=amount;}}
+this.kernelSliceGroup.shiftTimestampsForward(amount);this.asyncSliceGroup.shiftTimestampsForward(amount);},get isEmpty(){if(this.sliceGroup.length)return false;if(this.sliceGroup.openSliceCount)return false;if(this.timeSlices&&this.timeSlices.length)return false;if(this.kernelSliceGroup.length)return false;if(this.asyncSliceGroup.length)return false;if(this.samples_.length)return false;return true;},updateBounds:function(){this.bounds.reset();this.sliceGroup.updateBounds();this.bounds.addRange(this.sliceGroup.bounds);this.kernelSliceGroup.updateBounds();this.bounds.addRange(this.kernelSliceGroup.bounds);this.asyncSliceGroup.updateBounds();this.bounds.addRange(this.asyncSliceGroup.bounds);if(this.timeSlices&&this.timeSlices.length){this.bounds.addValue(this.timeSlices[0].start);this.bounds.addValue(this.timeSlices[this.timeSlices.length-1].end);}
+if(this.samples_&&this.samples_.length){this.bounds.addValue(this.samples_[0].start);this.bounds.addValue(this.samples_[this.samples_.length-1].end);}},addCategoriesToDict:function(categoriesDict){for(var i=0;i<this.sliceGroup.length;i++){categoriesDict[this.sliceGroup.slices[i].category]=true;}
+for(var i=0;i<this.kernelSliceGroup.length;i++){categoriesDict[this.kernelSliceGroup.slices[i].category]=true;}
+for(var i=0;i<this.asyncSliceGroup.length;i++){categoriesDict[this.asyncSliceGroup.slices[i].category]=true;}
+if(this.samples_){for(var i=0;i<this.samples_.length;i++){categoriesDict[this.samples_[i].category]=true;}}},autoCloseOpenSlices:function(){this.sliceGroup.autoCloseOpenSlices();this.kernelSliceGroup.autoCloseOpenSlices();},mergeKernelWithUserland:function(){if(this.kernelSliceGroup.length>0){var newSlices=SliceGroup.merge(this.sliceGroup,this.kernelSliceGroup);this.sliceGroup.slices=newSlices.slices;this.kernelSliceGroup=new SliceGroup(this);this.updateBounds();}},createSubSlices:function(){this.sliceGroup.createSubSlices();this.samples_=this.parent.model.samples.filter(function(sample){return sample.thread===this;},this);},get userFriendlyName(){return this.name||this.tid;},get userFriendlyDetails(){return'tid: '+this.tid+
+(this.name?', name: '+this.name:'');},getSettingsKey:function(){if(!this.name)return undefined;var parentKey=this.parent.getSettingsKey();if(!parentKey)return undefined;return parentKey+'.'+this.name;},getProcess:function(){return this.parent;},indexOfTimeSlice:function(timeSlice){var i=tr.b.math.findLowIndexInSortedArray(this.timeSlices,function(slice){return slice.start;},timeSlice.start);if(this.timeSlices[i]!==timeSlice)return undefined;return i;},getCpuStatsForRange:function(range){var stats={};stats.total=0;if(!this.timeSlices)return stats;function addStatsForSlice(threadTimeSlice){var freqRange=tr.b.math.Range.fromExplicitRange(threadTimeSlice.start,threadTimeSlice.end);var intersection=freqRange.findIntersection(range);if(threadTimeSlice.schedulingState===tr.model.SCHEDULING_STATE.RUNNING){var cpu=threadTimeSlice.cpuOnWhichThreadWasRunning;if(!(cpu.cpuNumber in stats)){stats[cpu.cpuNumber]=0;}
+stats[cpu.cpuNumber]+=intersection.duration;stats.total+=intersection.duration;}}
+tr.b.math.iterateOverIntersectingIntervals(this.timeSlices,function(x){return x.start;},function(x){return x.end;},range.min,range.max,addStatsForSlice);return stats;},getSchedulingStatsForRange:function(start,end){var stats={};if(!this.timeSlices)return stats;function addStatsForSlice(threadTimeSlice){var overlapStart=Math.max(threadTimeSlice.start,start);var overlapEnd=Math.min(threadTimeSlice.end,end);var schedulingState=threadTimeSlice.schedulingState;if(!(schedulingState in stats))stats[schedulingState]=0;stats[schedulingState]+=overlapEnd-overlapStart;}
+tr.b.math.iterateOverIntersectingIntervals(this.timeSlices,function(x){return x.start;},function(x){return x.end;},start,end,addStatsForSlice);return stats;},get samples(){return this.samples_;},getNetworkEventsInRange:function(rangeOfInterest){const networkEvents=[];for(const slice of Object.values(this.asyncSliceGroup.slices)){let match=false;if(slice.category==='net'||slice.category==='disabled-by-default-netlog'||slice.category==='netlog'||slice.category==='disabled-by-default-network'){match=true;}
+if(!match)continue;if(rangeOfInterest.intersectsExplicitRangeInclusive(slice.start,slice.end)){networkEvents.push(slice);}}
+return networkEvents;}};Thread.compare=function(x,y){var tmp=x.parent.compareTo(y.parent);if(tmp)return tmp;tmp=x.sortIndex-y.sortIndex;if(tmp)return tmp;tmp=tr.b.comparePossiblyUndefinedValues(x.name,y.name,function(x,y){return x.localeCompare(y);});if(tmp)return tmp;return x.tid-y.tid;};return{Thread,};});'use strict';tr.exportTo('tr.model',function(){var Thread=tr.model.Thread;var Counter=tr.model.Counter;function ProcessBase(model){if(!model){throw new Error('Must provide a model');}
+tr.model.EventContainer.call(this);this.model=model;this.threads={};this.counters={};this.objects=new tr.model.ObjectCollection(this);this.sortIndex=0;}
+ProcessBase.compare=function(x,y){return x.sortIndex-y.sortIndex;};ProcessBase.prototype={__proto__:tr.model.EventContainer.prototype,get stableId(){throw new Error('Not implemented');},childEventContainers:function*(){yield*tr.b.dictionaryValues(this.threads);yield*tr.b.dictionaryValues(this.counters);yield this.objects;},iterateAllPersistableObjects:function(cb){cb(this);for(var tid in this.threads){this.threads[tid].iterateAllPersistableObjects(cb);}},get numThreads(){var n=0;for(var p in this.threads){n++;}
+return n;},shiftTimestampsForward:function(amount){for(var child of this.childEventContainers()){child.shiftTimestampsForward(amount);}},autoCloseOpenSlices:function(){for(var tid in this.threads){var thread=this.threads[tid];thread.autoCloseOpenSlices();}},autoDeleteObjects:function(maxTimestamp){this.objects.autoDeleteObjects(maxTimestamp);},preInitializeObjects:function(){this.objects.preInitializeAllObjects();},initializeObjects:function(){this.objects.initializeAllObjects();},mergeKernelWithUserland:function(){for(var tid in this.threads){var thread=this.threads[tid];thread.mergeKernelWithUserland();}},updateBounds:function(){this.bounds.reset();for(var tid in this.threads){this.threads[tid].updateBounds();this.bounds.addRange(this.threads[tid].bounds);}
+for(var id in this.counters){this.counters[id].updateBounds();this.bounds.addRange(this.counters[id].bounds);}
+this.objects.updateBounds();this.bounds.addRange(this.objects.bounds);},addCategoriesToDict:function(categoriesDict){for(var tid in this.threads){this.threads[tid].addCategoriesToDict(categoriesDict);}
+for(var id in this.counters){categoriesDict[this.counters[id].category]=true;}
+this.objects.addCategoriesToDict(categoriesDict);},findAllThreadsMatching:function(predicate,opt_this){var threads=[];for(var tid in this.threads){var thread=this.threads[tid];if(predicate.call(opt_this,thread)){threads.push(thread);}}
+return threads;},findAllThreadsNamed:function(name){var threads=this.findAllThreadsMatching(function(thread){if(!thread.name)return false;return thread.name===name;});return threads;},findAtMostOneThreadNamed:function(name){var threads=this.findAllThreadsNamed(name);if(threads.length===0)return undefined;if(threads.length>1){throw new Error('Expected no more than one '+name);}
+return threads[0];},pruneEmptyContainers:function(){var threadsToKeep={};for(var tid in this.threads){var thread=this.threads[tid];if(!thread.isEmpty){threadsToKeep[tid]=thread;}}
+this.threads=threadsToKeep;},getThread:function(tid){return this.threads[tid];},getOrCreateThread:function(tid){if(!this.threads[tid]){this.threads[tid]=new Thread(this,tid);}
+return this.threads[tid];},getOrCreateCounter:function(cat,name){var id=cat+'.'+name;if(!this.counters[id]){this.counters[id]=new Counter(this,id,cat,name);}
+return this.counters[id];},getSettingsKey:function(){throw new Error('Not implemented');},createSubSlices:function(){for(var tid in this.threads){this.threads[tid].createSubSlices();}}};return{ProcessBase,};});'use strict';tr.exportTo('tr.model',function(){var ColorScheme=tr.b.ColorScheme;var Counter=tr.model.Counter;var CpuSlice=tr.model.CpuSlice;function Cpu(kernel,number){if(kernel===undefined||number===undefined){throw new Error('Missing arguments');}
+this.kernel=kernel;this.cpuNumber=number;this.slices=[];this.counters={};this.bounds_=new tr.b.math.Range();this.samples_=undefined;this.lastActiveTimestamp_=undefined;this.lastActiveThread_=undefined;this.lastActiveName_=undefined;this.lastActiveArgs_=undefined;}
+Cpu.prototype={__proto__:tr.model.EventContainer.prototype,get samples(){return this.samples_;},get userFriendlyName(){return'CPU '+this.cpuNumber;},findTopmostSlicesInThisContainer:function*(eventPredicate,opt_this){for(var s of this.slices){yield*s.findTopmostSlicesRelativeToThisSlice(eventPredicate,opt_this);}},childEvents:function*(){yield*this.slices;if(this.samples_){yield*this.samples_;}},childEventContainers:function*(){yield*tr.b.dictionaryValues(this.counters);},getOrCreateCounter:function(cat,name){var id=cat+'.'+name;if(!this.counters[id]){this.counters[id]=new Counter(this,id,cat,name);}
+return this.counters[id];},getCounter:function(cat,name){var id=cat+'.'+name;if(!this.counters[id]){return undefined;}
+return this.counters[id];},shiftTimestampsForward:function(amount){for(var sI=0;sI<this.slices.length;sI++){this.slices[sI].start=(this.slices[sI].start+amount);}
+for(var id in this.counters){this.counters[id].shiftTimestampsForward(amount);}},updateBounds:function(){this.bounds_.reset();if(this.slices.length){this.bounds_.addValue(this.slices[0].start);this.bounds_.addValue(this.slices[this.slices.length-1].end);}
+for(var id in this.counters){this.counters[id].updateBounds();this.bounds_.addRange(this.counters[id].bounds);}
+if(this.samples_&&this.samples_.length){this.bounds_.addValue(this.samples_[0].start);this.bounds_.addValue(this.samples_[this.samples_.length-1].end);}},createSubSlices:function(){this.samples_=this.kernel.model.samples.filter(function(sample){return sample.cpu===this;},this);},addCategoriesToDict:function(categoriesDict){for(var i=0;i<this.slices.length;i++){categoriesDict[this.slices[i].category]=true;}
+for(var id in this.counters){categoriesDict[this.counters[id].category]=true;}
+for(var i=0;i<this.samples_.length;i++){categoriesDict[this.samples_[i].category]=true;}},indexOf:function(cpuSlice){var i=tr.b.math.findLowIndexInSortedArray(this.slices,function(slice){return slice.start;},cpuSlice.start);if(this.slices[i]!==cpuSlice)return undefined;return i;},closeActiveThread:function(endTimestamp,args){if(this.lastActiveThread_===undefined||this.lastActiveThread_===0){return;}
+if(endTimestamp<this.lastActiveTimestamp_){throw new Error('The end timestamp of a thread running on CPU '+
+this.cpuNumber+' is before its start timestamp.');}
+for(var key in args){this.lastActiveArgs_[key]=args[key];}
+var duration=endTimestamp-this.lastActiveTimestamp_;var slice=new tr.model.CpuSlice('',this.lastActiveName_,ColorScheme.getColorIdForGeneralPurposeString(this.lastActiveName_),this.lastActiveTimestamp_,this.lastActiveArgs_,duration);slice.cpu=this;this.slices.push(slice);this.lastActiveTimestamp_=undefined;this.lastActiveThread_=undefined;this.lastActiveName_=undefined;this.lastActiveArgs_=undefined;},switchActiveThread:function(timestamp,oldThreadArgs,newThreadId,newThreadName,newThreadArgs){this.closeActiveThread(timestamp,oldThreadArgs);this.lastActiveTimestamp_=timestamp;this.lastActiveThread_=newThreadId;this.lastActiveName_=newThreadName;this.lastActiveArgs_=newThreadArgs;},getFreqStatsForRange:function(range){var stats={};function addStatsForFreq(freqSample,index){var freqEnd=(index<freqSample.series_.length-1)?freqSample.series_.samples_[index+1].timestamp:range.max;var freqRange=tr.b.math.Range.fromExplicitRange(freqSample.timestamp,freqEnd);var intersection=freqRange.findIntersection(range);if(!(freqSample.value in stats)){stats[freqSample.value]=0;}
+stats[freqSample.value]+=intersection.duration;}
+var freqCounter=this.getCounter('','Clock Frequency');if(freqCounter!==undefined){var freqSeries=freqCounter.getSeries(0);if(!freqSeries)return;tr.b.math.iterateOverIntersectingIntervals(freqSeries.samples_,function(x){return x.timestamp;},function(x,index){if(index<freqSeries.length-1){return freqSeries.samples_[index+1].timestamp;}
+return range.max;},range.min,range.max,addStatsForFreq);}
+return stats;}};Cpu.compare=function(x,y){return x.cpuNumber-y.cpuNumber;};return{Cpu,};});'use strict';tr.exportTo('tr.model',function(){var Event=tr.model.Event;var EventRegistry=tr.model.EventRegistry;function PowerSample(series,start,powerInW){Event.call(this);this.series_=series;this.start_=parseFloat(start);this.powerInW_=parseFloat(powerInW);}
+PowerSample.prototype={__proto__:Event.prototype,get series(){return this.series_;},get start(){return this.start_;},set start(value){this.start_=value;},get powerInW(){return this.powerInW_;},set powerInW(value){this.powerInW_=value;},addBoundsToRange:function(range){range.addValue(this.start);}};EventRegistry.register(PowerSample,{name:'powerSample',pluralName:'powerSamples'});return{PowerSample,};});'use strict';tr.exportTo('tr.model',function(){var PowerSample=tr.model.PowerSample;function PowerSeries(device){tr.model.EventContainer.call(this);this.device_=device;this.samples_=[];}
+PowerSeries.prototype={__proto__:tr.model.EventContainer.prototype,get device(){return this.device_;},get samples(){return this.samples_;},get stableId(){return this.device_.stableId+'.PowerSeries';},addPowerSample:function(ts,val){var sample=new PowerSample(this,ts,val);this.samples_.push(sample);return sample;},getEnergyConsumedInJ:function(start,end){var measurementRange=tr.b.math.Range.fromExplicitRange(start,end);var energyConsumedInJ=0;var startIndex=tr.b.math.findLowIndexInSortedArray(this.samples,x=>x.start,start)-1;var endIndex=tr.b.math.findLowIndexInSortedArray(this.samples,x=>x.start,end);if(startIndex<0){startIndex=0;}
+for(var i=startIndex;i<endIndex;i++){var sample=this.samples[i];var nextSample=this.samples[i+1];var sampleRange=new tr.b.math.Range();sampleRange.addValue(sample.start);sampleRange.addValue(nextSample?nextSample.start:sample.start);var intersectionRangeInMs=measurementRange.findIntersection(sampleRange);var durationInS=tr.b.convertUnit(intersectionRangeInMs.duration,tr.b.UnitPrefixScale.METRIC.MILLI,tr.b.UnitPrefixScale.METRIC.NONE);energyConsumedInJ+=durationInS*sample.powerInW;}
+return energyConsumedInJ;},getSamplesWithinRange:function(start,end){var startIndex=tr.b.math.findLowIndexInSortedArray(this.samples,x=>x.start,start);var endIndex=tr.b.math.findLowIndexInSortedArray(this.samples,x=>x.start,end);return this.samples.slice(startIndex,endIndex);},shiftTimestampsForward:function(amount){for(var i=0;i<this.samples_.length;++i){this.samples_[i].start+=amount;}},updateBounds:function(){this.bounds.reset();if(this.samples_.length===0)return;this.bounds.addValue(this.samples_[0].start);this.bounds.addValue(this.samples_[this.samples_.length-1].start);},childEvents:function*(){yield*this.samples_;},};return{PowerSeries,};});'use strict';tr.exportTo('tr.model',function(){function Device(model){if(!model){throw new Error('Must provide a model.');}
+tr.model.EventContainer.call(this);this.powerSeries_=undefined;this.cpuUsageSeries_=undefined;this.vSyncTimestamps_=[];}
+Device.compare=function(x,y){return x.guid-y.guid;};Device.prototype={__proto__:tr.model.EventContainer.prototype,compareTo:function(that){return Device.compare(this,that);},get userFriendlyName(){return'Device';},get userFriendlyDetails(){return'Device';},get stableId(){return'Device';},getSettingsKey:function(){return'device';},get powerSeries(){return this.powerSeries_;},set powerSeries(powerSeries){this.powerSeries_=powerSeries;},get cpuUsageSeries(){return this.cpuUsageSeries_;},set cpuUsageSeries(cpuUsageSeries){this.cpuUsageSeries_=cpuUsageSeries;},get vSyncTimestamps(){return this.vSyncTimestamps_;},set vSyncTimestamps(value){this.vSyncTimestamps_=value;},updateBounds:function(){this.bounds.reset();for(var child of this.childEventContainers()){child.updateBounds();this.bounds.addRange(child.bounds);}},shiftTimestampsForward:function(amount){for(var child of this.childEventContainers()){child.shiftTimestampsForward(amount);}
+for(var i=0;i<this.vSyncTimestamps_.length;i++){this.vSyncTimestamps_[i]+=amount;}},addCategoriesToDict:function(categoriesDict){},childEventContainers:function*(){if(this.powerSeries_){yield this.powerSeries_;}
+if(this.cpuUsageSeries_){yield this.cpuUsageSeries_;}}};return{Device,};});'use strict';tr.exportTo('tr.model',function(){function FlowEvent(category,id,title,colorId,start,args,opt_duration){tr.model.TimedEvent.call(this,start);this.category=category||'';this.title=title;this.colorId=colorId;this.start=start;this.args=args;this.id=id;this.startSlice=undefined;this.endSlice=undefined;this.startStackFrame=undefined;this.endStackFrame=undefined;if(opt_duration!==undefined){this.duration=opt_duration;}}
 FlowEvent.prototype={__proto__:tr.model.TimedEvent.prototype,get userFriendlyName(){return'Flow event named '+this.title+' at '+
-tr.b.Unit.byName.timeStampInMs.format(this.timestamp);}};tr.model.EventRegistry.register(FlowEvent,{name:'flowEvent',pluralName:'flowEvents'});return{FlowEvent:FlowEvent};});'use strict';tr.exportTo('tr.model',function(){function ContainerMemoryDump(start){tr.model.TimedEvent.call(this,start);this.levelOfDetail=undefined;this.memoryAllocatorDumps_=undefined;this.memoryAllocatorDumpsByFullName_=undefined;};ContainerMemoryDump.LevelOfDetail={BACKGROUND:0,LIGHT:1,DETAILED:2};ContainerMemoryDump.prototype={__proto__:tr.model.TimedEvent.prototype,shiftTimestampsForward:function(amount){this.start+=amount;},get memoryAllocatorDumps(){return this.memoryAllocatorDumps_;},set memoryAllocatorDumps(memoryAllocatorDumps){this.memoryAllocatorDumps_=memoryAllocatorDumps;this.forceRebuildingMemoryAllocatorDumpByFullNameIndex();},getMemoryAllocatorDumpByFullName:function(fullName){if(this.memoryAllocatorDumps_===undefined)
-return undefined;if(this.memoryAllocatorDumpsByFullName_===undefined){var index={};function addDumpsToIndex(dumps){dumps.forEach(function(dump){index[dump.fullName]=dump;addDumpsToIndex(dump.children);});};addDumpsToIndex(this.memoryAllocatorDumps_);this.memoryAllocatorDumpsByFullName_=index;}
-return this.memoryAllocatorDumpsByFullName_[fullName];},forceRebuildingMemoryAllocatorDumpByFullNameIndex:function(){this.memoryAllocatorDumpsByFullName_=undefined;},iterateRootAllocatorDumps:function(fn,opt_this){if(this.memoryAllocatorDumps===undefined)
-return;this.memoryAllocatorDumps.forEach(fn,opt_this||this);}};return{ContainerMemoryDump:ContainerMemoryDump};});'use strict';tr.exportTo('tr.model',function(){function MemoryAllocatorDump(containerMemoryDump,fullName,opt_guid){this.fullName=fullName;this.parent=undefined;this.children=[];this.numerics={};this.diagnostics={};this.containerMemoryDump=containerMemoryDump;this.owns=undefined;this.ownedBy=[];this.ownedBySiblingSizes=new Map();this.retains=[];this.retainedBy=[];this.weak=false;this.infos=[];this.guid=opt_guid;}
+tr.b.Unit.byName.timeStampInMs.format(this.timestamp);}};tr.model.EventRegistry.register(FlowEvent,{name:'flowEvent',pluralName:'flowEvents'});return{FlowEvent,};});'use strict';tr.exportTo('tr.model',function(){function ContainerMemoryDump(start){tr.model.TimedEvent.call(this,start);this.levelOfDetail=undefined;this.memoryAllocatorDumps_=undefined;this.memoryAllocatorDumpsByFullName_=undefined;}
+ContainerMemoryDump.LevelOfDetail={BACKGROUND:0,LIGHT:1,DETAILED:2};ContainerMemoryDump.prototype={__proto__:tr.model.TimedEvent.prototype,shiftTimestampsForward:function(amount){this.start+=amount;},get memoryAllocatorDumps(){return this.memoryAllocatorDumps_;},set memoryAllocatorDumps(memoryAllocatorDumps){this.memoryAllocatorDumps_=memoryAllocatorDumps;this.forceRebuildingMemoryAllocatorDumpByFullNameIndex();},getMemoryAllocatorDumpByFullName:function(fullName){if(this.memoryAllocatorDumps_===undefined)return undefined;if(this.memoryAllocatorDumpsByFullName_===undefined){var index={};function addDumpsToIndex(dumps){dumps.forEach(function(dump){index[dump.fullName]=dump;addDumpsToIndex(dump.children);});}
+addDumpsToIndex(this.memoryAllocatorDumps_);this.memoryAllocatorDumpsByFullName_=index;}
+return this.memoryAllocatorDumpsByFullName_[fullName];},forceRebuildingMemoryAllocatorDumpByFullNameIndex:function(){this.memoryAllocatorDumpsByFullName_=undefined;},iterateRootAllocatorDumps:function(fn,opt_this){if(this.memoryAllocatorDumps===undefined)return;this.memoryAllocatorDumps.forEach(fn,opt_this||this);}};return{ContainerMemoryDump,};});'use strict';tr.exportTo('tr.model',function(){function MemoryAllocatorDump(containerMemoryDump,fullName,opt_guid){this.fullName=fullName;this.parent=undefined;this.children=[];this.numerics={};this.diagnostics={};this.containerMemoryDump=containerMemoryDump;this.owns=undefined;this.ownedBy=[];this.ownedBySiblingSizes=new Map();this.retains=[];this.retainedBy=[];this.weak=false;this.infos=[];this.guid=opt_guid;}
 MemoryAllocatorDump.SIZE_NUMERIC_NAME='size';MemoryAllocatorDump.EFFECTIVE_SIZE_NUMERIC_NAME='effective_size';MemoryAllocatorDump.RESIDENT_SIZE_NUMERIC_NAME='resident_size';MemoryAllocatorDump.DISPLAYED_SIZE_NUMERIC_NAME=MemoryAllocatorDump.EFFECTIVE_SIZE_NUMERIC_NAME;MemoryAllocatorDump.prototype={get name(){return this.fullName.substring(this.fullName.lastIndexOf('/')+1);},get quantifiedName(){return'\''+this.fullName+'\' in '+
-this.containerMemoryDump.containerName;},getDescendantDumpByFullName:function(fullName){return this.containerMemoryDump.getMemoryAllocatorDumpByFullName(this.fullName+'/'+fullName);},isDescendantOf:function(otherDump){var dump=this;while(dump!==undefined){if(dump===otherDump)
-return true;dump=dump.parent;}
-return false;},addNumeric:function(name,numeric){if(!(numeric instanceof tr.v.ScalarNumeric))
-throw new Error('Numeric value must be an instance of ScalarNumeric.');if(name in this.numerics)
-throw new Error('Duplicate numeric name: '+name+'.');this.numerics[name]=numeric;},addDiagnostic:function(name,text){if(typeof text!=='string')
-throw new Error('Diagnostic text must be a string.');if(name in this.diagnostics)
-throw new Error('Duplicate diagnostic name: '+name+'.');this.diagnostics[name]=text;},aggregateNumericsRecursively:function(opt_model){var numericNames=new Set();this.children.forEach(function(child){child.aggregateNumericsRecursively(opt_model);tr.b.iterItems(child.numerics,numericNames.add,numericNames);},this);numericNames.forEach(function(numericName){if(numericName===MemoryAllocatorDump.SIZE_NUMERIC_NAME||numericName===MemoryAllocatorDump.EFFECTIVE_SIZE_NUMERIC_NAME||this.numerics[numericName]!==undefined){return;}
-this.numerics[numericName]=MemoryAllocatorDump.aggregateNumerics(this.children.map(function(child){return child.numerics[numericName];}),opt_model);},this);}};MemoryAllocatorDump.aggregateNumerics=function(numerics,opt_model){var shouldLogWarning=!!opt_model;var aggregatedUnit=undefined;var aggregatedValue=0;numerics.forEach(function(numeric){if(numeric===undefined)
-return;var unit=numeric.unit;if(aggregatedUnit===undefined){aggregatedUnit=unit;}else if(aggregatedUnit!==unit){if(shouldLogWarning){opt_model.importWarning({type:'numeric_parse_error',message:'Multiple units provided for numeric: \''+
+this.containerMemoryDump.containerName;},getDescendantDumpByFullName:function(fullName){return this.containerMemoryDump.getMemoryAllocatorDumpByFullName(this.fullName+'/'+fullName);},isDescendantOf:function(otherDump){if(this===otherDump)return true;if(this.parent===undefined)return false;return this.parent.isDescendantOf(otherDump);},addNumeric:function(name,numeric){if(!(numeric instanceof tr.b.Scalar)){throw new Error('Numeric value must be an instance of Scalar.');}
+if(name in this.numerics){throw new Error('Duplicate numeric name: '+name+'.');}
+this.numerics[name]=numeric;},addDiagnostic:function(name,text){if(typeof text!=='string'){throw new Error('Diagnostic text must be a string.');}
+if(name in this.diagnostics){throw new Error('Duplicate diagnostic name: '+name+'.');}
+this.diagnostics[name]=text;},aggregateNumericsRecursively:function(opt_model){var numericNames=new Set();this.children.forEach(function(child){child.aggregateNumericsRecursively(opt_model);for(var[item,value]of Object.entries(child.numerics)){numericNames.add(item,value);}},this);numericNames.forEach(function(numericName){if(numericName===MemoryAllocatorDump.SIZE_NUMERIC_NAME||numericName===MemoryAllocatorDump.EFFECTIVE_SIZE_NUMERIC_NAME||this.numerics[numericName]!==undefined){return;}
+this.numerics[numericName]=MemoryAllocatorDump.aggregateNumerics(this.children.map(function(child){return child.numerics[numericName];}),opt_model);},this);}};MemoryAllocatorDump.aggregateNumerics=function(numerics,opt_model){var shouldLogWarning=!!opt_model;var aggregatedUnit=undefined;var aggregatedValue=0;numerics.forEach(function(numeric){if(numeric===undefined)return;var unit=numeric.unit;if(aggregatedUnit===undefined){aggregatedUnit=unit;}else if(aggregatedUnit!==unit){if(shouldLogWarning){opt_model.importWarning({type:'numeric_parse_error',message:'Multiple units provided for numeric: \''+
 aggregatedUnit.unitName+'\' and \''+unit.unitName+'\'.'});shouldLogWarning=false;}
 aggregatedUnit=tr.b.Unit.byName.unitlessNumber_smallerIsBetter;}
-aggregatedValue+=numeric.value;},this);if(aggregatedUnit===undefined)
-return undefined;return new tr.v.ScalarNumeric(aggregatedUnit,aggregatedValue);};function MemoryAllocatorDumpLink(source,target,opt_importance){this.source=source;this.target=target;this.importance=opt_importance;this.size=undefined;}
-var MemoryAllocatorDumpInfoType={PROVIDED_SIZE_LESS_THAN_AGGREGATED_CHILDREN:0,PROVIDED_SIZE_LESS_THAN_LARGEST_OWNER:1};return{MemoryAllocatorDump:MemoryAllocatorDump,MemoryAllocatorDumpLink:MemoryAllocatorDumpLink,MemoryAllocatorDumpInfoType:MemoryAllocatorDumpInfoType};});'use strict';tr.exportTo('tr.model',function(){function GlobalMemoryDump(model,start){tr.model.ContainerMemoryDump.call(this,start);this.model=model;this.processMemoryDumps={};}
-var SIZE_NUMERIC_NAME=tr.model.MemoryAllocatorDump.SIZE_NUMERIC_NAME;var EFFECTIVE_SIZE_NUMERIC_NAME=tr.model.MemoryAllocatorDump.EFFECTIVE_SIZE_NUMERIC_NAME;var MemoryAllocatorDumpInfoType=tr.model.MemoryAllocatorDumpInfoType;var PROVIDED_SIZE_LESS_THAN_AGGREGATED_CHILDREN=MemoryAllocatorDumpInfoType.PROVIDED_SIZE_LESS_THAN_AGGREGATED_CHILDREN;var PROVIDED_SIZE_LESS_THAN_LARGEST_OWNER=MemoryAllocatorDumpInfoType.PROVIDED_SIZE_LESS_THAN_LARGEST_OWNER;function inPlaceFilter(array,predicate,opt_this){opt_this=opt_this||this;var nextPosition=0;for(var i=0;i<array.length;i++){if(!predicate.call(opt_this,array[i],i))
-continue;if(nextPosition<i)
-array[nextPosition]=array[i];nextPosition++;}
-if(nextPosition<array.length)
-array.length=nextPosition;}
-function getSize(dump){var numeric=dump.numerics[SIZE_NUMERIC_NAME];if(numeric===undefined)
-return 0;return numeric.value;}
+aggregatedValue+=numeric.value;},this);if(aggregatedUnit===undefined)return undefined;return new tr.b.Scalar(aggregatedUnit,aggregatedValue);};function MemoryAllocatorDumpLink(source,target,opt_importance){this.source=source;this.target=target;this.importance=opt_importance;this.size=undefined;}
+var MemoryAllocatorDumpInfoType={PROVIDED_SIZE_LESS_THAN_AGGREGATED_CHILDREN:0,PROVIDED_SIZE_LESS_THAN_LARGEST_OWNER:1};return{MemoryAllocatorDump,MemoryAllocatorDumpLink,MemoryAllocatorDumpInfoType,};});'use strict';tr.exportTo('tr.model',function(){function GlobalMemoryDump(model,start){tr.model.ContainerMemoryDump.call(this,start);this.model=model;this.processMemoryDumps={};}
+var SIZE_NUMERIC_NAME=tr.model.MemoryAllocatorDump.SIZE_NUMERIC_NAME;var EFFECTIVE_SIZE_NUMERIC_NAME=tr.model.MemoryAllocatorDump.EFFECTIVE_SIZE_NUMERIC_NAME;var MemoryAllocatorDumpInfoType=tr.model.MemoryAllocatorDumpInfoType;var PROVIDED_SIZE_LESS_THAN_AGGREGATED_CHILDREN=MemoryAllocatorDumpInfoType.PROVIDED_SIZE_LESS_THAN_AGGREGATED_CHILDREN;var PROVIDED_SIZE_LESS_THAN_LARGEST_OWNER=MemoryAllocatorDumpInfoType.PROVIDED_SIZE_LESS_THAN_LARGEST_OWNER;function inPlaceFilter(array,predicate,opt_this){opt_this=opt_this||this;var nextPosition=0;for(var i=0;i<array.length;i++){if(!predicate.call(opt_this,array[i],i))continue;if(nextPosition<i){array[nextPosition]=array[i];}
+nextPosition++;}
+if(nextPosition<array.length){array.length=nextPosition;}}
+function getSize(dump){var numeric=dump.numerics[SIZE_NUMERIC_NAME];if(numeric===undefined)return 0;return numeric.value;}
 function hasSize(dump){return dump.numerics[SIZE_NUMERIC_NAME]!==undefined;}
-function optional(value,defaultValue){if(value===undefined)
-return defaultValue;return value;}
+function optional(value,defaultValue){if(value===undefined)return defaultValue;return value;}
 GlobalMemoryDump.prototype={__proto__:tr.model.ContainerMemoryDump.prototype,get userFriendlyName(){return'Global memory dump at '+
-tr.b.Unit.byName.timeStampInMs.format(this.start);},get containerName(){return'global space';},finalizeGraph:function(){this.removeWeakDumps();this.setUpTracingOverheadOwnership();this.aggregateNumerics();this.calculateSizes();this.calculateEffectiveSizes();this.discountTracingOverheadFromVmRegions();this.forceRebuildingMemoryAllocatorDumpByFullNameIndices();},removeWeakDumps:function(){this.traverseAllocatorDumpsInDepthFirstPreOrder(function(dump){if(dump.weak)
-return;if((dump.owns!==undefined&&dump.owns.target.weak)||(dump.parent!==undefined&&dump.parent.weak)){dump.weak=true;}});function removeWeakDumpsFromListRecursively(dumps){inPlaceFilter(dumps,function(dump){if(dump.weak){return false;}
+tr.b.Unit.byName.timeStampInMs.format(this.start);},get containerName(){return'global space';},finalizeGraph:function(){this.removeWeakDumps();this.setUpTracingOverheadOwnership();this.aggregateNumerics();this.calculateSizes();this.calculateEffectiveSizes();this.discountTracingOverheadFromVmRegions();this.forceRebuildingMemoryAllocatorDumpByFullNameIndices();},removeWeakDumps:function(){this.traverseAllocatorDumpsInDepthFirstPreOrder(function(dump){if(dump.weak)return;if((dump.owns!==undefined&&dump.owns.target.weak)||(dump.parent!==undefined&&dump.parent.weak)){dump.weak=true;}});function removeWeakDumpsFromListRecursively(dumps){inPlaceFilter(dumps,function(dump){if(dump.weak){return false;}
 removeWeakDumpsFromListRecursively(dump.children);inPlaceFilter(dump.ownedBy,function(ownershipLink){return!ownershipLink.source.weak;});return true;});}
-this.iterateContainerDumps(function(containerDump){var memoryAllocatorDumps=containerDump.memoryAllocatorDumps;if(memoryAllocatorDumps!==undefined)
-removeWeakDumpsFromListRecursively(memoryAllocatorDumps);});},calculateSizes:function(){this.traverseAllocatorDumpsInDepthFirstPostOrder(this.calculateMemoryAllocatorDumpSize_.bind(this));},calculateMemoryAllocatorDumpSize_:function(dump){var shouldDefineSize=false;function getDependencySize(dependencyDump){var numeric=dependencyDump.numerics[SIZE_NUMERIC_NAME];if(numeric===undefined)
-return 0;shouldDefineSize=true;return numeric.value;}
+this.iterateContainerDumps(function(containerDump){var memoryAllocatorDumps=containerDump.memoryAllocatorDumps;if(memoryAllocatorDumps!==undefined){removeWeakDumpsFromListRecursively(memoryAllocatorDumps);}});},calculateSizes:function(){this.traverseAllocatorDumpsInDepthFirstPostOrder(this.calculateMemoryAllocatorDumpSize_.bind(this));},calculateMemoryAllocatorDumpSize_:function(dump){var shouldDefineSize=false;function getDependencySize(dependencyDump){var numeric=dependencyDump.numerics[SIZE_NUMERIC_NAME];if(numeric===undefined)return 0;shouldDefineSize=true;return numeric.value;}
 var sizeNumeric=dump.numerics[SIZE_NUMERIC_NAME];var size=0;var checkDependencySizeIsConsistent=function(){};if(sizeNumeric!==undefined){size=sizeNumeric.value;shouldDefineSize=true;if(sizeNumeric.unit!==tr.b.Unit.byName.sizeInBytes_smallerIsBetter){this.model.importWarning({type:'memory_dump_parse_error',message:'Invalid unit of \'size\' numeric of memory allocator '+'dump '+dump.quantifiedName+': '+
 sizeNumeric.unit.unitName+'.'});}
-checkDependencySizeIsConsistent=function(dependencySize,dependencyInfoType,dependencyName){if(size>=dependencySize)
-return;this.model.importWarning({type:'memory_dump_parse_error',message:'Size provided by memory allocator dump \''+
+checkDependencySizeIsConsistent=function(dependencySize,dependencyInfoType,dependencyName){if(size>=dependencySize)return;this.model.importWarning({type:'memory_dump_parse_error',message:'Size provided by memory allocator dump \''+
 dump.fullName+'\''+
 tr.b.Unit.byName.sizeInBytes.format(size)+') is less than '+dependencyName+' ('+
 tr.b.Unit.byName.sizeInBytes.format(dependencySize)+').'});dump.infos.push({type:dependencyInfoType,providedSize:size,dependencySize:dependencySize});}.bind(this);}
-var aggregatedChildrenSize=0;var allOverlaps={};dump.children.forEach(function(childDump){function aggregateDescendantDump(descendantDump){var ownedDumpLink=descendantDump.owns;if(ownedDumpLink!==undefined&&ownedDumpLink.target.isDescendantOf(dump)){var ownedChildDump=ownedDumpLink.target;while(ownedChildDump.parent!==dump)
-ownedChildDump=ownedChildDump.parent;if(childDump!==ownedChildDump){var ownedBySiblingSize=getDependencySize(descendantDump);if(ownedBySiblingSize>0){var previousTotalOwnedBySiblingSize=ownedChildDump.ownedBySiblingSizes.get(childDump)||0;var updatedTotalOwnedBySiblingSize=previousTotalOwnedBySiblingSize+ownedBySiblingSize;ownedChildDump.ownedBySiblingSizes.set(childDump,updatedTotalOwnedBySiblingSize);}}
+var aggregatedChildrenSize=0;var allOverlaps={};dump.children.forEach(function(childDump){function aggregateDescendantDump(descendantDump){var ownedDumpLink=descendantDump.owns;if(ownedDumpLink!==undefined&&ownedDumpLink.target.isDescendantOf(dump)){var ownedChildDump=ownedDumpLink.target;while(ownedChildDump.parent!==dump){ownedChildDump=ownedChildDump.parent;}
+if(childDump!==ownedChildDump){var ownedBySiblingSize=getDependencySize(descendantDump);if(ownedBySiblingSize>0){var previousTotalOwnedBySiblingSize=ownedChildDump.ownedBySiblingSizes.get(childDump)||0;var updatedTotalOwnedBySiblingSize=previousTotalOwnedBySiblingSize+ownedBySiblingSize;ownedChildDump.ownedBySiblingSizes.set(childDump,updatedTotalOwnedBySiblingSize);}}
 return;}
 if(descendantDump.children.length===0){aggregatedChildrenSize+=getDependencySize(descendantDump);return;}
 descendantDump.children.forEach(aggregateDescendantDump);}
 aggregateDescendantDump(childDump);});checkDependencySizeIsConsistent(aggregatedChildrenSize,PROVIDED_SIZE_LESS_THAN_AGGREGATED_CHILDREN,'the aggregated size of its children');var largestOwnerSize=0;dump.ownedBy.forEach(function(ownershipLink){var owner=ownershipLink.source;var ownerSize=getDependencySize(owner);largestOwnerSize=Math.max(largestOwnerSize,ownerSize);});checkDependencySizeIsConsistent(largestOwnerSize,PROVIDED_SIZE_LESS_THAN_LARGEST_OWNER,'the size of its largest owner');if(!shouldDefineSize){delete dump.numerics[SIZE_NUMERIC_NAME];return;}
-size=Math.max(size,aggregatedChildrenSize,largestOwnerSize);dump.numerics[SIZE_NUMERIC_NAME]=new tr.v.ScalarNumeric(tr.b.Unit.byName.sizeInBytes_smallerIsBetter,size);if(aggregatedChildrenSize<size&&dump.children!==undefined&&dump.children.length>0){var virtualChild=new tr.model.MemoryAllocatorDump(dump.containerMemoryDump,dump.fullName+'/<unspecified>');virtualChild.parent=dump;dump.children.unshift(virtualChild);virtualChild.numerics[SIZE_NUMERIC_NAME]=new tr.v.ScalarNumeric(tr.b.Unit.byName.sizeInBytes_smallerIsBetter,size-aggregatedChildrenSize);}},calculateEffectiveSizes:function(){this.traverseAllocatorDumpsInDepthFirstPostOrder(this.calculateDumpSubSizes_.bind(this));this.traverseAllocatorDumpsInDepthFirstPostOrder(this.calculateDumpOwnershipCoefficient_.bind(this));this.traverseAllocatorDumpsInDepthFirstPreOrder(this.calculateDumpCumulativeOwnershipCoefficient_.bind(this));this.traverseAllocatorDumpsInDepthFirstPostOrder(this.calculateDumpEffectiveSize_.bind(this));},calculateDumpSubSizes_:function(dump){if(!hasSize(dump))
-return;if(dump.children===undefined||dump.children.length===0){var size=getSize(dump);dump.notOwningSubSize_=size;dump.notOwnedSubSize_=size;return;}
-var notOwningSubSize=0;dump.children.forEach(function(childDump){if(childDump.owns!==undefined)
-return;notOwningSubSize+=optional(childDump.notOwningSubSize_,0);});dump.notOwningSubSize_=notOwningSubSize;var notOwnedSubSize=0;dump.children.forEach(function(childDump){if(childDump.ownedBy.length===0){notOwnedSubSize+=optional(childDump.notOwnedSubSize_,0);return;}
-var largestChildOwnerSize=0;childDump.ownedBy.forEach(function(ownershipLink){largestChildOwnerSize=Math.max(largestChildOwnerSize,getSize(ownershipLink.source));});notOwnedSubSize+=getSize(childDump)-largestChildOwnerSize;});dump.notOwnedSubSize_=notOwnedSubSize;},calculateDumpOwnershipCoefficient_:function(dump){if(!hasSize(dump))
-return;if(dump.ownedBy.length===0)
-return;var owners=dump.ownedBy.map(function(ownershipLink){return{dump:ownershipLink.source,importance:optional(ownershipLink.importance,0),notOwningSubSize:optional(ownershipLink.source.notOwningSubSize_,0)};});owners.sort(function(a,b){if(a.importance===b.importance)
-return a.notOwningSubSize-b.notOwningSubSize;return b.importance-a.importance;});var currentImportanceStartPos=0;var alreadyAttributedSubSize=0;while(currentImportanceStartPos<owners.length){var currentImportance=owners[currentImportanceStartPos].importance;var nextImportanceStartPos=currentImportanceStartPos+1;while(nextImportanceStartPos<owners.length&&owners[nextImportanceStartPos].importance===currentImportance){nextImportanceStartPos++;}
+size=Math.max(size,aggregatedChildrenSize,largestOwnerSize);dump.numerics[SIZE_NUMERIC_NAME]=new tr.b.Scalar(tr.b.Unit.byName.sizeInBytes_smallerIsBetter,size);if(aggregatedChildrenSize<size&&dump.children!==undefined&&dump.children.length>0){var virtualChild=new tr.model.MemoryAllocatorDump(dump.containerMemoryDump,dump.fullName+'/<unspecified>');virtualChild.parent=dump;dump.children.unshift(virtualChild);virtualChild.numerics[SIZE_NUMERIC_NAME]=new tr.b.Scalar(tr.b.Unit.byName.sizeInBytes_smallerIsBetter,size-aggregatedChildrenSize);}},calculateEffectiveSizes:function(){this.traverseAllocatorDumpsInDepthFirstPostOrder(this.calculateDumpSubSizes_.bind(this));this.traverseAllocatorDumpsInDepthFirstPostOrder(this.calculateDumpOwnershipCoefficient_.bind(this));this.traverseAllocatorDumpsInDepthFirstPreOrder(this.calculateDumpCumulativeOwnershipCoefficient_.bind(this));this.traverseAllocatorDumpsInDepthFirstPostOrder(this.calculateDumpEffectiveSize_.bind(this));},calculateDumpSubSizes_:function(dump){if(!hasSize(dump))return;if(dump.children===undefined||dump.children.length===0){var size=getSize(dump);dump.notOwningSubSize_=size;dump.notOwnedSubSize_=size;return;}
+var notOwningSubSize=0;dump.children.forEach(function(childDump){if(childDump.owns!==undefined)return;notOwningSubSize+=optional(childDump.notOwningSubSize_,0);});dump.notOwningSubSize_=notOwningSubSize;var notOwnedSubSize=0;dump.children.forEach(function(childDump){if(childDump.ownedBy.length===0){notOwnedSubSize+=optional(childDump.notOwnedSubSize_,0);return;}
+var largestChildOwnerSize=0;childDump.ownedBy.forEach(function(ownershipLink){largestChildOwnerSize=Math.max(largestChildOwnerSize,getSize(ownershipLink.source));});notOwnedSubSize+=getSize(childDump)-largestChildOwnerSize;});dump.notOwnedSubSize_=notOwnedSubSize;},calculateDumpOwnershipCoefficient_:function(dump){if(!hasSize(dump))return;if(dump.ownedBy.length===0)return;var owners=dump.ownedBy.map(function(ownershipLink){return{dump:ownershipLink.source,importance:optional(ownershipLink.importance,0),notOwningSubSize:optional(ownershipLink.source.notOwningSubSize_,0)};});owners.sort(function(a,b){if(a.importance===b.importance){return a.notOwningSubSize-b.notOwningSubSize;}
+return b.importance-a.importance;});var currentImportanceStartPos=0;var alreadyAttributedSubSize=0;while(currentImportanceStartPos<owners.length){var currentImportance=owners[currentImportanceStartPos].importance;var nextImportanceStartPos=currentImportanceStartPos+1;while(nextImportanceStartPos<owners.length&&owners[nextImportanceStartPos].importance===currentImportance){nextImportanceStartPos++;}
 var attributedNotOwningSubSize=0;for(var pos=currentImportanceStartPos;pos<nextImportanceStartPos;pos++){var owner=owners[pos];var notOwningSubSize=owner.notOwningSubSize;if(notOwningSubSize>alreadyAttributedSubSize){attributedNotOwningSubSize+=(notOwningSubSize-alreadyAttributedSubSize)/(nextImportanceStartPos-pos);alreadyAttributedSubSize=notOwningSubSize;}
-var owningCoefficient=0;if(notOwningSubSize!==0)
-owningCoefficient=attributedNotOwningSubSize/notOwningSubSize;owner.dump.owningCoefficient_=owningCoefficient;}
+var owningCoefficient=0;if(notOwningSubSize!==0){owningCoefficient=attributedNotOwningSubSize/notOwningSubSize;}
+owner.dump.owningCoefficient_=owningCoefficient;}
 currentImportanceStartPos=nextImportanceStartPos;}
-var notOwnedSubSize=optional(dump.notOwnedSubSize_,0);var remainderSubSize=notOwnedSubSize-alreadyAttributedSubSize;var ownedCoefficient=0;if(notOwnedSubSize!==0)
-ownedCoefficient=remainderSubSize/notOwnedSubSize;dump.ownedCoefficient_=ownedCoefficient;},calculateDumpCumulativeOwnershipCoefficient_:function(dump){if(!hasSize(dump))
-return;var cumulativeOwnedCoefficient=optional(dump.ownedCoefficient_,1);var parent=dump.parent;if(dump.parent!==undefined)
-cumulativeOwnedCoefficient*=dump.parent.cumulativeOwnedCoefficient_;dump.cumulativeOwnedCoefficient_=cumulativeOwnedCoefficient;var cumulativeOwningCoefficient;if(dump.owns!==undefined){cumulativeOwningCoefficient=dump.owningCoefficient_*dump.owns.target.cumulativeOwningCoefficient_;}else if(dump.parent!==undefined){cumulativeOwningCoefficient=dump.parent.cumulativeOwningCoefficient_;}else{cumulativeOwningCoefficient=1;}
+var notOwnedSubSize=optional(dump.notOwnedSubSize_,0);var remainderSubSize=notOwnedSubSize-alreadyAttributedSubSize;var ownedCoefficient=0;if(notOwnedSubSize!==0){ownedCoefficient=remainderSubSize/notOwnedSubSize;}
+dump.ownedCoefficient_=ownedCoefficient;},calculateDumpCumulativeOwnershipCoefficient_:function(dump){if(!hasSize(dump))return;var cumulativeOwnedCoefficient=optional(dump.ownedCoefficient_,1);var parent=dump.parent;if(dump.parent!==undefined){cumulativeOwnedCoefficient*=dump.parent.cumulativeOwnedCoefficient_;}
+dump.cumulativeOwnedCoefficient_=cumulativeOwnedCoefficient;var cumulativeOwningCoefficient;if(dump.owns!==undefined){cumulativeOwningCoefficient=dump.owningCoefficient_*dump.owns.target.cumulativeOwningCoefficient_;}else if(dump.parent!==undefined){cumulativeOwningCoefficient=dump.parent.cumulativeOwningCoefficient_;}else{cumulativeOwningCoefficient=1;}
 dump.cumulativeOwningCoefficient_=cumulativeOwningCoefficient;},calculateDumpEffectiveSize_:function(dump){if(!hasSize(dump)){delete dump.numerics[EFFECTIVE_SIZE_NUMERIC_NAME];return;}
-var effectiveSize;if(dump.children===undefined||dump.children.length===0){effectiveSize=getSize(dump)*dump.cumulativeOwningCoefficient_*dump.cumulativeOwnedCoefficient_;}else{effectiveSize=0;dump.children.forEach(function(childDump){if(!hasSize(childDump))
-return;effectiveSize+=childDump.numerics[EFFECTIVE_SIZE_NUMERIC_NAME].value;});}
-dump.numerics[EFFECTIVE_SIZE_NUMERIC_NAME]=new tr.v.ScalarNumeric(tr.b.Unit.byName.sizeInBytes_smallerIsBetter,effectiveSize);},aggregateNumerics:function(){this.iterateRootAllocatorDumps(function(dump){dump.aggregateNumericsRecursively(this.model);});this.iterateRootAllocatorDumps(this.propagateNumericsAndDiagnosticsRecursively);tr.b.iterItems(this.processMemoryDumps,function(pid,processMemoryDump){processMemoryDump.iterateRootAllocatorDumps(function(dump){dump.aggregateNumericsRecursively(this.model);},this);},this);},propagateNumericsAndDiagnosticsRecursively:function(globalAllocatorDump){['numerics','diagnostics'].forEach(function(field){tr.b.iterItems(globalAllocatorDump[field],function(name,value){globalAllocatorDump.ownedBy.forEach(function(ownershipLink){var processAllocatorDump=ownershipLink.source;if(processAllocatorDump[field][name]!==undefined){return;}
-processAllocatorDump[field][name]=value;});});});globalAllocatorDump.children.forEach(this.propagateNumericsAndDiagnosticsRecursively,this);},setUpTracingOverheadOwnership:function(){tr.b.iterItems(this.processMemoryDumps,function(pid,dump){dump.setUpTracingOverheadOwnership(this.model);},this);},discountTracingOverheadFromVmRegions:function(){tr.b.iterItems(this.processMemoryDumps,function(pid,dump){dump.discountTracingOverheadFromVmRegions(this.model);},this);},forceRebuildingMemoryAllocatorDumpByFullNameIndices:function(){this.iterateContainerDumps(function(containerDump){containerDump.forceRebuildingMemoryAllocatorDumpByFullNameIndex();});},iterateContainerDumps:function(fn){fn.call(this,this);tr.b.iterItems(this.processMemoryDumps,function(pid,processDump){fn.call(this,processDump);},this);},iterateAllRootAllocatorDumps:function(fn){this.iterateContainerDumps(function(containerDump){containerDump.iterateRootAllocatorDumps(fn,this);});},traverseAllocatorDumpsInDepthFirstPostOrder:function(fn){var visitedDumps=new WeakSet();var openDumps=new WeakSet();function visit(dump){if(visitedDumps.has(dump))
-return;if(openDumps.has(dump))
-throw new Error(dump.userFriendlyName+' contains a cycle');openDumps.add(dump);dump.ownedBy.forEach(function(ownershipLink){visit.call(this,ownershipLink.source);},this);dump.children.forEach(visit,this);fn.call(this,dump);visitedDumps.add(dump);openDumps.delete(dump);}
-this.iterateAllRootAllocatorDumps(visit);},traverseAllocatorDumpsInDepthFirstPreOrder:function(fn){var visitedDumps=new WeakSet();function visit(dump){if(visitedDumps.has(dump))
-return;if(dump.owns!==undefined&&!visitedDumps.has(dump.owns.target))
-return;if(dump.parent!==undefined&&!visitedDumps.has(dump.parent))
-return;fn.call(this,dump);visitedDumps.add(dump);dump.ownedBy.forEach(function(ownershipLink){visit.call(this,ownershipLink.source);},this);dump.children.forEach(visit,this);}
-this.iterateAllRootAllocatorDumps(visit);}};tr.model.EventRegistry.register(GlobalMemoryDump,{name:'globalMemoryDump',pluralName:'globalMemoryDumps'});return{GlobalMemoryDump:GlobalMemoryDump};});'use strict';tr.exportTo('tr.model',function(){var InstantEventType={GLOBAL:1,PROCESS:2};function InstantEvent(category,title,colorId,start,args){tr.model.TimedEvent.call(this,start);this.category=category||'';this.title=title;this.colorId=colorId;this.args=args;this.type=undefined;}
+var effectiveSize;if(dump.children===undefined||dump.children.length===0){effectiveSize=getSize(dump)*dump.cumulativeOwningCoefficient_*dump.cumulativeOwnedCoefficient_;}else{effectiveSize=0;dump.children.forEach(function(childDump){if(!hasSize(childDump))return;effectiveSize+=childDump.numerics[EFFECTIVE_SIZE_NUMERIC_NAME].value;});}
+dump.numerics[EFFECTIVE_SIZE_NUMERIC_NAME]=new tr.b.Scalar(tr.b.Unit.byName.sizeInBytes_smallerIsBetter,effectiveSize);},aggregateNumerics:function(){this.iterateRootAllocatorDumps(function(dump){dump.aggregateNumericsRecursively(this.model);});this.iterateRootAllocatorDumps(this.propagateNumericsAndDiagnosticsRecursively);for(var processMemoryDump of Object.values(this.processMemoryDumps)){processMemoryDump.iterateRootAllocatorDumps(function(dump){dump.aggregateNumericsRecursively(this.model);},this);}},propagateNumericsAndDiagnosticsRecursively:function(globalAllocatorDump){['numerics','diagnostics'].forEach(function(field){for(var[name,value]of Object.entries(globalAllocatorDump[field])){globalAllocatorDump.ownedBy.forEach(function(ownershipLink){var processAllocatorDump=ownershipLink.source;if(processAllocatorDump[field][name]!==undefined){return;}
+processAllocatorDump[field][name]=value;});}});globalAllocatorDump.children.forEach(this.propagateNumericsAndDiagnosticsRecursively,this);},setUpTracingOverheadOwnership:function(){for(var dump of Object.values(this.processMemoryDumps)){dump.setUpTracingOverheadOwnership(this.model);}},discountTracingOverheadFromVmRegions:function(){for(var dump of Object.values(this.processMemoryDumps)){dump.discountTracingOverheadFromVmRegions(this.model);}},forceRebuildingMemoryAllocatorDumpByFullNameIndices:function(){this.iterateContainerDumps(function(containerDump){containerDump.forceRebuildingMemoryAllocatorDumpByFullNameIndex();});},iterateContainerDumps:function(fn){fn.call(this,this);for(var processDump of Object.values(this.processMemoryDumps)){fn.call(this,processDump);}},iterateAllRootAllocatorDumps:function(fn){this.iterateContainerDumps(function(containerDump){containerDump.iterateRootAllocatorDumps(fn,this);});},traverseAllocatorDumpsInDepthFirstPostOrder:function(fn){var visitedDumps=new WeakSet();var openDumps=new WeakSet();function visit(dump){if(visitedDumps.has(dump))return;if(openDumps.has(dump)){throw new Error(dump.userFriendlyName+' contains a cycle');}
+openDumps.add(dump);dump.ownedBy.forEach(function(ownershipLink){visit.call(this,ownershipLink.source);},this);dump.children.forEach(visit,this);fn.call(this,dump);visitedDumps.add(dump);openDumps.delete(dump);}
+this.iterateAllRootAllocatorDumps(visit);},traverseAllocatorDumpsInDepthFirstPreOrder:function(fn){var visitedDumps=new WeakSet();function visit(dump){if(visitedDumps.has(dump))return;if(dump.owns!==undefined&&!visitedDumps.has(dump.owns.target)){return;}
+if(dump.parent!==undefined&&!visitedDumps.has(dump.parent)){return;}
+fn.call(this,dump);visitedDumps.add(dump);dump.ownedBy.forEach(function(ownershipLink){visit.call(this,ownershipLink.source);},this);dump.children.forEach(visit,this);}
+this.iterateAllRootAllocatorDumps(visit);}};tr.model.EventRegistry.register(GlobalMemoryDump,{name:'globalMemoryDump',pluralName:'globalMemoryDumps'});return{GlobalMemoryDump,};});'use strict';tr.exportTo('tr.model',function(){var InstantEventType={GLOBAL:1,PROCESS:2};function InstantEvent(category,title,colorId,start,args){tr.model.TimedEvent.call(this,start);this.category=category||'';this.title=title;this.colorId=colorId;this.args=args;this.type=undefined;}
 InstantEvent.prototype={__proto__:tr.model.TimedEvent.prototype};function GlobalInstantEvent(category,title,colorId,start,args){InstantEvent.apply(this,arguments);this.type=InstantEventType.GLOBAL;}
 GlobalInstantEvent.prototype={__proto__:InstantEvent.prototype,get userFriendlyName(){return'Global instant event '+this.title+' @ '+
 tr.b.Unit.byName.timeStampInMs.format(start);}};function ProcessInstantEvent(category,title,colorId,start,args){InstantEvent.apply(this,arguments);this.type=InstantEventType.PROCESS;}
 ProcessInstantEvent.prototype={__proto__:InstantEvent.prototype,get userFriendlyName(){return'Process-level instant event '+this.title+' @ '+
-tr.b.Unit.byName.timeStampInMs.format(start);}};tr.model.EventRegistry.register(InstantEvent,{name:'instantEvent',pluralName:'instantEvents'});return{GlobalInstantEvent:GlobalInstantEvent,ProcessInstantEvent:ProcessInstantEvent,InstantEventType:InstantEventType,InstantEvent:InstantEvent};});'use strict';tr.exportTo('tr.model',function(){function CounterSample(series,timestamp,value){tr.model.Event.call(this);this.series_=series;this.timestamp_=timestamp;this.value_=value;}
-CounterSample.groupByTimestamp=function(samples){var samplesByTimestamp=tr.b.group(samples,function(sample){return sample.timestamp;});var timestamps=tr.b.dictionaryKeys(samplesByTimestamp);timestamps.sort();var groups=[];for(var i=0;i<timestamps.length;i++){var ts=timestamps[i];var group=samplesByTimestamp[ts];group.sort(function(x,y){return x.series.seriesIndex-y.series.seriesIndex;});groups.push(group);}
-return groups;};CounterSample.prototype={__proto__:tr.model.Event.prototype,get series(){return this.series_;},get timestamp(){return this.timestamp_;},get value(){return this.value_;},set timestamp(timestamp){this.timestamp_=timestamp;},addBoundsToRange:function(range){range.addValue(this.timestamp);},getSampleIndex:function(){return tr.b.findLowIndexInSortedArray(this.series.timestamps,function(x){return x;},this.timestamp_);},get userFriendlyName(){return'Counter sample from '+this.series_.title+' at '+
-tr.b.Unit.byName.timeStampInMs.format(this.timestamp);}};tr.model.EventRegistry.register(CounterSample,{name:'counterSample',pluralName:'counterSamples'});return{CounterSample:CounterSample};});'use strict';tr.exportTo('tr.model',function(){var CounterSample=tr.model.CounterSample;function CounterSeries(name,color){tr.model.EventContainer.call(this);this.name_=name;this.color_=color;this.timestamps_=[];this.samples_=[];this.counter=undefined;this.seriesIndex=undefined;}
-CounterSeries.prototype={__proto__:tr.model.EventContainer.prototype,get length(){return this.timestamps_.length;},get name(){return this.name_;},get color(){return this.color_;},get samples(){return this.samples_;},get timestamps(){return this.timestamps_;},getSample:function(idx){return this.samples_[idx];},getTimestamp:function(idx){return this.timestamps_[idx];},addCounterSample:function(ts,val){var sample=new CounterSample(this,ts,val);this.addSample(sample);return sample;},addSample:function(sample){this.timestamps_.push(sample.timestamp);this.samples_.push(sample);},getStatistics:function(sampleIndices){var sum=0;var min=Number.MAX_VALUE;var max=-Number.MAX_VALUE;for(var i=0;i<sampleIndices.length;++i){var sample=this.getSample(sampleIndices[i]).value;sum+=sample;min=Math.min(sample,min);max=Math.max(sample,max);}
-return{min:min,max:max,avg:(sum/sampleIndices.length),start:this.getSample(sampleIndices[0]).value,end:this.getSample(sampleIndices.length-1).value};},shiftTimestampsForward:function(amount){for(var i=0;i<this.timestamps_.length;++i){this.timestamps_[i]+=amount;this.samples_[i].timestamp=this.timestamps_[i];}},childEvents:function*(){yield*this.samples_;},childEventContainers:function*(){}};return{CounterSeries:CounterSeries};});'use strict';tr.exportTo('tr.model',function(){function Counter(parent,id,category,name){tr.model.EventContainer.call(this);this.parent_=parent;this.id_=id;this.category_=category||'';this.name_=name;this.series_=[];this.totals=[];}
-Counter.prototype={__proto__:tr.model.EventContainer.prototype,get parent(){return this.parent_;},get id(){return this.id_;},get category(){return this.category_;},get name(){return this.name_;},childEvents:function*(){},childEventContainers:function*(){yield*this.series;},set timestamps(arg){throw new Error('Bad counter API. No cookie.');},set seriesNames(arg){throw new Error('Bad counter API. No cookie.');},set seriesColors(arg){throw new Error('Bad counter API. No cookie.');},set samples(arg){throw new Error('Bad counter API. No cookie.');},addSeries:function(series){series.counter=this;series.seriesIndex=this.series_.length;this.series_.push(series);return series;},getSeries:function(idx){return this.series_[idx];},get series(){return this.series_;},get numSeries(){return this.series_.length;},get numSamples(){if(this.series_.length===0)
-return 0;return this.series_[0].length;},get timestamps(){if(this.series_.length===0)
-return[];return this.series_[0].timestamps;},getSampleStatistics:function(sampleIndices){sampleIndices.sort();var ret=[];this.series_.forEach(function(series){ret.push(series.getStatistics(sampleIndices));});return ret;},shiftTimestampsForward:function(amount){for(var i=0;i<this.series_.length;++i)
-this.series_[i].shiftTimestampsForward(amount);},updateBounds:function(){this.totals=[];this.maxTotal=0;this.bounds.reset();if(this.series_.length===0)
-return;var firstSeries=this.series_[0];var lastSeries=this.series_[this.series_.length-1];this.bounds.addValue(firstSeries.getTimestamp(0));this.bounds.addValue(lastSeries.getTimestamp(lastSeries.length-1));var numSeries=this.numSeries;this.maxTotal=-Infinity;for(var i=0;i<firstSeries.length;++i){var total=0;this.series_.forEach(function(series){total+=series.getSample(i).value;this.totals.push(total);}.bind(this));this.maxTotal=Math.max(total,this.maxTotal);}}};Counter.compare=function(x,y){var tmp=x.parent.compareTo(y);if(tmp!=0)
-return tmp;var tmp=x.name.localeCompare(y.name);if(tmp==0)
-return x.tid-y.tid;return tmp;};return{Counter:Counter};});'use strict';tr.exportTo('tr.model',function(){var Slice=tr.model.Slice;function CpuSlice(cat,title,colorId,start,args,opt_duration){Slice.apply(this,arguments);this.threadThatWasRunning=undefined;this.cpu=undefined;}
-CpuSlice.prototype={__proto__:Slice.prototype,get analysisTypeName(){return'tr.ui.analysis.CpuSlice';},getAssociatedTimeslice:function(){if(!this.threadThatWasRunning)
-return undefined;var timeSlices=this.threadThatWasRunning.timeSlices;for(var i=0;i<timeSlices.length;i++){var timeSlice=timeSlices[i];if(timeSlice.start!==this.start)
-continue;if(timeSlice.duration!==this.duration)
-continue;return timeSlice;}
-return undefined;}};tr.model.EventRegistry.register(CpuSlice,{name:'cpuSlice',pluralName:'cpuSlices'});return{CpuSlice:CpuSlice};});'use strict';tr.exportTo('tr.model',function(){function TimeToObjectInstanceMap(createObjectInstanceFunction,parent,scopedId){this.createObjectInstanceFunction_=createObjectInstanceFunction;this.parent=parent;this.scopedId=scopedId;this.instances=[];}
-TimeToObjectInstanceMap.prototype={idWasCreated:function(category,name,ts){if(this.instances.length==0){this.instances.push(this.createObjectInstanceFunction_(this.parent,this.scopedId,category,name,ts));this.instances[0].creationTsWasExplicit=true;return this.instances[0];}
-var lastInstance=this.instances[this.instances.length-1];if(ts<lastInstance.deletionTs){throw new Error('Mutation of the TimeToObjectInstanceMap must be '+'done in ascending timestamp order.');}
-lastInstance=this.createObjectInstanceFunction_(this.parent,this.scopedId,category,name,ts);lastInstance.creationTsWasExplicit=true;this.instances.push(lastInstance);return lastInstance;},addSnapshot:function(category,name,ts,args,opt_baseTypeName){if(this.instances.length==0){this.instances.push(this.createObjectInstanceFunction_(this.parent,this.scopedId,category,name,ts,opt_baseTypeName));}
-var i=tr.b.findIndexInSortedIntervals(this.instances,function(inst){return inst.creationTs;},function(inst){return inst.deletionTs-inst.creationTs;},ts);var instance;if(i<0){instance=this.instances[0];if(ts>instance.deletionTs||instance.creationTsWasExplicit){throw new Error('At the provided timestamp, no instance was still alive');}
-if(instance.snapshots.length!=0){throw new Error('Cannot shift creationTs forward, '+'snapshots have been added. First snap was at ts='+
-instance.snapshots[0].ts+' and creationTs was '+
-instance.creationTs);}
-instance.creationTs=ts;}else if(i>=this.instances.length){instance=this.instances[this.instances.length-1];if(ts>=instance.deletionTs){instance=this.createObjectInstanceFunction_(this.parent,this.scopedId,category,name,ts,opt_baseTypeName);this.instances.push(instance);}else{var lastValidIndex;for(var i=this.instances.length-1;i>=0;i--){var tmp=this.instances[i];if(ts>=tmp.deletionTs)
-break;if(tmp.creationTsWasExplicit==false&&tmp.snapshots.length==0)
-lastValidIndex=i;}
-if(lastValidIndex===undefined){throw new Error('Cannot add snapshot. No instance was alive that was mutable.');}
-instance=this.instances[lastValidIndex];instance.creationTs=ts;}}else{instance=this.instances[i];}
-return instance.addSnapshot(ts,args,name,opt_baseTypeName);},get lastInstance(){if(this.instances.length==0)
-return undefined;return this.instances[this.instances.length-1];},idWasDeleted:function(category,name,ts){if(this.instances.length==0){this.instances.push(this.createObjectInstanceFunction_(this.parent,this.scopedId,category,name,ts));}
-var lastInstance=this.instances[this.instances.length-1];if(ts<lastInstance.creationTs)
-throw new Error('Cannot delete an id before it was created');if(lastInstance.deletionTs==Number.MAX_VALUE){lastInstance.wasDeleted(ts);return lastInstance;}
-if(ts<lastInstance.deletionTs)
-throw new Error('id was already deleted earlier.');lastInstance=this.createObjectInstanceFunction_(this.parent,this.scopedId,category,name,ts);this.instances.push(lastInstance);lastInstance.wasDeleted(ts);return lastInstance;},getInstanceAt:function(ts){var i=tr.b.findIndexInSortedIntervals(this.instances,function(inst){return inst.creationTs;},function(inst){return inst.deletionTs-inst.creationTs;},ts);if(i<0){if(this.instances[0].creationTsWasExplicit)
-return undefined;return this.instances[0];}else if(i>=this.instances.length){return undefined;}
-return this.instances[i];},logToConsole:function(){for(var i=0;i<this.instances.length;i++){var instance=this.instances[i];var cEF='';var dEF='';if(instance.creationTsWasExplicit)
-cEF='(explicitC)';if(instance.deletionTsWasExplicit)
-dEF='(explicit)';console.log(instance.creationTs,cEF,instance.deletionTs,dEF,instance.category,instance.name,instance.snapshots.length+' snapshots');}}};return{TimeToObjectInstanceMap:TimeToObjectInstanceMap};});'use strict';tr.exportTo('tr.model',function(){var ObjectInstance=tr.model.ObjectInstance;var ObjectSnapshot=tr.model.ObjectSnapshot;function ObjectCollection(parent){tr.model.EventContainer.call(this);this.parent=parent;this.instanceMapsByScopedId_={};this.instancesByTypeName_={};this.createObjectInstance_=this.createObjectInstance_.bind(this);}
-ObjectCollection.prototype={__proto__:tr.model.EventContainer.prototype,childEvents:function*(){for(var instance of this.getAllObjectInstances()){yield instance;yield*instance.snapshots;}},createObjectInstance_:function(parent,scopedId,category,name,creationTs,opt_baseTypeName){var constructor=tr.model.ObjectInstance.subTypes.getConstructor(category,name);var instance=new constructor(parent,scopedId,category,name,creationTs,opt_baseTypeName);var typeName=instance.typeName;var instancesOfTypeName=this.instancesByTypeName_[typeName];if(!instancesOfTypeName){instancesOfTypeName=[];this.instancesByTypeName_[typeName]=instancesOfTypeName;}
-instancesOfTypeName.push(instance);return instance;},getOrCreateInstanceMap_:function(scopedId){var dict;if(scopedId.scope in this.instanceMapsByScopedId_){dict=this.instanceMapsByScopedId_[scopedId.scope];}else{dict={};this.instanceMapsByScopedId_[scopedId.scope]=dict;}
-var instanceMap=dict[scopedId.id];if(instanceMap)
-return instanceMap;instanceMap=new tr.model.TimeToObjectInstanceMap(this.createObjectInstance_,this.parent,scopedId);dict[scopedId.id]=instanceMap;return instanceMap;},idWasCreated:function(scopedId,category,name,ts){var instanceMap=this.getOrCreateInstanceMap_(scopedId);return instanceMap.idWasCreated(category,name,ts);},addSnapshot:function(scopedId,category,name,ts,args,opt_baseTypeName){var instanceMap=this.getOrCreateInstanceMap_(scopedId);var snapshot=instanceMap.addSnapshot(category,name,ts,args,opt_baseTypeName);if(snapshot.objectInstance.category!=category){var msg='Added snapshot name='+name+' with cat='+category+' impossible. It instance was created/snapshotted with cat='+
-snapshot.objectInstance.category+' name='+
-snapshot.objectInstance.name;throw new Error(msg);}
-if(opt_baseTypeName&&snapshot.objectInstance.baseTypeName!=opt_baseTypeName){throw new Error('Could not add snapshot with baseTypeName='+
-opt_baseTypeName+'. It '+'was previously created with name='+
-snapshot.objectInstance.baseTypeName);}
-if(snapshot.objectInstance.name!=name){throw new Error('Could not add snapshot with name='+name+'. It '+'was previously created with name='+
-snapshot.objectInstance.name);}
-return snapshot;},idWasDeleted:function(scopedId,category,name,ts){var instanceMap=this.getOrCreateInstanceMap_(scopedId);var deletedInstance=instanceMap.idWasDeleted(category,name,ts);if(!deletedInstance)
-return;if(deletedInstance.category!=category){var msg='Deleting object '+deletedInstance.name+' with a different category '+'than when it was created. It previous had cat='+
-deletedInstance.category+' but the delete command '+'had cat='+category;throw new Error(msg);}
-if(deletedInstance.baseTypeName!=name){throw new Error('Deletion requested for name='+
-name+' could not proceed: '+'An existing object with baseTypeName='+
-deletedInstance.baseTypeName+' existed.');}},autoDeleteObjects:function(maxTimestamp){tr.b.iterItems(this.instanceMapsByScopedId_,function(scope,imapById){tr.b.iterItems(imapById,function(id,i2imap){var lastInstance=i2imap.lastInstance;if(lastInstance.deletionTs!=Number.MAX_VALUE)
-return;i2imap.idWasDeleted(lastInstance.category,lastInstance.name,maxTimestamp);lastInstance.deletionTsWasExplicit=false;});});},getObjectInstanceAt:function(scopedId,ts){var instanceMap;if(scopedId.scope in this.instanceMapsByScopedId_)
-instanceMap=this.instanceMapsByScopedId_[scopedId.scope][scopedId.id];if(!instanceMap)
-return undefined;return instanceMap.getInstanceAt(ts);},getSnapshotAt:function(scopedId,ts){var instance=this.getObjectInstanceAt(scopedId,ts);if(!instance)
-return undefined;return instance.getSnapshotAt(ts);},iterObjectInstances:function(iter,opt_this){opt_this=opt_this||this;tr.b.iterItems(this.instanceMapsByScopedId_,function(scope,imapById){tr.b.iterItems(imapById,function(id,i2imap){i2imap.instances.forEach(iter,opt_this);});});},getAllObjectInstances:function(){var instances=[];this.iterObjectInstances(function(i){instances.push(i);});return instances;},getAllInstancesNamed:function(name){return this.instancesByTypeName_[name];},getAllInstancesByTypeName:function(){return this.instancesByTypeName_;},preInitializeAllObjects:function(){this.iterObjectInstances(function(instance){instance.preInitialize();});},initializeAllObjects:function(){this.iterObjectInstances(function(instance){instance.initialize();});},initializeInstances:function(){this.iterObjectInstances(function(instance){instance.initialize();});},updateBounds:function(){this.bounds.reset();this.iterObjectInstances(function(instance){instance.updateBounds();this.bounds.addRange(instance.bounds);},this);},shiftTimestampsForward:function(amount){this.iterObjectInstances(function(instance){instance.shiftTimestampsForward(amount);});},addCategoriesToDict:function(categoriesDict){this.iterObjectInstances(function(instance){categoriesDict[instance.category]=true;});}};return{ObjectCollection:ObjectCollection};});'use strict';tr.exportTo('tr.model',function(){function AsyncSliceGroup(parentContainer,opt_name){tr.model.EventContainer.call(this);this.parentContainer_=parentContainer;this.slices=[];this.name_=opt_name;this.viewSubGroups_=undefined;}
-AsyncSliceGroup.prototype={__proto__:tr.model.EventContainer.prototype,get parentContainer(){return this.parentContainer_;},get model(){return this.parentContainer_.parent.model;},get stableId(){return this.parentContainer_.stableId+'.AsyncSliceGroup';},getSettingsKey:function(){if(!this.name_)
-return undefined;var parentKey=this.parentContainer_.getSettingsKey();if(!parentKey)
-return undefined;return parentKey+'.'+this.name_;},push:function(slice){slice.parentContainer=this.parentContainer;this.slices.push(slice);return slice;},get length(){return this.slices.length;},shiftTimestampsForward:function(amount){for(var sI=0;sI<this.slices.length;sI++){var slice=this.slices[sI];slice.start=(slice.start+amount);var shiftSubSlices=function(subSlices){if(subSlices===undefined||subSlices.length===0)
-return;for(var sJ=0;sJ<subSlices.length;sJ++){subSlices[sJ].start+=amount;shiftSubSlices(subSlices[sJ].subSlices);}};shiftSubSlices(slice.subSlices);}},updateBounds:function(){this.bounds.reset();for(var i=0;i<this.slices.length;i++){this.bounds.addValue(this.slices[i].start);this.bounds.addValue(this.slices[i].end);}},get viewSubGroups(){if(this.viewSubGroups_===undefined){var prefix='';if(this.name!==undefined)
-prefix=this.name+'.';else
-prefix='';var subGroupsByTitle={};for(var i=0;i<this.slices.length;++i){var slice=this.slices[i];var subGroupTitle=slice.viewSubGroupTitle;if(!subGroupsByTitle[subGroupTitle]){subGroupsByTitle[subGroupTitle]=new AsyncSliceGroup(this.parentContainer_,prefix+subGroupTitle);}
-subGroupsByTitle[subGroupTitle].push(slice);}
-this.viewSubGroups_=tr.b.dictionaryValues(subGroupsByTitle);this.viewSubGroups_.sort(function(a,b){return a.slices[0].compareTo(b.slices[0]);});}
-return this.viewSubGroups_;},findTopmostSlicesInThisContainer:function*(eventPredicate,opt_this){for(var slice of this.slices){if(slice.isTopLevel){yield*slice.findTopmostSlicesRelativeToThisSlice(eventPredicate,opt_this);}}},childEvents:function*(){for(var slice of this.slices){yield slice;if(slice.subSlices)
-yield*slice.subSlices;}},childEventContainers:function*(){}};return{AsyncSliceGroup:AsyncSliceGroup};});'use strict';tr.exportTo('tr.model',function(){var Slice=tr.model.Slice;function ThreadSlice(cat,title,colorId,start,args,opt_duration,opt_cpuStart,opt_cpuDuration,opt_argsStripped,opt_bind_id){Slice.call(this,cat,title,colorId,start,args,opt_duration,opt_cpuStart,opt_cpuDuration,opt_argsStripped,opt_bind_id);this.subSlices=[];}
-ThreadSlice.prototype={__proto__:Slice.prototype,get overlappingSamples(){var samples=new tr.model.EventSet();if(!this.parentContainer||!this.parentContainer.samples)
-return samples;this.parentContainer.samples.forEach(function(sample){if(this.start<=sample.start&&sample.start<=this.end)
-samples.push(sample);},this);return samples;}};tr.model.EventRegistry.register(ThreadSlice,{name:'slice',pluralName:'slices'});return{ThreadSlice:ThreadSlice};});'use strict';tr.exportTo('tr.model',function(){var ColorScheme=tr.b.ColorScheme;var ThreadSlice=tr.model.ThreadSlice;function getSliceLo(s){return s.start;}
-function getSliceHi(s){return s.end;}
-function SliceGroup(parentContainer,opt_sliceConstructor,opt_name){tr.model.EventContainer.call(this);this.parentContainer_=parentContainer;var sliceConstructor=opt_sliceConstructor||ThreadSlice;this.sliceConstructor=sliceConstructor;this.sliceConstructorSubTypes=this.sliceConstructor.subTypes;if(!this.sliceConstructorSubTypes)
-throw new Error('opt_sliceConstructor must have a subtype registry.');this.openPartialSlices_=[];this.slices=[];this.topLevelSlices=[];this.haveTopLevelSlicesBeenBuilt=false;this.name_=opt_name;if(this.model===undefined)
-throw new Error('SliceGroup must have model defined.');}
-SliceGroup.prototype={__proto__:tr.model.EventContainer.prototype,get parentContainer(){return this.parentContainer_;},get model(){return this.parentContainer_.model;},get stableId(){return this.parentContainer_.stableId+'.SliceGroup';},getSettingsKey:function(){if(!this.name_)
-return undefined;var parentKey=this.parentContainer_.getSettingsKey();if(!parentKey)
-return undefined;return parentKey+'.'+this.name;},get length(){return this.slices.length;},pushSlice:function(slice){this.haveTopLevelSlicesBeenBuilt=false;slice.parentContainer=this.parentContainer_;this.slices.push(slice);return slice;},pushSlices:function(slices){this.haveTopLevelSlicesBeenBuilt=false;slices.forEach(function(slice){slice.parentContainer=this.parentContainer_;this.slices.push(slice);},this);},beginSlice:function(category,title,ts,opt_args,opt_tts,opt_argsStripped,opt_colorId){if(this.openPartialSlices_.length){var prevSlice=this.openPartialSlices_[this.openPartialSlices_.length-1];if(ts<prevSlice.start)
-throw new Error('Slices must be added in increasing timestamp order');}
-var colorId=opt_colorId||ColorScheme.getColorIdForGeneralPurposeString(title);var sliceConstructorSubTypes=this.sliceConstructorSubTypes;var sliceType=sliceConstructorSubTypes.getConstructor(category,title);var slice=new sliceType(category,title,colorId,ts,opt_args?opt_args:{},null,opt_tts,undefined,opt_argsStripped);this.openPartialSlices_.push(slice);slice.didNotFinish=true;this.pushSlice(slice);return slice;},isTimestampValidForBeginOrEnd:function(ts){if(!this.openPartialSlices_.length)
-return true;var top=this.openPartialSlices_[this.openPartialSlices_.length-1];return ts>=top.start;},get openSliceCount(){return this.openPartialSlices_.length;},get mostRecentlyOpenedPartialSlice(){if(!this.openPartialSlices_.length)
-return undefined;return this.openPartialSlices_[this.openPartialSlices_.length-1];},endSlice:function(ts,opt_tts,opt_colorId){if(!this.openSliceCount)
-throw new Error('endSlice called without an open slice');var slice=this.openPartialSlices_[this.openSliceCount-1];this.openPartialSlices_.splice(this.openSliceCount-1,1);if(ts<slice.start)
-throw new Error('Slice '+slice.title+' end time is before its start.');slice.duration=ts-slice.start;slice.didNotFinish=false;slice.colorId=opt_colorId||slice.colorId;if(opt_tts&&slice.cpuStart!==undefined)
-slice.cpuDuration=opt_tts-slice.cpuStart;return slice;},pushCompleteSlice:function(category,title,ts,duration,tts,cpuDuration,opt_args,opt_argsStripped,opt_colorId,opt_bind_id){var colorId=opt_colorId||ColorScheme.getColorIdForGeneralPurposeString(title);var sliceConstructorSubTypes=this.sliceConstructorSubTypes;var sliceType=sliceConstructorSubTypes.getConstructor(category,title);var slice=new sliceType(category,title,colorId,ts,opt_args?opt_args:{},duration,tts,cpuDuration,opt_argsStripped,opt_bind_id);if(duration===undefined)
-slice.didNotFinish=true;this.pushSlice(slice);return slice;},autoCloseOpenSlices:function(){this.updateBounds();var maxTimestamp=this.bounds.max;for(var sI=0;sI<this.slices.length;sI++){var slice=this.slices[sI];if(slice.didNotFinish)
-slice.duration=maxTimestamp-slice.start;}
-this.openPartialSlices_=[];},shiftTimestampsForward:function(amount){for(var sI=0;sI<this.slices.length;sI++){var slice=this.slices[sI];slice.start=(slice.start+amount);}},updateBounds:function(){this.bounds.reset();for(var i=0;i<this.slices.length;i++){this.bounds.addValue(this.slices[i].start);this.bounds.addValue(this.slices[i].end);}},copySlice:function(slice){var sliceConstructorSubTypes=this.sliceConstructorSubTypes;var sliceType=sliceConstructorSubTypes.getConstructor(slice.category,slice.title);var newSlice=new sliceType(slice.category,slice.title,slice.colorId,slice.start,slice.args,slice.duration,slice.cpuStart,slice.cpuDuration);newSlice.didNotFinish=slice.didNotFinish;return newSlice;},findTopmostSlicesInThisContainer:function*(eventPredicate,opt_this){if(!this.haveTopLevelSlicesBeenBuilt)
-throw new Error('Nope');for(var s of this.topLevelSlices)
-yield*s.findTopmostSlicesRelativeToThisSlice(eventPredicate);},childEvents:function*(){yield*this.slices;},childEventContainers:function*(){},getSlicesOfName:function(title){var slices=[];for(var i=0;i<this.slices.length;i++){if(this.slices[i].title==title){slices.push(this.slices[i]);}}
-return slices;},iterSlicesInTimeRange:function(callback,start,end){var ret=[];tr.b.iterateOverIntersectingIntervals(this.topLevelSlices,function(s){return s.start;},function(s){return s.duration;},start,end,function(topLevelSlice){callback(topLevelSlice);for(var slice of topLevelSlice.enumerateAllDescendents())
-callback(slice);});return ret;},findFirstSlice:function(){if(!this.haveTopLevelSlicesBeenBuilt)
-throw new Error('Nope');if(0===this.slices.length)
-return undefined;return this.slices[0];},findSliceAtTs:function(ts){if(!this.haveTopLevelSlicesBeenBuilt)
-throw new Error('Nope');var i=tr.b.findIndexInSortedClosedIntervals(this.topLevelSlices,getSliceLo,getSliceHi,ts);if(i==-1||i==this.topLevelSlices.length)
-return undefined;var curSlice=this.topLevelSlices[i];while(true){var i=tr.b.findIndexInSortedClosedIntervals(curSlice.subSlices,getSliceLo,getSliceHi,ts);if(i==-1||i==curSlice.subSlices.length)
-return curSlice;curSlice=curSlice.subSlices[i];}},findNextSliceAfter:function(ts,refGuid){var i=tr.b.findLowIndexInSortedArray(this.slices,getSliceLo,ts);if(i===this.slices.length)
-return undefined;for(;i<this.slices.length;i++){var slice=this.slices[i];if(slice.start>ts)
-return slice;if(slice.guid<=refGuid)
-continue;return slice;}
-return undefined;},createSubSlices:function(){this.haveTopLevelSlicesBeenBuilt=true;this.createSubSlicesImpl_();if(this.parentContainer.timeSlices)
-this.addCpuTimeToSubslices_(this.parentContainer.timeSlices);this.slices.forEach(function(slice){var selfTime=slice.duration;for(var i=0;i<slice.subSlices.length;i++)
-selfTime-=slice.subSlices[i].duration;slice.selfTime=selfTime;if(slice.cpuDuration===undefined)
-return;var cpuSelfTime=slice.cpuDuration;for(var i=0;i<slice.subSlices.length;i++){if(slice.subSlices[i].cpuDuration!==undefined)
-cpuSelfTime-=slice.subSlices[i].cpuDuration;}
-slice.cpuSelfTime=cpuSelfTime;});},createSubSlicesImpl_:function(){var precisionUnit=this.model.intrinsicTimeUnit;function addSliceIfBounds(root,child){if(root.bounds(child,precisionUnit)){if(root.subSlices&&root.subSlices.length>0){if(addSliceIfBounds(root.subSlices[root.subSlices.length-1],child))
-return true;}
-child.parentSlice=root;if(root.subSlices===undefined)
-root.subSlices=[];root.subSlices.push(child);return true;}
-return false;}
-if(!this.slices.length)
-return;var ops=[];for(var i=0;i<this.slices.length;i++){if(this.slices[i].subSlices)
-this.slices[i].subSlices.splice(0,this.slices[i].subSlices.length);ops.push(i);}
-var originalSlices=this.slices;ops.sort(function(ix,iy){var x=originalSlices[ix];var y=originalSlices[iy];if(x.start!=y.start)
-return x.start-y.start;return ix-iy;});var slices=new Array(this.slices.length);for(var i=0;i<ops.length;i++){slices[i]=originalSlices[ops[i]];}
-var rootSlice=slices[0];this.topLevelSlices=[];this.topLevelSlices.push(rootSlice);rootSlice.isTopLevel=true;for(var i=1;i<slices.length;i++){var slice=slices[i];if(!addSliceIfBounds(rootSlice,slice)){rootSlice=slice;rootSlice.isTopLevel=true;this.topLevelSlices.push(rootSlice);}}
-this.slices=slices;},addCpuTimeToSubslices_:function(timeSlices){var SCHEDULING_STATE=tr.model.SCHEDULING_STATE;var sliceIdx=0;timeSlices.forEach(function(timeSlice){if(timeSlice.schedulingState==SCHEDULING_STATE.RUNNING){while(sliceIdx<this.topLevelSlices.length){if(this.addCpuTimeToSubslice_(this.topLevelSlices[sliceIdx],timeSlice)){sliceIdx++;}else{break;}}}},this);},addCpuTimeToSubslice_:function(slice,timeSlice){if(slice.start>timeSlice.end||slice.end<timeSlice.start)
-return slice.end<=timeSlice.end;var duration=timeSlice.duration;if(slice.start>timeSlice.start)
-duration-=slice.start-timeSlice.start;if(timeSlice.end>slice.end)
-duration-=timeSlice.end-slice.end;if(slice.cpuDuration){slice.cpuDuration+=duration;}else{slice.cpuDuration=duration;}
-for(var i=0;i<slice.subSlices.length;i++){this.addCpuTimeToSubslice_(slice.subSlices[i],timeSlice);}
-return slice.end<=timeSlice.end;}};SliceGroup.merge=function(groupA,groupB){if(groupA.openPartialSlices_.length>0)
-throw new Error('groupA has open partial slices');if(groupB.openPartialSlices_.length>0)
-throw new Error('groupB has open partial slices');if(groupA.parentContainer!=groupB.parentContainer)
-throw new Error('Different parent threads. Cannot merge');if(groupA.sliceConstructor!=groupB.sliceConstructor)
-throw new Error('Different slice constructors. Cannot merge');var result=new SliceGroup(groupA.parentContainer,groupA.sliceConstructor,groupA.name_);var slicesA=groupA.slices;var slicesB=groupB.slices;var idxA=0;var idxB=0;var openA=[];var openB=[];var splitOpenSlices=function(when){for(var i=0;i<openB.length;i++){var oldSlice=openB[i];var oldEnd=oldSlice.end;if(when<oldSlice.start||oldEnd<when){throw new Error('slice should not be split');}
-var newSlice=result.copySlice(oldSlice);newSlice.start=when;newSlice.duration=oldEnd-when;if(newSlice.title.indexOf(' (cont.)')==-1)
-newSlice.title+=' (cont.)';oldSlice.duration=when-oldSlice.start;openB[i]=newSlice;result.pushSlice(newSlice);}};var closeOpenSlices=function(upTo){while(openA.length>0||openB.length>0){var nextA=openA[openA.length-1];var nextB=openB[openB.length-1];var endA=nextA&&nextA.end;var endB=nextB&&nextB.end;if((endA===undefined||endA>upTo)&&(endB===undefined||endB>upTo)){return;}
-if(endB===undefined||endA<endB){splitOpenSlices(endA);openA.pop();}else{openB.pop();}}};while(idxA<slicesA.length||idxB<slicesB.length){var sA=slicesA[idxA];var sB=slicesB[idxB];var nextSlice,isFromB;if(sA===undefined||(sB!==undefined&&sA.start>sB.start)){nextSlice=result.copySlice(sB);isFromB=true;idxB++;}else{nextSlice=result.copySlice(sA);isFromB=false;idxA++;}
-closeOpenSlices(nextSlice.start);result.pushSlice(nextSlice);if(isFromB){openB.push(nextSlice);}else{splitOpenSlices(nextSlice.start);openA.push(nextSlice);}}
-closeOpenSlices();return result;};return{SliceGroup:SliceGroup};});'use strict';tr.exportTo('tr.model',function(){var AsyncSlice=tr.model.AsyncSlice;var AsyncSliceGroup=tr.model.AsyncSliceGroup;var SliceGroup=tr.model.SliceGroup;var ThreadSlice=tr.model.ThreadSlice;var ThreadTimeSlice=tr.model.ThreadTimeSlice;function Thread(parent,tid){if(!parent)
-throw new Error('Parent must be provided.');tr.model.EventContainer.call(this);this.parent=parent;this.sortIndex=0;this.tid=tid;this.name=undefined;this.samples_=undefined;var that=this;this.sliceGroup=new SliceGroup(this,ThreadSlice,'slices');this.timeSlices=undefined;this.kernelSliceGroup=new SliceGroup(this,ThreadSlice,'kernel-slices');this.asyncSliceGroup=new AsyncSliceGroup(this,'async-slices');}
-Thread.prototype={__proto__:tr.model.EventContainer.prototype,get model(){return this.parent.model;},get stableId(){return this.parent.stableId+'.'+this.tid;},compareTo:function(that){return Thread.compare(this,that);},childEventContainers:function*(){if(this.sliceGroup.length)
-yield this.sliceGroup;if(this.kernelSliceGroup.length)
-yield this.kernelSliceGroup;if(this.asyncSliceGroup.length)
-yield this.asyncSliceGroup;},childEvents:function*(){if(this.timeSlices)
-yield*this.timeSlices;},iterateAllPersistableObjects:function(cb){cb(this);if(this.sliceGroup.length)
-cb(this.sliceGroup);this.asyncSliceGroup.viewSubGroups.forEach(cb);},shiftTimestampsForward:function(amount){this.sliceGroup.shiftTimestampsForward(amount);if(this.timeSlices){for(var i=0;i<this.timeSlices.length;i++){var slice=this.timeSlices[i];slice.start+=amount;}}
-this.kernelSliceGroup.shiftTimestampsForward(amount);this.asyncSliceGroup.shiftTimestampsForward(amount);},get isEmpty(){if(this.sliceGroup.length)
-return false;if(this.sliceGroup.openSliceCount)
-return false;if(this.timeSlices&&this.timeSlices.length)
-return false;if(this.kernelSliceGroup.length)
-return false;if(this.asyncSliceGroup.length)
-return false;if(this.samples_.length)
-return false;return true;},updateBounds:function(){this.bounds.reset();this.sliceGroup.updateBounds();this.bounds.addRange(this.sliceGroup.bounds);this.kernelSliceGroup.updateBounds();this.bounds.addRange(this.kernelSliceGroup.bounds);this.asyncSliceGroup.updateBounds();this.bounds.addRange(this.asyncSliceGroup.bounds);if(this.timeSlices&&this.timeSlices.length){this.bounds.addValue(this.timeSlices[0].start);this.bounds.addValue(this.timeSlices[this.timeSlices.length-1].end);}
-if(this.samples_&&this.samples_.length){this.bounds.addValue(this.samples_[0].start);this.bounds.addValue(this.samples_[this.samples_.length-1].end);}},addCategoriesToDict:function(categoriesDict){for(var i=0;i<this.sliceGroup.length;i++)
-categoriesDict[this.sliceGroup.slices[i].category]=true;for(var i=0;i<this.kernelSliceGroup.length;i++)
-categoriesDict[this.kernelSliceGroup.slices[i].category]=true;for(var i=0;i<this.asyncSliceGroup.length;i++)
-categoriesDict[this.asyncSliceGroup.slices[i].category]=true;if(this.samples_){for(var i=0;i<this.samples_.length;i++)
-categoriesDict[this.samples_[i].category]=true;}},autoCloseOpenSlices:function(){this.sliceGroup.autoCloseOpenSlices();this.kernelSliceGroup.autoCloseOpenSlices();},mergeKernelWithUserland:function(){if(this.kernelSliceGroup.length>0){var newSlices=SliceGroup.merge(this.sliceGroup,this.kernelSliceGroup);this.sliceGroup.slices=newSlices.slices;this.kernelSliceGroup=new SliceGroup(this);this.updateBounds();}},createSubSlices:function(){this.sliceGroup.createSubSlices();this.samples_=this.parent.model.samples.filter(function(sample){return sample.thread==this;},this);},get userFriendlyName(){return this.name||this.tid;},get userFriendlyDetails(){return'tid: '+this.tid+
-(this.name?', name: '+this.name:'');},getSettingsKey:function(){if(!this.name)
-return undefined;var parentKey=this.parent.getSettingsKey();if(!parentKey)
-return undefined;return parentKey+'.'+this.name;},getProcess:function(){return this.parent;},indexOfTimeSlice:function(timeSlice){var i=tr.b.findLowIndexInSortedArray(this.timeSlices,function(slice){return slice.start;},timeSlice.start);if(this.timeSlices[i]!==timeSlice)
-return undefined;return i;},getCpuStatsForRange:function(range){var stats={};stats.total=0;if(!this.timeSlices)
-return stats;function addStatsForSlice(threadTimeSlice){var freqRange=tr.b.Range.fromExplicitRange(threadTimeSlice.start,threadTimeSlice.end);var intersection=freqRange.findIntersection(range);if(threadTimeSlice.schedulingState==tr.model.SCHEDULING_STATE.RUNNING){var cpu=threadTimeSlice.cpuOnWhichThreadWasRunning;if(!(cpu.cpuNumber in stats))
-stats[cpu.cpuNumber]=0;stats[cpu.cpuNumber]+=intersection.duration;stats.total+=intersection.duration;}}
-tr.b.iterateOverIntersectingIntervals(this.timeSlices,function(x){return x.start;},function(x){return x.end;},range.min,range.max,addStatsForSlice);return stats;},getSchedulingStatsForRange:function(start,end){var stats={};if(!this.timeSlices)return stats;function addStatsForSlice(threadTimeSlice){var overlapStart=Math.max(threadTimeSlice.start,start);var overlapEnd=Math.min(threadTimeSlice.end,end);var schedulingState=threadTimeSlice.schedulingState;if(!(schedulingState in stats))
-stats[schedulingState]=0;stats[schedulingState]+=overlapEnd-overlapStart;}
-tr.b.iterateOverIntersectingIntervals(this.timeSlices,function(x){return x.start;},function(x){return x.end;},start,end,addStatsForSlice);return stats;},get samples(){return this.samples_;}};Thread.compare=function(x,y){var tmp=x.parent.compareTo(y.parent);if(tmp)
-return tmp;tmp=x.sortIndex-y.sortIndex;if(tmp)
-return tmp;tmp=tr.b.comparePossiblyUndefinedValues(x.name,y.name,function(x,y){return x.localeCompare(y);});if(tmp)
-return tmp;return x.tid-y.tid;};return{Thread:Thread};});'use strict';tr.exportTo('tr.model',function(){var Thread=tr.model.Thread;var Counter=tr.model.Counter;function ProcessBase(model){if(!model)
-throw new Error('Must provide a model');tr.model.EventContainer.call(this);this.model=model;this.threads={};this.counters={};this.objects=new tr.model.ObjectCollection(this);this.sortIndex=0;};ProcessBase.compare=function(x,y){return x.sortIndex-y.sortIndex;};ProcessBase.prototype={__proto__:tr.model.EventContainer.prototype,get stableId(){throw new Error('Not implemented');},childEventContainers:function*(){yield*tr.b.dictionaryValues(this.threads);yield*tr.b.dictionaryValues(this.counters);yield this.objects;},iterateAllPersistableObjects:function(cb){cb(this);for(var tid in this.threads)
-this.threads[tid].iterateAllPersistableObjects(cb);},get numThreads(){var n=0;for(var p in this.threads){n++;}
-return n;},shiftTimestampsForward:function(amount){for(var child of this.childEventContainers())
-child.shiftTimestampsForward(amount);},autoCloseOpenSlices:function(){for(var tid in this.threads){var thread=this.threads[tid];thread.autoCloseOpenSlices();}},autoDeleteObjects:function(maxTimestamp){this.objects.autoDeleteObjects(maxTimestamp);},preInitializeObjects:function(){this.objects.preInitializeAllObjects();},initializeObjects:function(){this.objects.initializeAllObjects();},mergeKernelWithUserland:function(){for(var tid in this.threads){var thread=this.threads[tid];thread.mergeKernelWithUserland();}},updateBounds:function(){this.bounds.reset();for(var tid in this.threads){this.threads[tid].updateBounds();this.bounds.addRange(this.threads[tid].bounds);}
-for(var id in this.counters){this.counters[id].updateBounds();this.bounds.addRange(this.counters[id].bounds);}
-this.objects.updateBounds();this.bounds.addRange(this.objects.bounds);},addCategoriesToDict:function(categoriesDict){for(var tid in this.threads)
-this.threads[tid].addCategoriesToDict(categoriesDict);for(var id in this.counters)
-categoriesDict[this.counters[id].category]=true;this.objects.addCategoriesToDict(categoriesDict);},findAllThreadsMatching:function(predicate,opt_this){var threads=[];for(var tid in this.threads){var thread=this.threads[tid];if(predicate.call(opt_this,thread))
-threads.push(thread);}
-return threads;},findAllThreadsNamed:function(name){var threads=this.findAllThreadsMatching(function(thread){if(!thread.name)
-return false;return thread.name===name;});return threads;},findAtMostOneThreadNamed:function(name){var threads=this.findAllThreadsNamed(name);if(threads.length===0)
-return undefined;if(threads.length>1)
-throw new Error('Expected no more than one '+name);return threads[0];},pruneEmptyContainers:function(){var threadsToKeep={};for(var tid in this.threads){var thread=this.threads[tid];if(!thread.isEmpty)
-threadsToKeep[tid]=thread;}
-this.threads=threadsToKeep;},getThread:function(tid){return this.threads[tid];},getOrCreateThread:function(tid){if(!this.threads[tid])
-this.threads[tid]=new Thread(this,tid);return this.threads[tid];},getOrCreateCounter:function(cat,name){var id=cat+'.'+name;if(!this.counters[id])
-this.counters[id]=new Counter(this,id,cat,name);return this.counters[id];},getSettingsKey:function(){throw new Error('Not implemented');},createSubSlices:function(){for(var tid in this.threads)
-this.threads[tid].createSubSlices();}};return{ProcessBase:ProcessBase};});'use strict';tr.exportTo('tr.model',function(){var ColorScheme=tr.b.ColorScheme;var Counter=tr.model.Counter;var CpuSlice=tr.model.CpuSlice;function Cpu(kernel,number){if(kernel===undefined||number===undefined)
-throw new Error('Missing arguments');this.kernel=kernel;this.cpuNumber=number;this.slices=[];this.counters={};this.bounds_=new tr.b.Range();this.samples_=undefined;this.lastActiveTimestamp_=undefined;this.lastActiveThread_=undefined;this.lastActiveName_=undefined;this.lastActiveArgs_=undefined;};Cpu.prototype={__proto__:tr.model.EventContainer.prototype,get samples(){return this.samples_;},get userFriendlyName(){return'CPU '+this.cpuNumber;},findTopmostSlicesInThisContainer:function*(eventPredicate,opt_this){for(var s of this.slices){yield*s.findTopmostSlicesRelativeToThisSlice(eventPredicate,opt_this);}},childEvents:function*(){yield*this.slices;if(this.samples_)
-yield*this.samples_;},childEventContainers:function*(){yield*tr.b.dictionaryValues(this.counters);},getOrCreateCounter:function(cat,name){var id=cat+'.'+name;if(!this.counters[id])
-this.counters[id]=new Counter(this,id,cat,name);return this.counters[id];},getCounter:function(cat,name){var id=cat+'.'+name;if(!this.counters[id])
-return undefined;return this.counters[id];},shiftTimestampsForward:function(amount){for(var sI=0;sI<this.slices.length;sI++)
-this.slices[sI].start=(this.slices[sI].start+amount);for(var id in this.counters)
-this.counters[id].shiftTimestampsForward(amount);},updateBounds:function(){this.bounds_.reset();if(this.slices.length){this.bounds_.addValue(this.slices[0].start);this.bounds_.addValue(this.slices[this.slices.length-1].end);}
-for(var id in this.counters){this.counters[id].updateBounds();this.bounds_.addRange(this.counters[id].bounds);}
-if(this.samples_&&this.samples_.length){this.bounds_.addValue(this.samples_[0].start);this.bounds_.addValue(this.samples_[this.samples_.length-1].end);}},createSubSlices:function(){this.samples_=this.kernel.model.samples.filter(function(sample){return sample.cpu==this;},this);},addCategoriesToDict:function(categoriesDict){for(var i=0;i<this.slices.length;i++)
-categoriesDict[this.slices[i].category]=true;for(var id in this.counters)
-categoriesDict[this.counters[id].category]=true;for(var i=0;i<this.samples_.length;i++)
-categoriesDict[this.samples_[i].category]=true;},indexOf:function(cpuSlice){var i=tr.b.findLowIndexInSortedArray(this.slices,function(slice){return slice.start;},cpuSlice.start);if(this.slices[i]!==cpuSlice)
-return undefined;return i;},closeActiveThread:function(end_timestamp,args){if(this.lastActiveThread_==undefined||this.lastActiveThread_==0)
-return;if(end_timestamp<this.lastActiveTimestamp_){throw new Error('The end timestamp of a thread running on CPU '+
-this.cpuNumber+' is before its start timestamp.');}
-for(var key in args){this.lastActiveArgs_[key]=args[key];}
-var duration=end_timestamp-this.lastActiveTimestamp_;var slice=new tr.model.CpuSlice('',this.lastActiveName_,ColorScheme.getColorIdForGeneralPurposeString(this.lastActiveName_),this.lastActiveTimestamp_,this.lastActiveArgs_,duration);slice.cpu=this;this.slices.push(slice);this.lastActiveTimestamp_=undefined;this.lastActiveThread_=undefined;this.lastActiveName_=undefined;this.lastActiveArgs_=undefined;},switchActiveThread:function(timestamp,old_thread_args,new_thread_id,new_thread_name,new_thread_args){this.closeActiveThread(timestamp,old_thread_args);this.lastActiveTimestamp_=timestamp;this.lastActiveThread_=new_thread_id;this.lastActiveName_=new_thread_name;this.lastActiveArgs_=new_thread_args;},getFreqStatsForRange:function(range){var stats={};function addStatsForFreq(freqSample,index){var freqEnd=(index<freqSample.series_.length-1)?freqSample.series_.samples_[index+1].timestamp:range.max;var freqRange=tr.b.Range.fromExplicitRange(freqSample.timestamp,freqEnd);var intersection=freqRange.findIntersection(range);if(!(freqSample.value in stats))
-stats[freqSample.value]=0;stats[freqSample.value]+=intersection.duration;}
-var freqCounter=this.getCounter('','Clock Frequency');if(freqCounter!==undefined){var freqSeries=freqCounter.getSeries(0);if(!freqSeries)
-return;tr.b.iterateOverIntersectingIntervals(freqSeries.samples_,function(x){return x.timestamp;},function(x,index){return index<freqSeries.length-1?freqSeries.samples_[index+1].timestamp:range.max;},range.min,range.max,addStatsForFreq);}
-return stats;}};Cpu.compare=function(x,y){return x.cpuNumber-y.cpuNumber;};return{Cpu:Cpu};});'use strict';tr.exportTo('tr.model',function(){var Cpu=tr.model.Cpu;var ProcessBase=tr.model.ProcessBase;function Kernel(model){ProcessBase.call(this,model);this.cpus={};this.softwareMeasuredCpuCount_=undefined;};Kernel.compare=function(x,y){return 0;};Kernel.prototype={__proto__:ProcessBase.prototype,compareTo:function(that){return Kernel.compare(this,that);},get userFriendlyName(){return'Kernel';},get userFriendlyDetails(){return'Kernel';},get stableId(){return'Kernel';},getOrCreateCpu:function(cpuNumber){if(!this.cpus[cpuNumber])
-this.cpus[cpuNumber]=new Cpu(this,cpuNumber);return this.cpus[cpuNumber];},get softwareMeasuredCpuCount(){return this.softwareMeasuredCpuCount_;},set softwareMeasuredCpuCount(softwareMeasuredCpuCount){if(this.softwareMeasuredCpuCount_!==undefined&&this.softwareMeasuredCpuCount_!==softwareMeasuredCpuCount){throw new Error('Cannot change the softwareMeasuredCpuCount once it is set');}
-this.softwareMeasuredCpuCount_=softwareMeasuredCpuCount;},get bestGuessAtCpuCount(){var realCpuCount=tr.b.dictionaryLength(this.cpus);if(realCpuCount!==0)
-return realCpuCount;return this.softwareMeasuredCpuCount;},updateBounds:function(){ProcessBase.prototype.updateBounds.call(this);for(var cpuNumber in this.cpus){var cpu=this.cpus[cpuNumber];cpu.updateBounds();this.bounds.addRange(cpu.bounds);}},createSubSlices:function(){ProcessBase.prototype.createSubSlices.call(this);for(var cpuNumber in this.cpus){var cpu=this.cpus[cpuNumber];cpu.createSubSlices();}},addCategoriesToDict:function(categoriesDict){ProcessBase.prototype.addCategoriesToDict.call(this,categoriesDict);for(var cpuNumber in this.cpus)
-this.cpus[cpuNumber].addCategoriesToDict(categoriesDict);},getSettingsKey:function(){return'kernel';},childEventContainers:function*(){yield*ProcessBase.prototype.childEventContainers.call(this);yield*tr.b.dictionaryValues(this.cpus);},};return{Kernel:Kernel};});'use strict';tr.exportTo('tr.model',function(){function ModelIndices(model){this.flowEventsById_={};model.flowEvents.forEach(function(fe){if(fe.id!==undefined){if(!this.flowEventsById_.hasOwnProperty(fe.id)){this.flowEventsById_[fe.id]=new Array();}
+tr.b.Unit.byName.timeStampInMs.format(start);}};tr.model.EventRegistry.register(InstantEvent,{name:'instantEvent',pluralName:'instantEvents'});return{GlobalInstantEvent,ProcessInstantEvent,InstantEventType,InstantEvent,};});'use strict';tr.exportTo('tr.model',function(){var Cpu=tr.model.Cpu;var ProcessBase=tr.model.ProcessBase;function Kernel(model){ProcessBase.call(this,model);this.cpus={};this.softwareMeasuredCpuCount_=undefined;}
+Kernel.compare=function(x,y){return 0;};Kernel.prototype={__proto__:ProcessBase.prototype,compareTo:function(that){return Kernel.compare(this,that);},get userFriendlyName(){return'Kernel';},get userFriendlyDetails(){return'Kernel';},get stableId(){return'Kernel';},getOrCreateCpu:function(cpuNumber){if(!this.cpus[cpuNumber]){this.cpus[cpuNumber]=new Cpu(this,cpuNumber);}
+return this.cpus[cpuNumber];},get softwareMeasuredCpuCount(){return this.softwareMeasuredCpuCount_;},set softwareMeasuredCpuCount(softwareMeasuredCpuCount){if(this.softwareMeasuredCpuCount_!==undefined&&this.softwareMeasuredCpuCount_!==softwareMeasuredCpuCount){throw new Error('Cannot change the softwareMeasuredCpuCount once it is set');}
+this.softwareMeasuredCpuCount_=softwareMeasuredCpuCount;},get bestGuessAtCpuCount(){var realCpuCount=tr.b.dictionaryLength(this.cpus);if(realCpuCount!==0){return realCpuCount;}
+return this.softwareMeasuredCpuCount;},updateBounds:function(){ProcessBase.prototype.updateBounds.call(this);for(var cpuNumber in this.cpus){var cpu=this.cpus[cpuNumber];cpu.updateBounds();this.bounds.addRange(cpu.bounds);}},createSubSlices:function(){ProcessBase.prototype.createSubSlices.call(this);for(var cpuNumber in this.cpus){var cpu=this.cpus[cpuNumber];cpu.createSubSlices();}},addCategoriesToDict:function(categoriesDict){ProcessBase.prototype.addCategoriesToDict.call(this,categoriesDict);for(var cpuNumber in this.cpus){this.cpus[cpuNumber].addCategoriesToDict(categoriesDict);}},getSettingsKey:function(){return'kernel';},childEventContainers:function*(){yield*ProcessBase.prototype.childEventContainers.call(this);yield*tr.b.dictionaryValues(this.cpus);},};return{Kernel,};});'use strict';tr.exportTo('tr.model',function(){function ModelIndices(model){this.flowEventsById_={};model.flowEvents.forEach(function(fe){if(fe.id!==undefined){if(!this.flowEventsById_.hasOwnProperty(fe.id)){this.flowEventsById_[fe.id]=[];}
 this.flowEventsById_[fe.id].push(fe);}},this);}
-ModelIndices.prototype={addEventWithId:function(id,event){if(!this.flowEventsById_.hasOwnProperty(id)){this.flowEventsById_[id]=new Array();}
-this.flowEventsById_[id].push(event);},getFlowEventsWithId:function(id){if(!this.flowEventsById_.hasOwnProperty(id))
-return[];return this.flowEventsById_[id];}};return{ModelIndices:ModelIndices};});'use strict';tr.exportTo('tr.model',function(){function ModelStats(){this.traceEventCountsByKey_=new Map();this.allTraceEventStats_=[];this.traceEventStatsInTimeIntervals_=new Map();this.allTraceEventStatsInTimeIntervals_=[];this.hasEventSizesinBytes_=false;}
+ModelIndices.prototype={addEventWithId:function(id,event){if(!this.flowEventsById_.hasOwnProperty(id)){this.flowEventsById_[id]=[];}
+this.flowEventsById_[id].push(event);},getFlowEventsWithId:function(id){if(!this.flowEventsById_.hasOwnProperty(id)){return[];}
+return this.flowEventsById_[id];}};return{ModelIndices,};});'use strict';tr.exportTo('tr.model',function(){function ModelStats(){this.traceEventCountsByKey_=new Map();this.allTraceEventStats_=[];this.traceEventStatsInTimeIntervals_=new Map();this.allTraceEventStatsInTimeIntervals_=[];this.hasEventSizesinBytes_=false;this.traceImportDurationMs_=undefined;}
 ModelStats.prototype={TIME_INTERVAL_SIZE_IN_MS:100,willProcessBasicTraceEvent:function(phase,category,title,ts,opt_eventSizeinBytes){var key=phase+'/'+category+'/'+title;var eventStats=this.traceEventCountsByKey_.get(key);if(eventStats===undefined){eventStats={phase:phase,category:category,title:title,numEvents:0,totalEventSizeinBytes:0};this.traceEventCountsByKey_.set(key,eventStats);this.allTraceEventStats_.push(eventStats);}
 eventStats.numEvents++;var timeIntervalKey=Math.floor(tr.b.Unit.timestampFromUs(ts)/this.TIME_INTERVAL_SIZE_IN_MS);var eventStatsByTimeInverval=this.traceEventStatsInTimeIntervals_.get(timeIntervalKey);if(eventStatsByTimeInverval===undefined){eventStatsByTimeInverval={timeInterval:timeIntervalKey,numEvents:0,totalEventSizeinBytes:0};this.traceEventStatsInTimeIntervals_.set(timeIntervalKey,eventStatsByTimeInverval);this.allTraceEventStatsInTimeIntervals_.push(eventStatsByTimeInverval);}
-eventStatsByTimeInverval.numEvents++;if(opt_eventSizeinBytes!==undefined){this.hasEventSizesinBytes_=true;eventStats.totalEventSizeinBytes+=opt_eventSizeinBytes;eventStatsByTimeInverval.totalEventSizeinBytes+=opt_eventSizeinBytes;}},get allTraceEventStats(){return this.allTraceEventStats_;},get allTraceEventStatsInTimeIntervals(){return this.allTraceEventStatsInTimeIntervals_;},get hasEventSizesinBytes(){return this.hasEventSizesinBytes_;}};return{ModelStats:ModelStats};});'use strict';tr.exportTo('tr.model',function(){function VMRegion(startAddress,sizeInBytes,protectionFlags,mappedFile,byteStats){this.startAddress=startAddress;this.sizeInBytes=sizeInBytes;this.protectionFlags=protectionFlags;this.mappedFile=mappedFile||'';this.byteStats=byteStats||{};};VMRegion.PROTECTION_FLAG_READ=4;VMRegion.PROTECTION_FLAG_WRITE=2;VMRegion.PROTECTION_FLAG_EXECUTE=1;VMRegion.PROTECTION_FLAG_MAYSHARE=128;VMRegion.prototype={get uniqueIdWithinProcess(){return this.mappedFile+'#'+this.startAddress;},get protectionFlagsToString(){if(this.protectionFlags===undefined)
-return undefined;return((this.protectionFlags&VMRegion.PROTECTION_FLAG_READ?'r':'-')+
+eventStatsByTimeInverval.numEvents++;if(opt_eventSizeinBytes!==undefined){this.hasEventSizesinBytes_=true;eventStats.totalEventSizeinBytes+=opt_eventSizeinBytes;eventStatsByTimeInverval.totalEventSizeinBytes+=opt_eventSizeinBytes;}},get allTraceEventStats(){return this.allTraceEventStats_;},get allTraceEventStatsInTimeIntervals(){return this.allTraceEventStatsInTimeIntervals_;},get hasEventSizesinBytes(){return this.hasEventSizesinBytes_;},get traceImportDurationMs(){return this.traceImportDurationMs_;},set traceImportDurationMs(traceImportDurationMs){this.traceImportDurationMs_=traceImportDurationMs;}};return{ModelStats,};});'use strict';tr.exportTo('tr.model',function(){function VMRegion(startAddress,sizeInBytes,protectionFlags,mappedFile,byteStats){this.startAddress=startAddress;this.sizeInBytes=sizeInBytes;this.protectionFlags=protectionFlags;this.mappedFile=mappedFile||'';this.byteStats=byteStats||{};}
+VMRegion.PROTECTION_FLAG_READ=4;VMRegion.PROTECTION_FLAG_WRITE=2;VMRegion.PROTECTION_FLAG_EXECUTE=1;VMRegion.PROTECTION_FLAG_MAYSHARE=128;VMRegion.prototype={get uniqueIdWithinProcess(){return this.mappedFile+'#'+this.startAddress;},get protectionFlagsToString(){if(this.protectionFlags===undefined)return undefined;return((this.protectionFlags&VMRegion.PROTECTION_FLAG_READ?'r':'-')+
 (this.protectionFlags&VMRegion.PROTECTION_FLAG_WRITE?'w':'-')+
 (this.protectionFlags&VMRegion.PROTECTION_FLAG_EXECUTE?'x':'-')+
 (this.protectionFlags&VMRegion.PROTECTION_FLAG_MAYSHARE?'s':'p'));}};VMRegion.fromDict=function(dict){return new VMRegion(dict.startAddress,dict.sizeInBytes,dict.protectionFlags,dict.mappedFile,dict.byteStats);};function VMRegionClassificationNode(opt_rule){this.rule_=opt_rule||VMRegionClassificationNode.CLASSIFICATION_RULES;this.hasRegions=false;this.sizeInBytes=undefined;this.byteStats={};this.children_=undefined;this.regions_=[];}
-VMRegionClassificationNode.CLASSIFICATION_RULES={name:'Total',children:[{name:'Android',file:/^\/dev\/ashmem(?!\/libc malloc)/,children:[{name:'Java runtime',file:/^\/dev\/ashmem\/dalvik-/,children:[{name:'Spaces',file:/\/dalvik-(alloc|main|large object|non moving|zygote) space/,children:[{name:'Normal',file:/\/dalvik-(alloc|main)/},{name:'Large',file:/\/dalvik-large object/},{name:'Zygote',file:/\/dalvik-zygote/},{name:'Non-moving',file:/\/dalvik-non moving/}]},{name:'Linear Alloc',file:/\/dalvik-LinearAlloc/},{name:'Indirect Reference Table',file:/\/dalvik-indirect.ref/},{name:'Cache',file:/\/dalvik-jit-code-cache/},{name:'Accounting'}]},{name:'Cursor',file:/\/CursorWindow/},{name:'Ashmem'}]},{name:'Native heap',file:/^((\[heap\])|(\[anon:)|(\/dev\/ashmem\/libc malloc)|(\[discounted tracing overhead\])|$)/},{name:'Stack',file:/^\[stack/},{name:'Files',file:/\.((((jar)|(apk)|(ttf)|(odex)|(oat)|(art))$)|(dex)|(so))/,children:[{name:'so',file:/\.so/},{name:'jar',file:/\.jar$/},{name:'apk',file:/\.apk$/},{name:'ttf',file:/\.ttf$/},{name:'dex',file:/\.((dex)|(odex$))/},{name:'oat',file:/\.oat$/},{name:'art',file:/\.art$/}]},{name:'Devices',file:/(^\/dev\/)|(anon_inode:dmabuf)/,children:[{name:'GPU',file:/\/((nv)|(mali)|(kgsl))/},{name:'DMA',file:/anon_inode:dmabuf/}]}]};VMRegionClassificationNode.OTHER_RULE={name:'Other'};VMRegionClassificationNode.fromRegions=function(regions,opt_rules){var tree=new VMRegionClassificationNode(opt_rules);tree.regions_=regions;for(var i=0;i<regions.length;i++)
-tree.addStatsFromRegion_(regions[i]);return tree;};VMRegionClassificationNode.prototype={get title(){return this.rule_.name;},get children(){if(this.isLeafNode)
-return undefined;if(this.children_===undefined)
-this.buildTree_();return this.children_;},get regions(){if(!this.isLeafNode){return undefined;}
+VMRegionClassificationNode.CLASSIFICATION_RULES={name:'Total',children:[{name:'Android',file:/^\/dev\/ashmem(?!\/libc malloc)/,children:[{name:'Java runtime',file:/^\/dev\/ashmem\/dalvik-/,children:[{name:'Spaces',file:/\/dalvik-(alloc|main|large object|non moving|zygote) space/,children:[{name:'Normal',file:/\/dalvik-(alloc|main)/},{name:'Large',file:/\/dalvik-large object/},{name:'Zygote',file:/\/dalvik-zygote/},{name:'Non-moving',file:/\/dalvik-non moving/}]},{name:'Linear Alloc',file:/\/dalvik-LinearAlloc/},{name:'Indirect Reference Table',file:/\/dalvik-indirect.ref/},{name:'Cache',file:/\/dalvik-jit-code-cache/},{name:'Accounting'}]},{name:'Cursor',file:/\/CursorWindow/},{name:'Ashmem'}]},{name:'Native heap',file:/^((\[heap\])|(\[anon:)|(\/dev\/ashmem\/libc malloc)|(\[discounted tracing overhead\])|$)/},{name:'Stack',file:/^\[stack/},{name:'Files',file:/\.((((jar)|(apk)|(ttf)|(odex)|(oat)|(art))$)|(dex)|(so))/,children:[{name:'so',file:/\.so/},{name:'jar',file:/\.jar$/},{name:'apk',file:/\.apk$/},{name:'ttf',file:/\.ttf$/},{name:'dex',file:/\.((dex)|(odex$))/},{name:'oat',file:/\.oat$/},{name:'art',file:/\.art$/}]},{name:'Devices',file:/(^\/dev\/)|(anon_inode:dmabuf)/,children:[{name:'GPU',file:/\/((nv)|(mali)|(kgsl))/},{name:'DMA',file:/anon_inode:dmabuf/}]}]};VMRegionClassificationNode.OTHER_RULE={name:'Other'};VMRegionClassificationNode.fromRegions=function(regions,opt_rules){var tree=new VMRegionClassificationNode(opt_rules);tree.regions_=regions;for(var i=0;i<regions.length;i++){tree.addStatsFromRegion_(regions[i]);}
+return tree;};VMRegionClassificationNode.prototype={get title(){return this.rule_.name;},get children(){if(this.isLeafNode){return undefined;}
+if(this.children_===undefined){this.buildTree_();}
+return this.children_;},get regions(){if(!this.isLeafNode){return undefined;}
 return this.regions_;},get allRegionsForTesting(){if(this.regions_!==undefined){if(this.children_!==undefined){throw new Error('Internal error: a VM region classification node '+'cannot have both regions and children');}
 return this.regions_;}
 var regions=[];this.children_.forEach(function(childNode){regions=regions.concat(childNode.allRegionsForTesting);});return regions;},get isLeafNode(){var children=this.rule_.children;return children===undefined||children.length===0;},addRegion:function(region){this.addRegionRecursively_(region,true);},someRegion:function(fn,opt_this){if(this.regions_!==undefined){return this.regions_.some(fn,opt_this);}
-return this.children_.some(function(childNode){return childNode.someRegion(fn,opt_this);});},addRegionRecursively_:function(region,addStatsToThisNode){if(addStatsToThisNode)
-this.addStatsFromRegion_(region);if(this.regions_!==undefined){if(this.children_!==undefined){throw new Error('Internal error: a VM region classification node '+'cannot have both regions and children');}
+return this.children_.some(function(childNode){return childNode.someRegion(fn,opt_this);});},addRegionRecursively_:function(region,addStatsToThisNode){if(addStatsToThisNode){this.addStatsFromRegion_(region);}
+if(this.regions_!==undefined){if(this.children_!==undefined){throw new Error('Internal error: a VM region classification node '+'cannot have both regions and children');}
 this.regions_.push(region);return;}
-function regionRowMatchesChildNide(child){var fileRegExp=child.rule_.file;if(fileRegExp===undefined)
-return true;return fileRegExp.test(region.mappedFile);}
-var matchedChild=tr.b.findFirstInArray(this.children_,regionRowMatchesChildNide);if(matchedChild===undefined){if(this.children_.length!==this.rule_.children.length)
-throw new Error('Internal error');matchedChild=new VMRegionClassificationNode(VMRegionClassificationNode.OTHER_RULE);this.children_.push(matchedChild);}
+function regionRowMatchesChildNide(child){var fileRegExp=child.rule_.file;if(fileRegExp===undefined)return true;return fileRegExp.test(region.mappedFile);}
+var matchedChild=tr.b.findFirstInArray(this.children_,regionRowMatchesChildNide);if(matchedChild===undefined){if(this.children_.length!==this.rule_.children.length){throw new Error('Internal error');}
+matchedChild=new VMRegionClassificationNode(VMRegionClassificationNode.OTHER_RULE);this.children_.push(matchedChild);}
 matchedChild.addRegionRecursively_(region,true);},buildTree_:function(){var cachedRegions=this.regions_;this.regions_=undefined;this.buildChildNodesRecursively_();for(var i=0;i<cachedRegions.length;i++){this.addRegionRecursively_(cachedRegions[i],false);}},buildChildNodesRecursively_:function(){if(this.children_!==undefined){throw new Error('Internal error: Classification node already has children');}
 if(this.regions_!==undefined&&this.regions_.length!==0){throw new Error('Internal error: Classification node should have no regions');}
-if(this.isLeafNode)
-return;this.regions_=undefined;this.children_=this.rule_.children.map(function(childRule){var child=new VMRegionClassificationNode(childRule);child.buildChildNodesRecursively_();return child;});},addStatsFromRegion_:function(region){this.hasRegions=true;var regionSizeInBytes=region.sizeInBytes;if(regionSizeInBytes!==undefined)
-this.sizeInBytes=(this.sizeInBytes||0)+regionSizeInBytes;var thisByteStats=this.byteStats;var regionByteStats=region.byteStats;for(var byteStatName in regionByteStats){var regionByteStatValue=regionByteStats[byteStatName];if(regionByteStatValue===undefined)
-continue;thisByteStats[byteStatName]=(thisByteStats[byteStatName]||0)+regionByteStatValue;}}};return{VMRegion:VMRegion,VMRegionClassificationNode:VMRegionClassificationNode};});'use strict';tr.exportTo('tr.model',function(){var DISCOUNTED_ALLOCATOR_NAMES=['winheap','malloc'];var TRACING_OVERHEAD_PATH=['allocated_objects','tracing_overhead'];var SIZE_NUMERIC_NAME=tr.model.MemoryAllocatorDump.SIZE_NUMERIC_NAME;var RESIDENT_SIZE_NUMERIC_NAME=tr.model.MemoryAllocatorDump.RESIDENT_SIZE_NUMERIC_NAME;function getSizeNumericValue(dump,sizeNumericName){var sizeNumeric=dump.numerics[sizeNumericName];if(sizeNumeric===undefined)
-return 0;return sizeNumeric.value;}
+if(this.isLeafNode){return;}
+this.regions_=undefined;this.children_=this.rule_.children.map(function(childRule){var child=new VMRegionClassificationNode(childRule);child.buildChildNodesRecursively_();return child;});},addStatsFromRegion_:function(region){this.hasRegions=true;var regionSizeInBytes=region.sizeInBytes;if(regionSizeInBytes!==undefined){this.sizeInBytes=(this.sizeInBytes||0)+regionSizeInBytes;}
+var thisByteStats=this.byteStats;var regionByteStats=region.byteStats;for(var byteStatName in regionByteStats){var regionByteStatValue=regionByteStats[byteStatName];if(regionByteStatValue===undefined)continue;thisByteStats[byteStatName]=(thisByteStats[byteStatName]||0)+regionByteStatValue;}}};return{VMRegion,VMRegionClassificationNode,};});'use strict';tr.exportTo('tr.model',function(){var DISCOUNTED_ALLOCATOR_NAMES=['winheap','malloc'];var TRACING_OVERHEAD_PATH=['allocated_objects','tracing_overhead'];var SIZE_NUMERIC_NAME=tr.model.MemoryAllocatorDump.SIZE_NUMERIC_NAME;var RESIDENT_SIZE_NUMERIC_NAME=tr.model.MemoryAllocatorDump.RESIDENT_SIZE_NUMERIC_NAME;function getSizeNumericValue(dump,sizeNumericName){var sizeNumeric=dump.numerics[sizeNumericName];if(sizeNumeric===undefined)return 0;return sizeNumeric.value;}
 function ProcessMemoryDump(globalMemoryDump,process,start){tr.model.ContainerMemoryDump.call(this,start);this.process=process;this.globalMemoryDump=globalMemoryDump;this.totals=undefined;this.vmRegions=undefined;this.heapDumps=undefined;this.tracingOverheadOwnershipSetUp_=false;this.tracingOverheadDiscountedFromVmRegions_=false;}
 ProcessMemoryDump.prototype={__proto__:tr.model.ContainerMemoryDump.prototype,get userFriendlyName(){return'Process memory dump at '+
-tr.b.Unit.byName.timeStampInMs.format(this.start);},get containerName(){return this.process.userFriendlyName;},get processMemoryDumps(){var dumps={};dumps[this.process.pid]=this;return dumps;},get hasOwnVmRegions(){return this.vmRegions!==undefined;},setUpTracingOverheadOwnership:function(opt_model){if(this.tracingOverheadOwnershipSetUp_)
-return;this.tracingOverheadOwnershipSetUp_=true;var tracingDump=this.getMemoryAllocatorDumpByFullName('tracing');if(tracingDump===undefined||tracingDump.owns!==undefined){return;}
-if(tracingDump.owns!==undefined)
-return;var hasDiscountedFromAllocatorDumps=DISCOUNTED_ALLOCATOR_NAMES.some(function(allocatorName){var allocatorDump=this.getMemoryAllocatorDumpByFullName(allocatorName);if(allocatorDump===undefined)
-return false;var nextPathIndex=0;var currentDump=allocatorDump;var currentFullName=allocatorName;for(;nextPathIndex<TRACING_OVERHEAD_PATH.length;nextPathIndex++){var childFullName=currentFullName+'/'+
-TRACING_OVERHEAD_PATH[nextPathIndex];var childDump=this.getMemoryAllocatorDumpByFullName(childFullName);if(childDump===undefined)
-break;currentDump=childDump;currentFullName=childFullName;}
+tr.b.Unit.byName.timeStampInMs.format(this.start);},get containerName(){return this.process.userFriendlyName;},get processMemoryDumps(){var dumps={};dumps[this.process.pid]=this;return dumps;},get hasOwnVmRegions(){return this.vmRegions!==undefined;},setUpTracingOverheadOwnership:function(opt_model){if(this.tracingOverheadOwnershipSetUp_)return;this.tracingOverheadOwnershipSetUp_=true;var tracingDump=this.getMemoryAllocatorDumpByFullName('tracing');if(tracingDump===undefined||tracingDump.owns!==undefined){return;}
+if(tracingDump.owns!==undefined)return;var hasDiscountedFromAllocatorDumps=DISCOUNTED_ALLOCATOR_NAMES.some(function(allocatorName){var allocatorDump=this.getMemoryAllocatorDumpByFullName(allocatorName);if(allocatorDump===undefined){return false;}
+var nextPathIndex=0;var currentDump=allocatorDump;var currentFullName=allocatorName;for(;nextPathIndex<TRACING_OVERHEAD_PATH.length;nextPathIndex++){var childFullName=currentFullName+'/'+
+TRACING_OVERHEAD_PATH[nextPathIndex];var childDump=this.getMemoryAllocatorDumpByFullName(childFullName);if(childDump===undefined)break;currentDump=childDump;currentFullName=childFullName;}
 for(;nextPathIndex<TRACING_OVERHEAD_PATH.length;nextPathIndex++){var childFullName=currentFullName+'/'+
 TRACING_OVERHEAD_PATH[nextPathIndex];var childDump=new tr.model.MemoryAllocatorDump(currentDump.containerMemoryDump,childFullName);childDump.parent=currentDump;currentDump.children.push(childDump);currentFullName=childFullName;currentDump=childDump;}
-var ownershipLink=new tr.model.MemoryAllocatorDumpLink(tracingDump,currentDump);tracingDump.owns=ownershipLink;currentDump.ownedBy.push(ownershipLink);return true;},this);if(hasDiscountedFromAllocatorDumps)
-this.forceRebuildingMemoryAllocatorDumpByFullNameIndex();},discountTracingOverheadFromVmRegions:function(opt_model){if(this.tracingOverheadDiscountedFromVmRegions_)
-return;this.tracingOverheadDiscountedFromVmRegions_=true;var tracingDump=this.getMemoryAllocatorDumpByFullName('tracing');if(tracingDump===undefined)
-return;var discountedSize=getSizeNumericValue(tracingDump,SIZE_NUMERIC_NAME);var discountedResidentSize=getSizeNumericValue(tracingDump,RESIDENT_SIZE_NUMERIC_NAME);if(discountedSize<=0&&discountedResidentSize<=0)
-return;if(this.totals!==undefined){if(this.totals.residentBytes!==undefined)
-this.totals.residentBytes-=discountedResidentSize;if(this.totals.peakResidentBytes!==undefined)
-this.totals.peakResidentBytes-=discountedResidentSize;}
-if(this.vmRegions!==undefined){var hasSizeInBytes=this.vmRegions.sizeInBytes!==undefined;var hasPrivateDirtyResident=this.vmRegions.byteStats.privateDirtyResident!==undefined;var hasProportionalResident=this.vmRegions.byteStats.proportionalResident!==undefined;if((hasSizeInBytes&&discountedSize>0)||((hasPrivateDirtyResident||hasProportionalResident)&&discountedResidentSize>0)){var byteStats={};if(hasPrivateDirtyResident)
-byteStats.privateDirtyResident=-discountedResidentSize;if(hasProportionalResident)
-byteStats.proportionalResident=-discountedResidentSize;this.vmRegions.addRegion(tr.model.VMRegion.fromDict({mappedFile:'[discounted tracing overhead]',sizeInBytes:hasSizeInBytes?-discountedSize:undefined,byteStats:byteStats}));}}}};ProcessMemoryDump.hookUpMostRecentVmRegionsLinks=function(processDumps){var mostRecentVmRegions=undefined;processDumps.forEach(function(processDump){if(processDump.vmRegions!==undefined)
-mostRecentVmRegions=processDump.vmRegions;processDump.mostRecentVmRegions=mostRecentVmRegions;});};tr.model.EventRegistry.register(ProcessMemoryDump,{name:'processMemoryDump',pluralName:'processMemoryDumps'});return{ProcessMemoryDump:ProcessMemoryDump};});'use strict';tr.exportTo('tr.model',function(){var ProcessBase=tr.model.ProcessBase;var ProcessInstantEvent=tr.model.ProcessInstantEvent;var Frame=tr.model.Frame;var ProcessMemoryDump=tr.model.ProcessMemoryDump;function Process(model,pid){if(model===undefined)
-throw new Error('model must be provided');if(pid===undefined)
-throw new Error('pid must be provided');tr.model.ProcessBase.call(this,model);this.pid=pid;this.name=undefined;this.labels=[];this.instantEvents=[];this.memoryDumps=[];this.frames=[];this.activities=[];};Process.compare=function(x,y){var tmp=tr.model.ProcessBase.compare(x,y);if(tmp)
-return tmp;tmp=tr.b.comparePossiblyUndefinedValues(x.name,y.name,function(x,y){return x.localeCompare(y);});if(tmp)
-return tmp;tmp=tr.b.compareArrays(x.labels,y.labels,function(x,y){return x.localeCompare(y);});if(tmp)
-return tmp;return x.pid-y.pid;};Process.prototype={__proto__:tr.model.ProcessBase.prototype,get stableId(){return this.pid;},compareTo:function(that){return Process.compare(this,that);},childEvents:function*(){yield*ProcessBase.prototype.childEvents.call(this);yield*this.instantEvents;yield*this.frames;yield*this.memoryDumps;},addLabelIfNeeded:function(labelName){for(var i=0;i<this.labels.length;i++){if(this.labels[i]===labelName)
-return;}
-this.labels.push(labelName);},get userFriendlyName(){var res;if(this.name)
-res=this.name+' (pid '+this.pid+')';else
-res='Process '+this.pid;if(this.labels.length)
-res+=': '+this.labels.join(', ');return res;},get userFriendlyDetails(){if(this.name)
-return this.name+' (pid '+this.pid+')';return'pid: '+this.pid;},getSettingsKey:function(){if(!this.name)
-return undefined;if(!this.labels.length)
-return'processes.'+this.name;return'processes.'+this.name+'.'+this.labels.join('.');},shiftTimestampsForward:function(amount){for(var i=0;i<this.instantEvents.length;i++)
-this.instantEvents[i].start+=amount;for(var i=0;i<this.frames.length;i++)
-this.frames[i].shiftTimestampsForward(amount);for(var i=0;i<this.memoryDumps.length;i++)
-this.memoryDumps[i].shiftTimestampsForward(amount);for(var i=0;i<this.activities.length;i++)
-this.activities[i].shiftTimestampsForward(amount);tr.model.ProcessBase.prototype.shiftTimestampsForward.apply(this,arguments);},updateBounds:function(){tr.model.ProcessBase.prototype.updateBounds.apply(this);for(var i=0;i<this.frames.length;i++)
-this.frames[i].addBoundsToRange(this.bounds);for(var i=0;i<this.memoryDumps.length;i++)
-this.memoryDumps[i].addBoundsToRange(this.bounds);for(var i=0;i<this.activities.length;i++)
-this.activities[i].addBoundsToRange(this.bounds);},sortMemoryDumps:function(){this.memoryDumps.sort(function(x,y){return x.start-y.start;});tr.model.ProcessMemoryDump.hookUpMostRecentVmRegionsLinks(this.memoryDumps);}};return{Process:Process};});'use strict';tr.exportTo('tr.model',function(){function Sample(cpu,thread,title,start,leafStackFrame,opt_weight,opt_args){tr.model.TimedEvent.call(this,start);this.title=title;this.cpu=cpu;this.thread=thread;this.leafStackFrame=leafStackFrame;this.weight=opt_weight;this.args=opt_args||{};}
-Sample.prototype={__proto__:tr.model.TimedEvent.prototype,get colorId(){return this.leafStackFrame.colorId;},get stackTrace(){return this.leafStackFrame.stackTrace;},getUserFriendlyStackTrace:function(){return this.leafStackFrame.getUserFriendlyStackTrace();},get userFriendlyName(){return'Sample at '+tr.b.Unit.byName.timeStampInMs.format(this.start);}};tr.model.EventRegistry.register(Sample,{name:'sample',pluralName:'samples'});return{Sample:Sample};});'use strict';tr.exportTo('tr.model',function(){function StackFrame(parentFrame,id,title,colorId,opt_sourceInfo){if(id===undefined)
-throw new Error('id must be given');this.parentFrame_=parentFrame;this.id=id;this.title_=title;this.colorId=colorId;this.children=[];this.sourceInfo_=opt_sourceInfo;if(this.parentFrame_)
-this.parentFrame_.addChild(this);}
+var ownershipLink=new tr.model.MemoryAllocatorDumpLink(tracingDump,currentDump);tracingDump.owns=ownershipLink;currentDump.ownedBy.push(ownershipLink);return true;},this);if(hasDiscountedFromAllocatorDumps){this.forceRebuildingMemoryAllocatorDumpByFullNameIndex();}},discountTracingOverheadFromVmRegions:function(opt_model){if(this.tracingOverheadDiscountedFromVmRegions_)return;this.tracingOverheadDiscountedFromVmRegions_=true;var tracingDump=this.getMemoryAllocatorDumpByFullName('tracing');if(tracingDump===undefined)return;var discountedSize=getSizeNumericValue(tracingDump,SIZE_NUMERIC_NAME);var discountedResidentSize=getSizeNumericValue(tracingDump,RESIDENT_SIZE_NUMERIC_NAME);if(discountedSize<=0&&discountedResidentSize<=0)return;if(this.totals!==undefined){if(this.totals.residentBytes!==undefined){this.totals.residentBytes-=discountedResidentSize;}
+if(this.totals.peakResidentBytes!==undefined){this.totals.peakResidentBytes-=discountedResidentSize;}}
+if(this.vmRegions!==undefined){var hasSizeInBytes=this.vmRegions.sizeInBytes!==undefined;var hasPrivateDirtyResident=this.vmRegions.byteStats.privateDirtyResident!==undefined;var hasProportionalResident=this.vmRegions.byteStats.proportionalResident!==undefined;if((hasSizeInBytes&&discountedSize>0)||((hasPrivateDirtyResident||hasProportionalResident)&&discountedResidentSize>0)){var byteStats={};if(hasPrivateDirtyResident){byteStats.privateDirtyResident=-discountedResidentSize;}
+if(hasProportionalResident){byteStats.proportionalResident=-discountedResidentSize;}
+this.vmRegions.addRegion(tr.model.VMRegion.fromDict({mappedFile:'[discounted tracing overhead]',sizeInBytes:hasSizeInBytes?-discountedSize:undefined,byteStats:byteStats}));}}}};ProcessMemoryDump.hookUpMostRecentVmRegionsLinks=function(processDumps){var mostRecentVmRegions=undefined;processDumps.forEach(function(processDump){if(processDump.vmRegions!==undefined){mostRecentVmRegions=processDump.vmRegions;}
+processDump.mostRecentVmRegions=mostRecentVmRegions;});};tr.model.EventRegistry.register(ProcessMemoryDump,{name:'processMemoryDump',pluralName:'processMemoryDumps'});return{ProcessMemoryDump,};});'use strict';tr.exportTo('tr.model',function(){var ProcessBase=tr.model.ProcessBase;var ProcessInstantEvent=tr.model.ProcessInstantEvent;var Frame=tr.model.Frame;var ProcessMemoryDump=tr.model.ProcessMemoryDump;function Process(model,pid){if(model===undefined){throw new Error('model must be provided');}
+if(pid===undefined){throw new Error('pid must be provided');}
+tr.model.ProcessBase.call(this,model);this.pid=pid;this.name=undefined;this.labels=[];this.uptime_seconds=0;this.instantEvents=[];this.memoryDumps=[];this.frames=[];this.activities=[];}
+Process.compare=function(x,y){var tmp=tr.model.ProcessBase.compare(x,y);if(tmp)return tmp;tmp=tr.b.comparePossiblyUndefinedValues(x.name,y.name,function(x,y){return x.localeCompare(y);});if(tmp)return tmp;tmp=tr.b.compareArrays(x.labels,y.labels,function(x,y){return x.localeCompare(y);});if(tmp)return tmp;return x.pid-y.pid;};Process.prototype={__proto__:tr.model.ProcessBase.prototype,get stableId(){return this.pid;},compareTo:function(that){return Process.compare(this,that);},childEvents:function*(){yield*ProcessBase.prototype.childEvents.call(this);yield*this.instantEvents;yield*this.frames;yield*this.memoryDumps;},addLabelIfNeeded:function(labelName){for(var i=0;i<this.labels.length;i++){if(this.labels[i]===labelName)return;}
+this.labels.push(labelName);},get userFriendlyName(){var res;if(this.name){res=this.name+' (pid '+this.pid+')';}else{res='Process '+this.pid;}
+if(this.labels.length){res+=': '+this.labels.join(', ');}
+if(this.uptime_seconds){res+=', uptime:'+this.uptime_seconds+'s';}
+return res;},get userFriendlyDetails(){if(this.name){return this.name+' (pid '+this.pid+')';}
+return'pid: '+this.pid;},getSettingsKey:function(){if(!this.name)return undefined;if(!this.labels.length)return'processes.'+this.name;return'processes.'+this.name+'.'+this.labels.join('.');},shiftTimestampsForward:function(amount){for(var i=0;i<this.instantEvents.length;i++){this.instantEvents[i].start+=amount;}
+for(var i=0;i<this.frames.length;i++){this.frames[i].shiftTimestampsForward(amount);}
+for(var i=0;i<this.memoryDumps.length;i++){this.memoryDumps[i].shiftTimestampsForward(amount);}
+for(var i=0;i<this.activities.length;i++){this.activities[i].shiftTimestampsForward(amount);}
+tr.model.ProcessBase.prototype.shiftTimestampsForward.apply(this,arguments);},updateBounds:function(){tr.model.ProcessBase.prototype.updateBounds.apply(this);for(var i=0;i<this.frames.length;i++){this.frames[i].addBoundsToRange(this.bounds);}
+for(var i=0;i<this.memoryDumps.length;i++){this.memoryDumps[i].addBoundsToRange(this.bounds);}
+for(var i=0;i<this.activities.length;i++){this.activities[i].addBoundsToRange(this.bounds);}},sortMemoryDumps:function(){this.memoryDumps.sort(function(x,y){return x.start-y.start;});tr.model.ProcessMemoryDump.hookUpMostRecentVmRegionsLinks(this.memoryDumps);}};return{Process,};});'use strict';tr.exportTo('tr.model',function(){function Sample(start,title,leafNode,thread,opt_cpu,opt_weight,opt_args){tr.model.TimedEvent.call(this,start);this.start_=start;this.title_=title;this.leafNode_=leafNode;this.thread_=thread;this.colorId_=leafNode.colorId;this.cpu_=opt_cpu;this.weight_=opt_weight;this.args=opt_args||{};}
+Sample.prototype={__proto__:tr.model.TimedEvent.prototype,get title(){return this.title_;},get colorId(){return this.colorId_;},get thread(){return this.thread_;},get leafNode(){return this.leafNode_;},get userFriendlyName(){return'Sample at '+
+tr.b.Unit.byName.timeStampInMs.format(this.start);},get userFriendlyStack(){return this.leafNode_.userFriendlyStack;},getNodesAsArray(){var nodes=[];let node=this.leafNode_;while(node!==undefined){nodes.push(node);node=node.parentNode;}
+return nodes;},get cpu(){return this.cpu_;},get weight(){return this.weight_;},};tr.model.EventRegistry.register(Sample,{name:'Sample',pluralName:'Samples'});return{Sample,};});'use strict';tr.exportTo('tr.model',function(){function StackFrame(parentFrame,id,title,colorId,opt_sourceInfo){if(id===undefined){throw new Error('id must be given');}
+this.parentFrame_=parentFrame;this.id=id;this.title_=title;this.colorId=colorId;this.children=[];this.sourceInfo_=opt_sourceInfo;if(this.parentFrame_){this.parentFrame_.addChild(this);}}
 StackFrame.prototype={get parentFrame(){return this.parentFrame_;},get title(){if(this.sourceInfo_){var src=this.sourceInfo_.toString();return this.title_+(src===''?'':' '+src);}
-return this.title_;},get domain(){var result='unknown';if(this.sourceInfo_&&this.sourceInfo_.domain)
-result=this.sourceInfo_.domain;if(result==='unknown'&&this.parentFrame)
-result=this.parentFrame.domain;return result;},get sourceInfo(){return this.sourceInfo_;},set parentFrame(parentFrame){if(this.parentFrame_)
-Polymer.dom(this.parentFrame_).removeChild(this);this.parentFrame_=parentFrame;if(this.parentFrame_)
-this.parentFrame_.addChild(this);},addChild:function(child){this.children.push(child);},removeChild:function(child){var i=this.children.indexOf(child.id);if(i==-1)
-throw new Error('omg');this.children.splice(i,1);},removeAllChildren:function(){for(var i=0;i<this.children.length;i++)
-this.children[i].parentFrame_=undefined;this.children.splice(0,this.children.length);},get stackTrace(){var stack=[];var cur=this;while(cur){stack.push(cur);cur=cur.parentFrame;}
-return stack;},getUserFriendlyStackTrace:function(){return this.stackTrace.map(function(x){return x.title;});}};return{StackFrame:StackFrame};});'use strict';tr.exportTo('tr.model.um',function(){function UserModel(parentModel){tr.model.EventContainer.call(this);this.parentModel_=parentModel;this.expectations_=new tr.model.EventSet();}
-UserModel.prototype={__proto__:tr.model.EventContainer.prototype,get stableId(){return'UserModel';},get parentModel(){return this.parentModel_;},sortExpectations:function(){this.expectations_.sortEvents((x,y)=>(x.start-y.start));},get expectations(){return this.expectations_;},shiftTimestampsForward:function(amount){},addCategoriesToDict:function(categoriesDict){},childEvents:function*(){yield*this.expectations;},childEventContainers:function*(){},updateBounds:function(){this.bounds.reset();this.expectations.forEach(function(expectation){expectation.addBoundsToRange(this.bounds);},this);}};return{UserModel:UserModel};});'use strict';tr.exportTo('tr',function(){var Process=tr.model.Process;var Device=tr.model.Device;var Kernel=tr.model.Kernel;var GlobalMemoryDump=tr.model.GlobalMemoryDump;var GlobalInstantEvent=tr.model.GlobalInstantEvent;var FlowEvent=tr.model.FlowEvent;var Alert=tr.model.Alert;var Sample=tr.model.Sample;function Model(){tr.model.EventContainer.call(this);tr.b.EventTarget.decorate(this);this.timestampShiftToZeroAmount_=0;this.faviconHue='blue';this.device=new Device(this);this.kernel=new Kernel(this);this.processes={};this.metadata=[];this.categories=[];this.instantEvents=[];this.flowEvents=[];this.clockSyncManager=new tr.model.ClockSyncManager();this.intrinsicTimeUnit_=undefined;this.stackFrames={};this.samples=[];this.alerts=[];this.userModel=new tr.model.um.UserModel(this);this.flowIntervalTree=new tr.b.IntervalTree((f)=>f.start,(f)=>f.end);this.globalMemoryDumps=[];this.userFriendlyCategoryDrivers_=[];this.annotationsByGuid_={};this.modelIndices=undefined;this.stats=new tr.model.ModelStats();this.importWarnings_=[];this.reportedImportWarnings_={};this.isTimeHighResolution_=true;this.patchupsToApply_=[];this.doesHelperGUIDSupportThisModel_={};this.helpersByConstructorGUID_={};this.eventsByStableId_=undefined;}
+return this.title_;},get domain(){var result='unknown';if(this.sourceInfo_&&this.sourceInfo_.domain){result=this.sourceInfo_.domain;}
+if(result==='unknown'&&this.parentFrame){result=this.parentFrame.domain;}
+return result;},get sourceInfo(){return this.sourceInfo_;},set parentFrame(parentFrame){if(this.parentFrame_){Polymer.dom(this.parentFrame_).removeChild(this);}
+this.parentFrame_=parentFrame;if(this.parentFrame_){this.parentFrame_.addChild(this);}},addChild:function(child){this.children.push(child);},removeChild:function(child){var i=this.children.indexOf(child.id);if(i===-1){throw new Error('omg');}
+this.children.splice(i,1);},removeAllChildren:function(){for(var i=0;i<this.children.length;i++){this.children[i].parentFrame_=undefined;}
+this.children.splice(0,this.children.length);},get stackTrace(){var stack=[this];var cur=this.parentFrame;while(cur){stack.push(cur);cur=cur.parentFrame;}
+return stack;},getUserFriendlyStackTrace:function(){return this.stackTrace.map(function(x){return x.title;});}};return{StackFrame,};});'use strict';tr.exportTo('tr.model.um',function(){class Segment extends tr.model.TimedEvent{constructor(start,duration){super(start);this.duration=duration;this.expectations_=[];}
+get expectations(){return this.expectations_;}
+clone(){var clone=new Segment(this.start,this.duration);clone.expectations.push(...this.expectations);return clone;}
+addSegment(other){this.duration+=other.duration;this.expectations.push(...other.expectations);}}
+return{Segment,};});'use strict';tr.exportTo('tr.model.um',function(){class UserModel extends tr.model.EventContainer{constructor(parentModel){super();this.parentModel_=parentModel;this.expectations_=new tr.model.EventSet();this.segments_=[];}
+get stableId(){return'UserModel';}
+get parentModel(){return this.parentModel_;}
+sortExpectations(){this.expectations_.sortEvents((x,y)=>(x.start-y.start));}
+get expectations(){return this.expectations_;}
+shiftTimestampsForward(amount){}
+addCategoriesToDict(categoriesDict){}
+get segments(){return this.segments_;}*childEvents(){yield*this.expectations;}*childEventContainers(){}
+updateBounds(){this.bounds.reset();for(var expectation of this.expectations){expectation.addBoundsToRange(this.bounds);}}
+resegment(getKeyForSegment){var newSegments=[];var prevKey=undefined;var prevSegment=undefined;for(var i=0;i<this.segments.length;++i){var segment=this.segments[i];var key=getKeyForSegment(segment,i);if(prevSegment!==undefined&&key===prevKey){prevSegment.addSegment(segment);}else{prevSegment=segment.clone();newSegments.push(prevSegment);}
+prevKey=key;}
+return newSegments;}}
+return{UserModel,};});'use strict';tr.exportTo('tr',function(){var Process=tr.model.Process;var Device=tr.model.Device;var Kernel=tr.model.Kernel;var GlobalMemoryDump=tr.model.GlobalMemoryDump;var GlobalInstantEvent=tr.model.GlobalInstantEvent;var FlowEvent=tr.model.FlowEvent;var Alert=tr.model.Alert;var Sample=tr.model.Sample;function Model(){tr.model.EventContainer.call(this);tr.b.EventTarget.decorate(this);this.timestampShiftToZeroAmount_=0;this.faviconHue='blue';this.device=new Device(this);this.kernel=new Kernel(this);this.processes={};this.metadata=[];this.categories=[];this.instantEvents=[];this.flowEvents=[];this.clockSyncManager=new tr.model.ClockSyncManager();this.intrinsicTimeUnit_=undefined;this.stackFrames={};this.samples=[];this.alerts=[];this.userModel=new tr.model.um.UserModel(this);this.flowIntervalTree=new tr.b.IntervalTree((f)=>f.start,(f)=>f.end);this.globalMemoryDumps=[];this.userFriendlyCategoryDrivers_=[];this.annotationsByGuid_={};this.modelIndices=undefined;this.stats=new tr.model.ModelStats();this.importWarnings_=[];this.reportedImportWarnings_={};this.isTimeHighResolution_=true;this.patchupsToApply_=[];this.doesHelperGUIDSupportThisModel_={};this.helpersByConstructorGUID_={};this.eventsByStableId_=undefined;}
 Model.prototype={__proto__:tr.model.EventContainer.prototype,getEventByStableId:function(stableId){if(this.eventsByStableId_===undefined){this.eventsByStableId_={};for(var event of this.getDescendantEvents()){this.eventsByStableId_[event.stableId]=event;}}
-return this.eventsByStableId_[stableId];},getOrCreateHelper:function(constructor){if(!constructor.guid)
-throw new Error('Helper constructors must have GUIDs');if(this.helpersByConstructorGUID_[constructor.guid]===undefined){if(this.doesHelperGUIDSupportThisModel_[constructor.guid]===undefined){this.doesHelperGUIDSupportThisModel_[constructor.guid]=constructor.supportsModel(this);}
-if(!this.doesHelperGUIDSupportThisModel_[constructor.guid])
-return undefined;this.helpersByConstructorGUID_[constructor.guid]=new constructor(this);}
-return this.helpersByConstructorGUID_[constructor.guid];},childEvents:function*(){yield*this.globalMemoryDumps;yield*this.instantEvents;yield*this.flowEvents;yield*this.alerts;yield*this.samples;},childEventContainers:function*(){yield this.userModel;yield this.device;yield this.kernel;yield*tr.b.dictionaryValues(this.processes);},iterateAllPersistableObjects:function(callback){this.kernel.iterateAllPersistableObjects(callback);for(var pid in this.processes)
-this.processes[pid].iterateAllPersistableObjects(callback);},updateBounds:function(){this.bounds.reset();var bounds=this.bounds;for(var ec of this.childEventContainers()){ec.updateBounds();bounds.addRange(ec.bounds);}
-for(var event of this.childEvents())
-event.addBoundsToRange(bounds);},shiftWorldToZero:function(){var shiftAmount=-this.bounds.min;this.timestampShiftToZeroAmount_=shiftAmount;for(var ec of this.childEventContainers())
-ec.shiftTimestampsForward(shiftAmount);for(var event of this.childEvents())
-event.start+=shiftAmount;this.updateBounds();},convertTimestampToModelTime:function(sourceClockDomainName,ts){if(sourceClockDomainName!=='traceEventClock')
-throw new Error('Only traceEventClock is supported.');return tr.b.Unit.timestampFromUs(ts)+
-this.timestampShiftToZeroAmount_;},get numProcesses(){var n=0;for(var p in this.processes)
-n++;return n;},getProcess:function(pid){return this.processes[pid];},getOrCreateProcess:function(pid){if(!this.processes[pid])
-this.processes[pid]=new Process(this,pid);return this.processes[pid];},addStackFrame:function(stackFrame){if(this.stackFrames[stackFrame.id])
-throw new Error('Stack frame already exists');this.stackFrames[stackFrame.id]=stackFrame;return stackFrame;},updateCategories_:function(){var categoriesDict={};this.userModel.addCategoriesToDict(categoriesDict);this.device.addCategoriesToDict(categoriesDict);this.kernel.addCategoriesToDict(categoriesDict);for(var pid in this.processes)
-this.processes[pid].addCategoriesToDict(categoriesDict);this.categories=[];for(var category in categoriesDict)
-if(category!='')
-this.categories.push(category);},getAllThreads:function(){var threads=[];for(var tid in this.kernel.threads){threads.push(process.threads[tid]);}
+return this.eventsByStableId_[stableId];},getOrCreateHelper:function(constructor){if(!constructor.guid){throw new Error('Helper constructors must have GUIDs');}
+if(this.helpersByConstructorGUID_[constructor.guid]===undefined){if(this.doesHelperGUIDSupportThisModel_[constructor.guid]===undefined){this.doesHelperGUIDSupportThisModel_[constructor.guid]=constructor.supportsModel(this);}
+if(!this.doesHelperGUIDSupportThisModel_[constructor.guid]){return undefined;}
+this.helpersByConstructorGUID_[constructor.guid]=new constructor(this);}
+return this.helpersByConstructorGUID_[constructor.guid];},childEvents:function*(){yield*this.globalMemoryDumps;yield*this.instantEvents;yield*this.flowEvents;yield*this.alerts;yield*this.samples;},childEventContainers:function*(){yield this.userModel;yield this.device;yield this.kernel;yield*tr.b.dictionaryValues(this.processes);},iterateAllPersistableObjects:function(callback){this.kernel.iterateAllPersistableObjects(callback);for(var pid in this.processes){this.processes[pid].iterateAllPersistableObjects(callback);}},updateBounds:function(){this.bounds.reset();var bounds=this.bounds;for(var ec of this.childEventContainers()){ec.updateBounds();bounds.addRange(ec.bounds);}
+for(var event of this.childEvents()){event.addBoundsToRange(bounds);}},shiftWorldToZero:function(){var shiftAmount=-this.bounds.min;this.timestampShiftToZeroAmount_=shiftAmount;for(var ec of this.childEventContainers()){ec.shiftTimestampsForward(shiftAmount);}
+for(var event of this.childEvents()){event.start+=shiftAmount;}
+this.updateBounds();},convertTimestampToModelTime:function(sourceClockDomainName,ts){if(sourceClockDomainName!=='traceEventClock'){throw new Error('Only traceEventClock is supported.');}
+return tr.b.Unit.timestampFromUs(ts)+
+this.timestampShiftToZeroAmount_;},get numProcesses(){var n=0;for(var p in this.processes){n++;}
+return n;},getProcess:function(pid){return this.processes[pid];},getOrCreateProcess:function(pid){if(!this.processes[pid]){this.processes[pid]=new Process(this,pid);}
+return this.processes[pid];},addStackFrame:function(stackFrame){if(this.stackFrames[stackFrame.id]){throw new Error('Stack frame already exists');}
+this.stackFrames[stackFrame.id]=stackFrame;return stackFrame;},updateCategories_:function(){var categoriesDict={};this.userModel.addCategoriesToDict(categoriesDict);this.device.addCategoriesToDict(categoriesDict);this.kernel.addCategoriesToDict(categoriesDict);for(var pid in this.processes){this.processes[pid].addCategoriesToDict(categoriesDict);}
+this.categories=[];for(var category in categoriesDict){if(category!==''){this.categories.push(category);}}},getAllThreads:function(){var threads=[];for(var tid in this.kernel.threads){threads.push(process.threads[tid]);}
 for(var pid in this.processes){var process=this.processes[pid];for(var tid in process.threads){threads.push(process.threads[tid]);}}
-return threads;},getAllProcesses:function(opt_predicate){var processes=[];for(var pid in this.processes){var process=this.processes[pid];if(opt_predicate===undefined||opt_predicate(process))
-processes.push(process);}
+return threads;},getAllProcesses:function(opt_predicate){var processes=[];for(var pid in this.processes){var process=this.processes[pid];if(opt_predicate===undefined||opt_predicate(process)){processes.push(process);}}
 return processes;},getAllCounters:function(){var counters=[];counters.push.apply(counters,tr.b.dictionaryValues(this.device.counters));counters.push.apply(counters,tr.b.dictionaryValues(this.kernel.counters));for(var pid in this.processes){var process=this.processes[pid];for(var tid in process.counters){counters.push(process.counters[tid]);}}
-return counters;},getAnnotationByGUID:function(guid){return this.annotationsByGuid_[guid];},addAnnotation:function(annotation){if(!annotation.guid)
-throw new Error('Annotation with undefined guid given');this.annotationsByGuid_[annotation.guid]=annotation;tr.b.dispatchSimpleEvent(this,'annotationChange');},removeAnnotation:function(annotation){this.annotationsByGuid_[annotation.guid].onRemove();delete this.annotationsByGuid_[annotation.guid];tr.b.dispatchSimpleEvent(this,'annotationChange');},getAllAnnotations:function(){return tr.b.dictionaryValues(this.annotationsByGuid_);},addUserFriendlyCategoryDriver:function(ufcd){this.userFriendlyCategoryDrivers_.push(ufcd);},getUserFriendlyCategoryFromEvent:function(event){for(var i=0;i<this.userFriendlyCategoryDrivers_.length;i++){var ufc=this.userFriendlyCategoryDrivers_[i].fromEvent(event);if(ufc!==undefined)
-return ufc;}
+return counters;},getAnnotationByGUID:function(guid){return this.annotationsByGuid_[guid];},addAnnotation:function(annotation){if(!annotation.guid){throw new Error('Annotation with undefined guid given');}
+this.annotationsByGuid_[annotation.guid]=annotation;tr.b.dispatchSimpleEvent(this,'annotationChange');},removeAnnotation:function(annotation){this.annotationsByGuid_[annotation.guid].onRemove();delete this.annotationsByGuid_[annotation.guid];tr.b.dispatchSimpleEvent(this,'annotationChange');},getAllAnnotations:function(){return tr.b.dictionaryValues(this.annotationsByGuid_);},addUserFriendlyCategoryDriver:function(ufcd){this.userFriendlyCategoryDrivers_.push(ufcd);},getUserFriendlyCategoryFromEvent:function(event){for(var i=0;i<this.userFriendlyCategoryDrivers_.length;i++){var ufc=this.userFriendlyCategoryDrivers_[i].fromEvent(event);if(ufc!==undefined)return ufc;}
 return undefined;},findAllThreadsNamed:function(name){var namedThreads=[];namedThreads.push.apply(namedThreads,this.kernel.findAllThreadsNamed(name));for(var pid in this.processes){namedThreads.push.apply(namedThreads,this.processes[pid].findAllThreadsNamed(name));}
-return namedThreads;},get importOptions(){return this.importOptions_;},set importOptions(options){this.importOptions_=options;},get intrinsicTimeUnit(){if(this.intrinsicTimeUnit_===undefined)
-return tr.b.TimeDisplayModes.ms;return this.intrinsicTimeUnit_;},set intrinsicTimeUnit(value){if(this.intrinsicTimeUnit_===value)
-return;if(this.intrinsicTimeUnit_!==undefined)
-throw new Error('Intrinsic time unit already set');this.intrinsicTimeUnit_=value;},get isTimeHighResolution(){return this.isTimeHighResolution_;},set isTimeHighResolution(value){this.isTimeHighResolution_=value;},get canonicalUrl(){return this.canonicalUrl_;},set canonicalUrl(value){if(this.canonicalUrl_===value)
-return;if(this.canonicalUrl_!==undefined)
-throw new Error('canonicalUrl already set');this.canonicalUrl_=value;},importWarning:function(data){data.showToUser=!!data.showToUser;this.importWarnings_.push(data);if(this.reportedImportWarnings_[data.type]===true)
-return;if(this.importOptions_.showImportWarnings)
-console.warn(data.message);this.reportedImportWarnings_[data.type]=true;},get hasImportWarnings(){return(this.importWarnings_.length>0);},get importWarnings(){return this.importWarnings_;},get importWarningsThatShouldBeShownToUser(){return this.importWarnings_.filter(function(warning){return warning.showToUser;});},autoCloseOpenSlices:function(){this.samples.sort(function(x,y){return x.start-y.start;});this.updateBounds();this.kernel.autoCloseOpenSlices();for(var pid in this.processes)
-this.processes[pid].autoCloseOpenSlices();},createSubSlices:function(){this.kernel.createSubSlices();for(var pid in this.processes)
-this.processes[pid].createSubSlices();},preInitializeObjects:function(){for(var pid in this.processes)
-this.processes[pid].preInitializeObjects();},initializeObjects:function(){for(var pid in this.processes)
-this.processes[pid].initializeObjects();},pruneEmptyContainers:function(){this.kernel.pruneEmptyContainers();for(var pid in this.processes)
-this.processes[pid].pruneEmptyContainers();},mergeKernelWithUserland:function(){for(var pid in this.processes)
-this.processes[pid].mergeKernelWithUserland();},computeWorldBounds:function(shiftWorldToZero){this.updateBounds();this.updateCategories_();if(shiftWorldToZero)
-this.shiftWorldToZero();},buildFlowEventIntervalTree:function(){for(var i=0;i<this.flowEvents.length;++i){var flowEvent=this.flowEvents[i];this.flowIntervalTree.insert(flowEvent);}
-this.flowIntervalTree.updateHighValues();},cleanupUndeletedObjects:function(){for(var pid in this.processes)
-this.processes[pid].autoDeleteObjects(this.bounds.max);},sortMemoryDumps:function(){this.globalMemoryDumps.sort(function(x,y){return x.start-y.start;});for(var pid in this.processes)
-this.processes[pid].sortMemoryDumps();},finalizeMemoryGraphs:function(){this.globalMemoryDumps.forEach(function(dump){dump.finalizeGraph();});},buildEventIndices:function(){this.modelIndices=new tr.model.ModelIndices(this);},sortAlerts:function(){this.alerts.sort(function(x,y){return x.start-y.start;});},applyObjectRefPatchups:function(){var unresolved=[];this.patchupsToApply_.forEach(function(patchup){if(patchup.pidRef in this.processes){var snapshot=this.processes[patchup.pidRef].objects.getSnapshotAt(patchup.scopedId,patchup.ts);if(snapshot){patchup.object[patchup.field]=snapshot;snapshot.referencedAt(patchup.item,patchup.object,patchup.field);return;}}
-unresolved.push(patchup);},this);this.patchupsToApply_=unresolved;},replacePIDRefsInPatchups:function(old_pid_ref,new_pid_ref){this.patchupsToApply_.forEach(function(patchup){if(patchup.pidRef===old_pid_ref)
-patchup.pidRef=new_pid_ref;});},joinRefs:function(){this.joinObjectRefs_();this.applyObjectRefPatchups();},joinObjectRefs_:function(){tr.b.iterItems(this.processes,function(pid,process){this.joinObjectRefsForProcess_(pid,process);},this);},joinObjectRefsForProcess_:function(pid,process){tr.b.iterItems(process.threads,function(tid,thread){thread.asyncSliceGroup.slices.forEach(function(item){this.searchItemForIDRefs_(pid,'start',item);},this);thread.sliceGroup.slices.forEach(function(item){this.searchItemForIDRefs_(pid,'start',item);},this);},this);process.objects.iterObjectInstances(function(instance){instance.snapshots.forEach(function(item){this.searchItemForIDRefs_(pid,'ts',item);},this);},this);},searchItemForIDRefs_:function(pid,itemTimestampField,item){if(!item.args&&!item.contexts)
-return;var patchupsToApply=this.patchupsToApply_;function handleField(object,fieldName,fieldValue){if(!fieldValue||(!fieldValue.id_ref&&!fieldValue.idRef))
-return;var scope=fieldValue.scope||tr.model.OBJECT_DEFAULT_SCOPE;var idRef=fieldValue.id_ref||fieldValue.idRef;var scopedId=new tr.model.ScopedId(scope,idRef);var pidRef=fieldValue.pid_ref||fieldValue.pidRef||pid;var ts=item[itemTimestampField];patchupsToApply.push({item:item,object:object,field:fieldName,pidRef:pidRef,scopedId:scopedId,ts:ts});}
-function iterObjectFieldsRecursively(object){if(!(object instanceof Object))
-return;if((object instanceof tr.model.ObjectSnapshot)||(object instanceof Float32Array)||(object instanceof tr.b.Quad))
-return;if(object instanceof Array){for(var i=0;i<object.length;i++){handleField(object,i,object[i]);iterObjectFieldsRecursively(object[i]);}
+return namedThreads;},get importOptions(){return this.importOptions_;},set importOptions(options){this.importOptions_=options;},get intrinsicTimeUnit(){if(this.intrinsicTimeUnit_===undefined){return tr.b.TimeDisplayModes.ms;}
+return this.intrinsicTimeUnit_;},set intrinsicTimeUnit(value){if(this.intrinsicTimeUnit_===value)return;if(this.intrinsicTimeUnit_!==undefined){throw new Error('Intrinsic time unit already set');}
+this.intrinsicTimeUnit_=value;},get isTimeHighResolution(){return this.isTimeHighResolution_;},set isTimeHighResolution(value){this.isTimeHighResolution_=value;},get canonicalUrl(){return this.canonicalUrl_;},set canonicalUrl(value){if(this.canonicalUrl_===value)return;if(this.canonicalUrl_!==undefined){throw new Error('canonicalUrl already set');}
+this.canonicalUrl_=value;},importWarning:function(data){data.showToUser=!!data.showToUser;this.importWarnings_.push(data);if(this.reportedImportWarnings_[data.type]===true)return;this.reportedImportWarnings_[data.type]=true;},get hasImportWarnings(){return(this.importWarnings_.length>0);},get importWarnings(){return this.importWarnings_;},get importWarningsThatShouldBeShownToUser(){return this.importWarnings_.filter(function(warning){return warning.showToUser;});},autoCloseOpenSlices:function(){this.samples.sort(function(x,y){return x.start-y.start;});this.updateBounds();this.kernel.autoCloseOpenSlices();for(var pid in this.processes){this.processes[pid].autoCloseOpenSlices();}},createSubSlices:function(){this.kernel.createSubSlices();for(var pid in this.processes){this.processes[pid].createSubSlices();}},preInitializeObjects:function(){for(var pid in this.processes){this.processes[pid].preInitializeObjects();}},initializeObjects:function(){for(var pid in this.processes){this.processes[pid].initializeObjects();}},pruneEmptyContainers:function(){this.kernel.pruneEmptyContainers();for(var pid in this.processes){this.processes[pid].pruneEmptyContainers();}},mergeKernelWithUserland:function(){for(var pid in this.processes){this.processes[pid].mergeKernelWithUserland();}},computeWorldBounds:function(shiftWorldToZero){this.updateBounds();this.updateCategories_();if(shiftWorldToZero){this.shiftWorldToZero();}},buildFlowEventIntervalTree:function(){for(var i=0;i<this.flowEvents.length;++i){var flowEvent=this.flowEvents[i];this.flowIntervalTree.insert(flowEvent);}
+this.flowIntervalTree.updateHighValues();},cleanupUndeletedObjects:function(){for(var pid in this.processes){this.processes[pid].autoDeleteObjects(this.bounds.max);}},sortMemoryDumps:function(){this.globalMemoryDumps.sort(function(x,y){return x.start-y.start;});for(var pid in this.processes){this.processes[pid].sortMemoryDumps();}},finalizeMemoryGraphs:function(){this.globalMemoryDumps.forEach(function(dump){dump.finalizeGraph();});},buildEventIndices:function(){this.modelIndices=new tr.model.ModelIndices(this);},sortAlerts:function(){this.alerts.sort(function(x,y){return x.start-y.start;});},applyObjectRefPatchups:function(){var unresolved=[];this.patchupsToApply_.forEach(function(patchup){if(patchup.pidRef in this.processes){var snapshot=this.processes[patchup.pidRef].objects.getSnapshotAt(patchup.scopedId,patchup.ts);if(snapshot){patchup.object[patchup.field]=snapshot;snapshot.referencedAt(patchup.item,patchup.object,patchup.field);return;}}
+unresolved.push(patchup);},this);this.patchupsToApply_=unresolved;},replacePIDRefsInPatchups:function(oldPidRef,newPidRef){this.patchupsToApply_.forEach(function(patchup){if(patchup.pidRef===oldPidRef){patchup.pidRef=newPidRef;}});},joinRefs:function(){this.joinObjectRefs_();this.applyObjectRefPatchups();},joinObjectRefs_:function(){for(var[pid,process]of Object.entries(this.processes)){this.joinObjectRefsForProcess_(pid,process);}},joinObjectRefsForProcess_:function(pid,process){for(var thread of Object.values(process.threads)){thread.asyncSliceGroup.slices.forEach(function(item){this.searchItemForIDRefs_(pid,'start',item);},this);thread.sliceGroup.slices.forEach(function(item){this.searchItemForIDRefs_(pid,'start',item);},this);}
+process.objects.iterObjectInstances(function(instance){instance.snapshots.forEach(function(item){this.searchItemForIDRefs_(pid,'ts',item);},this);},this);},searchItemForIDRefs_:function(pid,itemTimestampField,item){if(!item.args&&!item.contexts)return;var patchupsToApply=this.patchupsToApply_;function handleField(object,fieldName,fieldValue){if(!fieldValue||(!fieldValue.id_ref&&!fieldValue.idRef)){return;}
+var scope=fieldValue.scope||tr.model.OBJECT_DEFAULT_SCOPE;var idRef=fieldValue.id_ref||fieldValue.idRef;var scopedId=new tr.model.ScopedId(scope,idRef);var pidRef=fieldValue.pid_ref||fieldValue.pidRef||pid;var ts=item[itemTimestampField];patchupsToApply.push({item:item,object:object,field:fieldName,pidRef:pidRef,scopedId:scopedId,ts:ts});}
+function iterObjectFieldsRecursively(object){if(!(object instanceof Object))return;if((object instanceof tr.model.ObjectSnapshot)||(object instanceof Float32Array)||(object instanceof tr.b.math.Quad)){return;}
+if(object instanceof Array){for(var i=0;i<object.length;i++){handleField(object,i,object[i]);iterObjectFieldsRecursively(object[i]);}
 return;}
 for(var key in object){var value=object[key];handleField(object,key,value);iterObjectFieldsRecursively(value);}}
-iterObjectFieldsRecursively(item.args);iterObjectFieldsRecursively(item.contexts);}};return{Model:Model};});'use strict';tr.exportTo('tr.e.importer.etw',function(){var kThreadGuid='3D6FA8D1-FE05-11D0-9DDA-00C04FD7BA7C';var kThreadDCStartOpcode=3;function Decoder(){this.payload_=new DataView(new ArrayBuffer(256));};Decoder.prototype={__proto__:Object.prototype,reset:function(base64_payload){var decoded_size=tr.b.Base64.getDecodedBufferLength(base64_payload);if(decoded_size>this.payload_.byteLength)
-this.payload_=new DataView(new ArrayBuffer(decoded_size));tr.b.Base64.DecodeToTypedArray(base64_payload,this.payload_);this.position_=0;},skip:function(length){this.position_+=length;},decodeUInt8:function(){var result=this.payload_.getUint8(this.position_,true);this.position_+=1;return result;},decodeUInt16:function(){var result=this.payload_.getUint16(this.position_,true);this.position_+=2;return result;},decodeUInt32:function(){var result=this.payload_.getUint32(this.position_,true);this.position_+=4;return result;},decodeUInt64ToString:function(){var low=this.decodeUInt32();var high=this.decodeUInt32();var low_str=('0000000'+low.toString(16)).substr(-8);var high_str=('0000000'+high.toString(16)).substr(-8);var result=high_str+low_str;return result;},decodeInt8:function(){var result=this.payload_.getInt8(this.position_,true);this.position_+=1;return result;},decodeInt16:function(){var result=this.payload_.getInt16(this.position_,true);this.position_+=2;return result;},decodeInt32:function(){var result=this.payload_.getInt32(this.position_,true);this.position_+=4;return result;},decodeInt64ToString:function(){return this.decodeUInt64ToString();},decodeUInteger:function(is64){if(is64)
-return this.decodeUInt64ToString();return this.decodeUInt32();},decodeString:function(){var str='';while(true){var c=this.decodeUInt8();if(!c)
-return str;str=str+String.fromCharCode(c);}},decodeW16String:function(){var str='';while(true){var c=this.decodeUInt16();if(!c)
-return str;str=str+String.fromCharCode(c);}},decodeFixedW16String:function(length){var old_position=this.position_;var str='';for(var i=0;i<length;i++){var c=this.decodeUInt16();if(!c)
-break;str=str+String.fromCharCode(c);}
-this.position_=old_position+2*length;return str;},decodeBytes:function(length){var bytes=[];for(var i=0;i<length;++i){var c=this.decodeUInt8();bytes.push(c);}
-return bytes;},decodeSID:function(is64){var pSid=this.decodeUInteger(is64);var attributes=this.decodeUInt32();if(is64)
-this.decodeUInt32();var revision=this.decodeUInt8();var subAuthorityCount=this.decodeUInt8();this.decodeUInt16();this.decodeUInt32();if(revision!=1)
-throw'Invalid SID revision: could not decode the SID structure.';var sid=this.decodeBytes(4*subAuthorityCount);return{pSid:pSid,attributes:attributes,sid:sid};},decodeSystemTime:function(){var wYear=this.decodeInt16();var wMonth=this.decodeInt16();var wDayOfWeek=this.decodeInt16();var wDay=this.decodeInt16();var wHour=this.decodeInt16();var wMinute=this.decodeInt16();var wSecond=this.decodeInt16();var wMilliseconds=this.decodeInt16();return{wYear:wYear,wMonth:wMonth,wDayOfWeek:wDayOfWeek,wDay:wDay,wHour:wHour,wMinute:wMinute,wSecond:wSecond,wMilliseconds:wMilliseconds};},decodeTimeZoneInformation:function(){var bias=this.decodeUInt32();var standardName=this.decodeFixedW16String(32);var standardDate=this.decodeSystemTime();var standardBias=this.decodeUInt32();var daylightName=this.decodeFixedW16String(32);var daylightDate=this.decodeSystemTime();var daylightBias=this.decodeUInt32();return{bias:bias,standardName:standardName,standardDate:standardDate,standardBias:standardBias,daylightName:daylightName,daylightDate:daylightDate,daylightBias:daylightBias};}};function EtwImporter(model,events){this.importPriority=3;this.model_=model;this.events_=events;this.handlers_={};this.decoder_=new Decoder();this.walltime_=undefined;this.ticks_=undefined;this.is64bit_=undefined;this.tidsToPid_={};var allTypeInfos=tr.e.importer.etw.Parser.getAllRegisteredTypeInfos();this.parsers_=allTypeInfos.map(function(typeInfo){return new typeInfo.constructor(this);},this);}
+iterObjectFieldsRecursively(item.args);iterObjectFieldsRecursively(item.contexts);}};return{Model,};});'use strict';tr.exportTo('tr.e.importer.etw',function(){var kThreadGuid='3D6FA8D1-FE05-11D0-9DDA-00C04FD7BA7C';var kThreadDCStartOpcode=3;function Decoder(){this.payload_=new DataView(new ArrayBuffer(256));}
+Decoder.prototype={__proto__:Object.prototype,reset:function(base64Payload){var decodedSize=tr.b.Base64.getDecodedBufferLength(base64Payload);if(decodedSize>this.payload_.byteLength){this.payload_=new DataView(new ArrayBuffer(decodedSize));}
+tr.b.Base64.DecodeToTypedArray(base64Payload,this.payload_);this.position_=0;},skip:function(length){this.position_+=length;},decodeUInt8:function(){var result=this.payload_.getUint8(this.position_,true);this.position_+=1;return result;},decodeUInt16:function(){var result=this.payload_.getUint16(this.position_,true);this.position_+=2;return result;},decodeUInt32:function(){var result=this.payload_.getUint32(this.position_,true);this.position_+=4;return result;},decodeUInt64ToString:function(){var low=this.decodeUInt32();var high=this.decodeUInt32();var lowStr=('0000000'+low.toString(16)).substr(-8);var highStr=('0000000'+high.toString(16)).substr(-8);var result=highStr+lowStr;return result;},decodeInt8:function(){var result=this.payload_.getInt8(this.position_,true);this.position_+=1;return result;},decodeInt16:function(){var result=this.payload_.getInt16(this.position_,true);this.position_+=2;return result;},decodeInt32:function(){var result=this.payload_.getInt32(this.position_,true);this.position_+=4;return result;},decodeInt64ToString:function(){return this.decodeUInt64ToString();},decodeUInteger:function(is64){if(is64){return this.decodeUInt64ToString();}
+return this.decodeUInt32();},decodeString:function(){var str='';while(true){var c=this.decodeUInt8();if(!c)return str;str=str+String.fromCharCode(c);}},decodeW16String:function(){var str='';while(true){var c=this.decodeUInt16();if(!c)return str;str=str+String.fromCharCode(c);}},decodeFixedW16String:function(length){var oldPosition=this.position_;var str='';for(var i=0;i<length;i++){var c=this.decodeUInt16();if(!c)break;str=str+String.fromCharCode(c);}
+this.position_=oldPosition+2*length;return str;},decodeBytes:function(length){var bytes=[];for(var i=0;i<length;++i){var c=this.decodeUInt8();bytes.push(c);}
+return bytes;},decodeSID:function(is64){var pSid=this.decodeUInteger(is64);var attributes=this.decodeUInt32();if(is64){this.decodeUInt32();}
+var revision=this.decodeUInt8();var subAuthorityCount=this.decodeUInt8();this.decodeUInt16();this.decodeUInt32();if(revision!==1){throw new Error('Invalid SID revision: could not decode the SID structure.');}
+var sid=this.decodeBytes(4*subAuthorityCount);return{pSid:pSid,attributes:attributes,sid:sid};},decodeSystemTime:function(){var wYear=this.decodeInt16();var wMonth=this.decodeInt16();var wDayOfWeek=this.decodeInt16();var wDay=this.decodeInt16();var wHour=this.decodeInt16();var wMinute=this.decodeInt16();var wSecond=this.decodeInt16();var wMilliseconds=this.decodeInt16();return{wYear:wYear,wMonth:wMonth,wDayOfWeek:wDayOfWeek,wDay:wDay,wHour:wHour,wMinute:wMinute,wSecond:wSecond,wMilliseconds:wMilliseconds};},decodeTimeZoneInformation:function(){var bias=this.decodeUInt32();var standardName=this.decodeFixedW16String(32);var standardDate=this.decodeSystemTime();var standardBias=this.decodeUInt32();var daylightName=this.decodeFixedW16String(32);var daylightDate=this.decodeSystemTime();var daylightBias=this.decodeUInt32();return{bias:bias,standardName:standardName,standardDate:standardDate,standardBias:standardBias,daylightName:daylightName,daylightDate:daylightDate,daylightBias:daylightBias};}};function EtwImporter(model,events){this.importPriority=3;this.model_=model;this.events_=events;this.handlers_={};this.decoder_=new Decoder();this.walltime_=undefined;this.ticks_=undefined;this.is64bit_=undefined;this.tidsToPid_={};var allTypeInfos=tr.e.importer.etw.Parser.getAllRegisteredTypeInfos();this.parsers_=allTypeInfos.map(function(typeInfo){return new typeInfo.constructor(this);},this);}
 EtwImporter.canImport=function(events){if(!events.hasOwnProperty('name')||!events.hasOwnProperty('content')||events.name!=='ETW'){return false;}
-return true;};EtwImporter.prototype={__proto__:tr.importer.Importer.prototype,get importerName(){return'EtwImporter';},get model(){return this.model_;},createThreadIfNeeded:function(pid,tid){this.tidsToPid_[tid]=pid;},removeThreadIfPresent:function(tid){this.tidsToPid_[tid]=undefined;},getPidFromWindowsTid:function(tid){if(tid==0)
-return 0;var pid=this.tidsToPid_[tid];if(pid==undefined){return 0;}
-return pid;},getThreadFromWindowsTid:function(tid){var pid=this.getPidFromWindowsTid(tid);var process=this.model_.getProcess(pid);if(!process)
-return undefined;return process.getThread(tid);},getOrCreateCpu:function(cpuNumber){var cpu=this.model_.kernel.getOrCreateCpu(cpuNumber);return cpu;},importEvents:function(){this.events_.content.forEach(this.parseInfo.bind(this));if(this.walltime_==undefined||this.ticks_==undefined)
-throw Error('Cannot find clock sync information in the system trace.');if(this.is64bit_==undefined)
-throw Error('Cannot determine pointer size of the system trace.');this.events_.content.forEach(this.parseEvent.bind(this));},importTimestamp:function(timestamp){var ts=parseInt(timestamp,16);return(ts-this.walltime_+this.ticks_)/1000.;},parseInfo:function(event){if(event.hasOwnProperty('guid')&&event.hasOwnProperty('walltime')&&event.hasOwnProperty('tick')&&event.guid==='ClockSync'){this.walltime_=parseInt(event.walltime,16);this.ticks_=parseInt(event.tick,16);}
-if(this.is64bit_==undefined&&event.hasOwnProperty('guid')&&event.hasOwnProperty('op')&&event.hasOwnProperty('ver')&&event.hasOwnProperty('payload')&&event.guid===kThreadGuid&&event.op==kThreadDCStartOpcode){var decoded_size=tr.b.Base64.getDecodedBufferLength(event.payload);if(event.ver==1){if(decoded_size>=52)
-this.is64bit_=true;else
-this.is64bit_=false;}else if(event.ver==2){if(decoded_size>=64)
-this.is64bit_=true;else
-this.is64bit_=false;}else if(event.ver==3){if(decoded_size>=60)
-this.is64bit_=true;else
-this.is64bit_=false;}}
+return true;};EtwImporter.prototype={__proto__:tr.importer.Importer.prototype,get importerName(){return'EtwImporter';},get model(){return this.model_;},createThreadIfNeeded:function(pid,tid){this.tidsToPid_[tid]=pid;},removeThreadIfPresent:function(tid){this.tidsToPid_[tid]=undefined;},getPidFromWindowsTid:function(tid){if(tid===0)return 0;var pid=this.tidsToPid_[tid];if(pid===undefined){return 0;}
+return pid;},getThreadFromWindowsTid:function(tid){var pid=this.getPidFromWindowsTid(tid);var process=this.model_.getProcess(pid);if(!process)return undefined;return process.getThread(tid);},getOrCreateCpu:function(cpuNumber){var cpu=this.model_.kernel.getOrCreateCpu(cpuNumber);return cpu;},importEvents:function(){this.events_.content.forEach(this.parseInfo.bind(this));if(this.walltime_===undefined||this.ticks_===undefined){throw Error('Cannot find clock sync information in the system trace.');}
+if(this.is64bit_===undefined){throw Error('Cannot determine pointer size of the system trace.');}
+this.events_.content.forEach(this.parseEvent.bind(this));},importTimestamp:function(timestamp){var ts=parseInt(timestamp,16);return(ts-this.walltime_+this.ticks_)/1000.;},parseInfo:function(event){if(event.hasOwnProperty('guid')&&event.hasOwnProperty('walltime')&&event.hasOwnProperty('tick')&&event.guid==='ClockSync'){this.walltime_=parseInt(event.walltime,16);this.ticks_=parseInt(event.tick,16);}
+if(this.is64bit_===undefined&&event.hasOwnProperty('guid')&&event.hasOwnProperty('op')&&event.hasOwnProperty('ver')&&event.hasOwnProperty('payload')&&event.guid===kThreadGuid&&event.op===kThreadDCStartOpcode){var decodedSize=tr.b.Base64.getDecodedBufferLength(event.payload);if(event.ver===1){if(decodedSize>=52){this.is64bit_=true;}else{this.is64bit_=false;}}else if(event.ver===2){if(decodedSize>=64){this.is64bit_=true;}else{this.is64bit_=false;}}else if(event.ver===3){if(decodedSize>=60){this.is64bit_=true;}else{this.is64bit_=false;}}}
 return true;},parseEvent:function(event){if(!event.hasOwnProperty('guid')||!event.hasOwnProperty('op')||!event.hasOwnProperty('ver')||!event.hasOwnProperty('cpu')||!event.hasOwnProperty('ts')||!event.hasOwnProperty('payload')){return false;}
-var timestamp=this.importTimestamp(event.ts);var header={guid:event.guid,opcode:event.op,version:event.ver,cpu:event.cpu,timestamp:timestamp,is64:this.is64bit_};var decoder=this.decoder_;decoder.reset(event.payload);var handler=this.getEventHandler(header.guid,header.opcode);if(!handler)
-return false;if(!handler(header,decoder)){this.model_.importWarning({type:'parse_error',message:'Malformed '+header.guid+' event ('+event.payload+')'});return false;}
-return true;},registerEventHandler:function(guid,opcode,handler){if(this.handlers_[guid]==undefined)
-this.handlers_[guid]=[];this.handlers_[guid][opcode]=handler;},getEventHandler:function(guid,opcode){if(this.handlers_[guid]==undefined)
-return undefined;return this.handlers_[guid][opcode];}};tr.importer.Importer.register(EtwImporter);return{EtwImporter:EtwImporter};});!function(a){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=a();else if("function"==typeof define&&define.amd)define([],a);else{var b;"undefined"!=typeof window?b=window:"undefined"!=typeof global?b=global:"undefined"!=typeof self&&(b=self),b.JSZip=a()}}(function(){return function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);throw new Error("Cannot find module '"+g+"'")}var j=c[g]={exports:{}};b[g][0].call(j.exports,function(a){var c=b[g][1][a];return e(c?c:a)},j,j.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g<d.length;g++)e(d[g]);return e}({1:[function(a,b,c){"use strict";var d="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";c.encode=function(a){for(var b,c,e,f,g,h,i,j="",k=0;k<a.length;)b=a.charCodeAt(k++),c=a.charCodeAt(k++),e=a.charCodeAt(k++),f=b>>2,g=(3&b)<<4|c>>4,h=(15&c)<<2|e>>6,i=63&e,isNaN(c)?h=i=64:isNaN(e)&&(i=64),j=j+d.charAt(f)+d.charAt(g)+d.charAt(h)+d.charAt(i);return j},c.decode=function(a){var b,c,e,f,g,h,i,j="",k=0;for(a=a.replace(/[^A-Za-z0-9\+\/\=]/g,"");k<a.length;)f=d.indexOf(a.charAt(k++)),g=d.indexOf(a.charAt(k++)),h=d.indexOf(a.charAt(k++)),i=d.indexOf(a.charAt(k++)),b=f<<2|g>>4,c=(15&g)<<4|h>>2,e=(3&h)<<6|i,j+=String.fromCharCode(b),64!=h&&(j+=String.fromCharCode(c)),64!=i&&(j+=String.fromCharCode(e));return j}},{}],2:[function(a,b){"use strict";function c(){this.compressedSize=0,this.uncompressedSize=0,this.crc32=0,this.compressionMethod=null,this.compressedContent=null}c.prototype={getContent:function(){return null},getCompressedContent:function(){return null}},b.exports=c},{}],3:[function(a,b,c){"use strict";c.STORE={magic:"\x00\x00",compress:function(a){return a},uncompress:function(a){return a},compressInputType:null,uncompressInputType:null},c.DEFLATE=a("./flate")},{"./flate":8}],4:[function(a,b){"use strict";var c=a("./utils"),d=[0,1996959894,3993919788,2567524794,124634137,1886057615,3915621685,2657392035,249268274,2044508324,3772115230,2547177864,162941995,2125561021,3887607047,2428444049,498536548,1789927666,4089016648,2227061214,450548861,1843258603,4107580753,2211677639,325883990,1684777152,4251122042,2321926636,335633487,1661365465,4195302755,2366115317,997073096,1281953886,3579855332,2724688242,1006888145,1258607687,3524101629,2768942443,901097722,1119000684,3686517206,2898065728,853044451,1172266101,3705015759,2882616665,651767980,1373503546,3369554304,3218104598,565507253,1454621731,3485111705,3099436303,671266974,1594198024,3322730930,2970347812,795835527,1483230225,3244367275,3060149565,1994146192,31158534,2563907772,4023717930,1907459465,112637215,2680153253,3904427059,2013776290,251722036,2517215374,3775830040,2137656763,141376813,2439277719,3865271297,1802195444,476864866,2238001368,4066508878,1812370925,453092731,2181625025,4111451223,1706088902,314042704,2344532202,4240017532,1658658271,366619977,2362670323,4224994405,1303535960,984961486,2747007092,3569037538,1256170817,1037604311,2765210733,3554079995,1131014506,879679996,2909243462,3663771856,1141124467,855842277,2852801631,3708648649,1342533948,654459306,3188396048,3373015174,1466479909,544179635,3110523913,3462522015,1591671054,702138776,2966460450,3352799412,1504918807,783551873,3082640443,3233442989,3988292384,2596254646,62317068,1957810842,3939845945,2647816111,81470997,1943803523,3814918930,2489596804,225274430,2053790376,3826175755,2466906013,167816743,2097651377,4027552580,2265490386,503444072,1762050814,4150417245,2154129355,426522225,1852507879,4275313526,2312317920,282753626,1742555852,4189708143,2394877945,397917763,1622183637,3604390888,2714866558,953729732,1340076626,3518719985,2797360999,1068828381,1219638859,3624741850,2936675148,906185462,1090812512,3747672003,2825379669,829329135,1181335161,3412177804,3160834842,628085408,1382605366,3423369109,3138078467,570562233,1426400815,3317316542,2998733608,733239954,1555261956,3268935591,3050360625,752459403,1541320221,2607071920,3965973030,1969922972,40735498,2617837225,3943577151,1913087877,83908371,2512341634,3803740692,2075208622,213261112,2463272603,3855990285,2094854071,198958881,2262029012,4057260610,1759359992,534414190,2176718541,4139329115,1873836001,414664567,2282248934,4279200368,1711684554,285281116,2405801727,4167216745,1634467795,376229701,2685067896,3608007406,1308918612,956543938,2808555105,3495958263,1231636301,1047427035,2932959818,3654703836,1088359270,936918e3,2847714899,3736837829,1202900863,817233897,3183342108,3401237130,1404277552,615818150,3134207493,3453421203,1423857449,601450431,3009837614,3294710456,1567103746,711928724,3020668471,3272380065,1510334235,755167117];b.exports=function(a,b){if("undefined"==typeof a||!a.length)return 0;var e="string"!==c.getTypeOf(a);"undefined"==typeof b&&(b=0);var f=0,g=0,h=0;b=-1^b;for(var i=0,j=a.length;j>i;i++)h=e?a[i]:a.charCodeAt(i),g=255&(b^h),f=d[g],b=b>>>8^f;return-1^b}},{"./utils":21}],5:[function(a,b){"use strict";function c(){this.data=null,this.length=0,this.index=0}var d=a("./utils");c.prototype={checkOffset:function(a){this.checkIndex(this.index+a)},checkIndex:function(a){if(this.length<a||0>a)throw new Error("End of data reached (data length = "+this.length+", asked index = "+a+"). Corrupted zip ?")},setIndex:function(a){this.checkIndex(a),this.index=a},skip:function(a){this.setIndex(this.index+a)},byteAt:function(){},readInt:function(a){var b,c=0;for(this.checkOffset(a),b=this.index+a-1;b>=this.index;b--)c=(c<<8)+this.byteAt(b);return this.index+=a,c},readString:function(a){return d.transformTo("string",this.readData(a))},readData:function(){},lastIndexOfSignature:function(){},readDate:function(){var a=this.readInt(4);return new Date((a>>25&127)+1980,(a>>21&15)-1,a>>16&31,a>>11&31,a>>5&63,(31&a)<<1)}},b.exports=c},{"./utils":21}],6:[function(a,b,c){"use strict";c.base64=!1,c.binary=!1,c.dir=!1,c.createFolders=!1,c.date=null,c.compression=null,c.comment=null},{}],7:[function(a,b,c){"use strict";var d=a("./utils");c.string2binary=function(a){return d.string2binary(a)},c.string2Uint8Array=function(a){return d.transformTo("uint8array",a)},c.uint8Array2String=function(a){return d.transformTo("string",a)},c.string2Blob=function(a){var b=d.transformTo("arraybuffer",a);return d.arrayBuffer2Blob(b)},c.arrayBuffer2Blob=function(a){return d.arrayBuffer2Blob(a)},c.transformTo=function(a,b){return d.transformTo(a,b)},c.getTypeOf=function(a){return d.getTypeOf(a)},c.checkSupport=function(a){return d.checkSupport(a)},c.MAX_VALUE_16BITS=d.MAX_VALUE_16BITS,c.MAX_VALUE_32BITS=d.MAX_VALUE_32BITS,c.pretty=function(a){return d.pretty(a)},c.findCompression=function(a){return d.findCompression(a)},c.isRegExp=function(a){return d.isRegExp(a)}},{"./utils":21}],8:[function(a,b,c){"use strict";var d="undefined"!=typeof Uint8Array&&"undefined"!=typeof Uint16Array&&"undefined"!=typeof Uint32Array,e=a("pako");c.uncompressInputType=d?"uint8array":"array",c.compressInputType=d?"uint8array":"array",c.magic="\b\x00",c.compress=function(a){return e.deflateRaw(a)},c.uncompress=function(a){return e.inflateRaw(a)}},{pako:24}],9:[function(a,b){"use strict";function c(a,b){return this instanceof c?(this.files={},this.comment=null,this.root="",a&&this.load(a,b),void(this.clone=function(){var a=new c;for(var b in this)"function"!=typeof this[b]&&(a[b]=this[b]);return a})):new c(a,b)}var d=a("./base64");c.prototype=a("./object"),c.prototype.load=a("./load"),c.support=a("./support"),c.defaults=a("./defaults"),c.utils=a("./deprecatedPublicUtils"),c.base64={encode:function(a){return d.encode(a)},decode:function(a){return d.decode(a)}},c.compressions=a("./compressions"),b.exports=c},{"./base64":1,"./compressions":3,"./defaults":6,"./deprecatedPublicUtils":7,"./load":10,"./object":13,"./support":17}],10:[function(a,b){"use strict";var c=a("./base64"),d=a("./zipEntries");b.exports=function(a,b){var e,f,g,h;for(b=b||{},b.base64&&(a=c.decode(a)),f=new d(a,b),e=f.files,g=0;g<e.length;g++)h=e[g],this.file(h.fileName,h.decompressed,{binary:!0,optimizedBinaryString:!0,date:h.date,dir:h.dir,comment:h.fileComment.length?h.fileComment:null,createFolders:b.createFolders});return f.zipComment.length&&(this.comment=f.zipComment),this}},{"./base64":1,"./zipEntries":22}],11:[function(a,b){(function(a){"use strict";b.exports=function(b,c){return new a(b,c)},b.exports.test=function(b){return a.isBuffer(b)}}).call(this,"undefined"!=typeof Buffer?Buffer:void 0)},{}],12:[function(a,b){"use strict";function c(a){this.data=a,this.length=this.data.length,this.index=0}var d=a("./uint8ArrayReader");c.prototype=new d,c.prototype.readData=function(a){this.checkOffset(a);var b=this.data.slice(this.index,this.index+a);return this.index+=a,b},b.exports=c},{"./uint8ArrayReader":18}],13:[function(a,b){"use strict";var c=a("./support"),d=a("./utils"),e=a("./crc32"),f=a("./signature"),g=a("./defaults"),h=a("./base64"),i=a("./compressions"),j=a("./compressedObject"),k=a("./nodeBuffer"),l=a("./utf8"),m=a("./stringWriter"),n=a("./uint8ArrayWriter"),o=function(a){if(a._data instanceof j&&(a._data=a._data.getContent(),a.options.binary=!0,a.options.base64=!1,"uint8array"===d.getTypeOf(a._data))){var b=a._data;a._data=new Uint8Array(b.length),0!==b.length&&a._data.set(b,0)}return a._data},p=function(a){var b=o(a),e=d.getTypeOf(b);return"string"===e?!a.options.binary&&c.nodebuffer?k(b,"utf-8"):a.asBinary():b},q=function(a){var b=o(this);return null===b||"undefined"==typeof b?"":(this.options.base64&&(b=h.decode(b)),b=a&&this.options.binary?A.utf8decode(b):d.transformTo("string",b),a||this.options.binary||(b=d.transformTo("string",A.utf8encode(b))),b)},r=function(a,b,c){this.name=a,this.dir=c.dir,this.date=c.date,this.comment=c.comment,this._data=b,this.options=c,this._initialMetadata={dir:c.dir,date:c.date}};r.prototype={asText:function(){return q.call(this,!0)},asBinary:function(){return q.call(this,!1)},asNodeBuffer:function(){var a=p(this);return d.transformTo("nodebuffer",a)},asUint8Array:function(){var a=p(this);return d.transformTo("uint8array",a)},asArrayBuffer:function(){return this.asUint8Array().buffer}};var s=function(a,b){var c,d="";for(c=0;b>c;c++)d+=String.fromCharCode(255&a),a>>>=8;return d},t=function(){var a,b,c={};for(a=0;a<arguments.length;a++)for(b in arguments[a])arguments[a].hasOwnProperty(b)&&"undefined"==typeof c[b]&&(c[b]=arguments[a][b]);return c},u=function(a){return a=a||{},a.base64!==!0||null!==a.binary&&void 0!==a.binary||(a.binary=!0),a=t(a,g),a.date=a.date||new Date,null!==a.compression&&(a.compression=a.compression.toUpperCase()),a},v=function(a,b,c){var e,f=d.getTypeOf(b);if(c=u(c),c.createFolders&&(e=w(a))&&x.call(this,e,!0),c.dir||null===b||"undefined"==typeof b)c.base64=!1,c.binary=!1,b=null;else if("string"===f)c.binary&&!c.base64&&c.optimizedBinaryString!==!0&&(b=d.string2binary(b));else{if(c.base64=!1,c.binary=!0,!(f||b instanceof j))throw new Error("The data of '"+a+"' is in an unsupported format !");"arraybuffer"===f&&(b=d.transformTo("uint8array",b))}var g=new r(a,b,c);return this.files[a]=g,g},w=function(a){"/"==a.slice(-1)&&(a=a.substring(0,a.length-1));var b=a.lastIndexOf("/");return b>0?a.substring(0,b):""},x=function(a,b){return"/"!=a.slice(-1)&&(a+="/"),b="undefined"!=typeof b?b:!1,this.files[a]||v.call(this,a,null,{dir:!0,createFolders:b}),this.files[a]},y=function(a,b){var c,f=new j;return a._data instanceof j?(f.uncompressedSize=a._data.uncompressedSize,f.crc32=a._data.crc32,0===f.uncompressedSize||a.dir?(b=i.STORE,f.compressedContent="",f.crc32=0):a._data.compressionMethod===b.magic?f.compressedContent=a._data.getCompressedContent():(c=a._data.getContent(),f.compressedContent=b.compress(d.transformTo(b.compressInputType,c)))):(c=p(a),(!c||0===c.length||a.dir)&&(b=i.STORE,c=""),f.uncompressedSize=c.length,f.crc32=e(c),f.compressedContent=b.compress(d.transformTo(b.compressInputType,c))),f.compressedSize=f.compressedContent.length,f.compressionMethod=b.magic,f},z=function(a,b,c,g){var h,i,j,k,m=(c.compressedContent,d.transformTo("string",l.utf8encode(b.name))),n=b.comment||"",o=d.transformTo("string",l.utf8encode(n)),p=m.length!==b.name.length,q=o.length!==n.length,r=b.options,t="",u="",v="";j=b._initialMetadata.dir!==b.dir?b.dir:r.dir,k=b._initialMetadata.date!==b.date?b.date:r.date,h=k.getHours(),h<<=6,h|=k.getMinutes(),h<<=5,h|=k.getSeconds()/2,i=k.getFullYear()-1980,i<<=4,i|=k.getMonth()+1,i<<=5,i|=k.getDate(),p&&(u=s(1,1)+s(e(m),4)+m,t+="up"+s(u.length,2)+u),q&&(v=s(1,1)+s(this.crc32(o),4)+o,t+="uc"+s(v.length,2)+v);var w="";w+="\n\x00",w+=p||q?"\x00\b":"\x00\x00",w+=c.compressionMethod,w+=s(h,2),w+=s(i,2),w+=s(c.crc32,4),w+=s(c.compressedSize,4),w+=s(c.uncompressedSize,4),w+=s(m.length,2),w+=s(t.length,2);var x=f.LOCAL_FILE_HEADER+w+m+t,y=f.CENTRAL_FILE_HEADER+"\x00"+w+s(o.length,2)+"\x00\x00\x00\x00"+(j===!0?"\x00\x00\x00":"\x00\x00\x00\x00")+s(g,4)+m+t+o;return{fileRecord:x,dirRecord:y,compressedObject:c}},A={load:function(){throw new Error("Load method is not defined. Is the file jszip-load.js included ?")},filter:function(a){var b,c,d,e,f=[];for(b in this.files)this.files.hasOwnProperty(b)&&(d=this.files[b],e=new r(d.name,d._data,t(d.options)),c=b.slice(this.root.length,b.length),b.slice(0,this.root.length)===this.root&&a(c,e)&&f.push(e));return f},file:function(a,b,c){if(1===arguments.length){if(d.isRegExp(a)){var e=a;return this.filter(function(a,b){return!b.dir&&e.test(a)})}return this.filter(function(b,c){return!c.dir&&b===a})[0]||null}return a=this.root+a,v.call(this,a,b,c),this},folder:function(a){if(!a)return this;if(d.isRegExp(a))return this.filter(function(b,c){return c.dir&&a.test(b)});var b=this.root+a,c=x.call(this,b),e=this.clone();return e.root=c.name,e},remove:function(a){a=this.root+a;var b=this.files[a];if(b||("/"!=a.slice(-1)&&(a+="/"),b=this.files[a]),b&&!b.dir)delete this.files[a];else for(var c=this.filter(function(b,c){return c.name.slice(0,a.length)===a}),d=0;d<c.length;d++)delete this.files[c[d].name];return this},generate:function(a){a=t(a||{},{base64:!0,compression:"STORE",type:"base64",comment:null}),d.checkSupport(a.type);var b,c,e=[],g=0,j=0,k=d.transformTo("string",this.utf8encode(a.comment||this.comment||""));for(var l in this.files)if(this.files.hasOwnProperty(l)){var o=this.files[l],p=o.options.compression||a.compression.toUpperCase(),q=i[p];if(!q)throw new Error(p+" is not a valid compression method !");var r=y.call(this,o,q),u=z.call(this,l,o,r,g);g+=u.fileRecord.length+r.compressedSize,j+=u.dirRecord.length,e.push(u)}var v="";v=f.CENTRAL_DIRECTORY_END+"\x00\x00\x00\x00"+s(e.length,2)+s(e.length,2)+s(j,4)+s(g,4)+s(k.length,2)+k;var w=a.type.toLowerCase();for(b="uint8array"===w||"arraybuffer"===w||"blob"===w||"nodebuffer"===w?new n(g+j+v.length):new m(g+j+v.length),c=0;c<e.length;c++)b.append(e[c].fileRecord),b.append(e[c].compressedObject.compressedContent);for(c=0;c<e.length;c++)b.append(e[c].dirRecord);b.append(v);var x=b.finalize();switch(a.type.toLowerCase()){case"uint8array":case"arraybuffer":case"nodebuffer":return d.transformTo(a.type.toLowerCase(),x);case"blob":return d.arrayBuffer2Blob(d.transformTo("arraybuffer",x));case"base64":return a.base64?h.encode(x):x;default:return x}},crc32:function(a,b){return e(a,b)},utf8encode:function(a){return d.transformTo("string",l.utf8encode(a))},utf8decode:function(a){return l.utf8decode(a)}};b.exports=A},{"./base64":1,"./compressedObject":2,"./compressions":3,"./crc32":4,"./defaults":6,"./nodeBuffer":11,"./signature":14,"./stringWriter":16,"./support":17,"./uint8ArrayWriter":19,"./utf8":20,"./utils":21}],14:[function(a,b,c){"use strict";c.LOCAL_FILE_HEADER="PK",c.CENTRAL_FILE_HEADER="PK",c.CENTRAL_DIRECTORY_END="PK",c.ZIP64_CENTRAL_DIRECTORY_LOCATOR="PK",c.ZIP64_CENTRAL_DIRECTORY_END="PK",c.DATA_DESCRIPTOR="PK\b"},{}],15:[function(a,b){"use strict";function c(a,b){this.data=a,b||(this.data=e.string2binary(this.data)),this.length=this.data.length,this.index=0}var d=a("./dataReader"),e=a("./utils");c.prototype=new d,c.prototype.byteAt=function(a){return this.data.charCodeAt(a)},c.prototype.lastIndexOfSignature=function(a){return this.data.lastIndexOf(a)},c.prototype.readData=function(a){this.checkOffset(a);var b=this.data.slice(this.index,this.index+a);return this.index+=a,b},b.exports=c},{"./dataReader":5,"./utils":21}],16:[function(a,b){"use strict";var c=a("./utils"),d=function(){this.data=[]};d.prototype={append:function(a){a=c.transformTo("string",a),this.data.push(a)},finalize:function(){return this.data.join("")}},b.exports=d},{"./utils":21}],17:[function(a,b,c){(function(a){"use strict";if(c.base64=!0,c.array=!0,c.string=!0,c.arraybuffer="undefined"!=typeof ArrayBuffer&&"undefined"!=typeof Uint8Array,c.nodebuffer="undefined"!=typeof a,c.uint8array="undefined"!=typeof Uint8Array,"undefined"==typeof ArrayBuffer)c.blob=!1;else{var b=new ArrayBuffer(0);try{c.blob=0===new Blob([b],{type:"application/zip"}).size}catch(d){try{var e=window.BlobBuilder||window.WebKitBlobBuilder||window.MozBlobBuilder||window.MSBlobBuilder,f=new e;f.append(b),c.blob=0===f.getBlob("application/zip").size}catch(d){c.blob=!1}}}}).call(this,"undefined"!=typeof Buffer?Buffer:void 0)},{}],18:[function(a,b){"use strict";function c(a){a&&(this.data=a,this.length=this.data.length,this.index=0)}var d=a("./dataReader");c.prototype=new d,c.prototype.byteAt=function(a){return this.data[a]},c.prototype.lastIndexOfSignature=function(a){for(var b=a.charCodeAt(0),c=a.charCodeAt(1),d=a.charCodeAt(2),e=a.charCodeAt(3),f=this.length-4;f>=0;--f)if(this.data[f]===b&&this.data[f+1]===c&&this.data[f+2]===d&&this.data[f+3]===e)return f;return-1},c.prototype.readData=function(a){if(this.checkOffset(a),0===a)return new Uint8Array(0);var b=this.data.subarray(this.index,this.index+a);return this.index+=a,b},b.exports=c},{"./dataReader":5}],19:[function(a,b){"use strict";var c=a("./utils"),d=function(a){this.data=new Uint8Array(a),this.index=0};d.prototype={append:function(a){0!==a.length&&(a=c.transformTo("uint8array",a),this.data.set(a,this.index),this.index+=a.length)},finalize:function(){return this.data}},b.exports=d},{"./utils":21}],20:[function(a,b,c){"use strict";for(var d=a("./utils"),e=a("./support"),f=a("./nodeBuffer"),g=new Array(256),h=0;256>h;h++)g[h]=h>=252?6:h>=248?5:h>=240?4:h>=224?3:h>=192?2:1;g[254]=g[254]=1;var i=function(a){var b,c,d,f,g,h=a.length,i=0;for(f=0;h>f;f++)c=a.charCodeAt(f),55296===(64512&c)&&h>f+1&&(d=a.charCodeAt(f+1),56320===(64512&d)&&(c=65536+(c-55296<<10)+(d-56320),f++)),i+=128>c?1:2048>c?2:65536>c?3:4;for(b=e.uint8array?new Uint8Array(i):new Array(i),g=0,f=0;i>g;f++)c=a.charCodeAt(f),55296===(64512&c)&&h>f+1&&(d=a.charCodeAt(f+1),56320===(64512&d)&&(c=65536+(c-55296<<10)+(d-56320),f++)),128>c?b[g++]=c:2048>c?(b[g++]=192|c>>>6,b[g++]=128|63&c):65536>c?(b[g++]=224|c>>>12,b[g++]=128|c>>>6&63,b[g++]=128|63&c):(b[g++]=240|c>>>18,b[g++]=128|c>>>12&63,b[g++]=128|c>>>6&63,b[g++]=128|63&c);return b},j=function(a,b){var c;for(b=b||a.length,b>a.length&&(b=a.length),c=b-1;c>=0&&128===(192&a[c]);)c--;return 0>c?b:0===c?b:c+g[a[c]]>b?c:b},k=function(a){var b,c,e,f,h=a.length,i=new Array(2*h);for(c=0,b=0;h>b;)if(e=a[b++],128>e)i[c++]=e;else if(f=g[e],f>4)i[c++]=65533,b+=f-1;else{for(e&=2===f?31:3===f?15:7;f>1&&h>b;)e=e<<6|63&a[b++],f--;f>1?i[c++]=65533:65536>e?i[c++]=e:(e-=65536,i[c++]=55296|e>>10&1023,i[c++]=56320|1023&e)}return i.length!==c&&(i.subarray?i=i.subarray(0,c):i.length=c),d.applyFromCharCode(i)};c.utf8encode=function(a){return e.nodebuffer?f(a,"utf-8"):i(a)},c.utf8decode=function(a){if(e.nodebuffer)return d.transformTo("nodebuffer",a).toString("utf-8");a=d.transformTo(e.uint8array?"uint8array":"array",a);for(var b=[],c=0,f=a.length,g=65536;f>c;){var h=j(a,Math.min(c+g,f));b.push(e.uint8array?k(a.subarray(c,h)):k(a.slice(c,h))),c=h}return b.join("")}},{"./nodeBuffer":11,"./support":17,"./utils":21}],21:[function(a,b,c){"use strict";function d(a){return a}function e(a,b){for(var c=0;c<a.length;++c)b[c]=255&a.charCodeAt(c);return b}function f(a){var b=65536,d=[],e=a.length,f=c.getTypeOf(a),g=0,h=!0;try{switch(f){case"uint8array":String.fromCharCode.apply(null,new Uint8Array(0));break;case"nodebuffer":String.fromCharCode.apply(null,j(0))}}catch(i){h=!1}if(!h){for(var k="",l=0;l<a.length;l++)k+=String.fromCharCode(a[l]);return k}for(;e>g&&b>1;)try{d.push("array"===f||"nodebuffer"===f?String.fromCharCode.apply(null,a.slice(g,Math.min(g+b,e))):String.fromCharCode.apply(null,a.subarray(g,Math.min(g+b,e)))),g+=b}catch(i){b=Math.floor(b/2)}return d.join("")}function g(a,b){for(var c=0;c<a.length;c++)b[c]=a[c];return b}var h=a("./support"),i=a("./compressions"),j=a("./nodeBuffer");c.string2binary=function(a){for(var b="",c=0;c<a.length;c++)b+=String.fromCharCode(255&a.charCodeAt(c));return b},c.arrayBuffer2Blob=function(a){c.checkSupport("blob");try{return new Blob([a],{type:"application/zip"})}catch(b){try{var d=window.BlobBuilder||window.WebKitBlobBuilder||window.MozBlobBuilder||window.MSBlobBuilder,e=new d;return e.append(a),e.getBlob("application/zip")}catch(b){throw new Error("Bug : can't construct the Blob.")}}},c.applyFromCharCode=f;var k={};k.string={string:d,array:function(a){return e(a,new Array(a.length))},arraybuffer:function(a){return k.string.uint8array(a).buffer},uint8array:function(a){return e(a,new Uint8Array(a.length))},nodebuffer:function(a){return e(a,j(a.length))}},k.array={string:f,array:d,arraybuffer:function(a){return new Uint8Array(a).buffer},uint8array:function(a){return new Uint8Array(a)},nodebuffer:function(a){return j(a)}},k.arraybuffer={string:function(a){return f(new Uint8Array(a))},array:function(a){return g(new Uint8Array(a),new Array(a.byteLength))},arraybuffer:d,uint8array:function(a){return new Uint8Array(a)},nodebuffer:function(a){return j(new Uint8Array(a))}},k.uint8array={string:f,array:function(a){return g(a,new Array(a.length))},arraybuffer:function(a){return a.buffer},uint8array:d,nodebuffer:function(a){return j(a)}},k.nodebuffer={string:f,array:function(a){return g(a,new Array(a.length))},arraybuffer:function(a){return k.nodebuffer.uint8array(a).buffer},uint8array:function(a){return g(a,new Uint8Array(a.length))},nodebuffer:d},c.transformTo=function(a,b){if(b||(b=""),!a)return b;c.checkSupport(a);var d=c.getTypeOf(b),e=k[d][a](b);return e},c.getTypeOf=function(a){return"string"==typeof a?"string":"[object Array]"===Object.prototype.toString.call(a)?"array":h.nodebuffer&&j.test(a)?"nodebuffer":h.uint8array&&a instanceof Uint8Array?"uint8array":h.arraybuffer&&a instanceof ArrayBuffer?"arraybuffer":void 0},c.checkSupport=function(a){var b=h[a.toLowerCase()];if(!b)throw new Error(a+" is not supported by this browser")},c.MAX_VALUE_16BITS=65535,c.MAX_VALUE_32BITS=-1,c.pretty=function(a){var b,c,d="";for(c=0;c<(a||"").length;c++)b=a.charCodeAt(c),d+="\\x"+(16>b?"0":"")+b.toString(16).toUpperCase();return d},c.findCompression=function(a){for(var b in i)if(i.hasOwnProperty(b)&&i[b].magic===a)return i[b];return null},c.isRegExp=function(a){return"[object RegExp]"===Object.prototype.toString.call(a)}},{"./compressions":3,"./nodeBuffer":11,"./support":17}],22:[function(a,b){"use strict";function c(a,b){this.files=[],this.loadOptions=b,a&&this.load(a)}var d=a("./stringReader"),e=a("./nodeBufferReader"),f=a("./uint8ArrayReader"),g=a("./utils"),h=a("./signature"),i=a("./zipEntry"),j=a("./support"),k=a("./object");c.prototype={checkSignature:function(a){var b=this.reader.readString(4);if(b!==a)throw new Error("Corrupted zip or bug : unexpected signature ("+g.pretty(b)+", expected "+g.pretty(a)+")")},readBlockEndOfCentral:function(){this.diskNumber=this.reader.readInt(2),this.diskWithCentralDirStart=this.reader.readInt(2),this.centralDirRecordsOnThisDisk=this.reader.readInt(2),this.centralDirRecords=this.reader.readInt(2),this.centralDirSize=this.reader.readInt(4),this.centralDirOffset=this.reader.readInt(4),this.zipCommentLength=this.reader.readInt(2),this.zipComment=this.reader.readString(this.zipCommentLength),this.zipComment=k.utf8decode(this.zipComment)},readBlockZip64EndOfCentral:function(){this.zip64EndOfCentralSize=this.reader.readInt(8),this.versionMadeBy=this.reader.readString(2),this.versionNeeded=this.reader.readInt(2),this.diskNumber=this.reader.readInt(4),this.diskWithCentralDirStart=this.reader.readInt(4),this.centralDirRecordsOnThisDisk=this.reader.readInt(8),this.centralDirRecords=this.reader.readInt(8),this.centralDirSize=this.reader.readInt(8),this.centralDirOffset=this.reader.readInt(8),this.zip64ExtensibleData={};for(var a,b,c,d=this.zip64EndOfCentralSize-44,e=0;d>e;)a=this.reader.readInt(2),b=this.reader.readInt(4),c=this.reader.readString(b),this.zip64ExtensibleData[a]={id:a,length:b,value:c}},readBlockZip64EndOfCentralLocator:function(){if(this.diskWithZip64CentralDirStart=this.reader.readInt(4),this.relativeOffsetEndOfZip64CentralDir=this.reader.readInt(8),this.disksCount=this.reader.readInt(4),this.disksCount>1)throw new Error("Multi-volumes zip are not supported")},readLocalFiles:function(){var a,b;for(a=0;a<this.files.length;a++)b=this.files[a],this.reader.setIndex(b.localHeaderOffset),this.checkSignature(h.LOCAL_FILE_HEADER),b.readLocalPart(this.reader),b.handleUTF8()},readCentralDir:function(){var a;for(this.reader.setIndex(this.centralDirOffset);this.reader.readString(4)===h.CENTRAL_FILE_HEADER;)a=new i({zip64:this.zip64},this.loadOptions),a.readCentralPart(this.reader),this.files.push(a)},readEndOfCentral:function(){var a=this.reader.lastIndexOfSignature(h.CENTRAL_DIRECTORY_END);if(-1===a)throw new Error("Corrupted zip : can't find end of central directory");if(this.reader.setIndex(a),this.checkSignature(h.CENTRAL_DIRECTORY_END),this.readBlockEndOfCentral(),this.diskNumber===g.MAX_VALUE_16BITS||this.diskWithCentralDirStart===g.MAX_VALUE_16BITS||this.centralDirRecordsOnThisDisk===g.MAX_VALUE_16BITS||this.centralDirRecords===g.MAX_VALUE_16BITS||this.centralDirSize===g.MAX_VALUE_32BITS||this.centralDirOffset===g.MAX_VALUE_32BITS){if(this.zip64=!0,a=this.reader.lastIndexOfSignature(h.ZIP64_CENTRAL_DIRECTORY_LOCATOR),-1===a)throw new Error("Corrupted zip : can't find the ZIP64 end of central directory locator");this.reader.setIndex(a),this.checkSignature(h.ZIP64_CENTRAL_DIRECTORY_LOCATOR),this.readBlockZip64EndOfCentralLocator(),this.reader.setIndex(this.relativeOffsetEndOfZip64CentralDir),this.checkSignature(h.ZIP64_CENTRAL_DIRECTORY_END),this.readBlockZip64EndOfCentral()}},prepareReader:function(a){var b=g.getTypeOf(a);this.reader="string"!==b||j.uint8array?"nodebuffer"===b?new e(a):new f(g.transformTo("uint8array",a)):new d(a,this.loadOptions.optimizedBinaryString)},load:function(a){this.prepareReader(a),this.readEndOfCentral(),this.readCentralDir(),this.readLocalFiles()}},b.exports=c},{"./nodeBufferReader":12,"./object":13,"./signature":14,"./stringReader":15,"./support":17,"./uint8ArrayReader":18,"./utils":21,"./zipEntry":23}],23:[function(a,b){"use strict";function c(a,b){this.options=a,this.loadOptions=b}var d=a("./stringReader"),e=a("./utils"),f=a("./compressedObject"),g=a("./object");c.prototype={isEncrypted:function(){return 1===(1&this.bitFlag)},useUTF8:function(){return 2048===(2048&this.bitFlag)},prepareCompressedContent:function(a,b,c){return function(){var d=a.index;a.setIndex(b);var e=a.readData(c);return a.setIndex(d),e}},prepareContent:function(a,b,c,d,f){return function(){var a=e.transformTo(d.uncompressInputType,this.getCompressedContent()),b=d.uncompress(a);if(b.length!==f)throw new Error("Bug : uncompressed data size mismatch");return b}},readLocalPart:function(a){var b,c;if(a.skip(22),this.fileNameLength=a.readInt(2),c=a.readInt(2),this.fileName=a.readString(this.fileNameLength),a.skip(c),-1==this.compressedSize||-1==this.uncompressedSize)throw new Error("Bug or corrupted zip : didn't get enough informations from the central directory (compressedSize == -1 || uncompressedSize == -1)");if(b=e.findCompression(this.compressionMethod),null===b)throw new Error("Corrupted zip : compression "+e.pretty(this.compressionMethod)+" unknown (inner file : "+this.fileName+")");if(this.decompressed=new f,this.decompressed.compressedSize=this.compressedSize,this.decompressed.uncompressedSize=this.uncompressedSize,this.decompressed.crc32=this.crc32,this.decompressed.compressionMethod=this.compressionMethod,this.decompressed.getCompressedContent=this.prepareCompressedContent(a,a.index,this.compressedSize,b),this.decompressed.getContent=this.prepareContent(a,a.index,this.compressedSize,b,this.uncompressedSize),this.loadOptions.checkCRC32&&(this.decompressed=e.transformTo("string",this.decompressed.getContent()),g.crc32(this.decompressed)!==this.crc32))throw new Error("Corrupted zip : CRC32 mismatch")},readCentralPart:function(a){if(this.versionMadeBy=a.readString(2),this.versionNeeded=a.readInt(2),this.bitFlag=a.readInt(2),this.compressionMethod=a.readString(2),this.date=a.readDate(),this.crc32=a.readInt(4),this.compressedSize=a.readInt(4),this.uncompressedSize=a.readInt(4),this.fileNameLength=a.readInt(2),this.extraFieldsLength=a.readInt(2),this.fileCommentLength=a.readInt(2),this.diskNumberStart=a.readInt(2),this.internalFileAttributes=a.readInt(2),this.externalFileAttributes=a.readInt(4),this.localHeaderOffset=a.readInt(4),this.isEncrypted())throw new Error("Encrypted zip are not supported");this.fileName=a.readString(this.fileNameLength),this.readExtraFields(a),this.parseZIP64ExtraField(a),this.fileComment=a.readString(this.fileCommentLength),this.dir=16&this.externalFileAttributes?!0:!1},parseZIP64ExtraField:function(){if(this.extraFields[1]){var a=new d(this.extraFields[1].value);this.uncompressedSize===e.MAX_VALUE_32BITS&&(this.uncompressedSize=a.readInt(8)),this.compressedSize===e.MAX_VALUE_32BITS&&(this.compressedSize=a.readInt(8)),this.localHeaderOffset===e.MAX_VALUE_32BITS&&(this.localHeaderOffset=a.readInt(8)),this.diskNumberStart===e.MAX_VALUE_32BITS&&(this.diskNumberStart=a.readInt(4))}},readExtraFields:function(a){var b,c,d,e=a.index;for(this.extraFields=this.extraFields||{};a.index<e+this.extraFieldsLength;)b=a.readInt(2),c=a.readInt(2),d=a.readString(c),this.extraFields[b]={id:b,length:c,value:d}},handleUTF8:function(){if(this.useUTF8())this.fileName=g.utf8decode(this.fileName),this.fileComment=g.utf8decode(this.fileComment);else{var a=this.findExtraFieldUnicodePath();null!==a&&(this.fileName=a);var b=this.findExtraFieldUnicodeComment();null!==b&&(this.fileComment=b)}},findExtraFieldUnicodePath:function(){var a=this.extraFields[28789];if(a){var b=new d(a.value);return 1!==b.readInt(1)?null:g.crc32(this.fileName)!==b.readInt(4)?null:g.utf8decode(b.readString(a.length-5))}return null},findExtraFieldUnicodeComment:function(){var a=this.extraFields[25461];if(a){var b=new d(a.value);return 1!==b.readInt(1)?null:g.crc32(this.fileComment)!==b.readInt(4)?null:g.utf8decode(b.readString(a.length-5))}return null}},b.exports=c},{"./compressedObject":2,"./object":13,"./stringReader":15,"./utils":21}],24:[function(a,b){"use strict";var c=a("./lib/utils/common").assign,d=a("./lib/deflate"),e=a("./lib/inflate"),f=a("./lib/zlib/constants"),g={};c(g,d,e,f),b.exports=g},{"./lib/deflate":25,"./lib/inflate":26,"./lib/utils/common":27,"./lib/zlib/constants":30}],25:[function(a,b,c){"use strict";function d(a,b){var c=new s(b);if(c.push(a,!0),c.err)throw c.msg;return c.result}function e(a,b){return b=b||{},b.raw=!0,d(a,b)}function f(a,b){return b=b||{},b.gzip=!0,d(a,b)}var g=a("./zlib/deflate.js"),h=a("./utils/common"),i=a("./utils/strings"),j=a("./zlib/messages"),k=a("./zlib/zstream"),l=0,m=4,n=0,o=1,p=-1,q=0,r=8,s=function(a){this.options=h.assign({level:p,method:r,chunkSize:16384,windowBits:15,memLevel:8,strategy:q,to:""},a||{});var b=this.options;b.raw&&b.windowBits>0?b.windowBits=-b.windowBits:b.gzip&&b.windowBits>0&&b.windowBits<16&&(b.windowBits+=16),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new k,this.strm.avail_out=0;var c=g.deflateInit2(this.strm,b.level,b.method,b.windowBits,b.memLevel,b.strategy);if(c!==n)throw new Error(j[c]);b.header&&g.deflateSetHeader(this.strm,b.header)};s.prototype.push=function(a,b){var c,d,e=this.strm,f=this.options.chunkSize;if(this.ended)return!1;d=b===~~b?b:b===!0?m:l,e.input="string"==typeof a?i.string2buf(a):a,e.next_in=0,e.avail_in=e.input.length;do{if(0===e.avail_out&&(e.output=new h.Buf8(f),e.next_out=0,e.avail_out=f),c=g.deflate(e,d),c!==o&&c!==n)return this.onEnd(c),this.ended=!0,!1;(0===e.avail_out||0===e.avail_in&&d===m)&&this.onData("string"===this.options.to?i.buf2binstring(h.shrinkBuf(e.output,e.next_out)):h.shrinkBuf(e.output,e.next_out))}while((e.avail_in>0||0===e.avail_out)&&c!==o);return d===m?(c=g.deflateEnd(this.strm),this.onEnd(c),this.ended=!0,c===n):!0},s.prototype.onData=function(a){this.chunks.push(a)},s.prototype.onEnd=function(a){a===n&&(this.result="string"===this.options.to?this.chunks.join(""):h.flattenChunks(this.chunks)),this.chunks=[],this.err=a,this.msg=this.strm.msg},c.Deflate=s,c.deflate=d,c.deflateRaw=e,c.gzip=f},{"./utils/common":27,"./utils/strings":28,"./zlib/deflate.js":32,"./zlib/messages":37,"./zlib/zstream":39}],26:[function(a,b,c){"use strict";function d(a,b){var c=new m(b);if(c.push(a,!0),c.err)throw c.msg;return c.result}function e(a,b){return b=b||{},b.raw=!0,d(a,b)}var f=a("./zlib/inflate.js"),g=a("./utils/common"),h=a("./utils/strings"),i=a("./zlib/constants"),j=a("./zlib/messages"),k=a("./zlib/zstream"),l=a("./zlib/gzheader"),m=function(a){this.options=g.assign({chunkSize:16384,windowBits:0,to:""},a||{});var b=this.options;b.raw&&b.windowBits>=0&&b.windowBits<16&&(b.windowBits=-b.windowBits,0===b.windowBits&&(b.windowBits=-15)),!(b.windowBits>=0&&b.windowBits<16)||a&&a.windowBits||(b.windowBits+=32),b.windowBits>15&&b.windowBits<48&&0===(15&b.windowBits)&&(b.windowBits|=15),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new k,this.strm.avail_out=0;var c=f.inflateInit2(this.strm,b.windowBits);if(c!==i.Z_OK)throw new Error(j[c]);this.header=new l,f.inflateGetHeader(this.strm,this.header)};m.prototype.push=function(a,b){var c,d,e,j,k,l=this.strm,m=this.options.chunkSize;if(this.ended)return!1;d=b===~~b?b:b===!0?i.Z_FINISH:i.Z_NO_FLUSH,l.input="string"==typeof a?h.binstring2buf(a):a,l.next_in=0,l.avail_in=l.input.length;do{if(0===l.avail_out&&(l.output=new g.Buf8(m),l.next_out=0,l.avail_out=m),c=f.inflate(l,i.Z_NO_FLUSH),c!==i.Z_STREAM_END&&c!==i.Z_OK)return this.onEnd(c),this.ended=!0,!1;l.next_out&&(0===l.avail_out||c===i.Z_STREAM_END||0===l.avail_in&&d===i.Z_FINISH)&&("string"===this.options.to?(e=h.utf8border(l.output,l.next_out),j=l.next_out-e,k=h.buf2string(l.output,e),l.next_out=j,l.avail_out=m-j,j&&g.arraySet(l.output,l.output,e,j,0),this.onData(k)):this.onData(g.shrinkBuf(l.output,l.next_out)))}while(l.avail_in>0&&c!==i.Z_STREAM_END);return c===i.Z_STREAM_END&&(d=i.Z_FINISH),d===i.Z_FINISH?(c=f.inflateEnd(this.strm),this.onEnd(c),this.ended=!0,c===i.Z_OK):!0},m.prototype.onData=function(a){this.chunks.push(a)},m.prototype.onEnd=function(a){a===i.Z_OK&&(this.result="string"===this.options.to?this.chunks.join(""):g.flattenChunks(this.chunks)),this.chunks=[],this.err=a,this.msg=this.strm.msg},c.Inflate=m,c.inflate=d,c.inflateRaw=e,c.ungzip=d},{"./utils/common":27,"./utils/strings":28,"./zlib/constants":30,"./zlib/gzheader":33,"./zlib/inflate.js":35,"./zlib/messages":37,"./zlib/zstream":39}],27:[function(a,b,c){"use strict";var d="undefined"!=typeof Uint8Array&&"undefined"!=typeof Uint16Array&&"undefined"!=typeof Int32Array;c.assign=function(a){for(var b=Array.prototype.slice.call(arguments,1);b.length;){var c=b.shift();if(c){if("object"!=typeof c)throw new TypeError(c+"must be non-object");for(var d in c)c.hasOwnProperty(d)&&(a[d]=c[d])}}return a},c.shrinkBuf=function(a,b){return a.length===b?a:a.subarray?a.subarray(0,b):(a.length=b,a)};var e={arraySet:function(a,b,c,d,e){if(b.subarray&&a.subarray)return void a.set(b.subarray(c,c+d),e);for(var f=0;d>f;f++)a[e+f]=b[c+f]},flattenChunks:function(a){var b,c,d,e,f,g;for(d=0,b=0,c=a.length;c>b;b++)d+=a[b].length;for(g=new Uint8Array(d),e=0,b=0,c=a.length;c>b;b++)f=a[b],g.set(f,e),e+=f.length;return g}},f={arraySet:function(a,b,c,d,e){for(var f=0;d>f;f++)a[e+f]=b[c+f]},flattenChunks:function(a){return[].concat.apply([],a)}};c.setTyped=function(a){a?(c.Buf8=Uint8Array,c.Buf16=Uint16Array,c.Buf32=Int32Array,c.assign(c,e)):(c.Buf8=Array,c.Buf16=Array,c.Buf32=Array,c.assign(c,f))},c.setTyped(d)},{}],28:[function(a,b,c){"use strict";function d(a,b){if(65537>b&&(a.subarray&&g||!a.subarray&&f))return String.fromCharCode.apply(null,e.shrinkBuf(a,b));for(var c="",d=0;b>d;d++)c+=String.fromCharCode(a[d]);return c}var e=a("./common"),f=!0,g=!0;try{String.fromCharCode.apply(null,[0])}catch(h){f=!1}try{String.fromCharCode.apply(null,new Uint8Array(1))}catch(h){g=!1}for(var i=new e.Buf8(256),j=0;256>j;j++)i[j]=j>=252?6:j>=248?5:j>=240?4:j>=224?3:j>=192?2:1;i[254]=i[254]=1,c.string2buf=function(a){var b,c,d,f,g,h=a.length,i=0;for(f=0;h>f;f++)c=a.charCodeAt(f),55296===(64512&c)&&h>f+1&&(d=a.charCodeAt(f+1),56320===(64512&d)&&(c=65536+(c-55296<<10)+(d-56320),f++)),i+=128>c?1:2048>c?2:65536>c?3:4;for(b=new e.Buf8(i),g=0,f=0;i>g;f++)c=a.charCodeAt(f),55296===(64512&c)&&h>f+1&&(d=a.charCodeAt(f+1),56320===(64512&d)&&(c=65536+(c-55296<<10)+(d-56320),f++)),128>c?b[g++]=c:2048>c?(b[g++]=192|c>>>6,b[g++]=128|63&c):65536>c?(b[g++]=224|c>>>12,b[g++]=128|c>>>6&63,b[g++]=128|63&c):(b[g++]=240|c>>>18,b[g++]=128|c>>>12&63,b[g++]=128|c>>>6&63,b[g++]=128|63&c);return b},c.buf2binstring=function(a){return d(a,a.length)},c.binstring2buf=function(a){for(var b=new e.Buf8(a.length),c=0,d=b.length;d>c;c++)b[c]=a.charCodeAt(c);return b},c.buf2string=function(a,b){var c,e,f,g,h=b||a.length,j=new Array(2*h);for(e=0,c=0;h>c;)if(f=a[c++],128>f)j[e++]=f;else if(g=i[f],g>4)j[e++]=65533,c+=g-1;else{for(f&=2===g?31:3===g?15:7;g>1&&h>c;)f=f<<6|63&a[c++],g--;g>1?j[e++]=65533:65536>f?j[e++]=f:(f-=65536,j[e++]=55296|f>>10&1023,j[e++]=56320|1023&f)}return d(j,e)},c.utf8border=function(a,b){var c;for(b=b||a.length,b>a.length&&(b=a.length),c=b-1;c>=0&&128===(192&a[c]);)c--;return 0>c?b:0===c?b:c+i[a[c]]>b?c:b}},{"./common":27}],29:[function(a,b){"use strict";function c(a,b,c,d){for(var e=65535&a|0,f=a>>>16&65535|0,g=0;0!==c;){g=c>2e3?2e3:c,c-=g;do e=e+b[d++]|0,f=f+e|0;while(--g);e%=65521,f%=65521}return e|f<<16|0}b.exports=c},{}],30:[function(a,b){b.exports={Z_NO_FLUSH:0,Z_PARTIAL_FLUSH:1,Z_SYNC_FLUSH:2,Z_FULL_FLUSH:3,Z_FINISH:4,Z_BLOCK:5,Z_TREES:6,Z_OK:0,Z_STREAM_END:1,Z_NEED_DICT:2,Z_ERRNO:-1,Z_STREAM_ERROR:-2,Z_DATA_ERROR:-3,Z_BUF_ERROR:-5,Z_NO_COMPRESSION:0,Z_BEST_SPEED:1,Z_BEST_COMPRESSION:9,Z_DEFAULT_COMPRESSION:-1,Z_FILTERED:1,Z_HUFFMAN_ONLY:2,Z_RLE:3,Z_FIXED:4,Z_DEFAULT_STRATEGY:0,Z_BINARY:0,Z_TEXT:1,Z_UNKNOWN:2,Z_DEFLATED:8}},{}],31:[function(a,b){"use strict";function c(){for(var a,b=[],c=0;256>c;c++){a=c;for(var d=0;8>d;d++)a=1&a?3988292384^a>>>1:a>>>1;b[c]=a}return b}function d(a,b,c,d){var f=e,g=d+c;a=-1^a;for(var h=d;g>h;h++)a=a>>>8^f[255&(a^b[h])];return-1^a}var e=c();b.exports=d},{}],32:[function(a,b,c){"use strict";function d(a,b){return a.msg=G[b],b}function e(a){return(a<<1)-(a>4?9:0)}function f(a){for(var b=a.length;--b>=0;)a[b]=0}function g(a){var b=a.state,c=b.pending;c>a.avail_out&&(c=a.avail_out),0!==c&&(C.arraySet(a.output,b.pending_buf,b.pending_out,c,a.next_out),a.next_out+=c,b.pending_out+=c,a.total_out+=c,a.avail_out-=c,b.pending-=c,0===b.pending&&(b.pending_out=0))}function h(a,b){D._tr_flush_block(a,a.block_start>=0?a.block_start:-1,a.strstart-a.block_start,b),a.block_start=a.strstart,g(a.strm)}function i(a,b){a.pending_buf[a.pending++]=b}function j(a,b){a.pending_buf[a.pending++]=b>>>8&255,a.pending_buf[a.pending++]=255&b}function k(a,b,c,d){var e=a.avail_in;return e>d&&(e=d),0===e?0:(a.avail_in-=e,C.arraySet(b,a.input,a.next_in,e,c),1===a.state.wrap?a.adler=E(a.adler,b,e,c):2===a.state.wrap&&(a.adler=F(a.adler,b,e,c)),a.next_in+=e,a.total_in+=e,e)}function l(a,b){var c,d,e=a.max_chain_length,f=a.strstart,g=a.prev_length,h=a.nice_match,i=a.strstart>a.w_size-jb?a.strstart-(a.w_size-jb):0,j=a.window,k=a.w_mask,l=a.prev,m=a.strstart+ib,n=j[f+g-1],o=j[f+g];a.prev_length>=a.good_match&&(e>>=2),h>a.lookahead&&(h=a.lookahead);do if(c=b,j[c+g]===o&&j[c+g-1]===n&&j[c]===j[f]&&j[++c]===j[f+1]){f+=2,c++;do;while(j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&m>f);if(d=ib-(m-f),f=m-ib,d>g){if(a.match_start=b,g=d,d>=h)break;n=j[f+g-1],o=j[f+g]}}while((b=l[b&k])>i&&0!==--e);return g<=a.lookahead?g:a.lookahead}function m(a){var b,c,d,e,f,g=a.w_size;do{if(e=a.window_size-a.lookahead-a.strstart,a.strstart>=g+(g-jb)){C.arraySet(a.window,a.window,g,g,0),a.match_start-=g,a.strstart-=g,a.block_start-=g,c=a.hash_size,b=c;do d=a.head[--b],a.head[b]=d>=g?d-g:0;while(--c);c=g,b=c;do d=a.prev[--b],a.prev[b]=d>=g?d-g:0;while(--c);e+=g}if(0===a.strm.avail_in)break;if(c=k(a.strm,a.window,a.strstart+a.lookahead,e),a.lookahead+=c,a.lookahead+a.insert>=hb)for(f=a.strstart-a.insert,a.ins_h=a.window[f],a.ins_h=(a.ins_h<<a.hash_shift^a.window[f+1])&a.hash_mask;a.insert&&(a.ins_h=(a.ins_h<<a.hash_shift^a.window[f+hb-1])&a.hash_mask,a.prev[f&a.w_mask]=a.head[a.ins_h],a.head[a.ins_h]=f,f++,a.insert--,!(a.lookahead+a.insert<hb)););}while(a.lookahead<jb&&0!==a.strm.avail_in)}function n(a,b){var c=65535;for(c>a.pending_buf_size-5&&(c=a.pending_buf_size-5);;){if(a.lookahead<=1){if(m(a),0===a.lookahead&&b===H)return sb;if(0===a.lookahead)break}a.strstart+=a.lookahead,a.lookahead=0;var d=a.block_start+c;if((0===a.strstart||a.strstart>=d)&&(a.lookahead=a.strstart-d,a.strstart=d,h(a,!1),0===a.strm.avail_out))return sb;if(a.strstart-a.block_start>=a.w_size-jb&&(h(a,!1),0===a.strm.avail_out))return sb}return a.insert=0,b===K?(h(a,!0),0===a.strm.avail_out?ub:vb):a.strstart>a.block_start&&(h(a,!1),0===a.strm.avail_out)?sb:sb}function o(a,b){for(var c,d;;){if(a.lookahead<jb){if(m(a),a.lookahead<jb&&b===H)return sb;if(0===a.lookahead)break}if(c=0,a.lookahead>=hb&&(a.ins_h=(a.ins_h<<a.hash_shift^a.window[a.strstart+hb-1])&a.hash_mask,c=a.prev[a.strstart&a.w_mask]=a.head[a.ins_h],a.head[a.ins_h]=a.strstart),0!==c&&a.strstart-c<=a.w_size-jb&&(a.match_length=l(a,c)),a.match_length>=hb)if(d=D._tr_tally(a,a.strstart-a.match_start,a.match_length-hb),a.lookahead-=a.match_length,a.match_length<=a.max_lazy_match&&a.lookahead>=hb){a.match_length--;do a.strstart++,a.ins_h=(a.ins_h<<a.hash_shift^a.window[a.strstart+hb-1])&a.hash_mask,c=a.prev[a.strstart&a.w_mask]=a.head[a.ins_h],a.head[a.ins_h]=a.strstart;while(0!==--a.match_length);a.strstart++}else a.strstart+=a.match_length,a.match_length=0,a.ins_h=a.window[a.strstart],a.ins_h=(a.ins_h<<a.hash_shift^a.window[a.strstart+1])&a.hash_mask;else d=D._tr_tally(a,0,a.window[a.strstart]),a.lookahead--,a.strstart++;if(d&&(h(a,!1),0===a.strm.avail_out))return sb}return a.insert=a.strstart<hb-1?a.strstart:hb-1,b===K?(h(a,!0),0===a.strm.avail_out?ub:vb):a.last_lit&&(h(a,!1),0===a.strm.avail_out)?sb:tb}function p(a,b){for(var c,d,e;;){if(a.lookahead<jb){if(m(a),a.lookahead<jb&&b===H)return sb;if(0===a.lookahead)break}if(c=0,a.lookahead>=hb&&(a.ins_h=(a.ins_h<<a.hash_shift^a.window[a.strstart+hb-1])&a.hash_mask,c=a.prev[a.strstart&a.w_mask]=a.head[a.ins_h],a.head[a.ins_h]=a.strstart),a.prev_length=a.match_length,a.prev_match=a.match_start,a.match_length=hb-1,0!==c&&a.prev_length<a.max_lazy_match&&a.strstart-c<=a.w_size-jb&&(a.match_length=l(a,c),a.match_length<=5&&(a.strategy===S||a.match_length===hb&&a.strstart-a.match_start>4096)&&(a.match_length=hb-1)),a.prev_length>=hb&&a.match_length<=a.prev_length){e=a.strstart+a.lookahead-hb,d=D._tr_tally(a,a.strstart-1-a.prev_match,a.prev_length-hb),a.lookahead-=a.prev_length-1,a.prev_length-=2;do++a.strstart<=e&&(a.ins_h=(a.ins_h<<a.hash_shift^a.window[a.strstart+hb-1])&a.hash_mask,c=a.prev[a.strstart&a.w_mask]=a.head[a.ins_h],a.head[a.ins_h]=a.strstart);while(0!==--a.prev_length);if(a.match_available=0,a.match_length=hb-1,a.strstart++,d&&(h(a,!1),0===a.strm.avail_out))return sb}else if(a.match_available){if(d=D._tr_tally(a,0,a.window[a.strstart-1]),d&&h(a,!1),a.strstart++,a.lookahead--,0===a.strm.avail_out)return sb}else a.match_available=1,a.strstart++,a.lookahead--}return a.match_available&&(d=D._tr_tally(a,0,a.window[a.strstart-1]),a.match_available=0),a.insert=a.strstart<hb-1?a.strstart:hb-1,b===K?(h(a,!0),0===a.strm.avail_out?ub:vb):a.last_lit&&(h(a,!1),0===a.strm.avail_out)?sb:tb}function q(a,b){for(var c,d,e,f,g=a.window;;){if(a.lookahead<=ib){if(m(a),a.lookahead<=ib&&b===H)return sb;if(0===a.lookahead)break}if(a.match_length=0,a.lookahead>=hb&&a.strstart>0&&(e=a.strstart-1,d=g[e],d===g[++e]&&d===g[++e]&&d===g[++e])){f=a.strstart+ib;do;while(d===g[++e]&&d===g[++e]&&d===g[++e]&&d===g[++e]&&d===g[++e]&&d===g[++e]&&d===g[++e]&&d===g[++e]&&f>e);a.match_length=ib-(f-e),a.match_length>a.lookahead&&(a.match_length=a.lookahead)}if(a.match_length>=hb?(c=D._tr_tally(a,1,a.match_length-hb),a.lookahead-=a.match_length,a.strstart+=a.match_length,a.match_length=0):(c=D._tr_tally(a,0,a.window[a.strstart]),a.lookahead--,a.strstart++),c&&(h(a,!1),0===a.strm.avail_out))return sb}return a.insert=0,b===K?(h(a,!0),0===a.strm.avail_out?ub:vb):a.last_lit&&(h(a,!1),0===a.strm.avail_out)?sb:tb}function r(a,b){for(var c;;){if(0===a.lookahead&&(m(a),0===a.lookahead)){if(b===H)return sb;break}if(a.match_length=0,c=D._tr_tally(a,0,a.window[a.strstart]),a.lookahead--,a.strstart++,c&&(h(a,!1),0===a.strm.avail_out))return sb}return a.insert=0,b===K?(h(a,!0),0===a.strm.avail_out?ub:vb):a.last_lit&&(h(a,!1),0===a.strm.avail_out)?sb:tb}function s(a){a.window_size=2*a.w_size,f(a.head),a.max_lazy_match=B[a.level].max_lazy,a.good_match=B[a.level].good_length,a.nice_match=B[a.level].nice_length,a.max_chain_length=B[a.level].max_chain,a.strstart=0,a.block_start=0,a.lookahead=0,a.insert=0,a.match_length=a.prev_length=hb-1,a.match_available=0,a.ins_h=0}function t(){this.strm=null,this.status=0,this.pending_buf=null,this.pending_buf_size=0,this.pending_out=0,this.pending=0,this.wrap=0,this.gzhead=null,this.gzindex=0,this.method=Y,this.last_flush=-1,this.w_size=0,this.w_bits=0,this.w_mask=0,this.window=null,this.window_size=0,this.prev=null,this.head=null,this.ins_h=0,this.hash_size=0,this.hash_bits=0,this.hash_mask=0,this.hash_shift=0,this.block_start=0,this.match_length=0,this.prev_match=0,this.match_available=0,this.strstart=0,this.match_start=0,this.lookahead=0,this.prev_length=0,this.max_chain_length=0,this.max_lazy_match=0,this.level=0,this.strategy=0,this.good_match=0,this.nice_match=0,this.dyn_ltree=new C.Buf16(2*fb),this.dyn_dtree=new C.Buf16(2*(2*db+1)),this.bl_tree=new C.Buf16(2*(2*eb+1)),f(this.dyn_ltree),f(this.dyn_dtree),f(this.bl_tree),this.l_desc=null,this.d_desc=null,this.bl_desc=null,this.bl_count=new C.Buf16(gb+1),this.heap=new C.Buf16(2*cb+1),f(this.heap),this.heap_len=0,this.heap_max=0,this.depth=new C.Buf16(2*cb+1),f(this.depth),this.l_buf=0,this.lit_bufsize=0,this.last_lit=0,this.d_buf=0,this.opt_len=0,this.static_len=0,this.matches=0,this.insert=0,this.bi_buf=0,this.bi_valid=0}function u(a){var b;return a&&a.state?(a.total_in=a.total_out=0,a.data_type=X,b=a.state,b.pending=0,b.pending_out=0,b.wrap<0&&(b.wrap=-b.wrap),b.status=b.wrap?lb:qb,a.adler=2===b.wrap?0:1,b.last_flush=H,D._tr_init(b),M):d(a,O)}function v(a){var b=u(a);return b===M&&s(a.state),b}function w(a,b){return a&&a.state?2!==a.state.wrap?O:(a.state.gzhead=b,M):O}function x(a,b,c,e,f,g){if(!a)return O;var h=1;if(b===R&&(b=6),0>e?(h=0,e=-e):e>15&&(h=2,e-=16),1>f||f>Z||c!==Y||8>e||e>15||0>b||b>9||0>g||g>V)return d(a,O);8===e&&(e=9);var i=new t;return a.state=i,i.strm=a,i.wrap=h,i.gzhead=null,i.w_bits=e,i.w_size=1<<i.w_bits,i.w_mask=i.w_size-1,i.hash_bits=f+7,i.hash_size=1<<i.hash_bits,i.hash_mask=i.hash_size-1,i.hash_shift=~~((i.hash_bits+hb-1)/hb),i.window=new C.Buf8(2*i.w_size),i.head=new C.Buf16(i.hash_size),i.prev=new C.Buf16(i.w_size),i.lit_bufsize=1<<f+6,i.pending_buf_size=4*i.lit_bufsize,i.pending_buf=new C.Buf8(i.pending_buf_size),i.d_buf=i.lit_bufsize>>1,i.l_buf=3*i.lit_bufsize,i.level=b,i.strategy=g,i.method=c,v(a)}function y(a,b){return x(a,b,Y,$,_,W)}function z(a,b){var c,h,k,l;if(!a||!a.state||b>L||0>b)return a?d(a,O):O;if(h=a.state,!a.output||!a.input&&0!==a.avail_in||h.status===rb&&b!==K)return d(a,0===a.avail_out?Q:O);if(h.strm=a,c=h.last_flush,h.last_flush=b,h.status===lb)if(2===h.wrap)a.adler=0,i(h,31),i(h,139),i(h,8),h.gzhead?(i(h,(h.gzhead.text?1:0)+(h.gzhead.hcrc?2:0)+(h.gzhead.extra?4:0)+(h.gzhead.name?8:0)+(h.gzhead.comment?16:0)),i(h,255&h.gzhead.time),i(h,h.gzhead.time>>8&255),i(h,h.gzhead.time>>16&255),i(h,h.gzhead.time>>24&255),i(h,9===h.level?2:h.strategy>=T||h.level<2?4:0),i(h,255&h.gzhead.os),h.gzhead.extra&&h.gzhead.extra.length&&(i(h,255&h.gzhead.extra.length),i(h,h.gzhead.extra.length>>8&255)),h.gzhead.hcrc&&(a.adler=F(a.adler,h.pending_buf,h.pending,0)),h.gzindex=0,h.status=mb):(i(h,0),i(h,0),i(h,0),i(h,0),i(h,0),i(h,9===h.level?2:h.strategy>=T||h.level<2?4:0),i(h,wb),h.status=qb);else{var m=Y+(h.w_bits-8<<4)<<8,n=-1;n=h.strategy>=T||h.level<2?0:h.level<6?1:6===h.level?2:3,m|=n<<6,0!==h.strstart&&(m|=kb),m+=31-m%31,h.status=qb,j(h,m),0!==h.strstart&&(j(h,a.adler>>>16),j(h,65535&a.adler)),a.adler=1}if(h.status===mb)if(h.gzhead.extra){for(k=h.pending;h.gzindex<(65535&h.gzhead.extra.length)&&(h.pending!==h.pending_buf_size||(h.gzhead.hcrc&&h.pending>k&&(a.adler=F(a.adler,h.pending_buf,h.pending-k,k)),g(a),k=h.pending,h.pending!==h.pending_buf_size));)i(h,255&h.gzhead.extra[h.gzindex]),h.gzindex++;h.gzhead.hcrc&&h.pending>k&&(a.adler=F(a.adler,h.pending_buf,h.pending-k,k)),h.gzindex===h.gzhead.extra.length&&(h.gzindex=0,h.status=nb)}else h.status=nb;if(h.status===nb)if(h.gzhead.name){k=h.pending;do{if(h.pending===h.pending_buf_size&&(h.gzhead.hcrc&&h.pending>k&&(a.adler=F(a.adler,h.pending_buf,h.pending-k,k)),g(a),k=h.pending,h.pending===h.pending_buf_size)){l=1;break}l=h.gzindex<h.gzhead.name.length?255&h.gzhead.name.charCodeAt(h.gzindex++):0,i(h,l)}while(0!==l);h.gzhead.hcrc&&h.pending>k&&(a.adler=F(a.adler,h.pending_buf,h.pending-k,k)),0===l&&(h.gzindex=0,h.status=ob)}else h.status=ob;if(h.status===ob)if(h.gzhead.comment){k=h.pending;do{if(h.pending===h.pending_buf_size&&(h.gzhead.hcrc&&h.pending>k&&(a.adler=F(a.adler,h.pending_buf,h.pending-k,k)),g(a),k=h.pending,h.pending===h.pending_buf_size)){l=1;break}l=h.gzindex<h.gzhead.comment.length?255&h.gzhead.comment.charCodeAt(h.gzindex++):0,i(h,l)}while(0!==l);h.gzhead.hcrc&&h.pending>k&&(a.adler=F(a.adler,h.pending_buf,h.pending-k,k)),0===l&&(h.status=pb)}else h.status=pb;if(h.status===pb&&(h.gzhead.hcrc?(h.pending+2>h.pending_buf_size&&g(a),h.pending+2<=h.pending_buf_size&&(i(h,255&a.adler),i(h,a.adler>>8&255),a.adler=0,h.status=qb)):h.status=qb),0!==h.pending){if(g(a),0===a.avail_out)return h.last_flush=-1,M}else if(0===a.avail_in&&e(b)<=e(c)&&b!==K)return d(a,Q);if(h.status===rb&&0!==a.avail_in)return d(a,Q);if(0!==a.avail_in||0!==h.lookahead||b!==H&&h.status!==rb){var o=h.strategy===T?r(h,b):h.strategy===U?q(h,b):B[h.level].func(h,b);if((o===ub||o===vb)&&(h.status=rb),o===sb||o===ub)return 0===a.avail_out&&(h.last_flush=-1),M;if(o===tb&&(b===I?D._tr_align(h):b!==L&&(D._tr_stored_block(h,0,0,!1),b===J&&(f(h.head),0===h.lookahead&&(h.strstart=0,h.block_start=0,h.insert=0))),g(a),0===a.avail_out))return h.last_flush=-1,M}return b!==K?M:h.wrap<=0?N:(2===h.wrap?(i(h,255&a.adler),i(h,a.adler>>8&255),i(h,a.adler>>16&255),i(h,a.adler>>24&255),i(h,255&a.total_in),i(h,a.total_in>>8&255),i(h,a.total_in>>16&255),i(h,a.total_in>>24&255)):(j(h,a.adler>>>16),j(h,65535&a.adler)),g(a),h.wrap>0&&(h.wrap=-h.wrap),0!==h.pending?M:N)}function A(a){var b;return a&&a.state?(b=a.state.status,b!==lb&&b!==mb&&b!==nb&&b!==ob&&b!==pb&&b!==qb&&b!==rb?d(a,O):(a.state=null,b===qb?d(a,P):M)):O}var B,C=a("../utils/common"),D=a("./trees"),E=a("./adler32"),F=a("./crc32"),G=a("./messages"),H=0,I=1,J=3,K=4,L=5,M=0,N=1,O=-2,P=-3,Q=-5,R=-1,S=1,T=2,U=3,V=4,W=0,X=2,Y=8,Z=9,$=15,_=8,ab=29,bb=256,cb=bb+1+ab,db=30,eb=19,fb=2*cb+1,gb=15,hb=3,ib=258,jb=ib+hb+1,kb=32,lb=42,mb=69,nb=73,ob=91,pb=103,qb=113,rb=666,sb=1,tb=2,ub=3,vb=4,wb=3,xb=function(a,b,c,d,e){this.good_length=a,this.max_lazy=b,this.nice_length=c,this.max_chain=d,this.func=e};B=[new xb(0,0,0,0,n),new xb(4,4,8,4,o),new xb(4,5,16,8,o),new xb(4,6,32,32,o),new xb(4,4,16,16,p),new xb(8,16,32,32,p),new xb(8,16,128,128,p),new xb(8,32,128,256,p),new xb(32,128,258,1024,p),new xb(32,258,258,4096,p)],c.deflateInit=y,c.deflateInit2=x,c.deflateReset=v,c.deflateResetKeep=u,c.deflateSetHeader=w,c.deflate=z,c.deflateEnd=A,c.deflateInfo="pako deflate (from Nodeca project)"},{"../utils/common":27,"./adler32":29,"./crc32":31,"./messages":37,"./trees":38}],33:[function(a,b){"use strict";function c(){this.text=0,this.time=0,this.xflags=0,this.os=0,this.extra=null,this.extra_len=0,this.name="",this.comment="",this.hcrc=0,this.done=!1}b.exports=c},{}],34:[function(a,b){"use strict";var c=30,d=12;b.exports=function(a,b){var e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C;e=a.state,f=a.next_in,B=a.input,g=f+(a.avail_in-5),h=a.next_out,C=a.output,i=h-(b-a.avail_out),j=h+(a.avail_out-257),k=e.dmax,l=e.wsize,m=e.whave,n=e.wnext,o=e.window,p=e.hold,q=e.bits,r=e.lencode,s=e.distcode,t=(1<<e.lenbits)-1,u=(1<<e.distbits)-1;a:do{15>q&&(p+=B[f++]<<q,q+=8,p+=B[f++]<<q,q+=8),v=r[p&t];b:for(;;){if(w=v>>>24,p>>>=w,q-=w,w=v>>>16&255,0===w)C[h++]=65535&v;else{if(!(16&w)){if(0===(64&w)){v=r[(65535&v)+(p&(1<<w)-1)];continue b}if(32&w){e.mode=d;break a}a.msg="invalid literal/length code",e.mode=c;break a}x=65535&v,w&=15,w&&(w>q&&(p+=B[f++]<<q,q+=8),x+=p&(1<<w)-1,p>>>=w,q-=w),15>q&&(p+=B[f++]<<q,q+=8,p+=B[f++]<<q,q+=8),v=s[p&u];c:for(;;){if(w=v>>>24,p>>>=w,q-=w,w=v>>>16&255,!(16&w)){if(0===(64&w)){v=s[(65535&v)+(p&(1<<w)-1)];continue c}a.msg="invalid distance code",e.mode=c;break a}if(y=65535&v,w&=15,w>q&&(p+=B[f++]<<q,q+=8,w>q&&(p+=B[f++]<<q,q+=8)),y+=p&(1<<w)-1,y>k){a.msg="invalid distance too far back",e.mode=c;break a}if(p>>>=w,q-=w,w=h-i,y>w){if(w=y-w,w>m&&e.sane){a.msg="invalid distance too far back",e.mode=c;break a}if(z=0,A=o,0===n){if(z+=l-w,x>w){x-=w;do C[h++]=o[z++];while(--w);z=h-y,A=C}}else if(w>n){if(z+=l+n-w,w-=n,x>w){x-=w;do C[h++]=o[z++];while(--w);if(z=0,x>n){w=n,x-=w;do C[h++]=o[z++];while(--w);z=h-y,A=C}}}else if(z+=n-w,x>w){x-=w;do C[h++]=o[z++];while(--w);z=h-y,A=C}for(;x>2;)C[h++]=A[z++],C[h++]=A[z++],C[h++]=A[z++],x-=3;x&&(C[h++]=A[z++],x>1&&(C[h++]=A[z++]))}else{z=h-y;do C[h++]=C[z++],C[h++]=C[z++],C[h++]=C[z++],x-=3;while(x>2);x&&(C[h++]=C[z++],x>1&&(C[h++]=C[z++]))}break}}break}}while(g>f&&j>h);x=q>>3,f-=x,q-=x<<3,p&=(1<<q)-1,a.next_in=f,a.next_out=h,a.avail_in=g>f?5+(g-f):5-(f-g),a.avail_out=j>h?257+(j-h):257-(h-j),e.hold=p,e.bits=q}},{}],35:[function(a,b,c){"use strict";function d(a){return(a>>>24&255)+(a>>>8&65280)+((65280&a)<<8)+((255&a)<<24)}function e(){this.mode=0,this.last=!1,this.wrap=0,this.havedict=!1,this.flags=0,this.dmax=0,this.check=0,this.total=0,this.head=null,this.wbits=0,this.wsize=0,this.whave=0,this.wnext=0,this.window=null,this.hold=0,this.bits=0,this.length=0,this.offset=0,this.extra=0,this.lencode=null,this.distcode=null,this.lenbits=0,this.distbits=0,this.ncode=0,this.nlen=0,this.ndist=0,this.have=0,this.next=null,this.lens=new r.Buf16(320),this.work=new r.Buf16(288),this.lendyn=null,this.distdyn=null,this.sane=0,this.back=0,this.was=0}function f(a){var b;return a&&a.state?(b=a.state,a.total_in=a.total_out=b.total=0,a.msg="",b.wrap&&(a.adler=1&b.wrap),b.mode=K,b.last=0,b.havedict=0,b.dmax=32768,b.head=null,b.hold=0,b.bits=0,b.lencode=b.lendyn=new r.Buf32(ob),b.distcode=b.distdyn=new r.Buf32(pb),b.sane=1,b.back=-1,C):F}function g(a){var b;return a&&a.state?(b=a.state,b.wsize=0,b.whave=0,b.wnext=0,f(a)):F}function h(a,b){var c,d;return a&&a.state?(d=a.state,0>b?(c=0,b=-b):(c=(b>>4)+1,48>b&&(b&=15)),b&&(8>b||b>15)?F:(null!==d.window&&d.wbits!==b&&(d.window=null),d.wrap=c,d.wbits=b,g(a))):F}function i(a,b){var c,d;return a?(d=new e,a.state=d,d.window=null,c=h(a,b),c!==C&&(a.state=null),c):F}function j(a){return i(a,rb)}function k(a){if(sb){var b;for(p=new r.Buf32(512),q=new r.Buf32(32),b=0;144>b;)a.lens[b++]=8;for(;256>b;)a.lens[b++]=9;for(;280>b;)a.lens[b++]=7;for(;288>b;)a.lens[b++]=8;for(v(x,a.lens,0,288,p,0,a.work,{bits:9}),b=0;32>b;)a.lens[b++]=5;v(y,a.lens,0,32,q,0,a.work,{bits:5}),sb=!1}a.lencode=p,a.lenbits=9,a.distcode=q,a.distbits=5}function l(a,b,c,d){var e,f=a.state;return null===f.window&&(f.wsize=1<<f.wbits,f.wnext=0,f.whave=0,f.window=new r.Buf8(f.wsize)),d>=f.wsize?(r.arraySet(f.window,b,c-f.wsize,f.wsize,0),f.wnext=0,f.whave=f.wsize):(e=f.wsize-f.wnext,e>d&&(e=d),r.arraySet(f.window,b,c-d,e,f.wnext),d-=e,d?(r.arraySet(f.window,b,c-d,d,0),f.wnext=d,f.whave=f.wsize):(f.wnext+=e,f.wnext===f.wsize&&(f.wnext=0),f.whave<f.wsize&&(f.whave+=e))),0}function m(a,b){var c,e,f,g,h,i,j,m,n,o,p,q,ob,pb,qb,rb,sb,tb,ub,vb,wb,xb,yb,zb,Ab=0,Bb=new r.Buf8(4),Cb=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15];if(!a||!a.state||!a.output||!a.input&&0!==a.avail_in)return F;c=a.state,c.mode===V&&(c.mode=W),h=a.next_out,f=a.output,j=a.avail_out,g=a.next_in,e=a.input,i=a.avail_in,m=c.hold,n=c.bits,o=i,p=j,xb=C;a:for(;;)switch(c.mode){case K:if(0===c.wrap){c.mode=W;break}for(;16>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if(2&c.wrap&&35615===m){c.check=0,Bb[0]=255&m,Bb[1]=m>>>8&255,c.check=t(c.check,Bb,2,0),m=0,n=0,c.mode=L;break}if(c.flags=0,c.head&&(c.head.done=!1),!(1&c.wrap)||(((255&m)<<8)+(m>>8))%31){a.msg="incorrect header check",c.mode=lb;break}if((15&m)!==J){a.msg="unknown compression method",c.mode=lb;break}if(m>>>=4,n-=4,wb=(15&m)+8,0===c.wbits)c.wbits=wb;else if(wb>c.wbits){a.msg="invalid window size",c.mode=lb;break}c.dmax=1<<wb,a.adler=c.check=1,c.mode=512&m?T:V,m=0,n=0;break;case L:for(;16>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if(c.flags=m,(255&c.flags)!==J){a.msg="unknown compression method",c.mode=lb;break}if(57344&c.flags){a.msg="unknown header flags set",c.mode=lb;break}c.head&&(c.head.text=m>>8&1),512&c.flags&&(Bb[0]=255&m,Bb[1]=m>>>8&255,c.check=t(c.check,Bb,2,0)),m=0,n=0,c.mode=M;case M:for(;32>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}c.head&&(c.head.time=m),512&c.flags&&(Bb[0]=255&m,Bb[1]=m>>>8&255,Bb[2]=m>>>16&255,Bb[3]=m>>>24&255,c.check=t(c.check,Bb,4,0)),m=0,n=0,c.mode=N;case N:for(;16>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}c.head&&(c.head.xflags=255&m,c.head.os=m>>8),512&c.flags&&(Bb[0]=255&m,Bb[1]=m>>>8&255,c.check=t(c.check,Bb,2,0)),m=0,n=0,c.mode=O;case O:if(1024&c.flags){for(;16>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}c.length=m,c.head&&(c.head.extra_len=m),512&c.flags&&(Bb[0]=255&m,Bb[1]=m>>>8&255,c.check=t(c.check,Bb,2,0)),m=0,n=0}else c.head&&(c.head.extra=null);c.mode=P;case P:if(1024&c.flags&&(q=c.length,q>i&&(q=i),q&&(c.head&&(wb=c.head.extra_len-c.length,c.head.extra||(c.head.extra=new Array(c.head.extra_len)),r.arraySet(c.head.extra,e,g,q,wb)),512&c.flags&&(c.check=t(c.check,e,q,g)),i-=q,g+=q,c.length-=q),c.length))break a;c.length=0,c.mode=Q;case Q:if(2048&c.flags){if(0===i)break a;q=0;do wb=e[g+q++],c.head&&wb&&c.length<65536&&(c.head.name+=String.fromCharCode(wb));while(wb&&i>q);if(512&c.flags&&(c.check=t(c.check,e,q,g)),i-=q,g+=q,wb)break a}else c.head&&(c.head.name=null);c.length=0,c.mode=R;case R:if(4096&c.flags){if(0===i)break a;q=0;do wb=e[g+q++],c.head&&wb&&c.length<65536&&(c.head.comment+=String.fromCharCode(wb));while(wb&&i>q);if(512&c.flags&&(c.check=t(c.check,e,q,g)),i-=q,g+=q,wb)break a}else c.head&&(c.head.comment=null);c.mode=S;case S:if(512&c.flags){for(;16>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if(m!==(65535&c.check)){a.msg="header crc mismatch",c.mode=lb;break}m=0,n=0}c.head&&(c.head.hcrc=c.flags>>9&1,c.head.done=!0),a.adler=c.check=0,c.mode=V;break;case T:for(;32>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}a.adler=c.check=d(m),m=0,n=0,c.mode=U;case U:if(0===c.havedict)return a.next_out=h,a.avail_out=j,a.next_in=g,a.avail_in=i,c.hold=m,c.bits=n,E;a.adler=c.check=1,c.mode=V;case V:if(b===A||b===B)break a;case W:if(c.last){m>>>=7&n,n-=7&n,c.mode=ib;break}for(;3>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}switch(c.last=1&m,m>>>=1,n-=1,3&m){case 0:c.mode=X;break;case 1:if(k(c),c.mode=bb,b===B){m>>>=2,n-=2;break a}break;case 2:c.mode=$;break;case 3:a.msg="invalid block type",c.mode=lb}m>>>=2,n-=2;break;case X:for(m>>>=7&n,n-=7&n;32>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if((65535&m)!==(m>>>16^65535)){a.msg="invalid stored block lengths",c.mode=lb;break}if(c.length=65535&m,m=0,n=0,c.mode=Y,b===B)break a;case Y:c.mode=Z;case Z:if(q=c.length){if(q>i&&(q=i),q>j&&(q=j),0===q)break a;r.arraySet(f,e,g,q,h),i-=q,g+=q,j-=q,h+=q,c.length-=q;break}c.mode=V;break;case $:for(;14>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if(c.nlen=(31&m)+257,m>>>=5,n-=5,c.ndist=(31&m)+1,m>>>=5,n-=5,c.ncode=(15&m)+4,m>>>=4,n-=4,c.nlen>286||c.ndist>30){a.msg="too many length or distance symbols",c.mode=lb;break}c.have=0,c.mode=_;case _:for(;c.have<c.ncode;){for(;3>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}c.lens[Cb[c.have++]]=7&m,m>>>=3,n-=3}for(;c.have<19;)c.lens[Cb[c.have++]]=0;if(c.lencode=c.lendyn,c.lenbits=7,yb={bits:c.lenbits},xb=v(w,c.lens,0,19,c.lencode,0,c.work,yb),c.lenbits=yb.bits,xb){a.msg="invalid code lengths set",c.mode=lb;break}c.have=0,c.mode=ab;case ab:for(;c.have<c.nlen+c.ndist;){for(;Ab=c.lencode[m&(1<<c.lenbits)-1],qb=Ab>>>24,rb=Ab>>>16&255,sb=65535&Ab,!(n>=qb);){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if(16>sb)m>>>=qb,n-=qb,c.lens[c.have++]=sb;else{if(16===sb){for(zb=qb+2;zb>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if(m>>>=qb,n-=qb,0===c.have){a.msg="invalid bit length repeat",c.mode=lb;break}wb=c.lens[c.have-1],q=3+(3&m),m>>>=2,n-=2}else if(17===sb){for(zb=qb+3;zb>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}m>>>=qb,n-=qb,wb=0,q=3+(7&m),m>>>=3,n-=3}else{for(zb=qb+7;zb>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}m>>>=qb,n-=qb,wb=0,q=11+(127&m),m>>>=7,n-=7}if(c.have+q>c.nlen+c.ndist){a.msg="invalid bit length repeat",c.mode=lb;break}for(;q--;)c.lens[c.have++]=wb}}if(c.mode===lb)break;if(0===c.lens[256]){a.msg="invalid code -- missing end-of-block",c.mode=lb;break}if(c.lenbits=9,yb={bits:c.lenbits},xb=v(x,c.lens,0,c.nlen,c.lencode,0,c.work,yb),c.lenbits=yb.bits,xb){a.msg="invalid literal/lengths set",c.mode=lb;break}if(c.distbits=6,c.distcode=c.distdyn,yb={bits:c.distbits},xb=v(y,c.lens,c.nlen,c.ndist,c.distcode,0,c.work,yb),c.distbits=yb.bits,xb){a.msg="invalid distances set",c.mode=lb;break}if(c.mode=bb,b===B)break a;case bb:c.mode=cb;case cb:if(i>=6&&j>=258){a.next_out=h,a.avail_out=j,a.next_in=g,a.avail_in=i,c.hold=m,c.bits=n,u(a,p),h=a.next_out,f=a.output,j=a.avail_out,g=a.next_in,e=a.input,i=a.avail_in,m=c.hold,n=c.bits,c.mode===V&&(c.back=-1);break}for(c.back=0;Ab=c.lencode[m&(1<<c.lenbits)-1],qb=Ab>>>24,rb=Ab>>>16&255,sb=65535&Ab,!(n>=qb);){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if(rb&&0===(240&rb)){for(tb=qb,ub=rb,vb=sb;Ab=c.lencode[vb+((m&(1<<tb+ub)-1)>>tb)],qb=Ab>>>24,rb=Ab>>>16&255,sb=65535&Ab,!(n>=tb+qb);){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}m>>>=tb,n-=tb,c.back+=tb}if(m>>>=qb,n-=qb,c.back+=qb,c.length=sb,0===rb){c.mode=hb;break}if(32&rb){c.back=-1,c.mode=V;break}if(64&rb){a.msg="invalid literal/length code",c.mode=lb;break}c.extra=15&rb,c.mode=db;case db:if(c.extra){for(zb=c.extra;zb>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}c.length+=m&(1<<c.extra)-1,m>>>=c.extra,n-=c.extra,c.back+=c.extra}c.was=c.length,c.mode=eb;case eb:for(;Ab=c.distcode[m&(1<<c.distbits)-1],qb=Ab>>>24,rb=Ab>>>16&255,sb=65535&Ab,!(n>=qb);){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if(0===(240&rb)){for(tb=qb,ub=rb,vb=sb;Ab=c.distcode[vb+((m&(1<<tb+ub)-1)>>tb)],qb=Ab>>>24,rb=Ab>>>16&255,sb=65535&Ab,!(n>=tb+qb);){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}m>>>=tb,n-=tb,c.back+=tb}if(m>>>=qb,n-=qb,c.back+=qb,64&rb){a.msg="invalid distance code",c.mode=lb;break}c.offset=sb,c.extra=15&rb,c.mode=fb;case fb:if(c.extra){for(zb=c.extra;zb>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}c.offset+=m&(1<<c.extra)-1,m>>>=c.extra,n-=c.extra,c.back+=c.extra}if(c.offset>c.dmax){a.msg="invalid distance too far back",c.mode=lb;break}c.mode=gb;case gb:if(0===j)break a;if(q=p-j,c.offset>q){if(q=c.offset-q,q>c.whave&&c.sane){a.msg="invalid distance too far back",c.mode=lb;break}q>c.wnext?(q-=c.wnext,ob=c.wsize-q):ob=c.wnext-q,q>c.length&&(q=c.length),pb=c.window}else pb=f,ob=h-c.offset,q=c.length;q>j&&(q=j),j-=q,c.length-=q;do f[h++]=pb[ob++];while(--q);0===c.length&&(c.mode=cb);break;case hb:if(0===j)break a;f[h++]=c.length,j--,c.mode=cb;break;case ib:if(c.wrap){for(;32>n;){if(0===i)break a;i--,m|=e[g++]<<n,n+=8}if(p-=j,a.total_out+=p,c.total+=p,p&&(a.adler=c.check=c.flags?t(c.check,f,p,h-p):s(c.check,f,p,h-p)),p=j,(c.flags?m:d(m))!==c.check){a.msg="incorrect data check",c.mode=lb;break}m=0,n=0}c.mode=jb;case jb:if(c.wrap&&c.flags){for(;32>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if(m!==(4294967295&c.total)){a.msg="incorrect length check",c.mode=lb;break}m=0,n=0}c.mode=kb;case kb:xb=D;break a;case lb:xb=G;break a;case mb:return H;case nb:default:return F}return a.next_out=h,a.avail_out=j,a.next_in=g,a.avail_in=i,c.hold=m,c.bits=n,(c.wsize||p!==a.avail_out&&c.mode<lb&&(c.mode<ib||b!==z))&&l(a,a.output,a.next_out,p-a.avail_out)?(c.mode=mb,H):(o-=a.avail_in,p-=a.avail_out,a.total_in+=o,a.total_out+=p,c.total+=p,c.wrap&&p&&(a.adler=c.check=c.flags?t(c.check,f,p,a.next_out-p):s(c.check,f,p,a.next_out-p)),a.data_type=c.bits+(c.last?64:0)+(c.mode===V?128:0)+(c.mode===bb||c.mode===Y?256:0),(0===o&&0===p||b===z)&&xb===C&&(xb=I),xb)}function n(a){if(!a||!a.state)return F;var b=a.state;return b.window&&(b.window=null),a.state=null,C}function o(a,b){var c;return a&&a.state?(c=a.state,0===(2&c.wrap)?F:(c.head=b,b.done=!1,C)):F}var p,q,r=a("../utils/common"),s=a("./adler32"),t=a("./crc32"),u=a("./inffast"),v=a("./inftrees"),w=0,x=1,y=2,z=4,A=5,B=6,C=0,D=1,E=2,F=-2,G=-3,H=-4,I=-5,J=8,K=1,L=2,M=3,N=4,O=5,P=6,Q=7,R=8,S=9,T=10,U=11,V=12,W=13,X=14,Y=15,Z=16,$=17,_=18,ab=19,bb=20,cb=21,db=22,eb=23,fb=24,gb=25,hb=26,ib=27,jb=28,kb=29,lb=30,mb=31,nb=32,ob=852,pb=592,qb=15,rb=qb,sb=!0;c.inflateReset=g,c.inflateReset2=h,c.inflateResetKeep=f,c.inflateInit=j,c.inflateInit2=i,c.inflate=m,c.inflateEnd=n,c.inflateGetHeader=o,c.inflateInfo="pako inflate (from Nodeca project)"},{"../utils/common":27,"./adler32":29,"./crc32":31,"./inffast":34,"./inftrees":36}],36:[function(a,b){"use strict";var c=a("../utils/common"),d=15,e=852,f=592,g=0,h=1,i=2,j=[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,0,0],k=[16,16,16,16,16,16,16,16,17,17,17,17,18,18,18,18,19,19,19,19,20,20,20,20,21,21,21,21,16,72,78],l=[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0],m=[16,16,16,16,17,17,18,18,19,19,20,20,21,21,22,22,23,23,24,24,25,25,26,26,27,27,28,28,29,29,64,64];b.exports=function(a,b,n,o,p,q,r,s){var t,u,v,w,x,y,z,A,B,C=s.bits,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=null,O=0,P=new c.Buf16(d+1),Q=new c.Buf16(d+1),R=null,S=0;for(D=0;d>=D;D++)P[D]=0;for(E=0;o>E;E++)P[b[n+E]]++;for(H=C,G=d;G>=1&&0===P[G];G--);if(H>G&&(H=G),0===G)return p[q++]=20971520,p[q++]=20971520,s.bits=1,0;for(F=1;G>F&&0===P[F];F++);for(F>H&&(H=F),K=1,D=1;d>=D;D++)if(K<<=1,K-=P[D],0>K)return-1;if(K>0&&(a===g||1!==G))return-1;for(Q[1]=0,D=1;d>D;D++)Q[D+1]=Q[D]+P[D];for(E=0;o>E;E++)0!==b[n+E]&&(r[Q[b[n+E]]++]=E);if(a===g?(N=R=r,y=19):a===h?(N=j,O-=257,R=k,S-=257,y=256):(N=l,R=m,y=-1),M=0,E=0,D=F,x=q,I=H,J=0,v=-1,L=1<<H,w=L-1,a===h&&L>e||a===i&&L>f)return 1;for(var T=0;;){T++,z=D-J,r[E]<y?(A=0,B=r[E]):r[E]>y?(A=R[S+r[E]],B=N[O+r[E]]):(A=96,B=0),t=1<<D-J,u=1<<I,F=u;do u-=t,p[x+(M>>J)+u]=z<<24|A<<16|B|0;while(0!==u);for(t=1<<D-1;M&t;)t>>=1;if(0!==t?(M&=t-1,M+=t):M=0,E++,0===--P[D]){if(D===G)break;D=b[n+r[E]]}if(D>H&&(M&w)!==v){for(0===J&&(J=H),x+=F,I=D-J,K=1<<I;G>I+J&&(K-=P[I+J],!(0>=K));)I++,K<<=1;if(L+=1<<I,a===h&&L>e||a===i&&L>f)return 1;v=M&w,p[v]=H<<24|I<<16|x-q|0}}return 0!==M&&(p[x+M]=D-J<<24|64<<16|0),s.bits=H,0}},{"../utils/common":27}],37:[function(a,b){"use strict";b.exports={2:"need dictionary",1:"stream end",0:"","-1":"file error","-2":"stream error","-3":"data error","-4":"insufficient memory","-5":"buffer error","-6":"incompatible version"}},{}],38:[function(a,b,c){"use strict";function d(a){for(var b=a.length;--b>=0;)a[b]=0}function e(a){return 256>a?gb[a]:gb[256+(a>>>7)]}function f(a,b){a.pending_buf[a.pending++]=255&b,a.pending_buf[a.pending++]=b>>>8&255}function g(a,b,c){a.bi_valid>V-c?(a.bi_buf|=b<<a.bi_valid&65535,f(a,a.bi_buf),a.bi_buf=b>>V-a.bi_valid,a.bi_valid+=c-V):(a.bi_buf|=b<<a.bi_valid&65535,a.bi_valid+=c)}function h(a,b,c){g(a,c[2*b],c[2*b+1])}function i(a,b){var c=0;do c|=1&a,a>>>=1,c<<=1;while(--b>0);return c>>>1}function j(a){16===a.bi_valid?(f(a,a.bi_buf),a.bi_buf=0,a.bi_valid=0):a.bi_valid>=8&&(a.pending_buf[a.pending++]=255&a.bi_buf,a.bi_buf>>=8,a.bi_valid-=8)}function k(a,b){var c,d,e,f,g,h,i=b.dyn_tree,j=b.max_code,k=b.stat_desc.static_tree,l=b.stat_desc.has_stree,m=b.stat_desc.extra_bits,n=b.stat_desc.extra_base,o=b.stat_desc.max_length,p=0;for(f=0;U>=f;f++)a.bl_count[f]=0;for(i[2*a.heap[a.heap_max]+1]=0,c=a.heap_max+1;T>c;c++)d=a.heap[c],f=i[2*i[2*d+1]+1]+1,f>o&&(f=o,p++),i[2*d+1]=f,d>j||(a.bl_count[f]++,g=0,d>=n&&(g=m[d-n]),h=i[2*d],a.opt_len+=h*(f+g),l&&(a.static_len+=h*(k[2*d+1]+g)));if(0!==p){do{for(f=o-1;0===a.bl_count[f];)f--;a.bl_count[f]--,a.bl_count[f+1]+=2,a.bl_count[o]--,p-=2}while(p>0);for(f=o;0!==f;f--)for(d=a.bl_count[f];0!==d;)e=a.heap[--c],e>j||(i[2*e+1]!==f&&(a.opt_len+=(f-i[2*e+1])*i[2*e],i[2*e+1]=f),d--)}}function l(a,b,c){var d,e,f=new Array(U+1),g=0;for(d=1;U>=d;d++)f[d]=g=g+c[d-1]<<1;for(e=0;b>=e;e++){var h=a[2*e+1];0!==h&&(a[2*e]=i(f[h]++,h))}}function m(){var a,b,c,d,e,f=new Array(U+1);for(c=0,d=0;O-1>d;d++)for(ib[d]=c,a=0;a<1<<_[d];a++)hb[c++]=d;for(hb[c-1]=d,e=0,d=0;16>d;d++)for(jb[d]=e,a=0;a<1<<ab[d];a++)gb[e++]=d;for(e>>=7;R>d;d++)for(jb[d]=e<<7,a=0;a<1<<ab[d]-7;a++)gb[256+e++]=d;for(b=0;U>=b;b++)f[b]=0;for(a=0;143>=a;)eb[2*a+1]=8,a++,f[8]++;for(;255>=a;)eb[2*a+1]=9,a++,f[9]++;for(;279>=a;)eb[2*a+1]=7,a++,f[7]++;for(;287>=a;)eb[2*a+1]=8,a++,f[8]++;for(l(eb,Q+1,f),a=0;R>a;a++)fb[2*a+1]=5,fb[2*a]=i(a,5);kb=new nb(eb,_,P+1,Q,U),lb=new nb(fb,ab,0,R,U),mb=new nb(new Array(0),bb,0,S,W)}function n(a){var b;for(b=0;Q>b;b++)a.dyn_ltree[2*b]=0;for(b=0;R>b;b++)a.dyn_dtree[2*b]=0;for(b=0;S>b;b++)a.bl_tree[2*b]=0;a.dyn_ltree[2*X]=1,a.opt_len=a.static_len=0,a.last_lit=a.matches=0}function o(a){a.bi_valid>8?f(a,a.bi_buf):a.bi_valid>0&&(a.pending_buf[a.pending++]=a.bi_buf),a.bi_buf=0,a.bi_valid=0}function p(a,b,c,d){o(a),d&&(f(a,c),f(a,~c)),E.arraySet(a.pending_buf,a.window,b,c,a.pending),a.pending+=c}function q(a,b,c,d){var e=2*b,f=2*c;return a[e]<a[f]||a[e]===a[f]&&d[b]<=d[c]}function r(a,b,c){for(var d=a.heap[c],e=c<<1;e<=a.heap_len&&(e<a.heap_len&&q(b,a.heap[e+1],a.heap[e],a.depth)&&e++,!q(b,d,a.heap[e],a.depth));)a.heap[c]=a.heap[e],c=e,e<<=1;a.heap[c]=d}function s(a,b,c){var d,f,i,j,k=0;if(0!==a.last_lit)do d=a.pending_buf[a.d_buf+2*k]<<8|a.pending_buf[a.d_buf+2*k+1],f=a.pending_buf[a.l_buf+k],k++,0===d?h(a,f,b):(i=hb[f],h(a,i+P+1,b),j=_[i],0!==j&&(f-=ib[i],g(a,f,j)),d--,i=e(d),h(a,i,c),j=ab[i],0!==j&&(d-=jb[i],g(a,d,j)));while(k<a.last_lit);h(a,X,b)}function t(a,b){var c,d,e,f=b.dyn_tree,g=b.stat_desc.static_tree,h=b.stat_desc.has_stree,i=b.stat_desc.elems,j=-1;for(a.heap_len=0,a.heap_max=T,c=0;i>c;c++)0!==f[2*c]?(a.heap[++a.heap_len]=j=c,a.depth[c]=0):f[2*c+1]=0;for(;a.heap_len<2;)e=a.heap[++a.heap_len]=2>j?++j:0,f[2*e]=1,a.depth[e]=0,a.opt_len--,h&&(a.static_len-=g[2*e+1]);for(b.max_code=j,c=a.heap_len>>1;c>=1;c--)r(a,f,c);e=i;do c=a.heap[1],a.heap[1]=a.heap[a.heap_len--],r(a,f,1),d=a.heap[1],a.heap[--a.heap_max]=c,a.heap[--a.heap_max]=d,f[2*e]=f[2*c]+f[2*d],a.depth[e]=(a.depth[c]>=a.depth[d]?a.depth[c]:a.depth[d])+1,f[2*c+1]=f[2*d+1]=e,a.heap[1]=e++,r(a,f,1);while(a.heap_len>=2);a.heap[--a.heap_max]=a.heap[1],k(a,b),l(f,j,a.bl_count)}function u(a,b,c){var d,e,f=-1,g=b[1],h=0,i=7,j=4;for(0===g&&(i=138,j=3),b[2*(c+1)+1]=65535,d=0;c>=d;d++)e=g,g=b[2*(d+1)+1],++h<i&&e===g||(j>h?a.bl_tree[2*e]+=h:0!==e?(e!==f&&a.bl_tree[2*e]++,a.bl_tree[2*Y]++):10>=h?a.bl_tree[2*Z]++:a.bl_tree[2*$]++,h=0,f=e,0===g?(i=138,j=3):e===g?(i=6,j=3):(i=7,j=4))}function v(a,b,c){var d,e,f=-1,i=b[1],j=0,k=7,l=4;for(0===i&&(k=138,l=3),d=0;c>=d;d++)if(e=i,i=b[2*(d+1)+1],!(++j<k&&e===i)){if(l>j){do h(a,e,a.bl_tree);while(0!==--j)}else 0!==e?(e!==f&&(h(a,e,a.bl_tree),j--),h(a,Y,a.bl_tree),g(a,j-3,2)):10>=j?(h(a,Z,a.bl_tree),g(a,j-3,3)):(h(a,$,a.bl_tree),g(a,j-11,7));j=0,f=e,0===i?(k=138,l=3):e===i?(k=6,l=3):(k=7,l=4)}}function w(a){var b;for(u(a,a.dyn_ltree,a.l_desc.max_code),u(a,a.dyn_dtree,a.d_desc.max_code),t(a,a.bl_desc),b=S-1;b>=3&&0===a.bl_tree[2*cb[b]+1];b--);return a.opt_len+=3*(b+1)+5+5+4,b}function x(a,b,c,d){var e;for(g(a,b-257,5),g(a,c-1,5),g(a,d-4,4),e=0;d>e;e++)g(a,a.bl_tree[2*cb[e]+1],3);v(a,a.dyn_ltree,b-1),v(a,a.dyn_dtree,c-1)}function y(a){var b,c=4093624447;for(b=0;31>=b;b++,c>>>=1)if(1&c&&0!==a.dyn_ltree[2*b])return G;if(0!==a.dyn_ltree[18]||0!==a.dyn_ltree[20]||0!==a.dyn_ltree[26])return H;for(b=32;P>b;b++)if(0!==a.dyn_ltree[2*b])return H;return G}function z(a){pb||(m(),pb=!0),a.l_desc=new ob(a.dyn_ltree,kb),a.d_desc=new ob(a.dyn_dtree,lb),a.bl_desc=new ob(a.bl_tree,mb),a.bi_buf=0,a.bi_valid=0,n(a)}function A(a,b,c,d){g(a,(J<<1)+(d?1:0),3),p(a,b,c,!0)}function B(a){g(a,K<<1,3),h(a,X,eb),j(a)}function C(a,b,c,d){var e,f,h=0;a.level>0?(a.strm.data_type===I&&(a.strm.data_type=y(a)),t(a,a.l_desc),t(a,a.d_desc),h=w(a),e=a.opt_len+3+7>>>3,f=a.static_len+3+7>>>3,e>=f&&(e=f)):e=f=c+5,e>=c+4&&-1!==b?A(a,b,c,d):a.strategy===F||f===e?(g(a,(K<<1)+(d?1:0),3),s(a,eb,fb)):(g(a,(L<<1)+(d?1:0),3),x(a,a.l_desc.max_code+1,a.d_desc.max_code+1,h+1),s(a,a.dyn_ltree,a.dyn_dtree)),n(a),d&&o(a)}function D(a,b,c){return a.pending_buf[a.d_buf+2*a.last_lit]=b>>>8&255,a.pending_buf[a.d_buf+2*a.last_lit+1]=255&b,a.pending_buf[a.l_buf+a.last_lit]=255&c,a.last_lit++,0===b?a.dyn_ltree[2*c]++:(a.matches++,b--,a.dyn_ltree[2*(hb[c]+P+1)]++,a.dyn_dtree[2*e(b)]++),a.last_lit===a.lit_bufsize-1}var E=a("../utils/common"),F=4,G=0,H=1,I=2,J=0,K=1,L=2,M=3,N=258,O=29,P=256,Q=P+1+O,R=30,S=19,T=2*Q+1,U=15,V=16,W=7,X=256,Y=16,Z=17,$=18,_=[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0],ab=[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13],bb=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7],cb=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],db=512,eb=new Array(2*(Q+2));d(eb);var fb=new Array(2*R);d(fb);var gb=new Array(db);d(gb);var hb=new Array(N-M+1);d(hb);var ib=new Array(O);d(ib);var jb=new Array(R);d(jb);var kb,lb,mb,nb=function(a,b,c,d,e){this.static_tree=a,this.extra_bits=b,this.extra_base=c,this.elems=d,this.max_length=e,this.has_stree=a&&a.length},ob=function(a,b){this.dyn_tree=a,this.max_code=0,this.stat_desc=b},pb=!1;c._tr_init=z,c._tr_stored_block=A,c._tr_flush_block=C,c._tr_tally=D,c._tr_align=B},{"../utils/common":27}],39:[function(a,b){"use strict";function c(){this.input=null,this.next_in=0,this.avail_in=0,this.total_in=0,this.output=null,this.next_out=0,this.avail_out=0,this.total_out=0,this.msg="",this.state=null,this.data_type=2,this.adler=0}b.exports=c},{}]},{},[9])(9)});'use strict';if(tr.isVinn){global.window={};}'use strict';if(tr.isVinn){global.JSZip=global.window.JSZip;global.window=undefined;}else if(tr.isNode){var jsZipAbsPath=HTMLImportsLoader.hrefToAbsolutePath('/jszip.min.js');var jsZipModule=require(jsZipAbsPath);global.JSZip=jsZipModule;}'use strict';tr.exportTo('tr.e.importer',function(){var GZIP_MEMBER_HEADER_ID_SIZE=3;var GZIP_HEADER_ID1=0x1f;var GZIP_HEADER_ID2=0x8b;var GZIP_DEFLATE_COMPRESSION=8;function GzipImporter(model,eventData){if(typeof(eventData)==='string'||eventData instanceof String){eventData=JSZip.utils.transformTo('uint8array',eventData);}else if(eventData instanceof ArrayBuffer){eventData=new Uint8Array(eventData);}else
-throw new Error('Unknown gzip data format');this.model_=model;this.gzipData_=eventData;}
-GzipImporter.canImport=function(eventData){var header;if(eventData instanceof ArrayBuffer)
-header=new Uint8Array(eventData.slice(0,GZIP_MEMBER_HEADER_ID_SIZE));else if(typeof(eventData)==='string'||eventData instanceof String){header=eventData.substring(0,GZIP_MEMBER_HEADER_ID_SIZE);header=JSZip.utils.transformTo('uint8array',header);}else
-return false;return header[0]==GZIP_HEADER_ID1&&header[1]==GZIP_HEADER_ID2&&header[2]==GZIP_DEFLATE_COMPRESSION;};GzipImporter.inflateGzipData_=function(data){var position=0;function getByte(){if(position>=data.length)
-throw new Error('Unexpected end of gzip data');return data[position++];}
+var timestamp=this.importTimestamp(event.ts);var header={guid:event.guid,opcode:event.op,version:event.ver,cpu:event.cpu,timestamp:timestamp,is64:this.is64bit_};var decoder=this.decoder_;decoder.reset(event.payload);var handler=this.getEventHandler(header.guid,header.opcode);if(!handler)return false;if(!handler(header,decoder)){this.model_.importWarning({type:'parse_error',message:'Malformed '+header.guid+' event ('+event.payload+')'});return false;}
+return true;},registerEventHandler:function(guid,opcode,handler){if(this.handlers_[guid]===undefined){this.handlers_[guid]=[];}
+this.handlers_[guid][opcode]=handler;},getEventHandler:function(guid,opcode){if(this.handlers_[guid]===undefined){return undefined;}
+return this.handlers_[guid][opcode];}};tr.importer.Importer.register(EtwImporter);return{EtwImporter,};});!function(a){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=a();else if("function"==typeof define&&define.amd)define([],a);else{var b;"undefined"!=typeof window?b=window:"undefined"!=typeof global?b=global:"undefined"!=typeof self&&(b=self),b.JSZip=a()}}(function(){return function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);throw new Error("Cannot find module '"+g+"'")}var j=c[g]={exports:{}};b[g][0].call(j.exports,function(a){var c=b[g][1][a];return e(c?c:a)},j,j.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g<d.length;g++)e(d[g]);return e}({1:[function(a,b,c){"use strict";var d="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";c.encode=function(a){for(var b,c,e,f,g,h,i,j="",k=0;k<a.length;)b=a.charCodeAt(k++),c=a.charCodeAt(k++),e=a.charCodeAt(k++),f=b>>2,g=(3&b)<<4|c>>4,h=(15&c)<<2|e>>6,i=63&e,isNaN(c)?h=i=64:isNaN(e)&&(i=64),j=j+d.charAt(f)+d.charAt(g)+d.charAt(h)+d.charAt(i);return j},c.decode=function(a){var b,c,e,f,g,h,i,j="",k=0;for(a=a.replace(/[^A-Za-z0-9\+\/\=]/g,"");k<a.length;)f=d.indexOf(a.charAt(k++)),g=d.indexOf(a.charAt(k++)),h=d.indexOf(a.charAt(k++)),i=d.indexOf(a.charAt(k++)),b=f<<2|g>>4,c=(15&g)<<4|h>>2,e=(3&h)<<6|i,j+=String.fromCharCode(b),64!=h&&(j+=String.fromCharCode(c)),64!=i&&(j+=String.fromCharCode(e));return j}},{}],2:[function(a,b){"use strict";function c(){this.compressedSize=0,this.uncompressedSize=0,this.crc32=0,this.compressionMethod=null,this.compressedContent=null}c.prototype={getContent:function(){return null},getCompressedContent:function(){return null}},b.exports=c},{}],3:[function(a,b,c){"use strict";c.STORE={magic:"\x00\x00",compress:function(a){return a},uncompress:function(a){return a},compressInputType:null,uncompressInputType:null},c.DEFLATE=a("./flate")},{"./flate":8}],4:[function(a,b){"use strict";var c=a("./utils"),d=[0,1996959894,3993919788,2567524794,124634137,1886057615,3915621685,2657392035,249268274,2044508324,3772115230,2547177864,162941995,2125561021,3887607047,2428444049,498536548,1789927666,4089016648,2227061214,450548861,1843258603,4107580753,2211677639,325883990,1684777152,4251122042,2321926636,335633487,1661365465,4195302755,2366115317,997073096,1281953886,3579855332,2724688242,1006888145,1258607687,3524101629,2768942443,901097722,1119000684,3686517206,2898065728,853044451,1172266101,3705015759,2882616665,651767980,1373503546,3369554304,3218104598,565507253,1454621731,3485111705,3099436303,671266974,1594198024,3322730930,2970347812,795835527,1483230225,3244367275,3060149565,1994146192,31158534,2563907772,4023717930,1907459465,112637215,2680153253,3904427059,2013776290,251722036,2517215374,3775830040,2137656763,141376813,2439277719,3865271297,1802195444,476864866,2238001368,4066508878,1812370925,453092731,2181625025,4111451223,1706088902,314042704,2344532202,4240017532,1658658271,366619977,2362670323,4224994405,1303535960,984961486,2747007092,3569037538,1256170817,1037604311,2765210733,3554079995,1131014506,879679996,2909243462,3663771856,1141124467,855842277,2852801631,3708648649,1342533948,654459306,3188396048,3373015174,1466479909,544179635,3110523913,3462522015,1591671054,702138776,2966460450,3352799412,1504918807,783551873,3082640443,3233442989,3988292384,2596254646,62317068,1957810842,3939845945,2647816111,81470997,1943803523,3814918930,2489596804,225274430,2053790376,3826175755,2466906013,167816743,2097651377,4027552580,2265490386,503444072,1762050814,4150417245,2154129355,426522225,1852507879,4275313526,2312317920,282753626,1742555852,4189708143,2394877945,397917763,1622183637,3604390888,2714866558,953729732,1340076626,3518719985,2797360999,1068828381,1219638859,3624741850,2936675148,906185462,1090812512,3747672003,2825379669,829329135,1181335161,3412177804,3160834842,628085408,1382605366,3423369109,3138078467,570562233,1426400815,3317316542,2998733608,733239954,1555261956,3268935591,3050360625,752459403,1541320221,2607071920,3965973030,1969922972,40735498,2617837225,3943577151,1913087877,83908371,2512341634,3803740692,2075208622,213261112,2463272603,3855990285,2094854071,198958881,2262029012,4057260610,1759359992,534414190,2176718541,4139329115,1873836001,414664567,2282248934,4279200368,1711684554,285281116,2405801727,4167216745,1634467795,376229701,2685067896,3608007406,1308918612,956543938,2808555105,3495958263,1231636301,1047427035,2932959818,3654703836,1088359270,936918e3,2847714899,3736837829,1202900863,817233897,3183342108,3401237130,1404277552,615818150,3134207493,3453421203,1423857449,601450431,3009837614,3294710456,1567103746,711928724,3020668471,3272380065,1510334235,755167117];b.exports=function(a,b){if("undefined"==typeof a||!a.length)return 0;var e="string"!==c.getTypeOf(a);"undefined"==typeof b&&(b=0);var f=0,g=0,h=0;b=-1^b;for(var i=0,j=a.length;j>i;i++)h=e?a[i]:a.charCodeAt(i),g=255&(b^h),f=d[g],b=b>>>8^f;return-1^b}},{"./utils":21}],5:[function(a,b){"use strict";function c(){this.data=null,this.length=0,this.index=0}var d=a("./utils");c.prototype={checkOffset:function(a){this.checkIndex(this.index+a)},checkIndex:function(a){if(this.length<a||0>a)throw new Error("End of data reached (data length = "+this.length+", asked index = "+a+"). Corrupted zip ?")},setIndex:function(a){this.checkIndex(a),this.index=a},skip:function(a){this.setIndex(this.index+a)},byteAt:function(){},readInt:function(a){var b,c=0;for(this.checkOffset(a),b=this.index+a-1;b>=this.index;b--)c=(c<<8)+this.byteAt(b);return this.index+=a,c},readString:function(a){return d.transformTo("string",this.readData(a))},readData:function(){},lastIndexOfSignature:function(){},readDate:function(){var a=this.readInt(4);return new Date((a>>25&127)+1980,(a>>21&15)-1,a>>16&31,a>>11&31,a>>5&63,(31&a)<<1)}},b.exports=c},{"./utils":21}],6:[function(a,b,c){"use strict";c.base64=!1,c.binary=!1,c.dir=!1,c.createFolders=!1,c.date=null,c.compression=null,c.comment=null},{}],7:[function(a,b,c){"use strict";var d=a("./utils");c.string2binary=function(a){return d.string2binary(a)},c.string2Uint8Array=function(a){return d.transformTo("uint8array",a)},c.uint8Array2String=function(a){return d.transformTo("string",a)},c.string2Blob=function(a){var b=d.transformTo("arraybuffer",a);return d.arrayBuffer2Blob(b)},c.arrayBuffer2Blob=function(a){return d.arrayBuffer2Blob(a)},c.transformTo=function(a,b){return d.transformTo(a,b)},c.getTypeOf=function(a){return d.getTypeOf(a)},c.checkSupport=function(a){return d.checkSupport(a)},c.MAX_VALUE_16BITS=d.MAX_VALUE_16BITS,c.MAX_VALUE_32BITS=d.MAX_VALUE_32BITS,c.pretty=function(a){return d.pretty(a)},c.findCompression=function(a){return d.findCompression(a)},c.isRegExp=function(a){return d.isRegExp(a)}},{"./utils":21}],8:[function(a,b,c){"use strict";var d="undefined"!=typeof Uint8Array&&"undefined"!=typeof Uint16Array&&"undefined"!=typeof Uint32Array,e=a("pako");c.uncompressInputType=d?"uint8array":"array",c.compressInputType=d?"uint8array":"array",c.magic="\b\x00",c.compress=function(a){return e.deflateRaw(a)},c.uncompress=function(a){return e.inflateRaw(a)}},{pako:24}],9:[function(a,b){"use strict";function c(a,b){return this instanceof c?(this.files={},this.comment=null,this.root="",a&&this.load(a,b),void(this.clone=function(){var a=new c;for(var b in this)"function"!=typeof this[b]&&(a[b]=this[b]);return a})):new c(a,b)}var d=a("./base64");c.prototype=a("./object"),c.prototype.load=a("./load"),c.support=a("./support"),c.defaults=a("./defaults"),c.utils=a("./deprecatedPublicUtils"),c.base64={encode:function(a){return d.encode(a)},decode:function(a){return d.decode(a)}},c.compressions=a("./compressions"),b.exports=c},{"./base64":1,"./compressions":3,"./defaults":6,"./deprecatedPublicUtils":7,"./load":10,"./object":13,"./support":17}],10:[function(a,b){"use strict";var c=a("./base64"),d=a("./zipEntries");b.exports=function(a,b){var e,f,g,h;for(b=b||{},b.base64&&(a=c.decode(a)),f=new d(a,b),e=f.files,g=0;g<e.length;g++)h=e[g],this.file(h.fileName,h.decompressed,{binary:!0,optimizedBinaryString:!0,date:h.date,dir:h.dir,comment:h.fileComment.length?h.fileComment:null,createFolders:b.createFolders});return f.zipComment.length&&(this.comment=f.zipComment),this}},{"./base64":1,"./zipEntries":22}],11:[function(a,b){(function(a){"use strict";b.exports=function(b,c){return new a(b,c)},b.exports.test=function(b){return a.isBuffer(b)}}).call(this,"undefined"!=typeof Buffer?Buffer:void 0)},{}],12:[function(a,b){"use strict";function c(a){this.data=a,this.length=this.data.length,this.index=0}var d=a("./uint8ArrayReader");c.prototype=new d,c.prototype.readData=function(a){this.checkOffset(a);var b=this.data.slice(this.index,this.index+a);return this.index+=a,b},b.exports=c},{"./uint8ArrayReader":18}],13:[function(a,b){"use strict";var c=a("./support"),d=a("./utils"),e=a("./crc32"),f=a("./signature"),g=a("./defaults"),h=a("./base64"),i=a("./compressions"),j=a("./compressedObject"),k=a("./nodeBuffer"),l=a("./utf8"),m=a("./stringWriter"),n=a("./uint8ArrayWriter"),o=function(a){if(a._data instanceof j&&(a._data=a._data.getContent(),a.options.binary=!0,a.options.base64=!1,"uint8array"===d.getTypeOf(a._data))){var b=a._data;a._data=new Uint8Array(b.length),0!==b.length&&a._data.set(b,0)}return a._data},p=function(a){var b=o(a),e=d.getTypeOf(b);return"string"===e?!a.options.binary&&c.nodebuffer?k(b,"utf-8"):a.asBinary():b},q=function(a){var b=o(this);return null===b||"undefined"==typeof b?"":(this.options.base64&&(b=h.decode(b)),b=a&&this.options.binary?A.utf8decode(b):d.transformTo("string",b),a||this.options.binary||(b=d.transformTo("string",A.utf8encode(b))),b)},r=function(a,b,c){this.name=a,this.dir=c.dir,this.date=c.date,this.comment=c.comment,this._data=b,this.options=c,this._initialMetadata={dir:c.dir,date:c.date}};r.prototype={asText:function(){return q.call(this,!0)},asBinary:function(){return q.call(this,!1)},asNodeBuffer:function(){var a=p(this);return d.transformTo("nodebuffer",a)},asUint8Array:function(){var a=p(this);return d.transformTo("uint8array",a)},asArrayBuffer:function(){return this.asUint8Array().buffer}};var s=function(a,b){var c,d="";for(c=0;b>c;c++)d+=String.fromCharCode(255&a),a>>>=8;return d},t=function(){var a,b,c={};for(a=0;a<arguments.length;a++)for(b in arguments[a])arguments[a].hasOwnProperty(b)&&"undefined"==typeof c[b]&&(c[b]=arguments[a][b]);return c},u=function(a){return a=a||{},a.base64!==!0||null!==a.binary&&void 0!==a.binary||(a.binary=!0),a=t(a,g),a.date=a.date||new Date,null!==a.compression&&(a.compression=a.compression.toUpperCase()),a},v=function(a,b,c){var e,f=d.getTypeOf(b);if(c=u(c),c.createFolders&&(e=w(a))&&x.call(this,e,!0),c.dir||null===b||"undefined"==typeof b)c.base64=!1,c.binary=!1,b=null;else if("string"===f)c.binary&&!c.base64&&c.optimizedBinaryString!==!0&&(b=d.string2binary(b));else{if(c.base64=!1,c.binary=!0,!(f||b instanceof j))throw new Error("The data of '"+a+"' is in an unsupported format !");"arraybuffer"===f&&(b=d.transformTo("uint8array",b))}var g=new r(a,b,c);return this.files[a]=g,g},w=function(a){"/"==a.slice(-1)&&(a=a.substring(0,a.length-1));var b=a.lastIndexOf("/");return b>0?a.substring(0,b):""},x=function(a,b){return"/"!=a.slice(-1)&&(a+="/"),b="undefined"!=typeof b?b:!1,this.files[a]||v.call(this,a,null,{dir:!0,createFolders:b}),this.files[a]},y=function(a,b){var c,f=new j;return a._data instanceof j?(f.uncompressedSize=a._data.uncompressedSize,f.crc32=a._data.crc32,0===f.uncompressedSize||a.dir?(b=i.STORE,f.compressedContent="",f.crc32=0):a._data.compressionMethod===b.magic?f.compressedContent=a._data.getCompressedContent():(c=a._data.getContent(),f.compressedContent=b.compress(d.transformTo(b.compressInputType,c)))):(c=p(a),(!c||0===c.length||a.dir)&&(b=i.STORE,c=""),f.uncompressedSize=c.length,f.crc32=e(c),f.compressedContent=b.compress(d.transformTo(b.compressInputType,c))),f.compressedSize=f.compressedContent.length,f.compressionMethod=b.magic,f},z=function(a,b,c,g){var h,i,j,k,m=(c.compressedContent,d.transformTo("string",l.utf8encode(b.name))),n=b.comment||"",o=d.transformTo("string",l.utf8encode(n)),p=m.length!==b.name.length,q=o.length!==n.length,r=b.options,t="",u="",v="";j=b._initialMetadata.dir!==b.dir?b.dir:r.dir,k=b._initialMetadata.date!==b.date?b.date:r.date,h=k.getHours(),h<<=6,h|=k.getMinutes(),h<<=5,h|=k.getSeconds()/2,i=k.getFullYear()-1980,i<<=4,i|=k.getMonth()+1,i<<=5,i|=k.getDate(),p&&(u=s(1,1)+s(e(m),4)+m,t+="up"+s(u.length,2)+u),q&&(v=s(1,1)+s(this.crc32(o),4)+o,t+="uc"+s(v.length,2)+v);var w="";w+="\n\x00",w+=p||q?"\x00\b":"\x00\x00",w+=c.compressionMethod,w+=s(h,2),w+=s(i,2),w+=s(c.crc32,4),w+=s(c.compressedSize,4),w+=s(c.uncompressedSize,4),w+=s(m.length,2),w+=s(t.length,2);var x=f.LOCAL_FILE_HEADER+w+m+t,y=f.CENTRAL_FILE_HEADER+"\x00"+w+s(o.length,2)+"\x00\x00\x00\x00"+(j===!0?"\x00\x00\x00":"\x00\x00\x00\x00")+s(g,4)+m+t+o;return{fileRecord:x,dirRecord:y,compressedObject:c}},A={load:function(){throw new Error("Load method is not defined. Is the file jszip-load.js included ?")},filter:function(a){var b,c,d,e,f=[];for(b in this.files)this.files.hasOwnProperty(b)&&(d=this.files[b],e=new r(d.name,d._data,t(d.options)),c=b.slice(this.root.length,b.length),b.slice(0,this.root.length)===this.root&&a(c,e)&&f.push(e));return f},file:function(a,b,c){if(1===arguments.length){if(d.isRegExp(a)){var e=a;return this.filter(function(a,b){return!b.dir&&e.test(a)})}return this.filter(function(b,c){return!c.dir&&b===a})[0]||null}return a=this.root+a,v.call(this,a,b,c),this},folder:function(a){if(!a)return this;if(d.isRegExp(a))return this.filter(function(b,c){return c.dir&&a.test(b)});var b=this.root+a,c=x.call(this,b),e=this.clone();return e.root=c.name,e},remove:function(a){a=this.root+a;var b=this.files[a];if(b||("/"!=a.slice(-1)&&(a+="/"),b=this.files[a]),b&&!b.dir)delete this.files[a];else for(var c=this.filter(function(b,c){return c.name.slice(0,a.length)===a}),d=0;d<c.length;d++)delete this.files[c[d].name];return this},generate:function(a){a=t(a||{},{base64:!0,compression:"STORE",type:"base64",comment:null}),d.checkSupport(a.type);var b,c,e=[],g=0,j=0,k=d.transformTo("string",this.utf8encode(a.comment||this.comment||""));for(var l in this.files)if(this.files.hasOwnProperty(l)){var o=this.files[l],p=o.options.compression||a.compression.toUpperCase(),q=i[p];if(!q)throw new Error(p+" is not a valid compression method !");var r=y.call(this,o,q),u=z.call(this,l,o,r,g);g+=u.fileRecord.length+r.compressedSize,j+=u.dirRecord.length,e.push(u)}var v="";v=f.CENTRAL_DIRECTORY_END+"\x00\x00\x00\x00"+s(e.length,2)+s(e.length,2)+s(j,4)+s(g,4)+s(k.length,2)+k;var w=a.type.toLowerCase();for(b="uint8array"===w||"arraybuffer"===w||"blob"===w||"nodebuffer"===w?new n(g+j+v.length):new m(g+j+v.length),c=0;c<e.length;c++)b.append(e[c].fileRecord),b.append(e[c].compressedObject.compressedContent);for(c=0;c<e.length;c++)b.append(e[c].dirRecord);b.append(v);var x=b.finalize();switch(a.type.toLowerCase()){case"uint8array":case"arraybuffer":case"nodebuffer":return d.transformTo(a.type.toLowerCase(),x);case"blob":return d.arrayBuffer2Blob(d.transformTo("arraybuffer",x));case"base64":return a.base64?h.encode(x):x;default:return x}},crc32:function(a,b){return e(a,b)},utf8encode:function(a){return d.transformTo("string",l.utf8encode(a))},utf8decode:function(a){return l.utf8decode(a)}};b.exports=A},{"./base64":1,"./compressedObject":2,"./compressions":3,"./crc32":4,"./defaults":6,"./nodeBuffer":11,"./signature":14,"./stringWriter":16,"./support":17,"./uint8ArrayWriter":19,"./utf8":20,"./utils":21}],14:[function(a,b,c){"use strict";c.LOCAL_FILE_HEADER="PK",c.CENTRAL_FILE_HEADER="PK",c.CENTRAL_DIRECTORY_END="PK",c.ZIP64_CENTRAL_DIRECTORY_LOCATOR="PK",c.ZIP64_CENTRAL_DIRECTORY_END="PK",c.DATA_DESCRIPTOR="PK\b"},{}],15:[function(a,b){"use strict";function c(a,b){this.data=a,b||(this.data=e.string2binary(this.data)),this.length=this.data.length,this.index=0}var d=a("./dataReader"),e=a("./utils");c.prototype=new d,c.prototype.byteAt=function(a){return this.data.charCodeAt(a)},c.prototype.lastIndexOfSignature=function(a){return this.data.lastIndexOf(a)},c.prototype.readData=function(a){this.checkOffset(a);var b=this.data.slice(this.index,this.index+a);return this.index+=a,b},b.exports=c},{"./dataReader":5,"./utils":21}],16:[function(a,b){"use strict";var c=a("./utils"),d=function(){this.data=[]};d.prototype={append:function(a){a=c.transformTo("string",a),this.data.push(a)},finalize:function(){return this.data.join("")}},b.exports=d},{"./utils":21}],17:[function(a,b,c){(function(a){"use strict";if(c.base64=!0,c.array=!0,c.string=!0,c.arraybuffer="undefined"!=typeof ArrayBuffer&&"undefined"!=typeof Uint8Array,c.nodebuffer="undefined"!=typeof a,c.uint8array="undefined"!=typeof Uint8Array,"undefined"==typeof ArrayBuffer)c.blob=!1;else{var b=new ArrayBuffer(0);try{c.blob=0===new Blob([b],{type:"application/zip"}).size}catch(d){try{var e=window.BlobBuilder||window.WebKitBlobBuilder||window.MozBlobBuilder||window.MSBlobBuilder,f=new e;f.append(b),c.blob=0===f.getBlob("application/zip").size}catch(d){c.blob=!1}}}}).call(this,"undefined"!=typeof Buffer?Buffer:void 0)},{}],18:[function(a,b){"use strict";function c(a){a&&(this.data=a,this.length=this.data.length,this.index=0)}var d=a("./dataReader");c.prototype=new d,c.prototype.byteAt=function(a){return this.data[a]},c.prototype.lastIndexOfSignature=function(a){for(var b=a.charCodeAt(0),c=a.charCodeAt(1),d=a.charCodeAt(2),e=a.charCodeAt(3),f=this.length-4;f>=0;--f)if(this.data[f]===b&&this.data[f+1]===c&&this.data[f+2]===d&&this.data[f+3]===e)return f;return-1},c.prototype.readData=function(a){if(this.checkOffset(a),0===a)return new Uint8Array(0);var b=this.data.subarray(this.index,this.index+a);return this.index+=a,b},b.exports=c},{"./dataReader":5}],19:[function(a,b){"use strict";var c=a("./utils"),d=function(a){this.data=new Uint8Array(a),this.index=0};d.prototype={append:function(a){0!==a.length&&(a=c.transformTo("uint8array",a),this.data.set(a,this.index),this.index+=a.length)},finalize:function(){return this.data}},b.exports=d},{"./utils":21}],20:[function(a,b,c){"use strict";for(var d=a("./utils"),e=a("./support"),f=a("./nodeBuffer"),g=new Array(256),h=0;256>h;h++)g[h]=h>=252?6:h>=248?5:h>=240?4:h>=224?3:h>=192?2:1;g[254]=g[254]=1;var i=function(a){var b,c,d,f,g,h=a.length,i=0;for(f=0;h>f;f++)c=a.charCodeAt(f),55296===(64512&c)&&h>f+1&&(d=a.charCodeAt(f+1),56320===(64512&d)&&(c=65536+(c-55296<<10)+(d-56320),f++)),i+=128>c?1:2048>c?2:65536>c?3:4;for(b=e.uint8array?new Uint8Array(i):new Array(i),g=0,f=0;i>g;f++)c=a.charCodeAt(f),55296===(64512&c)&&h>f+1&&(d=a.charCodeAt(f+1),56320===(64512&d)&&(c=65536+(c-55296<<10)+(d-56320),f++)),128>c?b[g++]=c:2048>c?(b[g++]=192|c>>>6,b[g++]=128|63&c):65536>c?(b[g++]=224|c>>>12,b[g++]=128|c>>>6&63,b[g++]=128|63&c):(b[g++]=240|c>>>18,b[g++]=128|c>>>12&63,b[g++]=128|c>>>6&63,b[g++]=128|63&c);return b},j=function(a,b){var c;for(b=b||a.length,b>a.length&&(b=a.length),c=b-1;c>=0&&128===(192&a[c]);)c--;return 0>c?b:0===c?b:c+g[a[c]]>b?c:b},k=function(a){var b,c,e,f,h=a.length,i=new Array(2*h);for(c=0,b=0;h>b;)if(e=a[b++],128>e)i[c++]=e;else if(f=g[e],f>4)i[c++]=65533,b+=f-1;else{for(e&=2===f?31:3===f?15:7;f>1&&h>b;)e=e<<6|63&a[b++],f--;f>1?i[c++]=65533:65536>e?i[c++]=e:(e-=65536,i[c++]=55296|e>>10&1023,i[c++]=56320|1023&e)}return i.length!==c&&(i.subarray?i=i.subarray(0,c):i.length=c),d.applyFromCharCode(i)};c.utf8encode=function(a){return e.nodebuffer?f(a,"utf-8"):i(a)},c.utf8decode=function(a){if(e.nodebuffer)return d.transformTo("nodebuffer",a).toString("utf-8");a=d.transformTo(e.uint8array?"uint8array":"array",a);for(var b=[],c=0,f=a.length,g=65536;f>c;){var h=j(a,Math.min(c+g,f));b.push(e.uint8array?k(a.subarray(c,h)):k(a.slice(c,h))),c=h}return b.join("")}},{"./nodeBuffer":11,"./support":17,"./utils":21}],21:[function(a,b,c){"use strict";function d(a){return a}function e(a,b){for(var c=0;c<a.length;++c)b[c]=255&a.charCodeAt(c);return b}function f(a){var b=65536,d=[],e=a.length,f=c.getTypeOf(a),g=0,h=!0;try{switch(f){case"uint8array":String.fromCharCode.apply(null,new Uint8Array(0));break;case"nodebuffer":String.fromCharCode.apply(null,j(0))}}catch(i){h=!1}if(!h){for(var k="",l=0;l<a.length;l++)k+=String.fromCharCode(a[l]);return k}for(;e>g&&b>1;)try{d.push("array"===f||"nodebuffer"===f?String.fromCharCode.apply(null,a.slice(g,Math.min(g+b,e))):String.fromCharCode.apply(null,a.subarray(g,Math.min(g+b,e)))),g+=b}catch(i){b=Math.floor(b/2)}return d.join("")}function g(a,b){for(var c=0;c<a.length;c++)b[c]=a[c];return b}var h=a("./support"),i=a("./compressions"),j=a("./nodeBuffer");c.string2binary=function(a){for(var b="",c=0;c<a.length;c++)b+=String.fromCharCode(255&a.charCodeAt(c));return b},c.arrayBuffer2Blob=function(a){c.checkSupport("blob");try{return new Blob([a],{type:"application/zip"})}catch(b){try{var d=window.BlobBuilder||window.WebKitBlobBuilder||window.MozBlobBuilder||window.MSBlobBuilder,e=new d;return e.append(a),e.getBlob("application/zip")}catch(b){throw new Error("Bug : can't construct the Blob.")}}},c.applyFromCharCode=f;var k={};k.string={string:d,array:function(a){return e(a,new Array(a.length))},arraybuffer:function(a){return k.string.uint8array(a).buffer},uint8array:function(a){return e(a,new Uint8Array(a.length))},nodebuffer:function(a){return e(a,j(a.length))}},k.array={string:f,array:d,arraybuffer:function(a){return new Uint8Array(a).buffer},uint8array:function(a){return new Uint8Array(a)},nodebuffer:function(a){return j(a)}},k.arraybuffer={string:function(a){return f(new Uint8Array(a))},array:function(a){return g(new Uint8Array(a),new Array(a.byteLength))},arraybuffer:d,uint8array:function(a){return new Uint8Array(a)},nodebuffer:function(a){return j(new Uint8Array(a))}},k.uint8array={string:f,array:function(a){return g(a,new Array(a.length))},arraybuffer:function(a){return a.buffer},uint8array:d,nodebuffer:function(a){return j(a)}},k.nodebuffer={string:f,array:function(a){return g(a,new Array(a.length))},arraybuffer:function(a){return k.nodebuffer.uint8array(a).buffer},uint8array:function(a){return g(a,new Uint8Array(a.length))},nodebuffer:d},c.transformTo=function(a,b){if(b||(b=""),!a)return b;c.checkSupport(a);var d=c.getTypeOf(b),e=k[d][a](b);return e},c.getTypeOf=function(a){return"string"==typeof a?"string":"[object Array]"===Object.prototype.toString.call(a)?"array":h.nodebuffer&&j.test(a)?"nodebuffer":h.uint8array&&a instanceof Uint8Array?"uint8array":h.arraybuffer&&a instanceof ArrayBuffer?"arraybuffer":void 0},c.checkSupport=function(a){var b=h[a.toLowerCase()];if(!b)throw new Error(a+" is not supported by this browser")},c.MAX_VALUE_16BITS=65535,c.MAX_VALUE_32BITS=-1,c.pretty=function(a){var b,c,d="";for(c=0;c<(a||"").length;c++)b=a.charCodeAt(c),d+="\\x"+(16>b?"0":"")+b.toString(16).toUpperCase();return d},c.findCompression=function(a){for(var b in i)if(i.hasOwnProperty(b)&&i[b].magic===a)return i[b];return null},c.isRegExp=function(a){return"[object RegExp]"===Object.prototype.toString.call(a)}},{"./compressions":3,"./nodeBuffer":11,"./support":17}],22:[function(a,b){"use strict";function c(a,b){this.files=[],this.loadOptions=b,a&&this.load(a)}var d=a("./stringReader"),e=a("./nodeBufferReader"),f=a("./uint8ArrayReader"),g=a("./utils"),h=a("./signature"),i=a("./zipEntry"),j=a("./support"),k=a("./object");c.prototype={checkSignature:function(a){var b=this.reader.readString(4);if(b!==a)throw new Error("Corrupted zip or bug : unexpected signature ("+g.pretty(b)+", expected "+g.pretty(a)+")")},readBlockEndOfCentral:function(){this.diskNumber=this.reader.readInt(2),this.diskWithCentralDirStart=this.reader.readInt(2),this.centralDirRecordsOnThisDisk=this.reader.readInt(2),this.centralDirRecords=this.reader.readInt(2),this.centralDirSize=this.reader.readInt(4),this.centralDirOffset=this.reader.readInt(4),this.zipCommentLength=this.reader.readInt(2),this.zipComment=this.reader.readString(this.zipCommentLength),this.zipComment=k.utf8decode(this.zipComment)},readBlockZip64EndOfCentral:function(){this.zip64EndOfCentralSize=this.reader.readInt(8),this.versionMadeBy=this.reader.readString(2),this.versionNeeded=this.reader.readInt(2),this.diskNumber=this.reader.readInt(4),this.diskWithCentralDirStart=this.reader.readInt(4),this.centralDirRecordsOnThisDisk=this.reader.readInt(8),this.centralDirRecords=this.reader.readInt(8),this.centralDirSize=this.reader.readInt(8),this.centralDirOffset=this.reader.readInt(8),this.zip64ExtensibleData={};for(var a,b,c,d=this.zip64EndOfCentralSize-44,e=0;d>e;)a=this.reader.readInt(2),b=this.reader.readInt(4),c=this.reader.readString(b),this.zip64ExtensibleData[a]={id:a,length:b,value:c}},readBlockZip64EndOfCentralLocator:function(){if(this.diskWithZip64CentralDirStart=this.reader.readInt(4),this.relativeOffsetEndOfZip64CentralDir=this.reader.readInt(8),this.disksCount=this.reader.readInt(4),this.disksCount>1)throw new Error("Multi-volumes zip are not supported")},readLocalFiles:function(){var a,b;for(a=0;a<this.files.length;a++)b=this.files[a],this.reader.setIndex(b.localHeaderOffset),this.checkSignature(h.LOCAL_FILE_HEADER),b.readLocalPart(this.reader),b.handleUTF8()},readCentralDir:function(){var a;for(this.reader.setIndex(this.centralDirOffset);this.reader.readString(4)===h.CENTRAL_FILE_HEADER;)a=new i({zip64:this.zip64},this.loadOptions),a.readCentralPart(this.reader),this.files.push(a)},readEndOfCentral:function(){var a=this.reader.lastIndexOfSignature(h.CENTRAL_DIRECTORY_END);if(-1===a)throw new Error("Corrupted zip : can't find end of central directory");if(this.reader.setIndex(a),this.checkSignature(h.CENTRAL_DIRECTORY_END),this.readBlockEndOfCentral(),this.diskNumber===g.MAX_VALUE_16BITS||this.diskWithCentralDirStart===g.MAX_VALUE_16BITS||this.centralDirRecordsOnThisDisk===g.MAX_VALUE_16BITS||this.centralDirRecords===g.MAX_VALUE_16BITS||this.centralDirSize===g.MAX_VALUE_32BITS||this.centralDirOffset===g.MAX_VALUE_32BITS){if(this.zip64=!0,a=this.reader.lastIndexOfSignature(h.ZIP64_CENTRAL_DIRECTORY_LOCATOR),-1===a)throw new Error("Corrupted zip : can't find the ZIP64 end of central directory locator");this.reader.setIndex(a),this.checkSignature(h.ZIP64_CENTRAL_DIRECTORY_LOCATOR),this.readBlockZip64EndOfCentralLocator(),this.reader.setIndex(this.relativeOffsetEndOfZip64CentralDir),this.checkSignature(h.ZIP64_CENTRAL_DIRECTORY_END),this.readBlockZip64EndOfCentral()}},prepareReader:function(a){var b=g.getTypeOf(a);this.reader="string"!==b||j.uint8array?"nodebuffer"===b?new e(a):new f(g.transformTo("uint8array",a)):new d(a,this.loadOptions.optimizedBinaryString)},load:function(a){this.prepareReader(a),this.readEndOfCentral(),this.readCentralDir(),this.readLocalFiles()}},b.exports=c},{"./nodeBufferReader":12,"./object":13,"./signature":14,"./stringReader":15,"./support":17,"./uint8ArrayReader":18,"./utils":21,"./zipEntry":23}],23:[function(a,b){"use strict";function c(a,b){this.options=a,this.loadOptions=b}var d=a("./stringReader"),e=a("./utils"),f=a("./compressedObject"),g=a("./object");c.prototype={isEncrypted:function(){return 1===(1&this.bitFlag)},useUTF8:function(){return 2048===(2048&this.bitFlag)},prepareCompressedContent:function(a,b,c){return function(){var d=a.index;a.setIndex(b);var e=a.readData(c);return a.setIndex(d),e}},prepareContent:function(a,b,c,d,f){return function(){var a=e.transformTo(d.uncompressInputType,this.getCompressedContent()),b=d.uncompress(a);if(b.length!==f)throw new Error("Bug : uncompressed data size mismatch");return b}},readLocalPart:function(a){var b,c;if(a.skip(22),this.fileNameLength=a.readInt(2),c=a.readInt(2),this.fileName=a.readString(this.fileNameLength),a.skip(c),-1==this.compressedSize||-1==this.uncompressedSize)throw new Error("Bug or corrupted zip : didn't get enough informations from the central directory (compressedSize == -1 || uncompressedSize == -1)");if(b=e.findCompression(this.compressionMethod),null===b)throw new Error("Corrupted zip : compression "+e.pretty(this.compressionMethod)+" unknown (inner file : "+this.fileName+")");if(this.decompressed=new f,this.decompressed.compressedSize=this.compressedSize,this.decompressed.uncompressedSize=this.uncompressedSize,this.decompressed.crc32=this.crc32,this.decompressed.compressionMethod=this.compressionMethod,this.decompressed.getCompressedContent=this.prepareCompressedContent(a,a.index,this.compressedSize,b),this.decompressed.getContent=this.prepareContent(a,a.index,this.compressedSize,b,this.uncompressedSize),this.loadOptions.checkCRC32&&(this.decompressed=e.transformTo("string",this.decompressed.getContent()),g.crc32(this.decompressed)!==this.crc32))throw new Error("Corrupted zip : CRC32 mismatch")},readCentralPart:function(a){if(this.versionMadeBy=a.readString(2),this.versionNeeded=a.readInt(2),this.bitFlag=a.readInt(2),this.compressionMethod=a.readString(2),this.date=a.readDate(),this.crc32=a.readInt(4),this.compressedSize=a.readInt(4),this.uncompressedSize=a.readInt(4),this.fileNameLength=a.readInt(2),this.extraFieldsLength=a.readInt(2),this.fileCommentLength=a.readInt(2),this.diskNumberStart=a.readInt(2),this.internalFileAttributes=a.readInt(2),this.externalFileAttributes=a.readInt(4),this.localHeaderOffset=a.readInt(4),this.isEncrypted())throw new Error("Encrypted zip are not supported");this.fileName=a.readString(this.fileNameLength),this.readExtraFields(a),this.parseZIP64ExtraField(a),this.fileComment=a.readString(this.fileCommentLength),this.dir=16&this.externalFileAttributes?!0:!1},parseZIP64ExtraField:function(){if(this.extraFields[1]){var a=new d(this.extraFields[1].value);this.uncompressedSize===e.MAX_VALUE_32BITS&&(this.uncompressedSize=a.readInt(8)),this.compressedSize===e.MAX_VALUE_32BITS&&(this.compressedSize=a.readInt(8)),this.localHeaderOffset===e.MAX_VALUE_32BITS&&(this.localHeaderOffset=a.readInt(8)),this.diskNumberStart===e.MAX_VALUE_32BITS&&(this.diskNumberStart=a.readInt(4))}},readExtraFields:function(a){var b,c,d,e=a.index;for(this.extraFields=this.extraFields||{};a.index<e+this.extraFieldsLength;)b=a.readInt(2),c=a.readInt(2),d=a.readString(c),this.extraFields[b]={id:b,length:c,value:d}},handleUTF8:function(){if(this.useUTF8())this.fileName=g.utf8decode(this.fileName),this.fileComment=g.utf8decode(this.fileComment);else{var a=this.findExtraFieldUnicodePath();null!==a&&(this.fileName=a);var b=this.findExtraFieldUnicodeComment();null!==b&&(this.fileComment=b)}},findExtraFieldUnicodePath:function(){var a=this.extraFields[28789];if(a){var b=new d(a.value);return 1!==b.readInt(1)?null:g.crc32(this.fileName)!==b.readInt(4)?null:g.utf8decode(b.readString(a.length-5))}return null},findExtraFieldUnicodeComment:function(){var a=this.extraFields[25461];if(a){var b=new d(a.value);return 1!==b.readInt(1)?null:g.crc32(this.fileComment)!==b.readInt(4)?null:g.utf8decode(b.readString(a.length-5))}return null}},b.exports=c},{"./compressedObject":2,"./object":13,"./stringReader":15,"./utils":21}],24:[function(a,b){"use strict";var c=a("./lib/utils/common").assign,d=a("./lib/deflate"),e=a("./lib/inflate"),f=a("./lib/zlib/constants"),g={};c(g,d,e,f),b.exports=g},{"./lib/deflate":25,"./lib/inflate":26,"./lib/utils/common":27,"./lib/zlib/constants":30}],25:[function(a,b,c){"use strict";function d(a,b){var c=new s(b);if(c.push(a,!0),c.err)throw c.msg;return c.result}function e(a,b){return b=b||{},b.raw=!0,d(a,b)}function f(a,b){return b=b||{},b.gzip=!0,d(a,b)}var g=a("./zlib/deflate.js"),h=a("./utils/common"),i=a("./utils/strings"),j=a("./zlib/messages"),k=a("./zlib/zstream"),l=0,m=4,n=0,o=1,p=-1,q=0,r=8,s=function(a){this.options=h.assign({level:p,method:r,chunkSize:16384,windowBits:15,memLevel:8,strategy:q,to:""},a||{});var b=this.options;b.raw&&b.windowBits>0?b.windowBits=-b.windowBits:b.gzip&&b.windowBits>0&&b.windowBits<16&&(b.windowBits+=16),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new k,this.strm.avail_out=0;var c=g.deflateInit2(this.strm,b.level,b.method,b.windowBits,b.memLevel,b.strategy);if(c!==n)throw new Error(j[c]);b.header&&g.deflateSetHeader(this.strm,b.header)};s.prototype.push=function(a,b){var c,d,e=this.strm,f=this.options.chunkSize;if(this.ended)return!1;d=b===~~b?b:b===!0?m:l,e.input="string"==typeof a?i.string2buf(a):a,e.next_in=0,e.avail_in=e.input.length;do{if(0===e.avail_out&&(e.output=new h.Buf8(f),e.next_out=0,e.avail_out=f),c=g.deflate(e,d),c!==o&&c!==n)return this.onEnd(c),this.ended=!0,!1;(0===e.avail_out||0===e.avail_in&&d===m)&&this.onData("string"===this.options.to?i.buf2binstring(h.shrinkBuf(e.output,e.next_out)):h.shrinkBuf(e.output,e.next_out))}while((e.avail_in>0||0===e.avail_out)&&c!==o);return d===m?(c=g.deflateEnd(this.strm),this.onEnd(c),this.ended=!0,c===n):!0},s.prototype.onData=function(a){this.chunks.push(a)},s.prototype.onEnd=function(a){a===n&&(this.result="string"===this.options.to?this.chunks.join(""):h.flattenChunks(this.chunks)),this.chunks=[],this.err=a,this.msg=this.strm.msg},c.Deflate=s,c.deflate=d,c.deflateRaw=e,c.gzip=f},{"./utils/common":27,"./utils/strings":28,"./zlib/deflate.js":32,"./zlib/messages":37,"./zlib/zstream":39}],26:[function(a,b,c){"use strict";function d(a,b){var c=new m(b);if(c.push(a,!0),c.err)throw c.msg;return c.result}function e(a,b){return b=b||{},b.raw=!0,d(a,b)}var f=a("./zlib/inflate.js"),g=a("./utils/common"),h=a("./utils/strings"),i=a("./zlib/constants"),j=a("./zlib/messages"),k=a("./zlib/zstream"),l=a("./zlib/gzheader"),m=function(a){this.options=g.assign({chunkSize:16384,windowBits:0,to:""},a||{});var b=this.options;b.raw&&b.windowBits>=0&&b.windowBits<16&&(b.windowBits=-b.windowBits,0===b.windowBits&&(b.windowBits=-15)),!(b.windowBits>=0&&b.windowBits<16)||a&&a.windowBits||(b.windowBits+=32),b.windowBits>15&&b.windowBits<48&&0===(15&b.windowBits)&&(b.windowBits|=15),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new k,this.strm.avail_out=0;var c=f.inflateInit2(this.strm,b.windowBits);if(c!==i.Z_OK)throw new Error(j[c]);this.header=new l,f.inflateGetHeader(this.strm,this.header)};m.prototype.push=function(a,b){var c,d,e,j,k,l=this.strm,m=this.options.chunkSize;if(this.ended)return!1;d=b===~~b?b:b===!0?i.Z_FINISH:i.Z_NO_FLUSH,l.input="string"==typeof a?h.binstring2buf(a):a,l.next_in=0,l.avail_in=l.input.length;do{if(0===l.avail_out&&(l.output=new g.Buf8(m),l.next_out=0,l.avail_out=m),c=f.inflate(l,i.Z_NO_FLUSH),c!==i.Z_STREAM_END&&c!==i.Z_OK)return this.onEnd(c),this.ended=!0,!1;l.next_out&&(0===l.avail_out||c===i.Z_STREAM_END||0===l.avail_in&&d===i.Z_FINISH)&&("string"===this.options.to?(e=h.utf8border(l.output,l.next_out),j=l.next_out-e,k=h.buf2string(l.output,e),l.next_out=j,l.avail_out=m-j,j&&g.arraySet(l.output,l.output,e,j,0),this.onData(k)):this.onData(g.shrinkBuf(l.output,l.next_out)))}while(l.avail_in>0&&c!==i.Z_STREAM_END);return c===i.Z_STREAM_END&&(d=i.Z_FINISH),d===i.Z_FINISH?(c=f.inflateEnd(this.strm),this.onEnd(c),this.ended=!0,c===i.Z_OK):!0},m.prototype.onData=function(a){this.chunks.push(a)},m.prototype.onEnd=function(a){a===i.Z_OK&&(this.result="string"===this.options.to?this.chunks.join(""):g.flattenChunks(this.chunks)),this.chunks=[],this.err=a,this.msg=this.strm.msg},c.Inflate=m,c.inflate=d,c.inflateRaw=e,c.ungzip=d},{"./utils/common":27,"./utils/strings":28,"./zlib/constants":30,"./zlib/gzheader":33,"./zlib/inflate.js":35,"./zlib/messages":37,"./zlib/zstream":39}],27:[function(a,b,c){"use strict";var d="undefined"!=typeof Uint8Array&&"undefined"!=typeof Uint16Array&&"undefined"!=typeof Int32Array;c.assign=function(a){for(var b=Array.prototype.slice.call(arguments,1);b.length;){var c=b.shift();if(c){if("object"!=typeof c)throw new TypeError(c+"must be non-object");for(var d in c)c.hasOwnProperty(d)&&(a[d]=c[d])}}return a},c.shrinkBuf=function(a,b){return a.length===b?a:a.subarray?a.subarray(0,b):(a.length=b,a)};var e={arraySet:function(a,b,c,d,e){if(b.subarray&&a.subarray)return void a.set(b.subarray(c,c+d),e);for(var f=0;d>f;f++)a[e+f]=b[c+f]},flattenChunks:function(a){var b,c,d,e,f,g;for(d=0,b=0,c=a.length;c>b;b++)d+=a[b].length;for(g=new Uint8Array(d),e=0,b=0,c=a.length;c>b;b++)f=a[b],g.set(f,e),e+=f.length;return g}},f={arraySet:function(a,b,c,d,e){for(var f=0;d>f;f++)a[e+f]=b[c+f]},flattenChunks:function(a){return[].concat.apply([],a)}};c.setTyped=function(a){a?(c.Buf8=Uint8Array,c.Buf16=Uint16Array,c.Buf32=Int32Array,c.assign(c,e)):(c.Buf8=Array,c.Buf16=Array,c.Buf32=Array,c.assign(c,f))},c.setTyped(d)},{}],28:[function(a,b,c){"use strict";function d(a,b){if(65537>b&&(a.subarray&&g||!a.subarray&&f))return String.fromCharCode.apply(null,e.shrinkBuf(a,b));for(var c="",d=0;b>d;d++)c+=String.fromCharCode(a[d]);return c}var e=a("./common"),f=!0,g=!0;try{String.fromCharCode.apply(null,[0])}catch(h){f=!1}try{String.fromCharCode.apply(null,new Uint8Array(1))}catch(h){g=!1}for(var i=new e.Buf8(256),j=0;256>j;j++)i[j]=j>=252?6:j>=248?5:j>=240?4:j>=224?3:j>=192?2:1;i[254]=i[254]=1,c.string2buf=function(a){var b,c,d,f,g,h=a.length,i=0;for(f=0;h>f;f++)c=a.charCodeAt(f),55296===(64512&c)&&h>f+1&&(d=a.charCodeAt(f+1),56320===(64512&d)&&(c=65536+(c-55296<<10)+(d-56320),f++)),i+=128>c?1:2048>c?2:65536>c?3:4;for(b=new e.Buf8(i),g=0,f=0;i>g;f++)c=a.charCodeAt(f),55296===(64512&c)&&h>f+1&&(d=a.charCodeAt(f+1),56320===(64512&d)&&(c=65536+(c-55296<<10)+(d-56320),f++)),128>c?b[g++]=c:2048>c?(b[g++]=192|c>>>6,b[g++]=128|63&c):65536>c?(b[g++]=224|c>>>12,b[g++]=128|c>>>6&63,b[g++]=128|63&c):(b[g++]=240|c>>>18,b[g++]=128|c>>>12&63,b[g++]=128|c>>>6&63,b[g++]=128|63&c);return b},c.buf2binstring=function(a){return d(a,a.length)},c.binstring2buf=function(a){for(var b=new e.Buf8(a.length),c=0,d=b.length;d>c;c++)b[c]=a.charCodeAt(c);return b},c.buf2string=function(a,b){var c,e,f,g,h=b||a.length,j=new Array(2*h);for(e=0,c=0;h>c;)if(f=a[c++],128>f)j[e++]=f;else if(g=i[f],g>4)j[e++]=65533,c+=g-1;else{for(f&=2===g?31:3===g?15:7;g>1&&h>c;)f=f<<6|63&a[c++],g--;g>1?j[e++]=65533:65536>f?j[e++]=f:(f-=65536,j[e++]=55296|f>>10&1023,j[e++]=56320|1023&f)}return d(j,e)},c.utf8border=function(a,b){var c;for(b=b||a.length,b>a.length&&(b=a.length),c=b-1;c>=0&&128===(192&a[c]);)c--;return 0>c?b:0===c?b:c+i[a[c]]>b?c:b}},{"./common":27}],29:[function(a,b){"use strict";function c(a,b,c,d){for(var e=65535&a|0,f=a>>>16&65535|0,g=0;0!==c;){g=c>2e3?2e3:c,c-=g;do e=e+b[d++]|0,f=f+e|0;while(--g);e%=65521,f%=65521}return e|f<<16|0}b.exports=c},{}],30:[function(a,b){b.exports={Z_NO_FLUSH:0,Z_PARTIAL_FLUSH:1,Z_SYNC_FLUSH:2,Z_FULL_FLUSH:3,Z_FINISH:4,Z_BLOCK:5,Z_TREES:6,Z_OK:0,Z_STREAM_END:1,Z_NEED_DICT:2,Z_ERRNO:-1,Z_STREAM_ERROR:-2,Z_DATA_ERROR:-3,Z_BUF_ERROR:-5,Z_NO_COMPRESSION:0,Z_BEST_SPEED:1,Z_BEST_COMPRESSION:9,Z_DEFAULT_COMPRESSION:-1,Z_FILTERED:1,Z_HUFFMAN_ONLY:2,Z_RLE:3,Z_FIXED:4,Z_DEFAULT_STRATEGY:0,Z_BINARY:0,Z_TEXT:1,Z_UNKNOWN:2,Z_DEFLATED:8}},{}],31:[function(a,b){"use strict";function c(){for(var a,b=[],c=0;256>c;c++){a=c;for(var d=0;8>d;d++)a=1&a?3988292384^a>>>1:a>>>1;b[c]=a}return b}function d(a,b,c,d){var f=e,g=d+c;a=-1^a;for(var h=d;g>h;h++)a=a>>>8^f[255&(a^b[h])];return-1^a}var e=c();b.exports=d},{}],32:[function(a,b,c){"use strict";function d(a,b){return a.msg=G[b],b}function e(a){return(a<<1)-(a>4?9:0)}function f(a){for(var b=a.length;--b>=0;)a[b]=0}function g(a){var b=a.state,c=b.pending;c>a.avail_out&&(c=a.avail_out),0!==c&&(C.arraySet(a.output,b.pending_buf,b.pending_out,c,a.next_out),a.next_out+=c,b.pending_out+=c,a.total_out+=c,a.avail_out-=c,b.pending-=c,0===b.pending&&(b.pending_out=0))}function h(a,b){D._tr_flush_block(a,a.block_start>=0?a.block_start:-1,a.strstart-a.block_start,b),a.block_start=a.strstart,g(a.strm)}function i(a,b){a.pending_buf[a.pending++]=b}function j(a,b){a.pending_buf[a.pending++]=b>>>8&255,a.pending_buf[a.pending++]=255&b}function k(a,b,c,d){var e=a.avail_in;return e>d&&(e=d),0===e?0:(a.avail_in-=e,C.arraySet(b,a.input,a.next_in,e,c),1===a.state.wrap?a.adler=E(a.adler,b,e,c):2===a.state.wrap&&(a.adler=F(a.adler,b,e,c)),a.next_in+=e,a.total_in+=e,e)}function l(a,b){var c,d,e=a.max_chain_length,f=a.strstart,g=a.prev_length,h=a.nice_match,i=a.strstart>a.w_size-jb?a.strstart-(a.w_size-jb):0,j=a.window,k=a.w_mask,l=a.prev,m=a.strstart+ib,n=j[f+g-1],o=j[f+g];a.prev_length>=a.good_match&&(e>>=2),h>a.lookahead&&(h=a.lookahead);do if(c=b,j[c+g]===o&&j[c+g-1]===n&&j[c]===j[f]&&j[++c]===j[f+1]){f+=2,c++;do;while(j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&m>f);if(d=ib-(m-f),f=m-ib,d>g){if(a.match_start=b,g=d,d>=h)break;n=j[f+g-1],o=j[f+g]}}while((b=l[b&k])>i&&0!==--e);return g<=a.lookahead?g:a.lookahead}function m(a){var b,c,d,e,f,g=a.w_size;do{if(e=a.window_size-a.lookahead-a.strstart,a.strstart>=g+(g-jb)){C.arraySet(a.window,a.window,g,g,0),a.match_start-=g,a.strstart-=g,a.block_start-=g,c=a.hash_size,b=c;do d=a.head[--b],a.head[b]=d>=g?d-g:0;while(--c);c=g,b=c;do d=a.prev[--b],a.prev[b]=d>=g?d-g:0;while(--c);e+=g}if(0===a.strm.avail_in)break;if(c=k(a.strm,a.window,a.strstart+a.lookahead,e),a.lookahead+=c,a.lookahead+a.insert>=hb)for(f=a.strstart-a.insert,a.ins_h=a.window[f],a.ins_h=(a.ins_h<<a.hash_shift^a.window[f+1])&a.hash_mask;a.insert&&(a.ins_h=(a.ins_h<<a.hash_shift^a.window[f+hb-1])&a.hash_mask,a.prev[f&a.w_mask]=a.head[a.ins_h],a.head[a.ins_h]=f,f++,a.insert--,!(a.lookahead+a.insert<hb)););}while(a.lookahead<jb&&0!==a.strm.avail_in)}function n(a,b){var c=65535;for(c>a.pending_buf_size-5&&(c=a.pending_buf_size-5);;){if(a.lookahead<=1){if(m(a),0===a.lookahead&&b===H)return sb;if(0===a.lookahead)break}a.strstart+=a.lookahead,a.lookahead=0;var d=a.block_start+c;if((0===a.strstart||a.strstart>=d)&&(a.lookahead=a.strstart-d,a.strstart=d,h(a,!1),0===a.strm.avail_out))return sb;if(a.strstart-a.block_start>=a.w_size-jb&&(h(a,!1),0===a.strm.avail_out))return sb}return a.insert=0,b===K?(h(a,!0),0===a.strm.avail_out?ub:vb):a.strstart>a.block_start&&(h(a,!1),0===a.strm.avail_out)?sb:sb}function o(a,b){for(var c,d;;){if(a.lookahead<jb){if(m(a),a.lookahead<jb&&b===H)return sb;if(0===a.lookahead)break}if(c=0,a.lookahead>=hb&&(a.ins_h=(a.ins_h<<a.hash_shift^a.window[a.strstart+hb-1])&a.hash_mask,c=a.prev[a.strstart&a.w_mask]=a.head[a.ins_h],a.head[a.ins_h]=a.strstart),0!==c&&a.strstart-c<=a.w_size-jb&&(a.match_length=l(a,c)),a.match_length>=hb)if(d=D._tr_tally(a,a.strstart-a.match_start,a.match_length-hb),a.lookahead-=a.match_length,a.match_length<=a.max_lazy_match&&a.lookahead>=hb){a.match_length--;do a.strstart++,a.ins_h=(a.ins_h<<a.hash_shift^a.window[a.strstart+hb-1])&a.hash_mask,c=a.prev[a.strstart&a.w_mask]=a.head[a.ins_h],a.head[a.ins_h]=a.strstart;while(0!==--a.match_length);a.strstart++}else a.strstart+=a.match_length,a.match_length=0,a.ins_h=a.window[a.strstart],a.ins_h=(a.ins_h<<a.hash_shift^a.window[a.strstart+1])&a.hash_mask;else d=D._tr_tally(a,0,a.window[a.strstart]),a.lookahead--,a.strstart++;if(d&&(h(a,!1),0===a.strm.avail_out))return sb}return a.insert=a.strstart<hb-1?a.strstart:hb-1,b===K?(h(a,!0),0===a.strm.avail_out?ub:vb):a.last_lit&&(h(a,!1),0===a.strm.avail_out)?sb:tb}function p(a,b){for(var c,d,e;;){if(a.lookahead<jb){if(m(a),a.lookahead<jb&&b===H)return sb;if(0===a.lookahead)break}if(c=0,a.lookahead>=hb&&(a.ins_h=(a.ins_h<<a.hash_shift^a.window[a.strstart+hb-1])&a.hash_mask,c=a.prev[a.strstart&a.w_mask]=a.head[a.ins_h],a.head[a.ins_h]=a.strstart),a.prev_length=a.match_length,a.prev_match=a.match_start,a.match_length=hb-1,0!==c&&a.prev_length<a.max_lazy_match&&a.strstart-c<=a.w_size-jb&&(a.match_length=l(a,c),a.match_length<=5&&(a.strategy===S||a.match_length===hb&&a.strstart-a.match_start>4096)&&(a.match_length=hb-1)),a.prev_length>=hb&&a.match_length<=a.prev_length){e=a.strstart+a.lookahead-hb,d=D._tr_tally(a,a.strstart-1-a.prev_match,a.prev_length-hb),a.lookahead-=a.prev_length-1,a.prev_length-=2;do++a.strstart<=e&&(a.ins_h=(a.ins_h<<a.hash_shift^a.window[a.strstart+hb-1])&a.hash_mask,c=a.prev[a.strstart&a.w_mask]=a.head[a.ins_h],a.head[a.ins_h]=a.strstart);while(0!==--a.prev_length);if(a.match_available=0,a.match_length=hb-1,a.strstart++,d&&(h(a,!1),0===a.strm.avail_out))return sb}else if(a.match_available){if(d=D._tr_tally(a,0,a.window[a.strstart-1]),d&&h(a,!1),a.strstart++,a.lookahead--,0===a.strm.avail_out)return sb}else a.match_available=1,a.strstart++,a.lookahead--}return a.match_available&&(d=D._tr_tally(a,0,a.window[a.strstart-1]),a.match_available=0),a.insert=a.strstart<hb-1?a.strstart:hb-1,b===K?(h(a,!0),0===a.strm.avail_out?ub:vb):a.last_lit&&(h(a,!1),0===a.strm.avail_out)?sb:tb}function q(a,b){for(var c,d,e,f,g=a.window;;){if(a.lookahead<=ib){if(m(a),a.lookahead<=ib&&b===H)return sb;if(0===a.lookahead)break}if(a.match_length=0,a.lookahead>=hb&&a.strstart>0&&(e=a.strstart-1,d=g[e],d===g[++e]&&d===g[++e]&&d===g[++e])){f=a.strstart+ib;do;while(d===g[++e]&&d===g[++e]&&d===g[++e]&&d===g[++e]&&d===g[++e]&&d===g[++e]&&d===g[++e]&&d===g[++e]&&f>e);a.match_length=ib-(f-e),a.match_length>a.lookahead&&(a.match_length=a.lookahead)}if(a.match_length>=hb?(c=D._tr_tally(a,1,a.match_length-hb),a.lookahead-=a.match_length,a.strstart+=a.match_length,a.match_length=0):(c=D._tr_tally(a,0,a.window[a.strstart]),a.lookahead--,a.strstart++),c&&(h(a,!1),0===a.strm.avail_out))return sb}return a.insert=0,b===K?(h(a,!0),0===a.strm.avail_out?ub:vb):a.last_lit&&(h(a,!1),0===a.strm.avail_out)?sb:tb}function r(a,b){for(var c;;){if(0===a.lookahead&&(m(a),0===a.lookahead)){if(b===H)return sb;break}if(a.match_length=0,c=D._tr_tally(a,0,a.window[a.strstart]),a.lookahead--,a.strstart++,c&&(h(a,!1),0===a.strm.avail_out))return sb}return a.insert=0,b===K?(h(a,!0),0===a.strm.avail_out?ub:vb):a.last_lit&&(h(a,!1),0===a.strm.avail_out)?sb:tb}function s(a){a.window_size=2*a.w_size,f(a.head),a.max_lazy_match=B[a.level].max_lazy,a.good_match=B[a.level].good_length,a.nice_match=B[a.level].nice_length,a.max_chain_length=B[a.level].max_chain,a.strstart=0,a.block_start=0,a.lookahead=0,a.insert=0,a.match_length=a.prev_length=hb-1,a.match_available=0,a.ins_h=0}function t(){this.strm=null,this.status=0,this.pending_buf=null,this.pending_buf_size=0,this.pending_out=0,this.pending=0,this.wrap=0,this.gzhead=null,this.gzindex=0,this.method=Y,this.last_flush=-1,this.w_size=0,this.w_bits=0,this.w_mask=0,this.window=null,this.window_size=0,this.prev=null,this.head=null,this.ins_h=0,this.hash_size=0,this.hash_bits=0,this.hash_mask=0,this.hash_shift=0,this.block_start=0,this.match_length=0,this.prev_match=0,this.match_available=0,this.strstart=0,this.match_start=0,this.lookahead=0,this.prev_length=0,this.max_chain_length=0,this.max_lazy_match=0,this.level=0,this.strategy=0,this.good_match=0,this.nice_match=0,this.dyn_ltree=new C.Buf16(2*fb),this.dyn_dtree=new C.Buf16(2*(2*db+1)),this.bl_tree=new C.Buf16(2*(2*eb+1)),f(this.dyn_ltree),f(this.dyn_dtree),f(this.bl_tree),this.l_desc=null,this.d_desc=null,this.bl_desc=null,this.bl_count=new C.Buf16(gb+1),this.heap=new C.Buf16(2*cb+1),f(this.heap),this.heap_len=0,this.heap_max=0,this.depth=new C.Buf16(2*cb+1),f(this.depth),this.l_buf=0,this.lit_bufsize=0,this.last_lit=0,this.d_buf=0,this.opt_len=0,this.static_len=0,this.matches=0,this.insert=0,this.bi_buf=0,this.bi_valid=0}function u(a){var b;return a&&a.state?(a.total_in=a.total_out=0,a.data_type=X,b=a.state,b.pending=0,b.pending_out=0,b.wrap<0&&(b.wrap=-b.wrap),b.status=b.wrap?lb:qb,a.adler=2===b.wrap?0:1,b.last_flush=H,D._tr_init(b),M):d(a,O)}function v(a){var b=u(a);return b===M&&s(a.state),b}function w(a,b){return a&&a.state?2!==a.state.wrap?O:(a.state.gzhead=b,M):O}function x(a,b,c,e,f,g){if(!a)return O;var h=1;if(b===R&&(b=6),0>e?(h=0,e=-e):e>15&&(h=2,e-=16),1>f||f>Z||c!==Y||8>e||e>15||0>b||b>9||0>g||g>V)return d(a,O);8===e&&(e=9);var i=new t;return a.state=i,i.strm=a,i.wrap=h,i.gzhead=null,i.w_bits=e,i.w_size=1<<i.w_bits,i.w_mask=i.w_size-1,i.hash_bits=f+7,i.hash_size=1<<i.hash_bits,i.hash_mask=i.hash_size-1,i.hash_shift=~~((i.hash_bits+hb-1)/hb),i.window=new C.Buf8(2*i.w_size),i.head=new C.Buf16(i.hash_size),i.prev=new C.Buf16(i.w_size),i.lit_bufsize=1<<f+6,i.pending_buf_size=4*i.lit_bufsize,i.pending_buf=new C.Buf8(i.pending_buf_size),i.d_buf=i.lit_bufsize>>1,i.l_buf=3*i.lit_bufsize,i.level=b,i.strategy=g,i.method=c,v(a)}function y(a,b){return x(a,b,Y,$,_,W)}function z(a,b){var c,h,k,l;if(!a||!a.state||b>L||0>b)return a?d(a,O):O;if(h=a.state,!a.output||!a.input&&0!==a.avail_in||h.status===rb&&b!==K)return d(a,0===a.avail_out?Q:O);if(h.strm=a,c=h.last_flush,h.last_flush=b,h.status===lb)if(2===h.wrap)a.adler=0,i(h,31),i(h,139),i(h,8),h.gzhead?(i(h,(h.gzhead.text?1:0)+(h.gzhead.hcrc?2:0)+(h.gzhead.extra?4:0)+(h.gzhead.name?8:0)+(h.gzhead.comment?16:0)),i(h,255&h.gzhead.time),i(h,h.gzhead.time>>8&255),i(h,h.gzhead.time>>16&255),i(h,h.gzhead.time>>24&255),i(h,9===h.level?2:h.strategy>=T||h.level<2?4:0),i(h,255&h.gzhead.os),h.gzhead.extra&&h.gzhead.extra.length&&(i(h,255&h.gzhead.extra.length),i(h,h.gzhead.extra.length>>8&255)),h.gzhead.hcrc&&(a.adler=F(a.adler,h.pending_buf,h.pending,0)),h.gzindex=0,h.status=mb):(i(h,0),i(h,0),i(h,0),i(h,0),i(h,0),i(h,9===h.level?2:h.strategy>=T||h.level<2?4:0),i(h,wb),h.status=qb);else{var m=Y+(h.w_bits-8<<4)<<8,n=-1;n=h.strategy>=T||h.level<2?0:h.level<6?1:6===h.level?2:3,m|=n<<6,0!==h.strstart&&(m|=kb),m+=31-m%31,h.status=qb,j(h,m),0!==h.strstart&&(j(h,a.adler>>>16),j(h,65535&a.adler)),a.adler=1}if(h.status===mb)if(h.gzhead.extra){for(k=h.pending;h.gzindex<(65535&h.gzhead.extra.length)&&(h.pending!==h.pending_buf_size||(h.gzhead.hcrc&&h.pending>k&&(a.adler=F(a.adler,h.pending_buf,h.pending-k,k)),g(a),k=h.pending,h.pending!==h.pending_buf_size));)i(h,255&h.gzhead.extra[h.gzindex]),h.gzindex++;h.gzhead.hcrc&&h.pending>k&&(a.adler=F(a.adler,h.pending_buf,h.pending-k,k)),h.gzindex===h.gzhead.extra.length&&(h.gzindex=0,h.status=nb)}else h.status=nb;if(h.status===nb)if(h.gzhead.name){k=h.pending;do{if(h.pending===h.pending_buf_size&&(h.gzhead.hcrc&&h.pending>k&&(a.adler=F(a.adler,h.pending_buf,h.pending-k,k)),g(a),k=h.pending,h.pending===h.pending_buf_size)){l=1;break}l=h.gzindex<h.gzhead.name.length?255&h.gzhead.name.charCodeAt(h.gzindex++):0,i(h,l)}while(0!==l);h.gzhead.hcrc&&h.pending>k&&(a.adler=F(a.adler,h.pending_buf,h.pending-k,k)),0===l&&(h.gzindex=0,h.status=ob)}else h.status=ob;if(h.status===ob)if(h.gzhead.comment){k=h.pending;do{if(h.pending===h.pending_buf_size&&(h.gzhead.hcrc&&h.pending>k&&(a.adler=F(a.adler,h.pending_buf,h.pending-k,k)),g(a),k=h.pending,h.pending===h.pending_buf_size)){l=1;break}l=h.gzindex<h.gzhead.comment.length?255&h.gzhead.comment.charCodeAt(h.gzindex++):0,i(h,l)}while(0!==l);h.gzhead.hcrc&&h.pending>k&&(a.adler=F(a.adler,h.pending_buf,h.pending-k,k)),0===l&&(h.status=pb)}else h.status=pb;if(h.status===pb&&(h.gzhead.hcrc?(h.pending+2>h.pending_buf_size&&g(a),h.pending+2<=h.pending_buf_size&&(i(h,255&a.adler),i(h,a.adler>>8&255),a.adler=0,h.status=qb)):h.status=qb),0!==h.pending){if(g(a),0===a.avail_out)return h.last_flush=-1,M}else if(0===a.avail_in&&e(b)<=e(c)&&b!==K)return d(a,Q);if(h.status===rb&&0!==a.avail_in)return d(a,Q);if(0!==a.avail_in||0!==h.lookahead||b!==H&&h.status!==rb){var o=h.strategy===T?r(h,b):h.strategy===U?q(h,b):B[h.level].func(h,b);if((o===ub||o===vb)&&(h.status=rb),o===sb||o===ub)return 0===a.avail_out&&(h.last_flush=-1),M;if(o===tb&&(b===I?D._tr_align(h):b!==L&&(D._tr_stored_block(h,0,0,!1),b===J&&(f(h.head),0===h.lookahead&&(h.strstart=0,h.block_start=0,h.insert=0))),g(a),0===a.avail_out))return h.last_flush=-1,M}return b!==K?M:h.wrap<=0?N:(2===h.wrap?(i(h,255&a.adler),i(h,a.adler>>8&255),i(h,a.adler>>16&255),i(h,a.adler>>24&255),i(h,255&a.total_in),i(h,a.total_in>>8&255),i(h,a.total_in>>16&255),i(h,a.total_in>>24&255)):(j(h,a.adler>>>16),j(h,65535&a.adler)),g(a),h.wrap>0&&(h.wrap=-h.wrap),0!==h.pending?M:N)}function A(a){var b;return a&&a.state?(b=a.state.status,b!==lb&&b!==mb&&b!==nb&&b!==ob&&b!==pb&&b!==qb&&b!==rb?d(a,O):(a.state=null,b===qb?d(a,P):M)):O}var B,C=a("../utils/common"),D=a("./trees"),E=a("./adler32"),F=a("./crc32"),G=a("./messages"),H=0,I=1,J=3,K=4,L=5,M=0,N=1,O=-2,P=-3,Q=-5,R=-1,S=1,T=2,U=3,V=4,W=0,X=2,Y=8,Z=9,$=15,_=8,ab=29,bb=256,cb=bb+1+ab,db=30,eb=19,fb=2*cb+1,gb=15,hb=3,ib=258,jb=ib+hb+1,kb=32,lb=42,mb=69,nb=73,ob=91,pb=103,qb=113,rb=666,sb=1,tb=2,ub=3,vb=4,wb=3,xb=function(a,b,c,d,e){this.good_length=a,this.max_lazy=b,this.nice_length=c,this.max_chain=d,this.func=e};B=[new xb(0,0,0,0,n),new xb(4,4,8,4,o),new xb(4,5,16,8,o),new xb(4,6,32,32,o),new xb(4,4,16,16,p),new xb(8,16,32,32,p),new xb(8,16,128,128,p),new xb(8,32,128,256,p),new xb(32,128,258,1024,p),new xb(32,258,258,4096,p)],c.deflateInit=y,c.deflateInit2=x,c.deflateReset=v,c.deflateResetKeep=u,c.deflateSetHeader=w,c.deflate=z,c.deflateEnd=A,c.deflateInfo="pako deflate (from Nodeca project)"},{"../utils/common":27,"./adler32":29,"./crc32":31,"./messages":37,"./trees":38}],33:[function(a,b){"use strict";function c(){this.text=0,this.time=0,this.xflags=0,this.os=0,this.extra=null,this.extra_len=0,this.name="",this.comment="",this.hcrc=0,this.done=!1}b.exports=c},{}],34:[function(a,b){"use strict";var c=30,d=12;b.exports=function(a,b){var e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C;e=a.state,f=a.next_in,B=a.input,g=f+(a.avail_in-5),h=a.next_out,C=a.output,i=h-(b-a.avail_out),j=h+(a.avail_out-257),k=e.dmax,l=e.wsize,m=e.whave,n=e.wnext,o=e.window,p=e.hold,q=e.bits,r=e.lencode,s=e.distcode,t=(1<<e.lenbits)-1,u=(1<<e.distbits)-1;a:do{15>q&&(p+=B[f++]<<q,q+=8,p+=B[f++]<<q,q+=8),v=r[p&t];b:for(;;){if(w=v>>>24,p>>>=w,q-=w,w=v>>>16&255,0===w)C[h++]=65535&v;else{if(!(16&w)){if(0===(64&w)){v=r[(65535&v)+(p&(1<<w)-1)];continue b}if(32&w){e.mode=d;break a}a.msg="invalid literal/length code",e.mode=c;break a}x=65535&v,w&=15,w&&(w>q&&(p+=B[f++]<<q,q+=8),x+=p&(1<<w)-1,p>>>=w,q-=w),15>q&&(p+=B[f++]<<q,q+=8,p+=B[f++]<<q,q+=8),v=s[p&u];c:for(;;){if(w=v>>>24,p>>>=w,q-=w,w=v>>>16&255,!(16&w)){if(0===(64&w)){v=s[(65535&v)+(p&(1<<w)-1)];continue c}a.msg="invalid distance code",e.mode=c;break a}if(y=65535&v,w&=15,w>q&&(p+=B[f++]<<q,q+=8,w>q&&(p+=B[f++]<<q,q+=8)),y+=p&(1<<w)-1,y>k){a.msg="invalid distance too far back",e.mode=c;break a}if(p>>>=w,q-=w,w=h-i,y>w){if(w=y-w,w>m&&e.sane){a.msg="invalid distance too far back",e.mode=c;break a}if(z=0,A=o,0===n){if(z+=l-w,x>w){x-=w;do C[h++]=o[z++];while(--w);z=h-y,A=C}}else if(w>n){if(z+=l+n-w,w-=n,x>w){x-=w;do C[h++]=o[z++];while(--w);if(z=0,x>n){w=n,x-=w;do C[h++]=o[z++];while(--w);z=h-y,A=C}}}else if(z+=n-w,x>w){x-=w;do C[h++]=o[z++];while(--w);z=h-y,A=C}for(;x>2;)C[h++]=A[z++],C[h++]=A[z++],C[h++]=A[z++],x-=3;x&&(C[h++]=A[z++],x>1&&(C[h++]=A[z++]))}else{z=h-y;do C[h++]=C[z++],C[h++]=C[z++],C[h++]=C[z++],x-=3;while(x>2);x&&(C[h++]=C[z++],x>1&&(C[h++]=C[z++]))}break}}break}}while(g>f&&j>h);x=q>>3,f-=x,q-=x<<3,p&=(1<<q)-1,a.next_in=f,a.next_out=h,a.avail_in=g>f?5+(g-f):5-(f-g),a.avail_out=j>h?257+(j-h):257-(h-j),e.hold=p,e.bits=q}},{}],35:[function(a,b,c){"use strict";function d(a){return(a>>>24&255)+(a>>>8&65280)+((65280&a)<<8)+((255&a)<<24)}function e(){this.mode=0,this.last=!1,this.wrap=0,this.havedict=!1,this.flags=0,this.dmax=0,this.check=0,this.total=0,this.head=null,this.wbits=0,this.wsize=0,this.whave=0,this.wnext=0,this.window=null,this.hold=0,this.bits=0,this.length=0,this.offset=0,this.extra=0,this.lencode=null,this.distcode=null,this.lenbits=0,this.distbits=0,this.ncode=0,this.nlen=0,this.ndist=0,this.have=0,this.next=null,this.lens=new r.Buf16(320),this.work=new r.Buf16(288),this.lendyn=null,this.distdyn=null,this.sane=0,this.back=0,this.was=0}function f(a){var b;return a&&a.state?(b=a.state,a.total_in=a.total_out=b.total=0,a.msg="",b.wrap&&(a.adler=1&b.wrap),b.mode=K,b.last=0,b.havedict=0,b.dmax=32768,b.head=null,b.hold=0,b.bits=0,b.lencode=b.lendyn=new r.Buf32(ob),b.distcode=b.distdyn=new r.Buf32(pb),b.sane=1,b.back=-1,C):F}function g(a){var b;return a&&a.state?(b=a.state,b.wsize=0,b.whave=0,b.wnext=0,f(a)):F}function h(a,b){var c,d;return a&&a.state?(d=a.state,0>b?(c=0,b=-b):(c=(b>>4)+1,48>b&&(b&=15)),b&&(8>b||b>15)?F:(null!==d.window&&d.wbits!==b&&(d.window=null),d.wrap=c,d.wbits=b,g(a))):F}function i(a,b){var c,d;return a?(d=new e,a.state=d,d.window=null,c=h(a,b),c!==C&&(a.state=null),c):F}function j(a){return i(a,rb)}function k(a){if(sb){var b;for(p=new r.Buf32(512),q=new r.Buf32(32),b=0;144>b;)a.lens[b++]=8;for(;256>b;)a.lens[b++]=9;for(;280>b;)a.lens[b++]=7;for(;288>b;)a.lens[b++]=8;for(v(x,a.lens,0,288,p,0,a.work,{bits:9}),b=0;32>b;)a.lens[b++]=5;v(y,a.lens,0,32,q,0,a.work,{bits:5}),sb=!1}a.lencode=p,a.lenbits=9,a.distcode=q,a.distbits=5}function l(a,b,c,d){var e,f=a.state;return null===f.window&&(f.wsize=1<<f.wbits,f.wnext=0,f.whave=0,f.window=new r.Buf8(f.wsize)),d>=f.wsize?(r.arraySet(f.window,b,c-f.wsize,f.wsize,0),f.wnext=0,f.whave=f.wsize):(e=f.wsize-f.wnext,e>d&&(e=d),r.arraySet(f.window,b,c-d,e,f.wnext),d-=e,d?(r.arraySet(f.window,b,c-d,d,0),f.wnext=d,f.whave=f.wsize):(f.wnext+=e,f.wnext===f.wsize&&(f.wnext=0),f.whave<f.wsize&&(f.whave+=e))),0}function m(a,b){var c,e,f,g,h,i,j,m,n,o,p,q,ob,pb,qb,rb,sb,tb,ub,vb,wb,xb,yb,zb,Ab=0,Bb=new r.Buf8(4),Cb=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15];if(!a||!a.state||!a.output||!a.input&&0!==a.avail_in)return F;c=a.state,c.mode===V&&(c.mode=W),h=a.next_out,f=a.output,j=a.avail_out,g=a.next_in,e=a.input,i=a.avail_in,m=c.hold,n=c.bits,o=i,p=j,xb=C;a:for(;;)switch(c.mode){case K:if(0===c.wrap){c.mode=W;break}for(;16>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if(2&c.wrap&&35615===m){c.check=0,Bb[0]=255&m,Bb[1]=m>>>8&255,c.check=t(c.check,Bb,2,0),m=0,n=0,c.mode=L;break}if(c.flags=0,c.head&&(c.head.done=!1),!(1&c.wrap)||(((255&m)<<8)+(m>>8))%31){a.msg="incorrect header check",c.mode=lb;break}if((15&m)!==J){a.msg="unknown compression method",c.mode=lb;break}if(m>>>=4,n-=4,wb=(15&m)+8,0===c.wbits)c.wbits=wb;else if(wb>c.wbits){a.msg="invalid window size",c.mode=lb;break}c.dmax=1<<wb,a.adler=c.check=1,c.mode=512&m?T:V,m=0,n=0;break;case L:for(;16>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if(c.flags=m,(255&c.flags)!==J){a.msg="unknown compression method",c.mode=lb;break}if(57344&c.flags){a.msg="unknown header flags set",c.mode=lb;break}c.head&&(c.head.text=m>>8&1),512&c.flags&&(Bb[0]=255&m,Bb[1]=m>>>8&255,c.check=t(c.check,Bb,2,0)),m=0,n=0,c.mode=M;case M:for(;32>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}c.head&&(c.head.time=m),512&c.flags&&(Bb[0]=255&m,Bb[1]=m>>>8&255,Bb[2]=m>>>16&255,Bb[3]=m>>>24&255,c.check=t(c.check,Bb,4,0)),m=0,n=0,c.mode=N;case N:for(;16>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}c.head&&(c.head.xflags=255&m,c.head.os=m>>8),512&c.flags&&(Bb[0]=255&m,Bb[1]=m>>>8&255,c.check=t(c.check,Bb,2,0)),m=0,n=0,c.mode=O;case O:if(1024&c.flags){for(;16>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}c.length=m,c.head&&(c.head.extra_len=m),512&c.flags&&(Bb[0]=255&m,Bb[1]=m>>>8&255,c.check=t(c.check,Bb,2,0)),m=0,n=0}else c.head&&(c.head.extra=null);c.mode=P;case P:if(1024&c.flags&&(q=c.length,q>i&&(q=i),q&&(c.head&&(wb=c.head.extra_len-c.length,c.head.extra||(c.head.extra=new Array(c.head.extra_len)),r.arraySet(c.head.extra,e,g,q,wb)),512&c.flags&&(c.check=t(c.check,e,q,g)),i-=q,g+=q,c.length-=q),c.length))break a;c.length=0,c.mode=Q;case Q:if(2048&c.flags){if(0===i)break a;q=0;do wb=e[g+q++],c.head&&wb&&c.length<65536&&(c.head.name+=String.fromCharCode(wb));while(wb&&i>q);if(512&c.flags&&(c.check=t(c.check,e,q,g)),i-=q,g+=q,wb)break a}else c.head&&(c.head.name=null);c.length=0,c.mode=R;case R:if(4096&c.flags){if(0===i)break a;q=0;do wb=e[g+q++],c.head&&wb&&c.length<65536&&(c.head.comment+=String.fromCharCode(wb));while(wb&&i>q);if(512&c.flags&&(c.check=t(c.check,e,q,g)),i-=q,g+=q,wb)break a}else c.head&&(c.head.comment=null);c.mode=S;case S:if(512&c.flags){for(;16>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if(m!==(65535&c.check)){a.msg="header crc mismatch",c.mode=lb;break}m=0,n=0}c.head&&(c.head.hcrc=c.flags>>9&1,c.head.done=!0),a.adler=c.check=0,c.mode=V;break;case T:for(;32>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}a.adler=c.check=d(m),m=0,n=0,c.mode=U;case U:if(0===c.havedict)return a.next_out=h,a.avail_out=j,a.next_in=g,a.avail_in=i,c.hold=m,c.bits=n,E;a.adler=c.check=1,c.mode=V;case V:if(b===A||b===B)break a;case W:if(c.last){m>>>=7&n,n-=7&n,c.mode=ib;break}for(;3>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}switch(c.last=1&m,m>>>=1,n-=1,3&m){case 0:c.mode=X;break;case 1:if(k(c),c.mode=bb,b===B){m>>>=2,n-=2;break a}break;case 2:c.mode=$;break;case 3:a.msg="invalid block type",c.mode=lb}m>>>=2,n-=2;break;case X:for(m>>>=7&n,n-=7&n;32>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if((65535&m)!==(m>>>16^65535)){a.msg="invalid stored block lengths",c.mode=lb;break}if(c.length=65535&m,m=0,n=0,c.mode=Y,b===B)break a;case Y:c.mode=Z;case Z:if(q=c.length){if(q>i&&(q=i),q>j&&(q=j),0===q)break a;r.arraySet(f,e,g,q,h),i-=q,g+=q,j-=q,h+=q,c.length-=q;break}c.mode=V;break;case $:for(;14>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if(c.nlen=(31&m)+257,m>>>=5,n-=5,c.ndist=(31&m)+1,m>>>=5,n-=5,c.ncode=(15&m)+4,m>>>=4,n-=4,c.nlen>286||c.ndist>30){a.msg="too many length or distance symbols",c.mode=lb;break}c.have=0,c.mode=_;case _:for(;c.have<c.ncode;){for(;3>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}c.lens[Cb[c.have++]]=7&m,m>>>=3,n-=3}for(;c.have<19;)c.lens[Cb[c.have++]]=0;if(c.lencode=c.lendyn,c.lenbits=7,yb={bits:c.lenbits},xb=v(w,c.lens,0,19,c.lencode,0,c.work,yb),c.lenbits=yb.bits,xb){a.msg="invalid code lengths set",c.mode=lb;break}c.have=0,c.mode=ab;case ab:for(;c.have<c.nlen+c.ndist;){for(;Ab=c.lencode[m&(1<<c.lenbits)-1],qb=Ab>>>24,rb=Ab>>>16&255,sb=65535&Ab,!(n>=qb);){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if(16>sb)m>>>=qb,n-=qb,c.lens[c.have++]=sb;else{if(16===sb){for(zb=qb+2;zb>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if(m>>>=qb,n-=qb,0===c.have){a.msg="invalid bit length repeat",c.mode=lb;break}wb=c.lens[c.have-1],q=3+(3&m),m>>>=2,n-=2}else if(17===sb){for(zb=qb+3;zb>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}m>>>=qb,n-=qb,wb=0,q=3+(7&m),m>>>=3,n-=3}else{for(zb=qb+7;zb>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}m>>>=qb,n-=qb,wb=0,q=11+(127&m),m>>>=7,n-=7}if(c.have+q>c.nlen+c.ndist){a.msg="invalid bit length repeat",c.mode=lb;break}for(;q--;)c.lens[c.have++]=wb}}if(c.mode===lb)break;if(0===c.lens[256]){a.msg="invalid code -- missing end-of-block",c.mode=lb;break}if(c.lenbits=9,yb={bits:c.lenbits},xb=v(x,c.lens,0,c.nlen,c.lencode,0,c.work,yb),c.lenbits=yb.bits,xb){a.msg="invalid literal/lengths set",c.mode=lb;break}if(c.distbits=6,c.distcode=c.distdyn,yb={bits:c.distbits},xb=v(y,c.lens,c.nlen,c.ndist,c.distcode,0,c.work,yb),c.distbits=yb.bits,xb){a.msg="invalid distances set",c.mode=lb;break}if(c.mode=bb,b===B)break a;case bb:c.mode=cb;case cb:if(i>=6&&j>=258){a.next_out=h,a.avail_out=j,a.next_in=g,a.avail_in=i,c.hold=m,c.bits=n,u(a,p),h=a.next_out,f=a.output,j=a.avail_out,g=a.next_in,e=a.input,i=a.avail_in,m=c.hold,n=c.bits,c.mode===V&&(c.back=-1);break}for(c.back=0;Ab=c.lencode[m&(1<<c.lenbits)-1],qb=Ab>>>24,rb=Ab>>>16&255,sb=65535&Ab,!(n>=qb);){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if(rb&&0===(240&rb)){for(tb=qb,ub=rb,vb=sb;Ab=c.lencode[vb+((m&(1<<tb+ub)-1)>>tb)],qb=Ab>>>24,rb=Ab>>>16&255,sb=65535&Ab,!(n>=tb+qb);){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}m>>>=tb,n-=tb,c.back+=tb}if(m>>>=qb,n-=qb,c.back+=qb,c.length=sb,0===rb){c.mode=hb;break}if(32&rb){c.back=-1,c.mode=V;break}if(64&rb){a.msg="invalid literal/length code",c.mode=lb;break}c.extra=15&rb,c.mode=db;case db:if(c.extra){for(zb=c.extra;zb>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}c.length+=m&(1<<c.extra)-1,m>>>=c.extra,n-=c.extra,c.back+=c.extra}c.was=c.length,c.mode=eb;case eb:for(;Ab=c.distcode[m&(1<<c.distbits)-1],qb=Ab>>>24,rb=Ab>>>16&255,sb=65535&Ab,!(n>=qb);){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if(0===(240&rb)){for(tb=qb,ub=rb,vb=sb;Ab=c.distcode[vb+((m&(1<<tb+ub)-1)>>tb)],qb=Ab>>>24,rb=Ab>>>16&255,sb=65535&Ab,!(n>=tb+qb);){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}m>>>=tb,n-=tb,c.back+=tb}if(m>>>=qb,n-=qb,c.back+=qb,64&rb){a.msg="invalid distance code",c.mode=lb;break}c.offset=sb,c.extra=15&rb,c.mode=fb;case fb:if(c.extra){for(zb=c.extra;zb>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}c.offset+=m&(1<<c.extra)-1,m>>>=c.extra,n-=c.extra,c.back+=c.extra}if(c.offset>c.dmax){a.msg="invalid distance too far back",c.mode=lb;break}c.mode=gb;case gb:if(0===j)break a;if(q=p-j,c.offset>q){if(q=c.offset-q,q>c.whave&&c.sane){a.msg="invalid distance too far back",c.mode=lb;break}q>c.wnext?(q-=c.wnext,ob=c.wsize-q):ob=c.wnext-q,q>c.length&&(q=c.length),pb=c.window}else pb=f,ob=h-c.offset,q=c.length;q>j&&(q=j),j-=q,c.length-=q;do f[h++]=pb[ob++];while(--q);0===c.length&&(c.mode=cb);break;case hb:if(0===j)break a;f[h++]=c.length,j--,c.mode=cb;break;case ib:if(c.wrap){for(;32>n;){if(0===i)break a;i--,m|=e[g++]<<n,n+=8}if(p-=j,a.total_out+=p,c.total+=p,p&&(a.adler=c.check=c.flags?t(c.check,f,p,h-p):s(c.check,f,p,h-p)),p=j,(c.flags?m:d(m))!==c.check){a.msg="incorrect data check",c.mode=lb;break}m=0,n=0}c.mode=jb;case jb:if(c.wrap&&c.flags){for(;32>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if(m!==(4294967295&c.total)){a.msg="incorrect length check",c.mode=lb;break}m=0,n=0}c.mode=kb;case kb:xb=D;break a;case lb:xb=G;break a;case mb:return H;case nb:default:return F}return a.next_out=h,a.avail_out=j,a.next_in=g,a.avail_in=i,c.hold=m,c.bits=n,(c.wsize||p!==a.avail_out&&c.mode<lb&&(c.mode<ib||b!==z))&&l(a,a.output,a.next_out,p-a.avail_out)?(c.mode=mb,H):(o-=a.avail_in,p-=a.avail_out,a.total_in+=o,a.total_out+=p,c.total+=p,c.wrap&&p&&(a.adler=c.check=c.flags?t(c.check,f,p,a.next_out-p):s(c.check,f,p,a.next_out-p)),a.data_type=c.bits+(c.last?64:0)+(c.mode===V?128:0)+(c.mode===bb||c.mode===Y?256:0),(0===o&&0===p||b===z)&&xb===C&&(xb=I),xb)}function n(a){if(!a||!a.state)return F;var b=a.state;return b.window&&(b.window=null),a.state=null,C}function o(a,b){var c;return a&&a.state?(c=a.state,0===(2&c.wrap)?F:(c.head=b,b.done=!1,C)):F}var p,q,r=a("../utils/common"),s=a("./adler32"),t=a("./crc32"),u=a("./inffast"),v=a("./inftrees"),w=0,x=1,y=2,z=4,A=5,B=6,C=0,D=1,E=2,F=-2,G=-3,H=-4,I=-5,J=8,K=1,L=2,M=3,N=4,O=5,P=6,Q=7,R=8,S=9,T=10,U=11,V=12,W=13,X=14,Y=15,Z=16,$=17,_=18,ab=19,bb=20,cb=21,db=22,eb=23,fb=24,gb=25,hb=26,ib=27,jb=28,kb=29,lb=30,mb=31,nb=32,ob=852,pb=592,qb=15,rb=qb,sb=!0;c.inflateReset=g,c.inflateReset2=h,c.inflateResetKeep=f,c.inflateInit=j,c.inflateInit2=i,c.inflate=m,c.inflateEnd=n,c.inflateGetHeader=o,c.inflateInfo="pako inflate (from Nodeca project)"},{"../utils/common":27,"./adler32":29,"./crc32":31,"./inffast":34,"./inftrees":36}],36:[function(a,b){"use strict";var c=a("../utils/common"),d=15,e=852,f=592,g=0,h=1,i=2,j=[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,0,0],k=[16,16,16,16,16,16,16,16,17,17,17,17,18,18,18,18,19,19,19,19,20,20,20,20,21,21,21,21,16,72,78],l=[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0],m=[16,16,16,16,17,17,18,18,19,19,20,20,21,21,22,22,23,23,24,24,25,25,26,26,27,27,28,28,29,29,64,64];b.exports=function(a,b,n,o,p,q,r,s){var t,u,v,w,x,y,z,A,B,C=s.bits,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=null,O=0,P=new c.Buf16(d+1),Q=new c.Buf16(d+1),R=null,S=0;for(D=0;d>=D;D++)P[D]=0;for(E=0;o>E;E++)P[b[n+E]]++;for(H=C,G=d;G>=1&&0===P[G];G--);if(H>G&&(H=G),0===G)return p[q++]=20971520,p[q++]=20971520,s.bits=1,0;for(F=1;G>F&&0===P[F];F++);for(F>H&&(H=F),K=1,D=1;d>=D;D++)if(K<<=1,K-=P[D],0>K)return-1;if(K>0&&(a===g||1!==G))return-1;for(Q[1]=0,D=1;d>D;D++)Q[D+1]=Q[D]+P[D];for(E=0;o>E;E++)0!==b[n+E]&&(r[Q[b[n+E]]++]=E);if(a===g?(N=R=r,y=19):a===h?(N=j,O-=257,R=k,S-=257,y=256):(N=l,R=m,y=-1),M=0,E=0,D=F,x=q,I=H,J=0,v=-1,L=1<<H,w=L-1,a===h&&L>e||a===i&&L>f)return 1;for(var T=0;;){T++,z=D-J,r[E]<y?(A=0,B=r[E]):r[E]>y?(A=R[S+r[E]],B=N[O+r[E]]):(A=96,B=0),t=1<<D-J,u=1<<I,F=u;do u-=t,p[x+(M>>J)+u]=z<<24|A<<16|B|0;while(0!==u);for(t=1<<D-1;M&t;)t>>=1;if(0!==t?(M&=t-1,M+=t):M=0,E++,0===--P[D]){if(D===G)break;D=b[n+r[E]]}if(D>H&&(M&w)!==v){for(0===J&&(J=H),x+=F,I=D-J,K=1<<I;G>I+J&&(K-=P[I+J],!(0>=K));)I++,K<<=1;if(L+=1<<I,a===h&&L>e||a===i&&L>f)return 1;v=M&w,p[v]=H<<24|I<<16|x-q|0}}return 0!==M&&(p[x+M]=D-J<<24|64<<16|0),s.bits=H,0}},{"../utils/common":27}],37:[function(a,b){"use strict";b.exports={2:"need dictionary",1:"stream end",0:"","-1":"file error","-2":"stream error","-3":"data error","-4":"insufficient memory","-5":"buffer error","-6":"incompatible version"}},{}],38:[function(a,b,c){"use strict";function d(a){for(var b=a.length;--b>=0;)a[b]=0}function e(a){return 256>a?gb[a]:gb[256+(a>>>7)]}function f(a,b){a.pending_buf[a.pending++]=255&b,a.pending_buf[a.pending++]=b>>>8&255}function g(a,b,c){a.bi_valid>V-c?(a.bi_buf|=b<<a.bi_valid&65535,f(a,a.bi_buf),a.bi_buf=b>>V-a.bi_valid,a.bi_valid+=c-V):(a.bi_buf|=b<<a.bi_valid&65535,a.bi_valid+=c)}function h(a,b,c){g(a,c[2*b],c[2*b+1])}function i(a,b){var c=0;do c|=1&a,a>>>=1,c<<=1;while(--b>0);return c>>>1}function j(a){16===a.bi_valid?(f(a,a.bi_buf),a.bi_buf=0,a.bi_valid=0):a.bi_valid>=8&&(a.pending_buf[a.pending++]=255&a.bi_buf,a.bi_buf>>=8,a.bi_valid-=8)}function k(a,b){var c,d,e,f,g,h,i=b.dyn_tree,j=b.max_code,k=b.stat_desc.static_tree,l=b.stat_desc.has_stree,m=b.stat_desc.extra_bits,n=b.stat_desc.extra_base,o=b.stat_desc.max_length,p=0;for(f=0;U>=f;f++)a.bl_count[f]=0;for(i[2*a.heap[a.heap_max]+1]=0,c=a.heap_max+1;T>c;c++)d=a.heap[c],f=i[2*i[2*d+1]+1]+1,f>o&&(f=o,p++),i[2*d+1]=f,d>j||(a.bl_count[f]++,g=0,d>=n&&(g=m[d-n]),h=i[2*d],a.opt_len+=h*(f+g),l&&(a.static_len+=h*(k[2*d+1]+g)));if(0!==p){do{for(f=o-1;0===a.bl_count[f];)f--;a.bl_count[f]--,a.bl_count[f+1]+=2,a.bl_count[o]--,p-=2}while(p>0);for(f=o;0!==f;f--)for(d=a.bl_count[f];0!==d;)e=a.heap[--c],e>j||(i[2*e+1]!==f&&(a.opt_len+=(f-i[2*e+1])*i[2*e],i[2*e+1]=f),d--)}}function l(a,b,c){var d,e,f=new Array(U+1),g=0;for(d=1;U>=d;d++)f[d]=g=g+c[d-1]<<1;for(e=0;b>=e;e++){var h=a[2*e+1];0!==h&&(a[2*e]=i(f[h]++,h))}}function m(){var a,b,c,d,e,f=new Array(U+1);for(c=0,d=0;O-1>d;d++)for(ib[d]=c,a=0;a<1<<_[d];a++)hb[c++]=d;for(hb[c-1]=d,e=0,d=0;16>d;d++)for(jb[d]=e,a=0;a<1<<ab[d];a++)gb[e++]=d;for(e>>=7;R>d;d++)for(jb[d]=e<<7,a=0;a<1<<ab[d]-7;a++)gb[256+e++]=d;for(b=0;U>=b;b++)f[b]=0;for(a=0;143>=a;)eb[2*a+1]=8,a++,f[8]++;for(;255>=a;)eb[2*a+1]=9,a++,f[9]++;for(;279>=a;)eb[2*a+1]=7,a++,f[7]++;for(;287>=a;)eb[2*a+1]=8,a++,f[8]++;for(l(eb,Q+1,f),a=0;R>a;a++)fb[2*a+1]=5,fb[2*a]=i(a,5);kb=new nb(eb,_,P+1,Q,U),lb=new nb(fb,ab,0,R,U),mb=new nb(new Array(0),bb,0,S,W)}function n(a){var b;for(b=0;Q>b;b++)a.dyn_ltree[2*b]=0;for(b=0;R>b;b++)a.dyn_dtree[2*b]=0;for(b=0;S>b;b++)a.bl_tree[2*b]=0;a.dyn_ltree[2*X]=1,a.opt_len=a.static_len=0,a.last_lit=a.matches=0}function o(a){a.bi_valid>8?f(a,a.bi_buf):a.bi_valid>0&&(a.pending_buf[a.pending++]=a.bi_buf),a.bi_buf=0,a.bi_valid=0}function p(a,b,c,d){o(a),d&&(f(a,c),f(a,~c)),E.arraySet(a.pending_buf,a.window,b,c,a.pending),a.pending+=c}function q(a,b,c,d){var e=2*b,f=2*c;return a[e]<a[f]||a[e]===a[f]&&d[b]<=d[c]}function r(a,b,c){for(var d=a.heap[c],e=c<<1;e<=a.heap_len&&(e<a.heap_len&&q(b,a.heap[e+1],a.heap[e],a.depth)&&e++,!q(b,d,a.heap[e],a.depth));)a.heap[c]=a.heap[e],c=e,e<<=1;a.heap[c]=d}function s(a,b,c){var d,f,i,j,k=0;if(0!==a.last_lit)do d=a.pending_buf[a.d_buf+2*k]<<8|a.pending_buf[a.d_buf+2*k+1],f=a.pending_buf[a.l_buf+k],k++,0===d?h(a,f,b):(i=hb[f],h(a,i+P+1,b),j=_[i],0!==j&&(f-=ib[i],g(a,f,j)),d--,i=e(d),h(a,i,c),j=ab[i],0!==j&&(d-=jb[i],g(a,d,j)));while(k<a.last_lit);h(a,X,b)}function t(a,b){var c,d,e,f=b.dyn_tree,g=b.stat_desc.static_tree,h=b.stat_desc.has_stree,i=b.stat_desc.elems,j=-1;for(a.heap_len=0,a.heap_max=T,c=0;i>c;c++)0!==f[2*c]?(a.heap[++a.heap_len]=j=c,a.depth[c]=0):f[2*c+1]=0;for(;a.heap_len<2;)e=a.heap[++a.heap_len]=2>j?++j:0,f[2*e]=1,a.depth[e]=0,a.opt_len--,h&&(a.static_len-=g[2*e+1]);for(b.max_code=j,c=a.heap_len>>1;c>=1;c--)r(a,f,c);e=i;do c=a.heap[1],a.heap[1]=a.heap[a.heap_len--],r(a,f,1),d=a.heap[1],a.heap[--a.heap_max]=c,a.heap[--a.heap_max]=d,f[2*e]=f[2*c]+f[2*d],a.depth[e]=(a.depth[c]>=a.depth[d]?a.depth[c]:a.depth[d])+1,f[2*c+1]=f[2*d+1]=e,a.heap[1]=e++,r(a,f,1);while(a.heap_len>=2);a.heap[--a.heap_max]=a.heap[1],k(a,b),l(f,j,a.bl_count)}function u(a,b,c){var d,e,f=-1,g=b[1],h=0,i=7,j=4;for(0===g&&(i=138,j=3),b[2*(c+1)+1]=65535,d=0;c>=d;d++)e=g,g=b[2*(d+1)+1],++h<i&&e===g||(j>h?a.bl_tree[2*e]+=h:0!==e?(e!==f&&a.bl_tree[2*e]++,a.bl_tree[2*Y]++):10>=h?a.bl_tree[2*Z]++:a.bl_tree[2*$]++,h=0,f=e,0===g?(i=138,j=3):e===g?(i=6,j=3):(i=7,j=4))}function v(a,b,c){var d,e,f=-1,i=b[1],j=0,k=7,l=4;for(0===i&&(k=138,l=3),d=0;c>=d;d++)if(e=i,i=b[2*(d+1)+1],!(++j<k&&e===i)){if(l>j){do h(a,e,a.bl_tree);while(0!==--j)}else 0!==e?(e!==f&&(h(a,e,a.bl_tree),j--),h(a,Y,a.bl_tree),g(a,j-3,2)):10>=j?(h(a,Z,a.bl_tree),g(a,j-3,3)):(h(a,$,a.bl_tree),g(a,j-11,7));j=0,f=e,0===i?(k=138,l=3):e===i?(k=6,l=3):(k=7,l=4)}}function w(a){var b;for(u(a,a.dyn_ltree,a.l_desc.max_code),u(a,a.dyn_dtree,a.d_desc.max_code),t(a,a.bl_desc),b=S-1;b>=3&&0===a.bl_tree[2*cb[b]+1];b--);return a.opt_len+=3*(b+1)+5+5+4,b}function x(a,b,c,d){var e;for(g(a,b-257,5),g(a,c-1,5),g(a,d-4,4),e=0;d>e;e++)g(a,a.bl_tree[2*cb[e]+1],3);v(a,a.dyn_ltree,b-1),v(a,a.dyn_dtree,c-1)}function y(a){var b,c=4093624447;for(b=0;31>=b;b++,c>>>=1)if(1&c&&0!==a.dyn_ltree[2*b])return G;if(0!==a.dyn_ltree[18]||0!==a.dyn_ltree[20]||0!==a.dyn_ltree[26])return H;for(b=32;P>b;b++)if(0!==a.dyn_ltree[2*b])return H;return G}function z(a){pb||(m(),pb=!0),a.l_desc=new ob(a.dyn_ltree,kb),a.d_desc=new ob(a.dyn_dtree,lb),a.bl_desc=new ob(a.bl_tree,mb),a.bi_buf=0,a.bi_valid=0,n(a)}function A(a,b,c,d){g(a,(J<<1)+(d?1:0),3),p(a,b,c,!0)}function B(a){g(a,K<<1,3),h(a,X,eb),j(a)}function C(a,b,c,d){var e,f,h=0;a.level>0?(a.strm.data_type===I&&(a.strm.data_type=y(a)),t(a,a.l_desc),t(a,a.d_desc),h=w(a),e=a.opt_len+3+7>>>3,f=a.static_len+3+7>>>3,e>=f&&(e=f)):e=f=c+5,e>=c+4&&-1!==b?A(a,b,c,d):a.strategy===F||f===e?(g(a,(K<<1)+(d?1:0),3),s(a,eb,fb)):(g(a,(L<<1)+(d?1:0),3),x(a,a.l_desc.max_code+1,a.d_desc.max_code+1,h+1),s(a,a.dyn_ltree,a.dyn_dtree)),n(a),d&&o(a)}function D(a,b,c){return a.pending_buf[a.d_buf+2*a.last_lit]=b>>>8&255,a.pending_buf[a.d_buf+2*a.last_lit+1]=255&b,a.pending_buf[a.l_buf+a.last_lit]=255&c,a.last_lit++,0===b?a.dyn_ltree[2*c]++:(a.matches++,b--,a.dyn_ltree[2*(hb[c]+P+1)]++,a.dyn_dtree[2*e(b)]++),a.last_lit===a.lit_bufsize-1}var E=a("../utils/common"),F=4,G=0,H=1,I=2,J=0,K=1,L=2,M=3,N=258,O=29,P=256,Q=P+1+O,R=30,S=19,T=2*Q+1,U=15,V=16,W=7,X=256,Y=16,Z=17,$=18,_=[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0],ab=[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13],bb=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7],cb=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],db=512,eb=new Array(2*(Q+2));d(eb);var fb=new Array(2*R);d(fb);var gb=new Array(db);d(gb);var hb=new Array(N-M+1);d(hb);var ib=new Array(O);d(ib);var jb=new Array(R);d(jb);var kb,lb,mb,nb=function(a,b,c,d,e){this.static_tree=a,this.extra_bits=b,this.extra_base=c,this.elems=d,this.max_length=e,this.has_stree=a&&a.length},ob=function(a,b){this.dyn_tree=a,this.max_code=0,this.stat_desc=b},pb=!1;c._tr_init=z,c._tr_stored_block=A,c._tr_flush_block=C,c._tr_tally=D,c._tr_align=B},{"../utils/common":27}],39:[function(a,b){"use strict";function c(){this.input=null,this.next_in=0,this.avail_in=0,this.total_in=0,this.output=null,this.next_out=0,this.avail_out=0,this.total_out=0,this.msg="",this.state=null,this.data_type=2,this.adler=0}b.exports=c},{}]},{},[9])(9)});'use strict';if(tr.isVinn){global.window={};}'use strict';if(tr.isVinn){global.JSZip=global.window.JSZip;global.window=undefined;}else if(tr.isNode){var jsZipAbsPath=HTMLImportsLoader.hrefToAbsolutePath('/jszip.min.js');var jsZipModule=require(jsZipAbsPath);global.JSZip=jsZipModule;}'use strict';tr.exportTo('tr.e.importer',function(){var GZIP_MEMBER_HEADER_ID_SIZE=3;var GZIP_HEADER_ID1=0x1f;var GZIP_HEADER_ID2=0x8b;var GZIP_DEFLATE_COMPRESSION=8;function GzipImporter(model,eventData){if(typeof(eventData)==='string'||eventData instanceof String){eventData=JSZip.utils.transformTo('uint8array',eventData);}else if(eventData instanceof ArrayBuffer){eventData=new Uint8Array(eventData);}else{throw new Error('Unknown gzip data format');}
+this.model_=model;this.gzipData_=eventData;}
+GzipImporter.canImport=function(eventData){var header;if(eventData instanceof ArrayBuffer){header=new Uint8Array(eventData.slice(0,GZIP_MEMBER_HEADER_ID_SIZE));}else if(typeof(eventData)==='string'||eventData instanceof String){header=eventData.substring(0,GZIP_MEMBER_HEADER_ID_SIZE);header=JSZip.utils.transformTo('uint8array',header);}else{return false;}
+return header[0]===GZIP_HEADER_ID1&&header[1]===GZIP_HEADER_ID2&&header[2]===GZIP_DEFLATE_COMPRESSION;};GzipImporter.inflateGzipData_=function(data){var position=0;function getByte(){if(position>=data.length){throw new Error('Unexpected end of gzip data');}
+return data[position++];}
 function getWord(){var low=getByte();var high=getByte();return(high<<8)+low;}
 function skipBytes(amount){position+=amount;}
-function skipZeroTerminatedString(){while(getByte()!=0){}}
-var id1=getByte();var id2=getByte();if(id1!==GZIP_HEADER_ID1||id2!==GZIP_HEADER_ID2)
-throw new Error('Not gzip data');var compression_method=getByte();if(compression_method!==GZIP_DEFLATE_COMPRESSION)
-throw new Error('Unsupported compression method: '+compression_method);var flags=getByte();var have_header_crc=flags&(1<<1);var have_extra_fields=flags&(1<<2);var have_file_name=flags&(1<<3);var have_comment=flags&(1<<4);skipBytes(4+1+1);if(have_extra_fields){var bytes_to_skip=getWord();skipBytes(bytes_to_skip);}
-if(have_file_name)
-skipZeroTerminatedString();if(have_comment)
-skipZeroTerminatedString();if(have_header_crc)
-getWord();var inflated_data=JSZip.compressions['DEFLATE'].uncompress(data.subarray(position));var string=GzipImporter.transformToString(inflated_data);if(inflated_data.length>0&&string.length===0){throw new RangeError('Inflated gzip data too long to fit into a string'+' ('+inflated_data.length+').');}
+function skipZeroTerminatedString(){while(getByte()!==0){}}
+var id1=getByte();var id2=getByte();if(id1!==GZIP_HEADER_ID1||id2!==GZIP_HEADER_ID2){throw new Error('Not gzip data');}
+var compressionMethod=getByte();if(compressionMethod!==GZIP_DEFLATE_COMPRESSION){throw new Error('Unsupported compression method: '+compressionMethod);}
+var flags=getByte();var haveHeaderCrc=flags&(1<<1);var haveExtraFields=flags&(1<<2);var haveFileName=flags&(1<<3);var haveComment=flags&(1<<4);skipBytes(4+1+1);if(haveExtraFields){var bytesToSkip=getWord();skipBytes(bytesToSkip);}
+if(haveFileName)skipZeroTerminatedString();if(haveComment)skipZeroTerminatedString();if(haveHeaderCrc)getWord();var inflatedData=JSZip.compressions['DEFLATE'].uncompress(data.subarray(position));var string=GzipImporter.transformToString(inflatedData);if(inflatedData.length>0&&string.length===0){throw new RangeError('Inflated gzip data too long to fit into a string'+' ('+inflatedData.length+').');}
 return string;};GzipImporter.transformToString=function(data){if(typeof TextDecoder==='undefined'){return JSZip.utils.transformTo('string',data);}
-var type=JSZip.utils.getTypeOf(data);if(type==='string')
-return data;if(type==='array'){data=new Uint8Array(data);}
-var decoder=new TextDecoder('utf-8');return decoder.decode(data);};GzipImporter.prototype={__proto__:tr.importer.Importer.prototype,get importerName(){return'GzipImporter';},isTraceDataContainer:function(){return true;},extractSubtraces:function(){var eventData=GzipImporter.inflateGzipData_(this.gzipData_);return eventData?[eventData]:[];}};tr.importer.Importer.register(GzipImporter);return{GzipImporter:GzipImporter};});'use strict';tr.exportTo('tr.importer',function(){function SimpleLineReader(text){this.lines_=text.split('\n');this.curLine_=0;this.savedLines_=undefined;}
-SimpleLineReader.prototype={advanceToLineMatching:function(regex){for(;this.curLine_<this.lines_.length;this.curLine_++){var line=this.lines_[this.curLine_];if(this.savedLines_!==undefined)
-this.savedLines_.push(line);if(regex.test(line))
-return true;}
-return false;},get curLineNumber(){return this.curLine_;},beginSavingLines:function(){this.savedLines_=[];},endSavingLinesAndGetResult:function(){var tmp=this.savedLines_;this.savedLines_=undefined;return tmp;}};return{SimpleLineReader:SimpleLineReader};});'use strict';tr.exportTo('tr.e.importer',function(){function Trace2HTMLImporter(model,events){this.importPriority=0;}
-Trace2HTMLImporter.subtraces_=[];function _extractEventsFromHTML(text){Trace2HTMLImporter.subtraces_=[];var r=new tr.importer.SimpleLineReader(text);while(true){if(!r.advanceToLineMatching(new RegExp('^<\s*script id="viewer-data" '+'type="(application\/json|text\/plain)">$')))
-break;r.beginSavingLines();if(!r.advanceToLineMatching(/^<\/\s*script>$/))
-return;var raw_events=r.endSavingLinesAndGetResult();raw_events=raw_events.slice(1,raw_events.length-1);var data64=raw_events.join('\n');var buffer=new ArrayBuffer(tr.b.Base64.getDecodedBufferLength(data64));var len=tr.b.Base64.DecodeToTypedArray(data64,new DataView(buffer));Trace2HTMLImporter.subtraces_.push(buffer.slice(0,len));}}
-function _canImportFromHTML(text){if(/^<!DOCTYPE html>/.test(text)===false)
-return false;_extractEventsFromHTML(text);if(Trace2HTMLImporter.subtraces_.length===0)
-return false;return true;}
-Trace2HTMLImporter.canImport=function(events){return _canImportFromHTML(events);};Trace2HTMLImporter.prototype={__proto__:tr.importer.Importer.prototype,get importerName(){return'Trace2HTMLImporter';},isTraceDataContainer:function(){return true;},extractSubtraces:function(){return Trace2HTMLImporter.subtraces_;},importEvents:function(){}};tr.importer.Importer.register(Trace2HTMLImporter);return{Trace2HTMLImporter:Trace2HTMLImporter};});'use strict';tr.exportTo('tr.e.importer.v8',function(){function SplayTree(){};SplayTree.prototype.root_=null;SplayTree.prototype.isEmpty=function(){return!this.root_;};SplayTree.prototype.insert=function(key,value){if(this.isEmpty()){this.root_=new SplayTree.Node(key,value);return;}
-this.splay_(key);if(this.root_.key==key){return;}
+var type=JSZip.utils.getTypeOf(data);if(type==='string'){return data;}
+if(type==='array'){data=new Uint8Array(data);}
+var decoder=new TextDecoder('utf-8');return decoder.decode(data);};GzipImporter.prototype={__proto__:tr.importer.Importer.prototype,get importerName(){return'GzipImporter';},isTraceDataContainer:function(){return true;},extractSubtraces:function(){var eventData=GzipImporter.inflateGzipData_(this.gzipData_);return eventData?[eventData]:[];}};tr.importer.Importer.register(GzipImporter);return{GzipImporter,};});'use strict';tr.exportTo('tr.importer',function(){function SimpleLineReader(text){this.lines_=text.split('\n');this.curLine_=0;this.savedLines_=undefined;}
+SimpleLineReader.prototype={advanceToLineMatching:function(regex){for(;this.curLine_<this.lines_.length;this.curLine_++){var line=this.lines_[this.curLine_];if(this.savedLines_!==undefined){this.savedLines_.push(line);}
+if(regex.test(line))return true;}
+return false;},get curLineNumber(){return this.curLine_;},beginSavingLines:function(){this.savedLines_=[];},endSavingLinesAndGetResult:function(){var tmp=this.savedLines_;this.savedLines_=undefined;return tmp;}};return{SimpleLineReader,};});'use strict';tr.exportTo('tr.e.importer',function(){function Trace2HTMLImporter(model,events){this.importPriority=0;}
+Trace2HTMLImporter.subtraces_=[];function _extractEventsFromHTML(text){Trace2HTMLImporter.subtraces_=[];var r=new tr.importer.SimpleLineReader(text);while(true){if(!r.advanceToLineMatching(new RegExp('^<\s*script id="viewer-data" '+'type="(application\/json|text\/plain)">$'))){break;}
+r.beginSavingLines();if(!r.advanceToLineMatching(/^<\/\s*script>$/))return;var rawEvents=r.endSavingLinesAndGetResult();rawEvents=rawEvents.slice(1,rawEvents.length-1);var data64=rawEvents.join('\n');var buffer=new ArrayBuffer(tr.b.Base64.getDecodedBufferLength(data64));var len=tr.b.Base64.DecodeToTypedArray(data64,new DataView(buffer));Trace2HTMLImporter.subtraces_.push(buffer.slice(0,len));}}
+function _canImportFromHTML(text){if(!/^<!DOCTYPE html>/.test(text))return false;_extractEventsFromHTML(text);if(Trace2HTMLImporter.subtraces_.length===0)return false;return true;}
+Trace2HTMLImporter.canImport=function(events){return _canImportFromHTML(events);};Trace2HTMLImporter.prototype={__proto__:tr.importer.Importer.prototype,get importerName(){return'Trace2HTMLImporter';},isTraceDataContainer:function(){return true;},extractSubtraces:function(){return Trace2HTMLImporter.subtraces_;},importEvents:function(){}};tr.importer.Importer.register(Trace2HTMLImporter);return{Trace2HTMLImporter,};});'use strict';tr.exportTo('tr.e.importer.v8',function(){function SplayTree(){}
+SplayTree.prototype.root_=null;SplayTree.prototype.isEmpty=function(){return!this.root_;};SplayTree.prototype.insert=function(key,value){if(this.isEmpty()){this.root_=new SplayTree.Node(key,value);return;}
+this.splay_(key);if(this.root_.key===key){return;}
 var node=new SplayTree.Node(key,value);if(key>this.root_.key){node.left=this.root_;node.right=this.root_.right;this.root_.right=null;}else{node.right=this.root_;node.left=this.root_.left;this.root_.left=null;}
 this.root_=node;};SplayTree.prototype.remove=function(key){if(this.isEmpty()){throw Error('Key not found: '+key);}
-this.splay_(key);if(this.root_.key!=key){throw Error('Key not found: '+key);}
+this.splay_(key);if(this.root_.key!==key){throw Error('Key not found: '+key);}
 var removed=this.root_;if(!this.root_.left){this.root_=this.root_.right;}else{var right=this.root_.right;this.root_=this.root_.left;this.splay_(key);this.root_.right=right;}
-return removed;};SplayTree.prototype.find=function(key){if(this.isEmpty()){return null;}
-this.splay_(key);return this.root_.key==key?this.root_:null;};SplayTree.prototype.findMin=function(){if(this.isEmpty()){return null;}
-var current=this.root_;while(current.left){current=current.left;}
-return current;};SplayTree.prototype.findMax=function(opt_startNode){if(this.isEmpty()){return null;}
-var current=opt_startNode||this.root_;while(current.right){current=current.right;}
-return current;};SplayTree.prototype.findGreatestLessThan=function(key){if(this.isEmpty()){return null;}
-this.splay_(key);if(this.root_.key<=key){return this.root_;}else if(this.root_.left){return this.findMax(this.root_.left);}else{return null;}};SplayTree.prototype.exportKeysAndValues=function(){var result=[];this.traverse_(function(node){result.push([node.key,node.value]);});return result;};SplayTree.prototype.exportValues=function(){var result=[];this.traverse_(function(node){result.push(node.value);});return result;};SplayTree.prototype.splay_=function(key){if(this.isEmpty()){return;}
-var dummy,left,right;dummy=left=right=new SplayTree.Node(null,null);var current=this.root_;while(true){if(key<current.key){if(!current.left){break;}
+return removed;};SplayTree.prototype.find=function(key){if(this.isEmpty())return null;this.splay_(key);return this.root_.key===key?this.root_:null;};SplayTree.prototype.findMin=function(){if(this.isEmpty())return null;var current=this.root_;while(current.left){current=current.left;}
+return current;};SplayTree.prototype.findMax=function(opt_startNode){if(this.isEmpty())return null;var current=opt_startNode||this.root_;while(current.right){current=current.right;}
+return current;};SplayTree.prototype.findGreatestLessThan=function(key){if(this.isEmpty())return null;this.splay_(key);if(this.root_.key<=key){return this.root_;}
+if(this.root_.left){return this.findMax(this.root_.left);}
+return null;};SplayTree.prototype.exportKeysAndValues=function(){var result=[];this.traverse_(function(node){result.push([node.key,node.value]);});return result;};SplayTree.prototype.exportValues=function(){var result=[];this.traverse_(function(node){result.push(node.value);});return result;};SplayTree.prototype.splay_=function(key){if(this.isEmpty())return;var dummy;var left;var right;dummy=left=right=new SplayTree.Node(null,null);var current=this.root_;while(true){if(key<current.key){if(!current.left){break;}
 if(key<current.left.key){var tmp=current.left;current.left=tmp.right;tmp.right=current;current=tmp;if(!current.left){break;}}
 right.left=current;right=current;current=current.left;}else if(key>current.key){if(!current.right){break;}
 if(key>current.right.key){var tmp=current.right;current.right=tmp.left;tmp.left=current;current=tmp;if(!current.right){break;}}
 left.right=current;left=current;current=current.right;}else{break;}}
-left.right=current.left;right.left=current.right;current.left=dummy.right;current.right=dummy.left;this.root_=current;};SplayTree.prototype.traverse_=function(f){var nodesToVisit=[this.root_];while(nodesToVisit.length>0){var node=nodesToVisit.shift();if(node==null){continue;}
-f(node);nodesToVisit.push(node.left);nodesToVisit.push(node.right);}};SplayTree.Node=function(key,value){this.key=key;this.value=value;};SplayTree.Node.prototype.left=null;SplayTree.Node.prototype.right=null;return{SplayTree:SplayTree};});'use strict';tr.exportTo('tr.e.importer.v8',function(){function CodeMap(){this.dynamics_=new tr.e.importer.v8.SplayTree();this.dynamicsNameGen_=new tr.e.importer.v8.CodeMap.NameGenerator();this.statics_=new tr.e.importer.v8.SplayTree();this.libraries_=new tr.e.importer.v8.SplayTree();this.pages_=[];};CodeMap.PAGE_ALIGNMENT=12;CodeMap.PAGE_SIZE=1<<CodeMap.PAGE_ALIGNMENT;CodeMap.prototype.addCode=function(start,codeEntry){this.deleteAllCoveredNodes_(this.dynamics_,start,start+codeEntry.size);this.dynamics_.insert(start,codeEntry);};CodeMap.prototype.moveCode=function(from,to){var removedNode=this.dynamics_.remove(from);this.deleteAllCoveredNodes_(this.dynamics_,to,to+removedNode.value.size);this.dynamics_.insert(to,removedNode.value);};CodeMap.prototype.deleteCode=function(start){var removedNode=this.dynamics_.remove(start);};CodeMap.prototype.addLibrary=function(start,codeEntry){this.markPages_(start,start+codeEntry.size);this.libraries_.insert(start,codeEntry);};CodeMap.prototype.addStaticCode=function(start,codeEntry){this.statics_.insert(start,codeEntry);};CodeMap.prototype.markPages_=function(start,end){for(var addr=start;addr<=end;addr+=CodeMap.PAGE_SIZE){this.pages_[addr>>>CodeMap.PAGE_ALIGNMENT]=1;}};CodeMap.prototype.deleteAllCoveredNodes_=function(tree,start,end){var to_delete=[];var addr=end-1;while(addr>=start){var node=tree.findGreatestLessThan(addr);if(!node)break;var start2=node.key,end2=start2+node.value.size;if(start2<end&&start<end2)to_delete.push(start2);addr=start2-1;}
-for(var i=0,l=to_delete.length;i<l;++i)tree.remove(to_delete[i]);};CodeMap.prototype.isAddressBelongsTo_=function(addr,node){return addr>=node.key&&addr<(node.key+node.value.size);};CodeMap.prototype.findInTree_=function(tree,addr){var node=tree.findGreatestLessThan(addr);return node&&this.isAddressBelongsTo_(addr,node)?node.value:null;};CodeMap.prototype.findEntryInLibraries=function(addr){var pageAddr=addr>>>CodeMap.PAGE_ALIGNMENT;if(pageAddr in this.pages_)
-return this.findInTree_(this.libraries_,addr);return undefined;};CodeMap.prototype.findEntry=function(addr){var pageAddr=addr>>>CodeMap.PAGE_ALIGNMENT;if(pageAddr in this.pages_){return this.findInTree_(this.statics_,addr)||this.findInTree_(this.libraries_,addr);}
-var min=this.dynamics_.findMin();var max=this.dynamics_.findMax();if(max!=null&&addr<(max.key+max.value.size)&&addr>=min.key){var dynaEntry=this.findInTree_(this.dynamics_,addr);if(dynaEntry==null)return null;if(!dynaEntry.nameUpdated_){dynaEntry.name=this.dynamicsNameGen_.getName(dynaEntry.name);dynaEntry.nameUpdated_=true;}
+left.right=current.left;right.left=current.right;current.left=dummy.right;current.right=dummy.left;this.root_=current;};SplayTree.prototype.traverse_=function(f){var nodesToVisit=[this.root_];while(nodesToVisit.length>0){var node=nodesToVisit.shift();if(node===null)continue;f(node);nodesToVisit.push(node.left);nodesToVisit.push(node.right);}};SplayTree.Node=function(key,value){this.key=key;this.value=value;};SplayTree.Node.prototype.left=null;SplayTree.Node.prototype.right=null;return{SplayTree,};});'use strict';tr.exportTo('tr.e.importer.v8',function(){function CodeMap(){this.dynamics_=new tr.e.importer.v8.SplayTree();this.dynamicsNameGen_=new tr.e.importer.v8.CodeMap.NameGenerator();this.statics_=new tr.e.importer.v8.SplayTree();this.libraries_=new tr.e.importer.v8.SplayTree();this.pages_=[];}
+CodeMap.PAGE_ALIGNMENT=12;CodeMap.PAGE_SIZE=1<<CodeMap.PAGE_ALIGNMENT;CodeMap.prototype.addCode=function(start,codeEntry){this.deleteAllCoveredNodes_(this.dynamics_,start,start+codeEntry.size);this.dynamics_.insert(start,codeEntry);};CodeMap.prototype.moveCode=function(from,to){var removedNode=this.dynamics_.remove(from);this.deleteAllCoveredNodes_(this.dynamics_,to,to+removedNode.value.size);this.dynamics_.insert(to,removedNode.value);};CodeMap.prototype.deleteCode=function(start){var removedNode=this.dynamics_.remove(start);};CodeMap.prototype.addLibrary=function(start,codeEntry){this.markPages_(start,start+codeEntry.size);this.libraries_.insert(start,codeEntry);};CodeMap.prototype.addStaticCode=function(start,codeEntry){this.statics_.insert(start,codeEntry);};CodeMap.prototype.markPages_=function(start,end){for(var addr=start;addr<=end;addr+=CodeMap.PAGE_SIZE){this.pages_[addr>>>CodeMap.PAGE_ALIGNMENT]=1;}};CodeMap.prototype.deleteAllCoveredNodes_=function(tree,start,end){var toDelete=[];var addr=end-1;while(addr>=start){var node=tree.findGreatestLessThan(addr);if(!node)break;var start2=node.key;var end2=start2+node.value.size;if(start2<end&&start<end2)toDelete.push(start2);addr=start2-1;}
+for(var i=0,l=toDelete.length;i<l;++i)tree.remove(toDelete[i]);};CodeMap.prototype.isAddressBelongsTo_=function(addr,node){return addr>=node.key&&addr<(node.key+node.value.size);};CodeMap.prototype.findInTree_=function(tree,addr){var node=tree.findGreatestLessThan(addr);return node&&this.isAddressBelongsTo_(addr,node)?node.value:null;};CodeMap.prototype.findEntryInLibraries=function(addr){var pageAddr=addr>>>CodeMap.PAGE_ALIGNMENT;if(pageAddr in this.pages_){return this.findInTree_(this.libraries_,addr);}
+return undefined;};CodeMap.prototype.findEntry=function(addr){var pageAddr=addr>>>CodeMap.PAGE_ALIGNMENT;if(pageAddr in this.pages_){return this.findInTree_(this.statics_,addr)||this.findInTree_(this.libraries_,addr);}
+var min=this.dynamics_.findMin();var max=this.dynamics_.findMax();if(max!==null&&addr<(max.key+max.value.size)&&addr>=min.key){var dynaEntry=this.findInTree_(this.dynamics_,addr);if(dynaEntry===null)return null;if(!dynaEntry.nameUpdated_){dynaEntry.name=this.dynamicsNameGen_.getName(dynaEntry.name);dynaEntry.nameUpdated_=true;}
 return dynaEntry;}
 return null;};CodeMap.prototype.findDynamicEntryByStartAddress=function(addr){var node=this.dynamics_.find(addr);return node?node.value:null;};CodeMap.prototype.getAllDynamicEntries=function(){return this.dynamics_.exportValues();};CodeMap.prototype.getAllDynamicEntriesWithAddresses=function(){return this.dynamics_.exportKeysAndValues();};CodeMap.prototype.getAllStaticEntries=function(){return this.statics_.exportValues();};CodeMap.prototype.getAllLibrariesEntries=function(){return this.libraries_.exportValues();};CodeMap.CodeState={COMPILED:0,OPTIMIZABLE:1,OPTIMIZED:2};CodeMap.CodeEntry=function(size,opt_name,opt_type){this.id=tr.b.GUID.allocateSimple();this.size=size;this.name_=opt_name||'';this.type=opt_type||'';this.nameUpdated_=false;};CodeMap.CodeEntry.prototype={__proto__:Object.prototype,get name(){return this.name_;},set name(value){this.name_=value;},toString:function(){this.name_+': '+this.size.toString(16);}};CodeMap.CodeEntry.TYPE={SHARED_LIB:'SHARED_LIB',CPP:'CPP'};CodeMap.DynamicFuncCodeEntry=function(size,type,func,state){CodeMap.CodeEntry.call(this,size,'',type);this.func=func;this.state=state;};CodeMap.DynamicFuncCodeEntry.STATE_PREFIX=['','~','*'];CodeMap.DynamicFuncCodeEntry.prototype={__proto__:CodeMap.CodeEntry.prototype,get name(){return CodeMap.DynamicFuncCodeEntry.STATE_PREFIX[this.state]+
-this.func.name;},set name(value){this.name_=value;},getRawName:function(){return this.func.getName();},isJSFunction:function(){return true;},toString:function(){return this.type+': '+this.name+': '+this.size.toString(16);}};CodeMap.FunctionEntry=function(name){CodeMap.CodeEntry.call(this,0,name);};CodeMap.FunctionEntry.prototype={__proto__:CodeMap.CodeEntry.prototype,get name(){var name=this.name_;if(name.length==0){name='<anonymous>';}else if(name.charAt(0)==' '){name='<anonymous>'+name;}
+this.func.name;},set name(value){this.name_=value;},getRawName:function(){return this.func.getName();},isJSFunction:function(){return true;},toString:function(){return this.type+': '+this.name+': '+this.size.toString(16);}};CodeMap.FunctionEntry=function(name){CodeMap.CodeEntry.call(this,0,name);};CodeMap.FunctionEntry.prototype={__proto__:CodeMap.CodeEntry.prototype,get name(){var name=this.name_;if(name.length===0){name='<anonymous>';}else if(name.charAt(0)===' '){name='<anonymous>'+name;}
 return name;},set name(value){this.name_=value;}};CodeMap.NameGenerator=function(){this.knownNames_={};};CodeMap.NameGenerator.prototype.getName=function(name){if(!(name in this.knownNames_)){this.knownNames_[name]=0;return name;}
-var count=++this.knownNames_[name];return name+' {'+count+'}';};return{CodeMap:CodeMap};});'use strict';tr.exportTo('tr.e.importer.v8',function(){function CsvParser(){};CsvParser.CSV_FIELD_RE_=/^"((?:[^"]|"")*)"|([^,]*)/;CsvParser.DOUBLE_QUOTE_RE_=/""/g;CsvParser.prototype.parseLine=function(line){var fieldRe=CsvParser.CSV_FIELD_RE_;var doubleQuoteRe=CsvParser.DOUBLE_QUOTE_RE_;var pos=0;var endPos=line.length;var fields=[];if(endPos>0){do{var fieldMatch=fieldRe.exec(line.substr(pos));if(typeof fieldMatch[1]==='string'){var field=fieldMatch[1];pos+=field.length+3;fields.push(field.replace(doubleQuoteRe,'"'));}else{var field=fieldMatch[2];pos+=field.length+1;fields.push(field);}}while(pos<=endPos);}
-return fields;};function LogReader(dispatchTable){this.dispatchTable_=dispatchTable;this.lineNum_=0;this.csvParser_=new CsvParser();};LogReader.prototype.printError=function(str){};LogReader.prototype.processLogChunk=function(chunk){this.processLog_(chunk.split('\n'));};LogReader.prototype.processLogLine=function(line){this.processLog_([line]);};LogReader.prototype.processStack=function(pc,func,stack){var fullStack=func?[pc,func]:[pc];var prevFrame=pc;for(var i=0,n=stack.length;i<n;++i){var frame=stack[i];var firstChar=frame.charAt(0);if(firstChar=='+'||firstChar=='-'){prevFrame+=parseInt(frame,16);fullStack.push(prevFrame);}else if(firstChar!='o'){fullStack.push(parseInt(frame,16));}}
+var count=++this.knownNames_[name];return name+' {'+count+'}';};return{CodeMap,};});'use strict';tr.exportTo('tr.e.importer.v8',function(){function CsvParser(){}
+CsvParser.CSV_FIELD_RE_=/^"((?:[^"]|"")*)"|([^,]*)/;CsvParser.DOUBLE_QUOTE_RE_=/""/g;CsvParser.prototype.parseLine=function(line){var fieldRe=CsvParser.CSV_FIELD_RE_;var doubleQuoteRe=CsvParser.DOUBLE_QUOTE_RE_;var pos=0;var endPos=line.length;var fields=[];if(endPos>0){do{var fieldMatch=fieldRe.exec(line.substr(pos));if(typeof fieldMatch[1]==='string'){var field=fieldMatch[1];pos+=field.length+3;fields.push(field.replace(doubleQuoteRe,'"'));}else{var field=fieldMatch[2];pos+=field.length+1;fields.push(field);}}while(pos<=endPos);}
+return fields;};function LogReader(dispatchTable){this.dispatchTable_=dispatchTable;this.lineNum_=0;this.csvParser_=new CsvParser();}
+LogReader.prototype.printError=function(str){};LogReader.prototype.processLogChunk=function(chunk){this.processLog_(chunk.split('\n'));};LogReader.prototype.processLogLine=function(line){this.processLog_([line]);};LogReader.prototype.processStack=function(pc,func,stack){var fullStack=func?[pc,func]:[pc];var prevFrame=pc;for(var i=0,n=stack.length;i<n;++i){var frame=stack[i];var firstChar=frame.charAt(0);if(firstChar==='+'||firstChar==='-'){prevFrame+=parseInt(frame,16);fullStack.push(prevFrame);}else if(firstChar!=='o'){fullStack.push(parseInt(frame,16));}}
 return fullStack;};LogReader.prototype.skipDispatch=function(dispatch){return false;};LogReader.prototype.dispatchLogRow_=function(fields){var command=fields[0];if(!(command in this.dispatchTable_))return;var dispatch=this.dispatchTable_[command];if(dispatch===null||this.skipDispatch(dispatch)){return;}
-var parsedFields=[];for(var i=0;i<dispatch.parsers.length;++i){var parser=dispatch.parsers[i];if(parser===null){parsedFields.push(fields[1+i]);}else if(typeof parser=='function'){parsedFields.push(parser(fields[1+i]));}else{parsedFields.push(fields.slice(1+i));break;}}
+var parsedFields=[];for(var i=0;i<dispatch.parsers.length;++i){var parser=dispatch.parsers[i];if(parser===null){parsedFields.push(fields[1+i]);}else if(typeof parser==='function'){parsedFields.push(parser(fields[1+i]));}else{parsedFields.push(fields.slice(1+i));break;}}
 dispatch.processor.apply(this,parsedFields);};LogReader.prototype.processLog_=function(lines){for(var i=0,n=lines.length;i<n;++i,++this.lineNum_){var line=lines[i];if(!line){continue;}
 try{var fields=this.csvParser_.parseLine(line);this.dispatchLogRow_(fields);}catch(e){this.printError('line '+(this.lineNum_+1)+': '+
-(e.message||e));}}};return{LogReader:LogReader};});'use strict';tr.exportTo('tr.e.importer.v8',function(){var CodeEntry=tr.e.importer.v8.CodeMap.CodeEntry;var CodeMap=tr.e.importer.v8.CodeMap;var ColorScheme=tr.b.ColorScheme;var DynamicFuncCodeEntry=tr.e.importer.v8.CodeMap.DynamicFuncCodeEntry;var FunctionEntry=tr.e.importer.v8.CodeMap.FunctionEntry;function V8LogImporter(model,eventData){this.importPriority=3;this.model_=model;this.logData_=eventData;this.code_map_=new CodeMap();this.v8_timer_thread_=undefined;this.v8_thread_=undefined;this.root_stack_frame_=new tr.model.StackFrame(undefined,'v8-root-stack-frame','v8-root-stack-frame',0);this.v8_stack_timeline_=new Array();}
-var kV8BinarySuffixes=['/d8','/libv8.so'];var TimerEventDefaultArgs={'V8.Execute':{pause:false,no_execution:false},'V8.External':{pause:false,no_execution:true},'V8.CompileFullCode':{pause:true,no_execution:true},'V8.RecompileSynchronous':{pause:true,no_execution:true},'V8.RecompileParallel':{pause:false,no_execution:false},'V8.CompileEval':{pause:true,no_execution:true},'V8.Parse':{pause:true,no_execution:true},'V8.PreParse':{pause:true,no_execution:true},'V8.ParseLazy':{pause:true,no_execution:true},'V8.GCScavenger':{pause:true,no_execution:true},'V8.GCCompactor':{pause:true,no_execution:true},'V8.GCContext':{pause:true,no_execution:true}};V8LogImporter.canImport=function(eventData){if(typeof(eventData)!=='string'&&!(eventData instanceof String))
-return false;return eventData.substring(0,11)=='v8-version,'||eventData.substring(0,12)=='timer-event,'||eventData.substring(0,5)=='tick,'||eventData.substring(0,15)=='shared-library,'||eventData.substring(0,9)=='profiler,'||eventData.substring(0,14)=='code-creation,';};V8LogImporter.prototype={__proto__:tr.importer.Importer.prototype,get importerName(){return'V8LogImporter';},processTimerEvent_:function(name,startInUs,lengthInUs){var args=TimerEventDefaultArgs[name];if(args===undefined)return;var startInMs=tr.b.convertUnit(startInUs,tr.b.UnitScale.Metric.MICRO,tr.b.UnitScale.Metric.MILLI);var lengthInMs=tr.b.convertUnit(lengthInUs,tr.b.UnitScale.Metric.MICRO,tr.b.UnitScale.Metric.MILLI);var colorId=ColorScheme.getColorIdForGeneralPurposeString(name);var slice=new tr.model.ThreadSlice('v8',name,colorId,startInMs,args,lengthInMs);this.v8_timer_thread_.sliceGroup.pushSlice(slice);},processTimerEventStart_:function(name,startInUs){var args=TimerEventDefaultArgs[name];if(args===undefined)return;var startInMs=tr.b.convertUnit(startInUs,tr.b.UnitScale.Metric.MICRO,tr.b.UnitScale.Metric.MILLI);this.v8_timer_thread_.sliceGroup.beginSlice('v8',name,startInMs,args);},processTimerEventEnd_:function(name,endInUs){var endInMs=tr.b.convertUnit(endInUs,tr.b.UnitScale.Metric.MICRO,tr.b.UnitScale.Metric.MILLI);this.v8_timer_thread_.sliceGroup.endSlice(endInMs);},processCodeCreateEvent_:function(type,kind,address,size,name,maybe_func){function parseState(s){switch(s){case'':return CodeMap.CodeState.COMPILED;case'~':return CodeMap.CodeState.OPTIMIZABLE;case'*':return CodeMap.CodeState.OPTIMIZED;}
+(e.message||e));}}};return{LogReader,};});'use strict';tr.exportTo('tr.model',function(){function ProfileNode(id,title,parentNode){this.id_=id;this.title_=title;this.parentNode_=parentNode;this.colorId_=-1;this.userFriendlyStack_=[];}
+ProfileNode.prototype={__proto__:Object.prototype,get title(){return this.title_;},get parentNode(){return this.parentNode_;},set parentNode(value){this.parentNode_=value;},get id(){return this.id_;},get colorId(){return this.colorId_;},set colorId(value){this.colorId_=value;},get userFriendlyName(){return this.title_;},get userFriendlyStack(){if(this.userFriendlyStack_.length===0){this.userFriendlyStack_=[this.userFriendlyName];if(this.parentNode_!==undefined){this.userFriendlyStack_=this.userFriendlyStack_.concat(this.parentNode_.userFriendlyStack);}}
+return this.userFriendlyStack_;},get sampleTitle(){throw new Error('Not implemented.');}};tr.model.EventRegistry.register(ProfileNode,{name:'Node',pluralName:'Nodes'});return{ProfileNode,};});'use strict';tr.exportTo('tr.model',function(){function ProfileTree(){this.startTime_=undefined;this.endTime_=undefined;this.tree_=new Map();this.pid_=-1;this.tid_=-1;}
+ProfileTree.prototype={__proto__:Object.prototype,get pid(){return this.pid_;},set pid(value){this.pid_=value;},get tid(){return this.tid_;},set tid(value){this.tid_=value;},get tree(){return this.tree_;},get startTime(){return this.startTime_;},set startTime(value){this.startTime_=value;this.endTime_=value;},get endTime(){return this.endTime_;},set endTime(value){this.endTime_=value;},add:function(node){if(this.tree_.has(node.id)){throw new Error('Conflict id in the profile tree.');}
+this.tree_.set(node.id,node);return node;},getNode:function(nodeId){return this.tree_.get(nodeId);}};return{ProfileTree,};});'use strict';tr.exportTo('tr.e.importer.v8',function(){var CodeEntry=tr.e.importer.v8.CodeMap.CodeEntry;var CodeMap=tr.e.importer.v8.CodeMap;var ColorScheme=tr.b.ColorScheme;var DynamicFuncCodeEntry=tr.e.importer.v8.CodeMap.DynamicFuncCodeEntry;var FunctionEntry=tr.e.importer.v8.CodeMap.FunctionEntry;var ProfileNodeType=tr.model.ProfileNode.subTypes.getConstructor(undefined,'legacySample');function V8LogImporter(model,eventData){this.importPriority=3;this.model_=model;this.logData_=eventData;this.code_map_=new CodeMap();this.v8_timer_thread_=undefined;this.v8_thread_=undefined;this.profileTree_=new tr.model.ProfileTree();this.profileTree_.add(new ProfileNodeType(-1,{url:'',functionName:'unknown'}));this.v8_stack_timeline_=[];}
+var kV8BinarySuffixes=['/d8','/libv8.so'];var TimerEventDefaultArgs={'V8.Execute':{pause:false,no_execution:false},'V8.External':{pause:false,no_execution:true},'V8.CompileFullCode':{pause:true,no_execution:true},'V8.RecompileSynchronous':{pause:true,no_execution:true},'V8.RecompileParallel':{pause:false,no_execution:false},'V8.CompileEval':{pause:true,no_execution:true},'V8.Parse':{pause:true,no_execution:true},'V8.PreParse':{pause:true,no_execution:true},'V8.ParseLazy':{pause:true,no_execution:true},'V8.GCScavenger':{pause:true,no_execution:true},'V8.GCCompactor':{pause:true,no_execution:true},'V8.GCContext':{pause:true,no_execution:true}};V8LogImporter.canImport=function(eventData){if(typeof(eventData)!=='string'&&!(eventData instanceof String)){return false;}
+return eventData.substring(0,11)==='v8-version,'||eventData.substring(0,12)==='timer-event,'||eventData.substring(0,5)==='tick,'||eventData.substring(0,15)==='shared-library,'||eventData.substring(0,9)==='profiler,'||eventData.substring(0,14)==='code-creation,';};V8LogImporter.prototype={__proto__:tr.importer.Importer.prototype,get importerName(){return'V8LogImporter';},processTimerEvent_:function(name,startInUs,lengthInUs){var args=TimerEventDefaultArgs[name];if(args===undefined)return;var startInMs=tr.b.convertUnit(startInUs,tr.b.UnitPrefixScale.METRIC.MICRO,tr.b.UnitPrefixScale.METRIC.MILLI);var lengthInMs=tr.b.convertUnit(lengthInUs,tr.b.UnitPrefixScale.METRIC.MICRO,tr.b.UnitPrefixScale.METRIC.MILLI);var colorId=ColorScheme.getColorIdForGeneralPurposeString(name);var slice=new tr.model.ThreadSlice('v8',name,colorId,startInMs,args,lengthInMs);this.v8_timer_thread_.sliceGroup.pushSlice(slice);},processTimerEventStart_:function(name,startInUs){var args=TimerEventDefaultArgs[name];if(args===undefined)return;var startInMs=tr.b.convertUnit(startInUs,tr.b.UnitPrefixScale.METRIC.MICRO,tr.b.UnitPrefixScale.METRIC.MILLI);this.v8_timer_thread_.sliceGroup.beginSlice('v8',name,startInMs,args);},processTimerEventEnd_:function(name,endInUs){var endInMs=tr.b.convertUnit(endInUs,tr.b.UnitPrefixScale.METRIC.MICRO,tr.b.UnitPrefixScale.METRIC.MILLI);this.v8_timer_thread_.sliceGroup.endSlice(endInMs);},processCodeCreateEvent_:function(type,kind,address,size,name,maybeFunc){function parseState(s){switch(s){case'':return CodeMap.CodeState.COMPILED;case'~':return CodeMap.CodeState.OPTIMIZABLE;case'*':return CodeMap.CodeState.OPTIMIZED;}
 throw new Error('unknown code state: '+s);}
-if(maybe_func.length){var funcAddr=parseInt(maybe_func[0]);var state=parseState(maybe_func[1]);var func=this.code_map_.findDynamicEntryByStartAddress(funcAddr);if(!func){func=new FunctionEntry(name);func.kind=kind;this.code_map_.addCode(funcAddr,func);}else if(func.name!==name){func.name=name;}
-var entry=this.code_map_.findDynamicEntryByStartAddress(address);if(entry){if(entry.size===size&&entry.func===func){entry.state=state;}}else{entry=new DynamicFuncCodeEntry(size,type,func,state);entry.kind=kind;this.code_map_.addCode(address,entry);}}else{var code_entry=new CodeEntry(size,name);code_entry.kind=kind;this.code_map_.addCode(address,code_entry);}},processCodeMoveEvent_:function(from,to){this.code_map_.moveCode(from,to);},processCodeDeleteEvent_:function(address){this.code_map_.deleteCode(address);},processSharedLibrary_:function(name,start,end){var code_entry=new CodeEntry(end-start,name,CodeEntry.TYPE.SHARED_LIB);code_entry.kind=-3;for(var i=0;i<kV8BinarySuffixes.length;i++){var suffix=kV8BinarySuffixes[i];if(name.indexOf(suffix,name.length-suffix.length)>=0){code_entry.kind=-1;break;}}
-this.code_map_.addLibrary(start,code_entry);},processCppSymbol_:function(address,size,name){var code_entry=new CodeEntry(size,name,CodeEntry.TYPE.CPP);code_entry.kind=-1;this.code_map_.addStaticCode(address,code_entry);},processTickEvent_:function(pc,startInUs,is_external_callback,tos_or_external_callback,vmstate,stack){var startInMs=tr.b.convertUnit(startInUs,tr.b.UnitScale.Metric.MICRO,tr.b.UnitScale.Metric.MILLI);function findChildWithEntryID(stackFrame,entryID){for(var i=0;i<stackFrame.children.length;i++){if(stackFrame.children[i].entryID==entryID)
-return stackFrame.children[i];}
+if(maybeFunc.length){var funcAddr=parseInt(maybeFunc[0]);var state=parseState(maybeFunc[1]);var func=this.code_map_.findDynamicEntryByStartAddress(funcAddr);if(!func){func=new FunctionEntry(name);func.kind=kind;this.code_map_.addCode(funcAddr,func);}else if(func.name!==name){func.name=name;}
+var entry=this.code_map_.findDynamicEntryByStartAddress(address);if(entry){if(entry.size===size&&entry.func===func){entry.state=state;}}else{entry=new DynamicFuncCodeEntry(size,type,func,state);entry.kind=kind;this.code_map_.addCode(address,entry);}}else{var codeEntry=new CodeEntry(size,name);codeEntry.kind=kind;this.code_map_.addCode(address,codeEntry);}},processCodeMoveEvent_:function(from,to){this.code_map_.moveCode(from,to);},processCodeDeleteEvent_:function(address){this.code_map_.deleteCode(address);},processSharedLibrary_:function(name,start,end){var codeEntry=new CodeEntry(end-start,name,CodeEntry.TYPE.SHARED_LIB);codeEntry.kind=-3;for(var i=0;i<kV8BinarySuffixes.length;i++){var suffix=kV8BinarySuffixes[i];if(name.indexOf(suffix,name.length-suffix.length)>=0){codeEntry.kind=-1;break;}}
+this.code_map_.addLibrary(start,codeEntry);},processCppSymbol_:function(address,size,name){var codeEntry=new CodeEntry(size,name,CodeEntry.TYPE.CPP);codeEntry.kind=-1;this.code_map_.addStaticCode(address,codeEntry);},processTickEvent_:function(pc,startInUs,isExternalCallback,tosOrExternalCallback,vmstate,stack){var startInMs=tr.b.convertUnit(startInUs,tr.b.UnitPrefixScale.METRIC.MICRO,tr.b.UnitPrefixScale.METRIC.MILLI);function findChildWithEntryID(stackFrame,entryID){for(var i=0;i<stackFrame.children.length;i++){if(stackFrame.children[i].entryID===entryID){return stackFrame.children[i];}}
 return undefined;}
 function processStack(pc,func,stack){var fullStack=func?[pc,func]:[pc];var prevFrame=pc;for(var i=0,n=stack.length;i<n;++i){var frame=stack[i];var firstChar=frame.charAt(0);if(firstChar==='+'||firstChar==='-'){prevFrame+=parseInt(frame,16);fullStack.push(prevFrame);}else if(firstChar!=='o'){fullStack.push(parseInt(frame,16));}}
 return fullStack;}
-if(is_external_callback){pc=tos_or_external_callback;tos_or_external_callback=0;}else if(tos_or_external_callback){var funcEntry=this.code_map_.findEntry(tos_or_external_callback);if(!funcEntry||!funcEntry.isJSFunction||!funcEntry.isJSFunction())
-tos_or_external_callback=0;}
-var processedStack=processStack(pc,tos_or_external_callback,stack);var lastStackFrame=this.root_stack_frame_;processedStack=processedStack.reverse();for(var i=0,n=processedStack.length;i<n;i++){var frame=processedStack[i];if(!frame)
-break;var entry=this.code_map_.findEntry(frame);if(!entry&&i!==0){continue;}
-var sourceInfo=undefined;if(entry&&entry.type===CodeEntry.TYPE.CPP){var libEntry=this.code_map_.findEntryInLibraries(frame);if(libEntry)
-sourceInfo=libEntry.name;}
-var entryID=entry?entry.id:'Unknown';var childFrame=findChildWithEntryID(lastStackFrame,entryID);if(childFrame===undefined){var entryName=entry?entry.name:'Unknown';lastStackFrame=new tr.model.StackFrame(lastStackFrame,'v8sf-'+tr.b.GUID.allocateSimple(),entryName,ColorScheme.getColorIdForGeneralPurposeString(entryName),sourceInfo);lastStackFrame.entryID=entryID;this.model_.addStackFrame(lastStackFrame);}else{lastStackFrame=childFrame;}}
-if(lastStackFrame!==this.root_stack_frame_){var sample=new tr.model.Sample(undefined,this.v8_thread_,'V8 PC',startInMs,lastStackFrame,1);this.model_.samples.push(sample);}},processDistortion_:function(distortion_in_picoseconds){},processPlotRange_:function(start,end){},processV8Version_:function(major,minor,build,patch,candidate){},importEvents:function(){var logreader=new tr.e.importer.v8.LogReader({'timer-event':{parsers:[null,parseInt,parseInt],processor:this.processTimerEvent_.bind(this)},'shared-library':{parsers:[null,parseInt,parseInt],processor:this.processSharedLibrary_.bind(this)},'timer-event-start':{parsers:[null,parseInt],processor:this.processTimerEventStart_.bind(this)},'timer-event-end':{parsers:[null,parseInt],processor:this.processTimerEventEnd_.bind(this)},'code-creation':{parsers:[null,parseInt,parseInt,parseInt,null,'var-args'],processor:this.processCodeCreateEvent_.bind(this)},'code-move':{parsers:[parseInt,parseInt],processor:this.processCodeMoveEvent_.bind(this)},'code-delete':{parsers:[parseInt],processor:this.processCodeDeleteEvent_.bind(this)},'cpp':{parsers:[parseInt,parseInt,null],processor:this.processCppSymbol_.bind(this)},'tick':{parsers:[parseInt,parseInt,parseInt,parseInt,parseInt,'var-args'],processor:this.processTickEvent_.bind(this)},'distortion':{parsers:[parseInt],processor:this.processDistortion_.bind(this)},'plot-range':{parsers:[parseInt,parseInt],processor:this.processPlotRange_.bind(this)},'v8-version':{parsers:[parseInt,parseInt,parseInt,parseInt,parseInt],processor:this.processV8Version_.bind(this)}});this.v8_timer_thread_=this.model_.getOrCreateProcess(-32).getOrCreateThread(1);this.v8_timer_thread_.name='V8 Timers';this.v8_thread_=this.model_.getOrCreateProcess(-32).getOrCreateThread(2);this.v8_thread_.name='V8';var lines=this.logData_.split('\n');for(var i=0;i<lines.length;i++){logreader.processLogLine(lines[i]);}
-this.root_stack_frame_.removeAllChildren();function addSlices(slices,thread){for(var i=0;i<slices.length;i++){var duration=slices[i].end-slices[i].start;var slice=new tr.model.ThreadSlice('v8',slices[i].name,ColorScheme.getColorIdForGeneralPurposeString(slices[i].name),slices[i].start,{},duration);thread.sliceGroup.pushSlice(slice);addSlices(slices[i].children,thread);}}
-addSlices(this.v8_stack_timeline_,this.v8_thread_);}};tr.importer.Importer.register(V8LogImporter);return{V8LogImporter:V8LogImporter};});'use strict';tr.exportTo('tr.e.importer',function(){function ZipImporter(model,eventData){if(eventData instanceof ArrayBuffer)
-eventData=new Uint8Array(eventData);this.model_=model;this.eventData_=eventData;}
-ZipImporter.canImport=function(eventData){var header;if(eventData instanceof ArrayBuffer)
-header=new Uint8Array(eventData.slice(0,2));else if(typeof(eventData)==='string'||eventData instanceof String)
-header=[eventData.charCodeAt(0),eventData.charCodeAt(1)];else
-return false;return header[0]==='P'.charCodeAt(0)&&header[1]==='K'.charCodeAt(0);};ZipImporter.prototype={__proto__:tr.importer.Importer.prototype,get importerName(){return'ZipImporter';},isTraceDataContainer:function(){return true;},extractSubtraces:function(){var zip=new JSZip(this.eventData_);var subtraces=[];for(var idx in zip.files)
-subtraces.push(zip.files[idx].asBinary());return subtraces;}};tr.importer.Importer.register(ZipImporter);return{ZipImporter:ZipImporter};});'use strict';tr.exportTo('tr.model.source_info',function(){function SourceInfo(file,opt_line,opt_column){this.file_=file;this.line_=opt_line||-1;this.column_=opt_column||-1;}
-SourceInfo.prototype={get file(){return this.file_;},get line(){return this.line_;},get column(){return this.column_;},get domain(){if(!this.file_)
-return undefined;var domain=this.file_.match(/(.*:\/\/[^:\/]*)/i);return domain?domain[1]:undefined;},toString:function(){var str='';if(this.file_)
-str+=this.file_;if(this.line_>0)
-str+=':'+this.line_;if(this.column_>0)
-str+=':'+this.column_;return str;}};return{SourceInfo:SourceInfo};});'use strict';tr.exportTo('tr.model.source_info',function(){function JSSourceInfo(file,line,column,isNative,scriptId,state){tr.model.source_info.SourceInfo.call(this,file,line,column);this.isNative_=isNative;this.scriptId_=scriptId;this.state_=state;}
+if(isExternalCallback){pc=tosOrExternalCallback;tosOrExternalCallback=0;}else if(tosOrExternalCallback){var funcEntry=this.code_map_.findEntry(tosOrExternalCallback);if(!funcEntry||!funcEntry.isJSFunction||!funcEntry.isJSFunction()){tosOrExternalCallback=0;}}
+var processedStack=processStack(pc,tosOrExternalCallback,stack);var node=undefined;var lastNode=undefined;processedStack=processedStack.reverse();for(var i=0,n=processedStack.length;i<n;i++){var frame=processedStack[i];if(!frame)break;var entry=this.code_map_.findEntry(frame);if(!entry&&i!==0){continue;}
+var sourceInfo=undefined;if(entry&&entry.type===CodeEntry.TYPE.CPP){var libEntry=this.code_map_.findEntryInLibraries(frame);if(libEntry){sourceInfo={file:libEntry.name};}}
+var entryId=entry?entry.id:-1;var node=this.profileTree_.getNode(entryId);if(node===undefined){node=this.profileTree_.add(new ProfileNodeType(entryId,{functionName:entry.name,url:sourceInfo?sourceInfo.file:'',lineNumber:sourceInfo?sourceInfo.line:undefined,columnNumber:sourceInfo?sourceInfo.column:undefined,scriptId:sourceInfo?sourceInfo.scriptId:undefined},lastNode));}
+lastNode=node;}
+this.model_.samples.push(new tr.model.Sample(startInMs,'V8 PC',node,this.v8_thread_,undefined,1));},processDistortion_:function(distortionInPicoseconds){},processPlotRange_:function(start,end){},processV8Version_:function(major,minor,build,patch,candidate){},importEvents:function(){var logreader=new tr.e.importer.v8.LogReader({'timer-event':{parsers:[null,parseInt,parseInt],processor:this.processTimerEvent_.bind(this)},'shared-library':{parsers:[null,parseInt,parseInt],processor:this.processSharedLibrary_.bind(this)},'timer-event-start':{parsers:[null,parseInt],processor:this.processTimerEventStart_.bind(this)},'timer-event-end':{parsers:[null,parseInt],processor:this.processTimerEventEnd_.bind(this)},'code-creation':{parsers:[null,parseInt,parseInt,parseInt,null,'var-args'],processor:this.processCodeCreateEvent_.bind(this)},'code-move':{parsers:[parseInt,parseInt],processor:this.processCodeMoveEvent_.bind(this)},'code-delete':{parsers:[parseInt],processor:this.processCodeDeleteEvent_.bind(this)},'cpp':{parsers:[parseInt,parseInt,null],processor:this.processCppSymbol_.bind(this)},'tick':{parsers:[parseInt,parseInt,parseInt,parseInt,parseInt,'var-args'],processor:this.processTickEvent_.bind(this)},'distortion':{parsers:[parseInt],processor:this.processDistortion_.bind(this)},'plot-range':{parsers:[parseInt,parseInt],processor:this.processPlotRange_.bind(this)},'v8-version':{parsers:[parseInt,parseInt,parseInt,parseInt,parseInt],processor:this.processV8Version_.bind(this)}});this.v8_timer_thread_=this.model_.getOrCreateProcess(-32).getOrCreateThread(1);this.v8_timer_thread_.name='V8 Timers';this.v8_thread_=this.model_.getOrCreateProcess(-32).getOrCreateThread(2);this.v8_thread_.name='V8';var lines=this.logData_.split('\n');for(var i=0;i<lines.length;i++){logreader.processLogLine(lines[i]);}
+function addSlices(slices,thread){for(var i=0;i<slices.length;i++){var duration=slices[i].end-slices[i].start;var slice=new tr.model.ThreadSlice('v8',slices[i].name,ColorScheme.getColorIdForGeneralPurposeString(slices[i].name),slices[i].start,{},duration);thread.sliceGroup.pushSlice(slice);addSlices(slices[i].children,thread);}}
+addSlices(this.v8_stack_timeline_,this.v8_thread_);}};tr.importer.Importer.register(V8LogImporter);return{V8LogImporter,};});'use strict';tr.exportTo('tr.e.importer',function(){function ZipImporter(model,eventData){if(eventData instanceof ArrayBuffer){eventData=new Uint8Array(eventData);}
+this.model_=model;this.eventData_=eventData;}
+ZipImporter.canImport=function(eventData){var header;if(eventData instanceof ArrayBuffer){header=new Uint8Array(eventData.slice(0,2));}else if(typeof(eventData)==='string'||eventData instanceof String){header=[eventData.charCodeAt(0),eventData.charCodeAt(1)];}else{return false;}
+return header[0]==='P'.charCodeAt(0)&&header[1]==='K'.charCodeAt(0);};ZipImporter.prototype={__proto__:tr.importer.Importer.prototype,get importerName(){return'ZipImporter';},isTraceDataContainer:function(){return true;},extractSubtraces:function(){var zip=new JSZip(this.eventData_);var subtraces=[];for(var idx in zip.files){subtraces.push(zip.files[idx].asBinary());}
+return subtraces;}};tr.importer.Importer.register(ZipImporter);return{ZipImporter,};});'use strict';tr.exportTo('tr.model',function(){function HeapEntry(heapDump,leafStackFrame,objectTypeName,size,count){this.heapDump=heapDump;this.leafStackFrame=leafStackFrame;this.objectTypeName=objectTypeName;this.size=size;this.count=count;}
+function HeapDump(processMemoryDump,allocatorName){this.processMemoryDump=processMemoryDump;this.allocatorName=allocatorName;this.entries=[];}
+HeapDump.prototype={addEntry:function(leafStackFrame,objectTypeName,size,count){var entry=new HeapEntry(this,leafStackFrame,objectTypeName,size,count);this.entries.push(entry);return entry;}};return{HeapEntry,HeapDump,};});'use strict';tr.exportTo('tr.e.importer',function(){function HeapDumpTraceEventImporter(model,processMemoryDump,processObjectTypeNameMap,idPrefix,dumpId){this.model_=model;this.processObjectTypeNameMap_=processObjectTypeNameMap;this.idPrefix_=idPrefix;this.processMemoryDump_=processMemoryDump;this.pid_=this.processMemoryDump_.process.pid;this.dumpId_=dumpId;}
+HeapDumpTraceEventImporter.prototype={parseRawHeapDump:function(rawHeapDump,allocatorName){var model=this.model_;var processMemoryDump=this.processMemoryDump_;var heapDump=new tr.model.HeapDump(processMemoryDump,allocatorName);var entries=rawHeapDump.entries;if(entries===undefined||entries.length===0){this.model_.importWarning({type:'memory_dump_parse_error',message:'No heap entries in a '+allocatorName+' heap dump for PID='+this.pid_+' and dump ID='+this.dumpId_+'.'});return undefined;}
+var isOldFormat=entries[0].bt===undefined;if(!isOldFormat&&this.processObjectTypeNameMap_===undefined){return undefined;}
+for(var i=0;i<entries.length;i++){var entry=entries[i];var leafStackFrameIndex=entry.bt;var leafStackFrame;if(isOldFormat){if(leafStackFrameIndex===undefined){leafStackFrame=undefined;}else{var leafStackFrameId=this.idPrefix_+leafStackFrameIndex;if(leafStackFrameIndex===''){leafStackFrame=undefined;}else{leafStackFrame=model.stackFrames[leafStackFrameId];if(leafStackFrame===undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Missing leaf stack frame (ID '+
+leafStackFrameId+') of heap entry '+i+' (size '+
+size+') in a '+allocatorName+' heap dump for PID='+this.pid_+'.'});continue;}}
+leafStackFrameId+=':self';if(model.stackFrames[leafStackFrameId]!==undefined){leafStackFrame=model.stackFrames[leafStackFrameId];}else{leafStackFrame=new tr.model.StackFrame(leafStackFrame,leafStackFrameId,'<self>',undefined);model.addStackFrame(leafStackFrame);}}}else{if(leafStackFrameIndex===undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Missing stack frame ID of heap entry '+i+' (size '+size+') in a '+allocatorName+' heap dump for PID='+this.pid_+'.'});continue;}
+var leafStackFrameId=this.idPrefix_+leafStackFrameIndex;if(leafStackFrameIndex===''){leafStackFrame=undefined;}else{leafStackFrame=model.stackFrames[leafStackFrameId];if(leafStackFrame===undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Missing leaf stack frame (ID '+leafStackFrameId+') of heap entry '+i+' (size '+size+') in a '+
+allocatorName+' heap dump for PID='+this.pid_+'.'});continue;}}}
+var objectTypeId=entry.type;var objectTypeName;if(objectTypeId===undefined){objectTypeName=undefined;}else if(this.processObjectTypeNameMap_===undefined){continue;}else{objectTypeName=this.processObjectTypeNameMap_[objectTypeId];if(objectTypeName===undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Missing object type name (ID '+objectTypeId+') of heap entry '+i+' (size '+size+') in a '+
+allocatorName+' heap dump for PID='+this.pid_+'.'});continue;}}
+var size=parseInt(entry.size,16);var count=entry.count===undefined?undefined:parseInt(entry.count,16);heapDump.addEntry(leafStackFrame,objectTypeName,size,count);}
+return heapDump;},};return{HeapDumpTraceEventImporter,};});'use strict';tr.exportTo('tr.model.source_info',function(){function SourceInfo(file,opt_line,opt_column){this.file_=file;this.line_=opt_line||-1;this.column_=opt_column||-1;}
+SourceInfo.prototype={get file(){return this.file_;},get line(){return this.line_;},get column(){return this.column_;},get domain(){if(!this.file_)return undefined;var domain=this.file_.match(/(.*:\/\/[^:\/]*)/i);return domain?domain[1]:undefined;},toString:function(){var str='';if(this.file_){str+=this.file_;}
+if(this.line_>0){str+=':'+this.line_;}
+if(this.column_>0){str+=':'+this.column_;}
+return str;}};return{SourceInfo,};});'use strict';tr.exportTo('tr.model.source_info',function(){function JSSourceInfo(file,line,column,isNative,scriptId,state){tr.model.source_info.SourceInfo.call(this,file,line,column);this.isNative_=isNative;this.scriptId_=scriptId;this.state_=state;}
 JSSourceInfo.prototype={__proto__:tr.model.source_info.SourceInfo.prototype,get state(){return this.state_;},get isNative(){return this.isNative_;},get scriptId(){return this.scriptId_;},toString:function(){var str=this.isNative_?'[native v8] ':'';return str+
-tr.model.source_info.SourceInfo.prototype.toString.call(this);}};return{JSSourceInfo:JSSourceInfo,JSSourceState:{COMPILED:'compiled',OPTIMIZABLE:'optimizable',OPTIMIZED:'optimized',UNKNOWN:'unknown'}};});'use strict';tr.exportTo('tr.e.importer',function(){function TraceCodeEntry(address,size,name,scriptId){this.id_=tr.b.GUID.allocateSimple();this.address_=address;this.size_=size;var rePrefix=/^(\w*:)?([*~]?)(.*)$/m;var tokens=rePrefix.exec(name);var prefix=tokens[1];var state=tokens[2];var body=tokens[3];if(state==='*'){state=tr.model.source_info.JSSourceState.OPTIMIZED;}else if(state==='~'){state=tr.model.source_info.JSSourceState.OPTIMIZABLE;}else if(state===''){state=tr.model.source_info.JSSourceState.COMPILED;}else{console.warning('Unknown v8 code state '+state);state=tr.model.source_info.JSSourceState.UNKNOWN;}
+tr.model.source_info.SourceInfo.prototype.toString.call(this);}};var JSSourceState={COMPILED:'compiled',OPTIMIZABLE:'optimizable',OPTIMIZED:'optimized',UNKNOWN:'unknown',};return{JSSourceInfo,JSSourceState,};});'use strict';tr.exportTo('tr.e.importer',function(){function TraceCodeEntry(address,size,name,scriptId){this.id_=tr.b.GUID.allocateSimple();this.address_=address;this.size_=size;var rePrefix=/^(\w*:)?([*~]?)(.*)$/m;var tokens=rePrefix.exec(name);var prefix=tokens[1];var state=tokens[2];var body=tokens[3];if(state==='*'){state=tr.model.source_info.JSSourceState.OPTIMIZED;}else if(state==='~'){state=tr.model.source_info.JSSourceState.OPTIMIZABLE;}else if(state===''){state=tr.model.source_info.JSSourceState.COMPILED;}else{state=tr.model.source_info.JSSourceState.UNKNOWN;}
 var rawName;var rawUrl;if(prefix==='Script:'){rawName='';rawUrl=body;}else{var spacePos=body.lastIndexOf(' ');rawName=spacePos!==-1?body.substr(0,spacePos):body;rawUrl=spacePos!==-1?body.substr(spacePos+1):'';}
 function splitLineAndColumn(url){var lineColumnRegEx=/(?::(\d+))?(?::(\d+))?$/;var lineColumnMatch=lineColumnRegEx.exec(url);var lineNumber;var columnNumber;if(typeof(lineColumnMatch[1])==='string'){lineNumber=parseInt(lineColumnMatch[1],10);lineNumber=isNaN(lineNumber)?undefined:lineNumber-1;}
 if(typeof(lineColumnMatch[2])==='string'){columnNumber=parseInt(lineColumnMatch[2],10);columnNumber=isNaN(columnNumber)?undefined:columnNumber-1;}
 return{url:url.substring(0,url.length-lineColumnMatch[0].length),lineNumber:lineNumber,columnNumber:columnNumber};}
-var nativeSuffix=' native';var isNative=rawName.endsWith(nativeSuffix);this.name_=isNative?rawName.slice(0,-nativeSuffix.length):rawName;var urlData=splitLineAndColumn(rawUrl);var url=urlData.url||'';var line=urlData.lineNumber||0;var column=urlData.columnNumber||0;this.sourceInfo_=new tr.model.source_info.JSSourceInfo(url,line,column,isNative,scriptId,state);};TraceCodeEntry.prototype={get id(){return this.id_;},get sourceInfo(){return this.sourceInfo_;},get name(){return this.name_;},set address(address){this.address_=address;},get address(){return this.address_;},set size(size){this.size_=size;},get size(){return this.size_;}};return{TraceCodeEntry:TraceCodeEntry};});'use strict';tr.exportTo('tr.e.importer',function(){function TraceCodeMap(){this.banks_=new Map();}
-TraceCodeMap.prototype={addEntry:function(addressHex,size,name,scriptId){var entry=new tr.e.importer.TraceCodeEntry(this.getAddress_(addressHex),size,name,scriptId);this.addEntry_(addressHex,entry);},moveEntry:function(oldAddressHex,newAddressHex,size){var entry=this.getBank_(oldAddressHex).removeEntry(this.getAddress_(oldAddressHex));if(!entry)
-return;entry.address=this.getAddress_(newAddressHex);entry.size=size;this.addEntry_(newAddressHex,entry);},lookupEntry:function(addressHex){return this.getBank_(addressHex).lookupEntry(this.getAddress_(addressHex));},addEntry_:function(addressHex,entry){this.getBank_(addressHex).addEntry(entry);},getAddress_:function(addressHex){var bankSizeHexDigits=13;addressHex=addressHex.slice(2);return parseInt(addressHex.slice(-bankSizeHexDigits),16);},getBank_:function(addressHex){addressHex=addressHex.slice(2);var bankSizeHexDigits=13;var maxHexDigits=16;var bankName=addressHex.slice(-maxHexDigits,-bankSizeHexDigits);var bank=this.banks_.get(bankName);if(!bank){bank=new TraceCodeBank();this.banks_.set(bankName,bank);}
+var nativeSuffix=' native';var isNative=rawName.endsWith(nativeSuffix);this.name_=isNative?rawName.slice(0,-nativeSuffix.length):rawName;var urlData=splitLineAndColumn(rawUrl);var url=urlData.url||'';var line=urlData.lineNumber||0;var column=urlData.columnNumber||0;this.sourceInfo_=new tr.model.source_info.JSSourceInfo(url,line,column,isNative,scriptId,state);}
+TraceCodeEntry.prototype={get id(){return this.id_;},get sourceInfo(){return this.sourceInfo_;},get name(){return this.name_;},set address(address){this.address_=address;},get address(){return this.address_;},set size(size){this.size_=size;},get size(){return this.size_;}};return{TraceCodeEntry,};});'use strict';tr.exportTo('tr.e.importer',function(){function TraceCodeMap(){this.banks_=new Map();}
+TraceCodeMap.prototype={addEntry:function(addressHex,size,name,scriptId){var entry=new tr.e.importer.TraceCodeEntry(this.getAddress_(addressHex),size,name,scriptId);this.addEntry_(addressHex,entry);},moveEntry:function(oldAddressHex,newAddressHex,size){var entry=this.getBank_(oldAddressHex).removeEntry(this.getAddress_(oldAddressHex));if(!entry)return;entry.address=this.getAddress_(newAddressHex);entry.size=size;this.addEntry_(newAddressHex,entry);},lookupEntry:function(addressHex){return this.getBank_(addressHex).lookupEntry(this.getAddress_(addressHex));},addEntry_:function(addressHex,entry){this.getBank_(addressHex).addEntry(entry);},getAddress_:function(addressHex){var bankSizeHexDigits=13;addressHex=addressHex.slice(2);return parseInt(addressHex.slice(-bankSizeHexDigits),16);},getBank_:function(addressHex){addressHex=addressHex.slice(2);var bankSizeHexDigits=13;var maxHexDigits=16;var bankName=addressHex.slice(-maxHexDigits,-bankSizeHexDigits);var bank=this.banks_.get(bankName);if(!bank){bank=new TraceCodeBank();this.banks_.set(bankName,bank);}
 return bank;}};function TraceCodeBank(){this.entries_=[];}
-TraceCodeBank.prototype={removeEntry:function(address){if(this.entries_.length===0)
-return undefined;var index=tr.b.findLowIndexInSortedArray(this.entries_,function(entry){return entry.address;},address);var entry=this.entries_[index];if(!entry||entry.address!==address)
-return undefined;this.entries_.splice(index,1);return entry;},lookupEntry:function(address){var index=tr.b.findHighIndexInSortedArray(this.entries_,function(e){return address-e.address;})-1;var entry=this.entries_[index];return entry&&address<entry.address+entry.size?entry:undefined;},addEntry:function(newEntry){if(this.entries_.length===0)
-this.entries_.push(newEntry);var endAddress=newEntry.address+newEntry.size;var lastIndex=tr.b.findLowIndexInSortedArray(this.entries_,function(entry){return entry.address;},endAddress);var index;for(index=lastIndex-1;index>=0;--index){var entry=this.entries_[index];var entryEndAddress=entry.address+entry.size;if(entryEndAddress<=newEntry.address)
-break;}
-++index;this.entries_.splice(index,lastIndex-index,newEntry);}};return{TraceCodeMap:TraceCodeMap};});'use strict';tr.exportTo('tr.importer',function(){function ContextProcessor(model){this.model_=model;this.activeContexts_=[];this.stackPerType_={};this.contextCache_={};this.contextSetCache_={};this.cachedEntryForActiveContexts_=undefined;this.seenSnapshots_={};};ContextProcessor.prototype={enterContext:function(contextType,scopedId){var newActiveContexts=[this.getOrCreateContext_(contextType,scopedId)];for(var oldContext of this.activeContexts_){if(oldContext.type===contextType){this.pushContext_(oldContext);}else{newActiveContexts.push(oldContext);}}
-this.activeContexts_=newActiveContexts;this.cachedEntryForActiveContexts_=undefined;},leaveContext:function(contextType,scopedId){this.leaveContextImpl_(context=>context.type===contextType&&context.snapshot.scope===scopedId.scope&&context.snapshot.idRef===scopedId.id);},destroyContext:function(scopedId){tr.b.iterItems(this.stackPerType_,function(contextType,stack){var newLength=0;for(var i=0;i<stack.length;++i){if(stack[i].snapshot.scope!==scopedId.scope||stack[i].snapshot.idRef!==scopedId.id){stack[newLength++]=stack[i];}}
-stack.length=newLength;});this.leaveContextImpl_(context=>context.snapshot.scope===scopedId.scope&&context.snapshot.idRef===scopedId.id);},leaveContextImpl_:function(predicate){var newActiveContexts=[];for(var oldContext of this.activeContexts_){if(predicate(oldContext)){var previousContext=this.popContext_(oldContext.type);if(previousContext)
-newActiveContexts.push(previousContext);}else{newActiveContexts.push(oldContext);}}
-this.activeContexts_=newActiveContexts;this.cachedEntryForActiveContexts_=undefined;},getOrCreateContext_:function(contextType,scopedId){var context={type:contextType,snapshot:{scope:scopedId.scope,idRef:scopedId.id}};var key=this.getContextKey_(context);if(key in this.contextCache_)
-return this.contextCache_[key];this.contextCache_[key]=context;var snapshotKey=this.getSnapshotKey_(scopedId);this.seenSnapshots_[snapshotKey]=true;return context;},pushContext_:function(context){if(!(context.type in this.stackPerType_))
-this.stackPerType_[context.type]=[];this.stackPerType_[context.type].push(context);},popContext_:function(contextType){if(!(contextType in this.stackPerType_))
-return undefined;return this.stackPerType_[contextType].pop();},getContextKey_:function(context){return[context.type,context.snapshot.scope,context.snapshot.idRef].join('\x00');},getSnapshotKey_:function(scopedId){return[scopedId.scope,scopedId.idRef].join('\x00');},get activeContexts(){if(this.cachedEntryForActiveContexts_===undefined){var key=[];for(var context of this.activeContexts_)
-key.push(this.getContextKey_(context));key.sort();key=key.join('\x00');if(key in this.contextSetCache_){this.cachedEntryForActiveContexts_=this.contextSetCache_[key];}else{this.activeContexts_.sort(function(a,b){var keyA=this.getContextKey_(a);var keyB=this.getContextKey_(b);if(keyA<keyB)
-return-1;if(keyA>keyB)
-return 1;return 0;}.bind(this));this.contextSetCache_[key]=Object.freeze(this.activeContexts_);this.cachedEntryForActiveContexts_=this.contextSetCache_[key];}}
-return this.cachedEntryForActiveContexts_;},invalidateContextCacheForSnapshot:function(scopedId){var snapshotKey=this.getSnapshotKey_(scopedId);if(!(snapshotKey in this.seenSnapshots_))
-return;this.contextCache_={};this.contextSetCache_={};this.cachedEntryForActiveContexts_=undefined;this.activeContexts_=this.activeContexts_.map(function(context){if(context.snapshot.scope!==scopedId.scope||context.snapshot.idRef!==scopedId.id)
-return context;return{type:context.type,snapshot:{scope:context.snapshot.scope,idRef:context.snapshot.idRef}};});this.seenSnapshots_={};},};return{ContextProcessor:ContextProcessor};});'use strict';tr.exportTo('tr.model',function(){function YComponent(stableId,yPercentOffset){this.stableId=stableId;this.yPercentOffset=yPercentOffset;}
-YComponent.prototype={toDict:function(){return{stableId:this.stableId,yPercentOffset:this.yPercentOffset};}};function Location(xWorld,yComponents){this.xWorld_=xWorld;this.yComponents_=yComponents;};Location.fromViewCoordinates=function(viewport,viewX,viewY){var dt=viewport.currentDisplayTransform;var xWorld=dt.xViewToWorld(viewX);var yComponents=[];var elem=document.elementFromPoint(viewX+viewport.modelTrackContainer.canvas.offsetLeft,viewY+viewport.modelTrackContainer.canvas.offsetTop);while(elem instanceof tr.ui.tracks.Track){if(elem.eventContainer){var boundRect=elem.getBoundingClientRect();var yPercentOffset=(viewY-boundRect.top)/boundRect.height;yComponents.push(new YComponent(elem.eventContainer.stableId,yPercentOffset));}
+TraceCodeBank.prototype={removeEntry:function(address){if(this.entries_.length===0)return undefined;var index=tr.b.math.findLowIndexInSortedArray(this.entries_,function(entry){return entry.address;},address);var entry=this.entries_[index];if(!entry||entry.address!==address)return undefined;this.entries_.splice(index,1);return entry;},lookupEntry:function(address){var index=tr.b.math.findHighIndexInSortedArray(this.entries_,function(e){return address-e.address;})-1;var entry=this.entries_[index];return entry&&address<entry.address+entry.size?entry:undefined;},addEntry:function(newEntry){if(this.entries_.length===0){this.entries_.push(newEntry);}
+var endAddress=newEntry.address+newEntry.size;var lastIndex=tr.b.math.findLowIndexInSortedArray(this.entries_,function(entry){return entry.address;},endAddress);var index;for(index=lastIndex-1;index>=0;--index){var entry=this.entries_[index];var entryEndAddress=entry.address+entry.size;if(entryEndAddress<=newEntry.address)break;}
+++index;this.entries_.splice(index,lastIndex-index,newEntry);}};return{TraceCodeMap,};});'use strict';tr.exportTo('tr.importer',function(){function ContextProcessor(model){this.model_=model;this.activeContexts_=[];this.stackPerType_={};this.contextCache_={};this.contextSetCache_={};this.cachedEntryForActiveContexts_=undefined;this.seenSnapshots_={};}
+ContextProcessor.prototype={enterContext:function(contextType,scopedId){var newActiveContexts=[this.getOrCreateContext_(contextType,scopedId)];for(var oldContext of this.activeContexts_){if(oldContext.type===contextType){this.pushContext_(oldContext);}else{newActiveContexts.push(oldContext);}}
+this.activeContexts_=newActiveContexts;this.cachedEntryForActiveContexts_=undefined;},leaveContext:function(contextType,scopedId){this.leaveContextImpl_(context=>context.type===contextType&&context.snapshot.scope===scopedId.scope&&context.snapshot.idRef===scopedId.id);},destroyContext:function(scopedId){for(var stack of Object.values(this.stackPerType_)){var newLength=0;for(var i=0;i<stack.length;++i){if(stack[i].snapshot.scope!==scopedId.scope||stack[i].snapshot.idRef!==scopedId.id){stack[newLength++]=stack[i];}}
+stack.length=newLength;}
+this.leaveContextImpl_(context=>context.snapshot.scope===scopedId.scope&&context.snapshot.idRef===scopedId.id);},leaveContextImpl_:function(predicate){var newActiveContexts=[];for(var oldContext of this.activeContexts_){if(predicate(oldContext)){var previousContext=this.popContext_(oldContext.type);if(previousContext){newActiveContexts.push(previousContext);}}else{newActiveContexts.push(oldContext);}}
+this.activeContexts_=newActiveContexts;this.cachedEntryForActiveContexts_=undefined;},getOrCreateContext_:function(contextType,scopedId){var context={type:contextType,snapshot:{scope:scopedId.scope,idRef:scopedId.id}};var key=this.getContextKey_(context);if(key in this.contextCache_){return this.contextCache_[key];}
+this.contextCache_[key]=context;var snapshotKey=this.getSnapshotKey_(scopedId);this.seenSnapshots_[snapshotKey]=true;return context;},pushContext_:function(context){if(!(context.type in this.stackPerType_)){this.stackPerType_[context.type]=[];}
+this.stackPerType_[context.type].push(context);},popContext_:function(contextType){if(!(contextType in this.stackPerType_)){return undefined;}
+return this.stackPerType_[contextType].pop();},getContextKey_:function(context){return[context.type,context.snapshot.scope,context.snapshot.idRef].join('\x00');},getSnapshotKey_:function(scopedId){return[scopedId.scope,scopedId.idRef].join('\x00');},get activeContexts(){if(this.cachedEntryForActiveContexts_===undefined){var key=[];for(var context of this.activeContexts_){key.push(this.getContextKey_(context));}
+key.sort();key=key.join('\x00');if(key in this.contextSetCache_){this.cachedEntryForActiveContexts_=this.contextSetCache_[key];}else{this.activeContexts_.sort(function(a,b){var keyA=this.getContextKey_(a);var keyB=this.getContextKey_(b);if(keyA<keyB){return-1;}
+if(keyA>keyB){return 1;}
+return 0;}.bind(this));this.contextSetCache_[key]=Object.freeze(this.activeContexts_);this.cachedEntryForActiveContexts_=this.contextSetCache_[key];}}
+return this.cachedEntryForActiveContexts_;},invalidateContextCacheForSnapshot:function(scopedId){var snapshotKey=this.getSnapshotKey_(scopedId);if(!(snapshotKey in this.seenSnapshots_))return;this.contextCache_={};this.contextSetCache_={};this.cachedEntryForActiveContexts_=undefined;this.activeContexts_=this.activeContexts_.map(function(context){if(context.snapshot.scope!==scopedId.scope||context.snapshot.idRef!==scopedId.id){return context;}
+return{type:context.type,snapshot:{scope:context.snapshot.scope,idRef:context.snapshot.idRef}};});this.seenSnapshots_={};},};return{ContextProcessor,};});'use strict';tr.exportTo('tr.model',function(){function Annotation(){this.guid_=tr.b.GUID.allocateSimple();this.view_=undefined;}
+Annotation.fromDictIfPossible=function(args){if(args.typeName===undefined){throw new Error('Missing typeName argument');}
+var typeInfo=Annotation.findTypeInfoMatching(function(typeInfo){return typeInfo.metadata.typeName===args.typeName;});if(typeInfo===undefined)return undefined;return typeInfo.constructor.fromDict(args);};Annotation.fromDict=function(){throw new Error('Not implemented');};Annotation.prototype={get guid(){return this.guid_;},onRemove:function(){},toDict:function(){throw new Error('Not implemented');},getOrCreateView:function(viewport){if(!this.view_){this.view_=this.createView_(viewport);}
+return this.view_;},createView_:function(){throw new Error('Not implemented');}};var options=new tr.b.ExtensionRegistryOptions(tr.b.BASIC_REGISTRY_MODE);tr.b.decorateExtensionRegistry(Annotation,options);Annotation.addEventListener('will-register',function(e){if(!e.typeInfo.constructor.hasOwnProperty('fromDict')){throw new Error('Must have fromDict method');}
+if(!e.typeInfo.metadata.typeName){throw new Error('Registered Annotations must provide typeName');}});return{Annotation,};});'use strict';tr.exportTo('tr.model',function(){function YComponent(stableId,yPercentOffset){this.stableId=stableId;this.yPercentOffset=yPercentOffset;}
+YComponent.prototype={toDict:function(){return{stableId:this.stableId,yPercentOffset:this.yPercentOffset};}};function Location(xWorld,yComponents){this.xWorld_=xWorld;this.yComponents_=yComponents;}
+Location.fromViewCoordinates=function(viewport,viewX,viewY){var dt=viewport.currentDisplayTransform;var xWorld=dt.xViewToWorld(viewX);var yComponents=[];var elem=document.elementFromPoint(viewX+viewport.modelTrackContainer.canvas.offsetLeft,viewY+viewport.modelTrackContainer.canvas.offsetTop);while(elem instanceof tr.ui.tracks.Track){if(elem.eventContainer){var boundRect=elem.getBoundingClientRect();var yPercentOffset=(viewY-boundRect.top)/boundRect.height;yComponents.push(new YComponent(elem.eventContainer.stableId,yPercentOffset));}
 elem=elem.parentElement;}
-if(yComponents.length==0)
-return;return new Location(xWorld,yComponents);}
-Location.fromStableIdAndTimestamp=function(viewport,stableId,ts){var xWorld=ts;var yComponents=[];var containerToTrack=viewport.containerToTrackMap;var elem=containerToTrack.getTrackByStableId(stableId);if(!elem)
-return;var firstY=elem.getBoundingClientRect().top;while(elem instanceof tr.ui.tracks.Track){if(elem.eventContainer){var boundRect=elem.getBoundingClientRect();var yPercentOffset=(firstY-boundRect.top)/boundRect.height;yComponents.push(new YComponent(elem.eventContainer.stableId,yPercentOffset));}
+if(yComponents.length===0)return;return new Location(xWorld,yComponents);};Location.fromStableIdAndTimestamp=function(viewport,stableId,ts){var xWorld=ts;var yComponents=[];var containerToTrack=viewport.containerToTrackMap;var elem=containerToTrack.getTrackByStableId(stableId);if(!elem)return;var firstY=elem.getBoundingClientRect().top;while(elem instanceof tr.ui.tracks.Track){if(elem.eventContainer){var boundRect=elem.getBoundingClientRect();var yPercentOffset=(firstY-boundRect.top)/boundRect.height;yComponents.push(new YComponent(elem.eventContainer.stableId,yPercentOffset));}
 elem=elem.parentElement;}
-if(yComponents.length==0)
-return;return new Location(xWorld,yComponents);}
-Location.prototype={get xWorld(){return this.xWorld_;},getContainingTrack:function(viewport){var containerToTrack=viewport.containerToTrackMap;for(var i in this.yComponents_){var yComponent=this.yComponents_[i];var track=containerToTrack.getTrackByStableId(yComponent.stableId);if(track!==undefined)
-return track;}},toViewCoordinates:function(viewport){var dt=viewport.currentDisplayTransform;var containerToTrack=viewport.containerToTrackMap;var viewX=dt.xWorldToView(this.xWorld_);var viewY=-1;for(var index in this.yComponents_){var yComponent=this.yComponents_[index];var track=containerToTrack.getTrackByStableId(yComponent.stableId);if(track!==undefined){var boundRect=track.getBoundingClientRect();viewY=yComponent.yPercentOffset*boundRect.height+boundRect.top;break;}}
-return{viewX:viewX,viewY:viewY};},toDict:function(){return{xWorld:this.xWorld_,yComponents:this.yComponents_};}};return{Location:Location};});'use strict';tr.exportTo('tr.model',function(){function Annotation(){this.guid_=tr.b.GUID.allocateSimple();this.view_=undefined;};Annotation.fromDictIfPossible=function(args){if(args.typeName===undefined)
-throw new Error('Missing typeName argument');var typeInfo=Annotation.findTypeInfoMatching(function(typeInfo){return typeInfo.metadata.typeName===args.typeName;});if(typeInfo===undefined)
-return undefined;return typeInfo.constructor.fromDict(args);};Annotation.fromDict=function(){throw new Error('Not implemented');};Annotation.prototype={get guid(){return this.guid_;},onRemove:function(){},toDict:function(){throw new Error('Not implemented');},getOrCreateView:function(viewport){if(!this.view_)
-this.view_=this.createView_(viewport);return this.view_;},createView_:function(){throw new Error('Not implemented');}};var options=new tr.b.ExtensionRegistryOptions(tr.b.BASIC_REGISTRY_MODE);tr.b.decorateExtensionRegistry(Annotation,options);Annotation.addEventListener('will-register',function(e){if(!e.typeInfo.constructor.hasOwnProperty('fromDict'))
-throw new Error('Must have fromDict method');if(!e.typeInfo.metadata.typeName)
-throw new Error('Registered Annotations must provide typeName');});return{Annotation:Annotation};});'use strict';tr.exportTo('tr.ui.annotations',function(){function AnnotationView(viewport,annotation){}
-AnnotationView.prototype={draw:function(ctx){throw new Error('Not implemented');}};return{AnnotationView:AnnotationView};});'use strict';tr.exportTo('tr.ui.annotations',function(){function RectAnnotationView(viewport,annotation){this.viewport_=viewport;this.annotation_=annotation;}
+if(yComponents.length===0)return;return new Location(xWorld,yComponents);};Location.prototype={get xWorld(){return this.xWorld_;},getContainingTrack:function(viewport){var containerToTrack=viewport.containerToTrackMap;for(var i in this.yComponents_){var yComponent=this.yComponents_[i];var track=containerToTrack.getTrackByStableId(yComponent.stableId);if(track!==undefined)return track;}},toViewCoordinates:function(viewport){var dt=viewport.currentDisplayTransform;var containerToTrack=viewport.containerToTrackMap;var viewX=dt.xWorldToView(this.xWorld_);var viewY=-1;for(var index in this.yComponents_){var yComponent=this.yComponents_[index];var track=containerToTrack.getTrackByStableId(yComponent.stableId);if(track!==undefined){var boundRect=track.getBoundingClientRect();viewY=yComponent.yPercentOffset*boundRect.height+boundRect.top;break;}}
+return{viewX:viewX,viewY:viewY};},toDict:function(){return{xWorld:this.xWorld_,yComponents:this.yComponents_};}};return{Location,};});'use strict';tr.exportTo('tr.ui.annotations',function(){function AnnotationView(viewport,annotation){}
+AnnotationView.prototype={draw:function(ctx){throw new Error('Not implemented');}};return{AnnotationView,};});'use strict';tr.exportTo('tr.ui.annotations',function(){function RectAnnotationView(viewport,annotation){this.viewport_=viewport;this.annotation_=annotation;}
 RectAnnotationView.prototype={__proto__:tr.ui.annotations.AnnotationView.prototype,draw:function(ctx){var dt=this.viewport_.currentDisplayTransform;var startCoords=this.annotation_.startLocation.toViewCoordinates(this.viewport_);var endCoords=this.annotation_.endLocation.toViewCoordinates(this.viewport_);var startY=startCoords.viewY-ctx.canvas.getBoundingClientRect().top;var sizeY=endCoords.viewY-startCoords.viewY;if(startY+sizeY<0){startY=sizeY;}else if(startY<0){startY=0;}
-ctx.fillStyle=this.annotation_.fillStyle;ctx.fillRect(startCoords.viewX,startY,endCoords.viewX-startCoords.viewX,sizeY);}};return{RectAnnotationView:RectAnnotationView};});'use strict';tr.exportTo('tr.model',function(){function RectAnnotation(start,end){tr.model.Annotation.apply(this,arguments);this.startLocation_=start;this.endLocation_=end;this.fillStyle='rgba(255, 180, 0, 0.3)';}
-RectAnnotation.fromDict=function(dict){var args=dict.args;var startLoc=new tr.model.Location(args.start.xWorld,args.start.yComponents);var endLoc=new tr.model.Location(args.end.xWorld,args.end.yComponents);return new tr.model.RectAnnotation(startLoc,endLoc);}
-RectAnnotation.prototype={__proto__:tr.model.Annotation.prototype,get startLocation(){return this.startLocation_;},get endLocation(){return this.endLocation_;},toDict:function(){return{typeName:'rect',args:{start:this.startLocation.toDict(),end:this.endLocation.toDict()}};},createView_:function(viewport){return new tr.ui.annotations.RectAnnotationView(viewport,this);}};tr.model.Annotation.register(RectAnnotation,{typeName:'rect'});return{RectAnnotation:RectAnnotation};});'use strict';tr.exportTo('tr.ui.annotations',function(){function CommentBoxAnnotationView(viewport,annotation){this.viewport_=viewport;this.annotation_=annotation;this.textArea_=undefined;this.styleWidth=250;this.styleHeight=50;this.fontSize=10;this.rightOffset=50;this.topOffset=25;}
-CommentBoxAnnotationView.prototype={__proto__:tr.ui.annotations.AnnotationView.prototype,removeTextArea:function(){Polymer.dom(Polymer.dom(this.textArea_).parentNode).removeChild(this.textArea_);},draw:function(ctx){var coords=this.annotation_.location.toViewCoordinates(this.viewport_);if(coords.viewX<0){if(this.textArea_)
-this.textArea_.style.visibility='hidden';return;}
+ctx.fillStyle=this.annotation_.fillStyle;ctx.fillRect(startCoords.viewX,startY,endCoords.viewX-startCoords.viewX,sizeY);}};return{RectAnnotationView,};});'use strict';tr.exportTo('tr.model',function(){function RectAnnotation(start,end){tr.model.Annotation.apply(this,arguments);this.startLocation_=start;this.endLocation_=end;this.fillStyle='rgba(255, 180, 0, 0.3)';}
+RectAnnotation.fromDict=function(dict){var args=dict.args;var startLoc=new tr.model.Location(args.start.xWorld,args.start.yComponents);var endLoc=new tr.model.Location(args.end.xWorld,args.end.yComponents);return new tr.model.RectAnnotation(startLoc,endLoc);};RectAnnotation.prototype={__proto__:tr.model.Annotation.prototype,get startLocation(){return this.startLocation_;},get endLocation(){return this.endLocation_;},toDict:function(){return{typeName:'rect',args:{start:this.startLocation.toDict(),end:this.endLocation.toDict()}};},createView_:function(viewport){return new tr.ui.annotations.RectAnnotationView(viewport,this);}};tr.model.Annotation.register(RectAnnotation,{typeName:'rect'});return{RectAnnotation,};});'use strict';tr.exportTo('tr.ui.annotations',function(){function CommentBoxAnnotationView(viewport,annotation){this.viewport_=viewport;this.annotation_=annotation;this.textArea_=undefined;this.styleWidth=250;this.styleHeight=50;this.fontSize=10;this.rightOffset=50;this.topOffset=25;}
+CommentBoxAnnotationView.prototype={__proto__:tr.ui.annotations.AnnotationView.prototype,removeTextArea:function(){Polymer.dom(Polymer.dom(this.textArea_).parentNode).removeChild(this.textArea_);},draw:function(ctx){var coords=this.annotation_.location.toViewCoordinates(this.viewport_);if(coords.viewX<0){if(this.textArea_){this.textArea_.style.visibility='hidden';}
+return;}
 if(!this.textArea_){this.textArea_=document.createElement('textarea');this.textArea_.style.position='absolute';this.textArea_.readOnly=true;this.textArea_.value=this.annotation_.text;this.textArea_.style.zIndex=1;Polymer.dom(Polymer.dom(ctx.canvas).parentNode).appendChild(this.textArea_);}
 this.textArea_.style.width=this.styleWidth+'px';this.textArea_.style.height=this.styleHeight+'px';this.textArea_.style.fontSize=this.fontSize+'px';this.textArea_.style.visibility='visible';this.textArea_.style.left=coords.viewX+ctx.canvas.getBoundingClientRect().left+
 this.rightOffset+'px';this.textArea_.style.top=coords.viewY-ctx.canvas.getBoundingClientRect().top-
 this.topOffset+'px';ctx.strokeStyle='rgb(0, 0, 0)';ctx.lineWidth=2;ctx.beginPath();tr.ui.b.drawLine(ctx,coords.viewX,coords.viewY-ctx.canvas.getBoundingClientRect().top,coords.viewX+this.rightOffset,coords.viewY-this.topOffset-
-ctx.canvas.getBoundingClientRect().top);ctx.stroke();}};return{CommentBoxAnnotationView:CommentBoxAnnotationView};});'use strict';tr.exportTo('tr.model',function(){function CommentBoxAnnotation(location,text){tr.model.Annotation.apply(this,arguments);this.location=location;this.text=text;}
-CommentBoxAnnotation.fromDict=function(dict){var args=dict.args;var location=new tr.model.Location(args.location.xWorld,args.location.yComponents);return new tr.model.CommentBoxAnnotation(location,args.text);};CommentBoxAnnotation.prototype={__proto__:tr.model.Annotation.prototype,onRemove:function(){this.view_.removeTextArea();},toDict:function(){return{typeName:'comment_box',args:{text:this.text,location:this.location.toDict()}};},createView_:function(viewport){return new tr.ui.annotations.CommentBoxAnnotationView(viewport,this);}};tr.model.Annotation.register(CommentBoxAnnotation,{typeName:'comment_box'});return{CommentBoxAnnotation:CommentBoxAnnotation};});'use strict';tr.exportTo('tr.model',function(){function HeapEntry(heapDump,leafStackFrame,objectTypeName,size,count){this.heapDump=heapDump;this.leafStackFrame=leafStackFrame;this.objectTypeName=objectTypeName;this.size=size;this.count=count;}
-function HeapDump(processMemoryDump,allocatorName){this.processMemoryDump=processMemoryDump;this.allocatorName=allocatorName;this.entries=[];}
-HeapDump.prototype={addEntry:function(leafStackFrame,objectTypeName,size,count){var entry=new HeapEntry(this,leafStackFrame,objectTypeName,size,count);this.entries.push(entry);return entry;}};return{HeapEntry:HeapEntry,HeapDump:HeapDump};});'use strict';tr.exportTo('tr.model',function(){function ScopedId(scope,id){if(scope===undefined){throw new Error('Scope should be defined. Use \''+
+ctx.canvas.getBoundingClientRect().top);ctx.stroke();}};return{CommentBoxAnnotationView,};});'use strict';tr.exportTo('tr.model',function(){function CommentBoxAnnotation(location,text){tr.model.Annotation.apply(this,arguments);this.location=location;this.text=text;}
+CommentBoxAnnotation.fromDict=function(dict){var args=dict.args;var location=new tr.model.Location(args.location.xWorld,args.location.yComponents);return new tr.model.CommentBoxAnnotation(location,args.text);};CommentBoxAnnotation.prototype={__proto__:tr.model.Annotation.prototype,onRemove:function(){this.view_.removeTextArea();},toDict:function(){return{typeName:'comment_box',args:{text:this.text,location:this.location.toDict()}};},createView_:function(viewport){return new tr.ui.annotations.CommentBoxAnnotationView(viewport,this);}};tr.model.Annotation.register(CommentBoxAnnotation,{typeName:'comment_box'});return{CommentBoxAnnotation,};});'use strict';tr.exportTo('tr.model',function(){function ScopedId(scope,id,pid){if(scope===undefined){throw new Error('Scope should be defined. Use \''+
 tr.model.OBJECT_DEFAULT_SCOPE+'\' as the default scope.');}
-this.scope=scope;this.id=id;}
-ScopedId.prototype={toString:function(){return'{scope: '+this.scope+', id: '+this.id+'}';}};return{ScopedId:ScopedId};});'use strict';tr.exportTo('tr.ui.annotations',function(){function XMarkerAnnotationView(viewport,annotation){this.viewport_=viewport;this.annotation_=annotation;}
-XMarkerAnnotationView.prototype={__proto__:tr.ui.annotations.AnnotationView.prototype,draw:function(ctx){var dt=this.viewport_.currentDisplayTransform;var viewX=dt.xWorldToView(this.annotation_.timestamp);ctx.beginPath();tr.ui.b.drawLine(ctx,viewX,0,viewX,ctx.canvas.height);ctx.strokeStyle=this.annotation_.strokeStyle;ctx.stroke();}};return{XMarkerAnnotationView:XMarkerAnnotationView};});'use strict';tr.exportTo('tr.model',function(){function XMarkerAnnotation(timestamp){tr.model.Annotation.apply(this,arguments);this.timestamp=timestamp;this.strokeStyle='rgba(0, 0, 255, 0.5)';}
-XMarkerAnnotation.fromDict=function(dict){return new XMarkerAnnotation(dict.args.timestamp);}
-XMarkerAnnotation.prototype={__proto__:tr.model.Annotation.prototype,toDict:function(){return{typeName:'xmarker',args:{timestamp:this.timestamp}};},createView_:function(viewport){return new tr.ui.annotations.XMarkerAnnotationView(viewport,this);}};tr.model.Annotation.register(XMarkerAnnotation,{typeName:'xmarker'});return{XMarkerAnnotation:XMarkerAnnotation};});'use strict';tr.exportTo('tr.e.importer',function(){var Base64=tr.b.Base64;var deepCopy=tr.b.deepCopy;var ColorScheme=tr.b.ColorScheme;function getEventColor(event,opt_customName){if(event.cname)
-return ColorScheme.getColorIdForReservedName(event.cname);else if(opt_customName||event.name){return ColorScheme.getColorIdForGeneralPurposeString(opt_customName||event.name);}}
-var PRODUCER='producer';var CONSUMER='consumer';var STEP='step';var BACKGROUND=tr.model.ContainerMemoryDump.LevelOfDetail.BACKGROUND;var LIGHT=tr.model.ContainerMemoryDump.LevelOfDetail.LIGHT;var DETAILED=tr.model.ContainerMemoryDump.LevelOfDetail.DETAILED;var MEMORY_DUMP_LEVEL_OF_DETAIL_ORDER=[undefined,BACKGROUND,LIGHT,DETAILED];var GLOBAL_MEMORY_ALLOCATOR_DUMP_PREFIX='global/';var ASYNC_CLOCK_SYNC_EVENT_TITLE_PREFIX='ClockSyncEvent.';var BYTE_STAT_NAME_MAP={'pc':'privateCleanResident','pd':'privateDirtyResident','sc':'sharedCleanResident','sd':'sharedDirtyResident','pss':'proportionalResident','sw':'swapped'};var WEAK_MEMORY_ALLOCATOR_DUMP_FLAG=1<<0;var OBJECT_TYPE_NAME_PATTERNS=[{prefix:'const char *WTF::getStringWithTypeName() [T = ',suffix:']'},{prefix:'const char* WTF::getStringWithTypeName() [with T = ',suffix:']'},{prefix:'const char *__cdecl WTF::getStringWithTypeName<',suffix:'>(void)'}];var SUBTRACE_FIELDS=new Set(['powerTraceAsString','systemTraceEvents',]);var NON_METADATA_FIELDS=new Set(['samples','stackFrames','traceAnnotations','traceEvents']);for(var subtraceField in SUBTRACE_FIELDS)
-NON_METADATA_FIELDS.add(subtraceField);function TraceEventImporter(model,eventData){this.importPriority=1;this.model_=model;this.events_=undefined;this.sampleEvents_=undefined;this.stackFrameEvents_=undefined;this.subtraces_=[];this.eventsWereFromString_=false;this.softwareMeasuredCpuCount_=undefined;this.allAsyncEvents_=[];this.allFlowEvents_=[];this.allObjectEvents_=[];this.contextProcessorPerThread={};this.traceEventSampleStackFramesByName_={};this.v8ProcessCodeMaps_={};this.v8ProcessRootStackFrame_={};this.v8SamplingData_=[];this.asyncClockSyncStart_=undefined;this.asyncClockSyncFinish_=undefined;this.allMemoryDumpEvents_={};this.objectTypeNameMap_={};this.clockDomainId_=tr.model.ClockDomainId.UNKNOWN_CHROME_LEGACY;this.toModelTime_=undefined;if(typeof(eventData)==='string'||eventData instanceof String){eventData=eventData.trim();if(eventData[0]==='['){eventData=eventData.replace(/\s*,\s*$/,'');if(eventData[eventData.length-1]!==']')
-eventData=eventData+']';}
+this.scope=scope;this.id=id;this.pid=pid;}
+ScopedId.prototype={toString:function(){var pidStr=this.pid===undefined?'':'pid: '+this.pid+', ';return'{'+pidStr+'scope: '+this.scope+', id: '+this.id+'}';},toStringWithDelimiter:function(delim){return(this.pid===undefined?'':this.pid)+delim+
+this.scope+delim+this.id;}};return{ScopedId,};});'use strict';tr.exportTo('tr.ui.annotations',function(){function XMarkerAnnotationView(viewport,annotation){this.viewport_=viewport;this.annotation_=annotation;}
+XMarkerAnnotationView.prototype={__proto__:tr.ui.annotations.AnnotationView.prototype,draw:function(ctx){var dt=this.viewport_.currentDisplayTransform;var viewX=dt.xWorldToView(this.annotation_.timestamp);ctx.beginPath();tr.ui.b.drawLine(ctx,viewX,0,viewX,ctx.canvas.height);ctx.strokeStyle=this.annotation_.strokeStyle;ctx.stroke();}};return{XMarkerAnnotationView,};});'use strict';tr.exportTo('tr.model',function(){function XMarkerAnnotation(timestamp){tr.model.Annotation.apply(this,arguments);this.timestamp=timestamp;this.strokeStyle='rgba(0, 0, 255, 0.5)';}
+XMarkerAnnotation.fromDict=function(dict){return new XMarkerAnnotation(dict.args.timestamp);};XMarkerAnnotation.prototype={__proto__:tr.model.Annotation.prototype,toDict:function(){return{typeName:'xmarker',args:{timestamp:this.timestamp}};},createView_:function(viewport){return new tr.ui.annotations.XMarkerAnnotationView(viewport,this);}};tr.model.Annotation.register(XMarkerAnnotation,{typeName:'xmarker'});return{XMarkerAnnotation,};});'use strict';tr.exportTo('tr.e.importer',function(){var Base64=tr.b.Base64;var deepCopy=tr.b.deepCopy;var ColorScheme=tr.b.ColorScheme;var HeapDumpTraceEventImporter=tr.e.importer.HeapDumpTraceEventImporter;function getEventColor(event,opt_customName){if(event.cname){return ColorScheme.getColorIdForReservedName(event.cname);}else if(opt_customName||event.name){return ColorScheme.getColorIdForGeneralPurposeString(opt_customName||event.name);}}
+function isLegacyChromeClockSyncEvent(event){return event.name!==undefined&&event.name.startsWith(LEGACY_CHROME_CLOCK_SYNC_EVENT_NAME_PREFIX)&&((event.ph==='S')||(event.ph==='F'));}
+var PRODUCER='producer';var CONSUMER='consumer';var STEP='step';var BACKGROUND=tr.model.ContainerMemoryDump.LevelOfDetail.BACKGROUND;var LIGHT=tr.model.ContainerMemoryDump.LevelOfDetail.LIGHT;var DETAILED=tr.model.ContainerMemoryDump.LevelOfDetail.DETAILED;var MEMORY_DUMP_LEVEL_OF_DETAIL_ORDER=[undefined,BACKGROUND,LIGHT,DETAILED];var GLOBAL_MEMORY_ALLOCATOR_DUMP_PREFIX='global/';var LEGACY_CHROME_CLOCK_SYNC_EVENT_NAME_PREFIX='ClockSyncEvent.';var BYTE_STAT_NAME_MAP={'pc':'privateCleanResident','pd':'privateDirtyResident','sc':'sharedCleanResident','sd':'sharedDirtyResident','pss':'proportionalResident','sw':'swapped'};var WEAK_MEMORY_ALLOCATOR_DUMP_FLAG=1<<0;var OBJECT_TYPE_NAME_PATTERNS=[{prefix:'const char *WTF::getStringWithTypeName() [T = ',suffix:']'},{prefix:'const char* WTF::getStringWithTypeName() [with T = ',suffix:']'},{prefix:'const char *__cdecl WTF::getStringWithTypeName<',suffix:'>(void)'}];var SUBTRACE_FIELDS=new Set(['powerTraceAsString','systemTraceEvents',]);var NON_METADATA_FIELDS=new Set(['samples','stackFrames','traceAnnotations','traceEvents',...SUBTRACE_FIELDS]);function TraceEventImporter(model,eventData){this.importPriority=1;this.model_=model;this.events_=undefined;this.sampleEvents_=undefined;this.stackFrameEvents_=undefined;this.stackFrameTree_=new tr.model.ProfileTree();this.subtraces_=[];this.eventsWereFromString_=false;this.softwareMeasuredCpuCount_=undefined;this.allAsyncEvents_=[];this.allFlowEvents_=[];this.allObjectEvents_=[];this.contextProcessorPerThread={};this.traceEventSampleStackFramesByName_={};this.v8ProcessCodeMaps_={};this.v8ProcessRootStackFrame_={};this.v8SamplingData_=[];this.profileTrees_=new Map();this.profileInfo_=new Map();this.legacyChromeClockSyncStartEvent_=undefined;this.legacyChromeClockSyncFinishEvent_=undefined;this.allMemoryDumpEvents_={};this.objectTypeNameMap_={};this.clockDomainId_=tr.model.ClockDomainId.UNKNOWN_CHROME_LEGACY;this.toModelTime_=undefined;if(typeof(eventData)==='string'||eventData instanceof String){eventData=eventData.trim();if(eventData[0]==='['){eventData=eventData.replace(/\s*,\s*$/,'');if(eventData[eventData.length-1]!==']'){eventData=eventData+']';}}
 this.events_=JSON.parse(eventData);this.eventsWereFromString_=true;}else{this.events_=eventData;}
-this.traceAnnotations_=this.events_.traceAnnotations;if(this.events_.traceEvents){var container=this.events_;this.events_=this.events_.traceEvents;for(var subtraceField of SUBTRACE_FIELDS)
-if(container[subtraceField])
-this.subtraces_.push(container[subtraceField]);this.sampleEvents_=container.samples;this.stackFrameEvents_=container.stackFrames;if(container.displayTimeUnit){var unitName=container.displayTimeUnit;var unit=tr.b.TimeDisplayModes[unitName];if(unit===undefined){throw new Error('Unit '+unitName+' is not supported.');}
+this.traceAnnotations_=this.events_.traceAnnotations;if(this.events_.traceEvents){var container=this.events_;this.events_=this.events_.traceEvents;for(var subtraceField of SUBTRACE_FIELDS){if(container[subtraceField]){this.subtraces_.push(container[subtraceField]);}}
+this.sampleEvents_=container.samples;this.stackFrameEvents_=container.stackFrames;if(container.displayTimeUnit){var unitName=container.displayTimeUnit;var unit=tr.b.TimeDisplayModes[unitName];if(unit===undefined){throw new Error('Unit '+unitName+' is not supported.');}
 this.model_.intrinsicTimeUnit=unit;}
-for(var fieldName in container){if(NON_METADATA_FIELDS.has(fieldName))
-continue;this.model_.metadata.push({name:fieldName,value:container[fieldName]});if(fieldName==='metadata'){var metadata=container[fieldName];if(metadata['highres-ticks'])
-this.model_.isTimeHighResolution=metadata['highres-ticks'];if(metadata['clock-domain'])
-this.clockDomainId_=metadata['clock-domain'];}}}}
+for(var fieldName in container){if(NON_METADATA_FIELDS.has(fieldName))continue;this.model_.metadata.push({name:fieldName,value:container[fieldName]});if(fieldName==='metadata'){var metadata=container[fieldName];if(metadata['highres-ticks']){this.model_.isTimeHighResolution=metadata['highres-ticks'];}
+if(metadata['clock-domain']){this.clockDomainId_=metadata['clock-domain'];}}}}}
 TraceEventImporter.canImport=function(eventData){if(typeof(eventData)==='string'||eventData instanceof String){eventData=eventData.trim();return eventData[0]==='{'||eventData[0]==='[';}
-if(eventData instanceof Array&&eventData.length&&eventData[0].ph)
-return true;if(eventData.traceEvents){if(eventData.traceEvents instanceof Array){if(eventData.traceEvents.length&&eventData.traceEvents[0].ph)
-return true;if(eventData.samples.length&&eventData.stackFrames!==undefined)
-return true;}}
-return false;};TraceEventImporter.prototype={__proto__:tr.importer.Importer.prototype,get importerName(){return'TraceEventImporter';},extractSubtraces:function(){var subtraces=this.subtraces_;this.subtraces_=[];return subtraces;},deepCopyIfNeeded_:function(obj){if(obj===undefined)
-obj={};if(this.eventsWereFromString_)
-return obj;return deepCopy(obj);},deepCopyAlways_:function(obj){if(obj===undefined)
-obj={};return deepCopy(obj);},processAsyncEvent:function(event){var thread=this.model_.getOrCreateProcess(event.pid).getOrCreateThread(event.tid);this.allAsyncEvents_.push({sequenceNumber:this.allAsyncEvents_.length,event:event,thread:thread});},processFlowEvent:function(event,opt_slice){var thread=this.model_.getOrCreateProcess(event.pid).getOrCreateThread(event.tid);this.allFlowEvents_.push({refGuid:tr.b.GUID.getLastSimpleGuid(),sequenceNumber:this.allFlowEvents_.length,event:event,slice:opt_slice,thread:thread});},processCounterEvent:function(event){var ctr_name;if(event.id!==undefined)
-ctr_name=event.name+'['+event.id+']';else
-ctr_name=event.name;var ctr=this.model_.getOrCreateProcess(event.pid).getOrCreateCounter(event.cat,ctr_name);var reservedColorId=event.cname?getEventColor(event):undefined;if(ctr.numSeries===0){for(var seriesName in event.args){var colorId=reservedColorId||getEventColor(event,ctr.name+'.'+seriesName);ctr.addSeries(new tr.model.CounterSeries(seriesName,colorId));}
+if(eventData instanceof Array&&eventData.length&&eventData[0].ph){return true;}
+if(eventData.traceEvents){if(eventData.traceEvents instanceof Array){if(eventData.traceEvents.length&&eventData.traceEvents[0].ph){return true;}
+if(eventData.samples.length&&eventData.stackFrames!==undefined){return true;}}}
+return false;};TraceEventImporter.scopedIdForEvent_=function(event){var scope=event.scope||tr.model.OBJECT_DEFAULT_SCOPE;var pid=undefined;if(event.id!==undefined){if(event.id2!==undefined){throw new Error('Event has both id and id2');}
+var pid=tr.model.LOCAL_ID_PHASES.has(event.ph)?event.pid:undefined;return new tr.model.ScopedId(scope,event.id,pid);}else if(event.id2!==undefined){if(event.id2.global!==undefined){return new tr.model.ScopedId(scope,event.id2.global);}else if(event.id2.local!==undefined){return new tr.model.ScopedId(scope,event.id2.local,event.pid);}
+throw new Error('Event that uses id2 must have either a global or local ID');}
+return undefined;};TraceEventImporter.prototype={__proto__:tr.importer.Importer.prototype,get importerName(){return'TraceEventImporter';},extractSubtraces:function(){var subtraces=this.subtraces_;this.subtraces_=[];return subtraces;},deepCopyIfNeeded_:function(obj){if(obj===undefined)obj={};if(this.eventsWereFromString_)return obj;return deepCopy(obj);},deepCopyAlways_:function(obj){if(obj===undefined)obj={};return deepCopy(obj);},processAsyncEvent:function(event){var thread=this.model_.getOrCreateProcess(event.pid).getOrCreateThread(event.tid);this.allAsyncEvents_.push({sequenceNumber:this.allAsyncEvents_.length,event:event,thread:thread});},processFlowEvent:function(event,opt_slice){var thread=this.model_.getOrCreateProcess(event.pid).getOrCreateThread(event.tid);this.allFlowEvents_.push({refGuid:tr.b.GUID.getLastSimpleGuid(),sequenceNumber:this.allFlowEvents_.length,event:event,slice:opt_slice,thread:thread});},processCounterEvent:function(event){var ctrName;if(event.id!==undefined){ctrName=event.name+'['+event.id+']';}else{ctrName=event.name;}
+var ctr=this.model_.getOrCreateProcess(event.pid).getOrCreateCounter(event.cat,ctrName);var reservedColorId=event.cname?getEventColor(event):undefined;if(ctr.numSeries===0){for(var seriesName in event.args){var colorId=reservedColorId||getEventColor(event,ctr.name+'.'+seriesName);ctr.addSeries(new tr.model.CounterSeries(seriesName,colorId));}
 if(ctr.numSeries===0){this.model_.importWarning({type:'counter_parse_error',message:'Expected counter '+event.name+' to have at least one argument to use as a value.'});delete ctr.parent.counters[ctr.name];return;}}
-var ts=this.toModelTimeFromUs_(event.ts);ctr.series.forEach(function(series){var val=event.args[series.name]?event.args[series.name]:0;series.addCounterSample(ts,val);});},scopedIdForEvent_:function(event){return new tr.model.ScopedId(event.scope||tr.model.OBJECT_DEFAULT_SCOPE,event.id);},processObjectEvent:function(event){var thread=this.model_.getOrCreateProcess(event.pid).getOrCreateThread(event.tid);this.allObjectEvents_.push({sequenceNumber:this.allObjectEvents_.length,event:event,thread:thread});if(thread.guid in this.contextProcessorPerThread){var processor=this.contextProcessorPerThread[thread.guid];var scopedId=this.scopedIdForEvent_(event);if(event.ph==='D')
-processor.destroyContext(scopedId);processor.invalidateContextCacheForSnapshot(scopedId);}},processContextEvent:function(event){var thread=this.model_.getOrCreateProcess(event.pid).getOrCreateThread(event.tid);if(!(thread.guid in this.contextProcessorPerThread)){this.contextProcessorPerThread[thread.guid]=new tr.importer.ContextProcessor(this.model_);}
-var scopedId=this.scopedIdForEvent_(event);var contextType=event.name;var processor=this.contextProcessorPerThread[thread.guid];if(event.ph==='('){processor.enterContext(contextType,scopedId);}else if(event.ph===')'){processor.leaveContext(contextType,scopedId);}else{this.model_.importWarning({type:'unknown_context_phase',message:'Unknown context event phase: '+event.ph+'.'});}},setContextsFromThread_:function(thread,slice){if(thread.guid in this.contextProcessorPerThread){slice.contexts=this.contextProcessorPerThread[thread.guid].activeContexts;}},processDurationEvent:function(event){var thread=this.model_.getOrCreateProcess(event.pid).getOrCreateThread(event.tid);var ts=this.toModelTimeFromUs_(event.ts);if(!thread.sliceGroup.isTimestampValidForBeginOrEnd(ts)){this.model_.importWarning({type:'duration_parse_error',message:'Timestamps are moving backward.'});return;}
-if(event.ph==='B'){var slice=thread.sliceGroup.beginSlice(event.cat,event.name,this.toModelTimeFromUs_(event.ts),this.deepCopyIfNeeded_(event.args),this.toModelTimeFromUs_(event.tts),event.argsStripped,getEventColor(event));slice.startStackFrame=this.getStackFrameForEvent_(event);this.setContextsFromThread_(thread,slice);}else if(event.ph==='I'||event.ph==='i'||event.ph==='R'){if(event.s!==undefined&&event.s!=='t')
-throw new Error('This should never happen');thread.sliceGroup.beginSlice(event.cat,event.name,this.toModelTimeFromUs_(event.ts),this.deepCopyIfNeeded_(event.args),this.toModelTimeFromUs_(event.tts),event.argsStripped,getEventColor(event));var slice=thread.sliceGroup.endSlice(this.toModelTimeFromUs_(event.ts),this.toModelTimeFromUs_(event.tts));slice.startStackFrame=this.getStackFrameForEvent_(event);slice.endStackFrame=undefined;}else{if(!thread.sliceGroup.openSliceCount){this.model_.importWarning({type:'duration_parse_error',message:'E phase event without a matching B phase event.'});return;}
-var slice=thread.sliceGroup.endSlice(this.toModelTimeFromUs_(event.ts),this.toModelTimeFromUs_(event.tts),getEventColor(event));if(event.name&&slice.title!=event.name){this.model_.importWarning({type:'title_match_error',message:'Titles do not match. Title is '+
+var ts=this.toModelTimeFromUs_(event.ts);ctr.series.forEach(function(series){var val=event.args[series.name]?event.args[series.name]:0;series.addCounterSample(ts,val);});},processObjectEvent:function(event){var thread=this.model_.getOrCreateProcess(event.pid).getOrCreateThread(event.tid);this.allObjectEvents_.push({sequenceNumber:this.allObjectEvents_.length,event:event,thread:thread});if(thread.guid in this.contextProcessorPerThread){var processor=this.contextProcessorPerThread[thread.guid];var scopedId=TraceEventImporter.scopedIdForEvent_(event);if(event.ph==='D'){processor.destroyContext(scopedId);}
+processor.invalidateContextCacheForSnapshot(scopedId);}},processContextEvent:function(event){var thread=this.model_.getOrCreateProcess(event.pid).getOrCreateThread(event.tid);if(!(thread.guid in this.contextProcessorPerThread)){this.contextProcessorPerThread[thread.guid]=new tr.importer.ContextProcessor(this.model_);}
+var scopedId=TraceEventImporter.scopedIdForEvent_(event);var contextType=event.name;var processor=this.contextProcessorPerThread[thread.guid];if(event.ph==='('){processor.enterContext(contextType,scopedId);}else if(event.ph===')'){processor.leaveContext(contextType,scopedId);}else{this.model_.importWarning({type:'unknown_context_phase',message:'Unknown context event phase: '+event.ph+'.'});}},setContextsFromThread_:function(thread,slice){if(thread.guid in this.contextProcessorPerThread){slice.contexts=this.contextProcessorPerThread[thread.guid].activeContexts;}},processDurationEvent:function(event){var thread=this.model_.getOrCreateProcess(event.pid).getOrCreateThread(event.tid);var ts=this.toModelTimeFromUs_(event.ts);if(!thread.sliceGroup.isTimestampValidForBeginOrEnd(ts)){this.model_.importWarning({type:'duration_parse_error',message:'Timestamps are moving backward.'});return;}
+if(event.ph==='B'){var slice=thread.sliceGroup.beginSlice(event.cat,event.name,this.toModelTimeFromUs_(event.ts),this.deepCopyIfNeeded_(event.args),this.toModelTimeFromUs_(event.tts),event.argsStripped,getEventColor(event));slice.startStackFrame=this.getStackFrameForEvent_(event);this.setContextsFromThread_(thread,slice);}else if(event.ph==='I'||event.ph==='i'||event.ph==='R'){if(event.s!==undefined&&event.s!=='t'){throw new Error('This should never happen');}
+thread.sliceGroup.beginSlice(event.cat,event.name,this.toModelTimeFromUs_(event.ts),this.deepCopyIfNeeded_(event.args),this.toModelTimeFromUs_(event.tts),event.argsStripped,getEventColor(event));var slice=thread.sliceGroup.endSlice(this.toModelTimeFromUs_(event.ts),this.toModelTimeFromUs_(event.tts));slice.startStackFrame=this.getStackFrameForEvent_(event);slice.endStackFrame=undefined;}else{if(!thread.sliceGroup.openSliceCount){this.model_.importWarning({type:'duration_parse_error',message:'E phase event without a matching B phase event.'});return;}
+var slice=thread.sliceGroup.endSlice(this.toModelTimeFromUs_(event.ts),this.toModelTimeFromUs_(event.tts),getEventColor(event));if(event.name&&slice.title!==event.name){this.model_.importWarning({type:'title_match_error',message:'Titles do not match. Title is '+
 slice.title+' in openSlice, and is '+
 event.name+' in endSlice'});}
 slice.endStackFrame=this.getStackFrameForEvent_(event);this.mergeArgsInto_(slice.args,event.args,slice.title);}},mergeArgsInto_:function(dstArgs,srcArgs,eventName){for(var arg in srcArgs){if(dstArgs[arg]!==undefined){this.model_.importWarning({type:'arg_merge_error',message:'Different phases of '+eventName+' provided values for argument '+arg+'.'+' The last provided value will be used.'});}
-dstArgs[arg]=this.deepCopyIfNeeded_(srcArgs[arg]);}},processCompleteEvent:function(event){if(event.cat!==undefined&&event.cat.indexOf('trace_event_overhead')>-1)
-return undefined;var thread=this.model_.getOrCreateProcess(event.pid).getOrCreateThread(event.tid);if(event.flow_out){if(event.flow_in)
-event.flowPhase=STEP;else
-event.flowPhase=PRODUCER;}else if(event.flow_in){event.flowPhase=CONSUMER;}
-var slice=thread.sliceGroup.pushCompleteSlice(event.cat,event.name,this.toModelTimeFromUs_(event.ts),this.maybeToModelTimeFromUs_(event.dur),this.maybeToModelTimeFromUs_(event.tts),this.maybeToModelTimeFromUs_(event.tdur),this.deepCopyIfNeeded_(event.args),event.argsStripped,getEventColor(event),event.bind_id);slice.startStackFrame=this.getStackFrameForEvent_(event);slice.endStackFrame=this.getStackFrameForEvent_(event,true);this.setContextsFromThread_(thread,slice);return slice;},processJitCodeEvent:function(event){if(this.v8ProcessCodeMaps_[event.pid]===undefined)
-this.v8ProcessCodeMaps_[event.pid]=new tr.e.importer.TraceCodeMap();var map=this.v8ProcessCodeMaps_[event.pid];var data=event.args.data;if(event.name==='JitCodeMoved')
-map.moveEntry(data.code_start,data.new_code_start,data.code_len);else
-map.addEntry(data.code_start,data.code_len,data.name,data.script_id);},processMetadataEvent:function(event){if(event.name==='JitCodeAdded'||event.name==='JitCodeMoved'){this.v8SamplingData_.push(event);return;}
-if(event.argsStripped)
-return;if(event.name==='process_name'){var process=this.model_.getOrCreateProcess(event.pid);process.name=event.args.name;}else if(event.name==='process_labels'){var process=this.model_.getOrCreateProcess(event.pid);var labels=event.args.labels.split(',');for(var i=0;i<labels.length;i++)
-process.addLabelIfNeeded(labels[i]);}else if(event.name==='process_sort_index'){var process=this.model_.getOrCreateProcess(event.pid);process.sortIndex=event.args.sort_index;}else if(event.name==='thread_name'){var thread=this.model_.getOrCreateProcess(event.pid).getOrCreateThread(event.tid);thread.name=event.args.name;}else if(event.name==='thread_sort_index'){var thread=this.model_.getOrCreateProcess(event.pid).getOrCreateThread(event.tid);thread.sortIndex=event.args.sort_index;}else if(event.name==='num_cpus'){var n=event.args.number;if(this.softwareMeasuredCpuCount_!==undefined)
-n=Math.max(n,this.softwareMeasuredCpuCount_);this.softwareMeasuredCpuCount_=n;}else if(event.name==='stackFrames'){var stackFrames=event.args.stackFrames;if(stackFrames===undefined){this.model_.importWarning({type:'metadata_parse_error',message:'No stack frames found in a \''+event.name+'\' metadata event'});}else{this.importStackFrames_(stackFrames,'p'+event.pid+':');}}else if(event.name==='typeNames'){var objectTypeNameMap=event.args.typeNames;if(objectTypeNameMap===undefined){this.model_.importWarning({type:'metadata_parse_error',message:'No mapping from object type IDs to names found in a \''+
+dstArgs[arg]=this.deepCopyIfNeeded_(srcArgs[arg]);}},processCompleteEvent:function(event){if(event.cat!==undefined&&event.cat.indexOf('trace_event_overhead')>-1){return undefined;}
+var thread=this.model_.getOrCreateProcess(event.pid).getOrCreateThread(event.tid);if(event.flow_out){if(event.flow_in){event.flowPhase=STEP;}else{event.flowPhase=PRODUCER;}}else if(event.flow_in){event.flowPhase=CONSUMER;}
+var slice=thread.sliceGroup.pushCompleteSlice(event.cat,event.name,this.toModelTimeFromUs_(event.ts),this.maybeToModelTimeFromUs_(event.dur),this.maybeToModelTimeFromUs_(event.tts),this.maybeToModelTimeFromUs_(event.tdur),this.deepCopyIfNeeded_(event.args),event.argsStripped,getEventColor(event),event.bind_id);slice.startStackFrame=this.getStackFrameForEvent_(event);slice.endStackFrame=this.getStackFrameForEvent_(event,true);this.setContextsFromThread_(thread,slice);return slice;},processJitCodeEvent:function(event){if(this.v8ProcessCodeMaps_[event.pid]===undefined){this.v8ProcessCodeMaps_[event.pid]=new tr.e.importer.TraceCodeMap();}
+var map=this.v8ProcessCodeMaps_[event.pid];var data=event.args.data;if(event.name==='JitCodeMoved'){map.moveEntry(data.code_start,data.new_code_start,data.code_len);}else{map.addEntry(data.code_start,data.code_len,data.name,data.script_id);}},processMetadataEvent:function(event){if(event.name==='JitCodeAdded'||event.name==='JitCodeMoved'){this.v8SamplingData_.push(event);return;}
+if(event.argsStripped)return;if(event.name==='process_name'){var process=this.model_.getOrCreateProcess(event.pid);process.name=event.args.name;}else if(event.name==='process_labels'){var process=this.model_.getOrCreateProcess(event.pid);var labels=event.args.labels.split(',');for(var i=0;i<labels.length;i++){process.addLabelIfNeeded(labels[i]);}}else if(event.name==='process_uptime_seconds'){var process=this.model_.getOrCreateProcess(event.pid);process.uptime_seconds=event.args.uptime;}else if(event.name==='process_sort_index'){var process=this.model_.getOrCreateProcess(event.pid);process.sortIndex=event.args.sort_index;}else if(event.name==='thread_name'){var thread=this.model_.getOrCreateProcess(event.pid).getOrCreateThread(event.tid);thread.name=event.args.name;}else if(event.name==='thread_sort_index'){var thread=this.model_.getOrCreateProcess(event.pid).getOrCreateThread(event.tid);thread.sortIndex=event.args.sort_index;}else if(event.name==='num_cpus'){var n=event.args.number;if(this.softwareMeasuredCpuCount_!==undefined){n=Math.max(n,this.softwareMeasuredCpuCount_);}
+this.softwareMeasuredCpuCount_=n;}else if(event.name==='stackFrames'){var stackFrames=event.args.stackFrames;if(stackFrames===undefined){this.model_.importWarning({type:'metadata_parse_error',message:'No stack frames found in a \''+event.name+'\' metadata event'});}else{this.importStackFrames_(stackFrames,'p'+event.pid+':');}}else if(event.name==='typeNames'){var objectTypeNameMap=event.args.typeNames;if(objectTypeNameMap===undefined){this.model_.importWarning({type:'metadata_parse_error',message:'No mapping from object type IDs to names found in a \''+
 event.name+'\' metadata event'});}else{this.importObjectTypeNameMap_(objectTypeNameMap,event.pid);}}else if(event.name==='TraceConfig'){this.model_.metadata.push({name:'TraceConfig',value:event.args.value});}else{this.model_.importWarning({type:'metadata_parse_error',message:'Unrecognized metadata name: '+event.name});}},processInstantEvent:function(event){if(event.name==='JitCodeAdded'||event.name==='JitCodeMoved'){this.v8SamplingData_.push(event);return;}
 if(event.s==='t'||event.s===undefined){this.processDurationEvent(event);return;}
 var constructor;switch(event.s){case'g':constructor=tr.model.GlobalInstantEvent;break;case'p':constructor=tr.model.ProcessInstantEvent;break;default:this.model_.importWarning({type:'instant_parse_error',message:'I phase event with unknown "s" field value.'});return;}
-var instantEvent=new constructor(event.cat,event.name,getEventColor(event),this.toModelTimeFromUs_(event.ts),this.deepCopyIfNeeded_(event.args));switch(instantEvent.type){case tr.model.InstantEventType.GLOBAL:this.model_.instantEvents.push(instantEvent);break;case tr.model.InstantEventType.PROCESS:var process=this.model_.getOrCreateProcess(event.pid);process.instantEvents.push(instantEvent);break;default:throw new Error('Unknown instant event type: '+event.s);}},processV8Sample:function(event){var data=event.args.data;if(data.vm_state==='js'&&!data.stack.length)
-return;var rootStackFrame=this.v8ProcessRootStackFrame_[event.pid];if(!rootStackFrame){rootStackFrame=new tr.model.StackFrame(undefined,'v8-root-stack-frame','v8-root-stack-frame',0);this.v8ProcessRootStackFrame_[event.pid]=rootStackFrame;}
-function findChildWithEntryID(stackFrame,entryID){return tr.b.findFirstInArray(stackFrame.children,function(child){return child.entryID===entryID;});}
-var model=this.model_;function addStackFrame(lastStackFrame,entry){var childFrame=findChildWithEntryID(lastStackFrame,entry.id);if(childFrame)
-return childFrame;var frame=new tr.model.StackFrame(lastStackFrame,tr.b.GUID.allocateSimple(),entry.name,ColorScheme.getColorIdForGeneralPurposeString(entry.name),entry.sourceInfo);frame.entryID=entry.id;model.addStackFrame(frame);return frame;}
-var lastStackFrame=rootStackFrame;if(data.stack.length>0&&this.v8ProcessCodeMaps_[event.pid]){var map=this.v8ProcessCodeMaps_[event.pid];data.stack.reverse();for(var i=0;i<data.stack.length;i++){var entry=map.lookupEntry(data.stack[i]);if(entry===undefined){entry={id:'unknown',name:'unknown',sourceInfo:undefined};}
-lastStackFrame=addStackFrame(lastStackFrame,entry);}}else{var entry={id:data.vm_state,name:data.vm_state,sourceInfo:undefined};lastStackFrame=addStackFrame(lastStackFrame,entry);}
-var thread=this.model_.getOrCreateProcess(event.pid).getOrCreateThread(event.tid);var sample=new tr.model.Sample(undefined,thread,'V8 Sample',this.toModelTimeFromUs_(event.ts),lastStackFrame,1,this.deepCopyIfNeeded_(event.args));this.model_.samples.push(sample);},processTraceSampleEvent:function(event){if(event.name==='V8Sample'){this.v8SamplingData_.push(event);return;}
-var stackFrame=this.getStackFrameForEvent_(event);if(stackFrame===undefined){stackFrame=this.traceEventSampleStackFramesByName_[event.name];}
-if(stackFrame===undefined){var id='te-'+tr.b.GUID.allocateSimple();stackFrame=new tr.model.StackFrame(undefined,id,event.name,ColorScheme.getColorIdForGeneralPurposeString(event.name));this.model_.addStackFrame(stackFrame);this.traceEventSampleStackFramesByName_[event.name]=stackFrame;}
-var thread=this.model_.getOrCreateProcess(event.pid).getOrCreateThread(event.tid);var sample=new tr.model.Sample(undefined,thread,'Trace Event Sample',this.toModelTimeFromUs_(event.ts),stackFrame,1,this.deepCopyIfNeeded_(event.args));this.setContextsFromThread_(thread,sample);this.model_.samples.push(sample);},processMemoryDumpEvent:function(event){if(event.ph!=='v')
-throw new Error('Invalid memory dump event phase "'+event.ph+'".');var dumpId=event.id;if(dumpId===undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Memory dump event (phase \''+event.ph+'\') without a dump ID.'});return;}
+var instantEvent=new constructor(event.cat,event.name,getEventColor(event),this.toModelTimeFromUs_(event.ts),this.deepCopyIfNeeded_(event.args));switch(instantEvent.type){case tr.model.InstantEventType.GLOBAL:this.model_.instantEvents.push(instantEvent);break;case tr.model.InstantEventType.PROCESS:var process=this.model_.getOrCreateProcess(event.pid);process.instantEvents.push(instantEvent);break;default:throw new Error('Unknown instant event type: '+event.s);}},getOrCreateProfileTree_:function(sampleType,id){if(!this.profileTrees_.has(sampleType)){this.profileTrees_.set(sampleType,new Map());}
+var profileTreeMap=this.profileTrees_.get(sampleType);if(profileTreeMap.has(id)){return profileTreeMap.get(id);}
+var profileTree=new tr.model.ProfileTree();profileTreeMap.set(id,profileTree);let info=this.profileInfo_.get(id);if(info!==undefined){profileTree.startTime=info.startTime;profileTree.pid=info.pid;profileTree.tid=info.tid;}
+return profileTree;},processSample:function(event){if(event.args===undefined||event.args.data===undefined){return;}
+if(event.id===undefined){throw new Error('No event ID in sample');}
+var data=event.args.data;if(data['startTime']!==undefined){this.profileInfo_.set(event.id,{startTime:data['startTime'],pid:event.pid,tid:event.tid});}
+let timeDeltas=data['timeDeltas'];for(let sampleType in data){if(sampleType==='timeDeltas'||sampleType==='startTime'){continue;}
+if(data[sampleType]['samples']&&timeDeltas&&data[sampleType]['samples'].length!==timeDeltas.length){throw new Error('samples and timeDeltas array should have same length');}
+let profileTree=this.getOrCreateProfileTree_(sampleType,event['id']);let nodes=data[sampleType]['nodes'];let samples=data[sampleType]['samples'];if(nodes!==undefined){for(let node of nodes){var ProfileNodeType=tr.model.ProfileNode.subTypes.getConstructor(undefined,sampleType);let profileNode=ProfileNodeType.constructFromObject(profileTree,node);if(profileNode===undefined){continue;}
+profileTree.add(profileNode);}}
+if(samples!==undefined){let thread=this.model_.getOrCreateProcess(profileTree.pid).getOrCreateThread(profileTree.tid);for(let i=0,len=samples.length;i<len;++i){let node=profileTree.getNode(samples[i]);profileTree.endTime+=timeDeltas[i];let start=this.toModelTimeFromUs_(profileTree.endTime);this.model_.samples.push(new tr.model.Sample(start,node.sampleTitle,node,thread));}}}},processLegacyV8Sample:function(event){var data=event.args.data;var sampleType='legacySample';var ProfileNodeType=tr.model.ProfileNode.subTypes.getConstructor(undefined,sampleType);if(data.vm_state==='js'&&!data.stack.length)return;var profileTree=this.getOrCreateProfileTree_(sampleType,event.pid);if(profileTree.getNode(-1)===undefined){profileTree.add(new ProfileNodeType(-1,{url:'',scriptId:-1,functionName:'unknown'},undefined));}
+let node=undefined;if(data.stack.length>0&&this.v8ProcessCodeMaps_[event.pid]){let map=this.v8ProcessCodeMaps_[event.pid];data.stack.reverse();let parentNode=undefined;for(let i=0;i<data.stack.length;i++){let entry=map.lookupEntry(data.stack[i]);if(entry===undefined){node=profileTree.getNode(-1);}else{node=profileTree.getNode(entry.id);if(node===undefined){let sourceInfo=entry.sourceInfo;node=new ProfileNodeType(entry.id,{functionName:entry.name,url:entry.sourceInfo.file,lineNumber:sourceInfo.line!==-1?sourceInfo.line:undefined,columnNumber:sourceInfo.column!==-1?sourceInfo.column:undefined,scriptid:entry.sourceInfo.scriptId},parentNode);profileTree.add(node);}}
+parentNode=node;}}else{node=profileTree.getNode(data.vm_state);if(node===undefined){node=new ProfileNodeType(data.vm_state,{url:'',functionName:data.vm_state},undefined);profileTree.add(node);}}
+var thread=this.model_.getOrCreateProcess(event.pid).getOrCreateThread(event.tid);this.model_.samples.push(new tr.model.Sample(this.toModelTimeFromUs_(event.ts),node.sampleTitle,node,thread));},processTraceSampleEvent:function(event){if(event.name==='V8Sample'||event.name.startsWith('Profile')){this.v8SamplingData_.push(event);return;}
+var node=this.stackFrameTree_.getNode(event.name);if(node===undefined&&event.sf!==undefined){node=this.stackFrameTree_.getNode('g'+event.sf);}
+if(node===undefined){var id=event.name;if(event.sf){id='g'+event.sf;}
+var ProfileNodeType=tr.model.ProfileNode.subTypes.getConstructor(undefined,'legacySample');node=this.stackFrameTree_.add(new ProfileNodeType(id,{functionName:event.name},undefined));}
+var thread=this.model_.getOrCreateProcess(event.pid).getOrCreateThread(event.tid);var sample=new tr.model.Sample(this.toModelTimeFromUs_(event.ts),'Trace Event Sample',node,thread,undefined,1,this.deepCopyIfNeeded_(event.args));this.setContextsFromThread_(thread,sample);this.model_.samples.push(sample);},processMemoryDumpEvent:function(event){if(event.ph!=='v'){throw new Error('Invalid memory dump event phase "'+event.ph+'".');}
+var dumpId=event.id;if(dumpId===undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Memory dump event (phase \''+event.ph+'\') without a dump ID.'});return;}
 var pid=event.pid;if(pid===undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Memory dump event (phase\''+event.ph+'\', dump ID \''+
 dumpId+'\') without a PID.'});return;}
-var allEvents=this.allMemoryDumpEvents_;var dumpIdEvents=allEvents[dumpId];if(dumpIdEvents===undefined)
-allEvents[dumpId]=dumpIdEvents={};var processEvents=dumpIdEvents[pid];if(processEvents===undefined)
-dumpIdEvents[pid]=processEvents=[];processEvents.push(event);},processClockSyncEvent:function(event){if(event.ph!=='c')
-throw new Error('Invalid clock sync event phase "'+event.ph+'".');var syncId=event.args.sync_id;if(syncId===undefined){this.model_.importWarning({type:'clock_sync_parse_error',message:'Clock sync at time '+event.ts+' without an ID.'});return;}
-if(event.args&&event.args.issue_ts!==undefined){this.model_.clockSyncManager.addClockSyncMarker(this.clockDomainId_,syncId,tr.b.Unit.timestampFromUs(event.args.issue_ts),tr.b.Unit.timestampFromUs(event.ts));}else{this.model_.clockSyncManager.addClockSyncMarker(this.clockDomainId_,syncId,tr.b.Unit.timestampFromUs(event.ts));}},processV8Events:function(){this.v8SamplingData_.sort(function(a,b){if(a.ts!==b.ts)
-return a.ts-b.ts;if(a.ph==='M'||a.ph==='I')
-return-1;else if(b.ph==='M'||b.ph==='I')
-return 1;return 0;});var length=this.v8SamplingData_.length;for(var i=0;i<length;++i){var event=this.v8SamplingData_[i];if(event.ph==='M'||event.ph==='I'){this.processJitCodeEvent(event);}else if(event.ph==='P'){this.processV8Sample(event);}}},initBackcompatClockSyncEventTracker_:function(event){if(event.name!==undefined&&event.name.startsWith(ASYNC_CLOCK_SYNC_EVENT_TITLE_PREFIX)&&event.ph==='S')
-this.asyncClockSyncStart_=event;if(event.name!==undefined&&event.name.startsWith(ASYNC_CLOCK_SYNC_EVENT_TITLE_PREFIX)&&event.ph==='F')
-this.asyncClockSyncFinish_=event;if(this.asyncClockSyncStart_==undefined||this.asyncClockSyncFinish_==undefined)
-return;var syncId=this.asyncClockSyncStart_.name.substring(ASYNC_CLOCK_SYNC_EVENT_TITLE_PREFIX.length);if(syncId!==this.asyncClockSyncFinish_.name.substring(ASYNC_CLOCK_SYNC_EVENT_TITLE_PREFIX.length)){throw new Error('Inconsistent clock sync id of async clock sync '+'events.');}
-var clockSyncEvent={ph:'c',args:{sync_id:syncId,issue_ts:this.asyncClockSyncStart_.ts},ts:this.asyncClockSyncFinish_.ts,};this.asyncClockSyncStart_=undefined;this.asyncClockSyncFinish_=undefined;return clockSyncEvent;},importClockSyncMarkers:function(){var asyncClockSyncStart,asyncClockSyncFinish;for(var i=0;i<this.events_.length;i++){var event=this.events_[i];var possibleBackCompatClockSyncEvent=this.initBackcompatClockSyncEventTracker_(event);if(possibleBackCompatClockSyncEvent)
-this.processClockSyncEvent(possibleBackCompatClockSyncEvent);if(event.ph!=='c')
-continue;var eventSizeInBytes=this.model_.importOptions.trackDetailedModelStats?JSON.stringify(event).length:undefined;this.model_.stats.willProcessBasicTraceEvent('clock_sync',event.cat,event.name,event.ts,eventSizeInBytes);this.processClockSyncEvent(event);}},importEvents:function(){if(this.stackFrameEvents_)
-this.importStackFrames_(this.stackFrameEvents_,'g');if(this.traceAnnotations_)
-this.importAnnotations_();var importOptions=this.model_.importOptions;var trackDetailedModelStats=importOptions.trackDetailedModelStats;var modelStats=this.model_.stats;var events=this.events_;for(var eI=0;eI<events.length;eI++){var event=events[eI];if(event.args==='__stripped__'){event.argsStripped=true;event.args=undefined;}
-var eventSizeInBytes;if(trackDetailedModelStats)
-eventSizeInBytes=JSON.stringify(event).length;else
-eventSizeInBytes=undefined;if(event.ph==='B'||event.ph==='E'){modelStats.willProcessBasicTraceEvent('begin_end (non-compact)',event.cat,event.name,event.ts,eventSizeInBytes);this.processDurationEvent(event);}else if(event.ph==='X'){modelStats.willProcessBasicTraceEvent('begin_end (compact)',event.cat,event.name,event.ts,eventSizeInBytes);var slice=this.processCompleteEvent(event);if(slice!==undefined&&event.bind_id!==undefined)
-this.processFlowEvent(event,slice);}else if(event.ph==='b'||event.ph==='e'||event.ph==='n'||event.ph==='S'||event.ph==='F'||event.ph==='T'||event.ph==='p'){modelStats.willProcessBasicTraceEvent('async',event.cat,event.name,event.ts,eventSizeInBytes);this.processAsyncEvent(event);}else if(event.ph==='I'||event.ph==='i'||event.ph==='R'){modelStats.willProcessBasicTraceEvent('instant',event.cat,event.name,event.ts,eventSizeInBytes);this.processInstantEvent(event);}else if(event.ph==='P'){modelStats.willProcessBasicTraceEvent('samples',event.cat,event.name,event.ts,eventSizeInBytes);this.processTraceSampleEvent(event);}else if(event.ph==='C'){modelStats.willProcessBasicTraceEvent('counters',event.cat,event.name,event.ts,eventSizeInBytes);this.processCounterEvent(event);}else if(event.ph==='M'){modelStats.willProcessBasicTraceEvent('metadata',event.cat,event.name,event.ts,eventSizeInBytes);this.processMetadataEvent(event);}else if(event.ph==='N'||event.ph==='D'||event.ph==='O'){modelStats.willProcessBasicTraceEvent('objects',event.cat,event.name,event.ts,eventSizeInBytes);this.processObjectEvent(event);}else if(event.ph==='s'||event.ph==='t'||event.ph==='f'){modelStats.willProcessBasicTraceEvent('flows',event.cat,event.name,event.ts,eventSizeInBytes);this.processFlowEvent(event);}else if(event.ph==='v'){modelStats.willProcessBasicTraceEvent('memory_dumps',event.cat,event.name,event.ts,eventSizeInBytes);this.processMemoryDumpEvent(event);}else if(event.ph==='('||event.ph===')'){this.processContextEvent(event);}else if(event.ph==='c'){}else{modelStats.willProcessBasicTraceEvent('unknown',event.cat,event.name,event.ts,eventSizeInBytes);this.model_.importWarning({type:'parse_error',message:'Unrecognized event phase: '+
+var allEvents=this.allMemoryDumpEvents_;var dumpIdEvents=allEvents[dumpId];if(dumpIdEvents===undefined){allEvents[dumpId]=dumpIdEvents={};}
+var processEvents=dumpIdEvents[pid];if(processEvents===undefined){dumpIdEvents[pid]=processEvents=[];}
+processEvents.push(event);},processClockSyncEvent:function(event){if(event.ph!=='c'){throw new Error('Invalid clock sync event phase "'+event.ph+'".');}
+var syncId=event.args.sync_id;if(syncId===undefined){this.model_.importWarning({type:'clock_sync_parse_error',message:'Clock sync at time '+event.ts+' without an ID.'});return;}
+if(event.args&&event.args.issue_ts!==undefined){this.model_.clockSyncManager.addClockSyncMarker(this.clockDomainId_,syncId,tr.b.Unit.timestampFromUs(event.args.issue_ts),tr.b.Unit.timestampFromUs(event.ts));}else{this.model_.clockSyncManager.addClockSyncMarker(this.clockDomainId_,syncId,tr.b.Unit.timestampFromUs(event.ts));}},processLegacyChromeClockSyncEvent:function(event){if(event.ph==='S'){this.legacyChromeClockSyncStartEvent_=event;}else if(event.ph==='F'){this.legacyChromeClockSyncFinishEvent_=event;}
+if(this.legacyChromeClockSyncStartEvent_===undefined||this.legacyChromeClockSyncFinishEvent_===undefined){return;}
+var startSyncId=this.legacyChromeClockSyncStartEvent_.name.substring(LEGACY_CHROME_CLOCK_SYNC_EVENT_NAME_PREFIX.length);var finishSyncId=this.legacyChromeClockSyncFinishEvent_.name.substring(LEGACY_CHROME_CLOCK_SYNC_EVENT_NAME_PREFIX.length);if(startSyncId!==finishSyncId){throw new Error('Inconsistent clock sync ID of legacy Chrome clock sync events');}
+this.model_.clockSyncManager.addClockSyncMarker(this.clockDomainId_,startSyncId,tr.b.Unit.timestampFromUs(this.legacyChromeClockSyncStartEvent_.ts),tr.b.Unit.timestampFromUs(this.legacyChromeClockSyncFinishEvent_.ts));},processV8Events:function(){this.v8SamplingData_.sort(function(a,b){if(a.ts!==b.ts)return a.ts-b.ts;if(a.ph==='M'||a.ph==='I'){return-1;}else if(b.ph==='M'||b.ph==='I'){return 1;}
+return 0;});var length=this.v8SamplingData_.length;for(var i=0;i<length;++i){var event=this.v8SamplingData_[i];if(event.ph==='M'||event.ph==='I'){this.processJitCodeEvent(event);}else if(event.ph==='P'){if(event.name.startsWith('Profile')){this.processSample(event);}else{this.processLegacyV8Sample(event);}}}},importClockSyncMarkers:function(){for(var i=0;i<this.events_.length;i++){var event=this.events_[i];var isLegacyChromeClockSync=isLegacyChromeClockSyncEvent(event);if(event.ph!=='c'&&!isLegacyChromeClockSync)continue;var eventSizeInBytes=this.model_.importOptions.trackDetailedModelStats?JSON.stringify(event).length:undefined;this.model_.stats.willProcessBasicTraceEvent('clock_sync',event.cat,event.name,event.ts,eventSizeInBytes);if(isLegacyChromeClockSync){this.processLegacyChromeClockSyncEvent(event);}else{this.processClockSyncEvent(event);}}},importEvents:function(){if(this.stackFrameEvents_){this.importStackFrames_(this.stackFrameEvents_,'g');}
+if(this.traceAnnotations_){this.importAnnotations_();}
+var importOptions=this.model_.importOptions;var trackDetailedModelStats=importOptions.trackDetailedModelStats;var modelStats=this.model_.stats;var events=this.events_;for(var eI=0;eI<events.length;eI++){var event=events[eI];if(event.args==='__stripped__'){event.argsStripped=true;event.args=undefined;}
+var eventSizeInBytes;if(trackDetailedModelStats){eventSizeInBytes=JSON.stringify(event).length;}else{eventSizeInBytes=undefined;}
+if(event.ph==='B'||event.ph==='E'){modelStats.willProcessBasicTraceEvent('begin_end (non-compact)',event.cat,event.name,event.ts,eventSizeInBytes);this.processDurationEvent(event);}else if(event.ph==='X'){modelStats.willProcessBasicTraceEvent('begin_end (compact)',event.cat,event.name,event.ts,eventSizeInBytes);var slice=this.processCompleteEvent(event);if(slice!==undefined&&event.bind_id!==undefined){this.processFlowEvent(event,slice);}}else if(event.ph==='b'||event.ph==='e'||event.ph==='n'||event.ph==='S'||event.ph==='F'||event.ph==='T'||event.ph==='p'){modelStats.willProcessBasicTraceEvent('async',event.cat,event.name,event.ts,eventSizeInBytes);this.processAsyncEvent(event);}else if(event.ph==='I'||event.ph==='i'||event.ph==='R'){modelStats.willProcessBasicTraceEvent('instant',event.cat,event.name,event.ts,eventSizeInBytes);this.processInstantEvent(event);}else if(event.ph==='P'){modelStats.willProcessBasicTraceEvent('samples',event.cat,event.name,event.ts,eventSizeInBytes);this.processTraceSampleEvent(event);}else if(event.ph==='C'){modelStats.willProcessBasicTraceEvent('counters',event.cat,event.name,event.ts,eventSizeInBytes);this.processCounterEvent(event);}else if(event.ph==='M'){modelStats.willProcessBasicTraceEvent('metadata',event.cat,event.name,event.ts,eventSizeInBytes);this.processMetadataEvent(event);}else if(event.ph==='N'||event.ph==='D'||event.ph==='O'){modelStats.willProcessBasicTraceEvent('objects',event.cat,event.name,event.ts,eventSizeInBytes);this.processObjectEvent(event);}else if(event.ph==='s'||event.ph==='t'||event.ph==='f'){modelStats.willProcessBasicTraceEvent('flows',event.cat,event.name,event.ts,eventSizeInBytes);this.processFlowEvent(event);}else if(event.ph==='v'){modelStats.willProcessBasicTraceEvent('memory_dumps',event.cat,event.name,event.ts,eventSizeInBytes);this.processMemoryDumpEvent(event);}else if(event.ph==='('||event.ph===')'){this.processContextEvent(event);}else if(event.ph==='c'){}else{modelStats.willProcessBasicTraceEvent('unknown',event.cat,event.name,event.ts,eventSizeInBytes);this.model_.importWarning({type:'parse_error',message:'Unrecognized event phase: '+
 event.ph+' ('+event.name+')'});}}
-this.processV8Events();tr.b.iterItems(this.v8ProcessRootStackFrame_,function(name,frame){frame.removeAllChildren();});},importStackFrames_:function(rawStackFrames,idPrefix){var model=this.model_;for(var id in rawStackFrames){var rawStackFrame=rawStackFrames[id];var fullId=idPrefix+id;var textForColor=rawStackFrame.category?rawStackFrame.category:rawStackFrame.name;var stackFrame=new tr.model.StackFrame(undefined,fullId,rawStackFrame.name,ColorScheme.getColorIdForGeneralPurposeString(textForColor));model.addStackFrame(stackFrame);}
-for(var id in rawStackFrames){var fullId=idPrefix+id;var stackFrame=model.stackFrames[fullId];if(stackFrame===undefined)
-throw new Error('Internal error');var rawStackFrame=rawStackFrames[id];var parentId=rawStackFrame.parent;var parentStackFrame;if(parentId===undefined){parentStackFrame=undefined;}else{var parentFullId=idPrefix+parentId;parentStackFrame=model.stackFrames[parentFullId];if(parentStackFrame===undefined){this.model_.importWarning({type:'metadata_parse_error',message:'Missing parent frame with ID '+parentFullId+' for stack frame \''+stackFrame.name+'\' (ID '+fullId+').'});}}
-stackFrame.parentFrame=parentStackFrame;}},importObjectTypeNameMap_:function(rawObjectTypeNameMap,pid){if(pid in this.objectTypeNameMap_){this.model_.importWarning({type:'metadata_parse_error',message:'Mapping from object type IDs to names provided for pid='+
+this.processV8Events();for(var frame of Object.values(this.v8ProcessRootStackFrame_)){frame.removeAllChildren();}},importStackFrames_:function(rawStackFrames,idPrefix){var model=this.model_;for(var id in rawStackFrames){var rawStackFrame=rawStackFrames[id];var fullId=idPrefix+id;var textForColor=rawStackFrame.category?rawStackFrame.category:rawStackFrame.name;var stackFrame=new tr.model.StackFrame(undefined,fullId,rawStackFrame.name,ColorScheme.getColorIdForGeneralPurposeString(textForColor));model.addStackFrame(stackFrame);}
+for(var id in rawStackFrames){var fullId=idPrefix+id;var stackFrame=model.stackFrames[fullId];if(stackFrame===undefined){throw new Error('Internal error');}
+var rawStackFrame=rawStackFrames[id];var parentId=rawStackFrame.parent;var parentStackFrame;if(parentId===undefined){parentStackFrame=undefined;}else{var parentFullId=idPrefix+parentId;parentStackFrame=model.stackFrames[parentFullId];if(parentStackFrame===undefined){this.model_.importWarning({type:'metadata_parse_error',message:'Missing parent frame with ID '+parentFullId+' for stack frame \''+stackFrame.name+'\' (ID '+fullId+').'});}}
+stackFrame.parentFrame=parentStackFrame;}
+var ProfileNodeType=tr.model.ProfileNode.subTypes.getConstructor(undefined,'legacySample');if(idPrefix==='g'){for(var id in rawStackFrames){var rawStackFrame=rawStackFrames[id];var textForColor=rawStackFrame.category?rawStackFrame.category:rawStackFrame.name;var node=this.stackFrameTree_.add(new ProfileNodeType('g'+id,{functionName:rawStackFrame.name},undefined));node.colorId=ColorScheme.getColorIdForGeneralPurposeString(textForColor);node.parentId=rawStackFrame.parent;}
+for(var id in rawStackFrames){var node=this.stackFrameTree_.getNode('g'+id);var parentId=node.parentId;var parentNode=undefined;if(parentId!==undefined){parentNode=this.stackFrameTree_.getNode('g'+parentId);if(parentNode===undefined){this.model_.importWarning({type:'metadata_parse_error',message:'Missing parent frame with ID '+parentId+' for stack frame \''+node.name+'\' (ID '+node.id+').'});}
+node.parentNode=parentNode;}}}},importObjectTypeNameMap_:function(rawObjectTypeNameMap,pid){if(pid in this.objectTypeNameMap_){this.model_.importWarning({type:'metadata_parse_error',message:'Mapping from object type IDs to names provided for pid='+
 pid+' multiple times.'});return;}
 var objectTypeNamePrefix=undefined;var objectTypeNameSuffix=undefined;var objectTypeNameMap={};for(var objectTypeId in rawObjectTypeNameMap){var rawObjectTypeName=rawObjectTypeNameMap[objectTypeId];if(objectTypeNamePrefix===undefined){for(var i=0;i<OBJECT_TYPE_NAME_PATTERNS.length;i++){var pattern=OBJECT_TYPE_NAME_PATTERNS[i];if(rawObjectTypeName.startsWith(pattern.prefix)&&rawObjectTypeName.endsWith(pattern.suffix)){objectTypeNamePrefix=pattern.prefix;objectTypeNameSuffix=pattern.suffix;break;}}}
 if(objectTypeNamePrefix!==undefined&&rawObjectTypeName.startsWith(objectTypeNamePrefix)&&rawObjectTypeName.endsWith(objectTypeNameSuffix)){objectTypeNameMap[objectTypeId]=rawObjectTypeName.substring(objectTypeNamePrefix.length,rawObjectTypeName.length-objectTypeNameSuffix.length);}else{objectTypeNameMap[objectTypeId]=rawObjectTypeName;}}
@@ -5438,95 +5438,77 @@
 this.model_.addAnnotation(annotation);}},finalizeImport:function(){if(this.softwareMeasuredCpuCount_!==undefined){this.model_.kernel.softwareMeasuredCpuCount=this.softwareMeasuredCpuCount_;}
 this.createAsyncSlices_();this.createFlowSlices_();this.createExplicitObjects_();this.createImplicitObjects_();this.createMemoryDumps_();},getStackFrameForEvent_:function(event,opt_lookForEndEvent){var sf;var stack;if(opt_lookForEndEvent){sf=event.esf;stack=event.estack;}else{sf=event.sf;stack=event.stack;}
 if(stack!==undefined&&sf!==undefined){this.model_.importWarning({type:'stack_frame_and_stack_error',message:'Event at '+event.ts+' cannot have both a stack and a stackframe.'});return undefined;}
-if(stack!==undefined)
-return this.model_.resolveStackToStackFrame_(event.pid,stack);if(sf===undefined)
-return undefined;var stackFrame=this.model_.stackFrames['g'+sf];if(stackFrame===undefined){this.model_.importWarning({type:'sample_import_error',message:'No frame for '+sf});return;}
-return stackFrame;},resolveStackToStackFrame_:function(pid,stack){return undefined;},importSampleData:function(){if(!this.sampleEvents_)
-return;var m=this.model_;var events=this.sampleEvents_;if(this.events_.length===0){for(var i=0;i<events.length;i++){var event=events[i];m.getOrCreateProcess(event.tid).getOrCreateThread(event.tid);}}
+if(stack!==undefined){return this.model_.resolveStackToStackFrame_(event.pid,stack);}
+if(sf===undefined)return undefined;var stackFrame=this.model_.stackFrames['g'+sf];if(stackFrame===undefined){this.model_.importWarning({type:'sample_import_error',message:'No frame for '+sf});return;}
+return stackFrame;},resolveStackToStackFrame_:function(pid,stack){return undefined;},importSampleData:function(){if(!this.sampleEvents_)return;var m=this.model_;var events=this.sampleEvents_;if(this.events_.length===0){for(var i=0;i<events.length;i++){var event=events[i];m.getOrCreateProcess(event.tid).getOrCreateThread(event.tid);}}
 var threadsByTid={};m.getAllThreads().forEach(function(t){threadsByTid[t.tid]=t;});for(var i=0;i<events.length;i++){var event=events[i];var thread=threadsByTid[event.tid];if(thread===undefined){m.importWarning({type:'sample_import_error',message:'Thread '+events.tid+'not found'});continue;}
-var cpu;if(event.cpu!==undefined)
-cpu=m.kernel.getOrCreateCpu(event.cpu);var stackFrame=this.getStackFrameForEvent_(event);var sample=new tr.model.Sample(cpu,thread,event.name,this.toModelTimeFromUs_(event.ts),stackFrame,event.weight);m.samples.push(sample);}},createAsyncSlices_:function(){if(this.allAsyncEvents_.length===0)
-return;this.allAsyncEvents_.sort(function(x,y){var d=x.event.ts-y.event.ts;if(d!==0)
-return d;return x.sequenceNumber-y.sequenceNumber;});var legacyEvents=[];var nestableAsyncEventsByKey={};var nestableMeasureAsyncEventsByKey={};for(var i=0;i<this.allAsyncEvents_.length;i++){var asyncEventState=this.allAsyncEvents_[i];var event=asyncEventState.event;if(event.ph==='S'||event.ph==='F'||event.ph==='T'||event.ph==='p'){legacyEvents.push(asyncEventState);continue;}
+var cpu;if(event.cpu!==undefined){cpu=m.kernel.getOrCreateCpu(event.cpu);}
+var leafNode=this.stackFrameTree_.getNode('g'+event.sf);var sample=new tr.model.Sample(this.toModelTimeFromUs_(event.ts),event.name,leafNode,thread,cpu,event.weight);m.samples.push(sample);}},createAsyncSlices_:function(){if(this.allAsyncEvents_.length===0)return;this.allAsyncEvents_.sort(function(x,y){var d=x.event.ts-y.event.ts;if(d!==0)return d;return x.sequenceNumber-y.sequenceNumber;});var legacyEvents=[];var nestableAsyncEventsByKey={};var nestableMeasureAsyncEventsByKey={};for(var i=0;i<this.allAsyncEvents_.length;i++){var asyncEventState=this.allAsyncEvents_[i];var event=asyncEventState.event;if(event.ph==='S'||event.ph==='F'||event.ph==='T'||event.ph==='p'){legacyEvents.push(asyncEventState);continue;}
 if(event.cat===undefined){this.model_.importWarning({type:'async_slice_parse_error',message:'Nestable async events (ph: b, e, or n) require a '+'cat parameter.'});continue;}
 if(event.name===undefined){this.model_.importWarning({type:'async_slice_parse_error',message:'Nestable async events (ph: b, e, or n) require a '+'name parameter.'});continue;}
-if(event.id===undefined){this.model_.importWarning({type:'async_slice_parse_error',message:'Nestable async events (ph: b, e, or n) require an '+'id parameter.'});continue;}
-if(event.cat==='blink.user_timing'){var matched=/([^\/:]+):([^\/:]+)\/?(.*)/.exec(event.name);if(matched!==null){var key=matched[1]+':'+event.cat;event.args=JSON.parse(Base64.atob(matched[3])||'{}');if(nestableMeasureAsyncEventsByKey[key]===undefined)
-nestableMeasureAsyncEventsByKey[key]=[];nestableMeasureAsyncEventsByKey[key].push(asyncEventState);continue;}}
-var key=event.cat+':'+event.id;if(nestableAsyncEventsByKey[key]===undefined)
-nestableAsyncEventsByKey[key]=[];nestableAsyncEventsByKey[key].push(asyncEventState);}
-this.createLegacyAsyncSlices_(legacyEvents);this.createNestableAsyncSlices_(nestableMeasureAsyncEventsByKey);this.createNestableAsyncSlices_(nestableAsyncEventsByKey);},createLegacyAsyncSlices_:function(legacyEvents){if(legacyEvents.length===0)
-return;legacyEvents.sort(function(x,y){var d=x.event.ts-y.event.ts;if(d!=0)
-return d;return x.sequenceNumber-y.sequenceNumber;});var asyncEventStatesByNameThenID={};for(var i=0;i<legacyEvents.length;i++){var asyncEventState=legacyEvents[i];var event=asyncEventState.event;var name=event.name;if(name===undefined){this.model_.importWarning({type:'async_slice_parse_error',message:'Async events (ph: S, T, p, or F) require a name '+' parameter.'});continue;}
-var id=event.id;if(id===undefined){this.model_.importWarning({type:'async_slice_parse_error',message:'Async events (ph: S, T, p, or F) require an id parameter.'});continue;}
-if(event.ph==='S'){if(asyncEventStatesByNameThenID[name]===undefined)
-asyncEventStatesByNameThenID[name]={};if(asyncEventStatesByNameThenID[name][id]){this.model_.importWarning({type:'async_slice_parse_error',message:'At '+event.ts+', a slice of the same id '+id+' was alrady open.'});continue;}
-asyncEventStatesByNameThenID[name][id]=[];asyncEventStatesByNameThenID[name][id].push(asyncEventState);}else{if(asyncEventStatesByNameThenID[name]===undefined){this.model_.importWarning({type:'async_slice_parse_error',message:'At '+event.ts+', no slice named '+name+' was open.'});continue;}
-if(asyncEventStatesByNameThenID[name][id]===undefined){this.model_.importWarning({type:'async_slice_parse_error',message:'At '+event.ts+', no slice named '+name+' with id='+id+' was open.'});continue;}
-var events=asyncEventStatesByNameThenID[name][id];events.push(asyncEventState);if(event.ph==='F'){var asyncSliceConstructor=tr.model.AsyncSlice.subTypes.getConstructor(events[0].event.cat,name);var slice=new asyncSliceConstructor(events[0].event.cat,name,getEventColor(events[0].event),this.toModelTimeFromUs_(events[0].event.ts),tr.b.concatenateObjects(events[0].event.args,events[events.length-1].event.args),this.toModelTimeFromUs_(event.ts-events[0].event.ts),true,undefined,undefined,events[0].event.argsStripped);slice.startThread=events[0].thread;slice.endThread=asyncEventState.thread;slice.id=id;var stepType=events[1].event.ph;var isValid=true;for(var j=1;j<events.length-1;++j){if(events[j].event.ph==='T'||events[j].event.ph==='p'){isValid=this.assertStepTypeMatches_(stepType,events[j]);if(!isValid)
-break;}
-if(events[j].event.ph==='S'){this.model_.importWarning({type:'async_slice_parse_error',message:'At '+event.event.ts+', a slice named '+
-event.event.name+' with id='+event.event.id+' had a step before the start event.'});continue;}
-if(events[j].event.ph==='F'){this.model_.importWarning({type:'async_slice_parse_error',message:'At '+event.event.ts+', a slice named '+
-event.event.name+' with id='+event.event.id+' had a step after the finish event.'});continue;}
-var startIndex=j+(stepType==='T'?0:-1);var endIndex=startIndex+1;var subName=events[j].event.name;if(!events[j].event.argsStripped&&(events[j].event.ph==='T'||events[j].event.ph==='p'))
-subName=subName+':'+events[j].event.args.step;var asyncSliceConstructor=tr.model.AsyncSlice.subTypes.getConstructor(events[0].event.cat,subName);var subSlice=new asyncSliceConstructor(events[0].event.cat,subName,getEventColor(event,subName+j),this.toModelTimeFromUs_(events[startIndex].event.ts),this.deepCopyIfNeeded_(events[j].event.args),this.toModelTimeFromUs_(events[endIndex].event.ts-events[startIndex].event.ts),undefined,undefined,events[startIndex].event.argsStripped);subSlice.startThread=events[startIndex].thread;subSlice.endThread=events[endIndex].thread;subSlice.id=id;slice.subSlices.push(subSlice);}
+var id=TraceEventImporter.scopedIdForEvent_(event);if(id===undefined){this.model_.importWarning({type:'async_slice_parse_error',message:'Nestable async events (ph: b, e, or n) require an '+'id parameter.'});continue;}
+if(event.cat==='blink.user_timing'){var matched=/([^\/:]+):([^\/:]+)\/?(.*)/.exec(event.name);if(matched!==null){var key=matched[1]+':'+event.cat;event.args=JSON.parse(Base64.atob(matched[3])||'{}');if(nestableMeasureAsyncEventsByKey[key]===undefined){nestableMeasureAsyncEventsByKey[key]=[];}
+nestableMeasureAsyncEventsByKey[key].push(asyncEventState);continue;}}
+var key=event.cat+':'+id.toStringWithDelimiter(':');if(nestableAsyncEventsByKey[key]===undefined){nestableAsyncEventsByKey[key]=[];}
+nestableAsyncEventsByKey[key].push(asyncEventState);}
+this.createLegacyAsyncSlices_(legacyEvents);this.createNestableAsyncSlices_(nestableMeasureAsyncEventsByKey);this.createNestableAsyncSlices_(nestableAsyncEventsByKey);},createLegacyAsyncSlices_:function(legacyEvents){if(legacyEvents.length===0)return;legacyEvents.sort(function(x,y){var d=x.event.ts-y.event.ts;if(d!==0)return d;return x.sequenceNumber-y.sequenceNumber;});var asyncEventStatesByNameThenID={};for(var i=0;i<legacyEvents.length;i++){var asyncEventState=legacyEvents[i];var event=asyncEventState.event;var name=event.name;if(name===undefined){this.model_.importWarning({type:'async_slice_parse_error',message:'Async events (ph: S, T, p, or F) require a name '+' parameter.'});continue;}
+var id=TraceEventImporter.scopedIdForEvent_(event);if(id===undefined){this.model_.importWarning({type:'async_slice_parse_error',message:'Async events (ph: S, T, p, or F) require an id parameter.'});continue;}
+var key=id.toStringWithDelimiter(':');if(event.ph==='S'){if(asyncEventStatesByNameThenID[name]===undefined){asyncEventStatesByNameThenID[name]={};}
+if(asyncEventStatesByNameThenID[name][key]){this.model_.importWarning({type:'async_slice_parse_error',message:'At '+event.ts+', a slice of the same id '+id+' was alrady open.'});continue;}
+asyncEventStatesByNameThenID[name][key]=[];asyncEventStatesByNameThenID[name][key].push(asyncEventState);}else{if(asyncEventStatesByNameThenID[name]===undefined){this.model_.importWarning({type:'async_slice_parse_error',message:'At '+event.ts+', no slice named '+name+' was open.'});continue;}
+if(asyncEventStatesByNameThenID[name][key]===undefined){this.model_.importWarning({type:'async_slice_parse_error',message:'At '+event.ts+', no slice named '+name+' with id='+id+' was open.'});continue;}
+var events=asyncEventStatesByNameThenID[name][key];events.push(asyncEventState);if(event.ph==='F'){var asyncSliceConstructor=tr.model.AsyncSlice.subTypes.getConstructor(events[0].event.cat,name);var slice=new asyncSliceConstructor(events[0].event.cat,name,getEventColor(events[0].event),this.toModelTimeFromUs_(events[0].event.ts),tr.b.concatenateObjects(events[0].event.args,events[events.length-1].event.args),this.toModelTimeFromUs_(event.ts-events[0].event.ts),true,undefined,undefined,events[0].event.argsStripped);slice.startThread=events[0].thread;slice.endThread=asyncEventState.thread;slice.id=key;var stepType=events[1].event.ph;var isValid=true;for(var j=1;j<events.length-1;++j){if(events[j].event.ph==='T'||events[j].event.ph==='p'){isValid=this.assertStepTypeMatches_(stepType,events[j]);if(!isValid)break;}
+if(events[j].event.ph==='S'){this.model_.importWarning({type:'async_slice_parse_error',message:'At '+events[j].event.ts+', a slice named '+
+name+' with id='+id+' had a step before the start event.'});continue;}
+if(events[j].event.ph==='F'){this.model_.importWarning({type:'async_slice_parse_error',message:'At '+events[j].event.ts+', a slice named '+
+name+' with id='+id+' had a step after the finish event.'});continue;}
+var startIndex=j+(stepType==='T'?0:-1);var endIndex=startIndex+1;var subName=name;if(!events[j].event.argsStripped&&(events[j].event.ph==='T'||events[j].event.ph==='p')){subName=subName+':'+events[j].event.args.step;}
+var asyncSliceConstructor=tr.model.AsyncSlice.subTypes.getConstructor(events[0].event.cat,subName);var subSlice=new asyncSliceConstructor(events[0].event.cat,subName,getEventColor(event,subName+j),this.toModelTimeFromUs_(events[startIndex].event.ts),this.deepCopyIfNeeded_(events[j].event.args),this.toModelTimeFromUs_(events[endIndex].event.ts-events[startIndex].event.ts),undefined,undefined,events[startIndex].event.argsStripped);subSlice.startThread=events[startIndex].thread;subSlice.endThread=events[endIndex].thread;subSlice.id=key;slice.subSlices.push(subSlice);}
 if(isValid){slice.startThread.asyncSliceGroup.push(slice);}
-delete asyncEventStatesByNameThenID[name][id];}}}},createNestableAsyncSlices_:function(nestableEventsByKey){for(var key in nestableEventsByKey){var eventStateEntries=nestableEventsByKey[key];var parentStack=[];for(var i=0;i<eventStateEntries.length;++i){var eventStateEntry=eventStateEntries[i];if(eventStateEntry.event.ph==='e'){var parentIndex=-1;for(var k=parentStack.length-1;k>=0;--k){if(parentStack[k].event.name===eventStateEntry.event.name){parentIndex=k;break;}}
+delete asyncEventStatesByNameThenID[name][key];}}}},createNestableAsyncSlices_:function(nestableEventsByKey){for(var key in nestableEventsByKey){var eventStateEntries=nestableEventsByKey[key];var parentStack=[];for(var i=0;i<eventStateEntries.length;++i){var eventStateEntry=eventStateEntries[i];if(eventStateEntry.event.ph==='e'){var parentIndex=-1;for(var k=parentStack.length-1;k>=0;--k){if(parentStack[k].event.name===eventStateEntry.event.name){parentIndex=k;break;}}
 if(parentIndex===-1){eventStateEntry.finished=false;}else{parentStack[parentIndex].end=eventStateEntry;while(parentIndex<parentStack.length){parentStack.pop();}}}
-if(parentStack.length>0)
-eventStateEntry.parentEntry=parentStack[parentStack.length-1];if(eventStateEntry.event.ph==='b'){parentStack.push(eventStateEntry);}}
+if(parentStack.length>0){eventStateEntry.parentEntry=parentStack[parentStack.length-1];}
+if(eventStateEntry.event.ph==='b'){parentStack.push(eventStateEntry);}}
 var topLevelSlices=[];for(var i=0;i<eventStateEntries.length;++i){var eventStateEntry=eventStateEntries[i];if(eventStateEntry.event.ph==='e'&&eventStateEntry.finished===undefined){continue;}
-var startState=undefined;var endState=undefined;var sliceArgs=eventStateEntry.event.args||{};var sliceError=undefined;if(eventStateEntry.event.ph==='n'){startState=eventStateEntry;endState=eventStateEntry;}else if(eventStateEntry.event.ph==='b'){if(eventStateEntry.end===undefined){eventStateEntry.end=eventStateEntries[eventStateEntries.length-1];sliceError='Slice has no matching END. End time has been adjusted.';this.model_.importWarning({type:'async_slice_parse_error',message:'Nestable async BEGIN event at '+
+var startState=undefined;var endState=undefined;var sliceArgs=eventStateEntry.event.args||{};var sliceError=undefined;var id=TraceEventImporter.scopedIdForEvent_(eventStateEntry.event);if(eventStateEntry.event.ph==='n'){startState=eventStateEntry;endState=eventStateEntry;}else if(eventStateEntry.event.ph==='b'){if(eventStateEntry.end===undefined){eventStateEntry.end=eventStateEntries[eventStateEntries.length-1];sliceError='Slice has no matching END. End time has been adjusted.';this.model_.importWarning({type:'async_slice_parse_error',message:'Nestable async BEGIN event at '+
 eventStateEntry.event.ts+' with name='+
-eventStateEntry.event.name+' and id='+eventStateEntry.event.id+' was unmatched.'});}else{function concatenateArguments(args1,args2){if(args1.params===undefined||args2.params===undefined)
-return tr.b.concatenateObjects(args1,args2);var args3={};args3.params=tr.b.concatenateObjects(args1.params,args2.params);return tr.b.concatenateObjects(args1,args2,args3);}
+eventStateEntry.event.name+' and id='+id+' was unmatched.'});}else{function concatenateArguments(args1,args2){if(args1.params===undefined||args2.params===undefined){return tr.b.concatenateObjects(args1,args2);}
+var args3={};args3.params=tr.b.concatenateObjects(args1.params,args2.params);return tr.b.concatenateObjects(args1,args2,args3);}
 var endArgs=eventStateEntry.end.event.args||{};sliceArgs=concatenateArguments(sliceArgs,endArgs);}
 startState=eventStateEntry;endState=eventStateEntry.end;}else{sliceError='Slice has no matching BEGIN. Start time has been adjusted.';this.model_.importWarning({type:'async_slice_parse_error',message:'Nestable async END event at '+
 eventStateEntry.event.ts+' with name='+
-eventStateEntry.event.name+' and id='+eventStateEntry.event.id+' was unmatched.'});startState=eventStateEntries[0];endState=eventStateEntry;}
-var isTopLevel=(eventStateEntry.parentEntry===undefined);var asyncSliceConstructor=tr.model.AsyncSlice.subTypes.getConstructor(eventStateEntry.event.cat,eventStateEntry.event.name);var thread_start=undefined;var thread_duration=undefined;if(startState.event.tts&&startState.event.use_async_tts){thread_start=this.toModelTimeFromUs_(startState.event.tts);if(endState.event.tts){var thread_end=this.toModelTimeFromUs_(endState.event.tts);thread_duration=thread_end-thread_start;}}
-var slice=new asyncSliceConstructor(eventStateEntry.event.cat,eventStateEntry.event.name,getEventColor(endState.event),this.toModelTimeFromUs_(startState.event.ts),sliceArgs,this.toModelTimeFromUs_(endState.event.ts-startState.event.ts),isTopLevel,thread_start,thread_duration,startState.event.argsStripped);slice.startThread=startState.thread;slice.endThread=endState.thread;slice.startStackFrame=this.getStackFrameForEvent_(startState.event);slice.endStackFrame=this.getStackFrameForEvent_(endState.event);slice.id=key;if(sliceError!==undefined)
-slice.error=sliceError;eventStateEntry.slice=slice;if(isTopLevel){topLevelSlices.push(slice);}else if(eventStateEntry.parentEntry.slice!==undefined){eventStateEntry.parentEntry.slice.subSlices.push(slice);}}
-for(var si=0;si<topLevelSlices.length;si++){topLevelSlices[si].startThread.asyncSliceGroup.push(topLevelSlices[si]);}}},assertStepTypeMatches_:function(stepType,event){if(stepType!=event.event.ph){this.model_.importWarning({type:'async_slice_parse_error',message:'At '+event.event.ts+', a slice named '+
-event.event.name+' with id='+event.event.id+' had both begin and end steps, which is not allowed.'});return false;}
-return true;},createFlowSlices_:function(){if(this.allFlowEvents_.length===0)
-return;var that=this;function validateFlowEvent(){if(event.name===undefined){that.model_.importWarning({type:'flow_slice_parse_error',message:'Flow events (ph: s, t or f) require a name parameter.'});return false;}
-if(event.ph==='s'||event.ph==='f'||event.ph==='t'){if(event.id===undefined){that.model_.importWarning({type:'flow_slice_parse_error',message:'Flow events (ph: s, t or f) require an id parameter.'});return false;}
+eventStateEntry.event.name+' and id='+id+' was unmatched.'});startState=eventStateEntries[0];endState=eventStateEntry;}
+var isTopLevel=(eventStateEntry.parentEntry===undefined);var asyncSliceConstructor=tr.model.AsyncSlice.subTypes.getConstructor(eventStateEntry.event.cat,eventStateEntry.event.name);var threadStart=undefined;var threadDuration=undefined;if(startState.event.tts&&startState.event.use_async_tts){threadStart=this.toModelTimeFromUs_(startState.event.tts);if(endState.event.tts){var threadEnd=this.toModelTimeFromUs_(endState.event.tts);threadDuration=threadEnd-threadStart;}}
+var slice=new asyncSliceConstructor(eventStateEntry.event.cat,eventStateEntry.event.name,getEventColor(endState.event),this.toModelTimeFromUs_(startState.event.ts),sliceArgs,this.toModelTimeFromUs_(endState.event.ts-startState.event.ts),isTopLevel,threadStart,threadDuration,startState.event.argsStripped);slice.startThread=startState.thread;slice.endThread=endState.thread;slice.startStackFrame=this.getStackFrameForEvent_(startState.event);slice.endStackFrame=this.getStackFrameForEvent_(endState.event);slice.id=key;if(sliceError!==undefined){slice.error=sliceError;}
+eventStateEntry.slice=slice;if(isTopLevel){topLevelSlices.push(slice);}else if(eventStateEntry.parentEntry.slice!==undefined){eventStateEntry.parentEntry.slice.subSlices.push(slice);}}
+for(var si=0;si<topLevelSlices.length;si++){topLevelSlices[si].startThread.asyncSliceGroup.push(topLevelSlices[si]);}}},assertStepTypeMatches_:function(stepType,event){if(stepType!==event.event.ph){this.model_.importWarning({type:'async_slice_parse_error',message:'At '+event.event.ts+', a slice named '+
+event.event.name+' with id='+
+TraceEventImporter.scopedIdForEvent_(event.event)+' had both begin and end steps, which is not allowed.'});return false;}
+return true;},validateFlowEvent_(event){if(event.name===undefined){this.model_.importWarning({type:'flow_slice_parse_error',message:'Flow events (ph: s, t or f) require a name parameter.'});return false;}
+if(event.ph==='s'||event.ph==='f'||event.ph==='t'){if(event.id===undefined){this.model_.importWarning({type:'flow_slice_parse_error',message:'Flow events (ph: s, t or f) require an id parameter.'});return false;}
 return true;}
-if(event.bind_id){if(event.flow_in===undefined&&event.flow_out===undefined){that.model_.importWarning({type:'flow_slice_parse_error',message:'Flow producer or consumer require flow_in or flow_out.'});return false;}
+if(event.bind_id){if(event.flow_in===undefined&&event.flow_out===undefined){this.model_.importWarning({type:'flow_slice_parse_error',message:'Flow producer or consumer require flow_in or flow_out.'});return false;}
 return true;}
-return false;}
-var createFlowEvent=function(thread,event,opt_slice){var startSlice,flowId,flowStartTs;if(event.bind_id){startSlice=opt_slice;flowId=event.bind_id;flowStartTs=this.toModelTimeFromUs_(event.ts+event.dur);}else{var ts=this.toModelTimeFromUs_(event.ts);startSlice=thread.sliceGroup.findSliceAtTs(ts);if(startSlice===undefined)
-return undefined;flowId=event.id;flowStartTs=ts;}
-var flowEvent=new tr.model.FlowEvent(event.cat,flowId,event.name,getEventColor(event),flowStartTs,that.deepCopyAlways_(event.args));flowEvent.startSlice=startSlice;flowEvent.startStackFrame=that.getStackFrameForEvent_(event);flowEvent.endStackFrame=undefined;startSlice.outFlowEvents.push(flowEvent);return flowEvent;}.bind(this);var finishFlowEventWith=function(flowEvent,thread,event,refGuid,bindToParent,opt_slice){var endSlice;if(event.bind_id){endSlice=opt_slice;}else{var ts=this.toModelTimeFromUs_(event.ts);if(bindToParent){endSlice=thread.sliceGroup.findSliceAtTs(ts);}else{endSlice=thread.sliceGroup.findNextSliceAfter(ts,refGuid);}
-if(endSlice===undefined)
-return false;}
-endSlice.inFlowEvents.push(flowEvent);flowEvent.endSlice=endSlice;flowEvent.duration=this.toModelTimeFromUs_(event.ts)-flowEvent.start;flowEvent.endStackFrame=that.getStackFrameForEvent_(event);that.mergeArgsInto_(flowEvent.args,event.args,flowEvent.title);return true;}.bind(this);function processFlowConsumer(flowIdToEvent,sliceGuidToEvent,event,slice){var flowEvent=flowIdToEvent[event.bind_id];if(flowEvent===undefined){that.model_.importWarning({type:'flow_slice_ordering_error',message:'Flow consumer '+event.bind_id+' does not have '+'a flow producer'});return false;}else if(flowEvent.endSlice){var flowProducer=flowEvent.startSlice;flowEvent=createFlowEvent(undefined,sliceGuidToEvent[flowProducer.guid],flowProducer);}
-var ok=finishFlowEventWith(flowEvent,undefined,event,refGuid,undefined,slice);if(ok){that.model_.flowEvents.push(flowEvent);}else{that.model_.importWarning({type:'flow_slice_end_error',message:'Flow consumer '+event.bind_id+' does not end '+'at an actual slice, so cannot be created.'});return false;}
-return true;}
-function processFlowProducer(flowIdToEvent,flowStatus,event,slice){if(flowIdToEvent[event.bind_id]&&flowStatus[event.bind_id]){that.model_.importWarning({type:'flow_slice_start_error',message:'Flow producer '+event.bind_id+' already seen'});return false;}
-var flowEvent=createFlowEvent(undefined,event,slice);if(!flowEvent){that.model_.importWarning({type:'flow_slice_start_error',message:'Flow producer '+event.bind_id+' does not start'+'a flow'});return false;}
-flowIdToEvent[event.bind_id]=flowEvent;}
-this.allFlowEvents_.sort(function(x,y){var d=x.event.ts-y.event.ts;if(d!=0)
-return d;return x.sequenceNumber-y.sequenceNumber;});var flowIdToEvent={};var sliceGuidToEvent={};var flowStatus={};for(var i=0;i<this.allFlowEvents_.length;++i){var data=this.allFlowEvents_[i];var refGuid=data.refGuid;var event=data.event;var thread=data.thread;if(!validateFlowEvent(event))
-continue;if(event.bind_id){var slice=data.slice;sliceGuidToEvent[slice.guid]=event;if(event.flowPhase===PRODUCER){if(!processFlowProducer(flowIdToEvent,flowStatus,event,slice))
-continue;flowStatus[event.bind_id]=true;}
-else{if(!processFlowConsumer(flowIdToEvent,sliceGuidToEvent,event,slice))
-continue;flowStatus[event.bind_id]=false;if(event.flowPhase===STEP){if(!processFlowProducer(flowIdToEvent,flowStatus,event,slice))
-continue;flowStatus[event.bind_id]=true;}}
+return false;},createFlowSlices_:function(){if(this.allFlowEvents_.length===0)return;var createFlowEvent=function(thread,event,opt_slice){var startSlice;var flowId;var flowStartTs;if(event.bind_id){startSlice=opt_slice;flowId=event.bind_id;flowStartTs=this.toModelTimeFromUs_(event.ts+event.dur);}else{var ts=this.toModelTimeFromUs_(event.ts);startSlice=thread.sliceGroup.findSliceAtTs(ts);if(startSlice===undefined)return undefined;flowId=event.id;flowStartTs=ts;}
+var flowEvent=new tr.model.FlowEvent(event.cat,flowId,event.name,getEventColor(event),flowStartTs,this.deepCopyAlways_(event.args));flowEvent.startSlice=startSlice;flowEvent.startStackFrame=this.getStackFrameForEvent_(event);flowEvent.endStackFrame=undefined;startSlice.outFlowEvents.push(flowEvent);return flowEvent;}.bind(this);var finishFlowEventWith=function(flowEvent,thread,event,refGuid,bindToParent,opt_slice){var endSlice;if(event.bind_id){endSlice=opt_slice;}else{var ts=this.toModelTimeFromUs_(event.ts);if(bindToParent){endSlice=thread.sliceGroup.findSliceAtTs(ts);}else{endSlice=thread.sliceGroup.findNextSliceAfter(ts,refGuid);}
+if(endSlice===undefined)return false;}
+endSlice.inFlowEvents.push(flowEvent);flowEvent.endSlice=endSlice;flowEvent.duration=this.toModelTimeFromUs_(event.ts)-flowEvent.start;flowEvent.endStackFrame=this.getStackFrameForEvent_(event);this.mergeArgsInto_(flowEvent.args,event.args,flowEvent.title);return true;}.bind(this);const processFlowConsumer=function(flowIdToEvent,sliceGuidToEvent,event,slice){var flowEvent=flowIdToEvent[event.bind_id];if(flowEvent===undefined){this.model_.importWarning({type:'flow_slice_ordering_error',message:'Flow consumer '+event.bind_id+' does not have '+'a flow producer'});return false;}else if(flowEvent.endSlice){var flowProducer=flowEvent.startSlice;flowEvent=createFlowEvent(undefined,sliceGuidToEvent[flowProducer.guid],flowProducer);}
+var ok=finishFlowEventWith(flowEvent,undefined,event,refGuid,undefined,slice);if(ok){this.model_.flowEvents.push(flowEvent);}else{this.model_.importWarning({type:'flow_slice_end_error',message:'Flow consumer '+event.bind_id+' does not end '+'at an actual slice, so cannot be created.'});return false;}
+return true;}.bind(this);const processFlowProducer=function(flowIdToEvent,flowStatus,event,slice){if(flowIdToEvent[event.bind_id]&&flowStatus[event.bind_id]){this.model_.importWarning({type:'flow_slice_start_error',message:'Flow producer '+event.bind_id+' already seen'});return false;}
+var flowEvent=createFlowEvent(undefined,event,slice);if(!flowEvent){this.model_.importWarning({type:'flow_slice_start_error',message:'Flow producer '+event.bind_id+' does not start'+'a flow'});return false;}
+flowIdToEvent[event.bind_id]=flowEvent;}.bind(this);this.allFlowEvents_.sort(function(x,y){var d=x.event.ts-y.event.ts;if(d!==0)return d;return x.sequenceNumber-y.sequenceNumber;});var flowIdToEvent={};var sliceGuidToEvent={};var flowStatus={};for(var i=0;i<this.allFlowEvents_.length;++i){var data=this.allFlowEvents_[i];var refGuid=data.refGuid;var event=data.event;var thread=data.thread;if(!this.validateFlowEvent_(event))continue;if(event.bind_id){var slice=data.slice;sliceGuidToEvent[slice.guid]=event;if(event.flowPhase===PRODUCER){if(!processFlowProducer(flowIdToEvent,flowStatus,event,slice)){continue;}
+flowStatus[event.bind_id]=true;}else{if(!processFlowConsumer(flowIdToEvent,sliceGuidToEvent,event,slice)){continue;}
+flowStatus[event.bind_id]=false;if(event.flowPhase===STEP){if(!processFlowProducer(flowIdToEvent,flowStatus,event,slice)){continue;}
+flowStatus[event.bind_id]=true;}}
 continue;}
 var flowEvent;if(event.ph==='s'){if(flowIdToEvent[event.id]){this.model_.importWarning({type:'flow_slice_start_error',message:'event id '+event.id+' already seen when '+'encountering start of flow event.'});continue;}
 flowEvent=createFlowEvent(thread,event);if(!flowEvent){this.model_.importWarning({type:'flow_slice_start_error',message:'event id '+event.id+' does not start '+'at an actual slice, so cannot be created.'});continue;}
 flowIdToEvent[event.id]=flowEvent;}else if(event.ph==='t'||event.ph==='f'){flowEvent=flowIdToEvent[event.id];if(flowEvent===undefined){this.model_.importWarning({type:'flow_slice_ordering_error',message:'Found flow phase '+event.ph+' for id: '+event.id+' but no flow start found.'});continue;}
-var bindToParent=event.ph==='t';if(event.ph==='f'){if(event.bp===undefined){if(event.cat.indexOf('input')>-1)
-bindToParent=true;else if(event.cat.indexOf('ipc.flow')>-1)
-bindToParent=true;}else{if(event.bp!=='e'){this.model_.importWarning({type:'flow_slice_bind_point_error',message:'Flow event with invalid binding point (event.bp).'});continue;}
+var bindToParent=event.ph==='t';if(event.ph==='f'){if(event.bp===undefined){if(event.cat.indexOf('input')>-1){bindToParent=true;}else if(event.cat.indexOf('ipc.flow')>-1){bindToParent=true;}}else{if(event.bp!=='e'){this.model_.importWarning({type:'flow_slice_bind_point_error',message:'Flow event with invalid binding point (event.bp).'});continue;}
 bindToParent=true;}}
-var ok=finishFlowEventWith(flowEvent,thread,event,refGuid,bindToParent);if(ok){that.model_.flowEvents.push(flowEvent);}else{this.model_.importWarning({type:'flow_slice_end_error',message:'event id '+event.id+' does not end '+'at an actual slice, so cannot be created.'});}
-flowIdToEvent[event.id]=undefined;if(ok&&event.ph==='t'){flowEvent=createFlowEvent(thread,event);flowIdToEvent[event.id]=flowEvent;}}}},createExplicitObjects_:function(){if(this.allObjectEvents_.length===0)
-return;var processEvent=function(objectEventState){var event=objectEventState.event;var scopedId=this.scopedIdForEvent_(event);var thread=objectEventState.thread;if(event.name===undefined){this.model_.importWarning({type:'object_parse_error',message:'While processing '+JSON.stringify(event)+': '+'Object events require an name parameter.'});}
-if(scopedId.id===undefined){this.model_.importWarning({type:'object_parse_error',message:'While processing '+JSON.stringify(event)+': '+'Object events require an id parameter.'});}
+var ok=finishFlowEventWith(flowEvent,thread,event,refGuid,bindToParent);if(ok){this.model_.flowEvents.push(flowEvent);}else{this.model_.importWarning({type:'flow_slice_end_error',message:'event id '+event.id+' does not end '+'at an actual slice, so cannot be created.'});}
+flowIdToEvent[event.id]=undefined;if(ok&&event.ph==='t'){flowEvent=createFlowEvent(thread,event);flowIdToEvent[event.id]=flowEvent;}}}},createExplicitObjects_:function(){if(this.allObjectEvents_.length===0)return;var processEvent=function(objectEventState){var event=objectEventState.event;var scopedId=TraceEventImporter.scopedIdForEvent_(event);var thread=objectEventState.thread;if(event.name===undefined){this.model_.importWarning({type:'object_parse_error',message:'While processing '+JSON.stringify(event)+': '+'Object events require an name parameter.'});}
+if(scopedId===undefined||scopedId.id===undefined){this.model_.importWarning({type:'object_parse_error',message:'While processing '+JSON.stringify(event)+': '+'Object events require an id parameter.'});}
 var process=thread.parent;var ts=this.toModelTimeFromUs_(event.ts);var instance;if(event.ph==='N'){try{instance=process.objects.idWasCreated(scopedId,event.cat,event.name,ts);}catch(e){this.model_.importWarning({type:'object_parse_error',message:'While processing create of '+
 scopedId+' at ts='+ts+': '+e});return;}}else if(event.ph==='O'){if(event.args.snapshot===undefined){this.model_.importWarning({type:'object_parse_error',message:'While processing '+scopedId+' at ts='+ts+': '+'Snapshots must have args: {snapshot: ...}'});return;}
 var snapshot;try{var args=this.deepCopyIfNeeded_(event.args.snapshot);var cat;if(args.cat){cat=args.cat;delete args.cat;}else{cat=event.cat;}
@@ -5535,56 +5517,37 @@
 scopedId+' at ts='+ts+': '+e});return;}
 instance=snapshot.objectInstance;}else if(event.ph==='D'){try{process.objects.idWasDeleted(scopedId,event.cat,event.name,ts);var instanceMap=process.objects.getOrCreateInstanceMap_(scopedId);instance=instanceMap.lastInstance;}catch(e){this.model_.importWarning({type:'object_parse_error',message:'While processing delete of '+
 scopedId+' at ts='+ts+': '+e});return;}}
-if(instance)
-instance.colorId=getEventColor(event,instance.typeName);}.bind(this);this.allObjectEvents_.sort(function(x,y){var d=x.event.ts-y.event.ts;if(d!=0)
-return d;return x.sequenceNumber-y.sequenceNumber;});var allObjectEvents=this.allObjectEvents_;for(var i=0;i<allObjectEvents.length;i++){var objectEventState=allObjectEvents[i];try{processEvent.call(this,objectEventState);}catch(e){this.model_.importWarning({type:'object_parse_error',message:e.message});}}},createImplicitObjects_:function(){tr.b.iterItems(this.model_.processes,function(pid,process){this.createImplicitObjectsForProcess_(process);},this);},createImplicitObjectsForProcess_:function(process){function processField(referencingObject,referencingObjectFieldName,referencingObjectFieldValue,containingSnapshot){if(!referencingObjectFieldValue)
-return;if(referencingObjectFieldValue instanceof
-tr.model.ObjectSnapshot)
-return null;if(referencingObjectFieldValue.id===undefined)
-return;var implicitSnapshot=referencingObjectFieldValue;var rawId=implicitSnapshot.id;var m=/(.+)\/(.+)/.exec(rawId);if(!m)
-throw new Error('Implicit snapshots must have names.');delete implicitSnapshot.id;var name=m[1];var id=m[2];var res;var cat;if(implicitSnapshot.cat!==undefined)
-cat=implicitSnapshot.cat;else
-cat=containingSnapshot.objectInstance.category;var baseTypename;if(implicitSnapshot.base_type)
-baseTypename=implicitSnapshot.base_type;else
-baseTypename=undefined;var scope=containingSnapshot.objectInstance.scopedId.scope;try{res=process.objects.addSnapshot(new tr.model.ScopedId(scope,id),cat,name,containingSnapshot.ts,implicitSnapshot,baseTypename);}catch(e){this.model_.importWarning({type:'object_snapshot_parse_error',message:'While processing implicit snapshot of '+
+if(instance){instance.colorId=getEventColor(event,instance.typeName);}}.bind(this);this.allObjectEvents_.sort(function(x,y){var d=x.event.ts-y.event.ts;if(d!==0)return d;return x.sequenceNumber-y.sequenceNumber;});var allObjectEvents=this.allObjectEvents_;for(var i=0;i<allObjectEvents.length;i++){var objectEventState=allObjectEvents[i];try{processEvent.call(this,objectEventState);}catch(e){this.model_.importWarning({type:'object_parse_error',message:e.message});}}},createImplicitObjects_:function(){for(var proc of Object.values(this.model_.processes)){this.createImplicitObjectsForProcess_(proc);}},createImplicitObjectsForProcess_:function(process){function processField(referencingObject,referencingObjectFieldName,referencingObjectFieldValue,containingSnapshot){if(!referencingObjectFieldValue)return;if(referencingObjectFieldValue instanceof
+tr.model.ObjectSnapshot){return null;}
+if(referencingObjectFieldValue.id===undefined)return;var implicitSnapshot=referencingObjectFieldValue;var rawId=implicitSnapshot.id;var m=/(.+)\/(.+)/.exec(rawId);if(!m){throw new Error('Implicit snapshots must have names.');}
+delete implicitSnapshot.id;var name=m[1];var id=m[2];var res;var cat;if(implicitSnapshot.cat!==undefined){cat=implicitSnapshot.cat;}else{cat=containingSnapshot.objectInstance.category;}
+var baseTypename;if(implicitSnapshot.base_type){baseTypename=implicitSnapshot.base_type;}else{baseTypename=undefined;}
+var scope=containingSnapshot.objectInstance.scopedId.scope;try{res=process.objects.addSnapshot(new tr.model.ScopedId(scope,id),cat,name,containingSnapshot.ts,implicitSnapshot,baseTypename);}catch(e){this.model_.importWarning({type:'object_snapshot_parse_error',message:'While processing implicit snapshot of '+
 rawId+' at ts='+containingSnapshot.ts+': '+e});return;}
-res.objectInstance.hasImplicitSnapshots=true;res.containingSnapshot=containingSnapshot;res.snapshottedOnThread=containingSnapshot.snapshottedOnThread;referencingObject[referencingObjectFieldName]=res;if(!(res instanceof tr.model.ObjectSnapshot))
-throw new Error('Created object must be instanceof snapshot');return res.args;}
-function iterObject(object,func,containingSnapshot,thisArg){if(!(object instanceof Object))
-return;if(object instanceof Array){for(var i=0;i<object.length;i++){var res=func.call(thisArg,object,i,object[i],containingSnapshot);if(res===null)
-continue;if(res)
-iterObject(res,func,containingSnapshot,thisArg);else
-iterObject(object[i],func,containingSnapshot,thisArg);}
+res.objectInstance.hasImplicitSnapshots=true;res.containingSnapshot=containingSnapshot;res.snapshottedOnThread=containingSnapshot.snapshottedOnThread;referencingObject[referencingObjectFieldName]=res;if(!(res instanceof tr.model.ObjectSnapshot)){throw new Error('Created object must be instanceof snapshot');}
+return res.args;}
+function iterObject(object,func,containingSnapshot,thisArg){if(!(object instanceof Object))return;if(object instanceof Array){for(var i=0;i<object.length;i++){var res=func.call(thisArg,object,i,object[i],containingSnapshot);if(res===null)continue;if(res){iterObject(res,func,containingSnapshot,thisArg);}else{iterObject(object[i],func,containingSnapshot,thisArg);}}
 return;}
-for(var key in object){var res=func.call(thisArg,object,key,object[key],containingSnapshot);if(res===null)
-continue;if(res)
-iterObject(res,func,containingSnapshot,thisArg);else
-iterObject(object[key],func,containingSnapshot,thisArg);}}
-process.objects.iterObjectInstances(function(instance){instance.snapshots.forEach(function(snapshot){if(snapshot.args.id!==undefined)
-throw new Error('args cannot have an id field inside it');iterObject(snapshot.args,processField,snapshot,this);},this);},this);},createMemoryDumps_:function(){for(var dumpId in this.allMemoryDumpEvents_)
-this.createGlobalMemoryDump_(this.allMemoryDumpEvents_[dumpId],dumpId);},createGlobalMemoryDump_:function(dumpIdEvents,dumpId){var globalRange=new tr.b.Range();for(var pid in dumpIdEvents){var processEvents=dumpIdEvents[pid];for(var i=0;i<processEvents.length;i++)
-globalRange.addValue(this.toModelTimeFromUs_(processEvents[i].ts));}
-if(globalRange.isEmpty)
-throw new Error('Internal error: Global memory dump without events');var globalMemoryDump=new tr.model.GlobalMemoryDump(this.model_,globalRange.min);globalMemoryDump.duration=globalRange.range;this.model_.globalMemoryDumps.push(globalMemoryDump);var globalMemoryAllocatorDumpsByFullName={};var levelsOfDetail={};var allMemoryAllocatorDumpsByGuid={};for(var pid in dumpIdEvents){this.createProcessMemoryDump_(globalMemoryDump,globalMemoryAllocatorDumpsByFullName,levelsOfDetail,allMemoryAllocatorDumpsByGuid,dumpIdEvents[pid],pid,dumpId);}
-globalMemoryDump.levelOfDetail=levelsOfDetail.global;globalMemoryDump.memoryAllocatorDumps=this.inferMemoryAllocatorDumpTree_(globalMemoryAllocatorDumpsByFullName);this.parseMemoryDumpAllocatorEdges_(allMemoryAllocatorDumpsByGuid,dumpIdEvents,dumpId);},createProcessMemoryDump_:function(globalMemoryDump,globalMemoryAllocatorDumpsByFullName,levelsOfDetail,allMemoryAllocatorDumpsByGuid,processEvents,pid,dumpId){var processRange=new tr.b.Range();for(var i=0;i<processEvents.length;i++)
-processRange.addValue(this.toModelTimeFromUs_(processEvents[i].ts));if(processRange.isEmpty)
-throw new Error('Internal error: Process memory dump without events');var process=this.model_.getOrCreateProcess(pid);var processMemoryDump=new tr.model.ProcessMemoryDump(globalMemoryDump,process,processRange.min);processMemoryDump.duration=processRange.range;process.memoryDumps.push(processMemoryDump);globalMemoryDump.processMemoryDumps[pid]=processMemoryDump;var processMemoryAllocatorDumpsByFullName={};for(var i=0;i<processEvents.length;i++){var processEvent=processEvents[i];var dumps=processEvent.args.dumps;if(dumps===undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'\'dumps\' field not found in a process memory dump'+' event for PID='+pid+' and dump ID='+dumpId+'.'});continue;}
+for(var key in object){var res=func.call(thisArg,object,key,object[key],containingSnapshot);if(res===null)continue;if(res){iterObject(res,func,containingSnapshot,thisArg);}else{iterObject(object[key],func,containingSnapshot,thisArg);}}}
+process.objects.iterObjectInstances(function(instance){instance.snapshots.forEach(function(snapshot){if(snapshot.args.id!==undefined){throw new Error('args cannot have an id field inside it');}
+iterObject(snapshot.args,processField,snapshot,this);},this);},this);},createMemoryDumps_:function(){for(var dumpId in this.allMemoryDumpEvents_){this.createGlobalMemoryDump_(this.allMemoryDumpEvents_[dumpId],dumpId);}},createGlobalMemoryDump_:function(dumpIdEvents,dumpId){var globalRange=new tr.b.math.Range();for(var pid in dumpIdEvents){var processEvents=dumpIdEvents[pid];for(var i=0;i<processEvents.length;i++){globalRange.addValue(this.toModelTimeFromUs_(processEvents[i].ts));}}
+if(globalRange.isEmpty){throw new Error('Internal error: Global memory dump without events');}
+var globalMemoryDump=new tr.model.GlobalMemoryDump(this.model_,globalRange.min);globalMemoryDump.duration=globalRange.range;this.model_.globalMemoryDumps.push(globalMemoryDump);var globalMemoryAllocatorDumpsByFullName={};var levelsOfDetail={};var allMemoryAllocatorDumpsByGuid={};for(var pid in dumpIdEvents){this.createProcessMemoryDump_(globalMemoryDump,globalMemoryAllocatorDumpsByFullName,levelsOfDetail,allMemoryAllocatorDumpsByGuid,dumpIdEvents[pid],pid,dumpId);}
+globalMemoryDump.levelOfDetail=levelsOfDetail.global;globalMemoryDump.memoryAllocatorDumps=this.inferMemoryAllocatorDumpTree_(globalMemoryAllocatorDumpsByFullName);this.parseMemoryDumpAllocatorEdges_(allMemoryAllocatorDumpsByGuid,dumpIdEvents,dumpId);},createProcessMemoryDump_:function(globalMemoryDump,globalMemoryAllocatorDumpsByFullName,levelsOfDetail,allMemoryAllocatorDumpsByGuid,processEvents,pid,dumpId){var processRange=new tr.b.math.Range();for(var i=0;i<processEvents.length;i++){processRange.addValue(this.toModelTimeFromUs_(processEvents[i].ts));}
+if(processRange.isEmpty){throw new Error('Internal error: Process memory dump without events');}
+var process=this.model_.getOrCreateProcess(pid);var processMemoryDump=new tr.model.ProcessMemoryDump(globalMemoryDump,process,processRange.min);processMemoryDump.duration=processRange.range;process.memoryDumps.push(processMemoryDump);globalMemoryDump.processMemoryDumps[pid]=processMemoryDump;var processMemoryAllocatorDumpsByFullName={};for(var i=0;i<processEvents.length;i++){var processEvent=processEvents[i];var dumps=processEvent.args.dumps;if(dumps===undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'\'dumps\' field not found in a process memory dump'+' event for PID='+pid+' and dump ID='+dumpId+'.'});continue;}
 this.parseMemoryDumpTotals_(processMemoryDump,dumps,pid,dumpId);this.parseMemoryDumpVmRegions_(processMemoryDump,dumps,pid,dumpId);this.parseMemoryDumpHeapDumps_(processMemoryDump,dumps,pid,dumpId);this.parseMemoryDumpLevelOfDetail_(levelsOfDetail,dumps,pid,dumpId);this.parseMemoryDumpAllocatorDumps_(processMemoryDump,globalMemoryDump,processMemoryAllocatorDumpsByFullName,globalMemoryAllocatorDumpsByFullName,allMemoryAllocatorDumpsByGuid,dumps,pid,dumpId);}
 if(levelsOfDetail.process===undefined){levelsOfDetail.process=processMemoryDump.vmRegions?DETAILED:LIGHT;}
 if(!this.updateMemoryDumpLevelOfDetail_(levelsOfDetail,'global',levelsOfDetail.process)){this.model_.importWarning({type:'memory_dump_parse_error',message:'diffent levels of detail provided for global memory'+' dump (dump ID='+dumpId+').'});}
-processMemoryDump.levelOfDetail=levelsOfDetail.process;delete levelsOfDetail.process;processMemoryDump.memoryAllocatorDumps=this.inferMemoryAllocatorDumpTree_(processMemoryAllocatorDumpsByFullName);},parseMemoryDumpTotals_:function(processMemoryDump,dumps,pid,dumpId){var rawTotals=dumps.process_totals;if(rawTotals===undefined)
-return;if(processMemoryDump.totals!==undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Process totals provided multiple times for'+' process memory dump for PID='+pid+' and dump ID='+dumpId+'.'});return;}
-var totals={};var platformSpecificTotals=undefined;for(var rawTotalName in rawTotals){var rawTotalValue=rawTotals[rawTotalName];if(rawTotalValue===undefined)
-continue;if(rawTotalName==='resident_set_bytes'){totals.residentBytes=parseInt(rawTotalValue,16);continue;}
+processMemoryDump.levelOfDetail=levelsOfDetail.process;delete levelsOfDetail.process;processMemoryDump.memoryAllocatorDumps=this.inferMemoryAllocatorDumpTree_(processMemoryAllocatorDumpsByFullName);},parseMemoryDumpTotals_:function(processMemoryDump,dumps,pid,dumpId){var rawTotals=dumps.process_totals;if(rawTotals===undefined)return;if(processMemoryDump.totals!==undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Process totals provided multiple times for'+' process memory dump for PID='+pid+' and dump ID='+dumpId+'.'});return;}
+var totals={};var platformSpecificTotals=undefined;for(var rawTotalName in rawTotals){var rawTotalValue=rawTotals[rawTotalName];if(rawTotalValue===undefined)continue;if(rawTotalName==='resident_set_bytes'){totals.residentBytes=parseInt(rawTotalValue,16);continue;}
 if(rawTotalName==='peak_resident_set_bytes'){totals.peakResidentBytes=parseInt(rawTotalValue,16);continue;}
 if(rawTotalName==='is_peak_rss_resetable'){totals.arePeakResidentBytesResettable=!!rawTotalValue;continue;}
 if(platformSpecificTotals===undefined){platformSpecificTotals={};totals.platformSpecific=platformSpecificTotals;}
 platformSpecificTotals[rawTotalName]=parseInt(rawTotalValue,16);}
 if(totals.peakResidentBytes===undefined&&totals.arePeakResidentBytesResettable!==undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Optional field peak_resident_set_bytes found'+' but is_peak_rss_resetable not found in'+' process memory dump for PID='+pid+' and dump ID='+dumpId+'.'});}
 if(totals.arePeakResidentBytesResettable!==undefined&&totals.peakResidentBytes===undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Optional field is_peak_rss_resetable found'+' but peak_resident_set_bytes not found in'+' process memory dump for PID='+pid+' and dump ID='+dumpId+'.'});}
-processMemoryDump.totals=totals;},parseMemoryDumpVmRegions_:function(processMemoryDump,dumps,pid,dumpId){var rawProcessMmaps=dumps.process_mmaps;if(rawProcessMmaps===undefined)
-return;var rawVmRegions=rawProcessMmaps.vm_regions;if(rawVmRegions===undefined)
-return;if(processMemoryDump.vmRegions!==undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'VM regions provided multiple times for'+' process memory dump for PID='+pid+' and dump ID='+dumpId+'.'});return;}
+processMemoryDump.totals=totals;},parseMemoryDumpVmRegions_:function(processMemoryDump,dumps,pid,dumpId){var rawProcessMmaps=dumps.process_mmaps;if(rawProcessMmaps===undefined)return;var rawVmRegions=rawProcessMmaps.vm_regions;if(rawVmRegions===undefined)return;if(processMemoryDump.vmRegions!==undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'VM regions provided multiple times for'+' process memory dump for PID='+pid+' and dump ID='+dumpId+'.'});return;}
 var vmRegions=new Array(rawVmRegions.length);for(var i=0;i<rawVmRegions.length;i++){var rawVmRegion=rawVmRegions[i];var byteStats={};var rawByteStats=rawVmRegion.bs;for(var rawByteStatName in rawByteStats){var rawByteStatValue=rawByteStats[rawByteStatName];if(rawByteStatValue===undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Byte stat \''+rawByteStatName+'\' of VM region '+
 i+' ('+rawVmRegion.mf+') in process memory dump for '+'PID='+pid+' and dump ID='+dumpId+' does not have a value.'});continue;}
 var byteStatName=BYTE_STAT_NAME_MAP[rawByteStatName];if(byteStatName===undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Unknown byte stat name \''+rawByteStatName+'\' ('+
@@ -5592,33 +5555,17 @@
 rawVmRegion.mf+') in process memory dump for PID='+pid+' and dump ID='+dumpId+'.'});continue;}
 byteStats[byteStatName]=parseInt(rawByteStatValue,16);}
 vmRegions[i]=new tr.model.VMRegion(parseInt(rawVmRegion.sa,16),parseInt(rawVmRegion.sz,16),rawVmRegion.pf,rawVmRegion.mf,byteStats);}
-processMemoryDump.vmRegions=tr.model.VMRegionClassificationNode.fromRegions(vmRegions);},parseMemoryDumpHeapDumps_:function(processMemoryDump,dumps,pid,dumpId){var rawHeapDumps=dumps.heaps;if(rawHeapDumps===undefined)
-return;if(processMemoryDump.heapDumps!==undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Heap dumps provided multiple times for'+' process memory dump for PID='+pid+' and dump ID='+dumpId+'.'});return;}
-var model=this.model_;var idPrefix='p'+pid+':';var heapDumps={};var objectTypeNameMap=this.objectTypeNameMap_[pid];if(objectTypeNameMap===undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Missing mapping from object type IDs to names.'});}
-for(var allocatorName in rawHeapDumps){var entries=rawHeapDumps[allocatorName].entries;if(entries===undefined||entries.length===0){this.model_.importWarning({type:'memory_dump_parse_error',message:'No heap entries in a '+allocatorName+' heap dump for PID='+pid+' and dump ID='+dumpId+'.'});continue;}
-var isOldFormat=entries[0].bt===undefined;if(!isOldFormat&&objectTypeNameMap===undefined){continue;}
-var heapDump=new tr.model.HeapDump(processMemoryDump,allocatorName);for(var i=0;i<entries.length;i++){var entry=entries[i];var leafStackFrameIndex=entry.bt;var leafStackFrame;if(isOldFormat){if(leafStackFrameIndex===undefined){leafStackFrame=undefined;}else{var leafStackFrameId=idPrefix+leafStackFrameIndex;if(leafStackFrameIndex===''){leafStackFrame=undefined;}else{leafStackFrame=model.stackFrames[leafStackFrameId];if(leafStackFrame===undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Missing leaf stack frame (ID '+
-leafStackFrameId+') of heap entry '+i+' (size '+
-size+') in a '+allocatorName+' heap dump for PID='+pid+'.'});continue;}}
-leafStackFrameId+=':self';if(model.stackFrames[leafStackFrameId]!==undefined){leafStackFrame=model.stackFrames[leafStackFrameId];}else{leafStackFrame=new tr.model.StackFrame(leafStackFrame,leafStackFrameId,'<self>',undefined);model.addStackFrame(leafStackFrame);}}}else{if(leafStackFrameIndex===undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Missing stack frame ID of heap entry '+i+' (size '+size+') in a '+allocatorName+' heap dump for PID='+pid+'.'});continue;}
-var leafStackFrameId=idPrefix+leafStackFrameIndex;if(leafStackFrameIndex===''){leafStackFrame=undefined;}else{leafStackFrame=model.stackFrames[leafStackFrameId];if(leafStackFrame===undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Missing leaf stack frame (ID '+leafStackFrameId+') of heap entry '+i+' (size '+size+') in a '+
-allocatorName+' heap dump for PID='+pid+'.'});continue;}}}
-var objectTypeId=entry.type;var objectTypeName;if(objectTypeId===undefined){objectTypeName=undefined;}else if(objectTypeNameMap===undefined){continue;}else{objectTypeName=objectTypeNameMap[objectTypeId];if(objectTypeName===undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Missing object type name (ID '+objectTypeId+') of heap entry '+i+' (size '+size+') in a '+
-allocatorName+' heap dump for pid='+pid+'.'});continue;}}
-var size=parseInt(entry.size,16);var count=entry.count===undefined?undefined:parseInt(entry.count,16);heapDump.addEntry(leafStackFrame,objectTypeName,size,count);}
-if(heapDump.entries.length>0)
-heapDumps[allocatorName]=heapDump;}
-if(Object.keys(heapDumps).length>0)
-processMemoryDump.heapDumps=heapDumps;},parseMemoryDumpLevelOfDetail_:function(levelsOfDetail,dumps,pid,dumpId){var rawLevelOfDetail=dumps.level_of_detail;var level;switch(rawLevelOfDetail){case'background':level=BACKGROUND;break;case'light':level=LIGHT;break;case'detailed':level=DETAILED;break;case undefined:level=undefined;break;default:this.model_.importWarning({type:'memory_dump_parse_error',message:'unknown raw level of detail \''+rawLevelOfDetail+'\' of process memory dump for PID='+pid+' and dump ID='+dumpId+'.'});return;}
+processMemoryDump.vmRegions=tr.model.VMRegionClassificationNode.fromRegions(vmRegions);},parseMemoryDumpHeapDumps_:function(processMemoryDump,dumps,pid,dumpId){var rawHeapDumps=dumps.heaps;if(rawHeapDumps===undefined)return;if(processMemoryDump.heapDumps!==undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Heap dumps provided multiple times for'+' process memory dump for PID='+pid+' and dump ID='+dumpId+'.'});return;}
+var processTypeMap=this.objectTypeNameMap_[pid];if(processTypeMap===undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Missing mapping from object type IDs to names.'});}
+var idPrefix='p'+pid+':';var heapDumps={};var importer=new HeapDumpTraceEventImporter(this.model_,processMemoryDump,processTypeMap,idPrefix,dumpId);for(var allocatorName in rawHeapDumps){var rawHeapDump=rawHeapDumps[allocatorName];var heapDump=importer.parseRawHeapDump(rawHeapDump,allocatorName);if(heapDump!==undefined&&heapDump.entries.length>0){heapDumps[allocatorName]=heapDump;}}
+if(Object.keys(heapDumps).length>0){processMemoryDump.heapDumps=heapDumps;}},parseMemoryDumpLevelOfDetail_:function(levelsOfDetail,dumps,pid,dumpId){var rawLevelOfDetail=dumps.level_of_detail;var level;switch(rawLevelOfDetail){case'background':level=BACKGROUND;break;case'light':level=LIGHT;break;case'detailed':level=DETAILED;break;case undefined:level=undefined;break;default:this.model_.importWarning({type:'memory_dump_parse_error',message:'unknown raw level of detail \''+rawLevelOfDetail+'\' of process memory dump for PID='+pid+' and dump ID='+dumpId+'.'});return;}
 if(!this.updateMemoryDumpLevelOfDetail_(levelsOfDetail,'process',level)){this.model_.importWarning({type:'memory_dump_parse_error',message:'diffent levels of detail provided for process memory'+' dump for PID='+pid+' (dump ID='+dumpId+').'});}},updateMemoryDumpLevelOfDetail_:function(levelsOfDetail,scope,level){if(!(scope in levelsOfDetail)||level===levelsOfDetail[scope]){levelsOfDetail[scope]=level;return true;}
 if(MEMORY_DUMP_LEVEL_OF_DETAIL_ORDER.indexOf(level)>MEMORY_DUMP_LEVEL_OF_DETAIL_ORDER.indexOf(levelsOfDetail[scope])){levelsOfDetail[scope]=level;}
-return false;},parseMemoryDumpAllocatorDumps_:function(processMemoryDump,globalMemoryDump,processMemoryAllocatorDumpsByFullName,globalMemoryAllocatorDumpsByFullName,allMemoryAllocatorDumpsByGuid,dumps,pid,dumpId){var rawAllocatorDumps=dumps.allocators;if(rawAllocatorDumps===undefined)
-return;for(var fullName in rawAllocatorDumps){var rawAllocatorDump=rawAllocatorDumps[fullName];var guid=rawAllocatorDump.guid;if(guid===undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Memory allocator dump '+fullName+' for PID='+pid+' and dump ID='+dumpId+' does not have a GUID.'});}
+return false;},parseMemoryDumpAllocatorDumps_:function(processMemoryDump,globalMemoryDump,processMemoryAllocatorDumpsByFullName,globalMemoryAllocatorDumpsByFullName,allMemoryAllocatorDumpsByGuid,dumps,pid,dumpId){var rawAllocatorDumps=dumps.allocators;if(rawAllocatorDumps===undefined)return;for(var fullName in rawAllocatorDumps){var rawAllocatorDump=rawAllocatorDumps[fullName];var guid=rawAllocatorDump.guid;if(guid===undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Memory allocator dump '+fullName+' for PID='+pid+' and dump ID='+dumpId+' does not have a GUID.'});}
 var flags=rawAllocatorDump.flags||0;var isWeakDump=!!(flags&WEAK_MEMORY_ALLOCATOR_DUMP_FLAG);var containerMemoryDump;var dstIndex;if(fullName.startsWith(GLOBAL_MEMORY_ALLOCATOR_DUMP_PREFIX)){fullName=fullName.substring(GLOBAL_MEMORY_ALLOCATOR_DUMP_PREFIX.length);containerMemoryDump=globalMemoryDump;dstIndex=globalMemoryAllocatorDumpsByFullName;}else{containerMemoryDump=processMemoryDump;dstIndex=processMemoryAllocatorDumpsByFullName;}
 var allocatorDump=allMemoryAllocatorDumpsByGuid[guid];if(allocatorDump===undefined){if(fullName in dstIndex){this.model_.importWarning({type:'memory_dump_parse_error',message:'Multiple GUIDs provided for'+' memory allocator dump '+fullName+': '+
 dstIndex[fullName].guid+', '+guid+' (ignored) for'+' PID='+pid+' and dump ID='+dumpId+'.'});continue;}
-allocatorDump=new tr.model.MemoryAllocatorDump(containerMemoryDump,fullName,guid);allocatorDump.weak=isWeakDump;dstIndex[fullName]=allocatorDump;if(guid!==undefined)
-allMemoryAllocatorDumpsByGuid[guid]=allocatorDump;}else{if(allocatorDump.containerMemoryDump!==containerMemoryDump){this.model_.importWarning({type:'memory_dump_parse_error',message:'Memory allocator dump '+fullName+' (GUID='+guid+') for PID='+pid+' and dump ID='+
+allocatorDump=new tr.model.MemoryAllocatorDump(containerMemoryDump,fullName,guid);allocatorDump.weak=isWeakDump;dstIndex[fullName]=allocatorDump;if(guid!==undefined){allMemoryAllocatorDumpsByGuid[guid]=allocatorDump;}}else{if(allocatorDump.containerMemoryDump!==containerMemoryDump){this.model_.importWarning({type:'memory_dump_parse_error',message:'Memory allocator dump '+fullName+' (GUID='+guid+') for PID='+pid+' and dump ID='+
 dumpId+' dumped in different contexts.'});continue;}
 if(allocatorDump.fullName!==fullName){this.model_.importWarning({type:'memory_dump_parse_error',message:'Memory allocator dump with GUID='+guid+' for PID='+
 pid+' and dump ID='+dumpId+' has multiple names: '+
@@ -5628,7 +5575,7 @@
 for(var attrName in attributes){var attrArgs=attributes[attrName];var attrType=attrArgs.type;var attrValue=attrArgs.value;switch(attrType){case'scalar':if(attrName in allocatorDump.numerics){this.model_.importWarning({type:'memory_dump_parse_error',message:'Multiple values provided for scalar attribute '+
 attrName+' of memory allocator dump '+fullName+' (GUID='+guid+') for PID='+pid+' and dump ID='+
 dumpId+'.'});break;}
-var unit=attrArgs.units==='bytes'?tr.b.Unit.byName.sizeInBytes_smallerIsBetter:tr.b.Unit.byName.unitlessNumber_smallerIsBetter;var value=parseInt(attrValue,16);allocatorDump.addNumeric(attrName,new tr.v.ScalarNumeric(unit,value));break;case'string':if(attrName in allocatorDump.diagnostics){this.model_.importWarning({type:'memory_dump_parse_error',message:'Multiple values provided for string attribute '+
+var unit=attrArgs.units==='bytes'?tr.b.Unit.byName.sizeInBytes_smallerIsBetter:tr.b.Unit.byName.unitlessNumber_smallerIsBetter;var value=parseInt(attrValue,16);allocatorDump.addNumeric(attrName,new tr.b.Scalar(unit,value));break;case'string':if(attrName in allocatorDump.diagnostics){this.model_.importWarning({type:'memory_dump_parse_error',message:'Multiple values provided for string attribute '+
 attrName+' of memory allocator dump '+fullName+' (GUID='+guid+') for PID='+pid+' and dump ID='+
 dumpId+'.'});break;}
 allocatorDump.addDiagnostic(attrName,attrValue);break;default:this.model_.importWarning({type:'memory_dump_parse_error',message:'Unknown type provided for attribute '+attrName+' of memory allocator dump '+fullName+' (GUID='+guid+') for PID='+pid+' and dump ID='+dumpId+': '+
@@ -5638,221 +5585,152 @@
 allocatorDump.parent=parentAllocatorDump;parentAllocatorDump.children.push(allocatorDump);if(parentAlreadyExisted){if(!allocatorDump.weak){while(parentAllocatorDump!==undefined&&parentAllocatorDump.weak===undefined){parentAllocatorDump.weak=false;parentAllocatorDump=parentAllocatorDump.parent;}}
 break;}
 fullName=parentFullName;allocatorDump=parentAllocatorDump;}}
-for(var fullName in memoryAllocatorDumpsByFullName){var allocatorDump=memoryAllocatorDumpsByFullName[fullName];if(allocatorDump.weak===undefined)
-allocatorDump.weak=true;}
-return rootAllocatorDumps;},parseMemoryDumpAllocatorEdges_:function(allMemoryAllocatorDumpsByGuid,dumpIdEvents,dumpId){for(var pid in dumpIdEvents){var processEvents=dumpIdEvents[pid];for(var i=0;i<processEvents.length;i++){var processEvent=processEvents[i];var dumps=processEvent.args.dumps;if(dumps===undefined)
-continue;var rawEdges=dumps.allocators_graph;if(rawEdges===undefined)
-continue;for(var j=0;j<rawEdges.length;j++){var rawEdge=rawEdges[j];var sourceGuid=rawEdge.source;var sourceDump=allMemoryAllocatorDumpsByGuid[sourceGuid];if(sourceDump===undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Edge for PID='+pid+' and dump ID='+dumpId+' is missing source memory allocator dump (GUID='+
+for(var fullName in memoryAllocatorDumpsByFullName){var allocatorDump=memoryAllocatorDumpsByFullName[fullName];if(allocatorDump.weak===undefined){allocatorDump.weak=true;}}
+return rootAllocatorDumps;},parseMemoryDumpAllocatorEdges_:function(allMemoryAllocatorDumpsByGuid,dumpIdEvents,dumpId){for(var pid in dumpIdEvents){var processEvents=dumpIdEvents[pid];for(var i=0;i<processEvents.length;i++){var processEvent=processEvents[i];var dumps=processEvent.args.dumps;if(dumps===undefined)continue;var rawEdges=dumps.allocators_graph;if(rawEdges===undefined)continue;for(var j=0;j<rawEdges.length;j++){var rawEdge=rawEdges[j];var sourceGuid=rawEdge.source;var sourceDump=allMemoryAllocatorDumpsByGuid[sourceGuid];if(sourceDump===undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Edge for PID='+pid+' and dump ID='+dumpId+' is missing source memory allocator dump (GUID='+
 sourceGuid+').'});continue;}
 var targetGuid=rawEdge.target;var targetDump=allMemoryAllocatorDumpsByGuid[targetGuid];if(targetDump===undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Edge for PID='+pid+' and dump ID='+dumpId+' is missing target memory allocator dump (GUID='+
 targetGuid+').'});continue;}
 var importance=rawEdge.importance;var edge=new tr.model.MemoryAllocatorDumpLink(sourceDump,targetDump,importance);switch(rawEdge.type){case'ownership':if(sourceDump.owns!==undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Memory allocator dump '+sourceDump.fullName+' (GUID='+sourceGuid+') already owns a memory'+' allocator dump ('+
 sourceDump.owns.target.fullName+').'});}else{sourceDump.owns=edge;targetDump.ownedBy.push(edge);}
 break;case'retention':sourceDump.retains.push(edge);targetDump.retainedBy.push(edge);break;default:this.model_.importWarning({type:'memory_dump_parse_error',message:'Invalid edge type: '+rawEdge.type+' (PID='+pid+', dump ID='+dumpId+', source='+sourceGuid+', target='+targetGuid+', importance='+importance+').'});}}}}},toModelTimeFromUs_:function(ts){if(!this.toModelTime_){this.toModelTime_=this.model_.clockSyncManager.getModelTimeTransformer(this.clockDomainId_);}
-return this.toModelTime_(tr.b.Unit.timestampFromUs(ts));},maybeToModelTimeFromUs_:function(ts){if(ts===undefined)
-return undefined;return this.toModelTimeFromUs_(ts);}};tr.importer.Importer.register(TraceEventImporter);return{TraceEventImporter:TraceEventImporter};});'use strict';tr.exportTo('tr.e.measure',function(){var AsyncSlice=tr.model.AsyncSlice;function MeasureAsyncSlice(){this.groupTitle_='Ungrouped Measure';var matched=/([^\/:]+):([^\/:]+)\/?(.*)/.exec(arguments[1]);if(matched!==null){arguments[1]=matched[2];this.groupTitle_=matched[1];}
+return this.toModelTime_(tr.b.Unit.timestampFromUs(ts));},maybeToModelTimeFromUs_:function(ts){if(ts===undefined){return undefined;}
+return this.toModelTimeFromUs_(ts);}};tr.importer.Importer.register(TraceEventImporter);return{TraceEventImporter,};});'use strict';tr.exportTo('tr.e.measure',function(){var AsyncSlice=tr.model.AsyncSlice;function MeasureAsyncSlice(){this.groupTitle_='Ungrouped Measure';var matched=/([^\/:]+):([^\/:]+)\/?(.*)/.exec(arguments[1]);if(matched!==null){arguments[1]=matched[2];this.groupTitle_=matched[1];}
 AsyncSlice.apply(this,arguments);}
-MeasureAsyncSlice.prototype={__proto__:AsyncSlice.prototype,get viewSubGroupTitle(){return this.groupTitle_;},get title(){return this.title_;},set title(title){this.title_=title;}};AsyncSlice.subTypes.register(MeasureAsyncSlice,{categoryParts:['blink.user_timing']});return{MeasureAsyncSlice:MeasureAsyncSlice};});'use strict';tr.exportTo('tr.e.net',function(){var AsyncSlice=tr.model.AsyncSlice;function NetAsyncSlice(){AsyncSlice.apply(this,arguments);this.url_=undefined;this.byteCount_=undefined;this.isTitleComputed_=false;this.isUrlComputed_=false;}
-NetAsyncSlice.prototype={__proto__:AsyncSlice.prototype,get viewSubGroupTitle(){return'NetLog';},get title(){if(this.isTitleComputed_||!this.isTopLevel)
-return this.title_;if(this.url!==undefined&&this.url.length>0){this.title_=this.url;}else if(this.args!==undefined&&this.args.source_type!==undefined){this.title_=this.args.source_type;}
-this.isTitleComputed_=true;return this.title_;},set title(title){this.title_=title;},get url(){if(this.isUrlComputed_)
-return this.url_;if(this.args!==undefined&&this.args.params!==undefined&&this.args.params.url!==undefined){this.url_=this.args.params.url;}else if(this.subSlices!==undefined&&this.subSlices.length>0){for(var i=0;i<this.subSlices.length&&!this.url_;i++){if(this.subSlices[i].url!==undefined)
-this.url_=this.subSlices[i].url;}}
-this.isUrlComputed_=true;return this.url_;},get byteCount(){if(this.byteCount_!==undefined)
-return this.byteCount_;this.byteCount_=0;if((this.originalTitle==='URL_REQUEST_JOB_FILTERED_BYTES_READ'||this.originalTitle==='URL_REQUEST_JOB_BYTES_READ')&&this.args!==undefined&&this.args.params!==undefined&&this.args.params.byte_count!==undefined){this.byteCount_=this.args.params.byte_count;}
+MeasureAsyncSlice.prototype={__proto__:AsyncSlice.prototype,get viewSubGroupTitle(){return this.groupTitle_;},get title(){return this.title_;},set title(title){this.title_=title;}};AsyncSlice.subTypes.register(MeasureAsyncSlice,{categoryParts:['blink.user_timing']});return{MeasureAsyncSlice,};});'use strict';tr.exportTo('tr.e.net',function(){var AsyncSlice=tr.model.AsyncSlice;function NetAsyncSlice(){AsyncSlice.apply(this,arguments);this.url_=undefined;this.byteCount_=undefined;this.isTitleComputed_=false;this.isUrlComputed_=false;}
+NetAsyncSlice.prototype={__proto__:AsyncSlice.prototype,get viewSubGroupTitle(){return'NetLog';},get title(){if(this.isTitleComputed_||!this.isTopLevel){return this.title_;}
+if(this.url!==undefined&&this.url.length>0){this.title_=this.url;}else if(this.args!==undefined&&this.args.source_type!==undefined){this.title_=this.args.source_type;}
+this.isTitleComputed_=true;return this.title_;},set title(title){this.title_=title;},get url(){if(this.isUrlComputed_){return this.url_;}
+if(this.args!==undefined&&this.args.params!==undefined&&this.args.params.url!==undefined){this.url_=this.args.params.url;}else if(this.subSlices!==undefined&&this.subSlices.length>0){for(var i=0;i<this.subSlices.length&&!this.url_;i++){if(this.subSlices[i].url!==undefined){this.url_=this.subSlices[i].url;}}}
+this.isUrlComputed_=true;return this.url_;},get byteCount(){if(this.byteCount_!==undefined){return this.byteCount_;}
+this.byteCount_=0;if((this.originalTitle==='URL_REQUEST_JOB_FILTERED_BYTES_READ'||this.originalTitle==='URL_REQUEST_JOB_BYTES_READ')&&this.args!==undefined&&this.args.params!==undefined&&this.args.params.byte_count!==undefined){this.byteCount_=this.args.params.byte_count;}
 for(var i=0;i<this.subSlices.length;i++){this.byteCount_+=this.subSlices[i].byteCount;}
-return this.byteCount_;}};AsyncSlice.subTypes.register(NetAsyncSlice,{categoryParts:['netlog','disabled-by-default-netlog']});return{NetAsyncSlice:NetAsyncSlice};});'use strict';tr.exportTo('tr.model',function(){var ColorScheme=tr.b.ColorScheme;function Activity(name,category,range,args){tr.model.TimedEvent.call(this,range.min);this.title=name;this.category=category;this.colorId=ColorScheme.getColorIdForGeneralPurposeString(name);this.duration=range.duration;this.args=args;this.name=name;};Activity.prototype={__proto__:tr.model.TimedEvent.prototype,shiftTimestampsForward:function(amount){this.start+=amount;},addBoundsToRange:function(range){range.addValue(this.start);range.addValue(this.end);}};return{Activity:Activity};});'use strict';tr.exportTo('tr.e.importer.android',function(){var Importer=tr.importer.Importer;var ACTIVITY_STATE={NONE:'none',CREATED:'created',STARTED:'started',RESUMED:'resumed',PAUSED:'paused',STOPPED:'stopped',DESTROYED:'destroyed'};var activityMap={};function EventLogImporter(model,events){this.model_=model;this.events_=events;this.importPriority=3;}
-var eventLogActivityRE=new RegExp('(\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}.\\d+)'+'\\s+(\\d+)\\s+(\\d+)\\s+([A-Z])\\s*'+'(am_\\w+)\\s*:(.*)');var amCreateRE=new RegExp('\s*\\[.*,.*,.*,(.*),.*,.*,.*,.*\\]');var amFocusedRE=new RegExp('\s*\\[\\d+,(.*)\\]');var amProcStartRE=new RegExp('\s*\\[\\d+,\\d+,\\d+,.*,activity,(.*)\\]');var amOnResumeRE=new RegExp('\s*\\[\\d+,(.*)\\]');var amOnPauseRE=new RegExp('\s*\\[\\d+,(.*)\\]');var amLaunchTimeRE=new RegExp('\s*\\[\\d+,\\d+,(.*),(\\d+),(\\d+)');var amDestroyRE=new RegExp('\s*\\[\\d+,\\d+,\\d+,(.*)\\]');EventLogImporter.canImport=function(events){if(!(typeof(events)==='string'||events instanceof String))
-return false;if(/^<!DOCTYPE html>/.test(events))
-return false;return eventLogActivityRE.test(events);};EventLogImporter.prototype={__proto__:Importer.prototype,get importerName(){return'EventLogImporter';},get model(){return this.model_;},getFullActivityName:function(component){var componentSplit=component.split('/');if(componentSplit[1].startsWith('.'))
-return componentSplit[0]+componentSplit[1];return componentSplit[1];},getProcName:function(component){var componentSplit=component.split('/');return componentSplit[0];},findOrCreateActivity:function(activityName){if(activityName in activityMap)
-return activityMap[activityName];var activity={state:ACTIVITY_STATE.NONE,name:activityName};activityMap[activityName]=activity;return activity;},deleteActivity:function(activityName){delete activityMap[activityName];},handleCreateActivity:function(ts,activityName){var activity=this.findOrCreateActivity(activityName);activity.state=ACTIVITY_STATE.CREATED;activity.createdTs=ts;},handleFocusActivity:function(ts,procName,activityName){var activity=this.findOrCreateActivity(activityName);activity.lastFocusedTs=ts;},handleProcStartForActivity:function(ts,activityName){var activity=this.findOrCreateActivity(activityName);activity.procStartTs=ts;},handleOnResumeCalled:function(ts,pid,activityName){var activity=this.findOrCreateActivity(activityName);activity.state=ACTIVITY_STATE.RESUMED;activity.lastResumeTs=ts;activity.pid=pid;},handleOnPauseCalled:function(ts,activityName){var activity=this.findOrCreateActivity(activityName);activity.state=ACTIVITY_STATE.PAUSED;activity.lastPauseTs=ts;if(ts>this.model_.bounds.min&&ts<this.model_.bounds.max)
-this.addActivityToProcess(activity);},handleLaunchTime:function(ts,activityName,launchTime){var activity=this.findOrCreateActivity(activityName);activity.launchTime=launchTime;},handleDestroyActivity:function(ts,activityName){this.deleteActivity(activityName);},addActivityToProcess:function(activity){if(activity.pid===undefined)
-return;var process=this.model_.getOrCreateProcess(activity.pid);var range=tr.b.Range.fromExplicitRange(Math.max(this.model_.bounds.min,activity.lastResumeTs),activity.lastPauseTs);var newActivity=new tr.model.Activity(activity.name,'Android Activity',range,{created:activity.createdTs,procstart:activity.procStartTs,lastfocus:activity.lastFocusedTs});process.activities.push(newActivity);},parseAmLine_:function(line){var match=eventLogActivityRE.exec(line);if(!match)
-return;var first_realtime_ts=this.model_.bounds.min-
-this.model_.realtime_to_monotonic_offset_ms;var year=new Date(first_realtime_ts).getFullYear();var ts=match[1].substring(0,5)+'-'+year+' '+
-match[1].substring(5,match[1].length);var monotonic_ts=Date.parse(ts)+
-this.model_.realtime_to_monotonic_offset_ms;var pid=match[2];var action=match[5];var data=match[6];if(action==='am_create_activity'){match=amCreateRE.exec(data);if(match&&match.length>=2){this.handleCreateActivity(monotonic_ts,this.getFullActivityName(match[1]));}}else if(action==='am_focused_activity'){match=amFocusedRE.exec(data);if(match&&match.length>=2){this.handleFocusActivity(monotonic_ts,this.getProcName(match[1]),this.getFullActivityName(match[1]));}}else if(action==='am_proc_start'){match=amProcStartRE.exec(data);if(match&&match.length>=2){this.handleProcStartForActivity(monotonic_ts,this.getFullActivityName(match[1]));}}else if(action==='am_on_resume_called'){match=amOnResumeRE.exec(data);if(match&&match.length>=2)
-this.handleOnResumeCalled(monotonic_ts,pid,match[1]);}else if(action==='am_on_paused_called'){match=amOnPauseRE.exec(data);if(match&&match.length>=2)
-this.handleOnPauseCalled(monotonic_ts,match[1]);}else if(action==='am_activity_launch_time'){match=amLaunchTimeRE.exec(data);this.handleLaunchTime(monotonic_ts,this.getFullActivityName(match[1]),match[2]);}else if(action==='am_destroy_activity'){match=amDestroyRE.exec(data);if(match&&match.length==2){this.handleDestroyActivity(monotonic_ts,this.getFullActivityName(match[1]));}}},importEvents:function(){if(isNaN(this.model_.realtime_to_monotonic_offset_ms)){this.model_.importWarning({type:'eveng_log_clock_sync',message:'Need a trace_event_clock_sync to map realtime to import.'});return;}
-this.model_.updateBounds();var lines=this.events_.split('\n');lines.forEach(this.parseAmLine_,this);for(var activityName in activityMap){var activity=activityMap[activityName];if(activity.state==ACTIVITY_STATE.RESUMED){activity.lastPauseTs=this.model_.bounds.max;this.addActivityToProcess(activity);}}}};Importer.register(EventLogImporter);return{EventLogImporter:EventLogImporter};});'use strict';tr.exportTo('tr.e.importer.battor',function(){function BattorImporter(model,events){this.importPriority=3;this.model_=model;this.samples_=[];this.syncTimestampsById_=new Map();this.parseTrace_(events);}
-var battorDataLineRE=new RegExp('^(-?\\d+\\.\\d+)\\s+(-?\\d+\\.\\d+)\\s+(-?\\d+\\.\\d+)'+'(?:\\s+<(\\S+)>)?$');var battorHeaderLineRE=/^# BattOr/;BattorImporter.canImport=function(events){if(!(typeof(events)==='string'||events instanceof String))
-return false;return battorHeaderLineRE.test(events);};BattorImporter.prototype={__proto__:tr.importer.Importer.prototype,get importerName(){return'BattorImporter';},get model(){return this.model_;},importClockSyncMarkers:function(){for(var[syncId,ts]of this.syncTimestampsById_){this.model_.clockSyncManager.addClockSyncMarker(tr.model.ClockDomainId.BATTOR,syncId,ts);}},importEvents:function(){if(this.model_.device.powerSeries){this.model_.importWarning({type:'import_error',message:'Power counter exists, can not import BattOr power trace.'});return;}
-var modelTimeTransformer=this.model_.clockSyncManager.getModelTimeTransformer(tr.model.ClockDomainId.BATTOR);var powerSeries=this.model_.device.powerSeries=new tr.model.PowerSeries(this.model_.device);for(var i=0;i<this.samples_.length;i++){var sample=this.samples_[i];powerSeries.addPowerSample(modelTimeTransformer(sample.ts),sample.powerInW);}},parseTrace_:function(trace){var lines=trace.split('\n');for(var line of lines){line=line.trim();if(line.length===0)
-continue;if(line.startsWith('#'))
-continue;var groups=battorDataLineRE.exec(line);if(!groups){this.model_.importWarning({type:'parse_error',message:'Unrecognized line in BattOr trace: '+line});continue;}
-var ts=parseFloat(groups[1]);var voltageInV=tr.b.convertUnit(parseFloat(groups[2]),tr.b.UnitScale.Metric.MILLI,tr.b.UnitScale.Metric.NONE);var currentInA=tr.b.convertUnit(parseFloat(groups[3]),tr.b.UnitScale.Metric.MILLI,tr.b.UnitScale.Metric.NONE);var syncId=groups[4];if(syncId)
-this.syncTimestampsById_.set(syncId,ts);if(voltageInV<0||currentInA<0){this.model_.importWarning({type:'parse_error',message:'The following line in the BattOr trace has a negative '+'voltage or current, neither of which are allowed: '+line+'. A common cause of this is that the device is charging '+'while the trace is being recorded.'});continue;}
+return this.byteCount_;}};AsyncSlice.subTypes.register(NetAsyncSlice,{categoryParts:['netlog','disabled-by-default-netlog']});return{NetAsyncSlice,};});'use strict';tr.exportTo('tr.model',function(){var ColorScheme=tr.b.ColorScheme;function Activity(name,category,range,args){tr.model.TimedEvent.call(this,range.min);this.title=name;this.category=category;this.colorId=ColorScheme.getColorIdForGeneralPurposeString(name);this.duration=range.duration;this.args=args;this.name=name;}
+Activity.prototype={__proto__:tr.model.TimedEvent.prototype,shiftTimestampsForward:function(amount){this.start+=amount;},addBoundsToRange:function(range){range.addValue(this.start);range.addValue(this.end);}};return{Activity,};});'use strict';tr.exportTo('tr.e.importer.android',function(){var Importer=tr.importer.Importer;var ACTIVITY_STATE={NONE:'none',CREATED:'created',STARTED:'started',RESUMED:'resumed',PAUSED:'paused',STOPPED:'stopped',DESTROYED:'destroyed'};var activityMap={};function EventLogImporter(model,events){this.model_=model;this.events_=events;this.importPriority=3;}
+var eventLogActivityRE=new RegExp('(\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}.\\d+)'+'\\s+(\\d+)\\s+(\\d+)\\s+([A-Z])\\s*'+'(am_\\w+)\\s*:(.*)');var amCreateRE=new RegExp('\s*\\[.*,.*,.*,(.*),.*,.*,.*,.*\\]');var amFocusedRE=new RegExp('\s*\\[\\d+,(.*)\\]');var amProcStartRE=new RegExp('\s*\\[\\d+,\\d+,\\d+,.*,activity,(.*)\\]');var amOnResumeRE=new RegExp('\s*\\[\\d+,(.*)\\]');var amOnPauseRE=new RegExp('\s*\\[\\d+,(.*)\\]');var amLaunchTimeRE=new RegExp('\s*\\[\\d+,\\d+,(.*),(\\d+),(\\d+)');var amDestroyRE=new RegExp('\s*\\[\\d+,\\d+,\\d+,(.*)\\]');EventLogImporter.canImport=function(events){if(!(typeof(events)==='string'||events instanceof String)){return false;}
+if(/^<!DOCTYPE html>/.test(events))return false;return eventLogActivityRE.test(events);};EventLogImporter.prototype={__proto__:Importer.prototype,get importerName(){return'EventLogImporter';},get model(){return this.model_;},getFullActivityName:function(component){var componentSplit=component.split('/');if(componentSplit[1].startsWith('.')){return componentSplit[0]+componentSplit[1];}
+return componentSplit[1];},getProcName:function(component){var componentSplit=component.split('/');return componentSplit[0];},findOrCreateActivity:function(activityName){if(activityName in activityMap){return activityMap[activityName];}
+var activity={state:ACTIVITY_STATE.NONE,name:activityName};activityMap[activityName]=activity;return activity;},deleteActivity:function(activityName){delete activityMap[activityName];},handleCreateActivity:function(ts,activityName){var activity=this.findOrCreateActivity(activityName);activity.state=ACTIVITY_STATE.CREATED;activity.createdTs=ts;},handleFocusActivity:function(ts,procName,activityName){var activity=this.findOrCreateActivity(activityName);activity.lastFocusedTs=ts;},handleProcStartForActivity:function(ts,activityName){var activity=this.findOrCreateActivity(activityName);activity.procStartTs=ts;},handleOnResumeCalled:function(ts,pid,activityName){var activity=this.findOrCreateActivity(activityName);activity.state=ACTIVITY_STATE.RESUMED;activity.lastResumeTs=ts;activity.pid=pid;},handleOnPauseCalled:function(ts,activityName){var activity=this.findOrCreateActivity(activityName);activity.state=ACTIVITY_STATE.PAUSED;activity.lastPauseTs=ts;if(ts>this.model_.bounds.min&&ts<this.model_.bounds.max){this.addActivityToProcess(activity);}},handleLaunchTime:function(ts,activityName,launchTime){var activity=this.findOrCreateActivity(activityName);activity.launchTime=launchTime;},handleDestroyActivity:function(ts,activityName){this.deleteActivity(activityName);},addActivityToProcess:function(activity){if(activity.pid===undefined)return;var process=this.model_.getOrCreateProcess(activity.pid);var range=tr.b.math.Range.fromExplicitRange(Math.max(this.model_.bounds.min,activity.lastResumeTs),activity.lastPauseTs);var newActivity=new tr.model.Activity(activity.name,'Android Activity',range,{created:activity.createdTs,procstart:activity.procStartTs,lastfocus:activity.lastFocusedTs});process.activities.push(newActivity);},parseAmLine_:function(line){var match=eventLogActivityRE.exec(line);if(!match)return;var firstRealtimeTs=this.model_.bounds.min-
+this.model_.realtime_to_monotonic_offset_ms;var year=new Date(firstRealtimeTs).getFullYear();var ts=match[1].substring(0,5)+'-'+year+' '+
+match[1].substring(5,match[1].length);var monotonicTs=Date.parse(ts)+
+this.model_.realtime_to_monotonic_offset_ms;var pid=match[2];var action=match[5];var data=match[6];if(action==='am_create_activity'){match=amCreateRE.exec(data);if(match&&match.length>=2){this.handleCreateActivity(monotonicTs,this.getFullActivityName(match[1]));}}else if(action==='am_focused_activity'){match=amFocusedRE.exec(data);if(match&&match.length>=2){this.handleFocusActivity(monotonicTs,this.getProcName(match[1]),this.getFullActivityName(match[1]));}}else if(action==='am_proc_start'){match=amProcStartRE.exec(data);if(match&&match.length>=2){this.handleProcStartForActivity(monotonicTs,this.getFullActivityName(match[1]));}}else if(action==='am_on_resume_called'){match=amOnResumeRE.exec(data);if(match&&match.length>=2){this.handleOnResumeCalled(monotonicTs,pid,match[1]);}}else if(action==='am_on_paused_called'){match=amOnPauseRE.exec(data);if(match&&match.length>=2){this.handleOnPauseCalled(monotonicTs,match[1]);}}else if(action==='am_activity_launch_time'){match=amLaunchTimeRE.exec(data);this.handleLaunchTime(monotonicTs,this.getFullActivityName(match[1]),match[2]);}else if(action==='am_destroy_activity'){match=amDestroyRE.exec(data);if(match&&match.length===2){this.handleDestroyActivity(monotonicTs,this.getFullActivityName(match[1]));}}},importEvents:function(){if(isNaN(this.model_.realtime_to_monotonic_offset_ms)){this.model_.importWarning({type:'eveng_log_clock_sync',message:'Need a trace_event_clock_sync to map realtime to import.'});return;}
+this.model_.updateBounds();var lines=this.events_.split('\n');lines.forEach(this.parseAmLine_,this);for(var activityName in activityMap){var activity=activityMap[activityName];if(activity.state===ACTIVITY_STATE.RESUMED){activity.lastPauseTs=this.model_.bounds.max;this.addActivityToProcess(activity);}}}};Importer.register(EventLogImporter);return{EventLogImporter,};});'use strict';tr.exportTo('tr.e.importer.battor',function(){function BattorImporter(model,events){this.importPriority=3;this.model_=model;this.samples_=[];this.syncTimestampsById_=new Map();this.parseTrace_(events);}
+var battorDataLineRE=new RegExp('^(-?\\d+\\.\\d+)\\s+(-?\\d+\\.\\d+)\\s+(-?\\d+\\.\\d+)'+'(?:\\s+<(\\S+)>)?$');var battorHeaderLineRE=/^# BattOr/;BattorImporter.canImport=function(events){if(!(typeof(events)==='string'||events instanceof String)){return false;}
+return battorHeaderLineRE.test(events);};BattorImporter.prototype={__proto__:tr.importer.Importer.prototype,get importerName(){return'BattorImporter';},get model(){return this.model_;},importClockSyncMarkers:function(){for(var[syncId,ts]of this.syncTimestampsById_){this.model_.clockSyncManager.addClockSyncMarker(tr.model.ClockDomainId.BATTOR,syncId,ts);}},importEvents:function(){if(this.model_.device.powerSeries){this.model_.importWarning({type:'import_error',message:'Power counter exists, can not import BattOr power trace.'});return;}
+var modelTimeTransformer=this.model_.clockSyncManager.getModelTimeTransformer(tr.model.ClockDomainId.BATTOR);var powerSeries=this.model_.device.powerSeries=new tr.model.PowerSeries(this.model_.device);for(var i=0;i<this.samples_.length;i++){var sample=this.samples_[i];powerSeries.addPowerSample(modelTimeTransformer(sample.ts),sample.powerInW);}},parseTrace_:function(trace){var lines=trace.split('\n');for(var line of lines){line=line.trim();if(line.length===0)continue;if(line.startsWith('#'))continue;var groups=battorDataLineRE.exec(line);if(!groups){this.model_.importWarning({type:'parse_error',message:'Unrecognized line in BattOr trace: '+line});continue;}
+var ts=parseFloat(groups[1]);var voltageInV=tr.b.convertUnit(parseFloat(groups[2]),tr.b.UnitPrefixScale.METRIC.MILLI,tr.b.UnitPrefixScale.METRIC.NONE);var currentInA=tr.b.convertUnit(parseFloat(groups[3]),tr.b.UnitPrefixScale.METRIC.MILLI,tr.b.UnitPrefixScale.METRIC.NONE);var syncId=groups[4];if(syncId){this.syncTimestampsById_.set(syncId,ts);}
+if(voltageInV<0||currentInA<0){this.model_.importWarning({type:'parse_error',message:'The following line in the BattOr trace has a negative '+'voltage or current, neither of which are allowed: '+line+'. A common cause of this is that the device is charging '+'while the trace is being recorded.'});continue;}
 this.samples_.push(new Sample(ts,voltageInV,currentInA));}}};function Sample(ts,voltageInV,currentInA){this.ts=ts;this.voltageInV=voltageInV;this.currentInA=currentInA;}
-Sample.prototype={get powerInW(){return this.voltageInV*this.currentInA;}};tr.importer.Importer.register(BattorImporter);return{BattorImporter:BattorImporter};});'use strict';tr.exportTo('tr.e.importer.ddms',function(){var kPid=0;var kCategory='java';var kMethodLutEndMarker='\n*end\n';var kThreadsStart='\n*threads\n';var kMethodsStart='\n*methods\n';var kTraceMethodEnter=0x00;var kTraceMethodExit=0x01;var kTraceUnroll=0x02;var kTraceMethodActionMask=0x03;var kTraceHeaderLength=32;var kTraceMagicValue=0x574f4c53;var kTraceVersionSingleClock=2;var kTraceVersionDualClock=3;var kTraceRecordSizeSingleClock=10;var kTraceRecordSizeDualClock=14;function Reader(string_payload){this.position_=0;this.data_=JSZip.utils.transformTo('uint8array',string_payload);}
-Reader.prototype={__proto__:Object.prototype,uint8:function(){var result=this.data_[this.position_];this.position_+=1;return result;},uint16:function(){var result=0;result+=this.uint8();result+=this.uint8()<<8;return result;},uint32:function(){var result=0;result+=this.uint8();result+=this.uint8()<<8;result+=this.uint8()<<16;result+=this.uint8()<<24;return result;},uint64:function(){var low=this.uint32();var high=this.uint32();var low_str=('0000000'+low.toString(16)).substr(-8);var high_str=('0000000'+high.toString(16)).substr(-8);var result=high_str+low_str;return result;},seekTo:function(position){this.position_=position;},hasMore:function(){return this.position_<this.data_.length;}};function DdmsImporter(model,data){this.importPriority=3;this.model_=model;this.data_=data;}
+Sample.prototype={get powerInW(){return this.voltageInV*this.currentInA;}};tr.importer.Importer.register(BattorImporter);return{BattorImporter,};});'use strict';tr.exportTo('tr.e.importer.ddms',function(){var kPid=0;var kCategory='java';var kMethodLutEndMarker='\n*end\n';var kThreadsStart='\n*threads\n';var kMethodsStart='\n*methods\n';var kTraceMethodEnter=0x00;var kTraceMethodExit=0x01;var kTraceUnroll=0x02;var kTraceMethodActionMask=0x03;var kTraceHeaderLength=32;var kTraceMagicValue=0x574f4c53;var kTraceVersionSingleClock=2;var kTraceVersionDualClock=3;var kTraceRecordSizeSingleClock=10;var kTraceRecordSizeDualClock=14;function Reader(stringPayload){this.position_=0;this.data_=JSZip.utils.transformTo('uint8array',stringPayload);}
+Reader.prototype={__proto__:Object.prototype,uint8:function(){var result=this.data_[this.position_];this.position_+=1;return result;},uint16:function(){var result=0;result+=this.uint8();result+=this.uint8()<<8;return result;},uint32:function(){var result=0;result+=this.uint8();result+=this.uint8()<<8;result+=this.uint8()<<16;result+=this.uint8()<<24;return result;},uint64:function(){var low=this.uint32();var high=this.uint32();var lowStr=('0000000'+low.toString(16)).substr(-8);var highStr=('0000000'+high.toString(16)).substr(-8);var result=highStr+lowStr;return result;},seekTo:function(position){this.position_=position;},hasMore:function(){return this.position_<this.data_.length;}};function DdmsImporter(model,data){this.importPriority=3;this.model_=model;this.data_=data;}
 DdmsImporter.canImport=function(data){if(typeof(data)==='string'||data instanceof String){var header=data.slice(0,1000);return header.startsWith('*version\n')&&header.indexOf('\nvm=')>=0&&header.indexOf(kThreadsStart)>=0;}
 return false;};DdmsImporter.prototype={__proto__:tr.importer.Importer.prototype,get importerName(){return'DdmsImporter';},get model(){return this.model_;},importEvents:function(){var divider=this.data_.indexOf(kMethodLutEndMarker)+
-kMethodLutEndMarker.length;this.metadata_=this.data_.slice(0,divider);this.methods_={};this.parseThreads();this.parseMethods();var traceReader=new Reader(this.data_.slice(divider));var magic=traceReader.uint32();if(magic!=kTraceMagicValue){throw Error('Failed to match magic value');}
-this.version_=traceReader.uint16();if(this.version_!=kTraceVersionDualClock){throw Error('Unknown version');}
-var dataOffest=traceReader.uint16();var startDateTime=traceReader.uint64();var recordSize=traceReader.uint16();traceReader.seekTo(dataOffest);while(traceReader.hasMore()){this.parseTraceEntry(traceReader);}},parseTraceEntry:function(reader){var tid=reader.uint16();var methodPacked=reader.uint32();var cpuSinceStart=reader.uint32();var wallClockSinceStart=reader.uint32();var method=methodPacked&~kTraceMethodActionMask;var action=methodPacked&kTraceMethodActionMask;var thread=this.getTid(tid);method=this.getMethodName(method);if(action==kTraceMethodEnter){thread.sliceGroup.beginSlice(kCategory,method,wallClockSinceStart,undefined,cpuSinceStart);}else if(thread.sliceGroup.openSliceCount){thread.sliceGroup.endSlice(wallClockSinceStart,cpuSinceStart);}},parseThreads:function(){var threads=this.metadata_.slice(this.metadata_.indexOf(kThreadsStart)+
-kThreadsStart.length);threads=threads.slice(0,threads.indexOf('\n*'));threads=threads.split('\n');threads.forEach(this.parseThread.bind(this));},parseThread:function(thread_line){var tid=thread_line.slice(0,thread_line.indexOf('\t'));var thread=this.getTid(parseInt(tid));thread.name=thread_line.slice(thread_line.indexOf('\t')+1);},getTid:function(tid){return this.model_.getOrCreateProcess(kPid).getOrCreateThread(tid);},parseMethods:function(){var methods=this.metadata_.slice(this.metadata_.indexOf(kMethodsStart)+
-kMethodsStart.length);methods=methods.slice(0,methods.indexOf('\n*'));methods=methods.split('\n');methods.forEach(this.parseMethod.bind(this));},parseMethod:function(method_line){var data=method_line.split('\t');var methodId=parseInt(data[0]);var methodName=data[1]+'.'+data[2]+data[3];this.addMethod(methodId,methodName);},addMethod:function(methodId,methodName){this.methods_[methodId]=methodName;},getMethodName:function(methodId){return this.methods_[methodId];}};tr.importer.Importer.register(DdmsImporter);return{DdmsImporter:DdmsImporter};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){function Parser(importer){this.importer=importer;this.model=importer.model;}
-Parser.prototype={__proto__:Object.prototype};var options=new tr.b.ExtensionRegistryOptions(tr.b.BASIC_REGISTRY_MODE);options.mandatoryBaseClass=Parser;tr.b.decorateExtensionRegistry(Parser,options);return{Parser:Parser};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){var ColorScheme=tr.b.ColorScheme;var Parser=tr.e.importer.linux_perf.Parser;function AndroidParser(importer){Parser.call(this,importer);importer.registerEventHandler('tracing_mark_write:android',AndroidParser.prototype.traceMarkWriteAndroidEvent.bind(this));importer.registerEventHandler('0:android',AndroidParser.prototype.traceMarkWriteAndroidEvent.bind(this));this.model_=importer.model_;this.ppids_={};}
-function parseArgs(argsString){var args={};if(argsString){var argsArray=argsString.split(';');for(var i=0;i<argsArray.length;++i){var parts=argsArray[i].split('=');if(parts[0])
-args[parts.shift()]=parts.join('=');}}
+kMethodLutEndMarker.length;this.metadata_=this.data_.slice(0,divider);this.methods_={};this.parseThreads();this.parseMethods();var traceReader=new Reader(this.data_.slice(divider));var magic=traceReader.uint32();if(magic!==kTraceMagicValue){throw Error('Failed to match magic value');}
+this.version_=traceReader.uint16();if(this.version_!==kTraceVersionDualClock){throw Error('Unknown version');}
+var dataOffest=traceReader.uint16();var startDateTime=traceReader.uint64();var recordSize=traceReader.uint16();traceReader.seekTo(dataOffest);while(traceReader.hasMore()){this.parseTraceEntry(traceReader);}},parseTraceEntry:function(reader){var tid=reader.uint16();var methodPacked=reader.uint32();var cpuSinceStart=reader.uint32();var wallClockSinceStart=reader.uint32();var method=methodPacked&~kTraceMethodActionMask;var action=methodPacked&kTraceMethodActionMask;var thread=this.getTid(tid);method=this.getMethodName(method);if(action===kTraceMethodEnter){thread.sliceGroup.beginSlice(kCategory,method,wallClockSinceStart,undefined,cpuSinceStart);}else if(thread.sliceGroup.openSliceCount){thread.sliceGroup.endSlice(wallClockSinceStart,cpuSinceStart);}},parseThreads:function(){var threads=this.metadata_.slice(this.metadata_.indexOf(kThreadsStart)+
+kThreadsStart.length);threads=threads.slice(0,threads.indexOf('\n*'));threads=threads.split('\n');threads.forEach(this.parseThread.bind(this));},parseThread:function(threadLine){var tid=threadLine.slice(0,threadLine.indexOf('\t'));var thread=this.getTid(parseInt(tid));thread.name=threadLine.slice(threadLine.indexOf('\t')+1);},getTid:function(tid){return this.model_.getOrCreateProcess(kPid).getOrCreateThread(tid);},parseMethods:function(){var methods=this.metadata_.slice(this.metadata_.indexOf(kMethodsStart)+
+kMethodsStart.length);methods=methods.slice(0,methods.indexOf('\n*'));methods=methods.split('\n');methods.forEach(this.parseMethod.bind(this));},parseMethod:function(methodLine){var data=methodLine.split('\t');var methodId=parseInt(data[0]);var methodName=data[1]+'.'+data[2]+data[3];this.addMethod(methodId,methodName);},addMethod:function(methodId,methodName){this.methods_[methodId]=methodName;},getMethodName:function(methodId){return this.methods_[methodId];}};tr.importer.Importer.register(DdmsImporter);return{DdmsImporter,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){function Parser(importer){this.importer=importer;this.model=importer.model;}
+Parser.prototype={__proto__:Object.prototype};var options=new tr.b.ExtensionRegistryOptions(tr.b.BASIC_REGISTRY_MODE);options.mandatoryBaseClass=Parser;tr.b.decorateExtensionRegistry(Parser,options);return{Parser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){var ColorScheme=tr.b.ColorScheme;var Parser=tr.e.importer.linux_perf.Parser;function AndroidParser(importer){Parser.call(this,importer);importer.registerEventHandler('tracing_mark_write:android',AndroidParser.prototype.traceMarkWriteAndroidEvent.bind(this));importer.registerEventHandler('0:android',AndroidParser.prototype.traceMarkWriteAndroidEvent.bind(this));this.model_=importer.model_;this.ppids_={};}
+function parseArgs(argsString){var args={};if(argsString){var argsArray=argsString.split(';');for(var i=0;i<argsArray.length;++i){var parts=argsArray[i].split('=');if(parts[0]){args[parts.shift()]=parts.join('=');}}}
 return args;}
 AndroidParser.prototype={__proto__:Parser.prototype,openAsyncSlice:function(thread,category,name,cookie,ts,args){var asyncSliceConstructor=tr.model.AsyncSlice.subTypes.getConstructor(category,name);var slice=new asyncSliceConstructor(category,name,ColorScheme.getColorIdForGeneralPurposeString(name),ts,args);var key=category+':'+name+':'+cookie;slice.id=cookie;slice.startThread=thread;if(!this.openAsyncSlices){this.openAsyncSlices={};}
 this.openAsyncSlices[key]=slice;},closeAsyncSlice:function(thread,category,name,cookie,ts,args){if(!this.openAsyncSlices){return;}
 var key=category+':'+name+':'+cookie;var slice=this.openAsyncSlices[key];if(!slice){return;}
 for(var arg in args){if(slice.args[arg]!==undefined){this.model_.importWarning({type:'parse_error',message:'Both the S and F events of '+slice.title+' provided values for argument '+arg+'.'+' The value of the F event will be used.'});}
 slice.args[arg]=args[arg];}
-slice.endThread=thread;slice.duration=ts-slice.start;slice.startThread.asyncSliceGroup.push(slice);slice.subSlices=[new tr.model.AsyncSlice(slice.category,slice.title,slice.colorId,slice.start,slice.args,slice.duration)];delete this.openAsyncSlices[key];},traceMarkWriteAndroidEvent:function(eventName,cpuNumber,pid,ts,eventBase){var eventData=eventBase.details.split('|');switch(eventData[0]){case'B':var ppid=parseInt(eventData[1]);var title=eventData[2];var args=parseArgs(eventData[3]);var category=eventData[4];if(category===undefined)
-category='android';var thread=this.model_.getOrCreateProcess(ppid).getOrCreateThread(pid);thread.name=eventBase.threadName;if(!thread.sliceGroup.isTimestampValidForBeginOrEnd(ts)){this.model_.importWarning({type:'parse_error',message:'Timestamps are moving backward.'});return false;}
+slice.endThread=thread;slice.duration=ts-slice.start;slice.startThread.asyncSliceGroup.push(slice);delete this.openAsyncSlices[key];},traceMarkWriteAndroidEvent:function(eventName,cpuNumber,pid,ts,eventBase){var eventData=eventBase.details.split('|');switch(eventData[0]){case'B':var ppid=parseInt(eventData[1]);var title=eventData[2];var args=parseArgs(eventData[3]);var category=eventData[4];if(category===undefined){category='android';}
+var thread=this.model_.getOrCreateProcess(ppid).getOrCreateThread(pid);thread.name=eventBase.threadName;if(!thread.sliceGroup.isTimestampValidForBeginOrEnd(ts)){this.model_.importWarning({type:'parse_error',message:'Timestamps are moving backward.'});return false;}
 this.ppids_[pid]=ppid;thread.sliceGroup.beginSlice(category,title,ts,args);break;case'E':var ppid=this.ppids_[pid];if(ppid===undefined){break;}
 var thread=this.model_.getOrCreateProcess(ppid).getOrCreateThread(pid);if(!thread.sliceGroup.openSliceCount){break;}
 var slice=thread.sliceGroup.endSlice(ts);var args=parseArgs(eventData[3]);for(var arg in args){if(slice.args[arg]!==undefined){this.model_.importWarning({type:'parse_error',message:'Both the B and E events of '+slice.title+' provided values for argument '+arg+'.'+' The value of the E event will be used.'});}
 slice.args[arg]=args[arg];}
-break;case'C':var ppid=parseInt(eventData[1]);var name=eventData[2];var value=parseInt(eventData[3]);var category=eventData[4];if(category===undefined)
-category='android';var ctr=this.model_.getOrCreateProcess(ppid).getOrCreateCounter(category,name);if(ctr.numSeries===0){ctr.addSeries(new tr.model.CounterSeries(value,ColorScheme.getColorIdForGeneralPurposeString(ctr.name+'.'+'value')));}
-ctr.series.forEach(function(series){series.addCounterSample(ts,value);});break;case'S':var ppid=parseInt(eventData[1]);var name=eventData[2];var cookie=parseInt(eventData[3]);var args=parseArgs(eventData[4]);var category=eventData[5];if(category===undefined)
-category='android';var thread=this.model_.getOrCreateProcess(ppid).getOrCreateThread(pid);thread.name=eventBase.threadName;this.ppids_[pid]=ppid;this.openAsyncSlice(thread,category,name,cookie,ts,args);break;case'F':var ppid=parseInt(eventData[1]);var name=eventData[2];var cookie=parseInt(eventData[3]);var args=parseArgs(eventData[4]);var category=eventData[5];if(category===undefined)
-category='android';var thread=this.model_.getOrCreateProcess(ppid).getOrCreateThread(pid);thread.name=eventBase.threadName;this.ppids_[pid]=ppid;this.closeAsyncSlice(thread,category,name,cookie,ts,args);break;default:return false;}
-return true;}};Parser.register(AndroidParser);return{AndroidParser:AndroidParser};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){var ColorScheme=tr.b.ColorScheme;var Parser=tr.e.importer.linux_perf.Parser;var binderTransRE=new RegExp('transaction=(\\d+) dest_node=(\\d+) '+'dest_proc=(\\d+) dest_thread=(\\d+) '+'reply=(\\d+) flags=(0x[0-9a-fA-F]+) '+'code=(0x[0-9a-fA-F]+)');var binderTransReceivedRE=/transaction=(\d+)/;function isBinderThread(name){return(name.indexOf('Binder')>-1);}
-var TF_ONE_WAY=0x01;var TF_ROOT_OBJECT=0x04;var TF_STATUS_CODE=0x08;var TF_ACCEPT_FDS=0x10;var NO_FLAGS=0;function binderFlagsToHuman(num){var flag=parseInt(num,16);var str='';if(flag&TF_ONE_WAY)
-str+='this is a one-way call: async, no return; ';if(flag&TF_ROOT_OBJECT)
-str+='contents are the components root object; ';if(flag&TF_STATUS_CODE)
-str+='contents are a 32-bit status code; ';if(flag&TF_ACCEPT_FDS)
-str+='allow replies with file descriptors; ';if(flag===NO_FLAGS)
-str+='No Flags Set';return str;}
+break;case'C':var ppid=parseInt(eventData[1]);var name=eventData[2];var value=parseInt(eventData[3]);var category=eventData[4];if(category===undefined)category='android';var ctr=this.model_.getOrCreateProcess(ppid).getOrCreateCounter(category,name);if(ctr.numSeries===0){ctr.addSeries(new tr.model.CounterSeries(value,ColorScheme.getColorIdForGeneralPurposeString(ctr.name+'.'+'value')));}
+ctr.series.forEach(function(series){series.addCounterSample(ts,value);});break;case'S':var ppid=parseInt(eventData[1]);var name=eventData[2];var cookie=parseInt(eventData[3]);var args=parseArgs(eventData[4]);var category=eventData[5];if(category===undefined)category='android';var thread=this.model_.getOrCreateProcess(ppid).getOrCreateThread(pid);thread.name=eventBase.threadName;this.ppids_[pid]=ppid;this.openAsyncSlice(thread,category,name,cookie,ts,args);break;case'F':var ppid=parseInt(eventData[1]);var name=eventData[2];var cookie=parseInt(eventData[3]);var args=parseArgs(eventData[4]);var category=eventData[5];if(category===undefined)category='android';var thread=this.model_.getOrCreateProcess(ppid).getOrCreateThread(pid);thread.name=eventBase.threadName;this.ppids_[pid]=ppid;this.closeAsyncSlice(thread,category,name,cookie,ts,args);break;default:return false;}
+return true;}};Parser.register(AndroidParser);return{AndroidParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){var ColorScheme=tr.b.ColorScheme;var Parser=tr.e.importer.linux_perf.Parser;var binderTransRE=new RegExp('transaction=(\\d+) dest_node=(\\d+) '+'dest_proc=(\\d+) dest_thread=(\\d+) '+'reply=(\\d+) flags=(0x[0-9a-fA-F]+) '+'code=(0x[0-9a-fA-F]+)');var binderTransReceivedRE=/transaction=(\d+)/;function isBinderThread(name){return(name.indexOf('Binder')>-1);}
+var TF_ONE_WAY=0x01;var TF_ROOT_OBJECT=0x04;var TF_STATUS_CODE=0x08;var TF_ACCEPT_FDS=0x10;var NO_FLAGS=0;function binderFlagsToHuman(num){var flag=parseInt(num,16);var str='';if(flag&TF_ONE_WAY){str+='this is a one-way call: async, no return; ';}
+if(flag&TF_ROOT_OBJECT){str+='contents are the components root object; ';}
+if(flag&TF_STATUS_CODE){str+='contents are a 32-bit status code; ';}
+if(flag&TF_ACCEPT_FDS){str+='allow replies with file descriptors; ';}
+if(flag===NO_FLAGS){str+='No Flags Set';}
+return str;}
 function isReplyToOrigin(calling,called){return(called.dest_proc===calling.calling_pid||called.dest_thread===calling.calling_pid);}
 function binderCodeToHuman(code){return'Java Layer Dependent';}
 function doInternalSlice(trans,slice,ts){if(slice.subSlices.length!==0){slice.subSlices[0].start=ts;return slice.subSlices[0];}
-var kthread=trans.calling_kthread.thread;var internal_slice=kthread.sliceGroup.pushCompleteSlice('binder',slice.title,ts,.001,0,0,slice.args);internal_slice.title=slice.title;internal_slice.id=slice.id;internal_slice.colorId=slice.colorId;slice.subSlices.push(internal_slice);return internal_slice;}
-function generateBinderArgsForSlice(trans,c_threadName){return{'Transaction Id':trans.transaction_key,'Destination Node':trans.dest_node,'Destination Process':trans.dest_proc,'Destination Thread':trans.dest_thread,'Destination Name':c_threadName,'Reply transaction?':trans.is_reply_transaction,'Flags':trans.flags+' '+
+var kthread=trans.calling_kthread.thread;var internalSlice=kthread.sliceGroup.pushCompleteSlice('binder',slice.title,ts,.001,0,0,slice.args);internalSlice.title=slice.title;internalSlice.id=slice.id;internalSlice.colorId=slice.colorId;slice.subSlices.push(internalSlice);return internalSlice;}
+function generateBinderArgsForSlice(trans,cThreadName){return{'Transaction Id':trans.transaction_key,'Destination Node':trans.dest_node,'Destination Process':trans.dest_proc,'Destination Thread':trans.dest_thread,'Destination Name':cThreadName,'Reply transaction?':trans.is_reply_transaction,'Flags':trans.flags+' '+
 binderFlagsToHuman(trans.flags),'Code':trans.code+' '+
 binderCodeToHuman(trans.code),'Calling PID':trans.calling_pid,'Calling tgid':trans.calling_kthread.thread.parent.pid};}
-function BinderTransaction(events,calling_pid,calling_ts,calling_kthread){this.transaction_key=parseInt(events[1]);this.dest_node=parseInt(events[2]);this.dest_proc=parseInt(events[3]);this.dest_thread=parseInt(events[4]);this.is_reply_transaction=parseInt(events[5])===1?true:false;this.expect_reply=((this.is_reply_transaction===false)&&(parseInt(events[6],16)&TF_ONE_WAY)===0);this.flags=events[6];this.code=events[7];this.calling_pid=calling_pid;this.calling_ts=calling_ts;this.calling_kthread=calling_kthread;}
+function BinderTransaction(events,callingPid,callingTs,callingKthread){this.transaction_key=parseInt(events[1]);this.dest_node=parseInt(events[2]);this.dest_proc=parseInt(events[3]);this.dest_thread=parseInt(events[4]);this.is_reply_transaction=parseInt(events[5])===1?true:false;this.expect_reply=((this.is_reply_transaction===false)&&(parseInt(events[6],16)&TF_ONE_WAY)===0);this.flags=events[6];this.code=events[7];this.calling_pid=callingPid;this.calling_ts=callingTs;this.calling_kthread=callingKthread;}
 function BinderParser(importer){Parser.call(this,importer);importer.registerEventHandler('binder_locked',BinderParser.prototype.binderLocked.bind(this));importer.registerEventHandler('binder_unlock',BinderParser.prototype.binderUnlock.bind(this));importer.registerEventHandler('binder_lock',BinderParser.prototype.binderLock.bind(this));importer.registerEventHandler('binder_transaction',BinderParser.prototype.binderTransaction.bind(this));importer.registerEventHandler('binder_transaction_received',BinderParser.prototype.binderTransactionReceived.bind(this));this.model_=importer.model;this.kthreadlookup={};this.importer_=importer;this.transWaitingRecv={};this.syncTransWaitingCompletion={};this.recursiveSyncTransWaitingCompletion_ByPID={};this.receivedTransWaitingConversion={};}
-BinderParser.prototype={__proto__:Parser.prototype,binderLock:function(eventName,cpuNumber,pid,ts,eventBase){var tgid=parseInt(eventBase.tgid);this.doNameMappings(pid,tgid,eventName.threadName);var kthread=this.importer_.getOrCreateBinderKernelThread(eventBase.threadName,tgid,pid);kthread.binderAttemptLockTS=ts;kthread.binderOpenTsA=ts;return true;},binderLocked:function(eventName,cpuNumber,pid,ts,eventBase){var binder_thread=isBinderThread(eventBase.threadName);var tgid,name;var as_slice;var need_push=false;var kthread,rthread;tgid=parseInt(eventBase.tgid);name=eventBase.threadName;kthread=this.importer_.getOrCreateBinderKernelThread(eventBase.threadName,tgid,pid);this.doNameMappings(pid,tgid,name);rthread=kthread.thread;kthread.binderLockAquiredTS=ts;if(kthread.binderAttemptLockTS===undefined)
-return false;var args=this.generateArgsForSlice(tgid,pid,name,kthread);rthread.sliceGroup.pushCompleteSlice('binder','binder lock waiting',kthread.binderAttemptLockTS,ts-kthread.binderAttemptLockTS,0,0,args);kthread.binderAttemptLockTS=undefined;return true;},binderUnlock:function(eventName,cpuNumber,pid,ts,eventBase){var tgid=parseInt(eventBase.tgid);var kthread=this.importer_.getOrCreateBinderKernelThread(eventBase.threadName,tgid,pid);if(kthread.binderLockAquiredTS===undefined)
-return false;var args=this.generateArgsForSlice(tgid,pid,eventBase.threadName,kthread);kthread.thread.sliceGroup.pushCompleteSlice('binder','binder lock held',kthread.binderLockAquiredTS,ts-kthread.binderLockAquiredTS,0,0,args);kthread.binderLockAquiredTS=undefined;return true;},binderTransaction:function(eventName,cpuNumber,pid,ts,eventBase){var event=binderTransRE.exec(eventBase.details);if(event===undefined)
-return false;var tgid=parseInt(eventBase.tgid);this.doNameMappings(pid,tgid,eventBase.threadName);var kthread;kthread=this.importer_.getOrCreateBinderKernelThread(eventBase.threadName,tgid,pid);var trans=new BinderTransaction(event,pid,ts,kthread);var args=generateBinderArgsForSlice(trans,eventBase.threadName);var prior_receive=this.getPriorReceiveOnPID(pid);if(prior_receive!==false){return this.modelPriorReceive(prior_receive,ts,pid,tgid,kthread,trans,args,event);}
-var recursive_trans=this.getRecursiveTransactionNeedingCompletion(pid);if(recursive_trans!==false)
-return this.modelRecursiveTransactions(recursive_trans,ts,pid,kthread,trans,args);var slice=kthread.thread.sliceGroup.pushCompleteSlice('binder','',ts,.03,0,0,args);slice.colorId=ColorScheme.getColorIdForGeneralPurposeString(ts.toString());trans.slice=slice;if(trans.expect_reply)
-slice.title='binder transaction';else
-slice.title='binder transaction async';this.addTransactionWaitingForRecv(trans.transaction_key,trans);return true;},binderTransactionReceived:function(eventName,cpuNumber,pid,ts,eventBase){var event=binderTransReceivedRE.exec(eventBase.details);if(event===undefined)
-return false;var transactionkey=parseInt(event[1]);var tgid=parseInt(eventBase.tgid);var kthread;kthread=this.importer_.getOrCreateBinderKernelThread(eventBase.threadName,tgid,pid);var syncComplete=this.getSyncTransNeedsCompletion(transactionkey);if(syncComplete!==false){var sync_trans=syncComplete[0];var sync_slice=sync_trans.slice;var response_trans=syncComplete[1];var response_slice=response_trans.slice;sync_slice.duration=ts-sync_slice.start;var sync_internal=doInternalSlice(sync_trans,sync_slice,ts);var response_ts=response_slice.start+response_slice.duration;var response_internal=doInternalSlice(response_trans,response_slice,response_ts);if(response_slice.outFlowEvents.length===0||sync_slice.inFlowEvents.length===0){var flow=this.generateFlow(response_internal,sync_internal,response_trans,sync_trans);sync_slice.inFlowEvents.push(flow);response_slice.outFlowEvents.push(flow);this.model_.flowEvents.push(flow);}
-for(var i=1;i<sync_slice.inFlowEvents.length;i++){sync_slice.inFlowEvents[i].duration=ts-sync_slice.inFlowEvents[i].start;}
+BinderParser.prototype={__proto__:Parser.prototype,binderLock:function(eventName,cpuNumber,pid,ts,eventBase){var tgid=parseInt(eventBase.tgid);this.doNameMappings(pid,tgid,eventName.threadName);var kthread=this.importer_.getOrCreateBinderKernelThread(eventBase.threadName,tgid,pid);kthread.binderAttemptLockTS=ts;kthread.binderOpenTsA=ts;return true;},binderLocked:function(eventName,cpuNumber,pid,ts,eventBase){var binderThread=isBinderThread(eventBase.threadName);var tgid;var name;var kthread;var rthread;tgid=parseInt(eventBase.tgid);name=eventBase.threadName;kthread=this.importer_.getOrCreateBinderKernelThread(eventBase.threadName,tgid,pid);this.doNameMappings(pid,tgid,name);rthread=kthread.thread;kthread.binderLockAquiredTS=ts;if(kthread.binderAttemptLockTS===undefined)return false;var args=this.generateArgsForSlice(tgid,pid,name,kthread);rthread.sliceGroup.pushCompleteSlice('binder','binder lock waiting',kthread.binderAttemptLockTS,ts-kthread.binderAttemptLockTS,0,0,args);kthread.binderAttemptLockTS=undefined;return true;},binderUnlock:function(eventName,cpuNumber,pid,ts,eventBase){var tgid=parseInt(eventBase.tgid);var kthread=this.importer_.getOrCreateBinderKernelThread(eventBase.threadName,tgid,pid);if(kthread.binderLockAquiredTS===undefined)return false;var args=this.generateArgsForSlice(tgid,pid,eventBase.threadName,kthread);kthread.thread.sliceGroup.pushCompleteSlice('binder','binder lock held',kthread.binderLockAquiredTS,ts-kthread.binderLockAquiredTS,0,0,args);kthread.binderLockAquiredTS=undefined;return true;},binderTransaction:function(eventName,cpuNumber,pid,ts,eventBase){var event=binderTransRE.exec(eventBase.details);if(event===undefined)return false;var tgid=parseInt(eventBase.tgid);this.doNameMappings(pid,tgid,eventBase.threadName);var kthread;kthread=this.importer_.getOrCreateBinderKernelThread(eventBase.threadName,tgid,pid);var trans=new BinderTransaction(event,pid,ts,kthread);var args=generateBinderArgsForSlice(trans,eventBase.threadName);var priorReceive=this.getPriorReceiveOnPID(pid);if(priorReceive!==false){return this.modelPriorReceive(priorReceive,ts,pid,tgid,kthread,trans,args,event);}
+var recursiveTrans=this.getRecursiveTransactionNeedingCompletion(pid);if(recursiveTrans!==false){return this.modelRecursiveTransactions(recursiveTrans,ts,pid,kthread,trans,args);}
+var slice=kthread.thread.sliceGroup.pushCompleteSlice('binder','',ts,.03,0,0,args);slice.colorId=ColorScheme.getColorIdForGeneralPurposeString(ts.toString());trans.slice=slice;if(trans.expect_reply){slice.title='binder transaction';}else{slice.title='binder transaction async';}
+this.addTransactionWaitingForRecv(trans.transaction_key,trans);return true;},binderTransactionReceived:function(eventName,cpuNumber,pid,ts,eventBase){var event=binderTransReceivedRE.exec(eventBase.details);if(event===undefined)return false;var transactionkey=parseInt(event[1]);var tgid=parseInt(eventBase.tgid);var kthread;kthread=this.importer_.getOrCreateBinderKernelThread(eventBase.threadName,tgid,pid);var syncComplete=this.getSyncTransNeedsCompletion(transactionkey);if(syncComplete!==false){var syncTrans=syncComplete[0];var syncSlice=syncTrans.slice;var responseTrans=syncComplete[1];var responseSlice=responseTrans.slice;syncSlice.duration=ts-syncSlice.start;var syncInternal=doInternalSlice(syncTrans,syncSlice,ts);var responseTs=responseSlice.start+responseSlice.duration;var responseInternal=doInternalSlice(responseTrans,responseSlice,responseTs);if(responseSlice.outFlowEvents.length===0||syncSlice.inFlowEvents.length===0){var flow=this.generateFlow(responseInternal,syncInternal,responseTrans,syncTrans);syncSlice.inFlowEvents.push(flow);responseSlice.outFlowEvents.push(flow);this.model_.flowEvents.push(flow);}
+for(var i=1;i<syncSlice.inFlowEvents.length;i++){syncSlice.inFlowEvents[i].duration=ts-syncSlice.inFlowEvents[i].start;}
 return true;}
-var tr_for_recv=this.getTransactionWaitingForRecv(transactionkey);if(tr_for_recv!==false){if(!tr_for_recv.expect_reply){var args=generateBinderArgsForSlice(tr_for_recv,eventBase.threadName);var slice=kthread.thread.sliceGroup.pushCompleteSlice('binder','binder Async recv',ts,.03,0,0,args);var fake_event=[0,0,0,0,0,0,0];var fake_trans=new BinderTransaction(fake_event,pid,ts,kthread);var flow=this.generateFlow(tr_for_recv.slice,slice,tr_for_recv,fake_trans);this.model_.flowEvents.push(flow);tr_for_recv.slice.title='binder transaction async';tr_for_recv.slice.duration=.03;return true;}
-tr_for_recv.slice.title='binder transaction';this.setCurrentReceiveOnPID(pid,[ts,tr_for_recv]);return true;}
-return false;},modelRecursiveTransactions:function(recursive_trans,ts,pid,kthread,trans,args){var recursive_slice=recursive_trans[1].slice;var orig_slice=recursive_trans[0].slice;recursive_slice.duration=ts-recursive_slice.start;trans.slice=recursive_slice;if(trans.is_reply_transaction){orig_slice.duration=ts-orig_slice.start;this.addSyncTransNeedingCompletion(trans.transaction_key,recursive_trans);if(isReplyToOrigin(recursive_trans[0],trans))
-this.removeRecursiveTransaction(pid);}else{var slice=kthread.thread.sliceGroup.pushCompleteSlice('binder','',ts,.03,0,0,args);trans.slice=slice;this.addTransactionWaitingForRecv(trans.transaction_key,trans);}
-return true;},modelPriorReceive:function(prior_receive,ts,pid,tgid,kthread,trans,args,event){var callee_slice=prior_receive[1].slice;var callee_trans=prior_receive[1];var recv_ts=prior_receive[0];var slice=kthread.thread.sliceGroup.pushCompleteSlice('binder','',recv_ts,ts-recv_ts,0,0,args);var flow=this.generateFlow(callee_slice,slice,callee_trans,trans);this.model_.flowEvents.push(flow);trans.slice=slice;if(trans.is_reply_transaction){slice.title='binder reply';this.addSyncTransNeedingCompletion(trans.transaction_key,[callee_trans,trans]);}else{slice.title='binder reply';var trans1=new BinderTransaction(event,pid,ts,kthread);slice=kthread.thread.sliceGroup.pushCompleteSlice('binder','binder transaction',recv_ts,(ts-recv_ts),0,0,args);if(!trans.expect_reply){slice.title='binder transaction async';slice.duration=.03;}else{}
-trans1.slice=slice;this.addRecursiveSyncTransNeedingCompletion(pid,[callee_trans,trans]);this.addTransactionWaitingForRecv(trans.transaction_key,trans1);}
-return true;},getRecursiveTransactionNeedingCompletion:function(pid){if(this.recursiveSyncTransWaitingCompletion_ByPID[pid]===undefined)
-return false;var len=this.recursiveSyncTransWaitingCompletion_ByPID[pid].length;if(len===0)
-return false;return this.recursiveSyncTransWaitingCompletion_ByPID[pid][len-1];},addRecursiveSyncTransNeedingCompletion:function(pid,tuple){if(this.recursiveSyncTransWaitingCompletion_ByPID[pid]===undefined)
-this.recursiveSyncTransWaitingCompletion_ByPID[pid]=[];this.recursiveSyncTransWaitingCompletion_ByPID[pid].push(tuple);},removeRecursiveTransaction:function(pid){var len=this.recursiveSyncTransWaitingCompletion_ByPID[pid].length;if(len===0){delete this.recursiveSyncTransWaitingCompletion_ByPID[pid];return;}
+var trForRecv=this.getTransactionWaitingForRecv(transactionkey);if(trForRecv!==false){if(!trForRecv.expect_reply){var args=generateBinderArgsForSlice(trForRecv,eventBase.threadName);var slice=kthread.thread.sliceGroup.pushCompleteSlice('binder','binder Async recv',ts,.03,0,0,args);var fakeEvent=[0,0,0,0,0,0,0];var fakeTrans=new BinderTransaction(fakeEvent,pid,ts,kthread);var flow=this.generateFlow(trForRecv.slice,slice,trForRecv,fakeTrans);this.model_.flowEvents.push(flow);trForRecv.slice.title='binder transaction async';trForRecv.slice.duration=.03;return true;}
+trForRecv.slice.title='binder transaction';this.setCurrentReceiveOnPID(pid,[ts,trForRecv]);return true;}
+return false;},modelRecursiveTransactions:function(recursiveTrans,ts,pid,kthread,trans,args){var recursiveSlice=recursiveTrans[1].slice;var origSlice=recursiveTrans[0].slice;recursiveSlice.duration=ts-recursiveSlice.start;trans.slice=recursiveSlice;if(trans.is_reply_transaction){origSlice.duration=ts-origSlice.start;this.addSyncTransNeedingCompletion(trans.transaction_key,recursiveTrans);if(isReplyToOrigin(recursiveTrans[0],trans)){this.removeRecursiveTransaction(pid);}}else{var slice=kthread.thread.sliceGroup.pushCompleteSlice('binder','',ts,.03,0,0,args);trans.slice=slice;this.addTransactionWaitingForRecv(trans.transaction_key,trans);}
+return true;},modelPriorReceive:function(priorReceive,ts,pid,tgid,kthread,trans,args,event){var calleeSlice=priorReceive[1].slice;var calleeTrans=priorReceive[1];var recvTs=priorReceive[0];var slice=kthread.thread.sliceGroup.pushCompleteSlice('binder','',recvTs,ts-recvTs,0,0,args);var flow=this.generateFlow(calleeSlice,slice,calleeTrans,trans);this.model_.flowEvents.push(flow);trans.slice=slice;if(trans.is_reply_transaction){slice.title='binder reply';this.addSyncTransNeedingCompletion(trans.transaction_key,[calleeTrans,trans]);}else{slice.title='binder reply';var trans1=new BinderTransaction(event,pid,ts,kthread);slice=kthread.thread.sliceGroup.pushCompleteSlice('binder','binder transaction',recvTs,(ts-recvTs),0,0,args);if(!trans.expect_reply){slice.title='binder transaction async';slice.duration=.03;}else{}
+trans1.slice=slice;this.addRecursiveSyncTransNeedingCompletion(pid,[calleeTrans,trans]);this.addTransactionWaitingForRecv(trans.transaction_key,trans1);}
+return true;},getRecursiveTransactionNeedingCompletion:function(pid){if(this.recursiveSyncTransWaitingCompletion_ByPID[pid]===undefined){return false;}
+var len=this.recursiveSyncTransWaitingCompletion_ByPID[pid].length;if(len===0)return false;return this.recursiveSyncTransWaitingCompletion_ByPID[pid][len-1];},addRecursiveSyncTransNeedingCompletion:function(pid,tuple){if(this.recursiveSyncTransWaitingCompletion_ByPID[pid]===undefined){this.recursiveSyncTransWaitingCompletion_ByPID[pid]=[];}
+this.recursiveSyncTransWaitingCompletion_ByPID[pid].push(tuple);},removeRecursiveTransaction:function(pid){var len=this.recursiveSyncTransWaitingCompletion_ByPID[pid].length;if(len===0){delete this.recursiveSyncTransWaitingCompletion_ByPID[pid];return;}
 this.recursiveSyncTransWaitingCompletion_ByPID[pid].splice(len-1,1);},setCurrentReceiveOnPID:function(pid,tuple){if(this.receivedTransWaitingConversion[pid]===undefined){this.receivedTransWaitingConversion[pid]=[];}
-this.receivedTransWaitingConversion[pid].push(tuple);},getPriorReceiveOnPID:function(pid){if(this.receivedTransWaitingConversion[pid]===undefined)
-return false;var len=this.receivedTransWaitingConversion[pid].length;if(len===0)
-return false;return this.receivedTransWaitingConversion[pid].splice(len-1,1)[0];},addSyncTransNeedingCompletion:function(transactionkey,tuple){var dict=this.syncTransWaitingCompletion;dict[transactionkey]=tuple;},getSyncTransNeedsCompletion:function(transactionkey){var ret=this.syncTransWaitingCompletion[transactionkey];if(ret===undefined)
-return false;delete this.syncTransWaitingCompletion[transactionkey];return ret;},getTransactionWaitingForRecv:function(transactionkey){var ret=this.transWaitingRecv[transactionkey];if(ret===undefined)
-return false;delete this.transWaitingRecv[transactionkey];return ret;},addTransactionWaitingForRecv:function(transactionkey,transaction){this.transWaitingRecv[transactionkey]=transaction;},generateFlow:function(from,to,from_trans,to_trans){var title='Transaction from : '+
-this.pid2name(from_trans.calling_pid)+' From PID: '+from_trans.calling_pid+' to pid: '+
-to_trans.calling_pid+' Thread Name: '+this.pid2name(to_trans.calling_pid);var ts=from.start;var flow=new tr.model.FlowEvent('binder','binder',title,1,ts,[]);flow.startSlice=from;flow.endSlice=to;flow.start=from.start;flow.duration=to.start-ts;from.outFlowEvents.push(flow);to.inFlowEvents.push(flow);return flow;},generateArgsForSlice:function(tgid,pid,name,kthread){return{'Thread Name':name,'pid':pid,'gid':tgid};},pid2name:function(pid){return this.kthreadlookup[pid];},doNameMappings:function(pid,tgid,name){this.registerPidName(pid,name);this.registerPidName(tgid,name);},registerPidName:function(pid,name){if(this.pid2name(pid)===undefined)
-this.kthreadlookup[pid]=name;}};Parser.register(BinderParser);return{BinderParser:BinderParser};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){var ColorScheme=tr.b.ColorScheme;var Parser=tr.e.importer.linux_perf.Parser;function BusParser(importer){Parser.call(this,importer);importer.registerEventHandler('memory_bus_usage',BusParser.prototype.traceMarkWriteBusEvent.bind(this));this.model_=importer.model_;this.ppids_={};}
-BusParser.prototype={__proto__:Parser.prototype,traceMarkWriteBusEvent:function(eventName,cpuNumber,pid,ts,eventBase,threadName){var re=new RegExp('bus=(\\S+) rw_bytes=(\\d+) r_bytes=(\\d+) '+'w_bytes=(\\d+) cycles=(\\d+) ns=(\\d+)');var event=re.exec(eventBase.details);var name=event[1];var rwBytes=parseInt(event[2]);var rBytes=parseInt(event[3]);var wBytes=parseInt(event[4]);var cycles=parseInt(event[5]);var ns=parseInt(event[6]);var sec=tr.b.convertUnit(ns,tr.b.UnitScale.Metric.NANO,tr.b.UnitScale.Metric.NONE);var readBandwidthInBps=rBytes/sec;var readBandwidthInMiBps=tr.b.convertUnit(readBandwidthInBps,tr.b.UnitScale.Binary.NONE,tr.b.UnitScale.Binary.MEBI);var writeBandwidthInBps=wBytes/sec;var writeBandwidthInMiBps=tr.b.convertUnit(writeBandwidthInBps,tr.b.UnitScale.Binary.NONE,tr.b.UnitScale.Binary.MEBI);var ctr=this.model_.kernel.getOrCreateCounter(null,'bus '+name+' read');if(ctr.numSeries===0){ctr.addSeries(new tr.model.CounterSeries('value',ColorScheme.getColorIdForGeneralPurposeString(ctr.name+'.'+'value')));}
+this.receivedTransWaitingConversion[pid].push(tuple);},getPriorReceiveOnPID:function(pid){if(this.receivedTransWaitingConversion[pid]===undefined){return false;}
+var len=this.receivedTransWaitingConversion[pid].length;if(len===0)return false;return this.receivedTransWaitingConversion[pid].splice(len-1,1)[0];},addSyncTransNeedingCompletion:function(transactionkey,tuple){var dict=this.syncTransWaitingCompletion;dict[transactionkey]=tuple;},getSyncTransNeedsCompletion:function(transactionkey){var ret=this.syncTransWaitingCompletion[transactionkey];if(ret===undefined)return false;delete this.syncTransWaitingCompletion[transactionkey];return ret;},getTransactionWaitingForRecv:function(transactionkey){var ret=this.transWaitingRecv[transactionkey];if(ret===undefined)return false;delete this.transWaitingRecv[transactionkey];return ret;},addTransactionWaitingForRecv:function(transactionkey,transaction){this.transWaitingRecv[transactionkey]=transaction;},generateFlow:function(from,to,fromTrans,toTrans){var title='Transaction from : '+
+this.pid2name(fromTrans.calling_pid)+' From PID: '+fromTrans.calling_pid+' to pid: '+
+toTrans.calling_pid+' Thread Name: '+this.pid2name(toTrans.calling_pid);var ts=from.start;var flow=new tr.model.FlowEvent('binder','binder',title,1,ts,[]);flow.startSlice=from;flow.endSlice=to;flow.start=from.start;flow.duration=to.start-ts;from.outFlowEvents.push(flow);to.inFlowEvents.push(flow);return flow;},generateArgsForSlice:function(tgid,pid,name,kthread){return{'Thread Name':name,'pid':pid,'gid':tgid};},pid2name:function(pid){return this.kthreadlookup[pid];},doNameMappings:function(pid,tgid,name){this.registerPidName(pid,name);this.registerPidName(tgid,name);},registerPidName:function(pid,name){if(this.pid2name(pid)===undefined){this.kthreadlookup[pid]=name;}}};Parser.register(BinderParser);return{BinderParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){var ColorScheme=tr.b.ColorScheme;var Parser=tr.e.importer.linux_perf.Parser;function BusParser(importer){Parser.call(this,importer);importer.registerEventHandler('memory_bus_usage',BusParser.prototype.traceMarkWriteBusEvent.bind(this));this.model_=importer.model_;this.ppids_={};}
+BusParser.prototype={__proto__:Parser.prototype,traceMarkWriteBusEvent:function(eventName,cpuNumber,pid,ts,eventBase,threadName){var re=new RegExp('bus=(\\S+) rw_bytes=(\\d+) r_bytes=(\\d+) '+'w_bytes=(\\d+) cycles=(\\d+) ns=(\\d+)');var event=re.exec(eventBase.details);var name=event[1];var rwBytes=parseInt(event[2]);var rBytes=parseInt(event[3]);var wBytes=parseInt(event[4]);var cycles=parseInt(event[5]);var ns=parseInt(event[6]);var sec=tr.b.convertUnit(ns,tr.b.UnitPrefixScale.METRIC.NANO,tr.b.UnitPrefixScale.METRIC.NONE);var readBandwidthInBps=rBytes/sec;var readBandwidthInMiBps=tr.b.convertUnit(readBandwidthInBps,tr.b.UnitPrefixScale.BINARY.NONE,tr.b.UnitPrefixScale.BINARY.MEBI);var writeBandwidthInBps=wBytes/sec;var writeBandwidthInMiBps=tr.b.convertUnit(writeBandwidthInBps,tr.b.UnitPrefixScale.BINARY.NONE,tr.b.UnitPrefixScale.BINARY.MEBI);var ctr=this.model_.kernel.getOrCreateCounter(null,'bus '+name+' read');if(ctr.numSeries===0){ctr.addSeries(new tr.model.CounterSeries('value',ColorScheme.getColorIdForGeneralPurposeString(ctr.name+'.'+'value')));}
 ctr.series.forEach(function(series){series.addCounterSample(ts,readBandwidthInMiBps);});ctr=this.model_.kernel.getOrCreateCounter(null,'bus '+name+' write');if(ctr.numSeries===0){ctr.addSeries(new tr.model.CounterSeries('value',ColorScheme.getColorIdForGeneralPurposeString(ctr.name+'.'+'value')));}
-ctr.series.forEach(function(series){series.addCounterSample(ts,writeBandwidthInMiBps);});return true;}};Parser.register(BusParser);return{BusParser:BusParser};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){var ColorScheme=tr.b.ColorScheme;var Parser=tr.e.importer.linux_perf.Parser;function ClockParser(importer){Parser.call(this,importer);importer.registerEventHandler('clock_set_rate',ClockParser.prototype.traceMarkWriteClockEvent.bind(this));this.model_=importer.model_;this.ppids_={};}
+ctr.series.forEach(function(series){series.addCounterSample(ts,writeBandwidthInMiBps);});return true;}};Parser.register(BusParser);return{BusParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){var ColorScheme=tr.b.ColorScheme;var Parser=tr.e.importer.linux_perf.Parser;function ClockParser(importer){Parser.call(this,importer);importer.registerEventHandler('clock_set_rate',ClockParser.prototype.traceMarkWriteClockEvent.bind(this));this.model_=importer.model_;this.ppids_={};}
 ClockParser.prototype={__proto__:Parser.prototype,traceMarkWriteClockEvent:function(eventName,cpuNumber,pid,ts,eventBase,threadName){var event=/(\S+) state=(\d+) cpu_id=(\d+)/.exec(eventBase.details);var name=event[1];var rate=parseInt(event[2]);var ctr=this.model_.kernel.getOrCreateCounter(null,name);if(ctr.numSeries===0){ctr.addSeries(new tr.model.CounterSeries('value',ColorScheme.getColorIdForGeneralPurposeString(ctr.name+'.'+'value')));}
-ctr.series.forEach(function(series){series.addCounterSample(ts,rate);});return true;}};Parser.register(ClockParser);return{ClockParser:ClockParser};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){var ColorScheme=tr.b.ColorScheme;var Parser=tr.e.importer.linux_perf.Parser;function CpufreqParser(importer){Parser.call(this,importer);importer.registerEventHandler('cpufreq_interactive_up',CpufreqParser.prototype.cpufreqUpDownEvent.bind(this));importer.registerEventHandler('cpufreq_interactive_down',CpufreqParser.prototype.cpufreqUpDownEvent.bind(this));importer.registerEventHandler('cpufreq_interactive_already',CpufreqParser.prototype.cpufreqTargetEvent.bind(this));importer.registerEventHandler('cpufreq_interactive_notyet',CpufreqParser.prototype.cpufreqTargetEvent.bind(this));importer.registerEventHandler('cpufreq_interactive_setspeed',CpufreqParser.prototype.cpufreqTargetEvent.bind(this));importer.registerEventHandler('cpufreq_interactive_target',CpufreqParser.prototype.cpufreqTargetEvent.bind(this));importer.registerEventHandler('cpufreq_interactive_boost',CpufreqParser.prototype.cpufreqBoostUnboostEvent.bind(this));importer.registerEventHandler('cpufreq_interactive_unboost',CpufreqParser.prototype.cpufreqBoostUnboostEvent.bind(this));}
+ctr.series.forEach(function(series){series.addCounterSample(ts,rate);});return true;}};Parser.register(ClockParser);return{ClockParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){var ColorScheme=tr.b.ColorScheme;var Parser=tr.e.importer.linux_perf.Parser;function CpufreqParser(importer){Parser.call(this,importer);importer.registerEventHandler('cpufreq_interactive_up',CpufreqParser.prototype.cpufreqUpDownEvent.bind(this));importer.registerEventHandler('cpufreq_interactive_down',CpufreqParser.prototype.cpufreqUpDownEvent.bind(this));importer.registerEventHandler('cpufreq_interactive_already',CpufreqParser.prototype.cpufreqTargetEvent.bind(this));importer.registerEventHandler('cpufreq_interactive_notyet',CpufreqParser.prototype.cpufreqTargetEvent.bind(this));importer.registerEventHandler('cpufreq_interactive_setspeed',CpufreqParser.prototype.cpufreqTargetEvent.bind(this));importer.registerEventHandler('cpufreq_interactive_target',CpufreqParser.prototype.cpufreqTargetEvent.bind(this));importer.registerEventHandler('cpufreq_interactive_boost',CpufreqParser.prototype.cpufreqBoostUnboostEvent.bind(this));importer.registerEventHandler('cpufreq_interactive_unboost',CpufreqParser.prototype.cpufreqBoostUnboostEvent.bind(this));}
 function splitData(input){var data={};var args=input.split(/\s+/);var len=args.length;for(var i=0;i<len;i++){var item=args[i].split('=');data[item[0]]=parseInt(item[1]);}
 return data;}
-CpufreqParser.prototype={__proto__:Parser.prototype,cpufreqSlice:function(ts,eventName,cpu,args){var kthread=this.importer.getOrCreatePseudoThread('cpufreq');kthread.openSlice=eventName;var slice=new tr.model.ThreadSlice('',kthread.openSlice,ColorScheme.getColorIdForGeneralPurposeString(kthread.openSlice),ts,args,0);kthread.thread.sliceGroup.pushSlice(slice);},cpufreqBoostSlice:function(ts,eventName,args){var kthread=this.importer.getOrCreatePseudoThread('cpufreq_boost');kthread.openSlice=eventName;var slice=new tr.model.ThreadSlice('',kthread.openSlice,ColorScheme.getColorIdForGeneralPurposeString(kthread.openSlice),ts,args,0);kthread.thread.sliceGroup.pushSlice(slice);},cpufreqUpDownEvent:function(eventName,cpuNumber,pid,ts,eventBase){var data=splitData(eventBase.details);this.cpufreqSlice(ts,eventName,data.cpu,data);return true;},cpufreqTargetEvent:function(eventName,cpuNumber,pid,ts,eventBase){var data=splitData(eventBase.details);this.cpufreqSlice(ts,eventName,data.cpu,data);return true;},cpufreqBoostUnboostEvent:function(eventName,cpuNumber,pid,ts,eventBase){this.cpufreqBoostSlice(ts,eventName,{type:eventBase.details});return true;}};Parser.register(CpufreqParser);return{CpufreqParser:CpufreqParser};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){var ColorScheme=tr.b.ColorScheme;var Parser=tr.e.importer.linux_perf.Parser;function DiskParser(importer){Parser.call(this,importer);importer.registerEventHandler('f2fs_write_begin',DiskParser.prototype.f2fsWriteBeginEvent.bind(this));importer.registerEventHandler('f2fs_write_end',DiskParser.prototype.f2fsWriteEndEvent.bind(this));importer.registerEventHandler('f2fs_sync_file_enter',DiskParser.prototype.f2fsSyncFileEnterEvent.bind(this));importer.registerEventHandler('f2fs_sync_file_exit',DiskParser.prototype.f2fsSyncFileExitEvent.bind(this));importer.registerEventHandler('ext4_sync_file_enter',DiskParser.prototype.ext4SyncFileEnterEvent.bind(this));importer.registerEventHandler('ext4_sync_file_exit',DiskParser.prototype.ext4SyncFileExitEvent.bind(this));importer.registerEventHandler('ext4_da_write_begin',DiskParser.prototype.ext4WriteBeginEvent.bind(this));importer.registerEventHandler('ext4_da_write_end',DiskParser.prototype.ext4WriteEndEvent.bind(this));importer.registerEventHandler('block_rq_issue',DiskParser.prototype.blockRqIssueEvent.bind(this));importer.registerEventHandler('block_rq_complete',DiskParser.prototype.blockRqCompleteEvent.bind(this));}
+CpufreqParser.prototype={__proto__:Parser.prototype,cpufreqSlice:function(ts,eventName,cpu,args){var kthread=this.importer.getOrCreatePseudoThread('cpufreq');kthread.openSlice=eventName;var slice=new tr.model.ThreadSlice('',kthread.openSlice,ColorScheme.getColorIdForGeneralPurposeString(kthread.openSlice),ts,args,0);kthread.thread.sliceGroup.pushSlice(slice);},cpufreqBoostSlice:function(ts,eventName,args){var kthread=this.importer.getOrCreatePseudoThread('cpufreq_boost');kthread.openSlice=eventName;var slice=new tr.model.ThreadSlice('',kthread.openSlice,ColorScheme.getColorIdForGeneralPurposeString(kthread.openSlice),ts,args,0);kthread.thread.sliceGroup.pushSlice(slice);},cpufreqUpDownEvent:function(eventName,cpuNumber,pid,ts,eventBase){var data=splitData(eventBase.details);this.cpufreqSlice(ts,eventName,data.cpu,data);return true;},cpufreqTargetEvent:function(eventName,cpuNumber,pid,ts,eventBase){var data=splitData(eventBase.details);this.cpufreqSlice(ts,eventName,data.cpu,data);return true;},cpufreqBoostUnboostEvent:function(eventName,cpuNumber,pid,ts,eventBase){this.cpufreqBoostSlice(ts,eventName,{type:eventBase.details});return true;}};Parser.register(CpufreqParser);return{CpufreqParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){var ColorScheme=tr.b.ColorScheme;var Parser=tr.e.importer.linux_perf.Parser;function DiskParser(importer){Parser.call(this,importer);importer.registerEventHandler('f2fs_write_begin',DiskParser.prototype.f2fsWriteBeginEvent.bind(this));importer.registerEventHandler('f2fs_write_end',DiskParser.prototype.f2fsWriteEndEvent.bind(this));importer.registerEventHandler('f2fs_sync_file_enter',DiskParser.prototype.f2fsSyncFileEnterEvent.bind(this));importer.registerEventHandler('f2fs_sync_file_exit',DiskParser.prototype.f2fsSyncFileExitEvent.bind(this));importer.registerEventHandler('ext4_sync_file_enter',DiskParser.prototype.ext4SyncFileEnterEvent.bind(this));importer.registerEventHandler('ext4_sync_file_exit',DiskParser.prototype.ext4SyncFileExitEvent.bind(this));importer.registerEventHandler('ext4_da_write_begin',DiskParser.prototype.ext4WriteBeginEvent.bind(this));importer.registerEventHandler('ext4_da_write_end',DiskParser.prototype.ext4WriteEndEvent.bind(this));importer.registerEventHandler('block_rq_issue',DiskParser.prototype.blockRqIssueEvent.bind(this));importer.registerEventHandler('block_rq_complete',DiskParser.prototype.blockRqCompleteEvent.bind(this));}
 DiskParser.prototype={__proto__:Parser.prototype,openAsyncSlice:function(ts,category,threadName,pid,key,name){var kthread=this.importer.getOrCreateKernelThread(category+':'+threadName,pid);var asyncSliceConstructor=tr.model.AsyncSlice.subTypes.getConstructor(category,name);var slice=new asyncSliceConstructor(category,name,ColorScheme.getColorIdForGeneralPurposeString(name),ts);slice.startThread=kthread.thread;if(!kthread.openAsyncSlices){kthread.openAsyncSlices={};}
-kthread.openAsyncSlices[key]=slice;},closeAsyncSlice:function(ts,category,threadName,pid,key,args){var kthread=this.importer.getOrCreateKernelThread(category+':'+threadName,pid);if(kthread.openAsyncSlices){var slice=kthread.openAsyncSlices[key];if(slice){slice.duration=ts-slice.start;slice.args=args;slice.endThread=kthread.thread;slice.subSlices=[new tr.model.AsyncSlice(category,slice.title,slice.colorId,slice.start,slice.args,slice.duration)];kthread.thread.asyncSliceGroup.push(slice);delete kthread.openAsyncSlices[key];}}},f2fsWriteBeginEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/dev = \((\d+,\d+)\), ino = (\d+), pos = (\d+), len = (\d+), flags = (\d+)/.exec(eventBase.details);if(!event)
-return false;var device=event[1];var inode=parseInt(event[2]);var pos=parseInt(event[3]);var len=parseInt(event[4]);var key=device+'-'+inode+'-'+pos+'-'+len;this.openAsyncSlice(ts,'f2fs',eventBase.threadName,eventBase.pid,key,'f2fs_write');return true;},f2fsWriteEndEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/dev = \((\d+,\d+)\), ino = (\d+), pos = (\d+), len = (\d+), copied = (\d+)/.exec(eventBase.details);if(!event)
-return false;var device=event[1];var inode=parseInt(event[2]);var pos=parseInt(event[3]);var len=parseInt(event[4]);var error=parseInt(event[5])!==len;var key=device+'-'+inode+'-'+pos+'-'+len;this.closeAsyncSlice(ts,'f2fs',eventBase.threadName,eventBase.pid,key,{device:device,inode:inode,error:error});return true;},ext4WriteBeginEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/dev (\d+,\d+) ino (\d+) pos (\d+) len (\d+) flags (\d+)/.exec(eventBase.details);if(!event)
-return false;var device=event[1];var inode=parseInt(event[2]);var pos=parseInt(event[3]);var len=parseInt(event[4]);var key=device+'-'+inode+'-'+pos+'-'+len;this.openAsyncSlice(ts,'ext4',eventBase.threadName,eventBase.pid,key,'ext4_write');return true;},ext4WriteEndEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/dev (\d+,\d+) ino (\d+) pos (\d+) len (\d+) copied (\d+)/.exec(eventBase.details);if(!event)
-return false;var device=event[1];var inode=parseInt(event[2]);var pos=parseInt(event[3]);var len=parseInt(event[4]);var error=parseInt(event[5])!==len;var key=device+'-'+inode+'-'+pos+'-'+len;this.closeAsyncSlice(ts,'ext4',eventBase.threadName,eventBase.pid,key,{device:device,inode:inode,error:error});return true;},f2fsSyncFileEnterEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=new RegExp('dev = \\((\\d+,\\d+)\\), ino = (\\d+), pino = (\\d+), i_mode = (\\S+), '+'i_size = (\\d+), i_nlink = (\\d+), i_blocks = (\\d+), i_advise = (\\d+)').exec(eventBase.details);if(!event)
-return false;var device=event[1];var inode=parseInt(event[2]);var key=device+'-'+inode;this.openAsyncSlice(ts,'f2fs',eventBase.threadName,eventBase.pid,key,'fsync');return true;},f2fsSyncFileExitEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=new RegExp('dev = \\((\\d+,\\d+)\\), ino = (\\d+), checkpoint is (\\S+), '+'datasync = (\\d+), ret = (\\d+)').exec(eventBase.details.replace('not needed','not_needed'));if(!event)
-return false;var device=event[1];var inode=parseInt(event[2]);var error=parseInt(event[5]);var key=device+'-'+inode;this.closeAsyncSlice(ts,'f2fs',eventBase.threadName,eventBase.pid,key,{device:device,inode:inode,error:error});return true;},ext4SyncFileEnterEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/dev (\d+,\d+) ino (\d+) parent (\d+) datasync (\d+)/.exec(eventBase.details);if(!event)
-return false;var device=event[1];var inode=parseInt(event[2]);var datasync=event[4]==1;var key=device+'-'+inode;var action=datasync?'fdatasync':'fsync';this.openAsyncSlice(ts,'ext4',eventBase.threadName,eventBase.pid,key,action);return true;},ext4SyncFileExitEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/dev (\d+,\d+) ino (\d+) ret (\d+)/.exec(eventBase.details);if(!event)
-return false;var device=event[1];var inode=parseInt(event[2]);var error=parseInt(event[3]);var key=device+'-'+inode;this.closeAsyncSlice(ts,'ext4',eventBase.threadName,eventBase.pid,key,{device:device,inode:inode,error:error});return true;},blockRqIssueEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=new RegExp('(\\d+,\\d+) (F)?([DWRN])(F)?(A)?(S)?(M)? '+'\\d+ \\(.*\\) (\\d+) \\+ (\\d+) \\[.*\\]').exec(eventBase.details);if(!event)
-return false;var action;switch(event[3]){case'D':action='discard';break;case'W':action='write';break;case'R':action='read';break;case'N':action='none';break;default:action='unknown';break;}
+kthread.openAsyncSlices[key]=slice;},closeAsyncSlice:function(ts,category,threadName,pid,key,args){var kthread=this.importer.getOrCreateKernelThread(category+':'+threadName,pid);if(kthread.openAsyncSlices){var slice=kthread.openAsyncSlices[key];if(slice){slice.duration=ts-slice.start;slice.args=args;slice.endThread=kthread.thread;slice.subSlices=[new tr.model.AsyncSlice(category,slice.title,slice.colorId,slice.start,slice.args,slice.duration)];kthread.thread.asyncSliceGroup.push(slice);delete kthread.openAsyncSlices[key];}}},f2fsWriteBeginEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/dev = \((\d+,\d+)\), ino = (\d+), pos = (\d+), len = (\d+), flags = (\d+)/.exec(eventBase.details);if(!event)return false;var device=event[1];var inode=parseInt(event[2]);var pos=parseInt(event[3]);var len=parseInt(event[4]);var key=device+'-'+inode+'-'+pos+'-'+len;this.openAsyncSlice(ts,'f2fs',eventBase.threadName,eventBase.pid,key,'f2fs_write');return true;},f2fsWriteEndEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/dev = \((\d+,\d+)\), ino = (\d+), pos = (\d+), len = (\d+), copied = (\d+)/.exec(eventBase.details);if(!event)return false;var device=event[1];var inode=parseInt(event[2]);var pos=parseInt(event[3]);var len=parseInt(event[4]);var error=parseInt(event[5])!==len;var key=device+'-'+inode+'-'+pos+'-'+len;this.closeAsyncSlice(ts,'f2fs',eventBase.threadName,eventBase.pid,key,{device:device,inode:inode,error:error});return true;},ext4WriteBeginEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/dev (\d+,\d+) ino (\d+) pos (\d+) len (\d+) flags (\d+)/.exec(eventBase.details);if(!event)return false;var device=event[1];var inode=parseInt(event[2]);var pos=parseInt(event[3]);var len=parseInt(event[4]);var key=device+'-'+inode+'-'+pos+'-'+len;this.openAsyncSlice(ts,'ext4',eventBase.threadName,eventBase.pid,key,'ext4_write');return true;},ext4WriteEndEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/dev (\d+,\d+) ino (\d+) pos (\d+) len (\d+) copied (\d+)/.exec(eventBase.details);if(!event)return false;var device=event[1];var inode=parseInt(event[2]);var pos=parseInt(event[3]);var len=parseInt(event[4]);var error=parseInt(event[5])!==len;var key=device+'-'+inode+'-'+pos+'-'+len;this.closeAsyncSlice(ts,'ext4',eventBase.threadName,eventBase.pid,key,{device:device,inode:inode,error:error});return true;},f2fsSyncFileEnterEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=new RegExp('dev = \\((\\d+,\\d+)\\), ino = (\\d+), pino = (\\d+), i_mode = (\\S+), '+'i_size = (\\d+), i_nlink = (\\d+), i_blocks = (\\d+), i_advise = (\\d+)').exec(eventBase.details);if(!event)return false;var device=event[1];var inode=parseInt(event[2]);var key=device+'-'+inode;this.openAsyncSlice(ts,'f2fs',eventBase.threadName,eventBase.pid,key,'fsync');return true;},f2fsSyncFileExitEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=new RegExp('dev = \\((\\d+,\\d+)\\), ino = (\\d+), checkpoint is (\\S+), '+'datasync = (\\d+), ret = (\\d+)').exec(eventBase.details.replace('not needed','not_needed'));if(!event)return false;var device=event[1];var inode=parseInt(event[2]);var error=parseInt(event[5]);var key=device+'-'+inode;this.closeAsyncSlice(ts,'f2fs',eventBase.threadName,eventBase.pid,key,{device:device,inode:inode,error:error});return true;},ext4SyncFileEnterEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/dev (\d+,\d+) ino (\d+) parent (\d+) datasync (\d+)/.exec(eventBase.details);if(!event)return false;var device=event[1];var inode=parseInt(event[2]);var datasync=(event[4]==='1')||(event[4]===1);var key=device+'-'+inode;var action=datasync?'fdatasync':'fsync';this.openAsyncSlice(ts,'ext4',eventBase.threadName,eventBase.pid,key,action);return true;},ext4SyncFileExitEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/dev (\d+,\d+) ino (\d+) ret (\d+)/.exec(eventBase.details);if(!event)return false;var device=event[1];var inode=parseInt(event[2]);var error=parseInt(event[3]);var key=device+'-'+inode;this.closeAsyncSlice(ts,'ext4',eventBase.threadName,eventBase.pid,key,{device:device,inode:inode,error:error});return true;},blockRqIssueEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=new RegExp('(\\d+,\\d+) (F)?([DWRN])(F)?(A)?(S)?(M)? '+'\\d+ \\(.*\\) (\\d+) \\+ (\\d+) \\[.*\\]').exec(eventBase.details);if(!event)return false;var action;switch(event[3]){case'D':action='discard';break;case'W':action='write';break;case'R':action='read';break;case'N':action='none';break;default:action='unknown';break;}
 if(event[2]){action+=' flush';}
-if(event[4]=='F'){action+=' fua';}
-if(event[5]=='A'){action+=' ahead';}
-if(event[6]=='S'){action+=' sync';}
-if(event[7]=='M'){action+=' meta';}
-var device=event[1];var sector=parseInt(event[8]);var numSectors=parseInt(event[9]);var key=device+'-'+sector+'-'+numSectors;this.openAsyncSlice(ts,'block',eventBase.threadName,eventBase.pid,key,action);return true;},blockRqCompleteEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=new RegExp('(\\d+,\\d+) (F)?([DWRN])(F)?(A)?(S)?(M)? '+'\\(.*\\) (\\d+) \\+ (\\d+) \\[(.*)\\]').exec(eventBase.details);if(!event)
-return false;var device=event[1];var sector=parseInt(event[8]);var numSectors=parseInt(event[9]);var error=parseInt(event[10]);var key=device+'-'+sector+'-'+numSectors;this.closeAsyncSlice(ts,'block',eventBase.threadName,eventBase.pid,key,{device:device,sector:sector,numSectors:numSectors,error:error});return true;}};Parser.register(DiskParser);return{DiskParser:DiskParser};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){var ColorScheme=tr.b.ColorScheme;var Parser=tr.e.importer.linux_perf.Parser;function DrmParser(importer){Parser.call(this,importer);importer.registerEventHandler('drm_vblank_event',DrmParser.prototype.vblankEvent.bind(this));}
-DrmParser.prototype={__proto__:Parser.prototype,drmVblankSlice:function(ts,eventName,args){var kthread=this.importer.getOrCreatePseudoThread('drm_vblank');kthread.openSlice=eventName;var slice=new tr.model.ThreadSlice('',kthread.openSlice,ColorScheme.getColorIdForGeneralPurposeString(kthread.openSlice),ts,args,0);kthread.thread.sliceGroup.pushSlice(slice);},vblankEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/crtc=(\d+), seq=(\d+)/.exec(eventBase.details);if(!event)
-return false;var crtc=parseInt(event[1]);var seq=parseInt(event[2]);this.drmVblankSlice(ts,'vblank:'+crtc,{crtc:crtc,seq:seq});return true;}};Parser.register(DrmParser);return{DrmParser:DrmParser};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){var ColorScheme=tr.b.ColorScheme;var Parser=tr.e.importer.linux_perf.Parser;function ExynosParser(importer){Parser.call(this,importer);importer.registerEventHandler('exynos_busfreq_target_int',ExynosParser.prototype.busfreqTargetIntEvent.bind(this));importer.registerEventHandler('exynos_busfreq_target_mif',ExynosParser.prototype.busfreqTargetMifEvent.bind(this));importer.registerEventHandler('exynos_page_flip_state',ExynosParser.prototype.pageFlipStateEvent.bind(this));}
+if(event[4]==='F'){action+=' fua';}
+if(event[5]==='A'){action+=' ahead';}
+if(event[6]==='S'){action+=' sync';}
+if(event[7]==='M'){action+=' meta';}
+var device=event[1];var sector=parseInt(event[8]);var numSectors=parseInt(event[9]);var key=device+'-'+sector+'-'+numSectors;this.openAsyncSlice(ts,'block',eventBase.threadName,eventBase.pid,key,action);return true;},blockRqCompleteEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=new RegExp('(\\d+,\\d+) (F)?([DWRN])(F)?(A)?(S)?(M)? '+'\\(.*\\) (\\d+) \\+ (\\d+) \\[(.*)\\]').exec(eventBase.details);if(!event)return false;var device=event[1];var sector=parseInt(event[8]);var numSectors=parseInt(event[9]);var error=parseInt(event[10]);var key=device+'-'+sector+'-'+numSectors;this.closeAsyncSlice(ts,'block',eventBase.threadName,eventBase.pid,key,{device:device,sector:sector,numSectors:numSectors,error:error});return true;}};Parser.register(DiskParser);return{DiskParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){var ColorScheme=tr.b.ColorScheme;var Parser=tr.e.importer.linux_perf.Parser;function DrmParser(importer){Parser.call(this,importer);importer.registerEventHandler('drm_vblank_event',DrmParser.prototype.vblankEvent.bind(this));}
+DrmParser.prototype={__proto__:Parser.prototype,drmVblankSlice:function(ts,eventName,args){var kthread=this.importer.getOrCreatePseudoThread('drm_vblank');kthread.openSlice=eventName;var slice=new tr.model.ThreadSlice('',kthread.openSlice,ColorScheme.getColorIdForGeneralPurposeString(kthread.openSlice),ts,args,0);kthread.thread.sliceGroup.pushSlice(slice);},vblankEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/crtc=(\d+), seq=(\d+)/.exec(eventBase.details);if(!event)return false;var crtc=parseInt(event[1]);var seq=parseInt(event[2]);this.drmVblankSlice(ts,'vblank:'+crtc,{crtc:crtc,seq:seq});return true;}};Parser.register(DrmParser);return{DrmParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){var ColorScheme=tr.b.ColorScheme;var Parser=tr.e.importer.linux_perf.Parser;function ExynosParser(importer){Parser.call(this,importer);importer.registerEventHandler('exynos_busfreq_target_int',ExynosParser.prototype.busfreqTargetIntEvent.bind(this));importer.registerEventHandler('exynos_busfreq_target_mif',ExynosParser.prototype.busfreqTargetMifEvent.bind(this));importer.registerEventHandler('exynos_page_flip_state',ExynosParser.prototype.pageFlipStateEvent.bind(this));}
 ExynosParser.prototype={__proto__:Parser.prototype,exynosBusfreqSample:function(name,ts,frequency){var targetCpu=this.importer.getOrCreateCpu(0);var counter=targetCpu.getOrCreateCounter('',name);if(counter.numSeries===0){counter.addSeries(new tr.model.CounterSeries('frequency',ColorScheme.getColorIdForGeneralPurposeString(counter.name+'.'+'frequency')));}
-counter.series.forEach(function(series){series.addCounterSample(ts,frequency);});},busfreqTargetIntEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/frequency=(\d+)/.exec(eventBase.details);if(!event)
-return false;this.exynosBusfreqSample('INT Frequency',ts,parseInt(event[1]));return true;},busfreqTargetMifEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/frequency=(\d+)/.exec(eventBase.details);if(!event)
-return false;this.exynosBusfreqSample('MIF Frequency',ts,parseInt(event[1]));return true;},exynosPageFlipStateOpenSlice:function(ts,pipe,fb,state){var kthread=this.importer.getOrCreatePseudoThread('exynos_flip_state (pipe:'+pipe+', fb:'+fb+')');kthread.openSliceTS=ts;kthread.openSlice=state;},exynosPageFlipStateCloseSlice:function(ts,pipe,fb,args){var kthread=this.importer.getOrCreatePseudoThread('exynos_flip_state (pipe:'+pipe+', fb:'+fb+')');if(kthread.openSlice){var slice=new tr.model.ThreadSlice('',kthread.openSlice,ColorScheme.getColorIdForGeneralPurposeString(kthread.openSlice),kthread.openSliceTS,args,ts-kthread.openSliceTS);kthread.thread.sliceGroup.pushSlice(slice);}
-kthread.openSlice=undefined;},pageFlipStateEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/pipe=(\d+), fb=(\d+), state=(.*)/.exec(eventBase.details);if(!event)
-return false;var pipe=parseInt(event[1]);var fb=parseInt(event[2]);var state=event[3];this.exynosPageFlipStateCloseSlice(ts,pipe,fb,{pipe:pipe,fb:fb});if(state!=='flipped')
-this.exynosPageFlipStateOpenSlice(ts,pipe,fb,state);return true;}};Parser.register(ExynosParser);return{ExynosParser:ExynosParser};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){var Parser=tr.e.importer.linux_perf.Parser;function GestureParser(importer){Parser.call(this,importer);importer.registerEventHandler('tracing_mark_write:log',GestureParser.prototype.logEvent.bind(this));importer.registerEventHandler('tracing_mark_write:SyncInterpret',GestureParser.prototype.syncEvent.bind(this));importer.registerEventHandler('tracing_mark_write:HandleTimer',GestureParser.prototype.timerEvent.bind(this));}
-GestureParser.prototype={__proto__:Parser.prototype,gestureOpenSlice:function(title,ts,opt_args){var thread=this.importer.getOrCreatePseudoThread('gesture').thread;thread.sliceGroup.beginSlice('touchpad_gesture',title,ts,opt_args);},gestureCloseSlice:function(title,ts){var thread=this.importer.getOrCreatePseudoThread('gesture').thread;if(thread.sliceGroup.openSliceCount){var slice=thread.sliceGroup.mostRecentlyOpenedPartialSlice;if(slice.title!=title){this.importer.model.importWarning({type:'title_match_error',message:'Titles do not match. Title is '+
+counter.series.forEach(function(series){series.addCounterSample(ts,frequency);});},busfreqTargetIntEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/frequency=(\d+)/.exec(eventBase.details);if(!event)return false;this.exynosBusfreqSample('INT Frequency',ts,parseInt(event[1]));return true;},busfreqTargetMifEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/frequency=(\d+)/.exec(eventBase.details);if(!event)return false;this.exynosBusfreqSample('MIF Frequency',ts,parseInt(event[1]));return true;},exynosPageFlipStateOpenSlice:function(ts,pipe,fb,state){var kthread=this.importer.getOrCreatePseudoThread('exynos_flip_state (pipe:'+pipe+', fb:'+fb+')');kthread.openSliceTS=ts;kthread.openSlice=state;},exynosPageFlipStateCloseSlice:function(ts,pipe,fb,args){var kthread=this.importer.getOrCreatePseudoThread('exynos_flip_state (pipe:'+pipe+', fb:'+fb+')');if(kthread.openSlice){var slice=new tr.model.ThreadSlice('',kthread.openSlice,ColorScheme.getColorIdForGeneralPurposeString(kthread.openSlice),kthread.openSliceTS,args,ts-kthread.openSliceTS);kthread.thread.sliceGroup.pushSlice(slice);}
+kthread.openSlice=undefined;},pageFlipStateEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/pipe=(\d+), fb=(\d+), state=(.*)/.exec(eventBase.details);if(!event)return false;var pipe=parseInt(event[1]);var fb=parseInt(event[2]);var state=event[3];this.exynosPageFlipStateCloseSlice(ts,pipe,fb,{pipe:pipe,fb:fb});if(state!=='flipped'){this.exynosPageFlipStateOpenSlice(ts,pipe,fb,state);}
+return true;}};Parser.register(ExynosParser);return{ExynosParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){var Parser=tr.e.importer.linux_perf.Parser;function GestureParser(importer){Parser.call(this,importer);importer.registerEventHandler('tracing_mark_write:log',GestureParser.prototype.logEvent.bind(this));importer.registerEventHandler('tracing_mark_write:SyncInterpret',GestureParser.prototype.syncEvent.bind(this));importer.registerEventHandler('tracing_mark_write:HandleTimer',GestureParser.prototype.timerEvent.bind(this));}
+GestureParser.prototype={__proto__:Parser.prototype,gestureOpenSlice:function(title,ts,opt_args){var thread=this.importer.getOrCreatePseudoThread('gesture').thread;thread.sliceGroup.beginSlice('touchpad_gesture',title,ts,opt_args);},gestureCloseSlice:function(title,ts){var thread=this.importer.getOrCreatePseudoThread('gesture').thread;if(thread.sliceGroup.openSliceCount){var slice=thread.sliceGroup.mostRecentlyOpenedPartialSlice;if(slice.title!==title){this.importer.model.importWarning({type:'title_match_error',message:'Titles do not match. Title is '+
 slice.title+' in openSlice, and is '+
 title+' in endSlice'});}else{thread.sliceGroup.endSlice(ts);}}},logEvent:function(eventName,cpuNumber,pid,ts,eventBase){var innerEvent=/^\s*(\w+):\s*(\w+)$/.exec(eventBase.details);switch(innerEvent[1]){case'start':this.gestureOpenSlice('GestureLog',ts,{name:innerEvent[2]});break;case'end':this.gestureCloseSlice('GestureLog',ts);}
 return true;},syncEvent:function(eventName,cpuNumber,pid,ts,eventBase){var innerEvent=/^\s*(\w+):\s*(\w+)$/.exec(eventBase.details);switch(innerEvent[1]){case'start':this.gestureOpenSlice('SyncInterpret',ts,{interpreter:innerEvent[2]});break;case'end':this.gestureCloseSlice('SyncInterpret',ts);}
 return true;},timerEvent:function(eventName,cpuNumber,pid,ts,eventBase){var innerEvent=/^\s*(\w+):\s*(\w+)$/.exec(eventBase.details);switch(innerEvent[1]){case'start':this.gestureOpenSlice('HandleTimer',ts,{interpreter:innerEvent[2]});break;case'end':this.gestureCloseSlice('HandleTimer',ts);}
-return true;}};Parser.register(GestureParser);return{GestureParser:GestureParser};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){var ColorScheme=tr.b.ColorScheme;var Parser=tr.e.importer.linux_perf.Parser;function I2cParser(importer){Parser.call(this,importer);importer.registerEventHandler('i2c_write',I2cParser.prototype.i2cWriteEvent.bind(this));importer.registerEventHandler('i2c_read',I2cParser.prototype.i2cReadEvent.bind(this));importer.registerEventHandler('i2c_reply',I2cParser.prototype.i2cReplyEvent.bind(this));importer.registerEventHandler('i2c_result',I2cParser.prototype.i2cResultEvent.bind(this));}
-var i2cWriteReplyRE=new RegExp('i2c-(\\d+) #(\\d+) a=([\\da-fA-F]+) f=([\\da-fA-F]+) l=(\\d+) '+'(\\[[\\da-fA-F\\-]+\\])');var i2cReadRE=/i2c-(\d+) #(\d+) a=([\da-fA-F]+) f=([\da-fA-F]+) l=(\d+)/;var i2cResultRE=/i2c-(\d+) n=(\d+) ret=(\d+)/;I2cParser.prototype={__proto__:Parser.prototype,i2cWriteEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=i2cWriteReplyRE.exec(eventBase.details);if(!event)
-return false;var adapterNumber=parseInt(event[1]);var messageNumber=event[2];var address=event[3];var flags=event[4];var dataLength=event[5];var data=event[6];var thread=this.importer.getOrCreatePseudoThread('i2c adapter '+adapterNumber);pushLastSliceIfNeeded(thread,event[1],ts);thread.lastEntryTitle='i2c write';thread.lastEntryTs=ts;thread.lastEntryArgs={'Message number':messageNumber,'Address':address,'Flags':flags,'Data Length':dataLength,'Data':data};return true;},i2cReadEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=i2cReadRE.exec(eventBase.details);if(!event)
-return false;var adapterNumber=parseInt(event[1]);var messageNumber=event[2];var address=event[3];var flags=event[4];var dataLength=event[5];var thread=this.importer.getOrCreatePseudoThread('i2c adapter '+adapterNumber);pushLastSliceIfNeeded(thread,event[1],ts);thread.lastEntryTitle='i2c read';thread.lastEntryTs=ts;thread.lastEntryArgs={'Message number':messageNumber,'Address':address,'Flags':flags,'Data Length':dataLength};return true;},i2cReplyEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=i2cWriteReplyRE.exec(eventBase.details);if(!event)
-return false;var adapterNumber=parseInt(event[1]);var messageNumber=event[2];var address=event[3];var flags=event[4];var dataLength=event[5];var data=event[6];var thread=this.importer.getOrCreatePseudoThread('i2c adapter '+adapterNumber);pushLastSliceIfNeeded(thread,event[1],ts);thread.lastEntryTitle='i2c reply';thread.lastEntryTs=ts;thread.lastEntryArgs={'Message number':messageNumber,'Address':address,'Flags':flags,'Data Length':dataLength,'Data':data};return true;},i2cResultEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=i2cResultRE.exec(eventBase.details);if(!event)
-return false;var adapterNumber=parseInt(event[1]);var numMessages=event[2];var ret=event[3];var thread=this.importer.getOrCreatePseudoThread('i2c adapter '+adapterNumber);var args=thread.lastEntryArgs;if(args!==undefined){args['Number of messages']=numMessages;args['Return']=ret;}
+return true;}};Parser.register(GestureParser);return{GestureParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){var ColorScheme=tr.b.ColorScheme;var Parser=tr.e.importer.linux_perf.Parser;function I2cParser(importer){Parser.call(this,importer);importer.registerEventHandler('i2c_write',I2cParser.prototype.i2cWriteEvent.bind(this));importer.registerEventHandler('i2c_read',I2cParser.prototype.i2cReadEvent.bind(this));importer.registerEventHandler('i2c_reply',I2cParser.prototype.i2cReplyEvent.bind(this));importer.registerEventHandler('i2c_result',I2cParser.prototype.i2cResultEvent.bind(this));}
+var i2cWriteReplyRE=new RegExp('i2c-(\\d+) #(\\d+) a=([\\da-fA-F]+) f=([\\da-fA-F]+) l=(\\d+) '+'(\\[[\\da-fA-F\\-]+\\])');var i2cReadRE=/i2c-(\d+) #(\d+) a=([\da-fA-F]+) f=([\da-fA-F]+) l=(\d+)/;var i2cResultRE=/i2c-(\d+) n=(\d+) ret=(\d+)/;I2cParser.prototype={__proto__:Parser.prototype,i2cWriteEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=i2cWriteReplyRE.exec(eventBase.details);if(!event)return false;var adapterNumber=parseInt(event[1]);var messageNumber=event[2];var address=event[3];var flags=event[4];var dataLength=event[5];var data=event[6];var thread=this.importer.getOrCreatePseudoThread('i2c adapter '+adapterNumber);pushLastSliceIfNeeded(thread,event[1],ts);thread.lastEntryTitle='i2c write';thread.lastEntryTs=ts;thread.lastEntryArgs={'Message number':messageNumber,'Address':address,'Flags':flags,'Data Length':dataLength,'Data':data};return true;},i2cReadEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=i2cReadRE.exec(eventBase.details);if(!event)return false;var adapterNumber=parseInt(event[1]);var messageNumber=event[2];var address=event[3];var flags=event[4];var dataLength=event[5];var thread=this.importer.getOrCreatePseudoThread('i2c adapter '+adapterNumber);pushLastSliceIfNeeded(thread,event[1],ts);thread.lastEntryTitle='i2c read';thread.lastEntryTs=ts;thread.lastEntryArgs={'Message number':messageNumber,'Address':address,'Flags':flags,'Data Length':dataLength};return true;},i2cReplyEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=i2cWriteReplyRE.exec(eventBase.details);if(!event)return false;var adapterNumber=parseInt(event[1]);var messageNumber=event[2];var address=event[3];var flags=event[4];var dataLength=event[5];var data=event[6];var thread=this.importer.getOrCreatePseudoThread('i2c adapter '+adapterNumber);pushLastSliceIfNeeded(thread,event[1],ts);thread.lastEntryTitle='i2c reply';thread.lastEntryTs=ts;thread.lastEntryArgs={'Message number':messageNumber,'Address':address,'Flags':flags,'Data Length':dataLength,'Data':data};return true;},i2cResultEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=i2cResultRE.exec(eventBase.details);if(!event)return false;var adapterNumber=parseInt(event[1]);var numMessages=event[2];var ret=event[3];var thread=this.importer.getOrCreatePseudoThread('i2c adapter '+adapterNumber);var args=thread.lastEntryArgs;if(args!==undefined){args['Number of messages']=numMessages;args['Return']=ret;}
 pushLastSliceIfNeeded(thread,event[1],ts);thread.lastEntryTitle=undefined;thread.lastEntryTs=undefined;thread.lastEntryArgs=undefined;return true;},};function pushLastSliceIfNeeded(thread,id,currentTs){if(thread.lastEntryTs!==undefined){var duration=currentTs-thread.lastEntryTs;var slice=new tr.model.ThreadSlice('',thread.lastEntryTitle,ColorScheme.getColorIdForGeneralPurposeString(id),thread.lastEntryTs,thread.lastEntryArgs,duration);thread.thread.sliceGroup.pushSlice(slice);}}
-Parser.register(I2cParser);return{I2cParser:I2cParser};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){var ColorScheme=tr.b.ColorScheme;var Parser=tr.e.importer.linux_perf.Parser;function I915Parser(importer){Parser.call(this,importer);importer.registerEventHandler('i915_gem_object_create',I915Parser.prototype.gemObjectCreateEvent.bind(this));importer.registerEventHandler('i915_gem_object_bind',I915Parser.prototype.gemObjectBindEvent.bind(this));importer.registerEventHandler('i915_gem_object_unbind',I915Parser.prototype.gemObjectBindEvent.bind(this));importer.registerEventHandler('i915_gem_object_change_domain',I915Parser.prototype.gemObjectChangeDomainEvent.bind(this));importer.registerEventHandler('i915_gem_object_pread',I915Parser.prototype.gemObjectPreadWriteEvent.bind(this));importer.registerEventHandler('i915_gem_object_pwrite',I915Parser.prototype.gemObjectPreadWriteEvent.bind(this));importer.registerEventHandler('i915_gem_object_fault',I915Parser.prototype.gemObjectFaultEvent.bind(this));importer.registerEventHandler('i915_gem_object_clflush',I915Parser.prototype.gemObjectDestroyEvent.bind(this));importer.registerEventHandler('i915_gem_object_destroy',I915Parser.prototype.gemObjectDestroyEvent.bind(this));importer.registerEventHandler('i915_gem_ring_dispatch',I915Parser.prototype.gemRingDispatchEvent.bind(this));importer.registerEventHandler('i915_gem_ring_flush',I915Parser.prototype.gemRingFlushEvent.bind(this));importer.registerEventHandler('i915_gem_request',I915Parser.prototype.gemRequestEvent.bind(this));importer.registerEventHandler('i915_gem_request_add',I915Parser.prototype.gemRequestEvent.bind(this));importer.registerEventHandler('i915_gem_request_complete',I915Parser.prototype.gemRequestEvent.bind(this));importer.registerEventHandler('i915_gem_request_retire',I915Parser.prototype.gemRequestEvent.bind(this));importer.registerEventHandler('i915_gem_request_wait_begin',I915Parser.prototype.gemRequestEvent.bind(this));importer.registerEventHandler('i915_gem_request_wait_end',I915Parser.prototype.gemRequestEvent.bind(this));importer.registerEventHandler('i915_gem_ring_wait_begin',I915Parser.prototype.gemRingWaitEvent.bind(this));importer.registerEventHandler('i915_gem_ring_wait_end',I915Parser.prototype.gemRingWaitEvent.bind(this));importer.registerEventHandler('i915_reg_rw',I915Parser.prototype.regRWEvent.bind(this));importer.registerEventHandler('i915_flip_request',I915Parser.prototype.flipEvent.bind(this));importer.registerEventHandler('i915_flip_complete',I915Parser.prototype.flipEvent.bind(this));importer.registerEventHandler('intel_gpu_freq_change',I915Parser.prototype.gpuFrequency.bind(this));}
+Parser.register(I2cParser);return{I2cParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){var ColorScheme=tr.b.ColorScheme;var Parser=tr.e.importer.linux_perf.Parser;function I915Parser(importer){Parser.call(this,importer);importer.registerEventHandler('i915_gem_object_create',I915Parser.prototype.gemObjectCreateEvent.bind(this));importer.registerEventHandler('i915_gem_object_bind',I915Parser.prototype.gemObjectBindEvent.bind(this));importer.registerEventHandler('i915_gem_object_unbind',I915Parser.prototype.gemObjectBindEvent.bind(this));importer.registerEventHandler('i915_gem_object_change_domain',I915Parser.prototype.gemObjectChangeDomainEvent.bind(this));importer.registerEventHandler('i915_gem_object_pread',I915Parser.prototype.gemObjectPreadWriteEvent.bind(this));importer.registerEventHandler('i915_gem_object_pwrite',I915Parser.prototype.gemObjectPreadWriteEvent.bind(this));importer.registerEventHandler('i915_gem_object_fault',I915Parser.prototype.gemObjectFaultEvent.bind(this));importer.registerEventHandler('i915_gem_object_clflush',I915Parser.prototype.gemObjectDestroyEvent.bind(this));importer.registerEventHandler('i915_gem_object_destroy',I915Parser.prototype.gemObjectDestroyEvent.bind(this));importer.registerEventHandler('i915_gem_ring_dispatch',I915Parser.prototype.gemRingDispatchEvent.bind(this));importer.registerEventHandler('i915_gem_ring_flush',I915Parser.prototype.gemRingFlushEvent.bind(this));importer.registerEventHandler('i915_gem_request',I915Parser.prototype.gemRequestEvent.bind(this));importer.registerEventHandler('i915_gem_request_add',I915Parser.prototype.gemRequestEvent.bind(this));importer.registerEventHandler('i915_gem_request_complete',I915Parser.prototype.gemRequestEvent.bind(this));importer.registerEventHandler('i915_gem_request_retire',I915Parser.prototype.gemRequestEvent.bind(this));importer.registerEventHandler('i915_gem_request_wait_begin',I915Parser.prototype.gemRequestEvent.bind(this));importer.registerEventHandler('i915_gem_request_wait_end',I915Parser.prototype.gemRequestEvent.bind(this));importer.registerEventHandler('i915_gem_ring_wait_begin',I915Parser.prototype.gemRingWaitEvent.bind(this));importer.registerEventHandler('i915_gem_ring_wait_end',I915Parser.prototype.gemRingWaitEvent.bind(this));importer.registerEventHandler('i915_reg_rw',I915Parser.prototype.regRWEvent.bind(this));importer.registerEventHandler('i915_flip_request',I915Parser.prototype.flipEvent.bind(this));importer.registerEventHandler('i915_flip_complete',I915Parser.prototype.flipEvent.bind(this));importer.registerEventHandler('intel_gpu_freq_change',I915Parser.prototype.gpuFrequency.bind(this));}
 I915Parser.prototype={__proto__:Parser.prototype,i915FlipOpenSlice:function(ts,obj,plane){var kthread=this.importer.getOrCreatePseudoThread('i915_flip');kthread.openSliceTS=ts;kthread.openSlice='flip:'+obj+'/'+plane;},i915FlipCloseSlice:function(ts,args){var kthread=this.importer.getOrCreatePseudoThread('i915_flip');if(kthread.openSlice){var slice=new tr.model.ThreadSlice('',kthread.openSlice,ColorScheme.getColorIdForGeneralPurposeString(kthread.openSlice),kthread.openSliceTS,args,ts-kthread.openSliceTS);kthread.thread.sliceGroup.pushSlice(slice);}
-kthread.openSlice=undefined;},i915GemObjectSlice:function(ts,eventName,obj,args){var kthread=this.importer.getOrCreatePseudoThread('i915_gem');kthread.openSlice=eventName+':'+obj;var slice=new tr.model.ThreadSlice('',kthread.openSlice,ColorScheme.getColorIdForGeneralPurposeString(kthread.openSlice),ts,args,0);kthread.thread.sliceGroup.pushSlice(slice);},i915GemRingSlice:function(ts,eventName,dev,ring,args){var kthread=this.importer.getOrCreatePseudoThread('i915_gem_ring');kthread.openSlice=eventName+':'+dev+'.'+ring;var slice=new tr.model.ThreadSlice('',kthread.openSlice,ColorScheme.getColorIdForGeneralPurposeString(kthread.openSlice),ts,args,0);kthread.thread.sliceGroup.pushSlice(slice);},i915RegSlice:function(ts,eventName,reg,args){var kthread=this.importer.getOrCreatePseudoThread('i915_reg');kthread.openSlice=eventName+':'+reg;var slice=new tr.model.ThreadSlice('',kthread.openSlice,ColorScheme.getColorIdForGeneralPurposeString(kthread.openSlice),ts,args,0);kthread.thread.sliceGroup.pushSlice(slice);},i915FreqChangeSlice:function(ts,eventName,args){var kthread=this.importer.getOrCreatePseudoThread('i915_gpu_freq');kthread.openSlice=eventName;var slice=new tr.model.ThreadSlice('',kthread.openSlice,ColorScheme.getColorIdForGeneralPurposeString(kthread.openSlice),ts,args,0);kthread.thread.sliceGroup.pushSlice(slice);},gemObjectCreateEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/obj=(\w+), size=(\d+)/.exec(eventBase.details);if(!event)
-return false;var obj=event[1];var size=parseInt(event[2]);this.i915GemObjectSlice(ts,eventName,obj,{obj:obj,size:size});return true;},gemObjectBindEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/obj=(\w+), offset=(\w+), size=(\d+)/.exec(eventBase.details);if(!event)
-return false;var obj=event[1];var offset=event[2];var size=parseInt(event[3]);this.i915ObjectGemSlice(ts,eventName+':'+obj,{obj:obj,offset:offset,size:size});return true;},gemObjectChangeDomainEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/obj=(\w+), read=(\w+=>\w+), write=(\w+=>\w+)/.exec(eventBase.details);if(!event)
-return false;var obj=event[1];var read=event[2];var write=event[3];this.i915GemObjectSlice(ts,eventName,obj,{obj:obj,read:read,write:write});return true;},gemObjectPreadWriteEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/obj=(\w+), offset=(\d+), len=(\d+)/.exec(eventBase.details);if(!event)
-return false;var obj=event[1];var offset=parseInt(event[2]);var len=parseInt(event[3]);this.i915GemObjectSlice(ts,eventName,obj,{obj:obj,offset:offset,len:len});return true;},gemObjectFaultEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/obj=(\w+), (\w+) index=(\d+)/.exec(eventBase.details);if(!event)
-return false;var obj=event[1];var type=event[2];var index=parseInt(event[3]);this.i915GemObjectSlice(ts,eventName,obj,{obj:obj,type:type,index:index});return true;},gemObjectDestroyEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/obj=(\w+)/.exec(eventBase.details);if(!event)
-return false;var obj=event[1];this.i915GemObjectSlice(ts,eventName,obj,{obj:obj});return true;},gemRingDispatchEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/dev=(\d+), ring=(\d+), seqno=(\d+)/.exec(eventBase.details);if(!event)
-return false;var dev=parseInt(event[1]);var ring=parseInt(event[2]);var seqno=parseInt(event[3]);this.i915GemRingSlice(ts,eventName,dev,ring,{dev:dev,ring:ring,seqno:seqno});return true;},gemRingFlushEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/dev=(\d+), ring=(\w+), invalidate=(\w+), flush=(\w+)/.exec(eventBase.details);if(!event)
-return false;var dev=parseInt(event[1]);var ring=parseInt(event[2]);var invalidate=event[3];var flush=event[4];this.i915GemRingSlice(ts,eventName,dev,ring,{dev:dev,ring:ring,invalidate:invalidate,flush:flush});return true;},gemRequestEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/dev=(\d+), ring=(\d+), seqno=(\d+)/.exec(eventBase.details);if(!event)
-return false;var dev=parseInt(event[1]);var ring=parseInt(event[2]);var seqno=parseInt(event[3]);this.i915GemRingSlice(ts,eventName,dev,ring,{dev:dev,ring:ring,seqno:seqno});return true;},gemRingWaitEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/dev=(\d+), ring=(\d+)/.exec(eventBase.details);if(!event)
-return false;var dev=parseInt(event[1]);var ring=parseInt(event[2]);this.i915GemRingSlice(ts,eventName,dev,ring,{dev:dev,ring:ring});return true;},regRWEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/(\w+) reg=(\w+), len=(\d+), val=(\(\w+, \w+\))/.exec(eventBase.details);if(!event)
-return false;var rw=event[1];var reg=event[2];var len=event[3];var data=event[3];this.i915RegSlice(ts,rw,reg,{rw:rw,reg:reg,len:len,data:data});return true;},flipEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/plane=(\d+), obj=(\w+)/.exec(eventBase.details);if(!event)
-return false;var plane=parseInt(event[1]);var obj=event[2];if(eventName=='i915_flip_request')
-this.i915FlipOpenSlice(ts,obj,plane);else
-this.i915FlipCloseSlice(ts,{obj:obj,plane:plane});return true;},gpuFrequency:function(eventName,cpuNumver,pid,ts,eventBase){var event=/new_freq=(\d+)/.exec(eventBase.details);if(!event)
-return false;var freq=parseInt(event[1]);this.i915FreqChangeSlice(ts,eventName,{freq:freq});return true;}};Parser.register(I915Parser);return{I915Parser:I915Parser};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){var ColorScheme=tr.b.ColorScheme;var Parser=tr.e.importer.linux_perf.Parser;function IrqParser(importer){Parser.call(this,importer);importer.registerEventHandler('irq_handler_entry',IrqParser.prototype.irqHandlerEntryEvent.bind(this));importer.registerEventHandler('irq_handler_exit',IrqParser.prototype.irqHandlerExitEvent.bind(this));importer.registerEventHandler('softirq_raise',IrqParser.prototype.softirqRaiseEvent.bind(this));importer.registerEventHandler('softirq_entry',IrqParser.prototype.softirqEntryEvent.bind(this));importer.registerEventHandler('softirq_exit',IrqParser.prototype.softirqExitEvent.bind(this));importer.registerEventHandler('ipi_entry',IrqParser.prototype.ipiEntryEvent.bind(this));importer.registerEventHandler('ipi_exit',IrqParser.prototype.ipiExitEvent.bind(this));}
-var irqHandlerEntryRE=/irq=(\d+) name=(.+)/;var irqHandlerExitRE=/irq=(\d+) ret=(.+)/;var softirqRE=/vec=(\d+) \[action=(.+)\]/;var ipiHandlerExitRE=/\((.+)\)/;IrqParser.prototype={__proto__:Parser.prototype,irqHandlerEntryEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=irqHandlerEntryRE.exec(eventBase.details);if(!event)
-return false;var irq=parseInt(event[1]);var name=event[2];var thread=this.importer.getOrCreatePseudoThread('irqs cpu '+cpuNumber);thread.lastEntryTs=ts;thread.irqName=name;return true;},irqHandlerExitEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=irqHandlerExitRE.exec(eventBase.details);if(!event)
-return false;var irq=parseInt(event[1]);var ret=event[2];var thread=this.importer.getOrCreatePseudoThread('irqs cpu '+cpuNumber);if(thread.lastEntryTs!==undefined){var duration=ts-thread.lastEntryTs;var slice=new tr.model.ThreadSlice('','IRQ ('+thread.irqName+')',ColorScheme.getColorIdForGeneralPurposeString(event[1]),thread.lastEntryTs,{ret:ret},duration);thread.thread.sliceGroup.pushSlice(slice);}
-thread.lastEntryTs=undefined;thread.irqName=undefined;return true;},softirqRaiseEvent:function(eventName,cpuNumber,pid,ts,eventBase){return true;},softirqEntryEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=softirqRE.exec(eventBase.details);if(!event)
-return false;var action=event[2];var thread=this.importer.getOrCreatePseudoThread('softirq cpu '+cpuNumber);thread.lastEntryTs=ts;return true;},softirqExitEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=softirqRE.exec(eventBase.details);if(!event)
-return false;var vec=parseInt(event[1]);var action=event[2];var thread=this.importer.getOrCreatePseudoThread('softirq cpu '+cpuNumber);if(thread.lastEntryTs!==undefined){var duration=ts-thread.lastEntryTs;var slice=new tr.model.ThreadSlice('',action,ColorScheme.getColorIdForGeneralPurposeString(event[1]),thread.lastEntryTs,{vec:vec},duration);thread.thread.sliceGroup.pushSlice(slice);}
-thread.lastEntryTs=undefined;return true;},ipiEntryEvent:function(eventName,cpuNumber,pid,ts,eventBase){var thread=this.importer.getOrCreatePseudoThread('irqs cpu '+cpuNumber);thread.lastEntryTs=ts;return true;},ipiExitEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=ipiHandlerExitRE.exec(eventBase.details);if(!event)
-return false;var ipiName=event[1];var thread=this.importer.getOrCreatePseudoThread('irqs cpu '+cpuNumber);if(thread.lastEntryTs!==undefined){var duration=ts-thread.lastEntryTs;var slice=new tr.model.ThreadSlice('','IPI ('+ipiName+')',ColorScheme.getColorIdForGeneralPurposeString(ipiName),thread.lastEntryTs,{},duration);thread.thread.sliceGroup.pushSlice(slice);}
-thread.lastEntryTs=undefined;return true;}};Parser.register(IrqParser);return{IrqParser:IrqParser};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){var LinuxPerfParser=tr.e.importer.linux_perf.Parser;function KernelFuncParser(importer){LinuxPerfParser.call(this,importer);importer.registerEventHandler('graph_ent',KernelFuncParser.prototype.traceKernelFuncEnterEvent.bind(this));importer.registerEventHandler('graph_ret',KernelFuncParser.prototype.traceKernelFuncReturnEvent.bind(this));this.model_=importer.model_;this.ppids_={};}
-var TestExports={};var funcEnterRE=new RegExp('func=(.+)');TestExports.funcEnterRE=funcEnterRE;KernelFuncParser.prototype={__proto__:LinuxPerfParser.prototype,traceKernelFuncEnterEvent:function(eventName,cpuNumber,pid,ts,eventBase){var eventData=funcEnterRE.exec(eventBase.details);if(!eventData)
-return false;if(eventBase.tgid===undefined){return false;}
+kthread.openSlice=undefined;},i915GemObjectSlice:function(ts,eventName,obj,args){var kthread=this.importer.getOrCreatePseudoThread('i915_gem');kthread.openSlice=eventName+':'+obj;var slice=new tr.model.ThreadSlice('',kthread.openSlice,ColorScheme.getColorIdForGeneralPurposeString(kthread.openSlice),ts,args,0);kthread.thread.sliceGroup.pushSlice(slice);},i915GemRingSlice:function(ts,eventName,dev,ring,args){var kthread=this.importer.getOrCreatePseudoThread('i915_gem_ring');kthread.openSlice=eventName+':'+dev+'.'+ring;var slice=new tr.model.ThreadSlice('',kthread.openSlice,ColorScheme.getColorIdForGeneralPurposeString(kthread.openSlice),ts,args,0);kthread.thread.sliceGroup.pushSlice(slice);},i915RegSlice:function(ts,eventName,reg,args){var kthread=this.importer.getOrCreatePseudoThread('i915_reg');kthread.openSlice=eventName+':'+reg;var slice=new tr.model.ThreadSlice('',kthread.openSlice,ColorScheme.getColorIdForGeneralPurposeString(kthread.openSlice),ts,args,0);kthread.thread.sliceGroup.pushSlice(slice);},i915FreqChangeSlice:function(ts,eventName,args){var kthread=this.importer.getOrCreatePseudoThread('i915_gpu_freq');kthread.openSlice=eventName;var slice=new tr.model.ThreadSlice('',kthread.openSlice,ColorScheme.getColorIdForGeneralPurposeString(kthread.openSlice),ts,args,0);kthread.thread.sliceGroup.pushSlice(slice);},gemObjectCreateEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/obj=(\w+), size=(\d+)/.exec(eventBase.details);if(!event)return false;var obj=event[1];var size=parseInt(event[2]);this.i915GemObjectSlice(ts,eventName,obj,{obj:obj,size:size});return true;},gemObjectBindEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/obj=(\w+), offset=(\w+), size=(\d+)/.exec(eventBase.details);if(!event)return false;var obj=event[1];var offset=event[2];var size=parseInt(event[3]);this.i915ObjectGemSlice(ts,eventName+':'+obj,{obj:obj,offset:offset,size:size});return true;},gemObjectChangeDomainEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/obj=(\w+), read=(\w+=>\w+), write=(\w+=>\w+)/.exec(eventBase.details);if(!event)return false;var obj=event[1];var read=event[2];var write=event[3];this.i915GemObjectSlice(ts,eventName,obj,{obj:obj,read:read,write:write});return true;},gemObjectPreadWriteEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/obj=(\w+), offset=(\d+), len=(\d+)/.exec(eventBase.details);if(!event)return false;var obj=event[1];var offset=parseInt(event[2]);var len=parseInt(event[3]);this.i915GemObjectSlice(ts,eventName,obj,{obj:obj,offset:offset,len:len});return true;},gemObjectFaultEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/obj=(\w+), (\w+) index=(\d+)/.exec(eventBase.details);if(!event)return false;var obj=event[1];var type=event[2];var index=parseInt(event[3]);this.i915GemObjectSlice(ts,eventName,obj,{obj:obj,type:type,index:index});return true;},gemObjectDestroyEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/obj=(\w+)/.exec(eventBase.details);if(!event)return false;var obj=event[1];this.i915GemObjectSlice(ts,eventName,obj,{obj:obj});return true;},gemRingDispatchEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/dev=(\d+), ring=(\d+), seqno=(\d+)/.exec(eventBase.details);if(!event)return false;var dev=parseInt(event[1]);var ring=parseInt(event[2]);var seqno=parseInt(event[3]);this.i915GemRingSlice(ts,eventName,dev,ring,{dev:dev,ring:ring,seqno:seqno});return true;},gemRingFlushEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/dev=(\d+), ring=(\w+), invalidate=(\w+), flush=(\w+)/.exec(eventBase.details);if(!event)return false;var dev=parseInt(event[1]);var ring=parseInt(event[2]);var invalidate=event[3];var flush=event[4];this.i915GemRingSlice(ts,eventName,dev,ring,{dev:dev,ring:ring,invalidate:invalidate,flush:flush});return true;},gemRequestEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/dev=(\d+), ring=(\d+), seqno=(\d+)/.exec(eventBase.details);if(!event)return false;var dev=parseInt(event[1]);var ring=parseInt(event[2]);var seqno=parseInt(event[3]);this.i915GemRingSlice(ts,eventName,dev,ring,{dev:dev,ring:ring,seqno:seqno});return true;},gemRingWaitEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/dev=(\d+), ring=(\d+)/.exec(eventBase.details);if(!event)return false;var dev=parseInt(event[1]);var ring=parseInt(event[2]);this.i915GemRingSlice(ts,eventName,dev,ring,{dev:dev,ring:ring});return true;},regRWEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/(\w+) reg=(\w+), len=(\d+), val=(\(\w+, \w+\))/.exec(eventBase.details);if(!event)return false;var rw=event[1];var reg=event[2];var len=event[3];var data=event[3];this.i915RegSlice(ts,rw,reg,{rw:rw,reg:reg,len:len,data:data});return true;},flipEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/plane=(\d+), obj=(\w+)/.exec(eventBase.details);if(!event)return false;var plane=parseInt(event[1]);var obj=event[2];if(eventName==='i915_flip_request'){this.i915FlipOpenSlice(ts,obj,plane);}else{this.i915FlipCloseSlice(ts,{obj:obj,plane:plane});}
+return true;},gpuFrequency:function(eventName,cpuNumver,pid,ts,eventBase){var event=/new_freq=(\d+)/.exec(eventBase.details);if(!event)return false;var freq=parseInt(event[1]);this.i915FreqChangeSlice(ts,eventName,{freq:freq});return true;}};Parser.register(I915Parser);return{I915Parser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){var ColorScheme=tr.b.ColorScheme;var Parser=tr.e.importer.linux_perf.Parser;function IrqParser(importer){Parser.call(this,importer);importer.registerEventHandler('irq_handler_entry',IrqParser.prototype.irqHandlerEntryEvent.bind(this));importer.registerEventHandler('irq_handler_exit',IrqParser.prototype.irqHandlerExitEvent.bind(this));importer.registerEventHandler('softirq_raise',IrqParser.prototype.softirqRaiseEvent.bind(this));importer.registerEventHandler('softirq_entry',IrqParser.prototype.softirqEntryEvent.bind(this));importer.registerEventHandler('softirq_exit',IrqParser.prototype.softirqExitEvent.bind(this));importer.registerEventHandler('ipi_entry',IrqParser.prototype.ipiEntryEvent.bind(this));importer.registerEventHandler('ipi_exit',IrqParser.prototype.ipiExitEvent.bind(this));}
+var irqHandlerEntryRE=/irq=(\d+) name=(.+)/;var irqHandlerExitRE=/irq=(\d+) ret=(.+)/;var softirqRE=/vec=(\d+) \[action=(.+)\]/;var ipiHandlerExitRE=/\((.+)\)/;IrqParser.prototype={__proto__:Parser.prototype,irqHandlerEntryEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=irqHandlerEntryRE.exec(eventBase.details);if(!event)return false;var irq=parseInt(event[1]);var name=event[2];var thread=this.importer.getOrCreatePseudoThread('irqs cpu '+cpuNumber);thread.lastEntryTs=ts;thread.irqName=name;return true;},irqHandlerExitEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=irqHandlerExitRE.exec(eventBase.details);if(!event)return false;var irq=parseInt(event[1]);var ret=event[2];var thread=this.importer.getOrCreatePseudoThread('irqs cpu '+cpuNumber);if(thread.lastEntryTs!==undefined){var duration=ts-thread.lastEntryTs;var slice=new tr.model.ThreadSlice('','IRQ ('+thread.irqName+')',ColorScheme.getColorIdForGeneralPurposeString(event[1]),thread.lastEntryTs,{ret:ret},duration);thread.thread.sliceGroup.pushSlice(slice);}
+thread.lastEntryTs=undefined;thread.irqName=undefined;return true;},softirqRaiseEvent:function(eventName,cpuNumber,pid,ts,eventBase){return true;},softirqEntryEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=softirqRE.exec(eventBase.details);if(!event)return false;var action=event[2];var thread=this.importer.getOrCreatePseudoThread('softirq cpu '+cpuNumber);thread.lastEntryTs=ts;return true;},softirqExitEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=softirqRE.exec(eventBase.details);if(!event)return false;var vec=parseInt(event[1]);var action=event[2];var thread=this.importer.getOrCreatePseudoThread('softirq cpu '+cpuNumber);if(thread.lastEntryTs!==undefined){var duration=ts-thread.lastEntryTs;var slice=new tr.model.ThreadSlice('',action,ColorScheme.getColorIdForGeneralPurposeString(event[1]),thread.lastEntryTs,{vec:vec},duration);thread.thread.sliceGroup.pushSlice(slice);}
+thread.lastEntryTs=undefined;return true;},ipiEntryEvent:function(eventName,cpuNumber,pid,ts,eventBase){var thread=this.importer.getOrCreatePseudoThread('irqs cpu '+cpuNumber);thread.lastEntryTs=ts;return true;},ipiExitEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=ipiHandlerExitRE.exec(eventBase.details);if(!event)return false;var ipiName=event[1];var thread=this.importer.getOrCreatePseudoThread('irqs cpu '+cpuNumber);if(thread.lastEntryTs!==undefined){var duration=ts-thread.lastEntryTs;var slice=new tr.model.ThreadSlice('','IPI ('+ipiName+')',ColorScheme.getColorIdForGeneralPurposeString(ipiName),thread.lastEntryTs,{},duration);thread.thread.sliceGroup.pushSlice(slice);}
+thread.lastEntryTs=undefined;return true;}};Parser.register(IrqParser);return{IrqParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){var LinuxPerfParser=tr.e.importer.linux_perf.Parser;function KernelFuncParser(importer){LinuxPerfParser.call(this,importer);importer.registerEventHandler('graph_ent',KernelFuncParser.prototype.traceKernelFuncEnterEvent.bind(this));importer.registerEventHandler('graph_ret',KernelFuncParser.prototype.traceKernelFuncReturnEvent.bind(this));this.model_=importer.model_;this.ppids_={};}
+var TestExports={};var funcEnterRE=new RegExp('func=(.+)');TestExports.funcEnterRE=funcEnterRE;KernelFuncParser.prototype={__proto__:LinuxPerfParser.prototype,traceKernelFuncEnterEvent:function(eventName,cpuNumber,pid,ts,eventBase){var eventData=funcEnterRE.exec(eventBase.details);if(!eventData)return false;if(eventBase.tgid===undefined){return false;}
 var tgid=parseInt(eventBase.tgid);var name=eventData[1];var thread=this.model_.getOrCreateProcess(tgid).getOrCreateThread(pid);thread.name=eventBase.threadName;var slices=thread.kernelSliceGroup;if(!slices.isTimestampValidForBeginOrEnd(ts)){this.model_.importWarning({type:'parse_error',message:'Timestamps are moving backward.'});return false;}
 var slice=slices.beginSlice(null,name,ts,{});return true;},traceKernelFuncReturnEvent:function(eventName,cpuNumber,pid,ts,eventBase){if(eventBase.tgid===undefined){return false;}
 var tgid=parseInt(eventBase.tgid);var thread=this.model_.getOrCreateProcess(tgid).getOrCreateThread(pid);thread.name=eventBase.threadName;var slices=thread.kernelSliceGroup;if(!slices.isTimestampValidForBeginOrEnd(ts)){this.model_.importWarning({type:'parse_error',message:'Timestamps are moving backward.'});return false;}
 if(slices.openSliceCount>0){slices.endSlice(ts);}
-return true;}};LinuxPerfParser.register(KernelFuncParser);return{KernelFuncParser:KernelFuncParser};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){var ColorScheme=tr.b.ColorScheme;var Parser=tr.e.importer.linux_perf.Parser;function MaliParser(importer){Parser.call(this,importer);importer.registerEventHandler('mali_dvfs_event',MaliParser.prototype.dvfsEventEvent.bind(this));importer.registerEventHandler('mali_dvfs_set_clock',MaliParser.prototype.dvfsSetClockEvent.bind(this));importer.registerEventHandler('mali_dvfs_set_voltage',MaliParser.prototype.dvfsSetVoltageEvent.bind(this));this.addJMCounter('mali_hwc_MESSAGES_SENT','Messages Sent');this.addJMCounter('mali_hwc_MESSAGES_RECEIVED','Messages Received');this.addJMCycles('mali_hwc_GPU_ACTIVE','GPU Active');this.addJMCycles('mali_hwc_IRQ_ACTIVE','IRQ Active');for(var i=0;i<7;i++){var jobStr='JS'+i;var jobHWCStr='mali_hwc_'+jobStr;this.addJMCounter(jobHWCStr+'_JOBS',jobStr+' Jobs');this.addJMCounter(jobHWCStr+'_TASKS',jobStr+' Tasks');this.addJMCycles(jobHWCStr+'_ACTIVE',jobStr+' Active');this.addJMCycles(jobHWCStr+'_WAIT_READ',jobStr+' Wait Read');this.addJMCycles(jobHWCStr+'_WAIT_ISSUE',jobStr+' Wait Issue');this.addJMCycles(jobHWCStr+'_WAIT_DEPEND',jobStr+' Wait Depend');this.addJMCycles(jobHWCStr+'_WAIT_FINISH',jobStr+' Wait Finish');}
+return true;}};LinuxPerfParser.register(KernelFuncParser);return{KernelFuncParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){var ColorScheme=tr.b.ColorScheme;var Parser=tr.e.importer.linux_perf.Parser;function MaliParser(importer){Parser.call(this,importer);importer.registerEventHandler('mali_dvfs_event',MaliParser.prototype.dvfsEventEvent.bind(this));importer.registerEventHandler('mali_dvfs_set_clock',MaliParser.prototype.dvfsSetClockEvent.bind(this));importer.registerEventHandler('mali_dvfs_set_voltage',MaliParser.prototype.dvfsSetVoltageEvent.bind(this));this.addJMCounter('mali_hwc_MESSAGES_SENT','Messages Sent');this.addJMCounter('mali_hwc_MESSAGES_RECEIVED','Messages Received');this.addJMCycles('mali_hwc_GPU_ACTIVE','GPU Active');this.addJMCycles('mali_hwc_IRQ_ACTIVE','IRQ Active');for(var i=0;i<7;i++){var jobStr='JS'+i;var jobHWCStr='mali_hwc_'+jobStr;this.addJMCounter(jobHWCStr+'_JOBS',jobStr+' Jobs');this.addJMCounter(jobHWCStr+'_TASKS',jobStr+' Tasks');this.addJMCycles(jobHWCStr+'_ACTIVE',jobStr+' Active');this.addJMCycles(jobHWCStr+'_WAIT_READ',jobStr+' Wait Read');this.addJMCycles(jobHWCStr+'_WAIT_ISSUE',jobStr+' Wait Issue');this.addJMCycles(jobHWCStr+'_WAIT_DEPEND',jobStr+' Wait Depend');this.addJMCycles(jobHWCStr+'_WAIT_FINISH',jobStr+' Wait Finish');}
 this.addTilerCounter('mali_hwc_TRIANGLES','Triangles');this.addTilerCounter('mali_hwc_QUADS','Quads');this.addTilerCounter('mali_hwc_POLYGONS','Polygons');this.addTilerCounter('mali_hwc_POINTS','Points');this.addTilerCounter('mali_hwc_LINES','Lines');this.addTilerCounter('mali_hwc_VCACHE_HIT','VCache Hit');this.addTilerCounter('mali_hwc_VCACHE_MISS','VCache Miss');this.addTilerCounter('mali_hwc_FRONT_FACING','Front Facing');this.addTilerCounter('mali_hwc_BACK_FACING','Back Facing');this.addTilerCounter('mali_hwc_PRIM_VISIBLE','Prim Visible');this.addTilerCounter('mali_hwc_PRIM_CULLED','Prim Culled');this.addTilerCounter('mali_hwc_PRIM_CLIPPED','Prim Clipped');this.addTilerCounter('mali_hwc_WRBUF_HIT','Wrbuf Hit');this.addTilerCounter('mali_hwc_WRBUF_MISS','Wrbuf Miss');this.addTilerCounter('mali_hwc_WRBUF_LINE','Wrbuf Line');this.addTilerCounter('mali_hwc_WRBUF_PARTIAL','Wrbuf Partial');this.addTilerCounter('mali_hwc_WRBUF_STALL','Wrbuf Stall');this.addTilerCycles('mali_hwc_ACTIVE','Tiler Active');this.addTilerCycles('mali_hwc_INDEX_WAIT','Index Wait');this.addTilerCycles('mali_hwc_INDEX_RANGE_WAIT','Index Range Wait');this.addTilerCycles('mali_hwc_VERTEX_WAIT','Vertex Wait');this.addTilerCycles('mali_hwc_PCACHE_WAIT','Pcache Wait');this.addTilerCycles('mali_hwc_WRBUF_WAIT','Wrbuf Wait');this.addTilerCycles('mali_hwc_BUS_READ','Bus Read');this.addTilerCycles('mali_hwc_BUS_WRITE','Bus Write');this.addTilerCycles('mali_hwc_TILER_UTLB_STALL','Tiler UTLB Stall');this.addTilerCycles('mali_hwc_TILER_UTLB_HIT','Tiler UTLB Hit');this.addFragCycles('mali_hwc_FRAG_ACTIVE','Active');this.addFragCounter('mali_hwc_FRAG_PRIMATIVES','Primitives');this.addFragCounter('mali_hwc_FRAG_PRIMATIVES_DROPPED','Primitives Dropped');this.addFragCycles('mali_hwc_FRAG_CYCLE_DESC','Descriptor Processing');this.addFragCycles('mali_hwc_FRAG_CYCLES_PLR','PLR Processing??');this.addFragCycles('mali_hwc_FRAG_CYCLES_VERT','Vertex Processing');this.addFragCycles('mali_hwc_FRAG_CYCLES_TRISETUP','Triangle Setup');this.addFragCycles('mali_hwc_FRAG_CYCLES_RAST','Rasterization???');this.addFragCounter('mali_hwc_FRAG_THREADS','Threads');this.addFragCounter('mali_hwc_FRAG_DUMMY_THREADS','Dummy Threads');this.addFragCounter('mali_hwc_FRAG_QUADS_RAST','Quads Rast');this.addFragCounter('mali_hwc_FRAG_QUADS_EZS_TEST','Quads EZS Test');this.addFragCounter('mali_hwc_FRAG_QUADS_EZS_KILLED','Quads EZS Killed');this.addFragCounter('mali_hwc_FRAG_QUADS_LZS_TEST','Quads LZS Test');this.addFragCounter('mali_hwc_FRAG_QUADS_LZS_KILLED','Quads LZS Killed');this.addFragCycles('mali_hwc_FRAG_CYCLE_NO_TILE','No Tiles');this.addFragCounter('mali_hwc_FRAG_NUM_TILES','Tiles');this.addFragCounter('mali_hwc_FRAG_TRANS_ELIM','Transactions Eliminated');this.addComputeCycles('mali_hwc_COMPUTE_ACTIVE','Active');this.addComputeCounter('mali_hwc_COMPUTE_TASKS','Tasks');this.addComputeCounter('mali_hwc_COMPUTE_THREADS','Threads Started');this.addComputeCycles('mali_hwc_COMPUTE_CYCLES_DESC','Waiting for Descriptors');this.addTripipeCycles('mali_hwc_TRIPIPE_ACTIVE','Active');this.addArithCounter('mali_hwc_ARITH_WORDS','Instructions (/Pipes)');this.addArithCycles('mali_hwc_ARITH_CYCLES_REG','Reg scheduling stalls (/Pipes)');this.addArithCycles('mali_hwc_ARITH_CYCLES_L0','L0 cache miss stalls (/Pipes)');this.addArithCounter('mali_hwc_ARITH_FRAG_DEPEND','Frag dep check failures (/Pipes)');this.addLSCounter('mali_hwc_LS_WORDS','Instruction Words Completed');this.addLSCounter('mali_hwc_LS_ISSUES','Full Pipeline Issues');this.addLSCounter('mali_hwc_LS_RESTARTS','Restarts (unpairable insts)');this.addLSCounter('mali_hwc_LS_REISSUES_MISS','Pipeline reissue (cache miss/uTLB)');this.addLSCounter('mali_hwc_LS_REISSUES_VD','Pipeline reissue (varying data)');this.addLSCounter('mali_hwc_LS_REISSUE_ATTRIB_MISS','Pipeline reissue (attribute cache miss)');this.addLSCounter('mali_hwc_LS_REISSUE_NO_WB','Writeback not used');this.addTexCounter('mali_hwc_TEX_WORDS','Words');this.addTexCounter('mali_hwc_TEX_BUBBLES','Bubbles');this.addTexCounter('mali_hwc_TEX_WORDS_L0','Words L0');this.addTexCounter('mali_hwc_TEX_WORDS_DESC','Words Desc');this.addTexCounter('mali_hwc_TEX_THREADS','Threads');this.addTexCounter('mali_hwc_TEX_RECIRC_FMISS','Recirc due to Full Miss');this.addTexCounter('mali_hwc_TEX_RECIRC_DESC','Recirc due to Desc Miss');this.addTexCounter('mali_hwc_TEX_RECIRC_MULTI','Recirc due to Multipass');this.addTexCounter('mali_hwc_TEX_RECIRC_PMISS','Recirc due to Partial Cache Miss');this.addTexCounter('mali_hwc_TEX_RECIRC_CONF','Recirc due to Cache Conflict');this.addLSCCounter('mali_hwc_LSC_READ_HITS','Read Hits');this.addLSCCounter('mali_hwc_LSC_READ_MISSES','Read Misses');this.addLSCCounter('mali_hwc_LSC_WRITE_HITS','Write Hits');this.addLSCCounter('mali_hwc_LSC_WRITE_MISSES','Write Misses');this.addLSCCounter('mali_hwc_LSC_ATOMIC_HITS','Atomic Hits');this.addLSCCounter('mali_hwc_LSC_ATOMIC_MISSES','Atomic Misses');this.addLSCCounter('mali_hwc_LSC_LINE_FETCHES','Line Fetches');this.addLSCCounter('mali_hwc_LSC_DIRTY_LINE','Dirty Lines');this.addLSCCounter('mali_hwc_LSC_SNOOPS','Snoops');this.addAXICounter('mali_hwc_AXI_TLB_STALL','Address channel stall');this.addAXICounter('mali_hwc_AXI_TLB_MISS','Cache Miss');this.addAXICounter('mali_hwc_AXI_TLB_TRANSACTION','Transactions');this.addAXICounter('mali_hwc_LS_TLB_MISS','LS Cache Miss');this.addAXICounter('mali_hwc_LS_TLB_HIT','LS Cache Hit');this.addAXICounter('mali_hwc_AXI_BEATS_READ','Read Beats');this.addAXICounter('mali_hwc_AXI_BEATS_WRITE','Write Beats');this.addMMUCounter('mali_hwc_MMU_TABLE_WALK','Page Table Walks');this.addMMUCounter('mali_hwc_MMU_REPLAY_MISS','Cache Miss from Replay Buffer');this.addMMUCounter('mali_hwc_MMU_REPLAY_FULL','Replay Buffer Full');this.addMMUCounter('mali_hwc_MMU_NEW_MISS','Cache Miss on New Request');this.addMMUCounter('mali_hwc_MMU_HIT','Cache Hit');this.addMMUCycles('mali_hwc_UTLB_STALL','UTLB Stalled');this.addMMUCycles('mali_hwc_UTLB_REPLAY_MISS','UTLB Replay Miss');this.addMMUCycles('mali_hwc_UTLB_REPLAY_FULL','UTLB Replay Full');this.addMMUCycles('mali_hwc_UTLB_NEW_MISS','UTLB New Miss');this.addMMUCycles('mali_hwc_UTLB_HIT','UTLB Hit');this.addL2Counter('mali_hwc_L2_READ_BEATS','Read Beats');this.addL2Counter('mali_hwc_L2_WRITE_BEATS','Write Beats');this.addL2Counter('mali_hwc_L2_ANY_LOOKUP','Any Lookup');this.addL2Counter('mali_hwc_L2_READ_LOOKUP','Read Lookup');this.addL2Counter('mali_hwc_L2_SREAD_LOOKUP','Shareable Read Lookup');this.addL2Counter('mali_hwc_L2_READ_REPLAY','Read Replayed');this.addL2Counter('mali_hwc_L2_READ_SNOOP','Read Snoop');this.addL2Counter('mali_hwc_L2_READ_HIT','Read Cache Hit');this.addL2Counter('mali_hwc_L2_CLEAN_MISS','CleanUnique Miss');this.addL2Counter('mali_hwc_L2_WRITE_LOOKUP','Write Lookup');this.addL2Counter('mali_hwc_L2_SWRITE_LOOKUP','Shareable Write Lookup');this.addL2Counter('mali_hwc_L2_WRITE_REPLAY','Write Replayed');this.addL2Counter('mali_hwc_L2_WRITE_SNOOP','Write Snoop');this.addL2Counter('mali_hwc_L2_WRITE_HIT','Write Cache Hit');this.addL2Counter('mali_hwc_L2_EXT_READ_FULL','ExtRD with BIU Full');this.addL2Counter('mali_hwc_L2_EXT_READ_HALF','ExtRD with BIU >1/2 Full');this.addL2Counter('mali_hwc_L2_EXT_WRITE_FULL','ExtWR with BIU Full');this.addL2Counter('mali_hwc_L2_EXT_WRITE_HALF','ExtWR with BIU >1/2 Full');this.addL2Counter('mali_hwc_L2_EXT_READ','External Read (ExtRD)');this.addL2Counter('mali_hwc_L2_EXT_READ_LINE','ExtRD (linefill)');this.addL2Counter('mali_hwc_L2_EXT_WRITE','External Write (ExtWR)');this.addL2Counter('mali_hwc_L2_EXT_WRITE_LINE','ExtWR (linefill)');this.addL2Counter('mali_hwc_L2_EXT_WRITE_SMALL','ExtWR (burst size <64B)');this.addL2Counter('mali_hwc_L2_EXT_BARRIER','External Barrier');this.addL2Counter('mali_hwc_L2_EXT_AR_STALL','Address Read stalls');this.addL2Counter('mali_hwc_L2_EXT_R_BUF_FULL','Response Buffer full stalls');this.addL2Counter('mali_hwc_L2_EXT_RD_BUF_FULL','Read Data Buffer full stalls');this.addL2Counter('mali_hwc_L2_EXT_R_RAW','RAW hazard stalls');this.addL2Counter('mali_hwc_L2_EXT_W_STALL','Write Data stalls');this.addL2Counter('mali_hwc_L2_EXT_W_BUF_FULL','Write Data Buffer full');this.addL2Counter('mali_hwc_L2_EXT_R_W_HAZARD','WAW or WAR hazard stalls');this.addL2Counter('mali_hwc_L2_TAG_HAZARD','Tag hazard replays');this.addL2Cycles('mali_hwc_L2_SNOOP_FULL','Snoop buffer full');this.addL2Cycles('mali_hwc_L2_REPLAY_FULL','Replay buffer full');importer.registerEventHandler('tracing_mark_write:mali_driver',MaliParser.prototype.maliDDKEvent.bind(this));this.model_=importer.model_;}
 MaliParser.prototype={__proto__:Parser.prototype,maliDDKOpenSlice:function(pid,tid,ts,func,blockinfo){var thread=this.importer.model_.getOrCreateProcess(pid).getOrCreateThread(tid);var funcArgs=/^([\w\d_]*)(?:\(\))?:?\s*(.*)$/.exec(func);thread.sliceGroup.beginSlice('gpu-driver',funcArgs[1],ts,{'args':funcArgs[2],'blockinfo':blockinfo});},maliDDKCloseSlice:function(pid,tid,ts,args,blockinfo){var thread=this.importer.model_.getOrCreateProcess(pid).getOrCreateThread(tid);if(!thread.sliceGroup.openSliceCount){return;}
-thread.sliceGroup.endSlice(ts);},autoDetectLineRE:function(line){var lineREWithThread=/^\s*\(([\w\-]*)\)\s*(\w+):\s*([\w\\\/\.\-]*@\d*):?\s*(.*)$/;if(lineREWithThread.test(line))
-return lineREWithThread;var lineRENoThread=/^s*()(\w+):\s*([\w\\\/.\-]*):?\s*(.*)$/;if(lineRENoThread.test(line))
-return lineRENoThread;return null;},lineRE:null,maliDDKEvent:function(eventName,cpuNumber,pid,ts,eventBase){if(this.lineRE==null){this.lineRE=this.autoDetectLineRE(eventBase.details);if(this.lineRE==null)
-return false;}
+thread.sliceGroup.endSlice(ts);},autoDetectLineRE:function(line){var lineREWithThread=/^\s*\(([\w\-]*)\)\s*(\w+):\s*([\w\\\/\.\-]*@\d*):?\s*(.*)$/;if(lineREWithThread.test(line)){return lineREWithThread;}
+var lineRENoThread=/^s*()(\w+):\s*([\w\\\/.\-]*):?\s*(.*)$/;if(lineRENoThread.test(line)){return lineRENoThread;}
+return null;},lineRE:null,maliDDKEvent:function(eventName,cpuNumber,pid,ts,eventBase){if(this.lineRE===null){this.lineRE=this.autoDetectLineRE(eventBase.details);if(this.lineRE===null)return false;}
 var maliEvent=this.lineRE.exec(eventBase.details);var tid=(maliEvent[1]===''?'mali':maliEvent[1]);switch(maliEvent[2]){case'cros_trace_print_enter':this.maliDDKOpenSlice(pid,tid,ts,maliEvent[4],maliEvent[3]);break;case'cros_trace_print_exit':this.maliDDKCloseSlice(pid,tid,ts,[],maliEvent[3]);}
 return true;},dvfsSample:function(counterName,seriesName,ts,s){var value=parseInt(s);var counter=this.model_.kernel.getOrCreateCounter('DVFS',counterName);if(counter.numSeries===0){counter.addSeries(new tr.model.CounterSeries(seriesName,ColorScheme.getColorIdForGeneralPurposeString(counter.name)));}
-counter.series.forEach(function(series){series.addCounterSample(ts,value);});},dvfsEventEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/utilization=(\d+)/.exec(eventBase.details);if(!event)
-return false;this.dvfsSample('DVFS Utilization','utilization',ts,event[1]);return true;},dvfsSetClockEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/frequency=(\d+)/.exec(eventBase.details);if(!event)
-return false;this.dvfsSample('DVFS Frequency','frequency',ts,event[1]);return true;},dvfsSetVoltageEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/voltage=(\d+)/.exec(eventBase.details);if(!event)
-return false;this.dvfsSample('DVFS Voltage','voltage',ts,event[1]);return true;},hwcSample:function(cat,counterName,seriesName,ts,eventBase){var event=/val=(\d+)/.exec(eventBase.details);if(!event)
-return false;var value=parseInt(event[1]);var counter=this.model_.kernel.getOrCreateCounter(cat,counterName);if(counter.numSeries===0){counter.addSeries(new tr.model.CounterSeries(seriesName,ColorScheme.getColorIdForGeneralPurposeString(counter.name)));}
+counter.series.forEach(function(series){series.addCounterSample(ts,value);});},dvfsEventEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/utilization=(\d+)/.exec(eventBase.details);if(!event)return false;this.dvfsSample('DVFS Utilization','utilization',ts,event[1]);return true;},dvfsSetClockEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/frequency=(\d+)/.exec(eventBase.details);if(!event)return false;this.dvfsSample('DVFS Frequency','frequency',ts,event[1]);return true;},dvfsSetVoltageEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/voltage=(\d+)/.exec(eventBase.details);if(!event)return false;this.dvfsSample('DVFS Voltage','voltage',ts,event[1]);return true;},hwcSample:function(cat,counterName,seriesName,ts,eventBase){var event=/val=(\d+)/.exec(eventBase.details);if(!event)return false;var value=parseInt(event[1]);var counter=this.model_.kernel.getOrCreateCounter(cat,counterName);if(counter.numSeries===0){counter.addSeries(new tr.model.CounterSeries(seriesName,ColorScheme.getColorIdForGeneralPurposeString(counter.name)));}
 counter.series.forEach(function(series){series.addCounterSample(ts,value);});return true;},jmSample:function(ctrName,seriesName,ts,eventBase){return this.hwcSample('mali:jm','JM: '+ctrName,seriesName,ts,eventBase);},addJMCounter:function(hwcEventName,hwcTitle){function handler(eventName,cpuNumber,pid,ts,eventBase){return this.jmSample(hwcTitle,'count',ts,eventBase);}
 this.importer.registerEventHandler(hwcEventName,handler.bind(this));},addJMCycles:function(hwcEventName,hwcTitle){function handler(eventName,cpuNumber,pid,ts,eventBase){return this.jmSample(hwcTitle,'cycles',ts,eventBase);}
 this.importer.registerEventHandler(hwcEventName,handler.bind(this));},tilerSample:function(ctrName,seriesName,ts,eventBase){return this.hwcSample('mali:tiler','Tiler: '+ctrName,seriesName,ts,eventBase);},addTilerCounter:function(hwcEventName,hwcTitle){function handler(eventName,cpuNumber,pid,ts,eventBase){return this.tilerSample(hwcTitle,'count',ts,eventBase);}
@@ -5872,1282 +5750,1428 @@
 this.importer.registerEventHandler(hwcEventName,handler.bind(this));},addMMUCycles:function(hwcEventName,hwcTitle){function handler(eventName,cpuNumber,pid,ts,eventBase){return this.mmuSample(hwcTitle,'cycles',ts,eventBase);}
 this.importer.registerEventHandler(hwcEventName,handler.bind(this));},l2Sample:function(ctrName,seriesName,ts,eventBase){return this.hwcSample('mali:l2','L2: '+ctrName,seriesName,ts,eventBase);},addL2Counter:function(hwcEventName,hwcTitle){function handler(eventName,cpuNumber,pid,ts,eventBase){return this.l2Sample(hwcTitle,'count',ts,eventBase);}
 this.importer.registerEventHandler(hwcEventName,handler.bind(this));},addL2Cycles:function(hwcEventName,hwcTitle){function handler(eventName,cpuNumber,pid,ts,eventBase){return this.l2Sample(hwcTitle,'cycles',ts,eventBase);}
-this.importer.registerEventHandler(hwcEventName,handler.bind(this));}};Parser.register(MaliParser);return{MaliParser:MaliParser};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){var Parser=tr.e.importer.linux_perf.Parser;function MemReclaimParser(importer){Parser.call(this,importer);importer.registerEventHandler('mm_vmscan_kswapd_wake',MemReclaimParser.prototype.kswapdWake.bind(this));importer.registerEventHandler('mm_vmscan_kswapd_sleep',MemReclaimParser.prototype.kswapdSleep.bind(this));importer.registerEventHandler('mm_vmscan_direct_reclaim_begin',MemReclaimParser.prototype.reclaimBegin.bind(this));importer.registerEventHandler('mm_vmscan_direct_reclaim_end',MemReclaimParser.prototype.reclaimEnd.bind(this));}
-var kswapdWakeRE=/nid=(\d+) order=(\d+)/;var kswapdSleepRE=/nid=(\d+)/;var reclaimBeginRE=/order=(\d+) may_writepage=\d+ gfp_flags=(.+)/;var reclaimEndRE=/nr_reclaimed=(\d+)/;MemReclaimParser.prototype={__proto__:Parser.prototype,kswapdWake:function(eventName,cpuNumber,pid,ts,eventBase){var event=kswapdWakeRE.exec(eventBase.details);if(!event)
-return false;var tgid=parseInt(eventBase.tgid);var nid=parseInt(event[1]);var order=parseInt(event[2]);var kthread=this.importer.getOrCreateKernelThread(eventBase.threadName,tgid,pid);if(kthread.openSliceTS){if(order>kthread.order){kthread.order=order;}}else{kthread.openSliceTS=ts;kthread.order=order;}
+this.importer.registerEventHandler(hwcEventName,handler.bind(this));}};Parser.register(MaliParser);return{MaliParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){var Parser=tr.e.importer.linux_perf.Parser;function MemReclaimParser(importer){Parser.call(this,importer);importer.registerEventHandler('mm_vmscan_kswapd_wake',MemReclaimParser.prototype.kswapdWake.bind(this));importer.registerEventHandler('mm_vmscan_kswapd_sleep',MemReclaimParser.prototype.kswapdSleep.bind(this));importer.registerEventHandler('mm_vmscan_direct_reclaim_begin',MemReclaimParser.prototype.reclaimBegin.bind(this));importer.registerEventHandler('mm_vmscan_direct_reclaim_end',MemReclaimParser.prototype.reclaimEnd.bind(this));}
+var kswapdWakeRE=/nid=(\d+) order=(\d+)/;var kswapdSleepRE=/nid=(\d+)/;var reclaimBeginRE=/order=(\d+) may_writepage=\d+ gfp_flags=(.+)/;var reclaimEndRE=/nr_reclaimed=(\d+)/;MemReclaimParser.prototype={__proto__:Parser.prototype,kswapdWake:function(eventName,cpuNumber,pid,ts,eventBase){var event=kswapdWakeRE.exec(eventBase.details);if(!event)return false;var tgid=parseInt(eventBase.tgid);var nid=parseInt(event[1]);var order=parseInt(event[2]);var kthread=this.importer.getOrCreateKernelThread(eventBase.threadName,tgid,pid);if(kthread.openSliceTS){if(order>kthread.order){kthread.order=order;}}else{kthread.openSliceTS=ts;kthread.order=order;}
 return true;},kswapdSleep:function(eventName,cpuNumber,pid,ts,eventBase){var tgid=parseInt(eventBase.tgid);var kthread=this.importer.getOrCreateKernelThread(eventBase.threadName,tgid,pid);if(kthread.openSliceTS){kthread.thread.sliceGroup.pushCompleteSlice('memreclaim',eventBase.threadName,kthread.openSliceTS,ts-kthread.openSliceTS,0,0,{order:kthread.order});}
-kthread.openSliceTS=undefined;kthread.order=undefined;return true;},reclaimBegin:function(eventName,cpuNumber,pid,ts,eventBase){var event=reclaimBeginRE.exec(eventBase.details);if(!event)
-return false;var order=parseInt(event[1]);var gfp=event[2];var tgid=parseInt(eventBase.tgid);var kthread=this.importer.getOrCreateKernelThread(eventBase.threadName,tgid,pid);kthread.openSliceTS=ts;kthread.order=order;kthread.gfp=gfp;return true;},reclaimEnd:function(eventName,cpuNumber,pid,ts,eventBase){var event=reclaimEndRE.exec(eventBase.details);if(!event)
-return false;var nr_reclaimed=parseInt(event[1]);var tgid=parseInt(eventBase.tgid);var kthread=this.importer.getOrCreateKernelThread(eventBase.threadName,tgid,pid);if(kthread.openSliceTS!==undefined){kthread.thread.sliceGroup.pushCompleteSlice('memreclaim','direct reclaim',kthread.openSliceTS,ts-kthread.openSliceTS,0,0,{order:kthread.order,gfp:kthread.gfp,nr_reclaimed:nr_reclaimed});}
-kthread.openSliceTS=undefined;kthread.order=undefined;kthread.gfp=undefined;return true;}};Parser.register(MemReclaimParser);return{MemReclaimParser:MemReclaimParser};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){var ColorScheme=tr.b.ColorScheme;var Parser=tr.e.importer.linux_perf.Parser;function PowerParser(importer){Parser.call(this,importer);importer.registerEventHandler('power_start',PowerParser.prototype.powerStartEvent.bind(this));importer.registerEventHandler('power_frequency',PowerParser.prototype.powerFrequencyEvent.bind(this));importer.registerEventHandler('cpu_frequency',PowerParser.prototype.cpuFrequencyEvent.bind(this));importer.registerEventHandler('cpu_frequency_limits',PowerParser.prototype.cpuFrequencyLimitsEvent.bind(this));importer.registerEventHandler('cpu_idle',PowerParser.prototype.cpuIdleEvent.bind(this));}
-PowerParser.prototype={__proto__:Parser.prototype,cpuStateSlice:function(ts,targetCpuNumber,eventType,cpuState){var targetCpu=this.importer.getOrCreateCpu(targetCpuNumber);var powerCounter;if(eventType!='1'){this.importer.model.importWarning({type:'parse_error',message:'Don\'t understand power_start events of '+'type '+eventType});return;}
+kthread.openSliceTS=undefined;kthread.order=undefined;return true;},reclaimBegin:function(eventName,cpuNumber,pid,ts,eventBase){var event=reclaimBeginRE.exec(eventBase.details);if(!event)return false;var order=parseInt(event[1]);var gfp=event[2];var tgid=parseInt(eventBase.tgid);var kthread=this.importer.getOrCreateKernelThread(eventBase.threadName,tgid,pid);kthread.openSliceTS=ts;kthread.order=order;kthread.gfp=gfp;return true;},reclaimEnd:function(eventName,cpuNumber,pid,ts,eventBase){var event=reclaimEndRE.exec(eventBase.details);if(!event)return false;var nrReclaimed=parseInt(event[1]);var tgid=parseInt(eventBase.tgid);var kthread=this.importer.getOrCreateKernelThread(eventBase.threadName,tgid,pid);if(kthread.openSliceTS!==undefined){kthread.thread.sliceGroup.pushCompleteSlice('memreclaim','direct reclaim',kthread.openSliceTS,ts-kthread.openSliceTS,0,0,{order:kthread.order,gfp:kthread.gfp,nr_reclaimed:nrReclaimed});}
+kthread.openSliceTS=undefined;kthread.order=undefined;kthread.gfp=undefined;return true;}};Parser.register(MemReclaimParser);return{MemReclaimParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){var ColorScheme=tr.b.ColorScheme;var Parser=tr.e.importer.linux_perf.Parser;function PowerParser(importer){Parser.call(this,importer);importer.registerEventHandler('power_start',PowerParser.prototype.powerStartEvent.bind(this));importer.registerEventHandler('power_frequency',PowerParser.prototype.powerFrequencyEvent.bind(this));importer.registerEventHandler('cpu_frequency',PowerParser.prototype.cpuFrequencyEvent.bind(this));importer.registerEventHandler('cpu_frequency_limits',PowerParser.prototype.cpuFrequencyLimitsEvent.bind(this));importer.registerEventHandler('cpu_idle',PowerParser.prototype.cpuIdleEvent.bind(this));}
+PowerParser.prototype={__proto__:Parser.prototype,cpuStateSlice:function(ts,targetCpuNumber,eventType,cpuState){var targetCpu=this.importer.getOrCreateCpu(targetCpuNumber);var powerCounter;if(eventType!=='1'){this.importer.model.importWarning({type:'parse_error',message:'Don\'t understand power_start events of '+'type '+eventType});return;}
 powerCounter=targetCpu.getOrCreateCounter('','C-State');if(powerCounter.numSeries===0){powerCounter.addSeries(new tr.model.CounterSeries('state',ColorScheme.getColorIdForGeneralPurposeString(powerCounter.name+'.'+'state')));}
 powerCounter.series.forEach(function(series){series.addCounterSample(ts,cpuState);});},cpuIdleSlice:function(ts,targetCpuNumber,cpuState){var targetCpu=this.importer.getOrCreateCpu(targetCpuNumber);var powerCounter=targetCpu.getOrCreateCounter('','C-State');if(powerCounter.numSeries===0){powerCounter.addSeries(new tr.model.CounterSeries('state',ColorScheme.getColorIdForGeneralPurposeString(powerCounter.name)));}
-var val=(cpuState!=4294967295?cpuState+1:0);powerCounter.series.forEach(function(series){series.addCounterSample(ts,val);});},cpuFrequencySlice:function(ts,targetCpuNumber,powerState){var targetCpu=this.importer.getOrCreateCpu(targetCpuNumber);var powerCounter=targetCpu.getOrCreateCounter('','Clock Frequency');if(powerCounter.numSeries===0){powerCounter.addSeries(new tr.model.CounterSeries('state',ColorScheme.getColorIdForGeneralPurposeString(powerCounter.name+'.'+'state')));}
+var val=(cpuState!==4294967295?cpuState+1:0);powerCounter.series.forEach(function(series){series.addCounterSample(ts,val);});},cpuFrequencySlice:function(ts,targetCpuNumber,powerState){var targetCpu=this.importer.getOrCreateCpu(targetCpuNumber);var powerCounter=targetCpu.getOrCreateCounter('','Clock Frequency');if(powerCounter.numSeries===0){powerCounter.addSeries(new tr.model.CounterSeries('state',ColorScheme.getColorIdForGeneralPurposeString(powerCounter.name+'.'+'state')));}
 powerCounter.series.forEach(function(series){series.addCounterSample(ts,powerState);});},cpuFrequencyLimitsSlice:function(ts,targetCpuNumber,minFreq,maxFreq){var targetCpu=this.importer.getOrCreateCpu(targetCpuNumber);var powerCounter=targetCpu.getOrCreateCounter('','Clock Frequency Limits');if(powerCounter.numSeries===0){powerCounter.addSeries(new tr.model.CounterSeries('Min Frequency',ColorScheme.getColorIdForGeneralPurposeString(powerCounter.name+'.'+'Min Frequency')));powerCounter.addSeries(new tr.model.CounterSeries('Max Frequency',ColorScheme.getColorIdForGeneralPurposeString(powerCounter.name+'.'+'Max Frequency')));}
-powerCounter.series.forEach(function(series){if(series.name=='Min Frequency')
-series.addCounterSample(ts,minFreq);if(series.name=='Max Frequency')
-series.addCounterSample(ts,maxFreq);});},powerStartEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/type=(\d+) state=(\d) cpu_id=(\d+)/.exec(eventBase.details);if(!event)
-return false;var targetCpuNumber=parseInt(event[3]);var cpuState=parseInt(event[2]);this.cpuStateSlice(ts,targetCpuNumber,event[1],cpuState);return true;},powerFrequencyEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/type=(\d+) state=(\d+) cpu_id=(\d+)/.exec(eventBase.details);if(!event)
-return false;var targetCpuNumber=parseInt(event[3]);var powerState=parseInt(event[2]);this.cpuFrequencySlice(ts,targetCpuNumber,powerState);return true;},cpuFrequencyEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/state=(\d+) cpu_id=(\d+)/.exec(eventBase.details);if(!event)
-return false;var targetCpuNumber=parseInt(event[2]);var powerState=parseInt(event[1]);this.cpuFrequencySlice(ts,targetCpuNumber,powerState);return true;},cpuFrequencyLimitsEvent:function(eventName,cpu,pid,ts,eventBase){var event=/min=(\d+) max=(\d+) cpu_id=(\d+)/.exec(eventBase.details);if(!event)
-return false;var targetCpuNumber=parseInt(event[3]);var minFreq=parseInt(event[1]);var maxFreq=parseInt(event[2]);this.cpuFrequencyLimitsSlice(ts,targetCpuNumber,minFreq,maxFreq);return true;},cpuIdleEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/state=(\d+) cpu_id=(\d+)/.exec(eventBase.details);if(!event)
-return false;var targetCpuNumber=parseInt(event[2]);var cpuState=parseInt(event[1]);this.cpuIdleSlice(ts,targetCpuNumber,cpuState);return true;}};Parser.register(PowerParser);return{PowerParser:PowerParser};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){var ColorScheme=tr.b.ColorScheme;var Parser=tr.e.importer.linux_perf.Parser;function RegulatorParser(importer){Parser.call(this,importer);importer.registerEventHandler('regulator_enable',RegulatorParser.prototype.regulatorEnableEvent.bind(this));importer.registerEventHandler('regulator_enable_delay',RegulatorParser.prototype.regulatorEnableDelayEvent.bind(this));importer.registerEventHandler('regulator_enable_complete',RegulatorParser.prototype.regulatorEnableCompleteEvent.bind(this));importer.registerEventHandler('regulator_disable',RegulatorParser.prototype.regulatorDisableEvent.bind(this));importer.registerEventHandler('regulator_disable_complete',RegulatorParser.prototype.regulatorDisableCompleteEvent.bind(this));importer.registerEventHandler('regulator_set_voltage',RegulatorParser.prototype.regulatorSetVoltageEvent.bind(this));importer.registerEventHandler('regulator_set_voltage_complete',RegulatorParser.prototype.regulatorSetVoltageCompleteEvent.bind(this));this.model_=importer.model_;}
+powerCounter.series.forEach(function(series){if(series.name==='Min Frequency'){series.addCounterSample(ts,minFreq);}
+if(series.name==='Max Frequency'){series.addCounterSample(ts,maxFreq);}});},powerStartEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/type=(\d+) state=(\d) cpu_id=(\d+)/.exec(eventBase.details);if(!event)return false;var targetCpuNumber=parseInt(event[3]);var cpuState=parseInt(event[2]);this.cpuStateSlice(ts,targetCpuNumber,event[1],cpuState);return true;},powerFrequencyEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/type=(\d+) state=(\d+) cpu_id=(\d+)/.exec(eventBase.details);if(!event)return false;var targetCpuNumber=parseInt(event[3]);var powerState=parseInt(event[2]);this.cpuFrequencySlice(ts,targetCpuNumber,powerState);return true;},cpuFrequencyEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/state=(\d+) cpu_id=(\d+)/.exec(eventBase.details);if(!event)return false;var targetCpuNumber=parseInt(event[2]);var powerState=parseInt(event[1]);this.cpuFrequencySlice(ts,targetCpuNumber,powerState);return true;},cpuFrequencyLimitsEvent:function(eventName,cpu,pid,ts,eventBase){var event=/min=(\d+) max=(\d+) cpu_id=(\d+)/.exec(eventBase.details);if(!event)return false;var targetCpuNumber=parseInt(event[3]);var minFreq=parseInt(event[1]);var maxFreq=parseInt(event[2]);this.cpuFrequencyLimitsSlice(ts,targetCpuNumber,minFreq,maxFreq);return true;},cpuIdleEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/state=(\d+) cpu_id=(\d+)/.exec(eventBase.details);if(!event)return false;var targetCpuNumber=parseInt(event[2]);var cpuState=parseInt(event[1]);this.cpuIdleSlice(ts,targetCpuNumber,cpuState);return true;}};Parser.register(PowerParser);return{PowerParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){var ColorScheme=tr.b.ColorScheme;var Parser=tr.e.importer.linux_perf.Parser;function RegulatorParser(importer){Parser.call(this,importer);importer.registerEventHandler('regulator_enable',RegulatorParser.prototype.regulatorEnableEvent.bind(this));importer.registerEventHandler('regulator_enable_delay',RegulatorParser.prototype.regulatorEnableDelayEvent.bind(this));importer.registerEventHandler('regulator_enable_complete',RegulatorParser.prototype.regulatorEnableCompleteEvent.bind(this));importer.registerEventHandler('regulator_disable',RegulatorParser.prototype.regulatorDisableEvent.bind(this));importer.registerEventHandler('regulator_disable_complete',RegulatorParser.prototype.regulatorDisableCompleteEvent.bind(this));importer.registerEventHandler('regulator_set_voltage',RegulatorParser.prototype.regulatorSetVoltageEvent.bind(this));importer.registerEventHandler('regulator_set_voltage_complete',RegulatorParser.prototype.regulatorSetVoltageCompleteEvent.bind(this));this.model_=importer.model_;}
 var regulatorEnableRE=/name=(.+)/;var regulatorDisableRE=/name=(.+)/;var regulatorSetVoltageCompleteRE=/name=(\S+), val=(\d+)/;RegulatorParser.prototype={__proto__:Parser.prototype,getCtr_:function(ctrName,valueName){var ctr=this.model_.kernel.getOrCreateCounter(null,'vreg '+ctrName+' '+valueName);if(ctr.series[0]===undefined){ctr.addSeries(new tr.model.CounterSeries(valueName,ColorScheme.getColorIdForGeneralPurposeString(ctrName+'.'+valueName)));}
-return ctr;},regulatorEnableEvent:function(eventName,cpuNum,pid,ts,eventBase){var event=regulatorEnableRE.exec(eventBase.details);if(!event)
-return false;var name=event[1];var ctr=this.getCtr_(name,'enabled');ctr.series[0].addCounterSample(ts,1);return true;},regulatorEnableDelayEvent:function(eventName,cpuNum,pid,ts,eventBase){return true;},regulatorEnableCompleteEvent:function(eventName,cpuNum,pid,ts,eventBase){return true;},regulatorDisableEvent:function(eventName,cpuNum,pid,ts,eventBase){var event=regulatorDisableRE.exec(eventBase.details);if(!event)
-return false;var name=event[1];var ctr=this.getCtr_(name,'enabled');ctr.series[0].addCounterSample(ts,0);return true;},regulatorDisableCompleteEvent:function(eventName,cpuNum,pid,ts,eventBase){return true;},regulatorSetVoltageEvent:function(eventName,cpuNum,pid,ts,eventBase){return true;},regulatorSetVoltageCompleteEvent:function(eventName,cpuNum,pid,ts,eventBase){var event=regulatorSetVoltageCompleteRE.exec(eventBase.details);if(!event)
-return false;var name=event[1];var voltage=parseInt(event[2]);var ctr=this.getCtr_(name,'voltage');ctr.series[0].addCounterSample(ts,voltage);return true;}};Parser.register(RegulatorParser);return{RegulatorParser:RegulatorParser};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){var Parser=tr.e.importer.linux_perf.Parser;function SchedParser(importer){Parser.call(this,importer);importer.registerEventHandler('sched_switch',SchedParser.prototype.schedSwitchEvent.bind(this));importer.registerEventHandler('sched_wakeup',SchedParser.prototype.schedWakeupEvent.bind(this));importer.registerEventHandler('sched_blocked_reason',SchedParser.prototype.schedBlockedEvent.bind(this));importer.registerEventHandler('sched_cpu_hotplug',SchedParser.prototype.schedCpuHotplugEvent.bind(this));}
-var TestExports={};var schedSwitchRE=new RegExp('prev_comm=(.+) prev_pid=(\\d+) prev_prio=(\\d+) '+'prev_state=(\\S\\+?|\\S\\|\\S) ==> '+'next_comm=(.+) next_pid=(\\d+) next_prio=(\\d+)');var schedBlockedRE=new RegExp('pid=(\\d+) iowait=(\\d) caller=(.+)');TestExports.schedSwitchRE=schedSwitchRE;var schedWakeupRE=/comm=(.+) pid=(\d+) prio=(\d+) success=(\d+) target_cpu=(\d+)/;TestExports.schedWakeupRE=schedWakeupRE;SchedParser.prototype={__proto__:Parser.prototype,schedSwitchEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=schedSwitchRE.exec(eventBase.details);if(!event)
-return false;var prevState=event[4];var nextComm=event[5];var nextPid=parseInt(event[6]);var nextPrio=parseInt(event[7]);var nextThread=this.importer.threadsByLinuxPid[nextPid];var nextName;if(nextThread)
-nextName=nextThread.userFriendlyName;else
-nextName=nextComm;var cpu=this.importer.getOrCreateCpu(cpuNumber);cpu.switchActiveThread(ts,{stateWhenDescheduled:prevState},nextPid,nextName,{comm:nextComm,tid:nextPid,prio:nextPrio});return true;},schedWakeupEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=schedWakeupRE.exec(eventBase.details);if(!event)
-return false;var fromPid=pid;var comm=event[1];var pid=parseInt(event[2]);var prio=parseInt(event[3]);this.importer.markPidRunnable(ts,pid,comm,prio,fromPid);return true;},schedCpuHotplugEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/cpu (\d+) (.+) error=(\d+)/.exec(eventBase.details);if(!event)
-return false;var cpuNumber=event[1];var state=event[2];var targetCpu=this.importer.getOrCreateCpu(cpuNumber);var powerCounter=targetCpu.getOrCreateCounter('','Cpu Hotplug');if(powerCounter.numSeries===0){powerCounter.addSeries(new tr.model.CounterSeries('State',tr.b.ColorScheme.getColorIdForGeneralPurposeString(powerCounter.name+'.'+'State')));}
-powerCounter.series.forEach(function(series){if(series.name=='State')
-series.addCounterSample(ts,state.localeCompare('offline')?0:1);});return true;},schedBlockedEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=schedBlockedRE.exec(eventBase.details);if(!event)
-return false;var pid=parseInt(event[1]);var iowait=parseInt(event[2]);var caller=event[3];this.importer.addPidBlockedReason(ts,pid,iowait,caller);return true;}};Parser.register(SchedParser);return{SchedParser:SchedParser,_SchedParserTestExports:TestExports};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){var ColorScheme=tr.b.ColorScheme;var Parser=tr.e.importer.linux_perf.Parser;function SyncParser(importer){Parser.call(this,importer);importer.registerEventHandler('sync_timeline',SyncParser.prototype.timelineEvent.bind(this));importer.registerEventHandler('sync_wait',SyncParser.prototype.syncWaitEvent.bind(this));importer.registerEventHandler('sync_pt',SyncParser.prototype.syncPtEvent.bind(this));this.model_=importer.model_;}
-var syncTimelineRE=/name=(\S+) value=(\S*)/;var syncWaitRE=/(\S+) name=(\S+) state=(\d+)/;var syncPtRE=/name=(\S+) value=(\S*)/;SyncParser.prototype={__proto__:Parser.prototype,timelineEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=syncTimelineRE.exec(eventBase.details);if(!event)
-return false;var thread=this.importer.getOrCreatePseudoThread(event[1]);if(thread.lastActiveTs!==undefined){var duration=ts-thread.lastActiveTs;var value=thread.lastActiveValue;if(value==undefined)
-value=' ';var slice=new tr.model.ThreadSlice('',value,ColorScheme.getColorIdForGeneralPurposeString(value),thread.lastActiveTs,{},duration);thread.thread.sliceGroup.pushSlice(slice);}
-thread.lastActiveTs=ts;thread.lastActiveValue=event[2];return true;},syncWaitEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=syncWaitRE.exec(eventBase.details);if(!event)
-return false;if(eventBase.tgid===undefined){return false;}
+return ctr;},regulatorEnableEvent:function(eventName,cpuNum,pid,ts,eventBase){var event=regulatorEnableRE.exec(eventBase.details);if(!event)return false;var name=event[1];var ctr=this.getCtr_(name,'enabled');ctr.series[0].addCounterSample(ts,1);return true;},regulatorEnableDelayEvent:function(eventName,cpuNum,pid,ts,eventBase){return true;},regulatorEnableCompleteEvent:function(eventName,cpuNum,pid,ts,eventBase){return true;},regulatorDisableEvent:function(eventName,cpuNum,pid,ts,eventBase){var event=regulatorDisableRE.exec(eventBase.details);if(!event)return false;var name=event[1];var ctr=this.getCtr_(name,'enabled');ctr.series[0].addCounterSample(ts,0);return true;},regulatorDisableCompleteEvent:function(eventName,cpuNum,pid,ts,eventBase){return true;},regulatorSetVoltageEvent:function(eventName,cpuNum,pid,ts,eventBase){return true;},regulatorSetVoltageCompleteEvent:function(eventName,cpuNum,pid,ts,eventBase){var event=regulatorSetVoltageCompleteRE.exec(eventBase.details);if(!event)return false;var name=event[1];var voltage=parseInt(event[2]);var ctr=this.getCtr_(name,'voltage');ctr.series[0].addCounterSample(ts,voltage);return true;}};Parser.register(RegulatorParser);return{RegulatorParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){var Parser=tr.e.importer.linux_perf.Parser;function SchedParser(importer){Parser.call(this,importer);importer.registerEventHandler('sched_switch',SchedParser.prototype.schedSwitchEvent.bind(this));importer.registerEventHandler('sched_wakeup',SchedParser.prototype.schedWakeupEvent.bind(this));importer.registerEventHandler('sched_blocked_reason',SchedParser.prototype.schedBlockedEvent.bind(this));importer.registerEventHandler('sched_cpu_hotplug',SchedParser.prototype.schedCpuHotplugEvent.bind(this));}
+var TestExports={};var schedSwitchRE=new RegExp('prev_comm=(.+) prev_pid=(\\d+) prev_prio=(\\d+) '+'prev_state=(\\S\\+?|\\S\\|\\S) ==> '+'next_comm=(.+) next_pid=(\\d+) next_prio=(\\d+)');var schedBlockedRE=new RegExp('pid=(\\d+) iowait=(\\d) caller=(.+)');TestExports.schedSwitchRE=schedSwitchRE;var schedWakeupRE=/comm=(.+) pid=(\d+) prio=(\d+)(?: success=\d+)? target_cpu=(\d+)/;TestExports.schedWakeupRE=schedWakeupRE;SchedParser.prototype={__proto__:Parser.prototype,schedSwitchEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=schedSwitchRE.exec(eventBase.details);if(!event)return false;var prevState=event[4];var nextComm=event[5];var nextPid=parseInt(event[6]);var nextPrio=parseInt(event[7]);if(eventBase.tgid!==undefined){var tgid=parseInt(eventBase.tgid);var process=this.importer.model_.getOrCreateProcess(tgid);if(!process.getThread(pid)){var thread=process.getOrCreateThread(pid);thread.name=eventBase.threadName;}}
+var nextThread=this.importer.threadsByLinuxPid[nextPid];var nextName;if(nextThread){nextName=nextThread.userFriendlyName;}else{nextName=nextComm;}
+var cpu=this.importer.getOrCreateCpu(cpuNumber);cpu.switchActiveThread(ts,{stateWhenDescheduled:prevState},nextPid,nextName,{comm:nextComm,tid:nextPid,prio:nextPrio});return true;},schedWakeupEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=schedWakeupRE.exec(eventBase.details);if(!event)return false;var fromPid=pid;var comm=event[1];var pid=parseInt(event[2]);var prio=parseInt(event[3]);this.importer.markPidRunnable(ts,pid,comm,prio,fromPid);return true;},schedCpuHotplugEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=/cpu (\d+) (.+) error=(\d+)/.exec(eventBase.details);if(!event)return false;var cpuNumber=event[1];var state=event[2];var targetCpu=this.importer.getOrCreateCpu(cpuNumber);var powerCounter=targetCpu.getOrCreateCounter('','Cpu Hotplug');if(powerCounter.numSeries===0){powerCounter.addSeries(new tr.model.CounterSeries('State',tr.b.ColorScheme.getColorIdForGeneralPurposeString(powerCounter.name+'.'+'State')));}
+powerCounter.series.forEach(function(series){if(series.name==='State'){series.addCounterSample(ts,state.localeCompare('offline')?0:1);}});return true;},schedBlockedEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=schedBlockedRE.exec(eventBase.details);if(!event)return false;var pid=parseInt(event[1]);var iowait=parseInt(event[2]);var caller=event[3];this.importer.addPidBlockedReason(ts,pid,iowait,caller);return true;}};Parser.register(SchedParser);return{SchedParser,_SchedParserTestExports:TestExports};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){var ColorScheme=tr.b.ColorScheme;var Parser=tr.e.importer.linux_perf.Parser;function SyncParser(importer){Parser.call(this,importer);importer.registerEventHandler('sync_timeline',SyncParser.prototype.timelineEvent.bind(this));importer.registerEventHandler('sync_wait',SyncParser.prototype.syncWaitEvent.bind(this));importer.registerEventHandler('sync_pt',SyncParser.prototype.syncPtEvent.bind(this));this.model_=importer.model_;}
+var syncTimelineRE=/name=(\S+) value=(\S*)/;var syncWaitRE=/(\S+) name=(\S+) state=(\d+)/;var syncPtRE=/name=(\S+) value=(\S*)/;SyncParser.prototype={__proto__:Parser.prototype,timelineEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=syncTimelineRE.exec(eventBase.details);if(!event)return false;var thread=this.importer.getOrCreatePseudoThread(event[1]);if(thread.lastActiveTs!==undefined){var duration=ts-thread.lastActiveTs;var value=thread.lastActiveValue;if(value===undefined)value=' ';var slice=new tr.model.ThreadSlice('',value,ColorScheme.getColorIdForGeneralPurposeString(value),thread.lastActiveTs,{},duration);thread.thread.sliceGroup.pushSlice(slice);}
+thread.lastActiveTs=ts;thread.lastActiveValue=event[2];return true;},syncWaitEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=syncWaitRE.exec(eventBase.details);if(!event)return false;if(eventBase.tgid===undefined){return false;}
 var tgid=parseInt(eventBase.tgid);var thread=this.model_.getOrCreateProcess(tgid).getOrCreateThread(pid);thread.name=eventBase.threadName;var slices=thread.kernelSliceGroup;if(!slices.isTimestampValidForBeginOrEnd(ts)){this.model_.importWarning({type:'parse_error',message:'Timestamps are moving backward.'});return false;}
-var name='fence_wait("'+event[2]+'")';if(event[1]=='begin'){var slice=slices.beginSlice(null,name,ts,{'Start state':event[3]});}else if(event[1]=='end'){if(slices.openSliceCount>0){slices.endSlice(ts);}}else{return false;}
-return true;},syncPtEvent:function(eventName,cpuNumber,pid,ts,eventBase){return!!syncPtRE.exec(eventBase.details);}};Parser.register(SyncParser);return{SyncParser:SyncParser};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){var ColorScheme=tr.b.ColorScheme;var Parser=tr.e.importer.linux_perf.Parser;function WorkqueueParser(importer){Parser.call(this,importer);importer.registerEventHandler('workqueue_execute_start',WorkqueueParser.prototype.executeStartEvent.bind(this));importer.registerEventHandler('workqueue_execute_end',WorkqueueParser.prototype.executeEndEvent.bind(this));importer.registerEventHandler('workqueue_queue_work',WorkqueueParser.prototype.executeQueueWork.bind(this));importer.registerEventHandler('workqueue_activate_work',WorkqueueParser.prototype.executeActivateWork.bind(this));}
-var workqueueExecuteStartRE=/work struct (.+): function (\S+)/;var workqueueExecuteEndRE=/work struct (.+)/;WorkqueueParser.prototype={__proto__:Parser.prototype,executeStartEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=workqueueExecuteStartRE.exec(eventBase.details);if(!event)
-return false;var kthread=this.importer.getOrCreateKernelThread(eventBase.threadName,pid,pid);kthread.openSliceTS=ts;kthread.openSlice=event[2];return true;},executeEndEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=workqueueExecuteEndRE.exec(eventBase.details);if(!event)
-return false;var kthread=this.importer.getOrCreateKernelThread(eventBase.threadName,pid,pid);if(kthread.openSlice){var slice=new tr.model.ThreadSlice('',kthread.openSlice,ColorScheme.getColorIdForGeneralPurposeString(kthread.openSlice),kthread.openSliceTS,{},ts-kthread.openSliceTS);kthread.thread.sliceGroup.pushSlice(slice);}
-kthread.openSlice=undefined;return true;},executeQueueWork:function(eventName,cpuNumber,pid,ts,eventBase){return true;},executeActivateWork:function(eventName,cpuNumber,pid,ts,eventBase){return true;}};Parser.register(WorkqueueParser);return{WorkqueueParser:WorkqueueParser};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){var MONOTONIC_TO_FTRACE_GLOBAL_SYNC_ID='linux_clock_monotonic_to_ftrace_global';function FTraceImporter(model,events){this.importPriority=2;this.model_=model;this.events_=events;this.wakeups_=[];this.blocked_reasons_=[];this.kernelThreadStates_={};this.buildMapFromLinuxPidsToThreads_();this.lines_=[];this.pseudoThreadCounter=1;this.parsers_=[];this.eventHandlers_={};this.haveClockSyncedMonotonicToGlobal_=false;}
+var name='fence_wait("'+event[2]+'")';if(event[1]==='begin'){var slice=slices.beginSlice(null,name,ts,{'Start state':event[3]});}else if(event[1]==='end'){if(slices.openSliceCount>0){slices.endSlice(ts);}}else{return false;}
+return true;},syncPtEvent:function(eventName,cpuNumber,pid,ts,eventBase){return!!syncPtRE.exec(eventBase.details);}};Parser.register(SyncParser);return{SyncParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){var ColorScheme=tr.b.ColorScheme;var Parser=tr.e.importer.linux_perf.Parser;function WorkqueueParser(importer){Parser.call(this,importer);importer.registerEventHandler('workqueue_execute_start',WorkqueueParser.prototype.executeStartEvent.bind(this));importer.registerEventHandler('workqueue_execute_end',WorkqueueParser.prototype.executeEndEvent.bind(this));importer.registerEventHandler('workqueue_queue_work',WorkqueueParser.prototype.executeQueueWork.bind(this));importer.registerEventHandler('workqueue_activate_work',WorkqueueParser.prototype.executeActivateWork.bind(this));}
+var workqueueExecuteStartRE=/work struct (.+): function (\S+)/;var workqueueExecuteEndRE=/work struct (.+)/;WorkqueueParser.prototype={__proto__:Parser.prototype,executeStartEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=workqueueExecuteStartRE.exec(eventBase.details);if(!event)return false;var kthread=this.importer.getOrCreateKernelThread(eventBase.threadName,pid,pid);kthread.openSliceTS=ts;kthread.openSlice=event[2];return true;},executeEndEvent:function(eventName,cpuNumber,pid,ts,eventBase){var event=workqueueExecuteEndRE.exec(eventBase.details);if(!event)return false;var kthread=this.importer.getOrCreateKernelThread(eventBase.threadName,pid,pid);if(kthread.openSlice){var slice=new tr.model.ThreadSlice('',kthread.openSlice,ColorScheme.getColorIdForGeneralPurposeString(kthread.openSlice),kthread.openSliceTS,{},ts-kthread.openSliceTS);kthread.thread.sliceGroup.pushSlice(slice);}
+kthread.openSlice=undefined;return true;},executeQueueWork:function(eventName,cpuNumber,pid,ts,eventBase){return true;},executeActivateWork:function(eventName,cpuNumber,pid,ts,eventBase){return true;}};Parser.register(WorkqueueParser);return{WorkqueueParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){var MONOTONIC_TO_FTRACE_GLOBAL_SYNC_ID='linux_clock_monotonic_to_ftrace_global';function FTraceImporter(model,events){this.importPriority=2;this.model_=model;this.events_=events;this.wakeups_=[];this.blockedReasons_=[];this.kernelThreadStates_={};this.buildMapFromLinuxPidsToThreads_();this.lines_=[];this.pseudoThreadCounter=1;this.parsers_=[];this.eventHandlers_={};this.haveClockSyncedMonotonicToGlobal_=false;}
 var TestExports={};var lineREWithTGID=new RegExp('^\\s*(.+)-(\\d+)\\s+\\(\\s*(\\d+|-+)\\)\\s\\[(\\d+)\\]'+'\\s+[dX.][Nnp.][Hhs.][0-9a-f.]'+'\\s+(\\d+\\.\\d+):\\s+(\\S+):\\s(.*)$');var lineParserWithTGID=function(line){var groups=lineREWithTGID.exec(line);if(!groups){return groups;}
-var tgid=groups[3];if(tgid[0]==='-')
-tgid=undefined;return{threadName:groups[1],pid:groups[2],tgid:tgid,cpuNumber:groups[4],timestamp:groups[5],eventName:groups[6],details:groups[7]};};TestExports.lineParserWithTGID=lineParserWithTGID;var lineREWithIRQInfo=new RegExp('^\\s*(.+)-(\\d+)\\s+\\[(\\d+)\\]'+'\\s+[dX.][Nnp.][Hhs.][0-9a-f.]'+'\\s+(\\d+\\.\\d+):\\s+(\\S+):\\s(.*)$');var lineParserWithIRQInfo=function(line){var groups=lineREWithIRQInfo.exec(line);if(!groups){return groups;}
+var tgid=groups[3];if(tgid[0]==='-')tgid=undefined;return{threadName:groups[1],pid:groups[2],tgid:tgid,cpuNumber:groups[4],timestamp:groups[5],eventName:groups[6],details:groups[7]};};TestExports.lineParserWithTGID=lineParserWithTGID;var lineREWithIRQInfo=new RegExp('^\\s*(.+)-(\\d+)\\s+\\[(\\d+)\\]'+'\\s+[dX.][Nnp.][Hhs.][0-9a-f.]'+'\\s+(\\d+\\.\\d+):\\s+(\\S+):\\s(.*)$');var lineParserWithIRQInfo=function(line){var groups=lineREWithIRQInfo.exec(line);if(!groups){return groups;}
 return{threadName:groups[1],pid:groups[2],cpuNumber:groups[3],timestamp:groups[4],eventName:groups[5],details:groups[6]};};TestExports.lineParserWithIRQInfo=lineParserWithIRQInfo;var lineREWithLegacyFmt=/^\s*(.+)-(\d+)\s+\[(\d+)\]\s*(\d+\.\d+):\s+(\S+):\s(.*)$/;var lineParserWithLegacyFmt=function(line){var groups=lineREWithLegacyFmt.exec(line);if(!groups){return groups;}
-return{threadName:groups[1],pid:groups[2],cpuNumber:groups[3],timestamp:groups[4],eventName:groups[5],details:groups[6]};};TestExports.lineParserWithLegacyFmt=lineParserWithLegacyFmt;var traceEventClockSyncRE=/trace_event_clock_sync: parent_ts=(\d+\.?\d*)/;TestExports.traceEventClockSyncRE=traceEventClockSyncRE;var realTimeClockSyncRE=/trace_event_clock_sync: realtime_ts=(\d+)/;var genericClockSyncRE=/trace_event_clock_sync: name=([\w\-]+)/;var pseudoKernelPID=0;function autoDetectLineParser(line){if(line[0]=='{')
-return false;if(lineREWithTGID.test(line))
-return lineParserWithTGID;if(lineREWithIRQInfo.test(line))
-return lineParserWithIRQInfo;if(lineREWithLegacyFmt.test(line))
-return lineParserWithLegacyFmt;return undefined;};TestExports.autoDetectLineParser=autoDetectLineParser;FTraceImporter.canImport=function(events){if(!(typeof(events)==='string'||events instanceof String))
-return false;if(FTraceImporter._extractEventsFromSystraceHTML(events,false).ok)
-return true;if(FTraceImporter._extractEventsFromSystraceMultiHTML(events,false).ok)
-return true;if(/^# tracer:/.test(events))
-return true;var lineBreakIndex=events.indexOf('\n');if(lineBreakIndex>-1)
-events=events.substring(0,lineBreakIndex);if(autoDetectLineParser(events))
-return true;return false;};FTraceImporter._extractEventsFromSystraceHTML=function(incoming_events,produce_result){var failure={ok:false};if(produce_result===undefined)
-produce_result=true;if(/^<!DOCTYPE html>/.test(incoming_events)==false)
-return failure;var r=new tr.importer.SimpleLineReader(incoming_events);if(!r.advanceToLineMatching(/^  <script>$/))
-return failure;if(!r.advanceToLineMatching(/^  var linuxPerfData = "\\$/))
-return failure;var events_begin_at_line=r.curLineNumber+1;r.beginSavingLines();if(!r.advanceToLineMatching(/^  <\/script>$/))
-return failure;var raw_events=r.endSavingLinesAndGetResult();raw_events=raw_events.slice(1,raw_events.length-1);if(!r.advanceToLineMatching(/^<\/body>$/))
-return failure;if(!r.advanceToLineMatching(/^<\/html>$/))
-return failure;function endsWith(str,suffix){return str.indexOf(suffix,str.length-suffix.length)!==-1;}
-function stripSuffix(str,suffix){if(!endsWith(str,suffix))
-return str;return str.substring(str,str.length-suffix.length);}
-var events=[];if(produce_result){for(var i=0;i<raw_events.length;i++){var event=raw_events[i];event=stripSuffix(event,'\\n\\');events.push(event);}}else{events=[raw_events[raw_events.length-1]];}
-var oldLastEvent=events[events.length-1];var newLastEvent=stripSuffix(oldLastEvent,'\\n";');if(newLastEvent==oldLastEvent)
-return failure;events[events.length-1]=newLastEvent;return{ok:true,lines:produce_result?events:undefined,events_begin_at_line:events_begin_at_line};};FTraceImporter._extractEventsFromSystraceMultiHTML=function(incoming_events,produce_result){var failure={ok:false};if(produce_result===undefined)
-produce_result=true;if(new RegExp('^<!DOCTYPE HTML>','i').test(incoming_events)==false)
-return failure;var r=new tr.importer.SimpleLineReader(incoming_events);var events=[];while(!/^# tracer:/.test(events)){if(!r.advanceToLineMatching(/^  <script class="trace-data" type="application\/text">$/))
-return failure;var events_begin_at_line=r.curLineNumber+1;r.beginSavingLines();if(!r.advanceToLineMatching(/^  <\/script>$/))
-return failure;events=r.endSavingLinesAndGetResult();events=events.slice(1,events.length-1);}
-if(!r.advanceToLineMatching(/^<\/body>$/))
-return failure;if(!r.advanceToLineMatching(/^<\/html>$/))
-return failure;return{ok:true,lines:produce_result?events:undefined,events_begin_at_line:events_begin_at_line};};FTraceImporter.prototype={__proto__:tr.importer.Importer.prototype,get importerName(){return'FTraceImporter';},get model(){return this.model_;},importClockSyncMarkers:function(){this.lazyInit_();this.forEachLine_(function(text,eventBase,cpuNumber,pid,ts){var eventName=eventBase.eventName;if(eventName!=='tracing_mark_write'&&eventName!=='0')
-return;if(traceEventClockSyncRE.exec(eventBase.details)||genericClockSyncRE.exec(eventBase.details)){this.traceClockSyncEvent_(eventName,cpuNumber,pid,ts,eventBase);}else if(realTimeClockSyncRE.exec(eventBase.details)){var match=realTimeClockSyncRE.exec(eventBase.details);this.model_.realtime_to_monotonic_offset_ms=ts-match[1];}}.bind(this));},importEvents:function(){var modelTimeTransformer=this.model_.clockSyncManager.getModelTimeTransformer(tr.model.ClockDomainId.LINUX_FTRACE_GLOBAL);this.importCpuData_(modelTimeTransformer);this.buildMapFromLinuxPidsToThreads_();this.buildPerThreadCpuSlicesFromCpuState_();},registerEventHandler:function(eventName,handler){this.eventHandlers_[eventName]=handler;},getOrCreateCpu:function(cpuNumber){return this.model_.kernel.getOrCreateCpu(cpuNumber);},getOrCreateKernelThread:function(kernelThreadName,pid,tid){if(!this.kernelThreadStates_[kernelThreadName]){var thread=this.model_.getOrCreateProcess(pid).getOrCreateThread(tid);thread.name=kernelThreadName;this.kernelThreadStates_[kernelThreadName]={pid:pid,thread:thread,openSlice:undefined,openSliceTS:undefined};this.threadsByLinuxPid[pid]=thread;}
+return{threadName:groups[1],pid:groups[2],cpuNumber:groups[3],timestamp:groups[4],eventName:groups[5],details:groups[6]};};TestExports.lineParserWithLegacyFmt=lineParserWithLegacyFmt;var traceEventClockSyncRE=/trace_event_clock_sync: parent_ts=(\d+\.?\d*)/;TestExports.traceEventClockSyncRE=traceEventClockSyncRE;var realTimeClockSyncRE=/trace_event_clock_sync: realtime_ts=(\d+)/;var genericClockSyncRE=/trace_event_clock_sync: name=([\w\-]+)/;var pseudoKernelPID=0;function autoDetectLineParser(line){if(line[0]==='{')return false;if(lineREWithTGID.test(line)){return lineParserWithTGID;}
+if(lineREWithIRQInfo.test(line)){return lineParserWithIRQInfo;}
+if(lineREWithLegacyFmt.test(line)){return lineParserWithLegacyFmt;}
+return undefined;}
+TestExports.autoDetectLineParser=autoDetectLineParser;FTraceImporter.canImport=function(events){if(!(typeof(events)==='string'||events instanceof String)){return false;}
+if(FTraceImporter._extractEventsFromSystraceHTML(events,false).ok){return true;}
+if(FTraceImporter._extractEventsFromSystraceMultiHTML(events,false).ok){return true;}
+if(/^# tracer:/.test(events)){return true;}
+var lineBreakIndex=events.indexOf('\n');if(lineBreakIndex>-1){events=events.substring(0,lineBreakIndex);}
+if(autoDetectLineParser(events)){return true;}
+return false;};FTraceImporter._extractEventsFromSystraceHTML=function(incomingEvents,produceResult){var failure={ok:false};if(produceResult===undefined){produceResult=true;}
+if(!/^<!DOCTYPE html>/.test(incomingEvents)){return failure;}
+var r=new tr.importer.SimpleLineReader(incomingEvents);if(!r.advanceToLineMatching(/^  <script>$/)){return failure;}
+if(!r.advanceToLineMatching(/^  var linuxPerfData = "\\$/)){return failure;}
+var eventsBeginAtLine=r.curLineNumber+1;r.beginSavingLines();if(!r.advanceToLineMatching(/^  <\/script>$/)){return failure;}
+var rawEvents=r.endSavingLinesAndGetResult();rawEvents=rawEvents.slice(1,rawEvents.length-1);if(!r.advanceToLineMatching(/^<\/body>$/)){return failure;}
+if(!r.advanceToLineMatching(/^<\/html>$/)){return failure;}
+function endsWith(str,suffix){return str.indexOf(suffix,str.length-suffix.length)!==-1;}
+function stripSuffix(str,suffix){if(!endsWith(str,suffix)){return str;}
+return str.substring(str,str.length-suffix.length);}
+var events=[];if(produceResult){for(var i=0;i<rawEvents.length;i++){var event=rawEvents[i];event=stripSuffix(event,'\\n\\');events.push(event);}}else{events=[rawEvents[rawEvents.length-1]];}
+var oldLastEvent=events[events.length-1];var newLastEvent=stripSuffix(oldLastEvent,'\\n";');if(newLastEvent===oldLastEvent){return failure;}
+events[events.length-1]=newLastEvent;return{ok:true,lines:produceResult?events:undefined,eventsBeginAtLine:eventsBeginAtLine};};FTraceImporter._extractEventsFromSystraceMultiHTML=function(incomingEvents,produceResult){var failure={ok:false};if(produceResult===undefined){produceResult=true;}
+if(!(new RegExp('^<!DOCTYPE HTML>','i').test(incomingEvents))){return failure;}
+var r=new tr.importer.SimpleLineReader(incomingEvents);var events=[];while(!/^# tracer:/.test(events)){if(!r.advanceToLineMatching(/^  <script class="trace-data" type="application\/text">$/)){return failure;}
+var eventsBeginAtLine=r.curLineNumber+1;r.beginSavingLines();if(!r.advanceToLineMatching(/^  <\/script>$/))return failure;events=r.endSavingLinesAndGetResult();events=events.slice(1,events.length-1);}
+if(!r.advanceToLineMatching(/^<\/body>$/)){return failure;}
+if(!r.advanceToLineMatching(/^<\/html>$/)){return failure;}
+return{ok:true,lines:produceResult?events:undefined,eventsBeginAtLine:eventsBeginAtLine};};FTraceImporter.prototype={__proto__:tr.importer.Importer.prototype,get importerName(){return'FTraceImporter';},get model(){return this.model_;},importClockSyncMarkers:function(){this.lazyInit_();this.forEachLine_(function(text,eventBase,cpuNumber,pid,ts){var eventName=eventBase.eventName;if(eventName!=='tracing_mark_write'&&eventName!=='0'){return;}
+if(traceEventClockSyncRE.exec(eventBase.details)||genericClockSyncRE.exec(eventBase.details)){this.traceClockSyncEvent_(eventName,cpuNumber,pid,ts,eventBase);}else if(realTimeClockSyncRE.exec(eventBase.details)){var match=realTimeClockSyncRE.exec(eventBase.details);this.model_.realtime_to_monotonic_offset_ms=ts-match[1];}}.bind(this));},importEvents:function(){var modelTimeTransformer=this.model_.clockSyncManager.getModelTimeTransformer(tr.model.ClockDomainId.LINUX_FTRACE_GLOBAL);this.importCpuData_(modelTimeTransformer);this.buildMapFromLinuxPidsToThreads_();this.buildPerThreadCpuSlicesFromCpuState_();},registerEventHandler:function(eventName,handler){this.eventHandlers_[eventName]=handler;},getOrCreateCpu:function(cpuNumber){return this.model_.kernel.getOrCreateCpu(cpuNumber);},getOrCreateKernelThread:function(kernelThreadName,pid,tid){if(!this.kernelThreadStates_[kernelThreadName]){var thread=this.model_.getOrCreateProcess(pid).getOrCreateThread(tid);thread.name=kernelThreadName;this.kernelThreadStates_[kernelThreadName]={pid:pid,thread:thread,openSlice:undefined,openSliceTS:undefined};this.threadsByLinuxPid[pid]=thread;}
 return this.kernelThreadStates_[kernelThreadName];},getOrCreateBinderKernelThread:function(kernelThreadName,pid,tid){var key=kernelThreadName+pid+tid;if(!this.kernelThreadStates_[key]){var thread=this.model_.getOrCreateProcess(pid).getOrCreateThread(tid);thread.name=kernelThreadName;this.kernelThreadStates_[key]={pid:pid,thread:thread,openSlice:undefined,openSliceTS:undefined};this.threadsByLinuxPid[pid]=thread;}
 return this.kernelThreadStates_[key];},getOrCreatePseudoThread:function(threadName){var thread=this.kernelThreadStates_[threadName];if(!thread){thread=this.getOrCreateKernelThread(threadName,pseudoKernelPID,this.pseudoThreadCounter);this.pseudoThreadCounter++;}
-return thread;},markPidRunnable:function(ts,pid,comm,prio,fromPid){this.wakeups_.push({ts:ts,tid:pid,fromTid:fromPid});},addPidBlockedReason:function(ts,pid,iowait,caller){this.blocked_reasons_.push({ts:ts,tid:pid,iowait:iowait,caller:caller});},buildMapFromLinuxPidsToThreads_:function(){this.threadsByLinuxPid={};this.model_.getAllThreads().forEach(function(thread){this.threadsByLinuxPid[thread.tid]=thread;}.bind(this));},buildPerThreadCpuSlicesFromCpuState_:function(){var SCHEDULING_STATE=tr.model.SCHEDULING_STATE;for(var cpuNumber in this.model_.kernel.cpus){var cpu=this.model_.kernel.cpus[cpuNumber];for(var i=0;i<cpu.slices.length;i++){var cpuSlice=cpu.slices[i];var thread=this.threadsByLinuxPid[cpuSlice.args.tid];if(!thread)
-continue;cpuSlice.threadThatWasRunning=thread;if(!thread.tempCpuSlices)
-thread.tempCpuSlices=[];thread.tempCpuSlices.push(cpuSlice);}}
-for(var i in this.wakeups_){var wakeup=this.wakeups_[i];var thread=this.threadsByLinuxPid[wakeup.tid];if(!thread)
-continue;thread.tempWakeups=thread.tempWakeups||[];thread.tempWakeups.push(wakeup);}
-for(var i in this.blocked_reasons_){var reason=this.blocked_reasons_[i];var thread=this.threadsByLinuxPid[reason.tid];if(!thread)
-continue;thread.tempBlockedReasons=thread.tempBlockedReasons||[];thread.tempBlockedReasons.push(reason);}
-this.model_.getAllThreads().forEach(function(thread){if(thread.tempCpuSlices===undefined)
-return;var origSlices=thread.tempCpuSlices;delete thread.tempCpuSlices;origSlices.sort(function(x,y){return x.start-y.start;});var wakeups=thread.tempWakeups||[];delete thread.tempWakeups;wakeups.sort(function(x,y){return x.ts-y.ts;});var reasons=thread.tempBlockedReasons||[];delete thread.tempBlockedReasons;reasons.sort(function(x,y){return x.ts-y.ts;});var slices=[];if(origSlices.length){var slice=origSlices[0];if(wakeups.length&&wakeups[0].ts<slice.start){var wakeup=wakeups.shift();var wakeupDuration=slice.start-wakeup.ts;var args={'wakeup from tid':wakeup.fromTid};slices.push(new tr.model.ThreadTimeSlice(thread,SCHEDULING_STATE.RUNNABLE,'',wakeup.ts,args,wakeupDuration));}
+return thread;},markPidRunnable:function(ts,pid,comm,prio,fromPid){this.wakeups_.push({ts:ts,tid:pid,fromTid:fromPid});},addPidBlockedReason:function(ts,pid,iowait,caller){this.blockedReasons_.push({ts:ts,tid:pid,iowait:iowait,caller:caller});},buildMapFromLinuxPidsToThreads_:function(){this.threadsByLinuxPid={};this.model_.getAllThreads().forEach(function(thread){this.threadsByLinuxPid[thread.tid]=thread;}.bind(this));},buildPerThreadCpuSlicesFromCpuState_:function(){var SCHEDULING_STATE=tr.model.SCHEDULING_STATE;for(var cpuNumber in this.model_.kernel.cpus){var cpu=this.model_.kernel.cpus[cpuNumber];for(var i=0;i<cpu.slices.length;i++){var cpuSlice=cpu.slices[i];var thread=this.threadsByLinuxPid[cpuSlice.args.tid];if(!thread)continue;cpuSlice.threadThatWasRunning=thread;if(!thread.tempCpuSlices){thread.tempCpuSlices=[];}
+thread.tempCpuSlices.push(cpuSlice);}}
+for(var i in this.wakeups_){var wakeup=this.wakeups_[i];var thread=this.threadsByLinuxPid[wakeup.tid];if(!thread)continue;thread.tempWakeups=thread.tempWakeups||[];thread.tempWakeups.push(wakeup);}
+for(var i in this.blockedReasons_){var reason=this.blockedReasons_[i];var thread=this.threadsByLinuxPid[reason.tid];if(!thread)continue;thread.tempBlockedReasons=thread.tempBlockedReasons||[];thread.tempBlockedReasons.push(reason);}
+this.model_.getAllThreads().forEach(function(thread){if(thread.tempCpuSlices===undefined)return;var origSlices=thread.tempCpuSlices;delete thread.tempCpuSlices;origSlices.sort(function(x,y){return x.start-y.start;});var wakeups=thread.tempWakeups||[];delete thread.tempWakeups;wakeups.sort(function(x,y){return x.ts-y.ts;});var reasons=thread.tempBlockedReasons||[];delete thread.tempBlockedReasons;reasons.sort(function(x,y){return x.ts-y.ts;});var slices=[];if(origSlices.length){var slice=origSlices[0];if(wakeups.length&&wakeups[0].ts<slice.start){var wakeup=wakeups.shift();var wakeupDuration=slice.start-wakeup.ts;var args={'wakeup from tid':wakeup.fromTid};slices.push(new tr.model.ThreadTimeSlice(thread,SCHEDULING_STATE.RUNNABLE,'',wakeup.ts,args,wakeupDuration));}
 var runningSlice=new tr.model.ThreadTimeSlice(thread,SCHEDULING_STATE.RUNNING,'',slice.start,{},slice.duration);runningSlice.cpuOnWhichThreadWasRunning=slice.cpu;slices.push(runningSlice);}
 var wakeup=undefined;for(var i=1;i<origSlices.length;i++){var prevSlice=origSlices[i-1];var nextSlice=origSlices[i];var midDuration=nextSlice.start-prevSlice.end;while(wakeups.length&&wakeups[0].ts<nextSlice.start){var w=wakeups.shift();if(wakeup===undefined&&w.ts>prevSlice.end){wakeup=w;}}
-var blocked_reason=undefined;while(reasons.length&&reasons[0].ts<prevSlice.end){var r=reasons.shift();}
-if(wakeup!==undefined&&reasons.length&&reasons[0].ts<wakeup.ts){blocked_reason=reasons.shift();}
+var blockedReason=undefined;while(reasons.length&&reasons[0].ts<prevSlice.end){var r=reasons.shift();}
+if(wakeup!==undefined&&reasons.length&&reasons[0].ts<wakeup.ts){blockedReason=reasons.shift();}
 var pushSleep=function(state){if(wakeup!==undefined){midDuration=wakeup.ts-prevSlice.end;}
-if(blocked_reason!==undefined){var args={'kernel callsite when blocked:':blocked_reason.caller};if(blocked_reason.iowait){switch(state){case SCHEDULING_STATE.UNINTR_SLEEP:state=SCHEDULING_STATE.UNINTR_SLEEP_IO;break;case SCHEDULING_STATE.UNINTR_SLEEP_WAKE_KILL:state=SCHEDULING_STATE.UNINTR_SLEEP_WAKE_KILL_IO;break;case SCHEDULING_STATE.UNINTR_SLEEP_WAKING:state=SCHEDULING_STATE.UNINTR_SLEEP_WAKE_KILL_IO;break;default:}}
+if(blockedReason!==undefined){var args={'kernel callsite when blocked:':blockedReason.caller};if(blockedReason.iowait){switch(state){case SCHEDULING_STATE.UNINTR_SLEEP:state=SCHEDULING_STATE.UNINTR_SLEEP_IO;break;case SCHEDULING_STATE.UNINTR_SLEEP_WAKE_KILL:state=SCHEDULING_STATE.UNINTR_SLEEP_WAKE_KILL_IO;break;case SCHEDULING_STATE.UNINTR_SLEEP_WAKING:state=SCHEDULING_STATE.UNINTR_SLEEP_WAKE_KILL_IO;break;default:}}
 slices.push(new tr.model.ThreadTimeSlice(thread,state,'',prevSlice.end,args,midDuration));}else{slices.push(new tr.model.ThreadTimeSlice(thread,state,'',prevSlice.end,{},midDuration));}
-if(wakeup!==undefined){var wakeupDuration=nextSlice.start-wakeup.ts;var args={'wakeup from tid':wakeup.fromTid};slices.push(new tr.model.ThreadTimeSlice(thread,SCHEDULING_STATE.RUNNABLE,'',wakeup.ts,args,wakeupDuration));wakeup=undefined;}};if(prevSlice.args.stateWhenDescheduled=='S'){pushSleep(SCHEDULING_STATE.SLEEPING);}else if(prevSlice.args.stateWhenDescheduled=='R'||prevSlice.args.stateWhenDescheduled=='R+'){slices.push(new tr.model.ThreadTimeSlice(thread,SCHEDULING_STATE.RUNNABLE,'',prevSlice.end,{},midDuration));}else if(prevSlice.args.stateWhenDescheduled=='D'){pushSleep(SCHEDULING_STATE.UNINTR_SLEEP);}else if(prevSlice.args.stateWhenDescheduled=='T'){slices.push(new tr.model.ThreadTimeSlice(thread,SCHEDULING_STATE.STOPPED,'',prevSlice.end,{},midDuration));}else if(prevSlice.args.stateWhenDescheduled=='t'){slices.push(new tr.model.ThreadTimeSlice(thread,SCHEDULING_STATE.DEBUG,'',prevSlice.end,{},midDuration));}else if(prevSlice.args.stateWhenDescheduled=='Z'){slices.push(new tr.model.ThreadTimeSlice(thread,SCHEDULING_STATE.ZOMBIE,'',prevSlice.end,{},midDuration));}else if(prevSlice.args.stateWhenDescheduled=='X'){slices.push(new tr.model.ThreadTimeSlice(thread,SCHEDULING_STATE.EXIT_DEAD,'',prevSlice.end,{},midDuration));}else if(prevSlice.args.stateWhenDescheduled=='x'){slices.push(new tr.model.ThreadTimeSlice(thread,SCHEDULING_STATE.TASK_DEAD,'',prevSlice.end,{},midDuration));}else if(prevSlice.args.stateWhenDescheduled=='K'){slices.push(new tr.model.ThreadTimeSlice(thread,SCHEDULING_STATE.WAKE_KILL,'',prevSlice.end,{},midDuration));}else if(prevSlice.args.stateWhenDescheduled=='W'){slices.push(new tr.model.ThreadTimeSlice(thread,SCHEDULING_STATE.WAKING,'',prevSlice.end,{},midDuration));}else if(prevSlice.args.stateWhenDescheduled=='D|K'){pushSleep(SCHEDULING_STATE.UNINTR_SLEEP_WAKE_KILL);}else if(prevSlice.args.stateWhenDescheduled=='D|W'){pushSleep(SCHEDULING_STATE.UNINTR_SLEEP_WAKING);}else{slices.push(new tr.model.ThreadTimeSlice(thread,SCHEDULING_STATE.UNKNOWN,'',prevSlice.end,{},midDuration));this.model_.importWarning({type:'parse_error',message:'Unrecognized sleep state: '+
+if(wakeup!==undefined){var wakeupDuration=nextSlice.start-wakeup.ts;var args={'wakeup from tid':wakeup.fromTid};slices.push(new tr.model.ThreadTimeSlice(thread,SCHEDULING_STATE.RUNNABLE,'',wakeup.ts,args,wakeupDuration));wakeup=undefined;}};if(prevSlice.args.stateWhenDescheduled==='S'){pushSleep(SCHEDULING_STATE.SLEEPING);}else if(prevSlice.args.stateWhenDescheduled==='R'||prevSlice.args.stateWhenDescheduled==='R+'){slices.push(new tr.model.ThreadTimeSlice(thread,SCHEDULING_STATE.RUNNABLE,'',prevSlice.end,{},midDuration));}else if(prevSlice.args.stateWhenDescheduled==='D'){pushSleep(SCHEDULING_STATE.UNINTR_SLEEP);}else if(prevSlice.args.stateWhenDescheduled==='T'){slices.push(new tr.model.ThreadTimeSlice(thread,SCHEDULING_STATE.STOPPED,'',prevSlice.end,{},midDuration));}else if(prevSlice.args.stateWhenDescheduled==='t'){slices.push(new tr.model.ThreadTimeSlice(thread,SCHEDULING_STATE.DEBUG,'',prevSlice.end,{},midDuration));}else if(prevSlice.args.stateWhenDescheduled==='Z'){slices.push(new tr.model.ThreadTimeSlice(thread,SCHEDULING_STATE.ZOMBIE,'',prevSlice.end,{},midDuration));}else if(prevSlice.args.stateWhenDescheduled==='X'){slices.push(new tr.model.ThreadTimeSlice(thread,SCHEDULING_STATE.EXIT_DEAD,'',prevSlice.end,{},midDuration));}else if(prevSlice.args.stateWhenDescheduled==='x'){slices.push(new tr.model.ThreadTimeSlice(thread,SCHEDULING_STATE.TASK_DEAD,'',prevSlice.end,{},midDuration));}else if(prevSlice.args.stateWhenDescheduled==='K'){slices.push(new tr.model.ThreadTimeSlice(thread,SCHEDULING_STATE.WAKE_KILL,'',prevSlice.end,{},midDuration));}else if(prevSlice.args.stateWhenDescheduled==='W'){slices.push(new tr.model.ThreadTimeSlice(thread,SCHEDULING_STATE.WAKING,'',prevSlice.end,{},midDuration));}else if(prevSlice.args.stateWhenDescheduled==='D|K'){pushSleep(SCHEDULING_STATE.UNINTR_SLEEP_WAKE_KILL);}else if(prevSlice.args.stateWhenDescheduled==='D|W'){pushSleep(SCHEDULING_STATE.UNINTR_SLEEP_WAKING);}else{slices.push(new tr.model.ThreadTimeSlice(thread,SCHEDULING_STATE.UNKNOWN,'',prevSlice.end,{},midDuration));this.model_.importWarning({type:'parse_error',message:'Unrecognized sleep state: '+
 prevSlice.args.stateWhenDescheduled});}
 var runningSlice=new tr.model.ThreadTimeSlice(thread,SCHEDULING_STATE.RUNNING,'',nextSlice.start,{},nextSlice.duration);runningSlice.cpuOnWhichThreadWasRunning=prevSlice.cpu;slices.push(runningSlice);}
-thread.timeSlices=slices;},this);},createParsers_:function(){var allTypeInfos=tr.e.importer.linux_perf.Parser.getAllRegisteredTypeInfos();var parsers=allTypeInfos.map(function(typeInfo){return new typeInfo.constructor(this);},this);return parsers;},registerDefaultHandlers_:function(){this.registerEventHandler('tracing_mark_write',FTraceImporter.prototype.traceMarkingWriteEvent_.bind(this));this.registerEventHandler('0',FTraceImporter.prototype.traceMarkingWriteEvent_.bind(this));this.registerEventHandler('tracing_mark_write:trace_event_clock_sync',function(){return true;});this.registerEventHandler('0:trace_event_clock_sync',function(){return true;});},traceClockSyncEvent_:function(eventName,cpuNumber,pid,ts,eventBase){var event=/name=(\w+?)\s(.+)/.exec(eventBase.details);if(event){var name=event[1];var pieces=event[2].split(' ');var args={perfTs:ts};for(var i=0;i<pieces.length;i++){var parts=pieces[i].split('=');if(parts.length!=2)
-throw new Error('omgbbq');args[parts[0]]=parts[1];}
+thread.timeSlices=slices;},this);},createParsers_:function(){var allTypeInfos=tr.e.importer.linux_perf.Parser.getAllRegisteredTypeInfos();var parsers=allTypeInfos.map(function(typeInfo){return new typeInfo.constructor(this);},this);return parsers;},registerDefaultHandlers_:function(){this.registerEventHandler('tracing_mark_write',FTraceImporter.prototype.traceMarkingWriteEvent_.bind(this));this.registerEventHandler('0',FTraceImporter.prototype.traceMarkingWriteEvent_.bind(this));this.registerEventHandler('tracing_mark_write:trace_event_clock_sync',function(){return true;});this.registerEventHandler('0:trace_event_clock_sync',function(){return true;});},traceClockSyncEvent_:function(eventName,cpuNumber,pid,ts,eventBase){var event=/name=(\w+?)\s(.+)/.exec(eventBase.details);if(event){var name=event[1];var pieces=event[2].split(' ');var args={perfTs:ts};for(var i=0;i<pieces.length;i++){var parts=pieces[i].split('=');if(parts.length!==2){throw new Error('omgbbq');}
+args[parts[0]]=parts[1];}
 this.model_.clockSyncManager.addClockSyncMarker(tr.model.ClockDomainId.LINUX_FTRACE_GLOBAL,name,ts);return true;}
 var event=/name=([\w\-]+)/.exec(eventBase.details);if(event){this.model_.clockSyncManager.addClockSyncMarker(tr.model.ClockDomainId.LINUX_FTRACE_GLOBAL,event[1],ts);return true;}
-event=/parent_ts=(\d+\.?\d*)/.exec(eventBase.details);if(!event)
-return false;var monotonicTs=event[1]*1000;if(monotonicTs===0)
-monotonicTs=ts;if(this.haveClockSyncedMonotonicToGlobal_)
-return true;this.model_.clockSyncManager.addClockSyncMarker(tr.model.ClockDomainId.LINUX_FTRACE_GLOBAL,MONOTONIC_TO_FTRACE_GLOBAL_SYNC_ID,ts);this.model_.clockSyncManager.addClockSyncMarker(tr.model.ClockDomainId.LINUX_CLOCK_MONOTONIC,MONOTONIC_TO_FTRACE_GLOBAL_SYNC_ID,monotonicTs);this.haveClockSyncedMonotonicToGlobal_=true;return true;},traceMarkingWriteEvent_:function(eventName,cpuNumber,pid,ts,eventBase,threadName){eventBase.details=eventBase.details.replace(/\\n.*$/,'');var event=/^\s*(\w+):\s*(.*)$/.exec(eventBase.details);if(!event){var tag=eventBase.details.substring(0,2);if(tag=='B|'||tag=='E'||tag=='E|'||tag=='X|'||tag=='C|'||tag=='S|'||tag=='F|'){eventBase.subEventName='android';}else{return false;}}else{eventBase.subEventName=event[1];eventBase.details=event[2];}
+event=/parent_ts=(\d+\.?\d*)/.exec(eventBase.details);if(!event)return false;var monotonicTs=event[1]*1000;if(monotonicTs===0)monotonicTs=ts;if(this.haveClockSyncedMonotonicToGlobal_){return true;}
+this.model_.clockSyncManager.addClockSyncMarker(tr.model.ClockDomainId.LINUX_FTRACE_GLOBAL,MONOTONIC_TO_FTRACE_GLOBAL_SYNC_ID,ts);this.model_.clockSyncManager.addClockSyncMarker(tr.model.ClockDomainId.LINUX_CLOCK_MONOTONIC,MONOTONIC_TO_FTRACE_GLOBAL_SYNC_ID,monotonicTs);this.haveClockSyncedMonotonicToGlobal_=true;return true;},traceMarkingWriteEvent_:function(eventName,cpuNumber,pid,ts,eventBase,threadName){eventBase.details=eventBase.details.replace(/\\n.*$/,'');var event=/^\s*(\w+):\s*(.*)$/.exec(eventBase.details);if(!event){var tag=eventBase.details.substring(0,2);if(tag==='B|'||tag==='E'||tag==='E|'||tag==='X|'||tag==='C|'||tag==='S|'||tag==='F|'){eventBase.subEventName='android';}else{return false;}}else{eventBase.subEventName=event[1];eventBase.details=event[2];}
 var writeEventName=eventName+':'+eventBase.subEventName;var handler=this.eventHandlers_[writeEventName];if(!handler){this.model_.importWarning({type:'parse_error',message:'Unknown trace_marking_write event '+writeEventName});return true;}
 return handler(writeEventName,cpuNumber,pid,ts,eventBase,threadName);},importCpuData_:function(modelTimeTransformer){this.forEachLine_(function(text,eventBase,cpuNumber,pid,ts){var eventName=eventBase.eventName;var handler=this.eventHandlers_[eventName];if(!handler){this.model_.importWarning({type:'parse_error',message:'Unknown event '+eventName+' ('+text+')'});return;}
-ts=modelTimeTransformer(ts);if(!handler(eventName,cpuNumber,pid,ts,eventBase)){this.model_.importWarning({type:'parse_error',message:'Malformed '+eventName+' event ('+text+')'});}}.bind(this));},parseLines_:function(){var lines=[];var extractResult=FTraceImporter._extractEventsFromSystraceHTML(this.events_,true);if(!extractResult.ok)
-extractResult=FTraceImporter._extractEventsFromSystraceMultiHTML(this.events_,true);var lines=extractResult.ok?extractResult.lines:this.events_.split('\n');var lineParser=undefined;for(var lineNumber=0;lineNumber<lines.length;++lineNumber){var line=lines[lineNumber].trim();if(line.length==0||/^#/.test(line))
-continue;if(!lineParser){lineParser=autoDetectLineParser(line);if(!lineParser){this.model_.importWarning({type:'parse_error',message:'Cannot parse line: '+line});continue;}}
+ts=modelTimeTransformer(ts);if(!handler(eventName,cpuNumber,pid,ts,eventBase)){this.model_.importWarning({type:'parse_error',message:'Malformed '+eventName+' event ('+text+')'});}}.bind(this));},parseLines_:function(){var lines=[];var extractResult=FTraceImporter._extractEventsFromSystraceHTML(this.events_,true);if(!extractResult.ok){extractResult=FTraceImporter._extractEventsFromSystraceMultiHTML(this.events_,true);}
+var lines=extractResult.ok?extractResult.lines:this.events_.split('\n');var lineParser=undefined;for(var lineNumber=0;lineNumber<lines.length;++lineNumber){var line=lines[lineNumber].trim();if(line.length===0||/^#/.test(line))continue;if(!lineParser){lineParser=autoDetectLineParser(line);if(!lineParser){this.model_.importWarning({type:'parse_error',message:'Cannot parse line: '+line});continue;}}
 var eventBase=lineParser(line);if(!eventBase){this.model_.importWarning({type:'parse_error',message:'Unrecognized line: '+line});continue;}
-this.lines_.push([line,eventBase,parseInt(eventBase.cpuNumber),parseInt(eventBase.pid),parseFloat(eventBase.timestamp)*1000]);}},forEachLine_:function(handler){for(var i=0;i<this.lines_.length;++i){var line=this.lines_[i];handler.apply(this,line);}},lazyInit_:function(){this.parsers_=this.createParsers_();this.registerDefaultHandlers_();this.parseLines_();}};tr.importer.Importer.register(FTraceImporter);return{FTraceImporter:FTraceImporter,_FTraceImporterTestExports:TestExports};});'use strict';function filterDuplicateTimestamps(timestamps){var dedupedTimestamps=[];var lastTs=0;for(var ts of timestamps){if(ts-lastTs>=1){dedupedTimestamps.push(ts);lastTs=ts;}}
+this.lines_.push([line,eventBase,parseInt(eventBase.cpuNumber),parseInt(eventBase.pid),parseFloat(eventBase.timestamp)*1000]);}},forEachLine_:function(handler){for(var i=0;i<this.lines_.length;++i){var line=this.lines_[i];handler.apply(this,line);}},lazyInit_:function(){this.parsers_=this.createParsers_();this.registerDefaultHandlers_();this.parseLines_();}};tr.importer.Importer.register(FTraceImporter);return{FTraceImporter,_FTraceImporterTestExports:TestExports};});'use strict';function filterDuplicateTimestamps(timestamps){var dedupedTimestamps=[];var lastTs=0;for(var ts of timestamps){if(ts-lastTs>=1){dedupedTimestamps.push(ts);lastTs=ts;}}
 return dedupedTimestamps;}
-tr.exportTo('tr.e.audits',function(){var VSYNC_COUNTER_PRECISIONS={'android.VSYNC-app':15,'android.VSYNC':15};var VSYNC_SLICE_PRECISIONS={'RenderWidgetHostViewAndroid::OnVSync':5,'VSYNC':10,'vblank':10,'DisplayLinkMac::GetVSyncParameters':5};var BEGIN_FRAME_SLICE_PRECISION={'DisplayScheduler::BeginFrame':10};function VSyncAuditor(model){tr.c.Auditor.call(this,model);};VSyncAuditor.prototype={__proto__:tr.c.Auditor.prototype,runAnnotate:function(){this.model.device.vSyncTimestamps=this.findVSyncTimestamps(this.model);},findVSyncTimestamps:function(model){var times=[];var maxPrecision=Number.NEGATIVE_INFINITY;var maxTitle=undefined;function useInstead(title,precisions){var precision=precisions[title];if(precision===undefined)
-return false;if(title===maxTitle)
-return true;if(precision<=maxPrecision){if(precision===maxPrecision){console.warn('Encountered two different VSync events ('+
-maxTitle+', '+title+') with the same precision, '+'ignoring the newer one ('+title+')');}
+tr.exportTo('tr.e.audits',function(){var VSYNC_COUNTER_PRECISIONS={'android.VSYNC-app':15,'android.VSYNC':15};var VSYNC_SLICE_PRECISIONS={'RenderWidgetHostViewAndroid::OnVSync':5,'VSYNC':10,'vblank':10,'DisplayLinkMac::GetVSyncParameters':5};var BEGIN_FRAME_SLICE_PRECISION={'DisplayScheduler::BeginFrame':10};function VSyncAuditor(model){tr.c.Auditor.call(this,model);}
+VSyncAuditor.prototype={__proto__:tr.c.Auditor.prototype,runAnnotate:function(){this.model.device.vSyncTimestamps=this.findVSyncTimestamps(this.model);},findVSyncTimestamps:function(model){var times=[];var maxPrecision=Number.NEGATIVE_INFINITY;var maxTitle=undefined;function useInstead(title,precisions){var precision=precisions[title];if(precision===undefined)return false;if(title===maxTitle)return true;if(precision<=maxPrecision){if(precision===maxPrecision){model.importWarning({type:'VSyncAuditor',message:'Encountered two different VSync events ('+
+maxTitle+', '+title+') with the same precision, '+'ignoring the newer one ('+title+')',showToUser:false,});}
 return false;}
 maxPrecision=precision;maxTitle=title;times=[];return true;}
 for(var pid in model.processes){var process=model.processes[pid];for(var cid in process.counters){if(useInstead(cid,VSYNC_COUNTER_PRECISIONS)){var counter=process.counters[cid];for(var i=0;i<counter.series.length;i++){var series=counter.series[i];Array.prototype.push.apply(times,series.timestamps);}}}
-for(var tid in process.threads){var thread=process.threads[tid];for(var i=0;i<thread.sliceGroup.slices.length;i++){var slice=thread.sliceGroup.slices[i];if(useInstead(slice.title,VSYNC_SLICE_PRECISIONS))
-times.push(slice.start);else if(useInstead(slice.title,BEGIN_FRAME_SLICE_PRECISION)&&slice.args.args&&slice.args.args.frame_time_us)
-times.push(slice.args.args.frame_time_us/1000.0);}}}
-times.sort(function(x,y){return x-y;});return filterDuplicateTimestamps(times);}};tr.c.Auditor.register(VSyncAuditor);return{VSyncAuditor:VSyncAuditor};});'use strict';tr.exportTo('tr.importer',function(){function EmptyImporter(events){this.importPriority=0;};EmptyImporter.canImport=function(eventData){if(eventData instanceof Array&&eventData.length==0)
-return true;if(typeof(eventData)==='string'||eventData instanceof String){return eventData.length==0;}
-return false;};EmptyImporter.prototype={__proto__:tr.importer.Importer.prototype,get importerName(){return'EmptyImporter';}};tr.importer.Importer.register(EmptyImporter);return{EmptyImporter:EmptyImporter};});'use strict';tr.exportTo('tr.model.um',function(){function AnimationExpectation(parentModel,initiatorTitle,start,duration){tr.model.um.UserExpectation.call(this,parentModel,initiatorTitle,start,duration);this.frameEvents_=undefined;}
-AnimationExpectation.prototype={__proto__:tr.model.um.UserExpectation.prototype,constructor:AnimationExpectation,get frameEvents(){if(this.frameEvents_)
-return this.frameEvents_;this.frameEvents_=new tr.model.EventSet();this.associatedEvents.forEach(function(event){if(event.title===tr.model.helpers.IMPL_RENDERING_STATS)
-this.frameEvents_.push(event);},this);return this.frameEvents_;}};tr.model.um.UserExpectation.subTypes.register(AnimationExpectation,{stageTitle:'Animation',colorId:tr.b.ColorScheme.getColorIdForReservedName('rail_animation')});return{AnimationExpectation:AnimationExpectation};});'use strict';tr.exportTo('tr.importer',function(){function ProtoExpectation(irType,name){this.irType=irType;this.names=new Set(name?[name]:undefined);this.start=Infinity;this.end=-Infinity;this.associatedEvents=new tr.model.EventSet();this.isAnimationBegin=false;}
-ProtoExpectation.RESPONSE_TYPE='r';ProtoExpectation.ANIMATION_TYPE='a';ProtoExpectation.IGNORED_TYPE='ignored';ProtoExpectation.prototype={get isValid(){return this.end>this.start;},containsTypeNames:function(typeNames){return this.associatedEvents.some(x=>typeNames.indexOf(x.typeName)>=0);},containsSliceTitle:function(title){return this.associatedEvents.some(x=>title===x.title);},createInteractionRecord:function(model){if(!this.isValid){console.error('Invalid ProtoExpectation: '+this.debug()+' File a bug with this trace!');return undefined;}
-var initiatorTitles=[];this.names.forEach(function(name){initiatorTitles.push(name);});initiatorTitles=initiatorTitles.sort().join(',');var duration=this.end-this.start;var ir=undefined;switch(this.irType){case ProtoExpectation.RESPONSE_TYPE:ir=new tr.model.um.ResponseExpectation(model,initiatorTitles,this.start,duration,this.isAnimationBegin);break;case ProtoExpectation.ANIMATION_TYPE:ir=new tr.model.um.AnimationExpectation(model,initiatorTitles,this.start,duration);break;}
-if(!ir)
-return undefined;ir.sourceEvents.addEventSet(this.associatedEvents);function pushAssociatedEvents(event){ir.associatedEvents.push(event);if(event.associatedEvents)
-ir.associatedEvents.addEventSet(event.associatedEvents);}
-this.associatedEvents.forEach(function(event){pushAssociatedEvents(event);if(event.subSlices)
-event.subSlices.forEach(pushAssociatedEvents);});return ir;},merge:function(other){other.names.forEach(function(name){this.names.add(name);}.bind(this));this.associatedEvents.addEventSet(other.associatedEvents);this.start=Math.min(this.start,other.start);this.end=Math.max(this.end,other.end);if(other.isAnimationBegin)
-this.isAnimationBegin=true;},pushEvent:function(event){this.start=Math.min(this.start,event.start);this.end=Math.max(this.end,event.end);this.associatedEvents.push(event);},containsTimestampInclusive:function(timestamp){return(this.start<=timestamp)&&(timestamp<=this.end);},intersects:function(other){return(other.start<this.end)&&(other.end>this.start);},isNear:function(event,threshold){return(this.end+threshold)>event.start;},debug:function(){var debugString=this.irType+'(';debugString+=parseInt(this.start)+' ';debugString+=parseInt(this.end);this.associatedEvents.forEach(function(event){debugString+=' '+event.typeName;});return debugString+')';}};return{ProtoExpectation:ProtoExpectation};});'use strict';tr.exportTo('tr.importer',function(){var ProtoExpectation=tr.importer.ProtoExpectation;var INPUT_TYPE=tr.e.cc.INPUT_EVENT_TYPE_NAMES;var KEYBOARD_TYPE_NAMES=[INPUT_TYPE.CHAR,INPUT_TYPE.KEY_DOWN_RAW,INPUT_TYPE.KEY_DOWN,INPUT_TYPE.KEY_UP];var MOUSE_RESPONSE_TYPE_NAMES=[INPUT_TYPE.CLICK,INPUT_TYPE.CONTEXT_MENU];var MOUSE_WHEEL_TYPE_NAMES=[INPUT_TYPE.MOUSE_WHEEL];var MOUSE_DRAG_TYPE_NAMES=[INPUT_TYPE.MOUSE_DOWN,INPUT_TYPE.MOUSE_MOVE,INPUT_TYPE.MOUSE_UP];var TAP_TYPE_NAMES=[INPUT_TYPE.TAP,INPUT_TYPE.TAP_CANCEL,INPUT_TYPE.TAP_DOWN];var PINCH_TYPE_NAMES=[INPUT_TYPE.PINCH_BEGIN,INPUT_TYPE.PINCH_END,INPUT_TYPE.PINCH_UPDATE];var FLING_TYPE_NAMES=[INPUT_TYPE.FLING_CANCEL,INPUT_TYPE.FLING_START];var TOUCH_TYPE_NAMES=[INPUT_TYPE.TOUCH_END,INPUT_TYPE.TOUCH_MOVE,INPUT_TYPE.TOUCH_START];var SCROLL_TYPE_NAMES=[INPUT_TYPE.SCROLL_BEGIN,INPUT_TYPE.SCROLL_END,INPUT_TYPE.SCROLL_UPDATE];var ALL_HANDLED_TYPE_NAMES=[].concat(KEYBOARD_TYPE_NAMES,MOUSE_RESPONSE_TYPE_NAMES,MOUSE_WHEEL_TYPE_NAMES,MOUSE_DRAG_TYPE_NAMES,PINCH_TYPE_NAMES,TAP_TYPE_NAMES,FLING_TYPE_NAMES,TOUCH_TYPE_NAMES,SCROLL_TYPE_NAMES);var RENDERER_FLING_TITLE='InputHandlerProxy::HandleGestureFling::started';var PLAYBACK_EVENT_TITLE='VideoPlayback';var CSS_ANIMATION_TITLE='Animation';var INPUT_MERGE_THRESHOLD_MS=200;var ANIMATION_MERGE_THRESHOLD_MS=32;var MOUSE_WHEEL_THRESHOLD_MS=40;var MOUSE_MOVE_THRESHOLD_MS=40;var KEYBOARD_IR_NAME='Keyboard';var MOUSE_IR_NAME='Mouse';var MOUSEWHEEL_IR_NAME='MouseWheel';var TAP_IR_NAME='Tap';var PINCH_IR_NAME='Pinch';var FLING_IR_NAME='Fling';var TOUCH_IR_NAME='Touch';var SCROLL_IR_NAME='Scroll';var CSS_IR_NAME='CSS';var WEBGL_IR_NAME='WebGL';var VIDEO_IR_NAME='Video';function compareEvents(x,y){if(x.start!==y.start)
-return x.start-y.start;if(x.end!==y.end)
-return x.end-y.end;if(x.guid&&y.guid)
-return x.guid-y.guid;return 0;}
+for(var tid in process.threads){var thread=process.threads[tid];for(var i=0;i<thread.sliceGroup.slices.length;i++){var slice=thread.sliceGroup.slices[i];if(useInstead(slice.title,VSYNC_SLICE_PRECISIONS)){times.push(slice.start);}else if(useInstead(slice.title,BEGIN_FRAME_SLICE_PRECISION)&&slice.args.args&&slice.args.args.frame_time_us){times.push(slice.args.args.frame_time_us/1000.0);}}}}
+times.sort(function(x,y){return x-y;});return filterDuplicateTimestamps(times);}};tr.c.Auditor.register(VSyncAuditor);return{VSyncAuditor,};});'use strict';tr.exportTo('tr.importer',function(){function EmptyImporter(events){this.importPriority=0;}
+EmptyImporter.canImport=function(eventData){if(eventData instanceof Array&&eventData.length===0){return true;}
+if(typeof(eventData)==='string'||eventData instanceof String){return eventData.length===0;}
+return false;};EmptyImporter.prototype={__proto__:tr.importer.Importer.prototype,get importerName(){return'EmptyImporter';}};tr.importer.Importer.register(EmptyImporter);return{EmptyImporter,};});'use strict';tr.exportTo('tr.model.um',function(){function AnimationExpectation(parentModel,initiatorTitle,start,duration){tr.model.um.UserExpectation.call(this,parentModel,initiatorTitle,start,duration);this.frameEvents_=undefined;}
+AnimationExpectation.prototype={__proto__:tr.model.um.UserExpectation.prototype,constructor:AnimationExpectation,get frameEvents(){if(this.frameEvents_){return this.frameEvents_;}
+this.frameEvents_=new tr.model.EventSet();this.associatedEvents.forEach(function(event){if(event.title===tr.model.helpers.IMPL_RENDERING_STATS){this.frameEvents_.push(event);}},this);return this.frameEvents_;}};tr.model.um.UserExpectation.subTypes.register(AnimationExpectation,{stageTitle:'Animation',colorId:tr.b.ColorScheme.getColorIdForReservedName('rail_animation')});return{AnimationExpectation,};});'use strict';tr.exportTo('tr.importer',function(){function ProtoExpectation(type,initiatorType){this.type=type;this.initiatorType=initiatorType;this.start=Infinity;this.end=-Infinity;this.associatedEvents=new tr.model.EventSet();this.isAnimationBegin=false;}
+ProtoExpectation.RESPONSE_TYPE='r';ProtoExpectation.ANIMATION_TYPE='a';ProtoExpectation.IGNORED_TYPE='ignored';var INITIATOR_HIERARCHY=[tr.model.um.INITIATOR_TYPE.PINCH,tr.model.um.INITIATOR_TYPE.FLING,tr.model.um.INITIATOR_TYPE.MOUSE_WHEEL,tr.model.um.INITIATOR_TYPE.SCROLL,tr.model.um.INITIATOR_TYPE.VIDEO,tr.model.um.INITIATOR_TYPE.WEBGL,tr.model.um.INITIATOR_TYPE.CSS,tr.model.um.INITIATOR_TYPE.MOUSE,tr.model.um.INITIATOR_TYPE.KEYBOARD,tr.model.um.INITIATOR_TYPE.TAP,tr.model.um.INITIATOR_TYPE.TOUCH];function combineInitiatorTypes(title1,title2){for(var item of INITIATOR_HIERARCHY){if(title1===item||title2===item)return item;}
+throw new Error('Invalid titles in combineInitiatorTypes');}
+ProtoExpectation.prototype={get isValid(){return this.end>this.start;},containsTypeNames:function(typeNames){return this.associatedEvents.some(x=>typeNames.indexOf(x.typeName)>=0);},containsSliceTitle:function(title){return this.associatedEvents.some(x=>title===x.title);},createInteractionRecord:function(model){if(this.type!==ProtoExpectation.IGNORED_TYPE&&!this.isValid){model.importWarning({type:'ProtoExpectation',message:'Please file a bug with this trace. '+this.debug(),showToUser:true});return undefined;}
+var duration=this.end-this.start;var ir=undefined;switch(this.type){case ProtoExpectation.RESPONSE_TYPE:ir=new tr.model.um.ResponseExpectation(model,this.initiatorType,this.start,duration,this.isAnimationBegin);break;case ProtoExpectation.ANIMATION_TYPE:ir=new tr.model.um.AnimationExpectation(model,this.initiatorType,this.start,duration);break;}
+if(!ir)return undefined;ir.sourceEvents.addEventSet(this.associatedEvents);function pushAssociatedEvents(event){ir.associatedEvents.push(event);if(event.associatedEvents){ir.associatedEvents.addEventSet(event.associatedEvents);}}
+this.associatedEvents.forEach(function(event){pushAssociatedEvents(event);if(event.subSlices){event.subSlices.forEach(pushAssociatedEvents);}});return ir;},merge:function(other){this.initiatorType=combineInitiatorTypes(this.initiatorType,other.initiatorType);this.associatedEvents.addEventSet(other.associatedEvents);this.start=Math.min(this.start,other.start);this.end=Math.max(this.end,other.end);if(other.isAnimationBegin){this.isAnimationBegin=true;}},pushEvent:function(event){this.start=Math.min(this.start,event.start);this.end=Math.max(this.end,event.end);this.associatedEvents.push(event);},containsTimestampInclusive:function(timestamp){return(this.start<=timestamp)&&(timestamp<=this.end);},intersects:function(other){return(other.start<this.end)&&(other.end>this.start);},isNear:function(event,threshold){return(this.end+threshold)>event.start;},debug:function(){var debugString=this.type+'(';debugString+=parseInt(this.start)+' ';debugString+=parseInt(this.end);this.associatedEvents.forEach(function(event){debugString+=' '+event.typeName;});return debugString+')';}};return{ProtoExpectation,};});'use strict';tr.exportTo('tr.importer',function(){var ProtoExpectation=tr.importer.ProtoExpectation;var INITIATOR_TYPE=tr.model.um.INITIATOR_TYPE;var INPUT_TYPE=tr.e.cc.INPUT_EVENT_TYPE_NAMES;var KEYBOARD_TYPE_NAMES=[INPUT_TYPE.CHAR,INPUT_TYPE.KEY_DOWN_RAW,INPUT_TYPE.KEY_DOWN,INPUT_TYPE.KEY_UP];var MOUSE_RESPONSE_TYPE_NAMES=[INPUT_TYPE.CLICK,INPUT_TYPE.CONTEXT_MENU];var MOUSE_WHEEL_TYPE_NAMES=[INPUT_TYPE.MOUSE_WHEEL];var MOUSE_DRAG_TYPE_NAMES=[INPUT_TYPE.MOUSE_DOWN,INPUT_TYPE.MOUSE_MOVE,INPUT_TYPE.MOUSE_UP];var TAP_TYPE_NAMES=[INPUT_TYPE.TAP,INPUT_TYPE.TAP_CANCEL,INPUT_TYPE.TAP_DOWN];var PINCH_TYPE_NAMES=[INPUT_TYPE.PINCH_BEGIN,INPUT_TYPE.PINCH_END,INPUT_TYPE.PINCH_UPDATE];var FLING_TYPE_NAMES=[INPUT_TYPE.FLING_CANCEL,INPUT_TYPE.FLING_START];var TOUCH_TYPE_NAMES=[INPUT_TYPE.TOUCH_END,INPUT_TYPE.TOUCH_MOVE,INPUT_TYPE.TOUCH_START];var SCROLL_TYPE_NAMES=[INPUT_TYPE.SCROLL_BEGIN,INPUT_TYPE.SCROLL_END,INPUT_TYPE.SCROLL_UPDATE];var ALL_HANDLED_TYPE_NAMES=[].concat(KEYBOARD_TYPE_NAMES,MOUSE_RESPONSE_TYPE_NAMES,MOUSE_WHEEL_TYPE_NAMES,MOUSE_DRAG_TYPE_NAMES,PINCH_TYPE_NAMES,TAP_TYPE_NAMES,FLING_TYPE_NAMES,TOUCH_TYPE_NAMES,SCROLL_TYPE_NAMES);var RENDERER_FLING_TITLE='InputHandlerProxy::HandleGestureFling::started';var PLAYBACK_EVENT_TITLE='VideoPlayback';var CSS_ANIMATION_TITLE='Animation';var INPUT_MERGE_THRESHOLD_MS=200;var ANIMATION_MERGE_THRESHOLD_MS=32;var MOUSE_WHEEL_THRESHOLD_MS=40;var MOUSE_MOVE_THRESHOLD_MS=40;function compareEvents(x,y){if(x.start!==y.start){return x.start-y.start;}
+if(x.end!==y.end){return x.end-y.end;}
+if(x.guid&&y.guid){return x.guid-y.guid;}
+return 0;}
 function forEventTypesIn(events,typeNames,cb,opt_this){events.forEach(function(event){if(typeNames.indexOf(event.typeName)>=0){cb.call(opt_this,event);}});}
 function causedFrame(event){return event.associatedEvents.some(x=>x.title===tr.model.helpers.IMPL_RENDERING_STATS);}
-function getSortedFrameEventsByProcess(modelHelper){var frameEventsByPid={};tr.b.iterItems(modelHelper.rendererHelpers,function(pid,rendererHelper){frameEventsByPid[pid]=rendererHelper.getFrameEventsInRange(tr.model.helpers.IMPL_FRAMETIME_TYPE,modelHelper.model.bounds);});return frameEventsByPid;}
-function getSortedInputEvents(modelHelper){var inputEvents=[];var browserProcess=modelHelper.browserHelper.process;var mainThread=browserProcess.findAtMostOneThreadNamed('CrBrowserMain');for(var slice of mainThread.asyncSliceGroup.getDescendantEvents()){if(!slice.isTopLevel)
-continue;if(!(slice instanceof tr.e.cc.InputLatencyAsyncSlice))
-continue;if(isNaN(slice.start)||isNaN(slice.duration)||isNaN(slice.end))
-continue;inputEvents.push(slice);}
+function getSortedFrameEventsByProcess(modelHelper){var frameEventsByPid={};for(var[pid,rendererHelper]of
+Object.entries(modelHelper.rendererHelpers)){frameEventsByPid[pid]=rendererHelper.getFrameEventsInRange(tr.model.helpers.IMPL_FRAMETIME_TYPE,modelHelper.model.bounds);}
+return frameEventsByPid;}
+function getSortedInputEvents(modelHelper){var inputEvents=[];var browserProcess=modelHelper.browserHelper.process;var mainThread=browserProcess.findAtMostOneThreadNamed('CrBrowserMain');for(var slice of mainThread.asyncSliceGroup.getDescendantEvents()){if(!slice.isTopLevel)continue;if(!(slice instanceof tr.e.cc.InputLatencyAsyncSlice))continue;if(isNaN(slice.start)||isNaN(slice.duration)||isNaN(slice.end)){continue;}
+inputEvents.push(slice);}
 return inputEvents.sort(compareEvents);}
 function findProtoExpectations(modelHelper,sortedInputEvents){var protoExpectations=[];var handlers=[handleKeyboardEvents,handleMouseResponseEvents,handleMouseWheelEvents,handleMouseDragEvents,handleTapResponseEvents,handlePinchEvents,handleFlingEvents,handleTouchEvents,handleScrollEvents,handleCSSAnimations,handleWebGLAnimations,handleVideoAnimations];handlers.forEach(function(handler){protoExpectations.push.apply(protoExpectations,handler(modelHelper,sortedInputEvents));});protoExpectations.sort(compareEvents);return protoExpectations;}
-function handleKeyboardEvents(modelHelper,sortedInputEvents){var protoExpectations=[];forEventTypesIn(sortedInputEvents,KEYBOARD_TYPE_NAMES,function(event){var pe=new ProtoExpectation(ProtoExpectation.RESPONSE_TYPE,KEYBOARD_IR_NAME);pe.pushEvent(event);protoExpectations.push(pe);});return protoExpectations;}
-function handleMouseResponseEvents(modelHelper,sortedInputEvents){var protoExpectations=[];forEventTypesIn(sortedInputEvents,MOUSE_RESPONSE_TYPE_NAMES,function(event){var pe=new ProtoExpectation(ProtoExpectation.RESPONSE_TYPE,MOUSE_IR_NAME);pe.pushEvent(event);protoExpectations.push(pe);});return protoExpectations;}
-function handleMouseWheelEvents(modelHelper,sortedInputEvents){var protoExpectations=[];var currentPE=undefined;var prevEvent_=undefined;forEventTypesIn(sortedInputEvents,MOUSE_WHEEL_TYPE_NAMES,function(event){var prevEvent=prevEvent_;prevEvent_=event;if(currentPE&&(prevEvent.start+MOUSE_WHEEL_THRESHOLD_MS)>=event.start){if(currentPE.irType===ProtoExpectation.ANIMATION_TYPE){currentPE.pushEvent(event);}else{currentPE=new ProtoExpectation(ProtoExpectation.ANIMATION_TYPE,MOUSEWHEEL_IR_NAME);currentPE.pushEvent(event);protoExpectations.push(currentPE);}
+function handleKeyboardEvents(modelHelper,sortedInputEvents){var protoExpectations=[];forEventTypesIn(sortedInputEvents,KEYBOARD_TYPE_NAMES,function(event){var pe=new ProtoExpectation(ProtoExpectation.RESPONSE_TYPE,INITIATOR_TYPE.KEYBOARD);pe.pushEvent(event);protoExpectations.push(pe);});return protoExpectations;}
+function handleMouseResponseEvents(modelHelper,sortedInputEvents){var protoExpectations=[];forEventTypesIn(sortedInputEvents,MOUSE_RESPONSE_TYPE_NAMES,function(event){var pe=new ProtoExpectation(ProtoExpectation.RESPONSE_TYPE,INITIATOR_TYPE.MOUSE);pe.pushEvent(event);protoExpectations.push(pe);});return protoExpectations;}
+function handleMouseWheelEvents(modelHelper,sortedInputEvents){var protoExpectations=[];var currentPE=undefined;var prevEvent_=undefined;forEventTypesIn(sortedInputEvents,MOUSE_WHEEL_TYPE_NAMES,function(event){var prevEvent=prevEvent_;prevEvent_=event;if(currentPE&&(prevEvent.start+MOUSE_WHEEL_THRESHOLD_MS)>=event.start){if(currentPE.type===ProtoExpectation.ANIMATION_TYPE){currentPE.pushEvent(event);}else{currentPE=new ProtoExpectation(ProtoExpectation.ANIMATION_TYPE,INITIATOR_TYPE.MOUSE_WHEEL);currentPE.pushEvent(event);protoExpectations.push(currentPE);}
 return;}
-currentPE=new ProtoExpectation(ProtoExpectation.RESPONSE_TYPE,MOUSEWHEEL_IR_NAME);currentPE.pushEvent(event);protoExpectations.push(currentPE);});return protoExpectations;}
-function handleMouseDragEvents(modelHelper,sortedInputEvents){var protoExpectations=[];var currentPE=undefined;var mouseDownEvent=undefined;forEventTypesIn(sortedInputEvents,MOUSE_DRAG_TYPE_NAMES,function(event){switch(event.typeName){case INPUT_TYPE.MOUSE_DOWN:if(causedFrame(event)){var pe=new ProtoExpectation(ProtoExpectation.RESPONSE_TYPE,MOUSE_IR_NAME);pe.pushEvent(event);protoExpectations.push(pe);}else{mouseDownEvent=event;}
-break;case INPUT_TYPE.MOUSE_MOVE:if(!causedFrame(event)){var pe=new ProtoExpectation(ProtoExpectation.IGNORED_TYPE);pe.pushEvent(event);protoExpectations.push(pe);}else if(!currentPE||!currentPE.isNear(event,MOUSE_MOVE_THRESHOLD_MS)){currentPE=new ProtoExpectation(ProtoExpectation.RESPONSE_TYPE,MOUSE_IR_NAME);currentPE.pushEvent(event);if(mouseDownEvent){currentPE.associatedEvents.push(mouseDownEvent);mouseDownEvent=undefined;}
-protoExpectations.push(currentPE);}else{if(currentPE.irType===ProtoExpectation.ANIMATION_TYPE){currentPE.pushEvent(event);}else{currentPE=new ProtoExpectation(ProtoExpectation.ANIMATION_TYPE,MOUSE_IR_NAME);currentPE.pushEvent(event);protoExpectations.push(currentPE);}}
-break;case INPUT_TYPE.MOUSE_UP:if(!mouseDownEvent){var pe=new ProtoExpectation(causedFrame(event)?ProtoExpectation.RESPONSE_TYPE:ProtoExpectation.IGNORED_TYPE,MOUSE_IR_NAME);pe.pushEvent(event);protoExpectations.push(pe);break;}
-if(currentPE){currentPE.pushEvent(event);}else{currentPE=new ProtoExpectation(ProtoExpectation.RESPONSE_TYPE,MOUSE_IR_NAME);if(mouseDownEvent)
-currentPE.associatedEvents.push(mouseDownEvent);currentPE.pushEvent(event);protoExpectations.push(currentPE);}
+currentPE=new ProtoExpectation(ProtoExpectation.RESPONSE_TYPE,INITIATOR_TYPE.MOUSE_WHEEL);currentPE.pushEvent(event);protoExpectations.push(currentPE);});return protoExpectations;}
+function handleMouseDragEvents(modelHelper,sortedInputEvents){var protoExpectations=[];var currentPE=undefined;var mouseDownEvent=undefined;forEventTypesIn(sortedInputEvents,MOUSE_DRAG_TYPE_NAMES,function(event){switch(event.typeName){case INPUT_TYPE.MOUSE_DOWN:if(causedFrame(event)){var pe=new ProtoExpectation(ProtoExpectation.RESPONSE_TYPE,INITIATOR_TYPE.MOUSE);pe.pushEvent(event);protoExpectations.push(pe);}else{mouseDownEvent=event;}
+break;case INPUT_TYPE.MOUSE_MOVE:if(!causedFrame(event)){var pe=new ProtoExpectation(ProtoExpectation.IGNORED_TYPE);pe.pushEvent(event);protoExpectations.push(pe);}else if(!currentPE||!currentPE.isNear(event,MOUSE_MOVE_THRESHOLD_MS)){currentPE=new ProtoExpectation(ProtoExpectation.RESPONSE_TYPE,INITIATOR_TYPE.MOUSE);currentPE.pushEvent(event);if(mouseDownEvent){currentPE.associatedEvents.push(mouseDownEvent);mouseDownEvent=undefined;}
+protoExpectations.push(currentPE);}else{if(currentPE.type===ProtoExpectation.ANIMATION_TYPE){currentPE.pushEvent(event);}else{currentPE=new ProtoExpectation(ProtoExpectation.ANIMATION_TYPE,INITIATOR_TYPE.MOUSE);currentPE.pushEvent(event);protoExpectations.push(currentPE);}}
+break;case INPUT_TYPE.MOUSE_UP:if(!mouseDownEvent){var pe=new ProtoExpectation(causedFrame(event)?ProtoExpectation.RESPONSE_TYPE:ProtoExpectation.IGNORED_TYPE,INITIATOR_TYPE.MOUSE);pe.pushEvent(event);protoExpectations.push(pe);break;}
+if(currentPE){currentPE.pushEvent(event);}else{currentPE=new ProtoExpectation(ProtoExpectation.RESPONSE_TYPE,INITIATOR_TYPE.MOUSE);if(mouseDownEvent){currentPE.associatedEvents.push(mouseDownEvent);}
+currentPE.pushEvent(event);protoExpectations.push(currentPE);}
 mouseDownEvent=undefined;currentPE=undefined;break;}});if(mouseDownEvent){currentPE=new ProtoExpectation(ProtoExpectation.IGNORED_TYPE);currentPE.pushEvent(mouseDownEvent);protoExpectations.push(currentPE);}
 return protoExpectations;}
-function handleTapResponseEvents(modelHelper,sortedInputEvents){var protoExpectations=[];var currentPE=undefined;forEventTypesIn(sortedInputEvents,TAP_TYPE_NAMES,function(event){switch(event.typeName){case INPUT_TYPE.TAP_DOWN:currentPE=new ProtoExpectation(ProtoExpectation.RESPONSE_TYPE,TAP_IR_NAME);currentPE.pushEvent(event);protoExpectations.push(currentPE);break;case INPUT_TYPE.TAP:if(currentPE){currentPE.pushEvent(event);}else{currentPE=new ProtoExpectation(ProtoExpectation.RESPONSE_TYPE,TAP_IR_NAME);currentPE.pushEvent(event);protoExpectations.push(currentPE);}
+function handleTapResponseEvents(modelHelper,sortedInputEvents){var protoExpectations=[];var currentPE=undefined;forEventTypesIn(sortedInputEvents,TAP_TYPE_NAMES,function(event){switch(event.typeName){case INPUT_TYPE.TAP_DOWN:currentPE=new ProtoExpectation(ProtoExpectation.RESPONSE_TYPE,INITIATOR_TYPE.TAP);currentPE.pushEvent(event);protoExpectations.push(currentPE);break;case INPUT_TYPE.TAP:if(currentPE){currentPE.pushEvent(event);}else{currentPE=new ProtoExpectation(ProtoExpectation.RESPONSE_TYPE,INITIATOR_TYPE.TAP);currentPE.pushEvent(event);protoExpectations.push(currentPE);}
 currentPE=undefined;break;case INPUT_TYPE.TAP_CANCEL:if(!currentPE){var pe=new ProtoExpectation(ProtoExpectation.IGNORED_TYPE);pe.pushEvent(event);protoExpectations.push(pe);break;}
-if(currentPE.isNear(event,INPUT_MERGE_THRESHOLD_MS)){currentPE.pushEvent(event);}else{currentPE=new ProtoExpectation(ProtoExpectation.RESPONSE_TYPE,TAP_IR_NAME);currentPE.pushEvent(event);protoExpectations.push(currentPE);}
+if(currentPE.isNear(event,INPUT_MERGE_THRESHOLD_MS)){currentPE.pushEvent(event);}else{currentPE=new ProtoExpectation(ProtoExpectation.RESPONSE_TYPE,INITIATOR_TYPE.TAP);currentPE.pushEvent(event);protoExpectations.push(currentPE);}
 currentPE=undefined;break;}});return protoExpectations;}
 function handlePinchEvents(modelHelper,sortedInputEvents){var protoExpectations=[];var currentPE=undefined;var sawFirstUpdate=false;var modelBounds=modelHelper.model.bounds;forEventTypesIn(sortedInputEvents,PINCH_TYPE_NAMES,function(event){switch(event.typeName){case INPUT_TYPE.PINCH_BEGIN:if(currentPE&&currentPE.isNear(event,INPUT_MERGE_THRESHOLD_MS)){currentPE.pushEvent(event);break;}
-currentPE=new ProtoExpectation(ProtoExpectation.RESPONSE_TYPE,PINCH_IR_NAME);currentPE.pushEvent(event);currentPE.isAnimationBegin=true;protoExpectations.push(currentPE);sawFirstUpdate=false;break;case INPUT_TYPE.PINCH_UPDATE:if(!currentPE||((currentPE.irType===ProtoExpectation.RESPONSE_TYPE)&&sawFirstUpdate)||!currentPE.isNear(event,INPUT_MERGE_THRESHOLD_MS)){currentPE=new ProtoExpectation(ProtoExpectation.ANIMATION_TYPE,PINCH_IR_NAME);currentPE.pushEvent(event);protoExpectations.push(currentPE);}else{currentPE.pushEvent(event);sawFirstUpdate=true;}
+currentPE=new ProtoExpectation(ProtoExpectation.RESPONSE_TYPE,INITIATOR_TYPE.PINCH);currentPE.pushEvent(event);currentPE.isAnimationBegin=true;protoExpectations.push(currentPE);sawFirstUpdate=false;break;case INPUT_TYPE.PINCH_UPDATE:if(!currentPE||((currentPE.type===ProtoExpectation.RESPONSE_TYPE)&&sawFirstUpdate)||!currentPE.isNear(event,INPUT_MERGE_THRESHOLD_MS)){currentPE=new ProtoExpectation(ProtoExpectation.ANIMATION_TYPE,INITIATOR_TYPE.PINCH);currentPE.pushEvent(event);protoExpectations.push(currentPE);}else{currentPE.pushEvent(event);sawFirstUpdate=true;}
 break;case INPUT_TYPE.PINCH_END:if(currentPE){currentPE.pushEvent(event);}else{var pe=new ProtoExpectation(ProtoExpectation.IGNORED_TYPE);pe.pushEvent(event);protoExpectations.push(pe);}
 currentPE=undefined;break;}});return protoExpectations;}
 function handleFlingEvents(modelHelper,sortedInputEvents){var protoExpectations=[];var currentPE=undefined;function isRendererFling(event){return event.title===RENDERER_FLING_TITLE;}
-var browserHelper=modelHelper.browserHelper;var flingEvents=browserHelper.getAllAsyncSlicesMatching(isRendererFling);forEventTypesIn(sortedInputEvents,FLING_TYPE_NAMES,function(event){flingEvents.push(event);});flingEvents.sort(compareEvents);flingEvents.forEach(function(event){if(event.title===RENDERER_FLING_TITLE){if(currentPE){currentPE.pushEvent(event);}else{currentPE=new ProtoExpectation(ProtoExpectation.ANIMATION_TYPE,FLING_IR_NAME);currentPE.pushEvent(event);protoExpectations.push(currentPE);}
+var browserHelper=modelHelper.browserHelper;var flingEvents=browserHelper.getAllAsyncSlicesMatching(isRendererFling);forEventTypesIn(sortedInputEvents,FLING_TYPE_NAMES,function(event){flingEvents.push(event);});flingEvents.sort(compareEvents);flingEvents.forEach(function(event){if(event.title===RENDERER_FLING_TITLE){if(currentPE){currentPE.pushEvent(event);}else{currentPE=new ProtoExpectation(ProtoExpectation.ANIMATION_TYPE,INITIATOR_TYPE.FLING);currentPE.pushEvent(event);protoExpectations.push(currentPE);}
 return;}
-switch(event.typeName){case INPUT_TYPE.FLING_START:if(currentPE){console.error('Another FlingStart? File a bug with this trace!');currentPE.pushEvent(event);}else{currentPE=new ProtoExpectation(ProtoExpectation.ANIMATION_TYPE,FLING_IR_NAME);currentPE.pushEvent(event);currentPE.end=0;protoExpectations.push(currentPE);}
+switch(event.typeName){case INPUT_TYPE.FLING_START:if(currentPE){modelHelper.model.importWarning({type:'UserModelBuilder',message:'Please file a bug with this trace: FlingStart',showToUser:true});currentPE.pushEvent(event);}else{currentPE=new ProtoExpectation(ProtoExpectation.ANIMATION_TYPE,INITIATOR_TYPE.FLING);currentPE.pushEvent(event);currentPE.end=0;protoExpectations.push(currentPE);}
 break;case INPUT_TYPE.FLING_CANCEL:if(currentPE){currentPE.pushEvent(event);currentPE.end=event.start;currentPE=undefined;}else{var pe=new ProtoExpectation(ProtoExpectation.IGNORED_TYPE);pe.pushEvent(event);protoExpectations.push(pe);}
-break;}});if(currentPE&&!currentPE.end)
-currentPE.end=modelHelper.model.bounds.max;return protoExpectations;}
-function handleTouchEvents(modelHelper,sortedInputEvents){var protoExpectations=[];var currentPE=undefined;var sawFirstMove=false;forEventTypesIn(sortedInputEvents,TOUCH_TYPE_NAMES,function(event){switch(event.typeName){case INPUT_TYPE.TOUCH_START:if(currentPE){currentPE.pushEvent(event);}else{currentPE=new ProtoExpectation(ProtoExpectation.RESPONSE_TYPE,TOUCH_IR_NAME);currentPE.pushEvent(event);currentPE.isAnimationBegin=true;protoExpectations.push(currentPE);sawFirstMove=false;}
-break;case INPUT_TYPE.TOUCH_MOVE:if(!currentPE){currentPE=new ProtoExpectation(ProtoExpectation.ANIMATION_TYPE,TOUCH_IR_NAME);currentPE.pushEvent(event);protoExpectations.push(currentPE);break;}
-if((sawFirstMove&&(currentPE.irType===ProtoExpectation.RESPONSE_TYPE))||!currentPE.isNear(event,INPUT_MERGE_THRESHOLD_MS)){var prevEnd=currentPE.end;currentPE=new ProtoExpectation(ProtoExpectation.ANIMATION_TYPE,TOUCH_IR_NAME);currentPE.pushEvent(event);currentPE.start=prevEnd;protoExpectations.push(currentPE);}else{currentPE.pushEvent(event);sawFirstMove=true;}
+break;}});if(currentPE&&!currentPE.end){currentPE.end=modelHelper.model.bounds.max;}
+return protoExpectations;}
+function handleTouchEvents(modelHelper,sortedInputEvents){var protoExpectations=[];var currentPE=undefined;var sawFirstMove=false;forEventTypesIn(sortedInputEvents,TOUCH_TYPE_NAMES,function(event){switch(event.typeName){case INPUT_TYPE.TOUCH_START:if(currentPE){currentPE.pushEvent(event);}else{currentPE=new ProtoExpectation(ProtoExpectation.RESPONSE_TYPE,INITIATOR_TYPE.TOUCH);currentPE.pushEvent(event);currentPE.isAnimationBegin=true;protoExpectations.push(currentPE);sawFirstMove=false;}
+break;case INPUT_TYPE.TOUCH_MOVE:if(!currentPE){currentPE=new ProtoExpectation(ProtoExpectation.ANIMATION_TYPE,INITIATOR_TYPE.TOUCH);currentPE.pushEvent(event);protoExpectations.push(currentPE);break;}
+if((sawFirstMove&&(currentPE.type===ProtoExpectation.RESPONSE_TYPE))||!currentPE.isNear(event,INPUT_MERGE_THRESHOLD_MS)){var prevEnd=currentPE.end;currentPE=new ProtoExpectation(ProtoExpectation.ANIMATION_TYPE,INITIATOR_TYPE.TOUCH);currentPE.pushEvent(event);currentPE.start=prevEnd;protoExpectations.push(currentPE);}else{currentPE.pushEvent(event);sawFirstMove=true;}
 break;case INPUT_TYPE.TOUCH_END:if(!currentPE){var pe=new ProtoExpectation(ProtoExpectation.IGNORED_TYPE);pe.pushEvent(event);protoExpectations.push(pe);break;}
 if(currentPE.isNear(event,INPUT_MERGE_THRESHOLD_MS)){currentPE.pushEvent(event);}else{var pe=new ProtoExpectation(ProtoExpectation.IGNORED_TYPE);pe.pushEvent(event);protoExpectations.push(pe);}
 currentPE=undefined;break;}});return protoExpectations;}
-function handleScrollEvents(modelHelper,sortedInputEvents){var protoExpectations=[];var currentPE=undefined;var sawFirstUpdate=false;forEventTypesIn(sortedInputEvents,SCROLL_TYPE_NAMES,function(event){switch(event.typeName){case INPUT_TYPE.SCROLL_BEGIN:currentPE=new ProtoExpectation(ProtoExpectation.RESPONSE_TYPE,SCROLL_IR_NAME);currentPE.pushEvent(event);currentPE.isAnimationBegin=true;protoExpectations.push(currentPE);sawFirstUpdate=false;break;case INPUT_TYPE.SCROLL_UPDATE:if(currentPE){if(currentPE.isNear(event,INPUT_MERGE_THRESHOLD_MS)&&((currentPE.irType===ProtoExpectation.ANIMATION_TYPE)||!sawFirstUpdate)){currentPE.pushEvent(event);sawFirstUpdate=true;}else{currentPE=new ProtoExpectation(ProtoExpectation.ANIMATION_TYPE,SCROLL_IR_NAME);currentPE.pushEvent(event);protoExpectations.push(currentPE);}}else{currentPE=new ProtoExpectation(ProtoExpectation.ANIMATION_TYPE,SCROLL_IR_NAME);currentPE.pushEvent(event);protoExpectations.push(currentPE);}
-break;case INPUT_TYPE.SCROLL_END:if(!currentPE){console.error('ScrollEnd without ScrollUpdate? '+'File a bug with this trace!');var pe=new ProtoExpectation(ProtoExpectation.IGNORED_TYPE);pe.pushEvent(event);protoExpectations.push(pe);break;}
+function handleScrollEvents(modelHelper,sortedInputEvents){var protoExpectations=[];var currentPE=undefined;var sawFirstUpdate=false;forEventTypesIn(sortedInputEvents,SCROLL_TYPE_NAMES,function(event){switch(event.typeName){case INPUT_TYPE.SCROLL_BEGIN:currentPE=new ProtoExpectation(ProtoExpectation.RESPONSE_TYPE,INITIATOR_TYPE.SCROLL);currentPE.pushEvent(event);currentPE.isAnimationBegin=true;protoExpectations.push(currentPE);sawFirstUpdate=false;break;case INPUT_TYPE.SCROLL_UPDATE:if(currentPE){if(currentPE.isNear(event,INPUT_MERGE_THRESHOLD_MS)&&((currentPE.type===ProtoExpectation.ANIMATION_TYPE)||!sawFirstUpdate)){currentPE.pushEvent(event);sawFirstUpdate=true;}else{currentPE=new ProtoExpectation(ProtoExpectation.ANIMATION_TYPE,INITIATOR_TYPE.SCROLL);currentPE.pushEvent(event);protoExpectations.push(currentPE);}}else{currentPE=new ProtoExpectation(ProtoExpectation.ANIMATION_TYPE,INITIATOR_TYPE.SCROLL);currentPE.pushEvent(event);protoExpectations.push(currentPE);}
+break;case INPUT_TYPE.SCROLL_END:if(!currentPE){modelHelper.model.importWarning({type:'UserModelBuilder',message:'Please file a bug with this trace: ScrollEnd',showToUser:true});var pe=new ProtoExpectation(ProtoExpectation.IGNORED_TYPE);pe.pushEvent(event);protoExpectations.push(pe);break;}
 currentPE.pushEvent(event);break;}});return protoExpectations;}
-function handleVideoAnimations(modelHelper,sortedInputEvents){var events=[];for(var pid in modelHelper.rendererHelpers){for(var asyncSlice of
-modelHelper.rendererHelpers[pid].mainThread.asyncSliceGroup.slices){if(asyncSlice.title===PLAYBACK_EVENT_TITLE)
-events.push(asyncSlice);}}
-events.sort(tr.importer.compareEvents);var protoExpectations=[];for(var event of events){var currentPE=new ProtoExpectation(ProtoExpectation.ANIMATION_TYPE,VIDEO_IR_NAME);currentPE.start=event.start;currentPE.end=event.end;currentPE.pushEvent(event);protoExpectations.push(currentPE);}
+function handleVideoAnimations(modelHelper,sortedInputEvents){var events=[];for(var pid in modelHelper.rendererHelpers){for(var tid in modelHelper.rendererHelpers[pid].process.threads){for(var asyncSlice of
+modelHelper.rendererHelpers[pid].process.threads[tid].asyncSliceGroup.slices){if(asyncSlice.title===PLAYBACK_EVENT_TITLE){events.push(asyncSlice);}}}}
+events.sort(tr.importer.compareEvents);var protoExpectations=[];for(var event of events){var currentPE=new ProtoExpectation(ProtoExpectation.ANIMATION_TYPE,INITIATOR_TYPE.VIDEO);currentPE.start=event.start;currentPE.end=event.end;currentPE.pushEvent(event);protoExpectations.push(currentPE);}
 return protoExpectations;}
-function handleCSSAnimations(modelHelper,sortedInputEvents){var animationEvents=modelHelper.browserHelper.getAllAsyncSlicesMatching(function(event){return((event.title===CSS_ANIMATION_TITLE)&&event.isTopLevel&&(event.duration>0));});var animationRanges=[];function pushAnimationRange(start,end,animation){var range=tr.b.Range.fromExplicitRange(start,end);range.animation=animation;animationRanges.push(range);}
+function handleCSSAnimations(modelHelper,sortedInputEvents){var animationEvents=modelHelper.browserHelper.getAllAsyncSlicesMatching(function(event){return((event.title===CSS_ANIMATION_TITLE)&&event.isTopLevel&&(event.duration>0));});var animationRanges=[];function pushAnimationRange(start,end,animation){var range=tr.b.math.Range.fromExplicitRange(start,end);range.animation=animation;animationRanges.push(range);}
 animationEvents.forEach(function(animation){if(animation.subSlices.length===0){pushAnimationRange(animation.start,animation.end,animation);}else{var start=undefined;animation.subSlices.forEach(function(sub){if((sub.args.data.state==='running')&&(start===undefined)){start=sub.start;}else if((sub.args.data.state==='paused')||(sub.args.data.state==='idle')||(sub.args.data.state==='finished')){if(start===undefined){start=modelHelper.model.bounds.min;}
-pushAnimationRange(start,sub.start,animation);start=undefined;}});if(start!==undefined)
-pushAnimationRange(start,animation.end,animation);}});return animationRanges.map(function(range){var protoExpectation=new ProtoExpectation(ProtoExpectation.ANIMATION_TYPE,CSS_IR_NAME);protoExpectation.start=range.min;protoExpectation.end=range.max;protoExpectation.associatedEvents.push(range.animation);return protoExpectation;});}
-function findWebGLEvents(modelHelper,mailboxEvents,animationEvents){for(var event of modelHelper.model.getDescendantEvents()){if(event.title==='DrawingBuffer::prepareMailbox')
-mailboxEvents.push(event);else if(event.title==='PageAnimator::serviceScriptedAnimations')
-animationEvents.push(event);}}
-function findMailboxEventsNearAnimationEvents(mailboxEvents,animationEvents){if(animationEvents.length===0)
-return[];mailboxEvents.sort(compareEvents);animationEvents.sort(compareEvents);var animationIterator=animationEvents[Symbol.iterator]();var animationEvent=animationIterator.next().value;var filteredEvents=[];for(var event of mailboxEvents){while(animationEvent&&(animationEvent.start<(event.start-ANIMATION_MERGE_THRESHOLD_MS)))
-animationEvent=animationIterator.next().value;if(!animationEvent)
-break;if(animationEvent.start<(event.start+ANIMATION_MERGE_THRESHOLD_MS))
-filteredEvents.push(event);}
+pushAnimationRange(start,sub.start,animation);start=undefined;}});if(start!==undefined){pushAnimationRange(start,animation.end,animation);}}});return animationRanges.map(function(range){var protoExpectation=new ProtoExpectation(ProtoExpectation.ANIMATION_TYPE,INITIATOR_TYPE.CSS);protoExpectation.start=range.min;protoExpectation.end=range.max;protoExpectation.associatedEvents.push(range.animation);return protoExpectation;});}
+function findWebGLEvents(modelHelper,mailboxEvents,animationEvents){for(var event of modelHelper.model.getDescendantEvents()){if(event.title==='DrawingBuffer::prepareMailbox'){mailboxEvents.push(event);}else if(event.title==='PageAnimator::serviceScriptedAnimations'){animationEvents.push(event);}}}
+function findMailboxEventsNearAnimationEvents(mailboxEvents,animationEvents){if(animationEvents.length===0)return[];mailboxEvents.sort(compareEvents);animationEvents.sort(compareEvents);var animationIterator=animationEvents[Symbol.iterator]();var animationEvent=animationIterator.next().value;var filteredEvents=[];for(var event of mailboxEvents){while(animationEvent&&(animationEvent.start<(event.start-ANIMATION_MERGE_THRESHOLD_MS))){animationEvent=animationIterator.next().value;}
+if(!animationEvent)break;if(animationEvent.start<(event.start+ANIMATION_MERGE_THRESHOLD_MS)){filteredEvents.push(event);}}
 return filteredEvents;}
-function createProtoExpectationsFromMailboxEvents(mailboxEvents){var protoExpectations=[];var currentPE=undefined;for(var event of mailboxEvents){if(currentPE===undefined||!currentPE.isNear(event,ANIMATION_MERGE_THRESHOLD_MS)){currentPE=new ProtoExpectation(ProtoExpectation.ANIMATION_TYPE,WEBGL_IR_NAME);currentPE.pushEvent(event);protoExpectations.push(currentPE);}
-else{currentPE.pushEvent(event);}}
+function createProtoExpectationsFromMailboxEvents(mailboxEvents){var protoExpectations=[];var currentPE=undefined;for(var event of mailboxEvents){if(currentPE===undefined||!currentPE.isNear(event,ANIMATION_MERGE_THRESHOLD_MS)){currentPE=new ProtoExpectation(ProtoExpectation.ANIMATION_TYPE,INITIATOR_TYPE.WEBGL);currentPE.pushEvent(event);protoExpectations.push(currentPE);}else{currentPE.pushEvent(event);}}
 return protoExpectations;}
 function handleWebGLAnimations(modelHelper,sortedInputEvents){var prepareMailboxEvents=[];var scriptedAnimationEvents=[];findWebGLEvents(modelHelper,prepareMailboxEvents,scriptedAnimationEvents);var webGLMailboxEvents=findMailboxEventsNearAnimationEvents(prepareMailboxEvents,scriptedAnimationEvents);return createProtoExpectationsFromMailboxEvents(webGLMailboxEvents);}
 function postProcessProtoExpectations(modelHelper,protoExpectations){protoExpectations=findFrameEventsForAnimations(modelHelper,protoExpectations);protoExpectations=mergeIntersectingResponses(protoExpectations);protoExpectations=mergeIntersectingAnimations(protoExpectations);protoExpectations=fixResponseAnimationStarts(protoExpectations);protoExpectations=fixTapResponseTouchAnimations(protoExpectations);return protoExpectations;}
-function mergeIntersectingResponses(protoExpectations){var newPEs=[];while(protoExpectations.length){var pe=protoExpectations.shift();newPEs.push(pe);if(pe.irType!==ProtoExpectation.RESPONSE_TYPE)
-continue;for(var i=0;i<protoExpectations.length;++i){var otherPE=protoExpectations[i];if(otherPE.irType!==pe.irType)
-continue;if(!otherPE.intersects(pe))
-continue;var typeNames=pe.associatedEvents.map(function(event){return event.typeName;});if(otherPE.containsTypeNames(typeNames))
-continue;pe.merge(otherPE);protoExpectations.splice(i,1);--i;}}
+function mergeIntersectingResponses(protoExpectations){var newPEs=[];while(protoExpectations.length){var pe=protoExpectations.shift();newPEs.push(pe);if(pe.type!==ProtoExpectation.RESPONSE_TYPE)continue;for(var i=0;i<protoExpectations.length;++i){var otherPE=protoExpectations[i];if(otherPE.type!==pe.type)continue;if(!otherPE.intersects(pe))continue;var typeNames=pe.associatedEvents.map(function(event){return event.typeName;});if(otherPE.containsTypeNames(typeNames))continue;pe.merge(otherPE);protoExpectations.splice(i,1);--i;}}
 return newPEs;}
-function mergeIntersectingAnimations(protoExpectations){var newPEs=[];while(protoExpectations.length){var pe=protoExpectations.shift();newPEs.push(pe);if(pe.irType!==ProtoExpectation.ANIMATION_TYPE)
-continue;var isCSS=pe.containsSliceTitle(CSS_ANIMATION_TITLE);var isFling=pe.containsTypeNames([INPUT_TYPE.FLING_START]);var isVideo=pe.containsTypeNames([VIDEO_IR_NAME]);for(var i=0;i<protoExpectations.length;++i){var otherPE=protoExpectations[i];if(otherPE.irType!==pe.irType)
-continue;if(isCSS!=otherPE.containsSliceTitle(CSS_ANIMATION_TITLE))
-continue;if(isCSS){if(!pe.isNear(otherPE,ANIMATION_MERGE_THRESHOLD_MS))
-continue;}else if(!otherPE.intersects(pe)){continue;}
-if(isFling!==otherPE.containsTypeNames([INPUT_TYPE.FLING_START]))
-continue;if(isVideo!==otherPE.containsTypeNames([VIDEO_IR_NAME]))
-continue;pe.merge(otherPE);protoExpectations.splice(i,1);--i;}}
+function mergeIntersectingAnimations(protoExpectations){var newPEs=[];while(protoExpectations.length){var pe=protoExpectations.shift();newPEs.push(pe);if(pe.type!==ProtoExpectation.ANIMATION_TYPE)continue;var isCSS=pe.initiatorType===INITIATOR_TYPE.CSS;var isFling=pe.containsTypeNames([INPUT_TYPE.FLING_START]);var isVideo=pe.initiatorType===INITIATOR_TYPE.VIDEO;for(var i=0;i<protoExpectations.length;++i){var otherPE=protoExpectations[i];if(otherPE.type!==pe.type)continue;if((isCSS&&otherPE.initiatorType!==INITIATOR_TYPE.CSS)||isFling!==otherPE.containsTypeNames([INPUT_TYPE.FLING_START])||isVideo&&otherPE.initiatorType!==INITIATOR_TYPE.VIDEO){continue;}
+if(isCSS){if(!pe.isNear(otherPE,ANIMATION_MERGE_THRESHOLD_MS)){continue;}}else if(!otherPE.intersects(pe)){continue;}
+pe.merge(otherPE);protoExpectations.splice(i,1);--i;}}
 return newPEs;}
-function fixResponseAnimationStarts(protoExpectations){protoExpectations.forEach(function(ape){if(ape.irType!==ProtoExpectation.ANIMATION_TYPE)
-return;protoExpectations.forEach(function(rpe){if(rpe.irType!==ProtoExpectation.RESPONSE_TYPE)
-return;if(!ape.containsTimestampInclusive(rpe.end))
-return;if(ape.containsTimestampInclusive(rpe.start))
-return;ape.start=rpe.end;});});return protoExpectations;}
-function fixTapResponseTouchAnimations(protoExpectations){function isTapResponse(pe){return(pe.irType===ProtoExpectation.RESPONSE_TYPE)&&pe.containsTypeNames([INPUT_TYPE.TAP]);}
-function isTouchAnimation(pe){return(pe.irType===ProtoExpectation.ANIMATION_TYPE)&&pe.containsTypeNames([INPUT_TYPE.TOUCH_MOVE])&&!pe.containsTypeNames([INPUT_TYPE.SCROLL_UPDATE,INPUT_TYPE.PINCH_UPDATE]);}
-var newPEs=[];while(protoExpectations.length){var pe=protoExpectations.shift();newPEs.push(pe);var peIsTapResponse=isTapResponse(pe);var peIsTouchAnimation=isTouchAnimation(pe);if(!peIsTapResponse&&!peIsTouchAnimation)
-continue;for(var i=0;i<protoExpectations.length;++i){var otherPE=protoExpectations[i];if(!otherPE.intersects(pe))
-continue;if(peIsTapResponse&&!isTouchAnimation(otherPE))
-continue;if(peIsTouchAnimation&&!isTapResponse(otherPE))
-continue;pe.irType=ProtoExpectation.RESPONSE_TYPE;pe.merge(otherPE);protoExpectations.splice(i,1);--i;}}
+function fixResponseAnimationStarts(protoExpectations){protoExpectations.forEach(function(ape){if(ape.type!==ProtoExpectation.ANIMATION_TYPE){return;}
+protoExpectations.forEach(function(rpe){if(rpe.type!==ProtoExpectation.RESPONSE_TYPE){return;}
+if(!ape.containsTimestampInclusive(rpe.end)){return;}
+if(ape.containsTimestampInclusive(rpe.start)){return;}
+ape.start=rpe.end;});});return protoExpectations;}
+function fixTapResponseTouchAnimations(protoExpectations){function isTapResponse(pe){return(pe.type===ProtoExpectation.RESPONSE_TYPE)&&pe.containsTypeNames([INPUT_TYPE.TAP]);}
+function isTouchAnimation(pe){return(pe.type===ProtoExpectation.ANIMATION_TYPE)&&pe.containsTypeNames([INPUT_TYPE.TOUCH_MOVE])&&!pe.containsTypeNames([INPUT_TYPE.SCROLL_UPDATE,INPUT_TYPE.PINCH_UPDATE]);}
+var newPEs=[];while(protoExpectations.length){var pe=protoExpectations.shift();newPEs.push(pe);var peIsTapResponse=isTapResponse(pe);var peIsTouchAnimation=isTouchAnimation(pe);if(!peIsTapResponse&&!peIsTouchAnimation){continue;}
+for(var i=0;i<protoExpectations.length;++i){var otherPE=protoExpectations[i];if(!otherPE.intersects(pe))continue;if(peIsTapResponse&&!isTouchAnimation(otherPE))continue;if(peIsTouchAnimation&&!isTapResponse(otherPE))continue;pe.type=ProtoExpectation.RESPONSE_TYPE;pe.merge(otherPE);protoExpectations.splice(i,1);--i;}}
 return newPEs;}
-function findFrameEventsForAnimations(modelHelper,protoExpectations){var newPEs=[];var frameEventsByPid=getSortedFrameEventsByProcess(modelHelper);for(var pe of protoExpectations){if(pe.irType!==ProtoExpectation.ANIMATION_TYPE){newPEs.push(pe);continue;}
-var frameEvents=[];for(var pid of Object.keys(modelHelper.rendererHelpers)){var range=tr.b.Range.fromExplicitRange(pe.start,pe.end);frameEvents.push.apply(frameEvents,range.filterArray(frameEventsByPid[pid],e=>e.start));}
-if(frameEvents.length===0&&!pe.names.has(WEBGL_IR_NAME)){pe.irType=ProtoExpectation.IGNORED_TYPE;newPEs.push(pe);continue;}
+function findFrameEventsForAnimations(modelHelper,protoExpectations){var newPEs=[];var frameEventsByPid=getSortedFrameEventsByProcess(modelHelper);for(var pe of protoExpectations){if(pe.type!==ProtoExpectation.ANIMATION_TYPE){newPEs.push(pe);continue;}
+var frameEvents=[];for(var pid of Object.keys(modelHelper.rendererHelpers)){var range=tr.b.math.Range.fromExplicitRange(pe.start,pe.end);frameEvents.push.apply(frameEvents,range.filterArray(frameEventsByPid[pid],e=>e.start));}
+if(frameEvents.length===0&&!(pe.initiatorType===INITIATOR_TYPE.WEBGL)){pe.type=ProtoExpectation.IGNORED_TYPE;newPEs.push(pe);continue;}
 pe.associatedEvents.addEventSet(frameEvents);newPEs.push(pe);}
 return newPEs;}
-function checkAllInputEventsHandled(sortedInputEvents,protoExpectations){var handledEvents=[];protoExpectations.forEach(function(protoExpectation){protoExpectation.associatedEvents.forEach(function(event){if((event.title===CSS_ANIMATION_TITLE)&&(event.subSlices.length>0))
-return;if((handledEvents.indexOf(event)>=0)&&(event.title!==tr.model.helpers.IMPL_RENDERING_STATS)){console.error('double-handled event',event.typeName,parseInt(event.start),parseInt(event.end),protoExpectation);return;}
-handledEvents.push(event);});});sortedInputEvents.forEach(function(event){if(handledEvents.indexOf(event)<0){console.error('UNHANDLED INPUT EVENT!',event.typeName,parseInt(event.start),parseInt(event.end));}});}
-function findInputExpectations(modelHelper){var sortedInputEvents=getSortedInputEvents(modelHelper);var protoExpectations=findProtoExpectations(modelHelper,sortedInputEvents);protoExpectations=postProcessProtoExpectations(modelHelper,protoExpectations);checkAllInputEventsHandled(sortedInputEvents,protoExpectations);var irs=[];protoExpectations.forEach(function(protoExpectation){var ir=protoExpectation.createInteractionRecord(modelHelper.model);if(ir)
-irs.push(ir);});return irs;}
-return{findInputExpectations:findInputExpectations,compareEvents:compareEvents,CSS_ANIMATION_TITLE:CSS_ANIMATION_TITLE};});'use strict';tr.exportTo('tr.model.um',function(){var LOAD_SUBTYPE_NAMES={SUCCESSFUL:'Successful',FAILED:'Failed',};var DOES_LOAD_SUBTYPE_NAME_EXIST={};for(var key in LOAD_SUBTYPE_NAMES){DOES_LOAD_SUBTYPE_NAME_EXIST[LOAD_SUBTYPE_NAMES[key]]=true;;}
-function LoadExpectation(parentModel,initiatorTitle,start,duration){if(!DOES_LOAD_SUBTYPE_NAME_EXIST[initiatorTitle])
-throw new Error(initiatorTitle+' is not in LOAD_SUBTYPE_NAMES');tr.model.um.UserExpectation.call(this,parentModel,initiatorTitle,start,duration);this.renderProcess=undefined;this.renderMainThread=undefined;this.routingId=undefined;this.parentRoutingId=undefined;this.loadFinishedEvent=undefined;}
-LoadExpectation.prototype={__proto__:tr.model.um.UserExpectation.prototype,constructor:LoadExpectation};tr.model.um.UserExpectation.subTypes.register(LoadExpectation,{stageTitle:'Load',colorId:tr.b.ColorScheme.getColorIdForReservedName('rail_load')});return{LOAD_SUBTYPE_NAMES:LOAD_SUBTYPE_NAMES,LoadExpectation:LoadExpectation};});'use strict';tr.exportTo('tr.importer',function(){var NAVIGATION_START='NavigationTiming navigationStart';var FIRST_CONTENTFUL_PAINT_TITLE='firstContentfulPaint';function findLoadExpectations(modelHelper){var events=[];for(var event of modelHelper.model.getDescendantEvents()){if((event.title===NAVIGATION_START)||(event.title===FIRST_CONTENTFUL_PAINT_TITLE))
-events.push(event);}
+function checkAllInputEventsHandled(modelHelper,sortedInputEvents,protoExpectations){var handledEvents=[];protoExpectations.forEach(function(protoExpectation){protoExpectation.associatedEvents.forEach(function(event){if((event.title===CSS_ANIMATION_TITLE)&&(event.subSlices.length>0)){return;}
+if((handledEvents.indexOf(event)>=0)&&(event.title!==tr.model.helpers.IMPL_RENDERING_STATS)){modelHelper.model.importWarning({type:'UserModelBuilder',message:'Please file a bug with this trace: '+`double-handled event:${event.typeName}@${event.start}`,showToUser:true});return;}
+handledEvents.push(event);});});sortedInputEvents.forEach(function(event){if(handledEvents.indexOf(event)<0){modelHelper.model.importWarning({type:'UserModelBuilder',message:'Please file a bug with this trace: '+`double-handled event:${event.typeName}@${event.start}`,showToUser:true});}});}
+function findInputExpectations(modelHelper){var sortedInputEvents=getSortedInputEvents(modelHelper);var protoExpectations=findProtoExpectations(modelHelper,sortedInputEvents);protoExpectations=postProcessProtoExpectations(modelHelper,protoExpectations);checkAllInputEventsHandled(modelHelper,sortedInputEvents,protoExpectations);var expectations=[];protoExpectations.forEach(function(protoExpectation){var ir=protoExpectation.createInteractionRecord(modelHelper.model);if(ir){expectations.push(ir);}});return expectations;}
+return{findInputExpectations,compareEvents,CSS_ANIMATION_TITLE,INITIATOR_TYPE};});'use strict';tr.exportTo('tr.model.um',function(){var LOAD_SUBTYPE_NAMES={SUCCESSFUL:'Successful',FAILED:'Failed',};var DOES_LOAD_SUBTYPE_NAME_EXIST={};for(var key in LOAD_SUBTYPE_NAMES){DOES_LOAD_SUBTYPE_NAME_EXIST[LOAD_SUBTYPE_NAMES[key]]=true;}
+function LoadExpectation(parentModel,initiatorTitle,start,duration){if(!DOES_LOAD_SUBTYPE_NAME_EXIST[initiatorTitle]){throw new Error(initiatorTitle+' is not in LOAD_SUBTYPE_NAMES');}
+tr.model.um.UserExpectation.call(this,parentModel,initiatorTitle,start,duration);this.renderProcess=undefined;this.renderMainThread=undefined;this.routingId=undefined;this.parentRoutingId=undefined;this.loadFinishedEvent=undefined;}
+LoadExpectation.prototype={__proto__:tr.model.um.UserExpectation.prototype,constructor:LoadExpectation};tr.model.um.UserExpectation.subTypes.register(LoadExpectation,{stageTitle:'Load',colorId:tr.b.ColorScheme.getColorIdForReservedName('rail_load')});return{LOAD_SUBTYPE_NAMES,LoadExpectation,};});'use strict';tr.exportTo('tr.importer',function(){var NAVIGATION_START='NavigationTiming navigationStart';var FIRST_CONTENTFUL_PAINT_TITLE='firstContentfulPaint';function findLoadExpectations(modelHelper){var events=[];for(var event of modelHelper.model.getDescendantEvents()){if((event.title===NAVIGATION_START)||(event.title===FIRST_CONTENTFUL_PAINT_TITLE)){events.push(event);}}
 events.sort(tr.importer.compareEvents);var loads=[];var startEvent=undefined;for(var event of events){if(event.title===NAVIGATION_START){startEvent=event;}else if(event.title===FIRST_CONTENTFUL_PAINT_TITLE){if(startEvent){loads.push(new tr.model.um.LoadExpectation(modelHelper.model,tr.model.um.LOAD_SUBTYPE_NAMES.SUCCESSFUL,startEvent.start,event.start-startEvent.start));startEvent=undefined;}}}
 if(startEvent){loads.push(new tr.model.um.LoadExpectation(modelHelper.model,tr.model.um.LOAD_SUBTYPE_NAMES.SUCCESSFUL,startEvent.start,modelHelper.model.bounds.max-startEvent.start));}
 return loads;}
-return{findLoadExpectations:findLoadExpectations};});'use strict';tr.exportTo('tr.model.um',function(){function StartupExpectation(parentModel,start,duration){tr.model.um.UserExpectation.call(this,parentModel,'',start,duration);}
-StartupExpectation.prototype={__proto__:tr.model.um.UserExpectation.prototype,constructor:StartupExpectation};tr.model.um.UserExpectation.subTypes.register(StartupExpectation,{stageTitle:'Startup',colorId:tr.b.ColorScheme.getColorIdForReservedName('startup')});return{StartupExpectation:StartupExpectation};});'use strict';tr.exportTo('tr.importer',function(){function getAllFrameEvents(modelHelper){var frameEvents=[];frameEvents.push.apply(frameEvents,modelHelper.browserHelper.getFrameEventsInRange(tr.model.helpers.IMPL_FRAMETIME_TYPE,modelHelper.model.bounds));tr.b.iterItems(modelHelper.rendererHelpers,function(pid,renderer){frameEvents.push.apply(frameEvents,renderer.getFrameEventsInRange(tr.model.helpers.IMPL_FRAMETIME_TYPE,modelHelper.model.bounds));});return frameEvents.sort(tr.importer.compareEvents);}
+return{findLoadExpectations,};});'use strict';tr.exportTo('tr.model.um',function(){function StartupExpectation(parentModel,start,duration){tr.model.um.UserExpectation.call(this,parentModel,'',start,duration);}
+StartupExpectation.prototype={__proto__:tr.model.um.UserExpectation.prototype,constructor:StartupExpectation};tr.model.um.UserExpectation.subTypes.register(StartupExpectation,{stageTitle:'Startup',colorId:tr.b.ColorScheme.getColorIdForReservedName('startup')});return{StartupExpectation,};});'use strict';tr.exportTo('tr.importer',function(){function getAllFrameEvents(modelHelper){var frameEvents=[];frameEvents.push.apply(frameEvents,modelHelper.browserHelper.getFrameEventsInRange(tr.model.helpers.IMPL_FRAMETIME_TYPE,modelHelper.model.bounds));for(var renderer of Object.values(modelHelper.rendererHelpers)){frameEvents.push.apply(frameEvents,renderer.getFrameEventsInRange(tr.model.helpers.IMPL_FRAMETIME_TYPE,modelHelper.model.bounds));}
+return frameEvents.sort(tr.importer.compareEvents);}
 function getStartupEvents(modelHelper){function isStartupSlice(slice){return slice.title==='BrowserMainLoop::CreateThreads';}
-var events=modelHelper.browserHelper.getAllAsyncSlicesMatching(isStartupSlice);var deduper=new tr.model.EventSet();events.forEach(function(event){var sliceGroup=event.parentContainer.sliceGroup;var slice=sliceGroup&&sliceGroup.findFirstSlice();if(slice)
-deduper.push(slice);});return deduper.toArray();}
-function findStartupExpectations(modelHelper){var openingEvents=getStartupEvents(modelHelper);var closingEvents=getAllFrameEvents(modelHelper);var startups=[];openingEvents.forEach(function(openingEvent){closingEvents.forEach(function(closingEvent){if(openingEvent.closingEvent)
-return;if(closingEvent.openingEvent)
-return;if(closingEvent.start<=openingEvent.start)
-return;if(openingEvent.parentContainer.parent.pid!==closingEvent.parentContainer.parent.pid)
-return;openingEvent.closingEvent=closingEvent;closingEvent.openingEvent=openingEvent;var se=new tr.model.um.StartupExpectation(modelHelper.model,openingEvent.start,closingEvent.end-openingEvent.start);se.associatedEvents.push(openingEvent);se.associatedEvents.push(closingEvent);startups.push(se);});});return startups;}
-return{findStartupExpectations:findStartupExpectations};});'use strict';tr.exportTo('tr.model',function(){function getAssociatedEvents(irs){var allAssociatedEvents=new tr.model.EventSet();irs.forEach(function(ir){ir.associatedEvents.forEach(function(event){if(event instanceof tr.model.FlowEvent)
-return;allAssociatedEvents.push(event);});});return allAssociatedEvents;}
-function getUnassociatedEvents(model,associatedEvents){var unassociatedEvents=new tr.model.EventSet();for(var proc of model.getAllProcesses())
-for(var thread of tr.b.dictionaryValues(proc.threads))
-for(var event of thread.sliceGroup.getDescendantEvents())
-if(!associatedEvents.contains(event))
-unassociatedEvents.push(event);return unassociatedEvents;}
-function getTotalCpuDuration(events){var cpuMs=0;events.forEach(function(event){if(event.cpuSelfTime)
-cpuMs+=event.cpuSelfTime;});return cpuMs;}
-function getIRCoverageFromModel(model){var associatedEvents=getAssociatedEvents(model.userModel.expectations);if(!associatedEvents.length)
-return undefined;var unassociatedEvents=getUnassociatedEvents(model,associatedEvents);var associatedCpuMs=getTotalCpuDuration(associatedEvents);var unassociatedCpuMs=getTotalCpuDuration(unassociatedEvents);var totalEventCount=associatedEvents.length+unassociatedEvents.length;var totalCpuMs=associatedCpuMs+unassociatedCpuMs;var coveredEventsCpuTimeRatio=undefined;if(totalCpuMs!==0)
-coveredEventsCpuTimeRatio=associatedCpuMs/totalCpuMs;return{associatedEventsCount:associatedEvents.length,unassociatedEventsCount:unassociatedEvents.length,associatedEventsCpuTimeMs:associatedCpuMs,unassociatedEventsCpuTimeMs:unassociatedCpuMs,coveredEventsCountRatio:associatedEvents.length/totalEventCount,coveredEventsCpuTimeRatio:coveredEventsCpuTimeRatio};}
-return{getIRCoverageFromModel:getIRCoverageFromModel,getAssociatedEvents:getAssociatedEvents,getUnassociatedEvents:getUnassociatedEvents};});'use strict';tr.exportTo('tr.model.um',function(){function IdleExpectation(parentModel,start,duration){var initiatorTitle='';tr.model.um.UserExpectation.call(this,parentModel,initiatorTitle,start,duration);}
-IdleExpectation.prototype={__proto__:tr.model.um.UserExpectation.prototype,constructor:IdleExpectation};tr.model.um.UserExpectation.subTypes.register(IdleExpectation,{stageTitle:'Idle',colorId:tr.b.ColorScheme.getColorIdForReservedName('rail_idle')});return{IdleExpectation:IdleExpectation};});'use strict';tr.exportTo('tr.importer',function(){var INSIGNIFICANT_MS=1;function UserModelBuilder(model){this.model=model;this.modelHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);};UserModelBuilder.supportsModelHelper=function(modelHelper){return modelHelper.browserHelper!==undefined;};UserModelBuilder.prototype={buildUserModel:function(){if(!this.modelHelper||!this.modelHelper.browserHelper)
-return;var expectations=undefined;try{expectations=this.findUserExpectations();}catch(error){this.model.importWarning({type:'UserModelBuilder',message:error,showToUser:true});return;}
-expectations.forEach(function(expectation){this.model.userModel.expectations.push(expectation);},this);},findUserExpectations:function(){var expectations=[];expectations.push.apply(expectations,tr.importer.findStartupExpectations(this.modelHelper));expectations.push.apply(expectations,tr.importer.findLoadExpectations(this.modelHelper));expectations.push.apply(expectations,tr.importer.findInputExpectations(this.modelHelper));expectations.push.apply(expectations,this.findIdleExpectations(expectations));this.collectUnassociatedEvents_(expectations);return expectations;},collectUnassociatedEvents_:function(rirs){var vacuumIRs=[];rirs.forEach(function(ir){if(ir instanceof tr.model.um.IdleExpectation||ir instanceof tr.model.um.LoadExpectation||ir instanceof tr.model.um.StartupExpectation)
-vacuumIRs.push(ir);});if(vacuumIRs.length===0)
-return;var allAssociatedEvents=tr.model.getAssociatedEvents(rirs);var unassociatedEvents=tr.model.getUnassociatedEvents(this.model,allAssociatedEvents);unassociatedEvents.forEach(function(event){if(!(event instanceof tr.model.ThreadSlice))
-return;if(!event.isTopLevel)
-return;for(var iri=0;iri<vacuumIRs.length;++iri){var ir=vacuumIRs[iri];if((event.start>=ir.start)&&(event.start<ir.end)){ir.associatedEvents.addEventSet(event.entireHierarchy);return;}}});},findIdleExpectations:function(otherIRs){if(this.model.bounds.isEmpty)
-return;var emptyRanges=tr.b.findEmptyRangesBetweenRanges(tr.b.convertEventsToRanges(otherIRs),this.model.bounds);var irs=[];var model=this.model;emptyRanges.forEach(function(range){if(range.max<(range.min+INSIGNIFICANT_MS))
-return;irs.push(new tr.model.um.IdleExpectation(model,range.min,range.max-range.min));});return irs;}};function createCustomizeModelLinesFromModel(model){var modelLines=[];modelLines.push('      audits.addEvent(model.browserMain,');modelLines.push('          {title: \'model start\', start: 0, end: 1});');var typeNames={};for(var typeName in tr.e.cc.INPUT_EVENT_TYPE_NAMES){typeNames[tr.e.cc.INPUT_EVENT_TYPE_NAMES[typeName]]=typeName;}
-var modelEvents=new tr.model.EventSet();model.userModel.expectations.forEach(function(ir,index){modelEvents.addEventSet(ir.sourceEvents);});modelEvents=modelEvents.toArray();modelEvents.sort(tr.importer.compareEvents);modelEvents.forEach(function(event){var startAndEnd='start: '+parseInt(event.start)+', '+'end: '+parseInt(event.end)+'});';if(event instanceof tr.e.cc.InputLatencyAsyncSlice){modelLines.push('      audits.addInputEvent(model, INPUT_TYPE.'+
-typeNames[event.typeName]+',');}else if(event.title==='RenderFrameImpl::didCommitProvisionalLoad'){modelLines.push('      audits.addCommitLoadEvent(model,');}else if(event.title==='InputHandlerProxy::HandleGestureFling::started'){modelLines.push('      audits.addFlingAnimationEvent(model,');}else if(event.title===tr.model.helpers.IMPL_RENDERING_STATS){modelLines.push('      audits.addFrameEvent(model,');}else if(event.title===tr.importer.CSS_ANIMATION_TITLE){modelLines.push('      audits.addEvent(model.rendererMain, {');modelLines.push('        title: \'Animation\', '+startAndEnd);return;}else{throw('You must extend createCustomizeModelLinesFromModel()'+'to support this event:\n'+event.title+'\n');}
-modelLines.push('          {'+startAndEnd);});modelLines.push('      audits.addEvent(model.browserMain,');modelLines.push('          {'+'title: \'model end\', '+'start: '+(parseInt(model.bounds.max)-1)+', '+'end: '+parseInt(model.bounds.max)+'});');return modelLines;}
-function createExpectedIRLinesFromModel(model){var expectedLines=[];var irCount=model.userModel.expectations.length;model.userModel.expectations.forEach(function(ir,index){var irString='      {';irString+='title: \''+ir.title+'\', ';irString+='start: '+parseInt(ir.start)+', ';irString+='end: '+parseInt(ir.end)+', ';irString+='eventCount: '+ir.sourceEvents.length;irString+='}';if(index<(irCount-1))
-irString+=',';expectedLines.push(irString);});return expectedLines;}
-function createIRFinderTestCaseStringFromModel(model){var filename=window.location.hash.substr(1);var testName=filename.substr(filename.lastIndexOf('/')+1);testName=testName.substr(0,testName.indexOf('.'));try{var testLines=[];testLines.push('  /*');testLines.push('    This test was generated from');testLines.push('    '+filename+'');testLines.push('   */');testLines.push('  test(\''+testName+'\', function() {');testLines.push('    var verifier = new UserExpectationVerifier();');testLines.push('    verifier.customizeModelCallback = function(model) {');testLines.push.apply(testLines,createCustomizeModelLinesFromModel(model));testLines.push('    };');testLines.push('    verifier.expectedIRs = [');testLines.push.apply(testLines,createExpectedIRLinesFromModel(model));testLines.push('    ];');testLines.push('    verifier.verify();');testLines.push('  });');return testLines.join('\n');}catch(error){return error;}}
-return{UserModelBuilder:UserModelBuilder,createIRFinderTestCaseStringFromModel:createIRFinderTestCaseStringFromModel};});'use strict';tr.exportTo('tr.ui.b',function(){function decorate(source,constr){var elements;if(typeof source=='string')
-elements=Polymer.dom(tr.doc).querySelectorAll(source);else
-elements=[source];for(var i=0,el;el=elements[i];i++){if(!(el instanceof constr))
-constr.decorate(el);}}
-function define(className,opt_parentConstructor,opt_tagNS){if(typeof className=='function'){throw new Error('Passing functions as className is deprecated. Please '+'use (className, opt_parentConstructor) to subclass');}
-var className=className.toLowerCase();if(opt_parentConstructor&&!opt_parentConstructor.tagName)
-throw new Error('opt_parentConstructor was not '+'created by tr.ui.b.define');var tagName=className;var tagNS=undefined;if(opt_parentConstructor){if(opt_tagNS)
-throw new Error('Must not specify tagNS if parentConstructor is given');var parent=opt_parentConstructor;while(parent&&parent.tagName){tagName=parent.tagName;tagNS=parent.tagNS;parent=parent.parentConstructor;}}else{tagNS=opt_tagNS;}
-function f(){if(opt_parentConstructor&&f.prototype.__proto__!=opt_parentConstructor.prototype){throw new Error(className+' prototye\'s __proto__ field is messed up. '+'It MUST be the prototype of '+opt_parentConstructor.tagName);}
-var el;if(tagNS===undefined)
-el=tr.doc.createElement(tagName);else
-el=tr.doc.createElementNS(tagNS,tagName);f.decorate.call(this,el,arguments);return el;}
-f.decorate=function(el){el.__proto__=f.prototype;el.decorate.apply(el,arguments[1]);el.constructor=f;};f.className=className;f.tagName=tagName;f.tagNS=tagNS;f.parentConstructor=(opt_parentConstructor?opt_parentConstructor:undefined);f.toString=function(){if(!f.parentConstructor)
-return f.tagName;return f.parentConstructor.toString()+'::'+f.className;};return f;}
-function elementIsChildOf(el,potentialParent){if(el==potentialParent)
-return false;var cur=el;while(Polymer.dom(cur).parentNode){if(cur==potentialParent)
-return true;cur=Polymer.dom(cur).parentNode;}
-return false;};return{decorate:decorate,define:define,elementIsChildOf:elementIsChildOf};});'use strict';tr.exportTo('tr.b',function(){function Rect(){this.x=0;this.y=0;this.width=0;this.height=0;};Rect.fromXYWH=function(x,y,w,h){var rect=new Rect();rect.x=x;rect.y=y;rect.width=w;rect.height=h;return rect;}
-Rect.fromArray=function(ary){if(ary.length!=4)
-throw new Error('ary.length must be 4');var rect=new Rect();rect.x=ary[0];rect.y=ary[1];rect.width=ary[2];rect.height=ary[3];return rect;}
-Rect.prototype={__proto__:Object.prototype,get left(){return this.x;},get top(){return this.y;},get right(){return this.x+this.width;},get bottom(){return this.y+this.height;},toString:function(){return'Rect('+this.x+', '+this.y+', '+
-this.width+', '+this.height+')';},toArray:function(){return[this.x,this.y,this.width,this.height];},clone:function(){var rect=new Rect();rect.x=this.x;rect.y=this.y;rect.width=this.width;rect.height=this.height;return rect;},enlarge:function(pad){var rect=new Rect();this.enlargeFast(rect,pad);return rect;},enlargeFast:function(out,pad){out.x=this.x-pad;out.y=this.y-pad;out.width=this.width+2*pad;out.height=this.height+2*pad;return out;},size:function(){return{width:this.width,height:this.height};},scale:function(s){var rect=new Rect();this.scaleFast(rect,s);return rect;},scaleSize:function(s){return Rect.fromXYWH(this.x,this.y,this.width*s,this.height*s);},scaleFast:function(out,s){out.x=this.x*s;out.y=this.y*s;out.width=this.width*s;out.height=this.height*s;return out;},translate:function(v){var rect=new Rect();this.translateFast(rect,v);return rect;},translateFast:function(out,v){out.x=this.x+v[0];out.y=this.x+v[1];out.width=this.width;out.height=this.height;return out;},asUVRectInside:function(containingRect){var rect=new Rect();rect.x=(this.x-containingRect.x)/containingRect.width;rect.y=(this.y-containingRect.y)/containingRect.height;rect.width=this.width/containingRect.width;rect.height=this.height/containingRect.height;return rect;},intersects:function(that){var ok=true;ok&=this.x<that.right;ok&=this.right>that.x;ok&=this.y<that.bottom;ok&=this.bottom>that.y;return ok;},equalTo:function(rect){return rect&&(this.x===rect.x)&&(this.y===rect.y)&&(this.width===rect.width)&&(this.height===rect.height);}};return{Rect:Rect};});'use strict';tr.exportTo('tr.ui.b',function(){function instantiateTemplate(selector,doc){doc=doc||document;var el=Polymer.dom(doc).querySelector(selector);if(!el)
-throw new Error('Element not found');return doc.importNode(el.content,true);}
+var events=modelHelper.browserHelper.getAllAsyncSlicesMatching(isStartupSlice);var deduper=new tr.model.EventSet();events.forEach(function(event){var sliceGroup=event.parentContainer.sliceGroup;var slice=sliceGroup&&sliceGroup.findFirstSlice();if(slice){deduper.push(slice);}});return deduper.toArray();}
+function findStartupExpectations(modelHelper){var openingEvents=getStartupEvents(modelHelper);var closingEvents=getAllFrameEvents(modelHelper);var startups=[];openingEvents.forEach(function(openingEvent){closingEvents.forEach(function(closingEvent){if(openingEvent.closingEvent)return;if(closingEvent.openingEvent)return;if(closingEvent.start<=openingEvent.start)return;if(openingEvent.parentContainer.parent.pid!==closingEvent.parentContainer.parent.pid){return;}
+openingEvent.closingEvent=closingEvent;closingEvent.openingEvent=openingEvent;var se=new tr.model.um.StartupExpectation(modelHelper.model,openingEvent.start,closingEvent.end-openingEvent.start);se.associatedEvents.push(openingEvent);se.associatedEvents.push(closingEvent);startups.push(se);});});return startups;}
+return{findStartupExpectations,};});'use strict';tr.exportTo('tr.model',function(){function getAssociatedEvents(irs){var allAssociatedEvents=new tr.model.EventSet();irs.forEach(function(ir){ir.associatedEvents.forEach(function(event){if(event instanceof tr.model.FlowEvent)return;allAssociatedEvents.push(event);});});return allAssociatedEvents;}
+function getUnassociatedEvents(model,associatedEvents){var unassociatedEvents=new tr.model.EventSet();for(var proc of model.getAllProcesses()){for(var thread of tr.b.dictionaryValues(proc.threads)){for(var event of thread.sliceGroup.getDescendantEvents()){if(!associatedEvents.contains(event)){unassociatedEvents.push(event);}}}}
+return unassociatedEvents;}
+function getTotalCpuDuration(events){var cpuMs=0;events.forEach(function(event){if(event.cpuSelfTime){cpuMs+=event.cpuSelfTime;}});return cpuMs;}
+function getIRCoverageFromModel(model){var associatedEvents=getAssociatedEvents(model.userModel.expectations);if(!associatedEvents.length)return undefined;var unassociatedEvents=getUnassociatedEvents(model,associatedEvents);var associatedCpuMs=getTotalCpuDuration(associatedEvents);var unassociatedCpuMs=getTotalCpuDuration(unassociatedEvents);var totalEventCount=associatedEvents.length+unassociatedEvents.length;var totalCpuMs=associatedCpuMs+unassociatedCpuMs;var coveredEventsCpuTimeRatio=undefined;if(totalCpuMs!==0){coveredEventsCpuTimeRatio=associatedCpuMs/totalCpuMs;}
+return{associatedEventsCount:associatedEvents.length,unassociatedEventsCount:unassociatedEvents.length,associatedEventsCpuTimeMs:associatedCpuMs,unassociatedEventsCpuTimeMs:unassociatedCpuMs,coveredEventsCountRatio:associatedEvents.length/totalEventCount,coveredEventsCpuTimeRatio:coveredEventsCpuTimeRatio};}
+return{getIRCoverageFromModel,getAssociatedEvents,getUnassociatedEvents,};});'use strict';tr.exportTo('tr.model.um',function(){function IdleExpectation(parentModel,start,duration){var initiatorTitle='';tr.model.um.UserExpectation.call(this,parentModel,initiatorTitle,start,duration);}
+IdleExpectation.prototype={__proto__:tr.model.um.UserExpectation.prototype,constructor:IdleExpectation};tr.model.um.UserExpectation.subTypes.register(IdleExpectation,{stageTitle:'Idle',colorId:tr.b.ColorScheme.getColorIdForReservedName('rail_idle')});return{IdleExpectation,};});'use strict';tr.exportTo('tr.importer',function(){var INSIGNIFICANT_MS=1;class UserModelBuilder{constructor(model){this.model=model;this.modelHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);}
+static supportsModelHelper(modelHelper){return modelHelper.browserHelper!==undefined;}
+buildUserModel(){if(!this.modelHelper||!this.modelHelper.browserHelper)return;try{for(var ue of this.findUserExpectations()){this.model.userModel.expectations.push(ue);}
+this.model.userModel.segments.push(...this.findSegments());}catch(error){this.model.importWarning({type:'UserModelBuilder',message:error,showToUser:true});}}
+findSegments(){var timestamps=new Set();for(var expectation of this.model.userModel.expectations){timestamps.add(expectation.start);timestamps.add(expectation.end);}
+timestamps=[...timestamps];timestamps.sort((x,y)=>x-y);var segments=[];for(var i=0;i<timestamps.length-1;++i){var segment=new tr.model.um.Segment(timestamps[i],timestamps[i+1]-timestamps[i]);segments.push(segment);var segmentRange=tr.b.math.Range.fromExplicitRange(segment.start,segment.end);for(var expectation of this.model.userModel.expectations){var expectationRange=tr.b.math.Range.fromExplicitRange(expectation.start,expectation.end);if(segmentRange.intersectsRangeExclusive(expectationRange)){segment.expectations.push(expectation);}}}
+return segments;}
+findUserExpectations(){var expectations=[];expectations.push.apply(expectations,tr.importer.findStartupExpectations(this.modelHelper));expectations.push.apply(expectations,tr.importer.findLoadExpectations(this.modelHelper));expectations.push.apply(expectations,tr.importer.findInputExpectations(this.modelHelper));expectations.push.apply(expectations,this.findIdleExpectations(expectations));this.collectUnassociatedEvents_(expectations);return expectations;}
+collectUnassociatedEvents_(expectations){var vacuumUEs=[];for(var expectation of expectations){if(expectation instanceof tr.model.um.IdleExpectation||expectation instanceof tr.model.um.LoadExpectation||expectation instanceof tr.model.um.StartupExpectation){vacuumUEs.push(expectation);}}
+if(vacuumUEs.length===0)return;var allAssociatedEvents=tr.model.getAssociatedEvents(expectations);var unassociatedEvents=tr.model.getUnassociatedEvents(this.model,allAssociatedEvents);for(var event of unassociatedEvents){if(!(event instanceof tr.model.ThreadSlice))continue;if(!event.isTopLevel)continue;for(var index=0;index<vacuumUEs.length;++index){var expectation=vacuumUEs[index];if((event.start>=expectation.start)&&(event.start<expectation.end)){expectation.associatedEvents.addEventSet(event.entireHierarchy);break;}}}}
+findIdleExpectations(otherUEs){if(this.model.bounds.isEmpty)return;var emptyRanges=tr.b.math.findEmptyRangesBetweenRanges(tr.b.math.convertEventsToRanges(otherUEs),this.model.bounds);var expectations=[];var model=this.model;for(var range of emptyRanges){if(range.max<(range.min+INSIGNIFICANT_MS))continue;expectations.push(new tr.model.um.IdleExpectation(model,range.min,range.max-range.min));}
+return expectations;}}
+function createCustomizeModelLinesFromModel(model){var modelLines=[];modelLines.push('      audits.addEvent(model.browserMain,');modelLines.push('          {title: \'model start\', start: 0, end: 1});');var typeNames={};for(var typeName in tr.e.cc.INPUT_EVENT_TYPE_NAMES){typeNames[tr.e.cc.INPUT_EVENT_TYPE_NAMES[typeName]]=typeName;}
+var modelEvents=new tr.model.EventSet();for(var ue of model.userModel.expectations){modelEvents.addEventSet(ue.sourceEvents);}
+modelEvents=modelEvents.toArray();modelEvents.sort(tr.importer.compareEvents);for(var event of modelEvents){var startAndEnd='start: '+parseInt(event.start)+', '+'end: '+parseInt(event.end)+'});';if(event instanceof tr.e.cc.InputLatencyAsyncSlice){modelLines.push('      audits.addInputEvent(model, INPUT_TYPE.'+
+typeNames[event.typeName]+',');}else if(event.title==='RenderFrameImpl::didCommitProvisionalLoad'){modelLines.push('      audits.addCommitLoadEvent(model,');}else if(event.title==='InputHandlerProxy::HandleGestureFling::started'){modelLines.push('      audits.addFlingAnimationEvent(model,');}else if(event.title===tr.model.helpers.IMPL_RENDERING_STATS){modelLines.push('      audits.addFrameEvent(model,');}else if(event.title===tr.importer.CSS_ANIMATION_TITLE){modelLines.push('      audits.addEvent(model.rendererMain, {');modelLines.push('        title: \'Animation\', '+startAndEnd);return;}else{throw new Error('You must extend createCustomizeModelLinesFromModel()'+'to support this event:\n'+event.title+'\n');}
+modelLines.push('          {'+startAndEnd);}
+modelLines.push('      audits.addEvent(model.browserMain,');modelLines.push('          {'+'title: \'model end\', '+'start: '+(parseInt(model.bounds.max)-1)+', '+'end: '+parseInt(model.bounds.max)+'});');return modelLines;}
+function createExpectedUELinesFromModel(model){var expectedLines=[];var ueCount=model.userModel.expectations.length;for(var index=0;index<ueCount;++index){var expectation=model.userModel.expectations[index];var ueString='      {';ueString+='title: \''+expectation.title+'\', ';ueString+='start: '+parseInt(expectation.start)+', ';ueString+='end: '+parseInt(expectation.end)+', ';ueString+='eventCount: '+expectation.sourceEvents.length;ueString+='}';if(index<(ueCount-1))ueString+=',';expectedLines.push(ueString);}
+return expectedLines;}
+function createUEFinderTestCaseStringFromModel(model){var filename=window.location.hash.substr(1);var testName=filename.substr(filename.lastIndexOf('/')+1);testName=testName.substr(0,testName.indexOf('.'));try{var testLines=[];testLines.push('  /*');testLines.push('    This test was generated from');testLines.push('    '+filename+'');testLines.push('   */');testLines.push('  test(\''+testName+'\', function() {');testLines.push('    var verifier = new UserExpectationVerifier();');testLines.push('    verifier.customizeModelCallback = function(model) {');testLines.push.apply(testLines,createCustomizeModelLinesFromModel(model));testLines.push('    };');testLines.push('    verifier.expectedUEs = [');testLines.push.apply(testLines,createExpectedUELinesFromModel(model));testLines.push('    ];');testLines.push('    verifier.verify();');testLines.push('  });');return testLines.join('\n');}catch(error){return error;}}
+return{UserModelBuilder,createUEFinderTestCaseStringFromModel,};});'use strict';tr.exportTo('tr.ui.b',function(){function decorate(source,constr){var elements;if(typeof source==='string'){elements=Polymer.dom(tr.doc).querySelectorAll(source);}else{elements=[source];}
+for(var i=0,el;el=elements[i];i++){if(!(el instanceof constr)){constr.decorate(el);}}}
+function define(className,opt_parentConstructor,opt_tagNS){if(typeof className==='function'){throw new Error('Passing functions as className is deprecated. Please '+'use (className, opt_parentConstructor) to subclass');}
+var className=className.toLowerCase();if(opt_parentConstructor&&!opt_parentConstructor.tagName){throw new Error('opt_parentConstructor was not '+'created by tr.ui.b.define');}
+var tagName=className;var tagNS=undefined;if(opt_parentConstructor){if(opt_tagNS){throw new Error('Must not specify tagNS if parentConstructor is given');}
+var parent=opt_parentConstructor;while(parent&&parent.tagName){tagName=parent.tagName;tagNS=parent.tagNS;parent=parent.parentConstructor;}}else{tagNS=opt_tagNS;}
+function f(){if(opt_parentConstructor&&f.prototype.__proto__!==opt_parentConstructor.prototype){throw new Error(className+' prototye\'s __proto__ field is messed up. '+'It MUST be the prototype of '+opt_parentConstructor.tagName);}
+var el;if(tagNS===undefined){el=tr.doc.createElement(tagName);}else{el=tr.doc.createElementNS(tagNS,tagName);}
+f.decorate.call(this,el,arguments);return el;}
+f.decorate=function(el){el.__proto__=f.prototype;el.decorate.apply(el,arguments[1]);el.constructor=f;};f.className=className;f.tagName=tagName;f.tagNS=tagNS;f.parentConstructor=(opt_parentConstructor?opt_parentConstructor:undefined);f.toString=function(){if(!f.parentConstructor){return f.tagName;}
+return f.parentConstructor.toString()+'::'+f.className;};return f;}
+function elementIsChildOf(el,potentialParent){if(el===potentialParent)return false;var cur=el;while(Polymer.dom(cur).parentNode){if(cur===potentialParent)return true;cur=Polymer.dom(cur).parentNode;}
+return false;}
+return{decorate,define,elementIsChildOf,};});'use strict';tr.exportTo('tr.b.math',function(){function Rect(){this.x=0;this.y=0;this.width=0;this.height=0;}
+Rect.fromXYWH=function(x,y,w,h){var rect=new Rect();rect.x=x;rect.y=y;rect.width=w;rect.height=h;return rect;};Rect.fromArray=function(ary){if(ary.length!==4){throw new Error('ary.length must be 4');}
+var rect=new Rect();rect.x=ary[0];rect.y=ary[1];rect.width=ary[2];rect.height=ary[3];return rect;};Rect.prototype={__proto__:Object.prototype,get left(){return this.x;},get top(){return this.y;},get right(){return this.x+this.width;},get bottom(){return this.y+this.height;},toString:function(){return'Rect('+this.x+', '+this.y+', '+
+this.width+', '+this.height+')';},toArray:function(){return[this.x,this.y,this.width,this.height];},clone:function(){var rect=new Rect();rect.x=this.x;rect.y=this.y;rect.width=this.width;rect.height=this.height;return rect;},enlarge:function(pad){var rect=new Rect();this.enlargeFast(rect,pad);return rect;},enlargeFast:function(out,pad){out.x=this.x-pad;out.y=this.y-pad;out.width=this.width+2*pad;out.height=this.height+2*pad;return out;},size:function(){return{width:this.width,height:this.height};},scale:function(s){var rect=new Rect();this.scaleFast(rect,s);return rect;},scaleSize:function(s){return Rect.fromXYWH(this.x,this.y,this.width*s,this.height*s);},scaleFast:function(out,s){out.x=this.x*s;out.y=this.y*s;out.width=this.width*s;out.height=this.height*s;return out;},translate:function(v){var rect=new Rect();this.translateFast(rect,v);return rect;},translateFast:function(out,v){out.x=this.x+v[0];out.y=this.x+v[1];out.width=this.width;out.height=this.height;return out;},asUVRectInside:function(containingRect){var rect=new Rect();rect.x=(this.x-containingRect.x)/containingRect.width;rect.y=(this.y-containingRect.y)/containingRect.height;rect.width=this.width/containingRect.width;rect.height=this.height/containingRect.height;return rect;},intersects:function(that){var ok=true;ok&=this.x<that.right;ok&=this.right>that.x;ok&=this.y<that.bottom;ok&=this.bottom>that.y;return ok;},equalTo:function(rect){return rect&&(this.x===rect.x)&&(this.y===rect.y)&&(this.width===rect.width)&&(this.height===rect.height);}};return{Rect,};});'use strict';tr.exportTo('tr.ui.b',function(){function instantiateTemplate(selector,doc){doc=doc||document;var el=Polymer.dom(doc).querySelector(selector);if(!el){throw new Error('Element not found');}
+return doc.importNode(el.content,true);}
 function windowRectForElement(element){var position=[element.offsetLeft,element.offsetTop];var size=[element.offsetWidth,element.offsetHeight];var node=element.offsetParent;while(node){position[0]+=node.offsetLeft;position[1]+=node.offsetTop;node=node.offsetParent;}
-return tr.b.Rect.fromXYWH(position[0],position[1],size[0],size[1]);}
+return tr.b.math.Rect.fromXYWH(position[0],position[1],size[0],size[1]);}
 function scrollIntoViewIfNeeded(el){var pr=el.parentElement.getBoundingClientRect();var cr=el.getBoundingClientRect();if(cr.top<pr.top){el.scrollIntoView(true);}else if(cr.bottom>pr.bottom){el.scrollIntoView(false);}}
 function extractUrlString(url){var extracted=url.replace(/url\((.*)\)/,'$1');extracted=extracted.replace(/\"(.*)\"/,'$1');return extracted;}
 function toThreeDigitLocaleString(value){return value.toLocaleString(undefined,{minimumFractionDigits:3,maximumFractionDigits:3});}
 function isUnknownElementName(name){return document.createElement(name)instanceof HTMLUnknownElement;}
-return{isUnknownElementName:isUnknownElementName,toThreeDigitLocaleString:toThreeDigitLocaleString,instantiateTemplate:instantiateTemplate,windowRectForElement:windowRectForElement,scrollIntoViewIfNeeded:scrollIntoViewIfNeeded,extractUrlString:extractUrlString};});'use strict';tr.exportTo('tr.ui.b',function(){if(tr.isHeadless)
-return{};var THIS_DOC=document.currentScript.ownerDocument;var Overlay=tr.ui.b.define('overlay');Overlay.prototype={__proto__:HTMLDivElement.prototype,decorate:function(){Polymer.dom(this).classList.add('overlay');this.parentEl_=this.ownerDocument.body;this.visible_=false;this.userCanClose_=true;this.onKeyDown_=this.onKeyDown_.bind(this);this.onClick_=this.onClick_.bind(this);this.onFocusIn_=this.onFocusIn_.bind(this);this.onDocumentClick_=this.onDocumentClick_.bind(this);this.onClose_=this.onClose_.bind(this);this.addEventListener('visible-change',tr.ui.b.Overlay.prototype.onVisibleChange_.bind(this),true);var createShadowRoot=this.createShadowRoot||this.webkitCreateShadowRoot;this.shadow_=createShadowRoot.call(this);Polymer.dom(this.shadow_).appendChild(tr.ui.b.instantiateTemplate('#overlay-template',THIS_DOC));this.closeBtn_=Polymer.dom(this.shadow_).querySelector('close-button');this.closeBtn_.addEventListener('click',this.onClose_);Polymer.dom(this.shadow_).querySelector('overlay-frame').addEventListener('click',this.onClick_);this.observer_=new WebKitMutationObserver(this.didButtonBarMutate_.bind(this));this.observer_.observe(Polymer.dom(this.shadow_).querySelector('button-bar'),{childList:true});Object.defineProperty(this,'title',{get:function(){return Polymer.dom(Polymer.dom(this.shadow_).querySelector('title')).textContent;},set:function(title){Polymer.dom(Polymer.dom(this.shadow_).querySelector('title')).textContent=title;}});},set userCanClose(userCanClose){this.userCanClose_=userCanClose;this.closeBtn_.style.display=userCanClose?'block':'none';},get buttons(){return Polymer.dom(this.shadow_).querySelector('button-bar');},get visible(){return this.visible_;},set visible(newValue){if(this.visible_===newValue)
-return;this.visible_=newValue;var e=new tr.b.Event('visible-change');this.dispatchEvent(e);},onVisibleChange_:function(){this.visible_?this.show_():this.hide_();},show_:function(){Polymer.dom(this.parentEl_).appendChild(this);if(this.userCanClose_){this.addEventListener('keydown',this.onKeyDown_.bind(this));this.addEventListener('click',this.onDocumentClick_.bind(this));this.closeBtn_.addEventListener('click',this.onClose_);}
-this.parentEl_.addEventListener('focusin',this.onFocusIn_);this.tabIndex=0;var focusEl=undefined;var elList=Polymer.dom(this).querySelectorAll('button, input, list, select, a');if(elList.length>0){if(elList[0]===this.closeBtn_){if(elList.length>1)
-focusEl=elList[1];}else{focusEl=elList[0];}}
-if(focusEl===undefined)
-focusEl=this;focusEl.focus();},hide_:function(){Polymer.dom(this.parentEl_).removeChild(this);this.parentEl_.removeEventListener('focusin',this.onFocusIn_);if(this.closeBtn_)
-this.closeBtn_.removeEventListener('click',this.onClose_);document.removeEventListener('keydown',this.onKeyDown_);document.removeEventListener('click',this.onDocumentClick_);},onClose_:function(e){this.visible=false;if((e.type!='keydown')||(e.type==='keydown'&&e.keyCode===27))
-e.stopPropagation();e.preventDefault();tr.b.dispatchSimpleEvent(this,'closeclick');},onFocusIn_:function(e){if(e.target===this)
-return;window.setTimeout(function(){this.focus();},0);e.preventDefault();e.stopPropagation();},didButtonBarMutate_:function(e){var hasButtons=this.buttons.children.length>0;if(hasButtons){Polymer.dom(this.shadow_).querySelector('button-bar').style.display=undefined;}else{Polymer.dom(this.shadow_).querySelector('button-bar').style.display='none';}},onKeyDown_:function(e){if(e.keyCode===9&&e.shiftKey&&e.target===this){e.preventDefault();return;}
-if(e.keyCode!==27)
-return;this.onClose_(e);},onClick_:function(e){e.stopPropagation();},onDocumentClick_:function(e){if(!this.userCanClose_)
-return;this.onClose_(e);}};Overlay.showError=function(msg,opt_err){var o=new Overlay();o.title='Error';Polymer.dom(o).textContent=msg;if(opt_err){var e=tr.b.normalizeException(opt_err);var stackDiv=document.createElement('pre');Polymer.dom(stackDiv).textContent=e.stack;stackDiv.style.paddingLeft='8px';stackDiv.style.margin=0;Polymer.dom(o).appendChild(stackDiv);}
-var b=document.createElement('button');Polymer.dom(b).textContent='OK';b.addEventListener('click',function(){o.visible=false;});Polymer.dom(o.buttons).appendChild(b);o.visible=true;return o;};return{Overlay:Overlay};});'use strict';tr.exportTo('tr.importer',function(){var Timing=tr.b.Timing;function ImportOptions(){this.shiftWorldToZero=true;this.pruneEmptyContainers=true;this.showImportWarnings=true;this.trackDetailedModelStats=false;this.customizeModelCallback=undefined;var auditorTypes=tr.c.Auditor.getAllRegisteredTypeInfos();this.auditorConstructors=auditorTypes.map(function(typeInfo){return typeInfo.constructor;});}
-function Import(model,opt_options){if(model===undefined)
-throw new Error('Must provide model to import into.');this.importing_=false;this.importOptions_=opt_options||new ImportOptions();this.model_=model;this.model_.importOptions=this.importOptions_;}
-Import.prototype={__proto__:Object.prototype,importTraces:function(traces){var progressMeter={update:function(msg){}};tr.b.Task.RunSynchronously(this.createImportTracesTask(progressMeter,traces));},importTracesWithProgressDialog:function(traces){if(tr.isHeadless)
-throw new Error('Cannot use this method in headless mode.');var overlay=tr.ui.b.Overlay();overlay.title='Importing...';overlay.userCanClose=false;overlay.msgEl=document.createElement('div');Polymer.dom(overlay).appendChild(overlay.msgEl);overlay.msgEl.style.margin='20px';overlay.update=function(msg){Polymer.dom(this.msgEl).textContent=msg;};overlay.visible=true;var promise=tr.b.Task.RunWhenIdle(this.createImportTracesTask(overlay,traces));promise.then(function(){overlay.visible=false;},function(err){overlay.visible=false;});return promise;},createImportTracesTask:function(progressMeter,traces){if(this.importing_)
-throw new Error('Already importing.');this.importing_=true;var importTask=new tr.b.Task(function prepareImport(){progressMeter.update('I will now import your traces for you...');},this);var lastTask=importTask;var importers=[];lastTask=lastTask.timedAfter('TraceImport',function createImports(){traces=traces.slice(0);progressMeter.update('Creating importers...');for(var i=0;i<traces.length;++i)
-importers.push(this.createImporter_(traces[i]));for(var i=0;i<importers.length;i++){var subtraces=importers[i].extractSubtraces();for(var j=0;j<subtraces.length;j++){try{traces.push(subtraces[j]);importers.push(this.createImporter_(subtraces[j]));}catch(error){console.warn(error.name+': '+error.message);continue;}}}
+return{isUnknownElementName,toThreeDigitLocaleString,instantiateTemplate,windowRectForElement,scrollIntoViewIfNeeded,extractUrlString,};});'use strict';tr.exportTo('tr.ui.b',function(){if(tr.isHeadless)return{};var THIS_DOC=document.currentScript.ownerDocument;var Overlay=tr.ui.b.define('overlay');Overlay.prototype={__proto__:HTMLDivElement.prototype,decorate:function(){Polymer.dom(this).classList.add('overlay');this.parentEl_=this.ownerDocument.body;this.visible_=false;this.userCanClose_=true;this.onKeyDown_=this.onKeyDown_.bind(this);this.onClick_=this.onClick_.bind(this);this.onFocusIn_=this.onFocusIn_.bind(this);this.onDocumentClick_=this.onDocumentClick_.bind(this);this.onClose_=this.onClose_.bind(this);this.addEventListener('visible-change',tr.ui.b.Overlay.prototype.onVisibleChange_.bind(this),true);var createShadowRoot=this.createShadowRoot||this.webkitCreateShadowRoot;this.shadow_=createShadowRoot.call(this);Polymer.dom(this.shadow_).appendChild(tr.ui.b.instantiateTemplate('#overlay-template',THIS_DOC));this.closeBtn_=Polymer.dom(this.shadow_).querySelector('close-button');this.closeBtn_.addEventListener('click',this.onClose_);Polymer.dom(this.shadow_).querySelector('overlay-frame').addEventListener('click',this.onClick_);this.observer_=new WebKitMutationObserver(this.didButtonBarMutate_.bind(this));this.observer_.observe(Polymer.dom(this.shadow_).querySelector('button-bar'),{childList:true});Object.defineProperty(this,'title',{get:function(){return Polymer.dom(Polymer.dom(this.shadow_).querySelector('title')).textContent;},set:function(title){Polymer.dom(Polymer.dom(this.shadow_).querySelector('title')).textContent=title;}});},set userCanClose(userCanClose){this.userCanClose_=userCanClose;this.closeBtn_.style.display=userCanClose?'block':'none';},get buttons(){return Polymer.dom(this.shadow_).querySelector('button-bar');},get visible(){return this.visible_;},set visible(newValue){if(this.visible_===newValue)return;this.visible_=newValue;var e=new tr.b.Event('visible-change');this.dispatchEvent(e);},onVisibleChange_:function(){this.visible_?this.show_():this.hide_();},show_:function(){Polymer.dom(this.parentEl_).appendChild(this);if(this.userCanClose_){this.addEventListener('keydown',this.onKeyDown_.bind(this));this.addEventListener('click',this.onDocumentClick_.bind(this));this.closeBtn_.addEventListener('click',this.onClose_);}
+this.parentEl_.addEventListener('focusin',this.onFocusIn_);this.tabIndex=0;var elList=Polymer.dom(this).querySelectorAll('button, input, list, select, a');if(elList.length>0){if(elList[0]===this.closeBtn_){if(elList.length>1)return elList[1].focus();}else{return elList[0].focus();}}
+this.focus();},hide_:function(){Polymer.dom(this.parentEl_).removeChild(this);this.parentEl_.removeEventListener('focusin',this.onFocusIn_);if(this.closeBtn_){this.closeBtn_.removeEventListener('click',this.onClose_);}
+document.removeEventListener('keydown',this.onKeyDown_);document.removeEventListener('click',this.onDocumentClick_);},onClose_:function(e){this.visible=false;if((e.type!=='keydown')||(e.type==='keydown'&&e.keyCode===27)){e.stopPropagation();}
+e.preventDefault();tr.b.dispatchSimpleEvent(this,'closeclick');},onFocusIn_:function(e){if(e.target===this)return;tr.b.timeout(0).then(()=>this.focus());e.preventDefault();e.stopPropagation();},didButtonBarMutate_:function(e){var hasButtons=this.buttons.children.length>0;if(hasButtons){Polymer.dom(this.shadow_).querySelector('button-bar').style.display=undefined;}else{Polymer.dom(this.shadow_).querySelector('button-bar').style.display='none';}},onKeyDown_:function(e){if(e.keyCode===9&&e.shiftKey&&e.target===this){e.preventDefault();return;}
+if(e.keyCode!==27)return;this.onClose_(e);},onClick_:function(e){e.stopPropagation();},onDocumentClick_:function(e){if(!this.userCanClose_)return;this.onClose_(e);}};Overlay.showError=function(msg,opt_err){var o=new Overlay();o.title='Error';Polymer.dom(o).textContent=msg;if(opt_err){var e=tr.b.normalizeException(opt_err);var stackDiv=document.createElement('pre');Polymer.dom(stackDiv).textContent=e.stack;stackDiv.style.paddingLeft='8px';stackDiv.style.margin=0;Polymer.dom(o).appendChild(stackDiv);}
+var b=document.createElement('button');Polymer.dom(b).textContent='OK';b.addEventListener('click',function(){o.visible=false;});Polymer.dom(o.buttons).appendChild(b);o.visible=true;return o;};return{Overlay,};});'use strict';tr.exportTo('tr.importer',function(){var Timing=tr.b.Timing;function ImportOptions(){this.shiftWorldToZero=true;this.pruneEmptyContainers=true;this.showImportWarnings=true;this.trackDetailedModelStats=false;this.customizeModelCallback=undefined;var auditorTypes=tr.c.Auditor.getAllRegisteredTypeInfos();this.auditorConstructors=auditorTypes.map(function(typeInfo){return typeInfo.constructor;});}
+function Import(model,opt_options){if(model===undefined){throw new Error('Must provide model to import into.');}
+this.importing_=false;this.importOptions_=opt_options||new ImportOptions();this.model_=model;this.model_.importOptions=this.importOptions_;}
+Import.prototype={__proto__:Object.prototype,importTraces:function(traces){var progressMeter={update:function(msg){}};tr.b.Task.RunSynchronously(this.createImportTracesTask(progressMeter,traces));},importTracesWithProgressDialog:function(traces){if(tr.isHeadless){throw new Error('Cannot use this method in headless mode.');}
+var overlay=tr.ui.b.Overlay();overlay.title='Importing...';overlay.userCanClose=false;overlay.msgEl=document.createElement('div');Polymer.dom(overlay).appendChild(overlay.msgEl);overlay.msgEl.style.margin='20px';overlay.update=function(msg){Polymer.dom(this.msgEl).textContent=msg;};overlay.visible=true;var promise=tr.b.Task.RunWhenIdle(this.createImportTracesTask(overlay,traces));promise.then(function(){overlay.visible=false;},function(err){overlay.visible=false;});return promise;},createImportTracesTask:function(progressMeter,traces){var importStartTimeMs=performance.now();if(this.importing_){throw new Error('Already importing.');}
+this.importing_=true;var importTask=new tr.b.Task(function prepareImport(){progressMeter.update('I will now import your traces for you...');},this);var lastTask=importTask;var importers=[];function addImportStage(title,callback){lastTask=lastTask.after(()=>progressMeter.update(title));lastTask.updatesUi=true;lastTask=lastTask.after(callback);}
+function addStageForEachImporter(title,callback){lastTask=lastTask.after((task)=>{importers.forEach((importer,index)=>{var uiSubTask=task.subTask(()=>{progressMeter.update(`${title}${index+1}of ${importers.length}`);});uiSubTask.updatesUi=true;task.subTask(()=>callback(importer));});});}
+addImportStage('Creating importers...',()=>{traces=traces.slice(0);progressMeter.update('Creating importers...');for(var i=0;i<traces.length;++i){importers.push(this.createImporter_(traces[i]));}
+for(var i=0;i<importers.length;i++){var subtraces=importers[i].extractSubtraces();for(var j=0;j<subtraces.length;j++){try{traces.push(subtraces[j]);importers.push(this.createImporter_(subtraces[j]));}catch(error){this.model_.importWarning({type:error.name,message:error.message,showToUser:true,});continue;}}}
 if(traces.length&&!this.hasEventDataDecoder_(importers)){throw new Error('Could not find an importer for the provided eventData.');}
-importers.sort(function(x,y){return x.importPriority-y.importPriority;});},this);lastTask=lastTask.timedAfter('TraceImport',function importClockSyncMarkers(task){importers.forEach(function(importer,index){task.subTask(Timing.wrapNamedFunction('TraceImport',importer.importerName,function runImportClockSyncMarkersOnOneImporter(){progressMeter.update('Importing clock sync markers '+(index+1)+' of '+
-importers.length);importer.importClockSyncMarkers();}),this);},this);},this);lastTask=lastTask.timedAfter('TraceImport',function runImport(task){importers.forEach(function(importer,index){task.subTask(Timing.wrapNamedFunction('TraceImport',importer.importerName,function runImportEventsOnOneImporter(){progressMeter.update('Importing '+(index+1)+' of '+importers.length);importer.importEvents();}),this);},this);},this);if(this.importOptions_.customizeModelCallback){lastTask=lastTask.timedAfter('TraceImport',function runCustomizeCallbacks(task){this.importOptions_.customizeModelCallback(this.model_);},this);}
-lastTask=lastTask.timedAfter('TraceImport',function importSampleData(task){importers.forEach(function(importer,index){progressMeter.update('Importing sample data '+(index+1)+'/'+importers.length);importer.importSampleData();},this);},this);lastTask=lastTask.timedAfter('TraceImport',function runAutoclosers(){progressMeter.update('Autoclosing open slices...');this.model_.autoCloseOpenSlices();this.model_.createSubSlices();},this);lastTask=lastTask.timedAfter('TraceImport',function finalizeImport(task){importers.forEach(function(importer,index){progressMeter.update('Finalizing import '+(index+1)+'/'+importers.length);importer.finalizeImport();},this);},this);lastTask=lastTask.timedAfter('TraceImport',function runPreinits(){progressMeter.update('Initializing objects (step 1/2)...');this.model_.preInitializeObjects();},this);if(this.importOptions_.pruneEmptyContainers){lastTask=lastTask.timedAfter('TraceImport',function runPruneEmptyContainers(){progressMeter.update('Pruning empty containers...');this.model_.pruneEmptyContainers();},this);}
-lastTask=lastTask.timedAfter('TraceImport',function runMergeKernelWithuserland(){progressMeter.update('Merging kernel with userland...');this.model_.mergeKernelWithUserland();},this);var auditors=[];lastTask=lastTask.timedAfter('TraceImport',function createAuditorsAndRunAnnotate(){progressMeter.update('Adding arbitrary data to model...');auditors=this.importOptions_.auditorConstructors.map(function(auditorConstructor){return new auditorConstructor(this.model_);},this);auditors.forEach(function(auditor){auditor.runAnnotate();auditor.installUserFriendlyCategoryDriverIfNeeded();});},this);lastTask=lastTask.timedAfter('TraceImport',function computeWorldBounds(){progressMeter.update('Computing final world bounds...');this.model_.computeWorldBounds(this.importOptions_.shiftWorldToZero);},this);lastTask=lastTask.timedAfter('TraceImport',function buildFlowEventIntervalTree(){progressMeter.update('Building flow event map...');this.model_.buildFlowEventIntervalTree();},this);lastTask=lastTask.timedAfter('TraceImport',function joinRefs(){progressMeter.update('Joining object refs...');this.model_.joinRefs();},this);lastTask=lastTask.timedAfter('TraceImport',function cleanupUndeletedObjects(){progressMeter.update('Cleaning up undeleted objects...');this.model_.cleanupUndeletedObjects();},this);lastTask=lastTask.timedAfter('TraceImport',function sortMemoryDumps(){progressMeter.update('Sorting memory dumps...');this.model_.sortMemoryDumps();},this);lastTask=lastTask.timedAfter('TraceImport',function finalizeMemoryGraphs(){progressMeter.update('Finalizing memory dump graphs...');this.model_.finalizeMemoryGraphs();},this);lastTask=lastTask.timedAfter('TraceImport',function initializeObjects(){progressMeter.update('Initializing objects (step 2/2)...');this.model_.initializeObjects();},this);lastTask=lastTask.timedAfter('TraceImport',function buildEventIndices(){progressMeter.update('Building event indices...');this.model_.buildEventIndices();},this);lastTask=lastTask.timedAfter('TraceImport',function buildUserModel(){progressMeter.update('Building UserModel...');var userModelBuilder=new tr.importer.UserModelBuilder(this.model_);userModelBuilder.buildUserModel();},this);lastTask=lastTask.timedAfter('TraceImport',function sortExpectations(){progressMeter.update('Sorting user expectations...');this.model_.userModel.sortExpectations();},this);lastTask=lastTask.timedAfter('TraceImport',function runAudits(){progressMeter.update('Running auditors...');auditors.forEach(function(auditor){auditor.runAudit();});},this);lastTask=lastTask.timedAfter('TraceImport',function sortAlerts(){progressMeter.update('Updating alerts...');this.model_.sortAlerts();},this);lastTask=lastTask.timedAfter('TraceImport',function lastUpdateBounds(){progressMeter.update('Update bounds...');this.model_.updateBounds();},this);lastTask=lastTask.timedAfter('TraceImport',function addModelWarnings(){progressMeter.update('Looking for warnings...');if(!this.model_.isTimeHighResolution){this.model_.importWarning({type:'low_resolution_timer',message:'Trace time is low resolution, trace may be unusable.',showToUser:true});}},this);lastTask.after(function(){this.importing_=false;},this);return importTask;},createImporter_:function(eventData){var importerConstructor=tr.importer.Importer.findImporterFor(eventData);if(!importerConstructor){throw new Error('Couldn\'t create an importer for the provided '+'eventData.');}
-return new importerConstructor(this.model_,eventData);},hasEventDataDecoder_:function(importers){for(var i=0;i<importers.length;++i){if(!importers[i].isTraceDataContainer())
-return true;}
-return false;}};return{ImportOptions:ImportOptions,Import:Import};});'use strict';tr.exportTo('tr.e.v8',function(){var ThreadSlice=tr.model.ThreadSlice;function V8ThreadSlice(){ThreadSlice.apply(this,arguments);this.runtimeCallStats_=undefined;}
+importers.sort(function(x,y){return x.importPriority-y.importPriority;});});addStageForEachImporter('Importing clock sync markers',importer=>importer.importClockSyncMarkers());addStageForEachImporter('Importing',importer=>importer.importEvents());if(this.importOptions_.customizeModelCallback){addImportStage('Customizing',()=>{this.importOptions_.customizeModelCallback(this.model_);});}
+addStageForEachImporter('Importing sample data',importer=>importer.importSampleData());addImportStage('Autoclosing open slices...',()=>{this.model_.autoCloseOpenSlices();this.model_.createSubSlices();});addStageForEachImporter('Finalizing import',importer=>importer.finalizeImport());addImportStage('Initializing objects (step 1/2)...',()=>this.model_.preInitializeObjects());if(this.importOptions_.pruneEmptyContainers){addImportStage('Pruning empty containers...',()=>this.model_.pruneEmptyContainers());}
+addImportStage('Merging kernel with userland...',()=>this.model_.mergeKernelWithUserland());var auditors=[];addImportStage('Adding arbitrary data to model...',()=>{auditors=this.importOptions_.auditorConstructors.map(auditorConstructor=>new auditorConstructor(this.model_));auditors.forEach((auditor)=>{auditor.runAnnotate();auditor.installUserFriendlyCategoryDriverIfNeeded();});});addImportStage('Computing final world bounds...',()=>{this.model_.computeWorldBounds(this.importOptions_.shiftWorldToZero);});addImportStage('Building flow event map...',()=>this.model_.buildFlowEventIntervalTree());addImportStage('Joining object refs...',()=>this.model_.joinRefs());addImportStage('Cleaning up undeleted objects...',()=>this.model_.cleanupUndeletedObjects());addImportStage('Sorting memory dumps...',()=>this.model_.sortMemoryDumps());addImportStage('Finalizing memory dump graphs...',()=>this.model_.finalizeMemoryGraphs());addImportStage('Initializing objects (step 2/2)...',()=>this.model_.initializeObjects());addImportStage('Building event indices...',()=>this.model_.buildEventIndices());addImportStage('Building UserModel...',()=>{var userModelBuilder=new tr.importer.UserModelBuilder(this.model_);userModelBuilder.buildUserModel();});addImportStage('Sorting user expectations...',()=>this.model_.userModel.sortExpectations());addImportStage('Running auditors...',()=>{auditors.forEach(auditor=>auditor.runAudit());});addImportStage('Updating alerts...',()=>this.model_.sortAlerts());addImportStage('Update bounds...',()=>this.model_.updateBounds());addImportStage('Looking for warnings...',()=>{if(!this.model_.isTimeHighResolution){this.model_.importWarning({type:'low_resolution_timer',message:'Trace time is low resolution, trace may be unusable.',showToUser:true});}});lastTask.after(()=>{this.importing_=false;this.model_.stats.traceImportDurationMs=performance.now()-importStartTimeMs;});return importTask;},createImporter_:function(eventData){var importerConstructor=tr.importer.Importer.findImporterFor(eventData);if(!importerConstructor){throw new Error('Couldn\'t create an importer for the provided '+'eventData.');}
+return new importerConstructor(this.model_,eventData);},hasEventDataDecoder_:function(importers){for(var i=0;i<importers.length;++i){if(!importers[i].isTraceDataContainer())return true;}
+return false;}};return{ImportOptions,Import,};});'use strict';tr.exportTo('tr.e.v8',function(){var ProfileNode=tr.model.ProfileNode;function V8CpuProfileNode(id,callFrame,parentNode){ProfileNode.call(this,id,callFrame.functionName,parentNode);this.callFrame_=tr.b.deepCopy(callFrame);this.deoptReason_='';this.colorId_=tr.b.ColorScheme.getColorIdForGeneralPurposeString(callFrame.functionName);}
+V8CpuProfileNode.prototype={__proto__:ProfileNode.prototype,get functionName(){return this.callFrame_.functionName;},get scriptId(){return this.callFrame_.scriptId;},get url(){if(!this.callFrame_.url){return'unknown';}
+var url=this.callFrame_.url;if(this.callFrame_.lineNumber===undefined){return url;}
+url=url+':'+this.callFrame_.lineNumber;if(this.callFrame_.columnNumber===undefined){return url;}
+url=url+':'+this.callFrame_.columnNumber;return url;},get deoptReason(){return this.deoptReason_;},set deoptReason(value){this.deoptReason_=value;},get userFriendlyName(){let name=this.functionName+' url: '+this.url;return!this.deoptReason_?name:name+' Deoptimized reason: '+this.deoptReason_;},get sampleTitle(){return'V8 Sample';}};V8CpuProfileNode.constructFromObject=function(profileTree,node){let nodeId=node['id'];if(nodeId===1){return undefined;}
+var parentNode=profileTree.getNode(node['parent']);var profileNode=new V8CpuProfileNode(nodeId,node['callFrame'],parentNode);if(node['deoptReason']!==undefined){profileNode.deoptReason=node['deoptReason'];}
+return profileNode;};ProfileNode.subTypes.register(V8CpuProfileNode,{typeName:'cpuProfile',name:'v8 cpu profile node',pluralName:'v8 cpu profile nodes'});ProfileNode.subTypes.register(V8CpuProfileNode,{typeName:'legacySample',name:'v8 cpu profile node',pluralName:'v8 cpu profile nodes'});return{ProfileNode,};});'use strict';tr.exportTo('tr.e.v8',function(){var ThreadSlice=tr.model.ThreadSlice;function V8GCStatsThreadSlice(){ThreadSlice.apply(this,arguments);this.liveObjects_=JSON.parse(this.args['live']);delete this.args['live'];this.deadObjects_=JSON.parse(this.args['dead']);delete this.args['dead'];}
+V8GCStatsThreadSlice.prototype={__proto__:ThreadSlice.prototype,get liveObjects(){return this.liveObjects_;},get deadObjects(){return this.deadObjects_;}};ThreadSlice.subTypes.register(V8GCStatsThreadSlice,{categoryParts:['disabled-by-default-v8.gc_stats'],name:'v8 gc stats slice',pluralName:'v8 gc stats slices'});return{V8GCStatsThreadSlice,};});'use strict';tr.exportTo('tr.e.v8',function(){var ThreadSlice=tr.model.ThreadSlice;function V8ICStatsThreadSlice(){ThreadSlice.apply(this,arguments);this.icStats_=undefined;if(this.args['ic-stats']){this.icStats_=this.args['ic-stats']['data'];delete this.args['ic-stats'];}}
+V8ICStatsThreadSlice.prototype={__proto__:ThreadSlice.prototype,get icStats(){return this.icStats_;}};ThreadSlice.subTypes.register(V8ICStatsThreadSlice,{categoryParts:['disabled-by-default-v8.ic_stats'],name:'v8 ic stats slice',pluralName:'v8 ic stats slices'});return{V8ICStatsThreadSlice,};});'use strict';tr.exportTo('tr.e.v8',function(){var ThreadSlice=tr.model.ThreadSlice;function V8ThreadSlice(){ThreadSlice.apply(this,arguments);this.runtimeCallStats_=undefined;}
 V8ThreadSlice.prototype={__proto__:ThreadSlice.prototype,get runtimeCallStats(){if('runtime-call-stats'in this.args){this.runtimeCallStats_=this.args['runtime-call-stats'];delete this.args['runtime-call-stats'];}
-return this.runtimeCallStats_;}};ThreadSlice.subTypes.register(V8ThreadSlice,{categoryParts:['v8'],name:'v8 slice',pluralName:'v8 slices'});return{V8ThreadSlice:V8ThreadSlice};});'use strict';tr.exportTo('tr.e.cc',function(){function PictureAsImageData(picture,errorOrImageData){this.picture_=picture;if(errorOrImageData instanceof ImageData){this.error_=undefined;this.imageData_=errorOrImageData;}else{this.error_=errorOrImageData;this.imageData_=undefined;}};PictureAsImageData.Pending=function(picture){return new PictureAsImageData(picture,undefined);};PictureAsImageData.prototype={get picture(){return this.picture_;},get error(){return this.error_;},get imageData(){return this.imageData_;},isPending:function(){return this.error_===undefined&&this.imageData_===undefined;},asCanvas:function(){if(!this.imageData_)
-return;var canvas=document.createElement('canvas');var ctx=canvas.getContext('2d');canvas.width=this.imageData_.width;canvas.height=this.imageData_.height;ctx.putImageData(this.imageData_,0,0);return canvas;}};return{PictureAsImageData:PictureAsImageData};});'use strict';tr.exportTo('tr.e.cc',function(){var convertedNameCache={};function convertNameToJSConvention(name){if(name in convertedNameCache)
-return convertedNameCache[name];if(name[0]=='_'||name[name.length-1]=='_'){convertedNameCache[name]=name;return name;}
-var words=name.split('_');if(words.length==1){convertedNameCache[name]=words[0];return words[0];}
-for(var i=1;i<words.length;i++)
-words[i]=words[i][0].toUpperCase()+words[i].substring(1);convertedNameCache[name]=words.join('');return convertedNameCache[name];}
+return this.runtimeCallStats_;}};ThreadSlice.subTypes.register(V8ThreadSlice,{categoryParts:['v8','disabled-by-default-v8.runtime_stats'],name:'v8 slice',pluralName:'v8 slices'});return{V8ThreadSlice,};});'use strict';tr.exportTo('tr.e.cc',function(){function PictureAsImageData(picture,errorOrImageData){this.picture_=picture;if(errorOrImageData instanceof ImageData){this.error_=undefined;this.imageData_=errorOrImageData;}else{this.error_=errorOrImageData;this.imageData_=undefined;}}
+PictureAsImageData.Pending=function(picture){return new PictureAsImageData(picture,undefined);};PictureAsImageData.prototype={get picture(){return this.picture_;},get error(){return this.error_;},get imageData(){return this.imageData_;},isPending:function(){return this.error_===undefined&&this.imageData_===undefined;},asCanvas:function(){if(!this.imageData_)return;var canvas=document.createElement('canvas');var ctx=canvas.getContext('2d');canvas.width=this.imageData_.width;canvas.height=this.imageData_.height;ctx.putImageData(this.imageData_,0,0);return canvas;}};return{PictureAsImageData,};});'use strict';tr.exportTo('tr.e.cc',function(){var convertedNameCache={};function convertNameToJSConvention(name){if(name in convertedNameCache){return convertedNameCache[name];}
+if(name[0]==='_'||name[name.length-1]==='_'){convertedNameCache[name]=name;return name;}
+var words=name.split('_');if(words.length===1){convertedNameCache[name]=words[0];return words[0];}
+for(var i=1;i<words.length;i++){words[i]=words[i][0].toUpperCase()+words[i].substring(1);}
+convertedNameCache[name]=words.join('');return convertedNameCache[name];}
 function convertObjectFieldNamesToJSConventions(object){tr.b.iterObjectFieldsRecursively(object,function(object,fieldName,fieldValue){delete object[fieldName];object[newFieldName]=fieldValue;return newFieldName;});}
 function convertQuadSuffixedTypesToQuads(object){tr.b.iterObjectFieldsRecursively(object,function(object,fieldName,fieldValue){});}
 function convertObject(object){convertObjectFieldNamesToJSConventions(object);convertQuadSuffixedTypesToQuads(object);}
-function moveRequiredFieldsFromArgsToToplevel(object,fields){for(var i=0;i<fields.length;i++){var key=fields[i];if(object.args[key]===undefined)
-throw Error('Expected field '+key+' not found in args');if(object[key]!==undefined)
-throw Error('Field '+key+' already in object');object[key]=object.args[key];delete object.args[key];}}
-function moveOptionalFieldsFromArgsToToplevel(object,fields){for(var i=0;i<fields.length;i++){var key=fields[i];if(object.args[key]===undefined)
-continue;if(object[key]!==undefined)
-throw Error('Field '+key+' already in object');object[key]=object.args[key];delete object.args[key];}}
+function moveRequiredFieldsFromArgsToToplevel(object,fields){for(var i=0;i<fields.length;i++){var key=fields[i];if(object.args[key]===undefined){throw Error('Expected field '+key+' not found in args');}
+if(object[key]!==undefined){throw Error('Field '+key+' already in object');}
+object[key]=object.args[key];delete object.args[key];}}
+function moveOptionalFieldsFromArgsToToplevel(object,fields){for(var i=0;i<fields.length;i++){var key=fields[i];if(object.args[key]===undefined)continue;if(object[key]!==undefined){throw Error('Field '+key+' already in object');}
+object[key]=object.args[key];delete object.args[key];}}
 function preInitializeObject(object){preInitializeObjectInner(object.args,false);}
-function preInitializeObjectInner(object,hasRecursed){if(!(object instanceof Object))
-return;if(object instanceof Array){for(var i=0;i<object.length;i++)
-preInitializeObjectInner(object[i],true);return;}
-if(hasRecursed&&(object instanceof tr.model.ObjectSnapshot||object instanceof tr.model.ObjectInstance))
-return;for(var key in object){var newKey=convertNameToJSConvention(key);if(newKey!=key){var value=object[key];delete object[key];object[newKey]=value;key=newKey;}
-if(/Quad$/.test(key)&&!(object[key]instanceof tr.b.Quad)){var q;try{q=tr.b.Quad.from8Array(object[key]);}catch(e){console.log(e);}
+function preInitializeObjectInner(object,hasRecursed){if(!(object instanceof Object))return;if(object instanceof Array){for(var i=0;i<object.length;i++){preInitializeObjectInner(object[i],true);}
+return;}
+if(hasRecursed&&(object instanceof tr.model.ObjectSnapshot||object instanceof tr.model.ObjectInstance)){return;}
+for(var key in object){var newKey=convertNameToJSConvention(key);if(newKey!==key){var value=object[key];delete object[key];object[newKey]=value;key=newKey;}
+if(/Quad$/.test(key)&&!(object[key]instanceof tr.b.math.Quad)){var q;try{q=tr.b.math.Quad.from8Array(object[key]);}catch(e){}
 object[key]=q;continue;}
-if(/Rect$/.test(key)&&!(object[key]instanceof tr.b.Rect)){var r;try{r=tr.b.Rect.fromArray(object[key]);}catch(e){console.log(e);}
+if(/Rect$/.test(key)&&!(object[key]instanceof tr.b.math.Rect)){var r;try{r=tr.b.math.Rect.fromArray(object[key]);}catch(e){}
 object[key]=r;}
 preInitializeObjectInner(object[key],true);}}
 return{preInitializeObject:preInitializeObject,convertNameToJSConvention:convertNameToJSConvention,moveRequiredFieldsFromArgsToToplevel:moveRequiredFieldsFromArgsToToplevel,moveOptionalFieldsFromArgsToToplevel:moveOptionalFieldsFromArgsToToplevel,};});'use strict';tr.exportTo('tr.e.cc',function(){var ObjectSnapshot=tr.model.ObjectSnapshot;var PictureCount=0;var OPS_TIMING_ITERATIONS=3;function Picture(skp64,layerRect){this.skp64_=skp64;this.layerRect_=layerRect;this.guid_=tr.b.GUID.allocateSimple();}
 Picture.prototype={get canSave(){return true;},get layerRect(){return this.layerRect_;},get guid(){return this.guid_;},getBase64SkpData:function(){return this.skp64_;},getOps:function(){if(!PictureSnapshot.CanGetOps()){console.error(PictureSnapshot.HowToEnablePictureDebugging());return undefined;}
-var ops=window.chrome.skiaBenchmarking.getOps({skp64:this.skp64_,params:{layer_rect:this.layerRect_.toArray()}});if(!ops)
-console.error('Failed to get picture ops.');return ops;},getOpTimings:function(){if(!PictureSnapshot.CanGetOpTimings()){console.error(PictureSnapshot.HowToEnablePictureDebugging());return undefined;}
-var opTimings=window.chrome.skiaBenchmarking.getOpTimings({skp64:this.skp64_,params:{layer_rect:this.layerRect_.toArray()}});if(!opTimings)
-console.error('Failed to get picture op timings.');return opTimings;},tagOpsWithTimings:function(ops){var opTimings=new Array();for(var iteration=0;iteration<OPS_TIMING_ITERATIONS;iteration++){opTimings[iteration]=this.getOpTimings();if(!opTimings[iteration]||!opTimings[iteration].cmd_times)
-return ops;if(opTimings[iteration].cmd_times.length!=ops.length)
-return ops;}
-for(var opIndex=0;opIndex<ops.length;opIndex++){var min=Number.MAX_VALUE;for(var i=0;i<OPS_TIMING_ITERATIONS;i++)
-min=Math.min(min,opTimings[i].cmd_times[opIndex]);ops[opIndex].cmd_time=min;}
+var ops=window.chrome.skiaBenchmarking.getOps({skp64:this.skp64_,params:{layer_rect:this.layerRect_.toArray()}});if(!ops){console.error('Failed to get picture ops.');}
+return ops;},getOpTimings:function(){if(!PictureSnapshot.CanGetOpTimings()){console.error(PictureSnapshot.HowToEnablePictureDebugging());return undefined;}
+var opTimings=window.chrome.skiaBenchmarking.getOpTimings({skp64:this.skp64_,params:{layer_rect:this.layerRect_.toArray()}});if(!opTimings){console.error('Failed to get picture op timings.');}
+return opTimings;},tagOpsWithTimings:function(ops){var opTimings=[];for(var iteration=0;iteration<OPS_TIMING_ITERATIONS;iteration++){opTimings[iteration]=this.getOpTimings();if(!opTimings[iteration]||!opTimings[iteration].cmd_times){return ops;}
+if(opTimings[iteration].cmd_times.length!==ops.length){return ops;}}
+for(var opIndex=0;opIndex<ops.length;opIndex++){var min=Number.MAX_VALUE;for(var i=0;i<OPS_TIMING_ITERATIONS;i++){min=Math.min(min,opTimings[i].cmd_times[opIndex]);}
+ops[opIndex].cmd_time=min;}
 return ops;},rasterize:function(params,rasterCompleteCallback){if(!PictureSnapshot.CanRasterize()||!PictureSnapshot.CanGetOps()){rasterCompleteCallback(new tr.e.cc.PictureAsImageData(this,tr.e.cc.PictureSnapshot.HowToEnablePictureDebugging()));return;}
 var raster=window.chrome.skiaBenchmarking.rasterize({skp64:this.skp64_,params:{layer_rect:this.layerRect_.toArray()}},{stop:params.stopIndex===undefined?-1:params.stopIndex,overdraw:!!params.showOverdraw,params:{}});if(raster){var canvas=document.createElement('canvas');var ctx=canvas.getContext('2d');canvas.width=raster.width;canvas.height=raster.height;var imageData=ctx.createImageData(raster.width,raster.height);imageData.data.set(new Uint8ClampedArray(raster.data));rasterCompleteCallback(new tr.e.cc.PictureAsImageData(this,imageData));}else{var error='Failed to rasterize picture. '+'Your recording may be from an old Chrome version. '+'The SkPicture format is not backward compatible.';rasterCompleteCallback(new tr.e.cc.PictureAsImageData(this,error));}}};function LayeredPicture(pictures){this.guid_=tr.b.GUID.allocateSimple();this.pictures_=pictures;this.layerRect_=undefined;}
-LayeredPicture.prototype={__proto__:Picture.prototype,get canSave(){return false;},get typeName(){return'cc::LayeredPicture';},get layerRect(){if(this.layerRect_!==undefined)
-return this.layerRect_;this.layerRect_={x:0,y:0,width:0,height:0};for(var i=0;i<this.pictures_.length;++i){var rect=this.pictures_[i].layerRect;this.layerRect_.x=Math.min(this.layerRect_.x,rect.x);this.layerRect_.y=Math.min(this.layerRect_.y,rect.y);this.layerRect_.width=Math.max(this.layerRect_.width,rect.x+rect.width);this.layerRect_.height=Math.max(this.layerRect_.height,rect.y+rect.height);}
-return this.layerRect_;},get guid(){return this.guid_;},getBase64SkpData:function(){throw new Error('Not available with a LayeredPicture.');},getOps:function(){var ops=[];for(var i=0;i<this.pictures_.length;++i)
-ops=ops.concat(this.pictures_[i].getOps());return ops;},getOpTimings:function(){var opTimings=this.pictures_[0].getOpTimings();for(var i=1;i<this.pictures_.length;++i){var timings=this.pictures_[i].getOpTimings();opTimings.cmd_times=opTimings.cmd_times.concat(timings.cmd_times);opTimings.total_time+=timings.total_time;}
-return opTimings;},tagOpsWithTimings:function(ops){var opTimings=new Array();for(var iteration=0;iteration<OPS_TIMING_ITERATIONS;iteration++){opTimings[iteration]=this.getOpTimings();if(!opTimings[iteration]||!opTimings[iteration].cmd_times)
-return ops;}
-for(var opIndex=0;opIndex<ops.length;opIndex++){var min=Number.MAX_VALUE;for(var i=0;i<OPS_TIMING_ITERATIONS;i++)
-min=Math.min(min,opTimings[i].cmd_times[opIndex]);ops[opIndex].cmd_time=min;}
-return ops;},rasterize:function(params,rasterCompleteCallback){this.picturesAsImageData_=[];var rasterCallback=function(pictureAsImageData){this.picturesAsImageData_.push(pictureAsImageData);if(this.picturesAsImageData_.length!==this.pictures_.length)
-return;var canvas=document.createElement('canvas');var ctx=canvas.getContext('2d');canvas.width=this.layerRect.width;canvas.height=this.layerRect.height;for(var i=0;i<this.picturesAsImageData_.length;++i){ctx.putImageData(this.picturesAsImageData_[i].imageData,this.pictures_[i].layerRect.x,this.pictures_[i].layerRect.y);}
-this.picturesAsImageData_=[];rasterCompleteCallback(new tr.e.cc.PictureAsImageData(this,ctx.getImageData(this.layerRect.x,this.layerRect.y,this.layerRect.width,this.layerRect.height)));}.bind(this);for(var i=0;i<this.pictures_.length;++i)
-this.pictures_[i].rasterize(params,rasterCallback);}};function PictureSnapshot(){ObjectSnapshot.apply(this,arguments);}
-PictureSnapshot.HasSkiaBenchmarking=function(){return tr.isExported('chrome.skiaBenchmarking');};PictureSnapshot.CanRasterize=function(){if(!PictureSnapshot.HasSkiaBenchmarking())
-return false;if(!window.chrome.skiaBenchmarking.rasterize)
-return false;return true;};PictureSnapshot.CanGetOps=function(){if(!PictureSnapshot.HasSkiaBenchmarking())
-return false;if(!window.chrome.skiaBenchmarking.getOps)
-return false;return true;};PictureSnapshot.CanGetOpTimings=function(){if(!PictureSnapshot.HasSkiaBenchmarking())
-return false;if(!window.chrome.skiaBenchmarking.getOpTimings)
-return false;return true;};PictureSnapshot.CanGetInfo=function(){if(!PictureSnapshot.HasSkiaBenchmarking())
-return false;if(!window.chrome.skiaBenchmarking.getInfo)
-return false;return true;};PictureSnapshot.HowToEnablePictureDebugging=function(){if(tr.isHeadless)
-return'Pictures only work in chrome';var usualReason=['For pictures to show up, you need to have Chrome running with ','--enable-skia-benchmarking. Please restart chrome with this flag ','and try again.'].join('');if(!tr.isExported('global.chrome.skiaBenchmarking'))
-return usualReason;if(!global.chrome.skiaBenchmarking.rasterize)
-return'Your chrome is old';if(!global.chrome.skiaBenchmarking.getOps)
-return'Your chrome is old: skiaBenchmarking.getOps not found';if(!global.chrome.skiaBenchmarking.getOpTimings)
-return'Your chrome is old: skiaBenchmarking.getOpTimings not found';if(!global.chrome.skiaBenchmarking.getInfo)
-return'Your chrome is old: skiaBenchmarking.getInfo not found';return'Rasterizing is on';};PictureSnapshot.prototype={__proto__:ObjectSnapshot.prototype,preInitialize:function(){tr.e.cc.preInitializeObject(this);this.rasterResult_=undefined;},initialize:function(){if(this.args.alias)
-this.args=this.args.alias.args;if(!this.args.params.layerRect)
-throw new Error('Missing layer rect');this.layerRect_=this.args.params.layerRect;this.picture_=new Picture(this.args.skp64,this.args.params.layerRect);},set picture(picture){this.picture_=picture;},get canSave(){return this.picture_.canSave;},get layerRect(){return this.layerRect_?this.layerRect_:this.picture_.layerRect;},get guid(){return this.picture_.guid;},getBase64SkpData:function(){return this.picture_.getBase64SkpData();},getOps:function(){return this.picture_.getOps();},getOpTimings:function(){return this.picture_.getOpTimings();},tagOpsWithTimings:function(ops){return this.picture_.tagOpsWithTimings(ops);},rasterize:function(params,rasterCompleteCallback){this.picture_.rasterize(params,rasterCompleteCallback);}};ObjectSnapshot.subTypes.register(PictureSnapshot,{typeNames:['cc::Picture']});return{PictureSnapshot:PictureSnapshot,Picture:Picture,LayeredPicture:LayeredPicture};});'use strict';tr.exportTo('tr.e.cc',function(){var ObjectSnapshot=tr.model.ObjectSnapshot;function DisplayItemList(skp64,layerRect){tr.e.cc.Picture.apply(this,arguments);}
+LayeredPicture.prototype={__proto__:Picture.prototype,get canSave(){return false;},get typeName(){return'cc::LayeredPicture';},get layerRect(){if(this.layerRect_!==undefined){return this.layerRect_;}
+this.layerRect_={x:0,y:0,width:0,height:0};for(var i=0;i<this.pictures_.length;++i){var rect=this.pictures_[i].layerRect;this.layerRect_.x=Math.min(this.layerRect_.x,rect.x);this.layerRect_.y=Math.min(this.layerRect_.y,rect.y);this.layerRect_.width=Math.max(this.layerRect_.width,rect.x+rect.width);this.layerRect_.height=Math.max(this.layerRect_.height,rect.y+rect.height);}
+return this.layerRect_;},get guid(){return this.guid_;},getBase64SkpData:function(){throw new Error('Not available with a LayeredPicture.');},getOps:function(){var ops=[];for(var i=0;i<this.pictures_.length;++i){ops=ops.concat(this.pictures_[i].getOps());}
+return ops;},getOpTimings:function(){var opTimings=this.pictures_[0].getOpTimings();for(var i=1;i<this.pictures_.length;++i){var timings=this.pictures_[i].getOpTimings();opTimings.cmd_times=opTimings.cmd_times.concat(timings.cmd_times);opTimings.total_time+=timings.total_time;}
+return opTimings;},tagOpsWithTimings:function(ops){var opTimings=[];for(var iteration=0;iteration<OPS_TIMING_ITERATIONS;iteration++){opTimings[iteration]=this.getOpTimings();if(!opTimings[iteration]||!opTimings[iteration].cmd_times){return ops;}}
+for(var opIndex=0;opIndex<ops.length;opIndex++){var min=Number.MAX_VALUE;for(var i=0;i<OPS_TIMING_ITERATIONS;i++){min=Math.min(min,opTimings[i].cmd_times[opIndex]);}
+ops[opIndex].cmd_time=min;}
+return ops;},rasterize:function(params,rasterCompleteCallback){this.picturesAsImageData_=[];var rasterCallback=function(pictureAsImageData){this.picturesAsImageData_.push(pictureAsImageData);if(this.picturesAsImageData_.length!==this.pictures_.length){return;}
+var canvas=document.createElement('canvas');var ctx=canvas.getContext('2d');canvas.width=this.layerRect.width;canvas.height=this.layerRect.height;for(var i=0;i<this.picturesAsImageData_.length;++i){ctx.putImageData(this.picturesAsImageData_[i].imageData,this.pictures_[i].layerRect.x,this.pictures_[i].layerRect.y);}
+this.picturesAsImageData_=[];rasterCompleteCallback(new tr.e.cc.PictureAsImageData(this,ctx.getImageData(this.layerRect.x,this.layerRect.y,this.layerRect.width,this.layerRect.height)));}.bind(this);for(var i=0;i<this.pictures_.length;++i){this.pictures_[i].rasterize(params,rasterCallback);}}};function PictureSnapshot(){ObjectSnapshot.apply(this,arguments);}
+PictureSnapshot.HasSkiaBenchmarking=function(){return tr.isExported('chrome.skiaBenchmarking');};PictureSnapshot.CanRasterize=function(){if(!PictureSnapshot.HasSkiaBenchmarking()){return false;}
+if(!window.chrome.skiaBenchmarking.rasterize){return false;}
+return true;};PictureSnapshot.CanGetOps=function(){if(!PictureSnapshot.HasSkiaBenchmarking()){return false;}
+if(!window.chrome.skiaBenchmarking.getOps){return false;}
+return true;};PictureSnapshot.CanGetOpTimings=function(){if(!PictureSnapshot.HasSkiaBenchmarking()){return false;}
+if(!window.chrome.skiaBenchmarking.getOpTimings){return false;}
+return true;};PictureSnapshot.CanGetInfo=function(){if(!PictureSnapshot.HasSkiaBenchmarking()){return false;}
+if(!window.chrome.skiaBenchmarking.getInfo){return false;}
+return true;};PictureSnapshot.HowToEnablePictureDebugging=function(){if(tr.isHeadless){return'Pictures only work in chrome';}
+var usualReason=['For pictures to show up, you need to have Chrome running with ','--enable-skia-benchmarking. Please restart chrome with this flag ','and try again.'].join('');if(!tr.isExported('global.chrome.skiaBenchmarking')){return usualReason;}
+if(!global.chrome.skiaBenchmarking.rasterize){return'Your chrome is old';}
+if(!global.chrome.skiaBenchmarking.getOps){return'Your chrome is old: skiaBenchmarking.getOps not found';}
+if(!global.chrome.skiaBenchmarking.getOpTimings){return'Your chrome is old: skiaBenchmarking.getOpTimings not found';}
+if(!global.chrome.skiaBenchmarking.getInfo){return'Your chrome is old: skiaBenchmarking.getInfo not found';}
+return'Rasterizing is on';};PictureSnapshot.prototype={__proto__:ObjectSnapshot.prototype,preInitialize:function(){tr.e.cc.preInitializeObject(this);this.rasterResult_=undefined;},initialize:function(){if(this.args.alias){this.args=this.args.alias.args;}
+if(!this.args.params.layerRect){throw new Error('Missing layer rect');}
+this.layerRect_=this.args.params.layerRect;this.picture_=new Picture(this.args.skp64,this.args.params.layerRect);},set picture(picture){this.picture_=picture;},get canSave(){return this.picture_.canSave;},get layerRect(){return this.layerRect_?this.layerRect_:this.picture_.layerRect;},get guid(){return this.picture_.guid;},getBase64SkpData:function(){return this.picture_.getBase64SkpData();},getOps:function(){return this.picture_.getOps();},getOpTimings:function(){return this.picture_.getOpTimings();},tagOpsWithTimings:function(ops){return this.picture_.tagOpsWithTimings(ops);},rasterize:function(params,rasterCompleteCallback){this.picture_.rasterize(params,rasterCompleteCallback);}};ObjectSnapshot.subTypes.register(PictureSnapshot,{typeNames:['cc::Picture']});return{PictureSnapshot,Picture,LayeredPicture,};});'use strict';tr.exportTo('tr.e.cc',function(){var ObjectSnapshot=tr.model.ObjectSnapshot;function DisplayItemList(skp64,layerRect){tr.e.cc.Picture.apply(this,arguments);}
 DisplayItemList.prototype={__proto__:tr.e.cc.Picture.prototype};function DisplayItemListSnapshot(){tr.e.cc.PictureSnapshot.apply(this,arguments);}
-DisplayItemListSnapshot.prototype={__proto__:tr.e.cc.PictureSnapshot.prototype,initialize:function(){tr.e.cc.PictureSnapshot.prototype.initialize.call(this);this.displayItems_=this.args.params.items;},get items(){return this.displayItems_;}};ObjectSnapshot.subTypes.register(DisplayItemListSnapshot,{typeNames:['cc::DisplayItemList']});return{DisplayItemListSnapshot:DisplayItemListSnapshot,DisplayItemList:DisplayItemList};});'use strict';tr.exportTo('tr.b',function(){function BBox2(){this.isEmpty_=true;this.min_=undefined;this.max_=undefined;};BBox2.prototype={__proto__:Object.prototype,reset:function(){this.isEmpty_=true;this.min_=undefined;this.max_=undefined;},get isEmpty(){return this.isEmpty_;},addBBox2:function(bbox2){if(bbox2.isEmpty)
-return;this.addVec2(bbox2.min_);this.addVec2(bbox2.max_);},clone:function(){var bbox=new BBox2();bbox.addBBox2(this);return bbox;},addXY:function(x,y){if(this.isEmpty_){this.max_=vec2.create();this.min_=vec2.create();vec2.set(this.max_,x,y);vec2.set(this.min_,x,y);this.isEmpty_=false;return;}
+DisplayItemListSnapshot.prototype={__proto__:tr.e.cc.PictureSnapshot.prototype,initialize:function(){tr.e.cc.PictureSnapshot.prototype.initialize.call(this);this.displayItems_=this.args.params.items;},get items(){return this.displayItems_;}};ObjectSnapshot.subTypes.register(DisplayItemListSnapshot,{typeNames:['cc::DisplayItemList']});return{DisplayItemListSnapshot,DisplayItemList,};});'use strict';tr.exportTo('tr.b.math',function(){function BBox2(){this.isEmpty_=true;this.min_=undefined;this.max_=undefined;}
+BBox2.prototype={__proto__:Object.prototype,reset:function(){this.isEmpty_=true;this.min_=undefined;this.max_=undefined;},get isEmpty(){return this.isEmpty_;},addBBox2:function(bbox2){if(bbox2.isEmpty)return;this.addVec2(bbox2.min_);this.addVec2(bbox2.max_);},clone:function(){var bbox=new BBox2();bbox.addBBox2(this);return bbox;},addXY:function(x,y){if(this.isEmpty_){this.max_=vec2.create();this.min_=vec2.create();vec2.set(this.max_,x,y);vec2.set(this.min_,x,y);this.isEmpty_=false;return;}
 this.max_[0]=Math.max(this.max_[0],x);this.max_[1]=Math.max(this.max_[1],y);this.min_[0]=Math.min(this.min_[0],x);this.min_[1]=Math.min(this.min_[1],y);},addVec2:function(value){if(this.isEmpty_){this.max_=vec2.create();this.min_=vec2.create();vec2.set(this.max_,value[0],value[1]);vec2.set(this.min_,value[0],value[1]);this.isEmpty_=false;return;}
-this.max_[0]=Math.max(this.max_[0],value[0]);this.max_[1]=Math.max(this.max_[1],value[1]);this.min_[0]=Math.min(this.min_[0],value[0]);this.min_[1]=Math.min(this.min_[1],value[1]);},addQuad:function(quad){this.addVec2(quad.p1);this.addVec2(quad.p2);this.addVec2(quad.p3);this.addVec2(quad.p4);},get minVec2(){if(this.isEmpty_)
-return undefined;return this.min_;},get maxVec2(){if(this.isEmpty_)
-return undefined;return this.max_;},get sizeAsVec2(){if(this.isEmpty_)
-throw new Error('Empty BBox2 has no size');var size=vec2.create();vec2.subtract(size,this.max_,this.min_);return size;},get size(){if(this.isEmpty_)
-throw new Error('Empty BBox2 has no size');return{width:this.max_[0]-this.min_[0],height:this.max_[1]-this.min_[1]};},get width(){if(this.isEmpty_)
-throw new Error('Empty BBox2 has no width');return this.max_[0]-this.min_[0];},get height(){if(this.isEmpty_)
-throw new Error('Empty BBox2 has no width');return this.max_[1]-this.min_[1];},toString:function(){if(this.isEmpty_)
-return'empty';return'min=('+this.min_[0]+','+this.min_[1]+') '+'max=('+this.max_[0]+','+this.max_[1]+')';},asRect:function(){return tr.b.Rect.fromXYWH(this.min_[0],this.min_[1],this.max_[0]-this.min_[0],this.max_[1]-this.min_[1]);}};return{BBox2:BBox2};});'use strict';tr.exportTo('tr.e.cc',function(){var constants={};constants.ACTIVE_TREE=0;constants.PENDING_TREE=1;constants.HIGH_PRIORITY_BIN=0;constants.LOW_PRIORITY_BIN=1;constants.SEND_BEGIN_FRAME_EVENT='ThreadProxy::ScheduledActionSendBeginMainFrame';constants.BEGIN_MAIN_FRAME_EVENT='ThreadProxy::BeginMainFrame';return{constants:constants};});'use strict';tr.exportTo('tr.e.cc',function(){function Region(){this.rects=[];}
-Region.fromArray=function(array){if(array.length%4!=0)
-throw new Error('Array must consist be a multiple of 4 in length');var r=new Region();for(var i=0;i<array.length;i+=4){r.rects.push(tr.b.Rect.fromXYWH(array[i],array[i+1],array[i+2],array[i+3]));}
-return r;}
-Region.fromArrayOrUndefined=function(array){if(array===undefined)
-return new Region();return Region.fromArray(array);};Region.prototype={__proto__:Region.prototype,rectIntersects:function(r){for(var i=0;i<this.rects.length;i++){if(this.rects[i].intersects(r))
-return true;}
-return false;},addRect:function(r){this.rects.push(r);}};return{Region:Region};});'use strict';tr.exportTo('tr.e.cc',function(){function TileCoverageRect(rect,tile){this.geometryRect=rect;this.tile=tile;}
-return{TileCoverageRect:TileCoverageRect};});'use strict';tr.exportTo('tr.e.cc',function(){var constants=tr.e.cc.constants;var ObjectSnapshot=tr.model.ObjectSnapshot;function LayerImplSnapshot(){ObjectSnapshot.apply(this,arguments);}
-LayerImplSnapshot.prototype={__proto__:ObjectSnapshot.prototype,preInitialize:function(){tr.e.cc.preInitializeObject(this);this.layerTreeImpl_=undefined;this.parentLayer=undefined;},initialize:function(){this.invalidation=new tr.e.cc.Region();this.annotatedInvalidation=new tr.e.cc.Region();this.unrecordedRegion=new tr.e.cc.Region();this.pictures=[];tr.e.cc.moveRequiredFieldsFromArgsToToplevel(this,['layerId','layerQuad']);tr.e.cc.moveOptionalFieldsFromArgsToToplevel(this,['children','maskLayer','replicaLayer','idealContentsScale','geometryContentsScale','layoutRects','usingGpuRasterization']);this.gpuMemoryUsageInBytes=this.args.gpuMemoryUsage;this.bounds=tr.b.Rect.fromXYWH(0,0,this.args.bounds.width,this.args.bounds.height);if(this.args.animationBounds){this.animationBoundsRect=tr.b.Rect.fromXYWH(this.args.animationBounds[0],this.args.animationBounds[1],this.args.animationBounds[3],this.args.animationBounds[4]);}
-if(this.children){for(var i=0;i<this.children.length;i++)
-this.children[i].parentLayer=this;}
-if(this.maskLayer)
-this.maskLayer.parentLayer=this;if(this.replicaLayer)
-this.replicaLayer.parentLayer=this;if(!this.geometryContentsScale)
-this.geometryContentsScale=1.0;if(!this.idealContentsScale)
-this.idealContentsScale=1.0;this.touchEventHandlerRegion=tr.e.cc.Region.fromArrayOrUndefined(this.args.touchEventHandlerRegion);this.wheelEventHandlerRegion=tr.e.cc.Region.fromArrayOrUndefined(this.args.wheelEventHandlerRegion);this.nonFastScrollableRegion=tr.e.cc.Region.fromArrayOrUndefined(this.args.nonFastScrollableRegion);},get layerTreeImpl(){if(this.layerTreeImpl_)
-return this.layerTreeImpl_;if(this.parentLayer)
-return this.parentLayer.layerTreeImpl;return undefined;},set layerTreeImpl(layerTreeImpl){this.layerTreeImpl_=layerTreeImpl;},get activeLayer(){if(this.layerTreeImpl.whichTree==constants.ACTIVE_TREE)
-return this;var activeTree=this.layerTreeImpl.layerTreeHostImpl.activeTree;return activeTree.findLayerWithId(this.layerId);},get pendingLayer(){if(this.layerTreeImpl.whichTree==constants.PENDING_TREE)
-return this;var pendingTree=this.layerTreeImpl.layerTreeHostImpl.pendingTree;return pendingTree.findLayerWithId(this.layerId);}};function PictureLayerImplSnapshot(){LayerImplSnapshot.apply(this,arguments);}
+this.max_[0]=Math.max(this.max_[0],value[0]);this.max_[1]=Math.max(this.max_[1],value[1]);this.min_[0]=Math.min(this.min_[0],value[0]);this.min_[1]=Math.min(this.min_[1],value[1]);},addQuad:function(quad){this.addVec2(quad.p1);this.addVec2(quad.p2);this.addVec2(quad.p3);this.addVec2(quad.p4);},get minVec2(){if(this.isEmpty_)return undefined;return this.min_;},get maxVec2(){if(this.isEmpty_)return undefined;return this.max_;},get sizeAsVec2(){if(this.isEmpty_){throw new Error('Empty BBox2 has no size');}
+var size=vec2.create();vec2.subtract(size,this.max_,this.min_);return size;},get size(){if(this.isEmpty_){throw new Error('Empty BBox2 has no size');}
+return{width:this.max_[0]-this.min_[0],height:this.max_[1]-this.min_[1]};},get width(){if(this.isEmpty_){throw new Error('Empty BBox2 has no width');}
+return this.max_[0]-this.min_[0];},get height(){if(this.isEmpty_){throw new Error('Empty BBox2 has no width');}
+return this.max_[1]-this.min_[1];},toString:function(){if(this.isEmpty_)return'empty';return'min=('+this.min_[0]+','+this.min_[1]+') '+'max=('+this.max_[0]+','+this.max_[1]+')';},asRect:function(){return tr.b.math.Rect.fromXYWH(this.min_[0],this.min_[1],this.max_[0]-this.min_[0],this.max_[1]-this.min_[1]);}};return{BBox2,};});'use strict';tr.exportTo('tr.e.cc',function(){var constants={};constants.ACTIVE_TREE=0;constants.PENDING_TREE=1;constants.HIGH_PRIORITY_BIN=0;constants.LOW_PRIORITY_BIN=1;constants.SEND_BEGIN_FRAME_EVENT='ThreadProxy::ScheduledActionSendBeginMainFrame';constants.BEGIN_MAIN_FRAME_EVENT='ThreadProxy::BeginMainFrame';return{constants:constants};});'use strict';tr.exportTo('tr.e.cc',function(){function Region(){this.rects=[];}
+Region.fromArray=function(array){if(array.length%4!==0){throw new Error('Array must consist be a multiple of 4 in length');}
+var r=new Region();for(var i=0;i<array.length;i+=4){r.rects.push(tr.b.math.Rect.fromXYWH(array[i],array[i+1],array[i+2],array[i+3]));}
+return r;};Region.fromArrayOrUndefined=function(array){if(array===undefined)return new Region();return Region.fromArray(array);};Region.prototype={__proto__:Region.prototype,rectIntersects:function(r){for(var i=0;i<this.rects.length;i++){if(this.rects[i].intersects(r))return true;}
+return false;},addRect:function(r){this.rects.push(r);}};return{Region,};});'use strict';tr.exportTo('tr.e.cc',function(){function TileCoverageRect(rect,tile){this.geometryRect=rect;this.tile=tile;}
+return{TileCoverageRect,};});'use strict';tr.exportTo('tr.e.cc',function(){var constants=tr.e.cc.constants;var ObjectSnapshot=tr.model.ObjectSnapshot;function LayerImplSnapshot(){ObjectSnapshot.apply(this,arguments);}
+LayerImplSnapshot.prototype={__proto__:ObjectSnapshot.prototype,preInitialize:function(){tr.e.cc.preInitializeObject(this);this.layerTreeImpl_=undefined;this.parentLayer=undefined;},initialize:function(){this.invalidation=new tr.e.cc.Region();this.annotatedInvalidation=new tr.e.cc.Region();this.unrecordedRegion=new tr.e.cc.Region();this.pictures=[];tr.e.cc.moveRequiredFieldsFromArgsToToplevel(this,['layerId','layerQuad']);tr.e.cc.moveOptionalFieldsFromArgsToToplevel(this,['children','maskLayer','replicaLayer','idealContentsScale','geometryContentsScale','layoutRects','usingGpuRasterization']);this.gpuMemoryUsageInBytes=this.args.gpuMemoryUsage;this.bounds=tr.b.math.Rect.fromXYWH(0,0,this.args.bounds.width,this.args.bounds.height);if(this.args.animationBounds){this.animationBoundsRect=tr.b.math.Rect.fromXYWH(this.args.animationBounds[0],this.args.animationBounds[1],this.args.animationBounds[3],this.args.animationBounds[4]);}
+if(this.children){for(var i=0;i<this.children.length;i++){this.children[i].parentLayer=this;}}
+if(this.maskLayer){this.maskLayer.parentLayer=this;}
+if(this.replicaLayer){this.replicaLayer.parentLayer=this;}
+if(!this.geometryContentsScale){this.geometryContentsScale=1.0;}
+if(!this.idealContentsScale){this.idealContentsScale=1.0;}
+this.touchEventHandlerRegion=tr.e.cc.Region.fromArrayOrUndefined(this.args.touchEventHandlerRegion);this.wheelEventHandlerRegion=tr.e.cc.Region.fromArrayOrUndefined(this.args.wheelEventHandlerRegion);this.nonFastScrollableRegion=tr.e.cc.Region.fromArrayOrUndefined(this.args.nonFastScrollableRegion);},get layerTreeImpl(){if(this.layerTreeImpl_){return this.layerTreeImpl_;}
+if(this.parentLayer){return this.parentLayer.layerTreeImpl;}
+return undefined;},set layerTreeImpl(layerTreeImpl){this.layerTreeImpl_=layerTreeImpl;},get activeLayer(){if(this.layerTreeImpl.whichTree===constants.ACTIVE_TREE){return this;}
+var activeTree=this.layerTreeImpl.layerTreeHostImpl.activeTree;return activeTree.findLayerWithId(this.layerId);},get pendingLayer(){if(this.layerTreeImpl.whichTree===constants.PENDING_TREE){return this;}
+var pendingTree=this.layerTreeImpl.layerTreeHostImpl.pendingTree;return pendingTree.findLayerWithId(this.layerId);}};function PictureLayerImplSnapshot(){LayerImplSnapshot.apply(this,arguments);}
 PictureLayerImplSnapshot.prototype={__proto__:LayerImplSnapshot.prototype,initialize:function(){LayerImplSnapshot.prototype.initialize.call(this);if(this.args.invalidation){this.invalidation=tr.e.cc.Region.fromArray(this.args.invalidation);delete this.args.invalidation;}
 if(this.args.annotatedInvalidationRects){this.annotatedInvalidation=new tr.e.cc.Region();for(var i=0;i<this.args.annotatedInvalidationRects.length;++i){var annotatedRect=this.args.annotatedInvalidationRects[i];var rect=annotatedRect.geometryRect;rect.reason=annotatedRect.reason;this.annotatedInvalidation.addRect(rect);}
 delete this.args.annotatedInvalidationRects;}
 if(this.args.unrecordedRegion){this.unrecordedRegion=tr.e.cc.Region.fromArray(this.args.unrecordedRegion);delete this.args.unrecordedRegion;}
 if(this.args.pictures){this.pictures=this.args.pictures;this.pictures.sort(function(a,b){return a.ts-b.ts;});}
 this.tileCoverageRects=[];if(this.args.coverageTiles){for(var i=0;i<this.args.coverageTiles.length;++i){var rect=this.args.coverageTiles[i].geometryRect.scale(this.idealContentsScale);var tile=this.args.coverageTiles[i].tile;this.tileCoverageRects.push(new tr.e.cc.TileCoverageRect(rect,tile));}
-delete this.args.coverageTiles;}}};ObjectSnapshot.subTypes.register(PictureLayerImplSnapshot,{typeName:'cc::PictureLayerImpl'});ObjectSnapshot.subTypes.register(LayerImplSnapshot,{typeNames:['cc::LayerImpl','cc::DelegatedRendererLayerImpl','cc::HeadsUpDisplayLayerImpl','cc::IOSurfaceLayerImpl','cc::NinePatchLayerImpl','cc::PictureImageLayerImpl','cc::ScrollbarLayerImpl','cc::SolidColorLayerImpl','cc::SolidColorScrollbarLayerImpl','cc::SurfaceLayerImpl','cc::TextureLayerImpl','cc::TiledLayerImpl','cc::VideoLayerImpl','cc::PaintedScrollbarLayerImpl','ClankPatchLayer','TabBorderLayer','CounterLayer']});return{LayerImplSnapshot:LayerImplSnapshot,PictureLayerImplSnapshot:PictureLayerImplSnapshot};});'use strict';tr.exportTo('tr.e.cc',function(){var constants=tr.e.cc.constants;var ObjectSnapshot=tr.model.ObjectSnapshot;function LayerTreeImplSnapshot(){ObjectSnapshot.apply(this,arguments);}
-LayerTreeImplSnapshot.prototype={__proto__:ObjectSnapshot.prototype,preInitialize:function(){tr.e.cc.preInitializeObject(this);this.layerTreeHostImpl=undefined;this.whichTree=undefined;this.sourceFrameNumber=undefined;},initialize:function(){tr.e.cc.moveRequiredFieldsFromArgsToToplevel(this,['renderSurfaceLayerList']);tr.e.cc.moveOptionalFieldsFromArgsToToplevel(this,['rootLayer','layers']);if(this.args.sourceFrameNumber)
-this.sourceFrameNumber=this.args.sourceFrameNumber;if(this.rootLayer){this.rootLayer.layerTreeImpl=this;}else{for(var i=0;i<this.layers.length;i++){this.layers[i].layerTreeImpl=this;}}
-if(this.args.swapPromiseTraceIds&&this.args.swapPromiseTraceIds.length){this.tracedInputLatencies=[];var ownProcess=this.objectInstance.parent;var modelHelper=ownProcess.model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(modelHelper)
-this._initializeTracedInputLatencies(modelHelper);}},_initializeTracedInputLatencies:function(modelHelper){var latencyEvents=modelHelper.browserHelper.getLatencyEventsInRange(modelHelper.model.bounds);latencyEvents.forEach(function(event){for(var i=0;i<this.args.swapPromiseTraceIds.length;i++){if(!event.args.data||!event.args.data.trace_id)
-continue;if(parseInt(event.args.data.trace_id)===this.args.swapPromiseTraceIds[i])
-this.tracedInputLatencies.push(event);}},this);},get hasSourceFrameBeenDrawnBefore(){if(this.whichTree==tr.e.cc.constants.PENDING_TREE)
-return false;if(this.sourceFrameNumber===undefined)
-return;var thisLTHI=this.layerTreeHostImpl;var thisLTHIIndex=thisLTHI.objectInstance.snapshots.indexOf(thisLTHI);var prevLTHIIndex=thisLTHIIndex-1;if(prevLTHIIndex<0||prevLTHIIndex>=thisLTHI.objectInstance.snapshots.length)
-return false;var prevLTHI=thisLTHI.objectInstance.snapshots[prevLTHIIndex];if(!prevLTHI.activeTree)
-return false;if(prevLTHI.activeTree.sourceFrameNumber===undefined)
-return;return prevLTHI.activeTree.sourceFrameNumber==this.sourceFrameNumber;},get otherTree(){var other=this.whichTree==constants.ACTIVE_TREE?constants.PENDING_TREE:constants.ACTIVE_TREE;return this.layerTreeHostImpl.getTree(other);},get gpuMemoryUsageInBytes(){var totalBytes=0;this.iterLayers(function(layer){if(layer.gpuMemoryUsageInBytes!==undefined)
-totalBytes+=layer.gpuMemoryUsageInBytes;});return totalBytes;},iterLayers:function(func,thisArg){var visitedLayers={};function visitLayer(layer,depth,isMask,isReplica){if(visitedLayers[layer.layerId])
-return;visitedLayers[layer.layerId]=true;func.call(thisArg,layer,depth,isMask,isReplica);if(layer.children){for(var i=0;i<layer.children.length;i++)
-visitLayer(layer.children[i],depth+1);}
-if(layer.maskLayer)
-visitLayer(layer.maskLayer,depth+1,true,false);if(layer.replicaLayer)
-visitLayer(layer.replicaLayer,depth+1,false,true);}
-if(this.rootLayer){visitLayer(this.rootLayer,0,false,false);}else{for(var i=0;i<this.layers.length;i++)
-visitLayer(this.layers[i],0,false,false);}},findLayerWithId:function(id){var foundLayer=undefined;function visitLayer(layer){if(layer.layerId==id)
-foundLayer=layer;}
-this.iterLayers(visitLayer);return foundLayer;}};ObjectSnapshot.subTypes.register(LayerTreeImplSnapshot,{typeName:'cc::LayerTreeImpl'});return{LayerTreeImplSnapshot:LayerTreeImplSnapshot};});'use strict';tr.exportTo('tr.e.cc',function(){var constants=tr.e.cc.constants;var ObjectSnapshot=tr.model.ObjectSnapshot;var ObjectInstance=tr.model.ObjectInstance;function LayerTreeHostImplSnapshot(){ObjectSnapshot.apply(this,arguments);}
+delete this.args.coverageTiles;}}};ObjectSnapshot.subTypes.register(PictureLayerImplSnapshot,{typeName:'cc::PictureLayerImpl'});ObjectSnapshot.subTypes.register(LayerImplSnapshot,{typeNames:['cc::LayerImpl','cc::DelegatedRendererLayerImpl','cc::HeadsUpDisplayLayerImpl','cc::IOSurfaceLayerImpl','cc::NinePatchLayerImpl','cc::PictureImageLayerImpl','cc::ScrollbarLayerImpl','cc::SolidColorLayerImpl','cc::SolidColorScrollbarLayerImpl','cc::SurfaceLayerImpl','cc::TextureLayerImpl','cc::TiledLayerImpl','cc::VideoLayerImpl','cc::PaintedScrollbarLayerImpl','ClankPatchLayer','TabBorderLayer','CounterLayer']});return{LayerImplSnapshot,PictureLayerImplSnapshot,};});'use strict';tr.exportTo('tr.e.cc',function(){var constants=tr.e.cc.constants;var ObjectSnapshot=tr.model.ObjectSnapshot;function LayerTreeImplSnapshot(){ObjectSnapshot.apply(this,arguments);}
+LayerTreeImplSnapshot.prototype={__proto__:ObjectSnapshot.prototype,preInitialize:function(){tr.e.cc.preInitializeObject(this);this.layerTreeHostImpl=undefined;this.whichTree=undefined;this.sourceFrameNumber=undefined;},initialize:function(){tr.e.cc.moveRequiredFieldsFromArgsToToplevel(this,['renderSurfaceLayerList']);tr.e.cc.moveOptionalFieldsFromArgsToToplevel(this,['rootLayer','layers']);if(this.args.sourceFrameNumber){this.sourceFrameNumber=this.args.sourceFrameNumber;}
+if(this.rootLayer){this.rootLayer.layerTreeImpl=this;}else{for(var i=0;i<this.layers.length;i++){this.layers[i].layerTreeImpl=this;}}
+if(this.args.swapPromiseTraceIds&&this.args.swapPromiseTraceIds.length){this.tracedInputLatencies=[];var ownProcess=this.objectInstance.parent;var modelHelper=ownProcess.model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(modelHelper){this._initializeTracedInputLatencies(modelHelper);}}},_initializeTracedInputLatencies:function(modelHelper){var latencyEvents=modelHelper.browserHelper.getLatencyEventsInRange(modelHelper.model.bounds);latencyEvents.forEach(function(event){for(var i=0;i<this.args.swapPromiseTraceIds.length;i++){if(!event.args.data||!event.args.data.trace_id){continue;}
+if(parseInt(event.args.data.trace_id)===this.args.swapPromiseTraceIds[i]){this.tracedInputLatencies.push(event);}}},this);},get hasSourceFrameBeenDrawnBefore(){if(this.whichTree===tr.e.cc.constants.PENDING_TREE){return false;}
+if(this.sourceFrameNumber===undefined)return;var thisLTHI=this.layerTreeHostImpl;var thisLTHIIndex=thisLTHI.objectInstance.snapshots.indexOf(thisLTHI);var prevLTHIIndex=thisLTHIIndex-1;if(prevLTHIIndex<0||prevLTHIIndex>=thisLTHI.objectInstance.snapshots.length){return false;}
+var prevLTHI=thisLTHI.objectInstance.snapshots[prevLTHIIndex];if(!prevLTHI.activeTree)return false;if(prevLTHI.activeTree.sourceFrameNumber===undefined)return;return prevLTHI.activeTree.sourceFrameNumber===this.sourceFrameNumber;},get otherTree(){var other=this.whichTree===constants.ACTIVE_TREE?constants.PENDING_TREE:constants.ACTIVE_TREE;return this.layerTreeHostImpl.getTree(other);},get gpuMemoryUsageInBytes(){var totalBytes=0;this.iterLayers(function(layer){if(layer.gpuMemoryUsageInBytes!==undefined){totalBytes+=layer.gpuMemoryUsageInBytes;}});return totalBytes;},iterLayers:function(func,thisArg){var visitedLayers={};function visitLayer(layer,depth,isMask,isReplica){if(visitedLayers[layer.layerId])return;visitedLayers[layer.layerId]=true;func.call(thisArg,layer,depth,isMask,isReplica);if(layer.children){for(var i=0;i<layer.children.length;i++){visitLayer(layer.children[i],depth+1);}}
+if(layer.maskLayer){visitLayer(layer.maskLayer,depth+1,true,false);}
+if(layer.replicaLayer){visitLayer(layer.replicaLayer,depth+1,false,true);}}
+if(this.rootLayer){visitLayer(this.rootLayer,0,false,false);}else{for(var i=0;i<this.layers.length;i++){visitLayer(this.layers[i],0,false,false);}}},findLayerWithId:function(id){var foundLayer=undefined;function visitLayer(layer){if(layer.layerId===id){foundLayer=layer;}}
+this.iterLayers(visitLayer);return foundLayer;}};ObjectSnapshot.subTypes.register(LayerTreeImplSnapshot,{typeName:'cc::LayerTreeImpl'});return{LayerTreeImplSnapshot,};});'use strict';tr.exportTo('tr.e.cc',function(){var constants=tr.e.cc.constants;var ObjectSnapshot=tr.model.ObjectSnapshot;var ObjectInstance=tr.model.ObjectInstance;function LayerTreeHostImplSnapshot(){ObjectSnapshot.apply(this,arguments);}
 LayerTreeHostImplSnapshot.prototype={__proto__:ObjectSnapshot.prototype,preInitialize:function(){tr.e.cc.preInitializeObject(this);},initialize:function(){tr.e.cc.moveRequiredFieldsFromArgsToToplevel(this,['deviceViewportSize','activeTree']);tr.e.cc.moveOptionalFieldsFromArgsToToplevel(this,['pendingTree']);if(this.args.activeTiles!==undefined){this.activeTiles=this.args.activeTiles;delete this.args.activeTiles;}else if(this.args.tiles!==undefined){this.activeTiles=this.args.tiles;delete this.args.tiles;}
-if(!this.activeTiles)
-this.activeTiles=[];this.activeTree.layerTreeHostImpl=this;this.activeTree.whichTree=constants.ACTIVE_TREE;if(this.pendingTree){this.pendingTree.layerTreeHostImpl=this;this.pendingTree.whichTree=constants.PENDING_TREE;}},getContentsScaleNames:function(){var scales={};for(var i=0;i<this.activeTiles.length;++i){var tile=this.activeTiles[i];scales[tile.contentsScale]=tile.resolution;}
-return scales;},getTree:function(whichTree){if(whichTree==constants.ACTIVE_TREE)
-return this.activeTree;if(whichTree==constants.PENDING_TREE)
-return this.pendingTree;throw new Exception('Unknown tree type + '+whichTree);},get tilesHaveGpuMemoryUsageInfo(){if(this.tilesHaveGpuMemoryUsageInfo_!==undefined)
-return this.tilesHaveGpuMemoryUsageInfo_;for(var i=0;i<this.activeTiles.length;i++){if(this.activeTiles[i].gpuMemoryUsageInBytes===undefined)
-continue;this.tilesHaveGpuMemoryUsageInfo_=true;return true;}
-this.tilesHaveGpuMemoryUsageInfo_=false;return false;},get gpuMemoryUsageInBytes(){if(!this.tilesHaveGpuMemoryUsageInfo)
-return;var usage=0;for(var i=0;i<this.activeTiles.length;i++){var u=this.activeTiles[i].gpuMemoryUsageInBytes;if(u!==undefined)
-usage+=u;}
-return usage;},get userFriendlyName(){var frameNumber;if(!this.activeTree){frameNumber=this.objectInstance.snapshots.indexOf(this);}else{if(this.activeTree.sourceFrameNumber===undefined)
-frameNumber=this.objectInstance.snapshots.indexOf(this);else
-frameNumber=this.activeTree.sourceFrameNumber;}
+if(!this.activeTiles){this.activeTiles=[];}
+this.activeTree.layerTreeHostImpl=this;this.activeTree.whichTree=constants.ACTIVE_TREE;if(this.pendingTree){this.pendingTree.layerTreeHostImpl=this;this.pendingTree.whichTree=constants.PENDING_TREE;}},getContentsScaleNames:function(){var scales={};for(var i=0;i<this.activeTiles.length;++i){var tile=this.activeTiles[i];scales[tile.contentsScale]=tile.resolution;}
+return scales;},getTree:function(whichTree){if(whichTree===constants.ACTIVE_TREE){return this.activeTree;}
+if(whichTree===constants.PENDING_TREE){return this.pendingTree;}
+throw new Exception('Unknown tree type + '+whichTree);},get tilesHaveGpuMemoryUsageInfo(){if(this.tilesHaveGpuMemoryUsageInfo_!==undefined){return this.tilesHaveGpuMemoryUsageInfo_;}
+for(var i=0;i<this.activeTiles.length;i++){if(this.activeTiles[i].gpuMemoryUsageInBytes===undefined){continue;}
+this.tilesHaveGpuMemoryUsageInfo_=true;return true;}
+this.tilesHaveGpuMemoryUsageInfo_=false;return false;},get gpuMemoryUsageInBytes(){if(!this.tilesHaveGpuMemoryUsageInfo)return;var usage=0;for(var i=0;i<this.activeTiles.length;i++){var u=this.activeTiles[i].gpuMemoryUsageInBytes;if(u!==undefined)usage+=u;}
+return usage;},get userFriendlyName(){var frameNumber;if(!this.activeTree){frameNumber=this.objectInstance.snapshots.indexOf(this);}else{if(this.activeTree.sourceFrameNumber===undefined){frameNumber=this.objectInstance.snapshots.indexOf(this);}else{frameNumber=this.activeTree.sourceFrameNumber;}}
 return'cc::LayerTreeHostImpl frame '+frameNumber;}};ObjectSnapshot.subTypes.register(LayerTreeHostImplSnapshot,{typeName:'cc::LayerTreeHostImpl'});function LayerTreeHostImplInstance(){ObjectInstance.apply(this,arguments);this.allLayersBBox_=undefined;}
-LayerTreeHostImplInstance.prototype={__proto__:ObjectInstance.prototype,get allContentsScales(){if(this.allContentsScales_)
-return this.allContentsScales_;var scales={};for(var tileID in this.allTileHistories_){var tileHistory=this.allTileHistories_[tileID];scales[tileHistory.contentsScale]=true;}
-this.allContentsScales_=tr.b.dictionaryKeys(scales);return this.allContentsScales_;},get allLayersBBox(){if(this.allLayersBBox_)
-return this.allLayersBBox_;var bbox=new tr.b.BBox2();function handleTree(tree){tree.renderSurfaceLayerList.forEach(function(layer){bbox.addQuad(layer.layerQuad);});}
-this.snapshots.forEach(function(lthi){handleTree(lthi.activeTree);if(lthi.pendingTree)
-handleTree(lthi.pendingTree);});this.allLayersBBox_=bbox;return this.allLayersBBox_;}};ObjectInstance.subTypes.register(LayerTreeHostImplInstance,{typeName:'cc::LayerTreeHostImpl'});return{LayerTreeHostImplSnapshot:LayerTreeHostImplSnapshot,LayerTreeHostImplInstance:LayerTreeHostImplInstance};});'use strict';tr.exportTo('tr.e.cc',function(){var tileTypes={highRes:'highRes',lowRes:'lowRes',extraHighRes:'extraHighRes',extraLowRes:'extraLowRes',missing:'missing',culled:'culled',solidColor:'solidColor',picture:'picture',directPicture:'directPicture',unknown:'unknown'};var tileBorder={highRes:{color:'rgba(80, 200, 200, 0.7)',width:1},lowRes:{color:'rgba(212, 83, 192, 0.7)',width:2},extraHighRes:{color:'rgba(239, 231, 20, 0.7)',width:2},extraLowRes:{color:'rgba(93, 186, 18, 0.7)',width:2},missing:{color:'rgba(255, 0, 0, 0.7)',width:1},culled:{color:'rgba(160, 100, 0, 0.8)',width:1},solidColor:{color:'rgba(128, 128, 128, 0.7)',width:1},picture:{color:'rgba(64, 64, 64, 0.7)',width:1},directPicture:{color:'rgba(127, 255, 0, 1.0)',width:1},unknown:{color:'rgba(0, 0, 0, 1.0)',width:2}};return{tileTypes:tileTypes,tileBorder:tileBorder};});'use strict';tr.exportTo('tr.e.cc',function(){var ObjectSnapshot=tr.model.ObjectSnapshot;function TileSnapshot(){ObjectSnapshot.apply(this,arguments);}
+LayerTreeHostImplInstance.prototype={__proto__:ObjectInstance.prototype,get allContentsScales(){if(this.allContentsScales_){return this.allContentsScales_;}
+var scales={};for(var tileID in this.allTileHistories_){var tileHistory=this.allTileHistories_[tileID];scales[tileHistory.contentsScale]=true;}
+this.allContentsScales_=Object.keys(scales);return this.allContentsScales_;},get allLayersBBox(){if(this.allLayersBBox_){return this.allLayersBBox_;}
+var bbox=new tr.b.math.BBox2();function handleTree(tree){tree.renderSurfaceLayerList.forEach(function(layer){bbox.addQuad(layer.layerQuad);});}
+this.snapshots.forEach(function(lthi){handleTree(lthi.activeTree);if(lthi.pendingTree){handleTree(lthi.pendingTree);}});this.allLayersBBox_=bbox;return this.allLayersBBox_;}};ObjectInstance.subTypes.register(LayerTreeHostImplInstance,{typeName:'cc::LayerTreeHostImpl'});return{LayerTreeHostImplSnapshot,LayerTreeHostImplInstance,};});'use strict';tr.exportTo('tr.e.cc',function(){var tileTypes={highRes:'highRes',lowRes:'lowRes',extraHighRes:'extraHighRes',extraLowRes:'extraLowRes',missing:'missing',culled:'culled',solidColor:'solidColor',picture:'picture',directPicture:'directPicture',unknown:'unknown'};var tileBorder={highRes:{color:'rgba(80, 200, 200, 0.7)',width:1},lowRes:{color:'rgba(212, 83, 192, 0.7)',width:2},extraHighRes:{color:'rgba(239, 231, 20, 0.7)',width:2},extraLowRes:{color:'rgba(93, 186, 18, 0.7)',width:2},missing:{color:'rgba(255, 0, 0, 0.7)',width:1},culled:{color:'rgba(160, 100, 0, 0.8)',width:1},solidColor:{color:'rgba(128, 128, 128, 0.7)',width:1},picture:{color:'rgba(64, 64, 64, 0.7)',width:1},directPicture:{color:'rgba(127, 255, 0, 1.0)',width:1},unknown:{color:'rgba(0, 0, 0, 1.0)',width:2}};return{tileTypes:tileTypes,tileBorder:tileBorder};});'use strict';tr.exportTo('tr.e.cc',function(){var ObjectSnapshot=tr.model.ObjectSnapshot;function TileSnapshot(){ObjectSnapshot.apply(this,arguments);}
 TileSnapshot.prototype={__proto__:ObjectSnapshot.prototype,preInitialize:function(){tr.e.cc.preInitializeObject(this);},initialize:function(){tr.e.cc.moveOptionalFieldsFromArgsToToplevel(this,['layerId','contentsScale','contentRect']);if(this.args.managedState){this.resolution=this.args.managedState.resolution;this.isSolidColor=this.args.managedState.isSolidColor;this.isUsingGpuMemory=this.args.managedState.isUsingGpuMemory;this.hasResource=this.args.managedState.hasResource;this.scheduledPriority=this.args.scheduledPriority;this.gpuMemoryUsageInBytes=this.args.gpuMemoryUsage;}else{this.resolution=this.args.resolution;this.isSolidColor=this.args.drawInfo.isSolidColor;this.isUsingGpuMemory=this.args.isUsingGpuMemory;this.hasResource=this.args.hasResource;this.scheduledPriority=this.args.scheduledPriority;this.gpuMemoryUsageInBytes=this.args.gpuMemoryUsage;}
-if(this.contentRect)
-this.layerRect=this.contentRect.scale(1.0/this.contentsScale);if(this.isSolidColor)
-this.type_=tr.e.cc.tileTypes.solidColor;else if(!this.hasResource)
-this.type_=tr.e.cc.tileTypes.missing;else if(this.resolution==='HIGH_RESOLUTION')
-this.type_=tr.e.cc.tileTypes.highRes;else if(this.resolution==='LOW_RESOLUTION')
-this.type_=tr.e.cc.tileTypes.lowRes;else
-this.type_=tr.e.cc.tileTypes.unknown;},getTypeForLayer:function(layer){var type=this.type_;if(type==tr.e.cc.tileTypes.unknown){if(this.contentsScale<layer.idealContentsScale)
-type=tr.e.cc.tileTypes.extraLowRes;else if(this.contentsScale>layer.idealContentsScale)
-type=tr.e.cc.tileTypes.extraHighRes;}
-return type;}};ObjectSnapshot.subTypes.register(TileSnapshot,{typeName:'cc::Tile'});return{TileSnapshot:TileSnapshot};});'use strict';tr.exportTo('tr.ui.b',function(){var Location=tr.model.Location;function UIState(location,scaleX){this.location_=location;this.scaleX_=scaleX;};UIState.fromUserFriendlyString=function(model,viewport,stateString){var navByFinderPattern=/^(-?\d+(\.\d+)?)@(.+)x(\d+(\.\d+)?)$/g;var match=navByFinderPattern.exec(stateString);if(!match)
-return;var timestamp=parseFloat(match[1]);var stableId=match[3];var scaleX=parseFloat(match[4]);if(scaleX<=0)
-throw new Error('Invalid ScaleX value in UI State string.');if(!viewport.containerToTrackMap.getTrackByStableId(stableId))
-throw new Error('Invalid StableID given in UI State String.');var loc=tr.model.Location.fromStableIdAndTimestamp(viewport,stableId,timestamp);return new UIState(loc,scaleX);}
-UIState.prototype={get location(){return this.location_;},get scaleX(){return this.scaleX_;},toUserFriendlyString:function(viewport){var timestamp=this.location_.xWorld;var stableId=this.location_.getContainingTrack(viewport).eventContainer.stableId;var scaleX=this.scaleX_;return timestamp.toFixed(5)+'@'+stableId+'x'+scaleX.toFixed(5);},toDict:function(){return{location:this.location_.toDict(),scaleX:this.scaleX_};}};return{UIState:UIState};});'use strict';tr.exportTo('tr.ui.b',function(){var EventSet=tr.model.EventSet;var SelectionState=tr.model.SelectionState;function BrushingState(){this.guid_=tr.b.GUID.allocateSimple();this.selection_=new EventSet();this.findMatches_=new EventSet();this.analysisViewRelatedEvents_=new EventSet();this.analysisLinkHoveredEvents_=new EventSet();this.appliedToModel_=undefined;this.viewSpecificBrushingStates_={};}
-BrushingState.prototype={get guid(){return this.guid_;},clone:function(){var that=new BrushingState();that.selection_=this.selection_;that.findMatches_=this.findMatches_;that.analysisViewRelatedEvents_=this.analysisViewRelatedEvents_;that.analysisLinkHoveredEvents_=this.analysisLinkHoveredEvents_;that.viewSpecificBrushingStates_=this.viewSpecificBrushingStates_;return that;},equals:function(that){if(!this.selection_.equals(that.selection_))
-return false;if(!this.findMatches_.equals(that.findMatches_))
-return false;if(!this.analysisViewRelatedEvents_.equals(that.analysisViewRelatedEvents_)){return false;}
+if(this.contentRect){this.layerRect=this.contentRect.scale(1.0/this.contentsScale);}
+if(this.isSolidColor){this.type_=tr.e.cc.tileTypes.solidColor;}else if(!this.hasResource){this.type_=tr.e.cc.tileTypes.missing;}else if(this.resolution==='HIGH_RESOLUTION'){this.type_=tr.e.cc.tileTypes.highRes;}else if(this.resolution==='LOW_RESOLUTION'){this.type_=tr.e.cc.tileTypes.lowRes;}else{this.type_=tr.e.cc.tileTypes.unknown;}},getTypeForLayer:function(layer){var type=this.type_;if(type===tr.e.cc.tileTypes.unknown){if(this.contentsScale<layer.idealContentsScale){type=tr.e.cc.tileTypes.extraLowRes;}else if(this.contentsScale>layer.idealContentsScale){type=tr.e.cc.tileTypes.extraHighRes;}}
+return type;}};ObjectSnapshot.subTypes.register(TileSnapshot,{typeName:'cc::Tile'});return{TileSnapshot,};});'use strict';tr.exportTo('tr.ui.b',function(){var Location=tr.model.Location;function UIState(location,scaleX){this.location_=location;this.scaleX_=scaleX;}
+UIState.fromUserFriendlyString=function(model,viewport,stateString){var navByFinderPattern=/^(-?\d+(\.\d+)?)@(.+)x(\d+(\.\d+)?)$/g;var match=navByFinderPattern.exec(stateString);if(!match)return;var timestamp=parseFloat(match[1]);var stableId=match[3];var scaleX=parseFloat(match[4]);if(scaleX<=0){throw new Error('Invalid ScaleX value in UI State string.');}
+if(!viewport.containerToTrackMap.getTrackByStableId(stableId)){throw new Error('Invalid StableID given in UI State String.');}
+var loc=tr.model.Location.fromStableIdAndTimestamp(viewport,stableId,timestamp);return new UIState(loc,scaleX);};UIState.prototype={get location(){return this.location_;},get scaleX(){return this.scaleX_;},toUserFriendlyString:function(viewport){var timestamp=this.location_.xWorld;var stableId=this.location_.getContainingTrack(viewport).eventContainer.stableId;var scaleX=this.scaleX_;return timestamp.toFixed(5)+'@'+stableId+'x'+scaleX.toFixed(5);},toDict:function(){return{location:this.location_.toDict(),scaleX:this.scaleX_};}};return{UIState,};});'use strict';tr.exportTo('tr.ui.b',function(){var EventSet=tr.model.EventSet;var SelectionState=tr.model.SelectionState;function BrushingState(){this.guid_=tr.b.GUID.allocateSimple();this.selection_=new EventSet();this.findMatches_=new EventSet();this.analysisViewRelatedEvents_=new EventSet();this.analysisLinkHoveredEvents_=new EventSet();this.appliedToModel_=undefined;this.viewSpecificBrushingStates_={};}
+BrushingState.prototype={get guid(){return this.guid_;},clone:function(){var that=new BrushingState();that.selection_=this.selection_;that.findMatches_=this.findMatches_;that.analysisViewRelatedEvents_=this.analysisViewRelatedEvents_;that.analysisLinkHoveredEvents_=this.analysisLinkHoveredEvents_;that.viewSpecificBrushingStates_=this.viewSpecificBrushingStates_;return that;},equals:function(that){if(!this.selection_.equals(that.selection_)){return false;}
+if(!this.findMatches_.equals(that.findMatches_)){return false;}
+if(!this.analysisViewRelatedEvents_.equals(that.analysisViewRelatedEvents_)){return false;}
 if(!this.analysisLinkHoveredEvents_.equals(that.analysisLinkHoveredEvents_)){return false;}
-return true;},get selectionOfInterest(){if(this.selection_.length)
-return this.selection_;if(this.highlight_.length)
-return this.highlight_;if(this.analysisViewRelatedEvents_.length)
-return this.analysisViewRelatedEvents_;if(this.analysisLinkHoveredEvents_.length)
-return this.analysisLinkHoveredEvents_;return this.selection_;},get selection(){return this.selection_;},set selection(selection){if(this.appliedToModel_)
-throw new Error('Cannot mutate this state right now');if(selection===undefined)
-selection=new EventSet();this.selection_=selection;},get findMatches(){return this.findMatches_;},set findMatches(findMatches){if(this.appliedToModel_)
-throw new Error('Cannot mutate this state right now');if(findMatches===undefined)
-findMatches=new EventSet();this.findMatches_=findMatches;},get analysisViewRelatedEvents(){return this.analysisViewRelatedEvents_;},set analysisViewRelatedEvents(analysisViewRelatedEvents){if(this.appliedToModel_)
-throw new Error('Cannot mutate this state right now');if(analysisViewRelatedEvents===undefined)
-analysisViewRelatedEvents=new EventSet();this.analysisViewRelatedEvents_=analysisViewRelatedEvents;},get analysisLinkHoveredEvents(){return this.analysisLinkHoveredEvents_;},set analysisLinkHoveredEvents(analysisLinkHoveredEvents){if(this.appliedToModel_)
-throw new Error('Cannot mutate this state right now');if(analysisLinkHoveredEvents===undefined)
-analysisLinkHoveredEvents=new EventSet();this.analysisLinkHoveredEvents_=analysisLinkHoveredEvents;},get isAppliedToModel(){return this.appliedToModel_!==undefined;},get viewSpecificBrushingStates(){return this.viewSpecificBrushingStates_;},set viewSpecificBrushingStates(viewSpecificBrushingStates){this.viewSpecificBrushingStates_=viewSpecificBrushingStates;},get dimmedEvents_(){var dimmedEvents=new EventSet();dimmedEvents.addEventSet(this.findMatches);dimmedEvents.addEventSet(this.analysisViewRelatedEvents_);return dimmedEvents;},get brightenedEvents_(){var brightenedEvents=new EventSet();brightenedEvents.addEventSet(this.selection_);brightenedEvents.addEventSet(this.analysisLinkHoveredEvents_);return brightenedEvents;},applyToEventSelectionStates:function(model){this.appliedToModel_=model;var dimmedEvents=this.dimmedEvents_;if(model){var newDefaultState=(dimmedEvents.length?SelectionState.DIMMED0:SelectionState.NONE);var currentDefaultState=tr.b.getFirstElement(model.getDescendantEvents()).selectionState;if(currentDefaultState!==newDefaultState)
-for(var e of model.getDescendantEvents())
-e.selectionState=newDefaultState;}
-var score;for(var e of dimmedEvents){score=0;if(this.findMatches_.contains(e))
-score++;if(this.analysisViewRelatedEvents_.contains(e))
-score++;e.selectionState=SelectionState.getFromDimmingLevel(score);}
-for(var e of this.brightenedEvents_){score=0;if(this.selection_.contains(e))
-score++;if(this.analysisLinkHoveredEvents_.contains(e))
-score++;e.selectionState=SelectionState.getFromBrighteningLevel(score);}},transferModelOwnershipToClone:function(that){if(!this.appliedToModel_)
-throw new Error('Not applied');that.appliedToModel_=this.appliedToModel_;this.appliedToModel_=undefined;},unapplyFromEventSelectionStates:function(){if(!this.appliedToModel_)
-throw new Error('Not applied');var model=this.appliedToModel_;this.appliedToModel_=undefined;var dimmedEvents=this.dimmedEvents_;var defaultState=(dimmedEvents.length?SelectionState.DIMMED0:SelectionState.NONE);for(var e of this.brightenedEvents_)
-e.selectionState=defaultState;for(var e of dimmedEvents)
-e.selectionState=defaultState;return defaultState;}};return{BrushingState:BrushingState};});'use strict';tr.exportTo('tr.ui.b',function(){function Animation(){}
-Animation.prototype={canTakeOverFor:function(existingAnimation){throw new Error('Not implemented');},takeOverFor:function(existingAnimation,newStartTimestamp,target){throw new Error('Not implemented');},start:function(timestamp,target){throw new Error('Not implemented');},didStopEarly:function(timestamp,target,willBeTakenOverByAnotherAnimation){},tick:function(timestamp,target){throw new Error('Not implemented');}};return{Animation:Animation};});'use strict';tr.exportTo('tr.ui.b',function(){function AnimationController(){tr.b.EventTarget.call(this);this.target_=undefined;this.activeAnimation_=undefined;this.tickScheduled_=false;}
-AnimationController.prototype={__proto__:tr.b.EventTarget.prototype,get target(){return this.target_;},set target(target){if(this.activeAnimation_)
-throw new Error('Cannot change target while animation is running.');if(target.cloneAnimationState===undefined||typeof target.cloneAnimationState!=='function')
-throw new Error('target must have a cloneAnimationState function');this.target_=target;},get activeAnimation(){return this.activeAnimation_;},get hasActiveAnimation(){return!!this.activeAnimation_;},queueAnimation:function(animation,opt_now){if(this.target_===undefined)
-throw new Error('Cannot queue animations without a target');var now;if(opt_now!==undefined)
-now=opt_now;else
-now=window.performance.now();if(this.activeAnimation_){var done=this.activeAnimation_.tick(now,this.target_);if(done)
-this.activeAnimation_=undefined;}
+return true;},get selectionOfInterest(){if(this.selection_.length){return this.selection_;}
+if(this.highlight_.length){return this.highlight_;}
+if(this.analysisViewRelatedEvents_.length){return this.analysisViewRelatedEvents_;}
+if(this.analysisLinkHoveredEvents_.length){return this.analysisLinkHoveredEvents_;}
+return this.selection_;},get selection(){return this.selection_;},set selection(selection){if(this.appliedToModel_){throw new Error('Cannot mutate this state right now');}
+if(selection===undefined){selection=new EventSet();}
+this.selection_=selection;},get findMatches(){return this.findMatches_;},set findMatches(findMatches){if(this.appliedToModel_){throw new Error('Cannot mutate this state right now');}
+if(findMatches===undefined){findMatches=new EventSet();}
+this.findMatches_=findMatches;},get analysisViewRelatedEvents(){return this.analysisViewRelatedEvents_;},set analysisViewRelatedEvents(analysisViewRelatedEvents){if(this.appliedToModel_){throw new Error('Cannot mutate this state right now');}
+if(analysisViewRelatedEvents===undefined){analysisViewRelatedEvents=new EventSet();}
+this.analysisViewRelatedEvents_=analysisViewRelatedEvents;},get analysisLinkHoveredEvents(){return this.analysisLinkHoveredEvents_;},set analysisLinkHoveredEvents(analysisLinkHoveredEvents){if(this.appliedToModel_){throw new Error('Cannot mutate this state right now');}
+if(analysisLinkHoveredEvents===undefined){analysisLinkHoveredEvents=new EventSet();}
+this.analysisLinkHoveredEvents_=analysisLinkHoveredEvents;},get isAppliedToModel(){return this.appliedToModel_!==undefined;},get viewSpecificBrushingStates(){return this.viewSpecificBrushingStates_;},set viewSpecificBrushingStates(viewSpecificBrushingStates){this.viewSpecificBrushingStates_=viewSpecificBrushingStates;},get dimmedEvents_(){var dimmedEvents=new EventSet();dimmedEvents.addEventSet(this.findMatches);dimmedEvents.addEventSet(this.analysisViewRelatedEvents_);return dimmedEvents;},get brightenedEvents_(){var brightenedEvents=new EventSet();brightenedEvents.addEventSet(this.selection_);brightenedEvents.addEventSet(this.analysisLinkHoveredEvents_);return brightenedEvents;},applyToEventSelectionStates:function(model){this.appliedToModel_=model;var dimmedEvents=this.dimmedEvents_;if(model){var newDefaultState=(dimmedEvents.length?SelectionState.DIMMED0:SelectionState.NONE);var currentDefaultState=tr.b.getFirstElement(model.getDescendantEvents()).selectionState;if(currentDefaultState!==newDefaultState){for(var e of model.getDescendantEvents()){e.selectionState=newDefaultState;}}}
+var score;for(var e of dimmedEvents){score=0;if(this.findMatches_.contains(e)){score++;}
+if(this.analysisViewRelatedEvents_.contains(e)){score++;}
+e.selectionState=SelectionState.getFromDimmingLevel(score);}
+for(var e of this.brightenedEvents_){score=0;if(this.selection_.contains(e)){score++;}
+if(this.analysisLinkHoveredEvents_.contains(e)){score++;}
+e.selectionState=SelectionState.getFromBrighteningLevel(score);}},transferModelOwnershipToClone:function(that){if(!this.appliedToModel_){throw new Error('Not applied');}
+that.appliedToModel_=this.appliedToModel_;this.appliedToModel_=undefined;},unapplyFromEventSelectionStates:function(){if(!this.appliedToModel_){throw new Error('Not applied');}
+var model=this.appliedToModel_;this.appliedToModel_=undefined;var dimmedEvents=this.dimmedEvents_;var defaultState=(dimmedEvents.length?SelectionState.DIMMED0:SelectionState.NONE);for(var e of this.brightenedEvents_){e.selectionState=defaultState;}
+for(var e of dimmedEvents){e.selectionState=defaultState;}
+return defaultState;}};return{BrushingState,};});'use strict';tr.exportTo('tr.ui.b',function(){function Animation(){}
+Animation.prototype={canTakeOverFor:function(existingAnimation){throw new Error('Not implemented');},takeOverFor:function(existingAnimation,newStartTimestamp,target){throw new Error('Not implemented');},start:function(timestamp,target){throw new Error('Not implemented');},didStopEarly:function(timestamp,target,willBeTakenOverByAnotherAnimation){},tick:function(timestamp,target){throw new Error('Not implemented');}};return{Animation,};});'use strict';tr.exportTo('tr.ui.b',function(){function AnimationController(){tr.b.EventTarget.call(this);this.target_=undefined;this.activeAnimation_=undefined;this.tickScheduled_=false;}
+AnimationController.prototype={__proto__:tr.b.EventTarget.prototype,get target(){return this.target_;},set target(target){if(this.activeAnimation_){throw new Error('Cannot change target while animation is running.');}
+if(target.cloneAnimationState===undefined||typeof target.cloneAnimationState!=='function'){throw new Error('target must have a cloneAnimationState function');}
+this.target_=target;},get activeAnimation(){return this.activeAnimation_;},get hasActiveAnimation(){return!!this.activeAnimation_;},queueAnimation:function(animation,opt_now){if(this.target_===undefined){throw new Error('Cannot queue animations without a target');}
+var now;if(opt_now!==undefined){now=opt_now;}else{now=window.performance.now();}
+if(this.activeAnimation_){var done=this.activeAnimation_.tick(now,this.target_);if(done){this.activeAnimation_=undefined;}}
 if(this.activeAnimation_){if(animation.canTakeOverFor(this.activeAnimation_)){this.activeAnimation_.didStopEarly(now,this.target_,true);animation.takeOverFor(this.activeAnimation_,now,this.target_);}else{this.activeAnimation_.didStopEarly(now,this.target_,false);}}
-this.activeAnimation_=animation;this.activeAnimation_.start(now,this.target_);if(this.tickScheduled_)
-return;this.tickScheduled_=true;tr.b.requestAnimationFrame(this.tickActiveAnimation_,this);},cancelActiveAnimation:function(opt_now){if(!this.activeAnimation_)
-return;var now;if(opt_now!==undefined)
-now=opt_now;else
-now=window.performance.now();this.activeAnimation_.didStopEarly(now,this.target_,false);this.activeAnimation_=undefined;},tickActiveAnimation_:function(frameBeginTime){this.tickScheduled_=false;if(!this.activeAnimation_)
-return;if(this.target_===undefined){this.activeAnimation_.didStopEarly(frameBeginTime,this.target_,false);return;}
-var oldTargetState=this.target_.cloneAnimationState();var done=this.activeAnimation_.tick(frameBeginTime,this.target_);if(done)
-this.activeAnimation_=undefined;if(this.activeAnimation_){this.tickScheduled_=true;tr.b.requestAnimationFrame(this.tickActiveAnimation_,this);}
-if(oldTargetState){var e=new tr.b.Event('didtick');e.oldTargetState=oldTargetState;this.dispatchEvent(e,false,false);}}};return{AnimationController:AnimationController};});'use strict';tr.exportTo('tr.b',function(){function Settings(){return Settings;};if(tr.b.unittest&&tr.b.unittest.TestRunner){tr.b.unittest.TestRunner.addEventListener('tr-unittest-will-run',function(){if(tr.isHeadless)
-Settings.setAlternativeStorageInstance(new HeadlessStorage());else
-Settings.setAlternativeStorageInstance(global.sessionStorage);});}
+this.activeAnimation_=animation;this.activeAnimation_.start(now,this.target_);if(this.tickScheduled_)return;this.tickScheduled_=true;tr.b.requestAnimationFrame(this.tickActiveAnimation_,this);},cancelActiveAnimation:function(opt_now){if(!this.activeAnimation_)return;var now;if(opt_now!==undefined){now=opt_now;}else{now=window.performance.now();}
+this.activeAnimation_.didStopEarly(now,this.target_,false);this.activeAnimation_=undefined;},tickActiveAnimation_:function(frameBeginTime){this.tickScheduled_=false;if(!this.activeAnimation_)return;if(this.target_===undefined){this.activeAnimation_.didStopEarly(frameBeginTime,this.target_,false);return;}
+var oldTargetState=this.target_.cloneAnimationState();var done=this.activeAnimation_.tick(frameBeginTime,this.target_);if(done){this.activeAnimation_=undefined;}
+if(this.activeAnimation_){this.tickScheduled_=true;tr.b.requestAnimationFrame(this.tickActiveAnimation_,this);}
+if(oldTargetState){var e=new tr.b.Event('didtick');e.oldTargetState=oldTargetState;this.dispatchEvent(e,false,false);}}};return{AnimationController,};});'use strict';tr.exportTo('tr.b',function(){function Settings(){return Settings;}
+if(tr.b.unittest&&tr.b.unittest.TestRunner){tr.b.unittest.TestRunner.addEventListener('tr-unittest-will-run',function(){if(tr.isHeadless){Settings.setAlternativeStorageInstance(new HeadlessStorage());}else{Settings.setAlternativeStorageInstance(global.sessionStorage);}});}
 function SessionSettings(){return SessionSettings;}
-function AddStaticStorageFunctionsToClass_(input_class,storage){input_class.storage_=storage;input_class.get=function(key,opt_default,opt_namespace){key=input_class.namespace_(key,opt_namespace);var rawVal=input_class.storage_.getItem(key);if(rawVal===null||rawVal===undefined)
-return opt_default;try{return JSON.parse(rawVal).value;}catch(e){input_class.storage_.removeItem(key);return opt_default;}};input_class.set=function(key,value,opt_namespace){if(value===undefined)
-throw new Error('Settings.set: value must not be undefined');var v=JSON.stringify({value:value});input_class.storage_.setItem(input_class.namespace_(key,opt_namespace),v);};input_class.keys=function(opt_namespace){var result=[];opt_namespace=opt_namespace||'';for(var i=0;i<input_class.storage_.length;i++){var key=input_class.storage_.key(i);if(input_class.isnamespaced_(key,opt_namespace))
-result.push(input_class.unnamespace_(key,opt_namespace));}
-return result;};input_class.isnamespaced_=function(key,opt_namespace){return key.indexOf(input_class.normalize_(opt_namespace))==0;};input_class.namespace_=function(key,opt_namespace){return input_class.normalize_(opt_namespace)+key;};input_class.unnamespace_=function(key,opt_namespace){return key.replace(input_class.normalize_(opt_namespace),'');};input_class.normalize_=function(opt_namespace){return input_class.NAMESPACE+(opt_namespace?opt_namespace+'.':'');};input_class.setAlternativeStorageInstance=function(instance){input_class.storage_=instance;};input_class.getAlternativeStorageInstance=function(){if(!tr.isHeadless&&input_class.storage_===localStorage)
-return undefined;return input_class.storage_;};input_class.NAMESPACE='trace-viewer';};function HeadlessStorage(){this.length=0;this.hasItem_={};this.items_={};this.itemsAsArray_=undefined;}
-HeadlessStorage.prototype={key:function(index){return this.itemsAsArray[index];},get itemsAsArray(){if(this.itemsAsArray_!==undefined)
-return this.itemsAsArray_;var itemsAsArray=[];for(var k in this.items_)
-itemsAsArray.push(k);this.itemsAsArray_=itemsAsArray;return this.itemsAsArray_;},getItem:function(key){if(!this.hasItem_[key])
-return null;return this.items_[key];},removeItem:function(key){if(!this.hasItem_[key])
-return;var value=this.items_[key];delete this.hasItem_[key];delete this.items_[key];this.length--;this.itemsAsArray_=undefined;return value;},setItem:function(key,value){if(this.hasItem_[key]){this.items_[key]=value;return;}
+function AddStaticStorageFunctionsToClass_(inputClass,storage){inputClass.storage_=storage;inputClass.get=function(key,opt_default,opt_namespace){key=inputClass.namespace_(key,opt_namespace);var rawVal=inputClass.storage_.getItem(key);if(rawVal===null||rawVal===undefined){return opt_default;}
+try{return JSON.parse(rawVal).value;}catch(e){inputClass.storage_.removeItem(key);return opt_default;}};inputClass.set=function(key,value,opt_namespace){if(value===undefined){throw new Error('Settings.set: value must not be undefined');}
+var v=JSON.stringify({value:value});inputClass.storage_.setItem(inputClass.namespace_(key,opt_namespace),v);};inputClass.keys=function(opt_namespace){var result=[];opt_namespace=opt_namespace||'';for(var i=0;i<inputClass.storage_.length;i++){var key=inputClass.storage_.key(i);if(inputClass.isnamespaced_(key,opt_namespace)){result.push(inputClass.unnamespace_(key,opt_namespace));}}
+return result;};inputClass.isnamespaced_=function(key,opt_namespace){return key.indexOf(inputClass.normalize_(opt_namespace))===0;};inputClass.namespace_=function(key,opt_namespace){return inputClass.normalize_(opt_namespace)+key;};inputClass.unnamespace_=function(key,opt_namespace){return key.replace(inputClass.normalize_(opt_namespace),'');};inputClass.normalize_=function(opt_namespace){return inputClass.NAMESPACE+(opt_namespace?opt_namespace+'.':'');};inputClass.setAlternativeStorageInstance=function(instance){inputClass.storage_=instance;};inputClass.getAlternativeStorageInstance=function(){if(!tr.isHeadless&&inputClass.storage_===localStorage){return undefined;}
+return inputClass.storage_;};inputClass.NAMESPACE='trace-viewer';}
+function HeadlessStorage(){this.length=0;this.hasItem_={};this.items_={};this.itemsAsArray_=undefined;}
+HeadlessStorage.prototype={key:function(index){return this.itemsAsArray[index];},get itemsAsArray(){if(this.itemsAsArray_!==undefined){return this.itemsAsArray_;}
+var itemsAsArray=[];for(var k in this.items_){itemsAsArray.push(k);}
+this.itemsAsArray_=itemsAsArray;return this.itemsAsArray_;},getItem:function(key){if(!this.hasItem_[key]){return null;}
+return this.items_[key];},removeItem:function(key){if(!this.hasItem_[key]){return;}
+var value=this.items_[key];delete this.hasItem_[key];delete this.items_[key];this.length--;this.itemsAsArray_=undefined;return value;},setItem:function(key,value){if(this.hasItem_[key]){this.items_[key]=value;return;}
 this.items_[key]=value;this.hasItem_[key]=true;this.length++;this.itemsAsArray_=undefined;return value;}};if(tr.isHeadless){AddStaticStorageFunctionsToClass_(Settings,new HeadlessStorage());AddStaticStorageFunctionsToClass_(SessionSettings,new HeadlessStorage());}else{AddStaticStorageFunctionsToClass_(Settings,localStorage);AddStaticStorageFunctionsToClass_(SessionSettings,sessionStorage);}
-return{Settings:Settings,SessionSettings:SessionSettings};});'use strict';tr.exportTo('tr.ui.b',function(){function createSpan(opt_dictionary){var ownerDocument=document;if(opt_dictionary&&opt_dictionary.ownerDocument)
-ownerDocument=opt_dictionary.ownerDocument;var spanEl=ownerDocument.createElement('span');if(opt_dictionary){if(opt_dictionary.className)
-spanEl.className=opt_dictionary.className;if(opt_dictionary.textContent){Polymer.dom(spanEl).textContent=opt_dictionary.textContent;}
-if(opt_dictionary.tooltip)
-spanEl.title=opt_dictionary.tooltip;if(opt_dictionary.parent)
-Polymer.dom(opt_dictionary.parent).appendChild(spanEl);if(opt_dictionary.bold)
-spanEl.style.fontWeight='bold';if(opt_dictionary.italic)
-spanEl.style.fontStyle='italic';if(opt_dictionary.marginLeft)
-spanEl.style.marginLeft=opt_dictionary.marginLeft;if(opt_dictionary.marginRight)
-spanEl.style.marginRight=opt_dictionary.marginRight;if(opt_dictionary.backgroundColor)
-spanEl.style.backgroundColor=opt_dictionary.backgroundColor;if(opt_dictionary.color)
-spanEl.style.color=opt_dictionary.color;}
-return spanEl;};function createDiv(opt_dictionary){var divEl=document.createElement('div');if(opt_dictionary){if(opt_dictionary.className)
-divEl.className=opt_dictionary.className;if(opt_dictionary.parent)
-Polymer.dom(opt_dictionary.parent).appendChild(divEl);if(opt_dictionary.textContent)
-Polymer.dom(divEl).textContent=opt_dictionary.textContent;if(opt_dictionary.maxWidth)
-divEl.style.maxWidth=opt_dictionary.maxWidth;}
-return divEl;};function createScopedStyle(styleContent){var styleEl=document.createElement('style');styleEl.scoped=true;Polymer.dom(styleEl).innerHTML=styleContent;return styleEl;}
-function valuesEqual(a,b){if(a instanceof Array&&b instanceof Array)
-return a.length===b.length&&JSON.stringify(a)===JSON.stringify(b);return a===b;}
+return{Settings,SessionSettings,};});'use strict';tr.exportTo('tr.ui.b',function(){function createSpan(opt_dictionary){var ownerDocument=document;if(opt_dictionary&&opt_dictionary.ownerDocument){ownerDocument=opt_dictionary.ownerDocument;}
+var spanEl=ownerDocument.createElement('span');if(opt_dictionary){if(opt_dictionary.className){spanEl.className=opt_dictionary.className;}
+if(opt_dictionary.textContent){Polymer.dom(spanEl).textContent=opt_dictionary.textContent;}
+if(opt_dictionary.tooltip){spanEl.title=opt_dictionary.tooltip;}
+if(opt_dictionary.parent){Polymer.dom(opt_dictionary.parent).appendChild(spanEl);}
+if(opt_dictionary.bold){spanEl.style.fontWeight='bold';}
+if(opt_dictionary.italic){spanEl.style.fontStyle='italic';}
+if(opt_dictionary.marginLeft){spanEl.style.marginLeft=opt_dictionary.marginLeft;}
+if(opt_dictionary.marginRight){spanEl.style.marginRight=opt_dictionary.marginRight;}
+if(opt_dictionary.backgroundColor){spanEl.style.backgroundColor=opt_dictionary.backgroundColor;}
+if(opt_dictionary.color){spanEl.style.color=opt_dictionary.color;}}
+return spanEl;}
+function createLink(opt_args){var ownerDocument=document;if(opt_args&&opt_args.ownerDocument){ownerDocument=opt_args.ownerDocument;}
+var linkEl=ownerDocument.createElement('a');if(opt_args){if(opt_args.href)linkEl.href=opt_args.href;if(opt_args.tooltip)linkEl.title=opt_args.tooltip;if(opt_args.color)linkEl.style.color=opt_args.color;if(opt_args.bold)linkEl.style.fontWeight='bold';if(opt_args.italic)linkEl.style.fontStyle='italic';if(opt_args.className)linkEl.className=opt_args.className;if(opt_args.parent)Polymer.dom(opt_args.parent).appendChild(linkEl);if(opt_args.marginLeft)linkEl.style.marginLeft=opt_args.marginLeft;if(opt_args.marginRight)linkEl.style.marginRight=opt_args.marginRight;if(opt_args.backgroundColor){linkEl.style.backgroundColor=opt_args.backgroundColor;}
+if(opt_args.textContent){Polymer.dom(linkEl).textContent=opt_args.textContent;}}
+return linkEl;}
+function createDiv(opt_dictionary){var divEl=document.createElement('div');if(opt_dictionary){if(opt_dictionary.className){divEl.className=opt_dictionary.className;}
+if(opt_dictionary.parent){Polymer.dom(opt_dictionary.parent).appendChild(divEl);}
+if(opt_dictionary.textContent){Polymer.dom(divEl).textContent=opt_dictionary.textContent;}
+if(opt_dictionary.maxWidth){divEl.style.maxWidth=opt_dictionary.maxWidth;}}
+return divEl;}
+function createScopedStyle(styleContent){var styleEl=document.createElement('style');styleEl.scoped=true;Polymer.dom(styleEl).innerHTML=styleContent;return styleEl;}
+function valuesEqual(a,b){if(a instanceof Array&&b instanceof Array){return a.length===b.length&&JSON.stringify(a)===JSON.stringify(b);}
+return a===b;}
 function createSelector(targetEl,targetElProperty,settingsKey,defaultValue,items,opt_namespace){var defaultValueIndex;for(var i=0;i<items.length;i++){var item=items[i];if(valuesEqual(item.value,defaultValue)){defaultValueIndex=i;break;}}
-if(defaultValueIndex===undefined)
-throw new Error('defaultValue must be in the items list');var selectorEl=document.createElement('select');selectorEl.addEventListener('change',onChange);for(var i=0;i<items.length;i++){var item=items[i];var optionEl=document.createElement('option');Polymer.dom(optionEl).textContent=item.label;optionEl.targetPropertyValue=item.value;optionEl.item=item;Polymer.dom(selectorEl).appendChild(optionEl);}
+if(defaultValueIndex===undefined){throw new Error('defaultValue must be in the items list');}
+var selectorEl=document.createElement('select');selectorEl.addEventListener('change',onChange);for(var i=0;i<items.length;i++){var item=items[i];var optionEl=document.createElement('option');Polymer.dom(optionEl).textContent=item.label;optionEl.targetPropertyValue=item.value;optionEl.item=item;Polymer.dom(selectorEl).appendChild(optionEl);}
 function onChange(e){var value=selectorEl.selectedOptions[0].targetPropertyValue;tr.b.Settings.set(settingsKey,value,opt_namespace);targetEl[targetElProperty]=value;}
-var oldSetter=targetEl.__lookupSetter__('selectedIndex');selectorEl.__defineGetter__('selectedValue',function(v){return selectorEl.children[selectorEl.selectedIndex].targetPropertyValue;});selectorEl.__defineGetter__('selectedItem',function(v){return selectorEl.children[selectorEl.selectedIndex].item;});selectorEl.__defineSetter__('selectedValue',function(v){for(var i=0;i<selectorEl.children.length;i++){var value=selectorEl.children[i].targetPropertyValue;if(valuesEqual(value,v)){var changed=selectorEl.selectedIndex!=i;if(changed){selectorEl.selectedIndex=i;onChange();}
+var oldSetter=targetEl.__lookupSetter__('selectedIndex');selectorEl.__defineGetter__('selectedValue',function(v){return selectorEl.children[selectorEl.selectedIndex].targetPropertyValue;});selectorEl.__defineGetter__('selectedItem',function(v){return selectorEl.children[selectorEl.selectedIndex].item;});selectorEl.__defineSetter__('selectedValue',function(v){for(var i=0;i<selectorEl.children.length;i++){var value=selectorEl.children[i].targetPropertyValue;if(valuesEqual(value,v)){var changed=selectorEl.selectedIndex!==i;if(changed){selectorEl.selectedIndex=i;onChange();}
 return;}}
 throw new Error('Not a valid value');});var initialValue=tr.b.Settings.get(settingsKey,defaultValue,opt_namespace);var didSet=false;for(var i=0;i<selectorEl.children.length;i++){if(valuesEqual(selectorEl.children[i].targetPropertyValue,initialValue)){didSet=true;targetEl[targetElProperty]=initialValue;selectorEl.selectedIndex=i;break;}}
 if(!didSet){selectorEl.selectedIndex=defaultValueIndex;targetEl[targetElProperty]=defaultValue;}
 return selectorEl;}
 function createEditCategorySpan(optionGroupEl,targetEl){var spanEl=createSpan({className:'edit-categories'});Polymer.dom(spanEl).textContent='Edit categories';Polymer.dom(spanEl).classList.add('labeled-option');spanEl.addEventListener('click',function(){targetEl.onClickEditCategories();});return spanEl;}
-function createOptionGroup(targetEl,targetElProperty,settingsKey,defaultValue,items){function onChange(){var value=[];if(this.value.length)
-value=this.value.split(',');tr.b.Settings.set(settingsKey,value);targetEl[targetElProperty]=value;}
-var optionGroupEl=createSpan({className:'labeled-option-group'});var initialValue=tr.b.Settings.get(settingsKey,defaultValue);for(var i=0;i<items.length;++i){var item=items[i];var id='category-preset-'+item.label.replace(/ /g,'-');var radioEl=document.createElement('input');radioEl.type='radio';Polymer.dom(radioEl).setAttribute('id',id);Polymer.dom(radioEl).setAttribute('name','category-presets-group');Polymer.dom(radioEl).setAttribute('value',item.value);radioEl.addEventListener('change',onChange.bind(radioEl,targetEl,targetElProperty,settingsKey));if(valuesEqual(initialValue,item.value))
-radioEl.checked=true;var labelEl=document.createElement('label');Polymer.dom(labelEl).textContent=item.label;Polymer.dom(labelEl).setAttribute('for',id);var spanEl=createSpan({className:'labeled-option'});Polymer.dom(spanEl).appendChild(radioEl);Polymer.dom(spanEl).appendChild(labelEl);spanEl.__defineSetter__('checked',function(opt_bool){var changed=radioEl.checked!==(!!opt_bool);if(!changed)
-return;radioEl.checked=!!opt_bool;onChange();});spanEl.__defineGetter__('checked',function(){return radioEl.checked;});Polymer.dom(optionGroupEl).appendChild(spanEl);}
-Polymer.dom(optionGroupEl).appendChild(createEditCategorySpan(optionGroupEl,targetEl));if(!initialValue.length)
-Polymer.dom(optionGroupEl).classList.add('categories-expanded');targetEl[targetElProperty]=initialValue;return optionGroupEl;}
-var nextCheckboxId=1;function createCheckBox(targetEl,targetElProperty,settingsKey,defaultValue,label,opt_changeCb){var buttonEl=document.createElement('input');buttonEl.type='checkbox';var initialValue=tr.b.Settings.get(settingsKey,defaultValue);buttonEl.checked=!!initialValue;if(targetEl)
-targetEl[targetElProperty]=initialValue;function onChange(){tr.b.Settings.set(settingsKey,buttonEl.checked);if(targetEl)
-targetEl[targetElProperty]=buttonEl.checked;if(opt_changeCb)
-opt_changeCb.call();}
-buttonEl.addEventListener('change',onChange);var id='#checkbox-'+nextCheckboxId++;var spanEl=createSpan({className:'labeled-checkbox'});Polymer.dom(buttonEl).setAttribute('id',id);var labelEl=document.createElement('label');Polymer.dom(labelEl).textContent=label;Polymer.dom(labelEl).setAttribute('for',id);Polymer.dom(spanEl).appendChild(buttonEl);Polymer.dom(spanEl).appendChild(labelEl);spanEl.__defineSetter__('checked',function(opt_bool){var changed=buttonEl.checked!==(!!opt_bool);if(!changed)
-return;buttonEl.checked=!!opt_bool;onChange();});spanEl.__defineGetter__('checked',function(){return buttonEl.checked;});return spanEl;}
+function createOptionGroup(targetEl,targetElProperty,settingsKey,defaultValue,items){function onChange(){var value=[];if(this.value.length){value=this.value.split(',');}
+tr.b.Settings.set(settingsKey,value);targetEl[targetElProperty]=value;}
+var optionGroupEl=createSpan({className:'labeled-option-group'});var initialValue=tr.b.Settings.get(settingsKey,defaultValue);for(var i=0;i<items.length;++i){var item=items[i];var id='category-preset-'+item.label.replace(/ /g,'-');var radioEl=document.createElement('input');radioEl.type='radio';Polymer.dom(radioEl).setAttribute('id',id);Polymer.dom(radioEl).setAttribute('name','category-presets-group');Polymer.dom(radioEl).setAttribute('value',item.value);radioEl.addEventListener('change',onChange.bind(radioEl,targetEl,targetElProperty,settingsKey));if(valuesEqual(initialValue,item.value)){radioEl.checked=true;}
+var labelEl=document.createElement('label');Polymer.dom(labelEl).textContent=item.label;Polymer.dom(labelEl).setAttribute('for',id);var spanEl=createSpan({className:'labeled-option'});Polymer.dom(spanEl).appendChild(radioEl);Polymer.dom(spanEl).appendChild(labelEl);spanEl.__defineSetter__('checked',function(opt_bool){var changed=radioEl.checked!==(!!opt_bool);if(!changed)return;radioEl.checked=!!opt_bool;onChange();});spanEl.__defineGetter__('checked',function(){return radioEl.checked;});Polymer.dom(optionGroupEl).appendChild(spanEl);}
+Polymer.dom(optionGroupEl).appendChild(createEditCategorySpan(optionGroupEl,targetEl));if(!initialValue.length){Polymer.dom(optionGroupEl).classList.add('categories-expanded');}
+targetEl[targetElProperty]=initialValue;return optionGroupEl;}
+var nextCheckboxId=1;function createCheckBox(targetEl,targetElProperty,settingsKey,defaultValue,label,opt_changeCb){var buttonEl=document.createElement('input');buttonEl.type='checkbox';var initialValue=defaultValue;if(settingsKey!==undefined){initialValue=tr.b.Settings.get(settingsKey,defaultValue);buttonEl.checked=!!initialValue;}
+if(targetEl){targetEl[targetElProperty]=initialValue;}
+function onChange(){if(settingsKey!==undefined){tr.b.Settings.set(settingsKey,buttonEl.checked);}
+if(targetEl){targetEl[targetElProperty]=buttonEl.checked;}
+if(opt_changeCb){opt_changeCb.call();}}
+buttonEl.addEventListener('change',onChange);var id='#checkbox-'+nextCheckboxId++;var spanEl=createSpan({className:'labeled-checkbox'});Polymer.dom(buttonEl).setAttribute('id',id);var labelEl=document.createElement('label');Polymer.dom(labelEl).textContent=label;Polymer.dom(labelEl).setAttribute('for',id);Polymer.dom(spanEl).appendChild(buttonEl);Polymer.dom(spanEl).appendChild(labelEl);spanEl.__defineSetter__('checked',function(opt_bool){var changed=buttonEl.checked!==(!!opt_bool);if(!changed)return;buttonEl.checked=!!opt_bool;onChange();});spanEl.__defineGetter__('checked',function(){return buttonEl.checked;});return spanEl;}
 function createButton(label,opt_callback,opt_this){var buttonEl=document.createElement('input');buttonEl.type='button';buttonEl.value=label;function onClick(){opt_callback.call(opt_this||buttonEl);}
-if(opt_callback)
-buttonEl.addEventListener('click',onClick);return buttonEl;}
+if(opt_callback){buttonEl.addEventListener('click',onClick);}
+return buttonEl;}
 function createTextInput(targetEl,targetElProperty,settingsKey,defaultValue){var initialValue=tr.b.Settings.get(settingsKey,defaultValue);var el=document.createElement('input');el.type='text';function onChange(e){tr.b.Settings.set(settingsKey,el.value);targetEl[targetElProperty]=el.value;}
 el.addEventListener('input',onChange);el.value=initialValue;targetEl[targetElProperty]=initialValue;return el;}
-function isElementAttachedToDocument(el){var cur=el;while(Polymer.dom(cur).parentNode)
-cur=Polymer.dom(cur).parentNode;return(cur===el.ownerDocument||cur.nodeName==='#document-fragment');}
-function asHTMLOrTextNode(value,opt_ownerDocument){if(value instanceof Node)
-return value;var ownerDocument=opt_ownerDocument||document;return ownerDocument.createTextNode(value);}
-return{createSpan:createSpan,createDiv:createDiv,createScopedStyle:createScopedStyle,createSelector:createSelector,createOptionGroup:createOptionGroup,createCheckBox:createCheckBox,createButton:createButton,createTextInput:createTextInput,isElementAttachedToDocument:isElementAttachedToDocument,asHTMLOrTextNode:asHTMLOrTextNode};});'use strict';tr.exportTo('tr.ui.b',function(){var elidedTitleCacheDict=new Map();var elidedTitleCache=new ElidedTitleCache();function ElidedTitleCache(){this.textWidthMap=new Map();}
+function isElementAttachedToDocument(el){var cur=el;while(Polymer.dom(cur).parentNode){cur=Polymer.dom(cur).parentNode;}
+return(cur===el.ownerDocument||cur.nodeName==='#document-fragment');}
+function asHTMLOrTextNode(value,opt_ownerDocument){if(value instanceof Node){return value;}
+var ownerDocument=opt_ownerDocument||document;return ownerDocument.createTextNode(value);}
+return{createSpan,createLink,createDiv,createScopedStyle,createSelector,createOptionGroup,createCheckBox,createButton,createTextInput,isElementAttachedToDocument,asHTMLOrTextNode,};});'use strict';tr.exportTo('tr.ui.b',function(){var elidedTitleCacheDict=new Map();var elidedTitleCache=new ElidedTitleCache();function ElidedTitleCache(){this.textWidthMap=new Map();}
 ElidedTitleCache.prototype={get:function(ctx,pixWidth,title,width,sliceDuration){var elidedDict=elidedTitleCacheDict.get(title);if(!elidedDict){elidedDict=new Map();elidedTitleCacheDict.set(title,elidedDict);}
 var elidedDictForPixWidth=elidedDict.get(pixWidth);if(!elidedDictForPixWidth){elidedDict.set(pixWidth,new Map());elidedDictForPixWidth=elidedDict.get(pixWidth);}
-var stringWidthPair=elidedDictForPixWidth.get(sliceDuration);if(stringWidthPair===undefined){var newtitle=title;var elided=false;while(this.labelWidthWorld(ctx,newtitle,pixWidth)>sliceDuration){if(newtitle.length*0.75<1)
-break;newtitle=newtitle.substring(0,newtitle.length*0.75);elided=true;}
-if(elided&&newtitle.length>3)
-newtitle=newtitle.substring(0,newtitle.length-3)+'...';stringWidthPair=new ElidedStringWidthPair(newtitle,this.labelWidth(ctx,newtitle));elidedDictForPixWidth.set(sliceDuration,stringWidthPair);}
+var stringWidthPair=elidedDictForPixWidth.get(sliceDuration);if(stringWidthPair===undefined){var newtitle=title;var elided=false;while(this.labelWidthWorld(ctx,newtitle,pixWidth)>sliceDuration){if(newtitle.length*0.75<1)break;newtitle=newtitle.substring(0,newtitle.length*0.75);elided=true;}
+if(elided&&newtitle.length>3){newtitle=newtitle.substring(0,newtitle.length-3)+'...';}
+stringWidthPair=new ElidedStringWidthPair(newtitle,this.labelWidth(ctx,newtitle));elidedDictForPixWidth.set(sliceDuration,stringWidthPair);}
 return stringWidthPair;},quickMeasureText_:function(ctx,text){var w=this.textWidthMap.get(text);if(!w){w=ctx.measureText(text).width;this.textWidthMap.set(text,w);}
 return w;},labelWidth:function(ctx,title){return this.quickMeasureText_(ctx,title)+2;},labelWidthWorld:function(ctx,title,pixWidth){return this.labelWidth(ctx,title)*pixWidth;}};function ElidedStringWidthPair(string,width){this.string=string;this.width=width;}
-return{ElidedTitleCache:ElidedTitleCache};});'use strict';tr.exportTo('tr.ui.b',function(){var ColorScheme=tr.b.ColorScheme;var colors=ColorScheme.colors;var colorsAsStrings=ColorScheme.colorsAsStrings;var numColorsPerVariant=ColorScheme.properties.numColorsPerVariant;var SelectionState=tr.model.SelectionState;var EventPresenter={getSelectableItemColorAsString:function(item){var colorId=item.colorId+this.getColorIdOffset_(item);return colorsAsStrings[colorId];},getColorIdOffset_:function(event){return event.selectionState;},getTextColor:function(event){if(event.selectionState===SelectionState.DIMMED)
-return'rgb(60,60,60)';return'rgb(0,0,0)';},getSliceColorId:function(slice){return slice.colorId+this.getColorIdOffset_(slice);},getSliceAlpha:function(slice,async){var alpha=1;if(async)
-alpha*=0.3;return alpha;},getInstantSliceColor:function(instant){var colorId=instant.colorId+this.getColorIdOffset_(instant);return colors[colorId].toStringWithAlphaOverride(1.0);},getObjectInstanceColor:function(instance){var colorId=instance.colorId+this.getColorIdOffset_(instance);return colors[colorId].toStringWithAlphaOverride(0.25);},getObjectSnapshotColor:function(snapshot){var colorId=snapshot.objectInstance.colorId+this.getColorIdOffset_(snapshot);return colors[colorId];},getCounterSeriesColor:function(colorId,selectionState,opt_alphaMultiplier){var event={selectionState:selectionState};var c=colors[colorId+this.getColorIdOffset_(event)];return c.toStringWithAlphaOverride(opt_alphaMultiplier!==undefined?opt_alphaMultiplier:1.0);},getBarSnapshotColor:function(snapshot,offset){var colorId=(snapshot.objectInstance.colorId+offset)%numColorsPerVariant;colorId+=this.getColorIdOffset_(snapshot);return colors[colorId].toStringWithAlphaOverride(1.0);}};return{EventPresenter:EventPresenter};});'use strict';tr.exportTo('tr.ui.b',function(){var elidedTitleCache=new tr.ui.b.ElidedTitleCache();var ColorScheme=tr.b.ColorScheme;var colorsAsStrings=ColorScheme.colorsAsStrings;var EventPresenter=tr.ui.b.EventPresenter;var blackColorId=ColorScheme.getColorIdForReservedName('black');var THIN_SLICE_HEIGHT=4;var SLICE_WAITING_WIDTH_DRAW_THRESHOLD=3;var SLICE_ACTIVE_WIDTH_DRAW_THRESHOLD=1;var SHOULD_ELIDE_TEXT=true;function drawLine(ctx,x1,y1,x2,y2){ctx.moveTo(x1,y1);ctx.lineTo(x2,y2);}
+return{ElidedTitleCache,};});'use strict';tr.exportTo('tr.ui.b',function(){var ColorScheme=tr.b.ColorScheme;var colors=ColorScheme.colors;var colorsAsStrings=ColorScheme.colorsAsStrings;var numColorsPerVariant=ColorScheme.properties.numColorsPerVariant;var SelectionState=tr.model.SelectionState;var EventPresenter={getSelectableItemColorAsString:function(item){var colorId=item.colorId+this.getColorIdOffset_(item);return colorsAsStrings[colorId];},getColorIdOffset_:function(event){return event.selectionState;},getTextColor:function(event){if(event.selectionState===SelectionState.DIMMED){return'rgb(60,60,60)';}
+return'rgb(0,0,0)';},getSliceColorId:function(slice){return slice.colorId+this.getColorIdOffset_(slice);},getSliceAlpha:function(slice,async){var alpha=1;if(async){alpha*=0.3;}
+return alpha;},getInstantSliceColor:function(instant){var colorId=instant.colorId+this.getColorIdOffset_(instant);return colors[colorId].toStringWithAlphaOverride(1.0);},getObjectInstanceColor:function(instance){var colorId=instance.colorId+this.getColorIdOffset_(instance);return colors[colorId].toStringWithAlphaOverride(0.25);},getObjectSnapshotColor:function(snapshot){var colorId=snapshot.objectInstance.colorId+this.getColorIdOffset_(snapshot);return colors[colorId];},getCounterSeriesColor:function(colorId,selectionState,opt_alphaMultiplier){var event={selectionState:selectionState};var c=colors[colorId+this.getColorIdOffset_(event)];return c.toStringWithAlphaOverride(opt_alphaMultiplier!==undefined?opt_alphaMultiplier:1.0);},getBarSnapshotColor:function(snapshot,offset){var colorId=(snapshot.objectInstance.colorId+offset)%numColorsPerVariant;colorId+=this.getColorIdOffset_(snapshot);return colors[colorId].toStringWithAlphaOverride(1.0);}};return{EventPresenter,};});'use strict';tr.exportTo('tr.ui.b',function(){var elidedTitleCache=new tr.ui.b.ElidedTitleCache();var ColorScheme=tr.b.ColorScheme;var colorsAsStrings=ColorScheme.colorsAsStrings;var EventPresenter=tr.ui.b.EventPresenter;var blackColorId=ColorScheme.getColorIdForReservedName('black');var THIN_SLICE_HEIGHT=4;var SLICE_WAITING_WIDTH_DRAW_THRESHOLD=3;var SLICE_ACTIVE_WIDTH_DRAW_THRESHOLD=1;var SHOULD_ELIDE_TEXT=true;function drawLine(ctx,x1,y1,x2,y2){ctx.moveTo(x1,y1);ctx.lineTo(x2,y2);}
 function drawTriangle(ctx,x1,y1,x2,y2,x3,y3){ctx.beginPath();ctx.moveTo(x1,y1);ctx.lineTo(x2,y2);ctx.lineTo(x3,y3);ctx.closePath();}
 function drawArrow(ctx,x1,y1,x2,y2,arrowLength,arrowWidth){var dx=x2-x1;var dy=y2-y1;var len=Math.sqrt(dx*dx+dy*dy);var perc=(len-arrowLength)/len;var bx=x1+perc*dx;var by=y1+perc*dy;var ux=dx/len;var uy=dy/len;var ax=uy*arrowWidth;var ay=-ux*arrowWidth;ctx.beginPath();drawLine(ctx,x1,y1,x2,y2);ctx.stroke();drawTriangle(ctx,bx+ax,by+ay,x2,y2,bx-ax,by-ay);ctx.fill();}
-function drawSlices(ctx,dt,viewLWorld,viewRWorld,viewHeight,slices,async){var pixelRatio=window.devicePixelRatio||1;var pixWidth=dt.xViewVectorToWorld(1);var height=viewHeight*pixelRatio;var darkRectHeight=THIN_SLICE_HEIGHT*pixelRatio;if(height<darkRectHeight)
-darkRectHeight=0;var lightRectHeight=height-darkRectHeight;ctx.save();dt.applyTransformToCanvas(ctx);var rect=new tr.ui.b.FastRectRenderer(ctx,2*pixWidth,2*pixWidth,colorsAsStrings);rect.setYandH(0,height);var lowSlice=tr.b.findLowIndexInSortedArray(slices,function(slice){return slice.start+slice.duration;},viewLWorld);var hadTopLevel=false;for(var i=lowSlice;i<slices.length;++i){var slice=slices[i];var x=slice.start;if(x>viewRWorld)
-break;var w=pixWidth;if(slice.duration>0){w=Math.max(slice.duration,0.000001);if(w<pixWidth)
-w=pixWidth;}
+function drawSlices(ctx,dt,viewLWorld,viewRWorld,viewHeight,slices,async){var pixelRatio=window.devicePixelRatio||1;var pixWidth=dt.xViewVectorToWorld(1);var height=viewHeight*pixelRatio;var darkRectHeight=THIN_SLICE_HEIGHT*pixelRatio;if(height<darkRectHeight){darkRectHeight=0;}
+var lightRectHeight=height-darkRectHeight;ctx.save();dt.applyTransformToCanvas(ctx);var rect=new tr.ui.b.FastRectRenderer(ctx,2*pixWidth,2*pixWidth,colorsAsStrings);rect.setYandH(0,height);var lowSlice=tr.b.math.findLowIndexInSortedArray(slices,function(slice){return slice.start+slice.duration;},viewLWorld);var hadTopLevel=false;for(var i=lowSlice;i<slices.length;++i){var slice=slices[i];var x=slice.start;if(x>viewRWorld)break;var w=pixWidth;if(slice.duration>0){w=Math.max(slice.duration,0.000001);if(w<pixWidth){w=pixWidth;}}
 var colorId=EventPresenter.getSliceColorId(slice);var alpha=EventPresenter.getSliceAlpha(slice,async);var lightAlpha=alpha*0.70;if(async&&slice.isTopLevel){rect.setYandH(3,height-3);hadTopLevel=true;}else{rect.setYandH(0,height);}
 if(!slice.cpuDuration){rect.fillRect(x,w,colorId,alpha);continue;}
 var activeWidth=w*(slice.cpuDuration/slice.duration);var waitingWidth=w-activeWidth;if(activeWidth<SLICE_ACTIVE_WIDTH_DRAW_THRESHOLD*pixWidth){activeWidth=0;waitingWidth=w;}
 if(waitingWidth<SLICE_WAITING_WIDTH_DRAW_THRESHOLD*pixWidth){activeWidth=w;waitingWidth=0;}
 if(activeWidth>0){rect.fillRect(x,activeWidth,colorId,alpha);}
 if(waitingWidth>0){rect.setYandH(0,lightRectHeight);rect.fillRect(x+activeWidth-pixWidth,waitingWidth+pixWidth,colorId,lightAlpha);rect.setYandH(lightRectHeight,darkRectHeight);rect.fillRect(x+activeWidth-pixWidth,waitingWidth+pixWidth,colorId,alpha);rect.setYandH(0,height);}}
-rect.flush();if(async&&hadTopLevel){rect.setYandH(2,1);for(var i=lowSlice;i<slices.length;++i){var slice=slices[i];var x=slice.start;if(x>viewRWorld)
-break;if(!slice.isTopLevel)
-continue;var w=pixWidth;if(slice.duration>0){w=Math.max(slice.duration,0.000001);if(w<pixWidth)
-w=pixWidth;}
+rect.flush();if(async&&hadTopLevel){rect.setYandH(2,1);for(var i=lowSlice;i<slices.length;++i){var slice=slices[i];var x=slice.start;if(x>viewRWorld)break;if(!slice.isTopLevel)continue;var w=pixWidth;if(slice.duration>0){w=Math.max(slice.duration,0.000001);if(w<pixWidth){w=pixWidth;}}
 rect.fillRect(x,w,blackColorId,0.7);}
 rect.flush();}
 ctx.restore();}
-function drawInstantSlicesAsLines(ctx,dt,viewLWorld,viewRWorld,viewHeight,slices,lineWidthInPixels){var pixelRatio=window.devicePixelRatio||1;var height=viewHeight*pixelRatio;var pixWidth=dt.xViewVectorToWorld(1);ctx.save();ctx.lineWidth=pixWidth*lineWidthInPixels*pixelRatio;dt.applyTransformToCanvas(ctx);ctx.beginPath();var lowSlice=tr.b.findLowIndexInSortedArray(slices,function(slice){return slice.start;},viewLWorld);for(var i=lowSlice;i<slices.length;++i){var slice=slices[i];var x=slice.start;if(x>viewRWorld)
-break;ctx.strokeStyle=EventPresenter.getInstantSliceColor(slice);ctx.beginPath();ctx.moveTo(x,0);ctx.lineTo(x,height);ctx.stroke();}
+function drawInstantSlicesAsLines(ctx,dt,viewLWorld,viewRWorld,viewHeight,slices,lineWidthInPixels){var pixelRatio=window.devicePixelRatio||1;var height=viewHeight*pixelRatio;var pixWidth=dt.xViewVectorToWorld(1);ctx.save();ctx.lineWidth=pixWidth*lineWidthInPixels*pixelRatio;dt.applyTransformToCanvas(ctx);ctx.beginPath();var lowSlice=tr.b.math.findLowIndexInSortedArray(slices,function(slice){return slice.start;},viewLWorld);for(var i=lowSlice;i<slices.length;++i){var slice=slices[i];var x=slice.start;if(x>viewRWorld)break;ctx.strokeStyle=EventPresenter.getInstantSliceColor(slice);ctx.beginPath();ctx.moveTo(x,0);ctx.lineTo(x,height);ctx.stroke();}
 ctx.restore();}
-function drawLabels(ctx,dt,viewLWorld,viewRWorld,slices,async,fontSize,yOffset){var pixelRatio=window.devicePixelRatio||1;var pixWidth=dt.xViewVectorToWorld(1);ctx.save();ctx.textAlign='center';ctx.textBaseline='top';ctx.font=(fontSize*pixelRatio)+'px sans-serif';if(async)
-ctx.font='italic '+ctx.font;var cY=yOffset*pixelRatio;var lowSlice=tr.b.findLowIndexInSortedArray(slices,function(slice){return slice.start+slice.duration;},viewLWorld);var quickDiscardThresshold=pixWidth*20;for(var i=lowSlice;i<slices.length;++i){var slice=slices[i];if(slice.start>viewRWorld)
-break;if(slice.duration<=quickDiscardThresshold)
-continue;var title=slice.title+
+function drawLabels(ctx,dt,viewLWorld,viewRWorld,slices,async,fontSize,yOffset){var pixelRatio=window.devicePixelRatio||1;var pixWidth=dt.xViewVectorToWorld(1);ctx.save();ctx.textAlign='center';ctx.textBaseline='top';ctx.font=(fontSize*pixelRatio)+'px sans-serif';if(async){ctx.font='italic '+ctx.font;}
+var cY=yOffset*pixelRatio;var lowSlice=tr.b.math.findLowIndexInSortedArray(slices,function(slice){return slice.start+slice.duration;},viewLWorld);var quickDiscardThresshold=pixWidth*20;for(var i=lowSlice;i<slices.length;++i){var slice=slices[i];if(slice.start>viewRWorld)break;if(slice.duration<=quickDiscardThresshold)continue;var title=slice.title+
 (slice.didNotFinish?' (Did Not Finish)':'');var drawnTitle=title;var drawnWidth=elidedTitleCache.labelWidth(ctx,drawnTitle);var fullLabelWidth=elidedTitleCache.labelWidthWorld(ctx,drawnTitle,pixWidth);if(SHOULD_ELIDE_TEXT&&fullLabelWidth>slice.duration){var elidedValues=elidedTitleCache.get(ctx,pixWidth,drawnTitle,drawnWidth,slice.duration);drawnTitle=elidedValues.string;drawnWidth=elidedValues.width;}
 if(drawnWidth*pixWidth<slice.duration){ctx.fillStyle=EventPresenter.getTextColor(slice);var cX=dt.xWorldToView(slice.start+0.5*slice.duration);ctx.fillText(drawnTitle,cX,cY,drawnWidth);}}
 ctx.restore();}
-return{drawSlices:drawSlices,drawInstantSlicesAsLines:drawInstantSlicesAsLines,drawLabels:drawLabels,drawLine:drawLine,drawTriangle:drawTriangle,drawArrow:drawArrow,elidedTitleCache_:elidedTitleCache,THIN_SLICE_HEIGHT:THIN_SLICE_HEIGHT};});'use strict';tr.exportTo('tr.ui',function(){function TimelineDisplayTransform(opt_that){if(opt_that){this.set(opt_that);return;}
+return{drawSlices,drawInstantSlicesAsLines,drawLabels,drawLine,drawTriangle,drawArrow,elidedTitleCache_:elidedTitleCache,THIN_SLICE_HEIGHT,};});'use strict';tr.exportTo('tr.ui',function(){function TimelineDisplayTransform(opt_that){if(opt_that){this.set(opt_that);return;}
 this.scaleX=1;this.panX=0;this.panY=0;}
-TimelineDisplayTransform.prototype={set:function(that){this.scaleX=that.scaleX;this.panX=that.panX;this.panY=that.panY;},clone:function(){return new TimelineDisplayTransform(this);},equals:function(that){var eq=true;if(that===undefined||that===null)
-return false;eq&=this.panX===that.panX;eq&=this.panY===that.panY;eq&=this.scaleX===that.scaleX;return!!eq;},almostEquals:function(that){var eq=true;if(that===undefined||that===null)
-return false;eq&=Math.abs(this.panX-that.panX)<0.001;eq&=Math.abs(this.panY-that.panY)<0.001;eq&=Math.abs(this.scaleX-that.scaleX)<0.001;return!!eq;},incrementPanXInViewUnits:function(xDeltaView){this.panX+=this.xViewVectorToWorld(xDeltaView);},xPanWorldPosToViewPos:function(worldX,viewX,viewWidth){if(typeof viewX=='string'){if(viewX==='left'){viewX=0;}else if(viewX==='center'){viewX=viewWidth/2;}else if(viewX==='right'){viewX=viewWidth-1;}else{throw new Error('viewX must be left|center|right or number.');}}
-this.panX=(viewX/this.scaleX)-worldX;},xPanWorldBoundsIntoView:function(worldMin,worldMax,viewWidth){if(this.xWorldToView(worldMin)<0)
-this.xPanWorldPosToViewPos(worldMin,'left',viewWidth);else if(this.xWorldToView(worldMax)>viewWidth)
-this.xPanWorldPosToViewPos(worldMax,'right',viewWidth);},xSetWorldBounds:function(worldMin,worldMax,viewWidth){var worldWidth=worldMax-worldMin;var scaleX=viewWidth/worldWidth;var panX=-worldMin;this.setPanAndScale(panX,scaleX);},setPanAndScale:function(p,s){this.scaleX=s;this.panX=p;},xWorldToView:function(x){return(x+this.panX)*this.scaleX;},xWorldVectorToView:function(x){return x*this.scaleX;},xViewToWorld:function(x){return(x/this.scaleX)-this.panX;},xViewVectorToWorld:function(x){return x/this.scaleX;},applyTransformToCanvas:function(ctx){ctx.transform(this.scaleX,0,0,1,this.panX*this.scaleX,0);}};return{TimelineDisplayTransform:TimelineDisplayTransform};});'use strict';tr.exportTo('tr.ui',function(){function SnapIndicator(y,height){this.y=y;this.height=height;}
-function TimelineInterestRange(vp){this.viewport_=vp;this.range_=new tr.b.Range();this.leftSelected_=false;this.rightSelected_=false;this.leftSnapIndicator_=undefined;this.rightSnapIndicator_=undefined;}
-TimelineInterestRange.prototype={get isEmpty(){return this.range_.isEmpty;},reset:function(){this.range_.reset();this.leftSelected_=false;this.rightSelected_=false;this.leftSnapIndicator_=undefined;this.rightSnapIndicator_=undefined;this.viewport_.dispatchChangeEvent();},get min(){return this.range_.min;},set min(min){this.range_.min=min;this.viewport_.dispatchChangeEvent();},get max(){return this.range_.max;},set max(max){this.range_.max=max;this.viewport_.dispatchChangeEvent();},set:function(range){this.range_.reset();this.range_.addRange(range);this.viewport_.dispatchChangeEvent();},setMinAndMax:function(min,max){this.range_.min=min;this.range_.max=max;this.viewport_.dispatchChangeEvent();},get range(){return this.range_.range;},asRangeObject:function(){var range=new tr.b.Range();range.addRange(this.range_);return range;},get leftSelected(){return this.leftSelected_;},set leftSelected(leftSelected){if(this.leftSelected_==leftSelected)
-return;this.leftSelected_=leftSelected;this.viewport_.dispatchChangeEvent();},get rightSelected(){return this.rightSelected_;},set rightSelected(rightSelected){if(this.rightSelected_==rightSelected)
-return;this.rightSelected_=rightSelected;this.viewport_.dispatchChangeEvent();},get leftSnapIndicator(){return this.leftSnapIndicator_;},set leftSnapIndicator(leftSnapIndicator){this.leftSnapIndicator_=leftSnapIndicator;this.viewport_.dispatchChangeEvent();},get rightSnapIndicator(){return this.rightSnapIndicator_;},set rightSnapIndicator(rightSnapIndicator){this.rightSnapIndicator_=rightSnapIndicator;this.viewport_.dispatchChangeEvent();},draw:function(ctx,viewLWorld,viewRWorld){if(this.range_.isEmpty)
-return;var dt=this.viewport_.currentDisplayTransform;var markerLWorld=this.min;var markerRWorld=this.max;var markerLView=Math.round(dt.xWorldToView(markerLWorld));var markerRView=Math.round(dt.xWorldToView(markerRWorld));ctx.fillStyle='rgba(0, 0, 0, 0.2)';if(markerLWorld>viewLWorld){ctx.fillRect(dt.xWorldToView(viewLWorld),0,markerLView,ctx.canvas.height);}
+TimelineDisplayTransform.prototype={set:function(that){this.scaleX=that.scaleX;this.panX=that.panX;this.panY=that.panY;},clone:function(){return new TimelineDisplayTransform(this);},equals:function(that){var eq=true;if(that===undefined||that===null){return false;}
+eq&=this.panX===that.panX;eq&=this.panY===that.panY;eq&=this.scaleX===that.scaleX;return!!eq;},almostEquals:function(that){var eq=true;if(that===undefined||that===null){return false;}
+eq&=Math.abs(this.panX-that.panX)<0.001;eq&=Math.abs(this.panY-that.panY)<0.001;eq&=Math.abs(this.scaleX-that.scaleX)<0.001;return!!eq;},incrementPanXInViewUnits:function(xDeltaView){this.panX+=this.xViewVectorToWorld(xDeltaView);},xPanWorldPosToViewPos:function(worldX,viewX,viewWidth){if(typeof viewX==='string'){if(viewX==='left'){viewX=0;}else if(viewX==='center'){viewX=viewWidth/2;}else if(viewX==='right'){viewX=viewWidth-1;}else{throw new Error('viewX must be left|center|right or number.');}}
+this.panX=(viewX/this.scaleX)-worldX;},xPanWorldBoundsIntoView:function(worldMin,worldMax,viewWidth){if(this.xWorldToView(worldMin)<0){this.xPanWorldPosToViewPos(worldMin,'left',viewWidth);}else if(this.xWorldToView(worldMax)>viewWidth){this.xPanWorldPosToViewPos(worldMax,'right',viewWidth);}},xSetWorldBounds:function(worldMin,worldMax,viewWidth){var worldWidth=worldMax-worldMin;var scaleX=viewWidth/worldWidth;var panX=-worldMin;this.setPanAndScale(panX,scaleX);},setPanAndScale:function(p,s){this.scaleX=s;this.panX=p;},xWorldToView:function(x){return(x+this.panX)*this.scaleX;},xWorldVectorToView:function(x){return x*this.scaleX;},xViewToWorld:function(x){return(x/this.scaleX)-this.panX;},xViewVectorToWorld:function(x){return x/this.scaleX;},applyTransformToCanvas:function(ctx){ctx.transform(this.scaleX,0,0,1,this.panX*this.scaleX,0);}};return{TimelineDisplayTransform,};});'use strict';tr.exportTo('tr.ui',function(){function SnapIndicator(y,height){this.y=y;this.height=height;}
+function TimelineInterestRange(vp){this.viewport_=vp;this.range_=new tr.b.math.Range();this.leftSelected_=false;this.rightSelected_=false;this.leftSnapIndicator_=undefined;this.rightSnapIndicator_=undefined;}
+TimelineInterestRange.prototype={get isEmpty(){return this.range_.isEmpty;},reset:function(){this.range_.reset();this.leftSelected_=false;this.rightSelected_=false;this.leftSnapIndicator_=undefined;this.rightSnapIndicator_=undefined;this.viewport_.dispatchChangeEvent();},get min(){return this.range_.min;},set min(min){this.range_.min=min;this.viewport_.dispatchChangeEvent();},get max(){return this.range_.max;},set max(max){this.range_.max=max;this.viewport_.dispatchChangeEvent();},set:function(range){this.range_.reset();this.range_.addRange(range);this.viewport_.dispatchChangeEvent();},setMinAndMax:function(min,max){this.range_.min=min;this.range_.max=max;this.viewport_.dispatchChangeEvent();},get range(){return this.range_.range;},asRangeObject:function(){var range=new tr.b.math.Range();range.addRange(this.range_);return range;},get leftSelected(){return this.leftSelected_;},set leftSelected(leftSelected){if(this.leftSelected_===leftSelected)return;this.leftSelected_=leftSelected;this.viewport_.dispatchChangeEvent();},get rightSelected(){return this.rightSelected_;},set rightSelected(rightSelected){if(this.rightSelected_===rightSelected)return;this.rightSelected_=rightSelected;this.viewport_.dispatchChangeEvent();},get leftSnapIndicator(){return this.leftSnapIndicator_;},set leftSnapIndicator(leftSnapIndicator){this.leftSnapIndicator_=leftSnapIndicator;this.viewport_.dispatchChangeEvent();},get rightSnapIndicator(){return this.rightSnapIndicator_;},set rightSnapIndicator(rightSnapIndicator){this.rightSnapIndicator_=rightSnapIndicator;this.viewport_.dispatchChangeEvent();},draw:function(ctx,viewLWorld,viewRWorld){if(this.range_.isEmpty)return;var dt=this.viewport_.currentDisplayTransform;var markerLWorld=this.min;var markerRWorld=this.max;var markerLView=Math.round(dt.xWorldToView(markerLWorld));var markerRView=Math.round(dt.xWorldToView(markerRWorld));ctx.fillStyle='rgba(0, 0, 0, 0.2)';if(markerLWorld>viewLWorld){ctx.fillRect(dt.xWorldToView(viewLWorld),0,markerLView,ctx.canvas.height);}
 if(markerRWorld<viewRWorld){ctx.fillRect(markerRView,0,dt.xWorldToView(viewRWorld),ctx.canvas.height);}
 var pixelRatio=window.devicePixelRatio||1;ctx.lineWidth=Math.round(pixelRatio);if(this.range_.range>0){this.drawLine_(ctx,viewLWorld,viewRWorld,ctx.canvas.height,this.min,this.leftSelected_);this.drawLine_(ctx,viewLWorld,viewRWorld,ctx.canvas.height,this.max,this.rightSelected_);}else{this.drawLine_(ctx,viewLWorld,viewRWorld,ctx.canvas.height,this.min,this.leftSelected_||this.rightSelected_);}
-ctx.lineWidth=1;},drawLine_:function(ctx,viewLWorld,viewRWorld,height,ts,selected){if(ts<viewLWorld||ts>=viewRWorld)
-return;var dt=this.viewport_.currentDisplayTransform;var viewX=Math.round(dt.xWorldToView(ts));ctx.save();ctx.translate((Math.round(ctx.lineWidth)%2)/2,0);ctx.beginPath();tr.ui.b.drawLine(ctx,viewX,0,viewX,height);if(selected)
-ctx.strokeStyle='rgb(255, 0, 0)';else
-ctx.strokeStyle='rgb(0, 0, 0)';ctx.stroke();ctx.restore();},drawIndicators:function(ctx,viewLWorld,viewRWorld){if(this.leftSnapIndicator_){this.drawIndicator_(ctx,viewLWorld,viewRWorld,this.range_.min,this.leftSnapIndicator_,this.leftSelected_);}
-if(this.rightSnapIndicator_){this.drawIndicator_(ctx,viewLWorld,viewRWorld,this.range_.max,this.rightSnapIndicator_,this.rightSelected_);}},drawIndicator_:function(ctx,viewLWorld,viewRWorld,xWorld,si,selected){var dt=this.viewport_.currentDisplayTransform;var viewX=Math.round(dt.xWorldToView(xWorld));ctx.save();ctx.translate((Math.round(ctx.lineWidth)%2)/2,0);var pixelRatio=window.devicePixelRatio||1;var viewY=si.y*devicePixelRatio;var viewHeight=si.height*devicePixelRatio;var arrowSize=4*pixelRatio;if(selected)
-ctx.fillStyle='rgb(255, 0, 0)';else
-ctx.fillStyle='rgb(0, 0, 0)';tr.ui.b.drawTriangle(ctx,viewX-arrowSize*0.75,viewY,viewX+arrowSize*0.75,viewY,viewX,viewY+arrowSize);ctx.fill();tr.ui.b.drawTriangle(ctx,viewX-arrowSize*0.75,viewY+viewHeight,viewX+arrowSize*0.75,viewY+viewHeight,viewX,viewY+viewHeight-arrowSize);ctx.fill();ctx.restore();}};return{SnapIndicator:SnapIndicator,TimelineInterestRange:TimelineInterestRange};});'use strict';tr.exportTo('tr.ui.tracks',function(){function ContainerToTrackMap(){this.stableIdToTrackMap_={};}
-ContainerToTrackMap.prototype={addContainer:function(container,track){if(!track)
-throw new Error('Must provide a track.');this.stableIdToTrackMap_[container.stableId]=track;},clear:function(){this.stableIdToTrackMap_={};},getTrackByStableId:function(stableId){return this.stableIdToTrackMap_[stableId];}};return{ContainerToTrackMap:ContainerToTrackMap};});'use strict';tr.exportTo('tr.ui.tracks',function(){function EventToTrackMap(){}
-EventToTrackMap.prototype={addEvent:function(event,track){if(!track)
-throw new Error('Must provide a track.');this[event.guid]=track;}};return{EventToTrackMap:EventToTrackMap};});'use strict';tr.exportTo('tr.ui',function(){var TimelineDisplayTransform=tr.ui.TimelineDisplayTransform;var TimelineInterestRange=tr.ui.TimelineInterestRange;function TimelineViewport(parentEl){this.parentEl_=parentEl;this.modelTrackContainer_=undefined;this.currentDisplayTransform_=new TimelineDisplayTransform();this.initAnimationController_();this.showFlowEvents_=false;this.highlightVSync_=false;this.highDetails_=false;this.gridTimebase_=0;this.gridStep_=1000/60;this.gridEnabled_=false;this.hasCalledSetupFunction_=false;this.onResize_=this.onResize_.bind(this);this.onModelTrackControllerScroll_=this.onModelTrackControllerScroll_.bind(this);this.checkForAttachInterval_=setInterval(this.checkForAttach_.bind(this),250);this.majorMarkPositions=[];this.interestRange_=new TimelineInterestRange(this);this.eventToTrackMap_=new tr.ui.tracks.EventToTrackMap();this.containerToTrackMap=new tr.ui.tracks.ContainerToTrackMap();}
-TimelineViewport.prototype={__proto__:tr.b.EventTarget.prototype,setWhenPossible:function(fn){this.pendingSetFunction_=fn;},get isAttachedToDocumentOrInTestMode(){if(this.parentEl_===undefined)
-return;return tr.ui.b.isElementAttachedToDocument(this.parentEl_);},onResize_:function(){this.dispatchChangeEvent();},checkForAttach_:function(){if(!this.isAttachedToDocumentOrInTestMode||this.clientWidth==0)
-return;window.addEventListener('resize',this.dispatchChangeEvent.bind(this));var curSize=this.parentEl_.clientWidth+'x'+
-this.parentEl_.clientHeight;if(this.pendingSetFunction_){this.lastSize_=curSize;try{this.pendingSetFunction_();}catch(ex){console.log('While running setWhenPossible:',ex.message?ex.message+'\n'+ex.stack:ex.stack);}
-this.pendingSetFunction_=undefined;}
-window.clearInterval(this.checkForAttachInterval_);this.checkForAttachInterval_=undefined;},dispatchChangeEvent:function(){tr.b.dispatchSimpleEvent(this,'change');},detach:function(){if(this.checkForAttachInterval_){window.clearInterval(this.checkForAttachInterval_);this.checkForAttachInterval_=undefined;}
-window.removeEventListener('resize',this.dispatchChangeEvent.bind(this));},initAnimationController_:function(){this.dtAnimationController_=new tr.ui.b.AnimationController();this.dtAnimationController_.addEventListener('didtick',function(e){this.onCurentDisplayTransformChange_(e.oldTargetState);}.bind(this));var that=this;this.dtAnimationController_.target={get panX(){return that.currentDisplayTransform_.panX;},set panX(panX){that.currentDisplayTransform_.panX=panX;},get panY(){return that.currentDisplayTransform_.panY;},set panY(panY){that.currentDisplayTransform_.panY=panY;},get scaleX(){return that.currentDisplayTransform_.scaleX;},set scaleX(scaleX){that.currentDisplayTransform_.scaleX=scaleX;},cloneAnimationState:function(){return that.currentDisplayTransform_.clone();},xPanWorldPosToViewPos:function(xWorld,xView){that.currentDisplayTransform_.xPanWorldPosToViewPos(xWorld,xView,that.modelTrackContainer_.canvas.clientWidth);}};},get currentDisplayTransform(){return this.currentDisplayTransform_;},setDisplayTransformImmediately:function(displayTransform){this.dtAnimationController_.cancelActiveAnimation();var oldDisplayTransform=this.dtAnimationController_.target.cloneAnimationState();this.currentDisplayTransform_.set(displayTransform);this.onCurentDisplayTransformChange_(oldDisplayTransform);},queueDisplayTransformAnimation:function(animation){if(!(animation instanceof tr.ui.b.Animation))
-throw new Error('animation must be instanceof tr.ui.b.Animation');this.dtAnimationController_.queueAnimation(animation);},onCurentDisplayTransformChange_:function(oldDisplayTransform){if(this.modelTrackContainer_){this.currentDisplayTransform.panY=tr.b.clamp(this.currentDisplayTransform.panY,0,this.modelTrackContainer_.scrollHeight-
+ctx.lineWidth=1;},drawLine_:function(ctx,viewLWorld,viewRWorld,height,ts,selected){if(ts<viewLWorld||ts>=viewRWorld)return;var dt=this.viewport_.currentDisplayTransform;var viewX=Math.round(dt.xWorldToView(ts));ctx.save();ctx.translate((Math.round(ctx.lineWidth)%2)/2,0);ctx.beginPath();tr.ui.b.drawLine(ctx,viewX,0,viewX,height);if(selected){ctx.strokeStyle='rgb(255, 0, 0)';}else{ctx.strokeStyle='rgb(0, 0, 0)';}
+ctx.stroke();ctx.restore();},drawIndicators:function(ctx,viewLWorld,viewRWorld){if(this.leftSnapIndicator_){this.drawIndicator_(ctx,viewLWorld,viewRWorld,this.range_.min,this.leftSnapIndicator_,this.leftSelected_);}
+if(this.rightSnapIndicator_){this.drawIndicator_(ctx,viewLWorld,viewRWorld,this.range_.max,this.rightSnapIndicator_,this.rightSelected_);}},drawIndicator_:function(ctx,viewLWorld,viewRWorld,xWorld,si,selected){var dt=this.viewport_.currentDisplayTransform;var viewX=Math.round(dt.xWorldToView(xWorld));ctx.save();ctx.translate((Math.round(ctx.lineWidth)%2)/2,0);var pixelRatio=window.devicePixelRatio||1;var viewY=si.y*devicePixelRatio;var viewHeight=si.height*devicePixelRatio;var arrowSize=4*pixelRatio;if(selected){ctx.fillStyle='rgb(255, 0, 0)';}else{ctx.fillStyle='rgb(0, 0, 0)';}
+tr.ui.b.drawTriangle(ctx,viewX-arrowSize*0.75,viewY,viewX+arrowSize*0.75,viewY,viewX,viewY+arrowSize);ctx.fill();tr.ui.b.drawTriangle(ctx,viewX-arrowSize*0.75,viewY+viewHeight,viewX+arrowSize*0.75,viewY+viewHeight,viewX,viewY+viewHeight-arrowSize);ctx.fill();ctx.restore();}};return{SnapIndicator,TimelineInterestRange,};});'use strict';tr.exportTo('tr.ui.tracks',function(){function ContainerToTrackMap(){this.stableIdToTrackMap_={};}
+ContainerToTrackMap.prototype={addContainer:function(container,track){if(!track){throw new Error('Must provide a track.');}
+this.stableIdToTrackMap_[container.stableId]=track;},clear:function(){this.stableIdToTrackMap_={};},getTrackByStableId:function(stableId){return this.stableIdToTrackMap_[stableId];}};return{ContainerToTrackMap,};});'use strict';tr.exportTo('tr.ui.tracks',function(){function EventToTrackMap(){}
+EventToTrackMap.prototype={addEvent:function(event,track){if(!track){throw new Error('Must provide a track.');}
+this[event.guid]=track;}};return{EventToTrackMap,};});'use strict';tr.exportTo('tr.ui',function(){var TimelineDisplayTransform=tr.ui.TimelineDisplayTransform;var TimelineInterestRange=tr.ui.TimelineInterestRange;var IDEAL_MAJOR_MARK_DISTANCE_PX=150;var MAJOR_MARK_ROUNDING_FACTOR=100000;class AnimationControllerProxy{constructor(target){this.target_=target;}
+get panX(){return this.target_.currentDisplayTransform_.panX;}
+set panX(panX){this.target_.currentDisplayTransform_.panX=panX;}
+get panY(){return this.target_.currentDisplayTransform_.panY;}
+set panY(panY){this.target_.currentDisplayTransform_.panY=panY;}
+get scaleX(){return this.target_.currentDisplayTransform_.scaleX;}
+set scaleX(scaleX){this.target_.currentDisplayTransform_.scaleX=scaleX;}
+cloneAnimationState(){return this.target_.currentDisplayTransform_.clone();}
+xPanWorldPosToViewPos(xWorld,xView){this.target_.currentDisplayTransform_.xPanWorldPosToViewPos(xWorld,xView,this.target_.modelTrackContainer_.canvas.clientWidth);}}
+function TimelineViewport(parentEl){this.parentEl_=parentEl;this.modelTrackContainer_=undefined;this.currentDisplayTransform_=new TimelineDisplayTransform();this.initAnimationController_();this.showFlowEvents_=false;this.highlightVSync_=false;this.highDetails_=false;this.gridTimebase_=0;this.gridStep_=1000/60;this.gridEnabled_=false;this.hasCalledSetupFunction_=false;this.onResize_=this.onResize_.bind(this);this.onModelTrackControllerScroll_=this.onModelTrackControllerScroll_.bind(this);this.timeMode_=TimelineViewport.TimeMode.TIME_IN_MS;this.majorMarkWorldPositions_=[];this.majorMarkUnit_=undefined;this.interestRange_=new TimelineInterestRange(this);this.eventToTrackMap_=new tr.ui.tracks.EventToTrackMap();this.containerToTrackMap=new tr.ui.tracks.ContainerToTrackMap();this.dispatchChangeEvent=this.dispatchChangeEvent.bind(this);}
+TimelineViewport.TimeMode={TIME_IN_MS:0,REVISIONS:1};TimelineViewport.prototype={__proto__:tr.b.EventTarget.prototype,get isAttachedToDocumentOrInTestMode(){if(this.parentEl_===undefined)return;return tr.ui.b.isElementAttachedToDocument(this.parentEl_);},onResize_:function(){this.dispatchChangeEvent();},dispatchChangeEvent:function(){tr.b.dispatchSimpleEvent(this,'change');},detach:function(){window.removeEventListener('resize',this.dispatchChangeEvent);},initAnimationController_:function(){this.dtAnimationController_=new tr.ui.b.AnimationController();this.dtAnimationController_.addEventListener('didtick',function(e){this.onCurentDisplayTransformChange_(e.oldTargetState);}.bind(this));this.dtAnimationController_.target=new AnimationControllerProxy(this);},get currentDisplayTransform(){return this.currentDisplayTransform_;},setDisplayTransformImmediately:function(displayTransform){this.dtAnimationController_.cancelActiveAnimation();var oldDisplayTransform=this.dtAnimationController_.target.cloneAnimationState();this.currentDisplayTransform_.set(displayTransform);this.onCurentDisplayTransformChange_(oldDisplayTransform);},queueDisplayTransformAnimation:function(animation){if(!(animation instanceof tr.ui.b.Animation)){throw new Error('animation must be instanceof tr.ui.b.Animation');}
+this.dtAnimationController_.queueAnimation(animation);},onCurentDisplayTransformChange_:function(oldDisplayTransform){if(this.modelTrackContainer_){this.currentDisplayTransform.panY=tr.b.math.clamp(this.currentDisplayTransform.panY,0,this.modelTrackContainer_.scrollHeight-
 this.modelTrackContainer_.clientHeight);}
-var changed=!this.currentDisplayTransform.equals(oldDisplayTransform);var yChanged=this.currentDisplayTransform.panY!==oldDisplayTransform.panY;if(yChanged)
-this.modelTrackContainer_.scrollTop=this.currentDisplayTransform.panY;if(changed)
-this.dispatchChangeEvent();},onModelTrackControllerScroll_:function(e){if(this.dtAnimationController_.activeAnimation&&this.dtAnimationController_.activeAnimation.affectsPanY)
-this.dtAnimationController_.cancelActiveAnimation();var panY=this.modelTrackContainer_.scrollTop;this.currentDisplayTransform_.panY=panY;},get modelTrackContainer(){return this.modelTrackContainer_;},set modelTrackContainer(m){if(this.modelTrackContainer_)
-this.modelTrackContainer_.removeEventListener('scroll',this.onModelTrackControllerScroll_);this.modelTrackContainer_=m;this.modelTrackContainer_.addEventListener('scroll',this.onModelTrackControllerScroll_);},get showFlowEvents(){return this.showFlowEvents_;},set showFlowEvents(showFlowEvents){this.showFlowEvents_=showFlowEvents;this.dispatchChangeEvent();},get highlightVSync(){return this.highlightVSync_;},set highlightVSync(highlightVSync){this.highlightVSync_=highlightVSync;this.dispatchChangeEvent();},get highDetails(){return this.highDetails_;},set highDetails(highDetails){this.highDetails_=highDetails;this.dispatchChangeEvent();},get gridEnabled(){return this.gridEnabled_;},set gridEnabled(enabled){if(this.gridEnabled_==enabled)
-return;this.gridEnabled_=enabled&&true;this.dispatchChangeEvent();},get gridTimebase(){return this.gridTimebase_;},set gridTimebase(timebase){if(this.gridTimebase_==timebase)
-return;this.gridTimebase_=timebase;this.dispatchChangeEvent();},get gridStep(){return this.gridStep_;},get interestRange(){return this.interestRange_;},drawMajorMarkLines:function(ctx){ctx.save();ctx.translate((Math.round(ctx.lineWidth)%2)/2,0);ctx.beginPath();for(var idx in this.majorMarkPositions){var x=Math.floor(this.majorMarkPositions[idx]);tr.ui.b.drawLine(ctx,x,0,x,ctx.canvas.height);}
-ctx.strokeStyle='#ddd';ctx.stroke();ctx.restore();},drawGridLines:function(ctx,viewLWorld,viewRWorld){if(!this.gridEnabled)
-return;var dt=this.currentDisplayTransform;var x=this.gridTimebase;ctx.save();ctx.translate((Math.round(ctx.lineWidth)%2)/2,0);ctx.beginPath();while(x<viewRWorld){if(x>=viewLWorld){var vx=Math.floor(dt.xWorldToView(x));tr.ui.b.drawLine(ctx,vx,0,vx,ctx.canvas.height);}
+var changed=!this.currentDisplayTransform.equals(oldDisplayTransform);var yChanged=this.currentDisplayTransform.panY!==oldDisplayTransform.panY;if(yChanged){this.modelTrackContainer_.scrollTop=this.currentDisplayTransform.panY;}
+if(changed){this.dispatchChangeEvent();}},onModelTrackControllerScroll_:function(e){if(this.dtAnimationController_.activeAnimation&&this.dtAnimationController_.activeAnimation.affectsPanY){this.dtAnimationController_.cancelActiveAnimation();}
+var panY=this.modelTrackContainer_.scrollTop;this.currentDisplayTransform_.panY=panY;},get modelTrackContainer(){return this.modelTrackContainer_;},set modelTrackContainer(m){if(this.modelTrackContainer_){this.modelTrackContainer_.removeEventListener('scroll',this.onModelTrackControllerScroll_);}
+this.modelTrackContainer_=m;this.modelTrackContainer_.addEventListener('scroll',this.onModelTrackControllerScroll_);},get showFlowEvents(){return this.showFlowEvents_;},set showFlowEvents(showFlowEvents){this.showFlowEvents_=showFlowEvents;this.dispatchChangeEvent();},get highlightVSync(){return this.highlightVSync_;},set highlightVSync(highlightVSync){this.highlightVSync_=highlightVSync;this.dispatchChangeEvent();},get highDetails(){return this.highDetails_;},set highDetails(highDetails){this.highDetails_=highDetails;this.dispatchChangeEvent();},get gridEnabled(){return this.gridEnabled_;},set gridEnabled(enabled){if(this.gridEnabled_===enabled)return;this.gridEnabled_=enabled&&true;this.dispatchChangeEvent();},get gridTimebase(){return this.gridTimebase_;},set gridTimebase(timebase){if(this.gridTimebase_===timebase)return;this.gridTimebase_=timebase;this.dispatchChangeEvent();},get gridStep(){return this.gridStep_;},get interestRange(){return this.interestRange_;},get majorMarkWorldPositions(){return this.majorMarkWorldPositions_;},get majorMarkUnit(){switch(this.timeMode_){case TimelineViewport.TimeMode.TIME_IN_MS:return tr.b.Unit.byName.timeInMsAutoFormat;case TimelineViewport.TimeMode.REVISIONS:return tr.b.Unit.byName.count;default:throw new Error('Cannot get Unit for unsupported time mode '+this.timeMode_);}},get timeMode(){return this.timeMode_;},set timeMode(mode){this.timeMode_=mode;this.dispatchChangeEvent();},updateMajorMarkData:function(viewLWorld,viewRWorld){var pixelRatio=window.devicePixelRatio||1;var dt=this.currentDisplayTransform;var idealMajorMarkDistancePix=IDEAL_MAJOR_MARK_DISTANCE_PX*pixelRatio;var idealMajorMarkDistanceWorld=dt.xViewVectorToWorld(idealMajorMarkDistancePix);var majorMarkDistanceWorld=tr.b.math.preferredNumberLargerThanMin(idealMajorMarkDistanceWorld);var firstMajorMark=Math.floor(viewLWorld/majorMarkDistanceWorld)*majorMarkDistanceWorld;this.majorMarkWorldPositions_=[];for(var curX=firstMajorMark;curX<viewRWorld;curX+=majorMarkDistanceWorld){this.majorMarkWorldPositions_.push(Math.floor(MAJOR_MARK_ROUNDING_FACTOR*curX)/MAJOR_MARK_ROUNDING_FACTOR);}},drawMajorMarkLines:function(ctx){ctx.save();ctx.translate((Math.round(ctx.lineWidth)%2)/2,0);ctx.beginPath();for(var majorMark of this.majorMarkWorldPositions_){var x=this.currentDisplayTransform.xWorldToView(majorMark);tr.ui.b.drawLine(ctx,x,0,x,ctx.canvas.height);}
+ctx.strokeStyle='#ddd';ctx.stroke();ctx.restore();},drawGridLines:function(ctx,viewLWorld,viewRWorld){if(!this.gridEnabled)return;var dt=this.currentDisplayTransform;var x=this.gridTimebase;ctx.save();ctx.translate((Math.round(ctx.lineWidth)%2)/2,0);ctx.beginPath();while(x<viewRWorld){if(x>=viewLWorld){var vx=Math.floor(dt.xWorldToView(x));tr.ui.b.drawLine(ctx,vx,0,vx,ctx.canvas.height);}
 x+=this.gridStep;}
 ctx.strokeStyle='rgba(255, 0, 0, 0.25)';ctx.stroke();ctx.restore();},getShiftedSelection:function(selection,offset){var newSelection=new tr.model.EventSet();for(var event of selection){if(event instanceof tr.model.FlowEvent){if(offset>0){newSelection.push(event.endSlice);}else if(offset<0){newSelection.push(event.startSlice);}else{}
 continue;}
 var track=this.trackForEvent(event);track.addEventNearToProvidedEventToSelection(event,offset,newSelection);}
-if(newSelection.length==0)
-return undefined;return newSelection;},rebuildEventToTrackMap:function(){this.eventToTrackMap_=new tr.ui.tracks.EventToTrackMap();this.modelTrackContainer_.addEventsToTrackMap(this.eventToTrackMap_);},rebuildContainerToTrackMap:function(){this.containerToTrackMap.clear();this.modelTrackContainer_.addContainersToTrackMap(this.containerToTrackMap);},trackForEvent:function(event){return this.eventToTrackMap_[event.guid];}};return{TimelineViewport:TimelineViewport};});'use strict';tr.exportTo('tr.c',function(){var BrushingState=tr.ui.b.BrushingState;var EventSet=tr.model.EventSet;var SelectionState=tr.model.SelectionState;var Viewport=tr.ui.TimelineViewport;function BrushingStateController(timelineView){tr.b.EventTarget.call(this);this.timelineView_=timelineView;this.currentBrushingState_=new BrushingState();this.onPopState_=this.onPopState_.bind(this);this.historyEnabled_=false;this.selections_={};}
-BrushingStateController.prototype={__proto__:tr.b.EventTarget.prototype,dispatchChangeEvent_:function(){var e=new tr.b.Event('change',false,false);this.dispatchEvent(e);},get model(){if(!this.timelineView_)
-return undefined;return this.timelineView_.model;},get trackView(){if(!this.timelineView_)
-return undefined;return this.timelineView_.trackView;},get viewport(){if(!this.timelineView_)
-return undefined;if(!this.timelineView_.trackView)
-return undefined;return this.timelineView_.trackView.viewport;},get historyEnabled(){return this.historyEnabled_;},set historyEnabled(historyEnabled){this.historyEnabled_=!!historyEnabled;if(historyEnabled)
-window.addEventListener('popstate',this.onPopState_);else
-window.removeEventListener('popstate',this.onPopState_);},modelWillChange:function(){if(this.currentBrushingState_.isAppliedToModel)
-this.currentBrushingState_.unapplyFromEventSelectionStates();},modelDidChange:function(){this.selections_={};this.currentBrushingState_=new BrushingState();this.currentBrushingState_.applyToEventSelectionStates(this.model);var e=new tr.b.Event('model-changed',false,false);this.dispatchEvent(e);this.dispatchChangeEvent_();},onUserInitiatedSelectionChange_:function(){var selection=this.selection;if(this.historyEnabled){this.selections_[selection.guid]=selection;var state={selection_guid:selection.guid};window.history.pushState(state,document.title);}},onPopState_:function(e){if(e.state===null)
-return;var selection=this.selections_[e.state.selection_guid];if(selection){var newState=this.currentBrushingState_.clone();newState.selection=selection;this.currentBrushingState=newState;}
-e.stopPropagation();},get selection(){return this.currentBrushingState_.selection;},get findMatches(){return this.currentBrushingState_.findMatches;},get selectionOfInterest(){return this.currentBrushingState_.selectionOfInterest;},get currentBrushingState(){return this.currentBrushingState_;},set currentBrushingState(newBrushingState){if(newBrushingState.isAppliedToModel)
-throw new Error('Cannot apply this state, it is applied');var hasValueChanged=!this.currentBrushingState_.equals(newBrushingState);if(newBrushingState!==this.currentBrushingState_&&!hasValueChanged){if(this.currentBrushingState_.isAppliedToModel){this.currentBrushingState_.transferModelOwnershipToClone(newBrushingState);}
+if(newSelection.length===0)return undefined;return newSelection;},rebuildEventToTrackMap:function(){this.eventToTrackMap_=new tr.ui.tracks.EventToTrackMap();this.modelTrackContainer_.addEventsToTrackMap(this.eventToTrackMap_);},rebuildContainerToTrackMap:function(){this.containerToTrackMap.clear();this.modelTrackContainer_.addContainersToTrackMap(this.containerToTrackMap);},trackForEvent:function(event){return this.eventToTrackMap_[event.guid];}};return{TimelineViewport,};});'use strict';tr.exportTo('tr.c',function(){var BrushingState=tr.ui.b.BrushingState;var EventSet=tr.model.EventSet;var SelectionState=tr.model.SelectionState;var Viewport=tr.ui.TimelineViewport;function BrushingStateController(timelineView){tr.b.EventTarget.call(this);this.timelineView_=timelineView;this.currentBrushingState_=new BrushingState();this.onPopState_=this.onPopState_.bind(this);this.historyEnabled_=false;this.selections_={};}
+BrushingStateController.prototype={__proto__:tr.b.EventTarget.prototype,dispatchChangeEvent_:function(){var e=new tr.b.Event('change',false,false);this.dispatchEvent(e);},get model(){if(!this.timelineView_){return undefined;}
+return this.timelineView_.model;},get trackView(){if(!this.timelineView_){return undefined;}
+return this.timelineView_.trackView;},get viewport(){if(!this.timelineView_){return undefined;}
+if(!this.timelineView_.trackView){return undefined;}
+return this.timelineView_.trackView.viewport;},get historyEnabled(){return this.historyEnabled_;},set historyEnabled(historyEnabled){this.historyEnabled_=!!historyEnabled;if(historyEnabled){window.addEventListener('popstate',this.onPopState_);}else{window.removeEventListener('popstate',this.onPopState_);}},modelWillChange:function(){if(this.currentBrushingState_.isAppliedToModel){this.currentBrushingState_.unapplyFromEventSelectionStates();}},modelDidChange:function(){this.selections_={};this.currentBrushingState_=new BrushingState();this.currentBrushingState_.applyToEventSelectionStates(this.model);var e=new tr.b.Event('model-changed',false,false);this.dispatchEvent(e);this.dispatchChangeEvent_();},onUserInitiatedSelectionChange_:function(){var selection=this.selection;if(this.historyEnabled){this.selections_[selection.guid]=selection;var state={selection_guid:selection.guid};window.history.pushState(state,document.title);}},onPopState_:function(e){if(e.state===null)return;var selection=this.selections_[e.state.selection_guid];if(selection){var newState=this.currentBrushingState_.clone();newState.selection=selection;this.currentBrushingState=newState;}
+e.stopPropagation();},get selection(){return this.currentBrushingState_.selection;},get findMatches(){return this.currentBrushingState_.findMatches;},get selectionOfInterest(){return this.currentBrushingState_.selectionOfInterest;},get currentBrushingState(){return this.currentBrushingState_;},set currentBrushingState(newBrushingState){if(newBrushingState.isAppliedToModel){throw new Error('Cannot apply this state, it is applied');}
+var hasValueChanged=!this.currentBrushingState_.equals(newBrushingState);if(newBrushingState!==this.currentBrushingState_&&!hasValueChanged){if(this.currentBrushingState_.isAppliedToModel){this.currentBrushingState_.transferModelOwnershipToClone(newBrushingState);}
 this.currentBrushingState_=newBrushingState;return;}
-if(this.currentBrushingState_.isAppliedToModel)
-this.currentBrushingState_.unapplyFromEventSelectionStates();this.currentBrushingState_=newBrushingState;this.currentBrushingState_.applyToEventSelectionStates(this.model);this.dispatchChangeEvent_();},addAllEventsMatchingFilterToSelectionAsTask:function(filter,selection){var timelineView=this.timelineView_.trackView;if(!timelineView)
-return new tr.b.Task();return timelineView.addAllEventsMatchingFilterToSelectionAsTask(filter,selection);},findTextChangedTo:function(allPossibleMatches){var newBrushingState=this.currentBrushingState_.clone();newBrushingState.findMatches=allPossibleMatches;this.currentBrushingState=newBrushingState;},findFocusChangedTo:function(currentFocus){var newBrushingState=this.currentBrushingState_.clone();newBrushingState.selection=currentFocus;this.currentBrushingState=newBrushingState;this.onUserInitiatedSelectionChange_();},findTextCleared:function(){if(this.xNavStringMarker_!==undefined){this.model.removeAnnotation(this.xNavStringMarker_);this.xNavStringMarker_=undefined;}
+if(this.currentBrushingState_.isAppliedToModel){this.currentBrushingState_.unapplyFromEventSelectionStates();}
+this.currentBrushingState_=newBrushingState;this.currentBrushingState_.applyToEventSelectionStates(this.model);this.dispatchChangeEvent_();},addAllEventsMatchingFilterToSelectionAsTask:function(filter,selection){var timelineView=this.timelineView_.trackView;if(!timelineView){return new tr.b.Task();}
+return timelineView.addAllEventsMatchingFilterToSelectionAsTask(filter,selection);},findTextChangedTo:function(allPossibleMatches){var newBrushingState=this.currentBrushingState_.clone();newBrushingState.findMatches=allPossibleMatches;this.currentBrushingState=newBrushingState;},findFocusChangedTo:function(currentFocus){var newBrushingState=this.currentBrushingState_.clone();newBrushingState.selection=currentFocus;this.currentBrushingState=newBrushingState;this.onUserInitiatedSelectionChange_();},findTextCleared:function(){if(this.xNavStringMarker_!==undefined){this.model.removeAnnotation(this.xNavStringMarker_);this.xNavStringMarker_=undefined;}
 if(this.guideLineAnnotation_!==undefined){this.model.removeAnnotation(this.guideLineAnnotation_);this.guideLineAnnotation_=undefined;}
-var newBrushingState=this.currentBrushingState_.clone();newBrushingState.selection=new EventSet();newBrushingState.findMatches=new EventSet();this.currentBrushingState=newBrushingState;this.onUserInitiatedSelectionChange_();},uiStateFromString:function(string){return tr.ui.b.UIState.fromUserFriendlyString(this.model,this.viewport,string);},navToPosition:function(uiState,showNavLine){this.trackView.navToPosition(uiState,showNavLine);},changeSelectionFromTimeline:function(selection){var newBrushingState=this.currentBrushingState_.clone();newBrushingState.selection=selection;newBrushingState.findMatches=new EventSet();this.currentBrushingState=newBrushingState;this.onUserInitiatedSelectionChange_();},showScriptControlSelection:function(selection){var newBrushingState=this.currentBrushingState_.clone();newBrushingState.selection=selection;newBrushingState.findMatches=new EventSet();this.currentBrushingState=newBrushingState;},changeSelectionFromRequestSelectionChangeEvent:function(selection){var newBrushingState=this.currentBrushingState_.clone();newBrushingState.selection=selection;newBrushingState.findMatches=new EventSet();this.currentBrushingState=newBrushingState;this.onUserInitiatedSelectionChange_();},changeAnalysisViewRelatedEvents:function(eventSet){var newBrushingState=this.currentBrushingState_.clone();newBrushingState.analysisViewRelatedEvents=eventSet;this.currentBrushingState=newBrushingState;},changeAnalysisLinkHoveredEvents:function(eventSet){var newBrushingState=this.currentBrushingState_.clone();newBrushingState.analysisLinkHoveredEvents=eventSet;this.currentBrushingState=newBrushingState;},getViewSpecificBrushingState:function(viewId){return this.currentBrushingState.viewSpecificBrushingStates[viewId];},changeViewSpecificBrushingState:function(viewId,newState){var oldStates=this.currentBrushingState_.viewSpecificBrushingStates;var newStates={};for(var id in oldStates)
-newStates[id]=oldStates[id];if(newState===undefined)
-delete newStates[viewId];else
-newStates[viewId]=newState;var newBrushingState=this.currentBrushingState_.clone();newBrushingState.viewSpecificBrushingStates=newStates;this.currentBrushingState=newBrushingState;}};BrushingStateController.getControllerForElement=function(element){if(tr.isHeadless)
-throw new Error('Unsupported');var currentElement=element;while(currentElement){if(currentElement.brushingStateController)
-return currentElement.brushingStateController;if(currentElement.parentElement){currentElement=currentElement.parentElement;continue;}
-var currentNode=currentElement;while(Polymer.dom(currentNode).parentNode)
-currentNode=Polymer.dom(currentNode).parentNode;currentElement=currentNode.host;}
-return undefined;};return{BrushingStateController:BrushingStateController};});'use strict';Polymer({is:'tr-ui-a-analysis-link',properties:{href:{type:String}},listeners:{'click':'onClicked_','mouseenter':'onMouseEnter_','mouseleave':'onMouseLeave_'},ready:function(){this.selection_=undefined;},attached:function(){this.controller_=tr.c.BrushingStateController.getControllerForElement(this);},detached:function(){this.clearHighlight_();this.controller_=undefined;},set color(c){this.style.color=c;},get selection(){return this.selection_;},set selection(selection){this.selection_=selection;Polymer.dom(this).textContent=selection.userFriendlyName;},setSelectionAndContent:function(selection,opt_textContent){this.selection_=selection;if(opt_textContent)
-Polymer.dom(this).textContent=opt_textContent;},getCurrentSelection_:function(){if(typeof this.selection_==='function')
-return this.selection_();return this.selection_;},setHighlight_:function(opt_eventSet){if(this.controller_)
-this.controller_.changeAnalysisLinkHoveredEvents(opt_eventSet);},clearHighlight_:function(opt_eventSet){this.setHighlight_();},onClicked_:function(clickEvent){if(!this.selection_)
-return;clickEvent.stopPropagation();var event=new tr.model.RequestSelectionChangeEvent();event.selection=this.getCurrentSelection_();this.dispatchEvent(event);},onMouseEnter_:function(){this.setHighlight_(this.getCurrentSelection_());},onMouseLeave_:function(){this.clearHighlight_();}});'use strict';tr.exportTo('tr.ui.b',function(){var TableFormat={};TableFormat.SelectionMode={NONE:0,ROW:1,CELL:2};TableFormat.HighlightStyle={DEFAULT:0,NONE:1,LIGHT:2,DARK:3};TableFormat.ColumnAlignment={LEFT:0,RIGHT:1};return{TableFormat:TableFormat};});'use strict';(function(){var RIGHT_ARROW=String.fromCharCode(0x25b6);var UNSORTED_ARROW=String.fromCharCode(0x25BF);var ASCENDING_ARROW=String.fromCharCode(0x25B4);var DESCENDING_ARROW=String.fromCharCode(0x25BE);var BASIC_INDENTATION=8;var SelectionMode=tr.ui.b.TableFormat.SelectionMode;var HighlightStyle=tr.ui.b.TableFormat.HighlightStyle;var ColumnAlignment=tr.ui.b.TableFormat.ColumnAlignment;Polymer({is:'tr-ui-b-table',created:function(){this.selectionMode_=SelectionMode.NONE;this.rowHighlightStyle_=HighlightStyle.DEFAULT;this.cellHighlightStyle_=HighlightStyle.DEFAULT;this.selectedTableRowInfo_=undefined;this.selectedColumnIndex_=undefined;this.tableColumns_=[];this.tableRows_=[];this.tableRowsInfo_=new WeakMap();this.tableFooterRows_=[];this.tableFooterRowsInfo_=new WeakMap();this.sortColumnIndex_=undefined;this.sortDescending_=false;this.columnsWithExpandButtons_=[];this.headerCells_=[];this.showHeader_=true;this.emptyValue_=undefined;this.subRowsPropertyName_='subRows';this.customizeTableRowCallback_=undefined;this.defaultExpansionStateCallback_=undefined;this.userCanModifySortOrder_=true;},ready:function(){this.$.body.addEventListener('keydown',this.onKeyDown_.bind(this),true);},clear:function(){this.selectionMode_=SelectionMode.NONE;this.rowHighlightStyle_=HighlightStyle.DEFAULT;this.cellHighlightStyle_=HighlightStyle.DEFAULT;this.selectedTableRowInfo_=undefined;this.selectedColumnIndex_=undefined;Polymer.dom(this).textContent='';this.tableColumns_=[];this.tableRows_=[];this.tableRowsInfo_=new WeakMap();this.tableFooterRows_=[];this.tableFooterRowsInfo_=new WeakMap();this.sortColumnIndex_=undefined;this.sortDescending_=false;this.columnsWithExpandButtons_=[];this.headerCells_=[];this.showHeader_=true;this.emptyValue_=undefined;this.subRowsPropertyName_='subRows';this.defaultExpansionStateCallback_=undefined;this.userCanModifySortOrder_=true;},get showHeader(){return this.showHeader_;},set showHeader(showHeader){this.showHeader_=showHeader;this.scheduleRebuildHeaders_();},set subRowsPropertyName(name){this.subRowsPropertyName_=name;},set defaultExpansionStateCallback(cb){this.defaultExpansionStateCallback_=cb;this.scheduleRebuildBody_();},set customizeTableRowCallback(cb){this.customizeTableRowCallback_=cb;this.scheduleRebuildBody_();},get emptyValue(){return this.emptyValue_;},set emptyValue(emptyValue){var previousEmptyValue=this.emptyValue_;this.emptyValue_=emptyValue;if(this.tableRows_.length===0&&emptyValue!==previousEmptyValue)
-this.scheduleRebuildBody_();},set tableColumns(columns){var columnsWithExpandButtons=[];for(var i=0;i<columns.length;i++){if(columns[i].showExpandButtons)
-columnsWithExpandButtons.push(i);}
+var newBrushingState=this.currentBrushingState_.clone();newBrushingState.selection=new EventSet();newBrushingState.findMatches=new EventSet();this.currentBrushingState=newBrushingState;this.onUserInitiatedSelectionChange_();},uiStateFromString:function(string){return tr.ui.b.UIState.fromUserFriendlyString(this.model,this.viewport,string);},navToPosition:function(uiState,showNavLine){this.trackView.navToPosition(uiState,showNavLine);},changeSelectionFromTimeline:function(selection){var newBrushingState=this.currentBrushingState_.clone();newBrushingState.selection=selection;newBrushingState.findMatches=new EventSet();this.currentBrushingState=newBrushingState;this.onUserInitiatedSelectionChange_();},showScriptControlSelection:function(selection){var newBrushingState=this.currentBrushingState_.clone();newBrushingState.selection=selection;newBrushingState.findMatches=new EventSet();this.currentBrushingState=newBrushingState;},changeSelectionFromRequestSelectionChangeEvent:function(selection){var newBrushingState=this.currentBrushingState_.clone();newBrushingState.selection=selection;newBrushingState.findMatches=new EventSet();this.currentBrushingState=newBrushingState;this.onUserInitiatedSelectionChange_();},changeAnalysisViewRelatedEvents:function(eventSet){var newBrushingState=this.currentBrushingState_.clone();newBrushingState.analysisViewRelatedEvents=eventSet;this.currentBrushingState=newBrushingState;},changeAnalysisLinkHoveredEvents:function(eventSet){var newBrushingState=this.currentBrushingState_.clone();newBrushingState.analysisLinkHoveredEvents=eventSet;this.currentBrushingState=newBrushingState;},getViewSpecificBrushingState:function(viewId){return this.currentBrushingState.viewSpecificBrushingStates[viewId];},changeViewSpecificBrushingState:function(viewId,newState){var oldStates=this.currentBrushingState_.viewSpecificBrushingStates;var newStates={};for(var id in oldStates){newStates[id]=oldStates[id];}
+if(newState===undefined){delete newStates[viewId];}else{newStates[viewId]=newState;}
+var newBrushingState=this.currentBrushingState_.clone();newBrushingState.viewSpecificBrushingStates=newStates;this.currentBrushingState=newBrushingState;}};BrushingStateController.getControllerForElement=function(element){if(tr.isHeadless){throw new Error('Unsupported');}
+var currentElement=element;while(currentElement){if(currentElement.brushingStateController){return currentElement.brushingStateController;}
+if(currentElement.parentElement){currentElement=currentElement.parentElement;continue;}
+var currentNode=currentElement;while(Polymer.dom(currentNode).parentNode){currentNode=Polymer.dom(currentNode).parentNode;}
+currentElement=currentNode.host;}
+return undefined;};return{BrushingStateController,};});'use strict';Polymer({is:'tr-ui-a-analysis-link',properties:{href:{type:String}},listeners:{'click':'onClicked_','mouseenter':'onMouseEnter_','mouseleave':'onMouseLeave_'},ready:function(){this.selection_=undefined;},attached:function(){this.controller_=tr.c.BrushingStateController.getControllerForElement(this);},detached:function(){this.clearHighlight_();this.controller_=undefined;},set color(c){this.style.color=c;},get selection(){return this.selection_;},set selection(selection){this.selection_=selection;Polymer.dom(this).textContent=selection.userFriendlyName;},setSelectionAndContent:function(selection,opt_textContent){this.selection_=selection;if(opt_textContent){Polymer.dom(this).textContent=opt_textContent;}},getCurrentSelection_:function(){if(typeof this.selection_==='function'){return this.selection_();}
+return this.selection_;},setHighlight_:function(opt_eventSet){if(this.controller_){this.controller_.changeAnalysisLinkHoveredEvents(opt_eventSet);}},clearHighlight_:function(opt_eventSet){this.setHighlight_();},onClicked_:function(clickEvent){if(!this.selection_)return;clickEvent.stopPropagation();var event=new tr.model.RequestSelectionChangeEvent();event.selection=this.getCurrentSelection_();this.dispatchEvent(event);},onMouseEnter_:function(){this.setHighlight_(this.getCurrentSelection_());},onMouseLeave_:function(){this.clearHighlight_();}});'use strict';tr.exportTo('tr.ui.b',function(){var TableFormat={};TableFormat.SelectionMode={NONE:0,ROW:1,CELL:2};TableFormat.HighlightStyle={DEFAULT:0,NONE:1,LIGHT:2,DARK:3};TableFormat.ColumnAlignment={LEFT:0,RIGHT:1};return{TableFormat,};});'use strict';(function(){var RIGHT_ARROW=String.fromCharCode(0x25b6);var UNSORTED_ARROW=String.fromCharCode(0x25BF);var ASCENDING_ARROW=String.fromCharCode(0x25B4);var DESCENDING_ARROW=String.fromCharCode(0x25BE);var SelectionMode=tr.ui.b.TableFormat.SelectionMode;var HighlightStyle=tr.ui.b.TableFormat.HighlightStyle;var ColumnAlignment=tr.ui.b.TableFormat.ColumnAlignment;Polymer({is:'tr-ui-b-table',created:function(){this.selectionMode_=SelectionMode.NONE;this.rowHighlightStyle_=HighlightStyle.DEFAULT;this.cellHighlightStyle_=HighlightStyle.DEFAULT;this.selectedTableRowInfo_=undefined;this.selectedColumnIndex_=undefined;this.tableColumns_=[];this.tableRows_=[];this.tableRowsInfo_=new WeakMap();this.tableFooterRows_=[];this.tableFooterRowsInfo_=new WeakMap();this.sortColumnIndex_=undefined;this.sortDescending_=false;this.columnsWithExpandButtons_=[];this.headerCells_=[];this.showHeader_=true;this.emptyValue_=undefined;this.subRowsPropertyName_='subRows';this.customizeTableRowCallback_=undefined;this.defaultExpansionStateCallback_=undefined;this.userCanModifySortOrder_=true;this.computedFontSizePx_=undefined;},ready:function(){this.$.body.addEventListener('keydown',this.onKeyDown_.bind(this),true);this.$.body.addEventListener('focus',this.onFocus_.bind(this),true);},clear:function(){this.selectionMode_=SelectionMode.NONE;this.rowHighlightStyle_=HighlightStyle.DEFAULT;this.cellHighlightStyle_=HighlightStyle.DEFAULT;this.selectedTableRowInfo_=undefined;this.selectedColumnIndex_=undefined;Polymer.dom(this).textContent='';this.tableColumns_=[];this.tableRows_=[];this.tableRowsInfo_=new WeakMap();this.tableFooterRows_=[];this.tableFooterRowsInfo_=new WeakMap();this.sortColumnIndex_=undefined;this.sortDescending_=false;this.columnsWithExpandButtons_=[];this.headerCells_=[];this.showHeader_=true;this.emptyValue_=undefined;this.subRowsPropertyName_='subRows';this.defaultExpansionStateCallback_=undefined;this.userCanModifySortOrder_=true;},set zebra(zebra){if(zebra){this.setAttribute('zebra',true);}else{this.removeAttribute('zebra');}},get zebra(){return this.getAttribute('zebra');},get showHeader(){return this.showHeader_;},set showHeader(showHeader){this.showHeader_=showHeader;this.scheduleRebuildHeaders_();},set subRowsPropertyName(name){this.subRowsPropertyName_=name;},set defaultExpansionStateCallback(cb){this.defaultExpansionStateCallback_=cb;this.scheduleRebuildBody_();},set customizeTableRowCallback(cb){this.customizeTableRowCallback_=cb;this.scheduleRebuildBody_();},get emptyValue(){return this.emptyValue_;},set emptyValue(emptyValue){var previousEmptyValue=this.emptyValue_;this.emptyValue_=emptyValue;if(this.tableRows_.length===0&&emptyValue!==previousEmptyValue){this.scheduleRebuildBody_();}},set tableColumns(columns){var columnsWithExpandButtons=[];for(var i=0;i<columns.length;i++){if(columns[i].showExpandButtons){columnsWithExpandButtons.push(i);}}
 if(columnsWithExpandButtons.length===0){columnsWithExpandButtons=[0];}
-for(var i=0;i<columns.length;i++){var colInfo=columns[i];if(colInfo.width===undefined)
-continue;var hasExpandButton=columnsWithExpandButtons.indexOf(i)!==-1;var w=colInfo.width;if(w){if(/\d+px/.test(w)){continue;}else if(/\d+%/.test(w)){if(hasExpandButton){throw new Error('Columns cannot be %-sized and host '+' an expand button');}}else{throw new Error('Unrecognized width string');}}}
-this.tableColumns_=columns;this.headerCells_=[];this.columnsWithExpandButtons_=columnsWithExpandButtons;this.sortColumnIndex=undefined;this.scheduleRebuildHeaders_();this.tableRows=this.tableRows_;},get tableColumns(){return this.tableColumns_;},set tableRows(rows){this.selectedTableRowInfo_=undefined;this.selectedColumnIndex_=undefined;this.maybeUpdateSelectedRow_();this.tableRows_=rows;this.tableRowsInfo_=new WeakMap();this.scheduleRebuildBody_();},get tableRows(){return this.tableRows_;},set footerRows(rows){this.tableFooterRows_=rows;this.tableFooterRowsInfo_=new WeakMap();this.scheduleRebuildFooter_();},get footerRows(){return this.tableFooterRows_;},get userCanModifySortOrder(){return this.userCanModifySortOrder_;},set userCanModifySortOrder(userCanModifySortOrder){var newUserCanModifySortOrder=!!userCanModifySortOrder;if(newUserCanModifySortOrder===this.userCanModifySortOrder_)
-return
-this.userCanModifySortOrder_=newUserCanModifySortOrder;this.scheduleRebuildHeaders_();},set sortColumnIndex(number){if(number===this.sortColumnIndex_)
-return;if(number!==undefined){if(this.tableColumns_.length<=number)
-throw new Error('Column number '+number+' is out of bounds.');if(!this.tableColumns_[number].cmp)
-throw new Error('Column '+number+' does not have a comparator.');}
+for(var i=0;i<columns.length;i++){var colInfo=columns[i];if(colInfo.width===undefined)continue;var hasExpandButton=columnsWithExpandButtons.includes(i);var w=colInfo.width;if(w){if(/\d+px/.test(w)){continue;}else if(/\d+%/.test(w)){if(hasExpandButton){throw new Error('Columns cannot be %-sized and host '+' an expand button');}}else{throw new Error('Unrecognized width string');}}}
+var sortIndex=undefined;var currentSortColumn=this.tableColumns[this.sortColumnIndex_];if(currentSortColumn){for(var[i,column]of columns.entries()){if(currentSortColumn.title===column.title){sortIndex=i;break;}}}
+this.tableColumns_=columns;this.headerCells_=[];this.columnsWithExpandButtons_=columnsWithExpandButtons;this.scheduleRebuildHeaders_();this.sortColumnIndex=sortIndex;this.tableRows=this.tableRows_;},get tableColumns(){return this.tableColumns_;},set tableRows(rows){this.selectedTableRowInfo_=undefined;this.selectedColumnIndex_=undefined;this.tableRows_=rows;this.tableRowsInfo_=new WeakMap();this.scheduleRebuildBody_();},get tableRows(){return this.tableRows_;},set footerRows(rows){this.tableFooterRows_=rows;this.tableFooterRowsInfo_=new WeakMap();this.scheduleRebuildFooter_();},get footerRows(){return this.tableFooterRows_;},get userCanModifySortOrder(){return this.userCanModifySortOrder_;},set userCanModifySortOrder(userCanModifySortOrder){var newUserCanModifySortOrder=!!userCanModifySortOrder;if(newUserCanModifySortOrder===this.userCanModifySortOrder_){return;}
+this.userCanModifySortOrder_=newUserCanModifySortOrder;this.scheduleRebuildHeaders_();},set sortColumnIndex(number){if(number===this.sortColumnIndex_)return;if(number!==undefined){if(this.tableColumns_.length<=number){throw new Error('Column number '+number+' is out of bounds.');}
+if(!this.tableColumns_[number].cmp){throw new Error('Column '+number+' does not have a comparator.');}}
 this.sortColumnIndex_=number;this.updateHeaderArrows_();this.scheduleRebuildBody_();this.dispatchSortingChangedEvent_();},get sortColumnIndex(){return this.sortColumnIndex_;},set sortDescending(value){var newValue=!!value;if(newValue!==this.sortDescending_){this.sortDescending_=newValue;this.updateHeaderArrows_();this.scheduleRebuildBody_();this.dispatchSortingChangedEvent_();}},get sortDescending(){return this.sortDescending_;},updateHeaderArrows_:function(){for(var i=0;i<this.headerCells_.length;i++){var headerCell=this.headerCells_[i];var isColumnCurrentlySorted=i===this.sortColumnIndex_;if(!this.tableColumns_[i].cmp||(!this.userCanModifySortOrder_&&!isColumnCurrentlySorted)){headerCell.sideContent='';continue;}
 if(!isColumnCurrentlySorted){headerCell.sideContent=UNSORTED_ARROW;headerCell.sideContentDisabled=false;continue;}
 headerCell.sideContent=this.sortDescending_?DESCENDING_ARROW:ASCENDING_ARROW;headerCell.sideContentDisabled=!this.userCanModifySortOrder_;}},generateHeaderColumns_:function(){var selectedTableColumnIndex=this.selectedTableColumnIndex;Polymer.dom(this.$.cols).textContent='';for(var i=0;i<this.tableColumns_.length;++i){var colElement=document.createElement('col');if(i===selectedTableColumnIndex){colElement.setAttribute('selected',true);}
 Polymer.dom(this.$.cols).appendChild(colElement);}
-this.headerCells_=[];Polymer.dom(this.$.head).textContent='';if(!this.showHeader_)
-return;var tr=this.appendNewElement_(this.$.head,'tr');for(var i=0;i<this.tableColumns_.length;i++){var td=this.appendNewElement_(tr,'td');var headerCell=document.createElement('tr-ui-b-table-header-cell');headerCell.column=this.tableColumns_[i];if(this.tableColumns_[i].cmp){var isColumnCurrentlySorted=i===this.sortColumnIndex_;if(isColumnCurrentlySorted){headerCell.sideContent=this.sortDescending_?DESCENDING_ARROW:ASCENDING_ARROW;if(!this.userCanModifySortOrder_)
-headerCell.sideContentDisabled=true;}
-if(this.userCanModifySortOrder_){Polymer.dom(td).classList.add('sensitive');if(!isColumnCurrentlySorted)
-headerCell.sideContent=UNSORTED_ARROW;headerCell.tapCallback=this.createSortCallback_(i);}}
-Polymer.dom(td).appendChild(headerCell);this.headerCells_.push(headerCell);}},applySizes_:function(){if(this.tableRows_.length===0&&!this.showHeader)
-return;var rowToRemoveSizing;var rowToSize;if(this.showHeader){rowToSize=Polymer.dom(this.$.head).children[0];rowToRemoveSizing=Polymer.dom(this.$.body).children[0];}else{rowToSize=Polymer.dom(this.$.body).children[0];rowToRemoveSizing=Polymer.dom(this.$.head).children[0];}
+this.headerCells_=[];Polymer.dom(this.$.head).textContent='';if(!this.showHeader_)return;var tr=this.appendNewElement_(this.$.head,'tr');for(var i=0;i<this.tableColumns_.length;i++){var td=this.appendNewElement_(tr,'td');var headerCell=document.createElement('tr-ui-b-table-header-cell');headerCell.column=this.tableColumns_[i];if(this.tableColumns_[i].cmp){var isColumnCurrentlySorted=i===this.sortColumnIndex_;if(isColumnCurrentlySorted){headerCell.sideContent=this.sortDescending_?DESCENDING_ARROW:ASCENDING_ARROW;if(!this.userCanModifySortOrder_){headerCell.sideContentDisabled=true;}}
+if(this.userCanModifySortOrder_){Polymer.dom(td).classList.add('sensitive');if(!isColumnCurrentlySorted){headerCell.sideContent=UNSORTED_ARROW;}
+headerCell.tapCallback=this.createSortCallback_(i);}}
+Polymer.dom(td).appendChild(headerCell);this.headerCells_.push(headerCell);}},applySizes_:function(){if(this.tableRows_.length===0&&!this.showHeader)return;var rowToRemoveSizing;var rowToSize;if(this.showHeader){rowToSize=Polymer.dom(this.$.head).children[0];rowToRemoveSizing=Polymer.dom(this.$.body).children[0];}else{rowToSize=Polymer.dom(this.$.body).children[0];rowToRemoveSizing=Polymer.dom(this.$.head).children[0];}
 for(var i=0;i<this.tableColumns_.length;i++){if(rowToRemoveSizing&&Polymer.dom(rowToRemoveSizing).children[i]){var tdToRemoveSizing=Polymer.dom(rowToRemoveSizing).children[i];tdToRemoveSizing.style.minWidth='';tdToRemoveSizing.style.width='';}
-var td=Polymer.dom(rowToSize).children[i];var delta;if(this.columnsWithExpandButtons_.indexOf(i)!==-1){td.style.paddingLeft=BASIC_INDENTATION+'px';delta=BASIC_INDENTATION+'px';}else{delta=undefined;}
-function calc(base,delta){if(delta)
-return'calc('+base+' - '+delta+')';else
+var td=Polymer.dom(rowToSize).children[i];var delta;if(this.columnsWithExpandButtons_.includes(i)){td.style.paddingLeft=this.basicIndentation_+'px';delta=this.basicIndentation_+'px';}else{delta=undefined;}
+function calc(base,delta){if(delta){return'calc('+base+' - '+delta+')';}
 return base;}
-var w=this.tableColumns_[i].width;if(w){if(/\d+px/.test(w)){td.style.minWidth=calc(w,delta);}else if(/\d+%/.test(w)){td.style.width=w;}else{throw new Error('Unrecognized width string: '+w);}}}},createSortCallback_:function(columnNumber){return function(){if(!this.userCanModifySortOrder_)
-return;var previousIndex=this.sortColumnIndex;this.sortColumnIndex=columnNumber;if(previousIndex!==columnNumber)
-this.sortDescending=false;else
-this.sortDescending=!this.sortDescending;}.bind(this);},generateTableRowNodes_:function(tableSection,userRows,rowInfoMap,indentation,lastAddedRow,parentRowInfo){if(this.sortColumnIndex_!==undefined&&tableSection===this.$.body){userRows=userRows.slice();userRows.sort(function(rowA,rowB){var c=this.tableColumns_[this.sortColumnIndex_].cmp(rowA,rowB);if(this.sortDescending_)
-c=-c;return c;}.bind(this));}
+var w=this.tableColumns_[i].width;if(w){if(/\d+px/.test(w)){td.style.minWidth=calc(w,delta);}else if(/\d+%/.test(w)){td.style.width=w;}else{throw new Error('Unrecognized width string: '+w);}}}},createSortCallback_:function(columnNumber){return function(){if(!this.userCanModifySortOrder_)return;var previousIndex=this.sortColumnIndex;this.sortColumnIndex=columnNumber;if(previousIndex!==columnNumber){this.sortDescending=false;}else{this.sortDescending=!this.sortDescending;}}.bind(this);},generateTableRowNodes_:function(tableSection,userRows,rowInfoMap,indentation,lastAddedRow,parentRowInfo){if(this.sortColumnIndex_!==undefined&&tableSection===this.$.body){userRows=userRows.slice();userRows.sort(function(rowA,rowB){var c=this.tableColumns_[this.sortColumnIndex_].cmp(rowA,rowB);if(this.sortDescending_){c=-c;}
+return c;}.bind(this));}
 for(var i=0;i<userRows.length;i++){var userRow=userRows[i];var rowInfo=this.getOrCreateRowInfoFor_(rowInfoMap,userRow,parentRowInfo);var htmlNode=this.getHTMLNodeForRowInfo_(tableSection,rowInfo,rowInfoMap,indentation);if(lastAddedRow===undefined){Polymer.dom(tableSection).insertBefore(htmlNode,Polymer.dom(tableSection).firstChild);}else{var nextSiblingOfLastAdded=Polymer.dom(lastAddedRow).nextSibling;Polymer.dom(tableSection).insertBefore(htmlNode,nextSiblingOfLastAdded);}
-this.updateTabIndexForTableRowNode_(htmlNode);lastAddedRow=htmlNode;if(!rowInfo.isExpanded)
-continue;lastAddedRow=this.generateTableRowNodes_(tableSection,userRow[this.subRowsPropertyName_],rowInfoMap,indentation+1,lastAddedRow,rowInfo);}
+lastAddedRow=htmlNode;if(!rowInfo.isExpanded)continue;lastAddedRow=this.generateTableRowNodes_(tableSection,userRow[this.subRowsPropertyName_],rowInfoMap,indentation+1,lastAddedRow,rowInfo);}
 return lastAddedRow;},getOrCreateRowInfoFor_:function(rowInfoMap,userRow,parentRowInfo){var rowInfo=undefined;if(rowInfoMap.has(userRow)){rowInfo=rowInfoMap.get(userRow);}else{rowInfo={userRow:userRow,htmlNode:undefined,parentRowInfo:parentRowInfo};rowInfoMap.set(userRow,rowInfo);}
-rowInfo.isExpanded=this.getExpandedForUserRow_(userRow);return rowInfo;},customizeTableRow_:function(userRow,trElement){if(!this.customizeTableRowCallback_)
-return;this.customizeTableRowCallback_(userRow,trElement);},getHTMLNodeForRowInfo_:function(tableSection,rowInfo,rowInfoMap,indentation){if(rowInfo.htmlNode){this.customizeTableRow_(rowInfo.userRow,rowInfo.htmlNode);return rowInfo.htmlNode;}
-var INDENT_SPACE=indentation*16;var INDENT_SPACE_NO_BUTTON=indentation*16+BASIC_INDENTATION;var trElement=this.ownerDocument.createElement('tr');rowInfo.htmlNode=trElement;rowInfo.indentation=indentation;trElement.rowInfo=rowInfo;this.customizeTableRow_(rowInfo.userRow,trElement);var isBodyRow=tableSection===this.$.body;var isExpandableRow=rowInfo.userRow[this.subRowsPropertyName_]&&rowInfo.userRow[this.subRowsPropertyName_].length;for(var i=0;i<this.tableColumns_.length;){var td=this.appendNewElement_(trElement,'td');td.columnIndex=i;var column=this.tableColumns_[i];var value=column.value(rowInfo.userRow);var colSpan=column.colSpan?column.colSpan:1;td.style.colSpan=colSpan;switch(column.align){case undefined:case ColumnAlignment.LEFT:break;case ColumnAlignment.RIGHT:td.style.textAlign='right';break;default:throw new Error('Invalid alignment of column at index='+i+': '+column.align);}
-if(this.doesColumnIndexSupportSelection(i))
-Polymer.dom(td).classList.add('supports-selection');if(this.columnsWithExpandButtons_.indexOf(i)!=-1){if(rowInfo.userRow[this.subRowsPropertyName_]&&rowInfo.userRow[this.subRowsPropertyName_].length>0){td.style.paddingLeft=INDENT_SPACE+'px';var expandButton=this.appendNewElement_(td,'expand-button');Polymer.dom(expandButton).textContent=RIGHT_ARROW;if(rowInfo.isExpanded)
-Polymer.dom(expandButton).classList.add('button-expanded');}else{td.style.paddingLeft=INDENT_SPACE_NO_BUTTON+'px';}}
+rowInfo.isExpanded=this.getExpandedForUserRow_(userRow);return rowInfo;},customizeTableRow_:function(userRow,trElement){if(!this.customizeTableRowCallback_)return;this.customizeTableRowCallback_(userRow,trElement);},get basicIndentation_(){if(this.computedFontSizePx_===undefined){this.computedFontSizePx_=parseInt(getComputedStyle(this).fontSize)||16;}
+return this.computedFontSizePx_-2;},getHTMLNodeForRowInfo_:function(tableSection,rowInfo,rowInfoMap,indentation){if(rowInfo.htmlNode){this.customizeTableRow_(rowInfo.userRow,rowInfo.htmlNode);return rowInfo.htmlNode;}
+var INDENT_SPACE=indentation*16;var INDENT_SPACE_NO_BUTTON=indentation*16+this.basicIndentation_;var trElement=this.ownerDocument.createElement('tr');rowInfo.htmlNode=trElement;rowInfo.indentation=indentation;trElement.rowInfo=rowInfo;this.customizeTableRow_(rowInfo.userRow,trElement);var isBodyRow=tableSection===this.$.body;var isExpandableRow=rowInfo.userRow[this.subRowsPropertyName_]&&rowInfo.userRow[this.subRowsPropertyName_].length;for(var i=0;i<this.tableColumns_.length;){var td=this.appendNewElement_(trElement,'td');td.columnIndex=i;var column=this.tableColumns_[i];var value=column.value(rowInfo.userRow);var colSpan=column.colSpan?column.colSpan:1;td.style.colSpan=colSpan;switch(column.align){case undefined:case ColumnAlignment.LEFT:break;case ColumnAlignment.RIGHT:td.style.textAlign='right';break;default:throw new Error('Invalid alignment of column at index='+i+': '+column.align);}
+if(this.doesColumnIndexSupportSelection(i)){Polymer.dom(td).classList.add('supports-selection');}
+if(this.columnsWithExpandButtons_.includes(i)){if(rowInfo.userRow[this.subRowsPropertyName_]&&rowInfo.userRow[this.subRowsPropertyName_].length>0){td.style.paddingLeft=INDENT_SPACE+'px';td.style.display='flex';var expandButton=this.appendNewElement_(td,'expand-button');Polymer.dom(expandButton).textContent=RIGHT_ARROW;if(rowInfo.isExpanded){Polymer.dom(expandButton).classList.add('button-expanded');}}else{td.style.paddingLeft=INDENT_SPACE_NO_BUTTON+'px';}}
 if(value!==undefined){Polymer.dom(td).appendChild(tr.ui.b.asHTMLOrTextNode(value,this.ownerDocument));}
-if(isBodyRow||isExpandableRow){td.addEventListener('click',function(i,e){e.stopPropagation();if(e.target.tagName==='EXPAND-BUTTON'){this.setExpandedForUserRow_(tableSection,rowInfoMap,rowInfo.userRow,!rowInfo.isExpanded);return;}
-if(isBodyRow&&this.selectionMode_!==SelectionMode.NONE){var shouldSelect=false;switch(this.selectionMode_){case SelectionMode.ROW:shouldSelect=this.selectedTableRowInfo_!==rowInfo;break;case SelectionMode.CELL:if(this.doesColumnIndexSupportSelection(i)){shouldSelect=this.selectedTableRowInfo_!==rowInfo||this.selectedColumnIndex_!==i;}
+td.addEventListener('click',function(i,clickEvent){clickEvent.preventDefault();if(!isBodyRow&&!isExpandableRow)return;clickEvent.stopPropagation();if(clickEvent.target.tagName==='EXPAND-BUTTON'){this.setExpandedForUserRow_(tableSection,rowInfoMap,rowInfo.userRow,!rowInfo.isExpanded);return;}
+if(isBodyRow&&this.selectionMode_!==SelectionMode.NONE){var shouldSelect=false;var shouldFocus=false;switch(this.selectionMode_){case SelectionMode.ROW:shouldSelect=this.selectedTableRowInfo_!==rowInfo;shouldFocus=true;break;case SelectionMode.CELL:if(this.doesColumnIndexSupportSelection(i)){shouldSelect=this.selectedTableRowInfo_!==rowInfo||this.selectedColumnIndex_!==i;shouldFocus=true;}
 break;default:throw new Error('Invalid selection mode '+
 this.selectionMode_);}
+if(shouldFocus){this.focus();}
 if(shouldSelect){this.didTableRowInfoGetClicked_(rowInfo,i);return;}}
-if(isExpandableRow){this.setExpandedForUserRow_(tableSection,rowInfoMap,rowInfo.userRow,!rowInfo.isExpanded);}}.bind(this,i));}
-if(isBodyRow){td.addEventListener('dblclick',function(i,e){e.stopPropagation();this.dispatchStepIntoEvent_(rowInfo,i);}.bind(this,i));}
+if(isExpandableRow){this.setExpandedForUserRow_(tableSection,rowInfoMap,rowInfo.userRow,!rowInfo.isExpanded);}}.bind(this,i));if(isBodyRow){td.addEventListener('dblclick',function(i,e){e.stopPropagation();this.dispatchStepIntoEvent_(rowInfo,i);}.bind(this,i));}
 i+=colSpan;}
-return rowInfo.htmlNode;},removeSubNodes_:function(tableSection,rowInfo,rowInfoMap){if(rowInfo.userRow[this.subRowsPropertyName_]===undefined)
-return;for(var i=0;i<rowInfo.userRow[this.subRowsPropertyName_].length;i++){var subRow=rowInfo.userRow[this.subRowsPropertyName_][i];var subRowInfo=rowInfoMap.get(subRow);if(!subRowInfo)
-continue;var subNode=subRowInfo.htmlNode;if(subNode&&Polymer.dom(subNode).parentNode===tableSection){Polymer.dom(tableSection).removeChild(subNode);this.removeSubNodes_(tableSection,subRowInfo,rowInfoMap);}}},scheduleRebuildHeaders_:function(){this.headerDirty_=true;this.scheduleRebuild_();},scheduleRebuildBody_:function(){this.bodyDirty_=true;this.scheduleRebuild_();},scheduleRebuildFooter_:function(){this.footerDirty_=true;this.scheduleRebuild_();},scheduleRebuild_:function(){if(this.rebuildPending_)
-return;this.rebuildPending_=true;setTimeout(function(){this.rebuildPending_=false;this.rebuild();}.bind(this),0);},rebuildIfNeeded_:function(){this.rebuild();},rebuild:function(){var wasBodyOrHeaderDirty=this.headerDirty_||this.bodyDirty_;if(this.headerDirty_){this.generateHeaderColumns_();this.headerDirty_=false;}
+return rowInfo.htmlNode;},removeSubNodes_:function(tableSection,rowInfo,rowInfoMap){if(rowInfo.userRow[this.subRowsPropertyName_]===undefined)return;for(var i=0;i<rowInfo.userRow[this.subRowsPropertyName_].length;i++){var subRow=rowInfo.userRow[this.subRowsPropertyName_][i];var subRowInfo=rowInfoMap.get(subRow);if(!subRowInfo)continue;var subNode=subRowInfo.htmlNode;if(subNode&&Polymer.dom(subNode).parentNode===tableSection){Polymer.dom(tableSection).removeChild(subNode);this.removeSubNodes_(tableSection,subRowInfo,rowInfoMap);}}},scheduleRebuildHeaders_:function(){this.headerDirty_=true;this.scheduleRebuild_();},scheduleRebuildBody_:function(){this.bodyDirty_=true;this.scheduleRebuild_();},scheduleRebuildFooter_:function(){this.footerDirty_=true;this.scheduleRebuild_();},scheduleRebuild_:function(){if(this.rebuildPending_)return;this.rebuildPending_=true;setTimeout(function(){this.rebuildPending_=false;this.rebuild();}.bind(this),0);},rebuildIfNeeded_:function(){this.rebuild();},rebuild:function(){var wasBodyOrHeaderDirty=this.headerDirty_||this.bodyDirty_;if(this.headerDirty_){this.generateHeaderColumns_();this.headerDirty_=false;}
 if(this.bodyDirty_){Polymer.dom(this.$.body).textContent='';this.generateTableRowNodes_(this.$.body,this.tableRows_,this.tableRowsInfo_,0,undefined,undefined);if(this.tableRows_.length===0&&this.emptyValue_!==undefined){var trElement=this.ownerDocument.createElement('tr');Polymer.dom(this.$.body).appendChild(trElement);Polymer.dom(trElement).classList.add('empty-row');var td=this.ownerDocument.createElement('td');Polymer.dom(trElement).appendChild(td);td.colSpan=this.tableColumns_.length;var emptyValue=this.emptyValue_;Polymer.dom(td).appendChild(tr.ui.b.asHTMLOrTextNode(emptyValue,this.ownerDocument));}
 this.bodyDirty_=false;}
-if(wasBodyOrHeaderDirty)
-this.applySizes_();if(this.footerDirty_){Polymer.dom(this.$.foot).textContent='';this.generateTableRowNodes_(this.$.foot,this.tableFooterRows_,this.tableFooterRowsInfo_,0,undefined,undefined);if(this.tableFooterRowsInfo_.length){Polymer.dom(this.$.body).classList.add('has-footer');}else{Polymer.dom(this.$.body).classList.remove('has-footer');}
-this.footerDirty_=false;}},appendNewElement_:function(parent,tagName){var element=parent.ownerDocument.createElement(tagName);Polymer.dom(parent).appendChild(element);return element;},getExpandedForTableRow:function(userRow){this.rebuildIfNeeded_();var rowInfo=this.tableRowsInfo_.get(userRow);if(rowInfo===undefined)
-throw new Error('Row has not been seen, must expand its parents');return rowInfo.isExpanded;},getExpandedForUserRow_:function(userRow){if(userRow[this.subRowsPropertyName_]===undefined)
-return false;if(userRow[this.subRowsPropertyName_].length===0)
-return false;if(userRow.isExpanded)
-return true;if(userRow.isExpanded===false)
-return false;var rowInfo=this.tableRowsInfo_.get(userRow);if(rowInfo&&rowInfo.isExpanded)
-return true;if(this.defaultExpansionStateCallback_===undefined)
-return false;var parentUserRow=undefined;if(rowInfo&&rowInfo.parentRowInfo)
-parentUserRow=rowInfo.parentRowInfo.userRow;return this.defaultExpansionStateCallback_(userRow,parentUserRow);},setExpandedForTableRow:function(userRow,expanded){this.rebuildIfNeeded_();var rowInfo=this.tableRowsInfo_.get(userRow);if(rowInfo===undefined)
-throw new Error('Row has not been seen, must expand its parents');return this.setExpandedForUserRow_(this.$.body,this.tableRowsInfo_,userRow,expanded);},setExpandedForUserRow_:function(tableSection,rowInfoMap,userRow,expanded){this.rebuildIfNeeded_();var rowInfo=rowInfoMap.get(userRow);if(rowInfo===undefined)
-throw new Error('Row has not been seen, must expand its parents');rowInfo.isExpanded=!!expanded;if(rowInfo.htmlNode===undefined)
-return;if(rowInfo.htmlNode.parentElement!==tableSection)
-return;var expandButton=Polymer.dom(rowInfo.htmlNode).querySelector('expand-button');if(rowInfo.isExpanded){Polymer.dom(expandButton).classList.add('button-expanded');var lastAddedRow=rowInfo.htmlNode;if(rowInfo.userRow[this.subRowsPropertyName_]){this.generateTableRowNodes_(tableSection,rowInfo.userRow[this.subRowsPropertyName_],rowInfoMap,rowInfo.indentation+1,lastAddedRow,rowInfo);}}else{Polymer.dom(expandButton).classList.remove('button-expanded');this.removeSubNodes_(tableSection,rowInfo,rowInfoMap);}
-this.maybeUpdateSelectedRow_();},get selectionMode(){return this.selectionMode_;},set selectionMode(selectionMode){if(!tr.b.dictionaryContainsValue(SelectionMode,selectionMode))
-throw new Error('Invalid selection mode '+selectionMode);this.rebuildIfNeeded_();this.selectionMode_=selectionMode;this.didSelectionStateChange_();},get rowHighlightStyle(){return this.rowHighlightStyle_;},set rowHighlightStyle(rowHighlightStyle){if(!tr.b.dictionaryContainsValue(HighlightStyle,rowHighlightStyle))
-throw new Error('Invalid row highlight style '+rowHighlightStyle);this.rebuildIfNeeded_();this.rowHighlightStyle_=rowHighlightStyle;this.didSelectionStateChange_();},get resolvedRowHighlightStyle(){if(this.rowHighlightStyle_!==HighlightStyle.DEFAULT)
-return this.rowHighlightStyle_;switch(this.selectionMode_){case SelectionMode.NONE:return HighlightStyle.NONE;case SelectionMode.ROW:return HighlightStyle.DARK;case SelectionMode.CELL:return HighlightStyle.LIGHT;default:throw new Error('Invalid selection mode '+selectionMode);}},get cellHighlightStyle(){return this.cellHighlightStyle_;},set cellHighlightStyle(cellHighlightStyle){if(!tr.b.dictionaryContainsValue(HighlightStyle,cellHighlightStyle))
-throw new Error('Invalid cell highlight style '+cellHighlightStyle);this.rebuildIfNeeded_();this.cellHighlightStyle_=cellHighlightStyle;this.didSelectionStateChange_();},get resolvedCellHighlightStyle(){if(this.cellHighlightStyle_!==HighlightStyle.DEFAULT)
-return this.cellHighlightStyle_;switch(this.selectionMode_){case SelectionMode.NONE:case SelectionMode.ROW:return HighlightStyle.NONE;case SelectionMode.CELL:return HighlightStyle.DARK;default:throw new Error('Invalid selection mode '+selectionMode);}},setHighlightStyle_:function(highlightAttribute,resolvedHighlightStyle){switch(resolvedHighlightStyle){case HighlightStyle.NONE:Polymer.dom(this.$.body).removeAttribute(highlightAttribute);break;case HighlightStyle.LIGHT:Polymer.dom(this.$.body).setAttribute(highlightAttribute,'light');break;case HighlightStyle.DARK:Polymer.dom(this.$.body).setAttribute(highlightAttribute,'dark');break;default:throw new Error('Invalid resolved highlight style '+
-resolvedHighlightStyle);}},didSelectionStateChange_:function(){this.setHighlightStyle_('row-highlight-style',this.resolvedRowHighlightStyle);this.setHighlightStyle_('cell-highlight-style',this.resolvedCellHighlightStyle);for(var i=0;i<Polymer.dom(this.$.body).children.length;i++){this.updateTabIndexForTableRowNode_(Polymer.dom(this.$.body).children[i]);}
-this.maybeUpdateSelectedRow_();},maybeUpdateSelectedRow_:function(){if(this.selectedTableRowInfo_===undefined)
-return;if(this.selectionMode_===SelectionMode.NONE){this.removeSelectedState_();this.selectedTableRowInfo_=undefined;return;}
-function isVisible(rowInfo){if(!rowInfo.htmlNode)
-return false;return!!rowInfo.htmlNode.parentElement;}
+if(wasBodyOrHeaderDirty)this.applySizes_();if(this.footerDirty_){Polymer.dom(this.$.foot).textContent='';this.generateTableRowNodes_(this.$.foot,this.tableFooterRows_,this.tableFooterRowsInfo_,0,undefined,undefined);if(this.tableFooterRowsInfo_.length){Polymer.dom(this.$.body).classList.add('has-footer');}else{Polymer.dom(this.$.body).classList.remove('has-footer');}
+this.footerDirty_=false;}},appendNewElement_:function(parent,tagName){var element=parent.ownerDocument.createElement(tagName);Polymer.dom(parent).appendChild(element);return element;},getExpandedForTableRow:function(userRow){this.rebuildIfNeeded_();var rowInfo=this.tableRowsInfo_.get(userRow);if(rowInfo===undefined){throw new Error('Row has not been seen, must expand its parents');}
+return rowInfo.isExpanded;},getExpandedForUserRow_:function(userRow){if(userRow[this.subRowsPropertyName_]===undefined){return false;}
+if(userRow[this.subRowsPropertyName_].length===0){return false;}
+if(userRow.isExpanded){return true;}
+if((userRow.isExpanded!==undefined)&&(userRow.isExpanded===false)){return false;}
+var rowInfo=this.tableRowsInfo_.get(userRow);if(rowInfo&&rowInfo.isExpanded){return true;}
+if(this.defaultExpansionStateCallback_===undefined){return false;}
+var parentUserRow=undefined;if(rowInfo&&rowInfo.parentRowInfo){parentUserRow=rowInfo.parentRowInfo.userRow;}
+return this.defaultExpansionStateCallback_(userRow,parentUserRow);},setExpandedForTableRow:function(userRow,expanded){this.rebuildIfNeeded_();var rowInfo=this.tableRowsInfo_.get(userRow);if(rowInfo===undefined){throw new Error('Row has not been seen, must expand its parents');}
+return this.setExpandedForUserRow_(this.$.body,this.tableRowsInfo_,userRow,expanded);},setExpandedForUserRow_:function(tableSection,rowInfoMap,userRow,expanded){this.rebuildIfNeeded_();var rowInfo=rowInfoMap.get(userRow);if(rowInfo===undefined){throw new Error('Row has not been seen, must expand its parents');}
+const wasExpanded=rowInfo.isExpanded;rowInfo.isExpanded=!!expanded;if(rowInfo.htmlNode===undefined)return;if(rowInfo.htmlNode.parentElement!==tableSection){return;}
+var expandButton=Polymer.dom(rowInfo.htmlNode).querySelector('expand-button');if(rowInfo.isExpanded){Polymer.dom(expandButton).classList.add('button-expanded');var lastAddedRow=rowInfo.htmlNode;if(rowInfo.userRow[this.subRowsPropertyName_]){this.generateTableRowNodes_(tableSection,rowInfo.userRow[this.subRowsPropertyName_],rowInfoMap,rowInfo.indentation+1,lastAddedRow,rowInfo);}}else{Polymer.dom(expandButton).classList.remove('button-expanded');this.removeSubNodes_(tableSection,rowInfo,rowInfoMap);}
+if(wasExpanded!==rowInfo.isExpanded){let e=new tr.b.Event('row-expanded-changed');e.row=rowInfo.userRow;this.dispatchEvent(e);}
+this.maybeUpdateSelectedRow_();},get selectionMode(){return this.selectionMode_;},set selectionMode(selectionMode){if(!tr.b.dictionaryContainsValue(SelectionMode,selectionMode)){throw new Error('Invalid selection mode '+selectionMode);}
+this.rebuildIfNeeded_();this.selectionMode_=selectionMode;this.didSelectionStateChange_();},get rowHighlightStyle(){return this.rowHighlightStyle_;},set rowHighlightStyle(rowHighlightStyle){if(!tr.b.dictionaryContainsValue(HighlightStyle,rowHighlightStyle)){throw new Error('Invalid row highlight style '+rowHighlightStyle);}
+this.rebuildIfNeeded_();this.rowHighlightStyle_=rowHighlightStyle;this.didSelectionStateChange_();},get resolvedRowHighlightStyle(){if(this.rowHighlightStyle_!==HighlightStyle.DEFAULT){return this.rowHighlightStyle_;}
+switch(this.selectionMode_){case SelectionMode.NONE:return HighlightStyle.NONE;case SelectionMode.ROW:return HighlightStyle.DARK;case SelectionMode.CELL:return HighlightStyle.LIGHT;default:throw new Error('Invalid selection mode '+selectionMode);}},get cellHighlightStyle(){return this.cellHighlightStyle_;},set cellHighlightStyle(cellHighlightStyle){if(!tr.b.dictionaryContainsValue(HighlightStyle,cellHighlightStyle)){throw new Error('Invalid cell highlight style '+cellHighlightStyle);}
+this.rebuildIfNeeded_();this.cellHighlightStyle_=cellHighlightStyle;this.didSelectionStateChange_();},get resolvedCellHighlightStyle(){if(this.cellHighlightStyle_!==HighlightStyle.DEFAULT){return this.cellHighlightStyle_;}
+switch(this.selectionMode_){case SelectionMode.NONE:case SelectionMode.ROW:return HighlightStyle.NONE;case SelectionMode.CELL:return HighlightStyle.DARK;default:throw new Error('Invalid selection mode '+selectionMode);}},setHighlightStyle_:function(highlightAttribute,resolvedHighlightStyle){switch(resolvedHighlightStyle){case HighlightStyle.NONE:Polymer.dom(this.$.body).removeAttribute(highlightAttribute);break;case HighlightStyle.LIGHT:Polymer.dom(this.$.body).setAttribute(highlightAttribute,'light');break;case HighlightStyle.DARK:Polymer.dom(this.$.body).setAttribute(highlightAttribute,'dark');break;default:throw new Error('Invalid resolved highlight style '+
+resolvedHighlightStyle);}},didSelectionStateChange_:function(){this.setHighlightStyle_('row-highlight-style',this.resolvedRowHighlightStyle);this.setHighlightStyle_('cell-highlight-style',this.resolvedCellHighlightStyle);this.removeSelectedState_();switch(this.selectionMode_){case SelectionMode.ROW:Polymer.dom(this.$.body).setAttribute('selection-mode','row');Polymer.dom(this.$.body).setAttribute('tabindex',0);this.selectedColumnIndex_=undefined;break;case SelectionMode.CELL:Polymer.dom(this.$.body).setAttribute('selection-mode','cell');Polymer.dom(this.$.body).setAttribute('tabindex',0);if(this.selectedTableRowInfo_&&this.selectedColumnIndex_===undefined){var i=this.getFirstSelectableColumnIndex_();if(i===-1){this.selectedTableRowInfo_=undefined;}else{this.selectedColumnIndex_=i;}}
+break;case SelectionMode.NONE:Polymer.dom(this.$.body).removeAttribute('selection-mode');Polymer.dom(this.$.body).removeAttribute('tabindex');this.$.body.blur();this.selectedTableRowInfo_=undefined;this.selectedColumnIndex_=undefined;break;default:throw new Error('Invalid selection mode '+this.selectionMode_);}
+this.maybeUpdateSelectedRow_();},maybeUpdateSelectedRow_:function(){if(this.selectedTableRowInfo_===undefined)return;function isVisible(rowInfo){if(!rowInfo.htmlNode)return false;return!!rowInfo.htmlNode.parentElement;}
 if(isVisible(this.selectedTableRowInfo_)){this.updateSelectedState_();return;}
-this.removeSelectedState_();var curRowInfo=this.selectedTableRowInfo_;while(curRowInfo&&!isVisible(curRowInfo))
-curRowInfo=curRowInfo.parentRowInfo;this.selectedTableRowInfo_=curRowInfo;if(this.selectedTableRowInfo_)
-this.updateSelectedState_();},didTableRowInfoGetClicked_:function(rowInfo,columnIndex){switch(this.selectionMode_){case SelectionMode.NONE:return;case SelectionMode.CELL:if(!this.doesColumnIndexSupportSelection(columnIndex))
-return;if(this.selectedColumnIndex!==columnIndex)
-this.selectedColumnIndex=columnIndex;case SelectionMode.ROW:if(this.selectedTableRowInfo_!==rowInfo)
-this.selectedTableRow=rowInfo.userRow;}},dispatchStepIntoEvent_:function(rowInfo,columnIndex){var e=new tr.b.Event('step-into');e.tableRow=rowInfo.userRow;e.tableColumn=this.tableColumns_[columnIndex];e.columnIndex=columnIndex;this.dispatchEvent(e);},get selectedCell(){var row=this.selectedTableRow;var columnIndex=this.selectedColumnIndex;if(row===undefined||columnIndex===undefined||this.tableColumns_.length<=columnIndex)
-return undefined;var column=this.tableColumns_[columnIndex];return{row:row,column:column,value:column.value(row)};},get selectedTableColumnIndex(){var cols=Polymer.dom(this.$.cols).children;for(var i=0;i<cols.length;++i){if(cols[i].getAttribute('selected')){return i;}}
-return undefined;},set selectedTableColumnIndex(selectedIndex){var cols=Polymer.dom(this.$.cols).children;for(var i=0;i<cols.length;++i){if(i===selectedIndex)
-cols[i].setAttribute('selected',true);else
-cols[i].removeAttribute('selected');}},get selectedTableRow(){if(!this.selectedTableRowInfo_)
-return undefined;return this.selectedTableRowInfo_.userRow;},set selectedTableRow(userRow){this.rebuildIfNeeded_();if(this.selectionMode_===SelectionMode.NONE)
-throw new Error('Selection is off.');var rowInfo;if(userRow===undefined){rowInfo=undefined;}else{rowInfo=this.tableRowsInfo_.get(userRow);if(!rowInfo)
-throw new Error('Row has not been seen, must expand its parents.');}
-var e=this.prepareToChangeSelection_();this.selectedTableRowInfo_=rowInfo;if(this.selectedTableRowInfo_===undefined){this.selectedColumnIndex_=undefined;this.removeSelectedState_();}else{switch(this.selectionMode_){case SelectionMode.ROW:this.selectedColumnIndex_=undefined;break;case SelectionMode.CELL:if(this.selectedColumnIndex_===undefined){var i=this.getFirstSelectableColumnIndex_();if(i==-1)
-throw new Error('Cannot find a selectable column.');this.selectedColumnIndex_=i;}
-break;default:throw new Error('Invalid selection mode '+this.selectionMode_);}
-this.updateSelectedState_();}
-this.dispatchEvent(e);},updateTabIndexForTableRowNode_:function(row){if(this.selectionMode_===SelectionMode.ROW)
-row.tabIndex=0;else
-Polymer.dom(row).removeAttribute('tabIndex');var enableCellTab=this.selectionMode_===SelectionMode.CELL;for(var i=0;i<this.tableColumns_.length;i++){var cell=Polymer.dom(row).children[i];if(enableCellTab&&this.doesColumnIndexSupportSelection(i))
-cell.tabIndex=0;else
-Polymer.dom(cell).removeAttribute('tabIndex');}},prepareToChangeSelection_:function(){var e=new tr.b.Event('selection-changed');var previousSelectedRowInfo=this.selectedTableRowInfo_;if(previousSelectedRowInfo)
-e.previousSelectedTableRow=previousSelectedRowInfo.userRow;else
-e.previousSelectedTableRow=undefined;this.removeSelectedState_();return e;},removeSelectedState_:function(){this.setSelectedState_(false);},updateSelectedState_:function(){this.setSelectedState_(true);},setSelectedState_:function(select){if(this.selectedTableRowInfo_===undefined)
-return;var rowNode=this.selectedTableRowInfo_.htmlNode;if(select)
-Polymer.dom(rowNode).setAttribute('selected',true);else
-Polymer.dom(rowNode).removeAttribute('selected');var cellNode=Polymer.dom(rowNode).children[this.selectedColumnIndex_];if(!cellNode)
-return;if(select)
-Polymer.dom(cellNode).setAttribute('selected',true);else
-Polymer.dom(cellNode).removeAttribute('selected');},doesColumnIndexSupportSelection:function(columnIndex){var columnInfo=this.tableColumns_[columnIndex];var scs=columnInfo.supportsCellSelection;if(scs===false)
-return false;return true;},getFirstSelectableColumnIndex_:function(){for(var i=0;i<this.tableColumns_.length;i++){if(this.doesColumnIndexSupportSelection(i))
-return i;}
-return-1;},getSelectableNodeGivenTableRowNode_:function(htmlNode){switch(this.selectionMode_){case SelectionMode.ROW:return htmlNode;case SelectionMode.CELL:return Polymer.dom(htmlNode).children[this.selectedColumnIndex_];default:throw new Error('Invalid selection mode '+this.selectionMode_);}},get selectedColumnIndex(){if(this.selectionMode_!==SelectionMode.CELL)
-return undefined;return this.selectedColumnIndex_;},set selectedColumnIndex(selectedColumnIndex){this.rebuildIfNeeded_();if(this.selectionMode_===SelectionMode.NONE)
-throw new Error('Selection is off.');if(selectedColumnIndex<0||selectedColumnIndex>=this.tableColumns_.length)
-throw new Error('Invalid index');if(!this.doesColumnIndexSupportSelection(selectedColumnIndex))
-throw new Error('Selection is not supported on this column');var e=this.prepareToChangeSelection_();this.selectedColumnIndex_=selectedColumnIndex;if(this.selectedColumnIndex_===undefined)
-this.selectedTableRowInfo_=undefined;this.updateSelectedState_();this.dispatchEvent(e);},onKeyDown_:function(e){if(this.selectionMode_===SelectionMode.NONE)
-return;if(this.selectedTableRowInfo_===undefined)
-return;var code_to_command_names={13:'ENTER',32:'SPACE',37:'ARROW_LEFT',38:'ARROW_UP',39:'ARROW_RIGHT',40:'ARROW_DOWN'};var cmdName=code_to_command_names[e.keyCode];if(cmdName===undefined)
-return;e.stopPropagation();e.preventDefault();this.performKeyCommand_(cmdName);},performKeyCommand_:function(cmdName){this.rebuildIfNeeded_();var rowInfo=this.selectedTableRowInfo_;var htmlNode=rowInfo.htmlNode;if(cmdName==='ARROW_UP'){var prev=htmlNode.previousElementSibling;if(prev){tr.ui.b.scrollIntoViewIfNeeded(prev);this.selectedTableRow=prev.rowInfo.userRow;this.focusSelected_();return;}
-return;}
-if(cmdName==='ARROW_DOWN'){var next=htmlNode.nextElementSibling;if(next){tr.ui.b.scrollIntoViewIfNeeded(next);this.selectedTableRow=next.rowInfo.userRow;this.focusSelected_();return;}
-return;}
-if(cmdName==='ARROW_RIGHT'){switch(this.selectionMode_){case SelectionMode.ROW:if(rowInfo.userRow[this.subRowsPropertyName_]===undefined)
-return;if(rowInfo.userRow[this.subRowsPropertyName_].length===0)
-return;if(!rowInfo.isExpanded)
-this.setExpandedForTableRow(rowInfo.userRow,true);this.selectedTableRow=htmlNode.nextElementSibling.rowInfo.userRow;this.focusSelected_();return;case SelectionMode.CELL:var newIndex=this.selectedColumnIndex_+1;if(newIndex>=this.tableColumns_.length)
-return;if(!this.doesColumnIndexSupportSelection(newIndex))
-return;this.selectedColumnIndex=newIndex;this.focusSelected_();return;default:throw new Error('Invalid selection mode '+this.selectionMode_);}}
-if(cmdName==='ARROW_LEFT'){switch(this.selectionMode_){case SelectionMode.ROW:if(rowInfo.isExpanded){this.setExpandedForTableRow(rowInfo.userRow,false);this.focusSelected_();return;}
-var parentRowInfo=rowInfo.parentRowInfo;if(parentRowInfo){this.selectedTableRow=parentRowInfo.userRow;this.focusSelected_();return;}
-return;case SelectionMode.CELL:var newIndex=this.selectedColumnIndex_-1;if(newIndex<0)
-return;if(!this.doesColumnIndexSupportSelection(newIndex))
-return;this.selectedColumnIndex=newIndex;this.focusSelected_();return;default:throw new Error('Invalid selection mode '+this.selectionMode_);}}
-if(cmdName==='SPACE'){if(rowInfo.userRow[this.subRowsPropertyName_]===undefined)
-return;if(rowInfo.userRow[this.subRowsPropertyName_].length===0)
-return;this.setExpandedForTableRow(rowInfo.userRow,!rowInfo.isExpanded);this.focusSelected_();return;}
-if(cmdName==='ENTER'){this.dispatchStepIntoEvent_(rowInfo,this.selectedColumnIndex_);return;}
-throw new Error('Unrecognized command '+cmdName);},focusSelected_:function(){if(!this.selectedTableRowInfo_)
-return;var node=this.getSelectableNodeGivenTableRowNode_(this.selectedTableRowInfo_.htmlNode);node.focus();},dispatchSortingChangedEvent_:function(){var e=new tr.b.Event('sort-column-changed');e.sortColumnIndex=this.sortColumnIndex_;e.sortDescending=this.sortDescending_;this.dispatchEvent(e);}});})();'use strict';var ColumnAlignment=tr.ui.b.TableFormat.ColumnAlignment;Polymer({is:'tr-ui-b-table-header-cell',created:function(){this.tapCallback_=undefined;this.cellTitle_='';this.align_=undefined;this.selectable_=false;this.column_=undefined;},ready:function(){this.addEventListener('click',this.onTap_.bind(this));},set column(column){this.column_=column;this.align=column.align;this.cellTitle=column.title;},get column(){return this.column_;},set cellTitle(value){this.cellTitle_=value;var titleNode=tr.ui.b.asHTMLOrTextNode(this.cellTitle_,this.ownerDocument);this.$.title.innerText='';Polymer.dom(this.$.title).appendChild(titleNode);},get cellTitle(){return this.cellTitle_;},set align(align){switch(align){case undefined:case ColumnAlignment.LEFT:this.style.justifyContent='';break;case ColumnAlignment.RIGHT:this.style.justifyContent='flex-end';break;default:throw new Error('Invalid alignment of column (title=\''+
+this.removeSelectedState_();var curRowInfo=this.selectedTableRowInfo_;while(curRowInfo&&!isVisible(curRowInfo)){curRowInfo=curRowInfo.parentRowInfo;}
+this.selectedTableRowInfo_=curRowInfo;if(this.selectedTableRowInfo_){this.updateSelectedState_();}else{this.selectedColumnIndex_=undefined;}},didTableRowInfoGetClicked_:function(rowInfo,columnIndex){switch(this.selectionMode_){case SelectionMode.NONE:return;case SelectionMode.CELL:if(!this.doesColumnIndexSupportSelection(columnIndex)){return;}
+if(this.selectedColumnIndex!==columnIndex){this.selectedColumnIndex=columnIndex;}
+case SelectionMode.ROW:if(this.selectedTableRowInfo_!==rowInfo){this.selectedTableRow=rowInfo.userRow;}}},dispatchStepIntoEvent_:function(rowInfo,columnIndex){var e=new tr.b.Event('step-into');e.tableRow=rowInfo.userRow;e.tableColumn=this.tableColumns_[columnIndex];e.columnIndex=columnIndex;this.dispatchEvent(e);},get selectedCell(){var row=this.selectedTableRow;var columnIndex=this.selectedColumnIndex;if(row===undefined||columnIndex===undefined||this.tableColumns_.length<=columnIndex){return undefined;}
+var column=this.tableColumns_[columnIndex];return{row:row,column:column,value:column.value(row)};},get selectedTableColumnIndex(){var cols=Polymer.dom(this.$.cols).children;for(var i=0;i<cols.length;++i){if(cols[i].getAttribute('selected')){return i;}}
+return undefined;},set selectedTableColumnIndex(selectedIndex){var cols=Polymer.dom(this.$.cols).children;for(var i=0;i<cols.length;++i){if(i===selectedIndex){cols[i].setAttribute('selected',true);}else{cols[i].removeAttribute('selected');}}},get selectedTableRow(){if(!this.selectedTableRowInfo_)return undefined;return this.selectedTableRowInfo_.userRow;},set selectedTableRow(userRow){this.rebuildIfNeeded_();if(this.selectionMode_===SelectionMode.NONE){throw new Error('Selection is off.');}
+var rowInfo;if(userRow===undefined){rowInfo=undefined;}else{rowInfo=this.tableRowsInfo_.get(userRow);if(!rowInfo){throw new Error('Row has not been seen, must expand its parents.');}}
+var e=this.prepareToChangeSelection_();if(!rowInfo){this.selectedColumnIndex_=undefined;}else{switch(this.selectionMode_){case SelectionMode.ROW:this.selectedColumnIndex_=undefined;break;case SelectionMode.CELL:if(this.selectedColumnIndex_===undefined){var i=this.getFirstSelectableColumnIndex_();if(i===-1){throw new Error('Cannot find a selectable column.');}
+this.selectedColumnIndex_=i;}
+break;default:throw new Error('Invalid selection mode '+this.selectionMode_);}}
+this.selectedTableRowInfo_=rowInfo;this.updateSelectedState_();this.dispatchEvent(e);},prepareToChangeSelection_:function(){var e=new tr.b.Event('selection-changed');var previousSelectedRowInfo=this.selectedTableRowInfo_;if(previousSelectedRowInfo){e.previousSelectedTableRow=previousSelectedRowInfo.userRow;}else{e.previousSelectedTableRow=undefined;}
+this.removeSelectedState_();return e;},removeSelectedState_:function(){this.setSelectedState_(false);},updateSelectedState_:function(){this.setSelectedState_(true);},setSelectedState_:function(select){if(this.selectedTableRowInfo_===undefined)return;var rowNode=this.selectedTableRowInfo_.htmlNode;if(select){Polymer.dom(rowNode).setAttribute('selected',true);}else{Polymer.dom(rowNode).removeAttribute('selected');}
+var cellNode=Polymer.dom(rowNode).children[this.selectedColumnIndex_];if(!cellNode)return;if(select){Polymer.dom(cellNode).setAttribute('selected',true);}else{Polymer.dom(cellNode).removeAttribute('selected');}},doesColumnIndexSupportSelection:function(columnIndex){var columnInfo=this.tableColumns_[columnIndex];var scs=columnInfo.supportsCellSelection;if(scs===false)return false;return true;},getFirstSelectableColumnIndex_:function(){for(var i=0;i<this.tableColumns_.length;i++){if(this.doesColumnIndexSupportSelection(i)){return i;}}
+return-1;},getSelectableNodeGivenTableRowNode_:function(htmlNode){switch(this.selectionMode_){case SelectionMode.ROW:return htmlNode;case SelectionMode.CELL:return Polymer.dom(htmlNode).children[this.selectedColumnIndex_];default:throw new Error('Invalid selection mode '+this.selectionMode_);}},get selectedColumnIndex(){if(this.selectionMode_!==SelectionMode.CELL){return undefined;}
+return this.selectedColumnIndex_;},set selectedColumnIndex(selectedColumnIndex){this.rebuildIfNeeded_();if(this.selectionMode_===SelectionMode.NONE){throw new Error('Selection is off.');}
+if(selectedColumnIndex<0||selectedColumnIndex>=this.tableColumns_.length){throw new Error('Invalid index');}
+if(!this.doesColumnIndexSupportSelection(selectedColumnIndex)){throw new Error('Selection is not supported on this column');}
+var e=this.prepareToChangeSelection_();if(this.selectedColumnIndex_===undefined){this.selectedTableRowInfo_=undefined;}else if(!this.selectedTableRowInfo_){if(this.tableRows_.length===0){throw new Error('No available row to be selected');}
+this.selectedTableRowInfo_=this.tableRowsInfo_.get(this.tableRows_[0]);}
+this.selectedColumnIndex_=selectedColumnIndex;this.updateSelectedState_();this.dispatchEvent(e);},onKeyDown_:function(e){if(this.selectionMode_===SelectionMode.NONE)return;var CODE_TO_COMMAND_NAMES={13:'ENTER',32:'SPACE',37:'ARROW_LEFT',38:'ARROW_UP',39:'ARROW_RIGHT',40:'ARROW_DOWN'};var cmdName=CODE_TO_COMMAND_NAMES[e.keyCode];if(cmdName===undefined)return;e.stopPropagation();e.preventDefault();this.performKeyCommand_(cmdName);},onFocus_:function(e){if(this.selectionMode_===SelectionMode.NONE||this.selectedTableRow||this.tableRows_.length===0){return;}
+if(this.selectionMode_===SelectionMode.CELL&&this.getFirstSelectableColumnIndex_()===-1){return;}
+this.selectedTableRow=this.tableRows_[0];},focus:function(){this.$.body.focus();this.onFocus_();},blur:function(){this.$.body.blur();},get isFocused(){return this.root.activeElement===this.$.body;},performKeyCommand_:function(cmdName){this.rebuildIfNeeded_();switch(cmdName){case'ARROW_UP':this.selectPreviousOrFirstRowIfPossible_();return;case'ARROW_DOWN':this.selectNextOrFirstRowIfPossible_();return;case'ARROW_RIGHT':switch(this.selectionMode_){case SelectionMode.NONE:return;case SelectionMode.ROW:this.expandRowAndSelectChildRowIfPossible_();return;case SelectionMode.CELL:this.selectNextSelectableCellToTheRightIfPossible_();return;default:throw new Error('Invalid selection mode '+this.selectionMode_);}
+case'ARROW_LEFT':switch(this.selectionMode_){case SelectionMode.NONE:return;case SelectionMode.ROW:this.collapseRowOrSelectParentRowIfPossible_();return;case SelectionMode.CELL:this.selectNextSelectableCellToTheLeftIfPossible_();return;default:throw new Error('Invalid selection mode '+this.selectionMode_);}
+case'SPACE':this.toggleRowExpansionStateIfPossible_();return;case'ENTER':this.stepIntoSelectionIfPossible_();return;default:throw new Error('Unrecognized command '+cmdName);}},selectPreviousOrFirstRowIfPossible_:function(){var prev=this.selectedTableRowInfo_?this.selectedTableRowInfo_.htmlNode.previousElementSibling:this.$.body.firstChild;if(!prev)return;if(this.selectionMode_===SelectionMode.CELL&&this.getFirstSelectableColumnIndex_()===-1){return;}
+tr.ui.b.scrollIntoViewIfNeeded(prev);this.selectedTableRow=prev.rowInfo.userRow;},selectNextOrFirstRowIfPossible_:function(){this.getFirstSelectableColumnIndex_;var next=this.selectedTableRowInfo_?this.selectedTableRowInfo_.htmlNode.nextElementSibling:this.$.body.firstChild;if(!next)return;if(this.selectionMode_===SelectionMode.CELL&&this.getFirstSelectableColumnIndex_()===-1){return;}
+tr.ui.b.scrollIntoViewIfNeeded(next);this.selectedTableRow=next.rowInfo.userRow;},expandRowAndSelectChildRowIfPossible_:function(){var selectedRowInfo=this.selectedTableRowInfo_;if(!selectedRowInfo||selectedRowInfo.userRow[this.subRowsPropertyName_]===undefined||selectedRowInfo.userRow[this.subRowsPropertyName_].length===0){return;}
+if(!selectedRowInfo.isExpanded){this.setExpandedForTableRow(selectedRowInfo.userRow,true);}
+this.selectedTableRow=selectedRowInfo.htmlNode.nextElementSibling.rowInfo.userRow;},collapseRowOrSelectParentRowIfPossible_:function(){var selectedRowInfo=this.selectedTableRowInfo_;if(!selectedRowInfo)return;if(selectedRowInfo.isExpanded){this.setExpandedForTableRow(selectedRowInfo.userRow,false);}else{var parentRowInfo=selectedRowInfo.parentRowInfo;if(parentRowInfo){this.selectedTableRow=parentRowInfo.userRow;}}},selectNextSelectableCellToTheRightIfPossible_:function(){if(!this.selectedTableRowInfo_||this.selectedColumnIndex_===undefined){return;}
+for(var i=this.selectedColumnIndex_+1;i<this.tableColumns_.length;i++){if(this.doesColumnIndexSupportSelection(i)){this.selectedColumnIndex=i;return;}}},selectNextSelectableCellToTheLeftIfPossible_:function(){if(!this.selectedTableRowInfo_||this.selectedColumnIndex_===undefined){return;}
+for(var i=this.selectedColumnIndex_-1;i>=0;i--){if(this.doesColumnIndexSupportSelection(i)){this.selectedColumnIndex=i;return;}}},toggleRowExpansionStateIfPossible_:function(){var selectedRowInfo=this.selectedTableRowInfo_;if(!selectedRowInfo||selectedRowInfo.userRow[this.subRowsPropertyName_]===undefined||selectedRowInfo.userRow[this.subRowsPropertyName_].length===0){return;}
+this.setExpandedForTableRow(selectedRowInfo.userRow,!selectedRowInfo.isExpanded);},stepIntoSelectionIfPossible_:function(){if(!this.selectedTableRowInfo_)return;this.dispatchStepIntoEvent_(this.selectedTableRowInfo_,this.selectedColumnIndex_);},dispatchSortingChangedEvent_:function(){var e=new tr.b.Event('sort-column-changed');e.sortColumnIndex=this.sortColumnIndex_;e.sortDescending=this.sortDescending_;this.dispatchEvent(e);}});})();'use strict';var ColumnAlignment=tr.ui.b.TableFormat.ColumnAlignment;Polymer({is:'tr-ui-b-table-header-cell',created:function(){this.tapCallback_=undefined;this.cellTitle_='';this.align_=undefined;this.selectable_=false;this.column_=undefined;},ready:function(){this.addEventListener('click',this.onTap_.bind(this));},set column(column){this.column_=column;this.align=column.align;this.cellTitle=column.title;},get column(){return this.column_;},set cellTitle(value){this.cellTitle_=value;var titleNode=tr.ui.b.asHTMLOrTextNode(this.cellTitle_,this.ownerDocument);this.$.title.innerText='';Polymer.dom(this.$.title).appendChild(titleNode);},get cellTitle(){return this.cellTitle_;},set align(align){switch(align){case undefined:case ColumnAlignment.LEFT:this.style.justifyContent='';break;case ColumnAlignment.RIGHT:this.style.justifyContent='flex-end';break;default:throw new Error('Invalid alignment of column (title=\''+
 this.cellTitle_+'\'): '+align);}
-this.align_=align;},get align(){return this.align_;},clearSideContent:function(){Polymer.dom(this.$.side).textContent='';},set sideContent(content){Polymer.dom(this.$.side).textContent=content;this.$.side.style.display=content?'inline':'none';},get sideContent(){return Polymer.dom(this.$.side).textContent;},set sideContentDisabled(sideContentDisabled){this.$.side.classList.toggle('disabled',sideContentDisabled);},get sideContentDisabled(){return this.$.side.classList.contains('disabled');},set tapCallback(callback){this.style.cursor='pointer';this.tapCallback_=callback;},get tapCallback(){return this.tapCallback_;},onTap_:function(){if(this.tapCallback_)
-this.tapCallback_();}});'use strict';tr.exportTo('tr.b',function(){function _iterateElementDeeplyImpl(element,cb,thisArg,includeElement){if(includeElement&&cb.call(thisArg,element))
-return true;if(element.root&&element.root!==element&&_iterateElementDeeplyImpl(element.root,cb,thisArg,false)){return true;}
-var children=Polymer.dom(element).children;for(var i=0;i<children.length;i++)
-if(_iterateElementDeeplyImpl(children[i],cb,thisArg,true))
-return true;return false;}
-function iterateElementDeeply(element,cb,thisArg){_iterateElementDeeplyImpl(element,cb,thisArg,false);}
-function findDeepElementMatchingPredicate(element,predicate){var foundElement=undefined;function matches(element){var match=predicate(element);if(!match)
-return false;foundElement=element;return true;}
-iterateElementDeeply(element,matches);return foundElement;}
-function findDeepElementsMatchingPredicate(element,predicate){var foundElements=[];function matches(element){var match=predicate(element);if(match){foundElements.push(element);}
-return false;}
-iterateElementDeeply(element,matches);return foundElements;}
-function findDeepElementMatching(element,selector){return findDeepElementMatchingPredicate(element,function(element){return element.matches(selector);});}
-function findDeepElementsMatching(element,selector){return findDeepElementsMatchingPredicate(element,function(element){return element.matches(selector);});}
-function findDeepElementWithTextContent(element,re){return findDeepElementMatchingPredicate(element,function(element){if(element.children.length!==0)
-return false;return re.test(Polymer.dom(element).textContent);});}
-return{iterateElementDeeply:iterateElementDeeply,findDeepElementMatching:findDeepElementMatching,findDeepElementsMatching:findDeepElementsMatching,findDeepElementMatchingPredicate:findDeepElementMatchingPredicate,findDeepElementsMatchingPredicate:findDeepElementsMatchingPredicate,findDeepElementWithTextContent:findDeepElementWithTextContent};});'use strict';tr.exportTo('tr.b',function(){class RunningStatistics{constructor(){this.mean_=0;this.count_=0;this.max_=-Infinity;this.min_=Infinity;this.sum_=0;this.variance_=0;this.meanlogs_=0;}
+this.align_=align;},get align(){return this.align_;},clearSideContent:function(){Polymer.dom(this.$.side).textContent='';},set sideContent(content){Polymer.dom(this.$.side).textContent=content;this.$.side.style.display=content?'inline':'none';},get sideContent(){return Polymer.dom(this.$.side).textContent;},set sideContentDisabled(sideContentDisabled){this.$.side.classList.toggle('disabled',sideContentDisabled);},get sideContentDisabled(){return this.$.side.classList.contains('disabled');},set tapCallback(callback){this.style.cursor='pointer';this.tapCallback_=callback;},get tapCallback(){return this.tapCallback_;},onTap_:function(){if(this.tapCallback_){this.tapCallback_();}}});'use strict';tr.exportTo('tr.b.math',function(){class RunningStatistics{constructor(){this.mean_=0;this.count_=0;this.max_=-Infinity;this.min_=Infinity;this.sum_=0;this.variance_=0;this.meanlogs_=0;}
 get count(){return this.count_;}
-get geometricMean(){if(this.meanlogs_===undefined)
-return 0;return Math.exp(this.meanlogs_);}
-get mean(){if(this.count_==0)
-return undefined;return this.mean_;}
+get geometricMean(){if(this.meanlogs_===undefined)return 0;return Math.exp(this.meanlogs_);}
+get mean(){if(this.count_===0)return undefined;return this.mean_;}
 get max(){return this.max_;}
 get min(){return this.min_;}
 get sum(){return this.sum_;}
-get variance(){if(this.count_==0)
-return undefined;if(this.count_==1)
-return 0;return this.variance_/(this.count_-1);}
-get stddev(){if(this.count_==0)
-return undefined;return Math.sqrt(this.variance);}
-add(x){this.count_++;this.max_=Math.max(this.max_,x);this.min_=Math.min(this.min_,x);this.sum_+=x;if(x<=0)
-this.meanlogs_=undefined;else if(this.meanlogs_!==undefined)
-this.meanlogs_+=(Math.log(Math.abs(x))-this.meanlogs_)/this.count;if(this.count_===1){this.mean_=x;this.variance_=0;}else{var oldMean=this.mean_;var oldVariance=this.variance_;if(oldMean===Infinity||oldMean===-Infinity){this.mean_=this.sum_/this.count_;}else{this.mean_=oldMean+(x-oldMean)/this.count_;}
+get variance(){if(this.count_===0)return undefined;if(this.count_===1)return 0;return this.variance_/(this.count_-1);}
+get stddev(){if(this.count_===0)return undefined;return Math.sqrt(this.variance);}
+add(x){this.count_++;this.max_=Math.max(this.max_,x);this.min_=Math.min(this.min_,x);this.sum_+=x;if(x<=0){this.meanlogs_=undefined;}else if(this.meanlogs_!==undefined){this.meanlogs_+=(Math.log(Math.abs(x))-this.meanlogs_)/this.count;}
+if(this.count_===1){this.mean_=x;this.variance_=0;}else{var oldMean=this.mean_;var oldVariance=this.variance_;if(oldMean===Infinity||oldMean===-Infinity){this.mean_=this.sum_/this.count_;}else{this.mean_=oldMean+(x-oldMean)/this.count_;}
 this.variance_=oldVariance+(x-oldMean)*(x-this.mean_);}}
 merge(other){var result=new RunningStatistics();result.count_=this.count_+other.count_;result.sum_=this.sum_+other.sum_;result.min_=Math.min(this.min_,other.min_);result.max_=Math.max(this.max_,other.max_);if(result.count===0){result.mean_=0;result.variance_=0;result.meanlogs_=0;}else{result.mean_=result.sum/result.count;var deltaMean=(this.mean||0)-(other.mean||0);result.variance_=this.variance_+other.variance_+
 (this.count*other.count*deltaMean*deltaMean/result.count);if(this.meanlogs_===undefined||other.meanlogs_===undefined){result.meanlogs_=undefined;}else{result.meanlogs_=(this.count*this.meanlogs_+
 other.count*other.meanlogs_)/result.count;}}
 return result;}
-asDict(){return{mean:this.mean,meanlogs:this.meanlogs_,count:this.count,max:this.max,min:this.min,sum:this.sum,variance:this.variance_};}
-static fromDict(d){var result=new RunningStatistics();result.mean_=d.mean;result.count_=d.count;result.max_=d.max;result.min_=d.min;result.sum_=d.sum;result.variance_=d.variance;result.meanlogs_=d.meanlogs;return result;}}
-return{RunningStatistics:RunningStatistics};});'use strict';tr.exportTo('tr.v',function(){var MAX_DIAGNOSTIC_MAPS=16;var DEFAULT_ALPHA=0.05;var Significance={DONT_CARE:-1,INSIGNIFICANT:0,SIGNIFICANT:1};var DEFAULT_BOUNDARIES_FOR_UNIT=new Map();class HistogramBin{constructor(range){this.range=range;this.count=0;this.diagnosticMaps=[];}
+asDict(){if(!this.count){return[];}
+return[this.count_,this.max_,this.meanlogs_,this.mean_,this.min_,this.sum_,this.variance_,];}
+static fromDict(dict){var result=new RunningStatistics();if(dict.length!==7){return result;}
+[result.count_,result.max_,result.meanlogs_,result.mean_,result.min_,result.sum_,result.variance_,]=dict;return result;}}
+return{RunningStatistics,};});'use strict';tr.exportTo('tr.v.d',function(){class Diagnostic{constructor(){this.guid_=undefined;}
+clone(){return new this.constructor();}
+canAddDiagnostic(otherDiagnostic){return false;}
+addDiagnostic(otherDiagnostic){throw new Error('Abstract virtual method: subclasses must override '+'this method if they override canAddDiagnostic');}
+get guid(){if(this.guid_===undefined){this.guid_=tr.b.GUID.allocateUUID4();}
+return this.guid_;}
+set guid(guid){if(this.guid_!==undefined){throw new Error('Cannot reset guid');}
+this.guid_=guid;}
+asDictOrReference(){if(this.guid_!==undefined){return this.guid_;}
+return this.asDict();}
+asDict(){let result={type:this.constructor.name};if(this.guid_!==undefined){result.guid=this.guid_;}
+this.asDictInto_(result);return result;}
+asDictInto_(d){throw new Error('Abstract virtual method: subclasses must override '+'this method if they override canAddDiagnostic');}
+static fromDict(d){let typeInfo=Diagnostic.findTypeInfoWithName(d.type);if(!typeInfo){throw new Error('Unrecognized diagnostic type: '+d.type);}
+return typeInfo.constructor.fromDict(d);}}
+let options=new tr.b.ExtensionRegistryOptions(tr.b.BASIC_REGISTRY_MODE);options.defaultMetadata={};options.mandatoryBaseClass=Diagnostic;tr.b.decorateExtensionRegistry(Diagnostic,options);Diagnostic.addEventListener('will-register',function(e){let constructor=e.typeInfo.constructor;if(!(constructor.fromDict instanceof Function)||(constructor.fromDict===Diagnostic.fromDict)||(constructor.fromDict.length!==1)){throw new Error('Diagnostics must define fromDict(d)');}});return{Diagnostic,};});'use strict';tr.exportTo('tr.v.d',function(){class Breakdown extends tr.v.d.Diagnostic{constructor(){super();this.values_=new Map();this.colorScheme=undefined;}
+clone(){let clone=new Breakdown();clone.colorScheme=this.colorScheme;clone.addDiagnostic(this);return clone;}
+canAddDiagnostic(otherDiagnostic){return((otherDiagnostic instanceof Breakdown)&&(otherDiagnostic.colorScheme===this.colorScheme));}
+addDiagnostic(otherDiagnostic){for(let[name,value]of otherDiagnostic){this.set(name,this.get(name)+value);}
+return this;}
+set(name,value){if(typeof name!=='string'||typeof value!=='number'){throw new Error('Breakdown maps from strings to numbers');}
+this.values_.set(name,value);}
+get(name){return this.values_.get(name)||0;}*[Symbol.iterator](){for(let pair of this.values_){yield pair;}}
+asDictInto_(d){d.values={};for(let[name,value]of this){d.values[name]=tr.b.numberToJson(value);}
+if(this.colorScheme){d.colorScheme=this.colorScheme;}}
+static fromDict(d){let breakdown=new Breakdown();for(let[name,value]of Object.entries(d.values)){breakdown.set(name,tr.b.numberFromJson(value));}
+if(d.colorScheme){breakdown.colorScheme=d.colorScheme;}
+return breakdown;}}
+tr.v.d.Diagnostic.register(Breakdown,{elementName:'tr-v-ui-breakdown-span'});return{Breakdown,};});'use strict';tr.exportTo('tr.v.d',function(){class BuildbotInfo extends tr.v.d.Diagnostic{constructor(info){super();this.displayMasterName_=info.displayMasterName||'';this.displayBotName_=info.displayBotName||'';this.buildbotMasterName_=info.buildbotMasterName||'';this.buildbotName_=info.buildbotName||'';this.buildNumber_=info.buildNumber||0;this.logUri_=info.logUri||'';}
+addToHistogram(hist){hist.diagnostics.set(BuildbotInfo.NAME,this);}
+clone(){let clone=new tr.v.d.MergedBuildbotInfo();clone.addDiagnostic(this);return clone;}
+asDictInto_(d){d.displayMasterName=this.displayMasterName;d.displayBotName=this.displayBotName;d.buildbotMasterName=this.buildbotMasterName;d.buildbotName=this.buildbotName;d.buildNumber=this.buildNumber;d.logUri=this.logUri;}
+get displayMasterName(){return this.displayMasterName_;}
+get displayBotName(){return this.displayBotName_;}
+get buildbotMasterName(){return this.buildbotMasterName_;}
+get buildbotName(){return this.buildbotName_;}
+get buildNumber(){return this.buildNumber_;}
+get logUri(){return this.logUri_;}
+static fromDict(d){return new BuildbotInfo(d);}
+static getFromHistogram(hist){return hist.diagnostics.get(BuildbotInfo.NAME);}
+static getField(hist,fieldName,defaultValue){let buildbot=BuildbotInfo.getFromHistogram(hist);if(!(buildbot instanceof tr.v.d.BuildbotInfo)||!buildbot[fieldName]){return defaultValue;}
+return buildbot[fieldName];}}
+BuildbotInfo.NAME='buildbot';tr.v.d.Diagnostic.register(BuildbotInfo,{elementName:'tr-v-ui-buildbot-info-span'});return{BuildbotInfo,};});'use strict';tr.exportTo('tr.v.d',function(){class Generic extends tr.v.d.Diagnostic{constructor(value){super();this.value=value;}
+clone(){let clone=new tr.v.d.CollectedGeneric();clone.addDiagnostic(this);return clone;}
+asDictInto_(d){d.value=this.value;}
+static fromDict(d){return new Generic(d.value);}}
+tr.v.d.Diagnostic.register(Generic,{elementName:'tr-v-ui-generic-diagnostic-span'});return{Generic,};});'use strict';tr.exportTo('tr.v.d',function(){class CollectedGeneric extends tr.v.d.Generic{constructor(){super([]);}
+canAddDiagnostic(otherDiagnostic){return otherDiagnostic instanceof tr.v.d.Generic;}
+addDiagnostic(otherDiagnostic){if(otherDiagnostic instanceof CollectedGeneric){this.value.push(...otherDiagnostic.value);}else{this.value.push(otherDiagnostic.value);}}}
+tr.v.d.Diagnostic.register(CollectedGeneric,{elementName:'tr-v-ui-generic-diagnostic-span'});return{CollectedGeneric,};});'use strict';tr.exportTo('tr.v.d',function(){class CollectedRelatedEventSet extends tr.v.d.Diagnostic{constructor(){super();this.eventSetsByCanonicalUrl_=new Map();}
+asDictInto_(d){d.events={};for(let[canonicalUrl,eventSet]of this){d.events[canonicalUrl]=[];for(let event of eventSet){d.events[canonicalUrl].push({stableId:event.stableId,title:event.title,start:event.start,duration:event.duration});}}}
+static fromDict(d){let result=new CollectedRelatedEventSet();for(let[canonicalUrl,events]of Object.entries(d.events)){result.eventSetsByCanonicalUrl_.set(canonicalUrl,events.map(e=>new tr.v.d.EventRef(e)));}
+return result;}
+get size(){return this.eventSetsByCanonicalUrl_.size;}
+get(canonicalUrl){return this.eventSetsByCanonicalUrl_.get(canonicalUrl);}*[Symbol.iterator](){for(let[canonicalUrl,eventSet]of this.eventSetsByCanonicalUrl_){yield[canonicalUrl,eventSet];}}
+canAddDiagnostic(otherDiagnostic){return otherDiagnostic instanceof tr.v.d.RelatedEventSet||otherDiagnostic instanceof tr.v.d.CollectedRelatedEventSet;}
+addEventSetForCanonicalUrl(canonicalUrl,events){let myEventSet=this.eventSetsByCanonicalUrl_.get(canonicalUrl);if(myEventSet===undefined){myEventSet=new Set();this.eventSetsByCanonicalUrl_.set(canonicalUrl,myEventSet);}
+for(let event of events){myEventSet.add(event);}}
+addDiagnostic(otherDiagnostic){if(otherDiagnostic instanceof tr.v.d.CollectedRelatedEventSet){for(let[canonicalUrl,otherEventSet]of otherDiagnostic){this.addEventSetForCanonicalUrl(canonicalUrl,otherEventSet);}
+return;}
+if(!otherDiagnostic.canonicalUrl)return;this.addEventSetForCanonicalUrl(otherDiagnostic.canonicalUrl,otherDiagnostic);}}
+tr.v.d.Diagnostic.register(CollectedRelatedEventSet,{elementName:'tr-v-ui-collected-related-event-set-span'});return{CollectedRelatedEventSet,};});'use strict';tr.exportTo('tr.v.d',function(){class DeviceInfo extends tr.v.d.Diagnostic{constructor(opt_info){super();let info=opt_info||{};this.chromeVersion_=info.chromeVersion||'';this.osName_=info.osName||'';this.osVersion_=info.osVersion||'';this.gpuInfo_=info.gpuInfo||undefined;this.arch_=info.arch||undefined;this.ram_=info.ram||0;}
+addToHistogram(hist){hist.diagnostics.set(DeviceInfo.NAME,this);}
+static getFromHistogram(hist){return hist.diagnostics.get(DeviceInfo.NAME);}
+clone(){let clone=new tr.v.d.MergedDeviceInfo();clone.addDiagnostic(this);return clone;}
+asDictInto_(d){d.chromeVersion=this.chromeVersion;d.osName=this.osName;d.osVersion=this.osVersion;d.gpuInfo=this.gpuInfo;d.arch=this.arch;d.ram=this.ram;}
+static fromDict(d){return new DeviceInfo(d);}
+get chromeVersion(){return this.chromeVersion_;}
+get osName(){return this.osName_;}
+get osVersion(){return this.osVersion_;}
+get gpuInfo(){return this.gpuInfo_;}
+get arch(){return this.arch_;}
+get ram(){return this.ram_;}}
+DeviceInfo.NAME='device';tr.v.d.Diagnostic.register(DeviceInfo,{elementName:'tr-v-ui-device-info-span'});return{DeviceInfo,};});'use strict';tr.exportTo('tr.v.d',function(){class DiagnosticRef{constructor(guid){this.guid=guid;}
+asDict(){return this.guid;}}
+return{DiagnosticRef,};});'use strict';tr.exportTo('tr.v.d',function(){class GroupingPath extends tr.v.d.Diagnostic{constructor(groupingPath){super();this.groupingPath_=groupingPath;}
+clone(){return new GroupingPath(Array.from(this.groupingPath_));}
+addToHistogram(hist){hist.diagnostics.set(GroupingPath.NAME,this);}
+static getFromHistogram(hist){return hist.diagnostics.get(GroupingPath.NAME);}
+equals(other){return 0===tr.b.compareArrays(this.groupingPath_,other.groupingPath_,(x,y)=>x.localeCompare(y));}
+asDictInto_(d){d.groupingPath=this.groupingPath_;}
+static fromDict(d){return new GroupingPath(d.groupingPath);}}
+GroupingPath.NAME='grouping path';tr.v.d.Diagnostic.register(GroupingPath);return{GroupingPath,};});'use strict';tr.exportTo('tr.v.d',function(){class MergedBuildbotInfo extends tr.v.d.Diagnostic{constructor(info){super();this.displayMasterNames_=new Set();this.displayBotNames_=new Set();this.buildbotMasterNames_=new Set();this.buildbotNames_=new Set();this.buildNumbers_=new Set();this.logUris_=new Set();}
+clone(){let clone=new MergedBuildbotInfo();clone.addDiagnostic(this);return clone;}
+addToHistogram(hist){hist.diagnostics.set(tr.v.d.BuildbotInfo.NAME,this);}
+equals(other){if(!(other instanceof MergedBuildbotInfo))return false;if(!tr.b.setsEqual(this.displayMasterNames,other.displayMasterNames)){return false;}
+if(!tr.b.setsEqual(this.displayBotNames,other.displayBotNames)){return false;}
+if(!tr.b.setsEqual(this.buildbotMasterNames,other.buildbotMasterNames)){return false;}
+if(!tr.b.setsEqual(this.buildbotNames,other.buildbotNames)){return false;}
+if(!tr.b.setsEqual(this.buildNumbers,other.buildNumbers))return false;if(!tr.b.setsEqual(this.logUris,other.logUris))return false;return true;}
+canAddDiagnostic(otherDiagnostic){return otherDiagnostic instanceof MergedBuildbotInfo||otherDiagnostic instanceof tr.v.d.BuildbotInfo;}
+addDiagnostic(otherDiagnostic){if(otherDiagnostic instanceof MergedBuildbotInfo){for(let name of otherDiagnostic.displayMasterNames){this.displayMasterNames.add(name);}
+for(let name of otherDiagnostic.displayBotNames){this.displayBotNames.add(name);}
+for(let name of otherDiagnostic.buildbotMasterNames){this.buildbotMasterNames.add(name);}
+for(let name of otherDiagnostic.buildbotNames){this.buildbotNames.add(name);}
+for(let name of otherDiagnostic.buildNumbers){this.buildNumbers.add(name);}
+for(let name of otherDiagnostic.logUris){this.logUris.add(name);}
+return this;}
+if(otherDiagnostic.displayMasterName){this.displayMasterNames.add(otherDiagnostic.displayMasterName);}
+if(otherDiagnostic.displayBotName){this.displayBotNames.add(otherDiagnostic.displayBotName);}
+if(otherDiagnostic.buildbotMasterName){this.buildbotMasterNames.add(otherDiagnostic.buildbotMasterName);}
+if(otherDiagnostic.buildbotName){this.buildbotNames.add(otherDiagnostic.buildbotName);}
+if(otherDiagnostic.buildNumber){this.buildNumbers.add(otherDiagnostic.buildNumber);}
+if(otherDiagnostic.logUri){this.logUris.add(otherDiagnostic.logUri);}
+return this;}
+asDictInto_(d){d.displayMasterNames=Array.from(this.displayMasterNames);d.displayBotNames=Array.from(this.displayBotNames);d.buildbotMasterNames=Array.from(this.buildbotMasterNames);d.buildbotNames=Array.from(this.buildbotNames);d.buildNumbers=Array.from(this.buildNumbers);d.logUris=Array.from(this.logUris);}
+get displayMasterNames(){return this.displayMasterNames_;}
+get displayBotNames(){return this.displayBotNames_;}
+get buildbotMasterNames(){return this.buildbotMasterNames_;}
+get buildbotNames(){return this.buildbotNames_;}
+get buildNumbers(){return this.buildNumbers_;}
+get logUris(){return this.logUris_;}
+static fromDict(d){let info=new MergedBuildbotInfo();for(let name of d.displayMasterNames){info.displayMasterNames.add(name);}
+for(let name of d.displayBotNames){info.displayBotNames.add(name);}
+for(let name of d.buildbotMasterNames){info.buildbotMasterNames.add(name);}
+for(let name of d.buildbotNames){info.buildbotNames.add(name);}
+for(let name of d.buildNumbers){info.buildNumbers.add(name);}
+for(let name of d.logUris){info.logUris.add(name);}
+return info;}}
+tr.v.d.Diagnostic.register(MergedBuildbotInfo,{elementName:'tr-v-ui-merged-buildbot-info-span'});return{MergedBuildbotInfo,};});'use strict';tr.exportTo('tr.v.d',function(){class MergedDeviceInfo extends tr.v.d.Diagnostic{constructor(){super();this.chromeVersions_=new Set();this.osNames_=new Set();this.osVersions_=new Set();}
+clone(){let clone=new tr.v.d.MergedDeviceInfo();clone.addDiagnostic(this);return clone;}
+addToHistogram(hist){hist.diagnostics.set(tr.v.d.DeviceInfo.NAME,this);}
+canAddDiagnostic(otherDiagnostic){return otherDiagnostic instanceof MergedDeviceInfo||otherDiagnostic instanceof tr.v.d.DeviceInfo;}
+addDiagnostic(otherDiagnostic){if(otherDiagnostic instanceof MergedDeviceInfo){for(let name of otherDiagnostic.osNames){this.osNames.add(name);}
+for(let name of otherDiagnostic.osVersions){this.osVersions.add(name);}
+for(let name of otherDiagnostic.chromeVersions){this.chromeVersions.add(name);}
+return this;}
+if(otherDiagnostic.osName){this.osNames.add(otherDiagnostic.osName);}
+if(otherDiagnostic.osVersion){this.osVersions.add(otherDiagnostic.osVersion);}
+if(otherDiagnostic.chromeVersion){this.chromeVersions.add(otherDiagnostic.chromeVersion);}
+return this;}
+equals(other){if(!(other instanceof MergedDeviceInfo))return false;if(!tr.b.setsEqual(this.chromeVersions,other.chromeVersions)){return false;}
+if(!tr.b.setsEqual(this.osVersions,other.osVersions))return false;if(!tr.b.setsEqual(this.osNames,other.osNames))return false;return true;}
+asDictInto_(d){d.chromeVersions=Array.from(this.chromeVersions);d.osNames=Array.from(this.osNames);d.osVersions=Array.from(this.osVersions);}
+static fromDict(d){let info=new MergedDeviceInfo();for(let chromeVersion of d.chromeVersions){info.chromeVersions.add(chromeVersion);}
+for(let osName of d.osNames){info.osNames.add(osName);}
+for(let osVersion of d.osVersions){info.osVersions.add(osVersion);}
+return info;}
+get chromeVersions(){return this.chromeVersions_;}
+get osNames(){return this.osNames_;}
+get osVersions(){return this.osVersions_;}}
+tr.v.d.Diagnostic.register(MergedDeviceInfo,{elementName:'tr-v-ui-merged-device-info-span'});return{MergedDeviceInfo,};});'use strict';tr.exportTo('tr.v.d',function(){const REPO_NAMES=['chromium','v8','catapult','angle','skia','webrtc'];class MergedRevisionInfo extends tr.v.d.Diagnostic{constructor(opt_info){super();let info=opt_info||{};this.chromiumCommitPositions_=new Set(info.chromiumCommitPositions||[]);this.v8CommitPositions_=new Set(info.v8CommitPositions||[]);this.chromium_=info.chromium||[];this.v8_=info.v8||[];this.catapult_=info.catapult||[];this.angle_=info.angle||[];this.skia_=info.skia||[];this.webrtc_=info.webrtc||[];}
+clone(){let clone=new MergedRevisionInfo();clone.addDiagnostic(this);return clone;}
+addToHistogram(hist){hist.diagnostics.set(tr.v.d.RevisionInfo.NAME,this);}
+canAddDiagnostic(otherDiagnostic,name,parentHist,otherParentHist){return otherDiagnostic instanceof tr.v.d.RevisionInfo||otherDiagnostic instanceof MergedRevisionInfo;}
+addDiagnostic(otherDiagnostic,name,parentHist,otherParentHist){if(otherDiagnostic instanceof MergedRevisionInfo){for(let pos of otherDiagnostic.chromiumCommitPositions){this.chromiumCommitPositions.add(pos);}
+for(let pos of otherDiagnostic.v8CommitPositions){this.v8CommitPositions.add(pos);}
+for(let repo of REPO_NAMES){for(let otherRevs of otherDiagnostic[repo]){let found=false;for(let revs of this[repo]){if(otherRevs[0]===revs[0]&&otherRevs[1]===revs[1]){found=true;break;}}
+if(!found){this[repo].push(otherRevs);}}}
+return this;}
+if(otherDiagnostic.chromiumCommitPosition!==undefined){this.chromiumCommitPositions.add(otherDiagnostic.chromiumCommitPosition);}
+if(otherDiagnostic.v8CommitPosition!==undefined){this.v8CommitPositions.add(otherDiagnostic.v8CommitPosition);}
+for(let repo of REPO_NAMES){let otherRevs=otherDiagnostic[repo];let found=false;for(let revs of this[repo]){if(otherRevs[0]===revs[0]&&otherRevs[1]===revs[1]){found=true;break;}}
+if(!found){this[repo].push(otherRevs);}}
+return this;}
+asDictInto_(d){d.chromiumCommitPositions=this.chromiumCommitPositions;d.v8CommitPositions=this.v8CommitPositions;d.chromium=this.chromium;d.v8=this.v8;d.catapult=this.catapult;d.angle=this.angle;d.skia=this.skia;d.webrtc=this.webrtc;}
+static fromDict(d){return new MergedRevisionInfo(d);}
+get chromiumCommitPositions(){return this.chromiumCommitPositions_;}
+get v8CommitPositions(){return this.v8CommitPositions_;}
+get chromium(){return this.chromium_;}
+get v8(){return this.v8_;}
+get catapult(){return this.catapult_;}
+get angle(){return this.angle_;}
+get skia(){return this.skia_;}
+get webrtc(){return this.webrtc_;}}
+tr.v.d.Diagnostic.register(MergedRevisionInfo,{elementName:'tr-v-ui-merged-revision-info-span'});return{MergedRevisionInfo,};});'use strict';tr.exportTo('tr.v.d',function(){class MergedTelemetryInfo extends tr.v.d.Diagnostic{constructor(){super();this.benchmarkNames_=new Set();this.benchmarkStartsMs_=new Set();this.labels_=new Set();this.legacyTIRLabels_=new Set();this.storyDisplayNames_=new Set();this.storyGroupingKeys_=new Map();this.storysetRepeatCounters_=new Set();}
+clone(){let clone=new tr.v.d.MergedTelemetryInfo();clone.addDiagnostic(this);return clone;}
+addToHistogram(hist){hist.diagnostics.set(tr.v.d.TelemetryInfo.NAME,this);}
+equals(other){if(!(other instanceof MergedTelemetryInfo))return false;if(!tr.b.setsEqual(this.benchmarkNames,other.benchmarkNames)){return false;}
+if(!tr.b.setsEqual(this.labels,other.labels))return false;if(!tr.b.setsEqual(this.storyDisplayNames,other.storyDisplayNames)){return false;}
+if(!tr.b.setsEqual(this.legacyTIRLabels,other.legacyTIRLabels)){return false;}
+if(!tr.b.setsEqual(this.storysetRepeatCounters,other.storysetRepeatCounters)){return false;}
+if(!tr.b.setsEqual(this.benchmarkStartsMs,other.benchmarkStartsMs)){return false;}
+if(!tr.b.setsEqual(new Set(this.storyGroupingKeys.keys()),new Set(other.storyGroupingKeys.keys()))){return false;}
+for(let[k,vs]of this.storyGroupingKeys){if(!tr.b.setsEqual(vs,other.storyGroupingKeys.get(k))){return false;}}
+return true;}
+canAddDiagnostic(otherDiagnostic){return otherDiagnostic instanceof MergedTelemetryInfo||otherDiagnostic instanceof tr.v.d.TelemetryInfo;}
+addDiagnostic(otherDiagnostic){if(otherDiagnostic instanceof MergedTelemetryInfo){for(let name of otherDiagnostic.benchmarkNames){this.benchmarkNames.add(name);}
+for(let t of otherDiagnostic.benchmarkStartsMs){this.benchmarkStartsMs.add(t);}
+for(let name of otherDiagnostic.labels){this.labels.add(name);}
+for(let name of otherDiagnostic.legacyTIRLabels){this.legacyTIRLabels.add(name);}
+for(let name of otherDiagnostic.storyDisplayNames){this.storyDisplayNames.add(name);}
+for(let name of otherDiagnostic.storysetRepeatCounters){this.storysetRepeatCounters.add(name);}
+for(let[name,value]of otherDiagnostic.storyGroupingKeys){if(this.storyGroupingKeys.has(name)){for(let subValue of value){this.storyGroupingKeys.get(name).add(subValue);}}else{this.storyGroupingKeys.set(name,new Set(value));}}
+return;}
+if(otherDiagnostic.benchmarkName){this.benchmarkNames.add(otherDiagnostic.benchmarkName);}
+if(otherDiagnostic.benchmarkStart!==undefined){this.benchmarkStartsMs.add(otherDiagnostic.benchmarkStart.getTime());}
+if(otherDiagnostic.label){this.labels.add(otherDiagnostic.label);}
+if(otherDiagnostic.legacyTIRLabel){this.legacyTIRLabels.add(otherDiagnostic.legacyTIRLabel);}
+if(otherDiagnostic.storyDisplayName){this.storyDisplayNames.add(otherDiagnostic.storyDisplayName);}
+for(let[name,value]of otherDiagnostic.storyGroupingKeys){if(this.storyGroupingKeys.has(name)){this.storyGroupingKeys.get(name).add(value);}else{this.storyGroupingKeys.set(name,new Set([value]));}}
+if(otherDiagnostic.storysetRepeatCounter!==undefined){this.storysetRepeatCounters.add(otherDiagnostic.storysetRepeatCounter);}}
+asDictInto_(d){if(this.benchmarkNames.size){d.benchmarkNames=Array.from(this.benchmarkNames);}
+if(this.benchmarkStartsMs.size){d.benchmarkStartsMs=Array.from(this.benchmarkStartsMs);}
+if(this.labels.size){d.labels=Array.from(this.labels);}
+if(this.legacyTIRLabels.size){d.legacyTIRLabels=this.legacyTIRLabels;}
+if(this.storyDisplayNames.size){d.storyDisplayNames=Array.from(this.storyDisplayNames);}
+if(this.storyGroupingKeys.size){d.storyGroupingKeys={};for(let[name,values]of this.storyGroupingKeys){d.storyGroupingKeys[name]=Array.from(values);}}
+if(this.storysetRepeatCounters.size){d.storysetRepeatCounters=Array.from(this.storysetRepeatCounters);}}
+static fromDict(d){let info=new MergedTelemetryInfo();for(let n of d.benchmarkNames||[]){info.benchmarkNames_.add(n);}
+for(let n of d.benchmarkStartsMs||[]){info.benchmarkStartsMs_.add(n);}
+for(let n of d.labels||[]){info.labels_.add(n);}
+for(let n of d.legacyTIRLabels||[]){info.legacyTIRLabels_.add(n);}
+for(let n of d.storyDisplayNames||[]){info.storyDisplayNames_.add(n);}
+for(let[name,values]of Object.entries(d.storyGroupingKeys||{})){info.storyGroupingKeys_.set(name,new Set(values));}
+for(let n of d.storysetRepeatCounters||[]){info.storysetRepeatCounters_.add(n);}
+return info;}
+get displayLabel(){if(this.labels.size){return Array.from(this.labels).join('\n');}
+return Array.from(this.benchmarkNames).concat(this.benchmarkStartStrings).join('\n');}
+get benchmarkNames(){return this.benchmarkNames_;}
+get labels(){return this.labels_;}
+get legacyTIRLabels(){return this.legacyTIRLabels_;}
+get storyGroupingKeys(){return this.storyGroupingKeys_;}
+get storyDisplayNames(){return this.storyDisplayNames_;}
+get storysetRepeatCounters(){return this.storysetRepeatCounters_;}
+get storysetRepeatCounterLabel(){return'storyset repeat '+Array.from(this.storysetRepeatCounters).join(',');}
+get benchmarkStartsMs(){return this.benchmarkStartsMs_;}
+get benchmarkStarts(){let startsMs=Array.from(this.benchmarkStartsMs);startsMs.sort((x,y)=>x-y);return startsMs.map(t=>new Date(t));}
+get benchmarkStartStrings(){return this.benchmarkStarts.map(tr.b.formatDate);}
+static getField(hist,fieldName,defaultValue){let telemetry=tr.v.d.TelemetryInfo.getFromHistogram(hist);if(!(telemetry instanceof tr.v.d.MergedTelemetryInfo)||!telemetry[fieldName]){return defaultValue;}
+return telemetry[fieldName];}
+static getStoryGroupingKeyLabel(hist,storyGroupingKey){let telemetry=tr.v.d.TelemetryInfo.getFromHistogram(hist);if(!(telemetry instanceof tr.v.d.MergedTelemetryInfo)){return storyGroupingKey+': undefined';}
+return storyGroupingKey+': '+
+telemetry.storyGroupingKeys[storyGroupingKey];}
+static makeStoryGroupingKeyLabelGetter(storyGroupingKey){return v=>MergedTelemetryInfo.getStoryGroupingKeyLabel(v,storyGroupingKey);}}
+tr.v.d.Diagnostic.register(MergedTelemetryInfo,{elementName:'tr-v-ui-merged-telemetry-info-span'});return{MergedTelemetryInfo,};});'use strict';tr.exportTo('tr.v.d',function(){class EventRef{constructor(event){this.stableId=event.stableId;this.title=event.title;this.start=event.start;this.duration=event.duration;this.end=this.start+this.duration;this.guid=tr.b.GUID.allocateSimple();}}
+return{EventRef,};});'use strict';tr.exportTo('tr.v.d',function(){class RelatedEventSet extends tr.v.d.Diagnostic{constructor(opt_events){super();this.eventsByStableId_=new Map();this.canonicalUrl_=undefined;if(opt_events){if(opt_events instanceof tr.model.EventSet||opt_events instanceof Array){for(let event of opt_events){this.add(event);}}else{this.add(opt_events);}}}
+clone(){let clone=new tr.v.d.CollectedRelatedEventSet();clone.addDiagnostic(this);return clone;}
+add(event){this.eventsByStableId_.set(event.stableId,event);}
+has(event){return this.eventsByStableId_.has(event.stableId);}
+get length(){return this.eventsByStableId_.size;}*[Symbol.iterator](){for(let event of this.eventsByStableId_.values()){yield event;}}
+get canonicalUrl(){return this.canonicalUrl_;}
+resolve(model,opt_required){for(let[stableId,event]of this.eventsByStableId_){if(!(event instanceof tr.v.d.EventRef))continue;event=model.getEventByStableId(stableId);if(event instanceof tr.model.Event){this.eventsByStableId_.set(stableId,event);}else if(opt_required){throw new Error('Unable to find Event '+stableId);}}}
+asDictInto_(d){d.events=[];for(let event of this){d.events.push({stableId:event.stableId,title:event.title,start:event.start,duration:event.duration});}}
+static fromDict(d){return new RelatedEventSet(d.events.map(event=>new tr.v.d.EventRef(event)));}}
+tr.v.d.Diagnostic.register(RelatedEventSet,{elementName:'tr-v-ui-related-event-set-span'});return{RelatedEventSet,};});'use strict';tr.exportTo('tr.v.d',function(){function HistogramRef(guid){this.guid=guid;}
+return{HistogramRef};});'use strict';tr.exportTo('tr.v.d',function(){class RelatedHistogramMap extends tr.v.d.Diagnostic{constructor(){super();this.histogramsByName_=new Map();}
+canAddDiagnostic(otherDiagnostic){return otherDiagnostic instanceof RelatedHistogramMap;}
+addDiagnostic(otherDiagnostic){}
+mergeRelationships(otherDiagnostic,parentHist,otherParentHist){let parentGroupingPath=tr.v.d.GroupingPath.getFromHistogram(parentHist);for(let[name,otherRelatedHist]of otherDiagnostic){let mergedTo=otherRelatedHist.diagnostics.get(tr.v.d.MERGED_TO_DIAGNOSTIC_KEY);if(mergedTo===undefined)continue;for(let relatedHist of mergedTo){let relatedGroupingPath=tr.v.d.GroupingPath.getFromHistogram(relatedHist);if(relatedGroupingPath===undefined)continue;if(!parentGroupingPath.equals(relatedGroupingPath))continue;this.set(name,relatedHist);}}}
+get(name){return this.histogramsByName_.get(name);}
+set(name,hist){if(!(hist instanceof tr.v.Histogram)&&!(hist instanceof tr.v.d.HistogramRef)){throw new Error('Must be instanceof Histogram or HistogramRef: '+
+hist);}
+this.histogramsByName_.set(name,hist);}
+add(hist){this.set(hist.name,hist);}
+get length(){return this.histogramsByName_.size;}*[Symbol.iterator](){for(let pair of this.histogramsByName_){yield pair;}}
+resolve(histograms,opt_required){for(let[name,hist]of this){if(!(hist instanceof tr.v.d.HistogramRef))continue;let guid=hist.guid;hist=histograms.lookupHistogram(guid);if(hist instanceof tr.v.Histogram){this.histogramsByName_.set(name,hist);}else if(opt_required){throw new Error('Unable to find Histogram '+guid);}}}
+asDictInto_(d){d.values={};for(let[name,hist]of this){d.values[name]=hist.guid;}}
+static fromDict(d){let map=new RelatedHistogramMap();for(let[name,guid]of Object.entries(d.values)){map.set(name,new tr.v.d.HistogramRef(guid));}
+return map;}}
+tr.v.d.Diagnostic.register(RelatedHistogramMap,{elementName:'tr-v-ui-related-histogram-map-span'});return{RelatedHistogramMap,};});'use strict';tr.exportTo('tr.v.d',function(){const COLOR_SCHEME_CHROME_USER_FRIENDLY_CATEGORY_DRIVER='ChromeUserFriendlyCategory';class RelatedHistogramBreakdown extends tr.v.d.RelatedHistogramMap{constructor(){super();this.colorScheme=undefined;}
+clone(){let clone=new RelatedHistogramBreakdown();clone.colorScheme=this.colorScheme;return clone;}
+canAddDiagnostic(otherDiagnostic){return otherDiagnostic instanceof RelatedHistogramBreakdown&&otherDiagnostic.colorScheme===this.colorScheme;}
+set(name,hist){if(!(hist instanceof tr.v.d.HistogramRef)){if(!(hist instanceof tr.v.Histogram)){throw new Error('RelatedHistogramBreakdown can only contain Histograms');}
+if((this.length>0)&&(hist.unit!==tr.b.getFirstElement(this)[1].unit)){throw new Error('Units mismatch',tr.b.getFirstElement(this)[1].unit,hist.unit);}}
+tr.v.d.RelatedHistogramMap.prototype.set.call(this,name,hist);}
+asDictInto_(d){tr.v.d.RelatedHistogramMap.prototype.asDictInto_.call(this,d);if(this.colorScheme)d.colorScheme=this.colorScheme;}
+static fromDict(d){let diagnostic=new RelatedHistogramBreakdown();for(let[name,guid]of Object.entries(d.values)){diagnostic.set(name,new tr.v.d.HistogramRef(guid));}
+if(d.colorScheme)diagnostic.colorScheme=d.colorScheme;return diagnostic;}
+static buildFromEvents(histograms,namePrefix,events,categoryForEvent,unit,opt_sampleForEvent,opt_binBoundaries,opt_this){let sampleForEvent=opt_sampleForEvent||((event)=>event.cpuSelfTime);let diagnostic=new RelatedHistogramBreakdown();for(let event of events){let sample=sampleForEvent.call(opt_this,event);if(sample===undefined)continue;let eventCategory=categoryForEvent.call(opt_this,event);let hist=diagnostic.get(eventCategory);if(hist===undefined){hist=new tr.v.Histogram(namePrefix+eventCategory,unit,opt_binBoundaries);histograms.addHistogram(hist);diagnostic.set(eventCategory,hist);}
+hist.addSample(sample,{relatedEvents:new tr.v.d.RelatedEventSet([event])});}
+return diagnostic;}}
+tr.v.d.Diagnostic.register(RelatedHistogramBreakdown,{elementName:'tr-v-ui-breakdown-span'});return{COLOR_SCHEME_CHROME_USER_FRIENDLY_CATEGORY_DRIVER,RelatedHistogramBreakdown,};});'use strict';tr.exportTo('tr.v.d',function(){class RelatedHistogramSet extends tr.v.d.Diagnostic{constructor(opt_histograms){super();this.histogramsByGuid_=new Map();if(opt_histograms){for(let hist of opt_histograms){this.add(hist);}}}
+canAddDiagnostic(otherDiagnostic){return otherDiagnostic instanceof RelatedHistogramSet;}
+addDiagnostic(otherDiagnostic){}
+mergeRelationships(otherDiagnostic,parentHist,otherParentHist){let parentGroupingPath=tr.v.d.GroupingPath.getFromHistogram(parentHist);for(let otherRelatedHist of otherDiagnostic){let mergedTo=otherRelatedHist.diagnostics.get(tr.v.d.MERGED_TO_DIAGNOSTIC_KEY);if(mergedTo===undefined)continue;for(let relatedHist of mergedTo){if(this.has(relatedHist))continue;let relatedGroupingPath=tr.v.d.GroupingPath.getFromHistogram(relatedHist);if(relatedGroupingPath===undefined)continue;if(!parentGroupingPath.equals(relatedGroupingPath))continue;this.add(relatedHist);}}}
+add(hist){if(!(hist instanceof tr.v.Histogram)&&!(hist instanceof tr.v.d.HistogramRef)){throw new Error('Must be instanceof Histogram or HistogramRef: '+
+hist);}
+if(this.histogramsByGuid_.has(hist.guid)){throw new Error('Tried to add same hist twice');}
+this.histogramsByGuid_.set(hist.guid,hist);}
+has(hist){return this.histogramsByGuid_.has(hist.guid);}
+get length(){return this.histogramsByGuid_.size;}*[Symbol.iterator](){for(let[guid,hist]of this.histogramsByGuid_){yield hist;}}
+resolve(histograms,opt_required){for(let[guid,hist]of this.histogramsByGuid_){if(!(hist instanceof tr.v.d.HistogramRef))continue;hist=histograms.lookupHistogram(guid);if(hist instanceof tr.v.Histogram){this.histogramsByGuid_.set(guid,hist);}else if(opt_required){throw new Error('Unable to find Histogram '+guid);}}}
+asDictInto_(d){d.guids=[];for(let hist of this){d.guids.push(hist.guid);}}
+static fromDict(d){return new RelatedHistogramSet(d.guids.map(guid=>new tr.v.d.HistogramRef(guid)));}}
+tr.v.d.Diagnostic.register(RelatedHistogramSet,{elementName:'tr-v-ui-related-histogram-set-span'});return{RelatedHistogramSet,};});'use strict';tr.exportTo('tr.v.d',function(){class RevisionInfo extends tr.v.d.Diagnostic{constructor(info){super();this.chromiumCommitPosition_=info.chromiumCommitPosition||undefined;this.v8CommitPosition_=info.v8CommitPosition||undefined;this.chromium_=info.chromium||[];this.v8_=info.v8||[];this.catapult_=info.catapult||[];this.angle_=info.angle||[];this.skia_=info.skia||[];this.webrtc_=info.webrtc||[];}
+addToHistogram(hist){hist.diagnostics.set(RevisionInfo.NAME,this);}
+clone(){let clone=new tr.v.d.MergedRevisionInfo();clone.addDiagnostic(this);return clone;}
+asDictInto_(d){d.chromiumCommitPosition=this.chromiumCommitPosition;d.v8CommitPosition=this.v8CommitPosition;d.chromium=this.chromium;d.v8=this.v8;d.catapult=this.catapult;d.angle=this.angle;d.skia=this.skia;d.webrtc=this.webrtc;}
+static fromDict(d){return new RevisionInfo(d);}
+get chromiumCommitPosition(){return this.chromiumCommitPosition_;}
+get v8CommitPosition(){return this.v8CommitPosition_;}
+get chromium(){return this.chromium_;}
+get v8(){return this.v8_;}
+get catapult(){return this.catapult_;}
+get angle(){return this.angle_;}
+get skia(){return this.skia_;}
+get webrtc(){return this.webrtc_;}}
+RevisionInfo.NAME='revisions';tr.v.d.Diagnostic.register(RevisionInfo,{elementName:'tr-v-ui-revision-info-span'});return{RevisionInfo,};});'use strict';tr.exportTo('tr.v.d',function(){class Scalar extends tr.v.d.Diagnostic{constructor(value){super();if(!(value instanceof tr.b.Scalar)){throw new Error('expected Scalar');}
+this.value=value;}
+clone(){return new Scalar(this.value);}
+asDictInto_(d){d.value=this.value.asDict();}
+static fromDict(d){return new Scalar(tr.b.Scalar.fromDict(d.value));}}
+tr.v.d.Diagnostic.register(Scalar,{elementName:'tr-v-ui-scalar-diagnostic-span'});return{Scalar,};});'use strict';tr.exportTo('tr.v.d',function(){class TelemetryInfo extends tr.v.d.Diagnostic{constructor(opt_info){super();this.benchmarkName_='';this.benchmarkStart_=undefined;this.label_='';this.legacyTIRLabel_='';this.storyDisplayName_='';this.storyGroupingKeys_=new Map();this.storysetRepeatCounter_=undefined;this.canonicalUrl_='';if(opt_info){this.addInfo(opt_info);}}
+clone(){let clone=new tr.v.d.MergedTelemetryInfo();clone.addDiagnostic(this);return clone;}
+addInfo(info){if(info.benchmarkName){this.benchmarkName_=info.benchmarkName;}
+if(info.benchmarkStartMs!==undefined){this.benchmarkStart_=new Date(info.benchmarkStartMs);}
+if(info.label){this.label_=info.label;}
+if(info.storyDisplayName){this.storyDisplayName_=info.storyDisplayName;}
+for(let[name,value]of Object.entries(info.storyGroupingKeys||{})){this.storyGroupingKeys_.set(name,value);}
+if(info.storysetRepeatCounter!==undefined){this.storysetRepeatCounter_=info.storysetRepeatCounter;}
+if(info.legacyTIRLabel){this.legacyTIRLabel_=info.legacyTIRLabel;}
+if(info.canonicalUrl){this.canonicalUrl_=info.canonicalUrl;}}
+addToHistogram(hist){hist.diagnostics.set(TelemetryInfo.NAME,this);}
+static getFromHistogram(hist){return hist.diagnostics.get(TelemetryInfo.NAME)||hist.diagnostics.get('iteration');}
+asDictInto_(d){d.benchmarkName=this.benchmarkName;if(this.benchmarkStart){d.benchmarkStartMs=this.benchmarkStart.getTime();}
+d.label=this.label;d.storyDisplayName=this.storyDisplayName;if(this.storyGroupingKeys.size>0){d.storyGroupingKeys={};for(let[name,value]of this.storyGroupingKeys){d.storyGroupingKeys[name]=value;}}
+d.storysetRepeatCounter=this.storysetRepeatCounter;d.legacyTIRLabel=this.legacyTIRLabel;d.canonicalUrl=this.canonicalUrl;}
+static fromDict(d){let info=new TelemetryInfo();info.addInfo(d);return info;}
+get displayLabel(){if(this.label)return this.label;return this.benchmarkName+'\n'+this.benchmarkStartString;}
+get benchmarkName(){return this.benchmarkName_;}
+get label(){return this.label_;}
+get legacyTIRLabel(){return this.legacyTIRLabel_;}
+set legacyTIRLabel(tir){this.legacyTIRLabel_=tir;}
+get storyGroupingKeys(){return this.storyGroupingKeys_;}
+get storyDisplayName(){return this.storyDisplayName_;}
+get storysetRepeatCounter(){return this.storysetRepeatCounter_;}
+get storysetRepeatCounterLabel(){return'storyset repeat '+this.storysetRepeatCounter;}
+get canonicalUrl(){return this.canonicalUrl_;}
+get benchmarkStart(){return this.benchmarkStart_;}
+get benchmarkStartString(){if(this.benchmarkStart_===undefined)return'';return tr.b.formatDate(this.benchmarkStart);}
+static getField(hist,fieldName,defaultValue){let telemetry=tr.v.d.TelemetryInfo.getFromHistogram(hist);if(!(telemetry instanceof tr.v.d.TelemetryInfo)||!telemetry[fieldName]){return defaultValue;}
+return telemetry[fieldName];}
+static getStoryGroupingKeyLabel(hist,storyGroupingKey){let telemetry=tr.v.d.TelemetryInfo.getFromHistogram(hist);if(!(telemetry instanceof tr.v.d.TelemetryInfo)){return storyGroupingKey+': undefined';}
+return storyGroupingKey+': '+
+telemetry.storyGroupingKeys.get(storyGroupingKey);}
+static makeStoryGroupingKeyLabelGetter(storyGroupingKey){return v=>TelemetryInfo.getStoryGroupingKeyLabel(v,storyGroupingKey);}}
+TelemetryInfo.NAME='telemetry';tr.v.d.Diagnostic.register(TelemetryInfo,{elementName:'tr-v-ui-telemetry-info-span'});return{TelemetryInfo,};});'use strict';tr.exportTo('tr.v.d',function(){class UnmergeableDiagnosticSet extends tr.v.d.Diagnostic{constructor(diagnostics){super();this._diagnostics=diagnostics;}
+clone(){let clone=new tr.v.d.UnmergeableDiagnosticSet();clone.addDiagnostic(this);return clone;}
+canAddDiagnostic(otherDiagnostic){return true;}
+addDiagnostic(otherDiagnostic){if(otherDiagnostic instanceof UnmergeableDiagnosticSet){for(let subOtherDiagnostic of otherDiagnostic){let clone=subOtherDiagnostic.clone();this.addDiagnostic(clone);}
+return;}
+for(let i=0;i<this._diagnostics.length;++i){if(this._diagnostics[i].canAddDiagnostic(otherDiagnostic)){this._diagnostics[i].addDiagnostic(otherDiagnostic);return;}}
+let clone=otherDiagnostic.clone();this._diagnostics.push(clone);}
+mergeRelationships(otherDiagnostic,parentHist,otherParentHist){if(otherDiagnostic instanceof UnmergeableDiagnosticSet){for(let subDiagnostic of otherDiagnostic){this.mergeRelationships(subDiagnostic,parentHist,otherParentHist);}
+return;}
+for(let subDiagnostic of this){if(!(subDiagnostic instanceof tr.v.d.RelatedHistogramSet)&&!(subDiagnostic instanceof tr.v.d.RelatedHistogramMap)&&!(subDiagnostic instanceof tr.v.d.RelatedHistogramBreakdown)){continue;}
+subDiagnostic.mergeRelationships(otherDiagnostic,parentHist,otherParentHist);}}
+get length(){return this._diagnostics.length;}*[Symbol.iterator](){for(let diagnostic of this._diagnostics)yield diagnostic;}
+asDictInto_(d){d.diagnostics=this._diagnostics.map(d=>d.asDictOrReference());}
+static fromDict(d){return new UnmergeableDiagnosticSet(d.diagnostics.map(d=>((typeof d==='string')?new tr.v.d.DiagnosticRef(d):tr.v.d.Diagnostic.fromDict(d))));}}
+tr.v.d.Diagnostic.register(UnmergeableDiagnosticSet,{elementName:'tr-v-ui-unmergeable-diagnostic-set-span'});return{UnmergeableDiagnosticSet,};});'use strict';tr.exportTo('tr.v.d',function(){const MERGED_FROM_DIAGNOSTIC_KEY='merged from';const MERGED_TO_DIAGNOSTIC_KEY='merged to';class DiagnosticMap extends Map{set(name,diagnostic){if(typeof(name)!=='string'){throw new Error('name must be string, not '+name);}
+if(!(diagnostic instanceof tr.v.d.Diagnostic)&&!(diagnostic instanceof tr.v.d.DiagnosticRef)){throw new Error('Must be instanceof Diagnostic: '+diagnostic);}
+Map.prototype.set.call(this,name,diagnostic);}
+addDicts(dict){for(var[name,diagnosticDict]of Object.entries(dict)){if(typeof diagnosticDict==='string'){this.set(name,new tr.v.d.DiagnosticRef(diagnosticDict));}else{this.set(name,tr.v.d.Diagnostic.fromDict(diagnosticDict));}}}
+resolveSharedDiagnostics(histograms,opt_required){for(let[name,diagnostic]of this){if(!(diagnostic instanceof tr.v.d.DiagnosticRef)){continue;}
+let guid=diagnostic.guid;diagnostic=histograms.lookupDiagnostic(guid);if(diagnostic instanceof tr.v.d.Diagnostic){this.set(name,diagnostic);}else if(opt_required){throw new Error('Unable to find shared Diagnostic '+guid);}}}
+asDict(){let dict={};for(let[name,diagnostic]of this){dict[name]=diagnostic.asDictOrReference();}
+return dict;}
+static fromDict(d){let diagnostics=new DiagnosticMap();diagnostics.addDicts(d);return diagnostics;}
+static fromObject(obj){let diagnostics=new DiagnosticMap();for(let[name,diagnostic]of Object.entries(obj)){diagnostics.set(name,diagnostic);}
+return diagnostics;}
+addDiagnostics(other){for(let[name,otherDiagnostic]of other){if(name===MERGED_FROM_DIAGNOSTIC_KEY||name===MERGED_TO_DIAGNOSTIC_KEY||name===tr.v.d.GroupingPath){continue;}
+let myDiagnostic=this.get(name);if(myDiagnostic!==undefined&&myDiagnostic.canAddDiagnostic(otherDiagnostic)){myDiagnostic.addDiagnostic(otherDiagnostic);continue;}
+let clone=otherDiagnostic.clone();if(myDiagnostic===undefined){this.set(name,clone);continue;}
+this.set(name,new tr.v.d.UnmergeableDiagnosticSet([myDiagnostic,clone]));}}
+mergeRelationships(parentHist){for(let[name,diagnostic]of this){if(!(diagnostic instanceof tr.v.d.RelatedHistogramSet)&&!(diagnostic instanceof tr.v.d.RelatedHistogramMap)&&!(diagnostic instanceof tr.v.d.RelatedHistogramBreakdown)&&!(diagnostic instanceof tr.v.d.UnmergeableDiagnosticSet)){continue;}
+for(let otherHist of this.get(MERGED_FROM_DIAGNOSTIC_KEY)){let otherDiagnostic=otherHist.diagnostics.get(name);if(!(otherDiagnostic instanceof tr.v.d.RelatedHistogramSet)&&!(otherDiagnostic instanceof tr.v.d.RelatedHistogramMap)&&!(otherDiagnostic instanceof tr.v.d.RelatedHistogramBreakdown)&&!(otherDiagnostic instanceof tr.v.d.UnmergeableDiagnosticSet)){continue;}
+diagnostic.mergeRelationships(otherDiagnostic,parentHist,otherHist);}}}}
+return{DiagnosticMap,MERGED_FROM_DIAGNOSTIC_KEY,MERGED_TO_DIAGNOSTIC_KEY,};});'use strict';tr.exportTo('tr.v',function(){const MAX_DIAGNOSTIC_MAPS=16;const DEFAULT_SAMPLE_VALUES_PER_BIN=10;const DEFAULT_REBINNED_COUNT=40;const DEFAULT_BOUNDARIES_FOR_UNIT=new Map();const DELTA=String.fromCharCode(916);const Z_SCORE_NAME='z-score';const P_VALUE_NAME='p-value';const U_STATISTIC_NAME='U';function percentToString(percent){if(percent<0||percent>1){throw new Error('percent must be in [0,1]');}
+if(percent===0)return'000';if(percent===1)return'100';let str=percent.toString();if(str[1]!=='.'){throw new Error('Unexpected percent');}
+str=str+'0'.repeat(Math.max(4-str.length,0));if(str.length>4)str=str.slice(0,4)+'_'+str.slice(4);return'0'+str.slice(2);}
+function percentFromString(s){return parseFloat(s[0]+'.'+s.substr(1).replace(/_/g,''));}
+class HistogramBin{constructor(range){this.range=range;this.count=0;this.diagnosticMaps=[];}
 addSample(value){this.count+=1;}
-addDiagnosticMap(diagnostics){tr.b.Statistics.uniformlySampleStream(this.diagnosticMaps,this.count,diagnostics,MAX_DIAGNOSTIC_MAPS);}
-addBin(other){if(!this.range.equals(other.range))
-throw new Error('Merging incompatible Histogram bins.');tr.b.Statistics.mergeSampledStreams(this.diagnosticMaps,this.count,other.diagnosticMaps,other.count,MAX_DIAGNOSTIC_MAPS);this.count+=other.count;}
-fromDict(d){this.count=d.count;for(var map of d.diagnosticMaps)
-this.diagnosticMaps.push(tr.v.d.DiagnosticMap.fromDict(map));}
-asDict(){return{count:this.count,diagnosticMaps:this.diagnosticMaps.map(d=>d.asDict())};}}
-class Histogram{constructor(name,unit,opt_binBoundaries){var binBoundaries=opt_binBoundaries;if(!binBoundaries){var baseUnit=unit.baseUnit?unit.baseUnit:unit;binBoundaries=DEFAULT_BOUNDARIES_FOR_UNIT.get(baseUnit.unitName);}
-this.guid_=undefined;this.allBins=[];this.centralBins=[];this.description='';this.diagnostics=new tr.v.d.DiagnosticMap();this.maxCount_=0;this.name_=name;this.nanDiagnosticMaps=[];this.numNans=0;this.running=new tr.b.RunningStatistics();this.sampleValues_=[];this.shortName=undefined;this.summaryOptions={count:true,sum:true,avg:true,geometricMean:false,std:true,min:true,max:true,nans:false,percentile:[]};this.unit=unit;this.underflowBin=new HistogramBin(tr.b.Range.fromExplicitRange(-Number.MAX_VALUE,binBoundaries.minBinBoundary));this.overflowBin=new HistogramBin(tr.b.Range.fromExplicitRange(binBoundaries.maxBinBoundary,Number.MAX_VALUE));for(var range of binBoundaries)
-this.centralBins.push(new HistogramBin(range));this.allBins.push(this.underflowBin);for(var bin of this.centralBins)
-this.allBins.push(bin);this.allBins.push(this.overflowBin);this.maxNumSampleValues=this.allBins.length*10;}
+addDiagnosticMap(diagnostics){tr.b.math.Statistics.uniformlySampleStream(this.diagnosticMaps,this.count,diagnostics,MAX_DIAGNOSTIC_MAPS);}
+addBin(other){if(!this.range.equals(other.range)){throw new Error('Merging incompatible Histogram bins.');}
+tr.b.math.Statistics.mergeSampledStreams(this.diagnosticMaps,this.count,other.diagnosticMaps,other.count,MAX_DIAGNOSTIC_MAPS);this.count+=other.count;}
+fromDict(dict){this.count=dict[0];if(dict.length>1){for(let map of dict[1]){this.diagnosticMaps.push(tr.v.d.DiagnosticMap.fromDict(map));}}}
+asDict(){if(!this.diagnosticMaps.length){return[this.count];}
+return[this.count,this.diagnosticMaps.map(d=>d.asDict())];}}
+const DEFAULT_SUMMARY_OPTIONS=new Map([['avg',true],['count',true],['geometricMean',false],['max',true],['min',true],['nans',false],['std',true],['sum',true],]);class Histogram{constructor(name,unit,opt_binBoundaries){let binBoundaries=opt_binBoundaries;if(!binBoundaries){let baseUnit=unit.baseUnit?unit.baseUnit:unit;binBoundaries=DEFAULT_BOUNDARIES_FOR_UNIT.get(baseUnit.unitName);}
+this.guid_=undefined;this.binBoundariesDict_=binBoundaries.asDict();this.allBins=binBoundaries.bins.slice();this.description='';this.diagnostics=new tr.v.d.DiagnosticMap();this.maxNumSampleValues_=this.defaultMaxNumSampleValues_;this.name_=name;this.nanDiagnosticMaps=[];this.numNans=0;this.running_=undefined;this.sampleValues_=[];this.shortName=undefined;this.summaryOptions=new Map(DEFAULT_SUMMARY_OPTIONS);this.summaryOptions.set('percentile',[]);this.unit=unit;}
+get running(){return this.running_;}
+get maxNumSampleValues(){return this.maxNumSampleValues_;}
+set maxNumSampleValues(n){this.maxNumSampleValues_=n;tr.b.math.Statistics.uniformlySampleArray(this.sampleValues_,this.maxNumSampleValues_);}
 get name(){return this.name_;}
-get guid(){if(this.guid_===undefined)
-this.guid_=tr.b.GUID.allocateUUID4();return this.guid_;}
-set guid(guid){if(this.guid_!==undefined)
-throw new Error('Cannot reset guid');this.guid_=guid;}
-static fromDict(d){var boundaries=HistogramBinBoundaries.createWithBoundaries(d.binBoundaries);var n=new Histogram(d.name,tr.b.Unit.fromJSON(d.unit),boundaries);n.guid=d.guid;n.shortName=d.shortName;n.description=d.description;n.diagnostics.addDicts(d.diagnostics);n.underflowBin.fromDict(d.underflowBin);for(var i=0;i<d.centralBins.length;++i)
-n.centralBins[i].fromDict(d.centralBins[i]);n.overflowBin.fromDict(d.overflowBin);for(var bin of n.allBins)
-n.maxCount_=Math.max(n.maxCount_,bin.count);if(d.running)
-n.running=tr.b.RunningStatistics.fromDict(d.running);if(d.summaryOptions)
-n.customizeSummaryOptions(d.summaryOptions);n.maxNumSampleValues=d.maxNumSampleValues;n.sampleValues_=d.sampleValues;n.numNans=d.numNans;for(var map of d.nanDiagnosticMaps)
-n.nanDiagnosticMaps.push(tr.v.d.DiagnosticMap.fromDict(map));return n;}
-static buildFromSamples(unit,samples){var boundaries=HistogramBinBoundaries.createFromSamples(samples);var result=new Histogram(unit,boundaries);result.maxNumSampleValues=1000;for(var sample of samples)
-result.addSample(sample);return result;}
-get numValues(){return tr.b.Statistics.sum(this.allBins,function(e){return e.count;});}
-get average(){return this.running.mean;}
-get geometricMean(){return this.running.geometricMean;}
-get sum(){return this.running.sum;}
-get maxCount(){return this.maxCount_;}
-getDifferenceSignificance(other,opt_alpha){if(this.unit!==other.unit)
-throw new Error('Cannot compare Numerics with different units');if(this.unit.improvementDirection===tr.b.ImprovementDirection.DONT_CARE){return tr.v.Significance.DONT_CARE;}
-if(!(other instanceof Histogram))
-throw new Error('Unable to compute a p-value');var mwu=tr.b.Statistics.mwu.test(this.sampleValues,other.sampleValues);if(mwu.p<(opt_alpha||DEFAULT_ALPHA))
-return tr.v.Significance.SIGNIFICANT;return tr.v.Significance.INSIGNIFICANT;}
-getApproximatePercentile(percent){if(!(percent>=0&&percent<=1))
-throw new Error('percent must be [0,1]');if(this.numValues==0)
-return 0;var valuesToSkip=Math.floor((this.numValues-1)*percent);for(var i=0;i<this.allBins.length;i++){var bin=this.allBins[i];valuesToSkip-=bin.count;if(valuesToSkip<0){if(bin===this.underflowBin)
-return bin.range.max;else if(bin===this.overflowBin)
-return bin.range.min;else
-return bin.range.center;}}
-throw new Error('Unreachable');}
-getBinForValue(value){var binIndex=tr.b.findHighIndexInSortedArray(this.allBins,b=>value<b.range.max?-1:1);return this.allBins[binIndex]||this.overflowBin;}
-addSample(value,opt_diagnostics){if(opt_diagnostics&&!(opt_diagnostics instanceof tr.v.d.DiagnosticMap))
-opt_diagnostics=tr.v.d.DiagnosticMap.fromObject(opt_diagnostics);if(typeof(value)!=='number'||isNaN(value)){this.numNans++;if(opt_diagnostics){tr.b.Statistics.uniformlySampleStream(this.nanDiagnosticMaps,this.numNans,opt_diagnostics,MAX_DIAGNOSTIC_MAPS);}}else{this.running.add(value);var bin=this.getBinForValue(value);bin.addSample(value);if(opt_diagnostics)
-bin.addDiagnosticMap(opt_diagnostics);if(bin.count>this.maxCount_)
-this.maxCount_=bin.count;}
-tr.b.Statistics.uniformlySampleStream(this.sampleValues_,this.numValues+this.numNans,value,this.maxNumSampleValues);}
-sampleValuesInto(samples){for(var sampleValue of this.sampleValues)
-samples.push(sampleValue);}
-canAddHistogram(other){if(this.unit!==other.unit)
-return false;if(this.allBins.length!==other.allBins.length)
-return false;for(var i=0;i<this.allBins.length;++i)
-if(!this.allBins[i].range.equals(other.allBins[i].range))
-return false;return true;}
-addHistogram(other){if(!this.canAddHistogram(other))
-throw new Error('Merging incompatible Numerics.');tr.b.Statistics.mergeSampledStreams(this.nanDiagnosticMaps,this.numNans,other.nanDiagnosticMaps,other.numNans,MAX_DIAGNOSTIC_MAPS);tr.b.Statistics.mergeSampledStreams(this.sampleValues,this.numValues,other.sampleValues,other.numValues,tr.b.Statistics.mean([this.maxNumSampleValues,other.maxNumSampleValues]));this.numNans+=other.numNans;this.running=this.running.merge(other.running);for(var i=0;i<this.allBins.length;++i){this.allBins[i].addBin(other.allBins[i]);}}
-customizeSummaryOptions(summaryOptions){tr.b.iterItems(summaryOptions,function(key,value){this.summaryOptions[key]=value;},this);}
-get statisticsScalars(){function statNameToKey(stat){switch(stat){case'std':return'stddev';case'avg':return'mean';}
-return stat;}
-function percentToString(percent){if(percent<0||percent>1)
-throw new Error('Percent must be between 0.0 and 1.0');switch(percent){case 0:return'000';case 1:return'100';}
-var str=percent.toString();if(str[1]!=='.')
-throw new Error('Unexpected percent');str=str+'0'.repeat(Math.max(4-str.length,0));if(str.length>4)
-str=str.slice(0,4)+'_'+str.slice(4);return'0'+str.slice(2);}
-var results=new Map();tr.b.iterItems(this.summaryOptions,function(stat,option){if(!option)
-return;if(stat==='percentile'){option.forEach(function(percent){var percentile=this.getApproximatePercentile(percent);results.set('pct_'+percentToString(percent),new tr.v.ScalarNumeric(this.unit,percentile));},this);}else if(stat==='nans'){results.set('nans',new tr.v.ScalarNumeric(tr.b.Unit.byName.count_smallerIsBetter,this.numNans));}else{var statUnit=stat==='count'?tr.b.Unit.byName.count_smallerIsBetter:this.unit;var key=statNameToKey(stat);var statValue=this.running[key];if(typeof(statValue)==='number'){results.set(stat,new tr.v.ScalarNumeric(statUnit,statValue));}}},this);return results;}
+get guid(){if(this.guid_===undefined){this.guid_=tr.b.GUID.allocateUUID4();}
+return this.guid_;}
+set guid(guid){if(this.guid_!==undefined){throw new Error('Cannot reset guid');}
+this.guid_=guid;}
+static fromDict(dict){let hist=new Histogram(dict.name,tr.b.Unit.fromJSON(dict.unit),HistogramBinBoundaries.fromDict(dict.binBoundaries));hist.guid=dict.guid;if(dict.shortName){hist.shortName=dict.shortName;}
+if(dict.description){hist.description=dict.description;}
+if(dict.diagnostics){hist.diagnostics.addDicts(dict.diagnostics);}
+if(dict.allBins){if(dict.allBins.length!==undefined){for(let i=0;i<dict.allBins.length;++i){hist.allBins[i]=new HistogramBin(hist.allBins[i].range);hist.allBins[i].fromDict(dict.allBins[i]);}}else{for(var[i,binDict]of Object.entries(dict.allBins)){hist.allBins[i]=new HistogramBin(hist.allBins[i].range);hist.allBins[i].fromDict(binDict);}}}
+if(dict.running){hist.running_=tr.b.math.RunningStatistics.fromDict(dict.running);}
+if(dict.summaryOptions){hist.customizeSummaryOptions(dict.summaryOptions);}
+if(dict.maxNumSampleValues!==undefined){hist.maxNumSampleValues=dict.maxNumSampleValues;}
+if(dict.sampleValues){hist.sampleValues_=dict.sampleValues;}
+if(dict.numNans){hist.numNans=dict.numNans;}
+if(dict.nanDiagnostics){for(let map of dict.nanDiagnostics){hist.nanDiagnosticMaps.push(tr.v.d.DiagnosticMap.fromDict(map));}}
+return hist;}
+get numValues(){return this.running_?this.running_.count:0;}
+get average(){return this.running_?this.running_.mean:undefined;}
+get standardDeviation(){return this.running_?this.running_.stddev:undefined;}
+get geometricMean(){return this.running_?this.running_.geometricMean:0;}
+get sum(){return this.running_?this.running_.sum:0;}
+get min(){return this.running_?this.running_.min:Infinity;}
+get max(){return this.running_?this.running_.max:-Infinity;}
+getDifferenceSignificance(other,opt_alpha){if(this.unit!==other.unit){throw new Error('Cannot compare Histograms with different units');}
+if(this.unit.improvementDirection===tr.b.ImprovementDirection.DONT_CARE){return tr.b.math.Statistics.Significance.DONT_CARE;}
+if(!(other instanceof Histogram)){throw new Error('Unable to compute a p-value');}
+let testResult=tr.b.math.Statistics.mwu(this.sampleValues,other.sampleValues,opt_alpha);return testResult.significance;}
+getApproximatePercentile(percent){if(percent<0||percent>1){throw new Error('percent must be in [0,1]');}
+if(this.numValues===0){return 0;}
+if(this.allBins.length===1){let sortedSampleValues=this.sampleValues.slice().sort((x,y)=>x-y);return sortedSampleValues[Math.floor((sortedSampleValues.length-1)*percent)];}
+let valuesToSkip=Math.floor((this.numValues-1)*percent);for(let bin of this.allBins){valuesToSkip-=bin.count;if(valuesToSkip>=0)continue;if(bin.range.min===-Number.MAX_VALUE){return bin.range.max;}
+if(bin.range.max===Number.MAX_VALUE){return bin.range.min;}
+return bin.range.center;}
+return this.allBins[this.allBins.length-1].range.min;}
+getBinIndexForValue(value){const i=tr.b.math.findHighIndexInSortedArray(this.allBins,b=>((value<b.range.max)?-1:1));if(0<=i&&i<this.allBins.length)return i;return this.allBins.length-1;}
+getBinForValue(value){return this.allBins[this.getBinIndexForValue(value)];}
+addSample(value,opt_diagnostics){if(opt_diagnostics&&!(opt_diagnostics instanceof tr.v.d.DiagnosticMap)){opt_diagnostics=tr.v.d.DiagnosticMap.fromObject(opt_diagnostics);}
+if(typeof(value)!=='number'||isNaN(value)){this.numNans++;if(opt_diagnostics){tr.b.math.Statistics.uniformlySampleStream(this.nanDiagnosticMaps,this.numNans,opt_diagnostics,MAX_DIAGNOSTIC_MAPS);}}else{if(this.running_===undefined){this.running_=new tr.b.math.RunningStatistics();}
+this.running_.add(value);const binIndex=this.getBinIndexForValue(value);let bin=this.allBins[binIndex];if(bin.count===0){bin=new HistogramBin(bin.range);this.allBins[binIndex]=bin;}
+bin.addSample(value);if(opt_diagnostics){bin.addDiagnosticMap(opt_diagnostics);}}
+tr.b.math.Statistics.uniformlySampleStream(this.sampleValues_,this.numValues+this.numNans,value,this.maxNumSampleValues);}
+sampleValuesInto(samples){for(let sampleValue of this.sampleValues){samples.push(sampleValue);}}
+canAddHistogram(other){if(this.unit!==other.unit){return false;}
+if(this.binBoundariesDict_===other.binBoundariesDict_){return true;}
+if(this.binBoundariesDict_.length!==other.binBoundariesDict_.length){return false;}
+for(let i=0;i<this.binBoundariesDict_.length;++i){let slice=this.binBoundariesDict_[i];let otherSlice=other.binBoundariesDict_[i];if(slice instanceof Array){if(!(otherSlice instanceof Array)){return false;}
+if(slice[0]!==otherSlice[0]||!tr.b.math.approximately(slice[1],otherSlice[1])||slice[2]!==otherSlice[2]){return false;}}else{if(otherSlice instanceof Array){return false;}
+if(!tr.b.math.approximately(slice,otherSlice)){return false;}}}
+return true;}
+addHistogram(other){if(!this.canAddHistogram(other)){throw new Error('Merging incompatible Histograms');}
+tr.b.math.Statistics.mergeSampledStreams(this.nanDiagnosticMaps,this.numNans,other.nanDiagnosticMaps,other.numNans,MAX_DIAGNOSTIC_MAPS);tr.b.math.Statistics.mergeSampledStreams(this.sampleValues,this.numValues+this.numNans,other.sampleValues,other.numValues+other.numNans,(this.maxNumSampleValues+other.maxNumSampleValues)/2);this.numNans+=other.numNans;if(other.running_!==undefined){if(this.running_===undefined){this.running_=new tr.b.math.RunningStatistics();}
+this.running_=this.running_.merge(other.running_);}
+for(let i=0;i<this.allBins.length;++i){this.allBins[i].addBin(other.allBins[i]);}
+let mergedFrom=this.diagnostics.get(tr.v.d.MERGED_FROM_DIAGNOSTIC_KEY);if(!mergedFrom){mergedFrom=new tr.v.d.RelatedHistogramSet();this.diagnostics.set(tr.v.d.MERGED_FROM_DIAGNOSTIC_KEY,mergedFrom);}
+mergedFrom.add(other);let mergedTo=other.diagnostics.get(tr.v.d.MERGED_TO_DIAGNOSTIC_KEY);if(!mergedTo){mergedTo=new tr.v.d.RelatedHistogramSet();other.diagnostics.set(tr.v.d.MERGED_TO_DIAGNOSTIC_KEY,mergedTo);}
+mergedTo.add(this);this.diagnostics.addDiagnostics(other.diagnostics);for(let[stat,option]of other.summaryOptions){if(stat==='percentile'){for(let percent of option){let percentiles=this.summaryOptions.get(stat);if(percentiles.indexOf(percent)<0){percentiles.push(percent);}}}else if(option&&!this.summaryOptions.get(stat)){this.summaryOptions.set(stat,true);}}}
+customizeSummaryOptions(summaryOptions){for(var[key,value]of Object.entries(summaryOptions)){this.summaryOptions.set(key,value);}}
+getStatisticScalar(statName,opt_referenceHistogram,opt_mwu){if(statName==='avg'){if(this.running_===undefined)return undefined;return new tr.b.Scalar(this.unit,this.average);}
+if(statName==='std'){if(this.standardDeviation===undefined)return undefined;return new tr.b.Scalar(this.unit,this.standardDeviation);}
+if(statName==='geometricMean'){return new tr.b.Scalar(this.unit,this.geometricMean);}
+if(statName==='min'||statName==='max'||statName==='sum'){if(this.running_===undefined){this.running_=new tr.b.math.RunningStatistics();}
+return new tr.b.Scalar(this.unit,this.running_[statName]);}
+if(statName==='nans'){return new tr.b.Scalar(tr.b.Unit.byName.count_smallerIsBetter,this.numNans);}
+if(statName==='count'){return new tr.b.Scalar(tr.b.Unit.byName.count_smallerIsBetter,this.numValues);}
+if(statName.substr(0,4)==='pct_'){let percent=percentFromString(statName.substr(4));let percentile=this.getApproximatePercentile(percent);return new tr.b.Scalar(this.unit,percentile);}
+if(!this.canCompare(opt_referenceHistogram)){throw new Error('Cannot compute '+statName+' when histograms are not comparable');}
+const suffix=tr.b.Unit.nameSuffixForImprovementDirection(this.unit.improvementDirection);const deltaIndex=statName.indexOf(DELTA);if(deltaIndex>=0){const baseStatName=statName.substr(deltaIndex+1);const thisStat=this.getStatisticScalar(baseStatName);const otherStat=opt_referenceHistogram.getStatisticScalar(baseStatName);const deltaValue=thisStat.value-otherStat.value;if(statName[0]==='%'){return new tr.b.Scalar(tr.b.Unit.byName['normalizedPercentageDelta'+suffix],deltaValue/otherStat.value);}
+return new tr.b.Scalar(thisStat.unit.correspondingDeltaUnit,deltaValue);}
+if(statName===Z_SCORE_NAME){return new tr.b.Scalar(tr.b.Unit.byName['sigmaDelta'+suffix],(this.average-opt_referenceHistogram.average)/opt_referenceHistogram.standardDeviation);}
+let mwu=opt_mwu||tr.b.math.Statistics.mwu(this.sampleValues,opt_referenceHistogram.sampleValues);if(statName===P_VALUE_NAME){return new tr.b.Scalar(tr.b.Unit.byName.unitlessNumber,mwu.p);}
+if(statName===U_STATISTIC_NAME){return new tr.b.Scalar(tr.b.Unit.byName.unitlessNumber,mwu.U);}
+throw new Error('Unrecognized statistic name: '+statName);}
+get statisticsNames(){let statisticsNames=new Set();for(let[statName,option]of this.summaryOptions){if(statName==='percentile'){for(let pctile of option){statisticsNames.add('pct_'+tr.v.percentToString(pctile));}}else if(option){statisticsNames.add(statName);}}
+return statisticsNames;}
+canCompare(other){return other instanceof Histogram&&this.unit===other.unit&&this.numValues>0&&other.numValues>0;}
+getAvailableStatisticName(statName,opt_referenceHist){if(this.canCompare(opt_referenceHist))return statName;if(statName===Z_SCORE_NAME||statName===P_VALUE_NAME||statName===U_STATISTIC_NAME){return'avg';}
+const deltaIndex=statName.indexOf(DELTA);if(deltaIndex<0)return statName;return statName.substr(deltaIndex+1);}
+static getDeltaStatisticsNames(statNames){const deltaNames=[];for(const statName of statNames){deltaNames.push(`${DELTA}${statName}`);deltaNames.push(`%${DELTA}${statName}`);}
+return deltaNames.concat([Z_SCORE_NAME,P_VALUE_NAME,U_STATISTIC_NAME]);}
+get statisticsScalars(){let results=new Map();for(let statName of this.statisticsNames){let scalar=this.getStatisticScalar(statName);if(scalar===undefined)continue;results.set(statName,scalar);}
+return results;}
 get sampleValues(){return this.sampleValues_;}
-get binBoundaries(){var boundaries=[];for(var bin of this.centralBins)
-boundaries.push(bin.range.min);boundaries.push(this.overflowBin.range.min);return boundaries;}
-clone(){return Histogram.fromDict(this.asDict());}
-asDict(){return{name:this.name,guid:this.guid,shortName:this.shortName,description:this.description,diagnostics:this.diagnostics.asDict(),unit:this.unit.asJSON(),binBoundaries:this.binBoundaries,underflowBin:this.underflowBin.asDict(),centralBins:this.centralBins.map(bin=>bin.asDict()),overflowBin:this.overflowBin.asDict(),running:this.running.asDict(),summaryOptions:this.summaryOptions,maxNumSampleValues:this.maxNumSampleValues,sampleValues:this.sampleValues,numNans:this.numNans,nanDiagnosticMaps:this.nanDiagnosticMaps.map(dm=>dm.asDict()),};}}
-class HistogramBinBoundaries{static createLinear(min,max,numBins){return new HistogramBinBoundaries(min).addLinearBins(max,numBins);}
+clone(){let binBoundaries=HistogramBinBoundaries.fromDict(this.binBoundariesDict_);let hist=new Histogram(this.name,this.unit,binBoundaries);for(let[stat,option]of this.summaryOptions){if(stat==='percentile'){option=Array.from(option);}
+hist.summaryOptions.set(stat,option);}
+hist.addHistogram(this);return hist;}
+rebin(newBoundaries){const rebinned=new tr.v.Histogram(this.name,this.unit,newBoundaries);rebinned.description=this.description;for(const sample of this.sampleValues){rebinned.addSample(sample);}
+rebinned.running_=this.running_;for(const[name,diagnostic]of this.diagnostics){rebinned.diagnostics.set(name,diagnostic);}
+for(let[stat,option]of this.summaryOptions){if(stat==='percentile')option=Array.from(option);rebinned.summaryOptions.set(stat,option);}
+return rebinned;}
+asDict(){let dict={};dict.name=this.name;dict.unit=this.unit.asJSON();dict.guid=this.guid;if(this.binBoundariesDict_!==undefined){dict.binBoundaries=this.binBoundariesDict_;}
+if(this.shortName){dict.shortName=this.shortName;}
+if(this.description){dict.description=this.description;}
+if(this.diagnostics.size){dict.diagnostics=this.diagnostics.asDict();}
+if(this.maxNumSampleValues!==this.defaultMaxNumSampleValues_){dict.maxNumSampleValues=this.maxNumSampleValues;}
+if(this.numNans){dict.numNans=this.numNans;}
+if(this.nanDiagnosticMaps.length){dict.nanDiagnostics=this.nanDiagnosticMaps.map(dm=>dm.asDict());}
+if(this.numValues){dict.sampleValues=this.sampleValues.slice();dict.running=this.running_.asDict();dict.allBins=this.allBinsAsDict_();}
+let summaryOptions={};let anyOverriddenSummaryOptions=false;for(let[name,option]of this.summaryOptions){if(name==='percentile'){if(option.length===0){continue;}
+option=option.slice();}else if(option===DEFAULT_SUMMARY_OPTIONS.get(name)){continue;}
+summaryOptions[name]=option;anyOverriddenSummaryOptions=true;}
+if(anyOverriddenSummaryOptions){dict.summaryOptions=summaryOptions;}
+return dict;}
+allBinsAsDict_(){let numBins=this.allBins.length;let emptyBins=0;for(let i=0;i<numBins;++i){if(this.allBins[i].count===0){++emptyBins;}}
+if(emptyBins===numBins){return undefined;}
+if(emptyBins>(numBins/2)){let allBinsDict={};for(let i=0;i<numBins;++i){let bin=this.allBins[i];if(bin.count>0){allBinsDict[i]=bin.asDict();}}
+return allBinsDict;}
+let allBinsArray=[];for(let i=0;i<numBins;++i){allBinsArray.push(this.allBins[i].asDict());}
+return allBinsArray;}
+get defaultMaxNumSampleValues_(){return DEFAULT_SAMPLE_VALUES_PER_BIN*Math.max(this.allBins.length,DEFAULT_REBINNED_COUNT);}}
+const HISTOGRAM_BIN_BOUNDARIES_CACHE=new Map();class HistogramBinBoundaries{static createLinear(min,max,numBins){return new HistogramBinBoundaries(min).addLinearBins(max,numBins);}
 static createExponential(min,max,numBins){return new HistogramBinBoundaries(min).addExponentialBins(max,numBins);}
-static createWithBoundaries(binBoundaries){var builder=new HistogramBinBoundaries(binBoundaries[0]);for(var boundary of binBoundaries.slice(1))
-builder.addBinBoundary(boundary);return builder;}
-static createFromSamples(samples){var range=new tr.b.Range();for(var sample of samples)
-if(!isNaN(Math.max(sample)))
-range.addValue(sample);if(range.isEmpty)
-range.addValue(1);if(range.min===range.max)
-range.addValue(range.min-1);var numBins=Math.ceil(Math.sqrt(samples.length));var builder=new HistogramBinBoundaries(range.min);builder.addLinearBins(range.max,numBins);return builder;}
-constructor(minBinBoundary){this.boundaries_=[minBinBoundary];}
-get minBinBoundary(){return this.boundaries_[0];}
-get maxBinBoundary(){return this.boundaries_[this.boundaries_.length-1];}*[Symbol.iterator](){for(var i=0;i<this.boundaries_.length-1;++i){yield tr.b.Range.fromExplicitRange(this.boundaries_[i],this.boundaries_[i+1]);}}
-addBinBoundary(nextMaxBinBoundary){if(nextMaxBinBoundary<=this.maxBinBoundary){throw new Error('The added max bin boundary must be larger than '+'the current max boundary');}
-this.boundaries_.push(nextMaxBinBoundary);return this;}
-addLinearBins(nextMaxBinBoundary,binCount){if(binCount<=0)
-throw new Error('Bin count must be positive');var curMaxBinBoundary=this.maxBinBoundary;if(curMaxBinBoundary>=nextMaxBinBoundary){throw new Error('The new max bin boundary must be greater than '+'the previous max bin boundary');}
-var binWidth=(nextMaxBinBoundary-curMaxBinBoundary)/binCount;for(var i=1;i<binCount;i++)
-this.addBinBoundary(curMaxBinBoundary+i*binWidth);this.addBinBoundary(nextMaxBinBoundary);return this;}
-addExponentialBins(nextMaxBinBoundary,binCount){if(binCount<=0)
-throw new Error('Bin count must be positive');var curMaxBinBoundary=this.maxBinBoundary;if(curMaxBinBoundary<=0)
-throw new Error('Current max bin boundary must be positive');if(curMaxBinBoundary>=nextMaxBinBoundary){throw new Error('The last added max boundary must be greater than '+'the current max boundary boundary');}
-var binExponentWidth=Math.log(nextMaxBinBoundary/curMaxBinBoundary)/binCount;for(var i=1;i<binCount;i++){this.addBinBoundary(curMaxBinBoundary*Math.exp(i*binExponentWidth));}
-this.addBinBoundary(nextMaxBinBoundary);return this;}}
-DEFAULT_BOUNDARIES_FOR_UNIT.set(tr.b.Unit.byName.timeDurationInMs.unitName,HistogramBinBoundaries.createExponential(1e-3,1e6,1e2));DEFAULT_BOUNDARIES_FOR_UNIT.set(tr.b.Unit.byName.timeStampInMs.unitName,HistogramBinBoundaries.createLinear(0,1e10,1e3));DEFAULT_BOUNDARIES_FOR_UNIT.set(tr.b.Unit.byName.normalizedPercentage.unitName,HistogramBinBoundaries.createLinear(0,1.0,20));DEFAULT_BOUNDARIES_FOR_UNIT.set(tr.b.Unit.byName.sizeInBytes.unitName,HistogramBinBoundaries.createExponential(1,1e12,1e2));DEFAULT_BOUNDARIES_FOR_UNIT.set(tr.b.Unit.byName.energyInJoules.unitName,HistogramBinBoundaries.createExponential(1e-3,1e3,50));DEFAULT_BOUNDARIES_FOR_UNIT.set(tr.b.Unit.byName.powerInWatts.unitName,HistogramBinBoundaries.createExponential(1e-3,1,50));DEFAULT_BOUNDARIES_FOR_UNIT.set(tr.b.Unit.byName.unitlessNumber.unitName,HistogramBinBoundaries.createExponential(1e-3,1e3,50));DEFAULT_BOUNDARIES_FOR_UNIT.set(tr.b.Unit.byName.count.unitName,HistogramBinBoundaries.createExponential(1,1e3,20));return{Significance:Significance,Histogram:Histogram,HistogramBinBoundaries:HistogramBinBoundaries,};});'use strict';tr.exportTo('tr.v.ui',function(){Polymer({is:'tr-v-ui-scalar-context-controller',created:function(){this.host_=undefined;this.groupToContext_=new Map();this.dirtyGroups_=new Set();},attached:function(){if(this.host_){throw new Error('Scalar context controller is already attached to a host');}
-var host=findParentOrHost(this);if(host.__scalarContextController){throw new Error('Multiple scalar context controllers attached to this host');}
-host.__scalarContextController=this;this.host_=host;},detached:function(){if(!this.host_)
-throw new Error('Scalar context controller is not attached to a host');if(this.host_.__scalarContextController!==this){throw new Error('Scalar context controller is not attached to its host');}
-delete this.host_.__scalarContextController;this.host_=undefined;},getContext:function(group){return this.groupToContext_.get(group);},onScalarSpanAdded:function(group,span){var context=this.groupToContext_.get(group);if(context===undefined){context={spans:new Set(),range:new tr.b.Range()};this.groupToContext_.set(group,context);}
-if(context.spans.has(span))
-throw new Error('Scalar span already registered with group: '+group);context.spans.add(span);this._markGroupDirtyAndScheduleUpdate(group);},onScalarSpanRemoved:function(group,span){var context=this.groupToContext_.get(group);if(!context.spans.has(span))
-throw new Error('Scalar span not registered with group: '+group);context.spans.delete(span);this._markGroupDirtyAndScheduleUpdate(group);},onScalarSpanUpdated:function(group,span){var context=this.groupToContext_.get(group);if(!context.spans.has(span))
-throw new Error('Scalar span not registered with group: '+group);this._markGroupDirtyAndScheduleUpdate(group);},_markGroupDirtyAndScheduleUpdate:function(group){var alreadyDirty=this.dirtyGroups_.size>0;this.dirtyGroups_.add(group);if(!alreadyDirty)
-tr.b.requestAnimationFrame(this.updateContext.bind(this));},updateContext:function(){var groups=this.dirtyGroups_;if(groups.size===0)
-return;this.dirtyGroups_=new Set();for(var group of groups)
-this.updateGroup_(group);var event=new tr.b.Event('context-updated');event.groups=groups;this.dispatchEvent(event);},updateGroup_:function(group){var context=this.groupToContext_.get(group);if(context.spans.size===0){this.groupToContext_.delete(group);return;}
-context.range.reset();for(var span of context.spans)
-context.range.addValue(span.value);}});function getScalarContextControllerForElement(element){while(element){if(element.__scalarContextController)
-return element.__scalarContextController;element=findParentOrHost(element);}
+static createWithBoundaries(binBoundaries){let builder=new HistogramBinBoundaries(binBoundaries[0]);for(let boundary of binBoundaries.slice(1)){builder.addBinBoundary(boundary);}
+return builder;}
+constructor(minBinBoundary){this.builder_=[minBinBoundary];this.range_=new tr.b.math.Range();this.range_.addValue(minBinBoundary);this.binRanges_=undefined;this.bins_=undefined;}
+get range(){return this.range_;}
+asDict(){if(this.builder_.length===1&&this.builder_[0]===Number.MAX_VALUE){return undefined;}
+return this.builder_;}
+pushBuilderSlice_(slice){this.builder_.push(slice);this.builder_=this.builder_.slice();}
+static fromDict(dict){if(dict===undefined){return HistogramBinBoundaries.SINGULAR;}
+let cacheKey=JSON.stringify(dict);if(HISTOGRAM_BIN_BOUNDARIES_CACHE.has(cacheKey)){return HISTOGRAM_BIN_BOUNDARIES_CACHE.get(cacheKey);}
+let binBoundaries=new HistogramBinBoundaries(dict[0]);for(let slice of dict.slice(1)){if(!(slice instanceof Array)){binBoundaries.addBinBoundary(slice);continue;}
+switch(slice[0]){case HistogramBinBoundaries.SLICE_TYPE.LINEAR:binBoundaries.addLinearBins(slice[1],slice[2]);break;case HistogramBinBoundaries.SLICE_TYPE.EXPONENTIAL:binBoundaries.addExponentialBins(slice[1],slice[2]);break;default:throw new Error('Unrecognized HistogramBinBoundaries slice type');}}
+HISTOGRAM_BIN_BOUNDARIES_CACHE.set(cacheKey,binBoundaries);return binBoundaries;}
+get bins(){if(this.bins_===undefined){this.buildBins_();}
+return this.bins_;}
+buildBins_(){this.bins_=this.binRanges.map(r=>new HistogramBin(r));}
+get binRanges(){if(this.binRanges_===undefined){this.buildBinRanges_();}
+return this.binRanges_;}
+buildBinRanges_(){if(typeof this.builder_[0]!=='number'){throw new Error('Invalid start of builder_');}
+this.binRanges_=[];let prevBoundary=this.builder_[0];if(prevBoundary>-Number.MAX_VALUE){this.binRanges_.push(tr.b.math.Range.fromExplicitRange(-Number.MAX_VALUE,prevBoundary));}
+for(let slice of this.builder_.slice(1)){if(!(slice instanceof Array)){this.binRanges_.push(tr.b.math.Range.fromExplicitRange(prevBoundary,slice));prevBoundary=slice;continue;}
+let nextMaxBinBoundary=slice[1];let binCount=slice[2];let sliceMinBinBoundary=prevBoundary;switch(slice[0]){case HistogramBinBoundaries.SLICE_TYPE.LINEAR:{let binWidth=(nextMaxBinBoundary-prevBoundary)/binCount;for(let i=1;i<binCount;i++){let boundary=sliceMinBinBoundary+i*binWidth;this.binRanges_.push(tr.b.math.Range.fromExplicitRange(prevBoundary,boundary));prevBoundary=boundary;}
+break;}
+case HistogramBinBoundaries.SLICE_TYPE.EXPONENTIAL:{let binExponentWidth=Math.log(nextMaxBinBoundary/prevBoundary)/binCount;for(let i=1;i<binCount;i++){let boundary=sliceMinBinBoundary*Math.exp(i*binExponentWidth);this.binRanges_.push(tr.b.math.Range.fromExplicitRange(prevBoundary,boundary));prevBoundary=boundary;}
+break;}
+default:throw new Error('Unrecognized HistogramBinBoundaries slice type');}
+this.binRanges_.push(tr.b.math.Range.fromExplicitRange(prevBoundary,nextMaxBinBoundary));prevBoundary=nextMaxBinBoundary;}
+if(prevBoundary<Number.MAX_VALUE){this.binRanges_.push(tr.b.math.Range.fromExplicitRange(prevBoundary,Number.MAX_VALUE));}}
+addBinBoundary(nextMaxBinBoundary){if(nextMaxBinBoundary<=this.range.max){throw new Error('The added max bin boundary must be larger than '+'the current max boundary');}
+this.binRanges_=undefined;this.bins_=undefined;this.pushBuilderSlice_(nextMaxBinBoundary);this.range.addValue(nextMaxBinBoundary);return this;}
+addLinearBins(nextMaxBinBoundary,binCount){if(binCount<=0){throw new Error('Bin count must be positive');}
+if(nextMaxBinBoundary<=this.range.max){throw new Error('The new max bin boundary must be greater than '+'the previous max bin boundary');}
+this.binRanges_=undefined;this.bins_=undefined;this.pushBuilderSlice_([HistogramBinBoundaries.SLICE_TYPE.LINEAR,nextMaxBinBoundary,binCount]);this.range.addValue(nextMaxBinBoundary);return this;}
+addExponentialBins(nextMaxBinBoundary,binCount){if(binCount<=0){throw new Error('Bin count must be positive');}
+if(this.range.max<=0){throw new Error('Current max bin boundary must be positive');}
+if(this.range.max>=nextMaxBinBoundary){throw new Error('The last added max boundary must be greater than '+'the current max boundary boundary');}
+this.binRanges_=undefined;this.bins_=undefined;this.pushBuilderSlice_([HistogramBinBoundaries.SLICE_TYPE.EXPONENTIAL,nextMaxBinBoundary,binCount]);this.range.addValue(nextMaxBinBoundary);return this;}}
+HistogramBinBoundaries.SLICE_TYPE={LINEAR:0,EXPONENTIAL:1,};HistogramBinBoundaries.SINGULAR=new HistogramBinBoundaries(Number.MAX_VALUE);DEFAULT_BOUNDARIES_FOR_UNIT.set(tr.b.Unit.byName.timeDurationInMs.unitName,HistogramBinBoundaries.createExponential(1e-3,1e6,1e2));DEFAULT_BOUNDARIES_FOR_UNIT.set(tr.b.Unit.byName.timeStampInMs.unitName,HistogramBinBoundaries.createLinear(0,1e10,1e3));DEFAULT_BOUNDARIES_FOR_UNIT.set(tr.b.Unit.byName.normalizedPercentage.unitName,HistogramBinBoundaries.createLinear(0,1.0,20));DEFAULT_BOUNDARIES_FOR_UNIT.set(tr.b.Unit.byName.sizeInBytes.unitName,HistogramBinBoundaries.createExponential(1,1e12,1e2));DEFAULT_BOUNDARIES_FOR_UNIT.set(tr.b.Unit.byName.energyInJoules.unitName,HistogramBinBoundaries.createExponential(1e-3,1e3,50));DEFAULT_BOUNDARIES_FOR_UNIT.set(tr.b.Unit.byName.powerInWatts.unitName,HistogramBinBoundaries.createExponential(1e-3,1,50));DEFAULT_BOUNDARIES_FOR_UNIT.set(tr.b.Unit.byName.unitlessNumber.unitName,HistogramBinBoundaries.createExponential(1e-3,1e3,50));DEFAULT_BOUNDARIES_FOR_UNIT.set(tr.b.Unit.byName.count.unitName,HistogramBinBoundaries.createExponential(1,1e3,20));DEFAULT_BOUNDARIES_FOR_UNIT.set(tr.b.Unit.byName.sigma.unitName,HistogramBinBoundaries.createLinear(-5,5,50));return{DEFAULT_REBINNED_COUNT,DELTA,Histogram,HistogramBinBoundaries,P_VALUE_NAME,U_STATISTIC_NAME,Z_SCORE_NAME,percentFromString,percentToString,};});'use strict';tr.exportTo('tr.v.ui',function(){Polymer({is:'tr-v-ui-scalar-context-controller',created:function(){this.host_=undefined;this.groupToContext_=new Map();this.dirtyGroups_=new Set();},attached:function(){if(this.host_){throw new Error('Scalar context controller is already attached to a host');}
+let host=findParentOrHost(this);if(host.__scalarContextController){throw new Error('Multiple scalar context controllers attached to this host');}
+host.__scalarContextController=this;this.host_=host;},detached:function(){if(!this.host_){throw new Error('Scalar context controller is not attached to a host');}
+if(this.host_.__scalarContextController!==this){throw new Error('Scalar context controller is not attached to its host');}
+delete this.host_.__scalarContextController;this.host_=undefined;},getContext:function(group){return this.groupToContext_.get(group);},onScalarSpanAdded:function(group,span){let context=this.groupToContext_.get(group);if(context===undefined){context={spans:new Set(),range:new tr.b.math.Range()};this.groupToContext_.set(group,context);}
+if(context.spans.has(span)){throw new Error('Scalar span already registered with group: '+group);}
+context.spans.add(span);this._markGroupDirtyAndScheduleUpdate(group);},onScalarSpanRemoved:function(group,span){let context=this.groupToContext_.get(group);if(!context.spans.has(span)){throw new Error('Scalar span not registered with group: '+group);}
+context.spans.delete(span);this._markGroupDirtyAndScheduleUpdate(group);},onScalarSpanUpdated:function(group,span){let context=this.groupToContext_.get(group);if(!context.spans.has(span)){throw new Error('Scalar span not registered with group: '+group);}
+this._markGroupDirtyAndScheduleUpdate(group);},_markGroupDirtyAndScheduleUpdate:function(group){let alreadyDirty=this.dirtyGroups_.size>0;this.dirtyGroups_.add(group);if(!alreadyDirty){tr.b.requestAnimationFrameInThisFrameIfPossible(this.updateContext,this);}},updateContext:function(){let groups=this.dirtyGroups_;if(groups.size===0)return;this.dirtyGroups_=new Set();for(let group of groups){this.updateGroup_(group);}
+let event=new tr.b.Event('context-updated');event.groups=groups;this.dispatchEvent(event);},updateGroup_:function(group){let context=this.groupToContext_.get(group);if(context.spans.size===0){this.groupToContext_.delete(group);return;}
+context.range.reset();for(let span of context.spans){context.range.addValue(span.value);}}});function getScalarContextControllerForElement(element){while(element){if(element.__scalarContextController){return element.__scalarContextController;}
+element=findParentOrHost(element);}
 return undefined;}
-function findParentOrHost(node){if(node.parentElement)
-return node.parentElement;while(Polymer.dom(node).parentNode)
-node=Polymer.dom(node).parentNode;return node.host;}
-return{getScalarContextControllerForElement:getScalarContextControllerForElement};});'use strict';tr.exportTo('tr.v.ui',function(){var emojiPrefix=String.fromCharCode(55357);var Emoji={GRINNING_FACE:emojiPrefix+String.fromCharCode(56835),NEUTRAL_FACE:emojiPrefix+String.fromCharCode(56848),CONFOUNDED_FACE:emojiPrefix+String.fromCharCode(56854)};function createScalarSpan(value,opt_config){if(value===undefined)
-return'';var config=opt_config||{};var ownerDocument=config.ownerDocument||document;var span=ownerDocument.createElement('tr-v-ui-scalar-span');var numericValue;if(value instanceof tr.v.ScalarNumeric){span.value=value;numericValue=value.value;}else if(value instanceof tr.v.Histogram){numericValue=value.average;if(numericValue===undefined)
-return'';span.setValueAndUnit(numericValue,value.unit);}else{var unit=config.unit;if(unit===undefined){throw new Error('Unit must be provided in config when value is a number');}
+function findParentOrHost(node){if(node.parentElement){return node.parentElement;}
+while(Polymer.dom(node).parentNode){node=Polymer.dom(node).parentNode;}
+return node.host;}
+return{getScalarContextControllerForElement,};});'use strict';tr.exportTo('tr.v.ui',function(){function createScalarSpan(value,opt_config){if(value===undefined)return'';let config=opt_config||{};let ownerDocument=config.ownerDocument||document;let span=ownerDocument.createElement('tr-v-ui-scalar-span');let numericValue;if(value instanceof tr.b.Scalar){span.value=value;numericValue=value.value;}else if(value instanceof tr.v.Histogram){numericValue=value.average;if(numericValue===undefined)return'';span.setValueAndUnit(numericValue,value.unit);}else{let unit=config.unit;if(unit===undefined){throw new Error('Unit must be provided in config when value is a number');}
 span.setValueAndUnit(value,unit);numericValue=value;}
-if(config.context)
-span.context=config.context;if(config.customContextRange)
-span.customContextRange=config.customContextRange;if(config.rightAlign)
-span.rightAlign=true;if(config.significance!==undefined)
-span.significance=config.significance;if(config.contextGroup!==undefined)
-span.contextGroup=config.contextGroup;return span;}
-return{Emoji:Emoji,createScalarSpan:createScalarSpan};});'use strict';Polymer({is:'tr-v-ui-scalar-span',properties:{contextGroup:{type:String,reflectToAttribute:true,observer:'contextGroupChanged_'}},created:function(){this.value_=undefined;this.unit_=undefined;this.context_=undefined;this.warning_=undefined;this.significance_=tr.v.Significance.DONT_CARE;this.shouldSearchForContextController_=false;this.lazyContextController_=undefined;this.onContextUpdated_=this.onContextUpdated_.bind(this);this.customContextRange_=undefined;},get significance(){return this.significance_;},set significance(s){this.significance_=s;this.updateContents_();},set contentTextDecoration(deco){this.$.content.style.textDecoration=deco;},get value(){return this.value_;},set value(value){if(value instanceof tr.v.ScalarNumeric){this.value_=value.value;this.unit_=value.unit;}else{this.value_=value;}
-this.updateContents_();if(this.hasContext_(this.contextGroup))
-this.contextController_.onScalarSpanUpdated(this.contextGroup,this);else
-this.updateSparkline_();},get contextController_(){if(this.shouldSearchForContextController_){this.lazyContextController_=tr.v.ui.getScalarContextControllerForElement(this);this.shouldSearchForContextController_=false;}
-return this.lazyContextController_;},hasContext_:function(contextGroup){return!!(contextGroup&&this.contextController_);},contextGroupChanged_:function(newContextGroup,oldContextGroup){this.detachFromContextControllerIfPossible_(oldContextGroup);if(!this.attachToContextControllerIfPossible_(newContextGroup)){this.onContextUpdated_();}},attachToContextControllerIfPossible_:function(contextGroup){if(!this.hasContext_(contextGroup))
-return false;this.contextController_.addEventListener('context-updated',this.onContextUpdated_);this.contextController_.onScalarSpanAdded(contextGroup,this);return true;},detachFromContextControllerIfPossible_:function(contextGroup){if(!this.hasContext_(contextGroup))
-return;this.contextController_.removeEventListener('context-updated',this.onContextUpdated_);this.contextController_.onScalarSpanRemoved(contextGroup,this);},attached:function(){tr.b.Unit.addEventListener('display-mode-changed',this.updateContents_.bind(this));this.shouldSearchForContextController_=true;this.attachToContextControllerIfPossible_(this.contextGroup);},detached:function(){tr.b.Unit.removeEventListener('display-mode-changed',this.updateContents_.bind(this));this.detachFromContextControllerIfPossible_(this.contextGroup);this.shouldSearchForContextController_=false;this.lazyContextController_=undefined;},onContextUpdated_:function(){this.updateSparkline_();},get context(){return this.context_;},set context(context){this.context_=context;this.updateContents_();},get unit(){return this.unit_;},set unit(unit){this.unit_=unit;this.updateContents_();this.updateSparkline_();},setValueAndUnit:function(value,unit){this.value_=value;this.unit_=unit;this.updateContents_();},get customContextRange(){return this.customContextRange_;},set customContextRange(customContextRange){this.customContextRange_=customContextRange;this.updateSparkline_();},get rightAlign(){return Polymer.dom(this.$.content).classList.contains('right-align');},set rightAlign(rightAlign){if(rightAlign)
-Polymer.dom(this.$.content).classList.add('right-align');else
-Polymer.dom(this.$.content).classList.remove('right-align');},updateSparkline_:function(){Polymer.dom(this.$.sparkline).classList.remove('positive');Polymer.dom(this.$.sparkline).classList.remove('better');Polymer.dom(this.$.sparkline).classList.remove('worse');Polymer.dom(this.$.sparkline).classList.remove('same');this.$.sparkline.style.display='none';this.$.sparkline.style.left='0';this.$.sparkline.style.width='0';var range=this.customContextRange_;if(!range&&this.hasContext_(this.contextGroup)){var context=this.contextController_.getContext(this.contextGroup);if(context)
-range=context.range;}
-if(!range||range.isEmpty)
-return;var leftPoint=Math.min(range.min,0);var rightPoint=Math.max(range.max,0);var pointDistance=rightPoint-leftPoint;if(pointDistance===0){return;}
-this.$.sparkline.style.display='block';var left,width;if(this.value>0){width=Math.min(this.value,rightPoint);left=-leftPoint;Polymer.dom(this.$.sparkline).classList.add('positive');}else if(this.value<=0){width=-Math.max(this.value,leftPoint);left=(-leftPoint)-width;}
-this.$.sparkline.style.left=this.buildSparklineStyle_(left/pointDistance,false);this.$.sparkline.style.width=this.buildSparklineStyle_(width/pointDistance,true);var changeClass=this.changeClassName_;if(changeClass)
-Polymer.dom(this.$.sparkline).classList.add(changeClass);},buildSparklineStyle_:function(ratio,isWidth){var position='calc('+ratio+' * (100% - 1px)';if(isWidth)
-position+=' + 1px';position+=')';return position;},updateContents_:function(){Polymer.dom(this.$.significance).textContent='';Polymer.dom(this.$.content).textContent='';Polymer.dom(this.$.content).classList.remove('better');Polymer.dom(this.$.content).classList.remove('worse');Polymer.dom(this.$.content).classList.remove('same');Polymer.dom(this.$.significance).classList.remove('better');Polymer.dom(this.$.significance).classList.remove('worse');Polymer.dom(this.$.significance).classList.remove('same');if(this.unit_===undefined)
-return;this.$.content.title='';Polymer.dom(this.$.content).textContent=this.unit_.format(this.value,this.context);this.updateDelta_();},updateDelta_:function(){if(!this.unit_.isDelta){this.$.significance.style.display='none';return;}
-this.$.significance.style.display='';var changeClass=this.changeClassName_;if(!changeClass)
-return;var emoji,title;switch(changeClass){case'better':emoji=tr.v.ui.Emoji.GRINNING_FACE;title='improvement';break;case'worse':emoji=tr.v.ui.Emoji.CONFOUNDED_FACE;title='regression';break;case'same':emoji=tr.v.ui.Emoji.NEUTRAL_FACE;title='no change';break;default:throw new Error('Unknown change class: '+changeClass);}
-Polymer.dom(this.$.content).classList.add(changeClass);switch(this.significance){case tr.v.Significance.DONT_CARE:emoji='';changeClass='same';break;case tr.v.Significance.INSIGNIFICANT:if(changeClass!=='same')
-title='insignificant '+title;emoji=tr.v.ui.Emoji.NEUTRAL_FACE;changeClass='same';break;case tr.v.Significance.SIGNIFICANT:if(changeClass==='same')
-throw new Error('How can no change be significant?');title='significant '+title;break;}
-Polymer.dom(this.$.significance).textContent=emoji;this.$.significance.title=title;this.$.content.title=title;Polymer.dom(this.$.significance).classList.add(changeClass);},get changeClassName_(){if(!this.unit_||!this.unit_.isDelta)
-return undefined;switch(this.unit_.improvementDirection){case tr.b.ImprovementDirection.DONT_CARE:return undefined;case tr.b.ImprovementDirection.BIGGER_IS_BETTER:if(this.value===0)
-return'same';return this.value>0?'better':'worse';case tr.b.ImprovementDirection.SMALLER_IS_BETTER:if(this.value===0)
-return'same';return this.value<0?'better':'worse';default:throw new Error('Unknown improvement direction: '+
-this.unit_.improvementDirection);}},get warning(){return this.warning_;},set warning(warning){this.warning_=warning;var warningEl=this.$.warning;if(this.warning_){warningEl.title=warning;warningEl.style.display='';}else{warningEl.title='';warningEl.style.display='none';}},get timestamp(){return this.value;},set timestamp(timestamp){if(timestamp instanceof tr.b.u.TimeStamp){this.value=timestamp;return;}
+if(config.context){span.context=config.context;}
+if(config.customContextRange){span.customContextRange=config.customContextRange;}
+if(config.leftAlign){span.leftAlign=true;}
+if(config.inline){span.inline=true;}
+if(config.significance!==undefined){span.significance=config.significance;}
+if(config.contextGroup!==undefined){span.contextGroup=config.contextGroup;}
+return span;}
+return{createScalarSpan,};});'use strict';Polymer({is:'tr-v-ui-scalar-span',properties:{contextGroup:{type:String,reflectToAttribute:true,observer:'contextGroupChanged_'}},created:function(){this.value_=undefined;this.unit_=undefined;this.context_=undefined;this.warning_=undefined;this.significance_=tr.b.math.Statistics.Significance.DONT_CARE;this.shouldSearchForContextController_=false;this.lazyContextController_=undefined;this.onContextUpdated_=this.onContextUpdated_.bind(this);this.customContextRange_=undefined;},get significance(){return this.significance_;},set significance(s){this.significance_=s;this.updateContents_();},set contentTextDecoration(deco){this.$.content.style.textDecoration=deco;},get value(){return this.value_;},set value(value){if(value instanceof tr.b.Scalar){this.value_=value.value;this.unit_=value.unit;}else{this.value_=value;}
+this.updateContents_();if(this.hasContext_(this.contextGroup)){this.contextController_.onScalarSpanUpdated(this.contextGroup,this);}else{this.updateSparkline_();}},get contextController_(){if(this.shouldSearchForContextController_){this.lazyContextController_=tr.v.ui.getScalarContextControllerForElement(this);this.shouldSearchForContextController_=false;}
+return this.lazyContextController_;},hasContext_:function(contextGroup){return!!(contextGroup&&this.contextController_);},contextGroupChanged_:function(newContextGroup,oldContextGroup){this.detachFromContextControllerIfPossible_(oldContextGroup);if(!this.attachToContextControllerIfPossible_(newContextGroup)){this.onContextUpdated_();}},attachToContextControllerIfPossible_:function(contextGroup){if(!this.hasContext_(contextGroup))return false;this.contextController_.addEventListener('context-updated',this.onContextUpdated_);this.contextController_.onScalarSpanAdded(contextGroup,this);return true;},detachFromContextControllerIfPossible_:function(contextGroup){if(!this.hasContext_(contextGroup))return;this.contextController_.removeEventListener('context-updated',this.onContextUpdated_);this.contextController_.onScalarSpanRemoved(contextGroup,this);},attached:function(){tr.b.Unit.addEventListener('display-mode-changed',this.updateContents_.bind(this));this.shouldSearchForContextController_=true;this.attachToContextControllerIfPossible_(this.contextGroup);},detached:function(){tr.b.Unit.removeEventListener('display-mode-changed',this.updateContents_.bind(this));this.detachFromContextControllerIfPossible_(this.contextGroup);this.shouldSearchForContextController_=false;this.lazyContextController_=undefined;},onContextUpdated_:function(){this.updateSparkline_();},get context(){return this.context_;},set context(context){this.context_=context;this.updateContents_();},get unit(){return this.unit_;},set unit(unit){this.unit_=unit;this.updateContents_();this.updateSparkline_();},setValueAndUnit:function(value,unit){this.value_=value;this.unit_=unit;this.updateContents_();},get customContextRange(){return this.customContextRange_;},set customContextRange(customContextRange){this.customContextRange_=customContextRange;this.updateSparkline_();},get inline(){return Polymer.dom(this).classList.contains('inline');},set inline(inline){if(inline){Polymer.dom(this).classList.add('inline');}else{Polymer.dom(this).classList.remove('inline');}},get leftAlign(){return Polymer.dom(this).classList.contains('left-align');},set leftAlign(leftAlign){if(leftAlign){Polymer.dom(this).classList.add('left-align');}else{Polymer.dom(this).classList.remove('left-align');}},updateSparkline_:function(){Polymer.dom(this.$.sparkline).classList.remove('positive');Polymer.dom(this.$.sparkline).classList.remove('better');Polymer.dom(this.$.sparkline).classList.remove('worse');Polymer.dom(this.$.sparkline).classList.remove('same');this.$.sparkline.style.display='none';this.$.sparkline.style.left='0';this.$.sparkline.style.width='0';let range=this.customContextRange_;if(!range&&this.hasContext_(this.contextGroup)){let context=this.contextController_.getContext(this.contextGroup);if(context){range=context.range;}}
+if(!range||range.isEmpty)return;let leftPoint=Math.min(range.min,0);let rightPoint=Math.max(range.max,0);let pointDistance=rightPoint-leftPoint;if(pointDistance===0){return;}
+this.$.sparkline.style.display='block';let left;let width;if(this.value>0){width=Math.min(this.value,rightPoint);left=-leftPoint;Polymer.dom(this.$.sparkline).classList.add('positive');}else if(this.value<=0){width=-Math.max(this.value,leftPoint);left=(-leftPoint)-width;}
+this.$.sparkline.style.left=this.buildSparklineStyle_(left/pointDistance,false);this.$.sparkline.style.width=this.buildSparklineStyle_(width/pointDistance,true);let changeClass=this.changeClassName_;if(changeClass){Polymer.dom(this.$.sparkline).classList.add(changeClass);}},buildSparklineStyle_:function(ratio,isWidth){let position='calc('+ratio+' * (100% - 1px)';if(isWidth){position+=' + 1px';}
+position+=')';return position;},updateContents_:function(){Polymer.dom(this.$.content).textContent='';Polymer.dom(this.$.content).classList.remove('better');Polymer.dom(this.$.content).classList.remove('worse');Polymer.dom(this.$.content).classList.remove('same');this.$.insignificant.style.display='';this.$.significantly_better.style.display='';this.$.significantly_worse.style.display='';if(this.unit_===undefined)return;this.$.content.title='';Polymer.dom(this.$.content).textContent=this.unit_.format(this.value,this.context);this.updateDelta_();},updateDelta_:function(){let changeClass=this.changeClassName_;if(!changeClass){this.$.significance.style.display='none';return;}
+this.$.significance.style.display='inline';let title;switch(changeClass){case'better':title='improvement';break;case'worse':title='regression';break;case'same':title='no change';break;default:throw new Error('Unknown change class: '+changeClass);}
+Polymer.dom(this.$.content).classList.add(changeClass);switch(this.significance){case tr.b.math.Statistics.Significance.DONT_CARE:break;case tr.b.math.Statistics.Significance.INSIGNIFICANT:if(changeClass!=='same')title='insignificant '+title;this.$.insignificant.style.display='inline';changeClass='same';break;case tr.b.math.Statistics.Significance.SIGNIFICANT:if(changeClass==='same'){throw new Error('How can no change be significant?');}
+this.$['significantly_'+changeClass].style.display='inline';title='significant '+title;break;default:throw new Error('Unknown significance '+this.significance);}
+this.$.significance.title=title;this.$.content.title=title;},get changeClassName_(){if(!this.unit_||!this.unit_.isDelta)return undefined;switch(this.unit_.improvementDirection){case tr.b.ImprovementDirection.DONT_CARE:return undefined;case tr.b.ImprovementDirection.BIGGER_IS_BETTER:if(this.value===0)return'same';return this.value>0?'better':'worse';case tr.b.ImprovementDirection.SMALLER_IS_BETTER:if(this.value===0)return'same';return this.value<0?'better':'worse';default:throw new Error('Unknown improvement direction: '+
+this.unit_.improvementDirection);}},get warning(){return this.warning_;},set warning(warning){this.warning_=warning;let warningEl=this.$.warning;if(this.warning_){warningEl.title=warning;warningEl.style.display='inline';}else{warningEl.title='';warningEl.style.display='';}},get timestamp(){return this.value;},set timestamp(timestamp){if(timestamp instanceof tr.b.u.TimeStamp){this.value=timestamp;return;}
 this.setValueAndUnit(timestamp,tr.b.u.Units.timeStampInMs);},get duration(){return this.value;},set duration(duration){if(duration instanceof tr.b.u.TimeDuration){this.value=duration;return;}
 this.setValueAndUnit(duration,tr.b.u.Units.timeDurationInMs);}});'use strict';var URL_REGEX=/^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)$/;function isTable(object){if(!(object instanceof Array)||(object.length<2))return false;for(var colName in object[0]){if(typeof colName!=='string')return false;}
-for(var i=0;i<object.length;++i){if(!(object[i]instanceof Object))return false;for(var colName in object[i]){if(i&&(object[0][colName]===undefined))return false;var cellType=typeof object[i][colName];if(cellType!=='string'&&cellType!='number')return false;}
+for(var i=0;i<object.length;++i){if(!(object[i]instanceof Object))return false;for(var colName in object[i]){if(i&&(object[0][colName]===undefined))return false;var cellType=typeof object[i][colName];if(cellType!=='string'&&cellType!=='number')return false;}
 if(i){for(var colName in object[0]){if(object[i][colName]===undefined)return false;}}}
 return true;}
 Polymer({is:'tr-ui-a-generic-object-view',ready:function(){this.object_=undefined;},get object(){return this.object_;},set object(object){this.object_=object;this.updateContents_();},updateContents_:function(){Polymer.dom(this.$.content).textContent='';this.appendElementsForType_('',this.object_,0,0,5,'');},appendElementsForType_:function(label,object,indent,depth,maxDepth,suffix){if(depth>maxDepth){this.appendSimpleText_(label,indent,'<recursion limit reached>',suffix);return;}
 if(object===undefined){this.appendSimpleText_(label,indent,'undefined',suffix);return;}
 if(object===null){this.appendSimpleText_(label,indent,'null',suffix);return;}
-if(!(object instanceof Object)){var type=typeof object;if(type=='string'){var objectReplaced=false;if((object[0]=='{'&&object[object.length-1]=='}')||(object[0]=='['&&object[object.length-1]==']')){try{object=JSON.parse(object);objectReplaced=true;}catch(e){}}
-if(!objectReplaced){if(object.indexOf('\n')!==-1){var lines=object.split('\n');lines.forEach(function(line,i){var text,ioff,ll,ss;if(i==0){text='"'+line;ioff=0;ll=label;ss='';}else if(i<lines.length-1){text=line;ioff=1;ll='';ss='';}else{text=line+'"';ioff=1;ll='';ss=suffix;}
-var el=this.appendSimpleText_(ll,indent+ioff*label.length+ioff,text,ss);el.style.whiteSpace='pre';return el;},this);return;}else if(object.match(URL_REGEX)){var link=document.createElement('a');link.href=object;link.textContent=object;this.appendElementWithLabel_(label,indent,link,suffix);return;}else{this.appendSimpleText_(label,indent,'"'+object+'"',suffix);return;}}
-else{}}else{return this.appendSimpleText_(label,indent,object,suffix);}}
+if(!(object instanceof Object)){var type=typeof object;if(type!=='string'){return this.appendSimpleText_(label,indent,object,suffix);}
+var objectReplaced=false;if((object[0]==='{'&&object[object.length-1]==='}')||(object[0]==='['&&object[object.length-1]===']')){try{object=JSON.parse(object);objectReplaced=true;}catch(e){}}
+if(!objectReplaced){if(object.includes('\n')){var lines=object.split('\n');lines.forEach(function(line,i){var text;var ioff;var ll;var ss;if(i===0){text='"'+line;ioff=0;ll=label;ss='';}else if(i<lines.length-1){text=line;ioff=1;ll='';ss='';}else{text=line+'"';ioff=1;ll='';ss=suffix;}
+var el=this.appendSimpleText_(ll,indent+ioff*label.length+ioff,text,ss);el.style.whiteSpace='pre';return el;},this);return;}
+if(object.match(URL_REGEX)){var link=document.createElement('a');link.href=object;link.textContent=object;this.appendElementWithLabel_(label,indent,link,suffix);return;}
+this.appendSimpleText_(label,indent,'"'+object+'"',suffix);return;}}
 if(object instanceof tr.model.ObjectSnapshot){var link=document.createElement('tr-ui-a-analysis-link');link.selection=new tr.model.EventSet(object);this.appendElementWithLabel_(label,indent,link,suffix);return;}
 if(object instanceof tr.model.ObjectInstance){var link=document.createElement('tr-ui-a-analysis-link');link.selection=new tr.model.EventSet(object);this.appendElementWithLabel_(label,indent,link,suffix);return;}
-if(object instanceof tr.b.Rect){this.appendSimpleText_(label,indent,object.toString(),suffix);return;}
-if(object instanceof tr.v.ScalarNumeric){var el=this.ownerDocument.createElement('tr-v-ui-scalar-span');el.value=object;this.appendElementWithLabel_(label,indent,el,suffix);return;}
+if(object instanceof tr.b.math.Rect){this.appendSimpleText_(label,indent,object.toString(),suffix);return;}
+if(object instanceof tr.b.Scalar){var el=this.ownerDocument.createElement('tr-v-ui-scalar-span');el.value=object;el.inline=true;this.appendElementWithLabel_(label,indent,el,suffix);return;}
 if(object instanceof Array){this.appendElementsForArray_(label,object,indent,depth,maxDepth,suffix);return;}
-this.appendElementsForObject_(label,object,indent,depth,maxDepth,suffix);},appendElementsForArray_:function(label,object,indent,depth,maxDepth,suffix){if(object.length==0){this.appendSimpleText_(label,indent,'[]',suffix);return;}
-if(isTable(object)){var table=document.createElement('tr-ui-b-table');var columns=[];tr.b.iterItems(object[0],function(colName){var allStrings=true;var allNumbers=true;for(var i=0;i<object.length;++i){if(typeof(object[i][colName])!=='string')
-allStrings=false;if(typeof(object[i][colName])!=='number')
-allNumbers=false;if(!allStrings&&!allNumbers)
-break;}
+this.appendElementsForObject_(label,object,indent,depth,maxDepth,suffix);},appendElementsForArray_:function(label,object,indent,depth,maxDepth,suffix){if(object.length===0){this.appendSimpleText_(label,indent,'[]',suffix);return;}
+if(isTable(object)){var table=document.createElement('tr-ui-b-table');var columns=[];for(let colName of Object.keys(object[0])){var allStrings=true;var allNumbers=true;for(var i=0;i<object.length;++i){if(typeof(object[i][colName])!=='string'){allStrings=false;}
+if(typeof(object[i][colName])!=='number'){allNumbers=false;}
+if(!allStrings&&!allNumbers)break;}
 var column={title:colName};column.value=function(row){return row[colName];};if(allStrings){column.cmp=function(x,y){return x[colName].localeCompare(y[colName]);};}else if(allNumbers){column.cmp=function(x,y){return x[colName]-y[colName];};}
-columns.push(column);});table.tableColumns=columns;table.tableRows=object;this.appendElementWithLabel_(label,indent,table,suffix);table.rebuild();return;}
+columns.push(column);}
+table.tableColumns=columns;table.tableRows=object;this.appendElementWithLabel_(label,indent,table,suffix);table.rebuild();return;}
 this.appendElementsForType_(label+'[',object[0],indent,depth+1,maxDepth,object.length>1?',':']'+suffix);for(var i=1;i<object.length;i++){this.appendElementsForType_('',object[i],indent+label.length+1,depth+1,maxDepth,i<object.length-1?',':']'+suffix);}
-return;},appendElementsForObject_:function(label,object,indent,depth,maxDepth,suffix){var keys=tr.b.dictionaryKeys(object);if(keys.length==0){this.appendSimpleText_(label,indent,'{}',suffix);return;}
-this.appendElementsForType_(label+'{'+keys[0]+': ',object[keys[0]],indent,depth,maxDepth,keys.length>1?',':'}'+suffix);for(var i=1;i<keys.length;i++){this.appendElementsForType_(keys[i]+': ',object[keys[i]],indent+label.length+1,depth+1,maxDepth,i<keys.length-1?',':'}'+suffix);}},appendElementWithLabel_:function(label,indent,dataElement,suffix){var row=document.createElement('div');var indentSpan=document.createElement('span');indentSpan.style.whiteSpace='pre';for(var i=0;i<indent;i++)
-Polymer.dom(indentSpan).textContent+=' ';Polymer.dom(row).appendChild(indentSpan);var labelSpan=document.createElement('span');Polymer.dom(labelSpan).textContent=label;Polymer.dom(row).appendChild(labelSpan);Polymer.dom(row).appendChild(dataElement);var suffixSpan=document.createElement('span');Polymer.dom(suffixSpan).textContent=suffix;Polymer.dom(row).appendChild(suffixSpan);row.dataElement=dataElement;Polymer.dom(this.$.content).appendChild(row);},appendSimpleText_:function(label,indent,text,suffix){var el=this.ownerDocument.createElement('span');Polymer.dom(el).textContent=text;this.appendElementWithLabel_(label,indent,el,suffix);return el;}});'use strict';Polymer({is:'tr-ui-a-generic-object-view-with-label',ready:function(){this.labelEl_=document.createElement('div');this.genericObjectView_=document.createElement('tr-ui-a-generic-object-view');Polymer.dom(this.root).appendChild(this.labelEl_);Polymer.dom(this.root).appendChild(this.genericObjectView_);},get label(){return Polymer.dom(this.labelEl_).textContent;},set label(label){Polymer.dom(this.labelEl_).textContent=label;},get object(){return this.genericObjectView_.object;},set object(object){this.genericObjectView_.object=object;}});'use strict';tr.exportTo('tr.ui.analysis',function(){var ObjectSnapshotView=tr.ui.b.define('object-snapshot-view');ObjectSnapshotView.prototype={__proto__:HTMLDivElement.prototype,decorate:function(){this.objectSnapshot_=undefined;},get requiresTallView(){return true;},set modelEvent(obj){this.objectSnapshot=obj;},get modelEvent(){return this.objectSnapshot;},get objectSnapshot(){return this.objectSnapshot_;},set objectSnapshot(i){this.objectSnapshot_=i;this.updateContents();},updateContents:function(){throw new Error('Not implemented');}};var options=new tr.b.ExtensionRegistryOptions(tr.b.TYPE_BASED_REGISTRY_MODE);options.mandatoryBaseClass=ObjectSnapshotView;options.defaultMetadata={showInstances:true,showInTrackView:true};tr.b.decorateExtensionRegistry(ObjectSnapshotView,options);return{ObjectSnapshotView:ObjectSnapshotView};});'use strict';Polymer({is:'tr-ui-b-drag-handle',created:function(){this.lastMousePos_=0;this.onMouseMove_=this.onMouseMove_.bind(this);this.onMouseUp_=this.onMouseUp_.bind(this);this.addEventListener('mousedown',this.onMouseDown_);this.target_=undefined;this.horizontal=true;this.observer_=new WebKitMutationObserver(this.didTargetMutate_.bind(this));this.targetSizesByModeKey_={};},get modeKey_(){return this.target_.className==''?'.':this.target_.className;},get target(){return this.target_;},set target(target){this.observer_.disconnect();this.target_=target;if(!this.target_)
-return;this.observer_.observe(this.target_,{attributes:true,attributeFilter:['class']});},get horizontal(){return this.horizontal_;},set horizontal(h){this.horizontal_=h;if(this.horizontal_)
-this.className='horizontal-drag-handle';else
-this.className='vertical-drag-handle';},get vertical(){return!this.horizontal_;},set vertical(v){this.horizontal=!v;},forceMutationObserverFlush_:function(){var records=this.observer_.takeRecords();if(records.length)
-this.didTargetMutate_(records);},didTargetMutate_:function(e){var modeSize=this.targetSizesByModeKey_[this.modeKey_];if(modeSize!==undefined){this.setTargetSize_(modeSize);return;}
+return;},appendElementsForObject_:function(label,object,indent,depth,maxDepth,suffix){var keys=Object.keys(object);if(keys.length===0){this.appendSimpleText_(label,indent,'{}',suffix);return;}
+this.appendElementsForType_(label+'{'+keys[0]+': ',object[keys[0]],indent,depth,maxDepth,keys.length>1?',':'}'+suffix);for(var i=1;i<keys.length;i++){this.appendElementsForType_(keys[i]+': ',object[keys[i]],indent+label.length+1,depth+1,maxDepth,i<keys.length-1?',':'}'+suffix);}},appendElementWithLabel_:function(label,indent,dataElement,suffix){var row=document.createElement('div');var indentSpan=document.createElement('span');indentSpan.style.whiteSpace='pre';for(var i=0;i<indent;i++){Polymer.dom(indentSpan).textContent+=' ';}
+Polymer.dom(row).appendChild(indentSpan);var labelSpan=document.createElement('span');Polymer.dom(labelSpan).textContent=label;Polymer.dom(row).appendChild(labelSpan);Polymer.dom(row).appendChild(dataElement);var suffixSpan=document.createElement('span');Polymer.dom(suffixSpan).textContent=suffix;Polymer.dom(row).appendChild(suffixSpan);row.dataElement=dataElement;Polymer.dom(this.$.content).appendChild(row);},appendSimpleText_:function(label,indent,text,suffix){var el=this.ownerDocument.createElement('span');Polymer.dom(el).textContent=text;this.appendElementWithLabel_(label,indent,el,suffix);return el;}});'use strict';Polymer({is:'tr-ui-a-generic-object-view-with-label',ready:function(){this.labelEl_=document.createElement('div');this.genericObjectView_=document.createElement('tr-ui-a-generic-object-view');Polymer.dom(this.root).appendChild(this.labelEl_);Polymer.dom(this.root).appendChild(this.genericObjectView_);},get label(){return Polymer.dom(this.labelEl_).textContent;},set label(label){Polymer.dom(this.labelEl_).textContent=label;},get object(){return this.genericObjectView_.object;},set object(object){this.genericObjectView_.object=object;}});'use strict';tr.exportTo('tr.ui.analysis',function(){var ObjectSnapshotView=tr.ui.b.define('object-snapshot-view');ObjectSnapshotView.prototype={__proto__:HTMLDivElement.prototype,decorate:function(){this.objectSnapshot_=undefined;},get requiresTallView(){return true;},set modelEvent(obj){this.objectSnapshot=obj;},get modelEvent(){return this.objectSnapshot;},get objectSnapshot(){return this.objectSnapshot_;},set objectSnapshot(i){this.objectSnapshot_=i;this.updateContents();},updateContents:function(){throw new Error('Not implemented');}};var options=new tr.b.ExtensionRegistryOptions(tr.b.TYPE_BASED_REGISTRY_MODE);options.mandatoryBaseClass=ObjectSnapshotView;options.defaultMetadata={showInstances:true,showInTrackView:true};tr.b.decorateExtensionRegistry(ObjectSnapshotView,options);return{ObjectSnapshotView,};});'use strict';Polymer({is:'tr-ui-b-drag-handle',created:function(){this.lastMousePos_=0;this.onMouseMove_=this.onMouseMove_.bind(this);this.onMouseUp_=this.onMouseUp_.bind(this);this.addEventListener('mousedown',this.onMouseDown_);this.target_=undefined;this.horizontal=true;this.observer_=new WebKitMutationObserver(this.didTargetMutate_.bind(this));this.targetSizesByModeKey_={};},get modeKey_(){return this.target_.className===''?'.':this.target_.className;},get target(){return this.target_;},set target(target){this.observer_.disconnect();this.target_=target;if(!this.target_)return;this.observer_.observe(this.target_,{attributes:true,attributeFilter:['class']});},get horizontal(){return this.horizontal_;},set horizontal(h){this.horizontal_=h;if(this.horizontal_){this.className='horizontal-drag-handle';}else{this.className='vertical-drag-handle';}},get vertical(){return!this.horizontal_;},set vertical(v){this.horizontal=!v;},forceMutationObserverFlush_:function(){var records=this.observer_.takeRecords();if(records.length){this.didTargetMutate_(records);}},didTargetMutate_:function(e){var modeSize=this.targetSizesByModeKey_[this.modeKey_];if(modeSize!==undefined){this.setTargetSize_(modeSize);return;}
 this.target_.style[this.targetStyleKey_]='';},get targetStyleKey_(){return this.horizontal_?'height':'width';},getTargetSize_:function(){var targetStyleKey=this.targetStyleKey_;if(!this.target_.style[targetStyleKey]){this.target_.style[targetStyleKey]=window.getComputedStyle(this.target_)[targetStyleKey];}
 var size=parseInt(this.target_.style[targetStyleKey]);this.targetSizesByModeKey_[this.modeKey_]=size;return size;},setTargetSize_:function(s){this.target_.style[this.targetStyleKey_]=s+'px';this.targetSizesByModeKey_[this.modeKey_]=s;tr.b.dispatchSimpleEvent(this,'drag-handle-resize',true,false);},applyDelta_:function(delta){var curSize=this.getTargetSize_();var newSize;if(this.target_===this.nextElementSibling){newSize=curSize+delta;}else{newSize=curSize-delta;}
-this.setTargetSize_(newSize);},onMouseMove_:function(e){var curMousePos=this.horizontal_?e.clientY:e.clientX;var delta=this.lastMousePos_-curMousePos;this.applyDelta_(delta);this.lastMousePos_=curMousePos;e.preventDefault();return true;},onMouseDown_:function(e){if(!this.target_)
-return;this.forceMutationObserverFlush_();this.lastMousePos_=this.horizontal_?e.clientY:e.clientX;document.addEventListener('mousemove',this.onMouseMove_);document.addEventListener('mouseup',this.onMouseUp_);e.preventDefault();return true;},onMouseUp_:function(e){document.removeEventListener('mousemove',this.onMouseMove_);document.removeEventListener('mouseup',this.onMouseUp_);e.preventDefault();}});'use strict';tr.exportTo('tr.ui.b',function(){function HotKey(dict){if(dict.eventType===undefined)
-throw new Error('eventType must be given');if(dict.keyCode===undefined&&dict.keyCodes===undefined)
-throw new Error('keyCode or keyCodes must be given');if(dict.keyCode!==undefined&&dict.keyCodes!==undefined)
-throw new Error('Only keyCode or keyCodes can be given');if(dict.callback===undefined)
-throw new Error('callback must be given');this.eventType_=dict.eventType;this.keyCodes_=[];if(dict.keyCode)
-this.pushKeyCode_(dict.keyCode);else if(dict.keyCodes){dict.keyCodes.forEach(this.pushKeyCode_,this);}
+this.setTargetSize_(newSize);},onMouseMove_:function(e){var curMousePos=this.horizontal_?e.clientY:e.clientX;var delta=this.lastMousePos_-curMousePos;this.applyDelta_(delta);this.lastMousePos_=curMousePos;e.preventDefault();return true;},onMouseDown_:function(e){if(!this.target_)return;this.forceMutationObserverFlush_();this.lastMousePos_=this.horizontal_?e.clientY:e.clientX;document.addEventListener('mousemove',this.onMouseMove_);document.addEventListener('mouseup',this.onMouseUp_);e.preventDefault();return true;},onMouseUp_:function(e){document.removeEventListener('mousemove',this.onMouseMove_);document.removeEventListener('mouseup',this.onMouseUp_);e.preventDefault();}});'use strict';tr.exportTo('tr.ui.b',function(){function HotKey(dict){if(dict.eventType===undefined){throw new Error('eventType must be given');}
+if(dict.keyCode===undefined&&dict.keyCodes===undefined){throw new Error('keyCode or keyCodes must be given');}
+if(dict.keyCode!==undefined&&dict.keyCodes!==undefined){throw new Error('Only keyCode or keyCodes can be given');}
+if(dict.callback===undefined){throw new Error('callback must be given');}
+this.eventType_=dict.eventType;this.keyCodes_=[];if(dict.keyCode){this.pushKeyCode_(dict.keyCode);}else if(dict.keyCodes){dict.keyCodes.forEach(this.pushKeyCode_,this);}
 this.useCapture_=!!dict.useCapture;this.callback_=dict.callback;this.thisArg_=dict.thisArg!==undefined?dict.thisArg:undefined;this.helpText_=dict.helpText!==undefined?dict.helpText:undefined;}
-HotKey.prototype={get eventType(){return this.eventType_;},get keyCodes(){return this.keyCodes_;},get helpText(){return this.helpText_;},call:function(e){this.callback_.call(this.thisArg_,e);},pushKeyCode_:function(keyCode){this.keyCodes_.push(keyCode);}};return{HotKey:HotKey};});'use strict';Polymer({is:'tv-ui-b-hotkey-controller',created:function(){this.isAttached_=false;this.globalMode_=false;this.slavedToParentController_=undefined;this.curHost_=undefined;this.childControllers_=[];this.bubblingKeyDownHotKeys_={};this.capturingKeyDownHotKeys_={};this.bubblingKeyPressHotKeys_={};this.capturingKeyPressHotKeys_={};this.onBubblingKeyDown_=this.onKey_.bind(this,false);this.onCapturingKeyDown_=this.onKey_.bind(this,true);this.onBubblingKeyPress_=this.onKey_.bind(this,false);this.onCapturingKeyPress_=this.onKey_.bind(this,true);},attached:function(){this.isAttached_=true;var host=this.findHost_();if(host.__hotkeyController)
-throw new Error('Multiple hotkey controllers attached to this host');host.__hotkeyController=this;this.curHost_=host;var parentElement;if(host.parentElement)
-parentElement=host.parentElement;else
-parentElement=Polymer.dom(host).parentNode.host;var parentController=tr.b.getHotkeyControllerForElement(parentElement);if(parentController){this.slavedToParentController_=parentController;parentController.addChildController_(this);return;}
-host.addEventListener('keydown',this.onBubblingKeyDown_,false);host.addEventListener('keydown',this.onCapturingKeyDown_,true);host.addEventListener('keypress',this.onBubblingKeyPress_,false);host.addEventListener('keypress',this.onCapturingKeyPress_,true);},detached:function(){this.isAttached_=false;var host=this.curHost_;if(!host)
-return;delete host.__hotkeyController;this.curHost_=undefined;if(this.slavedToParentController_){this.slavedToParentController_.removeChildController_(this);this.slavedToParentController_=undefined;return;}
-host.removeEventListener('keydown',this.onBubblingKeyDown_,false);host.removeEventListener('keydown',this.onCapturingKeyDown_,true);host.removeEventListener('keypress',this.onBubblingKeyPress_,false);host.removeEventListener('keypress',this.onCapturingKeyPress_,true);},addChildController_:function(controller){var i=this.childControllers_.indexOf(controller);if(i!==-1)
-throw new Error('Controller already registered');this.childControllers_.push(controller);},removeChildController_:function(controller){var i=this.childControllers_.indexOf(controller);if(i===-1)
-throw new Error('Controller not registered');this.childControllers_.splice(i,1);return controller;},getKeyMapForEventType_:function(eventType,useCapture){if(eventType==='keydown'){if(!useCapture)
-return this.bubblingKeyDownHotKeys_;else
-return this.capturingKeyDownHotKeys_;}else if(eventType==='keypress'){if(!useCapture)
-return this.bubblingKeyPressHotKeys_;else
-return this.capturingKeyPressHotKeys_;}else{throw new Error('Unsupported key event');}},addHotKey:function(hotKey){if(!(hotKey instanceof tr.ui.b.HotKey))
-throw new Error('hotKey must be a tr.ui.b.HotKey');var keyMap=this.getKeyMapForEventType_(hotKey.eventType,hotKey.useCapture);for(var i=0;i<hotKey.keyCodes.length;i++){var keyCode=hotKey.keyCodes[i];if(keyMap[keyCode])
-throw new Error('Key is already bound for keyCode='+keyCode);}
+HotKey.prototype={get eventType(){return this.eventType_;},get keyCodes(){return this.keyCodes_;},get helpText(){return this.helpText_;},call:function(e){this.callback_.call(this.thisArg_,e);},pushKeyCode_:function(keyCode){this.keyCodes_.push(keyCode);}};return{HotKey,};});'use strict';Polymer({is:'tv-ui-b-hotkey-controller',created:function(){this.isAttached_=false;this.globalMode_=false;this.slavedToParentController_=undefined;this.curHost_=undefined;this.childControllers_=[];this.bubblingKeyDownHotKeys_={};this.capturingKeyDownHotKeys_={};this.bubblingKeyPressHotKeys_={};this.capturingKeyPressHotKeys_={};this.onBubblingKeyDown_=this.onKey_.bind(this,false);this.onCapturingKeyDown_=this.onKey_.bind(this,true);this.onBubblingKeyPress_=this.onKey_.bind(this,false);this.onCapturingKeyPress_=this.onKey_.bind(this,true);},attached:function(){this.isAttached_=true;var host=this.findHost_();if(host.__hotkeyController){throw new Error('Multiple hotkey controllers attached to this host');}
+host.__hotkeyController=this;this.curHost_=host;var parentElement;if(host.parentElement){parentElement=host.parentElement;}else{parentElement=Polymer.dom(host).parentNode.host;}
+var parentController=tr.b.getHotkeyControllerForElement(parentElement);if(parentController){this.slavedToParentController_=parentController;parentController.addChildController_(this);return;}
+host.addEventListener('keydown',this.onBubblingKeyDown_,false);host.addEventListener('keydown',this.onCapturingKeyDown_,true);host.addEventListener('keypress',this.onBubblingKeyPress_,false);host.addEventListener('keypress',this.onCapturingKeyPress_,true);},detached:function(){this.isAttached_=false;var host=this.curHost_;if(!host)return;delete host.__hotkeyController;this.curHost_=undefined;if(this.slavedToParentController_){this.slavedToParentController_.removeChildController_(this);this.slavedToParentController_=undefined;return;}
+host.removeEventListener('keydown',this.onBubblingKeyDown_,false);host.removeEventListener('keydown',this.onCapturingKeyDown_,true);host.removeEventListener('keypress',this.onBubblingKeyPress_,false);host.removeEventListener('keypress',this.onCapturingKeyPress_,true);},addChildController_:function(controller){var i=this.childControllers_.indexOf(controller);if(i!==-1){throw new Error('Controller already registered');}
+this.childControllers_.push(controller);},removeChildController_:function(controller){var i=this.childControllers_.indexOf(controller);if(i===-1){throw new Error('Controller not registered');}
+this.childControllers_.splice(i,1);return controller;},getKeyMapForEventType_:function(eventType,useCapture){if(eventType==='keydown'){if(!useCapture){return this.bubblingKeyDownHotKeys_;}
+return this.capturingKeyDownHotKeys_;}
+if(eventType==='keypress'){if(!useCapture){return this.bubblingKeyPressHotKeys_;}
+return this.capturingKeyPressHotKeys_;}
+throw new Error('Unsupported key event');},addHotKey:function(hotKey){if(!(hotKey instanceof tr.ui.b.HotKey)){throw new Error('hotKey must be a tr.ui.b.HotKey');}
+var keyMap=this.getKeyMapForEventType_(hotKey.eventType,hotKey.useCapture);for(var i=0;i<hotKey.keyCodes.length;i++){var keyCode=hotKey.keyCodes[i];if(keyMap[keyCode]){throw new Error('Key is already bound for keyCode='+keyCode);}}
 for(var i=0;i<hotKey.keyCodes.length;i++){var keyCode=hotKey.keyCodes[i];keyMap[keyCode]=hotKey;}
-return hotKey;},removeHotKey:function(hotKey){if(!(hotKey instanceof tr.ui.b.HotKey))
-throw new Error('hotKey must be a tr.ui.b.HotKey');var keyMap=this.getKeyMapForEventType_(hotKey.eventType,hotKey.useCapture);for(var i=0;i<hotKey.keyCodes.length;i++){var keyCode=hotKey.keyCodes[i];if(!keyMap[keyCode])
-throw new Error('Key is not bound for keyCode='+keyCode);keyMap[keyCode]=hotKey;}
+return hotKey;},removeHotKey:function(hotKey){if(!(hotKey instanceof tr.ui.b.HotKey)){throw new Error('hotKey must be a tr.ui.b.HotKey');}
+var keyMap=this.getKeyMapForEventType_(hotKey.eventType,hotKey.useCapture);for(var i=0;i<hotKey.keyCodes.length;i++){var keyCode=hotKey.keyCodes[i];if(!keyMap[keyCode]){throw new Error('Key is not bound for keyCode='+keyCode);}
+keyMap[keyCode]=hotKey;}
 for(var i=0;i<hotKey.keyCodes.length;i++){var keyCode=hotKey.keyCodes[i];delete keyMap[keyCode];}
-return hotKey;},get globalMode(){return this.globalMode_;},set globalMode(globalMode){var wasAttached=this.isAttached_;if(wasAttached)
-this.detached();this.globalMode_=!!globalMode;if(wasAttached)
-this.attached();},get topmostConroller_(){if(this.slavedToParentController_)
-return this.slavedToParentController_.topmostConroller_;return this;},childRequestsGeneralFocus:function(child){var topmost=this.topmostConroller_;if(topmost.curHost_){if(topmost.curHost_.hasAttribute('tabIndex')){topmost.curHost_.focus();}else{if(document.activeElement)
-document.activeElement.blur();}}else{if(document.activeElement)
-document.activeElement.blur();}},childRequestsBlur:function(child){child.blur();var topmost=this.topmostConroller_;if(topmost.curHost_){topmost.curHost_.focus();}},findHost_:function(){if(this.globalMode_){return document.body;}else{if(this.parentElement)
-return this.parentElement;var node=this;while(Polymer.dom(node).parentNode){node=Polymer.dom(node).parentNode;}
-return node.host;}},appendMatchingHotKeysTo_:function(matchedHotKeys,useCapture,e){var localKeyMap=this.getKeyMapForEventType_(e.type,useCapture);var localHotKey=localKeyMap[e.keyCode];if(localHotKey)
-matchedHotKeys.push(localHotKey);for(var i=0;i<this.childControllers_.length;i++){var controller=this.childControllers_[i];controller.appendMatchingHotKeysTo_(matchedHotKeys,useCapture,e);}},onKey_:function(useCapture,e){if(useCapture==false&&e.path[0].tagName=='INPUT')
-return;var sortedControllers;var matchedHotKeys=[];this.appendMatchingHotKeysTo_(matchedHotKeys,useCapture,e);if(matchedHotKeys.length===0)
-return false;if(matchedHotKeys.length>1){throw new Error('More than one hotKey is currently unsupported');}
-var hotKey=matchedHotKeys[0];var prevented=0;prevented|=hotKey.call(e);return!prevented&&e.defaultPrevented;}});'use strict';tr.exportTo('tr.b',function(){function getHotkeyControllerForElement(refElement){var curElement=refElement;while(curElement){if(curElement.tagName==='tv-ui-b-hotkey-controller')
-return curElement;if(curElement.__hotkeyController)
-return curElement.__hotkeyController;if(curElement.parentElement){curElement=curElement.parentElement;continue;}
+return hotKey;},get globalMode(){return this.globalMode_;},set globalMode(globalMode){var wasAttached=this.isAttached_;if(wasAttached){this.detached();}
+this.globalMode_=!!globalMode;if(wasAttached){this.attached();}},get topmostConroller_(){if(this.slavedToParentController_){return this.slavedToParentController_.topmostConroller_;}
+return this;},childRequestsGeneralFocus:function(child){var topmost=this.topmostConroller_;if(topmost.curHost_){if(topmost.curHost_.hasAttribute('tabIndex')){topmost.curHost_.focus();}else{if(document.activeElement){document.activeElement.blur();}}}else{if(document.activeElement){document.activeElement.blur();}}},childRequestsBlur:function(child){child.blur();var topmost=this.topmostConroller_;if(topmost.curHost_){topmost.curHost_.focus();}},findHost_:function(){if(this.globalMode_)return document.body;if(this.parentElement)return this.parentElement;if(!Polymer.dom(this).parentNode)return this.host;let node=this.parentNode;while(Polymer.dom(node).parentNode)node=Polymer.dom(node).parentNode;return node.host;},appendMatchingHotKeysTo_:function(matchedHotKeys,useCapture,e){var localKeyMap=this.getKeyMapForEventType_(e.type,useCapture);var localHotKey=localKeyMap[e.keyCode];if(localHotKey){matchedHotKeys.push(localHotKey);}
+for(var i=0;i<this.childControllers_.length;i++){var controller=this.childControllers_[i];controller.appendMatchingHotKeysTo_(matchedHotKeys,useCapture,e);}},onKey_:function(useCapture,e){if(!useCapture&&e.path[0].tagName==='INPUT')return;var sortedControllers;var matchedHotKeys=[];this.appendMatchingHotKeysTo_(matchedHotKeys,useCapture,e);if(matchedHotKeys.length===0)return false;if(matchedHotKeys.length>1){throw new Error('More than one hotKey is currently unsupported');}
+var hotKey=matchedHotKeys[0];var prevented=0;prevented|=hotKey.call(e);return!prevented&&e.defaultPrevented;}});'use strict';tr.exportTo('tr.b',function(){function getHotkeyControllerForElement(refElement){var curElement=refElement;while(curElement){if(curElement.tagName==='tv-ui-b-hotkey-controller'){return curElement;}
+if(curElement.__hotkeyController){return curElement.__hotkeyController;}
+if(curElement.parentElement){curElement=curElement.parentElement;continue;}
 curElement=findHost(curElement);}
 return undefined;}
 function findHost(initialNode){var node=initialNode;while(Polymer.dom(node).parentNode){node=Polymer.dom(node).parentNode;}
 return node.host;}
-return{getHotkeyControllerForElement:getHotkeyControllerForElement};});'use strict';Polymer({is:'tr-ui-b-info-bar',ready:function(){this.messageEl_=this.$.message;this.buttonsEl_=this.$.buttons;this.message='';this.visible=false;},get message(){return Polymer.dom(this.messageEl_).textContent;},set message(message){Polymer.dom(this.messageEl_).textContent=message;},get visible(){return!Polymer.dom(this).classList.contains('info-bar-hidden');},set visible(visible){if(visible)
-Polymer.dom(this).classList.remove('info-bar-hidden');else
-Polymer.dom(this).classList.add('info-bar-hidden');},removeAllButtons:function(){Polymer.dom(this.buttonsEl_).textContent='';},addButton:function(text,clickCallback){var button=document.createElement('button');Polymer.dom(button).textContent=text;button.addEventListener('click',clickCallback);Polymer.dom(this.buttonsEl_).appendChild(button);return button;}});'use strict';tr.exportTo('tr.ui.b',function(){var ContainerThatDecoratesItsChildren=tr.ui.b.define('div');ContainerThatDecoratesItsChildren.prototype={__proto__:HTMLDivElement.prototype,decorate:function(){this.observer_=new WebKitMutationObserver(this.didMutate_.bind(this));this.observer_.observe(this,{childList:true});Object.defineProperty(this,'textContent',{get:undefined,set:this.onSetTextContent_});},appendChild:function(x){HTMLDivElement.prototype.appendChild.call(this,x);this.didMutate_(this.observer_.takeRecords());},insertBefore:function(x,y){HTMLDivElement.prototype.insertBefore.call(this,x,y);this.didMutate_(this.observer_.takeRecords());},removeChild:function(x){HTMLDivElement.prototype.removeChild.call(this,x);this.didMutate_(this.observer_.takeRecords());},replaceChild:function(x,y){HTMLDivElement.prototype.replaceChild.call(this,x,y);this.didMutate_(this.observer_.takeRecords());},onSetTextContent_:function(textContent){if(textContent!='')
-throw new Error('textContent can only be set to \'\'.');this.clear();},clear:function(){while(Polymer.dom(this).lastChild)
-HTMLDivElement.prototype.removeChild.call(this,Polymer.dom(this).lastChild);this.didMutate_(this.observer_.takeRecords());},didMutate_:function(records){this.beginDecorating_();for(var i=0;i<records.length;i++){var addedNodes=records[i].addedNodes;if(addedNodes){for(var j=0;j<addedNodes.length;j++)
-this.decorateChild_(addedNodes[j]);}
+return{getHotkeyControllerForElement,};});'use strict';Polymer({is:'tr-ui-b-info-bar',ready:function(){this.messageEl_=this.$.message;this.buttonsEl_=this.$.buttons;this.message='';},get message(){return Polymer.dom(this.messageEl_).textContent;},set message(message){Polymer.dom(this.messageEl_).textContent=message;},get visible(){return!this.hidden;},set visible(visible){this.hidden=!visible;},removeAllButtons:function(){Polymer.dom(this.buttonsEl_).textContent='';},addButton:function(text,clickCallback){var button=document.createElement('button');Polymer.dom(button).textContent=text;button.addEventListener('click',clickCallback);Polymer.dom(this.buttonsEl_).appendChild(button);return button;}});'use strict';tr.exportTo('tr.ui.b',function(){var ContainerThatDecoratesItsChildren=tr.ui.b.define('div');ContainerThatDecoratesItsChildren.prototype={__proto__:HTMLDivElement.prototype,decorate:function(){this.observer_=new WebKitMutationObserver(this.didMutate_.bind(this));this.observer_.observe(this,{childList:true});Object.defineProperty(this,'textContent',{get:undefined,set:this.onSetTextContent_});},appendChild:function(x){HTMLDivElement.prototype.appendChild.call(this,x);this.didMutate_(this.observer_.takeRecords());},insertBefore:function(x,y){HTMLDivElement.prototype.insertBefore.call(this,x,y);this.didMutate_(this.observer_.takeRecords());},removeChild:function(x){HTMLDivElement.prototype.removeChild.call(this,x);this.didMutate_(this.observer_.takeRecords());},replaceChild:function(x,y){HTMLDivElement.prototype.replaceChild.call(this,x,y);this.didMutate_(this.observer_.takeRecords());},onSetTextContent_:function(textContent){if(textContent!==''){throw new Error('textContent can only be set to \'\'.');}
+this.clear();},clear:function(){while(Polymer.dom(this).lastChild){HTMLDivElement.prototype.removeChild.call(this,Polymer.dom(this).lastChild);}
+this.didMutate_(this.observer_.takeRecords());},didMutate_:function(records){this.beginDecorating_();for(var i=0;i<records.length;i++){var addedNodes=records[i].addedNodes;if(addedNodes){for(var j=0;j<addedNodes.length;j++){this.decorateChild_(addedNodes[j]);}}
 var removedNodes=records[i].removedNodes;if(removedNodes){for(var j=0;j<removedNodes.length;j++){this.undecorateChild_(removedNodes[j]);}}}
-this.doneDecoratingForNow_();},decorateChild_:function(child){throw new Error('Not implemented');},undecorateChild_:function(child){throw new Error('Not implemented');},beginDecorating_:function(){},doneDecoratingForNow_:function(){}};return{ContainerThatDecoratesItsChildren:ContainerThatDecoratesItsChildren};});'use strict';tr.exportTo('tr.ui.b',function(){var ListView=tr.ui.b.define('x-list-view',tr.ui.b.ContainerThatDecoratesItsChildren);ListView.prototype={__proto__:tr.ui.b.ContainerThatDecoratesItsChildren.prototype,decorate:function(){tr.ui.b.ContainerThatDecoratesItsChildren.prototype.decorate.call(this);Polymer.dom(this).classList.add('x-list-view');this.onItemClicked_=this.onItemClicked_.bind(this);this.onKeyDown_=this.onKeyDown_.bind(this);this.tabIndex=0;this.addEventListener('keydown',this.onKeyDown_);this.selectionChanged_=false;},decorateChild_:function(item){Polymer.dom(item).classList.add('list-item');item.addEventListener('click',this.onItemClicked_,true);var listView=this;Object.defineProperty(item,'selected',{configurable:true,set:function(value){var oldSelection=listView.selectedElement;if(oldSelection&&oldSelection!=this&&value)
-Polymer.dom(listView.selectedElement).removeAttribute('selected');if(value)
-Polymer.dom(this).setAttribute('selected','selected');else
-Polymer.dom(this).removeAttribute('selected');var newSelection=listView.selectedElement;if(newSelection!=oldSelection)
-tr.b.dispatchSimpleEvent(listView,'selection-changed',false);},get:function(){return this.hasAttribute('selected');}});},undecorateChild_:function(item){this.selectionChanged_|=item.selected;Polymer.dom(item).classList.remove('list-item');item.removeEventListener('click',this.onItemClicked_);delete item.selected;},beginDecorating_:function(){this.selectionChanged_=false;},doneDecoratingForNow_:function(){if(this.selectionChanged_)
-tr.b.dispatchSimpleEvent(this,'selection-changed',false);},get selectedElement(){var el=Polymer.dom(this).querySelector('.list-item[selected]');if(!el)
-return undefined;return el;},set selectedElement(el){if(!el){if(this.selectedElement)
-this.selectedElement.selected=false;return;}
-if(el.parentElement!=this)
-throw new Error('Can only select elements that are children of this list view');el.selected=true;},getElementByIndex:function(index){return Polymer.dom(this).querySelector('.list-item:nth-child('+index+')');},clear:function(){var changed=this.selectedElement!==undefined;tr.ui.b.ContainerThatDecoratesItsChildren.prototype.clear.call(this);if(changed)
-tr.b.dispatchSimpleEvent(this,'selection-changed',false);},onItemClicked_:function(e){var currentSelectedElement=this.selectedElement;if(currentSelectedElement)
-Polymer.dom(currentSelectedElement).removeAttribute('selected');var element=e.target;while(element.parentElement!=this)
-element=element.parentElement;if(element!==currentSelectedElement)
-Polymer.dom(element).setAttribute('selected','selected');tr.b.dispatchSimpleEvent(this,'selection-changed',false);},onKeyDown_:function(e){if(this.selectedElement===undefined)
-return;if(e.keyCode==38){var prev=Polymer.dom(this.selectedElement).previousSibling;if(prev){prev.selected=true;tr.ui.b.scrollIntoViewIfNeeded(prev);e.preventDefault();return true;}}else if(e.keyCode==40){var next=Polymer.dom(this.selectedElement).nextSibling;if(next){next.selected=true;tr.ui.b.scrollIntoViewIfNeeded(next);e.preventDefault();return true;}}},addItem:function(textContent){var item=document.createElement('div');Polymer.dom(item).textContent=textContent;Polymer.dom(this).appendChild(item);return item;}};return{ListView:ListView};});'use strict';tr.exportTo('tr.ui.b',function(){var MOUSE_SELECTOR_MODE={};MOUSE_SELECTOR_MODE.SELECTION=0x1;MOUSE_SELECTOR_MODE.PANSCAN=0x2;MOUSE_SELECTOR_MODE.ZOOM=0x4;MOUSE_SELECTOR_MODE.TIMING=0x8;MOUSE_SELECTOR_MODE.ROTATE=0x10;MOUSE_SELECTOR_MODE.ALL_MODES=0x1F;var MOUSE_SELECTOR_MODE_INFOS={};MOUSE_SELECTOR_MODE_INFOS[MOUSE_SELECTOR_MODE.PANSCAN]={mode:MOUSE_SELECTOR_MODE.PANSCAN,title:'pan',eventNames:{enter:'enterpan',begin:'beginpan',update:'updatepan',end:'endpan',exit:'exitpan'},activeBackgroundPosition:'-30px -10px',defaultBackgroundPosition:'0 -10px'};MOUSE_SELECTOR_MODE_INFOS[MOUSE_SELECTOR_MODE.SELECTION]={mode:MOUSE_SELECTOR_MODE.SELECTION,title:'selection',eventNames:{enter:'enterselection',begin:'beginselection',update:'updateselection',end:'endselection',exit:'exitselection'},activeBackgroundPosition:'-30px -40px',defaultBackgroundPosition:'0 -40px'};MOUSE_SELECTOR_MODE_INFOS[MOUSE_SELECTOR_MODE.ZOOM]={mode:MOUSE_SELECTOR_MODE.ZOOM,title:'zoom',eventNames:{enter:'enterzoom',begin:'beginzoom',update:'updatezoom',end:'endzoom',exit:'exitzoom'},activeBackgroundPosition:'-30px -70px',defaultBackgroundPosition:'0 -70px'};MOUSE_SELECTOR_MODE_INFOS[MOUSE_SELECTOR_MODE.TIMING]={mode:MOUSE_SELECTOR_MODE.TIMING,title:'timing',eventNames:{enter:'entertiming',begin:'begintiming',update:'updatetiming',end:'endtiming',exit:'exittiming'},activeBackgroundPosition:'-30px -100px',defaultBackgroundPosition:'0 -100px'};MOUSE_SELECTOR_MODE_INFOS[MOUSE_SELECTOR_MODE.ROTATE]={mode:MOUSE_SELECTOR_MODE.ROTATE,title:'rotate',eventNames:{enter:'enterrotate',begin:'beginrotate',update:'updaterotate',end:'endrotate',exit:'exitrotate'},activeBackgroundPosition:'-30px -130px',defaultBackgroundPosition:'0 -130px'};return{MOUSE_SELECTOR_MODE_INFOS:MOUSE_SELECTOR_MODE_INFOS,MOUSE_SELECTOR_MODE:MOUSE_SELECTOR_MODE};});'use strict';Polymer({is:'tr-ui-b-mouse-mode-icon',properties:{modeName:{type:String,reflectToAttribute:true,observer:'modeNameChanged'},},created:function(){this.active_=false;this.acceleratorKey_=undefined;},ready:function(){this.updateContents_();},get mode(){return tr.ui.b.MOUSE_SELECTOR_MODE[this.modeName];},set mode(mode){var modeInfo=tr.ui.b.MOUSE_SELECTOR_MODE_INFOS[mode];var modeName=tr.b.findFirstKeyInDictMatching(tr.ui.b.MOUSE_SELECTOR_MODE,function(modeName,candidateMode){return candidateMode===mode;});if(modeName===undefined)
-throw new Error('Unknown mode');this.modeName=modeName;},modeNameChanged:function(){this.updateContents_();},get active(){return this.active_;},set active(active){this.active_=!!active;if(this.active_)
-Polymer.dom(this).classList.add('active');else
-Polymer.dom(this).classList.remove('active');this.updateContents_();},get acceleratorKey(){return this.acceleratorKey_;},set acceleratorKey(acceleratorKey){this.acceleratorKey_=acceleratorKey;this.updateContents_();},updateContents_:function(){if(this.modeName===undefined)
-return;var mode=this.mode;if(mode===undefined)
-throw new Error('Invalid mode');var modeInfo=tr.ui.b.MOUSE_SELECTOR_MODE_INFOS[mode];if(!modeInfo)
-throw new Error('Invalid mode');var title=modeInfo.title;if(this.acceleratorKey_)
-title=title+' ('+this.acceleratorKey_+')';this.title=title;var bp;if(this.active_)
-bp=modeInfo.activeBackgroundPosition;else
-bp=modeInfo.defaultBackgroundPosition;this.style.backgroundPosition=bp;}});'use strict';tr.exportTo('tr.ui.b',function(){function MouseTracker(opt_targetElement){this.onMouseDown_=this.onMouseDown_.bind(this);this.onMouseMove_=this.onMouseMove_.bind(this);this.onMouseUp_=this.onMouseUp_.bind(this);this.targetElement=opt_targetElement;}
-MouseTracker.prototype={get targetElement(){return this.targetElement_;},set targetElement(targetElement){if(this.targetElement_)
-this.targetElement_.removeEventListener('mousedown',this.onMouseDown_);this.targetElement_=targetElement;if(this.targetElement_)
-this.targetElement_.addEventListener('mousedown',this.onMouseDown_);},onMouseDown_:function(e){if(e.button!==0)
-return true;e=this.remakeEvent_(e,'mouse-tracker-start');this.targetElement_.dispatchEvent(e);document.addEventListener('mousemove',this.onMouseMove_);document.addEventListener('mouseup',this.onMouseUp_);this.targetElement_.addEventListener('blur',this.onMouseUp_);this.savePreviousUserSelect_=document.body.style['-webkit-user-select'];document.body.style['-webkit-user-select']='none';e.preventDefault();return true;},onMouseMove_:function(e){e=this.remakeEvent_(e,'mouse-tracker-move');this.targetElement_.dispatchEvent(e);},onMouseUp_:function(e){document.removeEventListener('mousemove',this.onMouseMove_);document.removeEventListener('mouseup',this.onMouseUp_);this.targetElement_.removeEventListener('blur',this.onMouseUp_);document.body.style['-webkit-user-select']=this.savePreviousUserSelect_;e=this.remakeEvent_(e,'mouse-tracker-end');this.targetElement_.dispatchEvent(e);},remakeEvent_:function(e,newType){var remade=new tr.b.Event(newType,true,true);remade.x=e.x;remade.y=e.y;remade.offsetX=e.offsetX;remade.offsetY=e.offsetY;remade.clientX=e.clientX;remade.clientY=e.clientY;return remade;}};function trackMouseMovesUntilMouseUp(mouseMoveHandler,opt_mouseUpHandler,opt_keyUpHandler){function cleanupAndDispatchToMouseUp(e){document.removeEventListener('mousemove',mouseMoveHandler);if(opt_keyUpHandler)
-document.removeEventListener('keyup',opt_keyUpHandler);document.removeEventListener('mouseup',cleanupAndDispatchToMouseUp);if(opt_mouseUpHandler)
-opt_mouseUpHandler(e);}
-document.addEventListener('mousemove',mouseMoveHandler);if(opt_keyUpHandler)
-document.addEventListener('keyup',opt_keyUpHandler);document.addEventListener('mouseup',cleanupAndDispatchToMouseUp);}
-return{MouseTracker:MouseTracker,trackMouseMovesUntilMouseUp:trackMouseMovesUntilMouseUp};});'use strict';tr.exportTo('tr.ui.b',function(){var MOUSE_SELECTOR_MODE=tr.ui.b.MOUSE_SELECTOR_MODE;var MOUSE_SELECTOR_MODE_INFOS=tr.ui.b.MOUSE_SELECTOR_MODE_INFOS;var MIN_MOUSE_SELECTION_DISTANCE=4;var MODIFIER={SHIFT:0x1,SPACE:0x2,CMD_OR_CTRL:0x4};function isCmdOrCtrlPressed(event){if(tr.isMac)
-return event.metaKey;else
-return event.ctrlKey;}
-Polymer({is:'tr-ui-b-mouse-mode-selector',created:function(){this.supportedModeMask_=MOUSE_SELECTOR_MODE.ALL_MODES;this.initialRelativeMouseDownPos_={x:0,y:0};this.defaultMode_=MOUSE_SELECTOR_MODE.PANSCAN;this.settingsKey_=undefined;this.mousePos_={x:0,y:0};this.mouseDownPos_={x:0,y:0};this.onMouseDown_=this.onMouseDown_.bind(this);this.onMouseMove_=this.onMouseMove_.bind(this);this.onMouseUp_=this.onMouseUp_.bind(this);this.onKeyDown_=this.onKeyDown_.bind(this);this.onKeyUp_=this.onKeyUp_.bind(this);this.mode_=undefined;this.modeToKeyCodeMap_={};this.modifierToModeMap_={};this.targetElement_=undefined;this.modeBeforeAlternativeModeActivated_=null;this.isInteracting_=false;this.isClick_=false;},ready:function(){this.buttonsEl_=Polymer.dom(this.root).querySelector('.buttons');this.dragHandleEl_=Polymer.dom(this.root).querySelector('.drag-handle');this.supportedModeMask=MOUSE_SELECTOR_MODE.ALL_MODES;this.dragHandleEl_.addEventListener('mousedown',this.onDragHandleMouseDown_.bind(this));this.buttonsEl_.addEventListener('mouseup',this.onButtonMouseUp_);this.buttonsEl_.addEventListener('mousedown',this.onButtonMouseDown_);this.buttonsEl_.addEventListener('click',this.onButtonPress_.bind(this));},attached:function(){document.addEventListener('keydown',this.onKeyDown_);document.addEventListener('keyup',this.onKeyUp_);},detached:function(){document.removeEventListener('keydown',this.onKeyDown_);document.removeEventListener('keyup',this.onKeyUp_);},get targetElement(){return this.targetElement_;},set targetElement(target){if(this.targetElement_)
-this.targetElement_.removeEventListener('mousedown',this.onMouseDown_);this.targetElement_=target;if(this.targetElement_)
-this.targetElement_.addEventListener('mousedown',this.onMouseDown_);},get defaultMode(){return this.defaultMode_;},set defaultMode(defaultMode){this.defaultMode_=defaultMode;},get settingsKey(){return this.settingsKey_;},set settingsKey(settingsKey){this.settingsKey_=settingsKey;if(!this.settingsKey_)
-return;var mode=tr.b.Settings.get(this.settingsKey_+'.mode',undefined);if(MOUSE_SELECTOR_MODE_INFOS[mode]===undefined)
-mode=undefined;if((mode&this.supportedModeMask_)===0)
-mode=undefined;if(!mode)
-mode=this.defaultMode_;this.mode=mode;var pos=tr.b.Settings.get(this.settingsKey_+'.pos',undefined);if(pos)
-this.pos=pos;},get supportedModeMask(){return this.supportedModeMask_;},set supportedModeMask(supportedModeMask){if(this.mode&&(supportedModeMask&this.mode)===0)
-throw new Error('supportedModeMask must include current mode.');function createButtonForMode(mode){return button;}
-this.supportedModeMask_=supportedModeMask;Polymer.dom(this.buttonsEl_).textContent='';for(var modeName in MOUSE_SELECTOR_MODE){if(modeName=='ALL_MODES')
-continue;var mode=MOUSE_SELECTOR_MODE[modeName];if((this.supportedModeMask_&mode)===0)
-continue;var button=document.createElement('tr-ui-b-mouse-mode-icon');button.mode=mode;Polymer.dom(button).classList.add('tool-button');Polymer.dom(this.buttonsEl_).appendChild(button);}},getButtonForMode_:function(mode){for(var i=0;i<this.buttonsEl_.children.length;i++){var buttonEl=this.buttonsEl_.children[i];if(buttonEl.mode===mode)
-return buttonEl;}
-return undefined;},get mode(){return this.currentMode_;},set mode(newMode){if(newMode!==undefined){if(typeof newMode!=='number')
-throw new Error('Mode must be a number');if((newMode&this.supportedModeMask_)===0)
-throw new Error('Cannot switch to this mode, it is not supported');if(MOUSE_SELECTOR_MODE_INFOS[newMode]===undefined)
-throw new Error('Unrecognized mode');}
-var modeInfo;if(this.currentMode_===newMode)
-return;if(this.currentMode_){var buttonEl=this.getButtonForMode_(this.currentMode_);if(buttonEl)
-buttonEl.active=false;if(this.isInteracting_){var mouseEvent=this.createEvent_(MOUSE_SELECTOR_MODE_INFOS[this.mode].eventNames.end);this.dispatchEvent(mouseEvent);}
+this.doneDecoratingForNow_();},decorateChild_:function(child){throw new Error('Not implemented');},undecorateChild_:function(child){throw new Error('Not implemented');},beginDecorating_:function(){},doneDecoratingForNow_:function(){}};return{ContainerThatDecoratesItsChildren,};});'use strict';tr.exportTo('tr.ui.b',function(){var ListView=tr.ui.b.define('x-list-view',tr.ui.b.ContainerThatDecoratesItsChildren);ListView.prototype={__proto__:tr.ui.b.ContainerThatDecoratesItsChildren.prototype,decorate:function(){tr.ui.b.ContainerThatDecoratesItsChildren.prototype.decorate.call(this);Polymer.dom(this).classList.add('x-list-view');this.onItemClicked_=this.onItemClicked_.bind(this);this.onKeyDown_=this.onKeyDown_.bind(this);this.tabIndex=0;this.addEventListener('keydown',this.onKeyDown_);this.selectionChanged_=false;},decorateChild_:function(item){Polymer.dom(item).classList.add('list-item');item.addEventListener('click',this.onItemClicked_,true);Object.defineProperty(item,'selected',{configurable:true,get:()=>item.hasAttribute('selected'),set:value=>{const oldSelection=this.selectedElement;if(oldSelection&&oldSelection!==item&&value){Polymer.dom(this.selectedElement).removeAttribute('selected');}
+if(value){Polymer.dom(item).setAttribute('selected','selected');}else{Polymer.dom(item).removeAttribute('selected');}
+const newSelection=this.selectedElement;if(newSelection!==oldSelection){tr.b.dispatchSimpleEvent(this,'selection-changed',false);}},});},undecorateChild_:function(item){this.selectionChanged_|=item.selected;Polymer.dom(item).classList.remove('list-item');item.removeEventListener('click',this.onItemClicked_);delete item.selected;},beginDecorating_:function(){this.selectionChanged_=false;},doneDecoratingForNow_:function(){if(this.selectionChanged_){tr.b.dispatchSimpleEvent(this,'selection-changed',false);}},get selectedElement(){var el=Polymer.dom(this).querySelector('.list-item[selected]');if(!el)return undefined;return el;},set selectedElement(el){if(!el){if(this.selectedElement){this.selectedElement.selected=false;}
+return;}
+if(el.parentElement!==this){throw new Error('Can only select elements that are children of this list view');}
+el.selected=true;},getElementByIndex:function(index){return Polymer.dom(this).querySelector('.list-item:nth-child('+index+')');},clear:function(){var changed=this.selectedElement!==undefined;tr.ui.b.ContainerThatDecoratesItsChildren.prototype.clear.call(this);if(changed){tr.b.dispatchSimpleEvent(this,'selection-changed',false);}},onItemClicked_:function(e){var currentSelectedElement=this.selectedElement;if(currentSelectedElement){Polymer.dom(currentSelectedElement).removeAttribute('selected');}
+var element=e.target;while(element.parentElement!==this){element=element.parentElement;}
+if(element!==currentSelectedElement){Polymer.dom(element).setAttribute('selected','selected');}
+tr.b.dispatchSimpleEvent(this,'selection-changed',false);},onKeyDown_:function(e){if(this.selectedElement===undefined)return;if(e.keyCode===38){var prev=Polymer.dom(this.selectedElement).previousSibling;if(prev){prev.selected=true;tr.ui.b.scrollIntoViewIfNeeded(prev);e.preventDefault();return true;}}else if(e.keyCode===40){var next=Polymer.dom(this.selectedElement).nextSibling;if(next){next.selected=true;tr.ui.b.scrollIntoViewIfNeeded(next);e.preventDefault();return true;}}},addItem:function(textContent){var item=document.createElement('div');Polymer.dom(item).textContent=textContent;Polymer.dom(this).appendChild(item);return item;}};return{ListView,};});'use strict';tr.exportTo('tr.ui.b',function(){var MOUSE_SELECTOR_MODE={};MOUSE_SELECTOR_MODE.SELECTION=0x1;MOUSE_SELECTOR_MODE.PANSCAN=0x2;MOUSE_SELECTOR_MODE.ZOOM=0x4;MOUSE_SELECTOR_MODE.TIMING=0x8;MOUSE_SELECTOR_MODE.ROTATE=0x10;MOUSE_SELECTOR_MODE.ALL_MODES=0x1F;var MOUSE_SELECTOR_MODE_INFOS={};MOUSE_SELECTOR_MODE_INFOS[MOUSE_SELECTOR_MODE.PANSCAN]={mode:MOUSE_SELECTOR_MODE.PANSCAN,title:'pan',eventNames:{enter:'enterpan',begin:'beginpan',update:'updatepan',end:'endpan',exit:'exitpan'},activeBackgroundPosition:'-30px -10px',defaultBackgroundPosition:'0 -10px'};MOUSE_SELECTOR_MODE_INFOS[MOUSE_SELECTOR_MODE.SELECTION]={mode:MOUSE_SELECTOR_MODE.SELECTION,title:'selection',eventNames:{enter:'enterselection',begin:'beginselection',update:'updateselection',end:'endselection',exit:'exitselection'},activeBackgroundPosition:'-30px -40px',defaultBackgroundPosition:'0 -40px'};MOUSE_SELECTOR_MODE_INFOS[MOUSE_SELECTOR_MODE.ZOOM]={mode:MOUSE_SELECTOR_MODE.ZOOM,title:'zoom',eventNames:{enter:'enterzoom',begin:'beginzoom',update:'updatezoom',end:'endzoom',exit:'exitzoom'},activeBackgroundPosition:'-30px -70px',defaultBackgroundPosition:'0 -70px'};MOUSE_SELECTOR_MODE_INFOS[MOUSE_SELECTOR_MODE.TIMING]={mode:MOUSE_SELECTOR_MODE.TIMING,title:'timing',eventNames:{enter:'entertiming',begin:'begintiming',update:'updatetiming',end:'endtiming',exit:'exittiming'},activeBackgroundPosition:'-30px -100px',defaultBackgroundPosition:'0 -100px'};MOUSE_SELECTOR_MODE_INFOS[MOUSE_SELECTOR_MODE.ROTATE]={mode:MOUSE_SELECTOR_MODE.ROTATE,title:'rotate',eventNames:{enter:'enterrotate',begin:'beginrotate',update:'updaterotate',end:'endrotate',exit:'exitrotate'},activeBackgroundPosition:'-30px -130px',defaultBackgroundPosition:'0 -130px'};return{MOUSE_SELECTOR_MODE_INFOS,MOUSE_SELECTOR_MODE,};});'use strict';Polymer({is:'tr-ui-b-mouse-mode-icon',properties:{modeName:{type:String,reflectToAttribute:true,observer:'modeNameChanged'},},created:function(){this.active_=false;this.acceleratorKey_=undefined;},ready:function(){this.updateContents_();},get mode(){return tr.ui.b.MOUSE_SELECTOR_MODE[this.modeName];},set mode(mode){var modeInfo=tr.ui.b.MOUSE_SELECTOR_MODE_INFOS[mode];var modeName=tr.b.findFirstKeyInDictMatching(tr.ui.b.MOUSE_SELECTOR_MODE,function(modeName,candidateMode){return candidateMode===mode;});if(modeName===undefined){throw new Error('Unknown mode');}
+this.modeName=modeName;},modeNameChanged:function(){this.updateContents_();},get active(){return this.active_;},set active(active){this.active_=!!active;if(this.active_){Polymer.dom(this).classList.add('active');}else{Polymer.dom(this).classList.remove('active');}
+this.updateContents_();},get acceleratorKey(){return this.acceleratorKey_;},set acceleratorKey(acceleratorKey){this.acceleratorKey_=acceleratorKey;this.updateContents_();},updateContents_:function(){if(this.modeName===undefined)return;var mode=this.mode;if(mode===undefined){throw new Error('Invalid mode');}
+var modeInfo=tr.ui.b.MOUSE_SELECTOR_MODE_INFOS[mode];if(!modeInfo){throw new Error('Invalid mode');}
+var title=modeInfo.title;if(this.acceleratorKey_){title=title+' ('+this.acceleratorKey_+')';}
+this.title=title;var bp;if(this.active_){bp=modeInfo.activeBackgroundPosition;}else{bp=modeInfo.defaultBackgroundPosition;}
+this.style.backgroundPosition=bp;}});'use strict';tr.exportTo('tr.ui.b',function(){function MouseTracker(opt_targetElement){this.onMouseDown_=this.onMouseDown_.bind(this);this.onMouseMove_=this.onMouseMove_.bind(this);this.onMouseUp_=this.onMouseUp_.bind(this);this.targetElement=opt_targetElement;}
+MouseTracker.prototype={get targetElement(){return this.targetElement_;},set targetElement(targetElement){if(this.targetElement_){this.targetElement_.removeEventListener('mousedown',this.onMouseDown_);}
+this.targetElement_=targetElement;if(this.targetElement_){this.targetElement_.addEventListener('mousedown',this.onMouseDown_);}},onMouseDown_:function(e){if(e.button!==0)return true;e=this.remakeEvent_(e,'mouse-tracker-start');this.targetElement_.dispatchEvent(e);document.addEventListener('mousemove',this.onMouseMove_);document.addEventListener('mouseup',this.onMouseUp_);this.targetElement_.addEventListener('blur',this.onMouseUp_);this.savePreviousUserSelect_=document.body.style['-webkit-user-select'];document.body.style['-webkit-user-select']='none';e.preventDefault();return true;},onMouseMove_:function(e){e=this.remakeEvent_(e,'mouse-tracker-move');this.targetElement_.dispatchEvent(e);},onMouseUp_:function(e){document.removeEventListener('mousemove',this.onMouseMove_);document.removeEventListener('mouseup',this.onMouseUp_);this.targetElement_.removeEventListener('blur',this.onMouseUp_);document.body.style['-webkit-user-select']=this.savePreviousUserSelect_;e=this.remakeEvent_(e,'mouse-tracker-end');this.targetElement_.dispatchEvent(e);},remakeEvent_:function(e,newType){var remade=new tr.b.Event(newType,true,true);remade.x=e.x;remade.y=e.y;remade.offsetX=e.offsetX;remade.offsetY=e.offsetY;remade.clientX=e.clientX;remade.clientY=e.clientY;return remade;}};function trackMouseMovesUntilMouseUp(mouseMoveHandler,opt_mouseUpHandler,opt_keyUpHandler){function cleanupAndDispatchToMouseUp(e){document.removeEventListener('mousemove',mouseMoveHandler);if(opt_keyUpHandler){document.removeEventListener('keyup',opt_keyUpHandler);}
+document.removeEventListener('mouseup',cleanupAndDispatchToMouseUp);if(opt_mouseUpHandler){opt_mouseUpHandler(e);}}
+document.addEventListener('mousemove',mouseMoveHandler);if(opt_keyUpHandler){document.addEventListener('keyup',opt_keyUpHandler);}
+document.addEventListener('mouseup',cleanupAndDispatchToMouseUp);}
+return{MouseTracker,trackMouseMovesUntilMouseUp,};});'use strict';tr.exportTo('tr.ui.b',function(){var MOUSE_SELECTOR_MODE=tr.ui.b.MOUSE_SELECTOR_MODE;var MOUSE_SELECTOR_MODE_INFOS=tr.ui.b.MOUSE_SELECTOR_MODE_INFOS;var MIN_MOUSE_SELECTION_DISTANCE=4;var MODIFIER={SHIFT:0x1,SPACE:0x2,CMD_OR_CTRL:0x4};function isCmdOrCtrlPressed(event){if(tr.isMac)return event.metaKey;return event.ctrlKey;}
+Polymer({is:'tr-ui-b-mouse-mode-selector',created:function(){this.supportedModeMask_=MOUSE_SELECTOR_MODE.ALL_MODES;this.initialRelativeMouseDownPos_={x:0,y:0};this.defaultMode_=MOUSE_SELECTOR_MODE.PANSCAN;this.settingsKey_=undefined;this.mousePos_={x:0,y:0};this.mouseDownPos_={x:0,y:0};this.onMouseDown_=this.onMouseDown_.bind(this);this.onMouseMove_=this.onMouseMove_.bind(this);this.onMouseUp_=this.onMouseUp_.bind(this);this.onKeyDown_=this.onKeyDown_.bind(this);this.onKeyUp_=this.onKeyUp_.bind(this);this.mode_=undefined;this.modeToKeyCodeMap_={};this.modifierToModeMap_={};this.targetElement_=undefined;this.modeBeforeAlternativeModeActivated_=null;this.isInteracting_=false;this.isClick_=false;},ready:function(){this.buttonsEl_=Polymer.dom(this.root).querySelector('.buttons');this.dragHandleEl_=Polymer.dom(this.root).querySelector('.drag-handle');this.supportedModeMask=MOUSE_SELECTOR_MODE.ALL_MODES;this.dragHandleEl_.addEventListener('mousedown',this.onDragHandleMouseDown_.bind(this));this.buttonsEl_.addEventListener('mouseup',this.onButtonMouseUp_);this.buttonsEl_.addEventListener('mousedown',this.onButtonMouseDown_);this.buttonsEl_.addEventListener('click',this.onButtonPress_.bind(this));},attached:function(){document.addEventListener('keydown',this.onKeyDown_);document.addEventListener('keyup',this.onKeyUp_);},detached:function(){document.removeEventListener('keydown',this.onKeyDown_);document.removeEventListener('keyup',this.onKeyUp_);},get targetElement(){return this.targetElement_;},set targetElement(target){if(this.targetElement_){this.targetElement_.removeEventListener('mousedown',this.onMouseDown_);}
+this.targetElement_=target;if(this.targetElement_){this.targetElement_.addEventListener('mousedown',this.onMouseDown_);}},get defaultMode(){return this.defaultMode_;},set defaultMode(defaultMode){this.defaultMode_=defaultMode;},get settingsKey(){return this.settingsKey_;},set settingsKey(settingsKey){this.settingsKey_=settingsKey;if(!this.settingsKey_)return;var mode=tr.b.Settings.get(this.settingsKey_+'.mode',undefined);if(MOUSE_SELECTOR_MODE_INFOS[mode]===undefined){mode=undefined;}
+if((mode&this.supportedModeMask_)===0){mode=undefined;}
+if(!mode)mode=this.defaultMode_;this.mode=mode;var pos=tr.b.Settings.get(this.settingsKey_+'.pos',undefined);if(pos)this.pos=pos;},get supportedModeMask(){return this.supportedModeMask_;},set supportedModeMask(supportedModeMask){if(this.mode&&(supportedModeMask&this.mode)===0){throw new Error('supportedModeMask must include current mode.');}
+function createButtonForMode(mode){return button;}
+this.supportedModeMask_=supportedModeMask;Polymer.dom(this.buttonsEl_).textContent='';for(var modeName in MOUSE_SELECTOR_MODE){if(modeName==='ALL_MODES')continue;var mode=MOUSE_SELECTOR_MODE[modeName];if((this.supportedModeMask_&mode)===0)continue;var button=document.createElement('tr-ui-b-mouse-mode-icon');button.mode=mode;Polymer.dom(button).classList.add('tool-button');Polymer.dom(this.buttonsEl_).appendChild(button);}},getButtonForMode_:function(mode){for(var i=0;i<this.buttonsEl_.children.length;i++){var buttonEl=this.buttonsEl_.children[i];if(buttonEl.mode===mode){return buttonEl;}}
+return undefined;},get mode(){return this.currentMode_;},set mode(newMode){if(newMode!==undefined){if(typeof newMode!=='number'){throw new Error('Mode must be a number');}
+if((newMode&this.supportedModeMask_)===0){throw new Error('Cannot switch to this mode, it is not supported');}
+if(MOUSE_SELECTOR_MODE_INFOS[newMode]===undefined){throw new Error('Unrecognized mode');}}
+var modeInfo;if(this.currentMode_===newMode)return;if(this.currentMode_){var buttonEl=this.getButtonForMode_(this.currentMode_);if(buttonEl)buttonEl.active=false;if(this.isInteracting_){var mouseEvent=this.createEvent_(MOUSE_SELECTOR_MODE_INFOS[this.mode].eventNames.end);this.dispatchEvent(mouseEvent);}
 modeInfo=MOUSE_SELECTOR_MODE_INFOS[this.currentMode_];tr.b.dispatchSimpleEvent(this,modeInfo.eventNames.exit,true);}
-this.currentMode_=newMode;if(this.currentMode_){var buttonEl=this.getButtonForMode_(this.currentMode_);if(buttonEl)
-buttonEl.active=true;this.mouseDownPos_.x=this.mousePos_.x;this.mouseDownPos_.y=this.mousePos_.y;modeInfo=MOUSE_SELECTOR_MODE_INFOS[this.currentMode_];if(!this.isInAlternativeMode_)
-tr.b.dispatchSimpleEvent(this,modeInfo.eventNames.enter,true);if(this.isInteracting_){var mouseEvent=this.createEvent_(MOUSE_SELECTOR_MODE_INFOS[this.mode].eventNames.begin);this.dispatchEvent(mouseEvent);}}
-if(this.settingsKey_&&!this.isInAlternativeMode_)
-tr.b.Settings.set(this.settingsKey_+'.mode',this.mode);},setKeyCodeForMode:function(mode,keyCode){if((mode&this.supportedModeMask_)===0)
-throw new Error('Mode not supported');this.modeToKeyCodeMap_[mode]=keyCode;if(!this.buttonsEl_)
-return;var buttonEl=this.getButtonForMode_(mode);if(buttonEl)
-buttonEl.acceleratorKey=String.fromCharCode(keyCode);},setCurrentMousePosFromEvent_:function(e){this.mousePos_.x=e.clientX;this.mousePos_.y=e.clientY;},createEvent_:function(eventName,sourceEvent){var event=new tr.b.Event(eventName,true);event.clientX=this.mousePos_.x;event.clientY=this.mousePos_.y;event.deltaX=this.mousePos_.x-this.mouseDownPos_.x;event.deltaY=this.mousePos_.y-this.mouseDownPos_.y;event.mouseDownX=this.mouseDownPos_.x;event.mouseDownY=this.mouseDownPos_.y;event.didPreventDefault=false;event.preventDefault=function(){event.didPreventDefault=true;if(sourceEvent)
-sourceEvent.preventDefault();};event.stopPropagation=function(){sourceEvent.stopPropagation();};event.stopImmediatePropagation=function(){throw new Error('Not implemented');};return event;},onMouseDown_:function(e){if(e.button!==0)
-return;this.setCurrentMousePosFromEvent_(e);var mouseEvent=this.createEvent_(MOUSE_SELECTOR_MODE_INFOS[this.mode].eventNames.begin,e);if(this.mode===MOUSE_SELECTOR_MODE.SELECTION)
-mouseEvent.appendSelection=isCmdOrCtrlPressed(e);this.dispatchEvent(mouseEvent);this.isInteracting_=true;this.isClick_=true;tr.ui.b.trackMouseMovesUntilMouseUp(this.onMouseMove_,this.onMouseUp_);},onMouseMove_:function(e){this.setCurrentMousePosFromEvent_(e);var mouseEvent=this.createEvent_(MOUSE_SELECTOR_MODE_INFOS[this.mode].eventNames.update,e);this.dispatchEvent(mouseEvent);if(this.isInteracting_)
-this.checkIsClick_(e);},onMouseUp_:function(e){if(e.button!==0)
-return;var mouseEvent=this.createEvent_(MOUSE_SELECTOR_MODE_INFOS[this.mode].eventNames.end,e);mouseEvent.isClick=this.isClick_;this.dispatchEvent(mouseEvent);if(this.isClick_&&!mouseEvent.didPreventDefault)
-this.dispatchClickEvents_(e);this.isInteracting_=false;this.updateAlternativeModeState_(e);},onButtonMouseDown_:function(e){e.preventDefault();e.stopImmediatePropagation();},onButtonMouseUp_:function(e){e.preventDefault();e.stopImmediatePropagation();},onButtonPress_:function(e){this.modeBeforeAlternativeModeActivated_=undefined;this.mode=e.target.mode;e.preventDefault();},onKeyDown_:function(e){if(e.path[0].tagName=='INPUT')
-return;if(e.keyCode===' '.charCodeAt(0))
-this.spacePressed_=true;this.updateAlternativeModeState_(e);},onKeyUp_:function(e){if(e.path[0].tagName=='INPUT')
-return;if(e.keyCode===' '.charCodeAt(0))
-this.spacePressed_=false;var didHandleKey=false;tr.b.iterItems(this.modeToKeyCodeMap_,function(modeStr,keyCode){if(e.keyCode===keyCode){this.modeBeforeAlternativeModeActivated_=undefined;var mode=parseInt(modeStr);this.mode=mode;didHandleKey=true;}},this);if(didHandleKey){e.preventDefault();e.stopPropagation();return;}
+this.currentMode_=newMode;if(this.currentMode_){var buttonEl=this.getButtonForMode_(this.currentMode_);if(buttonEl)buttonEl.active=true;this.mouseDownPos_.x=this.mousePos_.x;this.mouseDownPos_.y=this.mousePos_.y;modeInfo=MOUSE_SELECTOR_MODE_INFOS[this.currentMode_];if(!this.isInAlternativeMode_){tr.b.dispatchSimpleEvent(this,modeInfo.eventNames.enter,true);}
+if(this.isInteracting_){var mouseEvent=this.createEvent_(MOUSE_SELECTOR_MODE_INFOS[this.mode].eventNames.begin);this.dispatchEvent(mouseEvent);}}
+if(this.settingsKey_&&!this.isInAlternativeMode_){tr.b.Settings.set(this.settingsKey_+'.mode',this.mode);}},setKeyCodeForMode:function(mode,keyCode){if((mode&this.supportedModeMask_)===0){throw new Error('Mode not supported');}
+this.modeToKeyCodeMap_[mode]=keyCode;if(!this.buttonsEl_)return;var buttonEl=this.getButtonForMode_(mode);if(buttonEl){buttonEl.acceleratorKey=String.fromCharCode(keyCode);}},setCurrentMousePosFromEvent_:function(e){this.mousePos_.x=e.clientX;this.mousePos_.y=e.clientY;},createEvent_:function(eventName,sourceEvent){var event=new tr.b.Event(eventName,true);event.clientX=this.mousePos_.x;event.clientY=this.mousePos_.y;event.deltaX=this.mousePos_.x-this.mouseDownPos_.x;event.deltaY=this.mousePos_.y-this.mouseDownPos_.y;event.mouseDownX=this.mouseDownPos_.x;event.mouseDownY=this.mouseDownPos_.y;event.didPreventDefault=false;event.preventDefault=function(){event.didPreventDefault=true;if(sourceEvent){sourceEvent.preventDefault();}};event.stopPropagation=function(){sourceEvent.stopPropagation();};event.stopImmediatePropagation=function(){throw new Error('Not implemented');};return event;},onMouseDown_:function(e){if(e.button!==0)return;this.setCurrentMousePosFromEvent_(e);var mouseEvent=this.createEvent_(MOUSE_SELECTOR_MODE_INFOS[this.mode].eventNames.begin,e);if(this.mode===MOUSE_SELECTOR_MODE.SELECTION){mouseEvent.appendSelection=isCmdOrCtrlPressed(e);}
+this.dispatchEvent(mouseEvent);this.isInteracting_=true;this.isClick_=true;tr.ui.b.trackMouseMovesUntilMouseUp(this.onMouseMove_,this.onMouseUp_);},onMouseMove_:function(e){this.setCurrentMousePosFromEvent_(e);var mouseEvent=this.createEvent_(MOUSE_SELECTOR_MODE_INFOS[this.mode].eventNames.update,e);this.dispatchEvent(mouseEvent);if(this.isInteracting_){this.checkIsClick_(e);}},onMouseUp_:function(e){if(e.button!==0)return;var mouseEvent=this.createEvent_(MOUSE_SELECTOR_MODE_INFOS[this.mode].eventNames.end,e);mouseEvent.isClick=this.isClick_;this.dispatchEvent(mouseEvent);if(this.isClick_&&!mouseEvent.didPreventDefault){this.dispatchClickEvents_(e);}
+this.isInteracting_=false;this.updateAlternativeModeState_(e);},onButtonMouseDown_:function(e){e.preventDefault();e.stopImmediatePropagation();},onButtonMouseUp_:function(e){e.preventDefault();e.stopImmediatePropagation();},onButtonPress_:function(e){this.modeBeforeAlternativeModeActivated_=undefined;this.mode=e.target.mode;e.preventDefault();},onKeyDown_:function(e){if(e.path[0].tagName==='INPUT')return;if(e.keyCode===' '.charCodeAt(0)){this.spacePressed_=true;}
+this.updateAlternativeModeState_(e);},onKeyUp_:function(e){if(e.path[0].tagName==='INPUT')return;if(e.keyCode===' '.charCodeAt(0)){this.spacePressed_=false;}
+var didHandleKey=false;for(var[modeStr,keyCode]of Object.entries(this.modeToKeyCodeMap_)){if(e.keyCode===keyCode){this.modeBeforeAlternativeModeActivated_=undefined;var mode=parseInt(modeStr);this.mode=mode;didHandleKey=true;}}
+if(didHandleKey){e.preventDefault();e.stopPropagation();return;}
 this.updateAlternativeModeState_(e);},updateAlternativeModeState_:function(e){var shiftPressed=e.shiftKey;var spacePressed=this.spacePressed_;var cmdOrCtrlPressed=isCmdOrCtrlPressed(e);var smm=this.supportedModeMask_;var newMode;var isNewModeAnAlternativeMode=false;if(shiftPressed&&(this.modifierToModeMap_[MODIFIER.SHIFT]&smm)!==0){newMode=this.modifierToModeMap_[MODIFIER.SHIFT];isNewModeAnAlternativeMode=true;}else if(spacePressed&&(this.modifierToModeMap_[MODIFIER.SPACE]&smm)!==0){newMode=this.modifierToModeMap_[MODIFIER.SPACE];isNewModeAnAlternativeMode=true;}else if(cmdOrCtrlPressed&&(this.modifierToModeMap_[MODIFIER.CMD_OR_CTRL]&smm)!==0){newMode=this.modifierToModeMap_[MODIFIER.CMD_OR_CTRL];isNewModeAnAlternativeMode=true;}else{if(this.isInAlternativeMode_){newMode=this.modeBeforeAlternativeModeActivated_;isNewModeAnAlternativeMode=false;}else{newMode=undefined;}}
-if(this.mode===newMode||newMode===undefined)
-return;if(isNewModeAnAlternativeMode)
-this.modeBeforeAlternativeModeActivated_=this.mode;this.mode=newMode;},get isInAlternativeMode_(){return!!this.modeBeforeAlternativeModeActivated_;},setModifierForAlternateMode:function(mode,modifier){this.modifierToModeMap_[modifier]=mode;},get pos(){return{x:parseInt(this.style.left),y:parseInt(this.style.top)};},set pos(pos){pos=this.constrainPositionToBounds_(pos);this.style.left=pos.x+'px';this.style.top=pos.y+'px';if(this.settingsKey_)
-tr.b.Settings.set(this.settingsKey_+'.pos',this.pos);},constrainPositionToBounds_:function(pos){var parent=this.offsetParent||document.body;var parentRect=tr.ui.b.windowRectForElement(parent);var top=0;var bottom=parentRect.height-this.offsetHeight;var left=0;var right=parentRect.width-this.offsetWidth;var res={};res.x=Math.max(pos.x,left);res.x=Math.min(res.x,right);res.y=Math.max(pos.y,top);res.y=Math.min(res.y,bottom);return res;},onDragHandleMouseDown_:function(e){e.preventDefault();e.stopImmediatePropagation();var mouseDownPos={x:e.clientX-this.offsetLeft,y:e.clientY-this.offsetTop};tr.ui.b.trackMouseMovesUntilMouseUp(function(e){var pos={};pos.x=e.clientX-mouseDownPos.x;pos.y=e.clientY-mouseDownPos.y;this.pos=pos;}.bind(this));},checkIsClick_:function(e){if(!this.isInteracting_||!this.isClick_)
-return;var deltaX=this.mousePos_.x-this.mouseDownPos_.x;var deltaY=this.mousePos_.y-this.mouseDownPos_.y;var minDist=MIN_MOUSE_SELECTION_DISTANCE;if(deltaX*deltaX+deltaY*deltaY>minDist*minDist)
-this.isClick_=false;},dispatchClickEvents_:function(e){if(!this.isClick_)
-return;var modeInfo=MOUSE_SELECTOR_MODE_INFOS[MOUSE_SELECTOR_MODE.SELECTION];var eventNames=modeInfo.eventNames;var mouseEvent=this.createEvent_(eventNames.begin);mouseEvent.appendSelection=isCmdOrCtrlPressed(e);this.dispatchEvent(mouseEvent);mouseEvent=this.createEvent_(eventNames.end);this.dispatchEvent(mouseEvent);}});return{MIN_MOUSE_SELECTION_DISTANCE:MIN_MOUSE_SELECTION_DISTANCE,MODIFIER:MODIFIER};});'use strict';(function(){var DETAILS_SPLIT_REGEX=/^(\S*)\s*([\S\s]*)$/;Polymer({is:'tr-ui-e-chrome-cc-display-item-list-item',created:function(){Polymer.dom(this).setAttribute('name','');Polymer.dom(this).setAttribute('rawDetails','');Polymer.dom(this).setAttribute('richDetails',undefined);Polymer.dom(this).setAttribute('data_',undefined);},get data(){return this.data_;},set data(data){this.data_=data;if(!data){this.name='DATA MISSING';this.rawDetails='';this.richDetails=undefined;}else if(typeof data==='string'){var match=data.match(DETAILS_SPLIT_REGEX);this.name=match[1];this.rawDetails=match[2];this.richDetails=undefined;}else{this.name=data.name;this.rawDetails='';this.richDetails=data;}},stopPropagation:function(e){e.stopPropagation();},_computeIf:function(richDetails){return richDetails&&richDetails.skp64;},_computeHref:function(richDetails){return'data:application/octet-stream;base64,'+richDetails.skp64;}});})();'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){function Selection(){this.selectionToSetIfClicked=undefined;};Selection.prototype={get specicifity(){throw new Error('Not implemented');},get associatedLayerId(){throw new Error('Not implemented');},get associatedRenderPassId(){throw new Error('Not implemented');},get highlightsByLayerId(){return{};},createAnalysis:function(){throw new Error('Not implemented');},findEquivalent:function(lthi){throw new Error('Not implemented');}};function RenderPassSelection(renderPass,renderPassId){if(!renderPass||(renderPassId===undefined))
-throw new Error('Render pass (with id) is required');this.renderPass_=renderPass;this.renderPassId_=renderPassId;}
-RenderPassSelection.prototype={__proto__:Selection.prototype,get specicifity(){return 1;},get associatedLayerId(){return undefined;},get associatedRenderPassId(){return this.renderPassId_;},get renderPass(){return this.renderPass_;},createAnalysis:function(){var dataView=document.createElement('tr-ui-a-generic-object-view-with-label');dataView.label='RenderPass '+this.renderPassId_;dataView.object=this.renderPass_.args;return dataView;},get title(){return this.renderPass_.objectInstance.typeName;}};function LayerSelection(layer){if(!layer)
-throw new Error('Layer is required');this.layer_=layer;}
-LayerSelection.prototype={__proto__:Selection.prototype,get specicifity(){return 1;},get associatedLayerId(){return this.layer_.layerId;},get associatedRenderPassId(){return undefined;},get layer(){return this.layer_;},createAnalysis:function(){var dataView=document.createElement('tr-ui-a-generic-object-view-with-label');dataView.label='Layer '+this.layer_.layerId;if(this.layer_.usingGpuRasterization)
-dataView.label+=' (GPU-rasterized)';dataView.object=this.layer_.args;return dataView;},get title(){return this.layer_.objectInstance.typeName;},findEquivalent:function(lthi){var layer=lthi.activeTree.findLayerWithId(this.layer_.layerId)||lthi.pendingTree.findLayerWithId(this.layer_.layerId);if(!layer)
-return undefined;return new LayerSelection(layer);}};function TileSelection(tile,opt_data){this.tile_=tile;this.data_=opt_data||{};}
+if(this.mode===newMode||newMode===undefined)return;if(isNewModeAnAlternativeMode){this.modeBeforeAlternativeModeActivated_=this.mode;}
+this.mode=newMode;},get isInAlternativeMode_(){return!!this.modeBeforeAlternativeModeActivated_;},setModifierForAlternateMode:function(mode,modifier){this.modifierToModeMap_[modifier]=mode;},get pos(){return{x:parseInt(this.style.left),y:parseInt(this.style.top)};},set pos(pos){pos=this.constrainPositionToBounds_(pos);this.style.left=pos.x+'px';this.style.top=pos.y+'px';if(this.settingsKey_){tr.b.Settings.set(this.settingsKey_+'.pos',this.pos);}},constrainPositionToBounds_:function(pos){var parent=this.offsetParent||document.body;var parentRect=tr.ui.b.windowRectForElement(parent);var top=0;var bottom=parentRect.height-this.offsetHeight;var left=0;var right=parentRect.width-this.offsetWidth;var res={};res.x=Math.max(pos.x,left);res.x=Math.min(res.x,right);res.y=Math.max(pos.y,top);res.y=Math.min(res.y,bottom);return res;},onDragHandleMouseDown_:function(e){e.preventDefault();e.stopImmediatePropagation();var mouseDownPos={x:e.clientX-this.offsetLeft,y:e.clientY-this.offsetTop};tr.ui.b.trackMouseMovesUntilMouseUp(function(e){var pos={};pos.x=e.clientX-mouseDownPos.x;pos.y=e.clientY-mouseDownPos.y;this.pos=pos;}.bind(this));},checkIsClick_:function(e){if(!this.isInteracting_||!this.isClick_)return;var deltaX=this.mousePos_.x-this.mouseDownPos_.x;var deltaY=this.mousePos_.y-this.mouseDownPos_.y;var minDist=MIN_MOUSE_SELECTION_DISTANCE;if(deltaX*deltaX+deltaY*deltaY>minDist*minDist){this.isClick_=false;}},dispatchClickEvents_:function(e){if(!this.isClick_)return;var modeInfo=MOUSE_SELECTOR_MODE_INFOS[MOUSE_SELECTOR_MODE.SELECTION];var eventNames=modeInfo.eventNames;var mouseEvent=this.createEvent_(eventNames.begin);mouseEvent.appendSelection=isCmdOrCtrlPressed(e);this.dispatchEvent(mouseEvent);mouseEvent=this.createEvent_(eventNames.end);this.dispatchEvent(mouseEvent);}});return{MIN_MOUSE_SELECTION_DISTANCE,MODIFIER,};});'use strict';(function(){var DETAILS_SPLIT_REGEX=/^(\S*)\s*([\S\s]*)$/;Polymer({is:'tr-ui-e-chrome-cc-display-item-list-item',created:function(){Polymer.dom(this).setAttribute('name','');Polymer.dom(this).setAttribute('rawDetails','');Polymer.dom(this).setAttribute('richDetails',undefined);Polymer.dom(this).setAttribute('data_',undefined);},get data(){return this.data_;},set data(data){this.data_=data;if(!data){this.name='DATA MISSING';this.rawDetails='';this.richDetails=undefined;}else if(typeof data==='string'){var match=data.match(DETAILS_SPLIT_REGEX);this.name=match[1];this.rawDetails=match[2];this.richDetails=undefined;}else{this.name=data.name;this.rawDetails='';this.richDetails=data;}},stopPropagation:function(e){e.stopPropagation();},_computeIf:function(richDetails){return richDetails&&richDetails.skp64;},_computeHref:function(richDetails){return'data:application/octet-stream;base64,'+richDetails.skp64;}});})();'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){function Selection(){this.selectionToSetIfClicked=undefined;}
+Selection.prototype={get specicifity(){throw new Error('Not implemented');},get associatedLayerId(){throw new Error('Not implemented');},get associatedRenderPassId(){throw new Error('Not implemented');},get highlightsByLayerId(){return{};},createAnalysis:function(){throw new Error('Not implemented');},findEquivalent:function(lthi){throw new Error('Not implemented');}};function RenderPassSelection(renderPass,renderPassId){if(!renderPass||(renderPassId===undefined)){throw new Error('Render pass (with id) is required');}
+this.renderPass_=renderPass;this.renderPassId_=renderPassId;}
+RenderPassSelection.prototype={__proto__:Selection.prototype,get specicifity(){return 1;},get associatedLayerId(){return undefined;},get associatedRenderPassId(){return this.renderPassId_;},get renderPass(){return this.renderPass_;},createAnalysis:function(){var dataView=document.createElement('tr-ui-a-generic-object-view-with-label');dataView.label='RenderPass '+this.renderPassId_;dataView.object=this.renderPass_.args;return dataView;},get title(){return this.renderPass_.objectInstance.typeName;}};function LayerSelection(layer){if(!layer){throw new Error('Layer is required');}
+this.layer_=layer;}
+LayerSelection.prototype={__proto__:Selection.prototype,get specicifity(){return 1;},get associatedLayerId(){return this.layer_.layerId;},get associatedRenderPassId(){return undefined;},get layer(){return this.layer_;},createAnalysis:function(){var dataView=document.createElement('tr-ui-a-generic-object-view-with-label');dataView.label='Layer '+this.layer_.layerId;if(this.layer_.usingGpuRasterization){dataView.label+=' (GPU-rasterized)';}
+dataView.object=this.layer_.args;return dataView;},get title(){return this.layer_.objectInstance.typeName;},findEquivalent:function(lthi){var layer=lthi.activeTree.findLayerWithId(this.layer_.layerId)||lthi.pendingTree.findLayerWithId(this.layer_.layerId);if(!layer)return undefined;return new LayerSelection(layer);}};function TileSelection(tile,opt_data){this.tile_=tile;this.data_=opt_data||{};}
 TileSelection.prototype={__proto__:Selection.prototype,get specicifity(){return 2;},get associatedLayerId(){return this.tile_.layerId;},get highlightsByLayerId(){var highlights={};highlights[this.tile_.layerId]=[{colorKey:this.tile_.objectInstance.typeName,rect:this.tile_.layerRect}];return highlights;},createAnalysis:function(){var analysis=document.createElement('tr-ui-a-generic-object-view-with-label');analysis.label='Tile '+this.tile_.objectInstance.id+' on layer '+
 this.tile_.layerId;if(this.data_){analysis.object={moreInfo:this.data_,tileArgs:this.tile_.args};}else{analysis.object=this.tile_.args;}
-return analysis;},findEquivalent:function(lthi){var tileInstance=this.tile_.tileInstance;if(lthi.ts<tileInstance.creationTs||lthi.ts>=tileInstance.deletionTs)
-return undefined;var tileSnapshot=tileInstance.getSnapshotAt(lthi.ts);if(!tileSnapshot)
-return undefined;return new TileSelection(tileSnapshot);}};function LayerRectSelection(layer,rectType,rect,opt_data){this.layer_=layer;this.rectType_=rectType;this.rect_=rect;this.data_=opt_data!==undefined?opt_data:rect;}
+return analysis;},findEquivalent:function(lthi){var tileInstance=this.tile_.tileInstance;if(lthi.ts<tileInstance.creationTs||lthi.ts>=tileInstance.deletionTs){return undefined;}
+var tileSnapshot=tileInstance.getSnapshotAt(lthi.ts);if(!tileSnapshot)return undefined;return new TileSelection(tileSnapshot);}};function LayerRectSelection(layer,rectType,rect,opt_data){this.layer_=layer;this.rectType_=rectType;this.rect_=rect;this.data_=opt_data!==undefined?opt_data:rect;}
 LayerRectSelection.prototype={__proto__:Selection.prototype,get specicifity(){return 2;},get associatedLayerId(){return this.layer_.layerId;},get highlightsByLayerId(){var highlights={};highlights[this.layer_.layerId]=[{colorKey:this.rectType_,rect:this.rect_}];return highlights;},createAnalysis:function(){var analysis=document.createElement('tr-ui-a-generic-object-view-with-label');analysis.label=this.rectType_+' on layer '+this.layer_.layerId;analysis.object=this.data_;return analysis;},findEquivalent:function(lthi){return undefined;}};function AnimationRectSelection(layer,rect){this.layer_=layer;this.rect_=rect;}
-AnimationRectSelection.prototype={__proto__:Selection.prototype,get specicifity(){return 0;},get associatedLayerId(){return this.layer_.layerId;},createAnalysis:function(){var analysis=document.createElement('tr-ui-a-generic-object-view-with-label');analysis.label='Animation Bounds of layer '+this.layer_.layerId;analysis.object=this.rect_;return analysis;}};return{Selection:Selection,RenderPassSelection:RenderPassSelection,LayerSelection:LayerSelection,TileSelection:TileSelection,LayerRectSelection:LayerRectSelection,AnimationRectSelection:AnimationRectSelection};});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){var OPS_TIMING_ITERATIONS=3;var ANNOTATION='Comment';var BEGIN_ANNOTATION='BeginCommentGroup';var END_ANNOTATION='EndCommentGroup';var ANNOTATION_ID='ID: ';var ANNOTATION_CLASS='CLASS: ';var ANNOTATION_TAG='TAG: ';var constants=tr.e.cc.constants;var PictureOpsListView=tr.ui.b.define('tr-ui-e-chrome-cc-picture-ops-list-view');PictureOpsListView.prototype={__proto__:HTMLDivElement.prototype,decorate:function(){this.opsList_=new tr.ui.b.ListView();Polymer.dom(this).appendChild(this.opsList_);this.selectedOp_=undefined;this.selectedOpIndex_=undefined;this.opsList_.addEventListener('selection-changed',this.onSelectionChanged_.bind(this));this.picture_=undefined;},get picture(){return this.picture_;},set picture(picture){this.picture_=picture;this.updateContents_();},updateContents_:function(){this.opsList_.clear();if(!this.picture_)
-return;var ops=this.picture_.getOps();if(!ops)
-return;ops=this.picture_.tagOpsWithTimings(ops);ops=this.opsTaggedWithAnnotations_(ops);for(var i=0;i<ops.length;i++){var op=ops[i];var item=document.createElement('div');item.opIndex=op.opIndex;Polymer.dom(item).textContent=i+') '+op.cmd_string;if(op.elementInfo.tag||op.elementInfo.id||op.elementInfo.class){var elementInfo=document.createElement('span');Polymer.dom(elementInfo).classList.add('elementInfo');var tag=op.elementInfo.tag?op.elementInfo.tag:'unknown';var id=op.elementInfo.id?'id='+op.elementInfo.id:undefined;var className=op.elementInfo.class?'class='+
+AnimationRectSelection.prototype={__proto__:Selection.prototype,get specicifity(){return 0;},get associatedLayerId(){return this.layer_.layerId;},createAnalysis:function(){var analysis=document.createElement('tr-ui-a-generic-object-view-with-label');analysis.label='Animation Bounds of layer '+this.layer_.layerId;analysis.object=this.rect_;return analysis;}};return{Selection,RenderPassSelection,LayerSelection,TileSelection,LayerRectSelection,AnimationRectSelection,};});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){var OPS_TIMING_ITERATIONS=3;var ANNOTATION='Comment';var BEGIN_ANNOTATION='BeginCommentGroup';var END_ANNOTATION='EndCommentGroup';var ANNOTATION_ID='ID: ';var ANNOTATION_CLASS='CLASS: ';var ANNOTATION_TAG='TAG: ';var constants=tr.e.cc.constants;var PictureOpsListView=tr.ui.b.define('tr-ui-e-chrome-cc-picture-ops-list-view');PictureOpsListView.prototype={__proto__:HTMLDivElement.prototype,decorate:function(){this.opsList_=new tr.ui.b.ListView();Polymer.dom(this).appendChild(this.opsList_);this.selectedOp_=undefined;this.selectedOpIndex_=undefined;this.opsList_.addEventListener('selection-changed',this.onSelectionChanged_.bind(this));this.picture_=undefined;},get picture(){return this.picture_;},set picture(picture){this.picture_=picture;this.updateContents_();},updateContents_:function(){this.opsList_.clear();if(!this.picture_)return;var ops=this.picture_.getOps();if(!ops)return;ops=this.picture_.tagOpsWithTimings(ops);ops=this.opsTaggedWithAnnotations_(ops);for(var i=0;i<ops.length;i++){var op=ops[i];var item=document.createElement('div');item.opIndex=op.opIndex;Polymer.dom(item).textContent=i+') '+op.cmd_string;if(op.elementInfo.tag||op.elementInfo.id||op.elementInfo.class){var elementInfo=document.createElement('span');Polymer.dom(elementInfo).classList.add('elementInfo');var tag=op.elementInfo.tag?op.elementInfo.tag:'unknown';var id=op.elementInfo.id?'id='+op.elementInfo.id:undefined;var className=op.elementInfo.class?'class='+
 op.elementInfo.class:undefined;Polymer.dom(elementInfo).textContent='<'+tag+(id?' ':'')+
 (id?id:'')+(className?' ':'')+
 (className?className:'')+'>';Polymer.dom(item).appendChild(elementInfo);}
@@ -7155,327 +7179,216 @@
 if(op.cmd_time&&op.cmd_time>=0.0001){var time=document.createElement('span');Polymer.dom(time).classList.add('time');var rounded=op.cmd_time.toFixed(4);Polymer.dom(time).textContent='('+rounded+'ms)';Polymer.dom(item).appendChild(time);}
 Polymer.dom(this.opsList_).appendChild(item);}},onSelectionChanged_:function(e){var beforeSelectedOp=true;if(this.opsList_.selectedElement===this.selectedOp_){this.opsList_.selectedElement=undefined;beforeSelectedOp=false;this.selectedOpIndex_=undefined;}
 this.selectedOp_=this.opsList_.selectedElement;var ops=this.opsList_.children;for(var i=0;i<ops.length;i++){var op=ops[i];if(op===this.selectedOp_){beforeSelectedOp=false;this.selectedOpIndex_=op.opIndex;}else if(beforeSelectedOp){Polymer.dom(op).setAttribute('beforeSelection','beforeSelection');}else{Polymer.dom(op).removeAttribute('beforeSelection');}}
-tr.b.dispatchSimpleEvent(this,'selection-changed',false);},get numOps(){return this.opsList_.children.length;},get selectedOpIndex(){return this.selectedOpIndex_;},set selectedOpIndex(s){this.selectedOpIndex_=s;if(s===undefined){this.opsList_.selectedElement=this.selectedOp_;this.onSelectionChanged_();}else{if(s<0)throw new Error('Invalid index');if(s>=this.numOps)throw new Error('Invalid index');this.opsList_.selectedElement=this.opsList_.getElementByIndex(s+1);tr.ui.b.scrollIntoViewIfNeeded(this.opsList_.selectedElement);}},opsTaggedWithAnnotations_:function(ops){var annotationGroups=new Array();var opsWithoutAnnotations=new Array();for(var opIndex=0;opIndex<ops.length;opIndex++){var op=ops[opIndex];op.opIndex=opIndex;switch(op.cmd_string){case BEGIN_ANNOTATION:annotationGroups.push(new Array());break;case END_ANNOTATION:annotationGroups.pop();break;case ANNOTATION:annotationGroups[annotationGroups.length-1].push(op);break;default:var annotations=new Array();var elementInfo={};annotationGroups.forEach(function(annotationGroup){elementInfo={};annotationGroup.forEach(function(annotation){annotation.info.forEach(function(info){if(info.indexOf(ANNOTATION_TAG)!=-1)
-elementInfo.tag=info.substring(info.indexOf(ANNOTATION_TAG)+
-ANNOTATION_TAG.length).toLowerCase();else if(info.indexOf(ANNOTATION_ID)!=-1)
-elementInfo.id=info.substring(info.indexOf(ANNOTATION_ID)+
-ANNOTATION_ID.length);else if(info.indexOf(ANNOTATION_CLASS)!=-1)
-elementInfo.class=info.substring(info.indexOf(ANNOTATION_CLASS)+
-ANNOTATION_CLASS.length);annotations.push(info);});});});op.annotations=annotations;op.elementInfo=elementInfo;opsWithoutAnnotations.push(op);}}
-return opsWithoutAnnotations;}};return{PictureOpsListView:PictureOpsListView};});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){var THIS_DOC=document.currentScript.ownerDocument;var DisplayItemDebugger=tr.ui.b.define('tr-ui-e-chrome-cc-display-item-debugger');DisplayItemDebugger.prototype={__proto__:HTMLDivElement.prototype,decorate:function(){var node=tr.ui.b.instantiateTemplate('#tr-ui-e-chrome-cc-display-item-debugger-template',THIS_DOC);Polymer.dom(this).appendChild(node);this.pictureAsImageData_=undefined;this.zoomScaleValue_=1;this.sizeInfo_=Polymer.dom(this).querySelector('.size');this.rasterArea_=Polymer.dom(this).querySelector('raster-area');this.rasterCanvas_=Polymer.dom(this.rasterArea_).querySelector('canvas');this.rasterCtx_=this.rasterCanvas_.getContext('2d');this.trackMouse_();this.displayItemInfo_=Polymer.dom(this).querySelector('display-item-info');this.displayItemInfo_.addEventListener('click',this.onDisplayItemInfoClick_.bind(this),false);this.displayItemListView_=new tr.ui.b.ListView();this.displayItemListView_.addEventListener('selection-changed',this.onDisplayItemListSelection_.bind(this));Polymer.dom(this.displayItemInfo_).appendChild(this.displayItemListView_);this.displayListFilename_=Polymer.dom(this).querySelector('.dlfilename');this.displayListExportButton_=Polymer.dom(this).querySelector('.dlexport');this.displayListExportButton_.addEventListener('click',this.onExportDisplayListClicked_.bind(this));this.skpFilename_=Polymer.dom(this).querySelector('.skpfilename');this.skpExportButton_=Polymer.dom(this).querySelector('.skpexport');this.skpExportButton_.addEventListener('click',this.onExportSkPictureClicked_.bind(this));var leftPanel=Polymer.dom(this).querySelector('left-panel');var middleDragHandle=document.createElement('tr-ui-b-drag-handle');middleDragHandle.horizontal=false;middleDragHandle.target=leftPanel;var rightPanel=Polymer.dom(this).querySelector('right-panel');this.infoBar_=document.createElement('tr-ui-b-info-bar');Polymer.dom(this.rasterArea_).insertBefore(this.infoBar_,this.rasterCanvas_);Polymer.dom(this).insertBefore(middleDragHandle,rightPanel);this.picture_=undefined;this.pictureOpsListView_=new tr.ui.e.chrome.cc.PictureOpsListView();Polymer.dom(rightPanel).insertBefore(this.pictureOpsListView_,this.rasterArea_);this.pictureOpsListDragHandle_=document.createElement('tr-ui-b-drag-handle');this.pictureOpsListDragHandle_.horizontal=false;this.pictureOpsListDragHandle_.target=this.pictureOpsListView_;Polymer.dom(rightPanel).insertBefore(this.pictureOpsListDragHandle_,this.rasterArea_);},get picture(){return this.picture_;},set displayItemList(displayItemList){this.displayItemList_=displayItemList;this.picture=this.displayItemList_;this.displayItemListView_.clear();this.displayItemList_.items.forEach(function(item){var listItem=document.createElement('tr-ui-e-chrome-cc-display-item-list-item');listItem.data=item;Polymer.dom(this.displayItemListView_).appendChild(listItem);}.bind(this));},set picture(picture){this.picture_=picture;var showOpsList=picture&&picture!==this.displayItemList_;this.updateDrawOpsList_(showOpsList);if(picture){var size=this.getRasterCanvasSize_();this.rasterCanvas_.width=size.width;this.rasterCanvas_.height=size.height;}
+tr.b.dispatchSimpleEvent(this,'selection-changed',false);},get numOps(){return this.opsList_.children.length;},get selectedOpIndex(){return this.selectedOpIndex_;},set selectedOpIndex(s){this.selectedOpIndex_=s;if(s===undefined){this.opsList_.selectedElement=this.selectedOp_;this.onSelectionChanged_();}else{if(s<0)throw new Error('Invalid index');if(s>=this.numOps)throw new Error('Invalid index');this.opsList_.selectedElement=this.opsList_.getElementByIndex(s+1);tr.ui.b.scrollIntoViewIfNeeded(this.opsList_.selectedElement);}},opsTaggedWithAnnotations_:function(ops){var annotationGroups=[];var opsWithoutAnnotations=[];for(var opIndex=0;opIndex<ops.length;opIndex++){var op=ops[opIndex];op.opIndex=opIndex;switch(op.cmd_string){case BEGIN_ANNOTATION:annotationGroups.push([]);break;case END_ANNOTATION:annotationGroups.pop();break;case ANNOTATION:annotationGroups[annotationGroups.length-1].push(op);break;default:var annotations=[];var elementInfo={};annotationGroups.forEach(function(annotationGroup){elementInfo={};annotationGroup.forEach(function(annotation){annotation.info.forEach(function(info){if(info.includes(ANNOTATION_TAG)){elementInfo.tag=info.substring(info.indexOf(ANNOTATION_TAG)+
+ANNOTATION_TAG.length).toLowerCase();}else if(info.includes(ANNOTATION_ID)){elementInfo.id=info.substring(info.indexOf(ANNOTATION_ID)+
+ANNOTATION_ID.length);}else if(info.includes(ANNOTATION_CLASS)){elementInfo.class=info.substring(info.indexOf(ANNOTATION_CLASS)+
+ANNOTATION_CLASS.length);}
+annotations.push(info);});});});op.annotations=annotations;op.elementInfo=elementInfo;opsWithoutAnnotations.push(op);}}
+return opsWithoutAnnotations;}};return{PictureOpsListView,};});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){var THIS_DOC=document.currentScript.ownerDocument;var DisplayItemDebugger=tr.ui.b.define('tr-ui-e-chrome-cc-display-item-debugger');DisplayItemDebugger.prototype={__proto__:HTMLDivElement.prototype,decorate:function(){var node=tr.ui.b.instantiateTemplate('#tr-ui-e-chrome-cc-display-item-debugger-template',THIS_DOC);Polymer.dom(this).appendChild(node);this.pictureAsImageData_=undefined;this.zoomScaleValue_=1;this.sizeInfo_=Polymer.dom(this).querySelector('.size');this.rasterArea_=Polymer.dom(this).querySelector('raster-area');this.rasterCanvas_=Polymer.dom(this.rasterArea_).querySelector('canvas');this.rasterCtx_=this.rasterCanvas_.getContext('2d');this.trackMouse_();this.displayItemInfo_=Polymer.dom(this).querySelector('display-item-info');this.displayItemInfo_.addEventListener('click',this.onDisplayItemInfoClick_.bind(this),false);this.displayItemListView_=new tr.ui.b.ListView();this.displayItemListView_.addEventListener('selection-changed',this.onDisplayItemListSelection_.bind(this));Polymer.dom(this.displayItemInfo_).appendChild(this.displayItemListView_);this.displayListFilename_=Polymer.dom(this).querySelector('.dlfilename');this.displayListExportButton_=Polymer.dom(this).querySelector('.dlexport');this.displayListExportButton_.addEventListener('click',this.onExportDisplayListClicked_.bind(this));this.skpFilename_=Polymer.dom(this).querySelector('.skpfilename');this.skpExportButton_=Polymer.dom(this).querySelector('.skpexport');this.skpExportButton_.addEventListener('click',this.onExportSkPictureClicked_.bind(this));var leftPanel=Polymer.dom(this).querySelector('left-panel');var middleDragHandle=document.createElement('tr-ui-b-drag-handle');middleDragHandle.horizontal=false;middleDragHandle.target=leftPanel;var rightPanel=Polymer.dom(this).querySelector('right-panel');this.infoBar_=document.createElement('tr-ui-b-info-bar');Polymer.dom(this.rasterArea_).insertBefore(this.infoBar_,this.rasterCanvas_);Polymer.dom(this).insertBefore(middleDragHandle,rightPanel);this.picture_=undefined;this.pictureOpsListView_=new tr.ui.e.chrome.cc.PictureOpsListView();Polymer.dom(rightPanel).insertBefore(this.pictureOpsListView_,this.rasterArea_);this.pictureOpsListDragHandle_=document.createElement('tr-ui-b-drag-handle');this.pictureOpsListDragHandle_.horizontal=false;this.pictureOpsListDragHandle_.target=this.pictureOpsListView_;Polymer.dom(rightPanel).insertBefore(this.pictureOpsListDragHandle_,this.rasterArea_);},get picture(){return this.picture_;},set displayItemList(displayItemList){this.displayItemList_=displayItemList;this.picture=this.displayItemList_;this.displayItemListView_.clear();this.displayItemList_.items.forEach(function(item){var listItem=document.createElement('tr-ui-e-chrome-cc-display-item-list-item');listItem.data=item;Polymer.dom(this.displayItemListView_).appendChild(listItem);}.bind(this));},set picture(picture){this.picture_=picture;var showOpsList=picture&&picture!==this.displayItemList_;this.updateDrawOpsList_(showOpsList);if(picture){var size=this.getRasterCanvasSize_();this.rasterCanvas_.width=size.width;this.rasterCanvas_.height=size.height;}
 var bounds=this.rasterArea_.getBoundingClientRect();var selectorBounds=this.mouseModeSelector_.getBoundingClientRect();this.mouseModeSelector_.pos={x:(bounds.right-selectorBounds.width-10),y:bounds.top};this.rasterize_();this.scheduleUpdateContents_();},getRasterCanvasSize_:function(){var style=window.getComputedStyle(this.rasterArea_);var width=parseInt(style.width);var height=parseInt(style.height);if(this.picture_){width=Math.max(width,this.picture_.layerRect.width);height=Math.max(height,this.picture_.layerRect.height);}
-return{width:width,height:height};},scheduleUpdateContents_:function(){if(this.updateContentsPending_)
-return;this.updateContentsPending_=true;tr.b.requestAnimationFrameInThisFrameIfPossible(this.updateContents_.bind(this));},updateContents_:function(){this.updateContentsPending_=false;if(this.picture_){Polymer.dom(this.sizeInfo_).textContent='('+
+return{width:width,height:height};},scheduleUpdateContents_:function(){if(this.updateContentsPending_)return;this.updateContentsPending_=true;tr.b.requestAnimationFrameInThisFrameIfPossible(this.updateContents_.bind(this));},updateContents_:function(){this.updateContentsPending_=false;if(this.picture_){Polymer.dom(this.sizeInfo_).textContent='('+
 this.picture_.layerRect.width+' x '+
 this.picture_.layerRect.height+')';}
-if(!this.pictureAsImageData_)
-return;this.infoBar_.visible=false;this.infoBar_.removeAllButtons();if(this.pictureAsImageData_.error){this.infoBar_.message='Cannot rasterize...';this.infoBar_.addButton('More info...',function(e){var overlay=new tr.ui.b.Overlay();Polymer.dom(overlay).textContent=this.pictureAsImageData_.error;overlay.visible=true;e.stopPropagation();return false;}.bind(this));this.infoBar_.visible=true;}
-this.drawPicture_();},drawPicture_:function(){var size=this.getRasterCanvasSize_();if(size.width!==this.rasterCanvas_.width)
-this.rasterCanvas_.width=size.width;if(size.height!==this.rasterCanvas_.height)
-this.rasterCanvas_.height=size.height;this.rasterCtx_.clearRect(0,0,size.width,size.height);if(!this.picture_||!this.pictureAsImageData_.imageData)
-return;var imgCanvas=this.pictureAsImageData_.asCanvas();var w=imgCanvas.width;var h=imgCanvas.height;this.rasterCtx_.drawImage(imgCanvas,0,0,w,h,0,0,w*this.zoomScaleValue_,h*this.zoomScaleValue_);},rasterize_:function(){if(this.picture_){this.picture_.rasterize({showOverdraw:false},this.onRasterComplete_.bind(this));}},onRasterComplete_:function(pictureAsImageData){this.pictureAsImageData_=pictureAsImageData;this.scheduleUpdateContents_();},onDisplayItemListSelection_:function(e){var selected=this.displayItemListView_.selectedElement;if(!selected){this.picture=this.displayItemList_;return;}
-var index=Array.prototype.indexOf.call(this.displayItemListView_.children,selected);var displayItem=this.displayItemList_.items[index];if(displayItem&&displayItem.skp64)
-this.picture=new tr.e.cc.Picture(displayItem.skp64,this.displayItemList_.layerRect);else
-this.picture=undefined;},onDisplayItemInfoClick_:function(e){if(e&&e.target==this.displayItemInfo_){this.displayItemListView_.selectedElement=undefined;}},updateDrawOpsList_:function(showOpsList){if(showOpsList){this.pictureOpsListView_.picture=this.picture_;if(this.pictureOpsListView_.numOps>0){Polymer.dom(this.pictureOpsListView_).classList.add('hasPictureOps');Polymer.dom(this.pictureOpsListDragHandle_).classList.add('hasPictureOps');}}else{Polymer.dom(this.pictureOpsListView_).classList.remove('hasPictureOps');Polymer.dom(this.pictureOpsListDragHandle_).classList.remove('hasPictureOps');}},trackMouse_:function(){this.mouseModeSelector_=document.createElement('tr-ui-b-mouse-mode-selector');this.mouseModeSelector_.targetElement=this.rasterArea_;Polymer.dom(this.rasterArea_).appendChild(this.mouseModeSelector_);this.mouseModeSelector_.supportedModeMask=tr.ui.b.MOUSE_SELECTOR_MODE.ZOOM;this.mouseModeSelector_.mode=tr.ui.b.MOUSE_SELECTOR_MODE.ZOOM;this.mouseModeSelector_.defaultMode=tr.ui.b.MOUSE_SELECTOR_MODE.ZOOM;this.mouseModeSelector_.settingsKey='pictureDebugger.mouseModeSelector';this.mouseModeSelector_.addEventListener('beginzoom',this.onBeginZoom_.bind(this));this.mouseModeSelector_.addEventListener('updatezoom',this.onUpdateZoom_.bind(this));this.mouseModeSelector_.addEventListener('endzoom',this.onEndZoom_.bind(this));},onBeginZoom_:function(e){this.isZooming_=true;this.lastMouseViewPos_=this.extractRelativeMousePosition_(e);e.preventDefault();},onUpdateZoom_:function(e){if(!this.isZooming_)
-return;var currentMouseViewPos=this.extractRelativeMousePosition_(e);this.zoomScaleValue_+=((this.lastMouseViewPos_.y-currentMouseViewPos.y)*0.001);this.zoomScaleValue_=Math.max(this.zoomScaleValue_,0.1);this.drawPicture_();this.lastMouseViewPos_=currentMouseViewPos;},onEndZoom_:function(e){this.lastMouseViewPos_=undefined;this.isZooming_=false;e.preventDefault();},extractRelativeMousePosition_:function(e){return{x:e.clientX-this.rasterArea_.offsetLeft,y:e.clientY-this.rasterArea_.offsetTop};},saveFile_:function(filename,rawData){if(!rawData)
-return;var length=rawData.length;var arrayBuffer=new ArrayBuffer(length);var uint8Array=new Uint8Array(arrayBuffer);for(var c=0;c<length;c++)
-uint8Array[c]=rawData.charCodeAt(c);var blob=new Blob([uint8Array],{type:'application/octet-binary'});var blobUrl=window.URL.createObjectURL(blob);var link=document.createElementNS('http://www.w3.org/1999/xhtml','a');link.href=blobUrl;link.download=filename;var event=document.createEvent('MouseEvents');event.initMouseEvent('click',true,false,window,0,0,0,0,0,false,false,false,false,0,null);link.dispatchEvent(event);},onExportDisplayListClicked_:function(){var rawData=JSON.stringify(this.displayItemList_.items);this.saveFile_(this.displayListFilename_.value,rawData);},onExportSkPictureClicked_:function(){var rawData=tr.b.Base64.atob(this.picture_.getBase64SkpData());this.saveFile_(this.skpFilename_.value,rawData);}};return{DisplayItemDebugger:DisplayItemDebugger};});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){var DisplayItemSnapshotView=tr.ui.b.define('tr-ui-e-chrome-cc-display-item-list-view',tr.ui.analysis.ObjectSnapshotView);DisplayItemSnapshotView.prototype={__proto__:tr.ui.analysis.ObjectSnapshotView.prototype,decorate:function(){Polymer.dom(this).classList.add('tr-ui-e-chrome-cc-display-item-list-view');this.displayItemDebugger_=new tr.ui.e.chrome.cc.DisplayItemDebugger();Polymer.dom(this).appendChild(this.displayItemDebugger_);},updateContents:function(){if(this.objectSnapshot_&&this.displayItemDebugger_)
-this.displayItemDebugger_.displayItemList=this.objectSnapshot_;}};tr.ui.analysis.ObjectSnapshotView.register(DisplayItemSnapshotView,{typeNames:['cc::DisplayItemList'],showInstances:false});return{DisplayItemSnapshotView:DisplayItemSnapshotView};});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){var constants=tr.e.cc.constants;var RENDER_PASS_QUADS=Math.max(constants.ACTIVE_TREE,constants.PENDING_TREE)+1;var LayerPicker=tr.ui.b.define('tr-ui-e-chrome-cc-layer-picker');LayerPicker.prototype={__proto__:HTMLUnknownElement.prototype,decorate:function(){this.lthi_=undefined;this.controls_=document.createElement('top-controls');this.renderPassQuads_=false;this.itemList_=new tr.ui.b.ListView();Polymer.dom(this).appendChild(this.controls_);Polymer.dom(this).appendChild(this.itemList_);this.itemList_.addEventListener('selection-changed',this.onItemSelectionChanged_.bind(this));Polymer.dom(this.controls_).appendChild(tr.ui.b.createSelector(this,'whichTree','layerPicker.whichTree',constants.ACTIVE_TREE,[{label:'Active tree',value:constants.ACTIVE_TREE},{label:'Pending tree',value:constants.PENDING_TREE},{label:'Render pass quads',value:RENDER_PASS_QUADS}]));this.showPureTransformLayers_=false;var showPureTransformLayers=tr.ui.b.createCheckBox(this,'showPureTransformLayers','layerPicker.showPureTransformLayers',false,'Transform layers');Polymer.dom(showPureTransformLayers).classList.add('show-transform-layers');showPureTransformLayers.title='When checked, pure transform layers are shown';Polymer.dom(this.controls_).appendChild(showPureTransformLayers);},get lthiSnapshot(){return this.lthiSnapshot_;},set lthiSnapshot(lthiSnapshot){this.lthiSnapshot_=lthiSnapshot;this.updateContents_();},get whichTree(){return this.renderPassQuads_?constants.ACTIVE_TREE:this.whichTree_;},set whichTree(whichTree){this.whichTree_=whichTree;this.renderPassQuads_=(whichTree==RENDER_PASS_QUADS);this.updateContents_();tr.b.dispatchSimpleEvent(this,'selection-change',false);},get layerTreeImpl(){if(this.lthiSnapshot===undefined)
-return undefined;return this.lthiSnapshot.getTree(this.whichTree);},get isRenderPassQuads(){return this.renderPassQuads_;},get showPureTransformLayers(){return this.showPureTransformLayers_;},set showPureTransformLayers(show){if(this.showPureTransformLayers_===show)
-return;this.showPureTransformLayers_=show;this.updateContents_();},getRenderPassInfos_:function(){if(!this.lthiSnapshot_)
-return[];var renderPassInfo=[];if(!this.lthiSnapshot_.args.frame||!this.lthiSnapshot_.args.frame.renderPasses)
-return renderPassInfo;var renderPasses=this.lthiSnapshot_.args.frame.renderPasses;for(var i=0;i<renderPasses.length;++i){var info={renderPass:renderPasses[i],depth:0,id:i,name:'cc::RenderPass'};renderPassInfo.push(info);}
-return renderPassInfo;},getLayerInfos_:function(){if(!this.lthiSnapshot_)
-return[];var tree=this.lthiSnapshot_.getTree(this.whichTree_);if(!tree)
-return[];var layerInfos=[];var showPureTransformLayers=this.showPureTransformLayers_;function isPureTransformLayer(layer){if(layer.args.compositingReasons&&layer.args.compositingReasons.length!=1&&layer.args.compositingReasons[0]!='No reasons given')
-return false;if(layer.args.drawsContent)
-return false;return true;}
-var visitedLayers={};function visitLayer(layer,depth,isMask,isReplica){if(visitedLayers[layer.layerId])
-return;visitedLayers[layer.layerId]=true;var info={layer:layer,depth:depth};if(layer.args.drawsContent)
-info.name=layer.objectInstance.name;else
-info.name='cc::LayerImpl';if(layer.usingGpuRasterization)
-info.name+=' (G)';info.isMaskLayer=isMask;info.replicaLayer=isReplica;if(showPureTransformLayers||!isPureTransformLayer(layer))
-layerInfos.push(info);}
-tree.iterLayers(visitLayer);return layerInfos;},updateContents_:function(){if(this.renderPassQuads_)
-this.updateRenderPassContents_();else
-this.updateLayerContents_();},updateRenderPassContents_:function(){this.itemList_.clear();var selectedRenderPassId;if(this.selection_&&this.selection_.associatedRenderPassId)
-selectedRenderPassId=this.selection_.associatedRenderPassId;var renderPassInfos=this.getRenderPassInfos_();renderPassInfos.forEach(function(renderPassInfo){var renderPass=renderPassInfo.renderPass;var id=renderPassInfo.id;var item=this.createElementWithDepth_(renderPassInfo.depth);var labelEl=Polymer.dom(item).appendChild(tr.ui.b.createSpan());Polymer.dom(labelEl).textContent=renderPassInfo.name+' '+id;item.renderPass=renderPass;item.renderPassId=id;Polymer.dom(this.itemList_).appendChild(item);if(id==selectedRenderPassId){renderPass.selectionState=tr.model.SelectionState.SELECTED;}},this);},updateLayerContents_:function(){this.changingItemSelection_=true;try{this.itemList_.clear();var selectedLayerId;if(this.selection_&&this.selection_.associatedLayerId)
-selectedLayerId=this.selection_.associatedLayerId;var layerInfos=this.getLayerInfos_();layerInfos.forEach(function(layerInfo){var layer=layerInfo.layer;var id=layer.layerId;var item=this.createElementWithDepth_(layerInfo.depth);var labelEl=Polymer.dom(item).appendChild(tr.ui.b.createSpan());Polymer.dom(labelEl).textContent=layerInfo.name+' '+id;var notesEl=Polymer.dom(item).appendChild(tr.ui.b.createSpan());if(layerInfo.isMaskLayer)
-Polymer.dom(notesEl).textContent+='(mask)';if(layerInfo.isReplicaLayer)
-Polymer.dom(notesEl).textContent+='(replica)';if((layer.gpuMemoryUsageInBytes!==undefined)&&(layer.gpuMemoryUsageInBytes>0)){var gpuUsageStr=tr.b.Unit.byName.sizeInBytes.format(layer.gpuMemoryUsageInBytes);Polymer.dom(notesEl).textContent+=' ('+gpuUsageStr+' MiB)';}
-item.layer=layer;Polymer.dom(this.itemList_).appendChild(item);if(layer.layerId==selectedLayerId){layer.selectionState=tr.model.SelectionState.SELECTED;item.selected=true;}},this);}finally{this.changingItemSelection_=false;}},createElementWithDepth_:function(depth){var item=document.createElement('div');var indentEl=Polymer.dom(item).appendChild(tr.ui.b.createSpan());indentEl.style.whiteSpace='pre';for(var i=0;i<depth;i++){Polymer.dom(indentEl).textContent=Polymer.dom(indentEl).textContent+' ';}
-return item;},onItemSelectionChanged_:function(e){if(this.changingItemSelection_)
-return;if(this.renderPassQuads_)
-this.onRenderPassSelected_(e);else
-this.onLayerSelected_(e);tr.b.dispatchSimpleEvent(this,'selection-change',false);},onRenderPassSelected_:function(e){var selectedRenderPass;var selectedRenderPassId;if(this.itemList_.selectedElement){selectedRenderPass=this.itemList_.selectedElement.renderPass;selectedRenderPassId=this.itemList_.selectedElement.renderPassId;}
-if(selectedRenderPass){this.selection_=new tr.ui.e.chrome.cc.RenderPassSelection(selectedRenderPass,selectedRenderPassId);}else{this.selection_=undefined;}},onLayerSelected_:function(e){var selectedLayer;if(this.itemList_.selectedElement)
-selectedLayer=this.itemList_.selectedElement.layer;if(selectedLayer)
-this.selection_=new tr.ui.e.chrome.cc.LayerSelection(selectedLayer);else
-this.selection_=undefined;},get selection(){return this.selection_;},set selection(selection){if(this.selection_==selection)
-return;this.selection_=selection;this.updateContents_();}};return{LayerPicker:LayerPicker};});'use strict';tr.exportTo('tr.e.cc',function(){var ObjectSnapshot=tr.model.ObjectSnapshot;function RenderPassSnapshot(){ObjectSnapshot.apply(this,arguments);}
-RenderPassSnapshot.prototype={__proto__:ObjectSnapshot.prototype,preInitialize:function(){tr.e.cc.preInitializeObject(this);},initialize:function(){tr.e.cc.moveRequiredFieldsFromArgsToToplevel(this,['quadList']);}};ObjectSnapshot.subTypes.register(RenderPassSnapshot,{typeName:'cc::RenderPass'});return{RenderPassSnapshot:RenderPassSnapshot};});'use strict';tr.exportTo('tr.ui.b',function(){var constants={DEFAULT_SCALE:0.5,DEFAULT_EYE_DISTANCE:10000,MINIMUM_DISTANCE:1000,MAXIMUM_DISTANCE:100000,FOV:15,RESCALE_TIMEOUT_MS:200,MAXIMUM_TILT:80,SETTINGS_NAMESPACE:'tr.ui_camera'};var Camera=tr.ui.b.define('camera');Camera.prototype={__proto__:HTMLUnknownElement.prototype,decorate:function(eventSource){this.eventSource_=eventSource;this.eventSource_.addEventListener('beginpan',this.onPanBegin_.bind(this));this.eventSource_.addEventListener('updatepan',this.onPanUpdate_.bind(this));this.eventSource_.addEventListener('endpan',this.onPanEnd_.bind(this));this.eventSource_.addEventListener('beginzoom',this.onZoomBegin_.bind(this));this.eventSource_.addEventListener('updatezoom',this.onZoomUpdate_.bind(this));this.eventSource_.addEventListener('endzoom',this.onZoomEnd_.bind(this));this.eventSource_.addEventListener('beginrotate',this.onRotateBegin_.bind(this));this.eventSource_.addEventListener('updaterotate',this.onRotateUpdate_.bind(this));this.eventSource_.addEventListener('endrotate',this.onRotateEnd_.bind(this));this.eye_=[0,0,constants.DEFAULT_EYE_DISTANCE];this.gazeTarget_=[0,0,0];this.rotation_=[0,0];this.pixelRatio_=window.devicePixelRatio||1;},get modelViewMatrix(){var mvMatrix=mat4.create();mat4.lookAt(mvMatrix,this.eye_,this.gazeTarget_,[0,1,0]);return mvMatrix;},get projectionMatrix(){var rect=tr.ui.b.windowRectForElement(this.canvas_).scaleSize(this.pixelRatio_);var aspectRatio=rect.width/rect.height;var matrix=mat4.create();mat4.perspective(matrix,tr.b.deg2rad(constants.FOV),aspectRatio,1,100000);return matrix;},set canvas(c){this.canvas_=c;},set deviceRect(rect){this.deviceRect_=rect;},get stackingDistanceDampening(){var gazeVector=[this.gazeTarget_[0]-this.eye_[0],this.gazeTarget_[1]-this.eye_[1],this.gazeTarget_[2]-this.eye_[2]];vec3.normalize(gazeVector,gazeVector);return 1+gazeVector[2];},loadCameraFromSettings:function(settings){this.eye_=settings.get('eye',this.eye_,constants.SETTINGS_NAMESPACE);this.gazeTarget_=settings.get('gaze_target',this.gazeTarget_,constants.SETTINGS_NAMESPACE);this.rotation_=settings.get('rotation',this.rotation_,constants.SETTINGS_NAMESPACE);this.dispatchRenderEvent_();},saveCameraToSettings:function(settings){settings.set('eye',this.eye_,constants.SETTINGS_NAMESPACE);settings.set('gaze_target',this.gazeTarget_,constants.SETTINGS_NAMESPACE);settings.set('rotation',this.rotation_,constants.SETTINGS_NAMESPACE);},resetCamera:function(){this.eye_=[0,0,constants.DEFAULT_EYE_DISTANCE];this.gazeTarget_=[0,0,0];this.rotation_=[0,0];var settings=tr.b.SessionSettings();var keys=settings.keys(constants.SETTINGS_NAMESPACE);if(keys.length!==0){this.loadCameraFromSettings(settings);return;}
+if(!this.pictureAsImageData_)return;this.infoBar_.visible=false;this.infoBar_.removeAllButtons();if(this.pictureAsImageData_.error){this.infoBar_.message='Cannot rasterize...';this.infoBar_.addButton('More info...',function(e){var overlay=new tr.ui.b.Overlay();Polymer.dom(overlay).textContent=this.pictureAsImageData_.error;overlay.visible=true;e.stopPropagation();return false;}.bind(this));this.infoBar_.visible=true;}
+this.drawPicture_();},drawPicture_:function(){var size=this.getRasterCanvasSize_();if(size.width!==this.rasterCanvas_.width){this.rasterCanvas_.width=size.width;}
+if(size.height!==this.rasterCanvas_.height){this.rasterCanvas_.height=size.height;}
+this.rasterCtx_.clearRect(0,0,size.width,size.height);if(!this.picture_||!this.pictureAsImageData_.imageData)return;var imgCanvas=this.pictureAsImageData_.asCanvas();var w=imgCanvas.width;var h=imgCanvas.height;this.rasterCtx_.drawImage(imgCanvas,0,0,w,h,0,0,w*this.zoomScaleValue_,h*this.zoomScaleValue_);},rasterize_:function(){if(this.picture_){this.picture_.rasterize({showOverdraw:false},this.onRasterComplete_.bind(this));}},onRasterComplete_:function(pictureAsImageData){this.pictureAsImageData_=pictureAsImageData;this.scheduleUpdateContents_();},onDisplayItemListSelection_:function(e){var selected=this.displayItemListView_.selectedElement;if(!selected){this.picture=this.displayItemList_;return;}
+var index=Array.prototype.indexOf.call(this.displayItemListView_.children,selected);var displayItem=this.displayItemList_.items[index];if(displayItem&&displayItem.skp64){this.picture=new tr.e.cc.Picture(displayItem.skp64,this.displayItemList_.layerRect);}else{this.picture=undefined;}},onDisplayItemInfoClick_:function(e){if(e&&e.target===this.displayItemInfo_){this.displayItemListView_.selectedElement=undefined;}},updateDrawOpsList_:function(showOpsList){if(showOpsList){this.pictureOpsListView_.picture=this.picture_;if(this.pictureOpsListView_.numOps>0){Polymer.dom(this.pictureOpsListView_).classList.add('hasPictureOps');Polymer.dom(this.pictureOpsListDragHandle_).classList.add('hasPictureOps');}}else{Polymer.dom(this.pictureOpsListView_).classList.remove('hasPictureOps');Polymer.dom(this.pictureOpsListDragHandle_).classList.remove('hasPictureOps');}},trackMouse_:function(){this.mouseModeSelector_=document.createElement('tr-ui-b-mouse-mode-selector');this.mouseModeSelector_.targetElement=this.rasterArea_;Polymer.dom(this.rasterArea_).appendChild(this.mouseModeSelector_);this.mouseModeSelector_.supportedModeMask=tr.ui.b.MOUSE_SELECTOR_MODE.ZOOM;this.mouseModeSelector_.mode=tr.ui.b.MOUSE_SELECTOR_MODE.ZOOM;this.mouseModeSelector_.defaultMode=tr.ui.b.MOUSE_SELECTOR_MODE.ZOOM;this.mouseModeSelector_.settingsKey='pictureDebugger.mouseModeSelector';this.mouseModeSelector_.addEventListener('beginzoom',this.onBeginZoom_.bind(this));this.mouseModeSelector_.addEventListener('updatezoom',this.onUpdateZoom_.bind(this));this.mouseModeSelector_.addEventListener('endzoom',this.onEndZoom_.bind(this));},onBeginZoom_:function(e){this.isZooming_=true;this.lastMouseViewPos_=this.extractRelativeMousePosition_(e);e.preventDefault();},onUpdateZoom_:function(e){if(!this.isZooming_)return;var currentMouseViewPos=this.extractRelativeMousePosition_(e);this.zoomScaleValue_+=((this.lastMouseViewPos_.y-currentMouseViewPos.y)*0.001);this.zoomScaleValue_=Math.max(this.zoomScaleValue_,0.1);this.drawPicture_();this.lastMouseViewPos_=currentMouseViewPos;},onEndZoom_:function(e){this.lastMouseViewPos_=undefined;this.isZooming_=false;e.preventDefault();},extractRelativeMousePosition_:function(e){return{x:e.clientX-this.rasterArea_.offsetLeft,y:e.clientY-this.rasterArea_.offsetTop};},saveFile_:function(filename,rawData){if(!rawData)return;var length=rawData.length;var arrayBuffer=new ArrayBuffer(length);var uint8Array=new Uint8Array(arrayBuffer);for(var c=0;c<length;c++){uint8Array[c]=rawData.charCodeAt(c);}
+var blob=new Blob([uint8Array],{type:'application/octet-binary'});var blobUrl=window.URL.createObjectURL(blob);var link=document.createElementNS('http://www.w3.org/1999/xhtml','a');link.href=blobUrl;link.download=filename;var event=document.createEvent('MouseEvents');event.initMouseEvent('click',true,false,window,0,0,0,0,0,false,false,false,false,0,null);link.dispatchEvent(event);},onExportDisplayListClicked_:function(){var rawData=JSON.stringify(this.displayItemList_.items);this.saveFile_(this.displayListFilename_.value,rawData);},onExportSkPictureClicked_:function(){var rawData=tr.b.Base64.atob(this.picture_.getBase64SkpData());this.saveFile_(this.skpFilename_.value,rawData);}};return{DisplayItemDebugger,};});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){var DisplayItemSnapshotView=tr.ui.b.define('tr-ui-e-chrome-cc-display-item-list-view',tr.ui.analysis.ObjectSnapshotView);DisplayItemSnapshotView.prototype={__proto__:tr.ui.analysis.ObjectSnapshotView.prototype,decorate:function(){Polymer.dom(this).classList.add('tr-ui-e-chrome-cc-display-item-list-view');this.displayItemDebugger_=new tr.ui.e.chrome.cc.DisplayItemDebugger();Polymer.dom(this).appendChild(this.displayItemDebugger_);},updateContents:function(){if(this.objectSnapshot_&&this.displayItemDebugger_){this.displayItemDebugger_.displayItemList=this.objectSnapshot_;}}};tr.ui.analysis.ObjectSnapshotView.register(DisplayItemSnapshotView,{typeNames:['cc::DisplayItemList'],showInstances:false});return{DisplayItemSnapshotView,};});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){var constants=tr.e.cc.constants;var RENDER_PASS_QUADS=Math.max(constants.ACTIVE_TREE,constants.PENDING_TREE)+1;var LayerPicker=tr.ui.b.define('tr-ui-e-chrome-cc-layer-picker');LayerPicker.prototype={__proto__:HTMLUnknownElement.prototype,decorate:function(){this.lthi_=undefined;this.controls_=document.createElement('top-controls');this.renderPassQuads_=false;this.itemList_=new tr.ui.b.ListView();Polymer.dom(this).appendChild(this.controls_);Polymer.dom(this).appendChild(this.itemList_);this.itemList_.addEventListener('selection-changed',this.onItemSelectionChanged_.bind(this));Polymer.dom(this.controls_).appendChild(tr.ui.b.createSelector(this,'whichTree','layerPicker.whichTree',constants.ACTIVE_TREE,[{label:'Active tree',value:constants.ACTIVE_TREE},{label:'Pending tree',value:constants.PENDING_TREE},{label:'Render pass quads',value:RENDER_PASS_QUADS}]));this.showPureTransformLayers_=false;var showPureTransformLayers=tr.ui.b.createCheckBox(this,'showPureTransformLayers','layerPicker.showPureTransformLayers',false,'Transform layers');Polymer.dom(showPureTransformLayers).classList.add('show-transform-layers');showPureTransformLayers.title='When checked, pure transform layers are shown';Polymer.dom(this.controls_).appendChild(showPureTransformLayers);},get lthiSnapshot(){return this.lthiSnapshot_;},set lthiSnapshot(lthiSnapshot){this.lthiSnapshot_=lthiSnapshot;this.updateContents_();},get whichTree(){return this.renderPassQuads_?constants.ACTIVE_TREE:this.whichTree_;},set whichTree(whichTree){this.whichTree_=whichTree;this.renderPassQuads_=(whichTree===RENDER_PASS_QUADS);this.updateContents_();tr.b.dispatchSimpleEvent(this,'selection-change',false);},get layerTreeImpl(){if(this.lthiSnapshot===undefined)return undefined;return this.lthiSnapshot.getTree(this.whichTree);},get isRenderPassQuads(){return this.renderPassQuads_;},get showPureTransformLayers(){return this.showPureTransformLayers_;},set showPureTransformLayers(show){if(this.showPureTransformLayers_===show)return;this.showPureTransformLayers_=show;this.updateContents_();},getRenderPassInfos_:function(){if(!this.lthiSnapshot_)return[];var renderPassInfo=[];if(!this.lthiSnapshot_.args.frame||!this.lthiSnapshot_.args.frame.renderPasses){return renderPassInfo;}
+var renderPasses=this.lthiSnapshot_.args.frame.renderPasses;for(var i=0;i<renderPasses.length;++i){var info={renderPass:renderPasses[i],depth:0,id:i,name:'cc::RenderPass'};renderPassInfo.push(info);}
+return renderPassInfo;},getLayerInfos_:function(){if(!this.lthiSnapshot_)return[];var tree=this.lthiSnapshot_.getTree(this.whichTree_);if(!tree)return[];var layerInfos=[];var showPureTransformLayers=this.showPureTransformLayers_;function isPureTransformLayer(layer){if(layer.args.compositingReasons&&layer.args.compositingReasons.length!==1&&layer.args.compositingReasons[0]!=='No reasons given'){return false;}
+if(layer.args.drawsContent)return false;return true;}
+var visitedLayers={};function visitLayer(layer,depth,isMask,isReplica){if(visitedLayers[layer.layerId])return;visitedLayers[layer.layerId]=true;var info={layer:layer,depth:depth};if(layer.args.drawsContent){info.name=layer.objectInstance.name;}else{info.name='cc::LayerImpl';}
+if(layer.usingGpuRasterization){info.name+=' (G)';}
+info.isMaskLayer=isMask;info.replicaLayer=isReplica;if(showPureTransformLayers||!isPureTransformLayer(layer)){layerInfos.push(info);}}
+tree.iterLayers(visitLayer);return layerInfos;},updateContents_:function(){if(this.renderPassQuads_){this.updateRenderPassContents_();}else{this.updateLayerContents_();}},updateRenderPassContents_:function(){this.itemList_.clear();var selectedRenderPassId;if(this.selection_&&this.selection_.associatedRenderPassId){selectedRenderPassId=this.selection_.associatedRenderPassId;}
+var renderPassInfos=this.getRenderPassInfos_();renderPassInfos.forEach(function(renderPassInfo){var renderPass=renderPassInfo.renderPass;var id=renderPassInfo.id;var item=this.createElementWithDepth_(renderPassInfo.depth);var labelEl=Polymer.dom(item).appendChild(tr.ui.b.createSpan());Polymer.dom(labelEl).textContent=renderPassInfo.name+' '+id;item.renderPass=renderPass;item.renderPassId=id;Polymer.dom(this.itemList_).appendChild(item);if(id===selectedRenderPassId){renderPass.selectionState=tr.model.SelectionState.SELECTED;}},this);},updateLayerContents_:function(){this.changingItemSelection_=true;try{this.itemList_.clear();var selectedLayerId;if(this.selection_&&this.selection_.associatedLayerId){selectedLayerId=this.selection_.associatedLayerId;}
+var layerInfos=this.getLayerInfos_();layerInfos.forEach(function(layerInfo){var layer=layerInfo.layer;var id=layer.layerId;var item=this.createElementWithDepth_(layerInfo.depth);var labelEl=Polymer.dom(item).appendChild(tr.ui.b.createSpan());Polymer.dom(labelEl).textContent=layerInfo.name+' '+id;var notesEl=Polymer.dom(item).appendChild(tr.ui.b.createSpan());if(layerInfo.isMaskLayer){Polymer.dom(notesEl).textContent+='(mask)';}
+if(layerInfo.isReplicaLayer){Polymer.dom(notesEl).textContent+='(replica)';}
+if((layer.gpuMemoryUsageInBytes!==undefined)&&(layer.gpuMemoryUsageInBytes>0)){var gpuUsageStr=tr.b.Unit.byName.sizeInBytes.format(layer.gpuMemoryUsageInBytes);Polymer.dom(notesEl).textContent+=' ('+gpuUsageStr+' MiB)';}
+item.layer=layer;Polymer.dom(this.itemList_).appendChild(item);if(layer.layerId===selectedLayerId){layer.selectionState=tr.model.SelectionState.SELECTED;item.selected=true;}},this);}finally{this.changingItemSelection_=false;}},createElementWithDepth_:function(depth){var item=document.createElement('div');var indentEl=Polymer.dom(item).appendChild(tr.ui.b.createSpan());indentEl.style.whiteSpace='pre';for(var i=0;i<depth;i++){Polymer.dom(indentEl).textContent=Polymer.dom(indentEl).textContent+' ';}
+return item;},onItemSelectionChanged_:function(e){if(this.changingItemSelection_)return;if(this.renderPassQuads_){this.onRenderPassSelected_(e);}else{this.onLayerSelected_(e);}
+tr.b.dispatchSimpleEvent(this,'selection-change',false);},onRenderPassSelected_:function(e){var selectedRenderPass;var selectedRenderPassId;if(this.itemList_.selectedElement){selectedRenderPass=this.itemList_.selectedElement.renderPass;selectedRenderPassId=this.itemList_.selectedElement.renderPassId;}
+if(selectedRenderPass){this.selection_=new tr.ui.e.chrome.cc.RenderPassSelection(selectedRenderPass,selectedRenderPassId);}else{this.selection_=undefined;}},onLayerSelected_:function(e){var selectedLayer;if(this.itemList_.selectedElement){selectedLayer=this.itemList_.selectedElement.layer;}
+if(selectedLayer){this.selection_=new tr.ui.e.chrome.cc.LayerSelection(selectedLayer);}else{this.selection_=undefined;}},get selection(){return this.selection_;},set selection(selection){if(this.selection_===selection)return;this.selection_=selection;this.updateContents_();}};return{LayerPicker,};});'use strict';tr.exportTo('tr.e.cc',function(){var ObjectSnapshot=tr.model.ObjectSnapshot;function RenderPassSnapshot(){ObjectSnapshot.apply(this,arguments);}
+RenderPassSnapshot.prototype={__proto__:ObjectSnapshot.prototype,preInitialize:function(){tr.e.cc.preInitializeObject(this);},initialize:function(){tr.e.cc.moveRequiredFieldsFromArgsToToplevel(this,['quadList']);}};ObjectSnapshot.subTypes.register(RenderPassSnapshot,{typeName:'cc::RenderPass'});return{RenderPassSnapshot,};});'use strict';tr.exportTo('tr.ui.b',function(){let deg2rad=tr.b.math.deg2rad;var constants={DEFAULT_SCALE:0.5,DEFAULT_EYE_DISTANCE:10000,MINIMUM_DISTANCE:1000,MAXIMUM_DISTANCE:100000,FOV:15,RESCALE_TIMEOUT_MS:200,MAXIMUM_TILT:80,SETTINGS_NAMESPACE:'tr.ui_camera'};var Camera=tr.ui.b.define('camera');Camera.prototype={__proto__:HTMLUnknownElement.prototype,decorate:function(eventSource){this.eventSource_=eventSource;this.eventSource_.addEventListener('beginpan',this.onPanBegin_.bind(this));this.eventSource_.addEventListener('updatepan',this.onPanUpdate_.bind(this));this.eventSource_.addEventListener('endpan',this.onPanEnd_.bind(this));this.eventSource_.addEventListener('beginzoom',this.onZoomBegin_.bind(this));this.eventSource_.addEventListener('updatezoom',this.onZoomUpdate_.bind(this));this.eventSource_.addEventListener('endzoom',this.onZoomEnd_.bind(this));this.eventSource_.addEventListener('beginrotate',this.onRotateBegin_.bind(this));this.eventSource_.addEventListener('updaterotate',this.onRotateUpdate_.bind(this));this.eventSource_.addEventListener('endrotate',this.onRotateEnd_.bind(this));this.eye_=[0,0,constants.DEFAULT_EYE_DISTANCE];this.gazeTarget_=[0,0,0];this.rotation_=[0,0];this.pixelRatio_=window.devicePixelRatio||1;},get modelViewMatrix(){var mvMatrix=mat4.create();mat4.lookAt(mvMatrix,this.eye_,this.gazeTarget_,[0,1,0]);return mvMatrix;},get projectionMatrix(){var rect=tr.ui.b.windowRectForElement(this.canvas_).scaleSize(this.pixelRatio_);var aspectRatio=rect.width/rect.height;var matrix=mat4.create();mat4.perspective(matrix,deg2rad(constants.FOV),aspectRatio,1,100000);return matrix;},set canvas(c){this.canvas_=c;},set deviceRect(rect){this.deviceRect_=rect;},get stackingDistanceDampening(){var gazeVector=[this.gazeTarget_[0]-this.eye_[0],this.gazeTarget_[1]-this.eye_[1],this.gazeTarget_[2]-this.eye_[2]];vec3.normalize(gazeVector,gazeVector);return 1+gazeVector[2];},loadCameraFromSettings:function(settings){this.eye_=settings.get('eye',this.eye_,constants.SETTINGS_NAMESPACE);this.gazeTarget_=settings.get('gaze_target',this.gazeTarget_,constants.SETTINGS_NAMESPACE);this.rotation_=settings.get('rotation',this.rotation_,constants.SETTINGS_NAMESPACE);this.dispatchRenderEvent_();},saveCameraToSettings:function(settings){settings.set('eye',this.eye_,constants.SETTINGS_NAMESPACE);settings.set('gaze_target',this.gazeTarget_,constants.SETTINGS_NAMESPACE);settings.set('rotation',this.rotation_,constants.SETTINGS_NAMESPACE);},resetCamera:function(){this.eye_=[0,0,constants.DEFAULT_EYE_DISTANCE];this.gazeTarget_=[0,0,0];this.rotation_=[0,0];var settings=tr.b.SessionSettings();var keys=settings.keys(constants.SETTINGS_NAMESPACE);if(keys.length!==0){this.loadCameraFromSettings(settings);return;}
 if(this.deviceRect_){var rect=tr.ui.b.windowRectForElement(this.canvas_).scaleSize(this.pixelRatio_);this.eye_[0]=this.deviceRect_.width/2;this.eye_[1]=this.deviceRect_.height/2;this.gazeTarget_[0]=this.deviceRect_.width/2;this.gazeTarget_[1]=this.deviceRect_.height/2;}
-this.saveCameraToSettings(settings);this.dispatchRenderEvent_();},updatePanByDelta:function(delta){var rect=tr.ui.b.windowRectForElement(this.canvas_).scaleSize(this.pixelRatio_);var eyeVector=[this.eye_[0]-this.gazeTarget_[0],this.eye_[1]-this.gazeTarget_[1],this.eye_[2]-this.gazeTarget_[2]];var length=vec3.length(eyeVector);vec3.normalize(eyeVector,eyeVector);var halfFov=constants.FOV/2;var multiplier=2.0*length*Math.tan(tr.b.deg2rad(halfFov))/rect.height;var up=[0,1,0];var rotMatrix=mat4.create();mat4.rotate(rotMatrix,rotMatrix,tr.b.deg2rad(this.rotation_[1]),[0,1,0]);mat4.rotate(rotMatrix,rotMatrix,tr.b.deg2rad(this.rotation_[0]),[1,0,0]);vec3.transformMat4(up,up,rotMatrix);var right=[0,0,0];vec3.cross(right,eyeVector,up);vec3.normalize(right,right);for(var i=0;i<3;++i){this.gazeTarget_[i]+=delta[0]*multiplier*right[i]-delta[1]*multiplier*up[i];this.eye_[i]=this.gazeTarget_[i]+length*eyeVector[i];}
-if(Math.abs(this.gazeTarget_[2])>1e-6){var gazeVector=[-eyeVector[0],-eyeVector[1],-eyeVector[2]];var newLength=tr.b.clamp(-this.eye_[2]/gazeVector[2],constants.MINIMUM_DISTANCE,constants.MAXIMUM_DISTANCE);for(var i=0;i<3;++i)
-this.gazeTarget_[i]=this.eye_[i]+newLength*gazeVector[i];}
-this.saveCameraToSettings(tr.b.SessionSettings());this.dispatchRenderEvent_();},updateZoomByDelta:function(delta){var deltaY=delta[1];deltaY=tr.b.clamp(deltaY,-50,50);var scale=1.0-deltaY/100.0;var eyeVector=[0,0,0];vec3.subtract(eyeVector,this.eye_,this.gazeTarget_);var length=vec3.length(eyeVector);if(length*scale<constants.MINIMUM_DISTANCE)
-scale=constants.MINIMUM_DISTANCE/length;else if(length*scale>constants.MAXIMUM_DISTANCE)
-scale=constants.MAXIMUM_DISTANCE/length;vec3.scale(eyeVector,eyeVector,scale);vec3.add(this.eye_,this.gazeTarget_,eyeVector);this.saveCameraToSettings(tr.b.SessionSettings());this.dispatchRenderEvent_();},updateRotateByDelta:function(delta){delta[0]*=0.5;delta[1]*=0.5;if(Math.abs(this.rotation_[0]+delta[1])>constants.MAXIMUM_TILT)
-return;if(Math.abs(this.rotation_[1]-delta[0])>constants.MAXIMUM_TILT)
-return;var eyeVector=[0,0,0,0];vec3.subtract(eyeVector,this.eye_,this.gazeTarget_);var rotMatrix=mat4.create();mat4.rotate(rotMatrix,rotMatrix,-tr.b.deg2rad(this.rotation_[0]),[1,0,0]);mat4.rotate(rotMatrix,rotMatrix,-tr.b.deg2rad(this.rotation_[1]),[0,1,0]);vec4.transformMat4(eyeVector,eyeVector,rotMatrix);this.rotation_[0]+=delta[1];this.rotation_[1]-=delta[0];mat4.identity(rotMatrix);mat4.rotate(rotMatrix,rotMatrix,tr.b.deg2rad(this.rotation_[1]),[0,1,0]);mat4.rotate(rotMatrix,rotMatrix,tr.b.deg2rad(this.rotation_[0]),[1,0,0]);vec4.transformMat4(eyeVector,eyeVector,rotMatrix);vec3.add(this.eye_,this.gazeTarget_,eyeVector);this.saveCameraToSettings(tr.b.SessionSettings());this.dispatchRenderEvent_();},onPanBegin_:function(e){this.panning_=true;this.lastMousePosition_=this.getMousePosition_(e);},onPanUpdate_:function(e){if(!this.panning_)
-return;var delta=this.getMouseDelta_(e,this.lastMousePosition_);this.lastMousePosition_=this.getMousePosition_(e);this.updatePanByDelta(delta);},onPanEnd_:function(e){this.panning_=false;},onZoomBegin_:function(e){this.zooming_=true;var p=this.getMousePosition_(e);this.lastMousePosition_=p;this.zoomPoint_=p;},onZoomUpdate_:function(e){if(!this.zooming_)
-return;var delta=this.getMouseDelta_(e,this.lastMousePosition_);this.lastMousePosition_=this.getMousePosition_(e);this.updateZoomByDelta(delta);},onZoomEnd_:function(e){this.zooming_=false;this.zoomPoint_=undefined;},onRotateBegin_:function(e){this.rotating_=true;this.lastMousePosition_=this.getMousePosition_(e);},onRotateUpdate_:function(e){if(!this.rotating_)
-return;var delta=this.getMouseDelta_(e,this.lastMousePosition_);this.lastMousePosition_=this.getMousePosition_(e);this.updateRotateByDelta(delta);},onRotateEnd_:function(e){this.rotating_=false;},getMousePosition_:function(e){var rect=tr.ui.b.windowRectForElement(this.canvas_);return[(e.clientX-rect.x)*this.pixelRatio_,(e.clientY-rect.y)*this.pixelRatio_];},getMouseDelta_:function(e,p){var newP=this.getMousePosition_(e);return[newP[0]-p[0],newP[1]-p[1]];},dispatchRenderEvent_:function(){tr.b.dispatchSimpleEvent(this,'renderrequired',false,false);}};return{Camera:Camera};});'use strict';tr.exportTo('tr.ui.b',function(){var THIS_DOC=document.currentScript.ownerDocument;var constants={};constants.IMAGE_LOAD_RETRY_TIME_MS=500;constants.SUBDIVISION_MINIMUM=1;constants.SUBDIVISION_RECURSION_DEPTH=3;constants.SUBDIVISION_DEPTH_THRESHOLD=100;constants.FAR_PLANE_DISTANCE=10000;function drawTexturedTriangle(ctx,img,p0,p1,p2,t0,t1,t2){var tmp_p0=[p0[0],p0[1]];var tmp_p1=[p1[0],p1[1]];var tmp_p2=[p2[0],p2[1]];var tmp_t0=[t0[0],t0[1]];var tmp_t1=[t1[0],t1[1]];var tmp_t2=[t2[0],t2[1]];ctx.beginPath();ctx.moveTo(tmp_p0[0],tmp_p0[1]);ctx.lineTo(tmp_p1[0],tmp_p1[1]);ctx.lineTo(tmp_p2[0],tmp_p2[1]);ctx.closePath();tmp_p1[0]-=tmp_p0[0];tmp_p1[1]-=tmp_p0[1];tmp_p2[0]-=tmp_p0[0];tmp_p2[1]-=tmp_p0[1];tmp_t1[0]-=tmp_t0[0];tmp_t1[1]-=tmp_t0[1];tmp_t2[0]-=tmp_t0[0];tmp_t2[1]-=tmp_t0[1];var det=1/(tmp_t1[0]*tmp_t2[1]-tmp_t2[0]*tmp_t1[1]),a=(tmp_t2[1]*tmp_p1[0]-tmp_t1[1]*tmp_p2[0])*det,b=(tmp_t2[1]*tmp_p1[1]-tmp_t1[1]*tmp_p2[1])*det,c=(tmp_t1[0]*tmp_p2[0]-tmp_t2[0]*tmp_p1[0])*det,d=(tmp_t1[0]*tmp_p2[1]-tmp_t2[0]*tmp_p1[1])*det,e=tmp_p0[0]-a*tmp_t0[0]-c*tmp_t0[1],f=tmp_p0[1]-b*tmp_t0[0]-d*tmp_t0[1];ctx.save();ctx.transform(a,b,c,d,e,f);ctx.clip();ctx.drawImage(img,0,0);ctx.restore();}
-function drawTriangleSub(ctx,img,p0,p1,p2,t0,t1,t2,opt_recursion_depth){var depth=opt_recursion_depth||0;var subdivisionIndex=0;if(depth<constants.SUBDIVISION_MINIMUM){subdivisionIndex=7;}else if(depth<constants.SUBDIVISION_RECURSION_DEPTH){if(Math.abs(p0[2]-p1[2])>constants.SUBDIVISION_DEPTH_THRESHOLD)
-subdivisionIndex+=1;if(Math.abs(p0[2]-p2[2])>constants.SUBDIVISION_DEPTH_THRESHOLD)
-subdivisionIndex+=2;if(Math.abs(p1[2]-p2[2])>constants.SUBDIVISION_DEPTH_THRESHOLD)
-subdivisionIndex+=4;}
+this.saveCameraToSettings(settings);this.dispatchRenderEvent_();},updatePanByDelta:function(delta){var rect=tr.ui.b.windowRectForElement(this.canvas_).scaleSize(this.pixelRatio_);var eyeVector=[this.eye_[0]-this.gazeTarget_[0],this.eye_[1]-this.gazeTarget_[1],this.eye_[2]-this.gazeTarget_[2]];var length=vec3.length(eyeVector);vec3.normalize(eyeVector,eyeVector);var halfFov=constants.FOV/2;var multiplier=2.0*length*Math.tan(deg2rad(halfFov))/rect.height;var up=[0,1,0];var rotMatrix=mat4.create();mat4.rotate(rotMatrix,rotMatrix,deg2rad(this.rotation_[1]),[0,1,0]);mat4.rotate(rotMatrix,rotMatrix,deg2rad(this.rotation_[0]),[1,0,0]);vec3.transformMat4(up,up,rotMatrix);var right=[0,0,0];vec3.cross(right,eyeVector,up);vec3.normalize(right,right);for(var i=0;i<3;++i){this.gazeTarget_[i]+=delta[0]*multiplier*right[i]-delta[1]*multiplier*up[i];this.eye_[i]=this.gazeTarget_[i]+length*eyeVector[i];}
+if(Math.abs(this.gazeTarget_[2])>1e-6){var gazeVector=[-eyeVector[0],-eyeVector[1],-eyeVector[2]];var newLength=tr.b.math.clamp(-this.eye_[2]/gazeVector[2],constants.MINIMUM_DISTANCE,constants.MAXIMUM_DISTANCE);for(var i=0;i<3;++i){this.gazeTarget_[i]=this.eye_[i]+newLength*gazeVector[i];}}
+this.saveCameraToSettings(tr.b.SessionSettings());this.dispatchRenderEvent_();},updateZoomByDelta:function(delta){var deltaY=delta[1];deltaY=tr.b.math.clamp(deltaY,-50,50);var scale=1.0-deltaY/100.0;var eyeVector=[0,0,0];vec3.subtract(eyeVector,this.eye_,this.gazeTarget_);var length=vec3.length(eyeVector);if(length*scale<constants.MINIMUM_DISTANCE){scale=constants.MINIMUM_DISTANCE/length;}else if(length*scale>constants.MAXIMUM_DISTANCE){scale=constants.MAXIMUM_DISTANCE/length;}
+vec3.scale(eyeVector,eyeVector,scale);vec3.add(this.eye_,this.gazeTarget_,eyeVector);this.saveCameraToSettings(tr.b.SessionSettings());this.dispatchRenderEvent_();},updateRotateByDelta:function(delta){delta[0]*=0.5;delta[1]*=0.5;if(Math.abs(this.rotation_[0]+delta[1])>constants.MAXIMUM_TILT){return;}
+if(Math.abs(this.rotation_[1]-delta[0])>constants.MAXIMUM_TILT){return;}
+var eyeVector=[0,0,0,0];vec3.subtract(eyeVector,this.eye_,this.gazeTarget_);var rotMatrix=mat4.create();mat4.rotate(rotMatrix,rotMatrix,-deg2rad(this.rotation_[0]),[1,0,0]);mat4.rotate(rotMatrix,rotMatrix,-deg2rad(this.rotation_[1]),[0,1,0]);vec4.transformMat4(eyeVector,eyeVector,rotMatrix);this.rotation_[0]+=delta[1];this.rotation_[1]-=delta[0];mat4.identity(rotMatrix);mat4.rotate(rotMatrix,rotMatrix,deg2rad(this.rotation_[1]),[0,1,0]);mat4.rotate(rotMatrix,rotMatrix,deg2rad(this.rotation_[0]),[1,0,0]);vec4.transformMat4(eyeVector,eyeVector,rotMatrix);vec3.add(this.eye_,this.gazeTarget_,eyeVector);this.saveCameraToSettings(tr.b.SessionSettings());this.dispatchRenderEvent_();},onPanBegin_:function(e){this.panning_=true;this.lastMousePosition_=this.getMousePosition_(e);},onPanUpdate_:function(e){if(!this.panning_)return;var delta=this.getMouseDelta_(e,this.lastMousePosition_);this.lastMousePosition_=this.getMousePosition_(e);this.updatePanByDelta(delta);},onPanEnd_:function(e){this.panning_=false;},onZoomBegin_:function(e){this.zooming_=true;var p=this.getMousePosition_(e);this.lastMousePosition_=p;this.zoomPoint_=p;},onZoomUpdate_:function(e){if(!this.zooming_)return;var delta=this.getMouseDelta_(e,this.lastMousePosition_);this.lastMousePosition_=this.getMousePosition_(e);this.updateZoomByDelta(delta);},onZoomEnd_:function(e){this.zooming_=false;this.zoomPoint_=undefined;},onRotateBegin_:function(e){this.rotating_=true;this.lastMousePosition_=this.getMousePosition_(e);},onRotateUpdate_:function(e){if(!this.rotating_)return;var delta=this.getMouseDelta_(e,this.lastMousePosition_);this.lastMousePosition_=this.getMousePosition_(e);this.updateRotateByDelta(delta);},onRotateEnd_:function(e){this.rotating_=false;},getMousePosition_:function(e){var rect=tr.ui.b.windowRectForElement(this.canvas_);return[(e.clientX-rect.x)*this.pixelRatio_,(e.clientY-rect.y)*this.pixelRatio_];},getMouseDelta_:function(e,p){var newP=this.getMousePosition_(e);return[newP[0]-p[0],newP[1]-p[1]];},dispatchRenderEvent_:function(){tr.b.dispatchSimpleEvent(this,'renderrequired',false,false);}};return{Camera,};});'use strict';tr.exportTo('tr.ui.b',function(){var THIS_DOC=document.currentScript.ownerDocument;var constants={};constants.IMAGE_LOAD_RETRY_TIME_MS=500;constants.SUBDIVISION_MINIMUM=1;constants.SUBDIVISION_RECURSION_DEPTH=3;constants.SUBDIVISION_DEPTH_THRESHOLD=100;constants.FAR_PLANE_DISTANCE=10000;function drawTexturedTriangle(ctx,img,p0,p1,p2,t0,t1,t2){var tmpP0=[p0[0],p0[1]];var tmpP1=[p1[0],p1[1]];var tmpP2=[p2[0],p2[1]];var tmpT0=[t0[0],t0[1]];var tmpT1=[t1[0],t1[1]];var tmpT2=[t2[0],t2[1]];ctx.beginPath();ctx.moveTo(tmpP0[0],tmpP0[1]);ctx.lineTo(tmpP1[0],tmpP1[1]);ctx.lineTo(tmpP2[0],tmpP2[1]);ctx.closePath();tmpP1[0]-=tmpP0[0];tmpP1[1]-=tmpP0[1];tmpP2[0]-=tmpP0[0];tmpP2[1]-=tmpP0[1];tmpT1[0]-=tmpT0[0];tmpT1[1]-=tmpT0[1];tmpT2[0]-=tmpT0[0];tmpT2[1]-=tmpT0[1];var det=1/(tmpT1[0]*tmpT2[1]-tmpT2[0]*tmpT1[1]);var a=(tmpT2[1]*tmpP1[0]-tmpT1[1]*tmpP2[0])*det;var b=(tmpT2[1]*tmpP1[1]-tmpT1[1]*tmpP2[1])*det;var c=(tmpT1[0]*tmpP2[0]-tmpT2[0]*tmpP1[0])*det;var d=(tmpT1[0]*tmpP2[1]-tmpT2[0]*tmpP1[1])*det;var e=tmpP0[0]-a*tmpT0[0]-c*tmpT0[1];var f=tmpP0[1]-b*tmpT0[0]-d*tmpT0[1];ctx.save();ctx.transform(a,b,c,d,e,f);ctx.clip();ctx.drawImage(img,0,0);ctx.restore();}
+function drawTriangleSub(ctx,img,p0,p1,p2,t0,t1,t2,opt_recursionDepth){var depth=opt_recursionDepth||0;var subdivisionIndex=0;if(depth<constants.SUBDIVISION_MINIMUM){subdivisionIndex=7;}else if(depth<constants.SUBDIVISION_RECURSION_DEPTH){if(Math.abs(p0[2]-p1[2])>constants.SUBDIVISION_DEPTH_THRESHOLD){subdivisionIndex+=1;}
+if(Math.abs(p0[2]-p2[2])>constants.SUBDIVISION_DEPTH_THRESHOLD){subdivisionIndex+=2;}
+if(Math.abs(p1[2]-p2[2])>constants.SUBDIVISION_DEPTH_THRESHOLD){subdivisionIndex+=4;}}
 var p01=vec4.create();var p02=vec4.create();var p12=vec4.create();var t01=vec2.create();var t02=vec2.create();var t12=vec2.create();for(var i=0;i<2;++i){p0[i]*=p0[2];p1[i]*=p1[2];p2[i]*=p2[2];}
 for(var i=0;i<4;++i){p01[i]=(p0[i]+p1[i])/2;p02[i]=(p0[i]+p2[i])/2;p12[i]=(p1[i]+p2[i])/2;}
 for(var i=0;i<2;++i){p0[i]/=p0[2];p1[i]/=p1[2];p2[i]/=p2[2];p01[i]/=p01[2];p02[i]/=p02[2];p12[i]/=p12[2];}
 for(var i=0;i<2;++i){t01[i]=(t0[i]+t1[i])/2;t02[i]=(t0[i]+t2[i])/2;t12[i]=(t1[i]+t2[i])/2;}
 switch(subdivisionIndex){case 1:drawTriangleSub(ctx,img,p0,p01,p2,t0,t01,t2,depth+1);drawTriangleSub(ctx,img,p01,p1,p2,t01,t1,t2,depth+1);break;case 2:drawTriangleSub(ctx,img,p0,p1,p02,t0,t1,t02,depth+1);drawTriangleSub(ctx,img,p1,p02,p2,t1,t02,t2,depth+1);break;case 3:drawTriangleSub(ctx,img,p0,p01,p02,t0,t01,t02,depth+1);drawTriangleSub(ctx,img,p02,p01,p2,t02,t01,t2,depth+1);drawTriangleSub(ctx,img,p01,p1,p2,t01,t1,t2,depth+1);break;case 4:drawTriangleSub(ctx,img,p0,p12,p2,t0,t12,t2,depth+1);drawTriangleSub(ctx,img,p0,p1,p12,t0,t1,t12,depth+1);break;case 5:drawTriangleSub(ctx,img,p0,p01,p2,t0,t01,t2,depth+1);drawTriangleSub(ctx,img,p2,p01,p12,t2,t01,t12,depth+1);drawTriangleSub(ctx,img,p01,p1,p12,t01,t1,t12,depth+1);break;case 6:drawTriangleSub(ctx,img,p0,p12,p02,t0,t12,t02,depth+1);drawTriangleSub(ctx,img,p0,p1,p12,t0,t1,t12,depth+1);drawTriangleSub(ctx,img,p02,p12,p2,t02,t12,t2,depth+1);break;case 7:drawTriangleSub(ctx,img,p0,p01,p02,t0,t01,t02,depth+1);drawTriangleSub(ctx,img,p01,p12,p02,t01,t12,t02,depth+1);drawTriangleSub(ctx,img,p01,p1,p12,t01,t1,t12,depth+1);drawTriangleSub(ctx,img,p02,p12,p2,t02,t12,t2,depth+1);break;default:drawTexturedTriangle(ctx,img,p0,p1,p2,t0,t1,t2);break;}}
-var tmp_vec4=vec4.create();function transform(transformed,point,matrix,viewport){vec4.set(tmp_vec4,point[0],point[1],0,1);vec4.transformMat4(tmp_vec4,tmp_vec4,matrix);var w=tmp_vec4[3];if(w<1e-6)w=1e-6;transformed[0]=((tmp_vec4[0]/w)+1)*viewport.width/2;transformed[1]=((tmp_vec4[1]/w)+1)*viewport.height/2;transformed[2]=w;}
-function drawProjectedQuadBackgroundToContext(quad,p1,p2,p3,p4,ctx,quadCanvas){if(quad.imageData){quadCanvas.width=quad.imageData.width;quadCanvas.height=quad.imageData.height;quadCanvas.getContext('2d').putImageData(quad.imageData,0,0);var quadBBox=new tr.b.BBox2();quadBBox.addQuad(quad);var iw=quadCanvas.width;var ih=quadCanvas.height;drawTriangleSub(ctx,quadCanvas,p1,p2,p4,[0,0],[iw,0],[0,ih]);drawTriangleSub(ctx,quadCanvas,p2,p3,p4,[iw,0],[iw,ih],[0,ih]);}
+var tmpVec4=vec4.create();function transform(transformed,point,matrix,viewport){vec4.set(tmpVec4,point[0],point[1],0,1);vec4.transformMat4(tmpVec4,tmpVec4,matrix);var w=tmpVec4[3];if(w<1e-6)w=1e-6;transformed[0]=((tmpVec4[0]/w)+1)*viewport.width/2;transformed[1]=((tmpVec4[1]/w)+1)*viewport.height/2;transformed[2]=w;}
+function drawProjectedQuadBackgroundToContext(quad,p1,p2,p3,p4,ctx,quadCanvas){if(quad.imageData){quadCanvas.width=quad.imageData.width;quadCanvas.height=quad.imageData.height;quadCanvas.getContext('2d').putImageData(quad.imageData,0,0);var quadBBox=new tr.b.math.BBox2();quadBBox.addQuad(quad);var iw=quadCanvas.width;var ih=quadCanvas.height;drawTriangleSub(ctx,quadCanvas,p1,p2,p4,[0,0],[iw,0],[0,ih]);drawTriangleSub(ctx,quadCanvas,p2,p3,p4,[iw,0],[iw,ih],[0,ih]);}
 if(quad.backgroundColor){ctx.fillStyle=quad.backgroundColor;ctx.beginPath();ctx.moveTo(p1[0],p1[1]);ctx.lineTo(p2[0],p2[1]);ctx.lineTo(p3[0],p3[1]);ctx.lineTo(p4[0],p4[1]);ctx.closePath();ctx.fill();}}
-function drawProjectedQuadOutlineToContext(quad,p1,p2,p3,p4,ctx,quadCanvas){ctx.beginPath();ctx.moveTo(p1[0],p1[1]);ctx.lineTo(p2[0],p2[1]);ctx.lineTo(p3[0],p3[1]);ctx.lineTo(p4[0],p4[1]);ctx.closePath();ctx.save();if(quad.borderColor)
-ctx.strokeStyle=quad.borderColor;else
-ctx.strokeStyle='rgb(128,128,128)';if(quad.shadowOffset){ctx.shadowColor='rgb(0, 0, 0)';ctx.shadowOffsetX=quad.shadowOffset[0];ctx.shadowOffsetY=quad.shadowOffset[1];if(quad.shadowBlur)
-ctx.shadowBlur=quad.shadowBlur;}
-if(quad.borderWidth)
-ctx.lineWidth=quad.borderWidth;else
-ctx.lineWidth=1;ctx.stroke();ctx.restore();}
-function drawProjectedQuadSelectionOutlineToContext(quad,p1,p2,p3,p4,ctx,quadCanvas){if(!quad.upperBorderColor)
-return;ctx.lineWidth=8;ctx.strokeStyle=quad.upperBorderColor;ctx.beginPath();ctx.moveTo(p1[0],p1[1]);ctx.lineTo(p2[0],p2[1]);ctx.lineTo(p3[0],p3[1]);ctx.lineTo(p4[0],p4[1]);ctx.closePath();ctx.stroke();}
+function drawProjectedQuadOutlineToContext(quad,p1,p2,p3,p4,ctx,quadCanvas){ctx.beginPath();ctx.moveTo(p1[0],p1[1]);ctx.lineTo(p2[0],p2[1]);ctx.lineTo(p3[0],p3[1]);ctx.lineTo(p4[0],p4[1]);ctx.closePath();ctx.save();if(quad.borderColor){ctx.strokeStyle=quad.borderColor;}else{ctx.strokeStyle='rgb(128,128,128)';}
+if(quad.shadowOffset){ctx.shadowColor='rgb(0, 0, 0)';ctx.shadowOffsetX=quad.shadowOffset[0];ctx.shadowOffsetY=quad.shadowOffset[1];if(quad.shadowBlur){ctx.shadowBlur=quad.shadowBlur;}}
+if(quad.borderWidth){ctx.lineWidth=quad.borderWidth;}else{ctx.lineWidth=1;}
+ctx.stroke();ctx.restore();}
+function drawProjectedQuadSelectionOutlineToContext(quad,p1,p2,p3,p4,ctx,quadCanvas){if(!quad.upperBorderColor)return;ctx.lineWidth=8;ctx.strokeStyle=quad.upperBorderColor;ctx.beginPath();ctx.moveTo(p1[0],p1[1]);ctx.lineTo(p2[0],p2[1]);ctx.lineTo(p3[0],p3[1]);ctx.lineTo(p4[0],p4[1]);ctx.closePath();ctx.stroke();}
 function drawProjectedQuadToContext(passNumber,quad,p1,p2,p3,p4,ctx,quadCanvas){if(passNumber===0){drawProjectedQuadBackgroundToContext(quad,p1,p2,p3,p4,ctx,quadCanvas);}else if(passNumber===1){drawProjectedQuadOutlineToContext(quad,p1,p2,p3,p4,ctx,quadCanvas);}else if(passNumber===2){drawProjectedQuadSelectionOutlineToContext(quad,p1,p2,p3,p4,ctx,quadCanvas);}else{throw new Error('Invalid pass number');}}
-var tmp_p1=vec3.create();var tmp_p2=vec3.create();var tmp_p3=vec3.create();var tmp_p4=vec3.create();function transformAndProcessQuads(matrix,viewport,quads,numPasses,handleQuadFunc,opt_arg1,opt_arg2){for(var passNumber=0;passNumber<numPasses;passNumber++){for(var i=0;i<quads.length;i++){var quad=quads[i];transform(tmp_p1,quad.p1,matrix,viewport);transform(tmp_p2,quad.p2,matrix,viewport);transform(tmp_p3,quad.p3,matrix,viewport);transform(tmp_p4,quad.p4,matrix,viewport);handleQuadFunc(passNumber,quad,tmp_p1,tmp_p2,tmp_p3,tmp_p4,opt_arg1,opt_arg2);}}}
-var QuadStackView=tr.ui.b.define('quad-stack-view');QuadStackView.prototype={__proto__:HTMLDivElement.prototype,decorate:function(){this.className='quad-stack-view';var node=tr.ui.b.instantiateTemplate('#quad-stack-view-template',THIS_DOC);Polymer.dom(this).appendChild(node);this.updateHeaderVisibility_();this.canvas_=Polymer.dom(this).querySelector('#canvas');this.chromeImages_={left:Polymer.dom(this).querySelector('#chrome-left'),mid:Polymer.dom(this).querySelector('#chrome-mid'),right:Polymer.dom(this).querySelector('#chrome-right')};var stackingDistanceSlider=Polymer.dom(this).querySelector('#stacking-distance-slider');stackingDistanceSlider.value=tr.b.Settings.get('quadStackView.stackingDistance',45);stackingDistanceSlider.addEventListener('change',this.onStackingDistanceChange_.bind(this));stackingDistanceSlider.addEventListener('input',this.onStackingDistanceChange_.bind(this));this.trackMouse_();this.camera_=new tr.ui.b.Camera(this.mouseModeSelector_);this.camera_.addEventListener('renderrequired',this.onRenderRequired_.bind(this));this.cameraWasReset_=false;this.camera_.canvas=this.canvas_;this.viewportRect_=tr.b.Rect.fromXYWH(0,0,0,0);this.pixelRatio_=window.devicePixelRatio||1;},updateHeaderVisibility_:function(){if(this.headerText)
-Polymer.dom(this).querySelector('#header').style.display='';else
-Polymer.dom(this).querySelector('#header').style.display='none';},get headerText(){return Polymer.dom(this).querySelector('#header').textContent;},set headerText(headerText){Polymer.dom(this).querySelector('#header').textContent=headerText;this.updateHeaderVisibility_();},onStackingDistanceChange_:function(e){tr.b.Settings.set('quadStackView.stackingDistance',this.stackingDistance);this.scheduleRender();e.stopPropagation();},get stackingDistance(){return Polymer.dom(this).querySelector('#stacking-distance-slider').value;},get mouseModeSelector(){return this.mouseModeSelector_;},get camera(){return this.camera_;},set quads(q){this.quads_=q;this.scheduleRender();},set deviceRect(rect){if(!rect||rect.equalTo(this.deviceRect_))
-return;this.deviceRect_=rect;this.camera_.deviceRect=rect;this.chromeQuad_=undefined;},resize:function(){if(!this.offsetParent)
-return true;var width=parseInt(window.getComputedStyle(this.offsetParent).width);var height=parseInt(window.getComputedStyle(this.offsetParent).height);var rect=tr.b.Rect.fromXYWH(0,0,width,height);if(rect.equalTo(this.viewportRect_))
-return false;this.viewportRect_=rect;this.style.width=width+'px';this.style.height=height+'px';this.canvas_.style.width=width+'px';this.canvas_.style.height=height+'px';this.canvas_.width=this.pixelRatio_*width;this.canvas_.height=this.pixelRatio_*height;if(!this.cameraWasReset_){this.camera_.resetCamera();this.cameraWasReset_=true;}
+var tmpP1=vec3.create();var tmpP2=vec3.create();var tmpP3=vec3.create();var tmpP4=vec3.create();function transformAndProcessQuads(matrix,viewport,quads,numPasses,handleQuadFunc,opt_arg1,opt_arg2){for(var passNumber=0;passNumber<numPasses;passNumber++){for(var i=0;i<quads.length;i++){var quad=quads[i];transform(tmpP1,quad.p1,matrix,viewport);transform(tmpP2,quad.p2,matrix,viewport);transform(tmpP3,quad.p3,matrix,viewport);transform(tmpP4,quad.p4,matrix,viewport);handleQuadFunc(passNumber,quad,tmpP1,tmpP2,tmpP3,tmpP4,opt_arg1,opt_arg2);}}}
+var QuadStackView=tr.ui.b.define('quad-stack-view');QuadStackView.prototype={__proto__:HTMLDivElement.prototype,decorate:function(){this.className='quad-stack-view';var node=tr.ui.b.instantiateTemplate('#quad-stack-view-template',THIS_DOC);Polymer.dom(this).appendChild(node);this.updateHeaderVisibility_();this.canvas_=Polymer.dom(this).querySelector('#canvas');this.chromeImages_={left:Polymer.dom(this).querySelector('#chrome-left'),mid:Polymer.dom(this).querySelector('#chrome-mid'),right:Polymer.dom(this).querySelector('#chrome-right')};var stackingDistanceSlider=Polymer.dom(this).querySelector('#stacking-distance-slider');stackingDistanceSlider.value=tr.b.Settings.get('quadStackView.stackingDistance',45);stackingDistanceSlider.addEventListener('change',this.onStackingDistanceChange_.bind(this));stackingDistanceSlider.addEventListener('input',this.onStackingDistanceChange_.bind(this));this.trackMouse_();this.camera_=new tr.ui.b.Camera(this.mouseModeSelector_);this.camera_.addEventListener('renderrequired',this.onRenderRequired_.bind(this));this.cameraWasReset_=false;this.camera_.canvas=this.canvas_;this.viewportRect_=tr.b.math.Rect.fromXYWH(0,0,0,0);this.pixelRatio_=window.devicePixelRatio||1;},updateHeaderVisibility_:function(){if(this.headerText){Polymer.dom(this).querySelector('#header').style.display='';}else{Polymer.dom(this).querySelector('#header').style.display='none';}},get headerText(){return Polymer.dom(this).querySelector('#header').textContent;},set headerText(headerText){Polymer.dom(this).querySelector('#header').textContent=headerText;this.updateHeaderVisibility_();},onStackingDistanceChange_:function(e){tr.b.Settings.set('quadStackView.stackingDistance',this.stackingDistance);this.scheduleRender();e.stopPropagation();},get stackingDistance(){return Polymer.dom(this).querySelector('#stacking-distance-slider').value;},get mouseModeSelector(){return this.mouseModeSelector_;},get camera(){return this.camera_;},set quads(q){this.quads_=q;this.scheduleRender();},set deviceRect(rect){if(!rect||rect.equalTo(this.deviceRect_))return;this.deviceRect_=rect;this.camera_.deviceRect=rect;this.chromeQuad_=undefined;},resize:function(){if(!this.offsetParent)return true;var width=parseInt(window.getComputedStyle(this.offsetParent).width);var height=parseInt(window.getComputedStyle(this.offsetParent).height);var rect=tr.b.math.Rect.fromXYWH(0,0,width,height);if(rect.equalTo(this.viewportRect_))return false;this.viewportRect_=rect;this.style.width=width+'px';this.style.height=height+'px';this.canvas_.style.width=width+'px';this.canvas_.style.height=height+'px';this.canvas_.width=this.pixelRatio_*width;this.canvas_.height=this.pixelRatio_*height;if(!this.cameraWasReset_){this.camera_.resetCamera();this.cameraWasReset_=true;}
 return true;},readyToDraw:function(){if(!this.chromeImages_.left.src){var leftContent=window.getComputedStyle(this.chromeImages_.left).backgroundImage;leftContent=tr.ui.b.extractUrlString(leftContent);var midContent=window.getComputedStyle(this.chromeImages_.mid).backgroundImage;midContent=tr.ui.b.extractUrlString(midContent);var rightContent=window.getComputedStyle(this.chromeImages_.right).backgroundImage;rightContent=tr.ui.b.extractUrlString(rightContent);this.chromeImages_.left.src=leftContent;this.chromeImages_.mid.src=midContent;this.chromeImages_.right.src=rightContent;}
-return(this.chromeImages_.left.height>0)&&(this.chromeImages_.mid.height>0)&&(this.chromeImages_.right.height>0);},get chromeQuad(){if(this.chromeQuad_)
-return this.chromeQuad_;var chromeCanvas=document.createElement('canvas');var offsetY=this.chromeImages_.left.height;chromeCanvas.width=this.deviceRect_.width;chromeCanvas.height=this.deviceRect_.height+offsetY;var leftWidth=this.chromeImages_.left.width;var midWidth=this.chromeImages_.mid.width;var rightWidth=this.chromeImages_.right.width;var chromeCtx=chromeCanvas.getContext('2d');chromeCtx.drawImage(this.chromeImages_.left,0,0);chromeCtx.save();chromeCtx.translate(leftWidth,0);var s=(this.deviceRect_.width-leftWidth-rightWidth)/midWidth;chromeCtx.scale(s,1);chromeCtx.drawImage(this.chromeImages_.mid,0,0);chromeCtx.restore();chromeCtx.drawImage(this.chromeImages_.right,leftWidth+s*midWidth,0);var chromeRect=tr.b.Rect.fromXYWH(this.deviceRect_.x,this.deviceRect_.y-offsetY,this.deviceRect_.width,this.deviceRect_.height+offsetY);var chromeQuad=tr.b.Quad.fromRect(chromeRect);chromeQuad.stackingGroupId=this.maxStackingGroupId_+1;chromeQuad.imageData=chromeCtx.getImageData(0,0,chromeCanvas.width,chromeCanvas.height);chromeQuad.shadowOffset=[0,0];chromeQuad.shadowBlur=5;chromeQuad.borderWidth=3;this.chromeQuad_=chromeQuad;return this.chromeQuad_;},scheduleRender:function(){if(this.redrawScheduled_)
-return false;this.redrawScheduled_=true;tr.b.requestAnimationFrame(this.render,this);},onRenderRequired_:function(e){this.scheduleRender();},stackTransformAndProcessQuads_:function(numPasses,handleQuadFunc,includeChromeQuad,opt_arg1,opt_arg2){var mv=this.camera_.modelViewMatrix;var p=this.camera_.projectionMatrix;var viewport=tr.b.Rect.fromXYWH(0,0,this.canvas_.width,this.canvas_.height);var quadStacks=[];for(var i=0;i<this.quads_.length;++i){var quad=this.quads_[i];var stackingId=quad.stackingGroupId||0;while(stackingId>=quadStacks.length)
-quadStacks.push([]);quadStacks[stackingId].push(quad);}
+return(this.chromeImages_.left.height>0)&&(this.chromeImages_.mid.height>0)&&(this.chromeImages_.right.height>0);},get chromeQuad(){if(this.chromeQuad_)return this.chromeQuad_;var chromeCanvas=document.createElement('canvas');var offsetY=this.chromeImages_.left.height;chromeCanvas.width=this.deviceRect_.width;chromeCanvas.height=this.deviceRect_.height+offsetY;var leftWidth=this.chromeImages_.left.width;var midWidth=this.chromeImages_.mid.width;var rightWidth=this.chromeImages_.right.width;var chromeCtx=chromeCanvas.getContext('2d');chromeCtx.drawImage(this.chromeImages_.left,0,0);chromeCtx.save();chromeCtx.translate(leftWidth,0);var s=(this.deviceRect_.width-leftWidth-rightWidth)/midWidth;chromeCtx.scale(s,1);chromeCtx.drawImage(this.chromeImages_.mid,0,0);chromeCtx.restore();chromeCtx.drawImage(this.chromeImages_.right,leftWidth+s*midWidth,0);var chromeRect=tr.b.math.Rect.fromXYWH(this.deviceRect_.x,this.deviceRect_.y-offsetY,this.deviceRect_.width,this.deviceRect_.height+offsetY);var chromeQuad=tr.b.math.Quad.fromRect(chromeRect);chromeQuad.stackingGroupId=this.maxStackingGroupId_+1;chromeQuad.imageData=chromeCtx.getImageData(0,0,chromeCanvas.width,chromeCanvas.height);chromeQuad.shadowOffset=[0,0];chromeQuad.shadowBlur=5;chromeQuad.borderWidth=3;this.chromeQuad_=chromeQuad;return this.chromeQuad_;},scheduleRender:function(){if(this.redrawScheduled_)return false;this.redrawScheduled_=true;tr.b.requestAnimationFrame(this.render,this);},onRenderRequired_:function(e){this.scheduleRender();},stackTransformAndProcessQuads_:function(numPasses,handleQuadFunc,includeChromeQuad,opt_arg1,opt_arg2){var mv=this.camera_.modelViewMatrix;var p=this.camera_.projectionMatrix;var viewport=tr.b.math.Rect.fromXYWH(0,0,this.canvas_.width,this.canvas_.height);var quadStacks=[];for(var i=0;i<this.quads_.length;++i){var quad=this.quads_[i];var stackingId=quad.stackingGroupId||0;while(stackingId>=quadStacks.length){quadStacks.push([]);}
+quadStacks[stackingId].push(quad);}
 var mvp=mat4.create();this.maxStackingGroupId_=quadStacks.length;var effectiveStackingDistance=this.stackingDistance*this.camera_.stackingDistanceDampening;mat4.multiply(mvp,p,mv);for(var i=0;i<quadStacks.length;++i){transformAndProcessQuads(mvp,viewport,quadStacks[i],numPasses,handleQuadFunc,opt_arg1,opt_arg2);mat4.translate(mv,mv,[0,0,effectiveStackingDistance]);mat4.multiply(mvp,p,mv);}
 if(includeChromeQuad&&this.deviceRect_){transformAndProcessQuads(mvp,viewport,[this.chromeQuad],numPasses,drawProjectedQuadToContext,opt_arg1,opt_arg2);}},render:function(){this.redrawScheduled_=false;if(!this.readyToDraw()){setTimeout(this.scheduleRender.bind(this),constants.IMAGE_LOAD_RETRY_TIME_MS);return;}
-if(!this.quads_)
-return;var canvasCtx=this.canvas_.getContext('2d');if(!this.resize())
-canvasCtx.clearRect(0,0,this.canvas_.width,this.canvas_.height);var quadCanvas=document.createElement('canvas');this.stackTransformAndProcessQuads_(3,drawProjectedQuadToContext,true,canvasCtx,quadCanvas);quadCanvas.width=0;},trackMouse_:function(){this.mouseModeSelector_=document.createElement('tr-ui-b-mouse-mode-selector');this.mouseModeSelector_.targetElement=this.canvas_;this.mouseModeSelector_.supportedModeMask=tr.ui.b.MOUSE_SELECTOR_MODE.SELECTION|tr.ui.b.MOUSE_SELECTOR_MODE.PANSCAN|tr.ui.b.MOUSE_SELECTOR_MODE.ZOOM|tr.ui.b.MOUSE_SELECTOR_MODE.ROTATE;this.mouseModeSelector_.mode=tr.ui.b.MOUSE_SELECTOR_MODE.PANSCAN;this.mouseModeSelector_.pos={x:0,y:100};Polymer.dom(this).appendChild(this.mouseModeSelector_);this.mouseModeSelector_.settingsKey='quadStackView.mouseModeSelector';this.mouseModeSelector_.setModifierForAlternateMode(tr.ui.b.MOUSE_SELECTOR_MODE.ROTATE,tr.ui.b.MODIFIER.SHIFT);this.mouseModeSelector_.setModifierForAlternateMode(tr.ui.b.MOUSE_SELECTOR_MODE.PANSCAN,tr.ui.b.MODIFIER.SPACE);this.mouseModeSelector_.setModifierForAlternateMode(tr.ui.b.MOUSE_SELECTOR_MODE.ZOOM,tr.ui.b.MODIFIER.CMD_OR_CTRL);this.mouseModeSelector_.addEventListener('updateselection',this.onSelectionUpdate_.bind(this));this.mouseModeSelector_.addEventListener('endselection',this.onSelectionUpdate_.bind(this));},extractRelativeMousePosition_:function(e){var br=this.canvas_.getBoundingClientRect();return[this.pixelRatio_*(e.clientX-this.canvas_.offsetLeft-br.left),this.pixelRatio_*(e.clientY-this.canvas_.offsetTop-br.top)];},onSelectionUpdate_:function(e){var mousePos=this.extractRelativeMousePosition_(e);var res=[];function handleQuad(passNumber,quad,p1,p2,p3,p4){if(tr.b.pointInImplicitQuad(mousePos,p1,p2,p3,p4))
-res.push(quad);}
-this.stackTransformAndProcessQuads_(1,handleQuad,false);var e=new tr.b.Event('selectionchange');e.quads=res;this.dispatchEvent(e);}};return{QuadStackView:QuadStackView};});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){var ColorScheme=tr.b.ColorScheme;var THIS_DOC=document.currentScript.ownerDocument;var TILE_HEATMAP_TYPE={};TILE_HEATMAP_TYPE.NONE='none';TILE_HEATMAP_TYPE.SCHEDULED_PRIORITY='scheduledPriority';TILE_HEATMAP_TYPE.USING_GPU_MEMORY='usingGpuMemory';var cc=tr.ui.e.chrome.cc;function createTileRectsSelectorBaseOptions(){return[{label:'None',value:'none'},{label:'Coverage Rects',value:'coverage'}];}
-var LayerTreeQuadStackView=tr.ui.b.define('tr-ui-e-chrome-cc-layer-tree-quad-stack-view');LayerTreeQuadStackView.prototype={__proto__:HTMLDivElement.prototype,decorate:function(){this.isRenderPassQuads_=false;this.pictureAsImageData_={};this.messages_=[];this.controls_=document.createElement('top-controls');this.infoBar_=document.createElement('tr-ui-b-info-bar');this.quadStackView_=new tr.ui.b.QuadStackView();this.quadStackView_.addEventListener('selectionchange',this.onQuadStackViewSelectionChange_.bind(this));this.extraHighlightsByLayerId_=undefined;this.inputEventImageData_=undefined;var m=tr.ui.b.MOUSE_SELECTOR_MODE;var mms=this.quadStackView_.mouseModeSelector;mms.settingsKey='tr.e.cc.layerTreeQuadStackView.mouseModeSelector';mms.setKeyCodeForMode(m.SELECTION,'Z'.charCodeAt(0));mms.setKeyCodeForMode(m.PANSCAN,'X'.charCodeAt(0));mms.setKeyCodeForMode(m.ZOOM,'C'.charCodeAt(0));mms.setKeyCodeForMode(m.ROTATE,'V'.charCodeAt(0));var node=tr.ui.b.instantiateTemplate('#tr-ui-e-chrome-cc-layer-tree-quad-stack-view-template',THIS_DOC);Polymer.dom(this).appendChild(node);Polymer.dom(this).appendChild(this.controls_);Polymer.dom(this).appendChild(this.infoBar_);Polymer.dom(this).appendChild(this.quadStackView_);this.tileRectsSelector_=tr.ui.b.createSelector(this,'howToShowTiles','layerView.howToShowTiles','none',createTileRectsSelectorBaseOptions());Polymer.dom(this.controls_).appendChild(this.tileRectsSelector_);var tileHeatmapText=tr.ui.b.createSpan({textContent:'Tile heatmap:'});Polymer.dom(this.controls_).appendChild(tileHeatmapText);var tileHeatmapSelector=tr.ui.b.createSelector(this,'tileHeatmapType','layerView.tileHeatmapType',TILE_HEATMAP_TYPE.NONE,[{label:'None',value:TILE_HEATMAP_TYPE.NONE},{label:'Scheduled Priority',value:TILE_HEATMAP_TYPE.SCHEDULED_PRIORITY},{label:'Is using GPU memory',value:TILE_HEATMAP_TYPE.USING_GPU_MEMORY}]);Polymer.dom(this.controls_).appendChild(tileHeatmapSelector);var showOtherLayersCheckbox=tr.ui.b.createCheckBox(this,'showOtherLayers','layerView.showOtherLayers',true,'Other layers/passes');showOtherLayersCheckbox.title='When checked, show all layers, selected or not.';Polymer.dom(this.controls_).appendChild(showOtherLayersCheckbox);var showInvalidationsCheckbox=tr.ui.b.createCheckBox(this,'showInvalidations','layerView.showInvalidations',true,'Invalidations');showInvalidationsCheckbox.title='When checked, compositing invalidations are highlighted in red';Polymer.dom(this.controls_).appendChild(showInvalidationsCheckbox);var showUnrecordedRegionCheckbox=tr.ui.b.createCheckBox(this,'showUnrecordedRegion','layerView.showUnrecordedRegion',true,'Unrecorded area');showUnrecordedRegionCheckbox.title='When checked, unrecorded areas are highlighted in yellow';Polymer.dom(this.controls_).appendChild(showUnrecordedRegionCheckbox);var showBottlenecksCheckbox=tr.ui.b.createCheckBox(this,'showBottlenecks','layerView.showBottlenecks',true,'Bottlenecks');showBottlenecksCheckbox.title='When checked, scroll bottlenecks are highlighted';Polymer.dom(this.controls_).appendChild(showBottlenecksCheckbox);var showLayoutRectsCheckbox=tr.ui.b.createCheckBox(this,'showLayoutRects','layerView.showLayoutRects',false,'Layout rects');showLayoutRectsCheckbox.title='When checked, shows rects for regions where layout happened';Polymer.dom(this.controls_).appendChild(showLayoutRectsCheckbox);var showContentsCheckbox=tr.ui.b.createCheckBox(this,'showContents','layerView.showContents',true,'Contents');showContentsCheckbox.title='When checked, show the rendered contents inside the layer outlines';Polymer.dom(this.controls_).appendChild(showContentsCheckbox);var showAnimationBoundsCheckbox=tr.ui.b.createCheckBox(this,'showAnimationBounds','layerView.showAnimationBounds',false,'Animation Bounds');showAnimationBoundsCheckbox.title='When checked, show a border around'+' a layer showing the extent of its animation.';Polymer.dom(this.controls_).appendChild(showAnimationBoundsCheckbox);var showInputEventsCheckbox=tr.ui.b.createCheckBox(this,'showInputEvents','layerView.showInputEvents',true,'Input events');showInputEventsCheckbox.title='When checked, input events are '+'displayed as circles.';Polymer.dom(this.controls_).appendChild(showInputEventsCheckbox);this.whatRasterizedLink_=document.createElement('a');Polymer.dom(this.whatRasterizedLink_).classList.add('what-rasterized');Polymer.dom(this.whatRasterizedLink_).textContent='What rasterized?';this.whatRasterizedLink_.addEventListener('click',this.onWhatRasterizedLinkClicked_.bind(this));Polymer.dom(this).appendChild(this.whatRasterizedLink_);},get layerTreeImpl(){return this.layerTreeImpl_;},set isRenderPassQuads(newValue){this.isRenderPassQuads_=newValue;},set layerTreeImpl(layerTreeImpl){if(this.layerTreeImpl_===layerTreeImpl)
-return;this.layerTreeImpl_=layerTreeImpl;this.selection=undefined;},get extraHighlightsByLayerId(){return this.extraHighlightsByLayerId_;},set extraHighlightsByLayerId(extraHighlightsByLayerId){this.extraHighlightsByLayerId_=extraHighlightsByLayerId;this.scheduleUpdateContents_();},get showOtherLayers(){return this.showOtherLayers_;},set showOtherLayers(show){this.showOtherLayers_=show;this.updateContents_();},get showAnimationBounds(){return this.showAnimationBounds_;},set showAnimationBounds(show){this.showAnimationBounds_=show;this.updateContents_();},get showInputEvents(){return this.showInputEvents_;},set showInputEvents(show){this.showInputEvents_=show;this.updateContents_();},get showContents(){return this.showContents_;},set showContents(show){this.showContents_=show;this.updateContents_();},get showInvalidations(){return this.showInvalidations_;},set showInvalidations(show){this.showInvalidations_=show;this.updateContents_();},get showUnrecordedRegion(){return this.showUnrecordedRegion_;},set showUnrecordedRegion(show){this.showUnrecordedRegion_=show;this.updateContents_();},get showBottlenecks(){return this.showBottlenecks_;},set showBottlenecks(show){this.showBottlenecks_=show;this.updateContents_();},get showLayoutRects(){return this.showLayoutRects_;},set showLayoutRects(show){this.showLayoutRects_=show;this.updateContents_();},get howToShowTiles(){return this.howToShowTiles_;},set howToShowTiles(val){console.assert((val==='none')||(val==='coverage')||!isNaN(parseFloat(val)));this.howToShowTiles_=val;this.updateContents_();},get tileHeatmapType(){return this.tileHeatmapType_;},set tileHeatmapType(val){this.tileHeatmapType_=val;this.updateContents_();},get selection(){return this.selection_;},set selection(selection){if(this.selection===selection)
-return;this.selection_=selection;tr.b.dispatchSimpleEvent(this,'selection-change');this.updateContents_();},regenerateContent:function(){this.updateTilesSelector_();this.updateContents_();},loadDataForImageElement_:function(image,callback){var imageContent=window.getComputedStyle(image).backgroundImage;image.src=tr.ui.b.extractUrlString(imageContent);image.onload=function(){var canvas=document.createElement('canvas');var ctx=canvas.getContext('2d');canvas.width=image.width;canvas.height=image.height;ctx.drawImage(image,0,0);var imageData=ctx.getImageData(0,0,canvas.width,canvas.height);callback(imageData);};},onQuadStackViewSelectionChange_:function(e){var selectableQuads=e.quads.filter(function(q){return q.selectionToSetIfClicked!==undefined;});if(selectableQuads.length==0){this.selection=undefined;return;}
-selectableQuads.sort(function(x,y){var z=x.stackingGroupId-y.stackingGroupId;if(z!=0)
-return z;return x.selectionToSetIfClicked.specicifity-
-y.selectionToSetIfClicked.specicifity;});var quadToSelect=selectableQuads[selectableQuads.length-1];this.selection=quadToSelect.selectionToSetIfClicked;},scheduleUpdateContents_:function(){if(this.updateContentsPending_)
-return;this.updateContentsPending_=true;tr.b.requestAnimationFrameInThisFrameIfPossible(this.updateContents_,this);},updateContents_:function(){if(!this.layerTreeImpl_){this.quadStackView_.headerText='No tree';this.quadStackView_.quads=[];return;}
-var status=this.computePictureLoadingStatus_();if(!status.picturesComplete)
-return;var lthi=this.layerTreeImpl_.layerTreeHostImpl;var lthiInstance=lthi.objectInstance;var worldViewportRect=tr.b.Rect.fromXYWH(0,0,lthi.deviceViewportSize.width,lthi.deviceViewportSize.height);this.quadStackView_.deviceRect=worldViewportRect;if(this.isRenderPassQuads_)
-this.quadStackView_.quads=this.generateRenderPassQuads();else
-this.quadStackView_.quads=this.generateLayerQuads();this.updateWhatRasterizedLinkState_();var message='';if(lthi.tilesHaveGpuMemoryUsageInfo){var thisTreeUsageInBytes=this.layerTreeImpl_.gpuMemoryUsageInBytes;var otherTreeUsageInBytes=lthi.gpuMemoryUsageInBytes-
-thisTreeUsageInBytes;message+=tr.b.convertUnit(thisTreeUsageInBytes,tr.b.UnitScale.Binary.NONE,tr.b.UnitScale.Binary.MEBI).toFixed(1)+' MiB on this tree';if(otherTreeUsageInBytes){message+=', '+tr.b.convertUnit(otherTreeUsageInBytes,tr.b.UnitScale.Binary.NONE,tr.b.UnitScale.Binary.MEBI).toFixed(1)+' MiB on the other tree';}}else{if(this.layerTreeImpl_){var thisTreeUsageInBytes=this.layerTreeImpl_.gpuMemoryUsageInBytes;message+=tr.b.convertUnit(thisTreeUsageInBytes,tr.b.UnitScale.Binary.NONE,tr.b.UnitScale.Binary.MEBI).toFixed(1)+' MiB on this tree';if(this.layerTreeImpl_.otherTree){message+=', ??? MiB on other tree. ';}}}
+if(!this.quads_)return;var canvasCtx=this.canvas_.getContext('2d');if(!this.resize()){canvasCtx.clearRect(0,0,this.canvas_.width,this.canvas_.height);}
+var quadCanvas=document.createElement('canvas');this.stackTransformAndProcessQuads_(3,drawProjectedQuadToContext,true,canvasCtx,quadCanvas);quadCanvas.width=0;},trackMouse_:function(){this.mouseModeSelector_=document.createElement('tr-ui-b-mouse-mode-selector');this.mouseModeSelector_.targetElement=this.canvas_;this.mouseModeSelector_.supportedModeMask=tr.ui.b.MOUSE_SELECTOR_MODE.SELECTION|tr.ui.b.MOUSE_SELECTOR_MODE.PANSCAN|tr.ui.b.MOUSE_SELECTOR_MODE.ZOOM|tr.ui.b.MOUSE_SELECTOR_MODE.ROTATE;this.mouseModeSelector_.mode=tr.ui.b.MOUSE_SELECTOR_MODE.PANSCAN;this.mouseModeSelector_.pos={x:0,y:100};Polymer.dom(this).appendChild(this.mouseModeSelector_);this.mouseModeSelector_.settingsKey='quadStackView.mouseModeSelector';this.mouseModeSelector_.setModifierForAlternateMode(tr.ui.b.MOUSE_SELECTOR_MODE.ROTATE,tr.ui.b.MODIFIER.SHIFT);this.mouseModeSelector_.setModifierForAlternateMode(tr.ui.b.MOUSE_SELECTOR_MODE.PANSCAN,tr.ui.b.MODIFIER.SPACE);this.mouseModeSelector_.setModifierForAlternateMode(tr.ui.b.MOUSE_SELECTOR_MODE.ZOOM,tr.ui.b.MODIFIER.CMD_OR_CTRL);this.mouseModeSelector_.addEventListener('updateselection',this.onSelectionUpdate_.bind(this));this.mouseModeSelector_.addEventListener('endselection',this.onSelectionUpdate_.bind(this));},extractRelativeMousePosition_:function(e){var br=this.canvas_.getBoundingClientRect();return[this.pixelRatio_*(e.clientX-this.canvas_.offsetLeft-br.left),this.pixelRatio_*(e.clientY-this.canvas_.offsetTop-br.top)];},onSelectionUpdate_:function(e){var mousePos=this.extractRelativeMousePosition_(e);var res=[];function handleQuad(passNumber,quad,p1,p2,p3,p4){if(tr.b.math.pointInImplicitQuad(mousePos,p1,p2,p3,p4)){res.push(quad);}}
+this.stackTransformAndProcessQuads_(1,handleQuad,false);var e=new tr.b.Event('selectionchange');e.quads=res;this.dispatchEvent(e);}};return{QuadStackView,};});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){var ColorScheme=tr.b.ColorScheme;var THIS_DOC=document.currentScript.ownerDocument;var TILE_HEATMAP_TYPE={};TILE_HEATMAP_TYPE.NONE='none';TILE_HEATMAP_TYPE.SCHEDULED_PRIORITY='scheduledPriority';TILE_HEATMAP_TYPE.USING_GPU_MEMORY='usingGpuMemory';var cc=tr.ui.e.chrome.cc;function createTileRectsSelectorBaseOptions(){return[{label:'None',value:'none'},{label:'Coverage Rects',value:'coverage'}];}
+var LayerTreeQuadStackView=tr.ui.b.define('tr-ui-e-chrome-cc-layer-tree-quad-stack-view');LayerTreeQuadStackView.prototype={__proto__:HTMLDivElement.prototype,decorate:function(){this.isRenderPassQuads_=false;this.pictureAsImageData_={};this.messages_=[];this.controls_=document.createElement('top-controls');this.infoBar_=document.createElement('tr-ui-b-info-bar');this.quadStackView_=new tr.ui.b.QuadStackView();this.quadStackView_.addEventListener('selectionchange',this.onQuadStackViewSelectionChange_.bind(this));this.extraHighlightsByLayerId_=undefined;this.inputEventImageData_=undefined;var m=tr.ui.b.MOUSE_SELECTOR_MODE;var mms=this.quadStackView_.mouseModeSelector;mms.settingsKey='tr.e.cc.layerTreeQuadStackView.mouseModeSelector';mms.setKeyCodeForMode(m.SELECTION,'Z'.charCodeAt(0));mms.setKeyCodeForMode(m.PANSCAN,'X'.charCodeAt(0));mms.setKeyCodeForMode(m.ZOOM,'C'.charCodeAt(0));mms.setKeyCodeForMode(m.ROTATE,'V'.charCodeAt(0));var node=tr.ui.b.instantiateTemplate('#tr-ui-e-chrome-cc-layer-tree-quad-stack-view-template',THIS_DOC);Polymer.dom(this).appendChild(node);Polymer.dom(this).appendChild(this.controls_);Polymer.dom(this).appendChild(this.infoBar_);Polymer.dom(this).appendChild(this.quadStackView_);this.tileRectsSelector_=tr.ui.b.createSelector(this,'howToShowTiles','layerView.howToShowTiles','none',createTileRectsSelectorBaseOptions());Polymer.dom(this.controls_).appendChild(this.tileRectsSelector_);var tileHeatmapText=tr.ui.b.createSpan({textContent:'Tile heatmap:'});Polymer.dom(this.controls_).appendChild(tileHeatmapText);var tileHeatmapSelector=tr.ui.b.createSelector(this,'tileHeatmapType','layerView.tileHeatmapType',TILE_HEATMAP_TYPE.NONE,[{label:'None',value:TILE_HEATMAP_TYPE.NONE},{label:'Scheduled Priority',value:TILE_HEATMAP_TYPE.SCHEDULED_PRIORITY},{label:'Is using GPU memory',value:TILE_HEATMAP_TYPE.USING_GPU_MEMORY}]);Polymer.dom(this.controls_).appendChild(tileHeatmapSelector);var showOtherLayersCheckbox=tr.ui.b.createCheckBox(this,'showOtherLayers','layerView.showOtherLayers',true,'Other layers/passes');showOtherLayersCheckbox.title='When checked, show all layers, selected or not.';Polymer.dom(this.controls_).appendChild(showOtherLayersCheckbox);var showInvalidationsCheckbox=tr.ui.b.createCheckBox(this,'showInvalidations','layerView.showInvalidations',true,'Invalidations');showInvalidationsCheckbox.title='When checked, compositing invalidations are highlighted in red';Polymer.dom(this.controls_).appendChild(showInvalidationsCheckbox);var showUnrecordedRegionCheckbox=tr.ui.b.createCheckBox(this,'showUnrecordedRegion','layerView.showUnrecordedRegion',true,'Unrecorded area');showUnrecordedRegionCheckbox.title='When checked, unrecorded areas are highlighted in yellow';Polymer.dom(this.controls_).appendChild(showUnrecordedRegionCheckbox);var showBottlenecksCheckbox=tr.ui.b.createCheckBox(this,'showBottlenecks','layerView.showBottlenecks',true,'Bottlenecks');showBottlenecksCheckbox.title='When checked, scroll bottlenecks are highlighted';Polymer.dom(this.controls_).appendChild(showBottlenecksCheckbox);var showLayoutRectsCheckbox=tr.ui.b.createCheckBox(this,'showLayoutRects','layerView.showLayoutRects',false,'Layout rects');showLayoutRectsCheckbox.title='When checked, shows rects for regions where layout happened';Polymer.dom(this.controls_).appendChild(showLayoutRectsCheckbox);var showContentsCheckbox=tr.ui.b.createCheckBox(this,'showContents','layerView.showContents',true,'Contents');showContentsCheckbox.title='When checked, show the rendered contents inside the layer outlines';Polymer.dom(this.controls_).appendChild(showContentsCheckbox);var showAnimationBoundsCheckbox=tr.ui.b.createCheckBox(this,'showAnimationBounds','layerView.showAnimationBounds',false,'Animation Bounds');showAnimationBoundsCheckbox.title='When checked, show a border around'+' a layer showing the extent of its animation.';Polymer.dom(this.controls_).appendChild(showAnimationBoundsCheckbox);var showInputEventsCheckbox=tr.ui.b.createCheckBox(this,'showInputEvents','layerView.showInputEvents',true,'Input events');showInputEventsCheckbox.title='When checked, input events are '+'displayed as circles.';Polymer.dom(this.controls_).appendChild(showInputEventsCheckbox);this.whatRasterizedLink_=document.createElement('a');Polymer.dom(this.whatRasterizedLink_).classList.add('what-rasterized');Polymer.dom(this.whatRasterizedLink_).textContent='What rasterized?';this.whatRasterizedLink_.addEventListener('click',this.onWhatRasterizedLinkClicked_.bind(this));Polymer.dom(this).appendChild(this.whatRasterizedLink_);},get layerTreeImpl(){return this.layerTreeImpl_;},set isRenderPassQuads(newValue){this.isRenderPassQuads_=newValue;},set layerTreeImpl(layerTreeImpl){if(this.layerTreeImpl_===layerTreeImpl)return;this.layerTreeImpl_=layerTreeImpl;this.selection=undefined;},get extraHighlightsByLayerId(){return this.extraHighlightsByLayerId_;},set extraHighlightsByLayerId(extraHighlightsByLayerId){this.extraHighlightsByLayerId_=extraHighlightsByLayerId;this.scheduleUpdateContents_();},get showOtherLayers(){return this.showOtherLayers_;},set showOtherLayers(show){this.showOtherLayers_=show;this.updateContents_();},get showAnimationBounds(){return this.showAnimationBounds_;},set showAnimationBounds(show){this.showAnimationBounds_=show;this.updateContents_();},get showInputEvents(){return this.showInputEvents_;},set showInputEvents(show){this.showInputEvents_=show;this.updateContents_();},get showContents(){return this.showContents_;},set showContents(show){this.showContents_=show;this.updateContents_();},get showInvalidations(){return this.showInvalidations_;},set showInvalidations(show){this.showInvalidations_=show;this.updateContents_();},get showUnrecordedRegion(){return this.showUnrecordedRegion_;},set showUnrecordedRegion(show){this.showUnrecordedRegion_=show;this.updateContents_();},get showBottlenecks(){return this.showBottlenecks_;},set showBottlenecks(show){this.showBottlenecks_=show;this.updateContents_();},get showLayoutRects(){return this.showLayoutRects_;},set showLayoutRects(show){this.showLayoutRects_=show;this.updateContents_();},get howToShowTiles(){return this.howToShowTiles_;},set howToShowTiles(val){if(val!=='none'&&val!=='coverage'&&isNaN(parseFloat(val))){throw new Error('howToShowTiles requires "none" or "coverage" or a number');}
+this.howToShowTiles_=val;this.updateContents_();},get tileHeatmapType(){return this.tileHeatmapType_;},set tileHeatmapType(val){this.tileHeatmapType_=val;this.updateContents_();},get selection(){return this.selection_;},set selection(selection){if(this.selection===selection)return;this.selection_=selection;tr.b.dispatchSimpleEvent(this,'selection-change');this.updateContents_();},regenerateContent:function(){this.updateTilesSelector_();this.updateContents_();},loadDataForImageElement_:function(image,callback){var imageContent=window.getComputedStyle(image).backgroundImage;image.src=tr.ui.b.extractUrlString(imageContent);image.onload=function(){var canvas=document.createElement('canvas');var ctx=canvas.getContext('2d');canvas.width=image.width;canvas.height=image.height;ctx.drawImage(image,0,0);var imageData=ctx.getImageData(0,0,canvas.width,canvas.height);callback(imageData);};},onQuadStackViewSelectionChange_:function(e){var selectableQuads=e.quads.filter(function(q){return q.selectionToSetIfClicked!==undefined;});if(selectableQuads.length===0){this.selection=undefined;return;}
+selectableQuads.sort(function(x,y){var z=x.stackingGroupId-y.stackingGroupId;if(z!==0)return z;return x.selectionToSetIfClicked.specicifity-
+y.selectionToSetIfClicked.specicifity;});var quadToSelect=selectableQuads[selectableQuads.length-1];this.selection=quadToSelect.selectionToSetIfClicked;},scheduleUpdateContents_:function(){if(this.updateContentsPending_)return;this.updateContentsPending_=true;tr.b.requestAnimationFrameInThisFrameIfPossible(this.updateContents_,this);},updateContents_:function(){if(!this.layerTreeImpl_){this.quadStackView_.headerText='No tree';this.quadStackView_.quads=[];return;}
+var status=this.computePictureLoadingStatus_();if(!status.picturesComplete)return;var lthi=this.layerTreeImpl_.layerTreeHostImpl;var lthiInstance=lthi.objectInstance;var worldViewportRect=tr.b.math.Rect.fromXYWH(0,0,lthi.deviceViewportSize.width,lthi.deviceViewportSize.height);this.quadStackView_.deviceRect=worldViewportRect;if(this.isRenderPassQuads_){this.quadStackView_.quads=this.generateRenderPassQuads();}else{this.quadStackView_.quads=this.generateLayerQuads();}
+this.updateWhatRasterizedLinkState_();var message='';if(lthi.tilesHaveGpuMemoryUsageInfo){var thisTreeUsageInBytes=this.layerTreeImpl_.gpuMemoryUsageInBytes;var otherTreeUsageInBytes=lthi.gpuMemoryUsageInBytes-
+thisTreeUsageInBytes;message+=tr.b.convertUnit(thisTreeUsageInBytes,tr.b.UnitPrefixScale.BINARY.NONE,tr.b.UnitPrefixScale.BINARY.MEBI).toFixed(1)+' MiB on this tree';if(otherTreeUsageInBytes){message+=', '+
+tr.b.convertUnit(otherTreeUsageInBytes,tr.b.UnitPrefixScale.BINARY.NONE,tr.b.UnitPrefixScale.BINARY.MEBI).toFixed(1)+' MiB on the other tree';}}else{if(this.layerTreeImpl_){var thisTreeUsageInBytes=this.layerTreeImpl_.gpuMemoryUsageInBytes;message+=tr.b.convertUnit(thisTreeUsageInBytes,tr.b.UnitPrefixScale.BINARY.NONE,tr.b.UnitPrefixScale.BINARY.MEBI).toFixed(1)+' MiB on this tree';if(this.layerTreeImpl_.otherTree){message+=', ??? MiB on other tree. ';}}}
 if(lthi.args.tileManagerBasicState){var tmgs=lthi.args.tileManagerBasicState.globalState;message+=' (softMax='+
-tr.b.convertUnit(tmgs.softMemoryLimitInBytes,tr.b.UnitScale.Binary.NONE,tr.b.UnitScale.Binary.MEBI).toFixed(1)+' MiB, hardMax='+
-tr.b.convertUnit(tmgs.hardMemoryLimitInBytes,tr.b.UnitScale.Binary.NONE,tr.b.UnitScale.Binary.MEBI).toFixed(1)+' MiB, '+
-tmgs.memoryLimitPolicy+')';}else{var thread=lthi.snapshottedOnThread;var didManageTilesSlices=thread.sliceGroup.slices.filter(function(s){if(s.category!=='tr.e.cc')
-return false;if(s.title!=='DidManage')
-return false;if(s.end>lthi.ts)
-return false;return true;});didManageTilesSlices.sort(function(x,y){return x.end-y.end;});if(didManageTilesSlices.length>0){var newest=didManageTilesSlices[didManageTilesSlices.length-1];var tmgs=newest.args.state.global_state;message+=' (softMax='+
-tr.b.convertUnit(tmgs.softMemoryLimitInBytes,tr.b.UnitScale.Binary.NONE,tr.b.UnitScale.Binary.MEBI).toFixed(1)+' MiB, hardMax='+
-tr.b.convertUnit(tmgs.hardMemoryLimitInBytes,tr.b.UnitScale.Binary.NONE,tr.b.UnitScale.Binary.MEBI).toFixed(1)+' MiB, '+
+tr.b.convertUnit(tmgs.softMemoryLimitInBytes,tr.b.UnitPrefixScale.BINARY.NONE,tr.b.UnitPrefixScale.BINARY.MEBI).toFixed(1)+' MiB, hardMax='+
+tr.b.convertUnit(tmgs.hardMemoryLimitInBytes,tr.b.UnitPrefixScale.BINARY.NONE,tr.b.UnitPrefixScale.BINARY.MEBI).toFixed(1)+' MiB, '+
+tmgs.memoryLimitPolicy+')';}else{var thread=lthi.snapshottedOnThread;var didManageTilesSlices=thread.sliceGroup.slices.filter(function(s){if(s.category!=='tr.e.cc')return false;if(s.title!=='DidManage')return false;if(s.end>lthi.ts)return false;return true;});didManageTilesSlices.sort(function(x,y){return x.end-y.end;});if(didManageTilesSlices.length>0){var newest=didManageTilesSlices[didManageTilesSlices.length-1];var tmgs=newest.args.state.global_state;message+=' (softMax='+
+tr.b.convertUnit(tmgs.softMemoryLimitInBytes,tr.b.UnitPrefixScale.BINARY.NONE,tr.b.UnitPrefixScale.BINARY.MEBI).toFixed(1)+' MiB, hardMax='+
+tr.b.convertUnit(tmgs.hardMemoryLimitInBytes,tr.b.UnitPrefixScale.BINARY.NONE,tr.b.UnitPrefixScale.BINARY.MEBI).toFixed(1)+' MiB, '+
 tmgs.memoryLimitPolicy+')';}}
-if(this.layerTreeImpl_.otherTree)
-message+=' (Another tree exists)';if(message.length)
-this.quadStackView_.headerText=message;else
-this.quadStackView_.headerText=undefined;this.updateInfoBar_(status.messages);},updateTilesSelector_:function(){var data=createTileRectsSelectorBaseOptions();if(this.layerTreeImpl_){var lthi=this.layerTreeImpl_.layerTreeHostImpl;var scaleNames=lthi.getContentsScaleNames();for(var scale in scaleNames){data.push({label:'Scale '+scale+' ('+scaleNames[scale]+')',value:scale});}}
-var new_selector=tr.ui.b.createSelector(this,'howToShowTiles','layerView.howToShowTiles','none',data);this.controls_.replaceChild(new_selector,this.tileRectsSelector_);this.tileRectsSelector_=new_selector;},computePictureLoadingStatus_:function(){var layers=this.layers;var status={messages:[],picturesComplete:true};if(this.showContents){var hasPendingRasterizeImage=false;var firstPictureError=undefined;var hasMissingLayerRect=false;var hasUnresolvedPictureRef=false;for(var i=0;i<layers.length;i++){var layer=layers[i];for(var ir=0;ir<layer.pictures.length;++ir){var picture=layer.pictures[ir];if(picture.idRef){hasUnresolvedPictureRef=true;continue;}
+if(this.layerTreeImpl_.otherTree){message+=' (Another tree exists)';}
+if(message.length){this.quadStackView_.headerText=message;}else{this.quadStackView_.headerText=undefined;}
+this.updateInfoBar_(status.messages);},updateTilesSelector_:function(){var data=createTileRectsSelectorBaseOptions();if(this.layerTreeImpl_){var lthi=this.layerTreeImpl_.layerTreeHostImpl;var scaleNames=lthi.getContentsScaleNames();for(var scale in scaleNames){data.push({label:'Scale '+scale+' ('+scaleNames[scale]+')',value:scale});}}
+var newSelector=tr.ui.b.createSelector(this,'howToShowTiles','layerView.howToShowTiles','none',data);this.controls_.replaceChild(newSelector,this.tileRectsSelector_);this.tileRectsSelector_=newSelector;},computePictureLoadingStatus_:function(){var layers=this.layers;var status={messages:[],picturesComplete:true};if(this.showContents){var hasPendingRasterizeImage=false;var firstPictureError=undefined;var hasMissingLayerRect=false;var hasUnresolvedPictureRef=false;for(var i=0;i<layers.length;i++){var layer=layers[i];for(var ir=0;ir<layer.pictures.length;++ir){var picture=layer.pictures[ir];if(picture.idRef){hasUnresolvedPictureRef=true;continue;}
 if(!picture.layerRect){hasMissingLayerRect=true;continue;}
 var pictureAsImageData=this.pictureAsImageData_[picture.guid];if(!pictureAsImageData){hasPendingRasterizeImage=true;this.pictureAsImageData_[picture.guid]=tr.e.cc.PictureAsImageData.Pending(this);picture.rasterize({stopIndex:undefined},function(pictureImageData){var picture_=pictureImageData.picture;this.pictureAsImageData_[picture_.guid]=pictureImageData;this.scheduleUpdateContents_();}.bind(this));continue;}
 if(pictureAsImageData.isPending()){hasPendingRasterizeImage=true;continue;}
-if(pictureAsImageData.error){if(!firstPictureError)
-firstPictureError=pictureAsImageData.error;break;}}}
-if(hasPendingRasterizeImage){status.picturesComplete=false;}else{if(hasUnresolvedPictureRef){status.messages.push({header:'Missing picture',details:'Your trace didnt have pictures for every layer. '+'Old chrome versions had this problem'});}
+if(pictureAsImageData.error){if(!firstPictureError){firstPictureError=pictureAsImageData.error;}
+break;}}}
+if(hasPendingRasterizeImage){status.picturesComplete=false;}else{if(hasUnresolvedPictureRef){status.messages.push({header:'Missing picture',details:'Your trace didn\'t have pictures for every layer. '+'Old chrome versions had this problem'});}
 if(hasMissingLayerRect){status.messages.push({header:'Missing layer rect',details:'Your trace may be corrupt or from a very old '+'Chrome revision.'});}
 if(firstPictureError){status.messages.push({header:'Cannot rasterize',details:firstPictureError});}}}
 if(this.showInputEvents&&this.layerTreeImpl.tracedInputLatencies&&this.inputEventImageData_===undefined){var image=Polymer.dom(this).querySelector('#input-event');if(!image.src){this.loadDataForImageElement_(image,function(imageData){this.inputEventImageData_=imageData;this.updateContentsPending_=false;this.scheduleUpdateContents_();}.bind(this));}
 status.picturesComplete=false;}
-return status;},get selectedRenderPass(){if(this.selection)
-return this.selection.renderPass_;},get selectedLayer(){if(this.selection){var selectedLayerId=this.selection.associatedLayerId;return this.layerTreeImpl_.findLayerWithId(selectedLayerId);}},get renderPasses(){var renderPasses=this.layerTreeImpl.layerTreeHostImpl.args.frame.renderPasses;if(!this.showOtherLayers){var selectedRenderPass=this.selectedRenderPass;if(selectedRenderPass)
-renderPasses=[selectedRenderPass];}
-return renderPasses;},get layers(){var layers=this.layerTreeImpl.renderSurfaceLayerList;if(!this.showOtherLayers){var selectedLayer=this.selectedLayer;if(selectedLayer)
-layers=[selectedLayer];}
-return layers;},appendImageQuads_:function(quads,layer,layerQuad){for(var ir=0;ir<layer.pictures.length;++ir){var picture=layer.pictures[ir];if(!picture.layerRect)
-continue;var unitRect=picture.layerRect.asUVRectInside(layer.bounds);var iq=layerQuad.projectUnitRect(unitRect);var pictureData=this.pictureAsImageData_[picture.guid];if(this.showContents&&pictureData&&pictureData.imageData){iq.imageData=pictureData.imageData;iq.borderColor='rgba(0,0,0,0)';}else{iq.imageData=undefined;}
-iq.stackingGroupId=layerQuad.stackingGroupId;quads.push(iq);}},appendAnimationQuads_:function(quads,layer,layerQuad){if(!layer.animationBoundsRect)
-return;var rect=layer.animationBoundsRect;var abq=tr.b.Quad.fromRect(rect);abq.backgroundColor='rgba(164,191,48,0.5)';abq.borderColor='rgba(205,255,0,0.75)';abq.borderWidth=3.0;abq.stackingGroupId=layerQuad.stackingGroupId;abq.selectionToSetIfClicked=new cc.AnimationRectSelection(layer,rect);quads.push(abq);},appendInvalidationQuads_:function(quads,layer,layerQuad){if(layer.layerTreeImpl.hasSourceFrameBeenDrawnBefore)
-return;for(var ir=0;ir<layer.annotatedInvalidation.rects.length;ir++){var rect=layer.annotatedInvalidation.rects[ir];var unitRect=rect.asUVRectInside(layer.bounds);var iq=layerQuad.projectUnitRect(unitRect);iq.backgroundColor='rgba(0, 255, 0, 0.1)';if(rect.reason==='renderer insertion')
-iq.backgroundColor='rgba(0, 255, 128, 0.1)';iq.borderColor='rgba(0, 255, 0, 1)';iq.stackingGroupId=layerQuad.stackingGroupId;iq.selectionToSetIfClicked=new cc.LayerRectSelection(layer,'Invalidation rect ('+rect.reason+')',rect,rect);quads.push(iq);}
-if(layer.annotatedInvalidation.rects.length===0){for(var ir=0;ir<layer.invalidation.rects.length;ir++){var rect=layer.invalidation.rects[ir];var unitRect=rect.asUVRectInside(layer.bounds);var iq=layerQuad.projectUnitRect(unitRect);iq.backgroundColor='rgba(0, 255, 0, 0.1)';iq.borderColor='rgba(0, 255, 0, 1)';iq.stackingGroupId=layerQuad.stackingGroupId;iq.selectionToSetIfClicked=new cc.LayerRectSelection(layer,'Invalidation rect',rect,rect);quads.push(iq);}}},appendUnrecordedRegionQuads_:function(quads,layer,layerQuad){for(var ir=0;ir<layer.unrecordedRegion.rects.length;ir++){var rect=layer.unrecordedRegion.rects[ir];var unitRect=rect.asUVRectInside(layer.bounds);var iq=layerQuad.projectUnitRect(unitRect);iq.backgroundColor='rgba(240, 230, 140, 0.3)';iq.borderColor='rgba(240, 230, 140, 1)';iq.stackingGroupId=layerQuad.stackingGroupId;iq.selectionToSetIfClicked=new cc.LayerRectSelection(layer,'Unrecorded area',rect,rect);quads.push(iq);}},appendBottleneckQuads_:function(quads,layer,layerQuad,stackingGroupId){function processRegion(region,label,borderColor){var backgroundColor=borderColor.clone();backgroundColor.a=0.4*(borderColor.a||1.0);if(!region||!region.rects)
-return;for(var ir=0;ir<region.rects.length;ir++){var rect=region.rects[ir];var unitRect=rect.asUVRectInside(layer.bounds);var iq=layerQuad.projectUnitRect(unitRect);iq.backgroundColor=backgroundColor.toString();iq.borderColor=borderColor.toString();iq.borderWidth=4.0;iq.stackingGroupId=stackingGroupId;iq.selectionToSetIfClicked=new cc.LayerRectSelection(layer,label,rect,rect);quads.push(iq);}}
-processRegion(layer.touchEventHandlerRegion,'Touch listener',tr.b.Color.fromString('rgb(228, 226, 27)'));processRegion(layer.wheelEventHandlerRegion,'Wheel listener',tr.b.Color.fromString('rgb(176, 205, 29)'));processRegion(layer.nonFastScrollableRegion,'Repaints on scroll',tr.b.Color.fromString('rgb(213, 134, 32)'));},appendTileCoverageRectQuads_:function(quads,layer,layerQuad,heatmapType){if(!layer.tileCoverageRects)
-return;var tiles=[];for(var ct=0;ct<layer.tileCoverageRects.length;++ct){var tile=layer.tileCoverageRects[ct].tile;if(tile!==undefined)
-tiles.push(tile);}
+return status;},get selectedRenderPass(){if(this.selection){return this.selection.renderPass_;}},get selectedLayer(){if(this.selection){var selectedLayerId=this.selection.associatedLayerId;return this.layerTreeImpl_.findLayerWithId(selectedLayerId);}},get renderPasses(){var renderPasses=this.layerTreeImpl.layerTreeHostImpl.args.frame.renderPasses;if(!this.showOtherLayers){var selectedRenderPass=this.selectedRenderPass;if(selectedRenderPass){renderPasses=[selectedRenderPass];}}
+return renderPasses;},get layers(){var layers=this.layerTreeImpl.renderSurfaceLayerList;if(!this.showOtherLayers){var selectedLayer=this.selectedLayer;if(selectedLayer){layers=[selectedLayer];}}
+return layers;},appendImageQuads_:function(quads,layer,layerQuad){for(var ir=0;ir<layer.pictures.length;++ir){var picture=layer.pictures[ir];if(!picture.layerRect)continue;var unitRect=picture.layerRect.asUVRectInside(layer.bounds);var iq=layerQuad.projectUnitRect(unitRect);var pictureData=this.pictureAsImageData_[picture.guid];if(this.showContents&&pictureData&&pictureData.imageData){iq.imageData=pictureData.imageData;iq.borderColor='rgba(0,0,0,0)';}else{iq.imageData=undefined;}
+iq.stackingGroupId=layerQuad.stackingGroupId;quads.push(iq);}},appendAnimationQuads_:function(quads,layer,layerQuad){if(!layer.animationBoundsRect)return;var rect=layer.animationBoundsRect;var abq=tr.b.math.Quad.fromRect(rect);abq.backgroundColor='rgba(164,191,48,0.5)';abq.borderColor='rgba(205,255,0,0.75)';abq.borderWidth=3.0;abq.stackingGroupId=layerQuad.stackingGroupId;abq.selectionToSetIfClicked=new cc.AnimationRectSelection(layer,rect);quads.push(abq);},appendInvalidationQuads_:function(quads,layer,layerQuad){if(layer.layerTreeImpl.hasSourceFrameBeenDrawnBefore)return;for(var ir=0;ir<layer.annotatedInvalidation.rects.length;ir++){var rect=layer.annotatedInvalidation.rects[ir];var unitRect=rect.asUVRectInside(layer.bounds);var iq=layerQuad.projectUnitRect(unitRect);iq.backgroundColor='rgba(0, 255, 0, 0.1)';if(rect.reason==='renderer insertion'){iq.backgroundColor='rgba(0, 255, 128, 0.1)';}
+iq.borderColor='rgba(0, 255, 0, 1)';iq.stackingGroupId=layerQuad.stackingGroupId;iq.selectionToSetIfClicked=new cc.LayerRectSelection(layer,'Invalidation rect ('+rect.reason+')',rect,rect);quads.push(iq);}
+if(layer.annotatedInvalidation.rects.length===0){for(var ir=0;ir<layer.invalidation.rects.length;ir++){var rect=layer.invalidation.rects[ir];var unitRect=rect.asUVRectInside(layer.bounds);var iq=layerQuad.projectUnitRect(unitRect);iq.backgroundColor='rgba(0, 255, 0, 0.1)';iq.borderColor='rgba(0, 255, 0, 1)';iq.stackingGroupId=layerQuad.stackingGroupId;iq.selectionToSetIfClicked=new cc.LayerRectSelection(layer,'Invalidation rect',rect,rect);quads.push(iq);}}},appendUnrecordedRegionQuads_:function(quads,layer,layerQuad){for(var ir=0;ir<layer.unrecordedRegion.rects.length;ir++){var rect=layer.unrecordedRegion.rects[ir];var unitRect=rect.asUVRectInside(layer.bounds);var iq=layerQuad.projectUnitRect(unitRect);iq.backgroundColor='rgba(240, 230, 140, 0.3)';iq.borderColor='rgba(240, 230, 140, 1)';iq.stackingGroupId=layerQuad.stackingGroupId;iq.selectionToSetIfClicked=new cc.LayerRectSelection(layer,'Unrecorded area',rect,rect);quads.push(iq);}},appendBottleneckQuads_:function(quads,layer,layerQuad,stackingGroupId){function processRegion(region,label,borderColor){var backgroundColor=borderColor.clone();backgroundColor.a=0.4*(borderColor.a||1.0);if(!region||!region.rects)return;for(var ir=0;ir<region.rects.length;ir++){var rect=region.rects[ir];var unitRect=rect.asUVRectInside(layer.bounds);var iq=layerQuad.projectUnitRect(unitRect);iq.backgroundColor=backgroundColor.toString();iq.borderColor=borderColor.toString();iq.borderWidth=4.0;iq.stackingGroupId=stackingGroupId;iq.selectionToSetIfClicked=new cc.LayerRectSelection(layer,label,rect,rect);quads.push(iq);}}
+processRegion(layer.touchEventHandlerRegion,'Touch listener',tr.b.Color.fromString('rgb(228, 226, 27)'));processRegion(layer.wheelEventHandlerRegion,'Wheel listener',tr.b.Color.fromString('rgb(176, 205, 29)'));processRegion(layer.nonFastScrollableRegion,'Repaints on scroll',tr.b.Color.fromString('rgb(213, 134, 32)'));},appendTileCoverageRectQuads_:function(quads,layer,layerQuad,heatmapType){if(!layer.tileCoverageRects)return;var tiles=[];for(var ct=0;ct<layer.tileCoverageRects.length;++ct){var tile=layer.tileCoverageRects[ct].tile;if(tile!==undefined)tiles.push(tile);}
 var lthi=this.layerTreeImpl_.layerTreeHostImpl;var minMax=this.getMinMaxForHeatmap_(lthi.activeTiles,heatmapType);var heatmapResult=this.computeHeatmapColors_(tiles,minMax,heatmapType);var heatIndex=0;for(var ct=0;ct<layer.tileCoverageRects.length;++ct){var rect=layer.tileCoverageRects[ct].geometryRect;rect=rect.scale(1.0/layer.geometryContentsScale);var tile=layer.tileCoverageRects[ct].tile;var unitRect=rect.asUVRectInside(layer.bounds);var quad=layerQuad.projectUnitRect(unitRect);quad.backgroundColor='rgba(0, 0, 0, 0)';quad.stackingGroupId=layerQuad.stackingGroupId;var type=tr.e.cc.tileTypes.missing;if(tile){type=tile.getTypeForLayer(layer);quad.backgroundColor=heatmapResult[heatIndex].color;++heatIndex;}
-quad.borderColor=tr.e.cc.tileBorder[type].color;quad.borderWidth=tr.e.cc.tileBorder[type].width;var label;if(tile)
-label='coverageRect';else
-label='checkerboard coverageRect';quad.selectionToSetIfClicked=new cc.LayerRectSelection(layer,label,rect,layer.tileCoverageRects[ct]);quads.push(quad);}},appendLayoutRectQuads_:function(quads,layer,layerQuad){if(!layer.layoutRects){return;}
-for(var ct=0;ct<layer.layoutRects.length;++ct){var rect=layer.layoutRects[ct].geometryRect;rect=rect.scale(1.0/layer.geometryContentsScale);var unitRect=rect.asUVRectInside(layer.bounds);var quad=layerQuad.projectUnitRect(unitRect);quad.backgroundColor='rgba(0, 0, 0, 0)';quad.stackingGroupId=layerQuad.stackingGroupId;quad.borderColor='rgba(0, 0, 200, 0.7)';quad.borderWidth=2;var label;label='Layout rect';quad.selectionToSetIfClicked=new cc.LayerRectSelection(layer,label,rect);quads.push(quad);}},getValueForHeatmap_:function(tile,heatmapType){if(heatmapType==TILE_HEATMAP_TYPE.SCHEDULED_PRIORITY){return tile.scheduledPriority==0?undefined:tile.scheduledPriority;}else if(heatmapType==TILE_HEATMAP_TYPE.USING_GPU_MEMORY){if(tile.isSolidColor)
-return 0.5;return tile.isUsingGpuMemory?0:1;}},getMinMaxForHeatmap_:function(tiles,heatmapType){var range=new tr.b.Range();if(heatmapType==TILE_HEATMAP_TYPE.USING_GPU_MEMORY){range.addValue(0);range.addValue(1);return range;}
-for(var i=0;i<tiles.length;++i){var value=this.getValueForHeatmap_(tiles[i],heatmapType);if(value===undefined)
-continue;range.addValue(value);}
-if(range.range===0)
-range.addValue(1);return range;},computeHeatmapColors_:function(tiles,minMax,heatmapType){var min=minMax.min;var max=minMax.max;var color=function(value){var hue=120*(1-(value-min)/(max-min));if(hue<0)
-hue=0;return'hsla('+hue+', 100%, 50%, 0.5)';};var values=[];for(var i=0;i<tiles.length;++i){var tile=tiles[i];var value=this.getValueForHeatmap_(tile,heatmapType);var res={value:value,color:value!==undefined?color(value):undefined};values.push(res);}
-return values;},appendTilesWithScaleQuads_:function(quads,layer,layerQuad,scale,heatmapType){var lthi=this.layerTreeImpl_.layerTreeHostImpl;var tiles=[];for(var i=0;i<lthi.activeTiles.length;++i){var tile=lthi.activeTiles[i];if(Math.abs(tile.contentsScale-scale)>1e-6)
-continue;if(layer.layerId!=tile.layerId)
-continue;tiles.push(tile);}
-var minMax=this.getMinMaxForHeatmap_(lthi.activeTiles,heatmapType);var heatmapResult=this.computeHeatmapColors_(tiles,minMax,heatmapType);for(var i=0;i<tiles.length;++i){var tile=tiles[i];var rect=tile.layerRect;if(!tile.layerRect)
-continue;var unitRect=rect.asUVRectInside(layer.bounds);var quad=layerQuad.projectUnitRect(unitRect);quad.backgroundColor='rgba(0, 0, 0, 0)';quad.stackingGroupId=layerQuad.stackingGroupId;var type=tile.getTypeForLayer(layer);quad.borderColor=tr.e.cc.tileBorder[type].color;quad.borderWidth=tr.e.cc.tileBorder[type].width;quad.backgroundColor=heatmapResult[i].color;var data={tileType:type};if(heatmapType!==TILE_HEATMAP_TYPE.NONE)
-data[heatmapType]=heatmapResult[i].value;quad.selectionToSetIfClicked=new cc.TileSelection(tile,data);quads.push(quad);}},appendHighlightQuadsForLayer_:function(quads,layer,layerQuad,highlights){highlights.forEach(function(highlight){var rect=highlight.rect;var unitRect=rect.asUVRectInside(layer.bounds);var quad=layerQuad.projectUnitRect(unitRect);var colorId=ColorScheme.getColorIdForGeneralPurposeString(highlight.colorKey);colorId+=ColorScheme.properties.brightenedOffsets[0];var color=ColorScheme.colors[colorId];var quadForDrawing=quad.clone();quadForDrawing.backgroundColor=color.withAlpha(0.5).toString();quadForDrawing.borderColor=color.withAlpha(1.0).darken().toString();quadForDrawing.stackingGroupId=layerQuad.stackingGroupId;quads.push(quadForDrawing);},this);},generateRenderPassQuads:function(){if(!this.layerTreeImpl.layerTreeHostImpl.args.frame)
-return[];var renderPasses=this.renderPasses;if(!renderPasses)
-return[];var quads=[];for(var i=0;i<renderPasses.length;++i){var quadList=renderPasses[i].quadList;for(var j=0;j<quadList.length;++j){var drawQuad=quadList[j];var quad=drawQuad.rectAsTargetSpaceQuad.clone();quad.borderColor='rgb(170, 204, 238)';quad.borderWidth=2;quad.stackingGroupId=i;quads.push(quad);}}
-return quads;},generateLayerQuads:function(){this.updateContentsPending_=false;var layers=this.layers;var quads=[];var nextStackingGroupId=0;var alreadyVisitedLayerIds={};var selectionHighlightsByLayerId;if(this.selection)
-selectionHighlightsByLayerId=this.selection.highlightsByLayerId;else
-selectionHighlightsByLayerId={};var extraHighlightsByLayerId=this.extraHighlightsByLayerId||{};for(var i=1;i<=layers.length;i++){var layer=layers[layers.length-i];alreadyVisitedLayerIds[layer.layerId]=true;if(layer.objectInstance.name=='cc::NinePatchLayerImpl')
-continue;var layerQuad=layer.layerQuad.clone();if(layer.usingGpuRasterization){var pixelRatio=window.devicePixelRatio||1;layerQuad.borderWidth=2.0*pixelRatio;layerQuad.borderColor='rgba(154,205,50,0.75)';}else{layerQuad.borderColor='rgba(0,0,0,0.75)';}
-layerQuad.stackingGroupId=nextStackingGroupId++;layerQuad.selectionToSetIfClicked=new cc.LayerSelection(layer);layerQuad.layer=layer;if(this.showOtherLayers&&this.selectedLayer==layer)
-layerQuad.upperBorderColor='rgb(156,189,45)';if(this.showAnimationBounds)
-this.appendAnimationQuads_(quads,layer,layerQuad);this.appendImageQuads_(quads,layer,layerQuad);quads.push(layerQuad);if(this.showInvalidations)
-this.appendInvalidationQuads_(quads,layer,layerQuad);if(this.showUnrecordedRegion)
-this.appendUnrecordedRegionQuads_(quads,layer,layerQuad);if(this.showBottlenecks)
-this.appendBottleneckQuads_(quads,layer,layerQuad,layerQuad.stackingGroupId);if(this.showLayoutRects)
-this.appendLayoutRectQuads_(quads,layer,layerQuad);if(this.howToShowTiles==='coverage'){this.appendTileCoverageRectQuads_(quads,layer,layerQuad,this.tileHeatmapType);}else if(this.howToShowTiles!=='none'){this.appendTilesWithScaleQuads_(quads,layer,layerQuad,this.howToShowTiles,this.tileHeatmapType);}
+quad.borderColor=tr.e.cc.tileBorder[type].color;quad.borderWidth=tr.e.cc.tileBorder[type].width;var label;if(tile){label='coverageRect';}else{label='checkerboard coverageRect';}
+quad.selectionToSetIfClicked=new cc.LayerRectSelection(layer,label,rect,layer.tileCoverageRects[ct]);quads.push(quad);}},appendLayoutRectQuads_:function(quads,layer,layerQuad){if(!layer.layoutRects){return;}
+for(var ct=0;ct<layer.layoutRects.length;++ct){var rect=layer.layoutRects[ct].geometryRect;rect=rect.scale(1.0/layer.geometryContentsScale);var unitRect=rect.asUVRectInside(layer.bounds);var quad=layerQuad.projectUnitRect(unitRect);quad.backgroundColor='rgba(0, 0, 0, 0)';quad.stackingGroupId=layerQuad.stackingGroupId;quad.borderColor='rgba(0, 0, 200, 0.7)';quad.borderWidth=2;var label;label='Layout rect';quad.selectionToSetIfClicked=new cc.LayerRectSelection(layer,label,rect);quads.push(quad);}},getValueForHeatmap_:function(tile,heatmapType){if(heatmapType===TILE_HEATMAP_TYPE.SCHEDULED_PRIORITY){return tile.scheduledPriority===0?undefined:tile.scheduledPriority;}else if(heatmapType===TILE_HEATMAP_TYPE.USING_GPU_MEMORY){if(tile.isSolidColor)return 0.5;return tile.isUsingGpuMemory?0:1;}},getMinMaxForHeatmap_:function(tiles,heatmapType){var range=new tr.b.math.Range();if(heatmapType===TILE_HEATMAP_TYPE.USING_GPU_MEMORY){range.addValue(0);range.addValue(1);return range;}
+for(var i=0;i<tiles.length;++i){var value=this.getValueForHeatmap_(tiles[i],heatmapType);if(value===undefined)continue;range.addValue(value);}
+if(range.range===0){range.addValue(1);}
+return range;},computeHeatmapColors_:function(tiles,minMax,heatmapType){var min=minMax.min;var max=minMax.max;var color=function(value){var hue=120*(1-(value-min)/(max-min));if(hue<0)hue=0;return'hsla('+hue+', 100%, 50%, 0.5)';};var values=[];for(var i=0;i<tiles.length;++i){var tile=tiles[i];var value=this.getValueForHeatmap_(tile,heatmapType);var res={value:value,color:value!==undefined?color(value):undefined};values.push(res);}
+return values;},appendTilesWithScaleQuads_:function(quads,layer,layerQuad,scale,heatmapType){var lthi=this.layerTreeImpl_.layerTreeHostImpl;var tiles=[];for(var i=0;i<lthi.activeTiles.length;++i){var tile=lthi.activeTiles[i];if(Math.abs(tile.contentsScale-scale)>1e-6){continue;}
+if(layer.layerId!==tile.layerId)continue;tiles.push(tile);}
+var minMax=this.getMinMaxForHeatmap_(lthi.activeTiles,heatmapType);var heatmapResult=this.computeHeatmapColors_(tiles,minMax,heatmapType);for(var i=0;i<tiles.length;++i){var tile=tiles[i];var rect=tile.layerRect;if(!tile.layerRect)continue;var unitRect=rect.asUVRectInside(layer.bounds);var quad=layerQuad.projectUnitRect(unitRect);quad.backgroundColor='rgba(0, 0, 0, 0)';quad.stackingGroupId=layerQuad.stackingGroupId;var type=tile.getTypeForLayer(layer);quad.borderColor=tr.e.cc.tileBorder[type].color;quad.borderWidth=tr.e.cc.tileBorder[type].width;quad.backgroundColor=heatmapResult[i].color;var data={tileType:type};if(heatmapType!==TILE_HEATMAP_TYPE.NONE){data[heatmapType]=heatmapResult[i].value;}
+quad.selectionToSetIfClicked=new cc.TileSelection(tile,data);quads.push(quad);}},appendHighlightQuadsForLayer_:function(quads,layer,layerQuad,highlights){highlights.forEach(function(highlight){var rect=highlight.rect;var unitRect=rect.asUVRectInside(layer.bounds);var quad=layerQuad.projectUnitRect(unitRect);var colorId=ColorScheme.getColorIdForGeneralPurposeString(highlight.colorKey);colorId+=ColorScheme.properties.brightenedOffsets[0];var color=ColorScheme.colors[colorId];var quadForDrawing=quad.clone();quadForDrawing.backgroundColor=color.withAlpha(0.5).toString();quadForDrawing.borderColor=color.withAlpha(1.0).darken().toString();quadForDrawing.stackingGroupId=layerQuad.stackingGroupId;quads.push(quadForDrawing);},this);},generateRenderPassQuads:function(){if(!this.layerTreeImpl.layerTreeHostImpl.args.frame)return[];var renderPasses=this.renderPasses;if(!renderPasses)return[];var quads=[];for(var i=0;i<renderPasses.length;++i){var quadList=renderPasses[i].quadList;for(var j=0;j<quadList.length;++j){var drawQuad=quadList[j];var quad=drawQuad.rectAsTargetSpaceQuad.clone();quad.borderColor='rgb(170, 204, 238)';quad.borderWidth=2;quad.stackingGroupId=i;quads.push(quad);}}
+return quads;},generateLayerQuads:function(){this.updateContentsPending_=false;var layers=this.layers;var quads=[];var nextStackingGroupId=0;var alreadyVisitedLayerIds={};var selectionHighlightsByLayerId;if(this.selection){selectionHighlightsByLayerId=this.selection.highlightsByLayerId;}else{selectionHighlightsByLayerId={};}
+var extraHighlightsByLayerId=this.extraHighlightsByLayerId||{};for(var i=1;i<=layers.length;i++){var layer=layers[layers.length-i];alreadyVisitedLayerIds[layer.layerId]=true;if(layer.objectInstance.name==='cc::NinePatchLayerImpl'){continue;}
+var layerQuad=layer.layerQuad.clone();if(layer.usingGpuRasterization){var pixelRatio=window.devicePixelRatio||1;layerQuad.borderWidth=2.0*pixelRatio;layerQuad.borderColor='rgba(154,205,50,0.75)';}else{layerQuad.borderColor='rgba(0,0,0,0.75)';}
+layerQuad.stackingGroupId=nextStackingGroupId++;layerQuad.selectionToSetIfClicked=new cc.LayerSelection(layer);layerQuad.layer=layer;if(this.showOtherLayers&&this.selectedLayer===layer){layerQuad.upperBorderColor='rgb(156,189,45)';}
+if(this.showAnimationBounds){this.appendAnimationQuads_(quads,layer,layerQuad);}
+this.appendImageQuads_(quads,layer,layerQuad);quads.push(layerQuad);if(this.showInvalidations){this.appendInvalidationQuads_(quads,layer,layerQuad);}
+if(this.showUnrecordedRegion){this.appendUnrecordedRegionQuads_(quads,layer,layerQuad);}
+if(this.showBottlenecks){this.appendBottleneckQuads_(quads,layer,layerQuad,layerQuad.stackingGroupId);}
+if(this.showLayoutRects){this.appendLayoutRectQuads_(quads,layer,layerQuad);}
+if(this.howToShowTiles==='coverage'){this.appendTileCoverageRectQuads_(quads,layer,layerQuad,this.tileHeatmapType);}else if(this.howToShowTiles!=='none'){this.appendTilesWithScaleQuads_(quads,layer,layerQuad,this.howToShowTiles,this.tileHeatmapType);}
 var highlights;highlights=extraHighlightsByLayerId[layer.layerId];if(highlights){this.appendHighlightQuadsForLayer_(quads,layer,layerQuad,highlights);}
 highlights=selectionHighlightsByLayerId[layer.layerId];if(highlights){this.appendHighlightQuadsForLayer_(quads,layer,layerQuad,highlights);}}
-this.layerTreeImpl.iterLayers(function(layer,depth,isMask,isReplica){if(!this.showOtherLayers&&this.selectedLayer!=layer)
-return;if(alreadyVisitedLayerIds[layer.layerId])
-return;var layerQuad=layer.layerQuad;var stackingGroupId=nextStackingGroupId++;if(this.showBottlenecks)
-this.appendBottleneckQuads_(quads,layer,layerQuad,stackingGroupId);},this);var tracedInputLatencies=this.layerTreeImpl.tracedInputLatencies;if(this.showInputEvents&&tracedInputLatencies){for(var i=0;i<tracedInputLatencies.length;i++){var coordinatesArray=tracedInputLatencies[i].args.data.coordinates;for(var j=0;j<coordinatesArray.length;j++){var inputQuad=tr.b.Quad.fromXYWH(coordinatesArray[j].x-25,coordinatesArray[j].y-25,50,50);inputQuad.borderColor='rgba(0, 0, 0, 0)';inputQuad.imageData=this.inputEventImageData_;quads.push(inputQuad);}}}
-return quads;},updateInfoBar_:function(infoBarMessages){if(infoBarMessages.length){this.infoBar_.removeAllButtons();this.infoBar_.message='Some problems were encountered...';this.infoBar_.addButton('More info...',function(e){var overlay=new tr.ui.b.Overlay();Polymer.dom(overlay).textContent='';infoBarMessages.forEach(function(message){var title=document.createElement('h3');Polymer.dom(title).textContent=message.header;var details=document.createElement('div');Polymer.dom(details).textContent=message.details;Polymer.dom(overlay).appendChild(title);Polymer.dom(overlay).appendChild(details);});overlay.visible=true;e.stopPropagation();return false;});this.infoBar_.visible=true;}else{this.infoBar_.removeAllButtons();this.infoBar_.message='';this.infoBar_.visible=false;}},getWhatRasterized_:function(){var lthi=this.layerTreeImpl_.layerTreeHostImpl;var renderProcess=lthi.objectInstance.parent;var tasks=[];for(var event of renderProcess.getDescendantEvents()){if(!(event instanceof tr.model.Slice))
-continue;var tile=tr.e.cc.getTileFromRasterTaskSlice(event);if(tile===undefined)
-continue;if(tile.containingSnapshot==lthi)
-tasks.push(event);}
-return tasks;},updateWhatRasterizedLinkState_:function(){var tasks=this.getWhatRasterized_();if(tasks.length){Polymer.dom(this.whatRasterizedLink_).textContent=tasks.length+' raster tasks';this.whatRasterizedLink_.style.display='';}else{Polymer.dom(this.whatRasterizedLink_).textContent='';this.whatRasterizedLink_.style.display='none';}},onWhatRasterizedLinkClicked_:function(){var tasks=this.getWhatRasterized_();var event=new tr.model.RequestSelectionChangeEvent();event.selection=new tr.model.EventSet(tasks);this.dispatchEvent(event);}};return{LayerTreeQuadStackView:LayerTreeQuadStackView};});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){var constants=tr.e.cc.constants;var LayerView=tr.ui.b.define('tr-ui-e-chrome-cc-layer-view');LayerView.prototype={__proto__:HTMLDivElement.prototype,decorate:function(){this.layerTreeQuadStackView_=new tr.ui.e.chrome.cc.LayerTreeQuadStackView();this.dragBar_=document.createElement('tr-ui-b-drag-handle');this.analysisEl_=document.createElement('tr-ui-e-chrome-cc-layer-view-analysis');this.analysisEl_.addEventListener('requestSelectionChange',this.onRequestSelectionChangeFromAnalysisEl_.bind(this));this.dragBar_.target=this.analysisEl_;Polymer.dom(this).appendChild(this.layerTreeQuadStackView_);Polymer.dom(this).appendChild(this.dragBar_);Polymer.dom(this).appendChild(this.analysisEl_);this.layerTreeQuadStackView_.addEventListener('selection-change',function(){this.layerTreeQuadStackViewSelectionChanged_();}.bind(this));this.layerTreeQuadStackViewSelectionChanged_();},get layerTreeImpl(){return this.layerTreeQuadStackView_.layerTreeImpl;},set layerTreeImpl(newValue){return this.layerTreeQuadStackView_.layerTreeImpl=newValue;},set isRenderPassQuads(newValue){return this.layerTreeQuadStackView_.isRenderPassQuads=newValue;},get selection(){return this.layerTreeQuadStackView_.selection;},set selection(newValue){this.layerTreeQuadStackView_.selection=newValue;},regenerateContent:function(){this.layerTreeQuadStackView_.regenerateContent();},layerTreeQuadStackViewSelectionChanged_:function(){var selection=this.layerTreeQuadStackView_.selection;if(selection){this.dragBar_.style.display='';this.analysisEl_.style.display='';Polymer.dom(this.analysisEl_).textContent='';var layer=selection.layer;if(layer&&layer.args&&layer.args.pictures){Polymer.dom(this.analysisEl_).appendChild(this.createPictureBtn_(layer.args.pictures));}
-var analysis=selection.createAnalysis();Polymer.dom(this.analysisEl_).appendChild(analysis);}else{this.dragBar_.style.display='none';this.analysisEl_.style.display='none';var analysis=Polymer.dom(this.analysisEl_).firstChild;if(analysis)
-Polymer.dom(this.analysisEl_).removeChild(analysis);this.layerTreeQuadStackView_.style.height=window.getComputedStyle(this).height;}
-tr.b.dispatchSimpleEvent(this,'selection-change');},createPictureBtn_:function(pictures){if(!(pictures instanceof Array))
-pictures=[pictures];var link=document.createElement('tr-ui-a-analysis-link');link.selection=function(){var layeredPicture=new tr.e.cc.LayeredPicture(pictures);var snapshot=new tr.e.cc.PictureSnapshot(layeredPicture);snapshot.picture=layeredPicture;var selection=new tr.model.EventSet();selection.push(snapshot);return selection;};Polymer.dom(link).textContent='View in Picture Debugger';return link;},onRequestSelectionChangeFromAnalysisEl_:function(e){if(!(e.selection instanceof tr.ui.e.chrome.cc.Selection))
-return;e.stopPropagation();this.selection=e.selection;},get extraHighlightsByLayerId(){return this.layerTreeQuadStackView_.extraHighlightsByLayerId;},set extraHighlightsByLayerId(extraHighlightsByLayerId){this.layerTreeQuadStackView_.extraHighlightsByLayerId=extraHighlightsByLayerId;}};return{LayerView:LayerView};});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){var LayerTreeHostImplSnapshotView=tr.ui.b.define('tr-ui-e-chrome-cc-layer-tree-host-impl-snapshot-view',tr.ui.analysis.ObjectSnapshotView);LayerTreeHostImplSnapshotView.prototype={__proto__:tr.ui.analysis.ObjectSnapshotView.prototype,decorate:function(){Polymer.dom(this).classList.add('tr-ui-e-chrome-cc-lthi-s-view');this.selection_=undefined;this.layerPicker_=new tr.ui.e.chrome.cc.LayerPicker();this.layerPicker_.addEventListener('selection-change',this.onLayerPickerSelectionChanged_.bind(this));this.layerView_=new tr.ui.e.chrome.cc.LayerView();this.layerView_.addEventListener('selection-change',this.onLayerViewSelectionChanged_.bind(this));this.dragHandle_=document.createElement('tr-ui-b-drag-handle');this.dragHandle_.horizontal=false;this.dragHandle_.target=this.layerView_;Polymer.dom(this).appendChild(this.layerPicker_);Polymer.dom(this).appendChild(this.dragHandle_);Polymer.dom(this).appendChild(this.layerView_);this.onLayerViewSelectionChanged_();this.onLayerPickerSelectionChanged_();},get objectSnapshot(){return this.objectSnapshot_;},set objectSnapshot(objectSnapshot){this.objectSnapshot_=objectSnapshot;var lthi=this.objectSnapshot;var layerTreeImpl;if(lthi)
-layerTreeImpl=lthi.getTree(this.layerPicker_.whichTree);this.layerPicker_.lthiSnapshot=lthi;this.layerView_.layerTreeImpl=layerTreeImpl;this.layerView_.regenerateContent();if(!this.selection_)
-return;this.selection=this.selection_.findEquivalent(lthi);},get selection(){return this.selection_;},set selection(selection){if(this.selection_==selection)
-return;this.selection_=selection;this.layerPicker_.selection=selection;this.layerView_.selection=selection;tr.b.dispatchSimpleEvent(this,'cc-selection-change');},onLayerPickerSelectionChanged_:function(){this.selection_=this.layerPicker_.selection;this.layerView_.selection=this.selection;this.layerView_.layerTreeImpl=this.layerPicker_.layerTreeImpl;this.layerView_.isRenderPassQuads=this.layerPicker_.isRenderPassQuads;this.layerView_.regenerateContent();tr.b.dispatchSimpleEvent(this,'cc-selection-change');},onLayerViewSelectionChanged_:function(){this.selection_=this.layerView_.selection;this.layerPicker_.selection=this.selection;tr.b.dispatchSimpleEvent(this,'cc-selection-change');},get extraHighlightsByLayerId(){return this.layerView_.extraHighlightsByLayerId;},set extraHighlightsByLayerId(extraHighlightsByLayerId){this.layerView_.extraHighlightsByLayerId=extraHighlightsByLayerId;}};tr.ui.analysis.ObjectSnapshotView.register(LayerTreeHostImplSnapshotView,{typeName:'cc::LayerTreeHostImpl'});return{LayerTreeHostImplSnapshotView:LayerTreeHostImplSnapshotView};});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){var OPS_TIMING_ITERATIONS=3;var CHART_PADDING_LEFT=65;var CHART_PADDING_RIGHT=40;var AXIS_PADDING_LEFT=60;var AXIS_PADDING_RIGHT=35;var AXIS_PADDING_TOP=25;var AXIS_PADDING_BOTTOM=45;var AXIS_LABEL_PADDING=5;var AXIS_TICK_SIZE=10;var LABEL_PADDING=5;var LABEL_INTERLEAVE_OFFSET=15;var BAR_PADDING=5;var VERTICAL_TICKS=5;var HUE_CHAR_CODE_ADJUSTMENT=5.7;var PictureOpsChartSummaryView=tr.ui.b.define('tr-ui-e-chrome-cc-picture-ops-chart-summary-view');PictureOpsChartSummaryView.prototype={__proto__:HTMLDivElement.prototype,decorate:function(){this.picture_=undefined;this.pictureDataProcessed_=false;this.chartScale_=window.devicePixelRatio;this.chart_=document.createElement('canvas');this.chartCtx_=this.chart_.getContext('2d');Polymer.dom(this).appendChild(this.chart_);this.opsTimingData_=[];this.chartWidth_=0;this.chartHeight_=0;this.requiresRedraw_=true;this.currentBarMouseOverTarget_=null;this.chart_.addEventListener('mousemove',this.onMouseMove_.bind(this));},get requiresRedraw(){return this.requiresRedraw_;},set requiresRedraw(requiresRedraw){this.requiresRedraw_=requiresRedraw;},get picture(){return this.picture_;},set picture(picture){this.picture_=picture;this.pictureDataProcessed_=false;if(Polymer.dom(this).classList.contains('hidden'))
-return;this.processPictureData_();this.requiresRedraw=true;this.updateChartContents();},hide:function(){Polymer.dom(this).classList.add('hidden');},show:function(){Polymer.dom(this).classList.remove('hidden');if(this.pictureDataProcessed_)
-return;this.processPictureData_();this.requiresRedraw=true;this.updateChartContents();},onMouseMove_:function(e){var lastBarMouseOverTarget=this.currentBarMouseOverTarget_;this.currentBarMouseOverTarget_=null;var x=e.offsetX;var y=e.offsetY;var chartLeft=CHART_PADDING_LEFT;var chartRight=this.chartWidth_-CHART_PADDING_RIGHT;var chartTop=AXIS_PADDING_TOP;var chartBottom=this.chartHeight_-AXIS_PADDING_BOTTOM;var chartInnerWidth=chartRight-chartLeft;if(x>chartLeft&&x<chartRight&&y>chartTop&&y<chartBottom){this.currentBarMouseOverTarget_=Math.floor((x-chartLeft)/chartInnerWidth*this.opsTimingData_.length);this.currentBarMouseOverTarget_=tr.b.clamp(this.currentBarMouseOverTarget_,0,this.opsTimingData_.length-1);}
-if(this.currentBarMouseOverTarget_===lastBarMouseOverTarget)
-return;this.drawChartContents_();},updateChartContents:function(){if(this.requiresRedraw)
-this.updateChartDimensions_();this.drawChartContents_();},updateChartDimensions_:function(){this.chartWidth_=this.offsetWidth;this.chartHeight_=this.offsetHeight;this.chart_.width=this.chartWidth_*this.chartScale_;this.chart_.height=this.chartHeight_*this.chartScale_;this.chart_.style.width=this.chartWidth_+'px';this.chart_.style.height=this.chartHeight_+'px';this.chartCtx_.scale(this.chartScale_,this.chartScale_);},processPictureData_:function(){this.resetOpsTimingData_();this.pictureDataProcessed_=true;if(!this.picture_)
-return;var ops=this.picture_.getOps();if(!ops)
-return;ops=this.picture_.tagOpsWithTimings(ops);if(ops[0].cmd_time===undefined)
-return;this.collapseOpsToTimingBuckets_(ops);},drawChartContents_:function(){this.clearChartContents_();if(this.opsTimingData_.length===0){this.showNoTimingDataMessage_();return;}
-this.drawChartAxes_();this.drawBars_();this.drawLineAtBottomOfChart_();if(this.currentBarMouseOverTarget_===null)
-return;this.drawTooltip_();},drawLineAtBottomOfChart_:function(){this.chartCtx_.strokeStyle='#AAA';this.chartCtx_.moveTo(0,this.chartHeight_-0.5);this.chartCtx_.lineTo(this.chartWidth_,this.chartHeight_-0.5);this.chartCtx_.stroke();},drawTooltip_:function(){var tooltipData=this.opsTimingData_[this.currentBarMouseOverTarget_];var tooltipTitle=tooltipData.cmd_string;var tooltipTime=tooltipData.cmd_time.toFixed(4);var tooltipWidth=110;var tooltipHeight=40;var chartInnerWidth=this.chartWidth_-CHART_PADDING_RIGHT-
+this.layerTreeImpl.iterLayers(function(layer,depth,isMask,isReplica){if(!this.showOtherLayers&&this.selectedLayer!==layer)return;if(alreadyVisitedLayerIds[layer.layerId])return;var layerQuad=layer.layerQuad;var stackingGroupId=nextStackingGroupId++;if(this.showBottlenecks){this.appendBottleneckQuads_(quads,layer,layerQuad,stackingGroupId);}},this);var tracedInputLatencies=this.layerTreeImpl.tracedInputLatencies;if(this.showInputEvents&&tracedInputLatencies){for(var i=0;i<tracedInputLatencies.length;i++){var coordinatesArray=tracedInputLatencies[i].args.data.coordinates;for(var j=0;j<coordinatesArray.length;j++){var inputQuad=tr.b.math.Quad.fromXYWH(coordinatesArray[j].x-25,coordinatesArray[j].y-25,50,50);inputQuad.borderColor='rgba(0, 0, 0, 0)';inputQuad.imageData=this.inputEventImageData_;quads.push(inputQuad);}}}
+return quads;},updateInfoBar_:function(infoBarMessages){if(infoBarMessages.length){this.infoBar_.removeAllButtons();this.infoBar_.message='Some problems were encountered...';this.infoBar_.addButton('More info...',function(e){var overlay=new tr.ui.b.Overlay();Polymer.dom(overlay).textContent='';infoBarMessages.forEach(function(message){var title=document.createElement('h3');Polymer.dom(title).textContent=message.header;var details=document.createElement('div');Polymer.dom(details).textContent=message.details;Polymer.dom(overlay).appendChild(title);Polymer.dom(overlay).appendChild(details);});overlay.visible=true;e.stopPropagation();return false;});this.infoBar_.visible=true;}else{this.infoBar_.removeAllButtons();this.infoBar_.message='';this.infoBar_.visible=false;}},getWhatRasterized_:function(){var lthi=this.layerTreeImpl_.layerTreeHostImpl;var renderProcess=lthi.objectInstance.parent;var tasks=[];for(var event of renderProcess.getDescendantEvents()){if(!(event instanceof tr.model.Slice))continue;var tile=tr.e.cc.getTileFromRasterTaskSlice(event);if(tile===undefined)continue;if(tile.containingSnapshot===lthi){tasks.push(event);}}
+return tasks;},updateWhatRasterizedLinkState_:function(){var tasks=this.getWhatRasterized_();if(tasks.length){Polymer.dom(this.whatRasterizedLink_).textContent=tasks.length+' raster tasks';this.whatRasterizedLink_.style.display='';}else{Polymer.dom(this.whatRasterizedLink_).textContent='';this.whatRasterizedLink_.style.display='none';}},onWhatRasterizedLinkClicked_:function(){var tasks=this.getWhatRasterized_();var event=new tr.model.RequestSelectionChangeEvent();event.selection=new tr.model.EventSet(tasks);this.dispatchEvent(event);}};return{LayerTreeQuadStackView,};});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){var constants=tr.e.cc.constants;var LayerView=tr.ui.b.define('tr-ui-e-chrome-cc-layer-view');LayerView.prototype={__proto__:HTMLDivElement.prototype,decorate:function(){this.layerTreeQuadStackView_=new tr.ui.e.chrome.cc.LayerTreeQuadStackView();this.dragBar_=document.createElement('tr-ui-b-drag-handle');this.analysisEl_=document.createElement('tr-ui-e-chrome-cc-layer-view-analysis');this.analysisEl_.addEventListener('requestSelectionChange',this.onRequestSelectionChangeFromAnalysisEl_.bind(this));this.dragBar_.target=this.analysisEl_;Polymer.dom(this).appendChild(this.layerTreeQuadStackView_);Polymer.dom(this).appendChild(this.dragBar_);Polymer.dom(this).appendChild(this.analysisEl_);this.layerTreeQuadStackView_.addEventListener('selection-change',function(){this.layerTreeQuadStackViewSelectionChanged_();}.bind(this));this.layerTreeQuadStackViewSelectionChanged_();},get layerTreeImpl(){return this.layerTreeQuadStackView_.layerTreeImpl;},set layerTreeImpl(newValue){return this.layerTreeQuadStackView_.layerTreeImpl=newValue;},set isRenderPassQuads(newValue){return this.layerTreeQuadStackView_.isRenderPassQuads=newValue;},get selection(){return this.layerTreeQuadStackView_.selection;},set selection(newValue){this.layerTreeQuadStackView_.selection=newValue;},regenerateContent:function(){this.layerTreeQuadStackView_.regenerateContent();},layerTreeQuadStackViewSelectionChanged_:function(){var selection=this.layerTreeQuadStackView_.selection;if(selection){this.dragBar_.style.display='';this.analysisEl_.style.display='';Polymer.dom(this.analysisEl_).textContent='';var layer=selection.layer;if(layer&&layer.args&&layer.args.pictures){Polymer.dom(this.analysisEl_).appendChild(this.createPictureBtn_(layer.args.pictures));}
+var analysis=selection.createAnalysis();Polymer.dom(this.analysisEl_).appendChild(analysis);}else{this.dragBar_.style.display='none';this.analysisEl_.style.display='none';var analysis=Polymer.dom(this.analysisEl_).firstChild;if(analysis){Polymer.dom(this.analysisEl_).removeChild(analysis);}
+this.layerTreeQuadStackView_.style.height=window.getComputedStyle(this).height;}
+tr.b.dispatchSimpleEvent(this,'selection-change');},createPictureBtn_:function(pictures){if(!(pictures instanceof Array)){pictures=[pictures];}
+var link=document.createElement('tr-ui-a-analysis-link');link.selection=function(){var layeredPicture=new tr.e.cc.LayeredPicture(pictures);var snapshot=new tr.e.cc.PictureSnapshot(layeredPicture);snapshot.picture=layeredPicture;var selection=new tr.model.EventSet();selection.push(snapshot);return selection;};Polymer.dom(link).textContent='View in Picture Debugger';return link;},onRequestSelectionChangeFromAnalysisEl_:function(e){if(!(e.selection instanceof tr.ui.e.chrome.cc.Selection)){return;}
+e.stopPropagation();this.selection=e.selection;},get extraHighlightsByLayerId(){return this.layerTreeQuadStackView_.extraHighlightsByLayerId;},set extraHighlightsByLayerId(extraHighlightsByLayerId){this.layerTreeQuadStackView_.extraHighlightsByLayerId=extraHighlightsByLayerId;}};return{LayerView,};});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){var LayerTreeHostImplSnapshotView=tr.ui.b.define('tr-ui-e-chrome-cc-layer-tree-host-impl-snapshot-view',tr.ui.analysis.ObjectSnapshotView);LayerTreeHostImplSnapshotView.prototype={__proto__:tr.ui.analysis.ObjectSnapshotView.prototype,decorate:function(){Polymer.dom(this).classList.add('tr-ui-e-chrome-cc-lthi-s-view');this.selection_=undefined;this.layerPicker_=new tr.ui.e.chrome.cc.LayerPicker();this.layerPicker_.addEventListener('selection-change',this.onLayerPickerSelectionChanged_.bind(this));this.layerView_=new tr.ui.e.chrome.cc.LayerView();this.layerView_.addEventListener('selection-change',this.onLayerViewSelectionChanged_.bind(this));this.dragHandle_=document.createElement('tr-ui-b-drag-handle');this.dragHandle_.horizontal=false;this.dragHandle_.target=this.layerView_;Polymer.dom(this).appendChild(this.layerPicker_);Polymer.dom(this).appendChild(this.dragHandle_);Polymer.dom(this).appendChild(this.layerView_);this.onLayerViewSelectionChanged_();this.onLayerPickerSelectionChanged_();},get objectSnapshot(){return this.objectSnapshot_;},set objectSnapshot(objectSnapshot){this.objectSnapshot_=objectSnapshot;var lthi=this.objectSnapshot;var layerTreeImpl;if(lthi){layerTreeImpl=lthi.getTree(this.layerPicker_.whichTree);}
+this.layerPicker_.lthiSnapshot=lthi;this.layerView_.layerTreeImpl=layerTreeImpl;this.layerView_.regenerateContent();if(!this.selection_)return;this.selection=this.selection_.findEquivalent(lthi);},get selection(){return this.selection_;},set selection(selection){if(this.selection_===selection)return;this.selection_=selection;this.layerPicker_.selection=selection;this.layerView_.selection=selection;tr.b.dispatchSimpleEvent(this,'cc-selection-change');},onLayerPickerSelectionChanged_:function(){this.selection_=this.layerPicker_.selection;this.layerView_.selection=this.selection;this.layerView_.layerTreeImpl=this.layerPicker_.layerTreeImpl;this.layerView_.isRenderPassQuads=this.layerPicker_.isRenderPassQuads;this.layerView_.regenerateContent();tr.b.dispatchSimpleEvent(this,'cc-selection-change');},onLayerViewSelectionChanged_:function(){this.selection_=this.layerView_.selection;this.layerPicker_.selection=this.selection;tr.b.dispatchSimpleEvent(this,'cc-selection-change');},get extraHighlightsByLayerId(){return this.layerView_.extraHighlightsByLayerId;},set extraHighlightsByLayerId(extraHighlightsByLayerId){this.layerView_.extraHighlightsByLayerId=extraHighlightsByLayerId;}};tr.ui.analysis.ObjectSnapshotView.register(LayerTreeHostImplSnapshotView,{typeName:'cc::LayerTreeHostImpl'});return{LayerTreeHostImplSnapshotView,};});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){var OPS_TIMING_ITERATIONS=3;var CHART_PADDING_LEFT=65;var CHART_PADDING_RIGHT=40;var AXIS_PADDING_LEFT=60;var AXIS_PADDING_RIGHT=35;var AXIS_PADDING_TOP=25;var AXIS_PADDING_BOTTOM=45;var AXIS_LABEL_PADDING=5;var AXIS_TICK_SIZE=10;var LABEL_PADDING=5;var LABEL_INTERLEAVE_OFFSET=15;var BAR_PADDING=5;var VERTICAL_TICKS=5;var HUE_CHAR_CODE_ADJUSTMENT=5.7;var PictureOpsChartSummaryView=tr.ui.b.define('tr-ui-e-chrome-cc-picture-ops-chart-summary-view');PictureOpsChartSummaryView.prototype={__proto__:HTMLDivElement.prototype,decorate:function(){this.picture_=undefined;this.pictureDataProcessed_=false;this.chartScale_=window.devicePixelRatio;this.chart_=document.createElement('canvas');this.chartCtx_=this.chart_.getContext('2d');Polymer.dom(this).appendChild(this.chart_);this.opsTimingData_=[];this.chartWidth_=0;this.chartHeight_=0;this.requiresRedraw_=true;this.currentBarMouseOverTarget_=null;this.chart_.addEventListener('mousemove',this.onMouseMove_.bind(this));},get requiresRedraw(){return this.requiresRedraw_;},set requiresRedraw(requiresRedraw){this.requiresRedraw_=requiresRedraw;},get picture(){return this.picture_;},set picture(picture){this.picture_=picture;this.pictureDataProcessed_=false;if(Polymer.dom(this).classList.contains('hidden'))return;this.processPictureData_();this.requiresRedraw=true;this.updateChartContents();},hide:function(){Polymer.dom(this).classList.add('hidden');},show:function(){Polymer.dom(this).classList.remove('hidden');if(this.pictureDataProcessed_)return;this.processPictureData_();this.requiresRedraw=true;this.updateChartContents();},onMouseMove_:function(e){var lastBarMouseOverTarget=this.currentBarMouseOverTarget_;this.currentBarMouseOverTarget_=null;var x=e.offsetX;var y=e.offsetY;var chartLeft=CHART_PADDING_LEFT;var chartRight=this.chartWidth_-CHART_PADDING_RIGHT;var chartTop=AXIS_PADDING_TOP;var chartBottom=this.chartHeight_-AXIS_PADDING_BOTTOM;var chartInnerWidth=chartRight-chartLeft;if(x>chartLeft&&x<chartRight&&y>chartTop&&y<chartBottom){this.currentBarMouseOverTarget_=Math.floor((x-chartLeft)/chartInnerWidth*this.opsTimingData_.length);this.currentBarMouseOverTarget_=tr.b.math.clamp(this.currentBarMouseOverTarget_,0,this.opsTimingData_.length-1);}
+if(this.currentBarMouseOverTarget_===lastBarMouseOverTarget)return;this.drawChartContents_();},updateChartContents:function(){if(this.requiresRedraw){this.updateChartDimensions_();}
+this.drawChartContents_();},updateChartDimensions_:function(){this.chartWidth_=this.offsetWidth;this.chartHeight_=this.offsetHeight;this.chart_.width=this.chartWidth_*this.chartScale_;this.chart_.height=this.chartHeight_*this.chartScale_;this.chart_.style.width=this.chartWidth_+'px';this.chart_.style.height=this.chartHeight_+'px';this.chartCtx_.scale(this.chartScale_,this.chartScale_);},processPictureData_:function(){this.resetOpsTimingData_();this.pictureDataProcessed_=true;if(!this.picture_)return;var ops=this.picture_.getOps();if(!ops)return;ops=this.picture_.tagOpsWithTimings(ops);if(ops[0].cmd_time===undefined)return;this.collapseOpsToTimingBuckets_(ops);},drawChartContents_:function(){this.clearChartContents_();if(this.opsTimingData_.length===0){this.showNoTimingDataMessage_();return;}
+this.drawChartAxes_();this.drawBars_();this.drawLineAtBottomOfChart_();if(this.currentBarMouseOverTarget_===null)return;this.drawTooltip_();},drawLineAtBottomOfChart_:function(){this.chartCtx_.strokeStyle='#AAA';this.chartCtx_.moveTo(0,this.chartHeight_-0.5);this.chartCtx_.lineTo(this.chartWidth_,this.chartHeight_-0.5);this.chartCtx_.stroke();},drawTooltip_:function(){var tooltipData=this.opsTimingData_[this.currentBarMouseOverTarget_];var tooltipTitle=tooltipData.cmd_string;var tooltipTime=tooltipData.cmd_time.toFixed(4);var tooltipWidth=110;var tooltipHeight=40;var chartInnerWidth=this.chartWidth_-CHART_PADDING_RIGHT-
 CHART_PADDING_LEFT;var barWidth=chartInnerWidth/this.opsTimingData_.length;var tooltipOffset=Math.round((tooltipWidth-barWidth)*0.5);var left=CHART_PADDING_LEFT+this.currentBarMouseOverTarget_*barWidth-tooltipOffset;var top=Math.round((this.chartHeight_-tooltipHeight)*0.5);this.chartCtx_.save();this.chartCtx_.shadowOffsetX=0;this.chartCtx_.shadowOffsetY=5;this.chartCtx_.shadowBlur=4;this.chartCtx_.shadowColor='rgba(0,0,0,0.4)';this.chartCtx_.strokeStyle='#888';this.chartCtx_.fillStyle='#EEE';this.chartCtx_.fillRect(left,top,tooltipWidth,tooltipHeight);this.chartCtx_.shadowColor='transparent';this.chartCtx_.translate(0.5,0.5);this.chartCtx_.strokeRect(left,top,tooltipWidth,tooltipHeight);this.chartCtx_.restore();this.chartCtx_.fillStyle='#222';this.chartCtx_.textBaseline='top';this.chartCtx_.font='800 12px Arial';this.chartCtx_.fillText(tooltipTitle,left+8,top+8);this.chartCtx_.fillStyle='#555';this.chartCtx_.textBaseline='top';this.chartCtx_.font='400 italic 10px Arial';this.chartCtx_.fillText('Total: '+tooltipTime+'ms',left+8,top+22);},drawBars_:function(){var len=this.opsTimingData_.length;var max=this.opsTimingData_[0].cmd_time;var min=this.opsTimingData_[len-1].cmd_time;var width=this.chartWidth_-CHART_PADDING_LEFT-CHART_PADDING_RIGHT;var height=this.chartHeight_-AXIS_PADDING_TOP-AXIS_PADDING_BOTTOM;var barWidth=Math.floor(width/len);var opData;var opTiming;var opHeight;var opLabel;var barLeft;for(var b=0;b<len;b++){opData=this.opsTimingData_[b];opTiming=opData.cmd_time/max;opHeight=Math.round(Math.max(1,opTiming*height));opLabel=opData.cmd_string;barLeft=CHART_PADDING_LEFT+b*barWidth;this.chartCtx_.fillStyle=this.getOpColor_(opLabel);this.chartCtx_.fillRect(barLeft+BAR_PADDING,AXIS_PADDING_TOP+
 height-opHeight,barWidth-2*BAR_PADDING,opHeight);}},getOpColor_:function(opName){var characters=opName.split('');var hue=characters.reduce(this.reduceNameToHue,0)%360;return'hsl('+hue+', 30%, 50%)';},reduceNameToHue:function(previousValue,currentValue,index,array){return Math.round(previousValue+currentValue.charCodeAt(0)*HUE_CHAR_CODE_ADJUSTMENT);},drawChartAxes_:function(){var len=this.opsTimingData_.length;var max=this.opsTimingData_[0].cmd_time;var min=this.opsTimingData_[len-1].cmd_time;var width=this.chartWidth_-AXIS_PADDING_LEFT-AXIS_PADDING_RIGHT;var height=this.chartHeight_-AXIS_PADDING_TOP-AXIS_PADDING_BOTTOM;var totalBarWidth=this.chartWidth_-CHART_PADDING_LEFT-
 CHART_PADDING_RIGHT;var barWidth=Math.floor(totalBarWidth/len);var tickYInterval=height/(VERTICAL_TICKS-1);var tickYPosition=0;var tickValInterval=(max-min)/(VERTICAL_TICKS-1);var tickVal=0;this.chartCtx_.fillStyle='#333';this.chartCtx_.strokeStyle='#777';this.chartCtx_.save();this.chartCtx_.translate(0.5,0.5);this.chartCtx_.save();this.chartCtx_.translate(AXIS_PADDING_LEFT,AXIS_PADDING_TOP);this.chartCtx_.moveTo(0,0);this.chartCtx_.lineTo(0,height);this.chartCtx_.lineTo(width,height);this.chartCtx_.font='10px Arial';this.chartCtx_.textAlign='right';this.chartCtx_.textBaseline='middle';for(var t=0;t<VERTICAL_TICKS;t++){tickYPosition=Math.round(t*tickYInterval);tickVal=(max-t*tickValInterval).toFixed(4);this.chartCtx_.moveTo(0,tickYPosition);this.chartCtx_.lineTo(-AXIS_TICK_SIZE,tickYPosition);this.chartCtx_.fillText(tickVal,-AXIS_TICK_SIZE-AXIS_LABEL_PADDING,tickYPosition);}
 this.chartCtx_.stroke();this.chartCtx_.restore();this.chartCtx_.save();this.chartCtx_.translate(CHART_PADDING_LEFT+Math.round(barWidth*0.5),AXIS_PADDING_TOP+height+LABEL_PADDING);this.chartCtx_.font='10px Arial';this.chartCtx_.textAlign='center';this.chartCtx_.textBaseline='top';var labelTickLeft;var labelTickBottom;for(var l=0;l<len;l++){labelTickLeft=Math.round(l*barWidth);labelTickBottom=l%2*LABEL_INTERLEAVE_OFFSET;this.chartCtx_.save();this.chartCtx_.moveTo(labelTickLeft,-LABEL_PADDING);this.chartCtx_.lineTo(labelTickLeft,labelTickBottom);this.chartCtx_.stroke();this.chartCtx_.restore();this.chartCtx_.fillText(this.opsTimingData_[l].cmd_string,labelTickLeft,labelTickBottom);}
-this.chartCtx_.restore();this.chartCtx_.restore();},clearChartContents_:function(){this.chartCtx_.clearRect(0,0,this.chartWidth_,this.chartHeight_);},showNoTimingDataMessage_:function(){this.chartCtx_.font='800 italic 14px Arial';this.chartCtx_.fillStyle='#333';this.chartCtx_.textAlign='center';this.chartCtx_.textBaseline='middle';this.chartCtx_.fillText('No timing data available.',this.chartWidth_*0.5,this.chartHeight_*0.5);},collapseOpsToTimingBuckets_:function(ops){var opsTimingDataIndexHash_={};var timingData=this.opsTimingData_;var op;var opIndex;for(var i=0;i<ops.length;i++){op=ops[i];if(op.cmd_time===undefined)
-continue;opIndex=opsTimingDataIndexHash_[op.cmd_string]||null;if(opIndex===null){timingData.push({cmd_time:0,cmd_string:op.cmd_string});opIndex=timingData.length-1;opsTimingDataIndexHash_[op.cmd_string]=opIndex;}
+this.chartCtx_.restore();this.chartCtx_.restore();},clearChartContents_:function(){this.chartCtx_.clearRect(0,0,this.chartWidth_,this.chartHeight_);},showNoTimingDataMessage_:function(){this.chartCtx_.font='800 italic 14px Arial';this.chartCtx_.fillStyle='#333';this.chartCtx_.textAlign='center';this.chartCtx_.textBaseline='middle';this.chartCtx_.fillText('No timing data available.',this.chartWidth_*0.5,this.chartHeight_*0.5);},collapseOpsToTimingBuckets_:function(ops){var opsTimingDataIndexHash_={};var timingData=this.opsTimingData_;var op;var opIndex;for(var i=0;i<ops.length;i++){op=ops[i];if(op.cmd_time===undefined)continue;opIndex=opsTimingDataIndexHash_[op.cmd_string]||null;if(opIndex===null){timingData.push({cmd_time:0,cmd_string:op.cmd_string});opIndex=timingData.length-1;opsTimingDataIndexHash_[op.cmd_string]=opIndex;}
 timingData[opIndex].cmd_time+=op.cmd_time;}
-timingData.sort(this.sortTimingBucketsByOpTimeDescending_);this.collapseTimingBucketsToOther_(4);},collapseTimingBucketsToOther_:function(count){var timingData=this.opsTimingData_;var otherSource=timingData.splice(count,timingData.length-count);var otherDestination=null;if(!otherSource.length)
-return;timingData.push({cmd_time:0,cmd_string:'Other'});otherDestination=timingData[timingData.length-1];for(var i=0;i<otherSource.length;i++){otherDestination.cmd_time+=otherSource[i].cmd_time;}},sortTimingBucketsByOpTimeDescending_:function(a,b){return b.cmd_time-a.cmd_time;},resetOpsTimingData_:function(){this.opsTimingData_.length=0;}};return{PictureOpsChartSummaryView:PictureOpsChartSummaryView};});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){var BAR_PADDING=1;var BAR_WIDTH=5;var CHART_PADDING_LEFT=65;var CHART_PADDING_RIGHT=30;var CHART_PADDING_BOTTOM=35;var CHART_PADDING_TOP=20;var AXIS_PADDING_LEFT=55;var AXIS_PADDING_RIGHT=30;var AXIS_PADDING_BOTTOM=35;var AXIS_PADDING_TOP=20;var AXIS_TICK_SIZE=5;var AXIS_LABEL_PADDING=5;var VERTICAL_TICKS=5;var HUE_CHAR_CODE_ADJUSTMENT=5.7;var PictureOpsChartView=tr.ui.b.define('tr-ui-e-chrome-cc-picture-ops-chart-view');PictureOpsChartView.prototype={__proto__:HTMLDivElement.prototype,decorate:function(){this.picture_=undefined;this.pictureOps_=undefined;this.opCosts_=undefined;this.chartScale_=window.devicePixelRatio;this.chart_=document.createElement('canvas');this.chartCtx_=this.chart_.getContext('2d');Polymer.dom(this).appendChild(this.chart_);this.selectedOpIndex_=undefined;this.chartWidth_=0;this.chartHeight_=0;this.dimensionsHaveChanged_=true;this.currentBarMouseOverTarget_=undefined;this.ninetyFifthPercentileCost_=0;this.totalOpCost_=0;this.chart_.addEventListener('click',this.onClick_.bind(this));this.chart_.addEventListener('mousemove',this.onMouseMove_.bind(this));this.usePercentileScale_=false;this.usePercentileScaleCheckbox_=tr.ui.b.createCheckBox(this,'usePercentileScale','PictureOpsChartView.usePercentileScale',false,'Limit to 95%-ile');Polymer.dom(this.usePercentileScaleCheckbox_).classList.add('use-percentile-scale');Polymer.dom(this).appendChild(this.usePercentileScaleCheckbox_);},get dimensionsHaveChanged(){return this.dimensionsHaveChanged_;},set dimensionsHaveChanged(dimensionsHaveChanged){this.dimensionsHaveChanged_=dimensionsHaveChanged;},get usePercentileScale(){return this.usePercentileScale_;},set usePercentileScale(usePercentileScale){this.usePercentileScale_=usePercentileScale;this.drawChartContents_();},get numOps(){return this.opCosts_.length;},get selectedOpIndex(){return this.selectedOpIndex_;},set selectedOpIndex(selectedOpIndex){if(selectedOpIndex<0)throw new Error('Invalid index');if(selectedOpIndex>=this.numOps)throw new Error('Invalid index');this.selectedOpIndex_=selectedOpIndex;},get picture(){return this.picture_;},set picture(picture){this.picture_=picture;this.pictureOps_=picture.tagOpsWithTimings(picture.getOps());this.currentBarMouseOverTarget_=undefined;this.processPictureData_();this.dimensionsHaveChanged=true;},processPictureData_:function(){if(this.pictureOps_===undefined)
-return;var totalOpCost=0;this.opCosts_=this.pictureOps_.map(function(op){totalOpCost+=op.cmd_time;return op.cmd_time;});this.opCosts_.sort();var ninetyFifthPercentileCostIndex=Math.floor(this.opCosts_.length*0.95);this.ninetyFifthPercentileCost_=this.opCosts_[ninetyFifthPercentileCostIndex];this.maxCost_=this.opCosts_[this.opCosts_.length-1];this.totalOpCost_=totalOpCost;},extractBarIndex_:function(e){var index=undefined;if(this.pictureOps_===undefined||this.pictureOps_.length===0)
-return index;var x=e.offsetX;var y=e.offsetY;var totalBarWidth=(BAR_WIDTH+BAR_PADDING)*this.pictureOps_.length;var chartLeft=CHART_PADDING_LEFT;var chartTop=0;var chartBottom=this.chartHeight_-CHART_PADDING_BOTTOM;var chartRight=chartLeft+totalBarWidth;if(x<chartLeft||x>chartRight||y<chartTop||y>chartBottom)
-return index;index=Math.floor((x-chartLeft)/totalBarWidth*this.pictureOps_.length);index=tr.b.clamp(index,0,this.pictureOps_.length-1);return index;},onClick_:function(e){var barClicked=this.extractBarIndex_(e);if(barClicked===undefined)
-return;if(barClicked===this.selectedOpIndex)
-this.selectedOpIndex=undefined;else
-this.selectedOpIndex=barClicked;e.preventDefault();tr.b.dispatchSimpleEvent(this,'selection-changed',false);},onMouseMove_:function(e){var lastBarMouseOverTarget=this.currentBarMouseOverTarget_;this.currentBarMouseOverTarget_=this.extractBarIndex_(e);if(this.currentBarMouseOverTarget_===lastBarMouseOverTarget)
-return;this.drawChartContents_();},scrollSelectedItemIntoViewIfNecessary:function(){if(this.selectedOpIndex===undefined)
-return;var width=this.offsetWidth;var left=this.scrollLeft;var right=left+width;var targetLeft=CHART_PADDING_LEFT+
-(BAR_WIDTH+BAR_PADDING)*this.selectedOpIndex;if(targetLeft>left&&targetLeft<right)
-return;this.scrollLeft=(targetLeft-width*0.5);},updateChartContents:function(){if(this.dimensionsHaveChanged)
-this.updateChartDimensions_();this.drawChartContents_();},updateChartDimensions_:function(){if(!this.pictureOps_)
-return;var width=CHART_PADDING_LEFT+CHART_PADDING_RIGHT+
-((BAR_WIDTH+BAR_PADDING)*this.pictureOps_.length);if(width<this.offsetWidth)
-width=this.offsetWidth;this.chartWidth_=width;this.chartHeight_=this.getBoundingClientRect().height;this.chart_.width=this.chartWidth_*this.chartScale_;this.chart_.height=this.chartHeight_*this.chartScale_;this.chart_.style.width=this.chartWidth_+'px';this.chart_.style.height=this.chartHeight_+'px';this.chartCtx_.scale(this.chartScale_,this.chartScale_);this.dimensionsHaveChanged=false;},drawChartContents_:function(){this.clearChartContents_();if(this.pictureOps_===undefined||this.pictureOps_.length===0||this.pictureOps_[0].cmd_time===undefined){this.showNoTimingDataMessage_();return;}
-this.drawSelection_();this.drawBars_();this.drawChartAxes_();this.drawLinesAtTickMarks_();this.drawLineAtBottomOfChart_();if(this.currentBarMouseOverTarget_===undefined)
-return;this.drawTooltip_();},drawSelection_:function(){if(this.selectedOpIndex===undefined)
-return;var width=(BAR_WIDTH+BAR_PADDING)*this.selectedOpIndex;this.chartCtx_.fillStyle='rgb(223, 235, 230)';this.chartCtx_.fillRect(CHART_PADDING_LEFT,CHART_PADDING_TOP,width,this.chartHeight_-CHART_PADDING_TOP-CHART_PADDING_BOTTOM);},drawChartAxes_:function(){var min=this.opCosts_[0];var max=this.opCosts_[this.opCosts_.length-1];var height=this.chartHeight_-AXIS_PADDING_TOP-AXIS_PADDING_BOTTOM;var tickYInterval=height/(VERTICAL_TICKS-1);var tickYPosition=0;var tickValInterval=(max-min)/(VERTICAL_TICKS-1);var tickVal=0;this.chartCtx_.fillStyle='#333';this.chartCtx_.strokeStyle='#777';this.chartCtx_.save();this.chartCtx_.translate(0.5,0.5);this.chartCtx_.beginPath();this.chartCtx_.moveTo(AXIS_PADDING_LEFT,AXIS_PADDING_TOP);this.chartCtx_.lineTo(AXIS_PADDING_LEFT,this.chartHeight_-
+timingData.sort(this.sortTimingBucketsByOpTimeDescending_);this.collapseTimingBucketsToOther_(4);},collapseTimingBucketsToOther_:function(count){var timingData=this.opsTimingData_;var otherSource=timingData.splice(count,timingData.length-count);var otherDestination=null;if(!otherSource.length)return;timingData.push({cmd_time:0,cmd_string:'Other'});otherDestination=timingData[timingData.length-1];for(var i=0;i<otherSource.length;i++){otherDestination.cmd_time+=otherSource[i].cmd_time;}},sortTimingBucketsByOpTimeDescending_:function(a,b){return b.cmd_time-a.cmd_time;},resetOpsTimingData_:function(){this.opsTimingData_.length=0;}};return{PictureOpsChartSummaryView,};});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){var BAR_PADDING=1;var BAR_WIDTH=5;var CHART_PADDING_LEFT=65;var CHART_PADDING_RIGHT=30;var CHART_PADDING_BOTTOM=35;var CHART_PADDING_TOP=20;var AXIS_PADDING_LEFT=55;var AXIS_PADDING_RIGHT=30;var AXIS_PADDING_BOTTOM=35;var AXIS_PADDING_TOP=20;var AXIS_TICK_SIZE=5;var AXIS_LABEL_PADDING=5;var VERTICAL_TICKS=5;var HUE_CHAR_CODE_ADJUSTMENT=5.7;var PictureOpsChartView=tr.ui.b.define('tr-ui-e-chrome-cc-picture-ops-chart-view');PictureOpsChartView.prototype={__proto__:HTMLDivElement.prototype,decorate:function(){this.picture_=undefined;this.pictureOps_=undefined;this.opCosts_=undefined;this.chartScale_=window.devicePixelRatio;this.chart_=document.createElement('canvas');this.chartCtx_=this.chart_.getContext('2d');Polymer.dom(this).appendChild(this.chart_);this.selectedOpIndex_=undefined;this.chartWidth_=0;this.chartHeight_=0;this.dimensionsHaveChanged_=true;this.currentBarMouseOverTarget_=undefined;this.ninetyFifthPercentileCost_=0;this.totalOpCost_=0;this.chart_.addEventListener('click',this.onClick_.bind(this));this.chart_.addEventListener('mousemove',this.onMouseMove_.bind(this));this.usePercentileScale_=false;this.usePercentileScaleCheckbox_=tr.ui.b.createCheckBox(this,'usePercentileScale','PictureOpsChartView.usePercentileScale',false,'Limit to 95%-ile');Polymer.dom(this.usePercentileScaleCheckbox_).classList.add('use-percentile-scale');Polymer.dom(this).appendChild(this.usePercentileScaleCheckbox_);},get dimensionsHaveChanged(){return this.dimensionsHaveChanged_;},set dimensionsHaveChanged(dimensionsHaveChanged){this.dimensionsHaveChanged_=dimensionsHaveChanged;},get usePercentileScale(){return this.usePercentileScale_;},set usePercentileScale(usePercentileScale){this.usePercentileScale_=usePercentileScale;this.drawChartContents_();},get numOps(){return this.opCosts_.length;},get selectedOpIndex(){return this.selectedOpIndex_;},set selectedOpIndex(selectedOpIndex){if(selectedOpIndex<0)throw new Error('Invalid index');if(selectedOpIndex>=this.numOps)throw new Error('Invalid index');this.selectedOpIndex_=selectedOpIndex;},get picture(){return this.picture_;},set picture(picture){this.picture_=picture;this.pictureOps_=picture.tagOpsWithTimings(picture.getOps());this.currentBarMouseOverTarget_=undefined;this.processPictureData_();this.dimensionsHaveChanged=true;},processPictureData_:function(){if(this.pictureOps_===undefined)return;var totalOpCost=0;this.opCosts_=this.pictureOps_.map(function(op){totalOpCost+=op.cmd_time;return op.cmd_time;});this.opCosts_.sort();var ninetyFifthPercentileCostIndex=Math.floor(this.opCosts_.length*0.95);this.ninetyFifthPercentileCost_=this.opCosts_[ninetyFifthPercentileCostIndex];this.maxCost_=this.opCosts_[this.opCosts_.length-1];this.totalOpCost_=totalOpCost;},extractBarIndex_:function(e){var index=undefined;if(this.pictureOps_===undefined||this.pictureOps_.length===0){return index;}
+var x=e.offsetX;var y=e.offsetY;var totalBarWidth=(BAR_WIDTH+BAR_PADDING)*this.pictureOps_.length;var chartLeft=CHART_PADDING_LEFT;var chartTop=0;var chartBottom=this.chartHeight_-CHART_PADDING_BOTTOM;var chartRight=chartLeft+totalBarWidth;if(x<chartLeft||x>chartRight||y<chartTop||y>chartBottom){return index;}
+index=Math.floor((x-chartLeft)/totalBarWidth*this.pictureOps_.length);index=tr.b.math.clamp(index,0,this.pictureOps_.length-1);return index;},onClick_:function(e){var barClicked=this.extractBarIndex_(e);if(barClicked===undefined)return;if(barClicked===this.selectedOpIndex){this.selectedOpIndex=undefined;}else{this.selectedOpIndex=barClicked;}
+e.preventDefault();tr.b.dispatchSimpleEvent(this,'selection-changed',false);},onMouseMove_:function(e){var lastBarMouseOverTarget=this.currentBarMouseOverTarget_;this.currentBarMouseOverTarget_=this.extractBarIndex_(e);if(this.currentBarMouseOverTarget_===lastBarMouseOverTarget){return;}
+this.drawChartContents_();},scrollSelectedItemIntoViewIfNecessary:function(){if(this.selectedOpIndex===undefined){return;}
+var width=this.offsetWidth;var left=this.scrollLeft;var right=left+width;var targetLeft=CHART_PADDING_LEFT+
+(BAR_WIDTH+BAR_PADDING)*this.selectedOpIndex;if(targetLeft>left&&targetLeft<right){return;}
+this.scrollLeft=(targetLeft-width*0.5);},updateChartContents:function(){if(this.dimensionsHaveChanged){this.updateChartDimensions_();}
+this.drawChartContents_();},updateChartDimensions_:function(){if(!this.pictureOps_)return;var width=CHART_PADDING_LEFT+CHART_PADDING_RIGHT+
+((BAR_WIDTH+BAR_PADDING)*this.pictureOps_.length);if(width<this.offsetWidth){width=this.offsetWidth;}
+this.chartWidth_=width;this.chartHeight_=this.getBoundingClientRect().height;this.chart_.width=this.chartWidth_*this.chartScale_;this.chart_.height=this.chartHeight_*this.chartScale_;this.chart_.style.width=this.chartWidth_+'px';this.chart_.style.height=this.chartHeight_+'px';this.chartCtx_.scale(this.chartScale_,this.chartScale_);this.dimensionsHaveChanged=false;},drawChartContents_:function(){this.clearChartContents_();if(this.pictureOps_===undefined||this.pictureOps_.length===0||this.pictureOps_[0].cmd_time===undefined){this.showNoTimingDataMessage_();return;}
+this.drawSelection_();this.drawBars_();this.drawChartAxes_();this.drawLinesAtTickMarks_();this.drawLineAtBottomOfChart_();if(this.currentBarMouseOverTarget_===undefined){return;}
+this.drawTooltip_();},drawSelection_:function(){if(this.selectedOpIndex===undefined){return;}
+var width=(BAR_WIDTH+BAR_PADDING)*this.selectedOpIndex;this.chartCtx_.fillStyle='rgb(223, 235, 230)';this.chartCtx_.fillRect(CHART_PADDING_LEFT,CHART_PADDING_TOP,width,this.chartHeight_-CHART_PADDING_TOP-CHART_PADDING_BOTTOM);},drawChartAxes_:function(){var min=this.opCosts_[0];var max=this.opCosts_[this.opCosts_.length-1];var height=this.chartHeight_-AXIS_PADDING_TOP-AXIS_PADDING_BOTTOM;var tickYInterval=height/(VERTICAL_TICKS-1);var tickYPosition=0;var tickValInterval=(max-min)/(VERTICAL_TICKS-1);var tickVal=0;this.chartCtx_.fillStyle='#333';this.chartCtx_.strokeStyle='#777';this.chartCtx_.save();this.chartCtx_.translate(0.5,0.5);this.chartCtx_.beginPath();this.chartCtx_.moveTo(AXIS_PADDING_LEFT,AXIS_PADDING_TOP);this.chartCtx_.lineTo(AXIS_PADDING_LEFT,this.chartHeight_-
 AXIS_PADDING_BOTTOM);this.chartCtx_.lineTo(this.chartWidth_-AXIS_PADDING_RIGHT,this.chartHeight_-AXIS_PADDING_BOTTOM);this.chartCtx_.stroke();this.chartCtx_.closePath();this.chartCtx_.translate(AXIS_PADDING_LEFT,AXIS_PADDING_TOP);this.chartCtx_.font='10px Arial';this.chartCtx_.textAlign='right';this.chartCtx_.textBaseline='middle';this.chartCtx_.beginPath();for(var t=0;t<VERTICAL_TICKS;t++){tickYPosition=Math.round(t*tickYInterval);tickVal=(max-t*tickValInterval).toFixed(4);this.chartCtx_.moveTo(0,tickYPosition);this.chartCtx_.lineTo(-AXIS_TICK_SIZE,tickYPosition);this.chartCtx_.fillText(tickVal,-AXIS_TICK_SIZE-AXIS_LABEL_PADDING,tickYPosition);}
 this.chartCtx_.stroke();this.chartCtx_.closePath();this.chartCtx_.restore();},drawLinesAtTickMarks_:function(){var height=this.chartHeight_-AXIS_PADDING_TOP-AXIS_PADDING_BOTTOM;var width=this.chartWidth_-AXIS_PADDING_LEFT-AXIS_PADDING_RIGHT;var tickYInterval=height/(VERTICAL_TICKS-1);var tickYPosition=0;this.chartCtx_.save();this.chartCtx_.translate(AXIS_PADDING_LEFT+0.5,AXIS_PADDING_TOP+0.5);this.chartCtx_.beginPath();this.chartCtx_.strokeStyle='rgba(0,0,0,0.05)';for(var t=0;t<VERTICAL_TICKS;t++){tickYPosition=Math.round(t*tickYInterval);this.chartCtx_.moveTo(0,tickYPosition);this.chartCtx_.lineTo(width,tickYPosition);this.chartCtx_.stroke();}
 this.chartCtx_.restore();this.chartCtx_.closePath();},drawLineAtBottomOfChart_:function(){this.chartCtx_.strokeStyle='#AAA';this.chartCtx_.beginPath();this.chartCtx_.moveTo(0,this.chartHeight_-0.5);this.chartCtx_.lineTo(this.chartWidth_,this.chartHeight_-0.5);this.chartCtx_.stroke();this.chartCtx_.closePath();},drawTooltip_:function(){var tooltipData=this.pictureOps_[this.currentBarMouseOverTarget_];var tooltipTitle=tooltipData.cmd_string;var tooltipTime=tooltipData.cmd_time.toFixed(4);var toolTipTimePercentage=((tooltipData.cmd_time/this.totalOpCost_)*100).toFixed(2);var tooltipWidth=120;var tooltipHeight=40;var chartInnerWidth=this.chartWidth_-CHART_PADDING_RIGHT-
 CHART_PADDING_LEFT;var barWidth=BAR_WIDTH+BAR_PADDING;var tooltipOffset=Math.round((tooltipWidth-barWidth)*0.5);var left=CHART_PADDING_LEFT+this.currentBarMouseOverTarget_*barWidth-tooltipOffset;var top=Math.round((this.chartHeight_-tooltipHeight)*0.5);this.chartCtx_.save();this.chartCtx_.shadowOffsetX=0;this.chartCtx_.shadowOffsetY=5;this.chartCtx_.shadowBlur=4;this.chartCtx_.shadowColor='rgba(0,0,0,0.4)';this.chartCtx_.strokeStyle='#888';this.chartCtx_.fillStyle='#EEE';this.chartCtx_.fillRect(left,top,tooltipWidth,tooltipHeight);this.chartCtx_.shadowColor='transparent';this.chartCtx_.translate(0.5,0.5);this.chartCtx_.strokeRect(left,top,tooltipWidth,tooltipHeight);this.chartCtx_.restore();this.chartCtx_.fillStyle='#222';this.chartCtx_.textAlign='left';this.chartCtx_.textBaseline='top';this.chartCtx_.font='800 12px Arial';this.chartCtx_.fillText(tooltipTitle,left+8,top+8);this.chartCtx_.fillStyle='#555';this.chartCtx_.font='400 italic 10px Arial';this.chartCtx_.fillText(tooltipTime+'ms ('+
 toolTipTimePercentage+'%)',left+8,top+22);},drawBars_:function(){var op;var opColor=0;var opHeight=0;var opWidth=BAR_WIDTH+BAR_PADDING;var opHover=false;var bottom=this.chartHeight_-CHART_PADDING_BOTTOM;var maxHeight=this.chartHeight_-CHART_PADDING_BOTTOM-
-CHART_PADDING_TOP;var maxValue;if(this.usePercentileScale)
-maxValue=this.ninetyFifthPercentileCost_;else
-maxValue=this.maxCost_;for(var b=0;b<this.pictureOps_.length;b++){op=this.pictureOps_[b];opHeight=Math.round((op.cmd_time/maxValue)*maxHeight);opHeight=Math.max(opHeight,1);opHover=(b===this.currentBarMouseOverTarget_);opColor=this.getOpColor_(op.cmd_string,opHover);if(b===this.selectedOpIndex)
-this.chartCtx_.fillStyle='#FFFF00';else
-this.chartCtx_.fillStyle=opColor;this.chartCtx_.fillRect(CHART_PADDING_LEFT+b*opWidth,bottom-opHeight,BAR_WIDTH,opHeight);}},getOpColor_:function(opName,hover){var characters=opName.split('');var hue=characters.reduce(this.reduceNameToHue,0)%360;var saturation=30;var lightness=hover?'75%':'50%';return'hsl('+hue+', '+saturation+'%, '+lightness+'%)';},reduceNameToHue:function(previousValue,currentValue,index,array){return Math.round(previousValue+currentValue.charCodeAt(0)*HUE_CHAR_CODE_ADJUSTMENT);},clearChartContents_:function(){this.chartCtx_.clearRect(0,0,this.chartWidth_,this.chartHeight_);},showNoTimingDataMessage_:function(){this.chartCtx_.font='800 italic 14px Arial';this.chartCtx_.fillStyle='#333';this.chartCtx_.textAlign='center';this.chartCtx_.textBaseline='middle';this.chartCtx_.fillText('No timing data available.',this.chartWidth_*0.5,this.chartHeight_*0.5);}};return{PictureOpsChartView:PictureOpsChartView};});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){var THIS_DOC=document.currentScript.ownerDocument;var PictureDebugger=tr.ui.b.define('tr-ui-e-chrome-cc-picture-debugger');PictureDebugger.prototype={__proto__:HTMLDivElement.prototype,decorate:function(){var node=tr.ui.b.instantiateTemplate('#tr-ui-e-chrome-cc-picture-debugger-template',THIS_DOC);Polymer.dom(this).appendChild(node);this.pictureAsImageData_=undefined;this.showOverdraw_=false;this.zoomScaleValue_=1;this.sizeInfo_=Polymer.dom(this).querySelector('.size');this.rasterArea_=Polymer.dom(this).querySelector('raster-area');this.rasterCanvas_=Polymer.dom(this.rasterArea_).querySelector('canvas');this.rasterCtx_=this.rasterCanvas_.getContext('2d');this.filename_=Polymer.dom(this).querySelector('.filename');this.drawOpsChartSummaryView_=new tr.ui.e.chrome.cc.PictureOpsChartSummaryView();this.drawOpsChartView_=new tr.ui.e.chrome.cc.PictureOpsChartView();this.drawOpsChartView_.addEventListener('selection-changed',this.onChartBarClicked_.bind(this));this.exportButton_=Polymer.dom(this).querySelector('.export');this.exportButton_.addEventListener('click',this.onSaveAsSkPictureClicked_.bind(this));this.trackMouse_();var overdrawCheckbox=tr.ui.b.createCheckBox(this,'showOverdraw','pictureView.showOverdraw',false,'Show overdraw');var chartCheckbox=tr.ui.b.createCheckBox(this,'showSummaryChart','pictureView.showSummaryChart',false,'Show timing summary');var pictureInfo=Polymer.dom(this).querySelector('picture-info');Polymer.dom(pictureInfo).appendChild(overdrawCheckbox);Polymer.dom(pictureInfo).appendChild(chartCheckbox);this.drawOpsView_=new tr.ui.e.chrome.cc.PictureOpsListView();this.drawOpsView_.addEventListener('selection-changed',this.onChangeDrawOps_.bind(this));var leftPanel=Polymer.dom(this).querySelector('left-panel');Polymer.dom(leftPanel).appendChild(this.drawOpsChartSummaryView_);Polymer.dom(leftPanel).appendChild(this.drawOpsView_);var middleDragHandle=document.createElement('tr-ui-b-drag-handle');middleDragHandle.horizontal=false;middleDragHandle.target=leftPanel;var rightPanel=Polymer.dom(this).querySelector('right-panel');rightPanel.replaceChild(this.drawOpsChartView_,Polymer.dom(rightPanel).querySelector('tr-ui-e-chrome-cc-picture-ops-chart-view'));this.infoBar_=document.createElement('tr-ui-b-info-bar');Polymer.dom(this.rasterArea_).appendChild(this.infoBar_);Polymer.dom(this).insertBefore(middleDragHandle,rightPanel);this.picture_=undefined;var hkc=document.createElement('tv-ui-b-hotkey-controller');hkc.addHotKey(new tr.ui.b.HotKey({eventType:'keypress',thisArg:this,keyCode:'h'.charCodeAt(0),callback:function(e){this.moveSelectedOpBy(-1);e.stopPropagation();}}));hkc.addHotKey(new tr.ui.b.HotKey({eventType:'keypress',thisArg:this,keyCode:'l'.charCodeAt(0),callback:function(e){this.moveSelectedOpBy(1);e.stopPropagation();}}));Polymer.dom(this).appendChild(hkc);this.mutationObserver_=new MutationObserver(this.onMutation_.bind(this));this.mutationObserver_.observe(leftPanel,{attributes:true});},onMutation_:function(mutations){for(var m=0;m<mutations.length;m++){if(mutations[m].attributeName==='style'){this.drawOpsChartSummaryView_.requiresRedraw=true;this.drawOpsChartSummaryView_.updateChartContents();this.drawOpsChartView_.dimensionsHaveChanged=true;this.drawOpsChartView_.updateChartContents();break;}}},onSaveAsSkPictureClicked_:function(){var rawData=tr.b.Base64.atob(this.picture_.getBase64SkpData());var length=rawData.length;var arrayBuffer=new ArrayBuffer(length);var uint8Array=new Uint8Array(arrayBuffer);for(var c=0;c<length;c++)
-uint8Array[c]=rawData.charCodeAt(c);var blob=new Blob([uint8Array],{type:'application/octet-binary'});var blobUrl=window.webkitURL.createObjectURL(blob);var link=document.createElementNS('http://www.w3.org/1999/xhtml','a');link.href=blobUrl;link.download=this.filename_.value;var event=document.createEvent('MouseEvents');event.initMouseEvent('click',true,false,window,0,0,0,0,0,false,false,false,false,0,null);link.dispatchEvent(event);},get picture(){return this.picture_;},set picture(picture){this.drawOpsView_.picture=picture;this.drawOpsChartView_.picture=picture;this.drawOpsChartSummaryView_.picture=picture;this.picture_=picture;this.exportButton_.disabled=!this.picture_.canSave;if(picture){var size=this.getRasterCanvasSize_();this.rasterCanvas_.width=size.width;this.rasterCanvas_.height=size.height;}
-var bounds=this.rasterArea_.getBoundingClientRect();var selectorBounds=this.mouseModeSelector_.getBoundingClientRect();this.mouseModeSelector_.pos={x:(bounds.right-selectorBounds.width-10),y:bounds.top};this.rasterize_();this.scheduleUpdateContents_();},getRasterCanvasSize_:function(){var style=window.getComputedStyle(this.rasterArea_);var width=Math.max(parseInt(style.width),this.picture_.layerRect.width);var height=Math.max(parseInt(style.height),this.picture_.layerRect.height);return{width:width,height:height};},scheduleUpdateContents_:function(){if(this.updateContentsPending_)
-return;this.updateContentsPending_=true;tr.b.requestAnimationFrameInThisFrameIfPossible(this.updateContents_.bind(this));},updateContents_:function(){this.updateContentsPending_=false;if(this.picture_){Polymer.dom(this.sizeInfo_).textContent='('+
+CHART_PADDING_TOP;var maxValue;if(this.usePercentileScale){maxValue=this.ninetyFifthPercentileCost_;}else{maxValue=this.maxCost_;}
+for(var b=0;b<this.pictureOps_.length;b++){op=this.pictureOps_[b];opHeight=Math.round((op.cmd_time/maxValue)*maxHeight);opHeight=Math.max(opHeight,1);opHover=(b===this.currentBarMouseOverTarget_);opColor=this.getOpColor_(op.cmd_string,opHover);if(b===this.selectedOpIndex){this.chartCtx_.fillStyle='#FFFF00';}else{this.chartCtx_.fillStyle=opColor;}
+this.chartCtx_.fillRect(CHART_PADDING_LEFT+b*opWidth,bottom-opHeight,BAR_WIDTH,opHeight);}},getOpColor_:function(opName,hover){var characters=opName.split('');var hue=characters.reduce(this.reduceNameToHue,0)%360;var saturation=30;var lightness=hover?'75%':'50%';return'hsl('+hue+', '+saturation+'%, '+lightness+'%)';},reduceNameToHue:function(previousValue,currentValue,index,array){return Math.round(previousValue+currentValue.charCodeAt(0)*HUE_CHAR_CODE_ADJUSTMENT);},clearChartContents_:function(){this.chartCtx_.clearRect(0,0,this.chartWidth_,this.chartHeight_);},showNoTimingDataMessage_:function(){this.chartCtx_.font='800 italic 14px Arial';this.chartCtx_.fillStyle='#333';this.chartCtx_.textAlign='center';this.chartCtx_.textBaseline='middle';this.chartCtx_.fillText('No timing data available.',this.chartWidth_*0.5,this.chartHeight_*0.5);}};return{PictureOpsChartView,};});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){var THIS_DOC=document.currentScript.ownerDocument;var PictureDebugger=tr.ui.b.define('tr-ui-e-chrome-cc-picture-debugger');PictureDebugger.prototype={__proto__:HTMLDivElement.prototype,decorate:function(){var node=tr.ui.b.instantiateTemplate('#tr-ui-e-chrome-cc-picture-debugger-template',THIS_DOC);Polymer.dom(this).appendChild(node);this.pictureAsImageData_=undefined;this.showOverdraw_=false;this.zoomScaleValue_=1;this.sizeInfo_=Polymer.dom(this).querySelector('.size');this.rasterArea_=Polymer.dom(this).querySelector('raster-area');this.rasterCanvas_=Polymer.dom(this.rasterArea_).querySelector('canvas');this.rasterCtx_=this.rasterCanvas_.getContext('2d');this.filename_=Polymer.dom(this).querySelector('.filename');this.drawOpsChartSummaryView_=new tr.ui.e.chrome.cc.PictureOpsChartSummaryView();this.drawOpsChartView_=new tr.ui.e.chrome.cc.PictureOpsChartView();this.drawOpsChartView_.addEventListener('selection-changed',this.onChartBarClicked_.bind(this));this.exportButton_=Polymer.dom(this).querySelector('.export');this.exportButton_.addEventListener('click',this.onSaveAsSkPictureClicked_.bind(this));this.trackMouse_();var overdrawCheckbox=tr.ui.b.createCheckBox(this,'showOverdraw','pictureView.showOverdraw',false,'Show overdraw');var chartCheckbox=tr.ui.b.createCheckBox(this,'showSummaryChart','pictureView.showSummaryChart',false,'Show timing summary');var pictureInfo=Polymer.dom(this).querySelector('picture-info');Polymer.dom(pictureInfo).appendChild(overdrawCheckbox);Polymer.dom(pictureInfo).appendChild(chartCheckbox);this.drawOpsView_=new tr.ui.e.chrome.cc.PictureOpsListView();this.drawOpsView_.addEventListener('selection-changed',this.onChangeDrawOps_.bind(this));var leftPanel=Polymer.dom(this).querySelector('left-panel');Polymer.dom(leftPanel).appendChild(this.drawOpsChartSummaryView_);Polymer.dom(leftPanel).appendChild(this.drawOpsView_);var middleDragHandle=document.createElement('tr-ui-b-drag-handle');middleDragHandle.horizontal=false;middleDragHandle.target=leftPanel;var rightPanel=Polymer.dom(this).querySelector('right-panel');rightPanel.replaceChild(this.drawOpsChartView_,Polymer.dom(rightPanel).querySelector('tr-ui-e-chrome-cc-picture-ops-chart-view'));this.infoBar_=document.createElement('tr-ui-b-info-bar');Polymer.dom(this.rasterArea_).appendChild(this.infoBar_);Polymer.dom(this).insertBefore(middleDragHandle,rightPanel);this.picture_=undefined;var hkc=document.createElement('tv-ui-b-hotkey-controller');hkc.addHotKey(new tr.ui.b.HotKey({eventType:'keypress',thisArg:this,keyCode:'h'.charCodeAt(0),callback:function(e){this.moveSelectedOpBy(-1);e.stopPropagation();}}));hkc.addHotKey(new tr.ui.b.HotKey({eventType:'keypress',thisArg:this,keyCode:'l'.charCodeAt(0),callback:function(e){this.moveSelectedOpBy(1);e.stopPropagation();}}));Polymer.dom(this).appendChild(hkc);this.mutationObserver_=new MutationObserver(this.onMutation_.bind(this));this.mutationObserver_.observe(leftPanel,{attributes:true});},onMutation_:function(mutations){for(var m=0;m<mutations.length;m++){if(mutations[m].attributeName==='style'){this.drawOpsChartSummaryView_.requiresRedraw=true;this.drawOpsChartSummaryView_.updateChartContents();this.drawOpsChartView_.dimensionsHaveChanged=true;this.drawOpsChartView_.updateChartContents();break;}}},onSaveAsSkPictureClicked_:function(){var rawData=tr.b.Base64.atob(this.picture_.getBase64SkpData());var length=rawData.length;var arrayBuffer=new ArrayBuffer(length);var uint8Array=new Uint8Array(arrayBuffer);for(var c=0;c<length;c++){uint8Array[c]=rawData.charCodeAt(c);}
+var blob=new Blob([uint8Array],{type:'application/octet-binary'});var blobUrl=window.webkitURL.createObjectURL(blob);var link=document.createElementNS('http://www.w3.org/1999/xhtml','a');link.href=blobUrl;link.download=this.filename_.value;var event=document.createEvent('MouseEvents');event.initMouseEvent('click',true,false,window,0,0,0,0,0,false,false,false,false,0,null);link.dispatchEvent(event);},get picture(){return this.picture_;},set picture(picture){this.drawOpsView_.picture=picture;this.drawOpsChartView_.picture=picture;this.drawOpsChartSummaryView_.picture=picture;this.picture_=picture;this.exportButton_.disabled=!this.picture_.canSave;if(picture){var size=this.getRasterCanvasSize_();this.rasterCanvas_.width=size.width;this.rasterCanvas_.height=size.height;}
+var bounds=this.rasterArea_.getBoundingClientRect();var selectorBounds=this.mouseModeSelector_.getBoundingClientRect();this.mouseModeSelector_.pos={x:(bounds.right-selectorBounds.width-10),y:bounds.top};this.rasterize_();this.scheduleUpdateContents_();},getRasterCanvasSize_:function(){var style=window.getComputedStyle(this.rasterArea_);var width=Math.max(parseInt(style.width),this.picture_.layerRect.width);var height=Math.max(parseInt(style.height),this.picture_.layerRect.height);return{width:width,height:height};},scheduleUpdateContents_:function(){if(this.updateContentsPending_)return;this.updateContentsPending_=true;tr.b.requestAnimationFrameInThisFrameIfPossible(this.updateContents_.bind(this));},updateContents_:function(){this.updateContentsPending_=false;if(this.picture_){Polymer.dom(this.sizeInfo_).textContent='('+
 this.picture_.layerRect.width+' x '+
 this.picture_.layerRect.height+')';}
-this.drawOpsChartView_.updateChartContents();this.drawOpsChartView_.scrollSelectedItemIntoViewIfNecessary();if(!this.pictureAsImageData_)
-return;this.infoBar_.visible=false;this.infoBar_.removeAllButtons();if(this.pictureAsImageData_.error){this.infoBar_.message='Cannot rasterize...';this.infoBar_.addButton('More info...',function(e){var overlay=new tr.ui.b.Overlay();Polymer.dom(overlay).textContent=this.pictureAsImageData_.error;overlay.visible=true;e.stopPropagation();return false;}.bind(this));this.infoBar_.visible=true;}
-this.drawPicture_();},drawPicture_:function(){var size=this.getRasterCanvasSize_();if(size.width!==this.rasterCanvas_.width)
-this.rasterCanvas_.width=size.width;if(size.height!==this.rasterCanvas_.height)
-this.rasterCanvas_.height=size.height;this.rasterCtx_.clearRect(0,0,size.width,size.height);if(!this.pictureAsImageData_.imageData)
-return;var imgCanvas=this.pictureAsImageData_.asCanvas();var w=imgCanvas.width;var h=imgCanvas.height;this.rasterCtx_.drawImage(imgCanvas,0,0,w,h,0,0,w*this.zoomScaleValue_,h*this.zoomScaleValue_);},rasterize_:function(){if(this.picture_){this.picture_.rasterize({stopIndex:this.drawOpsView_.selectedOpIndex,showOverdraw:this.showOverdraw_},this.onRasterComplete_.bind(this));}},onRasterComplete_:function(pictureAsImageData){this.pictureAsImageData_=pictureAsImageData;this.scheduleUpdateContents_();},moveSelectedOpBy:function(increment){if(this.selectedOpIndex===undefined){this.selectedOpIndex=0;return;}
-this.selectedOpIndex=tr.b.clamp(this.selectedOpIndex+increment,0,this.numOps);},get numOps(){return this.drawOpsView_.numOps;},get selectedOpIndex(){return this.drawOpsView_.selectedOpIndex;},set selectedOpIndex(index){this.drawOpsView_.selectedOpIndex=index;this.drawOpsChartView_.selectedOpIndex=index;},onChartBarClicked_:function(e){this.drawOpsView_.selectedOpIndex=this.drawOpsChartView_.selectedOpIndex;},onChangeDrawOps_:function(e){this.rasterize_();this.scheduleUpdateContents_();this.drawOpsChartView_.selectedOpIndex=this.drawOpsView_.selectedOpIndex;},set showOverdraw(v){this.showOverdraw_=v;this.rasterize_();},set showSummaryChart(chartShouldBeVisible){if(chartShouldBeVisible)
-this.drawOpsChartSummaryView_.show();else
-this.drawOpsChartSummaryView_.hide();},trackMouse_:function(){this.mouseModeSelector_=document.createElement('tr-ui-b-mouse-mode-selector');this.mouseModeSelector_.targetElement=this.rasterArea_;Polymer.dom(this.rasterArea_).appendChild(this.mouseModeSelector_);this.mouseModeSelector_.supportedModeMask=tr.ui.b.MOUSE_SELECTOR_MODE.ZOOM;this.mouseModeSelector_.mode=tr.ui.b.MOUSE_SELECTOR_MODE.ZOOM;this.mouseModeSelector_.defaultMode=tr.ui.b.MOUSE_SELECTOR_MODE.ZOOM;this.mouseModeSelector_.settingsKey='pictureDebugger.mouseModeSelector';this.mouseModeSelector_.addEventListener('beginzoom',this.onBeginZoom_.bind(this));this.mouseModeSelector_.addEventListener('updatezoom',this.onUpdateZoom_.bind(this));this.mouseModeSelector_.addEventListener('endzoom',this.onEndZoom_.bind(this));},onBeginZoom_:function(e){this.isZooming_=true;this.lastMouseViewPos_=this.extractRelativeMousePosition_(e);e.preventDefault();},onUpdateZoom_:function(e){if(!this.isZooming_)
-return;var currentMouseViewPos=this.extractRelativeMousePosition_(e);this.zoomScaleValue_+=((this.lastMouseViewPos_.y-currentMouseViewPos.y)*0.001);this.zoomScaleValue_=Math.max(this.zoomScaleValue_,0.1);this.drawPicture_();this.lastMouseViewPos_=currentMouseViewPos;},onEndZoom_:function(e){this.lastMouseViewPos_=undefined;this.isZooming_=false;e.preventDefault();},extractRelativeMousePosition_:function(e){return{x:e.clientX-this.rasterArea_.offsetLeft,y:e.clientY-this.rasterArea_.offsetTop};}};return{PictureDebugger:PictureDebugger};});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){var PictureSnapshotView=tr.ui.b.define('tr-ui-e-chrome-cc-picture-snapshot-view',tr.ui.analysis.ObjectSnapshotView);PictureSnapshotView.prototype={__proto__:tr.ui.analysis.ObjectSnapshotView.prototype,decorate:function(){Polymer.dom(this).classList.add('tr-ui-e-chrome-cc-picture-snapshot-view');this.pictureDebugger_=new tr.ui.e.chrome.cc.PictureDebugger();Polymer.dom(this).appendChild(this.pictureDebugger_);},updateContents:function(){if(this.objectSnapshot_&&this.pictureDebugger_)
-this.pictureDebugger_.picture=this.objectSnapshot_;}};tr.ui.analysis.ObjectSnapshotView.register(PictureSnapshotView,{typeNames:['cc::Picture','cc::LayeredPicture'],showInstances:false});return{PictureSnapshotView:PictureSnapshotView};});'use strict';tr.exportTo('tr.e.cc',function(){var knownRasterTaskNames=['TileManager::RunRasterTask','RasterWorkerPoolTaskImpl::RunRasterOnThread','RasterWorkerPoolTaskImpl::Raster','RasterTaskImpl::Raster','cc::RasterTask','RasterTask'];var knownAnalysisTaskNames=['TileManager::RunAnalyzeTask','RasterWorkerPoolTaskImpl::RunAnalysisOnThread','RasterWorkerPoolTaskImpl::Analyze','RasterTaskImpl::Analyze','cc::AnalyzeTask','AnalyzeTask'];function getTileFromRasterTaskSlice(slice){if(!(isSliceDoingRasterization(slice)||isSliceDoingAnalysis(slice)))
-return undefined;var tileData;if(slice.args.data)
-tileData=slice.args.data;else
-tileData=slice.args.tileData;if(tileData===undefined)
-return undefined;if(tileData.tile_id)
-return tileData.tile_id;var tile=tileData.tileId;if(!(tile instanceof tr.e.cc.TileSnapshot))
-return undefined;return tileData.tileId;}
-function isSliceDoingRasterization(slice){if(knownRasterTaskNames.indexOf(slice.title)!==-1)
-return true;return false;}
-function isSliceDoingAnalysis(slice){if(knownAnalysisTaskNames.indexOf(slice.title)!==-1)
-return true;return false;}
-return{getTileFromRasterTaskSlice:getTileFromRasterTaskSlice,isSliceDoingRasterization:isSliceDoingRasterization,isSliceDoingAnalysis:isSliceDoingAnalysis};});'use strict';tr.exportTo('tr.ui.analysis',function(){var AnalysisSubView={set tabLabel(label){Polymer.dom(this).setAttribute('tab-label',label);},get tabLabel(){return this.getAttribute('tab-label');},get requiresTallView(){return false;},get relatedEventsToHighlight(){return undefined;},set selection(selection){throw new Error('Not implemented!');},get selection(){throw new Error('Not implemented!');}};var allTypeInfosByEventProto=new Map();var onlyRootTypeInfosByEventProto=undefined;var eventProtoToRootTypeInfoMap=undefined;function AnalysisSubViewTypeInfo(eventConstructor,options){if(options.multi===undefined)
-throw new Error('missing field: multi');if(options.title===undefined)
-throw new Error('missing field: title');this.eventConstructor=eventConstructor;this.singleTagName=undefined;this.singleTitle=undefined;this.multiTagName=undefined;this.multiTitle=undefined;this.childrenTypeInfos_=undefined;};AnalysisSubViewTypeInfo.prototype={get childrenTypeInfos(){return this.childrenTypeInfos_;},resetchildrenTypeInfos:function(){this.childrenTypeInfos_=[];}};AnalysisSubView.register=function(tagName,eventConstructor,options){var typeInfo=allTypeInfosByEventProto.get(eventConstructor.prototype);if(typeInfo===undefined){typeInfo=new AnalysisSubViewTypeInfo(eventConstructor,options);allTypeInfosByEventProto.set(typeInfo.eventConstructor.prototype,typeInfo);onlyRootTypeInfosByEventProto=undefined;}
-if(!options.multi){if(typeInfo.singleTagName!==undefined)
-throw new Error('SingleTagName already set');typeInfo.singleTagName=tagName;typeInfo.singleTitle=options.title;}else{if(typeInfo.multiTagName!==undefined)
-throw new Error('MultiTagName already set');typeInfo.multiTagName=tagName;typeInfo.multiTitle=options.title;}
+this.drawOpsChartView_.updateChartContents();this.drawOpsChartView_.scrollSelectedItemIntoViewIfNecessary();if(!this.pictureAsImageData_)return;this.infoBar_.visible=false;this.infoBar_.removeAllButtons();if(this.pictureAsImageData_.error){this.infoBar_.message='Cannot rasterize...';this.infoBar_.addButton('More info...',function(e){var overlay=new tr.ui.b.Overlay();Polymer.dom(overlay).textContent=this.pictureAsImageData_.error;overlay.visible=true;e.stopPropagation();return false;}.bind(this));this.infoBar_.visible=true;}
+this.drawPicture_();},drawPicture_:function(){var size=this.getRasterCanvasSize_();if(size.width!==this.rasterCanvas_.width){this.rasterCanvas_.width=size.width;}
+if(size.height!==this.rasterCanvas_.height){this.rasterCanvas_.height=size.height;}
+this.rasterCtx_.clearRect(0,0,size.width,size.height);if(!this.pictureAsImageData_.imageData)return;var imgCanvas=this.pictureAsImageData_.asCanvas();var w=imgCanvas.width;var h=imgCanvas.height;this.rasterCtx_.drawImage(imgCanvas,0,0,w,h,0,0,w*this.zoomScaleValue_,h*this.zoomScaleValue_);},rasterize_:function(){if(this.picture_){this.picture_.rasterize({stopIndex:this.drawOpsView_.selectedOpIndex,showOverdraw:this.showOverdraw_},this.onRasterComplete_.bind(this));}},onRasterComplete_:function(pictureAsImageData){this.pictureAsImageData_=pictureAsImageData;this.scheduleUpdateContents_();},moveSelectedOpBy:function(increment){if(this.selectedOpIndex===undefined){this.selectedOpIndex=0;return;}
+this.selectedOpIndex=tr.b.math.clamp(this.selectedOpIndex+increment,0,this.numOps);},get numOps(){return this.drawOpsView_.numOps;},get selectedOpIndex(){return this.drawOpsView_.selectedOpIndex;},set selectedOpIndex(index){this.drawOpsView_.selectedOpIndex=index;this.drawOpsChartView_.selectedOpIndex=index;},onChartBarClicked_:function(e){this.drawOpsView_.selectedOpIndex=this.drawOpsChartView_.selectedOpIndex;},onChangeDrawOps_:function(e){this.rasterize_();this.scheduleUpdateContents_();this.drawOpsChartView_.selectedOpIndex=this.drawOpsView_.selectedOpIndex;},set showOverdraw(v){this.showOverdraw_=v;this.rasterize_();},set showSummaryChart(chartShouldBeVisible){if(chartShouldBeVisible){this.drawOpsChartSummaryView_.show();}else{this.drawOpsChartSummaryView_.hide();}},trackMouse_:function(){this.mouseModeSelector_=document.createElement('tr-ui-b-mouse-mode-selector');this.mouseModeSelector_.targetElement=this.rasterArea_;Polymer.dom(this.rasterArea_).appendChild(this.mouseModeSelector_);this.mouseModeSelector_.supportedModeMask=tr.ui.b.MOUSE_SELECTOR_MODE.ZOOM;this.mouseModeSelector_.mode=tr.ui.b.MOUSE_SELECTOR_MODE.ZOOM;this.mouseModeSelector_.defaultMode=tr.ui.b.MOUSE_SELECTOR_MODE.ZOOM;this.mouseModeSelector_.settingsKey='pictureDebugger.mouseModeSelector';this.mouseModeSelector_.addEventListener('beginzoom',this.onBeginZoom_.bind(this));this.mouseModeSelector_.addEventListener('updatezoom',this.onUpdateZoom_.bind(this));this.mouseModeSelector_.addEventListener('endzoom',this.onEndZoom_.bind(this));},onBeginZoom_:function(e){this.isZooming_=true;this.lastMouseViewPos_=this.extractRelativeMousePosition_(e);e.preventDefault();},onUpdateZoom_:function(e){if(!this.isZooming_)return;var currentMouseViewPos=this.extractRelativeMousePosition_(e);this.zoomScaleValue_+=((this.lastMouseViewPos_.y-currentMouseViewPos.y)*0.001);this.zoomScaleValue_=Math.max(this.zoomScaleValue_,0.1);this.drawPicture_();this.lastMouseViewPos_=currentMouseViewPos;},onEndZoom_:function(e){this.lastMouseViewPos_=undefined;this.isZooming_=false;e.preventDefault();},extractRelativeMousePosition_:function(e){return{x:e.clientX-this.rasterArea_.offsetLeft,y:e.clientY-this.rasterArea_.offsetTop};}};return{PictureDebugger,};});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){var PictureSnapshotView=tr.ui.b.define('tr-ui-e-chrome-cc-picture-snapshot-view',tr.ui.analysis.ObjectSnapshotView);PictureSnapshotView.prototype={__proto__:tr.ui.analysis.ObjectSnapshotView.prototype,decorate:function(){Polymer.dom(this).classList.add('tr-ui-e-chrome-cc-picture-snapshot-view');this.pictureDebugger_=new tr.ui.e.chrome.cc.PictureDebugger();Polymer.dom(this).appendChild(this.pictureDebugger_);},updateContents:function(){if(this.objectSnapshot_&&this.pictureDebugger_){this.pictureDebugger_.picture=this.objectSnapshot_;}}};tr.ui.analysis.ObjectSnapshotView.register(PictureSnapshotView,{typeNames:['cc::Picture','cc::LayeredPicture'],showInstances:false});return{PictureSnapshotView,};});'use strict';tr.exportTo('tr.e.cc',function(){var knownRasterTaskNames=['TileManager::RunRasterTask','RasterWorkerPoolTaskImpl::RunRasterOnThread','RasterWorkerPoolTaskImpl::Raster','RasterTaskImpl::Raster','cc::RasterTask','RasterTask'];var knownAnalysisTaskNames=['TileManager::RunAnalyzeTask','RasterWorkerPoolTaskImpl::RunAnalysisOnThread','RasterWorkerPoolTaskImpl::Analyze','RasterTaskImpl::Analyze','cc::AnalyzeTask','AnalyzeTask'];function getTileFromRasterTaskSlice(slice){if(!(isSliceDoingRasterization(slice)||isSliceDoingAnalysis(slice))){return undefined;}
+var tileData;if(slice.args.data){tileData=slice.args.data;}else{tileData=slice.args.tileData;}
+if(tileData===undefined)return undefined;if(tileData.tile_id)return tileData.tile_id;var tile=tileData.tileId;if(!(tile instanceof tr.e.cc.TileSnapshot)){return undefined;}
+return tileData.tileId;}
+function isSliceDoingRasterization(slice){return knownRasterTaskNames.includes(slice.title);}
+function isSliceDoingAnalysis(slice){return knownAnalysisTaskNames.includes(slice.title);}
+return{getTileFromRasterTaskSlice:getTileFromRasterTaskSlice,isSliceDoingRasterization:isSliceDoingRasterization,isSliceDoingAnalysis:isSliceDoingAnalysis};});'use strict';tr.exportTo('tr.ui.analysis',function(){var AnalysisSubView={set tabLabel(label){Polymer.dom(this).setAttribute('tab-label',label);},get tabLabel(){return this.getAttribute('tab-label');},get requiresTallView(){return false;},get relatedEventsToHighlight(){return undefined;},set selection(selection){throw new Error('Not implemented!');},get selection(){throw new Error('Not implemented!');}};var allTypeInfosByEventProto=new Map();var onlyRootTypeInfosByEventProto=undefined;var eventProtoToRootTypeInfoMap=undefined;function AnalysisSubViewTypeInfo(eventConstructor,options){if(options.multi===undefined){throw new Error('missing field: multi');}
+if(options.title===undefined){throw new Error('missing field: title');}
+this.eventConstructor=eventConstructor;this.singleTagName=undefined;this.singleTitle=undefined;this.multiTagName=undefined;this.multiTitle=undefined;this.childrenTypeInfos_=undefined;}
+AnalysisSubViewTypeInfo.prototype={get childrenTypeInfos(){return this.childrenTypeInfos_;},resetchildrenTypeInfos:function(){this.childrenTypeInfos_=[];}};AnalysisSubView.register=function(tagName,eventConstructor,options){var typeInfo=allTypeInfosByEventProto.get(eventConstructor.prototype);if(typeInfo===undefined){typeInfo=new AnalysisSubViewTypeInfo(eventConstructor,options);allTypeInfosByEventProto.set(typeInfo.eventConstructor.prototype,typeInfo);onlyRootTypeInfosByEventProto=undefined;}
+if(!options.multi){if(typeInfo.singleTagName!==undefined){throw new Error('SingleTagName already set');}
+typeInfo.singleTagName=tagName;typeInfo.singleTitle=options.title;}else{if(typeInfo.multiTagName!==undefined){throw new Error('MultiTagName already set');}
+typeInfo.multiTagName=tagName;typeInfo.multiTitle=options.title;}
 return typeInfo;};function rebuildRootSubViewTypeInfos(){onlyRootTypeInfosByEventProto=new Map();allTypeInfosByEventProto.forEach(function(typeInfo){typeInfo.resetchildrenTypeInfos();});allTypeInfosByEventProto.forEach(function(typeInfo,eventProto){var eventPrototype=typeInfo.eventConstructor.prototype;var lastEventProto=eventPrototype;var curEventProto=eventPrototype.__proto__;while(true){if(!allTypeInfosByEventProto.has(curEventProto)){var rootTypeInfo=allTypeInfosByEventProto.get(lastEventProto);var rootEventProto=lastEventProto;var isNew=onlyRootTypeInfosByEventProto.has(rootEventProto);onlyRootTypeInfosByEventProto.set(rootEventProto,rootTypeInfo);break;}
-lastEventProto=curEventProto;curEventProto=curEventProto.__proto__;}});allTypeInfosByEventProto.forEach(function(typeInfo,eventProto){var eventPrototype=typeInfo.eventConstructor.prototype;var parentEventProto=eventPrototype.__proto__;var parentTypeInfo=allTypeInfosByEventProto.get(parentEventProto);if(!parentTypeInfo)
-return;parentTypeInfo.childrenTypeInfos.push(typeInfo);});eventProtoToRootTypeInfoMap=new Map();allTypeInfosByEventProto.forEach(function(typeInfo,eventProto){var eventPrototype=typeInfo.eventConstructor.prototype;var curEventProto=eventPrototype;while(true){if(onlyRootTypeInfosByEventProto.has(curEventProto)){var rootTypeInfo=onlyRootTypeInfosByEventProto.get(curEventProto);eventProtoToRootTypeInfoMap.set(eventPrototype,rootTypeInfo);break;}
+lastEventProto=curEventProto;curEventProto=curEventProto.__proto__;}});allTypeInfosByEventProto.forEach(function(typeInfo,eventProto){var eventPrototype=typeInfo.eventConstructor.prototype;var parentEventProto=eventPrototype.__proto__;var parentTypeInfo=allTypeInfosByEventProto.get(parentEventProto);if(!parentTypeInfo)return;parentTypeInfo.childrenTypeInfos.push(typeInfo);});eventProtoToRootTypeInfoMap=new Map();allTypeInfosByEventProto.forEach(function(typeInfo,eventProto){var eventPrototype=typeInfo.eventConstructor.prototype;var curEventProto=eventPrototype;while(true){if(onlyRootTypeInfosByEventProto.has(curEventProto)){var rootTypeInfo=onlyRootTypeInfosByEventProto.get(curEventProto);eventProtoToRootTypeInfoMap.set(eventPrototype,rootTypeInfo);break;}
 curEventProto=curEventProto.__proto__;}});}
-function findLowestTypeInfoForEvents(thisTypeInfo,events){if(events.length===0)
-return thisTypeInfo;var event0=tr.b.getFirstElement(events);var candidateSubTypeInfo;for(var i=0;i<thisTypeInfo.childrenTypeInfos.length;i++){var childTypeInfo=thisTypeInfo.childrenTypeInfos[i];if(event0 instanceof childTypeInfo.eventConstructor){candidateSubTypeInfo=childTypeInfo;break;}}
-if(!candidateSubTypeInfo)
-return thisTypeInfo;var allMatch=true;for(var event of events){if(event instanceof candidateSubTypeInfo.eventConstructor)
-continue;allMatch=false;break;}
+function findLowestTypeInfoForEvents(thisTypeInfo,events){if(events.length===0)return thisTypeInfo;var event0=tr.b.getFirstElement(events);var candidateSubTypeInfo;for(var i=0;i<thisTypeInfo.childrenTypeInfos.length;i++){var childTypeInfo=thisTypeInfo.childrenTypeInfos[i];if(event0 instanceof childTypeInfo.eventConstructor){candidateSubTypeInfo=childTypeInfo;break;}}
+if(!candidateSubTypeInfo)return thisTypeInfo;var allMatch=true;for(var event of events){if(event instanceof candidateSubTypeInfo.eventConstructor)continue;allMatch=false;break;}
 if(!allMatch){return thisTypeInfo;}
 return findLowestTypeInfoForEvents(candidateSubTypeInfo,events);}
-var primaryEventProtoToTypeInfoMap=new Map();function getRootTypeInfoForEvent(event){var curProto=event.__proto__;var typeInfo=primaryEventProtoToTypeInfoMap.get(curProto);if(typeInfo)
-return typeInfo;return getRootTypeInfoForEventSlow(event);}
-function getRootTypeInfoForEventSlow(event){var typeInfo;var curProto=event.__proto__;while(true){if(curProto===Object.prototype)
-throw new Error('No view registered for '+event.toString());typeInfo=onlyRootTypeInfosByEventProto.get(curProto);if(typeInfo){primaryEventProtoToTypeInfoMap.set(event.__proto__,typeInfo);return typeInfo;}
+var primaryEventProtoToTypeInfoMap=new Map();function getRootTypeInfoForEvent(event){var curProto=event.__proto__;var typeInfo=primaryEventProtoToTypeInfoMap.get(curProto);if(typeInfo)return typeInfo;return getRootTypeInfoForEventSlow(event);}
+function getRootTypeInfoForEventSlow(event){var typeInfo;var curProto=event.__proto__;while(true){if(curProto===Object.prototype){throw new Error('No view registered for '+event.toString());}
+typeInfo=onlyRootTypeInfosByEventProto.get(curProto);if(typeInfo){primaryEventProtoToTypeInfoMap.set(event.__proto__,typeInfo);return typeInfo;}
 curProto=curProto.__proto__;}}
-AnalysisSubView.getEventsOrganizedByTypeInfo=function(selection){if(onlyRootTypeInfosByEventProto===undefined)
-rebuildRootSubViewTypeInfos();var eventsByRootTypeInfo=tr.b.groupIntoMap(selection,function(event){return getRootTypeInfoForEvent(event);},this,tr.model.EventSet);var eventsByLowestTypeInfo=new Map();eventsByRootTypeInfo.forEach(function(events,typeInfo){var lowestTypeInfo=findLowestTypeInfoForEvents(typeInfo,events);eventsByLowestTypeInfo.set(lowestTypeInfo,events);});return eventsByLowestTypeInfo;};return{AnalysisSubView:AnalysisSubView,AnalysisSubViewTypeInfo:AnalysisSubViewTypeInfo};});Polymer({is:'tr-ui-a-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView]});'use strict';Polymer({is:'tr-ui-a-stack-frame',ready:function(){this.stackFrame_=undefined;this.$.table.tableColumns=[];this.$.table.showHeader=true;},get stackFrame(){return this.stackFrame_;},set stackFrame(stackFrame){var table=this.$.table;this.stackFrame_=stackFrame;if(stackFrame===undefined){table.tableColumns=[];table.tableRows=[];table.rebuild();return;}
+AnalysisSubView.getEventsOrganizedByTypeInfo=function(selection){if(onlyRootTypeInfosByEventProto===undefined){rebuildRootSubViewTypeInfos();}
+var eventsByRootTypeInfo=tr.b.groupIntoMap(selection,function(event){return getRootTypeInfoForEvent(event);},this,tr.model.EventSet);var eventsByLowestTypeInfo=new Map();eventsByRootTypeInfo.forEach(function(events,typeInfo){var lowestTypeInfo=findLowestTypeInfoForEvents(typeInfo,events);eventsByLowestTypeInfo.set(lowestTypeInfo,events);});return eventsByLowestTypeInfo;};return{AnalysisSubView,AnalysisSubViewTypeInfo,};});Polymer({is:'tr-ui-a-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView]});'use strict';Polymer({is:'tr-ui-a-stack-frame',ready:function(){this.stackFrame_=undefined;this.$.table.tableColumns=[];this.$.table.showHeader=true;},get stackFrame(){return this.stackFrame_;},set stackFrame(stackFrame){var table=this.$.table;this.stackFrame_=stackFrame;if(stackFrame===undefined){table.tableColumns=[];table.tableRows=[];table.rebuild();return;}
 var hasName=false;var hasTitle=false;table.tableRows=stackFrame.stackTrace;table.tableRows.forEach(function(row){hasName|=row.name!==undefined;hasTitle|=row.title!==undefined;});var cols=[];if(hasName){cols.push({title:'Name',value:function(row){return row.name;}});}
 if(hasTitle){cols.push({title:'Title',value:function(row){return row.title;}});}
-table.tableColumns=cols;table.rebuild();},tableForTesting:function(){return this.$.table;}});'use strict';Polymer({is:'tr-ui-a-single-event-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],properties:{isFlow:{type:Boolean,value:false}},ready:function(){this.currentSelection_=undefined;this.$.table.tableColumns=[{title:'Label',value:function(row){return row.name;},width:'150px'},{title:'Value',width:'100%',value:function(row){return row.value;}}];this.$.table.showHeader=false;},get selection(){return this.currentSelection_;},set selection(selection){if(selection.length!==1)
-throw new Error('Only supports single slices');this.setSelectionWithoutErrorChecks(selection);},setSelectionWithoutErrorChecks:function(selection){this.currentSelection_=selection;this.updateContents_();},getFlowEventRows_:function(event){var rows=this.getEventRowsHelper_(event);rows.splice(0,0,{name:'ID',value:event.id});function createLinkTo(slice){var linkEl=document.createElement('tr-ui-a-analysis-link');linkEl.setSelectionAndContent(function(){return new tr.model.EventSet(slice);});Polymer.dom(linkEl).textContent=slice.userFriendlyName;return linkEl;}
-rows.push({name:'From',value:createLinkTo(event.startSlice)});rows.push({name:'To',value:createLinkTo(event.endSlice)});return rows;},getEventRowsHelper_:function(event){var rows=[];if(event.error)
-rows.push({name:'Error',value:event.error});if(event.title)
-rows.push({name:'Title',value:event.title});if(event.category)
-rows.push({name:'Category',value:event.category});if(event.model!==undefined){var ufc=event.model.getUserFriendlyCategoryFromEvent(event);if(ufc!==undefined)
-rows.push({name:'User Friendly Category',value:ufc});}
-if(event.name)
-rows.push({name:'Name',value:event.name});rows.push({name:'Start',value:tr.v.ui.createScalarSpan(event.start,{unit:tr.b.Unit.byName.timeStampInMs})});if(event.duration){rows.push({name:'Wall Duration',value:tr.v.ui.createScalarSpan(event.duration,{unit:tr.b.Unit.byName.timeDurationInMs})});}
+table.tableColumns=cols;table.rebuild();},tableForTesting:function(){return this.$.table;}});'use strict';Polymer({is:'tr-ui-a-single-event-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],properties:{isFlow:{type:Boolean,value:false}},ready:function(){this.currentSelection_=undefined;this.$.table.tableColumns=[{title:'Label',value:function(row){return row.name;},width:'150px'},{title:'Value',width:'100%',value:function(row){return row.value;}}];this.$.table.showHeader=false;},get selection(){return this.currentSelection_;},set selection(selection){if(selection.length!==1){throw new Error('Only supports single slices');}
+this.setSelectionWithoutErrorChecks(selection);},setSelectionWithoutErrorChecks:function(selection){this.currentSelection_=selection;this.updateContents_();},getFlowEventRows_:function(event){var rows=this.getEventRowsHelper_(event);rows.splice(0,0,{name:'ID',value:event.id});function createLinkTo(slice){var linkEl=document.createElement('tr-ui-a-analysis-link');linkEl.setSelectionAndContent(function(){return new tr.model.EventSet(slice);});Polymer.dom(linkEl).textContent=slice.userFriendlyName;return linkEl;}
+rows.push({name:'From',value:createLinkTo(event.startSlice)});rows.push({name:'To',value:createLinkTo(event.endSlice)});return rows;},getEventRowsHelper_:function(event){var rows=[];if(event.error){rows.push({name:'Error',value:event.error});}
+if(event.title){rows.push({name:'Title',value:event.title});}
+if(event.category){rows.push({name:'Category',value:event.category});}
+if(event.model!==undefined){var ufc=event.model.getUserFriendlyCategoryFromEvent(event);if(ufc!==undefined){rows.push({name:'User Friendly Category',value:ufc});}}
+if(event.name){rows.push({name:'Name',value:event.name});}
+rows.push({name:'Start',value:tr.v.ui.createScalarSpan(event.start,{unit:tr.b.Unit.byName.timeStampInMs})});if(event.duration){rows.push({name:'Wall Duration',value:tr.v.ui.createScalarSpan(event.duration,{unit:tr.b.Unit.byName.timeDurationInMs})});}
 if(event.cpuDuration){rows.push({name:'CPU Duration',value:tr.v.ui.createScalarSpan(event.cpuDuration,{unit:tr.b.Unit.byName.timeDurationInMs})});}
 if(event.subSlices!==undefined&&event.subSlices.length!==0){if(event.selfTime){rows.push({name:'Self Time',value:tr.v.ui.createScalarSpan(event.selfTime,{unit:tr.b.Unit.byName.timeDurationInMs})});}
 if(event.cpuSelfTime){var cpuSelfTimeEl=tr.v.ui.createScalarSpan(event.cpuSelfTime,{unit:tr.b.Unit.byName.timeDurationInMs});if(event.cpuSelfTime>event.selfTime){cpuSelfTimeEl.warning=' Note that CPU Self Time is larger than Self Time. '+'This is a known limitation of this system, which occurs '+'due to several subslices, rounding issues, and imprecise '+'time at which we get cpu- and real-time.';}
@@ -7485,516 +7398,593 @@
 if(event.startStackFrame&&event.endStackFrame){if(event.startStackFrame===event.endStackFrame){rows.push({name:'Start+End Stack Trace',value:createStackFrameEl(event.startStackFrame)});}else{rows.push({name:'Start Stack Trace',value:createStackFrameEl(event.startStackFrame)});rows.push({name:'End Stack Trace',value:createStackFrameEl(event.endStackFrame)});}}else if(event.startStackFrame){rows.push({name:'Start Stack Trace',value:createStackFrameEl(event.startStackFrame)});}else if(event.endStackFrame){rows.push({name:'End Stack Trace',value:createStackFrameEl(event.endStackFrame)});}
 if(event.info){var descriptionEl=tr.ui.b.createDiv({textContent:event.info.description,maxWidth:'300px'});rows.push({name:'Description',value:descriptionEl});if(event.info.docLinks){event.info.docLinks.forEach(function(linkObject){var linkEl=document.createElement('a');linkEl.target='_blank';linkEl.href=linkObject.href;Polymer.dom(linkEl).textContent=Polymer.dom(linkObject).textContent;rows.push({name:linkObject.label,value:linkEl});});}}
 if(event.associatedAlerts.length){var alertSubRows=[];event.associatedAlerts.forEach(function(alert){var linkEl=document.createElement('tr-ui-a-analysis-link');linkEl.setSelectionAndContent(function(){return new tr.model.EventSet(alert);},alert.info.description);alertSubRows.push({name:alert.title,value:linkEl});});rows.push({name:'Alerts',value:'',isExpanded:true,subRows:alertSubRows});}
-return rows;},getEventRows_:function(event){if(this.isFlow)
-return this.getFlowEventRows_(event);return this.getEventRowsHelper_(event);},addArgsToRows_:function(rows,args){var n=0;for(var argName in args){n+=1;}
+return rows;},getEventRows_:function(event){if(this.isFlow){return this.getFlowEventRows_(event);}
+return this.getEventRowsHelper_(event);},addArgsToRows_:function(rows,args){var n=0;for(var argName in args){n+=1;}
 if(n>0){var subRows=[];for(var argName in args){n+=1;}
 if(n>0){var subRows=[];for(var argName in args){var argView=document.createElement('tr-ui-a-generic-object-view');argView.object=args[argName];subRows.push({name:argName,value:argView});}
 rows.push({name:'Args',value:'',isExpanded:true,subRows:subRows});}}},addContextsToRows_:function(rows,contexts){if(contexts.length){var subRows=contexts.map(function(context){var contextView=document.createElement('tr-ui-a-generic-object-view');contextView.object=context;return{name:'Context',value:contextView};});rows.push({name:'Contexts',value:'',isExpanded:true,subRows:subRows});}},updateContents_:function(){if(this.currentSelection_===undefined){this.$.table.rows=[];this.$.table.rebuild();return;}
-var event=tr.b.getOnlyElement(this.currentSelection_);var rows=this.getEventRows_(event);if(event.argsStripped)
-rows.push({name:'Args',value:'Stripped'});else
-this.addArgsToRows_(rows,event.args);this.addContextsToRows_(rows,event.contexts);var customizeRowsEvent=new tr.b.Event('customize-rows');customizeRowsEvent.rows=rows;this.dispatchEvent(customizeRowsEvent);this.$.table.tableRows=rows;this.$.table.rebuild();}});'use strict';Polymer({is:'tr-ui-e-chrome-cc-raster-task-view',created:function(){this.selection_=undefined;},set selection(selection){this.selection_=selection;this.updateContents_();},updateColumns_:function(hadCpuDurations){var timeSpanConfig={unit:tr.b.Unit.byName.timeDurationInMs,ownerDocument:this.ownerDocument};var columns=[{title:'Layer',value:function(row){if(row.isTotals)
-return'Totals';if(row.layer){var linkEl=document.createElement('tr-ui-a-analysis-link');linkEl.setSelectionAndContent(function(){return new tr.ui.e.chrome.cc.LayerSelection(costs.layer);},'Layer '+row.layerId);return linkEl;}else{return'Layer '+row.layerId;}},width:'250px'},{title:'Num Tiles',value:function(row){return row.numTiles;},cmp:function(a,b){return a.numTiles-b.numTiles;}},{title:'Num Analysis Tasks',value:function(row){return row.numAnalysisTasks;},cmp:function(a,b){return a.numAnalysisTasks-b.numAnalysisTasks;}},{title:'Num Raster Tasks',value:function(row){return row.numRasterTasks;},cmp:function(a,b){return a.numRasterTasks-b.numRasterTasks;}},{title:'Wall Duration (ms)',value:function(row){return tr.v.ui.createScalarSpan(row.duration,timeSpanConfig);},cmp:function(a,b){return a.duration-b.duration;}}];if(hadCpuDurations){columns.push({title:'CPU Duration (ms)',value:function(row){return tr.v.ui.createScalarSpan(row.cpuDuration,timeSpanConfig);},cmp:function(a,b){return a.cpuDuration-b.cpuDuration;}});}
-var colWidthPercentage;if(columns.length==1)
-colWidthPercentage='100%';else
-colWidthPercentage=(100/(columns.length-1)).toFixed(3)+'%';for(var i=1;i<columns.length;i++)
-columns[i].width=colWidthPercentage;this.$.content.tableColumns=columns;this.$.content.sortColumnIndex=columns.length-1;},updateContents_:function(){var table=this.$.content;if(this.selection_.length===0){this.$.link.setSelectionAndContent(undefined,'');table.tableRows=[];table.footerRows=[];table.rebuild();return;}
-var lthi=tr.e.cc.getTileFromRasterTaskSlice(tr.b.getFirstElement(this.selection_)).containingSnapshot;this.$.link.setSelectionAndContent(function(){return new tr.model.EventSet(lthi);},lthi.userFriendlyName);var costsByLayerId={};function getCurrentCostsForLayerId(tile){var layerId=tile.layerId;var lthi=tile.containingSnapshot;var layer;if(lthi.activeTree)
-layer=lthi.activeTree.findLayerWithId(layerId);if(layer===undefined&&lthi.pendingTree)
-layer=lthi.pendingTree.findLayerWithId(layerId);if(costsByLayerId[layerId]===undefined){costsByLayerId[layerId]={layerId:layerId,layer:layer,numTiles:0,numAnalysisTasks:0,numRasterTasks:0,duration:0,cpuDuration:0};}
+var event=tr.b.getOnlyElement(this.currentSelection_);var rows=this.getEventRows_(event);if(event.argsStripped){rows.push({name:'Args',value:'Stripped'});}else{this.addArgsToRows_(rows,event.args);}
+this.addContextsToRows_(rows,event.contexts);var customizeRowsEvent=new tr.b.Event('customize-rows');customizeRowsEvent.rows=rows;this.dispatchEvent(customizeRowsEvent);this.$.table.tableRows=rows;this.$.table.rebuild();}});'use strict';Polymer({is:'tr-ui-e-chrome-cc-raster-task-view',created:function(){this.selection_=undefined;},set selection(selection){this.selection_=selection;this.updateContents_();},updateColumns_:function(hadCpuDurations){var timeSpanConfig={unit:tr.b.Unit.byName.timeDurationInMs,ownerDocument:this.ownerDocument};var columns=[{title:'Layer',value:function(row){if(row.isTotals)return'Totals';if(row.layer){var linkEl=document.createElement('tr-ui-a-analysis-link');linkEl.setSelectionAndContent(function(){return new tr.ui.e.chrome.cc.LayerSelection(costs.layer);},'Layer '+row.layerId);return linkEl;}
+return'Layer '+row.layerId;},width:'250px'},{title:'Num Tiles',value:function(row){return row.numTiles;},cmp:function(a,b){return a.numTiles-b.numTiles;}},{title:'Num Analysis Tasks',value:function(row){return row.numAnalysisTasks;},cmp:function(a,b){return a.numAnalysisTasks-b.numAnalysisTasks;}},{title:'Num Raster Tasks',value:function(row){return row.numRasterTasks;},cmp:function(a,b){return a.numRasterTasks-b.numRasterTasks;}},{title:'Wall Duration (ms)',value:function(row){return tr.v.ui.createScalarSpan(row.duration,timeSpanConfig);},cmp:function(a,b){return a.duration-b.duration;}}];if(hadCpuDurations){columns.push({title:'CPU Duration (ms)',value:function(row){return tr.v.ui.createScalarSpan(row.cpuDuration,timeSpanConfig);},cmp:function(a,b){return a.cpuDuration-b.cpuDuration;}});}
+var colWidthPercentage;if(columns.length===1){colWidthPercentage='100%';}else{colWidthPercentage=(100/(columns.length-1)).toFixed(3)+'%';}
+for(var i=1;i<columns.length;i++){columns[i].width=colWidthPercentage;}
+this.$.content.tableColumns=columns;this.$.content.sortColumnIndex=columns.length-1;},updateContents_:function(){var table=this.$.content;if(this.selection_.length===0){this.$.link.setSelectionAndContent(undefined,'');table.tableRows=[];table.footerRows=[];table.rebuild();return;}
+var lthi=tr.e.cc.getTileFromRasterTaskSlice(tr.b.getFirstElement(this.selection_)).containingSnapshot;this.$.link.setSelectionAndContent(function(){return new tr.model.EventSet(lthi);},lthi.userFriendlyName);var costsByLayerId={};function getCurrentCostsForLayerId(tile){var layerId=tile.layerId;var lthi=tile.containingSnapshot;var layer;if(lthi.activeTree){layer=lthi.activeTree.findLayerWithId(layerId);}
+if(layer===undefined&&lthi.pendingTree){layer=lthi.pendingTree.findLayerWithId(layerId);}
+if(costsByLayerId[layerId]===undefined){costsByLayerId[layerId]={layerId:layerId,layer:layer,numTiles:0,numAnalysisTasks:0,numRasterTasks:0,duration:0,cpuDuration:0};}
 return costsByLayerId[layerId];}
 var totalDuration=0;var totalCpuDuration=0;var totalNumAnalyzeTasks=0;var totalNumRasterizeTasks=0;var hadCpuDurations=false;var tilesThatWeHaveSeen={};this.selection_.forEach(function(slice){var tile=tr.e.cc.getTileFromRasterTaskSlice(slice);var curCosts=getCurrentCostsForLayerId(tile);if(!tilesThatWeHaveSeen[tile.objectInstance.id]){tilesThatWeHaveSeen[tile.objectInstance.id]=true;curCosts.numTiles+=1;}
 if(tr.e.cc.isSliceDoingAnalysis(slice)){curCosts.numAnalysisTasks+=1;totalNumAnalyzeTasks+=1;}else{curCosts.numRasterTasks+=1;totalNumRasterizeTasks+=1;}
-curCosts.duration+=slice.duration;totalDuration+=slice.duration;if(slice.cpuDuration!==undefined){curCosts.cpuDuration+=slice.cpuDuration;totalCpuDuration+=slice.cpuDuration;hadCpuDurations=true;}});this.updateColumns_(hadCpuDurations);table.tableRows=tr.b.dictionaryValues(costsByLayerId);table.rebuild();table.footerRows=[{isTotals:true,numTiles:tr.b.dictionaryLength(tilesThatWeHaveSeen),numAnalysisTasks:totalNumAnalyzeTasks,numRasterTasks:totalNumRasterizeTasks,duration:totalDuration,cpuDuration:totalCpuDuration}];}});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){function RasterTaskSelection(selection){tr.ui.e.chrome.cc.Selection.call(this);var whySupported=RasterTaskSelection.whySuported(selection);if(!whySupported.ok)
-throw new Error('Fail: '+whySupported.why);this.slices_=tr.b.asArray(selection);this.tiles_=this.slices_.map(function(slice){var tile=tr.e.cc.getTileFromRasterTaskSlice(slice);if(tile===undefined)
-throw new Error('This should never happen due to .supports check.');return tile;});}
-RasterTaskSelection.whySuported=function(selection){if(!(selection instanceof tr.model.EventSet))
-return{ok:false,why:'Must be selection'};if(selection.length===0)
-return{ok:false,why:'Selection must be non empty'};var referenceSnapshot=undefined;for(var event of selection){if(!(event instanceof tr.model.Slice))
-return{ok:false,why:'Not a slice'};var tile=tr.e.cc.getTileFromRasterTaskSlice(event);if(tile===undefined)
-return{ok:false,why:'No tile found'};if(!referenceSnapshot){referenceSnapshot=tile.containingSnapshot;}else{if(tile.containingSnapshot!=referenceSnapshot){return{ok:false,why:'Raster tasks are from different compositor instances'};}}}
-return{ok:true};};RasterTaskSelection.supports=function(selection){return RasterTaskSelection.whySuported(selection).ok;};RasterTaskSelection.prototype={__proto__:tr.ui.e.chrome.cc.Selection.prototype,get specicifity(){return 3;},get associatedLayerId(){var tile0=this.tiles_[0];var allSameLayer=this.tiles_.every(function(tile){tile.layerId==tile0.layerId;});if(allSameLayer)
-return tile0.layerId;return undefined;},get extraHighlightsByLayerId(){var highlights={};this.tiles_.forEach(function(tile,i){if(highlights[tile.layerId]===undefined)
-highlights[tile.layerId]=[];var slice=this.slices_[i];highlights[tile.layerId].push({colorKey:slice.title,rect:tile.layerRect});},this);return highlights;},createAnalysis:function(){var sel=new tr.model.EventSet();this.slices_.forEach(function(slice){sel.push(slice);});var analysis;if(sel.length==1)
-analysis=document.createElement('tr-ui-a-single-event-sub-view');else
-analysis=document.createElement('tr-ui-e-chrome-cc-raster-task-view');analysis.selection=sel;return analysis;},findEquivalent:function(lthi){return undefined;},get containingSnapshot(){return this.tiles_[0].containingSnapshot;}};return{RasterTaskSelection:RasterTaskSelection};});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){var TileSnapshotView=tr.ui.b.define('tr-ui-e-chrome-cc-tile-snapshot-view',tr.ui.analysis.ObjectSnapshotView);TileSnapshotView.prototype={__proto__:tr.ui.analysis.ObjectSnapshotView.prototype,decorate:function(){Polymer.dom(this).classList.add('tr-ui-e-chrome-cc-tile-snapshot-view');this.layerTreeView_=new tr.ui.e.chrome.cc.LayerTreeHostImplSnapshotView();Polymer.dom(this).appendChild(this.layerTreeView_);},updateContents:function(){var tile=this.objectSnapshot_;var layerTreeHostImpl=tile.containingSnapshot;if(!layerTreeHostImpl)
-return;this.layerTreeView_.objectSnapshot=layerTreeHostImpl;this.layerTreeView_.selection=new tr.ui.e.chrome.cc.TileSelection(tile);}};tr.ui.analysis.ObjectSnapshotView.register(TileSnapshotView,{typeName:'cc::Tile',showInTrackView:false});return{TileSnapshotView:TileSnapshotView};});'use strict';tr.exportTo('tr.e.gpu',function(){var ObjectSnapshot=tr.model.ObjectSnapshot;function StateSnapshot(){ObjectSnapshot.apply(this,arguments);}
-StateSnapshot.prototype={__proto__:ObjectSnapshot.prototype,preInitialize:function(){this.screenshot_=undefined;},initialize:function(){if(this.args.screenshot)
-this.screenshot_=this.args.screenshot;},get screenshot(){return this.screenshot_;}};ObjectSnapshot.subTypes.register(StateSnapshot,{typeName:'gpu::State'});return{StateSnapshot:StateSnapshot};});'use strict';tr.exportTo('tr.e.gpu',function(){var AsyncSlice=tr.model.AsyncSlice;function GpuAsyncSlice(){AsyncSlice.apply(this,arguments);}
-GpuAsyncSlice.prototype={__proto__:AsyncSlice.prototype,get viewSubGroupTitle(){if(this.args.channel){if(this.category=='disabled-by-default-gpu.device')
-return'Device.'+this.args.channel;else
+curCosts.duration+=slice.duration;totalDuration+=slice.duration;if(slice.cpuDuration!==undefined){curCosts.cpuDuration+=slice.cpuDuration;totalCpuDuration+=slice.cpuDuration;hadCpuDurations=true;}});this.updateColumns_(hadCpuDurations);table.tableRows=tr.b.dictionaryValues(costsByLayerId);table.rebuild();table.footerRows=[{isTotals:true,numTiles:tr.b.dictionaryLength(tilesThatWeHaveSeen),numAnalysisTasks:totalNumAnalyzeTasks,numRasterTasks:totalNumRasterizeTasks,duration:totalDuration,cpuDuration:totalCpuDuration}];}});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){function RasterTaskSelection(selection){tr.ui.e.chrome.cc.Selection.call(this);var whySupported=RasterTaskSelection.whySuported(selection);if(!whySupported.ok){throw new Error('Fail: '+whySupported.why);}
+this.slices_=tr.b.asArray(selection);this.tiles_=this.slices_.map(function(slice){var tile=tr.e.cc.getTileFromRasterTaskSlice(slice);if(tile===undefined){throw new Error('This should never happen due to .supports check.');}
+return tile;});}
+RasterTaskSelection.whySuported=function(selection){if(!(selection instanceof tr.model.EventSet)){return{ok:false,why:'Must be selection'};}
+if(selection.length===0){return{ok:false,why:'Selection must be non empty'};}
+var referenceSnapshot=undefined;for(var event of selection){if(!(event instanceof tr.model.Slice)){return{ok:false,why:'Not a slice'};}
+var tile=tr.e.cc.getTileFromRasterTaskSlice(event);if(tile===undefined){return{ok:false,why:'No tile found'};}
+if(!referenceSnapshot){referenceSnapshot=tile.containingSnapshot;}else{if(tile.containingSnapshot!==referenceSnapshot){return{ok:false,why:'Raster tasks are from different compositor instances'};}}}
+return{ok:true};};RasterTaskSelection.supports=function(selection){return RasterTaskSelection.whySuported(selection).ok;};RasterTaskSelection.prototype={__proto__:tr.ui.e.chrome.cc.Selection.prototype,get specicifity(){return 3;},get associatedLayerId(){var tile0=this.tiles_[0];var allSameLayer=this.tiles_.every(function(tile){tile.layerId===tile0.layerId;});if(allSameLayer){return tile0.layerId;}
+return undefined;},get extraHighlightsByLayerId(){var highlights={};this.tiles_.forEach(function(tile,i){if(highlights[tile.layerId]===undefined){highlights[tile.layerId]=[];}
+var slice=this.slices_[i];highlights[tile.layerId].push({colorKey:slice.title,rect:tile.layerRect});},this);return highlights;},createAnalysis:function(){var sel=new tr.model.EventSet();this.slices_.forEach(function(slice){sel.push(slice);});var analysis;if(sel.length===1){analysis=document.createElement('tr-ui-a-single-event-sub-view');}else{analysis=document.createElement('tr-ui-e-chrome-cc-raster-task-view');}
+analysis.selection=sel;return analysis;},findEquivalent:function(lthi){return undefined;},get containingSnapshot(){return this.tiles_[0].containingSnapshot;}};return{RasterTaskSelection,};});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){var TileSnapshotView=tr.ui.b.define('tr-ui-e-chrome-cc-tile-snapshot-view',tr.ui.analysis.ObjectSnapshotView);TileSnapshotView.prototype={__proto__:tr.ui.analysis.ObjectSnapshotView.prototype,decorate:function(){Polymer.dom(this).classList.add('tr-ui-e-chrome-cc-tile-snapshot-view');this.layerTreeView_=new tr.ui.e.chrome.cc.LayerTreeHostImplSnapshotView();Polymer.dom(this).appendChild(this.layerTreeView_);},updateContents:function(){var tile=this.objectSnapshot_;var layerTreeHostImpl=tile.containingSnapshot;if(!layerTreeHostImpl)return;this.layerTreeView_.objectSnapshot=layerTreeHostImpl;this.layerTreeView_.selection=new tr.ui.e.chrome.cc.TileSelection(tile);}};tr.ui.analysis.ObjectSnapshotView.register(TileSnapshotView,{typeName:'cc::Tile',showInTrackView:false});return{TileSnapshotView,};});'use strict';tr.exportTo('tr.e.gpu',function(){var AsyncSlice=tr.model.AsyncSlice;function GpuAsyncSlice(){AsyncSlice.apply(this,arguments);}
+GpuAsyncSlice.prototype={__proto__:AsyncSlice.prototype,get viewSubGroupTitle(){if(this.args.channel){if(this.category==='disabled-by-default-gpu.device'){return'Device.'+this.args.channel;}
 return'Service.'+this.args.channel;}
-return this.title;}};AsyncSlice.subTypes.register(GpuAsyncSlice,{categoryParts:['disabled-by-default-gpu.device','disabled-by-default-gpu.service']});return{GpuAsyncSlice:GpuAsyncSlice};});'use strict';tr.exportTo('tr.ui.e.chrome.gpu',function(){var StateSnapshotView=tr.ui.b.define('tr-ui-e-chrome-gpu-state-snapshot-view',tr.ui.analysis.ObjectSnapshotView);StateSnapshotView.prototype={__proto__:tr.ui.analysis.ObjectSnapshotView.prototype,decorate:function(){Polymer.dom(this).classList.add('tr-ui-e-chrome-gpu-state-snapshot-view');this.screenshotImage_=document.createElement('img');Polymer.dom(this).appendChild(this.screenshotImage_);},updateContents:function(){if(this.objectSnapshot_&&this.objectSnapshot_.screenshot){this.screenshotImage_.src='data:image/png;base64,'+
-this.objectSnapshot_.screenshot;}}};tr.ui.analysis.ObjectSnapshotView.register(StateSnapshotView,{typeName:'gpu::State'});return{StateSnapshotView:StateSnapshotView};});'use strict';tr.exportTo('tr.ui.analysis',function(){Polymer({is:'tr-ui-a-layout-tree-sub-view',behaviors:['tr-ui-a-sub-view'],set selection(selection){this.currentSelection_=selection;this.updateContents_();},get selection(){return this.currentSelection_;},updateContents_:function(){this.set('$.content.textContent','');if(!this.currentSelection_)
-return;var columns=[{title:'Tag/Name',value:function(layoutObject){return layoutObject.tag||':'+layoutObject.name;}},{title:'htmlId',value:function(layoutObject){return layoutObject.htmlId||'';}},{title:'classNames',value:function(layoutObject){return layoutObject.classNames||'';}},{title:'reasons',value:function(layoutObject){return layoutObject.needsLayoutReasons.join(', ');}},{title:'width',value:function(layoutObject){return layoutObject.absoluteRect.width;}},{title:'height',value:function(layoutObject){return layoutObject.absoluteRect.height;}},{title:'absX',value:function(layoutObject){return layoutObject.absoluteRect.left;}},{title:'absY',value:function(layoutObject){return layoutObject.absoluteRect.top;}},{title:'relX',value:function(layoutObject){return layoutObject.relativeRect.left;}},{title:'relY',value:function(layoutObject){return layoutObject.relativeRect.top;}},{title:'float',value:function(layoutObject){return layoutObject.isFloat?'float':'';}},{title:'positioned',value:function(layoutObject){return layoutObject.isPositioned?'positioned':'';}},{title:'relative',value:function(layoutObject){return layoutObject.isRelativePositioned?'relative':'';}},{title:'sticky',value:function(layoutObject){return layoutObject.isStickyPositioned?'sticky':'';}},{title:'anonymous',value:function(layoutObject){return layoutObject.isAnonymous?'anonymous':'';}},{title:'row',value:function(layoutObject){if(layoutObject.tableRow===undefined)
-return'';return layoutObject.tableRow;}},{title:'col',value:function(layoutObject){if(layoutObject.tableCol===undefined)
-return'';return layoutObject.tableCol;}},{title:'rowSpan',value:function(layoutObject){if(layoutObject.tableRowSpan===undefined)
-return'';return layoutObject.tableRowSpan;}},{title:'colSpan',value:function(layoutObject){if(layoutObject.tableColSpan===undefined)
-return'';return layoutObject.tableColSpan;}},{title:'address',value:function(layoutObject){return layoutObject.id.toString(16);}}];var table=this.ownerDocument.createElement('tr-ui-b-table');table.defaultExpansionStateCallback=function(layoutObject,parentLayoutObject){return true;};table.subRowsPropertyName='childLayoutObjects';table.tableColumns=columns;table.tableRows=this.currentSelection_.map(function(snapshot){return snapshot.rootLayoutObject;});table.rebuild();Polymer.dom(this.$.content).appendChild(table);},});return{};});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-layout-tree-sub-view',tr.e.chrome.LayoutTreeSnapshot,{multi:false,title:'Layout Tree',});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-layout-tree-sub-view',tr.e.chrome.LayoutTreeSnapshot,{multi:true,title:'Layout Trees',});'use strict';tr.exportTo('tr.ui.behaviors',function(){var SidePanel={get rangeOfInterest(){throw new Error('Not implemented');},set rangeOfInterest(rangeOfInterest){throw new Error('Not implemented');},get selection(){throw new Error('Not implemented');},set selection(selection){throw new Error('Not implemented');},get model(){throw new Error('Not implemented');},set model(model){throw new Error('Not implemented');},supportsModel:function(m){throw new Error('Not implemented');}};return{SidePanel:SidePanel};});'use strict';tr.exportTo('tr.ui.side_panel',function(){function SidePanelRegistry(){}
-var options=new tr.b.ExtensionRegistryOptions(tr.b.BASIC_REGISTRY_MODE);tr.b.decorateExtensionRegistry(SidePanelRegistry,options);return{SidePanelRegistry:SidePanelRegistry};});'use strict';tr.exportTo('tr.ui.e.s',function(){var BlameContextSnapshot=tr.e.chrome.BlameContextSnapshot;var FrameTreeNodeSnapshot=tr.e.chrome.FrameTreeNodeSnapshot;var RenderFrameSnapshot=tr.e.chrome.RenderFrameSnapshot;var TopLevelSnapshot=tr.e.chrome.TopLevelSnapshot;var BlameContextInstance=tr.e.chrome.BlameContextInstance;var FrameTreeNodeInstance=tr.e.chrome.FrameTreeNodeInstance;var RenderFrameInstance=tr.e.chrome.RenderFrameInstance;var TopLevelInstance=tr.e.chrome.TopLevelInstance;function Row(context){this.subRows=undefined;this.contexts=[];this.type=undefined;this.renderer='N/A';this.url=undefined;this.time=0;this.eventsOfInterest=new tr.model.EventSet();if(context===undefined)
-return;this.type=context.objectInstance.blameContextType;this.contexts.push(context);if(context instanceof FrameTreeNodeSnapshot){if(context.renderFrame){this.contexts.push(context.renderFrame);this.renderer=context.renderFrame.objectInstance.parent.pid;}}else if(context instanceof RenderFrameSnapshot){if(context.frameTreeNode)
-this.contexts.push(context.frameTreeNode);this.renderer=context.objectInstance.parent.pid;}else if(context instanceof TopLevelSnapshot){this.renderer=context.objectInstance.parent.pid;}else{throw new Error('Unknown context type');}
+return this.title;}};AsyncSlice.subTypes.register(GpuAsyncSlice,{categoryParts:['disabled-by-default-gpu.device','disabled-by-default-gpu.service']});return{GpuAsyncSlice,};});'use strict';tr.exportTo('tr.e.gpu',function(){var ObjectSnapshot=tr.model.ObjectSnapshot;function StateSnapshot(){ObjectSnapshot.apply(this,arguments);}
+StateSnapshot.prototype={__proto__:ObjectSnapshot.prototype,preInitialize:function(){this.screenshot_=undefined;},initialize:function(){if(this.args.screenshot){this.screenshot_=this.args.screenshot;}},get screenshot(){return this.screenshot_;}};ObjectSnapshot.subTypes.register(StateSnapshot,{typeName:'gpu::State'});return{StateSnapshot,};});'use strict';tr.exportTo('tr.ui.e.chrome.gpu',function(){var StateSnapshotView=tr.ui.b.define('tr-ui-e-chrome-gpu-state-snapshot-view',tr.ui.analysis.ObjectSnapshotView);StateSnapshotView.prototype={__proto__:tr.ui.analysis.ObjectSnapshotView.prototype,decorate:function(){Polymer.dom(this).classList.add('tr-ui-e-chrome-gpu-state-snapshot-view');this.screenshotImage_=document.createElement('img');Polymer.dom(this).appendChild(this.screenshotImage_);},updateContents:function(){if(this.objectSnapshot_&&this.objectSnapshot_.screenshot){this.screenshotImage_.src='data:image/png;base64,'+
+this.objectSnapshot_.screenshot;}}};tr.ui.analysis.ObjectSnapshotView.register(StateSnapshotView,{typeName:'gpu::State'});return{StateSnapshotView,};});'use strict';tr.exportTo('tr.ui.analysis',function(){Polymer({is:'tr-ui-a-layout-tree-sub-view',behaviors:['tr-ui-a-sub-view'],set selection(selection){this.currentSelection_=selection;this.updateContents_();},get selection(){return this.currentSelection_;},updateContents_:function(){this.set('$.content.textContent','');if(!this.currentSelection_)return;var columns=[{title:'Tag/Name',value:function(layoutObject){return layoutObject.tag||':'+layoutObject.name;}},{title:'htmlId',value:function(layoutObject){return layoutObject.htmlId||'';}},{title:'classNames',value:function(layoutObject){return layoutObject.classNames||'';}},{title:'reasons',value:function(layoutObject){return layoutObject.needsLayoutReasons.join(', ');}},{title:'width',value:function(layoutObject){return layoutObject.absoluteRect.width;}},{title:'height',value:function(layoutObject){return layoutObject.absoluteRect.height;}},{title:'absX',value:function(layoutObject){return layoutObject.absoluteRect.left;}},{title:'absY',value:function(layoutObject){return layoutObject.absoluteRect.top;}},{title:'relX',value:function(layoutObject){return layoutObject.relativeRect.left;}},{title:'relY',value:function(layoutObject){return layoutObject.relativeRect.top;}},{title:'float',value:function(layoutObject){return layoutObject.isFloat?'float':'';}},{title:'positioned',value:function(layoutObject){return layoutObject.isPositioned?'positioned':'';}},{title:'relative',value:function(layoutObject){return layoutObject.isRelativePositioned?'relative':'';}},{title:'sticky',value:function(layoutObject){return layoutObject.isStickyPositioned?'sticky':'';}},{title:'anonymous',value:function(layoutObject){return layoutObject.isAnonymous?'anonymous':'';}},{title:'row',value:function(layoutObject){if(layoutObject.tableRow===undefined){return'';}
+return layoutObject.tableRow;}},{title:'col',value:function(layoutObject){if(layoutObject.tableCol===undefined){return'';}
+return layoutObject.tableCol;}},{title:'rowSpan',value:function(layoutObject){if(layoutObject.tableRowSpan===undefined){return'';}
+return layoutObject.tableRowSpan;}},{title:'colSpan',value:function(layoutObject){if(layoutObject.tableColSpan===undefined){return'';}
+return layoutObject.tableColSpan;}},{title:'address',value:function(layoutObject){return layoutObject.id.toString(16);}}];var table=this.ownerDocument.createElement('tr-ui-b-table');table.defaultExpansionStateCallback=function(layoutObject,parentLayoutObject){return true;};table.subRowsPropertyName='childLayoutObjects';table.tableColumns=columns;table.tableRows=this.currentSelection_.map(function(snapshot){return snapshot.rootLayoutObject;});table.rebuild();Polymer.dom(this.$.content).appendChild(table);},});return{};});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-layout-tree-sub-view',tr.e.chrome.LayoutTreeSnapshot,{multi:false,title:'Layout Tree',});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-layout-tree-sub-view',tr.e.chrome.LayoutTreeSnapshot,{multi:true,title:'Layout Trees',});'use strict';tr.exportTo('tr.ui.behaviors',function(){var SidePanel={get rangeOfInterest(){throw new Error('Not implemented');},set rangeOfInterest(rangeOfInterest){throw new Error('Not implemented');},get selection(){throw new Error('Not implemented');},set selection(selection){throw new Error('Not implemented');},get model(){throw new Error('Not implemented');},set model(model){throw new Error('Not implemented');},supportsModel:function(m){throw new Error('Not implemented');}};return{SidePanel,};});'use strict';tr.exportTo('tr.ui.side_panel',function(){function SidePanelRegistry(){}
+var options=new tr.b.ExtensionRegistryOptions(tr.b.BASIC_REGISTRY_MODE);tr.b.decorateExtensionRegistry(SidePanelRegistry,options);return{SidePanelRegistry,};});'use strict';tr.exportTo('tr.ui.e.s',function(){var BlameContextSnapshot=tr.e.chrome.BlameContextSnapshot;var FrameTreeNodeSnapshot=tr.e.chrome.FrameTreeNodeSnapshot;var RenderFrameSnapshot=tr.e.chrome.RenderFrameSnapshot;var TopLevelSnapshot=tr.e.chrome.TopLevelSnapshot;var BlameContextInstance=tr.e.chrome.BlameContextInstance;var FrameTreeNodeInstance=tr.e.chrome.FrameTreeNodeInstance;var RenderFrameInstance=tr.e.chrome.RenderFrameInstance;var TopLevelInstance=tr.e.chrome.TopLevelInstance;function Row(context){this.subRows=undefined;this.contexts=[];this.type=undefined;this.renderer='N/A';this.url=undefined;this.time=0;this.eventsOfInterest=new tr.model.EventSet();if(context===undefined)return;this.type=context.objectInstance.blameContextType;this.contexts.push(context);if(context instanceof FrameTreeNodeSnapshot){if(context.renderFrame){this.contexts.push(context.renderFrame);this.renderer=context.renderFrame.objectInstance.parent.pid;}}else if(context instanceof RenderFrameSnapshot){if(context.frameTreeNode){this.contexts.push(context.frameTreeNode);}
+this.renderer=context.objectInstance.parent.pid;}else if(context instanceof TopLevelSnapshot){this.renderer=context.objectInstance.parent.pid;}else{throw new Error('Unknown context type');}
 this.eventsOfInterest.addEventSet(this.contexts);this.url=context.url;}
-var groupFunctions={none:rows=>rows,tree:function(rows,rowMap){var getParentRow=function(row){var pivot;row.contexts.forEach(function(context){if(context instanceof tr.e.chrome.FrameTreeNodeSnapshot)
-pivot=context;});if(pivot&&pivot.parentContext)
-return rowMap[pivot.parentContext.guid];return undefined;};var rootRows=[];rows.forEach(function(row){var parentRow=getParentRow(row);if(parentRow===undefined){rootRows.push(row);return;}
-if(parentRow.subRows===undefined)
-parentRow.subRows=[];parentRow.subRows.push(row);});var aggregateAllDescendants=function(row){if(!row.subRows){if(getParentRow(row))
-row.type='Subframe';return row;}
-var result=new Row();result.type='Frame Tree';result.renderer=row.renderer;result.url=row.url;result.subRows=[row];row.subRows.forEach(subRow=>result.subRows.push(aggregateAllDescendants(subRow)));result.subRows.forEach(function(subRow){result.time+=subRow.time;result.eventsOfInterest.addEventSet(subRow.eventsOfInterest);});row.subRows=undefined;return result;};return rootRows.map(rootRow=>aggregateAllDescendants(rootRow));}};Polymer({is:'tr-ui-e-s-frame-data-side-panel',behaviors:[tr.ui.behaviors.SidePanel],ready:function(){this.model_=undefined;this.rangeOfInterest_=new tr.b.Range();this.$.table.showHeader=true;this.$.table.selectionMode=tr.ui.b.TableFormat.SelectionMode.ROW;this.$.table.tableColumns=this.createFrameDataTableColumns_();this.$.table.addEventListener('selection-changed',function(e){this.selectEventSet_(this.$.table.selectedTableRow.eventsOfInterest);}.bind(this));this.$.select.addEventListener('change',function(e){this.updateContents_();}.bind(this));},selectEventSet_:function(eventSet){var event=new tr.model.RequestSelectionChangeEvent();event.selection=eventSet;this.dispatchEvent(event);},createFrameDataTableColumns_:function(){return[{title:'Renderer',value:row=>row.renderer,cmp:(a,b)=>a.renderer-b.renderer},{title:'Type',value:row=>row.type},{title:'Time',value:row=>tr.v.ui.createScalarSpan(row.time,{unit:tr.b.Unit.byName.timeStampInMs,ownerDocument:this.ownerDocument}),cmp:(a,b)=>a.time-b.time},{title:'URL',value:row=>row.url,cmp:(a,b)=>(a.url||'').localeCompare(b.url||'')}];},createFrameDataTableRows_:function(){if(!this.model_)
-return[];var rows=[];var rowMap={};tr.b.iterItems(this.model_.processes,function(pid,process){process.objects.iterObjectInstances(function(objectInstance){if(!(objectInstance instanceof BlameContextInstance))
-return;objectInstance.snapshots.forEach(function(snapshot){if(rowMap[snapshot.guid])
-return;var row=new Row(snapshot);row.contexts.forEach(context=>rowMap[context.guid]=row);rows.push(row);},this);},this);},this);tr.b.iterItems(this.model_.processes,function(pid,process){tr.b.iterItems(process.threads,function(tid,thread){thread.sliceGroup.iterSlicesInTimeRange(function(topLevelSlice){topLevelSlice.contexts.forEach(function(context){if(!context.snapshot.guid||!rowMap[context.snapshot.guid])
-return;var row=rowMap[context.snapshot.guid];row.eventsOfInterest.push(topLevelSlice);row.time+=topLevelSlice.selfTime||0;});},this.currentRangeOfInterest.min,this.currentRangeOfInterest.max);},this);},this);var select=this.$.select;var groupOption=select.options[select.selectedIndex].value;var groupFunction=groupFunctions[groupOption];return groupFunction(rows,rowMap);},updateContents_:function(){this.$.table.tableRows=this.createFrameDataTableRows_();this.$.table.rebuild();},supportsModel:function(m){if(!m){return{supported:false,reason:'No model available.'};}
-var ans={supported:false};tr.b.iterItems(m.processes,function(pid,process){process.objects.iterObjectInstances(function(instance){if(instance instanceof BlameContextInstance)
-ans.supported=true;});},this);if(!ans.supported)
-ans.reason='No frame data available';return ans;},get currentRangeOfInterest(){if(this.rangeOfInterest_.isEmpty)
-return this.model_.bounds;else
-return this.rangeOfInterest_;},get rangeOfInterest(){return this.rangeOfInterest_;},set rangeOfInterest(rangeOfInterest){this.rangeOfInterest_=rangeOfInterest;this.updateContents_();},get selection(){},set selection(_){},get textLabel(){return'Frame Data';},get model(){return this.model_;},set model(model){this.model_=model;this.updateContents_();}});tr.ui.side_panel.SidePanelRegistry.register(function(){return document.createElement('tr-ui-e-s-frame-data-side-panel');});});'use strict';(function(window){window.define=function(x){window.d3=x;};window.define.amd=true;})(this);!function(){function n(n){return null!=n&&!isNaN(n)}function t(n){return n.length}function e(n){for(var t=1;n*t%1;)t*=10;return t}function r(n,t){try{for(var e in t)Object.defineProperty(n.prototype,e,{value:t[e],enumerable:!1})}catch(r){n.prototype=t}}function u(){}function i(n){return aa+n in this}function o(n){return n=aa+n,n in this&&delete this[n]}function a(){var n=[];return this.forEach(function(t){n.push(t)}),n}function c(){var n=0;for(var t in this)t.charCodeAt(0)===ca&&++n;return n}function s(){for(var n in this)if(n.charCodeAt(0)===ca)return!1;return!0}function l(){}function f(n,t,e){return function(){var r=e.apply(t,arguments);return r===t?n:r}}function h(n,t){if(t in n)return t;t=t.charAt(0).toUpperCase()+t.substring(1);for(var e=0,r=sa.length;r>e;++e){var u=sa[e]+t;if(u in n)return u}}function g(){}function p(){}function v(n){function t(){for(var t,r=e,u=-1,i=r.length;++u<i;)(t=r[u].on)&&t.apply(this,arguments);return n}var e=[],r=new u;return t.on=function(t,u){var i,o=r.get(t);return arguments.length<2?o&&o.on:(o&&(o.on=null,e=e.slice(0,i=e.indexOf(o)).concat(e.slice(i+1)),r.remove(t)),u&&e.push(r.set(t,{on:u})),n)},t}function d(){Xo.event.preventDefault()}function m(){for(var n,t=Xo.event;n=t.sourceEvent;)t=n;return t}function y(n){for(var t=new p,e=0,r=arguments.length;++e<r;)t[arguments[e]]=v(t);return t.of=function(e,r){return function(u){try{var i=u.sourceEvent=Xo.event;u.target=n,Xo.event=u,t[u.type].apply(e,r)}finally{Xo.event=i}}},t}function x(n){return fa(n,da),n}function M(n){return"function"==typeof n?n:function(){return ha(n,this)}}function _(n){return"function"==typeof n?n:function(){return ga(n,this)}}function b(n,t){function e(){this.removeAttribute(n)}function r(){this.removeAttributeNS(n.space,n.local)}function u(){this.setAttribute(n,t)}function i(){this.setAttributeNS(n.space,n.local,t)}function o(){var e=t.apply(this,arguments);null==e?this.removeAttribute(n):this.setAttribute(n,e)}function a(){var e=t.apply(this,arguments);null==e?this.removeAttributeNS(n.space,n.local):this.setAttributeNS(n.space,n.local,e)}return n=Xo.ns.qualify(n),null==t?n.local?r:e:"function"==typeof t?n.local?a:o:n.local?i:u}function w(n){return n.trim().replace(/\s+/g," ")}function S(n){return new RegExp("(?:^|\\s+)"+Xo.requote(n)+"(?:\\s+|$)","g")}function k(n){return n.trim().split(/^|\s+/)}function E(n,t){function e(){for(var e=-1;++e<u;)n[e](this,t)}function r(){for(var e=-1,r=t.apply(this,arguments);++e<u;)n[e](this,r)}n=k(n).map(A);var u=n.length;return"function"==typeof t?r:e}function A(n){var t=S(n);return function(e,r){if(u=e.classList)return r?u.add(n):u.remove(n);var u=e.getAttribute("class")||"";r?(t.lastIndex=0,t.test(u)||e.setAttribute("class",w(u+" "+n))):e.setAttribute("class",w(u.replace(t," ")))}}function C(n,t,e){function r(){this.style.removeProperty(n)}function u(){this.style.setProperty(n,t,e)}function i(){var r=t.apply(this,arguments);null==r?this.style.removeProperty(n):this.style.setProperty(n,r,e)}return null==t?r:"function"==typeof t?i:u}function N(n,t){function e(){delete this[n]}function r(){this[n]=t}function u(){var e=t.apply(this,arguments);null==e?delete this[n]:this[n]=e}return null==t?e:"function"==typeof t?u:r}function L(n){return"function"==typeof n?n:(n=Xo.ns.qualify(n)).local?function(){return this.ownerDocument.createElementNS(n.space,n.local)}:function(){return this.ownerDocument.createElementNS(this.namespaceURI,n)}}function T(n){return{__data__:n}}function q(n){return function(){return va(this,n)}}function z(n){return arguments.length||(n=Xo.ascending),function(t,e){return t&&e?n(t.__data__,e.__data__):!t-!e}}function R(n,t){for(var e=0,r=n.length;r>e;e++)for(var u,i=n[e],o=0,a=i.length;a>o;o++)(u=i[o])&&t(u,o,e);return n}function D(n){return fa(n,ya),n}function P(n){var t,e;return function(r,u,i){var o,a=n[i].update,c=a.length;for(i!=e&&(e=i,t=0),u>=t&&(t=u+1);!(o=a[t])&&++t<c;);return o}}function U(){var n=this.__transition__;n&&++n.active}function j(n,t,e){function r(){var t=this[o];t&&(this.removeEventListener(n,t,t.$),delete this[o])}function u(){var u=c(t,Bo(arguments));r.call(this),this.addEventListener(n,this[o]=u,u.$=e),u._=t}function i(){var t,e=new RegExp("^__on([^.]+)"+Xo.requote(n)+"$");for(var r in this)if(t=r.match(e)){var u=this[r];this.removeEventListener(t[1],u,u.$),delete this[r]}}var o="__on"+n,a=n.indexOf("."),c=H;a>0&&(n=n.substring(0,a));var s=Ma.get(n);return s&&(n=s,c=F),a?t?u:r:t?g:i}function H(n,t){return function(e){var r=Xo.event;Xo.event=e,t[0]=this.__data__;try{n.apply(this,t)}finally{Xo.event=r}}}function F(n,t){var e=H(n,t);return function(n){var t=this,r=n.relatedTarget;r&&(r===t||8&r.compareDocumentPosition(t))||e.call(t,n)}}function O(){var n=".dragsuppress-"+ ++ba,t="click"+n,e=Xo.select(Go).on("touchmove"+n,d).on("dragstart"+n,d).on("selectstart"+n,d);if(_a){var r=Jo.style,u=r[_a];r[_a]="none"}return function(i){function o(){e.on(t,null)}e.on(n,null),_a&&(r[_a]=u),i&&(e.on(t,function(){d(),o()},!0),setTimeout(o,0))}}function Y(n,t){t.changedTouches&&(t=t.changedTouches[0]);var e=n.ownerSVGElement||n;if(e.createSVGPoint){var r=e.createSVGPoint();if(0>wa&&(Go.scrollX||Go.scrollY)){e=Xo.select("body").append("svg").style({position:"absolute",top:0,left:0,margin:0,padding:0,border:"none"},"important");var u=e[0][0].getScreenCTM();wa=!(u.f||u.e),e.remove()}return wa?(r.x=t.pageX,r.y=t.pageY):(r.x=t.clientX,r.y=t.clientY),r=r.matrixTransform(n.getScreenCTM().inverse()),[r.x,r.y]}var i=n.getBoundingClientRect();return[t.clientX-i.left-n.clientLeft,t.clientY-i.top-n.clientTop]}function I(n){return n>0?1:0>n?-1:0}function Z(n,t,e){return(t[0]-n[0])*(e[1]-n[1])-(t[1]-n[1])*(e[0]-n[0])}function V(n){return n>1?0:-1>n?Sa:Math.acos(n)}function X(n){return n>1?Ea:-1>n?-Ea:Math.asin(n)}function $(n){return((n=Math.exp(n))-1/n)/2}function B(n){return((n=Math.exp(n))+1/n)/2}function W(n){return((n=Math.exp(2*n))-1)/(n+1)}function J(n){return(n=Math.sin(n/2))*n}function G(){}function K(n,t,e){return new Q(n,t,e)}function Q(n,t,e){this.h=n,this.s=t,this.l=e}function nt(n,t,e){function r(n){return n>360?n-=360:0>n&&(n+=360),60>n?i+(o-i)*n/60:180>n?o:240>n?i+(o-i)*(240-n)/60:i}function u(n){return Math.round(255*r(n))}var i,o;return n=isNaN(n)?0:(n%=360)<0?n+360:n,t=isNaN(t)?0:0>t?0:t>1?1:t,e=0>e?0:e>1?1:e,o=.5>=e?e*(1+t):e+t-e*t,i=2*e-o,gt(u(n+120),u(n),u(n-120))}function tt(n,t,e){return new et(n,t,e)}function et(n,t,e){this.h=n,this.c=t,this.l=e}function rt(n,t,e){return isNaN(n)&&(n=0),isNaN(t)&&(t=0),ut(e,Math.cos(n*=Na)*t,Math.sin(n)*t)}function ut(n,t,e){return new it(n,t,e)}function it(n,t,e){this.l=n,this.a=t,this.b=e}function ot(n,t,e){var r=(n+16)/116,u=r+t/500,i=r-e/200;return u=ct(u)*Fa,r=ct(r)*Oa,i=ct(i)*Ya,gt(lt(3.2404542*u-1.5371385*r-.4985314*i),lt(-.969266*u+1.8760108*r+.041556*i),lt(.0556434*u-.2040259*r+1.0572252*i))}function at(n,t,e){return n>0?tt(Math.atan2(e,t)*La,Math.sqrt(t*t+e*e),n):tt(0/0,0/0,n)}function ct(n){return n>.206893034?n*n*n:(n-4/29)/7.787037}function st(n){return n>.008856?Math.pow(n,1/3):7.787037*n+4/29}function lt(n){return Math.round(255*(.00304>=n?12.92*n:1.055*Math.pow(n,1/2.4)-.055))}function ft(n){return gt(n>>16,255&n>>8,255&n)}function ht(n){return ft(n)+""}function gt(n,t,e){return new pt(n,t,e)}function pt(n,t,e){this.r=n,this.g=t,this.b=e}function vt(n){return 16>n?"0"+Math.max(0,n).toString(16):Math.min(255,n).toString(16)}function dt(n,t,e){var r,u,i,o,a=0,c=0,s=0;if(u=/([a-z]+)\((.*)\)/i.exec(n))switch(i=u[2].split(","),u[1]){case"hsl":return e(parseFloat(i[0]),parseFloat(i[1])/100,parseFloat(i[2])/100);case"rgb":return t(Mt(i[0]),Mt(i[1]),Mt(i[2]))}return(o=Va.get(n))?t(o.r,o.g,o.b):(null!=n&&"#"===n.charAt(0)&&(r=parseInt(n.substring(1),16),isNaN(r)||(4===n.length?(a=(3840&r)>>4,a=a>>4|a,c=240&r,c=c>>4|c,s=15&r,s=s<<4|s):7===n.length&&(a=(16711680&r)>>16,c=(65280&r)>>8,s=255&r))),t(a,c,s))}function mt(n,t,e){var r,u,i=Math.min(n/=255,t/=255,e/=255),o=Math.max(n,t,e),a=o-i,c=(o+i)/2;return a?(u=.5>c?a/(o+i):a/(2-o-i),r=n==o?(t-e)/a+(e>t?6:0):t==o?(e-n)/a+2:(n-t)/a+4,r*=60):(r=0/0,u=c>0&&1>c?0:r),K(r,u,c)}function yt(n,t,e){n=xt(n),t=xt(t),e=xt(e);var r=st((.4124564*n+.3575761*t+.1804375*e)/Fa),u=st((.2126729*n+.7151522*t+.072175*e)/Oa),i=st((.0193339*n+.119192*t+.9503041*e)/Ya);return ut(116*u-16,500*(r-u),200*(u-i))}function xt(n){return(n/=255)<=.04045?n/12.92:Math.pow((n+.055)/1.055,2.4)}function Mt(n){var t=parseFloat(n);return"%"===n.charAt(n.length-1)?Math.round(2.55*t):t}function _t(n){return"function"==typeof n?n:function(){return n}}function bt(n){return n}function wt(n){return function(t,e,r){return 2===arguments.length&&"function"==typeof e&&(r=e,e=null),St(t,e,n,r)}}function St(n,t,e,r){function u(){var n,t=c.status;if(!t&&c.responseText||t>=200&&300>t||304===t){try{n=e.call(i,c)}catch(r){return o.error.call(i,r),void 0}o.load.call(i,n)}else o.error.call(i,c)}var i={},o=Xo.dispatch("beforesend","progress","load","error"),a={},c=new XMLHttpRequest,s=null;return!Go.XDomainRequest||"withCredentials"in c||!/^(http(s)?:)?\/\//.test(n)||(c=new XDomainRequest),"onload"in c?c.onload=c.onerror=u:c.onreadystatechange=function(){c.readyState>3&&u()},c.onprogress=function(n){var t=Xo.event;Xo.event=n;try{o.progress.call(i,c)}finally{Xo.event=t}},i.header=function(n,t){return n=(n+"").toLowerCase(),arguments.length<2?a[n]:(null==t?delete a[n]:a[n]=t+"",i)},i.mimeType=function(n){return arguments.length?(t=null==n?null:n+"",i):t},i.responseType=function(n){return arguments.length?(s=n,i):s},i.response=function(n){return e=n,i},["get","post"].forEach(function(n){i[n]=function(){return i.send.apply(i,[n].concat(Bo(arguments)))}}),i.send=function(e,r,u){if(2===arguments.length&&"function"==typeof r&&(u=r,r=null),c.open(e,n,!0),null==t||"accept"in a||(a.accept=t+",*/*"),c.setRequestHeader)for(var l in a)c.setRequestHeader(l,a[l]);return null!=t&&c.overrideMimeType&&c.overrideMimeType(t),null!=s&&(c.responseType=s),null!=u&&i.on("error",u).on("load",function(n){u(null,n)}),o.beforesend.call(i,c),c.send(null==r?null:r),i},i.abort=function(){return c.abort(),i},Xo.rebind(i,o,"on"),null==r?i:i.get(kt(r))}function kt(n){return 1===n.length?function(t,e){n(null==t?e:null)}:n}function Et(){var n=At(),t=Ct()-n;t>24?(isFinite(t)&&(clearTimeout(Wa),Wa=setTimeout(Et,t)),Ba=0):(Ba=1,Ga(Et))}function At(){var n=Date.now();for(Ja=Xa;Ja;)n>=Ja.t&&(Ja.f=Ja.c(n-Ja.t)),Ja=Ja.n;return n}function Ct(){for(var n,t=Xa,e=1/0;t;)t.f?t=n?n.n=t.n:Xa=t.n:(t.t<e&&(e=t.t),t=(n=t).n);return $a=n,e}function Nt(n,t){return t-(n?Math.ceil(Math.log(n)/Math.LN10):1)}function Lt(n,t){var e=Math.pow(10,3*oa(8-t));return{scale:t>8?function(n){return n/e}:function(n){return n*e},symbol:n}}function Tt(n){var t=n.decimal,e=n.thousands,r=n.grouping,u=n.currency,i=r?function(n){for(var t=n.length,u=[],i=0,o=r[0];t>0&&o>0;)u.push(n.substring(t-=o,t+o)),o=r[i=(i+1)%r.length];return u.reverse().join(e)}:bt;return function(n){var e=Qa.exec(n),r=e[1]||" ",o=e[2]||">",a=e[3]||"",c=e[4]||"",s=e[5],l=+e[6],f=e[7],h=e[8],g=e[9],p=1,v="",d="",m=!1;switch(h&&(h=+h.substring(1)),(s||"0"===r&&"="===o)&&(s=r="0",o="=",f&&(l-=Math.floor((l-1)/4))),g){case"n":f=!0,g="g";break;case"%":p=100,d="%",g="f";break;case"p":p=100,d="%",g="r";break;case"b":case"o":case"x":case"X":"#"===c&&(v="0"+g.toLowerCase());case"c":case"d":m=!0,h=0;break;case"s":p=-1,g="r"}"$"===c&&(v=u[0],d=u[1]),"r"!=g||h||(g="g"),null!=h&&("g"==g?h=Math.max(1,Math.min(21,h)):("e"==g||"f"==g)&&(h=Math.max(0,Math.min(20,h)))),g=nc.get(g)||qt;var y=s&&f;return function(n){var e=d;if(m&&n%1)return"";var u=0>n||0===n&&0>1/n?(n=-n,"-"):a;if(0>p){var c=Xo.formatPrefix(n,h);n=c.scale(n),e=c.symbol+d}else n*=p;n=g(n,h);var x=n.lastIndexOf("."),M=0>x?n:n.substring(0,x),_=0>x?"":t+n.substring(x+1);!s&&f&&(M=i(M));var b=v.length+M.length+_.length+(y?0:u.length),w=l>b?new Array(b=l-b+1).join(r):"";return y&&(M=i(w+M)),u+=v,n=M+_,("<"===o?u+n+w:">"===o?w+u+n:"^"===o?w.substring(0,b>>=1)+u+n+w.substring(b):u+(y?n:w+n))+e}}}function qt(n){return n+""}function zt(){this._=new Date(arguments.length>1?Date.UTC.apply(this,arguments):arguments[0])}function Rt(n,t,e){function r(t){var e=n(t),r=i(e,1);return r-t>t-e?e:r}function u(e){return t(e=n(new ec(e-1)),1),e}function i(n,e){return t(n=new ec(+n),e),n}function o(n,r,i){var o=u(n),a=[];if(i>1)for(;r>o;)e(o)%i||a.push(new Date(+o)),t(o,1);else for(;r>o;)a.push(new Date(+o)),t(o,1);return a}function a(n,t,e){try{ec=zt;var r=new zt;return r._=n,o(r,t,e)}finally{ec=Date}}n.floor=n,n.round=r,n.ceil=u,n.offset=i,n.range=o;var c=n.utc=Dt(n);return c.floor=c,c.round=Dt(r),c.ceil=Dt(u),c.offset=Dt(i),c.range=a,n}function Dt(n){return function(t,e){try{ec=zt;var r=new zt;return r._=t,n(r,e)._}finally{ec=Date}}}function Pt(n){function t(n){function t(t){for(var e,u,i,o=[],a=-1,c=0;++a<r;)37===n.charCodeAt(a)&&(o.push(n.substring(c,a)),null!=(u=uc[e=n.charAt(++a)])&&(e=n.charAt(++a)),(i=C[e])&&(e=i(t,null==u?"e"===e?" ":"0":u)),o.push(e),c=a+1);return o.push(n.substring(c,a)),o.join("")}var r=n.length;return t.parse=function(t){var r={y:1900,m:0,d:1,H:0,M:0,S:0,L:0,Z:null},u=e(r,n,t,0);if(u!=t.length)return null;"p"in r&&(r.H=r.H%12+12*r.p);var i=null!=r.Z&&ec!==zt,o=new(i?zt:ec);return"j"in r?o.setFullYear(r.y,0,r.j):"w"in r&&("W"in r||"U"in r)?(o.setFullYear(r.y,0,1),o.setFullYear(r.y,0,"W"in r?(r.w+6)%7+7*r.W-(o.getDay()+5)%7:r.w+7*r.U-(o.getDay()+6)%7)):o.setFullYear(r.y,r.m,r.d),o.setHours(r.H+Math.floor(r.Z/100),r.M+r.Z%100,r.S,r.L),i?o._:o},t.toString=function(){return n},t}function e(n,t,e,r){for(var u,i,o,a=0,c=t.length,s=e.length;c>a;){if(r>=s)return-1;if(u=t.charCodeAt(a++),37===u){if(o=t.charAt(a++),i=N[o in uc?t.charAt(a++):o],!i||(r=i(n,e,r))<0)return-1}else if(u!=e.charCodeAt(r++))return-1}return r}function r(n,t,e){b.lastIndex=0;var r=b.exec(t.substring(e));return r?(n.w=w.get(r[0].toLowerCase()),e+r[0].length):-1}function u(n,t,e){M.lastIndex=0;var r=M.exec(t.substring(e));return r?(n.w=_.get(r[0].toLowerCase()),e+r[0].length):-1}function i(n,t,e){E.lastIndex=0;var r=E.exec(t.substring(e));return r?(n.m=A.get(r[0].toLowerCase()),e+r[0].length):-1}function o(n,t,e){S.lastIndex=0;var r=S.exec(t.substring(e));return r?(n.m=k.get(r[0].toLowerCase()),e+r[0].length):-1}function a(n,t,r){return e(n,C.c.toString(),t,r)}function c(n,t,r){return e(n,C.x.toString(),t,r)}function s(n,t,r){return e(n,C.X.toString(),t,r)}function l(n,t,e){var r=x.get(t.substring(e,e+=2).toLowerCase());return null==r?-1:(n.p=r,e)}var f=n.dateTime,h=n.date,g=n.time,p=n.periods,v=n.days,d=n.shortDays,m=n.months,y=n.shortMonths;t.utc=function(n){function e(n){try{ec=zt;var t=new ec;return t._=n,r(t)}finally{ec=Date}}var r=t(n);return e.parse=function(n){try{ec=zt;var t=r.parse(n);return t&&t._}finally{ec=Date}},e.toString=r.toString,e},t.multi=t.utc.multi=ee;var x=Xo.map(),M=jt(v),_=Ht(v),b=jt(d),w=Ht(d),S=jt(m),k=Ht(m),E=jt(y),A=Ht(y);p.forEach(function(n,t){x.set(n.toLowerCase(),t)});var C={a:function(n){return d[n.getDay()]},A:function(n){return v[n.getDay()]},b:function(n){return y[n.getMonth()]},B:function(n){return m[n.getMonth()]},c:t(f),d:function(n,t){return Ut(n.getDate(),t,2)},e:function(n,t){return Ut(n.getDate(),t,2)},H:function(n,t){return Ut(n.getHours(),t,2)},I:function(n,t){return Ut(n.getHours()%12||12,t,2)},j:function(n,t){return Ut(1+tc.dayOfYear(n),t,3)},L:function(n,t){return Ut(n.getMilliseconds(),t,3)},m:function(n,t){return Ut(n.getMonth()+1,t,2)},M:function(n,t){return Ut(n.getMinutes(),t,2)},p:function(n){return p[+(n.getHours()>=12)]},S:function(n,t){return Ut(n.getSeconds(),t,2)},U:function(n,t){return Ut(tc.sundayOfYear(n),t,2)},w:function(n){return n.getDay()},W:function(n,t){return Ut(tc.mondayOfYear(n),t,2)},x:t(h),X:t(g),y:function(n,t){return Ut(n.getFullYear()%100,t,2)},Y:function(n,t){return Ut(n.getFullYear()%1e4,t,4)},Z:ne,"%":function(){return"%"}},N={a:r,A:u,b:i,B:o,c:a,d:Bt,e:Bt,H:Jt,I:Jt,j:Wt,L:Qt,m:$t,M:Gt,p:l,S:Kt,U:Ot,w:Ft,W:Yt,x:c,X:s,y:Zt,Y:It,Z:Vt,"%":te};return t}function Ut(n,t,e){var r=0>n?"-":"",u=(r?-n:n)+"",i=u.length;return r+(e>i?new Array(e-i+1).join(t)+u:u)}function jt(n){return new RegExp("^(?:"+n.map(Xo.requote).join("|")+")","i")}function Ht(n){for(var t=new u,e=-1,r=n.length;++e<r;)t.set(n[e].toLowerCase(),e);return t}function Ft(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+1));return r?(n.w=+r[0],e+r[0].length):-1}function Ot(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e));return r?(n.U=+r[0],e+r[0].length):-1}function Yt(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e));return r?(n.W=+r[0],e+r[0].length):-1}function It(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+4));return r?(n.y=+r[0],e+r[0].length):-1}function Zt(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+2));return r?(n.y=Xt(+r[0]),e+r[0].length):-1}function Vt(n,t,e){return/^[+-]\d{4}$/.test(t=t.substring(e,e+5))?(n.Z=+t,e+5):-1}function Xt(n){return n+(n>68?1900:2e3)}function $t(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+2));return r?(n.m=r[0]-1,e+r[0].length):-1}function Bt(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+2));return r?(n.d=+r[0],e+r[0].length):-1}function Wt(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+3));return r?(n.j=+r[0],e+r[0].length):-1}function Jt(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+2));return r?(n.H=+r[0],e+r[0].length):-1}function Gt(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+2));return r?(n.M=+r[0],e+r[0].length):-1}function Kt(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+2));return r?(n.S=+r[0],e+r[0].length):-1}function Qt(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+3));return r?(n.L=+r[0],e+r[0].length):-1}function ne(n){var t=n.getTimezoneOffset(),e=t>0?"-":"+",r=~~(oa(t)/60),u=oa(t)%60;return e+Ut(r,"0",2)+Ut(u,"0",2)}function te(n,t,e){oc.lastIndex=0;var r=oc.exec(t.substring(e,e+1));return r?e+r[0].length:-1}function ee(n){for(var t=n.length,e=-1;++e<t;)n[e][0]=this(n[e][0]);return function(t){for(var e=0,r=n[e];!r[1](t);)r=n[++e];return r[0](t)}}function re(){}function ue(n,t,e){var r=e.s=n+t,u=r-n,i=r-u;e.t=n-i+(t-u)}function ie(n,t){n&&lc.hasOwnProperty(n.type)&&lc[n.type](n,t)}function oe(n,t,e){var r,u=-1,i=n.length-e;for(t.lineStart();++u<i;)r=n[u],t.point(r[0],r[1],r[2]);t.lineEnd()}function ae(n,t){var e=-1,r=n.length;for(t.polygonStart();++e<r;)oe(n[e],t,1);t.polygonEnd()}function ce(){function n(n,t){n*=Na,t=t*Na/2+Sa/4;var e=n-r,o=e>=0?1:-1,a=o*e,c=Math.cos(t),s=Math.sin(t),l=i*s,f=u*c+l*Math.cos(a),h=l*o*Math.sin(a);hc.add(Math.atan2(h,f)),r=n,u=c,i=s}var t,e,r,u,i;gc.point=function(o,a){gc.point=n,r=(t=o)*Na,u=Math.cos(a=(e=a)*Na/2+Sa/4),i=Math.sin(a)},gc.lineEnd=function(){n(t,e)}}function se(n){var t=n[0],e=n[1],r=Math.cos(e);return[r*Math.cos(t),r*Math.sin(t),Math.sin(e)]}function le(n,t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]}function fe(n,t){return[n[1]*t[2]-n[2]*t[1],n[2]*t[0]-n[0]*t[2],n[0]*t[1]-n[1]*t[0]]}function he(n,t){n[0]+=t[0],n[1]+=t[1],n[2]+=t[2]}function ge(n,t){return[n[0]*t,n[1]*t,n[2]*t]}function pe(n){var t=Math.sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]);n[0]/=t,n[1]/=t,n[2]/=t}function ve(n){return[Math.atan2(n[1],n[0]),X(n[2])]}function de(n,t){return oa(n[0]-t[0])<Aa&&oa(n[1]-t[1])<Aa}function me(n,t){n*=Na;var e=Math.cos(t*=Na);ye(e*Math.cos(n),e*Math.sin(n),Math.sin(t))}function ye(n,t,e){++pc,dc+=(n-dc)/pc,mc+=(t-mc)/pc,yc+=(e-yc)/pc}function xe(){function n(n,u){n*=Na;var i=Math.cos(u*=Na),o=i*Math.cos(n),a=i*Math.sin(n),c=Math.sin(u),s=Math.atan2(Math.sqrt((s=e*c-r*a)*s+(s=r*o-t*c)*s+(s=t*a-e*o)*s),t*o+e*a+r*c);vc+=s,xc+=s*(t+(t=o)),Mc+=s*(e+(e=a)),_c+=s*(r+(r=c)),ye(t,e,r)}var t,e,r;kc.point=function(u,i){u*=Na;var o=Math.cos(i*=Na);t=o*Math.cos(u),e=o*Math.sin(u),r=Math.sin(i),kc.point=n,ye(t,e,r)}}function Me(){kc.point=me}function _e(){function n(n,t){n*=Na;var e=Math.cos(t*=Na),o=e*Math.cos(n),a=e*Math.sin(n),c=Math.sin(t),s=u*c-i*a,l=i*o-r*c,f=r*a-u*o,h=Math.sqrt(s*s+l*l+f*f),g=r*o+u*a+i*c,p=h&&-V(g)/h,v=Math.atan2(h,g);bc+=p*s,wc+=p*l,Sc+=p*f,vc+=v,xc+=v*(r+(r=o)),Mc+=v*(u+(u=a)),_c+=v*(i+(i=c)),ye(r,u,i)}var t,e,r,u,i;kc.point=function(o,a){t=o,e=a,kc.point=n,o*=Na;var c=Math.cos(a*=Na);r=c*Math.cos(o),u=c*Math.sin(o),i=Math.sin(a),ye(r,u,i)},kc.lineEnd=function(){n(t,e),kc.lineEnd=Me,kc.point=me}}function be(){return!0}function we(n,t,e,r,u){var i=[],o=[];if(n.forEach(function(n){if(!((t=n.length-1)<=0)){var t,e=n[0],r=n[t];if(de(e,r)){u.lineStart();for(var a=0;t>a;++a)u.point((e=n[a])[0],e[1]);return u.lineEnd(),void 0}var c=new ke(e,n,null,!0),s=new ke(e,null,c,!1);c.o=s,i.push(c),o.push(s),c=new ke(r,n,null,!1),s=new ke(r,null,c,!0),c.o=s,i.push(c),o.push(s)}}),o.sort(t),Se(i),Se(o),i.length){for(var a=0,c=e,s=o.length;s>a;++a)o[a].e=c=!c;for(var l,f,h=i[0];;){for(var g=h,p=!0;g.v;)if((g=g.n)===h)return;l=g.z,u.lineStart();do{if(g.v=g.o.v=!0,g.e){if(p)for(var a=0,s=l.length;s>a;++a)u.point((f=l[a])[0],f[1]);else r(g.x,g.n.x,1,u);g=g.n}else{if(p){l=g.p.z;for(var a=l.length-1;a>=0;--a)u.point((f=l[a])[0],f[1])}else r(g.x,g.p.x,-1,u);g=g.p}g=g.o,l=g.z,p=!p}while(!g.v);u.lineEnd()}}}function Se(n){if(t=n.length){for(var t,e,r=0,u=n[0];++r<t;)u.n=e=n[r],e.p=u,u=e;u.n=e=n[0],e.p=u}}function ke(n,t,e,r){this.x=n,this.z=t,this.o=e,this.e=r,this.v=!1,this.n=this.p=null}function Ee(n,t,e,r){return function(u,i){function o(t,e){var r=u(t,e);n(t=r[0],e=r[1])&&i.point(t,e)}function a(n,t){var e=u(n,t);d.point(e[0],e[1])}function c(){y.point=a,d.lineStart()}function s(){y.point=o,d.lineEnd()}function l(n,t){v.push([n,t]);var e=u(n,t);M.point(e[0],e[1])}function f(){M.lineStart(),v=[]}function h(){l(v[0][0],v[0][1]),M.lineEnd();var n,t=M.clean(),e=x.buffer(),r=e.length;if(v.pop(),p.push(v),v=null,r){if(1&t){n=e[0];var u,r=n.length-1,o=-1;for(i.lineStart();++o<r;)i.point((u=n[o])[0],u[1]);return i.lineEnd(),void 0}r>1&&2&t&&e.push(e.pop().concat(e.shift())),g.push(e.filter(Ae))}}var g,p,v,d=t(i),m=u.invert(r[0],r[1]),y={point:o,lineStart:c,lineEnd:s,polygonStart:function(){y.point=l,y.lineStart=f,y.lineEnd=h,g=[],p=[],i.polygonStart()},polygonEnd:function(){y.point=o,y.lineStart=c,y.lineEnd=s,g=Xo.merge(g);var n=Le(m,p);g.length?we(g,Ne,n,e,i):n&&(i.lineStart(),e(null,null,1,i),i.lineEnd()),i.polygonEnd(),g=p=null},sphere:function(){i.polygonStart(),i.lineStart(),e(null,null,1,i),i.lineEnd(),i.polygonEnd()}},x=Ce(),M=t(x);return y}}function Ae(n){return n.length>1}function Ce(){var n,t=[];return{lineStart:function(){t.push(n=[])},point:function(t,e){n.push([t,e])},lineEnd:g,buffer:function(){var e=t;return t=[],n=null,e},rejoin:function(){t.length>1&&t.push(t.pop().concat(t.shift()))}}}function Ne(n,t){return((n=n.x)[0]<0?n[1]-Ea-Aa:Ea-n[1])-((t=t.x)[0]<0?t[1]-Ea-Aa:Ea-t[1])}function Le(n,t){var e=n[0],r=n[1],u=[Math.sin(e),-Math.cos(e),0],i=0,o=0;hc.reset();for(var a=0,c=t.length;c>a;++a){var s=t[a],l=s.length;if(l)for(var f=s[0],h=f[0],g=f[1]/2+Sa/4,p=Math.sin(g),v=Math.cos(g),d=1;;){d===l&&(d=0),n=s[d];var m=n[0],y=n[1]/2+Sa/4,x=Math.sin(y),M=Math.cos(y),_=m-h,b=_>=0?1:-1,w=b*_,S=w>Sa,k=p*x;if(hc.add(Math.atan2(k*b*Math.sin(w),v*M+k*Math.cos(w))),i+=S?_+b*ka:_,S^h>=e^m>=e){var E=fe(se(f),se(n));pe(E);var A=fe(u,E);pe(A);var C=(S^_>=0?-1:1)*X(A[2]);(r>C||r===C&&(E[0]||E[1]))&&(o+=S^_>=0?1:-1)}if(!d++)break;h=m,p=x,v=M,f=n}}return(-Aa>i||Aa>i&&0>hc)^1&o}function Te(n){var t,e=0/0,r=0/0,u=0/0;return{lineStart:function(){n.lineStart(),t=1},point:function(i,o){var a=i>0?Sa:-Sa,c=oa(i-e);oa(c-Sa)<Aa?(n.point(e,r=(r+o)/2>0?Ea:-Ea),n.point(u,r),n.lineEnd(),n.lineStart(),n.point(a,r),n.point(i,r),t=0):u!==a&&c>=Sa&&(oa(e-u)<Aa&&(e-=u*Aa),oa(i-a)<Aa&&(i-=a*Aa),r=qe(e,r,i,o),n.point(u,r),n.lineEnd(),n.lineStart(),n.point(a,r),t=0),n.point(e=i,r=o),u=a},lineEnd:function(){n.lineEnd(),e=r=0/0},clean:function(){return 2-t}}}function qe(n,t,e,r){var u,i,o=Math.sin(n-e);return oa(o)>Aa?Math.atan((Math.sin(t)*(i=Math.cos(r))*Math.sin(e)-Math.sin(r)*(u=Math.cos(t))*Math.sin(n))/(u*i*o)):(t+r)/2}function ze(n,t,e,r){var u;if(null==n)u=e*Ea,r.point(-Sa,u),r.point(0,u),r.point(Sa,u),r.point(Sa,0),r.point(Sa,-u),r.point(0,-u),r.point(-Sa,-u),r.point(-Sa,0),r.point(-Sa,u);else if(oa(n[0]-t[0])>Aa){var i=n[0]<t[0]?Sa:-Sa;u=e*i/2,r.point(-i,u),r.point(0,u),r.point(i,u)}else r.point(t[0],t[1])}function Re(n){function t(n,t){return Math.cos(n)*Math.cos(t)>i}function e(n){var e,i,c,s,l;return{lineStart:function(){s=c=!1,l=1},point:function(f,h){var g,p=[f,h],v=t(f,h),d=o?v?0:u(f,h):v?u(f+(0>f?Sa:-Sa),h):0;if(!e&&(s=c=v)&&n.lineStart(),v!==c&&(g=r(e,p),(de(e,g)||de(p,g))&&(p[0]+=Aa,p[1]+=Aa,v=t(p[0],p[1]))),v!==c)l=0,v?(n.lineStart(),g=r(p,e),n.point(g[0],g[1])):(g=r(e,p),n.point(g[0],g[1]),n.lineEnd()),e=g;else if(a&&e&&o^v){var m;d&i||!(m=r(p,e,!0))||(l=0,o?(n.lineStart(),n.point(m[0][0],m[0][1]),n.point(m[1][0],m[1][1]),n.lineEnd()):(n.point(m[1][0],m[1][1]),n.lineEnd(),n.lineStart(),n.point(m[0][0],m[0][1])))}!v||e&&de(e,p)||n.point(p[0],p[1]),e=p,c=v,i=d},lineEnd:function(){c&&n.lineEnd(),e=null},clean:function(){return l|(s&&c)<<1}}}function r(n,t,e){var r=se(n),u=se(t),o=[1,0,0],a=fe(r,u),c=le(a,a),s=a[0],l=c-s*s;if(!l)return!e&&n;var f=i*c/l,h=-i*s/l,g=fe(o,a),p=ge(o,f),v=ge(a,h);he(p,v);var d=g,m=le(p,d),y=le(d,d),x=m*m-y*(le(p,p)-1);if(!(0>x)){var M=Math.sqrt(x),_=ge(d,(-m-M)/y);if(he(_,p),_=ve(_),!e)return _;var b,w=n[0],S=t[0],k=n[1],E=t[1];w>S&&(b=w,w=S,S=b);var A=S-w,C=oa(A-Sa)<Aa,N=C||Aa>A;if(!C&&k>E&&(b=k,k=E,E=b),N?C?k+E>0^_[1]<(oa(_[0]-w)<Aa?k:E):k<=_[1]&&_[1]<=E:A>Sa^(w<=_[0]&&_[0]<=S)){var L=ge(d,(-m+M)/y);return he(L,p),[_,ve(L)]}}}function u(t,e){var r=o?n:Sa-n,u=0;return-r>t?u|=1:t>r&&(u|=2),-r>e?u|=4:e>r&&(u|=8),u}var i=Math.cos(n),o=i>0,a=oa(i)>Aa,c=cr(n,6*Na);return Ee(t,e,c,o?[0,-n]:[-Sa,n-Sa])}function De(n,t,e,r){return function(u){var i,o=u.a,a=u.b,c=o.x,s=o.y,l=a.x,f=a.y,h=0,g=1,p=l-c,v=f-s;if(i=n-c,p||!(i>0)){if(i/=p,0>p){if(h>i)return;g>i&&(g=i)}else if(p>0){if(i>g)return;i>h&&(h=i)}if(i=e-c,p||!(0>i)){if(i/=p,0>p){if(i>g)return;i>h&&(h=i)}else if(p>0){if(h>i)return;g>i&&(g=i)}if(i=t-s,v||!(i>0)){if(i/=v,0>v){if(h>i)return;g>i&&(g=i)}else if(v>0){if(i>g)return;i>h&&(h=i)}if(i=r-s,v||!(0>i)){if(i/=v,0>v){if(i>g)return;i>h&&(h=i)}else if(v>0){if(h>i)return;g>i&&(g=i)}return h>0&&(u.a={x:c+h*p,y:s+h*v}),1>g&&(u.b={x:c+g*p,y:s+g*v}),u}}}}}}function Pe(n,t,e,r){function u(r,u){return oa(r[0]-n)<Aa?u>0?0:3:oa(r[0]-e)<Aa?u>0?2:1:oa(r[1]-t)<Aa?u>0?1:0:u>0?3:2}function i(n,t){return o(n.x,t.x)}function o(n,t){var e=u(n,1),r=u(t,1);return e!==r?e-r:0===e?t[1]-n[1]:1===e?n[0]-t[0]:2===e?n[1]-t[1]:t[0]-n[0]}return function(a){function c(n){for(var t=0,e=d.length,r=n[1],u=0;e>u;++u)for(var i,o=1,a=d[u],c=a.length,s=a[0];c>o;++o)i=a[o],s[1]<=r?i[1]>r&&Z(s,i,n)>0&&++t:i[1]<=r&&Z(s,i,n)<0&&--t,s=i;return 0!==t}function s(i,a,c,s){var l=0,f=0;if(null==i||(l=u(i,c))!==(f=u(a,c))||o(i,a)<0^c>0){do s.point(0===l||3===l?n:e,l>1?r:t);while((l=(l+c+4)%4)!==f)}else s.point(a[0],a[1])}function l(u,i){return u>=n&&e>=u&&i>=t&&r>=i}function f(n,t){l(n,t)&&a.point(n,t)}function h(){N.point=p,d&&d.push(m=[]),S=!0,w=!1,_=b=0/0}function g(){v&&(p(y,x),M&&w&&A.rejoin(),v.push(A.buffer())),N.point=f,w&&a.lineEnd()}function p(n,t){n=Math.max(-Ac,Math.min(Ac,n)),t=Math.max(-Ac,Math.min(Ac,t));var e=l(n,t);if(d&&m.push([n,t]),S)y=n,x=t,M=e,S=!1,e&&(a.lineStart(),a.point(n,t));else if(e&&w)a.point(n,t);else{var r={a:{x:_,y:b},b:{x:n,y:t}};C(r)?(w||(a.lineStart(),a.point(r.a.x,r.a.y)),a.point(r.b.x,r.b.y),e||a.lineEnd(),k=!1):e&&(a.lineStart(),a.point(n,t),k=!1)}_=n,b=t,w=e}var v,d,m,y,x,M,_,b,w,S,k,E=a,A=Ce(),C=De(n,t,e,r),N={point:f,lineStart:h,lineEnd:g,polygonStart:function(){a=A,v=[],d=[],k=!0},polygonEnd:function(){a=E,v=Xo.merge(v);var t=c([n,r]),e=k&&t,u=v.length;(e||u)&&(a.polygonStart(),e&&(a.lineStart(),s(null,null,1,a),a.lineEnd()),u&&we(v,i,t,s,a),a.polygonEnd()),v=d=m=null}};return N}}function Ue(n,t){function e(e,r){return e=n(e,r),t(e[0],e[1])}return n.invert&&t.invert&&(e.invert=function(e,r){return e=t.invert(e,r),e&&n.invert(e[0],e[1])}),e}function je(n){var t=0,e=Sa/3,r=nr(n),u=r(t,e);return u.parallels=function(n){return arguments.length?r(t=n[0]*Sa/180,e=n[1]*Sa/180):[180*(t/Sa),180*(e/Sa)]},u}function He(n,t){function e(n,t){var e=Math.sqrt(i-2*u*Math.sin(t))/u;return[e*Math.sin(n*=u),o-e*Math.cos(n)]}var r=Math.sin(n),u=(r+Math.sin(t))/2,i=1+r*(2*u-r),o=Math.sqrt(i)/u;return e.invert=function(n,t){var e=o-t;return[Math.atan2(n,e)/u,X((i-(n*n+e*e)*u*u)/(2*u))]},e}function Fe(){function n(n,t){Nc+=u*n-r*t,r=n,u=t}var t,e,r,u;Rc.point=function(i,o){Rc.point=n,t=r=i,e=u=o},Rc.lineEnd=function(){n(t,e)}}function Oe(n,t){Lc>n&&(Lc=n),n>qc&&(qc=n),Tc>t&&(Tc=t),t>zc&&(zc=t)}function Ye(){function n(n,t){o.push("M",n,",",t,i)}function t(n,t){o.push("M",n,",",t),a.point=e}function e(n,t){o.push("L",n,",",t)}function r(){a.point=n}function u(){o.push("Z")}var i=Ie(4.5),o=[],a={point:n,lineStart:function(){a.point=t},lineEnd:r,polygonStart:function(){a.lineEnd=u},polygonEnd:function(){a.lineEnd=r,a.point=n},pointRadius:function(n){return i=Ie(n),a},result:function(){if(o.length){var n=o.join("");return o=[],n}}};return a}function Ie(n){return"m0,"+n+"a"+n+","+n+" 0 1,1 0,"+-2*n+"a"+n+","+n+" 0 1,1 0,"+2*n+"z"}function Ze(n,t){dc+=n,mc+=t,++yc}function Ve(){function n(n,r){var u=n-t,i=r-e,o=Math.sqrt(u*u+i*i);xc+=o*(t+n)/2,Mc+=o*(e+r)/2,_c+=o,Ze(t=n,e=r)}var t,e;Pc.point=function(r,u){Pc.point=n,Ze(t=r,e=u)}}function Xe(){Pc.point=Ze}function $e(){function n(n,t){var e=n-r,i=t-u,o=Math.sqrt(e*e+i*i);xc+=o*(r+n)/2,Mc+=o*(u+t)/2,_c+=o,o=u*n-r*t,bc+=o*(r+n),wc+=o*(u+t),Sc+=3*o,Ze(r=n,u=t)}var t,e,r,u;Pc.point=function(i,o){Pc.point=n,Ze(t=r=i,e=u=o)},Pc.lineEnd=function(){n(t,e)}}function Be(n){function t(t,e){n.moveTo(t,e),n.arc(t,e,o,0,ka)}function e(t,e){n.moveTo(t,e),a.point=r}function r(t,e){n.lineTo(t,e)}function u(){a.point=t}function i(){n.closePath()}var o=4.5,a={point:t,lineStart:function(){a.point=e},lineEnd:u,polygonStart:function(){a.lineEnd=i},polygonEnd:function(){a.lineEnd=u,a.point=t},pointRadius:function(n){return o=n,a},result:g};return a}function We(n){function t(n){return(a?r:e)(n)}function e(t){return Ke(t,function(e,r){e=n(e,r),t.point(e[0],e[1])})}function r(t){function e(e,r){e=n(e,r),t.point(e[0],e[1])}function r(){x=0/0,S.point=i,t.lineStart()}function i(e,r){var i=se([e,r]),o=n(e,r);u(x,M,y,_,b,w,x=o[0],M=o[1],y=e,_=i[0],b=i[1],w=i[2],a,t),t.point(x,M)}function o(){S.point=e,t.lineEnd()}function c(){r(),S.point=s,S.lineEnd=l}function s(n,t){i(f=n,h=t),g=x,p=M,v=_,d=b,m=w,S.point=i}function l(){u(x,M,y,_,b,w,g,p,f,v,d,m,a,t),S.lineEnd=o,o()}var f,h,g,p,v,d,m,y,x,M,_,b,w,S={point:e,lineStart:r,lineEnd:o,polygonStart:function(){t.polygonStart(),S.lineStart=c},polygonEnd:function(){t.polygonEnd(),S.lineStart=r}};return S}function u(t,e,r,a,c,s,l,f,h,g,p,v,d,m){var y=l-t,x=f-e,M=y*y+x*x;if(M>4*i&&d--){var _=a+g,b=c+p,w=s+v,S=Math.sqrt(_*_+b*b+w*w),k=Math.asin(w/=S),E=oa(oa(w)-1)<Aa||oa(r-h)<Aa?(r+h)/2:Math.atan2(b,_),A=n(E,k),C=A[0],N=A[1],L=C-t,T=N-e,q=x*L-y*T;(q*q/M>i||oa((y*L+x*T)/M-.5)>.3||o>a*g+c*p+s*v)&&(u(t,e,r,a,c,s,C,N,E,_/=S,b/=S,w,d,m),m.point(C,N),u(C,N,E,_,b,w,l,f,h,g,p,v,d,m))}}var i=.5,o=Math.cos(30*Na),a=16;return t.precision=function(n){return arguments.length?(a=(i=n*n)>0&&16,t):Math.sqrt(i)},t}function Je(n){var t=We(function(t,e){return n([t*La,e*La])});return function(n){return tr(t(n))}}function Ge(n){this.stream=n}function Ke(n,t){return{point:t,sphere:function(){n.sphere()},lineStart:function(){n.lineStart()},lineEnd:function(){n.lineEnd()},polygonStart:function(){n.polygonStart()},polygonEnd:function(){n.polygonEnd()}}}function Qe(n){return nr(function(){return n})()}function nr(n){function t(n){return n=a(n[0]*Na,n[1]*Na),[n[0]*h+c,s-n[1]*h]}function e(n){return n=a.invert((n[0]-c)/h,(s-n[1])/h),n&&[n[0]*La,n[1]*La]}function r(){a=Ue(o=ur(m,y,x),i);var n=i(v,d);return c=g-n[0]*h,s=p+n[1]*h,u()}function u(){return l&&(l.valid=!1,l=null),t}var i,o,a,c,s,l,f=We(function(n,t){return n=i(n,t),[n[0]*h+c,s-n[1]*h]}),h=150,g=480,p=250,v=0,d=0,m=0,y=0,x=0,M=Ec,_=bt,b=null,w=null;return t.stream=function(n){return l&&(l.valid=!1),l=tr(M(o,f(_(n)))),l.valid=!0,l},t.clipAngle=function(n){return arguments.length?(M=null==n?(b=n,Ec):Re((b=+n)*Na),u()):b},t.clipExtent=function(n){return arguments.length?(w=n,_=n?Pe(n[0][0],n[0][1],n[1][0],n[1][1]):bt,u()):w},t.scale=function(n){return arguments.length?(h=+n,r()):h},t.translate=function(n){return arguments.length?(g=+n[0],p=+n[1],r()):[g,p]},t.center=function(n){return arguments.length?(v=n[0]%360*Na,d=n[1]%360*Na,r()):[v*La,d*La]},t.rotate=function(n){return arguments.length?(m=n[0]%360*Na,y=n[1]%360*Na,x=n.length>2?n[2]%360*Na:0,r()):[m*La,y*La,x*La]},Xo.rebind(t,f,"precision"),function(){return i=n.apply(this,arguments),t.invert=i.invert&&e,r()}}function tr(n){return Ke(n,function(t,e){n.point(t*Na,e*Na)})}function er(n,t){return[n,t]}function rr(n,t){return[n>Sa?n-ka:-Sa>n?n+ka:n,t]}function ur(n,t,e){return n?t||e?Ue(or(n),ar(t,e)):or(n):t||e?ar(t,e):rr}function ir(n){return function(t,e){return t+=n,[t>Sa?t-ka:-Sa>t?t+ka:t,e]}}function or(n){var t=ir(n);return t.invert=ir(-n),t}function ar(n,t){function e(n,t){var e=Math.cos(t),a=Math.cos(n)*e,c=Math.sin(n)*e,s=Math.sin(t),l=s*r+a*u;return[Math.atan2(c*i-l*o,a*r-s*u),X(l*i+c*o)]}var r=Math.cos(n),u=Math.sin(n),i=Math.cos(t),o=Math.sin(t);return e.invert=function(n,t){var e=Math.cos(t),a=Math.cos(n)*e,c=Math.sin(n)*e,s=Math.sin(t),l=s*i-c*o;return[Math.atan2(c*i+s*o,a*r+l*u),X(l*r-a*u)]},e}function cr(n,t){var e=Math.cos(n),r=Math.sin(n);return function(u,i,o,a){var c=o*t;null!=u?(u=sr(e,u),i=sr(e,i),(o>0?i>u:u>i)&&(u+=o*ka)):(u=n+o*ka,i=n-.5*c);for(var s,l=u;o>0?l>i:i>l;l-=c)a.point((s=ve([e,-r*Math.cos(l),-r*Math.sin(l)]))[0],s[1])}}function sr(n,t){var e=se(t);e[0]-=n,pe(e);var r=V(-e[1]);return((-e[2]<0?-r:r)+2*Math.PI-Aa)%(2*Math.PI)}function lr(n,t,e){var r=Xo.range(n,t-Aa,e).concat(t);return function(n){return r.map(function(t){return[n,t]})}}function fr(n,t,e){var r=Xo.range(n,t-Aa,e).concat(t);return function(n){return r.map(function(t){return[t,n]})}}function hr(n){return n.source}function gr(n){return n.target}function pr(n,t,e,r){var u=Math.cos(t),i=Math.sin(t),o=Math.cos(r),a=Math.sin(r),c=u*Math.cos(n),s=u*Math.sin(n),l=o*Math.cos(e),f=o*Math.sin(e),h=2*Math.asin(Math.sqrt(J(r-t)+u*o*J(e-n))),g=1/Math.sin(h),p=h?function(n){var t=Math.sin(n*=h)*g,e=Math.sin(h-n)*g,r=e*c+t*l,u=e*s+t*f,o=e*i+t*a;return[Math.atan2(u,r)*La,Math.atan2(o,Math.sqrt(r*r+u*u))*La]}:function(){return[n*La,t*La]};return p.distance=h,p}function vr(){function n(n,u){var i=Math.sin(u*=Na),o=Math.cos(u),a=oa((n*=Na)-t),c=Math.cos(a);Uc+=Math.atan2(Math.sqrt((a=o*Math.sin(a))*a+(a=r*i-e*o*c)*a),e*i+r*o*c),t=n,e=i,r=o}var t,e,r;jc.point=function(u,i){t=u*Na,e=Math.sin(i*=Na),r=Math.cos(i),jc.point=n},jc.lineEnd=function(){jc.point=jc.lineEnd=g}}function dr(n,t){function e(t,e){var r=Math.cos(t),u=Math.cos(e),i=n(r*u);return[i*u*Math.sin(t),i*Math.sin(e)]}return e.invert=function(n,e){var r=Math.sqrt(n*n+e*e),u=t(r),i=Math.sin(u),o=Math.cos(u);return[Math.atan2(n*i,r*o),Math.asin(r&&e*i/r)]},e}function mr(n,t){function e(n,t){var e=oa(oa(t)-Ea)<Aa?0:o/Math.pow(u(t),i);return[e*Math.sin(i*n),o-e*Math.cos(i*n)]}var r=Math.cos(n),u=function(n){return Math.tan(Sa/4+n/2)},i=n===t?Math.sin(n):Math.log(r/Math.cos(t))/Math.log(u(t)/u(n)),o=r*Math.pow(u(n),i)/i;return i?(e.invert=function(n,t){var e=o-t,r=I(i)*Math.sqrt(n*n+e*e);return[Math.atan2(n,e)/i,2*Math.atan(Math.pow(o/r,1/i))-Ea]},e):xr}function yr(n,t){function e(n,t){var e=i-t;return[e*Math.sin(u*n),i-e*Math.cos(u*n)]}var r=Math.cos(n),u=n===t?Math.sin(n):(r-Math.cos(t))/(t-n),i=r/u+n;return oa(u)<Aa?er:(e.invert=function(n,t){var e=i-t;return[Math.atan2(n,e)/u,i-I(u)*Math.sqrt(n*n+e*e)]},e)}function xr(n,t){return[n,Math.log(Math.tan(Sa/4+t/2))]}function Mr(n){var t,e=Qe(n),r=e.scale,u=e.translate,i=e.clipExtent;return e.scale=function(){var n=r.apply(e,arguments);return n===e?t?e.clipExtent(null):e:n},e.translate=function(){var n=u.apply(e,arguments);return n===e?t?e.clipExtent(null):e:n},e.clipExtent=function(n){var o=i.apply(e,arguments);if(o===e){if(t=null==n){var a=Sa*r(),c=u();i([[c[0]-a,c[1]-a],[c[0]+a,c[1]+a]])}}else t&&(o=null);return o},e.clipExtent(null)}function _r(n,t){return[Math.log(Math.tan(Sa/4+t/2)),-n]}function br(n){return n[0]}function wr(n){return n[1]}function Sr(n){for(var t=n.length,e=[0,1],r=2,u=2;t>u;u++){for(;r>1&&Z(n[e[r-2]],n[e[r-1]],n[u])<=0;)--r;e[r++]=u}return e.slice(0,r)}function kr(n,t){return n[0]-t[0]||n[1]-t[1]}function Er(n,t,e){return(e[0]-t[0])*(n[1]-t[1])<(e[1]-t[1])*(n[0]-t[0])}function Ar(n,t,e,r){var u=n[0],i=e[0],o=t[0]-u,a=r[0]-i,c=n[1],s=e[1],l=t[1]-c,f=r[1]-s,h=(a*(c-s)-f*(u-i))/(f*o-a*l);return[u+h*o,c+h*l]}function Cr(n){var t=n[0],e=n[n.length-1];return!(t[0]-e[0]||t[1]-e[1])}function Nr(){Jr(this),this.edge=this.site=this.circle=null}function Lr(n){var t=Jc.pop()||new Nr;return t.site=n,t}function Tr(n){Or(n),$c.remove(n),Jc.push(n),Jr(n)}function qr(n){var t=n.circle,e=t.x,r=t.cy,u={x:e,y:r},i=n.P,o=n.N,a=[n];Tr(n);for(var c=i;c.circle&&oa(e-c.circle.x)<Aa&&oa(r-c.circle.cy)<Aa;)i=c.P,a.unshift(c),Tr(c),c=i;a.unshift(c),Or(c);for(var s=o;s.circle&&oa(e-s.circle.x)<Aa&&oa(r-s.circle.cy)<Aa;)o=s.N,a.push(s),Tr(s),s=o;a.push(s),Or(s);var l,f=a.length;for(l=1;f>l;++l)s=a[l],c=a[l-1],$r(s.edge,c.site,s.site,u);c=a[0],s=a[f-1],s.edge=Vr(c.site,s.site,null,u),Fr(c),Fr(s)}function zr(n){for(var t,e,r,u,i=n.x,o=n.y,a=$c._;a;)if(r=Rr(a,o)-i,r>Aa)a=a.L;else{if(u=i-Dr(a,o),!(u>Aa)){r>-Aa?(t=a.P,e=a):u>-Aa?(t=a,e=a.N):t=e=a;break}if(!a.R){t=a;break}a=a.R}var c=Lr(n);if($c.insert(t,c),t||e){if(t===e)return Or(t),e=Lr(t.site),$c.insert(c,e),c.edge=e.edge=Vr(t.site,c.site),Fr(t),Fr(e),void 0;if(!e)return c.edge=Vr(t.site,c.site),void 0;Or(t),Or(e);var s=t.site,l=s.x,f=s.y,h=n.x-l,g=n.y-f,p=e.site,v=p.x-l,d=p.y-f,m=2*(h*d-g*v),y=h*h+g*g,x=v*v+d*d,M={x:(d*y-g*x)/m+l,y:(h*x-v*y)/m+f};$r(e.edge,s,p,M),c.edge=Vr(s,n,null,M),e.edge=Vr(n,p,null,M),Fr(t),Fr(e)}}function Rr(n,t){var e=n.site,r=e.x,u=e.y,i=u-t;if(!i)return r;var o=n.P;if(!o)return-1/0;e=o.site;var a=e.x,c=e.y,s=c-t;if(!s)return a;var l=a-r,f=1/i-1/s,h=l/s;return f?(-h+Math.sqrt(h*h-2*f*(l*l/(-2*s)-c+s/2+u-i/2)))/f+r:(r+a)/2}function Dr(n,t){var e=n.N;if(e)return Rr(e,t);var r=n.site;return r.y===t?r.x:1/0}function Pr(n){this.site=n,this.edges=[]}function Ur(n){for(var t,e,r,u,i,o,a,c,s,l,f=n[0][0],h=n[1][0],g=n[0][1],p=n[1][1],v=Xc,d=v.length;d--;)if(i=v[d],i&&i.prepare())for(a=i.edges,c=a.length,o=0;c>o;)l=a[o].end(),r=l.x,u=l.y,s=a[++o%c].start(),t=s.x,e=s.y,(oa(r-t)>Aa||oa(u-e)>Aa)&&(a.splice(o,0,new Br(Xr(i.site,l,oa(r-f)<Aa&&p-u>Aa?{x:f,y:oa(t-f)<Aa?e:p}:oa(u-p)<Aa&&h-r>Aa?{x:oa(e-p)<Aa?t:h,y:p}:oa(r-h)<Aa&&u-g>Aa?{x:h,y:oa(t-h)<Aa?e:g}:oa(u-g)<Aa&&r-f>Aa?{x:oa(e-g)<Aa?t:f,y:g}:null),i.site,null)),++c)}function jr(n,t){return t.angle-n.angle}function Hr(){Jr(this),this.x=this.y=this.arc=this.site=this.cy=null}function Fr(n){var t=n.P,e=n.N;if(t&&e){var r=t.site,u=n.site,i=e.site;if(r!==i){var o=u.x,a=u.y,c=r.x-o,s=r.y-a,l=i.x-o,f=i.y-a,h=2*(c*f-s*l);if(!(h>=-Ca)){var g=c*c+s*s,p=l*l+f*f,v=(f*g-s*p)/h,d=(c*p-l*g)/h,f=d+a,m=Gc.pop()||new Hr;m.arc=n,m.site=u,m.x=v+o,m.y=f+Math.sqrt(v*v+d*d),m.cy=f,n.circle=m;for(var y=null,x=Wc._;x;)if(m.y<x.y||m.y===x.y&&m.x<=x.x){if(!x.L){y=x.P;break}x=x.L}else{if(!x.R){y=x;break}x=x.R}Wc.insert(y,m),y||(Bc=m)}}}}function Or(n){var t=n.circle;t&&(t.P||(Bc=t.N),Wc.remove(t),Gc.push(t),Jr(t),n.circle=null)}function Yr(n){for(var t,e=Vc,r=De(n[0][0],n[0][1],n[1][0],n[1][1]),u=e.length;u--;)t=e[u],(!Ir(t,n)||!r(t)||oa(t.a.x-t.b.x)<Aa&&oa(t.a.y-t.b.y)<Aa)&&(t.a=t.b=null,e.splice(u,1))}function Ir(n,t){var e=n.b;if(e)return!0;var r,u,i=n.a,o=t[0][0],a=t[1][0],c=t[0][1],s=t[1][1],l=n.l,f=n.r,h=l.x,g=l.y,p=f.x,v=f.y,d=(h+p)/2,m=(g+v)/2;if(v===g){if(o>d||d>=a)return;if(h>p){if(i){if(i.y>=s)return}else i={x:d,y:c};e={x:d,y:s}}else{if(i){if(i.y<c)return}else i={x:d,y:s};e={x:d,y:c}}}else if(r=(h-p)/(v-g),u=m-r*d,-1>r||r>1)if(h>p){if(i){if(i.y>=s)return}else i={x:(c-u)/r,y:c};e={x:(s-u)/r,y:s}}else{if(i){if(i.y<c)return}else i={x:(s-u)/r,y:s};e={x:(c-u)/r,y:c}}else if(v>g){if(i){if(i.x>=a)return}else i={x:o,y:r*o+u};e={x:a,y:r*a+u}}else{if(i){if(i.x<o)return}else i={x:a,y:r*a+u};e={x:o,y:r*o+u}}return n.a=i,n.b=e,!0}function Zr(n,t){this.l=n,this.r=t,this.a=this.b=null}function Vr(n,t,e,r){var u=new Zr(n,t);return Vc.push(u),e&&$r(u,n,t,e),r&&$r(u,t,n,r),Xc[n.i].edges.push(new Br(u,n,t)),Xc[t.i].edges.push(new Br(u,t,n)),u}function Xr(n,t,e){var r=new Zr(n,null);return r.a=t,r.b=e,Vc.push(r),r}function $r(n,t,e,r){n.a||n.b?n.l===e?n.b=r:n.a=r:(n.a=r,n.l=t,n.r=e)}function Br(n,t,e){var r=n.a,u=n.b;this.edge=n,this.site=t,this.angle=e?Math.atan2(e.y-t.y,e.x-t.x):n.l===t?Math.atan2(u.x-r.x,r.y-u.y):Math.atan2(r.x-u.x,u.y-r.y)}function Wr(){this._=null}function Jr(n){n.U=n.C=n.L=n.R=n.P=n.N=null}function Gr(n,t){var e=t,r=t.R,u=e.U;u?u.L===e?u.L=r:u.R=r:n._=r,r.U=u,e.U=r,e.R=r.L,e.R&&(e.R.U=e),r.L=e}function Kr(n,t){var e=t,r=t.L,u=e.U;u?u.L===e?u.L=r:u.R=r:n._=r,r.U=u,e.U=r,e.L=r.R,e.L&&(e.L.U=e),r.R=e}function Qr(n){for(;n.L;)n=n.L;return n}function nu(n,t){var e,r,u,i=n.sort(tu).pop();for(Vc=[],Xc=new Array(n.length),$c=new Wr,Wc=new Wr;;)if(u=Bc,i&&(!u||i.y<u.y||i.y===u.y&&i.x<u.x))(i.x!==e||i.y!==r)&&(Xc[i.i]=new Pr(i),zr(i),e=i.x,r=i.y),i=n.pop();else{if(!u)break;qr(u.arc)}t&&(Yr(t),Ur(t));var o={cells:Xc,edges:Vc};return $c=Wc=Vc=Xc=null,o}function tu(n,t){return t.y-n.y||t.x-n.x}function eu(n,t,e){return(n.x-e.x)*(t.y-n.y)-(n.x-t.x)*(e.y-n.y)}function ru(n){return n.x}function uu(n){return n.y}function iu(){return{leaf:!0,nodes:[],point:null,x:null,y:null}}function ou(n,t,e,r,u,i){if(!n(t,e,r,u,i)){var o=.5*(e+u),a=.5*(r+i),c=t.nodes;c[0]&&ou(n,c[0],e,r,o,a),c[1]&&ou(n,c[1],o,r,u,a),c[2]&&ou(n,c[2],e,a,o,i),c[3]&&ou(n,c[3],o,a,u,i)}}function au(n,t){n=Xo.rgb(n),t=Xo.rgb(t);var e=n.r,r=n.g,u=n.b,i=t.r-e,o=t.g-r,a=t.b-u;return function(n){return"#"+vt(Math.round(e+i*n))+vt(Math.round(r+o*n))+vt(Math.round(u+a*n))}}function cu(n,t){var e,r={},u={};for(e in n)e in t?r[e]=fu(n[e],t[e]):u[e]=n[e];for(e in t)e in n||(u[e]=t[e]);return function(n){for(e in r)u[e]=r[e](n);return u}}function su(n,t){return t-=n=+n,function(e){return n+t*e}}function lu(n,t){var e,r,u,i,o,a=0,c=0,s=[],l=[];for(n+="",t+="",Qc.lastIndex=0,r=0;e=Qc.exec(t);++r)e.index&&s.push(t.substring(a,c=e.index)),l.push({i:s.length,x:e[0]}),s.push(null),a=Qc.lastIndex;for(a<t.length&&s.push(t.substring(a)),r=0,i=l.length;(e=Qc.exec(n))&&i>r;++r)if(o=l[r],o.x==e[0]){if(o.i)if(null==s[o.i+1])for(s[o.i-1]+=o.x,s.splice(o.i,1),u=r+1;i>u;++u)l[u].i--;else for(s[o.i-1]+=o.x+s[o.i+1],s.splice(o.i,2),u=r+1;i>u;++u)l[u].i-=2;else if(null==s[o.i+1])s[o.i]=o.x;else for(s[o.i]=o.x+s[o.i+1],s.splice(o.i+1,1),u=r+1;i>u;++u)l[u].i--;l.splice(r,1),i--,r--}else o.x=su(parseFloat(e[0]),parseFloat(o.x));for(;i>r;)o=l.pop(),null==s[o.i+1]?s[o.i]=o.x:(s[o.i]=o.x+s[o.i+1],s.splice(o.i+1,1)),i--;return 1===s.length?null==s[0]?(o=l[0].x,function(n){return o(n)+""}):function(){return t}:function(n){for(r=0;i>r;++r)s[(o=l[r]).i]=o.x(n);return s.join("")}}function fu(n,t){for(var e,r=Xo.interpolators.length;--r>=0&&!(e=Xo.interpolators[r](n,t)););return e}function hu(n,t){var e,r=[],u=[],i=n.length,o=t.length,a=Math.min(n.length,t.length);for(e=0;a>e;++e)r.push(fu(n[e],t[e]));for(;i>e;++e)u[e]=n[e];for(;o>e;++e)u[e]=t[e];return function(n){for(e=0;a>e;++e)u[e]=r[e](n);return u}}function gu(n){return function(t){return 0>=t?0:t>=1?1:n(t)}}function pu(n){return function(t){return 1-n(1-t)}}function vu(n){return function(t){return.5*(.5>t?n(2*t):2-n(2-2*t))}}function du(n){return n*n}function mu(n){return n*n*n}function yu(n){if(0>=n)return 0;if(n>=1)return 1;var t=n*n,e=t*n;return 4*(.5>n?e:3*(n-t)+e-.75)}function xu(n){return function(t){return Math.pow(t,n)}}function Mu(n){return 1-Math.cos(n*Ea)}function _u(n){return Math.pow(2,10*(n-1))}function bu(n){return 1-Math.sqrt(1-n*n)}function wu(n,t){var e;return arguments.length<2&&(t=.45),arguments.length?e=t/ka*Math.asin(1/n):(n=1,e=t/4),function(r){return 1+n*Math.pow(2,-10*r)*Math.sin((r-e)*ka/t)}}function Su(n){return n||(n=1.70158),function(t){return t*t*((n+1)*t-n)}}function ku(n){return 1/2.75>n?7.5625*n*n:2/2.75>n?7.5625*(n-=1.5/2.75)*n+.75:2.5/2.75>n?7.5625*(n-=2.25/2.75)*n+.9375:7.5625*(n-=2.625/2.75)*n+.984375}function Eu(n,t){n=Xo.hcl(n),t=Xo.hcl(t);var e=n.h,r=n.c,u=n.l,i=t.h-e,o=t.c-r,a=t.l-u;return isNaN(o)&&(o=0,r=isNaN(r)?t.c:r),isNaN(i)?(i=0,e=isNaN(e)?t.h:e):i>180?i-=360:-180>i&&(i+=360),function(n){return rt(e+i*n,r+o*n,u+a*n)+""}}function Au(n,t){n=Xo.hsl(n),t=Xo.hsl(t);var e=n.h,r=n.s,u=n.l,i=t.h-e,o=t.s-r,a=t.l-u;return isNaN(o)&&(o=0,r=isNaN(r)?t.s:r),isNaN(i)?(i=0,e=isNaN(e)?t.h:e):i>180?i-=360:-180>i&&(i+=360),function(n){return nt(e+i*n,r+o*n,u+a*n)+""}}function Cu(n,t){n=Xo.lab(n),t=Xo.lab(t);var e=n.l,r=n.a,u=n.b,i=t.l-e,o=t.a-r,a=t.b-u;return function(n){return ot(e+i*n,r+o*n,u+a*n)+""}}function Nu(n,t){return t-=n,function(e){return Math.round(n+t*e)}}function Lu(n){var t=[n.a,n.b],e=[n.c,n.d],r=qu(t),u=Tu(t,e),i=qu(zu(e,t,-u))||0;t[0]*e[1]<e[0]*t[1]&&(t[0]*=-1,t[1]*=-1,r*=-1,u*=-1),this.rotate=(r?Math.atan2(t[1],t[0]):Math.atan2(-e[0],e[1]))*La,this.translate=[n.e,n.f],this.scale=[r,i],this.skew=i?Math.atan2(u,i)*La:0}function Tu(n,t){return n[0]*t[0]+n[1]*t[1]}function qu(n){var t=Math.sqrt(Tu(n,n));return t&&(n[0]/=t,n[1]/=t),t}function zu(n,t,e){return n[0]+=e*t[0],n[1]+=e*t[1],n}function Ru(n,t){var e,r=[],u=[],i=Xo.transform(n),o=Xo.transform(t),a=i.translate,c=o.translate,s=i.rotate,l=o.rotate,f=i.skew,h=o.skew,g=i.scale,p=o.scale;return a[0]!=c[0]||a[1]!=c[1]?(r.push("translate(",null,",",null,")"),u.push({i:1,x:su(a[0],c[0])},{i:3,x:su(a[1],c[1])})):c[0]||c[1]?r.push("translate("+c+")"):r.push(""),s!=l?(s-l>180?l+=360:l-s>180&&(s+=360),u.push({i:r.push(r.pop()+"rotate(",null,")")-2,x:su(s,l)})):l&&r.push(r.pop()+"rotate("+l+")"),f!=h?u.push({i:r.push(r.pop()+"skewX(",null,")")-2,x:su(f,h)}):h&&r.push(r.pop()+"skewX("+h+")"),g[0]!=p[0]||g[1]!=p[1]?(e=r.push(r.pop()+"scale(",null,",",null,")"),u.push({i:e-4,x:su(g[0],p[0])},{i:e-2,x:su(g[1],p[1])})):(1!=p[0]||1!=p[1])&&r.push(r.pop()+"scale("+p+")"),e=u.length,function(n){for(var t,i=-1;++i<e;)r[(t=u[i]).i]=t.x(n);return r.join("")}}function Du(n,t){return t=t-(n=+n)?1/(t-n):0,function(e){return(e-n)*t}}function Pu(n,t){return t=t-(n=+n)?1/(t-n):0,function(e){return Math.max(0,Math.min(1,(e-n)*t))}}function Uu(n){for(var t=n.source,e=n.target,r=Hu(t,e),u=[t];t!==r;)t=t.parent,u.push(t);for(var i=u.length;e!==r;)u.splice(i,0,e),e=e.parent;return u}function ju(n){for(var t=[],e=n.parent;null!=e;)t.push(n),n=e,e=e.parent;return t.push(n),t}function Hu(n,t){if(n===t)return n;for(var e=ju(n),r=ju(t),u=e.pop(),i=r.pop(),o=null;u===i;)o=u,u=e.pop(),i=r.pop();return o}function Fu(n){n.fixed|=2}function Ou(n){n.fixed&=-7}function Yu(n){n.fixed|=4,n.px=n.x,n.py=n.y}function Iu(n){n.fixed&=-5}function Zu(n,t,e){var r=0,u=0;if(n.charge=0,!n.leaf)for(var i,o=n.nodes,a=o.length,c=-1;++c<a;)i=o[c],null!=i&&(Zu(i,t,e),n.charge+=i.charge,r+=i.charge*i.cx,u+=i.charge*i.cy);if(n.point){n.leaf||(n.point.x+=Math.random()-.5,n.point.y+=Math.random()-.5);var s=t*e[n.point.index];n.charge+=n.pointCharge=s,r+=s*n.point.x,u+=s*n.point.y}n.cx=r/n.charge,n.cy=u/n.charge}function Vu(n,t){return Xo.rebind(n,t,"sort","children","value"),n.nodes=n,n.links=Wu,n}function Xu(n){return n.children}function $u(n){return n.value}function Bu(n,t){return t.value-n.value}function Wu(n){return Xo.merge(n.map(function(n){return(n.children||[]).map(function(t){return{source:n,target:t}})}))}function Ju(n){return n.x}function Gu(n){return n.y}function Ku(n,t,e){n.y0=t,n.y=e}function Qu(n){return Xo.range(n.length)}function ni(n){for(var t=-1,e=n[0].length,r=[];++t<e;)r[t]=0;return r}function ti(n){for(var t,e=1,r=0,u=n[0][1],i=n.length;i>e;++e)(t=n[e][1])>u&&(r=e,u=t);return r}function ei(n){return n.reduce(ri,0)}function ri(n,t){return n+t[1]}function ui(n,t){return ii(n,Math.ceil(Math.log(t.length)/Math.LN2+1))}function ii(n,t){for(var e=-1,r=+n[0],u=(n[1]-r)/t,i=[];++e<=t;)i[e]=u*e+r;return i}function oi(n){return[Xo.min(n),Xo.max(n)]}function ai(n,t){return n.parent==t.parent?1:2}function ci(n){var t=n.children;return t&&t.length?t[0]:n._tree.thread}function si(n){var t,e=n.children;return e&&(t=e.length)?e[t-1]:n._tree.thread}function li(n,t){var e=n.children;if(e&&(u=e.length))for(var r,u,i=-1;++i<u;)t(r=li(e[i],t),n)>0&&(n=r);return n}function fi(n,t){return n.x-t.x}function hi(n,t){return t.x-n.x}function gi(n,t){return n.depth-t.depth}function pi(n,t){function e(n,r){var u=n.children;if(u&&(o=u.length))for(var i,o,a=null,c=-1;++c<o;)i=u[c],e(i,a),a=i;t(n,r)}e(n,null)}function vi(n){for(var t,e=0,r=0,u=n.children,i=u.length;--i>=0;)t=u[i]._tree,t.prelim+=e,t.mod+=e,e+=t.shift+(r+=t.change)}function di(n,t,e){n=n._tree,t=t._tree;var r=e/(t.number-n.number);n.change+=r,t.change-=r,t.shift+=e,t.prelim+=e,t.mod+=e}function mi(n,t,e){return n._tree.ancestor.parent==t.parent?n._tree.ancestor:e}function yi(n,t){return n.value-t.value}function xi(n,t){var e=n._pack_next;n._pack_next=t,t._pack_prev=n,t._pack_next=e,e._pack_prev=t}function Mi(n,t){n._pack_next=t,t._pack_prev=n}function _i(n,t){var e=t.x-n.x,r=t.y-n.y,u=n.r+t.r;return.999*u*u>e*e+r*r}function bi(n){function t(n){l=Math.min(n.x-n.r,l),f=Math.max(n.x+n.r,f),h=Math.min(n.y-n.r,h),g=Math.max(n.y+n.r,g)}if((e=n.children)&&(s=e.length)){var e,r,u,i,o,a,c,s,l=1/0,f=-1/0,h=1/0,g=-1/0;if(e.forEach(wi),r=e[0],r.x=-r.r,r.y=0,t(r),s>1&&(u=e[1],u.x=u.r,u.y=0,t(u),s>2))for(i=e[2],Ei(r,u,i),t(i),xi(r,i),r._pack_prev=i,xi(i,u),u=r._pack_next,o=3;s>o;o++){Ei(r,u,i=e[o]);var p=0,v=1,d=1;for(a=u._pack_next;a!==u;a=a._pack_next,v++)if(_i(a,i)){p=1;break}if(1==p)for(c=r._pack_prev;c!==a._pack_prev&&!_i(c,i);c=c._pack_prev,d++);p?(d>v||v==d&&u.r<r.r?Mi(r,u=a):Mi(r=c,u),o--):(xi(r,i),u=i,t(i))}var m=(l+f)/2,y=(h+g)/2,x=0;for(o=0;s>o;o++)i=e[o],i.x-=m,i.y-=y,x=Math.max(x,i.r+Math.sqrt(i.x*i.x+i.y*i.y));n.r=x,e.forEach(Si)}}function wi(n){n._pack_next=n._pack_prev=n}function Si(n){delete n._pack_next,delete n._pack_prev}function ki(n,t,e,r){var u=n.children;if(n.x=t+=r*n.x,n.y=e+=r*n.y,n.r*=r,u)for(var i=-1,o=u.length;++i<o;)ki(u[i],t,e,r)}function Ei(n,t,e){var r=n.r+e.r,u=t.x-n.x,i=t.y-n.y;if(r&&(u||i)){var o=t.r+e.r,a=u*u+i*i;o*=o,r*=r;var c=.5+(r-o)/(2*a),s=Math.sqrt(Math.max(0,2*o*(r+a)-(r-=a)*r-o*o))/(2*a);e.x=n.x+c*u+s*i,e.y=n.y+c*i-s*u}else e.x=n.x+r,e.y=n.y}function Ai(n){return 1+Xo.max(n,function(n){return n.y})}function Ci(n){return n.reduce(function(n,t){return n+t.x},0)/n.length}function Ni(n){var t=n.children;return t&&t.length?Ni(t[0]):n}function Li(n){var t,e=n.children;return e&&(t=e.length)?Li(e[t-1]):n}function Ti(n){return{x:n.x,y:n.y,dx:n.dx,dy:n.dy}}function qi(n,t){var e=n.x+t[3],r=n.y+t[0],u=n.dx-t[1]-t[3],i=n.dy-t[0]-t[2];return 0>u&&(e+=u/2,u=0),0>i&&(r+=i/2,i=0),{x:e,y:r,dx:u,dy:i}}function zi(n){var t=n[0],e=n[n.length-1];return e>t?[t,e]:[e,t]}function Ri(n){return n.rangeExtent?n.rangeExtent():zi(n.range())}function Di(n,t,e,r){var u=e(n[0],n[1]),i=r(t[0],t[1]);return function(n){return i(u(n))}}function Pi(n,t){var e,r=0,u=n.length-1,i=n[r],o=n[u];return i>o&&(e=r,r=u,u=e,e=i,i=o,o=e),n[r]=t.floor(i),n[u]=t.ceil(o),n}function Ui(n){return n?{floor:function(t){return Math.floor(t/n)*n},ceil:function(t){return Math.ceil(t/n)*n}}:ls}function ji(n,t,e,r){var u=[],i=[],o=0,a=Math.min(n.length,t.length)-1;for(n[a]<n[0]&&(n=n.slice().reverse(),t=t.slice().reverse());++o<=a;)u.push(e(n[o-1],n[o])),i.push(r(t[o-1],t[o]));return function(t){var e=Xo.bisect(n,t,1,a)-1;return i[e](u[e](t))}}function Hi(n,t,e,r){function u(){var u=Math.min(n.length,t.length)>2?ji:Di,c=r?Pu:Du;return o=u(n,t,c,e),a=u(t,n,c,fu),i}function i(n){return o(n)}var o,a;return i.invert=function(n){return a(n)},i.domain=function(t){return arguments.length?(n=t.map(Number),u()):n},i.range=function(n){return arguments.length?(t=n,u()):t},i.rangeRound=function(n){return i.range(n).interpolate(Nu)},i.clamp=function(n){return arguments.length?(r=n,u()):r},i.interpolate=function(n){return arguments.length?(e=n,u()):e},i.ticks=function(t){return Ii(n,t)},i.tickFormat=function(t,e){return Zi(n,t,e)},i.nice=function(t){return Oi(n,t),u()},i.copy=function(){return Hi(n,t,e,r)},u()}function Fi(n,t){return Xo.rebind(n,t,"range","rangeRound","interpolate","clamp")}function Oi(n,t){return Pi(n,Ui(Yi(n,t)[2]))}function Yi(n,t){null==t&&(t=10);var e=zi(n),r=e[1]-e[0],u=Math.pow(10,Math.floor(Math.log(r/t)/Math.LN10)),i=t/r*u;return.15>=i?u*=10:.35>=i?u*=5:.75>=i&&(u*=2),e[0]=Math.ceil(e[0]/u)*u,e[1]=Math.floor(e[1]/u)*u+.5*u,e[2]=u,e}function Ii(n,t){return Xo.range.apply(Xo,Yi(n,t))}function Zi(n,t,e){var r=Yi(n,t);return Xo.format(e?e.replace(Qa,function(n,t,e,u,i,o,a,c,s,l){return[t,e,u,i,o,a,c,s||"."+Xi(l,r),l].join("")}):",."+Vi(r[2])+"f")}function Vi(n){return-Math.floor(Math.log(n)/Math.LN10+.01)}function Xi(n,t){var e=Vi(t[2]);return n in fs?Math.abs(e-Vi(Math.max(Math.abs(t[0]),Math.abs(t[1]))))+ +("e"!==n):e-2*("%"===n)}function $i(n,t,e,r){function u(n){return(e?Math.log(0>n?0:n):-Math.log(n>0?0:-n))/Math.log(t)}function i(n){return e?Math.pow(t,n):-Math.pow(t,-n)}function o(t){return n(u(t))}return o.invert=function(t){return i(n.invert(t))},o.domain=function(t){return arguments.length?(e=t[0]>=0,n.domain((r=t.map(Number)).map(u)),o):r},o.base=function(e){return arguments.length?(t=+e,n.domain(r.map(u)),o):t},o.nice=function(){var t=Pi(r.map(u),e?Math:gs);return n.domain(t),r=t.map(i),o},o.ticks=function(){var n=zi(r),o=[],a=n[0],c=n[1],s=Math.floor(u(a)),l=Math.ceil(u(c)),f=t%1?2:t;if(isFinite(l-s)){if(e){for(;l>s;s++)for(var h=1;f>h;h++)o.push(i(s)*h);o.push(i(s))}else for(o.push(i(s));s++<l;)for(var h=f-1;h>0;h--)o.push(i(s)*h);for(s=0;o[s]<a;s++);for(l=o.length;o[l-1]>c;l--);o=o.slice(s,l)}return o},o.tickFormat=function(n,t){if(!arguments.length)return hs;arguments.length<2?t=hs:"function"!=typeof t&&(t=Xo.format(t));var r,a=Math.max(.1,n/o.ticks().length),c=e?(r=1e-12,Math.ceil):(r=-1e-12,Math.floor);return function(n){return n/i(c(u(n)+r))<=a?t(n):""}},o.copy=function(){return $i(n.copy(),t,e,r)},Fi(o,n)}function Bi(n,t,e){function r(t){return n(u(t))}var u=Wi(t),i=Wi(1/t);return r.invert=function(t){return i(n.invert(t))},r.domain=function(t){return arguments.length?(n.domain((e=t.map(Number)).map(u)),r):e},r.ticks=function(n){return Ii(e,n)},r.tickFormat=function(n,t){return Zi(e,n,t)},r.nice=function(n){return r.domain(Oi(e,n))},r.exponent=function(o){return arguments.length?(u=Wi(t=o),i=Wi(1/t),n.domain(e.map(u)),r):t},r.copy=function(){return Bi(n.copy(),t,e)},Fi(r,n)}function Wi(n){return function(t){return 0>t?-Math.pow(-t,n):Math.pow(t,n)}}function Ji(n,t){function e(e){return o[((i.get(e)||"range"===t.t&&i.set(e,n.push(e)))-1)%o.length]}function r(t,e){return Xo.range(n.length).map(function(n){return t+e*n})}var i,o,a;return e.domain=function(r){if(!arguments.length)return n;n=[],i=new u;for(var o,a=-1,c=r.length;++a<c;)i.has(o=r[a])||i.set(o,n.push(o));return e[t.t].apply(e,t.a)},e.range=function(n){return arguments.length?(o=n,a=0,t={t:"range",a:arguments},e):o},e.rangePoints=function(u,i){arguments.length<2&&(i=0);var c=u[0],s=u[1],l=(s-c)/(Math.max(1,n.length-1)+i);return o=r(n.length<2?(c+s)/2:c+l*i/2,l),a=0,t={t:"rangePoints",a:arguments},e},e.rangeBands=function(u,i,c){arguments.length<2&&(i=0),arguments.length<3&&(c=i);var s=u[1]<u[0],l=u[s-0],f=u[1-s],h=(f-l)/(n.length-i+2*c);return o=r(l+h*c,h),s&&o.reverse(),a=h*(1-i),t={t:"rangeBands",a:arguments},e},e.rangeRoundBands=function(u,i,c){arguments.length<2&&(i=0),arguments.length<3&&(c=i);var s=u[1]<u[0],l=u[s-0],f=u[1-s],h=Math.floor((f-l)/(n.length-i+2*c)),g=f-l-(n.length-i)*h;return o=r(l+Math.round(g/2),h),s&&o.reverse(),a=Math.round(h*(1-i)),t={t:"rangeRoundBands",a:arguments},e},e.rangeBand=function(){return a},e.rangeExtent=function(){return zi(t.a[0])},e.copy=function(){return Ji(n,t)},e.domain(n)}function Gi(n,t){function e(){var e=0,i=t.length;for(u=[];++e<i;)u[e-1]=Xo.quantile(n,e/i);return r}function r(n){return isNaN(n=+n)?void 0:t[Xo.bisect(u,n)]}var u;return r.domain=function(t){return arguments.length?(n=t.filter(function(n){return!isNaN(n)}).sort(Xo.ascending),e()):n},r.range=function(n){return arguments.length?(t=n,e()):t},r.quantiles=function(){return u},r.invertExtent=function(e){return e=t.indexOf(e),0>e?[0/0,0/0]:[e>0?u[e-1]:n[0],e<u.length?u[e]:n[n.length-1]]},r.copy=function(){return Gi(n,t)},e()}function Ki(n,t,e){function r(t){return e[Math.max(0,Math.min(o,Math.floor(i*(t-n))))]}function u(){return i=e.length/(t-n),o=e.length-1,r}var i,o;return r.domain=function(e){return arguments.length?(n=+e[0],t=+e[e.length-1],u()):[n,t]},r.range=function(n){return arguments.length?(e=n,u()):e},r.invertExtent=function(t){return t=e.indexOf(t),t=0>t?0/0:t/i+n,[t,t+1/i]},r.copy=function(){return Ki(n,t,e)},u()}function Qi(n,t){function e(e){return e>=e?t[Xo.bisect(n,e)]:void 0}return e.domain=function(t){return arguments.length?(n=t,e):n},e.range=function(n){return arguments.length?(t=n,e):t},e.invertExtent=function(e){return e=t.indexOf(e),[n[e-1],n[e]]},e.copy=function(){return Qi(n,t)},e}function no(n){function t(n){return+n}return t.invert=t,t.domain=t.range=function(e){return arguments.length?(n=e.map(t),t):n},t.ticks=function(t){return Ii(n,t)},t.tickFormat=function(t,e){return Zi(n,t,e)},t.copy=function(){return no(n)},t}function to(n){return n.innerRadius}function eo(n){return n.outerRadius}function ro(n){return n.startAngle}function uo(n){return n.endAngle}function io(n){function t(t){function o(){s.push("M",i(n(l),a))}for(var c,s=[],l=[],f=-1,h=t.length,g=_t(e),p=_t(r);++f<h;)u.call(this,c=t[f],f)?l.push([+g.call(this,c,f),+p.call(this,c,f)]):l.length&&(o(),l=[]);return l.length&&o(),s.length?s.join(""):null}var e=br,r=wr,u=be,i=oo,o=i.key,a=.7;return t.x=function(n){return arguments.length?(e=n,t):e},t.y=function(n){return arguments.length?(r=n,t):r},t.defined=function(n){return arguments.length?(u=n,t):u},t.interpolate=function(n){return arguments.length?(o="function"==typeof n?i=n:(i=Ms.get(n)||oo).key,t):o},t.tension=function(n){return arguments.length?(a=n,t):a},t}function oo(n){return n.join("L")}function ao(n){return oo(n)+"Z"}function co(n){for(var t=0,e=n.length,r=n[0],u=[r[0],",",r[1]];++t<e;)u.push("H",(r[0]+(r=n[t])[0])/2,"V",r[1]);return e>1&&u.push("H",r[0]),u.join("")}function so(n){for(var t=0,e=n.length,r=n[0],u=[r[0],",",r[1]];++t<e;)u.push("V",(r=n[t])[1],"H",r[0]);return u.join("")}function lo(n){for(var t=0,e=n.length,r=n[0],u=[r[0],",",r[1]];++t<e;)u.push("H",(r=n[t])[0],"V",r[1]);return u.join("")}function fo(n,t){return n.length<4?oo(n):n[1]+po(n.slice(1,n.length-1),vo(n,t))}function ho(n,t){return n.length<3?oo(n):n[0]+po((n.push(n[0]),n),vo([n[n.length-2]].concat(n,[n[1]]),t))}function go(n,t){return n.length<3?oo(n):n[0]+po(n,vo(n,t))}function po(n,t){if(t.length<1||n.length!=t.length&&n.length!=t.length+2)return oo(n);var e=n.length!=t.length,r="",u=n[0],i=n[1],o=t[0],a=o,c=1;if(e&&(r+="Q"+(i[0]-2*o[0]/3)+","+(i[1]-2*o[1]/3)+","+i[0]+","+i[1],u=n[1],c=2),t.length>1){a=t[1],i=n[c],c++,r+="C"+(u[0]+o[0])+","+(u[1]+o[1])+","+(i[0]-a[0])+","+(i[1]-a[1])+","+i[0]+","+i[1];for(var s=2;s<t.length;s++,c++)i=n[c],a=t[s],r+="S"+(i[0]-a[0])+","+(i[1]-a[1])+","+i[0]+","+i[1]}if(e){var l=n[c];r+="Q"+(i[0]+2*a[0]/3)+","+(i[1]+2*a[1]/3)+","+l[0]+","+l[1]}return r}function vo(n,t){for(var e,r=[],u=(1-t)/2,i=n[0],o=n[1],a=1,c=n.length;++a<c;)e=i,i=o,o=n[a],r.push([u*(o[0]-e[0]),u*(o[1]-e[1])]);return r}function mo(n){if(n.length<3)return oo(n);var t=1,e=n.length,r=n[0],u=r[0],i=r[1],o=[u,u,u,(r=n[1])[0]],a=[i,i,i,r[1]],c=[u,",",i,"L",_o(ws,o),",",_o(ws,a)];for(n.push(n[e-1]);++t<=e;)r=n[t],o.shift(),o.push(r[0]),a.shift(),a.push(r[1]),bo(c,o,a);return n.pop(),c.push("L",r),c.join("")}function yo(n){if(n.length<4)return oo(n);for(var t,e=[],r=-1,u=n.length,i=[0],o=[0];++r<3;)t=n[r],i.push(t[0]),o.push(t[1]);for(e.push(_o(ws,i)+","+_o(ws,o)),--r;++r<u;)t=n[r],i.shift(),i.push(t[0]),o.shift(),o.push(t[1]),bo(e,i,o);return e.join("")}function xo(n){for(var t,e,r=-1,u=n.length,i=u+4,o=[],a=[];++r<4;)e=n[r%u],o.push(e[0]),a.push(e[1]);for(t=[_o(ws,o),",",_o(ws,a)],--r;++r<i;)e=n[r%u],o.shift(),o.push(e[0]),a.shift(),a.push(e[1]),bo(t,o,a);return t.join("")}function Mo(n,t){var e=n.length-1;if(e)for(var r,u,i=n[0][0],o=n[0][1],a=n[e][0]-i,c=n[e][1]-o,s=-1;++s<=e;)r=n[s],u=s/e,r[0]=t*r[0]+(1-t)*(i+u*a),r[1]=t*r[1]+(1-t)*(o+u*c);return mo(n)}function _o(n,t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]+n[3]*t[3]}function bo(n,t,e){n.push("C",_o(_s,t),",",_o(_s,e),",",_o(bs,t),",",_o(bs,e),",",_o(ws,t),",",_o(ws,e))}function wo(n,t){return(t[1]-n[1])/(t[0]-n[0])}function So(n){for(var t=0,e=n.length-1,r=[],u=n[0],i=n[1],o=r[0]=wo(u,i);++t<e;)r[t]=(o+(o=wo(u=i,i=n[t+1])))/2;return r[t]=o,r}function ko(n){for(var t,e,r,u,i=[],o=So(n),a=-1,c=n.length-1;++a<c;)t=wo(n[a],n[a+1]),oa(t)<Aa?o[a]=o[a+1]=0:(e=o[a]/t,r=o[a+1]/t,u=e*e+r*r,u>9&&(u=3*t/Math.sqrt(u),o[a]=u*e,o[a+1]=u*r));for(a=-1;++a<=c;)u=(n[Math.min(c,a+1)][0]-n[Math.max(0,a-1)][0])/(6*(1+o[a]*o[a])),i.push([u||0,o[a]*u||0]);return i}function Eo(n){return n.length<3?oo(n):n[0]+po(n,ko(n))}function Ao(n){for(var t,e,r,u=-1,i=n.length;++u<i;)t=n[u],e=t[0],r=t[1]+ys,t[0]=e*Math.cos(r),t[1]=e*Math.sin(r);return n}function Co(n){function t(t){function c(){v.push("M",a(n(m),f),l,s(n(d.reverse()),f),"Z")}for(var h,g,p,v=[],d=[],m=[],y=-1,x=t.length,M=_t(e),_=_t(u),b=e===r?function(){return g}:_t(r),w=u===i?function(){return p}:_t(i);++y<x;)o.call(this,h=t[y],y)?(d.push([g=+M.call(this,h,y),p=+_.call(this,h,y)]),m.push([+b.call(this,h,y),+w.call(this,h,y)])):d.length&&(c(),d=[],m=[]);return d.length&&c(),v.length?v.join(""):null}var e=br,r=br,u=0,i=wr,o=be,a=oo,c=a.key,s=a,l="L",f=.7;return t.x=function(n){return arguments.length?(e=r=n,t):r},t.x0=function(n){return arguments.length?(e=n,t):e},t.x1=function(n){return arguments.length?(r=n,t):r},t.y=function(n){return arguments.length?(u=i=n,t):i},t.y0=function(n){return arguments.length?(u=n,t):u},t.y1=function(n){return arguments.length?(i=n,t):i},t.defined=function(n){return arguments.length?(o=n,t):o},t.interpolate=function(n){return arguments.length?(c="function"==typeof n?a=n:(a=Ms.get(n)||oo).key,s=a.reverse||a,l=a.closed?"M":"L",t):c},t.tension=function(n){return arguments.length?(f=n,t):f},t}function No(n){return n.radius}function Lo(n){return[n.x,n.y]}function To(n){return function(){var t=n.apply(this,arguments),e=t[0],r=t[1]+ys;return[e*Math.cos(r),e*Math.sin(r)]}}function qo(){return 64}function zo(){return"circle"}function Ro(n){var t=Math.sqrt(n/Sa);return"M0,"+t+"A"+t+","+t+" 0 1,1 0,"+-t+"A"+t+","+t+" 0 1,1 0,"+t+"Z"}function Do(n,t){return fa(n,Ns),n.id=t,n}function Po(n,t,e,r){var u=n.id;return R(n,"function"==typeof e?function(n,i,o){n.__transition__[u].tween.set(t,r(e.call(n,n.__data__,i,o)))}:(e=r(e),function(n){n.__transition__[u].tween.set(t,e)}))}function Uo(n){return null==n&&(n=""),function(){this.textContent=n}}function jo(n,t,e,r){var i=n.__transition__||(n.__transition__={active:0,count:0}),o=i[e];if(!o){var a=r.time;o=i[e]={tween:new u,time:a,ease:r.ease,delay:r.delay,duration:r.duration},++i.count,Xo.timer(function(r){function u(r){return i.active>e?s():(i.active=e,o.event&&o.event.start.call(n,l,t),o.tween.forEach(function(e,r){(r=r.call(n,l,t))&&v.push(r)}),Xo.timer(function(){return p.c=c(r||1)?be:c,1},0,a),void 0)}function c(r){if(i.active!==e)return s();for(var u=r/g,a=f(u),c=v.length;c>0;)v[--c].call(n,a);return u>=1?(o.event&&o.event.end.call(n,l,t),s()):void 0}function s(){return--i.count?delete i[e]:delete n.__transition__,1}var l=n.__data__,f=o.ease,h=o.delay,g=o.duration,p=Ja,v=[];return p.t=h+a,r>=h?u(r-h):(p.c=u,void 0)},0,a)}}function Ho(n,t){n.attr("transform",function(n){return"translate("+t(n)+",0)"})}function Fo(n,t){n.attr("transform",function(n){return"translate(0,"+t(n)+")"})}function Oo(n){return n.toISOString()}function Yo(n,t,e){function r(t){return n(t)}function u(n,e){var r=n[1]-n[0],u=r/e,i=Xo.bisect(js,u);return i==js.length?[t.year,Yi(n.map(function(n){return n/31536e6}),e)[2]]:i?t[u/js[i-1]<js[i]/u?i-1:i]:[Os,Yi(n,e)[2]]}return r.invert=function(t){return Io(n.invert(t))},r.domain=function(t){return arguments.length?(n.domain(t),r):n.domain().map(Io)},r.nice=function(n,t){function e(e){return!isNaN(e)&&!n.range(e,Io(+e+1),t).length}var i=r.domain(),o=zi(i),a=null==n?u(o,10):"number"==typeof n&&u(o,n);return a&&(n=a[0],t=a[1]),r.domain(Pi(i,t>1?{floor:function(t){for(;e(t=n.floor(t));)t=Io(t-1);return t},ceil:function(t){for(;e(t=n.ceil(t));)t=Io(+t+1);return t}}:n))},r.ticks=function(n,t){var e=zi(r.domain()),i=null==n?u(e,10):"number"==typeof n?u(e,n):!n.range&&[{range:n},t];return i&&(n=i[0],t=i[1]),n.range(e[0],Io(+e[1]+1),1>t?1:t)},r.tickFormat=function(){return e},r.copy=function(){return Yo(n.copy(),t,e)},Fi(r,n)}function Io(n){return new Date(n)}function Zo(n){return JSON.parse(n.responseText)}function Vo(n){var t=Wo.createRange();return t.selectNode(Wo.body),t.createContextualFragment(n.responseText)}var Xo={version:"3.4.3"};Date.now||(Date.now=function(){return+new Date});var $o=[].slice,Bo=function(n){return $o.call(n)},Wo=document,Jo=Wo.documentElement,Go=window;try{Bo(Jo.childNodes)[0].nodeType}catch(Ko){Bo=function(n){for(var t=n.length,e=new Array(t);t--;)e[t]=n[t];return e}}try{Wo.createElement("div").style.setProperty("opacity",0,"")}catch(Qo){var na=Go.Element.prototype,ta=na.setAttribute,ea=na.setAttributeNS,ra=Go.CSSStyleDeclaration.prototype,ua=ra.setProperty;na.setAttribute=function(n,t){ta.call(this,n,t+"")},na.setAttributeNS=function(n,t,e){ea.call(this,n,t,e+"")},ra.setProperty=function(n,t,e){ua.call(this,n,t+"",e)}}Xo.ascending=function(n,t){return t>n?-1:n>t?1:n>=t?0:0/0},Xo.descending=function(n,t){return n>t?-1:t>n?1:t>=n?0:0/0},Xo.min=function(n,t){var e,r,u=-1,i=n.length;if(1===arguments.length){for(;++u<i&&!(null!=(e=n[u])&&e>=e);)e=void 0;for(;++u<i;)null!=(r=n[u])&&e>r&&(e=r)}else{for(;++u<i&&!(null!=(e=t.call(n,n[u],u))&&e>=e);)e=void 0;for(;++u<i;)null!=(r=t.call(n,n[u],u))&&e>r&&(e=r)}return e},Xo.max=function(n,t){var e,r,u=-1,i=n.length;if(1===arguments.length){for(;++u<i&&!(null!=(e=n[u])&&e>=e);)e=void 0;for(;++u<i;)null!=(r=n[u])&&r>e&&(e=r)}else{for(;++u<i&&!(null!=(e=t.call(n,n[u],u))&&e>=e);)e=void 0;for(;++u<i;)null!=(r=t.call(n,n[u],u))&&r>e&&(e=r)}return e},Xo.extent=function(n,t){var e,r,u,i=-1,o=n.length;if(1===arguments.length){for(;++i<o&&!(null!=(e=u=n[i])&&e>=e);)e=u=void 0;for(;++i<o;)null!=(r=n[i])&&(e>r&&(e=r),r>u&&(u=r))}else{for(;++i<o&&!(null!=(e=u=t.call(n,n[i],i))&&e>=e);)e=void 0;for(;++i<o;)null!=(r=t.call(n,n[i],i))&&(e>r&&(e=r),r>u&&(u=r))}return[e,u]},Xo.sum=function(n,t){var e,r=0,u=n.length,i=-1;if(1===arguments.length)for(;++i<u;)isNaN(e=+n[i])||(r+=e);else for(;++i<u;)isNaN(e=+t.call(n,n[i],i))||(r+=e);return r},Xo.mean=function(t,e){var r,u=t.length,i=0,o=-1,a=0;if(1===arguments.length)for(;++o<u;)n(r=t[o])&&(i+=(r-i)/++a);else for(;++o<u;)n(r=e.call(t,t[o],o))&&(i+=(r-i)/++a);return a?i:void 0},Xo.quantile=function(n,t){var e=(n.length-1)*t+1,r=Math.floor(e),u=+n[r-1],i=e-r;return i?u+i*(n[r]-u):u},Xo.median=function(t,e){return arguments.length>1&&(t=t.map(e)),t=t.filter(n),t.length?Xo.quantile(t.sort(Xo.ascending),.5):void 0},Xo.bisector=function(n){return{left:function(t,e,r,u){for(arguments.length<3&&(r=0),arguments.length<4&&(u=t.length);u>r;){var i=r+u>>>1;n.call(t,t[i],i)<e?r=i+1:u=i}return r},right:function(t,e,r,u){for(arguments.length<3&&(r=0),arguments.length<4&&(u=t.length);u>r;){var i=r+u>>>1;e<n.call(t,t[i],i)?u=i:r=i+1}return r}}};var ia=Xo.bisector(function(n){return n});Xo.bisectLeft=ia.left,Xo.bisect=Xo.bisectRight=ia.right,Xo.shuffle=function(n){for(var t,e,r=n.length;r;)e=0|Math.random()*r--,t=n[r],n[r]=n[e],n[e]=t;return n},Xo.permute=function(n,t){for(var e=t.length,r=new Array(e);e--;)r[e]=n[t[e]];return r},Xo.pairs=function(n){for(var t,e=0,r=n.length-1,u=n[0],i=new Array(0>r?0:r);r>e;)i[e]=[t=u,u=n[++e]];return i},Xo.zip=function(){if(!(u=arguments.length))return[];for(var n=-1,e=Xo.min(arguments,t),r=new Array(e);++n<e;)for(var u,i=-1,o=r[n]=new Array(u);++i<u;)o[i]=arguments[i][n];return r},Xo.transpose=function(n){return Xo.zip.apply(Xo,n)},Xo.keys=function(n){var t=[];for(var e in n)t.push(e);return t},Xo.values=function(n){var t=[];for(var e in n)t.push(n[e]);return t},Xo.entries=function(n){var t=[];for(var e in n)t.push({key:e,value:n[e]});return t},Xo.merge=function(n){for(var t,e,r,u=n.length,i=-1,o=0;++i<u;)o+=n[i].length;for(e=new Array(o);--u>=0;)for(r=n[u],t=r.length;--t>=0;)e[--o]=r[t];return e};var oa=Math.abs;Xo.range=function(n,t,r){if(arguments.length<3&&(r=1,arguments.length<2&&(t=n,n=0)),1/0===(t-n)/r)throw new Error("infinite range");var u,i=[],o=e(oa(r)),a=-1;if(n*=o,t*=o,r*=o,0>r)for(;(u=n+r*++a)>t;)i.push(u/o);else for(;(u=n+r*++a)<t;)i.push(u/o);return i},Xo.map=function(n){var t=new u;if(n instanceof u)n.forEach(function(n,e){t.set(n,e)});else for(var e in n)t.set(e,n[e]);return t},r(u,{has:i,get:function(n){return this[aa+n]},set:function(n,t){return this[aa+n]=t},remove:o,keys:a,values:function(){var n=[];return this.forEach(function(t,e){n.push(e)}),n},entries:function(){var n=[];return this.forEach(function(t,e){n.push({key:t,value:e})}),n},size:c,empty:s,forEach:function(n){for(var t in this)t.charCodeAt(0)===ca&&n.call(this,t.substring(1),this[t])}});var aa="\x00",ca=aa.charCodeAt(0);Xo.nest=function(){function n(t,a,c){if(c>=o.length)return r?r.call(i,a):e?a.sort(e):a;for(var s,l,f,h,g=-1,p=a.length,v=o[c++],d=new u;++g<p;)(h=d.get(s=v(l=a[g])))?h.push(l):d.set(s,[l]);return t?(l=t(),f=function(e,r){l.set(e,n(t,r,c))}):(l={},f=function(e,r){l[e]=n(t,r,c)}),d.forEach(f),l}function t(n,e){if(e>=o.length)return n;var r=[],u=a[e++];return n.forEach(function(n,u){r.push({key:n,values:t(u,e)})}),u?r.sort(function(n,t){return u(n.key,t.key)}):r}var e,r,i={},o=[],a=[];return i.map=function(t,e){return n(e,t,0)},i.entries=function(e){return t(n(Xo.map,e,0),0)},i.key=function(n){return o.push(n),i},i.sortKeys=function(n){return a[o.length-1]=n,i},i.sortValues=function(n){return e=n,i},i.rollup=function(n){return r=n,i},i},Xo.set=function(n){var t=new l;if(n)for(var e=0,r=n.length;r>e;++e)t.add(n[e]);return t},r(l,{has:i,add:function(n){return this[aa+n]=!0,n},remove:function(n){return n=aa+n,n in this&&delete this[n]},values:a,size:c,empty:s,forEach:function(n){for(var t in this)t.charCodeAt(0)===ca&&n.call(this,t.substring(1))}}),Xo.behavior={},Xo.rebind=function(n,t){for(var e,r=1,u=arguments.length;++r<u;)n[e=arguments[r]]=f(n,t,t[e]);return n};var sa=["webkit","ms","moz","Moz","o","O"];Xo.dispatch=function(){for(var n=new p,t=-1,e=arguments.length;++t<e;)n[arguments[t]]=v(n);return n},p.prototype.on=function(n,t){var e=n.indexOf("."),r="";if(e>=0&&(r=n.substring(e+1),n=n.substring(0,e)),n)return arguments.length<2?this[n].on(r):this[n].on(r,t);if(2===arguments.length){if(null==t)for(n in this)this.hasOwnProperty(n)&&this[n].on(r,null);return this}},Xo.event=null,Xo.requote=function(n){return n.replace(la,"\\$&")};var la=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g,fa={}.__proto__?function(n,t){n.__proto__=t}:function(n,t){for(var e in t)n[e]=t[e]},ha=function(n,t){return t.querySelector(n)},ga=function(n,t){return t.querySelectorAll(n)},pa=Jo[h(Jo,"matchesSelector")],va=function(n,t){return pa.call(n,t)};"function"==typeof Sizzle&&(ha=function(n,t){return Sizzle(n,t)[0]||null},ga=Sizzle,va=Sizzle.matchesSelector),Xo.selection=function(){return xa};var da=Xo.selection.prototype=[];da.select=function(n){var t,e,r,u,i=[];n=M(n);for(var o=-1,a=this.length;++o<a;){i.push(t=[]),t.parentNode=(r=this[o]).parentNode;for(var c=-1,s=r.length;++c<s;)(u=r[c])?(t.push(e=n.call(u,u.__data__,c,o)),e&&"__data__"in u&&(e.__data__=u.__data__)):t.push(null)}return x(i)},da.selectAll=function(n){var t,e,r=[];n=_(n);for(var u=-1,i=this.length;++u<i;)for(var o=this[u],a=-1,c=o.length;++a<c;)(e=o[a])&&(r.push(t=Bo(n.call(e,e.__data__,a,u))),t.parentNode=e);return x(r)};var ma={svg:"http://www.w3.org/2000/svg",xhtml:"http://www.w3.org/1999/xhtml",xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"};Xo.ns={prefix:ma,qualify:function(n){var t=n.indexOf(":"),e=n;return t>=0&&(e=n.substring(0,t),n=n.substring(t+1)),ma.hasOwnProperty(e)?{space:ma[e],local:n}:n}},da.attr=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node();return n=Xo.ns.qualify(n),n.local?e.getAttributeNS(n.space,n.local):e.getAttribute(n)}for(t in n)this.each(b(t,n[t]));return this}return this.each(b(n,t))},da.classed=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node(),r=(n=k(n)).length,u=-1;if(t=e.classList){for(;++u<r;)if(!t.contains(n[u]))return!1}else for(t=e.getAttribute("class");++u<r;)if(!S(n[u]).test(t))return!1;return!0}for(t in n)this.each(E(t,n[t]));return this}return this.each(E(n,t))},da.style=function(n,t,e){var r=arguments.length;if(3>r){if("string"!=typeof n){2>r&&(t="");for(e in n)this.each(C(e,n[e],t));return this}if(2>r)return Go.getComputedStyle(this.node(),null).getPropertyValue(n);e=""}return this.each(C(n,t,e))},da.property=function(n,t){if(arguments.length<2){if("string"==typeof n)return this.node()[n];for(t in n)this.each(N(t,n[t]));return this}return this.each(N(n,t))},da.text=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.textContent=null==t?"":t}:null==n?function(){this.textContent=""}:function(){this.textContent=n}):this.node().textContent},da.html=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.innerHTML=null==t?"":t}:null==n?function(){this.innerHTML=""}:function(){this.innerHTML=n}):this.node().innerHTML},da.append=function(n){return n=L(n),this.select(function(){return this.appendChild(n.apply(this,arguments))})},da.insert=function(n,t){return n=L(n),t=M(t),this.select(function(){return this.insertBefore(n.apply(this,arguments),t.apply(this,arguments)||null)})},da.remove=function(){return this.each(function(){var n=this.parentNode;n&&n.removeChild(this)})},da.data=function(n,t){function e(n,e){var r,i,o,a=n.length,f=e.length,h=Math.min(a,f),g=new Array(f),p=new Array(f),v=new Array(a);if(t){var d,m=new u,y=new u,x=[];for(r=-1;++r<a;)d=t.call(i=n[r],i.__data__,r),m.has(d)?v[r]=i:m.set(d,i),x.push(d);for(r=-1;++r<f;)d=t.call(e,o=e[r],r),(i=m.get(d))?(g[r]=i,i.__data__=o):y.has(d)||(p[r]=T(o)),y.set(d,o),m.remove(d);for(r=-1;++r<a;)m.has(x[r])&&(v[r]=n[r])}else{for(r=-1;++r<h;)i=n[r],o=e[r],i?(i.__data__=o,g[r]=i):p[r]=T(o);for(;f>r;++r)p[r]=T(e[r]);for(;a>r;++r)v[r]=n[r]}p.update=g,p.parentNode=g.parentNode=v.parentNode=n.parentNode,c.push(p),s.push(g),l.push(v)}var r,i,o=-1,a=this.length;if(!arguments.length){for(n=new Array(a=(r=this[0]).length);++o<a;)(i=r[o])&&(n[o]=i.__data__);return n}var c=D([]),s=x([]),l=x([]);if("function"==typeof n)for(;++o<a;)e(r=this[o],n.call(r,r.parentNode.__data__,o));else for(;++o<a;)e(r=this[o],n);return s.enter=function(){return c},s.exit=function(){return l},s},da.datum=function(n){return arguments.length?this.property("__data__",n):this.property("__data__")},da.filter=function(n){var t,e,r,u=[];"function"!=typeof n&&(n=q(n));for(var i=0,o=this.length;o>i;i++){u.push(t=[]),t.parentNode=(e=this[i]).parentNode;for(var a=0,c=e.length;c>a;a++)(r=e[a])&&n.call(r,r.__data__,a,i)&&t.push(r)}return x(u)},da.order=function(){for(var n=-1,t=this.length;++n<t;)for(var e,r=this[n],u=r.length-1,i=r[u];--u>=0;)(e=r[u])&&(i&&i!==e.nextSibling&&i.parentNode.insertBefore(e,i),i=e);return this},da.sort=function(n){n=z.apply(this,arguments);for(var t=-1,e=this.length;++t<e;)this[t].sort(n);return this.order()},da.each=function(n){return R(this,function(t,e,r){n.call(t,t.__data__,e,r)})},da.call=function(n){var t=Bo(arguments);return n.apply(t[0]=this,t),this},da.empty=function(){return!this.node()},da.node=function(){for(var n=0,t=this.length;t>n;n++)for(var e=this[n],r=0,u=e.length;u>r;r++){var i=e[r];if(i)return i}return null},da.size=function(){var n=0;return this.each(function(){++n}),n};var ya=[];Xo.selection.enter=D,Xo.selection.enter.prototype=ya,ya.append=da.append,ya.empty=da.empty,ya.node=da.node,ya.call=da.call,ya.size=da.size,ya.select=function(n){for(var t,e,r,u,i,o=[],a=-1,c=this.length;++a<c;){r=(u=this[a]).update,o.push(t=[]),t.parentNode=u.parentNode;for(var s=-1,l=u.length;++s<l;)(i=u[s])?(t.push(r[s]=e=n.call(u.parentNode,i.__data__,s,a)),e.__data__=i.__data__):t.push(null)}return x(o)},ya.insert=function(n,t){return arguments.length<2&&(t=P(this)),da.insert.call(this,n,t)},da.transition=function(){for(var n,t,e=ks||++Ls,r=[],u=Es||{time:Date.now(),ease:yu,delay:0,duration:250},i=-1,o=this.length;++i<o;){r.push(n=[]);for(var a=this[i],c=-1,s=a.length;++c<s;)(t=a[c])&&jo(t,c,e,u),n.push(t)}return Do(r,e)},da.interrupt=function(){return this.each(U)},Xo.select=function(n){var t=["string"==typeof n?ha(n,Wo):n];return t.parentNode=Jo,x([t])},Xo.selectAll=function(n){var t=Bo("string"==typeof n?ga(n,Wo):n);return t.parentNode=Jo,x([t])};var xa=Xo.select(Jo);da.on=function(n,t,e){var r=arguments.length;if(3>r){if("string"!=typeof n){2>r&&(t=!1);for(e in n)this.each(j(e,n[e],t));return this}if(2>r)return(r=this.node()["__on"+n])&&r._;e=!1}return this.each(j(n,t,e))};var Ma=Xo.map({mouseenter:"mouseover",mouseleave:"mouseout"});Ma.forEach(function(n){"on"+n in Wo&&Ma.remove(n)});var _a="onselectstart"in Wo?null:h(Jo.style,"userSelect"),ba=0;Xo.mouse=function(n){return Y(n,m())};var wa=/WebKit/.test(Go.navigator.userAgent)?-1:0;Xo.touches=function(n,t){return arguments.length<2&&(t=m().touches),t?Bo(t).map(function(t){var e=Y(n,t);return e.identifier=t.identifier,e}):[]},Xo.behavior.drag=function(){function n(){this.on("mousedown.drag",o).on("touchstart.drag",a)}function t(){return Xo.event.changedTouches[0].identifier}function e(n,t){return Xo.touches(n).filter(function(n){return n.identifier===t})[0]}function r(n,t,e,r){return function(){function o(){var n=t(l,g),e=n[0]-v[0],r=n[1]-v[1];d|=e|r,v=n,f({type:"drag",x:n[0]+c[0],y:n[1]+c[1],dx:e,dy:r})}function a(){m.on(e+"."+p,null).on(r+"."+p,null),y(d&&Xo.event.target===h),f({type:"dragend"})}var c,s=this,l=s.parentNode,f=u.of(s,arguments),h=Xo.event.target,g=n(),p=null==g?"drag":"drag-"+g,v=t(l,g),d=0,m=Xo.select(Go).on(e+"."+p,o).on(r+"."+p,a),y=O();i?(c=i.apply(s,arguments),c=[c.x-v[0],c.y-v[1]]):c=[0,0],f({type:"dragstart"})}}var u=y(n,"drag","dragstart","dragend"),i=null,o=r(g,Xo.mouse,"mousemove","mouseup"),a=r(t,e,"touchmove","touchend");return n.origin=function(t){return arguments.length?(i=t,n):i},Xo.rebind(n,u,"on")};var Sa=Math.PI,ka=2*Sa,Ea=Sa/2,Aa=1e-6,Ca=Aa*Aa,Na=Sa/180,La=180/Sa,Ta=Math.SQRT2,qa=2,za=4;Xo.interpolateZoom=function(n,t){function e(n){var t=n*y;if(m){var e=B(v),o=i/(qa*h)*(e*W(Ta*t+v)-$(v));return[r+o*s,u+o*l,i*e/B(Ta*t+v)]}return[r+n*s,u+n*l,i*Math.exp(Ta*t)]}var r=n[0],u=n[1],i=n[2],o=t[0],a=t[1],c=t[2],s=o-r,l=a-u,f=s*s+l*l,h=Math.sqrt(f),g=(c*c-i*i+za*f)/(2*i*qa*h),p=(c*c-i*i-za*f)/(2*c*qa*h),v=Math.log(Math.sqrt(g*g+1)-g),d=Math.log(Math.sqrt(p*p+1)-p),m=d-v,y=(m||Math.log(c/i))/Ta;return e.duration=1e3*y,e},Xo.behavior.zoom=function(){function n(n){n.on(A,s).on(Pa+".zoom",f).on(C,h).on("dblclick.zoom",g).on(L,l)}function t(n){return[(n[0]-S.x)/S.k,(n[1]-S.y)/S.k]}function e(n){return[n[0]*S.k+S.x,n[1]*S.k+S.y]}function r(n){S.k=Math.max(E[0],Math.min(E[1],n))}function u(n,t){t=e(t),S.x+=n[0]-t[0],S.y+=n[1]-t[1]}function i(){_&&_.domain(M.range().map(function(n){return(n-S.x)/S.k}).map(M.invert)),w&&w.domain(b.range().map(function(n){return(n-S.y)/S.k}).map(b.invert))}function o(n){n({type:"zoomstart"})}function a(n){i(),n({type:"zoom",scale:S.k,translate:[S.x,S.y]})}function c(n){n({type:"zoomend"})}function s(){function n(){l=1,u(Xo.mouse(r),g),a(i)}function e(){f.on(C,Go===r?h:null).on(N,null),p(l&&Xo.event.target===s),c(i)}var r=this,i=T.of(r,arguments),s=Xo.event.target,l=0,f=Xo.select(Go).on(C,n).on(N,e),g=t(Xo.mouse(r)),p=O();U.call(r),o(i)}function l(){function n(){var n=Xo.touches(g);return h=S.k,n.forEach(function(n){n.identifier in v&&(v[n.identifier]=t(n))}),n}function e(){for(var t=Xo.event.changedTouches,e=0,i=t.length;i>e;++e)v[t[e].identifier]=null;var o=n(),c=Date.now();if(1===o.length){if(500>c-x){var s=o[0],l=v[s.identifier];r(2*S.k),u(s,l),d(),a(p)}x=c}else if(o.length>1){var s=o[0],f=o[1],h=s[0]-f[0],g=s[1]-f[1];m=h*h+g*g}}function i(){for(var n,t,e,i,o=Xo.touches(g),c=0,s=o.length;s>c;++c,i=null)if(e=o[c],i=v[e.identifier]){if(t)break;n=e,t=i}if(i){var l=(l=e[0]-n[0])*l+(l=e[1]-n[1])*l,f=m&&Math.sqrt(l/m);n=[(n[0]+e[0])/2,(n[1]+e[1])/2],t=[(t[0]+i[0])/2,(t[1]+i[1])/2],r(f*h)}x=null,u(n,t),a(p)}function f(){if(Xo.event.touches.length){for(var t=Xo.event.changedTouches,e=0,r=t.length;r>e;++e)delete v[t[e].identifier];for(var u in v)return void n()}b.on(M,null).on(_,null),w.on(A,s).on(L,l),k(),c(p)}var h,g=this,p=T.of(g,arguments),v={},m=0,y=Xo.event.changedTouches[0].identifier,M="touchmove.zoom-"+y,_="touchend.zoom-"+y,b=Xo.select(Go).on(M,i).on(_,f),w=Xo.select(g).on(A,null).on(L,e),k=O();U.call(g),e(),o(p)}function f(){var n=T.of(this,arguments);m?clearTimeout(m):(U.call(this),o(n)),m=setTimeout(function(){m=null,c(n)},50),d();var e=v||Xo.mouse(this);p||(p=t(e)),r(Math.pow(2,.002*Ra())*S.k),u(e,p),a(n)}function h(){p=null}function g(){var n=T.of(this,arguments),e=Xo.mouse(this),i=t(e),s=Math.log(S.k)/Math.LN2;o(n),r(Math.pow(2,Xo.event.shiftKey?Math.ceil(s)-1:Math.floor(s)+1)),u(e,i),a(n),c(n)}var p,v,m,x,M,_,b,w,S={x:0,y:0,k:1},k=[960,500],E=Da,A="mousedown.zoom",C="mousemove.zoom",N="mouseup.zoom",L="touchstart.zoom",T=y(n,"zoomstart","zoom","zoomend");return n.event=function(n){n.each(function(){var n=T.of(this,arguments),t=S;ks?Xo.select(this).transition().each("start.zoom",function(){S=this.__chart__||{x:0,y:0,k:1},o(n)}).tween("zoom:zoom",function(){var e=k[0],r=k[1],u=e/2,i=r/2,o=Xo.interpolateZoom([(u-S.x)/S.k,(i-S.y)/S.k,e/S.k],[(u-t.x)/t.k,(i-t.y)/t.k,e/t.k]);return function(t){var r=o(t),c=e/r[2];this.__chart__=S={x:u-r[0]*c,y:i-r[1]*c,k:c},a(n)}}).each("end.zoom",function(){c(n)}):(this.__chart__=S,o(n),a(n),c(n))})},n.translate=function(t){return arguments.length?(S={x:+t[0],y:+t[1],k:S.k},i(),n):[S.x,S.y]},n.scale=function(t){return arguments.length?(S={x:S.x,y:S.y,k:+t},i(),n):S.k},n.scaleExtent=function(t){return arguments.length?(E=null==t?Da:[+t[0],+t[1]],n):E},n.center=function(t){return arguments.length?(v=t&&[+t[0],+t[1]],n):v},n.size=function(t){return arguments.length?(k=t&&[+t[0],+t[1]],n):k},n.x=function(t){return arguments.length?(_=t,M=t.copy(),S={x:0,y:0,k:1},n):_},n.y=function(t){return arguments.length?(w=t,b=t.copy(),S={x:0,y:0,k:1},n):w},Xo.rebind(n,T,"on")};var Ra,Da=[0,1/0],Pa="onwheel"in Wo?(Ra=function(){return-Xo.event.deltaY*(Xo.event.deltaMode?120:1)},"wheel"):"onmousewheel"in Wo?(Ra=function(){return Xo.event.wheelDelta},"mousewheel"):(Ra=function(){return-Xo.event.detail},"MozMousePixelScroll");G.prototype.toString=function(){return this.rgb()+""},Xo.hsl=function(n,t,e){return 1===arguments.length?n instanceof Q?K(n.h,n.s,n.l):dt(""+n,mt,K):K(+n,+t,+e)};var Ua=Q.prototype=new G;Ua.brighter=function(n){return n=Math.pow(.7,arguments.length?n:1),K(this.h,this.s,this.l/n)},Ua.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),K(this.h,this.s,n*this.l)},Ua.rgb=function(){return nt(this.h,this.s,this.l)},Xo.hcl=function(n,t,e){return 1===arguments.length?n instanceof et?tt(n.h,n.c,n.l):n instanceof it?at(n.l,n.a,n.b):at((n=yt((n=Xo.rgb(n)).r,n.g,n.b)).l,n.a,n.b):tt(+n,+t,+e)};var ja=et.prototype=new G;ja.brighter=function(n){return tt(this.h,this.c,Math.min(100,this.l+Ha*(arguments.length?n:1)))},ja.darker=function(n){return tt(this.h,this.c,Math.max(0,this.l-Ha*(arguments.length?n:1)))},ja.rgb=function(){return rt(this.h,this.c,this.l).rgb()},Xo.lab=function(n,t,e){return 1===arguments.length?n instanceof it?ut(n.l,n.a,n.b):n instanceof et?rt(n.l,n.c,n.h):yt((n=Xo.rgb(n)).r,n.g,n.b):ut(+n,+t,+e)};var Ha=18,Fa=.95047,Oa=1,Ya=1.08883,Ia=it.prototype=new G;Ia.brighter=function(n){return ut(Math.min(100,this.l+Ha*(arguments.length?n:1)),this.a,this.b)},Ia.darker=function(n){return ut(Math.max(0,this.l-Ha*(arguments.length?n:1)),this.a,this.b)},Ia.rgb=function(){return ot(this.l,this.a,this.b)},Xo.rgb=function(n,t,e){return 1===arguments.length?n instanceof pt?gt(n.r,n.g,n.b):dt(""+n,gt,nt):gt(~~n,~~t,~~e)};var Za=pt.prototype=new G;Za.brighter=function(n){n=Math.pow(.7,arguments.length?n:1);var t=this.r,e=this.g,r=this.b,u=30;return t||e||r?(t&&u>t&&(t=u),e&&u>e&&(e=u),r&&u>r&&(r=u),gt(Math.min(255,~~(t/n)),Math.min(255,~~(e/n)),Math.min(255,~~(r/n)))):gt(u,u,u)},Za.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),gt(~~(n*this.r),~~(n*this.g),~~(n*this.b))},Za.hsl=function(){return mt(this.r,this.g,this.b)},Za.toString=function(){return"#"+vt(this.r)+vt(this.g)+vt(this.b)};var Va=Xo.map({aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074});Va.forEach(function(n,t){Va.set(n,ft(t))}),Xo.functor=_t,Xo.xhr=wt(bt),Xo.dsv=function(n,t){function e(n,e,i){arguments.length<3&&(i=e,e=null);var o=St(n,t,null==e?r:u(e),i);return o.row=function(n){return arguments.length?o.response(null==(e=n)?r:u(n)):e},o}function r(n){return e.parse(n.responseText)}function u(n){return function(t){return e.parse(t.responseText,n)}}function i(t){return t.map(o).join(n)}function o(n){return a.test(n)?'"'+n.replace(/\"/g,'""')+'"':n}var a=new RegExp('["'+n+"\n]"),c=n.charCodeAt(0);return e.parse=function(n,t){var r;return e.parseRows(n,function(n,e){if(r)return r(n,e-1);var u=new Function("d","return {"+n.map(function(n,t){return JSON.stringify(n)+": d["+t+"]"}).join(",")+"}");r=t?function(n,e){return t(u(n),e)}:u})},e.parseRows=function(n,t){function e(){if(l>=s)return o;if(u)return u=!1,i;var t=l;if(34===n.charCodeAt(t)){for(var e=t;e++<s;)if(34===n.charCodeAt(e)){if(34!==n.charCodeAt(e+1))break;++e}l=e+2;var r=n.charCodeAt(e+1);return 13===r?(u=!0,10===n.charCodeAt(e+2)&&++l):10===r&&(u=!0),n.substring(t+1,e).replace(/""/g,'"')}for(;s>l;){var r=n.charCodeAt(l++),a=1;if(10===r)u=!0;else if(13===r)u=!0,10===n.charCodeAt(l)&&(++l,++a);else if(r!==c)continue;return n.substring(t,l-a)}return n.substring(t)}for(var r,u,i={},o={},a=[],s=n.length,l=0,f=0;(r=e())!==o;){for(var h=[];r!==i&&r!==o;)h.push(r),r=e();(!t||(h=t(h,f++)))&&a.push(h)}return a},e.format=function(t){if(Array.isArray(t[0]))return e.formatRows(t);var r=new l,u=[];return t.forEach(function(n){for(var t in n)r.has(t)||u.push(r.add(t))}),[u.map(o).join(n)].concat(t.map(function(t){return u.map(function(n){return o(t[n])}).join(n)})).join("\n")},e.formatRows=function(n){return n.map(i).join("\n")},e},Xo.csv=Xo.dsv(",","text/csv"),Xo.tsv=Xo.dsv("	","text/tab-separated-values");var Xa,$a,Ba,Wa,Ja,Ga=Go[h(Go,"requestAnimationFrame")]||function(n){setTimeout(n,17)};Xo.timer=function(n,t,e){var r=arguments.length;2>r&&(t=0),3>r&&(e=Date.now());var u=e+t,i={c:n,t:u,f:!1,n:null};$a?$a.n=i:Xa=i,$a=i,Ba||(Wa=clearTimeout(Wa),Ba=1,Ga(Et))},Xo.timer.flush=function(){At(),Ct()},Xo.round=function(n,t){return t?Math.round(n*(t=Math.pow(10,t)))/t:Math.round(n)};var Ka=["y","z","a","f","p","n","\xb5","m","","k","M","G","T","P","E","Z","Y"].map(Lt);Xo.formatPrefix=function(n,t){var e=0;return n&&(0>n&&(n*=-1),t&&(n=Xo.round(n,Nt(n,t))),e=1+Math.floor(1e-12+Math.log(n)/Math.LN10),e=Math.max(-24,Math.min(24,3*Math.floor((0>=e?e+1:e-1)/3)))),Ka[8+e/3]};var Qa=/(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i,nc=Xo.map({b:function(n){return n.toString(2)},c:function(n){return String.fromCharCode(n)},o:function(n){return n.toString(8)},x:function(n){return n.toString(16)},X:function(n){return n.toString(16).toUpperCase()},g:function(n,t){return n.toPrecision(t)},e:function(n,t){return n.toExponential(t)},f:function(n,t){return n.toFixed(t)},r:function(n,t){return(n=Xo.round(n,Nt(n,t))).toFixed(Math.max(0,Math.min(20,Nt(n*(1+1e-15),t))))}}),tc=Xo.time={},ec=Date;zt.prototype={getDate:function(){return this._.getUTCDate()},getDay:function(){return this._.getUTCDay()},getFullYear:function(){return this._.getUTCFullYear()},getHours:function(){return this._.getUTCHours()},getMilliseconds:function(){return this._.getUTCMilliseconds()},getMinutes:function(){return this._.getUTCMinutes()},getMonth:function(){return this._.getUTCMonth()},getSeconds:function(){return this._.getUTCSeconds()},getTime:function(){return this._.getTime()},getTimezoneOffset:function(){return 0},valueOf:function(){return this._.valueOf()},setDate:function(){rc.setUTCDate.apply(this._,arguments)},setDay:function(){rc.setUTCDay.apply(this._,arguments)},setFullYear:function(){rc.setUTCFullYear.apply(this._,arguments)},setHours:function(){rc.setUTCHours.apply(this._,arguments)},setMilliseconds:function(){rc.setUTCMilliseconds.apply(this._,arguments)},setMinutes:function(){rc.setUTCMinutes.apply(this._,arguments)},setMonth:function(){rc.setUTCMonth.apply(this._,arguments)},setSeconds:function(){rc.setUTCSeconds.apply(this._,arguments)},setTime:function(){rc.setTime.apply(this._,arguments)}};var rc=Date.prototype;tc.year=Rt(function(n){return n=tc.day(n),n.setMonth(0,1),n},function(n,t){n.setFullYear(n.getFullYear()+t)},function(n){return n.getFullYear()}),tc.years=tc.year.range,tc.years.utc=tc.year.utc.range,tc.day=Rt(function(n){var t=new ec(2e3,0);return t.setFullYear(n.getFullYear(),n.getMonth(),n.getDate()),t},function(n,t){n.setDate(n.getDate()+t)},function(n){return n.getDate()-1}),tc.days=tc.day.range,tc.days.utc=tc.day.utc.range,tc.dayOfYear=function(n){var t=tc.year(n);return Math.floor((n-t-6e4*(n.getTimezoneOffset()-t.getTimezoneOffset()))/864e5)},["sunday","monday","tuesday","wednesday","thursday","friday","saturday"].forEach(function(n,t){t=7-t;var e=tc[n]=Rt(function(n){return(n=tc.day(n)).setDate(n.getDate()-(n.getDay()+t)%7),n},function(n,t){n.setDate(n.getDate()+7*Math.floor(t))},function(n){var e=tc.year(n).getDay();return Math.floor((tc.dayOfYear(n)+(e+t)%7)/7)-(e!==t)});tc[n+"s"]=e.range,tc[n+"s"].utc=e.utc.range,tc[n+"OfYear"]=function(n){var e=tc.year(n).getDay();return Math.floor((tc.dayOfYear(n)+(e+t)%7)/7)}}),tc.week=tc.sunday,tc.weeks=tc.sunday.range,tc.weeks.utc=tc.sunday.utc.range,tc.weekOfYear=tc.sundayOfYear;var uc={"-":"",_:" ",0:"0"},ic=/^\s*\d+/,oc=/^%/;Xo.locale=function(n){return{numberFormat:Tt(n),timeFormat:Pt(n)}};var ac=Xo.locale({decimal:".",thousands:",",grouping:[3],currency:["$",""],dateTime:"%a %b %e %X %Y",date:"%m/%d/%Y",time:"%H:%M:%S",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});Xo.format=ac.numberFormat,Xo.geo={},re.prototype={s:0,t:0,add:function(n){ue(n,this.t,cc),ue(cc.s,this.s,this),this.s?this.t+=cc.t:this.s=cc.t},reset:function(){this.s=this.t=0},valueOf:function(){return this.s}};var cc=new re;Xo.geo.stream=function(n,t){n&&sc.hasOwnProperty(n.type)?sc[n.type](n,t):ie(n,t)};var sc={Feature:function(n,t){ie(n.geometry,t)},FeatureCollection:function(n,t){for(var e=n.features,r=-1,u=e.length;++r<u;)ie(e[r].geometry,t)}},lc={Sphere:function(n,t){t.sphere()},Point:function(n,t){n=n.coordinates,t.point(n[0],n[1],n[2])},MultiPoint:function(n,t){for(var e=n.coordinates,r=-1,u=e.length;++r<u;)n=e[r],t.point(n[0],n[1],n[2])},LineString:function(n,t){oe(n.coordinates,t,0)},MultiLineString:function(n,t){for(var e=n.coordinates,r=-1,u=e.length;++r<u;)oe(e[r],t,0)},Polygon:function(n,t){ae(n.coordinates,t)},MultiPolygon:function(n,t){for(var e=n.coordinates,r=-1,u=e.length;++r<u;)ae(e[r],t)},GeometryCollection:function(n,t){for(var e=n.geometries,r=-1,u=e.length;++r<u;)ie(e[r],t)}};Xo.geo.area=function(n){return fc=0,Xo.geo.stream(n,gc),fc};var fc,hc=new re,gc={sphere:function(){fc+=4*Sa},point:g,lineStart:g,lineEnd:g,polygonStart:function(){hc.reset(),gc.lineStart=ce},polygonEnd:function(){var n=2*hc;fc+=0>n?4*Sa+n:n,gc.lineStart=gc.lineEnd=gc.point=g}};Xo.geo.bounds=function(){function n(n,t){x.push(M=[l=n,h=n]),f>t&&(f=t),t>g&&(g=t)}function t(t,e){var r=se([t*Na,e*Na]);if(m){var u=fe(m,r),i=[u[1],-u[0],0],o=fe(i,u);pe(o),o=ve(o);var c=t-p,s=c>0?1:-1,v=o[0]*La*s,d=oa(c)>180;if(d^(v>s*p&&s*t>v)){var y=o[1]*La;y>g&&(g=y)}else if(v=(v+360)%360-180,d^(v>s*p&&s*t>v)){var y=-o[1]*La;f>y&&(f=y)}else f>e&&(f=e),e>g&&(g=e);d?p>t?a(l,t)>a(l,h)&&(h=t):a(t,h)>a(l,h)&&(l=t):h>=l?(l>t&&(l=t),t>h&&(h=t)):t>p?a(l,t)>a(l,h)&&(h=t):a(t,h)>a(l,h)&&(l=t)}else n(t,e);m=r,p=t}function e(){_.point=t}function r(){M[0]=l,M[1]=h,_.point=n,m=null}function u(n,e){if(m){var r=n-p;y+=oa(r)>180?r+(r>0?360:-360):r}else v=n,d=e;gc.point(n,e),t(n,e)}function i(){gc.lineStart()}function o(){u(v,d),gc.lineEnd(),oa(y)>Aa&&(l=-(h=180)),M[0]=l,M[1]=h,m=null}function a(n,t){return(t-=n)<0?t+360:t}function c(n,t){return n[0]-t[0]}function s(n,t){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:n<t[0]||t[1]<n}var l,f,h,g,p,v,d,m,y,x,M,_={point:n,lineStart:e,lineEnd:r,polygonStart:function(){_.point=u,_.lineStart=i,_.lineEnd=o,y=0,gc.polygonStart()},polygonEnd:function(){gc.polygonEnd(),_.point=n,_.lineStart=e,_.lineEnd=r,0>hc?(l=-(h=180),f=-(g=90)):y>Aa?g=90:-Aa>y&&(f=-90),M[0]=l,M[1]=h}};return function(n){g=h=-(l=f=1/0),x=[],Xo.geo.stream(n,_);var t=x.length;if(t){x.sort(c);for(var e,r=1,u=x[0],i=[u];t>r;++r)e=x[r],s(e[0],u)||s(e[1],u)?(a(u[0],e[1])>a(u[0],u[1])&&(u[1]=e[1]),a(e[0],u[1])>a(u[0],u[1])&&(u[0]=e[0])):i.push(u=e);for(var o,e,p=-1/0,t=i.length-1,r=0,u=i[t];t>=r;u=e,++r)e=i[r],(o=a(u[1],e[0]))>p&&(p=o,l=e[0],h=u[1])}return x=M=null,1/0===l||1/0===f?[[0/0,0/0],[0/0,0/0]]:[[l,f],[h,g]]}}(),Xo.geo.centroid=function(n){pc=vc=dc=mc=yc=xc=Mc=_c=bc=wc=Sc=0,Xo.geo.stream(n,kc);var t=bc,e=wc,r=Sc,u=t*t+e*e+r*r;return Ca>u&&(t=xc,e=Mc,r=_c,Aa>vc&&(t=dc,e=mc,r=yc),u=t*t+e*e+r*r,Ca>u)?[0/0,0/0]:[Math.atan2(e,t)*La,X(r/Math.sqrt(u))*La]};var pc,vc,dc,mc,yc,xc,Mc,_c,bc,wc,Sc,kc={sphere:g,point:me,lineStart:xe,lineEnd:Me,polygonStart:function(){kc.lineStart=_e},polygonEnd:function(){kc.lineStart=xe}},Ec=Ee(be,Te,ze,[-Sa,-Sa/2]),Ac=1e9;Xo.geo.clipExtent=function(){var n,t,e,r,u,i,o={stream:function(n){return u&&(u.valid=!1),u=i(n),u.valid=!0,u},extent:function(a){return arguments.length?(i=Pe(n=+a[0][0],t=+a[0][1],e=+a[1][0],r=+a[1][1]),u&&(u.valid=!1,u=null),o):[[n,t],[e,r]]}};return o.extent([[0,0],[960,500]])},(Xo.geo.conicEqualArea=function(){return je(He)}).raw=He,Xo.geo.albers=function(){return Xo.geo.conicEqualArea().rotate([96,0]).center([-.6,38.7]).parallels([29.5,45.5]).scale(1070)},Xo.geo.albersUsa=function(){function n(n){var i=n[0],o=n[1];return t=null,e(i,o),t||(r(i,o),t)||u(i,o),t}var t,e,r,u,i=Xo.geo.albers(),o=Xo.geo.conicEqualArea().rotate([154,0]).center([-2,58.5]).parallels([55,65]),a=Xo.geo.conicEqualArea().rotate([157,0]).center([-3,19.9]).parallels([8,18]),c={point:function(n,e){t=[n,e]}};return n.invert=function(n){var t=i.scale(),e=i.translate(),r=(n[0]-e[0])/t,u=(n[1]-e[1])/t;return(u>=.12&&.234>u&&r>=-.425&&-.214>r?o:u>=.166&&.234>u&&r>=-.214&&-.115>r?a:i).invert(n)},n.stream=function(n){var t=i.stream(n),e=o.stream(n),r=a.stream(n);return{point:function(n,u){t.point(n,u),e.point(n,u),r.point(n,u)},sphere:function(){t.sphere(),e.sphere(),r.sphere()},lineStart:function(){t.lineStart(),e.lineStart(),r.lineStart()},lineEnd:function(){t.lineEnd(),e.lineEnd(),r.lineEnd()},polygonStart:function(){t.polygonStart(),e.polygonStart(),r.polygonStart()},polygonEnd:function(){t.polygonEnd(),e.polygonEnd(),r.polygonEnd()}}},n.precision=function(t){return arguments.length?(i.precision(t),o.precision(t),a.precision(t),n):i.precision()},n.scale=function(t){return arguments.length?(i.scale(t),o.scale(.35*t),a.scale(t),n.translate(i.translate())):i.scale()},n.translate=function(t){if(!arguments.length)return i.translate();var s=i.scale(),l=+t[0],f=+t[1];return e=i.translate(t).clipExtent([[l-.455*s,f-.238*s],[l+.455*s,f+.238*s]]).stream(c).point,r=o.translate([l-.307*s,f+.201*s]).clipExtent([[l-.425*s+Aa,f+.12*s+Aa],[l-.214*s-Aa,f+.234*s-Aa]]).stream(c).point,u=a.translate([l-.205*s,f+.212*s]).clipExtent([[l-.214*s+Aa,f+.166*s+Aa],[l-.115*s-Aa,f+.234*s-Aa]]).stream(c).point,n},n.scale(1070)};var Cc,Nc,Lc,Tc,qc,zc,Rc={point:g,lineStart:g,lineEnd:g,polygonStart:function(){Nc=0,Rc.lineStart=Fe},polygonEnd:function(){Rc.lineStart=Rc.lineEnd=Rc.point=g,Cc+=oa(Nc/2)}},Dc={point:Oe,lineStart:g,lineEnd:g,polygonStart:g,polygonEnd:g},Pc={point:Ze,lineStart:Ve,lineEnd:Xe,polygonStart:function(){Pc.lineStart=$e},polygonEnd:function(){Pc.point=Ze,Pc.lineStart=Ve,Pc.lineEnd=Xe}};Xo.geo.path=function(){function n(n){return n&&("function"==typeof a&&i.pointRadius(+a.apply(this,arguments)),o&&o.valid||(o=u(i)),Xo.geo.stream(n,o)),i.result()}function t(){return o=null,n}var e,r,u,i,o,a=4.5;return n.area=function(n){return Cc=0,Xo.geo.stream(n,u(Rc)),Cc},n.centroid=function(n){return dc=mc=yc=xc=Mc=_c=bc=wc=Sc=0,Xo.geo.stream(n,u(Pc)),Sc?[bc/Sc,wc/Sc]:_c?[xc/_c,Mc/_c]:yc?[dc/yc,mc/yc]:[0/0,0/0]},n.bounds=function(n){return qc=zc=-(Lc=Tc=1/0),Xo.geo.stream(n,u(Dc)),[[Lc,Tc],[qc,zc]]},n.projection=function(n){return arguments.length?(u=(e=n)?n.stream||Je(n):bt,t()):e},n.context=function(n){return arguments.length?(i=null==(r=n)?new Ye:new Be(n),"function"!=typeof a&&i.pointRadius(a),t()):r},n.pointRadius=function(t){return arguments.length?(a="function"==typeof t?t:(i.pointRadius(+t),+t),n):a},n.projection(Xo.geo.albersUsa()).context(null)},Xo.geo.transform=function(n){return{stream:function(t){var e=new Ge(t);for(var r in n)e[r]=n[r];return e}}},Ge.prototype={point:function(n,t){this.stream.point(n,t)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart()},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}},Xo.geo.projection=Qe,Xo.geo.projectionMutator=nr,(Xo.geo.equirectangular=function(){return Qe(er)}).raw=er.invert=er,Xo.geo.rotation=function(n){function t(t){return t=n(t[0]*Na,t[1]*Na),t[0]*=La,t[1]*=La,t}return n=ur(n[0]%360*Na,n[1]*Na,n.length>2?n[2]*Na:0),t.invert=function(t){return t=n.invert(t[0]*Na,t[1]*Na),t[0]*=La,t[1]*=La,t},t},rr.invert=er,Xo.geo.circle=function(){function n(){var n="function"==typeof r?r.apply(this,arguments):r,t=ur(-n[0]*Na,-n[1]*Na,0).invert,u=[];return e(null,null,1,{point:function(n,e){u.push(n=t(n,e)),n[0]*=La,n[1]*=La}}),{type:"Polygon",coordinates:[u]}}var t,e,r=[0,0],u=6;return n.origin=function(t){return arguments.length?(r=t,n):r},n.angle=function(r){return arguments.length?(e=cr((t=+r)*Na,u*Na),n):t},n.precision=function(r){return arguments.length?(e=cr(t*Na,(u=+r)*Na),n):u},n.angle(90)},Xo.geo.distance=function(n,t){var e,r=(t[0]-n[0])*Na,u=n[1]*Na,i=t[1]*Na,o=Math.sin(r),a=Math.cos(r),c=Math.sin(u),s=Math.cos(u),l=Math.sin(i),f=Math.cos(i);return Math.atan2(Math.sqrt((e=f*o)*e+(e=s*l-c*f*a)*e),c*l+s*f*a)},Xo.geo.graticule=function(){function n(){return{type:"MultiLineString",coordinates:t()}}function t(){return Xo.range(Math.ceil(i/d)*d,u,d).map(h).concat(Xo.range(Math.ceil(s/m)*m,c,m).map(g)).concat(Xo.range(Math.ceil(r/p)*p,e,p).filter(function(n){return oa(n%d)>Aa}).map(l)).concat(Xo.range(Math.ceil(a/v)*v,o,v).filter(function(n){return oa(n%m)>Aa}).map(f))}var e,r,u,i,o,a,c,s,l,f,h,g,p=10,v=p,d=90,m=360,y=2.5;return n.lines=function(){return t().map(function(n){return{type:"LineString",coordinates:n}})},n.outline=function(){return{type:"Polygon",coordinates:[h(i).concat(g(c).slice(1),h(u).reverse().slice(1),g(s).reverse().slice(1))]}},n.extent=function(t){return arguments.length?n.majorExtent(t).minorExtent(t):n.minorExtent()},n.majorExtent=function(t){return arguments.length?(i=+t[0][0],u=+t[1][0],s=+t[0][1],c=+t[1][1],i>u&&(t=i,i=u,u=t),s>c&&(t=s,s=c,c=t),n.precision(y)):[[i,s],[u,c]]},n.minorExtent=function(t){return arguments.length?(r=+t[0][0],e=+t[1][0],a=+t[0][1],o=+t[1][1],r>e&&(t=r,r=e,e=t),a>o&&(t=a,a=o,o=t),n.precision(y)):[[r,a],[e,o]]},n.step=function(t){return arguments.length?n.majorStep(t).minorStep(t):n.minorStep()},n.majorStep=function(t){return arguments.length?(d=+t[0],m=+t[1],n):[d,m]},n.minorStep=function(t){return arguments.length?(p=+t[0],v=+t[1],n):[p,v]},n.precision=function(t){return arguments.length?(y=+t,l=lr(a,o,90),f=fr(r,e,y),h=lr(s,c,90),g=fr(i,u,y),n):y},n.majorExtent([[-180,-90+Aa],[180,90-Aa]]).minorExtent([[-180,-80-Aa],[180,80+Aa]])},Xo.geo.greatArc=function(){function n(){return{type:"LineString",coordinates:[t||r.apply(this,arguments),e||u.apply(this,arguments)]}}var t,e,r=hr,u=gr;return n.distance=function(){return Xo.geo.distance(t||r.apply(this,arguments),e||u.apply(this,arguments))},n.source=function(e){return arguments.length?(r=e,t="function"==typeof e?null:e,n):r},n.target=function(t){return arguments.length?(u=t,e="function"==typeof t?null:t,n):u},n.precision=function(){return arguments.length?n:0},n},Xo.geo.interpolate=function(n,t){return pr(n[0]*Na,n[1]*Na,t[0]*Na,t[1]*Na)},Xo.geo.length=function(n){return Uc=0,Xo.geo.stream(n,jc),Uc};var Uc,jc={sphere:g,point:g,lineStart:vr,lineEnd:g,polygonStart:g,polygonEnd:g},Hc=dr(function(n){return Math.sqrt(2/(1+n))},function(n){return 2*Math.asin(n/2)});(Xo.geo.azimuthalEqualArea=function(){return Qe(Hc)}).raw=Hc;var Fc=dr(function(n){var t=Math.acos(n);return t&&t/Math.sin(t)},bt);(Xo.geo.azimuthalEquidistant=function(){return Qe(Fc)}).raw=Fc,(Xo.geo.conicConformal=function(){return je(mr)}).raw=mr,(Xo.geo.conicEquidistant=function(){return je(yr)}).raw=yr;var Oc=dr(function(n){return 1/n},Math.atan);(Xo.geo.gnomonic=function(){return Qe(Oc)}).raw=Oc,xr.invert=function(n,t){return[n,2*Math.atan(Math.exp(t))-Ea]},(Xo.geo.mercator=function(){return Mr(xr)}).raw=xr;var Yc=dr(function(){return 1},Math.asin);(Xo.geo.orthographic=function(){return Qe(Yc)}).raw=Yc;var Ic=dr(function(n){return 1/(1+n)},function(n){return 2*Math.atan(n)});(Xo.geo.stereographic=function(){return Qe(Ic)}).raw=Ic,_r.invert=function(n,t){return[-t,2*Math.atan(Math.exp(n))-Ea]},(Xo.geo.transverseMercator=function(){var n=Mr(_r),t=n.center,e=n.rotate;return n.center=function(n){return n?t([-n[1],n[0]]):(n=t(),[-n[1],n[0]])},n.rotate=function(n){return n?e([n[0],n[1],n.length>2?n[2]+90:90]):(n=e(),[n[0],n[1],n[2]-90])},n.rotate([0,0])}).raw=_r,Xo.geom={},Xo.geom.hull=function(n){function t(n){if(n.length<3)return[];var t,u=_t(e),i=_t(r),o=n.length,a=[],c=[];for(t=0;o>t;t++)a.push([+u.call(this,n[t],t),+i.call(this,n[t],t),t]);for(a.sort(kr),t=0;o>t;t++)c.push([a[t][0],-a[t][1]]);var s=Sr(a),l=Sr(c),f=l[0]===s[0],h=l[l.length-1]===s[s.length-1],g=[];for(t=s.length-1;t>=0;--t)g.push(n[a[s[t]][2]]);for(t=+f;t<l.length-h;++t)g.push(n[a[l[t]][2]]);return g}var e=br,r=wr;return arguments.length?t(n):(t.x=function(n){return arguments.length?(e=n,t):e},t.y=function(n){return arguments.length?(r=n,t):r},t)},Xo.geom.polygon=function(n){return fa(n,Zc),n};var Zc=Xo.geom.polygon.prototype=[];Zc.area=function(){for(var n,t=-1,e=this.length,r=this[e-1],u=0;++t<e;)n=r,r=this[t],u+=n[1]*r[0]-n[0]*r[1];return.5*u},Zc.centroid=function(n){var t,e,r=-1,u=this.length,i=0,o=0,a=this[u-1];for(arguments.length||(n=-1/(6*this.area()));++r<u;)t=a,a=this[r],e=t[0]*a[1]-a[0]*t[1],i+=(t[0]+a[0])*e,o+=(t[1]+a[1])*e;return[i*n,o*n]},Zc.clip=function(n){for(var t,e,r,u,i,o,a=Cr(n),c=-1,s=this.length-Cr(this),l=this[s-1];++c<s;){for(t=n.slice(),n.length=0,u=this[c],i=t[(r=t.length-a)-1],e=-1;++e<r;)o=t[e],Er(o,l,u)?(Er(i,l,u)||n.push(Ar(i,o,l,u)),n.push(o)):Er(i,l,u)&&n.push(Ar(i,o,l,u)),i=o;a&&n.push(n[0]),l=u}return n};var Vc,Xc,$c,Bc,Wc,Jc=[],Gc=[];Pr.prototype.prepare=function(){for(var n,t=this.edges,e=t.length;e--;)n=t[e].edge,n.b&&n.a||t.splice(e,1);return t.sort(jr),t.length},Br.prototype={start:function(){return this.edge.l===this.site?this.edge.a:this.edge.b},end:function(){return this.edge.l===this.site?this.edge.b:this.edge.a}},Wr.prototype={insert:function(n,t){var e,r,u;if(n){if(t.P=n,t.N=n.N,n.N&&(n.N.P=t),n.N=t,n.R){for(n=n.R;n.L;)n=n.L;n.L=t}else n.R=t;e=n}else this._?(n=Qr(this._),t.P=null,t.N=n,n.P=n.L=t,e=n):(t.P=t.N=null,this._=t,e=null);for(t.L=t.R=null,t.U=e,t.C=!0,n=t;e&&e.C;)r=e.U,e===r.L?(u=r.R,u&&u.C?(e.C=u.C=!1,r.C=!0,n=r):(n===e.R&&(Gr(this,e),n=e,e=n.U),e.C=!1,r.C=!0,Kr(this,r))):(u=r.L,u&&u.C?(e.C=u.C=!1,r.C=!0,n=r):(n===e.L&&(Kr(this,e),n=e,e=n.U),e.C=!1,r.C=!0,Gr(this,r))),e=n.U;this._.C=!1},remove:function(n){n.N&&(n.N.P=n.P),n.P&&(n.P.N=n.N),n.N=n.P=null;var t,e,r,u=n.U,i=n.L,o=n.R;if(e=i?o?Qr(o):i:o,u?u.L===n?u.L=e:u.R=e:this._=e,i&&o?(r=e.C,e.C=n.C,e.L=i,i.U=e,e!==o?(u=e.U,e.U=n.U,n=e.R,u.L=n,e.R=o,o.U=e):(e.U=u,u=e,n=e.R)):(r=n.C,n=e),n&&(n.U=u),!r){if(n&&n.C)return n.C=!1,void 0;do{if(n===this._)break;if(n===u.L){if(t=u.R,t.C&&(t.C=!1,u.C=!0,Gr(this,u),t=u.R),t.L&&t.L.C||t.R&&t.R.C){t.R&&t.R.C||(t.L.C=!1,t.C=!0,Kr(this,t),t=u.R),t.C=u.C,u.C=t.R.C=!1,Gr(this,u),n=this._;break}}else if(t=u.L,t.C&&(t.C=!1,u.C=!0,Kr(this,u),t=u.L),t.L&&t.L.C||t.R&&t.R.C){t.L&&t.L.C||(t.R.C=!1,t.C=!0,Gr(this,t),t=u.L),t.C=u.C,u.C=t.L.C=!1,Kr(this,u),n=this._;break}t.C=!0,n=u,u=u.U}while(!n.C);n&&(n.C=!1)}}},Xo.geom.voronoi=function(n){function t(n){var t=new Array(n.length),r=a[0][0],u=a[0][1],i=a[1][0],o=a[1][1];return nu(e(n),a).cells.forEach(function(e,a){var c=e.edges,s=e.site,l=t[a]=c.length?c.map(function(n){var t=n.start();return[t.x,t.y]}):s.x>=r&&s.x<=i&&s.y>=u&&s.y<=o?[[r,o],[i,o],[i,u],[r,u]]:[];l.point=n[a]}),t}function e(n){return n.map(function(n,t){return{x:Math.round(i(n,t)/Aa)*Aa,y:Math.round(o(n,t)/Aa)*Aa,i:t}})}var r=br,u=wr,i=r,o=u,a=Kc;return n?t(n):(t.links=function(n){return nu(e(n)).edges.filter(function(n){return n.l&&n.r}).map(function(t){return{source:n[t.l.i],target:n[t.r.i]}})},t.triangles=function(n){var t=[];return nu(e(n)).cells.forEach(function(e,r){for(var u,i,o=e.site,a=e.edges.sort(jr),c=-1,s=a.length,l=a[s-1].edge,f=l.l===o?l.r:l.l;++c<s;)u=l,i=f,l=a[c].edge,f=l.l===o?l.r:l.l,r<i.i&&r<f.i&&eu(o,i,f)<0&&t.push([n[r],n[i.i],n[f.i]])}),t},t.x=function(n){return arguments.length?(i=_t(r=n),t):r},t.y=function(n){return arguments.length?(o=_t(u=n),t):u},t.clipExtent=function(n){return arguments.length?(a=null==n?Kc:n,t):a===Kc?null:a},t.size=function(n){return arguments.length?t.clipExtent(n&&[[0,0],n]):a===Kc?null:a&&a[1]},t)};var Kc=[[-1e6,-1e6],[1e6,1e6]];Xo.geom.delaunay=function(n){return Xo.geom.voronoi().triangles(n)},Xo.geom.quadtree=function(n,t,e,r,u){function i(n){function i(n,t,e,r,u,i,o,a){if(!isNaN(e)&&!isNaN(r))if(n.leaf){var c=n.x,l=n.y;if(null!=c)if(oa(c-e)+oa(l-r)<.01)s(n,t,e,r,u,i,o,a);else{var f=n.point;n.x=n.y=n.point=null,s(n,f,c,l,u,i,o,a),s(n,t,e,r,u,i,o,a)}else n.x=e,n.y=r,n.point=t}else s(n,t,e,r,u,i,o,a)}function s(n,t,e,r,u,o,a,c){var s=.5*(u+a),l=.5*(o+c),f=e>=s,h=r>=l,g=(h<<1)+f;n.leaf=!1,n=n.nodes[g]||(n.nodes[g]=iu()),f?u=s:a=s,h?o=l:c=l,i(n,t,e,r,u,o,a,c)}var l,f,h,g,p,v,d,m,y,x=_t(a),M=_t(c);if(null!=t)v=t,d=e,m=r,y=u;else if(m=y=-(v=d=1/0),f=[],h=[],p=n.length,o)for(g=0;p>g;++g)l=n[g],l.x<v&&(v=l.x),l.y<d&&(d=l.y),l.x>m&&(m=l.x),l.y>y&&(y=l.y),f.push(l.x),h.push(l.y);else for(g=0;p>g;++g){var _=+x(l=n[g],g),b=+M(l,g);v>_&&(v=_),d>b&&(d=b),_>m&&(m=_),b>y&&(y=b),f.push(_),h.push(b)}var w=m-v,S=y-d;w>S?y=d+w:m=v+S;var k=iu();if(k.add=function(n){i(k,n,+x(n,++g),+M(n,g),v,d,m,y)},k.visit=function(n){ou(n,k,v,d,m,y)},g=-1,null==t){for(;++g<p;)i(k,n[g],f[g],h[g],v,d,m,y);--g}else n.forEach(k.add);return f=h=n=l=null,k}var o,a=br,c=wr;return(o=arguments.length)?(a=ru,c=uu,3===o&&(u=e,r=t,e=t=0),i(n)):(i.x=function(n){return arguments.length?(a=n,i):a},i.y=function(n){return arguments.length?(c=n,i):c},i.extent=function(n){return arguments.length?(null==n?t=e=r=u=null:(t=+n[0][0],e=+n[0][1],r=+n[1][0],u=+n[1][1]),i):null==t?null:[[t,e],[r,u]]},i.size=function(n){return arguments.length?(null==n?t=e=r=u=null:(t=e=0,r=+n[0],u=+n[1]),i):null==t?null:[r-t,u-e]},i)},Xo.interpolateRgb=au,Xo.interpolateObject=cu,Xo.interpolateNumber=su,Xo.interpolateString=lu;var Qc=/[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g;Xo.interpolate=fu,Xo.interpolators=[function(n,t){var e=typeof t;return("string"===e?Va.has(t)||/^(#|rgb\(|hsl\()/.test(t)?au:lu:t instanceof G?au:"object"===e?Array.isArray(t)?hu:cu:su)(n,t)}],Xo.interpolateArray=hu;var ns=function(){return bt},ts=Xo.map({linear:ns,poly:xu,quad:function(){return du},cubic:function(){return mu},sin:function(){return Mu},exp:function(){return _u},circle:function(){return bu},elastic:wu,back:Su,bounce:function(){return ku}}),es=Xo.map({"in":bt,out:pu,"in-out":vu,"out-in":function(n){return vu(pu(n))}});Xo.ease=function(n){var t=n.indexOf("-"),e=t>=0?n.substring(0,t):n,r=t>=0?n.substring(t+1):"in";return e=ts.get(e)||ns,r=es.get(r)||bt,gu(r(e.apply(null,$o.call(arguments,1))))},Xo.interpolateHcl=Eu,Xo.interpolateHsl=Au,Xo.interpolateLab=Cu,Xo.interpolateRound=Nu,Xo.transform=function(n){var t=Wo.createElementNS(Xo.ns.prefix.svg,"g");return(Xo.transform=function(n){if(null!=n){t.setAttribute("transform",n);var e=t.transform.baseVal.consolidate()}return new Lu(e?e.matrix:rs)})(n)},Lu.prototype.toString=function(){return"translate("+this.translate+")rotate("+this.rotate+")skewX("+this.skew+")scale("+this.scale+")"};var rs={a:1,b:0,c:0,d:1,e:0,f:0};Xo.interpolateTransform=Ru,Xo.layout={},Xo.layout.bundle=function(){return function(n){for(var t=[],e=-1,r=n.length;++e<r;)t.push(Uu(n[e]));return t}},Xo.layout.chord=function(){function n(){var n,s,f,h,g,p={},v=[],d=Xo.range(i),m=[];for(e=[],r=[],n=0,h=-1;++h<i;){for(s=0,g=-1;++g<i;)s+=u[h][g];v.push(s),m.push(Xo.range(i)),n+=s}for(o&&d.sort(function(n,t){return o(v[n],v[t])}),a&&m.forEach(function(n,t){n.sort(function(n,e){return a(u[t][n],u[t][e])})}),n=(ka-l*i)/n,s=0,h=-1;++h<i;){for(f=s,g=-1;++g<i;){var y=d[h],x=m[y][g],M=u[y][x],_=s,b=s+=M*n;p[y+"-"+x]={index:y,subindex:x,startAngle:_,endAngle:b,value:M}}r[y]={index:y,startAngle:f,endAngle:s,value:(s-f)/n},s+=l}for(h=-1;++h<i;)for(g=h-1;++g<i;){var w=p[h+"-"+g],S=p[g+"-"+h];(w.value||S.value)&&e.push(w.value<S.value?{source:S,target:w}:{source:w,target:S})}c&&t()}function t(){e.sort(function(n,t){return c((n.source.value+n.target.value)/2,(t.source.value+t.target.value)/2)})}var e,r,u,i,o,a,c,s={},l=0;return s.matrix=function(n){return arguments.length?(i=(u=n)&&u.length,e=r=null,s):u},s.padding=function(n){return arguments.length?(l=n,e=r=null,s):l},s.sortGroups=function(n){return arguments.length?(o=n,e=r=null,s):o},s.sortSubgroups=function(n){return arguments.length?(a=n,e=null,s):a},s.sortChords=function(n){return arguments.length?(c=n,e&&t(),s):c},s.chords=function(){return e||n(),e},s.groups=function(){return r||n(),r},s},Xo.layout.force=function(){function n(n){return function(t,e,r,u){if(t.point!==n){var i=t.cx-n.x,o=t.cy-n.y,a=u-e,c=i*i+o*o;if(c>a*a/d){if(p>c){var s=t.charge/c;n.px-=i*s,n.py-=o*s}return!0}if(t.point&&c&&p>c){var s=t.pointCharge/c;n.px-=i*s,n.py-=o*s}}return!t.charge}}function t(n){n.px=Xo.event.x,n.py=Xo.event.y,a.resume()}var e,r,u,i,o,a={},c=Xo.dispatch("start","tick","end"),s=[1,1],l=.9,f=us,h=is,g=-30,p=os,v=.1,d=.64,m=[],y=[];return a.tick=function(){if((r*=.99)<.005)return c.end({type:"end",alpha:r=0}),!0;var t,e,a,f,h,p,d,x,M,_=m.length,b=y.length;for(e=0;b>e;++e)a=y[e],f=a.source,h=a.target,x=h.x-f.x,M=h.y-f.y,(p=x*x+M*M)&&(p=r*i[e]*((p=Math.sqrt(p))-u[e])/p,x*=p,M*=p,h.x-=x*(d=f.weight/(h.weight+f.weight)),h.y-=M*d,f.x+=x*(d=1-d),f.y+=M*d);if((d=r*v)&&(x=s[0]/2,M=s[1]/2,e=-1,d))for(;++e<_;)a=m[e],a.x+=(x-a.x)*d,a.y+=(M-a.y)*d;if(g)for(Zu(t=Xo.geom.quadtree(m),r,o),e=-1;++e<_;)(a=m[e]).fixed||t.visit(n(a));for(e=-1;++e<_;)a=m[e],a.fixed?(a.x=a.px,a.y=a.py):(a.x-=(a.px-(a.px=a.x))*l,a.y-=(a.py-(a.py=a.y))*l);c.tick({type:"tick",alpha:r})},a.nodes=function(n){return arguments.length?(m=n,a):m},a.links=function(n){return arguments.length?(y=n,a):y},a.size=function(n){return arguments.length?(s=n,a):s},a.linkDistance=function(n){return arguments.length?(f="function"==typeof n?n:+n,a):f},a.distance=a.linkDistance,a.linkStrength=function(n){return arguments.length?(h="function"==typeof n?n:+n,a):h},a.friction=function(n){return arguments.length?(l=+n,a):l},a.charge=function(n){return arguments.length?(g="function"==typeof n?n:+n,a):g},a.chargeDistance=function(n){return arguments.length?(p=n*n,a):Math.sqrt(p)},a.gravity=function(n){return arguments.length?(v=+n,a):v},a.theta=function(n){return arguments.length?(d=n*n,a):Math.sqrt(d)},a.alpha=function(n){return arguments.length?(n=+n,r?r=n>0?n:0:n>0&&(c.start({type:"start",alpha:r=n}),Xo.timer(a.tick)),a):r},a.start=function(){function n(n,r){if(!e){for(e=new Array(c),a=0;c>a;++a)e[a]=[];for(a=0;s>a;++a){var u=y[a];e[u.source.index].push(u.target),e[u.target.index].push(u.source)}}for(var i,o=e[t],a=-1,s=o.length;++a<s;)if(!isNaN(i=o[a][n]))return i;return Math.random()*r}var t,e,r,c=m.length,l=y.length,p=s[0],v=s[1];for(t=0;c>t;++t)(r=m[t]).index=t,r.weight=0;for(t=0;l>t;++t)r=y[t],"number"==typeof r.source&&(r.source=m[r.source]),"number"==typeof r.target&&(r.target=m[r.target]),++r.source.weight,++r.target.weight;for(t=0;c>t;++t)r=m[t],isNaN(r.x)&&(r.x=n("x",p)),isNaN(r.y)&&(r.y=n("y",v)),isNaN(r.px)&&(r.px=r.x),isNaN(r.py)&&(r.py=r.y);if(u=[],"function"==typeof f)for(t=0;l>t;++t)u[t]=+f.call(this,y[t],t);else for(t=0;l>t;++t)u[t]=f;if(i=[],"function"==typeof h)for(t=0;l>t;++t)i[t]=+h.call(this,y[t],t);else for(t=0;l>t;++t)i[t]=h;if(o=[],"function"==typeof g)for(t=0;c>t;++t)o[t]=+g.call(this,m[t],t);else for(t=0;c>t;++t)o[t]=g;return a.resume()},a.resume=function(){return a.alpha(.1)},a.stop=function(){return a.alpha(0)},a.drag=function(){return e||(e=Xo.behavior.drag().origin(bt).on("dragstart.force",Fu).on("drag.force",t).on("dragend.force",Ou)),arguments.length?(this.on("mouseover.force",Yu).on("mouseout.force",Iu).call(e),void 0):e},Xo.rebind(a,c,"on")};var us=20,is=1,os=1/0;Xo.layout.hierarchy=function(){function n(t,o,a){var c=u.call(e,t,o);if(t.depth=o,a.push(t),c&&(s=c.length)){for(var s,l,f=-1,h=t.children=new Array(s),g=0,p=o+1;++f<s;)l=h[f]=n(c[f],p,a),l.parent=t,g+=l.value;r&&h.sort(r),i&&(t.value=g)}else delete t.children,i&&(t.value=+i.call(e,t,o)||0);return t}function t(n,r){var u=n.children,o=0;if(u&&(a=u.length))for(var a,c=-1,s=r+1;++c<a;)o+=t(u[c],s);else i&&(o=+i.call(e,n,r)||0);return i&&(n.value=o),o}function e(t){var e=[];return n(t,0,e),e}var r=Bu,u=Xu,i=$u;return e.sort=function(n){return arguments.length?(r=n,e):r},e.children=function(n){return arguments.length?(u=n,e):u},e.value=function(n){return arguments.length?(i=n,e):i},e.revalue=function(n){return t(n,0),n},e},Xo.layout.partition=function(){function n(t,e,r,u){var i=t.children;if(t.x=e,t.y=t.depth*u,t.dx=r,t.dy=u,i&&(o=i.length)){var o,a,c,s=-1;for(r=t.value?r/t.value:0;++s<o;)n(a=i[s],e,c=a.value*r,u),e+=c}}function t(n){var e=n.children,r=0;if(e&&(u=e.length))for(var u,i=-1;++i<u;)r=Math.max(r,t(e[i]));return 1+r}function e(e,i){var o=r.call(this,e,i);return n(o[0],0,u[0],u[1]/t(o[0])),o}var r=Xo.layout.hierarchy(),u=[1,1];return e.size=function(n){return arguments.length?(u=n,e):u},Vu(e,r)},Xo.layout.pie=function(){function n(i){var o=i.map(function(e,r){return+t.call(n,e,r)}),a=+("function"==typeof r?r.apply(this,arguments):r),c=(("function"==typeof u?u.apply(this,arguments):u)-a)/Xo.sum(o),s=Xo.range(i.length);null!=e&&s.sort(e===as?function(n,t){return o[t]-o[n]}:function(n,t){return e(i[n],i[t])});var l=[];return s.forEach(function(n){var t;l[n]={data:i[n],value:t=o[n],startAngle:a,endAngle:a+=t*c}}),l}var t=Number,e=as,r=0,u=ka;return n.value=function(e){return arguments.length?(t=e,n):t},n.sort=function(t){return arguments.length?(e=t,n):e},n.startAngle=function(t){return arguments.length?(r=t,n):r},n.endAngle=function(t){return arguments.length?(u=t,n):u},n};var as={};Xo.layout.stack=function(){function n(a,c){var s=a.map(function(e,r){return t.call(n,e,r)}),l=s.map(function(t){return t.map(function(t,e){return[i.call(n,t,e),o.call(n,t,e)]})}),f=e.call(n,l,c);s=Xo.permute(s,f),l=Xo.permute(l,f);var h,g,p,v=r.call(n,l,c),d=s.length,m=s[0].length;for(g=0;m>g;++g)for(u.call(n,s[0][g],p=v[g],l[0][g][1]),h=1;d>h;++h)u.call(n,s[h][g],p+=l[h-1][g][1],l[h][g][1]);return a}var t=bt,e=Qu,r=ni,u=Ku,i=Ju,o=Gu;return n.values=function(e){return arguments.length?(t=e,n):t},n.order=function(t){return arguments.length?(e="function"==typeof t?t:cs.get(t)||Qu,n):e},n.offset=function(t){return arguments.length?(r="function"==typeof t?t:ss.get(t)||ni,n):r},n.x=function(t){return arguments.length?(i=t,n):i},n.y=function(t){return arguments.length?(o=t,n):o},n.out=function(t){return arguments.length?(u=t,n):u},n};var cs=Xo.map({"inside-out":function(n){var t,e,r=n.length,u=n.map(ti),i=n.map(ei),o=Xo.range(r).sort(function(n,t){return u[n]-u[t]}),a=0,c=0,s=[],l=[];for(t=0;r>t;++t)e=o[t],c>a?(a+=i[e],s.push(e)):(c+=i[e],l.push(e));return l.reverse().concat(s)},reverse:function(n){return Xo.range(n.length).reverse()},"default":Qu}),ss=Xo.map({silhouette:function(n){var t,e,r,u=n.length,i=n[0].length,o=[],a=0,c=[];for(e=0;i>e;++e){for(t=0,r=0;u>t;t++)r+=n[t][e][1];r>a&&(a=r),o.push(r)}for(e=0;i>e;++e)c[e]=(a-o[e])/2;return c},wiggle:function(n){var t,e,r,u,i,o,a,c,s,l=n.length,f=n[0],h=f.length,g=[];for(g[0]=c=s=0,e=1;h>e;++e){for(t=0,u=0;l>t;++t)u+=n[t][e][1];for(t=0,i=0,a=f[e][0]-f[e-1][0];l>t;++t){for(r=0,o=(n[t][e][1]-n[t][e-1][1])/(2*a);t>r;++r)o+=(n[r][e][1]-n[r][e-1][1])/a;i+=o*n[t][e][1]}g[e]=c-=u?i/u*a:0,s>c&&(s=c)}for(e=0;h>e;++e)g[e]-=s;return g},expand:function(n){var t,e,r,u=n.length,i=n[0].length,o=1/u,a=[];for(e=0;i>e;++e){for(t=0,r=0;u>t;t++)r+=n[t][e][1];if(r)for(t=0;u>t;t++)n[t][e][1]/=r;else for(t=0;u>t;t++)n[t][e][1]=o}for(e=0;i>e;++e)a[e]=0;return a},zero:ni});Xo.layout.histogram=function(){function n(n,i){for(var o,a,c=[],s=n.map(e,this),l=r.call(this,s,i),f=u.call(this,l,s,i),i=-1,h=s.length,g=f.length-1,p=t?1:1/h;++i<g;)o=c[i]=[],o.dx=f[i+1]-(o.x=f[i]),o.y=0;if(g>0)for(i=-1;++i<h;)a=s[i],a>=l[0]&&a<=l[1]&&(o=c[Xo.bisect(f,a,1,g)-1],o.y+=p,o.push(n[i]));return c}var t=!0,e=Number,r=oi,u=ui;return n.value=function(t){return arguments.length?(e=t,n):e},n.range=function(t){return arguments.length?(r=_t(t),n):r},n.bins=function(t){return arguments.length?(u="number"==typeof t?function(n){return ii(n,t)}:_t(t),n):u},n.frequency=function(e){return arguments.length?(t=!!e,n):t},n},Xo.layout.tree=function(){function n(n,i){function o(n,t){var r=n.children,u=n._tree;if(r&&(i=r.length)){for(var i,a,s,l=r[0],f=l,h=-1;++h<i;)s=r[h],o(s,a),f=c(s,a,f),a=s;vi(n);var g=.5*(l._tree.prelim+s._tree.prelim);t?(u.prelim=t._tree.prelim+e(n,t),u.mod=u.prelim-g):u.prelim=g}else t&&(u.prelim=t._tree.prelim+e(n,t))}function a(n,t){n.x=n._tree.prelim+t;var e=n.children;if(e&&(r=e.length)){var r,u=-1;for(t+=n._tree.mod;++u<r;)a(e[u],t)}}function c(n,t,r){if(t){for(var u,i=n,o=n,a=t,c=n.parent.children[0],s=i._tree.mod,l=o._tree.mod,f=a._tree.mod,h=c._tree.mod;a=si(a),i=ci(i),a&&i;)c=ci(c),o=si(o),o._tree.ancestor=n,u=a._tree.prelim+f-i._tree.prelim-s+e(a,i),u>0&&(di(mi(a,n,r),n,u),s+=u,l+=u),f+=a._tree.mod,s+=i._tree.mod,h+=c._tree.mod,l+=o._tree.mod;a&&!si(o)&&(o._tree.thread=a,o._tree.mod+=f-l),i&&!ci(c)&&(c._tree.thread=i,c._tree.mod+=s-h,r=n)}return r}var s=t.call(this,n,i),l=s[0];pi(l,function(n,t){n._tree={ancestor:n,prelim:0,mod:0,change:0,shift:0,number:t?t._tree.number+1:0}}),o(l),a(l,-l._tree.prelim);var f=li(l,hi),h=li(l,fi),g=li(l,gi),p=f.x-e(f,h)/2,v=h.x+e(h,f)/2,d=g.depth||1;return pi(l,u?function(n){n.x*=r[0],n.y=n.depth*r[1],delete n._tree}:function(n){n.x=(n.x-p)/(v-p)*r[0],n.y=n.depth/d*r[1],delete n._tree}),s}var t=Xo.layout.hierarchy().sort(null).value(null),e=ai,r=[1,1],u=!1;return n.separation=function(t){return arguments.length?(e=t,n):e},n.size=function(t){return arguments.length?(u=null==(r=t),n):u?null:r},n.nodeSize=function(t){return arguments.length?(u=null!=(r=t),n):u?r:null},Vu(n,t)},Xo.layout.pack=function(){function n(n,i){var o=e.call(this,n,i),a=o[0],c=u[0],s=u[1],l=null==t?Math.sqrt:"function"==typeof t?t:function(){return t};if(a.x=a.y=0,pi(a,function(n){n.r=+l(n.value)}),pi(a,bi),r){var f=r*(t?1:Math.max(2*a.r/c,2*a.r/s))/2;pi(a,function(n){n.r+=f}),pi(a,bi),pi(a,function(n){n.r-=f})}return ki(a,c/2,s/2,t?1:1/Math.max(2*a.r/c,2*a.r/s)),o}var t,e=Xo.layout.hierarchy().sort(yi),r=0,u=[1,1];return n.size=function(t){return arguments.length?(u=t,n):u},n.radius=function(e){return arguments.length?(t=null==e||"function"==typeof e?e:+e,n):t},n.padding=function(t){return arguments.length?(r=+t,n):r},Vu(n,e)},Xo.layout.cluster=function(){function n(n,i){var o,a=t.call(this,n,i),c=a[0],s=0;pi(c,function(n){var t=n.children;t&&t.length?(n.x=Ci(t),n.y=Ai(t)):(n.x=o?s+=e(n,o):0,n.y=0,o=n)});var l=Ni(c),f=Li(c),h=l.x-e(l,f)/2,g=f.x+e(f,l)/2;return pi(c,u?function(n){n.x=(n.x-c.x)*r[0],n.y=(c.y-n.y)*r[1]}:function(n){n.x=(n.x-h)/(g-h)*r[0],n.y=(1-(c.y?n.y/c.y:1))*r[1]}),a}var t=Xo.layout.hierarchy().sort(null).value(null),e=ai,r=[1,1],u=!1;return n.separation=function(t){return arguments.length?(e=t,n):e},n.size=function(t){return arguments.length?(u=null==(r=t),n):u?null:r},n.nodeSize=function(t){return arguments.length?(u=null!=(r=t),n):u?r:null},Vu(n,t)},Xo.layout.treemap=function(){function n(n,t){for(var e,r,u=-1,i=n.length;++u<i;)r=(e=n[u]).value*(0>t?0:t),e.area=isNaN(r)||0>=r?0:r}function t(e){var i=e.children;if(i&&i.length){var o,a,c,s=f(e),l=[],h=i.slice(),p=1/0,v="slice"===g?s.dx:"dice"===g?s.dy:"slice-dice"===g?1&e.depth?s.dy:s.dx:Math.min(s.dx,s.dy);for(n(h,s.dx*s.dy/e.value),l.area=0;(c=h.length)>0;)l.push(o=h[c-1]),l.area+=o.area,"squarify"!==g||(a=r(l,v))<=p?(h.pop(),p=a):(l.area-=l.pop().area,u(l,v,s,!1),v=Math.min(s.dx,s.dy),l.length=l.area=0,p=1/0);l.length&&(u(l,v,s,!0),l.length=l.area=0),i.forEach(t)}}function e(t){var r=t.children;if(r&&r.length){var i,o=f(t),a=r.slice(),c=[];for(n(a,o.dx*o.dy/t.value),c.area=0;i=a.pop();)c.push(i),c.area+=i.area,null!=i.z&&(u(c,i.z?o.dx:o.dy,o,!a.length),c.length=c.area=0);r.forEach(e)}}function r(n,t){for(var e,r=n.area,u=0,i=1/0,o=-1,a=n.length;++o<a;)(e=n[o].area)&&(i>e&&(i=e),e>u&&(u=e));return r*=r,t*=t,r?Math.max(t*u*p/r,r/(t*i*p)):1/0}function u(n,t,e,r){var u,i=-1,o=n.length,a=e.x,s=e.y,l=t?c(n.area/t):0;if(t==e.dx){for((r||l>e.dy)&&(l=e.dy);++i<o;)u=n[i],u.x=a,u.y=s,u.dy=l,a+=u.dx=Math.min(e.x+e.dx-a,l?c(u.area/l):0);u.z=!0,u.dx+=e.x+e.dx-a,e.y+=l,e.dy-=l}else{for((r||l>e.dx)&&(l=e.dx);++i<o;)u=n[i],u.x=a,u.y=s,u.dx=l,s+=u.dy=Math.min(e.y+e.dy-s,l?c(u.area/l):0);u.z=!1,u.dy+=e.y+e.dy-s,e.x+=l,e.dx-=l}}function i(r){var u=o||a(r),i=u[0];return i.x=0,i.y=0,i.dx=s[0],i.dy=s[1],o&&a.revalue(i),n([i],i.dx*i.dy/i.value),(o?e:t)(i),h&&(o=u),u}var o,a=Xo.layout.hierarchy(),c=Math.round,s=[1,1],l=null,f=Ti,h=!1,g="squarify",p=.5*(1+Math.sqrt(5));return i.size=function(n){return arguments.length?(s=n,i):s},i.padding=function(n){function t(t){var e=n.call(i,t,t.depth);return null==e?Ti(t):qi(t,"number"==typeof e?[e,e,e,e]:e)}function e(t){return qi(t,n)}if(!arguments.length)return l;var r;return f=null==(l=n)?Ti:"function"==(r=typeof n)?t:"number"===r?(n=[n,n,n,n],e):e,i},i.round=function(n){return arguments.length?(c=n?Math.round:Number,i):c!=Number},i.sticky=function(n){return arguments.length?(h=n,o=null,i):h},i.ratio=function(n){return arguments.length?(p=n,i):p},i.mode=function(n){return arguments.length?(g=n+"",i):g},Vu(i,a)},Xo.random={normal:function(n,t){var e=arguments.length;return 2>e&&(t=1),1>e&&(n=0),function(){var e,r,u;do e=2*Math.random()-1,r=2*Math.random()-1,u=e*e+r*r;while(!u||u>1);return n+t*e*Math.sqrt(-2*Math.log(u)/u)}},logNormal:function(){var n=Xo.random.normal.apply(Xo,arguments);return function(){return Math.exp(n())}},bates:function(n){var t=Xo.random.irwinHall(n);return function(){return t()/n}},irwinHall:function(n){return function(){for(var t=0,e=0;n>e;e++)t+=Math.random();return t}}},Xo.scale={};var ls={floor:bt,ceil:bt};Xo.scale.linear=function(){return Hi([0,1],[0,1],fu,!1)};var fs={s:1,g:1,p:1,r:1,e:1};Xo.scale.log=function(){return $i(Xo.scale.linear().domain([0,1]),10,!0,[1,10])};var hs=Xo.format(".0e"),gs={floor:function(n){return-Math.ceil(-n)},ceil:function(n){return-Math.floor(-n)}};Xo.scale.pow=function(){return Bi(Xo.scale.linear(),1,[0,1])},Xo.scale.sqrt=function(){return Xo.scale.pow().exponent(.5)},Xo.scale.ordinal=function(){return Ji([],{t:"range",a:[[]]})},Xo.scale.category10=function(){return Xo.scale.ordinal().range(ps)},Xo.scale.category20=function(){return Xo.scale.ordinal().range(vs)},Xo.scale.category20b=function(){return Xo.scale.ordinal().range(ds)},Xo.scale.category20c=function(){return Xo.scale.ordinal().range(ms)};var ps=[2062260,16744206,2924588,14034728,9725885,9197131,14907330,8355711,12369186,1556175].map(ht),vs=[2062260,11454440,16744206,16759672,2924588,10018698,14034728,16750742,9725885,12955861,9197131,12885140,14907330,16234194,8355711,13092807,12369186,14408589,1556175,10410725].map(ht),ds=[3750777,5395619,7040719,10264286,6519097,9216594,11915115,13556636,9202993,12426809,15186514,15190932,8666169,11356490,14049643,15177372,8077683,10834324,13528509,14589654].map(ht),ms=[3244733,7057110,10406625,13032431,15095053,16616764,16625259,16634018,3253076,7652470,10607003,13101504,7695281,10394312,12369372,14342891,6513507,9868950,12434877,14277081].map(ht);Xo.scale.quantile=function(){return Gi([],[])},Xo.scale.quantize=function(){return Ki(0,1,[0,1])},Xo.scale.threshold=function(){return Qi([.5],[0,1])},Xo.scale.identity=function(){return no([0,1])},Xo.svg={},Xo.svg.arc=function(){function n(){var n=t.apply(this,arguments),i=e.apply(this,arguments),o=r.apply(this,arguments)+ys,a=u.apply(this,arguments)+ys,c=(o>a&&(c=o,o=a,a=c),a-o),s=Sa>c?"0":"1",l=Math.cos(o),f=Math.sin(o),h=Math.cos(a),g=Math.sin(a);return c>=xs?n?"M0,"+i+"A"+i+","+i+" 0 1,1 0,"+-i+"A"+i+","+i+" 0 1,1 0,"+i+"M0,"+n+"A"+n+","+n+" 0 1,0 0,"+-n+"A"+n+","+n+" 0 1,0 0,"+n+"Z":"M0,"+i+"A"+i+","+i+" 0 1,1 0,"+-i+"A"+i+","+i+" 0 1,1 0,"+i+"Z":n?"M"+i*l+","+i*f+"A"+i+","+i+" 0 "+s+",1 "+i*h+","+i*g+"L"+n*h+","+n*g+"A"+n+","+n+" 0 "+s+",0 "+n*l+","+n*f+"Z":"M"+i*l+","+i*f+"A"+i+","+i+" 0 "+s+",1 "+i*h+","+i*g+"L0,0"+"Z"}var t=to,e=eo,r=ro,u=uo;return n.innerRadius=function(e){return arguments.length?(t=_t(e),n):t},n.outerRadius=function(t){return arguments.length?(e=_t(t),n):e},n.startAngle=function(t){return arguments.length?(r=_t(t),n):r},n.endAngle=function(t){return arguments.length?(u=_t(t),n):u},n.centroid=function(){var n=(t.apply(this,arguments)+e.apply(this,arguments))/2,i=(r.apply(this,arguments)+u.apply(this,arguments))/2+ys;return[Math.cos(i)*n,Math.sin(i)*n]},n};var ys=-Ea,xs=ka-Aa;Xo.svg.line=function(){return io(bt)};var Ms=Xo.map({linear:oo,"linear-closed":ao,step:co,"step-before":so,"step-after":lo,basis:mo,"basis-open":yo,"basis-closed":xo,bundle:Mo,cardinal:go,"cardinal-open":fo,"cardinal-closed":ho,monotone:Eo});Ms.forEach(function(n,t){t.key=n,t.closed=/-closed$/.test(n)});var _s=[0,2/3,1/3,0],bs=[0,1/3,2/3,0],ws=[0,1/6,2/3,1/6];Xo.svg.line.radial=function(){var n=io(Ao);return n.radius=n.x,delete n.x,n.angle=n.y,delete n.y,n},so.reverse=lo,lo.reverse=so,Xo.svg.area=function(){return Co(bt)},Xo.svg.area.radial=function(){var n=Co(Ao);return n.radius=n.x,delete n.x,n.innerRadius=n.x0,delete n.x0,n.outerRadius=n.x1,delete n.x1,n.angle=n.y,delete n.y,n.startAngle=n.y0,delete n.y0,n.endAngle=n.y1,delete n.y1,n},Xo.svg.chord=function(){function n(n,a){var c=t(this,i,n,a),s=t(this,o,n,a);return"M"+c.p0+r(c.r,c.p1,c.a1-c.a0)+(e(c,s)?u(c.r,c.p1,c.r,c.p0):u(c.r,c.p1,s.r,s.p0)+r(s.r,s.p1,s.a1-s.a0)+u(s.r,s.p1,c.r,c.p0))+"Z"}function t(n,t,e,r){var u=t.call(n,e,r),i=a.call(n,u,r),o=c.call(n,u,r)+ys,l=s.call(n,u,r)+ys;return{r:i,a0:o,a1:l,p0:[i*Math.cos(o),i*Math.sin(o)],p1:[i*Math.cos(l),i*Math.sin(l)]}}function e(n,t){return n.a0==t.a0&&n.a1==t.a1}function r(n,t,e){return"A"+n+","+n+" 0 "+ +(e>Sa)+",1 "+t}function u(n,t,e,r){return"Q 0,0 "+r}var i=hr,o=gr,a=No,c=ro,s=uo;return n.radius=function(t){return arguments.length?(a=_t(t),n):a},n.source=function(t){return arguments.length?(i=_t(t),n):i},n.target=function(t){return arguments.length?(o=_t(t),n):o},n.startAngle=function(t){return arguments.length?(c=_t(t),n):c},n.endAngle=function(t){return arguments.length?(s=_t(t),n):s},n},Xo.svg.diagonal=function(){function n(n,u){var i=t.call(this,n,u),o=e.call(this,n,u),a=(i.y+o.y)/2,c=[i,{x:i.x,y:a},{x:o.x,y:a},o];return c=c.map(r),"M"+c[0]+"C"+c[1]+" "+c[2]+" "+c[3]}var t=hr,e=gr,r=Lo;return n.source=function(e){return arguments.length?(t=_t(e),n):t},n.target=function(t){return arguments.length?(e=_t(t),n):e},n.projection=function(t){return arguments.length?(r=t,n):r},n},Xo.svg.diagonal.radial=function(){var n=Xo.svg.diagonal(),t=Lo,e=n.projection;return n.projection=function(n){return arguments.length?e(To(t=n)):t},n},Xo.svg.symbol=function(){function n(n,r){return(Ss.get(t.call(this,n,r))||Ro)(e.call(this,n,r))}var t=zo,e=qo;return n.type=function(e){return arguments.length?(t=_t(e),n):t},n.size=function(t){return arguments.length?(e=_t(t),n):e},n};var Ss=Xo.map({circle:Ro,cross:function(n){var t=Math.sqrt(n/5)/2;return"M"+-3*t+","+-t+"H"+-t+"V"+-3*t+"H"+t+"V"+-t+"H"+3*t+"V"+t+"H"+t+"V"+3*t+"H"+-t+"V"+t+"H"+-3*t+"Z"},diamond:function(n){var t=Math.sqrt(n/(2*Cs)),e=t*Cs;return"M0,"+-t+"L"+e+",0"+" 0,"+t+" "+-e+",0"+"Z"},square:function(n){var t=Math.sqrt(n)/2;return"M"+-t+","+-t+"L"+t+","+-t+" "+t+","+t+" "+-t+","+t+"Z"},"triangle-down":function(n){var t=Math.sqrt(n/As),e=t*As/2;return"M0,"+e+"L"+t+","+-e+" "+-t+","+-e+"Z"},"triangle-up":function(n){var t=Math.sqrt(n/As),e=t*As/2;return"M0,"+-e+"L"+t+","+e+" "+-t+","+e+"Z"}});Xo.svg.symbolTypes=Ss.keys();var ks,Es,As=Math.sqrt(3),Cs=Math.tan(30*Na),Ns=[],Ls=0;Ns.call=da.call,Ns.empty=da.empty,Ns.node=da.node,Ns.size=da.size,Xo.transition=function(n){return arguments.length?ks?n.transition():n:xa.transition()},Xo.transition.prototype=Ns,Ns.select=function(n){var t,e,r,u=this.id,i=[];n=M(n);for(var o=-1,a=this.length;++o<a;){i.push(t=[]);for(var c=this[o],s=-1,l=c.length;++s<l;)(r=c[s])&&(e=n.call(r,r.__data__,s,o))?("__data__"in r&&(e.__data__=r.__data__),jo(e,s,u,r.__transition__[u]),t.push(e)):t.push(null)}return Do(i,u)},Ns.selectAll=function(n){var t,e,r,u,i,o=this.id,a=[];n=_(n);for(var c=-1,s=this.length;++c<s;)for(var l=this[c],f=-1,h=l.length;++f<h;)if(r=l[f]){i=r.__transition__[o],e=n.call(r,r.__data__,f,c),a.push(t=[]);for(var g=-1,p=e.length;++g<p;)(u=e[g])&&jo(u,g,o,i),t.push(u)}return Do(a,o)},Ns.filter=function(n){var t,e,r,u=[];"function"!=typeof n&&(n=q(n));for(var i=0,o=this.length;o>i;i++){u.push(t=[]);for(var e=this[i],a=0,c=e.length;c>a;a++)(r=e[a])&&n.call(r,r.__data__,a,i)&&t.push(r)}return Do(u,this.id)},Ns.tween=function(n,t){var e=this.id;return arguments.length<2?this.node().__transition__[e].tween.get(n):R(this,null==t?function(t){t.__transition__[e].tween.remove(n)}:function(r){r.__transition__[e].tween.set(n,t)})},Ns.attr=function(n,t){function e(){this.removeAttribute(a)}function r(){this.removeAttributeNS(a.space,a.local)}function u(n){return null==n?e:(n+="",function(){var t,e=this.getAttribute(a);return e!==n&&(t=o(e,n),function(n){this.setAttribute(a,t(n))})})}function i(n){return null==n?r:(n+="",function(){var t,e=this.getAttributeNS(a.space,a.local);return e!==n&&(t=o(e,n),function(n){this.setAttributeNS(a.space,a.local,t(n))})})}if(arguments.length<2){for(t in n)this.attr(t,n[t]);return this}var o="transform"==n?Ru:fu,a=Xo.ns.qualify(n);return Po(this,"attr."+n,t,a.local?i:u)},Ns.attrTween=function(n,t){function e(n,e){var r=t.call(this,n,e,this.getAttribute(u));return r&&function(n){this.setAttribute(u,r(n))}}function r(n,e){var r=t.call(this,n,e,this.getAttributeNS(u.space,u.local));return r&&function(n){this.setAttributeNS(u.space,u.local,r(n))}}var u=Xo.ns.qualify(n);return this.tween("attr."+n,u.local?r:e)},Ns.style=function(n,t,e){function r(){this.style.removeProperty(n)}function u(t){return null==t?r:(t+="",function(){var r,u=Go.getComputedStyle(this,null).getPropertyValue(n);return u!==t&&(r=fu(u,t),function(t){this.style.setProperty(n,r(t),e)})})}var i=arguments.length;if(3>i){if("string"!=typeof n){2>i&&(t="");for(e in n)this.style(e,n[e],t);return this}e=""}return Po(this,"style."+n,t,u)},Ns.styleTween=function(n,t,e){function r(r,u){var i=t.call(this,r,u,Go.getComputedStyle(this,null).getPropertyValue(n));return i&&function(t){this.style.setProperty(n,i(t),e)}}return arguments.length<3&&(e=""),this.tween("style."+n,r)},Ns.text=function(n){return Po(this,"text",n,Uo)},Ns.remove=function(){return this.each("end.transition",function(){var n;this.__transition__.count<2&&(n=this.parentNode)&&n.removeChild(this)})},Ns.ease=function(n){var t=this.id;return arguments.length<1?this.node().__transition__[t].ease:("function"!=typeof n&&(n=Xo.ease.apply(Xo,arguments)),R(this,function(e){e.__transition__[t].ease=n}))},Ns.delay=function(n){var t=this.id;return R(this,"function"==typeof n?function(e,r,u){e.__transition__[t].delay=+n.call(e,e.__data__,r,u)}:(n=+n,function(e){e.__transition__[t].delay=n}))},Ns.duration=function(n){var t=this.id;return R(this,"function"==typeof n?function(e,r,u){e.__transition__[t].duration=Math.max(1,n.call(e,e.__data__,r,u))}:(n=Math.max(1,n),function(e){e.__transition__[t].duration=n}))},Ns.each=function(n,t){var e=this.id;if(arguments.length<2){var r=Es,u=ks;ks=e,R(this,function(t,r,u){Es=t.__transition__[e],n.call(t,t.__data__,r,u)}),Es=r,ks=u}else R(this,function(r){var u=r.__transition__[e];(u.event||(u.event=Xo.dispatch("start","end"))).on(n,t)});return this},Ns.transition=function(){for(var n,t,e,r,u=this.id,i=++Ls,o=[],a=0,c=this.length;c>a;a++){o.push(n=[]);for(var t=this[a],s=0,l=t.length;l>s;s++)(e=t[s])&&(r=Object.create(e.__transition__[u]),r.delay+=r.duration,jo(e,s,i,r)),n.push(e)}return Do(o,i)},Xo.svg.axis=function(){function n(n){n.each(function(){var n,s=Xo.select(this),l=this.__chart__||e,f=this.__chart__=e.copy(),h=null==c?f.ticks?f.ticks.apply(f,a):f.domain():c,g=null==t?f.tickFormat?f.tickFormat.apply(f,a):bt:t,p=s.selectAll(".tick").data(h,f),v=p.enter().insert("g",".domain").attr("class","tick").style("opacity",Aa),d=Xo.transition(p.exit()).style("opacity",Aa).remove(),m=Xo.transition(p).style("opacity",1),y=Ri(f),x=s.selectAll(".domain").data([0]),M=(x.enter().append("path").attr("class","domain"),Xo.transition(x));v.append("line"),v.append("text");var _=v.select("line"),b=m.select("line"),w=p.select("text").text(g),S=v.select("text"),k=m.select("text");switch(r){case"bottom":n=Ho,_.attr("y2",u),S.attr("y",Math.max(u,0)+o),b.attr("x2",0).attr("y2",u),k.attr("x",0).attr("y",Math.max(u,0)+o),w.attr("dy",".71em").style("text-anchor","middle"),M.attr("d","M"+y[0]+","+i+"V0H"+y[1]+"V"+i);break;case"top":n=Ho,_.attr("y2",-u),S.attr("y",-(Math.max(u,0)+o)),b.attr("x2",0).attr("y2",-u),k.attr("x",0).attr("y",-(Math.max(u,0)+o)),w.attr("dy","0em").style("text-anchor","middle"),M.attr("d","M"+y[0]+","+-i+"V0H"+y[1]+"V"+-i);break;case"left":n=Fo,_.attr("x2",-u),S.attr("x",-(Math.max(u,0)+o)),b.attr("x2",-u).attr("y2",0),k.attr("x",-(Math.max(u,0)+o)).attr("y",0),w.attr("dy",".32em").style("text-anchor","end"),M.attr("d","M"+-i+","+y[0]+"H0V"+y[1]+"H"+-i);break;case"right":n=Fo,_.attr("x2",u),S.attr("x",Math.max(u,0)+o),b.attr("x2",u).attr("y2",0),k.attr("x",Math.max(u,0)+o).attr("y",0),w.attr("dy",".32em").style("text-anchor","start"),M.attr("d","M"+i+","+y[0]+"H0V"+y[1]+"H"+i)}if(f.rangeBand){var E=f,A=E.rangeBand()/2;l=f=function(n){return E(n)+A}}else l.rangeBand?l=f:d.call(n,f);v.call(n,l),m.call(n,f)})}var t,e=Xo.scale.linear(),r=Ts,u=6,i=6,o=3,a=[10],c=null;return n.scale=function(t){return arguments.length?(e=t,n):e},n.orient=function(t){return arguments.length?(r=t in qs?t+"":Ts,n):r},n.ticks=function(){return arguments.length?(a=arguments,n):a},n.tickValues=function(t){return arguments.length?(c=t,n):c},n.tickFormat=function(e){return arguments.length?(t=e,n):t},n.tickSize=function(t){var e=arguments.length;return e?(u=+t,i=+arguments[e-1],n):u},n.innerTickSize=function(t){return arguments.length?(u=+t,n):u},n.outerTickSize=function(t){return arguments.length?(i=+t,n):i},n.tickPadding=function(t){return arguments.length?(o=+t,n):o},n.tickSubdivide=function(){return arguments.length&&n},n};var Ts="bottom",qs={top:1,right:1,bottom:1,left:1};Xo.svg.brush=function(){function n(i){i.each(function(){var i=Xo.select(this).style("pointer-events","all").style("-webkit-tap-highlight-color","rgba(0,0,0,0)").on("mousedown.brush",u).on("touchstart.brush",u),o=i.selectAll(".background").data([0]);o.enter().append("rect").attr("class","background").style("visibility","hidden").style("cursor","crosshair"),i.selectAll(".extent").data([0]).enter().append("rect").attr("class","extent").style("cursor","move");var a=i.selectAll(".resize").data(p,bt);a.exit().remove(),a.enter().append("g").attr("class",function(n){return"resize "+n}).style("cursor",function(n){return zs[n]}).append("rect").attr("x",function(n){return/[ew]$/.test(n)?-3:null}).attr("y",function(n){return/^[ns]/.test(n)?-3:null}).attr("width",6).attr("height",6).style("visibility","hidden"),a.style("display",n.empty()?"none":null);var l,f=Xo.transition(i),h=Xo.transition(o);c&&(l=Ri(c),h.attr("x",l[0]).attr("width",l[1]-l[0]),e(f)),s&&(l=Ri(s),h.attr("y",l[0]).attr("height",l[1]-l[0]),r(f)),t(f)})}function t(n){n.selectAll(".resize").attr("transform",function(n){return"translate("+l[+/e$/.test(n)]+","+f[+/^s/.test(n)]+")"})}function e(n){n.select(".extent").attr("x",l[0]),n.selectAll(".extent,.n>rect,.s>rect").attr("width",l[1]-l[0])}function r(n){n.select(".extent").attr("y",f[0]),n.selectAll(".extent,.e>rect,.w>rect").attr("height",f[1]-f[0])}function u(){function u(){32==Xo.event.keyCode&&(C||(x=null,L[0]-=l[1],L[1]-=f[1],C=2),d())}function p(){32==Xo.event.keyCode&&2==C&&(L[0]+=l[1],L[1]+=f[1],C=0,d())}function v(){var n=Xo.mouse(_),u=!1;M&&(n[0]+=M[0],n[1]+=M[1]),C||(Xo.event.altKey?(x||(x=[(l[0]+l[1])/2,(f[0]+f[1])/2]),L[0]=l[+(n[0]<x[0])],L[1]=f[+(n[1]<x[1])]):x=null),E&&m(n,c,0)&&(e(S),u=!0),A&&m(n,s,1)&&(r(S),u=!0),u&&(t(S),w({type:"brush",mode:C?"move":"resize"}))}function m(n,t,e){var r,u,a=Ri(t),c=a[0],s=a[1],p=L[e],v=e?f:l,d=v[1]-v[0];return C&&(c-=p,s-=d+p),r=(e?g:h)?Math.max(c,Math.min(s,n[e])):n[e],C?u=(r+=p)+d:(x&&(p=Math.max(c,Math.min(s,2*x[e]-r))),r>p?(u=r,r=p):u=p),v[0]!=r||v[1]!=u?(e?o=null:i=null,v[0]=r,v[1]=u,!0):void 0}function y(){v(),S.style("pointer-events","all").selectAll(".resize").style("display",n.empty()?"none":null),Xo.select("body").style("cursor",null),T.on("mousemove.brush",null).on("mouseup.brush",null).on("touchmove.brush",null).on("touchend.brush",null).on("keydown.brush",null).on("keyup.brush",null),N(),w({type:"brushend"})}var x,M,_=this,b=Xo.select(Xo.event.target),w=a.of(_,arguments),S=Xo.select(_),k=b.datum(),E=!/^(n|s)$/.test(k)&&c,A=!/^(e|w)$/.test(k)&&s,C=b.classed("extent"),N=O(),L=Xo.mouse(_),T=Xo.select(Go).on("keydown.brush",u).on("keyup.brush",p);if(Xo.event.changedTouches?T.on("touchmove.brush",v).on("touchend.brush",y):T.on("mousemove.brush",v).on("mouseup.brush",y),S.interrupt().selectAll("*").interrupt(),C)L[0]=l[0]-L[0],L[1]=f[0]-L[1];else if(k){var q=+/w$/.test(k),z=+/^n/.test(k);M=[l[1-q]-L[0],f[1-z]-L[1]],L[0]=l[q],L[1]=f[z]}else Xo.event.altKey&&(x=L.slice());S.style("pointer-events","none").selectAll(".resize").style("display",null),Xo.select("body").style("cursor",b.style("cursor")),w({type:"brushstart"}),v()}var i,o,a=y(n,"brushstart","brush","brushend"),c=null,s=null,l=[0,0],f=[0,0],h=!0,g=!0,p=Rs[0];return n.event=function(n){n.each(function(){var n=a.of(this,arguments),t={x:l,y:f,i:i,j:o},e=this.__chart__||t;this.__chart__=t,ks?Xo.select(this).transition().each("start.brush",function(){i=e.i,o=e.j,l=e.x,f=e.y,n({type:"brushstart"})}).tween("brush:brush",function(){var e=hu(l,t.x),r=hu(f,t.y);return i=o=null,function(u){l=t.x=e(u),f=t.y=r(u),n({type:"brush",mode:"resize"})}}).each("end.brush",function(){i=t.i,o=t.j,n({type:"brush",mode:"resize"}),n({type:"brushend"})}):(n({type:"brushstart"}),n({type:"brush",mode:"resize"}),n({type:"brushend"}))})},n.x=function(t){return arguments.length?(c=t,p=Rs[!c<<1|!s],n):c},n.y=function(t){return arguments.length?(s=t,p=Rs[!c<<1|!s],n):s},n.clamp=function(t){return arguments.length?(c&&s?(h=!!t[0],g=!!t[1]):c?h=!!t:s&&(g=!!t),n):c&&s?[h,g]:c?h:s?g:null},n.extent=function(t){var e,r,u,a,h;return arguments.length?(c&&(e=t[0],r=t[1],s&&(e=e[0],r=r[0]),i=[e,r],c.invert&&(e=c(e),r=c(r)),e>r&&(h=e,e=r,r=h),(e!=l[0]||r!=l[1])&&(l=[e,r])),s&&(u=t[0],a=t[1],c&&(u=u[1],a=a[1]),o=[u,a],s.invert&&(u=s(u),a=s(a)),u>a&&(h=u,u=a,a=h),(u!=f[0]||a!=f[1])&&(f=[u,a])),n):(c&&(i?(e=i[0],r=i[1]):(e=l[0],r=l[1],c.invert&&(e=c.invert(e),r=c.invert(r)),e>r&&(h=e,e=r,r=h))),s&&(o?(u=o[0],a=o[1]):(u=f[0],a=f[1],s.invert&&(u=s.invert(u),a=s.invert(a)),u>a&&(h=u,u=a,a=h))),c&&s?[[e,u],[r,a]]:c?[e,r]:s&&[u,a])},n.clear=function(){return n.empty()||(l=[0,0],f=[0,0],i=o=null),n},n.empty=function(){return!!c&&l[0]==l[1]||!!s&&f[0]==f[1]},Xo.rebind(n,a,"on")};var zs={n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},Rs=[["n","e","s","w","nw","ne","se","sw"],["e","w"],["n","s"],[]],Ds=tc.format=ac.timeFormat,Ps=Ds.utc,Us=Ps("%Y-%m-%dT%H:%M:%S.%LZ");Ds.iso=Date.prototype.toISOString&&+new Date("2000-01-01T00:00:00.000Z")?Oo:Us,Oo.parse=function(n){var t=new Date(n);return isNaN(t)?null:t},Oo.toString=Us.toString,tc.second=Rt(function(n){return new ec(1e3*Math.floor(n/1e3))},function(n,t){n.setTime(n.getTime()+1e3*Math.floor(t))},function(n){return n.getSeconds()}),tc.seconds=tc.second.range,tc.seconds.utc=tc.second.utc.range,tc.minute=Rt(function(n){return new ec(6e4*Math.floor(n/6e4))},function(n,t){n.setTime(n.getTime()+6e4*Math.floor(t))},function(n){return n.getMinutes()}),tc.minutes=tc.minute.range,tc.minutes.utc=tc.minute.utc.range,tc.hour=Rt(function(n){var t=n.getTimezoneOffset()/60;return new ec(36e5*(Math.floor(n/36e5-t)+t))},function(n,t){n.setTime(n.getTime()+36e5*Math.floor(t))},function(n){return n.getHours()}),tc.hours=tc.hour.range,tc.hours.utc=tc.hour.utc.range,tc.month=Rt(function(n){return n=tc.day(n),n.setDate(1),n},function(n,t){n.setMonth(n.getMonth()+t)},function(n){return n.getMonth()}),tc.months=tc.month.range,tc.months.utc=tc.month.utc.range;var js=[1e3,5e3,15e3,3e4,6e4,3e5,9e5,18e5,36e5,108e5,216e5,432e5,864e5,1728e5,6048e5,2592e6,7776e6,31536e6],Hs=[[tc.second,1],[tc.second,5],[tc.second,15],[tc.second,30],[tc.minute,1],[tc.minute,5],[tc.minute,15],[tc.minute,30],[tc.hour,1],[tc.hour,3],[tc.hour,6],[tc.hour,12],[tc.day,1],[tc.day,2],[tc.week,1],[tc.month,1],[tc.month,3],[tc.year,1]],Fs=Ds.multi([[".%L",function(n){return n.getMilliseconds()}],[":%S",function(n){return n.getSeconds()}],["%I:%M",function(n){return n.getMinutes()}],["%I %p",function(n){return n.getHours()}],["%a %d",function(n){return n.getDay()&&1!=n.getDate()}],["%b %d",function(n){return 1!=n.getDate()}],["%B",function(n){return n.getMonth()}],["%Y",be]]),Os={range:function(n,t,e){return Xo.range(Math.ceil(n/e)*e,+t,e).map(Io)},floor:bt,ceil:bt};Hs.year=tc.year,tc.scale=function(){return Yo(Xo.scale.linear(),Hs,Fs)};var Ys=Hs.map(function(n){return[n[0].utc,n[1]]}),Is=Ps.multi([[".%L",function(n){return n.getUTCMilliseconds()}],[":%S",function(n){return n.getUTCSeconds()}],["%I:%M",function(n){return n.getUTCMinutes()}],["%I %p",function(n){return n.getUTCHours()}],["%a %d",function(n){return n.getUTCDay()&&1!=n.getUTCDate()}],["%b %d",function(n){return 1!=n.getUTCDate()}],["%B",function(n){return n.getUTCMonth()}],["%Y",be]]);Ys.year=tc.year.utc,tc.scale.utc=function(){return Yo(Xo.scale.linear(),Ys,Is)},Xo.text=wt(function(n){return n.responseText}),Xo.json=function(n,t){return St(n,"application/json",Zo,t)},Xo.html=function(n,t){return St(n,"text/html",Vo,t)},Xo.xml=wt(function(n){return n.responseXML}),"function"==typeof define&&define.amd?define(Xo):"object"==typeof module&&module.exports?module.exports=Xo:this.d3=Xo}();'use strict';(function(window){window.define=undefined;}).call(this,this);'use strict';Polymer({is:'tr-ui-b-chart-legend-key',ready:function(){this.$.checkbox.addEventListener('change',this.onCheckboxChange_.bind(this));},onCheckboxChange_:function(){tr.b.dispatchSimpleEvent(this,tr.ui.b.DataSeriesEnableChangeEventType,true,false,{key:Polymer.dom(this).textContent,enabled:this.enabled});},set textContent(t){Polymer.dom(this.$.label).textContent=t;Polymer.dom(this.$.link).textContent=t;this.updateContents_();},set width(w){w-=20;this.$.link.style.width=w+'px';this.$.label.style.width=w+'px';},get textContent(){return Polymer.dom(this.$.label).textContent;},set optional(optional){this.$.checkbox.style.visibility=optional?'visible':'hidden';},get optional(){return this.$.checkbox.style.visibility==='visible';},set enabled(enabled){this.$.checkbox.checked=enabled?'checked':'';},get enabled(){return this.$.checkbox.checked;},set color(c){this.$.label.style.color=c;this.$.link.color=c;},set target(target){this.$.link.setSelectionAndContent(target,Polymer.dom(this.$.label).textContent);this.updateContents_();},get target(){return this.$.link.selection;},updateContents_:function(){this.$.link.style.display=this.target?'':'none';this.$.label.style.display=this.target?'none':'';this.$.label.htmlFor=this.optional?'checkbox':'';}});'use strict';tr.exportTo('tr.ui.b',function(){var DataSeriesEnableChangeEventType='data-series-enabled-change';var THIS_DOC=document.currentScript.ownerDocument;var svgNS='http://www.w3.org/2000/svg';var ColorScheme=tr.b.ColorScheme;function getColorOfKey(key,selected){var id=ColorScheme.getColorIdForGeneralPurposeString(key);if(selected)
-id+=ColorScheme.properties.brightenedOffsets[0];return ColorScheme.colorsAsStrings[id];}
-function DataSeries(key){this.key_=key;this.target_=undefined;this.optional_=false;this.enabled_=true;this.color_=getColorOfKey(key,false);this.highlightedColor_=getColorOfKey(key,true);}
-DataSeries.prototype={get key(){return this.key_;},get color(){return this.color_;},set color(c){this.color_=c;},get highlightedColor(){return this.highlightedColor_;},set highlightedColor(c){this.highlightedColor_=c;},get optional(){return this.optional_;},set optional(optional){this.optional_=optional;},get enabled(){return this.enabled_;},set enabled(enabled){if(!this.optional&&!enabled)
-this.optional=true;this.enabled_=enabled;},get target(){return this.target_;},set target(t){this.target_=t;}};var ChartBase=tr.ui.b.define('svg',undefined,svgNS);ChartBase.prototype={__proto__:HTMLUnknownElement.prototype,getDataSeries:function(key){if(!this.seriesByKey_.has(key))
-this.seriesByKey_.set(key,new DataSeries(key));return this.seriesByKey_.get(key);},decorate:function(){Polymer.dom(this).classList.add('chart-base');this.chartTitle_=undefined;this.seriesByKey_=new Map();this.width_=400;this.height_=300;this.margin={top:20,right:72,bottom:30,left:50};this.hideLegend_=false;var template=Polymer.dom(THIS_DOC).querySelector('#chart-base-template');var svgEl=Polymer.dom(template.content).querySelector('svg');for(var i=0;i<Polymer.dom(svgEl).children.length;i++)
-Polymer.dom(this).appendChild(Polymer.dom(svgEl.children[i]).cloneNode(true));Object.defineProperty(this,'width',{get:function(){return this.width_;},set:function(width){this.width_=width;this.updateContents_();}});Object.defineProperty(this,'height',{get:function(){return this.height_;},set:function(height){this.height_=height;this.updateContents_();}});this.addEventListener(DataSeriesEnableChangeEventType,this.onDataSeriesEnableChange_.bind(this));},get hideLegend(){return this.hideLegend_;},set hideLegend(h){this.hideLegend_=h;this.updateContents_();},isSeriesEnabled:function(key){return this.getDataSeries(key).enabled;},onDataSeriesEnableChange_:function(event){this.getDataSeries(event.key).enabled=event.enabled;this.updateContents_();},get chartTitle(){return this.chartTitle_;},set chartTitle(chartTitle){if(chartTitle&&!this.chartTitle_)
-this.margin.top+=this.titleMarginPx;else if(this.chartTitle_&&!chartTitle)
-this.margin.top-=this.titleMarginPx;this.chartTitle_=chartTitle;this.updateContents_();},get titleMarginPx(){return 20;},get chartAreaElement(){return Polymer.dom(this).querySelector('#chart-area');},setSize:function(size){this.width_=size.width;this.height_=size.height;this.updateContents_();},get chartAreaSize(){return{width:this.width_-this.margin.left-this.margin.right,height:this.height_-this.margin.top-this.margin.bottom};},updateScales_:function(){throw new Error('Not implemented');},updateContents_:function(){var thisSel=d3.select(this);thisSel.attr('width',this.width_);thisSel.attr('height',this.height_);var chartAreaSel=d3.select(this.chartAreaElement);chartAreaSel.attr('transform','translate('+this.margin.left+','+this.margin.top+')');this.updateScales_();this.updateTitle_(chartAreaSel);this.updateLegend_();},updateTitle_:function(chartAreaSel){var titleSel=chartAreaSel.select('#title');if(!this.chartTitle_){titleSel.style('display','none');return;}
-var width=this.chartAreaSize.width;titleSel.attr('transform','translate('+width*0.5+',-5)').style('display',undefined).style('text-anchor','middle').attr('class','title').attr('width',width).text(this.chartTitle_);},updateLegend_:function(){var chartAreaSel=d3.select(this.chartAreaElement);chartAreaSel.selectAll('.legend').remove();if(this.hideLegend)
-return;var series=[...this.seriesByKey_.values()].reverse();var legendEntriesSel=chartAreaSel.selectAll('.legend').data(series);var width=this.margin.right-2;legendEntriesSel.enter().append('foreignObject').attr('class','legend').attr('x',this.chartAreaSize.width+2).attr('width',width).attr('height',18).attr('transform',function(series,i){return'translate(0,'+i*18+')';}).append('xhtml:body').append('tr-ui-b-chart-legend-key').property('color',function(series){if(this.currentHighlightedLegendKey===series.key)
-return series.highlightedColor;return series.color;}.bind(this)).property('width',width).property('target',function(series){return series.target;}).property('optional',function(series){return series.optional;}).property('enabled',function(series){return series.enabled;}).text(function(series){return series.key;});legendEntriesSel.exit().remove();},get highlightedLegendKey(){return this.highlightedLegendKey_;},set highlightedLegendKey(highlightedLegendKey){this.highlightedLegendKey_=highlightedLegendKey;this.updateHighlight_();},get currentHighlightedLegendKey(){if(this.tempHighlightedLegendKey_)
-return this.tempHighlightedLegendKey_;return this.highlightedLegendKey_;},pushTempHighlightedLegendKey:function(key){if(this.tempHighlightedLegendKey_)
-throw new Error('push cannot nest');this.tempHighlightedLegendKey_=key;this.updateHighlight_();},popTempHighlightedLegendKey:function(key){if(this.tempHighlightedLegendKey_!=key)
-throw new Error('pop cannot happen');this.tempHighlightedLegendKey_=undefined;this.updateHighlight_();},updateHighlight_:function(){var chartAreaSel=d3.select(this.chartAreaElement);var legendEntriesSel=chartAreaSel.selectAll('.legend');var that=this;legendEntriesSel.each(function(key){var dataSeries=that.getDataSeries(key);if(key===that.currentHighlightedLegendKey){this.style.fill=dataSeries.highlightedColor;this.style.fontWeight='bold';}else{this.style.fill=dataSeries.color;this.style.fontWeight='';}});}};return{DataSeriesEnableChangeEventType:DataSeriesEnableChangeEventType,getColorOfKey:getColorOfKey,ChartBase:ChartBase};});'use strict';tr.exportTo('tr.ui.b',function(){var ChartBase=tr.ui.b.ChartBase;var ChartBase2D=tr.ui.b.define('chart-base-2d',ChartBase);ChartBase2D.prototype={__proto__:ChartBase.prototype,decorate:function(){ChartBase.prototype.decorate.call(this);Polymer.dom(this).classList.add('chart-base-2d');this.xScale_=d3.scale.linear();this.yScale_=d3.scale.linear();this.isYLogScale_=false;this.yLogScaleMin_=undefined;this.dataRange_=new tr.b.Range();this.hideXAxis_=false;this.hideYAxis_=false;this.data_=[];d3.select(this.chartAreaElement).append('g').attr('id','brushes');d3.select(this.chartAreaElement).append('g').attr('id','series');this.addEventListener('mousedown',this.onMouseDown_.bind(this));},get hideXAxis(){return this.hideXAxis_;},set hideXAxis(h){this.hideXAxis_=h;this.updateContents_();},get hideYAxis(){return this.hideYAxis_;},set hideYAxis(h){this.hideYAxis_=h;this.updateContents_();},get data(){return this.data_;},set data(data){if(data===undefined)
-throw new Error('data must be an Array');this.data_=data;this.updateSeriesKeys_();this.updateDataRange_();this.updateContents_();},set isYLogScale(logScale){if(logScale)
-this.yScale_=d3.scale.log(10);else
-this.yScale_=d3.scale.linear();this.isYLogScale_=logScale;},getYScaleMin_:function(){return this.isYLogScale_?this.yLogScaleMin_:0;},getYScaleDomain_:function(minValue,maxValue){if(this.isYLogScale_)
-return[this.getYScaleMin_(),maxValue];return[Math.min(minValue,this.getYScaleMin_()),maxValue];},getSampleWidth_:function(data,index,leftSide){var leftIndex,rightIndex;if(leftSide){leftIndex=Math.max(index-1,0);rightIndex=index;}else{leftIndex=index;rightIndex=Math.min(index+1,data.length-1);}
+var groupFunctions={none:rows=>rows,tree:function(rows,rowMap){var getParentRow=function(row){var pivot;row.contexts.forEach(function(context){if(context instanceof tr.e.chrome.FrameTreeNodeSnapshot){pivot=context;}});if(pivot&&pivot.parentContext){return rowMap[pivot.parentContext.guid];}
+return undefined;};var rootRows=[];rows.forEach(function(row){var parentRow=getParentRow(row);if(parentRow===undefined){rootRows.push(row);return;}
+if(parentRow.subRows===undefined){parentRow.subRows=[];}
+parentRow.subRows.push(row);});var aggregateAllDescendants=function(row){if(!row.subRows){if(getParentRow(row)){row.type='Subframe';}
+return row;}
+var result=new Row();result.type='Frame Tree';result.renderer=row.renderer;result.url=row.url;result.subRows=[row];row.subRows.forEach(subRow=>result.subRows.push(aggregateAllDescendants(subRow)));result.subRows.forEach(function(subRow){result.time+=subRow.time;result.eventsOfInterest.addEventSet(subRow.eventsOfInterest);});row.subRows=undefined;return result;};return rootRows.map(rootRow=>aggregateAllDescendants(rootRow));}};Polymer({is:'tr-ui-e-s-frame-data-side-panel',behaviors:[tr.ui.behaviors.SidePanel],ready:function(){this.model_=undefined;this.rangeOfInterest_=new tr.b.math.Range();this.$.table.showHeader=true;this.$.table.selectionMode=tr.ui.b.TableFormat.SelectionMode.ROW;this.$.table.tableColumns=this.createFrameDataTableColumns_();this.$.table.addEventListener('selection-changed',function(e){this.selectEventSet_(this.$.table.selectedTableRow.eventsOfInterest);}.bind(this));this.$.select.addEventListener('change',function(e){this.updateContents_();}.bind(this));},selectEventSet_:function(eventSet){var event=new tr.model.RequestSelectionChangeEvent();event.selection=eventSet;this.dispatchEvent(event);},createFrameDataTableColumns_:function(){return[{title:'Renderer',value:row=>row.renderer,cmp:(a,b)=>a.renderer-b.renderer},{title:'Type',value:row=>row.type},{title:'Time',value:row=>tr.v.ui.createScalarSpan(row.time,{unit:tr.b.Unit.byName.timeStampInMs,ownerDocument:this.ownerDocument}),cmp:(a,b)=>a.time-b.time},{title:'URL',value:row=>row.url,cmp:(a,b)=>(a.url||'').localeCompare(b.url||'')}];},createFrameDataTableRows_:function(){if(!this.model_)return[];var rows=[];var rowMap={};for(var proc of Object.values(this.model_.processes)){proc.objects.iterObjectInstances(function(objectInstance){if(!(objectInstance instanceof BlameContextInstance)){return;}
+objectInstance.snapshots.forEach(function(snapshot){if(rowMap[snapshot.guid])return;var row=new Row(snapshot);row.contexts.forEach(context=>rowMap[context.guid]=row);rows.push(row);},this);},this);}
+for(var proc of Object.values(this.model_.processes)){for(var thread of Object.values(proc.threads)){thread.sliceGroup.iterSlicesInTimeRange(function(topLevelSlice){topLevelSlice.contexts.forEach(function(context){if(!context.snapshot.guid||!rowMap[context.snapshot.guid]){return;}
+var row=rowMap[context.snapshot.guid];row.eventsOfInterest.push(topLevelSlice);row.time+=topLevelSlice.selfTime||0;});},this.currentRangeOfInterest.min,this.currentRangeOfInterest.max);}}
+var select=this.$.select;var groupOption=select.options[select.selectedIndex].value;var groupFunction=groupFunctions[groupOption];return groupFunction(rows,rowMap);},updateContents_:function(){this.$.table.tableRows=this.createFrameDataTableRows_();this.$.table.rebuild();},supportsModel:function(m){if(!m){return{supported:false,reason:'No model available.'};}
+var ans={supported:false};for(var proc of Object.values(m.processes)){proc.objects.iterObjectInstances(function(instance){if(instance instanceof BlameContextInstance){ans.supported=true;}});}
+if(!ans.supported){ans.reason='No frame data available';}
+return ans;},get currentRangeOfInterest(){if(this.rangeOfInterest_.isEmpty){return this.model_.bounds;}
+return this.rangeOfInterest_;},get rangeOfInterest(){return this.rangeOfInterest_;},set rangeOfInterest(rangeOfInterest){this.rangeOfInterest_=rangeOfInterest;this.updateContents_();},get selection(){},set selection(_){},get textLabel(){return'Frame Data';},get model(){return this.model_;},set model(model){this.model_=model;this.updateContents_();}});tr.ui.side_panel.SidePanelRegistry.register(function(){return document.createElement('tr-ui-e-s-frame-data-side-panel');});});'use strict';Polymer({is:'tr-ui-b-chart-legend-key',ready:function(){this.$.checkbox.addEventListener('change',this.onCheckboxChange_.bind(this));},onCheckboxChange_:function(){tr.b.dispatchSimpleEvent(this,tr.ui.b.DataSeriesEnableChangeEventType,true,false,{key:Polymer.dom(this).textContent,enabled:this.enabled});},set textContent(t){Polymer.dom(this.$.label).textContent=t;Polymer.dom(this.$.link).textContent=t;this.updateContents_();},set width(w){w-=20;this.$.link.style.width=w+'px';this.$.label.style.width=w+'px';},get textContent(){return Polymer.dom(this.$.label).textContent;},set optional(optional){this.$.checkbox.style.visibility=optional?'visible':'hidden';},get optional(){return this.$.checkbox.style.visibility==='visible';},set enabled(enabled){this.$.checkbox.checked=enabled?'checked':'';},get enabled(){return this.$.checkbox.checked;},set color(c){this.$.label.style.color=c;this.$.link.color=c;},set target(target){this.$.link.setSelectionAndContent(target,Polymer.dom(this.$.label).textContent);this.updateContents_();},get target(){return this.$.link.selection;},set title(title){this.$.link.title=title;},updateContents_:function(){this.$.link.style.display=this.target?'':'none';this.$.label.style.display=this.target?'none':'';this.$.label.htmlFor=this.optional?'checkbox':'';}});'use strict';(function(window){window.define=function(x){window.d3=x;};window.define.amd=true;})(this);!function(){function n(n){return null!=n&&!isNaN(n)}function t(n){return n.length}function e(n){for(var t=1;n*t%1;)t*=10;return t}function r(n,t){try{for(var e in t)Object.defineProperty(n.prototype,e,{value:t[e],enumerable:!1})}catch(r){n.prototype=t}}function u(){}function i(n){return aa+n in this}function o(n){return n=aa+n,n in this&&delete this[n]}function a(){var n=[];return this.forEach(function(t){n.push(t)}),n}function c(){var n=0;for(var t in this)t.charCodeAt(0)===ca&&++n;return n}function s(){for(var n in this)if(n.charCodeAt(0)===ca)return!1;return!0}function l(){}function f(n,t,e){return function(){var r=e.apply(t,arguments);return r===t?n:r}}function h(n,t){if(t in n)return t;t=t.charAt(0).toUpperCase()+t.substring(1);for(var e=0,r=sa.length;r>e;++e){var u=sa[e]+t;if(u in n)return u}}function g(){}function p(){}function v(n){function t(){for(var t,r=e,u=-1,i=r.length;++u<i;)(t=r[u].on)&&t.apply(this,arguments);return n}var e=[],r=new u;return t.on=function(t,u){var i,o=r.get(t);return arguments.length<2?o&&o.on:(o&&(o.on=null,e=e.slice(0,i=e.indexOf(o)).concat(e.slice(i+1)),r.remove(t)),u&&e.push(r.set(t,{on:u})),n)},t}function d(){Xo.event.preventDefault()}function m(){for(var n,t=Xo.event;n=t.sourceEvent;)t=n;return t}function y(n){for(var t=new p,e=0,r=arguments.length;++e<r;)t[arguments[e]]=v(t);return t.of=function(e,r){return function(u){try{var i=u.sourceEvent=Xo.event;u.target=n,Xo.event=u,t[u.type].apply(e,r)}finally{Xo.event=i}}},t}function x(n){return fa(n,da),n}function M(n){return"function"==typeof n?n:function(){return ha(n,this)}}function _(n){return"function"==typeof n?n:function(){return ga(n,this)}}function b(n,t){function e(){this.removeAttribute(n)}function r(){this.removeAttributeNS(n.space,n.local)}function u(){this.setAttribute(n,t)}function i(){this.setAttributeNS(n.space,n.local,t)}function o(){var e=t.apply(this,arguments);null==e?this.removeAttribute(n):this.setAttribute(n,e)}function a(){var e=t.apply(this,arguments);null==e?this.removeAttributeNS(n.space,n.local):this.setAttributeNS(n.space,n.local,e)}return n=Xo.ns.qualify(n),null==t?n.local?r:e:"function"==typeof t?n.local?a:o:n.local?i:u}function w(n){return n.trim().replace(/\s+/g," ")}function S(n){return new RegExp("(?:^|\\s+)"+Xo.requote(n)+"(?:\\s+|$)","g")}function k(n){return n.trim().split(/^|\s+/)}function E(n,t){function e(){for(var e=-1;++e<u;)n[e](this,t)}function r(){for(var e=-1,r=t.apply(this,arguments);++e<u;)n[e](this,r)}n=k(n).map(A);var u=n.length;return"function"==typeof t?r:e}function A(n){var t=S(n);return function(e,r){if(u=e.classList)return r?u.add(n):u.remove(n);var u=e.getAttribute("class")||"";r?(t.lastIndex=0,t.test(u)||e.setAttribute("class",w(u+" "+n))):e.setAttribute("class",w(u.replace(t," ")))}}function C(n,t,e){function r(){this.style.removeProperty(n)}function u(){this.style.setProperty(n,t,e)}function i(){var r=t.apply(this,arguments);null==r?this.style.removeProperty(n):this.style.setProperty(n,r,e)}return null==t?r:"function"==typeof t?i:u}function N(n,t){function e(){delete this[n]}function r(){this[n]=t}function u(){var e=t.apply(this,arguments);null==e?delete this[n]:this[n]=e}return null==t?e:"function"==typeof t?u:r}function L(n){return"function"==typeof n?n:(n=Xo.ns.qualify(n)).local?function(){return this.ownerDocument.createElementNS(n.space,n.local)}:function(){return this.ownerDocument.createElementNS(this.namespaceURI,n)}}function T(n){return{__data__:n}}function q(n){return function(){return va(this,n)}}function z(n){return arguments.length||(n=Xo.ascending),function(t,e){return t&&e?n(t.__data__,e.__data__):!t-!e}}function R(n,t){for(var e=0,r=n.length;r>e;e++)for(var u,i=n[e],o=0,a=i.length;a>o;o++)(u=i[o])&&t(u,o,e);return n}function D(n){return fa(n,ya),n}function P(n){var t,e;return function(r,u,i){var o,a=n[i].update,c=a.length;for(i!=e&&(e=i,t=0),u>=t&&(t=u+1);!(o=a[t])&&++t<c;);return o}}function U(){var n=this.__transition__;n&&++n.active}function j(n,t,e){function r(){var t=this[o];t&&(this.removeEventListener(n,t,t.$),delete this[o])}function u(){var u=c(t,Bo(arguments));r.call(this),this.addEventListener(n,this[o]=u,u.$=e),u._=t}function i(){var t,e=new RegExp("^__on([^.]+)"+Xo.requote(n)+"$");for(var r in this)if(t=r.match(e)){var u=this[r];this.removeEventListener(t[1],u,u.$),delete this[r]}}var o="__on"+n,a=n.indexOf("."),c=H;a>0&&(n=n.substring(0,a));var s=Ma.get(n);return s&&(n=s,c=F),a?t?u:r:t?g:i}function H(n,t){return function(e){var r=Xo.event;Xo.event=e,t[0]=this.__data__;try{n.apply(this,t)}finally{Xo.event=r}}}function F(n,t){var e=H(n,t);return function(n){var t=this,r=n.relatedTarget;r&&(r===t||8&r.compareDocumentPosition(t))||e.call(t,n)}}function O(){var n=".dragsuppress-"+ ++ba,t="click"+n,e=Xo.select(Go).on("touchmove"+n,d).on("dragstart"+n,d).on("selectstart"+n,d);if(_a){var r=Jo.style,u=r[_a];r[_a]="none"}return function(i){function o(){e.on(t,null)}e.on(n,null),_a&&(r[_a]=u),i&&(e.on(t,function(){d(),o()},!0),setTimeout(o,0))}}function Y(n,t){t.changedTouches&&(t=t.changedTouches[0]);var e=n.ownerSVGElement||n;if(e.createSVGPoint){var r=e.createSVGPoint();if(0>wa&&(Go.scrollX||Go.scrollY)){e=Xo.select("body").append("svg").style({position:"absolute",top:0,left:0,margin:0,padding:0,border:"none"},"important");var u=e[0][0].getScreenCTM();wa=!(u.f||u.e),e.remove()}return wa?(r.x=t.pageX,r.y=t.pageY):(r.x=t.clientX,r.y=t.clientY),r=r.matrixTransform(n.getScreenCTM().inverse()),[r.x,r.y]}var i=n.getBoundingClientRect();return[t.clientX-i.left-n.clientLeft,t.clientY-i.top-n.clientTop]}function I(n){return n>0?1:0>n?-1:0}function Z(n,t,e){return(t[0]-n[0])*(e[1]-n[1])-(t[1]-n[1])*(e[0]-n[0])}function V(n){return n>1?0:-1>n?Sa:Math.acos(n)}function X(n){return n>1?Ea:-1>n?-Ea:Math.asin(n)}function $(n){return((n=Math.exp(n))-1/n)/2}function B(n){return((n=Math.exp(n))+1/n)/2}function W(n){return((n=Math.exp(2*n))-1)/(n+1)}function J(n){return(n=Math.sin(n/2))*n}function G(){}function K(n,t,e){return new Q(n,t,e)}function Q(n,t,e){this.h=n,this.s=t,this.l=e}function nt(n,t,e){function r(n){return n>360?n-=360:0>n&&(n+=360),60>n?i+(o-i)*n/60:180>n?o:240>n?i+(o-i)*(240-n)/60:i}function u(n){return Math.round(255*r(n))}var i,o;return n=isNaN(n)?0:(n%=360)<0?n+360:n,t=isNaN(t)?0:0>t?0:t>1?1:t,e=0>e?0:e>1?1:e,o=.5>=e?e*(1+t):e+t-e*t,i=2*e-o,gt(u(n+120),u(n),u(n-120))}function tt(n,t,e){return new et(n,t,e)}function et(n,t,e){this.h=n,this.c=t,this.l=e}function rt(n,t,e){return isNaN(n)&&(n=0),isNaN(t)&&(t=0),ut(e,Math.cos(n*=Na)*t,Math.sin(n)*t)}function ut(n,t,e){return new it(n,t,e)}function it(n,t,e){this.l=n,this.a=t,this.b=e}function ot(n,t,e){var r=(n+16)/116,u=r+t/500,i=r-e/200;return u=ct(u)*Fa,r=ct(r)*Oa,i=ct(i)*Ya,gt(lt(3.2404542*u-1.5371385*r-.4985314*i),lt(-.969266*u+1.8760108*r+.041556*i),lt(.0556434*u-.2040259*r+1.0572252*i))}function at(n,t,e){return n>0?tt(Math.atan2(e,t)*La,Math.sqrt(t*t+e*e),n):tt(0/0,0/0,n)}function ct(n){return n>.206893034?n*n*n:(n-4/29)/7.787037}function st(n){return n>.008856?Math.pow(n,1/3):7.787037*n+4/29}function lt(n){return Math.round(255*(.00304>=n?12.92*n:1.055*Math.pow(n,1/2.4)-.055))}function ft(n){return gt(n>>16,255&n>>8,255&n)}function ht(n){return ft(n)+""}function gt(n,t,e){return new pt(n,t,e)}function pt(n,t,e){this.r=n,this.g=t,this.b=e}function vt(n){return 16>n?"0"+Math.max(0,n).toString(16):Math.min(255,n).toString(16)}function dt(n,t,e){var r,u,i,o,a=0,c=0,s=0;if(u=/([a-z]+)\((.*)\)/i.exec(n))switch(i=u[2].split(","),u[1]){case"hsl":return e(parseFloat(i[0]),parseFloat(i[1])/100,parseFloat(i[2])/100);case"rgb":return t(Mt(i[0]),Mt(i[1]),Mt(i[2]))}return(o=Va.get(n))?t(o.r,o.g,o.b):(null!=n&&"#"===n.charAt(0)&&(r=parseInt(n.substring(1),16),isNaN(r)||(4===n.length?(a=(3840&r)>>4,a=a>>4|a,c=240&r,c=c>>4|c,s=15&r,s=s<<4|s):7===n.length&&(a=(16711680&r)>>16,c=(65280&r)>>8,s=255&r))),t(a,c,s))}function mt(n,t,e){var r,u,i=Math.min(n/=255,t/=255,e/=255),o=Math.max(n,t,e),a=o-i,c=(o+i)/2;return a?(u=.5>c?a/(o+i):a/(2-o-i),r=n==o?(t-e)/a+(e>t?6:0):t==o?(e-n)/a+2:(n-t)/a+4,r*=60):(r=0/0,u=c>0&&1>c?0:r),K(r,u,c)}function yt(n,t,e){n=xt(n),t=xt(t),e=xt(e);var r=st((.4124564*n+.3575761*t+.1804375*e)/Fa),u=st((.2126729*n+.7151522*t+.072175*e)/Oa),i=st((.0193339*n+.119192*t+.9503041*e)/Ya);return ut(116*u-16,500*(r-u),200*(u-i))}function xt(n){return(n/=255)<=.04045?n/12.92:Math.pow((n+.055)/1.055,2.4)}function Mt(n){var t=parseFloat(n);return"%"===n.charAt(n.length-1)?Math.round(2.55*t):t}function _t(n){return"function"==typeof n?n:function(){return n}}function bt(n){return n}function wt(n){return function(t,e,r){return 2===arguments.length&&"function"==typeof e&&(r=e,e=null),St(t,e,n,r)}}function St(n,t,e,r){function u(){var n,t=c.status;if(!t&&c.responseText||t>=200&&300>t||304===t){try{n=e.call(i,c)}catch(r){return o.error.call(i,r),void 0}o.load.call(i,n)}else o.error.call(i,c)}var i={},o=Xo.dispatch("beforesend","progress","load","error"),a={},c=new XMLHttpRequest,s=null;return!Go.XDomainRequest||"withCredentials"in c||!/^(http(s)?:)?\/\//.test(n)||(c=new XDomainRequest),"onload"in c?c.onload=c.onerror=u:c.onreadystatechange=function(){c.readyState>3&&u()},c.onprogress=function(n){var t=Xo.event;Xo.event=n;try{o.progress.call(i,c)}finally{Xo.event=t}},i.header=function(n,t){return n=(n+"").toLowerCase(),arguments.length<2?a[n]:(null==t?delete a[n]:a[n]=t+"",i)},i.mimeType=function(n){return arguments.length?(t=null==n?null:n+"",i):t},i.responseType=function(n){return arguments.length?(s=n,i):s},i.response=function(n){return e=n,i},["get","post"].forEach(function(n){i[n]=function(){return i.send.apply(i,[n].concat(Bo(arguments)))}}),i.send=function(e,r,u){if(2===arguments.length&&"function"==typeof r&&(u=r,r=null),c.open(e,n,!0),null==t||"accept"in a||(a.accept=t+",*/*"),c.setRequestHeader)for(var l in a)c.setRequestHeader(l,a[l]);return null!=t&&c.overrideMimeType&&c.overrideMimeType(t),null!=s&&(c.responseType=s),null!=u&&i.on("error",u).on("load",function(n){u(null,n)}),o.beforesend.call(i,c),c.send(null==r?null:r),i},i.abort=function(){return c.abort(),i},Xo.rebind(i,o,"on"),null==r?i:i.get(kt(r))}function kt(n){return 1===n.length?function(t,e){n(null==t?e:null)}:n}function Et(){var n=At(),t=Ct()-n;t>24?(isFinite(t)&&(clearTimeout(Wa),Wa=setTimeout(Et,t)),Ba=0):(Ba=1,Ga(Et))}function At(){var n=Date.now();for(Ja=Xa;Ja;)n>=Ja.t&&(Ja.f=Ja.c(n-Ja.t)),Ja=Ja.n;return n}function Ct(){for(var n,t=Xa,e=1/0;t;)t.f?t=n?n.n=t.n:Xa=t.n:(t.t<e&&(e=t.t),t=(n=t).n);return $a=n,e}function Nt(n,t){return t-(n?Math.ceil(Math.log(n)/Math.LN10):1)}function Lt(n,t){var e=Math.pow(10,3*oa(8-t));return{scale:t>8?function(n){return n/e}:function(n){return n*e},symbol:n}}function Tt(n){var t=n.decimal,e=n.thousands,r=n.grouping,u=n.currency,i=r?function(n){for(var t=n.length,u=[],i=0,o=r[0];t>0&&o>0;)u.push(n.substring(t-=o,t+o)),o=r[i=(i+1)%r.length];return u.reverse().join(e)}:bt;return function(n){var e=Qa.exec(n),r=e[1]||" ",o=e[2]||">",a=e[3]||"",c=e[4]||"",s=e[5],l=+e[6],f=e[7],h=e[8],g=e[9],p=1,v="",d="",m=!1;switch(h&&(h=+h.substring(1)),(s||"0"===r&&"="===o)&&(s=r="0",o="=",f&&(l-=Math.floor((l-1)/4))),g){case"n":f=!0,g="g";break;case"%":p=100,d="%",g="f";break;case"p":p=100,d="%",g="r";break;case"b":case"o":case"x":case"X":"#"===c&&(v="0"+g.toLowerCase());case"c":case"d":m=!0,h=0;break;case"s":p=-1,g="r"}"$"===c&&(v=u[0],d=u[1]),"r"!=g||h||(g="g"),null!=h&&("g"==g?h=Math.max(1,Math.min(21,h)):("e"==g||"f"==g)&&(h=Math.max(0,Math.min(20,h)))),g=nc.get(g)||qt;var y=s&&f;return function(n){var e=d;if(m&&n%1)return"";var u=0>n||0===n&&0>1/n?(n=-n,"-"):a;if(0>p){var c=Xo.formatPrefix(n,h);n=c.scale(n),e=c.symbol+d}else n*=p;n=g(n,h);var x=n.lastIndexOf("."),M=0>x?n:n.substring(0,x),_=0>x?"":t+n.substring(x+1);!s&&f&&(M=i(M));var b=v.length+M.length+_.length+(y?0:u.length),w=l>b?new Array(b=l-b+1).join(r):"";return y&&(M=i(w+M)),u+=v,n=M+_,("<"===o?u+n+w:">"===o?w+u+n:"^"===o?w.substring(0,b>>=1)+u+n+w.substring(b):u+(y?n:w+n))+e}}}function qt(n){return n+""}function zt(){this._=new Date(arguments.length>1?Date.UTC.apply(this,arguments):arguments[0])}function Rt(n,t,e){function r(t){var e=n(t),r=i(e,1);return r-t>t-e?e:r}function u(e){return t(e=n(new ec(e-1)),1),e}function i(n,e){return t(n=new ec(+n),e),n}function o(n,r,i){var o=u(n),a=[];if(i>1)for(;r>o;)e(o)%i||a.push(new Date(+o)),t(o,1);else for(;r>o;)a.push(new Date(+o)),t(o,1);return a}function a(n,t,e){try{ec=zt;var r=new zt;return r._=n,o(r,t,e)}finally{ec=Date}}n.floor=n,n.round=r,n.ceil=u,n.offset=i,n.range=o;var c=n.utc=Dt(n);return c.floor=c,c.round=Dt(r),c.ceil=Dt(u),c.offset=Dt(i),c.range=a,n}function Dt(n){return function(t,e){try{ec=zt;var r=new zt;return r._=t,n(r,e)._}finally{ec=Date}}}function Pt(n){function t(n){function t(t){for(var e,u,i,o=[],a=-1,c=0;++a<r;)37===n.charCodeAt(a)&&(o.push(n.substring(c,a)),null!=(u=uc[e=n.charAt(++a)])&&(e=n.charAt(++a)),(i=C[e])&&(e=i(t,null==u?"e"===e?" ":"0":u)),o.push(e),c=a+1);return o.push(n.substring(c,a)),o.join("")}var r=n.length;return t.parse=function(t){var r={y:1900,m:0,d:1,H:0,M:0,S:0,L:0,Z:null},u=e(r,n,t,0);if(u!=t.length)return null;"p"in r&&(r.H=r.H%12+12*r.p);var i=null!=r.Z&&ec!==zt,o=new(i?zt:ec);return"j"in r?o.setFullYear(r.y,0,r.j):"w"in r&&("W"in r||"U"in r)?(o.setFullYear(r.y,0,1),o.setFullYear(r.y,0,"W"in r?(r.w+6)%7+7*r.W-(o.getDay()+5)%7:r.w+7*r.U-(o.getDay()+6)%7)):o.setFullYear(r.y,r.m,r.d),o.setHours(r.H+Math.floor(r.Z/100),r.M+r.Z%100,r.S,r.L),i?o._:o},t.toString=function(){return n},t}function e(n,t,e,r){for(var u,i,o,a=0,c=t.length,s=e.length;c>a;){if(r>=s)return-1;if(u=t.charCodeAt(a++),37===u){if(o=t.charAt(a++),i=N[o in uc?t.charAt(a++):o],!i||(r=i(n,e,r))<0)return-1}else if(u!=e.charCodeAt(r++))return-1}return r}function r(n,t,e){b.lastIndex=0;var r=b.exec(t.substring(e));return r?(n.w=w.get(r[0].toLowerCase()),e+r[0].length):-1}function u(n,t,e){M.lastIndex=0;var r=M.exec(t.substring(e));return r?(n.w=_.get(r[0].toLowerCase()),e+r[0].length):-1}function i(n,t,e){E.lastIndex=0;var r=E.exec(t.substring(e));return r?(n.m=A.get(r[0].toLowerCase()),e+r[0].length):-1}function o(n,t,e){S.lastIndex=0;var r=S.exec(t.substring(e));return r?(n.m=k.get(r[0].toLowerCase()),e+r[0].length):-1}function a(n,t,r){return e(n,C.c.toString(),t,r)}function c(n,t,r){return e(n,C.x.toString(),t,r)}function s(n,t,r){return e(n,C.X.toString(),t,r)}function l(n,t,e){var r=x.get(t.substring(e,e+=2).toLowerCase());return null==r?-1:(n.p=r,e)}var f=n.dateTime,h=n.date,g=n.time,p=n.periods,v=n.days,d=n.shortDays,m=n.months,y=n.shortMonths;t.utc=function(n){function e(n){try{ec=zt;var t=new ec;return t._=n,r(t)}finally{ec=Date}}var r=t(n);return e.parse=function(n){try{ec=zt;var t=r.parse(n);return t&&t._}finally{ec=Date}},e.toString=r.toString,e},t.multi=t.utc.multi=ee;var x=Xo.map(),M=jt(v),_=Ht(v),b=jt(d),w=Ht(d),S=jt(m),k=Ht(m),E=jt(y),A=Ht(y);p.forEach(function(n,t){x.set(n.toLowerCase(),t)});var C={a:function(n){return d[n.getDay()]},A:function(n){return v[n.getDay()]},b:function(n){return y[n.getMonth()]},B:function(n){return m[n.getMonth()]},c:t(f),d:function(n,t){return Ut(n.getDate(),t,2)},e:function(n,t){return Ut(n.getDate(),t,2)},H:function(n,t){return Ut(n.getHours(),t,2)},I:function(n,t){return Ut(n.getHours()%12||12,t,2)},j:function(n,t){return Ut(1+tc.dayOfYear(n),t,3)},L:function(n,t){return Ut(n.getMilliseconds(),t,3)},m:function(n,t){return Ut(n.getMonth()+1,t,2)},M:function(n,t){return Ut(n.getMinutes(),t,2)},p:function(n){return p[+(n.getHours()>=12)]},S:function(n,t){return Ut(n.getSeconds(),t,2)},U:function(n,t){return Ut(tc.sundayOfYear(n),t,2)},w:function(n){return n.getDay()},W:function(n,t){return Ut(tc.mondayOfYear(n),t,2)},x:t(h),X:t(g),y:function(n,t){return Ut(n.getFullYear()%100,t,2)},Y:function(n,t){return Ut(n.getFullYear()%1e4,t,4)},Z:ne,"%":function(){return"%"}},N={a:r,A:u,b:i,B:o,c:a,d:Bt,e:Bt,H:Jt,I:Jt,j:Wt,L:Qt,m:$t,M:Gt,p:l,S:Kt,U:Ot,w:Ft,W:Yt,x:c,X:s,y:Zt,Y:It,Z:Vt,"%":te};return t}function Ut(n,t,e){var r=0>n?"-":"",u=(r?-n:n)+"",i=u.length;return r+(e>i?new Array(e-i+1).join(t)+u:u)}function jt(n){return new RegExp("^(?:"+n.map(Xo.requote).join("|")+")","i")}function Ht(n){for(var t=new u,e=-1,r=n.length;++e<r;)t.set(n[e].toLowerCase(),e);return t}function Ft(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+1));return r?(n.w=+r[0],e+r[0].length):-1}function Ot(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e));return r?(n.U=+r[0],e+r[0].length):-1}function Yt(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e));return r?(n.W=+r[0],e+r[0].length):-1}function It(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+4));return r?(n.y=+r[0],e+r[0].length):-1}function Zt(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+2));return r?(n.y=Xt(+r[0]),e+r[0].length):-1}function Vt(n,t,e){return/^[+-]\d{4}$/.test(t=t.substring(e,e+5))?(n.Z=+t,e+5):-1}function Xt(n){return n+(n>68?1900:2e3)}function $t(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+2));return r?(n.m=r[0]-1,e+r[0].length):-1}function Bt(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+2));return r?(n.d=+r[0],e+r[0].length):-1}function Wt(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+3));return r?(n.j=+r[0],e+r[0].length):-1}function Jt(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+2));return r?(n.H=+r[0],e+r[0].length):-1}function Gt(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+2));return r?(n.M=+r[0],e+r[0].length):-1}function Kt(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+2));return r?(n.S=+r[0],e+r[0].length):-1}function Qt(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+3));return r?(n.L=+r[0],e+r[0].length):-1}function ne(n){var t=n.getTimezoneOffset(),e=t>0?"-":"+",r=~~(oa(t)/60),u=oa(t)%60;return e+Ut(r,"0",2)+Ut(u,"0",2)}function te(n,t,e){oc.lastIndex=0;var r=oc.exec(t.substring(e,e+1));return r?e+r[0].length:-1}function ee(n){for(var t=n.length,e=-1;++e<t;)n[e][0]=this(n[e][0]);return function(t){for(var e=0,r=n[e];!r[1](t);)r=n[++e];return r[0](t)}}function re(){}function ue(n,t,e){var r=e.s=n+t,u=r-n,i=r-u;e.t=n-i+(t-u)}function ie(n,t){n&&lc.hasOwnProperty(n.type)&&lc[n.type](n,t)}function oe(n,t,e){var r,u=-1,i=n.length-e;for(t.lineStart();++u<i;)r=n[u],t.point(r[0],r[1],r[2]);t.lineEnd()}function ae(n,t){var e=-1,r=n.length;for(t.polygonStart();++e<r;)oe(n[e],t,1);t.polygonEnd()}function ce(){function n(n,t){n*=Na,t=t*Na/2+Sa/4;var e=n-r,o=e>=0?1:-1,a=o*e,c=Math.cos(t),s=Math.sin(t),l=i*s,f=u*c+l*Math.cos(a),h=l*o*Math.sin(a);hc.add(Math.atan2(h,f)),r=n,u=c,i=s}var t,e,r,u,i;gc.point=function(o,a){gc.point=n,r=(t=o)*Na,u=Math.cos(a=(e=a)*Na/2+Sa/4),i=Math.sin(a)},gc.lineEnd=function(){n(t,e)}}function se(n){var t=n[0],e=n[1],r=Math.cos(e);return[r*Math.cos(t),r*Math.sin(t),Math.sin(e)]}function le(n,t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]}function fe(n,t){return[n[1]*t[2]-n[2]*t[1],n[2]*t[0]-n[0]*t[2],n[0]*t[1]-n[1]*t[0]]}function he(n,t){n[0]+=t[0],n[1]+=t[1],n[2]+=t[2]}function ge(n,t){return[n[0]*t,n[1]*t,n[2]*t]}function pe(n){var t=Math.sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]);n[0]/=t,n[1]/=t,n[2]/=t}function ve(n){return[Math.atan2(n[1],n[0]),X(n[2])]}function de(n,t){return oa(n[0]-t[0])<Aa&&oa(n[1]-t[1])<Aa}function me(n,t){n*=Na;var e=Math.cos(t*=Na);ye(e*Math.cos(n),e*Math.sin(n),Math.sin(t))}function ye(n,t,e){++pc,dc+=(n-dc)/pc,mc+=(t-mc)/pc,yc+=(e-yc)/pc}function xe(){function n(n,u){n*=Na;var i=Math.cos(u*=Na),o=i*Math.cos(n),a=i*Math.sin(n),c=Math.sin(u),s=Math.atan2(Math.sqrt((s=e*c-r*a)*s+(s=r*o-t*c)*s+(s=t*a-e*o)*s),t*o+e*a+r*c);vc+=s,xc+=s*(t+(t=o)),Mc+=s*(e+(e=a)),_c+=s*(r+(r=c)),ye(t,e,r)}var t,e,r;kc.point=function(u,i){u*=Na;var o=Math.cos(i*=Na);t=o*Math.cos(u),e=o*Math.sin(u),r=Math.sin(i),kc.point=n,ye(t,e,r)}}function Me(){kc.point=me}function _e(){function n(n,t){n*=Na;var e=Math.cos(t*=Na),o=e*Math.cos(n),a=e*Math.sin(n),c=Math.sin(t),s=u*c-i*a,l=i*o-r*c,f=r*a-u*o,h=Math.sqrt(s*s+l*l+f*f),g=r*o+u*a+i*c,p=h&&-V(g)/h,v=Math.atan2(h,g);bc+=p*s,wc+=p*l,Sc+=p*f,vc+=v,xc+=v*(r+(r=o)),Mc+=v*(u+(u=a)),_c+=v*(i+(i=c)),ye(r,u,i)}var t,e,r,u,i;kc.point=function(o,a){t=o,e=a,kc.point=n,o*=Na;var c=Math.cos(a*=Na);r=c*Math.cos(o),u=c*Math.sin(o),i=Math.sin(a),ye(r,u,i)},kc.lineEnd=function(){n(t,e),kc.lineEnd=Me,kc.point=me}}function be(){return!0}function we(n,t,e,r,u){var i=[],o=[];if(n.forEach(function(n){if(!((t=n.length-1)<=0)){var t,e=n[0],r=n[t];if(de(e,r)){u.lineStart();for(var a=0;t>a;++a)u.point((e=n[a])[0],e[1]);return u.lineEnd(),void 0}var c=new ke(e,n,null,!0),s=new ke(e,null,c,!1);c.o=s,i.push(c),o.push(s),c=new ke(r,n,null,!1),s=new ke(r,null,c,!0),c.o=s,i.push(c),o.push(s)}}),o.sort(t),Se(i),Se(o),i.length){for(var a=0,c=e,s=o.length;s>a;++a)o[a].e=c=!c;for(var l,f,h=i[0];;){for(var g=h,p=!0;g.v;)if((g=g.n)===h)return;l=g.z,u.lineStart();do{if(g.v=g.o.v=!0,g.e){if(p)for(var a=0,s=l.length;s>a;++a)u.point((f=l[a])[0],f[1]);else r(g.x,g.n.x,1,u);g=g.n}else{if(p){l=g.p.z;for(var a=l.length-1;a>=0;--a)u.point((f=l[a])[0],f[1])}else r(g.x,g.p.x,-1,u);g=g.p}g=g.o,l=g.z,p=!p}while(!g.v);u.lineEnd()}}}function Se(n){if(t=n.length){for(var t,e,r=0,u=n[0];++r<t;)u.n=e=n[r],e.p=u,u=e;u.n=e=n[0],e.p=u}}function ke(n,t,e,r){this.x=n,this.z=t,this.o=e,this.e=r,this.v=!1,this.n=this.p=null}function Ee(n,t,e,r){return function(u,i){function o(t,e){var r=u(t,e);n(t=r[0],e=r[1])&&i.point(t,e)}function a(n,t){var e=u(n,t);d.point(e[0],e[1])}function c(){y.point=a,d.lineStart()}function s(){y.point=o,d.lineEnd()}function l(n,t){v.push([n,t]);var e=u(n,t);M.point(e[0],e[1])}function f(){M.lineStart(),v=[]}function h(){l(v[0][0],v[0][1]),M.lineEnd();var n,t=M.clean(),e=x.buffer(),r=e.length;if(v.pop(),p.push(v),v=null,r){if(1&t){n=e[0];var u,r=n.length-1,o=-1;for(i.lineStart();++o<r;)i.point((u=n[o])[0],u[1]);return i.lineEnd(),void 0}r>1&&2&t&&e.push(e.pop().concat(e.shift())),g.push(e.filter(Ae))}}var g,p,v,d=t(i),m=u.invert(r[0],r[1]),y={point:o,lineStart:c,lineEnd:s,polygonStart:function(){y.point=l,y.lineStart=f,y.lineEnd=h,g=[],p=[],i.polygonStart()},polygonEnd:function(){y.point=o,y.lineStart=c,y.lineEnd=s,g=Xo.merge(g);var n=Le(m,p);g.length?we(g,Ne,n,e,i):n&&(i.lineStart(),e(null,null,1,i),i.lineEnd()),i.polygonEnd(),g=p=null},sphere:function(){i.polygonStart(),i.lineStart(),e(null,null,1,i),i.lineEnd(),i.polygonEnd()}},x=Ce(),M=t(x);return y}}function Ae(n){return n.length>1}function Ce(){var n,t=[];return{lineStart:function(){t.push(n=[])},point:function(t,e){n.push([t,e])},lineEnd:g,buffer:function(){var e=t;return t=[],n=null,e},rejoin:function(){t.length>1&&t.push(t.pop().concat(t.shift()))}}}function Ne(n,t){return((n=n.x)[0]<0?n[1]-Ea-Aa:Ea-n[1])-((t=t.x)[0]<0?t[1]-Ea-Aa:Ea-t[1])}function Le(n,t){var e=n[0],r=n[1],u=[Math.sin(e),-Math.cos(e),0],i=0,o=0;hc.reset();for(var a=0,c=t.length;c>a;++a){var s=t[a],l=s.length;if(l)for(var f=s[0],h=f[0],g=f[1]/2+Sa/4,p=Math.sin(g),v=Math.cos(g),d=1;;){d===l&&(d=0),n=s[d];var m=n[0],y=n[1]/2+Sa/4,x=Math.sin(y),M=Math.cos(y),_=m-h,b=_>=0?1:-1,w=b*_,S=w>Sa,k=p*x;if(hc.add(Math.atan2(k*b*Math.sin(w),v*M+k*Math.cos(w))),i+=S?_+b*ka:_,S^h>=e^m>=e){var E=fe(se(f),se(n));pe(E);var A=fe(u,E);pe(A);var C=(S^_>=0?-1:1)*X(A[2]);(r>C||r===C&&(E[0]||E[1]))&&(o+=S^_>=0?1:-1)}if(!d++)break;h=m,p=x,v=M,f=n}}return(-Aa>i||Aa>i&&0>hc)^1&o}function Te(n){var t,e=0/0,r=0/0,u=0/0;return{lineStart:function(){n.lineStart(),t=1},point:function(i,o){var a=i>0?Sa:-Sa,c=oa(i-e);oa(c-Sa)<Aa?(n.point(e,r=(r+o)/2>0?Ea:-Ea),n.point(u,r),n.lineEnd(),n.lineStart(),n.point(a,r),n.point(i,r),t=0):u!==a&&c>=Sa&&(oa(e-u)<Aa&&(e-=u*Aa),oa(i-a)<Aa&&(i-=a*Aa),r=qe(e,r,i,o),n.point(u,r),n.lineEnd(),n.lineStart(),n.point(a,r),t=0),n.point(e=i,r=o),u=a},lineEnd:function(){n.lineEnd(),e=r=0/0},clean:function(){return 2-t}}}function qe(n,t,e,r){var u,i,o=Math.sin(n-e);return oa(o)>Aa?Math.atan((Math.sin(t)*(i=Math.cos(r))*Math.sin(e)-Math.sin(r)*(u=Math.cos(t))*Math.sin(n))/(u*i*o)):(t+r)/2}function ze(n,t,e,r){var u;if(null==n)u=e*Ea,r.point(-Sa,u),r.point(0,u),r.point(Sa,u),r.point(Sa,0),r.point(Sa,-u),r.point(0,-u),r.point(-Sa,-u),r.point(-Sa,0),r.point(-Sa,u);else if(oa(n[0]-t[0])>Aa){var i=n[0]<t[0]?Sa:-Sa;u=e*i/2,r.point(-i,u),r.point(0,u),r.point(i,u)}else r.point(t[0],t[1])}function Re(n){function t(n,t){return Math.cos(n)*Math.cos(t)>i}function e(n){var e,i,c,s,l;return{lineStart:function(){s=c=!1,l=1},point:function(f,h){var g,p=[f,h],v=t(f,h),d=o?v?0:u(f,h):v?u(f+(0>f?Sa:-Sa),h):0;if(!e&&(s=c=v)&&n.lineStart(),v!==c&&(g=r(e,p),(de(e,g)||de(p,g))&&(p[0]+=Aa,p[1]+=Aa,v=t(p[0],p[1]))),v!==c)l=0,v?(n.lineStart(),g=r(p,e),n.point(g[0],g[1])):(g=r(e,p),n.point(g[0],g[1]),n.lineEnd()),e=g;else if(a&&e&&o^v){var m;d&i||!(m=r(p,e,!0))||(l=0,o?(n.lineStart(),n.point(m[0][0],m[0][1]),n.point(m[1][0],m[1][1]),n.lineEnd()):(n.point(m[1][0],m[1][1]),n.lineEnd(),n.lineStart(),n.point(m[0][0],m[0][1])))}!v||e&&de(e,p)||n.point(p[0],p[1]),e=p,c=v,i=d},lineEnd:function(){c&&n.lineEnd(),e=null},clean:function(){return l|(s&&c)<<1}}}function r(n,t,e){var r=se(n),u=se(t),o=[1,0,0],a=fe(r,u),c=le(a,a),s=a[0],l=c-s*s;if(!l)return!e&&n;var f=i*c/l,h=-i*s/l,g=fe(o,a),p=ge(o,f),v=ge(a,h);he(p,v);var d=g,m=le(p,d),y=le(d,d),x=m*m-y*(le(p,p)-1);if(!(0>x)){var M=Math.sqrt(x),_=ge(d,(-m-M)/y);if(he(_,p),_=ve(_),!e)return _;var b,w=n[0],S=t[0],k=n[1],E=t[1];w>S&&(b=w,w=S,S=b);var A=S-w,C=oa(A-Sa)<Aa,N=C||Aa>A;if(!C&&k>E&&(b=k,k=E,E=b),N?C?k+E>0^_[1]<(oa(_[0]-w)<Aa?k:E):k<=_[1]&&_[1]<=E:A>Sa^(w<=_[0]&&_[0]<=S)){var L=ge(d,(-m+M)/y);return he(L,p),[_,ve(L)]}}}function u(t,e){var r=o?n:Sa-n,u=0;return-r>t?u|=1:t>r&&(u|=2),-r>e?u|=4:e>r&&(u|=8),u}var i=Math.cos(n),o=i>0,a=oa(i)>Aa,c=cr(n,6*Na);return Ee(t,e,c,o?[0,-n]:[-Sa,n-Sa])}function De(n,t,e,r){return function(u){var i,o=u.a,a=u.b,c=o.x,s=o.y,l=a.x,f=a.y,h=0,g=1,p=l-c,v=f-s;if(i=n-c,p||!(i>0)){if(i/=p,0>p){if(h>i)return;g>i&&(g=i)}else if(p>0){if(i>g)return;i>h&&(h=i)}if(i=e-c,p||!(0>i)){if(i/=p,0>p){if(i>g)return;i>h&&(h=i)}else if(p>0){if(h>i)return;g>i&&(g=i)}if(i=t-s,v||!(i>0)){if(i/=v,0>v){if(h>i)return;g>i&&(g=i)}else if(v>0){if(i>g)return;i>h&&(h=i)}if(i=r-s,v||!(0>i)){if(i/=v,0>v){if(i>g)return;i>h&&(h=i)}else if(v>0){if(h>i)return;g>i&&(g=i)}return h>0&&(u.a={x:c+h*p,y:s+h*v}),1>g&&(u.b={x:c+g*p,y:s+g*v}),u}}}}}}function Pe(n,t,e,r){function u(r,u){return oa(r[0]-n)<Aa?u>0?0:3:oa(r[0]-e)<Aa?u>0?2:1:oa(r[1]-t)<Aa?u>0?1:0:u>0?3:2}function i(n,t){return o(n.x,t.x)}function o(n,t){var e=u(n,1),r=u(t,1);return e!==r?e-r:0===e?t[1]-n[1]:1===e?n[0]-t[0]:2===e?n[1]-t[1]:t[0]-n[0]}return function(a){function c(n){for(var t=0,e=d.length,r=n[1],u=0;e>u;++u)for(var i,o=1,a=d[u],c=a.length,s=a[0];c>o;++o)i=a[o],s[1]<=r?i[1]>r&&Z(s,i,n)>0&&++t:i[1]<=r&&Z(s,i,n)<0&&--t,s=i;return 0!==t}function s(i,a,c,s){var l=0,f=0;if(null==i||(l=u(i,c))!==(f=u(a,c))||o(i,a)<0^c>0){do s.point(0===l||3===l?n:e,l>1?r:t);while((l=(l+c+4)%4)!==f)}else s.point(a[0],a[1])}function l(u,i){return u>=n&&e>=u&&i>=t&&r>=i}function f(n,t){l(n,t)&&a.point(n,t)}function h(){N.point=p,d&&d.push(m=[]),S=!0,w=!1,_=b=0/0}function g(){v&&(p(y,x),M&&w&&A.rejoin(),v.push(A.buffer())),N.point=f,w&&a.lineEnd()}function p(n,t){n=Math.max(-Ac,Math.min(Ac,n)),t=Math.max(-Ac,Math.min(Ac,t));var e=l(n,t);if(d&&m.push([n,t]),S)y=n,x=t,M=e,S=!1,e&&(a.lineStart(),a.point(n,t));else if(e&&w)a.point(n,t);else{var r={a:{x:_,y:b},b:{x:n,y:t}};C(r)?(w||(a.lineStart(),a.point(r.a.x,r.a.y)),a.point(r.b.x,r.b.y),e||a.lineEnd(),k=!1):e&&(a.lineStart(),a.point(n,t),k=!1)}_=n,b=t,w=e}var v,d,m,y,x,M,_,b,w,S,k,E=a,A=Ce(),C=De(n,t,e,r),N={point:f,lineStart:h,lineEnd:g,polygonStart:function(){a=A,v=[],d=[],k=!0},polygonEnd:function(){a=E,v=Xo.merge(v);var t=c([n,r]),e=k&&t,u=v.length;(e||u)&&(a.polygonStart(),e&&(a.lineStart(),s(null,null,1,a),a.lineEnd()),u&&we(v,i,t,s,a),a.polygonEnd()),v=d=m=null}};return N}}function Ue(n,t){function e(e,r){return e=n(e,r),t(e[0],e[1])}return n.invert&&t.invert&&(e.invert=function(e,r){return e=t.invert(e,r),e&&n.invert(e[0],e[1])}),e}function je(n){var t=0,e=Sa/3,r=nr(n),u=r(t,e);return u.parallels=function(n){return arguments.length?r(t=n[0]*Sa/180,e=n[1]*Sa/180):[180*(t/Sa),180*(e/Sa)]},u}function He(n,t){function e(n,t){var e=Math.sqrt(i-2*u*Math.sin(t))/u;return[e*Math.sin(n*=u),o-e*Math.cos(n)]}var r=Math.sin(n),u=(r+Math.sin(t))/2,i=1+r*(2*u-r),o=Math.sqrt(i)/u;return e.invert=function(n,t){var e=o-t;return[Math.atan2(n,e)/u,X((i-(n*n+e*e)*u*u)/(2*u))]},e}function Fe(){function n(n,t){Nc+=u*n-r*t,r=n,u=t}var t,e,r,u;Rc.point=function(i,o){Rc.point=n,t=r=i,e=u=o},Rc.lineEnd=function(){n(t,e)}}function Oe(n,t){Lc>n&&(Lc=n),n>qc&&(qc=n),Tc>t&&(Tc=t),t>zc&&(zc=t)}function Ye(){function n(n,t){o.push("M",n,",",t,i)}function t(n,t){o.push("M",n,",",t),a.point=e}function e(n,t){o.push("L",n,",",t)}function r(){a.point=n}function u(){o.push("Z")}var i=Ie(4.5),o=[],a={point:n,lineStart:function(){a.point=t},lineEnd:r,polygonStart:function(){a.lineEnd=u},polygonEnd:function(){a.lineEnd=r,a.point=n},pointRadius:function(n){return i=Ie(n),a},result:function(){if(o.length){var n=o.join("");return o=[],n}}};return a}function Ie(n){return"m0,"+n+"a"+n+","+n+" 0 1,1 0,"+-2*n+"a"+n+","+n+" 0 1,1 0,"+2*n+"z"}function Ze(n,t){dc+=n,mc+=t,++yc}function Ve(){function n(n,r){var u=n-t,i=r-e,o=Math.sqrt(u*u+i*i);xc+=o*(t+n)/2,Mc+=o*(e+r)/2,_c+=o,Ze(t=n,e=r)}var t,e;Pc.point=function(r,u){Pc.point=n,Ze(t=r,e=u)}}function Xe(){Pc.point=Ze}function $e(){function n(n,t){var e=n-r,i=t-u,o=Math.sqrt(e*e+i*i);xc+=o*(r+n)/2,Mc+=o*(u+t)/2,_c+=o,o=u*n-r*t,bc+=o*(r+n),wc+=o*(u+t),Sc+=3*o,Ze(r=n,u=t)}var t,e,r,u;Pc.point=function(i,o){Pc.point=n,Ze(t=r=i,e=u=o)},Pc.lineEnd=function(){n(t,e)}}function Be(n){function t(t,e){n.moveTo(t,e),n.arc(t,e,o,0,ka)}function e(t,e){n.moveTo(t,e),a.point=r}function r(t,e){n.lineTo(t,e)}function u(){a.point=t}function i(){n.closePath()}var o=4.5,a={point:t,lineStart:function(){a.point=e},lineEnd:u,polygonStart:function(){a.lineEnd=i},polygonEnd:function(){a.lineEnd=u,a.point=t},pointRadius:function(n){return o=n,a},result:g};return a}function We(n){function t(n){return(a?r:e)(n)}function e(t){return Ke(t,function(e,r){e=n(e,r),t.point(e[0],e[1])})}function r(t){function e(e,r){e=n(e,r),t.point(e[0],e[1])}function r(){x=0/0,S.point=i,t.lineStart()}function i(e,r){var i=se([e,r]),o=n(e,r);u(x,M,y,_,b,w,x=o[0],M=o[1],y=e,_=i[0],b=i[1],w=i[2],a,t),t.point(x,M)}function o(){S.point=e,t.lineEnd()}function c(){r(),S.point=s,S.lineEnd=l}function s(n,t){i(f=n,h=t),g=x,p=M,v=_,d=b,m=w,S.point=i}function l(){u(x,M,y,_,b,w,g,p,f,v,d,m,a,t),S.lineEnd=o,o()}var f,h,g,p,v,d,m,y,x,M,_,b,w,S={point:e,lineStart:r,lineEnd:o,polygonStart:function(){t.polygonStart(),S.lineStart=c},polygonEnd:function(){t.polygonEnd(),S.lineStart=r}};return S}function u(t,e,r,a,c,s,l,f,h,g,p,v,d,m){var y=l-t,x=f-e,M=y*y+x*x;if(M>4*i&&d--){var _=a+g,b=c+p,w=s+v,S=Math.sqrt(_*_+b*b+w*w),k=Math.asin(w/=S),E=oa(oa(w)-1)<Aa||oa(r-h)<Aa?(r+h)/2:Math.atan2(b,_),A=n(E,k),C=A[0],N=A[1],L=C-t,T=N-e,q=x*L-y*T;(q*q/M>i||oa((y*L+x*T)/M-.5)>.3||o>a*g+c*p+s*v)&&(u(t,e,r,a,c,s,C,N,E,_/=S,b/=S,w,d,m),m.point(C,N),u(C,N,E,_,b,w,l,f,h,g,p,v,d,m))}}var i=.5,o=Math.cos(30*Na),a=16;return t.precision=function(n){return arguments.length?(a=(i=n*n)>0&&16,t):Math.sqrt(i)},t}function Je(n){var t=We(function(t,e){return n([t*La,e*La])});return function(n){return tr(t(n))}}function Ge(n){this.stream=n}function Ke(n,t){return{point:t,sphere:function(){n.sphere()},lineStart:function(){n.lineStart()},lineEnd:function(){n.lineEnd()},polygonStart:function(){n.polygonStart()},polygonEnd:function(){n.polygonEnd()}}}function Qe(n){return nr(function(){return n})()}function nr(n){function t(n){return n=a(n[0]*Na,n[1]*Na),[n[0]*h+c,s-n[1]*h]}function e(n){return n=a.invert((n[0]-c)/h,(s-n[1])/h),n&&[n[0]*La,n[1]*La]}function r(){a=Ue(o=ur(m,y,x),i);var n=i(v,d);return c=g-n[0]*h,s=p+n[1]*h,u()}function u(){return l&&(l.valid=!1,l=null),t}var i,o,a,c,s,l,f=We(function(n,t){return n=i(n,t),[n[0]*h+c,s-n[1]*h]}),h=150,g=480,p=250,v=0,d=0,m=0,y=0,x=0,M=Ec,_=bt,b=null,w=null;return t.stream=function(n){return l&&(l.valid=!1),l=tr(M(o,f(_(n)))),l.valid=!0,l},t.clipAngle=function(n){return arguments.length?(M=null==n?(b=n,Ec):Re((b=+n)*Na),u()):b},t.clipExtent=function(n){return arguments.length?(w=n,_=n?Pe(n[0][0],n[0][1],n[1][0],n[1][1]):bt,u()):w},t.scale=function(n){return arguments.length?(h=+n,r()):h},t.translate=function(n){return arguments.length?(g=+n[0],p=+n[1],r()):[g,p]},t.center=function(n){return arguments.length?(v=n[0]%360*Na,d=n[1]%360*Na,r()):[v*La,d*La]},t.rotate=function(n){return arguments.length?(m=n[0]%360*Na,y=n[1]%360*Na,x=n.length>2?n[2]%360*Na:0,r()):[m*La,y*La,x*La]},Xo.rebind(t,f,"precision"),function(){return i=n.apply(this,arguments),t.invert=i.invert&&e,r()}}function tr(n){return Ke(n,function(t,e){n.point(t*Na,e*Na)})}function er(n,t){return[n,t]}function rr(n,t){return[n>Sa?n-ka:-Sa>n?n+ka:n,t]}function ur(n,t,e){return n?t||e?Ue(or(n),ar(t,e)):or(n):t||e?ar(t,e):rr}function ir(n){return function(t,e){return t+=n,[t>Sa?t-ka:-Sa>t?t+ka:t,e]}}function or(n){var t=ir(n);return t.invert=ir(-n),t}function ar(n,t){function e(n,t){var e=Math.cos(t),a=Math.cos(n)*e,c=Math.sin(n)*e,s=Math.sin(t),l=s*r+a*u;return[Math.atan2(c*i-l*o,a*r-s*u),X(l*i+c*o)]}var r=Math.cos(n),u=Math.sin(n),i=Math.cos(t),o=Math.sin(t);return e.invert=function(n,t){var e=Math.cos(t),a=Math.cos(n)*e,c=Math.sin(n)*e,s=Math.sin(t),l=s*i-c*o;return[Math.atan2(c*i+s*o,a*r+l*u),X(l*r-a*u)]},e}function cr(n,t){var e=Math.cos(n),r=Math.sin(n);return function(u,i,o,a){var c=o*t;null!=u?(u=sr(e,u),i=sr(e,i),(o>0?i>u:u>i)&&(u+=o*ka)):(u=n+o*ka,i=n-.5*c);for(var s,l=u;o>0?l>i:i>l;l-=c)a.point((s=ve([e,-r*Math.cos(l),-r*Math.sin(l)]))[0],s[1])}}function sr(n,t){var e=se(t);e[0]-=n,pe(e);var r=V(-e[1]);return((-e[2]<0?-r:r)+2*Math.PI-Aa)%(2*Math.PI)}function lr(n,t,e){var r=Xo.range(n,t-Aa,e).concat(t);return function(n){return r.map(function(t){return[n,t]})}}function fr(n,t,e){var r=Xo.range(n,t-Aa,e).concat(t);return function(n){return r.map(function(t){return[t,n]})}}function hr(n){return n.source}function gr(n){return n.target}function pr(n,t,e,r){var u=Math.cos(t),i=Math.sin(t),o=Math.cos(r),a=Math.sin(r),c=u*Math.cos(n),s=u*Math.sin(n),l=o*Math.cos(e),f=o*Math.sin(e),h=2*Math.asin(Math.sqrt(J(r-t)+u*o*J(e-n))),g=1/Math.sin(h),p=h?function(n){var t=Math.sin(n*=h)*g,e=Math.sin(h-n)*g,r=e*c+t*l,u=e*s+t*f,o=e*i+t*a;return[Math.atan2(u,r)*La,Math.atan2(o,Math.sqrt(r*r+u*u))*La]}:function(){return[n*La,t*La]};return p.distance=h,p}function vr(){function n(n,u){var i=Math.sin(u*=Na),o=Math.cos(u),a=oa((n*=Na)-t),c=Math.cos(a);Uc+=Math.atan2(Math.sqrt((a=o*Math.sin(a))*a+(a=r*i-e*o*c)*a),e*i+r*o*c),t=n,e=i,r=o}var t,e,r;jc.point=function(u,i){t=u*Na,e=Math.sin(i*=Na),r=Math.cos(i),jc.point=n},jc.lineEnd=function(){jc.point=jc.lineEnd=g}}function dr(n,t){function e(t,e){var r=Math.cos(t),u=Math.cos(e),i=n(r*u);return[i*u*Math.sin(t),i*Math.sin(e)]}return e.invert=function(n,e){var r=Math.sqrt(n*n+e*e),u=t(r),i=Math.sin(u),o=Math.cos(u);return[Math.atan2(n*i,r*o),Math.asin(r&&e*i/r)]},e}function mr(n,t){function e(n,t){var e=oa(oa(t)-Ea)<Aa?0:o/Math.pow(u(t),i);return[e*Math.sin(i*n),o-e*Math.cos(i*n)]}var r=Math.cos(n),u=function(n){return Math.tan(Sa/4+n/2)},i=n===t?Math.sin(n):Math.log(r/Math.cos(t))/Math.log(u(t)/u(n)),o=r*Math.pow(u(n),i)/i;return i?(e.invert=function(n,t){var e=o-t,r=I(i)*Math.sqrt(n*n+e*e);return[Math.atan2(n,e)/i,2*Math.atan(Math.pow(o/r,1/i))-Ea]},e):xr}function yr(n,t){function e(n,t){var e=i-t;return[e*Math.sin(u*n),i-e*Math.cos(u*n)]}var r=Math.cos(n),u=n===t?Math.sin(n):(r-Math.cos(t))/(t-n),i=r/u+n;return oa(u)<Aa?er:(e.invert=function(n,t){var e=i-t;return[Math.atan2(n,e)/u,i-I(u)*Math.sqrt(n*n+e*e)]},e)}function xr(n,t){return[n,Math.log(Math.tan(Sa/4+t/2))]}function Mr(n){var t,e=Qe(n),r=e.scale,u=e.translate,i=e.clipExtent;return e.scale=function(){var n=r.apply(e,arguments);return n===e?t?e.clipExtent(null):e:n},e.translate=function(){var n=u.apply(e,arguments);return n===e?t?e.clipExtent(null):e:n},e.clipExtent=function(n){var o=i.apply(e,arguments);if(o===e){if(t=null==n){var a=Sa*r(),c=u();i([[c[0]-a,c[1]-a],[c[0]+a,c[1]+a]])}}else t&&(o=null);return o},e.clipExtent(null)}function _r(n,t){return[Math.log(Math.tan(Sa/4+t/2)),-n]}function br(n){return n[0]}function wr(n){return n[1]}function Sr(n){for(var t=n.length,e=[0,1],r=2,u=2;t>u;u++){for(;r>1&&Z(n[e[r-2]],n[e[r-1]],n[u])<=0;)--r;e[r++]=u}return e.slice(0,r)}function kr(n,t){return n[0]-t[0]||n[1]-t[1]}function Er(n,t,e){return(e[0]-t[0])*(n[1]-t[1])<(e[1]-t[1])*(n[0]-t[0])}function Ar(n,t,e,r){var u=n[0],i=e[0],o=t[0]-u,a=r[0]-i,c=n[1],s=e[1],l=t[1]-c,f=r[1]-s,h=(a*(c-s)-f*(u-i))/(f*o-a*l);return[u+h*o,c+h*l]}function Cr(n){var t=n[0],e=n[n.length-1];return!(t[0]-e[0]||t[1]-e[1])}function Nr(){Jr(this),this.edge=this.site=this.circle=null}function Lr(n){var t=Jc.pop()||new Nr;return t.site=n,t}function Tr(n){Or(n),$c.remove(n),Jc.push(n),Jr(n)}function qr(n){var t=n.circle,e=t.x,r=t.cy,u={x:e,y:r},i=n.P,o=n.N,a=[n];Tr(n);for(var c=i;c.circle&&oa(e-c.circle.x)<Aa&&oa(r-c.circle.cy)<Aa;)i=c.P,a.unshift(c),Tr(c),c=i;a.unshift(c),Or(c);for(var s=o;s.circle&&oa(e-s.circle.x)<Aa&&oa(r-s.circle.cy)<Aa;)o=s.N,a.push(s),Tr(s),s=o;a.push(s),Or(s);var l,f=a.length;for(l=1;f>l;++l)s=a[l],c=a[l-1],$r(s.edge,c.site,s.site,u);c=a[0],s=a[f-1],s.edge=Vr(c.site,s.site,null,u),Fr(c),Fr(s)}function zr(n){for(var t,e,r,u,i=n.x,o=n.y,a=$c._;a;)if(r=Rr(a,o)-i,r>Aa)a=a.L;else{if(u=i-Dr(a,o),!(u>Aa)){r>-Aa?(t=a.P,e=a):u>-Aa?(t=a,e=a.N):t=e=a;break}if(!a.R){t=a;break}a=a.R}var c=Lr(n);if($c.insert(t,c),t||e){if(t===e)return Or(t),e=Lr(t.site),$c.insert(c,e),c.edge=e.edge=Vr(t.site,c.site),Fr(t),Fr(e),void 0;if(!e)return c.edge=Vr(t.site,c.site),void 0;Or(t),Or(e);var s=t.site,l=s.x,f=s.y,h=n.x-l,g=n.y-f,p=e.site,v=p.x-l,d=p.y-f,m=2*(h*d-g*v),y=h*h+g*g,x=v*v+d*d,M={x:(d*y-g*x)/m+l,y:(h*x-v*y)/m+f};$r(e.edge,s,p,M),c.edge=Vr(s,n,null,M),e.edge=Vr(n,p,null,M),Fr(t),Fr(e)}}function Rr(n,t){var e=n.site,r=e.x,u=e.y,i=u-t;if(!i)return r;var o=n.P;if(!o)return-1/0;e=o.site;var a=e.x,c=e.y,s=c-t;if(!s)return a;var l=a-r,f=1/i-1/s,h=l/s;return f?(-h+Math.sqrt(h*h-2*f*(l*l/(-2*s)-c+s/2+u-i/2)))/f+r:(r+a)/2}function Dr(n,t){var e=n.N;if(e)return Rr(e,t);var r=n.site;return r.y===t?r.x:1/0}function Pr(n){this.site=n,this.edges=[]}function Ur(n){for(var t,e,r,u,i,o,a,c,s,l,f=n[0][0],h=n[1][0],g=n[0][1],p=n[1][1],v=Xc,d=v.length;d--;)if(i=v[d],i&&i.prepare())for(a=i.edges,c=a.length,o=0;c>o;)l=a[o].end(),r=l.x,u=l.y,s=a[++o%c].start(),t=s.x,e=s.y,(oa(r-t)>Aa||oa(u-e)>Aa)&&(a.splice(o,0,new Br(Xr(i.site,l,oa(r-f)<Aa&&p-u>Aa?{x:f,y:oa(t-f)<Aa?e:p}:oa(u-p)<Aa&&h-r>Aa?{x:oa(e-p)<Aa?t:h,y:p}:oa(r-h)<Aa&&u-g>Aa?{x:h,y:oa(t-h)<Aa?e:g}:oa(u-g)<Aa&&r-f>Aa?{x:oa(e-g)<Aa?t:f,y:g}:null),i.site,null)),++c)}function jr(n,t){return t.angle-n.angle}function Hr(){Jr(this),this.x=this.y=this.arc=this.site=this.cy=null}function Fr(n){var t=n.P,e=n.N;if(t&&e){var r=t.site,u=n.site,i=e.site;if(r!==i){var o=u.x,a=u.y,c=r.x-o,s=r.y-a,l=i.x-o,f=i.y-a,h=2*(c*f-s*l);if(!(h>=-Ca)){var g=c*c+s*s,p=l*l+f*f,v=(f*g-s*p)/h,d=(c*p-l*g)/h,f=d+a,m=Gc.pop()||new Hr;m.arc=n,m.site=u,m.x=v+o,m.y=f+Math.sqrt(v*v+d*d),m.cy=f,n.circle=m;for(var y=null,x=Wc._;x;)if(m.y<x.y||m.y===x.y&&m.x<=x.x){if(!x.L){y=x.P;break}x=x.L}else{if(!x.R){y=x;break}x=x.R}Wc.insert(y,m),y||(Bc=m)}}}}function Or(n){var t=n.circle;t&&(t.P||(Bc=t.N),Wc.remove(t),Gc.push(t),Jr(t),n.circle=null)}function Yr(n){for(var t,e=Vc,r=De(n[0][0],n[0][1],n[1][0],n[1][1]),u=e.length;u--;)t=e[u],(!Ir(t,n)||!r(t)||oa(t.a.x-t.b.x)<Aa&&oa(t.a.y-t.b.y)<Aa)&&(t.a=t.b=null,e.splice(u,1))}function Ir(n,t){var e=n.b;if(e)return!0;var r,u,i=n.a,o=t[0][0],a=t[1][0],c=t[0][1],s=t[1][1],l=n.l,f=n.r,h=l.x,g=l.y,p=f.x,v=f.y,d=(h+p)/2,m=(g+v)/2;if(v===g){if(o>d||d>=a)return;if(h>p){if(i){if(i.y>=s)return}else i={x:d,y:c};e={x:d,y:s}}else{if(i){if(i.y<c)return}else i={x:d,y:s};e={x:d,y:c}}}else if(r=(h-p)/(v-g),u=m-r*d,-1>r||r>1)if(h>p){if(i){if(i.y>=s)return}else i={x:(c-u)/r,y:c};e={x:(s-u)/r,y:s}}else{if(i){if(i.y<c)return}else i={x:(s-u)/r,y:s};e={x:(c-u)/r,y:c}}else if(v>g){if(i){if(i.x>=a)return}else i={x:o,y:r*o+u};e={x:a,y:r*a+u}}else{if(i){if(i.x<o)return}else i={x:a,y:r*a+u};e={x:o,y:r*o+u}}return n.a=i,n.b=e,!0}function Zr(n,t){this.l=n,this.r=t,this.a=this.b=null}function Vr(n,t,e,r){var u=new Zr(n,t);return Vc.push(u),e&&$r(u,n,t,e),r&&$r(u,t,n,r),Xc[n.i].edges.push(new Br(u,n,t)),Xc[t.i].edges.push(new Br(u,t,n)),u}function Xr(n,t,e){var r=new Zr(n,null);return r.a=t,r.b=e,Vc.push(r),r}function $r(n,t,e,r){n.a||n.b?n.l===e?n.b=r:n.a=r:(n.a=r,n.l=t,n.r=e)}function Br(n,t,e){var r=n.a,u=n.b;this.edge=n,this.site=t,this.angle=e?Math.atan2(e.y-t.y,e.x-t.x):n.l===t?Math.atan2(u.x-r.x,r.y-u.y):Math.atan2(r.x-u.x,u.y-r.y)}function Wr(){this._=null}function Jr(n){n.U=n.C=n.L=n.R=n.P=n.N=null}function Gr(n,t){var e=t,r=t.R,u=e.U;u?u.L===e?u.L=r:u.R=r:n._=r,r.U=u,e.U=r,e.R=r.L,e.R&&(e.R.U=e),r.L=e}function Kr(n,t){var e=t,r=t.L,u=e.U;u?u.L===e?u.L=r:u.R=r:n._=r,r.U=u,e.U=r,e.L=r.R,e.L&&(e.L.U=e),r.R=e}function Qr(n){for(;n.L;)n=n.L;return n}function nu(n,t){var e,r,u,i=n.sort(tu).pop();for(Vc=[],Xc=new Array(n.length),$c=new Wr,Wc=new Wr;;)if(u=Bc,i&&(!u||i.y<u.y||i.y===u.y&&i.x<u.x))(i.x!==e||i.y!==r)&&(Xc[i.i]=new Pr(i),zr(i),e=i.x,r=i.y),i=n.pop();else{if(!u)break;qr(u.arc)}t&&(Yr(t),Ur(t));var o={cells:Xc,edges:Vc};return $c=Wc=Vc=Xc=null,o}function tu(n,t){return t.y-n.y||t.x-n.x}function eu(n,t,e){return(n.x-e.x)*(t.y-n.y)-(n.x-t.x)*(e.y-n.y)}function ru(n){return n.x}function uu(n){return n.y}function iu(){return{leaf:!0,nodes:[],point:null,x:null,y:null}}function ou(n,t,e,r,u,i){if(!n(t,e,r,u,i)){var o=.5*(e+u),a=.5*(r+i),c=t.nodes;c[0]&&ou(n,c[0],e,r,o,a),c[1]&&ou(n,c[1],o,r,u,a),c[2]&&ou(n,c[2],e,a,o,i),c[3]&&ou(n,c[3],o,a,u,i)}}function au(n,t){n=Xo.rgb(n),t=Xo.rgb(t);var e=n.r,r=n.g,u=n.b,i=t.r-e,o=t.g-r,a=t.b-u;return function(n){return"#"+vt(Math.round(e+i*n))+vt(Math.round(r+o*n))+vt(Math.round(u+a*n))}}function cu(n,t){var e,r={},u={};for(e in n)e in t?r[e]=fu(n[e],t[e]):u[e]=n[e];for(e in t)e in n||(u[e]=t[e]);return function(n){for(e in r)u[e]=r[e](n);return u}}function su(n,t){return t-=n=+n,function(e){return n+t*e}}function lu(n,t){var e,r,u,i,o,a=0,c=0,s=[],l=[];for(n+="",t+="",Qc.lastIndex=0,r=0;e=Qc.exec(t);++r)e.index&&s.push(t.substring(a,c=e.index)),l.push({i:s.length,x:e[0]}),s.push(null),a=Qc.lastIndex;for(a<t.length&&s.push(t.substring(a)),r=0,i=l.length;(e=Qc.exec(n))&&i>r;++r)if(o=l[r],o.x==e[0]){if(o.i)if(null==s[o.i+1])for(s[o.i-1]+=o.x,s.splice(o.i,1),u=r+1;i>u;++u)l[u].i--;else for(s[o.i-1]+=o.x+s[o.i+1],s.splice(o.i,2),u=r+1;i>u;++u)l[u].i-=2;else if(null==s[o.i+1])s[o.i]=o.x;else for(s[o.i]=o.x+s[o.i+1],s.splice(o.i+1,1),u=r+1;i>u;++u)l[u].i--;l.splice(r,1),i--,r--}else o.x=su(parseFloat(e[0]),parseFloat(o.x));for(;i>r;)o=l.pop(),null==s[o.i+1]?s[o.i]=o.x:(s[o.i]=o.x+s[o.i+1],s.splice(o.i+1,1)),i--;return 1===s.length?null==s[0]?(o=l[0].x,function(n){return o(n)+""}):function(){return t}:function(n){for(r=0;i>r;++r)s[(o=l[r]).i]=o.x(n);return s.join("")}}function fu(n,t){for(var e,r=Xo.interpolators.length;--r>=0&&!(e=Xo.interpolators[r](n,t)););return e}function hu(n,t){var e,r=[],u=[],i=n.length,o=t.length,a=Math.min(n.length,t.length);for(e=0;a>e;++e)r.push(fu(n[e],t[e]));for(;i>e;++e)u[e]=n[e];for(;o>e;++e)u[e]=t[e];return function(n){for(e=0;a>e;++e)u[e]=r[e](n);return u}}function gu(n){return function(t){return 0>=t?0:t>=1?1:n(t)}}function pu(n){return function(t){return 1-n(1-t)}}function vu(n){return function(t){return.5*(.5>t?n(2*t):2-n(2-2*t))}}function du(n){return n*n}function mu(n){return n*n*n}function yu(n){if(0>=n)return 0;if(n>=1)return 1;var t=n*n,e=t*n;return 4*(.5>n?e:3*(n-t)+e-.75)}function xu(n){return function(t){return Math.pow(t,n)}}function Mu(n){return 1-Math.cos(n*Ea)}function _u(n){return Math.pow(2,10*(n-1))}function bu(n){return 1-Math.sqrt(1-n*n)}function wu(n,t){var e;return arguments.length<2&&(t=.45),arguments.length?e=t/ka*Math.asin(1/n):(n=1,e=t/4),function(r){return 1+n*Math.pow(2,-10*r)*Math.sin((r-e)*ka/t)}}function Su(n){return n||(n=1.70158),function(t){return t*t*((n+1)*t-n)}}function ku(n){return 1/2.75>n?7.5625*n*n:2/2.75>n?7.5625*(n-=1.5/2.75)*n+.75:2.5/2.75>n?7.5625*(n-=2.25/2.75)*n+.9375:7.5625*(n-=2.625/2.75)*n+.984375}function Eu(n,t){n=Xo.hcl(n),t=Xo.hcl(t);var e=n.h,r=n.c,u=n.l,i=t.h-e,o=t.c-r,a=t.l-u;return isNaN(o)&&(o=0,r=isNaN(r)?t.c:r),isNaN(i)?(i=0,e=isNaN(e)?t.h:e):i>180?i-=360:-180>i&&(i+=360),function(n){return rt(e+i*n,r+o*n,u+a*n)+""}}function Au(n,t){n=Xo.hsl(n),t=Xo.hsl(t);var e=n.h,r=n.s,u=n.l,i=t.h-e,o=t.s-r,a=t.l-u;return isNaN(o)&&(o=0,r=isNaN(r)?t.s:r),isNaN(i)?(i=0,e=isNaN(e)?t.h:e):i>180?i-=360:-180>i&&(i+=360),function(n){return nt(e+i*n,r+o*n,u+a*n)+""}}function Cu(n,t){n=Xo.lab(n),t=Xo.lab(t);var e=n.l,r=n.a,u=n.b,i=t.l-e,o=t.a-r,a=t.b-u;return function(n){return ot(e+i*n,r+o*n,u+a*n)+""}}function Nu(n,t){return t-=n,function(e){return Math.round(n+t*e)}}function Lu(n){var t=[n.a,n.b],e=[n.c,n.d],r=qu(t),u=Tu(t,e),i=qu(zu(e,t,-u))||0;t[0]*e[1]<e[0]*t[1]&&(t[0]*=-1,t[1]*=-1,r*=-1,u*=-1),this.rotate=(r?Math.atan2(t[1],t[0]):Math.atan2(-e[0],e[1]))*La,this.translate=[n.e,n.f],this.scale=[r,i],this.skew=i?Math.atan2(u,i)*La:0}function Tu(n,t){return n[0]*t[0]+n[1]*t[1]}function qu(n){var t=Math.sqrt(Tu(n,n));return t&&(n[0]/=t,n[1]/=t),t}function zu(n,t,e){return n[0]+=e*t[0],n[1]+=e*t[1],n}function Ru(n,t){var e,r=[],u=[],i=Xo.transform(n),o=Xo.transform(t),a=i.translate,c=o.translate,s=i.rotate,l=o.rotate,f=i.skew,h=o.skew,g=i.scale,p=o.scale;return a[0]!=c[0]||a[1]!=c[1]?(r.push("translate(",null,",",null,")"),u.push({i:1,x:su(a[0],c[0])},{i:3,x:su(a[1],c[1])})):c[0]||c[1]?r.push("translate("+c+")"):r.push(""),s!=l?(s-l>180?l+=360:l-s>180&&(s+=360),u.push({i:r.push(r.pop()+"rotate(",null,")")-2,x:su(s,l)})):l&&r.push(r.pop()+"rotate("+l+")"),f!=h?u.push({i:r.push(r.pop()+"skewX(",null,")")-2,x:su(f,h)}):h&&r.push(r.pop()+"skewX("+h+")"),g[0]!=p[0]||g[1]!=p[1]?(e=r.push(r.pop()+"scale(",null,",",null,")"),u.push({i:e-4,x:su(g[0],p[0])},{i:e-2,x:su(g[1],p[1])})):(1!=p[0]||1!=p[1])&&r.push(r.pop()+"scale("+p+")"),e=u.length,function(n){for(var t,i=-1;++i<e;)r[(t=u[i]).i]=t.x(n);return r.join("")}}function Du(n,t){return t=t-(n=+n)?1/(t-n):0,function(e){return(e-n)*t}}function Pu(n,t){return t=t-(n=+n)?1/(t-n):0,function(e){return Math.max(0,Math.min(1,(e-n)*t))}}function Uu(n){for(var t=n.source,e=n.target,r=Hu(t,e),u=[t];t!==r;)t=t.parent,u.push(t);for(var i=u.length;e!==r;)u.splice(i,0,e),e=e.parent;return u}function ju(n){for(var t=[],e=n.parent;null!=e;)t.push(n),n=e,e=e.parent;return t.push(n),t}function Hu(n,t){if(n===t)return n;for(var e=ju(n),r=ju(t),u=e.pop(),i=r.pop(),o=null;u===i;)o=u,u=e.pop(),i=r.pop();return o}function Fu(n){n.fixed|=2}function Ou(n){n.fixed&=-7}function Yu(n){n.fixed|=4,n.px=n.x,n.py=n.y}function Iu(n){n.fixed&=-5}function Zu(n,t,e){var r=0,u=0;if(n.charge=0,!n.leaf)for(var i,o=n.nodes,a=o.length,c=-1;++c<a;)i=o[c],null!=i&&(Zu(i,t,e),n.charge+=i.charge,r+=i.charge*i.cx,u+=i.charge*i.cy);if(n.point){n.leaf||(n.point.x+=Math.random()-.5,n.point.y+=Math.random()-.5);var s=t*e[n.point.index];n.charge+=n.pointCharge=s,r+=s*n.point.x,u+=s*n.point.y}n.cx=r/n.charge,n.cy=u/n.charge}function Vu(n,t){return Xo.rebind(n,t,"sort","children","value"),n.nodes=n,n.links=Wu,n}function Xu(n){return n.children}function $u(n){return n.value}function Bu(n,t){return t.value-n.value}function Wu(n){return Xo.merge(n.map(function(n){return(n.children||[]).map(function(t){return{source:n,target:t}})}))}function Ju(n){return n.x}function Gu(n){return n.y}function Ku(n,t,e){n.y0=t,n.y=e}function Qu(n){return Xo.range(n.length)}function ni(n){for(var t=-1,e=n[0].length,r=[];++t<e;)r[t]=0;return r}function ti(n){for(var t,e=1,r=0,u=n[0][1],i=n.length;i>e;++e)(t=n[e][1])>u&&(r=e,u=t);return r}function ei(n){return n.reduce(ri,0)}function ri(n,t){return n+t[1]}function ui(n,t){return ii(n,Math.ceil(Math.log(t.length)/Math.LN2+1))}function ii(n,t){for(var e=-1,r=+n[0],u=(n[1]-r)/t,i=[];++e<=t;)i[e]=u*e+r;return i}function oi(n){return[Xo.min(n),Xo.max(n)]}function ai(n,t){return n.parent==t.parent?1:2}function ci(n){var t=n.children;return t&&t.length?t[0]:n._tree.thread}function si(n){var t,e=n.children;return e&&(t=e.length)?e[t-1]:n._tree.thread}function li(n,t){var e=n.children;if(e&&(u=e.length))for(var r,u,i=-1;++i<u;)t(r=li(e[i],t),n)>0&&(n=r);return n}function fi(n,t){return n.x-t.x}function hi(n,t){return t.x-n.x}function gi(n,t){return n.depth-t.depth}function pi(n,t){function e(n,r){var u=n.children;if(u&&(o=u.length))for(var i,o,a=null,c=-1;++c<o;)i=u[c],e(i,a),a=i;t(n,r)}e(n,null)}function vi(n){for(var t,e=0,r=0,u=n.children,i=u.length;--i>=0;)t=u[i]._tree,t.prelim+=e,t.mod+=e,e+=t.shift+(r+=t.change)}function di(n,t,e){n=n._tree,t=t._tree;var r=e/(t.number-n.number);n.change+=r,t.change-=r,t.shift+=e,t.prelim+=e,t.mod+=e}function mi(n,t,e){return n._tree.ancestor.parent==t.parent?n._tree.ancestor:e}function yi(n,t){return n.value-t.value}function xi(n,t){var e=n._pack_next;n._pack_next=t,t._pack_prev=n,t._pack_next=e,e._pack_prev=t}function Mi(n,t){n._pack_next=t,t._pack_prev=n}function _i(n,t){var e=t.x-n.x,r=t.y-n.y,u=n.r+t.r;return.999*u*u>e*e+r*r}function bi(n){function t(n){l=Math.min(n.x-n.r,l),f=Math.max(n.x+n.r,f),h=Math.min(n.y-n.r,h),g=Math.max(n.y+n.r,g)}if((e=n.children)&&(s=e.length)){var e,r,u,i,o,a,c,s,l=1/0,f=-1/0,h=1/0,g=-1/0;if(e.forEach(wi),r=e[0],r.x=-r.r,r.y=0,t(r),s>1&&(u=e[1],u.x=u.r,u.y=0,t(u),s>2))for(i=e[2],Ei(r,u,i),t(i),xi(r,i),r._pack_prev=i,xi(i,u),u=r._pack_next,o=3;s>o;o++){Ei(r,u,i=e[o]);var p=0,v=1,d=1;for(a=u._pack_next;a!==u;a=a._pack_next,v++)if(_i(a,i)){p=1;break}if(1==p)for(c=r._pack_prev;c!==a._pack_prev&&!_i(c,i);c=c._pack_prev,d++);p?(d>v||v==d&&u.r<r.r?Mi(r,u=a):Mi(r=c,u),o--):(xi(r,i),u=i,t(i))}var m=(l+f)/2,y=(h+g)/2,x=0;for(o=0;s>o;o++)i=e[o],i.x-=m,i.y-=y,x=Math.max(x,i.r+Math.sqrt(i.x*i.x+i.y*i.y));n.r=x,e.forEach(Si)}}function wi(n){n._pack_next=n._pack_prev=n}function Si(n){delete n._pack_next,delete n._pack_prev}function ki(n,t,e,r){var u=n.children;if(n.x=t+=r*n.x,n.y=e+=r*n.y,n.r*=r,u)for(var i=-1,o=u.length;++i<o;)ki(u[i],t,e,r)}function Ei(n,t,e){var r=n.r+e.r,u=t.x-n.x,i=t.y-n.y;if(r&&(u||i)){var o=t.r+e.r,a=u*u+i*i;o*=o,r*=r;var c=.5+(r-o)/(2*a),s=Math.sqrt(Math.max(0,2*o*(r+a)-(r-=a)*r-o*o))/(2*a);e.x=n.x+c*u+s*i,e.y=n.y+c*i-s*u}else e.x=n.x+r,e.y=n.y}function Ai(n){return 1+Xo.max(n,function(n){return n.y})}function Ci(n){return n.reduce(function(n,t){return n+t.x},0)/n.length}function Ni(n){var t=n.children;return t&&t.length?Ni(t[0]):n}function Li(n){var t,e=n.children;return e&&(t=e.length)?Li(e[t-1]):n}function Ti(n){return{x:n.x,y:n.y,dx:n.dx,dy:n.dy}}function qi(n,t){var e=n.x+t[3],r=n.y+t[0],u=n.dx-t[1]-t[3],i=n.dy-t[0]-t[2];return 0>u&&(e+=u/2,u=0),0>i&&(r+=i/2,i=0),{x:e,y:r,dx:u,dy:i}}function zi(n){var t=n[0],e=n[n.length-1];return e>t?[t,e]:[e,t]}function Ri(n){return n.rangeExtent?n.rangeExtent():zi(n.range())}function Di(n,t,e,r){var u=e(n[0],n[1]),i=r(t[0],t[1]);return function(n){return i(u(n))}}function Pi(n,t){var e,r=0,u=n.length-1,i=n[r],o=n[u];return i>o&&(e=r,r=u,u=e,e=i,i=o,o=e),n[r]=t.floor(i),n[u]=t.ceil(o),n}function Ui(n){return n?{floor:function(t){return Math.floor(t/n)*n},ceil:function(t){return Math.ceil(t/n)*n}}:ls}function ji(n,t,e,r){var u=[],i=[],o=0,a=Math.min(n.length,t.length)-1;for(n[a]<n[0]&&(n=n.slice().reverse(),t=t.slice().reverse());++o<=a;)u.push(e(n[o-1],n[o])),i.push(r(t[o-1],t[o]));return function(t){var e=Xo.bisect(n,t,1,a)-1;return i[e](u[e](t))}}function Hi(n,t,e,r){function u(){var u=Math.min(n.length,t.length)>2?ji:Di,c=r?Pu:Du;return o=u(n,t,c,e),a=u(t,n,c,fu),i}function i(n){return o(n)}var o,a;return i.invert=function(n){return a(n)},i.domain=function(t){return arguments.length?(n=t.map(Number),u()):n},i.range=function(n){return arguments.length?(t=n,u()):t},i.rangeRound=function(n){return i.range(n).interpolate(Nu)},i.clamp=function(n){return arguments.length?(r=n,u()):r},i.interpolate=function(n){return arguments.length?(e=n,u()):e},i.ticks=function(t){return Ii(n,t)},i.tickFormat=function(t,e){return Zi(n,t,e)},i.nice=function(t){return Oi(n,t),u()},i.copy=function(){return Hi(n,t,e,r)},u()}function Fi(n,t){return Xo.rebind(n,t,"range","rangeRound","interpolate","clamp")}function Oi(n,t){return Pi(n,Ui(Yi(n,t)[2]))}function Yi(n,t){null==t&&(t=10);var e=zi(n),r=e[1]-e[0],u=Math.pow(10,Math.floor(Math.log(r/t)/Math.LN10)),i=t/r*u;return.15>=i?u*=10:.35>=i?u*=5:.75>=i&&(u*=2),e[0]=Math.ceil(e[0]/u)*u,e[1]=Math.floor(e[1]/u)*u+.5*u,e[2]=u,e}function Ii(n,t){return Xo.range.apply(Xo,Yi(n,t))}function Zi(n,t,e){var r=Yi(n,t);return Xo.format(e?e.replace(Qa,function(n,t,e,u,i,o,a,c,s,l){return[t,e,u,i,o,a,c,s||"."+Xi(l,r),l].join("")}):",."+Vi(r[2])+"f")}function Vi(n){return-Math.floor(Math.log(n)/Math.LN10+.01)}function Xi(n,t){var e=Vi(t[2]);return n in fs?Math.abs(e-Vi(Math.max(Math.abs(t[0]),Math.abs(t[1]))))+ +("e"!==n):e-2*("%"===n)}function $i(n,t,e,r){function u(n){return(e?Math.log(0>n?0:n):-Math.log(n>0?0:-n))/Math.log(t)}function i(n){return e?Math.pow(t,n):-Math.pow(t,-n)}function o(t){return n(u(t))}return o.invert=function(t){return i(n.invert(t))},o.domain=function(t){return arguments.length?(e=t[0]>=0,n.domain((r=t.map(Number)).map(u)),o):r},o.base=function(e){return arguments.length?(t=+e,n.domain(r.map(u)),o):t},o.nice=function(){var t=Pi(r.map(u),e?Math:gs);return n.domain(t),r=t.map(i),o},o.ticks=function(){var n=zi(r),o=[],a=n[0],c=n[1],s=Math.floor(u(a)),l=Math.ceil(u(c)),f=t%1?2:t;if(isFinite(l-s)){if(e){for(;l>s;s++)for(var h=1;f>h;h++)o.push(i(s)*h);o.push(i(s))}else for(o.push(i(s));s++<l;)for(var h=f-1;h>0;h--)o.push(i(s)*h);for(s=0;o[s]<a;s++);for(l=o.length;o[l-1]>c;l--);o=o.slice(s,l)}return o},o.tickFormat=function(n,t){if(!arguments.length)return hs;arguments.length<2?t=hs:"function"!=typeof t&&(t=Xo.format(t));var r,a=Math.max(.1,n/o.ticks().length),c=e?(r=1e-12,Math.ceil):(r=-1e-12,Math.floor);return function(n){return n/i(c(u(n)+r))<=a?t(n):""}},o.copy=function(){return $i(n.copy(),t,e,r)},Fi(o,n)}function Bi(n,t,e){function r(t){return n(u(t))}var u=Wi(t),i=Wi(1/t);return r.invert=function(t){return i(n.invert(t))},r.domain=function(t){return arguments.length?(n.domain((e=t.map(Number)).map(u)),r):e},r.ticks=function(n){return Ii(e,n)},r.tickFormat=function(n,t){return Zi(e,n,t)},r.nice=function(n){return r.domain(Oi(e,n))},r.exponent=function(o){return arguments.length?(u=Wi(t=o),i=Wi(1/t),n.domain(e.map(u)),r):t},r.copy=function(){return Bi(n.copy(),t,e)},Fi(r,n)}function Wi(n){return function(t){return 0>t?-Math.pow(-t,n):Math.pow(t,n)}}function Ji(n,t){function e(e){return o[((i.get(e)||"range"===t.t&&i.set(e,n.push(e)))-1)%o.length]}function r(t,e){return Xo.range(n.length).map(function(n){return t+e*n})}var i,o,a;return e.domain=function(r){if(!arguments.length)return n;n=[],i=new u;for(var o,a=-1,c=r.length;++a<c;)i.has(o=r[a])||i.set(o,n.push(o));return e[t.t].apply(e,t.a)},e.range=function(n){return arguments.length?(o=n,a=0,t={t:"range",a:arguments},e):o},e.rangePoints=function(u,i){arguments.length<2&&(i=0);var c=u[0],s=u[1],l=(s-c)/(Math.max(1,n.length-1)+i);return o=r(n.length<2?(c+s)/2:c+l*i/2,l),a=0,t={t:"rangePoints",a:arguments},e},e.rangeBands=function(u,i,c){arguments.length<2&&(i=0),arguments.length<3&&(c=i);var s=u[1]<u[0],l=u[s-0],f=u[1-s],h=(f-l)/(n.length-i+2*c);return o=r(l+h*c,h),s&&o.reverse(),a=h*(1-i),t={t:"rangeBands",a:arguments},e},e.rangeRoundBands=function(u,i,c){arguments.length<2&&(i=0),arguments.length<3&&(c=i);var s=u[1]<u[0],l=u[s-0],f=u[1-s],h=Math.floor((f-l)/(n.length-i+2*c)),g=f-l-(n.length-i)*h;return o=r(l+Math.round(g/2),h),s&&o.reverse(),a=Math.round(h*(1-i)),t={t:"rangeRoundBands",a:arguments},e},e.rangeBand=function(){return a},e.rangeExtent=function(){return zi(t.a[0])},e.copy=function(){return Ji(n,t)},e.domain(n)}function Gi(n,t){function e(){var e=0,i=t.length;for(u=[];++e<i;)u[e-1]=Xo.quantile(n,e/i);return r}function r(n){return isNaN(n=+n)?void 0:t[Xo.bisect(u,n)]}var u;return r.domain=function(t){return arguments.length?(n=t.filter(function(n){return!isNaN(n)}).sort(Xo.ascending),e()):n},r.range=function(n){return arguments.length?(t=n,e()):t},r.quantiles=function(){return u},r.invertExtent=function(e){return e=t.indexOf(e),0>e?[0/0,0/0]:[e>0?u[e-1]:n[0],e<u.length?u[e]:n[n.length-1]]},r.copy=function(){return Gi(n,t)},e()}function Ki(n,t,e){function r(t){return e[Math.max(0,Math.min(o,Math.floor(i*(t-n))))]}function u(){return i=e.length/(t-n),o=e.length-1,r}var i,o;return r.domain=function(e){return arguments.length?(n=+e[0],t=+e[e.length-1],u()):[n,t]},r.range=function(n){return arguments.length?(e=n,u()):e},r.invertExtent=function(t){return t=e.indexOf(t),t=0>t?0/0:t/i+n,[t,t+1/i]},r.copy=function(){return Ki(n,t,e)},u()}function Qi(n,t){function e(e){return e>=e?t[Xo.bisect(n,e)]:void 0}return e.domain=function(t){return arguments.length?(n=t,e):n},e.range=function(n){return arguments.length?(t=n,e):t},e.invertExtent=function(e){return e=t.indexOf(e),[n[e-1],n[e]]},e.copy=function(){return Qi(n,t)},e}function no(n){function t(n){return+n}return t.invert=t,t.domain=t.range=function(e){return arguments.length?(n=e.map(t),t):n},t.ticks=function(t){return Ii(n,t)},t.tickFormat=function(t,e){return Zi(n,t,e)},t.copy=function(){return no(n)},t}function to(n){return n.innerRadius}function eo(n){return n.outerRadius}function ro(n){return n.startAngle}function uo(n){return n.endAngle}function io(n){function t(t){function o(){s.push("M",i(n(l),a))}for(var c,s=[],l=[],f=-1,h=t.length,g=_t(e),p=_t(r);++f<h;)u.call(this,c=t[f],f)?l.push([+g.call(this,c,f),+p.call(this,c,f)]):l.length&&(o(),l=[]);return l.length&&o(),s.length?s.join(""):null}var e=br,r=wr,u=be,i=oo,o=i.key,a=.7;return t.x=function(n){return arguments.length?(e=n,t):e},t.y=function(n){return arguments.length?(r=n,t):r},t.defined=function(n){return arguments.length?(u=n,t):u},t.interpolate=function(n){return arguments.length?(o="function"==typeof n?i=n:(i=Ms.get(n)||oo).key,t):o},t.tension=function(n){return arguments.length?(a=n,t):a},t}function oo(n){return n.join("L")}function ao(n){return oo(n)+"Z"}function co(n){for(var t=0,e=n.length,r=n[0],u=[r[0],",",r[1]];++t<e;)u.push("H",(r[0]+(r=n[t])[0])/2,"V",r[1]);return e>1&&u.push("H",r[0]),u.join("")}function so(n){for(var t=0,e=n.length,r=n[0],u=[r[0],",",r[1]];++t<e;)u.push("V",(r=n[t])[1],"H",r[0]);return u.join("")}function lo(n){for(var t=0,e=n.length,r=n[0],u=[r[0],",",r[1]];++t<e;)u.push("H",(r=n[t])[0],"V",r[1]);return u.join("")}function fo(n,t){return n.length<4?oo(n):n[1]+po(n.slice(1,n.length-1),vo(n,t))}function ho(n,t){return n.length<3?oo(n):n[0]+po((n.push(n[0]),n),vo([n[n.length-2]].concat(n,[n[1]]),t))}function go(n,t){return n.length<3?oo(n):n[0]+po(n,vo(n,t))}function po(n,t){if(t.length<1||n.length!=t.length&&n.length!=t.length+2)return oo(n);var e=n.length!=t.length,r="",u=n[0],i=n[1],o=t[0],a=o,c=1;if(e&&(r+="Q"+(i[0]-2*o[0]/3)+","+(i[1]-2*o[1]/3)+","+i[0]+","+i[1],u=n[1],c=2),t.length>1){a=t[1],i=n[c],c++,r+="C"+(u[0]+o[0])+","+(u[1]+o[1])+","+(i[0]-a[0])+","+(i[1]-a[1])+","+i[0]+","+i[1];for(var s=2;s<t.length;s++,c++)i=n[c],a=t[s],r+="S"+(i[0]-a[0])+","+(i[1]-a[1])+","+i[0]+","+i[1]}if(e){var l=n[c];r+="Q"+(i[0]+2*a[0]/3)+","+(i[1]+2*a[1]/3)+","+l[0]+","+l[1]}return r}function vo(n,t){for(var e,r=[],u=(1-t)/2,i=n[0],o=n[1],a=1,c=n.length;++a<c;)e=i,i=o,o=n[a],r.push([u*(o[0]-e[0]),u*(o[1]-e[1])]);return r}function mo(n){if(n.length<3)return oo(n);var t=1,e=n.length,r=n[0],u=r[0],i=r[1],o=[u,u,u,(r=n[1])[0]],a=[i,i,i,r[1]],c=[u,",",i,"L",_o(ws,o),",",_o(ws,a)];for(n.push(n[e-1]);++t<=e;)r=n[t],o.shift(),o.push(r[0]),a.shift(),a.push(r[1]),bo(c,o,a);return n.pop(),c.push("L",r),c.join("")}function yo(n){if(n.length<4)return oo(n);for(var t,e=[],r=-1,u=n.length,i=[0],o=[0];++r<3;)t=n[r],i.push(t[0]),o.push(t[1]);for(e.push(_o(ws,i)+","+_o(ws,o)),--r;++r<u;)t=n[r],i.shift(),i.push(t[0]),o.shift(),o.push(t[1]),bo(e,i,o);return e.join("")}function xo(n){for(var t,e,r=-1,u=n.length,i=u+4,o=[],a=[];++r<4;)e=n[r%u],o.push(e[0]),a.push(e[1]);for(t=[_o(ws,o),",",_o(ws,a)],--r;++r<i;)e=n[r%u],o.shift(),o.push(e[0]),a.shift(),a.push(e[1]),bo(t,o,a);return t.join("")}function Mo(n,t){var e=n.length-1;if(e)for(var r,u,i=n[0][0],o=n[0][1],a=n[e][0]-i,c=n[e][1]-o,s=-1;++s<=e;)r=n[s],u=s/e,r[0]=t*r[0]+(1-t)*(i+u*a),r[1]=t*r[1]+(1-t)*(o+u*c);return mo(n)}function _o(n,t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]+n[3]*t[3]}function bo(n,t,e){n.push("C",_o(_s,t),",",_o(_s,e),",",_o(bs,t),",",_o(bs,e),",",_o(ws,t),",",_o(ws,e))}function wo(n,t){return(t[1]-n[1])/(t[0]-n[0])}function So(n){for(var t=0,e=n.length-1,r=[],u=n[0],i=n[1],o=r[0]=wo(u,i);++t<e;)r[t]=(o+(o=wo(u=i,i=n[t+1])))/2;return r[t]=o,r}function ko(n){for(var t,e,r,u,i=[],o=So(n),a=-1,c=n.length-1;++a<c;)t=wo(n[a],n[a+1]),oa(t)<Aa?o[a]=o[a+1]=0:(e=o[a]/t,r=o[a+1]/t,u=e*e+r*r,u>9&&(u=3*t/Math.sqrt(u),o[a]=u*e,o[a+1]=u*r));for(a=-1;++a<=c;)u=(n[Math.min(c,a+1)][0]-n[Math.max(0,a-1)][0])/(6*(1+o[a]*o[a])),i.push([u||0,o[a]*u||0]);return i}function Eo(n){return n.length<3?oo(n):n[0]+po(n,ko(n))}function Ao(n){for(var t,e,r,u=-1,i=n.length;++u<i;)t=n[u],e=t[0],r=t[1]+ys,t[0]=e*Math.cos(r),t[1]=e*Math.sin(r);return n}function Co(n){function t(t){function c(){v.push("M",a(n(m),f),l,s(n(d.reverse()),f),"Z")}for(var h,g,p,v=[],d=[],m=[],y=-1,x=t.length,M=_t(e),_=_t(u),b=e===r?function(){return g}:_t(r),w=u===i?function(){return p}:_t(i);++y<x;)o.call(this,h=t[y],y)?(d.push([g=+M.call(this,h,y),p=+_.call(this,h,y)]),m.push([+b.call(this,h,y),+w.call(this,h,y)])):d.length&&(c(),d=[],m=[]);return d.length&&c(),v.length?v.join(""):null}var e=br,r=br,u=0,i=wr,o=be,a=oo,c=a.key,s=a,l="L",f=.7;return t.x=function(n){return arguments.length?(e=r=n,t):r},t.x0=function(n){return arguments.length?(e=n,t):e},t.x1=function(n){return arguments.length?(r=n,t):r},t.y=function(n){return arguments.length?(u=i=n,t):i},t.y0=function(n){return arguments.length?(u=n,t):u},t.y1=function(n){return arguments.length?(i=n,t):i},t.defined=function(n){return arguments.length?(o=n,t):o},t.interpolate=function(n){return arguments.length?(c="function"==typeof n?a=n:(a=Ms.get(n)||oo).key,s=a.reverse||a,l=a.closed?"M":"L",t):c},t.tension=function(n){return arguments.length?(f=n,t):f},t}function No(n){return n.radius}function Lo(n){return[n.x,n.y]}function To(n){return function(){var t=n.apply(this,arguments),e=t[0],r=t[1]+ys;return[e*Math.cos(r),e*Math.sin(r)]}}function qo(){return 64}function zo(){return"circle"}function Ro(n){var t=Math.sqrt(n/Sa);return"M0,"+t+"A"+t+","+t+" 0 1,1 0,"+-t+"A"+t+","+t+" 0 1,1 0,"+t+"Z"}function Do(n,t){return fa(n,Ns),n.id=t,n}function Po(n,t,e,r){var u=n.id;return R(n,"function"==typeof e?function(n,i,o){n.__transition__[u].tween.set(t,r(e.call(n,n.__data__,i,o)))}:(e=r(e),function(n){n.__transition__[u].tween.set(t,e)}))}function Uo(n){return null==n&&(n=""),function(){this.textContent=n}}function jo(n,t,e,r){var i=n.__transition__||(n.__transition__={active:0,count:0}),o=i[e];if(!o){var a=r.time;o=i[e]={tween:new u,time:a,ease:r.ease,delay:r.delay,duration:r.duration},++i.count,Xo.timer(function(r){function u(r){return i.active>e?s():(i.active=e,o.event&&o.event.start.call(n,l,t),o.tween.forEach(function(e,r){(r=r.call(n,l,t))&&v.push(r)}),Xo.timer(function(){return p.c=c(r||1)?be:c,1},0,a),void 0)}function c(r){if(i.active!==e)return s();for(var u=r/g,a=f(u),c=v.length;c>0;)v[--c].call(n,a);return u>=1?(o.event&&o.event.end.call(n,l,t),s()):void 0}function s(){return--i.count?delete i[e]:delete n.__transition__,1}var l=n.__data__,f=o.ease,h=o.delay,g=o.duration,p=Ja,v=[];return p.t=h+a,r>=h?u(r-h):(p.c=u,void 0)},0,a)}}function Ho(n,t){n.attr("transform",function(n){return"translate("+t(n)+",0)"})}function Fo(n,t){n.attr("transform",function(n){return"translate(0,"+t(n)+")"})}function Oo(n){return n.toISOString()}function Yo(n,t,e){function r(t){return n(t)}function u(n,e){var r=n[1]-n[0],u=r/e,i=Xo.bisect(js,u);return i==js.length?[t.year,Yi(n.map(function(n){return n/31536e6}),e)[2]]:i?t[u/js[i-1]<js[i]/u?i-1:i]:[Os,Yi(n,e)[2]]}return r.invert=function(t){return Io(n.invert(t))},r.domain=function(t){return arguments.length?(n.domain(t),r):n.domain().map(Io)},r.nice=function(n,t){function e(e){return!isNaN(e)&&!n.range(e,Io(+e+1),t).length}var i=r.domain(),o=zi(i),a=null==n?u(o,10):"number"==typeof n&&u(o,n);return a&&(n=a[0],t=a[1]),r.domain(Pi(i,t>1?{floor:function(t){for(;e(t=n.floor(t));)t=Io(t-1);return t},ceil:function(t){for(;e(t=n.ceil(t));)t=Io(+t+1);return t}}:n))},r.ticks=function(n,t){var e=zi(r.domain()),i=null==n?u(e,10):"number"==typeof n?u(e,n):!n.range&&[{range:n},t];return i&&(n=i[0],t=i[1]),n.range(e[0],Io(+e[1]+1),1>t?1:t)},r.tickFormat=function(){return e},r.copy=function(){return Yo(n.copy(),t,e)},Fi(r,n)}function Io(n){return new Date(n)}function Zo(n){return JSON.parse(n.responseText)}function Vo(n){var t=Wo.createRange();return t.selectNode(Wo.body),t.createContextualFragment(n.responseText)}var Xo={version:"3.4.3"};Date.now||(Date.now=function(){return+new Date});var $o=[].slice,Bo=function(n){return $o.call(n)},Wo=document,Jo=Wo.documentElement,Go=window;try{Bo(Jo.childNodes)[0].nodeType}catch(Ko){Bo=function(n){for(var t=n.length,e=new Array(t);t--;)e[t]=n[t];return e}}try{Wo.createElement("div").style.setProperty("opacity",0,"")}catch(Qo){var na=Go.Element.prototype,ta=na.setAttribute,ea=na.setAttributeNS,ra=Go.CSSStyleDeclaration.prototype,ua=ra.setProperty;na.setAttribute=function(n,t){ta.call(this,n,t+"")},na.setAttributeNS=function(n,t,e){ea.call(this,n,t,e+"")},ra.setProperty=function(n,t,e){ua.call(this,n,t+"",e)}}Xo.ascending=function(n,t){return t>n?-1:n>t?1:n>=t?0:0/0},Xo.descending=function(n,t){return n>t?-1:t>n?1:t>=n?0:0/0},Xo.min=function(n,t){var e,r,u=-1,i=n.length;if(1===arguments.length){for(;++u<i&&!(null!=(e=n[u])&&e>=e);)e=void 0;for(;++u<i;)null!=(r=n[u])&&e>r&&(e=r)}else{for(;++u<i&&!(null!=(e=t.call(n,n[u],u))&&e>=e);)e=void 0;for(;++u<i;)null!=(r=t.call(n,n[u],u))&&e>r&&(e=r)}return e},Xo.max=function(n,t){var e,r,u=-1,i=n.length;if(1===arguments.length){for(;++u<i&&!(null!=(e=n[u])&&e>=e);)e=void 0;for(;++u<i;)null!=(r=n[u])&&r>e&&(e=r)}else{for(;++u<i&&!(null!=(e=t.call(n,n[u],u))&&e>=e);)e=void 0;for(;++u<i;)null!=(r=t.call(n,n[u],u))&&r>e&&(e=r)}return e},Xo.extent=function(n,t){var e,r,u,i=-1,o=n.length;if(1===arguments.length){for(;++i<o&&!(null!=(e=u=n[i])&&e>=e);)e=u=void 0;for(;++i<o;)null!=(r=n[i])&&(e>r&&(e=r),r>u&&(u=r))}else{for(;++i<o&&!(null!=(e=u=t.call(n,n[i],i))&&e>=e);)e=void 0;for(;++i<o;)null!=(r=t.call(n,n[i],i))&&(e>r&&(e=r),r>u&&(u=r))}return[e,u]},Xo.sum=function(n,t){var e,r=0,u=n.length,i=-1;if(1===arguments.length)for(;++i<u;)isNaN(e=+n[i])||(r+=e);else for(;++i<u;)isNaN(e=+t.call(n,n[i],i))||(r+=e);return r},Xo.mean=function(t,e){var r,u=t.length,i=0,o=-1,a=0;if(1===arguments.length)for(;++o<u;)n(r=t[o])&&(i+=(r-i)/++a);else for(;++o<u;)n(r=e.call(t,t[o],o))&&(i+=(r-i)/++a);return a?i:void 0},Xo.quantile=function(n,t){var e=(n.length-1)*t+1,r=Math.floor(e),u=+n[r-1],i=e-r;return i?u+i*(n[r]-u):u},Xo.median=function(t,e){return arguments.length>1&&(t=t.map(e)),t=t.filter(n),t.length?Xo.quantile(t.sort(Xo.ascending),.5):void 0},Xo.bisector=function(n){return{left:function(t,e,r,u){for(arguments.length<3&&(r=0),arguments.length<4&&(u=t.length);u>r;){var i=r+u>>>1;n.call(t,t[i],i)<e?r=i+1:u=i}return r},right:function(t,e,r,u){for(arguments.length<3&&(r=0),arguments.length<4&&(u=t.length);u>r;){var i=r+u>>>1;e<n.call(t,t[i],i)?u=i:r=i+1}return r}}};var ia=Xo.bisector(function(n){return n});Xo.bisectLeft=ia.left,Xo.bisect=Xo.bisectRight=ia.right,Xo.shuffle=function(n){for(var t,e,r=n.length;r;)e=0|Math.random()*r--,t=n[r],n[r]=n[e],n[e]=t;return n},Xo.permute=function(n,t){for(var e=t.length,r=new Array(e);e--;)r[e]=n[t[e]];return r},Xo.pairs=function(n){for(var t,e=0,r=n.length-1,u=n[0],i=new Array(0>r?0:r);r>e;)i[e]=[t=u,u=n[++e]];return i},Xo.zip=function(){if(!(u=arguments.length))return[];for(var n=-1,e=Xo.min(arguments,t),r=new Array(e);++n<e;)for(var u,i=-1,o=r[n]=new Array(u);++i<u;)o[i]=arguments[i][n];return r},Xo.transpose=function(n){return Xo.zip.apply(Xo,n)},Xo.keys=function(n){var t=[];for(var e in n)t.push(e);return t},Xo.values=function(n){var t=[];for(var e in n)t.push(n[e]);return t},Xo.entries=function(n){var t=[];for(var e in n)t.push({key:e,value:n[e]});return t},Xo.merge=function(n){for(var t,e,r,u=n.length,i=-1,o=0;++i<u;)o+=n[i].length;for(e=new Array(o);--u>=0;)for(r=n[u],t=r.length;--t>=0;)e[--o]=r[t];return e};var oa=Math.abs;Xo.range=function(n,t,r){if(arguments.length<3&&(r=1,arguments.length<2&&(t=n,n=0)),1/0===(t-n)/r)throw new Error("infinite range");var u,i=[],o=e(oa(r)),a=-1;if(n*=o,t*=o,r*=o,0>r)for(;(u=n+r*++a)>t;)i.push(u/o);else for(;(u=n+r*++a)<t;)i.push(u/o);return i},Xo.map=function(n){var t=new u;if(n instanceof u)n.forEach(function(n,e){t.set(n,e)});else for(var e in n)t.set(e,n[e]);return t},r(u,{has:i,get:function(n){return this[aa+n]},set:function(n,t){return this[aa+n]=t},remove:o,keys:a,values:function(){var n=[];return this.forEach(function(t,e){n.push(e)}),n},entries:function(){var n=[];return this.forEach(function(t,e){n.push({key:t,value:e})}),n},size:c,empty:s,forEach:function(n){for(var t in this)t.charCodeAt(0)===ca&&n.call(this,t.substring(1),this[t])}});var aa="\x00",ca=aa.charCodeAt(0);Xo.nest=function(){function n(t,a,c){if(c>=o.length)return r?r.call(i,a):e?a.sort(e):a;for(var s,l,f,h,g=-1,p=a.length,v=o[c++],d=new u;++g<p;)(h=d.get(s=v(l=a[g])))?h.push(l):d.set(s,[l]);return t?(l=t(),f=function(e,r){l.set(e,n(t,r,c))}):(l={},f=function(e,r){l[e]=n(t,r,c)}),d.forEach(f),l}function t(n,e){if(e>=o.length)return n;var r=[],u=a[e++];return n.forEach(function(n,u){r.push({key:n,values:t(u,e)})}),u?r.sort(function(n,t){return u(n.key,t.key)}):r}var e,r,i={},o=[],a=[];return i.map=function(t,e){return n(e,t,0)},i.entries=function(e){return t(n(Xo.map,e,0),0)},i.key=function(n){return o.push(n),i},i.sortKeys=function(n){return a[o.length-1]=n,i},i.sortValues=function(n){return e=n,i},i.rollup=function(n){return r=n,i},i},Xo.set=function(n){var t=new l;if(n)for(var e=0,r=n.length;r>e;++e)t.add(n[e]);return t},r(l,{has:i,add:function(n){return this[aa+n]=!0,n},remove:function(n){return n=aa+n,n in this&&delete this[n]},values:a,size:c,empty:s,forEach:function(n){for(var t in this)t.charCodeAt(0)===ca&&n.call(this,t.substring(1))}}),Xo.behavior={},Xo.rebind=function(n,t){for(var e,r=1,u=arguments.length;++r<u;)n[e=arguments[r]]=f(n,t,t[e]);return n};var sa=["webkit","ms","moz","Moz","o","O"];Xo.dispatch=function(){for(var n=new p,t=-1,e=arguments.length;++t<e;)n[arguments[t]]=v(n);return n},p.prototype.on=function(n,t){var e=n.indexOf("."),r="";if(e>=0&&(r=n.substring(e+1),n=n.substring(0,e)),n)return arguments.length<2?this[n].on(r):this[n].on(r,t);if(2===arguments.length){if(null==t)for(n in this)this.hasOwnProperty(n)&&this[n].on(r,null);return this}},Xo.event=null,Xo.requote=function(n){return n.replace(la,"\\$&")};var la=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g,fa={}.__proto__?function(n,t){n.__proto__=t}:function(n,t){for(var e in t)n[e]=t[e]},ha=function(n,t){return t.querySelector(n)},ga=function(n,t){return t.querySelectorAll(n)},pa=Jo[h(Jo,"matchesSelector")],va=function(n,t){return pa.call(n,t)};"function"==typeof Sizzle&&(ha=function(n,t){return Sizzle(n,t)[0]||null},ga=Sizzle,va=Sizzle.matchesSelector),Xo.selection=function(){return xa};var da=Xo.selection.prototype=[];da.select=function(n){var t,e,r,u,i=[];n=M(n);for(var o=-1,a=this.length;++o<a;){i.push(t=[]),t.parentNode=(r=this[o]).parentNode;for(var c=-1,s=r.length;++c<s;)(u=r[c])?(t.push(e=n.call(u,u.__data__,c,o)),e&&"__data__"in u&&(e.__data__=u.__data__)):t.push(null)}return x(i)},da.selectAll=function(n){var t,e,r=[];n=_(n);for(var u=-1,i=this.length;++u<i;)for(var o=this[u],a=-1,c=o.length;++a<c;)(e=o[a])&&(r.push(t=Bo(n.call(e,e.__data__,a,u))),t.parentNode=e);return x(r)};var ma={svg:"http://www.w3.org/2000/svg",xhtml:"http://www.w3.org/1999/xhtml",xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"};Xo.ns={prefix:ma,qualify:function(n){var t=n.indexOf(":"),e=n;return t>=0&&(e=n.substring(0,t),n=n.substring(t+1)),ma.hasOwnProperty(e)?{space:ma[e],local:n}:n}},da.attr=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node();return n=Xo.ns.qualify(n),n.local?e.getAttributeNS(n.space,n.local):e.getAttribute(n)}for(t in n)this.each(b(t,n[t]));return this}return this.each(b(n,t))},da.classed=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node(),r=(n=k(n)).length,u=-1;if(t=e.classList){for(;++u<r;)if(!t.contains(n[u]))return!1}else for(t=e.getAttribute("class");++u<r;)if(!S(n[u]).test(t))return!1;return!0}for(t in n)this.each(E(t,n[t]));return this}return this.each(E(n,t))},da.style=function(n,t,e){var r=arguments.length;if(3>r){if("string"!=typeof n){2>r&&(t="");for(e in n)this.each(C(e,n[e],t));return this}if(2>r)return Go.getComputedStyle(this.node(),null).getPropertyValue(n);e=""}return this.each(C(n,t,e))},da.property=function(n,t){if(arguments.length<2){if("string"==typeof n)return this.node()[n];for(t in n)this.each(N(t,n[t]));return this}return this.each(N(n,t))},da.text=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.textContent=null==t?"":t}:null==n?function(){this.textContent=""}:function(){this.textContent=n}):this.node().textContent},da.html=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.innerHTML=null==t?"":t}:null==n?function(){this.innerHTML=""}:function(){this.innerHTML=n}):this.node().innerHTML},da.append=function(n){return n=L(n),this.select(function(){return this.appendChild(n.apply(this,arguments))})},da.insert=function(n,t){return n=L(n),t=M(t),this.select(function(){return this.insertBefore(n.apply(this,arguments),t.apply(this,arguments)||null)})},da.remove=function(){return this.each(function(){var n=this.parentNode;n&&n.removeChild(this)})},da.data=function(n,t){function e(n,e){var r,i,o,a=n.length,f=e.length,h=Math.min(a,f),g=new Array(f),p=new Array(f),v=new Array(a);if(t){var d,m=new u,y=new u,x=[];for(r=-1;++r<a;)d=t.call(i=n[r],i.__data__,r),m.has(d)?v[r]=i:m.set(d,i),x.push(d);for(r=-1;++r<f;)d=t.call(e,o=e[r],r),(i=m.get(d))?(g[r]=i,i.__data__=o):y.has(d)||(p[r]=T(o)),y.set(d,o),m.remove(d);for(r=-1;++r<a;)m.has(x[r])&&(v[r]=n[r])}else{for(r=-1;++r<h;)i=n[r],o=e[r],i?(i.__data__=o,g[r]=i):p[r]=T(o);for(;f>r;++r)p[r]=T(e[r]);for(;a>r;++r)v[r]=n[r]}p.update=g,p.parentNode=g.parentNode=v.parentNode=n.parentNode,c.push(p),s.push(g),l.push(v)}var r,i,o=-1,a=this.length;if(!arguments.length){for(n=new Array(a=(r=this[0]).length);++o<a;)(i=r[o])&&(n[o]=i.__data__);return n}var c=D([]),s=x([]),l=x([]);if("function"==typeof n)for(;++o<a;)e(r=this[o],n.call(r,r.parentNode.__data__,o));else for(;++o<a;)e(r=this[o],n);return s.enter=function(){return c},s.exit=function(){return l},s},da.datum=function(n){return arguments.length?this.property("__data__",n):this.property("__data__")},da.filter=function(n){var t,e,r,u=[];"function"!=typeof n&&(n=q(n));for(var i=0,o=this.length;o>i;i++){u.push(t=[]),t.parentNode=(e=this[i]).parentNode;for(var a=0,c=e.length;c>a;a++)(r=e[a])&&n.call(r,r.__data__,a,i)&&t.push(r)}return x(u)},da.order=function(){for(var n=-1,t=this.length;++n<t;)for(var e,r=this[n],u=r.length-1,i=r[u];--u>=0;)(e=r[u])&&(i&&i!==e.nextSibling&&i.parentNode.insertBefore(e,i),i=e);return this},da.sort=function(n){n=z.apply(this,arguments);for(var t=-1,e=this.length;++t<e;)this[t].sort(n);return this.order()},da.each=function(n){return R(this,function(t,e,r){n.call(t,t.__data__,e,r)})},da.call=function(n){var t=Bo(arguments);return n.apply(t[0]=this,t),this},da.empty=function(){return!this.node()},da.node=function(){for(var n=0,t=this.length;t>n;n++)for(var e=this[n],r=0,u=e.length;u>r;r++){var i=e[r];if(i)return i}return null},da.size=function(){var n=0;return this.each(function(){++n}),n};var ya=[];Xo.selection.enter=D,Xo.selection.enter.prototype=ya,ya.append=da.append,ya.empty=da.empty,ya.node=da.node,ya.call=da.call,ya.size=da.size,ya.select=function(n){for(var t,e,r,u,i,o=[],a=-1,c=this.length;++a<c;){r=(u=this[a]).update,o.push(t=[]),t.parentNode=u.parentNode;for(var s=-1,l=u.length;++s<l;)(i=u[s])?(t.push(r[s]=e=n.call(u.parentNode,i.__data__,s,a)),e.__data__=i.__data__):t.push(null)}return x(o)},ya.insert=function(n,t){return arguments.length<2&&(t=P(this)),da.insert.call(this,n,t)},da.transition=function(){for(var n,t,e=ks||++Ls,r=[],u=Es||{time:Date.now(),ease:yu,delay:0,duration:250},i=-1,o=this.length;++i<o;){r.push(n=[]);for(var a=this[i],c=-1,s=a.length;++c<s;)(t=a[c])&&jo(t,c,e,u),n.push(t)}return Do(r,e)},da.interrupt=function(){return this.each(U)},Xo.select=function(n){var t=["string"==typeof n?ha(n,Wo):n];return t.parentNode=Jo,x([t])},Xo.selectAll=function(n){var t=Bo("string"==typeof n?ga(n,Wo):n);return t.parentNode=Jo,x([t])};var xa=Xo.select(Jo);da.on=function(n,t,e){var r=arguments.length;if(3>r){if("string"!=typeof n){2>r&&(t=!1);for(e in n)this.each(j(e,n[e],t));return this}if(2>r)return(r=this.node()["__on"+n])&&r._;e=!1}return this.each(j(n,t,e))};var Ma=Xo.map({mouseenter:"mouseover",mouseleave:"mouseout"});Ma.forEach(function(n){"on"+n in Wo&&Ma.remove(n)});var _a="onselectstart"in Wo?null:h(Jo.style,"userSelect"),ba=0;Xo.mouse=function(n){return Y(n,m())};var wa=/WebKit/.test(Go.navigator.userAgent)?-1:0;Xo.touches=function(n,t){return arguments.length<2&&(t=m().touches),t?Bo(t).map(function(t){var e=Y(n,t);return e.identifier=t.identifier,e}):[]},Xo.behavior.drag=function(){function n(){this.on("mousedown.drag",o).on("touchstart.drag",a)}function t(){return Xo.event.changedTouches[0].identifier}function e(n,t){return Xo.touches(n).filter(function(n){return n.identifier===t})[0]}function r(n,t,e,r){return function(){function o(){var n=t(l,g),e=n[0]-v[0],r=n[1]-v[1];d|=e|r,v=n,f({type:"drag",x:n[0]+c[0],y:n[1]+c[1],dx:e,dy:r})}function a(){m.on(e+"."+p,null).on(r+"."+p,null),y(d&&Xo.event.target===h),f({type:"dragend"})}var c,s=this,l=s.parentNode,f=u.of(s,arguments),h=Xo.event.target,g=n(),p=null==g?"drag":"drag-"+g,v=t(l,g),d=0,m=Xo.select(Go).on(e+"."+p,o).on(r+"."+p,a),y=O();i?(c=i.apply(s,arguments),c=[c.x-v[0],c.y-v[1]]):c=[0,0],f({type:"dragstart"})}}var u=y(n,"drag","dragstart","dragend"),i=null,o=r(g,Xo.mouse,"mousemove","mouseup"),a=r(t,e,"touchmove","touchend");return n.origin=function(t){return arguments.length?(i=t,n):i},Xo.rebind(n,u,"on")};var Sa=Math.PI,ka=2*Sa,Ea=Sa/2,Aa=1e-6,Ca=Aa*Aa,Na=Sa/180,La=180/Sa,Ta=Math.SQRT2,qa=2,za=4;Xo.interpolateZoom=function(n,t){function e(n){var t=n*y;if(m){var e=B(v),o=i/(qa*h)*(e*W(Ta*t+v)-$(v));return[r+o*s,u+o*l,i*e/B(Ta*t+v)]}return[r+n*s,u+n*l,i*Math.exp(Ta*t)]}var r=n[0],u=n[1],i=n[2],o=t[0],a=t[1],c=t[2],s=o-r,l=a-u,f=s*s+l*l,h=Math.sqrt(f),g=(c*c-i*i+za*f)/(2*i*qa*h),p=(c*c-i*i-za*f)/(2*c*qa*h),v=Math.log(Math.sqrt(g*g+1)-g),d=Math.log(Math.sqrt(p*p+1)-p),m=d-v,y=(m||Math.log(c/i))/Ta;return e.duration=1e3*y,e},Xo.behavior.zoom=function(){function n(n){n.on(A,s).on(Pa+".zoom",f).on(C,h).on("dblclick.zoom",g).on(L,l)}function t(n){return[(n[0]-S.x)/S.k,(n[1]-S.y)/S.k]}function e(n){return[n[0]*S.k+S.x,n[1]*S.k+S.y]}function r(n){S.k=Math.max(E[0],Math.min(E[1],n))}function u(n,t){t=e(t),S.x+=n[0]-t[0],S.y+=n[1]-t[1]}function i(){_&&_.domain(M.range().map(function(n){return(n-S.x)/S.k}).map(M.invert)),w&&w.domain(b.range().map(function(n){return(n-S.y)/S.k}).map(b.invert))}function o(n){n({type:"zoomstart"})}function a(n){i(),n({type:"zoom",scale:S.k,translate:[S.x,S.y]})}function c(n){n({type:"zoomend"})}function s(){function n(){l=1,u(Xo.mouse(r),g),a(i)}function e(){f.on(C,Go===r?h:null).on(N,null),p(l&&Xo.event.target===s),c(i)}var r=this,i=T.of(r,arguments),s=Xo.event.target,l=0,f=Xo.select(Go).on(C,n).on(N,e),g=t(Xo.mouse(r)),p=O();U.call(r),o(i)}function l(){function n(){var n=Xo.touches(g);return h=S.k,n.forEach(function(n){n.identifier in v&&(v[n.identifier]=t(n))}),n}function e(){for(var t=Xo.event.changedTouches,e=0,i=t.length;i>e;++e)v[t[e].identifier]=null;var o=n(),c=Date.now();if(1===o.length){if(500>c-x){var s=o[0],l=v[s.identifier];r(2*S.k),u(s,l),d(),a(p)}x=c}else if(o.length>1){var s=o[0],f=o[1],h=s[0]-f[0],g=s[1]-f[1];m=h*h+g*g}}function i(){for(var n,t,e,i,o=Xo.touches(g),c=0,s=o.length;s>c;++c,i=null)if(e=o[c],i=v[e.identifier]){if(t)break;n=e,t=i}if(i){var l=(l=e[0]-n[0])*l+(l=e[1]-n[1])*l,f=m&&Math.sqrt(l/m);n=[(n[0]+e[0])/2,(n[1]+e[1])/2],t=[(t[0]+i[0])/2,(t[1]+i[1])/2],r(f*h)}x=null,u(n,t),a(p)}function f(){if(Xo.event.touches.length){for(var t=Xo.event.changedTouches,e=0,r=t.length;r>e;++e)delete v[t[e].identifier];for(var u in v)return void n()}b.on(M,null).on(_,null),w.on(A,s).on(L,l),k(),c(p)}var h,g=this,p=T.of(g,arguments),v={},m=0,y=Xo.event.changedTouches[0].identifier,M="touchmove.zoom-"+y,_="touchend.zoom-"+y,b=Xo.select(Go).on(M,i).on(_,f),w=Xo.select(g).on(A,null).on(L,e),k=O();U.call(g),e(),o(p)}function f(){var n=T.of(this,arguments);m?clearTimeout(m):(U.call(this),o(n)),m=setTimeout(function(){m=null,c(n)},50),d();var e=v||Xo.mouse(this);p||(p=t(e)),r(Math.pow(2,.002*Ra())*S.k),u(e,p),a(n)}function h(){p=null}function g(){var n=T.of(this,arguments),e=Xo.mouse(this),i=t(e),s=Math.log(S.k)/Math.LN2;o(n),r(Math.pow(2,Xo.event.shiftKey?Math.ceil(s)-1:Math.floor(s)+1)),u(e,i),a(n),c(n)}var p,v,m,x,M,_,b,w,S={x:0,y:0,k:1},k=[960,500],E=Da,A="mousedown.zoom",C="mousemove.zoom",N="mouseup.zoom",L="touchstart.zoom",T=y(n,"zoomstart","zoom","zoomend");return n.event=function(n){n.each(function(){var n=T.of(this,arguments),t=S;ks?Xo.select(this).transition().each("start.zoom",function(){S=this.__chart__||{x:0,y:0,k:1},o(n)}).tween("zoom:zoom",function(){var e=k[0],r=k[1],u=e/2,i=r/2,o=Xo.interpolateZoom([(u-S.x)/S.k,(i-S.y)/S.k,e/S.k],[(u-t.x)/t.k,(i-t.y)/t.k,e/t.k]);return function(t){var r=o(t),c=e/r[2];this.__chart__=S={x:u-r[0]*c,y:i-r[1]*c,k:c},a(n)}}).each("end.zoom",function(){c(n)}):(this.__chart__=S,o(n),a(n),c(n))})},n.translate=function(t){return arguments.length?(S={x:+t[0],y:+t[1],k:S.k},i(),n):[S.x,S.y]},n.scale=function(t){return arguments.length?(S={x:S.x,y:S.y,k:+t},i(),n):S.k},n.scaleExtent=function(t){return arguments.length?(E=null==t?Da:[+t[0],+t[1]],n):E},n.center=function(t){return arguments.length?(v=t&&[+t[0],+t[1]],n):v},n.size=function(t){return arguments.length?(k=t&&[+t[0],+t[1]],n):k},n.x=function(t){return arguments.length?(_=t,M=t.copy(),S={x:0,y:0,k:1},n):_},n.y=function(t){return arguments.length?(w=t,b=t.copy(),S={x:0,y:0,k:1},n):w},Xo.rebind(n,T,"on")};var Ra,Da=[0,1/0],Pa="onwheel"in Wo?(Ra=function(){return-Xo.event.deltaY*(Xo.event.deltaMode?120:1)},"wheel"):"onmousewheel"in Wo?(Ra=function(){return Xo.event.wheelDelta},"mousewheel"):(Ra=function(){return-Xo.event.detail},"MozMousePixelScroll");G.prototype.toString=function(){return this.rgb()+""},Xo.hsl=function(n,t,e){return 1===arguments.length?n instanceof Q?K(n.h,n.s,n.l):dt(""+n,mt,K):K(+n,+t,+e)};var Ua=Q.prototype=new G;Ua.brighter=function(n){return n=Math.pow(.7,arguments.length?n:1),K(this.h,this.s,this.l/n)},Ua.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),K(this.h,this.s,n*this.l)},Ua.rgb=function(){return nt(this.h,this.s,this.l)},Xo.hcl=function(n,t,e){return 1===arguments.length?n instanceof et?tt(n.h,n.c,n.l):n instanceof it?at(n.l,n.a,n.b):at((n=yt((n=Xo.rgb(n)).r,n.g,n.b)).l,n.a,n.b):tt(+n,+t,+e)};var ja=et.prototype=new G;ja.brighter=function(n){return tt(this.h,this.c,Math.min(100,this.l+Ha*(arguments.length?n:1)))},ja.darker=function(n){return tt(this.h,this.c,Math.max(0,this.l-Ha*(arguments.length?n:1)))},ja.rgb=function(){return rt(this.h,this.c,this.l).rgb()},Xo.lab=function(n,t,e){return 1===arguments.length?n instanceof it?ut(n.l,n.a,n.b):n instanceof et?rt(n.l,n.c,n.h):yt((n=Xo.rgb(n)).r,n.g,n.b):ut(+n,+t,+e)};var Ha=18,Fa=.95047,Oa=1,Ya=1.08883,Ia=it.prototype=new G;Ia.brighter=function(n){return ut(Math.min(100,this.l+Ha*(arguments.length?n:1)),this.a,this.b)},Ia.darker=function(n){return ut(Math.max(0,this.l-Ha*(arguments.length?n:1)),this.a,this.b)},Ia.rgb=function(){return ot(this.l,this.a,this.b)},Xo.rgb=function(n,t,e){return 1===arguments.length?n instanceof pt?gt(n.r,n.g,n.b):dt(""+n,gt,nt):gt(~~n,~~t,~~e)};var Za=pt.prototype=new G;Za.brighter=function(n){n=Math.pow(.7,arguments.length?n:1);var t=this.r,e=this.g,r=this.b,u=30;return t||e||r?(t&&u>t&&(t=u),e&&u>e&&(e=u),r&&u>r&&(r=u),gt(Math.min(255,~~(t/n)),Math.min(255,~~(e/n)),Math.min(255,~~(r/n)))):gt(u,u,u)},Za.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),gt(~~(n*this.r),~~(n*this.g),~~(n*this.b))},Za.hsl=function(){return mt(this.r,this.g,this.b)},Za.toString=function(){return"#"+vt(this.r)+vt(this.g)+vt(this.b)};var Va=Xo.map({aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074});Va.forEach(function(n,t){Va.set(n,ft(t))}),Xo.functor=_t,Xo.xhr=wt(bt),Xo.dsv=function(n,t){function e(n,e,i){arguments.length<3&&(i=e,e=null);var o=St(n,t,null==e?r:u(e),i);return o.row=function(n){return arguments.length?o.response(null==(e=n)?r:u(n)):e},o}function r(n){return e.parse(n.responseText)}function u(n){return function(t){return e.parse(t.responseText,n)}}function i(t){return t.map(o).join(n)}function o(n){return a.test(n)?'"'+n.replace(/\"/g,'""')+'"':n}var a=new RegExp('["'+n+"\n]"),c=n.charCodeAt(0);return e.parse=function(n,t){var r;return e.parseRows(n,function(n,e){if(r)return r(n,e-1);var u=new Function("d","return {"+n.map(function(n,t){return JSON.stringify(n)+": d["+t+"]"}).join(",")+"}");r=t?function(n,e){return t(u(n),e)}:u})},e.parseRows=function(n,t){function e(){if(l>=s)return o;if(u)return u=!1,i;var t=l;if(34===n.charCodeAt(t)){for(var e=t;e++<s;)if(34===n.charCodeAt(e)){if(34!==n.charCodeAt(e+1))break;++e}l=e+2;var r=n.charCodeAt(e+1);return 13===r?(u=!0,10===n.charCodeAt(e+2)&&++l):10===r&&(u=!0),n.substring(t+1,e).replace(/""/g,'"')}for(;s>l;){var r=n.charCodeAt(l++),a=1;if(10===r)u=!0;else if(13===r)u=!0,10===n.charCodeAt(l)&&(++l,++a);else if(r!==c)continue;return n.substring(t,l-a)}return n.substring(t)}for(var r,u,i={},o={},a=[],s=n.length,l=0,f=0;(r=e())!==o;){for(var h=[];r!==i&&r!==o;)h.push(r),r=e();(!t||(h=t(h,f++)))&&a.push(h)}return a},e.format=function(t){if(Array.isArray(t[0]))return e.formatRows(t);var r=new l,u=[];return t.forEach(function(n){for(var t in n)r.has(t)||u.push(r.add(t))}),[u.map(o).join(n)].concat(t.map(function(t){return u.map(function(n){return o(t[n])}).join(n)})).join("\n")},e.formatRows=function(n){return n.map(i).join("\n")},e},Xo.csv=Xo.dsv(",","text/csv"),Xo.tsv=Xo.dsv("	","text/tab-separated-values");var Xa,$a,Ba,Wa,Ja,Ga=Go[h(Go,"requestAnimationFrame")]||function(n){setTimeout(n,17)};Xo.timer=function(n,t,e){var r=arguments.length;2>r&&(t=0),3>r&&(e=Date.now());var u=e+t,i={c:n,t:u,f:!1,n:null};$a?$a.n=i:Xa=i,$a=i,Ba||(Wa=clearTimeout(Wa),Ba=1,Ga(Et))},Xo.timer.flush=function(){At(),Ct()},Xo.round=function(n,t){return t?Math.round(n*(t=Math.pow(10,t)))/t:Math.round(n)};var Ka=["y","z","a","f","p","n","\xb5","m","","k","M","G","T","P","E","Z","Y"].map(Lt);Xo.formatPrefix=function(n,t){var e=0;return n&&(0>n&&(n*=-1),t&&(n=Xo.round(n,Nt(n,t))),e=1+Math.floor(1e-12+Math.log(n)/Math.LN10),e=Math.max(-24,Math.min(24,3*Math.floor((0>=e?e+1:e-1)/3)))),Ka[8+e/3]};var Qa=/(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i,nc=Xo.map({b:function(n){return n.toString(2)},c:function(n){return String.fromCharCode(n)},o:function(n){return n.toString(8)},x:function(n){return n.toString(16)},X:function(n){return n.toString(16).toUpperCase()},g:function(n,t){return n.toPrecision(t)},e:function(n,t){return n.toExponential(t)},f:function(n,t){return n.toFixed(t)},r:function(n,t){return(n=Xo.round(n,Nt(n,t))).toFixed(Math.max(0,Math.min(20,Nt(n*(1+1e-15),t))))}}),tc=Xo.time={},ec=Date;zt.prototype={getDate:function(){return this._.getUTCDate()},getDay:function(){return this._.getUTCDay()},getFullYear:function(){return this._.getUTCFullYear()},getHours:function(){return this._.getUTCHours()},getMilliseconds:function(){return this._.getUTCMilliseconds()},getMinutes:function(){return this._.getUTCMinutes()},getMonth:function(){return this._.getUTCMonth()},getSeconds:function(){return this._.getUTCSeconds()},getTime:function(){return this._.getTime()},getTimezoneOffset:function(){return 0},valueOf:function(){return this._.valueOf()},setDate:function(){rc.setUTCDate.apply(this._,arguments)},setDay:function(){rc.setUTCDay.apply(this._,arguments)},setFullYear:function(){rc.setUTCFullYear.apply(this._,arguments)},setHours:function(){rc.setUTCHours.apply(this._,arguments)},setMilliseconds:function(){rc.setUTCMilliseconds.apply(this._,arguments)},setMinutes:function(){rc.setUTCMinutes.apply(this._,arguments)},setMonth:function(){rc.setUTCMonth.apply(this._,arguments)},setSeconds:function(){rc.setUTCSeconds.apply(this._,arguments)},setTime:function(){rc.setTime.apply(this._,arguments)}};var rc=Date.prototype;tc.year=Rt(function(n){return n=tc.day(n),n.setMonth(0,1),n},function(n,t){n.setFullYear(n.getFullYear()+t)},function(n){return n.getFullYear()}),tc.years=tc.year.range,tc.years.utc=tc.year.utc.range,tc.day=Rt(function(n){var t=new ec(2e3,0);return t.setFullYear(n.getFullYear(),n.getMonth(),n.getDate()),t},function(n,t){n.setDate(n.getDate()+t)},function(n){return n.getDate()-1}),tc.days=tc.day.range,tc.days.utc=tc.day.utc.range,tc.dayOfYear=function(n){var t=tc.year(n);return Math.floor((n-t-6e4*(n.getTimezoneOffset()-t.getTimezoneOffset()))/864e5)},["sunday","monday","tuesday","wednesday","thursday","friday","saturday"].forEach(function(n,t){t=7-t;var e=tc[n]=Rt(function(n){return(n=tc.day(n)).setDate(n.getDate()-(n.getDay()+t)%7),n},function(n,t){n.setDate(n.getDate()+7*Math.floor(t))},function(n){var e=tc.year(n).getDay();return Math.floor((tc.dayOfYear(n)+(e+t)%7)/7)-(e!==t)});tc[n+"s"]=e.range,tc[n+"s"].utc=e.utc.range,tc[n+"OfYear"]=function(n){var e=tc.year(n).getDay();return Math.floor((tc.dayOfYear(n)+(e+t)%7)/7)}}),tc.week=tc.sunday,tc.weeks=tc.sunday.range,tc.weeks.utc=tc.sunday.utc.range,tc.weekOfYear=tc.sundayOfYear;var uc={"-":"",_:" ",0:"0"},ic=/^\s*\d+/,oc=/^%/;Xo.locale=function(n){return{numberFormat:Tt(n),timeFormat:Pt(n)}};var ac=Xo.locale({decimal:".",thousands:",",grouping:[3],currency:["$",""],dateTime:"%a %b %e %X %Y",date:"%m/%d/%Y",time:"%H:%M:%S",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});Xo.format=ac.numberFormat,Xo.geo={},re.prototype={s:0,t:0,add:function(n){ue(n,this.t,cc),ue(cc.s,this.s,this),this.s?this.t+=cc.t:this.s=cc.t},reset:function(){this.s=this.t=0},valueOf:function(){return this.s}};var cc=new re;Xo.geo.stream=function(n,t){n&&sc.hasOwnProperty(n.type)?sc[n.type](n,t):ie(n,t)};var sc={Feature:function(n,t){ie(n.geometry,t)},FeatureCollection:function(n,t){for(var e=n.features,r=-1,u=e.length;++r<u;)ie(e[r].geometry,t)}},lc={Sphere:function(n,t){t.sphere()},Point:function(n,t){n=n.coordinates,t.point(n[0],n[1],n[2])},MultiPoint:function(n,t){for(var e=n.coordinates,r=-1,u=e.length;++r<u;)n=e[r],t.point(n[0],n[1],n[2])},LineString:function(n,t){oe(n.coordinates,t,0)},MultiLineString:function(n,t){for(var e=n.coordinates,r=-1,u=e.length;++r<u;)oe(e[r],t,0)},Polygon:function(n,t){ae(n.coordinates,t)},MultiPolygon:function(n,t){for(var e=n.coordinates,r=-1,u=e.length;++r<u;)ae(e[r],t)},GeometryCollection:function(n,t){for(var e=n.geometries,r=-1,u=e.length;++r<u;)ie(e[r],t)}};Xo.geo.area=function(n){return fc=0,Xo.geo.stream(n,gc),fc};var fc,hc=new re,gc={sphere:function(){fc+=4*Sa},point:g,lineStart:g,lineEnd:g,polygonStart:function(){hc.reset(),gc.lineStart=ce},polygonEnd:function(){var n=2*hc;fc+=0>n?4*Sa+n:n,gc.lineStart=gc.lineEnd=gc.point=g}};Xo.geo.bounds=function(){function n(n,t){x.push(M=[l=n,h=n]),f>t&&(f=t),t>g&&(g=t)}function t(t,e){var r=se([t*Na,e*Na]);if(m){var u=fe(m,r),i=[u[1],-u[0],0],o=fe(i,u);pe(o),o=ve(o);var c=t-p,s=c>0?1:-1,v=o[0]*La*s,d=oa(c)>180;if(d^(v>s*p&&s*t>v)){var y=o[1]*La;y>g&&(g=y)}else if(v=(v+360)%360-180,d^(v>s*p&&s*t>v)){var y=-o[1]*La;f>y&&(f=y)}else f>e&&(f=e),e>g&&(g=e);d?p>t?a(l,t)>a(l,h)&&(h=t):a(t,h)>a(l,h)&&(l=t):h>=l?(l>t&&(l=t),t>h&&(h=t)):t>p?a(l,t)>a(l,h)&&(h=t):a(t,h)>a(l,h)&&(l=t)}else n(t,e);m=r,p=t}function e(){_.point=t}function r(){M[0]=l,M[1]=h,_.point=n,m=null}function u(n,e){if(m){var r=n-p;y+=oa(r)>180?r+(r>0?360:-360):r}else v=n,d=e;gc.point(n,e),t(n,e)}function i(){gc.lineStart()}function o(){u(v,d),gc.lineEnd(),oa(y)>Aa&&(l=-(h=180)),M[0]=l,M[1]=h,m=null}function a(n,t){return(t-=n)<0?t+360:t}function c(n,t){return n[0]-t[0]}function s(n,t){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:n<t[0]||t[1]<n}var l,f,h,g,p,v,d,m,y,x,M,_={point:n,lineStart:e,lineEnd:r,polygonStart:function(){_.point=u,_.lineStart=i,_.lineEnd=o,y=0,gc.polygonStart()},polygonEnd:function(){gc.polygonEnd(),_.point=n,_.lineStart=e,_.lineEnd=r,0>hc?(l=-(h=180),f=-(g=90)):y>Aa?g=90:-Aa>y&&(f=-90),M[0]=l,M[1]=h}};return function(n){g=h=-(l=f=1/0),x=[],Xo.geo.stream(n,_);var t=x.length;if(t){x.sort(c);for(var e,r=1,u=x[0],i=[u];t>r;++r)e=x[r],s(e[0],u)||s(e[1],u)?(a(u[0],e[1])>a(u[0],u[1])&&(u[1]=e[1]),a(e[0],u[1])>a(u[0],u[1])&&(u[0]=e[0])):i.push(u=e);for(var o,e,p=-1/0,t=i.length-1,r=0,u=i[t];t>=r;u=e,++r)e=i[r],(o=a(u[1],e[0]))>p&&(p=o,l=e[0],h=u[1])}return x=M=null,1/0===l||1/0===f?[[0/0,0/0],[0/0,0/0]]:[[l,f],[h,g]]}}(),Xo.geo.centroid=function(n){pc=vc=dc=mc=yc=xc=Mc=_c=bc=wc=Sc=0,Xo.geo.stream(n,kc);var t=bc,e=wc,r=Sc,u=t*t+e*e+r*r;return Ca>u&&(t=xc,e=Mc,r=_c,Aa>vc&&(t=dc,e=mc,r=yc),u=t*t+e*e+r*r,Ca>u)?[0/0,0/0]:[Math.atan2(e,t)*La,X(r/Math.sqrt(u))*La]};var pc,vc,dc,mc,yc,xc,Mc,_c,bc,wc,Sc,kc={sphere:g,point:me,lineStart:xe,lineEnd:Me,polygonStart:function(){kc.lineStart=_e},polygonEnd:function(){kc.lineStart=xe}},Ec=Ee(be,Te,ze,[-Sa,-Sa/2]),Ac=1e9;Xo.geo.clipExtent=function(){var n,t,e,r,u,i,o={stream:function(n){return u&&(u.valid=!1),u=i(n),u.valid=!0,u},extent:function(a){return arguments.length?(i=Pe(n=+a[0][0],t=+a[0][1],e=+a[1][0],r=+a[1][1]),u&&(u.valid=!1,u=null),o):[[n,t],[e,r]]}};return o.extent([[0,0],[960,500]])},(Xo.geo.conicEqualArea=function(){return je(He)}).raw=He,Xo.geo.albers=function(){return Xo.geo.conicEqualArea().rotate([96,0]).center([-.6,38.7]).parallels([29.5,45.5]).scale(1070)},Xo.geo.albersUsa=function(){function n(n){var i=n[0],o=n[1];return t=null,e(i,o),t||(r(i,o),t)||u(i,o),t}var t,e,r,u,i=Xo.geo.albers(),o=Xo.geo.conicEqualArea().rotate([154,0]).center([-2,58.5]).parallels([55,65]),a=Xo.geo.conicEqualArea().rotate([157,0]).center([-3,19.9]).parallels([8,18]),c={point:function(n,e){t=[n,e]}};return n.invert=function(n){var t=i.scale(),e=i.translate(),r=(n[0]-e[0])/t,u=(n[1]-e[1])/t;return(u>=.12&&.234>u&&r>=-.425&&-.214>r?o:u>=.166&&.234>u&&r>=-.214&&-.115>r?a:i).invert(n)},n.stream=function(n){var t=i.stream(n),e=o.stream(n),r=a.stream(n);return{point:function(n,u){t.point(n,u),e.point(n,u),r.point(n,u)},sphere:function(){t.sphere(),e.sphere(),r.sphere()},lineStart:function(){t.lineStart(),e.lineStart(),r.lineStart()},lineEnd:function(){t.lineEnd(),e.lineEnd(),r.lineEnd()},polygonStart:function(){t.polygonStart(),e.polygonStart(),r.polygonStart()},polygonEnd:function(){t.polygonEnd(),e.polygonEnd(),r.polygonEnd()}}},n.precision=function(t){return arguments.length?(i.precision(t),o.precision(t),a.precision(t),n):i.precision()},n.scale=function(t){return arguments.length?(i.scale(t),o.scale(.35*t),a.scale(t),n.translate(i.translate())):i.scale()},n.translate=function(t){if(!arguments.length)return i.translate();var s=i.scale(),l=+t[0],f=+t[1];return e=i.translate(t).clipExtent([[l-.455*s,f-.238*s],[l+.455*s,f+.238*s]]).stream(c).point,r=o.translate([l-.307*s,f+.201*s]).clipExtent([[l-.425*s+Aa,f+.12*s+Aa],[l-.214*s-Aa,f+.234*s-Aa]]).stream(c).point,u=a.translate([l-.205*s,f+.212*s]).clipExtent([[l-.214*s+Aa,f+.166*s+Aa],[l-.115*s-Aa,f+.234*s-Aa]]).stream(c).point,n},n.scale(1070)};var Cc,Nc,Lc,Tc,qc,zc,Rc={point:g,lineStart:g,lineEnd:g,polygonStart:function(){Nc=0,Rc.lineStart=Fe},polygonEnd:function(){Rc.lineStart=Rc.lineEnd=Rc.point=g,Cc+=oa(Nc/2)}},Dc={point:Oe,lineStart:g,lineEnd:g,polygonStart:g,polygonEnd:g},Pc={point:Ze,lineStart:Ve,lineEnd:Xe,polygonStart:function(){Pc.lineStart=$e},polygonEnd:function(){Pc.point=Ze,Pc.lineStart=Ve,Pc.lineEnd=Xe}};Xo.geo.path=function(){function n(n){return n&&("function"==typeof a&&i.pointRadius(+a.apply(this,arguments)),o&&o.valid||(o=u(i)),Xo.geo.stream(n,o)),i.result()}function t(){return o=null,n}var e,r,u,i,o,a=4.5;return n.area=function(n){return Cc=0,Xo.geo.stream(n,u(Rc)),Cc},n.centroid=function(n){return dc=mc=yc=xc=Mc=_c=bc=wc=Sc=0,Xo.geo.stream(n,u(Pc)),Sc?[bc/Sc,wc/Sc]:_c?[xc/_c,Mc/_c]:yc?[dc/yc,mc/yc]:[0/0,0/0]},n.bounds=function(n){return qc=zc=-(Lc=Tc=1/0),Xo.geo.stream(n,u(Dc)),[[Lc,Tc],[qc,zc]]},n.projection=function(n){return arguments.length?(u=(e=n)?n.stream||Je(n):bt,t()):e},n.context=function(n){return arguments.length?(i=null==(r=n)?new Ye:new Be(n),"function"!=typeof a&&i.pointRadius(a),t()):r},n.pointRadius=function(t){return arguments.length?(a="function"==typeof t?t:(i.pointRadius(+t),+t),n):a},n.projection(Xo.geo.albersUsa()).context(null)},Xo.geo.transform=function(n){return{stream:function(t){var e=new Ge(t);for(var r in n)e[r]=n[r];return e}}},Ge.prototype={point:function(n,t){this.stream.point(n,t)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart()},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}},Xo.geo.projection=Qe,Xo.geo.projectionMutator=nr,(Xo.geo.equirectangular=function(){return Qe(er)}).raw=er.invert=er,Xo.geo.rotation=function(n){function t(t){return t=n(t[0]*Na,t[1]*Na),t[0]*=La,t[1]*=La,t}return n=ur(n[0]%360*Na,n[1]*Na,n.length>2?n[2]*Na:0),t.invert=function(t){return t=n.invert(t[0]*Na,t[1]*Na),t[0]*=La,t[1]*=La,t},t},rr.invert=er,Xo.geo.circle=function(){function n(){var n="function"==typeof r?r.apply(this,arguments):r,t=ur(-n[0]*Na,-n[1]*Na,0).invert,u=[];return e(null,null,1,{point:function(n,e){u.push(n=t(n,e)),n[0]*=La,n[1]*=La}}),{type:"Polygon",coordinates:[u]}}var t,e,r=[0,0],u=6;return n.origin=function(t){return arguments.length?(r=t,n):r},n.angle=function(r){return arguments.length?(e=cr((t=+r)*Na,u*Na),n):t},n.precision=function(r){return arguments.length?(e=cr(t*Na,(u=+r)*Na),n):u},n.angle(90)},Xo.geo.distance=function(n,t){var e,r=(t[0]-n[0])*Na,u=n[1]*Na,i=t[1]*Na,o=Math.sin(r),a=Math.cos(r),c=Math.sin(u),s=Math.cos(u),l=Math.sin(i),f=Math.cos(i);return Math.atan2(Math.sqrt((e=f*o)*e+(e=s*l-c*f*a)*e),c*l+s*f*a)},Xo.geo.graticule=function(){function n(){return{type:"MultiLineString",coordinates:t()}}function t(){return Xo.range(Math.ceil(i/d)*d,u,d).map(h).concat(Xo.range(Math.ceil(s/m)*m,c,m).map(g)).concat(Xo.range(Math.ceil(r/p)*p,e,p).filter(function(n){return oa(n%d)>Aa}).map(l)).concat(Xo.range(Math.ceil(a/v)*v,o,v).filter(function(n){return oa(n%m)>Aa}).map(f))}var e,r,u,i,o,a,c,s,l,f,h,g,p=10,v=p,d=90,m=360,y=2.5;return n.lines=function(){return t().map(function(n){return{type:"LineString",coordinates:n}})},n.outline=function(){return{type:"Polygon",coordinates:[h(i).concat(g(c).slice(1),h(u).reverse().slice(1),g(s).reverse().slice(1))]}},n.extent=function(t){return arguments.length?n.majorExtent(t).minorExtent(t):n.minorExtent()},n.majorExtent=function(t){return arguments.length?(i=+t[0][0],u=+t[1][0],s=+t[0][1],c=+t[1][1],i>u&&(t=i,i=u,u=t),s>c&&(t=s,s=c,c=t),n.precision(y)):[[i,s],[u,c]]},n.minorExtent=function(t){return arguments.length?(r=+t[0][0],e=+t[1][0],a=+t[0][1],o=+t[1][1],r>e&&(t=r,r=e,e=t),a>o&&(t=a,a=o,o=t),n.precision(y)):[[r,a],[e,o]]},n.step=function(t){return arguments.length?n.majorStep(t).minorStep(t):n.minorStep()},n.majorStep=function(t){return arguments.length?(d=+t[0],m=+t[1],n):[d,m]},n.minorStep=function(t){return arguments.length?(p=+t[0],v=+t[1],n):[p,v]},n.precision=function(t){return arguments.length?(y=+t,l=lr(a,o,90),f=fr(r,e,y),h=lr(s,c,90),g=fr(i,u,y),n):y},n.majorExtent([[-180,-90+Aa],[180,90-Aa]]).minorExtent([[-180,-80-Aa],[180,80+Aa]])},Xo.geo.greatArc=function(){function n(){return{type:"LineString",coordinates:[t||r.apply(this,arguments),e||u.apply(this,arguments)]}}var t,e,r=hr,u=gr;return n.distance=function(){return Xo.geo.distance(t||r.apply(this,arguments),e||u.apply(this,arguments))},n.source=function(e){return arguments.length?(r=e,t="function"==typeof e?null:e,n):r},n.target=function(t){return arguments.length?(u=t,e="function"==typeof t?null:t,n):u},n.precision=function(){return arguments.length?n:0},n},Xo.geo.interpolate=function(n,t){return pr(n[0]*Na,n[1]*Na,t[0]*Na,t[1]*Na)},Xo.geo.length=function(n){return Uc=0,Xo.geo.stream(n,jc),Uc};var Uc,jc={sphere:g,point:g,lineStart:vr,lineEnd:g,polygonStart:g,polygonEnd:g},Hc=dr(function(n){return Math.sqrt(2/(1+n))},function(n){return 2*Math.asin(n/2)});(Xo.geo.azimuthalEqualArea=function(){return Qe(Hc)}).raw=Hc;var Fc=dr(function(n){var t=Math.acos(n);return t&&t/Math.sin(t)},bt);(Xo.geo.azimuthalEquidistant=function(){return Qe(Fc)}).raw=Fc,(Xo.geo.conicConformal=function(){return je(mr)}).raw=mr,(Xo.geo.conicEquidistant=function(){return je(yr)}).raw=yr;var Oc=dr(function(n){return 1/n},Math.atan);(Xo.geo.gnomonic=function(){return Qe(Oc)}).raw=Oc,xr.invert=function(n,t){return[n,2*Math.atan(Math.exp(t))-Ea]},(Xo.geo.mercator=function(){return Mr(xr)}).raw=xr;var Yc=dr(function(){return 1},Math.asin);(Xo.geo.orthographic=function(){return Qe(Yc)}).raw=Yc;var Ic=dr(function(n){return 1/(1+n)},function(n){return 2*Math.atan(n)});(Xo.geo.stereographic=function(){return Qe(Ic)}).raw=Ic,_r.invert=function(n,t){return[-t,2*Math.atan(Math.exp(n))-Ea]},(Xo.geo.transverseMercator=function(){var n=Mr(_r),t=n.center,e=n.rotate;return n.center=function(n){return n?t([-n[1],n[0]]):(n=t(),[-n[1],n[0]])},n.rotate=function(n){return n?e([n[0],n[1],n.length>2?n[2]+90:90]):(n=e(),[n[0],n[1],n[2]-90])},n.rotate([0,0])}).raw=_r,Xo.geom={},Xo.geom.hull=function(n){function t(n){if(n.length<3)return[];var t,u=_t(e),i=_t(r),o=n.length,a=[],c=[];for(t=0;o>t;t++)a.push([+u.call(this,n[t],t),+i.call(this,n[t],t),t]);for(a.sort(kr),t=0;o>t;t++)c.push([a[t][0],-a[t][1]]);var s=Sr(a),l=Sr(c),f=l[0]===s[0],h=l[l.length-1]===s[s.length-1],g=[];for(t=s.length-1;t>=0;--t)g.push(n[a[s[t]][2]]);for(t=+f;t<l.length-h;++t)g.push(n[a[l[t]][2]]);return g}var e=br,r=wr;return arguments.length?t(n):(t.x=function(n){return arguments.length?(e=n,t):e},t.y=function(n){return arguments.length?(r=n,t):r},t)},Xo.geom.polygon=function(n){return fa(n,Zc),n};var Zc=Xo.geom.polygon.prototype=[];Zc.area=function(){for(var n,t=-1,e=this.length,r=this[e-1],u=0;++t<e;)n=r,r=this[t],u+=n[1]*r[0]-n[0]*r[1];return.5*u},Zc.centroid=function(n){var t,e,r=-1,u=this.length,i=0,o=0,a=this[u-1];for(arguments.length||(n=-1/(6*this.area()));++r<u;)t=a,a=this[r],e=t[0]*a[1]-a[0]*t[1],i+=(t[0]+a[0])*e,o+=(t[1]+a[1])*e;return[i*n,o*n]},Zc.clip=function(n){for(var t,e,r,u,i,o,a=Cr(n),c=-1,s=this.length-Cr(this),l=this[s-1];++c<s;){for(t=n.slice(),n.length=0,u=this[c],i=t[(r=t.length-a)-1],e=-1;++e<r;)o=t[e],Er(o,l,u)?(Er(i,l,u)||n.push(Ar(i,o,l,u)),n.push(o)):Er(i,l,u)&&n.push(Ar(i,o,l,u)),i=o;a&&n.push(n[0]),l=u}return n};var Vc,Xc,$c,Bc,Wc,Jc=[],Gc=[];Pr.prototype.prepare=function(){for(var n,t=this.edges,e=t.length;e--;)n=t[e].edge,n.b&&n.a||t.splice(e,1);return t.sort(jr),t.length},Br.prototype={start:function(){return this.edge.l===this.site?this.edge.a:this.edge.b},end:function(){return this.edge.l===this.site?this.edge.b:this.edge.a}},Wr.prototype={insert:function(n,t){var e,r,u;if(n){if(t.P=n,t.N=n.N,n.N&&(n.N.P=t),n.N=t,n.R){for(n=n.R;n.L;)n=n.L;n.L=t}else n.R=t;e=n}else this._?(n=Qr(this._),t.P=null,t.N=n,n.P=n.L=t,e=n):(t.P=t.N=null,this._=t,e=null);for(t.L=t.R=null,t.U=e,t.C=!0,n=t;e&&e.C;)r=e.U,e===r.L?(u=r.R,u&&u.C?(e.C=u.C=!1,r.C=!0,n=r):(n===e.R&&(Gr(this,e),n=e,e=n.U),e.C=!1,r.C=!0,Kr(this,r))):(u=r.L,u&&u.C?(e.C=u.C=!1,r.C=!0,n=r):(n===e.L&&(Kr(this,e),n=e,e=n.U),e.C=!1,r.C=!0,Gr(this,r))),e=n.U;this._.C=!1},remove:function(n){n.N&&(n.N.P=n.P),n.P&&(n.P.N=n.N),n.N=n.P=null;var t,e,r,u=n.U,i=n.L,o=n.R;if(e=i?o?Qr(o):i:o,u?u.L===n?u.L=e:u.R=e:this._=e,i&&o?(r=e.C,e.C=n.C,e.L=i,i.U=e,e!==o?(u=e.U,e.U=n.U,n=e.R,u.L=n,e.R=o,o.U=e):(e.U=u,u=e,n=e.R)):(r=n.C,n=e),n&&(n.U=u),!r){if(n&&n.C)return n.C=!1,void 0;do{if(n===this._)break;if(n===u.L){if(t=u.R,t.C&&(t.C=!1,u.C=!0,Gr(this,u),t=u.R),t.L&&t.L.C||t.R&&t.R.C){t.R&&t.R.C||(t.L.C=!1,t.C=!0,Kr(this,t),t=u.R),t.C=u.C,u.C=t.R.C=!1,Gr(this,u),n=this._;break}}else if(t=u.L,t.C&&(t.C=!1,u.C=!0,Kr(this,u),t=u.L),t.L&&t.L.C||t.R&&t.R.C){t.L&&t.L.C||(t.R.C=!1,t.C=!0,Gr(this,t),t=u.L),t.C=u.C,u.C=t.L.C=!1,Kr(this,u),n=this._;break}t.C=!0,n=u,u=u.U}while(!n.C);n&&(n.C=!1)}}},Xo.geom.voronoi=function(n){function t(n){var t=new Array(n.length),r=a[0][0],u=a[0][1],i=a[1][0],o=a[1][1];return nu(e(n),a).cells.forEach(function(e,a){var c=e.edges,s=e.site,l=t[a]=c.length?c.map(function(n){var t=n.start();return[t.x,t.y]}):s.x>=r&&s.x<=i&&s.y>=u&&s.y<=o?[[r,o],[i,o],[i,u],[r,u]]:[];l.point=n[a]}),t}function e(n){return n.map(function(n,t){return{x:Math.round(i(n,t)/Aa)*Aa,y:Math.round(o(n,t)/Aa)*Aa,i:t}})}var r=br,u=wr,i=r,o=u,a=Kc;return n?t(n):(t.links=function(n){return nu(e(n)).edges.filter(function(n){return n.l&&n.r}).map(function(t){return{source:n[t.l.i],target:n[t.r.i]}})},t.triangles=function(n){var t=[];return nu(e(n)).cells.forEach(function(e,r){for(var u,i,o=e.site,a=e.edges.sort(jr),c=-1,s=a.length,l=a[s-1].edge,f=l.l===o?l.r:l.l;++c<s;)u=l,i=f,l=a[c].edge,f=l.l===o?l.r:l.l,r<i.i&&r<f.i&&eu(o,i,f)<0&&t.push([n[r],n[i.i],n[f.i]])}),t},t.x=function(n){return arguments.length?(i=_t(r=n),t):r},t.y=function(n){return arguments.length?(o=_t(u=n),t):u},t.clipExtent=function(n){return arguments.length?(a=null==n?Kc:n,t):a===Kc?null:a},t.size=function(n){return arguments.length?t.clipExtent(n&&[[0,0],n]):a===Kc?null:a&&a[1]},t)};var Kc=[[-1e6,-1e6],[1e6,1e6]];Xo.geom.delaunay=function(n){return Xo.geom.voronoi().triangles(n)},Xo.geom.quadtree=function(n,t,e,r,u){function i(n){function i(n,t,e,r,u,i,o,a){if(!isNaN(e)&&!isNaN(r))if(n.leaf){var c=n.x,l=n.y;if(null!=c)if(oa(c-e)+oa(l-r)<.01)s(n,t,e,r,u,i,o,a);else{var f=n.point;n.x=n.y=n.point=null,s(n,f,c,l,u,i,o,a),s(n,t,e,r,u,i,o,a)}else n.x=e,n.y=r,n.point=t}else s(n,t,e,r,u,i,o,a)}function s(n,t,e,r,u,o,a,c){var s=.5*(u+a),l=.5*(o+c),f=e>=s,h=r>=l,g=(h<<1)+f;n.leaf=!1,n=n.nodes[g]||(n.nodes[g]=iu()),f?u=s:a=s,h?o=l:c=l,i(n,t,e,r,u,o,a,c)}var l,f,h,g,p,v,d,m,y,x=_t(a),M=_t(c);if(null!=t)v=t,d=e,m=r,y=u;else if(m=y=-(v=d=1/0),f=[],h=[],p=n.length,o)for(g=0;p>g;++g)l=n[g],l.x<v&&(v=l.x),l.y<d&&(d=l.y),l.x>m&&(m=l.x),l.y>y&&(y=l.y),f.push(l.x),h.push(l.y);else for(g=0;p>g;++g){var _=+x(l=n[g],g),b=+M(l,g);v>_&&(v=_),d>b&&(d=b),_>m&&(m=_),b>y&&(y=b),f.push(_),h.push(b)}var w=m-v,S=y-d;w>S?y=d+w:m=v+S;var k=iu();if(k.add=function(n){i(k,n,+x(n,++g),+M(n,g),v,d,m,y)},k.visit=function(n){ou(n,k,v,d,m,y)},g=-1,null==t){for(;++g<p;)i(k,n[g],f[g],h[g],v,d,m,y);--g}else n.forEach(k.add);return f=h=n=l=null,k}var o,a=br,c=wr;return(o=arguments.length)?(a=ru,c=uu,3===o&&(u=e,r=t,e=t=0),i(n)):(i.x=function(n){return arguments.length?(a=n,i):a},i.y=function(n){return arguments.length?(c=n,i):c},i.extent=function(n){return arguments.length?(null==n?t=e=r=u=null:(t=+n[0][0],e=+n[0][1],r=+n[1][0],u=+n[1][1]),i):null==t?null:[[t,e],[r,u]]},i.size=function(n){return arguments.length?(null==n?t=e=r=u=null:(t=e=0,r=+n[0],u=+n[1]),i):null==t?null:[r-t,u-e]},i)},Xo.interpolateRgb=au,Xo.interpolateObject=cu,Xo.interpolateNumber=su,Xo.interpolateString=lu;var Qc=/[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g;Xo.interpolate=fu,Xo.interpolators=[function(n,t){var e=typeof t;return("string"===e?Va.has(t)||/^(#|rgb\(|hsl\()/.test(t)?au:lu:t instanceof G?au:"object"===e?Array.isArray(t)?hu:cu:su)(n,t)}],Xo.interpolateArray=hu;var ns=function(){return bt},ts=Xo.map({linear:ns,poly:xu,quad:function(){return du},cubic:function(){return mu},sin:function(){return Mu},exp:function(){return _u},circle:function(){return bu},elastic:wu,back:Su,bounce:function(){return ku}}),es=Xo.map({"in":bt,out:pu,"in-out":vu,"out-in":function(n){return vu(pu(n))}});Xo.ease=function(n){var t=n.indexOf("-"),e=t>=0?n.substring(0,t):n,r=t>=0?n.substring(t+1):"in";return e=ts.get(e)||ns,r=es.get(r)||bt,gu(r(e.apply(null,$o.call(arguments,1))))},Xo.interpolateHcl=Eu,Xo.interpolateHsl=Au,Xo.interpolateLab=Cu,Xo.interpolateRound=Nu,Xo.transform=function(n){var t=Wo.createElementNS(Xo.ns.prefix.svg,"g");return(Xo.transform=function(n){if(null!=n){t.setAttribute("transform",n);var e=t.transform.baseVal.consolidate()}return new Lu(e?e.matrix:rs)})(n)},Lu.prototype.toString=function(){return"translate("+this.translate+")rotate("+this.rotate+")skewX("+this.skew+")scale("+this.scale+")"};var rs={a:1,b:0,c:0,d:1,e:0,f:0};Xo.interpolateTransform=Ru,Xo.layout={},Xo.layout.bundle=function(){return function(n){for(var t=[],e=-1,r=n.length;++e<r;)t.push(Uu(n[e]));return t}},Xo.layout.chord=function(){function n(){var n,s,f,h,g,p={},v=[],d=Xo.range(i),m=[];for(e=[],r=[],n=0,h=-1;++h<i;){for(s=0,g=-1;++g<i;)s+=u[h][g];v.push(s),m.push(Xo.range(i)),n+=s}for(o&&d.sort(function(n,t){return o(v[n],v[t])}),a&&m.forEach(function(n,t){n.sort(function(n,e){return a(u[t][n],u[t][e])})}),n=(ka-l*i)/n,s=0,h=-1;++h<i;){for(f=s,g=-1;++g<i;){var y=d[h],x=m[y][g],M=u[y][x],_=s,b=s+=M*n;p[y+"-"+x]={index:y,subindex:x,startAngle:_,endAngle:b,value:M}}r[y]={index:y,startAngle:f,endAngle:s,value:(s-f)/n},s+=l}for(h=-1;++h<i;)for(g=h-1;++g<i;){var w=p[h+"-"+g],S=p[g+"-"+h];(w.value||S.value)&&e.push(w.value<S.value?{source:S,target:w}:{source:w,target:S})}c&&t()}function t(){e.sort(function(n,t){return c((n.source.value+n.target.value)/2,(t.source.value+t.target.value)/2)})}var e,r,u,i,o,a,c,s={},l=0;return s.matrix=function(n){return arguments.length?(i=(u=n)&&u.length,e=r=null,s):u},s.padding=function(n){return arguments.length?(l=n,e=r=null,s):l},s.sortGroups=function(n){return arguments.length?(o=n,e=r=null,s):o},s.sortSubgroups=function(n){return arguments.length?(a=n,e=null,s):a},s.sortChords=function(n){return arguments.length?(c=n,e&&t(),s):c},s.chords=function(){return e||n(),e},s.groups=function(){return r||n(),r},s},Xo.layout.force=function(){function n(n){return function(t,e,r,u){if(t.point!==n){var i=t.cx-n.x,o=t.cy-n.y,a=u-e,c=i*i+o*o;if(c>a*a/d){if(p>c){var s=t.charge/c;n.px-=i*s,n.py-=o*s}return!0}if(t.point&&c&&p>c){var s=t.pointCharge/c;n.px-=i*s,n.py-=o*s}}return!t.charge}}function t(n){n.px=Xo.event.x,n.py=Xo.event.y,a.resume()}var e,r,u,i,o,a={},c=Xo.dispatch("start","tick","end"),s=[1,1],l=.9,f=us,h=is,g=-30,p=os,v=.1,d=.64,m=[],y=[];return a.tick=function(){if((r*=.99)<.005)return c.end({type:"end",alpha:r=0}),!0;var t,e,a,f,h,p,d,x,M,_=m.length,b=y.length;for(e=0;b>e;++e)a=y[e],f=a.source,h=a.target,x=h.x-f.x,M=h.y-f.y,(p=x*x+M*M)&&(p=r*i[e]*((p=Math.sqrt(p))-u[e])/p,x*=p,M*=p,h.x-=x*(d=f.weight/(h.weight+f.weight)),h.y-=M*d,f.x+=x*(d=1-d),f.y+=M*d);if((d=r*v)&&(x=s[0]/2,M=s[1]/2,e=-1,d))for(;++e<_;)a=m[e],a.x+=(x-a.x)*d,a.y+=(M-a.y)*d;if(g)for(Zu(t=Xo.geom.quadtree(m),r,o),e=-1;++e<_;)(a=m[e]).fixed||t.visit(n(a));for(e=-1;++e<_;)a=m[e],a.fixed?(a.x=a.px,a.y=a.py):(a.x-=(a.px-(a.px=a.x))*l,a.y-=(a.py-(a.py=a.y))*l);c.tick({type:"tick",alpha:r})},a.nodes=function(n){return arguments.length?(m=n,a):m},a.links=function(n){return arguments.length?(y=n,a):y},a.size=function(n){return arguments.length?(s=n,a):s},a.linkDistance=function(n){return arguments.length?(f="function"==typeof n?n:+n,a):f},a.distance=a.linkDistance,a.linkStrength=function(n){return arguments.length?(h="function"==typeof n?n:+n,a):h},a.friction=function(n){return arguments.length?(l=+n,a):l},a.charge=function(n){return arguments.length?(g="function"==typeof n?n:+n,a):g},a.chargeDistance=function(n){return arguments.length?(p=n*n,a):Math.sqrt(p)},a.gravity=function(n){return arguments.length?(v=+n,a):v},a.theta=function(n){return arguments.length?(d=n*n,a):Math.sqrt(d)},a.alpha=function(n){return arguments.length?(n=+n,r?r=n>0?n:0:n>0&&(c.start({type:"start",alpha:r=n}),Xo.timer(a.tick)),a):r},a.start=function(){function n(n,r){if(!e){for(e=new Array(c),a=0;c>a;++a)e[a]=[];for(a=0;s>a;++a){var u=y[a];e[u.source.index].push(u.target),e[u.target.index].push(u.source)}}for(var i,o=e[t],a=-1,s=o.length;++a<s;)if(!isNaN(i=o[a][n]))return i;return Math.random()*r}var t,e,r,c=m.length,l=y.length,p=s[0],v=s[1];for(t=0;c>t;++t)(r=m[t]).index=t,r.weight=0;for(t=0;l>t;++t)r=y[t],"number"==typeof r.source&&(r.source=m[r.source]),"number"==typeof r.target&&(r.target=m[r.target]),++r.source.weight,++r.target.weight;for(t=0;c>t;++t)r=m[t],isNaN(r.x)&&(r.x=n("x",p)),isNaN(r.y)&&(r.y=n("y",v)),isNaN(r.px)&&(r.px=r.x),isNaN(r.py)&&(r.py=r.y);if(u=[],"function"==typeof f)for(t=0;l>t;++t)u[t]=+f.call(this,y[t],t);else for(t=0;l>t;++t)u[t]=f;if(i=[],"function"==typeof h)for(t=0;l>t;++t)i[t]=+h.call(this,y[t],t);else for(t=0;l>t;++t)i[t]=h;if(o=[],"function"==typeof g)for(t=0;c>t;++t)o[t]=+g.call(this,m[t],t);else for(t=0;c>t;++t)o[t]=g;return a.resume()},a.resume=function(){return a.alpha(.1)},a.stop=function(){return a.alpha(0)},a.drag=function(){return e||(e=Xo.behavior.drag().origin(bt).on("dragstart.force",Fu).on("drag.force",t).on("dragend.force",Ou)),arguments.length?(this.on("mouseover.force",Yu).on("mouseout.force",Iu).call(e),void 0):e},Xo.rebind(a,c,"on")};var us=20,is=1,os=1/0;Xo.layout.hierarchy=function(){function n(t,o,a){var c=u.call(e,t,o);if(t.depth=o,a.push(t),c&&(s=c.length)){for(var s,l,f=-1,h=t.children=new Array(s),g=0,p=o+1;++f<s;)l=h[f]=n(c[f],p,a),l.parent=t,g+=l.value;r&&h.sort(r),i&&(t.value=g)}else delete t.children,i&&(t.value=+i.call(e,t,o)||0);return t}function t(n,r){var u=n.children,o=0;if(u&&(a=u.length))for(var a,c=-1,s=r+1;++c<a;)o+=t(u[c],s);else i&&(o=+i.call(e,n,r)||0);return i&&(n.value=o),o}function e(t){var e=[];return n(t,0,e),e}var r=Bu,u=Xu,i=$u;return e.sort=function(n){return arguments.length?(r=n,e):r},e.children=function(n){return arguments.length?(u=n,e):u},e.value=function(n){return arguments.length?(i=n,e):i},e.revalue=function(n){return t(n,0),n},e},Xo.layout.partition=function(){function n(t,e,r,u){var i=t.children;if(t.x=e,t.y=t.depth*u,t.dx=r,t.dy=u,i&&(o=i.length)){var o,a,c,s=-1;for(r=t.value?r/t.value:0;++s<o;)n(a=i[s],e,c=a.value*r,u),e+=c}}function t(n){var e=n.children,r=0;if(e&&(u=e.length))for(var u,i=-1;++i<u;)r=Math.max(r,t(e[i]));return 1+r}function e(e,i){var o=r.call(this,e,i);return n(o[0],0,u[0],u[1]/t(o[0])),o}var r=Xo.layout.hierarchy(),u=[1,1];return e.size=function(n){return arguments.length?(u=n,e):u},Vu(e,r)},Xo.layout.pie=function(){function n(i){var o=i.map(function(e,r){return+t.call(n,e,r)}),a=+("function"==typeof r?r.apply(this,arguments):r),c=(("function"==typeof u?u.apply(this,arguments):u)-a)/Xo.sum(o),s=Xo.range(i.length);null!=e&&s.sort(e===as?function(n,t){return o[t]-o[n]}:function(n,t){return e(i[n],i[t])});var l=[];return s.forEach(function(n){var t;l[n]={data:i[n],value:t=o[n],startAngle:a,endAngle:a+=t*c}}),l}var t=Number,e=as,r=0,u=ka;return n.value=function(e){return arguments.length?(t=e,n):t},n.sort=function(t){return arguments.length?(e=t,n):e},n.startAngle=function(t){return arguments.length?(r=t,n):r},n.endAngle=function(t){return arguments.length?(u=t,n):u},n};var as={};Xo.layout.stack=function(){function n(a,c){var s=a.map(function(e,r){return t.call(n,e,r)}),l=s.map(function(t){return t.map(function(t,e){return[i.call(n,t,e),o.call(n,t,e)]})}),f=e.call(n,l,c);s=Xo.permute(s,f),l=Xo.permute(l,f);var h,g,p,v=r.call(n,l,c),d=s.length,m=s[0].length;for(g=0;m>g;++g)for(u.call(n,s[0][g],p=v[g],l[0][g][1]),h=1;d>h;++h)u.call(n,s[h][g],p+=l[h-1][g][1],l[h][g][1]);return a}var t=bt,e=Qu,r=ni,u=Ku,i=Ju,o=Gu;return n.values=function(e){return arguments.length?(t=e,n):t},n.order=function(t){return arguments.length?(e="function"==typeof t?t:cs.get(t)||Qu,n):e},n.offset=function(t){return arguments.length?(r="function"==typeof t?t:ss.get(t)||ni,n):r},n.x=function(t){return arguments.length?(i=t,n):i},n.y=function(t){return arguments.length?(o=t,n):o},n.out=function(t){return arguments.length?(u=t,n):u},n};var cs=Xo.map({"inside-out":function(n){var t,e,r=n.length,u=n.map(ti),i=n.map(ei),o=Xo.range(r).sort(function(n,t){return u[n]-u[t]}),a=0,c=0,s=[],l=[];for(t=0;r>t;++t)e=o[t],c>a?(a+=i[e],s.push(e)):(c+=i[e],l.push(e));return l.reverse().concat(s)},reverse:function(n){return Xo.range(n.length).reverse()},"default":Qu}),ss=Xo.map({silhouette:function(n){var t,e,r,u=n.length,i=n[0].length,o=[],a=0,c=[];for(e=0;i>e;++e){for(t=0,r=0;u>t;t++)r+=n[t][e][1];r>a&&(a=r),o.push(r)}for(e=0;i>e;++e)c[e]=(a-o[e])/2;return c},wiggle:function(n){var t,e,r,u,i,o,a,c,s,l=n.length,f=n[0],h=f.length,g=[];for(g[0]=c=s=0,e=1;h>e;++e){for(t=0,u=0;l>t;++t)u+=n[t][e][1];for(t=0,i=0,a=f[e][0]-f[e-1][0];l>t;++t){for(r=0,o=(n[t][e][1]-n[t][e-1][1])/(2*a);t>r;++r)o+=(n[r][e][1]-n[r][e-1][1])/a;i+=o*n[t][e][1]}g[e]=c-=u?i/u*a:0,s>c&&(s=c)}for(e=0;h>e;++e)g[e]-=s;return g},expand:function(n){var t,e,r,u=n.length,i=n[0].length,o=1/u,a=[];for(e=0;i>e;++e){for(t=0,r=0;u>t;t++)r+=n[t][e][1];if(r)for(t=0;u>t;t++)n[t][e][1]/=r;else for(t=0;u>t;t++)n[t][e][1]=o}for(e=0;i>e;++e)a[e]=0;return a},zero:ni});Xo.layout.histogram=function(){function n(n,i){for(var o,a,c=[],s=n.map(e,this),l=r.call(this,s,i),f=u.call(this,l,s,i),i=-1,h=s.length,g=f.length-1,p=t?1:1/h;++i<g;)o=c[i]=[],o.dx=f[i+1]-(o.x=f[i]),o.y=0;if(g>0)for(i=-1;++i<h;)a=s[i],a>=l[0]&&a<=l[1]&&(o=c[Xo.bisect(f,a,1,g)-1],o.y+=p,o.push(n[i]));return c}var t=!0,e=Number,r=oi,u=ui;return n.value=function(t){return arguments.length?(e=t,n):e},n.range=function(t){return arguments.length?(r=_t(t),n):r},n.bins=function(t){return arguments.length?(u="number"==typeof t?function(n){return ii(n,t)}:_t(t),n):u},n.frequency=function(e){return arguments.length?(t=!!e,n):t},n},Xo.layout.tree=function(){function n(n,i){function o(n,t){var r=n.children,u=n._tree;if(r&&(i=r.length)){for(var i,a,s,l=r[0],f=l,h=-1;++h<i;)s=r[h],o(s,a),f=c(s,a,f),a=s;vi(n);var g=.5*(l._tree.prelim+s._tree.prelim);t?(u.prelim=t._tree.prelim+e(n,t),u.mod=u.prelim-g):u.prelim=g}else t&&(u.prelim=t._tree.prelim+e(n,t))}function a(n,t){n.x=n._tree.prelim+t;var e=n.children;if(e&&(r=e.length)){var r,u=-1;for(t+=n._tree.mod;++u<r;)a(e[u],t)}}function c(n,t,r){if(t){for(var u,i=n,o=n,a=t,c=n.parent.children[0],s=i._tree.mod,l=o._tree.mod,f=a._tree.mod,h=c._tree.mod;a=si(a),i=ci(i),a&&i;)c=ci(c),o=si(o),o._tree.ancestor=n,u=a._tree.prelim+f-i._tree.prelim-s+e(a,i),u>0&&(di(mi(a,n,r),n,u),s+=u,l+=u),f+=a._tree.mod,s+=i._tree.mod,h+=c._tree.mod,l+=o._tree.mod;a&&!si(o)&&(o._tree.thread=a,o._tree.mod+=f-l),i&&!ci(c)&&(c._tree.thread=i,c._tree.mod+=s-h,r=n)}return r}var s=t.call(this,n,i),l=s[0];pi(l,function(n,t){n._tree={ancestor:n,prelim:0,mod:0,change:0,shift:0,number:t?t._tree.number+1:0}}),o(l),a(l,-l._tree.prelim);var f=li(l,hi),h=li(l,fi),g=li(l,gi),p=f.x-e(f,h)/2,v=h.x+e(h,f)/2,d=g.depth||1;return pi(l,u?function(n){n.x*=r[0],n.y=n.depth*r[1],delete n._tree}:function(n){n.x=(n.x-p)/(v-p)*r[0],n.y=n.depth/d*r[1],delete n._tree}),s}var t=Xo.layout.hierarchy().sort(null).value(null),e=ai,r=[1,1],u=!1;return n.separation=function(t){return arguments.length?(e=t,n):e},n.size=function(t){return arguments.length?(u=null==(r=t),n):u?null:r},n.nodeSize=function(t){return arguments.length?(u=null!=(r=t),n):u?r:null},Vu(n,t)},Xo.layout.pack=function(){function n(n,i){var o=e.call(this,n,i),a=o[0],c=u[0],s=u[1],l=null==t?Math.sqrt:"function"==typeof t?t:function(){return t};if(a.x=a.y=0,pi(a,function(n){n.r=+l(n.value)}),pi(a,bi),r){var f=r*(t?1:Math.max(2*a.r/c,2*a.r/s))/2;pi(a,function(n){n.r+=f}),pi(a,bi),pi(a,function(n){n.r-=f})}return ki(a,c/2,s/2,t?1:1/Math.max(2*a.r/c,2*a.r/s)),o}var t,e=Xo.layout.hierarchy().sort(yi),r=0,u=[1,1];return n.size=function(t){return arguments.length?(u=t,n):u},n.radius=function(e){return arguments.length?(t=null==e||"function"==typeof e?e:+e,n):t},n.padding=function(t){return arguments.length?(r=+t,n):r},Vu(n,e)},Xo.layout.cluster=function(){function n(n,i){var o,a=t.call(this,n,i),c=a[0],s=0;pi(c,function(n){var t=n.children;t&&t.length?(n.x=Ci(t),n.y=Ai(t)):(n.x=o?s+=e(n,o):0,n.y=0,o=n)});var l=Ni(c),f=Li(c),h=l.x-e(l,f)/2,g=f.x+e(f,l)/2;return pi(c,u?function(n){n.x=(n.x-c.x)*r[0],n.y=(c.y-n.y)*r[1]}:function(n){n.x=(n.x-h)/(g-h)*r[0],n.y=(1-(c.y?n.y/c.y:1))*r[1]}),a}var t=Xo.layout.hierarchy().sort(null).value(null),e=ai,r=[1,1],u=!1;return n.separation=function(t){return arguments.length?(e=t,n):e},n.size=function(t){return arguments.length?(u=null==(r=t),n):u?null:r},n.nodeSize=function(t){return arguments.length?(u=null!=(r=t),n):u?r:null},Vu(n,t)},Xo.layout.treemap=function(){function n(n,t){for(var e,r,u=-1,i=n.length;++u<i;)r=(e=n[u]).value*(0>t?0:t),e.area=isNaN(r)||0>=r?0:r}function t(e){var i=e.children;if(i&&i.length){var o,a,c,s=f(e),l=[],h=i.slice(),p=1/0,v="slice"===g?s.dx:"dice"===g?s.dy:"slice-dice"===g?1&e.depth?s.dy:s.dx:Math.min(s.dx,s.dy);for(n(h,s.dx*s.dy/e.value),l.area=0;(c=h.length)>0;)l.push(o=h[c-1]),l.area+=o.area,"squarify"!==g||(a=r(l,v))<=p?(h.pop(),p=a):(l.area-=l.pop().area,u(l,v,s,!1),v=Math.min(s.dx,s.dy),l.length=l.area=0,p=1/0);l.length&&(u(l,v,s,!0),l.length=l.area=0),i.forEach(t)}}function e(t){var r=t.children;if(r&&r.length){var i,o=f(t),a=r.slice(),c=[];for(n(a,o.dx*o.dy/t.value),c.area=0;i=a.pop();)c.push(i),c.area+=i.area,null!=i.z&&(u(c,i.z?o.dx:o.dy,o,!a.length),c.length=c.area=0);r.forEach(e)}}function r(n,t){for(var e,r=n.area,u=0,i=1/0,o=-1,a=n.length;++o<a;)(e=n[o].area)&&(i>e&&(i=e),e>u&&(u=e));return r*=r,t*=t,r?Math.max(t*u*p/r,r/(t*i*p)):1/0}function u(n,t,e,r){var u,i=-1,o=n.length,a=e.x,s=e.y,l=t?c(n.area/t):0;if(t==e.dx){for((r||l>e.dy)&&(l=e.dy);++i<o;)u=n[i],u.x=a,u.y=s,u.dy=l,a+=u.dx=Math.min(e.x+e.dx-a,l?c(u.area/l):0);u.z=!0,u.dx+=e.x+e.dx-a,e.y+=l,e.dy-=l}else{for((r||l>e.dx)&&(l=e.dx);++i<o;)u=n[i],u.x=a,u.y=s,u.dx=l,s+=u.dy=Math.min(e.y+e.dy-s,l?c(u.area/l):0);u.z=!1,u.dy+=e.y+e.dy-s,e.x+=l,e.dx-=l}}function i(r){var u=o||a(r),i=u[0];return i.x=0,i.y=0,i.dx=s[0],i.dy=s[1],o&&a.revalue(i),n([i],i.dx*i.dy/i.value),(o?e:t)(i),h&&(o=u),u}var o,a=Xo.layout.hierarchy(),c=Math.round,s=[1,1],l=null,f=Ti,h=!1,g="squarify",p=.5*(1+Math.sqrt(5));return i.size=function(n){return arguments.length?(s=n,i):s},i.padding=function(n){function t(t){var e=n.call(i,t,t.depth);return null==e?Ti(t):qi(t,"number"==typeof e?[e,e,e,e]:e)}function e(t){return qi(t,n)}if(!arguments.length)return l;var r;return f=null==(l=n)?Ti:"function"==(r=typeof n)?t:"number"===r?(n=[n,n,n,n],e):e,i},i.round=function(n){return arguments.length?(c=n?Math.round:Number,i):c!=Number},i.sticky=function(n){return arguments.length?(h=n,o=null,i):h},i.ratio=function(n){return arguments.length?(p=n,i):p},i.mode=function(n){return arguments.length?(g=n+"",i):g},Vu(i,a)},Xo.random={normal:function(n,t){var e=arguments.length;return 2>e&&(t=1),1>e&&(n=0),function(){var e,r,u;do e=2*Math.random()-1,r=2*Math.random()-1,u=e*e+r*r;while(!u||u>1);return n+t*e*Math.sqrt(-2*Math.log(u)/u)}},logNormal:function(){var n=Xo.random.normal.apply(Xo,arguments);return function(){return Math.exp(n())}},bates:function(n){var t=Xo.random.irwinHall(n);return function(){return t()/n}},irwinHall:function(n){return function(){for(var t=0,e=0;n>e;e++)t+=Math.random();return t}}},Xo.scale={};var ls={floor:bt,ceil:bt};Xo.scale.linear=function(){return Hi([0,1],[0,1],fu,!1)};var fs={s:1,g:1,p:1,r:1,e:1};Xo.scale.log=function(){return $i(Xo.scale.linear().domain([0,1]),10,!0,[1,10])};var hs=Xo.format(".0e"),gs={floor:function(n){return-Math.ceil(-n)},ceil:function(n){return-Math.floor(-n)}};Xo.scale.pow=function(){return Bi(Xo.scale.linear(),1,[0,1])},Xo.scale.sqrt=function(){return Xo.scale.pow().exponent(.5)},Xo.scale.ordinal=function(){return Ji([],{t:"range",a:[[]]})},Xo.scale.category10=function(){return Xo.scale.ordinal().range(ps)},Xo.scale.category20=function(){return Xo.scale.ordinal().range(vs)},Xo.scale.category20b=function(){return Xo.scale.ordinal().range(ds)},Xo.scale.category20c=function(){return Xo.scale.ordinal().range(ms)};var ps=[2062260,16744206,2924588,14034728,9725885,9197131,14907330,8355711,12369186,1556175].map(ht),vs=[2062260,11454440,16744206,16759672,2924588,10018698,14034728,16750742,9725885,12955861,9197131,12885140,14907330,16234194,8355711,13092807,12369186,14408589,1556175,10410725].map(ht),ds=[3750777,5395619,7040719,10264286,6519097,9216594,11915115,13556636,9202993,12426809,15186514,15190932,8666169,11356490,14049643,15177372,8077683,10834324,13528509,14589654].map(ht),ms=[3244733,7057110,10406625,13032431,15095053,16616764,16625259,16634018,3253076,7652470,10607003,13101504,7695281,10394312,12369372,14342891,6513507,9868950,12434877,14277081].map(ht);Xo.scale.quantile=function(){return Gi([],[])},Xo.scale.quantize=function(){return Ki(0,1,[0,1])},Xo.scale.threshold=function(){return Qi([.5],[0,1])},Xo.scale.identity=function(){return no([0,1])},Xo.svg={},Xo.svg.arc=function(){function n(){var n=t.apply(this,arguments),i=e.apply(this,arguments),o=r.apply(this,arguments)+ys,a=u.apply(this,arguments)+ys,c=(o>a&&(c=o,o=a,a=c),a-o),s=Sa>c?"0":"1",l=Math.cos(o),f=Math.sin(o),h=Math.cos(a),g=Math.sin(a);return c>=xs?n?"M0,"+i+"A"+i+","+i+" 0 1,1 0,"+-i+"A"+i+","+i+" 0 1,1 0,"+i+"M0,"+n+"A"+n+","+n+" 0 1,0 0,"+-n+"A"+n+","+n+" 0 1,0 0,"+n+"Z":"M0,"+i+"A"+i+","+i+" 0 1,1 0,"+-i+"A"+i+","+i+" 0 1,1 0,"+i+"Z":n?"M"+i*l+","+i*f+"A"+i+","+i+" 0 "+s+",1 "+i*h+","+i*g+"L"+n*h+","+n*g+"A"+n+","+n+" 0 "+s+",0 "+n*l+","+n*f+"Z":"M"+i*l+","+i*f+"A"+i+","+i+" 0 "+s+",1 "+i*h+","+i*g+"L0,0"+"Z"}var t=to,e=eo,r=ro,u=uo;return n.innerRadius=function(e){return arguments.length?(t=_t(e),n):t},n.outerRadius=function(t){return arguments.length?(e=_t(t),n):e},n.startAngle=function(t){return arguments.length?(r=_t(t),n):r},n.endAngle=function(t){return arguments.length?(u=_t(t),n):u},n.centroid=function(){var n=(t.apply(this,arguments)+e.apply(this,arguments))/2,i=(r.apply(this,arguments)+u.apply(this,arguments))/2+ys;return[Math.cos(i)*n,Math.sin(i)*n]},n};var ys=-Ea,xs=ka-Aa;Xo.svg.line=function(){return io(bt)};var Ms=Xo.map({linear:oo,"linear-closed":ao,step:co,"step-before":so,"step-after":lo,basis:mo,"basis-open":yo,"basis-closed":xo,bundle:Mo,cardinal:go,"cardinal-open":fo,"cardinal-closed":ho,monotone:Eo});Ms.forEach(function(n,t){t.key=n,t.closed=/-closed$/.test(n)});var _s=[0,2/3,1/3,0],bs=[0,1/3,2/3,0],ws=[0,1/6,2/3,1/6];Xo.svg.line.radial=function(){var n=io(Ao);return n.radius=n.x,delete n.x,n.angle=n.y,delete n.y,n},so.reverse=lo,lo.reverse=so,Xo.svg.area=function(){return Co(bt)},Xo.svg.area.radial=function(){var n=Co(Ao);return n.radius=n.x,delete n.x,n.innerRadius=n.x0,delete n.x0,n.outerRadius=n.x1,delete n.x1,n.angle=n.y,delete n.y,n.startAngle=n.y0,delete n.y0,n.endAngle=n.y1,delete n.y1,n},Xo.svg.chord=function(){function n(n,a){var c=t(this,i,n,a),s=t(this,o,n,a);return"M"+c.p0+r(c.r,c.p1,c.a1-c.a0)+(e(c,s)?u(c.r,c.p1,c.r,c.p0):u(c.r,c.p1,s.r,s.p0)+r(s.r,s.p1,s.a1-s.a0)+u(s.r,s.p1,c.r,c.p0))+"Z"}function t(n,t,e,r){var u=t.call(n,e,r),i=a.call(n,u,r),o=c.call(n,u,r)+ys,l=s.call(n,u,r)+ys;return{r:i,a0:o,a1:l,p0:[i*Math.cos(o),i*Math.sin(o)],p1:[i*Math.cos(l),i*Math.sin(l)]}}function e(n,t){return n.a0==t.a0&&n.a1==t.a1}function r(n,t,e){return"A"+n+","+n+" 0 "+ +(e>Sa)+",1 "+t}function u(n,t,e,r){return"Q 0,0 "+r}var i=hr,o=gr,a=No,c=ro,s=uo;return n.radius=function(t){return arguments.length?(a=_t(t),n):a},n.source=function(t){return arguments.length?(i=_t(t),n):i},n.target=function(t){return arguments.length?(o=_t(t),n):o},n.startAngle=function(t){return arguments.length?(c=_t(t),n):c},n.endAngle=function(t){return arguments.length?(s=_t(t),n):s},n},Xo.svg.diagonal=function(){function n(n,u){var i=t.call(this,n,u),o=e.call(this,n,u),a=(i.y+o.y)/2,c=[i,{x:i.x,y:a},{x:o.x,y:a},o];return c=c.map(r),"M"+c[0]+"C"+c[1]+" "+c[2]+" "+c[3]}var t=hr,e=gr,r=Lo;return n.source=function(e){return arguments.length?(t=_t(e),n):t},n.target=function(t){return arguments.length?(e=_t(t),n):e},n.projection=function(t){return arguments.length?(r=t,n):r},n},Xo.svg.diagonal.radial=function(){var n=Xo.svg.diagonal(),t=Lo,e=n.projection;return n.projection=function(n){return arguments.length?e(To(t=n)):t},n},Xo.svg.symbol=function(){function n(n,r){return(Ss.get(t.call(this,n,r))||Ro)(e.call(this,n,r))}var t=zo,e=qo;return n.type=function(e){return arguments.length?(t=_t(e),n):t},n.size=function(t){return arguments.length?(e=_t(t),n):e},n};var Ss=Xo.map({circle:Ro,cross:function(n){var t=Math.sqrt(n/5)/2;return"M"+-3*t+","+-t+"H"+-t+"V"+-3*t+"H"+t+"V"+-t+"H"+3*t+"V"+t+"H"+t+"V"+3*t+"H"+-t+"V"+t+"H"+-3*t+"Z"},diamond:function(n){var t=Math.sqrt(n/(2*Cs)),e=t*Cs;return"M0,"+-t+"L"+e+",0"+" 0,"+t+" "+-e+",0"+"Z"},square:function(n){var t=Math.sqrt(n)/2;return"M"+-t+","+-t+"L"+t+","+-t+" "+t+","+t+" "+-t+","+t+"Z"},"triangle-down":function(n){var t=Math.sqrt(n/As),e=t*As/2;return"M0,"+e+"L"+t+","+-e+" "+-t+","+-e+"Z"},"triangle-up":function(n){var t=Math.sqrt(n/As),e=t*As/2;return"M0,"+-e+"L"+t+","+e+" "+-t+","+e+"Z"}});Xo.svg.symbolTypes=Ss.keys();var ks,Es,As=Math.sqrt(3),Cs=Math.tan(30*Na),Ns=[],Ls=0;Ns.call=da.call,Ns.empty=da.empty,Ns.node=da.node,Ns.size=da.size,Xo.transition=function(n){return arguments.length?ks?n.transition():n:xa.transition()},Xo.transition.prototype=Ns,Ns.select=function(n){var t,e,r,u=this.id,i=[];n=M(n);for(var o=-1,a=this.length;++o<a;){i.push(t=[]);for(var c=this[o],s=-1,l=c.length;++s<l;)(r=c[s])&&(e=n.call(r,r.__data__,s,o))?("__data__"in r&&(e.__data__=r.__data__),jo(e,s,u,r.__transition__[u]),t.push(e)):t.push(null)}return Do(i,u)},Ns.selectAll=function(n){var t,e,r,u,i,o=this.id,a=[];n=_(n);for(var c=-1,s=this.length;++c<s;)for(var l=this[c],f=-1,h=l.length;++f<h;)if(r=l[f]){i=r.__transition__[o],e=n.call(r,r.__data__,f,c),a.push(t=[]);for(var g=-1,p=e.length;++g<p;)(u=e[g])&&jo(u,g,o,i),t.push(u)}return Do(a,o)},Ns.filter=function(n){var t,e,r,u=[];"function"!=typeof n&&(n=q(n));for(var i=0,o=this.length;o>i;i++){u.push(t=[]);for(var e=this[i],a=0,c=e.length;c>a;a++)(r=e[a])&&n.call(r,r.__data__,a,i)&&t.push(r)}return Do(u,this.id)},Ns.tween=function(n,t){var e=this.id;return arguments.length<2?this.node().__transition__[e].tween.get(n):R(this,null==t?function(t){t.__transition__[e].tween.remove(n)}:function(r){r.__transition__[e].tween.set(n,t)})},Ns.attr=function(n,t){function e(){this.removeAttribute(a)}function r(){this.removeAttributeNS(a.space,a.local)}function u(n){return null==n?e:(n+="",function(){var t,e=this.getAttribute(a);return e!==n&&(t=o(e,n),function(n){this.setAttribute(a,t(n))})})}function i(n){return null==n?r:(n+="",function(){var t,e=this.getAttributeNS(a.space,a.local);return e!==n&&(t=o(e,n),function(n){this.setAttributeNS(a.space,a.local,t(n))})})}if(arguments.length<2){for(t in n)this.attr(t,n[t]);return this}var o="transform"==n?Ru:fu,a=Xo.ns.qualify(n);return Po(this,"attr."+n,t,a.local?i:u)},Ns.attrTween=function(n,t){function e(n,e){var r=t.call(this,n,e,this.getAttribute(u));return r&&function(n){this.setAttribute(u,r(n))}}function r(n,e){var r=t.call(this,n,e,this.getAttributeNS(u.space,u.local));return r&&function(n){this.setAttributeNS(u.space,u.local,r(n))}}var u=Xo.ns.qualify(n);return this.tween("attr."+n,u.local?r:e)},Ns.style=function(n,t,e){function r(){this.style.removeProperty(n)}function u(t){return null==t?r:(t+="",function(){var r,u=Go.getComputedStyle(this,null).getPropertyValue(n);return u!==t&&(r=fu(u,t),function(t){this.style.setProperty(n,r(t),e)})})}var i=arguments.length;if(3>i){if("string"!=typeof n){2>i&&(t="");for(e in n)this.style(e,n[e],t);return this}e=""}return Po(this,"style."+n,t,u)},Ns.styleTween=function(n,t,e){function r(r,u){var i=t.call(this,r,u,Go.getComputedStyle(this,null).getPropertyValue(n));return i&&function(t){this.style.setProperty(n,i(t),e)}}return arguments.length<3&&(e=""),this.tween("style."+n,r)},Ns.text=function(n){return Po(this,"text",n,Uo)},Ns.remove=function(){return this.each("end.transition",function(){var n;this.__transition__.count<2&&(n=this.parentNode)&&n.removeChild(this)})},Ns.ease=function(n){var t=this.id;return arguments.length<1?this.node().__transition__[t].ease:("function"!=typeof n&&(n=Xo.ease.apply(Xo,arguments)),R(this,function(e){e.__transition__[t].ease=n}))},Ns.delay=function(n){var t=this.id;return R(this,"function"==typeof n?function(e,r,u){e.__transition__[t].delay=+n.call(e,e.__data__,r,u)}:(n=+n,function(e){e.__transition__[t].delay=n}))},Ns.duration=function(n){var t=this.id;return R(this,"function"==typeof n?function(e,r,u){e.__transition__[t].duration=Math.max(1,n.call(e,e.__data__,r,u))}:(n=Math.max(1,n),function(e){e.__transition__[t].duration=n}))},Ns.each=function(n,t){var e=this.id;if(arguments.length<2){var r=Es,u=ks;ks=e,R(this,function(t,r,u){Es=t.__transition__[e],n.call(t,t.__data__,r,u)}),Es=r,ks=u}else R(this,function(r){var u=r.__transition__[e];(u.event||(u.event=Xo.dispatch("start","end"))).on(n,t)});return this},Ns.transition=function(){for(var n,t,e,r,u=this.id,i=++Ls,o=[],a=0,c=this.length;c>a;a++){o.push(n=[]);for(var t=this[a],s=0,l=t.length;l>s;s++)(e=t[s])&&(r=Object.create(e.__transition__[u]),r.delay+=r.duration,jo(e,s,i,r)),n.push(e)}return Do(o,i)},Xo.svg.axis=function(){function n(n){n.each(function(){var n,s=Xo.select(this),l=this.__chart__||e,f=this.__chart__=e.copy(),h=null==c?f.ticks?f.ticks.apply(f,a):f.domain():c,g=null==t?f.tickFormat?f.tickFormat.apply(f,a):bt:t,p=s.selectAll(".tick").data(h,f),v=p.enter().insert("g",".domain").attr("class","tick").style("opacity",Aa),d=Xo.transition(p.exit()).style("opacity",Aa).remove(),m=Xo.transition(p).style("opacity",1),y=Ri(f),x=s.selectAll(".domain").data([0]),M=(x.enter().append("path").attr("class","domain"),Xo.transition(x));v.append("line"),v.append("text");var _=v.select("line"),b=m.select("line"),w=p.select("text").text(g),S=v.select("text"),k=m.select("text");switch(r){case"bottom":n=Ho,_.attr("y2",u),S.attr("y",Math.max(u,0)+o),b.attr("x2",0).attr("y2",u),k.attr("x",0).attr("y",Math.max(u,0)+o),w.attr("dy",".71em").style("text-anchor","middle"),M.attr("d","M"+y[0]+","+i+"V0H"+y[1]+"V"+i);break;case"top":n=Ho,_.attr("y2",-u),S.attr("y",-(Math.max(u,0)+o)),b.attr("x2",0).attr("y2",-u),k.attr("x",0).attr("y",-(Math.max(u,0)+o)),w.attr("dy","0em").style("text-anchor","middle"),M.attr("d","M"+y[0]+","+-i+"V0H"+y[1]+"V"+-i);break;case"left":n=Fo,_.attr("x2",-u),S.attr("x",-(Math.max(u,0)+o)),b.attr("x2",-u).attr("y2",0),k.attr("x",-(Math.max(u,0)+o)).attr("y",0),w.attr("dy",".32em").style("text-anchor","end"),M.attr("d","M"+-i+","+y[0]+"H0V"+y[1]+"H"+-i);break;case"right":n=Fo,_.attr("x2",u),S.attr("x",Math.max(u,0)+o),b.attr("x2",u).attr("y2",0),k.attr("x",Math.max(u,0)+o).attr("y",0),w.attr("dy",".32em").style("text-anchor","start"),M.attr("d","M"+i+","+y[0]+"H0V"+y[1]+"H"+i)}if(f.rangeBand){var E=f,A=E.rangeBand()/2;l=f=function(n){return E(n)+A}}else l.rangeBand?l=f:d.call(n,f);v.call(n,l),m.call(n,f)})}var t,e=Xo.scale.linear(),r=Ts,u=6,i=6,o=3,a=[10],c=null;return n.scale=function(t){return arguments.length?(e=t,n):e},n.orient=function(t){return arguments.length?(r=t in qs?t+"":Ts,n):r},n.ticks=function(){return arguments.length?(a=arguments,n):a},n.tickValues=function(t){return arguments.length?(c=t,n):c},n.tickFormat=function(e){return arguments.length?(t=e,n):t},n.tickSize=function(t){var e=arguments.length;return e?(u=+t,i=+arguments[e-1],n):u},n.innerTickSize=function(t){return arguments.length?(u=+t,n):u},n.outerTickSize=function(t){return arguments.length?(i=+t,n):i},n.tickPadding=function(t){return arguments.length?(o=+t,n):o},n.tickSubdivide=function(){return arguments.length&&n},n};var Ts="bottom",qs={top:1,right:1,bottom:1,left:1};Xo.svg.brush=function(){function n(i){i.each(function(){var i=Xo.select(this).style("pointer-events","all").style("-webkit-tap-highlight-color","rgba(0,0,0,0)").on("mousedown.brush",u).on("touchstart.brush",u),o=i.selectAll(".background").data([0]);o.enter().append("rect").attr("class","background").style("visibility","hidden").style("cursor","crosshair"),i.selectAll(".extent").data([0]).enter().append("rect").attr("class","extent").style("cursor","move");var a=i.selectAll(".resize").data(p,bt);a.exit().remove(),a.enter().append("g").attr("class",function(n){return"resize "+n}).style("cursor",function(n){return zs[n]}).append("rect").attr("x",function(n){return/[ew]$/.test(n)?-3:null}).attr("y",function(n){return/^[ns]/.test(n)?-3:null}).attr("width",6).attr("height",6).style("visibility","hidden"),a.style("display",n.empty()?"none":null);var l,f=Xo.transition(i),h=Xo.transition(o);c&&(l=Ri(c),h.attr("x",l[0]).attr("width",l[1]-l[0]),e(f)),s&&(l=Ri(s),h.attr("y",l[0]).attr("height",l[1]-l[0]),r(f)),t(f)})}function t(n){n.selectAll(".resize").attr("transform",function(n){return"translate("+l[+/e$/.test(n)]+","+f[+/^s/.test(n)]+")"})}function e(n){n.select(".extent").attr("x",l[0]),n.selectAll(".extent,.n>rect,.s>rect").attr("width",l[1]-l[0])}function r(n){n.select(".extent").attr("y",f[0]),n.selectAll(".extent,.e>rect,.w>rect").attr("height",f[1]-f[0])}function u(){function u(){32==Xo.event.keyCode&&(C||(x=null,L[0]-=l[1],L[1]-=f[1],C=2),d())}function p(){32==Xo.event.keyCode&&2==C&&(L[0]+=l[1],L[1]+=f[1],C=0,d())}function v(){var n=Xo.mouse(_),u=!1;M&&(n[0]+=M[0],n[1]+=M[1]),C||(Xo.event.altKey?(x||(x=[(l[0]+l[1])/2,(f[0]+f[1])/2]),L[0]=l[+(n[0]<x[0])],L[1]=f[+(n[1]<x[1])]):x=null),E&&m(n,c,0)&&(e(S),u=!0),A&&m(n,s,1)&&(r(S),u=!0),u&&(t(S),w({type:"brush",mode:C?"move":"resize"}))}function m(n,t,e){var r,u,a=Ri(t),c=a[0],s=a[1],p=L[e],v=e?f:l,d=v[1]-v[0];return C&&(c-=p,s-=d+p),r=(e?g:h)?Math.max(c,Math.min(s,n[e])):n[e],C?u=(r+=p)+d:(x&&(p=Math.max(c,Math.min(s,2*x[e]-r))),r>p?(u=r,r=p):u=p),v[0]!=r||v[1]!=u?(e?o=null:i=null,v[0]=r,v[1]=u,!0):void 0}function y(){v(),S.style("pointer-events","all").selectAll(".resize").style("display",n.empty()?"none":null),Xo.select("body").style("cursor",null),T.on("mousemove.brush",null).on("mouseup.brush",null).on("touchmove.brush",null).on("touchend.brush",null).on("keydown.brush",null).on("keyup.brush",null),N(),w({type:"brushend"})}var x,M,_=this,b=Xo.select(Xo.event.target),w=a.of(_,arguments),S=Xo.select(_),k=b.datum(),E=!/^(n|s)$/.test(k)&&c,A=!/^(e|w)$/.test(k)&&s,C=b.classed("extent"),N=O(),L=Xo.mouse(_),T=Xo.select(Go).on("keydown.brush",u).on("keyup.brush",p);if(Xo.event.changedTouches?T.on("touchmove.brush",v).on("touchend.brush",y):T.on("mousemove.brush",v).on("mouseup.brush",y),S.interrupt().selectAll("*").interrupt(),C)L[0]=l[0]-L[0],L[1]=f[0]-L[1];else if(k){var q=+/w$/.test(k),z=+/^n/.test(k);M=[l[1-q]-L[0],f[1-z]-L[1]],L[0]=l[q],L[1]=f[z]}else Xo.event.altKey&&(x=L.slice());S.style("pointer-events","none").selectAll(".resize").style("display",null),Xo.select("body").style("cursor",b.style("cursor")),w({type:"brushstart"}),v()}var i,o,a=y(n,"brushstart","brush","brushend"),c=null,s=null,l=[0,0],f=[0,0],h=!0,g=!0,p=Rs[0];return n.event=function(n){n.each(function(){var n=a.of(this,arguments),t={x:l,y:f,i:i,j:o},e=this.__chart__||t;this.__chart__=t,ks?Xo.select(this).transition().each("start.brush",function(){i=e.i,o=e.j,l=e.x,f=e.y,n({type:"brushstart"})}).tween("brush:brush",function(){var e=hu(l,t.x),r=hu(f,t.y);return i=o=null,function(u){l=t.x=e(u),f=t.y=r(u),n({type:"brush",mode:"resize"})}}).each("end.brush",function(){i=t.i,o=t.j,n({type:"brush",mode:"resize"}),n({type:"brushend"})}):(n({type:"brushstart"}),n({type:"brush",mode:"resize"}),n({type:"brushend"}))})},n.x=function(t){return arguments.length?(c=t,p=Rs[!c<<1|!s],n):c},n.y=function(t){return arguments.length?(s=t,p=Rs[!c<<1|!s],n):s},n.clamp=function(t){return arguments.length?(c&&s?(h=!!t[0],g=!!t[1]):c?h=!!t:s&&(g=!!t),n):c&&s?[h,g]:c?h:s?g:null},n.extent=function(t){var e,r,u,a,h;return arguments.length?(c&&(e=t[0],r=t[1],s&&(e=e[0],r=r[0]),i=[e,r],c.invert&&(e=c(e),r=c(r)),e>r&&(h=e,e=r,r=h),(e!=l[0]||r!=l[1])&&(l=[e,r])),s&&(u=t[0],a=t[1],c&&(u=u[1],a=a[1]),o=[u,a],s.invert&&(u=s(u),a=s(a)),u>a&&(h=u,u=a,a=h),(u!=f[0]||a!=f[1])&&(f=[u,a])),n):(c&&(i?(e=i[0],r=i[1]):(e=l[0],r=l[1],c.invert&&(e=c.invert(e),r=c.invert(r)),e>r&&(h=e,e=r,r=h))),s&&(o?(u=o[0],a=o[1]):(u=f[0],a=f[1],s.invert&&(u=s.invert(u),a=s.invert(a)),u>a&&(h=u,u=a,a=h))),c&&s?[[e,u],[r,a]]:c?[e,r]:s&&[u,a])},n.clear=function(){return n.empty()||(l=[0,0],f=[0,0],i=o=null),n},n.empty=function(){return!!c&&l[0]==l[1]||!!s&&f[0]==f[1]},Xo.rebind(n,a,"on")};var zs={n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},Rs=[["n","e","s","w","nw","ne","se","sw"],["e","w"],["n","s"],[]],Ds=tc.format=ac.timeFormat,Ps=Ds.utc,Us=Ps("%Y-%m-%dT%H:%M:%S.%LZ");Ds.iso=Date.prototype.toISOString&&+new Date("2000-01-01T00:00:00.000Z")?Oo:Us,Oo.parse=function(n){var t=new Date(n);return isNaN(t)?null:t},Oo.toString=Us.toString,tc.second=Rt(function(n){return new ec(1e3*Math.floor(n/1e3))},function(n,t){n.setTime(n.getTime()+1e3*Math.floor(t))},function(n){return n.getSeconds()}),tc.seconds=tc.second.range,tc.seconds.utc=tc.second.utc.range,tc.minute=Rt(function(n){return new ec(6e4*Math.floor(n/6e4))},function(n,t){n.setTime(n.getTime()+6e4*Math.floor(t))},function(n){return n.getMinutes()}),tc.minutes=tc.minute.range,tc.minutes.utc=tc.minute.utc.range,tc.hour=Rt(function(n){var t=n.getTimezoneOffset()/60;return new ec(36e5*(Math.floor(n/36e5-t)+t))},function(n,t){n.setTime(n.getTime()+36e5*Math.floor(t))},function(n){return n.getHours()}),tc.hours=tc.hour.range,tc.hours.utc=tc.hour.utc.range,tc.month=Rt(function(n){return n=tc.day(n),n.setDate(1),n},function(n,t){n.setMonth(n.getMonth()+t)},function(n){return n.getMonth()}),tc.months=tc.month.range,tc.months.utc=tc.month.utc.range;var js=[1e3,5e3,15e3,3e4,6e4,3e5,9e5,18e5,36e5,108e5,216e5,432e5,864e5,1728e5,6048e5,2592e6,7776e6,31536e6],Hs=[[tc.second,1],[tc.second,5],[tc.second,15],[tc.second,30],[tc.minute,1],[tc.minute,5],[tc.minute,15],[tc.minute,30],[tc.hour,1],[tc.hour,3],[tc.hour,6],[tc.hour,12],[tc.day,1],[tc.day,2],[tc.week,1],[tc.month,1],[tc.month,3],[tc.year,1]],Fs=Ds.multi([[".%L",function(n){return n.getMilliseconds()}],[":%S",function(n){return n.getSeconds()}],["%I:%M",function(n){return n.getMinutes()}],["%I %p",function(n){return n.getHours()}],["%a %d",function(n){return n.getDay()&&1!=n.getDate()}],["%b %d",function(n){return 1!=n.getDate()}],["%B",function(n){return n.getMonth()}],["%Y",be]]),Os={range:function(n,t,e){return Xo.range(Math.ceil(n/e)*e,+t,e).map(Io)},floor:bt,ceil:bt};Hs.year=tc.year,tc.scale=function(){return Yo(Xo.scale.linear(),Hs,Fs)};var Ys=Hs.map(function(n){return[n[0].utc,n[1]]}),Is=Ps.multi([[".%L",function(n){return n.getUTCMilliseconds()}],[":%S",function(n){return n.getUTCSeconds()}],["%I:%M",function(n){return n.getUTCMinutes()}],["%I %p",function(n){return n.getUTCHours()}],["%a %d",function(n){return n.getUTCDay()&&1!=n.getUTCDate()}],["%b %d",function(n){return 1!=n.getUTCDate()}],["%B",function(n){return n.getUTCMonth()}],["%Y",be]]);Ys.year=tc.year.utc,tc.scale.utc=function(){return Yo(Xo.scale.linear(),Ys,Is)},Xo.text=wt(function(n){return n.responseText}),Xo.json=function(n,t){return St(n,"application/json",Zo,t)},Xo.html=function(n,t){return St(n,"text/html",Vo,t)},Xo.xml=wt(function(n){return n.responseXML}),"function"==typeof define&&define.amd?define(Xo):"object"==typeof module&&module.exports?module.exports=Xo:this.d3=Xo}();'use strict';(function(window){window.define=undefined;}).call(this,this);'use strict';tr.exportTo('tr.ui.b',function(){var DataSeriesEnableChangeEventType='data-series-enabled-change';var THIS_DOC=document.currentScript.ownerDocument;var svgNS='http://www.w3.org/2000/svg';var ColorScheme=tr.b.ColorScheme;function getColorOfKey(key,selected){var id=ColorScheme.getColorIdForGeneralPurposeString(key);if(selected){id+=ColorScheme.properties.brightenedOffsets[0];}
+return ColorScheme.colorsAsStrings[id];}
+function getSVGTextSize(parentNode,text,opt_callback,opt_this){var textNode=document.createElementNS('http://www.w3.org/2000/svg','text');textNode.setAttributeNS(null,'x',0);textNode.setAttributeNS(null,'y',0);textNode.setAttributeNS(null,'fill','black');textNode.appendChild(document.createTextNode(text));parentNode.appendChild(textNode);if(opt_callback){opt_callback.call(opt_this||parentNode,textNode);}
+var width=textNode.getComputedTextLength();var height=textNode.getBBox().height;parentNode.removeChild(textNode);return{width,height};}
+function DataSeries(key){this.key_=key;this.target_=undefined;this.title_='';this.optional_=false;this.enabled_=true;this.color_=getColorOfKey(key,false);this.highlightedColor_=getColorOfKey(key,true);}
+DataSeries.prototype={get key(){return this.key_;},get title(){return this.title_;},set title(t){this.title_=t;},get color(){return this.color_;},set color(c){this.color_=c;},get highlightedColor(){return this.highlightedColor_;},set highlightedColor(c){this.highlightedColor_=c;},get optional(){return this.optional_;},set optional(optional){this.optional_=optional;},get enabled(){return this.enabled_;},set enabled(enabled){if(!this.optional&&!enabled){this.optional=true;}
+this.enabled_=enabled;},get target(){return this.target_;},set target(t){this.target_=t;}};var ChartBase=tr.ui.b.define('svg',undefined,svgNS);ChartBase.prototype={__proto__:HTMLUnknownElement.prototype,getDataSeries(key){if(!this.seriesByKey_.has(key)){this.seriesByKey_.set(key,new DataSeries(key));}
+return this.seriesByKey_.get(key);},decorate(){Polymer.dom(this).classList.add('chart-base');this.chartTitle_=undefined;this.seriesByKey_=new Map();this.graphWidth_=undefined;this.graphHeight_=undefined;this.margin={top:0,right:0,bottom:0,left:0,};this.hideLegend_=false;var template=Polymer.dom(THIS_DOC).querySelector('#chart-base-template');var svgEl=Polymer.dom(template.content).querySelector('svg');for(var i=0;i<Polymer.dom(svgEl).children.length;i++){Polymer.dom(this).appendChild(Polymer.dom(svgEl.children[i]).cloneNode(true));}
+this.addEventListener(DataSeriesEnableChangeEventType,this.onDataSeriesEnableChange_.bind(this));},get hideLegend(){return this.hideLegend_;},set hideLegend(h){this.hideLegend_=h;this.updateContents_();},isSeriesEnabled(key){return this.getDataSeries(key).enabled;},onDataSeriesEnableChange_(event){this.getDataSeries(event.key).enabled=event.enabled;this.updateContents_();},get chartTitle(){return this.chartTitle_;},set chartTitle(chartTitle){this.chartTitle_=chartTitle;this.updateContents_();},get chartAreaElement(){return Polymer.dom(this).querySelector('#chart-area');},get graphWidth(){if(this.graphWidth_===undefined)return this.defaultGraphWidth;return this.graphWidth_;},set graphWidth(width){this.graphWidth_=width;this.updateContents_();},get defaultGraphWidth(){return 0;},get graphHeight(){if(this.graphHeight_===undefined)return this.defaultGraphHeight;return this.graphHeight_;},set graphHeight(height){this.graphHeight_=height;this.updateContents_();},get defaultGraphHeight(){return 0;},get totalWidth(){return this.margin.left+this.graphWidth+this.margin.right;},get totalHeight(){return this.margin.top+this.graphHeight+this.margin.bottom;},updateMargins_(){var legendSize=this.computeLegendSize_();this.margin.right=Math.max(this.margin.right,legendSize.width);this.margin.bottom=Math.max(this.margin.bottom,legendSize.height-this.graphHeight);if(this.chartTitle_){var titleSize=getSVGTextSize(this,this.chartTitle_,textNode=>{textNode.style.fontSize='16pt';});this.margin.top=Math.max(this.margin.top,titleSize.height+15);var horizontalOverhangPx=(titleSize.width-this.graphWidth)/2;this.margin.left=Math.max(this.margin.left,horizontalOverhangPx);this.margin.right=Math.max(this.margin.right,horizontalOverhangPx);}},computeLegendSize_(){var width=0;var height=0;if(this.hideLegend)return{width,height};for(var series of this.seriesByKey_.values()){var textSize=getSVGTextSize(this,series.key);width=Math.max(width,textSize.width+20);height+=textSize.height;}
+return{width,height};},updateDimensions_(){var thisSel=d3.select(this);thisSel.attr('width',this.totalWidth);thisSel.attr('height',this.totalHeight);d3.select(this.chartAreaElement).attr('transform','translate('+this.margin.left+', '+this.margin.top+')');},updateContents_(){this.updateMargins_();this.updateDimensions_();this.updateTitle_();this.updateLegend_();},updateTitle_(){var titleSel=d3.select(this.chartAreaElement).select('#title');if(!this.chartTitle_){titleSel.style('display','none');return;}
+titleSel.attr('transform','translate('+this.graphWidth*0.5+',-15)').style('display',undefined).style('text-anchor','middle').style('font-size','16pt').attr('class','title').attr('width',this.graphWidth).text(this.chartTitle_);},updateLegend_(){var chartAreaSel=d3.select(this.chartAreaElement);chartAreaSel.selectAll('.legend').remove();if(this.hideLegend)return;var series=[...this.seriesByKey_.values()].reverse();var legendEntriesSel=chartAreaSel.selectAll('.legend').data(series);legendEntriesSel.enter().append('foreignObject').attr('class','legend').attr('x',this.graphWidth+2).attr('width',this.margin.right).attr('height',18).attr('transform',(series,i)=>'translate(0,'+i*18+')').append('xhtml:body').style('margin',0).append('tr-ui-b-chart-legend-key').property('color',series=>((this.currentHighlightedLegendKey===series.key)?series.highlightedColor:series.color)).property('width',this.margin.right).property('target',series=>series.target).property('title',series=>series.title).property('optional',series=>series.optional).property('enabled',series=>series.enabled).text(series=>series.key);legendEntriesSel.exit().remove();},get highlightedLegendKey(){return this.highlightedLegendKey_;},set highlightedLegendKey(highlightedLegendKey){this.highlightedLegendKey_=highlightedLegendKey;this.updateHighlight_();},get currentHighlightedLegendKey(){if(this.tempHighlightedLegendKey_){return this.tempHighlightedLegendKey_;}
+return this.highlightedLegendKey_;},pushTempHighlightedLegendKey(key){if(this.tempHighlightedLegendKey_){throw new Error('push cannot nest');}
+this.tempHighlightedLegendKey_=key;this.updateHighlight_();},popTempHighlightedLegendKey(key){if(this.tempHighlightedLegendKey_!==key){throw new Error('pop cannot happen');}
+this.tempHighlightedLegendKey_=undefined;this.updateHighlight_();},updateHighlight_(){const chartAreaSel=d3.select(this.chartAreaElement);const legendEntriesSel=chartAreaSel.selectAll('.legend');const getDataSeries=chart.getDataSeries.bind(chart);const currentHighlightedLegendKey=chart.currentHighlightedLegendKey;legendEntriesSel.each(function(key){var dataSeries=getDataSeries(key);if(key===currentHighlightedLegendKey){this.style.fill=dataSeries.highlightedColor;this.style.fontWeight='bold';}else{this.style.fill=dataSeries.color;this.style.fontWeight='';}});}};return{ChartBase,DataSeriesEnableChangeEventType,getColorOfKey,getSVGTextSize,};});'use strict';tr.exportTo('tr.ui.b',function(){var D3_Y_AXIS_WIDTH_PX=9;var D3_X_AXIS_HEIGHT_PX=23;function sanitizePower(x,defaultValue){if(!isNaN(x)&&isFinite(x)&&(x!==0))return x;return defaultValue;}
+var ChartBase2D=tr.ui.b.define('chart-base-2d',tr.ui.b.ChartBase);ChartBase2D.prototype={__proto__:tr.ui.b.ChartBase.prototype,decorate(){super.decorate();Polymer.dom(this).classList.add('chart-base-2d');this.xScale_=d3.scale.linear();this.yScale_=d3.scale.linear();this.isYLogScale_=false;this.yLogScaleMin_=undefined;this.autoDataRange_=new tr.b.math.Range();this.overrideDataRange_=undefined;this.hideXAxis_=false;this.hideYAxis_=false;this.data_=[];this.xAxisLabel_='';this.yAxisLabel_='';this.textHeightPx_=0;d3.select(this.chartAreaElement).append('g').attr('id','brushes');d3.select(this.chartAreaElement).append('g').attr('id','series');this.addEventListener('mousedown',this.onMouseDown_.bind(this));},get xAxisLabel(){return this.xAxisLabel_;},set xAxisLabel(label){this.xAxisLabel_=label;},get yAxisLabel(){return this.yAxisLabel_;},set yAxisLabel(label){this.yAxisLabel_=label;},get hideXAxis(){return this.hideXAxis_;},set hideXAxis(h){this.hideXAxis_=h;this.updateContents_();},get hideYAxis(){return this.hideYAxis_;},set hideYAxis(h){this.hideYAxis_=h;this.updateContents_();},get data(){return this.data_;},set data(data){if(data===undefined){throw new Error('data must be an Array');}
+this.data_=data;this.updateSeriesKeys_();this.updateDataRange_();this.updateContents_();},set isYLogScale(logScale){if(logScale){this.yScale_=d3.scale.log(10);}else{this.yScale_=d3.scale.linear();}
+this.isYLogScale_=logScale;},getYScaleMin_(){return this.isYLogScale_?this.yLogScaleMin_:0;},getYScaleDomain_(minValue,maxValue){if(this.overrideDataRange_!==undefined){return[this.dataRange.min,this.dataRange.max];}
+if(this.isYLogScale_){return[this.getYScaleMin_(),maxValue];}
+return[Math.min(minValue,this.getYScaleMin_()),maxValue];},getSampleWidth_(data,index,leftSide){var leftIndex;var rightIndex;if(leftSide){leftIndex=Math.max(index-1,0);rightIndex=index;}else{leftIndex=index;rightIndex=Math.min(index+1,data.length-1);}
 var leftWidth=this.getXForDatum_(data[index],index)-
 this.getXForDatum_(data[leftIndex],leftIndex);var rightWidth=this.getXForDatum_(data[rightIndex],rightIndex)-
-this.getXForDatum_(data[index],index);return leftWidth*0.5+rightWidth*0.5;},updateSeriesKeys_:function(){this.data_.forEach(function(datum){Object.keys(datum).forEach(function(key){if(this.isDatumFieldSeries_(key))
-this.getDataSeries(key);},this);},this);},isDatumFieldSeries_:function(fieldName){throw new Error('Not implemented');},getXForDatum_:function(datum,index){throw new Error('Not implemented');},updateScales_:function(){if(this.data_.length===0)
-return;var width=this.chartAreaSize.width;var height=this.chartAreaSize.height;this.xScale_.range([0,width]);this.xScale_.domain(d3.extent(this.data_,this.getXForDatum_.bind(this)));var yRange=new tr.b.Range();for(var i=0;i<this.data_.length;i++){for(var key in this.data_[i]){if(!isNaN(Math.max(this.data_[i][key])))
-yRange.addValue(this.data_[i][key]);}}
-this.yScale_.range([height,0]);this.yScale_.domain([yRange.min,yRange.max]);},updateBrushContents_:function(brushSel){brushSel.selectAll('*').remove();},updateXAxis_:function(xAxis){xAxis.selectAll('*').remove();xAxis[0][0].style.opacity=0;if(this.hideXAxis)
-return;this.drawXAxis_(xAxis);},drawXAxis_:function(xAxis){xAxis.attr('transform','translate(0,'+this.chartAreaSize.height+')').call(d3.svg.axis().scale(this.xScale_).orient('bottom'));tr.b.requestAnimationFrame(this.drawXAxisTicks_.bind(this,xAxis));},drawXAxisTicks_:function(xAxis){var previousRight=undefined;xAxis.selectAll('.tick')[0].forEach(function(tick){var currentLeft=tick.transform.baseVal[0].matrix.e;if((previousRight===undefined)||(currentLeft>(previousRight+3))){var currentWidth=tick.getBBox().width;previousRight=currentLeft+currentWidth;}else{tick.style.opacity=0;}});xAxis[0][0].style.opacity=1;},updateDataRange_:function(){var dataBySeriesKey=this.getDataBySeriesKey_();this.dataRange_.reset();tr.b.iterItems(dataBySeriesKey,function(series,values){for(var i=0;i<values.length;i++){this.dataRange_.addValue(values[i][series]);}},this);this.yLogScaleMin_=undefined;if(this.dataRange_.min!==undefined){var minValue=this.dataRange_.min;if(minValue==0)
-minValue=1;var onePowerLess=Math.floor(Math.log(minValue)/Math.log(10))-1;this.yLogScaleMin_=Math.pow(10,onePowerLess);}},updateYAxis_:function(yAxis){yAxis.selectAll('*').remove();yAxis[0][0].style.opacity=0;if(this.hideYAxis)
-return;this.drawYAxis_(yAxis);},drawYAxis_:function(yAxis){var axisModifier=d3.svg.axis().scale(this.yScale_).orient('left');if(this.isYLogScale_){if(this.yLogScaleMin_===undefined)
-return;var minValue=this.dataRange_.min;if(minValue==0)
-minValue=1;var largestPower=Math.ceil(Math.log(this.dataRange_.max)/Math.log(10))+1;var smallestPower=Math.floor(Math.log(minValue)/Math.log(10));var tickValues=[];for(var i=smallestPower;i<largestPower;i++){tickValues.push(Math.pow(10,i));}
-axisModifier=axisModifier.tickValues(tickValues).tickFormat(function(d){return d;});}
-yAxis.call(axisModifier);tr.b.requestAnimationFrame(this.drawYAxisTicks_.bind(this,yAxis));},drawYAxisTicks_:function(yAxis){var previousTop=undefined;var leftMargin=0;yAxis.selectAll('.tick')[0].forEach(function(tick){var bbox=tick.getBBox();leftMargin=Math.max(leftMargin,bbox.width);var currentTop=tick.transform.baseVal[0].matrix.f;var currentBottom=currentTop+bbox.height;if((previousTop===undefined)||(previousTop>(currentBottom+3))){previousTop=currentTop;}else{tick.style.opacity=0;}});leftMargin=parseInt(Math.ceil(leftMargin));if(leftMargin>this.margin.left){this.margin.left=leftMargin;this.updateContents_();}else{yAxis[0][0].style.opacity=1;}},updateContents_:function(){ChartBase.prototype.updateContents_.call(this);var chartAreaSel=d3.select(this.chartAreaElement);this.updateXAxis_(chartAreaSel.select('.x.axis'));this.updateYAxis_(chartAreaSel.select('.y.axis'));this.updateBrushContents_(chartAreaSel.select('#brushes'));this.updateDataContents_(chartAreaSel.select('#series'));},updateDataContents_:function(seriesSel){throw new Error('Not implemented');},getDataBySeriesKey_:function(){var dataBySeriesKey={};for(var[key,series]of this.seriesByKey_){dataBySeriesKey[key]=[];}
-this.data_.forEach(function(multiSeriesDatum,index){var x=this.getXForDatum_(multiSeriesDatum,index);d3.keys(multiSeriesDatum).forEach(function(seriesKey){if(seriesKey==='x')
-return;if(multiSeriesDatum[seriesKey]===undefined)
-return;if(!this.isDatumFieldSeries_(seriesKey))
-return;var singleSeriesDatum={x:x};singleSeriesDatum[seriesKey]=multiSeriesDatum[seriesKey];dataBySeriesKey[seriesKey].push(singleSeriesDatum);},this);},this);return dataBySeriesKey;},getChartPointAtClientPoint_:function(clientPoint){var rect=this.getBoundingClientRect();return{x:clientPoint.x-rect.left-this.margin.left,y:clientPoint.y-rect.top-this.margin.top};},getDataPointAtChartPoint_:function(chartPoint){return{x:tr.b.clamp(this.xScale_.invert(chartPoint.x),this.xScale_.domain()[0],this.xScale_.domain()[1]),y:tr.b.clamp(this.yScale_.invert(chartPoint.y),this.yScale_.domain()[0],this.yScale_.domain()[1])};},getDataPointAtClientPoint_:function(clientX,clientY){var chartPoint=this.getChartPointAtClientPoint_({x:clientX,y:clientY});return this.getDataPointAtChartPoint_(chartPoint);},prepareDataEvent_:function(mouseEvent,dataEvent){var dataPoint=this.getDataPointAtClientPoint_(mouseEvent.clientX,mouseEvent.clientY);dataEvent.x=dataPoint.x;dataEvent.y=dataPoint.y;},onMouseDown_:function(mouseEvent){tr.ui.b.trackMouseMovesUntilMouseUp(this.onMouseMove_.bind(this,mouseEvent.button),this.onMouseUp_.bind(this,mouseEvent.button));mouseEvent.preventDefault();mouseEvent.stopPropagation();var dataEvent=new tr.b.Event('item-mousedown');dataEvent.button=mouseEvent.button;Polymer.dom(this).classList.add('updating-brushing-state');this.prepareDataEvent_(mouseEvent,dataEvent);this.dispatchEvent(dataEvent);},onMouseMove_:function(button,mouseEvent){if(mouseEvent.buttons!==undefined){mouseEvent.preventDefault();mouseEvent.stopPropagation();}
-var dataEvent=new tr.b.Event('item-mousemove');dataEvent.button=button;this.prepareDataEvent_(mouseEvent,dataEvent);this.dispatchEvent(dataEvent);},onMouseUp_:function(button,mouseEvent){mouseEvent.preventDefault();mouseEvent.stopPropagation();var dataEvent=new tr.b.Event('item-mouseup');dataEvent.button=button;this.prepareDataEvent_(mouseEvent,dataEvent);this.dispatchEvent(dataEvent);Polymer.dom(this).classList.remove('updating-brushing-state');}};return{ChartBase2D:ChartBase2D};});'use strict';tr.exportTo('tr.ui.b',function(){var ChartBase2D=tr.ui.b.ChartBase2D;var ChartBase2DBrushX=tr.ui.b.define('chart-base-2d-brush-1d',ChartBase2D);ChartBase2DBrushX.prototype={__proto__:ChartBase2D.prototype,decorate:function(){ChartBase2D.prototype.decorate.call(this);this.brushedRange_=new tr.b.Range();},set brushedRange(range){this.brushedRange_.reset();this.brushedRange_.addRange(range);this.updateContents_();},computeBrushRangeFromIndices:function(indexA,indexB){indexA=tr.b.clamp(indexA,0,this.data_.length-1);indexB=tr.b.clamp(indexB,0,this.data_.length-1);var leftIndex=Math.min(indexA,indexB);var rightIndex=Math.max(indexA,indexB);var r=new tr.b.Range();r.addValue(this.getXForDatum_(this.data_[leftIndex],leftIndex)-
-this.getSampleWidth_(this.data_,leftIndex,true));r.addValue(this.getXForDatum_(this.data_[rightIndex],rightIndex)+
-this.getSampleWidth_(this.data_,rightIndex,false));return r;},getDataIndex_:function(dataX){if(!this.data_)
-return undefined;var bisect=d3.bisector(this.getXForDatum_.bind(this)).right;return bisect(this.data_,dataX)-1;},prepareDataEvent_:function(mouseEvent,dataEvent){ChartBase2D.prototype.prepareDataEvent_.call(this,mouseEvent,dataEvent);dataEvent.index=this.getDataIndex_(dataEvent.x);if(dataEvent.index!==undefined)
-dataEvent.data=this.data_[dataEvent.index];},updateBrushContents_:function(brushSel){brushSel.selectAll('*').remove();var brushes=this.brushedRange_.isEmpty?[]:[this.brushedRange_];var brushRectsSel=brushSel.selectAll('rect').data(brushes);brushRectsSel.enter().append('rect');brushRectsSel.exit().remove();this.drawBrush_(brushRectsSel);},drawBrush_:function(brushRectsSel){brushRectsSel.attr('x',function(d){return this.xScale_(d.min);}.bind(this)).attr('y',0).attr('width',function(d){return this.xScale_(d.max)-this.xScale_(d.min);}.bind(this)).attr('height',this.chartAreaSize.height);}};return{ChartBase2DBrushX:ChartBase2DBrushX};});'use strict';tr.exportTo('tr.ui.b',function(){var ChartBase2DBrushX=tr.ui.b.ChartBase2DBrushX;var LineChart=tr.ui.b.define('line-chart',ChartBase2DBrushX);LineChart.prototype={__proto__:ChartBase2DBrushX.prototype,decorate:function(){ChartBase2DBrushX.prototype.decorate.call(this);Polymer.dom(this).classList.add('line-chart');},isDatumFieldSeries_:function(fieldName){return fieldName!='x';},getXForDatum_:function(datum,index){return datum.x;},updateDataContents_:function(dataSel){dataSel.selectAll('*').remove();var dataBySeriesKey=this.getDataBySeriesKey_();var seriesKeys=[...this.seriesByKey_.keys()];var pathsSel=dataSel.selectAll('path').data(seriesKeys);pathsSel.enter().append('path').attr('class','line').style('stroke',function(key){return this.getDataSeries(key).color;}.bind(this)).attr('d',function(key){var line=d3.svg.line().x(function(d){return this.xScale_(d.x);}.bind(this)).y(function(d){return this.yScale_(d[key]);}.bind(this));return line(dataBySeriesKey[key]);}.bind(this));pathsSel.exit().remove();}};return{LineChart:LineChart};});'use strict';Polymer({is:'tr-ui-e-s-input-latency-side-panel',behaviors:[tr.ui.behaviors.SidePanel],ready:function(){this.rangeOfInterest_=new tr.b.Range();this.frametimeType_=tr.model.helpers.IMPL_FRAMETIME_TYPE;this.latencyChart_=undefined;this.frametimeChart_=undefined;this.selectedProcessId_=undefined;this.mouseDownIndex_=undefined;this.curMouseIndex_=undefined;},get model(){return this.model_;},set model(model){this.model_=model;if(this.model_){this.modelHelper_=this.model_.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);}else{this.modelHelper_=undefined;}
-this.updateToolbar_();this.updateContents_();},get frametimeType(){return this.frametimeType_;},set frametimeType(type){if(this.frametimeType_===type)
-return;this.frametimeType_=type;this.updateContents_();},get selectedProcessId(){return this.selectedProcessId_;},set selectedProcessId(process){if(this.selectedProcessId_===process)
-return;this.selectedProcessId_=process;this.updateContents_();},set selection(selection){if(this.latencyChart_===undefined)
-return;this.latencyChart_.brushedRange=selection.bounds;},setBrushedIndices:function(mouseDownIndex,curIndex){this.mouseDownIndex_=mouseDownIndex;this.curMouseIndex_=curIndex;this.updateBrushedRange_();},updateBrushedRange_:function(){if(this.latencyChart_===undefined)
-return;var r=new tr.b.Range();if(this.mouseDownIndex_===undefined){this.latencyChart_.brushedRange=r;return;}
-r=this.latencyChart_.computeBrushRangeFromIndices(this.mouseDownIndex_,this.curMouseIndex_);this.latencyChart_.brushedRange=r;var latencySlices=[];for(var thread of this.model_.getAllThreads())
-for(var event of thread.getDescendantEvents())
-if(event.title.indexOf('InputLatency:')===0)
-latencySlices.push(event);latencySlices=tr.model.helpers.getSlicesIntersectingRange(r,latencySlices);var event=new tr.model.RequestSelectionChangeEvent();event.selection=new tr.model.EventSet(latencySlices);this.latencyChart_.dispatchEvent(event);},registerMouseEventForLatencyChart_:function(){this.latencyChart_.addEventListener('item-mousedown',function(e){this.mouseDownIndex_=e.index;this.curMouseIndex_=e.index;this.updateBrushedRange_();}.bind(this));this.latencyChart_.addEventListener('item-mousemove',function(e){if(e.button==undefined)
-return;this.curMouseIndex_=e.index;this.updateBrushedRange_();}.bind(this));this.latencyChart_.addEventListener('item-mouseup',function(e){this.curMouseIndex=e.index;this.updateBrushedRange_();}.bind(this));},updateToolbar_:function(){var browserProcess=this.modelHelper_.browserProcess;var labels=[];if(browserProcess!==undefined){var label_str='Browser: '+browserProcess.pid;labels.push({label:label_str,value:browserProcess.pid});}
-tr.b.iterItems(this.modelHelper_.rendererHelpers,function(pid,rendererHelper){var rendererProcess=rendererHelper.process;var label_str='Renderer: '+rendererProcess.userFriendlyName;labels.push({label:label_str,value:rendererProcess.userFriendlyName});},this);if(labels.length===0)
-return;this.selectedProcessId_=labels[0].value;var toolbarEl=this.$.toolbar;Polymer.dom(toolbarEl).appendChild(tr.ui.b.createSelector(this,'frametimeType','inputLatencySidePanel.frametimeType',this.frametimeType_,[{label:'Main Thread Frame Times',value:tr.model.helpers.MAIN_FRAMETIME_TYPE},{label:'Impl Thread Frame Times',value:tr.model.helpers.IMPL_FRAMETIME_TYPE}]));Polymer.dom(toolbarEl).appendChild(tr.ui.b.createSelector(this,'selectedProcessId','inputLatencySidePanel.selectedProcessId',this.selectedProcessId_,labels));},get currentRangeOfInterest(){if(this.rangeOfInterest_.isEmpty)
-return this.model_.bounds;else
-return this.rangeOfInterest_;},createLatencyLineChart:function(data,title){var chart=new tr.ui.b.LineChart();var width=600;if(document.body.clientWidth!=undefined)
-width=document.body.clientWidth*0.5;chart.setSize({width:width,height:chart.height});chart.chartTitle=title;chart.data=data;return chart;},updateContents_:function(){var resultArea=this.$.result_area;this.latencyChart_=undefined;this.frametimeChart_=undefined;Polymer.dom(resultArea).textContent='';if(this.modelHelper_===undefined)
-return;var rangeOfInterest=this.currentRangeOfInterest;var chromeProcess;if(this.modelHelper_.rendererHelpers[this.selectedProcessId_])
-chromeProcess=this.modelHelper_.rendererHelpers[this.selectedProcessId_];else
-chromeProcess=this.modelHelper_.browserHelper;var frameEvents=chromeProcess.getFrameEventsInRange(this.frametimeType,rangeOfInterest);var frametimeData=tr.model.helpers.getFrametimeDataFromEvents(frameEvents);var averageFrametime=tr.b.Statistics.mean(frametimeData,function(d){return d.frametime;});var latencyEvents=this.modelHelper_.browserHelper.getLatencyEventsInRange(rangeOfInterest);var latencyData=[];latencyEvents.forEach(function(event){if(event.inputLatency===undefined)
-return;latencyData.push({x:event.start,latency:event.inputLatency/1000});});var averageLatency=tr.b.Statistics.mean(latencyData,function(d){return d.latency;});var latencySummaryText=document.createElement('div');Polymer.dom(latencySummaryText).appendChild(tr.ui.b.createSpan({textContent:'Average Latency '+averageLatency+' ms',bold:true}));Polymer.dom(resultArea).appendChild(latencySummaryText);var frametimeSummaryText=document.createElement('div');Polymer.dom(frametimeSummaryText).appendChild(tr.ui.b.createSpan({textContent:'Average Frame Time '+averageFrametime+' ms',bold:true}));Polymer.dom(resultArea).appendChild(frametimeSummaryText);if(latencyData.length!==0){this.latencyChart_=this.createLatencyLineChart(latencyData,'Latency Over Time');this.registerMouseEventForLatencyChart_();Polymer.dom(resultArea).appendChild(this.latencyChart_);}
-if(frametimeData.length!=0){this.frametimeChart_=this.createLatencyLineChart(frametimeData,'Frame Times');Polymer.dom(resultArea).appendChild(this.frametimeChart_);}},get rangeOfInterest(){return this.rangeOfInterest_;},set rangeOfInterest(rangeOfInterest){this.rangeOfInterest_=rangeOfInterest;this.updateContents_();},supportsModel:function(m){if(m==undefined){return{supported:false,reason:'Unknown tracing model'};}
+this.getXForDatum_(data[index],index);return tr.b.math.Statistics.mean([leftWidth,rightWidth]);},updateSeriesKeys_(){this.data_.forEach(function(datum){Object.keys(datum).forEach(function(key){if(this.isDatumFieldSeries_(key)){this.getDataSeries(key);}},this);},this);},isDatumFieldSeries_(fieldName){return fieldName!=='x';},getXForDatum_(datum,index){return datum.x;},updateMargins_(){this.margin.left=this.hideYAxis?0:this.yAxisWidth;this.margin.bottom=this.hideXAxis?0:this.xAxisHeight;if(this.hideXAxis&&!this.hideYAxis){this.margin.bottom=10;}
+if(this.hideYAxis&&!this.hideXAxis){this.margin.left=10;}
+this.margin.top=this.hideYAxis?0:10;if(this.yAxisLabel){this.margin.top+=this.textHeightPx_;}
+if(this.xAxisLabel){this.margin.right=Math.max(this.margin.right,16+tr.ui.b.getSVGTextSize(this,this.xAxisLabel).width);}
+super.updateMargins_();},get xAxisHeight(){return D3_X_AXIS_HEIGHT_PX;},computeScaleTickWidth_(scale){if(this.data.length===0)return 0;var tickValues=scale.ticks();var format=scale.tickFormat();if(this.isYLogScale_){var enclosingPowers=this.dataRange.enclosingPowers();tickValues=[sanitizePower(enclosingPowers.min,1),sanitizePower(enclosingPowers.max,10),];format=v=>v.toString();}
+return D3_Y_AXIS_WIDTH_PX+Math.max(tr.ui.b.getSVGTextSize(this,format(tickValues[0])).width,tr.ui.b.getSVGTextSize(this,format(tickValues[tickValues.length-1])).width);},get yAxisWidth(){return this.computeScaleTickWidth_(this.yScale_);},updateScales_(){if(this.data_.length===0)return;this.xScale_.range([0,this.graphWidth]);this.xScale_.domain(d3.extent(this.data_,this.getXForDatum_.bind(this)));this.yScale_.range([this.graphHeight,0]);this.yScale_.domain([this.dataRange.min,this.dataRange.max]);},updateBrushContents_(brushSel){brushSel.selectAll('*').remove();},updateXAxis_(xAxis){xAxis.selectAll('*').remove();xAxis[0][0].style.opacity=0;if(this.hideXAxis)return;this.drawXAxis_(xAxis);var label=xAxis.append('text').attr('class','label');this.drawXAxisTicks_(xAxis);this.drawXAxisLabel_(label);xAxis[0][0].style.opacity=1;},drawXAxis_(xAxis){xAxis.attr('transform','translate(0,'+this.graphHeight+')').call(d3.svg.axis().scale(this.xScale_).orient('bottom'));},drawXAxisLabel_(label){label.attr('x',this.graphWidth+16).attr('y',8).text(this.xAxisLabel);},drawXAxisTicks_(xAxis){var previousRight=undefined;xAxis.selectAll('.tick')[0].forEach(function(tick){var currentLeft=tick.transform.baseVal[0].matrix.e;if((previousRight===undefined)||(currentLeft>(previousRight+3))){var currentWidth=tick.getBBox().width;previousRight=currentLeft+currentWidth;}else{tick.style.opacity=0;}});},set overrideDataRange(range){this.overrideDataRange_=range;},get dataRange(){if(this.overrideDataRange_!==undefined){return this.overrideDataRange_;}
+return this.autoDataRange_;},updateDataRange_(){if(this.overrideDataRange_!==undefined)return;var dataBySeriesKey=this.getDataBySeriesKey_();this.autoDataRange_.reset();for(var[series,values]of Object.entries(dataBySeriesKey)){for(var i=0;i<values.length;i++){this.autoDataRange_.addValue(values[i][series]);}}
+this.yLogScaleMin_=undefined;if(this.autoDataRange_.min!==undefined){var minValue=this.autoDataRange_.min;if(minValue===0){minValue=1;}
+var onePowerLess=tr.b.math.lesserPower(minValue/10);this.yLogScaleMin_=onePowerLess;}},updateYAxis_(yAxis){yAxis.selectAll('*').remove();yAxis[0][0].style.opacity=0;if(this.hideYAxis)return;this.drawYAxis_(yAxis);this.drawYAxisTicks_(yAxis);var label=yAxis.append('text').attr('class','label');this.drawYAxisLabel_(label);},drawYAxis_(yAxis){var axisModifier=d3.svg.axis().scale(this.yScale_).orient('left');if(this.isYLogScale_){if(this.yLogScaleMin_===undefined)return;var tickValues=[];var enclosingPowers=this.dataRange.enclosingPowers();var maxPower=sanitizePower(enclosingPowers.max,10);for(var power=sanitizePower(enclosingPowers.min,1);power<=maxPower;power*=10){tickValues.push(power);}
+axisModifier=axisModifier.tickValues(tickValues).tickFormat(v=>v.toString());}
+yAxis.call(axisModifier);},drawYAxisLabel_(label){var labelWidthPx=Math.ceil(tr.ui.b.getSVGTextSize(this.chartAreaElement,this.yAxisLabel).width);label.attr('x',-labelWidthPx).attr('y',-8).text(this.yAxisLabel);},drawYAxisTicks_(yAxis){var previousTop=undefined;yAxis.selectAll('.tick')[0].forEach(function(tick){var bbox=tick.getBBox();var currentTop=tick.transform.baseVal[0].matrix.f;var currentBottom=currentTop+bbox.height;if((previousTop===undefined)||(previousTop>(currentBottom+3))){previousTop=currentTop;}else{tick.style.opacity=0;}});yAxis[0][0].style.opacity=1;},updateContents_(){if(this.textHeightPx_===0){this.textHeightPx_=tr.ui.b.getSVGTextSize(this,'Ay').height;}
+this.updateScales_();super.updateContents_();var chartAreaSel=d3.select(this.chartAreaElement);this.updateXAxis_(chartAreaSel.select('.x.axis'));this.updateYAxis_(chartAreaSel.select('.y.axis'));this.updateBrushContents_(chartAreaSel.select('#brushes'));this.updateDataContents_(chartAreaSel.select('#series'));},updateDataContents_(seriesSel){throw new Error('Not implemented');},getDataBySeriesKey_(){var dataBySeriesKey={};for(var[key,series]of this.seriesByKey_){dataBySeriesKey[key]=[];}
+this.data_.forEach(function(multiSeriesDatum,index){var x=this.getXForDatum_(multiSeriesDatum,index);d3.keys(multiSeriesDatum).forEach(function(seriesKey){if(seriesKey==='x')return;if(multiSeriesDatum[seriesKey]===undefined)return;if(!this.isDatumFieldSeries_(seriesKey))return;var singleSeriesDatum={x:x};singleSeriesDatum[seriesKey]=multiSeriesDatum[seriesKey];dataBySeriesKey[seriesKey].push(singleSeriesDatum);},this);},this);return dataBySeriesKey;},getChartPointAtClientPoint_(clientPoint){var rect=this.getBoundingClientRect();return{x:clientPoint.x-rect.left-this.margin.left,y:clientPoint.y-rect.top-this.margin.top};},getDataPointAtChartPoint_(chartPoint){return{x:tr.b.math.clamp(this.xScale_.invert(chartPoint.x),this.xScale_.domain()[0],this.xScale_.domain()[1]),y:tr.b.math.clamp(this.yScale_.invert(chartPoint.y),this.yScale_.domain()[0],this.yScale_.domain()[1])};},getDataPointAtClientPoint_(clientX,clientY){var chartPoint=this.getChartPointAtClientPoint_({x:clientX,y:clientY});return this.getDataPointAtChartPoint_(chartPoint);},prepareDataEvent_(mouseEvent,dataEvent){var dataPoint=this.getDataPointAtClientPoint_(mouseEvent.clientX,mouseEvent.clientY);dataEvent.x=dataPoint.x;dataEvent.y=dataPoint.y;},onMouseDown_(mouseEvent){tr.ui.b.trackMouseMovesUntilMouseUp(this.onMouseMove_.bind(this,mouseEvent.button),this.onMouseUp_.bind(this,mouseEvent.button));mouseEvent.preventDefault();mouseEvent.stopPropagation();var dataEvent=new tr.b.Event('item-mousedown');dataEvent.button=mouseEvent.button;Polymer.dom(this).classList.add('updating-brushing-state');this.prepareDataEvent_(mouseEvent,dataEvent);this.dispatchEvent(dataEvent);},onMouseMove_(button,mouseEvent){if(mouseEvent.buttons!==undefined){mouseEvent.preventDefault();mouseEvent.stopPropagation();}
+var dataEvent=new tr.b.Event('item-mousemove');dataEvent.button=button;this.prepareDataEvent_(mouseEvent,dataEvent);this.dispatchEvent(dataEvent);},onMouseUp_(button,mouseEvent){mouseEvent.preventDefault();mouseEvent.stopPropagation();var dataEvent=new tr.b.Event('item-mouseup');dataEvent.button=button;this.prepareDataEvent_(mouseEvent,dataEvent);this.dispatchEvent(dataEvent);Polymer.dom(this).classList.remove('updating-brushing-state');}};return{ChartBase2D,};});'use strict';tr.exportTo('tr.ui.b',function(){var ChartBase2D=tr.ui.b.ChartBase2D;var ChartBase2DBrushX=tr.ui.b.define('chart-base-2d-brush-1d',ChartBase2D);ChartBase2DBrushX.prototype={__proto__:ChartBase2D.prototype,decorate(){super.decorate();this.brushedRange_=new tr.b.math.Range();},set brushedRange(range){this.brushedRange_.reset();this.brushedRange_.addRange(range);this.updateContents_();},get brushedRange(){return tr.b.math.Range.fromDict(this.brushedRange_.toJSON());},computeBrushRangeFromIndices(indexA,indexB){indexA=tr.b.math.clamp(indexA,0,this.data_.length-1);indexB=tr.b.math.clamp(indexB,0,this.data_.length-1);var leftIndex=Math.min(indexA,indexB);var rightIndex=Math.max(indexA,indexB);var brushRange=new tr.b.math.Range();brushRange.addValue(this.getXForDatum_(this.data_[leftIndex],leftIndex)-
+this.getSampleWidth_(this.data_,leftIndex,true));brushRange.addValue(this.getXForDatum_(this.data_[rightIndex],rightIndex)+
+this.getSampleWidth_(this.data_,rightIndex,false));return brushRange;},getDataIndex_(dataX){if(this.data.length===0)return undefined;var bisect=d3.bisector(this.getXForDatum_.bind(this)).right;return bisect(this.data_,dataX)-1;},prepareDataEvent_(mouseEvent,dataEvent){ChartBase2D.prototype.prepareDataEvent_.call(this,mouseEvent,dataEvent);dataEvent.index=this.getDataIndex_(dataEvent.x);if(dataEvent.index!==undefined){dataEvent.data=this.data_[dataEvent.index];}},updateBrushContents_(brushSel){brushSel.selectAll('*').remove();var brushes=this.brushedRange_.isEmpty?[]:[this.brushedRange_];var brushRectsSel=brushSel.selectAll('rect').data(brushes);brushRectsSel.enter().append('rect');brushRectsSel.exit().remove();this.drawBrush_(brushRectsSel);},drawBrush_(brushRectsSel){brushRectsSel.attr('x',d=>this.xScale_(d.min)).attr('y',0).attr('width',d=>this.xScale_(d.max)-this.xScale_(d.min)).attr('height',this.graphHeight);}};return{ChartBase2DBrushX,};});'use strict';tr.exportTo('tr.ui.b',function(){var LineChart=tr.ui.b.define('line-chart',tr.ui.b.ChartBase2DBrushX);LineChart.prototype={__proto__:tr.ui.b.ChartBase2DBrushX.prototype,get defaultGraphWidth(){return 20*this.data_.length;},get defaultGraphHeight(){return 100;},updateDataContents_(dataSel){dataSel.selectAll('*').remove();var dataBySeriesKey=this.getDataBySeriesKey_();var seriesKeys=[...this.seriesByKey_.keys()];var pathsSel=dataSel.selectAll('path').data(seriesKeys);pathsSel.enter().append('path').style('fill','none').style('stroke-width','1.5px').style('stroke',key=>this.getDataSeries(key).color).attr('d',key=>{var line=d3.svg.line().x(d=>this.xScale_(d.x)).y(d=>this.yScale_(this.dataRange.clamp(d[key])));return line(dataBySeriesKey[key]);});pathsSel.exit().remove();}};return{LineChart,};});'use strict';Polymer({is:'tr-ui-e-s-input-latency-side-panel',behaviors:[tr.ui.behaviors.SidePanel],ready:function(){this.rangeOfInterest_=new tr.b.math.Range();this.frametimeType_=tr.model.helpers.IMPL_FRAMETIME_TYPE;this.latencyChart_=undefined;this.frametimeChart_=undefined;this.selectedProcessId_=undefined;this.mouseDownIndex_=undefined;this.curMouseIndex_=undefined;},get model(){return this.model_;},set model(model){this.model_=model;if(this.model_){this.modelHelper_=this.model_.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);}else{this.modelHelper_=undefined;}
+this.updateToolbar_();this.updateContents_();},get frametimeType(){return this.frametimeType_;},set frametimeType(type){if(this.frametimeType_===type)return;this.frametimeType_=type;this.updateContents_();},get selectedProcessId(){return this.selectedProcessId_;},set selectedProcessId(process){if(this.selectedProcessId_===process)return;this.selectedProcessId_=process;this.updateContents_();},set selection(selection){if(this.latencyChart_===undefined)return;this.latencyChart_.brushedRange=selection.bounds;},setBrushedIndices:function(mouseDownIndex,curIndex){this.mouseDownIndex_=mouseDownIndex;this.curMouseIndex_=curIndex;this.updateBrushedRange_();},updateBrushedRange_:function(){if(this.latencyChart_===undefined)return;var r=new tr.b.math.Range();if(this.mouseDownIndex_===undefined){this.latencyChart_.brushedRange=r;return;}
+r=this.latencyChart_.computeBrushRangeFromIndices(this.mouseDownIndex_,this.curMouseIndex_);this.latencyChart_.brushedRange=r;var latencySlices=[];for(var thread of this.model_.getAllThreads()){for(var event of thread.getDescendantEvents()){if(event.title.indexOf('InputLatency:')===0){latencySlices.push(event);}}}
+latencySlices=tr.model.helpers.getSlicesIntersectingRange(r,latencySlices);var event=new tr.model.RequestSelectionChangeEvent();event.selection=new tr.model.EventSet(latencySlices);this.latencyChart_.dispatchEvent(event);},registerMouseEventForLatencyChart_:function(){this.latencyChart_.addEventListener('item-mousedown',function(e){this.mouseDownIndex_=e.index;this.curMouseIndex_=e.index;this.updateBrushedRange_();}.bind(this));this.latencyChart_.addEventListener('item-mousemove',function(e){if(e.button===undefined)return;this.curMouseIndex_=e.index;this.updateBrushedRange_();}.bind(this));this.latencyChart_.addEventListener('item-mouseup',function(e){this.curMouseIndex=e.index;this.updateBrushedRange_();}.bind(this));},updateToolbar_:function(){var browserProcess=this.modelHelper_.browserProcess;var labels=[];if(browserProcess!==undefined){var labelStr='Browser: '+browserProcess.pid;labels.push({label:labelStr,value:browserProcess.pid});}
+for(var rendererHelper of
+Object.values(this.modelHelper_.rendererHelpers)){var rendererProcess=rendererHelper.process;var labelStr='Renderer: '+rendererProcess.userFriendlyName;labels.push({label:labelStr,value:rendererProcess.userFriendlyName});}
+if(labels.length===0)return;this.selectedProcessId_=labels[0].value;var toolbarEl=this.$.toolbar;Polymer.dom(toolbarEl).appendChild(tr.ui.b.createSelector(this,'frametimeType','inputLatencySidePanel.frametimeType',this.frametimeType_,[{label:'Main Thread Frame Times',value:tr.model.helpers.MAIN_FRAMETIME_TYPE},{label:'Impl Thread Frame Times',value:tr.model.helpers.IMPL_FRAMETIME_TYPE}]));Polymer.dom(toolbarEl).appendChild(tr.ui.b.createSelector(this,'selectedProcessId','inputLatencySidePanel.selectedProcessId',this.selectedProcessId_,labels));},get currentRangeOfInterest(){if(this.rangeOfInterest_.isEmpty){return this.model_.bounds;}
+return this.rangeOfInterest_;},createLatencyLineChart:function(data,title,parentNode){var chart=new tr.ui.b.LineChart();Polymer.dom(parentNode).appendChild(chart);var width=600;if(document.body.clientWidth!==undefined){width=document.body.clientWidth*0.5;}
+chart.graphWidth=width;chart.chartTitle=title;chart.data=data;return chart;},updateContents_:function(){var resultArea=this.$.result_area;this.latencyChart_=undefined;this.frametimeChart_=undefined;Polymer.dom(resultArea).textContent='';if(this.modelHelper_===undefined)return;var rangeOfInterest=this.currentRangeOfInterest;var chromeProcess;if(this.modelHelper_.rendererHelpers[this.selectedProcessId_]){chromeProcess=this.modelHelper_.rendererHelpers[this.selectedProcessId_];}else{chromeProcess=this.modelHelper_.browserHelper;}
+var frameEvents=chromeProcess.getFrameEventsInRange(this.frametimeType,rangeOfInterest);var frametimeData=tr.model.helpers.getFrametimeDataFromEvents(frameEvents);var averageFrametime=tr.b.math.Statistics.mean(frametimeData,d=>d.frametime);var latencyEvents=this.modelHelper_.browserHelper.getLatencyEventsInRange(rangeOfInterest);var latencyData=[];latencyEvents.forEach(function(event){if(event.inputLatency===undefined)return;latencyData.push({x:event.start,latency:event.inputLatency/1000});});var averageLatency=tr.b.math.Statistics.mean(latencyData,function(d){return d.latency;});var latencySummaryText=document.createElement('div');Polymer.dom(latencySummaryText).appendChild(tr.ui.b.createSpan({textContent:'Average Latency '+averageLatency+' ms',bold:true}));Polymer.dom(resultArea).appendChild(latencySummaryText);var frametimeSummaryText=document.createElement('div');Polymer.dom(frametimeSummaryText).appendChild(tr.ui.b.createSpan({textContent:'Average Frame Time '+averageFrametime+' ms',bold:true}));Polymer.dom(resultArea).appendChild(frametimeSummaryText);if(latencyData.length!==0){this.latencyChart_=this.createLatencyLineChart(latencyData,'Latency Over Time',resultArea);this.registerMouseEventForLatencyChart_();}
+if(frametimeData.length!==0){this.frametimeChart_=this.createLatencyLineChart(frametimeData,'Frame Times',resultArea);}},get rangeOfInterest(){return this.rangeOfInterest_;},set rangeOfInterest(rangeOfInterest){this.rangeOfInterest_=rangeOfInterest;this.updateContents_();},supportsModel:function(m){if(m===undefined){return{supported:false,reason:'Unknown tracing model'};}
 if(!tr.model.helpers.ChromeModelHelper.supportsModel(m)){return{supported:false,reason:'No Chrome browser or renderer process found'};}
 var modelHelper=m.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(modelHelper.browserHelper&&modelHelper.browserHelper.hasLatencyEvents){return{supported:true};}
-return{supported:false,reason:'No InputLatency events trace. Consider enabling '+'benchmark" and "input" category when recording the trace'};},get textLabel(){return'Input Latency';}});tr.ui.side_panel.SidePanelRegistry.register(function(){return document.createElement('tr-ui-e-s-input-latency-side-panel');});'use strict';tr.exportTo('tr.ui.b',function(){var ChartBase=tr.ui.b.ChartBase;var MIN_RADIUS=100;var PieChart=tr.ui.b.define('pie-chart',ChartBase);PieChart.prototype={__proto__:ChartBase.prototype,decorate:function(){ChartBase.prototype.decorate.call(this);Polymer.dom(this).classList.add('pie-chart');this.data_=undefined;var chartAreaSel=d3.select(this.chartAreaElement);var pieGroupSel=chartAreaSel.append('g').attr('class','pie-group');this.pieGroup_=pieGroupSel.node();this.pathsGroup_=pieGroupSel.append('g').attr('class','paths').node();this.labelsGroup_=pieGroupSel.append('g').attr('class','labels').node();this.linesGroup_=pieGroupSel.append('g').attr('class','lines').node();},get data(){return this.data_;},get titleMarginPx(){return 40;},set data(data){if(data!==undefined){var seenSeriesKeys={};data.forEach(function(d){var k=d.label;if(seenSeriesKeys[k])
-throw new Error('Label '+k+' has been used already');this.getDataSeries(k);seenSeriesKeys[k]=true;},this);}
-this.data_=data;this.updateContents_();},getMinSize:function(){this.updateContents_();var labelSel=d3.select(this.labelsGroup_).selectAll('.label');var maxLabelWidth=-Number.MAX_VALUE;var leftTextHeightSum=0;var rightTextHeightSum=0;labelSel.each(function(l){var r=this.getBoundingClientRect();maxLabelWidth=Math.max(maxLabelWidth,r.width+32);if(this.style.textAnchor=='end'){leftTextHeightSum+=r.height;}else{rightTextHeightSum+=r.height;}});var titleWidth=Polymer.dom(this).querySelector('#title').getBoundingClientRect().width;var margin=this.margin;var marginWidth=margin.left+margin.right;var marginHeight=margin.top+margin.bottom;return{width:Math.max(2*MIN_RADIUS+2*maxLabelWidth,titleWidth*1.1)+marginWidth,height:marginHeight+Math.max(2*MIN_RADIUS,leftTextHeightSum,rightTextHeightSum)*1.25};},updateScales_:function(width,height){if(this.data_===undefined)
-return;},updateContents_:function(){ChartBase.prototype.updateContents_.call(this);if(!this.data_)
-return;var width=this.chartAreaSize.width;var height=this.chartAreaSize.height;var radius=Math.max(MIN_RADIUS,Math.min(width,height*0.95)/2);d3.select(this.pieGroup_).attr('transform','translate('+width/2+','+height/2+')');var pieLayout=d3.layout.pie().value(function(d){return d.value;}).sort(null);var piePathsSel=d3.select(this.pathsGroup_).datum(this.data_).selectAll('path').data(pieLayout);function midAngle(d){return d.startAngle+(d.endAngle-d.startAngle)/2;}
-var pathsArc=d3.svg.arc().innerRadius(0).outerRadius(radius-30);var valueLabelArc=d3.svg.arc().innerRadius(radius-100).outerRadius(radius-30);var lineBeginArc=d3.svg.arc().innerRadius(radius-50).outerRadius(radius-50);var lineEndArc=d3.svg.arc().innerRadius(radius).outerRadius(radius);piePathsSel.enter().append('path').attr('class','arc').attr('fill',function(d,i){var origData=this.data_[i];var dataSeries=this.getDataSeries(origData.label);if(origData.label===this.currentHighlightedLegendKey)
-return dataSeries.highlightedColor;return dataSeries.color;}.bind(this)).attr('d',pathsArc).on('click',function(d,i){var origData=this.data_[i];var event=new tr.b.Event('item-click');event.data=origData;event.index=i;this.dispatchEvent(event);d3.event.stopPropagation();}.bind(this)).on('mouseenter',function(d,i){var origData=this.data_[i];this.pushTempHighlightedLegendKey(origData.label);}.bind(this)).on('mouseleave',function(d,i){var origData=this.data_[i];this.popTempHighlightedLegendKey(origData.label);}.bind(this));piePathsSel.enter().append('text').attr('class','arc-text').attr('transform',function(d){return'translate('+valueLabelArc.centroid(d)+')';}).attr('dy','.35em').style('text-anchor','middle').on('mouseenter',function(d,i){var origData=this.data_[i];this.pushTempHighlightedLegendKey(origData.label);}.bind(this)).on('mouseleave',function(d,i){var origData=this.data_[i];this.popTempHighlightedLegendKey(origData.label);}.bind(this)).text(function(d,i){var origData=this.data_[i];if(origData.valueText===undefined)
-return'';if(d.endAngle-d.startAngle<0.4)
-return'';return origData.valueText;}.bind(this));piePathsSel.exit().remove();var labelSel=d3.select(this.labelsGroup_).selectAll('.label').data(pieLayout(this.data_));labelSel.enter().append('text').attr('class','label').attr('dy','.35em');labelSel.text(function(d){if(d.data.label.length>40)
-return d.data.label.substr(0,40)+'...';return d.data.label;});labelSel.attr('transform',function(d){var pos=lineEndArc.centroid(d);pos[0]=radius*(midAngle(d)<Math.PI?1:-1);return'translate('+pos+')';});labelSel.style('text-anchor',function(d){return midAngle(d)<Math.PI?'start':'end';});var lineSel=d3.select(this.linesGroup_).selectAll('.line').data(pieLayout(this.data_));lineSel.enter().append('polyline').attr('class','line').attr('dy','.35em');lineSel.attr('points',function(d){var pos=lineEndArc.centroid(d);pos[0]=radius*0.95*(midAngle(d)<Math.PI?1:-1);return[lineBeginArc.centroid(d),lineEndArc.centroid(d),pos];});},updateHighlight_:function(){ChartBase.prototype.updateHighlight_.call(this);var pathsGroupSel=d3.select(this.pathsGroup_);var that=this;pathsGroupSel.selectAll('.arc').each(function(d,i){var origData=that.data_[i];var dataSeries=that.getDataSeries(origData.label);if(origData.label==that.currentHighlightedLegendKey)
-this.style.fill=dataSeries.highlightedColor;else
-this.style.fill=dataSeries.color;});}};return{PieChart:PieChart};});'use strict';(function(){var GROUP_BY_PROCESS_NAME='process';var GROUP_BY_THREAD_NAME='thread';var WALL_TIME_GROUPING_UNIT='Wall time';var CPU_TIME_GROUPING_UNIT='CPU time';function ResultsForGroup(model,name){this.model=model;this.name=name;this.topLevelSlices=[];this.allSlices=[];}
-ResultsForGroup.prototype={get wallTime(){var wallSum=tr.b.Statistics.sum(this.topLevelSlices,function(x){return x.duration;});return wallSum;},get cpuTime(){var cpuDuration=0;for(var i=0;i<this.topLevelSlices.length;i++){var x=this.topLevelSlices[i];if(x.cpuDuration===undefined){if(x.duration===undefined)
-continue;return 0;}
-cpuDuration+=x.cpuDuration;}
-return cpuDuration;},appendGroupContents:function(group){if(group.model!=this.model)
-throw new Error('Models must be the same');group.allSlices.forEach(function(slice){this.allSlices.push(slice);},this);group.topLevelSlices.forEach(function(slice){this.topLevelSlices.push(slice);},this);},appendThreadSlices:function(rangeOfInterest,thread){var tmp=this.getSlicesIntersectingRange(rangeOfInterest,thread.sliceGroup.slices);tmp.forEach(function(slice){this.allSlices.push(slice);},this);tmp=this.getSlicesIntersectingRange(rangeOfInterest,thread.sliceGroup.topLevelSlices);tmp.forEach(function(slice){this.topLevelSlices.push(slice);},this);},getSlicesIntersectingRange:function(rangeOfInterest,slices){var slicesInFilterRange=[];for(var i=0;i<slices.length;i++){var slice=slices[i];if(rangeOfInterest.intersectsExplicitRangeInclusive(slice.start,slice.end))
-slicesInFilterRange.push(slice);}
-return slicesInFilterRange;}};Polymer({is:'tr-ui-e-s-time-summary-side-panel',behaviors:[tr.ui.behaviors.SidePanel],ready:function(){this.rangeOfInterest_=new tr.b.Range();this.selection_=undefined;this.groupBy_=GROUP_BY_PROCESS_NAME;this.groupingUnit_=CPU_TIME_GROUPING_UNIT;this.showCpuIdleTime_=true;this.chart_=undefined;var toolbarEl=this.$.toolbar;this.groupBySelector_=tr.ui.b.createSelector(this,'groupBy','timeSummarySidePanel.groupBy',this.groupBy_,[{label:'Group by process',value:GROUP_BY_PROCESS_NAME},{label:'Group by thread',value:GROUP_BY_THREAD_NAME}]);Polymer.dom(toolbarEl).appendChild(this.groupBySelector_);this.groupingUnitSelector_=tr.ui.b.createSelector(this,'groupingUnit','timeSummarySidePanel.groupingUnit',this.groupingUnit_,[{label:'Wall time',value:WALL_TIME_GROUPING_UNIT},{label:'CPU time',value:CPU_TIME_GROUPING_UNIT}]);Polymer.dom(toolbarEl).appendChild(this.groupingUnitSelector_);this.showCpuIdleTimeCheckbox_=tr.ui.b.createCheckBox(this,'showCpuIdleTime','timeSummarySidePanel.showCpuIdleTime',this.showCpuIdleTime_,'Show CPU idle time');Polymer.dom(toolbarEl).appendChild(this.showCpuIdleTimeCheckbox_);this.updateShowCpuIdleTimeCheckboxVisibility_();},trimPieChartData:function(groups,otherGroup,getValue,opt_extraValue){groups=groups.filter(function(d){return getValue(d)!=0;});var sum=tr.b.Statistics.sum(groups,getValue);if(opt_extraValue!==undefined)
-sum+=opt_extraValue;function compareByValue(a,b){return getValue(a)-getValue(b);}
-groups.sort(compareByValue);var thresshold=0.1*sum;while(groups.length>1){var group=groups[0];if(getValue(group)>=thresshold)
-break;var v=getValue(group);if(v+getValue(otherGroup)>thresshold)
-break;groups.splice(0,1);otherGroup.appendGroupContents(group);}
-if(getValue(otherGroup)>0)
-groups.push(otherGroup);groups.sort(compareByValue);return groups;},generateResultsForGroup:function(model,name){return new ResultsForGroup(model,name);},createPieChartFromResultGroups:function(groups,title,getValue,opt_extraData){var chart=new tr.ui.b.PieChart();function pushDataForGroup(data,resultsForGroup,value){data.push({label:resultsForGroup.name,value:value,valueText:tr.b.Unit.byName.timeDurationInMs.format(value),resultsForGroup:resultsForGroup});}
-chart.addEventListener('item-click',function(clickEvent){var resultsForGroup=clickEvent.data.resultsForGroup;if(resultsForGroup===undefined)
-return;var event=new tr.model.RequestSelectionChangeEvent();event.selection=new tr.model.EventSet(resultsForGroup.allSlices);event.selection.timeSummaryGroupName=resultsForGroup.name;chart.dispatchEvent(event);});var data=[];groups.forEach(function(resultsForGroup){var value=getValue(resultsForGroup);if(value===0)
-return;pushDataForGroup(data,resultsForGroup,value);});if(opt_extraData)
-data.push.apply(data,opt_extraData);chart.chartTitle=title;chart.data=data;return chart;},get model(){return this.model_;},set model(model){this.model_=model;this.updateContents_();},get groupBy(){return groupBy_;},set groupBy(groupBy){this.groupBy_=groupBy;if(this.groupBySelector_)
-this.groupBySelector_.selectedValue=groupBy;this.updateContents_();},get groupingUnit(){return groupingUnit_;},set groupingUnit(groupingUnit){this.groupingUnit_=groupingUnit;if(this.groupingUnitSelector_)
-this.groupingUnitSelector_.selectedValue=groupingUnit;this.updateShowCpuIdleTimeCheckboxVisibility_();this.updateContents_();},get showCpuIdleTime(){return this.showCpuIdleTime_;},set showCpuIdleTime(showCpuIdleTime){this.showCpuIdleTime_=showCpuIdleTime;if(this.showCpuIdleTimeCheckbox_)
-this.showCpuIdleTimeCheckbox_.checked=showCpuIdleTime;this.updateContents_();},updateShowCpuIdleTimeCheckboxVisibility_:function(){if(!this.showCpuIdleTimeCheckbox_)
-return;var visible=this.groupingUnit_==CPU_TIME_GROUPING_UNIT;if(visible)
-this.showCpuIdleTimeCheckbox_.style.display='';else
-this.showCpuIdleTimeCheckbox_.style.display='none';},getGroupNameForThread_:function(thread){if(this.groupBy_==GROUP_BY_THREAD_NAME)
-return thread.name?thread.name:thread.userFriendlyName;if(this.groupBy_==GROUP_BY_PROCESS_NAME)
-return thread.parent.userFriendlyName;},updateContents_:function(){var resultArea=this.$.result_area;this.chart_=undefined;Polymer.dom(resultArea).textContent='';if(this.model_===undefined)
-return;var rangeOfInterest;if(this.rangeOfInterest_.isEmpty)
-rangeOfInterest=this.model_.bounds;else
-rangeOfInterest=this.rangeOfInterest_;var allGroup=this.generateResultsForGroup(this.model_,'all');var resultsByGroupName={};this.model_.getAllThreads().forEach(function(thread){var groupName=this.getGroupNameForThread_(thread);if(resultsByGroupName[groupName]===undefined){resultsByGroupName[groupName]=this.generateResultsForGroup(this.model_,groupName);}
-resultsByGroupName[groupName].appendThreadSlices(rangeOfInterest,thread);allGroup.appendThreadSlices(rangeOfInterest,thread);},this);var getValueFromGroup=function(group){if(this.groupingUnit_==WALL_TIME_GROUPING_UNIT)
-return group.wallTime;return group.cpuTime;}.bind(this);var summaryText=document.createElement('div');Polymer.dom(summaryText).appendChild(tr.ui.b.createSpan({textContent:'Total '+this.groupingUnit_+': ',bold:true}));Polymer.dom(summaryText).appendChild(tr.v.ui.createScalarSpan(getValueFromGroup(allGroup),{unit:tr.b.Unit.byName.timeDurationInMs,ownerDocument:this.ownerDocument}));Polymer.dom(resultArea).appendChild(summaryText);var extraValue=0;var extraData=[];if(this.showCpuIdleTime_&&this.groupingUnit_===CPU_TIME_GROUPING_UNIT&&this.model.kernel.bestGuessAtCpuCount!==undefined){var maxCpuTime=rangeOfInterest.range*this.model.kernel.bestGuessAtCpuCount;var idleTime=Math.max(0,maxCpuTime-allGroup.cpuTime);extraData.push({label:'CPU Idle',value:idleTime,valueText:tr.b.Unit.byName.timeDurationInMs.format(idleTime)});extraValue+=idleTime;}
-var otherGroup=this.generateResultsForGroup(this.model_,'Other');var groups=this.trimPieChartData(tr.b.dictionaryValues(resultsByGroupName),otherGroup,getValueFromGroup,extraValue);if(groups.length==0){Polymer.dom(resultArea).appendChild(tr.ui.b.createSpan({textContent:'No data'}));return undefined;}
-this.chart_=this.createPieChartFromResultGroups(groups,this.groupingUnit_+' breakdown by '+this.groupBy_,getValueFromGroup,extraData);Polymer.dom(resultArea).appendChild(this.chart_);this.chart_.addEventListener('click',function(){var event=new tr.model.RequestSelectionChangeEvent();event.selection=new tr.c.EventSet([]);this.dispatchEvent(event);});this.chart_.setSize(this.chart_.getMinSize());},get selection(){return selection_;},set selection(selection){this.selection_=selection;if(this.chart_===undefined)
-return;if(selection.timeSummaryGroupName)
-this.chart_.highlightedLegendKey=selection.timeSummaryGroupName;else
-this.chart_.highlightedLegendKey=undefined;},get rangeOfInterest(){return this.rangeOfInterest_;},set rangeOfInterest(rangeOfInterest){this.rangeOfInterest_=rangeOfInterest;this.updateContents_();},supportsModel:function(model){return{supported:false};},get textLabel(){return'Time Summary';}});}());'use strict';tr.exportTo('tr.e.system_stats',function(){var ObjectSnapshot=tr.model.ObjectSnapshot;function SystemStatsSnapshot(objectInstance,ts,args){ObjectSnapshot.apply(this,arguments);this.objectInstance=objectInstance;this.ts=ts;this.args=args;this.stats=args;}
-SystemStatsSnapshot.prototype={__proto__:ObjectSnapshot.prototype,initialize:function(){if(this.args.length==0)
-throw new Error('No system stats snapshot data.');this.stats_=this.args;},getStats:function(){return this.stats_;},setStats:function(stats){this.stats_=stats;}};ObjectSnapshot.subTypes.register(SystemStatsSnapshot,{typeName:'base::TraceEventSystemStatsMonitor::SystemStats'});return{SystemStatsSnapshot:SystemStatsSnapshot};});'use strict';tr.exportTo('tr.ui.e.system_stats',function(){var SystemStatsSnapshotView=tr.ui.b.define('tr-ui-e-system-stats-snapshot-view',tr.ui.analysis.ObjectSnapshotView);SystemStatsSnapshotView.prototype={__proto__:tr.ui.analysis.ObjectSnapshotView.prototype,decorate:function(){Polymer.dom(this).classList.add('tr-ui-e-system-stats-snapshot-view');},updateContents:function(){var snapshot=this.objectSnapshot_;if(!snapshot||!snapshot.getStats()){Polymer.dom(this).textContent='No system stats snapshot found.';return;}
-Polymer.dom(this).textContent='';var stats=snapshot.getStats();Polymer.dom(this).appendChild(this.buildList_(stats));},isFloat:function(n){return typeof n==='number'&&n%1!==0;},buildList_:function(stats){var statList=document.createElement('ul');for(var statName in stats){var statText=document.createElement('li');Polymer.dom(statText).textContent=''+statName+': ';Polymer.dom(statList).appendChild(statText);if(stats[statName]instanceof Object){Polymer.dom(statList).appendChild(this.buildList_(stats[statName]));}else{if(this.isFloat(stats[statName]))
-Polymer.dom(statText).textContent+=stats[statName].toFixed(2);else
-Polymer.dom(statText).textContent+=stats[statName];}}
-return statList;}};tr.ui.analysis.ObjectSnapshotView.register(SystemStatsSnapshotView,{typeName:'base::TraceEventSystemStatsMonitor::SystemStats'});return{SystemStatsSnapshotView:SystemStatsSnapshotView};});'use strict';tr.exportTo('tr.ui.b',function(){var constants={HEADING_WIDTH:250};return{constants:constants};});'use strict';Polymer({is:'tr-ui-heading',DOWN_ARROW:String.fromCharCode(0x25BE),RIGHT_ARROW:String.fromCharCode(0x25B8),ready:function(viewport){this.style.width=(tr.ui.b.constants.HEADING_WIDTH-6)+'px';this.heading_='';this.expanded_=true;this.arrowVisible_=false;this.selectionGenerator_=undefined;this.updateContents_();},get heading(){return this.heading_;},set heading(text){if(this.heading_===text)
-return;this.heading_=text;this.updateContents_();},set arrowVisible(val){if(this.arrowVisible_===val)
-return;this.arrowVisible_=!!val;this.updateContents_();},set tooltip(text){this.$.heading.title=text;},set selectionGenerator(generator){if(this.selectionGenerator_===generator)
-return;this.selectionGenerator_=generator;this.updateContents_();},get expanded(){return this.expanded_;},set expanded(expanded){if(this.expanded_===expanded)
-return;this.expanded_=!!expanded;this.updateContents_();},onHeadingDivClicked_:function(){this.dispatchEvent(new tr.b.Event('heading-clicked',{'bubbles':true}));},updateContents_:function(){if(this.arrowVisible_){this.$.arrow.style.display='';}else{this.$.arrow.style.display='none';this.$.heading.style.display=this.expanded_?'':'none';}
+return{supported:false,reason:'No InputLatency events trace. Consider enabling '+'benchmark" and "input" category when recording the trace'};},get textLabel(){return'Input Latency';}});tr.ui.side_panel.SidePanelRegistry.register(function(){return document.createElement('tr-ui-e-s-input-latency-side-panel');});'use strict';tr.exportTo('tr.e.system_stats',function(){var ObjectSnapshot=tr.model.ObjectSnapshot;function SystemStatsSnapshot(objectInstance,ts,args){ObjectSnapshot.apply(this,arguments);this.objectInstance=objectInstance;this.ts=ts;this.args=args;this.stats=args;}
+SystemStatsSnapshot.prototype={__proto__:ObjectSnapshot.prototype,initialize:function(){if(this.args.length===0){throw new Error('No system stats snapshot data.');}
+this.stats_=this.args;},getStats:function(){return this.stats_;},setStats:function(stats){this.stats_=stats;}};ObjectSnapshot.subTypes.register(SystemStatsSnapshot,{typeName:'base::TraceEventSystemStatsMonitor::SystemStats'});return{SystemStatsSnapshot,};});'use strict';tr.exportTo('tr.ui.b',function(){var constants={HEADING_WIDTH:250};return{constants,};});'use strict';Polymer({is:'tr-ui-b-heading',DOWN_ARROW:String.fromCharCode(0x25BE),RIGHT_ARROW:String.fromCharCode(0x25B8),ready:function(viewport){this.style.width=(tr.ui.b.constants.HEADING_WIDTH-6)+'px';this.heading_='';this.expanded_=true;this.arrowVisible_=false;this.selectionGenerator_=undefined;this.updateContents_();},get heading(){return this.heading_;},set heading(text){if(this.heading_===text)return;this.heading_=text;this.updateContents_();},set arrowVisible(val){if(this.arrowVisible_===val)return;this.arrowVisible_=!!val;this.updateContents_();},set tooltip(text){this.$.heading.title=text;},set selectionGenerator(generator){if(this.selectionGenerator_===generator)return;this.selectionGenerator_=generator;this.updateContents_();},get expanded(){return this.expanded_;},set expanded(expanded){if(this.expanded_===expanded)return;this.expanded_=!!expanded;this.updateContents_();},onHeadingDivClicked_:function(){this.dispatchEvent(new tr.b.Event('heading-clicked',true));},updateContents_:function(){if(this.arrowVisible_){this.$.arrow.style.display='';}else{this.$.arrow.style.display='none';this.$.heading.style.display=this.expanded_?'':'none';}
 if(this.arrowVisible_){Polymer.dom(this.$.arrow).textContent=this.expanded_?this.DOWN_ARROW:this.RIGHT_ARROW;}
-this.$.link.style.display='none';this.$.heading_content.style.display='none';if(this.selectionGenerator_){this.$.link.style.display='inline-block';this.$.link.selection=this.selectionGenerator_;Polymer.dom(this.$.link).textContent=this.heading_;}else{this.$.heading_content.style.display='inline-block';Polymer.dom(this.$.heading_content).textContent=this.heading_;}}});'use strict';tr.exportTo('tr.ui.tracks',function(){var Track=tr.ui.b.define('track',tr.ui.b.ContainerThatDecoratesItsChildren);Track.prototype={__proto__:tr.ui.b.ContainerThatDecoratesItsChildren.prototype,decorate:function(viewport){tr.ui.b.ContainerThatDecoratesItsChildren.prototype.decorate.call(this);if(viewport===undefined)
-throw new Error('viewport is required when creating a Track.');this.viewport_=viewport;Polymer.dom(this).classList.add('track');},get viewport(){return this.viewport_;},get drawingContainer(){var cur=this;while(cur){if(cur instanceof tr.ui.tracks.DrawingContainer)
-return cur;cur=cur.parentElement;}
-return undefined;},get eventContainer(){},invalidateDrawingContainer:function(){var dc=this.drawingContainer;if(dc)
-dc.invalidate();},context:function(){if(!Polymer.dom(this).parentNode)
-return undefined;if(!Polymer.dom(this).parentNode.context)
-throw new Error('Parent container does not support context() method.');return Polymer.dom(this).parentNode.context();},decorateChild_:function(childTrack){},undecorateChild_:function(childTrack){if(childTrack.detach)
-childTrack.detach();},updateContents_:function(){},drawTrack:function(type){var ctx=this.context();var pixelRatio=window.devicePixelRatio||1;var bounds=this.getBoundingClientRect();var canvasBounds=ctx.canvas.getBoundingClientRect();ctx.save();ctx.translate(0,pixelRatio*(bounds.top-canvasBounds.top));var dt=this.viewport.currentDisplayTransform;var viewLWorld=dt.xViewToWorld(0);var viewRWorld=dt.xViewToWorld(bounds.width*pixelRatio);this.draw(type,viewLWorld,viewRWorld);ctx.restore();},draw:function(type,viewLWorld,viewRWorld){},addEventsToTrackMap:function(eventToTrackMap){},addContainersToTrackMap:function(containerToTrackMap){},addIntersectingEventsInRangeToSelection:function(loVX,hiVX,loVY,hiVY,selection){var pixelRatio=window.devicePixelRatio||1;var dt=this.viewport.currentDisplayTransform;var viewPixWidthWorld=dt.xViewVectorToWorld(1);var loWX=dt.xViewToWorld(loVX*pixelRatio);var hiWX=dt.xViewToWorld(hiVX*pixelRatio);var clientRect=this.getBoundingClientRect();var a=Math.max(loVY,clientRect.top);var b=Math.min(hiVY,clientRect.bottom);if(a>b)
-return;this.addIntersectingEventsInRangeToSelectionInWorldSpace(loWX,hiWX,viewPixWidthWorld,selection);},addIntersectingEventsInRangeToSelectionInWorldSpace:function(loWX,hiWX,viewPixWidthWorld,selection){},addClosestEventToSelection:function(worldX,worldMaxDist,loY,hiY,selection){},addClosestInstantEventToSelection:function(instantEvents,worldX,worldMaxDist,selection){var instantEvent=tr.b.findClosestElementInSortedArray(instantEvents,function(x){return x.start;},worldX,worldMaxDist);if(!instantEvent)
-return;selection.push(instantEvent);}};return{Track:Track};});'use strict';tr.exportTo('tr.ui.tracks',function(){var SelectionState=tr.model.SelectionState;var EventPresenter=tr.ui.b.EventPresenter;var ObjectInstanceTrack=tr.ui.b.define('object-instance-track',tr.ui.tracks.Track);ObjectInstanceTrack.prototype={__proto__:tr.ui.tracks.Track.prototype,decorate:function(viewport){tr.ui.tracks.Track.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('object-instance-track');this.objectInstances_=[];this.objectSnapshots_=[];this.heading_=document.createElement('tr-ui-heading');Polymer.dom(this).appendChild(this.heading_);},set heading(heading){this.heading_.heading=heading;},get heading(){return this.heading_.heading;},set tooltip(tooltip){this.heading_.tooltip=tooltip;},get objectInstances(){return this.objectInstances_;},set objectInstances(objectInstances){if(!objectInstances||objectInstances.length==0){this.heading='';this.objectInstances_=[];this.objectSnapshots_=[];return;}
-this.heading=objectInstances[0].typeName;this.objectInstances_=objectInstances;this.objectSnapshots_=[];this.objectInstances_.forEach(function(instance){this.objectSnapshots_.push.apply(this.objectSnapshots_,instance.snapshots);},this);this.objectSnapshots_.sort(function(a,b){return a.ts-b.ts;});},get height(){return window.getComputedStyle(this).height;},set height(height){this.style.height=height;},get snapshotRadiusView(){return 7*(window.devicePixelRatio||1);},draw:function(type,viewLWorld,viewRWorld){switch(type){case tr.ui.tracks.DrawType.GENERAL_EVENT:this.drawLetterDots_(viewLWorld,viewRWorld);break;}},drawLetterDots_:function(viewLWorld,viewRWorld){var ctx=this.context();var pixelRatio=window.devicePixelRatio||1;var bounds=this.getBoundingClientRect();var height=bounds.height*pixelRatio;var halfHeight=height*0.5;var twoPi=Math.PI*2;var dt=this.viewport.currentDisplayTransform;var snapshotRadiusView=this.snapshotRadiusView;var snapshotRadiusWorld=dt.xViewVectorToWorld(height);var loI;ctx.save();dt.applyTransformToCanvas(ctx);var objectInstances=this.objectInstances_;var loI=tr.b.findLowIndexInSortedArray(objectInstances,function(instance){return instance.deletionTs;},viewLWorld);ctx.strokeStyle='rgb(0,0,0)';for(var i=loI;i<objectInstances.length;++i){var instance=objectInstances[i];var x=instance.creationTs;if(x>viewRWorld)
-break;var right=instance.deletionTs==Number.MAX_VALUE?viewRWorld:instance.deletionTs;ctx.fillStyle=EventPresenter.getObjectInstanceColor(instance);ctx.fillRect(x,pixelRatio,right-x,height-2*pixelRatio);}
-ctx.restore();var objectSnapshots=this.objectSnapshots_;loI=tr.b.findLowIndexInSortedArray(objectSnapshots,function(snapshot){return snapshot.ts+snapshotRadiusWorld;},viewLWorld);for(var i=loI;i<objectSnapshots.length;++i){var snapshot=objectSnapshots[i];var x=snapshot.ts;if(x-snapshotRadiusWorld>viewRWorld)
-break;var xView=dt.xWorldToView(x);ctx.fillStyle=EventPresenter.getObjectSnapshotColor(snapshot);ctx.beginPath();ctx.arc(xView,halfHeight,snapshotRadiusView,0,twoPi);ctx.fill();if(snapshot.selected){ctx.lineWidth=5;ctx.strokeStyle='rgb(100,100,0)';ctx.stroke();ctx.beginPath();ctx.arc(xView,halfHeight,snapshotRadiusView-1,0,twoPi);ctx.lineWidth=2;ctx.strokeStyle='rgb(255,255,0)';ctx.stroke();}else{ctx.lineWidth=1;ctx.strokeStyle='rgb(0,0,0)';ctx.stroke();}}
+this.$.link.style.display='none';this.$.heading_content.style.display='none';if(this.selectionGenerator_){this.$.link.style.display='inline-block';this.$.link.selection=this.selectionGenerator_;Polymer.dom(this.$.link).textContent=this.heading_;}else{this.$.heading_content.style.display='inline-block';Polymer.dom(this.$.heading_content).textContent=this.heading_;}}});'use strict';tr.exportTo('tr.ui.tracks',function(){var Track=tr.ui.b.define('track',tr.ui.b.ContainerThatDecoratesItsChildren);Track.prototype={__proto__:tr.ui.b.ContainerThatDecoratesItsChildren.prototype,decorate:function(viewport){tr.ui.b.ContainerThatDecoratesItsChildren.prototype.decorate.call(this);if(viewport===undefined){throw new Error('viewport is required when creating a Track.');}
+this.viewport_=viewport;Polymer.dom(this).classList.add('track');},get viewport(){return this.viewport_;},get drawingContainer(){if(this instanceof tr.ui.tracks.DrawingContainer)return this;let cur=this.parentElement;while(cur){if(cur instanceof tr.ui.tracks.DrawingContainer)return cur;cur=cur.parentElement;}
+return undefined;},get eventContainer(){},invalidateDrawingContainer:function(){var dc=this.drawingContainer;if(dc)dc.invalidate();},context:function(){if(!Polymer.dom(this).parentNode)return undefined;if(!Polymer.dom(this).parentNode.context){throw new Error('Parent container does not support context() method.');}
+return Polymer.dom(this).parentNode.context();},decorateChild_:function(childTrack){},undecorateChild_:function(childTrack){if(childTrack.detach){childTrack.detach();}},updateContents_:function(){},drawTrack:function(type){var ctx=this.context();var pixelRatio=window.devicePixelRatio||1;var bounds=this.getBoundingClientRect();var canvasBounds=ctx.canvas.getBoundingClientRect();ctx.save();ctx.translate(0,pixelRatio*(bounds.top-canvasBounds.top));var dt=this.viewport.currentDisplayTransform;var viewLWorld=dt.xViewToWorld(0);var viewRWorld=dt.xViewToWorld(bounds.width*pixelRatio);this.draw(type,viewLWorld,viewRWorld);ctx.restore();},draw:function(type,viewLWorld,viewRWorld){},addEventsToTrackMap:function(eventToTrackMap){},addContainersToTrackMap:function(containerToTrackMap){},addIntersectingEventsInRangeToSelection:function(loVX,hiVX,loVY,hiVY,selection){var pixelRatio=window.devicePixelRatio||1;var dt=this.viewport.currentDisplayTransform;var viewPixWidthWorld=dt.xViewVectorToWorld(1);var loWX=dt.xViewToWorld(loVX*pixelRatio);var hiWX=dt.xViewToWorld(hiVX*pixelRatio);var clientRect=this.getBoundingClientRect();var a=Math.max(loVY,clientRect.top);var b=Math.min(hiVY,clientRect.bottom);if(a>b)return;this.addIntersectingEventsInRangeToSelectionInWorldSpace(loWX,hiWX,viewPixWidthWorld,selection);},addIntersectingEventsInRangeToSelectionInWorldSpace:function(loWX,hiWX,viewPixWidthWorld,selection){},addClosestEventToSelection:function(worldX,worldMaxDist,loY,hiY,selection){},addClosestInstantEventToSelection:function(instantEvents,worldX,worldMaxDist,selection){var instantEvent=tr.b.math.findClosestElementInSortedArray(instantEvents,function(x){return x.start;},worldX,worldMaxDist);if(!instantEvent)return;selection.push(instantEvent);}};return{Track,};});'use strict';tr.exportTo('tr.ui.tracks',function(){var SelectionState=tr.model.SelectionState;var EventPresenter=tr.ui.b.EventPresenter;var ObjectInstanceTrack=tr.ui.b.define('object-instance-track',tr.ui.tracks.Track);ObjectInstanceTrack.prototype={__proto__:tr.ui.tracks.Track.prototype,decorate:function(viewport){tr.ui.tracks.Track.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('object-instance-track');this.objectInstances_=[];this.objectSnapshots_=[];this.heading_=document.createElement('tr-ui-b-heading');Polymer.dom(this).appendChild(this.heading_);},set heading(heading){this.heading_.heading=heading;},get heading(){return this.heading_.heading;},set tooltip(tooltip){this.heading_.tooltip=tooltip;},get objectInstances(){return this.objectInstances_;},set objectInstances(objectInstances){if(!objectInstances||objectInstances.length===0){this.heading='';this.objectInstances_=[];this.objectSnapshots_=[];return;}
+this.heading=objectInstances[0].typeName;this.objectInstances_=objectInstances;this.objectSnapshots_=[];this.objectInstances_.forEach(function(instance){this.objectSnapshots_.push.apply(this.objectSnapshots_,instance.snapshots);},this);this.objectSnapshots_.sort(function(a,b){return a.ts-b.ts;});},get height(){return window.getComputedStyle(this).height;},set height(height){this.style.height=height;},get snapshotRadiusView(){return 7*(window.devicePixelRatio||1);},draw:function(type,viewLWorld,viewRWorld){switch(type){case tr.ui.tracks.DrawType.GENERAL_EVENT:this.drawLetterDots_(viewLWorld,viewRWorld);break;}},drawLetterDots_:function(viewLWorld,viewRWorld){var ctx=this.context();var pixelRatio=window.devicePixelRatio||1;var bounds=this.getBoundingClientRect();var height=bounds.height*pixelRatio;var halfHeight=height*0.5;var twoPi=Math.PI*2;var dt=this.viewport.currentDisplayTransform;var snapshotRadiusView=this.snapshotRadiusView;var snapshotRadiusWorld=dt.xViewVectorToWorld(height);var loI;ctx.save();dt.applyTransformToCanvas(ctx);var objectInstances=this.objectInstances_;var loI=tr.b.math.findLowIndexInSortedArray(objectInstances,function(instance){return instance.deletionTs;},viewLWorld);ctx.strokeStyle='rgb(0,0,0)';for(var i=loI;i<objectInstances.length;++i){var instance=objectInstances[i];var x=instance.creationTs;if(x>viewRWorld)break;var right=instance.deletionTs===Number.MAX_VALUE?viewRWorld:instance.deletionTs;ctx.fillStyle=EventPresenter.getObjectInstanceColor(instance);ctx.fillRect(x,pixelRatio,right-x,height-2*pixelRatio);}
+ctx.restore();var objectSnapshots=this.objectSnapshots_;loI=tr.b.math.findLowIndexInSortedArray(objectSnapshots,function(snapshot){return snapshot.ts+snapshotRadiusWorld;},viewLWorld);for(var i=loI;i<objectSnapshots.length;++i){var snapshot=objectSnapshots[i];var x=snapshot.ts;if(x-snapshotRadiusWorld>viewRWorld)break;var xView=dt.xWorldToView(x);ctx.fillStyle=EventPresenter.getObjectSnapshotColor(snapshot);ctx.beginPath();ctx.arc(xView,halfHeight,snapshotRadiusView,0,twoPi);ctx.fill();if(snapshot.selected){ctx.lineWidth=5;ctx.strokeStyle='rgb(100,100,0)';ctx.stroke();ctx.beginPath();ctx.arc(xView,halfHeight,snapshotRadiusView-1,0,twoPi);ctx.lineWidth=2;ctx.strokeStyle='rgb(255,255,0)';ctx.stroke();}else{ctx.lineWidth=1;ctx.strokeStyle='rgb(0,0,0)';ctx.stroke();}}
 ctx.lineWidth=1;var selectionState=SelectionState.NONE;if(objectInstances.length&&objectInstances[0].selectionState===SelectionState.DIMMED){selectionState=SelectionState.DIMMED;}
 if(selectionState===SelectionState.DIMMED){var width=bounds.width*pixelRatio;ctx.fillStyle='rgba(255,255,255,0.5)';ctx.fillRect(0,0,width,height);ctx.restore();}},addEventsToTrackMap:function(eventToTrackMap){if(this.objectInstance_!==undefined){this.objectInstance_.forEach(function(obj){eventToTrackMap.addEvent(obj,this);},this);}
 if(this.objectSnapshots_!==undefined){this.objectSnapshots_.forEach(function(obj){eventToTrackMap.addEvent(obj,this);},this);}},addIntersectingEventsInRangeToSelectionInWorldSpace:function(loWX,hiWX,viewPixWidthWorld,selection){var foundSnapshot=false;function onSnapshot(snapshot){selection.push(snapshot);foundSnapshot=true;}
-var snapshotRadiusView=this.snapshotRadiusView;var snapshotRadiusWorld=viewPixWidthWorld*snapshotRadiusView;tr.b.iterateOverIntersectingIntervals(this.objectSnapshots_,function(x){return x.ts-snapshotRadiusWorld;},function(x){return 2*snapshotRadiusWorld;},loWX,hiWX,onSnapshot);if(foundSnapshot)
-return;tr.b.iterateOverIntersectingIntervals(this.objectInstances_,function(x){return x.creationTs;},function(x){return x.deletionTs-x.creationTs;},loWX,hiWX,selection.push.bind(selection));},addEventNearToProvidedEventToSelection:function(event,offset,selection){var events;if(event instanceof tr.model.ObjectSnapshot)
-events=this.objectSnapshots_;else if(event instanceof tr.model.ObjectInstance)
-events=this.objectInstances_;else
-throw new Error('Unrecognized event');var index=events.indexOf(event);var newIndex=index+offset;if(newIndex>=0&&newIndex<events.length){selection.push(events[newIndex]);return true;}
-return false;},addAllEventsMatchingFilterToSelection:function(filter,selection){},addClosestEventToSelection:function(worldX,worldMaxDist,loY,hiY,selection){var snapshot=tr.b.findClosestElementInSortedArray(this.objectSnapshots_,function(x){return x.ts;},worldX,worldMaxDist);if(!snapshot)
-return;selection.push(snapshot);}};var options=new tr.b.ExtensionRegistryOptions(tr.b.TYPE_BASED_REGISTRY_MODE);tr.b.decorateExtensionRegistry(ObjectInstanceTrack,options);return{ObjectInstanceTrack:ObjectInstanceTrack};});'use strict';tr.exportTo('tr.ui.tracks',function(){var StackedBarsTrack=tr.ui.b.define('stacked-bars-track',tr.ui.tracks.Track);StackedBarsTrack.prototype={__proto__:tr.ui.tracks.Track.prototype,decorate:function(viewport){tr.ui.tracks.Track.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('stacked-bars-track');this.objectInstance_=null;this.heading_=document.createElement('tr-ui-heading');Polymer.dom(this).appendChild(this.heading_);},set heading(heading){this.heading_.heading=heading;},get heading(){return this.heading_.heading;},set tooltip(tooltip){this.heading_.tooltip=tooltip;},addEventsToTrackMap:function(eventToTrackMap){var objectSnapshots=this.objectInstance_.snapshots;objectSnapshots.forEach(function(obj){eventToTrackMap.addEvent(obj,this);},this);},addIntersectingEventsInRangeToSelectionInWorldSpace:function(loWX,hiWX,viewPixWidthWorld,selection){function onSnapshot(snapshot){selection.push(snapshot);}
-var snapshots=this.objectInstance_.snapshots;var maxBounds=this.objectInstance_.parent.model.bounds.max;tr.b.iterateOverIntersectingIntervals(snapshots,function(x){return x.ts;},function(x,i){if(i==snapshots.length-1){if(snapshots.length==1)
-return maxBounds;return snapshots[i].ts-snapshots[i-1].ts;}
-return snapshots[i+1].ts-snapshots[i].ts;},loWX,hiWX,onSnapshot);},addEventNearToProvidedEventToSelection:function(event,offset,selection){if(!(event instanceof tr.model.ObjectSnapshot))
-throw new Error('Unrecognized event');var objectSnapshots=this.objectInstance_.snapshots;var index=objectSnapshots.indexOf(event);var newIndex=index+offset;if(newIndex>=0&&newIndex<objectSnapshots.length){selection.push(objectSnapshots[newIndex]);return true;}
-return false;},addAllEventsMatchingFilterToSelection:function(filter,selection){},addClosestEventToSelection:function(worldX,worldMaxDist,loY,hiY,selection){var snapshot=tr.b.findClosestElementInSortedArray(this.objectInstance_.snapshots,function(x){return x.ts;},worldX,worldMaxDist);if(!snapshot)
-return;selection.push(snapshot);}};return{StackedBarsTrack:StackedBarsTrack};});'use strict';tr.exportTo('tr.ui.e.system_stats',function(){var EventPresenter=tr.ui.b.EventPresenter;var statCount;var excludedStats={'meminfo':{'pswpin':0,'pswpout':0,'pgmajfault':0},'diskinfo':{'io':0,'io_time':0,'read_time':0,'reads':0,'reads_merged':0,'sectors_read':0,'sectors_written':0,'weighted_io_time':0,'write_time':0,'writes':0,'writes_merged':0},'swapinfo':{}};var SystemStatsInstanceTrack=tr.ui.b.define('tr-ui-e-system-stats-instance-track',tr.ui.tracks.StackedBarsTrack);SystemStatsInstanceTrack.prototype={__proto__:tr.ui.tracks.StackedBarsTrack.prototype,decorate:function(viewport){tr.ui.tracks.StackedBarsTrack.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('tr-ui-e-system-stats-instance-track');this.objectInstance_=null;},set objectInstances(objectInstances){if(!objectInstances){this.objectInstance_=[];return;}
-if(objectInstances.length!=1)
-throw new Error('Bad object instance count.');this.objectInstance_=objectInstances[0];if(this.objectInstance_!==null){this.computeRates_(this.objectInstance_.snapshots);this.maxStats_=this.computeMaxStats_(this.objectInstance_.snapshots);}},computeRates_:function(snapshots){for(var i=0;i<snapshots.length;i++){var snapshot=snapshots[i];var stats=snapshot.getStats();var prevSnapshot;var prevStats;if(i==0){prevSnapshot=snapshots[0];}else{prevSnapshot=snapshots[i-1];}
-prevStats=prevSnapshot.getStats();var timeIntervalSeconds=(snapshot.ts-prevSnapshot.ts)/1000;if(timeIntervalSeconds==0)
-timeIntervalSeconds=1;this.computeRatesRecursive_(prevStats,stats,timeIntervalSeconds);}},computeRatesRecursive_:function(prevStats,stats,timeIntervalSeconds){for(var statName in stats){if(stats[statName]instanceof Object){this.computeRatesRecursive_(prevStats[statName],stats[statName],timeIntervalSeconds);}else{if(statName=='sectors_read'){stats['bytes_read_per_sec']=(stats['sectors_read']-
+var snapshotRadiusView=this.snapshotRadiusView;var snapshotRadiusWorld=viewPixWidthWorld*snapshotRadiusView;tr.b.math.iterateOverIntersectingIntervals(this.objectSnapshots_,function(x){return x.ts-snapshotRadiusWorld;},function(x){return 2*snapshotRadiusWorld;},loWX,hiWX,onSnapshot);if(foundSnapshot)return;tr.b.math.iterateOverIntersectingIntervals(this.objectInstances_,function(x){return x.creationTs;},function(x){return x.deletionTs-x.creationTs;},loWX,hiWX,(value)=>{selection.push(value);});},addEventNearToProvidedEventToSelection:function(event,offset,selection){var events;if(event instanceof tr.model.ObjectSnapshot){events=this.objectSnapshots_;}else if(event instanceof tr.model.ObjectInstance){events=this.objectInstances_;}else{throw new Error('Unrecognized event');}
+var index=events.indexOf(event);var newIndex=index+offset;if(newIndex>=0&&newIndex<events.length){selection.push(events[newIndex]);return true;}
+return false;},addAllEventsMatchingFilterToSelection:function(filter,selection){},addClosestEventToSelection:function(worldX,worldMaxDist,loY,hiY,selection){var snapshot=tr.b.math.findClosestElementInSortedArray(this.objectSnapshots_,function(x){return x.ts;},worldX,worldMaxDist);if(!snapshot)return;selection.push(snapshot);}};var options=new tr.b.ExtensionRegistryOptions(tr.b.TYPE_BASED_REGISTRY_MODE);tr.b.decorateExtensionRegistry(ObjectInstanceTrack,options);return{ObjectInstanceTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){var StackedBarsTrack=tr.ui.b.define('stacked-bars-track',tr.ui.tracks.Track);StackedBarsTrack.prototype={__proto__:tr.ui.tracks.Track.prototype,decorate:function(viewport){tr.ui.tracks.Track.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('stacked-bars-track');this.objectInstance_=null;this.heading_=document.createElement('tr-ui-b-heading');Polymer.dom(this).appendChild(this.heading_);},set heading(heading){this.heading_.heading=heading;},get heading(){return this.heading_.heading;},set tooltip(tooltip){this.heading_.tooltip=tooltip;},addEventsToTrackMap:function(eventToTrackMap){var objectSnapshots=this.objectInstance_.snapshots;objectSnapshots.forEach(function(obj){eventToTrackMap.addEvent(obj,this);},this);},addIntersectingEventsInRangeToSelectionInWorldSpace:function(loWX,hiWX,viewPixWidthWorld,selection){function onSnapshot(snapshot){selection.push(snapshot);}
+var snapshots=this.objectInstance_.snapshots;var maxBounds=this.objectInstance_.parent.model.bounds.max;tr.b.math.iterateOverIntersectingIntervals(snapshots,function(x){return x.ts;},function(x,i){if(i===snapshots.length-1){if(snapshots.length===1){return maxBounds;}
+return snapshots[i].ts-snapshots[i-1].ts;}
+return snapshots[i+1].ts-snapshots[i].ts;},loWX,hiWX,onSnapshot);},addEventNearToProvidedEventToSelection:function(event,offset,selection){if(!(event instanceof tr.model.ObjectSnapshot)){throw new Error('Unrecognized event');}
+var objectSnapshots=this.objectInstance_.snapshots;var index=objectSnapshots.indexOf(event);var newIndex=index+offset;if(newIndex>=0&&newIndex<objectSnapshots.length){selection.push(objectSnapshots[newIndex]);return true;}
+return false;},addAllEventsMatchingFilterToSelection:function(filter,selection){},addClosestEventToSelection:function(worldX,worldMaxDist,loY,hiY,selection){var snapshot=tr.b.math.findClosestElementInSortedArray(this.objectInstance_.snapshots,function(x){return x.ts;},worldX,worldMaxDist);if(!snapshot)return;selection.push(snapshot);}};return{StackedBarsTrack,};});'use strict';tr.exportTo('tr.ui.e.system_stats',function(){var EventPresenter=tr.ui.b.EventPresenter;var statCount;var excludedStats={'meminfo':{'pswpin':0,'pswpout':0,'pgmajfault':0},'diskinfo':{'io':0,'io_time':0,'read_time':0,'reads':0,'reads_merged':0,'sectors_read':0,'sectors_written':0,'weighted_io_time':0,'write_time':0,'writes':0,'writes_merged':0},'swapinfo':{}};var SystemStatsInstanceTrack=tr.ui.b.define('tr-ui-e-system-stats-instance-track',tr.ui.tracks.StackedBarsTrack);SystemStatsInstanceTrack.prototype={__proto__:tr.ui.tracks.StackedBarsTrack.prototype,decorate:function(viewport){tr.ui.tracks.StackedBarsTrack.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('tr-ui-e-system-stats-instance-track');this.objectInstance_=null;},set objectInstances(objectInstances){if(!objectInstances){this.objectInstance_=[];return;}
+if(objectInstances.length!==1){throw new Error('Bad object instance count.');}
+this.objectInstance_=objectInstances[0];if(this.objectInstance_!==null){this.computeRates_(this.objectInstance_.snapshots);this.maxStats_=this.computeMaxStats_(this.objectInstance_.snapshots);}},computeRates_:function(snapshots){for(var i=0;i<snapshots.length;i++){var snapshot=snapshots[i];var stats=snapshot.getStats();var prevSnapshot;var prevStats;if(i===0){prevSnapshot=snapshots[0];}else{prevSnapshot=snapshots[i-1];}
+prevStats=prevSnapshot.getStats();var timeIntervalSeconds=(snapshot.ts-prevSnapshot.ts)/1000;if(timeIntervalSeconds===0){timeIntervalSeconds=1;}
+this.computeRatesRecursive_(prevStats,stats,timeIntervalSeconds);}},computeRatesRecursive_:function(prevStats,stats,timeIntervalSeconds){for(var statName in stats){if(stats[statName]instanceof Object){this.computeRatesRecursive_(prevStats[statName],stats[statName],timeIntervalSeconds);}else{if(statName==='sectors_read'){stats['bytes_read_per_sec']=(stats['sectors_read']-
 prevStats['sectors_read'])*512/timeIntervalSeconds;}
-if(statName=='sectors_written'){stats['bytes_written_per_sec']=(stats['sectors_written']-
+if(statName==='sectors_written'){stats['bytes_written_per_sec']=(stats['sectors_written']-
 prevStats['sectors_written'])*512/timeIntervalSeconds;}
-if(statName=='pgmajfault'){stats['pgmajfault_per_sec']=(stats['pgmajfault']-
+if(statName==='pgmajfault'){stats['pgmajfault_per_sec']=(stats['pgmajfault']-
 prevStats['pgmajfault'])/timeIntervalSeconds;}
-if(statName=='pswpin'){stats['bytes_swpin_per_sec']=(stats['pswpin']-
+if(statName==='pswpin'){stats['bytes_swpin_per_sec']=(stats['pswpin']-
 prevStats['pswpin'])*1000/timeIntervalSeconds;}
-if(statName=='pswpout'){stats['bytes_swpout_per_sec']=(stats['pswpout']-
-prevStats['pswpout'])*1000/timeIntervalSeconds;}}}},computeMaxStats_:function(snapshots){var maxStats=new Object();statCount=0;for(var i=0;i<snapshots.length;i++){var snapshot=snapshots[i];var stats=snapshot.getStats();this.computeMaxStatsRecursive_(stats,maxStats,excludedStats);}
-return maxStats;},computeMaxStatsRecursive_:function(stats,maxStats,excludedStats){for(var statName in stats){if(stats[statName]instanceof Object){if(!(statName in maxStats))
-maxStats[statName]=new Object();var excludedNested;if(excludedStats&&statName in excludedStats)
-excludedNested=excludedStats[statName];else
-excludedNested=null;this.computeMaxStatsRecursive_(stats[statName],maxStats[statName],excludedNested);}else{if(excludedStats&&statName in excludedStats)
-continue;if(!(statName in maxStats)){maxStats[statName]=0;statCount++;}
-if(stats[statName]>maxStats[statName])
-maxStats[statName]=stats[statName];}}},get height(){return window.getComputedStyle(this).height;},set height(height){this.style.height=height;},draw:function(type,viewLWorld,viewRWorld){switch(type){case tr.ui.tracks.DrawType.GENERAL_EVENT:this.drawStatBars_(viewLWorld,viewRWorld);break;}},drawStatBars_:function(viewLWorld,viewRWorld){var ctx=this.context();var pixelRatio=window.devicePixelRatio||1;var bounds=this.getBoundingClientRect();var width=bounds.width*pixelRatio;var height=(bounds.height*pixelRatio)/statCount;var vp=this.viewport.currentDisplayTransform;var maxStats=this.maxStats_;var objectSnapshots=this.objectInstance_.snapshots;var lowIndex=tr.b.findLowIndexInSortedArray(objectSnapshots,function(snapshot){return snapshot.ts;},viewLWorld);if(lowIndex>0)
-lowIndex-=1;for(var i=lowIndex;i<objectSnapshots.length;++i){var snapshot=objectSnapshots[i];var trace=snapshot.getStats();var currentY=height;var left=snapshot.ts;if(left>viewRWorld)
-break;var leftView=vp.xWorldToView(left);if(leftView<0)
-leftView=0;var right;if(i!=objectSnapshots.length-1){right=objectSnapshots[i+1].ts;}else{if(objectSnapshots.length>1)
-right=objectSnapshots[i].ts+(objectSnapshots[i].ts-
-objectSnapshots[i-1].ts);else
-right=this.objectInstance_.parent.model.bounds.max;}
-var rightView=vp.xWorldToView(right);if(rightView>width)
-rightView=width;leftView=Math.floor(leftView);rightView=Math.floor(rightView);this.drawStatBarsRecursive_(snapshot,leftView,rightView,height,trace,maxStats,currentY);if(i==lowIndex)
-this.drawStatNames_(leftView,height,currentY,'',maxStats);}
+if(statName==='pswpout'){stats['bytes_swpout_per_sec']=(stats['pswpout']-
+prevStats['pswpout'])*1000/timeIntervalSeconds;}}}},computeMaxStats_:function(snapshots){var maxStats={};statCount=0;for(var i=0;i<snapshots.length;i++){var snapshot=snapshots[i];var stats=snapshot.getStats();this.computeMaxStatsRecursive_(stats,maxStats,excludedStats);}
+return maxStats;},computeMaxStatsRecursive_:function(stats,maxStats,excludedStats){for(var statName in stats){if(stats[statName]instanceof Object){if(!(statName in maxStats)){maxStats[statName]={};}
+var excludedNested;if(excludedStats&&statName in excludedStats){excludedNested=excludedStats[statName];}else{excludedNested=null;}
+this.computeMaxStatsRecursive_(stats[statName],maxStats[statName],excludedNested);}else{if(excludedStats&&statName in excludedStats){continue;}
+if(!(statName in maxStats)){maxStats[statName]=0;statCount++;}
+if(stats[statName]>maxStats[statName]){maxStats[statName]=stats[statName];}}}},get height(){return window.getComputedStyle(this).height;},set height(height){this.style.height=height;},draw:function(type,viewLWorld,viewRWorld){switch(type){case tr.ui.tracks.DrawType.GENERAL_EVENT:this.drawStatBars_(viewLWorld,viewRWorld);break;}},drawStatBars_:function(viewLWorld,viewRWorld){var ctx=this.context();var pixelRatio=window.devicePixelRatio||1;var bounds=this.getBoundingClientRect();var width=bounds.width*pixelRatio;var height=(bounds.height*pixelRatio)/statCount;var vp=this.viewport.currentDisplayTransform;var maxStats=this.maxStats_;var objectSnapshots=this.objectInstance_.snapshots;var lowIndex=tr.b.math.findLowIndexInSortedArray(objectSnapshots,function(snapshot){return snapshot.ts;},viewLWorld);if(lowIndex>0)lowIndex-=1;for(var i=lowIndex;i<objectSnapshots.length;++i){var snapshot=objectSnapshots[i];var trace=snapshot.getStats();var currentY=height;var left=snapshot.ts;if(left>viewRWorld)break;var leftView=vp.xWorldToView(left);if(leftView<0)leftView=0;var right;if(i!==objectSnapshots.length-1){right=objectSnapshots[i+1].ts;}else{if(objectSnapshots.length>1){right=objectSnapshots[i].ts+(objectSnapshots[i].ts-
+objectSnapshots[i-1].ts);}else{right=this.objectInstance_.parent.model.bounds.max;}}
+var rightView=vp.xWorldToView(right);if(rightView>width){rightView=width;}
+leftView=Math.floor(leftView);rightView=Math.floor(rightView);this.drawStatBarsRecursive_(snapshot,leftView,rightView,height,trace,maxStats,currentY);if(i===lowIndex){this.drawStatNames_(leftView,height,currentY,'',maxStats);}}
 ctx.lineWidth=1;},drawStatBarsRecursive_:function(snapshot,leftView,rightView,height,stats,maxStats,currentY){var ctx=this.context();for(var statName in maxStats){if(stats[statName]instanceof Object){currentY=this.drawStatBarsRecursive_(snapshot,leftView,rightView,height,stats[statName],maxStats[statName],currentY);}else{var maxStat=maxStats[statName];ctx.fillStyle=EventPresenter.getBarSnapshotColor(snapshot,Math.round(currentY/height));var barHeight;if(maxStat>0){barHeight=height*Math.max(stats[statName],0)/maxStat;}else{barHeight=0;}
 ctx.fillRect(leftView,currentY-barHeight,Math.max(rightView-leftView,1),barHeight);currentY+=height;}}
-return currentY;},drawStatNames_:function(leftView,height,currentY,prefix,maxStats){var ctx=this.context();ctx.textAlign='end';ctx.font='12px Arial';ctx.fillStyle='#000000';for(var statName in maxStats){if(maxStats[statName]instanceof Object){currentY=this.drawStatNames_(leftView,height,currentY,statName,maxStats[statName]);}else{var fullname=statName;if(prefix!='')
-fullname=prefix+' :: '+statName;ctx.fillText(fullname,leftView-10,currentY-height/4);currentY+=height;}}
-return currentY;}};tr.ui.tracks.ObjectInstanceTrack.register(SystemStatsInstanceTrack,{typeName:'base::TraceEventSystemStatsMonitor::SystemStats'});return{SystemStatsInstanceTrack:SystemStatsInstanceTrack};});'use strict';tr.exportTo('tr.ui.e.v8',function(){function handleCodeSearch_(event){if(event.target.parentNode===undefined)return;var name=event.target.parentNode.entryName;var url='https://cs.chromium.org/search/?sq=package:chromium&type=cs&q=';if(name.startsWith('API_'))name=name.substring(4);url+=encodeURIComponent(name)+'+file:src/v8/src';window.open(url,'_blank');}
-var Entry=function(name,count,time){this.name_=name;this.count_=count;this.time_=time;};Entry.prototype={__proto__:Object.prototype,get name(){return this.name_;},get count(){return this.count_;},get time(){return this.time_;},accumulate:function(count,time){this.count_+=count;this.time_+=time;},reset:function(){this.count_=0;this.time_=0;}};var GroupedEntry=function(name,match_regex){Entry.call(this,name,0,0);this.regex_=match_regex;this.entries_=new Map();};GroupedEntry.prototype={__proto__:Entry.prototype,match:function(name){return this.regex_&&!(!name.match(this.regex_));},add:function(entry){var value=this.entries_.get(entry.name);if(value!==undefined)
-value.accumulate(entry.count,entry.time);else
-this.entries_.set(entry.name,entry);this.count_+=entry.count;this.time_+=entry.time;},get subRows(){return Array.from(this.entries_.values());},reset:function(entry){this.time_=0;this.count_=0;this.entries_.clear();}};Polymer({is:'tr-ui-e-v8-runtime-call-stats-table',ready:function(){this.table_=this.$.table;this.totalTime_=0;},constructTable_:function(){var totalTime=this.totalTime_;this.table_.selectionMode=tr.ui.b.TableFormat.SelectionMode.ROW;this.table_.tableColumns=[{title:'Name',value:function(row){var typeEl=document.createElement('span');typeEl.innerText=row.name;if(!(row instanceof GroupedEntry)){typeEl.title='click ? for code search';typeEl.entryName=row.name;var codeSearchEl=document.createElement('span');codeSearchEl.innerText='?';codeSearchEl.style.float='right';codeSearchEl.style.borderRadius='5px';codeSearchEl.style.backgroundColor='#EEE';codeSearchEl.addEventListener('click',handleCodeSearch_.bind(this));typeEl.appendChild(codeSearchEl);}
-return typeEl;},width:'200px',showExpandButtons:true},{title:'Time',value:function(row){var typeEl=document.createElement('span');typeEl.innerText=(row.time/1000.0).toFixed(3)+' ms';return typeEl;},width:'100px',cmp:function(a,b){return a.time-b.time;}},{title:'Count',value:function(row){var typeEl=document.createElement('span');typeEl.innerText=row.count;return typeEl;},width:'100px',cmp:function(a,b){return a.count-b.count;}},{title:'Percent',value:function(row){var typeEl=document.createElement('span');typeEl.innerText=(row.time/totalTime*100).toFixed(3)+'%';return typeEl;},width:'100px',cmp:function(a,b){return a.time-b.time;}}];this.table_.sortColumnIndex=1;this.table_.sortDescending=true;},set slices(slices){var groups=new Array(new GroupedEntry('Total'),new GroupedEntry('IC',/.*IC.*/),new GroupedEntry('Optimize',/StackGuard|.*Optimize.*|.*Deoptimize.*|Recompile.*/),new GroupedEntry('Compile',/.*Compile.*/),new GroupedEntry('Parse',/.*Parse.*/),new GroupedEntry('Callback',/.*Callback$/),new GroupedEntry('API',/.*API.*/),new GroupedEntry('GC',/GC|AllocateInTargetSpace/),new GroupedEntry('JavaScript',/JS_Execution/),new GroupedEntry('Runtime',/.*/));slices.forEach(function(slice){if(!(slice instanceof tr.e.v8.V8ThreadSlice))return;try{var runtimeCallStats=JSON.parse(slice.runtimeCallStats);}catch(e){var runtimeCallStats=slice.runtimeCallStats;}
-if(runtimeCallStats!==undefined){Object.getOwnPropertyNames(runtimeCallStats).forEach(function(runtimeCallStatName){for(var i=1;i<groups.length;++i){if(groups[i].match(runtimeCallStatName)){var runtimeCallStat=runtimeCallStats[runtimeCallStatName];if(runtimeCallStat.length!==2)break;var entry=new Entry(runtimeCallStatName,runtimeCallStat[0],runtimeCallStat[1]);groups[0].accumulate(runtimeCallStat[0],runtimeCallStat[1]);groups[i].add(entry);break;}}},this);}},this);this.totalTime_=groups[0].time;if(this.totalTime_>0){this.constructTable_();this.table_.tableRows=groups;this.table_.rebuild();}}});return{};});'use strict';Polymer({is:'tr-ui-e-multi-v8-thread-slice-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],get selection(){return this.$.content.selection;},set selection(selection){this.$.runtimeCallStats.slices=selection;this.$.content.selection=selection;}});tr.ui.analysis.AnalysisSubView.register('tr-ui-e-multi-v8-thread-slice-sub-view',tr.e.v8.V8ThreadSlice,{multi:true,title:'V8 slices'});'use strict';Polymer({is:'tr-ui-e-single-v8-thread-slice-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],get selection(){return this.$.content.selection;},set selection(selection){this.$.runtimeCallStats.slices=selection;this.$.content.selection=selection;}});tr.ui.analysis.AnalysisSubView.register('tr-ui-e-single-v8-thread-slice-sub-view',tr.e.v8.V8ThreadSlice,{multi:false,title:'V8 slice'});'use strict';tr.exportTo('tr.c',function(){function ScriptingObject(){}
-ScriptingObject.prototype={onModelChanged:function(model){}};return{ScriptingObject:ScriptingObject};});'use strict';tr.exportTo('tr.c',function(){function ScriptingController(brushingStateController){this.brushingStateController_=brushingStateController;this.scriptObjectNames_=[];this.scriptObjectValues_=[];this.brushingStateController.addEventListener('model-changed',this.onModelChanged_.bind(this));var typeInfos=ScriptingObjectRegistry.getAllRegisteredTypeInfos();typeInfos.forEach(function(typeInfo){this.addScriptObject(typeInfo.metadata.name,typeInfo.constructor);global[typeInfo.metadata.name]=typeInfo.constructor;},this);}
+return currentY;},drawStatNames_:function(leftView,height,currentY,prefix,maxStats){var ctx=this.context();ctx.textAlign='end';ctx.font='12px Arial';ctx.fillStyle='#000000';for(var statName in maxStats){if(maxStats[statName]instanceof Object){currentY=this.drawStatNames_(leftView,height,currentY,statName,maxStats[statName]);}else{var fullname=statName;if(prefix!==''){fullname=prefix+' :: '+statName;}
+ctx.fillText(fullname,leftView-10,currentY-height/4);currentY+=height;}}
+return currentY;}};tr.ui.tracks.ObjectInstanceTrack.register(SystemStatsInstanceTrack,{typeName:'base::TraceEventSystemStatsMonitor::SystemStats'});return{SystemStatsInstanceTrack,};});'use strict';tr.exportTo('tr.ui.e.system_stats',function(){var SystemStatsSnapshotView=tr.ui.b.define('tr-ui-e-system-stats-snapshot-view',tr.ui.analysis.ObjectSnapshotView);SystemStatsSnapshotView.prototype={__proto__:tr.ui.analysis.ObjectSnapshotView.prototype,decorate:function(){Polymer.dom(this).classList.add('tr-ui-e-system-stats-snapshot-view');},updateContents:function(){var snapshot=this.objectSnapshot_;if(!snapshot||!snapshot.getStats()){Polymer.dom(this).textContent='No system stats snapshot found.';return;}
+Polymer.dom(this).textContent='';var stats=snapshot.getStats();Polymer.dom(this).appendChild(this.buildList_(stats));},isFloat:function(n){return typeof n==='number'&&n%1!==0;},buildList_:function(stats){var statList=document.createElement('ul');for(var statName in stats){var statText=document.createElement('li');Polymer.dom(statText).textContent=''+statName+': ';Polymer.dom(statList).appendChild(statText);if(stats[statName]instanceof Object){Polymer.dom(statList).appendChild(this.buildList_(stats[statName]));}else{if(this.isFloat(stats[statName])){Polymer.dom(statText).textContent+=stats[statName].toFixed(2);}else{Polymer.dom(statText).textContent+=stats[statName];}}}
+return statList;}};tr.ui.analysis.ObjectSnapshotView.register(SystemStatsSnapshotView,{typeName:'base::TraceEventSystemStatsMonitor::SystemStats'});return{SystemStatsSnapshotView,};});'use strict';tr.exportTo('tr.ui.e.v8',function(){var InstanceTypeGroups={Rest:['ACCESSOR_INFO_TYPE','ACCESSOR_PAIR_TYPE','ACCESS_CHECK_INFO_TYPE','ALLOCATION_MEMENTO_TYPE','ALLOCATION_SITE_TYPE','CALL_HANDLER_INFO_TYPE','CELL_TYPE','FIXED_INT8_ARRAY_TYPE','FIXED_UINT8_ARRAY_TYPE','FIXED_UINT8_CLAMPED_ARRAY_TYPE','FIXED_INT16_ARRAY_TYPE','FIXED_UINT16_ARRAY_TYPE','FIXED_INT32_ARRAY_TYPE','FIXED_UINT32_ARRAY_TYPE','FIXED_FLOAT32_ARRAY_TYPE','FIXED_FLOAT64_ARRAY_TYPE','FIXED_DOUBLE_ARRAY_TYPE','FOREIGN_TYPE','FUNCTION_TEMPLATE_INFO_TYPE','HEAP_NUMBER_TYPE','INTERCEPTOR_INFO_TYPE','MUTABLE_HEAP_NUMBER_TYPE','OBJECT_TEMPLATE_INFO_TYPE','ODDBALL_TYPE','PROPERTY_CELL_TYPE','PROTOTYPE_INFO_TYPE','SCRIPT_TYPE','SYMBOL_TYPE','TRANSITION_ARRAY_TYPE','TYPE_FEEDBACK_INFO_TYPE'],Strings:['CONS_ONE_BYTE_STRING_TYPE','CONS_STRING_TYPE','EXTERNAL_ONE_BYTE_INTERNALIZED_STRING_TYPE','EXTERNAL_ONE_BYTE_STRING_TYPE','EXTERNAL_INTERNALIZED_STRING_TYPE','EXTERNAL_STRING_TYPE','INTERNALIZED_STRING_TYPE','ONE_BYTE_INTERNALIZED_STRING_TYPE','ONE_BYTE_STRING_TYPE','SHORT_EXTERNAL_INTERNALIZED_STRING_TYPE','SHORT_EXTERNAL_ONE_BYTE_STRING_TYPE','SHORT_EXTERNAL_ONE_BYTE_INTERNALIZED_STRING_TYPE','SHORT_EXTERNAL_STRING_TYPE','SLICED_ONE_BYTE_STRING_TYPE','SLICED_STRING_TYPE','STRING_TYPE'],JS_OTHER:['JS_API_OBJECT_TYPE','JS_ARGUMENTS_TYPE','JS_ARRAY_BUFFER_TYPE','JS_ARRAY_TYPE','JS_BOUND_FUNCTION_TYPE','JS_ERROR_TYPE','JS_DATE_TYPE','JS_FUNCTION_TYPE','JS_GLOBAL_OBJECT_TYPE','JS_GLOBAL_PROXY_TYPE','JS_MAP_ITERATOR_TYPE','JS_MAP_TYPE','JS_MESSAGE_OBJECT_TYPE','JS_PROMISE_TYPE','JS_REGEXP_TYPE','JS_SPECIAL_API_OBJECT_TYPE','JS_TYPED_ARRAY_TYPE','JS_VALUE_TYPE','JS_WEAK_MAP_TYPE'],FIXED_ARRAY_TYPE:['*FIXED_ARRAY_CODE_STUBS_TABLE_SUB_TYPE','*FIXED_ARRAY_COMPILATION_CACHE_TABLE_SUB_TYPE','*FIXED_ARRAY_CONTEXT_SUB_TYPE','*FIXED_ARRAY_COPY_ON_WRITE_SUB_TYPE','*FIXED_ARRAY_DEOPTIMIZATION_DATA_SUB_TYPE','*FIXED_ARRAY_DESCRIPTOR_ARRAY_SUB_TYPE','*FIXED_ARRAY_EMBEDDED_OBJECT_SUB_TYPE','*FIXED_ARRAY_ENUM_CACHE_SUB_TYPE','*FIXED_ARRAY_ENUM_INDICES_CACHE_SUB_TYPE','*FIXED_ARRAY_DEPENDENT_CODE_SUB_TYPE','*FIXED_ARRAY_DICTIONARY_ELEMENTS_SUB_TYPE','*FIXED_ARRAY_DICTIONARY_PROPERTIES_SUB_TYPE','*FIXED_ARRAY_EMPTY_PROPERTIES_DICTIONARY_SUB_TYPE','*FIXED_ARRAY_FAST_ELEMENTS_SUB_TYPE','*FIXED_ARRAY_FAST_PROPERTIES_SUB_TYPE','*FIXED_ARRAY_HANDLER_TABLE_SUB_TYPE','*FIXED_ARRAY_INTRINSIC_FUNCTION_NAMES_SUB_TYPE','*FIXED_ARRAY_JS_COLLECTION_SUB_TYPE','*FIXED_ARRAY_JS_WEAK_COLLECTION_SUB_TYPE','*FIXED_ARRAY_LITERALS_ARRAY_SUB_TYPE','*FIXED_ARRAY_MAP_CODE_CACHE_SUB_TYPE','*FIXED_ARRAY_NOSCRIPT_SHARED_FUNCTION_INFOS_SUB_TYPE','*FIXED_ARRAY_NUMBER_STRING_CACHE_SUB_TYPE','*FIXED_ARRAY_OBJECT_TO_CODE_SUB_TYPE','*FIXED_ARRAY_OPTIMIZED_CODE_LITERALS_TUB_TYPE','*FIXED_ARRAY_OPTIMIZED_CODE_MAP_SUB_TYPE','*FIXED_ARRAY_PROTOTYPE_USERS_SUB_TYPE','*FIXED_ARRAY_REGEXP_MULTIPLE_CACHE_SUB_TYPE','*FIXED_ARRAY_RETAINED_MAPS_SUB_TYPE','*FIXED_ARRAY_SCOPE_INFO_SUB_TYPE','*FIXED_ARRAY_SCRIPT_LIST_SUB_TYPE','*FIXED_ARRAY_SERIALIZED_TEMPLATES_SUB_TYPE','*FIXED_ARRAY_SHARED_FUNCTION_INFOS_SUB_TYPE','*FIXED_ARRAY_SINGLE_CHARACTER_STRING_CACHE_SUB_TYPE','*FIXED_ARRAY_STRING_SPLIT_CACHE_SUB_TYPE','*FIXED_ARRAY_STRING_TABLE_SUB_TYPE','*FIXED_ARRAY_TEMPLATE_INFO_SUB_TYPE','*FIXED_ARRAY_TEMPLATE_INSTANTIATIONS_CACHE_SUB_TYPE','*FIXED_ARRAY_TYPE_FEEDBACK_VECTOR_SUB_TYPE','*FIXED_ARRAY_TYPE_FEEDBACK_METADATA_SUB_TYPE','*FIXED_ARRAY_WEAK_NEW_SPACE_OBJECT_TO_CODE_SUB_TYPE','*FIXED_ARRAY_UNKNOWN_SUB_TYPE'],CODE_TYPE:['*CODE_FUNCTION','*CODE_OPTIMIZED_FUNCTION','*CODE_BYTECODE_HANDLER','*CODE_STUB','*CODE_HANDLER','*CODE_BUILTIN','*CODE_REGEXP','*CODE_WASM_FUNCTION','*CODE_WASM_TO_JS_FUNCTION','*CODE_JS_TO_WASM_FUNCTION','*CODE_LOAD_IC','*CODE_LOAD_GLOBAL_IC','*CODE_KEYED_LOAD_IC','*CODE_CALL_IC','*CODE_STORE_IC','*CODE_KEYED_STORE_IC','*CODE_BINARY_OP_IC','*CODE_COMPARE_IC','*CODE_TO_BOOLEAN_IC'],CONTEXT_EXTENSION_TYPE:['CONTEXT_EXTENSION_TYPE'],MAP_TYPE:['MAP_TYPE'],BYTE_ARRAY_TYPE:['BYTE_ARRAY_TYPE'],SHARED_FUNCTION_INFO_TYPE:['SHARED_FUNCTION_INFO_TYPE'],WEAK_CELL_TYPE:['WEAK_CELL_TYPE'],JS_OBJECT_TYPE:['JS_OBJECT_TYPE'],JS_CONTEXT_EXTENSION_OBJECT_TYPE:['JS_CONTEXT_EXTENSION_OBJECT_TYPE']};var InstanceSubTypeNames={FIXED_ARRAY_TYPE:{keyToName:key=>key.slice('*FIXED_ARRAY_'.length).slice(0,-('_SUB_TYPE'.length)),nameToKey:name=>'*FIXED_ARRAY_'+name+'_SUB_TYPE'},CODE_TYPE:{keyToName:key=>key.slice('*CODE_'.length),nameToKey:name=>'*CODE_'+name},Strings:{keyToName:key=>key,nameToKey:name=>name},Rest:{keyToName:key=>key,nameToKey:name=>name},JS_OTHER:{keyToName:key=>key,nameToKey:name=>name}};var DIFF_COLOR={GREEN:'#64DD17',RED:'#D50000'};function computePercentage(valueA,valueB){if(valueA===0)return 0;return valueA/valueB*100;}
+class DiffEntry{constructor(originalEntry,diffEntry){this.originalEntry_=originalEntry;this.diffEntry_=diffEntry;}
+get title(){return this.diffEntry_.title;}
+get overall(){return this.diffEntry_.overall;}
+get overAllocated(){return this.diffEntry_.overAllocated;}
+get count(){return this.diffEntry_.count;}
+get overallPercent(){return this.diffEntry_.overallPercent;}
+get overAllocatedPercent(){return this.diffEntry_.overAllocatedPercent;}
+get origin(){return this.originalEntry_;}
+get diff(){return this.diffEntry_;}
+get subRows(){return this.diffEntry_.subRows;}}
+class Entry{constructor(title,count,overall,overAllocated,histogram,overAllocatedHistogram){this.title_=title;this.overall_=overall;this.count_=count;this.overAllocated_=overAllocated;this.histogram_=histogram;this.overAllocatedHistogram_=overAllocatedHistogram;this.bucketSize_=this.histogram_.length;this.overallPercent_=100;this.overAllocatedPercent_=100;}
+get title(){return this.title_;}
+get overall(){return this.overall_;}
+get count(){return this.count_;}
+get overAllocated(){return this.overAllocated_;}
+get histogram(){return this.histogram_;}
+get overAllocatedHistogram(){return this.overAllocatedHistogram_;}
+get bucketSize(){return this.bucketSize_;}
+get overallPercent(){return this.overallPercent_;}
+set overallPercent(value){this.overallPercent_=value;}
+get overAllocatedPercent(){return this.overAllocatedPercent_;}
+set overAllocatedPercent(value){this.overAllocatedPercent_=value;}
+setFromObject(obj){this.count_=obj.count;this.overall_=obj.overall/1024;this.overAllocated_=obj.over_allocated/1024;this.histogram_=obj.histogram;this.overAllocatedHistogram_=obj.over_allocated_histogram;}
+diff(other){var entry=new Entry(this.title_,other.count_-this.count,other.overall_-this.overall,other.overAllocated_-this.overAllocated,[],[]);entry.overallPercent=computePercentage(entry.overall,this.overall);entry.overAllocatedPercent=computePercentage(entry.overAllocated,this.overAllocated);return new DiffEntry(this,entry);}}
+class GroupedEntry extends Entry{constructor(title,count,overall,overAllocated,histogram,overAllocatedHistogram){super(title,count,overall,overAllocated,histogram,overAllocatedHistogram);this.histogram_.fill(0);this.overAllocatedHistogram_.fill(0);this.entries_=new Map();}
+get title(){return this.title_;}
+set title(value){this.title_=value;}
+get subRows(){return Array.from(this.entries_.values());}
+getEntryFromTitle(title){return this.entries_.get(title);}
+add(entry){this.count_+=entry.count;this.overall_+=entry.overall;this.overAllocated_+=entry.overAllocated;if(this.bucketSize_===entry.bucketSize){for(var i=0;i<this.bucketSize_;++i){this.histogram_[i]+=entry.histogram[i];this.overAllocatedHistogram_[i]+=entry.overAllocatedHistogram[i];}}
+this.entries_.set(entry.title,entry);}
+accumulateUnknown(title){var unknownCount=this.count_;var unknownOverall=this.overall_;var unknownOverAllocated=this.overAllocated_;var unknownHistogram=tr.b.deepCopy(this.histogram_);var unknownOverAllocatedHistogram=tr.b.deepCopy(this.overAllocatedHistogram_);for(var entry of this.entries_.values()){unknownCount-=entry.count;unknownOverall-=entry.overall;unknownOverAllocated-=entry.overAllocated;for(var i=0;i<this.bucketSize_;++i){unknownHistogram[i]-=entry.histogram[i];unknownOverAllocatedHistogram[i]-=entry.overAllocatedHistogram[i];}}
+unknownOverAllocated=unknownOverAllocated<0?0:unknownOverAllocated;this.entries_.set(title,new Entry(title,unknownCount,unknownOverall,unknownOverAllocated,unknownHistogram,unknownOverAllocatedHistogram));}
+calculatePercentage(){for(var entry of this.entries_.values()){entry.overallPercent=computePercentage(entry.overall,this.overall_);entry.overAllocatedPercent=computePercentage(entry.overAllocated,this.overAllocated_);if(entry instanceof GroupedEntry)entry.calculatePercentage();}}
+diff(other){if(this.title_.startsWith('Isolate')){var newTitle='Total';}else{var newTitle=this.title_;}
+var result=new GroupedEntry(newTitle,0,0,0,[],[]);for(var entry of this.entries_){var otherEntry=other.getEntryFromTitle(entry[0]);if(otherEntry===undefined)continue;result.add(entry[1].diff(otherEntry));}
+result.overallPercent=computePercentage(result.overall,this.overall);result.overAllocatedPercent=computePercentage(result.overAllocated,this.overAllocated);return new DiffEntry(this,result);}}
+function createSelector(targetEl,defaultValue,items,callback){var selectorEl=document.createElement('select');selectorEl.addEventListener('change',callback.bind(targetEl));var defaultOptionEl=document.createElement('option');for(var i=0;i<items.length;i++){var item=items[i];var optionEl=document.createElement('option');Polymer.dom(optionEl).textContent=item.label;optionEl.targetPropertyValue=item.value;optionEl.item=item;Polymer.dom(selectorEl).appendChild(optionEl);}
+selectorEl.__defineGetter__('selectedValue',function(v){if(selectorEl.children[selectorEl.selectedIndex]===undefined){return undefined;}
+return selectorEl.children[selectorEl.selectedIndex].targetPropertyValue;});selectorEl.__defineGetter__('selectedItem',function(v){if(selectorEl.children[selectorEl.selectedIndex]===undefined){return undefined;}
+return selectorEl.children[selectorEl.selectedIndex].item;});selectorEl.__defineSetter__('selectedValue',function(v){for(var i=0;i<selectorEl.children.length;i++){var value=selectorEl.children[i].targetPropertyValue;if(value===v){var changed=selectorEl.selectedIndex!==i;if(changed){selectorEl.selectedIndex=i;callback();}
+return;}}
+throw new Error('Not a valid value');});selectorEl.selectedIndex=-1;return selectorEl;}
+function plusMinus(value,toFixed=3){return(value>0?'+':'')+value.toFixed(toFixed);}
+function addArrow(value){if(value===0)return value;if(value===Number.NEGATIVE_INFINITY)return'\u2193\u221E';if(value===Number.POSITIVE_INFINITY)return'\u2191\u221E';return(value>0?'\u2191':'\u2193')+Math.abs(value.toFixed(3));}
+Polymer({is:'tr-ui-e-v8-gc-objects-stats-table',ready:function(){this.$.diffOption.style.display='none';this.isolateEntries_=[];this.selector1_=undefined;this.selector2_=undefined;},constructDiffTable_:function(table){this.$.diffTable.selectionMode=tr.ui.b.TableFormat.SelectionMode.ROW;this.$.diffTable.tableColumns=[{title:'Component',value:function(row){var typeEl=document.createElement('span');typeEl.innerText=row.title;return typeEl;},showExpandButtons:true},{title:'Overall Memory(KB)',value:function(row){var spanEl=tr.ui.b.createSpan();spanEl.innerText=row.origin.overall.toFixed(3);return spanEl;},cmp:function(a,b){return a.origin.overall-b.origin.overall;}},{title:'diff(KB)',value:function(row){var spanEl=tr.ui.b.createSpan();spanEl.innerText=plusMinus(row.overall);if(row.overall>0){spanEl.style.color=DIFF_COLOR.RED;}else if(row.overall<0){spanEl.style.color=DIFF_COLOR.GREEN;}
+return spanEl;},cmp:function(a,b){return a.overall-b.overall;}},{title:'diff(%)',value:function(row){var spanEl=tr.ui.b.createSpan();spanEl.innerText=addArrow(row.overallPercent);if(row.overall>0){spanEl.style.color=DIFF_COLOR.RED;}else if(row.overall<0){spanEl.style.color=DIFF_COLOR.GREEN;}
+return spanEl;},cmp:function(a,b){return a.overall-b.overall;}},{title:'Over Allocated Memory(KB)',value:function(row){var spanEl=tr.ui.b.createSpan();spanEl.innerText=row.origin.overAllocated.toFixed(3);return spanEl;},cmp:function(a,b){return a.origin.overAllocated-b.origin.overAllocated;}},{title:'diff(KB)',value:function(row){var spanEl=tr.ui.b.createSpan();spanEl.innerText=plusMinus(row.overAllocated);if(row.overAllocated>0){spanEl.style.color=DIFF_COLOR.RED;}else if(row.overAllocated<0){spanEl.style.color=DIFF_COLOR.GREEN;}
+return spanEl;},cmp:function(a,b){return a.overAllocated-b.overAllocated;}},{title:'diff(%)',value:function(row){var spanEl=tr.ui.b.createSpan();spanEl.innerText=addArrow(row.overAllocatedPercent);if(row.overAllocated>0){spanEl.style.color=DIFF_COLOR.RED;}else if(row.overAllocated<0){spanEl.style.color=DIFF_COLOR.GREEN;}
+return spanEl;},cmp:function(a,b){return a.overAllocated-b.overAllocated;}},{title:'Count',value:function(row){var spanEl=tr.ui.b.createSpan();spanEl.innerText=row.origin.count;return spanEl;},cmp:function(a,b){return a.origin.count-b.origin.count;}},{title:'diff',value:function(row){var spanEl=tr.ui.b.createSpan();spanEl.innerText=plusMinus(row.count,0);if(row.count>0){spanEl.style.color=DIFF_COLOR.RED;}else if(row.count<0){spanEl.style.color=DIFF_COLOR.GREEN;}
+return spanEl;},cmp:function(a,b){return a.count-b.count;}},];},buildOptions_:function(){var items=[];for(var isolateEntry of this.isolateEntries_){items.push({label:isolateEntry.title,value:isolateEntry});}
+this.$.diffOption.style.display='inline-block';this.selector1_=createSelector(this,'',items,this.diffOptionChanged_);Polymer.dom(this.$.diffOption).appendChild(this.selector1_);var spanEl=tr.ui.b.createSpan();spanEl.innerText=' VS ';Polymer.dom(this.$.diffOption).appendChild(spanEl);this.selector2_=createSelector(this,'',items,this.diffOptionChanged_);Polymer.dom(this.$.diffOption).appendChild(this.selector2_);},diffOptionChanged_:function(){var isolateEntry1=this.selector1_.selectedValue;var isolateEntry2=this.selector2_.selectedValue;if(isolateEntry1===undefined||isolateEntry2===undefined){return;}
+if(isolateEntry1===isolateEntry2){this.$.diffTable.tableRows=[];this.$.diffTable.rebuild();return;}
+this.$.diffTable.tableRows=[isolateEntry1.diff(isolateEntry2)];this.$.diffTable.rebuild();},constructTable_:function(){this.$.table.selectionMode=tr.ui.b.TableFormat.SelectionMode.ROW;this.$.table.tableColumns=[{title:'Component',value:function(row){var typeEl=document.createElement('span');typeEl.innerText=row.title;return typeEl;},showExpandButtons:true},{title:'Overall Memory (KB)',value:function(row){var typeEl=document.createElement('span');typeEl.innerText=row.overall.toFixed(3);return typeEl;},cmp:function(a,b){return a.overall-b.overall;}},{title:'Over Allocated Memory (KB)',value:function(row){var typeEl=document.createElement('span');typeEl.innerText=row.overAllocated.toFixed(3);return typeEl;},cmp:function(a,b){return a.overAllocated-b.overAllocated;}},{title:'Overall Count',value:function(row){var typeEl=document.createElement('span');typeEl.innerText=row.count;return typeEl;},cmp:function(a,b){return a.count-b.count;}},{title:'Overall Memory Percent',value:function(row){var typeEl=document.createElement('span');typeEl.innerText=row.overallPercent.toFixed(3)+'%';return typeEl;},cmp:function(a,b){return a.overall-b.overall;}},{title:'Overall Allocated Memory Percent',value:function(row){var typeEl=document.createElement('span');typeEl.innerText=row.overAllocatedPercent.toFixed(3)+'%';return typeEl;},cmp:function(a,b){return a.overAllocated-b.overAllocated;}}];this.$.table.sortColumnIndex=1;this.$.table.sortDescending=true;},buildSubEntry_:function(objects,groupEntry,keyToName){var typeGroup=InstanceTypeGroups[groupEntry.title];for(var instanceType of typeGroup){var e=objects[instanceType];delete objects[instanceType];if(e===undefined)continue;var title=instanceType;if(keyToName!==undefined)title=keyToName(title);groupEntry.add(new Entry(title,e.count,e.overall/1024,e.over_allocated/1024,e.histogram,e.over_allocated_histogram));}},buildOthers_:function(objects,groupEntry){for(var title of Object.getOwnPropertyNames(objects)){if(title==='END')continue;var obj=objects[title];groupEntry.add(new Entry(title,obj.count,obj.overall,obj.over_allocated,obj.histogram,obj.over_allocated_histogram));}},build_:function(objects,objectEntry,bucketSize){var fixedArrayObject=objects['FIXED_ARRAY_TYPE'];if(fixedArrayObject===undefined){throw new Error('Fixed Array Object not found.');}
+var groupEntries={restEntry:new GroupedEntry('Rest',0,0,0,new Array(bucketSize),new Array(bucketSize)),stringEntry:new GroupedEntry('Strings',0,0,0,new Array(bucketSize),new Array(bucketSize)),jsEntry:new GroupedEntry('JS_OTHER',0,0,0,new Array(bucketSize),new Array(bucketSize)),fixedArrayEntry:new GroupedEntry('FIXED_ARRAY_TYPE',0,0,0,new Array(bucketSize),new Array(bucketSize)),codeEntry:new GroupedEntry('CODE_TYPE',0,0,0,new Array(bucketSize),new Array(bucketSize)),contextExtensionEntry:new GroupedEntry('CONTEXT_EXTENSION_TYPE',0,0,0,new Array(bucketSize),new Array(bucketSize)),mapEntry:new GroupedEntry('MAP_TYPE',0,0,0,new Array(bucketSize),new Array(bucketSize)),byteArrayEntry:new GroupedEntry('BYTE_ARRAY_TYPE',0,0,0,new Array(bucketSize),new Array(bucketSize)),sharedFunctionInfoEntry:new GroupedEntry('SHARED_FUNCTION_INFO_TYPE',0,0,0,new Array(bucketSize),new Array(bucketSize)),weakCellEntry:new GroupedEntry('WEAK_CELL_TYPE',0,0,0,new Array(bucketSize),new Array(bucketSize)),jsObjectEntry:new GroupedEntry('JS_OBJECT_TYPE',0,0,0,new Array(bucketSize),new Array(bucketSize)),jsContextExtensionObjectEntry:new GroupedEntry('JS_CONTEXT_EXTENSION_OBJECT_TYPE',0,0,0,new Array(bucketSize),new Array(bucketSize))};for(var name of Object.getOwnPropertyNames(groupEntries)){var groupEntry=groupEntries[name];var keyToName=undefined;if(InstanceSubTypeNames[groupEntry.title]!==undefined){keyToName=InstanceSubTypeNames[groupEntry.title].keyToName;}
+this.buildSubEntry_(objects,groupEntry,keyToName);if(name==='fixedArrayEntry'){groupEntry.setFromObject(fixedArrayObject);groupEntry.accumulateUnknown('UNKNOWN');}
+objectEntry.add(groupEntry);}},set selection(slices){slices.sortEvents(function(a,b){return b.start-a.start;});var previous=undefined;for(let slice of slices){if(!slice instanceof tr.e.v8.V8GCStatsThreadSlice)continue;var liveObjects=slice.liveObjects;var deadObjects=slice.deadObjects;var isolate=liveObjects.isolate;var isolateEntry=new GroupedEntry('Isolate_'+isolate+' at '+slice.start.toFixed(3)+' ms',0,0,0,[],[]);var liveEntry=new GroupedEntry('live objects',0,0,0,[],[]);var deadEntry=new GroupedEntry('dead objects',0,0,0,[],[]);var liveBucketSize=liveObjects.bucket_sizes.length;var deadBucketSize=deadObjects.bucket_sizes.length;this.build_(tr.b.deepCopy(liveObjects.type_data),liveEntry,liveBucketSize);isolateEntry.add(liveEntry);this.build_(tr.b.deepCopy(deadObjects.type_data),deadEntry,deadBucketSize);isolateEntry.add(deadEntry);isolateEntry.calculatePercentage();this.isolateEntries_.push(isolateEntry);}
+this.updateTable_();if(slices.length>1){this.buildOptions_();this.constructDiffTable_();}},updateTable_:function(){this.constructTable_();this.$.table.tableRows=this.isolateEntries_;this.$.table.rebuild();},});return{};});'use strict';Polymer({is:'tr-ui-e-multi-v8-gc-stats-thread-slice-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],get selection(){return this.$.content.selection;},set selection(selection){this.$.gcObjectsStats.selection=selection;}});tr.ui.analysis.AnalysisSubView.register('tr-ui-e-multi-v8-gc-stats-thread-slice-sub-view',tr.e.v8.V8GCStatsThreadSlice,{multi:true,title:'V8 GC Stats slices'});'use strict';tr.exportTo('tr.e.v8',function(){var IC_STATS_PROPERTIES=['type','category','scriptName','filePosition','state','isNative','map','propertiesMode','numberOfOwnProperties','instanceType'];class ICStatsEntry{constructor(obj){this.type_=obj.type;if(this.type_.includes('Store')){this.category_='Store';}else if(this.type_.includes('Load')){this.category_='Load';}
+this.state_=obj.state;if(obj.functionName){this.functionName_=obj.optimized?'*':'~';this.functionName_+=obj.functionName.length===0?'(anonymous function)':obj.functionName;}
+this.offset_=obj.offset;this.scriptName_=obj.scriptName?obj.scriptName:'unknown';this.isNative_=obj.scriptName&&obj.scriptName.includes('native');this.lineNum_=obj.lineNum?obj.lineNum:'unknown';this.filePosition_=this.scriptName_+':'+this.lineNum_;if(this.functionName_){this.filePosition_+=' '+this.functionName_+'+'+this.offset_;}
+this.constructor_=obj.constructor?false:true;this.map_=obj.map;if(this.map_){this.propertiesMode_=obj.dict===0?'slow':'fast';}else{this.propertiesMode_='unknown';}
+this.numberOfOwnProperties_=obj.own;this.instanceType_=obj.instanceType;this.key_=obj.key;}
+get type(){return this.type_;}
+get category(){return this.category_;}
+get state(){return this.state_;}
+get functionName(){return this.functionName_;}
+get offset(){return this.offset_;}
+get scriptName(){return this.scriptName_;}
+get isNative(){return this.isNative_;}
+get lineNumber(){return this.lineNum_;}
+get isConstructor(){return this.constructor_;}
+get map(){return this.map_;}
+get propertiesMode(){return this.propertiesMode_;}
+get numberOfOwnProperties(){return this.numberOfOwnProperties_;}
+get instanceType(){return this.instanceType_;}
+get filePosition(){return this.filePosition_;}}
+class ICStatsEntryGroup{constructor(property,key){this.property_=property;this.key_=key;this.percentage_=0;this.entries_=[];this.subGroup_=undefined;}
+static groupBy(groups,entries,property){for(let entry of entries){let key=entry[property];let group=groups.get(key);if(!group){group=new ICStatsEntryGroup(property,key);groups.set(key,group);}
+group.add(entry);}
+for(let group of groups.values()){group.percentage=group.length/entries.length;}}
+add(entry){this.entries_.push(entry);}
+createSubGroup(){if(this.subGroup_)return this.subGroup_;this.subGroup_=new Map();for(let property of IC_STATS_PROPERTIES){if(property===this.property_)continue;var groups=new Map();this.subGroup_.set(property,groups);ICStatsEntryGroup.groupBy(groups,this.entries_,property);}
+return this.subGroup_;}
+get entries(){return this.entries_;}
+get key(){return this.key_;}
+get length(){return this.entries_.length;}
+get percentage(){return this.percentage_;}
+set percentage(value){this.percentage_=value;}}
+class ICStatsCollection{constructor(){this.entries_=[];this.groupedEntries_=new Map();}
+add(entry){this.entries_.push(entry);}
+groupBy(property){if(this.groupedEntries_.has(property)){return Array.from(this.groupedEntries_.get(property).values());}
+var groups=new Map();this.groupedEntries_.set(property,groups);ICStatsEntryGroup.groupBy(groups,this.entries_,property);return Array.from(groups.values());}
+get entries(){return this.entries_;}
+get length(){return this.entries_.length;}}
+return{IC_STATS_PROPERTIES,ICStatsEntry,ICStatsEntryGroup,ICStatsCollection,};});'use strict';tr.exportTo('tr.ui.e.v8',function(){var PROPERTIES=tr.e.v8.IC_STATS_PROPERTIES.map(x=>{return{label:x,value:x};});var ICStatsEntry=tr.e.v8.ICStatsEntry;var ICStatsEntryGroup=tr.e.v8.ICStatsEntryGroup;var ICStatsCollection=tr.e.v8.ICStatsCollection;Polymer({is:'tr-ui-e-v8-ic-stats-table',ready:function(){this.icStatsCollection_=new ICStatsCollection();this.groupKey_=PROPERTIES[0].value;this.selector_=tr.ui.b.createSelector(this,'groupKey','v8ICStatsGroupKey',this.groupKey_,PROPERTIES);Polymer.dom(this.$.groupOption).appendChild(this.selector_);},get groupKey(){return this.groupKey_;},set groupKey(key){this.groupKey_=key;if(this.icStatsCollection_.length===0)return;this.updateTable_(this.groupKey_);},constructTable_:function(table,groupKey){table.tableColumns=[{title:'',value:row=>{let expanded=false;let buttonEl=tr.ui.b.createButton('details',function(){let previousSibling=Polymer.dom(this).parentNode.parentNode;let parentNode=previousSibling.parentNode;if(expanded){let trEls=parentNode.getElementsByClassName('subTable');Array.from(trEls).map(x=>x.parentNode.removeChild(x));expanded=false;return;}
+expanded=true;let subGroups=row.createSubGroup();let tr=document.createElement('tr');tr.classList.add('subTable');tr.appendChild(document.createElement('td'));let td=document.createElement('td');td.colSpan=3;for(let subGroup of subGroups){let property=subGroup[0];let all=Array.from(subGroup[1].values());let group=all.slice(0,20);let divEl=document.createElement('div');let spanEl=document.createElement('span');let subTableEl=document.createElement('tr-ui-b-table');spanEl.innerText=`Top 20 out of ${all.length}`;spanEl.style.fontWeight='bold';spanEl.style.fontSize='14px';divEl.appendChild(spanEl);this.constructTable_(subTableEl,property);subTableEl.tableRows=group;subTableEl.rebuild();divEl.appendChild(subTableEl);td.appendChild(divEl);}
+tr.appendChild(td);parentNode.insertBefore(tr,previousSibling.nextSibling);});return buttonEl;}},{title:'Percentage',value:function(row){let spanEl=document.createElement('span');spanEl.innerText=(row.percentage*100).toFixed(3)+'%';return spanEl;},cmp:(a,b)=>a.percentage-b.percentage},{title:'Count',value:function(row){let spanEl=document.createElement('span');spanEl.innerText=row.length;return spanEl;},cmp:(a,b)=>a.length-b.length},{title:groupKey,value:function(row){let spanEl=document.createElement('span');spanEl.innerText=row.key?row.key:'';return spanEl;}}];table.sortColumnIndex=1;table.sortDescending=true;},updateTable_:function(groupKey){this.constructTable_(this.$.table,groupKey);this.$.table.tableRows=this.icStatsCollection_.groupBy(groupKey);this.$.table.rebuild();},set selection(slices){for(let slice of slices){for(let icStatsObj of slice.icStats){let entry=new ICStatsEntry(icStatsObj);this.icStatsCollection_.add(entry);}}
+this.$.total.innerText='Total items: '+this.icStatsCollection_.length;this.updateTable_(this.selector_.selectedValue);}});return{};});'use strict';Polymer({is:'tr-ui-e-multi-v8-ic-stats-thread-slice-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],get selection(){return this.$.content.selection;},set selection(selection){this.$.table.selection=selection;}});tr.ui.analysis.AnalysisSubView.register('tr-ui-e-multi-v8-ic-stats-thread-slice-sub-view',tr.e.v8.V8ICStatsThreadSlice,{multi:true,title:'V8 IC stats slices'});'use strict';tr.exportTo('tr.e.v8',function(){class RuntimeStatsEntry{constructor(name,count,time){this.name_=name;this.count_=count;this.time_=time;}
+get name(){return this.name_;}
+get count(){return this.count_;}
+get time(){return this.time_;}
+addSample(count,time){this.count_+=count;this.time_+=time;}}
+class RuntimeStatsGroup extends RuntimeStatsEntry{constructor(name,matchRegex){super(name,0,0);this.regex_=matchRegex;this.entries_=new Map();}
+match(name){return this.regex_&&name.match(this.regex_);}
+add(entry){var value=this.entries_.get(entry.name);if(value!==undefined){value.addSample(entry.count,entry.time);}else{this.entries_.set(entry.name,entry);}
+this.count_+=entry.count;this.time_+=entry.time;}
+get values(){return Array.from(this.entries_.values());}}
+class RuntimeStatsGroupCollection{constructor(){this.groups_=[new RuntimeStatsGroup('Total'),new RuntimeStatsGroup('IC',/.*IC_.*/),new RuntimeStatsGroup('Optimize',/StackGuard|.*Optimize.*|.*Deoptimize.*|Recompile.*/),new RuntimeStatsGroup('Compile-Background',/(.*CompileBackground.*)/),new RuntimeStatsGroup('Compile',/(^Compile.*)|(.*_Compile.*)/),new RuntimeStatsGroup('Parse-Background',/.*ParseBackground.*/),new RuntimeStatsGroup('Parse',/.*Parse.*/),new RuntimeStatsGroup('Blink C++',/.*Callback.*/),new RuntimeStatsGroup('API',/.*API.*/),new RuntimeStatsGroup('GC',/GC|AllocateInTargetSpace/),new RuntimeStatsGroup('JavaScript',/JS_Execution/),new RuntimeStatsGroup('V8 C++',/.*/)];}
+addSlices(slices){for(var slice of slices){if(!(slice instanceof tr.e.v8.V8ThreadSlice))return;try{var runtimeCallStats=JSON.parse(slice.runtimeCallStats);}catch(e){var runtimeCallStats=slice.runtimeCallStats;}
+if(runtimeCallStats===undefined)continue;for(var[name,stat]of Object.entries(runtimeCallStats)){for(var i=1;i<this.groups_.length;++i){if(this.groups_[i].match(name)){if(stat.length!==2)break;var entry=new RuntimeStatsEntry(name,stat[0],stat[1]);this.groups_[0].addSample(stat[0],stat[1]);this.groups_[i].add(entry);break;}}}}}
+get totalTime(){return this.groups_[0].time;}
+get runtimeGroups(){return this.groups_;}}
+return{RuntimeStatsEntry,RuntimeStatsGroup,RuntimeStatsGroupCollection,};});'use strict';tr.exportTo('tr.ui.e.v8',function(){function handleCodeSearch_(event){if(event.target.parentNode===undefined)return;var name=event.target.parentNode.entryName;var url='https://cs.chromium.org/search/?sq=package:chromium&type=cs&q=';if(name.startsWith('API_'))name=name.substring(4);url+=encodeURIComponent(name)+'+file:src/v8/src';window.open(url,'_blank');}
+Polymer({is:'tr-ui-e-v8-runtime-call-stats-table',ready:function(){this.table_=this.$.table;this.totalTime_=0;},constructTable_:function(totalTime){this.table_.selectionMode=tr.ui.b.TableFormat.SelectionMode.ROW;this.table_.tableColumns=[{title:'Name',value:function(row){var typeEl=document.createElement('span');typeEl.innerText=row.name;if(!(row instanceof tr.e.v8.RuntimeStatsGroup)){typeEl.title='click ? for code search';typeEl.entryName=row.name;var codeSearchEl=document.createElement('span');codeSearchEl.innerText='?';codeSearchEl.style.float='right';codeSearchEl.style.borderRadius='5px';codeSearchEl.style.backgroundColor='#EEE';codeSearchEl.addEventListener('click',handleCodeSearch_.bind(this));typeEl.appendChild(codeSearchEl);}
+return typeEl;},width:'200px',showExpandButtons:true},{title:'Time',value:function(row){var typeEl=document.createElement('span');typeEl.innerText=(row.time/1000.0).toFixed(3)+' ms';return typeEl;},width:'100px',cmp:function(a,b){return a.time-b.time;}},{title:'Count',value:function(row){var typeEl=document.createElement('span');typeEl.innerText=row.count;return typeEl;},width:'100px',cmp:function(a,b){return a.count-b.count;}},{title:'Percent',value:function(row){var typeEl=document.createElement('span');typeEl.innerText=(row.time/totalTime*100).toFixed(3)+'%';return typeEl;},width:'100px',cmp:function(a,b){return a.time-b.time;}}];this.table_.sortColumnIndex=1;this.table_.sortDescending=true;this.table_.subRowsPropertyName='values';},set slices(slices){var runtimeGroupCollection=new tr.e.v8.RuntimeStatsGroupCollection();runtimeGroupCollection.addSlices(slices);if(runtimeGroupCollection.totalTime>0){this.constructTable_(runtimeGroupCollection.totalTime);this.table_.tableRows=runtimeGroupCollection.runtimeGroups;this.table_.rebuild();}}});return{};});'use strict';Polymer({is:'tr-ui-e-multi-v8-thread-slice-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],get selection(){return this.$.content.selection;},set selection(selection){this.$.runtimeCallStats.slices=selection;this.$.content.selection=selection;}});tr.ui.analysis.AnalysisSubView.register('tr-ui-e-multi-v8-thread-slice-sub-view',tr.e.v8.V8ThreadSlice,{multi:true,title:'V8 slices'});'use strict';Polymer({is:'tr-ui-e-single-v8-gc-stats-thread-slice-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],get selection(){return this.$.content.selection;},set selection(selection){this.$.content.selection=selection;this.$.gcObjectsStats.selection=selection;}});tr.ui.analysis.AnalysisSubView.register('tr-ui-e-single-v8-gc-stats-thread-slice-sub-view',tr.e.v8.V8GCStatsThreadSlice,{multi:false,title:'V8 GC stats slice'});'use strict';Polymer({is:'tr-ui-e-single-v8-ic-stats-thread-slice-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],get selection(){return this.$.content.selection;},set selection(selection){this.$.table.selection=selection;}});tr.ui.analysis.AnalysisSubView.register('tr-ui-e-single-v8-ic-stats-thread-slice-sub-view',tr.e.v8.V8ICStatsThreadSlice,{multi:false,title:'V8 IC stats slice'});'use strict';Polymer({is:'tr-ui-e-single-v8-thread-slice-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],get selection(){return this.$.content.selection;},set selection(selection){this.$.runtimeCallStats.slices=selection;this.$.content.selection=selection;}});tr.ui.analysis.AnalysisSubView.register('tr-ui-e-single-v8-thread-slice-sub-view',tr.e.v8.V8ThreadSlice,{multi:false,title:'V8 slice'});'use strict';tr.exportTo('tr.c',function(){function ScriptingObject(){}
+ScriptingObject.prototype={onModelChanged:function(model){}};return{ScriptingObject,};});'use strict';tr.exportTo('tr.c',function(){function ScriptingController(brushingStateController){this.brushingStateController_=brushingStateController;this.scriptObjectNames_=[];this.scriptObjectValues_=[];this.brushingStateController.addEventListener('model-changed',this.onModelChanged_.bind(this));var typeInfos=ScriptingObjectRegistry.getAllRegisteredTypeInfos();typeInfos.forEach(function(typeInfo){this.addScriptObject(typeInfo.metadata.name,typeInfo.constructor);global[typeInfo.metadata.name]=typeInfo.constructor;},this);}
 function ScriptingObjectRegistry(){}
-var options=new tr.b.ExtensionRegistryOptions(tr.b.BASIC_REGISTRY_MODE);tr.b.decorateExtensionRegistry(ScriptingObjectRegistry,options);ScriptingController.prototype={get brushingStateController(){return this.brushingStateController_;},onModelChanged_:function(){this.scriptObjectValues_.forEach(function(v){if(v.onModelChanged)
-v.onModelChanged(this.brushingStateController.model);},this);},addScriptObject:function(name,value){this.scriptObjectNames_.push(name);this.scriptObjectValues_.push(value);},executeCommand:function(command){var f=new Function(this.scriptObjectNames_,'return eval('+command+')');return f.apply(null,this.scriptObjectValues_);}};return{ScriptingController:ScriptingController,ScriptingObjectRegistry:ScriptingObjectRegistry};});'use strict';tr.exportTo('tr.metrics',function(){function MetricRegistry(){}
-var options=new tr.b.ExtensionRegistryOptions(tr.b.BASIC_REGISTRY_MODE);options.defaultMetadata={};tr.b.decorateExtensionRegistry(MetricRegistry,options);MetricRegistry.addEventListener('will-register',function(e){var metric=e.typeInfo.constructor;if(!(metric instanceof Function))
-throw new Error('Metrics must be functions');if(metric.length<2){throw new Error('Metrics take a ValueSet and a Model and '+'optionally an options dictionary');}});return{MetricRegistry:MetricRegistry};});'use strict';tr.exportTo('tr.b',function(){var PERCENTILE_PRECISION=1e-7;function PiecewiseLinearFunction(){this.pieces=[];}
-PiecewiseLinearFunction.prototype={push:function(x1,y1,x2,y2){if(x1>=x2)
-throw new Error('Invalid segment');if(this.pieces.length>0&&this.pieces[this.pieces.length-1].x2>x1){throw new Error('Potentially overlapping segments');}
-if(x1<x2)
-this.pieces.push(new Piece(x1,y1,x2,y2));},partBelow:function(y){return this.pieces.reduce((acc,p)=>(acc+p.partBelow(y)),0);},get min(){return this.pieces.reduce((acc,p)=>Math.min(acc,p.min),Infinity);},get max(){return this.pieces.reduce((acc,p)=>Math.max(acc,p.max),-Infinity);},get average(){var weightedSum=0;var totalWeight=0;this.pieces.forEach(function(piece){weightedSum+=piece.width*piece.average;totalWeight+=piece.width;});if(totalWeight===0)
-return 0;return weightedSum/totalWeight;},percentile:function(percent){if(!(percent>=0&&percent<=1))
-throw new Error('percent must be [0,1]');var lower=this.min;var upper=this.max;var total=this.partBelow(upper);if(total===0)
-return 0;while(upper-lower>PERCENTILE_PRECISION){var middle=(lower+upper)/2;var below=this.partBelow(middle);if(below/total<percent)
-lower=middle;else
-upper=middle;}
+var options=new tr.b.ExtensionRegistryOptions(tr.b.BASIC_REGISTRY_MODE);tr.b.decorateExtensionRegistry(ScriptingObjectRegistry,options);ScriptingController.prototype={get brushingStateController(){return this.brushingStateController_;},onModelChanged_:function(){this.scriptObjectValues_.forEach(function(v){if(v.onModelChanged){v.onModelChanged(this.brushingStateController.model);}},this);},addScriptObject:function(name,value){this.scriptObjectNames_.push(name);this.scriptObjectValues_.push(value);},executeCommand:function(command){var f=new Function(this.scriptObjectNames_,'return eval('+command+')');return f.apply(null,this.scriptObjectValues_);}};return{ScriptingController,ScriptingObjectRegistry,};});'use strict';tr.exportTo('tr.metrics',function(){function MetricRegistry(){}
+const options=new tr.b.ExtensionRegistryOptions(tr.b.BASIC_REGISTRY_MODE);options.defaultMetadata={};tr.b.decorateExtensionRegistry(MetricRegistry,options);function camelCaseToHackerString(camelCase){let hackerString='';for(const c of camelCase){const lowered=c.toLocaleLowerCase();if(lowered===c){hackerString+=c;}else{hackerString+='_'+lowered;}}
+return hackerString;}
+function getCallStack(){try{throw new Error();}catch(error){return error.stack;}}
+function getPathsFromStack(stack){return stack.split('\n').map(line=>{line=line.replace(/^ */,'').split(':');if(line.length<4)return'';return line[line.length-3].split('/');}).filter(tr.b.identity);}
+MetricRegistry.checkFilename=function(metricName,opt_metricPathForTest){if(metricName==='runtimeStatsTotalMetric'||metricName==='v8AndMemoryMetrics'){return;}
+const expectedFilename=camelCaseToHackerString(metricName)+'.html';const stack=getCallStack();let metricPath=opt_metricPathForTest;if(metricPath===undefined){const paths=getPathsFromStack(stack);const METRIC_STACK_INDEX=5;if(paths.length<=METRIC_STACK_INDEX||paths[METRIC_STACK_INDEX].join('/')===paths[0].join('/')){return;}
+metricPath=paths[METRIC_STACK_INDEX].slice(paths[METRIC_STACK_INDEX].length-2);}
+if(!metricPath[1].endsWith('_test.html')&&metricPath[1]!==expectedFilename&&metricPath.join('_')!==expectedFilename){throw new Error('Expected '+metricName+' to be in a file named '+
+expectedFilename+'; actual: '+metricPath.join('/')+'; stack: '+stack.replace(/\n/g,'\n  '));}};MetricRegistry.addEventListener('will-register',function(e){let metric=e.typeInfo.constructor;if(!(metric instanceof Function)){throw new Error('Metrics must be functions.');}
+if(!metric.name.endsWith('Metric')&&!metric.name.endsWith('Metrics')){throw new Error('Metric names must end with "Metric" or "Metrics".');}
+if(metric.length<2){throw new Error('Metrics take a HistogramSet and a Model and '+'optionally an options dictionary.');}
+MetricRegistry.checkFilename(metric.name);});return{MetricRegistry,};});'use strict';tr.exportTo('tr.metrics.sh',function(){const MAX_INPUT_EVENT_TO_STARTUP_DELAY_IN_MS=2000;const MIN_DRAW_DELAY_IN_MS=80;const MAX_DRAW_DELAY_IN_MS=2000;function findProcess(processName,model){for(var pid in model.processes){var process=model.processes[pid];if(process.name===processName){return process;}}
+return undefined;}
+function findThreads(process,threadPrefix){if(process===undefined)return undefined;var threads=[];for(var tid in process.threads){var thread=process.threads[tid];if(thread.name.startsWith(threadPrefix)){threads.push(thread);}}
+return threads;}
+function findUIThread(process){if(process===undefined)return undefined;var threads=findThreads(process,'UI Thread');if(threads!==undefined&&threads.length===1){return threads[0];}
+return process.threads[process.pid];}
+function findLaunchSlices(model){var launches=[];var binders=findThreads(findProcess('system_server',model),'Binder');for(var binderId in binders){var binder=binders[binderId];for(var sliceId in binder.asyncSliceGroup.slices){var slice=binder.asyncSliceGroup.slices[sliceId];if(slice.title.startsWith('launching:')){launches.push(slice);}}}
+return launches;}
+function findDrawSlice(appName,startNotBefore,model){var drawSlice=undefined;var thread=findUIThread(findProcess(appName,model));if(thread===undefined)return undefined;for(var sliceId in thread.sliceGroup.slices){var slice=thread.sliceGroup.slices[sliceId];if(slice.start<startNotBefore+MIN_DRAW_DELAY_IN_MS||slice.start>startNotBefore+MAX_DRAW_DELAY_IN_MS)continue;if(slice.title!=='draw')continue;if(drawSlice===undefined||slice.start<drawSlice.start){drawSlice=slice;}}
+return drawSlice;}
+function findInputEventSlice(endNotAfter,model){var endNotBefore=endNotAfter-MAX_INPUT_EVENT_TO_STARTUP_DELAY_IN_MS;var inputSlice=undefined;var systemUi=findUIThread(findProcess('com.android.systemui',model));if(systemUi===undefined)return undefined;for(var sliceId in systemUi.asyncSliceGroup.slices){var slice=systemUi.asyncSliceGroup.slices[sliceId];if(slice.end>endNotAfter||slice.end<endNotBefore)continue;if(slice.title!=='deliverInputEvent')continue;if(inputSlice===undefined||slice.end>inputSlice.end){inputSlice=slice;}}
+return inputSlice;}
+function computeStartupTimeInMs(appName,launchSlice,model){var startupStart=launchSlice.start;var startupEnd=launchSlice.end;var drawSlice=findDrawSlice(appName,launchSlice.end,model);if(drawSlice!==undefined){startupEnd=drawSlice.end;}
+var inputSlice=findInputEventSlice(launchSlice.start,model);if(inputSlice!==undefined){startupStart=inputSlice.start;}
+return startupEnd-startupStart;}
+function measureStartup(values,model){var launches=findLaunchSlices(model);for(var sliceId in launches){var launchSlice=launches[sliceId];var appName=launchSlice.title.split(': ')[1];var startupMs=computeStartupTimeInMs(appName,launchSlice,model);var hist=new tr.v.Histogram('android:systrace:startup:'+appName,tr.b.Unit.byName.timeDurationInMs_smallerIsBetter);hist.addSample(startupMs);values.addHistogram(hist);}}
+function measureThreadStates(values,model,rangeOfInterest){var addHistogram=function(processName,timerName,valueInMs,rangeInMs){var hist=new tr.v.Histogram('android:systrace:threadtime:'+processName+':'+timerName,tr.b.Unit.byName.normalizedPercentage);hist.addSample(valueInMs/rangeInMs);values.addHistogram(hist);};for(var pid in model.processes){var process=model.processes[pid];if(process.name===undefined)continue;var hasSlices=false;var timeRunning=0;var timeRunnable=0;var timeSleeping=0;var timeUninterruptible=0;var timeBlockIO=0;var timeUnknown=0;for(var tid in process.threads){var thread=process.threads[tid];if(thread.timeSlices===undefined)continue;for(var sliceId in thread.timeSlices){var slice=thread.timeSlices[sliceId];var sliceRange=tr.b.Range.fromExplicitRange(slice.start,slice.end);var intersection=rangeOfInterest.findIntersection(sliceRange);var duration=intersection.duration;if(duration===0)continue;hasSlices=true;if(slice.title==='Running'){timeRunning+=duration;}else if(slice.title==='Runnable'){timeRunnable+=duration;}else if(slice.title==='Sleeping'){timeSleeping+=duration;}else if(slice.title.startsWith('Uninterruptible')){timeUninterruptible+=duration;if(slice.title.includes('Block I/O'))timeBlockIO+=duration;}else{timeUnknown+=duration;}}}
+if(hasSlices){var wall=rangeOfInterest.max-rangeOfInterest.min;addHistogram(process.name,'running',timeRunning,wall);addHistogram(process.name,'runnable',timeRunnable,wall);addHistogram(process.name,'sleeping',timeSleeping,wall);addHistogram(process.name,'blockio',timeBlockIO,wall);addHistogram(process.name,'uninterruptible',timeUninterruptible,wall);if(timeUnknown>0){addHistogram(process.name,'unknown',timeUnknown,wall);}}}}
+function androidSystraceMetric(values,model,options){var rangeOfInterest=model.bounds;if(options!==undefined&&options.rangeOfInterest!==undefined){rangeOfInterest=options.rangeOfInterest;}
+measureStartup(values,model);measureThreadStates(values,model,rangeOfInterest);}
+tr.metrics.MetricRegistry.register(androidSystraceMetric,{supportsRangeOfInterest:true});return{androidSystraceMetric,};});'use strict';tr.exportTo('tr.b.math',function(){var PERCENTILE_PRECISION=1e-7;function PiecewiseLinearFunction(){this.pieces=[];}
+PiecewiseLinearFunction.prototype={push:function(x1,y1,x2,y2){if(x1>=x2){throw new Error('Invalid segment');}
+if(this.pieces.length>0&&this.pieces[this.pieces.length-1].x2>x1){throw new Error('Potentially overlapping segments');}
+if(x1<x2){this.pieces.push(new Piece(x1,y1,x2,y2));}},partBelow:function(y){return this.pieces.reduce((acc,p)=>(acc+p.partBelow(y)),0);},get min(){return this.pieces.reduce((acc,p)=>Math.min(acc,p.min),Infinity);},get max(){return this.pieces.reduce((acc,p)=>Math.max(acc,p.max),-Infinity);},get average(){var weightedSum=0;var totalWeight=0;this.pieces.forEach(function(piece){weightedSum+=piece.width*piece.average;totalWeight+=piece.width;});if(totalWeight===0)return 0;return weightedSum/totalWeight;},percentile:function(percent){if(!(percent>=0&&percent<=1)){throw new Error('percent must be [0,1]');}
+var lower=this.min;var upper=this.max;var total=this.partBelow(upper);if(total===0)return 0;while(upper-lower>PERCENTILE_PRECISION){var middle=(lower+upper)/2;var below=this.partBelow(middle);if(below/total<percent){lower=middle;}else{upper=middle;}}
 return(lower+upper)/2;}};function Piece(x1,y1,x2,y2){this.x1=x1;this.y1=y1;this.x2=x2;this.y2=y2;}
-Piece.prototype={partBelow:function(y){var width=this.width;if(width===0)
-return 0;var minY=this.min;var maxY=this.max;if(y>=maxY)
-return width;if(y<minY)
-return 0;return(y-minY)/(maxY-minY)*width;},get min(){return Math.min(this.y1,this.y2);},get max(){return Math.max(this.y1,this.y2);},get average(){return(this.y1+this.y2)/2;},get width(){return this.x2-this.x1;}};return{PiecewiseLinearFunction:PiecewiseLinearFunction};});'use strict';tr.exportTo('tr.metrics.v8.utils',function(){var IDLE_TASK_EVENT='SingleThreadIdleTaskRunner::RunTask';var V8_EXECUTE='V8.Execute';var GC_EVENT_PREFIX='V8.GC';var FULL_GC_EVENT='V8.GCCompactor';var LOW_MEMORY_EVENT='V8.GCLowMemoryNotification';var MAJOR_GC_EVENT='MajorGC';var MINOR_GC_EVENT='MinorGC';var TOP_GC_EVENTS={'V8.GCCompactor':'v8-gc-full-mark-compactor','V8.GCFinalizeMC':'v8-gc-latency-mark-compactor','V8.GCFinalizeMCReduceMemory':'v8-gc-memory-mark-compactor','V8.GCIncrementalMarking':'v8-gc-incremental-step','V8.GCIncrementalMarkingFinalize':'v8-gc-incremental-finalize','V8.GCIncrementalMarkingStart':'v8-gc-incremental-start','V8.GCPhantomHandleProcessingCallback':'v8-gc-phantom-handle-callback','V8.GCScavenger':'v8-gc-scavenger'};var LOW_MEMORY_MARK_COMPACTOR='v8-gc-low-memory-mark-compactor';function findParent(event,predicate){var parent=event.parentSlice;while(parent){if(predicate(parent)){return parent;}
+Piece.prototype={partBelow:function(y){var width=this.width;if(width===0)return 0;var minY=this.min;var maxY=this.max;if(y>=maxY)return width;if(y<minY)return 0;return(y-minY)/(maxY-minY)*width;},get min(){return Math.min(this.y1,this.y2);},get max(){return Math.max(this.y1,this.y2);},get average(){return(this.y1+this.y2)/2;},get width(){return this.x2-this.x1;}};return{PiecewiseLinearFunction,};});'use strict';tr.exportTo('tr.metrics.v8.utils',function(){var IDLE_TASK_EVENT='SingleThreadIdleTaskRunner::RunTask';var V8_EXECUTE='V8.Execute';var GC_EVENT_PREFIX='V8.GC';var FULL_GC_EVENT='V8.GCCompactor';var LOW_MEMORY_EVENT='V8.GCLowMemoryNotification';var MAJOR_GC_EVENT='MajorGC';var MINOR_GC_EVENT='MinorGC';var TOP_GC_EVENTS={'V8.GCCompactor':'v8-gc-full-mark-compactor','V8.GCFinalizeMC':'v8-gc-latency-mark-compactor','V8.GCFinalizeMCReduceMemory':'v8-gc-memory-mark-compactor','V8.GCIncrementalMarking':'v8-gc-incremental-step','V8.GCIncrementalMarkingFinalize':'v8-gc-incremental-finalize','V8.GCIncrementalMarkingStart':'v8-gc-incremental-start','V8.GCPhantomHandleProcessingCallback':'v8-gc-phantom-handle-callback','V8.GCScavenger':'v8-gc-scavenger'};var LOW_MEMORY_MARK_COMPACTOR='v8-gc-low-memory-mark-compactor';function findParent(event,predicate){var parent=event.parentSlice;while(parent){if(predicate(parent)){return parent;}
 parent=parent.parentSlice;}
 return null;}
 function isIdleTask(event){return event.title===IDLE_TASK_EVENT;}
 function isLowMemoryEvent(event){return event.title===LOW_MEMORY_EVENT;}
+function isV8Event(event){return event.title.startsWith('V8.');}
 function isV8ExecuteEvent(event){return event.title===V8_EXECUTE;}
 function isTopV8ExecuteEvent(event){return isV8ExecuteEvent(event)&&findParent(isV8ExecuteEvent)===null;}
-function isGarbageCollectionEvent(event){return event.title&&event.title.startsWith(GC_EVENT_PREFIX)&&event.title!=LOW_MEMORY_EVENT;}
+function isGarbageCollectionEvent(event){return event.title&&event.title.startsWith(GC_EVENT_PREFIX)&&event.title!==LOW_MEMORY_EVENT;}
 function isTopGarbageCollectionEvent(event){return event.title in TOP_GC_EVENTS;}
 function isForcedGarbageCollectionEvent(event){return findParent(event,isLowMemoryEvent)!==null;}
 function isSubGarbageCollectionEvent(event){return isGarbageCollectionEvent(event)&&event.parentSlice&&(isTopGarbageCollectionEvent(event.parentSlice)||event.parentSlice.title===MAJOR_GC_EVENT||event.parentSlice.title===MINOR_GC_EVENT);}
+function isCompileEvent(event){return tr.b.getCategoryParts(event.category).includes('disabled-by-default-v8.compile');}
+function isFullMarkCompactorEvent(event){return event.title==='V8.GCCompactor';}
+function isIncrementalMarkingEvent(event){return event.title.startsWith('V8.GCIncrementalMarking');}
+function isLatencyMarkCompactorEvent(event){return event.title==='V8.GCFinalizeMC';}
+function isMemoryMarkCompactorEvent(event){return event.title==='V8.GCFinalizeMCReduceMemory';}
+function isScavengerEvent(event){return event.title==='V8.GCScavenger';}
 function topGarbageCollectionEventName(event){if(event.title===FULL_GC_EVENT){if(findParent(event,isLowMemoryEvent)){return LOW_MEMORY_MARK_COMPACTOR;}}
 return TOP_GC_EVENTS[event.title];}
 function subGarbageCollectionEventName(event){var topEvent=findParent(event,isTopGarbageCollectionEvent);var prefix=topEvent?topGarbageCollectionEventName(topEvent):'unknown';var name=event.title.replace('V8.GC_MC_','').replace('V8.GC_SCAVENGER_','').replace('V8.GC_','').replace(/_/g,'-').toLowerCase();return prefix+'-'+name;}
 function groupAndProcessEvents(model,filterCallback,nameCallback,processCallback){var nameToEvents={};for(var event of model.getDescendantEvents()){if(!filterCallback(event))continue;var name=nameCallback(event);nameToEvents[name]=nameToEvents[name]||[];nameToEvents[name].push(event);}
-tr.b.iterItems(nameToEvents,function(name,events){processCallback(name,events);});}
-function unionOfIntervals(intervals){if(intervals.length===0)
-return[];return tr.b.mergeRanges(intervals.map(x=>({min:x.start,max:x.end})),1e-6,function(ranges){return{start:ranges.reduce((acc,x)=>Math.min(acc,x.min),ranges[0].min),end:ranges.reduce((acc,x)=>Math.max(acc,x.max),ranges[0].max)};});}
-function WindowEndpoint(start,points){this.points=points;this.lastIndex=-1;this.position=start;this.distanceUntilNextPoint=points[0].position-start;this.cummulativePause=0;this.stackDepth=0;}
-WindowEndpoint.prototype={advance:function(delta){var points=this.points;if(delta<this.distanceUntilNextPoint){this.position+=delta;this.cummulativePause+=this.stackDepth>0?delta:0;this.distanceUntilNextPoint=points[this.lastIndex+1].position-this.position;}else{this.position+=this.distanceUntilNextPoint;this.cummulativePause+=this.stackDepth>0?this.distanceUntilNextPoint:0;this.distanceUntilNextPoint=0;this.lastIndex++;if(this.lastIndex<points.length){this.stackDepth+=points[this.lastIndex].delta;if(this.lastIndex+1<points.length)
-this.distanceUntilNextPoint=points[this.lastIndex+1].position-this.position;}}}};function mutatorUtilization(start,end,timeWindow,intervals){var mu=new tr.b.PiecewiseLinearFunction();if(end-start<=timeWindow)
-return mu;if(intervals.length===0){mu.push(start,1.0,end-timeWindow,1.0);return mu;}
-intervals=unionOfIntervals(intervals);var points=[];intervals.forEach(function(interval){points.push({position:interval.start,delta:1});points.push({position:interval.end,delta:-1});});points.sort((a,b)=>a.position-b.position);points.push({position:end,delta:0});var left=new WindowEndpoint(start,points);var right=new WindowEndpoint(start,points);while(right.position-left.position<timeWindow)
-right.advance(timeWindow-(right.position-left.position));while(right.lastIndex<points.length){var distanceUntilNextPoint=Math.min(left.distanceUntilNextPoint,right.distanceUntilNextPoint);var position1=left.position;var value1=right.cummulativePause-left.cummulativePause;left.advance(distanceUntilNextPoint);right.advance(distanceUntilNextPoint);if(distanceUntilNextPoint>0){var position2=left.position;var value2=right.cummulativePause-left.cummulativePause;mu.push(position1,1.0-value1/timeWindow,position2,1.0-value2/timeWindow);}}
-return mu;}
+for(var[name,events]of Object.entries(nameToEvents)){processCallback(name,events);}}
+function unionOfIntervals(intervals){if(intervals.length===0)return[];return tr.b.math.mergeRanges(intervals.map(x=>{return{min:x.start,max:x.end};}),1e-6,function(ranges){return{start:ranges.reduce((acc,x)=>Math.min(acc,x.min),ranges[0].min),end:ranges.reduce((acc,x)=>Math.max(acc,x.max),ranges[0].max)};});}
 function hasV8Stats(globalMemoryDump){var v8stats=undefined;globalMemoryDump.iterateContainerDumps(function(dump){v8stats=v8stats||dump.getMemoryAllocatorDumpByFullName('v8');});return!!v8stats;}
-function rangeForMemoryDumps(model){var startOfFirstDumpWithV8=model.globalMemoryDumps.filter(hasV8Stats).reduce((start,dump)=>Math.min(start,dump.start),Infinity);if(startOfFirstDumpWithV8===Infinity)
-return new tr.b.Range();return tr.b.Range.fromExplicitRange(startOfFirstDumpWithV8,Infinity);}
-return{findParent:findParent,groupAndProcessEvents:groupAndProcessEvents,isForcedGarbageCollectionEvent:isForcedGarbageCollectionEvent,isGarbageCollectionEvent:isGarbageCollectionEvent,isIdleTask:isIdleTask,isLowMemoryEvent:isLowMemoryEvent,isSubGarbageCollectionEvent:isSubGarbageCollectionEvent,isTopGarbageCollectionEvent:isTopGarbageCollectionEvent,isTopV8ExecuteEvent:isTopV8ExecuteEvent,isV8ExecuteEvent:isV8ExecuteEvent,mutatorUtilization:mutatorUtilization,subGarbageCollectionEventName:subGarbageCollectionEventName,topGarbageCollectionEventName:topGarbageCollectionEventName,rangeForMemoryDumps:rangeForMemoryDumps,unionOfIntervals:unionOfIntervals};});'use strict';tr.exportTo('tr.metrics.blink',function(){var BLINK_GC_EVENTS={'BlinkGCMarking':'blink-gc-marking','ThreadState::completeSweep':'blink-gc-complete-sweep','ThreadState::performIdleLazySweep':'blink-gc-idle-lazy-sweep'};function isBlinkGarbageCollectionEvent(event){return event.title in BLINK_GC_EVENTS;}
+function rangeForMemoryDumps(model){var startOfFirstDumpWithV8=model.globalMemoryDumps.filter(hasV8Stats).reduce((start,dump)=>Math.min(start,dump.start),Infinity);if(startOfFirstDumpWithV8===Infinity)return new tr.b.math.Range();return tr.b.math.Range.fromExplicitRange(startOfFirstDumpWithV8,Infinity);}
+return{findParent,groupAndProcessEvents,isCompileEvent,isForcedGarbageCollectionEvent,isFullMarkCompactorEvent,isGarbageCollectionEvent,isIdleTask,isIncrementalMarkingEvent,isLatencyMarkCompactorEvent,isLowMemoryEvent,isMemoryMarkCompactorEvent,isScavengerEvent,isSubGarbageCollectionEvent,isTopGarbageCollectionEvent,isTopV8ExecuteEvent,isV8Event,isV8ExecuteEvent,rangeForMemoryDumps,subGarbageCollectionEventName,topGarbageCollectionEventName,unionOfIntervals,};});'use strict';tr.exportTo('tr.metrics.blink',function(){var BLINK_GC_EVENTS={'BlinkGCMarking':'blink-gc-marking','ThreadState::completeSweep':'blink-gc-complete-sweep','ThreadState::performIdleLazySweep':'blink-gc-idle-lazy-sweep'};function isBlinkGarbageCollectionEvent(event){return event.title in BLINK_GC_EVENTS;}
 function blinkGarbageCollectionEventName(event){return BLINK_GC_EVENTS[event.title];}
-function blinkGcMetric(values,model){addDurationOfTopEvents(values,model);addTotalDurationOfTopEvents(values,model);addIdleTimesOfTopEvents(values,model);addTotalIdleTimesOfTopEvents(values,model);}
+function blinkGcMetric(histograms,model){addDurationOfTopEvents(histograms,model);addTotalDurationOfTopEvents(histograms,model);addIdleTimesOfTopEvents(histograms,model);addTotalIdleTimesOfTopEvents(histograms,model);}
 tr.metrics.MetricRegistry.register(blinkGcMetric);var timeDurationInMs_smallerIsBetter=tr.b.Unit.byName.timeDurationInMs_smallerIsBetter;var percentage_biggerIsBetter=tr.b.Unit.byName.normalizedPercentage_biggerIsBetter;var CUSTOM_BOUNDARIES=tr.v.HistogramBinBoundaries.createLinear(0,20,200).addExponentialBins(200,100);function createNumericForTopEventTime(name){var n=new tr.v.Histogram(name,timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);n.customizeSummaryOptions({avg:true,count:true,max:true,min:false,std:true,sum:true,percentile:[0.90]});return n;}
 function createNumericForIdleTime(name){var n=new tr.v.Histogram(name,timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);n.customizeSummaryOptions({avg:true,count:false,max:true,min:false,std:false,sum:true,percentile:[]});return n;}
-function createPercentage(name,numerator,denominator){var histogram=new tr.v.Histogram(name,percentage_biggerIsBetter);if(denominator===0)
-histogram.addSample(0);else
-histogram.addSample(numerator/denominator);return histogram;}
-function addDurationOfTopEvents(values,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isBlinkGarbageCollectionEvent,blinkGarbageCollectionEventName,function(name,events){var cpuDuration=createNumericForTopEventTime(name);events.forEach(function(event){cpuDuration.addSample(event.cpuDuration);});values.addHistogram(cpuDuration);});}
-function addTotalDurationOfTopEvents(values,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isBlinkGarbageCollectionEvent,event=>'blink-gc-total',function(name,events){var cpuDuration=createNumericForTopEventTime(name);events.forEach(function(event){cpuDuration.addSample(event.cpuDuration);});values.addHistogram(cpuDuration);});}
-function addIdleTimesOfTopEvents(values,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isBlinkGarbageCollectionEvent,blinkGarbageCollectionEventName,function(name,events){addIdleTimes(values,model,name,events);});}
-function addTotalIdleTimesOfTopEvents(values,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isBlinkGarbageCollectionEvent,event=>'blink-gc-total',function(name,events){addIdleTimes(values,model,name,events);});}
-function addIdleTimes(values,model,name,events){var cpuDuration=createNumericForIdleTime(name+'_cpu');var insideIdle=createNumericForIdleTime(name+'_inside_idle');var outsideIdle=createNumericForIdleTime(name+'_outside_idle');var idleDeadlineOverrun=createNumericForIdleTime(name+'_idle_deadline_overrun');events.forEach(function(event){var idleTask=tr.metrics.v8.utils.findParent(event,tr.metrics.v8.utils.isIdleTask);var inside=0;var overrun=0;if(idleTask){var allottedTime=idleTask['args']['allotted_time_ms'];if(event.duration>allottedTime){overrun=event.duration-allottedTime;inside=event.cpuDuration*allottedTime/event.duration;}else{inside=event.cpuDuration;}}
-cpuDuration.addSample(event.cpuDuration);insideIdle.addSample(inside);outsideIdle.addSample(event.cpuDuration-inside);idleDeadlineOverrun.addSample(overrun);});values.addHistogram(idleDeadlineOverrun);values.addHistogram(outsideIdle);var percentage=createPercentage(name+'_percentage_idle',insideIdle.sum,cpuDuration.sum);values.addHistogram(percentage);}
-return{blinkGcMetric:blinkGcMetric};});'use strict';tr.exportTo('tr.metrics.sh',function(){function getCpuSnapshotsFromModel(model){var snapshots=[];for(var pid in model.processes){var snapshotInstances=model.processes[pid].objects.getAllInstancesNamed('CPUSnapshots');if(!snapshotInstances)
-continue;for(var object of snapshotInstances[0].snapshots)
-snapshots.push(object.args.processes);}
+function createPercentage(name,numerator,denominator){var histogram=new tr.v.Histogram(name,percentage_biggerIsBetter);if(denominator===0){histogram.addSample(0);}else{histogram.addSample(numerator/denominator);}
+return histogram;}
+function addDurationOfTopEvents(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isBlinkGarbageCollectionEvent,blinkGarbageCollectionEventName,function(name,events){var cpuDuration=createNumericForTopEventTime(name);events.forEach(function(event){cpuDuration.addSample(event.cpuDuration);});histograms.addHistogram(cpuDuration);});}
+function addTotalDurationOfTopEvents(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isBlinkGarbageCollectionEvent,event=>'blink-gc-total',function(name,events){var cpuDuration=createNumericForTopEventTime(name);events.forEach(function(event){cpuDuration.addSample(event.cpuDuration);});histograms.addHistogram(cpuDuration);});}
+function addIdleTimesOfTopEvents(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isBlinkGarbageCollectionEvent,blinkGarbageCollectionEventName,function(name,events){addIdleTimes(histograms,model,name,events);});}
+function addTotalIdleTimesOfTopEvents(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isBlinkGarbageCollectionEvent,event=>'blink-gc-total',function(name,events){addIdleTimes(histograms,model,name,events);});}
+function addIdleTimes(histograms,model,name,events){var cpuDuration=createNumericForIdleTime(name+'_cpu');var insideIdle=createNumericForIdleTime(name+'_inside_idle');var outsideIdle=createNumericForIdleTime(name+'_outside_idle');var idleDeadlineOverrun=createNumericForIdleTime(name+'_idle_deadline_overrun');events.forEach(function(event){var idleTask=tr.metrics.v8.utils.findParent(event,tr.metrics.v8.utils.isIdleTask);var inside=0;var overrun=0;if(idleTask){var allottedTime=idleTask['args']['allotted_time_ms'];if(event.duration>allottedTime){overrun=event.duration-allottedTime;inside=event.cpuDuration*allottedTime/event.duration;}else{inside=event.cpuDuration;}}
+cpuDuration.addSample(event.cpuDuration);insideIdle.addSample(inside);outsideIdle.addSample(event.cpuDuration-inside);idleDeadlineOverrun.addSample(overrun);});histograms.addHistogram(idleDeadlineOverrun);histograms.addHistogram(outsideIdle);var percentage=createPercentage(name+'_percentage_idle',insideIdle.sum,cpuDuration.sum);histograms.addHistogram(percentage);}
+return{blinkGcMetric,};});'use strict';tr.exportTo('tr.metrics.sh',function(){function getCpuSnapshotsFromModel(model){var snapshots=[];for(var pid in model.processes){var snapshotInstances=model.processes[pid].objects.getAllInstancesNamed('CPUSnapshots');if(!snapshotInstances)continue;for(var object of snapshotInstances[0].snapshots){snapshots.push(object.args.processes);}}
 return snapshots;}
-function getProcessSumsFromSnapshot(snapshot){var processSums=new Map();for(var processData of snapshot){var processName=processData.name;if(!(processSums.has(processName)))
-processSums.set(processName,{sum:0.0,paths:new Set()});processSums.get(processName).sum+=parseFloat(processData.pCpu);if(processData.path)
-processSums.get(processName).paths.add(processData.path);}
+function getProcessSumsFromSnapshot(snapshot){var processSums=new Map();for(var processData of snapshot){var processName=processData.name;if(!(processSums.has(processName))){processSums.set(processName,{sum:0.0,paths:new Set()});}
+processSums.get(processName).sum+=parseFloat(processData.pCpu);if(processData.path){processSums.get(processName).paths.add(processData.path);}}
 return processSums;}
 function buildNumericsFromSnapshots(snapshots){var processNumerics=new Map();for(var snapshot of snapshots){var processSums=getProcessSumsFromSnapshot(snapshot);for(var[processName,processData]of processSums.entries()){if(!(processNumerics.has(processName))){processNumerics.set(processName,{numeric:new tr.v.Histogram('cpu:percent:'+processName,tr.b.Unit.byName.normalizedPercentage_smallerIsBetter),paths:new Set()});}
-processNumerics.get(processName).numeric.addSample(processData.sum/100.0);for(var path of processData.paths)
-processNumerics.get(processName).paths.add(path);}}
+processNumerics.get(processName).numeric.addSample(processData.sum/100.0);for(var path of processData.paths){processNumerics.get(processName).paths.add(path);}}}
 return processNumerics;}
-function cpuProcessMetric(values,model){var snapshots=getCpuSnapshotsFromModel(model);var processNumerics=buildNumericsFromSnapshots(snapshots);for(var[processName,processData]of processNumerics){var numeric=processData.numeric;var missingSnapshotCount=snapshots.length-numeric.numValues;for(var i=0;i<missingSnapshotCount;i++)
-numeric.addSample(0);numeric.diagnostics.set('paths',new
-tr.v.d.Generic([...processData.paths]));values.addHistogram(numeric);}}
-tr.metrics.MetricRegistry.register(cpuProcessMetric);return{cpuProcessMetric:cpuProcessMetric};});'use strict';tr.exportTo('tr.metrics',function(){function sampleMetric(values,model){var hist=new tr.v.Histogram('foo',tr.b.Unit.byName.sizeInBytes_smallerIsBetter);hist.addSample(9);hist.addSample(91,{bar:new tr.v.d.Generic({hello:42})});for(var expectation of model.userModel.expectations){if(expectation instanceof tr.model.um.ResponseExpectation){}else if(expectation instanceof tr.model.um.AnimationExpectation){}else if(expectation instanceof tr.model.um.IdleExpectation){}else if(expectation instanceof tr.model.um.LoadExpectation){}}
-var chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);tr.b.iterItems(model.processes,function(pid,process){});values.addHistogram(hist);}
-tr.metrics.MetricRegistry.register(sampleMetric);return{sampleMetric:sampleMetric};});'use strict';tr.exportTo('tr.metrics.sh',function(){function perceptualBlend(ir,index,score){return Math.exp(1-score);}
-function filterExpectationsByRange(irs,opt_range){var filteredExpectations=[];irs.forEach(function(ir){if(!(ir instanceof tr.model.um.UserExpectation))
-return;if(!opt_range||opt_range.intersectsExplicitRangeInclusive(ir.start,ir.end))
-filteredExpectations.push(ir);});return filteredExpectations;}
-return{perceptualBlend:perceptualBlend,filterExpectationsByRange:filterExpectationsByRange};});'use strict';tr.exportTo('tr.metrics.sh',function(){function syncIsComplete(markers){return markers.length===2;}
-function syncInvolvesTelemetry(markers){for(var marker of markers)
-if(marker.domainId===tr.model.ClockDomainId.TELEMETRY)
-return true;return false;}
-function clockSyncLatencyMetric(values,model){for(var markers of model.clockSyncManager.markersBySyncId.values()){var latency=undefined;var targetDomain=undefined;if(!syncIsComplete(markers)||!syncInvolvesTelemetry(markers))
-continue;for(var marker of markers){var domain=marker.domainId;if(domain===tr.model.ClockDomainId.TELEMETRY)
-latency=(marker.endTs-marker.startTs);else
-targetDomain=domain.toLowerCase();}
-var hist=new tr.v.Histogram('clock_sync_latency_'+targetDomain,tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,tr.v.HistogramBinBoundaries.createExponential(1e-3,1e3,30));hist.description='Clock sync latency for domain '+targetDomain;hist.addSample(latency);values.addHistogram(hist);}}
-tr.metrics.MetricRegistry.register(clockSyncLatencyMetric);return{clockSyncLatencyMetric:clockSyncLatencyMetric};});'use strict';tr.exportTo('tr.metrics.sh',function(){var LONG_TASK_MS=50;var LONGEST_TASK_MS=1000;function iterateLongTopLevelTasksOnThreadInRange(thread,opt_range,cb,opt_this){thread.sliceGroup.topLevelSlices.forEach(function(slice){if(opt_range&&!opt_range.intersectsExplicitRangeInclusive(slice.start,slice.end))
-return;if(slice.duration<LONG_TASK_MS)
-return;cb.call(opt_this,slice);});}
-function iterateRendererMainThreads(model,cb,opt_this){var modelHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);tr.b.dictionaryValues(modelHelper.rendererHelpers).forEach(function(rendererHelper){if(!rendererHelper.mainThread)
-return;cb.call(opt_this,rendererHelper.mainThread);});}
-function longTasksMetric(values,model,opt_options){var rangeOfInterest=opt_options?opt_options.rangeOfInterest:undefined;var longTaskNumeric=new tr.v.Histogram('long tasks',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,tr.v.HistogramBinBoundaries.createLinear(LONG_TASK_MS,LONGEST_TASK_MS,40));longTaskNumeric.description='durations of long tasks';var slices=new tr.model.EventSet();iterateRendererMainThreads(model,function(thread){iterateLongTopLevelTasksOnThreadInRange(thread,rangeOfInterest,function(task){longTaskNumeric.addSample(task.duration,{relatedEvents:new tr.v.d.RelatedEventSet([task])});slices.push(task);slices.addEventSet(task.descendentSlices);});});values.addHistogram(longTaskNumeric);var sampleForEvent=undefined;var composition=tr.v.d.Composition.buildFromEvents(values,'long tasks ',slices,e=>(model.getUserFriendlyCategoryFromEvent(e)||'unknown'),tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,sampleForEvent,tr.v.HistogramBinBoundaries.createExponential(1,LONGEST_TASK_MS,40));composition.colorScheme=tr.v.d.COLOR_SCHEME_CHROME_USER_FRIENDLY_CATEGORY_DRIVER;longTaskValue.diagnostics.set('category',composition);}
-tr.metrics.MetricRegistry.register(longTasksMetric,{supportsRangeOfInterest:true});return{longTasksMetric:longTasksMetric,iterateLongTopLevelTasksOnThreadInRange:iterateLongTopLevelTasksOnThreadInRange,iterateRendererMainThreads:iterateRendererMainThreads,LONG_TASK_MS:LONG_TASK_MS,LONGEST_TASK_MS:LONGEST_TASK_MS};});'use strict';tr.exportTo('tr.metrics.sh',function(){var MS_PER_S=1000;var RESPONSE_RISK=tr.b.Statistics.LogNormalDistribution.fromMedianAndDiminishingReturns(100/MS_PER_S,50/MS_PER_S);function computeResponsivenessRisk(durationMs){durationMs+=16;return RESPONSE_RISK.computePercentile(durationMs/MS_PER_S);}
-function perceptualBlendSmallerIsBetter(hazardScore){return Math.exp(hazardScore);}
-function computeHazardForLongTasksInRangeOnThread(thread,opt_range){var taskHazardScores=[];tr.metrics.sh.iterateLongTopLevelTasksOnThreadInRange(thread,opt_range,function(task){taskHazardScores.push(computeResponsivenessRisk(task.duration));});return tr.b.Statistics.weightedMean(taskHazardScores,perceptualBlendSmallerIsBetter);}
-function computeHazardForLongTasks(model){var threadHazardScores=[];tr.metrics.sh.iterateRendererMainThreads(model,function(thread){threadHazardScores.push(computeHazardForLongTasksInRangeOnThread(thread));});return tr.b.Statistics.weightedMean(threadHazardScores,perceptualBlendSmallerIsBetter);}
-function hazardMetric(values,model){var overallHazard=computeHazardForLongTasks(model);if(overallHazard===undefined)
-overallHazard=0;var hist=new tr.v.Histogram('hazard',tr.b.Unit.byName.normalizedPercentage_smallerIsBetter);hist.addSample(overallHazard);values.addHistogram(hist);}
-tr.metrics.MetricRegistry.register(hazardMetric);return{hazardMetric:hazardMetric,computeHazardForLongTasksInRangeOnThread:computeHazardForLongTasksInRangeOnThread,computeHazardForLongTasks:computeHazardForLongTasks,computeResponsivenessRisk:computeResponsivenessRisk};});'use strict';tr.exportTo('tr.metrics.sh',function(){var RESPONSIVENESS_THRESHOLD=50;var INTERACTIVE_WINDOW_SIZE=5*1000;var timeDurationInMs_smallerIsBetter=tr.b.Unit.byName.timeDurationInMs_smallerIsBetter;var RelatedEventSet=tr.v.d.RelatedEventSet;function hasCategoryAndName(event,category,title){return event.title===title&&event.category&&tr.b.getCategoryParts(event.category).indexOf(category)!==-1;}
-function findTargetRendererHelper(chromeHelper){var largestPid=-1;for(var pid in chromeHelper.rendererHelpers){var rendererHelper=chromeHelper.rendererHelpers[pid];if(rendererHelper.isChromeTracingUI)
-continue;if(pid>largestPid)
-largestPid=pid;}
-if(largestPid===-1)
-return undefined;return chromeHelper.rendererHelpers[largestPid];}
-function createBreakdownDiagnostic(rendererHelper,start,end){var breakdownDict=rendererHelper.generateTimeBreakdownTree(start,end);var breakdownDiagnostic=new tr.v.d.Breakdown();breakdownDiagnostic.colorScheme=tr.v.d.COLOR_SCHEME_CHROME_USER_FRIENDLY_CATEGORY_DRIVER;for(var label in breakdownDict){breakdownDiagnostic.set(label,breakdownDict[label].total);}
+function cpuProcessMetric(histograms,model){var snapshots=getCpuSnapshotsFromModel(model);var processNumerics=buildNumericsFromSnapshots(snapshots);for(var[processName,processData]of processNumerics){var numeric=processData.numeric;var missingSnapshotCount=snapshots.length-numeric.numValues;for(var i=0;i<missingSnapshotCount;i++){numeric.addSample(0);}
+numeric.diagnostics.set('paths',new
+tr.v.d.Generic([...processData.paths]));histograms.addHistogram(numeric);}}
+tr.metrics.MetricRegistry.register(cpuProcessMetric);return{cpuProcessMetric,};});'use strict';tr.exportTo('tr.metrics',function(){function sampleMetric(histograms,model){var hist=new tr.v.Histogram('foo',tr.b.Unit.byName.sizeInBytes_smallerIsBetter);hist.addSample(9);hist.addSample(91,{bar:new tr.v.d.Generic({hello:42})});for(var expectation of model.userModel.expectations){if(expectation instanceof tr.model.um.ResponseExpectation){}else if(expectation instanceof tr.model.um.AnimationExpectation){}else if(expectation instanceof tr.model.um.IdleExpectation){}else if(expectation instanceof tr.model.um.LoadExpectation){}}
+var chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);for(var[pid,process]of Object.entries(model.processes)){}
+histograms.addHistogram(hist);}
+tr.metrics.MetricRegistry.register(sampleMetric);return{sampleMetric,};});'use strict';tr.exportTo('tr.metrics',function(){const HANDLE_INPUT_EVENT_TITLE='WebViewImpl::handleInputEvent';function findPrecedingEvents_(eventsA,eventsB){let events=new Map();let eventsBIndex=0;for(let eventA of eventsA){for(;eventsBIndex<eventsB.length;eventsBIndex++){if(eventsB[eventsBIndex].start>eventA.start)break;}
+if(eventsBIndex>0){events.set(eventA,eventsB[eventsBIndex-1]);}}
+return events;}
+function findFollowingEvents_(eventsA,eventsB){let events=new Map();let eventsBIndex=0;for(let eventA of eventsA){for(;eventsBIndex<eventsB.length;eventsBIndex++){if(eventsB[eventsBIndex].start>=eventA.start)break;}
+if(eventsBIndex>=0&&eventsBIndex<eventsB.length){events.set(eventA,eventsB[eventsBIndex]);}}
+return events;}
+function getSpaNavigationStartCandidates_(rendererHelper,browserHelper){let isNavStartEvent=e=>{if(e.title===HANDLE_INPUT_EVENT_TITLE&&e.args.type==='MouseUp'){return true;}
+return e.title==='NavigationControllerImpl::GoToIndex';};return[...rendererHelper.mainThread.sliceGroup.getDescendantEvents(),...browserHelper.mainThread.sliceGroup.getDescendantEvents()].filter(isNavStartEvent);}
+function getSpaNavigationEvents_(rendererHelper){let isNavEvent=e=>e.category==='blink'&&e.title==='FrameLoader::updateForSameDocumentNavigation';return[...rendererHelper.mainThread.sliceGroup.getDescendantEvents()].filter(isNavEvent);}
+function getInputLatencyEvents_(browserHelper){let isInputLatencyEvent=e=>e.title==='InputLatency::MouseUp';return browserHelper.getAllAsyncSlicesMatching(isInputLatencyEvent);}
+function getInputLatencyEventByBindIdMap_(browserHelper){let inputLatencyEventByBindIdMap=new Map();for(let event of getInputLatencyEvents_(browserHelper)){inputLatencyEventByBindIdMap.set(event.args.data.trace_id,event);}
+return inputLatencyEventByBindIdMap;}
+function getSpaNavigationEventToNavigationStartMap_(rendererHelper,browserHelper){let mainThread=rendererHelper.mainThread;let spaNavEvents=getSpaNavigationEvents_(rendererHelper);let navStartCandidates=getSpaNavigationStartCandidates_(rendererHelper,browserHelper).sort(tr.importer.compareEvents);let spaNavEventToNavStartCandidateMap=findPrecedingEvents_(spaNavEvents,navStartCandidates);let inputLatencyEventByBindIdMap=getInputLatencyEventByBindIdMap_(browserHelper);let spaNavEventToNavStartEventMap=new Map();for(let[spaNavEvent,navStartCandidate]of
+spaNavEventToNavStartCandidateMap){if(navStartCandidate.title===HANDLE_INPUT_EVENT_TITLE){let inputLatencySlice=inputLatencyEventByBindIdMap.get(Number(navStartCandidate.parentSlice.bindId));if(inputLatencySlice){spaNavEventToNavStartEventMap.set(spaNavEvent,inputLatencySlice);}}else{spaNavEventToNavStartEventMap.set(spaNavEvent,navStartCandidate);}}
+return spaNavEventToNavStartEventMap;}
+function getFirstPaintEvents_(rendererHelper){let isFirstPaintEvent=e=>e.category==='blink'&&e.title==='PaintLayerCompositor::updateIfNeededRecursive';return[...rendererHelper.mainThread.sliceGroup.getDescendantEvents()].filter(isFirstPaintEvent);}
+function getSpaNavigationEventToFirstPaintEventMap_(rendererHelper){let spaNavEvents=getSpaNavigationEvents_(rendererHelper).sort(tr.importer.compareEvents);let firstPaintEvents=getFirstPaintEvents_(rendererHelper).sort(tr.importer.compareEvents);return findFollowingEvents_(spaNavEvents,firstPaintEvents);}
+function findSpaNavigationsOnRenderer(rendererHelper,browserHelper){let spaNavEventToNavStartMap=getSpaNavigationEventToNavigationStartMap_(rendererHelper,browserHelper);let spaNavEventToFirstPaintEventMap=getSpaNavigationEventToFirstPaintEventMap_(rendererHelper);let spaNavigations=[];for(let[spaNavEvent,navStartEvent]of
+spaNavEventToNavStartMap){if(spaNavEventToFirstPaintEventMap.has(spaNavEvent)){let firstPaintEvent=spaNavEventToFirstPaintEventMap.get(spaNavEvent);let isNavStartAsyncSlice=navStartEvent instanceof tr.model.AsyncSlice;spaNavigations.push({navStartCandidates:{inputLatencyAsyncSlice:isNavStartAsyncSlice?navStartEvent:undefined,goToIndexSlice:isNavStartAsyncSlice?undefined:navStartEvent},firstPaintEvent:firstPaintEvent,url:spaNavEvent.args.url});}}
+return spaNavigations;}
+return{findSpaNavigationsOnRenderer,};});'use strict';tr.exportTo('tr.metrics',function(){const SPA_NAVIGATION_START_TO_FIRST_PAINT_DURATION_BIN_BOUNDARY=tr.v.HistogramBinBoundaries.createExponential(1,1000,50);function spaNavigationMetric(histograms,model){let histogram=new tr.v.Histogram('spaNavigationStartToFpDuration',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,SPA_NAVIGATION_START_TO_FIRST_PAINT_DURATION_BIN_BOUNDARY);histogram.description='Latency between the input event causing'+' a SPA navigation and the first paint event after it';histogram.customizeSummaryOptions({count:false,sum:false,});let modelHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(!modelHelper){return;}
+let rendererHelpers=modelHelper.rendererHelpers;if(!rendererHelpers){return;}
+let browserHelper=modelHelper.browserHelper;for(let rendererHelper of Object.values(rendererHelpers)){let spaNavigations=tr.metrics.findSpaNavigationsOnRenderer(rendererHelper,browserHelper);for(let spaNav of spaNavigations){let beginTs=0;if(spaNav.navStartCandidates.inputLatencyAsyncSlice){let beginData=spaNav.navStartCandidates.inputLatencyAsyncSlice.args.data;beginTs=model.convertTimestampToModelTime('traceEventClock',beginData.INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT.time);}else{beginTs=spaNav.navStartCandidates.goToIndexSlice.start;}
+let duration=spaNav.firstPaintEvent.start-beginTs;let breakdownDict=rendererHelper.generateWallClockTimeBreakdownTree(beginTs,spaNav.firstPaintEvent.start);let breakdownDiagnostic=new tr.v.d.Breakdown();breakdownDiagnostic.colorScheme=tr.v.d.COLOR_SCHEME_CHROME_USER_FRIENDLY_CATEGORY_DRIVER;for(let label in breakdownDict){breakdownDiagnostic.set(label,parseInt(breakdownDict[label].total*1e3)/1e3);}
+histogram.addSample(duration,{'Breakdown of [navStart, firstPaint]':breakdownDiagnostic,'Start':new tr.v.d.RelatedEventSet(spaNav.navigationStart),'End':new tr.v.d.RelatedEventSet(spaNav.firstPaintEvent),'Navigation infos':new tr.v.d.Generic({url:spaNav.url,pid:rendererHelper.pid,navStart:beginTs,firstPaint:spaNav.firstPaintEvent.start})});}}
+histograms.addHistogram(histogram);}
+tr.metrics.MetricRegistry.register(spaNavigationMetric);return{spaNavigationMetric,};});'use strict';tr.exportTo('tr.metrics.sh',function(){function perceptualBlend(ir,index,score){return Math.exp(1-score);}
+function filterExpectationsByRange(irs,opt_range){var filteredExpectations=[];irs.forEach(function(ir){if(!(ir instanceof tr.model.um.UserExpectation))return;if(!opt_range||opt_range.intersectsExplicitRangeInclusive(ir.start,ir.end)){filteredExpectations.push(ir);}});return filteredExpectations;}
+return{perceptualBlend,filterExpectationsByRange,};});'use strict';tr.exportTo('tr.metrics.sh',function(){var LATENCY_BOUNDS=tr.v.HistogramBinBoundaries.createLinear(0,20,100);function clockSyncLatencyMetric(values,model){var domains=Array.from(model.clockSyncManager.domainsSeen).sort();for(var i=0;i<domains.length;i++){for(var j=i+1;j<domains.length;j++){var latency=model.clockSyncManager.getTimeTransformerError(domains[i],domains[j]);var hist=new tr.v.Histogram('clock_sync_latency_'+
+domains[i].toLowerCase()+'_to_'+domains[j].toLowerCase(),tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,LATENCY_BOUNDS);hist.customizeSummaryOptions({avg:true,count:false,max:false,min:false,std:false,sum:false,});hist.description='Clock sync latency for domain '+domains[i]+' to domain '+domains[j];hist.addSample(latency);values.addHistogram(hist);}}}
+tr.metrics.MetricRegistry.register(clockSyncLatencyMetric);return{clockSyncLatencyMetric,};});'use strict';tr.exportTo('tr.metrics.sh',function(){var CPU_TIME_PERCENTAGE_BOUNDARIES=tr.v.HistogramBinBoundaries.createExponential(0.01,50,200);function cpuTimeMetric(histograms,model,opt_options){var rangeOfInterest=model.bounds;if(opt_options&&opt_options.rangeOfInterest){rangeOfInterest=opt_options.rangeOfInterest;}else{var chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(chromeHelper){var chromeBounds=chromeHelper.chromeBounds;if(chromeBounds){rangeOfInterest=chromeBounds;}}}
+var allProcessCpuTime=0;for(var pid in model.processes){var process=model.processes[pid];if(tr.model.helpers.ChromeRendererHelper.isTracingProcess(process)){continue;}
+var processCpuTime=0;for(var tid in process.threads){var thread=process.threads[tid];var threadCpuTime=0;thread.sliceGroup.topLevelSlices.forEach(function(slice){if(slice.duration===0)return;if(!slice.cpuDuration)return;var sliceRange=tr.b.math.Range.fromExplicitRange(slice.start,slice.end);var intersection=rangeOfInterest.findIntersection(sliceRange);var fractionOfSliceInsideRangeOfInterest=intersection.duration/slice.duration;threadCpuTime+=slice.cpuDuration*fractionOfSliceInsideRangeOfInterest;});processCpuTime+=threadCpuTime;}
+allProcessCpuTime+=processCpuTime;}
+var normalizedAllProcessCpuTime=0;if(rangeOfInterest.duration>0){normalizedAllProcessCpuTime=allProcessCpuTime/rangeOfInterest.duration;}
+var unit=tr.b.Unit.byName.normalizedPercentage_smallerIsBetter;var cpuTimeHist=new tr.v.Histogram('cpu_time_percentage',unit,CPU_TIME_PERCENTAGE_BOUNDARIES);cpuTimeHist.description='Percent CPU utilization, normalized against a single core. Can be '+'greater than 100% if machine has multiple cores.';cpuTimeHist.customizeSummaryOptions({avg:true,count:false,max:false,min:false,std:false,sum:false});cpuTimeHist.addSample(normalizedAllProcessCpuTime);histograms.addHistogram(cpuTimeHist);}
+tr.metrics.MetricRegistry.register(cpuTimeMetric,{supportsRangeOfInterest:true});return{cpuTimeMetric,};});'use strict';tr.exportTo('tr.metrics.sh',function(){var RESPONSIVENESS_THRESHOLD_MS=50;var INTERACTIVE_WINDOW_SIZE_MS=5*1000;var timeDurationInMs_smallerIsBetter=tr.b.Unit.byName.timeDurationInMs_smallerIsBetter;var RelatedEventSet=tr.v.d.RelatedEventSet;function hasCategoryAndName(event,category,title){return event.title===title&&event.category&&tr.b.getCategoryParts(event.category).includes(category);}
+function createBreakdownDiagnostic(breakdownTree){var breakdownDiagnostic=new tr.v.d.Breakdown();breakdownDiagnostic.colorScheme=tr.v.d.COLOR_SCHEME_CHROME_USER_FRIENDLY_CATEGORY_DRIVER;for(let label in breakdownTree){breakdownDiagnostic.set(label,breakdownTree[label].total);}
 return breakdownDiagnostic;}
-function NavigationStartFinder(rendererHelper){this.navigationStartsForFrameId_={};for(var ev of rendererHelper.mainThread.sliceGroup.childEvents()){if(!hasCategoryAndName(ev,'blink.user_timing','navigationStart'))
-continue;var frameIdRef=ev.args['frame'];var list=this.navigationStartsForFrameId_[frameIdRef];if(list===undefined)
-this.navigationStartsForFrameId_[frameIdRef]=list=[];list.unshift(ev);}}
-NavigationStartFinder.prototype={findNavigationStartEventForFrameBeforeTimestamp:function(frameIdRef,ts){var list=this.navigationStartsForFrameId_[frameIdRef];if(list===undefined){console.warn('No navigationStartEvent found for frame id "'+
-frameIdRef+'"');return undefined;}
-var eventBeforeTimestamp;for(var ev of list){if(ev.start>ts)
-continue;if(eventBeforeTimestamp===undefined)
-eventBeforeTimestamp=ev;}
-if(eventBeforeTimestamp===undefined){console.warn('Failed to find navigationStartEvent.');return undefined;}
-return eventBeforeTimestamp;}};var FIRST_PAINT_BOUNDARIES=tr.v.HistogramBinBoundaries.createLinear(0,1e3,20).addLinearBins(3e3,20).addExponentialBins(20e3,20);function createHistogram(name){var histogram=new tr.v.Histogram(name,timeDurationInMs_smallerIsBetter,FIRST_PAINT_BOUNDARIES);histogram.customizeSummaryOptions({avg:true,count:false,max:true,min:true,std:true,sum:false,percentile:[0.90,0.95,0.99],});return histogram;}
-function findFrameLoaderSnapshotAt(rendererHelper,frameIdRef,ts){var snapshot;var objects=rendererHelper.process.objects;var frameLoaderInstances=objects.instancesByTypeName_['FrameLoader'];if(frameLoaderInstances===undefined){console.warn('Failed to find FrameLoader for frameId "'+frameIdRef+'" at ts '+ts+', the trace maybe incomplete or from an old'+'Chrome.');return undefined;}
-var snapshot;for(var instance of frameLoaderInstances){if(!instance.isAliveAt(ts))
-continue;var maybeSnapshot=instance.getSnapshotAt(ts);if(frameIdRef!==maybeSnapshot.args['frame']['id_ref'])
-continue;snapshot=maybeSnapshot;}
+function NavigationStartFinder(rendererHelper){this.navigationStartsForFrameId_={};for(var ev of rendererHelper.mainThread.sliceGroup.childEvents()){if(!hasCategoryAndName(ev,'blink.user_timing','navigationStart')){continue;}
+var frameIdRef=ev.args['frame'];var list=this.navigationStartsForFrameId_[frameIdRef];if(list===undefined){this.navigationStartsForFrameId_[frameIdRef]=list=[];}
+list.unshift(ev);}}
+NavigationStartFinder.prototype={findNavigationStartEventForFrameBeforeTimestamp:function(frameIdRef,ts){var list=this.navigationStartsForFrameId_[frameIdRef];if(list===undefined)return undefined;var eventBeforeTimestamp;for(var ev of list){if(ev.start>ts)continue;if(eventBeforeTimestamp===undefined){eventBeforeTimestamp=ev;}}
+if(eventBeforeTimestamp===undefined)return undefined;return eventBeforeTimestamp;}};var FIRST_PAINT_BOUNDARIES=tr.v.HistogramBinBoundaries.createLinear(0,1e3,20).addLinearBins(3e3,20).addExponentialBins(20e3,20);function createHistogram(name){var histogram=new tr.v.Histogram(name,timeDurationInMs_smallerIsBetter,FIRST_PAINT_BOUNDARIES);histogram.customizeSummaryOptions({avg:true,count:false,max:true,min:true,std:true,sum:false,});return histogram;}
+function findFrameLoaderSnapshotAt(rendererHelper,frameIdRef,ts){var snapshot;var objects=rendererHelper.process.objects;var frameLoaderInstances=objects.instancesByTypeName_['FrameLoader'];if(frameLoaderInstances===undefined)return undefined;var snapshot;for(var instance of frameLoaderInstances){if(!instance.isAliveAt(ts))continue;var maybeSnapshot=instance.getSnapshotAt(ts);if(frameIdRef!==maybeSnapshot.args['frame']['id_ref'])continue;snapshot=maybeSnapshot;}
 return snapshot;}
-function findAllUserTimingEvents(rendererHelper,title){var targetEvents=[];for(var ev of rendererHelper.process.getDescendantEvents()){if(!hasCategoryAndName(ev,'blink.user_timing',title))
-continue;targetEvents.push(ev);}
+function findAllUserTimingEvents(rendererHelper,title){var targetEvents=[];for(var ev of rendererHelper.process.getDescendantEvents()){if(!hasCategoryAndName(ev,'blink.user_timing',title))continue;targetEvents.push(ev);}
 return targetEvents;}
-function findFirstMeaningfulPaintCandidates(rendererHelper){var isTelemetryInternalEvent=prepareTelemetryInternalEventPredicate(rendererHelper);var candidatesForFrameId={};for(var ev of rendererHelper.process.getDescendantEvents()){if(!hasCategoryAndName(ev,'loading','firstMeaningfulPaintCandidate'))
-continue;if(isTelemetryInternalEvent(ev))
-continue;var frameIdRef=ev.args['frame'];if(frameIdRef===undefined)
-continue;var list=candidatesForFrameId[frameIdRef];if(list===undefined)
-candidatesForFrameId[frameIdRef]=list=[];list.push(ev);}
+function findFirstMeaningfulPaintCandidates(rendererHelper){var candidatesForFrameId={};for(var ev of rendererHelper.process.getDescendantEvents()){if(!hasCategoryAndName(ev,'loading','firstMeaningfulPaintCandidate')){continue;}
+if(rendererHelper.isTelemetryInternalEvent(ev))continue;var frameIdRef=ev.args['frame'];if(frameIdRef===undefined)continue;var list=candidatesForFrameId[frameIdRef];if(list===undefined){candidatesForFrameId[frameIdRef]=list=[];}
+list.push(ev);}
 return candidatesForFrameId;}
-function prepareTelemetryInternalEventPredicate(rendererHelper){var ignoreRegions=[];var internalRegionStart;for(var slice of
-rendererHelper.mainThread.asyncSliceGroup.getDescendantEvents()){if(!!slice.title.match(/^telemetry\.internal\.[^.]*\.start$/))
-internalRegionStart=slice.start;if(!!slice.title.match(/^telemetry\.internal\.[^.]*\.end$/)){var timedEvent=new tr.model.TimedEvent(internalRegionStart);timedEvent.duration=slice.end-internalRegionStart;ignoreRegions.push(timedEvent);}}
-return function isTelemetryInternalEvent(slice){for(var region of ignoreRegions)
-if(region.bounds(slice))
-return true;return false;}}
-var URL_BLACKLIST=['about:blank','data:text/html,pluginplaceholderdata'];function shouldIgnoreURL(url){return URL_BLACKLIST.indexOf(url)>=0;}
-var METRICS=[{valueName:'timeToFirstContentfulPaint',title:'firstContentfulPaint',description:'time to first contentful paint'},{valueName:'timeToOnload',title:'loadEventStart',description:'time to onload. '+'This is temporary metric used for PCv1/v2 sanity checking'}];function timeToFirstContentfulPaintMetric(values,model){var chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);var rendererHelper=findTargetRendererHelper(chromeHelper);var isTelemetryInternalEvent=prepareTelemetryInternalEventPredicate(rendererHelper);var navigationStartFinder=new NavigationStartFinder(rendererHelper);for(var metric of METRICS){var histogram=createHistogram(metric.valueName);histogram.description=metric.description;var targetEvents=findAllUserTimingEvents(rendererHelper,metric.title);for(var ev of targetEvents){if(isTelemetryInternalEvent(ev))
-continue;var frameIdRef=ev.args['frame'];var snapshot=findFrameLoaderSnapshotAt(rendererHelper,frameIdRef,ev.start);if(snapshot===undefined||!snapshot.args.isLoadingMainFrame)
-continue;var url=snapshot.args.documentLoaderURL;if(shouldIgnoreURL(url))
-continue;var navigationStartEvent=navigationStartFinder.findNavigationStartEventForFrameBeforeTimestamp(frameIdRef,ev.start);if(navigationStartEvent===undefined)
-continue;var timeToEvent=ev.start-navigationStartEvent.start;histogram.addSample(timeToEvent,{url:new tr.v.d.Generic(url)});}
-values.addHistogram(histogram);}}
-function addTimeToInteractiveSampleToHistogram(histogram,rendererHelper,navigationStart,firstMeaningfulPaint,url){if(shouldIgnoreURL(url))
-return;var navigationStartTime=navigationStart.start;var firstInteractive=Infinity;var firstInteractiveCandidate=firstMeaningfulPaint;var lastLongTaskEvent=undefined;for(var ev of[...rendererHelper.mainThread.sliceGroup.childEvents()]){if(ev.start<firstInteractiveCandidate)
-continue;var interactiveDurationSoFar=ev.start-firstInteractiveCandidate;if(interactiveDurationSoFar>=INTERACTIVE_WINDOW_SIZE){firstInteractive=firstInteractiveCandidate;break;}
-if(ev.title==='TaskQueueManager::ProcessTaskFromWorkQueue'&&ev.duration>RESPONSIVENESS_THRESHOLD){firstInteractiveCandidate=ev.end-50;lastLongTaskEvent=ev;}}
-var breakdownDiagnostic=createBreakdownDiagnostic(rendererHelper,navigationStartTime,firstInteractive);var timeToFirstInteractive=firstInteractive-navigationStartTime;histogram.addSample(timeToFirstInteractive,{"Start":new RelatedEventSet(navigationStart),"Last long task":new RelatedEventSet(lastLongTaskEvent),"Navigation infos":new tr.v.d.Generic({url:url,pid:rendererHelper.pid,start:navigationStartTime,interactive:firstInteractive}),"Breakdown of [navStart, Interactive]":breakdownDiagnostic,});}
-function timeToFirstMeaningfulPaintAndTimeToInteractiveMetrics(values,model){var chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);var rendererHelper=findTargetRendererHelper(chromeHelper);var navigationStartFinder=new NavigationStartFinder(rendererHelper);var firstMeaningfulPaintHistogram=createHistogram('timeToFirstMeaningfulPaint');firstMeaningfulPaintHistogram.description='time to first meaningful paint';var firstInteractiveHistogram=createHistogram('timeToFirstInteractive');firstInteractiveHistogram.description='time to first interactive';function addFirstMeaningfulPaintSampleToHistogram(frameIdRef,navigationStart,fmpMarkerEvent){var snapshot=findFrameLoaderSnapshotAt(rendererHelper,frameIdRef,fmpMarkerEvent.start);if(snapshot===undefined||!snapshot.args.isLoadingMainFrame)
-return;var url=snapshot.args.documentLoaderURL;if(shouldIgnoreURL(url))
-return;var timeToFirstMeaningfulPaint=fmpMarkerEvent.start-navigationStart.start;var extraDiagnostic={url:url,pid:rendererHelper.pid};var breakdownDiagnostic=createBreakdownDiagnostic(rendererHelper,navigationStart.start,fmpMarkerEvent.start);firstMeaningfulPaintHistogram.addSample(timeToFirstMeaningfulPaint,{"Breakdown of [navStart, FMP]":breakdownDiagnostic,"Start":new RelatedEventSet(navigationStart),"End":new RelatedEventSet(fmpMarkerEvent),"Navigation infos":new tr.v.d.Generic({url:url,pid:rendererHelper.pid,start:navigationStart.start,fmp:fmpMarkerEvent.start}),});return{firstMeaningfulPaint:fmpMarkerEvent.start,url:url};}
-var candidatesForFrameId=findFirstMeaningfulPaintCandidates(rendererHelper);for(var frameIdRef in candidatesForFrameId){var navigationStart;var lastCandidate;for(var ev of candidatesForFrameId[frameIdRef]){var navigationStartForThisCandidate=navigationStartFinder.findNavigationStartEventForFrameBeforeTimestamp(frameIdRef,ev.start);if(navigationStartForThisCandidate===undefined)
-continue;if(navigationStart!==navigationStartForThisCandidate){if(navigationStart!==undefined&&lastCandidate!==undefined){data=addFirstMeaningfulPaintSampleToHistogram(frameIdRef,navigationStart,lastCandidate);if(data!==undefined)
-addTimeToInteractiveSampleToHistogram(firstInteractiveHistogram,rendererHelper,navigationStart,data.firstMeaningfulPaint,data.url);}
+var URL_BLACKLIST=['about:blank','data:text/html,pluginplaceholderdata','data:text/html,chromewebdata'];function shouldIgnoreURL(url){return URL_BLACKLIST.includes(url);}
+function collectTimeToEvent(eventName,rendererHelper,navigationStartFinder){var targetEvents=findAllUserTimingEvents(rendererHelper,eventName);var samples=[];for(var ev of targetEvents){if(rendererHelper.isTelemetryInternalEvent(ev))continue;var frameIdRef=ev.args['frame'];var snapshot=findFrameLoaderSnapshotAt(rendererHelper,frameIdRef,ev.start);if(snapshot===undefined||!snapshot.args.isLoadingMainFrame)continue;var url=snapshot.args.documentLoaderURL;if(shouldIgnoreURL(url))continue;var navigationStartEvent=navigationStartFinder.findNavigationStartEventForFrameBeforeTimestamp(frameIdRef,ev.start);if(navigationStartEvent===undefined)continue;var timeToEvent=ev.start-navigationStartEvent.start;samples.push({value:timeToEvent,diagnostics:{url:new tr.v.d.Generic(url)}});}
+return samples;}
+function addFirstMeaningfulPaintSample(samples,rendererHelper,frameIdRef,navigationStart,fmpMarkerEvent){var snapshot=findFrameLoaderSnapshotAt(rendererHelper,frameIdRef,fmpMarkerEvent.start);if(!snapshot||!snapshot.args.isLoadingMainFrame)return;var url=snapshot.args.documentLoaderURL;if(shouldIgnoreURL(url))return;var timeToFirstMeaningfulPaint=fmpMarkerEvent.start-navigationStart.start;var breakdownTree=rendererHelper.generateWallClockTimeBreakdownTree(navigationStart.start,fmpMarkerEvent.start);var breakdownDiagnostic=createBreakdownDiagnostic(breakdownTree);samples.push({value:timeToFirstMeaningfulPaint,diagnostics:{'Breakdown of [navStart, FMP]':breakdownDiagnostic,'Start':new RelatedEventSet(navigationStart),'End':new RelatedEventSet(fmpMarkerEvent),'Navigation infos':new tr.v.d.Generic({url,pid:rendererHelper.pid,start:navigationStart.start,fmp:fmpMarkerEvent.start}),}});return{firstMeaningfulPaint:fmpMarkerEvent.start,url:url};}
+function addFirstMeaningfulPaintCpuTimeSample(samples,rendererHelper,frameIdRef,navigationStart,fmpMarkerEvent){var rangeOfInterest=tr.b.math.Range.fromExplicitRange(navigationStart.cpuStart,fmpMarkerEvent.cpuStart);var snapshot=findFrameLoaderSnapshotAt(rendererHelper,frameIdRef,fmpMarkerEvent.start);if(!snapshot||!snapshot.args.isLoadingMainFrame)return;var url=snapshot.args.documentLoaderURL;if(shouldIgnoreURL(url))return;var mainThreadCpuTime=getMainThreadCpuTime(rendererHelper,rangeOfInterest);var breakdownTree=rendererHelper.generateCpuTimeBreakdownTree(navigationStart.cpuStart,fmpMarkerEvent.cpuStart);var breakdownDiagnostic=createBreakdownDiagnostic(breakdownTree);samples.push({value:mainThreadCpuTime,diagnostics:{'Breakdown of [navStart, FMP]':breakdownDiagnostic,'Start':new RelatedEventSet(navigationStart),'End':new RelatedEventSet(fmpMarkerEvent),'Navigation infos':new tr.v.d.Generic({url,pid:rendererHelper.pid,start:navigationStart.start,fmp:fmpMarkerEvent.start}),}});}
+function getMainThreadCpuTime(rendererHelper,rangeOfInterest){var mainThreadCpuTime=0;for(var slice of rendererHelper.mainThread.sliceGroup.topLevelSlices){if(!slice.cpuDuration)continue;var sliceRange=tr.b.math.Range.fromExplicitRange(slice.cpuStart,slice.cpuStart+slice.cpuDuration);var intersection=rangeOfInterest.findIntersection(sliceRange);mainThreadCpuTime+=intersection.duration;}
+return mainThreadCpuTime;}
+function addFirstInteractiveSample(samples,rendererHelper,navigationStart,firstMeaningfulPaint,url){if(shouldIgnoreURL(url))return;var navigationStartTime=navigationStart.start;var firstInteractive=Infinity;var firstInteractiveCandidate=firstMeaningfulPaint;var lastLongTaskEvent=undefined;for(var ev of[...rendererHelper.mainThread.sliceGroup.childEvents()]){if(ev.start<firstInteractiveCandidate)continue;var interactiveDurationSoFar=ev.start-firstInteractiveCandidate;if(interactiveDurationSoFar>=INTERACTIVE_WINDOW_SIZE_MS){firstInteractive=firstInteractiveCandidate;break;}
+if(ev.title==='TaskQueueManager::ProcessTaskFromWorkQueue'&&ev.duration>RESPONSIVENESS_THRESHOLD_MS){firstInteractiveCandidate=ev.end-50;lastLongTaskEvent=ev;}}
+var breakdownTree=rendererHelper.generateWallClockTimeBreakdownTree(navigationStartTime,firstInteractive);var breakdownDiagnostic=createBreakdownDiagnostic(breakdownTree);var timeToFirstInteractive=firstInteractive-navigationStartTime;samples.push({value:timeToFirstInteractive,diagnostics:{'Start':new RelatedEventSet(navigationStart),'Last long task':new RelatedEventSet(lastLongTaskEvent),'Navigation infos':new tr.v.d.Generic({url,pid:rendererHelper.pid,start:navigationStartTime,interactive:firstInteractive}),'Breakdown of [navStart, Interactive]':breakdownDiagnostic,}});}
+function collectFirstMeaningfulPaintAndTimeToInteractiveForRenderer(rendererHelper,navigationStartFinder){var firstMeaningfulPaintSamples=[];var firstMeaningfulPaintCpuTimeSamples=[];var firstInteractiveSamples=[];function addSamples(frameIdRef,navigationStart,fmpMarkerEvent){var data=addFirstMeaningfulPaintSample(firstMeaningfulPaintSamples,rendererHelper,frameIdRef,navigationStart,fmpMarkerEvent);addFirstMeaningfulPaintCpuTimeSample(firstMeaningfulPaintCpuTimeSamples,rendererHelper,frameIdRef,navigationStart,fmpMarkerEvent);if(data!==undefined){addFirstInteractiveSample(firstInteractiveSamples,rendererHelper,navigationStart,data.firstMeaningfulPaint,data.url);}}
+var candidatesForFrameId=findFirstMeaningfulPaintCandidates(rendererHelper);for(var frameIdRef in candidatesForFrameId){var navigationStart=undefined;var lastCandidate=undefined;for(var ev of candidatesForFrameId[frameIdRef]){var navigationStartForThisCandidate=navigationStartFinder.findNavigationStartEventForFrameBeforeTimestamp(frameIdRef,ev.start);if(navigationStartForThisCandidate===undefined)continue;if(navigationStart!==navigationStartForThisCandidate){if(navigationStart!==undefined&&lastCandidate!==undefined){addSamples(frameIdRef,navigationStart,lastCandidate);}
 navigationStart=navigationStartForThisCandidate;}
 lastCandidate=ev;}
-if(lastCandidate!==undefined){var data=addFirstMeaningfulPaintSampleToHistogram(frameIdRef,navigationStart,lastCandidate);if(data!==undefined)
-addTimeToInteractiveSampleToHistogram(firstInteractiveHistogram,rendererHelper,navigationStart,data.firstMeaningfulPaint,data.url);}}
-values.addHistogram(firstMeaningfulPaintHistogram);values.addHistogram(firstInteractiveHistogram);}
-function loadingMetric(values,model){timeToFirstContentfulPaintMetric(values,model);timeToFirstMeaningfulPaintAndTimeToInteractiveMetrics(values,model);}
-tr.metrics.MetricRegistry.register(loadingMetric);return{loadingMetric:loadingMetric};});'use strict';tr.exportTo('tr.b',function(){function MultiDimensionalViewNode(title,valueCount){this.title=title;var dimensions=title.length;this.children=new Array(dimensions);for(var i=0;i<dimensions;i++)
-this.children[i]=new Map();this.values=new Array(valueCount);for(var v=0;v<valueCount;v++)
-this.values[v]={self:0,total:0,totalState:NOT_PROVIDED};}
-MultiDimensionalViewNode.TotalState={NOT_PROVIDED:0,LOWER_BOUND:1,EXACT:2};var NOT_PROVIDED=MultiDimensionalViewNode.TotalState.NOT_PROVIDED;var LOWER_BOUND=MultiDimensionalViewNode.TotalState.LOWER_BOUND;var EXACT=MultiDimensionalViewNode.TotalState.EXACT;MultiDimensionalViewNode.prototype={get subRows(){return tr.b.mapValues(this.children[0]);}};function MultiDimensionalViewBuilder(dimensions,valueCount){if(typeof(dimensions)!=='number'||dimensions<0)
-throw new Error('Dimensions must be a non-negative number');this.dimensions_=dimensions;if(typeof(valueCount)!=='number'||valueCount<0)
-throw new Error('Number of values must be a non-negative number');this.valueCount_=valueCount;this.buildRoot_=this.createRootNode_();this.topDownTreeViewRoot_=undefined;this.topDownHeavyViewRoot_=undefined;this.bottomUpHeavyViewNode_=undefined;this.maxDimensionDepths_=new Array(dimensions);for(var d=0;d<dimensions;d++)
-this.maxDimensionDepths_[d]=0;}
+if(lastCandidate!==undefined){addSamples(frameIdRef,navigationStart,lastCandidate);}}
+return{firstMeaningfulPaintSamples,firstMeaningfulPaintCpuTimeSamples,firstInteractiveSamples};}
+function collectLoadingMetricsForRenderer(rendererHelper){var navigationStartFinder=new NavigationStartFinder(rendererHelper);var firstContentfulPaintSamples=collectTimeToEvent('firstContentfulPaint',rendererHelper,navigationStartFinder);var onLoadSamples=collectTimeToEvent('loadEventStart',rendererHelper,navigationStartFinder);var{firstMeaningfulPaintSamples,firstMeaningfulPaintCpuTimeSamples,firstInteractiveSamples}=collectFirstMeaningfulPaintAndTimeToInteractiveForRenderer(rendererHelper,navigationStartFinder);return{firstContentfulPaintSamples,onLoadSamples,firstMeaningfulPaintSamples,firstMeaningfulPaintCpuTimeSamples,firstInteractiveSamples};}
+function addSamplesToHistogram(samples,histogram){for(var sample of samples){histogram.addSample(sample.value,sample.diagnostics);}}
+function loadingMetric(histograms,model){var firstContentfulPaintHistogram=createHistogram('timeToFirstContentfulPaint');firstContentfulPaintHistogram.description='time to first contentful paint';var onLoadHistogram=createHistogram('timeToOnload');onLoadHistogram.description='time to onload. '+'This is temporary metric used for PCv1/v2 sanity checking';var firstMeaningfulPaintHistogram=createHistogram('timeToFirstMeaningfulPaint');firstMeaningfulPaintHistogram.description='time to first meaningful paint';var firstMeaningfulPaintCpuTimeHistogram=createHistogram('cpuTimeToFirstMeaningfulPaint');firstMeaningfulPaintCpuTimeHistogram.description='CPU time to first meaningful paint';var firstInteractiveHistogram=createHistogram('timeToFirstInteractive');firstInteractiveHistogram.description='time to first interactive';var chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);for(var pid in chromeHelper.rendererHelpers){var rendererHelper=chromeHelper.rendererHelpers[pid];if(rendererHelper.isChromeTracingUI)continue;var{firstContentfulPaintSamples,onLoadSamples,firstMeaningfulPaintSamples,firstMeaningfulPaintCpuTimeSamples,firstInteractiveSamples}=collectLoadingMetricsForRenderer(rendererHelper);addSamplesToHistogram(firstContentfulPaintSamples,firstContentfulPaintHistogram);addSamplesToHistogram(onLoadSamples,onLoadHistogram);addSamplesToHistogram(firstMeaningfulPaintSamples,firstMeaningfulPaintHistogram);addSamplesToHistogram(firstMeaningfulPaintCpuTimeSamples,firstMeaningfulPaintCpuTimeHistogram);addSamplesToHistogram(firstInteractiveSamples,firstInteractiveHistogram);}
+histograms.addHistogram(firstContentfulPaintHistogram);histograms.addHistogram(onLoadHistogram);histograms.addHistogram(firstMeaningfulPaintHistogram);histograms.addHistogram(firstMeaningfulPaintCpuTimeHistogram);histograms.addHistogram(firstInteractiveHistogram);}
+tr.metrics.MetricRegistry.register(loadingMetric);return{loadingMetric,collectLoadingMetricsForRenderer,RESPONSIVENESS_THRESHOLD_MS,INTERACTIVE_WINDOW_SIZE_MS,};});'use strict';tr.exportTo('tr.v',function(){class HistogramGrouping{constructor(key,callback,opt_label){this.key=key;this.callback=callback;this.label=opt_label||key;}}
+class HistogramSet{constructor(opt_histograms){this.histogramsByGuid_=new Map();this.sharedDiagnosticsByGuid_=new Map();if(opt_histograms!==undefined){for(let hist of opt_histograms){this.addHistogram(hist);}}}
+addHistogram(hist,opt_diagnostics){if(this.histogramsByGuid_.has(hist.guid)){throw new Error('Cannot add same Histogram twice');}
+if(opt_diagnostics!==undefined){if(opt_diagnostics instanceof tr.v.d.DiagnosticMap){for(let[name,diagnostic]of opt_diagnostics){hist.diagnostics.set(name,diagnostic);}}else{for(var[name,diagnostic]of Object.entries(opt_diagnostics)){hist.diagnostics.set(name,diagnostic);}}}
+this.histogramsByGuid_.set(hist.guid,hist);}
+addSharedDiagnostic(name,diagnostic){this.sharedDiagnosticsByGuid_.set(diagnostic.guid,diagnostic);for(let hist of this){hist.diagnostics.set(name,diagnostic);}}
+get length(){return this.histogramsByGuid_.size;}*[Symbol.iterator](){for(let hist of this.histogramsByGuid_.values()){yield hist;}}
+getHistogramsNamed(name){return[...this].filter(h=>h.name===name);}
+getHistogramNamed(name){let histograms=this.getHistogramsNamed(name);if(histograms.length===0)return undefined;if(histograms.length>1){throw new Error(`Unexpectedly found multiple histograms named"${name}"`);}
+return histograms[0];}
+lookupHistogram(guid){return this.histogramsByGuid_.get(guid);}
+lookupDiagnostic(guid){return this.sharedDiagnosticsByGuid_.get(guid);}
+resolveRelatedHistograms(){const handleDiagnosticMap=dm=>{for(let[name,diagnostic]of dm){if((diagnostic instanceof tr.v.d.RelatedHistogramSet)||(diagnostic instanceof tr.v.d.RelatedHistogramMap)){diagnostic.resolve(this);}}};for(let hist of this){hist.diagnostics.resolveSharedDiagnostics(this);handleDiagnosticMap(hist.diagnostics);for(let dm of hist.nanDiagnosticMaps){handleDiagnosticMap(dm);}
+for(let bin of hist.allBins){for(let dm of bin.diagnosticMaps){handleDiagnosticMap(dm);}}}}
+importDicts(dicts){for(let dict of dicts){if(dict.type&&tr.v.d.Diagnostic.findTypeInfoWithName(dict.type)){this.sharedDiagnosticsByGuid_.set(dict.guid,tr.v.d.Diagnostic.fromDict(dict));}else{this.addHistogram(tr.v.Histogram.fromDict(dict));}}}
+asDicts(){let dicts=[];for(let diagnostic of this.sharedDiagnosticsByGuid_.values()){dicts.push(diagnostic.asDict());}
+for(let hist of this){dicts.push(hist.asDict());}
+return dicts;}
+get sourceHistograms(){let sourceHistograms=new Map(this.histogramsByGuid_);function deleteSourceHistograms(diagnosticMap){for(let[name,diagnostic]of diagnosticMap){if(diagnostic instanceof tr.v.d.RelatedHistogramSet){for(let relatedHist of diagnostic){sourceHistograms.delete(relatedHist.guid);}}else if(diagnostic instanceof tr.v.d.RelatedHistogramMap){for(let[name,relatedHist]of diagnostic){sourceHistograms.delete(relatedHist.guid);}}}}
+for(let hist of this){deleteSourceHistograms(hist.diagnostics);for(let dm of hist.nanDiagnosticMaps){deleteSourceHistograms(dm);}
+for(let b of hist.allBins){for(let dm of b.diagnosticMaps){deleteSourceHistograms(dm);}}}
+return new HistogramSet([...sourceHistograms.values()]);}
+groupHistogramsRecursively(groupings,opt_skipGroupingCallback){function recurse(histograms,level){if(level===groupings.length){return histograms;}
+let grouping=groupings[level];let groupedHistograms=tr.b.groupIntoMap(histograms,grouping.callback);if(opt_skipGroupingCallback&&opt_skipGroupingCallback(grouping,groupedHistograms)){return recurse(histograms,level+1);}
+for(let[key,group]of groupedHistograms){groupedHistograms.set(key,recurse(group,level+1));}
+return groupedHistograms;}
+return recurse([...this],0);}
+deduplicateDiagnostics(){let diagnosticsToCounts=new Map();for(let hist of this){let candidates=[];let telemetryInfo=tr.v.d.TelemetryInfo.getFromHistogram(hist);if(telemetryInfo instanceof tr.v.d.MergedTelemetryInfo){candidates.push(telemetryInfo);}
+let buildbotInfo=tr.v.d.BuildbotInfo.getFromHistogram(hist);if(buildbotInfo instanceof tr.v.d.MergedBuildbotInfo){candidates.push(buildbotInfo);}
+let deviceInfo=tr.v.d.DeviceInfo.getFromHistogram(hist);if(deviceInfo instanceof tr.v.d.MergedDeviceInfo){candidates.push(deviceInfo);}
+for(let diagnostic of candidates){if(diagnostic===undefined)continue;let found=false;for(let[testDiagnostic,count]of diagnosticsToCounts){if(diagnostic.equals(testDiagnostic)){testDiagnostic.addToHistogram(hist);diagnosticsToCounts.set(testDiagnostic,count+1);found=true;break;}}
+if(!found){diagnosticsToCounts.set(diagnostic,1);}}}
+for(let[diagnostic,count]of diagnosticsToCounts){if(count>1){this.sharedDiagnosticsByGuid_.set(diagnostic.guid,diagnostic);}}}
+mergeRelationships(){for(let hist of this){hist.diagnostics.mergeRelationships(hist);}}}
+HistogramSet.GROUPINGS={HISTOGRAM_NAME:new HistogramGrouping('name',h=>h.name),BENCHMARK_NAME:new HistogramGrouping('benchmark',h=>tr.v.d.TelemetryInfo.getField(h,'benchmarkName','')),BENCHMARK_START:new HistogramGrouping('time',h=>tr.v.d.TelemetryInfo.getField(h,'benchmarkStartString','')),STORYSET_REPEAT:new HistogramGrouping('storyset_repeat',h=>tr.v.d.TelemetryInfo.getField(h,'storysetRepeatCounterLabel',0),'storyset repeat'),STORY_NAME:new HistogramGrouping('story',h=>tr.v.d.TelemetryInfo.getField(h,'storyDisplayName','')),LEGACY_TIR_LABEL:new HistogramGrouping('tir',h=>tr.v.d.TelemetryInfo.getField(h,'legacyTIRLabel','')),MASTER_NAME:new HistogramGrouping('master',h=>tr.v.d.BuildbotInfo.getField(h,'buildbotMasterName','')),SLAVE_NAME:new HistogramGrouping('bot',h=>tr.v.d.BuildbotInfo.getField(h,'buildbotName','')),BUILD_NUMBER:new HistogramGrouping('build',h=>tr.v.d.BuildbotInfo.getField(h,'buildNumber','')),DISPLAY_LABEL:new HistogramGrouping('label',h=>tr.v.d.TelemetryInfo.getField(h,'displayLabel','Value'))};return{HistogramGrouping,HistogramSet,};});'use strict';tr.exportTo('tr.e.chrome',function(){function hasTitleAndCategory(event,title,category){return event.title===title&&event.category&&tr.b.getCategoryParts(event.category).includes(category);}
+function getNavStartTimestamps(rendererHelper){var navStartTimestamps=[];for(var e of rendererHelper.mainThread.sliceGroup.childEvents()){if(hasTitleAndCategory(e,'navigationStart','blink.user_timing')){navStartTimestamps.push(e.start);}}
+return navStartTimestamps;}
+function getInteractiveTimestamps(model){var interactiveTimestampsMap=new Map();var chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);for(var rendererHelper of Object.values(chromeHelper.rendererHelpers)){var timestamps=[];interactiveTimestampsMap.set(rendererHelper.pid,timestamps);var samples=tr.metrics.sh.collectLoadingMetricsForRenderer(rendererHelper).firstInteractiveSamples;for(var sample of samples){timestamps.push(sample.diagnostics['Navigation infos'].value.interactive);}}
+return interactiveTimestampsMap;}
+function getPostInteractiveTaskWindows(interactiveTimestamps,navStartTimestamps,traceEndTimestamp){var navStartTsIndex=0;var lastTaskWindowEndTs=undefined;var taskWindows=[];for(var currTTI of interactiveTimestamps){while(navStartTsIndex<navStartTimestamps.length&&navStartTimestamps[navStartTsIndex]<currTTI){navStartTsIndex++;}
+var taskWindowEndTs=navStartTsIndex<navStartTimestamps.length?navStartTimestamps[navStartTsIndex]:traceEndTimestamp;if(taskWindowEndTs===lastTaskWindowEndTs){throw Error('Encountered two consecutive interactive timestamps '+'with no navigationStart between them. '+'PostInteractiveTaskWindow is not well defined in this case.');}
+taskWindows.push(tr.b.math.Range.fromExplicitRange(currTTI,taskWindowEndTs));lastTaskWindowEndTs=taskWindowEndTs;}
+return taskWindows;}
+function contributionToEQT(window,task){var startInWindow=Math.max(window.min,task.start);var endInWindow=Math.min(window.max,task.end);var durationInWindow=endInWindow-startInWindow;if(durationInWindow<=0)return 0;var probabilityOfTask=durationInWindow/(window.max-window.min);var minQueueingTime=task.end-endInWindow;var maxQueueingTime=task.end-startInWindow;var expectedQueueingTimeDueToTask=(maxQueueingTime+minQueueingTime)/2;return probabilityOfTask*expectedQueueingTimeDueToTask;}
+function weightedExpectedQueueingTime(window,weightedTasks){var result=0;for(var task of weightedTasks){result+=contributionToEQT(window,task)*task.weight;}
+return result;}
+function expectedQueueingTime(window,tasks){return weightedExpectedQueueingTime(window,tasks.map(function(task){return{start:task.start,end:task.end,weight:1};}));}
+class SlidingWindow{constructor(startTime,windowSize,sortedTasks){this.windowSize_=windowSize;this.sortedTasks_=sortedTasks;this.range_=tr.b.math.Range.fromExplicitRange(startTime,startTime+windowSize);this.firstTaskIndex_=sortedTasks.findIndex(task=>startTime<task.end);if(this.firstTaskIndex_===-1){this.firstTaskIndex_=sortedTasks.length;}
+this.lastTaskIndex_=-1;while(this.lastTaskIndex_+1<sortedTasks.length&&sortedTasks[this.lastTaskIndex_+1].start<startTime+windowSize){this.lastTaskIndex_++;}
+this.innerEQT_=0;for(var i=this.firstTaskIndex_+1;i<this.lastTaskIndex_;i++){this.innerEQT_+=contributionToEQT(this.range_,sortedTasks[i]);}}
+get getEQT(){var firstTaskEQT=0;if(this.firstTaskIndex_<this.sortedTasks_.length){firstTaskEQT=contributionToEQT(this.range_,this.sortedTasks_[this.firstTaskIndex_]);}
+var lastTaskEQT=0;if(this.firstTaskIndex_<this.lastTaskIndex_){lastTaskEQT=contributionToEQT(this.range_,this.sortedTasks_[this.lastTaskIndex_]);}
+return firstTaskEQT+this.innerEQT_+lastTaskEQT;}
+slide(t){this.range_=tr.b.math.Range.fromExplicitRange(t,t+this.windowSize_);if(this.firstTaskIndex_<this.sortedTasks_.length&&this.sortedTasks_[this.firstTaskIndex_].end<=t){this.firstTaskIndex_++;if(this.firstTaskIndex_<this.lastTaskIndex_){this.innerEQT_-=contributionToEQT(this.range_,this.sortedTasks_[this.firstTaskIndex_]);}}
+if(this.lastTaskIndex_+1<this.sortedTasks_.length&&this.sortedTasks_[this.lastTaskIndex_+1].start<t+this.windowSize_){if(this.firstTaskIndex_<this.lastTaskIndex_){this.innerEQT_+=contributionToEQT(this.range_,this.sortedTasks_[this.lastTaskIndex_]);}
+this.lastTaskIndex_++;}}}
+function maxExpectedQueueingTimeInSlidingWindow(startTime,endTime,windowSize,tasks){if(windowSize<=0){throw Error('The window size must be positive number');}
+if(startTime+windowSize>endTime){throw Error('The sliding window must fit in the specified time range');}
+var sortedTasks=tasks.slice().sort((a,b)=>a.start-b.start);for(var i=1;i<sortedTasks.length;i++){if(sortedTasks[i-1].end>sortedTasks[i].start+1e-3){throw Error('Tasks must not overlap');}}
+var endpoints=[];endpoints.push(startTime);endpoints.push(endTime-windowSize);for(var task of tasks){endpoints.push(task.start-windowSize);endpoints.push(task.start);endpoints.push(task.end-windowSize);endpoints.push(task.end);}
+endpoints=endpoints.filter(x=>(startTime<=x&&x+windowSize<=endTime));endpoints.sort((a,b)=>a-b);var slidingWindow=new SlidingWindow(endpoints[0],windowSize,sortedTasks);var maxEQT=0;for(var t of endpoints){slidingWindow.slide(t);maxEQT=Math.max(maxEQT,slidingWindow.getEQT);}
+return maxEQT;}
+return{getPostInteractiveTaskWindows,getNavStartTimestamps,getInteractiveTimestamps,expectedQueueingTime,maxExpectedQueueingTimeInSlidingWindow,weightedExpectedQueueingTime};});'use strict';tr.exportTo('tr.metrics.sh',function(){const WINDOW_SIZE_MS=500;const EQT_BOUNDARIES=tr.v.HistogramBinBoundaries.createExponential(0.01,WINDOW_SIZE_MS,50);const V8_EVENT_NAMES_TO_FILTERS=new Map([['v8',tr.metrics.v8.utils.isV8Event],['v8:compile',tr.metrics.v8.utils.isCompileEvent],['v8:execute',tr.metrics.v8.utils.isV8ExecuteEvent],['v8:gc',tr.metrics.v8.utils.isGarbageCollectionEvent],['v8:gc:full-mark-compactor',tr.metrics.v8.utils.isFullMarkCompactorEvent],['v8:gc:incremental-marking',tr.metrics.v8.utils.isIncrementalMarkingEvent],['v8:gc:latency-mark-compactor',tr.metrics.v8.utils.isLatencyMarkCompactorEvent],['v8:gc:memory-mark-compactor',tr.metrics.v8.utils.isMemoryMarkCompactorEvent],['v8:gc:scavenger',tr.metrics.v8.utils.isScavengerEvent]]);function containsForcedGC_(slice){return slice.findTopmostSlicesRelativeToThisSlice(tr.metrics.v8.utils.isForcedGarbageCollectionEvent).length>0;}
+function createHistogramForEQT_(name,description){let histogram=new tr.v.Histogram(name,tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,EQT_BOUNDARIES);histogram.customizeSummaryOptions({avg:false,count:false,max:true,min:false,std:false,sum:false,});histogram.description=description;return histogram;}
+function expectedQueueingTimeMetric(histograms,model){const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);const totalHistogram=createHistogramForEQT_(`total:${WINDOW_SIZE_MS}ms_window:renderer_eqt`,`The maximum EQT in a ${WINDOW_SIZE_MS}ms sliding window`+' for a given renderer');const interactiveHistogram=createHistogramForEQT_(`interactive:${WINDOW_SIZE_MS}ms_window:renderer_eqt`,`The maximum EQT in a ${WINDOW_SIZE_MS}ms sliding window`+' for a given renderer while the page is interactive');const rendererHelpers=tr.b.dictionaryValues(chromeHelper.rendererHelpers);const rendererToInteractiveTimestamps=tr.e.chrome.getInteractiveTimestamps(model);for(let rendererHelper of rendererHelpers){if(rendererHelper.isChromeTracingUI)continue;const tasks=rendererHelper.mainThread.sliceGroup.topLevelSlices.filter(slice=>slice.duration>0&&!containsForcedGC_(slice)).map(slice=>{return{start:slice.start,end:slice.end};});totalHistogram.addSample(tr.e.chrome.maxExpectedQueueingTimeInSlidingWindow(rendererHelper.mainThread.bounds.min,rendererHelper.mainThread.bounds.max,WINDOW_SIZE_MS,tasks));const interactiveTimestamps=rendererToInteractiveTimestamps.get(rendererHelper.pid);if(interactiveTimestamps.length===0)continue;if(interactiveTimestamps.length>1){continue;}
+const interactiveWindow=tr.b.math.Range.fromExplicitRange(interactiveTimestamps[0],Infinity).findIntersection(rendererHelper.mainThread.bounds);interactiveHistogram.addSample(tr.e.chrome.maxExpectedQueueingTimeInSlidingWindow(interactiveWindow.min,interactiveWindow.max,WINDOW_SIZE_MS,tasks));}
+addV8ContributionToExpectedQueueingTime(totalHistogram,interactiveHistogram,rendererToInteractiveTimestamps,histograms,model);histograms.addHistogram(totalHistogram);histograms.addHistogram(interactiveHistogram);}
+function addV8ContributionToExpectedQueueingTime(totalEqtHistogram,interactiveEqtHistogram,rendererToInteractiveTimestamps,histograms,model){if(!model.categories.includes('v8'))return;const breakdownForTotal=new tr.v.d.RelatedHistogramMap();const breakdownForInteractive=new tr.v.d.RelatedHistogramMap();for(let[eventName,filter]of V8_EVENT_NAMES_TO_FILTERS){const contribution=contributionToExpectedQueueingTime(filter,eventName,rendererToInteractiveTimestamps,histograms,model);breakdownForTotal.set(eventName,contribution.total);breakdownForInteractive.set(eventName,contribution.interactive);}
+totalEqtHistogram.diagnostics.set('v8',breakdownForTotal);interactiveEqtHistogram.diagnostics.set('v8',breakdownForInteractive);}
+function durationOfTopmostSubSlices(slice,predicate){let duration=0;for(let sub of slice.findTopmostSlicesRelativeToThisSlice(predicate)){duration+=sub.duration;}
+return duration;}
+function contributionToExpectedQueueingTime(eventPredicate,eventName,rendererToInteractiveTimestamps,histograms,model){const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);const totalHistogram=createHistogramForEQT_(`total:${WINDOW_SIZE_MS}ms_window:renderer_eqt:${eventName}`,`Contribution to the expected queueing time by ${eventName}`+' for a given renderer. It is computed as the maximum EQT in'+`a ${WINDOW_SIZE_MS}ms sliding window after shrinking top-level`+`tasks to contain only ${eventName}subevents`);const interactiveHistogram=createHistogramForEQT_(`interactive:${WINDOW_SIZE_MS}ms_window:renderer_eqt:${eventName}`,`Contribution to the expected queueing time by ${eventName}`+' for a given renderer while the page is interactive. It is computed'+`as the maximum EQT in a ${WINDOW_SIZE_MS}ms sliding window after`+`shrinking top-level tasks to contain only ${eventName}subevents`);const rendererHelpers=tr.b.dictionaryValues(chromeHelper.rendererHelpers);for(let rendererHelper of rendererHelpers){if(rendererHelper.isChromeTracingUI)continue;const tasks=rendererHelper.mainThread.sliceGroup.topLevelSlices.filter(slice=>slice.duration>0&&!containsForcedGC_(slice)).map(slice=>{return{start:slice.start,end:slice.start+
+durationOfTopmostSubSlices(slice,eventPredicate)};});totalHistogram.addSample(tr.e.chrome.maxExpectedQueueingTimeInSlidingWindow(rendererHelper.mainThread.bounds.min,rendererHelper.mainThread.bounds.max,WINDOW_SIZE_MS,tasks));const interactiveTimestamps=rendererToInteractiveTimestamps.get(rendererHelper.pid);if(interactiveTimestamps.length===0)continue;if(interactiveTimestamps.length>1){continue;}
+const interactiveWindow=tr.b.math.Range.fromExplicitRange(interactiveTimestamps[0],Infinity).findIntersection(rendererHelper.mainThread.bounds);interactiveHistogram.addSample(tr.e.chrome.maxExpectedQueueingTimeInSlidingWindow(interactiveWindow.min,interactiveWindow.max,WINDOW_SIZE_MS,tasks));}
+histograms.addHistogram(totalHistogram);histograms.addHistogram(interactiveHistogram);return{total:totalHistogram,interactive:interactiveHistogram};}
+tr.metrics.MetricRegistry.register(expectedQueueingTimeMetric);return{expectedQueueingTimeMetric,contributionToExpectedQueueingTime,};});'use strict';tr.exportTo('tr.metrics.sh',function(){var LONG_TASK_MS=50;var LONGEST_TASK_MS=1000;function iterateLongTopLevelTasksOnThreadInRange(thread,opt_range,cb,opt_this){thread.sliceGroup.topLevelSlices.forEach(function(slice){if(opt_range&&!opt_range.intersectsExplicitRangeInclusive(slice.start,slice.end)){return;}
+if(slice.duration<LONG_TASK_MS)return;cb.call(opt_this,slice);});}
+function iterateRendererMainThreads(model,cb,opt_this){var modelHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(modelHelper!==undefined){tr.b.dictionaryValues(modelHelper.rendererHelpers).forEach(function(rendererHelper){if(!rendererHelper.mainThread)return;cb.call(opt_this,rendererHelper.mainThread);});}}
+function longTasksMetric(histograms,model,opt_options){var rangeOfInterest=opt_options?opt_options.rangeOfInterest:undefined;var longTaskHist=new tr.v.Histogram('long tasks',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,tr.v.HistogramBinBoundaries.createLinear(LONG_TASK_MS,LONGEST_TASK_MS,40));longTaskHist.description='durations of long tasks';var slices=new tr.model.EventSet();iterateRendererMainThreads(model,function(thread){iterateLongTopLevelTasksOnThreadInRange(thread,rangeOfInterest,function(task){longTaskHist.addSample(task.duration,{relatedEvents:new tr.v.d.RelatedEventSet([task])});slices.push(task);slices.addEventSet(task.descendentSlices);});});histograms.addHistogram(longTaskHist);var sampleForEvent=undefined;var breakdown=tr.v.d.RelatedHistogramBreakdown.buildFromEvents(histograms,'long tasks ',slices,e=>(model.getUserFriendlyCategoryFromEvent(e)||'unknown'),tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,sampleForEvent,tr.v.HistogramBinBoundaries.createExponential(1,LONGEST_TASK_MS,40));breakdown.colorScheme=tr.v.d.COLOR_SCHEME_CHROME_USER_FRIENDLY_CATEGORY_DRIVER;longTaskHist.diagnostics.set('category',breakdown);}
+tr.metrics.MetricRegistry.register(longTasksMetric,{supportsRangeOfInterest:true});return{longTasksMetric,iterateLongTopLevelTasksOnThreadInRange,iterateRendererMainThreads,LONG_TASK_MS,LONGEST_TASK_MS,};});'use strict';tr.exportTo('tr.b',function(){function MultiDimensionalViewNode(title,valueCount){this.title=title;var dimensions=title.length;this.children=new Array(dimensions);for(var i=0;i<dimensions;i++){this.children[i]=new Map();}
+this.values=new Array(valueCount);for(var v=0;v<valueCount;v++){this.values[v]={self:0,total:0,totalState:NOT_PROVIDED};}}
+MultiDimensionalViewNode.TotalState={NOT_PROVIDED:0,LOWER_BOUND:1,EXACT:2};var NOT_PROVIDED=MultiDimensionalViewNode.TotalState.NOT_PROVIDED;var LOWER_BOUND=MultiDimensionalViewNode.TotalState.LOWER_BOUND;var EXACT=MultiDimensionalViewNode.TotalState.EXACT;MultiDimensionalViewNode.prototype={get subRows(){return tr.b.mapValues(this.children[0]);}};function MultiDimensionalViewBuilder(dimensions,valueCount){if(typeof(dimensions)!=='number'||dimensions<0){throw new Error('Dimensions must be a non-negative number');}
+this.dimensions_=dimensions;if(typeof(valueCount)!=='number'||valueCount<0){throw new Error('Number of values must be a non-negative number');}
+this.valueCount_=valueCount;this.buildRoot_=this.createRootNode_();this.topDownTreeViewRoot_=undefined;this.topDownHeavyViewRoot_=undefined;this.bottomUpHeavyViewNode_=undefined;this.maxDimensionDepths_=new Array(dimensions);for(var d=0;d<dimensions;d++){this.maxDimensionDepths_[d]=0;}}
 MultiDimensionalViewBuilder.ValueKind={SELF:0,TOTAL:1};MultiDimensionalViewBuilder.ViewType={TOP_DOWN_TREE_VIEW:0,TOP_DOWN_HEAVY_VIEW:1,BOTTOM_UP_HEAVY_VIEW:2};MultiDimensionalViewBuilder.prototype={addPath:function(path,values,valueKind){if(this.buildRoot_===undefined){throw new Error('Paths cannot be added after either view has been built');}
-if(path.length!==this.dimensions_)
-throw new Error('Path must be '+this.dimensions_+'-dimensional');if(values.length!==this.valueCount_)
-throw new Error('Must provide '+this.valueCount_+' values');var isTotal;switch(valueKind){case MultiDimensionalViewBuilder.ValueKind.SELF:isTotal=false;break;case MultiDimensionalViewBuilder.ValueKind.TOTAL:isTotal=true;break;default:throw new Error('Invalid value kind: '+valueKind);}
-var node=this.buildRoot_;for(var d=0;d<path.length;d++){var singleDimensionPath=path[d];var singleDimensionPathLength=singleDimensionPath.length;this.maxDimensionDepths_[d]=Math.max(this.maxDimensionDepths_[d],singleDimensionPathLength);for(var i=0;i<singleDimensionPathLength;i++)
-node=this.getOrCreateChildNode_(node,d,singleDimensionPath[i]);}
-for(var v=0;v<this.valueCount_;v++){var addedValue=values[v];if(addedValue===undefined)
-continue;var nodeValue=node.values[v];if(isTotal){nodeValue.total+=addedValue;nodeValue.totalState=EXACT;}else{nodeValue.self+=addedValue;nodeValue.totalState=Math.max(nodeValue.totalState,LOWER_BOUND);}}},buildView:function(viewType){switch(viewType){case MultiDimensionalViewBuilder.ViewType.TOP_DOWN_TREE_VIEW:return this.buildTopDownTreeView();case MultiDimensionalViewBuilder.ViewType.TOP_DOWN_HEAVY_VIEW:return this.buildTopDownHeavyView();case MultiDimensionalViewBuilder.ViewType.BOTTOM_UP_HEAVY_VIEW:return this.buildBottomUpHeavyView();default:throw new Error('Unknown multi-dimensional view type: '+viewType);}},buildTopDownTreeView:function(){if(this.topDownTreeViewRoot_===undefined){var treeViewRoot=this.buildRoot_;this.buildRoot_=undefined;this.setUpMissingChildRelationships_(treeViewRoot,0);this.finalizeTotalValues_(treeViewRoot,0,new WeakMap());this.topDownTreeViewRoot_=treeViewRoot;}
+if(path.length!==this.dimensions_){throw new Error('Path must be '+this.dimensions_+'-dimensional');}
+if(values.length!==this.valueCount_){throw new Error('Must provide '+this.valueCount_+' values');}
+var isTotal;switch(valueKind){case MultiDimensionalViewBuilder.ValueKind.SELF:isTotal=false;break;case MultiDimensionalViewBuilder.ValueKind.TOTAL:isTotal=true;break;default:throw new Error('Invalid value kind: '+valueKind);}
+var node=this.buildRoot_;for(var d=0;d<path.length;d++){var singleDimensionPath=path[d];var singleDimensionPathLength=singleDimensionPath.length;this.maxDimensionDepths_[d]=Math.max(this.maxDimensionDepths_[d],singleDimensionPathLength);for(var i=0;i<singleDimensionPathLength;i++){node=this.getOrCreateChildNode_(node,d,singleDimensionPath[i]);}}
+for(var v=0;v<this.valueCount_;v++){var addedValue=values[v];if(addedValue===undefined)continue;var nodeValue=node.values[v];if(isTotal){nodeValue.total+=addedValue;nodeValue.totalState=EXACT;}else{nodeValue.self+=addedValue;nodeValue.totalState=Math.max(nodeValue.totalState,LOWER_BOUND);}}},buildView:function(viewType){switch(viewType){case MultiDimensionalViewBuilder.ViewType.TOP_DOWN_TREE_VIEW:return this.buildTopDownTreeView();case MultiDimensionalViewBuilder.ViewType.TOP_DOWN_HEAVY_VIEW:return this.buildTopDownHeavyView();case MultiDimensionalViewBuilder.ViewType.BOTTOM_UP_HEAVY_VIEW:return this.buildBottomUpHeavyView();default:throw new Error('Unknown multi-dimensional view type: '+viewType);}},buildTopDownTreeView:function(){if(this.topDownTreeViewRoot_===undefined){var treeViewRoot=this.buildRoot_;this.buildRoot_=undefined;this.setUpMissingChildRelationships_(treeViewRoot,0);this.finalizeTotalValues_(treeViewRoot,0,new WeakMap());this.topDownTreeViewRoot_=treeViewRoot;}
 return this.topDownTreeViewRoot_;},buildTopDownHeavyView:function(){if(this.topDownHeavyViewRoot_===undefined){this.topDownHeavyViewRoot_=this.buildGenericHeavyView_(this.addDimensionToTopDownHeavyViewNode_.bind(this));}
 return this.topDownHeavyViewRoot_;},buildBottomUpHeavyView:function(){if(this.bottomUpHeavyViewNode_===undefined){this.bottomUpHeavyViewNode_=this.buildGenericHeavyView_(this.addDimensionToBottomUpHeavyViewNode_.bind(this));}
-return this.bottomUpHeavyViewNode_;},createRootNode_:function(){return new MultiDimensionalViewNode(new Array(this.dimensions_),this.valueCount_);},getOrCreateChildNode_:function(parentNode,dimension,childDimensionTitle){if(dimension<0||dimension>=this.dimensions_)
-throw new Error('Invalid dimension');var dimensionChildren=parentNode.children[dimension];var childNode=dimensionChildren.get(childDimensionTitle);if(childNode!==undefined)
-return childNode;var childTitle=parentNode.title.slice();childTitle[dimension]=childDimensionTitle;childNode=new MultiDimensionalViewNode(childTitle,this.valueCount_);dimensionChildren.set(childDimensionTitle,childNode);return childNode;},setUpMissingChildRelationships_:function(node,firstDimensionToSetUp){for(var d=firstDimensionToSetUp;d<this.dimensions_;d++){var currentDimensionChildTitles=new Set(node.children[d].keys());for(var i=0;i<d;i++){for(var previousDimensionChildNode of node.children[i].values()){for(var previousDimensionGrandChildTitle of
+return this.bottomUpHeavyViewNode_;},createRootNode_:function(){return new MultiDimensionalViewNode(new Array(this.dimensions_),this.valueCount_);},getOrCreateChildNode_:function(parentNode,dimension,childDimensionTitle){if(dimension<0||dimension>=this.dimensions_){throw new Error('Invalid dimension');}
+var dimensionChildren=parentNode.children[dimension];var childNode=dimensionChildren.get(childDimensionTitle);if(childNode!==undefined){return childNode;}
+var childTitle=parentNode.title.slice();childTitle[dimension]=childDimensionTitle;childNode=new MultiDimensionalViewNode(childTitle,this.valueCount_);dimensionChildren.set(childDimensionTitle,childNode);return childNode;},setUpMissingChildRelationships_:function(node,firstDimensionToSetUp){for(var d=firstDimensionToSetUp;d<this.dimensions_;d++){var currentDimensionChildTitles=new Set(node.children[d].keys());for(var i=0;i<d;i++){for(var previousDimensionChildNode of node.children[i].values()){for(var previousDimensionGrandChildTitle of
 previousDimensionChildNode.children[d].keys()){currentDimensionChildTitles.add(previousDimensionGrandChildTitle);}}}
 for(var currentDimensionChildTitle of currentDimensionChildTitles){var currentDimensionChildNode=this.getOrCreateChildNode_(node,d,currentDimensionChildTitle);for(var i=0;i<d;i++){for(var previousDimensionChildNode of node.children[i].values()){var previousDimensionGrandChildNode=previousDimensionChildNode.children[d].get(currentDimensionChildTitle);if(previousDimensionGrandChildNode!==undefined){currentDimensionChildNode.children[i].set(previousDimensionChildNode.title[i],previousDimensionGrandChildNode);}}}
-this.setUpMissingChildRelationships_(currentDimensionChildNode,d);}}},finalizeTotalValues_:function(node,firstDimensionToFinalize,dimensionalSelfSumsMap){var dimensionalSelfSums=new Array(this.dimensions_);var minResidual=new Array(this.valueCount_);for(var v=0;v<this.valueCount_;v++)
-minResidual[v]=0;var nodeValues=node.values;var nodeSelfSums=new Array(this.valueCount_);for(var v=0;v<this.valueCount_;v++)
-nodeSelfSums[v]=nodeValues[v].self;for(var d=0;d<this.dimensions_;d++){var childResidualSums=new Array(this.valueCount_);for(var v=0;v<this.valueCount_;v++)
-childResidualSums[v]=0;for(var childNode of node.children[d].values()){if(d>=firstDimensionToFinalize)
-this.finalizeTotalValues_(childNode,d,dimensionalSelfSumsMap);var childNodeSelfSums=dimensionalSelfSumsMap.get(childNode);var childNodeValues=childNode.values;for(var v=0;v<this.valueCount_;v++){nodeSelfSums[v]+=childNodeSelfSums[d][v];var residual=childNodeValues[v].total-
+this.setUpMissingChildRelationships_(currentDimensionChildNode,d);}}},finalizeTotalValues_:function(node,firstDimensionToFinalize,dimensionalSelfSumsMap){var dimensionalSelfSums=new Array(this.dimensions_);var minResidual=new Array(this.valueCount_);for(var v=0;v<this.valueCount_;v++)minResidual[v]=0;var nodeValues=node.values;var nodeSelfSums=new Array(this.valueCount_);for(var v=0;v<this.valueCount_;v++){nodeSelfSums[v]=nodeValues[v].self;}
+for(var d=0;d<this.dimensions_;d++){var childResidualSums=new Array(this.valueCount_);for(var v=0;v<this.valueCount_;v++){childResidualSums[v]=0;}
+for(var childNode of node.children[d].values()){if(d>=firstDimensionToFinalize){this.finalizeTotalValues_(childNode,d,dimensionalSelfSumsMap);}
+var childNodeSelfSums=dimensionalSelfSumsMap.get(childNode);var childNodeValues=childNode.values;for(var v=0;v<this.valueCount_;v++){nodeSelfSums[v]+=childNodeSelfSums[d][v];var residual=childNodeValues[v].total-
 childNodeSelfSums[this.dimensions_-1][v];childResidualSums[v]+=residual;if(childNodeValues[v].totalState>NOT_PROVIDED){nodeValues[v].totalState=Math.max(nodeValues[v].totalState,LOWER_BOUND);}}}
-dimensionalSelfSums[d]=nodeSelfSums.slice();for(var v=0;v<this.valueCount_;v++)
-minResidual[v]=Math.max(minResidual[v],childResidualSums[v]);}
+dimensionalSelfSums[d]=nodeSelfSums.slice();for(var v=0;v<this.valueCount_;v++){minResidual[v]=Math.max(minResidual[v],childResidualSums[v]);}}
 for(var v=0;v<this.valueCount_;v++){nodeValues[v].total=Math.max(nodeValues[v].total,nodeSelfSums[v]+minResidual[v]);}
-if(dimensionalSelfSumsMap.has(node))
-throw new Error('Internal error: Node finalized more than once');dimensionalSelfSumsMap.set(node,dimensionalSelfSums);},buildGenericHeavyView_:function(treeViewNodeHandler){var treeViewRoot=this.buildTopDownTreeView();var heavyViewRoot=this.createRootNode_();heavyViewRoot.values=treeViewRoot.values;var recursionDepthTrackers=new Array(this.dimensions_);for(var d=0;d<this.dimensions_;d++){recursionDepthTrackers[d]=new RecursionDepthTracker(this.maxDimensionDepths_[d],d);}
+if(dimensionalSelfSumsMap.has(node)){throw new Error('Internal error: Node finalized more than once');}
+dimensionalSelfSumsMap.set(node,dimensionalSelfSums);},buildGenericHeavyView_:function(treeViewNodeHandler){var treeViewRoot=this.buildTopDownTreeView();var heavyViewRoot=this.createRootNode_();heavyViewRoot.values=treeViewRoot.values;var recursionDepthTrackers=new Array(this.dimensions_);for(var d=0;d<this.dimensions_;d++){recursionDepthTrackers[d]=new RecursionDepthTracker(this.maxDimensionDepths_[d],d);}
 this.addDimensionsToGenericHeavyViewNode_(treeViewRoot,heavyViewRoot,0,recursionDepthTrackers,false,treeViewNodeHandler);this.setUpMissingChildRelationships_(heavyViewRoot,0);return heavyViewRoot;},addDimensionsToGenericHeavyViewNode_:function(treeViewParentNode,heavyViewParentNode,startDimension,recursionDepthTrackers,previousDimensionsRecursive,treeViewNodeHandler){for(var d=startDimension;d<this.dimensions_;d++){this.addDimensionDescendantsToGenericHeavyViewNode_(treeViewParentNode,heavyViewParentNode,d,recursionDepthTrackers,previousDimensionsRecursive,treeViewNodeHandler);}},addDimensionDescendantsToGenericHeavyViewNode_:function(treeViewParentNode,heavyViewParentNode,currentDimension,recursionDepthTrackers,previousDimensionsRecursive,treeViewNodeHandler){var treeViewChildren=treeViewParentNode.children[currentDimension];var recursionDepthTracker=recursionDepthTrackers[currentDimension];for(var treeViewChildNode of treeViewChildren.values()){recursionDepthTracker.push(treeViewChildNode);treeViewNodeHandler(treeViewChildNode,heavyViewParentNode,currentDimension,recursionDepthTrackers,previousDimensionsRecursive);this.addDimensionDescendantsToGenericHeavyViewNode_(treeViewChildNode,heavyViewParentNode,currentDimension,recursionDepthTrackers,previousDimensionsRecursive,treeViewNodeHandler);recursionDepthTracker.pop();}},addDimensionToTopDownHeavyViewNode_:function(treeViewChildNode,heavyViewParentNode,currentDimension,recursionDepthTrackers,previousDimensionsRecursive){this.addDimensionToTopDownHeavyViewNodeRecursively_(treeViewChildNode,heavyViewParentNode,currentDimension,recursionDepthTrackers,previousDimensionsRecursive,1);},addDimensionToTopDownHeavyViewNodeRecursively_:function(treeViewChildNode,heavyViewParentNode,currentDimension,recursionDepthTrackers,previousDimensionsRecursive,subTreeDepth){var recursionDepthTracker=recursionDepthTrackers[currentDimension];var currentDimensionRecursive=subTreeDepth<=recursionDepthTracker.recursionDepth;var currentOrPreviousDimensionsRecursive=currentDimensionRecursive||previousDimensionsRecursive;var dimensionTitle=treeViewChildNode.title[currentDimension];var heavyViewChildNode=this.getOrCreateChildNode_(heavyViewParentNode,currentDimension,dimensionTitle);this.addNodeValues_(treeViewChildNode,heavyViewChildNode,!currentOrPreviousDimensionsRecursive);this.addDimensionsToGenericHeavyViewNode_(treeViewChildNode,heavyViewChildNode,currentDimension+1,recursionDepthTrackers,currentOrPreviousDimensionsRecursive,this.addDimensionToTopDownHeavyViewNode_.bind(this));for(var treeViewGrandChildNode of
 treeViewChildNode.children[currentDimension].values()){recursionDepthTracker.push(treeViewGrandChildNode);this.addDimensionToTopDownHeavyViewNodeRecursively_(treeViewGrandChildNode,heavyViewChildNode,currentDimension,recursionDepthTrackers,previousDimensionsRecursive,subTreeDepth+1);recursionDepthTracker.pop();}},addDimensionToBottomUpHeavyViewNode_:function(treeViewChildNode,heavyViewParentNode,currentDimension,recursionDepthTrackers,previousDimensionsRecursive){var recursionDepthTracker=recursionDepthTrackers[currentDimension];var bottomIndex=recursionDepthTracker.bottomIndex;var topIndex=recursionDepthTracker.topIndex;var firstNonRecursiveIndex=bottomIndex+recursionDepthTracker.recursionDepth;var viewNodePath=recursionDepthTracker.viewNodePath;var trackerAncestorNode=recursionDepthTracker.trackerAncestorNode;var heavyViewDescendantNode=heavyViewParentNode;for(var i=bottomIndex;i<topIndex;i++){var treeViewAncestorNode=viewNodePath[i];var dimensionTitle=treeViewAncestorNode.title[currentDimension];heavyViewDescendantNode=this.getOrCreateChildNode_(heavyViewDescendantNode,currentDimension,dimensionTitle);var currentDimensionRecursive=i<firstNonRecursiveIndex;var currentOrPreviousDimensionsRecursive=currentDimensionRecursive||previousDimensionsRecursive;this.addNodeValues_(treeViewChildNode,heavyViewDescendantNode,!currentOrPreviousDimensionsRecursive);this.addDimensionsToGenericHeavyViewNode_(treeViewChildNode,heavyViewDescendantNode,currentDimension+1,recursionDepthTrackers,currentOrPreviousDimensionsRecursive,this.addDimensionToBottomUpHeavyViewNode_.bind(this));}},addNodeValues_:function(sourceNode,targetNode,addTotal){var targetNodeValues=targetNode.values;var sourceNodeValues=sourceNode.values;for(var v=0;v<this.valueCount_;v++){var targetNodeValue=targetNodeValues[v];var sourceNodeValue=sourceNodeValues[v];targetNodeValue.self+=sourceNodeValue.self;if(addTotal){targetNodeValue.total+=sourceNodeValue.total;if(sourceNodeValue.totalState>NOT_PROVIDED){targetNodeValue.totalState=Math.max(targetNodeValue.totalState,LOWER_BOUND);}}}}};function RecursionDepthTracker(maxDepth,dimension){this.titlePath=new Array(maxDepth);this.viewNodePath=new Array(maxDepth);this.bottomIndex=this.topIndex=maxDepth;this.dimension_=dimension;this.currentTrackerNode_=this.createNode_(0,undefined);}
-RecursionDepthTracker.prototype={push:function(viewNode){if(this.bottomIndex===0)
-throw new Error('Cannot push to a full tracker');var title=viewNode.title[this.dimension_];this.bottomIndex--;this.titlePath[this.bottomIndex]=title;this.viewNodePath[this.bottomIndex]=viewNode;var childTrackerNode=this.currentTrackerNode_.children.get(title);if(childTrackerNode!==undefined){this.currentTrackerNode_=childTrackerNode;return;}
-var maxLengths=zFunction(this.titlePath,this.bottomIndex);var recursionDepth=0;for(var i=0;i<maxLengths.length;i++)
-recursionDepth=Math.max(recursionDepth,maxLengths[i]);childTrackerNode=this.createNode_(recursionDepth,this.currentTrackerNode_);this.currentTrackerNode_.children.set(title,childTrackerNode);this.currentTrackerNode_=childTrackerNode;},pop:function(){if(this.bottomIndex===this.topIndex)
-throw new Error('Cannot pop from an empty tracker');this.titlePath[this.bottomIndex]=undefined;this.viewNodePath[this.bottomIndex]=undefined;this.bottomIndex++;this.currentTrackerNode_=this.currentTrackerNode_.parent;},get recursionDepth(){return this.currentTrackerNode_.recursionDepth;},createNode_:function(recursionDepth,parent){return{recursionDepth:recursionDepth,parent:parent,children:new Map()};}};function zFunction(list,startIndex){var n=list.length-startIndex;if(n===0)
-return[];var z=new Array(n);z[0]=0;for(var i=1,left=0,right=0;i<n;++i){var maxLength;if(i<=right)
-maxLength=Math.min(right-i+1,z[i-left]);else
-maxLength=0;while(i+maxLength<n&&list[startIndex+maxLength]===list[startIndex+i+maxLength]){++maxLength;}
+RecursionDepthTracker.prototype={push:function(viewNode){if(this.bottomIndex===0){throw new Error('Cannot push to a full tracker');}
+var title=viewNode.title[this.dimension_];this.bottomIndex--;this.titlePath[this.bottomIndex]=title;this.viewNodePath[this.bottomIndex]=viewNode;var childTrackerNode=this.currentTrackerNode_.children.get(title);if(childTrackerNode!==undefined){this.currentTrackerNode_=childTrackerNode;return;}
+var maxLengths=zFunction(this.titlePath,this.bottomIndex);var recursionDepth=0;for(var i=0;i<maxLengths.length;i++){recursionDepth=Math.max(recursionDepth,maxLengths[i]);}
+childTrackerNode=this.createNode_(recursionDepth,this.currentTrackerNode_);this.currentTrackerNode_.children.set(title,childTrackerNode);this.currentTrackerNode_=childTrackerNode;},pop:function(){if(this.bottomIndex===this.topIndex){throw new Error('Cannot pop from an empty tracker');}
+this.titlePath[this.bottomIndex]=undefined;this.viewNodePath[this.bottomIndex]=undefined;this.bottomIndex++;this.currentTrackerNode_=this.currentTrackerNode_.parent;},get recursionDepth(){return this.currentTrackerNode_.recursionDepth;},createNode_:function(recursionDepth,parent){return{recursionDepth:recursionDepth,parent:parent,children:new Map()};}};function zFunction(list,startIndex){var n=list.length-startIndex;if(n===0)return[];var z=new Array(n);z[0]=0;for(var i=1,left=0,right=0;i<n;++i){var maxLength;if(i<=right){maxLength=Math.min(right-i+1,z[i-left]);}else{maxLength=0;}
+while(i+maxLength<n&&list[startIndex+maxLength]===list[startIndex+i+maxLength]){++maxLength;}
 if(i+maxLength-1>right){left=i;right=i+maxLength-1;}
 z[i]=maxLength;}
 return z;}
-return{MultiDimensionalViewBuilder:MultiDimensionalViewBuilder,MultiDimensionalViewNode:MultiDimensionalViewNode,RecursionDepthTracker:RecursionDepthTracker,zFunction:zFunction};});'use strict';tr.exportTo('tr.metrics.sh',function(){var BACKGROUND=tr.model.ContainerMemoryDump.LevelOfDetail.BACKGROUND;var LIGHT=tr.model.ContainerMemoryDump.LevelOfDetail.LIGHT;var DETAILED=tr.model.ContainerMemoryDump.LevelOfDetail.DETAILED;var sizeInBytes_smallerIsBetter=tr.b.Unit.byName.sizeInBytes_smallerIsBetter;var unitlessNumber_smallerIsBetter=tr.b.Unit.byName.unitlessNumber_smallerIsBetter;var DISPLAYED_SIZE_NUMERIC_NAME=tr.model.MemoryAllocatorDump.DISPLAYED_SIZE_NUMERIC_NAME;var LEVEL_OF_DETAIL_NAMES=new Map();LEVEL_OF_DETAIL_NAMES.set(BACKGROUND,'background');LEVEL_OF_DETAIL_NAMES.set(LIGHT,'light');LEVEL_OF_DETAIL_NAMES.set(DETAILED,'detailed');var BOUNDARIES_FOR_UNIT_MAP=new WeakMap();BOUNDARIES_FOR_UNIT_MAP.set(unitlessNumber_smallerIsBetter,tr.v.HistogramBinBoundaries.createLinear(0,20,20));BOUNDARIES_FOR_UNIT_MAP.set(sizeInBytes_smallerIsBetter,new tr.v.HistogramBinBoundaries(0).addBinBoundary(1024).addExponentialBins(16*1024*1024*1024,4*24));function memoryMetric(values,model,opt_options){var rangeOfInterest=opt_options?opt_options.rangeOfInterest:undefined;var browserNameToGlobalDumps=splitGlobalDumpsByBrowserName(model,rangeOfInterest);addGeneralMemoryDumpValues(browserNameToGlobalDumps,values);addDetailedMemoryDumpValues(browserNameToGlobalDumps,values);addMemoryDumpCountValues(browserNameToGlobalDumps,values);}
+return{MultiDimensionalViewBuilder,MultiDimensionalViewNode,RecursionDepthTracker,zFunction,};});'use strict';tr.exportTo('tr.b',function(){class FixedColorScheme{constructor(namesToColors){this.namesToColors_=namesToColors;}
+static fromNames(names){var namesToColors=new Map();var generator=new tr.b.SinebowColorGenerator();for(var name of names){namesToColors.set(name,generator.colorForKey(name));}
+return new FixedColorScheme(namesToColors);}
+getColor(name){var color=this.namesToColors_.get(name);if(color===undefined)throw new Error('Unknown color: '+name);return color;}}
+var MemoryColumnColorScheme=new FixedColorScheme(new Map([['used_memory_column',new tr.b.Color(0,0,255)],['older_used_memory_column',new tr.b.Color(153,204,255)],['tracing_memory_column',new tr.b.Color(153,153,153)]]));function FixedColorSchemeRegistry(){}
+FixedColorSchemeRegistry.lookUp=function(name){var info=this.findTypeInfoMatching(info=>info.metadata.name===name);if(!info)return undefined;return info.constructor();};var options=new tr.b.ExtensionRegistryOptions(tr.b.BASIC_REGISTRY_MODE);tr.b.decorateExtensionRegistry(FixedColorSchemeRegistry,options);return{MemoryColumnColorScheme,FixedColorScheme,FixedColorSchemeRegistry,};});'use strict';tr.exportTo('tr.metrics.sh',function(){const CHROME_PROCESS_NAMES={BROWSER:'browser_process',RENDERER:'renderer_processes',ALL:'all_processes',GPU:'gpu_process',PPAPI:'ppapi_process',UNKNOWN:'unknown_processes',};const PROCESS_COLOR_SCHEME_NAME='ChromeProcessNames';const PROCESS_COLOR_SCHEME=tr.b.FixedColorScheme.fromNames(Object.values(CHROME_PROCESS_NAMES));tr.b.FixedColorSchemeRegistry.register(()=>PROCESS_COLOR_SCHEME,{'name':PROCESS_COLOR_SCHEME_NAME,});return{CHROME_PROCESS_NAMES,PROCESS_COLOR_SCHEME,PROCESS_COLOR_SCHEME_NAME,};});'use strict';tr.exportTo('tr.metrics.sh',function(){var BACKGROUND=tr.model.ContainerMemoryDump.LevelOfDetail.BACKGROUND;var LIGHT=tr.model.ContainerMemoryDump.LevelOfDetail.LIGHT;var DETAILED=tr.model.ContainerMemoryDump.LevelOfDetail.DETAILED;var sizeInBytes_smallerIsBetter=tr.b.Unit.byName.sizeInBytes_smallerIsBetter;var count_smallerIsBetter=tr.b.Unit.byName.count_smallerIsBetter;var DISPLAYED_SIZE_NUMERIC_NAME=tr.model.MemoryAllocatorDump.DISPLAYED_SIZE_NUMERIC_NAME;var LEVEL_OF_DETAIL_NAMES=new Map();LEVEL_OF_DETAIL_NAMES.set(BACKGROUND,'background');LEVEL_OF_DETAIL_NAMES.set(LIGHT,'light');LEVEL_OF_DETAIL_NAMES.set(DETAILED,'detailed');var HEAP_PROFILER_DETAIL_NAME='heap_profiler';var BOUNDARIES_FOR_UNIT_MAP=new WeakMap();BOUNDARIES_FOR_UNIT_MAP.set(count_smallerIsBetter,tr.v.HistogramBinBoundaries.createLinear(0,20,20));BOUNDARIES_FOR_UNIT_MAP.set(sizeInBytes_smallerIsBetter,new tr.v.HistogramBinBoundaries(0).addBinBoundary(1024).addExponentialBins(16*1024*1024*1024,4*24));var CHROME_PROCESS_NAMES=tr.metrics.sh.CHROME_PROCESS_NAMES;var PROCESS_COLOR_SCHEME=tr.metrics.sh.PROCESS_COLOR_SCHEME;var PROCESS_COLOR_SCHEME_NAME=tr.metrics.sh.PROCESS_COLOR_SCHEME_NAME;function memoryMetric(values,model,opt_options){var rangeOfInterest=opt_options?opt_options.rangeOfInterest:undefined;var browserNameToGlobalDumps=splitGlobalDumpsByBrowserName(model,rangeOfInterest);addGeneralMemoryDumpValues(browserNameToGlobalDumps,values);addDetailedMemoryDumpValues(browserNameToGlobalDumps,values);addMemoryDumpCountValues(browserNameToGlobalDumps,values);}
 function splitGlobalDumpsByBrowserName(model,opt_rangeOfInterest){var chromeModelHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);var browserNameToGlobalDumps=new Map();var globalDumpToBrowserHelper=new WeakMap();if(chromeModelHelper){chromeModelHelper.browserHelpers.forEach(function(helper){var globalDumps=skipDumpsThatDoNotIntersectRange(helper.process.memoryDumps.map(d=>d.globalMemoryDump),opt_rangeOfInterest);globalDumps.forEach(function(globalDump){var existingHelper=globalDumpToBrowserHelper.get(globalDump);if(existingHelper!==undefined){throw new Error('Memory dump ID clash across multiple browsers '+'with PIDs: '+existingHelper.pid+' and '+helper.pid);}
 globalDumpToBrowserHelper.set(globalDump,helper);});makeKeyUniqueAndSet(browserNameToGlobalDumps,canonicalizeName(helper.browserName),globalDumps);});}
 var unclassifiedGlobalDumps=skipDumpsThatDoNotIntersectRange(model.globalMemoryDumps.filter(g=>!globalDumpToBrowserHelper.has(g)),opt_rangeOfInterest);if(unclassifiedGlobalDumps.length>0){makeKeyUniqueAndSet(browserNameToGlobalDumps,'unknown_browser',unclassifiedGlobalDumps);}
 return browserNameToGlobalDumps;}
-function skipDumpsThatDoNotIntersectRange(dumps,opt_range){if(!opt_range)
-return dumps;return dumps.filter(d=>opt_range.intersectsExplicitRangeInclusive(d.start,d.end));}
+function skipDumpsThatDoNotIntersectRange(dumps,opt_range){if(!opt_range)return dumps;return dumps.filter(d=>opt_range.intersectsExplicitRangeInclusive(d.start,d.end));}
 function canonicalizeName(name){return name.toLowerCase().replace(' ','_');}
-var USER_FRIENDLY_BROWSER_NAMES={'chrome':'Chrome','webview':'WebView','unknown_browser':'an unknown browser'};function convertBrowserNameToUserFriendlyName(browserName){for(var baseName in USER_FRIENDLY_BROWSER_NAMES){if(!browserName.startsWith(baseName))
-continue;var userFriendlyBaseName=USER_FRIENDLY_BROWSER_NAMES[baseName];var suffix=browserName.substring(baseName.length);if(suffix.length===0)
-return userFriendlyBaseName;else if(/^\d+$/.test(suffix))
-return userFriendlyBaseName+'('+suffix+')';}
+var USER_FRIENDLY_BROWSER_NAMES={'chrome':'Chrome','webview':'WebView','unknown_browser':'an unknown browser'};function convertBrowserNameToUserFriendlyName(browserName){for(var baseName in USER_FRIENDLY_BROWSER_NAMES){if(!browserName.startsWith(baseName))continue;var userFriendlyBaseName=USER_FRIENDLY_BROWSER_NAMES[baseName];var suffix=browserName.substring(baseName.length);if(suffix.length===0){return userFriendlyBaseName;}else if(/^\d+$/.test(suffix)){return userFriendlyBaseName+'('+suffix+')';}}
 return'\''+browserName+'\' browser';}
-function canonicalizeProcessName(rawProcessName){if(!rawProcessName)
-return'unknown_processes';var baseCanonicalName=canonicalizeName(rawProcessName);switch(baseCanonicalName){case'renderer':return'renderer_processes';case'browser':return'browser_process';default:return baseCanonicalName;}}
-function convertProcessNameToUserFriendlyName(processName,opt_requirePlural){switch(processName){case'browser_process':return opt_requirePlural?'browser processes':'the browser process';case'renderer_processes':return'renderer processes';case'gpu_process':return opt_requirePlural?'GPU processes':'the GPU process';case'ppapi_process':return opt_requirePlural?'PPAPI processes':'the PPAPI process';case'all_processes':return'all processes';case'unknown_processes':return'unknown processes';default:return'\''+processName+'\' processes';}}
+function canonicalizeProcessName(rawProcessName){if(!rawProcessName)return CHROME_PROCESS_NAMES.UNKNOWN;const baseCanonicalName=canonicalizeName(rawProcessName);switch(baseCanonicalName){case'renderer':return CHROME_PROCESS_NAMES.RENDERER;case'browser':return CHROME_PROCESS_NAMES.BROWSER;}
+if(Object.values(CHROME_PROCESS_NAMES).includes(baseCanonicalName)){return baseCanonicalName;}
+throw new Error(`Unknown process id"${baseCanonicalName}".`+' Please add it to |CHROME_PROCESS_NAMES|.');}
+function convertProcessNameToUserFriendlyName(processName,opt_requirePlural){switch(processName){case CHROME_PROCESS_NAMES.BROWSER:return opt_requirePlural?'browser processes':'the browser process';case CHROME_PROCESS_NAMES.RENDERER:return'renderer processes';case CHROME_PROCESS_NAMES.GPU:return opt_requirePlural?'GPU processes':'the GPU process';case CHROME_PROCESS_NAMES.PPAPI:return opt_requirePlural?'PPAPI processes':'the PPAPI process';case CHROME_PROCESS_NAMES.ALL:return'all processes';case CHROME_PROCESS_NAMES.UNKNOWN:return'unknown processes';default:return'\''+processName+'\' processes';}}
 function makeKeyUniqueAndSet(map,key,value){var uniqueKey=key;var nextIndex=2;while(map.has(uniqueKey)){uniqueKey=key+nextIndex;nextIndex++;}
 map.set(uniqueKey,value);}
-function addGeneralMemoryDumpValues(browserNameToGlobalDumps,values){addMemoryDumpValues(browserNameToGlobalDumps,gmd=>true,function(processDump,addProcessScalar){addProcessScalar({source:'process_count',value:1,unit:unitlessNumber_smallerIsBetter,descriptionPrefixBuilder:buildProcessCountDescriptionPrefix});if(processDump.totals!==undefined){tr.b.iterItems(SYSTEM_TOTAL_VALUE_PROPERTIES,function(propertyName,propertySpec){addProcessScalar({source:'reported_by_os',property:propertyName,component:['system_memory'],value:propertySpec.getPropertyFunction(processDump),unit:sizeInBytes_smallerIsBetter,descriptionPrefixBuilder:propertySpec.descriptionPrefixBuilder});});}
-if(processDump.memoryAllocatorDumps===undefined)
-return;processDump.memoryAllocatorDumps.forEach(function(rootAllocatorDump){tr.b.iterItems(CHROME_VALUE_PROPERTIES,function(propertyName,descriptionPrefixBuilder){addProcessScalar({source:'reported_by_chrome',component:[rootAllocatorDump.name],property:propertyName,value:rootAllocatorDump.numerics[propertyName],descriptionPrefixBuilder:descriptionPrefixBuilder});});if(rootAllocatorDump.numerics['allocated_objects_size']===undefined){var allocatedObjectsDump=rootAllocatorDump.getDescendantDumpByFullName('allocated_objects');if(allocatedObjectsDump!==undefined){addProcessScalar({source:'reported_by_chrome',component:[rootAllocatorDump.name],property:'allocated_objects_size',value:allocatedObjectsDump.numerics['size'],descriptionPrefixBuilder:CHROME_VALUE_PROPERTIES['allocated_objects_size']});}}});addV8MemoryDumpValues(processDump,addProcessScalar);},function(componentTree){var tracingNode=componentTree.children[1].get('tracing');if(tracingNode===undefined)
-return;for(var i=0;i<componentTree.values.length;i++)
-componentTree.values[i].total-=tracingNode.values[i].total;},values);}
-function addV8MemoryDumpValues(processDump,addProcessScalar){var v8Dump=processDump.getMemoryAllocatorDumpByFullName('v8');if(v8Dump===undefined)
-return;v8Dump.children.forEach(function(isolateDump){var mallocDump=isolateDump.getDescendantDumpByFullName('malloc');if(mallocDump!==undefined){addV8ComponentValues(mallocDump,['v8','allocated_by_malloc'],addProcessScalar);}
-var heapDump=isolateDump.getDescendantDumpByFullName('heap_spaces');if(heapDump!==undefined){addV8ComponentValues(heapDump,['v8','heap'],addProcessScalar);heapDump.children.forEach(function(spaceDump){if(spaceDump.name==='other_spaces')
-return;addV8ComponentValues(spaceDump,['v8','heap',spaceDump.name],addProcessScalar);});}});addProcessScalar({source:'reported_by_chrome',component:['v8'],property:'code_and_metadata_size',value:v8Dump.numerics['code_and_metadata_size'],descriptionPrefixBuilder:buildCodeAndMetadataSizeValueDescriptionPrefix});addProcessScalar({source:'reported_by_chrome',component:['v8'],property:'code_and_metadata_size',value:v8Dump.numerics['bytecode_and_metadata_size'],descriptionPrefixBuilder:buildCodeAndMetadataSizeValueDescriptionPrefix});}
-function addV8ComponentValues(componentDump,componentPath,addProcessScalar){tr.b.iterItems(CHROME_VALUE_PROPERTIES,function(propertyName,descriptionPrefixBuilder){addProcessScalar({source:'reported_by_chrome',component:componentPath,property:propertyName,value:componentDump.numerics[propertyName],descriptionPrefixBuilder:descriptionPrefixBuilder});});}
-function buildProcessCountDescriptionPrefix(componentPath,processName){if(componentPath.length>0){throw new Error('Unexpected process count non-empty component path: '+
+function addGeneralMemoryDumpValues(browserNameToGlobalDumps,values){addMemoryDumpValues(browserNameToGlobalDumps,gmd=>true,function(processDump,addProcessScalar){addProcessScalar({source:'process_count',property:PROCESS_COUNT,value:1});if(processDump.totals!==undefined){addProcessScalar({source:'reported_by_os',property:RESIDENT_SIZE,component:['system_memory'],value:processDump.totals['residentBytes']});addProcessScalar({source:'reported_by_os',property:PEAK_RESIDENT_SIZE,component:['system_memory'],value:processDump.totals['peakResidentBytes']});}
+if(processDump.memoryAllocatorDumps===undefined)return;processDump.memoryAllocatorDumps.forEach(function(rootAllocatorDump){CHROME_VALUE_PROPERTIES.forEach(function(property){addProcessScalar({source:'reported_by_chrome',component:[rootAllocatorDump.name],property:property,value:rootAllocatorDump.numerics[property.name]});});if(rootAllocatorDump.numerics['allocated_objects_size']===undefined){var allocatedObjectsDump=rootAllocatorDump.getDescendantDumpByFullName('allocated_objects');if(allocatedObjectsDump!==undefined){addProcessScalar({source:'reported_by_chrome',component:[rootAllocatorDump.name],property:ALLOCATED_OBJECTS_SIZE,value:allocatedObjectsDump.numerics['size']});}}});addV8MemoryDumpValues(processDump,addProcessScalar);},function(componentTree){var tracingNode=componentTree.children[1].get('tracing');if(tracingNode===undefined)return;for(var i=0;i<componentTree.values.length;i++){componentTree.values[i].total-=tracingNode.values[i].total;}},values);}
+function addV8MemoryDumpValues(processDump,addProcessScalar){var v8Dump=processDump.getMemoryAllocatorDumpByFullName('v8');if(v8Dump===undefined)return;v8Dump.children.forEach(function(isolateDump){var mallocDump=isolateDump.getDescendantDumpByFullName('malloc');if(mallocDump!==undefined){addV8ComponentValues(mallocDump,['v8','allocated_by_malloc'],addProcessScalar);}
+var heapDump=isolateDump.getDescendantDumpByFullName('heap_spaces');if(heapDump!==undefined){addV8ComponentValues(heapDump,['v8','heap'],addProcessScalar);heapDump.children.forEach(function(spaceDump){if(spaceDump.name==='other_spaces')return;addV8ComponentValues(spaceDump,['v8','heap',spaceDump.name],addProcessScalar);});}});addProcessScalar({source:'reported_by_chrome',component:['v8'],property:CODE_AND_METADATA_SIZE,value:v8Dump.numerics['code_and_metadata_size']});addProcessScalar({source:'reported_by_chrome',component:['v8'],property:CODE_AND_METADATA_SIZE,value:v8Dump.numerics['bytecode_and_metadata_size']});}
+function addV8ComponentValues(componentDump,componentPath,addProcessScalar){CHROME_VALUE_PROPERTIES.forEach(function(property){addProcessScalar({source:'reported_by_chrome',component:componentPath,property:property,value:componentDump.numerics[property.name]});});}
+var PROCESS_COUNT={unit:count_smallerIsBetter,buildDescriptionPrefix:function(componentPath,processName){if(componentPath.length>0){throw new Error('Unexpected process count non-empty component path: '+
 componentPath.join(':'));}
-return'total number of '+convertProcessNameToUserFriendlyName(processName,true);}
-function buildChromeValueDescriptionPrefix(formatSpec,componentPath,processName){var nameParts=[];if(componentPath.length===0){nameParts.push('total');if(formatSpec.totalUserFriendlyPropertyName){nameParts.push(formatSpec.totalUserFriendlyPropertyName);}else{if(formatSpec.userFriendlyPropertyNamePrefix)
-nameParts.push(formatSpec.userFriendlyPropertyNamePrefix);nameParts.push(formatSpec.userFriendlyPropertyName);}
-nameParts.push('reported by Chrome for');}else{if(formatSpec.componentPreposition===undefined){if(formatSpec.userFriendlyPropertyNamePrefix)
-nameParts.push(formatSpec.userFriendlyPropertyNamePrefix);nameParts.push(componentPath.join(':'));nameParts.push(formatSpec.userFriendlyPropertyName);}else{if(formatSpec.userFriendlyPropertyNamePrefix)
-nameParts.push(formatSpec.userFriendlyPropertyNamePrefix);nameParts.push(formatSpec.userFriendlyPropertyName);nameParts.push(formatSpec.componentPreposition);if(componentPath[componentPath.length-1]==='allocated_by_malloc'){nameParts.push('objects allocated by malloc for');nameParts.push(componentPath.slice(0,componentPath.length-1).join(':'));}else{nameParts.push(componentPath.join(':'));}}
+return'total number of '+convertProcessNameToUserFriendlyName(processName,true);}};var EFFECTIVE_SIZE={name:'effective_size',unit:sizeInBytes_smallerIsBetter,buildDescriptionPrefix:function(componentPath,processName){return buildChromeValueDescriptionPrefix(componentPath,processName,{userFriendlyPropertyName:'effective size',componentPreposition:'of'});}};var ALLOCATED_OBJECTS_SIZE={name:'allocated_objects_size',unit:sizeInBytes_smallerIsBetter,buildDescriptionPrefix:function(componentPath,processName){return buildChromeValueDescriptionPrefix(componentPath,processName,{userFriendlyPropertyName:'size of all objects allocated',totalUserFriendlyPropertyName:'size of all allocated objects',componentPreposition:'by'});}};var LOCKED_SIZE={name:'locked_size',unit:sizeInBytes_smallerIsBetter,buildDescriptionPrefix:function(componentPath,processName){return buildChromeValueDescriptionPrefix(componentPath,processName,{userFriendlyPropertyName:'locked (pinned) size',componentPreposition:'of'});}};var PEAK_SIZE={name:'peak_size',unit:sizeInBytes_smallerIsBetter,buildDescriptionPrefix:function(componentPath,processName){return buildChromeValueDescriptionPrefix(componentPath,processName,{userFriendlyPropertyName:'peak size',componentPreposition:'of'});}};var CODE_AND_METADATA_SIZE={name:'code_and_metadata_size',unit:sizeInBytes_smallerIsBetter,buildDescriptionPrefix:function(componentPath,processName){return buildChromeValueDescriptionPrefix(componentPath,processName,{userFriendlyPropertyNamePrefix:'size of',userFriendlyPropertyName:'code and metadata'});}};var CHROME_VALUE_PROPERTIES=[EFFECTIVE_SIZE,ALLOCATED_OBJECTS_SIZE,LOCKED_SIZE,PEAK_SIZE];function buildChromeValueDescriptionPrefix(componentPath,processName,formatSpec){var nameParts=[];if(componentPath.length===0){nameParts.push('total');if(formatSpec.totalUserFriendlyPropertyName){nameParts.push(formatSpec.totalUserFriendlyPropertyName);}else{if(formatSpec.userFriendlyPropertyNamePrefix){nameParts.push(formatSpec.userFriendlyPropertyNamePrefix);}
+nameParts.push(formatSpec.userFriendlyPropertyName);}
+nameParts.push('reported by Chrome for');}else{if(formatSpec.componentPreposition===undefined){if(formatSpec.userFriendlyPropertyNamePrefix){nameParts.push(formatSpec.userFriendlyPropertyNamePrefix);}
+nameParts.push(componentPath.join(':'));nameParts.push(formatSpec.userFriendlyPropertyName);}else{if(formatSpec.userFriendlyPropertyNamePrefix){nameParts.push(formatSpec.userFriendlyPropertyNamePrefix);}
+nameParts.push(formatSpec.userFriendlyPropertyName);nameParts.push(formatSpec.componentPreposition);if(componentPath[componentPath.length-1]==='allocated_by_malloc'){nameParts.push('objects allocated by malloc for');nameParts.push(componentPath.slice(0,componentPath.length-1).join(':'));}else{nameParts.push(componentPath.join(':'));}}
 nameParts.push('in');}
 nameParts.push(convertProcessNameToUserFriendlyName(processName));return nameParts.join(' ');}
-var CHROME_VALUE_PROPERTIES={'effective_size':buildChromeValueDescriptionPrefix.bind(undefined,{userFriendlyPropertyName:'effective size',componentPreposition:'of'}),'allocated_objects_size':buildChromeValueDescriptionPrefix.bind(undefined,{userFriendlyPropertyName:'size of all objects allocated',totalUserFriendlyPropertyName:'size of all allocated objects',componentPreposition:'by'}),'locked_size':buildChromeValueDescriptionPrefix.bind(undefined,{userFriendlyPropertyName:'locked (pinned) size',componentPreposition:'of'}),'peak_size':buildChromeValueDescriptionPrefix.bind(undefined,{userFriendlyPropertyName:'peak size',componentPreposition:'of'}),};var SYSTEM_TOTAL_VALUE_PROPERTIES={'resident_size':{getPropertyFunction:function(processDump){return processDump.totals.residentBytes;},descriptionPrefixBuilder:buildOsValueDescriptionPrefix.bind(undefined,'resident set size (RSS)')},'peak_resident_size':{getPropertyFunction:function(processDump){return processDump.totals.peakResidentBytes;},descriptionPrefixBuilder:buildOsValueDescriptionPrefix.bind(undefined,'peak resident set size')}};function addDetailedMemoryDumpValues(browserNameToGlobalDumps,values){addMemoryDumpValues(browserNameToGlobalDumps,g=>g.levelOfDetail===DETAILED,function(processDump,addProcessScalar){tr.b.iterItems(SYSTEM_VALUE_COMPONENTS,function(componentName,componentSpec){tr.b.iterItems(SYSTEM_VALUE_PROPERTIES,function(propertyName,propertySpec){var node=getDescendantVmRegionClassificationNode(processDump.vmRegions,componentSpec.classificationPath);var componentPath=['system_memory'];if(componentName)
-componentPath.push(componentName);addProcessScalar({source:'reported_by_os',component:componentPath,property:propertyName,value:node===undefined?0:(node.byteStats[propertySpec.byteStat]||0),unit:sizeInBytes_smallerIsBetter,descriptionPrefixBuilder:propertySpec.descriptionPrefixBuilder});});});var memtrackDump=processDump.getMemoryAllocatorDumpByFullName('gpu/android_memtrack');if(memtrackDump!==undefined){var descriptionPrefixBuilder=SYSTEM_VALUE_PROPERTIES['proportional_resident_size'].descriptionPrefixBuilder;memtrackDump.children.forEach(function(memtrackChildDump){var childName=memtrackChildDump.name;addProcessScalar({source:'reported_by_os',component:['gpu_memory',childName],property:'proportional_resident_size',value:memtrackChildDump.numerics['memtrack_pss'],descriptionPrefixBuilder:descriptionPrefixBuilder});});}},function(componentTree){},values);}
-var SYSTEM_VALUE_COMPONENTS={'':{classificationPath:[],},'java_heap':{classificationPath:['Android','Java runtime','Spaces'],userFriendlyName:'the Java heap'},'ashmem':{classificationPath:['Android','Ashmem'],userFriendlyName:'ashmem'},'native_heap':{classificationPath:['Native heap'],userFriendlyName:'the native heap'}};var SYSTEM_VALUE_PROPERTIES={'proportional_resident_size':{byteStat:'proportionalResident',descriptionPrefixBuilder:buildOsValueDescriptionPrefix.bind(undefined,'proportional resident size (PSS)')},'private_dirty_size':{byteStat:'privateDirtyResident',descriptionPrefixBuilder:buildOsValueDescriptionPrefix.bind(undefined,'private dirty size')}};function buildOsValueDescriptionPrefix(userFriendlyPropertyName,componentPath,processName){if(componentPath.length>2){throw new Error('OS value component path for \''+
+var RESIDENT_SIZE={name:'resident_size',unit:sizeInBytes_smallerIsBetter,buildDescriptionPrefix:function(componentPath,processName){return buildOsValueDescriptionPrefix(componentPath,processName,'resident set size (RSS)');}};var PEAK_RESIDENT_SIZE={name:'peak_resident_size',unit:sizeInBytes_smallerIsBetter,buildDescriptionPrefix:function(componentPath,processName){return buildOsValueDescriptionPrefix(componentPath,processName,'peak resident set size');}};var PROPORTIONAL_RESIDENT_SIZE={name:'proportional_resident_size',unit:sizeInBytes_smallerIsBetter,buildDescriptionPrefix:function(componentPath,processName){return buildOsValueDescriptionPrefix(componentPath,processName,'proportional resident size (PSS)');}};var PRIVATE_DIRTY_SIZE={name:'private_dirty_size',unit:sizeInBytes_smallerIsBetter,buildDescriptionPrefix:function(componentPath,processName){return buildOsValueDescriptionPrefix(componentPath,processName,'private dirty size');}};function buildOsValueDescriptionPrefix(componentPath,processName,userFriendlyPropertyName){if(componentPath.length>2){throw new Error('OS value component path for \''+
 userFriendlyPropertyName+'\' too long: '+componentPath.join(':'));}
-var nameParts=[];if(componentPath.length<2)
-nameParts.push('total');nameParts.push(userFriendlyPropertyName);if(componentPath.length>0){switch(componentPath[0]){case'system_memory':if(componentPath.length>1){var userFriendlyComponentName=SYSTEM_VALUE_COMPONENTS[componentPath[1]].userFriendlyName;if(userFriendlyComponentName===undefined){throw new Error('System value sub-component for \''+
+var nameParts=[];if(componentPath.length<2){nameParts.push('total');}
+nameParts.push(userFriendlyPropertyName);if(componentPath.length>0){switch(componentPath[0]){case'system_memory':if(componentPath.length>1){var userFriendlyComponentName=SYSTEM_VALUE_COMPONENTS[componentPath[1]].userFriendlyName;if(userFriendlyComponentName===undefined){throw new Error('System value sub-component for \''+
 userFriendlyPropertyName+'\' unknown: '+
 componentPath.join(':'));}
 nameParts.push('of',userFriendlyComponentName,'in');}else{nameParts.push('of system memory (RAM) used by');}
@@ -8003,1379 +7993,1175 @@
 userFriendlyPropertyName+'\' unknown: '+
 componentPath.join(':'));}}else{nameParts.push('reported by the OS for');}
 nameParts.push(convertProcessNameToUserFriendlyName(processName));return nameParts.join(' ');}
-function buildCodeAndMetadataSizeValueDescriptionPrefix(componentPath,processName){return buildChromeValueDescriptionPrefix({userFriendlyPropertyNamePrefix:'size of',userFriendlyPropertyName:'code and metadata'},componentPath,processName);}
-function getDescendantVmRegionClassificationNode(node,path){for(var i=0;i<path.length;i++){if(node===undefined)
-break;node=tr.b.findFirstInArray(node.children,c=>c.title===path[i]);}
+function addDetailedMemoryDumpValues(browserNameToGlobalDumps,values){addMemoryDumpValues(browserNameToGlobalDumps,g=>g.levelOfDetail===DETAILED,function(processDump,addProcessScalar){for(var[componentName,componentSpec]of
+Object.entries(SYSTEM_VALUE_COMPONENTS)){var node=getDescendantVmRegionClassificationNode(processDump.vmRegions,componentSpec.classificationPath);var componentPath=['system_memory'];if(componentName)componentPath.push(componentName);addProcessScalar({source:'reported_by_os',component:componentPath,property:PROPORTIONAL_RESIDENT_SIZE,value:node===undefined?0:(node.byteStats['proportionalResident']||0)});addProcessScalar({source:'reported_by_os',component:componentPath,property:PRIVATE_DIRTY_SIZE,value:node===undefined?0:(node.byteStats['privateDirtyResident']||0)});}
+var memtrackDump=processDump.getMemoryAllocatorDumpByFullName('gpu/android_memtrack');if(memtrackDump!==undefined){memtrackDump.children.forEach(function(memtrackChildDump){addProcessScalar({source:'reported_by_os',component:['gpu_memory',memtrackChildDump.name],property:PROPORTIONAL_RESIDENT_SIZE,value:memtrackChildDump.numerics['memtrack_pss']});});}},function(componentTree){},values);}
+var SYSTEM_VALUE_COMPONENTS={'':{classificationPath:[],},'java_heap':{classificationPath:['Android','Java runtime','Spaces'],userFriendlyName:'the Java heap'},'ashmem':{classificationPath:['Android','Ashmem'],userFriendlyName:'ashmem'},'native_heap':{classificationPath:['Native heap'],userFriendlyName:'the native heap'},'stack':{classificationPath:['Stack'],userFriendlyName:'the thread stacks'}};function getDescendantVmRegionClassificationNode(node,path){for(var i=0;i<path.length;i++){if(node===undefined)break;node=tr.b.findFirstInArray(node.children,c=>c.title===path[i]);}
 return node;}
-function addMemoryDumpCountValues(browserNameToGlobalDumps,values){browserNameToGlobalDumps.forEach(function(globalDumps,browserName){var totalDumpCount=0;var levelOfDetailNameToDumpCount={};LEVEL_OF_DETAIL_NAMES.forEach(function(levelOfDetailName){levelOfDetailNameToDumpCount[levelOfDetailName]=0;});globalDumps.forEach(function(globalDump){totalDumpCount++;var levelOfDetailName=LEVEL_OF_DETAIL_NAMES.get(globalDump.levelOfDetail);if(!(levelOfDetailName in levelOfDetailNameToDumpCount))
-return;levelOfDetailNameToDumpCount[levelOfDetailName]++;});reportMemoryDumpCountAsValue(browserName,undefined,totalDumpCount,values);tr.b.iterItems(levelOfDetailNameToDumpCount,function(levelOfDetailName,levelOfDetailDumpCount){reportMemoryDumpCountAsValue(browserName,levelOfDetailName,levelOfDetailDumpCount,values);});});}
-function reportMemoryDumpCountAsValue(browserName,levelOfDetailName,levelOfDetailDumpCount,values){var nameParts=['memory',browserName,'all_processes','dump_count'];if(levelOfDetailName!==undefined)
-nameParts.push(levelOfDetailName);var name=nameParts.join(':');var histogram=new tr.v.Histogram(name,unitlessNumber_smallerIsBetter,BOUNDARIES_FOR_UNIT_MAP.get(unitlessNumber_smallerIsBetter));histogram.addSample(levelOfDetailDumpCount);histogram.description=['total number of',levelOfDetailName||'all','memory dumps added by',convertBrowserNameToUserFriendlyName(browserName),'to the trace'].join(' ');values.addHistogram(histogram);}
-function addMemoryDumpValues(browserNameToGlobalDumps,customGlobalDumpFilter,customProcessDumpValueExtractor,customComponentTreeModifier,values){browserNameToGlobalDumps.forEach(function(globalDumps,browserName){var filteredGlobalDumps=globalDumps.filter(customGlobalDumpFilter);var sourceToPropertyToData=extractDataFromGlobalDumps(filteredGlobalDumps,customProcessDumpValueExtractor);reportDataAsValues(sourceToPropertyToData,browserName,customComponentTreeModifier,values);});}
-function extractDataFromGlobalDumps(globalDumps,customProcessDumpValueExtractor){var sourceToPropertyToData=new Map();var dumpCount=globalDumps.length;globalDumps.forEach(function(globalDump,dumpIndex){tr.b.iterItems(globalDump.processMemoryDumps,function(_,processDump){extractDataFromProcessDump(processDump,sourceToPropertyToData,dumpIndex,dumpCount,customProcessDumpValueExtractor);});});return sourceToPropertyToData;}
-function extractDataFromProcessDump(processDump,sourceToPropertyToData,dumpIndex,dumpCount,customProcessDumpValueExtractor){var rawProcessName=processDump.process.name;var processNamePath=[canonicalizeProcessName(rawProcessName)];customProcessDumpValueExtractor(processDump,function addProcessScalar(spec){if(spec.value===undefined)
-return;var component=spec.component||[];function createDetailsForErrorMessage(){var propertyUserFriendlyName=spec.property===undefined?'(undefined)':spec.property;var componentUserFriendlyName=component.length===0?'(empty)':component.join(':');return['source=',spec.source,', property=',propertyUserFriendlyName,', component=',componentUserFriendlyName,' in ',processDump.process.userFriendlyName].join('');}
-var value,unit;if(spec.value instanceof tr.v.ScalarNumeric){value=spec.value.value;unit=spec.value.unit;if(spec.unit!==undefined){throw new Error('Histogram value for '+
-createDetailsForErrorMessage()+' already specifies a unit');}}else{value=spec.value;unit=spec.unit;}
-var propertyToData=sourceToPropertyToData.get(spec.source);if(propertyToData===undefined){propertyToData=new Map();sourceToPropertyToData.set(spec.source,propertyToData);}
-var data=propertyToData.get(spec.property);if(data===undefined){data={processAndComponentTreeBuilder:new tr.b.MultiDimensionalViewBuilder(2,dumpCount),unit:unit,descriptionPrefixBuilder:spec.descriptionPrefixBuilder};propertyToData.set(spec.property,data);}else if(data.unit!==unit){throw new Error('Multiple units provided for '+
-createDetailsForErrorMessage()+':'+
-data.unit.unitName+' and '+unit.unitName);}else if(data.descriptionPrefixBuilder!==spec.descriptionPrefixBuilder){throw new Error('Multiple description prefix builders provided for'+
-createDetailsForErrorMessage());}
-var values=new Array(dumpCount);values[dumpIndex]=value;data.processAndComponentTreeBuilder.addPath([processNamePath,component],values,tr.b.MultiDimensionalViewBuilder.ValueKind.TOTAL);});}
-function reportDataAsValues(sourceToPropertyToData,browserName,customComponentTreeModifier,values){sourceToPropertyToData.forEach(function(propertyToData,sourceName){propertyToData.forEach(function(data,propertyName){var tree=data.processAndComponentTreeBuilder.buildTopDownTreeView();var unit=data.unit;var descriptionPrefixBuilder=data.descriptionPrefixBuilder;customComponentTreeModifier(tree);reportComponentDataAsValues(browserName,sourceName,propertyName,'all_processes',[],tree,unit,descriptionPrefixBuilder,values);tree.children[0].forEach(function(processTree,processName){if(processTree.children[0].size>0){throw new Error('Multi-dimensional view node for source='+
-sourceName+', property='+
-(propertyName===undefined?'(undefined)':propertyName)+', process='+processName+' has children wrt the process name dimension');}
-customComponentTreeModifier(processTree);reportComponentDataAsValues(browserName,sourceName,propertyName,processName,[],processTree,unit,descriptionPrefixBuilder,values);});});});}
-function reportComponentDataAsValues(browserName,sourceName,propertyName,processName,componentPath,componentNode,unit,descriptionPrefixBuilder,values){var nameParts=['memory',browserName,processName,sourceName].concat(componentPath);if(propertyName!==undefined)
-nameParts.push(propertyName);var name=nameParts.join(':');var numeric=buildMemoryNumericFromNode(name,componentNode,unit);numeric.description=[descriptionPrefixBuilder(componentPath,processName),'in',convertBrowserNameToUserFriendlyName(browserName)].join(' ');values.addHistogram(numeric);var depth=componentPath.length;componentPath.push(undefined);componentNode.children[1].forEach(function(childNode,childName){componentPath[depth]=childName;reportComponentDataAsValues(browserName,sourceName,propertyName,processName,componentPath,childNode,unit,descriptionPrefixBuilder,values);});componentPath.pop();}
+function addMemoryDumpCountValues(browserNameToGlobalDumps,values){browserNameToGlobalDumps.forEach(function(globalDumps,browserName){var totalDumpCount=0;var levelOfDetailNameToDumpCount={};LEVEL_OF_DETAIL_NAMES.forEach(function(levelOfDetailName){levelOfDetailNameToDumpCount[levelOfDetailName]=0;});levelOfDetailNameToDumpCount[HEAP_PROFILER_DETAIL_NAME]=0;globalDumps.forEach(function(globalDump){totalDumpCount++;var levelOfDetailName=LEVEL_OF_DETAIL_NAMES.get(globalDump.levelOfDetail);if(levelOfDetailName===undefined){return;}
+levelOfDetailNameToDumpCount[levelOfDetailName]++;if(globalDump.levelOfDetail===DETAILED){if(detectHeapProfilerInMemoryDump(globalDump)){levelOfDetailNameToDumpCount[HEAP_PROFILER_DETAIL_NAME]++;}}});reportMemoryDumpCountAsValue(browserName,undefined,totalDumpCount,values);for(var[levelOfDetailName,levelOfDetailDumpCount]of
+Object.entries(levelOfDetailNameToDumpCount)){reportMemoryDumpCountAsValue(browserName,levelOfDetailName,levelOfDetailDumpCount,values);}});}
+function detectHeapProfilerInMemoryDump(globalDump){for(let processDump of Object.values(globalDump.processMemoryDumps)){if(processDump.heapDumps&&processDump.heapDumps.malloc){var mallocDump=processDump.heapDumps.malloc;if(mallocDump.entries&&mallocDump.entries.length>0){return true;}}}
+return false;}
+function reportMemoryDumpCountAsValue(browserName,levelOfDetailName,levelOfDetailDumpCount,values){var nameParts=['memory',browserName,'all_processes','dump_count'];if(levelOfDetailName!==undefined){nameParts.push(levelOfDetailName);}
+var name=nameParts.join(':');var histogram=new tr.v.Histogram(name,count_smallerIsBetter,BOUNDARIES_FOR_UNIT_MAP.get(count_smallerIsBetter));histogram.addSample(levelOfDetailDumpCount);var userFriendlyLevelOfDetail=(levelOfDetailName||'all').replace('_',' ');histogram.description=['total number of',userFriendlyLevelOfDetail,'memory dumps added by',convertBrowserNameToUserFriendlyName(browserName),'to the trace'].join(' ');values.addHistogram(histogram);}
+function addMemoryDumpValues(browserNameToGlobalDumps,customGlobalDumpFilter,customProcessDumpValueExtractor,customComponentTreeModifier,values){browserNameToGlobalDumps.forEach(function(globalDumps,browserName){var filteredGlobalDumps=globalDumps.filter(customGlobalDumpFilter);var sourceToPropertyToBuilder=extractDataFromGlobalDumps(filteredGlobalDumps,customProcessDumpValueExtractor);reportDataAsValues(sourceToPropertyToBuilder,browserName,customComponentTreeModifier,values);});}
+function extractDataFromGlobalDumps(globalDumps,customProcessDumpValueExtractor){var sourceToPropertyToBuilder=new Map();var dumpCount=globalDumps.length;globalDumps.forEach(function(globalDump,dumpIndex){for(var processDump of Object.values(globalDump.processMemoryDumps)){extractDataFromProcessDump(processDump,sourceToPropertyToBuilder,dumpIndex,dumpCount,customProcessDumpValueExtractor);}});return sourceToPropertyToBuilder;}
+function extractDataFromProcessDump(processDump,sourceToPropertyToBuilder,dumpIndex,dumpCount,customProcessDumpValueExtractor){var rawProcessName=processDump.process.name;var processNamePath=[canonicalizeProcessName(rawProcessName)];customProcessDumpValueExtractor(processDump,function addProcessScalar(spec){if(spec.value===undefined)return;var component=spec.component||[];function createDetailsForErrorMessage(){return['source=',spec.source,', property=',spec.property.name||'(undefined)',', component=',component.length===0?'(empty)':component.join(':'),' in ',processDump.process.userFriendlyName].join('');}
+var value;if(spec.value instanceof tr.b.Scalar){value=spec.value.value;if(spec.value.unit!==spec.property.unit){throw new Error('Scalar unit for '+
+createDetailsForErrorMessage()+' ('+
+spec.value.unit.unitName+') doesn\'t match the unit of the property ('+
+spec.property.unit.unitName+')');}}else{value=spec.value;}
+var propertyToBuilder=sourceToPropertyToBuilder.get(spec.source);if(propertyToBuilder===undefined){propertyToBuilder=new Map();sourceToPropertyToBuilder.set(spec.source,propertyToBuilder);}
+var builder=propertyToBuilder.get(spec.property);if(builder===undefined){builder=new tr.b.MultiDimensionalViewBuilder(2,dumpCount),propertyToBuilder.set(spec.property,builder);}
+var values=new Array(dumpCount);values[dumpIndex]=value;builder.addPath([processNamePath,component],values,tr.b.MultiDimensionalViewBuilder.ValueKind.TOTAL);});}
+function reportDataAsValues(sourceToPropertyToBuilder,browserName,customComponentTreeModifier,values){sourceToPropertyToBuilder.forEach(function(propertyToBuilder,sourceName){propertyToBuilder.forEach(function(builders,property){var tree=builders.buildTopDownTreeView();reportComponentDataAsValues(browserName,sourceName,property,[],tree,values,customComponentTreeModifier);});});}
+function reportComponentDataAsValues(browserName,sourceName,property,componentPath,tree,values,customComponentTreeModifier){var breakdown=new tr.v.d.RelatedHistogramBreakdown();breakdown.colorScheme=PROCESS_COLOR_SCHEME_NAME;tree.children[0].forEach(function(processTree,processName){customComponentTreeModifier(processTree);var numeric=buildNamedMemoryNumericFromNode(browserName,sourceName,property,processName,componentPath,processTree);values.addHistogram(numeric);breakdown.set(processName,numeric);});customComponentTreeModifier(tree);var numeric=buildNamedMemoryNumericFromNode(browserName,sourceName,property,'all_processes',componentPath,tree);numeric.diagnostics.set('processes',breakdown);values.addHistogram(numeric);var depth=componentPath.length;componentPath.push(undefined);tree.children[1].forEach(function(childNode,childName){componentPath[depth]=childName;reportComponentDataAsValues(browserName,sourceName,property,componentPath,childNode,values,customComponentTreeModifier);});componentPath.pop();}
+function getNumericName(browserName,sourceName,propertyName,processName,componentPath){var nameParts=['memory',browserName,processName,sourceName].concat(componentPath);if(propertyName!==undefined)nameParts.push(propertyName);return nameParts.join(':');}
+function getNumericDescription(property,browserName,processName,componentPath){return[property.buildDescriptionPrefix(componentPath,processName),'in',convertBrowserNameToUserFriendlyName(browserName)].join(' ');}
+function buildNamedMemoryNumericFromNode(browserName,sourceName,property,processName,componentPath,node){var name=getNumericName(browserName,sourceName,property.name,processName,componentPath);var description=getNumericDescription(property,browserName,processName,componentPath);var numeric=buildMemoryNumericFromNode(name,node,property.unit);numeric.description=description;return numeric;}
 function buildMemoryNumericFromNode(name,node,unit){var histogram=new tr.v.Histogram(name,unit,BOUNDARIES_FOR_UNIT_MAP.get(unit));node.values.forEach(v=>histogram.addSample(v.total));return histogram;}
-tr.metrics.MetricRegistry.register(memoryMetric,{supportsRangeOfInterest:true});return{memoryMetric:memoryMetric};});'use strict';tr.exportTo('tr.metrics.sh',function(){var IDEAL_FRAME_RATE_FPS=60;var IDEAL_FRAME_DURATION_MS=tr.b.convertUnit(1.0/IDEAL_FRAME_RATE_FPS,tr.b.UnitScale.Metric.NONE,tr.b.UnitScale.Metric.MILLI);function energyConsumedPerFrame(valueList,model){var frameEnergyConsumedInJ=new tr.v.Histogram('energy_consumed_per_frame',tr.b.Unit.byName.energyInJoules_smallerIsBetter,tr.v.HistogramBinBoundaries.createLinear(0,0.5,20));frameEnergyConsumedInJ.description='Energy consumption per frame in joules';var frameStartTs=parseFloat(model.device.powerSeries.samples[0].start);while(model.device.powerSeries.getSamplesWithinRange(frameStartTs,frameStartTs+IDEAL_FRAME_DURATION_MS).length){var currentFrameEnergy=model.device.powerSeries.getEnergyConsumedInJ(frameStartTs,frameStartTs+IDEAL_FRAME_DURATION_MS);frameStartTs+=IDEAL_FRAME_DURATION_MS;frameEnergyConsumedInJ.addSample(currentFrameEnergy);}
-valueList.addHistogram(frameEnergyConsumedInJ);}
-function powerMetric(valueList,model){if(!model.device.powerSeries)
-return;energyConsumedPerFrame(valueList,model);}
-tr.metrics.MetricRegistry.register(powerMetric);return{powerMetric:powerMetric};});'use strict';tr.exportTo('tr.metrics.sh',function(){function railPowerMetric(values,model){if(!model.device.powerSeries)
-return;var railStageToTimeHist=new Map();var railStageToEnergyHist=new Map();var railStageToPowerHist=new Map();for(var exp of model.userModel.expectations){var currTitle=exp.title.toLowerCase().replace(' ','_');if(!railStageToTimeHist.has(currTitle)){var timeHist=new tr.v.Histogram('time:'+currTitle,tr.b.Unit.byName.timeDurationInMs_smallerIsBetter);timeHist.description='Time spent in RAIL stage '+currTitle;var energyHist=new tr.v.Histogram('energy:'+currTitle,tr.b.Unit.byName.energyInJoules_smallerIsBetter);energyHist.description='Energy consumed by RAIL stage '+currTitle;var powerHist=new tr.v.Histogram('power:'+currTitle,tr.b.Unit.byName.powerInWatts_smallerIsBetter);powerHist.description='Energy consumption rate by RAIL stage '+currTitle;timeHist.customizeSummaryOptions({avg:false,count:false,max:true,min:true,std:false,sum:true,});energyHist.customizeSummaryOptions({avg:false,count:false,max:true,min:true,std:false,sum:true,});powerHist.customizeSummaryOptions({avg:true,count:false,max:true,min:true,std:false,sum:false,});railStageToTimeHist.set(currTitle,timeHist);railStageToEnergyHist.set(currTitle,energyHist);railStageToPowerHist.set(currTitle,powerHist);values.addHistogram(timeHist);values.addHistogram(energyHist);values.addHistogram(powerHist);}
-var durationInMs=exp.duration;var durationInS=tr.b.convertUnit(durationInMs,tr.b.UnitScale.Metric.MILLI,tr.b.UnitScale.Metric.NONE);var energyInJ=model.device.powerSeries.getEnergyConsumedInJ(exp.start,exp.end);var powerInW=energyInJ/durationInS;railStageToTimeHist.get(currTitle).addSample(durationInMs);railStageToEnergyHist.get(currTitle).addSample(energyInJ);railStageToPowerHist.get(currTitle).addSample(powerInW);}}
-tr.metrics.MetricRegistry.register(railPowerMetric);return{railPowerMetric:railPowerMetric};});'use strict';tr.exportTo('tr.metrics.sh',function(){function computeAnimationThroughput(animationExpectation){if(animationExpectation.frameEvents===undefined||animationExpectation.frameEvents.length===0)
-throw new Error('Animation missing frameEvents '+
-animationExpectation.stableId);var durationInS=tr.b.convertUnit(animationExpectation.duration,tr.b.UnitScale.Metric.MILLI,tr.b.UnitScale.Metric.NONE);return animationExpectation.frameEvents.length/durationInS;}
-function computeAnimationframeTimeDiscrepancy(animationExpectation){if(animationExpectation.frameEvents===undefined||animationExpectation.frameEvents.length===0)
-throw new Error('Animation missing frameEvents '+
-animationExpectation.stableId);var frameTimestamps=animationExpectation.frameEvents;frameTimestamps=frameTimestamps.toArray().map(function(event){return event.start;});var absolute=true;return tr.b.Statistics.timestampsDiscrepancy(frameTimestamps,absolute);}
-function responsivenessMetric(values,model,opt_options){var responseNumeric=new tr.v.Histogram('response latency',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,tr.v.HistogramBinBoundaries.createLinear(100,1e3,50));var throughputNumeric=new tr.v.Histogram('animation throughput',tr.b.Unit.byName.unitlessNumber_biggerIsBetter,tr.v.HistogramBinBoundaries.createLinear(10,60,10));var frameTimeDiscrepancyNumeric=new tr.v.Histogram('animation frameTimeDiscrepancy',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,tr.v.HistogramBinBoundaries.createLinear(0,1e3,50).addExponentialBins(1e4,10));var latencyNumeric=new tr.v.Histogram('animation latency',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,tr.v.HistogramBinBoundaries.createLinear(0,300,60));model.userModel.expectations.forEach(function(ue){if(opt_options&&opt_options.rangeOfInterest&&!opt_options.rangeOfInterest.intersectsExplicitRangeInclusive(ue.start,ue.end))
-return;var sampleDiagnosticMap=tr.v.d.DiagnosticMap.fromObject({relatedEvents:new tr.v.d.RelatedEventSet([ue])});if(ue instanceof tr.model.um.IdleExpectation){return;}else if(ue instanceof tr.model.um.StartupExpectation){return;}else if(ue instanceof tr.model.um.LoadExpectation){}else if(ue instanceof tr.model.um.ResponseExpectation){responseNumeric.addSample(ue.duration,sampleDiagnosticMap);}else if(ue instanceof tr.model.um.AnimationExpectation){if(ue.frameEvents===undefined||ue.frameEvents.length===0){return;}
-var throughput=computeAnimationThroughput(ue);if(throughput===undefined)
-throw new Error('Missing throughput for '+
-ue.stableId);throughputNumeric.addSample(throughput,sampleDiagnosticMap);var frameTimeDiscrepancy=computeAnimationframeTimeDiscrepancy(ue);if(frameTimeDiscrepancy===undefined)
-throw new Error('Missing frameTimeDiscrepancy for '+
-ue.stableId);frameTimeDiscrepancyNumeric.addSample(frameTimeDiscrepancy,sampleDiagnosticMap);ue.associatedEvents.forEach(function(event){if(!(event instanceof tr.e.cc.InputLatencyAsyncSlice))
-return;latencyNumeric.addSample(event.duration,sampleDiagnosticMap);});}else{throw new Error('Unrecognized stage for '+ue.stableId);}});[responseNumeric,throughputNumeric,frameTimeDiscrepancyNumeric,latencyNumeric].forEach(function(numeric){numeric.customizeSummaryOptions({avg:true,max:true,min:true,std:true});});values.addHistogram(responseNumeric);values.addHistogram(throughputNumeric);values.addHistogram(frameTimeDiscrepancyNumeric);values.addHistogram(latencyNumeric);}
-tr.metrics.MetricRegistry.register(responsivenessMetric,{supportsRangeOfInterest:true});return{responsivenessMetric:responsivenessMetric,};});'use strict';tr.exportTo('tr.metrics.sh',function(){function scrollPowerMetric(values,model){if(!model.device.powerSeries)
-return;var timeHist=new tr.v.Histogram('time:scroll',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter)
-timeHist.description='Time spent in scrolling';var energyHist=new tr.v.Histogram('energy:scroll',tr.b.Unit.byName.energyInJoules_smallerIsBetter)
-energyHist.description='Energy consumed by scrolling';var powerHist=new tr.v.Histogram('power:scroll',tr.b.Unit.byName.powerInWatts_smallerIsBetter)
-powerHist.description='Energy consumption rate in scrolling';timeHist.customizeSummaryOptions({avg:false,count:false,max:true,min:true,std:false,sum:true,});energyHist.customizeSummaryOptions({avg:false,count:false,max:true,min:true,std:false,sum:true,});powerHist.customizeSummaryOptions({avg:true,count:false,max:true,min:true,std:false,sum:false,});for(var exp of model.userModel.expectations){if(exp.title.indexOf("Scroll")!==-1){var durationInMs=exp.duration;var durationInS=tr.b.convertUnit(durationInMs,tr.b.UnitScale.Metric.MILLI,tr.b.UnitScale.Metric.NONE);var energyInJ=model.device.powerSeries.getEnergyConsumedInJ(exp.start,exp.end);var powerInW=energyInJ/durationInS;timeHist.addSample(durationInMs);energyHist.addSample(energyInJ);powerHist.addSample(powerInW);}}
-values.addHistogram(timeHist);values.addHistogram(energyHist);values.addHistogram(powerHist);}
-tr.metrics.MetricRegistry.register(scrollPowerMetric);return{scrollPowerMetric:scrollPowerMetric};});'use strict';tr.exportTo('tr.metrics.sh',function(){function cpuTimeMetric(values,model,optOptions){var rangeOfInterest=optOptions?optOptions.rangeOfInterest:undefined;var allProcessCpuTime=0;for(var pid in model.processes){var process=model.processes[pid];var processCpuTime=0;for(var tid in process.threads){var thread=process.threads[tid];var threadCpuTime=0;thread.sliceGroup.topLevelSlices.forEach(function(slice){if(rangeOfInterest&&!rangeOfInterest.intersectsExplicitRangeInclusive(slice.start,slice.end))
-return;threadCpuTime+=slice.cpuDuration;});processCpuTime+=threadCpuTime;}
-allProcessCpuTime+=processCpuTime;}
-var normalizationRange=rangeOfInterest?rangeOfInterest:model.bounds.range;var MILLISECONDS_PER_SECOND=1000;var clockTimeInSeconds=normalizationRange/MILLISECONDS_PER_SECOND;clockTimeInSeconds=Math.max(clockTimeInSeconds,0.0001);var normalizedAllProcessCpuTime=allProcessCpuTime/clockTimeInSeconds;var unit=tr.b.Unit.byName.timeDurationInMs_smallerIsBetter;var boundaries=tr.v.HistogramBinBoundaries.createExponential(1,100000,200)
-var cpuTimeNumeric=new tr.v.Histogram('cpu_time',unit,boundaries);cpuTimeNumeric.description='Total CPU time on all Chrome processes, per second of clock time.';cpuTimeNumeric.addSample(normalizedAllProcessCpuTime);values.addHistogram(cpuTimeNumeric);}
-tr.metrics.MetricRegistry.register(cpuTimeMetric,{supportsRangeOfInterest:true});return{cpuTimeMetric:cpuTimeMetric,};});'use strict';tr.exportTo('tr.metrics.sh',function(){function systemHealthMetrics(values,model){tr.metrics.sh.responsivenessMetric(values,model);tr.metrics.sh.longTasksMetric(values,model);tr.metrics.sh.hazardMetric(values,model);tr.metrics.sh.powerMetric(values,model);tr.metrics.sh.cpuTimeMetric(values,model);}
-tr.metrics.MetricRegistry.register(systemHealthMetrics);return{systemHealthMetrics:systemHealthMetrics};});'use strict';tr.exportTo('tr.metrics.sh',function(){function getNavigationTTIIntervals(model){var values=new tr.v.ValueSet();tr.metrics.sh.loadingMetric(values,model);var ttiValues=values.getValuesNamed('timeToFirstInteractive');var intervals=[];for(var bin of tr.b.getOnlyElement(ttiValues).allBins)
-for(var diagnostics of bin.diagnosticMaps){var info=diagnostics.get('Navigation infos');intervals.push(tr.b.Range.fromExplicitRange(info.value.start,info.value.interactive));}
+tr.metrics.MetricRegistry.register(memoryMetric,{supportsRangeOfInterest:true});return{memoryMetric,};});'use strict';tr.exportTo('tr.metrics.sh',function(){var CHROME_POWER_GRACE_PERIOD_MS=1;function createEmptyHistogram_(interval,histograms){if(interval.perSecond){return{perSecond:true,energy:createPowerHistogram_(histograms,interval.name,interval.description)};}
+return{perSecond:false,energy:createEnergyHistogram_(histograms,interval.name,interval.description)};}
+function createHistograms_(data,interval,histograms){if(data.histograms[interval.name]===undefined){data.histograms[interval.name]=createEmptyHistogram_(interval,histograms);}
+if(data.histograms[interval.name].perSecond){for(var sample of data.model.device.powerSeries.getSamplesWithinRange(interval.bounds.min,interval.bounds.max)){data.histograms[interval.name].energy.addSample(sample.powerInW);}}else{var energyInJ=data.model.device.powerSeries.getEnergyConsumedInJ(interval.bounds.min,interval.bounds.max);data.histograms[interval.name].energy.addSample(energyInJ);}}
+function getNavigationTTIIntervals_(model){var chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);var intervals=[];for(var rendererHelper of Object.values(chromeHelper.rendererHelpers)){var samples=tr.metrics.sh.collectLoadingMetricsForRenderer(rendererHelper).firstInteractiveSamples;for(var sample of samples){var info=sample.diagnostics['Navigation infos'].value;intervals.push(tr.b.math.Range.fromExplicitRange(info.start,info.interactive));}}
 return intervals.sort((x,y)=>x.min-y.min);}
-function ttiPowerMetric(values,model){if(!model.device.powerSeries)
-return;var intervals=getNavigationTTIIntervals(model);var lastLoadTime=0;var loadHistogram=new tr.v.Histogram('energy:load',tr.b.Unit.byName.energyInJoules_smallerIsBetter);loadHistogram.description='Energy consumed in page loads';loadHistogram.customizeSummaryOptions({avg:false,count:false,max:false,min:false,std:false,sum:true,});for(var interval of intervals){var energyInJ=model.device.powerSeries.getEnergyConsumedInJ(interval.min,interval.max);loadHistogram.addSample(energyInJ);lastLoadTime=interval.max;}
-values.addHistogram(loadHistogram);var afterLoadEnergyInJ=model.device.powerSeries.getEnergyConsumedInJ(lastLoadTime,model.bounds.max);var afterLoadTimeInMs=model.bounds.max-lastLoadTime;var afterLoadTimeInS=tr.b.convertUnit(afterLoadTimeInMs,tr.b.UnitScale.Metric.MILLI,tr.b.UnitScale.Metric.NONE);var afterLoadPowerInW=afterLoadEnergyInJ/afterLoadTimeInS;var afterLoadHistogram=new tr.v.Histogram('power:after_load',tr.b.Unit.byName.powerInWatts_smallerIsBetter);afterLoadHistogram.description='Average power after load';afterLoadHistogram.customizeSummaryOptions({avg:false,count:false,max:false,min:false,std:false,sum:false,});afterLoadHistogram.addSample(afterLoadPowerInW);values.addHistogram(afterLoadHistogram);}
-tr.metrics.MetricRegistry.register(ttiPowerMetric);return{ttiPowerMetric:ttiPowerMetric};});'use strict';tr.exportTo('tr.metrics.sh',function(){function webviewStartupMetric(values,model){var startupWallHist=new tr.v.Histogram('webview_startup_wall_time',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter);startupWallHist.description='WebView startup wall time';var startupCPUHist=new tr.v.Histogram('webview_startup_cpu_time',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter);startupCPUHist.description='WebView startup CPU time';var loadWallHist=new tr.v.Histogram('webview_url_load_wall_time',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter);loadWallHist.description='WebView blank URL load wall time';var loadCPUHist=new tr.v.Histogram('webview_url_load_cpu_time',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter);loadCPUHist.description='WebView blank URL load CPU time';for(var slice of model.getDescendantEvents()){if(!(slice instanceof tr.model.ThreadSlice))
-continue;if(slice.title==='WebViewStartupInterval'){startupWallHist.addSample(slice.duration);startupCPUHist.addSample(slice.cpuDuration);}
+function createEnergyHistogram_(histograms,histogramName,description){var histogram=new tr.v.Histogram(`${histogramName}:energy`,tr.b.Unit.byName.energyInJoules_smallerIsBetter);histogram.customizeSummaryOptions({avg:false,count:false,max:true,min:true,std:false,sum:true,});histogram.description='Energy consumed in '+description;histograms.addHistogram(histogram);return histogram;}
+function createPowerHistogram_(histograms,histogramName,description){var histogram=new tr.v.Histogram(`${histogramName}:power`,tr.b.Unit.byName.powerInWatts_smallerIsBetter);histogram.customizeSummaryOptions({avg:true,count:false,max:true,min:true,std:false,sum:false,});histogram.description='Energy consumption rate for '+description;histograms.addHistogram(histogram);return histogram;}
+function*computeTimeIntervals_(model){var chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);var powerSeries=model.device.powerSeries;if(powerSeries===undefined||powerSeries.samples.length===0){return;}
+yield{bounds:model.bounds,name:'story',description:'user story',perSecond:true};var chromeBounds=computeChromeBounds_(model);if(chromeBounds.isEmpty)return;var powerSeriesBoundsWithGracePeriod=tr.b.math.Range.fromExplicitRange(powerSeries.bounds.min-CHROME_POWER_GRACE_PERIOD_MS,powerSeries.bounds.max+CHROME_POWER_GRACE_PERIOD_MS);if(!powerSeriesBoundsWithGracePeriod.containsRangeExclusive(chromeBounds)){return;}
+for(var interval of getRailStageIntervals_(model)){yield{bounds:interval.bounds.findIntersection(chromeBounds),name:interval.name,description:interval.description,perSecond:interval.perSecond};}
+for(var interval of getLoadingIntervals_(model,chromeBounds)){yield{bounds:interval.bounds.findIntersection(chromeBounds),name:interval.name,description:interval.description,perSecond:interval.perSecond};}}
+function*getRailStageIntervals_(model){for(var exp of model.userModel.expectations){var histogramName=exp.title.toLowerCase().replace(' ','_');var energyHist=undefined;if(histogramName.includes('response')){yield{bounds:tr.b.math.Range.fromExplicitRange(exp.start,exp.end),name:histogramName,description:'RAIL stage '+histogramName,perSecond:false};}else if(histogramName.includes('animation')||histogramName.includes('idle')){yield{bounds:tr.b.math.Range.fromExplicitRange(exp.start,exp.end),name:histogramName,description:'RAIL stage '+histogramName,perSecond:true};}}}
+function*getLoadingIntervals_(model,chromeBounds){var ttiIntervals=getNavigationTTIIntervals_(model);var lastLoadTime=undefined;for(var ttiInterval of ttiIntervals){yield{bounds:ttiInterval,name:'load',description:'page loads',perSecond:false};lastLoadTime=lastLoadTime===undefined?ttiInterval.max:Math.max(lastLoadTime,ttiInterval.max);}
+if(lastLoadTime!==undefined){yield{bounds:tr.b.math.Range.fromExplicitRange(lastLoadTime,chromeBounds.max),name:'after_load',description:'period after load',perSecond:true};}}
+function computeChromeBounds_(model){var chromeBounds=new tr.b.math.Range();var chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(chromeHelper===undefined)return chromeBounds;for(var helper of chromeHelper.browserHelpers){if(helper.mainThread){chromeBounds.addRange(helper.mainThread.bounds);}}
+for(var pid in chromeHelper.rendererHelpers){if(chromeHelper.rendererHelpers[pid].mainThread){chromeBounds.addRange(chromeHelper.rendererHelpers[pid].mainThread.bounds);}}
+return chromeBounds;}
+function powerMetric(histograms,model){var data={model:model,histograms:{}};for(var interval of computeTimeIntervals_(model)){createHistograms_(data,interval,histograms);}}
+tr.metrics.MetricRegistry.register(powerMetric);return{powerMetric};});'use strict';tr.exportTo('tr.metrics.sh',function(){function computeAnimationThroughput(animationExpectation){if(animationExpectation.frameEvents===undefined||animationExpectation.frameEvents.length===0){throw new Error('Animation missing frameEvents '+
+animationExpectation.stableId);}
+var durationInS=tr.b.convertUnit(animationExpectation.duration,tr.b.UnitPrefixScale.METRIC.MILLI,tr.b.UnitPrefixScale.METRIC.NONE);return animationExpectation.frameEvents.length/durationInS;}
+function computeAnimationframeTimeDiscrepancy(animationExpectation){if(animationExpectation.frameEvents===undefined||animationExpectation.frameEvents.length===0){throw new Error('Animation missing frameEvents '+
+animationExpectation.stableId);}
+var frameTimestamps=animationExpectation.frameEvents;frameTimestamps=frameTimestamps.toArray().map(function(event){return event.start;});var absolute=true;return tr.b.math.Statistics.timestampsDiscrepancy(frameTimestamps,absolute);}
+function responsivenessMetric(histograms,model,opt_options){var responseNumeric=new tr.v.Histogram('response latency',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,tr.v.HistogramBinBoundaries.createLinear(100,1e3,50));var throughputNumeric=new tr.v.Histogram('animation throughput',tr.b.Unit.byName.unitlessNumber_biggerIsBetter,tr.v.HistogramBinBoundaries.createLinear(10,60,10));var frameTimeDiscrepancyNumeric=new tr.v.Histogram('animation frameTimeDiscrepancy',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,tr.v.HistogramBinBoundaries.createLinear(0,1e3,50).addExponentialBins(1e4,10));var latencyNumeric=new tr.v.Histogram('animation latency',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,tr.v.HistogramBinBoundaries.createLinear(0,300,60));model.userModel.expectations.forEach(function(ue){if(opt_options&&opt_options.rangeOfInterest&&!opt_options.rangeOfInterest.intersectsExplicitRangeInclusive(ue.start,ue.end)){return;}
+var sampleDiagnosticMap=tr.v.d.DiagnosticMap.fromObject({relatedEvents:new tr.v.d.RelatedEventSet([ue])});if(ue instanceof tr.model.um.IdleExpectation){return;}else if(ue instanceof tr.model.um.StartupExpectation){return;}else if(ue instanceof tr.model.um.LoadExpectation){}else if(ue instanceof tr.model.um.ResponseExpectation){responseNumeric.addSample(ue.duration,sampleDiagnosticMap);}else if(ue instanceof tr.model.um.AnimationExpectation){if(ue.frameEvents===undefined||ue.frameEvents.length===0){return;}
+var throughput=computeAnimationThroughput(ue);if(throughput===undefined){throw new Error('Missing throughput for '+
+ue.stableId);}
+throughputNumeric.addSample(throughput,sampleDiagnosticMap);var frameTimeDiscrepancy=computeAnimationframeTimeDiscrepancy(ue);if(frameTimeDiscrepancy===undefined){throw new Error('Missing frameTimeDiscrepancy for '+
+ue.stableId);}
+frameTimeDiscrepancyNumeric.addSample(frameTimeDiscrepancy,sampleDiagnosticMap);ue.associatedEvents.forEach(function(event){if(!(event instanceof tr.e.cc.InputLatencyAsyncSlice)){return;}
+latencyNumeric.addSample(event.duration,sampleDiagnosticMap);});}else{throw new Error('Unrecognized stage for '+ue.stableId);}});[responseNumeric,throughputNumeric,frameTimeDiscrepancyNumeric,latencyNumeric].forEach(function(numeric){numeric.customizeSummaryOptions({avg:true,max:true,min:true,std:true});});histograms.addHistogram(responseNumeric);histograms.addHistogram(throughputNumeric);histograms.addHistogram(frameTimeDiscrepancyNumeric);histograms.addHistogram(latencyNumeric);}
+tr.metrics.MetricRegistry.register(responsivenessMetric,{supportsRangeOfInterest:true});return{responsivenessMetric,};});'use strict';tr.exportTo('tr.metrics.sh',function(){function webviewStartupMetric(histograms,model){var startupWallHist=new tr.v.Histogram('webview_startup_wall_time',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter);startupWallHist.description='WebView startup wall time';var startupCPUHist=new tr.v.Histogram('webview_startup_cpu_time',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter);startupCPUHist.description='WebView startup CPU time';var loadWallHist=new tr.v.Histogram('webview_url_load_wall_time',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter);loadWallHist.description='WebView blank URL load wall time';var loadCPUHist=new tr.v.Histogram('webview_url_load_cpu_time',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter);loadCPUHist.description='WebView blank URL load CPU time';for(var slice of model.getDescendantEvents()){if(!(slice instanceof tr.model.ThreadSlice))continue;if(slice.title==='WebViewStartupInterval'){startupWallHist.addSample(slice.duration);startupCPUHist.addSample(slice.cpuDuration);}
 if(slice.title==='WebViewBlankUrlLoadInterval'){loadWallHist.addSample(slice.duration);loadCPUHist.addSample(slice.cpuDuration);}}
-values.addHistogram(startupWallHist);values.addHistogram(startupCPUHist);values.addHistogram(loadWallHist);values.addHistogram(loadCPUHist);}
-tr.metrics.MetricRegistry.register(webviewStartupMetric);return{webviewStartupMetric:webviewStartupMetric};});'use strict';tr.exportTo('tr.metrics',function(){var MEMORY_INFRA_TRACING_CATEGORY='disabled-by-default-memory-infra';var TIME_BOUNDARIES=tr.v.HistogramBinBoundaries.createExponential(1e-3,1e5,30);var BYTE_BOUNDARIES=tr.v.HistogramBinBoundaries.createExponential(1,1e9,30);var COUNT_BOUNDARIES=tr.v.HistogramBinBoundaries.createExponential(1,1e5,30);function addTimeDurationValue(valueName,duration,allValues){var hist=new tr.v.Histogram(valueName,tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,TIME_BOUNDARIES);hist.addSample(duration);allValues.addHistogram(hist);}
-function addMemoryInfraValues(values,model,categoryNamesToTotalEventSizes){var memoryDumpCount=model.globalMemoryDumps.length;if(memoryDumpCount===0)
-return;var totalOverhead=0;var nonMemoryInfraThreadOverhead=0;var overheadByProvider={};tr.b.iterItems(model.processes,function(pid,process){tr.b.iterItems(process.threads,function(tid,thread){tr.b.iterItems(thread.sliceGroup.slices,function(slice_id,slice){if(slice.category!==MEMORY_INFRA_TRACING_CATEGORY)
-return;totalOverhead+=slice.duration;if(thread.name!=='MemoryInfra')
-nonMemoryInfraThreadOverhead+=slice.duration;if(slice.args&&slice.args['dump_provider.name']){var providerName=slice.args['dump_provider.name'];var durationAndCount=overheadByProvider[providerName];if(durationAndCount===undefined){overheadByProvider[providerName]=durationAndCount={duration:0,count:0};}
-durationAndCount.duration+=slice.duration;durationAndCount.count++;}});});});addTimeDurationValue('Average CPU overhead on all threads per memory-infra dump',totalOverhead/memoryDumpCount,values);addTimeDurationValue('Average CPU overhead on non-memory-infra threads per memory-infra '+'dump',nonMemoryInfraThreadOverhead/memoryDumpCount,values);tr.b.iterItems(overheadByProvider,function(providerName,overhead){addTimeDurationValue('Average CPU overhead of '+providerName+' per OnMemoryDump call',overhead.duration/overhead.count,values);});var memoryInfraEventsSize=categoryNamesToTotalEventSizes.get(MEMORY_INFRA_TRACING_CATEGORY);var memoryInfraTraceBytesValue=new tr.v.Histogram('Total trace size of memory-infra dumps in bytes',tr.b.Unit.byName.sizeInBytes_smallerIsBetter,BYTE_BOUNDARIES);memoryInfraTraceBytesValue.addSample(memoryInfraEventsSize);values.addHistogram(memoryInfraTraceBytesValue);var traceBytesPerDumpValue=new tr.v.Histogram('Average trace size of memory-infra dumps in bytes',tr.b.Unit.byName.sizeInBytes_smallerIsBetter,BYTE_BOUNDARIES);traceBytesPerDumpValue.addSample(memoryInfraEventsSize/memoryDumpCount);values.addHistogram(traceBytesPerDumpValue);}
-function tracingMetric(values,model){if(!model.stats.hasEventSizesinBytes){throw new Error('Model stats does not have event size information. '+'Please enable ImportOptions.trackDetailedModelStats.');}
-var eventStats=model.stats.allTraceEventStatsInTimeIntervals;eventStats.sort(function(a,b){return a.timeInterval-b.timeInterval;});var totalTraceBytes=eventStats.reduce((a,b)=>(a+b.totalEventSizeinBytes),0);var maxEventCountPerSec=0;var maxEventBytesPerSec=0;var INTERVALS_PER_SEC=Math.floor(1000/model.stats.TIME_INTERVAL_SIZE_IN_MS);var runningEventNumPerSec=0;var runningEventBytesPerSec=0;var start=0;var end=0;while(end<eventStats.length){runningEventNumPerSec+=eventStats[end].numEvents;runningEventBytesPerSec+=eventStats[end].totalEventSizeinBytes;end++;while((eventStats[end-1].timeInterval-
+histograms.addHistogram(startupWallHist);histograms.addHistogram(startupCPUHist);histograms.addHistogram(loadWallHist);histograms.addHistogram(loadCPUHist);}
+tr.metrics.MetricRegistry.register(webviewStartupMetric);return{webviewStartupMetric,};});'use strict';tr.exportTo('tr.metrics',function(){const MEMORY_INFRA_TRACING_CATEGORY='disabled-by-default-memory-infra';const TIME_BOUNDARIES=tr.v.HistogramBinBoundaries.createExponential(1e-3,1e5,30);const BYTE_BOUNDARIES=tr.v.HistogramBinBoundaries.createExponential(1,1e9,30);const COUNT_BOUNDARIES=tr.v.HistogramBinBoundaries.createExponential(1,1e5,30);function addTimeDurationHistogram(histogramName,duration,histograms,opt_description){let hist=new tr.v.Histogram(histogramName,tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,TIME_BOUNDARIES);hist.addSample(duration);hist.customizeSummaryOptions({count:false,min:false,max:false,sum:false,avg:true,std:false});histograms.addHistogram(hist);if(opt_description)hist.description=opt_description;}
+function addMemoryInfraHistograms(histograms,model,categoryNamesToTotalEventSizes){let memoryDumpCount=model.globalMemoryDumps.length;if(memoryDumpCount===0)return;let totalOverhead=0;let nonMemoryInfraThreadOverhead=0;let overheadByProvider={};for(let process of Object.values(model.processes)){for(let thread of Object.values(process.threads)){for(let slice of Object.values(thread.sliceGroup.slices)){if(slice.category!==MEMORY_INFRA_TRACING_CATEGORY)continue;totalOverhead+=slice.duration;if(thread.name!=='MemoryInfra'){nonMemoryInfraThreadOverhead+=slice.duration;}
+if(slice.args&&slice.args['dump_provider.name']){let providerName=slice.args['dump_provider.name'];let durationAndCount=overheadByProvider[providerName];if(durationAndCount===undefined){overheadByProvider[providerName]=durationAndCount={duration:0,count:0};}
+durationAndCount.duration+=slice.duration;durationAndCount.count++;}}}}
+addTimeDurationHistogram('memory_dump_cpu_overhead',totalOverhead/memoryDumpCount,histograms,'Average CPU overhead on all threads per memory-infra dump');addTimeDurationHistogram('nonmemory_thread_memory_dump_cpu_overhead',nonMemoryInfraThreadOverhead/memoryDumpCount,histograms,'Average CPU overhead on non-memory-infra threads per memory-infra '+'dump');for(let[providerName,overhead]of Object.entries(overheadByProvider)){addTimeDurationHistogram(`${providerName}_memory_dump_cpu_overhead`,overhead.duration/overhead.count,histograms,'Average CPU overhead of '+providerName+' per OnMemoryDump call');}
+let memoryInfraEventsSize=categoryNamesToTotalEventSizes.get(MEMORY_INFRA_TRACING_CATEGORY);let memoryInfraTraceBytesValue=new tr.v.Histogram('total_memory_dump_size',tr.b.Unit.byName.sizeInBytes_smallerIsBetter,BYTE_BOUNDARIES);memoryInfraTraceBytesValue.description='Total trace size of memory-infra dumps in bytes';memoryInfraTraceBytesValue.customizeSummaryOptions({count:false,min:false,max:false,sum:false,avg:true,std:false});memoryInfraTraceBytesValue.addSample(memoryInfraEventsSize);histograms.addHistogram(memoryInfraTraceBytesValue);let traceBytesPerDumpValue=new tr.v.Histogram('memory_dump_size',tr.b.Unit.byName.sizeInBytes_smallerIsBetter,BYTE_BOUNDARIES);traceBytesPerDumpValue.description='Average trace size of memory-infra dumps in bytes';traceBytesPerDumpValue.customizeSummaryOptions({count:false,min:false,max:false,sum:false,avg:true,std:false});traceBytesPerDumpValue.addSample(memoryInfraEventsSize/memoryDumpCount);histograms.addHistogram(traceBytesPerDumpValue);}
+function tracingMetric(histograms,model){addTimeDurationHistogram('trace_import_duration',model.stats.traceImportDurationMs,histograms,'Duration that trace viewer required to import the trace');if(!model.stats.hasEventSizesinBytes)return;let eventStats=model.stats.allTraceEventStatsInTimeIntervals;eventStats.sort((a,b)=>a.timeInterval-b.timeInterval);let totalTraceBytes=eventStats.reduce((a,b)=>a+b.totalEventSizeinBytes,0);let maxEventCountPerSec=0;let maxEventBytesPerSec=0;let INTERVALS_PER_SEC=Math.floor(1000/model.stats.TIME_INTERVAL_SIZE_IN_MS);let runningEventNumPerSec=0;let runningEventBytesPerSec=0;let start=0;let end=0;while(end<eventStats.length){runningEventNumPerSec+=eventStats[end].numEvents;runningEventBytesPerSec+=eventStats[end].totalEventSizeinBytes;end++;while((eventStats[end-1].timeInterval-
 eventStats[start].timeInterval)>=INTERVALS_PER_SEC){runningEventNumPerSec-=eventStats[start].numEvents;runningEventBytesPerSec-=eventStats[start].totalEventSizeinBytes;start++;}
 maxEventCountPerSec=Math.max(maxEventCountPerSec,runningEventNumPerSec);maxEventBytesPerSec=Math.max(maxEventBytesPerSec,runningEventBytesPerSec);}
-var stats=model.stats.allTraceEventStats;var categoryNamesToTotalEventSizes=(stats.reduce((map,stat)=>(map.set(stat.category,((map.get(stat.category)||0)+
-stat.totalEventSizeinBytes))),new Map()));var maxCatNameAndBytes=Array.from(categoryNamesToTotalEventSizes.entries()).reduce((a,b)=>(b[1]>=a[1])?b:a);var maxEventBytesPerCategory=maxCatNameAndBytes[1];var categoryWithMaxEventBytes=maxCatNameAndBytes[0];var maxEventCountPerSecValue=new tr.v.Histogram('Max number of events per second',tr.b.Unit.byName.count_smallerIsBetter,COUNT_BOUNDARIES);maxEventCountPerSecValue.addSample(maxEventCountPerSec);var maxEventBytesPerSecValue=new tr.v.Histogram('Max event size in bytes per second',tr.b.Unit.byName.sizeInBytes_smallerIsBetter,BYTE_BOUNDARIES);maxEventBytesPerSecValue.addSample(maxEventBytesPerSec);var totalTraceBytesValue=new tr.v.Histogram('Total trace size in bytes',tr.b.Unit.byName.sizeInBytes_smallerIsBetter,BYTE_BOUNDARIES);totalTraceBytesValue.addSample(totalTraceBytes);var biggestCategory={name:categoryWithMaxEventBytes,size_in_bytes:maxEventBytesPerCategory};totalTraceBytesValue.diagnostics.set('category_with_max_event_size',new tr.v.d.Generic(biggestCategory));values.addHistogram(totalTraceBytesValue);maxEventCountPerSecValue.diagnostics.set('category_with_max_event_size',new tr.v.d.Generic(biggestCategory));values.addHistogram(maxEventCountPerSecValue);maxEventBytesPerSecValue.diagnostics.set('category_with_max_event_size',new tr.v.d.Generic(biggestCategory));values.addHistogram(maxEventBytesPerSecValue);addMemoryInfraValues(values,model,categoryNamesToTotalEventSizes);}
-tr.metrics.MetricRegistry.register(tracingMetric);return{tracingMetric:tracingMetric,MEMORY_INFRA_TRACING_CATEGORY:MEMORY_INFRA_TRACING_CATEGORY};});'use strict';tr.exportTo('tr.metrics.v8',function(){var CUSTOM_BOUNDARIES=tr.v.HistogramBinBoundaries.createLinear(4,200,100);function computeExecuteMetrics(values,model){var cpuTotalExecution=new tr.v.Histogram('v8_execution_cpu_total',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);cpuTotalExecution.description='cpu total time spent in script execution';var wallTotalExecution=new tr.v.Histogram('v8_execution_wall_total',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);wallTotalExecution.description='wall total time spent in script execution';var cpuSelfExecution=new tr.v.Histogram('v8_execution_cpu_self',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);cpuSelfExecution.description='cpu self time spent in script execution';var wallSelfExecution=new tr.v.Histogram('v8_execution_wall_self',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);wallSelfExecution.description='wall self time spent in script execution';for(var e of model.findTopmostSlicesNamed('V8.Execute')){cpuTotalExecution.addSample(e.cpuDuration);wallTotalExecution.addSample(e.duration);cpuSelfExecution.addSample(e.cpuSelfTime);wallSelfExecution.addSample(e.selfTime);}
-values.addHistogram(cpuTotalExecution);values.addHistogram(wallTotalExecution);values.addHistogram(cpuSelfExecution);values.addHistogram(wallSelfExecution);}
-function computeParseLazyMetrics(values,model){var cpuSelfParseLazy=new tr.v.Histogram('v8_parse_lazy_cpu_self',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);cpuSelfParseLazy.description='cpu self time spent performing lazy parsing';var wallSelfParseLazy=new tr.v.Histogram('v8_parse_lazy_wall_self',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);wallSelfParseLazy.description='wall self time spent performing lazy parsing';for(var e of model.findTopmostSlicesNamed('V8.ParseLazyMicroSeconds')){cpuSelfParseLazy.addSample(e.cpuSelfTime);wallSelfParseLazy.addSample(e.selfTime);}
+let stats=model.stats.allTraceEventStats;let categoryNamesToTotalEventSizes=(stats.reduce((map,stat)=>(map.set(stat.category,((map.get(stat.category)||0)+
+stat.totalEventSizeinBytes))),new Map()));let maxCatNameAndBytes=Array.from(categoryNamesToTotalEventSizes.entries()).reduce((a,b)=>((b[1]>=a[1])?b:a));let maxEventBytesPerCategory=maxCatNameAndBytes[1];let categoryWithMaxEventBytes=maxCatNameAndBytes[0];let maxEventCountPerSecValue=new tr.v.Histogram('peak_event_rate',tr.b.Unit.byName.count_smallerIsBetter,COUNT_BOUNDARIES);maxEventCountPerSecValue.description='Max number of events per second';maxEventCountPerSecValue.customizeSummaryOptions({count:false,min:false,max:false,sum:false,avg:true,std:false});maxEventCountPerSecValue.addSample(maxEventCountPerSec);let maxEventBytesPerSecValue=new tr.v.Histogram('peak_event_size_rate',tr.b.Unit.byName.sizeInBytes_smallerIsBetter,BYTE_BOUNDARIES);maxEventBytesPerSecValue.description='Max event size in bytes per second';maxEventBytesPerSecValue.customizeSummaryOptions({count:false,min:false,max:false,sum:false,avg:true,std:false});maxEventBytesPerSecValue.addSample(maxEventBytesPerSec);let totalTraceBytesValue=new tr.v.Histogram('trace_size',tr.b.Unit.byName.sizeInBytes_smallerIsBetter,BYTE_BOUNDARIES);totalTraceBytesValue.customizeSummaryOptions({count:false,min:false,max:false,sum:false,avg:true,std:false});totalTraceBytesValue.addSample(totalTraceBytes);let biggestCategory={name:categoryWithMaxEventBytes,size_in_bytes:maxEventBytesPerCategory};totalTraceBytesValue.diagnostics.set('category_with_max_event_size',new tr.v.d.Generic(biggestCategory));histograms.addHistogram(totalTraceBytesValue);maxEventCountPerSecValue.diagnostics.set('category_with_max_event_size',new tr.v.d.Generic(biggestCategory));histograms.addHistogram(maxEventCountPerSecValue);maxEventBytesPerSecValue.diagnostics.set('category_with_max_event_size',new tr.v.d.Generic(biggestCategory));histograms.addHistogram(maxEventBytesPerSecValue);addMemoryInfraHistograms(histograms,model,categoryNamesToTotalEventSizes);}
+tr.metrics.MetricRegistry.register(tracingMetric);return{tracingMetric,MEMORY_INFRA_TRACING_CATEGORY,};});'use strict';tr.exportTo('tr.metrics.v8',function(){var CUSTOM_BOUNDARIES=tr.v.HistogramBinBoundaries.createLinear(4,200,100);function computeExecuteMetrics(histograms,model){var cpuTotalExecution=new tr.v.Histogram('v8_execution_cpu_total',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);cpuTotalExecution.description='cpu total time spent in script execution';var wallTotalExecution=new tr.v.Histogram('v8_execution_wall_total',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);wallTotalExecution.description='wall total time spent in script execution';var cpuSelfExecution=new tr.v.Histogram('v8_execution_cpu_self',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);cpuSelfExecution.description='cpu self time spent in script execution';var wallSelfExecution=new tr.v.Histogram('v8_execution_wall_self',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);wallSelfExecution.description='wall self time spent in script execution';for(var e of model.findTopmostSlicesNamed('V8.Execute')){cpuTotalExecution.addSample(e.cpuDuration);wallTotalExecution.addSample(e.duration);cpuSelfExecution.addSample(e.cpuSelfTime);wallSelfExecution.addSample(e.selfTime);}
+histograms.addHistogram(cpuTotalExecution);histograms.addHistogram(wallTotalExecution);histograms.addHistogram(cpuSelfExecution);histograms.addHistogram(wallSelfExecution);}
+function computeParseLazyMetrics(histograms,model){var cpuSelfParseLazy=new tr.v.Histogram('v8_parse_lazy_cpu_self',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);cpuSelfParseLazy.description='cpu self time spent performing lazy parsing';var wallSelfParseLazy=new tr.v.Histogram('v8_parse_lazy_wall_self',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);wallSelfParseLazy.description='wall self time spent performing lazy parsing';for(var e of model.findTopmostSlicesNamed('V8.ParseLazyMicroSeconds')){cpuSelfParseLazy.addSample(e.cpuSelfTime);wallSelfParseLazy.addSample(e.selfTime);}
 for(var e of model.findTopmostSlicesNamed('V8.ParseLazy')){cpuSelfParseLazy.addSample(e.cpuSelfTime);wallSelfParseLazy.addSample(e.selfTime);}
-values.addHistogram(cpuSelfParseLazy);values.addHistogram(wallSelfParseLazy);}
-function computeCompileFullCodeMetrics(values,model){var cpuSelfCompileFullCode=new tr.v.Histogram('v8_compile_full_code_cpu_self',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);cpuSelfCompileFullCode.description='cpu self time spent performing compiling full code';var wallSelfCompileFullCode=new tr.v.Histogram('v8_compile_full_code_wall_self',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);wallSelfCompileFullCode.description='wall self time spent performing compiling full code';for(var e of model.findTopmostSlicesNamed('V8.CompileFullCode')){cpuSelfCompileFullCode.addSample(e.cpuSelfTime);wallSelfCompileFullCode.addSample(e.selfTime);}
-values.addHistogram(cpuSelfCompileFullCode);values.addHistogram(wallSelfCompileFullCode);}
-function computeCompileIgnitionMetrics(values,model){var cpuSelfCompileIgnition=new tr.v.Histogram('v8_compile_ignition_cpu_self',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);cpuSelfCompileIgnition.description='cpu self time spent in compile ignition';var wallSelfCompileIgnition=new tr.v.Histogram('v8_compile_ignition_wall_self',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);wallSelfCompileIgnition.description='wall self time spent in compile ignition';for(var e of model.findTopmostSlicesNamed('V8.CompileIgnition')){cpuSelfCompileIgnition.addSample(e.cpuSelfTime);wallSelfCompileIgnition.addSample(e.selfTime);}
-values.addHistogram(cpuSelfCompileIgnition);values.addHistogram(wallSelfCompileIgnition);}
-function computeRecompileMetrics(values,model){var cpuTotalRecompileSynchronous=new tr.v.Histogram('v8_recompile_synchronous_cpu_total',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);cpuTotalRecompileSynchronous.description='cpu total time spent in synchronous recompilation';var wallTotalRecompileSynchronous=new tr.v.Histogram('v8_recompile_synchronous_wall_total',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);wallTotalRecompileSynchronous.description='wall total time spent in synchronous recompilation';var cpuTotalRecompileConcurrent=new tr.v.Histogram('v8_recompile_concurrent_cpu_total',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);cpuTotalRecompileConcurrent.description='cpu total time spent in concurrent recompilation';var wallTotalRecompileConcurrent=new tr.v.Histogram('v8_recompile_concurrent_wall_total',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);wallTotalRecompileConcurrent.description='wall total time spent in concurrent recompilation';var cpuTotalRecompileOverall=new tr.v.Histogram('v8_recompile_overall_cpu_total',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);cpuTotalRecompileOverall.description='cpu total time spent in synchronous or concurrent recompilation';var wallTotalRecompileOverall=new tr.v.Histogram('v8_recompile_overall_wall_total',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);wallTotalRecompileOverall.description='wall total time spent in synchronous or concurrent recompilation';for(var e of model.findTopmostSlicesNamed('V8.RecompileSynchronous')){cpuTotalRecompileSynchronous.addSample(e.cpuDuration);wallTotalRecompileSynchronous.addSample(e.duration);cpuTotalRecompileOverall.addSample(e.cpuDuration);wallTotalRecompileOverall.addSample(e.duration);}
-values.addHistogram(cpuTotalRecompileSynchronous);values.addHistogram(wallTotalRecompileSynchronous);for(var e of model.findTopmostSlicesNamed('V8.RecompileConcurrent')){cpuTotalRecompileConcurrent.addSample(e.cpuDuration);wallTotalRecompileConcurrent.addSample(e.duration);cpuTotalRecompileOverall.addSample(e.cpuDuration);wallTotalRecompileOverall.addSample(e.duration);}
-values.addHistogram(cpuTotalRecompileConcurrent);values.addHistogram(wallTotalRecompileConcurrent);values.addHistogram(cpuTotalRecompileOverall);values.addHistogram(wallTotalRecompileOverall);}
-function computeOptimizeCodeMetrics(values,model){var cpuTotalOptimizeCode=new tr.v.Histogram('v8_optimize_code_cpu_total',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);cpuTotalOptimizeCode.description='cpu total time spent in code optimization';var wallTotalOptimizeCode=new tr.v.Histogram('v8_optimize_code_wall_total',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);wallTotalOptimizeCode.description='wall total time spent in code optimization';for(var e of model.findTopmostSlicesNamed('V8.OptimizeCode')){cpuTotalOptimizeCode.addSample(e.cpuDuration);wallTotalOptimizeCode.addSample(e.duration);}
-values.addHistogram(cpuTotalOptimizeCode);values.addHistogram(wallTotalOptimizeCode);}
-function computeDeoptimizeCodeMetrics(values,model){var cpuTotalDeoptimizeCode=new tr.v.Histogram('v8_deoptimize_code_cpu_total',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);cpuTotalDeoptimizeCode.description='cpu total time spent in code deoptimization';var wallTotalDeoptimizeCode=new tr.v.Histogram('v8_deoptimize_code_wall_total',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);wallTotalDeoptimizeCode.description='wall total time spent in code deoptimization';for(var e of model.findTopmostSlicesNamed('V8.DeoptimizeCode')){cpuTotalDeoptimizeCode.addSample(e.cpuDuration);wallTotalDeoptimizeCode.addSample(e.duration);}
-values.addHistogram(cpuTotalDeoptimizeCode);values.addHistogram(wallTotalDeoptimizeCode);}
-function executionMetric(values,model){computeExecuteMetrics(values,model);computeParseLazyMetrics(values,model);computeCompileIgnitionMetrics(values,model);computeCompileFullCodeMetrics(values,model);computeRecompileMetrics(values,model);computeOptimizeCodeMetrics(values,model);computeDeoptimizeCodeMetrics(values,model);}
-tr.metrics.MetricRegistry.register(executionMetric);return{executionMetric:executionMetric};});'use strict';tr.exportTo('tr.metrics.v8',function(){var TARGET_FPS=60;var MS_PER_SECOND=1000;var WINDOW_SIZE_MS=MS_PER_SECOND/TARGET_FPS;function gcMetric(values,model){addDurationOfTopEvents(values,model);addTotalDurationOfTopEvents(values,model);addDurationOfSubEvents(values,model);addIdleTimesOfTopEvents(values,model);addTotalIdleTimesOfTopEvents(values,model);addPercentageInV8ExecuteOfTopEvents(values,model);addTotalPercentageInV8Execute(values,model);addV8ExecuteMutatorUtilization(values,model);}
+histograms.addHistogram(cpuSelfParseLazy);histograms.addHistogram(wallSelfParseLazy);}
+function computeCompileFullCodeMetrics(histograms,model){var cpuSelfCompileFullCode=new tr.v.Histogram('v8_compile_full_code_cpu_self',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);cpuSelfCompileFullCode.description='cpu self time spent performing compiling full code';var wallSelfCompileFullCode=new tr.v.Histogram('v8_compile_full_code_wall_self',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);wallSelfCompileFullCode.description='wall self time spent performing compiling full code';for(var e of model.findTopmostSlicesNamed('V8.CompileFullCode')){cpuSelfCompileFullCode.addSample(e.cpuSelfTime);wallSelfCompileFullCode.addSample(e.selfTime);}
+histograms.addHistogram(cpuSelfCompileFullCode);histograms.addHistogram(wallSelfCompileFullCode);}
+function computeCompileIgnitionMetrics(histograms,model){var cpuSelfCompileIgnition=new tr.v.Histogram('v8_compile_ignition_cpu_self',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);cpuSelfCompileIgnition.description='cpu self time spent in compile ignition';var wallSelfCompileIgnition=new tr.v.Histogram('v8_compile_ignition_wall_self',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);wallSelfCompileIgnition.description='wall self time spent in compile ignition';for(var e of model.findTopmostSlicesNamed('V8.CompileIgnition')){cpuSelfCompileIgnition.addSample(e.cpuSelfTime);wallSelfCompileIgnition.addSample(e.selfTime);}
+histograms.addHistogram(cpuSelfCompileIgnition);histograms.addHistogram(wallSelfCompileIgnition);}
+function computeRecompileMetrics(histograms,model){var cpuTotalRecompileSynchronous=new tr.v.Histogram('v8_recompile_synchronous_cpu_total',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);cpuTotalRecompileSynchronous.description='cpu total time spent in synchronous recompilation';var wallTotalRecompileSynchronous=new tr.v.Histogram('v8_recompile_synchronous_wall_total',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);wallTotalRecompileSynchronous.description='wall total time spent in synchronous recompilation';var cpuTotalRecompileConcurrent=new tr.v.Histogram('v8_recompile_concurrent_cpu_total',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);cpuTotalRecompileConcurrent.description='cpu total time spent in concurrent recompilation';var wallTotalRecompileConcurrent=new tr.v.Histogram('v8_recompile_concurrent_wall_total',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);wallTotalRecompileConcurrent.description='wall total time spent in concurrent recompilation';var cpuTotalRecompileOverall=new tr.v.Histogram('v8_recompile_overall_cpu_total',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);cpuTotalRecompileOverall.description='cpu total time spent in synchronous or concurrent recompilation';var wallTotalRecompileOverall=new tr.v.Histogram('v8_recompile_overall_wall_total',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);wallTotalRecompileOverall.description='wall total time spent in synchronous or concurrent recompilation';for(var e of model.findTopmostSlicesNamed('V8.RecompileSynchronous')){cpuTotalRecompileSynchronous.addSample(e.cpuDuration);wallTotalRecompileSynchronous.addSample(e.duration);cpuTotalRecompileOverall.addSample(e.cpuDuration);wallTotalRecompileOverall.addSample(e.duration);}
+histograms.addHistogram(cpuTotalRecompileSynchronous);histograms.addHistogram(wallTotalRecompileSynchronous);for(var e of model.findTopmostSlicesNamed('V8.RecompileConcurrent')){cpuTotalRecompileConcurrent.addSample(e.cpuDuration);wallTotalRecompileConcurrent.addSample(e.duration);cpuTotalRecompileOverall.addSample(e.cpuDuration);wallTotalRecompileOverall.addSample(e.duration);}
+histograms.addHistogram(cpuTotalRecompileConcurrent);histograms.addHistogram(wallTotalRecompileConcurrent);histograms.addHistogram(cpuTotalRecompileOverall);histograms.addHistogram(wallTotalRecompileOverall);}
+function computeOptimizeCodeMetrics(histograms,model){var cpuTotalOptimizeCode=new tr.v.Histogram('v8_optimize_code_cpu_total',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);cpuTotalOptimizeCode.description='cpu total time spent in code optimization';var wallTotalOptimizeCode=new tr.v.Histogram('v8_optimize_code_wall_total',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);wallTotalOptimizeCode.description='wall total time spent in code optimization';for(var e of model.findTopmostSlicesNamed('V8.OptimizeCode')){cpuTotalOptimizeCode.addSample(e.cpuDuration);wallTotalOptimizeCode.addSample(e.duration);}
+histograms.addHistogram(cpuTotalOptimizeCode);histograms.addHistogram(wallTotalOptimizeCode);}
+function computeDeoptimizeCodeMetrics(histograms,model){var cpuTotalDeoptimizeCode=new tr.v.Histogram('v8_deoptimize_code_cpu_total',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);cpuTotalDeoptimizeCode.description='cpu total time spent in code deoptimization';var wallTotalDeoptimizeCode=new tr.v.Histogram('v8_deoptimize_code_wall_total',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);wallTotalDeoptimizeCode.description='wall total time spent in code deoptimization';for(var e of model.findTopmostSlicesNamed('V8.DeoptimizeCode')){cpuTotalDeoptimizeCode.addSample(e.cpuDuration);wallTotalDeoptimizeCode.addSample(e.duration);}
+histograms.addHistogram(cpuTotalDeoptimizeCode);histograms.addHistogram(wallTotalDeoptimizeCode);}
+function executionMetric(histograms,model){computeExecuteMetrics(histograms,model);computeParseLazyMetrics(histograms,model);computeCompileIgnitionMetrics(histograms,model);computeCompileFullCodeMetrics(histograms,model);computeRecompileMetrics(histograms,model);computeOptimizeCodeMetrics(histograms,model);computeDeoptimizeCodeMetrics(histograms,model);}
+tr.metrics.MetricRegistry.register(executionMetric);return{executionMetric,};});'use strict';tr.exportTo('tr.metrics.v8',function(){var TARGET_FPS=60;var MS_PER_SECOND=1000;var WINDOW_SIZE_MS=MS_PER_SECOND/TARGET_FPS;function gcMetric(histograms,model){addDurationOfTopEvents(histograms,model);addTotalDurationOfTopEvents(histograms,model);addDurationOfSubEvents(histograms,model);addPercentageInV8ExecuteOfTopEvents(histograms,model);addTotalPercentageInV8Execute(histograms,model);}
 tr.metrics.MetricRegistry.register(gcMetric);var timeDurationInMs_smallerIsBetter=tr.b.Unit.byName.timeDurationInMs_smallerIsBetter;var percentage_biggerIsBetter=tr.b.Unit.byName.normalizedPercentage_biggerIsBetter;var percentage_smallerIsBetter=tr.b.Unit.byName.normalizedPercentage_smallerIsBetter;var CUSTOM_BOUNDARIES=tr.v.HistogramBinBoundaries.createLinear(0,20,200).addExponentialBins(200,100);function createNumericForTopEventTime(name){var n=new tr.v.Histogram(name,timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);n.customizeSummaryOptions({avg:true,count:true,max:true,min:false,std:true,sum:true,percentile:[0.90]});return n;}
 function createNumericForSubEventTime(name){var n=new tr.v.Histogram(name,timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);n.customizeSummaryOptions({avg:true,count:false,max:true,min:false,std:false,sum:false,percentile:[0.90]});return n;}
 function createNumericForIdleTime(name){var n=new tr.v.Histogram(name,timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);n.customizeSummaryOptions({avg:true,count:false,max:true,min:false,std:false,sum:true,percentile:[]});return n;}
-function createPercentage(name,numerator,denominator,unit){var hist=new tr.v.Histogram(name,unit);if(denominator===0)
-hist.addSample(0);else
-hist.addSample(numerator/denominator);hist.customizeSummaryOptions({avg:true,count:false,max:false,min:false,std:false,sum:false,percentile:[]});return hist;}
+function createPercentage(name,numerator,denominator,unit){var hist=new tr.v.Histogram(name,unit);if(denominator===0){hist.addSample(0);}else{hist.addSample(numerator/denominator);}
+hist.customizeSummaryOptions({avg:true,count:false,max:false,min:false,std:false,sum:false,percentile:[]});return hist;}
 function isNotForcedTopGarbageCollectionEvent(event){return tr.metrics.v8.utils.isTopGarbageCollectionEvent(event)&&!tr.metrics.v8.utils.isForcedGarbageCollectionEvent(event);}
 function isNotForcedSubGarbageCollectionEvent(event){return tr.metrics.v8.utils.isSubGarbageCollectionEvent(event)&&!tr.metrics.v8.utils.isForcedGarbageCollectionEvent(event);}
-function addDurationOfTopEvents(values,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isNotForcedTopGarbageCollectionEvent,tr.metrics.v8.utils.topGarbageCollectionEventName,function(name,events){var cpuDuration=createNumericForTopEventTime(name);events.forEach(function(event){cpuDuration.addSample(event.cpuDuration);});values.addHistogram(cpuDuration);});}
-function addTotalDurationOfTopEvents(values,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isNotForcedTopGarbageCollectionEvent,event=>'v8-gc-total',function(name,events){var cpuDuration=createNumericForTopEventTime(name);events.forEach(function(event){cpuDuration.addSample(event.cpuDuration);});values.addHistogram(cpuDuration);});}
-function addDurationOfSubEvents(values,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isNotForcedSubGarbageCollectionEvent,tr.metrics.v8.utils.subGarbageCollectionEventName,function(name,events){var cpuDuration=createNumericForSubEventTime(name);events.forEach(function(event){cpuDuration.addSample(event.cpuDuration);});values.addHistogram(cpuDuration);});}
-function addIdleTimesOfTopEvents(values,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isNotForcedTopGarbageCollectionEvent,tr.metrics.v8.utils.topGarbageCollectionEventName,function(name,events){addIdleTimes(values,model,name,events);});}
-function addTotalIdleTimesOfTopEvents(values,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isNotForcedTopGarbageCollectionEvent,event=>'v8-gc-total',function(name,events){addIdleTimes(values,model,name,events);});}
-function addIdleTimes(values,model,name,events){var cpuDuration=createNumericForIdleTime();var insideIdle=createNumericForIdleTime();var outsideIdle=createNumericForIdleTime(name+'_outside_idle');var idleDeadlineOverrun=createNumericForIdleTime(name+'_idle_deadline_overrun');events.forEach(function(event){var idleTask=tr.metrics.v8.utils.findParent(event,tr.metrics.v8.utils.isIdleTask);var inside=0;var overrun=0;if(idleTask){var allottedTime=idleTask['args']['allotted_time_ms'];if(event.duration>allottedTime){overrun=event.duration-allottedTime;inside=event.cpuDuration*allottedTime/event.duration;}else{inside=event.cpuDuration;}}
-cpuDuration.addSample(event.cpuDuration);insideIdle.addSample(inside);outsideIdle.addSample(event.cpuDuration-inside);idleDeadlineOverrun.addSample(overrun);});values.addHistogram(idleDeadlineOverrun);values.addHistogram(outsideIdle);var percentage=createPercentage(name+'_percentage_idle',insideIdle.sum,cpuDuration.sum,percentage_biggerIsBetter);values.addHistogram(percentage);}
-function addPercentageInV8ExecuteOfTopEvents(values,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isNotForcedTopGarbageCollectionEvent,tr.metrics.v8.utils.topGarbageCollectionEventName,function(name,events){addPercentageInV8Execute(values,model,name,events);});}
-function addTotalPercentageInV8Execute(values,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isNotForcedTopGarbageCollectionEvent,event=>'v8-gc-total',function(name,events){addPercentageInV8Execute(values,model,name,events);});}
-function addPercentageInV8Execute(values,model,name,events){var cpuDurationInV8Execute=0;var cpuDurationTotal=0;events.forEach(function(event){var v8Execute=tr.metrics.v8.utils.findParent(event,tr.metrics.v8.utils.isV8ExecuteEvent);if(v8Execute){cpuDurationInV8Execute+=event.cpuDuration;}
-cpuDurationTotal+=event.cpuDuration;});var percentage=createPercentage(name+'_percentage_in_v8_execute',cpuDurationInV8Execute,cpuDurationTotal,percentage_smallerIsBetter);values.addHistogram(percentage);}
-function addV8ExecuteMutatorUtilization(values,model){tr.metrics.v8.utils.groupAndProcessEvents(model,tr.metrics.v8.utils.isTopV8ExecuteEvent,event=>'v8-execute',function(name,events){events.sort((a,b)=>a.start-b.start);var time=0;var pauses=[];for(var topEvent of events){for(var e of topEvent.enumerateAllDescendents()){if(isNotForcedTopGarbageCollectionEvent(e)){pauses.push({start:e.start-topEvent.start+time,end:e.end-topEvent.start+time});}}
-time+=topEvent.duration;}
-var mutatorUtilization=tr.metrics.v8.utils.mutatorUtilization(0,time,WINDOW_SIZE_MS,pauses);[0.90,0.95,0.99].forEach(function(percent){var hist=new tr.v.Histogram('v8-execute-mutator-utilization_pct_0'+percent*100,percentage_biggerIsBetter);hist.addSample(mutatorUtilization.percentile(1.0-percent));values.addHistogram(hist);});var hist=new tr.v.Histogram('v8-execute-mutator-utilization_min',percentage_biggerIsBetter);hist.addSample(mutatorUtilization.min);values.addHistogram(hist);});}
-return{gcMetric:gcMetric,WINDOW_SIZE_MS:WINDOW_SIZE_MS};});'use strict';tr.exportTo('tr.metrics.v8',function(){function v8AndMemoryMetrics(values,model){tr.metrics.v8.executionMetric(values,model);tr.metrics.v8.gcMetric(values,model);tr.metrics.sh.memoryMetric(values,model,{rangeOfInterest:tr.metrics.v8.utils.rangeForMemoryDumps(model)});}
-tr.metrics.MetricRegistry.register(v8AndMemoryMetrics);return{v8AndMemoryMetrics:v8AndMemoryMetrics};});'use strict';Polymer({is:'tr-ui-a-alert-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],ready:function(){this.currentSelection_=undefined;this.$.table.tableColumns=[{title:'Label',value:function(row){return row.name;},width:'150px'},{title:'Value',width:'100%',value:function(row){return row.value;}}];this.$.table.showHeader=false;},get selection(){return this.currentSelection_;},set selection(selection){this.currentSelection_=selection;this.updateContents_();},getRowsForSingleAlert_:function(alert){var rows=[];for(var argName in alert.args){var argView=document.createElement('tr-ui-a-generic-object-view');argView.object=alert.args[argName];rows.push({name:argName,value:argView});}
-if(alert.associatedEvents.length){alert.associatedEvents.forEach(function(event,i){var linkEl=document.createElement('tr-ui-a-analysis-link');linkEl.setSelectionAndContent(new tr.model.EventSet(event),event.title);var valueString='';if(event instanceof tr.model.TimedEvent)
-valueString='took '+event.duration.toFixed(2)+'ms';rows.push({name:linkEl,value:valueString});});}
+function addDurationOfTopEvents(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isNotForcedTopGarbageCollectionEvent,tr.metrics.v8.utils.topGarbageCollectionEventName,function(name,events){var cpuDuration=createNumericForTopEventTime(name);events.forEach(function(event){cpuDuration.addSample(event.cpuDuration);});histograms.addHistogram(cpuDuration);});}
+function addTotalDurationOfTopEvents(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isNotForcedTopGarbageCollectionEvent,event=>'v8-gc-total',function(name,events){var cpuDuration=createNumericForTopEventTime(name);events.forEach(function(event){cpuDuration.addSample(event.cpuDuration);});histograms.addHistogram(cpuDuration);});}
+function addDurationOfSubEvents(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isNotForcedSubGarbageCollectionEvent,tr.metrics.v8.utils.subGarbageCollectionEventName,function(name,events){var cpuDuration=createNumericForSubEventTime(name);events.forEach(function(event){cpuDuration.addSample(event.cpuDuration);});histograms.addHistogram(cpuDuration);});}
+function addPercentageInV8ExecuteOfTopEvents(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isNotForcedTopGarbageCollectionEvent,tr.metrics.v8.utils.topGarbageCollectionEventName,function(name,events){addPercentageInV8Execute(histograms,model,name,events);});}
+function addTotalPercentageInV8Execute(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isNotForcedTopGarbageCollectionEvent,event=>'v8-gc-total',function(name,events){addPercentageInV8Execute(histograms,model,name,events);});}
+function addPercentageInV8Execute(histograms,model,name,events){var cpuDurationInV8Execute=0;var cpuDurationTotal=0;events.forEach(function(event){var v8Execute=tr.metrics.v8.utils.findParent(event,tr.metrics.v8.utils.isV8ExecuteEvent);if(v8Execute){cpuDurationInV8Execute+=event.cpuDuration;}
+cpuDurationTotal+=event.cpuDuration;});var percentage=createPercentage(name+'_percentage_in_v8_execute',cpuDurationInV8Execute,cpuDurationTotal,percentage_smallerIsBetter);histograms.addHistogram(percentage);}
+return{gcMetric,WINDOW_SIZE_MS,};});'use strict';tr.exportTo('tr.metrics.v8',function(){var COUNT_CUSTOM_BOUNDARIES=tr.v.HistogramBinBoundaries.createExponential(1,1000000,50);var DURATION_CUSTOM_BOUNDARIES=tr.v.HistogramBinBoundaries.createExponential(0.1,10000,50);function computeDomContentLoadedTime_(model){var chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);var domContentLoadedTime=0;for(var rendererHelper of Object.values(chromeHelper.rendererHelpers)){for(var ev of rendererHelper.mainThread.sliceGroup.childEvents()){if(ev.title==='domContentLoadedEventEnd'&&ev.start>domContentLoadedTime){domContentLoadedTime=ev.start;}}}
+return domContentLoadedTime;}
+function computeInteractiveTime_(model){var chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);var interactiveTime=0;for(var rendererHelper of Object.values(chromeHelper.rendererHelpers)){var samples=tr.metrics.sh.collectLoadingMetricsForRenderer(rendererHelper).firstInteractiveSamples;if(samples.length===0)continue;if(interactiveTime!==0)throw new Error('Too many navigations');interactiveTime=tr.b.getOnlyElement(samples).diagnostics['Navigation infos'].value.interactive;}
+return interactiveTime;}
+function createDurationHistogram_(name){var histogram=new tr.v.Histogram(name+':duration',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,DURATION_CUSTOM_BOUNDARIES);histogram.customizeSummaryOptions({std:false,count:false,sum:false,min:false,max:false});return histogram;}
+function createCountHistogram_(name){var histogram=new tr.v.Histogram(name+':count',tr.b.Unit.byName.count_smallerIsBetter,COUNT_CUSTOM_BOUNDARIES);histogram.customizeSummaryOptions({std:false,count:false,sum:false,min:false,max:false});return histogram;}
+function convertMicroToMilli_(time){return tr.b.convertUnit(time,tr.b.UnitPrefixScale.METRIC.MICRO,tr.b.UnitPrefixScale.METRIC.MILLI);}
+function computeRuntimeStats(histograms,slices){var runtimeGroupCollection=new tr.e.v8.RuntimeStatsGroupCollection();runtimeGroupCollection.addSlices(slices);for(var runtimeGroup of runtimeGroupCollection.runtimeGroups){var durationSamples=new tr.v.d.RelatedHistogramBreakdown();var countSamples=new tr.v.d.RelatedHistogramBreakdown();for(var entry of runtimeGroup.values){var durationSampleHistogram=createDurationHistogram_(entry.name);durationSampleHistogram.addSample(convertMicroToMilli_(entry.time));durationSamples.set(entry.name+':duration',durationSampleHistogram);histograms.addHistogram(durationSampleHistogram);var countSampleHistogram=createCountHistogram_(entry.name);countSampleHistogram.addSample(entry.count);countSamples.set(entry.name+':count',countSampleHistogram);histograms.addHistogram(countSampleHistogram);}
+var durationHistogram=createDurationHistogram_(runtimeGroup.name);durationHistogram.addSample(convertMicroToMilli_(runtimeGroup.time),{samples:durationSamples});var countHistogram=createCountHistogram_(runtimeGroup.name);countHistogram.addSample(runtimeGroup.count,{samples:countSamples});histograms.addHistogram(durationHistogram);histograms.addHistogram(countHistogram);}}
+function runtimeStatsMetric(histograms,model){var interactiveTime=computeInteractiveTime_(model);var domContentLoadedTime=computeDomContentLoadedTime_(model);var endTime=Math.max(interactiveTime,domContentLoadedTime);var slices=[...model.getDescendantEvents()].filter(event=>event instanceof tr.e.v8.V8ThreadSlice&&event.start<=endTime);computeRuntimeStats(histograms,slices);}
+function computeRuntimeStatsBucketOnUE(histograms,slices,v8SlicesBucketOnUEMap){let durationRelatedHistsByGroupName=new Map();let countRelatedHistsByGroupName=new Map();for(var[name,slicesUE]of v8SlicesBucketOnUEMap){var runtimeGroupCollection=new tr.e.v8.RuntimeStatsGroupCollection();runtimeGroupCollection.addSlices(slicesUE);for(var runtimeGroup of runtimeGroupCollection.runtimeGroups){var histogramName=name+'_'+runtimeGroup.name;var durationHistogram=createDurationHistogram_(histogramName);durationHistogram.addSample(convertMicroToMilli_(runtimeGroup.time));histograms.addHistogram(durationHistogram);if(durationRelatedHistsByGroupName.get(runtimeGroup.name)===undefined){var durationHistogramMap=new tr.v.d.RelatedHistogramMap();durationHistogramMap.set(name,durationHistogram);durationRelatedHistsByGroupName.set(runtimeGroup.name,durationHistogramMap);}else{durationRelatedHistsByGroupName.get(runtimeGroup.name).set(name,durationHistogram);}
+var countHistogram=createCountHistogram_(histogramName);countHistogram.addSample(runtimeGroup.count);histograms.addHistogram(countHistogram);if(countRelatedHistsByGroupName.get(runtimeGroup.name)===undefined){var countHistogramMap=new tr.v.d.RelatedHistogramMap();countHistogramMap.set(name,countHistogram);countRelatedHistsByGroupName.set(runtimeGroup.name,countHistogramMap);}else{countRelatedHistsByGroupName.get(runtimeGroup.name).set(name,countHistogram);}}}
+var runtimeGroupCollection=new tr.e.v8.RuntimeStatsGroupCollection();runtimeGroupCollection.addSlices(slices);for(var runtimeGroup of runtimeGroupCollection.runtimeGroups){var histogramName=runtimeGroup.name;var durationHistogram=createDurationHistogram_(histogramName);durationHistogram.addSample(convertMicroToMilli_(runtimeGroup.time));histograms.addHistogram(durationHistogram);var durationRelatedHistogram=durationRelatedHistsByGroupName.get(runtimeGroup.name);if(durationRelatedHistogram!==undefined){durationHistogram.diagnostics.set('RAIL stages',durationRelatedHistogram);}
+var countHistogram=createCountHistogram_(histogramName);countHistogram.addSample(runtimeGroup.count);var countRelatedHistogram=countRelatedHistsByGroupName.get(runtimeGroup.name);if(countRelatedHistogram!==undefined){countHistogram.diagnostics.set('RAIL stages',countRelatedHistogram);}
+histograms.addHistogram(countHistogram);}}
+function runtimeStatsTotalMetric(histograms,model){var v8ThreadSlices=[...model.getDescendantEvents()].filter(event=>event instanceof tr.e.v8.V8ThreadSlice).sort((e1,e2)=>e1.start-e2.start);var v8SlicesBucketOnUEMap=new Map();for(var expectation of model.userModel.expectations){var slices=expectation.range.filterArray(v8ThreadSlices,event=>event.start);if(slices.length===0)continue;var lastSlice=slices[slices.length-1];if(!expectation.range.intersectsRangeExclusive(lastSlice.range)){slices.pop();}
+if(v8SlicesBucketOnUEMap.get(expectation.stageTitle)===undefined){v8SlicesBucketOnUEMap.set(expectation.stageTitle,slices);}else{var totalSlices=v8SlicesBucketOnUEMap.get(expectation.stageTitle).concat(slices);v8SlicesBucketOnUEMap.set(expectation.stageTitle,totalSlices);}}
+computeRuntimeStatsBucketOnUE(histograms,v8ThreadSlices,v8SlicesBucketOnUEMap);}
+tr.metrics.MetricRegistry.register(runtimeStatsTotalMetric);tr.metrics.MetricRegistry.register(runtimeStatsMetric);return{runtimeStatsMetric,runtimeStatsTotalMetric,};});'use strict';tr.exportTo('tr.metrics.v8',function(){function v8AndMemoryMetrics(histograms,model){tr.metrics.v8.executionMetric(histograms,model);tr.metrics.v8.gcMetric(histograms,model);tr.metrics.sh.memoryMetric(histograms,model,{rangeOfInterest:tr.metrics.v8.utils.rangeForMemoryDumps(model)});}
+tr.metrics.MetricRegistry.register(v8AndMemoryMetrics);return{v8AndMemoryMetrics,};});'use strict';tr.exportTo('tr.metrics.webrtc',function(){const DISPLAY_HERTZ=60.0;const VSYNC_DURATION_US=1e6/DISPLAY_HERTZ;const SEVERITY=3;const FROZEN_FRAME_VSYNC_COUNT_THRESHOLD=6;const WEB_MEDIA_PLAYER_UPDATE_TITLE='WebMediaPlayerMS::UpdateCurrentFrame';const IDEAL_RENDER_INSTANT_NAME='Ideal Render Instant';const ACTUAL_RENDER_BEGIN_NAME='Actual Render Begin';const ACTUAL_RENDER_END_NAME='Actual Render End';const STREAM_ID_NAME='Serial';const REQUIRED_EVENT_ARGS_NAMES=[IDEAL_RENDER_INSTANT_NAME,ACTUAL_RENDER_BEGIN_NAME,ACTUAL_RENDER_END_NAME,STREAM_ID_NAME];const count_smallerIsBetter=tr.b.Unit.byName.count_smallerIsBetter;const percentage_biggerIsBetter=tr.b.Unit.byName.normalizedPercentage_biggerIsBetter;const percentage_smallerIsBetter=tr.b.Unit.byName.normalizedPercentage_smallerIsBetter;const timeDurationInMs_smallerIsBetter=tr.b.Unit.byName.timeDurationInMs_smallerIsBetter;const unitlessNumber_biggerIsBetter=tr.b.Unit.byName.unitlessNumber_biggerIsBetter;function isValidEvent(event){if(event.title!==WEB_MEDIA_PLAYER_UPDATE_TITLE||!event.args){return false;}
+for(let parameter of REQUIRED_EVENT_ARGS_NAMES){if(!(parameter in event.args)){return false;}}
+return true;}
+function webrtcRenderingMetric(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isValidEvent,event=>event.args[STREAM_ID_NAME],(streamName,events)=>getTimeStats(histograms,streamName,events));}
+tr.metrics.MetricRegistry.register(webrtcRenderingMetric);function addHistogram(samples,histograms,name,unit,opt_summaryOptions){let summaryOptions=opt_summaryOptions;if(!summaryOptions){summaryOptions={count:false,max:false,min:false,std:false,sum:false,};}
+let histogram=new tr.v.Histogram(name,unit);for(let sample of samples){histogram.addSample(sample);}
+histogram.customizeSummaryOptions(summaryOptions);histograms.addHistogram(histogram);}
+function getTimeStats(histograms,streamName,events){let frameHist=getFrameDistribution(histograms,events);addFpsFromFrameDistribution(histograms,frameHist);addFreezingScore(histograms,frameHist);let driftTimeStats=getDriftStats(events);addHistogram(driftTimeStats.driftTime,histograms,'WebRTCRendering_drift_time',timeDurationInMs_smallerIsBetter,{count:false,min:false,percentile:[0.75,0.9]});addHistogram([driftTimeStats.renderingLengthError],histograms,'WebRTCRendering_rendering_length_error',percentage_smallerIsBetter);let smoothnessStats=getSmoothnessStats(driftTimeStats.driftTime);addHistogram([smoothnessStats.percentBadlyOutOfSync],histograms,'WebRTCRendering_percent_badly_out_of_sync',percentage_smallerIsBetter);addHistogram([smoothnessStats.percentOutOfSync],histograms,'WebRTCRendering_percent_out_of_sync',percentage_smallerIsBetter);addHistogram([smoothnessStats.smoothnessScore],histograms,'WebRTCRendering_smoothness_score',percentage_biggerIsBetter);addHistogram([smoothnessStats.framesOutOfSync],histograms,'WebRTCRendering_frames_out_of_sync',count_smallerIsBetter);addHistogram([smoothnessStats.framesSeverelyOutOfSync],histograms,'WebRTCRendering_frames_badly_out_of_sync',count_smallerIsBetter);}
+function getFrameDistribution(histograms,events){const cadence=tr.b.runLengthEncoding(events.map(e=>e.args[IDEAL_RENDER_INSTANT_NAME]));const frameHist=new tr.v.Histogram('WebRTCRendering_frame_distribution',count_smallerIsBetter,tr.v.HistogramBinBoundaries.createLinear(1,50,49));for(const ticks of cadence){frameHist.addSample(ticks.count);}
+frameHist.customizeSummaryOptions({percentile:[0.75,0.9]});histograms.addHistogram(frameHist);return frameHist;}
+function addFpsFromFrameDistribution(histograms,frameHist){let numberFrames=0;let numberVsyncs=0;for(let ticks=1;ticks<frameHist.allBins.length;++ticks){const count=frameHist.allBins[ticks].count;numberFrames+=count;numberVsyncs+=ticks*count;}
+let meanRatio=numberVsyncs/numberFrames;addHistogram([DISPLAY_HERTZ/meanRatio],histograms,'WebRTCRendering_fps',unitlessNumber_biggerIsBetter);}
+function frozenPenaltyWeight(numberFrozenFrames){const penalty={5:1,6:5,7:15,8:25};return penalty[numberFrozenFrames]||(8*(numberFrozenFrames-4));}
+function addFreezingScore(histograms,frameHist){let numberVsyncs=0;let freezingScore=0;let frozenFramesCount=0;for(let ticks=1;ticks<frameHist.allBins.length;++ticks){const count=frameHist.allBins[ticks].count;numberVsyncs+=ticks*count;if(ticks>=FROZEN_FRAME_VSYNC_COUNT_THRESHOLD){frozenFramesCount+=count*(ticks-1);freezingScore+=count*frozenPenaltyWeight(ticks-1);}}
+freezingScore=1-freezingScore/numberVsyncs;if(freezingScore<0){freezingScore=0;}
+addHistogram([frozenFramesCount],histograms,'WebRTCRendering_frozen_frames_count',count_smallerIsBetter);addHistogram([freezingScore],histograms,'WebRTCRendering_freezing_score',percentage_biggerIsBetter);}
+function getDriftStats(events){let driftTime=[];let discrepancy=[];let oldIdealRender=0;let expectedIdealRender=0;for(let event of events){let currentIdealRender=event.args[IDEAL_RENDER_INSTANT_NAME];expectedIdealRender+=VSYNC_DURATION_US;if(currentIdealRender===oldIdealRender){continue;}
+let actualRenderBegin=event.args[ACTUAL_RENDER_BEGIN_NAME];driftTime.push(actualRenderBegin-currentIdealRender);discrepancy.push(Math.abs(currentIdealRender-expectedIdealRender));expectedIdealRender=currentIdealRender;oldIdealRender=currentIdealRender;}
+let discrepancySum=tr.b.math.Statistics.sum(discrepancy)-discrepancy[0];let lastIdealRender=events[events.length-1].args[IDEAL_RENDER_INSTANT_NAME];let firstIdealRender=events[0].args[IDEAL_RENDER_INSTANT_NAME];let idealRenderSpan=lastIdealRender-firstIdealRender;let renderingLengthError=discrepancySum/idealRenderSpan;return{driftTime,renderingLengthError};}
+function getSmoothnessStats(driftTimes){let meanDriftTime=tr.b.math.Statistics.mean(driftTimes);let normDriftTimes=driftTimes.map(driftTime=>Math.abs(driftTime-meanDriftTime));let framesSeverelyOutOfSync=normDriftTimes.filter(driftTime=>driftTime>2*VSYNC_DURATION_US).length;let framesOutOfSync=normDriftTimes.filter(driftTime=>driftTime>VSYNC_DURATION_US).length;let percentBadlyOutOfSync=framesSeverelyOutOfSync/driftTimes.length;let percentOutOfSync=framesOutOfSync/driftTimes.length;let framesOutOfSyncOnlyOnce=framesOutOfSync-framesSeverelyOutOfSync;let smoothnessScore=1-(framesOutOfSyncOnlyOnce+
+SEVERITY*framesSeverelyOutOfSync)/driftTimes.length;if(smoothnessScore<0){smoothnessScore=0;}
+return{framesOutOfSync,framesSeverelyOutOfSync,percentBadlyOutOfSync,percentOutOfSync,smoothnessScore};}
+return{webrtcRenderingMetric,};});'use strict';Polymer({is:'tr-ui-a-alert-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],ready:function(){this.currentSelection_=undefined;this.$.table.tableColumns=[{title:'Label',value:function(row){return row.name;},width:'150px'},{title:'Value',width:'100%',value:function(row){return row.value;}}];this.$.table.showHeader=false;},get selection(){return this.currentSelection_;},set selection(selection){this.currentSelection_=selection;this.updateContents_();},getRowsForSingleAlert_:function(alert){var rows=[];for(var argName in alert.args){var argView=document.createElement('tr-ui-a-generic-object-view');argView.object=alert.args[argName];rows.push({name:argName,value:argView});}
+if(alert.associatedEvents.length){alert.associatedEvents.forEach(function(event,i){var linkEl=document.createElement('tr-ui-a-analysis-link');linkEl.setSelectionAndContent(new tr.model.EventSet(event),event.title);var valueString='';if(event instanceof tr.model.TimedEvent){valueString='took '+event.duration.toFixed(2)+'ms';}
+rows.push({name:linkEl,value:valueString});});}
 var descriptionEl=tr.ui.b.createDiv({textContent:alert.info.description,maxWidth:'300px'});rows.push({name:'Description',value:descriptionEl});if(alert.info.docLinks){alert.info.docLinks.forEach(function(linkObject){var linkEl=document.createElement('a');linkEl.target='_blank';linkEl.href=linkObject.href;Polymer.dom(linkEl).textContent=Polymer.dom(linkObject).textContent;rows.push({name:linkObject.label,value:linkEl});});}
-return rows;},getRowsForAlerts_:function(alerts){if(alerts.length==1){var rows=[{name:'Alert',value:tr.b.getOnlyElement(alerts).title}];var detailRows=this.getRowsForSingleAlert_(tr.b.getOnlyElement(alerts));rows.push.apply(rows,detailRows);return rows;}else{return alerts.map(function(alert){return{name:'Alert',value:alert.title,isExpanded:alerts.size<10,subRows:this.getRowsForSingleAlert_(alert)};},this);}},updateContents_:function(){if(this.currentSelection_===undefined){this.$.table.rows=[];this.$.table.rebuild();return;}
-var alerts=this.currentSelection_;this.$.table.tableRows=this.getRowsForAlerts_(alerts);this.$.table.rebuild();},get relatedEventsToHighlight(){if(!this.currentSelection_)
-return undefined;var result=new tr.model.EventSet();for(var event of this.currentSelection_)
-result.addEventSet(event.associatedEvents);return result;}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-alert-sub-view',tr.model.Alert,{multi:false,title:'Alert',});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-alert-sub-view',tr.model.Alert,{multi:true,title:'Alerts',});'use strict';tr.exportTo('tr.ui.analysis',function(){var NO_BREAK_SPACE=String.fromCharCode(160);var RIGHTWARDS_ARROW=String.fromCharCode(8594);var COLLATOR=new Intl.Collator(undefined,{numeric:true});function TitleColumn(title){this.title=title;}
-TitleColumn.prototype={supportsCellSelection:false,value:function(row){var formattedTitle=this.formatTitle(row);var contexts=row.contexts;if(contexts===undefined||contexts.length===0)
-return formattedTitle;var firstContext=contexts[0];var lastContext=contexts[contexts.length-1];var changeDefinedContextCount=0;for(var i=1;i<contexts.length;i++){if((contexts[i]===undefined)!==(contexts[i-1]===undefined))
-changeDefinedContextCount++;}
+return rows;},getRowsForAlerts_:function(alerts){if(alerts.length===1){var rows=[{name:'Alert',value:tr.b.getOnlyElement(alerts).title}];var detailRows=this.getRowsForSingleAlert_(tr.b.getOnlyElement(alerts));rows.push.apply(rows,detailRows);return rows;}
+return alerts.map(function(alert){return{name:'Alert',value:alert.title,isExpanded:alerts.size<10,subRows:this.getRowsForSingleAlert_(alert)};},this);},updateContents_:function(){if(this.currentSelection_===undefined){this.$.table.rows=[];this.$.table.rebuild();return;}
+var alerts=this.currentSelection_;this.$.table.tableRows=this.getRowsForAlerts_(alerts);this.$.table.rebuild();},get relatedEventsToHighlight(){if(!this.currentSelection_)return undefined;var result=new tr.model.EventSet();for(var event of this.currentSelection_){result.addEventSet(event.associatedEvents);}
+return result;}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-alert-sub-view',tr.model.Alert,{multi:false,title:'Alert',});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-alert-sub-view',tr.model.Alert,{multi:true,title:'Alerts',});'use strict';tr.exportTo('tr.ui.analysis',function(){var NO_BREAK_SPACE=String.fromCharCode(160);var RIGHTWARDS_ARROW=String.fromCharCode(8594);var COLLATOR=new Intl.Collator(undefined,{numeric:true});function TitleColumn(title){this.title=title;}
+TitleColumn.prototype={supportsCellSelection:false,value:function(row){var formattedTitle=this.formatTitle(row);var contexts=row.contexts;if(contexts===undefined||contexts.length===0){return formattedTitle;}
+var firstContext=contexts[0];var lastContext=contexts[contexts.length-1];var changeDefinedContextCount=0;for(var i=1;i<contexts.length;i++){if((contexts[i]===undefined)!==(contexts[i-1]===undefined)){changeDefinedContextCount++;}}
 var color=undefined;var prefix=undefined;if(!firstContext&&lastContext){color='red';prefix='+++';}else if(firstContext&&!lastContext){color='green';prefix='---';}
 if(changeDefinedContextCount>1){color='purple';}
-if(color===undefined&&prefix===undefined)
-return formattedTitle;var titleEl=document.createElement('span');if(prefix!==undefined){var prefixEl=tr.ui.b.createSpan({textContent:prefix});prefixEl.style.fontFamily='monospace';Polymer.dom(titleEl).appendChild(prefixEl);Polymer.dom(titleEl).appendChild(tr.ui.b.asHTMLOrTextNode(NO_BREAK_SPACE));}
-if(color!==undefined)
-titleEl.style.color=color;Polymer.dom(titleEl).appendChild(tr.ui.b.asHTMLOrTextNode(formattedTitle));return titleEl;},formatTitle:function(row){return row.title;},cmp:function(rowA,rowB){return COLLATOR.compare(rowA.title,rowB.title);}};function MemoryColumn(name,cellPath,aggregationMode){this.name=name;this.cellPath=cellPath;this.aggregationMode=aggregationMode;}
-MemoryColumn.fromRows=function(rows,cellKey,aggregationMode,rules){var cellNames=new Set();function gatherCellNames(rows){rows.forEach(function(row){if(row===undefined)
-return;var fieldCells=row[cellKey];if(fieldCells!==undefined){tr.b.iterItems(fieldCells,function(fieldName,fieldCell){if(fieldCell===undefined||fieldCell.fields===undefined)
-return;cellNames.add(fieldName);});}
-var subRows=row.subRows;if(subRows!==undefined)
-gatherCellNames(subRows);});}
-gatherCellNames(rows);var positions=[];cellNames.forEach(function(cellName){var cellPath=[cellKey,cellName];var matchingRule=MemoryColumn.findMatchingRule(cellName,rules);var constructor=matchingRule.columnConstructor;var column=new constructor(cellName,cellPath,aggregationMode);positions.push({importance:matchingRule.importance,column:column});});positions.sort(function(a,b){if(a.importance===b.importance)
-return COLLATOR.compare(a.column.name,b.column.name);return b.importance-a.importance;});return positions.map(function(position){return position.column});};MemoryColumn.spaceEqually=function(columns){var columnWidth=(100/columns.length).toFixed(3)+'%';columns.forEach(function(column){column.width=columnWidth;});};MemoryColumn.findMatchingRule=function(name,rules){for(var i=0;i<rules.length;i++){var rule=rules[i];if(MemoryColumn.nameMatchesCondition(name,rule.condition))
-return rule;}
-return undefined;};MemoryColumn.nameMatchesCondition=function(name,condition){if(condition===undefined)
-return true;if(typeof(condition)==='string')
-return name===condition;return condition.test(name);};MemoryColumn.AggregationMode={DIFF:0,MAX:1};MemoryColumn.SOME_TIMESTAMPS_INFO_QUANTIFIER='at some selected timestamps';MemoryColumn.prototype={get title(){return this.name;},cell:function(row){var cell=row;var cellPath=this.cellPath;for(var i=0;i<cellPath.length;i++){if(cell===undefined)
-return undefined;cell=cell[cellPath[i]];}
-return cell;},aggregateCells:function(row,subRows){},fields:function(row){var cell=this.cell(row);if(cell===undefined)
-return undefined;return cell.fields;},value:function(row){var fields=this.fields(row);if(this.hasAllRelevantFieldsUndefined(fields))
-return'';var contexts=row.contexts;var color=this.color(fields,contexts);var infos=[];this.addInfos(fields,contexts,infos);var formattedFields=this.formatFields(fields);if((color===undefined||formattedFields==='')&&infos.length===0)
-return formattedFields;var fieldEl=document.createElement('span');fieldEl.style.display='flex';fieldEl.style.alignItems='center';fieldEl.style.justifyContent='flex-end';Polymer.dom(fieldEl).appendChild(tr.ui.b.asHTMLOrTextNode(formattedFields));infos.forEach(function(info){var infoEl=document.createElement('span');infoEl.style.paddingLeft='4px';infoEl.style.cursor='help';infoEl.style.fontWeight='bold';Polymer.dom(infoEl).textContent=info.icon;if(info.color!==undefined)
-infoEl.style.color=info.color;infoEl.title=info.message;Polymer.dom(fieldEl).appendChild(infoEl);},this);if(color!==undefined)
-fieldEl.style.color=color;return fieldEl;},hasAllRelevantFieldsUndefined:function(fields){if(fields===undefined)
-return true;switch(this.aggregationMode){case MemoryColumn.AggregationMode.DIFF:return fields[0]===undefined&&fields[fields.length-1]===undefined;case MemoryColumn.AggregationMode.MAX:default:return fields.every(function(field){return field===undefined;});}},color:function(fields,contexts){return undefined;},formatFields:function(fields){if(fields.length===1)
-return this.formatSingleField(fields[0]);else
-return this.formatMultipleFields(fields);},formatSingleField:function(field){throw new Error('Not implemented');},formatMultipleFields:function(fields){switch(this.aggregationMode){case MemoryColumn.AggregationMode.DIFF:return this.formatMultipleFieldsDiff(fields[0],fields[fields.length-1]);case MemoryColumn.AggregationMode.MAX:return this.formatMultipleFieldsMax(fields);default:return tr.ui.b.createSpan({textContent:'(unsupported aggregation mode)',italic:true});}},formatMultipleFieldsDiff:function(firstField,lastField){throw new Error('Not implemented');},formatMultipleFieldsMax:function(fields){return this.formatSingleField(this.getMaxField(fields));},cmp:function(rowA,rowB){var fieldsA=this.fields(rowA);var fieldsB=this.fields(rowB);if(fieldsA!==undefined&&fieldsB!==undefined&&fieldsA.length!==fieldsB.length)
-throw new Error('Different number of fields');var undefinedA=this.hasAllRelevantFieldsUndefined(fieldsA);var undefinedB=this.hasAllRelevantFieldsUndefined(fieldsB);if(undefinedA&&undefinedB)
-return 0;if(undefinedA)
-return-1;if(undefinedB)
-return 1;return this.compareFields(fieldsA,fieldsB);},compareFields:function(fieldsA,fieldsB){if(fieldsA.length===1)
-return this.compareSingleFields(fieldsA[0],fieldsB[0]);else
-return this.compareMultipleFields(fieldsA,fieldsB);},compareSingleFields:function(fieldA,fieldB){throw new Error('Not implemented');},compareMultipleFields:function(fieldsA,fieldsB){switch(this.aggregationMode){case MemoryColumn.AggregationMode.DIFF:return this.compareMultipleFieldsDiff(fieldsA[0],fieldsA[fieldsA.length-1],fieldsB[0],fieldsB[fieldsB.length-1]);case MemoryColumn.AggregationMode.MAX:return this.compareMultipleFieldsMax(fieldsA,fieldsB);default:return 0;}},compareMultipleFieldsDiff:function(firstFieldA,lastFieldA,firstFieldB,lastFieldB){throw new Error('Not implemented');},compareMultipleFieldsMax:function(fieldsA,fieldsB){return this.compareSingleFields(this.getMaxField(fieldsA),this.getMaxField(fieldsB));},getMaxField:function(fields){return fields.reduce(function(accumulator,field){if(field===undefined)
-return accumulator;if(accumulator===undefined||this.compareSingleFields(field,accumulator)>0){return field;}
-return accumulator;}.bind(this),undefined);},addInfos:function(fields,contexts,infos){},getImportance:function(importanceRules){if(importanceRules.length===0)
-return 0;var matchingRule=MemoryColumn.findMatchingRule(this.name,importanceRules);if(matchingRule!==undefined)
-return matchingRule.importance;var minImportance=importanceRules[0].importance;for(var i=1;i<importanceRules.length;i++)
-minImportance=Math.min(minImportance,importanceRules[i].importance);return minImportance-1;}};function StringMemoryColumn(name,cellPath,aggregationMode){MemoryColumn.call(this,name,cellPath,aggregationMode);}
-StringMemoryColumn.prototype={__proto__:MemoryColumn.prototype,formatSingleField:function(string){return string;},formatMultipleFieldsDiff:function(firstString,lastString){if(firstString===undefined){var spanEl=tr.ui.b.createSpan({color:'red'});Polymer.dom(spanEl).appendChild(tr.ui.b.asHTMLOrTextNode('+'));Polymer.dom(spanEl).appendChild(tr.ui.b.asHTMLOrTextNode(this.formatSingleField(lastString)));return spanEl;}else if(lastString===undefined){var spanEl=tr.ui.b.createSpan({color:'green'});Polymer.dom(spanEl).appendChild(tr.ui.b.asHTMLOrTextNode('-'));Polymer.dom(spanEl).appendChild(tr.ui.b.asHTMLOrTextNode(this.formatSingleField(firstString)));return spanEl;}else if(firstString===lastString){return this.formatSingleField(firstString);}else{var spanEl=tr.ui.b.createSpan({color:'DarkOrange'});Polymer.dom(spanEl).appendChild(tr.ui.b.asHTMLOrTextNode(this.formatSingleField(firstString)));Polymer.dom(spanEl).appendChild(tr.ui.b.asHTMLOrTextNode(' '+RIGHTWARDS_ARROW+' '));Polymer.dom(spanEl).appendChild(tr.ui.b.asHTMLOrTextNode(this.formatSingleField(lastString)));return spanEl;}},compareSingleFields:function(stringA,stringB){return COLLATOR.compare(stringA,stringB);},compareMultipleFieldsDiff:function(firstStringA,lastStringA,firstStringB,lastStringB){if(firstStringA===undefined&&firstStringB!==undefined)
-return 1;if(firstStringA!==undefined&&firstStringB===undefined)
-return-1;if(firstStringA===undefined&&firstStringB===undefined)
-return this.compareSingleFields(lastStringA,lastStringB);if(lastStringA===undefined&&lastStringB!==undefined)
-return-1;if(lastStringA!==undefined&&lastStringB===undefined)
-return 1;if(lastStringA===undefined&&lastStringB===undefined)
-return this.compareSingleFields(firstStringB,firstStringA);var areStringsAEqual=firstStringA===lastStringA;var areStringsBEqual=firstStringB===lastStringB;if(areStringsAEqual&&areStringsBEqual)
-return 0;if(areStringsAEqual)
-return-1;if(areStringsBEqual)
-return 1;return 0;}};function NumericMemoryColumn(name,cellPath,aggregationMode){MemoryColumn.call(this,name,cellPath,aggregationMode);}
-NumericMemoryColumn.DIFF_EPSILON=0.0001;NumericMemoryColumn.prototype={__proto__:MemoryColumn.prototype,align:tr.ui.b.TableFormat.ColumnAlignment.RIGHT,aggregateCells:function(row,subRows){var subRowCells=subRows.map(this.cell,this);var hasDefinedSubRowNumeric=false;var timestampCount=undefined;subRowCells.forEach(function(subRowCell){if(subRowCell===undefined)
-return;var subRowNumerics=subRowCell.fields;if(subRowNumerics===undefined)
-return;if(timestampCount===undefined)
-timestampCount=subRowNumerics.length;else if(timestampCount!==subRowNumerics.length)
-throw new Error('Sub-rows have different numbers of timestamps');if(hasDefinedSubRowNumeric)
-return;hasDefinedSubRowNumeric=subRowNumerics.some(function(numeric){return numeric!==undefined;});});if(!hasDefinedSubRowNumeric)
-return;var cellPath=this.cellPath;var rowCell=row;for(var i=0;i<cellPath.length;i++){var nextStepName=cellPath[i];var nextStep=rowCell[nextStepName];if(nextStep===undefined){if(i<cellPath.length-1)
-nextStep={};else
-nextStep=new MemoryCell(undefined);rowCell[nextStepName]=nextStep;}
+if(color===undefined&&prefix===undefined){return formattedTitle;}
+var titleEl=document.createElement('span');if(prefix!==undefined){var prefixEl=tr.ui.b.createSpan({textContent:prefix});prefixEl.style.fontFamily='monospace';Polymer.dom(titleEl).appendChild(prefixEl);Polymer.dom(titleEl).appendChild(tr.ui.b.asHTMLOrTextNode(NO_BREAK_SPACE));}
+if(color!==undefined){titleEl.style.color=color;}
+Polymer.dom(titleEl).appendChild(tr.ui.b.asHTMLOrTextNode(formattedTitle));return titleEl;},formatTitle:function(row){return row.title;},cmp:function(rowA,rowB){return COLLATOR.compare(rowA.title,rowB.title);}};function MemoryColumn(name,cellPath,aggregationMode){this.name=name;this.cellPath=cellPath;this.shouldSetContextGroup=false;this.aggregationMode=aggregationMode;}
+MemoryColumn.fromRows=function(rows,config){var cellNames=new Set();function gatherCellNames(rows){rows.forEach(function(row){if(row===undefined)return;var fieldCells=row[config.cellKey];if(fieldCells!==undefined){for(var[fieldName,fieldCell]of Object.entries(fieldCells)){if(fieldCell===undefined||fieldCell.fields===undefined){continue;}
+cellNames.add(fieldName);}}
+var subRows=row.subRows;if(subRows!==undefined){gatherCellNames(subRows);}});}
+gatherCellNames(rows);var positions=[];cellNames.forEach(function(cellName){var cellPath=[config.cellKey,cellName];var matchingRule=MemoryColumn.findMatchingRule(cellName,config.rules);var constructor=matchingRule.columnConstructor;var column=new constructor(cellName,cellPath,config.aggregationMode);column.shouldSetContextGroup=!!config.shouldSetContextGroup;positions.push({importance:matchingRule.importance,column:column});});positions.sort(function(a,b){if(a.importance===b.importance){return COLLATOR.compare(a.column.name,b.column.name);}
+return b.importance-a.importance;});return positions.map(function(position){return position.column;});};MemoryColumn.spaceEqually=function(columns){var columnWidth=(100/columns.length).toFixed(3)+'%';columns.forEach(function(column){column.width=columnWidth;});};MemoryColumn.findMatchingRule=function(name,rules){for(var i=0;i<rules.length;i++){var rule=rules[i];if(MemoryColumn.nameMatchesCondition(name,rule.condition)){return rule;}}
+return undefined;};MemoryColumn.nameMatchesCondition=function(name,condition){if(condition===undefined)return true;if(typeof(condition)==='string')return name===condition;return condition.test(name);};MemoryColumn.AggregationMode={DIFF:0,MAX:1};MemoryColumn.SOME_TIMESTAMPS_INFO_QUANTIFIER='at some selected timestamps';MemoryColumn.prototype={get title(){return this.name;},cell:function(row){var cell=row;var cellPath=this.cellPath;for(var i=0;i<cellPath.length;i++){if(cell===undefined)return undefined;cell=cell[cellPath[i]];}
+return cell;},aggregateCells:function(row,subRows){},fields:function(row){var cell=this.cell(row);if(cell===undefined)return undefined;return cell.fields;},value:function(row){var fields=this.fields(row);if(this.hasAllRelevantFieldsUndefined(fields))return'';var contexts=row.contexts;var color=this.color(fields,contexts);var infos=[];this.addInfos(fields,contexts,infos);var formattedFields=this.formatFields(fields);if((color===undefined||formattedFields==='')&&infos.length===0){return formattedFields;}
+var fieldEl=document.createElement('span');fieldEl.style.display='flex';fieldEl.style.alignItems='center';fieldEl.style.justifyContent='flex-end';Polymer.dom(fieldEl).appendChild(tr.ui.b.asHTMLOrTextNode(formattedFields));infos.forEach(function(info){var infoEl=document.createElement('span');infoEl.style.paddingLeft='4px';infoEl.style.cursor='help';infoEl.style.fontWeight='bold';Polymer.dom(infoEl).textContent=info.icon;if(info.color!==undefined){infoEl.style.color=info.color;}
+infoEl.title=info.message;Polymer.dom(fieldEl).appendChild(infoEl);},this);if(color!==undefined){fieldEl.style.color=color;}
+return fieldEl;},hasAllRelevantFieldsUndefined:function(fields){if(fields===undefined)return true;switch(this.aggregationMode){case MemoryColumn.AggregationMode.DIFF:return fields[0]===undefined&&fields[fields.length-1]===undefined;case MemoryColumn.AggregationMode.MAX:default:return fields.every(function(field){return field===undefined;});}},color:function(fields,contexts){return undefined;},formatFields:function(fields){if(fields.length===1){return this.formatSingleField(fields[0]);}
+return this.formatMultipleFields(fields);},formatSingleField:function(field){throw new Error('Not implemented');},formatMultipleFields:function(fields){switch(this.aggregationMode){case MemoryColumn.AggregationMode.DIFF:return this.formatMultipleFieldsDiff(fields[0],fields[fields.length-1]);case MemoryColumn.AggregationMode.MAX:return this.formatMultipleFieldsMax(fields);default:return tr.ui.b.createSpan({textContent:'(unsupported aggregation mode)',italic:true});}},formatMultipleFieldsDiff:function(firstField,lastField){throw new Error('Not implemented');},formatMultipleFieldsMax:function(fields){return this.formatSingleField(this.getMaxField(fields));},cmp:function(rowA,rowB){var fieldsA=this.fields(rowA);var fieldsB=this.fields(rowB);if(fieldsA!==undefined&&fieldsB!==undefined&&fieldsA.length!==fieldsB.length){throw new Error('Different number of fields');}
+var undefinedA=this.hasAllRelevantFieldsUndefined(fieldsA);var undefinedB=this.hasAllRelevantFieldsUndefined(fieldsB);if(undefinedA&&undefinedB)return 0;if(undefinedA)return-1;if(undefinedB)return 1;return this.compareFields(fieldsA,fieldsB);},compareFields:function(fieldsA,fieldsB){if(fieldsA.length===1){return this.compareSingleFields(fieldsA[0],fieldsB[0]);}
+return this.compareMultipleFields(fieldsA,fieldsB);},compareSingleFields:function(fieldA,fieldB){throw new Error('Not implemented');},compareMultipleFields:function(fieldsA,fieldsB){switch(this.aggregationMode){case MemoryColumn.AggregationMode.DIFF:return this.compareMultipleFieldsDiff(fieldsA[0],fieldsA[fieldsA.length-1],fieldsB[0],fieldsB[fieldsB.length-1]);case MemoryColumn.AggregationMode.MAX:return this.compareMultipleFieldsMax(fieldsA,fieldsB);default:return 0;}},compareMultipleFieldsDiff:function(firstFieldA,lastFieldA,firstFieldB,lastFieldB){throw new Error('Not implemented');},compareMultipleFieldsMax:function(fieldsA,fieldsB){return this.compareSingleFields(this.getMaxField(fieldsA),this.getMaxField(fieldsB));},getMaxField:function(fields){return fields.reduce(function(accumulator,field){if(field===undefined){return accumulator;}
+if(accumulator===undefined||this.compareSingleFields(field,accumulator)>0){return field;}
+return accumulator;}.bind(this),undefined);},addInfos:function(fields,contexts,infos){},getImportance:function(importanceRules){if(importanceRules.length===0)return 0;var matchingRule=MemoryColumn.findMatchingRule(this.name,importanceRules);if(matchingRule!==undefined){return matchingRule.importance;}
+var minImportance=importanceRules[0].importance;for(var i=1;i<importanceRules.length;i++){minImportance=Math.min(minImportance,importanceRules[i].importance);}
+return minImportance-1;}};function StringMemoryColumn(name,cellPath,aggregationMode){MemoryColumn.call(this,name,cellPath,aggregationMode);}
+StringMemoryColumn.prototype={__proto__:MemoryColumn.prototype,formatSingleField:function(string){return string;},formatMultipleFieldsDiff:function(firstString,lastString){if(firstString===undefined){var spanEl=tr.ui.b.createSpan({color:'red'});Polymer.dom(spanEl).appendChild(tr.ui.b.asHTMLOrTextNode('+'));Polymer.dom(spanEl).appendChild(tr.ui.b.asHTMLOrTextNode(this.formatSingleField(lastString)));return spanEl;}else if(lastString===undefined){var spanEl=tr.ui.b.createSpan({color:'green'});Polymer.dom(spanEl).appendChild(tr.ui.b.asHTMLOrTextNode('-'));Polymer.dom(spanEl).appendChild(tr.ui.b.asHTMLOrTextNode(this.formatSingleField(firstString)));return spanEl;}else if(firstString===lastString){return this.formatSingleField(firstString);}
+var spanEl=tr.ui.b.createSpan({color:'DarkOrange'});Polymer.dom(spanEl).appendChild(tr.ui.b.asHTMLOrTextNode(this.formatSingleField(firstString)));Polymer.dom(spanEl).appendChild(tr.ui.b.asHTMLOrTextNode(' '+RIGHTWARDS_ARROW+' '));Polymer.dom(spanEl).appendChild(tr.ui.b.asHTMLOrTextNode(this.formatSingleField(lastString)));return spanEl;},compareSingleFields:function(stringA,stringB){return COLLATOR.compare(stringA,stringB);},compareMultipleFieldsDiff:function(firstStringA,lastStringA,firstStringB,lastStringB){if(firstStringA===undefined&&firstStringB!==undefined){return 1;}
+if(firstStringA!==undefined&&firstStringB===undefined){return-1;}
+if(firstStringA===undefined&&firstStringB===undefined){return this.compareSingleFields(lastStringA,lastStringB);}
+if(lastStringA===undefined&&lastStringB!==undefined){return-1;}
+if(lastStringA!==undefined&&lastStringB===undefined){return 1;}
+if(lastStringA===undefined&&lastStringB===undefined){return this.compareSingleFields(firstStringB,firstStringA);}
+var areStringsAEqual=firstStringA===lastStringA;var areStringsBEqual=firstStringB===lastStringB;if(areStringsAEqual&&areStringsBEqual)return 0;if(areStringsAEqual)return-1;if(areStringsBEqual)return 1;return 0;}};function NumericMemoryColumn(name,cellPath,aggregationMode){MemoryColumn.call(this,name,cellPath,aggregationMode);}
+NumericMemoryColumn.DIFF_EPSILON=0.0001;NumericMemoryColumn.prototype={__proto__:MemoryColumn.prototype,align:tr.ui.b.TableFormat.ColumnAlignment.RIGHT,aggregateCells:function(row,subRows){var subRowCells=subRows.map(this.cell,this);var hasDefinedSubRowNumeric=false;var timestampCount=undefined;subRowCells.forEach(function(subRowCell){if(subRowCell===undefined)return;var subRowNumerics=subRowCell.fields;if(subRowNumerics===undefined)return;if(timestampCount===undefined){timestampCount=subRowNumerics.length;}else if(timestampCount!==subRowNumerics.length){throw new Error('Sub-rows have different numbers of timestamps');}
+if(hasDefinedSubRowNumeric){return;}
+hasDefinedSubRowNumeric=subRowNumerics.some(function(numeric){return numeric!==undefined;});});if(!hasDefinedSubRowNumeric){return;}
+var cellPath=this.cellPath;var rowCell=row;for(var i=0;i<cellPath.length;i++){var nextStepName=cellPath[i];var nextStep=rowCell[nextStepName];if(nextStep===undefined){if(i<cellPath.length-1){nextStep={};}else{nextStep=new MemoryCell(undefined);}
+rowCell[nextStepName]=nextStep;}
 rowCell=nextStep;}
 if(rowCell.fields===undefined){rowCell.fields=new Array(timestampCount);}else if(rowCell.fields.length!==timestampCount){throw new Error('Row has a different number of timestamps than sub-rows');}
-for(var i=0;i<timestampCount;i++){if(rowCell.fields[i]!==undefined)
-continue;rowCell.fields[i]=tr.model.MemoryAllocatorDump.aggregateNumerics(subRowCells.map(function(subRowCell){if(subRowCell===undefined||subRowCell.fields===undefined)
-return undefined;return subRowCell.fields[i];}));}},formatSingleField:function(numeric){var formattingContext=this.getFormattingContext(numeric.unit);var config=formattingContext!==undefined?{context:formattingContext}:undefined;return tr.v.ui.createScalarSpan(numeric,config);},getFormattingContext:function(unit){return undefined;},formatMultipleFieldsDiff:function(firstNumeric,lastNumeric){return this.formatSingleField(this.getDiffField_(firstNumeric,lastNumeric));},compareSingleFields:function(numericA,numericB){return numericA.value-numericB.value;},compareMultipleFieldsDiff:function(firstNumericA,lastNumericA,firstNumericB,lastNumericB){return this.getDiffFieldValue_(firstNumericA,lastNumericA)-
-this.getDiffFieldValue_(firstNumericB,lastNumericB);},getDiffField_:function(firstNumeric,lastNumeric){var definedNumeric=firstNumeric||lastNumeric;return new tr.v.ScalarNumeric(definedNumeric.unit.correspondingDeltaUnit,this.getDiffFieldValue_(firstNumeric,lastNumeric));},getDiffFieldValue_:function(firstNumeric,lastNumeric){var firstValue=firstNumeric===undefined?0:firstNumeric.value;var lastValue=lastNumeric===undefined?0:lastNumeric.value;var diff=lastValue-firstValue;return Math.abs(diff)<NumericMemoryColumn.DIFF_EPSILON?0:diff;}};function MemoryCell(fields){this.fields=fields;}
-MemoryCell.extractFields=function(cell){if(cell===undefined)
-return undefined;return cell.fields;};var RECURSIVE_EXPANSION_MAX_VISIBLE_ROW_COUNT=10;function expandTableRowsRecursively(table){var currentLevelRows=table.tableRows;var totalVisibleRowCount=currentLevelRows.length;while(currentLevelRows.length>0){var nextLevelRowCount=0;currentLevelRows.forEach(function(currentLevelRow){var subRows=currentLevelRow.subRows;if(subRows===undefined||subRows.length===0)
-return;nextLevelRowCount+=subRows.length;});if(totalVisibleRowCount+nextLevelRowCount>RECURSIVE_EXPANSION_MAX_VISIBLE_ROW_COUNT){break;}
-var nextLevelRows=new Array(nextLevelRowCount);var nextLevelRowIndex=0;currentLevelRows.forEach(function(currentLevelRow){var subRows=currentLevelRow.subRows;if(subRows===undefined||subRows.length===0)
-return;table.setExpandedForTableRow(currentLevelRow,true);subRows.forEach(function(subRow){nextLevelRows[nextLevelRowIndex++]=subRow;});});totalVisibleRowCount+=nextLevelRowCount;currentLevelRows=nextLevelRows;}}
-function aggregateTableRowCellsRecursively(row,columns,opt_predicate){var subRows=row.subRows;if(subRows===undefined||subRows.length===0)
-return;subRows.forEach(function(subRow){aggregateTableRowCellsRecursively(subRow,columns,opt_predicate);});if(opt_predicate===undefined||opt_predicate(row.contexts))
-aggregateTableRowCells(row,subRows,columns);}
-function aggregateTableRowCells(row,subRows,columns){columns.forEach(function(column){if(!(column instanceof MemoryColumn))
-return;column.aggregateCells(row,subRows);});}
+for(var i=0;i<timestampCount;i++){if(rowCell.fields[i]!==undefined)continue;rowCell.fields[i]=tr.model.MemoryAllocatorDump.aggregateNumerics(subRowCells.map(function(subRowCell){if(subRowCell===undefined||subRowCell.fields===undefined){return undefined;}
+return subRowCell.fields[i];}));}},formatSingleField:function(numeric){return tr.v.ui.createScalarSpan(numeric,{context:this.getFormattingContext(numeric.unit),contextGroup:this.shouldSetContextGroup?this.name:undefined,inline:true,});},getFormattingContext:function(unit){return undefined;},formatMultipleFieldsDiff:function(firstNumeric,lastNumeric){return this.formatSingleField(this.getDiffField_(firstNumeric,lastNumeric));},compareSingleFields:function(numericA,numericB){return numericA.value-numericB.value;},compareMultipleFieldsDiff:function(firstNumericA,lastNumericA,firstNumericB,lastNumericB){return this.getDiffFieldValue_(firstNumericA,lastNumericA)-
+this.getDiffFieldValue_(firstNumericB,lastNumericB);},getDiffField_:function(firstNumeric,lastNumeric){var definedNumeric=firstNumeric||lastNumeric;return new tr.b.Scalar(definedNumeric.unit.correspondingDeltaUnit,this.getDiffFieldValue_(firstNumeric,lastNumeric));},getDiffFieldValue_:function(firstNumeric,lastNumeric){var firstValue=firstNumeric===undefined?0:firstNumeric.value;var lastValue=lastNumeric===undefined?0:lastNumeric.value;var diff=lastValue-firstValue;return Math.abs(diff)<NumericMemoryColumn.DIFF_EPSILON?0:diff;}};function MemoryCell(fields){this.fields=fields;}
+MemoryCell.extractFields=function(cell){if(cell===undefined)return undefined;return cell.fields;};var RECURSIVE_EXPANSION_MAX_VISIBLE_ROW_COUNT=10;function expandTableRowsRecursively(table){var currentLevelRows=table.tableRows;var totalVisibleRowCount=currentLevelRows.length;while(currentLevelRows.length>0){var nextLevelRowCount=0;currentLevelRows.forEach(function(currentLevelRow){var subRows=currentLevelRow.subRows;if(subRows===undefined||subRows.length===0)return;nextLevelRowCount+=subRows.length;});if(totalVisibleRowCount+nextLevelRowCount>RECURSIVE_EXPANSION_MAX_VISIBLE_ROW_COUNT){break;}
+var nextLevelRows=new Array(nextLevelRowCount);var nextLevelRowIndex=0;currentLevelRows.forEach(function(currentLevelRow){var subRows=currentLevelRow.subRows;if(subRows===undefined||subRows.length===0)return;table.setExpandedForTableRow(currentLevelRow,true);subRows.forEach(function(subRow){nextLevelRows[nextLevelRowIndex++]=subRow;});});totalVisibleRowCount+=nextLevelRowCount;currentLevelRows=nextLevelRows;}}
+function aggregateTableRowCellsRecursively(row,columns,opt_predicate){var subRows=row.subRows;if(subRows===undefined||subRows.length===0)return;subRows.forEach(function(subRow){aggregateTableRowCellsRecursively(subRow,columns,opt_predicate);});if(opt_predicate===undefined||opt_predicate(row.contexts)){aggregateTableRowCells(row,subRows,columns);}}
+function aggregateTableRowCells(row,subRows,columns){columns.forEach(function(column){if(!(column instanceof MemoryColumn))return;column.aggregateCells(row,subRows);});}
 function createCells(timeToValues,valueFieldsGetter,opt_this){opt_this=opt_this||this;var fieldNameToFields=tr.b.invertArrayOfDicts(timeToValues,valueFieldsGetter,opt_this);return tr.b.mapItems(fieldNameToFields,function(fieldName,fields){return new tr.ui.analysis.MemoryCell(fields);});}
 function createWarningInfo(message){return{message:message,icon:String.fromCharCode(9888),color:'red'};}
 function DetailsNumericMemoryColumn(name,cellPath,aggregationMode){NumericMemoryColumn.call(this,name,cellPath,aggregationMode);}
-DetailsNumericMemoryColumn.prototype={__proto__:NumericMemoryColumn.prototype,getFormattingContext:function(unit){if(unit.baseUnit===tr.b.Unit.byName.sizeInBytes)
-return{unitPrefix:tr.b.UnitScale.Binary.KIBI};return undefined;}};return{TitleColumn:TitleColumn,MemoryColumn:MemoryColumn,StringMemoryColumn:StringMemoryColumn,NumericMemoryColumn:NumericMemoryColumn,MemoryCell:MemoryCell,expandTableRowsRecursively:expandTableRowsRecursively,aggregateTableRowCellsRecursively:aggregateTableRowCellsRecursively,aggregateTableRowCells:aggregateTableRowCells,createCells:createCells,createWarningInfo:createWarningInfo,DetailsNumericMemoryColumn:DetailsNumericMemoryColumn};});'use strict';tr.exportTo('tr.ui.analysis',function(){var RebuildableBehavior={rebuild:function(){if(!this.paneDirty_){return;}
-this.paneDirty_=false;this.onRebuild_();},scheduleRebuild_:function(){if(this.paneDirty_)
-return;this.paneDirty_=true;tr.b.requestAnimationFrame(this.rebuild.bind(this));},onRebuild_:function(){}};return{RebuildableBehavior:RebuildableBehavior};});'use strict';tr.exportTo('tr.ui.analysis',function(){var StackedPaneImpl={set childPaneBuilder(childPaneBuilder){this.childPaneBuilder_=childPaneBuilder;this.dispatchEvent(new tr.b.Event('request-child-pane-change'));},get childPaneBuilder(){return this.childPaneBuilder_;},appended:function(){this.rebuild();}};var StackedPane=[tr.ui.analysis.RebuildableBehavior,StackedPaneImpl];return{StackedPane:StackedPane};});Polymer({is:'tr-ui-a-stacked-pane',behaviors:[tr.ui.analysis.StackedPane]});'use strict';tr.exportTo('tr.ui.analysis',function(){var ScalarNumeric=tr.v.ScalarNumeric;var sizeInBytes_smallerIsBetter=tr.b.Unit.byName.sizeInBytes_smallerIsBetter;var unitlessNumber_smallerIsBetter=tr.b.Unit.byName.unitlessNumber_smallerIsBetter;var MultiDimensionalViewBuilder=tr.b.MultiDimensionalViewBuilder;var TotalState=tr.b.MultiDimensionalViewNode.TotalState;var RowDimension={ROOT:-1,STACK_FRAME:0,OBJECT_TYPE:1};var LATIN_SMALL_LETTER_F_WITH_HOOK=String.fromCharCode(0x0192);var CIRCLED_LATIN_CAPITAL_LETTER_T=String.fromCharCode(0x24C9);function HeapDumpNodeTitleColumn(title){tr.ui.analysis.TitleColumn.call(this,title);}
-HeapDumpNodeTitleColumn.prototype={__proto__:tr.ui.analysis.TitleColumn.prototype,formatTitle:function(row){var title=row.title;var dimension=row.dimension;switch(dimension){case RowDimension.ROOT:return title;case RowDimension.STACK_FRAME:case RowDimension.OBJECT_TYPE:return this.formatSubRow_(title,dimension);default:throw new Error('Invalid row dimension: '+row.dimension);}},cmp:function(rowA,rowB){if(rowA.dimension!==rowB.dimension)
-return rowA.dimension-rowB.dimension;return tr.ui.analysis.TitleColumn.prototype.cmp.call(this,rowA,rowB);},formatSubRow_:function(title,dimension){var titleEl=document.createElement('span');var symbolEl=document.createElement('span');var symbolColorName;if(dimension===RowDimension.STACK_FRAME){Polymer.dom(symbolEl).textContent=LATIN_SMALL_LETTER_F_WITH_HOOK;symbolEl.title='Stack frame';symbolColorName='heap_dump_stack_frame';}else{Polymer.dom(symbolEl).textContent=CIRCLED_LATIN_CAPITAL_LETTER_T;symbolEl.title='Object type';symbolColorName='heap_dump_object_type';}
-symbolEl.style.color=tr.b.ColorScheme.getColorForReservedNameAsString(symbolColorName);symbolEl.style.paddingRight='4px';symbolEl.style.cursor='help';symbolEl.style.weight='bold';Polymer.dom(titleEl).appendChild(symbolEl);Polymer.dom(titleEl).appendChild(document.createTextNode(title));return titleEl;}};function AllocationCountColumn(name,cellPath,aggregationMode){tr.ui.analysis.DetailsNumericMemoryColumn.call(this,name,cellPath,aggregationMode);}
-AllocationCountColumn.prototype={__proto__:tr.ui.analysis.DetailsNumericMemoryColumn.prototype,getFormattingContext:function(unit){return{minimumFractionDigits:0};}};var COLUMN_RULES=[{condition:'Size',importance:3,columnConstructor:tr.ui.analysis.DetailsNumericMemoryColumn},{condition:'Count',importance:2,columnConstructor:AllocationCountColumn},{condition:'Average size per allocation',importance:1,columnConstructor:tr.ui.analysis.DetailsNumericMemoryColumn},{importance:0,columnConstructor:tr.ui.analysis.DetailsNumericMemoryColumn}];Polymer({is:'tr-ui-a-memory-dump-heap-details-pane',behaviors:[tr.ui.analysis.StackedPane],created:function(){this.heapDumps_=undefined;this.aggregationMode_=undefined;this.viewMode_=undefined;},ready:function(){this.$.table.selectionMode=tr.ui.b.TableFormat.SelectionMode.ROW;this.$.info_bar.message='Note: Values displayed in the heavy view '+'are lower bounds (except for the root).';Polymer.dom(this.$.view_mode_container).appendChild(tr.ui.b.createSelector(this,'viewMode','memoryDumpHeapDetailsPane.viewMode',MultiDimensionalViewBuilder.ViewType.TOP_DOWN_TREE_VIEW,[{label:'Top-down (Tree)',value:MultiDimensionalViewBuilder.ViewType.TOP_DOWN_TREE_VIEW},{label:'Top-down (Heavy)',value:MultiDimensionalViewBuilder.ViewType.TOP_DOWN_HEAVY_VIEW},{label:'Bottom-up (Heavy)',value:MultiDimensionalViewBuilder.ViewType.BOTTOM_UP_HEAVY_VIEW}]));},set heapDumps(heapDumps){this.heapDumps_=heapDumps;this.scheduleRebuild_();},get heapDumps(){return this.heapDumps_;},set aggregationMode(aggregationMode){this.aggregationMode_=aggregationMode;this.scheduleRebuild_();},get aggregationMode(){return this.aggregationMode_;},set viewMode(viewMode){this.viewMode_=viewMode;this.scheduleRebuild_();},get viewMode(){return this.viewMode_;},get heavyView(){switch(this.viewMode){case MultiDimensionalViewBuilder.ViewType.TOP_DOWN_HEAVY_VIEW:case MultiDimensionalViewBuilder.ViewType.BOTTOM_UP_HEAVY_VIEW:return true;default:return false;}},onRebuild_:function(){if(this.heapDumps_===undefined||this.heapDumps_.length===0){this.$.info_text.style.display='block';this.$.table.style.display='none';this.$.view_mode_container.style.display='none';this.$.info_bar.visible=false;this.$.table.clear();this.$.table.rebuild();return;}
-this.$.info_text.style.display='none';this.$.table.style.display='block';this.$.view_mode_container.style.display='block';this.$.info_bar.visible=this.heavyView;var stackFrameTrees=this.createStackFrameTrees_(this.heapDumps_);var rows=this.createRows_(stackFrameTrees);var columns=this.createColumns_(rows);this.$.table.tableRows=rows;this.$.table.tableColumns=columns;this.$.table.rebuild();tr.ui.analysis.expandTableRowsRecursively(this.$.table);},createStackFrameTrees_:function(heapDumps){return heapDumps.map(function(heapDump){if(heapDump===undefined)
-return undefined;var builder=new MultiDimensionalViewBuilder(2,2);heapDump.entries.forEach(function(entry){var leafStackFrame=entry.leafStackFrame;var stackTracePath=leafStackFrame===undefined?[]:leafStackFrame.getUserFriendlyStackTrace().reverse();var objectTypeName=entry.objectTypeName;var objectTypeNamePath=objectTypeName===undefined?[]:[objectTypeName];builder.addPath([stackTracePath,objectTypeNamePath],[entry.size,entry.count],MultiDimensionalViewBuilder.ValueKind.TOTAL);},this);return builder.buildView(this.viewMode);},this);},createRows_:function(stackFrameTrees){var definedHeapDump=tr.b.findFirstInArray(this.heapDumps);if(definedHeapDump===undefined)
-return[];var rootRowTitle=definedHeapDump.allocatorName;return[this.createHeapRowRecursively_(stackFrameTrees,RowDimension.ROOT,rootRowTitle)];},createHeapRowRecursively_:function(nodes,dimension,title){var cells=tr.ui.analysis.createCells(nodes,function(node){var size=node.values[0].total;var row={'Size':new ScalarNumeric(sizeInBytes_smallerIsBetter,size)};var countValue=node.values[1];if(countValue.totalState>=this.minDisplayedTotalState_){var count=countValue.total;row['Count']=new ScalarNumeric(unitlessNumber_smallerIsBetter,count);row['Average size per allocation']=new ScalarNumeric(sizeInBytes_smallerIsBetter,count===0?0:size/count);}
-return row;},this);var row={dimension:dimension,title:title,contexts:nodes,cells:cells};var stackFrameSubRows=this.createHeapDimensionSubRowsRecursively_(nodes,RowDimension.STACK_FRAME);var objectTypeSubRows=this.createHeapDimensionSubRowsRecursively_(nodes,RowDimension.OBJECT_TYPE);var subRows=stackFrameSubRows.concat(objectTypeSubRows);if(subRows.length>0)
-row.subRows=subRows;return row;},get minDisplayedTotalState_(){if(this.heavyView){return TotalState.LOWER_BOUND;}else{return TotalState.EXACT;}},createHeapDimensionSubRowsRecursively_:function(nodes,dimension){var dimensionGroupedChildNodes=tr.b.dictionaryValues(tr.b.invertArrayOfDicts(nodes,function(node){var childDict={};var displayedChildrenTotalSize=0;var displayedChildrenTotalCount=0;var hasDisplayedChildren=false;var allDisplayedChildrenHaveDisplayedCounts=true;for(var child of node.children[dimension].values()){if(child.values[0].totalState<this.minDisplayedTotalState_)
-continue;if(child.values[1].totalState<this.minDisplayedTotalState_)
-allDisplayedChildrenHaveDisplayedCounts=false;childDict[child.title[dimension]]=child;displayedChildrenTotalSize+=child.values[0].total;displayedChildrenTotalCount+=child.values[1].total;hasDisplayedChildren=true;}
-var nodeTotalSize=node.values[0].total;var nodeTotalCount=node.values[1].total;var hasUnclassifiedSizeOrCount=displayedChildrenTotalSize<nodeTotalSize||displayedChildrenTotalCount<nodeTotalCount;if(!this.heavyView&&hasUnclassifiedSizeOrCount&&hasDisplayedChildren){var otherTitle=node.title.slice();otherTitle[dimension]='<other>';childDict['<other>']={title:otherTitle,values:[{self:0,total:nodeTotalSize-displayedChildrenTotalSize,totalState:this.minDisplayedTotalState_},{self:0,total:nodeTotalCount-displayedChildrenTotalCount,totalState:allDisplayedChildrenHaveDisplayedCounts?this.minDisplayedTotalState_:TotalState.NOT_PROVIDED}],children:[new Map(),new Map()]};}
-return childDict;},this));return dimensionGroupedChildNodes.map(function(subRowNodes){var subRowTitle=tr.b.findFirstInArray(subRowNodes).title[dimension];return this.createHeapRowRecursively_(subRowNodes,dimension,subRowTitle);},this);},createColumns_:function(rows){var titleColumn=new HeapDumpNodeTitleColumn('Stack frame');titleColumn.width='500px';var numericColumns=tr.ui.analysis.MemoryColumn.fromRows(rows,'cells',this.aggregationMode_,COLUMN_RULES);tr.ui.analysis.MemoryColumn.spaceEqually(numericColumns);var columns=[titleColumn].concat(numericColumns);return columns;}});return{RowDimension:RowDimension,AllocationCountColumn:AllocationCountColumn};});'use strict';tr.exportTo('tr.ui.analysis',function(){var SUBALLOCATION_CONTEXT=true;var MemoryAllocatorDumpInfoType=tr.model.MemoryAllocatorDumpInfoType;var PROVIDED_SIZE_LESS_THAN_AGGREGATED_CHILDREN=MemoryAllocatorDumpInfoType.PROVIDED_SIZE_LESS_THAN_AGGREGATED_CHILDREN;var PROVIDED_SIZE_LESS_THAN_LARGEST_OWNER=MemoryAllocatorDumpInfoType.PROVIDED_SIZE_LESS_THAN_LARGEST_OWNER;var LEFTWARDS_OPEN_HEADED_ARROW=String.fromCharCode(0x21FD);var RIGHTWARDS_OPEN_HEADED_ARROW=String.fromCharCode(0x21FE);var EN_DASH=String.fromCharCode(0x2013);var CIRCLED_LATIN_SMALL_LETTER_I=String.fromCharCode(0x24D8);function AllocatorDumpNameColumn(){tr.ui.analysis.TitleColumn.call(this,'Component');}
-AllocatorDumpNameColumn.prototype={__proto__:tr.ui.analysis.TitleColumn.prototype,formatTitle:function(row){if(!row.suballocation)
-return row.title;return tr.ui.b.createSpan({textContent:row.title,italic:true,tooltip:row.fullNames===undefined?undefined:row.fullNames.join(', ')});}};function getAndUpdateEntry(map,name,createdCallback){var entry=map.get(name);if(entry===undefined){entry={count:0};createdCallback(entry);map.set(name,entry);}
+DetailsNumericMemoryColumn.prototype={__proto__:NumericMemoryColumn.prototype,getFormattingContext:function(unit){if(unit.baseUnit===tr.b.Unit.byName.sizeInBytes){return{unitPrefix:tr.b.UnitPrefixScale.BINARY.KIBI};}
+return undefined;}};return{TitleColumn,MemoryColumn,StringMemoryColumn,NumericMemoryColumn,MemoryCell,expandTableRowsRecursively,aggregateTableRowCellsRecursively,aggregateTableRowCells,createCells,createWarningInfo,DetailsNumericMemoryColumn,};});'use strict';tr.exportTo('tr.ui.analysis',function(){var LATIN_SMALL_LETTER_F_WITH_HOOK=String.fromCharCode(0x0192);var CIRCLED_LATIN_CAPITAL_LETTER_T=String.fromCharCode(0x24C9);var HeapDetailsRowDimension={ROOT:{},STACK_FRAME:{label:'Stack frame',symbol:LATIN_SMALL_LETTER_F_WITH_HOOK,color:'heap_dump_stack_frame'},OBJECT_TYPE:{label:'Object type',symbol:CIRCLED_LATIN_CAPITAL_LETTER_T,color:'heap_dump_object_type'}};function HeapDetailsTitleColumn(title){tr.ui.analysis.TitleColumn.call(this,title);}
+HeapDetailsTitleColumn.prototype={__proto__:tr.ui.analysis.TitleColumn.prototype,formatTitle:function(row){if(row.dimension===HeapDetailsRowDimension.ROOT){return row.title;}
+var symbolEl=document.createElement('span');Polymer.dom(symbolEl).textContent=row.dimension.symbol;symbolEl.title=row.dimension.label;symbolEl.style.color=tr.b.ColorScheme.getColorForReservedNameAsString(row.dimension.color);symbolEl.style.paddingRight='4px';symbolEl.style.cursor='help';symbolEl.style.fontWeight='bold';var titleEl=document.createElement('span');Polymer.dom(titleEl).appendChild(symbolEl);Polymer.dom(titleEl).appendChild(document.createTextNode(row.title));return titleEl;}};function AllocationCountColumn(name,cellPath,aggregationMode){tr.ui.analysis.DetailsNumericMemoryColumn.call(this,name,cellPath,aggregationMode);}
+AllocationCountColumn.prototype={__proto__:tr.ui.analysis.DetailsNumericMemoryColumn.prototype,getFormattingContext:function(unit){return{minimumFractionDigits:0};}};var HEAP_DETAILS_COLUMN_RULES=[{condition:'Size',importance:2,columnConstructor:tr.ui.analysis.DetailsNumericMemoryColumn},{condition:'Count',importance:1,columnConstructor:AllocationCountColumn},{importance:0,columnConstructor:tr.ui.analysis.DetailsNumericMemoryColumn}];return{HeapDetailsRowDimension,HeapDetailsTitleColumn,AllocationCountColumn,HEAP_DETAILS_COLUMN_RULES,};});'use strict';tr.exportTo('tr.ui.analysis',function(){var RebuildableBehavior={rebuild:function(){if(!this.paneDirty_){return;}
+this.paneDirty_=false;this.onRebuild_();},scheduleRebuild_:function(){if(this.paneDirty_)return;this.paneDirty_=true;tr.b.requestAnimationFrame(this.rebuild.bind(this));},onRebuild_:function(){}};return{RebuildableBehavior,};});'use strict';Polymer({is:'tr-ui-b-tab-view',properties:{label_:{type:String,value:()=>''},selectedSubView_:Object,subViews_:{type:Array,value:()=>[]},tabsHidden:{type:Boolean,value:false,observer:'tabsHiddenChanged_'}},ready(){this.$.tabs.addEventListener('keydown',this.onKeyDown_.bind(this),true);this.updateFocusability_();},set label(newLabel){this.set('label_',newLabel);},get tabs(){return this.get('subViews_');},get selectedSubView(){return this.selectedSubView_;},set selectedSubView(subView){if(subView===this.selectedSubView_)return;if(this.selectedSubView_){Polymer.dom(this.$.subView).removeChild(this.selectedSubView_);let oldInput=this.root.getElementById(this.computeRadioId_(this.selectedSubView_));if(oldInput){oldInput.checked=false;}}
+this.set('selectedSubView_',subView);if(subView){Polymer.dom(this.$.subView).appendChild(subView);let newInput=this.root.getElementById(this.computeRadioId_(subView));if(newInput){newInput.checked=true;}}
+this.fire('selected-tab-change');},clearSubViews(){this.splice('subViews_',0,this.subViews_.length);this.selectedSubView=undefined;this.updateFocusability_();},addSubView(subView){this.push('subViews_',subView);if(!this.selectedSubView_)this.selectedSubView=subView;this.updateFocusability_();},resetSubViews(subViews){this.splice('subViews_',0,this.subViews_.length);if(subViews.length){for(let subView of subViews){this.push('subViews_',subView);}
+this.selectedSubView=subViews[0];}else{this.selectedSubView=undefined;}
+this.updateFocusability_();},onTabChanged_(event){this.selectedSubView=event.model.item;},isChecked_(subView){return this.selectedSubView_===subView;},tabsHiddenChanged_(){this.updateFocusability_();},onKeyDown_(e){if(this.tabsHidden)return;let keyHandled=false;switch(e.keyCode){case 37:keyHandled=this.selectPreviousTabIfPossible();break;case 39:keyHandled=this.selectNextTabIfPossible();break;}
+if(!keyHandled)return;e.stopPropagation();e.preventDefault();},selectNextTabIfPossible(){return this.selectTabByOffsetIfPossible_(1);},selectPreviousTabIfPossible(){return this.selectTabByOffsetIfPossible_(-1);},selectTabByOffsetIfPossible_(offset){if(!this.selectedSubView_)return false;let currentIndex=this.subViews_.indexOf(this.selectedSubView_);let newSubView=this.tabs[currentIndex+offset];if(!newSubView)return false;this.selectedSubView=newSubView;return true;},shouldBeFocusable_(){return!this.tabsHidden&&this.subViews_.length>0;},updateFocusability_(){if(this.shouldBeFocusable_()){Polymer.dom(this.$.tabs).setAttribute('tabindex',0);}else{Polymer.dom(this.$.tabs).removeAttribute('tabindex');}},computeRadioId_(subView){return subView.tagName+'-'+subView.tabLabel.replace(/ /g,'-');}});'use strict';tr.exportTo('tr.ui.analysis',function(){var RESONABLE_NUMBER_OF_ROWS=200;var TabUiState={NO_LONG_TAIL:0,HIDING_LONG_TAIL:1,SHOWING_LONG_TAIL:2,};function EmptyFillerColumn(){}
+EmptyFillerColumn.prototype={title:'',value:function(){return'';},};Polymer({is:'tr-ui-a-memory-dump-heap-details-breakdown-view',behaviors:[tr.ui.analysis.RebuildableBehavior],created:function(){this.displayedNode_=undefined;this.dimensionToTab_=new Map();},ready:function(){this.scheduleRebuild_();this.root.addEventListener('keydown',this.onKeyDown_.bind(this),true);},get displayedNode(){return this.displayedNode_;},set displayedNode(node){this.displayedNode_=node;this.scheduleRebuild_();},get aggregationMode(){return this.aggregationMode_;},set aggregationMode(aggregationMode){this.aggregationMode_=aggregationMode;for(var tab of this.$.tabs.tabs){tab.aggregationMode=aggregationMode;}},onRebuild_:function(){var previouslySelectedTab=this.$.tabs.selectedSubView;var previouslySelectedTabFocused=false;var previouslySelectedDimension=undefined;if(previouslySelectedTab){previouslySelectedTabFocused=previouslySelectedTab.isFocused;previouslySelectedDimension=previouslySelectedTab.dimension;}
+for(var tab of this.$.tabs.tabs){tab.nodes=undefined;}
+this.$.tabs.clearSubViews();if(this.displayedNode_===undefined){this.$.tabs.label='No heap node provided.';return;}
+for(var[dimension,children]of this.displayedNode_.childNodes){if(!this.dimensionToTab_.has(dimension)){this.dimensionToTab_.set(dimension,document.createElement('tr-ui-a-memory-dump-heap-details-breakdown-view-tab'));}
+var tab=this.dimensionToTab_.get(dimension);tab.aggregationMode=this.aggregationMode_;tab.dimension=dimension;tab.nodes=children;this.$.tabs.addSubView(tab);tab.rebuild();if(dimension===previouslySelectedDimension){this.$.tabs.selectedSubView=tab;if(previouslySelectedTabFocused){tab.focus();}}}
+if(this.$.tabs.tabs.length>0){this.$.tabs.label='Break selected node further by:';}else{this.$.tabs.label='Selected node cannot be broken down any further.';}},onKeyDown_:function(keyEvent){if(!this.displayedNode_)return;var keyHandled=false;switch(keyEvent.keyCode){case 8:if(!this.displayedNode_.parentNode)break;var viewEvent=new tr.b.Event('enter-node');viewEvent.node=this.displayedNode_.parentNode;this.dispatchEvent(viewEvent);keyHandled=true;break;case 37:case 39:var wasFocused=this.$.tabs.selectedSubView.isFocused;keyHandled=keyEvent.keyCode===37?this.$.tabs.selectPreviousTabIfPossible():this.$.tabs.selectNextTabIfPossible();if(wasFocused&&keyHandled){this.$.tabs.selectedSubView.focus();}}
+if(!keyHandled)return;keyEvent.stopPropagation();keyEvent.preventDefault();}});Polymer({is:'tr-ui-a-memory-dump-heap-details-breakdown-view-tab',behaviors:[tr.ui.analysis.RebuildableBehavior],created:function(){this.dimension_=undefined;this.nodes_=undefined;this.aggregationMode_=undefined;this.displayLongTail_=false;},ready:function(){this.$.table.addEventListener('step-into',function(tableEvent){var viewEvent=new tr.b.Event('enter-node');viewEvent.node=tableEvent.tableRow;this.dispatchEvent(viewEvent);}.bind(this));},get displayLongTail(){return this.displayLongTail_;},set displayLongTail(newValue){if(this.displayLongTail===newValue)return;this.displayLongTail_=newValue;this.scheduleRebuild_();},get dimension(){return this.dimension_;},set dimension(dimension){this.dimension_=dimension;this.scheduleRebuild_();},get nodes(){return this.nodes_;},set nodes(nodes){this.nodes_=nodes;this.scheduleRebuild_();},get nodes(){return this.nodes_||[];},get dimensionLabel_(){if(this.dimension_===undefined)return'(undefined)';return this.dimension_.label;},get tabLabel(){var nodeCount=0;if(this.nodes_){nodeCount=this.nodes_.length;}
+return this.dimensionLabel_+' ('+nodeCount+')';},get tabIcon(){if(this.dimension_===undefined||this.dimension_===tr.ui.analysis.HeapDetailsRowDimension.ROOT){return undefined;}
+return{text:this.dimension_.symbol,style:'color: '+tr.b.ColorScheme.getColorForReservedNameAsString(this.dimension_.color)+';'};},get aggregationMode(){return this.aggregationMode_;},set aggregationMode(aggregationMode){this.aggregationMode_=aggregationMode;this.scheduleRebuild_();},focus:function(){this.$.table.focus();},blur:function(){this.$.table.blur();},get isFocused(){return this.$.table.isFocused;},onRebuild_:function(){this.$.table.selectionMode=tr.ui.b.TableFormat.SelectionMode.ROW;this.$.table.emptyValue='Cannot break down by '+
+this.dimensionLabel_.toLowerCase()+' any further.';var[state,rows]=this.getRows_();var total=this.nodes.length;var displayed=rows.length;var hidden=total-displayed;this.updateInfoBar_(state,[total,displayed,hidden]);this.$.table.tableRows=rows;this.$.table.tableColumns=this.createColumns_(rows);if(this.$.table.sortColumnIndex===undefined){this.$.table.sortColumnIndex=0;this.$.table.sortDescending=false;}
+this.$.table.rebuild();},createColumns_:function(rows){var titleColumn=new tr.ui.analysis.HeapDetailsTitleColumn(this.dimensionLabel_);titleColumn.width='400px';var numericColumns=tr.ui.analysis.MemoryColumn.fromRows(rows,{cellKey:'cells',aggregationMode:this.aggregationMode_,rules:tr.ui.analysis.HEAP_DETAILS_COLUMN_RULES,shouldSetContextGroup:true});if(numericColumns.length===0){numericColumns.push(new EmptyFillerColumn());}
+tr.ui.analysis.MemoryColumn.spaceEqually(numericColumns);var columns=[titleColumn].concat(numericColumns);return columns;},getRows_(){var rows=this.nodes;if(rows.length<=RESONABLE_NUMBER_OF_ROWS){return[TabUiState.NO_LONG_TAIL,rows];}else if(this.displayLongTail){return[TabUiState.SHOWING_LONG_TAIL,rows];}
+var absSize=row=>Math.max(row.cells.Size.fields[0].value);rows.sort((a,b)=>absSize(b)-absSize(a));rows=rows.slice(0,RESONABLE_NUMBER_OF_ROWS);return[TabUiState.HIDING_LONG_TAIL,rows];},updateInfoBar_(state,rowStats){if(state===TabUiState.SHOWING_LONG_TAIL){this.longTailVisibleInfoBar_(rowStats);}else if(state===TabUiState.HIDING_LONG_TAIL){this.longTailHiddenInfoBar_(rowStats);}else{this.hideInfoBar_();}},longTailVisibleInfoBar_:function(rowStats){var[total,visible,hidden]=rowStats;var couldHide=total-RESONABLE_NUMBER_OF_ROWS;this.$.info.message='Showing '+total+' rows. This may be slow.';this.$.info.removeAllButtons();var buttonText='Hide '+couldHide+' rows.';this.$.info.addButton(buttonText,()=>this.displayLongTail=false);this.$.info.visible=true;},longTailHiddenInfoBar_:function(rowStats){var[total,visible,hidden]=rowStats;this.$.info.message='Hiding the smallest '+hidden+' rows.';this.$.info.removeAllButtons();this.$.info.addButton('Show all.',()=>this.displayLongTail=true);this.$.info.visible=true;},hideInfoBar_:function(){this.$.info.visible=false;},});return{};});'use strict';tr.exportTo('tr.ui.analysis',function(){var DOWNWARDS_ARROW_WITH_TIP_RIGHTWARDS=String.fromCharCode(0x21B3);function HeapDetailsPathColumn(title){tr.ui.analysis.HeapDetailsTitleColumn.call(this,title);}
+HeapDetailsPathColumn.prototype={__proto__:tr.ui.analysis.HeapDetailsTitleColumn.prototype,formatTitle:function(row){var title=tr.ui.analysis.HeapDetailsTitleColumn.prototype.formatTitle.call(this,row);if(row.dimension===tr.ui.analysis.HeapDetailsRowDimension.ROOT){return title;}
+var arrowEl=document.createElement('span');Polymer.dom(arrowEl).textContent=DOWNWARDS_ARROW_WITH_TIP_RIGHTWARDS;arrowEl.style.paddingRight='2px';arrowEl.style.fontWeight='bold';arrowEl.style.color=tr.b.ColorScheme.getColorForReservedNameAsString('heap_dump_child_node_arrow');var rowEl=document.createElement('span');Polymer.dom(rowEl).appendChild(arrowEl);Polymer.dom(rowEl).appendChild(tr.ui.b.asHTMLOrTextNode(title));return rowEl;}};Polymer({is:'tr-ui-a-memory-dump-heap-details-path-view',behaviors:[tr.ui.analysis.RebuildableBehavior],created:function(){this.selectedNode_=undefined;this.aggregationMode_=undefined;},ready:function(){this.$.table.addEventListener('selection-changed',function(event){this.selectedNode_=this.$.table.selectedTableRow;this.didSelectedNodeChange_();}.bind(this));},didSelectedNodeChange_:function(){this.dispatchEvent(new tr.b.Event('selected-node-changed'));},get selectedNode(){return this.selectedNode_;},set selectedNode(node){this.selectedNode_=node;this.didSelectedNodeChange_();this.scheduleRebuild_();},get aggregationMode(){return this.aggregationMode_;},set aggregationMode(aggregationMode){this.aggregationMode_=aggregationMode;this.scheduleRebuild_();},onRebuild_:function(){if(this.selectedNode_===undefined){this.$.table.clear();return;}
+if(this.$.table.tableRows.includes(this.selectedNode_)){this.$.table.selectedTableRow=this.selectedNode_;return;}
+this.$.table.selectionMode=tr.ui.b.TableFormat.SelectionMode.ROW;this.$.table.userCanModifySortOrder=false;var rows=this.createRows_(this.selectedNode_);this.$.table.tableRows=rows;this.$.table.tableColumns=this.createColumns_(rows);this.$.table.selectedTableRow=rows[rows.length-1];},createRows_:function(node){var rows=[];while(node){rows.push(node);node=node.parentNode;}
+rows.reverse();return rows;},createColumns_:function(rows){var titleColumn=new HeapDetailsPathColumn('Current path');titleColumn.width='200px';var numericColumns=tr.ui.analysis.MemoryColumn.fromRows(rows,{cellKey:'cells',aggregationMode:this.aggregationMode_,rules:tr.ui.analysis.HEAP_DETAILS_COLUMN_RULES,shouldSetContextGroup:true});tr.ui.analysis.MemoryColumn.spaceEqually(numericColumns);return[titleColumn].concat(numericColumns);}});return{};});'use strict';tr.exportTo('tr.ui.analysis',function(){var StackedPaneImpl={set childPaneBuilder(childPaneBuilder){this.childPaneBuilder_=childPaneBuilder;this.dispatchEvent(new tr.b.Event('request-child-pane-change'));},get childPaneBuilder(){return this.childPaneBuilder_;},appended:function(){this.rebuild();}};var StackedPane=[tr.ui.analysis.RebuildableBehavior,StackedPaneImpl];return{StackedPane,};});Polymer({is:'tr-ui-a-stacked-pane',behaviors:[tr.ui.analysis.StackedPane]});'use strict';tr.exportTo('tr.ui.analysis',function(){var Scalar=tr.b.Scalar;var sizeInBytes_smallerIsBetter=tr.b.Unit.byName.sizeInBytes_smallerIsBetter;var count_smallerIsBetter=tr.b.Unit.byName.count_smallerIsBetter;var MultiDimensionalViewBuilder=tr.b.MultiDimensionalViewBuilder;var TotalState=tr.b.MultiDimensionalViewNode.TotalState;function HeapDumpTreeNode(stackFrameNodes,dimension,title,heavyView,parentNode){this.dimension=dimension;this.title=title;this.parentNode=parentNode;this.heavyView_=heavyView;this.stackFrameNodes_=stackFrameNodes;this.lazyCells_=undefined;this.lazyChildNodes_=undefined;}
+HeapDumpTreeNode.prototype={get minDisplayedTotalState_(){if(this.heavyView_){return TotalState.LOWER_BOUND;}
+return TotalState.EXACT;},get childNodes(){if(!this.lazyChildNodes_){this.lazyChildNodes_=new Map();this.addDimensionChildNodes_(tr.ui.analysis.HeapDetailsRowDimension.STACK_FRAME,0);this.addDimensionChildNodes_(tr.ui.analysis.HeapDetailsRowDimension.OBJECT_TYPE,1);this.releaseStackFrameNodesIfPossible_();}
+return this.lazyChildNodes_;},get cells(){if(!this.lazyCells_){this.addCells_();this.releaseStackFrameNodesIfPossible_();}
+return this.lazyCells_;},releaseStackFrameNodesIfPossible_:function(){if(this.lazyCells_&&this.lazyChildNodes_){this.stackFrameNodes_=undefined;}},addDimensionChildNodes_:function(dimension,dimensionIndex){var dimensionChildTitleToStackFrameNodes=tr.b.invertArrayOfDicts(this.stackFrameNodes_,node=>this.convertStackFrameNodeDimensionToChildDict_(node,dimensionIndex));var dimensionChildNodes=[];for(var[childTitle,childStackFrameNodes]of
+Object.entries(dimensionChildTitleToStackFrameNodes)){dimensionChildNodes.push(new HeapDumpTreeNode(childStackFrameNodes,dimension,childTitle,this.heavyView_,this));}
+this.lazyChildNodes_.set(dimension,dimensionChildNodes);},convertStackFrameNodeDimensionToChildDict_:function(stackFrameNode,dimensionIndex){var childDict={};var displayedChildrenTotalSize=0;var displayedChildrenTotalCount=0;var hasDisplayedChildren=false;var allDisplayedChildrenHaveDisplayedCounts=true;for(var child of stackFrameNode.children[dimensionIndex].values()){if(child.values[0].totalState<this.minDisplayedTotalState_){continue;}
+if(child.values[1].totalState<this.minDisplayedTotalState_){allDisplayedChildrenHaveDisplayedCounts=false;}
+childDict[child.title[dimensionIndex]]=child;displayedChildrenTotalSize+=child.values[0].total;displayedChildrenTotalCount+=child.values[1].total;hasDisplayedChildren=true;}
+var nodeTotalSize=stackFrameNode.values[0].total;var nodeTotalCount=stackFrameNode.values[1].total;var hasUnclassifiedSizeOrCount=displayedChildrenTotalSize<nodeTotalSize||displayedChildrenTotalCount<nodeTotalCount;if(!this.heavyView_&&hasUnclassifiedSizeOrCount&&hasDisplayedChildren){var otherTitle=stackFrameNode.title.slice();otherTitle[dimensionIndex]='<other>';var otherNode=new tr.b.MultiDimensionalViewNode(otherTitle,2);childDict[otherTitle[dimensionIndex]]=otherNode;otherNode.values[0].total=nodeTotalSize-displayedChildrenTotalSize;otherNode.values[0].totalState=this.minDisplayedTotalState_;otherNode.values[1].total=nodeTotalCount-displayedChildrenTotalCount;otherNode.values[1].totalState=allDisplayedChildrenHaveDisplayedCounts?this.minDisplayedTotalState_:TotalState.NOT_PROVIDED;}
+return childDict;},addCells_:function(){this.lazyCells_=tr.ui.analysis.createCells(this.stackFrameNodes_,function(stackFrameNode){var size=stackFrameNode.values[0].total;var numerics={'Size':new Scalar(sizeInBytes_smallerIsBetter,size)};var countValue=stackFrameNode.values[1];if(countValue.totalState>=this.minDisplayedTotalState_){var count=countValue.total;numerics['Count']=new Scalar(count_smallerIsBetter,count);}
+return numerics;},this);}};Polymer({is:'tr-ui-a-memory-dump-heap-details-pane',behaviors:[tr.ui.analysis.StackedPane],created:function(){this.heapDumps_=undefined;this.viewMode_=undefined;this.aggregationMode_=undefined;this.cachedBuilders_=new Map();},ready:function(){this.$.info_bar.message='Note: Values displayed in the heavy view '+'are lower bounds (except for the root).';Polymer.dom(this.$.view_mode_container).appendChild(tr.ui.b.createSelector(this,'viewMode','memoryDumpHeapDetailsPane.viewMode',MultiDimensionalViewBuilder.ViewType.TOP_DOWN_TREE_VIEW,[{label:'Top-down (Tree)',value:MultiDimensionalViewBuilder.ViewType.TOP_DOWN_TREE_VIEW},{label:'Top-down (Heavy)',value:MultiDimensionalViewBuilder.ViewType.TOP_DOWN_HEAVY_VIEW},{label:'Bottom-up (Heavy)',value:MultiDimensionalViewBuilder.ViewType.BOTTOM_UP_HEAVY_VIEW}]));this.$.drag_handle.target=this.$.path_view;this.$.drag_handle.horizontal=false;this.$.path_view.addEventListener('selected-node-changed',(function(e){this.$.breakdown_view.displayedNode=this.$.path_view.selectedNode;}).bind(this));this.$.breakdown_view.addEventListener('enter-node',(function(e){this.$.path_view.selectedNode=e.node;}).bind(this));},set heapDumps(heapDumps){this.heapDumps_=heapDumps;this.scheduleRebuild_();},get heapDumps(){return this.heapDumps_;},set aggregationMode(aggregationMode){this.aggregationMode_=aggregationMode;this.$.path_view.aggregationMode=aggregationMode;this.$.breakdown_view.aggregationMode=aggregationMode;},get aggregationMode(){return this.aggregationMode_;},set viewMode(viewMode){this.viewMode_=viewMode;this.scheduleRebuild_();},get viewMode(){return this.viewMode_;},get heavyView(){switch(this.viewMode){case MultiDimensionalViewBuilder.ViewType.TOP_DOWN_HEAVY_VIEW:case MultiDimensionalViewBuilder.ViewType.BOTTOM_UP_HEAVY_VIEW:return true;default:return false;}},onRebuild_:function(){if(this.heapDumps_===undefined||this.heapDumps_.length===0){this.$.info_text.style.display='block';this.$.split_view.style.display='none';this.$.view_mode_container.style.display='none';this.$.info_bar.hidden=true;this.$.path_view.selectedNode=undefined;return;}
+this.$.info_text.style.display='none';this.$.split_view.style.display='flex';this.$.view_mode_container.style.display='block';this.$.info_bar.hidden=!this.heavyView;this.$.path_view.selectedNode=this.createHeapTree_();this.$.path_view.rebuild();this.$.breakdown_view.rebuild();},createHeapTree_:function(){var definedHeapDump=tr.b.findFirstInArray(this.heapDumps_);if(definedHeapDump===undefined)return undefined;var rootRowTitle=definedHeapDump.allocatorName;var stackFrameTrees=this.createStackFrameTrees_(this.heapDumps_);return new HeapDumpTreeNode(stackFrameTrees,tr.ui.analysis.HeapDetailsRowDimension.ROOT,rootRowTitle,this.heavyView);},createStackFrameTrees_:function(heapDumps){var builders=heapDumps.map(heapDump=>this.createBuilder_(heapDump));var views=builders.map(builder=>{if(builder===undefined)return undefined;return builder.buildView(this.viewMode);});return views;},createBuilder_:function(heapDump){if(heapDump===undefined)return undefined;if(this.cachedBuilders_.has(heapDump)){return this.cachedBuilders_.get(heapDump);}
+var dimensions=2;var valueCount=2;var builder=new MultiDimensionalViewBuilder(dimensions,valueCount);for(var entry of heapDump.entries){var leafStackFrame=entry.leafStackFrame;var stackTracePath=leafStackFrame===undefined?[]:leafStackFrame.getUserFriendlyStackTrace().reverse();var objectTypeName=entry.objectTypeName;var objectTypeNamePath=objectTypeName===undefined?[]:[objectTypeName];builder.addPath([stackTracePath,objectTypeNamePath],[entry.size,entry.count],MultiDimensionalViewBuilder.ValueKind.TOTAL);}
+this.cachedBuilders_.set(heapDump,builder);return builder;},});return{};});'use strict';tr.exportTo('tr.ui.analysis',function(){var URL_TO_SIZE_VS_EFFECTIVE_SIZE='https://chromium.googlesource.com/chromium/src/+/master/docs/memory-infra/README.md#effective_size-vs_size';var SUBALLOCATION_CONTEXT=true;var MemoryAllocatorDumpInfoType=tr.model.MemoryAllocatorDumpInfoType;var PROVIDED_SIZE_LESS_THAN_AGGREGATED_CHILDREN=MemoryAllocatorDumpInfoType.PROVIDED_SIZE_LESS_THAN_AGGREGATED_CHILDREN;var PROVIDED_SIZE_LESS_THAN_LARGEST_OWNER=MemoryAllocatorDumpInfoType.PROVIDED_SIZE_LESS_THAN_LARGEST_OWNER;var LEFTWARDS_OPEN_HEADED_ARROW=String.fromCharCode(0x21FD);var RIGHTWARDS_OPEN_HEADED_ARROW=String.fromCharCode(0x21FE);var EN_DASH=String.fromCharCode(0x2013);var CIRCLED_LATIN_SMALL_LETTER_I=String.fromCharCode(0x24D8);function AllocatorDumpNameColumn(){tr.ui.analysis.TitleColumn.call(this,'Component');}
+AllocatorDumpNameColumn.prototype={__proto__:tr.ui.analysis.TitleColumn.prototype,formatTitle:function(row){if(!row.suballocation){return row.title;}
+return tr.ui.b.createSpan({textContent:row.title,italic:true,tooltip:row.fullNames===undefined?undefined:row.fullNames.join(', ')});}};function getAndUpdateEntry(map,name,createdCallback){var entry=map.get(name);if(entry===undefined){entry={count:0};createdCallback(entry);map.set(name,entry);}
 entry.count++;return entry;}
 function SizeInfoMessageBuilder(){this.parts_=[];this.indent_=0;}
-SizeInfoMessageBuilder.prototype={append:function(){this.parts_.push.apply(this.parts_,Array.prototype.slice.apply(arguments));},appendMap:function(map,hasPluralSuffix,emptyText,itemCallback,opt_this){opt_this=opt_this||this;if(map.size===0){if(emptyText)
-this.append(emptyText);}else if(map.size===1){this.parts_.push(' ');var key=map.keys().next().value;itemCallback.call(opt_this,key,map.get(key));}else{if(hasPluralSuffix)
-this.parts_.push('s');this.parts_.push(':');this.indent_++;for(var key of map.keys()){this.parts_.push('\n',' '.repeat(3*(this.indent_-1)),' - ');itemCallback.call(opt_this,key,map.get(key));}
-this.indent_--;}},appendImportanceRange:function(range){this.append(' (importance: ');if(range.min===range.max)
-this.append(range.min);else
-this.append(range.min,EN_DASH,range.max);this.append(')');},appendSizeIfDefined:function(size){if(size!==undefined)
-this.append(' (',tr.b.Unit.byName.sizeInBytes.format(size),')');},appendSomeTimestampsQuantifier:function(){this.append(' ',tr.ui.analysis.MemoryColumn.SOME_TIMESTAMPS_INFO_QUANTIFIER);},build:function(){return this.parts_.join('');}};function EffectiveSizeColumn(name,cellPath,aggregationMode){tr.ui.analysis.DetailsNumericMemoryColumn.call(this,name,cellPath,aggregationMode);}
-EffectiveSizeColumn.prototype={__proto__:tr.ui.analysis.DetailsNumericMemoryColumn.prototype,addInfos:function(numerics,memoryAllocatorDumps,infos){if(memoryAllocatorDumps===undefined)
-return;var ownerNameToEntry=new Map();var ownedNameToEntry=new Map();for(var i=0;i<numerics.length;i++){if(numerics[i]===undefined)
-continue;var dump=memoryAllocatorDumps[i];if(dump===SUBALLOCATION_CONTEXT)
-return;dump.ownedBy.forEach(function(ownerLink){var ownerDump=ownerLink.source;this.getAndUpdateOwnershipEntry_(ownerNameToEntry,ownerDump,ownerLink);},this);var ownedLink=dump.owns;if(ownedLink!==undefined){var ownedDump=ownedLink.target;var ownedEntry=this.getAndUpdateOwnershipEntry_(ownedNameToEntry,ownedDump,ownedLink,true);var sharerNameToEntry=ownedEntry.sharerNameToEntry;ownedDump.ownedBy.forEach(function(sharerLink){var sharerDump=sharerLink.source;if(sharerDump===dump)
-return;this.getAndUpdateOwnershipEntry_(sharerNameToEntry,sharerDump,sharerLink);},this);}}
-if(ownerNameToEntry.size>0){var messageBuilder=new SizeInfoMessageBuilder();messageBuilder.append('shared by');messageBuilder.appendMap(ownerNameToEntry,false,undefined,function(ownerName,ownerEntry){messageBuilder.append(ownerName);if(ownerEntry.count<numerics.length)
-messageBuilder.appendSomeTimestampsQuantifier();messageBuilder.appendImportanceRange(ownerEntry.importanceRange);},this);infos.push({message:messageBuilder.build(),icon:LEFTWARDS_OPEN_HEADED_ARROW,color:'green'});}
-if(ownedNameToEntry.size>0){var messageBuilder=new SizeInfoMessageBuilder();messageBuilder.append('shares');messageBuilder.appendMap(ownedNameToEntry,false,undefined,function(ownedName,ownedEntry){messageBuilder.append(ownedName);var ownedCount=ownedEntry.count;if(ownedCount<numerics.length)
-messageBuilder.appendSomeTimestampsQuantifier();messageBuilder.appendImportanceRange(ownedEntry.importanceRange);messageBuilder.append(' with');messageBuilder.appendMap(ownedEntry.sharerNameToEntry,false,' no other dumps',function(sharerName,sharerEntry){messageBuilder.append(sharerName);if(sharerEntry.count<ownedCount)
-messageBuilder.appendSomeTimestampsQuantifier();messageBuilder.appendImportanceRange(sharerEntry.importanceRange);},this);},this);infos.push({message:messageBuilder.build(),icon:RIGHTWARDS_OPEN_HEADED_ARROW,color:'green'});}},getAndUpdateOwnershipEntry_:function(map,dump,link,opt_withSharerNameToEntry){var entry=getAndUpdateEntry(map,dump.quantifiedName,function(newEntry){newEntry.importanceRange=new tr.b.Range();if(opt_withSharerNameToEntry)
-newEntry.sharerNameToEntry=new Map();});entry.importanceRange.addValue(link.importance||0);return entry;}};function SizeColumn(name,cellPath,aggregationMode){tr.ui.analysis.DetailsNumericMemoryColumn.call(this,name,cellPath,aggregationMode);}
-SizeColumn.prototype={__proto__:tr.ui.analysis.DetailsNumericMemoryColumn.prototype,addInfos:function(numerics,memoryAllocatorDumps,infos){if(memoryAllocatorDumps===undefined)
-return;this.addOverlapInfo_(numerics,memoryAllocatorDumps,infos);this.addProvidedSizeWarningInfos_(numerics,memoryAllocatorDumps,infos);},addOverlapInfo_:function(numerics,memoryAllocatorDumps,infos){var siblingNameToEntry=new Map();for(var i=0;i<numerics.length;i++){if(numerics[i]===undefined)
-continue;var dump=memoryAllocatorDumps[i];if(dump===SUBALLOCATION_CONTEXT)
-return;var ownedBySiblingSizes=dump.ownedBySiblingSizes;for(var siblingDump of ownedBySiblingSizes.keys()){var siblingName=siblingDump.name;getAndUpdateEntry(siblingNameToEntry,siblingName,function(newEntry){if(numerics.length===1)
-newEntry.size=ownedBySiblingSizes.get(siblingDump);});}}
-if(siblingNameToEntry.size>0){var messageBuilder=new SizeInfoMessageBuilder();messageBuilder.append('overlaps with its sibling');messageBuilder.appendMap(siblingNameToEntry,true,undefined,function(siblingName,siblingEntry){messageBuilder.append('\'',siblingName,'\'');messageBuilder.appendSizeIfDefined(siblingEntry.size);if(siblingEntry.count<numerics.length)
-messageBuilder.appendSomeTimestampsQuantifier();},this);infos.push({message:messageBuilder.build(),icon:CIRCLED_LATIN_SMALL_LETTER_I,color:'blue'});}},addProvidedSizeWarningInfos_:function(numerics,memoryAllocatorDumps,infos){var infoTypeToEntry=new Map();for(var i=0;i<numerics.length;i++){if(numerics[i]===undefined)
-continue;var dump=memoryAllocatorDumps[i];if(dump===SUBALLOCATION_CONTEXT)
-return;dump.infos.forEach(function(dumpInfo){getAndUpdateEntry(infoTypeToEntry,dumpInfo.type,function(newEntry){if(numerics.length===1){newEntry.providedSize=dumpInfo.providedSize;newEntry.dependencySize=dumpInfo.dependencySize;}});});}
+SizeInfoMessageBuilder.prototype={append:function(){this.parts_.push.apply(this.parts_,Array.prototype.slice.apply(arguments));},appendMap:function(map,hasPluralSuffix,emptyText,itemCallback,opt_this){opt_this=opt_this||this;if(map.size===0){if(emptyText){this.append(emptyText);}}else if(map.size===1){this.parts_.push(' ');var key=map.keys().next().value;itemCallback.call(opt_this,key,map.get(key));}else{if(hasPluralSuffix){this.parts_.push('s');}
+this.parts_.push(':');this.indent_++;for(var key of map.keys()){this.parts_.push('\n',' '.repeat(3*(this.indent_-1)),' - ');itemCallback.call(opt_this,key,map.get(key));}
+this.indent_--;}},appendImportanceRange:function(range){this.append(' (importance: ');if(range.min===range.max){this.append(range.min);}else{this.append(range.min,EN_DASH,range.max);}
+this.append(')');},appendSizeIfDefined:function(size){if(size!==undefined){this.append(' (',tr.b.Unit.byName.sizeInBytes.format(size),')');}},appendSomeTimestampsQuantifier:function(){this.append(' ',tr.ui.analysis.MemoryColumn.SOME_TIMESTAMPS_INFO_QUANTIFIER);},build:function(){return this.parts_.join('');}};function EffectiveSizeColumn(name,cellPath,aggregationMode){tr.ui.analysis.DetailsNumericMemoryColumn.call(this,name,cellPath,aggregationMode);}
+EffectiveSizeColumn.prototype={__proto__:tr.ui.analysis.DetailsNumericMemoryColumn.prototype,get title(){return tr.ui.b.createLink({textContent:this.name,tooltip:'Memory used by this component',href:URL_TO_SIZE_VS_EFFECTIVE_SIZE});},addInfos:function(numerics,memoryAllocatorDumps,infos){if(memoryAllocatorDumps===undefined)return;var ownerNameToEntry=new Map();var ownedNameToEntry=new Map();for(var i=0;i<numerics.length;i++){if(numerics[i]===undefined)continue;var dump=memoryAllocatorDumps[i];if(dump===SUBALLOCATION_CONTEXT){return;}
+dump.ownedBy.forEach(function(ownerLink){var ownerDump=ownerLink.source;this.getAndUpdateOwnershipEntry_(ownerNameToEntry,ownerDump,ownerLink);},this);var ownedLink=dump.owns;if(ownedLink!==undefined){var ownedDump=ownedLink.target;var ownedEntry=this.getAndUpdateOwnershipEntry_(ownedNameToEntry,ownedDump,ownedLink,true);var sharerNameToEntry=ownedEntry.sharerNameToEntry;ownedDump.ownedBy.forEach(function(sharerLink){var sharerDump=sharerLink.source;if(sharerDump===dump)return;this.getAndUpdateOwnershipEntry_(sharerNameToEntry,sharerDump,sharerLink);},this);}}
+if(ownerNameToEntry.size>0){var messageBuilder=new SizeInfoMessageBuilder();messageBuilder.append('shared by');messageBuilder.appendMap(ownerNameToEntry,false,undefined,function(ownerName,ownerEntry){messageBuilder.append(ownerName);if(ownerEntry.count<numerics.length){messageBuilder.appendSomeTimestampsQuantifier();}
+messageBuilder.appendImportanceRange(ownerEntry.importanceRange);},this);infos.push({message:messageBuilder.build(),icon:LEFTWARDS_OPEN_HEADED_ARROW,color:'green'});}
+if(ownedNameToEntry.size>0){var messageBuilder=new SizeInfoMessageBuilder();messageBuilder.append('shares');messageBuilder.appendMap(ownedNameToEntry,false,undefined,function(ownedName,ownedEntry){messageBuilder.append(ownedName);var ownedCount=ownedEntry.count;if(ownedCount<numerics.length){messageBuilder.appendSomeTimestampsQuantifier();}
+messageBuilder.appendImportanceRange(ownedEntry.importanceRange);messageBuilder.append(' with');messageBuilder.appendMap(ownedEntry.sharerNameToEntry,false,' no other dumps',function(sharerName,sharerEntry){messageBuilder.append(sharerName);if(sharerEntry.count<ownedCount){messageBuilder.appendSomeTimestampsQuantifier();}
+messageBuilder.appendImportanceRange(sharerEntry.importanceRange);},this);},this);infos.push({message:messageBuilder.build(),icon:RIGHTWARDS_OPEN_HEADED_ARROW,color:'green'});}},getAndUpdateOwnershipEntry_:function(map,dump,link,opt_withSharerNameToEntry){var entry=getAndUpdateEntry(map,dump.quantifiedName,function(newEntry){newEntry.importanceRange=new tr.b.math.Range();if(opt_withSharerNameToEntry){newEntry.sharerNameToEntry=new Map();}});entry.importanceRange.addValue(link.importance||0);return entry;}};function SizeColumn(name,cellPath,aggregationMode){tr.ui.analysis.DetailsNumericMemoryColumn.call(this,name,cellPath,aggregationMode);}
+SizeColumn.prototype={__proto__:tr.ui.analysis.DetailsNumericMemoryColumn.prototype,get title(){return tr.ui.b.createLink({textContent:this.name,tooltip:'Memory requested by this component',href:URL_TO_SIZE_VS_EFFECTIVE_SIZE});},addInfos:function(numerics,memoryAllocatorDumps,infos){if(memoryAllocatorDumps===undefined)return;this.addOverlapInfo_(numerics,memoryAllocatorDumps,infos);this.addProvidedSizeWarningInfos_(numerics,memoryAllocatorDumps,infos);},addOverlapInfo_:function(numerics,memoryAllocatorDumps,infos){var siblingNameToEntry=new Map();for(var i=0;i<numerics.length;i++){if(numerics[i]===undefined)continue;var dump=memoryAllocatorDumps[i];if(dump===SUBALLOCATION_CONTEXT){return;}
+var ownedBySiblingSizes=dump.ownedBySiblingSizes;for(var siblingDump of ownedBySiblingSizes.keys()){var siblingName=siblingDump.name;getAndUpdateEntry(siblingNameToEntry,siblingName,function(newEntry){if(numerics.length===1){newEntry.size=ownedBySiblingSizes.get(siblingDump);}});}}
+if(siblingNameToEntry.size>0){var messageBuilder=new SizeInfoMessageBuilder();messageBuilder.append('overlaps with its sibling');messageBuilder.appendMap(siblingNameToEntry,true,undefined,function(siblingName,siblingEntry){messageBuilder.append('\'',siblingName,'\'');messageBuilder.appendSizeIfDefined(siblingEntry.size);if(siblingEntry.count<numerics.length){messageBuilder.appendSomeTimestampsQuantifier();}},this);infos.push({message:messageBuilder.build(),icon:CIRCLED_LATIN_SMALL_LETTER_I,color:'blue'});}},addProvidedSizeWarningInfos_:function(numerics,memoryAllocatorDumps,infos){var infoTypeToEntry=new Map();for(var i=0;i<numerics.length;i++){if(numerics[i]===undefined)continue;var dump=memoryAllocatorDumps[i];if(dump===SUBALLOCATION_CONTEXT){return;}
+dump.infos.forEach(function(dumpInfo){getAndUpdateEntry(infoTypeToEntry,dumpInfo.type,function(newEntry){if(numerics.length===1){newEntry.providedSize=dumpInfo.providedSize;newEntry.dependencySize=dumpInfo.dependencySize;}});});}
 for(var infoType of infoTypeToEntry.keys()){var entry=infoTypeToEntry.get(infoType);var messageBuilder=new SizeInfoMessageBuilder();messageBuilder.append('provided size');messageBuilder.appendSizeIfDefined(entry.providedSize);var dependencyName;switch(infoType){case PROVIDED_SIZE_LESS_THAN_AGGREGATED_CHILDREN:dependencyName='the aggregated size of the children';break;case PROVIDED_SIZE_LESS_THAN_LARGEST_OWNER:dependencyName='the size of the largest owner';break;default:dependencyName='an unknown dependency';break;}
-messageBuilder.append(' was less than ',dependencyName);messageBuilder.appendSizeIfDefined(entry.dependencySize);if(entry.count<numerics.length)
-messageBuilder.appendSomeTimestampsQuantifier();infos.push(tr.ui.analysis.createWarningInfo(messageBuilder.build()));}}};var NUMERIC_COLUMN_RULES=[{condition:tr.model.MemoryAllocatorDump.EFFECTIVE_SIZE_NUMERIC_NAME,importance:10,columnConstructor:EffectiveSizeColumn},{condition:tr.model.MemoryAllocatorDump.SIZE_NUMERIC_NAME,importance:9,columnConstructor:SizeColumn},{condition:'page_size',importance:0,columnConstructor:tr.ui.analysis.DetailsNumericMemoryColumn},{condition:/size/,importance:5,columnConstructor:tr.ui.analysis.DetailsNumericMemoryColumn},{importance:0,columnConstructor:tr.ui.analysis.DetailsNumericMemoryColumn}];var DIAGNOSTIC_COLUMN_RULES=[{importance:0,columnConstructor:tr.ui.analysis.StringMemoryColumn}];Polymer({is:'tr-ui-a-memory-dump-allocator-details-pane',behaviors:[tr.ui.analysis.StackedPane],created:function(){this.memoryAllocatorDumps_=undefined;this.heapDumps_=undefined;this.aggregationMode_=undefined;},ready:function(){this.$.table.selectionMode=tr.ui.b.TableFormat.SelectionMode.ROW;},set memoryAllocatorDumps(memoryAllocatorDumps){this.memoryAllocatorDumps_=memoryAllocatorDumps;this.scheduleRebuild_();},get memoryAllocatorDumps(){return this.memoryAllocatorDumps_;},set heapDumps(heapDumps){this.heapDumps_=heapDumps;this.scheduleRebuild_();},set aggregationMode(aggregationMode){this.aggregationMode_=aggregationMode;this.scheduleRebuild_();},get aggregationMode(){return this.aggregationMode_;},onRebuild_:function(){if(this.memoryAllocatorDumps_===undefined||this.memoryAllocatorDumps_.length===0){this.$.info_text.style.display='block';this.$.table.style.display='none';this.$.table.clear();this.$.table.rebuild();this.childPaneBuilder=undefined;return;}
+messageBuilder.append(' was less than ',dependencyName);messageBuilder.appendSizeIfDefined(entry.dependencySize);if(entry.count<numerics.length){messageBuilder.appendSomeTimestampsQuantifier();}
+infos.push(tr.ui.analysis.createWarningInfo(messageBuilder.build()));}}};var NUMERIC_COLUMN_RULES=[{condition:tr.model.MemoryAllocatorDump.EFFECTIVE_SIZE_NUMERIC_NAME,importance:10,columnConstructor:EffectiveSizeColumn},{condition:tr.model.MemoryAllocatorDump.SIZE_NUMERIC_NAME,importance:9,columnConstructor:SizeColumn},{condition:'page_size',importance:0,columnConstructor:tr.ui.analysis.DetailsNumericMemoryColumn},{condition:/size/,importance:5,columnConstructor:tr.ui.analysis.DetailsNumericMemoryColumn},{importance:0,columnConstructor:tr.ui.analysis.DetailsNumericMemoryColumn}];var DIAGNOSTIC_COLUMN_RULES=[{importance:0,columnConstructor:tr.ui.analysis.StringMemoryColumn}];Polymer({is:'tr-ui-a-memory-dump-allocator-details-pane',behaviors:[tr.ui.analysis.StackedPane],created:function(){this.memoryAllocatorDumps_=undefined;this.heapDumps_=undefined;this.aggregationMode_=undefined;},ready:function(){this.$.table.selectionMode=tr.ui.b.TableFormat.SelectionMode.ROW;},set memoryAllocatorDumps(memoryAllocatorDumps){this.memoryAllocatorDumps_=memoryAllocatorDumps;this.scheduleRebuild_();},get memoryAllocatorDumps(){return this.memoryAllocatorDumps_;},set heapDumps(heapDumps){this.heapDumps_=heapDumps;this.scheduleRebuild_();},set aggregationMode(aggregationMode){this.aggregationMode_=aggregationMode;this.scheduleRebuild_();},get aggregationMode(){return this.aggregationMode_;},onRebuild_:function(){if(this.memoryAllocatorDumps_===undefined||this.memoryAllocatorDumps_.length===0){this.$.info_text.style.display='block';this.$.table.style.display='none';this.$.table.clear();this.$.table.rebuild();this.childPaneBuilder=undefined;return;}
 this.$.info_text.style.display='none';this.$.table.style.display='block';var rows=this.createRows_();var columns=this.createColumns_(rows);rows.forEach(function(rootRow){tr.ui.analysis.aggregateTableRowCellsRecursively(rootRow,columns,function(contexts){return contexts!==undefined&&contexts.some(function(context){return context===SUBALLOCATION_CONTEXT;});});});this.$.table.tableRows=rows;this.$.table.tableColumns=columns;this.$.table.rebuild();tr.ui.analysis.expandTableRowsRecursively(this.$.table);if(this.heapDumps_===undefined){this.childPaneBuilder=undefined;}else{this.childPaneBuilder=function(){var pane=document.createElement('tr-ui-a-memory-dump-heap-details-pane');pane.heapDumps=this.heapDumps_;pane.aggregationMode=this.aggregationMode_;return pane;}.bind(this);}},createRows_:function(){return[this.createAllocatorRowRecursively_(this.memoryAllocatorDumps_)];},createAllocatorRowRecursively_:function(dumps){var definedDump=tr.b.findFirstInArray(dumps);var title=definedDump.name;var fullName=definedDump.fullName;var numericCells=tr.ui.analysis.createCells(dumps,function(dump){return dump.numerics;});var diagnosticCells=tr.ui.analysis.createCells(dumps,function(dump){return dump.diagnostics;});var suballocatedBy=undefined;if(title.startsWith('__')){for(var i=0;i<dumps.length;i++){var dump=dumps[i];if(dump===undefined||dump.ownedBy.length===0){continue;}
 var ownerDump=dump.ownedBy[0].source;if(dump.ownedBy.length>1||dump.children.length>0||ownerDump.containerMemoryDump!==dump.containerMemoryDump){suballocatedBy=undefined;break;}
 if(suballocatedBy===undefined){suballocatedBy=ownerDump.fullName;}else if(suballocatedBy!==ownerDump.fullName){suballocatedBy=undefined;break;}}}
-var row={title:title,fullNames:[fullName],contexts:dumps,numericCells:numericCells,diagnosticCells:diagnosticCells,suballocatedBy:suballocatedBy};var childDumpNameToDumps=tr.b.invertArrayOfDicts(dumps,function(dump){return tr.b.arrayToDict(dump.children,function(child){return child.name;});});var subRows=[];var suballocationClassificationRootNode=undefined;tr.b.iterItems(childDumpNameToDumps,function(childName,childDumps){var childRow=this.createAllocatorRowRecursively_(childDumps);if(childRow.suballocatedBy===undefined){subRows.push(childRow);}else{suballocationClassificationRootNode=this.classifySuballocationRow_(childRow,suballocationClassificationRootNode);}},this);if(suballocationClassificationRootNode!==undefined){var suballocationRow=this.createSuballocationRowRecursively_('suballocations',suballocationClassificationRootNode);subRows.push(suballocationRow);}
-if(subRows.length>0)
-row.subRows=subRows;return row;},classifySuballocationRow_:function(suballocationRow,rootNode){if(rootNode===undefined){rootNode={children:{},row:undefined};}
+var row={title:title,fullNames:[fullName],contexts:dumps,numericCells:numericCells,diagnosticCells:diagnosticCells,suballocatedBy:suballocatedBy};var childDumpNameToDumps=tr.b.invertArrayOfDicts(dumps,function(dump){return tr.b.arrayToDict(dump.children,function(child){return child.name;});});var subRows=[];var suballocationClassificationRootNode=undefined;for(var childDumps of Object.values(childDumpNameToDumps)){var childRow=this.createAllocatorRowRecursively_(childDumps);if(childRow.suballocatedBy===undefined){subRows.push(childRow);}else{suballocationClassificationRootNode=this.classifySuballocationRow_(childRow,suballocationClassificationRootNode);}}
+if(suballocationClassificationRootNode!==undefined){var suballocationRow=this.createSuballocationRowRecursively_('suballocations',suballocationClassificationRootNode);subRows.push(suballocationRow);}
+if(subRows.length>0){row.subRows=subRows;}
+return row;},classifySuballocationRow_:function(suballocationRow,rootNode){if(rootNode===undefined){rootNode={children:{},row:undefined};}
 var suballocationLevels=suballocationRow.suballocatedBy.split('/');var currentNode=rootNode;for(var i=0;i<suballocationLevels.length;i++){var suballocationLevel=suballocationLevels[i];var nextNode=currentNode.children[suballocationLevel];if(nextNode===undefined){currentNode.children[suballocationLevel]=nextNode={children:{},row:undefined};}
 var currentNode=nextNode;}
-var existingRow=currentNode.row;if(existingRow!==undefined){for(var i=0;i<suballocationRow.contexts.length;i++){var newContext=suballocationRow.contexts[i];if(newContext===undefined)
-continue;if(existingRow.contexts[i]!==undefined)
-throw new Error('Multiple suballocations with the same owner name');existingRow.contexts[i]=newContext;['numericCells','diagnosticCells'].forEach(function(cellKey){var suballocationCells=suballocationRow[cellKey];if(suballocationCells===undefined)
-return;tr.b.iterItems(suballocationCells,function(cellName,cell){if(cell===undefined)
-return;var fields=cell.fields;if(fields===undefined)
-return;var field=fields[i];if(field===undefined)
-return;var existingCells=existingRow[cellKey];if(existingCells===undefined){existingCells={};existingRow[cellKey]=existingCells;}
+var existingRow=currentNode.row;if(existingRow!==undefined){for(var i=0;i<suballocationRow.contexts.length;i++){var newContext=suballocationRow.contexts[i];if(newContext===undefined)continue;if(existingRow.contexts[i]!==undefined){throw new Error('Multiple suballocations with the same owner name');}
+existingRow.contexts[i]=newContext;['numericCells','diagnosticCells'].forEach(function(cellKey){var suballocationCells=suballocationRow[cellKey];if(suballocationCells===undefined)return;for(var[cellName,cell]of Object.entries(suballocationCells)){if(cell===undefined)continue;var fields=cell.fields;if(fields===undefined)continue;var field=fields[i];if(field===undefined)continue;var existingCells=existingRow[cellKey];if(existingCells===undefined){existingCells={};existingRow[cellKey]=existingCells;}
 var existingCell=existingCells[cellName];if(existingCell===undefined){existingCell=new tr.ui.analysis.MemoryCell(new Array(fields.length));existingCells[cellName]=existingCell;}
-existingCell.fields[i]=field;});});}
+existingCell.fields[i]=field;}});}
 existingRow.fullNames.push.apply(existingRow.fullNames,suballocationRow.fullNames);}else{currentNode.row=suballocationRow;}
-return rootNode;},createSuballocationRowRecursively_:function(name,node){var childCount=Object.keys(node.children).length;if(childCount===0){if(node.row===undefined)
-throw new Error('Suballocation node must have a row or children');var row=node.row;row.title=name;row.suballocation=true;return row;}
+return rootNode;},createSuballocationRowRecursively_:function(name,node){var childCount=Object.keys(node.children).length;if(childCount===0){if(node.row===undefined){throw new Error('Suballocation node must have a row or children');}
+var row=node.row;row.title=name;row.suballocation=true;return row;}
 var subRows=tr.b.dictionaryValues(tr.b.mapItems(node.children,this.createSuballocationRowRecursively_,this));if(node.row!==undefined){var row=node.row;row.title='<unspecified>';row.suballocation=true;subRows.unshift(row);}
-var contexts=new Array(subRows[0].contexts.length);for(var i=0;i<subRows.length;i++){subRows[i].contexts.forEach(function(subContext,index){if(subContext!==undefined)
-contexts[index]=SUBALLOCATION_CONTEXT;});}
-return{title:name,suballocation:true,contexts:contexts,subRows:subRows};},createColumns_:function(rows){var titleColumn=new AllocatorDumpNameColumn();titleColumn.width='200px';var numericColumns=tr.ui.analysis.MemoryColumn.fromRows(rows,'numericCells',this.aggregationMode_,NUMERIC_COLUMN_RULES);var diagnosticColumns=tr.ui.analysis.MemoryColumn.fromRows(rows,'diagnosticCells',this.aggregationMode_,DIAGNOSTIC_COLUMN_RULES);var fieldColumns=numericColumns.concat(diagnosticColumns);tr.ui.analysis.MemoryColumn.spaceEqually(fieldColumns);var columns=[titleColumn].concat(fieldColumns);return columns;}});return{SUBALLOCATION_CONTEXT:SUBALLOCATION_CONTEXT,AllocatorDumpNameColumn:AllocatorDumpNameColumn,EffectiveSizeColumn:EffectiveSizeColumn,SizeColumn:SizeColumn};});'use strict';tr.exportTo('tr.ui.analysis',function(){var ScalarNumeric=tr.v.ScalarNumeric;var sizeInBytes_smallerIsBetter=tr.b.Unit.byName.sizeInBytes_smallerIsBetter;var CONSTANT_COLUMN_RULES=[{condition:'Start address',importance:0,columnConstructor:tr.ui.analysis.StringMemoryColumn}];var VARIABLE_COLUMN_RULES=[{condition:'Virtual size',importance:7,columnConstructor:tr.ui.analysis.DetailsNumericMemoryColumn},{condition:'Protection flags',importance:6,columnConstructor:tr.ui.analysis.StringMemoryColumn},{condition:'PSS',importance:5,columnConstructor:tr.ui.analysis.DetailsNumericMemoryColumn},{condition:'Private dirty',importance:4,columnConstructor:tr.ui.analysis.DetailsNumericMemoryColumn},{condition:'Private clean',importance:3,columnConstructor:tr.ui.analysis.DetailsNumericMemoryColumn},{condition:'Shared dirty',importance:2,columnConstructor:tr.ui.analysis.DetailsNumericMemoryColumn},{condition:'Shared clean',importance:1,columnConstructor:tr.ui.analysis.DetailsNumericMemoryColumn},{condition:'Swapped',importance:0,columnConstructor:tr.ui.analysis.DetailsNumericMemoryColumn}];var BYTE_STAT_COLUMN_MAP={'proportionalResident':'PSS','privateDirtyResident':'Private dirty','privateCleanResident':'Private clean','sharedDirtyResident':'Shared dirty','sharedCleanResident':'Shared clean','swapped':'Swapped'};function hexString(address,is64BitAddress){if(address===undefined)
-return undefined;var hexPadding=is64BitAddress?'0000000000000000':'00000000';return(hexPadding+address.toString(16)).substr(-hexPadding.length);}
-function pruneEmptyRuleRows(row){if(row.subRows===undefined||row.subRows.length===0)
-return;if(row.subRows[0].rule===undefined){return;}
+var contexts=new Array(subRows[0].contexts.length);for(var i=0;i<subRows.length;i++){subRows[i].contexts.forEach(function(subContext,index){if(subContext!==undefined){contexts[index]=SUBALLOCATION_CONTEXT;}});}
+return{title:name,suballocation:true,contexts:contexts,subRows:subRows};},createColumns_:function(rows){var titleColumn=new AllocatorDumpNameColumn();titleColumn.width='200px';var numericColumns=tr.ui.analysis.MemoryColumn.fromRows(rows,{cellKey:'numericCells',aggregationMode:this.aggregationMode_,rules:NUMERIC_COLUMN_RULES});var diagnosticColumns=tr.ui.analysis.MemoryColumn.fromRows(rows,{cellKey:'diagnosticCells',aggregationMode:this.aggregationMode_,rules:DIAGNOSTIC_COLUMN_RULES});var fieldColumns=numericColumns.concat(diagnosticColumns);tr.ui.analysis.MemoryColumn.spaceEqually(fieldColumns);var columns=[titleColumn].concat(fieldColumns);return columns;}});return{SUBALLOCATION_CONTEXT,AllocatorDumpNameColumn,EffectiveSizeColumn,SizeColumn,};});'use strict';tr.exportTo('tr.ui.analysis',function(){var Scalar=tr.b.Scalar;var sizeInBytes_smallerIsBetter=tr.b.Unit.byName.sizeInBytes_smallerIsBetter;var CONSTANT_COLUMN_RULES=[{condition:'Start address',importance:0,columnConstructor:tr.ui.analysis.StringMemoryColumn}];var VARIABLE_COLUMN_RULES=[{condition:'Virtual size',importance:7,columnConstructor:tr.ui.analysis.DetailsNumericMemoryColumn},{condition:'Protection flags',importance:6,columnConstructor:tr.ui.analysis.StringMemoryColumn},{condition:'PSS',importance:5,columnConstructor:tr.ui.analysis.DetailsNumericMemoryColumn},{condition:'Private dirty',importance:4,columnConstructor:tr.ui.analysis.DetailsNumericMemoryColumn},{condition:'Private clean',importance:3,columnConstructor:tr.ui.analysis.DetailsNumericMemoryColumn},{condition:'Shared dirty',importance:2,columnConstructor:tr.ui.analysis.DetailsNumericMemoryColumn},{condition:'Shared clean',importance:1,columnConstructor:tr.ui.analysis.DetailsNumericMemoryColumn},{condition:'Swapped',importance:0,columnConstructor:tr.ui.analysis.DetailsNumericMemoryColumn}];var BYTE_STAT_COLUMN_MAP={'proportionalResident':'PSS','privateDirtyResident':'Private dirty','privateCleanResident':'Private clean','sharedDirtyResident':'Shared dirty','sharedCleanResident':'Shared clean','swapped':'Swapped'};function hexString(address,is64BitAddress){if(address===undefined)return undefined;var hexPadding=is64BitAddress?'0000000000000000':'00000000';return(hexPadding+address.toString(16)).substr(-hexPadding.length);}
+function pruneEmptyRuleRows(row){if(row.subRows===undefined||row.subRows.length===0)return;if(row.subRows[0].rule===undefined){return;}
 row.subRows.forEach(pruneEmptyRuleRows);row.subRows=row.subRows.filter(function(subRow){return subRow.subRows.length>0;});}
 Polymer({is:'tr-ui-a-memory-dump-vm-regions-details-pane',behaviors:[tr.ui.analysis.StackedPane],created:function(){this.vmRegions_=undefined;this.aggregationMode_=undefined;},ready:function(){this.$.table.selectionMode=tr.ui.b.TableFormat.SelectionMode.ROW;},set vmRegions(vmRegions){this.vmRegions_=vmRegions;this.scheduleRebuild_();},get vmRegions(){return this.vmRegions_;},set aggregationMode(aggregationMode){this.aggregationMode_=aggregationMode;this.scheduleRebuild_();},get aggregationMode(){return this.aggregationMode_;},onRebuild_:function(){if(this.vmRegions_===undefined||this.vmRegions_.length===0){this.$.info_text.style.display='block';this.$.table.style.display='none';this.$.table.clear();this.$.table.rebuild();return;}
-this.$.info_text.style.display='none';this.$.table.style.display='block';var rows=this.createRows_(this.vmRegions_);var columns=this.createColumns_(rows);this.$.table.tableRows=rows;this.$.table.tableColumns=columns;this.$.table.rebuild();tr.ui.analysis.expandTableRowsRecursively(this.$.table);},createRows_:function(timeToVmRegionTree){var is64BitAddress=timeToVmRegionTree.some(function(vmRegionTree){if(vmRegionTree===undefined)
-return false;return vmRegionTree.someRegion(function(region){if(region.startAddress===undefined)
-return false;return region.startAddress>=4294967296;});});return[this.createClassificationNodeRow(timeToVmRegionTree,is64BitAddress)];},createClassificationNodeRow:function(timeToNode,is64BitAddress){var definedNode=tr.b.findFirstInArray(timeToNode);var childNodeIdToTimeToNode=tr.b.dictionaryValues(tr.b.invertArrayOfDicts(timeToNode,function(node){var children=node.children;if(children===undefined)
-return undefined;var childMap={};children.forEach(function(childNode){if(!childNode.hasRegions)
-return;childMap[childNode.title]=childNode;});return childMap;}));var childNodeSubRows=childNodeIdToTimeToNode.map(function(timeToChildNode){return this.createClassificationNodeRow(timeToChildNode,is64BitAddress);},this);var regionIdToTimeToRegion=tr.b.dictionaryValues(tr.b.invertArrayOfDicts(timeToNode,function(node){var regions=node.regions;if(regions===undefined)
-return undefined;return tr.b.arrayToDict(regions,function(region){return region.uniqueIdWithinProcess;});}));var regionSubRows=regionIdToTimeToRegion.map(function(timeToRegion){return this.createRegionRow_(timeToRegion,is64BitAddress);},this);var subRows=childNodeSubRows.concat(regionSubRows);return{title:definedNode.title,contexts:timeToNode,variableCells:this.createVariableCells_(timeToNode),subRows:subRows};},createRegionRow_:function(timeToRegion,is64BitAddress){var definedRegion=tr.b.findFirstInArray(timeToRegion);return{title:definedRegion.mappedFile,contexts:timeToRegion,constantCells:this.createConstantCells_(definedRegion,is64BitAddress),variableCells:this.createVariableCells_(timeToRegion)};},createConstantCells_:function(definedRegion,is64BitAddress){return tr.ui.analysis.createCells([definedRegion],function(region){var startAddress=region.startAddress;if(startAddress===undefined)
-return undefined;return{'Start address':hexString(startAddress,is64BitAddress)};});},createVariableCells_:function(timeToRegion){return tr.ui.analysis.createCells(timeToRegion,function(region){var fields={};var sizeInBytes=region.sizeInBytes;if(sizeInBytes!==undefined){fields['Virtual size']=new ScalarNumeric(sizeInBytes_smallerIsBetter,sizeInBytes);}
-var protectionFlags=region.protectionFlagsToString;if(protectionFlags!==undefined)
-fields['Protection flags']=protectionFlags;tr.b.iterItems(BYTE_STAT_COLUMN_MAP,function(byteStatName,columnName){var byteStat=region.byteStats[byteStatName];if(byteStat===undefined)
-return;fields[columnName]=new ScalarNumeric(sizeInBytes_smallerIsBetter,byteStat);});return fields;});},createColumns_:function(rows){var titleColumn=new tr.ui.analysis.TitleColumn('Mapped file');titleColumn.width='200px';var constantColumns=tr.ui.analysis.MemoryColumn.fromRows(rows,'constantCells',undefined,CONSTANT_COLUMN_RULES);var variableColumns=tr.ui.analysis.MemoryColumn.fromRows(rows,'variableCells',this.aggregationMode_,VARIABLE_COLUMN_RULES);var fieldColumns=constantColumns.concat(variableColumns);tr.ui.analysis.MemoryColumn.spaceEqually(fieldColumns);var columns=[titleColumn].concat(fieldColumns);return columns;}});return{};});'use strict';Polymer({is:'tr-ui-b-color-legend',ready:function(){var blackSquareCharCode=9632;this.$.square.innerText=String.fromCharCode(blackSquareCharCode);this.label_=undefined;this.compoundEventSelectionState_=tr.model.CompoundEventSelectionState.NOT_SELECTED;},set compoundEventSelectionState(compoundEventSelectionState){this.compoundEventSelectionState_=compoundEventSelectionState;},get label(){return this.label_;},set label(label){if(label===undefined){this.setLabelAndColorId(undefined,undefined);return;}
-var colorId=tr.b.ColorScheme.getColorIdForGeneralPurposeString(label);this.setLabelAndColorId(label,colorId);},setLabelAndColorId:function(label,colorId){this.label_=label;Polymer.dom(this.$.label).textContent='';Polymer.dom(this.$.label).appendChild(tr.ui.b.asHTMLOrTextNode(label));if(colorId===undefined)
-this.$.square.style.color='initial';else
-this.$.square.style.color=tr.b.ColorScheme.colorsAsStrings[colorId];}});'use strict';Polymer({is:'tr-ui-b-view-specific-brushing-state',get viewId(){return this.getAttribute('view-id');},set viewId(viewId){Polymer.dom(this).setAttribute('view-id',viewId);},get:function(){var viewId=this.viewId;if(!viewId)
-throw new Error('Element must have a view-id attribute!');var brushingStateController=tr.c.BrushingStateController.getControllerForElement(this);if(!brushingStateController)
-return undefined;return brushingStateController.getViewSpecificBrushingState(viewId);},set:function(state){var viewId=this.viewId;if(!viewId)
-throw new Error('Element must have a view-id attribute!');var brushingStateController=tr.c.BrushingStateController.getControllerForElement(this);if(!brushingStateController)
-return;brushingStateController.changeViewSpecificBrushingState(viewId,state);}});'use strict';tr.exportTo('tr.ui.analysis',function(){var ColorScheme=tr.b.ColorScheme;var ScalarNumeric=tr.v.ScalarNumeric;var sizeInBytes_smallerIsBetter=tr.b.Unit.byName.sizeInBytes_smallerIsBetter;var PLATFORM_SPECIFIC_TOTAL_NAME_SUFFIX='_bytes';var DISPLAYED_SIZE_NUMERIC_NAME=tr.model.MemoryAllocatorDump.DISPLAYED_SIZE_NUMERIC_NAME;var SOME_TIMESTAMPS_INFO_QUANTIFIER=tr.ui.analysis.MemoryColumn.SOME_TIMESTAMPS_INFO_QUANTIFIER;var RIGHTWARDS_ARROW_WITH_HOOK=String.fromCharCode(0x21AA);var RIGHTWARDS_ARROW_FROM_BAR=String.fromCharCode(0x21A6);var GREATER_THAN_OR_EQUAL_TO=String.fromCharCode(0x2265);var UNMARRIED_PARTNERSHIP_SYMBOL=String.fromCharCode(0x26AF);var TRIGRAM_FOR_HEAVEN=String.fromCharCode(0x2630);function lazyMap(list,fn,opt_this){opt_this=opt_this||this;var result=undefined;list.forEach(function(item,index){var value=fn.call(opt_this,item,index);if(value===undefined)
-return;if(result===undefined)
-result=new Array(list.length);result[index]=value;});return result;}
+this.$.info_text.style.display='none';this.$.table.style.display='block';var rows=this.createRows_(this.vmRegions_);var columns=this.createColumns_(rows);this.$.table.tableRows=rows;this.$.table.tableColumns=columns;this.$.table.rebuild();tr.ui.analysis.expandTableRowsRecursively(this.$.table);},createRows_:function(timeToVmRegionTree){var is64BitAddress=timeToVmRegionTree.some(function(vmRegionTree){if(vmRegionTree===undefined)return false;return vmRegionTree.someRegion(function(region){if(region.startAddress===undefined)return false;return region.startAddress>=4294967296;});});return[this.createClassificationNodeRow(timeToVmRegionTree,is64BitAddress)];},createClassificationNodeRow:function(timeToNode,is64BitAddress){var definedNode=tr.b.findFirstInArray(timeToNode);var childNodeIdToTimeToNode=tr.b.dictionaryValues(tr.b.invertArrayOfDicts(timeToNode,function(node){var children=node.children;if(children===undefined)return undefined;var childMap={};children.forEach(function(childNode){if(!childNode.hasRegions)return;childMap[childNode.title]=childNode;});return childMap;}));var childNodeSubRows=childNodeIdToTimeToNode.map(function(timeToChildNode){return this.createClassificationNodeRow(timeToChildNode,is64BitAddress);},this);var regionIdToTimeToRegion=tr.b.dictionaryValues(tr.b.invertArrayOfDicts(timeToNode,function(node){var regions=node.regions;if(regions===undefined)return undefined;return tr.b.arrayToDict(regions,function(region){return region.uniqueIdWithinProcess;});}));var regionSubRows=regionIdToTimeToRegion.map(function(timeToRegion){return this.createRegionRow_(timeToRegion,is64BitAddress);},this);var subRows=childNodeSubRows.concat(regionSubRows);return{title:definedNode.title,contexts:timeToNode,variableCells:this.createVariableCells_(timeToNode),subRows:subRows};},createRegionRow_:function(timeToRegion,is64BitAddress){var definedRegion=tr.b.findFirstInArray(timeToRegion);return{title:definedRegion.mappedFile,contexts:timeToRegion,constantCells:this.createConstantCells_(definedRegion,is64BitAddress),variableCells:this.createVariableCells_(timeToRegion)};},createConstantCells_:function(definedRegion,is64BitAddress){return tr.ui.analysis.createCells([definedRegion],function(region){var startAddress=region.startAddress;if(startAddress===undefined)return undefined;return{'Start address':hexString(startAddress,is64BitAddress)};});},createVariableCells_:function(timeToRegion){return tr.ui.analysis.createCells(timeToRegion,function(region){var fields={};var sizeInBytes=region.sizeInBytes;if(sizeInBytes!==undefined){fields['Virtual size']=new Scalar(sizeInBytes_smallerIsBetter,sizeInBytes);}
+var protectionFlags=region.protectionFlagsToString;if(protectionFlags!==undefined){fields['Protection flags']=protectionFlags;}
+for(var[byteStatName,columnName]of
+Object.entries(BYTE_STAT_COLUMN_MAP)){var byteStat=region.byteStats[byteStatName];if(byteStat===undefined)continue;fields[columnName]=new Scalar(sizeInBytes_smallerIsBetter,byteStat);}
+return fields;});},createColumns_:function(rows){var titleColumn=new tr.ui.analysis.TitleColumn('Mapped file');titleColumn.width='200px';var constantColumns=tr.ui.analysis.MemoryColumn.fromRows(rows,{cellKey:'constantCells',aggregationMode:undefined,rules:CONSTANT_COLUMN_RULES});var variableColumns=tr.ui.analysis.MemoryColumn.fromRows(rows,{cellKey:'variableCells',aggregationMode:this.aggregationMode_,rules:VARIABLE_COLUMN_RULES});var fieldColumns=constantColumns.concat(variableColumns);tr.ui.analysis.MemoryColumn.spaceEqually(fieldColumns);var columns=[titleColumn].concat(fieldColumns);return columns;}});return{};});'use strict';Polymer({is:'tr-ui-b-color-legend',ready:function(){var blackSquareCharCode=9632;this.$.square.innerText=String.fromCharCode(blackSquareCharCode);this.label_=undefined;this.compoundEventSelectionState_=tr.model.CompoundEventSelectionState.NOT_SELECTED;},set compoundEventSelectionState(compoundEventSelectionState){this.compoundEventSelectionState_=compoundEventSelectionState;},get label(){return this.label_;},set label(label){if(label===undefined){this.setLabelAndColorId(undefined,undefined);return;}
+var colorId=tr.b.ColorScheme.getColorIdForGeneralPurposeString(label);this.setLabelAndColorId(label,colorId);},setLabelAndColorId:function(label,colorId){this.label_=label;Polymer.dom(this.$.label).textContent='';Polymer.dom(this.$.label).appendChild(tr.ui.b.asHTMLOrTextNode(label));if(colorId===undefined){this.$.square.style.color='initial';}else{this.$.square.style.color=tr.b.ColorScheme.colorsAsStrings[colorId];}}});'use strict';Polymer({is:'tr-ui-b-view-specific-brushing-state',get viewId(){return this.getAttribute('view-id');},set viewId(viewId){Polymer.dom(this).setAttribute('view-id',viewId);},get:function(){var viewId=this.viewId;if(!viewId){throw new Error('Element must have a view-id attribute!');}
+var brushingStateController=tr.c.BrushingStateController.getControllerForElement(this);if(!brushingStateController)return undefined;return brushingStateController.getViewSpecificBrushingState(viewId);},set:function(state){var viewId=this.viewId;if(!viewId){throw new Error('Element must have a view-id attribute!');}
+var brushingStateController=tr.c.BrushingStateController.getControllerForElement(this);if(!brushingStateController)return;brushingStateController.changeViewSpecificBrushingState(viewId,state);}});'use strict';tr.exportTo('tr.ui.analysis',function(){var MemoryColumnColorScheme=tr.b.MemoryColumnColorScheme;var Scalar=tr.b.Scalar;var sizeInBytes_smallerIsBetter=tr.b.Unit.byName.sizeInBytes_smallerIsBetter;var PLATFORM_SPECIFIC_TOTAL_NAME_SUFFIX='_bytes';var DISPLAYED_SIZE_NUMERIC_NAME=tr.model.MemoryAllocatorDump.DISPLAYED_SIZE_NUMERIC_NAME;var SOME_TIMESTAMPS_INFO_QUANTIFIER=tr.ui.analysis.MemoryColumn.SOME_TIMESTAMPS_INFO_QUANTIFIER;var RIGHTWARDS_ARROW_WITH_HOOK=String.fromCharCode(0x21AA);var RIGHTWARDS_ARROW_FROM_BAR=String.fromCharCode(0x21A6);var GREATER_THAN_OR_EQUAL_TO=String.fromCharCode(0x2265);var UNMARRIED_PARTNERSHIP_SYMBOL=String.fromCharCode(0x26AF);var TRIGRAM_FOR_HEAVEN=String.fromCharCode(0x2630);function lazyMap(list,fn,opt_this){opt_this=opt_this||this;var result=undefined;list.forEach(function(item,index){var value=fn.call(opt_this,item,index);if(value===undefined)return;if(result===undefined){result=new Array(list.length);}
+result[index]=value;});return result;}
 function ProcessNameColumn(){tr.ui.analysis.TitleColumn.call(this,'Process');}
-ProcessNameColumn.prototype={__proto__:tr.ui.analysis.TitleColumn.prototype,formatTitle:function(row){if(row.contexts===undefined)
-return row.title;var titleEl=document.createElement('tr-ui-b-color-legend');titleEl.label=row.title;return titleEl;}};function UsedMemoryColumn(name,cellPath,aggregationMode){tr.ui.analysis.NumericMemoryColumn.call(this,name,cellPath,aggregationMode);}
-UsedMemoryColumn.COLOR=ColorScheme.getColorForReservedNameAsString('used_memory_column');UsedMemoryColumn.OLDER_COLOR=ColorScheme.getColorForReservedNameAsString('older_used_memory_column');UsedMemoryColumn.prototype={__proto__:tr.ui.analysis.NumericMemoryColumn.prototype,get title(){return tr.ui.b.createSpan({textContent:this.name,color:UsedMemoryColumn.COLOR});},getFormattingContext:function(unit){return{unitPrefix:tr.b.UnitScale.Binary.MEBI};},color:function(numerics,processMemoryDumps){return UsedMemoryColumn.COLOR;},getChildPaneBuilder:function(processMemoryDumps){if(processMemoryDumps===undefined)
-return undefined;var vmRegions=lazyMap(processMemoryDumps,function(pmd){if(pmd===undefined)
-return undefined;return pmd.mostRecentVmRegions;});if(vmRegions===undefined)
-return undefined;return function(){var pane=document.createElement('tr-ui-a-memory-dump-vm-regions-details-pane');pane.vmRegions=vmRegions;pane.aggregationMode=this.aggregationMode;return pane;}.bind(this);}};function PeakMemoryColumn(name,cellPath,aggregationMode){UsedMemoryColumn.call(this,name,cellPath,aggregationMode);}
-PeakMemoryColumn.prototype={__proto__:UsedMemoryColumn.prototype,addInfos:function(numerics,processMemoryDumps,infos){if(processMemoryDumps===undefined)
-return;var resettableValueCount=0;var nonResettableValueCount=0;for(var i=0;i<numerics.length;i++){if(numerics[i]===undefined)
-continue;if(processMemoryDumps[i].arePeakResidentBytesResettable)
-resettableValueCount++;else
-nonResettableValueCount++;}
+ProcessNameColumn.prototype={__proto__:tr.ui.analysis.TitleColumn.prototype,formatTitle:function(row){if(row.contexts===undefined){return row.title;}
+var titleEl=document.createElement('tr-ui-b-color-legend');titleEl.label=row.title;return titleEl;}};function UsedMemoryColumn(name,cellPath,aggregationMode){tr.ui.analysis.NumericMemoryColumn.call(this,name,cellPath,aggregationMode);}
+UsedMemoryColumn.COLOR=MemoryColumnColorScheme.getColor('used_memory_column').toString();UsedMemoryColumn.OLDER_COLOR=MemoryColumnColorScheme.getColor('older_used_memory_column').toString();UsedMemoryColumn.prototype={__proto__:tr.ui.analysis.NumericMemoryColumn.prototype,get title(){return tr.ui.b.createSpan({textContent:this.name,color:UsedMemoryColumn.COLOR});},getFormattingContext:function(unit){return{unitPrefix:tr.b.UnitPrefixScale.BINARY.MEBI};},color:function(numerics,processMemoryDumps){return UsedMemoryColumn.COLOR;},getChildPaneBuilder:function(processMemoryDumps){if(processMemoryDumps===undefined)return undefined;var vmRegions=lazyMap(processMemoryDumps,function(pmd){if(pmd===undefined)return undefined;return pmd.mostRecentVmRegions;});if(vmRegions===undefined)return undefined;return function(){var pane=document.createElement('tr-ui-a-memory-dump-vm-regions-details-pane');pane.vmRegions=vmRegions;pane.aggregationMode=this.aggregationMode;return pane;}.bind(this);}};function PeakMemoryColumn(name,cellPath,aggregationMode){UsedMemoryColumn.call(this,name,cellPath,aggregationMode);}
+PeakMemoryColumn.prototype={__proto__:UsedMemoryColumn.prototype,addInfos:function(numerics,processMemoryDumps,infos){if(processMemoryDumps===undefined)return;var resettableValueCount=0;var nonResettableValueCount=0;for(var i=0;i<numerics.length;i++){if(numerics[i]===undefined)continue;if(processMemoryDumps[i].arePeakResidentBytesResettable){resettableValueCount++;}else{nonResettableValueCount++;}}
 if(resettableValueCount>0&&nonResettableValueCount>0){infos.push(tr.ui.analysis.createWarningInfo('Both resettable and '+'non-resettable peak RSS values were provided by the process'));}else if(resettableValueCount>0){infos.push({icon:RIGHTWARDS_ARROW_WITH_HOOK,message:'Peak RSS since previous memory dump.'});}else{infos.push({icon:RIGHTWARDS_ARROW_FROM_BAR,message:'Peak RSS since process startup. Finer grained '+'peaks require a Linux kernel version '+
 GREATER_THAN_OR_EQUAL_TO+' 4.0.'});}}};function ByteStatColumn(name,cellPath,aggregationMode){UsedMemoryColumn.call(this,name,cellPath,aggregationMode);}
-ByteStatColumn.prototype={__proto__:UsedMemoryColumn.prototype,color:function(numerics,processMemoryDumps){if(processMemoryDumps===undefined)
-return UsedMemoryColumn.COLOR;var allOlderValues=processMemoryDumps.every(function(processMemoryDump){if(processMemoryDump===undefined)
-return true;return!processMemoryDump.hasOwnVmRegions;});if(allOlderValues)
-return UsedMemoryColumn.OLDER_COLOR;else
-return UsedMemoryColumn.COLOR;},addInfos:function(numerics,processMemoryDumps,infos){if(processMemoryDumps===undefined)
-return;var olderValueCount=0;for(var i=0;i<numerics.length;i++){var processMemoryDump=processMemoryDumps[i];if(processMemoryDump!==undefined&&!processMemoryDump.hasOwnVmRegions){olderValueCount++;}}
-if(olderValueCount===0)
-return;var infoQuantifier=olderValueCount<numerics.length?' '+SOME_TIMESTAMPS_INFO_QUANTIFIER:'';infos.push({message:'Older value'+infoQuantifier+' (only heavy (purple) memory dumps contain memory maps).',icon:UNMARRIED_PARTNERSHIP_SYMBOL});}};UsedMemoryColumn.RULES=[{condition:'Total resident',importance:10,columnConstructor:UsedMemoryColumn},{condition:'Peak total resident',importance:9,columnConstructor:PeakMemoryColumn},{condition:'PSS',importance:8,columnConstructor:ByteStatColumn},{condition:'Private dirty',importance:7,columnConstructor:ByteStatColumn},{condition:'Swapped',importance:6,columnConstructor:ByteStatColumn},{importance:0,columnConstructor:UsedMemoryColumn}];UsedMemoryColumn.TOTALS_MAP={'residentBytes':'Total resident','peakResidentBytes':'Peak total resident'};UsedMemoryColumn.BYTE_STAT_MAP={'proportionalResident':'PSS','privateDirtyResident':'Private dirty','swapped':'Swapped'};function AllocatorColumn(name,cellPath,aggregationMode){tr.ui.analysis.NumericMemoryColumn.call(this,name,cellPath,aggregationMode);}
-AllocatorColumn.prototype={__proto__:tr.ui.analysis.NumericMemoryColumn.prototype,get title(){var titleEl=document.createElement('tr-ui-b-color-legend');titleEl.label=this.name;return titleEl;},getFormattingContext:function(unit){return{unitPrefix:tr.b.UnitScale.Binary.MEBI};},addInfos:function(numerics,processMemoryDumps,infos){if(processMemoryDumps===undefined)
-return;var heapDumpCount=0;var missingSizeCount=0;for(var i=0;i<processMemoryDumps.length;i++){var processMemoryDump=processMemoryDumps[i];if(processMemoryDump===undefined)
-continue;var heapDumps=processMemoryDump.heapDumps;if(heapDumps!==undefined&&heapDumps[this.name]!==undefined)
-heapDumpCount++;var allocatorDump=processMemoryDump.getMemoryAllocatorDumpByFullName(this.name);if(allocatorDump!==undefined&&allocatorDump.numerics[DISPLAYED_SIZE_NUMERIC_NAME]===undefined){missingSizeCount++;}}
+ByteStatColumn.prototype={__proto__:UsedMemoryColumn.prototype,color:function(numerics,processMemoryDumps){if(processMemoryDumps===undefined){return UsedMemoryColumn.COLOR;}
+var allOlderValues=processMemoryDumps.every(function(processMemoryDump){if(processMemoryDump===undefined)return true;return!processMemoryDump.hasOwnVmRegions;});if(allOlderValues){return UsedMemoryColumn.OLDER_COLOR;}
+return UsedMemoryColumn.COLOR;},addInfos:function(numerics,processMemoryDumps,infos){if(processMemoryDumps===undefined)return;var olderValueCount=0;for(var i=0;i<numerics.length;i++){var processMemoryDump=processMemoryDumps[i];if(processMemoryDump!==undefined&&!processMemoryDump.hasOwnVmRegions){olderValueCount++;}}
+if(olderValueCount===0){return;}
+var infoQuantifier=olderValueCount<numerics.length?' '+SOME_TIMESTAMPS_INFO_QUANTIFIER:'';infos.push({message:'Older value'+infoQuantifier+' (only heavy (purple) memory dumps contain memory maps).',icon:UNMARRIED_PARTNERSHIP_SYMBOL});}};UsedMemoryColumn.RULES=[{condition:'Total resident',importance:10,columnConstructor:UsedMemoryColumn},{condition:'Peak total resident',importance:9,columnConstructor:PeakMemoryColumn},{condition:'PSS',importance:8,columnConstructor:ByteStatColumn},{condition:'Private dirty',importance:7,columnConstructor:ByteStatColumn},{condition:'Swapped',importance:6,columnConstructor:ByteStatColumn},{importance:0,columnConstructor:UsedMemoryColumn}];UsedMemoryColumn.TOTALS_MAP={'residentBytes':'Total resident','peakResidentBytes':'Peak total resident'};UsedMemoryColumn.BYTE_STAT_MAP={'proportionalResident':'PSS','privateDirtyResident':'Private dirty','swapped':'Swapped'};function AllocatorColumn(name,cellPath,aggregationMode){tr.ui.analysis.NumericMemoryColumn.call(this,name,cellPath,aggregationMode);}
+AllocatorColumn.prototype={__proto__:tr.ui.analysis.NumericMemoryColumn.prototype,get title(){var titleEl=document.createElement('tr-ui-b-color-legend');titleEl.label=this.name;return titleEl;},getFormattingContext:function(unit){return{unitPrefix:tr.b.UnitPrefixScale.BINARY.MEBI};},addInfos:function(numerics,processMemoryDumps,infos){if(processMemoryDumps===undefined)return;var heapDumpCount=0;var missingSizeCount=0;for(var i=0;i<processMemoryDumps.length;i++){var processMemoryDump=processMemoryDumps[i];if(processMemoryDump===undefined)continue;var heapDumps=processMemoryDump.heapDumps;if(heapDumps!==undefined&&heapDumps[this.name]!==undefined){heapDumpCount++;}
+var allocatorDump=processMemoryDump.getMemoryAllocatorDumpByFullName(this.name);if(allocatorDump!==undefined&&allocatorDump.numerics[DISPLAYED_SIZE_NUMERIC_NAME]===undefined){missingSizeCount++;}}
 if(heapDumpCount>0){var infoQuantifier=heapDumpCount<numerics.length?' '+SOME_TIMESTAMPS_INFO_QUANTIFIER:'';infos.push({message:'Heap dump provided'+infoQuantifier+'.',icon:TRIGRAM_FOR_HEAVEN});}
-if(missingSizeCount>0){var infoQuantifier=missingSizeCount<numerics.length?' '+SOME_TIMESTAMPS_INFO_QUANTIFIER:'';infos.push(tr.ui.analysis.createWarningInfo('Size was not provided'+infoQuantifier+'.'));}},getChildPaneBuilder:function(processMemoryDumps){if(processMemoryDumps===undefined)
-return undefined;var memoryAllocatorDumps=lazyMap(processMemoryDumps,function(pmd){if(pmd===undefined)
-return undefined;return pmd.getMemoryAllocatorDumpByFullName(this.name);},this);if(memoryAllocatorDumps===undefined)
-return undefined;var heapDumps=lazyMap(processMemoryDumps,function(pmd){if(pmd===undefined||pmd.heapDumps===undefined)
-return undefined;return pmd.heapDumps[this.name];},this);return function(){var pane=document.createElement('tr-ui-a-memory-dump-allocator-details-pane');pane.memoryAllocatorDumps=memoryAllocatorDumps;pane.heapDumps=heapDumps;pane.aggregationMode=this.aggregationMode;return pane;}.bind(this);}};function TracingColumn(name,cellPath,aggregationMode){AllocatorColumn.call(this,name,cellPath,aggregationMode);}
-TracingColumn.COLOR=ColorScheme.getColorForReservedNameAsString('tracing_memory_column');TracingColumn.prototype={__proto__:AllocatorColumn.prototype,get title(){return tr.ui.b.createSpan({textContent:this.name,color:TracingColumn.COLOR});},color:function(numerics,processMemoryDumps){return TracingColumn.COLOR;}};AllocatorColumn.RULES=[{condition:'tracing',importance:0,columnConstructor:TracingColumn},{importance:1,columnConstructor:AllocatorColumn}];Polymer({is:'tr-ui-a-memory-dump-overview-pane',behaviors:[tr.ui.analysis.StackedPane],created:function(){this.processMemoryDumps_=undefined;this.aggregationMode_=undefined;},ready:function(){this.$.table.selectionMode=tr.ui.b.TableFormat.SelectionMode.CELL;this.$.table.addEventListener('selection-changed',function(tableEvent){tableEvent.stopPropagation();this.changeChildPane_();}.bind(this));},set processMemoryDumps(processMemoryDumps){this.processMemoryDumps_=processMemoryDumps;this.scheduleRebuild_();},get processMemoryDumps(){return this.processMemoryDumps_;},set aggregationMode(aggregationMode){this.aggregationMode_=aggregationMode;this.scheduleRebuild_();},get aggregationMode(){return this.aggregationMode_;},get selectedMemoryCell(){if(this.processMemoryDumps_===undefined||this.processMemoryDumps_.length===0){return undefined;}
-var selectedTableRow=this.$.table.selectedTableRow;if(!selectedTableRow)
-return undefined;var selectedColumnIndex=this.$.table.selectedColumnIndex;if(selectedColumnIndex===undefined)
-return undefined;var selectedColumn=this.$.table.tableColumns[selectedColumnIndex];var selectedMemoryCell=selectedColumn.cell(selectedTableRow);return selectedMemoryCell;},changeChildPane_:function(){this.storeSelection_();this.childPaneBuilder=this.determineChildPaneBuilderFromSelection_();},determineChildPaneBuilderFromSelection_:function(){if(this.processMemoryDumps_===undefined||this.processMemoryDumps_.length===0){return undefined;}
-var selectedTableRow=this.$.table.selectedTableRow;if(!selectedTableRow)
-return undefined;var selectedColumnIndex=this.$.table.selectedColumnIndex;if(selectedColumnIndex===undefined)
-return undefined;var selectedColumn=this.$.table.tableColumns[selectedColumnIndex];return selectedColumn.getChildPaneBuilder(selectedTableRow.contexts);},onRebuild_:function(){if(this.processMemoryDumps_===undefined||this.processMemoryDumps_.length===0){this.$.info_text.style.display='block';this.$.table.style.display='none';this.$.table.clear();this.$.table.rebuild();return;}
-this.$.info_text.style.display='none';this.$.table.style.display='block';var rows=this.createRows_();var columns=this.createColumns_(rows);var footerRows=this.createFooterRows_(rows,columns);this.$.table.tableRows=rows;this.$.table.footerRows=footerRows;this.$.table.tableColumns=columns;this.$.table.rebuild();this.restoreSelection_();},createRows_:function(){var timeToPidToProcessMemoryDump=this.processMemoryDumps_;var pidToTimeToProcessMemoryDump=tr.b.invertArrayOfDicts(timeToPidToProcessMemoryDump);return tr.b.dictionaryValues(tr.b.mapItems(pidToTimeToProcessMemoryDump,function(pid,timeToDump){var process=tr.b.findFirstInArray(timeToDump).process;var usedMemoryCells=tr.ui.analysis.createCells(timeToDump,function(dump){var sizes={};var totals=dump.totals;if(totals!==undefined){tr.b.iterItems(UsedMemoryColumn.TOTALS_MAP,function(totalName,cellName){var total=totals[totalName];if(total===undefined)
-return;sizes[cellName]=new ScalarNumeric(sizeInBytes_smallerIsBetter,total);});var platformSpecific=totals.platformSpecific;if(platformSpecific!==undefined){tr.b.iterItems(platformSpecific,function(name,size){if(name.endsWith(PLATFORM_SPECIFIC_TOTAL_NAME_SUFFIX)){name=name.substring(0,name.length-
+if(missingSizeCount>0){var infoQuantifier=missingSizeCount<numerics.length?' '+SOME_TIMESTAMPS_INFO_QUANTIFIER:'';infos.push(tr.ui.analysis.createWarningInfo('Size was not provided'+infoQuantifier+'.'));}},getChildPaneBuilder:function(processMemoryDumps){if(processMemoryDumps===undefined)return undefined;var memoryAllocatorDumps=lazyMap(processMemoryDumps,function(pmd){if(pmd===undefined)return undefined;return pmd.getMemoryAllocatorDumpByFullName(this.name);},this);if(memoryAllocatorDumps===undefined)return undefined;var heapDumps=lazyMap(processMemoryDumps,function(pmd){if(pmd===undefined||pmd.heapDumps===undefined)return undefined;return pmd.heapDumps[this.name];},this);return function(){var pane=document.createElement('tr-ui-a-memory-dump-allocator-details-pane');pane.memoryAllocatorDumps=memoryAllocatorDumps;pane.heapDumps=heapDumps;pane.aggregationMode=this.aggregationMode;return pane;}.bind(this);}};function TracingColumn(name,cellPath,aggregationMode){AllocatorColumn.call(this,name,cellPath,aggregationMode);}
+TracingColumn.COLOR=MemoryColumnColorScheme.getColor('tracing_memory_column').toString();TracingColumn.prototype={__proto__:AllocatorColumn.prototype,get title(){return tr.ui.b.createSpan({textContent:this.name,color:TracingColumn.COLOR});},color:function(numerics,processMemoryDumps){return TracingColumn.COLOR;}};AllocatorColumn.RULES=[{condition:'tracing',importance:0,columnConstructor:TracingColumn},{importance:1,columnConstructor:AllocatorColumn}];Polymer({is:'tr-ui-a-memory-dump-overview-pane',behaviors:[tr.ui.analysis.StackedPane],created:function(){this.processMemoryDumps_=undefined;this.aggregationMode_=undefined;},ready:function(){this.$.table.selectionMode=tr.ui.b.TableFormat.SelectionMode.CELL;this.$.table.addEventListener('selection-changed',function(tableEvent){tableEvent.stopPropagation();this.changeChildPane_();}.bind(this));},set processMemoryDumps(processMemoryDumps){this.processMemoryDumps_=processMemoryDumps;this.scheduleRebuild_();},get processMemoryDumps(){return this.processMemoryDumps_;},set aggregationMode(aggregationMode){this.aggregationMode_=aggregationMode;this.scheduleRebuild_();},get aggregationMode(){return this.aggregationMode_;},get selectedMemoryCell(){if(this.processMemoryDumps_===undefined||this.processMemoryDumps_.length===0){return undefined;}
+var selectedTableRow=this.$.table.selectedTableRow;if(!selectedTableRow)return undefined;var selectedColumnIndex=this.$.table.selectedColumnIndex;if(selectedColumnIndex===undefined)return undefined;var selectedColumn=this.$.table.tableColumns[selectedColumnIndex];var selectedMemoryCell=selectedColumn.cell(selectedTableRow);return selectedMemoryCell;},changeChildPane_:function(){this.storeSelection_();this.childPaneBuilder=this.determineChildPaneBuilderFromSelection_();},determineChildPaneBuilderFromSelection_:function(){if(this.processMemoryDumps_===undefined||this.processMemoryDumps_.length===0){return undefined;}
+var selectedTableRow=this.$.table.selectedTableRow;if(!selectedTableRow)return undefined;var selectedColumnIndex=this.$.table.selectedColumnIndex;if(selectedColumnIndex===undefined)return undefined;var selectedColumn=this.$.table.tableColumns[selectedColumnIndex];return selectedColumn.getChildPaneBuilder(selectedTableRow.contexts);},onRebuild_:function(){if(this.processMemoryDumps_===undefined||this.processMemoryDumps_.length===0){this.$.info_text.style.display='block';this.$.table.style.display='none';this.$.table.clear();this.$.table.rebuild();return;}
+this.$.info_text.style.display='none';this.$.table.style.display='block';var rows=this.createRows_();var columns=this.createColumns_(rows);var footerRows=this.createFooterRows_(rows,columns);this.$.table.tableRows=rows;this.$.table.footerRows=footerRows;this.$.table.tableColumns=columns;this.$.table.rebuild();this.restoreSelection_();},createRows_:function(){var timeToPidToProcessMemoryDump=this.processMemoryDumps_;var pidToTimeToProcessMemoryDump=tr.b.invertArrayOfDicts(timeToPidToProcessMemoryDump);return tr.b.dictionaryValues(tr.b.mapItems(pidToTimeToProcessMemoryDump,function(pid,timeToDump){var process=tr.b.findFirstInArray(timeToDump).process;var usedMemoryCells=tr.ui.analysis.createCells(timeToDump,function(dump){var sizes={};var totals=dump.totals;if(totals!==undefined){for(var[totalName,cellName]of
+Object.entries(UsedMemoryColumn.TOTALS_MAP)){var total=totals[totalName];if(total===undefined)continue;sizes[cellName]=new Scalar(sizeInBytes_smallerIsBetter,total);}
+var platformSpecific=totals.platformSpecific;if(platformSpecific!==undefined){for(var[name,size]of
+Object.entries(platformSpecific)){if(name.endsWith(PLATFORM_SPECIFIC_TOTAL_NAME_SUFFIX)){name=name.substring(0,name.length-
 PLATFORM_SPECIFIC_TOTAL_NAME_SUFFIX.length);}
-name=name.replace('_',' ').trim();name=name.charAt(0).toUpperCase()+name.slice(1);sizes[name]=new ScalarNumeric(sizeInBytes_smallerIsBetter,size);});}}
-var vmRegions=dump.mostRecentVmRegions;if(vmRegions!==undefined){tr.b.iterItems(UsedMemoryColumn.BYTE_STAT_MAP,function(byteStatName,cellName){var byteStat=vmRegions.byteStats[byteStatName];if(byteStat===undefined)
-return;sizes[cellName]=new ScalarNumeric(sizeInBytes_smallerIsBetter,byteStat);});}
-return sizes;});var allocatorCells=tr.ui.analysis.createCells(timeToDump,function(dump){var memoryAllocatorDumps=dump.memoryAllocatorDumps;if(memoryAllocatorDumps===undefined)
-return undefined;var sizes={};memoryAllocatorDumps.forEach(function(allocatorDump){var rootDisplayedSizeNumeric=allocatorDump.numerics[DISPLAYED_SIZE_NUMERIC_NAME];if(rootDisplayedSizeNumeric===undefined){rootDisplayedSizeNumeric=new ScalarNumeric(sizeInBytes_smallerIsBetter,0);}
-sizes[allocatorDump.fullName]=rootDisplayedSizeNumeric;});return sizes;});return{title:process.userFriendlyName,contexts:timeToDump,usedMemoryCells:usedMemoryCells,allocatorCells:allocatorCells};}));},createFooterRows_:function(rows,columns){if(rows.length<=1)
-return[];var totalRow={title:'Total'};tr.ui.analysis.aggregateTableRowCells(totalRow,rows,columns);return[totalRow];},createColumns_:function(rows){var titleColumn=new ProcessNameColumn();titleColumn.width='200px';var usedMemorySizeColumns=tr.ui.analysis.MemoryColumn.fromRows(rows,'usedMemoryCells',this.aggregationMode_,UsedMemoryColumn.RULES);var allocatorSizeColumns=tr.ui.analysis.MemoryColumn.fromRows(rows,'allocatorCells',this.aggregationMode_,AllocatorColumn.RULES);var sizeColumns=usedMemorySizeColumns.concat(allocatorSizeColumns);tr.ui.analysis.MemoryColumn.spaceEqually(sizeColumns);var columns=[titleColumn].concat(sizeColumns);return columns;},storeSelection_:function(){var selectedRowTitle;var selectedRow=this.$.table.selectedTableRow;if(selectedRow!==undefined)
-selectedRowTitle=selectedRow.title;var selectedColumnName;var selectedColumnIndex=this.$.table.selectedColumnIndex;if(selectedColumnIndex!==undefined){var selectedColumn=this.$.table.tableColumns[selectedColumnIndex];selectedColumnName=selectedColumn.name;}
-this.$.state.set({rowTitle:selectedRowTitle,columnName:selectedColumnName});},restoreSelection_:function(){var settings=this.$.state.get();if(settings===undefined||settings.rowTitle===undefined||settings.columnName===undefined)
-return;var selectedColumnName=settings.columnName;var selectedColumnIndex=tr.b.findFirstIndexInArray(this.$.table.tableColumns,function(column){return column.name===selectedColumnName;});if(selectedColumnIndex<0)
-return;var selectedRowTitle=settings.rowTitle;var selectedRow=tr.b.findFirstInArray(this.$.table.tableRows,function(row){return row.title===selectedRowTitle;});if(selectedRow===undefined)
-return;this.$.table.selectedTableRow=selectedRow;this.$.table.selectedColumnIndex=selectedColumnIndex;}});return{ProcessNameColumn:ProcessNameColumn,UsedMemoryColumn:UsedMemoryColumn,PeakMemoryColumn:PeakMemoryColumn,ByteStatColumn:ByteStatColumn,AllocatorColumn:AllocatorColumn,TracingColumn:TracingColumn};});'use strict';tr.exportTo('tr.ui.analysis',function(){Polymer({is:'tr-ui-a-memory-dump-header-pane',behaviors:[tr.ui.analysis.StackedPane],created:function(){this.containerMemoryDumps_=undefined;},ready:function(){Polymer.dom(this.$.aggregation_mode_container).appendChild(tr.ui.b.createSelector(this,'aggregationMode','memoryDumpHeaderPane.aggregationMode',tr.ui.analysis.MemoryColumn.AggregationMode.DIFF,[{label:'Diff',value:tr.ui.analysis.MemoryColumn.AggregationMode.DIFF},{label:'Max',value:tr.ui.analysis.MemoryColumn.AggregationMode.MAX}]));},set containerMemoryDumps(containerMemoryDumps){this.containerMemoryDumps_=containerMemoryDumps;this.scheduleRebuild_();},get containerMemoryDumps(){return this.containerMemoryDumps_;},set aggregationMode(aggregationMode){this.aggregationMode_=aggregationMode;this.scheduleRebuild_();},get aggregationMode(){return this.aggregationMode_;},onRebuild_:function(){this.updateLabel_();this.updateAggregationModeSelector_();this.changeChildPane_();},updateLabel_:function(){Polymer.dom(this.$.label).textContent='';if(this.containerMemoryDumps_===undefined||this.containerMemoryDumps_.length<=0){Polymer.dom(this.$.label).textContent='No memory dumps selected';return;}
+name=name.replace('_',' ').trim();name=name.charAt(0).toUpperCase()+name.slice(1);sizes[name]=new Scalar(sizeInBytes_smallerIsBetter,size);}}}
+var vmRegions=dump.mostRecentVmRegions;if(vmRegions!==undefined){for(var[byteStatName,cellName]of
+Object.entries(UsedMemoryColumn.BYTE_STAT_MAP)){var byteStat=vmRegions.byteStats[byteStatName];if(byteStat===undefined)continue;sizes[cellName]=new Scalar(sizeInBytes_smallerIsBetter,byteStat);}}
+return sizes;});var allocatorCells=tr.ui.analysis.createCells(timeToDump,function(dump){var memoryAllocatorDumps=dump.memoryAllocatorDumps;if(memoryAllocatorDumps===undefined)return undefined;var sizes={};memoryAllocatorDumps.forEach(function(allocatorDump){var rootDisplayedSizeNumeric=allocatorDump.numerics[DISPLAYED_SIZE_NUMERIC_NAME];if(rootDisplayedSizeNumeric===undefined){rootDisplayedSizeNumeric=new Scalar(sizeInBytes_smallerIsBetter,0);}
+sizes[allocatorDump.fullName]=rootDisplayedSizeNumeric;});return sizes;});return{title:process.userFriendlyName,contexts:timeToDump,usedMemoryCells:usedMemoryCells,allocatorCells:allocatorCells};}));},createFooterRows_:function(rows,columns){if(rows.length<=1)return[];var totalRow={title:'Total'};tr.ui.analysis.aggregateTableRowCells(totalRow,rows,columns);return[totalRow];},createColumns_:function(rows){var titleColumn=new ProcessNameColumn();titleColumn.width='200px';var usedMemorySizeColumns=tr.ui.analysis.MemoryColumn.fromRows(rows,{cellKey:'usedMemoryCells',aggregationMode:this.aggregationMode_,rules:UsedMemoryColumn.RULES});var allocatorSizeColumns=tr.ui.analysis.MemoryColumn.fromRows(rows,{cellKey:'allocatorCells',aggregationMode:this.aggregationMode_,rules:AllocatorColumn.RULES});var sizeColumns=usedMemorySizeColumns.concat(allocatorSizeColumns);tr.ui.analysis.MemoryColumn.spaceEqually(sizeColumns);var columns=[titleColumn].concat(sizeColumns);return columns;},storeSelection_:function(){var selectedRowTitle;var selectedRow=this.$.table.selectedTableRow;if(selectedRow!==undefined){selectedRowTitle=selectedRow.title;}
+var selectedColumnName;var selectedColumnIndex=this.$.table.selectedColumnIndex;if(selectedColumnIndex!==undefined){var selectedColumn=this.$.table.tableColumns[selectedColumnIndex];selectedColumnName=selectedColumn.name;}
+this.$.state.set({rowTitle:selectedRowTitle,columnName:selectedColumnName});},restoreSelection_:function(){var settings=this.$.state.get();if(settings===undefined||settings.rowTitle===undefined||settings.columnName===undefined){return;}
+var selectedColumnName=settings.columnName;var selectedColumnIndex=tr.b.findFirstIndexInArray(this.$.table.tableColumns,function(column){return column.name===selectedColumnName;});if(selectedColumnIndex<0)return;var selectedRowTitle=settings.rowTitle;var selectedRow=tr.b.findFirstInArray(this.$.table.tableRows,function(row){return row.title===selectedRowTitle;});if(selectedRow===undefined)return;this.$.table.selectedTableRow=selectedRow;this.$.table.selectedColumnIndex=selectedColumnIndex;}});return{ProcessNameColumn,UsedMemoryColumn,PeakMemoryColumn,ByteStatColumn,AllocatorColumn,TracingColumn,};});'use strict';tr.exportTo('tr.ui.analysis',function(){Polymer({is:'tr-ui-a-memory-dump-header-pane',behaviors:[tr.ui.analysis.StackedPane],created:function(){this.containerMemoryDumps_=undefined;},ready:function(){Polymer.dom(this.$.aggregation_mode_container).appendChild(tr.ui.b.createSelector(this,'aggregationMode','memoryDumpHeaderPane.aggregationMode',tr.ui.analysis.MemoryColumn.AggregationMode.DIFF,[{label:'Diff',value:tr.ui.analysis.MemoryColumn.AggregationMode.DIFF},{label:'Max',value:tr.ui.analysis.MemoryColumn.AggregationMode.MAX}]));},set containerMemoryDumps(containerMemoryDumps){this.containerMemoryDumps_=containerMemoryDumps;this.scheduleRebuild_();},get containerMemoryDumps(){return this.containerMemoryDumps_;},set aggregationMode(aggregationMode){this.aggregationMode_=aggregationMode;this.scheduleRebuild_();},get aggregationMode(){return this.aggregationMode_;},onRebuild_:function(){this.updateLabel_();this.updateAggregationModeSelector_();this.changeChildPane_();},updateLabel_:function(){Polymer.dom(this.$.label).textContent='';if(this.containerMemoryDumps_===undefined||this.containerMemoryDumps_.length<=0){Polymer.dom(this.$.label).textContent='No memory dumps selected';return;}
 var containerDumpCount=this.containerMemoryDumps_.length;var isMultiSelection=containerDumpCount>1;Polymer.dom(this.$.label).appendChild(document.createTextNode('Selected '+containerDumpCount+' memory dump'+
-(isMultiSelection?'s':'')+' in '+this.containerMemoryDumps_[0].containerName+' at '));Polymer.dom(this.$.label).appendChild(document.createTextNode(tr.b.Unit.byName.timeStampInMs.format(this.containerMemoryDumps_[0].start)));if(isMultiSelection){var ELLIPSIS=String.fromCharCode(8230);Polymer.dom(this.$.label).appendChild(document.createTextNode(ELLIPSIS));Polymer.dom(this.$.label).appendChild(document.createTextNode(tr.b.Unit.byName.timeStampInMs.format(this.containerMemoryDumps_[containerDumpCount-1].start)));}},updateAggregationModeSelector_:function(){var displayStyle;if(this.containerMemoryDumps_===undefined||this.containerMemoryDumps_.length<=1)
-displayStyle='none';else
-displayStyle='initial';this.$.aggregation_mode_container.style.display=displayStyle;},changeChildPane_:function(){this.childPaneBuilder=function(){if(this.containerMemoryDumps_===undefined||this.containerMemoryDumps_.length<=0)
-return undefined;var overviewPane=document.createElement('tr-ui-a-memory-dump-overview-pane');overviewPane.processMemoryDumps=this.containerMemoryDumps_.map(function(containerDump){return containerDump.processMemoryDumps;});overviewPane.aggregationMode=this.aggregationMode;return overviewPane;}.bind(this);}});return{};});'use strict';Polymer({is:'tr-ui-a-stacked-pane-view',setPaneBuilder:function(paneBuilder,opt_parentPane){var paneContainer=this.$.pane_container;if(opt_parentPane){if(!(opt_parentPane instanceof HTMLElement))
-throw new Error('Parent pane must be an HTML element');if(opt_parentPane.parentElement!==paneContainer)
-throw new Error('Parent pane must be a child of the pane container');}
-while(Polymer.dom(paneContainer).lastElementChild!==null&&Polymer.dom(paneContainer).lastElementChild!==opt_parentPane){var removedPane=Polymer.dom(this.$.pane_container).lastElementChild;var listener=this.listeners_.get(removedPane);if(listener===undefined)
-throw new Error('No listener associated with pane');this.listeners_.delete(removedPane);removedPane.removeEventListener('request-child-pane-change',listener);Polymer.dom(paneContainer).removeChild(removedPane);}
-if(opt_parentPane&&opt_parentPane.parentElement!==paneContainer)
-throw new Error('Parent pane was removed from the pane container');if(!paneBuilder)
-return;var pane=paneBuilder();if(!pane)
-return;if(!(pane instanceof HTMLElement))
-throw new Error('Pane must be an HTML element');var listener=function(event){this.setPaneBuilder(pane.childPaneBuilder,pane);}.bind(this);if(!this.listeners_){this.listeners_=new WeakMap();}
+(isMultiSelection?'s':'')+' in '+this.containerMemoryDumps_[0].containerName+' at '));Polymer.dom(this.$.label).appendChild(document.createTextNode(tr.b.Unit.byName.timeStampInMs.format(this.containerMemoryDumps_[0].start)));if(isMultiSelection){var ELLIPSIS=String.fromCharCode(8230);Polymer.dom(this.$.label).appendChild(document.createTextNode(ELLIPSIS));Polymer.dom(this.$.label).appendChild(document.createTextNode(tr.b.Unit.byName.timeStampInMs.format(this.containerMemoryDumps_[containerDumpCount-1].start)));}},updateAggregationModeSelector_:function(){var displayStyle;if(this.containerMemoryDumps_===undefined||this.containerMemoryDumps_.length<=1){displayStyle='none';}else{displayStyle='initial';}
+this.$.aggregation_mode_container.style.display=displayStyle;},changeChildPane_:function(){this.childPaneBuilder=function(){if(this.containerMemoryDumps_===undefined||this.containerMemoryDumps_.length<=0){return undefined;}
+var overviewPane=document.createElement('tr-ui-a-memory-dump-overview-pane');overviewPane.processMemoryDumps=this.containerMemoryDumps_.map(function(containerDump){return containerDump.processMemoryDumps;});overviewPane.aggregationMode=this.aggregationMode;return overviewPane;}.bind(this);}});return{};});'use strict';Polymer({is:'tr-ui-a-stacked-pane-view',setPaneBuilder:function(paneBuilder,opt_parentPane){var paneContainer=this.$.pane_container;if(opt_parentPane){if(!(opt_parentPane instanceof HTMLElement)){throw new Error('Parent pane must be an HTML element');}
+if(opt_parentPane.parentElement!==paneContainer){throw new Error('Parent pane must be a child of the pane container');}}
+while(Polymer.dom(paneContainer).lastElementChild!==null&&Polymer.dom(paneContainer).lastElementChild!==opt_parentPane){var removedPane=Polymer.dom(this.$.pane_container).lastElementChild;var listener=this.listeners_.get(removedPane);if(listener===undefined){throw new Error('No listener associated with pane');}
+this.listeners_.delete(removedPane);removedPane.removeEventListener('request-child-pane-change',listener);Polymer.dom(paneContainer).removeChild(removedPane);}
+if(opt_parentPane&&opt_parentPane.parentElement!==paneContainer){throw new Error('Parent pane was removed from the pane container');}
+if(!paneBuilder)return;var pane=paneBuilder();if(!pane)return;if(!(pane instanceof HTMLElement)){throw new Error('Pane must be an HTML element');}
+var listener=function(event){this.setPaneBuilder(pane.childPaneBuilder,pane);}.bind(this);if(!this.listeners_){this.listeners_=new WeakMap();}
 this.listeners_.set(pane,listener);pane.addEventListener('request-child-pane-change',listener);Polymer.dom(paneContainer).appendChild(pane);pane.appended();},rebuild:function(){var currentPane=Polymer.dom(this.$.pane_container).firstElementChild;while(currentPane){currentPane.rebuild();currentPane=currentPane.nextElementSibling;}},get panesForTesting(){var panes=[];var currentChild=Polymer.dom(this.$.pane_container).firstElementChild;while(currentChild){panes.push(currentChild);currentChild=currentChild.nextElementSibling;}
 return panes;}});'use strict';tr.exportTo('tr.ui.analysis',function(){Polymer({is:'tr-ui-a-container-memory-dump-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],set selection(selection){if(selection===undefined){this.currentSelection_=undefined;this.dumpsByContainerName_=undefined;this.updateContents_();return;}
-selection.forEach(function(event){if(!(event instanceof tr.model.ContainerMemoryDump)){throw new Error('Memory dump sub-view only supports container memory dumps');}});this.currentSelection_=selection;this.dumpsByContainerName_=tr.b.group(this.currentSelection_.toArray(),function(dump){return dump.containerName;});tr.b.iterItems(this.dumpsByContainerName_,function(containerName,dumps){dumps.sort(function(a,b){return a.start-b.start;});});this.updateContents_();},get selection(){return this.currentSelection_;},get requiresTallView(){return true;},updateContents_:function(){Polymer.dom(this.$.content).textContent='';if(this.dumpsByContainerName_===undefined)
-return;var containerNames=Object.keys(this.dumpsByContainerName_);if(containerNames.length===0)
-return;if(containerNames.length>1)
-this.buildViewForMultipleContainerNames_();else
-this.buildViewForSingleContainerName_();},buildViewForSingleContainerName_:function(){var containerMemoryDumps=tr.b.dictionaryValues(this.dumpsByContainerName_)[0];var dumpView=this.ownerDocument.createElement('tr-ui-a-stacked-pane-view');Polymer.dom(this.$.content).appendChild(dumpView);dumpView.setPaneBuilder(function(){var headerPane=document.createElement('tr-ui-a-memory-dump-header-pane');headerPane.containerMemoryDumps=containerMemoryDumps;return headerPane;});},buildViewForMultipleContainerNames_:function(){var ownerDocument=this.ownerDocument;var rows=tr.b.dictionaryValues(tr.b.mapItems(this.dumpsByContainerName_,function(containerName,dumps){return{containerName:containerName,subRows:dumps,isExpanded:true};}));rows.sort(function(a,b){return a.containerName.localeCompare(b.containerName);});var columns=[{title:'Dump',value:function(row){if(row.subRows===undefined)
-return this.singleDumpValue_(row);else
+selection.forEach(function(event){if(!(event instanceof tr.model.ContainerMemoryDump)){throw new Error('Memory dump sub-view only supports container memory dumps');}});this.currentSelection_=selection;this.dumpsByContainerName_=tr.b.group(this.currentSelection_.toArray(),dump=>dump.containerName);for(var dumps of Object.values(this.dumpsByContainerName_)){dumps.sort((a,b)=>a.start-b.start);}
+this.updateContents_();},get selection(){return this.currentSelection_;},get requiresTallView(){return true;},updateContents_:function(){Polymer.dom(this.$.content).textContent='';if(this.dumpsByContainerName_===undefined)return;var containerNames=Object.keys(this.dumpsByContainerName_);if(containerNames.length===0)return;if(containerNames.length>1){this.buildViewForMultipleContainerNames_();}else{this.buildViewForSingleContainerName_();}},buildViewForSingleContainerName_:function(){var containerMemoryDumps=tr.b.dictionaryValues(this.dumpsByContainerName_)[0];var dumpView=this.ownerDocument.createElement('tr-ui-a-stacked-pane-view');Polymer.dom(this.$.content).appendChild(dumpView);dumpView.setPaneBuilder(function(){var headerPane=document.createElement('tr-ui-a-memory-dump-header-pane');headerPane.containerMemoryDumps=containerMemoryDumps;return headerPane;});},buildViewForMultipleContainerNames_:function(){var ownerDocument=this.ownerDocument;var rows=tr.b.dictionaryValues(tr.b.mapItems(this.dumpsByContainerName_,function(containerName,dumps){return{containerName:containerName,subRows:dumps,isExpanded:true};}));rows.sort(function(a,b){return a.containerName.localeCompare(b.containerName);});var columns=[{title:'Dump',value:function(row){if(row.subRows===undefined){return this.singleDumpValue_(row);}
 return this.groupedDumpValue_(row);},singleDumpValue_:function(row){var linkEl=ownerDocument.createElement('tr-ui-a-analysis-link');linkEl.setSelectionAndContent(new tr.model.EventSet([row]));Polymer.dom(linkEl).appendChild(tr.v.ui.createScalarSpan(row.start,{unit:tr.b.Unit.byName.timeStampInMs,ownerDocument:ownerDocument}));return linkEl;},groupedDumpValue_:function(row){var linkEl=ownerDocument.createElement('tr-ui-a-analysis-link');linkEl.setSelectionAndContent(new tr.model.EventSet(row.subRows));Polymer.dom(linkEl).appendChild(tr.ui.b.createSpan({ownerDocument:ownerDocument,textContent:row.subRows.length+' memory dump'+
-(row.subRows.length===1?'':'s')+' in '}));Polymer.dom(linkEl).appendChild(tr.ui.b.createSpan({ownerDocument:ownerDocument,textContent:row.containerName,bold:true}));return linkEl;}}];var table=this.ownerDocument.createElement('tr-ui-b-table');table.tableColumns=columns;table.tableRows=rows;table.showHeader=false;table.rebuild();Polymer.dom(this.$.content).appendChild(table);}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-container-memory-dump-sub-view',tr.model.GlobalMemoryDump,{multi:false,title:'Global Memory Dump',});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-container-memory-dump-sub-view',tr.model.GlobalMemoryDump,{multi:true,title:'Global Memory Dumps',});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-container-memory-dump-sub-view',tr.model.ProcessMemoryDump,{multi:false,title:'Process Memory Dump',});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-container-memory-dump-sub-view',tr.model.ProcessMemoryDump,{multi:true,title:'Process Memory Dumps',});return{};});'use strict';(function(){var COUNTER_SAMPLE_TABLE_COLUMNS=[{title:'Counter',width:'150px',value:function(row){return row.counter;}},{title:'Series',width:'150px',value:function(row){return row.series;}},{title:'Time',width:'150px',value:function(row){return row.start;}},{title:'Value',width:'100%',value:function(row){return row.value;}}];Polymer({is:'tr-ui-a-counter-sample-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],ready:function(){this.currentSelection_=undefined;this.$.table.tableColumns=COUNTER_SAMPLE_TABLE_COLUMNS;},get selection(){return this.currentSelection_;},set selection(selection){this.currentSelection_=selection;this.updateContents_();},updateContents_:function(){this.$.table.tableRows=this.selection?this.getRows_(this.selection.toArray()):[];this.$.table.rebuild();},getRows_:function(samples){var samplesByCounter=tr.b.group(samples,function(sample){return sample.series.counter.guid;});var rows=[];tr.b.iterItems(samplesByCounter,function(unused,counterSamples){var samplesBySeries=tr.b.group(counterSamples,function(sample){return sample.series.guid;});tr.b.iterItems(samplesBySeries,function(unused,seriesSamples){var seriesRows=this.getRowsForSamples_(seriesSamples);seriesRows[0].counter=seriesSamples[0].series.counter.name;seriesRows[0].series=seriesSamples[0].series.name;if(seriesRows.length>1){seriesRows[0].subRows=seriesRows.slice(1);seriesRows[0].isExpanded=true;}
-rows.push(seriesRows[0]);},this);},this);return rows;},getRowsForSamples_:function(samples){return samples.map(function(sample){return{start:sample.timestamp,value:sample.value};});}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-counter-sample-sub-view',tr.model.CounterSample,{multi:false,title:'Counter Sample',});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-counter-sample-sub-view',tr.model.CounterSample,{multi:true,title:'Counter Samples',});})();'use strict';tr.exportTo('tr.ui.analysis',function(){function MultiEventSummary(title,events){this.title=title;this.duration_=undefined;this.selfTime_=undefined;this.events_=events;this.cpuTimesComputed_=false;this.cpuSelfTime_=undefined;this.cpuDuration_=undefined;this.maxDuration_=undefined;this.maxCpuDuration_=undefined;this.maxSelfTime_=undefined;this.maxCpuSelfTime_=undefined;this.untotallableArgs_=[];this.totalledArgs_=undefined;};MultiEventSummary.prototype={set title(title){if(title=='Totals')
-this.totalsRow=true;this.title_=title;},get title(){return this.title_;},get duration(){if(this.duration_===undefined){this.duration_=tr.b.Statistics.sum(this.events_,function(event){return event.duration;});}
-return this.duration_;},get cpuSelfTime(){this.computeCpuTimesIfNeeded_();return this.cpuSelfTime_;},get cpuDuration(){this.computeCpuTimesIfNeeded_();return this.cpuDuration_;},computeCpuTimesIfNeeded_:function(){if(this.cpuTimesComputed_)
-return;this.cpuTimesComputed_=true;var cpuSelfTime=0;var cpuDuration=0;var hasCpuData=false;for(var event of this.events_){if(event.cpuDuration!==undefined){cpuDuration+=event.cpuDuration;hasCpuData=true;}
+(row.subRows.length===1?'':'s')+' in '}));Polymer.dom(linkEl).appendChild(tr.ui.b.createSpan({ownerDocument:ownerDocument,textContent:row.containerName,bold:true}));return linkEl;}}];var table=this.ownerDocument.createElement('tr-ui-b-table');table.tableColumns=columns;table.tableRows=rows;table.showHeader=false;table.rebuild();Polymer.dom(this.$.content).appendChild(table);}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-container-memory-dump-sub-view',tr.model.GlobalMemoryDump,{multi:false,title:'Global Memory Dump',});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-container-memory-dump-sub-view',tr.model.GlobalMemoryDump,{multi:true,title:'Global Memory Dumps',});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-container-memory-dump-sub-view',tr.model.ProcessMemoryDump,{multi:false,title:'Process Memory Dump',});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-container-memory-dump-sub-view',tr.model.ProcessMemoryDump,{multi:true,title:'Process Memory Dumps',});return{};});'use strict';(function(){var COUNTER_SAMPLE_TABLE_COLUMNS=[{title:'Counter',width:'150px',value:function(row){return row.counter;}},{title:'Series',width:'150px',value:function(row){return row.series;}},{title:'Time',width:'150px',value:function(row){return row.start;}},{title:'Value',width:'100%',value:function(row){return row.value;}}];Polymer({is:'tr-ui-a-counter-sample-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],ready:function(){this.currentSelection_=undefined;this.$.table.tableColumns=COUNTER_SAMPLE_TABLE_COLUMNS;},get selection(){return this.currentSelection_;},set selection(selection){this.currentSelection_=selection;this.updateContents_();},updateContents_:function(){this.$.table.tableRows=this.selection?this.getRows_(this.selection.toArray()):[];this.$.table.rebuild();},getRows_:function(samples){var samplesByCounter=tr.b.groupIntoMap(samples,sample=>sample.series.counter.guid);var rows=[];for(var counterSamples of samplesByCounter.values()){var samplesBySeries=tr.b.groupIntoMap(counterSamples,sample=>sample.series.guid);for(var seriesSamples of samplesBySeries.values()){var seriesRows=this.getRowsForSamples_(seriesSamples);seriesRows[0].counter=seriesSamples[0].series.counter.name;seriesRows[0].series=seriesSamples[0].series.name;if(seriesRows.length>1){seriesRows[0].subRows=seriesRows.slice(1);seriesRows[0].isExpanded=true;}
+rows.push(seriesRows[0]);}}
+return rows;},getRowsForSamples_:function(samples){return samples.map(function(sample){return{start:sample.timestamp,value:sample.value};});}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-counter-sample-sub-view',tr.model.CounterSample,{multi:false,title:'Counter Sample',});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-counter-sample-sub-view',tr.model.CounterSample,{multi:true,title:'Counter Samples',});})();'use strict';tr.exportTo('tr.ui.analysis',function(){function MultiEventSummary(title,events){this.title=title;this.duration_=undefined;this.selfTime_=undefined;this.events_=events;this.cpuTimesComputed_=false;this.cpuSelfTime_=undefined;this.cpuDuration_=undefined;this.maxDuration_=undefined;this.maxCpuDuration_=undefined;this.maxSelfTime_=undefined;this.maxCpuSelfTime_=undefined;this.untotallableArgs_=[];this.totalledArgs_=undefined;}
+MultiEventSummary.prototype={set title(title){if(title==='Totals'){this.totalsRow=true;}
+this.title_=title;},get title(){return this.title_;},get duration(){if(this.duration_===undefined){this.duration_=tr.b.math.Statistics.sum(this.events_,function(event){return event.duration;});}
+return this.duration_;},get cpuSelfTime(){this.computeCpuTimesIfNeeded_();return this.cpuSelfTime_;},get cpuDuration(){this.computeCpuTimesIfNeeded_();return this.cpuDuration_;},computeCpuTimesIfNeeded_:function(){if(this.cpuTimesComputed_)return;this.cpuTimesComputed_=true;var cpuSelfTime=0;var cpuDuration=0;var hasCpuData=false;for(var event of this.events_){if(event.cpuDuration!==undefined){cpuDuration+=event.cpuDuration;hasCpuData=true;}
 if(event.cpuSelfTime!==undefined){cpuSelfTime+=event.cpuSelfTime;hasCpuData=true;}}
-if(hasCpuData){this.cpuDuration_=cpuDuration;this.cpuSelfTime_=cpuSelfTime;}},get selfTime(){if(this.selfTime_===undefined){this.selfTime_=0;for(var event of this.events_)
-if(event.selfTime!==undefined)
-this.selfTime_+=event.selfTime;}
-return this.selfTime_;},get events(){return this.events_;},get numEvents(){return this.events_.length;},get numAlerts(){if(this.numAlerts_===undefined){this.numAlerts_=tr.b.Statistics.sum(this.events_,function(event){return event.associatedAlerts.length;});}
-return this.numAlerts_;},get untotallableArgs(){this.updateArgsIfNeeded_();return this.untotallableArgs_;},get totalledArgs(){this.updateArgsIfNeeded_();return this.totalledArgs_;},get maxDuration(){if(this.maxDuration_===undefined){this.maxDuration_=tr.b.Statistics.max(this.events_,function(event){return event.duration;});}
-return this.maxDuration_;},get maxCpuDuration(){if(this.maxCpuDuration_===undefined){this.maxCpuDuration_=tr.b.Statistics.max(this.events_,function(event){return event.cpuDuration;});}
-return this.maxCpuDuration_;},get maxSelfTime(){if(this.maxSelfTime_===undefined){this.maxSelfTime_=tr.b.Statistics.max(this.events_,function(event){return event.selfTime;});}
-return this.maxSelfTime_;},get maxCpuSelfTime(){if(this.maxCpuSelfTime_===undefined){this.maxCpuSelfTime_=tr.b.Statistics.max(this.events_,function(event){return event.cpuSelfTime;});}
-return this.maxCpuSelfTime_;},updateArgsIfNeeded_:function(){if(this.totalledArgs_!==undefined)
-return;var untotallableArgs={};var totalledArgs={};for(var event of this.events_){for(var argName in event.args){var argVal=event.args[argName];var type=typeof argVal;if(type!=='number'){untotallableArgs[argName]=true;delete totalledArgs[argName];continue;}
+if(hasCpuData){this.cpuDuration_=cpuDuration;this.cpuSelfTime_=cpuSelfTime;}},get selfTime(){if(this.selfTime_===undefined){this.selfTime_=0;for(var event of this.events_){if(event.selfTime!==undefined){this.selfTime_+=event.selfTime;}}}
+return this.selfTime_;},get events(){return this.events_;},get numEvents(){return this.events_.length;},get numAlerts(){if(this.numAlerts_===undefined){this.numAlerts_=tr.b.math.Statistics.sum(this.events_,event=>event.associatedAlerts.length);}
+return this.numAlerts_;},get untotallableArgs(){this.updateArgsIfNeeded_();return this.untotallableArgs_;},get totalledArgs(){this.updateArgsIfNeeded_();return this.totalledArgs_;},get maxDuration(){if(this.maxDuration_===undefined){this.maxDuration_=tr.b.math.Statistics.max(this.events_,function(event){return event.duration;});}
+return this.maxDuration_;},get maxCpuDuration(){if(this.maxCpuDuration_===undefined){this.maxCpuDuration_=tr.b.math.Statistics.max(this.events_,function(event){return event.cpuDuration;});}
+return this.maxCpuDuration_;},get maxSelfTime(){if(this.maxSelfTime_===undefined){this.maxSelfTime_=tr.b.math.Statistics.max(this.events_,function(event){return event.selfTime;});}
+return this.maxSelfTime_;},get maxCpuSelfTime(){if(this.maxCpuSelfTime_===undefined){this.maxCpuSelfTime_=tr.b.math.Statistics.max(this.events_,function(event){return event.cpuSelfTime;});}
+return this.maxCpuSelfTime_;},updateArgsIfNeeded_:function(){if(this.totalledArgs_!==undefined)return;var untotallableArgs={};var totalledArgs={};for(var event of this.events_){for(var argName in event.args){var argVal=event.args[argName];var type=typeof argVal;if(type!=='number'){untotallableArgs[argName]=true;delete totalledArgs[argName];continue;}
 if(untotallableArgs[argName]){continue;}
-if(totalledArgs[argName]===undefined)
-totalledArgs[argName]=0;totalledArgs[argName]+=argVal;}}
-this.untotallableArgs_=tr.b.dictionaryKeys(untotallableArgs);this.totalledArgs_=totalledArgs;}};return{MultiEventSummary:MultiEventSummary};});'use strict';Polymer({is:'tr-ui-a-multi-event-details-table',created:function(){this.selection_=undefined;this.eventsHaveDuration_=true;this.eventsHaveSubRows_=true;},ready:function(){this.initTitleTable_();},get eventsHaveDuration(){return this.eventsHaveDuration_;},set eventsHaveDuration(eventsHaveDuration){this.eventsHaveDuration_=eventsHaveDuration;this.updateContents_();},get eventsHaveSubRows(){return this.eventsHaveSubRows_;},set eventsHaveSubRows(eventsHaveSubRows){this.eventsHaveSubRows_=eventsHaveSubRows;this.updateContents_();},get selection(){return this.selection_;},set selection(selection){this.selection_=selection;this.updateContents_();},updateContents_:function(){var selection=this.selection_;this.updateTitleTable_();if(this.selection_===undefined){this.$.table.tableRows=[];this.$.table.tableFooterRows=[];this.$.table.rebuild();return;}
-var summary=new tr.ui.analysis.MultiEventSummary('Totals',this.selection_);this.updateColumns_(summary);this.updateRows_(summary);this.$.table.rebuild();},initTitleTable_:function(){var table=this.$.titletable;table.showHeader=false;table.tableColumns=[{title:'Title',value:function(row){return row.title;},width:'350px'},{title:'Value',width:'100%',value:function(row){return row.value;}}];},getSelectionTitle_:function(){if(!this.selection||!this.selection.length)
-return'<No selection>';var firstTitle=tr.b.getFirstElement(this.selection).title;if(!tr.b.every(this.selection,(event)=>event.title===firstTitle))
-return'<Different titles>';return firstTitle;},updateTitleTable_:function(){this.$.titletable.tableRows=[{title:'Title',value:this.getSelectionTitle_()}];},updateColumns_:function(summary){var hasCpuData;if(summary.cpuDuration!==undefined)
-hasCpuData=true;if(summary.cpuSelfTime!==undefined)
-hasCpuData=true;var colWidthPercentage;if(hasCpuData)
-colWidthPercentage='20%';else
-colWidthPercentage='33.3333%';var ownerDocument=this.ownerDocument;var columns=[];columns.push({title:'Start',value:function(row){if(row.__proto__===tr.ui.analysis.MultiEventSummary.prototype){return row.title;}
-var linkEl=document.createElement('tr-ui-a-analysis-link');linkEl.setSelectionAndContent(function(){return new tr.model.EventSet(row.event);});Polymer.dom(linkEl).appendChild(tr.v.ui.createScalarSpan(row.start,{unit:tr.b.Unit.byName.timeStampInMs,ownerDocument:ownerDocument}));return linkEl;},width:'350px',cmp:function(rowA,rowB){return rowA.start-rowB.start;}});if(this.eventsHaveDuration_){columns.push({title:'Wall Duration (ms)',value:function(row){return tr.v.ui.createScalarSpan(row.duration,{unit:tr.b.Unit.byName.timeDurationInMs,ownerDocument:ownerDocument});},width:'<upated further down>',cmp:function(rowA,rowB){return rowA.duration-rowB.duration;}});}
-if(this.eventsHaveDuration_&&hasCpuData){columns.push({title:'CPU Duration (ms)',value:function(row){return tr.v.ui.createScalarSpan(row.cpuDuration,{unit:tr.b.Unit.byName.timeDurationInMs,ownerDocument:ownerDocument});},width:'<upated further down>',cmp:function(rowA,rowB){return rowA.cpuDuration-rowB.cpuDuration;}});}
-if(this.eventsHaveSubRows_&&this.eventsHaveDuration_){columns.push({title:'Self time (ms)',value:function(row){return tr.v.ui.createScalarSpan(row.selfTime,{unit:tr.b.Unit.byName.timeDurationInMs,ownerDocument:ownerDocument});},width:'<upated further down>',cmp:function(rowA,rowB){return rowA.selfTime-rowB.selfTime;}});}
-if(this.eventsHaveSubRows_&&this.eventsHaveDuration_&&hasCpuData){columns.push({title:'CPU Self Time (ms)',value:function(row){return tr.v.ui.createScalarSpan(row.cpuSelfTime,{unit:tr.b.Unit.byName.timeDurationInMs,ownerDocument:ownerDocument});},width:'<upated further down>',cmp:function(rowA,rowB){return rowA.cpuSelfTime-rowB.cpuSelfTime;}});}
-var argKeys=tr.b.dictionaryKeys(summary.totalledArgs);argKeys.sort();var otherKeys=summary.untotallableArgs.slice(0);otherKeys.sort();argKeys.push.apply(argKeys,otherKeys);var keysWithColumns=argKeys.slice(0,4);var keysInOtherColumn=argKeys.slice(4);keysWithColumns.forEach(function(argKey){var hasTotal=summary.totalledArgs[argKey];var colDesc={title:'Arg: '+argKey,value:function(row){if(row.__proto__!==tr.ui.analysis.MultiEventSummary.prototype){var argView=document.createElement('tr-ui-a-generic-object-view');argView.object=row.args[argKey];return argView;}
-if(hasTotal)
-return row.totalledArgs[argKey];return'';},width:'<upated further down>'};if(hasTotal){colDesc.cmp=function(rowA,rowB){return rowA.args[argKey]-rowB.args[argKey];};}
-columns.push(colDesc);});if(keysInOtherColumn.length){columns.push({title:'Other Args',value:function(row){if(row.__proto__===tr.ui.analysis.MultiEventSummary.prototype)
-return'';var argView=document.createElement('tr-ui-a-generic-object-view');var obj={};for(var i=0;i<keysInOtherColumn.length;i++)
-obj[keysInOtherColumn[i]]=row.args[keysInOtherColumn[i]];argView.object=obj;return argView;},width:'<upated further down>'});}
-var colWidthPercentage;if(columns.length==1)
-colWidthPercentage='100%';else
-colWidthPercentage=(100/(columns.length-1)).toFixed(3)+'%';for(var i=1;i<columns.length;i++)
-columns[i].width=colWidthPercentage;this.$.table.tableColumns=columns;},updateRows_:function(summary){this.$.table.sortColumnIndex=0;function Row(event){this.event=event;}
-Row.prototype={get start(){return this.event.start;},get duration(){return this.event.duration;},get cpuDuration(){return this.event.cpuDuration;},get selfTime(){return this.event.selfTime;},get cpuSelfTime(){return this.event.cpuSelfTime;},get args(){return this.event.args;}};this.$.table.tableRows=this.selection_.map(function(event){return new Row(event);});this.$.table.footerRows=[summary];}});'use strict';Polymer({is:'tr-ui-a-multi-event-summary-table',ready:function(){this.showTotals_=false;this.eventsHaveDuration_=true;this.eventsHaveSubRows_=true;this.eventsByTitle_=undefined;},updateTableColumns_:function(rows,maxValues){var hasCpuData=false;var hasAlerts=false;rows.forEach(function(row){if(row.cpuDuration!==undefined)
-hasCpuData=true;if(row.cpuSelfTime!==undefined)
-hasCpuData=true;if(row.numAlerts)
-hasAlerts=true;});var ownerDocument=this.ownerDocument;var columns=[];columns.push({title:'Name',value:function(row){if(row.title==='Totals')
-return'Totals';var linkEl=document.createElement('tr-ui-a-analysis-link');linkEl.setSelectionAndContent(function(){return new tr.model.EventSet(row.events);},row.title);return linkEl;},width:'350px',cmp:function(rowA,rowB){return rowA.title.localeCompare(rowB.title);}});if(this.eventsHaveDuration_){columns.push({title:'Wall Duration',value:function(row){return tr.v.ui.createScalarSpan(row.duration,{unit:tr.b.Unit.byName.timeDurationInMs,customContextRange:row.totalsRow?undefined:tr.b.Range.fromExplicitRange(0,maxValues.duration),ownerDocument:ownerDocument,rightAlign:true});},width:'<upated further down>',cmp:function(rowA,rowB){return rowA.duration-rowB.duration;}});}
-if(this.eventsHaveDuration_&&hasCpuData){columns.push({title:'CPU Duration',value:function(row){return tr.v.ui.createScalarSpan(row.cpuDuration,{unit:tr.b.Unit.byName.timeDurationInMs,customContextRange:row.totalsRow?undefined:tr.b.Range.fromExplicitRange(0,maxValues.cpuDuration),ownerDocument:ownerDocument,rightAlign:true});},width:'<upated further down>',cmp:function(rowA,rowB){return rowA.cpuDuration-rowB.cpuDuration;}});}
-if(this.eventsHaveSubRows_&&this.eventsHaveDuration_){columns.push({title:'Self time',value:function(row){return tr.v.ui.createScalarSpan(row.selfTime,{unit:tr.b.Unit.byName.timeDurationInMs,customContextRange:row.totalsRow?undefined:tr.b.Range.fromExplicitRange(0,maxValues.selfTime),ownerDocument:ownerDocument,rightAlign:true});},width:'<upated further down>',cmp:function(rowA,rowB){return rowA.selfTime-rowB.selfTime;}});}
-if(this.eventsHaveSubRows_&&this.eventsHaveDuration_&&hasCpuData){columns.push({title:'CPU Self Time',value:function(row){return tr.v.ui.createScalarSpan(row.cpuSelfTime,{unit:tr.b.Unit.byName.timeDurationInMs,customContextRange:row.totalsRow?undefined:tr.b.Range.fromExplicitRange(0,maxValues.cpuSelfTime),ownerDocument:ownerDocument,rightAlign:true});},width:'<upated further down>',cmp:function(rowA,rowB){return rowA.cpuSelfTime-rowB.cpuSelfTime;}});}
-if(this.eventsHaveDuration_){columns.push({title:'Average '+(hasCpuData?'CPU':'Wall')+' Duration',value:function(row){var totalDuration=hasCpuData?row.cpuDuration:row.duration;return tr.v.ui.createScalarSpan(totalDuration/row.numEvents,{unit:tr.b.Unit.byName.timeDurationInMs,customContextRange:row.totalsRow?undefined:tr.b.Range.fromExplicitRange(0,maxValues.duration),ownerDocument:ownerDocument,rightAlign:true});},width:'<upated further down>',cmp:function(rowA,rowB){if(hasCpuData){return rowA.cpuDuration/rowA.numEvents-
-rowB.cpuDuration/rowB.numEvents;}else{return rowA.duration/rowA.numEvents-
-rowB.duration/rowB.numEvents;}}});}
+if(totalledArgs[argName]===undefined){totalledArgs[argName]=0;}
+totalledArgs[argName]+=argVal;}}
+this.untotallableArgs_=Object.keys(untotallableArgs);this.totalledArgs_=totalledArgs;}};return{MultiEventSummary,};});'use strict';Polymer({is:'tr-ui-a-multi-event-summary-table',ready:function(){this.showTotals_=false;this.eventsHaveDuration_=true;this.eventsHaveSubRows_=true;this.eventsByTitle_=undefined;},updateTableColumns_:function(rows,maxValues){var hasCpuData=false;var hasAlerts=false;rows.forEach(function(row){if(row.cpuDuration!==undefined){hasCpuData=true;}
+if(row.cpuSelfTime!==undefined){hasCpuData=true;}
+if(row.numAlerts){hasAlerts=true;}});var ownerDocument=this.ownerDocument;var columns=[];columns.push({title:'Name',value:function(row){if(row.title==='Totals')return'Totals';var linkEl=document.createElement('tr-ui-a-analysis-link');linkEl.setSelectionAndContent(function(){return new tr.model.EventSet(row.events);},row.title);return linkEl;},width:'350px',cmp:function(rowA,rowB){return rowA.title.localeCompare(rowB.title);}});if(this.eventsHaveDuration_){columns.push({title:'Wall Duration',value:function(row){return tr.v.ui.createScalarSpan(row.duration,{unit:tr.b.Unit.byName.timeDurationInMs,customContextRange:row.totalsRow?undefined:tr.b.math.Range.fromExplicitRange(0,maxValues.duration),ownerDocument:ownerDocument,});},width:'<upated further down>',cmp:function(rowA,rowB){return rowA.duration-rowB.duration;}});}
+if(this.eventsHaveDuration_&&hasCpuData){columns.push({title:'CPU Duration',value:function(row){return tr.v.ui.createScalarSpan(row.cpuDuration,{unit:tr.b.Unit.byName.timeDurationInMs,customContextRange:row.totalsRow?undefined:tr.b.math.Range.fromExplicitRange(0,maxValues.cpuDuration),ownerDocument:ownerDocument,});},width:'<upated further down>',cmp:function(rowA,rowB){return rowA.cpuDuration-rowB.cpuDuration;}});}
+if(this.eventsHaveSubRows_&&this.eventsHaveDuration_){columns.push({title:'Self time',value:function(row){return tr.v.ui.createScalarSpan(row.selfTime,{unit:tr.b.Unit.byName.timeDurationInMs,customContextRange:row.totalsRow?undefined:tr.b.math.Range.fromExplicitRange(0,maxValues.selfTime),ownerDocument:ownerDocument,});},width:'<upated further down>',cmp:function(rowA,rowB){return rowA.selfTime-rowB.selfTime;}});}
+if(this.eventsHaveSubRows_&&this.eventsHaveDuration_&&hasCpuData){columns.push({title:'CPU Self Time',value:function(row){return tr.v.ui.createScalarSpan(row.cpuSelfTime,{unit:tr.b.Unit.byName.timeDurationInMs,customContextRange:row.totalsRow?undefined:tr.b.math.Range.fromExplicitRange(0,maxValues.cpuSelfTime),ownerDocument:ownerDocument,});},width:'<upated further down>',cmp:function(rowA,rowB){return rowA.cpuSelfTime-rowB.cpuSelfTime;}});}
+if(this.eventsHaveDuration_){columns.push({title:'Average '+(hasCpuData?'CPU':'Wall')+' Duration',value:function(row){var totalDuration=hasCpuData?row.cpuDuration:row.duration;return tr.v.ui.createScalarSpan(totalDuration/row.numEvents,{unit:tr.b.Unit.byName.timeDurationInMs,customContextRange:row.totalsRow?undefined:tr.b.math.Range.fromExplicitRange(0,maxValues.duration),ownerDocument:ownerDocument,});},width:'<upated further down>',cmp:function(rowA,rowB){if(hasCpuData){return rowA.cpuDuration/rowA.numEvents-
+rowB.cpuDuration/rowB.numEvents;}
+return rowA.duration/rowA.numEvents-
+rowB.duration/rowB.numEvents;}});}
 columns.push({title:'Occurrences',value:function(row){return row.numEvents;},width:'<upated further down>',cmp:function(rowA,rowB){return rowA.numEvents-rowB.numEvents;}});var alertsColumnIndex;if(hasAlerts){columns.push({title:'Num Alerts',value:function(row){return row.numAlerts;},width:'<upated further down>',cmp:function(rowA,rowB){return rowA.numAlerts-rowB.numAlerts;}});alertsColumnIndex=columns.length-1;}
-var colWidthPercentage;if(columns.length==1)
-colWidthPercentage='100%';else
-colWidthPercentage=(100/(columns.length-1)).toFixed(3)+'%';for(var i=1;i<columns.length;i++)
-columns[i].width=colWidthPercentage;this.$.table.tableColumns=columns;if(hasAlerts){this.$.table.sortColumnIndex=alertsColumnIndex;this.$.table.sortDescending=true;}},configure:function(config){if(config.eventsByTitle===undefined)
-throw new Error('Required: eventsByTitle');if(config.showTotals!==undefined)
-this.showTotals_=config.showTotals;else
-this.showTotals_=true;if(config.eventsHaveDuration!==undefined)
-this.eventsHaveDuration_=config.eventsHaveDuration;else
-this.eventsHaveDuration_=true;if(config.eventsHaveSubRows!==undefined)
-this.eventsHaveSubRows_=config.eventsHaveSubRows;else
-this.eventsHaveSubRows_=true;this.eventsByTitle_=config.eventsByTitle;this.updateContents_();},get showTotals(){return this.showTotals_;},set showTotals(showTotals){this.showTotals_=showTotals;this.updateContents_();},get eventsHaveDuration(){return this.eventsHaveDuration_;},set eventsHaveDuration(eventsHaveDuration){this.eventsHaveDuration_=eventsHaveDuration;this.updateContents_();},get eventsHaveSubRows(){return this.eventsHaveSubRows_;},set eventsHaveSubRows(eventsHaveSubRows){this.eventsHaveSubRows_=eventsHaveSubRows;this.updateContents_();},get eventsByTitle(){return this.eventsByTitle_;},set eventsByTitle(eventsByTitle){this.eventsByTitle_=eventsByTitle;this.updateContents_();},get selectionBounds(){return this.selectionBounds_;},set selectionBounds(selectionBounds){this.selectionBounds_=selectionBounds;this.updateContents_();},updateContents_:function(){var eventsByTitle;if(this.eventsByTitle_!==undefined)
-eventsByTitle=this.eventsByTitle_;else
-eventsByTitle=[];var allEvents=new tr.model.EventSet();var rows=[];tr.b.iterItems(eventsByTitle,function(title,eventsOfSingleTitle){for(var event of allEvents)
-allEvents.push(event);var row=new tr.ui.analysis.MultiEventSummary(title,eventsOfSingleTitle);rows.push(row);});this.updateTableColumns_(rows);this.$.table.tableRows=rows;var maxValues={duration:undefined,selfTime:undefined,cpuSelfTime:undefined,cpuDuration:undefined};if(this.eventsHaveDuration){for(var column in maxValues){maxValues[column]=tr.b.Statistics.max(rows,function(event){return event[column];});}}
+var colWidthPercentage;if(columns.length===1){colWidthPercentage='100%';}else{colWidthPercentage=(100/(columns.length-1)).toFixed(3)+'%';}
+for(var i=1;i<columns.length;i++){columns[i].width=colWidthPercentage;}
+this.$.table.tableColumns=columns;if(hasAlerts){this.$.table.sortColumnIndex=alertsColumnIndex;this.$.table.sortDescending=true;}},configure:function(config){if(config.eventsByTitle===undefined){throw new Error('Required: eventsByTitle');}
+if(config.showTotals!==undefined){this.showTotals_=config.showTotals;}else{this.showTotals_=true;}
+if(config.eventsHaveDuration!==undefined){this.eventsHaveDuration_=config.eventsHaveDuration;}else{this.eventsHaveDuration_=true;}
+if(config.eventsHaveSubRows!==undefined){this.eventsHaveSubRows_=config.eventsHaveSubRows;}else{this.eventsHaveSubRows_=true;}
+this.eventsByTitle_=config.eventsByTitle;this.updateContents_();},get showTotals(){return this.showTotals_;},set showTotals(showTotals){this.showTotals_=showTotals;this.updateContents_();},get eventsHaveDuration(){return this.eventsHaveDuration_;},set eventsHaveDuration(eventsHaveDuration){this.eventsHaveDuration_=eventsHaveDuration;this.updateContents_();},get eventsHaveSubRows(){return this.eventsHaveSubRows_;},set eventsHaveSubRows(eventsHaveSubRows){this.eventsHaveSubRows_=eventsHaveSubRows;this.updateContents_();},get eventsByTitle(){return this.eventsByTitle_;},set eventsByTitle(eventsByTitle){this.eventsByTitle_=eventsByTitle;this.updateContents_();},get selectionBounds(){return this.selectionBounds_;},set selectionBounds(selectionBounds){this.selectionBounds_=selectionBounds;this.updateContents_();},updateContents_:function(){var eventsByTitle;if(this.eventsByTitle_!==undefined){eventsByTitle=this.eventsByTitle_;}else{eventsByTitle=[];}
+var allEvents=new tr.model.EventSet();var rows=[];for(var[title,eventsOfSingleTitle]of Object.entries(eventsByTitle)){for(var event of eventsOfSingleTitle)allEvents.push(event);var row=new tr.ui.analysis.MultiEventSummary(title,eventsOfSingleTitle);rows.push(row);}
+this.updateTableColumns_(rows);this.$.table.tableRows=rows;var maxValues={duration:undefined,selfTime:undefined,cpuSelfTime:undefined,cpuDuration:undefined};if(this.eventsHaveDuration){for(var column in maxValues){maxValues[column]=tr.b.math.Statistics.max(rows,function(event){return event[column];});}}
 var footerRows=[];if(this.showTotals_){var multiEventSummary=new tr.ui.analysis.MultiEventSummary('Totals',allEvents);footerRows.push(multiEventSummary);}
-this.updateTableColumns_(rows,maxValues);this.$.table.tableRows=rows;this.$.table.footerRows=footerRows;this.$.table.rebuild();}});'use strict';Polymer({is:'tr-ui-a-selection-summary-table',created:function(){this.selection_=new tr.b.Range();},ready:function(){this.$.table.showHeader=false;this.$.table.tableColumns=[{title:'Name',value:function(row){return row.title;},width:'350px'},{title:'Value',width:'100%',value:function(row){return row.value;}}];},get selection(){return this.selection_;},set selection(selection){this.selection_=selection;this.updateContents_();},updateContents_:function(){var selection=this.selection_;var rows=[];var hasRange;if(this.selection_&&(!selection.bounds.isEmpty))
-hasRange=true;else
-hasRange=false;rows.push({title:'Selection start',value:hasRange?tr.v.ui.createScalarSpan(selection.bounds.min,{unit:tr.b.Unit.byName.timeStampInMs,ownerDocument:this.ownerDocument}):'<empty>'});rows.push({title:'Selection extent',value:hasRange?tr.v.ui.createScalarSpan(selection.bounds.range,{unit:tr.b.Unit.byName.timeDurationInMs,ownerDocument:this.ownerDocument}):'<empty>'});this.$.table.tableRows=rows;this.$.table.rebuild();}});'use strict';Polymer({is:'tr-ui-a-multi-event-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],created:function(){this.currentSelection_=undefined;this.eventsHaveDuration_=true;this.eventsHaveSubRows_=true;},set selection(selection){if(selection.length<=1)
-throw new Error('Only supports multiple items');this.setSelectionWithoutErrorChecks(selection);},get selection(){return this.currentSelection_;},setSelectionWithoutErrorChecks:function(selection){this.currentSelection_=selection;this.updateContents_();},get eventsHaveDuration(){return this.eventsHaveDuration_;},set eventsHaveDuration(eventsHaveDuration){this.eventsHaveDuration_=eventsHaveDuration;this.updateContents_();},get eventsHaveSubRows(){return this.eventsHaveSubRows_;},set eventsHaveSubRows(eventsHaveSubRows){this.eventsHaveSubRows_=eventsHaveSubRows;this.updateContents_();},updateContents_:function(){var selection=this.currentSelection_;Polymer.dom(this.$.content).textContent='';if(!selection)
-return;var eventsByTitle=selection.getEventsOrganizedByTitle();var numTitles=tr.b.dictionaryLength(eventsByTitle);var summaryTableEl=document.createElement('tr-ui-a-multi-event-summary-table');summaryTableEl.configure({showTotals:numTitles>1,eventsByTitle:eventsByTitle,eventsHaveDuration:this.eventsHaveDuration_,eventsHaveSubRows:this.eventsHaveSubRows_});Polymer.dom(this.$.content).appendChild(summaryTableEl);var selectionSummaryTableEl=document.createElement('tr-ui-a-selection-summary-table');selectionSummaryTableEl.selection=this.currentSelection_;Polymer.dom(this.$.content).appendChild(selectionSummaryTableEl);if(numTitles===1){var detailsTableEl=document.createElement('tr-ui-a-multi-event-details-table');detailsTableEl.eventsHaveDuration=this.eventsHaveDuration_;detailsTableEl.eventsHaveSubRows=this.eventsHaveSubRows_;detailsTableEl.selection=selection;Polymer.dom(this.$.content).appendChild(detailsTableEl);}}});'use strict';tr.exportTo('tr.ui.analysis',function(){var FLOW_IN=0x1;var FLOW_OUT=0x2;var FLOW_IN_OUT=FLOW_IN|FLOW_OUT;function FlowClassifier(){this.numEvents_=0;this.eventsByGUID_={};}
+this.updateTableColumns_(rows,maxValues);this.$.table.tableRows=rows;this.$.table.footerRows=footerRows;this.$.table.rebuild();}});'use strict';Polymer({is:'tr-ui-a-selection-summary-table',created:function(){this.selection_=new tr.b.math.Range();},ready:function(){this.$.table.showHeader=false;this.$.table.tableColumns=[{title:'Name',value:function(row){return row.title;},width:'350px'},{title:'Value',width:'100%',value:function(row){return row.value;}}];},get selection(){return this.selection_;},set selection(selection){this.selection_=selection;this.updateContents_();},updateContents_:function(){var selection=this.selection_;var rows=[];var hasRange;if(this.selection_&&(!selection.bounds.isEmpty)){hasRange=true;}else{hasRange=false;}
+rows.push({title:'Selection start',value:hasRange?tr.v.ui.createScalarSpan(selection.bounds.min,{unit:tr.b.Unit.byName.timeStampInMs,ownerDocument:this.ownerDocument}):'<empty>'});rows.push({title:'Selection extent',value:hasRange?tr.v.ui.createScalarSpan(selection.bounds.range,{unit:tr.b.Unit.byName.timeDurationInMs,ownerDocument:this.ownerDocument}):'<empty>'});this.$.table.tableRows=rows;this.$.table.rebuild();}});'use strict';Polymer({is:'tr-ui-b-radio-picker',created:function(){this.needsInit_=true;this.settingsKey_=undefined;this.isReady_=false;this.radioButtons_=undefined;this.selectedKey_=undefined;},ready:function(){this.isReady_=true;this.maybeInit_();this.maybeRenderRadioButtons_();},get vertical(){return this.getAttribute('vertical');},set vertical(vertical){if(vertical){this.setAttribute('vertical',true);}else{this.removeAttribute('vertical');}},get settingsKey(){return this.settingsKey_;},set settingsKey(settingsKey){if(!this.needsInit_){throw new Error('Already initialized.');}
+this.settingsKey_=settingsKey;this.maybeInit_();},maybeInit_:function(){if(!this.needsInit_)return;if(this.settingsKey_===undefined)return;this.needsInit_=false;this.select(tr.b.Settings.get(this.settingsKey_));},set items(items){this.radioButtons_={};items.forEach(function(e){if(e.key in this.radioButtons_){throw new Error(e.key+' already exists');}
+var radioButton=document.createElement('div');var input=document.createElement('input');var label=document.createElement('label');input.type='radio';input.id=e.label;input.addEventListener('click',function(){this.select(e.key);}.bind(this));Polymer.dom(label).innerHTML=e.label;label.htmlFor=e.label;label.style.display='inline';Polymer.dom(radioButton).appendChild(input);Polymer.dom(radioButton).appendChild(label);this.radioButtons_[e.key]=input;}.bind(this));this.maybeInit_();this.maybeRenderRadioButtons_();},maybeRenderRadioButtons_:function(){if(!this.isReady_)return;if(this.radioButtons_===undefined)return;for(var key in this.radioButtons_){Polymer.dom(this.$.container).appendChild(this.radioButtons_[key].parentElement);}
+if(this.selectedKey_!==undefined){this.select(this.selectedKey_);}},select:function(key){if(key===undefined||key===this.selectedKey_){return;}
+if(this.radioButtons_===undefined){this.selectedKey_=key;return;}
+if(!(key in this.radioButtons_)){throw new Error(key+' does not exists');}
+if(this.selectedKey_!==undefined){this.radioButtons_[this.selectedKey_].checked=false;}
+this.selectedKey_=key;tr.b.Settings.set(this.settingsKey_,this.selectedKey_);if(this.selectedKey_!==undefined){this.radioButtons_[this.selectedKey_].checked=true;}
+this.dispatchEvent(new tr.b.Event('change',false));},get selectedKey(){return this.selectedKey_;},});'use strict';tr.exportTo('tr.ui.b',function(){var ColumnChart=tr.ui.b.define('column-chart',tr.ui.b.ChartBase2DBrushX);ColumnChart.prototype={__proto__:tr.ui.b.ChartBase2DBrushX.prototype,decorate(){super.decorate();this.xCushion_=1;this.isStacked_=false;this.enableHoverBox=true;},set isStacked(stacked){this.isStacked_=true;this.updateContents_();},get isStacked(){return this.isStacked_;},get defaultGraphHeight(){return 100;},get defaultGraphWidth(){return 10*this.data_.length;},updateScales_(){if(this.data_.length===0)return;var xDifferences=0;var currentX=undefined;var previousX=undefined;this.data_.forEach(function(datum,index){previousX=currentX;currentX=this.getXForDatum_(datum,index);if(previousX!==undefined){xDifferences+=currentX-previousX;}},this);this.xScale_.range([0,this.graphWidth]);var domain=d3.extent(this.data_,this.getXForDatum_.bind(this));if(this.data_.length>1){this.xCushion_=xDifferences/(this.data_.length-1);}
+this.xScale_.domain([domain[0],domain[1]+this.xCushion_]);this.yScale_.range([this.graphHeight,0]);this.yScale_.domain(this.getYScaleDomain_(this.dataRange.min,this.dataRange.max));},updateDataRange_(){if(!this.isStacked){super.updateDataRange_();return;}
+this.autoDataRange_.reset();this.autoDataRange_.addValue(0);for(var datum of this.data_){var sum=0;for(var[key,series]of this.seriesByKey_){if(datum[key]===undefined){continue;}
+sum+=datum[key];}
+this.autoDataRange_.addValue(sum);}},getStackedRectsForDatum_(datum,index){var stacks=[];var bottom=this.yScale_.range()[0];var sum=0;for(var[key,series]of this.seriesByKey_){if(datum[key]===undefined||!this.isSeriesEnabled(key)){continue;}
+sum+=this.dataRange.clamp(datum[key]);var heightPx=bottom-this.yScale_(sum);bottom-=heightPx;stacks.push({key:key,value:datum[key],color:this.getDataSeries(key).color,heightPx:heightPx,topPx:bottom,underflow:sum<this.dataRange.min,overflow:sum>this.dataRange.max,});}
+return stacks;},getRectsForDatum_(datum,index){if(this.isStacked){return this.getStackedRectsForDatum_(datum,index);}
+var stacks=[];for(var[key,series]of this.seriesByKey_){if(datum[key]===undefined||!this.isSeriesEnabled(key)){continue;}
+var clampedValue=this.dataRange.clamp(datum[key]);var topPx=this.yScale_(Math.max(clampedValue,this.getYScaleMin_()));stacks.push({key:key,value:datum[key],topPx:topPx,heightPx:this.yScale_.range()[0]-topPx,color:this.getDataSeries(key).color,underflow:datum[key]<this.dataRange.min,overflow:datum[key]>this.dataRange.max,});}
+stacks.sort(function(a,b){return b.topPx-a.topPx;});return stacks;},drawHoverValueBox_(rect){var rectHoverEvent=new tr.b.Event('rect-mouseenter');rectHoverEvent.rect=rect;this.dispatchEvent(rectHoverEvent);if(!this.enableHoverBox)return;var seriesKeys=[...this.seriesByKey_.keys()];var chartAreaSel=d3.select(this.chartAreaElement);chartAreaSel.selectAll('.hover').remove();var keyWidthPx=0;var keyHeightPx=0;if(seriesKeys.length>1){keyWidthPx=tr.ui.b.getSVGTextSize(this.chartAreaElement,rect.key).width+5;keyHeightPx=16;}
+var valueWidthPx=tr.ui.b.getSVGTextSize(this.chartAreaElement,rect.value).width+5;var valueHeightPx=16;var hoverLeftPx=rect.leftPx+(rect.widthPx/2);chartAreaSel.append('rect').attr('class','hover').attr('fill','white').attr('x',hoverLeftPx).attr('y',rect.topPx).attr('width',Math.max(keyWidthPx,valueWidthPx)).attr('height',keyHeightPx+valueHeightPx);if(seriesKeys.length>1){chartAreaSel.append('text').attr('class','hover').attr('fill',rect.color).attr('x',hoverLeftPx+2).attr('y',rect.topPx+keyHeightPx-3).text(rect.key);}
+chartAreaSel.append('text').attr('class','hover').attr('fill',rect.color).attr('x',hoverLeftPx+2).attr('y',rect.topPx+keyHeightPx+valueHeightPx-3).text(rect.value);},clearHoverValueBox_(rect){var rectHoverEvent=new tr.b.Event('rect-mouseleave');rectHoverEvent.rect=rect;this.dispatchEvent(rectHoverEvent);d3.select(this.chartAreaElement).selectAll('.hover').remove();},drawRect_(rect,sel){sel=sel.data([rect]);sel.enter().append('rect').attr('fill',rect.color).attr('x',rect.leftPx).attr('y',rect.topPx).attr('width',rect.widthPx).attr('height',rect.heightPx).on('mouseenter',this.drawHoverValueBox_.bind(this,rect)).on('mouseleave',this.clearHoverValueBox_.bind(this,rect));sel.exit().remove();},drawUnderflow_(rect,sel){sel=sel.data([rect]);sel.enter().append('text').text('*').attr('fill',rect.color).attr('x',rect.leftPx+(rect.widthPx/2)).attr('y',this.graphHeight).on('mouseenter',this.drawHoverValueBox_.bind(this,rect)).on('mouseleave',this.clearHoverValueBox_.bind(this,rect));sel.exit().remove();},drawOverflow_(rect,sel){sel=sel.data([rect]);sel.enter().append('text').text('*').attr('fill',rect.color).attr('x',rect.leftPx+(rect.widthPx/2)).attr('y',0);sel.exit().remove();},updateDataContents_(dataSel){dataSel.selectAll('*').remove();var chartAreaSel=d3.select(this.chartAreaElement);var seriesKeys=[...this.seriesByKey_.keys()];var rectsSel=dataSel.selectAll('path');this.data_.forEach(function(datum,index){var currentX=this.getXForDatum_(datum,index);var width=undefined;if(index<(this.data_.length-1)){var nextX=this.getXForDatum_(this.data_[index+1],index+1);width=nextX-currentX;}else{width=this.xCushion_;}
+for(var rect of this.getRectsForDatum_(datum,index)){rect.datum=datum;rect.index=index;rect.leftPx=this.xScale_(currentX);rect.rightPx=this.xScale_(currentX+width);rect.widthPx=rect.rightPx-rect.leftPx;this.drawRect_(rect,rectsSel);if(rect.underflow){this.drawUnderflow_(rect,rectsSel);}
+if(rect.overflow){this.drawOverflow_(rect,rectsSel);}}},this);}};return{ColumnChart,};});'use strict';tr.exportTo('tr.ui.b',function(){var MIN_GUIDELINE_HEIGHT_PX=3;var CHECKBOX_WIDTH_PX=18;var NameColumnChart=tr.ui.b.define('name-column-chart',tr.ui.b.ColumnChart);NameColumnChart.prototype={__proto__:tr.ui.b.ColumnChart.prototype,get xAxisHeight(){return 5+(this.textHeightPx_*this.data_.length);},updateMargins_(){super.updateMargins_();var xAxisTickOverhangPx=0;for(var i=0;i<this.data_.length;++i){var datum=this.data_[i];xAxisTickOverhangPx=Math.max(xAxisTickOverhangPx,this.xScale_(i)+tr.ui.b.getSVGTextSize(this,datum.x).width-
+this.graphWidth);}
+this.margin.right=Math.max(this.margin.right,xAxisTickOverhangPx);},getXForDatum_:function(datum,index){return index;},get xAxisTickOffset(){return 0.5;},updateXAxis_:function(xAxis){xAxis.selectAll('*').remove();if(this.hideXAxis)return;var nameTexts=xAxis.selectAll('text').data(this.data_);nameTexts.enter().append('text').attr('transform',(d,index)=>'translate(0, '+
+this.textHeightPx_*(this.data_.length-index)+')').attr('x',(d,index)=>this.xScale_(index)).attr('y',d=>this.graphHeight).text(d=>d.x);nameTexts.exit().remove();var guideLines=xAxis.selectAll('line.guide').data(this.data_);guideLines.enter().append('line').attr('x1',(d,index)=>this.xScale_(index+this.xAxisTickOffset)).attr('x2',(d,index)=>this.xScale_(index+this.xAxisTickOffset)).attr('y1',()=>this.graphHeight).attr('y2',(d,index)=>this.graphHeight+Math.max(MIN_GUIDELINE_HEIGHT_PX,(this.textHeightPx_*(this.data_.length-index-1))));}};return{NameColumnChart,};});'use strict';tr.exportTo('tr.ui.b',function(){var LineChart=tr.ui.b.LineChart;var NameLineChart=tr.ui.b.define('name-line-chart',LineChart);NameLineChart.prototype={__proto__:LineChart.prototype,getXForDatum_:function(datum,index){return index;},get xAxisHeight(){return 5+(this.textHeightPx_*this.data_.length);},get xAxisTickOffset(){return 0;},updateMargins_(){tr.ui.b.NameColumnChart.prototype.updateMargins_.call(this);},updateXAxis_:function(xAxis){xAxis.selectAll('*').remove();if(this.hideXAxis)return;tr.ui.b.NameColumnChart.prototype.updateXAxis_.call(this,xAxis);var baseline=xAxis.selectAll('path').data([this]);baseline.enter().append('line').attr('stroke','black').attr('x1',this.xScale_(0)).attr('x2',this.xScale_(this.data_.length-1)).attr('y1',this.graphHeight).attr('y2',this.graphHeight);baseline.exit().remove();}};return{NameLineChart,};});'use strict';tr.exportTo('tr.ui.b',function(){var BoxChart=tr.ui.b.define('box-chart',tr.ui.b.NameLineChart);BoxChart.prototype={__proto__:tr.ui.b.NameLineChart.prototype,get hideLegend(){return true;},updateDataRange_(){if(this.overrideDataRange_!==undefined){return;}
+this.autoDataRange_.reset();for(var datum of this.data_){this.autoDataRange_.addValue(datum.percentile_0);this.autoDataRange_.addValue(datum.percentile_100);}},updateScales_(){super.updateScales_();this.xScale_.domain([0,this.data_.length]);},get xAxisTickOffset(){return 0.5;},updateDataRange_(){if(this.overrideDataRange_!==undefined)return;this.autoDataRange_.reset();for(var datum of this.data_){this.autoDataRange_.addValue(datum.percentile_0);this.autoDataRange_.addValue(datum.percentile_100);}},updateXAxis_(xAxis){xAxis.selectAll('*').remove();if(this.hideXAxis)return;tr.ui.b.NameColumnChart.prototype.updateXAxis_.call(this,xAxis);var baseline=xAxis.selectAll('path').data([this]);baseline.enter().append('line').attr('stroke','black').attr('x1',this.xScale_(0)).attr('x2',this.xScale_(this.data_.length)).attr('y1',this.graphHeight).attr('y2',this.graphHeight);baseline.exit().remove();},updateDataContents_(dataSel){dataSel.selectAll('*').remove();var boxesSel=dataSel.selectAll('path');for(var index=0;index<this.data_.length;++index){var datum=this.data_[index];var color=datum.color||'black';var sel=boxesSel.data([datum]);sel.enter().append('rect').attr('fill',color).attr('x',this.xScale_(index+0.2)).attr('width',this.xScale_(index+0.8)-this.xScale_(index+0.2)).attr('y',this.yScale_(datum.percentile_75)).attr('height',this.yScale_(datum.percentile_25)-
+this.yScale_(datum.percentile_75));sel.exit().remove();sel=boxesSel.data([datum]);sel.enter().append('line').attr('stroke',color).attr('x1',this.xScale_(index)).attr('x2',this.xScale_(index+1)).attr('y1',this.yScale_(datum.percentile_50)).attr('y2',this.yScale_(datum.percentile_50));sel.exit().remove();sel=boxesSel.data([datum]);sel.enter().append('line').attr('stroke',color).attr('x1',this.xScale_(index+0.4)).attr('x2',this.xScale_(index+0.6)).attr('y1',this.yScale_(datum.percentile_0)).attr('y2',this.yScale_(datum.percentile_0));sel.exit().remove();sel=boxesSel.data([datum]);sel.enter().append('line').attr('stroke',color).attr('x1',this.xScale_(index+0.4)).attr('x2',this.xScale_(index+0.6)).attr('y1',this.yScale_(datum.percentile_100)).attr('y2',this.yScale_(datum.percentile_100));sel.exit().remove();sel=boxesSel.data([datum]);sel.enter().append('line').attr('stroke',color).attr('x1',this.xScale_(index+0.5)).attr('x2',this.xScale_(index+0.5)).attr('y1',this.yScale_(datum.percentile_100)).attr('y2',this.yScale_(datum.percentile_0));sel.exit().remove();}}};return{BoxChart,};});'use strict';tr.exportTo('tr.ui.b',function(){var BarChart=tr.ui.b.define('bar-chart',tr.ui.b.ColumnChart);BarChart.prototype={__proto__:tr.ui.b.ColumnChart.prototype,decorate(){super.decorate();this.verticalScale_=undefined;this.horizontalScale_=undefined;},updateScales_(){super.updateScales_();this.yScale_.range([this.graphWidth,0]);this.xScale_.range([0,this.graphHeight]);this.verticalScale_=this.isYLogScale_?d3.scale.log(10):d3.scale.linear();this.verticalScale_.domain(this.xScale_.domain());this.verticalScale_.range([this.graphHeight,0]);this.horizontalScale_=d3.scale.linear();this.horizontalScale_.domain(this.yScale_.domain());this.horizontalScale_.range([0,this.graphWidth]);},get defaultGraphHeight(){return Math.max(20,10*this.data_.length);},get defaultGraphWidth(){return 100;},get barHeight(){return this.graphHeight/this.data.length;},drawBrush_(brushRectsSel){brushRectsSel.attr('x',0).attr('width',this.graphWidth).attr('y',d=>this.verticalScale_(d.max)).attr('height',d=>this.verticalScale_(d.min)-this.verticalScale_(d.max));},getDataPointAtChartPoint_(chartPoint){var flippedPoint={x:this.graphHeight-chartPoint.y,y:this.graphWidth-chartPoint.x};return super.getDataPointAtChartPoint_(flippedPoint);},drawXAxis_(xAxis){xAxis.attr('transform','translate(0,'+this.graphHeight+')').call(d3.svg.axis().scale(this.horizontalScale_).orient('bottom'));},get yAxisWidth(){return this.computeScaleTickWidth_(this.verticalScale_);},drawYAxis_(yAxis){var axisModifier=d3.svg.axis().scale(this.verticalScale_).orient('left');yAxis.call(axisModifier);},drawHoverValueBox_(rect){var rectHoverEvent=new tr.b.Event('rect-mouseenter');rectHoverEvent.rect=rect;this.dispatchEvent(rectHoverEvent);if(!this.enableHoverBox)return;var seriesKeys=[...this.seriesByKey_.keys()];var chartAreaSel=d3.select(this.chartAreaElement);chartAreaSel.selectAll('.hover').remove();var keyWidthPx=0;var keyHeightPx=0;var xWidthPx=0;var xHeightPx=0;if(seriesKeys.length>1){keyWidthPx=tr.ui.b.getSVGTextSize(this.chartAreaElement,rect.key).width;keyHeightPx=this.textHeightPx_;}
+if(this.data.length>1){xWidthPx=tr.ui.b.getSVGTextSize(this.chartAreaElement,''+rect.datum.x).width;xHeightPx=this.textHeightPx_;}
+var valueWidthPx=tr.ui.b.getSVGTextSize(this.chartAreaElement,rect.value).width;var valueHeightPx=this.textHeightPx_;var hoverWidthPx=Math.min(Math.max(keyWidthPx,xWidthPx,valueWidthPx)+5,Math.max(50,rect.widthPx));var hoverTopPx=rect.topPx+(rect.heightPx/2);var hoverLeftPx=rect.leftPx+rect.widthPx-hoverWidthPx;chartAreaSel.append('rect').attr('class','hover').attr('fill','white').attr('x',hoverLeftPx).attr('y',hoverTopPx).attr('width',hoverWidthPx).attr('height',keyHeightPx+xHeightPx+valueHeightPx);if(seriesKeys.length>1){chartAreaSel.append('text').attr('class','hover').attr('fill',rect.color).attr('x',hoverLeftPx+2).attr('y',hoverTopPx+keyHeightPx-3).text(rect.key);}
+if(this.data.length>1){chartAreaSel.append('text').attr('class','hover').attr('fill',rect.color).attr('x',hoverLeftPx+2).attr('y',hoverTopPx+keyHeightPx+valueHeightPx-3).text(''+rect.datum.x);}
+chartAreaSel.append('text').attr('class','hover').attr('fill',rect.color).attr('x',hoverLeftPx+2).attr('y',hoverTopPx+xHeightPx+keyHeightPx+valueHeightPx-3).text(rect.value);},flipRect_(rect){return{datum:rect.datum,index:rect.index,key:rect.key,value:rect.value,color:rect.color,topPx:this.graphHeight-rect.leftPx-rect.widthPx,leftPx:this.graphWidth-rect.topPx-rect.heightPx,widthPx:rect.heightPx,heightPx:rect.widthPx,underflow:rect.underflow,overflow:rect.overflow,};},drawRect_(rect,sel){super.drawRect_(this.flipRect_(rect),sel);},drawUnderflow_(rect,rectsSel){var sel=rectsSel.data([rect]);sel.enter().append('text').text('*').attr('fill',rect.color).attr('x',0).attr('y',this.graphHeight-rect.leftPx+
+3+(rect.widthPx/2));sel.exit().remove();sel=rectsSel.data([rect]);sel.enter().append('rect').attr('fill','rgba(0, 0, 0, 0)').attr('x',0).attr('y',this.graphHeight-rect.leftPx-rect.widthPx).attr('width',10).attr('height',rect.widthPx).on('mouseenter',()=>this.drawHoverValueBox_(this.flipRect_(rect))).on('mouseleave',()=>this.clearHoverValueBox_(rect));sel.exit().remove();},drawOverflow_(rect,sel){sel=sel.data([rect]);sel.enter().append('text').text('*').attr('fill',rect.color).attr('x',this.graphWidth).attr('y',this.graphHeight-rect.leftPx+
+3+(rect.widthPx/2));sel.exit().remove();}};return{BarChart,};});'use strict';tr.exportTo('tr.ui.b',function(){var NameBarChart=tr.ui.b.define('name-bar-chart',tr.ui.b.BarChart);var Y_AXIS_PADDING=2;NameBarChart.prototype={__proto__:tr.ui.b.BarChart.prototype,getDataPointAtChartPoint_(chartPoint){return{x:tr.ui.b.BarChart.prototype.getDataPointAtChartPoint_.call(this,chartPoint).x,y:parseInt(Math.floor((this.graphHeight-chartPoint.y)/this.barHeight))};},getXForDatum_(datum,index){return index;},get yAxisWidth(){if(this.data.length===0)return 0;return Y_AXIS_PADDING+tr.b.math.Statistics.max(this.data_,d=>tr.ui.b.getSVGTextSize(this,d.x).width);},get defaultGraphHeight(){return(3+this.textHeightPx_)*this.data.length;},updateYAxis_(yAxis){if(tr.ui.b.getSVGTextSize(this,'test').width===0){tr.b.requestAnimationFrame(()=>this.updateYAxis_(yAxis));return;}
+yAxis.selectAll('*').remove();var nameTexts=yAxis.selectAll('text').data(this.data_);nameTexts.enter().append('text').attr('x',d=>-(tr.ui.b.getSVGTextSize(this,d.x).width+Y_AXIS_PADDING)).attr('y',(d,index)=>this.verticalScale_(index)).text(d=>d.x);nameTexts.exit().remove();var previousTop=undefined;for(var text of nameTexts[0]){var bbox=text.getBBox();if((previousTop===undefined)||(previousTop>(bbox.y+bbox.height))){previousTop=bbox.y;}else{text.style.opacity=0;}}}};return{NameBarChart,};});'use strict';const DEFAULT_COLOR_SCHEME=new tr.b.SinebowColorGenerator();class BreakdownTableSummaryRow{constructor(displayElement,histogramNames){this.displayElement_=displayElement;this.histogramNames_=histogramNames;this.keySpan_=undefined;}
+get numberValue(){return undefined;}
+get keySpan(){if(this.keySpan_===undefined){if(this.histogramNames_.length){this.keySpan_=document.createElement('tr-ui-a-analysis-link');this.keySpan_.setSelectionAndContent(this.histogramNames_,'Select All');}else{this.keySpan_='Sum';}}
+return this.keySpan_;}
+get displayElement(){return this.displayElement_;}
+get stringPercent(){return'100%';}}
+class BreakdownTableRow{constructor(name,value,color){this.name_=name;this.value=value;if(!this.isHistogram&&typeof value!=='number'){throw new Error('unsupported value '+value);}
+this.tableSum_=undefined;this.keySpan_=undefined;this.color_=color;let hsl=this.color.toHSL();hsl.l*=0.85;this.highlightedColor_=tr.b.Color.fromHSL(hsl);}
+get isHistogram(){return this.value instanceof tr.v.Histogram;}
+get name(){return this.name_;}
+get color(){return this.color_;}
+get highlightedColor(){return this.highlightedColor_;}
+get keySpan(){if(this.keySpan_===undefined){if(this.isHistogram){this.keySpan_=document.createElement('tr-ui-a-analysis-link');this.keySpan_.setSelectionAndContent([this.value.name],this.name);this.keySpan_.color=this.color;this.keySpan_.title=this.value.name;}else{this.keySpan_=document.createElement('span');this.keySpan_.innerText=this.name;this.keySpan_.style.color=this.color;}}
+return this.keySpan_;}
+get numberValue(){if(this.isHistogram)return this.value.sum;if(!isNaN(this.value)&&(this.value!==Infinity)&&(this.value!==-Infinity)&&(this.value>0))return this.value;return undefined;}
+get stringValue(){if(this.numberValue===undefined)return this.value.toString();return this.numberValue.toString();}
+set tableSum(s){this.tableSum_=s;}
+get stringPercent(){if(this.tableSum_===undefined)return'';let num=this.numberValue;if(num===undefined)return'';return Math.floor(num*100.0/this.tableSum_)+'%';}
+get displayElement(){if(this.numberValue===undefined)return this.value.toString();if(this.isHistogram){return tr.v.ui.createScalarSpan(this.numberValue,{unit:this.value.unit,});}
+return this.numberValue;}
+compare(other){if(this.numberValue===undefined){if(other.numberValue===undefined){return this.name.localeCompare(other.name);}
+return 1;}
+if(other.numberValue===undefined){return-1;}
+if(this.numberValue===other.numberValue){return this.name.localeCompare(other.name);}
+return other.numberValue-this.numberValue;}}
+Polymer({is:'tr-v-ui-breakdown-span',created(){this.diagnostic_=undefined;this.chart_=new tr.ui.b.ColumnChart();this.chart_.graphHeight=130;this.chart_.isStacked=true;this.chart_.hideXAxis=true;this.chart_.hideLegend=true;this.chart_.enableHoverBox=false;this.chart_.addEventListener('rect-mouseenter',event=>this.onRectMouseEnter_(event));this.chart_.addEventListener('rect-mouseleave',event=>this.onRectMouseLeave_(event));},onRectMouseEnter_(event){for(let row of this.$.table.tableRows){if(row.name===event.rect.key){row.keySpan.parentNode.nextSibling.style.background=event.rect.color;row.keySpan.scrollIntoViewIfNeeded();}else{row.keySpan.parentNode.nextSibling.style.background='';}}},onRectMouseLeave_(event){for(let row of this.$.table.tableRows){row.keySpan.parentNode.nextSibling.style.background='';}},ready(){Polymer.dom(this.$.container).appendChild(this.chart_);this.$.table.zebra=true;this.$.table.showHeader=false;this.$.table.tableColumns=[{value:row=>row.keySpan,},{value:row=>row.displayElement,align:tr.ui.b.TableFormat.ColumnAlignment.RIGHT,},{value:row=>row.stringPercent,align:tr.ui.b.TableFormat.ColumnAlignment.RIGHT,},];},attached(){if(this.diagnostic_)this.updateContents_();},get diagnostic(){return this.diagnostic_;},set diagnostic(d){this.diagnostic_=d;if(this.isAttached)this.updateContents_();},updateContents_(){this.$.container.style.display='none';this.$.table.style.display='none';this.$.empty.style.display='block';if(!this.diagnostic_){this.chart_.data=[];return;}
+let colorScheme=undefined;if(this.diagnostic.colorScheme===tr.v.d.COLOR_SCHEME_CHROME_USER_FRIENDLY_CATEGORY_DRIVER){colorScheme=(name)=>{let cat=name.split(' ');cat=cat[cat.length-1];return tr.e.chrome.ChromeUserFriendlyCategoryDriver.getColor(cat);};}else if(this.diagnostic.colorScheme!==undefined){colorScheme=(name)=>tr.b.FixedColorSchemeRegistry.lookUp(this.diagnostic.colorScheme).getColor(name);}else{colorScheme=(name)=>DEFAULT_COLOR_SCHEME.colorForKey(name);}
+let tableRows=[];let tableSum=0;let histogramNames=[];let unit=undefined;for(let[name,value]of this.diagnostic){let row=new BreakdownTableRow(name,value,colorScheme(name));tableRows.push(row);if(row.numberValue!==undefined)tableSum+=row.numberValue;if(row.isHistogram){histogramNames.push(value.name);if(unit===undefined)unit=value.unit;}}
+tableRows.sort((x,y)=>x.compare(y));if(tableSum>0){let summaryDisplayElement=tableSum;if(unit!==undefined){summaryDisplayElement=unit.format(tableSum);}
+tableRows.unshift(new BreakdownTableSummaryRow(summaryDisplayElement,histogramNames));}
+let chartData={x:0};for(let row of tableRows){if(row.numberValue===undefined)continue;row.tableSum=tableSum;chartData[row.name]=row.numberValue;let dataSeries=this.chart_.getDataSeries(row.name);dataSeries.color=row.color;dataSeries.highlightedColor=row.highlightedColor;}
+if(tableRows.length>0){this.$.table.style.display='block';this.$.empty.style.display='none';this.$.table.tableRows=tableRows;this.$.table.rebuild();}
+if(tr.b.dictionaryLength(chartData)>1){this.$.container.style.display='block';this.$.empty.style.display='none';this.chart_.data=[chartData];}}});'use strict';Polymer({is:'tr-v-ui-buildbot-info-span',ready(){this.diagnostic_=undefined;this.$.table.showHeader=false;this.$.table.tableColumns=[{value:row=>row[0]},{value:row=>row[1]}];},get diagnostic(){return this.diagnostic_;},set diagnostic(d){this.diagnostic_=d;this.updateContents_();},updateContents_(){if(this.diagnostic===undefined){this.$.table.tableRows=[];return;}
+let rows=[];if(this.diagnostic.displayMasterName){rows.push(['master',this.diagnostic.displayMasterName]);}
+if(this.diagnostic.buildbotMasterName){rows.push(['master',this.diagnostic.buildbotMasterName]);}
+if(this.diagnostic.displayBotName){rows.push(['bot',this.diagnostic.displayBotName]);}
+if(this.diagnostic.buildbotName){rows.push(['bot',this.diagnostic.buildbotName]);}
+if(this.diagnostic.buildNumber){rows.push(['build number',this.diagnostic.buildNumber]);}
+if(this.diagnostic.logUri){let anchor=document.createElement('a');anchor.href=this.diagnostic.logUri;anchor.innerText=this.diagnostic.logUri;rows.push(['log',anchor]);}
+this.$.table.tableRows=rows;}});'use strict';Polymer({is:'tr-v-ui-collected-related-event-set-span',ready(){this.diagnostic_=undefined;},get diagnostic(){return this.diagnostic_;},set diagnostic(d){this.diagnostic_=d;this.updateContents_();},updateContents_(){Polymer.dom(this).textContent='';for(let[canonicalUrl,events]of this.diagnostic){let link=document.createElement('a');if(events.length===1){let event=tr.b.getOnlyElement(events);link.textContent=event.title+' '+
+tr.b.Unit.byName.timeDurationInMs.format(event.duration);}else{link.textContent=events.length+' events';}
+link.href=canonicalUrl;Polymer.dom(this).appendChild(link);Polymer.dom(this).appendChild(document.createElement('br'));}}});'use strict';Polymer({is:'tr-v-ui-device-info-span',ready(){this.diagnostic_=undefined;this.$.table.showHeader=false;this.$.table.tableColumns=[{value(row){return row[0];},},{value(row){return row[1];}}];},get diagnostic(){return this.diagnostic_;},set diagnostic(d){this.diagnostic_=d;this.updateContents_();},updateContents_(){if(this.diagnostic===undefined){this.$.table.tableRows=[];return;}
+let rows=[];if(this.diagnostic.chromeVersion){rows.push(['chrome version',this.diagnostic.chromeVersion]);}
+if(this.diagnostic.osName){rows.push(['OS name',this.diagnostic.osName]);}
+if(this.diagnostic.osVersion){rows.push(['OS version',this.diagnostic.osVersion]);}
+if(this.diagnostic.gpuInfo){rows.push(['GPU',JSON.stringify(this.diagnostic.gpuInfo)]);}
+if(this.diagnostic.arch){rows.push(['arch',this.diagnostic.arch]);}
+if(this.diagnostic.ram){rows.push(['ram',tr.b.Unit.byName.sizeInBytes.format(this.diagnostic.ram)]);}
+this.$.table.tableRows=rows;}});'use strict';Polymer({is:'tr-v-ui-generic-diagnostic-span',ready(){this.diagnostic_=undefined;},get diagnostic(){return this.diagnostic_;},set diagnostic(d){this.diagnostic_=d;this.updateContents_();},updateContents_(){if(this.diagnostic===undefined){this.$.generic.object=undefined;return;}
+this.$.generic.object=this.diagnostic.value;}});'use strict';Polymer({is:'tr-v-ui-merged-buildbot-info-span',ready(){this.diagnostic_=undefined;this.$.table.showHeader=false;this.$.table.tableColumns=[{value:row=>row[0]},{value:row=>row[1]},];},get diagnostic(){return this.diagnostic_;},set diagnostic(d){this.diagnostic_=d;this.updateContents_();},updateContents_(){if(this.diagnostic===undefined){this.$.table.tableRows=[];return;}
+let rows=[];if(this.diagnostic.displayMasterNames.size){rows.push(['masters',Array.from(this.diagnostic.displayMasterNames).join(', ')]);}
+if(this.diagnostic.displayBotNames.size){rows.push(['bots',Array.from(this.diagnostic.displayBotNames).join(', ')]);}
+if(this.diagnostic.buildNumbers.size){rows.push(['builds',Array.from(this.diagnostic.buildNumbers).join(', ')]);}
+for(let logUri of this.diagnostic.logUris){let anchor=document.createElement('a');anchor.href=logUri;anchor.innerText=logUri;rows.push(['log',anchor]);}
+this.$.table.tableRows=rows;}});'use strict';Polymer({is:'tr-v-ui-merged-device-info-span',ready(){this.diagnostic_=undefined;this.$.table.showHeader=false;this.$.table.tableColumns=[{value:row=>row[0]},{value:row=>row[1]},];},get diagnostic(){return this.diagnostic_;},set diagnostic(d){this.diagnostic_=d;this.updateContents_();},updateContents_(){if(this.diagnostic===undefined){this.$.table.tableRows=[];return;}
+let rows=[];if(this.diagnostic.chromeVersions.size){rows.push(['chrome versions',Array.from(this.diagnostic.chromeVersions).join(', ')]);}
+if(this.diagnostic.osNames.size){rows.push(['os names',Array.from(this.diagnostic.osNames).join(', ')]);}
+if(this.diagnostic.osVersions.size){rows.push(['os versions',Array.from(this.diagnostic.osVersions).join(', ')]);}
+this.$.table.tableRows=rows;}});'use strict';Polymer({is:'tr-v-ui-merged-revision-info-span',ready(){this.diagnostic_=undefined;this.$.table.showHeader=false;this.$.table.tableColumns=[{value:row=>row[0]},{value:row=>row[1]},];},get diagnostic(){return this.diagnostic_;},set diagnostic(d){this.diagnostic_=d;this.updateContents_();},buildRow_(rows,label,revisions,host){if(revisions.length===0)return;let valueSpan=document.createElement('span');for(let revs of revisions){let anchor=document.createElement('a');anchor.innerText=revs[0];if(revs.length===1){anchor.href=host+'+/'+revs[0];}else{anchor.innerText+='..'+revs[1];anchor.href=host+'+log/'+revs[0]+'..'+revs[1];}
+anchor.addEventListener('click',event=>{event.stopPropagation();});valueSpan.appendChild(anchor);valueSpan.appendChild(document.createTextNode(' '));}
+rows.push([label,valueSpan]);},updateContents_(){if(this.diagnostic===undefined){this.$.table.tableRows=[];return;}
+let rows=[];if(this.diagnostic.chromiumCommitPosition){let positions=Array.from(this.diagnostic.chromiumCommitPositions);positions.sort((x,y)=>x-y);rows.push(['chromiumCommitPositions',positions.join(', ')]);}
+if(this.diagnostic.v8CommitPosition){let positions=Array.from(this.diagnostic.v8CommitPositions);rows.push(['v8CommitPositions',positions.join(', ')]);}
+this.buildRow_(rows,'chromium',this.diagnostic.chromium,tr.v.ui.CHROMIUM_REVISION_HOST);this.buildRow_(rows,'v8',this.diagnostic.v8,tr.v.ui.V8_REVISION_HOST);this.buildRow_(rows,'catapult',this.diagnostic.catapult,tr.v.ui.CATAPULT_REVISION_HOST);this.buildRow_(rows,'angle',this.diagnostic.angle,tr.v.ui.ANGLE_REVISION_HOST);this.buildRow_(rows,'skia',this.diagnostic.skia,tr.v.ui.SKIA_REVISION_HOST);this.buildRow_(rows,'webrtc',this.diagnostic.webrtc,tr.v.ui.WEBRTC_REVISION_HOST);this.$.table.tableRows=rows;}});'use strict';Polymer({is:'tr-v-ui-merged-telemetry-info-span',ready(){this.diagnostic_=undefined;this.$.table.showHeader=false;this.$.table.tableColumns=[{value:row=>row[0]},{value:row=>row[1]},];},onShow_(){this.$.show.style.display='none';this.$.hide.style.display='block';this.$.table.style.display='table';},onHide_(){this.$.show.style.display='block';this.$.hide.style.display='none';this.$.table.style.display='none';},get diagnostic(){return this.diagnostic_;},set diagnostic(d){this.diagnostic_=d;this.updateContents_();},updateContents_(){if(this.diagnostic===undefined){this.$.table.tableRows=[];return;}
+let rows=[];if(this.diagnostic.benchmarkNames.size){rows.push(['benchmark names',Array.from(this.diagnostic.benchmarkNames).join(', ')]);}
+if(this.diagnostic.benchmarkStarts.length){rows.push(['benchmark starts',this.diagnostic.benchmarkStartStrings.join(', ')]);}
+if(this.diagnostic.storyDisplayNames.size){rows.push(['stories',Array.from(this.diagnostic.storyDisplayNames).join(', ')]);}
+if(this.diagnostic.storysetRepeatCounters.size){rows.push(['storyset repeats',Array.from(this.diagnostic.storysetRepeatCounters).join(', ')]);}
+if(this.diagnostic.labels.size){rows.push(['label',Array.from(this.diagnostic.labels).join(', ')]);}
+if(this.diagnostic.storyGroupingKeys.size){let gov=document.createElement('tr-ui-a-generic-object-view');let obj={};for(let[key,value]of this.diagnostic.storyGroupingKeys){obj[key]=Array.from(value);}
+gov.object=obj;rows.push(['grouping keys',gov]);}
+this.$.table.tableRows=rows;}});'use strict';Polymer({is:'tr-v-ui-related-event-set-span',ready(){this.diagnostic_=undefined;},get diagnostic(){return this.diagnostic_;},set diagnostic(d){this.diagnostic_=d;this.updateContents_();},updateContents_(){Polymer.dom(this).textContent='';let events=new tr.model.EventSet([...this.diagnostic]);let link=document.createElement('tr-ui-a-analysis-link');let label=events.length+' events';if(events.length===1){let event=tr.b.getOnlyElement(events);label=event.title+' ';label+=tr.b.Unit.byName.timeDurationInMs.format(event.duration);}
+link.setSelectionAndContent(events,label);Polymer.dom(this).appendChild(link);}});'use strict';Polymer({is:'tr-v-ui-related-histogram-map-span',ready(){this.diagnostic_=undefined;this.$.table.showHeader=false;this.$.table.tableColumns=[{value:row=>row[0]},{value:row=>row[1]},];},get diagnostic(){return this.diagnostic_;},set diagnostic(d){this.diagnostic_=d;this.updateContents_();},updateContents_(){Polymer.dom(this).textContent='';let rows=[];let histogramNames=new Set();for(let[name,hist]of this.diagnostic){histogramNames.add(hist.name);}
+if(histogramNames.size>1){let link=document.createElement('tr-ui-a-analysis-link');link.setSelectionAndContent(Array.from(histogramNames),'Select All');rows.push([link,'']);}
+for(let[name,hist]of this.diagnostic){let link=document.createElement('tr-ui-a-analysis-link');link.setSelectionAndContent([hist.name],name);let scalarSpan=tr.v.ui.createScalarSpan(hist);rows.push([link,scalarSpan]);}
+this.$.table.tableRows=rows;this.$.table.rebuild();}});'use strict';Polymer({is:'tr-v-ui-related-histogram-set-span',ready(){this.diagnostic_=undefined;},get diagnostic(){return this.diagnostic_;},set diagnostic(d){this.diagnostic_=d;this.updateContents_();},addLink_(selection,content){let link=document.createElement('tr-ui-a-analysis-link');link.setSelectionAndContent(selection,content);Polymer.dom(this).appendChild(link);Polymer.dom(this).appendChild(document.createElement('br'));},updateContents_(){Polymer.dom(this).textContent='';let histogramNames=new Set();for(let hist of this.diagnostic){histogramNames.add(hist.name);}
+if(histogramNames.size>1){this.addLink_(Array.from(histogramNames),'Select All');}
+for(let hist of this.diagnostic){this.addLink_([hist.name],hist.name);}}});'use strict';tr.exportTo('tr.v.ui',function(){const CHROMIUM_REVISION_HOST='https://chromium.googlesource.com/chromium/src/';const V8_REVISION_HOST='https://chromium.googlesource.com/v8/v8.git/';const CATAPULT_REVISION_HOST='https://chromium.googlesource.com/external/github.com/catapult-project/catapult.git/';const ANGLE_REVISION_HOST='https://chromium.googlesource.com/angle/angle/';const SKIA_REVISION_HOST='https://chromium.googlesource.com/skia/';const WEBRTC_REVISION_HOST='https://chromium.googlesource.com/external/webrtc/';Polymer({is:'tr-v-ui-revision-info-span',ready(){this.diagnostic_=undefined;this.$.table.showHeader=false;this.$.table.tableColumns=[{value:row=>row[0]},{value:row=>row[1]},];},get diagnostic(){return this.diagnostic_;},set diagnostic(d){this.diagnostic_=d;this.updateContents_();},buildRow_(rows,label,revisions,host){if(revisions.length===0)return;let anchor=document.createElement('a');anchor.innerText=revisions[0];if(revisions.length===1){anchor.href=host+'+/'+revisions[0];}else{anchor.innerText+='..'+revisions[1];anchor.href=host+'+log/'+revisions[0]+'..'+revisions[1];}
+anchor.addEventListener('click',event=>{event.stopPropagation();});rows.push([label,anchor]);},updateContents_(){if(this.diagnostic===undefined){this.$.table.tableRows=[];return;}
+let rows=[];if(this.diagnostic.chromiumCommitPosition){rows.push(['chromiumCommitPosition',this.diagnostic.chromiumCommitPosition]);}
+if(this.diagnostic.v8CommitPosition){rows.push(['v8CommitPosition',this.diagnostic.v8CommitPosition]);}
+this.buildRow_(rows,'chromium',this.diagnostic.chromium,CHROMIUM_REVISION_HOST);this.buildRow_(rows,'v8',this.diagnostic.v8,V8_REVISION_HOST);this.buildRow_(rows,'catapult',this.diagnostic.catapult,CATAPULT_REVISION_HOST);this.buildRow_(rows,'angle',this.diagnostic.angle,ANGLE_REVISION_HOST);this.buildRow_(rows,'skia',this.diagnostic.skia,SKIA_REVISION_HOST);this.buildRow_(rows,'webrtc',this.diagnostic.webrtc,WEBRTC_REVISION_HOST);this.$.table.tableRows=rows;}});return{CHROMIUM_REVISION_HOST,V8_REVISION_HOST,CATAPULT_REVISION_HOST,ANGLE_REVISION_HOST,SKIA_REVISION_HOST,WEBRTC_REVISION_HOST,};});'use strict';Polymer({is:'tr-v-ui-scalar-diagnostic-span',ready:function(){this.diagnostic_=undefined;},get diagnostic(){return this.diagnostic_;},set diagnostic(d){this.diagnostic_=d;this.updateContents_();},updateContents_:function(){this.$.scalar.setValueAndUnit(this.diagnostic.value.value,this.diagnostic.value.unit);}});'use strict';Polymer({is:'tr-v-ui-telemetry-info-span',ready(){this.diagnostic_=undefined;this.$.table.showHeader=false;this.$.table.tableColumns=[{value:row=>row[0]},{value:row=>row[1]},];},get diagnostic(){return this.diagnostic_;},set diagnostic(d){this.diagnostic_=d;this.updateContents_();},updateContents_(){if(this.diagnostic===undefined){this.$.table.tableRows=[];return;}
+let rows=[];if(this.diagnostic.benchmarkName){rows.push(['benchmark name',this.diagnostic.benchmarkName]);}
+if(this.diagnostic.benchmarkStart){rows.push(['benchmark start',this.diagnostic.benchmarkStartString]);}
+if(this.diagnostic.storyDisplayName){rows.push(['story',this.diagnostic.storyDisplayName]);}
+if(this.diagnostic.storysetRepeatCounter!==undefined){rows.push(['storyset repeat',this.diagnostic.storysetRepeatCounter]);}
+if(this.diagnostic.label){rows.push(['label',this.diagnostic.label]);}
+if(this.diagnostic.storyGroupingKeys.size){let gov=document.createElement('tr-ui-a-generic-object-view');let obj={};for(let[key,value]of this.diagnostic.storyGroupingKeys){obj[key]=value;}
+gov.object=obj;rows.push(['grouping keys',gov]);}
+this.$.table.tableRows=rows;}});'use strict';Polymer({is:'tr-v-ui-unmergeable-diagnostic-set-span',ready(){this.diagnostic_=undefined;},get diagnostic(){return this.diagnostic_;},set diagnostic(d){this.diagnostic_=d;this.updateContents_();},updateContents_(){Polymer.dom(this).textContent='';for(let diagnostic of this.diagnostic){Polymer.dom(this).appendChild(tr.v.ui.createDiagnosticSpan(diagnostic));Polymer.dom(this).appendChild(document.createElement('br'));}}});'use strict';tr.exportTo('tr.v.ui',function(){function findElementNameForDiagnostic(diagnostic){let typeInfo=undefined;let curProto=diagnostic.constructor.prototype;while(curProto){typeInfo=tr.v.d.Diagnostic.findTypeInfo(curProto.constructor);if(typeInfo&&typeInfo.metadata.elementName)break;typeInfo=undefined;curProto=curProto.__proto__;}
+if(typeInfo===undefined){throw new Error(diagnostic.constructor.name+' or a base class must have a registered elementName');}
+let tagName=typeInfo.metadata.elementName;if(tr.ui.b.isUnknownElementName(tagName)){throw new Error('Element not registered: '+tagName);}
+return tagName;}
+function createDiagnosticSpan(diagnostic){let tagName=findElementNameForDiagnostic(diagnostic);let span=document.createElement(tagName);span.diagnostic=diagnostic;return span;}
+return{createDiagnosticSpan,};});'use strict';Polymer({is:'tr-v-ui-diagnostic-map-table',created(){this.diagnosticMaps_=undefined;},set diagnosticMaps(maps){this.diagnosticMaps_=maps;this.updateContents_();},updateContents_(){if(this.diagnosticMaps_===undefined||this.diagnosticMaps_.length===0){this.$.table.tableRows=[];this.$.table.tableColumns=[];return;}
+let columnTitles=new Set();for(let map of this.diagnosticMaps_){for(let[name,diagnostic]of map){if(diagnostic instanceof tr.v.d.UnmergeableDiagnosticSet)continue;if(diagnostic instanceof tr.v.d.CollectedRelatedEventSet)continue;if(diagnostic instanceof tr.v.d.GroupingPath)continue;columnTitles.add(name);}}
+let columns=[];function makeColumn(title){return{title:title,value(map){let diagnostic=map.get(title);if(!diagnostic)return'';return tr.v.ui.createDiagnosticSpan(diagnostic);}};}
+for(let title of columnTitles){columns.push(makeColumn(title));}
+this.$.table.tableColumns=columns;this.$.table.tableRows=this.diagnosticMaps_;this.$.table.rebuild();}});'use strict';Polymer({is:'tr-v-ui-scalar-map-table',created:function(){this.scalarMap_=new Map();this.significance_=new Map();},ready:function(){this.$.table.showHeader=false;this.$.table.tableColumns=[{value:function(row){return row.name;}},{value:function(row){let span=tr.v.ui.createScalarSpan(row.value);if(row.significance!==undefined){span.significance=row.significance;}else if(row.anyRowsHaveSignificance){span.style.marginRight='18px';}
+span.style.whiteSpace='nowrap';return span;}}];},get scalarMap(){return this.scalarMap_;},set scalarMap(map){this.scalarMap_=map;this.updateContents_();},setSignificanceForKey:function(key,significance){this.significance_.set(key,significance);this.updateContents_();},updateContents_:function(){let rows=[];for(let[key,scalar]of this.scalarMap){rows.push({name:key,value:scalar,significance:this.significance_.get(key),anyRowsHaveSignificance:(this.significance_.size>0)});}
+this.$.table.tableRows=rows;this.$.table.rebuild();}});'use strict';tr.exportTo('tr.v.ui',function(){const DEFAULT_BAR_HEIGHT_PX=5;const TRUNCATE_BIN_MARGIN=0.15;const IGNORE_DELTA_STATISTICS_NAMES=[`${tr.v.DELTA}min`,`%${tr.v.DELTA}min`,`${tr.v.DELTA}max`,`%${tr.v.DELTA}max`,`${tr.v.DELTA}sum`,`%${tr.v.DELTA}sum`,`${tr.v.DELTA}count`,`%${tr.v.DELTA}count`,];Polymer({is:'tr-v-ui-histogram-span',created(){this.histogram_=undefined;this.referenceHistogram_=undefined;this.graphWidth_=undefined;this.graphHeight_=undefined;this.mouseDownBin_=undefined;this.brushedBins_=[];this.prevBrushedBins_=[];this.canMergeSampleDiagnostics_=true;},ready(){this.$.drag_handle.target=this.$.container;this.$.drag_handle.addEventListener('drag-handle-resize',this.onResize_.bind(this));},attached(){if(this.histogram_!==undefined)this.updateContents_();},get canMergeSampleDiagnostics(){return this.canMergeSampleDiagnostics_;},set canMergeSampleDiagnostics(merge){this.canMergeSampleDiagnostics_=merge;this.$.merge_sample_diagnostics_container.style.display=(merge?'':'none');},onResize_(event){event.stopPropagation();let heightPx=parseInt(this.$.container.style.height);if(heightPx<this.defaultGraphHeight){heightPx=this.defaultGraphHeight;this.$.container.style.height=this.defaultGraphHeight+'px';}
+this.chart_.graphHeight=heightPx-(this.chart_.margin.top+
+this.chart_.margin.bottom);},get graphWidth(){return this.graphWidth_||this.defaultGraphWidth;},set graphWidth(width){this.graphWidth_=width;},get graphHeight(){return this.graphHeight_||this.defaultGraphHeight;},set graphHeight(height){this.graphHeight_=height;},get barHeight(){return this.chart_.barHeight;},set barHeight(px){this.graphHeight=this.computeChartHeight_(px);},computeChartHeight_(barHeightPx){return(this.chart_.margin.top+
+this.chart_.margin.bottom+
+(barHeightPx*this.histogram.allBins.length));},get defaultGraphHeight(){if(this.histogram&&this.histogram.allBins.length===1){return 150;}
+return this.computeChartHeight_(DEFAULT_BAR_HEIGHT_PX);},get defaultGraphWidth(){if(this.histogram.allBins.length===1){return 100;}
+return 300;},get brushedBins(){return this.brushedBins_;},set brushedBins(bins){this.brushedBins_=bins;if(this.chart_===undefined)return;let brushedBinIndices=new tr.b.math.Range();for(let bin of bins){brushedBinIndices.addValue(this.histogram.allBins.indexOf(bin));}
+this.chart_.brushedRange=brushedBinIndices;this.updateDiagnostics_();},get brushedBinRange(){if(this.chart_===undefined)return new tr.b.math.Range();return this.chart_.brushedRange;},set brushedBinRange(r){if(this.chart_===undefined)return;let brushedBins=[];for(let i=r.min;i<=r.max;++i){brushedBins.push(this.histogram.allBins[i]);}
+this.brushedBins=brushedBins;},get mergeSampleDiagnostics(){return this.canMergeSampleDiagnostics&&this.$.merge_sample_diagnostics.checked;},set mergeSampleDiagnostics(m){this.$.merge_sample_diagnostics.checked=m;},updateBrushedRange_(binIndex){let brushedBinIndices=new tr.b.math.Range();brushedBinIndices.addValue(tr.b.math.clamp(this.mouseDownBinIndex_,0,this.histogram.allBins.length-1));brushedBinIndices.addValue(tr.b.math.clamp(binIndex,0,this.histogram.allBins.length-1));brushedBinIndices.max+=1;this.chart_.brushedRange=brushedBinIndices;this.brushedBins_=[];for(let i=brushedBinIndices.min;i<brushedBinIndices.max;++i){this.brushedBins.push(this.histogram.allBins[i]);}},onMouseDown_(chartEvent){chartEvent.stopPropagation();if(!this.histogram){return;}
+this.prevBrushedBins_=this.brushedBins_;this.mouseDownBinIndex_=chartEvent.y;this.updateBrushedRange_(chartEvent.y);},onMouseMove_(chartEvent){chartEvent.stopPropagation();if(!this.histogram){return;}
+this.updateBrushedRange_(chartEvent.y);},onMouseUp_(chartEvent){chartEvent.stopPropagation();if(!this.histogram){return;}
+this.updateBrushedRange_(chartEvent.y);if(this.prevBrushedBins_.length===1&&this.brushedBins_.length===1&&this.prevBrushedBins_[0]===this.brushedBins_[0]){this.brushedBins_=[];this.chart_.brushedRange=new tr.b.math.Range();}
+this.updateDiagnostics_();this.mouseDownBinIndex_=undefined;},updateDiagnostics_(){let maps=[];for(let bin of this.brushedBins){for(let map of bin.diagnosticMaps){maps.push(map);}}
+if(maps.length===0){this.$.sample_diagnostics_container.style.display='none';return;}
+if(this.mergeSampleDiagnostics){let merged=new tr.v.d.DiagnosticMap();for(let map of maps){merged.addDiagnostics(map);}
+maps=[merged];}
+this.$.sample_diagnostics_container.style.display='block';this.$.sample_diagnostics.diagnosticMaps=maps;},get histogram(){return this.histogram_;},set histogram(histogram){if(histogram===this.histogram_)return;this.histogram_=histogram;if(this.isAttached)this.updateContents_();},get referenceHistogram(){return this.referenceHistogram_;},set referenceHistogram(histogram){if(histogram===this.referenceHistogram_){return;}
+this.referenceHistogram_=histogram;if(this.histogram)this.updateContents_();},getDeltaScalars_(statNames,scalarMap){if(!this.histogram.canCompare(this.referenceHistogram))return;let mwu=tr.b.math.Statistics.mwu(this.histogram.sampleValues,this.referenceHistogram.sampleValues);for(let deltaStatName of tr.v.Histogram.getDeltaStatisticsNames(statNames)){if(IGNORE_DELTA_STATISTICS_NAMES.includes(deltaStatName))continue;let scalar=this.histogram.getStatisticScalar(deltaStatName,this.referenceHistogram,mwu);if(scalar===undefined)continue;scalarMap.set(deltaStatName,scalar);}
+if(this.histogram.unit.improvementDirection!==tr.b.ImprovementDirection.DONT_CARE){this.$.stats.setSignificanceForKey(`${tr.v.DELTA}avg`,mwu.significance);}},set isYLogScale(logScale){this.chart_.isYLogScale=logScale;},updateContents_(){this.$.chart.style.display='none';this.$.drag_handle.style.display='none';this.$.sample_diagnostics_container.style.display='none';this.$.container.style.justifyContent='';this.brushedBins_=[];while(Polymer.dom(this.$.chart).lastChild){Polymer.dom(this.$.chart).removeChild(Polymer.dom(this.$.chart).lastChild);}
+if(!this.histogram)return;this.$.container.style.display='';let scalarMap=new Map();this.getDeltaScalars_(this.histogram.statisticsNames,scalarMap);for(let[name,scalar]of this.histogram.statisticsScalars){scalarMap.set(name,scalar);}
+this.$.stats.scalarMap=scalarMap;if(this.histogram.diagnostics.size>0){let diagnosticMap=new tr.v.d.DiagnosticMap();for(let[key,diagnostic]of this.histogram.diagnostics){if(key!==tr.v.d.MERGED_FROM_DIAGNOSTIC_KEY&&key!==tr.v.d.MERGED_TO_DIAGNOSTIC_KEY){diagnosticMap.set(key,diagnostic);}}
+this.$.histogram_diagnostics.diagnosticMaps=[diagnosticMap];this.$.histogram_diagnostics.style.display='block';}else{this.$.histogram_diagnostics.style.display='none';}
+if(this.histogram.numValues<=1){this.brushedBins_=this.histogram.allBins;this.updateDiagnostics_();this.$.container.style.justifyContent='flex-end';return;}
+this.$.chart.style.display='block';this.$.drag_handle.style.display='block';if(this.histogram.allBins.length===1){if(this.histogram.min!==this.histogram.max){this.chart_=new tr.ui.b.BoxChart();Polymer.dom(this.$.chart).appendChild(this.chart_);this.chart_.graphWidth=this.graphWidth;this.chart_.graphHeight=this.graphHeight;this.chart_.hideXAxis=true;this.chart_.data=[{x:'',color:'blue',percentile_0:this.histogram.running.min,percentile_25:this.histogram.getApproximatePercentile(0.25),percentile_50:this.histogram.getApproximatePercentile(0.5),percentile_75:this.histogram.getApproximatePercentile(0.75),percentile_100:this.histogram.running.max,}];}
+this.brushedBins_=this.histogram.allBins;this.updateDiagnostics_();return;}
+this.chart_=new tr.ui.b.NameBarChart();Polymer.dom(this.$.chart).appendChild(this.chart_);this.chart_.graphWidth=this.graphWidth;this.chart_.graphHeight=this.graphHeight;this.chart_.addEventListener('item-mousedown',this.onMouseDown_.bind(this));this.chart_.addEventListener('item-mousemove',this.onMouseMove_.bind(this));this.chart_.addEventListener('item-mouseup',this.onMouseUp_.bind(this));this.chart_.hideLegend=true;this.chart_.getDataSeries('y').color='blue';this.chart_.xAxisLabel='#';this.chart_.brushedRange=new tr.b.math.Range();let chartData=[];let binCounts=[];for(let bin of this.histogram.allBins){let x=bin.range.min;if(x===-Number.MAX_VALUE){x='<'+new tr.b.Scalar(this.histogram.unit,bin.range.max).toString();}else{x=new tr.b.Scalar(this.histogram.unit,x).toString();}
+chartData.push({x:x,y:bin.count});binCounts.push(bin.count);}
+binCounts.sort((x,y)=>y-x);let dataRange=tr.b.math.Range.fromExplicitRange(0,binCounts[0]);if(binCounts[1]>0&&binCounts[0]>(binCounts[1]*2)){dataRange.max=binCounts[1]*(1+TRUNCATE_BIN_MARGIN);}
+if(binCounts[2]>0&&binCounts[1]>(binCounts[2]*2)){dataRange.max=binCounts[2]*(1+TRUNCATE_BIN_MARGIN);}
+this.chart_.overrideDataRange=dataRange;this.chart_.data=chartData;}});});'use strict';tr.exportTo('tr.ui.analysis',function(){var EVENT_FIELD=[{key:'start',label:'Start'},{key:'cpuDuration',label:'CPU Duration'},{key:'duration',label:'Duration'},{key:'cpuSelfTime',label:'CPU Self Time'},{key:'selfTime',label:'Self Time'}];function buildDiagnostics_(slice){var diagnostics={};for(var item of EVENT_FIELD){var fieldName=item.key;if(slice[fieldName]===undefined)continue;diagnostics[fieldName]=new tr.v.d.Scalar(new tr.b.Scalar(tr.b.Unit.byName.timeDurationInMs,slice[fieldName]));}
+diagnostics['args']=new tr.v.d.Generic(slice.args);diagnostics['event']=new tr.v.d.RelatedEventSet(slice);return diagnostics;}
+Polymer({is:'tr-ui-a-multi-event-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],created(){this.currentSelection_=undefined;this.eventsHaveDuration_=true;this.eventsHaveSubRows_=true;},ready(){this.$.radioPicker.style.display='none';this.$.radioPicker.items=EVENT_FIELD;this.$.radioPicker.select('cpuSelfTime');this.$.radioPicker.addEventListener('change',()=>{if(this.isAttached)this.updateContents_();});this.$.histogramSpan.graphWidth=400;this.$.histogramSpan.canMergeSampleDiagnostics=false;this.$.histogramContainer.style.display='none';},attached(){if(this.currentSelection_!==undefined)this.updateContents_();},set selection(selection){if(selection.length<=1){throw new Error('Only supports multiple items');}
+this.setSelectionWithoutErrorChecks(selection);},get selection(){return this.currentSelection_;},setSelectionWithoutErrorChecks(selection){this.currentSelection_=selection;if(this.isAttached)this.updateContents_();},get eventsHaveDuration(){return this.eventsHaveDuration_;},set eventsHaveDuration(eventsHaveDuration){this.eventsHaveDuration_=eventsHaveDuration;if(this.isAttached)this.updateContents_();},get eventsHaveSubRows(){return this.eventsHaveSubRows_;},set eventsHaveSubRows(eventsHaveSubRows){this.eventsHaveSubRows_=eventsHaveSubRows;if(this.isAttached)this.updateContents_();},buildHistogram_(selectedKey){var leftBoundary=Number.MAX_VALUE;var rightBoundary=tr.b.math.Statistics.percentile(this.currentSelection_,0.95,function(value){leftBoundary=Math.min(leftBoundary,value[selectedKey]);return value[selectedKey];});if(leftBoundary===rightBoundary)rightBoundary+=1;var histogram=new tr.v.Histogram('',tr.b.Unit.byName.timeDurationInMs,tr.v.HistogramBinBoundaries.createLinear(leftBoundary,rightBoundary,Math.ceil(Math.sqrt(this.currentSelection_.length))));histogram.customizeSummaryOptions({sum:false});for(var slice of this.currentSelection_){histogram.addSample(slice[selectedKey],buildDiagnostics_(slice));}
+return histogram;},updateContents_(){var selection=this.currentSelection_;if(!selection)return;var eventsByTitle=selection.getEventsOrganizedByTitle();var numTitles=tr.b.dictionaryLength(eventsByTitle);this.$.eventSummaryTable.configure({showTotals:numTitles>1,eventsByTitle:eventsByTitle,eventsHaveDuration:this.eventsHaveDuration_,eventsHaveSubRows:this.eventsHaveSubRows_});this.$.selectionSummaryTable.selection=this.currentSelection_;if(numTitles===1){this.$.radioPicker.style.display='block';this.$.histogramContainer.style.display='flex';this.$.histogramSpan.histogram=this.buildHistogram_(this.$.radioPicker.selectedKey);if(this.$.histogramSpan.histogram.numValues===0){this.$.histogramContainer.style.display='none';}}else{this.$.radioPicker.style.display='none';this.$.histogramContainer.style.display='none';}}});return{};});'use strict';tr.exportTo('tr.ui.analysis',function(){var FLOW_IN=0x1;var FLOW_OUT=0x2;var FLOW_IN_OUT=FLOW_IN|FLOW_OUT;function FlowClassifier(){this.numEvents_=0;this.eventsByGUID_={};}
 FlowClassifier.prototype={getFS_:function(event){var fs=this.eventsByGUID_[event.guid];if(fs===undefined){this.numEvents_++;fs={state:0,event:event};this.eventsByGUID_[event.guid]=fs;}
-return fs;},addInFlow:function(event){var fs=this.getFS_(event);fs.state|=FLOW_IN;return event;},addOutFlow:function(event){var fs=this.getFS_(event);fs.state|=FLOW_OUT;return event;},hasEvents:function(){return this.numEvents_>0;},get inFlowEvents(){var selection=new tr.model.EventSet();for(var guid in this.eventsByGUID_){var fs=this.eventsByGUID_[guid];if(fs.state===FLOW_IN)
-selection.push(fs.event);}
-return selection;},get outFlowEvents(){var selection=new tr.model.EventSet();for(var guid in this.eventsByGUID_){var fs=this.eventsByGUID_[guid];if(fs.state===FLOW_OUT)
-selection.push(fs.event);}
-return selection;},get internalFlowEvents(){var selection=new tr.model.EventSet();for(var guid in this.eventsByGUID_){var fs=this.eventsByGUID_[guid];if(fs.state===FLOW_IN_OUT)
-selection.push(fs.event);}
-return selection;}};return{FlowClassifier:FlowClassifier};});'use strict';function*getEventInFlowEvents(event){if(!event.inFlowEvents)
-return;yield*event.inFlowEvents;}
-function*getEventOutFlowEvents(event){if(!event.outFlowEvents)
-return;yield*event.outFlowEvents;}
-function*getEventAncestors(event){if(!event.enumerateAllAncestors)
-return;yield*event.enumerateAllAncestors();}
-function*getEventDescendents(event){if(!event.enumerateAllDescendents)
-return;yield*event.enumerateAllDescendents();}
-Polymer({is:'tr-ui-a-related-events',ready:function(){this.eventGroups_=[];this.cancelFunctions_=[];this.$.table.tableColumns=[{title:'Event(s)',value:function(row){var typeEl=document.createElement('span');typeEl.innerText=row.type;if(row.tooltip)
-typeEl.title=row.tooltip;return typeEl;},width:'150px'},{title:'Link',width:'100%',value:function(row){var linkEl=document.createElement('tr-ui-a-analysis-link');if(row.name)
-linkEl.setSelectionAndContent(row.selection,row.name);else
-linkEl.selection=row.selection;return linkEl;}}];},hasRelatedEvents:function(){return(this.eventGroups_&&this.eventGroups_.length>0);},setRelatedEvents:function(eventSet){this.cancelAllTasks_();this.eventGroups_=[];this.addRuntimeCallStats_(eventSet);this.addV8Slices_(eventSet);this.addConnectedFlows_(eventSet);this.addConnectedEvents_(eventSet);this.addOverlappingSamples_(eventSet);this.updateContents_();},addConnectedFlows_:function(eventSet){var classifier=new tr.ui.analysis.FlowClassifier();eventSet.forEach(function(slice){if(slice.inFlowEvents){slice.inFlowEvents.forEach(function(flow){classifier.addInFlow(flow);});}
-if(slice.outFlowEvents){slice.outFlowEvents.forEach(function(flow){classifier.addOutFlow(flow);});}});if(!classifier.hasEvents())
-return;var addToEventGroups=function(type,flowEvent){this.eventGroups_.push({type:type,selection:new tr.model.EventSet(flowEvent),name:flowEvent.title});};classifier.inFlowEvents.forEach(addToEventGroups.bind(this,'Incoming flow'));classifier.outFlowEvents.forEach(addToEventGroups.bind(this,'Outgoing flow'));classifier.internalFlowEvents.forEach(addToEventGroups.bind(this,'Internal flow'));},cancelAllTasks_:function(){this.cancelFunctions_.forEach(function(cancelFunction){cancelFunction();});this.cancelFunctions_=[];},addConnectedEvents_:function(eventSet){this.cancelFunctions_.push(this.createEventsLinkIfNeeded_('Preceding events','Add all events that have led to the selected one(s), connected by '+'flow arrows or by call stack.',eventSet,function*(event){yield*getEventInFlowEvents(event);yield*getEventAncestors(event);if(event.startSlice)
-yield event.startSlice;}.bind(this)));this.cancelFunctions_.push(this.createEventsLinkIfNeeded_('Following events','Add all events that have been caused by the selected one(s), '+'connected by flow arrows or by call stack.',eventSet,function*(event){yield*getEventOutFlowEvents(event);yield*getEventDescendents(event);if(event.endSlice)
-yield event.endSlice;}.bind(this)));this.cancelFunctions_.push(this.createEventsLinkIfNeeded_('All connected events','Add all events connected to the selected one(s) by flow arrows or '+'by call stack.',eventSet,function*(event){yield*getEventInFlowEvents(event);yield*getEventOutFlowEvents(event);yield*getEventAncestors(event);yield*getEventDescendents(event);if(event.startSlice)
-yield event.startSlice;if(event.endSlice)
-yield event.endSlice;}.bind(this)));},createEventsLinkIfNeeded_:function(title,tooltip,events,connectedFn){events=new tr.model.EventSet(events);var eventsToProcess=new Set(events);var wasChanged=false;var task;var isCanceled=false;function addEventsUntilTimeout(){if(isCanceled)
-return;var timeout=window.performance.now()+8;while(eventsToProcess.size>0&&window.performance.now()<=timeout){var nextEvent=tr.b.getFirstElement(eventsToProcess);eventsToProcess.delete(nextEvent);for(var eventToAdd of connectedFn(nextEvent)){if(!events.contains(eventToAdd)){events.push(eventToAdd);eventsToProcess.add(eventToAdd);wasChanged=true;}}}
-if(eventsToProcess.size>0){var newTask=new tr.b.Task(addEventsUntilTimeout.bind(this),this);task.after(newTask);task=newTask;return;}
-if(!wasChanged)
-return;this.eventGroups_.push({type:title,tooltip:tooltip,selection:events});this.updateContents_();}
+return fs;},addInFlow:function(event){var fs=this.getFS_(event);fs.state|=FLOW_IN;return event;},addOutFlow:function(event){var fs=this.getFS_(event);fs.state|=FLOW_OUT;return event;},hasEvents:function(){return this.numEvents_>0;},get inFlowEvents(){var selection=new tr.model.EventSet();for(var guid in this.eventsByGUID_){var fs=this.eventsByGUID_[guid];if(fs.state===FLOW_IN){selection.push(fs.event);}}
+return selection;},get outFlowEvents(){var selection=new tr.model.EventSet();for(var guid in this.eventsByGUID_){var fs=this.eventsByGUID_[guid];if(fs.state===FLOW_OUT){selection.push(fs.event);}}
+return selection;},get internalFlowEvents(){var selection=new tr.model.EventSet();for(var guid in this.eventsByGUID_){var fs=this.eventsByGUID_[guid];if(fs.state===FLOW_IN_OUT){selection.push(fs.event);}}
+return selection;}};return{FlowClassifier,};});'use strict';function*getEventInFlowEvents(event){if(!event.inFlowEvents)return;yield*event.inFlowEvents;}
+function*getEventOutFlowEvents(event){if(!event.outFlowEvents)return;yield*event.outFlowEvents;}
+function*getEventAncestors(event){if(!event.enumerateAllAncestors)return;yield*event.enumerateAllAncestors();}
+function*getEventDescendents(event){if(!event.enumerateAllDescendents)return;yield*event.enumerateAllDescendents();}
+Polymer({is:'tr-ui-a-related-events',ready:function(){this.eventGroups_=[];this.cancelFunctions_=[];this.$.table.tableColumns=[{title:'Event(s)',value:function(row){let typeEl=document.createElement('span');typeEl.innerText=row.type;if(row.tooltip){typeEl.title=row.tooltip;}
+return typeEl;},width:'150px'},{title:'Link',width:'100%',value:function(row){let linkEl=document.createElement('tr-ui-a-analysis-link');if(row.name){linkEl.setSelectionAndContent(row.selection,row.name);}else{linkEl.selection=row.selection;}
+return linkEl;}}];},hasRelatedEvents:function(){return(this.eventGroups_&&this.eventGroups_.length>0);},setRelatedEvents:function(eventSet){this.cancelAllTasks_();this.eventGroups_=[];this.addRuntimeCallStats_(eventSet);this.addOverlappingV8ICStats_(eventSet);this.addV8GCObjectStats_(eventSet);this.addV8Slices_(eventSet);this.addConnectedFlows_(eventSet);this.addConnectedEvents_(eventSet);this.addOverlappingSamples_(eventSet);this.updateContents_();},addConnectedFlows_:function(eventSet){let classifier=new tr.ui.analysis.FlowClassifier();eventSet.forEach(function(slice){if(slice.inFlowEvents){slice.inFlowEvents.forEach(function(flow){classifier.addInFlow(flow);});}
+if(slice.outFlowEvents){slice.outFlowEvents.forEach(function(flow){classifier.addOutFlow(flow);});}});if(!classifier.hasEvents())return;let addToEventGroups=function(type,flowEvent){this.eventGroups_.push({type:type,selection:new tr.model.EventSet(flowEvent),name:flowEvent.title});};classifier.inFlowEvents.forEach(addToEventGroups.bind(this,'Incoming flow'));classifier.outFlowEvents.forEach(addToEventGroups.bind(this,'Outgoing flow'));classifier.internalFlowEvents.forEach(addToEventGroups.bind(this,'Internal flow'));},cancelAllTasks_:function(){this.cancelFunctions_.forEach(function(cancelFunction){cancelFunction();});this.cancelFunctions_=[];},addConnectedEvents_:function(eventSet){this.cancelFunctions_.push(this.createEventsLinkIfNeeded_('Preceding events','Add all events that have led to the selected one(s), connected by '+'flow arrows or by call stack.',eventSet,function*(event){yield*getEventInFlowEvents(event);yield*getEventAncestors(event);if(event.startSlice){yield event.startSlice;}}.bind(this)));this.cancelFunctions_.push(this.createEventsLinkIfNeeded_('Following events','Add all events that have been caused by the selected one(s), '+'connected by flow arrows or by call stack.',eventSet,function*(event){yield*getEventOutFlowEvents(event);yield*getEventDescendents(event);if(event.endSlice){yield event.endSlice;}}.bind(this)));this.cancelFunctions_.push(this.createEventsLinkIfNeeded_('All connected events','Add all events connected to the selected one(s) by flow arrows or '+'by call stack.',eventSet,function*(event){yield*getEventInFlowEvents(event);yield*getEventOutFlowEvents(event);yield*getEventAncestors(event);yield*getEventDescendents(event);if(event.startSlice){yield event.startSlice;}
+if(event.endSlice){yield event.endSlice;}}.bind(this)));},createEventsLinkIfNeeded_:function(title,tooltip,events,connectedFn){events=new tr.model.EventSet(events);let eventsToProcess=new Set(events);let wasChanged=false;let task;let isCanceled=false;function addEventsUntilTimeout(){if(isCanceled)return;let timeout=window.performance.now()+8;while(eventsToProcess.size>0&&window.performance.now()<=timeout){let nextEvent=tr.b.getFirstElement(eventsToProcess);eventsToProcess.delete(nextEvent);for(let eventToAdd of connectedFn(nextEvent)){if(!events.contains(eventToAdd)){events.push(eventToAdd);eventsToProcess.add(eventToAdd);wasChanged=true;}}}
+if(eventsToProcess.size>0){let newTask=new tr.b.Task(addEventsUntilTimeout.bind(this),this);task.after(newTask);task=newTask;return;}
+if(!wasChanged)return;this.eventGroups_.push({type:title,tooltip:tooltip,selection:events});this.updateContents_();}
 function cancelTask(){isCanceled=true;}
-task=new tr.b.Task(addEventsUntilTimeout.bind(this),this);tr.b.Task.RunWhenIdle(task);return cancelTask;},addOverlappingSamples_:function(eventSet){var samples=new tr.model.EventSet;for(var slice of eventSet){if(!slice.parentContainer||!slice.parentContainer.samples)
-continue;var candidates=slice.parentContainer.samples;var range=tr.b.Range.fromExplicitRange(slice.start,slice.start+slice.duration);var filteredSamples=range.filterArray(candidates,function(value){return value.start;});for(var sample of filteredSamples)
-samples.push(sample);}
-if(samples.length>0){this.eventGroups_.push({type:'Overlapping samples',tooltip:'All samples overlapping the selected slice(s).',selection:samples});}},addV8Slices_:function(eventSet){var v8Slices=new tr.model.EventSet;for(var slice of eventSet){if(slice.category==='v8')
-v8Slices.push(slice);}
-if(v8Slices.length>0){this.eventGroups_.push({type:'V8 Slices',tooltip:'All V8 slices in the selected slice(s).',selection:v8Slices});}},addRuntimeCallStats_:function(eventSet){var slices=new tr.model.EventSet;for(var slice of eventSet){if(slice.category==='v8'&&slice.runtimeCallStats)
-slices.push(slice);}
-if(slices.length>0){this.eventGroups_.push({type:'Runtime call stats table',tooltip:'All V8 slices containing runtime call stats table in the selected slice(s).',selection:slices});}},updateContents_:function(){var table=this.$.table;if(this.eventGroups_===undefined)
-table.tableRows=[];else
-table.tableRows=this.eventGroups_.slice();table.rebuild();}});'use strict';Polymer({is:'tr-ui-a-multi-async-slice-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],get selection(){return this.$.content.selection;},set selection(selection){this.$.content.selection=selection;this.$.relatedEvents.setRelatedEvents(selection);if(this.$.relatedEvents.hasRelatedEvents()){this.$.relatedEvents.style.display='';}else{this.$.relatedEvents.style.display='none';}},get relatedEventsToHighlight(){if(!this.$.content.selection)
-return undefined;var selection=new tr.model.EventSet();this.$.content.selection.forEach(function(asyncEvent){if(!asyncEvent.associatedEvents)
-return;asyncEvent.associatedEvents.forEach(function(event){selection.push(event);});});if(selection.length)
-return selection;return undefined;}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-multi-async-slice-sub-view',tr.model.AsyncSlice,{multi:true,title:'Async Slices',});'use strict';Polymer({is:'tr-ui-a-multi-cpu-slice-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],ready:function(){this.$.content.eventsHaveSubRows=false;},get selection(){return this.$.content.selection;},set selection(selection){this.$.content.setSelectionWithoutErrorChecks(selection);}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-multi-cpu-slice-sub-view',tr.model.CpuSlice,{multi:true,title:'CPU Slices',});'use strict';Polymer({is:'tr-ui-a-multi-flow-event-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],ready:function(){this.$.content.eventsHaveDuration=false;this.$.content.eventsHaveSubRows=false;},set selection(selection){this.$.content.selection=selection;},get selection(){return this.$.content.selection;}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-multi-flow-event-sub-view',tr.model.FlowEvent,{multi:true,title:'Flow Events',});'use strict';Polymer({is:'tr-ui-a-multi-frame-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],created:function(){this.currentSelection_=undefined;},set selection(selection){Polymer.dom(this).textContent='';var realView=document.createElement('tr-ui-a-multi-event-sub-view');realView.eventsHaveDuration=false;realView.eventsHaveSubRows=false;Polymer.dom(this).appendChild(realView);realView.setSelectionWithoutErrorChecks(selection);this.currentSelection_=selection;},get selection(){return this.currentSelection_;},get relatedEventsToHighlight(){if(!this.currentSelection_)
-return undefined;var selection=new tr.model.EventSet();this.currentSelection_.forEach(function(frameEvent){frameEvent.associatedEvents.forEach(function(event){selection.push(event);});});return selection;}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-multi-frame-sub-view',tr.model.Frame,{multi:true,title:'Frames',});'use strict';Polymer({is:'tr-ui-a-multi-instant-event-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],created:function(){this.currentSelection_=undefined;},set selection(selection){Polymer.dom(this.$.content).textContent='';var realView=document.createElement('tr-ui-a-multi-event-sub-view');realView.eventsHaveDuration=false;realView.eventsHaveSubRows=false;Polymer.dom(this.$.content).appendChild(realView);realView.setSelectionWithoutErrorChecks(selection);this.currentSelection_=selection;},get selection(){return this.currentSelection_;}});'use strict';Polymer({is:'tr-ui-a-multi-object-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],created:function(){this.currentSelection_=undefined;},ready:function(){this.$.content.showHeader=false;},get selection(){return this.currentSelection_;},set selection(selection){this.currentSelection_=selection;var objectEvents=tr.b.asArray(selection).sort(tr.b.Range.compareByMinTimes);var timeSpanConfig={unit:tr.b.Unit.byName.timeStampInMs,ownerDocument:this.ownerDocument};var table=this.$.content;table.tableColumns=[{title:'First',value:function(event){if(event instanceof tr.model.ObjectSnapshot)
-return tr.v.ui.createScalarSpan(event.ts,timeSpanConfig);var spanEl=document.createElement('span');Polymer.dom(spanEl).appendChild(tr.v.ui.createScalarSpan(event.creationTs,timeSpanConfig));Polymer.dom(spanEl).appendChild(tr.ui.b.createSpan({textContent:'-',marginLeft:'4px',marginRight:'4px'}));if(event.deletionTs!=Number.MAX_VALUE){Polymer.dom(spanEl).appendChild(tr.v.ui.createScalarSpan(event.deletionTs,timeSpanConfig));}
-return spanEl;},width:'200px'},{title:'Second',value:function(event){var linkEl=document.createElement('tr-ui-a-analysis-link');linkEl.setSelectionAndContent(function(){return new tr.model.EventSet(event);},event.userFriendlyName);return linkEl;},width:'100%'}];table.tableRows=objectEvents;table.rebuild();}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-multi-object-sub-view',tr.model.ObjectInstance,{multi:true,title:'Object Instances',});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-multi-object-sub-view',tr.model.ObjectSnapshot,{multi:true,title:'Object Snapshots',});'use strict';var EventSet=tr.model.EventSet;var CHART_TITLE='Power (W) by ms since vertical sync';var CHART_WIDTH_FRACTION_OF_BODY=0.5;Polymer({is:'tr-ui-a-frame-power-usage-chart',ready:function(){this.chart_=undefined;this.samples_=new EventSet();this.vSyncTimestamps_=[];},get chart(){return this.chart_;},get samples(){return this.samples_;},get vSyncTimestamps(){return this.vSyncTimestamps_;},setData:function(samples,vSyncTimestamps){this.samples_=(samples===undefined)?new EventSet():samples;this.vSyncTimestamps_=(vSyncTimestamps===undefined)?[]:vSyncTimestamps;this.updateContents_();},updateContents_:function(){this.clearChart_();var data=this.getDataForLineChart_();if(data.length===0)
-return;this.chart_=this.createChart_(data);Polymer.dom(this.$.content).appendChild(this.chart_);},createChart_:function(data){var chart=new tr.ui.b.LineChart();var width=document.body.clientWidth*CHART_WIDTH_FRACTION_OF_BODY;chart.setSize({width:width,height:chart.height});chart.chartTitle=CHART_TITLE;chart.data=data;return chart;},clearChart_:function(){var content=this.$.content;while(Polymer.dom(content).firstChild)
-Polymer.dom(content).removeChild(Polymer.dom(content).firstChild);this.chart_=undefined;},getDataForLineChart_:function(){var sortedSamples=this.sortSamplesByTimestampAscending_(this.samples);var vSyncTimestamps=this.vSyncTimestamps.slice();var lastVSyncTimestamp=undefined;var points=[];var frameNumber=0;sortedSamples.forEach(function(sample){while(vSyncTimestamps.length>0&&vSyncTimestamps[0]<=sample.start){lastVSyncTimestamp=vSyncTimestamps.shift();frameNumber++;}
-if(lastVSyncTimestamp===undefined)
-return;var point={x:sample.start-lastVSyncTimestamp};point['f'+frameNumber]=sample.powerInW;points.push(point);});return points;},sortSamplesByTimestampAscending_:function(samples){return samples.toArray().sort(function(smpl1,smpl2){return smpl1.start-smpl2.start;});}});'use strict';Polymer({is:'tr-ui-a-power-sample-summary-table',ready:function(){this.$.table.tableColumns=[{title:'Min power',width:'100px',value:function(row){return tr.b.Unit.byName.powerInWatts.format(row.min);}},{title:'Max power',width:'100px',value:function(row){return tr.b.Unit.byName.powerInWatts.format(row.max);}},{title:'Time-weighted average',width:'100px',value:function(row){return tr.b.Unit.byName.powerInWatts.format(row.timeWeightedAverageInW);}},{title:'Energy consumed',width:'100px',value:function(row){return tr.b.Unit.byName.energyInJoules.format(row.energyConsumedInJ);}},{title:'Sample count',width:'100%',value:function(row){return row.sampleCount;}}];this.samples=new tr.model.EventSet();},get samples(){return this.samples_;},set samples(samples){if(samples===this.samples)
-return;this.samples_=(samples===undefined)?new tr.model.EventSet():samples;this.updateContents_();},updateContents_:function(){if(this.samples.length===0){this.$.table.tableRows=[];}else{this.$.table.tableRows=[{min:this.getMin(),max:this.getMax(),timeWeightedAverageInW:this.getTimeWeightedAverageInW(),energyConsumedInJ:this.getEnergyConsumedInJ(),sampleCount:this.samples.length}];}
-this.$.table.rebuild();},getMin:function(){return Math.min.apply(null,this.samples.map(function(sample){return sample.powerInW;}));},getMax:function(){return Math.max.apply(null,this.samples.map(function(sample){return sample.powerInW;}));},getTimeWeightedAverageInW:function(){var energyConsumedInJ=this.getEnergyConsumedInJ();if(energyConsumedInJ==='N/A')
-return'N/A';var durationInS=tr.b.convertUnit(this.samples.bounds.duration,tr.b.UnitScale.Metric.MILLI,tr.b.UnitScale.Metric.NONE);return energyConsumedInJ/durationInS;},getEnergyConsumedInJ:function(){if(this.samples.length<2)
-return'N/A';var bounds=this.samples.bounds;var series=tr.b.getFirstElement(this.samples).series;return series.getEnergyConsumedInJ(bounds.min,bounds.max);}});'use strict';Polymer({is:'tr-ui-a-multi-power-sample-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],ready:function(){this.currentSelection_=undefined;},get selection(){return this.currentSelection_;},set selection(selection){this.currentSelection_=selection;this.updateContents_();},updateContents_:function(){var samples=this.selection;var vSyncTimestamps=(!samples?[]:tr.b.getFirstElement(samples).series.device.vSyncTimestamps);this.$.summaryTable.samples=samples;this.$.chart.setData(this.selection,vSyncTimestamps);}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-multi-power-sample-sub-view',tr.model.PowerSample,{multi:true,title:'Power Samples',});'use strict';(function(){var MultiDimensionalViewBuilder=tr.b.MultiDimensionalViewBuilder;var SAMPLE_TYPE={COMPILER:'compiler',EXTERNAL:'external',GC:'gc',NATIVEV8:'[native v8]',OTHER:'other',UNKNOWN:'unknown'};Polymer({is:'tr-ui-a-multi-sample-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],created:function(){this.viewOption_=undefined;this.selection_=undefined;},ready:function(){var viewSelector=tr.ui.b.createSelector(this,'viewOption','tracing.ui.analysis.multi_sample_sub_view',MultiDimensionalViewBuilder.ViewType.TOP_DOWN_TREE_VIEW,[{label:'Top-down (Tree)',value:MultiDimensionalViewBuilder.ViewType.TOP_DOWN_TREE_VIEW},{label:'Top-down (Heavy)',value:MultiDimensionalViewBuilder.ViewType.TOP_DOWN_HEAVY_VIEW},{label:'Bottom-up (Heavy)',value:MultiDimensionalViewBuilder.ViewType.BOTTOM_UP_HEAVY_VIEW}]);Polymer.dom(this.$.control).appendChild(viewSelector);this.$.table.selectionMode=tr.ui.b.TableFormat.SelectionMode.ROW;},get selection(){return this.selection_;},set selection(selection){this.selection_=selection;this.updateContents_();},get viewOption(){return this.viewOption_;},set viewOption(viewOption){this.viewOption_=viewOption;this.updateContents_();},createSamplingSummary_:function(selection,viewOption){var builder=new MultiDimensionalViewBuilder(1,1);var samples=selection.filter(function(event){return event instanceof tr.model.Sample;});samples.forEach(function(sample){builder.addPath([sample.getUserFriendlyStackTrace().reverse()],[1],MultiDimensionalViewBuilder.ValueKind.SELF);});return builder.buildView(viewOption);},processTypedSampleRow_:function(row){var title=row.title[0];switch(title){case SAMPLE_TYPE.COMPILER:case SAMPLE_TYPE.EXTERNAL:case SAMPLE_TYPE.GC:case SAMPLE_TYPE.OTHER:row.functionName=title;row.fileName='N/A';return true;case SAMPLE_TYPE.UNKNOWN:row.functionName=SAMPLE_TYPE.UNKNOWN;row.fileName=SAMPLE_TYPE.UNKNOWN;return true;default:return false;}},processNativeV8SampleRow_:function(row){var title=row.title[0];if(!title.includes(SAMPLE_TYPE.NATIVEV8))
-return false;var arr=title.split(SAMPLE_TYPE.NATIVEV8);row.functionName=arr[0].trim();if(row.functionName==='')
-row.functionName='(anonymous function)';row.fileName=SAMPLE_TYPE.NATIVEV8;var fileNameSuffix=arr[1].trim();if(fileNameSuffix!=='')
-row.fileName+=' '+fileNameSuffix;return true;},processGeneralSampleRow_:function(row){var title=row.title[0];var idx=title.lastIndexOf(' ');if(idx===-1){row.functionName=title;row.fileName='unknown';return;}
-var prefix=title.substr(0,idx);var suffix=title.substr(idx+1);if(suffix.startsWith('v8/')){row.functionName=suffix;row.fileName='unknown';}else if(suffix===''){row.functionName=prefix;row.fileName='unknown';}else if(prefix===''){row.functionName='(anonymous function)';row.fileName=suffix.substr(suffix.lastIndexOf('/')+1);}else{row.functionName=prefix;row.fileName=suffix.substr(suffix.lastIndexOf('/')+1);}},processSampleRows_:function(rows){rows.forEach(function(row){if(!this.processTypedSampleRow_(row)&&!this.processNativeV8SampleRow_(row))
-this.processGeneralSampleRow_(row);this.processSampleRows_(row.subRows);},this);},updateContents_:function(){if(this.selection===undefined){this.$.table.tableColumns=[];this.$.table.tableRows=[];this.$.table.rebuild();return;}
-var samplingData=this.createSamplingSummary_(this.selection,this.viewOption);var total=samplingData.values[0].total;var columns=[this.createPercentColumn_('Total',total),this.createSamplesColumn_('Total'),this.createPercentColumn_('Self',total),this.createSamplesColumn_('Self'),{title:'Function Name',value:function(row){return row.functionName;},width:'150px',cmp:function(a,b){return a.functionName.localeCompare(b.functionName);},showExpandButtons:true},{title:'Location',value:function(row){return row.fileName;},width:'250px',cmp:function(a,b){return a.fileName.localeCompare(b.fileName);}}];this.processSampleRows_(samplingData.subRows);this.$.table.tableColumns=columns;this.$.table.sortColumnIndex=1;this.$.table.sortDescending=true;this.$.table.tableRows=samplingData.subRows;this.$.table.rebuild();},createPercentColumn_:function(title,samplingDataTotal){var field=title.toLowerCase();return{title:title+' percent',value:function(row){return tr.v.ui.createScalarSpan(row.values[0][field]/samplingDataTotal,{customContextRange:tr.b.Range.PERCENT_RANGE,unit:tr.b.Unit.byName.normalizedPercentage,context:{minimumFractionDigits:2,maximumFractionDigits:2},rightAlign:true});},width:'60px',cmp:function(a,b){return a.values[0][field]-b.values[0][field];}};},createSamplesColumn_:function(title){var field=title.toLowerCase();return{title:title+' samples',value:function(row){return tr.v.ui.createScalarSpan(row.values[0][field],{unit:tr.b.Unit.byName.unitlessNumber,context:{maximumFractionDigits:0},rightAlign:true});},width:'60px',cmp:function(a,b){return a.values[0][field]-b.values[0][field];}};}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-multi-sample-sub-view',tr.model.Sample,{multi:true,title:'Samples',});})();'use strict';Polymer({is:'tr-ui-a-multi-thread-slice-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],created:function(){this.selection_=undefined;},get selection(){return this.selection_;},set selection(selection){this.selection_=selection;if(tr.isExported('tr.ui.e.chrome.cc.RasterTaskSelection')){if(tr.ui.e.chrome.cc.RasterTaskSelection.supports(selection)){var ltvSelection=new tr.ui.e.chrome.cc.RasterTaskSelection(selection);var ltv=new tr.ui.e.chrome.cc.LayerTreeHostImplSnapshotView();ltv.objectSnapshot=ltvSelection.containingSnapshot;ltv.selection=ltvSelection;ltv.extraHighlightsByLayerId=ltvSelection.extraHighlightsByLayerId;Polymer.dom(this.$.content).textContent='';Polymer.dom(this.$.content).appendChild(ltv);this.requiresTallView_=true;return;}}
-Polymer.dom(this.$.content).textContent='';var mesv=document.createElement('tr-ui-a-multi-event-sub-view');mesv.selection=selection;Polymer.dom(this.$.content).appendChild(mesv);var relatedEvents=document.createElement('tr-ui-a-related-events');relatedEvents.setRelatedEvents(selection);if(relatedEvents.hasRelatedEvents()){Polymer.dom(this.$.content).appendChild(relatedEvents);}},get requiresTallView(){if(this.$.content.children.length===0)
-return false;var childTagName=this.$.content.children[0].tagName;if(childTagName==='TR-UI-A-MULTI-EVENT-SUB-VIEW')
-return false;return true;}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-multi-thread-slice-sub-view',tr.model.ThreadSlice,{multi:true,title:'Slices',});'use strict';Polymer({is:'tr-ui-a-multi-thread-time-slice-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],ready:function(){this.$.content.eventsHaveSubRows=false;},get selection(){return this.$.content.selection;},set selection(selection){this.$.content.setSelectionWithoutErrorChecks(selection);}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-multi-thread-time-slice-sub-view',tr.model.ThreadTimeSlice,{multi:true,title:'Thread Timeslices',});'use strict';Polymer({is:'tr-ui-a-user-expectation-related-samples-table',ready:function(){this.samples_=[];this.$.table.tableColumns=[{title:'Event(s)',value:function(row){var typeEl=document.createElement('span');typeEl.innerText=row.type;if(row.tooltip)
-typeEl.title=row.tooltip;return typeEl;},width:'150px'},{title:'Link',width:'100%',value:function(row){var linkEl=document.createElement('tr-ui-a-analysis-link');if(row.name)
-linkEl.setSelectionAndContent(row.selection,row.name);else
-linkEl.selection=row.selection;return linkEl;}}];},hasRelatedSamples:function(){return(this.samples_&&this.samples_.length>0);},set selection(eventSet){this.samples_=[];var samples=new tr.model.EventSet;eventSet.forEach(function(ue){samples.addEventSet(ue.associatedSamples);}.bind(this));if(samples.length>0){this.samples_.push({type:'Overlapping samples',tooltip:'All samples overlapping the selected user expectation(s).',selection:samples});}
-this.updateContents_();},updateContents_:function(){var table=this.$.table;if(this.samples_&&this.samples_.length>0)
-table.tableRows=this.samples_.slice();else
-table.tableRows=[];table.rebuild();}});'use strict';Polymer({is:'tr-ui-a-multi-interaction-record-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],created:function(){this.currentSelection_=undefined;},set selection(selection){this.currentSelection_=selection;this.$.realView.setSelectionWithoutErrorChecks(selection);this.currentSelection_=selection;this.$.relatedSamples.selection=selection;if(this.$.relatedSamples.hasRelatedSamples())
-this.$.events.style.display='';else
-this.$.events.style.display='none';},get selection(){return this.currentSelection_;},get relatedEventsToHighlight(){if(!this.currentSelection_)
-return undefined;var selection=new tr.model.EventSet();this.currentSelection_.forEach(function(ir){ir.associatedEvents.forEach(function(event){selection.push(event);});});return selection;}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-single-user-expectation-sub-view',tr.model.um.UserExpectation,{multi:true,title:'User Expectations',});'use strict';Polymer({is:'tr-ui-a-single-async-slice-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],get selection(){return this.$.content.selection;},set selection(selection){if(selection.length!==1)
-throw new Error('Only supports single slices');this.$.content.setSelectionWithoutErrorChecks(selection);this.$.relatedEvents.setRelatedEvents(selection);if(this.$.relatedEvents.hasRelatedEvents()){this.$.relatedEvents.style.display='';}else{this.$.relatedEvents.style.display='none';}},getEventRows_:function(event){var rows=this.__proto__.__proto__.getEventRows_(event);rows.splice(0,0,{name:'ID',value:event.id});return rows;},get relatedEventsToHighlight(){if(!this.currentSelection_)
-return undefined;return tr.b.getOnlyElement(this.currentSelection_).associatedEvents;}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-single-async-slice-sub-view',tr.model.AsyncSlice,{multi:false,title:'Async Slice',});'use strict';Polymer({is:'tr-ui-a-single-cpu-slice-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],created:function(){this.currentSelection_=undefined;},get selection(){return this.currentSelection_;},set selection(selection){var cpuSlice=tr.b.getOnlyElement(selection);if(!(cpuSlice instanceof tr.model.CpuSlice))
-throw new Error('Only supports thread time slices');this.currentSelection_=selection;var thread=cpuSlice.threadThatWasRunning;var root=Polymer.dom(this.root);if(thread){Polymer.dom(root.querySelector('#process-name')).textContent=thread.parent.userFriendlyName;Polymer.dom(root.querySelector('#thread-name')).textContent=thread.userFriendlyName;}else{root.querySelector('#process-name').parentElement.style.display='none';Polymer.dom(root.querySelector('#thread-name')).textContent=cpuSlice.title;}
+task=new tr.b.Task(addEventsUntilTimeout.bind(this),this);tr.b.Task.RunWhenIdle(task);return cancelTask;},addOverlappingSamples_:function(eventSet){let samples=new tr.model.EventSet();for(let slice of eventSet){if(!slice.parentContainer||!slice.parentContainer.samples){continue;}
+let candidates=slice.parentContainer.samples;let range=tr.b.math.Range.fromExplicitRange(slice.start,slice.start+slice.duration);let filteredSamples=range.filterArray(candidates,function(value){return value.start;});for(let sample of filteredSamples){samples.push(sample);}}
+if(samples.length>0){this.eventGroups_.push({type:'Overlapping samples',tooltip:'All samples overlapping the selected slice(s).',selection:samples});}},addV8Slices_:function(eventSet){let v8Slices=new tr.model.EventSet();for(let slice of eventSet){if(slice.category==='v8'){v8Slices.push(slice);}}
+if(v8Slices.length>0){this.eventGroups_.push({type:'V8 Slices',tooltip:'All V8 slices in the selected slice(s).',selection:v8Slices});}},addRuntimeCallStats_:function(eventSet){let slices=new tr.model.EventSet();for(let slice of eventSet){if(slice.category==='v8'&&slice.runtimeCallStats){slices.push(slice);}}
+if(slices.length>0){this.eventGroups_.push({type:'Runtime call stats table',tooltip:'All V8 slices containing runtime call stats table in the selected slice(s).',selection:slices});}},addV8GCObjectStats_:function(eventSet){let slices=new tr.model.EventSet();for(let slice of eventSet){if(slice.title==='V8.GC_Objects_Stats'){slices.push(slice);}}
+if(slices.length>0){this.eventGroups_.push({type:'V8 GC stats table',tooltip:'All V8 GC statistics slices in the selected set.',selection:slices});}},addOverlappingV8ICStats_:function(eventSet){let slices=new tr.model.EventSet();for(let slice of eventSet){if(!slice.parentContainer||!slice.parentContainer.sliceGroup){continue;}
+let sliceGroup=slice.parentContainer.sliceGroup.slices;let range=tr.b.math.Range.fromExplicitRange(slice.start,slice.start+slice.duration);let filteredSlices=range.filterArray(sliceGroup,value=>value.start);let icSlices=filteredSlices.filter(x=>x.title==='V8.ICStats');for(let icSlice of icSlices){slices.push(icSlice);}}
+if(slices.length>0){this.eventGroups_.push({type:'Overlapping V8 IC stats',tooltip:'All V8 IC statistics overlapping the selected set.',selection:slices});}},updateContents_:function(){let table=this.$.table;if(this.eventGroups_===undefined){table.tableRows=[];}else{table.tableRows=this.eventGroups_.slice();}
+table.rebuild();}});'use strict';Polymer({is:'tr-ui-a-multi-async-slice-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],get selection(){return this.$.content.selection;},set selection(selection){this.$.content.selection=selection;this.$.relatedEvents.setRelatedEvents(selection);if(this.$.relatedEvents.hasRelatedEvents()){this.$.relatedEvents.style.display='';}else{this.$.relatedEvents.style.display='none';}},get relatedEventsToHighlight(){if(!this.$.content.selection)return undefined;var selection=new tr.model.EventSet();this.$.content.selection.forEach(function(asyncEvent){if(!asyncEvent.associatedEvents)return;asyncEvent.associatedEvents.forEach(function(event){selection.push(event);});});if(selection.length)return selection;return undefined;}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-multi-async-slice-sub-view',tr.model.AsyncSlice,{multi:true,title:'Async Slices',});'use strict';Polymer({is:'tr-ui-a-multi-cpu-slice-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],ready:function(){this.$.content.eventsHaveSubRows=false;},get selection(){return this.$.content.selection;},set selection(selection){this.$.content.setSelectionWithoutErrorChecks(selection);}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-multi-cpu-slice-sub-view',tr.model.CpuSlice,{multi:true,title:'CPU Slices',});'use strict';Polymer({is:'tr-ui-a-multi-flow-event-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],ready:function(){this.$.content.eventsHaveDuration=false;this.$.content.eventsHaveSubRows=false;},set selection(selection){this.$.content.selection=selection;},get selection(){return this.$.content.selection;}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-multi-flow-event-sub-view',tr.model.FlowEvent,{multi:true,title:'Flow Events',});'use strict';Polymer({is:'tr-ui-a-multi-frame-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],created:function(){this.currentSelection_=undefined;},set selection(selection){Polymer.dom(this).textContent='';var realView=document.createElement('tr-ui-a-multi-event-sub-view');realView.eventsHaveDuration=false;realView.eventsHaveSubRows=false;Polymer.dom(this).appendChild(realView);realView.setSelectionWithoutErrorChecks(selection);this.currentSelection_=selection;},get selection(){return this.currentSelection_;},get relatedEventsToHighlight(){if(!this.currentSelection_)return undefined;var selection=new tr.model.EventSet();this.currentSelection_.forEach(function(frameEvent){frameEvent.associatedEvents.forEach(function(event){selection.push(event);});});return selection;}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-multi-frame-sub-view',tr.model.Frame,{multi:true,title:'Frames',});'use strict';Polymer({is:'tr-ui-a-multi-instant-event-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],created:function(){this.currentSelection_=undefined;},set selection(selection){Polymer.dom(this.$.content).textContent='';var realView=document.createElement('tr-ui-a-multi-event-sub-view');realView.eventsHaveDuration=false;realView.eventsHaveSubRows=false;Polymer.dom(this.$.content).appendChild(realView);realView.setSelectionWithoutErrorChecks(selection);this.currentSelection_=selection;},get selection(){return this.currentSelection_;}});'use strict';Polymer({is:'tr-ui-a-multi-object-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],created:function(){this.currentSelection_=undefined;},ready:function(){this.$.content.showHeader=false;},get selection(){return this.currentSelection_;},set selection(selection){this.currentSelection_=selection;var objectEvents=tr.b.asArray(selection).sort(tr.b.math.Range.compareByMinTimes);var timeSpanConfig={unit:tr.b.Unit.byName.timeStampInMs,ownerDocument:this.ownerDocument};var table=this.$.content;table.tableColumns=[{title:'First',value:function(event){if(event instanceof tr.model.ObjectSnapshot){return tr.v.ui.createScalarSpan(event.ts,timeSpanConfig);}
+var spanEl=document.createElement('span');Polymer.dom(spanEl).appendChild(tr.v.ui.createScalarSpan(event.creationTs,timeSpanConfig));Polymer.dom(spanEl).appendChild(tr.ui.b.createSpan({textContent:'-',marginLeft:'4px',marginRight:'4px'}));if(event.deletionTs!==Number.MAX_VALUE){Polymer.dom(spanEl).appendChild(tr.v.ui.createScalarSpan(event.deletionTs,timeSpanConfig));}
+return spanEl;},width:'200px'},{title:'Second',value:function(event){var linkEl=document.createElement('tr-ui-a-analysis-link');linkEl.setSelectionAndContent(function(){return new tr.model.EventSet(event);},event.userFriendlyName);return linkEl;},width:'100%'}];table.tableRows=objectEvents;table.rebuild();}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-multi-object-sub-view',tr.model.ObjectInstance,{multi:true,title:'Object Instances',});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-multi-object-sub-view',tr.model.ObjectSnapshot,{multi:true,title:'Object Snapshots',});'use strict';var EventSet=tr.model.EventSet;var CHART_TITLE='Power (W) by ms since vertical sync';Polymer({is:'tr-ui-a-frame-power-usage-chart',ready:function(){this.chart_=undefined;this.samples_=new EventSet();this.vSyncTimestamps_=[];},attached(){if(this.samples_)this.updateContents_();},get chart(){return this.chart_;},get samples(){return this.samples_;},get vSyncTimestamps(){return this.vSyncTimestamps_;},setData:function(samples,vSyncTimestamps){this.samples_=(samples===undefined)?new EventSet():samples;this.vSyncTimestamps_=(vSyncTimestamps===undefined)?[]:vSyncTimestamps;if(this.isAttached)this.updateContents_();},updateContents_:function(){this.clearChart_();var data=this.getDataForLineChart_();if(data.length===0)return;this.chart_=new tr.ui.b.LineChart();Polymer.dom(this.$.content).appendChild(this.chart_);this.chart_.chartTitle=CHART_TITLE;this.chart_.data=data;},clearChart_:function(){var content=this.$.content;while(Polymer.dom(content).firstChild){Polymer.dom(content).removeChild(Polymer.dom(content).firstChild);}
+this.chart_=undefined;},getDataForLineChart_:function(){var sortedSamples=this.sortSamplesByTimestampAscending_(this.samples);var vSyncTimestamps=this.vSyncTimestamps.slice();var lastVSyncTimestamp=undefined;var points=[];var frameNumber=0;sortedSamples.forEach(function(sample){while(vSyncTimestamps.length>0&&vSyncTimestamps[0]<=sample.start){lastVSyncTimestamp=vSyncTimestamps.shift();frameNumber++;}
+if(lastVSyncTimestamp===undefined)return;var point={x:sample.start-lastVSyncTimestamp};point['f'+frameNumber]=sample.powerInW;points.push(point);});return points;},sortSamplesByTimestampAscending_:function(samples){return samples.toArray().sort(function(smpl1,smpl2){return smpl1.start-smpl2.start;});}});'use strict';Polymer({is:'tr-ui-a-power-sample-summary-table',ready:function(){this.$.table.tableColumns=[{title:'Min power',width:'100px',value:function(row){return tr.b.Unit.byName.powerInWatts.format(row.min);}},{title:'Max power',width:'100px',value:function(row){return tr.b.Unit.byName.powerInWatts.format(row.max);}},{title:'Time-weighted average',width:'100px',value:function(row){return tr.b.Unit.byName.powerInWatts.format(row.timeWeightedAverageInW);}},{title:'Energy consumed',width:'100px',value:function(row){return tr.b.Unit.byName.energyInJoules.format(row.energyConsumedInJ);}},{title:'Sample count',width:'100%',value:function(row){return row.sampleCount;}}];this.samples=new tr.model.EventSet();},get samples(){return this.samples_;},set samples(samples){if(samples===this.samples)return;this.samples_=(samples===undefined)?new tr.model.EventSet():samples;this.updateContents_();},updateContents_:function(){if(this.samples.length===0){this.$.table.tableRows=[];}else{this.$.table.tableRows=[{min:this.getMin(),max:this.getMax(),timeWeightedAverageInW:this.getTimeWeightedAverageInW(),energyConsumedInJ:this.getEnergyConsumedInJ(),sampleCount:this.samples.length}];}
+this.$.table.rebuild();},getMin:function(){return Math.min.apply(null,this.samples.map(function(sample){return sample.powerInW;}));},getMax:function(){return Math.max.apply(null,this.samples.map(function(sample){return sample.powerInW;}));},getTimeWeightedAverageInW:function(){var energyConsumedInJ=this.getEnergyConsumedInJ();if(energyConsumedInJ==='N/A')return'N/A';var durationInS=tr.b.convertUnit(this.samples.bounds.duration,tr.b.UnitPrefixScale.METRIC.MILLI,tr.b.UnitPrefixScale.METRIC.NONE);return energyConsumedInJ/durationInS;},getEnergyConsumedInJ:function(){if(this.samples.length<2)return'N/A';var bounds=this.samples.bounds;var series=tr.b.getFirstElement(this.samples).series;return series.getEnergyConsumedInJ(bounds.min,bounds.max);}});'use strict';Polymer({is:'tr-ui-a-multi-power-sample-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],ready:function(){this.currentSelection_=undefined;},get selection(){return this.currentSelection_;},set selection(selection){this.currentSelection_=selection;this.updateContents_();},updateContents_:function(){var samples=this.selection;var vSyncTimestamps=(!samples?[]:tr.b.getFirstElement(samples).series.device.vSyncTimestamps);this.$.summaryTable.samples=samples;this.$.chart.setData(this.selection,vSyncTimestamps);}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-multi-power-sample-sub-view',tr.model.PowerSample,{multi:true,title:'Power Samples',});'use strict';(function(){var MultiDimensionalViewBuilder=tr.b.MultiDimensionalViewBuilder;Polymer({is:'tr-ui-a-multi-sample-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],created:function(){this.viewOption_=undefined;this.selection_=undefined;},ready:function(){var viewSelector=tr.ui.b.createSelector(this,'viewOption','tracing.ui.analysis.multi_sample_sub_view',MultiDimensionalViewBuilder.ViewType.TOP_DOWN_TREE_VIEW,[{label:'Top-down (Tree)',value:MultiDimensionalViewBuilder.ViewType.TOP_DOWN_TREE_VIEW},{label:'Top-down (Heavy)',value:MultiDimensionalViewBuilder.ViewType.TOP_DOWN_HEAVY_VIEW},{label:'Bottom-up (Heavy)',value:MultiDimensionalViewBuilder.ViewType.BOTTOM_UP_HEAVY_VIEW}]);Polymer.dom(this.$.control).appendChild(viewSelector);this.$.table.selectionMode=tr.ui.b.TableFormat.SelectionMode.ROW;},get selection(){return this.selection_;},set selection(selection){this.selection_=selection;this.updateContents_();},get viewOption(){return this.viewOption_;},set viewOption(viewOption){this.viewOption_=viewOption;this.updateContents_();},createSamplingSummary_:function(selection,viewOption){var builder=new MultiDimensionalViewBuilder(1,1);var samples=selection.filter(event=>event instanceof tr.model.Sample);samples.forEach(function(sample){builder.addPath([sample.userFriendlyStack.reverse()],[1],MultiDimensionalViewBuilder.ValueKind.SELF);});return builder.buildView(viewOption);},processSampleRows_:function(rows){for(var row of rows){var title=row.title[0];var results=/(.*) (Deoptimized reason: .*)/.exec(title);if(results!==null){row.deoptReason=results[2];title=results[1];}
+results=/(.*) url: (.*)/.exec(title);if(results!==null){row.functionName=results[1];row.url=results[2];if(row.functionName===''){row.functionName='(anonymous function)';}
+if(row.url===''){row.url='unknown';}}else{row.functionName=title;row.url='unknown';}
+this.processSampleRows_(row.subRows);}},updateContents_:function(){if(this.selection===undefined){this.$.table.tableColumns=[];this.$.table.tableRows=[];this.$.table.rebuild();return;}
+var samplingData=this.createSamplingSummary_(this.selection,this.viewOption);var total=samplingData.values[0].total;var columns=[this.createPercentColumn_('Total',total),this.createSamplesColumn_('Total'),this.createPercentColumn_('Self',total),this.createSamplesColumn_('Self'),{title:'Function Name',value:function(row){if(row.deoptReason!==undefined){var spanEl=tr.ui.b.createSpan({italic:true,color:'#F44336',tooltip:row.deoptReason});spanEl.innerText=row.functionName;return spanEl;}
+return row.functionName;},width:'150px',cmp:(a,b)=>a.functionName.localeCompare(b.functionName),showExpandButtons:true},{title:'Location',value:function(row){return row.url;},width:'250px',cmp:(a,b)=>a.url.localeCompare(b.url),}];this.processSampleRows_(samplingData.subRows);this.$.table.tableColumns=columns;this.$.table.sortColumnIndex=1;this.$.table.sortDescending=true;this.$.table.tableRows=samplingData.subRows;this.$.table.rebuild();},createPercentColumn_:function(title,samplingDataTotal){var field=title.toLowerCase();return{title:title+' percent',value:function(row){return tr.v.ui.createScalarSpan(row.values[0][field]/samplingDataTotal,{customContextRange:tr.b.math.Range.PERCENT_RANGE,unit:tr.b.Unit.byName.normalizedPercentage,context:{minimumFractionDigits:2,maximumFractionDigits:2},});},width:'60px',cmp:(a,b)=>a.values[0][field]-b.values[0][field]};},createSamplesColumn_:function(title){var field=title.toLowerCase();return{title:title+' samples',value:function(row){return tr.v.ui.createScalarSpan(row.values[0][field],{unit:tr.b.Unit.byName.unitlessNumber,context:{maximumFractionDigits:0},});},width:'60px',cmp:(a,b)=>a.values[0][field]-b.values[0][field]};}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-multi-sample-sub-view',tr.model.Sample,{multi:true,title:'Samples',});})();'use strict';Polymer({is:'tr-ui-a-multi-thread-slice-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],created:function(){this.selection_=undefined;},get selection(){return this.selection_;},set selection(selection){this.selection_=selection;if(tr.isExported('tr.ui.e.chrome.cc.RasterTaskSelection')){if(tr.ui.e.chrome.cc.RasterTaskSelection.supports(selection)){var ltvSelection=new tr.ui.e.chrome.cc.RasterTaskSelection(selection);var ltv=new tr.ui.e.chrome.cc.LayerTreeHostImplSnapshotView();ltv.objectSnapshot=ltvSelection.containingSnapshot;ltv.selection=ltvSelection;ltv.extraHighlightsByLayerId=ltvSelection.extraHighlightsByLayerId;Polymer.dom(this.$.content).textContent='';Polymer.dom(this.$.content).appendChild(ltv);this.requiresTallView_=true;return;}}
+Polymer.dom(this.$.content).textContent='';var mesv=document.createElement('tr-ui-a-multi-event-sub-view');mesv.selection=selection;Polymer.dom(this.$.content).appendChild(mesv);var relatedEvents=document.createElement('tr-ui-a-related-events');relatedEvents.setRelatedEvents(selection);if(relatedEvents.hasRelatedEvents()){Polymer.dom(this.$.content).appendChild(relatedEvents);}},get requiresTallView(){if(this.$.content.children.length===0)return false;var childTagName=this.$.content.children[0].tagName;if(childTagName==='TR-UI-A-MULTI-EVENT-SUB-VIEW'){return false;}
+return true;}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-multi-thread-slice-sub-view',tr.model.ThreadSlice,{multi:true,title:'Slices',});'use strict';Polymer({is:'tr-ui-a-multi-thread-time-slice-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],ready:function(){this.$.content.eventsHaveSubRows=false;},get selection(){return this.$.content.selection;},set selection(selection){this.$.content.setSelectionWithoutErrorChecks(selection);}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-multi-thread-time-slice-sub-view',tr.model.ThreadTimeSlice,{multi:true,title:'Thread Timeslices',});'use strict';Polymer({is:'tr-ui-a-user-expectation-related-samples-table',ready:function(){this.samples_=[];this.$.table.tableColumns=[{title:'Event(s)',value:function(row){var typeEl=document.createElement('span');typeEl.innerText=row.type;if(row.tooltip){typeEl.title=row.tooltip;}
+return typeEl;},width:'150px'},{title:'Link',width:'100%',value:function(row){var linkEl=document.createElement('tr-ui-a-analysis-link');if(row.name){linkEl.setSelectionAndContent(row.selection,row.name);}else{linkEl.selection=row.selection;}
+return linkEl;}}];},hasRelatedSamples:function(){return(this.samples_&&this.samples_.length>0);},set selection(eventSet){this.samples_=[];var samples=new tr.model.EventSet;eventSet.forEach(function(ue){samples.addEventSet(ue.associatedSamples);}.bind(this));if(samples.length>0){this.samples_.push({type:'Overlapping samples',tooltip:'All samples overlapping the selected user expectation(s).',selection:samples});}
+this.updateContents_();},updateContents_:function(){var table=this.$.table;if(this.samples_&&this.samples_.length>0){table.tableRows=this.samples_.slice();}else{table.tableRows=[];}
+table.rebuild();}});'use strict';Polymer({is:'tr-ui-a-multi-interaction-record-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],created:function(){this.currentSelection_=undefined;},set selection(selection){this.currentSelection_=selection;this.$.realView.setSelectionWithoutErrorChecks(selection);this.currentSelection_=selection;this.$.relatedSamples.selection=selection;if(this.$.relatedSamples.hasRelatedSamples()){this.$.events.style.display='';}else{this.$.events.style.display='none';}},get selection(){return this.currentSelection_;},get relatedEventsToHighlight(){if(!this.currentSelection_)return undefined;var selection=new tr.model.EventSet();this.currentSelection_.forEach(function(ir){ir.associatedEvents.forEach(function(event){selection.push(event);});});return selection;}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-single-user-expectation-sub-view',tr.model.um.UserExpectation,{multi:true,title:'User Expectations',});'use strict';Polymer({is:'tr-ui-a-single-async-slice-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],get selection(){return this.$.content.selection;},set selection(selection){if(selection.length!==1){throw new Error('Only supports single slices');}
+this.$.content.setSelectionWithoutErrorChecks(selection);this.$.relatedEvents.setRelatedEvents(selection);if(this.$.relatedEvents.hasRelatedEvents()){this.$.relatedEvents.style.display='';}else{this.$.relatedEvents.style.display='none';}},getEventRows_:function(event){var rows=this.__proto__.__proto__.getEventRows_(event);rows.splice(0,0,{name:'ID',value:event.id});return rows;},get relatedEventsToHighlight(){if(!this.currentSelection_)return undefined;return tr.b.getOnlyElement(this.currentSelection_).associatedEvents;}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-single-async-slice-sub-view',tr.model.AsyncSlice,{multi:false,title:'Async Slice',});'use strict';Polymer({is:'tr-ui-a-single-cpu-slice-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],created:function(){this.currentSelection_=undefined;},get selection(){return this.currentSelection_;},set selection(selection){var cpuSlice=tr.b.getOnlyElement(selection);if(!(cpuSlice instanceof tr.model.CpuSlice)){throw new Error('Only supports thread time slices');}
+this.currentSelection_=selection;var thread=cpuSlice.threadThatWasRunning;var root=Polymer.dom(this.root);if(thread){Polymer.dom(root.querySelector('#process-name')).textContent=thread.parent.userFriendlyName;Polymer.dom(root.querySelector('#thread-name')).textContent=thread.userFriendlyName;}else{root.querySelector('#process-name').parentElement.style.display='none';Polymer.dom(root.querySelector('#thread-name')).textContent=cpuSlice.title;}
 root.querySelector('#start').setValueAndUnit(cpuSlice.start,tr.b.Unit.byName.timeStampInMs);root.querySelector('#duration').setValueAndUnit(cpuSlice.duration,tr.b.Unit.byName.timeDurationInMs);var runningThreadEl=root.querySelector('#running-thread');var timeSlice=cpuSlice.getAssociatedTimeslice();if(!timeSlice){runningThreadEl.parentElement.style.display='none';}else{var threadLink=document.createElement('tr-ui-a-analysis-link');threadLink.selection=new tr.model.EventSet(timeSlice);Polymer.dom(threadLink).textContent='Click to select';runningThreadEl.parentElement.style.display='';Polymer.dom(runningThreadEl).textContent='';Polymer.dom(runningThreadEl).appendChild(threadLink);}
 root.querySelector('#args').object=cpuSlice.args;}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-single-cpu-slice-sub-view',tr.model.CpuSlice,{multi:false,title:'CPU Slice',});'use strict';function createAnalysisLinkTo(event){var linkEl=document.createElement('tr-ui-a-analysis-link');linkEl.setSelectionAndContent(new tr.model.EventSet(event),event.userFriendlyName);return linkEl;}
-Polymer({is:'tr-ui-a-single-flow-event-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],listeners:{'singleEventSubView.customize-rows':'onCustomizeRows_'},set selection(selection){this.currentSelection_=selection;this.$.singleEventSubView.setSelectionWithoutErrorChecks(selection);},get selection(){return this.currentSelection_;},onCustomizeRows_:function(e){var event=tr.b.getOnlyElement(this.currentSelection_);var rows=e.rows;rows.unshift({name:'ID',value:event.id});rows.push({name:'From',value:createAnalysisLinkTo(event.startSlice)});rows.push({name:'To',value:createAnalysisLinkTo(event.endSlice)});}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-single-flow-event-sub-view',tr.model.FlowEvent,{multi:false,title:'Flow Event',});'use strict';Polymer({is:'tr-ui-a-single-frame-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],ready:function(){this.currentSelection_=undefined;},get selection(){return this.currentSelection_;},set selection(selection){this.currentSelection_=selection;this.$.asv.selection=tr.b.getOnlyElement(selection).associatedAlerts;},get relatedEventsToHighlight(){if(!this.currentSelection_)
-return undefined;return tr.b.getOnlyElement(this.currentSelection_).associatedEvents;}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-single-frame-sub-view',tr.model.Frame,{multi:false,title:'Frame',});'use strict';Polymer({is:'tr-ui-a-single-instant-event-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],created:function(){this.currentSelection_=undefined;},set selection(selection){Polymer.dom(this.$.content).textContent='';var realView=document.createElement('tr-ui-a-single-event-sub-view');realView.setSelectionWithoutErrorChecks(selection);Polymer.dom(this.$.content).appendChild(realView);this.currentSelection_=selection;},get selection(){return this.currentSelection_;}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-single-instant-event-sub-view',tr.model.InstantEvent,{multi:false,title:'Instant Event',});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-multi-instant-event-sub-view',tr.model.InstantEvent,{multi:true,title:'Instant Events',});'use strict';tr.exportTo('tr.ui.analysis',function(){var ObjectInstanceView=tr.ui.b.define('object-instance-view');ObjectInstanceView.prototype={__proto__:HTMLDivElement.prototype,decorate:function(){this.objectInstance_=undefined;},get requiresTallView(){return true;},set modelEvent(obj){this.objectInstance=obj;},get modelEvent(){return this.objectInstance;},get objectInstance(){return this.objectInstance_;},set objectInstance(i){this.objectInstance_=i;this.updateContents();},updateContents:function(){throw new Error('Not implemented');}};var options=new tr.b.ExtensionRegistryOptions(tr.b.TYPE_BASED_REGISTRY_MODE);options.mandatoryBaseClass=ObjectInstanceView;options.defaultMetadata={showInTrackView:true};tr.b.decorateExtensionRegistry(ObjectInstanceView,options);return{ObjectInstanceView:ObjectInstanceView};});'use strict';Polymer({is:'tr-ui-a-single-object-instance-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],created:function(){this.currentSelection_=undefined;},get requiresTallView(){if(this.$.content.children.length===0)
-return false;if(this.$.content.children[0]instanceof
-tr.ui.analysis.ObjectInstanceView)
-return this.$.content.children[0].requiresTallView;},get selection(){return this.currentSelection_;},set selection(selection){var instance=tr.b.getOnlyElement(selection);if(!(instance instanceof tr.model.ObjectInstance))
-throw new Error('Only supports object instances');Polymer.dom(this.$.content).textContent='';this.currentSelection_=selection;var typeInfo=tr.ui.analysis.ObjectInstanceView.getTypeInfo(instance.category,instance.typeName);if(typeInfo){var customView=new typeInfo.constructor();Polymer.dom(this.$.content).appendChild(customView);customView.modelEvent=instance;}else{this.appendGenericAnalysis_(instance);}},appendGenericAnalysis_:function(instance){var html='';html+='<div class="title">'+
+Polymer({is:'tr-ui-a-single-flow-event-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],listeners:{'singleEventSubView.customize-rows':'onCustomizeRows_'},set selection(selection){this.currentSelection_=selection;this.$.singleEventSubView.setSelectionWithoutErrorChecks(selection);},get selection(){return this.currentSelection_;},onCustomizeRows_:function(e){var event=tr.b.getOnlyElement(this.currentSelection_);var rows=e.rows;rows.unshift({name:'ID',value:event.id});rows.push({name:'From',value:createAnalysisLinkTo(event.startSlice)});rows.push({name:'To',value:createAnalysisLinkTo(event.endSlice)});}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-single-flow-event-sub-view',tr.model.FlowEvent,{multi:false,title:'Flow Event',});'use strict';Polymer({is:'tr-ui-a-single-frame-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],ready:function(){this.currentSelection_=undefined;},get selection(){return this.currentSelection_;},set selection(selection){this.currentSelection_=selection;this.$.asv.selection=tr.b.getOnlyElement(selection).associatedAlerts;},get relatedEventsToHighlight(){if(!this.currentSelection_)return undefined;return tr.b.getOnlyElement(this.currentSelection_).associatedEvents;}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-single-frame-sub-view',tr.model.Frame,{multi:false,title:'Frame',});'use strict';Polymer({is:'tr-ui-a-single-instant-event-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],created:function(){this.currentSelection_=undefined;},set selection(selection){Polymer.dom(this.$.content).textContent='';var realView=document.createElement('tr-ui-a-single-event-sub-view');realView.setSelectionWithoutErrorChecks(selection);Polymer.dom(this.$.content).appendChild(realView);this.currentSelection_=selection;},get selection(){return this.currentSelection_;}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-single-instant-event-sub-view',tr.model.InstantEvent,{multi:false,title:'Instant Event',});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-multi-instant-event-sub-view',tr.model.InstantEvent,{multi:true,title:'Instant Events',});'use strict';tr.exportTo('tr.ui.analysis',function(){var ObjectInstanceView=tr.ui.b.define('object-instance-view');ObjectInstanceView.prototype={__proto__:HTMLDivElement.prototype,decorate:function(){this.objectInstance_=undefined;},get requiresTallView(){return true;},set modelEvent(obj){this.objectInstance=obj;},get modelEvent(){return this.objectInstance;},get objectInstance(){return this.objectInstance_;},set objectInstance(i){this.objectInstance_=i;this.updateContents();},updateContents:function(){throw new Error('Not implemented');}};var options=new tr.b.ExtensionRegistryOptions(tr.b.TYPE_BASED_REGISTRY_MODE);options.mandatoryBaseClass=ObjectInstanceView;options.defaultMetadata={showInTrackView:true};tr.b.decorateExtensionRegistry(ObjectInstanceView,options);return{ObjectInstanceView,};});'use strict';Polymer({is:'tr-ui-a-single-object-instance-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],created:function(){this.currentSelection_=undefined;},get requiresTallView(){if(this.$.content.children.length===0){return false;}
+if(this.$.content.children[0]instanceof
+tr.ui.analysis.ObjectInstanceView){return this.$.content.children[0].requiresTallView;}},get selection(){return this.currentSelection_;},set selection(selection){var instance=tr.b.getOnlyElement(selection);if(!(instance instanceof tr.model.ObjectInstance)){throw new Error('Only supports object instances');}
+Polymer.dom(this.$.content).textContent='';this.currentSelection_=selection;var typeInfo=tr.ui.analysis.ObjectInstanceView.getTypeInfo(instance.category,instance.typeName);if(typeInfo){var customView=new typeInfo.constructor();Polymer.dom(this.$.content).appendChild(customView);customView.modelEvent=instance;}else{this.appendGenericAnalysis_(instance);}},appendGenericAnalysis_:function(instance){var html='';html+='<div class="title">'+
 instance.typeName+' '+
 instance.id+'</div>\n';html+='<table>';html+='<tr>';html+='<tr><td>creationTs:</td><td>'+
-instance.creationTs+'</td></tr>\n';if(instance.deletionTs!=Number.MAX_VALUE){html+='<tr><td>deletionTs:</td><td>'+
+instance.creationTs+'</td></tr>\n';if(instance.deletionTs!==Number.MAX_VALUE){html+='<tr><td>deletionTs:</td><td>'+
 instance.deletionTs+'</td></tr>\n';}else{html+='<tr><td>deletionTs:</td><td>not deleted</td></tr>\n';}
-html+='<tr><td>snapshots:</td><td id="snapshots"></td></tr>\n';html+='</table>';Polymer.dom(this.$.content).innerHTML=html;var snapshotsEl=Polymer.dom(this.$.content).querySelector('#snapshots');instance.snapshots.forEach(function(snapshot){var snapshotLink=document.createElement('tr-ui-a-analysis-link');snapshotLink.selection=new tr.model.EventSet(snapshot);Polymer.dom(snapshotsEl).appendChild(snapshotLink);});}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-single-object-instance-sub-view',tr.model.ObjectInstance,{multi:false,title:'Object Instance',});'use strict';Polymer({is:'tr-ui-a-single-object-snapshot-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],created:function(){this.currentSelection_=undefined;},get requiresTallView(){if(this.children.length===0)
-return false;if(this.children[0]instanceof tr.ui.analysis.ObjectSnapshotView)
-return this.children[0].requiresTallView;},get selection(){return this.currentSelection_;},set selection(selection){var snapshot=tr.b.getOnlyElement(selection);if(!(snapshot instanceof tr.model.ObjectSnapshot))
-throw new Error('Only supports object instances');Polymer.dom(this).textContent='';this.currentSelection_=selection;var typeInfo=tr.ui.analysis.ObjectSnapshotView.getTypeInfo(snapshot.objectInstance.category,snapshot.objectInstance.typeName);if(typeInfo){var customView=new typeInfo.constructor();Polymer.dom(this).appendChild(customView);customView.modelEvent=snapshot;}else{this.appendGenericAnalysis_(snapshot);}},appendGenericAnalysis_:function(snapshot){var instance=snapshot.objectInstance;Polymer.dom(this).textContent='';var titleEl=document.createElement('div');Polymer.dom(titleEl).classList.add('title');Polymer.dom(titleEl).appendChild(document.createTextNode('Snapshot of '));Polymer.dom(this).appendChild(titleEl);var instanceLinkEl=document.createElement('tr-ui-a-analysis-link');instanceLinkEl.selection=new tr.model.EventSet(instance);Polymer.dom(titleEl).appendChild(instanceLinkEl);Polymer.dom(titleEl).appendChild(document.createTextNode(' @ '));Polymer.dom(titleEl).appendChild(tr.v.ui.createScalarSpan(snapshot.ts,{unit:tr.b.Unit.byName.timeStampInMs,ownerDocument:this.ownerDocument}));var tableEl=document.createElement('table');Polymer.dom(this).appendChild(tableEl);var rowEl=document.createElement('tr');Polymer.dom(tableEl).appendChild(rowEl);var labelEl=document.createElement('td');Polymer.dom(labelEl).textContent='args:';Polymer.dom(rowEl).appendChild(labelEl);var argsEl=document.createElement('td');argsEl.id='args';Polymer.dom(rowEl).appendChild(argsEl);var objectViewEl=document.createElement('tr-ui-a-generic-object-view');objectViewEl.object=snapshot.args;Polymer.dom(argsEl).appendChild(objectViewEl);}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-single-object-snapshot-sub-view',tr.model.ObjectSnapshot,{multi:false,title:'Object Snapshot',});'use strict';Polymer({is:'tr-ui-a-power-sample-table',ready:function(){this.$.table.tableColumns=[{title:'Time',width:'100px',value:function(row){return tr.v.ui.createScalarSpan(row.start,{unit:tr.b.Unit.byName.timeStampInMs});}},{title:'Power',width:'100%',value:function(row){return tr.v.ui.createScalarSpan(row.powerInW,{unit:tr.b.Unit.byName.powerInWatts});}}];this.sample=undefined;},get sample(){return this.sample_;},set sample(sample){this.sample_=sample;this.updateContents_();},updateContents_:function(){if(this.sample===undefined)
-this.$.table.tableRows=[];else
-this.$.table.tableRows=[this.sample];this.$.table.rebuild();}});'use strict';Polymer({is:'tr-ui-a-single-power-sample-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],ready:function(){this.currentSelection_=undefined;},get selection(){return this.currentSelection_;},set selection(selection){this.currentSelection_=selection;this.updateContents_();},updateContents_:function(){if(this.selection.length!=1)
-throw'Cannot pass multiple samples to sample table.';this.$.samplesTable.sample=tr.b.getOnlyElement(this.selection);}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-single-power-sample-sub-view',tr.model.PowerSample,{multi:false,title:'Power Sample',});'use strict';Polymer({is:'tr-ui-a-single-sample-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],created:function(){this.currentSelection_=undefined;},ready:function(){this.$.content.tableColumns=[{title:'FirstColumn',value:function(row){return row.title;},width:'250px'},{title:'SecondColumn',value:function(row){return row.value;},width:'100%'}];this.$.content.showHeader=false;},get selection(){return this.currentSelection_;},set selection(selection){this.currentSelection_=selection;if(this.currentSelection_===undefined){this.$.content.tableRows=[];return;}
-var sample=tr.b.getOnlyElement(this.currentSelection_);var table=this.$.content;var rows=[];rows.push({title:'Title',value:sample.title});rows.push({title:'Sample time',value:tr.v.ui.createScalarSpan(sample.start,{unit:tr.b.Unit.byName.timeStampInMs,ownerDocument:this.ownerDocument})});var sfEl=document.createElement('tr-ui-a-stack-frame');sfEl.stackFrame=sample.leafStackFrame;rows.push({title:'Stack trace',value:sfEl});table.tableRows=rows;table.rebuild();}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-single-sample-sub-view',tr.model.Sample,{multi:false,title:'Sample',});'use strict';Polymer({is:'tr-ui-a-single-thread-slice-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],get selection(){return this.$.content.selection;},set selection(selection){this.$.content.selection=selection;this.$.relatedEvents.setRelatedEvents(selection);if(this.$.relatedEvents.hasRelatedEvents())
-this.$.relatedEvents.style.display='';else
-this.$.relatedEvents.style.display='none';}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-single-thread-slice-sub-view',tr.model.ThreadSlice,{multi:false,title:'Slice',});'use strict';Polymer({is:'tr-ui-a-single-thread-time-slice-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],created:function(){this.currentSelection_=undefined;},get selection(){return this.currentSelection_;},set selection(selection){var timeSlice=tr.b.getOnlyElement(selection);if(!(timeSlice instanceof tr.model.ThreadTimeSlice))
-throw new Error('Only supports thread time slices');this.currentSelection_=selection;var thread=timeSlice.thread;var root=Polymer.dom(this.root);Polymer.dom(root.querySelector('#state')).textContent=timeSlice.title;var stateColor=tr.b.ColorScheme.colorsAsStrings[timeSlice.colorId];root.querySelector('#state').style.backgroundColor=stateColor;Polymer.dom(root.querySelector('#process-name')).textContent=thread.parent.userFriendlyName;Polymer.dom(root.querySelector('#thread-name')).textContent=thread.userFriendlyName;root.querySelector('#start').setValueAndUnit(timeSlice.start,tr.b.Unit.byName.timeStampInMs);root.querySelector('#duration').setValueAndUnit(timeSlice.duration,tr.b.Unit.byName.timeDurationInMs);var onCpuEl=root.querySelector('#on-cpu');Polymer.dom(onCpuEl).textContent='';var runningInsteadEl=root.querySelector('#running-instead');if(timeSlice.cpuOnWhichThreadWasRunning){Polymer.dom(runningInsteadEl.parentElement).removeChild(runningInsteadEl);var cpuLink=document.createElement('tr-ui-a-analysis-link');cpuLink.selection=new tr.model.EventSet(timeSlice.getAssociatedCpuSlice());Polymer.dom(cpuLink).textContent=timeSlice.cpuOnWhichThreadWasRunning.userFriendlyName;Polymer.dom(onCpuEl).appendChild(cpuLink);}else{Polymer.dom(onCpuEl.parentElement).removeChild(onCpuEl);var cpuSliceThatTookCpu=timeSlice.getCpuSliceThatTookCpu();if(cpuSliceThatTookCpu){var cpuLink=document.createElement('tr-ui-a-analysis-link');cpuLink.selection=new tr.model.EventSet(cpuSliceThatTookCpu);if(cpuSliceThatTookCpu.thread)
-Polymer.dom(cpuLink).textContent=cpuSliceThatTookCpu.thread.userFriendlyName;else
-Polymer.dom(cpuLink).textContent=cpuSliceThatTookCpu.title;Polymer.dom(runningInsteadEl).appendChild(cpuLink);}else{Polymer.dom(runningInsteadEl.parentElement).removeChild(runningInsteadEl);}}
-var argsEl=root.querySelector('#args');if(tr.b.dictionaryKeys(timeSlice.args).length>0){var argsView=document.createElement('tr-ui-a-generic-object-view');argsView.object=timeSlice.args;argsEl.parentElement.style.display='';Polymer.dom(argsEl).textContent='';Polymer.dom(argsEl).appendChild(argsView);}else{argsEl.parentElement.style.display='none';}}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-single-thread-time-slice-sub-view',tr.model.ThreadTimeSlice,{multi:false,title:'Thread Timeslice',});'use strict';tr.exportTo('tr.v',function(){class ValueSet{constructor(opt_values){this.values_=new Map();if(opt_values!==undefined)
-for(var value of opt_values)
-this.addHistogram(value);}
-get valueDicts(){return this.map(v=>v.asDict());}
-lookup(guid){return this.values_.get(guid);}
-get length(){return this.values_.size;}
-toArray(){return[...this];}*[Symbol.iterator](){for(var[guid,value]of this.values_)
-yield value;}
-map(callback,opt_this){return this.toArray().map(callback,opt_this||this);}
-addValuesFromDicts(dicts){for(var dict of dicts)
-this.addHistogram(tr.v.Histogram.fromDict(dict));for(var value of this){for(var[name,diagnostic]of value.diagnostics){if((diagnostic instanceof tr.v.d.RelatedValueSet)||(diagnostic instanceof tr.v.d.RelatedValueMap)){diagnostic.resolve(this);}}
-for(var bin of value.allBins){for(var dm of bin.diagnosticMaps){for(var[name,diagnostic]of dm){if((diagnostic instanceof tr.v.d.RelatedValueSet)||(diagnostic instanceof tr.v.d.RelatedValueMap)){diagnostic.resolve(this);}}}}}}
-get sourceValues(){var sourceValues=new Map(this.values_);function deleteSourceValues(diagnosticMap){for(var[name,diagnostic]of diagnosticMap){if(diagnostic instanceof tr.v.d.RelatedValueSet)
-for(var relatedValue of diagnostic)
-sourceValues.delete(relatedValue.guid);else if(diagnostic instanceof tr.v.d.RelatedValueMap)
-for(var[name,relatedValue]of diagnostic)
-sourceValues.delete(relatedValue.guid);}}
-for(var hist of this){deleteSourceValues(hist.diagnostics);for(var b of hist.allBins){for(var dm of b.diagnosticMaps){deleteSourceValues(dm);}}}
-return[...sourceValues.values()];}
-getValuesNamed(name){return this.toArray().filter(h=>h.name===name);}
-addHistogram(h){if(this.values_.get(h.guid))
-throw new Error('Cannot add same Histogram twice');this.values_.set(h.guid,h);}}
-ValueSet.GROUPINGS={HISTOGRAM_NAME:{key:'histogramName',label:'name',dataFn:v=>v.name},BENCHMARK_NAME:{key:'benchmarkName',label:'benchmark',dataFn:v=>tr.v.d.IterationInfo.getField(v,'benchmarkName','')},BENCHMARK_START:{key:'benchmarkStart',label:'time',dataFn:v=>tr.v.d.IterationInfo.getField(v,'benchmarkStartString','')},STORYSET_REPEAT:{key:'storysetRepeat',label:'storyset repeat',dataFn:v=>tr.v.d.IterationInfo.getField(v,'storysetRepeatCounterLabel',0)},STORY_REPEAT:{key:'storyRepeat',label:'story repeat',dataFn:v=>tr.v.d.IterationInfo.getField(v,'storyRepeatCounterLabel',0)},STORY_NAME:{key:'storyName',label:'story',dataFn:v=>tr.v.d.IterationInfo.getField(v,'storyDisplayName','')},DISPLAY_LABEL:{key:'displayLabel',label:'label',dataFn:v=>tr.v.d.IterationInfo.getField(v,'displayLabel','Value')}};return{ValueSet:ValueSet};});'use strict';Polymer({is:'tr-ui-a-single-user-expectation-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],created:function(){this.currentSelection_=undefined;},get selection(){return this.currentSelection_;},set selection(selection){this.$.realView.addEventListener('customize-rows',this.onCustomizeRows_.bind(this));this.currentSelection_=selection;this.$.realView.setSelectionWithoutErrorChecks(selection);this.$.relatedSamples.selection=selection;if(this.$.relatedSamples.hasRelatedSamples())
-this.$.events.style.display='';else
-this.$.events.style.display='none';},get relatedEventsToHighlight(){if(!this.currentSelection_)
-return undefined;return tr.b.getOnlyElement(this.currentSelection_).associatedEvents;},onCustomizeRows_:function(event){var ue=tr.b.getOnlyElement(this.selection);if(ue.rawCpuMs){event.rows.push({name:'Total CPU',value:tr.v.ui.createScalarSpan(ue.totalCpuMs,{unit:tr.b.Unit.byName.timeDurationInMs})});}}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-single-user-expectation-sub-view',tr.model.um.UserExpectation,{multi:false,title:'User Expectation',});'use strict';Polymer({is:'tr-ui-b-tab-view',properties:{label_:{type:String,value:()=>''},selectedSubView_:Object,subViews_:{type:Array,value:()=>[]},tabsHidden:{type:Boolean,value:false}},set label(newLabel){this.set('label_',newLabel);},get tabs(){return this.get('subViews_');},get selectedSubView(){return this.selectedSubView_;},set selectedSubView(subView){if(subView===this.selectedSubView_)
-return;if(this.selectedSubView_)
-Polymer.dom(this.$.subView).removeChild(this.selectedSubView_);this.set('selectedSubView_',subView);if(subView)
-Polymer.dom(this.$.subView).appendChild(subView);this.fire('selected-tab-change');},clearSubViews:function(){this.splice('subViews_',0,this.subViews_.length);this.selectedSubView=undefined;},addSubView:function(subView){if(!this.selectedSubView_)
-this.selectedSubView=subView;this.push('subViews_',subView);},resetSubViews:function(subViews){this.splice('subViews_',0,this.subViews_.length);if(subViews.length){for(var subView of subViews)
-this.push('subViews_',subView);this.selectedSubView=subViews[0];}
-else{this.selectedSubView=undefined;}},onTabChanged_:function(event){this.selectedSubView=event.model.item;},isChecked_:function(subView){return this.selectedSubView_===subView;},computeRadioId_:function(subView){return subView.tagName+'-'+subView.tabLabel.replace(/ /g,'-');}});'use strict';(function(){var EventRegistry=tr.model.EventRegistry;function getTabStripLabel(numEvents){if(numEvents===0)
-return'Nothing selected. Tap stuff.';else if(numEvents===1)
-return'1 item selected.';return numEvents+' items selected.';}
-function createSubView(subViewTypeInfo,selection){var tagName;if(selection.length==1)
-tagName=subViewTypeInfo.singleTagName;else
-tagName=subViewTypeInfo.multiTagName;if(tagName===undefined){throw new Error('No view registered for '+
+html+='<tr><td>snapshots:</td><td id="snapshots"></td></tr>\n';html+='</table>';Polymer.dom(this.$.content).innerHTML=html;var snapshotsEl=Polymer.dom(this.$.content).querySelector('#snapshots');instance.snapshots.forEach(function(snapshot){var snapshotLink=document.createElement('tr-ui-a-analysis-link');snapshotLink.selection=new tr.model.EventSet(snapshot);Polymer.dom(snapshotsEl).appendChild(snapshotLink);});}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-single-object-instance-sub-view',tr.model.ObjectInstance,{multi:false,title:'Object Instance',});'use strict';Polymer({is:'tr-ui-a-single-object-snapshot-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],created:function(){this.currentSelection_=undefined;},get requiresTallView(){if(this.children.length===0){return false;}
+if(this.children[0]instanceof tr.ui.analysis.ObjectSnapshotView){return this.children[0].requiresTallView;}},get selection(){return this.currentSelection_;},set selection(selection){var snapshot=tr.b.getOnlyElement(selection);if(!(snapshot instanceof tr.model.ObjectSnapshot)){throw new Error('Only supports object instances');}
+Polymer.dom(this).textContent='';this.currentSelection_=selection;var typeInfo=tr.ui.analysis.ObjectSnapshotView.getTypeInfo(snapshot.objectInstance.category,snapshot.objectInstance.typeName);if(typeInfo){var customView=new typeInfo.constructor();Polymer.dom(this).appendChild(customView);customView.modelEvent=snapshot;}else{this.appendGenericAnalysis_(snapshot);}},appendGenericAnalysis_:function(snapshot){var instance=snapshot.objectInstance;Polymer.dom(this).textContent='';var titleEl=document.createElement('div');Polymer.dom(titleEl).classList.add('title');Polymer.dom(titleEl).appendChild(document.createTextNode('Snapshot of '));Polymer.dom(this).appendChild(titleEl);var instanceLinkEl=document.createElement('tr-ui-a-analysis-link');instanceLinkEl.selection=new tr.model.EventSet(instance);Polymer.dom(titleEl).appendChild(instanceLinkEl);Polymer.dom(titleEl).appendChild(document.createTextNode(' @ '));Polymer.dom(titleEl).appendChild(tr.v.ui.createScalarSpan(snapshot.ts,{unit:tr.b.Unit.byName.timeStampInMs,ownerDocument:this.ownerDocument,inline:true,}));var tableEl=document.createElement('table');Polymer.dom(this).appendChild(tableEl);var rowEl=document.createElement('tr');Polymer.dom(tableEl).appendChild(rowEl);var labelEl=document.createElement('td');Polymer.dom(labelEl).textContent='args:';Polymer.dom(rowEl).appendChild(labelEl);var argsEl=document.createElement('td');argsEl.id='args';Polymer.dom(rowEl).appendChild(argsEl);var objectViewEl=document.createElement('tr-ui-a-generic-object-view');objectViewEl.object=snapshot.args;Polymer.dom(argsEl).appendChild(objectViewEl);}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-single-object-snapshot-sub-view',tr.model.ObjectSnapshot,{multi:false,title:'Object Snapshot',});'use strict';Polymer({is:'tr-ui-a-power-sample-table',ready:function(){this.$.table.tableColumns=[{title:'Time',width:'100px',value:function(row){return tr.v.ui.createScalarSpan(row.start,{unit:tr.b.Unit.byName.timeStampInMs});}},{title:'Power',width:'100%',value:function(row){return tr.v.ui.createScalarSpan(row.powerInW,{unit:tr.b.Unit.byName.powerInWatts});}}];this.sample=undefined;},get sample(){return this.sample_;},set sample(sample){this.sample_=sample;this.updateContents_();},updateContents_:function(){if(this.sample===undefined){this.$.table.tableRows=[];}else{this.$.table.tableRows=[this.sample];}
+this.$.table.rebuild();}});'use strict';Polymer({is:'tr-ui-a-single-power-sample-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],ready:function(){this.currentSelection_=undefined;},get selection(){return this.currentSelection_;},set selection(selection){this.currentSelection_=selection;this.updateContents_();},updateContents_:function(){if(this.selection.length!==1){throw new Error('Cannot pass multiple samples to sample table.');}
+this.$.samplesTable.sample=tr.b.getOnlyElement(this.selection);}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-single-power-sample-sub-view',tr.model.PowerSample,{multi:false,title:'Power Sample',});'use strict';Polymer({is:'tr-ui-a-single-sample-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],created:function(){this.currentSelection_=undefined;},ready:function(){this.$.content.tableColumns=[{title:'',value:row=>row.title,width:'100px'},{title:'',value:row=>row.value,width:'100%'}];this.$.content.showHeader=false;},get selection(){return this.currentSelection_;},set selection(selection){this.currentSelection_=selection;if(this.currentSelection_===undefined){this.$.content.tableRows=[];return;}
+var sample=tr.b.getOnlyElement(this.currentSelection_);var table=this.$.content;var rows=[];rows.push({title:'Title',value:sample.title});rows.push({title:'Sample time',value:tr.v.ui.createScalarSpan(sample.start,{unit:tr.b.Unit.byName.timeStampInMs,ownerDocument:this.ownerDocument})});var callStackTableEl=document.createElement('tr-ui-b-table');callStackTableEl.tableRows=sample.getNodesAsArray().reverse();callStackTableEl.tableColumns=[{title:'function name',value:row=>row.functionName||'(anonymous function)'},{title:'location',value:row=>row.url}];callStackTableEl.rebuild();rows.push({title:'Call stack',value:callStackTableEl});table.tableRows=rows;table.rebuild();}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-single-sample-sub-view',tr.model.Sample,{multi:false,title:'Sample',});'use strict';Polymer({is:'tr-ui-a-single-thread-slice-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],get selection(){return this.$.content.selection;},set selection(selection){this.$.content.selection=selection;this.$.relatedEvents.setRelatedEvents(selection);if(this.$.relatedEvents.hasRelatedEvents()){this.$.relatedEvents.style.display='';}else{this.$.relatedEvents.style.display='none';}}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-single-thread-slice-sub-view',tr.model.ThreadSlice,{multi:false,title:'Slice',});'use strict';Polymer({is:'tr-ui-a-single-thread-time-slice-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],created:function(){this.currentSelection_=undefined;},get selection(){return this.currentSelection_;},set selection(selection){var timeSlice=tr.b.getOnlyElement(selection);if(!(timeSlice instanceof tr.model.ThreadTimeSlice)){throw new Error('Only supports thread time slices');}
+this.currentSelection_=selection;var thread=timeSlice.thread;var root=Polymer.dom(this.root);Polymer.dom(root.querySelector('#state')).textContent=timeSlice.title;var stateColor=tr.b.ColorScheme.colorsAsStrings[timeSlice.colorId];root.querySelector('#state').style.backgroundColor=stateColor;Polymer.dom(root.querySelector('#process-name')).textContent=thread.parent.userFriendlyName;Polymer.dom(root.querySelector('#thread-name')).textContent=thread.userFriendlyName;root.querySelector('#start').setValueAndUnit(timeSlice.start,tr.b.Unit.byName.timeStampInMs);root.querySelector('#duration').setValueAndUnit(timeSlice.duration,tr.b.Unit.byName.timeDurationInMs);var onCpuEl=root.querySelector('#on-cpu');Polymer.dom(onCpuEl).textContent='';var runningInsteadEl=root.querySelector('#running-instead');if(timeSlice.cpuOnWhichThreadWasRunning){Polymer.dom(runningInsteadEl.parentElement).removeChild(runningInsteadEl);var cpuLink=document.createElement('tr-ui-a-analysis-link');cpuLink.selection=new tr.model.EventSet(timeSlice.getAssociatedCpuSlice());Polymer.dom(cpuLink).textContent=timeSlice.cpuOnWhichThreadWasRunning.userFriendlyName;Polymer.dom(onCpuEl).appendChild(cpuLink);}else{Polymer.dom(onCpuEl.parentElement).removeChild(onCpuEl);var cpuSliceThatTookCpu=timeSlice.getCpuSliceThatTookCpu();if(cpuSliceThatTookCpu){var cpuLink=document.createElement('tr-ui-a-analysis-link');cpuLink.selection=new tr.model.EventSet(cpuSliceThatTookCpu);if(cpuSliceThatTookCpu.thread){Polymer.dom(cpuLink).textContent=cpuSliceThatTookCpu.thread.userFriendlyName;}else{Polymer.dom(cpuLink).textContent=cpuSliceThatTookCpu.title;}
+Polymer.dom(runningInsteadEl).appendChild(cpuLink);}else{Polymer.dom(runningInsteadEl.parentElement).removeChild(runningInsteadEl);}}
+var argsEl=root.querySelector('#args');if(tr.b.dictionaryLength(timeSlice.args)>0){var argsView=document.createElement('tr-ui-a-generic-object-view');argsView.object=timeSlice.args;argsEl.parentElement.style.display='';Polymer.dom(argsEl).textContent='';Polymer.dom(argsEl).appendChild(argsView);}else{argsEl.parentElement.style.display='none';}}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-single-thread-time-slice-sub-view',tr.model.ThreadTimeSlice,{multi:false,title:'Thread Timeslice',});'use strict';Polymer({is:'tr-ui-a-single-user-expectation-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],created:function(){this.currentSelection_=undefined;},get selection(){return this.currentSelection_;},set selection(selection){this.$.realView.addEventListener('customize-rows',this.onCustomizeRows_.bind(this));this.currentSelection_=selection;this.$.realView.setSelectionWithoutErrorChecks(selection);this.$.relatedSamples.selection=selection;if(this.$.relatedSamples.hasRelatedSamples()){this.$.events.style.display='';}else{this.$.events.style.display='none';}},get relatedEventsToHighlight(){if(!this.currentSelection_)return undefined;return tr.b.getOnlyElement(this.currentSelection_).associatedEvents;},onCustomizeRows_:function(event){var ue=tr.b.getOnlyElement(this.selection);if(ue.rawCpuMs){event.rows.push({name:'Total CPU',value:tr.v.ui.createScalarSpan(ue.totalCpuMs,{unit:tr.b.Unit.byName.timeDurationInMs})});}}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-single-user-expectation-sub-view',tr.model.um.UserExpectation,{multi:false,title:'User Expectation',});'use strict';(function(){var EventRegistry=tr.model.EventRegistry;function getTabStripLabel(numEvents){if(numEvents===0){return'Nothing selected. Tap stuff.';}else if(numEvents===1){return'1 item selected.';}
+return numEvents+' items selected.';}
+function createSubView(subViewTypeInfo,selection){var tagName;if(selection.length===1){tagName=subViewTypeInfo.singleTagName;}else{tagName=subViewTypeInfo.multiTagName;}
+if(tagName===undefined){throw new Error('No view registered for '+
 subViewTypeInfo.eventConstructor.name);}
-var subView=document.createElement(tagName);var title;if(selection.length===1)
-title=subViewTypeInfo.singleTitle;else
-title=subViewTypeInfo.multiTitle;title+=' ('+selection.length+')';subView.tabLabel=title;subView.selection=selection;return subView;}
+var subView=document.createElement(tagName);var title;if(selection.length===1){title=subViewTypeInfo.singleTitle;}else{title=subViewTypeInfo.multiTitle;}
+title+=' ('+selection.length+')';subView.tabLabel=title;subView.selection=selection;return subView;}
 Polymer({is:'tr-ui-a-analysis-view',ready:function(){this.brushingStateController_=undefined;this.lastSelection_=undefined;this.tabView_=document.createElement('tr-ui-b-tab-view');this.tabView_.addEventListener('selected-tab-change',this.onSelectedSubViewChanged_.bind(this));Polymer.dom(this).appendChild(this.tabView_);},set tallMode(value){Polymer.dom(this).classList.toggle('tall-mode',value);},get tallMode(){return Polymer.dom(this).classList.contains('tall-mode');},get tabView(){return this.tabView_;},get brushingStateController(){return this.brushingStateController_;},set brushingStateController(brushingStateController){if(this.brushingStateController_){this.brushingStateController_.removeEventListener('change',this.onSelectionChanged_.bind(this));}
 this.brushingStateController_=brushingStateController;if(this.brushingStateController){this.brushingStateController_.addEventListener('change',this.onSelectionChanged_.bind(this));}
-this.onSelectionChanged_();},get selection(){return this.brushingStateController_.selection;},onSelectionChanged_:function(e){if(this.lastSelection_&&this.selection.equals(this.lastSelection_))
-return;this.lastSelection_=this.selection;this.tallMode=false;this.tabView_.label=getTabStripLabel(this.selection.length);var eventsByBaseTypeName=this.selection.getEventsOrganizedByBaseType(true);var ASV=tr.ui.analysis.AnalysisSubView;var eventsByTagName=ASV.getEventsOrganizedByTypeInfo(this.selection);var newSubViews=[];eventsByTagName.forEach(function(events,typeInfo){newSubViews.push(createSubView(typeInfo,events));});this.tabView_.resetSubViews(newSubViews);},onSelectedSubViewChanged_:function(){var selectedSubView=this.tabView_.selectedSubView;if(!selectedSubView){this.tallMode=false;this.maybeChangeRelatedEvents_(undefined);return;}
-this.tallMode=selectedSubView.requiresTallView;this.maybeChangeRelatedEvents_(selectedSubView.relatedEventsToHighlight);},maybeChangeRelatedEvents_:function(events){if(this.brushingStateController)
-this.brushingStateController.changeAnalysisViewRelatedEvents(events);}});})();'use strict';Polymer({is:'tr-ui-b-dropdown',ready:function(){this.$.outer.tabIndex=0;},get iconElement(){return this.$.icon;},onOuterKeyDown_:function(e){if(e.keyCode===' '.charCodeAt(0)){this.toggle_();e.preventDefault();e.stopPropagation();}},onOuterClick_:function(e){var or=this.$.outer.getBoundingClientRect();var inside=true;inside&=e.clientX>=or.left;inside&=e.clientX<or.right;inside&=e.clientY>=or.top;inside&=e.clientY<or.bottom;if(!inside)
-return;e.preventDefault();this.toggle_();},toggle_:function(){if(!this.isOpen)
-this.show();else
-this.close();},show:function(){if(this.isOpen)
-return;Polymer.dom(this.$.outer).classList.add('open');var ddr=this.$.outer.getBoundingClientRect();var rW=Math.max(ddr.width,150);this.$.dialog.style.minWidth=rW+'px';this.$.dialog.showModal();var ddw=this.$.outer.getBoundingClientRect().width;var w=this.$.dialog.getBoundingClientRect().width;this.$.dialog.style.top=ddr.bottom-1+'px';this.$.dialog.style.left=ddr.left+'px';},onDialogClick_:function(e){if(!this.isOpen)
-return;if(e.srcElement!==this.$.dialog)
-return;e.preventDefault();this.close();},onDialogCancel_:function(e){e.preventDefault();this.close();},close:function(){if(!this.isOpen)
-return;this.$.dialog.close();Polymer.dom(this.$.outer).classList.remove('open');this.$.outer.focus();},get isOpen(){return this.$.dialog.hasAttribute('open');}});'use strict';tr.exportTo('tr.ui.b',function(){var FaviconsByHue={blue:'data:image/vndmicrosofticon;base64,AAABAAIAEBAAAAEAIABoBAAAJgAAACAgAAABACAAqBAAAI4EAAAoAAAAEAAAACAAAAABACAAAAAAAAAEAAASCwAAEgsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjj8xAGArIgqOPzE8nUY3dqJJOJeiSTiXnUY3do4/MTxhKyIKjkAxAAAAAAAAAAAAAAAAAAAAAABQJBwAAAAAAZJBMzSoSzqlsU8+6bRQP/21UT//tVE//7RQP/2wTz3ppko6pY9AMjQAAAABTyMbAAAAAAB7e3sAAP//AKFSRE+wTz3dtVE//7VRP/+1UT//tVE//7VRP/+zUD7/sE89/7BOPf+qTDvdl0M0TwAAAABWJx4A+fn5ANjd3TnIiX7ftVA9/7VRP/+1UT//tVE//7VRP/+xTz3/rE08/6xMO/+sTDv/rE08/6dKOt+SQTM5q0w7ALO0tA3v8fGu05uR/7NMOf+0Tzz/tE88/7RPPv+uTT3/p0o7/6ZJOv+mSTr/pkk6/6ZJOv+mSjr/n0Y4rnIwKg3h4eFK9/j48N2zrP/FeGr/xnps/8Z6bP/AaUv/tlw1/7RbNf+1WzX/tFs1/7RbNf+0WzX/tFs1/7NbNPCqWy1K7e3tjPn5+f/49vX/9vLy//by8v/28vH/8bZv/+6RH//ukyP/7pMj/+6SI//ukiP/7pMj/+2SIv/qjyL/34kfjPHx8bL5+fn/+fn5//n5+f/5+fr/+fn5//W7cP/zlB3/85Yh//OWIf/zliH/85Yh//GVIf/rkR//6ZAf/+KLHrLz8/O2+fn5//n5+f/5+fn/+fn5//n5+f/1unD/85Qd//OWIf/zliH/85Yh//CUIP/mjh//44we/+OMHv/diR628vLymfn5+f/5+fn/+fn5//n5+f/5+fn/9bx0//OXI//zmCb/85gm/++VIv/hjB//3Yoe/92KHv/dih7/2IYdmfHx8Vz4+Pj3+fn5//n5+f/5+fn/+fn5//jo0//33bv/9929//bbtf/euDX/06oJ/9OrC//Tqwv/06oM98yfD1zr6+sY9/f3xvn5+f/5+fn/+fn5//n5+f/5+vv/+fv8//n7/f/3+PH/3Ms6/9O8AP/UvQD/1L0A/9K8AMbItAAY////APT09Fb4+Pjy+fn5//n5+f/5+fn/+fn5//n5+f/5+fr/9/bu/9zKOf/TuwD/1LwA/9S8APLQuABW3cQAAOzs7ADm5uYF9vb2ePn5+fT5+fn/+fn5//n5+f/5+fn/+fn6//f27v/cyTn/07sA/9S8APTRugB4w60ABcmyAAAAAAAA8PDwAOzs7Ab29vZd+Pj40vn5+fz5+fn/+fn5//n5+f/49/H/5Ndu/NjEIdLSugBdybIABsy1AAAAAAAAAAAAAAAAAADn5+cAqKioAPT09CH39/dy+Pj4tvj4+NX4+PjV+Pj4tvX063Lt6MMhOQAAAM+/RAAAAAAAAAAAAPAPAADAAwAAwAMAAIABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIABAACAAQAAwAMAAPAPAAAoAAAAIAAAAEAAAAABACAAAAAAAAAQAAASCwAAEgsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCwUEDDgZExxWJx4tYiwiN2IsIjdWJx4tOBkTHAsFBAwAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wAbDAkKZS0jMYs+MWydRjeipko6x6tMO9utTTzjrU0846tMO9umSjrHnUY3oos+MWxlLSMxGwwJCv///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgZFAAPBwUHcjMoPJtFNpqsTTzhs1A+/LVRP/+2UT//tVE//7VRP/+1UT//tVE//7ZRP/+1UT//s1A+/KxNPOGbRTaacTInPA8HBQc4GRMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/yp4AUCQcGZVDNICtTjzktVE//7VRP/+1UT//tVE//7VRP/+1UT//tVE//7VRP/+1UT//tVE//7VRP/+0UT//s1A+/7JQPv+rTDvkkkEzgE8jGxn/xZoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAA////AGswJSqiSTivs1A++7VRP/+1UT//tVE//7VRP/+1UT//tVE//7VRP/+1UT//tVE//7VRP/+1UT//tFA+/7FPPf+xTz3/sU89/7FPPf+vTj37nkc3r2guJCr///8AAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwMDAP/DogB/VEwsqE09v7VRP/+1UT//tVE//7VRP/+1UT//tVE//7VRP/+1UT//tVE//7VRP/+1UT//tVE//7NQPv+vTj3/r049/69OPf+vTj3/r049/69OPf+uTjz/oUg4v20xJiz/nnsAAgEBAAAAAAAAAAAAAAAAAAAAAAD19fUAkp2fHdK2sbW5W0r/tVA+/7VRP/+1UT//tVE//7VRP/+1UT//tVE//7VRP/+1UT//tVE//7VRP/+yUD7/rU08/6xNPP+tTTz/rU08/61NPP+tTTz/rU08/61NPP+sTTz/nkY3tWAqIR2pSzsAAAAAAAAAAAAAAAAAeXl5ADY2Ngnd39+O6tbT/blbSv+1UD7/tVE//7VRP/+1UT//tVE//7VRP/+1UT//tVE//7VRP/+1UT//slA+/6xNPP+rTDv/q0w7/6tMO/+rTDv/q0w7/6tMO/+rTDv/q0w7/6tMO/+qTDv9lkM0jiUQDQlSJR0AAAAAAAAAAAD///8AxMTES/X29u3s2NX/uVtK/7VQPv+1UT//tVE//7VRP/+1UT//tVE//7VRP/+1UT//tVE//7FPPv+qTDv/qEs6/6hLOv+oSzr/qEs6/6hLOv+oSzr/qEs6/6hLOv+oSzr/qEs6/6lLOv+lSTnthDsuS/+TcgAAAAAAm5ubAHBwcA/o6Oix+vv8/+zY1P+5W0r/tVA+/7VRP/+1UT//tVE//7VRP/+1UT//tVE//7VRP/+xTz3/qEs6/6ZKOv+mSjr/pko6/6ZKOv+mSjr/pko6/6ZKOv+mSjr/pko6/6ZKOv+mSjr/pko6/6ZKOv+bRTaxSiEaD2cuJAD///8AycnJRfX19fD6+/z/69fU/7hYR/+0Tjv/tE48/7ROPP+0Tjz/tE48/7ROPP+0Tz3/r04+/6VJOv+jSDn/o0g5/6NIOf+jSDn/o0g5/6NIOf+jSDn/o0g5/6NIOf+jSDr/o0g5/6NIOf+jSDn/o0g6/6BHOfCCOS9F0FxKAAAAAALk5OSN+fn5//n6+v/y5+X/05uS/9CTiP/QlIn/0JSJ/9CUif/QlIn/0JSK/8yGb//AaDb/vWc0/71nNf+9ZzT/vWc0/71nNP+9ZjT/vWY0/71mNP+9ZjT/vGY0/7xmNP+8ZjT/vGY0/7xmNP+8ZjT/u2U0/7FiLY0AAAACk5OTFu/v78X5+fn/+fn5//n5+f/5+vr/+fn5//n5+f/5+fn/+fn5//n5+f/5+/3/99iy//KWI//ylSH/8ZUh//GVIf/xlSH/8ZUh//GVIf/xlSH/8ZUh//GVIf/xlSH/8ZUh//GVIf/xlSH/8ZUh//CUIf/vkyD/5Y0fxY1XExbDw8Mz9PT05fn5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n7/f/32LL/85cj//OWIf/zliH/85Yh//OWIf/zliH/85Yh//OWIf/zliH/85Yh//OWIf/zliH/85Yh//OWIf/wlCD/7pIg/+6SIP/pjx/lunIZM9XV1VD39/f0+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fv9//fYsv/zlyP/85Yh//OWIf/zliH/85Yh//OWIf/zliH/85Yh//OWIf/zliH/85Yh//OWIf/zliH/75Mg/+uRH//qkB//6pAf/+iPH/TIfBtQ3d3dYfj4+Pn5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+/3/99iy//OXI//zliH/85Yh//OWIf/zliH/85Yh//OWIf/zliH/85Yh//OWIf/zliH/85Yh/+6TIP/ojx//548f/+ePH//njx//5o4f+c1/HGHh4eFl+Pj4+vn5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n7/f/32LL/85cj//OWIf/zliH/85Yh//OWIf/zliH/85Yh//OWIf/zliH/85Yh//OWIf/tkiD/5Y0f/+SNH//ljR//5Y0f/+WNH//kjB/6zn8cZeDg4Fr4+Pj3+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fv9//fYsv/zlyP/85Yh//OWIf/zliH/85Yh//OWIf/zliH/85Yh//OWIf/zliH/65Eg/+KMHv/iix7/4ose/+KLHv/iix7/4ose/+CLHvfLfRta3NzcQvf39+/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+/3/99iy//OXI//zliH/85Yh//OWIf/zliH/85Yh//OWIf/zliH/85Yh/+qRIP/gih7/34oe/9+KHv/fih7/34oe/9+KHv/fih7/3Yge78V6GkLS0tIj9fX12fn5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n7/f/32LH/85Yg//OVHv/zlR7/85Ue//OVHv/zlR7/85Ue//OVIf/pjyH/3ogf/92HH//dhx//3Ycf/92HH//dhx//3Ycf/92HH//ahh7ZunMZI56engjy8vKu+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fr7//jr2f/2ypL/9smP//bJkP/2yZD/9smQ//bJkP/2yZD/5rNI/9OeFP/SnhX/0p4V/9KeFf/SnhX/0Z0V/9GdFf/RnRX/0Z0V/8yWFq6KVBcI////AO3t7Wr5+fn++fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn6//n6/P/5+vz/+fr8//n6/P/5+vz/+fr8//n6/P/h013/0rsA/9O8AP/TvAD/07wA/9O8AP/TvAD/07wA/9O8AP/SvAD+yLMAav/mAADr6+sA4eHhJPb29tv5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5/+LSW//TuwD/1LwA/9S8AP/UvAD/1LwA/9S8AP/UvAD/1LwA/9K6ANu/qgAkyLEAALu7uwAAAAAA8vLygfn5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/4tJb/9O7AP/UvAD/1LwA/9S8AP/UvAD/1LwA/9S8AP/UvAD/zrYAgQAAAACfjQAAAAAAAOzs7ADk5OQe9vb2zPn5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/i0lv/07sA/9S8AP/UvAD/1LwA/9S8AP/UvAD/1LwA/9K6AMzCrAAeybIAAAAAAAAAAAAAsLCwAP///wDv7+9O+Pj47Pn5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5/+LSW//TuwD/1LwA/9S8AP/UvAD/1LwA/9S8AP/TuwDsy7QATu7UAACXhQAAAAAAAAAAAAAAAAAA1tbWALS0tAPy8vJv+Pj49Pn5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/4tJb/9O7AP/UvAD/1LwA/9S8AP/UvAD/07wA9M63AG6ZiQADtqIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4uLiANfX1wbz8/Nz+Pj48Pn5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/i0lv/07sA/9S8AP/UvAD/1LwA/9O8APDPuABzuKMABsGrAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4+PjANjY2ATy8vJZ+Pj42vn5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5/+HSW//TugD/1LsA/9S8AP/TuwDazrcAWbejAATBqwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1NTUAB8fHwDw8PAr9vb2nPj4+O35+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/7uas/+bZdv/j1mvt2cYznMu0ACsUFAAAtaEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOvr6wDj4+MG8vLyOvb29pD4+PjS+fn58vn5+f35+fn/+fn5//n5+f/5+fn/+fn5/fn5+fL4+frS9/j8kPT1/Trs8v8G8PP/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADh4eEA1tbWAu/v7xv09PRJ9vb2dvb29pf39/eo9/f3qPb29pf29vZ29PT0Se/v7xvW1tYC4eHhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/gB///gAH//gAAf/wAAD/4AAAf8AAAD+AAAAfAAAADwAAAA4AAAAGAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAGAAAABwAAAA8AAAAPgAAAH4AAAB/AAAA/4AAAf/gAAf/8AAP//wAP/',green:'data:image/vndmicrosofticon;base64,AAABAAIAEBAAAAEAIABoBAAAJgAAACAgAAABACAAqBAAAI4EAAAoAAAAEAAAACAAAAABACAAAAAAAAAEAAASCwAAEgsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWJLAEpCMwptYks8eWxTdn1wVpd9cFaXeWxTdm1iSzxKQzMKbWJLAAAAAAAAAAAAAAAAAAAAAAA+OCsAAAAAAXBlTTSBdFmliHpe6Yp8X/2LfWD/i31g/4p8X/2HeV3pf3NYpW5jTDQAAAABPTcqAAAAAAB7e3sAlv//AIB1Xk+HeV3di31g/4t9YP+LfWD/i31g/4t9YP+Je1//h3pd/4d5Xf+DdVrddGhQTwAAAABDPC4A+fn5ANrb3DmupZPfinxf/4t9YP+LfWD/i31g/4t9YP+Iel7/hHdb/4R2W/+Edlv/hHdb/4BzWN9wZU05g3ZaALS0tA3w8PGuu7Sj/4h5W/+Je17/iXte/4t8X/+HeFz/gnNY/4FyWP+Bclj/gXJY/4FyWP+Bclj/fG1Url9NPA3h4eFK9/j48MvFuf+kmoP/ppuF/6abhf+JkHL/c4Rj/3OEY/9zhGP/coNj/3KDY/9yg2P/coNj/3CDYvBgf19K7e3tjPn5+f/39vb/9fTz//X08//09PP/itKw/0m+h/9Mv4n/TL+J/0y/if9Mv4n/TL+J/0y+iP9Lu4b/RrJ/jPHx8bL5+fn/+fn5//n5+f/5+fn/+fn5/4rXtP9Hwon/SsOL/0rDi/9Kw4v/SsOL/0nCiv9HvYb/RruF/0S1gbLz8/O2+fn5//n5+f/5+fn/+fn5//n5+f+K17P/R8KJ/0rDi/9Kw4v/SsOL/0nBif9GuYT/RbaC/0W2gv9Dsn+28vLymfn5+f/5+fn/+fn5//n5+f/5+fn/jdi1/0vDjP9OxI7/TsSO/0rAiv9FtoP/RLKA/0SygP9EsoD/Qq59mfHx8Vz4+Pj3+fn5//n5+f/5+fn/+fn5/9rw5v/H6tn/yOra/8Lp2f9e1b7/O8yz/z3MtP89zLT/Pcuy9zzApVzr6+sY9/f3xvn5+f/5+fn/+fn5//n5+f/7+vr//Pr7//z6+//z+fn/ZuPY/zbczv853c7/Od3O/zjbzcY10sYY////APT09Fb4+Pjy+fn5//n5+f/5+fn/+fn5//n5+f/6+fn/8Pj3/2Xj1/823Mz/OdzN/znczfI42MlWO+XWAOzs7ADm5uYF9vb2ePn5+fT5+fn/+fn5//n5+f/5+fn/+vn5//D49/9j4tf/NdvM/znczfQ42ct4Ncu9BTbRwgAAAAAA8PDwAOzs7Ab29vZd+Pj40vn5+fz5+fn/+fn5//n5+f/z+Pj/jung/FLf0tI42ctdNdHCBjfUxgAAAAAAAAAAAAAAAADn5+cAqKioAPT09CH39/dy+Pj4tvj4+NX4+PjV+Pj4tu329XLO7+whAFQmAGrUygAAAAAAAAAAAPAPAADAAwAAwAMAAIABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIABAACAAQAAwAMAAPAPAAAoAAAAIAAAAEAAAAABACAAAAAAAAAQAAASCwAAEgsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCQgGDCsmHRxCOy4tS0M0N0tDNDdCOy4tKyYdHAkIBgwAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wAVEg4KTUU1MWtgSmx5bVOigHNYx4N2W9uFd1zjhXdc44N2W9uAc1jHeW1TomtgSmxNRjUxFRMOCv///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACsnHgALCggHWE88PHdrUpqEd1vhiXxf/It9YP+LfWD/i31g/4t9YP+LfWD/i31g/4t9YP+LfWD/iXxf/IR3W+F3a1KaV048PAsKCAcrJx4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///AAPjcqGXJnT4CFeFzki31g/4t9YP+LfWD/i31g/4t9YP+LfWD/i31g/4t9YP+LfWD/i31g/4t9YP+KfWD/iXxf/4l7Xv+DdlrkcGVNgDw2Khn//+sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AFJKOSp9cFavinxf+4t9YP+LfWD/i31g/4t9YP+LfWD/i31g/4t9YP+LfWD/i31g/4t9YP+LfWD/inxf/4h6Xv+Iel3/iHpd/4h6Xv+GeV37eW1Ur1BINyr///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwMDAP//3gBsZ1osgnVbv4t9YP+LfWD/i31g/4t9YP+LfWD/i31g/4t9YP+LfWD/i31g/4t9YP+LfWD/i31g/4l8X/+HeV3/hnlc/4Z5XP+GeVz/hnlc/4Z5XP+GeFz/fG9Vv1RLOiz/9LoAAgIBAAAAAAAAAAAAAAAAAAAAAAD19fUAl5ibHcbCurWShGn/i31g/4t9YP+LfWD/i31g/4t9YP+LfWD/i31g/4t9YP+LfWD/i31g/4t9YP+Je1//hXhc/4R3W/+Fd1v/hXdb/4V3W/+Fd1v/hXdb/4V3W/+Ed1v/eW1TtUlCMh2CdVkAAAAAAAAAAAAAAAAAeXl5ADY2Ngne3t+O4t/Z/ZKFaf+LfV//i31g/4t9YP+LfWD/i31g/4t9YP+LfWD/i31g/4t9YP+LfWD/iXte/4R3W/+Ddlr/g3Za/4N2Wv+Ddlr/g3Za/4N2Wv+Ddlr/g3Za/4N2Wv+CdVr9c2dPjhwZEwk/OSsAAAAAAAAAAAD///8AxMTES/X19u3k4dv/koRp/4t9X/+LfWD/i31g/4t9YP+LfWD/i31g/4t9YP+LfWD/i31g/4h6Xv+CdVr/gXRZ/4F0Wf+BdFn/gXRZ/4F0Wf+BdFn/gXRZ/4F0Wf+BdFn/gXRZ/4F0Wf9+clftZVtGS/3jrgAAAAAAm5ubAHBwcA/o6Oix+/v7/+Pg2/+ShGn/i31f/4t9YP+LfWD/i31g/4t9YP+LfWD/i31g/4t9YP+Iel7/gXRZ/4BzWP+Ac1j/gHNY/4BzWP+Ac1j/gHNY/4BzWP+Ac1j/gHNY/4BzWP+Ac1j/gHNY/4BzWP93a1KxOTMnD1BHNwD///8AycnJRfX19fD7+/v/4+Da/5CCZ/+Jel3/iXtd/4l7Xf+Je13/iXtd/4l7Xf+Ke17/iHhd/4BxV/9/cFb/f3BW/39wVv9/cFb/f3BW/39wVv9/cFb/f3BW/39wVv9/cFb/f3BW/39wVv9/cFb/f3BW/31uVPBnWURFo45tAAAAAALk5OSN+fn5//r6+v/t7Oj/vLSk/7aunP+3rp3/t66d/7eunf+3rp3/uK+e/6Gmjv9vkG3/bI5r/2yOa/9sjmv/bI5r/2yOa/9sjmv/bI5r/2yOa/9sjmr/bI1q/2yNav9sjWr/bI1q/2uNav9rjWr/a41q/16GZI0AAAACk5OTFu/v78X5+fn/+fn5//n5+f/5+fr/+fn5//n5+f/5+fn/+fn5//n5+f/8+vv/wOfV/0vCi/9Kwor/SsKK/0rCiv9Kwor/SsKK/0rCiv9Kwor/SsKK/0rCiv9Kwor/SsKK/0rCiv9Kwor/SsKK/0nAif9Jv4j/RreCxStxUBbDw8Mz9PT05fn5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//z6+/+/59X/TMSM/0rDi/9Kw4v/SsOL/0rDi/9Kw4v/SsOL/0rDi/9Kw4v/SsOL/0rDi/9Kw4v/SsOL/0rDi/9JwYn/SL6I/0i+iP9GuoXlOJVqM9XV1VD39/f0+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn//Pr7/7/n1f9Mw4z/SsOL/0rDi/9Kw4v/SsOL/0rDi/9Kw4v/SsOL/0rDi/9Kw4v/SsOL/0rDi/9Kw4v/ScCJ/0e8hv9HvIb/R7yG/0a6hfQ9oXJQ3d3dYfj4+Pn5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/8+vv/v+fV/0zDjP9Kw4v/SsOL/0rDi/9Kw4v/SsOL/0rDi/9Kw4v/SsOL/0rDi/9Kw4v/SsOL/0i/iP9GuoX/RrqE/0a6hP9GuoT/RrmD+T6ldWHh4eFl+Pj4+vn5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//z6+/+/59X/TMOM/0rDi/9Kw4v/SsOL/0rDi/9Kw4v/SsOL/0rDi/9Kw4v/SsOL/0rDi/9Ivof/RbiD/0W3gv9FuIP/RbiD/0W4g/9Ft4L6PqZ2ZeDg4Fr4+Pj3+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn//Pr7/7/n1f9Mw4z/SsOL/0rDi/9Kw4v/SsOL/0rDi/9Kw4v/SsOL/0rDi/9Kw4v/SL2H/0W2gv9FtYH/RbWB/0W1gf9FtYH/RbWB/0S0gPc+o3Ra3NzcQvf39+/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/8+vv/v+fV/0zDjP9Kw4v/SsOL/0rDi/9Kw4v/SsOL/0rDi/9Kw4v/SsOL/0e8hv9EtID/RLOA/0SzgP9Es4D/RLOA/0SzgP9Es4D/Q7F/7zyecULS0tIj9fX12fn5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//z6+/+/59X/SsOL/0jCiv9Iwor/SMKK/0jCiv9Iwor/SMKK/0rCiv9HuoT/RLF+/0Owff9EsH3/RLB9/0Swff9EsH3/RLB9/0Swff9CrnzZOJZrI56engjy8vKu+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+vn6/9/x6f+l38X/o9/D/6Tfw/+k38P/pN/D/6Tfw/+k38T/a9Kz/0DBof9BwKH/QcCh/0HAof9BwKD/QcCg/0G/oP9Bv6D/Qb+g/0C4mK4tbU4I////AO3t7Wr5+fn++fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+vn6//v6+//7+vv/+/r7//v6+//7+vv//Pr7//v6+/+B597/NdvN/znczf853M3/OdzN/znczf853M3/OdzN/znczf85283+NtHDakb/+gDr6+sA4eHhJPb29tv5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5/3/n3f823Mz/OdzN/znczf853M3/OdzN/znczf853M3/OdzN/zjay9s0x7kkNs/BALu7uwAAAAAA8vLygfn5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/f+fd/zbbzP853M3/OdzN/znczf853M3/OdzN/znczf853M3/N9XHgQAAAAAspZoAAAAAAOzs7ADk5OQe9vb2zPn5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f9/593/NtvM/znczf853M3/OdzN/znczf853M3/OdzN/zjay8w0yrweNtDCAAAAAAAAAAAAsLCwAP///wDv7+9O+Pj47Pn5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5/3/n3f8228z/OdzN/znczf853M3/OdzN/znczf8528zsN9PETkD45gAonJEAAAAAAAAAAAAAAAAA1tbWALS0tAPy8vJv+Pj49Pn5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/f+fd/zbbzP853M3/OdzN/znczf853M3/OdvM9DjWx24qoJUDMb2wAAAAAAAAAAAAAAAAAAAAAAAAAAAA4uLiANfX1wbz8/Nz+Pj48Pn5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f9/593/NtvM/znczf853M3/OdzN/znbzPA418hzMr6xBjTIugAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4+PjANjY2ATy8vJZ+Pj42vn5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5/37m3f8z28z/N9zN/znczf8528zaONbIWTK/sgQ0yLsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1NTUAB8fHwDw8PAr9vb2nPj4+O35+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/vfDr/5Tq4v+L6ODtYODUnDTTxSsAGBsAMrywAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOvr6wDj4+MG8vLyOvb29pD4+PjS+fn58vn5+f35+fn/+fn5//n5+f/5+fn/+fn5/fn5+fL6+PjS+vf3kPv09Tr/6u4G/+/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADh4eEA1tbWAu/v7xv09PRJ9vb2dvb29pf39/eo9/f3qPb29pf29vZ29PT0Se/v7xvW1tYC4eHhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/gB///gAH//gAAf/wAAD/4AAAf8AAAD+AAAAfAAAADwAAAA4AAAAGAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAGAAAABwAAAA8AAAAPgAAAH4AAAB/AAAA/4AAAf/gAAf/8AAP//wAP/',red:'data:image/vndmicrosofticon;base64,AAABAAIAEBAAAAEAIABoBAAAJgAAACAgAAABACAAqBAAAI4EAAAoAAAAEAAAACAAAAABACAAAAAAAAAEAAASCwAAEgsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQxmbAC0RagpDGZs8ShysdkwdspdMHbKXShysdkMZmzwuEWoKQxmcAAAAAAAAAAAAAAAAAAAAAAAmDlgAAAAAAUQanzRPHrilUx/B6VQgxf1VIMb/VSDG/1Qgxf1TH8DpTh22pUMZnDQAAAABJQ5XAAAAAAB7ensA//8AAFUrr09SH8DdVSDG/1Ugxv9VIMb/VSDG/1Ugxv9UH8P/Ux/B/1IfwP9QHrrdRxqlTwAAAAAoD14A+fn5ANzf1zmMatPfVB7G/1Ugxv9VIMb/VSDG/1Ugxv9TH8L/UR68/1AevP9QHrz/UR68/04dt99EGaA5UB67ALS0sw3x8u+unYDd/1AZxP9THcX/Ux3F/1Qexf9THr//Tx23/08ctv9PHbb/Tx22/08dtv9PHbb/SxuurjkSfg3h4eFK+Pj38LWf5P97UtL/fVXS/31V0/9fOcz/SSfC/0knwP9JJ8D/SSfA/0knwP9JJ8D/SSfA/0gnv/A/KLNK7e3tjPn5+f/29fj/8vD3//Px9//y8Pf/fILz/zQ/8P83QvD/N0Lw/zdC8P83QvD/N0Lw/zdB7/82QOz/Mz3gjPHx8bL5+fn/+fn5//n6+f/5+vn/+fn5/36G9v8yQPT/NkP0/zZD9P82Q/T/NkP0/zVC8v80QOz/M0Dq/zI+47Lz8/O2+fn5//n5+f/5+fn/+fn5//n5+f99hvb/MkD0/zZD9P82Q/T/NkP0/zVC8f8zP+f/Mj7k/zI+5P8xPd628vLymfn5+f/5+fn/+fn5//n5+f/5+fn/gYn2/zdE9P87R/T/O0f0/zZF8P8yQOP/MT/e/zE/3v8xP97/Lz3ZmfHx8Vz4+Pj3+fn5//n5+f/5+fn/+fn5/9fZ+P/Bxfj/wsb4/7vD+P87j/X/Dnzx/xF98f8RffH/EXzw9xZv5Vzr6+sY9/f3xvn5+f/5+fn/+fn5//n5+f/7+/n//Pz5//38+f/x+Pn/OrD+/wCY//8Amf//AJn//wCZ/cYAlPMY////APT09Fb4+Pjy+fn5//n5+f/5+fn/+fn5//n5+f/6+fn/7vX5/zmu/v8Al///AJj//wCY/vIAlfpWAJ//AOzs7ADm5uYF9vb2ePn5+fT5+fn/+fn5//n5+f/5+fn/+vn5/+71+f85rf7/AJb//wCY//QAlvx4AIzrBQCQ8gAAAAAA8PDwAOzs7Ab29vZd+Pj40vn5+fz5+fn/+fn5//n5+f/x9vn/bsP8/CGk/tIAlvxdAJDyBgCT9QAAAAAAAAAAAAAAAADn5+cAqKioAPT09CH39/dy+Pj4tvj4+NX4+PjV+Pj4tuvy93LD4fUhAAC7AESo6wAAAAAAAAAAAPAPAADAAwAAwAMAAIABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIABAACAAQAAwAMAAPAPAAAoAAAAIAAAAEAAAAABACAAAAAAAAAQAAASCwAAEgsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBgIMDBoKPRwoD14tLhFrNy4RazcoD14tGgo9HAYCDAwAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP+3/wANBR0KLxJuMUEYmGxKHKyiTh22x1Aeu9tRHr3jUR6941Aeu9tOHbbHShysokEYmGwvEm4xDQUeCv+6/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABoKPgAHAxAHNhR9PEkbqppRHr3hVCDE/FUgxv9VIMf/VSDH/1Ugxv9VIMb/VSDH/1Ugx/9VIMb/VCDE/FEevOFIG6maNRR8PAcDEAcaCj0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADVUP8AJg5YGUYao4BRH77kVSDG/1Ugxv9VIMb/VSDG/1Ugxv9VIMb/VSDG/1Ugxv9VIMb/VSDG/1Ugxv9VIMX/VB/E/1Qfw/9QHrvkRRmggCUOVhnQTv8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEA////ADITdSpMHbKvVCDE+1Ugxv9VIMb/VSDG/1Ugxv9VIMb/VSDG/1Ugxv9VIMb/VSDG/1Ugxv9VIMb/VCDE/1Mfwv9TH8H/Ux/B/1Mfwv9SH7/7ShytrzEScSr///8AAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwMDAMto/wBVPoYsUSC3v1Ugxv9VIMb/VSDG/1Ugxv9VIMb/VSDG/1Ugxv9VIMb/VSDG/1Ugxv9VIMb/VSDG/1QfxP9SHsD/Uh6//1Iev/9SHr//Uh6//1Iev/9SHr//SxywvzMTdyymPf8AAQACAAAAAAAAAAAAAAAAAAAAAAD19fUAnaKQHbep1rVfLcn/VB/G/1Ugxv9VIMb/VSDG/1Ugxv9VIMb/VSDG/1Ugxv9VIMb/VSDG/1Ugxv9UH8P/UR6+/1Eevf9RHr3/UR69/1Eevf9RHr3/UR69/1Eevf9RHr3/ShuttS0RaB1PHrkAAAAAAAAAAAAAAAAAeXl5ADY2Ngnf4NyO18zu/V8tyf9UH8b/VSDG/1Ugxv9VIMb/VSDG/1Ugxv9VIMb/VSDG/1Ugxv9VIMb/VB/D/1EevP9QHrr/UB67/1Aeu/9QHrv/UB67/1Aeu/9QHrv/UB67/1Aeu/9QHrr9RhqkjhEGKAknDloAAAAAAAAAAAD///8AxMTES/b39O3Zzu//Xy3J/1Qfxv9VIMb/VSDG/1Ugxv9VIMb/VSDG/1Ugxv9VIMb/VSDG/1Mfwv9QHbr/Tx24/08duP9PHbj/Tx24/08duP9PHbj/Tx24/08duP9PHbj/Tx24/08duf9NHLTtPheRS5s5/wAAAAAAm5ubAHBwcA/o6Oix+/z6/9jO7/9fLcn/VB/G/1Ugxv9VIMb/VSDG/1Ugxv9VIMb/VSDG/1Ugxv9TH8H/Tx24/04dtv9OHbb/Th22/04dtv9OHbb/Th22/04dtv9OHbb/Th22/04dtv9OHbb/Th22/04dtv9JG6mxIw1RDzAScQD///8AycnJRfX19fD7/Pr/2M3v/1wqyP9SHMX/UhzF/1Icxf9SHMX/UhzF/1Icxf9THcX/Ux7A/04ctf9NHLL/Thyz/04cs/9NHLP/TRyz/00cs/9OHLP/Thyz/04cs/9OHLP/Thyz/04cs/9NHLP/Thyz/0wcsPA/Fo9FYyTkAAAAAALk5OSN+fn5//r6+f/n4vT/noDd/5Z22v+Wdtr/lnba/5Z22v+Wdtr/mHfb/35g1/9KMMr/SC/H/0gvx/9IL8f/SC/H/0gvx/9IL8b/SC/G/0gvxv9HL8b/Ry/G/0cvxv9HL8b/Ry/G/0cvxv9HL8X/Ry7F/z8tuI0AAAACk5OTFu/v78X5+fn/+fn5//n5+f/6+vn/+fr5//n6+f/5+vn/+fr5//n6+f/9/fn/ub73/zhF8v82Q/L/NkPy/zZD8v82Q/L/NkPy/zZD8v82Q/L/NkPy/zZD8v82Q/L/NkPy/zZD8v82Q/L/NkPy/zVC8f81QvD/Mz/mxR8njhbDw8Mz9PT05fn5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//z8+f+5vff/OEX0/zZD9P82Q/T/NkP0/zZD9P82Q/T/NkP0/zZD9P82Q/T/NkP0/zZD9P82Q/T/NkP0/zZD9P81QvH/NEHv/zRB7/8zQOrlKTO6M9XV1VD39/f0+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn//Pz5/7m99/84RfT/NkP0/zZD9P82Q/T/NkP0/zZD9P82Q/T/NkP0/zZD9P82Q/T/NkP0/zZD9P82Q/T/NULw/zRA7P80QOv/NEDr/zNA6fQsN8lQ3d3dYfj4+Pn5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/8/Pn/ub33/zhF9P82Q/T/NkP0/zZD9P82Q/T/NkP0/zZD9P82Q/T/NkP0/zZD9P82Q/T/NkP0/zVB7/8zQOn/Mz/o/zM/6P8zQOj/Mz/n+S04zmHh4eFl+Pj4+vn5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//z8+f+5vff/OEX0/zZD9P82Q/T/NkP0/zZD9P82Q/T/NkP0/zZD9P82Q/T/NkP0/zZD9P80Qe7/Mz/m/zM/5f8zP+b/Mz/m/zM/5v8yP+X6LjnPZeDg4Fr4+Pj3+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn//Pz5/7m99/84RfT/NkP0/zZD9P82Q/T/NkP0/zZD9P82Q/T/NkP0/zZD9P82Q/T/NEHs/zI+4/8yPuP/Mj7j/zI+4/8yPuP/Mj7j/zI+4fctOMxa3NzcQvf39+/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/8/Pn/ub33/zhF9P82Q/T/NkP0/zZD9P82Q/T/NkP0/zZD9P82Q/T/NkP0/zRA6/8xPeH/MT3g/zE94P8xPeD/MT3g/zE94P8xPeD/MT3e7ys2xkLS0tIj9fX12fn5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//z8+f+4vff/NkP0/zNB9P80QfT/NEH0/zRB9P80QfT/NEH0/zZC8/81P+n/Mjze/zI73f8yO93/Mjvd/zI73f8yO93/Mjvd/zI73f8xO9rZKTO7I56engjy8vKu+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+/r5/9ze+P+covf/mqD3/5qg9/+aoPf/mqD3/5qg9/+aoPf/UoLz/x1p5/8eaeb/Hmnm/x5p5v8eaeX/Hmnl/x5p5f8eaOX/Hmjl/yBh3a4jJokI////AO3t7Wr5+fn++fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+vr5//z8+f/8/Pn//Pz5//z8+f/8/Pn//Pz5//z8+f9dvfz/AJf+/wCZ/v8Amf7/AJn+/wCZ/v8Amf7/AJn+/wCZ/v8AmP7+AJLxagC4/wDr6+sA4eHhJPb29tv5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5/1u8/f8Alv//AJj//wCY//8AmP//AJj//wCY//8AmP//AJj//wCW/NsAieckAI/xALu7uwAAAAAA8vLygfn5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/W7z9/wCW//8AmP//AJj//wCY//8AmP//AJj//wCY//8AmP//AJP3gQAAAAAAcr8AAAAAAOzs7ADk5OQe9vb2zPn5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f9bvP3/AJb//wCY//8AmP//AJj//wCY//8AmP//AJj//wCW/MwAi+oeAJDxAAAAAAAAAAAAsLCwAP///wDv7+9O+Pj47Pn5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5/1u8/f8Alv//AJj//wCY//8AmP//AJj//wCY//8Al/7sAJL0TgCr/wAAa7QAAAAAAAAAAAAAAAAA1tbWALS0tAPy8vJv+Pj49Pn5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/W7z9/wCW//8AmP//AJj//wCY//8AmP//AJj+9ACU+G4AbrgDAIPaAAAAAAAAAAAAAAAAAAAAAAAAAAAA4uLiANfX1wbz8/Nz+Pj48Pn5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f9bvP3/AJb//wCY//8AmP//AJj//wCY/vAAlflzAITcBgCK5wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4+PjANjY2ATy8vJZ+Pj42vn5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5/1u7/f8Alf//AJf//wCY//8Al/7aAJT4WQCE3AQAiucAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1NTUAB8fHwDw8PAr9vb2nPj4+O35+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/rNv7/3bG/P9rwfztM6r7nACR9SsAER0AAIPZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOvr6wDj4+MG8vLyOvb29pD4+PjS+fn58vn5+f35+fn/+fn5//n5+f/5+fn/+fn5/fn5+fL6+fjS/Pj2kP338jr/+eIG//fqAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADh4eEA1tbWAu/v7xv09PRJ9vb2dvb29pf39/eo9/f3qPb29pf29vZ29PT0Se/v7xvW1tYC4eHhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/gB///gAH//gAAf/wAAD/4AAAf8AAAD+AAAAfAAAADwAAAA4AAAAGAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAGAAAABwAAAA8AAAAPgAAAH4AAAB/AAAA/4AAAf/gAAf/8AAP//wAP/',yellow:'data:image/vndmicrosofticon;base64,AAABAAIAICAAAAEAIACoEAAAJgAAABAQAAABACAAaAQAAM4QAAAoAAAAIAAAAEAAAAABACAAAAAAAAAQAAASCwAAEgsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwAZKhQAOWAiAEV0KgBFdCoAOWAiABkqFAAAAAcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8ZAAAChAHAEp8JwBvu10AgNeSAInluACN7c4Aj/DXAI/w1wCN7c4AieW4AIDXkgBvu10ASnwnAAoQBwA8ZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbLgAAAAAFAFmWMwB/1YwAj/DXAJX7+QCY//8AmP//AJj//wCY//8AmP//AJj//wCY//8AmP//AJX7+QCP79cAftWMAFmVMwAAAAUAGy4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7v8AAD1mFQB6zXYAkPLdAJf+/gCY//8AmP//AJj//wCY//8AmP//AJj//wCY//8AmP//AJj//wCY//8AmP7/AJf+/wCV/P4AjvDdAHjKdgA8ZBUA6f8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//AABWkCYAh+KoAJb8+QCY//8AmP//AJj//wCY//8AmP//AJj//wCY//8AmP//AJj//wCY//8AmP//AJf+/wCV+v8AlPr/AJT6/wCV+v8Akvf5AIPdqABTjCYA//8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgICABb//wAka5wqAozquwCY/v8AmP//AJj//wCY//8AmP//AJj//wCY//8AmP//AJj//wCY//8AmP//AJj//wCX/f8Ak/j/AJP3/wCT9/8Ak/f/AJP3/wCT9/8Akvb/AIbiuwBZlyoA//8AAAECAAAAAAAAAAAAAAAAAAAAAADz8/MAqJaJHZDD5rQLnP7/AJj//wCY//8AmP//AJj//wCY//8AmP//AJj//wCY//8AmP//AJj//wCY//8Alvz/AJL2/wCR9P8AkfT/AJH0/wCR9P8AkfT/AJH0/wCR9P8AkfT/AITftABQhh0AjO0AAAAAAAAAAAAAAAAAfX19ADw8PAni3tuPuuD5/Quc//8AmP//AJj//wCY//8AmP//AJj//wCY//8AmP//AJj//wCY//8AmP//AJb8/wCQ8/8Aj/H/AI/x/wCP8f8Aj/H/AI/x/wCP8f8Aj/H/AI/x/wCP8f8AjvD9AH7UjwAiOQkASHkAAAAAAAgICAD///8AxcXFT/j19O+94vv/Cpz//wCY//8AmP//AJj//wCY//8AmP//AJj//wCY//8AmP//AJj//wCV+/8Aj/H/AI3u/wCN7v8Aje7/AI3u/wCN7v8Aje7/AI3u/wCN7v8Aje7/AI3u/wCO7v8AiunvAHC8TwD//wAABQgAqKioAHp6ehHp6em3/fv5/7zh+v8KnP//AJj//wCY//8AmP//AJj//wCY//8AmP//AJj//wCY//8Alfr/AI7u/wCM6/8AjOv/AIzr/wCM6/8AjOv/AIzr/wCM6/8AjOv/AIzr/wCM6/8AjOv/AIzr/wCM6/8Ag9y3AERyEQBenQD///8AzMzMTfb29vP9+/n/vOH6/wqb//8Alv//AJb//wCW//8Alv//AJb//wCW//8Al///AJT5/wCL6/8Aiej/AIno/wCJ6P8Aiej/AIno/wCJ6P8Aiej/AIno/wCJ6P8Aiej/AIno/wCJ6P8Aiej/AIno/wCH5fMAb75NAMP/AAAAAAXl5eWX+fn5//v6+f/T6vr/Wbv9/0+3/f9Qt/3/ULf9/1C3/f9Qt/3/Ubj9/zew+/8InO//B5nr/weZ6/8Hmev/B5nq/weZ6v8Hmer/B5nq/weZ6v8Hmer/B5jq/weY6v8HmOn/B5jp/weY6f8HmOn/Bpjp/weP15cBAAAFpKSkHfDw8M/5+fn/+fn5//n5+f/1+Pn/9Pf5//T3+f/09/n/9Pf5//T3+f/4+Pn/o+T6/wq//f8Hv/3/CL/9/wi//f8Iv/3/CL/9/wi//f8Iv/3/CL/8/wi+/P8Ivvz/CL78/wi+/P8Ivvz/CL78/we9+/8HvPr/BrbxzwR9pR3Ly8tA9fX17Pn5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//36+f+l5vv/CcL//wfB//8Hwf//B8H//wfB//8Hwf//B8H//wfB//8Hwf//B8H//wfB//8Hwf//B8H//wfB//8Hv/3/Br36/wa9+v8GuvbsBZnLQNra2mD39/f4+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn//fr5/6Xm+/8Jwf//B8H//wfB//8Hwf//B8H//wfB//8Hwf//B8H//wfB//8Hwf//B8H//wfB//8Hwf//B778/wa79/8Guvf/Brr3/wa59fgFo9hg4uLidPj4+P35+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/9+vn/peb7/wnB//8Hwf//B8H//wfB//8Hwf//B8H//wfB//8Hwf//B8H//wfB//8Hwf//B8H//we++/8GufX/Brj0/wa49P8GuPT/Brfz/QWm3XTk5OR6+Pj4/fn5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//36+f+l5vv/CcH//wfB//8Hwf//B8H//wfB//8Hwf//B8H//wfB//8Hwf//B8H//wfB//8Hvfr/Brfy/wa28f8GtvH/Brbx/wa28f8GtfD9BafdeuXl5W/4+Pj8+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn//fr5/6Xm+/8Jwf//B8H//wfB//8Hwf//B8H//wfB//8Hwf//B8H//wfB//8Hwf//B7z5/wa17/8GtO7/BrTu/wa07v8GtO7/BrTu/waz7fwFpdtv4eHhVvj4+Pb5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/9+vn/peb7/wnB//8Hwf//B8H//wfB//8Hwf//B8H//wfB//8Hwf//B8H//we7+P8Gsu3/BrHr/wax6/8Gsev/BrHr/wax6/8Gsev/BrDq9gWh1Vba2toz9vb25vn5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//36+f+k5fv/BsH//wPA//8DwP//A8D//wPA//8DwP//A8D//wXA//8Guvb/BrDq/wau6P8Gruj/Bq7o/wau6P8Gruj/Bq7o/wau6P8GreXmBZnLM7+/vxH09PTC+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+/r5/83v+v9x2vz/btn9/2/Z/f9v2f3/b9n9/2/Z/f9v2f3/RdL5/yXG7v8mxOz/JsTs/ybE6/8mxOv/JsTr/yXE6/8lw+v/JcPr/yK95cIQirAR////APDw8IH5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+vn5//r5+f/6+fn/+vn5//r5+f/6+fn/+vn5//r5+f+H8Pz/Oer+/zzq/v886v7/POr+/zzq/v886v7/POr+/zzq/v886v3/OuDzgWz//wD09PQA5+fnNPf39+n5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5/4Xw/f846///O+v//zvr//876///O+v//zvr//876///O+v//zvp/ek32+00Ouf6AMrKygCzs7MF8/Pzmvn5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/hfD9/zjr//876///O+v//zvr//876///O+v//zvr//876///OuX5miqptwUwv88AAAAAAPPz8wDp6eku9/f33fn5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f+F8P3/OOv//zvr//876///O+v//zvr//876///O+v//zvp/d033O8uOuX5AAAAAAAAAAAAvr6+AP///wDx8fFl+Pj49fn5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5/4Xw/f846///O+v//zvr//876///O+v//zvr//876v71OeP2ZY7//wAus8IAAAAAAAAAAAAAAAAA4ODgANPT0wj09PSI+fn5+vn5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/hfD9/zjr//876///O+v//zvr//876///O+v/+jrm+Ygyx9gINdPlAAAAAAAAAAAAAAAAAAAAAAAAAAAA6enpAOHh4Q309PSM+fn5+Pn5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f+F8P3/OOv//zvr//876///O+v//zvr//g65/qMNtXnDTjd7wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6enpAOLi4gr09PRw+Pj45/n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5/4Pw/f816///Oev//zvr//876v7nOub5cDbW5wo33O4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4ODgANHR0QLx8fE89/f3sfn5+fX5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/t/T7/4Xx/f+A8P31Xez8sTnk9zwuxdUCNtTkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAREREAP///wDo6OgM9PT0Tff396T4+Pjf+fn5+Pn5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+fj5+Pjf9vf3pPL09E3m6OgM7/3/APtbOwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACMjIwD19fUA4uLiBvHx8Sn19fVd9vb2jff396739/e99/f3vff396729vaN9fX1XfHx8Snl4uIG9PX1AFEnIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/wD///gAH//gAAf/wAAD/4AAAf8AAAD+AAAAfAAAADwAAAA4AAAAGAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAGAAAABgAAAAcAAAAPgAAAH4AAAB/AAAA/4AAAf/AAAP/8AAP//wAP/KAAAABAAAAAgAAAAAQAgAAAAAAAABAAAEgsAABILAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABorgAAS34IAHTDNQCC22wAh+OMAIfjjACC22wAdMQ1AEx/CABorwAAAAAAAAAAAAAAAAAAAAAAAEBrAAAAAAAAecswAIzsngCU+OUAl/37AJj+/wCY/v8Al/37AJP35QCL6Z4Ad8gwAAAAAAA+aQAAAAAAcXd6AP8AAAAOiNtNAJP32gCY//8AmP//AJj//wCY//8AmP//AJb8/wCU+f8Ak/j/AI7w2gB+1E0AAAAAAEd4APn7/ADc2NU5T7P33gCX//8AmP//AJj//wCY//8AmP//AJX6/wCR8/8AkPL/AJDz/wCQ8/8AjOzeAHrOOQCR9AC3t7cO8e/vsGnA/f8Alf//AJf//wCX//8Al///AJP4/wCN7v8AjOz/AIzs/wCM7P8AjOz/AIzt/wCG4rAAY6oO4uLiT/j39/GIzfz/Mav+/zSs/v80rP7/FaH5/wOV7f8DlOv/A5Tr/wOU6/8DlOv/A5Tr/wOU6/8Dk+jxBIvVT+3t7ZT5+fn/8fb5/+vz+f/r9Pn/6vP5/1nR+/8EvPz/B738/we9/P8Hvfz/B738/we9/P8HvPv/B7r4/wax7ZTy8vK7+fn5//n5+f/6+fn/+vn5//n5+f9e1f3/A8D//wfB//8Hwf//B8H//wfB//8HwP3/Brv3/wa59f8GtO678/Pzwfn5+f/5+fn/+fn5//n5+f/4+fn/XtX9/wPA//8Hwf//B8H//wfB//8Hv/z/Brfz/wa17/8Gte//BrHqwfPz86X5+fn/+fn5//n5+f/5+fn/+Pn5/2DW/f8Gwf//CsL//wrC//8Jv/v/CLXu/wix6f8Isen/CLHp/wet5KXy8vJo+fn5+vn5+f/5+fn/+fn5//n5+f/I7vr/quf7/6zn+/+m5/v/Tdz5/yzV9P8u1fT/LtX0/y7U8/ooyOpo7OzsH/f399D5+fn/+fn5//n5+f/5+fn//Pr5//36+f/++vn/9fn5/2rv/v857P//POz//zzs//886/3QOuLzH////wD09PRh+fn59vn5+f/5+fn/+fn5//n5+f/5+fn/+fn5//H4+f9o7v7/OOv//zvr//876//2Ouf6YUH//wDu7u4A6enpB/b29oT5+fn3+fn5//n5+f/5+fn/+fn5//n5+f/x+Pn/Zu7+/zfr//876//3Ouj8hDfc7wc44PMAAAAAAPHx8QDu7u4I9vb2aPj4+Nn5+fn9+fn5//n5+f/5+fn/8/n5/4zx/P1S7P7ZO+n8aDfh9Ag55PcAAAAAAAAAAAAAAAAA6+vrAN/f3wH19fUo9/f3fvj4+MH4+Pje+Pj43vj4+MHq9vh+w/H2KADM5wFk4e8AAAAAAAAAAADwDwAA4AcAAMADAACAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAQAAgAEAAMADAADgBwAA'};return{FaviconsByHue:FaviconsByHue};});'use strict';Polymer({is:'tr-ui-b-info-bar-group',ready:function(){this.messages_=[];},clearMessages:function(){this.messages_=[];this.updateContents_();},addMessage:function(text,opt_buttons){opt_buttons=opt_buttons||[];for(var i=0;i<opt_buttons.length;i++){if(opt_buttons[i].buttonText===undefined)
-throw new Error('buttonText must be provided');if(opt_buttons[i].onClick===undefined)
-throw new Error('onClick must be provided');}
-this.messages_.push({text:text,buttons:opt_buttons||[]});this.updateContents_();},updateContents_:function(){Polymer.dom(this.$.messages).textContent='';this.messages_.forEach(function(message){var bar=document.createElement('tr-ui-b-info-bar');bar.message=message.text;bar.visible=true;message.buttons.forEach(function(button){bar.addButton(button.buttonText,button.onClick);},this);Polymer.dom(this.$.messages).appendChild(bar);},this);}});'use strict';Polymer({is:'tr-ui-b-toolbar-button'});'use strict';tr.exportTo('tr.ui',function(){var Task=tr.b.Task;function FindController(brushingStateController){this.brushingStateController_=brushingStateController;this.filterHits_=[];this.currentHitIndex_=-1;this.activePromise_=Promise.resolve();this.activeTask_=undefined;};FindController.prototype={__proto__:Object.prototype,get model(){return this.brushingStateController_.model;},get brushingStateController(){return this.brushingStateController_;},enqueueOperation_:function(operation){var task;if(operation instanceof tr.b.Task)
-task=operation;else
-task=new tr.b.Task(operation,this);if(this.activeTask_){this.activeTask_=this.activeTask_.enqueue(task);}else{this.activeTask_=task;this.activePromise_=Task.RunWhenIdle(this.activeTask_);this.activePromise_.then(function(){this.activePromise_=undefined;this.activeTask_=undefined;}.bind(this));}},startFiltering:function(filterText){var sc=this.brushingStateController_;if(!sc)
-return;this.enqueueOperation_(function(){this.filterHits_=[];this.currentHitIndex_=-1;}.bind(this));var stateFromString;try{stateFromString=sc.uiStateFromString(filterText);}catch(e){this.enqueueOperation_(function(){var overlay=new tr.ui.b.Overlay();Polymer.dom(overlay).textContent=e.message;overlay.title='UI State Navigation Error';overlay.visible=true;});return this.activePromise_;}
+this.onSelectionChanged_();},get selection(){return this.brushingStateController_.selection;},onSelectionChanged_:function(e){if(this.lastSelection_&&this.selection.equals(this.lastSelection_)){return;}
+this.lastSelection_=this.selection;this.tallMode=false;this.tabView_.label=getTabStripLabel(this.selection.length);var eventsByBaseTypeName=this.selection.getEventsOrganizedByBaseType(true);var ASV=tr.ui.analysis.AnalysisSubView;var eventsByTagName=ASV.getEventsOrganizedByTypeInfo(this.selection);var newSubViews=[];eventsByTagName.forEach(function(events,typeInfo){newSubViews.push(createSubView(typeInfo,events));});this.tabView_.resetSubViews(newSubViews);},onSelectedSubViewChanged_:function(){var selectedSubView=this.tabView_.selectedSubView;if(!selectedSubView){this.tallMode=false;this.maybeChangeRelatedEvents_(undefined);return;}
+this.tallMode=selectedSubView.requiresTallView;this.maybeChangeRelatedEvents_(selectedSubView.relatedEventsToHighlight);},maybeChangeRelatedEvents_:function(events){if(this.brushingStateController){this.brushingStateController.changeAnalysisViewRelatedEvents(events);}}});})();'use strict';Polymer({is:'tr-ui-b-dropdown',ready:function(){this.$.outer.tabIndex=0;},get iconElement(){return this.$.icon;},onOuterKeyDown_:function(e){if(e.keyCode===' '.charCodeAt(0)){this.toggle_();e.preventDefault();e.stopPropagation();}},onOuterClick_:function(e){var or=this.$.outer.getBoundingClientRect();var inside=true;inside&=e.clientX>=or.left;inside&=e.clientX<or.right;inside&=e.clientY>=or.top;inside&=e.clientY<or.bottom;if(!inside)return;e.preventDefault();this.toggle_();},toggle_:function(){if(!this.isOpen){this.show();}else{this.close();}},show:function(){if(this.isOpen)return;Polymer.dom(this.$.outer).classList.add('open');var ddr=this.$.outer.getBoundingClientRect();var rW=Math.max(ddr.width,150);this.$.dialog.style.minWidth=rW+'px';this.$.dialog.showModal();var ddw=this.$.outer.getBoundingClientRect().width;var w=this.$.dialog.getBoundingClientRect().width;this.$.dialog.style.top=ddr.bottom-1+'px';this.$.dialog.style.left=ddr.left+'px';},onDialogClick_:function(e){if(!this.isOpen)return;if(e.srcElement!==this.$.dialog)return;e.preventDefault();this.close();},onDialogCancel_:function(e){e.preventDefault();this.close();},close:function(){if(!this.isOpen)return;this.$.dialog.close();Polymer.dom(this.$.outer).classList.remove('open');this.$.outer.focus();},get isOpen(){return this.$.dialog.hasAttribute('open');}});'use strict';tr.exportTo('tr.ui.b',function(){var FaviconsByHue={blue:'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALgAAAC4CAYAAABQMybHAAAlrklEQVR4Ae2dCXwdVb3H5265yc3SpEk3ukEXCqVUBLT4Wm19oFKtaN0fKijy9CMguPBarIJsIiA8qsjTh7SllAoFeVBaEARkLV1ooXtL0yRdkqZp9u3uy/v/5uY/OZm75y659+acdnLOnP385zv/+58zZ2YMinTplIAhzsoDceaT2RKUQLwHIMFqh0V2ll0kn4XA6byv9/Vw834kX19e7keRQCzhRyk6bJJYRvD1YTXuhRdeqDj77LPPtNls400mU7HRaCzFFggEVJ/iSqhsicFgKIXUKL6bvB6fz9fj9/u7Kb4bPjaK67Xb7Q0HDhw49IUvfKEd2XUb7WpxHIYvXRgJ8AELkzRso1gmKrwkBfjG7373u5Zly5ZNKS8vn2G1Ws80m83YphPI0wnQUemQFp0IzQR9tdfrxXbI5XId6ujo+PCuu+6qXbNmjYfa9NMmngDoBmt+hIe944M53AUhwqwCvXTp0qJrr732opKSkk8XFhZ+imC+gIAryAZB0QnlJuB3OJ3Ot3p6el5/6KGHttxzzz0O6pse+GEP+3AGnKE2EhgG0tAFt99++4WkoT9tsVgW0DaH4guzAeg4+uD0eDxbaXuDNPzrt9xyy3bS8G4qB8BF6OOoKr+yDDfAB0B91VVXFf72t7+9lLT05QUFBZfQoYWtnA+ux+12v0ra/W+/+tWvXlq5cqWTBjUsYR8OgDPU8KGtjR9++OHHx4wZ8+2ioqKv0X4lbfnsWh0Ox9+bmprWzpgxYxsNFBpd1Op5bcbkM+AMtgr11q1bTz/zzDP/gy4Qv02zGtPzmehIY6MZmmq6UF176NChJ+bMmXOkD3QR9khFczY+HwEXwTbV1NTMI229FCYIXSTm43gTho8uUgMwYUir3zN16tR3qAIfbXkJej4dcIxF1dbkm44ePfqZqqqqpTT7MZf2pYsgAZqN2dTS0nLP5MmTX6EsDDrDHqFU7kTnA+Aa2BMmTDBv2bLliyNHjlxCZsgFuXMYhr6nZL7saGtru/eiiy7aUF9f76UeAfKcBz2XAUffVbgJbAuB/Y3KysoldONl5tDjkrs9oBtL+1tbWwH6UwS6/mZSzg0sVwHXTJG9e/deOGXKlOWksS/MOelncYdJo2+vra396axZs7ZTN0XTJYt7Hdq1XANc1dg0DNOqVatGLl68+DZa/3E1XTwCeOn6JLCly6ncU9+mNLnBZRLOYPAHHI5H2l5/8TdHbl3SRjUx6DkztZgrgKOfDLf5xIkT36moqLiLzJG0rAFJAomsKDp1W51S74IZnSIX8DcrXV3LlK/Oe5xqZPsckGc96LkAOPpowrZ79+5ZNK31BzkzQtKI4qxvV0dJTSLJ592kHKu7QfnPxXupFmhzbFkNeTb/tGsae/bs2Va6wr/lrLPO2izhTgLQZIuaaMp1yvTNyvNbb1HomFB1ZtrAUNYqymztGMNt2rhx44T58+evohs1n0r2+AyX8mnT4KIAvZ63lA82f1/55TX1FJ21tnk2As4zJObq6urP0BTgCmlri2TFDmcEcHQDtnlz4w+Uyz+Hm0Rsm2PuPGtcNpkomtZesGBBYXNz8210d+05CXfWsBLaEQNd5I+e8JyyYettCh0zyoBrpawyWbJFg2twv/jiixPnzZu3mhZFzQ2VqIyJRwIZ0+BiZzyeTcqebVcqS350nKKzxmTJBsDRB3WWZN++fXPpps060tpVouxkODEJDAng6GIg0KI0Hv+mcsXnN9FeVsyyDLWJwnCbadXfomnTpm2UcCcGc1blNhiqlNMmblT+9soi6hdmWKC4hlSJDiXgaBsCsNDKvysnTpz4JIWLaJMupyVgKFLGjHtSefrNK2kYFtpwjIeMs6FqWIOb7kr+Yty4cX+m2+0446XLBwkESHuPrPqz8uymX9BwhhTyoQBchZseQiigdcj30grAO+SDCPlAtW4MeLikdMQdyvqt9yp0rCl1SDR5pgFX4V64cGERvdhmRWlp6XU6scjdfJNAcfF1ysqNK5Q5C2F+ZhzyTF4AqHCPGjXKSjdwHqUHfr+ab8cyW8YzZLMo0QTgcj2jfO/S7ynNzS7KxtOI0UqkJC1TGlyFm3pccPDgwfsk3Ck5drlVidX6VWXFxvvAAG0Z0+SZAJzhtjQ2Ni6ld5D8KLeOjOxtyiRgK/6R8uy7S6m+jF14phtwmEBow3L8+PGr6FnJm1MmLFlRbkqgtOxm5am3rgITtIGNtJrJ6QQcHcdPkYUuKL9MsybLKSydlICijKxcrjz+0pdJFKzJ0wZ5ugBnuM27du2aT7ffV9JUIGCXTkqAJEAsjJ2wQlm1fj7tpPWOZzoAB9yo1/zSSy/NoLdJraMwFsdLJyUgSqBQGX/GOuX+FTMoEpCDmZRr8nQBbqIHgovnzp27mtaWlImjkmEpAU0CYGPmR1crF19cTHH4hU854KmuECcMOmo9derUAyNGjLiawtJlWAJZOQ8eTQb27keUyz7xM8qS8jnyVGpwNk0s+/fv/4qEO9oRlWkDJGArvVpZ89JXKC7lMyupApzhNm/YsGH6GWec8eCAAcgdKYFYEhhz2oPK3X+ZTtlSao+nEnDzxWRL0eNmj0q7O9bRlOkhEoA9ft6cR5WPq/Y4IE+J+ZyKSjS7m56jvK+srEzeqQw5epmNyDkbXBRPT8//Kl++6EaKSok9nqwG10yTHTt2fJpWB0q4xYMlw4lLoJhu5z/y3KepYEpMlWQBV7U3mSXFNN99H71YPfEByRJSAqIEwND4yfcpFyzgqcOkGE2mMGtvy2OPPXY9vZjnTLGfMiwlMGgJWCxnKktv/QmVT3pWZbCAM9zmxx9//IzRo0fj0STppARSJ4HykTcqN//3GVRhUqZKMoCrC6no6Zy7yTSxpW5ksiYpAZKA0WhTPj73dxRKakHWYABn7W3Zs2cPvjH5eXlApATSIoGi4i8oK56/tA9ysAr2EnKDARxlzJdddlkJ3dC5N6HWZGYpgUQlMH7SvbRWpYSKsamSUA2JAs7a2/ynP/3pOvrc9eSEWpOZpQQSlYDZPFn54a/xcDoDnpAWTxRw5DfRJ7DL6HUPP060rzK/lMCgJFA+8sfKZd/CqlRc9yXEbCKZWXtbli1b9gN6EX3loDorC0kJJCoBk6lS+ebVP6BiCU8bJgI48ppxU2fs2LHXJNpHmV9KICkJVFZdo3zsY7j5w6ZKXNXFCzhrb/PDDz/8HbK9x8ZVu8wkJZAqCZjNY5Wf3vkdqo4Bj8sWjxdw5DPRt3KKTjvtNNxhkk5KIPMSqBz1E2Xq7ITekBUP4Ky9LevWrfsGae9JmR+ZbFFKgCRgLpik3HL3NygUty0eD+Cq9h4/fnwBbTdIQUsJDKkERo+9QSkr47ubMfmNlQHaG5v56aef/ndaUDVtSAcnG5cSMFumKXc/fDGYpI35jCiXeADH3KOZ7lp+Sy6HjShHmZApCWA57dgJ3wKTtIFNQB7RxQIc6abLL7+cniEesTBiLTJBSiCTEiguWah8/isjqEkAHpXhaIk4M5BuXrp06ZfoOUtcvUonJTD0EjCaipSvff9L1JGYU4bRAEeaCjh9P+fr0jwZ+uMqe9AnAZgpo0Z/nfYY8IgcR0qA9sZmeuCBBybZbLZ/66taelIC2SEBKzF5zTJMWbMdDl5DXDTAVe29aNGib5D2jpQvpEIZISWQEQkYicm5C0QtnjDg6uwJPY72tYx0WDYiJZCoBMorGXDW4iE1hNPMOBMQb1qzZs0MmvueHlJKRmS1BCZYYZoOA2exTFd+dT/eTsuzKSFaPJwkNMDPO++8+fLiMvdA+Z8JJcqPN+9RGnocoZ0PBELjFF2cbjdYIEykvq4wWehd4APb05dBari4gaWCe/p8AT+uFOdT4j7aoJTB7oAGowFurqqqmicBV5QPmgLKX3b7lVbHANmRLLPVVSjnGT6hzFRa44dHHEqIHhQThXC8+YQiqQ66K9rnvakoD1O9DPiAJvSAo8vYjMXFxWZ6U9VFA3IP052fv+5VGntzBW4+SCYl4KtQ/L3tpCBJ0+WpC/hKLgKrvb29DDj41Q4WIvUOcaZHH310lslkGqlPHI77uQd38CgZTBbSVBVKXk+CGYwjS758/ywwS1sIz/oI1uCmmTNnflKaJ7l/OmuQG3migQ9xnvg0W2gaN/2TfYDzoLQDFw5wVYOT/T1XAq7JKacDKuS2csVg1B/unB6W2nkwaiiumEs7rMEBueZEG5zpN9Gt+QKyv+douWQg5yXAkPvtHYO78MxiCZisJXNsVRML7C3HndRN5li1w/WnNPaNDz744Ll0ZpRm8Zhk1wYhAYacjPJBlM7eIgHFUFryxZvPpR6q/Io9DavBJ0yYcJY0T0Qx5U84CDnNrtjb82dQZHqZysefRQPaRltEDc4JRlr7PS1/Ri9HopeAwWRWjLYKQiF/NLnBWgpmocGZY3XYoomCBOybaPXgNKnBVfnk7R8V8qLyvIAcrBoLiqaCXdoYcvXYMeB8KmPfSIBPUVPln7yWQD/kjEEOD7fABsBVfvtGoTIdYoOPHDnSXFhYODmHhyq7noAEgpCPUPyOTiql3QBMoIbsyGo0F04uInYdbW3RTZRbb711AnXZmh3dlr3IhAQYcpooz0RzaWmDTk1r0YLrwS4GwRaJuoMGmXrjOeecI5fHQiLDzKmQF9ILXFXIGYfc8q2jZ4JdBlyFnE9ZHolx1KhR8gJzmMHNw9Ugz8U7nrijWToyZCZFtMEBu7GoqGgiD1j6w08CKuTWUsXv6s65O56GApVdlWM+cnoNbqB3D+JzEdINYwkw5DlnkxvNYJetEdVEETU4Ioy0RLZEzoEPY7r7hh6EvIQ0eQ/FZP/sCpilPgNwKG0VbgyFdzTqCXC8ZFw6KQEAoxgLS3NoPbkR7GosIyxqcBxSgwQcYpCOJWDAOnIrKUbS5AH9M5GcKUt8OiEZcK1HbIMjQiVfAq7JRgb6JADIDQR5tpuuAaMGuGaisAbXIiTgkutwEujX5L2UnJ02uSEIOHdfZVpqcBaH9GNKIKjJQ6yAmOUylYHsa+6cprBZg3MfpA3OkpB+WAkENXmxEnDbs2+e3KABrvU9RINTih56LbMMSAlAAqomL7BRQFOU2SGYgMouOqV1jGHWIrxer50+8iofV8uOQ5a1vVA1OUEecOPtWdlhkxsUH/2saE5lmufBtVifz4erCOmkBGJKIKjJ8V0ETT/GLJPODAG/X8+uOg+O0087BaHB09kJWXd+SSCoyYuUgIceaB/qeXL/AA2uci3a4JB8QGrw/AIwE6NRNbmlcMht8oBftT40ZY2xsw2OsJogAYcopEtUAqomt5Am9w6dJg8ENPNagzysBs/2W7KJCl/mz4wE8OYsg3loNLnKbNAG1+DGqFmDI1LdpA2eGRjytRX19XAEecDr6kMqcyM1BNTrR41ltCxqcAYc6yOlkxIYtASCmhyP9WZ2doVmUXhtL1hWHWtw3lccDkcb1H22L6zROiwDWSmBoCa39mnyDHSRmPV7nG36lliDs1r3t7e31+kzyX0pgcFIQNPkGbrj6be3gV287Z95Vk0U7MCpkdXV1bXyIjMoEPk3eQmokJsKglOIAD1tm6J4Wo7UMsd9PQ+wBse+CvgzzzwjAe+TjvRSIwGGnB4qS02F4WohE8W58zk94CGzKP6XX3652+VyNdN6lFHh6pFxUgKDkQAgDygWxeDzDKZ47DJeV3PvvtfpVQChJgoKs80C+8Xf09NzRJopEIt0qZQAIFfou0GpXoUIVv0uxxHqq8ov+cxzyDShmsFut9elcmCyLikBloAKuZEm71Jsi/vdKrMi4GqTbIMz8cjgw0yK1OB8SKSfagkMgDwVlZMGDzg6oJR9tIFh5lmzwdEMR/pPnjxZiwjppATSJQHVJg/QRaffm3wT9Gvg624GswPgRsXhNLh//fr1u2nRFYCXTkogbRJQbybCXEl2diXgCzh2bthNFQHwAZAz4BgEgEaijz4C29zZ2VkjzRSIRbp0SiAIOT7MgCnExDeyThS/s7uma+vaZqpANFHUbusBZ8i9ra2tWyXgqozknzRLQIMcF56JOiLc19O6lYrB1hmgvVGVCDj2VQ1Ovq+mpmaLBBwikS4TElAhx7vJE55dIWhb6rZQH6G9WYNrXRYBh/ZmDe5buXLlVj85LacMSAmkWQIa5Im0Q4x2bXkUGpzhZo7VWsIBrp4JGzZsaCc7/KDU4olIW+ZNVgL9kMe2x4P2d+dB+86X8NFP1uARAUffWIPDnvHSdOE2CTjEIl0mJRCEPA57nAj3dzXj468qr+SzDa51V9TgiGTAcTZ4yQ7fLAHXZCUDGZSABnlUm5wgba3dDFZpE00Uraf6Bx5YveNM8C5fvnzbJZdc4iwuLqYH7Yavq+ytURq70rRIKIvEGlmZAYswDjZCRBchLUJ0ULeGqYzaQL8AfEj/PA5nz8u/Zw3O2ntAC+EAR0bVnnn33Xe7Gxsb35gyZcqlxhR9mGj/oU7liWfrlPZOd5jRZGfUbK9bmUnPGIYIeEB3B8i1PyUKBHTo+vPFEYrcfpR6orYfR6NZmiUQ8Cs9XU1vbDiyEysI2f5myLVe6wFHAqSlanDyPTt37nz+9NNPTxngv/3DHqW5lV4tkGPO67ErPi+9pgw/mYAGfjyO8zJo+vL6dH2dmc6vb1/fP31/9Pn1+7HK69P15fXt9eUP+LxKR/OB5yk7flrFOfABNehtcCSKgHuvu+66t2n5bGtk7TGgvpg7uQg3BmW22BSTGa8pIwehx+s4L3wxzOXFOM4j+sjHecSwmEcMi3nEsJhHDIt5ENY75IXjMhxWI+P4E6u8Pp3bYV/fHsWDRb/f1Vq3b9XblBzxAhNFowEOte+hlYWO+vr6f6QKcDSaq06F3FQYdeUEow9fDGfLmMU+ieFI/RPziOFU5Y9UT/T4gOJ2tP/D7e7Bmz+hwcNeYKKOcIAjHiaKZqa8+uqr6+l9KYgf9g6QG/sgxwHXbxAQgyCG9fmGal/skxiO1B8xjxhOVf5I9USLV8j+7mjd/Rz1RzRPwGuIiwQ4zBScFaDas3Tp0r0dHR2HpRYPyo8hD+7Jv5mUABj0eeyHjx58Yh+1y4CDVTAb4qIBzpCjEjfNiW+Qd+775dcPeTRdI9NCf+OSlQl98M3RvAFM0sbmCVhNCHAcSah8TYuvXr16PT2MjAql65OAapPjXXzRnP4iCnk5Llw5ToMvhsPlzYU4cQxiOFLfxTxiuC+/3+/xNB9/cz3tito7rHmCIpE0ONJwRrAd7l61alXjkSNHXpBaHKLpd5hZMfELJ3FA9Buy8oESw/p8vC/mEcOcnmu+OAYxHGkcYh4xTPlx38DtaHnhZP3rjZQEDR5xehBF4eIFXDVT1q5d+whp8YhnS7DK4fdXhdyEd/FJl04J+ANef3PDpkeoDTZPkgIcfR2gxe+7776aY8eO/VNq8dDD2A95sjamLE8/eSTggRsuLj2Otn821D5fQ4lxaW8cpWgaHOnQ1pqZQmHXU0899VePxxPWoEeB4ewYchwadhzmw4V4jhPDnJ6oL9YhhuOtRywjhuMtr88n1iGGOZ8YJ4Y5PZKv+H2BthOb/0pl8F5mEfCoFkUswNEHVICLTdVMufPOOw+QFn9TanGIJtTBHjeSucIHCjkQZsfhSOmcL14/2fqSLa/vZ6z6YqXr68M+1p24nK1vHq3++wHaZfMETEaFG2XjARzaWgOcwq4XX3zxYdLiKC9dGAkw5Pqf2czso0OMkRhGXG5u9N5vpb3p/YdpAKy9AR+YjGlJxAs4a3GcPa4lS5bsOnHixGapxUkaEVwQcnqrasYdw80wowMcl/HOJN0gtLfb1bH5yMHHd1FlDDhr75QAjk6yFsdVKyB3bty48UE5owLRRHYa5JgSY8dhniZDPMeJYU5P1BfrEMOR6hHzIBzLcV8j1aePR31cRgxzPjFODPel+xWvv6N5x4OUhCWoYA8MxqW9KV9cJgryAXBocQbcdeONN+6kd4k/J9eoQDyRnQq5se+Fk3yg2UcxDvcdULUmjotcbeQULhtvffr8XC6Sj5a5TORe9KdwXq5PXz5KOn0WUHH2nnyudt/qnVSMtXfMqcH+xuMHHGVYi6sXm2jwpptuWk4PJrfLNSqiSEPDGuShSTImggTUNSdee/uxA2uXUxaGO27bm6uNxwbnvKzF8fOABp2vvfZa89atW/8oLzhZRJF9zVyJnEWmCBKgb14qPZ01f2xv3o03VsE8AXNx295cVSKAo4yoxVXIFy9e/Aw91rZLXnCySCP7Jpo+NNLnPMQvHXAYfjz/UDuXEcNcVowTw5yeal9sQwxHakfMI4bF/HhiyuPq2LV/293PUB6GO2HtjfoHA7g4o4LGnWvWrLnL6XT6pKkCkUZ3gNxAL4HnA4rcCMfrOG+k8rHS420n3nyJthcrPxgK+D2+5oa37qI+qHyRj4vLhLU3xpAo4CjDgOOMUrX4HXfcse/AgQPr6I20SJcuhgQYcvVijS++pN938RpQHD0n1h378Cms99Zrb7CXkBsM4GiAIVenDGnfccMNNzzU0tLSKE2V+OSvmiuYXZFOkwDmvD2e7saa/X99iCLxOBoAF7W3ljfewGABZ1ucpw2d7733XusTTzxxE33+xCNNlfjED3vcqELON2WGr0+WCS03cXtaTmy6qbutppUkyHAnNO+tl/xgAUc9DLmmxWnacAeB/hDdANK3I/cjSCAIebi3d0QokKfRZHcrvZ01D9XtW72DhqjX3mBtUC5ZwGGqaFqcws5LL7109dGjR9+WN4DiPx7DHXLc0HE5Wt7es/m21WCob4PiTOimTjiJJwM46gPg2PiCE2ee/Wc/+9lvyB5vkvY4SSNO12+uxFkgT7LB7vZ6uptq9678DQ3JThsYggkAppgvCg7OJQs4WkUnMH2CMw6dc9ANoJNPPvnkL8ke90p7nCQSpzPS9CFscryHbzhsEEvA7/a2NLzzy46WXSdpV+WH/KQuLFEvu1QAzrY4mypqJ+lVE9u3bdv2Z9jjEnIWd2wfkBsM+W+T9813093K6j/X7l+9nSQjwp3UhaUo5VQAjvoY8gGmysKFC1fSgqxX3G6ckNLFKwEVcu3rY/k5swK729Hb9Mqed29fSXLRmyawCAZ9YSnKOVWAo06GHDTjQgGdti9atOjXdNH5noScpJGAU00VI74+ln+OXv2gOJ0t7x3cduevaXQqJ+TztGDK4IbkUg24aI+rkNNXIrquuOKKG+kBiYNyURZEHr/LR8j99OFXt6v94KH377/R4WjtImkAcBFuMJQS7Q1Jp0NFoHNiBw0Eube2tnbT/PnzFzz3UtMIA76mJV1cEjAYcIhInLgTkuMOZonH3XW8dvdff9zZur+JhtNLGwMO8zal2hviSgfgqBduAOhki7u6u7u3NHWO+yxNidkk5EEhxfM3CHmfSHN0zQq98Fjxunta6w+v+9GphneO0Wj0cKdUc7Nc0wW4qG608AcffNBrMlvfLx0x5XMGo7lAQs6HIbbfLytNnLELZUkOrO2mF2b2nDz64rX1hzccpG7p4YbmBuApd+kCHB3lI8G+2vnOlr0dBYVV+4tKxl1MswWW/gOX8rHlXYUsq+C8ChaeZv8/vOqYvo5hb2l48+d1+9fiNrwId8rmuyMd7HQCLrYJyDXQ20/tOGUxF+6wlU1aYDQWFPGBEwvIcHgJ9MtKE2f4jFkQq9rcnu72xrp//OTIgccx181wY8477XBDBJkGXAO9o2VPm+JzbioZMXWewVRQ2n/g0C3poklgoKyyc57cTxeUXnfHCVrXfU1D7fr9NJ4e2gA4w530OpNoMuK0TAGO9ljlaJB3tVd3u1yNb5ZVzPy40Wyt7L+Y4u5JP5IE+iFnsUbKmfl4zHN7nG3VdXtWXNvU8GYd9QBgZxxujDyTgKM9OAZc9e1dDY6ejoOvl1fNnm0yFY1TaApR/QhoMK/8G0UCGuQGEmUWKHK83jhA89z0gvoPDu1cfn1b864T1H29WZIRzc1iyzTgA+CmTqj7Lkeru6156xsVoy+cQk+fn44DJyHnQxTd1yBXRRk9bzpTsSrQ7/MoLvvJN/a/d9uSno5jLdQew40bOVghmFG4Md5MA4424UJA97rtvub6f71VPupcq9lSNttgNBLj8oZQUFzR/w6UU+ZVOeD2eV2B3u7ax/a9e/PvXI7OTuqxCDcuKDMON6Q2VICjbYacJ/jpHYte/8mjr35gtVUdLCwaPYfmyunDlFKbQ1ixXBByiDRzTl0RGPBiPXd7S8Pbyw68d+/TdAz5YlK8QzkkcEMSQwk42mfI4Wugt53c3uB0nHyttHz6THo4dywOnjRZIK7ojiHPxOw4lg4EYJI4mnfW7V95ff3h9bupd9DarLlhkohTgZk9+/pElS2AA27eVOjt3fW9p4699kr5qFkmc0HZR6TJ0nfEYngDzZUYmQeZrN6ZhEnSeXj1nk2/vr2nsw5vn4LGZrj1i6cG2VLyxYYacIyAz2zW4hro9HPnO3nstZ2FhZX7Cm1j5tCDAEWkyqU2j3HctV+7FJvjWE+CWRKvt6utpeGtX+7f/vv/6zNJGG7McfPFZNpuv8cY/oDkbAAcHRIhF0FXw21N2084HfWv2UonjaHPhEwJaikJ+oAjqdvRINfFD2ZXfSILF5I+Fz2kUP/akT0rlhyv2bCX6mKNDcD1N3CgqIbc4RzPJof+YOoEJx7eioNPl+FDlHSxqdgQnj77h5+oGPeJXxQUlE3Cg7qZ+EmmdnPWYYYjGRec/nMrbnfnsbaT2+6v2f3wZqoPJghDzVOAvNwVDbLCSqbplJTNNsAxKP5hBeR4OBGfSQDkDHpRYWFFyYzzf/Gd4oqpV5JGt+IZxlRqLGorr1zwmdjEmOMZEp/X4erpqFld/f4Djzud7ZghgabGBrDZ1sYsCa/lTqwhKphOly0min6MLCT42KAV2Kbzeb1Ob9Pxf+32utteLSqZOJ4++jRJmi16Efbv95/8rDsi++pzFX3mCM1kvXPkw7X/Vbd31eskc3H6D9pbhBvHJ7mfiv7upjSUjRpcHCD6xyYLa3PW6DBbVM0+4/yffKq88iPXmq0jJuOdf/J2vyjC/nBQk/fviyHRzva6u462N+96qHrng29RHtbUrLUx9cc3bljpsEISq8yKcLYDzkIC5Aw6bHNAzva5CrnZbC6c/pHrLykbefYVZmv5NAk6iy66PwBsV8fhrrYDj1Xv+uOr9GYyBpt9ntcWbe2s1NriiHMFcPSZtTlAhzZn0AE4ww7fOuP86z45ovLcKyzWkecEL0RN0kYnwYguaGP78MJLetl8277O1j2Pffj+n96mPAAZG8BmHxobYPMdSYCdtVqb+qa5XAKcO40+49qBQYc2Z42uAk77qj919tUfqxh1wZXWosrz6cEKslxQbPhOLwZNFKz4I7D9broL2fp+e/OO1TW7H3mPBMNgi75ojgBqvpCkYG64XAQckkW/sYlmCzQ6Ty2KoBeccc53Z5eP/uiXrIWjFpjNRTaD+no0FM1/2DWo6cIRb3D1eh12l7P5jY5TH6yv27cGt9cBsQg1wtDWvIl2dk5obeq75nIVcB4AQ86gs+nCoLNmV7V8YcnY4ikzvr3ANuKMz1mLqi4k0E3q+7nVu6OoIn+cOv9NUyJ4+ACfBKG3t263d9a9XPvh2jecPSdxg4a1M4BmyBlqnvaD1s4ZcyTc0ct1wHlMetBhi7CNziYM+6qmrzrtwtHjJi/6rK1k/OfoiblpAJ1hz0XNzpoai6AANTafu/uwvafh5cajG//ZcmL7KZIJA8xwiz7SoK1ZY+c02DQO1eUL4OJ4grZH0E6HRmetDsAZetE3T5q6eHr5mPPmWQurzjcXls8i0K20VFcx4iWYeA9JFpoyA4CmJatYI0JQu7zOjr0uZ8v7HU073zlW82w1dR7aGPAC5nA+0llj8z2HnDNFaAxhXb4BzoMMUtlvo0Ojs1bXA69qdEqHby4sLLeOm7p4Vln5tAsshRXnFxSMOJseirbgAhXPjAZvmrDYgn7/jRRuPjV+EGLUxbzRBSKWqdJ7RnChGKBPftAt9AMeZ/v7XR2HdzTWPLvX6eyAycFQA2jeGHBOY23NGhuNcEMUzA/HRyo/RhM6ChF0aHbRVhe1O0POceybiovH28ZNW/SR4pJJ55oLiieZzLZJJottPFY2BoHHWnWAT1Wr0owkUn18JJYoHv9xUQiQNd/roJfnNPi89mNed++x3p5jexoPb9zV29uAu4qAlDUx+ww2fI6Dz0CL9nWkzlD23HZ6qef2aKL3HmNl84VBZ83OQEfyOR98lDWOnjB3dFnFOZOttjGTLIWlk81m20RaMlBpUEw2Ay2QoRPARg1SffQXF7F9vtpFaOEgxbSrhuhDAV57gBZ+BBSf3e9ztXq99uMeZ/dRl73pWFf7vqOn6jfBhmYoRe0rwhsuLOZlu5p9tTv5/Gc4Ac7HEWMWN4ZW9AE6Q83Q8z6fHKKvQq+r10DmjrmoZEKx1Ta6yGItK7aYy7AiUvF4u+weV1evy37K4eip7yWzAmBCi4obwwyfta7oI8xAM8TYF/NwWbHevNXWNPYQNxwBF4Uggo4wg8q+CL0IuAg350Ec18H1oi0xjH3RMXiI4zBrVwZcDyxDy1DzPudnn+tjX2x32IQhfOmCEmBZMJDwGXQxLMYBbqSxz5AjDg4+b7wPH9DBMXz6fUCKOEAs+gwv+0gTw9jHBsd+cG+Y/uUDMUyHH3XYLBsGNJIvQq3PgwbEesQGGUDRR1i/Mez6eHEf9WJfOp0EWPi6aLkbQQIsLwYZ2aLFiekRqhwAJkPK8KJMtLhIdcr4PgnwwZECSU4Cejnq91G7Po7BFVvWx+n3xbwyHIcE/h9VLWRYHWXC/QAAAABJRU5ErkJggg==',green:'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALgAAAC4CAYAAABQMybHAAAltklEQVR4Ae2dCXQcxZnHR3NoNDp8SD7kU7bxFXCchBhMYoLNmhCcOBBykGw2gYTkPV6AhGXD2sTZJQcJG3jsgw3hscuCsTEsOAQW1sbY+MAHxpYtHzI+5EOy5UMStnWPZkZzab9/j75WTWt6NKO5Z6r82lVdXV1d9e/ffPq6uro7zyBDIhXIi7DyngjLyWJRKhDpCYiy2pwoztrpxSwCb+d1bayFm9f1Yu3+cj2MAgOJH2bXnNnEGiHWppW8d999d/inPvWp6YWFheNMJlOR0WgswdLT06PElFdM+xbn5eWVQDXK76TI7vP57H6/v5PyOxFjobwuh8Nx4dixYye+9rWvtaK4ZqFVNY/TiGUIoQCfsBCbcjaLNVHgJRUQG3/4wx9ali1bNmXYsGEzrFbrdLPZjGUagTyNAB2ZCLXoh3CJoD/p9XqxnOju7j7R1tZ2/LHHHqtbtWqVh47pp0X8AaAZbPmRzvnAJzPXhRBhVoBeunSp7b777ruuuLj4xoKCghsI5s8TcPnpIBT9oNwE/D6Xy7Xdbrd/8Oyzz+5+/PHHndQ2LfA5D3suA85QGwmMPLLQ+b///e/nkIW+0WKxLKBlLuUXpAPQEbTB5fF4KmnZShb+g0ceeaSKLLyb9gPgIvQRVJVdRXIN8CCo77777oI//vGPt5CV/n5+fv5NdGrhK2dDsLvd7k1k3f/n17/+9frly5e7qFM5CXsuAM5QI4a1Nh4/fvza0aNH/4PNZvs2rZfRks2h2el0/u2TTz55dcaMGXuoo7DoolXPajcmmwFnsBWoKysrJ02fPv3v6QLxH2hUY1o2E63XNxqhOUkXqq+eOHHitblz557pBV2EXW/XjM3PRsBFsE21tbXXk7VeCheELhKzsb9Rw0cXqT1wYciqP37FFVd8SBX4aMlK0LPphKMvirWm2FRfX//lESNGLKXRj3m0LoOOAjQas/Py5cuPV1RUbKQiDDrDrrNX5mRnA+Aq2OPHjzfv3r3766WlpUvIDfl85pyG1LeU3Jd9LS0tT1x33XVrzp8/76UWAfKMBz2TAUfbFbgJbAuBfUdZWdkSuvFyZepxydwW0I2lo83NzQD9rwS69mZSxnUsUwFXXZHDhw/PmTJlytNksedknPpp3GCy6FV1dXX/OGvWrCpqpui6pHGr+zct0wBXLDZ1w/TSSy+V3n777b+j+R8/pYtHAC9DrwIO9xHD5c5XDF5fS0ya0MWo3+nwvrBx47nfLLlvKypj0DNmaDFTAEc7GW5zQ0PDD4YPH/4YuSMJmQMSExVpsPPxhjsMHt/FuLWkp8dwqb3dt2zhnD2vUKXsnwPytAc9EwBHG01YDh06NIuGtf5DjoyQGmHC4XMLwmwd/Caft2fnmXr3A3d8Zf9hqgXWHEtaQ57Of9pViz179mwrXeE/MnPmzF0S7sEDGuueJnPevCuusO76sPq6R2bPHm2l+sy0gKG0NZTp2jCG27R27drx8+fPf4lu1NwQ6wnKlf0TZcFF/bwe//Z9uxw/vvfuj89Tftr65ukIOI+QmE+ePPllGgJ8UfraIloDp5MBOFoB37zxQvdPvr5gP24SsW+OsfO0CenkoqhWe8GCBQWXLl36Hd1de1vCnTas9GtIXp5h5LgJ1re3H7z2dwsWjMTUYlwrpZXLki4WXIV73bp1E66//vqVNClK3mLvh1RkGcmy4GJr3B7/zkOVXXfd86PD5yg/bVyWdAAcbVBGSY4cOTKPbtqsJqs9QhRPpqNTIBWAo4U0l+1yw1nXd29duH8nrabFKEuqXRSG20yz/hZPnTp1rYQ7OpjTqTRNUhwxtsK69t3tcxZTuzDCAsOVUiOaSsBxbAhgoZl/d02YMOF1SttokSGDFSCabeVj819/v3LOXdQNCy04xynjLFUHVuGmu5K/HDNmzHN0ux2/eBmyQoEe84gRluc2V13zS+pOSiFPBeAK3PQQQj7NQ36CZgA+Kh9EyAqqgzpBQ4h5w4aZH6URlidwrmljSix5sgFX4F60aJGNXmzzYklJyf1BqsiVrFOgqNh0/5ubJr24aFEp3M+kQ57MCwAF7pEjR1rpBs4KeuD3W1l3NtOkQ6kaRQnXfZfL/+Y3bqz7Ed3f6KZyPIwYbpe4bEuWBVfgphbn19TUPCnhjsu5y6hKCgqM33pr4+QnwQAtSbPkyQCc4bY0NjYupXeQ3JNRZ0Y2Nm4KFBab7tlSdc1SqjBpF56JBhwuEI5hOXfu3N30rOS/xk0tWVFGKjB0mPlfN1bOuRtM0AI2EuomJxJwNBx/iix0QfkNupJ+mtIySAUMpSPyn16z5fPfICnYkicM8kQBznCbq6ur59Pt9+U0FAjYZZAK4J6+aczE/BffWn/1fJIjoXc8EwE44Ea95vXr18+gt0mtpjQmx8sgFVAVIEgKJkzJX/2fq66aQZmAHMzE3ZInCnATPRBcNG/evJU0t2SI2iuZkAoIChiNeUM+O6d45cLbxxVRNv7Cxx3weFeIHwwaar148eJTQ4cO/SmlZUiyAuk4Dh5Ogs5O3wsLPrfnQSoT9zHyeFpw/FhQn+Xo0aPflHCHO6Vym6hASYnpp29v+dw3wQ4tYChuhjdegDPc5jVr1kybPHnyM2IHZFoqMJAC48Zbn/nzi1dNo3Jx9cfjCbh54cKFRfS42Qrpdw90OuV2rQLwx6/9QvGKhQsVfxyQx8WKx6MS/EgUv5vmGTw5ZMgQeadSe/aSvJ5pPrgoj73D91/zr97zEOXFxR+P1YKrrsm+fftupNmBEm7xbMl01AoUlRjvWb1u9o20Y1xclVgBV6w3uSVFNN79JL3LLuoOyR2kAqICYKhisu3JBQvG8tBhTIzGsjNbb8vLL7/8C3oxz3SxoTItFRisAhaLcfqyP435Oe0f86jKYAFnuM2vvPLK5FGjRuHRJBmkAnFToLTM8tCfnpk5mSqMyVWJBXBcWFro6Zw/0Z+Vwrj1TFYkFSAFwNQX5w/5N0rGNCFrMICz9bZ8/PHH+MbkV+UZkQokQoGiQtPX/rb+M7f0Qg5WwV5UYTCAYx/zrbfeWkw3dJ6I6miysFQgSgXGV9ieWHjrqGLajV2VqGqIFnC23ua//OUv99PnriuiOposLBWIUgGLJa9iya8q8HA6Ax6VFY8WcJQ30Sewh9DrHn4WZVtlcanAoBQYXmr62fe+NwGzUnHdFxWz0RRm621ZtmzZT+hF9GWDaq3cSSoQpQImU17ZnfeO+gntFvWwYTSAo6wZN3XKy8vvjbKNsrhUICYFykZa7r1mwUjc/GFXJaL6IgWcrbf5+eef/wH53uUR1S4LSQXipIDZklf+m99N/AFVx4BH5ItHCjjKmehbObaxY8fiDpMMUoGkK0BW/OezZxdH9YasSABn621ZvXr1HWS9Jya9Z/KAUgFSID8/b+KjT02/g5IR++KRAK5Y73HjxuXT8oBUWiqQSgVGlVseoCnZfHdzQH4HKgDrjcX8xhtv/B1NqJqays7JY0sFLPl5U59bVbEQTNLCfOoKEwngGHs0013L78npsLo6yg1JUgAMjhlb8D0wSQvYBOS6YSDAsd30/e9/n54hHrpItxa5QSqQRAWKh5gWffWbY4bSIQF4WIbDbcQvA9vNS5cuvY2es8TVqwxSgZQrYDQabHffU34bNWTAIcNwgGObAjh9P+c70j1J+XmVDehVACyOLs//Dq0y4Loc621g59301FNPTSwsLPyiVFcqkE4K2ArzvvjPv52GIWv2w0P64uEAV6z34sWL76BfjF65dOqzbEsOKQAm5/9diWjFowZcGT2hx9G+nUO6ya5mkAL0WBsDzla8X+tDWWa+uDStWrVqBo19T+u3l8xIawUsplFp3b54NY7mik/703/MxNtpeTSlnxWHk64NKuCf/exn58uLS6086b8+3Pqg4WDNHw0O5yf9Gkuf9+sX6N3twXmaVWwMkUWv+Q7eLVShHk1mv310Kg9Vrt/h/PStQoN/PlVxhBYYa7AbVCwc4PQxzxHXS8ANhkZ7jaGq8W8Gh6ed9MuM4C2ebrD7Jhp6CIJsDr481/UGw4nnqY8MeFB3tYDjF6BY8KKiIjO9qeq6oNI5urL+1L8bOt2XM673PrPf4OjwZDXk/p6e68BqV1cXAx5kxUP54MgzrVixYpbJZCrNuLOagAZnItyQwWQ2GgppXlKeEec8OwON75V+/YErZlHv2A8P6qieBTddeeWVX5LuSZBWGbnCkDs7PQa/PyO7EL7RZI5HTCj+EhXaTwt7IKpfprXgintCBU3kf8+TgIfXNlO2AnJbicVAt7izLoBR2xDLPOoYW/CgP1eiBWf6TXRrPp/877lZp0YOd4ghhyUPNUKRydJYbaa5IyYU5l8+53BRP5hjxYprf9NYNz7zzDOfpl9GSSZ3Wra9vwIMORm9rArUn5Kbfzzt09QphV+xcyEt+Pjx42dK90SUKXvSDDksedYEwnrYyIKZ1J89tOhacN5gpLnfU7Om87Ij/RRgyLPJiFlsZjALC84cK/1GBgdswLqJZg9OzabOcwdl3KcAIC8oNuMtrn2ZGZpCHyxW0xXU/H4Xmgw49xLrRgJ8Sob2VTY7CgVUyLNgnLwXcIXfXgkUpvv54KWlpeaCgoKKKHSSRTNYAQXyIrPB1eXVzOLIrE5ZrcaK0lKbuaXFCbDZYCsuCfcEmcbf/va34ym2cqaMs18BhjyTZ/3TmKB17ncngV1Y8X6AM/XGq65SPsaZ/WdV9jBIAUBuLSSfnPFgIjIoHj2pCFO7xR6oFpy7YRw5cqS8wAw69bmz0gc5cMiwQE0uKrH0G0kRfXDFQbfZbBMyrGuyuXFUQIGc3p/Q7fSRT65O6YjjERJXVX6hCewqHPNRsILAFjyP3j2Iz0XIkMMKBCA3ZdwQosloBLsqyziFogXHBiNNkS3OhrFRdE6GwSsAyPPJkrvJkmeCHVeYNeUBcPbBlc7ziko9AY6XjMsgFVDmkysXnqAjAwIN54NdlWWkRQuOLuRJwCGDDKyA0ZRnsNrM5JOn/zi5yZzHgHPz1VEUZCjkS8BVbWSiVwGGXCEkjVUxGlXA1b85bMHVDAl4Gp/BFDaNIXe7vGk7uEL+iOheK0zzKAqkkxY8hQBlwqEBeX4BJmilZ2uNRuX6UeGYW8gWnNelD85KyDikAgy5uzv9xslNRvUiU217PwtOW7TQq4VlQioABRTIrTQzNc1MeU9eD9gNacHVPzper9dBH3mVj6tJlsMqwJB7yJKnyzg5vTXAITRaYZrHwdV8n8/Xpa7IhFQgjAKAnOZhp83gSo/foGVXGQfHD1D9EcKCh+mT3CQVCFKAIfe6yZKrFAUVSdqK39cjsqtwLfrgaEiPtOBJOx9ZcyBAbs7H3JUUd8mnWPCgn5l4QalskICn+CRl6OEVyMld8brp9VkpMuU9fj+7KCrkIS14v9fpZqjostnJVYDuJJIlJ6RSYMrBrK9HAVyFG71nC45MZZE+eHKhyLajMeQ+jz/phtzvy4MPrrIMbUULzoDbs0102Z/kKgDITRZj0g253+8Huwy40mm24KoCTqezBeZezglXJZGJQSgAyA0EOSx5MgLcfp+7p0V7LLbgTL2/tbX1tLaQXJcKDEYBtuSD2Xcw+zg6u8EuflHMs+KiYAVByTx58mSdvMgMCCL/j12BpEFO9Laed9Yxx70t72ELjnUF8DfffFMC3quOjOKjAEOeyMEVfOyqevtFLeD9RlH8GzZs6Ozu7r5E81FGxqd7shapAI1mwCen5zz93sT45H5Pz6UTey52ktb9XBTor1jv3o1+u91+RropkEWGeCoAyI0EebyHV8Bqt8t7htoKuEMCjn4AcqWAw+E4jQwZpALxVkCBnG7tK5DDZ4nT4nb5wawIuNJ09sFFC+7DSIq04PE+tbI+VoAhj5dPjiHCbrsXgNNTGMEWXBwHVyFvamqq48bIWCqQCAUAeQ8ZcJoBGHP1+KF0NHvALCw4c6zUG8qC+995551DNOkq9iPH3HRZQTYrgJuJmKQVa6CvOffUfNhwiOoRXRSFXwYcx0AGCvjoI7CX2tvba6WbAllkSKQCsUKuXGB2eWsr37twidopuihKs7WAM+Te5ubmSgl4Ik+trJsVYMgHMz0E/ndXm6eS6qI3E+m7KHwsxYLTiq+2tna3BJxlkXGiFQDceDe5EiMd6UIPzLU0OneD2d4FDKtBz4L7li9fXkmzs4IKq3vJhFQgAQow5NFUTYT696w5DwsuuieK/416QgGu/BLWrFnTSn54jbTi0cgty8aqgAo5rj0HWHB7vtvhqTnyUVMrlWYLDrhDAo62YQOsNvwZLw0X7pGAkxIyJFWBgHsy8CHhf9tb3Pj4q8IrxWBXhRs1iBYc6ww4fg1e8sN3ScAhiwzJVoAhJ1dc/2YnNaq5oWsXRQBcdFHU5oo3epAJwBly79NPP73npptuchUVFRWoe+RgwnXRZmh3YBQqu4OuMQuyiX0a6GQHCuhs1D1GX7VBKVhp7APgtfvSS4dcm1bUsQVn6x105FCAo6Diz3z00UedjY2NW6dMmXKL0ag19kHtiHil9nyj4b2dVYaOLvEVFhHvnpKCXs9XDUa3m44dpF1QW7TiB23UWdHdR+cw8DlDBlCgE/S30A5h9tOpLubsaG/r6JWnJ+gNrtbmrRdO7sYMQva/GXK1nVrAsQGaoCDMvufgwYP/N2nSpLgB/sJb6w0tHWhTZgV3t4teidBNjYbkkEhPem2/uCyjpt1fu127f7LLa4+vbZ+2Pdry2vWB9tdu1+6vPV6gvN/vMzTUHv8/Ku2hBaz2gxs1hTLLqIEB995///07aPpss661QS1RhEyEG93LtxbQKxH4+7gQPdLAZRGLad5fzOMyYoxyXEZMi2XEtFhGTItlxLRYBmltQFkE3ofTSmYE/w20v3Y7H4dj7fECrorP42mu2rZhB23VvcDEnuEAh9n30MxC5/nz59+LF+A4aKaGAOT5wbxpO6M9X9jOedqyqVjntujxo21Tostrjxfheldnx3tuu91JxWHBQ15goqpQgCMfFpytuGfTpk3v0PtSkJ/zAZBbLL2QMyRiDIUYCjEtlkllWmyTmNZrk1hGTMervF49YfL99JbNpvrat6k5onsCXvsFPcDhpuBXofjhS5cuPdzW1nZKWvGAfhaGvJ+cMiPRCoBBj8t16tCOTUfoWAw4WAWz/UKoi0wUQmGGHJW4aUx8TVlZ2YP0DR9sz/kAyBG8HsgjQ7IUAOD2jvY1dDwMa0F8hjsk4HoWHO2FyVet+MqVK9+hh5Hl2YQyvQGQm/PJXQkXcKcCge9YcFrJDPFftOVDVJFWWdH2Z4Dy9PpjT92R/e9QH8EiPAwwGtI9oXxdHxzb8ItgP9z90ksvNZ45c+ZdOf8K0vQFC42sKJAzwNoYRfmkiWltOV4Xy4hp3p5psdgHMa3XD7GMmKbyALKrs/3dMx8faKQkLDgAB6MhrTflRww4fi3uV1999QWy4rq/FlSYi0GB3GLJxa4ntc9+r9d/5tjHL9BB2T2JCXA0PsiKP/nkk7Vnz559X1rx/ueVIQ9z8a+OJMsygYGmaHTAXVdnZ+f7x/bsqO0FfEC4cZbC+eDYDmutuimU7v7rX//63x6PR/dPAnbK1QDITcoQYq/fDSHwp5hjMR3I7b9d70+3Xj7XPdj6Yt1f266B6htou7a+3nW6c9lTf/Lwf9PuuJ0suidhPYqBAEdzUAEcecVN+cMf/nCMrPg2acUhTf9goYtOk5ncFT5RKII0B07rbedykcax1hfr/tp2DlTfQNu19dE6Rk4c9o5th3d+cIxW2T0Je3HJ1UQCOKy1Cjilu9etW/c8WXGuQ8YaBVTINflydXAK+H007+TUyedpb7begA9MDuhJRAo4W3H8erqXLFlS3dDQsEtacVJDJ0jIdYSJMhvW29nVuevAtvXVtCsDztY7LoCjSWzF4dgDctfatWufkSMqkEY/AHIzja5gLjMHTgcm9AfyOQ9lOM3bo43FOsS0Xj1iGaQHCtG2D/XxPmKa2yPmiWne3uP3+Zvqjj9D21y0gD0wGJH1pnIDXmSiDAIAD7rYfOihhw7Su8TflnNUFH10/zPTRafJbFZOMp9ojrETp/mEinm6lYbZEG192vLcDr042vZp69fuH247psR2tDS/XbVl/UHaj613RKMnLFEkLgqXZSuuXGzigA8//PDT9GByK/6MyKCvAEOuX0Ju0SoAprzd3a3VO9Y/TdsY7oh9b64vWsDZF8cBXZs3b75UWVn5Z3nByXLqxwHI5c0gfYWCt8B6Nzde+HPj6dN4VhDuCZiL2Pfm2qIBHPuIVlyB/Pbbb3+THmurlhecLKl+DH9cHULkYuyfI45kwX68j5jmfcU8Mc3b4x2LxxDTescRy4hpoTwezXN1dVVvfeuVN6kIwx219Ub1gwGcrbhysYkGrFq16jGXy+WTrgokDR8UyE00iZNPKIojHWngsnr7D7Q90uNEWi7a4w1QHgz5vF5f3ZEDj1ETADdfXEZtvdGFaAHHPgw4flGKFX/00UePHDt2bDW9kRbbZRhAAYYcWMslWAMDPcxgb768mm7qYL631nqDvajCYADHARhytuLOBx544NnLly83SlclMv0BuZFGV2ToU6CH4HY7nI37Nr/3LOXicTSt9e4rHGFqsICzL66Oi+/du7f5tddee5g+f+KRrkpk6pvplr4CObsbORwDKBpy9pyuqX74YkN9M60y3FGNe2uVHyzgqIchV604DRvuI9CfpRtA2uPIdR0FFMjlU1L0pQcvjZo0PHvggw37SCqt9QZrgwqxAg5XRbXilHbdcsstK+vr63fIG0CRnw+GPFf9cbpbaejqaNuxZfXylWCod+G7lmAsJYDjDOLgWPiCE788x4MPPvgb8sc/kf44qRFhCECeez45/O5up/OTqo3v/oakwuvOwBBcADDFfFFycCEWC85HRCMwfIJfHBrnpBtATa+//vqvyB/3Sn+cFIkw4Ja+URxCzHKfHGaZ/tJ76SmdX9FrIJpoVeGHYrDEw4KUHHyIB+BoJxrDrorSSHrVRNWePXuegz8uIY/8BCmQG7P/zQVgAn735aYLz+3fsq6KFBLhjunCUlQ7HoCjPoY8yFVZtGjRcpqQtdGtvLhSPKxMh1MgYMkBefZ65TRJ0NDZ1rpxy2vLl1NHta4JDCaYijnEC3A0hCHnURU02rF48eJ/oYvOvRLy6M6ViVwVoymepye64yeytI8sd1dH+94tb6z4FzDSu/CwYNzgRh/iqSAAF/1xNNhBX4nouPPOOx+iByRq5KQsSB55YMizyRXHiEm3vbNm99o3HnJ2dHSQGgBchBsMxcV6Q+lEOHtonNjAPILcW1dXt3P+/PkLPth/eGgePqclQ0QK4L3synvBs2BKMmYIuhz2c/s2rf1ZY33tJyRAFy0MONzbuFpvCJwIwFEvQhDo5It3d3Z27naYCm6mGXWFPNE9UFT+H04B/vhAgPHM9Mv9fvpglNPZfGjnpntOHzl0lvqrhTuulpv1TBTgogVX0wcOHOiix7j2Dx899is0HJYvIefTMHCc1/uFjUwckcL9EHphpv34gY/uq9nzUU0IuGG5AXjcQ6IAR0MZbI6VxjfV17UVlQw5OqR0xEKah2GRkEd+TlXIIW2GGHK86tjtcjnqjx74pwNb38dteNFyx228W0/FRAIuHhOQq6BfqD1+0WIp2Dds1KgFNCRmkz65KFX4tAp5Bvjk8LndDkfriQN7fn5g6waMdTPcGPNOONxQMtmAq6DTnasWn8e1s7R8wvVkyEv4xKFRMoRXQDUIiiGnz16n4b8eGud2d9kbqnd+cC+9bu0o9chOCwBnuHEzJyF+N9WrhmQBjgOyBVchv9xwobOro3XbqPGTrjVZLGV8MaW2TiZ0FQhATlKyqrolk78B49z0HsGT+zatua/uyMHT1AKAnXS40fNkAo7jITDgStx++aKz+cLZD8onTZ1NryEeA59c+uUBoQb6X4UcBdPAJ8dwJt5CRTMDD+xY88YvGs+caqCWad2SpFhu1i7ZgAfBTY1Q1umdz+7zp45uHXfFjCn0AstJeUYJOZ+ggWLVXUmxKcesQHqWkm6/t2zd+saKJW0Xmy5T2xlu3MjBDMGkwg3tkg04jonQD3S60vbVVh/cPmbyFGu+rXA2+eRkyGGWZBhIAdYpYMST75H30Bg3fcqlp62p4eWNry7/N3rVWju1WYQbF5RJhxu6pQpwHJsh5wsN+nit13+quupA4ZChNSVDh881mkw0wiKtOcQaKEAnCJrMoMwIpJESj6Orlaa8Ltv2v6++QeeQLybFO5QpgRtapBJwHJ8hR6yCfuFUzQX6U7d5RPn4K8kvL5cuC6QaOKiQJ8EfJ2/bgItJR3vbwb1b1v3iaOX2Q9RCWG223HBJxKHAZP/+FMHSBXDAzYsCPV18dp06eGBjecVkk7Ww6DPSZVHO14D/sbsyYMEYCuDOpNfj7mlpOL9yw6oXf996sQFvn4LFZri1k6diOFpsu6YacLSef9lsxVXQ6c+d79ShqoN05/NI0TByWYxwWWjAQPrmYc96nz7xNeWBhxRofNvpbDl7rPpX2/73f97qdUkYboxx88Vkwm6/h+28ZmM6AI4miZCLoCvp86eON9ibWzYPHVk+mlyWKXBZMC7WdyI1vZKrvdqwrLEJArAxSoJvgna0XNpctXntkqOVHx6mWtliA3DtDRwYqpQHkJJOAe3BXFr88PCmSist+OKqjZZCpK+55bYvVEy78pcFRcUT8eRL3zAZbZWhnwIAM5bAw3/dXfaz9SeO/vve99fsovrggjDUPATI011xwPj8smJpeO++6QY4mhUwzwHI8Zg5vrQKyBl0W0FJSfENt/39D0pHj73LYrVayXWR1pwE0guBGYjRMaeOkNBDtc1NDSs/XLP6FVdnJ0ZIYKmxAGz2tTFKwnO5ozsQ7ZjIkC4uiraPLBJiLLAK7NP5vG63t/bQvkMOR8emoWWjx9Fr0CZKt0UrYd96nyvHtkM/xhwudkfsra0fHtz6/j/v2/zuB6S5OPwH6y3CjfMT25+KvubGNZWOFlzsINrHLgtbc7bocFsUyz7vq9+6oXzK9PsKCgsraE6L4rb0nVSxutxOByx5aA3Yz/aRn+1yOOobT598dte6N7dTabbUbLUx9Mc3btjosEEKXXkKc9MdcJYGkDPo8M0BOfvnCuRms7lg7uJv31Q+ruJOa1HxVLzcEv65BJ0lDB0z2LiAJD/7VNOF+pcr1/5tE72uhMHmmMe1RV87La222NNMARxtZmsO0GHNGXQAzrAjtn5x0Te/VD556p0FxSVX4Y1RmIorQSdlhKCAjfFsL1lse+eRptOnXv7ovbd2UBGAjAVgcwyLDbD5jiTATlurTW1TQyYBzo1Gm3HtwKDDmrNFVwCndSW+5uavXzNu8oy7CocMuRpfVgi8hiF3hxcDLgpm/GFilMfg6OjYf+H08ZU0MrKXNGOwxVh0RwA1X0hSMjNCJgIOZdFuLKLbAovOQ4si6PlXz7959tipM28rKhm2wGzNL8TrGHLlopShxoQo3Fr3drsdXZ1tWxtO1byzf9v7uL0OiEWokYa15kX0szPCalPb1ZCpgHMHGHIGnV0XBp0tu2Lli4eNKPrc/C8vKC0v/0phybA5NI5uogldivuSbePpGAkB3JifjU+CODrbqlqamjYc2LZxq73tMm7QsHUG0Aw5Q83DfrDaGeOOMBRinOmAc1+0oPONInZfxFix9BOmXjVq+py5Nw8rG/kVmp47lV+XFvDVM8+NUS11H9R0S91xqq350oYTVZXvnzt15CKJxQAz3GKMbbDWbLEzGmwRDE5nQ8ygIwbksOhs1QE54NbG5qu+cMO0cZOmXW8bMvTqgsLiWQS7FW95hc+ersAHA+1XXmRJlrqbXqxz2NnRvv/CmZMfHtm1/ST1F9YY8ALmUDG2s8WGC5IVYFM/lJAtFpz7wzH6xbADdF4AuBZ4xaJTvrKtoLjYOuvaL80qGzPx8wVDSq622Yo/ZTSbLLhbqjwzqsxPp9JKCMiXqBGaAMQ4UMD1xU0Y8jsMmM2HJ9ZpLprH6bQfc3V07m9uPLvv8J4dh112O1wOhhpA88KA8za21myxldqpfFaFbAWcT5IIOvx00VcXrTtDznkcm4aWlRXOuGbeZ4aXjfm0xVYwMT/fOtFsLRhnwsMYyvCjUQG/76KVD62NtVIHoNWWUiAmoHFRCJAVX5pi+oKdk+zzBbe7+6zH6Trb2tz48fG9O6vbm5txVxGQsiXmmMFGzHmIGWjRvw7dGCqc6UGreqb3J1z70VcAzjFbddGVYbC1sVhW+aFUzPzMqNETJ1YUDyubaLUVVeRbrRNMFnOZyWguzAvAj9fToZ6AmwPLjxUKCk1EMltoir30OJOjhyD2+b0On8fb7O7uPtft7Kq3tzWf/eTs2fr6mmr40AylaH1FeEOlxbLsfnCstCeb/2PNs7mP2r6hz+ICeNmycwwwGWqGnde5jBgjjUWsN4/cHfPQ0lFF9PidzVpUWFRgK8KMSIPL2eXo7qLRuvZWZ3vLxS5yKwAmuwgcM8yI2eqKMdIMNEOMdbEM78t1ckzFciPkIuDimQ0CkjYwqByL8IuAY7u4jcujPqS5XkoGpbEuBhE4TrN1ZcC1wDK0DDWvc3mOuT6OxePmTDrXARdPNGvBcCLWgsvrDDEgRzmOOT9UXTgW5wM6BIZPuw5IkQeIxZjh5RjbxDTWsSBwHFjL0f9Z8BztfthuszaIwy0i1NpyOIBYj3hABlCMkdYuDLs2X1xHvViXQaMAi6/Jlqs6CrBeDDKKhcsTt+tUGQQmQ8rwYp9weXp1yvxeBfjkSEFiU0Cro3YdtWvzGFzxyNo87bpYVqYjUOD/AZrbm7Ts1rpFAAAAAElFTkSuQmCC',red:'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALgAAAC4CAYAAABQMybHAAAk/0lEQVR4Ae2dCZxUxZ3Hq8/pnhkGmOEQuQS5VCTxWHEDBlyNkciakMMkxujGuOvHO24IKCae0UQlKwmyroocoqtozGpA4oFiVAQU5IaRcchwDsPczNF39/5/b+bfVL/p7ume6bur+DyqXt31r2//5//q1XvPIJRLpgQMMVYeiDGfyhanBGKdgDirzYvsLLtIPguB0/lc7+vh5vNIvr68Oo8ige6EH6Vo3iSxjODrw1rcm2++2f+MM84YV1hYONRkMhUZjcY+OAKBgOZTXDGVLTYYDH0gNYpvIa/V5/O1+v3+FopvgY+D4tra29uP7N27d98VV1zRiOy6g06DcRyGr1wYCfCEhUnK2yiWiQYvSQG+8ac//all3rx5o/v16ze+oKBgnNlsxjGWQB5LgA5MhrToh1BL0Fd4vV4c+1wu176mpqYvHnnkkf0rVqzwUJt+OuQfALrBmh/hvHc8mfkuCBlmDei5c+fab7nllguLi4svttlsXyeYzyPgrJkgKPpBuQn4LU6n88PW1tZ1ixYt2vjoo486qG964PMe9nwGnKE2EhgG0tDWBx988HzS0BdbLJbpdEymeFsmAB1DH5wej2cTHR+Qhl937733biYN76ZyAFyGPoaqcitLvgEeAvX1119ve/jhhy8nLX211Wq9lKYWtnIuuFa3272WtPv/3nPPPW8tWbLESYPKS9jzAXCGGj60tfGLL764YPDgwT+x2+3fp/MyOnLZ1Tscjj/X1NS8OH78+E9poNDoslbPaTMmlwFnsDWoN23adNq4ceN+TBeIP6FVjbG5THSksdEKTQVdqL64b9++lyZPnlzVCboMe6SiWRufi4DLYJsqKyunkraeCxOELhJzcbxxw0cXqQGYMKTVHz399NM/pgp8dOQk6Lk04RiLpq3JNx04cOAbAwYMmEurH1PoXLkIEqDVmPV1dXWPjhw58l3KwqAz7BFKZU90LgAeBHvYsGHmjRs3/mtpaekcMkPOy55pSH9PyXzZ0tDQ8NiFF1646vDhw17qESDPetCzGXD0XYObwLYQ2FeVlZXNoRsvZ6Yfl+ztAd1Y2lNfXw/QXyHQ9TeTsm5g2Qp40BTZtWvX+aNHj15AGvv8rJN+BneYNPrm/fv3/2LixImbqZuy6ZLBve7atWwDXNPYNAzT0qVLS2fNmvUA7f+4gS4eAbxynRIwHN8ozDseFQZHTW9l4m/3BBa/8nnDfT97vKqBKmPQs2ZpMVsARz8ZbvPRo0ev6d+//yNkjiRlD0hvqUh3ecsrpwtD2+GEdcMfELUNbWLewNniBaqU7XNAnvGgZwPg6KMJx44dOybSstYf1coISSOKsy4tiJLa8ySPX6wvrxN3TLpX7KJaoM1xZDTkmfynPaixJ02aVEBX+PdOmDBhg4K754D2tqTFKKacPVhsOPEnce+kSQK/IjMdYChjFWWmdozhNq1evXrYtGnTltKNmq/3doLypXyyNLgsP49XfPhOpfjZzCcEbKGMtc0zEXBeITFXVFR8g5YAn1O2toxW9+FUAI5ewDY/UC9+PvrXAjeJ2DbH2nnGuEwyUYJae/r06bba2toH6O7a6wrujGGlS0eMBjFw1ADxetMT4oHpZ2lbi3GtlFEmS6Zo8CDca9asGT516tTltClK3WLvglRsEanS4HJvXF6x/v0vxHXfWigOUXzGmCyZADj6oK2S7N69ewrdtFlJWnuALDwVjk8C6QAcPSSTpa6iTvxwwm/EejrNiFWWdJsoDLeZdv3NHDNmzGoFd3wwZ1JuMlkGjBsoVlf9TsykfmGFBYorrUo0nYCjbQjAQjv/rhs+fPjLFLbToVwWS4Boto/sL14++ri4joZhoQNznDbO0tVwEG66K/nLIUOGPEW32/GLVy43JGAeUiKeqvsv8UsaTlohTwfgGtz0EIKV9iE/RjsAH1IPIuQG1SGjCAhDWaF4qHmBeGzwYIG3EaRFk6cacA3uGTNm2OnFNs/16dPn1hChqJOck0CJTdxaeY94bsZkzfxMOeSpvADQ4B44cGAB3cBZRg/8fi/nZjNDBpSuVZRow3d4xGsjHxT/VlsrXJSPlxGjFUlIWqo0uAY39dhaXl4+X8GdkLnLqkrsFvE90uTzwQAdKdPkqQCc4bZUV1fPpXeQ3JhVM6M6mzAJ9LGJG+v/IOZShSm78Ew24DCB0Ibl0KFD19Ozkr9JmLRURVkpgdIi8Zvqx8X1YIIOsJFUMzmZgKPj+FNkoQvK79CqyQIKK6ckIE4pEQsqHxbfIVGwJk8a5MkCnOE2b9++fRrdfl9CS4GAXTklATwiYRpVJp7bfb+YRuJI6h3PZAAOuFGv+a233hpPb5NaSeHkPGJCFSuXnRIgSGwTBomVb/2nGE8jAORgJuGaPFmAm+iB4KIpU6Ysp70lJdk5BarXyZaA0ShKLh4tls+6QBRRW/gLn3DAE10hfjDoaMHx48ef6Nu37w0UVi7FEsjEdfBoImh2iMX97hR3Up6Er5EnUoPjx4L6LHv27PmugjvalKo0WQJ97eKGLx8U3wU7dIChhCneRAHOcJtXrVo1dtSoUQvlAaiwkkB3EqAngxauuk2MpXwJtccTCbj5kksuKaLHzZYpu7u76VTpegnAHr9svFh2yQTNHgfkCdHiiagEPxLN7qbnKOeXlJSoO5X62UvxebbZ4LJ4yB5/muzx2RSXEHu8txo8aJps2bLlYtodqOCWZ0uF45YA7T68cfu94mIqmBBTpbeAa9qbzJIiWu+eTy9Wj3tAqoCSgCwBIETr4/OnjwsuHfaK0d4UZu1tef7552+nF/OMkzuqwkoCPZWA1SzGvXS9uI3K93pVpaeAM9zmF154YdSgQYPwaJJySgIJk8DgvmL2C/8hRlGFvTJVegM4Liwt9HTO78k0KUzYyFRFSgIkATJVCq88S/yOgr3akNUTwFl7W3bu3IlvTH5LzYiSQDIk0KdAXEEbsi7vhBysgr24XE8ARxnzlVdeWUw3dB6LqzWVWUkgTgmMHSgeu3Ky9oFeNlXiqiFewFl7m5988slb6XPXI+NqTWVWEohTAhaTGPnMLIGH0xnwuLR4vIAjv4k+gV1Cr3u4Kc6+quxKAj2SwIA+4qbrpwjsSsV1X1zMxpOZtbdl3rx5P6cX0Zf1qLeqkJJAnBIwmUTZ/TPFz6lY3MuG8QCOvGbc1DnllFNujrOPKruSQK8kQG/Kuple0Yx942yqxFRfrICz9jY/88wz15DtfUpMtatMSgIJkoDZJE5Z9mNxDVXHgMdki8cKOPKZ6Fs59lNPPRV3mJRTEki5BIb0FbdNOj2+N2TFAjhrb8vKlSuvIu09IuUjUw0qCZAErBYx4i/XiasoGLMtHgvgmvYeOnSolY47lKSVBNIpgWH9xR0lJcG7m93y210GaG8c5ldfffVfaEPVmHQOTrWtJEAbsca8f7O4BEzSwXxGFEwsgGPt0Ux3LX+ktsNGlKNKSJEEsJ121CDxIzBJB9gE5BFdd4Aj3XT11VfTM8R9Z0SsRSUoCaRQAn0LxIyrvyb6UpMAPCrD0RLxy0C6ee7cud+m5yzV50VIGMqlXwL0/Kb9nsvEt6kn3S4ZRgMcaRrg9P2cHyjzJP0Tq3rQIQGYKSP6iR/QGQMekeNICdDeOExPPPHEiMLCwq91VK3+VxLIDAkUWcXXnrhaYMma7XDw2sVFA1zT3jNnzryKtHekfF0qVBFKAqmQABFpnDUxRIvHDbi2ekKPo30/FR1WbSgJxCuBwcVBwFmLd6kinGbGLwHxphUrVoynte+xXUqpiIyWQKBoWEb3L1GdozXxsS/9u/Z2Wl5N6aLFYaTrXRDwr371q9PUxaVePJl/3nzef4uaN28S7hNHunQ2EOgSRa/r1rkuEXild1enr6unecJVHktd9OlwaOJp1LPddEApg92QotEANw8YMGCqApwktmen8K9cIURTI8kv810BdXGI72JR73LR9+ND5jvzOx9nD80u11QhVj1DxRjwkBr0gOMXoGnwoqIiM72p6sKQ3Hl64nv0fhGoPZ5Vo8ff7P5+v2jw+Eil5S7kfQKBC8FqW1sbAx6ixRGpd4gzLVu2bKLJZCrVJ+bjebbBzXNkoTsipfRQo0HTWRybWz7BWvqHkYMn0qjYDg8ZoB5w1uCmM8888yJlnoTIKitPGHIj3R3hyc0lHwCPLbRdRB4A56EF5yoc4Igzkf09RQEelFNWBwB5f3okJhfnE2MqNZumgFk6wC4gDzoZcKbfRLfmrWR/Tw7mUoGsl0Ao5DzVueEXmUyThxcW8heUeVDanMmAIwLnxoULF55Nv4w+Wg71X85IgCE3AoEccjScPr8ZderZNCSNX3lo8ioKk28aNmzYhFz8cyYPPF/DHZAbRKPXmzNrK6B6qM0ygbxP6WCOtaUjWYNzgpH2fo/JVwDyYdxmUuH9zWZN3eXKePuYjGAWPDPH2tD0GhwZTLR7cIzS4Jp8cvY/QN6PIG/KAU0OVouMxtPBLh0MuTZ3rMFBPRzOjQT4aO1M/ZfTEmDIc8Emt5s0wDV+OydNY5oBR5ym2ktLS802m21kTs+sGlxQAoC8r4nMFZp9DQAGIct8m9EwstRuh0XCw9DGqAfceP/992MrGrYzKJcnEjgJOdjIUhcQBbcPHQx2wXRwIGyDM/XGs846S22PzdI57k23AXkJmbAnfNm5dwUAn1mkbe3+ohNwRAVYgwcBHzhwoLrA7A0pWVxWg5xe5Wo8qQCzZjQAuNRs7rKSwhocAwHsRrvdPhwnyuWnBAB5H9LkLZomzy4ZFJmNYFfjmHuu1+AGevdgMScqPz8loEGuafLsGr/ZYAC7bI3A1x6751EgwkhbZIvVGjiLJH99QF5Mmrw1SzQ5mKVFcAAOpa3BjdnjkyD1BDheMq6ckoDQNDntQsQSYjY4ghzsBllGWLbBMQaDAhxiUI4lYCLNWEzmiqbJM/zBIKvByIBz9zUNzica+QpwFofyWQIMObGe0c4kAgx4sKeswYMRCvCMnsO0dY4hb/P5M/YZT7NJ0+AsI41pXkVBJCKUicLiUX4XCQDyIhNWyYP6sEuedEZE0+DcLwU4S0L5YSXAkLdrmjxslrRFGmOxwal3bLakraOq4cyWACAv1DR5ZvWTVlHArmaJcM/YRAn+zfF6ve2cqHwlgUgSYMi7rDNTASYs1b7PH5DZ1Zjm/gXH4fP52oInKqAkEEUCgJz2YWeMRU6Xv3p2NZWO1c3gCqfS4FFmVCV1kQBD7qS3aKX7LXE+v1/W4BrXbKJwxwNKg7MolB+rBAC5jd69Ql5anS8goMGDyhqdkS8otQQFeFrnKGsb1zQ5Qa5p8jSNwm8ImihByMNq8EC6/9akSUCq2d5JAK+H0zR576rpUWkwSyuXETW4Zq9QzQFlg/dIvqpQpwQYche9vDvVb7X1BgRs8CDL6JKswbUEAry1s6/KUxLokQQAeQFtQUz1HU96FzrYZcC1vss2uBbhcDgaoO7VnvAeza0q1CmBDsiFcPlTIxJQ7aTXoetbYw3O1PsbGxv/oc+kzpUEeiKBk5q8J6XjL9Pk9YBd/KSYZ81EwQmcFllRUbFfXWR2CET933sJAHKrZq4k9w4nelrldOwnLwg3wqzBka4lvPbaawpwSEO5hEkgCHkS18kB72v1zXrAg+vgTL3/7bffbnG5XLVms3lgwkaoKsp7CQByC0nBo+nRxIvD7ffXrjve1EI1dzFR0FoQcGRobW2tUmYKxKJcIiWgQU6gJ1qRg9U2X6CK+gq4wwKOcQByLUN7e/s/EKGckkCiJQDI6fUOCd9x2O7zgVkZcK3rbIPLGtyHlRSlwRM9tao+loAMOcf1xge8TT4vAPfREaLB5XXwIOTHjh3b35sGVVklge4kAMhhqngTsC0E9dR6fGA2BG70IZwG97/xxhs7aNMVgFdOSSBpEsDNxA5zpXdWuY/MjVW1zTuoowA8BHIGHIMA0Ej00Udga5ubmyuVmQKxKJdMCQByE/ENfd6Tf6C2xR+ofPFITS31UzZRtG7rAWfIvfX19ZsU4MmcWlU3SyAIeQ8UOYCt93g3keelI0R7o34ZcJxrGpx8X2Vl5UYFOESiXCokAMgBI3lxHTDkqxyujVQU2ps1eLDLMuD4MbAG9y1ZsmSTn1wwpwooCSRZAgx5PM3Qg3L+JTX10OAMN3OsVRMOcO2XsGrVqkayw8uVFo9H3CpvbyXAkMNa6e7AQ6DNXl/5W8fqGyk7a/CIgKNvrMFhz3hpufBTBTjEolwqJQDIAXd3DrDWuj34+KvGK/lsgweLyhockQw4fg1essM3KMCDslKBFEqAIY+mxdGdynbPBvIAuGyiIElz8o0eRLB6xy/Bu2DBgk8vvfRSZ1FRkU3Lnaf/VRaVCM/xmpwffSRlBijCuUjxyBsxLUJCpMfbkB39AvD6/jn8fufjh46wBmftHdJCOMCRUbNnPvnkk5bq6uoPRo8efbmRnphOhGvbWiGO/c9fhaeuORHVpaQOt+8rwlmCb7uHyC6k7UgpUctEKBStTEijnSf6iZfzRGhCyxJvO3K96Q7T42mi2nnig21N5dhByPY3Qx7snh5wJEAmmgYn37Nt27a/nnbaaQkDfP+dTwp3dT3aySrn9HtEu9+r2YYQUCw2IgbIeRk0lOO4cOmIk12q88ttI8x9jdR/fX79eXfl9en68pHG7w34xW5nzV8pv4cOeQ08pIpwahltMuDeW2+99SPaPlsfTUuE1NjNSTbCjSEVGS2i0NihD2KFG+U4L3w5jDQ4OY7zyL6cRw7LeeSwnEcOy3nksJwHYb1DXjguw2EtMob/uiuvT+d22Ne3h3iw6Az46he37PyITiNeYKJsNMCh9j20s9Bx+PDhvyUKcDSarQ6Q2wnyaNf4nMa3nTFWjsuEcXNfYu1fsvP3RCbQwLU+598a3W4HBaHBw15gou5wgCMeGpy1uGft2rVv0OskEJ/3DpDbjCYNWoZE9iEghkIOy3nSGZb7JIcj9UnOI4cTlT9SPdHiAeZ2Z93r5MnmCaK7uEiA40eCXwWo9sydO3dXU1PTl0qLd8iPIe84U/+nUgJgsC3g+XJJ8+7d1C4DDlbBbBcX7iITmZCZIUclbloTX1VWVnYnfcMH6XnvADmcKwDZKpcqCUBN13jbVpHnpoPNE+a1SzciaXBkRF1BLb58+fI36GFkVKhcpwQ0Td7lS4xKPMmUgFv4PG+3HXmD2pC1d1jzBP2IBjh+FSgIM8W9dOnS6qqqqjfV/iuShuSwsmJTkEsSSV4Qa9+1Pseb77ZWVVMr0OBgE4yC1bAuVsA1M+XFF19cTFo84q8lbAt5EKkgT80kuwMB/7q2I4upNTZPegU4eh2ixefPn1958ODBd5QW7zqhDHm0q3+V1nMJkPIW9f72d149UVHZCXi3cGOWomlwpENbB80UCrteeeWVZz0eT8Q/CSiUr64DciwhnnQcjnbjArk5PV6fy3KL+va6q6+35fX1d1dfd+n6+vjcL/yBjx3Vz1J5Fx2yeRLVougOcPQHFeBiUzNTfvvb3+4lLf53pcUhmq4ON4IKDB2QY3Lg2JfDPHFyHMLxOq67p/X1try+v93V1126vj6cd9jezr+vaCrfS6dsnoDJqHCjbCyAQ1sHAaewa82aNc+QFkd55cJIQA85w5cKH91hiORwKtpOVhs+4nij89gzNB7W3oAPTHZrScQKOGtx/Hpcc+bM2X706NENSouTNCI4QG4lTZ5qx3AzbGif41Ldl0S0B+1d73dtWNy4ezvVx4Cz9k4I4Ogna3EY9oDcuXr16oVqRQWiiexOavKTiOEyC44vtzisRXbGcxznicfnsrHWp8/P5SL5+v531zd9/fry3aV7aOVkk+P4QsrnpAPsgcGYtDfli8lEQT4ADi3OgLtmz569jd4l/rraowLxRHY2TZPjY6kd/5CTJ1kOR0qPXHP4FK471vr0+blcJF/uc/gehMbq69eXj5buoy2xR31trz/duGMblWPtHdPqCfciFhOF87IW1y420eBdd921gB5MblR7VFhE4X2GPHyqig0ngY49J97GxU27FlA6wx2z7c11xgs42+Jo0Pnee+/Vbtq06U/qgpPFGdkH5FhdUS42CeD5qb2exj997qzFG6tgnoC5mG1vbiUewFFG1uIa5LNmzXqNHmvbri44WaSR/QLaZstLiJyLrXP4sRwox2XkMJeV4+Qwpyfal9uQw5HakfPIYTk/tHej37X9vuMbX6M8DHfc2hv19wRw1uLaxSY6sGLFikecTifegYg6lYsiAUCO1RWeUGRFOFbHeSOV7y491nZizRdve93lB0Nu+qD8O22HH6E+AG6+uIxbe2MM8QKOMgw4flGaFn/ooYd27927dyW9kRbpynUjgSDkeP+HOkJkEKBfwCF/68oXmvdgv7dee4O9uFxPAEcDDDlrcccdd9yxqK6urlqZKrHJH5BbeqRfYqs/G3NhzftEwF39ZNPORdR/PI6m195xD6ungLMtzsuGzs8++6z+pZdeuos+f0JLl8pUiWUmGHL82c73A69hcwm/5/3WQ3eVOxrw2gWGO651b73cewo46mHIg1qclg23EOiL6AaQvh11HkECgNysNDltdPKLfe6GRU837d5CotJr7x5rzN4CDlMlqMUp7Lz88suXHzhw4CN1AygC0WGi8x1y3NCp8To++lXN+uVgqPPgu5ZgLC2AY6rQOA6+4MQvr/3OO++8j+zxGmWPkzRidJq5YuiNvomxoQzLBru72e+pWdS46z7qWjsdYAgmAJhivijYM5cIiaITWD7BLw6dc9ANoGMvv/zy3WSP0zeGevzjo6ryy2H50EKQR7pNnmvx0MvugN/7vuPw3Vucx47RbGv8kA+WeFmwVxAkAnAQjM6wqaJ1kl41sfnTTz99Cva4gjz2OQLk+DBTrjswAbt7r6fhqacbdm6m8cpw9+rCUpZdIgBHfQx5iKkyY8aMJbQh6123Gz9I5WKVwElNnrurK16C+4i39d05NeuXkFz0pgkUZkL+9CcKcMwdQw6acaGATrfPnDnz13TR+ZmCnKQRh4OpYs5Rm9yjXVS2f3ZX3YZfk0g0TsjnZcGEwQ1xJxpw2R7XIKevRJy49tprZ9MDEuVqUxZEHrtjyHNpjRwrJvU+R/nDjZtn13scJ0gaAFyGGwwlRHtD0snY3obOyR00EOTe/fv3r582bdr0pmXv9MVXbpWLTQImklWHQGWRxlY203IB7kaf69CC5p037XDU4osCbXQw4DBvE6q9Mf5kAI564UJAJ1vc1dLSsnFUZctltKOuUEHeIaRY/gfkcBBotq6k+KnzJwKe+mUnym9c13roIA1FD3dCNTfkBZcswGV1Ewxv3bq1rcBk+Xycpd836c+vVUHeMQmx/M+yCgozlkIZkoe2mYrWgKf19ROVt/y55cty6pYebmhuAJ5wlyzA0VGeC/a1zm9z1jaVme17hluKL6HVAgtPXMJHloMVsqxCBJrh4+yA292+tv3Ifz7btAu34WW4E7beHUkMyQRcbhNzEpyXTY5jx+kJly2jrSXTSZPbeeLkAiocXgIsq6Aww2fLiFjY3Cf8nsbX2/bf9mzjLqx1M9xY80463BBCqgEPgr7VWdvQbvCuH28tnUo2eR+eOHRKuegSCMqKTHOY55l44F0mDQHn0eXNX9z8yomKPTSiVjoAOMONmzlJsbup3qBLFeBokJVOEPJyV2PLUW/738+2DbjAZjCV8cVUsHcqEFECgDwoyIi50pOAde46n6NiYePuW9a2HfgH9QJgpxxujD6VgKM9OJ4XzT/gOeHY7W5Yd65t0CS70TRE24nRuWrQkV39H0kCDHmmrK1gZnH7/ZjXsfWRhs23b3HUHKW+682SlGhullmqAQ+Bmzqhndd6He5PHDUfTC48ZXShwXyagpynp3ufzRUIMp0OuwLpWUq6/d72wd21G+fsdzfVUX8YbtzIwQ7BlMINeaQacLQJ1wX0Fr/b9zfnwQ/PKxhUUGKyTjIJo4Enr6OI+j+SBGQ5YcU81Qfgdga8gQpP0/O/qP/4d41eB77yK8ONC8qUww15pQtwtM2Q84VGwEsbyN9srdo60FRYPsRin2wxmOzYS4AHc5WLLoGT5kr0fIlMxY5AvL+k2e9ufK/98Lz7aje9SnPIF5PyHcq0wI2xphNwtM+Qww+CvsFRfaTa2/beuILSM+0G0ynKZIGounephJxNkhpf+7aFjTtvp5WSHdRDaG3W3DBJ5KVAzHHKXaYADrj50KCv8rS0rXFUvXtOwSBTX5P1K8pkiY0NNleSSRNu3sAkKfc0L7+j9sMH97ua8fYpaGyGW795KrbOJyFXugHHkHgu4DPkmjanP3e+Na1V2waa7buHmAsn0/ZRu7YXQ5ksUVE4adIlducKcU0mCW7euBvWOo7c/UDtxr90miQMN9a4+WIyabffow5el5gJgKNLMuQy6Fp4g+PY0cNksoyylgymz4SM7nioS9nmurkMOT0JeUh0j05ga/toiuj78OKQr/W9RY3b57x64stdVBlrbACuv4EDJZV2l2lXb+gPrivxw8OXVgvosNFhp6MQ4TvKzvnni+yn/rLUaB2BJ1/4TzKlKRdGArCVe+PY1m70uw9+7Kz+wx/rt26g+mCCMNS8BMjbXbW/vr1pM5FlMw1wjA19wgHI8SVmKx2AnEG39zfbiu8vu+CasdZ+19HHWAvM2ESqzBYSUXgHDRwv5rxC0ub3uCrI1n6w/tMXGr1OrJBAU+MA2GxrY5WE93LH2xQVTZ7LFBNFP0IWEnwc0Aps0/mcfq/3rbYDO+r9zrUjLMVDaePWCGW26EV48px//Kw5ovl4wxSbI/Ty+Y+fa97zq0WNO9aRzOXlP2hvGW7MT0aYJCdH3RHCWDPZoX9ssrA2Z40Os0XT7HMGnP/1C2yDbulrtI7E64nx7lae1EweXKr7Bq0cybGd7SI7m9a1D3zmqln0WN3nH1J+1tSstbH0xzduWOlErjhSgymKz3TAWQyAnEGHbQ7I2T7XIDebzba7+p1z6STbgGv7GwvGKNBZdNF9GWx6J/eXO5x1z/++aetaejMZg80+r2vLtnZGam15xNkCOPrM2hygQ5sz6ACcYYdf8Kuy8y86zz7g2jKj7SwFOkkkjJPBJlNv9xZH3fOP12/+iLICZBwAm31obIDNdyQBdsZqbepb0GUT4Nxp9BnXDgw6tDlrdA1wOtf828rO+afJtkHXDTLZz7XiNQxUBIXz1XwB1KASa9n0Rilx3Of4fJPz+PKF9Vs/o2gGW/ZlcwRQ84UkBbPDZSPgkKzGKfmy2QKNzkuLMujWG0rPmnRhwZBvDzbbp9sN5kLAni8XpQy1n9AG1I6At51edPnBRlf1G4sbduP2OiCWoUYY2poP2c7OCq1NfQ+6bAWcB4D+A3IGnU0XBp01u6blh5qLi27od8b0Mdb+3xxosp9PoJvwch3Anmvr6Vi/BtRegprA9tX6HJu/dDe+vbhp7wf0RincoGHtDKAZcoaal/2gtbPGHKG+dnHZDjgPSA86TBi20dmEYV/T9FMKTx00q3j0ZSOsfb5ZYrCO0UyYLNbssqbuhBpfS/jyoLvl7f9r3f/O+vajx0kmDDDDLftIg7ZmjZ3VYNM4NJcrgMvjgTbHuAA5NDprdQDO0Mu++Yf9xo2dXDB4Kmn1c/uZCibShWkBPi+CR+gy1ZSRgcbmJzxJQ0t8riafaxdp6883uWo+Xtm0r4LGDG0MeAFzOB/prLFhguQE2DQOzeUa4PK4WKsDdD4Ath54TaNTvJbWz2wr+FHfsRMnWErPG2iyndvfVHAGwW7BBSqA7/jX0QwLL1kXrYAYjg1f+LhMBNC4UCSoPfSmqL21Pufn5Z6GLS83V+xq8jphcjDUAJoPBpzTWFuzxu6ongrkkuM5yqUxyWPB+Bh0va0ua3eGnOPYNw21FRX+oHDcV06zlpxdQvtfCg2mEYVGy1CrMNpZw7Mvwy93AmG9oBlafT6GGPYzQGbfLfyOdr/nSHvAd5B28x2scp/Y+Wr7vu1HnG24qwhIWROzz2DD5zj4DLRsX0fqDmXPbqeXe3aPJnrvGXT2WavLpgyDrfflvNoP5eLiYYMmWctGDjEVj+hrtowsMliG01cayugppEK6k2qnbWCFlNGMxhh81vRsXkAbgywizUuvWWinW+QOT8DX7vL76tsCnkPNXs+Bal/rwR3u+gPrWg/DhmYoZe0rwxsuLOdl84P96BLLgdR8Apynq4O5DqWKMOAFtLIPwBlqhp3P9Xk14DvrCKmbzB3zSGtx0RBjob2fuaCoj8GKHZGiJeBub/K62qr97Y4D7tY2MisAZofyPukzzPBZ68o+wgw0Q4xzOQ+X1ddN2fLD5SPg8syGAEkJMqx6kGXA9WlcDvUhzPWiLTmMc9kxeIjjMGtXBlwPLEPLUPM552ef62NfbjdvwhC+ch0SYFkwkPD14PI5QwzokY99jg9XF1rheEAHx/DpzwEp4gCx7DO87CNNDuMcBxz7HWd5+j8LPE+HH3XYLBv40Q4Zan0+NCDXIzfIAMo+wvqDYdfHy+eoF+fK6STAwtdFq9MIEmB5McjIFi1OTo9QZQiYDCnDizLR4iLVqeI7JcCTowTSOwno5ag/R+36OAZXblkfpz+X86pwDBL4fwN/IZwMBwH5AAAAAElFTkSuQmCC',yellow:'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALcAAAC4CAYAAAChOH1KAAAlaElEQVR4Ae2dCZhUxbXHTy+zL8ywDDsSVhEVJQoCkoSIIr4kvohLxO2ZfC8an0mQrCQm+uJ7qHkv5hE/xSQaNokBogkxigaUuLDIpsiOMA4MOwyz7zPd7/yLOZfqnu7p7umeXut83+2qW7du3apTv3v63Lr31rWRkUhowOZViPc6Nutpbq/8WPVO8173sYtJ6kgDusI7yme2nYdTdIZQj0NH1vrgwYNtc+bMyb344otzCgsL87KysnLT0tKym5ub6+rr62vKy8urd+7cWfv444/XlJSUAGSB2VfoKw3HM9KBBqQzOsiSspsEXgmhCMTtDGT2TTfdNDwvL28kQzvC6XSOcDgcQ2w2Wzfenme323M5nuN2uwPql/O5OF+dy+Wq4X2rOV7Z2tpa3NLSsp9Pgv3V1dX7XnnllU/4RKnj7S5edNARl4WjRnQNBFS+njnJ4wKxHtp37NgxpG/fvlPY6l7CAI/kZTgv/YMBN1L64hPAzcAf5eUTXvax9d9x/PjxtZdcckkxH0OAF8gljNThE7acVIdbQLZzDyJuX7du3YChQ4dOycnJ+QJb5M+zFR4Qr73L1v4IW/h3amtr/3nw4MG1kyZNOsJ1FdglBOwpKakItwfQTz31VN4dd9wxnd2LL7J1/hzDPCxRSWDYD7BVf5fdmbeXLl26avbs2dXcFsCdkqCnCtwCNEL7gAEDnBs2bPh8QUHBnenp6TdyWi4vySY1TU1NKysqKl6cMGHCO0eOHGnhBuqQJ71FT3a4FczcqQgdu3btGtWvX787MzMzv8YWun+y0eyvPWzRjzY0NPzp2LFjL44ePXoP52vlRbfo/nZN6PRkhdvyoX/7299245GNe9iHvoMvBC9P6N6KQOX5gvRD9tGX8gjMovvuu6+SixRrjjCpJJngRluwAGz78uXLu0+dOvXbDPW3eL2QFyOeGihnyOevWbPm6VtvvfUsbwLcAnpSuCzJALcH1KtXr+4zduzYWbm5uf/OnZWMvrQnouGv1bD8ftu2bf937bXXnmgDPCkgT3S4lZXmDrGvX79+0EUXXTSbRz3u5fXM8Ps85Upo4FGWBbt3735q4sSJh7n1YskRJqQkKtyoN8B2vPzyyz2uueaaX2RnZ9/NN1bSE7IX4qjSfMOoqa6ubvFbb7318xkzZpRx1XDxKZY8jmoauCqJBjfqi8WB4bzNmzf/W/fu3R/j9R6Bm5o6OWyuErK1fMiKwuhfJ8VNZWUVtT/77MTHFrYNI8oIS8L444kEt7ggju3bt182bNiweXwHcXwnuy5pdwPYzrofMdiR8SaaW9wffFLc+N3RE/7xEStNrHhkCu/iXkgEuFFHBfb8+fMLb7vttkf4YvGb7II4u1g3CVm8vXEpORtfiGzdbbaWmpqW3724ou4/v/WDj8u5cMAd965KvMMt1tp56NCh24qKip7gmy99IttzyVWao3ERYekKcbnpxKmy5h/3HbVpGZcvdzzj1ooDnngUnHQOLI8++mhBZWXlC3369FlowI5tV9lt1KdPz7SFdaUTXnj00REF0kccxqWRjMdKWWBv2rTpUn7YfwnfWRwZ225NnKN3peXWtdDion0799TedfkXPvqY0+GLywWnni2m8Xiz3KgPLLbz6NGj3xgzZsw7BuyY8uH34E47jRxzUc47J/eN/wb6ixf0W1zxFC+VEWvtnDt3biE/ybagZ8+ez7CysngxEqcasNkoq6i785m6w1ctmDt3GB5xEMjjwiOIh0qgDjjJMG49hp9ae5Gt9fA47c+4r1a03BJvRbS43J/s3FN3R5ubIhebMR0Tj7XlFoudtm/fvmsuvfTSNQZsb2wSY91ptw0fMzrnrYObr7iGa5zGC9yUmBrPWMItYGOY7xZ+W/wvrIw8XowkqAa4Q/M+MzjjL0d2jL+FmxBzFyVWcOO4OLPT+AH6b/ELuAs5bp4LYSUkujDg6f37Ohee3ncVHjUWCx4TzmJxUAE7/eTJk4/06NHjKb7bGIt6JDpH8Vt/N9l7dnc8dfaTcY9wJWG0YjKSEm2oFNh80ZhRVlb2NL/D+KP47SFTs3A1UFiQ9qOakglPjx7dKyMWgEcTbgX2+PHjs3j6hCX8fMjXw1We2T/+NZCTY//65jeGLRk/vjuGdaNqwaN1NavA5salnz179jl+9evO+O+WxKxhrIYCA2mrtq71xdwLNt7P+Zp4kacLA+0W1vZoWG4BO4197McM2GH1V8LunJPtuLP84FWPcQOidpHZ1XCjfCxppaWl32Ef+6GE7R1T8bA1UJDveOjUnvHfAQ+8CBthl+uvgK6EGy6PAru4uPj23r17z/VXCZOeOhro1cs5t3T7uNu5xQJ4l7nGXQU3KqzGsfmF0+v79+8/P5oTR6YOKgnYUjfZ+vdLm7/vg7HXtwEOTroE8K6AWyy2kx9ZHTdkyJAlbY1IwJ4wVe4KDTAgacMHZy3Z9vbl47h83MkEhxEHvCvgRpnOefPmFfF49lLMU83rRowGPDRgs1POpRdlLZ33xJAi3iCAe+QJdyXSZ4sCmyuVwY+truA5RKaFW0Gzf2gaiNehQH+tqKt3vZkzaAOeRWnkRZ4m9Jc9pPRIWm6cKMrPPnz48HcN2CH1Q8pmzs6yTzux+8rvsgJkiDBiBjdScAvY8LOv4hd58UyBEaOBoDRQ1DPtEfa/r+LMEX2SMFJwoxzHE0880Yv97AXsZ6OSRowGgtIAeLl0VNaCJx4d0ot3wL9/RLiMxF8AKgKY4WcvY3dkOseNxEgDieZz62qqq29dlTNo422cFhH/O9wzBCeHgptfOHjAgK13lYmHqoHsLMf0ozvHPcD7yehJWMY3HLgtsBcvXnwB34F8ONTGmPxGA94a6FuU9vDiZy6+gNPDBjxcuNXoyA033PA4+01mLmzvnjLrIWuA36jP/dcb8h7nHcMePeks3JbV3rp167X8sVF8NMmI0UBENJCXa7/xo3fGXsuFhWW9Ows39nNed911uRdeeOEvI9IiU4jRgKaBi0Zk/vK663rDGxDAta3BRTsDt2W1n3/++Vk8jfDQ4A5lchkNBK+BNKdt6OJfD5rFewjcIV9chgq3BTZ/UGkY36wxz2cH318mZ4ga4Js7Dy1fMHpYZwHvDNzqIpI/1fEkX0Sab8+E2GEme/AasNltmdO/kP8k79Gpi8tQ4Las9rvvvjuBXxe7LvhqmpxGA53TQE6O7bp1r4+RW/PgNWj3JFS4ldXmW+zfY6vdudqavYwGQtAAOONb89/nXUK23sHCbVnt119//TKelmFqCPUzWY0GwtJAbq596j9eueQyLiSki8tQ4IbVdl555ZWzOTRmO6zuMjuHpgGbbfxlOeAOcIPDoPgLBm7LavM3H0fl5+d/KbSKmdxGA+FrID/P/qWXXxw1iksK2noHC7ey2pMnT8bQXzD7hN8aU4LRgIcGbPYpV3UDf0Fb70CgitV2LFq0aAhb7RkexzMrRgNR1ADPezJj0fyLhvAhYWzBbofuSbBwO6dOnfogX7miUCNGAzHRAA+cOP5lSt6DfPCgXJNg4HawO5LDs0XdFJMWmYMaDWgaKChw3DR5ck/MqBDwwrIjuMUlwTQN0/lzHvjuoBGjgZhqwG6ngmfnDsTbXgGtd0dwY5u6kBwwYMCt5qZNTPvUHLxNA+BwYP/0W3lVLiz9MuxvA6w2FsecOXN68fPaX2wr2wRGAzHXQF6O44tzZlsvEwur7erVEdzY5rz77rtv5s9S49anEaOBuNCA3W5L+/rt3W/mynTomnQEt3JJ+JvrmA3IiNFAXGmgX1FawC+m+YJbzLxjyZIlI/mNdtzTN2I0EFcayMqyXfbS7y4cyZWSURNw6yH+4MYOjokTJ95iLiQ99BX3K271Tx331Qy7guBy0vg8WG/FKoft4IbP4i3IpPztwsLCz3tvNOvxrYEW23iqKPs9VxKfnUlc4fncPSrvtUpYb2lygU+/frc33JZLcs899xSwS3KpxxFSdMVWv4dsle+Qzd0c9xqAGevm/AJVVBSTy+ViCDwhQQN8JLVLc1P7/bz39VXOuTye+/rK5zvNcz+U1ZG4XO5Lb5teWbBs1QHMUCXsWoV4w42yYLUd99133yQ2/dBVSoutbhc5997MmkscS4hOK2hxU1mlb7h9daj3f7r3uq99Yp3GJ67jnqktk5atopVcF3Dr0UnecKNNCu5+/fpNNv42m4PyN8jWdDTW/Rjy8TF22yPLTWeriFyWLQu5mLjeAbD26eaezMHfeQG3SLJaiwRdsK7g5icAJ+kbUjVuc+OziYkpPD0Cdc/nDk0EM9xJFedn2ybyrvizEnatknS4oQIsjlmzZvXMzs6+0MplIgmrAQHcwT0tHZxMYXaGe9QDX03vCW55kaap/vIFt33mzJlXt2VUmcxPYmsAgBfmsWkD4Nz9SbbYvnq1G7yC5Q7hRgYH35W82vjbiQ20d+11wL23JfI6OO1TSIBbXBMArsTbciu4eU4Sc1dSNJREoQKcZ9+DBY+U4F8AIv8GEleJUfrJyiTw2g5uGS0R2hXcPL79mSjVyxwmyhpwwkXJdVNFTeRGUQRwNEXiEkajedkZBF4FblUN/nHr5zDi9p/85Cd92NSzh2YkWTUAwAtgwcWkJXhD+UTKm3VLWh9uhmJYmiNwo5lY7Pw8yXDZaMLk1YAArkZRuOdhaRN5mXSxDdyCZ2FZrUgPKrj55s1QczEpKknuEIB347cRYcGFiEQMUf++3V1DubcEbtVx7Sw3v3UzLLm71LRO14AADqudyJKbaQO3ArdqjQ434naeB3CIsdyJ3M2h110Aj+QoSui16Pwe4DUnm4ZwCYphDhXcGC2Rcxahg0dKkMlIimkAgOdnu6mqzvdTg/Gujqx0G7jFiInFM0iHIME+atSo9MzMzAEqxfyknAbOAc4gMBWJdnGZke4eMOozmengmBcFuA63bdq0aYV4jDDletU02NIAAM/LOge3lZgYEceUMa2FXFWAbcEtKzaen4RHP42kugYE8M6Mg8uFqVh+6FLSfOlVtnU2v+yHcFCRG/xaPOt3KG29evUyN2989UAKpgHwXH4evKZee0A6SD0IsMgucQl9FaFvk7iEgfLr27vnucGvwK38E9lu42FAY7lFGyYkZcGz2YkFLgkg+TkOsdyqtjJaomjnZ7gN3AnQidGsosOhWXDrHZdo1iC4YwHgzEzfbglKsBm4g1NkquUSwGsb4neYEG5MTjp5WG6P0RIeBswxN3BSDd3g2gvAc/irox35wsGV1DW5UK/0DDemNlZeCI5ijQkikT91jY1GjAZ8asAX4AI7Qj3us4BOJOpl6nFfRaU77AI3Ntv00RK+gDBw+1KaSTuvgXOAu6mOZwqRuUcEOuSSuITn9+x8TC9L4hLqpTqdynIjCdbbc+6t1tbWFiQaMRroSAMAnF/MpXoA3lHGKG9rddk8+BWfG9WwNTU11fqaoSjKdTSHSwANAPCsjDYTGQf1xb9IYzPVclWU1UaVdLipoaEBG40YDQSlAR1wuYrzDlGQRZuPUmWb937+1r3L0/fnuQM9+NXhdhu4fWjfJHWoAQtwocwrtyQHC6vX7u1WvctDBkmrb7YBbstTErhVQl1dHb82asRoIDQNAHA8j+frIi+0ksLLzRe5wq/iGaMlQrq7oqLCw6yHdyizdypp4JwFd1MDzz4noyjRbD9OrMpaD8vtlqFAAO4uLy8X8qNZL3OsJNEAf6uGLfg5wKPdJMBdXuMCv4plHF/cElWX06dPV5vREqUK89NJDZwDPPouCv4tTpVTtV5tgVvRvnv37hoDt64eE++MBgB4Bs+hDGvqvaA8pIlIXPIhXdIkjx7KNskvIa4q9xyyA24Py40ViHvlypXVPNbNMzobMRoITwMW4F7FeMOJzZKmxwVa71DPg7hIczNVvba+SdwSJFszTgntbh4xKTHWW1RmwnA0AMDTYcG5kK5ccAXLIyUlfBiLY9Rb3BLEscFVW1tbghUjRgOR0IAFuOaKRKJc7zJqG2wlnObiRTwRBbfQjg0uHg781Fhu1oSRiGkAgKfxuJy3ixGpdVS0qtb9KQeKYQ4V02K5BXA3j5gUI7MRo4FIakAAj2SZelmnKuggr1scYxvg1hNaecTkoLHcUI2RSGtAAI+UxZZyUM89h1wwyviamcWzWG5sVyZ94cKFn/L3CxE3YjQQcQ0AcCfPjAMwIyVMq2vhasenXJ5iWMoVuIV2165duxp4xOSYZDCh0UCkNaADLtY3nLC+yXZs14EmfsPTuqAEz9ZoiQU3p7XW1NQY1wTaMdJlGsC7urDg4Qpc6JoGN/xtuCSw3MKyB9xi0l0nTpzYbPzucNVu9g+kAQE8lDFwlOnh0TDKJ8tsmznZ4pfjHpYb+yABGVq3bNmywbjdUImRrtYAAHeE4IML2HJC4OvIW/e3buB66pZbVbudz41MP/3pT3fziwvmNnxX96wpX2kAgHd2ZtnGFqr68QuO3eCWF59uCQ5iWe7q6uqms2fPbjWuCdRiJBoaEMBDORb4LKugLYwrvmGuw62KEcuNFQtujrccO3bsAwO30pH5iZIGBPBgR05QrWNltk0c4K33gHADcGRq2bBhw3rjd7MmjERVAwAccAcj8LfX7Wxdz3kFbvCLRYleDOIYnOEX9tWca93OnDmznmd+7aFypuiP48jjhMVIdDUQjNdQVecuK7iheSLXrJIXPO7KM6ko46wAl9fMOE0Rj0Q1YsJhC8O9mT+Vfb09Ub8EhFaFKc2taVReYRmDMEszu4eigY4Ad7HZPnyKMAQoVtvjYhLH0eHGusCNHVr27du3euDAgRGF21axm2zH1pLN3Yzjxb3YG89Qel02PzIM3bUXf9jjtSdf4i8def3s4veFW39l+StHHaODjaGW5zd/R8fw08pQy2ppddOuva2rGVPFKrdN4EYzlehuCRKwDuB5Pk/KGzlyZM/169e/z5/vi8gXFwC28x9fZrDh1ieOVNW7cBcscSqcAjWtbXRXf/l/K6/ed6rpDDcXr5fh9jtAtzrKl+XGRtDXzJa77siRI6tHjBhxUyRcE9vhV8lWe4SLTizJR3XZLtRiwMlIzDWAx/qOn6HV+04Rf1iQ4AKAV3Brgc1x6/Y74iIw71hwFjTxqMlKniBTtoUV2lyJS0c+f+GLJzc3EgcaYI+EthyilVwVAAVOhVmP2unj3LJBLLfyZe6///5NVVVVRzty7mXHZA8BeC6PJcF3M0tsdADbzF94OPq9P5OMb4NTsdweCPqDWwCHyW8uKSl5zcB9Tm95fDWSg8FSIzHRAC48SyvoNT64YpNDARvMeogvuJEBZh474axo5ikf/trM784bOacBATzYO2kmH1t5/quLxNLCCK/aSX8Fl7yI1Qav7cQf3DgLLL/7ySefLC4rK9turPd5/QHwbOODn1dIFGKw2uW1tP3/3qZiPpzub7ez2qhOMHDj7GjasWPHSy1qSBG7GYEGlAVnwI3/HR0dtLK53XWCXmLVC9hgE0Y4JLg5v9oBO8L8N82cOfM1nvah1FhvqOa85BoLfl4ZXRiD1a6sp9L7lil/G3CDS79goyr+LDe24Wyw/G5+9axh+/btf4jUsCAOkCwigEfCpzRl+PbN8ZDUzhP0h5oadbNG97d9Wm2wFQhuAVxZ729+85t/raysPGmsd/vTEoBn8dRhRiKvAWW1G+jk7OXqQlKstt9REqlBR3AjD8w+CgHcjUePHq3duXPnImO9WRs+xFhw31Y33H8jWO29J2jR0Qr1QSc8+QcewSX49CuB4IblRgHqopLDxm9/+9sr+E2dMmO9fesUY+DGgvvWTWdSYbVrmqjsxytpBfjjRS4mO/S3caxAcCOPWG8FOD9vUrVnz54XjfWGanyLAG5GUcIfRcFzJPtP0ov7jhPe6RWwA1pt9EwwcIv1Vn4379M4Z86cl9h6VxrrDRX6FgW4GQf3rZwgU2G1qxup8ud/V8N/YrXBYUCrjUMEAzfyifVWvvfGjRvLN2/e/LS5awnV+Bfc5MnCOHiE7s6lWjktTN22Unp6awmVs5aD9rWlR4KF29t6N8yYMWMFT96z07xnKar0HQLwTDOK4ls5HaTCHTlVTTvvWqR8bTyrLaMkQVltFB0s3MjrYb358yL1y5Ytm8vzm7iMewL1+BcB3PjgwfnguMPC85G4Xt5Gc3nShnrWbMhWG70RCtztrPfDDz/88f79+/9sLi79gy1bBHBZN6F/DeA2+4Ez9OdfvE4fc65OWW2UHgrcyC/WG38ROJsavv/97/+Gb8ufNdYb6ulY4H/DRUk13zmU9kKDlY109sd/pd9wFGDLhWRQIyTYXyRUuGG9cRAMC+Kg9e+9914ZX2D+mt0UXjUSSAMAPMP75b5AO6XQ9iama0sJ/XrjQSrjZotLAt7AHfgLWkKFGwUL4GrkhNfr+eJyJd+93Gbck+D0LoCHYtFSIS/uRJ6oom23v6BeIROwwVnIYKMnOgs33BPrriXHG+bNm/cIv45WY0ZPoNbAIoAHznk+By5IIXJhKnGV2JYuaXpe2R4o1PfR4/720/PocX/5O0rH6EhlA9U88096hPPp7gg4A28hWW0cqzNwYz/xvS3r/dxzzx1cvXr1L3j0hOfZCLkeKDPlBP43XBSAEcwCBQlEelz21dP0uGwPFOr76HF/++l59Li//P7S20ZHaO0++sXv31cfbvK22uAtZOks3DgQDijWG2da/V133fXm3r17l5ubO1BPcALA01PcB29mp4OnaVj+jcX0JmsNYMsIiVjt4JTplSscuGGeAbhYb1So7pZbbvnV8ePH9xr/20vTHaxaFpxNWyr41nob4WefrKG9dy6kX4EfXsQlAVfgq9NuQAS+SsKHPy829rt5gquWDydMmPCVjIyMdMzaKWI/8S5hMdJeA+r7MNyN6GwR0Zy/v/NIp+O4ckw9LsfR0/S4bA81xAx1VQ1U+8s19K1/7qVjXGYtL7Dc8oCUpg0cMTQJx3LjSDi4t3tSN3/+fPjfjxn/O7TOyGAXJQ2f0ODdsEAkPLfWtb9yLH/HD7Q9lNrhsqyBnY639tJjv39X+dlitQXssKw26hIu3ChDABf3BGdeHfvfq/jR2BVm/BsqCl4E8OD3SMycGM/ec4JW3LuYVnELADa4wb2TsN0RLkNJJOBGQTjLMBbpAfj06dOfLC4uft8ADhUFLwAcF5m6b5pMcVxAlpyl9298jp5krXiDDY7AU9gSKbhREVhwffSkjt+3rLn55pt/WFpa+rEZQQmtrwA3XJRkE4B9pJw+/trz9MPKOjVhvLc7Ao4iIpGGW/xv/L2o0ZMDBw5U8HyDD/HjscUteEDXSNAaEMCTxWrjgaiT1VT84Ev00IFTVMGKELDBiwz7RQzurrYNqqKHDh1q4tvzGyZfmHVNbsUHufz5byNBasDRZn7kvpiuOokj9LXgEJJHj/vKK/kk7Ex+7KOLlIUQdyBP19CJOa/Q/W/soSOchJERwB1RP5vLs6Qr4PZ55vHFZb2r1b1lXNHRa/muXKYB3OqDgBFfgOvg+CtAz6PHuyo/jqEvOA7WYbF5GrSKX71NDyzaQAc4SYb88O+O67SI+dlcliVdATcKB+CyyMFsH+w6WsnTAO+8pD9dx4CnGcBFNYFDAVwfBw+8V+xzAGyeKar+D+vpu798k7ZzjWCtxR3Rh/0iXtmugluvqECO0MbPD5zpmUs7RvamKQx4Rgp/S0rXUVBxAVwpkk1ivPviAPtsPVUv2Uizfvaq+jgToBarDbBhsdGcLpFowo0GqIas3kOnud0fjBlAk/nWc450Wpe0MMkKFV3BB9ddgHiLtzC27GOfmvcWPTj3DfqIuwFQY4ErolvshIab22KdnWiIasyGYqrkZwreu/ICmsivYBVIpyGzkY41IP92cpHZce7ob23icY/jVXToZ3+jB55fR59wDfCNSN1iR3xkxFcro2G55bhyhlqAf3yEanccp7WfG05j+XszRQAcf7VGAmsgHgHHyQawedqzXQ8up/949WNrVEQHO2J3IANpKZpwS10EbhV+eoYaV++lt68dRaPyM2mAAVzUFDgUwJEz1v436oBb6p+W0cZbX6CHNn1KpzkJUMPP1h+GYo/U+ifnaNdJtOHWwZZGus/UUMufPqS114+mfvkZNNxpLHjQPS6Ax9JFwRh2Hdtjnqzy9WnP0k9Ky9QNmpiCDQVGG27pNB1yxF31jeT63Xu07ooLqLx3N7oy3UFO6TjZyYS+NaAPqUbbgquhvkZq5FGwX13/ND3L/YgPnsLH9jXch76OmsQKbjRQQd0WWrAv30r7bXbaOLIPXZHppG7GTQmOBQ/AeZeuHj1B7/HEOXSikg4/vZZmzV5Ba/mwsNbiX8uoiNygQR9HVWIJtzQUjYaLYrkp6w5QOfvhb04aSn3YDx9m3BRRVcehAN7VFCk3hAfz9p6kN29fQD9Y+REd4poJ2GKx9TuPXV0ln4qJNdxotPeiQD9dTc3sprw39gI60zufxsFNkb9cny0xiUoDMtrUFTTBr1e30huo8e399D/TfkPPcj9V8oF1/1qeFRGLHbOeiTXcaLj0g1hvPXSv2EqfuG20bngRXc43fAod/H8rHRgzrcX5gS39tOlKjEI4IcDGmzPHKql43ts0i7/g+y6rQe44yoiIgC19GFNNwTWLF0FdcLJh4cf1Cd/p5Q9SqyW7ex7lLL2X7vjsILq3WyZ/vIBzWZ3ImYy01wDch3AFUOMZbJ5TpH7rYVrAs64uPVvtYan1N2hgrbGIwQr38GHtHw+WW28AlCKLnP0IW+ubyLX0A9pxqIzWXNib+vLk7oPlYtNArqvwfDwcvYgLUsu2eP9pemfOSvrBwyvpHe4HuCAyGgKwvZ/siwuwoYV4styojwieYsaCGT14dj1lxfl7YZYlz3j2drr6Xy6m2T3zqD+PqpAZNmTt+BGAiiVYgcWHC8L3H46+toOeenAZvc/7wuUAzAI01vVnRCLwP8ElRlDiFW40EXUTwAVyAG4tA3tR3oI76J4xA+mOvHTKgKtiIIfq2kswcANquCDVTdTIj0YsvXcpLSo9rcatYZ31RaDmU8Aa5Wp/0BinxDPcUA3qhwXuEwCHLw5LbgGO+MyraNCDk+nuEb1pOrsr6TyyYiBnxXiLP8ABNW6dswvStP8UrXrmPVr8x410mPfXgUYcUGOID1CLbx3CfwLvFUWJd7hFFagnrLhALq4KLjoF9IyvXkb9Zk+lmSOK6Cv8XfZMfl7cQC4a9BECatyIqW6gBob6b0+toT/+5SM1OQ5cDgEbcd0FAdRwQeIWaq6bkkSBG5VFXQVyWHFxVQC4QK7iU4ZTr4e/RLeN7kdfzcugXAU57xnOBRYfIykE1htv8yioG6lm1zH6y3/9nZat/UQ96CQgA2yJ+3JB4h5sdFYiwS1wCeDe/jjAFosOa57+2c9Q4X9/mW4Z3Zdm8J3OQszJJ3c7Uwl0AI0Fkw80sFPBU5iV7zpOL//0VVqx9VP1pTAALEAjrltq8asTwlpz3S1JRLil8gI5XBUs8MdlfFwgV8AX5lHW4zfSxCsG0vUDu9NEfnY8Xax5Ml+Awu0QK13bRE2lZ2n9llJ6g4f11pdXW4+h6hYacfjUcus8YVwQrnM7SWS40RjUXyAXn1wgB+ACucTTxw2mbj+cRlNH9aZp/QroYobcpmZ3QkFcUiJbdLHQ8Bnw0gC7Hu5jFbRzDz8Dwi/nrtlUom6VwzLLIhYa6zrUsNJiqRPCBeH6tpNEh1sa5AtyfXQFcAN6gVydAHdNoAF3j6PpQ3rQlIIcGoxRFoCubg5x5niHXYcZz3wAaIx6VNRSSXEZrV28iVYt2UBHuCkCrkCNdT0O10OsdMJDzW1Rkixw6+0R0MVd0V0WuQgV0MXKO798CRV9bRxdMbQHje3TjS7n0Za+cF0wdq7DjgPFwroDZIgCmkPAjDHpttGO4/zo6YcHy2jbnzbRlld30CnOAmB1qAVoPR1Ay4IjyMLRxJdkg1vvEbQNroq4KwI7ABeovUNsQz7nzHHU78YxdMWQnjSWn0q8LDuNemIObVyQAnbrwpQzQ3Tg9fi5rYF/BV7klLgijX9wIQiYEeKtcn7r5czJKvqo+AxtW7mdtvxxkxq+E+urwytwSyh5BGgu0XI/AlcywXIkM9zSFWLJBXSEFsQcB+BYF+glriBvS7dPGU6FUy+iwcOKaFBRPg3qlkUD89NpIFv4fmzdnQAez1OLK6POLK91bBPLq0IGFxd8AjHSsN5mlVt4/PlYVROV8qQ2paeq6DDPr3d4zW4q4WE7fAsdYAJWAVbiANk7DpiRJjAjVIflMGkFfZAqgrbqC+AF6Ahl8QU20mS7vo86WXIzyfm1K6jfZQNpQEEm5fP0w1lZaZTNw47Z7L9ns4XPZl8+i61+NsOdwQA3svWtY9+4ni1xHfvJdTw8V1fPS2Mz1Vc0UNVHpXTkT1voWE2DB5AAFFCK1RVgBWR9Xc8j+wjMEnJRyS2pBLfekzrkiCtQOdThFaB9wS0nhYRShne5so5jIy4CwCACmncolhWQCpwIJS7wAmyJ+8rrXS5nTx3RFZ46rfZsqQCohwK7Hgr4HaXpZUgcR9PjWBfo9LikIRSQBWZ93V8a0vUyJI5jpKRA6UY8NSAgeoeAGmmBQu/9UDrSvAXwQQRCPRRQA4X6PhI/V6r59al0oxZPDQisSPUVlzQ9lLx6iLi3AEiIHgqk3qHk886rCjA/7TWADjESugZ0vUncO5RSJV3W9VBAlTRZ9w6xXdIkrwkDaKAjxQfY1WwOoIFQdGvADaDMzmz+f6SMYEX4z7hMAAAAAElFTkSuQmCC'};return{FaviconsByHue,};});'use strict';Polymer({is:'tr-ui-b-info-bar-group',ready:function(){this.messages_=[];},clearMessages:function(){this.messages_=[];this.updateContents_();},addMessage:function(text,opt_buttons){opt_buttons=opt_buttons||[];for(var i=0;i<opt_buttons.length;i++){if(opt_buttons[i].buttonText===undefined){throw new Error('buttonText must be provided');}
+if(opt_buttons[i].onClick===undefined){throw new Error('onClick must be provided');}}
+this.messages_.push({text:text,buttons:opt_buttons||[]});this.updateContents_();},updateContents_:function(){Polymer.dom(this.$.messages).textContent='';this.messages_.forEach(function(message){var bar=document.createElement('tr-ui-b-info-bar');bar.message=message.text;bar.visible=true;message.buttons.forEach(function(button){bar.addButton(button.buttonText,button.onClick);},this);Polymer.dom(this.$.messages).appendChild(bar);},this);}});'use strict';Polymer({is:'tr-ui-b-toolbar-button'});'use strict';tr.exportTo('tr.ui',function(){var Task=tr.b.Task;function FindController(brushingStateController){this.brushingStateController_=brushingStateController;this.filterHits_=[];this.currentHitIndex_=-1;this.activePromise_=Promise.resolve();this.activeTask_=undefined;}
+FindController.prototype={__proto__:Object.prototype,get model(){return this.brushingStateController_.model;},get brushingStateController(){return this.brushingStateController_;},enqueueOperation_:function(operation){var task;if(operation instanceof tr.b.Task){task=operation;}else{task=new tr.b.Task(operation,this);}
+if(this.activeTask_){this.activeTask_=this.activeTask_.enqueue(task);}else{this.activeTask_=task;this.activePromise_=Task.RunWhenIdle(this.activeTask_);this.activePromise_.then(function(){this.activePromise_=undefined;this.activeTask_=undefined;}.bind(this));}},startFiltering:function(filterText){var sc=this.brushingStateController_;if(!sc)return;this.enqueueOperation_(function(){this.filterHits_=[];this.currentHitIndex_=-1;}.bind(this));var stateFromString;try{stateFromString=sc.uiStateFromString(filterText);}catch(e){this.enqueueOperation_(function(){var overlay=new tr.ui.b.Overlay();Polymer.dom(overlay).textContent=e.message;overlay.title='UI State Navigation Error';overlay.visible=true;});return this.activePromise_;}
 if(stateFromString!==undefined){this.enqueueOperation_(sc.navToPosition.bind(this,stateFromString,true));}else{if(filterText.length===0){this.enqueueOperation_(sc.findTextCleared.bind(sc));}else{var filter=new tr.c.FullTextFilter(filterText);var filterHitSet=new tr.model.EventSet();this.enqueueOperation_(sc.addAllEventsMatchingFilterToSelectionAsTask(filter,filterHitSet));this.enqueueOperation_(function(){this.filterHits_=filterHitSet.toArray();sc.findTextChangedTo(filterHitSet);}.bind(this));}}
-return this.activePromise_;},get filterHits(){return this.filterHits_;},get currentHitIndex(){return this.currentHitIndex_;},find_:function(dir){var firstHit=this.currentHitIndex_===-1;if(firstHit&&dir<0)
-this.currentHitIndex_=0;var N=this.filterHits.length;this.currentHitIndex_=(this.currentHitIndex_+dir+N)%N;if(!this.brushingStateController_)
-return;this.brushingStateController_.findFocusChangedTo(new tr.model.EventSet(this.filterHits[this.currentHitIndex]));},findNext:function(){this.find_(1);},findPrevious:function(){this.find_(-1);}};return{FindController:FindController};});'use strict';tr.exportTo('tr.ui.b',function(){function TimingTool(viewport,targetElement){this.viewport_=viewport;this.onMouseMove_=this.onMouseMove_.bind(this);this.onDblClick_=this.onDblClick_.bind(this);this.targetElement_=targetElement;this.isMovingLeftEdge_=false;};TimingTool.prototype={onEnterTiming:function(e){this.targetElement_.addEventListener('mousemove',this.onMouseMove_);this.targetElement_.addEventListener('dblclick',this.onDblClick_);},onBeginTiming:function(e){if(!this.isTouchPointInsideTrackBounds_(e.clientX,e.clientY))
-return;var pt=this.getSnappedToEventPosition_(e);this.mouseDownAt_(pt.x,pt.y);this.updateSnapIndicators_(pt);},updateSnapIndicators_:function(pt){if(!pt.snapped)
-return;var ir=this.viewport_.interestRange;if(ir.min===pt.x)
-ir.leftSnapIndicator=new tr.ui.SnapIndicator(pt.y,pt.height);if(ir.max===pt.x)
-ir.rightSnapIndicator=new tr.ui.SnapIndicator(pt.y,pt.height);},onUpdateTiming:function(e){var pt=this.getSnappedToEventPosition_(e);this.mouseMoveAt_(pt.x,pt.y,true);this.updateSnapIndicators_(pt);},onEndTiming:function(e){this.mouseUp_();},onExitTiming:function(e){this.targetElement_.removeEventListener('mousemove',this.onMouseMove_);this.targetElement_.removeEventListener('dblclick',this.onDblClick_);},onMouseMove_:function(e){if(e.button)
-return;var worldX=this.getWorldXFromEvent_(e);this.mouseMoveAt_(worldX,e.clientY,false);},onDblClick_:function(e){console.error('not implemented');},isTouchPointInsideTrackBounds_:function(clientX,clientY){if(!this.viewport_||!this.viewport_.modelTrackContainer||!this.viewport_.modelTrackContainer.canvas)
-return false;var canvas=this.viewport_.modelTrackContainer.canvas;var canvasRect=canvas.getBoundingClientRect();if(clientX>=canvasRect.left&&clientX<=canvasRect.right&&clientY>=canvasRect.top&&clientY<=canvasRect.bottom)
-return true;return false;},mouseDownAt_:function(worldX,y){var ir=this.viewport_.interestRange;var dt=this.viewport_.currentDisplayTransform;var pixelRatio=window.devicePixelRatio||1;var nearnessThresholdWorld=dt.xViewVectorToWorld(6*pixelRatio);if(ir.isEmpty){ir.setMinAndMax(worldX,worldX);ir.rightSelected=true;this.isMovingLeftEdge_=false;return;}
+return this.activePromise_;},get filterHits(){return this.filterHits_;},get currentHitIndex(){return this.currentHitIndex_;},find_:function(dir){var firstHit=this.currentHitIndex_===-1;if(firstHit&&dir<0){this.currentHitIndex_=0;}
+var N=this.filterHits.length;this.currentHitIndex_=(this.currentHitIndex_+dir+N)%N;if(!this.brushingStateController_)return;this.brushingStateController_.findFocusChangedTo(new tr.model.EventSet(this.filterHits[this.currentHitIndex]));},findNext:function(){this.find_(1);},findPrevious:function(){this.find_(-1);}};return{FindController,};});'use strict';tr.exportTo('tr.ui.b',function(){function TimingTool(viewport,targetElement){this.viewport_=viewport;this.onMouseMove_=this.onMouseMove_.bind(this);this.onDblClick_=this.onDblClick_.bind(this);this.targetElement_=targetElement;this.isMovingLeftEdge_=false;}
+TimingTool.prototype={onEnterTiming:function(e){this.targetElement_.addEventListener('mousemove',this.onMouseMove_);this.targetElement_.addEventListener('dblclick',this.onDblClick_);},onBeginTiming:function(e){if(!this.isTouchPointInsideTrackBounds_(e.clientX,e.clientY)){return;}
+var pt=this.getSnappedToEventPosition_(e);this.mouseDownAt_(pt.x,pt.y);this.updateSnapIndicators_(pt);},updateSnapIndicators_:function(pt){if(!pt.snapped)return;var ir=this.viewport_.interestRange;if(ir.min===pt.x){ir.leftSnapIndicator=new tr.ui.SnapIndicator(pt.y,pt.height);}
+if(ir.max===pt.x){ir.rightSnapIndicator=new tr.ui.SnapIndicator(pt.y,pt.height);}},onUpdateTiming:function(e){var pt=this.getSnappedToEventPosition_(e);this.mouseMoveAt_(pt.x,pt.y,true);this.updateSnapIndicators_(pt);},onEndTiming:function(e){this.mouseUp_();},onExitTiming:function(e){this.targetElement_.removeEventListener('mousemove',this.onMouseMove_);this.targetElement_.removeEventListener('dblclick',this.onDblClick_);},onMouseMove_:function(e){if(e.button)return;var worldX=this.getWorldXFromEvent_(e);this.mouseMoveAt_(worldX,e.clientY,false);},onDblClick_:function(e){},isTouchPointInsideTrackBounds_:function(clientX,clientY){if(!this.viewport_||!this.viewport_.modelTrackContainer||!this.viewport_.modelTrackContainer.canvas){return false;}
+var canvas=this.viewport_.modelTrackContainer.canvas;var canvasRect=canvas.getBoundingClientRect();if(clientX>=canvasRect.left&&clientX<=canvasRect.right&&clientY>=canvasRect.top&&clientY<=canvasRect.bottom){return true;}
+return false;},mouseDownAt_:function(worldX,y){var ir=this.viewport_.interestRange;var dt=this.viewport_.currentDisplayTransform;var pixelRatio=window.devicePixelRatio||1;var nearnessThresholdWorld=dt.xViewVectorToWorld(6*pixelRatio);if(ir.isEmpty){ir.setMinAndMax(worldX,worldX);ir.rightSelected=true;this.isMovingLeftEdge_=false;return;}
 if(Math.abs(worldX-ir.min)<nearnessThresholdWorld){ir.leftSelected=true;ir.min=worldX;this.isMovingLeftEdge_=true;return;}
 if(Math.abs(worldX-ir.max)<nearnessThresholdWorld){ir.rightSelected=true;ir.max=worldX;this.isMovingLeftEdge_=false;return;}
 ir.setMinAndMax(worldX,worldX);ir.rightSelected=true;this.isMovingLeftEdge_=false;},mouseMoveAt_:function(worldX,y,mouseDown){var ir=this.viewport_.interestRange;if(mouseDown){this.updateMovingEdge_(worldX);return;}
 var ir=this.viewport_.interestRange;var dt=this.viewport_.currentDisplayTransform;var pixelRatio=window.devicePixelRatio||1;var nearnessThresholdWorld=dt.xViewVectorToWorld(6*pixelRatio);if(Math.abs(worldX-ir.min)<nearnessThresholdWorld){ir.leftSelected=true;ir.rightSelected=false;return;}
 if(Math.abs(worldX-ir.max)<nearnessThresholdWorld){ir.leftSelected=false;ir.rightSelected=true;return;}
-ir.leftSelected=false;ir.rightSelected=false;return;},updateMovingEdge_:function(newWorldX){var ir=this.viewport_.interestRange;var a=ir.min;var b=ir.max;if(this.isMovingLeftEdge_)
-a=newWorldX;else
-b=newWorldX;if(a<=b)
-ir.setMinAndMax(a,b);else
-ir.setMinAndMax(b,a);if(ir.min==newWorldX){this.isMovingLeftEdge_=true;ir.leftSelected=true;ir.rightSelected=false;}else{this.isMovingLeftEdge_=false;ir.leftSelected=false;ir.rightSelected=true;}},mouseUp_:function(){var dt=this.viewport_.currentDisplayTransform;var ir=this.viewport_.interestRange;ir.leftSelected=false;ir.rightSelected=false;var pixelRatio=window.devicePixelRatio||1;var minWidthValue=dt.xViewVectorToWorld(2*pixelRatio);if(ir.range<minWidthValue)
-ir.reset();},getWorldXFromEvent_:function(e){var pixelRatio=window.devicePixelRatio||1;var canvas=this.viewport_.modelTrackContainer.canvas;var worldOffset=canvas.getBoundingClientRect().left;var viewX=(e.clientX-worldOffset)*pixelRatio;return this.viewport_.currentDisplayTransform.xViewToWorld(viewX);},getSnappedToEventPosition_:function(e){var pixelRatio=window.devicePixelRatio||1;var EVENT_SNAP_RANGE=16*pixelRatio;var modelTrackContainer=this.viewport_.modelTrackContainer;var modelTrackContainerRect=modelTrackContainer.getBoundingClientRect();var viewport=this.viewport_;var dt=viewport.currentDisplayTransform;var worldMaxDist=dt.xViewVectorToWorld(EVENT_SNAP_RANGE);var worldX=this.getWorldXFromEvent_(e);var mouseY=e.clientY;var selection=new tr.model.EventSet();modelTrackContainer.addClosestEventToSelection(worldX,worldMaxDist,mouseY,mouseY,selection);if(!selection.length){modelTrackContainer.addClosestEventToSelection(worldX,worldMaxDist,modelTrackContainerRect.top,modelTrackContainerRect.bottom,selection);}
-var minDistX=worldMaxDist;var minDistY=Infinity;var pixWidth=dt.xViewVectorToWorld(1);var result={x:worldX,y:mouseY-modelTrackContainerRect.top,height:0,snapped:false};var eventBounds=new tr.b.Range();for(var event of selection){var track=viewport.trackForEvent(event);var trackRect=track.getBoundingClientRect();eventBounds.reset();event.addBoundsToRange(eventBounds);var eventX;if(Math.abs(eventBounds.min-worldX)<Math.abs(eventBounds.max-worldX)){eventX=eventBounds.min;}else{eventX=eventBounds.max;}
+ir.leftSelected=false;ir.rightSelected=false;return;},updateMovingEdge_:function(newWorldX){var ir=this.viewport_.interestRange;var a=ir.min;var b=ir.max;if(this.isMovingLeftEdge_){a=newWorldX;}else{b=newWorldX;}
+if(a<=b){ir.setMinAndMax(a,b);}else{ir.setMinAndMax(b,a);}
+if(ir.min===newWorldX){this.isMovingLeftEdge_=true;ir.leftSelected=true;ir.rightSelected=false;}else{this.isMovingLeftEdge_=false;ir.leftSelected=false;ir.rightSelected=true;}},mouseUp_:function(){var dt=this.viewport_.currentDisplayTransform;var ir=this.viewport_.interestRange;ir.leftSelected=false;ir.rightSelected=false;var pixelRatio=window.devicePixelRatio||1;var minWidthValue=dt.xViewVectorToWorld(2*pixelRatio);if(ir.range<minWidthValue){ir.reset();}},getWorldXFromEvent_:function(e){var pixelRatio=window.devicePixelRatio||1;var canvas=this.viewport_.modelTrackContainer.canvas;var worldOffset=canvas.getBoundingClientRect().left;var viewX=(e.clientX-worldOffset)*pixelRatio;return this.viewport_.currentDisplayTransform.xViewToWorld(viewX);},getSnappedToEventPosition_:function(e){var pixelRatio=window.devicePixelRatio||1;var EVENT_SNAP_RANGE=16*pixelRatio;var modelTrackContainer=this.viewport_.modelTrackContainer;var modelTrackContainerRect=modelTrackContainer.getBoundingClientRect();var viewport=this.viewport_;var dt=viewport.currentDisplayTransform;var worldMaxDist=dt.xViewVectorToWorld(EVENT_SNAP_RANGE);var worldX=this.getWorldXFromEvent_(e);var mouseY=e.clientY;var selection=new tr.model.EventSet();modelTrackContainer.addClosestEventToSelection(worldX,worldMaxDist,mouseY,mouseY,selection);if(!selection.length){modelTrackContainer.addClosestEventToSelection(worldX,worldMaxDist,modelTrackContainerRect.top,modelTrackContainerRect.bottom,selection);}
+var minDistX=worldMaxDist;var minDistY=Infinity;var pixWidth=dt.xViewVectorToWorld(1);var result={x:worldX,y:mouseY-modelTrackContainerRect.top,height:0,snapped:false};var eventBounds=new tr.b.math.Range();for(var event of selection){var track=viewport.trackForEvent(event);var trackRect=track.getBoundingClientRect();eventBounds.reset();event.addBoundsToRange(eventBounds);var eventX;if(Math.abs(eventBounds.min-worldX)<Math.abs(eventBounds.max-worldX)){eventX=eventBounds.min;}else{eventX=eventBounds.max;}
 var distX=eventX-worldX;var eventY=trackRect.top;var eventHeight=trackRect.height;var distY=Math.abs(eventY+eventHeight/2-mouseY);if((distX<=minDistX||Math.abs(distX-minDistX)<pixWidth)&&distY<minDistY){minDistX=distX;minDistY=distY;result.x=eventX;result.y=eventY+
 modelTrackContainer.scrollTop-modelTrackContainerRect.top;result.height=eventHeight;result.snapped=true;}}
-return result;}};return{TimingTool:TimingTool};});'use strict';tr.exportTo('tr.ui',function(){var kDefaultPanAnimationDurationMs=100.0;function TimelineDisplayTransformPanAnimation(deltaX,deltaY,opt_durationMs){this.deltaX=deltaX;this.deltaY=deltaY;if(opt_durationMs===undefined)
-this.durationMs=kDefaultPanAnimationDurationMs;else
-this.durationMs=opt_durationMs;this.startPanX=undefined;this.startPanY=undefined;this.startTimeMs=undefined;}
-TimelineDisplayTransformPanAnimation.prototype={__proto__:tr.ui.b.Animation.prototype,get affectsPanY(){return this.deltaY!==0;},canTakeOverFor:function(existingAnimation){return existingAnimation instanceof TimelineDisplayTransformPanAnimation;},takeOverFor:function(existing,timestamp,target){var remainingDeltaXOnExisting=existing.goalPanX-target.panX;var remainingDeltaYOnExisting=existing.goalPanY-target.panY;var remainingTimeOnExisting=timestamp-(existing.startTimeMs+existing.durationMs);remainingTimeOnExisting=Math.max(remainingTimeOnExisting,0);this.deltaX+=remainingDeltaXOnExisting;this.deltaY+=remainingDeltaYOnExisting;this.durationMs+=remainingTimeOnExisting;},start:function(timestamp,target){this.startTimeMs=timestamp;this.startPanX=target.panX;this.startPanY=target.panY;},tick:function(timestamp,target){var percentDone=(timestamp-this.startTimeMs)/this.durationMs;percentDone=tr.b.clamp(percentDone,0,1);target.panX=tr.b.lerp(percentDone,this.startPanX,this.goalPanX);if(this.affectsPanY)
-target.panY=tr.b.lerp(percentDone,this.startPanY,this.goalPanY);return timestamp>=this.startTimeMs+this.durationMs;},get goalPanX(){return this.startPanX+this.deltaX;},get goalPanY(){return this.startPanY+this.deltaY;}};function TimelineDisplayTransformZoomToAnimation(goalFocalPointXWorld,goalFocalPointXView,goalFocalPointY,zoomInRatioX,opt_durationMs){this.goalFocalPointXWorld=goalFocalPointXWorld;this.goalFocalPointXView=goalFocalPointXView;this.goalFocalPointY=goalFocalPointY;this.zoomInRatioX=zoomInRatioX;if(opt_durationMs===undefined)
-this.durationMs=kDefaultPanAnimationDurationMs;else
-this.durationMs=opt_durationMs;this.startTimeMs=undefined;this.startScaleX=undefined;this.goalScaleX=undefined;this.startPanY=undefined;}
-TimelineDisplayTransformZoomToAnimation.prototype={__proto__:tr.ui.b.Animation.prototype,get affectsPanY(){return this.startPanY!=this.goalFocalPointY;},canTakeOverFor:function(existingAnimation){return false;},takeOverFor:function(existingAnimation,timestamp,target){this.goalScaleX=target.scaleX*this.zoomInRatioX;},start:function(timestamp,target){this.startTimeMs=timestamp;this.startScaleX=target.scaleX;this.goalScaleX=this.zoomInRatioX*target.scaleX;this.startPanY=target.panY;},tick:function(timestamp,target){var percentDone=(timestamp-this.startTimeMs)/this.durationMs;percentDone=tr.b.clamp(percentDone,0,1);target.scaleX=tr.b.lerp(percentDone,this.startScaleX,this.goalScaleX);if(this.affectsPanY){target.panY=tr.b.lerp(percentDone,this.startPanY,this.goalFocalPointY);}
-target.xPanWorldPosToViewPos(this.goalFocalPointXWorld,this.goalFocalPointXView);return timestamp>=this.startTimeMs+this.durationMs;}};return{TimelineDisplayTransformPanAnimation:TimelineDisplayTransformPanAnimation,TimelineDisplayTransformZoomToAnimation:TimelineDisplayTransformZoomToAnimation};});'use strict';tr.exportTo('tr.ui.tracks',function(){var DrawType={GENERAL_EVENT:1,INSTANT_EVENT:2,BACKGROUND:3,GRID:4,FLOW_ARROWS:5,MARKERS:6,HIGHLIGHTS:7,ANNOTATIONS:8};var DrawingContainer=tr.ui.b.define('drawing-container',tr.ui.tracks.Track);DrawingContainer.prototype={__proto__:tr.ui.tracks.Track.prototype,decorate:function(viewport){tr.ui.tracks.Track.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('drawing-container');this.canvas_=document.createElement('canvas');this.canvas_.className='drawing-container-canvas';this.canvas_.style.left=tr.ui.b.constants.HEADING_WIDTH+'px';Polymer.dom(this).appendChild(this.canvas_);this.ctx_=this.canvas_.getContext('2d');this.viewportChange_=this.viewportChange_.bind(this);this.viewport.addEventListener('change',this.viewportChange_);},get canvas(){return this.canvas_;},context:function(){return this.ctx_;},viewportChange_:function(){this.invalidate();},invalidate:function(){if(this.rafPending_)
-return;this.rafPending_=true;tr.b.requestPreAnimationFrame(this.preDraw_,this);},preDraw_:function(){this.rafPending_=false;this.updateCanvasSizeIfNeeded_();tr.b.requestAnimationFrameInThisFrameIfPossible(this.draw_,this);},draw_:function(){this.ctx_.clearRect(0,0,this.canvas_.width,this.canvas_.height);var typesToDraw=[DrawType.BACKGROUND,DrawType.HIGHLIGHTS,DrawType.GRID,DrawType.INSTANT_EVENT,DrawType.GENERAL_EVENT,DrawType.MARKERS,DrawType.ANNOTATIONS,DrawType.FLOW_ARROWS];for(var idx in typesToDraw){for(var i=0;i<this.children.length;++i){if(!(this.children[i]instanceof tr.ui.tracks.Track))
-continue;this.children[i].drawTrack(typesToDraw[idx]);}}
-var pixelRatio=window.devicePixelRatio||1;var bounds=this.canvas_.getBoundingClientRect();var dt=this.viewport.currentDisplayTransform;var viewLWorld=dt.xViewToWorld(0);var viewRWorld=dt.xViewToWorld(bounds.width*pixelRatio);this.viewport.drawGridLines(this.ctx_,viewLWorld,viewRWorld);},updateCanvasSizeIfNeeded_:function(){var visibleChildTracks=tr.b.asArray(this.children).filter(this.visibleFilter_);var thisBounds=this.getBoundingClientRect();var firstChildTrackBounds=visibleChildTracks[0].getBoundingClientRect();var lastChildTrackBounds=visibleChildTracks[visibleChildTracks.length-1].getBoundingClientRect();var innerWidth=firstChildTrackBounds.width-
-tr.ui.b.constants.HEADING_WIDTH;var innerHeight=lastChildTrackBounds.bottom-firstChildTrackBounds.top;var pixelRatio=window.devicePixelRatio||1;if(this.canvas_.width!=innerWidth*pixelRatio){this.canvas_.width=innerWidth*pixelRatio;this.canvas_.style.width=innerWidth+'px';}
-if(this.canvas_.height!=innerHeight*pixelRatio){this.canvas_.height=innerHeight*pixelRatio;this.canvas_.style.height=innerHeight+'px';}},visibleFilter_:function(element){if(!(element instanceof tr.ui.tracks.Track))
-return false;return window.getComputedStyle(element).display!=='none';},addClosestEventToSelection:function(worldX,worldMaxDist,loY,hiY,selection){for(var i=0;i<this.children.length;++i){if(!(this.children[i]instanceof tr.ui.tracks.Track))
-continue;var trackClientRect=this.children[i].getBoundingClientRect();var a=Math.max(loY,trackClientRect.top);var b=Math.min(hiY,trackClientRect.bottom);if(a<=b){this.children[i].addClosestEventToSelection(worldX,worldMaxDist,loY,hiY,selection);}}
-tr.ui.tracks.Track.prototype.addClosestEventToSelection.apply(this,arguments);},addEventsToTrackMap:function(eventToTrackMap){for(var i=0;i<this.children.length;++i){if(!(this.children[i]instanceof tr.ui.tracks.Track))
-continue;this.children[i].addEventsToTrackMap(eventToTrackMap);}}};return{DrawingContainer:DrawingContainer,DrawType:DrawType};});'use strict';tr.exportTo('tr.model',function(){var SelectableItem=tr.model.SelectableItem;var SelectionState=tr.model.SelectionState;function ProxySelectableItem(modelItem){SelectableItem.call(this,modelItem);};ProxySelectableItem.prototype={__proto__:SelectableItem.prototype,get selectionState(){var modelItem=this.modelItem_;if(modelItem===undefined)
-return SelectionState.NONE;return modelItem.selectionState;}};return{ProxySelectableItem:ProxySelectableItem};});'use strict';tr.exportTo('tr.ui.tracks',function(){var EventPresenter=tr.ui.b.EventPresenter;var SelectionState=tr.model.SelectionState;var LetterDotTrack=tr.ui.b.define('letter-dot-track',tr.ui.tracks.Track);LetterDotTrack.prototype={__proto__:tr.ui.tracks.Track.prototype,decorate:function(viewport){tr.ui.tracks.Track.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('letter-dot-track');this.items_=undefined;this.heading_=document.createElement('tr-ui-heading');Polymer.dom(this).appendChild(this.heading_);},set heading(heading){this.heading_.heading=heading;},get heading(){return this.heading_.heading;},set tooltip(tooltip){this.heading_.tooltip=tooltip;},get items(){return this.items_;},set items(items){this.items_=items;this.invalidateDrawingContainer();},get height(){return window.getComputedStyle(this).height;},set height(height){this.style.height=height;},get dumpRadiusView(){return 7*(window.devicePixelRatio||1);},draw:function(type,viewLWorld,viewRWorld){if(this.items_===undefined)
-return;switch(type){case tr.ui.tracks.DrawType.GENERAL_EVENT:this.drawLetterDots_(viewLWorld,viewRWorld);break;}},drawLetterDots_:function(viewLWorld,viewRWorld){var ctx=this.context();var pixelRatio=window.devicePixelRatio||1;var bounds=this.getBoundingClientRect();var height=bounds.height*pixelRatio;var halfHeight=height*0.5;var twoPi=Math.PI*2;var dt=this.viewport.currentDisplayTransform;var dumpRadiusView=this.dumpRadiusView;var itemRadiusWorld=dt.xViewVectorToWorld(height);var items=this.items_;var loI=tr.b.findLowIndexInSortedArray(items,function(item){return item.start;},viewLWorld);var oldFont=ctx.font;ctx.font='400 '+Math.floor(9*pixelRatio)+'px Arial';ctx.strokeStyle='rgb(0,0,0)';ctx.textBaseline='middle';ctx.textAlign='center';var drawItems=function(selected){for(var i=loI;i<items.length;++i){var item=items[i];var x=item.start;if(x-itemRadiusWorld>viewRWorld)
-break;if(item.selected!==selected)
-continue;var xView=dt.xWorldToView(x);ctx.fillStyle=EventPresenter.getSelectableItemColorAsString(item);ctx.beginPath();ctx.arc(xView,halfHeight,dumpRadiusView+0.5,0,twoPi);ctx.fill();if(item.selected){ctx.lineWidth=3;ctx.strokeStyle='rgb(100,100,0)';ctx.stroke();ctx.beginPath();ctx.arc(xView,halfHeight,dumpRadiusView,0,twoPi);ctx.lineWidth=1.5;ctx.strokeStyle='rgb(255,255,0)';ctx.stroke();}else{ctx.lineWidth=1;ctx.strokeStyle='rgb(0,0,0)';ctx.stroke();}
-ctx.fillStyle='rgb(255, 255, 255)';ctx.fillText(item.dotLetter,xView,halfHeight);}};drawItems(false);drawItems(true);ctx.lineWidth=1;ctx.font=oldFont;},addEventsToTrackMap:function(eventToTrackMap){if(this.items_===undefined)
-return;this.items_.forEach(function(item){item.addToTrackMap(eventToTrackMap,this);},this);},addIntersectingEventsInRangeToSelectionInWorldSpace:function(loWX,hiWX,viewPixWidthWorld,selection){if(this.items_===undefined)
-return;var itemRadiusWorld=viewPixWidthWorld*this.dumpRadiusView;tr.b.iterateOverIntersectingIntervals(this.items_,function(x){return x.start-itemRadiusWorld;},function(x){return 2*itemRadiusWorld;},loWX,hiWX,function(item){item.addToSelection(selection);}.bind(this));},addEventNearToProvidedEventToSelection:function(event,offset,selection){if(this.items_===undefined)
-return;var items=this.items_;var index=tr.b.findFirstIndexInArray(items,function(item){return item.modelItem===event;});if(index===-1)
-return false;var newIndex=index+offset;if(newIndex>=0&&newIndex<items.length){items[newIndex].addToSelection(selection);return true;}
-return false;},addAllEventsMatchingFilterToSelection:function(filter,selection){},addClosestEventToSelection:function(worldX,worldMaxDist,loY,hiY,selection){if(this.items_===undefined)
-return;var item=tr.b.findClosestElementInSortedArray(this.items_,function(x){return x.start;},worldX,worldMaxDist);if(!item)
-return;item.addToSelection(selection);}};function LetterDot(modelItem,dotLetter,colorId,start){tr.model.ProxySelectableItem.call(this,modelItem);this.dotLetter=dotLetter;this.colorId=colorId;this.start=start;};LetterDot.prototype={__proto__:tr.model.ProxySelectableItem.prototype};return{LetterDotTrack:LetterDotTrack,LetterDot:LetterDot};});'use strict';tr.exportTo('tr.ui.tracks',function(){var AlertTrack=tr.ui.b.define('alert-track',tr.ui.tracks.LetterDotTrack);AlertTrack.prototype={__proto__:tr.ui.tracks.LetterDotTrack.prototype,decorate:function(viewport){tr.ui.tracks.LetterDotTrack.prototype.decorate.call(this,viewport);this.heading='Alerts';this.alerts_=undefined;},get alerts(){return this.alerts_;},set alerts(alerts){this.alerts_=alerts;if(alerts===undefined){this.items=undefined;return;}
-this.items=this.alerts_.map(function(alert){return new tr.ui.tracks.LetterDot(alert,String.fromCharCode(9888),alert.colorId,alert.start);});}};return{AlertTrack:AlertTrack};});'use strict';tr.exportTo('tr.ui.tracks',function(){var Task=tr.b.Task;var ContainerTrack=tr.ui.b.define('container-track',tr.ui.tracks.Track);ContainerTrack.prototype={__proto__:tr.ui.tracks.Track.prototype,decorate:function(viewport){tr.ui.tracks.Track.prototype.decorate.call(this,viewport);},detach:function(){Polymer.dom(this).textContent='';},get tracks_(){var tracks=[];for(var i=0;i<this.children.length;i++){if(this.children[i]instanceof tr.ui.tracks.Track)
-tracks.push(this.children[i]);}
-return tracks;},drawTrack:function(type){this.tracks_.forEach(function(track){track.drawTrack(type);});},addIntersectingEventsInRangeToSelection:function(loVX,hiVX,loY,hiY,selection){for(var i=0;i<this.tracks_.length;i++){var trackClientRect=this.tracks_[i].getBoundingClientRect();var a=Math.max(loY,trackClientRect.top);var b=Math.min(hiY,trackClientRect.bottom);if(a<=b)
-this.tracks_[i].addIntersectingEventsInRangeToSelection(loVX,hiVX,loY,hiY,selection);}
-tr.ui.tracks.Track.prototype.addIntersectingEventsInRangeToSelection.apply(this,arguments);},addEventsToTrackMap:function(eventToTrackMap){for(var i=0;i<this.children.length;++i)
-this.children[i].addEventsToTrackMap(eventToTrackMap);},addAllEventsMatchingFilterToSelection:function(filter,selection){for(var i=0;i<this.tracks_.length;i++)
-this.tracks_[i].addAllEventsMatchingFilterToSelection(filter,selection);},addAllEventsMatchingFilterToSelectionAsTask:function(filter,selection){var task=new Task();for(var i=0;i<this.tracks_.length;i++){task.subTask(function(i){return function(){this.tracks_[i].addAllEventsMatchingFilterToSelection(filter,selection);}}(i),this);}
+return result;}};return{TimingTool,};});'use strict';tr.exportTo('tr.ui',function(){var kDefaultPanAnimationDurationMs=100.0;const lerp=tr.b.math.lerp;function TimelineDisplayTransformPanAnimation(deltaX,deltaY,opt_durationMs){this.deltaX=deltaX;this.deltaY=deltaY;if(opt_durationMs===undefined){this.durationMs=kDefaultPanAnimationDurationMs;}else{this.durationMs=opt_durationMs;}
+this.startPanX=undefined;this.startPanY=undefined;this.startTimeMs=undefined;}
+TimelineDisplayTransformPanAnimation.prototype={__proto__:tr.ui.b.Animation.prototype,get affectsPanY(){return this.deltaY!==0;},canTakeOverFor:function(existingAnimation){return existingAnimation instanceof TimelineDisplayTransformPanAnimation;},takeOverFor:function(existing,timestamp,target){var remainingDeltaXOnExisting=existing.goalPanX-target.panX;var remainingDeltaYOnExisting=existing.goalPanY-target.panY;var remainingTimeOnExisting=timestamp-(existing.startTimeMs+existing.durationMs);remainingTimeOnExisting=Math.max(remainingTimeOnExisting,0);this.deltaX+=remainingDeltaXOnExisting;this.deltaY+=remainingDeltaYOnExisting;this.durationMs+=remainingTimeOnExisting;},start:function(timestamp,target){this.startTimeMs=timestamp;this.startPanX=target.panX;this.startPanY=target.panY;},tick:function(timestamp,target){var percentDone=(timestamp-this.startTimeMs)/this.durationMs;percentDone=tr.b.math.clamp(percentDone,0,1);target.panX=lerp(percentDone,this.startPanX,this.goalPanX);if(this.affectsPanY){target.panY=lerp(percentDone,this.startPanY,this.goalPanY);}
+return timestamp>=this.startTimeMs+this.durationMs;},get goalPanX(){return this.startPanX+this.deltaX;},get goalPanY(){return this.startPanY+this.deltaY;}};function TimelineDisplayTransformZoomToAnimation(goalFocalPointXWorld,goalFocalPointXView,goalFocalPointY,zoomInRatioX,opt_durationMs){this.goalFocalPointXWorld=goalFocalPointXWorld;this.goalFocalPointXView=goalFocalPointXView;this.goalFocalPointY=goalFocalPointY;this.zoomInRatioX=zoomInRatioX;if(opt_durationMs===undefined){this.durationMs=kDefaultPanAnimationDurationMs;}else{this.durationMs=opt_durationMs;}
+this.startTimeMs=undefined;this.startScaleX=undefined;this.goalScaleX=undefined;this.startPanY=undefined;}
+TimelineDisplayTransformZoomToAnimation.prototype={__proto__:tr.ui.b.Animation.prototype,get affectsPanY(){return this.startPanY!==this.goalFocalPointY;},canTakeOverFor:function(existingAnimation){return false;},takeOverFor:function(existingAnimation,timestamp,target){this.goalScaleX=target.scaleX*this.zoomInRatioX;},start:function(timestamp,target){this.startTimeMs=timestamp;this.startScaleX=target.scaleX;this.goalScaleX=this.zoomInRatioX*target.scaleX;this.startPanY=target.panY;},tick:function(timestamp,target){var percentDone=(timestamp-this.startTimeMs)/this.durationMs;percentDone=tr.b.math.clamp(percentDone,0,1);target.scaleX=lerp(percentDone,this.startScaleX,this.goalScaleX);if(this.affectsPanY){target.panY=lerp(percentDone,this.startPanY,this.goalFocalPointY);}
+target.xPanWorldPosToViewPos(this.goalFocalPointXWorld,this.goalFocalPointXView);return timestamp>=this.startTimeMs+this.durationMs;}};return{TimelineDisplayTransformPanAnimation,TimelineDisplayTransformZoomToAnimation,};});'use strict';tr.exportTo('tr.ui.tracks',function(){var DrawType={GENERAL_EVENT:1,INSTANT_EVENT:2,BACKGROUND:3,GRID:4,FLOW_ARROWS:5,MARKERS:6,HIGHLIGHTS:7,ANNOTATIONS:8};var MAX_OVERSIZE_MULTIPLE=3.0;var REDRAW_SLOP=(MAX_OVERSIZE_MULTIPLE-1)/2;var DrawingContainer=tr.ui.b.define('drawing-container',tr.ui.tracks.Track);DrawingContainer.prototype={__proto__:tr.ui.tracks.Track.prototype,decorate:function(viewport){tr.ui.tracks.Track.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('drawing-container');this.canvas_=document.createElement('canvas');this.canvas_.className='drawing-container-canvas';this.canvas_.style.left=tr.ui.b.constants.HEADING_WIDTH+'px';Polymer.dom(this).appendChild(this.canvas_);this.ctx_=this.canvas_.getContext('2d');this.offsetY_=0;this.viewportChange_=this.viewportChange_.bind(this);this.viewport.addEventListener('change',this.viewportChange_);window.addEventListener('resize',this.windowResized_.bind(this));this.addEventListener('scroll',this.scrollChanged_.bind(this));},get canvas(){return this.canvas_;},context:function(){return this.ctx_;},viewportChange_:function(){this.invalidate();},windowResized_:function(){this.invalidate();},scrollChanged_:function(){if(this.updateOffsetY_()){this.invalidate();}},invalidate:function(){if(this.rafPending_)return;this.rafPending_=true;tr.b.requestPreAnimationFrame(this.preDraw_,this);},preDraw_:function(){this.rafPending_=false;this.updateCanvasSizeIfNeeded_();tr.b.requestAnimationFrameInThisFrameIfPossible(this.draw_,this);},draw_:function(){this.ctx_.clearRect(0,0,this.canvas_.width,this.canvas_.height);var typesToDraw=[DrawType.BACKGROUND,DrawType.HIGHLIGHTS,DrawType.GRID,DrawType.INSTANT_EVENT,DrawType.GENERAL_EVENT,DrawType.MARKERS,DrawType.ANNOTATIONS,DrawType.FLOW_ARROWS];for(var idx in typesToDraw){for(var i=0;i<this.children.length;++i){if(!(this.children[i]instanceof tr.ui.tracks.Track)){continue;}
+this.children[i].drawTrack(typesToDraw[idx]);}}
+var pixelRatio=window.devicePixelRatio||1;var bounds=this.canvas_.getBoundingClientRect();var dt=this.viewport.currentDisplayTransform;var viewLWorld=dt.xViewToWorld(0);var viewRWorld=dt.xViewToWorld(bounds.width*pixelRatio);this.viewport.drawGridLines(this.ctx_,viewLWorld,viewRWorld);},updateOffsetY_:function(){var maxYDelta=window.innerHeight*REDRAW_SLOP;var newOffset=this.scrollTop-maxYDelta;if(Math.abs(newOffset-this.offsetY_)<=maxYDelta)return false;var maxOffset=this.scrollHeight-
+this.canvas_.getBoundingClientRect().height;newOffset=Math.max(0,Math.min(newOffset,maxOffset));if(newOffset!==this.offsetY_){this.offsetY_=newOffset;return true;}
+return false;},updateCanvasSizeIfNeeded_:function(){var visibleChildTracks=tr.b.asArray(this.children).filter(this.visibleFilter_);if(visibleChildTracks.length===0){return;}
+var thisBounds=this.getBoundingClientRect();var firstChildTrackBounds=visibleChildTracks[0].getBoundingClientRect();var lastChildTrackBounds=visibleChildTracks[visibleChildTracks.length-1].getBoundingClientRect();var innerWidth=firstChildTrackBounds.width-
+tr.ui.b.constants.HEADING_WIDTH;var innerHeight=lastChildTrackBounds.bottom-firstChildTrackBounds.top;var innerHeight=Math.min(innerHeight,Math.floor(window.innerHeight*MAX_OVERSIZE_MULTIPLE));var pixelRatio=window.devicePixelRatio||1;if(this.canvas_.width!==innerWidth*pixelRatio){this.canvas_.width=innerWidth*pixelRatio;this.canvas_.style.width=innerWidth+'px';}
+if(this.canvas_.height!==innerHeight*pixelRatio){this.canvas_.height=innerHeight*pixelRatio;this.canvas_.style.height=innerHeight+'px';}
+if(this.canvas_.top!==this.offsetY_){this.canvas_.top=this.offsetY_;this.canvas_.style.top=this.offsetY_+'px';}},visibleFilter_:function(element){if(!(element instanceof tr.ui.tracks.Track))return false;return window.getComputedStyle(element).display!=='none';},addClosestEventToSelection:function(worldX,worldMaxDist,loY,hiY,selection){for(var i=0;i<this.children.length;++i){if(!(this.children[i]instanceof tr.ui.tracks.Track)){continue;}
+var trackClientRect=this.children[i].getBoundingClientRect();var a=Math.max(loY,trackClientRect.top);var b=Math.min(hiY,trackClientRect.bottom);if(a<=b){this.children[i].addClosestEventToSelection(worldX,worldMaxDist,loY,hiY,selection);}}
+tr.ui.tracks.Track.prototype.addClosestEventToSelection.apply(this,arguments);},addEventsToTrackMap:function(eventToTrackMap){for(var i=0;i<this.children.length;++i){if(!(this.children[i]instanceof tr.ui.tracks.Track)){continue;}
+this.children[i].addEventsToTrackMap(eventToTrackMap);}}};return{DrawingContainer,DrawType,};});'use strict';tr.exportTo('tr.model',function(){var SelectableItem=tr.model.SelectableItem;var SelectionState=tr.model.SelectionState;function ProxySelectableItem(modelItem){SelectableItem.call(this,modelItem);}
+ProxySelectableItem.prototype={__proto__:SelectableItem.prototype,get selectionState(){var modelItem=this.modelItem_;if(modelItem===undefined){return SelectionState.NONE;}
+return modelItem.selectionState;}};return{ProxySelectableItem,};});'use strict';tr.exportTo('tr.ui.tracks',function(){var EventPresenter=tr.ui.b.EventPresenter;var SelectionState=tr.model.SelectionState;var LetterDotTrack=tr.ui.b.define('letter-dot-track',tr.ui.tracks.Track);LetterDotTrack.prototype={__proto__:tr.ui.tracks.Track.prototype,decorate:function(viewport){tr.ui.tracks.Track.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('letter-dot-track');this.items_=undefined;this.heading_=document.createElement('tr-ui-b-heading');Polymer.dom(this).appendChild(this.heading_);},set heading(heading){this.heading_.heading=heading;},get heading(){return this.heading_.heading;},set tooltip(tooltip){this.heading_.tooltip=tooltip;},get items(){return this.items_;},set items(items){this.items_=items;this.invalidateDrawingContainer();},get height(){return window.getComputedStyle(this).height;},set height(height){this.style.height=height;},get dumpRadiusView(){return 7*(window.devicePixelRatio||1);},draw:function(type,viewLWorld,viewRWorld){if(this.items_===undefined)return;switch(type){case tr.ui.tracks.DrawType.GENERAL_EVENT:this.drawLetterDots_(viewLWorld,viewRWorld);break;}},drawLetterDots_:function(viewLWorld,viewRWorld){var ctx=this.context();var pixelRatio=window.devicePixelRatio||1;var bounds=this.getBoundingClientRect();var height=bounds.height*pixelRatio;var halfHeight=height*0.5;var twoPi=Math.PI*2;var dt=this.viewport.currentDisplayTransform;var dumpRadiusView=this.dumpRadiusView;var itemRadiusWorld=dt.xViewVectorToWorld(height);var items=this.items_;var loI=tr.b.math.findLowIndexInSortedArray(items,function(item){return item.start;},viewLWorld);var oldFont=ctx.font;ctx.font='400 '+Math.floor(9*pixelRatio)+'px Arial';ctx.strokeStyle='rgb(0,0,0)';ctx.textBaseline='middle';ctx.textAlign='center';var drawItems=function(selected){for(var i=loI;i<items.length;++i){var item=items[i];var x=item.start;if(x-itemRadiusWorld>viewRWorld)break;if(item.selected!==selected)continue;var xView=dt.xWorldToView(x);ctx.fillStyle=EventPresenter.getSelectableItemColorAsString(item);ctx.beginPath();ctx.arc(xView,halfHeight,dumpRadiusView+0.5,0,twoPi);ctx.fill();if(item.selected){ctx.lineWidth=3;ctx.strokeStyle='rgb(100,100,0)';ctx.stroke();ctx.beginPath();ctx.arc(xView,halfHeight,dumpRadiusView,0,twoPi);ctx.lineWidth=1.5;ctx.strokeStyle='rgb(255,255,0)';ctx.stroke();}else{ctx.lineWidth=1;ctx.strokeStyle='rgb(0,0,0)';ctx.stroke();}
+ctx.fillStyle='rgb(255, 255, 255)';ctx.fillText(item.dotLetter,xView,halfHeight);}};drawItems(false);drawItems(true);ctx.lineWidth=1;ctx.font=oldFont;},addEventsToTrackMap:function(eventToTrackMap){if(this.items_===undefined)return;this.items_.forEach(function(item){item.addToTrackMap(eventToTrackMap,this);},this);},addIntersectingEventsInRangeToSelectionInWorldSpace:function(loWX,hiWX,viewPixWidthWorld,selection){if(this.items_===undefined)return;var itemRadiusWorld=viewPixWidthWorld*this.dumpRadiusView;tr.b.math.iterateOverIntersectingIntervals(this.items_,function(x){return x.start-itemRadiusWorld;},function(x){return 2*itemRadiusWorld;},loWX,hiWX,function(item){item.addToSelection(selection);}.bind(this));},addEventNearToProvidedEventToSelection:function(event,offset,selection){if(this.items_===undefined)return;var items=this.items_;var index=tr.b.findFirstIndexInArray(items,function(item){return item.modelItem===event;});if(index===-1)return false;var newIndex=index+offset;if(newIndex>=0&&newIndex<items.length){items[newIndex].addToSelection(selection);return true;}
+return false;},addAllEventsMatchingFilterToSelection:function(filter,selection){},addClosestEventToSelection:function(worldX,worldMaxDist,loY,hiY,selection){if(this.items_===undefined)return;var item=tr.b.math.findClosestElementInSortedArray(this.items_,function(x){return x.start;},worldX,worldMaxDist);if(!item)return;item.addToSelection(selection);}};function LetterDot(modelItem,dotLetter,colorId,start){tr.model.ProxySelectableItem.call(this,modelItem);this.dotLetter=dotLetter;this.colorId=colorId;this.start=start;}
+LetterDot.prototype={__proto__:tr.model.ProxySelectableItem.prototype};return{LetterDotTrack,LetterDot,};});'use strict';tr.exportTo('tr.ui.tracks',function(){var AlertTrack=tr.ui.b.define('alert-track',tr.ui.tracks.LetterDotTrack);AlertTrack.prototype={__proto__:tr.ui.tracks.LetterDotTrack.prototype,decorate:function(viewport){tr.ui.tracks.LetterDotTrack.prototype.decorate.call(this,viewport);this.heading='Alerts';this.alerts_=undefined;},get alerts(){return this.alerts_;},set alerts(alerts){this.alerts_=alerts;if(alerts===undefined){this.items=undefined;return;}
+this.items=this.alerts_.map(function(alert){return new tr.ui.tracks.LetterDot(alert,String.fromCharCode(9888),alert.colorId,alert.start);});}};return{AlertTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){var Task=tr.b.Task;var ContainerTrack=tr.ui.b.define('container-track',tr.ui.tracks.Track);ContainerTrack.prototype={__proto__:tr.ui.tracks.Track.prototype,decorate:function(viewport){tr.ui.tracks.Track.prototype.decorate.call(this,viewport);},detach:function(){Polymer.dom(this).textContent='';},get tracks_(){var tracks=[];for(var i=0;i<this.children.length;i++){if(this.children[i]instanceof tr.ui.tracks.Track){tracks.push(this.children[i]);}}
+return tracks;},drawTrack:function(type){this.tracks_.forEach(function(track){track.drawTrack(type);});},addIntersectingEventsInRangeToSelection:function(loVX,hiVX,loY,hiY,selection){for(var i=0;i<this.tracks_.length;i++){var trackClientRect=this.tracks_[i].getBoundingClientRect();var a=Math.max(loY,trackClientRect.top);var b=Math.min(hiY,trackClientRect.bottom);if(a<=b){this.tracks_[i].addIntersectingEventsInRangeToSelection(loVX,hiVX,loY,hiY,selection);}}
+tr.ui.tracks.Track.prototype.addIntersectingEventsInRangeToSelection.apply(this,arguments);},addEventsToTrackMap:function(eventToTrackMap){for(var track of this.tracks_){track.addEventsToTrackMap(eventToTrackMap);}},addAllEventsMatchingFilterToSelection:function(filter,selection){for(var i=0;i<this.tracks_.length;i++){this.tracks_[i].addAllEventsMatchingFilterToSelection(filter,selection);}},addAllEventsMatchingFilterToSelectionAsTask:function(filter,selection){var task=new Task();for(var i=0;i<this.tracks_.length;i++){task.subTask(function(i){return function(){this.tracks_[i].addAllEventsMatchingFilterToSelection(filter,selection);};}(i),this);}
 return task;},addClosestEventToSelection:function(worldX,worldMaxDist,loY,hiY,selection){for(var i=0;i<this.tracks_.length;i++){var trackClientRect=this.tracks_[i].getBoundingClientRect();var a=Math.max(loY,trackClientRect.top);var b=Math.min(hiY,trackClientRect.bottom);if(a<=b){this.tracks_[i].addClosestEventToSelection(worldX,worldMaxDist,loY,hiY,selection);}}
-tr.ui.tracks.Track.prototype.addClosestEventToSelection.apply(this,arguments);},addContainersToTrackMap:function(containerToTrackMap){this.tracks_.forEach(function(track){track.addContainersToTrackMap(containerToTrackMap);});},clearTracks_:function(){this.tracks_.forEach(function(track){Polymer.dom(this).removeChild(track);},this);}};return{ContainerTrack:ContainerTrack};});'use strict';tr.exportTo('tr.ui.tracks',function(){function ChartAxis(opt_min,opt_max){this.guid_=tr.b.GUID.allocateSimple();this.bounds=new tr.b.Range();if(opt_min!==undefined)
-this.bounds.addValue(opt_min);if(opt_max!==undefined)
-this.bounds.addValue(opt_max);};ChartAxis.prototype={get guid(){return this.guid_;},valueToUnitRange:function(value){if(this.bounds.isEmpty)
-throw new Error('Chart axis bounds are empty');var bounds=this.bounds;if(bounds.range===0)
-return 0;return(value-bounds.min)/bounds.range;},autoSetFromSeries:function(series,opt_config){var range=new tr.b.Range();series.forEach(function(s){range.addRange(s.range);},this);this.autoSetFromRange(range,opt_config);},autoSetFromRange:function(range,opt_config){if(range.isEmpty)
-return;var bounds=this.bounds;if(bounds.isEmpty){bounds.addRange(range);return;}
-if(!opt_config)
-return;var useRangeMin=(opt_config.expandMin&&range.min<bounds.min||opt_config.shrinkMin&&range.min>bounds.min);var useRangeMax=(opt_config.expandMax&&range.max>bounds.max||opt_config.shrinkMax&&range.max<bounds.max);if(!useRangeMin&&!useRangeMax)
-return;if(useRangeMin&&useRangeMax){bounds.min=range.min;bounds.max=range.max;return;}
-if(useRangeMin){bounds.min=Math.min(range.min,bounds.max);}else{bounds.max=Math.max(range.max,bounds.min);}}};return{ChartAxis:ChartAxis};});'use strict';tr.exportTo('tr.ui.tracks',function(){function ChartPoint(modelItem,x,y,opt_yBase){tr.model.ProxySelectableItem.call(this,modelItem);this.x=x;this.y=y;this.yBase=opt_yBase;};ChartPoint.prototype={__proto__:tr.model.ProxySelectableItem.prototype};return{ChartPoint:ChartPoint};});'use strict';tr.exportTo('tr.ui.tracks',function(){var EventPresenter=tr.ui.b.EventPresenter;var SelectionState=tr.model.SelectionState;var ChartSeriesType={LINE:0,AREA:1};var DEFAULT_RENDERING_CONFIG={chartType:ChartSeriesType.LINE,selectedPointSize:4,unselectedPointSize:3,colorId:0,lineWidth:1,skipDistance:1,unselectedPointDensityTransparent:0.10,unselectedPointDensityOpaque:0.05,backgroundOpacity:0.5};var LAST_POINT_WIDTH=16;var ChartSeriesComponent={BACKGROUND:0,LINE:1,DOTS:2};function ChartSeries(points,axis,opt_renderingConfig){this.points=points;this.axis=axis;this.useRenderingConfig_(opt_renderingConfig);}
-ChartSeries.prototype={useRenderingConfig_:function(opt_renderingConfig){var config=opt_renderingConfig||{};tr.b.iterItems(DEFAULT_RENDERING_CONFIG,function(key,defaultValue){var value=config[key];if(value===undefined)
-value=defaultValue;this[key+'_']=value;},this);this.topPadding=this.bottomPadding=Math.max(this.selectedPointSize_,this.unselectedPointSize_)/2;},get range(){var range=new tr.b.Range();this.points.forEach(function(point){range.addValue(point.y);},this);return range;},draw:function(ctx,transform,highDetails){if(this.points===undefined||this.points.length===0)
-return;if(this.chartType_===ChartSeriesType.AREA){this.drawComponent_(ctx,transform,ChartSeriesComponent.BACKGROUND,highDetails);}
+tr.ui.tracks.Track.prototype.addClosestEventToSelection.apply(this,arguments);},addContainersToTrackMap:function(containerToTrackMap){this.tracks_.forEach(function(track){track.addContainersToTrackMap(containerToTrackMap);});},clearTracks_:function(){this.tracks_.forEach(function(track){Polymer.dom(this).removeChild(track);},this);}};return{ContainerTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){function ChartPoint(modelItem,x,y,opt_yBase){tr.model.ProxySelectableItem.call(this,modelItem);this.x=x;this.y=y;this.dotLetter=undefined;this.yBase=opt_yBase;}
+ChartPoint.prototype={__proto__:tr.model.ProxySelectableItem.prototype,};return{ChartPoint,};});'use strict';tr.exportTo('tr.ui.tracks',function(){var ColorScheme=tr.b.ColorScheme;var EventPresenter=tr.ui.b.EventPresenter;var SelectionState=tr.model.SelectionState;var ChartSeriesType={LINE:0,AREA:1};var DEFAULT_RENDERING_CONFIG={chartType:ChartSeriesType.LINE,selectedPointSize:4,unselectedPointSize:3,solidSelectedDots:false,colorId:0,lineWidth:1,skipDistance:1,unselectedPointDensityTransparent:0.10,unselectedPointDensityOpaque:0.05,backgroundOpacity:0.5,stepGraph:true};var LAST_POINT_WIDTH=16;var DOT_LETTER_RADIUS_PX=7;var DOT_LETTER_RADIUS_PADDING_PX=0.5;var DOT_LETTER_SELECTED_OUTLINE_WIDTH_PX=3;var DOT_LETTER_SELECTED_OUTLINE_DETAIL_WIDTH_PX=1.5;var DOT_LETTER_UNSELECTED_OUTLINE_WIDTH_PX=1;var DOT_LETTER_FONT_WEIGHT=400;var DOT_LETTER_FONT_SIZE_PX=9;var DOT_LETTER_FONT='Arial';var ChartSeriesComponent={BACKGROUND:0,LINE:1,DOTS:2};function ChartSeries(points,seriesYAxis,opt_renderingConfig){this.points=points;this.seriesYAxis=seriesYAxis;this.useRenderingConfig_(opt_renderingConfig);}
+ChartSeries.prototype={useRenderingConfig_:function(opt_renderingConfig){var config=opt_renderingConfig||{};for(var[key,defaultValue]of
+Object.entries(DEFAULT_RENDERING_CONFIG)){var value=config[key];if(value===undefined){value=defaultValue;}
+this[key+'_']=value;}
+this.topPadding=this.bottomPadding=Math.max(this.selectedPointSize_,this.unselectedPointSize_)/2;},get range(){var range=new tr.b.math.Range();this.points.forEach(function(point){range.addValue(point.y);},this);return range;},draw:function(ctx,transform,highDetails){if(this.points===undefined||this.points.length===0){return;}
+if(this.chartType_===ChartSeriesType.AREA){this.drawComponent_(ctx,transform,ChartSeriesComponent.BACKGROUND,highDetails);}
 if(this.chartType_===ChartSeriesType.LINE||highDetails){this.drawComponent_(ctx,transform,ChartSeriesComponent.LINE,highDetails);}
 this.drawComponent_(ctx,transform,ChartSeriesComponent.DOTS,highDetails);},drawComponent_:function(ctx,transform,component,highDetails){var extraPixels=0;if(component===ChartSeriesComponent.DOTS){extraPixels=Math.max(this.selectedPointSize_,this.unselectedPointSize_);}
-var leftViewX=transform.leftViewX-extraPixels*transform.pixelRatio;var rightViewX=transform.rightViewX+
-extraPixels*transform.pixelRatio;var leftTimestamp=transform.leftTimestamp-extraPixels;var rightTimestamp=transform.rightTimestamp+extraPixels;var firstVisibleIndex=tr.b.findLowIndexInSortedArray(this.points,function(point){return point.x;},leftTimestamp);var lastVisibleIndex=tr.b.findLowIndexInSortedArray(this.points,function(point){return point.x;},rightTimestamp);if(lastVisibleIndex>=this.points.length||this.points[lastVisibleIndex].x>rightTimestamp){lastVisibleIndex--;}
-var viewSkipDistance=this.skipDistance_*transform.pixelRatio;var circleRadius;var squareSize;var squareHalfSize;var squareOpacity;switch(component){case ChartSeriesComponent.DOTS:ctx.strokeStyle=EventPresenter.getCounterSeriesColor(this.colorId_,SelectionState.NONE);ctx.lineWidth=transform.pixelRatio;circleRadius=(this.selectedPointSize_/2)*transform.pixelRatio;squareSize=this.unselectedPointSize_*transform.pixelRatio;squareHalfSize=squareSize/2;if(!highDetails){squareOpacity=0;break;}
+var pixelRatio=transform.pixelRatio;var leftViewX=transform.leftViewX-extraPixels*pixelRatio;var rightViewX=transform.rightViewX+extraPixels*pixelRatio;var leftTimestamp=transform.leftTimestamp-extraPixels;var rightTimestamp=transform.rightTimestamp+extraPixels;var firstVisibleIndex=tr.b.math.findLowIndexInSortedArray(this.points,function(point){return point.x;},leftTimestamp);var lastVisibleIndex=tr.b.math.findLowIndexInSortedArray(this.points,function(point){return point.x;},rightTimestamp);if(lastVisibleIndex>=this.points.length||this.points[lastVisibleIndex].x>rightTimestamp){lastVisibleIndex--;}
+var viewSkipDistance=this.skipDistance_*pixelRatio;var selectedCircleRadius;var letterDotRadius;var squareSize;var squareHalfSize;var squareOpacity;var unselectedSeriesColor;var currentStateSeriesColor;ctx.save();ctx.font=DOT_LETTER_FONT_WEIGHT+' '+
+Math.floor(DOT_LETTER_FONT_SIZE_PX*pixelRatio)+'px '+
+DOT_LETTER_FONT;ctx.textBaseline='middle';ctx.textAlign='center';switch(component){case ChartSeriesComponent.DOTS:selectedCircleRadius=(this.selectedPointSize_/2)*pixelRatio;letterDotRadius=Math.max(selectedCircleRadius,DOT_LETTER_RADIUS_PX*pixelRatio);squareSize=this.unselectedPointSize_*pixelRatio;squareHalfSize=squareSize/2;unselectedSeriesColor=EventPresenter.getCounterSeriesColor(this.colorId_,SelectionState.NONE);if(!highDetails){squareOpacity=0;break;}
 var visibleIndexRange=lastVisibleIndex-firstVisibleIndex;if(visibleIndexRange<=0){squareOpacity=1;break;}
 var visibleViewXRange=transform.worldXToViewX(this.points[lastVisibleIndex].x)-
 transform.worldXToViewX(this.points[firstVisibleIndex].x);if(visibleViewXRange===0){squareOpacity=1;break;}
-var density=visibleIndexRange/visibleViewXRange;var clampedDensity=tr.b.clamp(density,this.unselectedPointDensityOpaque_,this.unselectedPointDensityTransparent_);var densityRange=this.unselectedPointDensityTransparent_-
-this.unselectedPointDensityOpaque_;squareOpacity=(this.unselectedPointDensityTransparent_-clampedDensity)/densityRange;break;case ChartSeriesComponent.LINE:ctx.strokeStyle=EventPresenter.getCounterSeriesColor(this.colorId_,SelectionState.NONE);ctx.lineWidth=this.lineWidth_*transform.pixelRatio;break;case ChartSeriesComponent.BACKGROUND:break;default:throw new Error('Invalid component: '+component);}
+var density=visibleIndexRange/visibleViewXRange;var clampedDensity=tr.b.math.clamp(density,this.unselectedPointDensityOpaque_,this.unselectedPointDensityTransparent_);var densityRange=this.unselectedPointDensityTransparent_-
+this.unselectedPointDensityOpaque_;squareOpacity=(this.unselectedPointDensityTransparent_-clampedDensity)/densityRange;break;case ChartSeriesComponent.LINE:ctx.strokeStyle=EventPresenter.getCounterSeriesColor(this.colorId_,SelectionState.NONE);ctx.lineWidth=this.lineWidth_*pixelRatio;break;case ChartSeriesComponent.BACKGROUND:break;default:throw new Error('Invalid component: '+component);}
 var previousViewX=undefined;var previousViewY=undefined;var previousViewYBase=undefined;var lastSelectionState=undefined;var baseSteps=undefined;var startIndex=Math.max(firstVisibleIndex-1,0);for(var i=startIndex;i<this.points.length;i++){var currentPoint=this.points[i];var currentViewX=transform.worldXToViewX(currentPoint.x);if(currentViewX>rightViewX){if(previousViewX!==undefined){previousViewX=currentViewX=rightViewX;if(component===ChartSeriesComponent.BACKGROUND||component===ChartSeriesComponent.LINE){ctx.lineTo(currentViewX,previousViewY);}}
 break;}
 if(i+1<this.points.length){var nextPoint=this.points[i+1];var nextViewX=transform.worldXToViewX(nextPoint.x);if(previousViewX!==undefined&&nextViewX-previousViewX<=viewSkipDistance&&nextViewX<rightViewX){continue;}
 if(currentViewX<leftViewX){currentViewX=leftViewX;}}
 if(previousViewX!==undefined&&currentViewX-previousViewX<viewSkipDistance){currentViewX=previousViewX+viewSkipDistance;}
 var currentViewY=Math.round(transform.worldYToViewY(currentPoint.y));var currentViewYBase;if(currentPoint.yBase===undefined){currentViewYBase=transform.outerBottomViewY;}else{currentViewYBase=Math.round(transform.worldYToViewY(currentPoint.yBase));}
-var currentSelectionState=currentPoint.selectionState;switch(component){case ChartSeriesComponent.DOTS:if(currentSelectionState!==lastSelectionState){if(currentSelectionState===SelectionState.SELECTED){ctx.fillStyle=EventPresenter.getCounterSeriesColor(this.colorId_,currentSelectionState);}else if(squareOpacity>0){ctx.fillStyle=EventPresenter.getCounterSeriesColor(this.colorId_,currentSelectionState,squareOpacity);}}
-if(currentSelectionState===SelectionState.SELECTED){ctx.beginPath();ctx.arc(currentViewX,currentViewY,circleRadius,0,2*Math.PI);ctx.fill();ctx.stroke();}else if(squareOpacity>0){ctx.fillRect(currentViewX-squareHalfSize,currentViewY-squareHalfSize,squareSize,squareSize);}
-break;case ChartSeriesComponent.LINE:if(previousViewX===undefined){ctx.beginPath();ctx.moveTo(currentViewX,currentViewY);}else{ctx.lineTo(currentViewX,previousViewY);}
-ctx.lineTo(currentViewX,currentViewY);break;case ChartSeriesComponent.BACKGROUND:if(previousViewX!==undefined)
-ctx.lineTo(currentViewX,previousViewY);if(currentSelectionState!==lastSelectionState){if(previousViewX!==undefined){var previousBaseStepViewX=currentViewX;for(var j=baseSteps.length-1;j>=0;j--){var baseStep=baseSteps[j];var baseStepViewX=baseStep.viewX;var baseStepViewY=baseStep.viewY;ctx.lineTo(previousBaseStepViewX,baseStepViewY);ctx.lineTo(baseStepViewX,baseStepViewY);previousBaseStepViewX=baseStepViewX;}
+var currentSelectionState=currentPoint.selectionState;if(currentSelectionState!==lastSelectionState){var opacity=currentSelectionState===SelectionState.SELECTED?1:squareOpacity;currentStateSeriesColor=EventPresenter.getCounterSeriesColor(this.colorId_,currentSelectionState,opacity);}
+switch(component){case ChartSeriesComponent.DOTS:if(currentPoint.dotLetter){ctx.fillStyle=unselectedSeriesColor;ctx.strokeStyle=ColorScheme.getColorForReservedNameAsString('black');ctx.beginPath();ctx.arc(currentViewX,currentViewY,letterDotRadius+DOT_LETTER_RADIUS_PADDING_PX,0,2*Math.PI);ctx.fill();if(currentSelectionState===SelectionState.SELECTED){ctx.lineWidth=DOT_LETTER_SELECTED_OUTLINE_WIDTH_PX;ctx.strokeStyle=ColorScheme.getColorForReservedNameAsString('olive');ctx.stroke();ctx.beginPath();ctx.arc(currentViewX,currentViewY,letterDotRadius,0,2*Math.PI);ctx.lineWidth=DOT_LETTER_SELECTED_OUTLINE_DETAIL_WIDTH_PX;ctx.strokeStyle=ColorScheme.getColorForReservedNameAsString('yellow');ctx.stroke();}else{ctx.lineWidth=DOT_LETTER_UNSELECTED_OUTLINE_WIDTH_PX;ctx.strokeStyle=ColorScheme.getColorForReservedNameAsString('black');ctx.stroke();}
+ctx.fillStyle=ColorScheme.getColorForReservedNameAsString('white');ctx.fillText(currentPoint.dotLetter,currentViewX,currentViewY);}else{ctx.strokeStyle=unselectedSeriesColor;ctx.lineWidth=pixelRatio;if(currentSelectionState===SelectionState.SELECTED){if(this.solidSelectedDots_){ctx.fillStyle=ctx.strokeStyle;}else{ctx.fillStyle=currentStateSeriesColor;}
+ctx.beginPath();ctx.arc(currentViewX,currentViewY,selectedCircleRadius,0,2*Math.PI);ctx.fill();ctx.stroke();}else if(squareOpacity>0){ctx.fillStyle=currentStateSeriesColor;ctx.fillRect(currentViewX-squareHalfSize,currentViewY-squareHalfSize,squareSize,squareSize);}}
+break;case ChartSeriesComponent.LINE:if(previousViewX===undefined){ctx.beginPath();ctx.moveTo(currentViewX,currentViewY);}else if(this.stepGraph_){ctx.lineTo(currentViewX,previousViewY);}
+ctx.lineTo(currentViewX,currentViewY);break;case ChartSeriesComponent.BACKGROUND:if(previousViewX!==undefined&&this.stepGraph_){ctx.lineTo(currentViewX,previousViewY);}else{ctx.lineTo(currentViewX,currentViewY);}
+if(currentSelectionState!==lastSelectionState){if(previousViewX!==undefined){var previousBaseStepViewX=currentViewX;for(var j=baseSteps.length-1;j>=0;j--){var baseStep=baseSteps[j];var baseStepViewX=baseStep.viewX;var baseStepViewY=baseStep.viewY;ctx.lineTo(previousBaseStepViewX,baseStepViewY);ctx.lineTo(baseStepViewX,baseStepViewY);previousBaseStepViewX=baseStepViewX;}
 ctx.closePath();ctx.fill();}
 ctx.beginPath();ctx.fillStyle=EventPresenter.getCounterSeriesColor(this.colorId_,currentSelectionState,this.backgroundOpacity_);ctx.moveTo(currentViewX,currentViewYBase);baseSteps=[];}
 if(currentViewYBase!==previousViewYBase||currentSelectionState!==lastSelectionState){baseSteps.push({viewX:currentViewX,viewY:currentViewYBase});}
 ctx.lineTo(currentViewX,currentViewY);break;default:throw new Error('Not reachable');}
 previousViewX=currentViewX;previousViewY=currentViewY;previousViewYBase=currentViewYBase;lastSelectionState=currentSelectionState;}
 if(previousViewX!==undefined){switch(component){case ChartSeriesComponent.DOTS:break;case ChartSeriesComponent.LINE:ctx.stroke();break;case ChartSeriesComponent.BACKGROUND:var previousBaseStepViewX=currentViewX;for(var j=baseSteps.length-1;j>=0;j--){var baseStep=baseSteps[j];var baseStepViewX=baseStep.viewX;var baseStepViewY=baseStep.viewY;ctx.lineTo(previousBaseStepViewX,baseStepViewY);ctx.lineTo(baseStepViewX,baseStepViewY);previousBaseStepViewX=baseStepViewX;}
-ctx.closePath();ctx.fill();break;default:throw new Error('Not reachable');}}},addIntersectingEventsInRangeToSelectionInWorldSpace:function(loWX,hiWX,viewPixWidthWorld,selection){var points=this.points;function getPointWidth(point,i){if(i===points.length-1)
-return LAST_POINT_WIDTH*viewPixWidthWorld;var nextPoint=points[i+1];return nextPoint.x-point.x;}
+ctx.closePath();ctx.fill();break;default:throw new Error('Not reachable');}}
+ctx.restore();},addIntersectingEventsInRangeToSelectionInWorldSpace:function(loWX,hiWX,viewPixWidthWorld,selection){var points=this.points;function getPointWidth(point,i){if(i===points.length-1){return LAST_POINT_WIDTH*viewPixWidthWorld;}
+var nextPoint=points[i+1];return nextPoint.x-point.x;}
 function selectPoint(point){point.addToSelection(selection);}
-tr.b.iterateOverIntersectingIntervals(this.points,function(point){return point.x},getPointWidth,loWX,hiWX,selectPoint);},addEventNearToProvidedEventToSelection:function(event,offset,selection){if(this.points===undefined)
-return false;var index=tr.b.findFirstIndexInArray(this.points,function(point){return point.modelItem===event;},this);if(index===-1)
-return false;var newIndex=index+offset;if(newIndex<0||newIndex>=this.points.length)
-return false;this.points[newIndex].addToSelection(selection);return true;},addClosestEventToSelection:function(worldX,worldMaxDist,loY,hiY,selection){if(this.points===undefined)
-return;var item=tr.b.findClosestElementInSortedArray(this.points,function(point){return point.x},worldX,worldMaxDist);if(!item)
-return;item.addToSelection(selection);}};return{ChartSeries:ChartSeries,ChartSeriesType:ChartSeriesType};});'use strict';tr.exportTo('tr.ui.tracks',function(){function ChartTransform(displayTransform,axis,trackWidth,trackHeight,topPadding,bottomPadding,pixelRatio){this.pixelRatio=pixelRatio;this.leftViewX=0;this.rightViewX=trackWidth;this.leftTimestamp=displayTransform.xViewToWorld(this.leftViewX);this.rightTimestamp=displayTransform.xViewToWorld(this.rightViewX);this.displayTransform_=displayTransform;this.outerTopViewY=0;this.innerTopViewY=topPadding;this.innerBottomViewY=trackHeight-bottomPadding;this.outerBottomViewY=trackHeight;this.axis_=axis;this.innerHeight_=this.innerBottomViewY-this.innerTopViewY;};ChartTransform.prototype={worldXToViewX:function(worldX){return this.displayTransform_.xWorldToView(worldX);},viewXToWorldX:function(viewX){return this.displayTransform_.xViewToWorld(viewX);},worldYToViewY:function(worldY){var innerHeightCoefficient=1-this.axis_.valueToUnitRange(worldY);return innerHeightCoefficient*this.innerHeight_+this.innerTopViewY;}};return{ChartTransform:ChartTransform};});'use strict';tr.exportTo('tr.ui.tracks',function(){var ChartTrack=tr.ui.b.define('chart-track',tr.ui.tracks.Track);ChartTrack.prototype={__proto__:tr.ui.tracks.Track.prototype,decorate:function(viewport){tr.ui.tracks.Track.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('chart-track');this.series_=undefined;this.axisGuidToAxisData_=undefined;this.topPadding_=undefined;this.bottomPadding_=undefined;this.heading_=document.createElement('tr-ui-heading');Polymer.dom(this).appendChild(this.heading_);},set heading(heading){this.heading_.heading=heading;},get heading(){return this.heading_.heading;},set tooltip(tooltip){this.heading_.tooltip=tooltip;},get series(){return this.series_;},set series(series){this.series_=series;this.calculateAxisDataAndPadding_();this.invalidateDrawingContainer();},get height(){return window.getComputedStyle(this).height;},set height(height){this.style.height=height;this.invalidateDrawingContainer();},get hasVisibleContent(){return!!this.series&&this.series.length>0;},calculateAxisDataAndPadding_:function(){if(!this.series_){this.axisGuidToAxisData_=undefined;this.topPadding_=undefined;this.bottomPadding_=undefined;return;}
-var axisGuidToAxisData={};var topPadding=0;var bottomPadding=0;this.series_.forEach(function(series){var axis=series.axis;var axisGuid=axis.guid;if(!(axisGuid in axisGuidToAxisData)){axisGuidToAxisData[axisGuid]={axis:axis,series:[]};}
-axisGuidToAxisData[axisGuid].series.push(series);topPadding=Math.max(topPadding,series.topPadding);bottomPadding=Math.max(bottomPadding,series.bottomPadding);},this);this.axisGuidToAxisData_=axisGuidToAxisData;this.topPadding_=topPadding;this.bottomPadding_=bottomPadding;},draw:function(type,viewLWorld,viewRWorld){switch(type){case tr.ui.tracks.DrawType.GENERAL_EVENT:this.drawChart_(viewLWorld,viewRWorld);break;}},drawChart_:function(viewLWorld,viewRWorld){if(!this.series_)
-return;var ctx=this.context();var displayTransform=this.viewport.currentDisplayTransform;var pixelRatio=window.devicePixelRatio||1;var bounds=this.getBoundingClientRect();var highDetails=this.viewport.highDetails;var width=bounds.width*pixelRatio;var height=bounds.height*pixelRatio;var topPadding=this.topPadding_*pixelRatio;var bottomPadding=this.bottomPadding_*pixelRatio;ctx.save();ctx.beginPath();ctx.rect(0,0,width,height);ctx.clip();this.series_.forEach(function(series){var chartTransform=new tr.ui.tracks.ChartTransform(displayTransform,series.axis,width,height,topPadding,bottomPadding,pixelRatio);series.draw(ctx,chartTransform,highDetails);},this);ctx.restore();},addEventsToTrackMap:function(eventToTrackMap){this.series_.forEach(function(series){series.points.forEach(function(point){point.addToTrackMap(eventToTrackMap,this);},this);},this);},addIntersectingEventsInRangeToSelectionInWorldSpace:function(loWX,hiWX,viewPixWidthWorld,selection){this.series_.forEach(function(series){series.addIntersectingEventsInRangeToSelectionInWorldSpace(loWX,hiWX,viewPixWidthWorld,selection);},this);},addEventNearToProvidedEventToSelection:function(event,offset,selection){var foundItem=false;this.series_.forEach(function(series){foundItem=foundItem||series.addEventNearToProvidedEventToSelection(event,offset,selection);},this);return foundItem;},addAllEventsMatchingFilterToSelection:function(filter,selection){},addClosestEventToSelection:function(worldX,worldMaxDist,loY,hiY,selection){this.series_.forEach(function(series){series.addClosestEventToSelection(worldX,worldMaxDist,loY,hiY,selection);},this);},autoSetAllAxes:function(opt_config){tr.b.iterItems(this.axisGuidToAxisData_,function(axisGuid,axisData){var axis=axisData.axis;var series=axisData.series;axis.autoSetFromSeries(series,opt_config);},this);},autoSetAxis:function(axis,opt_config){var series=this.axisGuidToAxisData_[axis.guid].series;axis.autoSetFromSeries(series,opt_config);}};return{ChartTrack:ChartTrack};});'use strict';tr.exportTo('tr.ui.tracks',function(){var ColorScheme=tr.b.ColorScheme;var ChartTrack=tr.ui.tracks.ChartTrack;var MAX_CPU_TRACK_INTERVAL_COUNT=100000;var CpuUsageTrack=tr.ui.b.define('cpu-usage-track',ChartTrack);CpuUsageTrack.prototype={__proto__:ChartTrack.prototype,DEFAULT_INTERVAL:5,decorate:function(viewport){ChartTrack.prototype.decorate.call(this,viewport);this.classList.add('cpu-usage-track');this.heading='CPU usage';this.cpuUsageSeries_=undefined;},initialize:function(model,interval){if(interval!==undefined)
-this.interval_=interval;else
-this.interval_=this.DEFAULT_INTERVAL;if(model!==undefined)
-this.cpuUsageSeries_=this.computeCpuUsage_(model);else
-this.cpuUsageSeries_=undefined;this.series=this.buildChartSeries_();this.autoSetAllAxes({expandMax:true});},computeCpuUsage_:function(model){var intervalCount=Math.ceil(model.bounds.max/this.interval_);if(intervalCount>MAX_CPU_TRACK_INTERVAL_COUNT){throw new Error('The trace is too long or the CPU usage counter '+'interval is too small, leading to too many CPU usage intervals.');}
-var cpuUsage=undefined;if(intervalCount>0){tr.b.iterItems(model.processes,function(pid,process){for(var e of process.getDescendantEvents()){if(!(e instanceof tr.model.ThreadSlice)||e.duration===0||e.cpuDuration===undefined){continue;}
-if(e.selfTime===0||e.selfTime===undefined||e.cpuSelfTime===undefined){continue;}
-var cpuSelfTimeRatio=e.cpuSelfTime/e.selfTime;cpuSelfTimeRatio=Math.max(0,cpuSelfTimeRatio);cpuSelfTimeRatio=Math.min(1,cpuSelfTimeRatio);if(cpuUsage===undefined)
-cpuUsage=new Array(intervalCount).fill(0);var lastTime=e.start;e.subSlices.forEach(function(slice){this.addCPUUsageOverInterval_(cpuUsage,cpuSelfTimeRatio,lastTime,slice.start);lastTime=slice.end;},this);this.addCPUUsageOverInterval_(cpuUsage,cpuSelfTimeRatio,lastTime,e.end);}},this);}
-return cpuUsage||[];},addCPUUsageOverInterval_:function(cpuUsageArray,cpuUsage,start,end){if(start>=end)
-return;var interval=this.interval_;var startIndex=Math.floor(start/interval);var endIndex=Math.ceil(end/interval)-1;var cpuUsagePerTime=cpuUsage/interval;for(var i=startIndex;i<endIndex;i++)
-cpuUsageArray[i]+=cpuUsage;cpuUsageArray[startIndex]-=cpuUsagePerTime*(start-startIndex*interval);cpuUsageArray[endIndex]+=cpuUsagePerTime*(end-endIndex*interval);},get hasVisibleContent(){return!!this.cpuUsageSeries_&&this.cpuUsageSeries_.length>0;},addContainersToTrackMap:function(containerToTrackMap){containerToTrackMap.addContainer(this.series_,this);},buildChartSeries_:function(){if(!this.hasVisibleContent)
-return[];var axis=new tr.ui.tracks.ChartAxis(0,undefined);var pts=new Array(this.cpuUsageSeries_.length+1);for(var i=0;i<=this.cpuUsageSeries_.length;i++){pts[i]=new tr.ui.tracks.ChartPoint(undefined,this.interval_*i,this.cpuUsageSeries_[i]||0);}
-var renderingConfig={chartType:tr.ui.tracks.ChartSeriesType.AREA,colorId:ColorScheme.getColorIdForGeneralPurposeString(this.heading)};return[new tr.ui.tracks.ChartSeries(pts,axis,renderingConfig)];}};return{CpuUsageTrack:CpuUsageTrack};});'use strict';tr.exportTo('tr.ui.tracks',function(){var ColorScheme=tr.b.ColorScheme;var ChartTrack=tr.ui.tracks.ChartTrack;var PowerSeriesTrack=tr.ui.b.define('power-series-track',ChartTrack);PowerSeriesTrack.prototype={__proto__:ChartTrack.prototype,decorate:function(viewport){ChartTrack.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('power-series-track');this.heading='Power';this.powerSeries_=undefined;},set powerSeries(powerSeries){this.powerSeries_=powerSeries;this.series=this.buildChartSeries_();this.autoSetAllAxes({expandMax:true});},get hasVisibleContent(){return(this.powerSeries_&&this.powerSeries_.samples.length>0);},addContainersToTrackMap:function(containerToTrackMap){containerToTrackMap.addContainer(this.powerSeries_,this);},buildChartSeries_:function(){if(!this.hasVisibleContent)
-return[];var axis=new tr.ui.tracks.ChartAxis(0,undefined);var pts=this.powerSeries_.samples.map(function(smpl){return new tr.ui.tracks.ChartPoint(smpl,smpl.start,smpl.powerInW);});var renderingConfig={chartType:tr.ui.tracks.ChartSeriesType.AREA,colorId:ColorScheme.getColorIdForGeneralPurposeString(this.heading)};return[new tr.ui.tracks.ChartSeries(pts,axis,renderingConfig)];}};return{PowerSeriesTrack:PowerSeriesTrack};});'use strict';tr.exportTo('tr.ui.tracks',function(){var SpacingTrack=tr.ui.b.define('spacing-track',tr.ui.tracks.Track);SpacingTrack.prototype={__proto__:tr.ui.tracks.Track.prototype,decorate:function(viewport){tr.ui.tracks.Track.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('spacing-track');this.heading_=document.createElement('tr-ui-heading');Polymer.dom(this).appendChild(this.heading_);},addAllEventsMatchingFilterToSelection:function(filter,selection){}};return{SpacingTrack:SpacingTrack};});'use strict';tr.exportTo('tr.ui.tracks',function(){var ContainerTrack=tr.ui.tracks.ContainerTrack;var DeviceTrack=tr.ui.b.define('device-track',ContainerTrack);DeviceTrack.prototype={__proto__:ContainerTrack.prototype,decorate:function(viewport){ContainerTrack.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('device-track');this.device_=undefined;this.powerSeriesTrack_=undefined;},get device(){return this.device_;},set device(device){this.device_=device;this.updateContents_();},get powerSeriesTrack(){return this.powerSeriesTrack_;},get hasVisibleContent(){return(this.powerSeriesTrack_&&this.powerSeriesTrack_.hasVisibleContent);},addContainersToTrackMap:function(containerToTrackMap){tr.ui.tracks.ContainerTrack.prototype.addContainersToTrackMap.call(this,containerToTrackMap);containerToTrackMap.addContainer(this.device,this);},addEventsToTrackMap:function(eventToTrackMap){this.tracks_.forEach(function(track){track.addEventsToTrackMap(eventToTrackMap);});},appendPowerSeriesTrack_:function(){this.powerSeriesTrack_=new tr.ui.tracks.PowerSeriesTrack(this.viewport);this.powerSeriesTrack_.powerSeries=this.device.powerSeries;if(this.powerSeriesTrack_.hasVisibleContent){Polymer.dom(this).appendChild(this.powerSeriesTrack_);Polymer.dom(this).appendChild(new tr.ui.tracks.SpacingTrack(this.viewport));}},updateContents_:function(){this.clearTracks_();this.appendPowerSeriesTrack_();}};return{DeviceTrack:DeviceTrack};});'use strict';tr.exportTo('tr.ui.tracks',function(){var ColorScheme=tr.b.ColorScheme;var DISPLAYED_SIZE_NUMERIC_NAME=tr.model.MemoryAllocatorDump.DISPLAYED_SIZE_NUMERIC_NAME;var BACKGROUND=tr.model.ContainerMemoryDump.LevelOfDetail.BACKGROUND;var LIGHT=tr.model.ContainerMemoryDump.LevelOfDetail.LIGHT;var DETAILED=tr.model.ContainerMemoryDump.LevelOfDetail.DETAILED;function addDictionary(dstDict,srcDict){tr.b.iterItems(srcDict,function(key,value){var existingValue=dstDict[key];if(existingValue===undefined)
-existingValue=0;dstDict[key]=existingValue+value;});}
-function getProcessMemoryDumpAllocatorSizes(processMemoryDump){var allocatorDumps=processMemoryDump.memoryAllocatorDumps;if(allocatorDumps===undefined)
-return{};var allocatorSizes={};allocatorDumps.forEach(function(allocatorDump){if(allocatorDump.fullName==='tracing')
-return;var allocatorSize=allocatorDump.numerics[DISPLAYED_SIZE_NUMERIC_NAME];if(allocatorSize===undefined)
-return;var allocatorSizeValue=allocatorSize.value;if(allocatorSizeValue===undefined)
-return;allocatorSizes[allocatorDump.fullName]=allocatorSizeValue;});return allocatorSizes;};function getGlobalMemoryDumpAllocatorSizes(globalMemoryDump){var globalAllocatorSizes={};tr.b.iterItems(globalMemoryDump.processMemoryDumps,function(pid,processMemoryDump){addDictionary(globalAllocatorSizes,getProcessMemoryDumpAllocatorSizes(processMemoryDump));});return globalAllocatorSizes;}
-function buildAllocatedMemoryChartSeries(memoryDumps,memoryDumpToAllocatorSizesFn){var allocatorNameToPoints={};var dumpsData=memoryDumps.map(function(memoryDump){var allocatorSizes=memoryDumpToAllocatorSizesFn(memoryDump);tr.b.iterItems(allocatorSizes,function(allocatorName){allocatorNameToPoints[allocatorName]=[];});return{dump:memoryDump,sizes:allocatorSizes};});if(Object.keys(allocatorNameToPoints).length===0)
-return undefined;dumpsData.forEach(function(dumpData){var memoryDump=dumpData.dump;var allocatorSizes=dumpData.sizes;tr.b.iterItems(allocatorNameToPoints,function(allocatorName,points){var allocatorSize=allocatorSizes[allocatorName]||0;points.push(new tr.ui.tracks.ChartPoint(memoryDump,memoryDump.start,allocatorSize));});});var axis=new tr.ui.tracks.ChartAxis(0);var series=[];tr.b.iterItems(allocatorNameToPoints,function(allocatorName,points){var colorId=ColorScheme.getColorIdForGeneralPurposeString(allocatorName);var renderingConfig={chartType:tr.ui.tracks.ChartSeriesType.LINE,colorId:colorId};series.push(new tr.ui.tracks.ChartSeries(points,axis,renderingConfig));});return series;}
+tr.b.math.iterateOverIntersectingIntervals(this.points,function(point){return point.x;},getPointWidth,loWX,hiWX,selectPoint);},addEventNearToProvidedEventToSelection:function(event,offset,selection){if(this.points===undefined)return false;var index=tr.b.findFirstIndexInArray(this.points,function(point){return point.modelItem===event;},this);if(index===-1)return false;var newIndex=index+offset;if(newIndex<0||newIndex>=this.points.length)return false;this.points[newIndex].addToSelection(selection);return true;},addClosestEventToSelection:function(worldX,worldMaxDist,loY,hiY,selection){if(this.points===undefined)return;var item=tr.b.math.findClosestElementInSortedArray(this.points,function(point){return point.x;},worldX,worldMaxDist);if(!item)return;item.addToSelection(selection);}};return{ChartSeries,ChartSeriesType,};});'use strict';tr.exportTo('tr.ui.tracks',function(){var ColorScheme=tr.b.ColorScheme;var IDEAL_MAJOR_MARK_HEIGHT_PX=30;var AXIS_LABLE_MARGIN_PX=10;var AXIS_LABLE_FONT_SIZE_PX=9;var AXIS_LABLE_FONT='Arial';function ChartSeriesYAxis(opt_min,opt_max){this.guid_=tr.b.GUID.allocateSimple();this.bounds=new tr.b.math.Range();if(opt_min!==undefined)this.bounds.addValue(opt_min);if(opt_max!==undefined)this.bounds.addValue(opt_max);}
+ChartSeriesYAxis.prototype={get guid(){return this.guid_;},valueToUnitRange:function(value){if(this.bounds.isEmpty){throw new Error('Chart series y-axis bounds are empty');}
+var bounds=this.bounds;if(bounds.range===0)return 0;return(value-bounds.min)/bounds.range;},unitRangeToValue:function(unitRange){if(this.bounds.isEmpty){throw new Error('Chart series y-axis bounds are empty');}
+return unitRange*this.bounds.range+this.bounds.min;},autoSetFromSeries:function(series,opt_config){var range=new tr.b.math.Range();series.forEach(function(s){range.addRange(s.range);},this);this.autoSetFromRange(range,opt_config);},autoSetFromRange:function(range,opt_config){if(range.isEmpty)return;var bounds=this.bounds;if(bounds.isEmpty){bounds.addRange(range);return;}
+if(!opt_config)return;var useRangeMin=(opt_config.expandMin&&range.min<bounds.min||opt_config.shrinkMin&&range.min>bounds.min);var useRangeMax=(opt_config.expandMax&&range.max>bounds.max||opt_config.shrinkMax&&range.max<bounds.max);if(!useRangeMin&&!useRangeMax)return;if(useRangeMin&&useRangeMax){bounds.min=range.min;bounds.max=range.max;return;}
+if(useRangeMin){bounds.min=Math.min(range.min,bounds.max);}else{bounds.max=Math.max(range.max,bounds.min);}},majorMarkHeightWorld_:function(transform,pixelRatio){var idealMajorMarkHeightPx=IDEAL_MAJOR_MARK_HEIGHT_PX*pixelRatio;var idealMajorMarkHeightWorld=transform.vectorToWorldDistance(idealMajorMarkHeightPx);return tr.b.math.preferredNumberLargerThanMin(idealMajorMarkHeightWorld);},draw:function(ctx,transform,showYAxisLabels,showYGridLines){if(!showYAxisLabels&&!showYGridLines)return;var pixelRatio=transform.pixelRatio;var viewTop=transform.outerTopViewY;var worldTop=transform.viewYToWorldY(viewTop);var viewBottom=transform.outerBottomViewY;var viewHeight=viewBottom-viewTop;var viewLeft=transform.leftViewX;var viewRight=transform.rightViewX;var labelLeft=transform.leftYLabel;ctx.save();ctx.lineWidth=pixelRatio;ctx.fillStyle=ColorScheme.getColorForReservedNameAsString('black');ctx.textAlign='left';ctx.textBaseline='center';ctx.font=(AXIS_LABLE_FONT_SIZE_PX*pixelRatio)+'px '+AXIS_LABLE_FONT;ctx.beginPath();ctx.strokeStyle=ColorScheme.getColorForReservedNameAsString('black');tr.ui.b.drawLine(ctx,viewLeft,viewTop,viewLeft,viewBottom,viewLeft);ctx.stroke();ctx.closePath();ctx.beginPath();ctx.strokeStyle=ColorScheme.getColorForReservedNameAsString('grey');var majorMarkHeight=this.majorMarkHeightWorld_(transform,pixelRatio);var maxMajorMark=Math.max(transform.viewYToWorldY(viewTop),Math.abs(transform.viewYToWorldY(viewBottom)));for(var curWorldY=0;curWorldY<=maxMajorMark;curWorldY+=majorMarkHeight){var roundedUnitValue=Math.floor(curWorldY*1000000)/1000000;var curViewYPositive=transform.worldYToViewY(curWorldY);if(curViewYPositive>=viewTop){if(showYAxisLabels){ctx.fillText(roundedUnitValue,viewLeft+AXIS_LABLE_MARGIN_PX,curViewYPositive-AXIS_LABLE_MARGIN_PX);}
+if(showYGridLines){tr.ui.b.drawLine(ctx,viewLeft,curViewYPositive,viewRight,curViewYPositive);}}
+var curViewYNegative=transform.worldYToViewY(-1*curWorldY);if(curViewYNegative<=viewBottom){if(showYAxisLabels){ctx.fillText(roundedUnitValue,viewLeft+AXIS_LABLE_MARGIN_PX,curViewYNegative-AXIS_LABLE_MARGIN_PX);}
+if(showYGridLines){tr.ui.b.drawLine(ctx,viewLeft,curViewYNegative,viewRight,curViewYNegative);}}}
+ctx.stroke();ctx.restore();}};return{ChartSeriesYAxis,};});'use strict';tr.exportTo('tr.ui.tracks',function(){function ChartTransform(displayTransform,axis,trackWidth,trackHeight,topPadding,bottomPadding,pixelRatio){this.pixelRatio=pixelRatio;this.leftViewX=0;this.rightViewX=trackWidth;this.leftTimestamp=displayTransform.xViewToWorld(this.leftViewX);this.rightTimestamp=displayTransform.xViewToWorld(this.rightViewX);this.displayTransform_=displayTransform;this.outerTopViewY=0;this.innerTopViewY=topPadding;this.innerBottomViewY=trackHeight-bottomPadding;this.outerBottomViewY=trackHeight;this.axis_=axis;this.innerHeight_=this.innerBottomViewY-this.innerTopViewY;}
+ChartTransform.prototype={worldXToViewX:function(worldX){return this.displayTransform_.xWorldToView(worldX);},viewXToWorldX:function(viewX){return this.displayTransform_.xViewToWorld(viewX);},vectorToWorldDistance:function(viewY){return this.axis_.bounds.range*Math.abs(viewY/this.innerHeight_);},viewYToWorldY:function(viewY){return this.axis_.unitRangeToValue(1-(viewY-this.innerTopViewY)/this.innerHeight_);},worldYToViewY:function(worldY){var innerHeightCoefficient=1-this.axis_.valueToUnitRange(worldY);return innerHeightCoefficient*this.innerHeight_+this.innerTopViewY;}};return{ChartTransform,};});'use strict';tr.exportTo('tr.ui.tracks',function(){var ChartTrack=tr.ui.b.define('chart-track',tr.ui.tracks.Track);ChartTrack.prototype={__proto__:tr.ui.tracks.Track.prototype,decorate:function(viewport){tr.ui.tracks.Track.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('chart-track');this.series_=undefined;this.axes_=undefined;this.axisGuidToAxisData_=undefined;this.topPadding_=undefined;this.bottomPadding_=undefined;this.showYAxisLabels_=undefined;this.showGridLines_=undefined;this.heading_=document.createElement('tr-ui-b-heading');Polymer.dom(this).appendChild(this.heading_);},set heading(heading){this.heading_.heading=heading;},get heading(){return this.heading_.heading;},set tooltip(tooltip){this.heading_.tooltip=tooltip;},get series(){return this.series_;},set series(series){this.series_=series;this.calculateAxisDataAndPadding_();this.invalidateDrawingContainer();},get height(){return window.getComputedStyle(this).height;},set height(height){this.style.height=height;this.invalidateDrawingContainer();},get showYAxisLabels(){return this.showYAxisLabels_;},set showYAxisLabels(showYAxisLabels){this.showYAxisLabels_=showYAxisLabels;this.invalidateDrawingContainer();},get showGridLines(){return this.showGridLines_;},set showGridLines(showGridLines){this.showGridLines_=showGridLines;this.invalidateDrawingContainer();},get hasVisibleContent(){return!!this.series&&this.series.length>0;},calculateAxisDataAndPadding_:function(){if(!this.series_){this.axes_=undefined;this.axisGuidToAxisData_=undefined;this.topPadding_=undefined;this.bottomPadding_=undefined;return;}
+var axisGuidToAxisData={};var topPadding=0;var bottomPadding=0;this.series_.forEach(function(series){var seriesYAxis=series.seriesYAxis;var axisGuid=seriesYAxis.guid;if(!(axisGuid in axisGuidToAxisData)){axisGuidToAxisData[axisGuid]={axis:seriesYAxis,series:[]};if(!this.axes_)this.axes_=[];this.axes_.push(seriesYAxis);}
+axisGuidToAxisData[axisGuid].series.push(series);topPadding=Math.max(topPadding,series.topPadding);bottomPadding=Math.max(bottomPadding,series.bottomPadding);},this);this.axisGuidToAxisData_=axisGuidToAxisData;this.topPadding_=topPadding;this.bottomPadding_=bottomPadding;},draw:function(type,viewLWorld,viewRWorld){switch(type){case tr.ui.tracks.DrawType.GENERAL_EVENT:this.drawChart_(viewLWorld,viewRWorld);break;}},drawChart_:function(viewLWorld,viewRWorld){if(!this.series_)return;var ctx=this.context();var displayTransform=this.viewport.currentDisplayTransform;var pixelRatio=window.devicePixelRatio||1;var bounds=this.getBoundingClientRect();var highDetails=this.viewport.highDetails;var width=bounds.width*pixelRatio;var height=bounds.height*pixelRatio;var topPadding=this.topPadding_*pixelRatio;var bottomPadding=this.bottomPadding_*pixelRatio;ctx.save();ctx.beginPath();ctx.rect(0,0,width,height);ctx.clip();if(this.axes_){if((this.showGridLines_||this.showYAxisLabels_)&&this.axes_.length>1){throw new Error('Only one axis allowed when showing grid lines.');}
+for(var yAxis of this.axes_){var chartTransform=new tr.ui.tracks.ChartTransform(displayTransform,yAxis,width,height,topPadding,bottomPadding,pixelRatio);yAxis.draw(ctx,chartTransform,this.showYAxisLabels_,this.showGridLines_);}}
+for(var series of this.series){var chartTransform=new tr.ui.tracks.ChartTransform(displayTransform,series.seriesYAxis,width,height,topPadding,bottomPadding,pixelRatio);series.draw(ctx,chartTransform,highDetails);}
+ctx.restore();},addEventsToTrackMap:function(eventToTrackMap){this.series_.forEach(function(series){series.points.forEach(function(point){point.addToTrackMap(eventToTrackMap,this);},this);},this);},addIntersectingEventsInRangeToSelectionInWorldSpace:function(loWX,hiWX,viewPixWidthWorld,selection){this.series_.forEach(function(series){series.addIntersectingEventsInRangeToSelectionInWorldSpace(loWX,hiWX,viewPixWidthWorld,selection);},this);},addEventNearToProvidedEventToSelection:function(event,offset,selection){var foundItem=false;this.series_.forEach(function(series){foundItem=foundItem||series.addEventNearToProvidedEventToSelection(event,offset,selection);},this);return foundItem;},addAllEventsMatchingFilterToSelection:function(filter,selection){},addClosestEventToSelection:function(worldX,worldMaxDist,loY,hiY,selection){this.series_.forEach(function(series){series.addClosestEventToSelection(worldX,worldMaxDist,loY,hiY,selection);},this);},autoSetAllAxes:function(opt_config){for(var axisData of Object.values(this.axisGuidToAxisData_)){var seriesYAxis=axisData.axis;var series=axisData.series;seriesYAxis.autoSetFromSeries(series,opt_config);}},autoSetAxis:function(seriesYAxis,opt_config){var series=this.axisGuidToAxisData_[seriesYAxis.guid].series;seriesYAxis.autoSetFromSeries(series,opt_config);}};return{ChartTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){var ColorScheme=tr.b.ColorScheme;var ChartTrack=tr.ui.tracks.ChartTrack;var CpuUsageTrack=tr.ui.b.define('cpu-usage-track',ChartTrack);CpuUsageTrack.prototype={__proto__:ChartTrack.prototype,decorate:function(viewport){ChartTrack.prototype.decorate.call(this,viewport);this.classList.add('cpu-usage-track');this.heading='CPU usage';this.cpuUsageSeries_=undefined;},initialize:function(model){if(model!==undefined){this.cpuUsageSeries_=model.device.cpuUsageSeries;}else{this.cpuUsageSeries_=undefined;}
+this.series=this.buildChartSeries_();this.autoSetAllAxes({expandMax:true});},get hasVisibleContent(){return!!this.cpuUsageSeries_&&this.cpuUsageSeries_.samples.length>0;},addContainersToTrackMap:function(containerToTrackMap){containerToTrackMap.addContainer(this.series_,this);},buildChartSeries_:function(yAxis,color){if(!this.hasVisibleContent)return[];var yAxis=new tr.ui.tracks.ChartSeriesYAxis(0,undefined);var usageSamples=this.cpuUsageSeries_.samples;var pts=new Array(usageSamples.length+1);for(var i=0;i<usageSamples.length;i++){pts[i]=new tr.ui.tracks.ChartPoint(undefined,usageSamples[i].start,usageSamples[i].usage);}
+pts[usageSamples.length]=new tr.ui.tracks.ChartPoint(undefined,usageSamples[usageSamples.length-1].start,0);var renderingConfig={chartType:tr.ui.tracks.ChartSeriesType.AREA,colorId:color};return[new tr.ui.tracks.ChartSeries(pts,yAxis,renderingConfig)];},};return{CpuUsageTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){var ColorScheme=tr.b.ColorScheme;var ChartTrack=tr.ui.tracks.ChartTrack;var PowerSeriesTrack=tr.ui.b.define('power-series-track',ChartTrack);PowerSeriesTrack.prototype={__proto__:ChartTrack.prototype,decorate:function(viewport){ChartTrack.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('power-series-track');this.heading='Power';this.powerSeries_=undefined;},set powerSeries(powerSeries){this.powerSeries_=powerSeries;this.series=this.buildChartSeries_();this.autoSetAllAxes({expandMax:true});},get hasVisibleContent(){return(this.powerSeries_&&this.powerSeries_.samples.length>0);},addContainersToTrackMap:function(containerToTrackMap){containerToTrackMap.addContainer(this.powerSeries_,this);},buildChartSeries_:function(){if(!this.hasVisibleContent)return[];var seriesYAxis=new tr.ui.tracks.ChartSeriesYAxis(0,undefined);var pts=this.powerSeries_.samples.map(function(smpl){return new tr.ui.tracks.ChartPoint(smpl,smpl.start,smpl.powerInW);});var renderingConfig={chartType:tr.ui.tracks.ChartSeriesType.AREA,colorId:ColorScheme.getColorIdForGeneralPurposeString(this.heading)};return[new tr.ui.tracks.ChartSeries(pts,seriesYAxis,renderingConfig)];}};return{PowerSeriesTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){var SpacingTrack=tr.ui.b.define('spacing-track',tr.ui.tracks.Track);SpacingTrack.prototype={__proto__:tr.ui.tracks.Track.prototype,decorate:function(viewport){tr.ui.tracks.Track.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('spacing-track');this.heading_=document.createElement('tr-ui-b-heading');Polymer.dom(this).appendChild(this.heading_);},addAllEventsMatchingFilterToSelection:function(filter,selection){}};return{SpacingTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){var ContainerTrack=tr.ui.tracks.ContainerTrack;var DeviceTrack=tr.ui.b.define('device-track',ContainerTrack);DeviceTrack.prototype={__proto__:ContainerTrack.prototype,decorate:function(viewport){ContainerTrack.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('device-track');this.device_=undefined;this.powerSeriesTrack_=undefined;},get device(){return this.device_;},set device(device){this.device_=device;this.updateContents_();},get powerSeriesTrack(){return this.powerSeriesTrack_;},get hasVisibleContent(){return(this.powerSeriesTrack_&&this.powerSeriesTrack_.hasVisibleContent);},addContainersToTrackMap:function(containerToTrackMap){tr.ui.tracks.ContainerTrack.prototype.addContainersToTrackMap.call(this,containerToTrackMap);containerToTrackMap.addContainer(this.device,this);},addEventsToTrackMap:function(eventToTrackMap){this.tracks_.forEach(function(track){track.addEventsToTrackMap(eventToTrackMap);});},appendPowerSeriesTrack_:function(){this.powerSeriesTrack_=new tr.ui.tracks.PowerSeriesTrack(this.viewport);this.powerSeriesTrack_.powerSeries=this.device.powerSeries;if(this.powerSeriesTrack_.hasVisibleContent){Polymer.dom(this).appendChild(this.powerSeriesTrack_);Polymer.dom(this).appendChild(new tr.ui.tracks.SpacingTrack(this.viewport));}},updateContents_:function(){this.clearTracks_();this.appendPowerSeriesTrack_();}};return{DeviceTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){var ColorScheme=tr.b.ColorScheme;var DISPLAYED_SIZE_NUMERIC_NAME=tr.model.MemoryAllocatorDump.DISPLAYED_SIZE_NUMERIC_NAME;var BACKGROUND=tr.model.ContainerMemoryDump.LevelOfDetail.BACKGROUND;var LIGHT=tr.model.ContainerMemoryDump.LevelOfDetail.LIGHT;var DETAILED=tr.model.ContainerMemoryDump.LevelOfDetail.DETAILED;function extractGlobalMemoryDumpUsedSizes(globalMemoryDump,addSize){for(var[pid,pmd]of
+Object.entries(globalMemoryDump.processMemoryDumps)){var mostRecentVmRegions=pmd.mostRecentVmRegions;if(mostRecentVmRegions===undefined)continue;addSize(pid,mostRecentVmRegions.byteStats.proportionalResident||0,pmd.process.userFriendlyName);}}
+function extractProcessMemoryDumpAllocatorSizes(processMemoryDump,addSize){var allocatorDumps=processMemoryDump.memoryAllocatorDumps;if(allocatorDumps===undefined)return;allocatorDumps.forEach(function(allocatorDump){if(allocatorDump.fullName==='tracing')return;var allocatorSize=allocatorDump.numerics[DISPLAYED_SIZE_NUMERIC_NAME];if(allocatorSize===undefined)return;var allocatorSizeValue=allocatorSize.value;if(allocatorSizeValue===undefined)return;addSize(allocatorDump.fullName,allocatorSizeValue);});}
+function extractGlobalMemoryDumpAllocatorSizes(globalMemoryDump,addSize){for(var pmd of Object.values(globalMemoryDump.processMemoryDumps)){extractProcessMemoryDumpAllocatorSizes(pmd,addSize);}}
+function buildMemoryChartSeries(memoryDumps,dumpSizeExtractor){var dumpCount=memoryDumps.length;var idToTimestampToPoint={};var idToName={};memoryDumps.forEach(function(dump,index){dumpSizeExtractor(dump,function addSize(id,size,opt_name){var timestampToPoint=idToTimestampToPoint[id];if(timestampToPoint===undefined){idToTimestampToPoint[id]=timestampToPoint=new Array(dumpCount);for(var i=0;i<dumpCount;i++){var modelItem=memoryDumps[i];timestampToPoint[i]=new tr.ui.tracks.ChartPoint(modelItem,modelItem.start,0);}}
+timestampToPoint[index].y+=size;if(opt_name!==undefined)idToName[id]=opt_name;});});var ids=Object.keys(idToTimestampToPoint);if(ids.length===0)return undefined;ids.sort();for(var i=0;i<dumpCount;i++){var baseSize=0;for(var j=ids.length-1;j>=0;j--){var point=idToTimestampToPoint[ids[j]][i];point.yBase=baseSize;point.y+=baseSize;baseSize=point.y;}}
+var seriesYAxis=new tr.ui.tracks.ChartSeriesYAxis(0);var series=ids.map(function(id){var colorId=ColorScheme.getColorIdForGeneralPurposeString(idToName[id]||id);var renderingConfig={chartType:tr.ui.tracks.ChartSeriesType.AREA,colorId:colorId,backgroundOpacity:0.8};return new tr.ui.tracks.ChartSeries(idToTimestampToPoint[id],seriesYAxis,renderingConfig);});series.reverse();return series;}
 function buildMemoryLetterDots(memoryDumps){var backgroundMemoryColorId=ColorScheme.getColorIdForReservedName('background_memory_dump');var lightMemoryColorId=ColorScheme.getColorIdForReservedName('light_memory_dump');var detailedMemoryColorId=ColorScheme.getColorIdForReservedName('detailed_memory_dump');return memoryDumps.map(function(memoryDump){var memoryColorId;switch(memoryDump.levelOfDetail){case BACKGROUND:memoryColorId=backgroundMemoryColorId;break;case DETAILED:memoryColorId=detailedMemoryColorId;break;case LIGHT:default:memoryColorId=lightMemoryColorId;}
 return new tr.ui.tracks.LetterDot(memoryDump,'M',memoryColorId,memoryDump.start);});}
-function buildGlobalUsedMemoryChartSeries(globalMemoryDumps){var containsVmRegions=globalMemoryDumps.some(function(globalDump){for(var pid in globalDump.processMemoryDumps)
-if(globalDump.processMemoryDumps[pid].mostRecentVmRegions)
-return true;return false;});if(!containsVmRegions)
-return undefined;var pidToProcess={};globalMemoryDumps.forEach(function(globalDump){tr.b.iterItems(globalDump.processMemoryDumps,function(pid,processDump){pidToProcess[pid]=processDump.process;});});var pidToPoints={};tr.b.iterItems(pidToProcess,function(pid,process){pidToPoints[pid]=[];});globalMemoryDumps.forEach(function(globalDump){var pssBase=0;tr.b.iterItems(pidToPoints,function(pid,points){var processMemoryDump=globalDump.processMemoryDumps[pid];var cumulativePss=pssBase;if(processMemoryDump!==undefined){var vmRegions=processMemoryDump.mostRecentVmRegions;if(vmRegions!==undefined)
-cumulativePss+=vmRegions.byteStats.proportionalResident||0;}
-points.push(new tr.ui.tracks.ChartPoint(globalDump,globalDump.start,cumulativePss,pssBase));pssBase=cumulativePss;});});var axis=new tr.ui.tracks.ChartAxis(0);var series=[];tr.b.iterItems(pidToPoints,function(pid,points){var process=pidToProcess[pid];var colorId=ColorScheme.getColorIdForGeneralPurposeString(process.userFriendlyName);var renderingConfig={chartType:tr.ui.tracks.ChartSeriesType.AREA,colorId:colorId,backgroundOpacity:0.8};series.push(new tr.ui.tracks.ChartSeries(points,axis,renderingConfig));});series.reverse();return series;}
-function buildProcessAllocatedMemoryChartSeries(processMemoryDumps){return buildAllocatedMemoryChartSeries(processMemoryDumps,getProcessMemoryDumpAllocatorSizes);}
-function buildGlobalAllocatedMemoryChartSeries(globalMemoryDumps){return buildAllocatedMemoryChartSeries(globalMemoryDumps,getGlobalMemoryDumpAllocatorSizes);}
-return{buildMemoryLetterDots:buildMemoryLetterDots,buildGlobalUsedMemoryChartSeries:buildGlobalUsedMemoryChartSeries,buildProcessAllocatedMemoryChartSeries:buildProcessAllocatedMemoryChartSeries,buildGlobalAllocatedMemoryChartSeries:buildGlobalAllocatedMemoryChartSeries};});'use strict';tr.exportTo('tr.ui.tracks',function(){var USED_MEMORY_TRACK_HEIGHT=50;var ALLOCATED_MEMORY_TRACK_HEIGHT=50;var GlobalMemoryDumpTrack=tr.ui.b.define('global-memory-dump-track',tr.ui.tracks.ContainerTrack);GlobalMemoryDumpTrack.prototype={__proto__:tr.ui.tracks.ContainerTrack.prototype,decorate:function(viewport){tr.ui.tracks.ContainerTrack.prototype.decorate.call(this,viewport);this.memoryDumps_=undefined;},get memoryDumps(){return this.memoryDumps_;},set memoryDumps(memoryDumps){this.memoryDumps_=memoryDumps;this.updateContents_();},updateContents_:function(){this.clearTracks_();if(!this.memoryDumps_||!this.memoryDumps_.length)
-return;this.appendDumpDotsTrack_();this.appendUsedMemoryTrack_();this.appendAllocatedMemoryTrack_();},appendDumpDotsTrack_:function(){var items=tr.ui.tracks.buildMemoryLetterDots(this.memoryDumps_);if(!items)
-return;var track=new tr.ui.tracks.LetterDotTrack(this.viewport);track.heading='Memory Dumps';track.items=items;Polymer.dom(this).appendChild(track);},appendUsedMemoryTrack_:function(){var series=tr.ui.tracks.buildGlobalUsedMemoryChartSeries(this.memoryDumps_);if(!series)
-return;var track=new tr.ui.tracks.ChartTrack(this.viewport);track.heading='Memory per process';track.height=USED_MEMORY_TRACK_HEIGHT+'px';track.series=series;track.autoSetAllAxes({expandMax:true});Polymer.dom(this).appendChild(track);},appendAllocatedMemoryTrack_:function(){var series=tr.ui.tracks.buildGlobalAllocatedMemoryChartSeries(this.memoryDumps_);if(!series)
-return;var track=new tr.ui.tracks.ChartTrack(this.viewport);track.heading='Memory per component';track.height=ALLOCATED_MEMORY_TRACK_HEIGHT+'px';track.series=series;track.autoSetAllAxes({expandMax:true});Polymer.dom(this).appendChild(track);}};return{GlobalMemoryDumpTrack:GlobalMemoryDumpTrack};});'use strict';tr.exportTo('tr.ui.tracks',function(){function Highlighter(viewport){if(viewport===undefined){throw new Error('viewport must be provided');}
-this.viewport_=viewport;};Highlighter.prototype={__proto__:Object.prototype,processModel:function(model){throw new Error('processModel implementation missing');},drawHighlight:function(ctx,dt,viewLWorld,viewRWorld,viewHeight){throw new Error('drawHighlight implementation missing');}};var options=new tr.b.ExtensionRegistryOptions(tr.b.BASIC_REGISTRY_MODE);options.defaultMetadata={};options.mandatoryBaseClass=Highlighter;tr.b.decorateExtensionRegistry(Highlighter,options);return{Highlighter:Highlighter};});'use strict';tr.exportTo('tr.ui.b',function(){function FastRectRenderer(ctx,minRectSize,maxMergeDist,pallette){this.ctx_=ctx;this.minRectSize_=minRectSize;this.maxMergeDist_=maxMergeDist;this.pallette_=pallette;}
-FastRectRenderer.prototype={y_:0,h_:0,merging_:false,mergeStartX_:0,mergeCurRight_:0,mergedColorId_:0,mergedAlpha_:0,setYandH:function(y,h){if(this.y_===y&&this.h_===h)
-return;this.flush();this.y_=y;this.h_=h;},fillRect:function(x,w,colorId,alpha){var r=x+w;if(w<this.minRectSize_){if(r-this.mergeStartX_>this.maxMergeDist_)
-this.flush();if(!this.merging_){this.merging_=true;this.mergeStartX_=x;this.mergeCurRight_=r;this.mergedColorId_=colorId;this.mergedAlpha_=alpha;}else{this.mergeCurRight_=r;if(this.mergedAlpha_<alpha||(this.mergedAlpha_===alpha&&this.mergedColorId_<colorId)){this.mergedAlpha_=alpha;this.mergedColorId_=colorId;}}}else{if(this.merging_)
-this.flush();this.ctx_.fillStyle=this.pallette_[colorId];this.ctx_.globalAlpha=alpha;this.ctx_.fillRect(x,this.y_,w,this.h_);}},flush:function(){if(this.merging_){this.ctx_.fillStyle=this.pallette_[this.mergedColorId_];this.ctx_.globalAlpha=this.mergedAlpha_;this.ctx_.fillRect(this.mergeStartX_,this.y_,this.mergeCurRight_-this.mergeStartX_,this.h_);this.merging_=false;}}};return{FastRectRenderer:FastRectRenderer};});'use strict';tr.exportTo('tr.ui.tracks',function(){var RectTrack=tr.ui.b.define('rect-track',tr.ui.tracks.Track);RectTrack.prototype={__proto__:tr.ui.tracks.Track.prototype,decorate:function(viewport){tr.ui.tracks.Track.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('rect-track');this.asyncStyle_=false;this.rects_=null;this.heading_=document.createElement('tr-ui-heading');Polymer.dom(this).appendChild(this.heading_);},set heading(heading){this.heading_.heading=heading;},get heading(){return this.heading_.heading;},set tooltip(tooltip){this.heading_.tooltip=tooltip;},set selectionGenerator(generator){this.heading_.selectionGenerator=generator;},set expanded(expanded){this.heading_.expanded=!!expanded;},set arrowVisible(arrowVisible){this.heading_.arrowVisible=!!arrowVisible;},get expanded(){return this.heading_.expanded;},get asyncStyle(){return this.asyncStyle_;},set asyncStyle(v){this.asyncStyle_=!!v;},get rects(){return this.rects_;},set rects(rects){this.rects_=rects||[];this.invalidateDrawingContainer();},get height(){return window.getComputedStyle(this).height;},set height(height){this.style.height=height;this.invalidateDrawingContainer();},get hasVisibleContent(){return this.rects_.length>0;},draw:function(type,viewLWorld,viewRWorld){switch(type){case tr.ui.tracks.DrawType.GENERAL_EVENT:this.drawRects_(viewLWorld,viewRWorld);break;}},drawRects_:function(viewLWorld,viewRWorld){var ctx=this.context();ctx.save();var bounds=this.getBoundingClientRect();tr.ui.b.drawSlices(ctx,this.viewport.currentDisplayTransform,viewLWorld,viewRWorld,bounds.height,this.rects_,this.asyncStyle_);ctx.restore();if(bounds.height<=6)
-return;var fontSize,yOffset;if(bounds.height<15){fontSize=6;yOffset=1.0;}else{fontSize=10;yOffset=2.5;}
-tr.ui.b.drawLabels(ctx,this.viewport.currentDisplayTransform,viewLWorld,viewRWorld,this.rects_,this.asyncStyle_,fontSize,yOffset);},addEventsToTrackMap:function(eventToTrackMap){if(this.rects_===undefined||this.rects_===null)
-return;this.rects_.forEach(function(rect){rect.addToTrackMap(eventToTrackMap,this);},this);},addIntersectingEventsInRangeToSelectionInWorldSpace:function(loWX,hiWX,viewPixWidthWorld,selection){function onRect(rect){rect.addToSelection(selection);}
-onRect=onRect.bind(this);var instantEventWidth=2*viewPixWidthWorld;tr.b.iterateOverIntersectingIntervals(this.rects_,function(x){return x.start;},function(x){return x.duration==0?x.duration+instantEventWidth:x.duration;},loWX,hiWX,onRect);},addEventNearToProvidedEventToSelection:function(event,offset,selection){var index=tr.b.findFirstIndexInArray(this.rects_,function(rect){return rect.modelItem===event;});if(index===-1)
-return false;var newIndex=index+offset;if(newIndex<0||newIndex>=this.rects_.length)
-return false;this.rects_[newIndex].addToSelection(selection);return true;},addAllEventsMatchingFilterToSelection:function(filter,selection){for(var i=0;i<this.rects_.length;++i){var modelItem=this.rects_[i].modelItem;if(!modelItem)
-continue;if(filter.matchSlice(modelItem))
-selection.push(modelItem);}},addClosestEventToSelection:function(worldX,worldMaxDist,loY,hiY,selection){var rect=tr.b.findClosestIntervalInSortedIntervals(this.rects_,function(x){return x.start;},function(x){return x.end;},worldX,worldMaxDist);if(!rect)
-return;rect.addToSelection(selection);}};function Rect(modelItem,title,colorId,start,duration){tr.model.ProxySelectableItem.call(this,modelItem);this.title=title;this.colorId=colorId;this.start=start;this.duration=duration;this.end=start+duration;};Rect.prototype={__proto__:tr.model.ProxySelectableItem.prototype};return{RectTrack:RectTrack,Rect:Rect};});'use strict';tr.exportTo('tr.ui.tracks',function(){var SliceTrack=tr.ui.b.define('slice-track',tr.ui.tracks.RectTrack);SliceTrack.prototype={__proto__:tr.ui.tracks.RectTrack.prototype,decorate:function(viewport){tr.ui.tracks.RectTrack.prototype.decorate.call(this,viewport);},get slices(){return this.rects;},set slices(slices){this.rects=slices;}};return{SliceTrack:SliceTrack};});'use strict';tr.exportTo('tr.ui.tracks',function(){var CpuTrack=tr.ui.b.define('cpu-track',tr.ui.tracks.ContainerTrack);CpuTrack.prototype={__proto__:tr.ui.tracks.ContainerTrack.prototype,decorate:function(viewport){tr.ui.tracks.ContainerTrack.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('cpu-track');this.detailedMode_=true;},get cpu(){return this.cpu_;},set cpu(cpu){this.cpu_=cpu;this.updateContents_();},get detailedMode(){return this.detailedMode_;},set detailedMode(detailedMode){this.detailedMode_=detailedMode;this.updateContents_();},get tooltip(){return this.tooltip_;},set tooltip(value){this.tooltip_=value;this.updateContents_();},get hasVisibleContent(){if(this.cpu_===undefined)
-return false;var cpu=this.cpu_;if(cpu.slices.length)
-return true;if(cpu.samples&&cpu.samples.length)
-return true;if(tr.b.dictionaryLength(cpu.counters)>0)
-return true;return false;},updateContents_:function(){this.detach();if(!this.cpu_)
-return;var slices=this.cpu_.slices;if(slices.length){var track=new tr.ui.tracks.SliceTrack(this.viewport);track.slices=slices;track.heading=this.cpu_.userFriendlyName+':';Polymer.dom(this).appendChild(track);}
+function buildGlobalUsedMemoryChartSeries(globalMemoryDumps){return buildMemoryChartSeries(globalMemoryDumps,extractGlobalMemoryDumpUsedSizes);}
+function buildProcessAllocatedMemoryChartSeries(processMemoryDumps){return buildMemoryChartSeries(processMemoryDumps,extractProcessMemoryDumpAllocatorSizes);}
+function buildGlobalAllocatedMemoryChartSeries(globalMemoryDumps){return buildMemoryChartSeries(globalMemoryDumps,extractGlobalMemoryDumpAllocatorSizes);}
+return{buildMemoryLetterDots,buildGlobalUsedMemoryChartSeries,buildProcessAllocatedMemoryChartSeries,buildGlobalAllocatedMemoryChartSeries,};});'use strict';tr.exportTo('tr.ui.tracks',function(){var USED_MEMORY_TRACK_HEIGHT=50;var ALLOCATED_MEMORY_TRACK_HEIGHT=50;var GlobalMemoryDumpTrack=tr.ui.b.define('global-memory-dump-track',tr.ui.tracks.ContainerTrack);GlobalMemoryDumpTrack.prototype={__proto__:tr.ui.tracks.ContainerTrack.prototype,decorate:function(viewport){tr.ui.tracks.ContainerTrack.prototype.decorate.call(this,viewport);this.memoryDumps_=undefined;},get memoryDumps(){return this.memoryDumps_;},set memoryDumps(memoryDumps){this.memoryDumps_=memoryDumps;this.updateContents_();},updateContents_:function(){this.clearTracks_();if(!this.memoryDumps_||!this.memoryDumps_.length)return;this.appendDumpDotsTrack_();this.appendUsedMemoryTrack_();this.appendAllocatedMemoryTrack_();},appendDumpDotsTrack_:function(){var items=tr.ui.tracks.buildMemoryLetterDots(this.memoryDumps_);if(!items)return;var track=new tr.ui.tracks.LetterDotTrack(this.viewport);track.heading='Memory Dumps';track.items=items;Polymer.dom(this).appendChild(track);},appendUsedMemoryTrack_:function(){var series=tr.ui.tracks.buildGlobalUsedMemoryChartSeries(this.memoryDumps_);if(!series)return;var track=new tr.ui.tracks.ChartTrack(this.viewport);track.heading='Memory per process';track.height=USED_MEMORY_TRACK_HEIGHT+'px';track.series=series;track.autoSetAllAxes({expandMax:true});Polymer.dom(this).appendChild(track);},appendAllocatedMemoryTrack_:function(){var series=tr.ui.tracks.buildGlobalAllocatedMemoryChartSeries(this.memoryDumps_);if(!series)return;var track=new tr.ui.tracks.ChartTrack(this.viewport);track.heading='Memory per component';track.height=ALLOCATED_MEMORY_TRACK_HEIGHT+'px';track.series=series;track.autoSetAllAxes({expandMax:true});Polymer.dom(this).appendChild(track);}};return{GlobalMemoryDumpTrack,};});'use strict';tr.exportTo('tr.ui.b',function(){function FastRectRenderer(ctx,minRectSize,maxMergeDist,pallette){this.ctx_=ctx;this.minRectSize_=minRectSize;this.maxMergeDist_=maxMergeDist;this.pallette_=pallette;}
+FastRectRenderer.prototype={y_:0,h_:0,merging_:false,mergeStartX_:0,mergeCurRight_:0,mergedColorId_:0,mergedAlpha_:0,setYandH:function(y,h){if(this.y_===y&&this.h_===h){return;}
+this.flush();this.y_=y;this.h_=h;},fillRect:function(x,w,colorId,alpha){var r=x+w;if(w<this.minRectSize_){if(r-this.mergeStartX_>this.maxMergeDist_){this.flush();}
+if(!this.merging_){this.merging_=true;this.mergeStartX_=x;this.mergeCurRight_=r;this.mergedColorId_=colorId;this.mergedAlpha_=alpha;}else{this.mergeCurRight_=r;if(this.mergedAlpha_<alpha||(this.mergedAlpha_===alpha&&this.mergedColorId_<colorId)){this.mergedAlpha_=alpha;this.mergedColorId_=colorId;}}}else{if(this.merging_){this.flush();}
+this.ctx_.fillStyle=this.pallette_[colorId];this.ctx_.globalAlpha=alpha;this.ctx_.fillRect(x,this.y_,w,this.h_);}},flush:function(){if(this.merging_){this.ctx_.fillStyle=this.pallette_[this.mergedColorId_];this.ctx_.globalAlpha=this.mergedAlpha_;this.ctx_.fillRect(this.mergeStartX_,this.y_,this.mergeCurRight_-this.mergeStartX_,this.h_);this.merging_=false;}}};return{FastRectRenderer,};});'use strict';tr.exportTo('tr.ui.tracks',function(){var RectTrack=tr.ui.b.define('rect-track',tr.ui.tracks.Track);RectTrack.prototype={__proto__:tr.ui.tracks.Track.prototype,decorate:function(viewport){tr.ui.tracks.Track.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('rect-track');this.asyncStyle_=false;this.rects_=null;this.heading_=document.createElement('tr-ui-b-heading');Polymer.dom(this).appendChild(this.heading_);},set heading(heading){this.heading_.heading=heading;},get heading(){return this.heading_.heading;},set tooltip(tooltip){this.heading_.tooltip=tooltip;},set selectionGenerator(generator){this.heading_.selectionGenerator=generator;},set expanded(expanded){this.heading_.expanded=!!expanded;},set arrowVisible(arrowVisible){this.heading_.arrowVisible=!!arrowVisible;},get expanded(){return this.heading_.expanded;},get asyncStyle(){return this.asyncStyle_;},set asyncStyle(v){this.asyncStyle_=!!v;},get rects(){return this.rects_;},set rects(rects){this.rects_=rects||[];this.invalidateDrawingContainer();},get height(){return window.getComputedStyle(this).height;},set height(height){this.style.height=height;this.invalidateDrawingContainer();},get hasVisibleContent(){return this.rects_.length>0;},draw:function(type,viewLWorld,viewRWorld){switch(type){case tr.ui.tracks.DrawType.GENERAL_EVENT:this.drawRects_(viewLWorld,viewRWorld);break;}},drawRects_:function(viewLWorld,viewRWorld){var ctx=this.context();ctx.save();var bounds=this.getBoundingClientRect();tr.ui.b.drawSlices(ctx,this.viewport.currentDisplayTransform,viewLWorld,viewRWorld,bounds.height,this.rects_,this.asyncStyle_);ctx.restore();if(bounds.height<=6)return;var fontSize;var yOffset;if(bounds.height<15){fontSize=6;yOffset=1.0;}else{fontSize=10;yOffset=2.5;}
+tr.ui.b.drawLabels(ctx,this.viewport.currentDisplayTransform,viewLWorld,viewRWorld,this.rects_,this.asyncStyle_,fontSize,yOffset);},addEventsToTrackMap:function(eventToTrackMap){if(this.rects_===undefined||this.rects_===null){return;}
+this.rects_.forEach(function(rect){rect.addToTrackMap(eventToTrackMap,this);},this);},addIntersectingEventsInRangeToSelectionInWorldSpace:function(loWX,hiWX,viewPixWidthWorld,selection){function onRect(rect){rect.addToSelection(selection);}
+onRect=onRect.bind(this);var instantEventWidth=2*viewPixWidthWorld;tr.b.math.iterateOverIntersectingIntervals(this.rects_,function(x){return x.start;},function(x){return x.duration===0?x.duration+instantEventWidth:x.duration;},loWX,hiWX,onRect);},addEventNearToProvidedEventToSelection:function(event,offset,selection){var index=tr.b.findFirstIndexInArray(this.rects_,function(rect){return rect.modelItem===event;});if(index===-1)return false;var newIndex=index+offset;if(newIndex<0||newIndex>=this.rects_.length)return false;this.rects_[newIndex].addToSelection(selection);return true;},addAllEventsMatchingFilterToSelection:function(filter,selection){for(var i=0;i<this.rects_.length;++i){var modelItem=this.rects_[i].modelItem;if(!modelItem)continue;if(filter.matchSlice(modelItem)){selection.push(modelItem);}}},addClosestEventToSelection:function(worldX,worldMaxDist,loY,hiY,selection){var rect=tr.b.math.findClosestIntervalInSortedIntervals(this.rects_,function(x){return x.start;},function(x){return x.end;},worldX,worldMaxDist);if(!rect)return;rect.addToSelection(selection);}};function Rect(modelItem,title,colorId,start,duration){tr.model.ProxySelectableItem.call(this,modelItem);this.title=title;this.colorId=colorId;this.start=start;this.duration=duration;this.end=start+duration;}
+Rect.prototype={__proto__:tr.model.ProxySelectableItem.prototype};return{RectTrack,Rect,};});'use strict';tr.exportTo('tr.ui.tracks',function(){var SliceTrack=tr.ui.b.define('slice-track',tr.ui.tracks.RectTrack);SliceTrack.prototype={__proto__:tr.ui.tracks.RectTrack.prototype,decorate:function(viewport){tr.ui.tracks.RectTrack.prototype.decorate.call(this,viewport);},get slices(){return this.rects;},set slices(slices){this.rects=slices;}};return{SliceTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){var CpuTrack=tr.ui.b.define('cpu-track',tr.ui.tracks.ContainerTrack);CpuTrack.prototype={__proto__:tr.ui.tracks.ContainerTrack.prototype,decorate:function(viewport){tr.ui.tracks.ContainerTrack.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('cpu-track');this.detailedMode_=true;},get cpu(){return this.cpu_;},set cpu(cpu){this.cpu_=cpu;this.updateContents_();},get detailedMode(){return this.detailedMode_;},set detailedMode(detailedMode){this.detailedMode_=detailedMode;this.updateContents_();},get tooltip(){return this.tooltip_;},set tooltip(value){this.tooltip_=value;this.updateContents_();},get hasVisibleContent(){if(this.cpu_===undefined)return false;var cpu=this.cpu_;if(cpu.slices.length)return true;if(cpu.samples&&cpu.samples.length)return true;if(tr.b.dictionaryLength(cpu.counters)>0)return true;return false;},updateContents_:function(){this.detach();if(!this.cpu_)return;var slices=this.cpu_.slices;if(slices.length){var track=new tr.ui.tracks.SliceTrack(this.viewport);track.slices=slices;track.heading=this.cpu_.userFriendlyName+':';Polymer.dom(this).appendChild(track);}
 if(this.detailedMode_){this.appendSamplesTracks_();for(var counterName in this.cpu_.counters){var counter=this.cpu_.counters[counterName];track=new tr.ui.tracks.CounterTrack(this.viewport);track.heading=this.cpu_.userFriendlyName+' '+
-counter.name+':';track.counter=counter;Polymer.dom(this).appendChild(track);}}},appendSamplesTracks_:function(){var samples=this.cpu_.samples;if(samples===undefined||samples.length===0)
-return;var samplesByTitle={};samples.forEach(function(sample){if(samplesByTitle[sample.title]===undefined)
-samplesByTitle[sample.title]=[];samplesByTitle[sample.title].push(sample);});var sampleTitles=tr.b.dictionaryKeys(samplesByTitle);sampleTitles.sort();sampleTitles.forEach(function(sampleTitle){var samples=samplesByTitle[sampleTitle];var samplesTrack=new tr.ui.tracks.SliceTrack(this.viewport);samplesTrack.group=this.cpu_;samplesTrack.slices=samples;samplesTrack.heading=this.cpu_.userFriendlyName+': '+
+counter.name+':';track.counter=counter;Polymer.dom(this).appendChild(track);}}},appendSamplesTracks_:function(){var samples=this.cpu_.samples;if(samples===undefined||samples.length===0){return;}
+var samplesByTitle={};samples.forEach(function(sample){if(samplesByTitle[sample.title]===undefined){samplesByTitle[sample.title]=[];}
+samplesByTitle[sample.title].push(sample);});var sampleTitles=Object.keys(samplesByTitle);sampleTitles.sort();sampleTitles.forEach(function(sampleTitle){var samples=samplesByTitle[sampleTitle];var samplesTrack=new tr.ui.tracks.SliceTrack(this.viewport);samplesTrack.group=this.cpu_;samplesTrack.slices=samples;samplesTrack.heading=this.cpu_.userFriendlyName+': '+
 sampleTitle;samplesTrack.tooltip=this.cpu_.userFriendlyDetails;samplesTrack.selectionGenerator=function(){var selection=new tr.model.EventSet();for(var i=0;i<samplesTrack.slices.length;i++){selection.push(samplesTrack.slices[i]);}
-return selection;};Polymer.dom(this).appendChild(samplesTrack);},this);}};return{CpuTrack:CpuTrack};});'use strict';tr.exportTo('tr.model',function(){var Settings=tr.b.Settings;function ModelSettings(model){this.model=model;this.objectsByKey_=[];this.nonuniqueKeys_=[];this.buildObjectsByKeyMap_();this.removeNonuniqueKeysFromSettings_();this.ephemeralSettingsByGUID_={};}
-ModelSettings.prototype={buildObjectsByKeyMap_:function(){var objects=[];this.model.iterateAllPersistableObjects(function(o){objects.push(o);});var objectsByKey={};var NONUNIQUE_KEY='nonuniqueKey';for(var i=0;i<objects.length;i++){var object=objects[i];var objectKey=object.getSettingsKey();if(!objectKey)
-continue;if(objectsByKey[objectKey]===undefined){objectsByKey[objectKey]=object;continue;}
+return selection;};Polymer.dom(this).appendChild(samplesTrack);},this);}};return{CpuTrack,};});'use strict';tr.exportTo('tr.model',function(){var Settings=tr.b.Settings;function ModelSettings(model){this.model=model;this.objectsByKey_=[];this.nonuniqueKeys_=[];this.buildObjectsByKeyMap_();this.removeNonuniqueKeysFromSettings_();this.ephemeralSettingsByGUID_={};}
+ModelSettings.prototype={buildObjectsByKeyMap_:function(){var objects=[];this.model.iterateAllPersistableObjects(function(o){objects.push(o);});var objectsByKey={};var NONUNIQUE_KEY='nonuniqueKey';for(var i=0;i<objects.length;i++){var object=objects[i];var objectKey=object.getSettingsKey();if(!objectKey)continue;if(objectsByKey[objectKey]===undefined){objectsByKey[objectKey]=object;continue;}
 objectsByKey[objectKey]=NONUNIQUE_KEY;}
-var nonuniqueKeys={};tr.b.dictionaryKeys(objectsByKey).forEach(function(objectKey){if(objectsByKey[objectKey]!==NONUNIQUE_KEY)
-return;delete objectsByKey[objectKey];nonuniqueKeys[objectKey]=true;});this.nonuniqueKeys=nonuniqueKeys;this.objectsByKey_=objectsByKey;},removeNonuniqueKeysFromSettings_:function(){var settings=Settings.get('trace_model_settings',{});var settingsChanged=false;tr.b.dictionaryKeys(settings).forEach(function(objectKey){if(!this.nonuniqueKeys[objectKey])
-return;settingsChanged=true;delete settings[objectKey];},this);if(settingsChanged)
-Settings.set('trace_model_settings',settings);},hasUniqueSettingKey:function(object){var objectKey=object.getSettingsKey();if(!objectKey)
-return false;return this.objectsByKey_[objectKey]!==undefined;},getSettingFor:function(object,objectLevelKey,defaultValue){var objectKey=object.getSettingsKey();if(!objectKey||!this.objectsByKey_[objectKey]){var settings=this.getEphemeralSettingsFor_(object);var ephemeralValue=settings[objectLevelKey];if(ephemeralValue!==undefined)
-return ephemeralValue;return defaultValue;}
-var settings=Settings.get('trace_model_settings',{});if(!settings[objectKey])
-settings[objectKey]={};var value=settings[objectKey][objectLevelKey];if(value!==undefined)
-return value;return defaultValue;},setSettingFor:function(object,objectLevelKey,value){var objectKey=object.getSettingsKey();if(!objectKey||!this.objectsByKey_[objectKey]){this.getEphemeralSettingsFor_(object)[objectLevelKey]=value;return;}
-var settings=Settings.get('trace_model_settings',{});if(!settings[objectKey])
-settings[objectKey]={};if(settings[objectKey][objectLevelKey]===value)
-return;settings[objectKey][objectLevelKey]=value;Settings.set('trace_model_settings',settings);},getEphemeralSettingsFor_:function(object){if(object.guid===undefined)
-throw new Error('Only objects with GUIDs can be persisted');if(this.ephemeralSettingsByGUID_[object.guid]===undefined)
-this.ephemeralSettingsByGUID_[object.guid]={};return this.ephemeralSettingsByGUID_[object.guid];}};return{ModelSettings:ModelSettings};});'use strict';tr.exportTo('tr.ui.tracks',function(){var CounterTrack=tr.ui.b.define('counter-track',tr.ui.tracks.ChartTrack);CounterTrack.prototype={__proto__:tr.ui.tracks.ChartTrack.prototype,decorate:function(viewport){tr.ui.tracks.ChartTrack.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('counter-track');},get counter(){return this.chart;},set counter(counter){this.heading=counter.name+': ';this.series=CounterTrack.buildChartSeriesFromCounter(counter);this.autoSetAllAxes({expandMax:true});},getModelEventFromItem:function(chartValue){return chartValue;}};CounterTrack.buildChartSeriesFromCounter=function(counter){var numSeries=counter.series.length;var totals=counter.totals;var chartAxis=new tr.ui.tracks.ChartAxis(0,undefined);var chartSeries=counter.series.map(function(series,seriesIndex){var chartPoints=series.samples.map(function(sample,sampleIndex){var total=totals[sampleIndex*numSeries+seriesIndex];return new tr.ui.tracks.ChartPoint(sample,sample.timestamp,total);});var renderingConfig={chartType:tr.ui.tracks.ChartSeriesType.AREA,colorId:series.color};return new tr.ui.tracks.ChartSeries(chartPoints,chartAxis,renderingConfig);});chartSeries.reverse();return chartSeries;};return{CounterTrack:CounterTrack};});'use strict';tr.exportTo('tr.ui.tracks',function(){var startCompare=function(x,y){return x.start-y.start;}
-var FrameTrack=tr.ui.b.define('frame-track',tr.ui.tracks.LetterDotTrack);FrameTrack.prototype={__proto__:tr.ui.tracks.LetterDotTrack.prototype,decorate:function(viewport){tr.ui.tracks.LetterDotTrack.prototype.decorate.call(this,viewport);this.heading='Frames';this.frames_=undefined;this.items=undefined;},get frames(){return this.frames_;},set frames(frames){this.frames_=frames;if(frames===undefined)
-return;this.frames_=this.frames_.slice();this.frames_.sort(startCompare);this.items=this.frames_.map(function(frame){return new FrameDot(frame);});}};function FrameDot(frame){tr.ui.tracks.LetterDot.call(this,frame,'F',frame.colorId,frame.start);}
-FrameDot.prototype={__proto__:tr.ui.tracks.LetterDot.prototype};return{FrameTrack:FrameTrack};});'use strict';tr.exportTo('tr.ui.tracks',function(){var MultiRowTrack=tr.ui.b.define('multi-row-track',tr.ui.tracks.ContainerTrack);MultiRowTrack.prototype={__proto__:tr.ui.tracks.ContainerTrack.prototype,decorate:function(viewport){tr.ui.tracks.ContainerTrack.prototype.decorate.call(this,viewport);this.tooltip_='';this.heading_='';this.groupingSource_=undefined;this.itemsToGroup_=undefined;this.defaultToCollapsedWhenSubRowCountMoreThan=1;this.itemsGroupedOnLastUpdateContents_=undefined;this.currentSubRows_=[];this.expanded_=true;},get itemsToGroup(){return this.itemsToGroup_;},setItemsToGroup:function(itemsToGroup,opt_groupingSource){this.itemsToGroup_=itemsToGroup;this.groupingSource_=opt_groupingSource;this.updateContents_();this.updateExpandedStateFromGroupingSource_();},get heading(){return this.heading_;},set heading(h){this.heading_=h;this.updateContents_();},get tooltip(){return this.tooltip_;},set tooltip(t){this.tooltip_=t;this.updateContents_();},get subRows(){return this.currentSubRows_;},get hasVisibleContent(){return this.children.length>0;},get expanded(){return this.expanded_;},set expanded(expanded){if(this.expanded_==expanded)
-return;this.expanded_=expanded;this.expandedStateChanged_();},onHeadingClicked_:function(e){if(this.subRows.length<=1)
-return;this.expanded=!this.expanded;if(this.groupingSource_){var modelSettings=new tr.model.ModelSettings(this.groupingSource_.model);modelSettings.setSettingFor(this.groupingSource_,'expanded',this.expanded);}
+var nonuniqueKeys={};Object.keys(objectsByKey).forEach(function(objectKey){if(objectsByKey[objectKey]!==NONUNIQUE_KEY){return;}
+delete objectsByKey[objectKey];nonuniqueKeys[objectKey]=true;});this.nonuniqueKeys=nonuniqueKeys;this.objectsByKey_=objectsByKey;},removeNonuniqueKeysFromSettings_:function(){var settings=Settings.get('trace_model_settings',{});var settingsChanged=false;Object.keys(settings).forEach(function(objectKey){if(!this.nonuniqueKeys[objectKey]){return;}
+settingsChanged=true;delete settings[objectKey];},this);if(settingsChanged){Settings.set('trace_model_settings',settings);}},hasUniqueSettingKey:function(object){var objectKey=object.getSettingsKey();if(!objectKey)return false;return this.objectsByKey_[objectKey]!==undefined;},getSettingFor:function(object,objectLevelKey,defaultValue){var objectKey=object.getSettingsKey();if(!objectKey||!this.objectsByKey_[objectKey]){var settings=this.getEphemeralSettingsFor_(object);var ephemeralValue=settings[objectLevelKey];if(ephemeralValue!==undefined){return ephemeralValue;}
+return defaultValue;}
+var settings=Settings.get('trace_model_settings',{});if(!settings[objectKey]){settings[objectKey]={};}
+var value=settings[objectKey][objectLevelKey];if(value!==undefined){return value;}
+return defaultValue;},setSettingFor:function(object,objectLevelKey,value){var objectKey=object.getSettingsKey();if(!objectKey||!this.objectsByKey_[objectKey]){this.getEphemeralSettingsFor_(object)[objectLevelKey]=value;return;}
+var settings=Settings.get('trace_model_settings',{});if(!settings[objectKey]){settings[objectKey]={};}
+if(settings[objectKey][objectLevelKey]===value){return;}
+settings[objectKey][objectLevelKey]=value;Settings.set('trace_model_settings',settings);},getEphemeralSettingsFor_:function(object){if(object.guid===undefined){throw new Error('Only objects with GUIDs can be persisted');}
+if(this.ephemeralSettingsByGUID_[object.guid]===undefined){this.ephemeralSettingsByGUID_[object.guid]={};}
+return this.ephemeralSettingsByGUID_[object.guid];}};return{ModelSettings,};});'use strict';tr.exportTo('tr.ui.tracks',function(){var CounterTrack=tr.ui.b.define('counter-track',tr.ui.tracks.ChartTrack);CounterTrack.prototype={__proto__:tr.ui.tracks.ChartTrack.prototype,decorate:function(viewport){tr.ui.tracks.ChartTrack.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('counter-track');},get counter(){return this.chart;},set counter(counter){this.heading=counter.name+': ';this.series=CounterTrack.buildChartSeriesFromCounter(counter);this.autoSetAllAxes({expandMax:true});},getModelEventFromItem:function(chartValue){return chartValue;}};CounterTrack.buildChartSeriesFromCounter=function(counter){var numSeries=counter.series.length;var totals=counter.totals;var seriesYAxis=new tr.ui.tracks.ChartSeriesYAxis(0,undefined);var chartSeries=counter.series.map(function(series,seriesIndex){var chartPoints=series.samples.map(function(sample,sampleIndex){var total=totals[sampleIndex*numSeries+seriesIndex];return new tr.ui.tracks.ChartPoint(sample,sample.timestamp,total);});var renderingConfig={chartType:tr.ui.tracks.ChartSeriesType.AREA,colorId:series.color};return new tr.ui.tracks.ChartSeries(chartPoints,seriesYAxis,renderingConfig);});chartSeries.reverse();return chartSeries;};return{CounterTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){var startCompare=function(x,y){return x.start-y.start;};var FrameTrack=tr.ui.b.define('frame-track',tr.ui.tracks.LetterDotTrack);FrameTrack.prototype={__proto__:tr.ui.tracks.LetterDotTrack.prototype,decorate:function(viewport){tr.ui.tracks.LetterDotTrack.prototype.decorate.call(this,viewport);this.heading='Frames';this.frames_=undefined;this.items=undefined;},get frames(){return this.frames_;},set frames(frames){this.frames_=frames;if(frames===undefined)return;this.frames_=this.frames_.slice();this.frames_.sort(startCompare);this.items=this.frames_.map(function(frame){return new FrameDot(frame);});}};function FrameDot(frame){tr.ui.tracks.LetterDot.call(this,frame,'F',frame.colorId,frame.start);}
+FrameDot.prototype={__proto__:tr.ui.tracks.LetterDot.prototype};return{FrameTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){var MultiRowTrack=tr.ui.b.define('multi-row-track',tr.ui.tracks.ContainerTrack);MultiRowTrack.prototype={__proto__:tr.ui.tracks.ContainerTrack.prototype,decorate:function(viewport){tr.ui.tracks.ContainerTrack.prototype.decorate.call(this,viewport);this.tooltip_='';this.heading_='';this.groupingSource_=undefined;this.itemsToGroup_=undefined;this.defaultToCollapsedWhenSubRowCountMoreThan=1;this.itemsGroupedOnLastUpdateContents_=undefined;this.currentSubRows_=[];this.expanded_=true;},get itemsToGroup(){return this.itemsToGroup_;},setItemsToGroup:function(itemsToGroup,opt_groupingSource){this.itemsToGroup_=itemsToGroup;this.groupingSource_=opt_groupingSource;this.updateContents_();this.updateExpandedStateFromGroupingSource_();},get heading(){return this.heading_;},set heading(h){this.heading_=h;this.updateContents_();},get tooltip(){return this.tooltip_;},set tooltip(t){this.tooltip_=t;this.updateContents_();},get subRows(){return this.currentSubRows_;},get hasVisibleContent(){return this.children.length>0;},get expanded(){return this.expanded_;},set expanded(expanded){if(this.expanded_===expanded)return;this.expanded_=expanded;this.expandedStateChanged_();},onHeadingClicked_:function(e){if(this.subRows.length<=1)return;this.expanded=!this.expanded;if(this.groupingSource_){var modelSettings=new tr.model.ModelSettings(this.groupingSource_.model);modelSettings.setSettingFor(this.groupingSource_,'expanded',this.expanded);}
 e.stopPropagation();},updateExpandedStateFromGroupingSource_:function(){if(this.groupingSource_){var numSubRows=this.subRows.length;var modelSettings=new tr.model.ModelSettings(this.groupingSource_.model);if(numSubRows>1){var defaultExpanded;if(numSubRows>this.defaultToCollapsedWhenSubRowCountMoreThan){defaultExpanded=false;}else{defaultExpanded=true;}
-this.expanded=modelSettings.getSettingFor(this.groupingSource_,'expanded',defaultExpanded);}else{this.expanded=undefined;}}},expandedStateChanged_:function(){var minH=Math.max(2,Math.ceil(18/this.children.length));var h=(this.expanded_?18:minH)+'px';for(var i=0;i<this.children.length;i++){this.children[i].height=h;if(i===0)
-this.children[i].arrowVisible=true;this.children[i].expanded=this.expanded;}
+this.expanded=modelSettings.getSettingFor(this.groupingSource_,'expanded',defaultExpanded);}else{this.expanded=undefined;}}},expandedStateChanged_:function(){var minH=Math.max(2,Math.ceil(18/this.children.length));var h=(this.expanded_?18:minH)+'px';for(var i=0;i<this.children.length;i++){this.children[i].height=h;if(i===0){this.children[i].arrowVisible=true;}
+this.children[i].expanded=this.expanded;}
 if(this.children.length===1){this.children[0].expanded=true;this.children[0].arrowVisible=false;}},updateContents_:function(){tr.ui.tracks.ContainerTrack.prototype.updateContents_.call(this);if(!this.itemsToGroup_){this.updateHeadingAndTooltip_();this.currentSubRows_=[];return;}
 if(this.areArrayContentsSame_(this.itemsGroupedOnLastUpdateContents_,this.itemsToGroup_)){this.updateHeadingAndTooltip_();return;}
 this.itemsGroupedOnLastUpdateContents_=this.itemsToGroup_;this.detach();if(!this.itemsToGroup_.length){this.currentSubRows_=[];return;}
-var subRows=this.buildSubRows_(this.itemsToGroup_);this.currentSubRows_=subRows;for(var srI=0;srI<subRows.length;srI++){var subRow=subRows[srI];if(!subRow.length)
-continue;var track=this.addSubTrack_(subRow);track.addEventListener('heading-clicked',this.onHeadingClicked_.bind(this));}
-this.updateHeadingAndTooltip_();this.expandedStateChanged_();},updateHeadingAndTooltip_:function(){if(!Polymer.dom(this).firstChild)
-return;Polymer.dom(this).firstChild.heading=this.heading_;Polymer.dom(this).firstChild.tooltip=this.tooltip_;},buildSubRows_:function(itemsToGroup){throw new Error('Not implemented');},addSubTrack_:function(subRowItems){throw new Error('Not implemented');},areArrayContentsSame_:function(a,b){if(!a||!b)
-return false;if(!a.length||!b.length)
-return false;if(a.length!=b.length)
-return false;for(var i=0;i<a.length;++i){if(a[i]!=b[i])
-return false;}
-return true;}};return{MultiRowTrack:MultiRowTrack};});'use strict';tr.exportTo('tr.ui.tracks',function(){var ObjectInstanceGroupTrack=tr.ui.b.define('object-instance-group-track',tr.ui.tracks.MultiRowTrack);ObjectInstanceGroupTrack.prototype={__proto__:tr.ui.tracks.MultiRowTrack.prototype,decorate:function(viewport){tr.ui.tracks.MultiRowTrack.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('object-instance-group-track');this.objectInstances_=undefined;},get objectInstances(){return this.itemsToGroup;},set objectInstances(objectInstances){this.setItemsToGroup(objectInstances);},addSubTrack_:function(objectInstances){var hasMultipleRows=this.subRows.length>1;var track=new tr.ui.tracks.ObjectInstanceTrack(this.viewport);track.objectInstances=objectInstances;Polymer.dom(this).appendChild(track);return track;},buildSubRows_:function(objectInstances){objectInstances.sort(function(x,y){return x.creationTs-y.creationTs;});var subRows=[];for(var i=0;i<objectInstances.length;i++){var objectInstance=objectInstances[i];var found=false;for(var j=0;j<subRows.length;j++){var subRow=subRows[j];var lastItemInSubRow=subRow[subRow.length-1];if(objectInstance.creationTs>=lastItemInSubRow.deletionTs){found=true;subRow.push(objectInstance);break;}}
+var subRows=this.buildSubRows_(this.itemsToGroup_);this.currentSubRows_=subRows;for(var srI=0;srI<subRows.length;srI++){var subRow=subRows[srI];if(!subRow.length)continue;var track=this.addSubTrack_(subRow);track.addEventListener('heading-clicked',this.onHeadingClicked_.bind(this));}
+this.updateHeadingAndTooltip_();this.expandedStateChanged_();},updateHeadingAndTooltip_:function(){if(!Polymer.dom(this).firstChild)return;Polymer.dom(this).firstChild.heading=this.heading_;Polymer.dom(this).firstChild.tooltip=this.tooltip_;},buildSubRows_:function(itemsToGroup){throw new Error('Not implemented');},addSubTrack_:function(subRowItems){throw new Error('Not implemented');},areArrayContentsSame_:function(a,b){if(!a||!b)return false;if(!a.length||!b.length)return false;if(a.length!==b.length)return false;for(var i=0;i<a.length;++i){if(a[i]!==b[i])return false;}
+return true;}};return{MultiRowTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){var ObjectInstanceGroupTrack=tr.ui.b.define('object-instance-group-track',tr.ui.tracks.MultiRowTrack);ObjectInstanceGroupTrack.prototype={__proto__:tr.ui.tracks.MultiRowTrack.prototype,decorate:function(viewport){tr.ui.tracks.MultiRowTrack.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('object-instance-group-track');this.objectInstances_=undefined;},get objectInstances(){return this.itemsToGroup;},set objectInstances(objectInstances){this.setItemsToGroup(objectInstances);},addSubTrack_:function(objectInstances){var hasMultipleRows=this.subRows.length>1;var track=new tr.ui.tracks.ObjectInstanceTrack(this.viewport);track.objectInstances=objectInstances;Polymer.dom(this).appendChild(track);return track;},buildSubRows_:function(objectInstances){objectInstances.sort(function(x,y){return x.creationTs-y.creationTs;});var subRows=[];for(var i=0;i<objectInstances.length;i++){var objectInstance=objectInstances[i];var found=false;for(var j=0;j<subRows.length;j++){var subRow=subRows[j];var lastItemInSubRow=subRow[subRow.length-1];if(objectInstance.creationTs>=lastItemInSubRow.deletionTs){found=true;subRow.push(objectInstance);break;}}
 if(!found){var subRow=[objectInstance];subRows.push(subRow);}}
-return subRows;},updateHeadingAndTooltip_:function(){}};return{ObjectInstanceGroupTrack:ObjectInstanceGroupTrack};});'use strict';tr.exportTo('tr.ui.tracks',function(){var ColorScheme=tr.b.ColorScheme;var ProcessSummaryTrack=tr.ui.b.define('process-summary-track',tr.ui.tracks.RectTrack);ProcessSummaryTrack.buildRectsFromProcess=function(process){if(!process)
-return[];var ops=[];var pushOp=function(isStart,time,slice){ops.push({isStart:isStart,time:time,slice:slice});};for(var tid in process.threads){var sliceGroup=process.threads[tid].sliceGroup;sliceGroup.topLevelSlices.forEach(function(slice){pushOp(true,slice.start,undefined);pushOp(false,slice.end,undefined);});sliceGroup.slices.forEach(function(slice){if(slice.important){pushOp(true,slice.start,slice);pushOp(false,slice.end,slice);}});}
-ops.sort(function(a,b){return a.time-b.time;});var rects=[];var genericColorId=ColorScheme.getColorIdForReservedName('generic_work');var pushRect=function(start,end,slice){rects.push(new tr.ui.tracks.Rect(slice,slice?slice.title:'',slice?slice.colorId:genericColorId,start,end-start));}
-var depth=0;var currentSlice=undefined;var lastStart=undefined;ops.forEach(function(op){depth+=op.isStart?1:-1;if(currentSlice){if(!op.isStart&&op.slice==currentSlice){pushRect(lastStart,op.time,currentSlice);lastStart=depth>=1?op.time:undefined;currentSlice=undefined;}}else{if(op.isStart){if(depth==1){lastStart=op.time;currentSlice=op.slice;}else if(op.slice){if(op.time!=lastStart){pushRect(lastStart,op.time,undefined);lastStart=op.time;}
-currentSlice=op.slice;}}else{if(depth==0){pushRect(lastStart,op.time,undefined);lastStart=undefined;}}}});return rects;};ProcessSummaryTrack.prototype={__proto__:tr.ui.tracks.RectTrack.prototype,decorate:function(viewport){tr.ui.tracks.RectTrack.prototype.decorate.call(this,viewport);},get process(){return this.process_;},set process(process){this.process_=process;this.rects=ProcessSummaryTrack.buildRectsFromProcess(process);}};return{ProcessSummaryTrack:ProcessSummaryTrack};});'use strict';tr.exportTo('tr.ui.tracks',function(){var AsyncSliceGroupTrack=tr.ui.b.define('async-slice-group-track',tr.ui.tracks.MultiRowTrack);AsyncSliceGroupTrack.prototype={__proto__:tr.ui.tracks.MultiRowTrack.prototype,decorate:function(viewport){tr.ui.tracks.MultiRowTrack.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('async-slice-group-track');this.group_=undefined;},addSubTrack_:function(slices){var track=new tr.ui.tracks.SliceTrack(this.viewport);track.slices=slices;Polymer.dom(this).appendChild(track);track.asyncStyle=true;return track;},get group(){return this.group_;},set group(group){this.group_=group;this.setItemsToGroup(this.group_.slices,this.group_);},get eventContainer(){return this.group;},addContainersToTrackMap:function(containerToTrackMap){tr.ui.tracks.MultiRowTrack.prototype.addContainersToTrackMap.apply(this,arguments);containerToTrackMap.addContainer(this.group,this);},buildSubRows_:function(slices,opt_skipSort){if(!opt_skipSort){slices.sort(function(x,y){return x.start-y.start;});}
-var findLevel=function(sliceToPut,rows,n){if(n>=rows.length)
-return true;var subRow=rows[n];var lastSliceInSubRow=subRow[subRow.length-1];if(sliceToPut.start>=lastSliceInSubRow.end){if(sliceToPut.subSlices===undefined||sliceToPut.subSlices.length===0){return true;}
-for(var subSlice of sliceToPut.subSlices){if(!findLevel(subSlice,rows,n+1))
-return false;}
+return subRows;},updateHeadingAndTooltip_:function(){}};return{ObjectInstanceGroupTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){var AsyncSliceGroupTrack=tr.ui.b.define('async-slice-group-track',tr.ui.tracks.MultiRowTrack);AsyncSliceGroupTrack.prototype={__proto__:tr.ui.tracks.MultiRowTrack.prototype,decorate:function(viewport){tr.ui.tracks.MultiRowTrack.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('async-slice-group-track');this.group_=undefined;},addSubTrack_:function(slices){var track=new tr.ui.tracks.SliceTrack(this.viewport);track.slices=slices;Polymer.dom(this).appendChild(track);track.asyncStyle=true;return track;},get group(){return this.group_;},set group(group){this.group_=group;this.setItemsToGroup(this.group_.slices,this.group_);},get eventContainer(){return this.group;},addContainersToTrackMap:function(containerToTrackMap){tr.ui.tracks.MultiRowTrack.prototype.addContainersToTrackMap.apply(this,arguments);containerToTrackMap.addContainer(this.group,this);},buildSubRows_:function(slices,opt_skipSort){if(!opt_skipSort){slices.sort(function(x,y){return x.start-y.start;});}
+var findLevel=function(sliceToPut,rows,n){if(n>=rows.length){return true;}
+var subRow=rows[n];var lastSliceInSubRow=subRow[subRow.length-1];if(sliceToPut.start>=lastSliceInSubRow.end){if(sliceToPut.subSlices===undefined||sliceToPut.subSlices.length===0){return true;}
+for(var subSlice of sliceToPut.subSlices){if(!findLevel(subSlice,rows,n+1)){return false;}}
 return true;}
 return false;};var subRows=[];for(var slice of slices){var found=false;var index=subRows.length;for(var j=0;j<subRows.length;j++){if(findLevel(slice,subRows,j)){found=true;index=j;break;}}
-if(!found)
-subRows.push([]);subRows[index].push(slice);var fitSubSlicesRecursively=function(subSlices,level,rows){if(subSlices===undefined||subSlices.length===0)
-return;if(level===rows.length)
-rows.push([]);for(var subSlice of subSlices){rows[level].push(subSlice);fitSubSlicesRecursively(subSlice.subSlices,level+1,rows);}};fitSubSlicesRecursively(slice.subSlices,index+1,subRows);}
-return subRows;}};return{AsyncSliceGroupTrack:AsyncSliceGroupTrack};});'use strict';tr.exportTo('tr.ui.tracks',function(){var SampleTrack=tr.ui.b.define('sample-track',tr.ui.tracks.RectTrack);SampleTrack.prototype={__proto__:tr.ui.tracks.RectTrack.prototype,decorate:function(viewport){tr.ui.tracks.RectTrack.prototype.decorate.call(this,viewport);},get samples(){return this.rects;},set samples(samples){this.rects=samples;}};return{SampleTrack:SampleTrack};});'use strict';tr.exportTo('tr.ui.tracks',function(){var SliceGroupTrack=tr.ui.b.define('slice-group-track',tr.ui.tracks.MultiRowTrack);SliceGroupTrack.prototype={__proto__:tr.ui.tracks.MultiRowTrack.prototype,decorate:function(viewport){tr.ui.tracks.MultiRowTrack.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('slice-group-track');this.group_=undefined;this.defaultToCollapsedWhenSubRowCountMoreThan=100;},addSubTrack_:function(slices){var track=new tr.ui.tracks.SliceTrack(this.viewport);track.slices=slices;Polymer.dom(this).appendChild(track);return track;},get group(){return this.group_;},set group(group){this.group_=group;this.setItemsToGroup(this.group_.slices,this.group_);},get eventContainer(){return this.group;},addContainersToTrackMap:function(containerToTrackMap){tr.ui.tracks.MultiRowTrack.prototype.addContainersToTrackMap.apply(this,arguments);containerToTrackMap.addContainer(this.group,this);},buildSubRows_:function(slices){var precisionUnit=this.group.model.intrinsicTimeUnit;if(!slices.length)
-return[];var ops=[];for(var i=0;i<slices.length;i++){if(slices[i].subSlices)
-slices[i].subSlices.splice(0,slices[i].subSlices.length);ops.push(i);}
-ops.sort(function(ix,iy){var x=slices[ix];var y=slices[iy];if(x.start!=y.start)
-return x.start-y.start;return ix-iy;});var subRows=[[]];this.badSlices_=[];for(var i=0;i<ops.length;i++){var op=ops[i];var slice=slices[op];var inserted=false;for(var j=subRows.length-1;j>=0;j--){if(subRows[j].length==0)
-continue;var insertedSlice=subRows[j][subRows[j].length-1];if(slice.start<insertedSlice.start){this.badSlices_.push(slice);inserted=true;}
-if(insertedSlice.bounds(slice,precisionUnit)){while(subRows.length<=j+1)
-subRows.push([]);subRows[j+1].push(slice);if(insertedSlice.subSlices)
-insertedSlice.subSlices.push(slice);inserted=true;break;}}
-if(inserted)
-continue;subRows[0].push(slice);}
-return subRows;}};return{SliceGroupTrack:SliceGroupTrack};});'use strict';tr.exportTo('tr.ui.tracks',function(){var ThreadTrack=tr.ui.b.define('thread-track',tr.ui.tracks.ContainerTrack);ThreadTrack.prototype={__proto__:tr.ui.tracks.ContainerTrack.prototype,decorate:function(viewport){tr.ui.tracks.ContainerTrack.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('thread-track');},get thread(){return this.thread_;},set thread(thread){this.thread_=thread;this.updateContents_();},get hasVisibleContent(){return this.tracks_.length>0;},get eventContainer(){return this.thread;},addContainersToTrackMap:function(containerToTrackMap){tr.ui.tracks.ContainerTrack.prototype.addContainersToTrackMap.apply(this,arguments);containerToTrackMap.addContainer(this.thread,this);},updateContents_:function(){this.detach();if(!this.thread_)
-return;this.heading=this.thread_.userFriendlyName+': ';this.tooltip=this.thread_.userFriendlyDetails;if(this.thread_.asyncSliceGroup.length)
-this.appendAsyncSliceTracks_();this.appendThreadSamplesTracks_();if(this.thread_.timeSlices){var timeSlicesTrack=new tr.ui.tracks.SliceTrack(this.viewport);timeSlicesTrack.heading='';timeSlicesTrack.height=tr.ui.b.THIN_SLICE_HEIGHT+'px';timeSlicesTrack.slices=this.thread_.timeSlices;if(timeSlicesTrack.hasVisibleContent)
-Polymer.dom(this).appendChild(timeSlicesTrack);}
-if(this.thread_.sliceGroup.length){var track=new tr.ui.tracks.SliceGroupTrack(this.viewport);track.heading=this.thread_.userFriendlyName;track.tooltip=this.thread_.userFriendlyDetails;track.group=this.thread_.sliceGroup;if(track.hasVisibleContent)
-Polymer.dom(this).appendChild(track);}},appendAsyncSliceTracks_:function(){var subGroups=this.thread_.asyncSliceGroup.viewSubGroups;subGroups.forEach(function(subGroup){var asyncTrack=new tr.ui.tracks.AsyncSliceGroupTrack(this.viewport);var title=subGroup.slices[0].viewSubGroupTitle;asyncTrack.group=subGroup;asyncTrack.heading=title;if(asyncTrack.hasVisibleContent)
-Polymer.dom(this).appendChild(asyncTrack);},this);},appendThreadSamplesTracks_:function(){var threadSamples=this.thread_.samples;if(threadSamples===undefined||threadSamples.length===0)
-return;var samplesByTitle={};threadSamples.forEach(function(sample){if(samplesByTitle[sample.title]===undefined)
-samplesByTitle[sample.title]=[];samplesByTitle[sample.title].push(sample);});var sampleTitles=tr.b.dictionaryKeys(samplesByTitle);sampleTitles.sort();sampleTitles.forEach(function(sampleTitle){var samples=samplesByTitle[sampleTitle];var samplesTrack=new tr.ui.tracks.SampleTrack(this.viewport);samplesTrack.group=this.thread_;samplesTrack.samples=samples;samplesTrack.heading=this.thread_.userFriendlyName+': '+
+if(!found){subRows.push([]);}
+subRows[index].push(slice);var fitSubSlicesRecursively=function(subSlices,level,rows){if(subSlices===undefined||subSlices.length===0){return;}
+if(level===rows.length){rows.push([]);}
+for(var subSlice of subSlices){rows[level].push(subSlice);fitSubSlicesRecursively(subSlice.subSlices,level+1,rows);}};fitSubSlicesRecursively(slice.subSlices,index+1,subRows);}
+return subRows;}};return{AsyncSliceGroupTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){var SampleTrack=tr.ui.b.define('sample-track',tr.ui.tracks.RectTrack);SampleTrack.prototype={__proto__:tr.ui.tracks.RectTrack.prototype,decorate:function(viewport){tr.ui.tracks.RectTrack.prototype.decorate.call(this,viewport);},get samples(){return this.rects;},set samples(samples){this.rects=samples;}};return{SampleTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){var SliceGroupTrack=tr.ui.b.define('slice-group-track',tr.ui.tracks.MultiRowTrack);SliceGroupTrack.prototype={__proto__:tr.ui.tracks.MultiRowTrack.prototype,decorate:function(viewport){tr.ui.tracks.MultiRowTrack.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('slice-group-track');this.group_=undefined;this.defaultToCollapsedWhenSubRowCountMoreThan=100;},addSubTrack_:function(slices){var track=new tr.ui.tracks.SliceTrack(this.viewport);track.slices=slices;Polymer.dom(this).appendChild(track);return track;},get group(){return this.group_;},set group(group){this.group_=group;this.setItemsToGroup(this.group_.slices,this.group_);},get eventContainer(){return this.group;},addContainersToTrackMap:function(containerToTrackMap){tr.ui.tracks.MultiRowTrack.prototype.addContainersToTrackMap.apply(this,arguments);containerToTrackMap.addContainer(this.group,this);},buildSubRows_:function(slices){var precisionUnit=this.group.model.intrinsicTimeUnit;if(!slices.length)return[];var ops=[];for(var i=0;i<slices.length;i++){if(slices[i].subSlices){slices[i].subSlices.splice(0,slices[i].subSlices.length);}
+ops.push(i);}
+ops.sort(function(ix,iy){var x=slices[ix];var y=slices[iy];if(x.start!==y.start)return x.start-y.start;return ix-iy;});var subRows=[[]];this.badSlices_=[];for(var i=0;i<ops.length;i++){var op=ops[i];var slice=slices[op];var inserted=false;for(var j=subRows.length-1;j>=0;j--){if(subRows[j].length===0)continue;var insertedSlice=subRows[j][subRows[j].length-1];if(slice.start<insertedSlice.start){this.badSlices_.push(slice);inserted=true;}
+if(insertedSlice.bounds(slice,precisionUnit)){while(subRows.length<=j+1){subRows.push([]);}
+subRows[j+1].push(slice);if(insertedSlice.subSlices){insertedSlice.subSlices.push(slice);}
+inserted=true;break;}}
+if(inserted)continue;subRows[0].push(slice);}
+return subRows;}};return{SliceGroupTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){var ThreadTrack=tr.ui.b.define('thread-track',tr.ui.tracks.ContainerTrack);ThreadTrack.prototype={__proto__:tr.ui.tracks.ContainerTrack.prototype,decorate:function(viewport){tr.ui.tracks.ContainerTrack.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('thread-track');this.heading_=document.createElement('tr-ui-b-heading');},get thread(){return this.thread_;},set thread(thread){this.thread_=thread;this.updateContents_();},get hasVisibleContent(){return this.tracks_.length>0;},get hasSlices(){return this.thread_.asyncSliceGroup.length>0||this.thread_.sliceGroup.length>0;},get hasTimeSlices(){return this.thread_.timeSlices;},get eventContainer(){return this.thread;},addContainersToTrackMap:function(containerToTrackMap){tr.ui.tracks.ContainerTrack.prototype.addContainersToTrackMap.apply(this,arguments);containerToTrackMap.addContainer(this.thread,this);},updateContents_:function(){this.detach();if(!this.thread_)return;this.heading_.heading=this.thread_.userFriendlyName;this.heading_.tooltip=this.thread_.userFriendlyDetails;if(this.thread_.asyncSliceGroup.length){this.appendAsyncSliceTracks_();}
+this.appendThreadSamplesTracks_();var needsHeading=false;if(this.thread_.timeSlices){var timeSlicesTrack=new tr.ui.tracks.SliceTrack(this.viewport);timeSlicesTrack.heading='';timeSlicesTrack.height=tr.ui.b.THIN_SLICE_HEIGHT+'px';timeSlicesTrack.slices=this.thread_.timeSlices;if(timeSlicesTrack.hasVisibleContent){needsHeading=true;Polymer.dom(this).appendChild(timeSlicesTrack);}}
+if(this.thread_.sliceGroup.length){var track=new tr.ui.tracks.SliceGroupTrack(this.viewport);track.heading=this.thread_.userFriendlyName;track.tooltip=this.thread_.userFriendlyDetails;track.group=this.thread_.sliceGroup;if(track.hasVisibleContent){needsHeading=false;Polymer.dom(this).appendChild(track);}}
+if(needsHeading){Polymer.dom(this).appendChild(this.heading_);}},appendAsyncSliceTracks_:function(){var subGroups=this.thread_.asyncSliceGroup.viewSubGroups;subGroups.forEach(function(subGroup){var asyncTrack=new tr.ui.tracks.AsyncSliceGroupTrack(this.viewport);var title=subGroup.slices[0].viewSubGroupTitle;asyncTrack.group=subGroup;asyncTrack.heading=title;if(asyncTrack.hasVisibleContent){Polymer.dom(this).appendChild(asyncTrack);}},this);},appendThreadSamplesTracks_:function(){var threadSamples=this.thread_.samples;if(threadSamples===undefined||threadSamples.length===0){return;}
+var samplesByTitle={};threadSamples.forEach(function(sample){if(samplesByTitle[sample.title]===undefined){samplesByTitle[sample.title]=[];}
+samplesByTitle[sample.title].push(sample);});var sampleTitles=Object.keys(samplesByTitle);sampleTitles.sort();sampleTitles.forEach(function(sampleTitle){var samples=samplesByTitle[sampleTitle];var samplesTrack=new tr.ui.tracks.SampleTrack(this.viewport);samplesTrack.group=this.thread_;samplesTrack.samples=samples;samplesTrack.heading=this.thread_.userFriendlyName+': '+
 sampleTitle;samplesTrack.tooltip=this.thread_.userFriendlyDetails;samplesTrack.selectionGenerator=function(){var selection=new tr.model.EventSet();for(var i=0;i<samplesTrack.samples.length;i++){selection.push(samplesTrack.samples[i]);}
 return selection;};Polymer.dom(this).appendChild(samplesTrack);},this);},collapsedDidChange:function(collapsed){if(collapsed){var h=parseInt(this.tracks[0].height);for(var i=0;i<this.tracks.length;++i){if(h>2){this.tracks[i].height=Math.floor(h)+'px';}else{this.tracks[i].style.display='none';}
-h=h*0.5;}}else{for(var i=0;i<this.tracks.length;++i){this.tracks[i].height=this.tracks[0].height;this.tracks[i].style.display='';}}}};return{ThreadTrack:ThreadTrack};});'use strict';tr.exportTo('tr.ui.tracks',function(){var ObjectSnapshotView=tr.ui.analysis.ObjectSnapshotView;var ObjectInstanceView=tr.ui.analysis.ObjectInstanceView;var SpacingTrack=tr.ui.tracks.SpacingTrack;var ProcessTrackBase=tr.ui.b.define('process-track-base',tr.ui.tracks.ContainerTrack);ProcessTrackBase.prototype={__proto__:tr.ui.tracks.ContainerTrack.prototype,decorate:function(viewport){tr.ui.tracks.ContainerTrack.prototype.decorate.call(this,viewport);this.processBase_=undefined;Polymer.dom(this).classList.add('process-track-base');Polymer.dom(this).classList.add('expanded');this.processNameEl_=tr.ui.b.createSpan();Polymer.dom(this.processNameEl_).classList.add('process-track-name');this.headerEl_=tr.ui.b.createDiv({className:'process-track-header'});Polymer.dom(this.headerEl_).appendChild(this.processNameEl_);this.headerEl_.addEventListener('click',this.onHeaderClick_.bind(this));Polymer.dom(this).appendChild(this.headerEl_);},get processBase(){return this.processBase_;},set processBase(processBase){this.processBase_=processBase;if(this.processBase_){var modelSettings=new tr.model.ModelSettings(this.processBase_.model);var defaultValue=this.processBase_.important;this.expanded=modelSettings.getSettingFor(this.processBase_,'expanded',defaultValue);}
-this.updateContents_();},get expanded(){return Polymer.dom(this).classList.contains('expanded');},set expanded(expanded){expanded=!!expanded;if(this.expanded===expanded)
-return;Polymer.dom(this).classList.toggle('expanded');this.viewport_.dispatchChangeEvent();if(!this.processBase_)
-return;var modelSettings=new tr.model.ModelSettings(this.processBase_.model);modelSettings.setSettingFor(this.processBase_,'expanded',expanded);this.updateContents_();this.viewport.rebuildEventToTrackMap();this.viewport.rebuildContainerToTrackMap();},get hasVisibleContent(){if(this.expanded)
-return this.children.length>1;return true;},onHeaderClick_:function(e){e.stopPropagation();e.preventDefault();this.expanded=!this.expanded;},updateContents_:function(){this.clearTracks_();if(!this.processBase_)
-return;Polymer.dom(this.processNameEl_).textContent=this.processBase_.userFriendlyName;this.headerEl_.title=this.processBase_.userFriendlyDetails;this.willAppendTracks_();if(this.expanded){this.appendMemoryDumpTrack_();this.appendObjectInstanceTracks_();this.appendCounterTracks_();this.appendFrameTrack_();this.appendThreadTracks_();}else{this.appendSummaryTrack_();}
-this.didAppendTracks_();},addEventsToTrackMap:function(eventToTrackMap){this.tracks_.forEach(function(track){track.addEventsToTrackMap(eventToTrackMap);});},willAppendTracks_:function(){},didAppendTracks_:function(){},appendMemoryDumpTrack_:function(){},appendSummaryTrack_:function(){var track=new tr.ui.tracks.ProcessSummaryTrack(this.viewport);track.process=this.process;if(!track.hasVisibleContent)
-return;Polymer.dom(this).appendChild(track);},appendFrameTrack_:function(){var frames=this.process?this.process.frames:undefined;if(!frames||!frames.length)
-return;var track=new tr.ui.tracks.FrameTrack(this.viewport);track.frames=frames;Polymer.dom(this).appendChild(track);},appendObjectInstanceTracks_:function(){var instancesByTypeName=this.processBase_.objects.getAllInstancesByTypeName();var instanceTypeNames=tr.b.dictionaryKeys(instancesByTypeName);instanceTypeNames.sort();var didAppendAtLeastOneTrack=false;instanceTypeNames.forEach(function(typeName){var allInstances=instancesByTypeName[typeName];var instanceViewInfo=ObjectInstanceView.getTypeInfo(undefined,typeName);var snapshotViewInfo=ObjectSnapshotView.getTypeInfo(undefined,typeName);if(instanceViewInfo&&!instanceViewInfo.metadata.showInTrackView)
-instanceViewInfo=undefined;if(snapshotViewInfo&&!snapshotViewInfo.metadata.showInTrackView)
-snapshotViewInfo=undefined;var hasViewInfo=instanceViewInfo||snapshotViewInfo;var visibleInstances=[];for(var i=0;i<allInstances.length;i++){var instance=allInstances[i];if(instance.snapshots.length===0)
-continue;if(instance.hasImplicitSnapshots&&!hasViewInfo)
-continue;visibleInstances.push(instance);}
-if(visibleInstances.length===0)
-return;var trackConstructor=tr.ui.tracks.ObjectInstanceTrack.getConstructor(undefined,typeName);if(!trackConstructor){var snapshotViewInfo=ObjectSnapshotView.getTypeInfo(undefined,typeName);if(snapshotViewInfo&&snapshotViewInfo.metadata.showInstances){trackConstructor=tr.ui.tracks.ObjectInstanceGroupTrack;}else{trackConstructor=tr.ui.tracks.ObjectInstanceTrack;}}
-var track=new trackConstructor(this.viewport);track.objectInstances=visibleInstances;Polymer.dom(this).appendChild(track);didAppendAtLeastOneTrack=true;},this);if(didAppendAtLeastOneTrack)
-Polymer.dom(this).appendChild(new SpacingTrack(this.viewport));},appendCounterTracks_:function(){var counters=tr.b.dictionaryValues(this.processBase.counters);counters.sort(tr.model.Counter.compare);counters.forEach(function(counter){var track=new tr.ui.tracks.CounterTrack(this.viewport);track.counter=counter;Polymer.dom(this).appendChild(track);Polymer.dom(this).appendChild(new SpacingTrack(this.viewport));}.bind(this));},appendThreadTracks_:function(){var threads=tr.b.dictionaryValues(this.processBase.threads);threads.sort(tr.model.Thread.compare);threads.forEach(function(thread){var track=new tr.ui.tracks.ThreadTrack(this.viewport);track.thread=thread;if(!track.hasVisibleContent)
-return;Polymer.dom(this).appendChild(track);Polymer.dom(this).appendChild(new SpacingTrack(this.viewport));}.bind(this));}};return{ProcessTrackBase:ProcessTrackBase};});'use strict';tr.exportTo('tr.ui.tracks',function(){var Cpu=tr.model.Cpu;var CpuTrack=tr.ui.tracks.cpu_track;var ProcessTrackBase=tr.ui.tracks.ProcessTrackBase;var SpacingTrack=tr.ui.tracks.SpacingTrack;var KernelTrack=tr.ui.b.define('kernel-track',ProcessTrackBase);KernelTrack.prototype={__proto__:ProcessTrackBase.prototype,decorate:function(viewport){ProcessTrackBase.prototype.decorate.call(this,viewport);},set kernel(kernel){this.processBase=kernel;},get kernel(){return this.processBase;},get eventContainer(){return this.kernel;},get hasVisibleContent(){return this.children.length>1;},addContainersToTrackMap:function(containerToTrackMap){tr.ui.tracks.ProcessTrackBase.prototype.addContainersToTrackMap.call(this,containerToTrackMap);containerToTrackMap.addContainer(this.kernel,this);},willAppendTracks_:function(){var cpus=tr.b.dictionaryValues(this.kernel.cpus);cpus.sort(tr.model.Cpu.compare);var didAppendAtLeastOneTrack=false;for(var i=0;i<cpus.length;++i){var cpu=cpus[i];var track=new tr.ui.tracks.CpuTrack(this.viewport);track.detailedMode=this.expanded;track.cpu=cpu;if(!track.hasVisibleContent)
-continue;Polymer.dom(this).appendChild(track);didAppendAtLeastOneTrack=true;}
-if(didAppendAtLeastOneTrack)
-Polymer.dom(this).appendChild(new SpacingTrack(this.viewport));}};return{KernelTrack:KernelTrack};});'use strict';tr.exportTo('tr.ui.tracks',function(){var InteractionTrack=tr.ui.b.define('interaction-track',tr.ui.tracks.MultiRowTrack);InteractionTrack.prototype={__proto__:tr.ui.tracks.MultiRowTrack.prototype,decorate:function(viewport){tr.ui.tracks.MultiRowTrack.prototype.decorate.call(this,viewport);this.heading='Interactions';this.subRows_=[];},set model(model){this.setItemsToGroup(model.userModel.expectations,{guid:tr.b.GUID.allocateSimple(),model:model,getSettingsKey:function(){return undefined;}});},buildSubRows_:function(slices){if(this.subRows_.length)
-return this.subRows_;this.subRows_.push.apply(this.subRows_,tr.ui.tracks.AsyncSliceGroupTrack.prototype.buildSubRows_.call({},slices,true));return this.subRows_;},addSubTrack_:function(slices){var track=new tr.ui.tracks.SliceTrack(this.viewport);track.slices=slices;Polymer.dom(this).appendChild(track);return track;}};return{InteractionTrack:InteractionTrack};});'use strict';tr.exportTo('tr.ui.tracks',function(){var ALLOCATED_MEMORY_TRACK_HEIGHT=50;var ProcessMemoryDumpTrack=tr.ui.b.define('process-memory-dump-track',tr.ui.tracks.ContainerTrack);ProcessMemoryDumpTrack.prototype={__proto__:tr.ui.tracks.ContainerTrack.prototype,decorate:function(viewport){tr.ui.tracks.ContainerTrack.prototype.decorate.call(this,viewport);this.memoryDumps_=undefined;},get memoryDumps(){return this.memoryDumps_;},set memoryDumps(memoryDumps){this.memoryDumps_=memoryDumps;this.updateContents_();},updateContents_:function(){this.clearTracks_();if(!this.memoryDumps_||!this.memoryDumps_.length)
-return;this.appendAllocatedMemoryTrack_();},appendAllocatedMemoryTrack_:function(){var series=tr.ui.tracks.buildProcessAllocatedMemoryChartSeries(this.memoryDumps_);if(!series)
-return;var track=new tr.ui.tracks.ChartTrack(this.viewport);track.heading='Memory per component';track.height=ALLOCATED_MEMORY_TRACK_HEIGHT+'px';track.series=series;track.autoSetAllAxes({expandMax:true});Polymer.dom(this).appendChild(track);}};return{ProcessMemoryDumpTrack:ProcessMemoryDumpTrack};});'use strict';tr.exportTo('tr.ui.tracks',function(){var ProcessTrackBase=tr.ui.tracks.ProcessTrackBase;var ProcessTrack=tr.ui.b.define('process-track',ProcessTrackBase);ProcessTrack.prototype={__proto__:ProcessTrackBase.prototype,decorate:function(viewport){tr.ui.tracks.ProcessTrackBase.prototype.decorate.call(this,viewport);},drawTrack:function(type){switch(type){case tr.ui.tracks.DrawType.INSTANT_EVENT:if(!this.processBase.instantEvents||this.processBase.instantEvents.length===0)
-break;var ctx=this.context();var pixelRatio=window.devicePixelRatio||1;var bounds=this.getBoundingClientRect();var canvasBounds=ctx.canvas.getBoundingClientRect();ctx.save();ctx.translate(0,pixelRatio*(bounds.top-canvasBounds.top));var dt=this.viewport.currentDisplayTransform;var viewLWorld=dt.xViewToWorld(0);var viewRWorld=dt.xViewToWorld(bounds.width*pixelRatio);tr.ui.b.drawInstantSlicesAsLines(ctx,this.viewport.currentDisplayTransform,viewLWorld,viewRWorld,bounds.height,this.processBase.instantEvents,2);ctx.restore();break;case tr.ui.tracks.DrawType.BACKGROUND:this.drawBackground_();return;}
-tr.ui.tracks.ContainerTrack.prototype.drawTrack.call(this,type);},drawBackground_:function(){var ctx=this.context();var canvasBounds=ctx.canvas.getBoundingClientRect();var pixelRatio=window.devicePixelRatio||1;var draw=false;ctx.fillStyle='#eee';for(var i=0;i<this.children.length;++i){if(!(this.children[i]instanceof tr.ui.tracks.Track)||(this.children[i]instanceof tr.ui.tracks.SpacingTrack))
-continue;draw=!draw;if(!draw)
-continue;var bounds=this.children[i].getBoundingClientRect();ctx.fillRect(0,pixelRatio*(bounds.top-canvasBounds.top),ctx.canvas.width,pixelRatio*bounds.height);}},set process(process){this.processBase=process;},get process(){return this.processBase;},get eventContainer(){return this.process;},addContainersToTrackMap:function(containerToTrackMap){tr.ui.tracks.ProcessTrackBase.prototype.addContainersToTrackMap.apply(this,arguments);containerToTrackMap.addContainer(this.process,this);},appendMemoryDumpTrack_:function(){var processMemoryDumps=this.process.memoryDumps;if(processMemoryDumps.length){var pmdt=new tr.ui.tracks.ProcessMemoryDumpTrack(this.viewport_);pmdt.memoryDumps=processMemoryDumps;Polymer.dom(this).appendChild(pmdt);}},addIntersectingEventsInRangeToSelectionInWorldSpace:function(loWX,hiWX,viewPixWidthWorld,selection){function onPickHit(instantEvent){selection.push(instantEvent);}
-var instantEventWidth=2*viewPixWidthWorld;tr.b.iterateOverIntersectingIntervals(this.processBase.instantEvents,function(x){return x.start;},function(x){return x.duration+instantEventWidth;},loWX,hiWX,onPickHit.bind(this));tr.ui.tracks.ContainerTrack.prototype.addIntersectingEventsInRangeToSelectionInWorldSpace.apply(this,arguments);},addClosestEventToSelection:function(worldX,worldMaxDist,loY,hiY,selection){this.addClosestInstantEventToSelection(this.processBase.instantEvents,worldX,worldMaxDist,selection);tr.ui.tracks.ContainerTrack.prototype.addClosestEventToSelection.apply(this,arguments);}};return{ProcessTrack:ProcessTrack};});'use strict';tr.exportTo('tr.ui.tracks',function(){var SelectionState=tr.model.SelectionState;var EventPresenter=tr.ui.b.EventPresenter;var ModelTrack=tr.ui.b.define('model-track',tr.ui.tracks.ContainerTrack);ModelTrack.prototype={__proto__:tr.ui.tracks.ContainerTrack.prototype,decorate:function(viewport){tr.ui.tracks.ContainerTrack.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('model-track');var typeInfos=tr.ui.tracks.Highlighter.getAllRegisteredTypeInfos();this.highlighters_=typeInfos.map(function(typeInfo){return new typeInfo.constructor(viewport);});this.upperMode_=false;this.annotationViews_=[];},get upperMode(){return this.upperMode_;},set upperMode(upperMode){this.upperMode_=upperMode;this.updateContents_();},detach:function(){tr.ui.tracks.ContainerTrack.prototype.detach.call(this);},get model(){return this.model_;},set model(model){this.model_=model;this.updateContents_();this.model_.addEventListener('annotationChange',this.updateAnnotations_.bind(this));},get hasVisibleContent(){return this.children.length>0;},updateContents_:function(){Polymer.dom(this).textContent='';if(!this.model_)
-return;if(this.upperMode_)
-this.updateContentsForUpperMode_();else
-this.updateContentsForLowerMode_();},updateContentsForUpperMode_:function(){},updateContentsForLowerMode_:function(){if(this.model_.userModel.expectations.length){var mrt=new tr.ui.tracks.InteractionTrack(this.viewport_);mrt.model=this.model_;Polymer.dom(this).appendChild(mrt);}
+h=h*0.5;}}else{for(var i=0;i<this.tracks.length;++i){this.tracks[i].height=this.tracks[0].height;this.tracks[i].style.display='';}}}};return{ThreadTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){var OtherThreadsTrack=tr.ui.b.define('other-threads-track',tr.ui.tracks.OtherThreadsTrack);var SpacingTrack=tr.ui.tracks.SpacingTrack;OtherThreadsTrack.prototype={__proto__:tr.ui.tracks.ContainerTrack.prototype,decorate:function(viewport){tr.ui.tracks.ContainerTrack.prototype.decorate.call(this,viewport);this.header_=document.createElement('tr-ui-b-heading');this.header_.addEventListener('click',this.onHeaderClick_.bind(this));this.header_.heading='Other Threads';this.header_.tooltip='Threads with only scheduling information';this.header_.arrowVisible=true;this.threads_=[];this.expanded=false;this.collapsible_=true;},set threads(threads){this.threads_=threads;this.updateContents_();},set collapsible(collapsible){this.collapsible_=collapsible;this.updateContents_();},onHeaderClick_:function(e){e.stopPropagation();e.preventDefault();this.expanded=!this.expanded;},get expanded(){return this.header_.expanded;},set expanded(expanded){expanded=!!expanded;if(this.expanded===expanded)return;this.header_.expanded=expanded;this.viewport_.dispatchChangeEvent();this.updateContents_();},updateContents_:function(){this.detach();if(this.collapsible_){Polymer.dom(this).appendChild(this.header_);}
+if(this.expanded||!this.collapsible_){for(var thread of this.threads_){var track=new tr.ui.tracks.ThreadTrack(this.viewport);track.thread=thread;if(!track.hasVisibleContent)return;Polymer.dom(this).appendChild(track);Polymer.dom(this).appendChild(new SpacingTrack(this.viewport));}}}};return{OtherThreadsTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){var ColorScheme=tr.b.ColorScheme;var ProcessSummaryTrack=tr.ui.b.define('process-summary-track',tr.ui.tracks.RectTrack);ProcessSummaryTrack.buildRectsFromProcess=function(process){if(!process)return[];var ops=[];var pushOp=function(isStart,time,slice){ops.push({isStart:isStart,time:time,slice:slice});};for(var tid in process.threads){var sliceGroup=process.threads[tid].sliceGroup;sliceGroup.topLevelSlices.forEach(function(slice){pushOp(true,slice.start,undefined);pushOp(false,slice.end,undefined);});sliceGroup.slices.forEach(function(slice){if(slice.important){pushOp(true,slice.start,slice);pushOp(false,slice.end,slice);}});}
+ops.sort(function(a,b){return a.time-b.time;});var rects=[];var genericColorId=ColorScheme.getColorIdForReservedName('generic_work');var pushRect=function(start,end,slice){rects.push(new tr.ui.tracks.Rect(slice,slice?slice.title:'',slice?slice.colorId:genericColorId,start,end-start));};var depth=0;var currentSlice=undefined;var lastStart=undefined;ops.forEach(function(op){depth+=op.isStart?1:-1;if(currentSlice){if(!op.isStart&&op.slice===currentSlice){pushRect(lastStart,op.time,currentSlice);lastStart=depth>=1?op.time:undefined;currentSlice=undefined;}}else{if(op.isStart){if(depth===1){lastStart=op.time;currentSlice=op.slice;}else if(op.slice){if(op.time!==lastStart){pushRect(lastStart,op.time,undefined);lastStart=op.time;}
+currentSlice=op.slice;}}else{if(depth===0){pushRect(lastStart,op.time,undefined);lastStart=undefined;}}}});return rects;};ProcessSummaryTrack.prototype={__proto__:tr.ui.tracks.RectTrack.prototype,decorate:function(viewport){tr.ui.tracks.RectTrack.prototype.decorate.call(this,viewport);},get process(){return this.process_;},set process(process){this.process_=process;this.rects=ProcessSummaryTrack.buildRectsFromProcess(process);}};return{ProcessSummaryTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){var ObjectSnapshotView=tr.ui.analysis.ObjectSnapshotView;var ObjectInstanceView=tr.ui.analysis.ObjectInstanceView;var SpacingTrack=tr.ui.tracks.SpacingTrack;var ProcessTrackBase=tr.ui.b.define('process-track-base',tr.ui.tracks.ContainerTrack);ProcessTrackBase.prototype={__proto__:tr.ui.tracks.ContainerTrack.prototype,decorate:function(viewport){tr.ui.tracks.ContainerTrack.prototype.decorate.call(this,viewport);this.processBase_=undefined;Polymer.dom(this).classList.add('process-track-base');Polymer.dom(this).classList.add('expanded');this.processNameEl_=tr.ui.b.createSpan();Polymer.dom(this.processNameEl_).classList.add('process-track-name');this.headerEl_=tr.ui.b.createDiv({className:'process-track-header'});Polymer.dom(this.headerEl_).appendChild(this.processNameEl_);this.headerEl_.addEventListener('click',this.onHeaderClick_.bind(this));Polymer.dom(this).appendChild(this.headerEl_);},get processBase(){return this.processBase_;},set processBase(processBase){this.processBase_=processBase;if(this.processBase_){var modelSettings=new tr.model.ModelSettings(this.processBase_.model);var defaultValue=this.processBase_.important;this.expanded=modelSettings.getSettingFor(this.processBase_,'expanded',defaultValue);}
+this.updateContents_();},get expanded(){return Polymer.dom(this).classList.contains('expanded');},set expanded(expanded){expanded=!!expanded;if(this.expanded===expanded)return;Polymer.dom(this).classList.toggle('expanded');this.viewport_.dispatchChangeEvent();if(!this.processBase_)return;var modelSettings=new tr.model.ModelSettings(this.processBase_.model);modelSettings.setSettingFor(this.processBase_,'expanded',expanded);this.updateContents_();this.viewport.rebuildEventToTrackMap();this.viewport.rebuildContainerToTrackMap();},get hasVisibleContent(){if(this.expanded){return this.children.length>1;}
+return true;},onHeaderClick_:function(e){e.stopPropagation();e.preventDefault();this.expanded=!this.expanded;},updateContents_:function(){this.clearTracks_();if(!this.processBase_)return;Polymer.dom(this.processNameEl_).textContent=this.processBase_.userFriendlyName;this.headerEl_.title=this.processBase_.userFriendlyDetails;this.willAppendTracks_();if(this.expanded){this.appendMemoryDumpTrack_();this.appendObjectInstanceTracks_();this.appendCounterTracks_();this.appendFrameTrack_();this.appendThreadTracks_();}else{this.appendSummaryTrack_();}
+this.didAppendTracks_();},willAppendTracks_:function(){},didAppendTracks_:function(){},appendMemoryDumpTrack_:function(){},appendSummaryTrack_:function(){var track=new tr.ui.tracks.ProcessSummaryTrack(this.viewport);track.process=this.process;if(!track.hasVisibleContent)return;Polymer.dom(this).appendChild(track);},appendFrameTrack_:function(){var frames=this.process?this.process.frames:undefined;if(!frames||!frames.length)return;var track=new tr.ui.tracks.FrameTrack(this.viewport);track.frames=frames;Polymer.dom(this).appendChild(track);},appendObjectInstanceTracks_:function(){var instancesByTypeName=this.processBase_.objects.getAllInstancesByTypeName();var instanceTypeNames=Object.keys(instancesByTypeName);instanceTypeNames.sort();var didAppendAtLeastOneTrack=false;instanceTypeNames.forEach(function(typeName){var allInstances=instancesByTypeName[typeName];var instanceViewInfo=ObjectInstanceView.getTypeInfo(undefined,typeName);var snapshotViewInfo=ObjectSnapshotView.getTypeInfo(undefined,typeName);if(instanceViewInfo&&!instanceViewInfo.metadata.showInTrackView){instanceViewInfo=undefined;}
+if(snapshotViewInfo&&!snapshotViewInfo.metadata.showInTrackView){snapshotViewInfo=undefined;}
+var hasViewInfo=instanceViewInfo||snapshotViewInfo;var visibleInstances=[];for(var i=0;i<allInstances.length;i++){var instance=allInstances[i];if(instance.snapshots.length===0)continue;if(instance.hasImplicitSnapshots&&!hasViewInfo)continue;visibleInstances.push(instance);}
+if(visibleInstances.length===0)return;var trackConstructor=tr.ui.tracks.ObjectInstanceTrack.getConstructor(undefined,typeName);if(!trackConstructor){var snapshotViewInfo=ObjectSnapshotView.getTypeInfo(undefined,typeName);if(snapshotViewInfo&&snapshotViewInfo.metadata.showInstances){trackConstructor=tr.ui.tracks.ObjectInstanceGroupTrack;}else{trackConstructor=tr.ui.tracks.ObjectInstanceTrack;}}
+var track=new trackConstructor(this.viewport);track.objectInstances=visibleInstances;Polymer.dom(this).appendChild(track);didAppendAtLeastOneTrack=true;},this);if(didAppendAtLeastOneTrack){Polymer.dom(this).appendChild(new SpacingTrack(this.viewport));}},appendCounterTracks_:function(){var counters=tr.b.dictionaryValues(this.processBase.counters);counters.sort(tr.model.Counter.compare);counters.forEach(function(counter){var track=new tr.ui.tracks.CounterTrack(this.viewport);track.counter=counter;Polymer.dom(this).appendChild(track);Polymer.dom(this).appendChild(new SpacingTrack(this.viewport));}.bind(this));},appendThreadTracks_:function(){var threads=tr.b.dictionaryValues(this.processBase.threads);threads.sort(tr.model.Thread.compare);var otherThreads=[];var hasVisibleThreads=false;threads.forEach(function(thread){var track=new tr.ui.tracks.ThreadTrack(this.viewport);track.thread=thread;if(!track.hasVisibleContent)return;if(track.hasSlices){hasVisibleThreads=true;Polymer.dom(this).appendChild(track);Polymer.dom(this).appendChild(new SpacingTrack(this.viewport));}else if(track.hasTimeSlices){otherThreads.push(thread);}}.bind(this));if(otherThreads.length>0){var track=new tr.ui.tracks.OtherThreadsTrack(this.viewport);track.threads=otherThreads;track.collapsible=otherThreads.length>1&&hasVisibleThreads;Polymer.dom(this).appendChild(track);}}};return{ProcessTrackBase,};});'use strict';tr.exportTo('tr.ui.tracks',function(){var Cpu=tr.model.Cpu;var CpuTrack=tr.ui.tracks.cpu_track;var ProcessTrackBase=tr.ui.tracks.ProcessTrackBase;var SpacingTrack=tr.ui.tracks.SpacingTrack;var KernelTrack=tr.ui.b.define('kernel-track',ProcessTrackBase);KernelTrack.prototype={__proto__:ProcessTrackBase.prototype,decorate:function(viewport){ProcessTrackBase.prototype.decorate.call(this,viewport);},set kernel(kernel){this.processBase=kernel;},get kernel(){return this.processBase;},get eventContainer(){return this.kernel;},get hasVisibleContent(){return this.children.length>1;},addContainersToTrackMap:function(containerToTrackMap){tr.ui.tracks.ProcessTrackBase.prototype.addContainersToTrackMap.call(this,containerToTrackMap);containerToTrackMap.addContainer(this.kernel,this);},willAppendTracks_:function(){var cpus=tr.b.dictionaryValues(this.kernel.cpus);cpus.sort(tr.model.Cpu.compare);var didAppendAtLeastOneTrack=false;for(var i=0;i<cpus.length;++i){var cpu=cpus[i];var track=new tr.ui.tracks.CpuTrack(this.viewport);track.detailedMode=this.expanded;track.cpu=cpu;if(!track.hasVisibleContent)continue;Polymer.dom(this).appendChild(track);didAppendAtLeastOneTrack=true;}
+if(didAppendAtLeastOneTrack){Polymer.dom(this).appendChild(new SpacingTrack(this.viewport));}}};return{KernelTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){var InteractionTrack=tr.ui.b.define('interaction-track',tr.ui.tracks.MultiRowTrack);InteractionTrack.prototype={__proto__:tr.ui.tracks.MultiRowTrack.prototype,decorate:function(viewport){tr.ui.tracks.MultiRowTrack.prototype.decorate.call(this,viewport);this.heading='Interactions';this.subRows_=[];},set model(model){this.setItemsToGroup(model.userModel.expectations,{guid:tr.b.GUID.allocateSimple(),model:model,getSettingsKey:function(){return undefined;}});},buildSubRows_:function(slices){if(this.subRows_.length){return this.subRows_;}
+this.subRows_.push.apply(this.subRows_,tr.ui.tracks.AsyncSliceGroupTrack.prototype.buildSubRows_.call({},slices,true));return this.subRows_;},addSubTrack_:function(slices){var track=new tr.ui.tracks.SliceTrack(this.viewport);track.slices=slices;Polymer.dom(this).appendChild(track);return track;}};return{InteractionTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){var ALLOCATED_MEMORY_TRACK_HEIGHT=50;var ProcessMemoryDumpTrack=tr.ui.b.define('process-memory-dump-track',tr.ui.tracks.ContainerTrack);ProcessMemoryDumpTrack.prototype={__proto__:tr.ui.tracks.ContainerTrack.prototype,decorate:function(viewport){tr.ui.tracks.ContainerTrack.prototype.decorate.call(this,viewport);this.memoryDumps_=undefined;},get memoryDumps(){return this.memoryDumps_;},set memoryDumps(memoryDumps){this.memoryDumps_=memoryDumps;this.updateContents_();},updateContents_:function(){this.clearTracks_();if(!this.memoryDumps_||!this.memoryDumps_.length)return;this.appendAllocatedMemoryTrack_();},appendAllocatedMemoryTrack_:function(){var series=tr.ui.tracks.buildProcessAllocatedMemoryChartSeries(this.memoryDumps_);if(!series)return;var track=new tr.ui.tracks.ChartTrack(this.viewport);track.heading='Memory per component';track.height=ALLOCATED_MEMORY_TRACK_HEIGHT+'px';track.series=series;track.autoSetAllAxes({expandMax:true});Polymer.dom(this).appendChild(track);}};return{ProcessMemoryDumpTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){var ProcessTrackBase=tr.ui.tracks.ProcessTrackBase;var ProcessTrack=tr.ui.b.define('process-track',ProcessTrackBase);ProcessTrack.prototype={__proto__:ProcessTrackBase.prototype,decorate:function(viewport){tr.ui.tracks.ProcessTrackBase.prototype.decorate.call(this,viewport);},drawTrack:function(type){switch(type){case tr.ui.tracks.DrawType.INSTANT_EVENT:if(!this.processBase.instantEvents||this.processBase.instantEvents.length===0){break;}
+var ctx=this.context();var pixelRatio=window.devicePixelRatio||1;var bounds=this.getBoundingClientRect();var canvasBounds=ctx.canvas.getBoundingClientRect();ctx.save();ctx.translate(0,pixelRatio*(bounds.top-canvasBounds.top));var dt=this.viewport.currentDisplayTransform;var viewLWorld=dt.xViewToWorld(0);var viewRWorld=dt.xViewToWorld(bounds.width*pixelRatio);tr.ui.b.drawInstantSlicesAsLines(ctx,this.viewport.currentDisplayTransform,viewLWorld,viewRWorld,bounds.height,this.processBase.instantEvents,2);ctx.restore();break;case tr.ui.tracks.DrawType.BACKGROUND:this.drawBackground_();return;}
+tr.ui.tracks.ContainerTrack.prototype.drawTrack.call(this,type);},drawBackground_:function(){var ctx=this.context();var canvasBounds=ctx.canvas.getBoundingClientRect();var pixelRatio=window.devicePixelRatio||1;var draw=false;ctx.fillStyle='#eee';for(var i=0;i<this.children.length;++i){if(!(this.children[i]instanceof tr.ui.tracks.Track)||(this.children[i]instanceof tr.ui.tracks.SpacingTrack)){continue;}
+draw=!draw;if(!draw)continue;var bounds=this.children[i].getBoundingClientRect();ctx.fillRect(0,pixelRatio*(bounds.top-canvasBounds.top),ctx.canvas.width,pixelRatio*bounds.height);}},set process(process){this.processBase=process;},get process(){return this.processBase;},get eventContainer(){return this.process;},addContainersToTrackMap:function(containerToTrackMap){tr.ui.tracks.ProcessTrackBase.prototype.addContainersToTrackMap.apply(this,arguments);containerToTrackMap.addContainer(this.process,this);},appendMemoryDumpTrack_:function(){var processMemoryDumps=this.process.memoryDumps;if(processMemoryDumps.length){var pmdt=new tr.ui.tracks.ProcessMemoryDumpTrack(this.viewport_);pmdt.memoryDumps=processMemoryDumps;Polymer.dom(this).appendChild(pmdt);}},addIntersectingEventsInRangeToSelectionInWorldSpace:function(loWX,hiWX,viewPixWidthWorld,selection){function onPickHit(instantEvent){selection.push(instantEvent);}
+var instantEventWidth=2*viewPixWidthWorld;tr.b.math.iterateOverIntersectingIntervals(this.processBase.instantEvents,function(x){return x.start;},function(x){return x.duration+instantEventWidth;},loWX,hiWX,onPickHit.bind(this));tr.ui.tracks.ContainerTrack.prototype.addIntersectingEventsInRangeToSelectionInWorldSpace.apply(this,arguments);},addClosestEventToSelection:function(worldX,worldMaxDist,loY,hiY,selection){this.addClosestInstantEventToSelection(this.processBase.instantEvents,worldX,worldMaxDist,selection);tr.ui.tracks.ContainerTrack.prototype.addClosestEventToSelection.apply(this,arguments);}};return{ProcessTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){var SelectionState=tr.model.SelectionState;var ColorScheme=tr.b.ColorScheme;var EventPresenter=tr.ui.b.EventPresenter;var ModelTrack=tr.ui.b.define('model-track',tr.ui.tracks.ContainerTrack);ModelTrack.VSYNC_HIGHLIGHT_ALPHA=0.1;ModelTrack.VSYNC_DENSITY_TRANSPARENT=0.20;ModelTrack.VSYNC_DENSITY_OPAQUE=0.10;ModelTrack.VSYNC_DENSITY_RANGE=ModelTrack.VSYNC_DENSITY_TRANSPARENT-ModelTrack.VSYNC_DENSITY_OPAQUE;ModelTrack.generateStripes_=function(times,minTime,maxTime){if(times.length===0)return[];var lowIndex=tr.b.math.findLowIndexInSortedArray(times,tr.b.identity,minTime);var highIndex=lowIndex-1;while(times[highIndex+1]<=maxTime){highIndex++;}
+var stripes=[];for(var i=lowIndex-(lowIndex%2);i<=highIndex;i+=2){var left=i<lowIndex?minTime:times[i];var right=i+1>highIndex?maxTime:times[i+1];stripes.push(tr.b.math.Range.fromExplicitRange(left,right));}
+return stripes;};ModelTrack.prototype={__proto__:tr.ui.tracks.ContainerTrack.prototype,decorate:function(viewport){tr.ui.tracks.ContainerTrack.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('model-track');this.upperMode_=false;this.annotationViews_=[];this.vSyncTimes_=[];},get upperMode(){return this.upperMode_;},set upperMode(upperMode){this.upperMode_=upperMode;this.updateContents_();},detach:function(){tr.ui.tracks.ContainerTrack.prototype.detach.call(this);},get model(){return this.model_;},set model(model){this.model_=model;this.updateContents_();this.model_.addEventListener('annotationChange',this.updateAnnotations_.bind(this));},get hasVisibleContent(){return this.children.length>0;},updateContents_:function(){Polymer.dom(this).textContent='';if(!this.model_)return;if(this.upperMode_){this.updateContentsForUpperMode_();}else{this.updateContentsForLowerMode_();}},updateContentsForUpperMode_:function(){},updateContentsForLowerMode_:function(){if(this.model_.userModel.expectations.length>1){var mrt=new tr.ui.tracks.InteractionTrack(this.viewport_);mrt.model=this.model_;Polymer.dom(this).appendChild(mrt);}
 if(this.model_.alerts.length){var at=new tr.ui.tracks.AlertTrack(this.viewport_);at.alerts=this.model_.alerts;Polymer.dom(this).appendChild(at);}
 if(this.model_.globalMemoryDumps.length){var gmdt=new tr.ui.tracks.GlobalMemoryDumpTrack(this.viewport_);gmdt.memoryDumps=this.model_.globalMemoryDumps;Polymer.dom(this).appendChild(gmdt);}
-this.appendDeviceTrack_();this.appendCpuUsageTrack_();this.appendKernelTrack_();var processes=this.model_.getAllProcesses();processes.sort(tr.model.Process.compare);for(var i=0;i<processes.length;++i){var process=processes[i];var track=new tr.ui.tracks.ProcessTrack(this.viewport);track.process=process;if(!track.hasVisibleContent)
-continue;Polymer.dom(this).appendChild(track);}
-this.viewport_.rebuildEventToTrackMap();this.viewport_.rebuildContainerToTrackMap();for(var i=0;i<this.highlighters_.length;i++){this.highlighters_[i].processModel(this.model_);}
-this.updateAnnotations_();},updateAnnotations_:function(){this.annotationViews_=[];var annotations=this.model_.getAllAnnotations();for(var i=0;i<annotations.length;i++){this.annotationViews_.push(annotations[i].getOrCreateView(this.viewport_));}
-this.invalidateDrawingContainer();},addEventsToTrackMap:function(eventToTrackMap){if(!this.model_)
-return;var tracks=this.children;for(var i=0;i<tracks.length;++i)
-tracks[i].addEventsToTrackMap(eventToTrackMap);if(this.instantEvents===undefined)
-return;var vp=this.viewport_;this.instantEvents.forEach(function(ev){eventToTrackMap.addEvent(ev,this);}.bind(this));},appendDeviceTrack_:function(){var device=this.model.device;var track=new tr.ui.tracks.DeviceTrack(this.viewport);track.device=this.model.device;if(!track.hasVisibleContent)
-return;Polymer.dom(this).appendChild(track);},appendKernelTrack_:function(){var kernel=this.model.kernel;var track=new tr.ui.tracks.KernelTrack(this.viewport);track.kernel=this.model.kernel;if(!track.hasVisibleContent)
-return;Polymer.dom(this).appendChild(track);},appendCpuUsageTrack_:function(){var track=new tr.ui.tracks.CpuUsageTrack(this.viewport);track.initialize(this.model);if(!track.hasVisibleContent)
-return;this.appendChild(track);},appendCpuUsageTrack_:function(){var track=new tr.ui.tracks.CpuUsageTrack(this.viewport);track.initialize(this.model);if(!track.hasVisibleContent)
-return;this.appendChild(track);},drawTrack:function(type){var ctx=this.context();if(!this.model_)
-return;var pixelRatio=window.devicePixelRatio||1;var bounds=this.getBoundingClientRect();var canvasBounds=ctx.canvas.getBoundingClientRect();ctx.save();ctx.translate(0,pixelRatio*(bounds.top-canvasBounds.top));var dt=this.viewport.currentDisplayTransform;var viewLWorld=dt.xViewToWorld(0);var viewRWorld=dt.xViewToWorld(bounds.width*pixelRatio);switch(type){case tr.ui.tracks.DrawType.GRID:this.viewport.drawMajorMarkLines(ctx);ctx.restore();return;case tr.ui.tracks.DrawType.FLOW_ARROWS:if(this.model_.flowIntervalTree.size===0){ctx.restore();return;}
-this.drawFlowArrows_(viewLWorld,viewRWorld);ctx.restore();return;case tr.ui.tracks.DrawType.INSTANT_EVENT:if(!this.model_.instantEvents||this.model_.instantEvents.length===0)
-break;tr.ui.b.drawInstantSlicesAsLines(ctx,this.viewport.currentDisplayTransform,viewLWorld,viewRWorld,bounds.height,this.model_.instantEvents,4);break;case tr.ui.tracks.DrawType.MARKERS:if(!this.viewport.interestRange.isEmpty){this.viewport.interestRange.draw(ctx,viewLWorld,viewRWorld);this.viewport.interestRange.drawIndicators(ctx,viewLWorld,viewRWorld);}
-ctx.restore();return;case tr.ui.tracks.DrawType.HIGHLIGHTS:for(var i=0;i<this.highlighters_.length;i++){this.highlighters_[i].drawHighlight(ctx,dt,viewLWorld,viewRWorld,bounds.height);}
-ctx.restore();return;case tr.ui.tracks.DrawType.ANNOTATIONS:for(var i=0;i<this.annotationViews_.length;i++){this.annotationViews_[i].draw(ctx);}
+this.appendDeviceTrack_();this.appendCpuUsageTrack_();this.appendKernelTrack_();var processes=this.model_.getAllProcesses();processes.sort(tr.model.Process.compare);for(var i=0;i<processes.length;++i){var process=processes[i];var track=new tr.ui.tracks.ProcessTrack(this.viewport);track.process=process;if(!track.hasVisibleContent)continue;Polymer.dom(this).appendChild(track);}
+this.viewport_.rebuildEventToTrackMap();this.viewport_.rebuildContainerToTrackMap();this.vSyncTimes_=this.model_.device.vSyncTimestamps;this.updateAnnotations_();},getContentBounds:function(){return this.model.bounds;},addAnnotation:function(annotation){this.model.addAnnotation(annotation);},removeAnnotation:function(annotation){this.model.removeAnnotation(annotation);},updateAnnotations_:function(){this.annotationViews_=[];var annotations=this.model_.getAllAnnotations();for(var i=0;i<annotations.length;i++){this.annotationViews_.push(annotations[i].getOrCreateView(this.viewport_));}
+this.invalidateDrawingContainer();},addEventsToTrackMap:function(eventToTrackMap){if(!this.model_)return;var tracks=this.children;for(var i=0;i<tracks.length;++i){tracks[i].addEventsToTrackMap(eventToTrackMap);}
+if(this.instantEvents===undefined)return;var vp=this.viewport_;this.instantEvents.forEach(function(ev){eventToTrackMap.addEvent(ev,this);}.bind(this));},appendDeviceTrack_:function(){var device=this.model.device;var track=new tr.ui.tracks.DeviceTrack(this.viewport);track.device=this.model.device;if(!track.hasVisibleContent)return;Polymer.dom(this).appendChild(track);},appendKernelTrack_:function(){var kernel=this.model.kernel;var track=new tr.ui.tracks.KernelTrack(this.viewport);track.kernel=this.model.kernel;if(!track.hasVisibleContent)return;Polymer.dom(this).appendChild(track);},appendCpuUsageTrack_:function(){var track=new tr.ui.tracks.CpuUsageTrack(this.viewport);track.initialize(this.model);if(!track.hasVisibleContent)return;this.appendChild(track);},appendCpuUsageTrack_:function(){var track=new tr.ui.tracks.CpuUsageTrack(this.viewport);track.initialize(this.model);if(!track.hasVisibleContent)return;this.appendChild(track);},drawTrack:function(type){var ctx=this.context();if(!this.model_)return;var pixelRatio=window.devicePixelRatio||1;var bounds=this.getBoundingClientRect();var canvasBounds=ctx.canvas.getBoundingClientRect();ctx.save();ctx.translate(0,pixelRatio*(bounds.top-canvasBounds.top));var dt=this.viewport.currentDisplayTransform;var viewLWorld=dt.xViewToWorld(0);var viewRWorld=dt.xViewToWorld(bounds.width*pixelRatio);switch(type){case tr.ui.tracks.DrawType.GRID:this.viewport.drawMajorMarkLines(ctx);ctx.restore();return;case tr.ui.tracks.DrawType.FLOW_ARROWS:if(this.model_.flowIntervalTree.size===0){ctx.restore();return;}
+this.drawFlowArrows_(viewLWorld,viewRWorld);ctx.restore();return;case tr.ui.tracks.DrawType.INSTANT_EVENT:if(!this.model_.instantEvents||this.model_.instantEvents.length===0){break;}
+tr.ui.b.drawInstantSlicesAsLines(ctx,this.viewport.currentDisplayTransform,viewLWorld,viewRWorld,bounds.height,this.model_.instantEvents,4);break;case tr.ui.tracks.DrawType.MARKERS:if(!this.viewport.interestRange.isEmpty){this.viewport.interestRange.draw(ctx,viewLWorld,viewRWorld);this.viewport.interestRange.drawIndicators(ctx,viewLWorld,viewRWorld);}
+ctx.restore();return;case tr.ui.tracks.DrawType.HIGHLIGHTS:this.drawVSyncHighlight(ctx,dt,viewLWorld,viewRWorld,bounds.height);ctx.restore();return;case tr.ui.tracks.DrawType.ANNOTATIONS:for(var i=0;i<this.annotationViews_.length;i++){this.annotationViews_[i].draw(ctx);}
 ctx.restore();return;}
-ctx.restore();tr.ui.tracks.ContainerTrack.prototype.drawTrack.call(this,type);},drawFlowArrows_:function(viewLWorld,viewRWorld){var ctx=this.context();var dt=this.viewport.currentDisplayTransform;dt.applyTransformToCanvas(ctx);var pixWidth=dt.xViewVectorToWorld(1);ctx.strokeStyle='rgba(0, 0, 0, 0.4)';ctx.fillStyle='rgba(0, 0, 0, 0.4)';ctx.lineWidth=pixWidth>1.0?1:pixWidth;var events=this.model_.flowIntervalTree.findIntersection(viewLWorld,viewRWorld);var onlyHighlighted=!this.viewport.showFlowEvents;var canvasBounds=ctx.canvas.getBoundingClientRect();for(var i=0;i<events.length;++i){if(onlyHighlighted&&events[i].selectionState!==SelectionState.SELECTED&&events[i].selectionState!==SelectionState.HIGHLIGHTED)
-continue;this.drawFlowArrow_(ctx,events[i],canvasBounds,pixWidth);}},drawFlowArrow_:function(ctx,flowEvent,canvasBounds,pixWidth){var pixelRatio=window.devicePixelRatio||1;var startTrack=this.viewport.trackForEvent(flowEvent.startSlice);var endTrack=this.viewport.trackForEvent(flowEvent.endSlice);if(startTrack===undefined||endTrack===undefined)
-return;var startBounds=startTrack.getBoundingClientRect();var endBounds=endTrack.getBoundingClientRect();if(flowEvent.selectionState==SelectionState.SELECTED){ctx.shadowBlur=1;ctx.shadowColor='red';ctx.shadowOffsety=2;ctx.strokeStyle='red';}else if(flowEvent.selectionState==SelectionState.HIGHLIGHTED){ctx.shadowBlur=1;ctx.shadowColor='red';ctx.shadowOffsety=2;ctx.strokeStyle='red';}else if(flowEvent.selectionState==SelectionState.DIMMED){ctx.shadowBlur=0;ctx.shadowOffsetX=0;ctx.strokeStyle='rgba(0, 0, 0, 0.2)';}else{var hasBoost=false;var startSlice=flowEvent.startSlice;hasBoost|=startSlice.selectionState===SelectionState.SELECTED;hasBoost|=startSlice.selectionState===SelectionState.HIGHLIGHTED;var endSlice=flowEvent.endSlice;hasBoost|=endSlice.selectionState===SelectionState.SELECTED;hasBoost|=endSlice.selectionState===SelectionState.HIGHLIGHTED;if(hasBoost){ctx.shadowBlur=1;ctx.shadowColor='rgba(255, 0, 0, 0.4)';ctx.shadowOffsety=2;ctx.strokeStyle='rgba(255, 0, 0, 0.4)';}else{ctx.shadowBlur=0;ctx.shadowOffsetX=0;ctx.strokeStyle='rgba(0, 0, 0, 0.4)';}}
+ctx.restore();tr.ui.tracks.ContainerTrack.prototype.drawTrack.call(this,type);},drawFlowArrows_:function(viewLWorld,viewRWorld){var ctx=this.context();var dt=this.viewport.currentDisplayTransform;dt.applyTransformToCanvas(ctx);var pixWidth=dt.xViewVectorToWorld(1);ctx.strokeStyle='rgba(0, 0, 0, 0.4)';ctx.fillStyle='rgba(0, 0, 0, 0.4)';ctx.lineWidth=pixWidth>1.0?1:pixWidth;var events=this.model_.flowIntervalTree.findIntersection(viewLWorld,viewRWorld);var onlyHighlighted=!this.viewport.showFlowEvents;var canvasBounds=ctx.canvas.getBoundingClientRect();for(var i=0;i<events.length;++i){if(onlyHighlighted&&events[i].selectionState!==SelectionState.SELECTED&&events[i].selectionState!==SelectionState.HIGHLIGHTED){continue;}
+this.drawFlowArrow_(ctx,events[i],canvasBounds,pixWidth);}},drawFlowArrow_:function(ctx,flowEvent,canvasBounds,pixWidth){var pixelRatio=window.devicePixelRatio||1;var startTrack=this.viewport.trackForEvent(flowEvent.startSlice);var endTrack=this.viewport.trackForEvent(flowEvent.endSlice);if(startTrack===undefined||endTrack===undefined)return;var startBounds=startTrack.getBoundingClientRect();var endBounds=endTrack.getBoundingClientRect();if(flowEvent.selectionState===SelectionState.SELECTED){ctx.shadowBlur=1;ctx.shadowColor='red';ctx.shadowOffsety=2;ctx.strokeStyle='red';}else if(flowEvent.selectionState===SelectionState.HIGHLIGHTED){ctx.shadowBlur=1;ctx.shadowColor='red';ctx.shadowOffsety=2;ctx.strokeStyle='red';}else if(flowEvent.selectionState===SelectionState.DIMMED){ctx.shadowBlur=0;ctx.shadowOffsetX=0;ctx.strokeStyle='rgba(0, 0, 0, 0.2)';}else{var hasBoost=false;var startSlice=flowEvent.startSlice;hasBoost|=startSlice.selectionState===SelectionState.SELECTED;hasBoost|=startSlice.selectionState===SelectionState.HIGHLIGHTED;var endSlice=flowEvent.endSlice;hasBoost|=endSlice.selectionState===SelectionState.SELECTED;hasBoost|=endSlice.selectionState===SelectionState.HIGHLIGHTED;if(hasBoost){ctx.shadowBlur=1;ctx.shadowColor='rgba(255, 0, 0, 0.4)';ctx.shadowOffsety=2;ctx.strokeStyle='rgba(255, 0, 0, 0.4)';}else{ctx.shadowBlur=0;ctx.shadowOffsetX=0;ctx.strokeStyle='rgba(0, 0, 0, 0.4)';}}
 var startSize=startBounds.left+startBounds.top+
 startBounds.bottom+startBounds.right;var endSize=endBounds.left+endBounds.top+
-endBounds.bottom+endBounds.right;if(startSize===0&&endSize===0)
-return;var startY=this.calculateTrackY_(startTrack,canvasBounds);var endY=this.calculateTrackY_(endTrack,canvasBounds);var pixelStartY=pixelRatio*startY;var pixelEndY=pixelRatio*endY;var half=(flowEvent.end-flowEvent.start)/2;ctx.beginPath();ctx.moveTo(flowEvent.start,pixelStartY);ctx.bezierCurveTo(flowEvent.start+half,pixelStartY,flowEvent.start+half,pixelEndY,flowEvent.end,pixelEndY);ctx.stroke();var arrowWidth=5*pixWidth*pixelRatio;var distance=flowEvent.end-flowEvent.start;if(distance<=(2*arrowWidth))
-return;var tipX=flowEvent.end;var tipY=pixelEndY;var arrowHeight=(endBounds.height/4)*pixelRatio;tr.ui.b.drawTriangle(ctx,tipX,tipY,tipX-arrowWidth,tipY-arrowHeight,tipX-arrowWidth,tipY+arrowHeight);ctx.fill();},calculateTrackY_:function(track,canvasBounds){var bounds=track.getBoundingClientRect();var size=bounds.left+bounds.top+bounds.bottom+bounds.right;if(size===0)
-return this.calculateTrackY_(Polymer.dom(track).parentNode,canvasBounds);return bounds.top-canvasBounds.top+(bounds.height/2);},addIntersectingEventsInRangeToSelectionInWorldSpace:function(loWX,hiWX,viewPixWidthWorld,selection){function onPickHit(instantEvent){selection.push(instantEvent);}
-var instantEventWidth=3*viewPixWidthWorld;tr.b.iterateOverIntersectingIntervals(this.model_.instantEvents,function(x){return x.start;},function(x){return x.duration+instantEventWidth;},loWX,hiWX,onPickHit.bind(this));tr.ui.tracks.ContainerTrack.prototype.addIntersectingEventsInRangeToSelectionInWorldSpace.apply(this,arguments);},addClosestEventToSelection:function(worldX,worldMaxDist,loY,hiY,selection){this.addClosestInstantEventToSelection(this.model_.instantEvents,worldX,worldMaxDist,selection);tr.ui.tracks.ContainerTrack.prototype.addClosestEventToSelection.apply(this,arguments);}};return{ModelTrack:ModelTrack};});'use strict';tr.exportTo('tr.ui.tracks',function(){var RulerTrack=tr.ui.b.define('ruler-track',tr.ui.tracks.Track);var logOf10=Math.log(10);function log10(x){return Math.log(x)/logOf10;}
-RulerTrack.prototype={__proto__:tr.ui.tracks.Track.prototype,decorate:function(viewport){tr.ui.tracks.Track.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('ruler-track');this.strings_secs_=[];this.strings_msecs_=[];this.strings_usecs_=[];this.strings_nsecs_=[];this.viewportChange_=this.viewportChange_.bind(this);viewport.addEventListener('change',this.viewportChange_);var heading=document.createElement('tr-ui-heading');heading.arrowVisible=false;Polymer.dom(this).appendChild(heading);},detach:function(){tr.ui.tracks.Track.prototype.detach.call(this);this.viewport.removeEventListener('change',this.viewportChange_);},viewportChange_:function(){if(this.viewport.interestRange.isEmpty)
-Polymer.dom(this).classList.remove('tall-mode');else
-Polymer.dom(this).classList.add('tall-mode');},draw:function(type,viewLWorld,viewRWorld){switch(type){case tr.ui.tracks.DrawType.GRID:this.drawGrid_(viewLWorld,viewRWorld);break;case tr.ui.tracks.DrawType.MARKERS:if(!this.viewport.interestRange.isEmpty)
-this.viewport.interestRange.draw(this.context(),viewLWorld,viewRWorld);break;}},drawGrid_:function(viewLWorld,viewRWorld){var ctx=this.context();var pixelRatio=window.devicePixelRatio||1;var canvasBounds=ctx.canvas.getBoundingClientRect();var trackBounds=this.getBoundingClientRect();var width=canvasBounds.width*pixelRatio;var height=trackBounds.height*pixelRatio;var hasInterestRange=!this.viewport.interestRange.isEmpty;var rulerHeight=hasInterestRange?(height*2)/5:height;var vp=this.viewport;var dt=vp.currentDisplayTransform;var idealMajorMarkDistancePix=150*pixelRatio;var idealMajorMarkDistanceWorld=dt.xViewVectorToWorld(idealMajorMarkDistancePix);var majorMarkDistanceWorld;var conservativeGuess=Math.pow(10,Math.ceil(log10(idealMajorMarkDistanceWorld)));var divisors=[10,5,2,1];for(var i=0;i<divisors.length;++i){var tightenedGuess=conservativeGuess/divisors[i];if(dt.xWorldVectorToView(tightenedGuess)<idealMajorMarkDistancePix)
-continue;majorMarkDistanceWorld=conservativeGuess/divisors[i-1];break;}
-var unit;var unitDivisor;var tickLabels=undefined;if(majorMarkDistanceWorld<0.0001){unit='ns';unitDivisor=0.000001;tickLabels=this.strings_nsecs_;}else if(majorMarkDistanceWorld<0.1){unit='us';unitDivisor=0.001;tickLabels=this.strings_usecs_;}else if(majorMarkDistanceWorld<100){unit='ms';unitDivisor=1;tickLabels=this.strings_msecs_;}else{unit='s';unitDivisor=1000;tickLabels=this.strings_secs_;}
-var numTicksPerMajor=5;var minorMarkDistanceWorld=majorMarkDistanceWorld/numTicksPerMajor;var minorMarkDistancePx=dt.xWorldVectorToView(minorMarkDistanceWorld);var firstMajorMark=Math.floor(viewLWorld/majorMarkDistanceWorld)*majorMarkDistanceWorld;var minorTickH=Math.floor(rulerHeight*0.25);ctx.save();var pixelRatio=window.devicePixelRatio||1;ctx.lineWidth=Math.round(pixelRatio);var crispLineCorrection=(ctx.lineWidth%2)/2;ctx.translate(crispLineCorrection,-crispLineCorrection);ctx.fillStyle='rgb(0, 0, 0)';ctx.strokeStyle='rgb(0, 0, 0)';ctx.textAlign='left';ctx.textBaseline='top';ctx.font=(9*pixelRatio)+'px sans-serif';vp.majorMarkPositions=[];ctx.beginPath();for(var curX=firstMajorMark;curX<viewRWorld;curX+=majorMarkDistanceWorld){var curXView=Math.floor(dt.xWorldToView(curX));var unitValue=curX/unitDivisor;var roundedUnitValue=Math.round(unitValue*100000)/100000;if(!tickLabels[roundedUnitValue])
-tickLabels[roundedUnitValue]=roundedUnitValue+' '+unit;ctx.fillText(tickLabels[roundedUnitValue],curXView+(2*pixelRatio),0);vp.majorMarkPositions.push(curXView);tr.ui.b.drawLine(ctx,curXView,0,curXView,rulerHeight);for(var i=1;i<numTicksPerMajor;++i){var xView=Math.floor(curXView+minorMarkDistancePx*i);tr.ui.b.drawLine(ctx,xView,rulerHeight-minorTickH,xView,rulerHeight);}}
-ctx.strokeStyle='rgb(0, 0, 0)';tr.ui.b.drawLine(ctx,0,height,width,height);ctx.stroke();if(!hasInterestRange)
-return;tr.ui.b.drawLine(ctx,0,rulerHeight,width,rulerHeight);ctx.stroke();var displayDistance;var displayTextColor='rgb(0,0,0)';var arrowSpacing=10*pixelRatio;var arrowColor='rgb(128,121,121)';var arrowPosY=rulerHeight*1.75;var arrowWidthView=3*pixelRatio;var arrowLengthView=10*pixelRatio;var spaceForArrowsView=2*(arrowWidthView+arrowSpacing);ctx.textBaseline='middle';ctx.font=(14*pixelRatio)+'px sans-serif';var textPosY=arrowPosY;var interestRange=vp.interestRange;if(interestRange.range===0){var markerWorld=interestRange.min;var markerView=dt.xWorldToView(markerWorld);var displayValue=markerWorld/unitDivisor;displayValue=Math.abs((Math.round(displayValue*1000)/1000));var textToDraw=displayValue+' '+unit;var textLeftView=markerView+4*pixelRatio;var textWidthView=ctx.measureText(textToDraw).width;if(textLeftView+textWidthView>width)
-textLeftView=markerView-4*pixelRatio-textWidthView;ctx.fillStyle=displayTextColor;ctx.fillText(textToDraw,textLeftView,textPosY);return;}
-var leftMarker=interestRange.min;var rightMarker=interestRange.max;var leftMarkerView=dt.xWorldToView(leftMarker);var rightMarkerView=dt.xWorldToView(rightMarker);var distanceBetweenMarkers=interestRange.range;var distanceBetweenMarkersView=dt.xWorldVectorToView(distanceBetweenMarkers);var positionInMiddleOfMarkersView=leftMarkerView+(distanceBetweenMarkersView/2);if(distanceBetweenMarkers<0.0001){unit='ns';unitDivisor=0.000001;}else if(distanceBetweenMarkers<0.1){unit='us';unitDivisor=0.001;}else if(distanceBetweenMarkers<100){unit='ms';unitDivisor=1;}else{unit='s';unitDivisor=1000;}
-displayDistance=distanceBetweenMarkers/unitDivisor;var roundedDisplayDistance=Math.abs((Math.round(displayDistance*1000)/1000));var textToDraw=roundedDisplayDistance+' '+unit;var textWidthView=ctx.measureText(textToDraw).width;var spaceForArrowsAndTextView=textWidthView+spaceForArrowsView+arrowSpacing;var textLeftView=positionInMiddleOfMarkersView-textWidthView/2;var textRightView=textLeftView+textWidthView;if(spaceForArrowsAndTextView>distanceBetweenMarkersView){textLeftView=rightMarkerView+2*arrowSpacing;if(textLeftView+textWidthView>width)
-textLeftView=leftMarkerView-2*arrowSpacing-textWidthView;ctx.fillStyle=displayTextColor;ctx.fillText(textToDraw,textLeftView,textPosY);ctx.strokeStyle=arrowColor;ctx.beginPath();tr.ui.b.drawLine(ctx,leftMarkerView,arrowPosY,rightMarkerView,arrowPosY);ctx.stroke();ctx.fillStyle=arrowColor;tr.ui.b.drawArrow(ctx,leftMarkerView-1.5*arrowSpacing,arrowPosY,leftMarkerView,arrowPosY,arrowLengthView,arrowWidthView);tr.ui.b.drawArrow(ctx,rightMarkerView+1.5*arrowSpacing,arrowPosY,rightMarkerView,arrowPosY,arrowLengthView,arrowWidthView);}else if(spaceForArrowsView<=distanceBetweenMarkersView){var leftArrowStart;var rightArrowStart;if(spaceForArrowsAndTextView<=distanceBetweenMarkersView){ctx.fillStyle=displayTextColor;ctx.fillText(textToDraw,textLeftView,textPosY);leftArrowStart=textLeftView-arrowSpacing;rightArrowStart=textRightView+arrowSpacing;}else{leftArrowStart=positionInMiddleOfMarkersView;rightArrowStart=positionInMiddleOfMarkersView;}
+endBounds.bottom+endBounds.right;if(startSize===0&&endSize===0)return;var startY=this.calculateTrackY_(startTrack,canvasBounds);var endY=this.calculateTrackY_(endTrack,canvasBounds);var pixelStartY=pixelRatio*startY;var pixelEndY=pixelRatio*endY;var half=(flowEvent.end-flowEvent.start)/2;ctx.beginPath();ctx.moveTo(flowEvent.start,pixelStartY);ctx.bezierCurveTo(flowEvent.start+half,pixelStartY,flowEvent.start+half,pixelEndY,flowEvent.end,pixelEndY);ctx.stroke();var arrowWidth=5*pixWidth*pixelRatio;var distance=flowEvent.end-flowEvent.start;if(distance<=(2*arrowWidth))return;var tipX=flowEvent.end;var tipY=pixelEndY;var arrowHeight=(endBounds.height/4)*pixelRatio;tr.ui.b.drawTriangle(ctx,tipX,tipY,tipX-arrowWidth,tipY-arrowHeight,tipX-arrowWidth,tipY+arrowHeight);ctx.fill();},drawVSyncHighlight:function(ctx,dt,viewLWorld,viewRWorld,viewHeight){if(!this.viewport_.highlightVSync){return;}
+var stripes=ModelTrack.generateStripes_(this.vSyncTimes_,viewLWorld,viewRWorld);if(stripes.length===0){return;}
+var vSyncHighlightColor=new tr.b.Color(ColorScheme.getColorForReservedNameAsString('vsync_highlight_color'));var stripeRange=stripes[stripes.length-1].max-stripes[0].min;var stripeDensity=stripeRange?stripes.length/(dt.scaleX*stripeRange):0;var clampedStripeDensity=tr.b.math.clamp(stripeDensity,ModelTrack.VSYNC_DENSITY_OPAQUE,ModelTrack.VSYNC_DENSITY_TRANSPARENT);var opacity=(ModelTrack.VSYNC_DENSITY_TRANSPARENT-clampedStripeDensity)/ModelTrack.VSYNC_DENSITY_RANGE;if(opacity===0){return;}
+var pixelRatio=window.devicePixelRatio||1;var height=viewHeight*pixelRatio;ctx.fillStyle=vSyncHighlightColor.toStringWithAlphaOverride(ModelTrack.VSYNC_HIGHLIGHT_ALPHA*opacity);for(var i=0;i<stripes.length;i++){var xLeftView=dt.xWorldToView(stripes[i].min);var xRightView=dt.xWorldToView(stripes[i].max);ctx.fillRect(xLeftView,0,xRightView-xLeftView,height);}},calculateTrackY_:function(track,canvasBounds){var bounds=track.getBoundingClientRect();var size=bounds.left+bounds.top+bounds.bottom+bounds.right;if(size===0){return this.calculateTrackY_(Polymer.dom(track).parentNode,canvasBounds);}
+return bounds.top-canvasBounds.top+(bounds.height/2);},addIntersectingEventsInRangeToSelectionInWorldSpace:function(loWX,hiWX,viewPixWidthWorld,selection){function onPickHit(instantEvent){selection.push(instantEvent);}
+var instantEventWidth=3*viewPixWidthWorld;tr.b.math.iterateOverIntersectingIntervals(this.model_.instantEvents,function(x){return x.start;},function(x){return x.duration+instantEventWidth;},loWX,hiWX,onPickHit.bind(this));tr.ui.tracks.ContainerTrack.prototype.addIntersectingEventsInRangeToSelectionInWorldSpace.apply(this,arguments);},addClosestEventToSelection:function(worldX,worldMaxDist,loY,hiY,selection){this.addClosestInstantEventToSelection(this.model_.instantEvents,worldX,worldMaxDist,selection);tr.ui.tracks.ContainerTrack.prototype.addClosestEventToSelection.apply(this,arguments);}};return{ModelTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){var XAxisTrack=tr.ui.b.define('x-axis-track',tr.ui.tracks.Track);XAxisTrack.prototype={__proto__:tr.ui.tracks.Track.prototype,decorate:function(viewport){tr.ui.tracks.Track.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('x-axis-track');this.strings_secs_=[];this.strings_msecs_=[];this.strings_usecs_=[];this.strings_nsecs_=[];this.viewportChange_=this.viewportChange_.bind(this);viewport.addEventListener('change',this.viewportChange_);var heading=document.createElement('tr-ui-b-heading');heading.arrowVisible=false;Polymer.dom(this).appendChild(heading);},detach:function(){tr.ui.tracks.Track.prototype.detach.call(this);this.viewport.removeEventListener('change',this.viewportChange_);},viewportChange_:function(){if(this.viewport.interestRange.isEmpty){Polymer.dom(this).classList.remove('tall-mode');}else{Polymer.dom(this).classList.add('tall-mode');}},draw:function(type,viewLWorld,viewRWorld){switch(type){case tr.ui.tracks.DrawType.GRID:this.drawGrid_(viewLWorld,viewRWorld);break;case tr.ui.tracks.DrawType.MARKERS:if(!this.viewport.interestRange.isEmpty){this.viewport.interestRange.draw(this.context(),viewLWorld,viewRWorld);}
+break;}},drawGrid_:function(viewLWorld,viewRWorld){var ctx=this.context();var pixelRatio=window.devicePixelRatio||1;var canvasBounds=ctx.canvas.getBoundingClientRect();var trackBounds=this.getBoundingClientRect();var width=canvasBounds.width*pixelRatio;var height=trackBounds.height*pixelRatio;var hasInterestRange=!this.viewport.interestRange.isEmpty;var xAxisHeightPx=hasInterestRange?(height*2)/5:height;var vp=this.viewport;var dt=vp.currentDisplayTransform;vp.updateMajorMarkData(viewLWorld,viewRWorld);var majorMarkDistanceWorld=vp.majorMarkWorldPositions.length>1?vp.majorMarkWorldPositions[1]-vp.majorMarkWorldPositions[0]:0;var numTicksPerMajor=5;var minorMarkDistanceWorld=majorMarkDistanceWorld/numTicksPerMajor;var minorMarkDistancePx=dt.xWorldVectorToView(minorMarkDistanceWorld);var minorTickHeight=Math.floor(xAxisHeightPx*0.25);ctx.save();var pixelRatio=window.devicePixelRatio||1;ctx.lineWidth=Math.round(pixelRatio);var crispLineCorrection=(ctx.lineWidth%2)/2;ctx.translate(crispLineCorrection,-crispLineCorrection);ctx.fillStyle='rgb(0, 0, 0)';ctx.strokeStyle='rgb(0, 0, 0)';ctx.textAlign='left';ctx.textBaseline='top';ctx.font=(9*pixelRatio)+'px sans-serif';var tickLabels=[];ctx.beginPath();for(var i=0;i<vp.majorMarkWorldPositions.length;i++){var curXWorld=vp.majorMarkWorldPositions[i];var curXView=dt.xWorldToView(curXWorld);var displayText=vp.majorMarkUnit.format(curXWorld,{deltaValue:majorMarkDistanceWorld});ctx.fillText(displayText,curXView+(2*pixelRatio),0);tr.ui.b.drawLine(ctx,curXView,0,curXView,xAxisHeightPx);if(minorMarkDistancePx){for(var j=1;j<numTicksPerMajor;++j){var xView=Math.floor(curXView+minorMarkDistancePx*j);tr.ui.b.drawLine(ctx,xView,xAxisHeightPx-minorTickHeight,xView,xAxisHeightPx);}}}
+ctx.strokeStyle='rgb(0, 0, 0)';tr.ui.b.drawLine(ctx,0,height,width,height);ctx.stroke();if(!hasInterestRange)return;tr.ui.b.drawLine(ctx,0,xAxisHeightPx,width,xAxisHeightPx);ctx.stroke();var displayDistance;var displayTextColor='rgb(0,0,0)';var arrowSpacing=10*pixelRatio;var arrowColor='rgb(128,121,121)';var arrowPosY=xAxisHeightPx*1.75;var arrowWidthView=3*pixelRatio;var arrowLengthView=10*pixelRatio;var spaceForArrowsView=2*(arrowWidthView+arrowSpacing);ctx.textBaseline='middle';ctx.font=(14*pixelRatio)+'px sans-serif';var textPosY=arrowPosY;var interestRange=vp.interestRange;if(interestRange.range===0){var markerWorld=interestRange.min;var markerView=dt.xWorldToView(markerWorld);var textToDraw=vp.majorMarkUnit.format(markerWorld);var textLeftView=markerView+4*pixelRatio;var textWidthView=ctx.measureText(textToDraw).width;if(textLeftView+textWidthView>width){textLeftView=markerView-4*pixelRatio-textWidthView;}
+ctx.fillStyle=displayTextColor;ctx.fillText(textToDraw,textLeftView,textPosY);return;}
+var leftMarker=interestRange.min;var rightMarker=interestRange.max;var leftMarkerView=dt.xWorldToView(leftMarker);var rightMarkerView=dt.xWorldToView(rightMarker);var distanceBetweenMarkers=interestRange.range;var distanceBetweenMarkersView=dt.xWorldVectorToView(distanceBetweenMarkers);var positionInMiddleOfMarkersView=leftMarkerView+(distanceBetweenMarkersView/2);var textToDraw=vp.majorMarkUnit.format(distanceBetweenMarkers);var textWidthView=ctx.measureText(textToDraw).width;var spaceForArrowsAndTextView=textWidthView+spaceForArrowsView+arrowSpacing;var textLeftView=positionInMiddleOfMarkersView-textWidthView/2;var textRightView=textLeftView+textWidthView;if(spaceForArrowsAndTextView>distanceBetweenMarkersView){textLeftView=rightMarkerView+2*arrowSpacing;if(textLeftView+textWidthView>width){textLeftView=leftMarkerView-2*arrowSpacing-textWidthView;}
+ctx.fillStyle=displayTextColor;ctx.fillText(textToDraw,textLeftView,textPosY);ctx.strokeStyle=arrowColor;ctx.beginPath();tr.ui.b.drawLine(ctx,leftMarkerView,arrowPosY,rightMarkerView,arrowPosY);ctx.stroke();ctx.fillStyle=arrowColor;tr.ui.b.drawArrow(ctx,leftMarkerView-1.5*arrowSpacing,arrowPosY,leftMarkerView,arrowPosY,arrowLengthView,arrowWidthView);tr.ui.b.drawArrow(ctx,rightMarkerView+1.5*arrowSpacing,arrowPosY,rightMarkerView,arrowPosY,arrowLengthView,arrowWidthView);}else if(spaceForArrowsView<=distanceBetweenMarkersView){var leftArrowStart;var rightArrowStart;if(spaceForArrowsAndTextView<=distanceBetweenMarkersView){ctx.fillStyle=displayTextColor;ctx.fillText(textToDraw,textLeftView,textPosY);leftArrowStart=textLeftView-arrowSpacing;rightArrowStart=textRightView+arrowSpacing;}else{leftArrowStart=positionInMiddleOfMarkersView;rightArrowStart=positionInMiddleOfMarkersView;}
 ctx.strokeStyle=arrowColor;ctx.fillStyle=arrowColor;tr.ui.b.drawArrow(ctx,leftArrowStart,arrowPosY,leftMarkerView,arrowPosY,arrowLengthView,arrowWidthView);tr.ui.b.drawArrow(ctx,rightArrowStart,arrowPosY,rightMarkerView,arrowPosY,arrowLengthView,arrowWidthView);}
-ctx.restore();},addIntersectingEventsInRangeToSelection:function(loVX,hiVX,loY,hiY,selection){},addAllEventsMatchingFilterToSelection:function(filter,selection){}};return{RulerTrack:RulerTrack};});'use strict';Polymer({is:'tr-ui-timeline-track-view',ready:function(){this.displayTransform_=new tr.ui.TimelineDisplayTransform();this.model_=undefined;this.timelineView_=undefined;this.viewport_=new tr.ui.TimelineViewport(this);this.viewportDisplayTransformAtMouseDown_=undefined;this.brushingStateController_=undefined;this.rulerTrackContainer_=new tr.ui.tracks.DrawingContainer(this.viewport_);Polymer.dom(this).appendChild(this.rulerTrackContainer_);this.rulerTrackContainer_.invalidate();this.rulerTrack_=new tr.ui.tracks.RulerTrack(this.viewport_);Polymer.dom(this.rulerTrackContainer_).appendChild(this.rulerTrack_);this.upperModelTrack_=new tr.ui.tracks.ModelTrack(this.viewport_);this.upperModelTrack_.upperMode=true;Polymer.dom(this.rulerTrackContainer_).appendChild(this.upperModelTrack_);this.modelTrackContainer_=new tr.ui.tracks.DrawingContainer(this.viewport_);Polymer.dom(this).appendChild(this.modelTrackContainer_);this.modelTrackContainer_.style.display='block';this.modelTrackContainer_.invalidate();this.viewport_.modelTrackContainer=this.modelTrackContainer_;this.modelTrack_=new tr.ui.tracks.ModelTrack(this.viewport_);Polymer.dom(this.modelTrackContainer_).appendChild(this.modelTrack_);this.timingTool_=new tr.ui.b.TimingTool(this.viewport_,this);this.initMouseModeSelector();this.hideDragBox_();this.initHintText_();this.onSelectionChanged_=this.onSelectionChanged_.bind(this);this.onDblClick_=this.onDblClick_.bind(this);this.addEventListener('dblclick',this.onDblClick_);this.onMouseWheel_=this.onMouseWheel_.bind(this);this.addEventListener('mousewheel',this.onMouseWheel_);this.onMouseDown_=this.onMouseDown_.bind(this);this.addEventListener('mousedown',this.onMouseDown_);this.onMouseMove_=this.onMouseMove_.bind(this);this.addEventListener('mousemove',this.onMouseMove_);this.onTouchStart_=this.onTouchStart_.bind(this);this.addEventListener('touchstart',this.onTouchStart_);this.onTouchMove_=this.onTouchMove_.bind(this);this.addEventListener('touchmove',this.onTouchMove_);this.onTouchEnd_=this.onTouchEnd_.bind(this);this.addEventListener('touchend',this.onTouchEnd_);this.addHotKeys_();this.mouseViewPosAtMouseDown_={x:0,y:0};this.lastMouseViewPos_={x:0,y:0};this.lastTouchViewPositions_=[];this.alert_=undefined;this.isPanningAndScanning_=false;this.isZooming_=false;},initMouseModeSelector:function(){this.mouseModeSelector_=document.createElement('tr-ui-b-mouse-mode-selector');this.mouseModeSelector_.targetElement=this;Polymer.dom(this).appendChild(this.mouseModeSelector_);this.mouseModeSelector_.addEventListener('beginpan',this.onBeginPanScan_.bind(this));this.mouseModeSelector_.addEventListener('updatepan',this.onUpdatePanScan_.bind(this));this.mouseModeSelector_.addEventListener('endpan',this.onEndPanScan_.bind(this));this.mouseModeSelector_.addEventListener('beginselection',this.onBeginSelection_.bind(this));this.mouseModeSelector_.addEventListener('updateselection',this.onUpdateSelection_.bind(this));this.mouseModeSelector_.addEventListener('endselection',this.onEndSelection_.bind(this));this.mouseModeSelector_.addEventListener('beginzoom',this.onBeginZoom_.bind(this));this.mouseModeSelector_.addEventListener('updatezoom',this.onUpdateZoom_.bind(this));this.mouseModeSelector_.addEventListener('endzoom',this.onEndZoom_.bind(this));this.mouseModeSelector_.addEventListener('entertiming',this.timingTool_.onEnterTiming.bind(this.timingTool_));this.mouseModeSelector_.addEventListener('begintiming',this.timingTool_.onBeginTiming.bind(this.timingTool_));this.mouseModeSelector_.addEventListener('updatetiming',this.timingTool_.onUpdateTiming.bind(this.timingTool_));this.mouseModeSelector_.addEventListener('endtiming',this.timingTool_.onEndTiming.bind(this.timingTool_));this.mouseModeSelector_.addEventListener('exittiming',this.timingTool_.onExitTiming.bind(this.timingTool_));var m=tr.ui.b.MOUSE_SELECTOR_MODE;this.mouseModeSelector_.supportedModeMask=m.SELECTION|m.PANSCAN|m.ZOOM|m.TIMING;this.mouseModeSelector_.settingsKey='timelineTrackView.mouseModeSelector';this.mouseModeSelector_.setKeyCodeForMode(m.PANSCAN,'2'.charCodeAt(0));this.mouseModeSelector_.setKeyCodeForMode(m.SELECTION,'1'.charCodeAt(0));this.mouseModeSelector_.setKeyCodeForMode(m.ZOOM,'3'.charCodeAt(0));this.mouseModeSelector_.setKeyCodeForMode(m.TIMING,'4'.charCodeAt(0));this.mouseModeSelector_.setModifierForAlternateMode(m.SELECTION,tr.ui.b.MODIFIER.SHIFT);this.mouseModeSelector_.setModifierForAlternateMode(m.PANSCAN,tr.ui.b.MODIFIER.SPACE);},get brushingStateController(){return this.brushingStateController_;},set brushingStateController(brushingStateController){if(this.brushingStateController_){this.brushingStateController_.removeEventListener('change',this.onSelectionChanged_);}
-this.brushingStateController_=brushingStateController;if(this.brushingStateController_){this.brushingStateController_.addEventListener('change',this.onSelectionChanged_);}},set timelineView(view){this.timelineView_=view;},onSelectionChanged_:function(){this.showHintText_('Press \'m\' to mark current selection');this.viewport_.dispatchChangeEvent();},set selection(selection){throw new Error('DO NOT CALL THIS');},set highlight(highlight){throw new Error('DO NOT CALL THIS');},detach:function(){this.modelTrack_.detach();this.upperModelTrack_.detach();this.viewport_.detach();},get viewport(){return this.viewport_;},get model(){return this.model_;},set model(model){if(!model)
-throw new Error('Model cannot be undefined');var modelInstanceChanged=this.model_!==model;this.model_=model;this.modelTrack_.model=model;this.upperModelTrack_.model=model;if(modelInstanceChanged)
-this.viewport_.setWhenPossible(this.setInitialViewport_.bind(this));},get hasVisibleContent(){return this.modelTrack_.hasVisibleContent||this.upperModelTrack_.hasVisibleContent;},setInitialViewport_:function(){this.modelTrackContainer_.updateCanvasSizeIfNeeded_();var w=this.modelTrackContainer_.canvas.width;var min;var range;if(this.model_.bounds.isEmpty){min=0;range=1000;}else if(this.model_.bounds.range===0){min=this.model_.bounds.min;range=1000;}else{min=this.model_.bounds.min;range=this.model_.bounds.range;}
-var boost=range*0.15;this.displayTransform_.set(this.viewport_.currentDisplayTransform);this.displayTransform_.xSetWorldBounds(min-boost,min+range+boost,w);this.viewport_.setDisplayTransformImmediately(this.displayTransform_);},addAllEventsMatchingFilterToSelectionAsTask:function(filter,selection){var modelTrack=this.modelTrack_;var firstT=modelTrack.addAllEventsMatchingFilterToSelectionAsTask(filter,selection);var lastT=firstT.after(function(){this.upperModelTrack_.addAllEventsMatchingFilterToSelection(filter,selection);},this);return firstT;},onMouseMove_:function(e){if(this.isZooming_)
-return;this.storeLastMousePos_(e);},onTouchStart_:function(e){this.storeLastTouchPositions_(e);this.focusElements_();},onTouchMove_:function(e){e.preventDefault();this.onUpdateTransformForTouch_(e);},onTouchEnd_:function(e){this.storeLastTouchPositions_(e);this.focusElements_();},addHotKeys_:function(){this.addKeyDownHotKeys_();this.addKeyPressHotKeys_();},addKeyPressHotKeys_:function(){var addBinding=function(dict){dict.eventType='keypress';dict.useCapture=false;dict.thisArg=this;var binding=new tr.ui.b.HotKey(dict);this.$.hotkey_controller.addHotKey(binding);}.bind(this);addBinding({keyCodes:['w'.charCodeAt(0),','.charCodeAt(0)],callback:function(e){this.zoomBy_(1.5,true);e.stopPropagation();}});addBinding({keyCodes:['s'.charCodeAt(0),'o'.charCodeAt(0)],callback:function(e){this.zoomBy_(1/1.5,true);e.stopPropagation();}});addBinding({keyCode:'g'.charCodeAt(0),callback:function(e){this.onGridToggle_(true);e.stopPropagation();}});addBinding({keyCode:'G'.charCodeAt(0),callback:function(e){this.onGridToggle_(false);e.stopPropagation();}});addBinding({keyCodes:['W'.charCodeAt(0),'<'.charCodeAt(0)],callback:function(e){this.zoomBy_(10,true);e.stopPropagation();}});addBinding({keyCodes:['S'.charCodeAt(0),'O'.charCodeAt(0)],callback:function(e){this.zoomBy_(1/10,true);e.stopPropagation();}});addBinding({keyCode:'a'.charCodeAt(0),callback:function(e){this.queueSmoothPan_(this.viewWidth_*0.3,0);e.stopPropagation();}});addBinding({keyCodes:['d'.charCodeAt(0),'e'.charCodeAt(0)],callback:function(e){this.queueSmoothPan_(this.viewWidth_*-0.3,0);e.stopPropagation();}});addBinding({keyCode:'A'.charCodeAt(0),callback:function(e){this.queueSmoothPan_(viewWidth*0.5,0);e.stopPropagation();}});addBinding({keyCode:'D'.charCodeAt(0),callback:function(e){this.queueSmoothPan_(viewWidth*-0.5,0);e.stopPropagation();}});addBinding({keyCode:'0'.charCodeAt(0),callback:function(e){this.setInitialViewport_();e.stopPropagation();}});addBinding({keyCode:'f'.charCodeAt(0),callback:function(e){this.zoomToSelection();e.stopPropagation();}});addBinding({keyCode:'m'.charCodeAt(0),callback:function(e){this.setCurrentSelectionAsInterestRange_();e.stopPropagation();}});addBinding({keyCode:'p'.charCodeAt(0),callback:function(e){this.selectPowerSamplesInCurrentTimeRange_();e.stopPropagation();}});addBinding({keyCode:'h'.charCodeAt(0),callback:function(e){this.toggleHighDetails_();e.stopPropagation();}});},get viewWidth_(){return this.modelTrackContainer_.canvas.clientWidth;},addKeyDownHotKeys_:function(){var addBinding=function(dict){dict.eventType='keydown';dict.useCapture=false;dict.thisArg=this;var binding=new tr.ui.b.HotKey(dict);this.$.hotkey_controller.addHotKey(binding);}.bind(this);addBinding({keyCode:37,callback:function(e){var curSel=this.brushingStateController_.selection;var sel=this.viewport.getShiftedSelection(curSel,-1);if(sel){this.brushingStateController.changeSelectionFromTimeline(sel);this.panToSelection();}else{this.queueSmoothPan_(this.viewWidth_*0.3,0);}
+ctx.restore();},addIntersectingEventsInRangeToSelection:function(loVX,hiVX,loY,hiY,selection){},addAllEventsMatchingFilterToSelection:function(filter,selection){}};return{XAxisTrack,};});'use strict';Polymer({is:'tr-ui-timeline-track-view',ready:function(){this.displayTransform_=new tr.ui.TimelineDisplayTransform();this.model_=undefined;this.timelineView_=undefined;this.pollIfViewportAttachedInterval_=undefined;this.viewport_=new tr.ui.TimelineViewport(this);this.viewportDisplayTransformAtMouseDown_=undefined;this.brushingStateController_=undefined;this.rulerTrackContainer_=new tr.ui.tracks.DrawingContainer(this.viewport_);Polymer.dom(this).appendChild(this.rulerTrackContainer_);this.rulerTrackContainer_.invalidate();this.rulerTrack_=new tr.ui.tracks.XAxisTrack(this.viewport_);Polymer.dom(this.rulerTrackContainer_).appendChild(this.rulerTrack_);this.upperModelTrack_=new tr.ui.tracks.ModelTrack(this.viewport_);this.upperModelTrack_.upperMode=true;Polymer.dom(this.rulerTrackContainer_).appendChild(this.upperModelTrack_);this.modelTrackContainer_=new tr.ui.tracks.DrawingContainer(this.viewport_);Polymer.dom(this).appendChild(this.modelTrackContainer_);this.modelTrackContainer_.style.display='block';this.modelTrackContainer_.invalidate();this.viewport_.modelTrackContainer=this.modelTrackContainer_;this.modelTrack_=new tr.ui.tracks.ModelTrack(this.viewport_);Polymer.dom(this.modelTrackContainer_).appendChild(this.modelTrack_);this.timingTool_=new tr.ui.b.TimingTool(this.viewport_,this);this.initMouseModeSelector();this.hideDragBox_();this.initHintText_();this.onSelectionChanged_=this.onSelectionChanged_.bind(this);this.onDblClick_=this.onDblClick_.bind(this);this.addEventListener('dblclick',this.onDblClick_);this.onMouseWheel_=this.onMouseWheel_.bind(this);this.addEventListener('mousewheel',this.onMouseWheel_);this.onMouseDown_=this.onMouseDown_.bind(this);this.addEventListener('mousedown',this.onMouseDown_);this.onMouseMove_=this.onMouseMove_.bind(this);this.addEventListener('mousemove',this.onMouseMove_);this.onTouchStart_=this.onTouchStart_.bind(this);this.addEventListener('touchstart',this.onTouchStart_);this.onTouchMove_=this.onTouchMove_.bind(this);this.addEventListener('touchmove',this.onTouchMove_);this.onTouchEnd_=this.onTouchEnd_.bind(this);this.addEventListener('touchend',this.onTouchEnd_);this.addHotKeys_();this.mouseViewPosAtMouseDown_={x:0,y:0};this.lastMouseViewPos_={x:0,y:0};this.lastTouchViewPositions_=[];this.alert_=undefined;this.isPanningAndScanning_=false;this.isZooming_=false;},initMouseModeSelector:function(){this.mouseModeSelector_=document.createElement('tr-ui-b-mouse-mode-selector');this.mouseModeSelector_.targetElement=this;Polymer.dom(this).appendChild(this.mouseModeSelector_);this.mouseModeSelector_.addEventListener('beginpan',this.onBeginPanScan_.bind(this));this.mouseModeSelector_.addEventListener('updatepan',this.onUpdatePanScan_.bind(this));this.mouseModeSelector_.addEventListener('endpan',this.onEndPanScan_.bind(this));this.mouseModeSelector_.addEventListener('beginselection',this.onBeginSelection_.bind(this));this.mouseModeSelector_.addEventListener('updateselection',this.onUpdateSelection_.bind(this));this.mouseModeSelector_.addEventListener('endselection',this.onEndSelection_.bind(this));this.mouseModeSelector_.addEventListener('beginzoom',this.onBeginZoom_.bind(this));this.mouseModeSelector_.addEventListener('updatezoom',this.onUpdateZoom_.bind(this));this.mouseModeSelector_.addEventListener('endzoom',this.onEndZoom_.bind(this));this.mouseModeSelector_.addEventListener('entertiming',this.timingTool_.onEnterTiming.bind(this.timingTool_));this.mouseModeSelector_.addEventListener('begintiming',this.timingTool_.onBeginTiming.bind(this.timingTool_));this.mouseModeSelector_.addEventListener('updatetiming',this.timingTool_.onUpdateTiming.bind(this.timingTool_));this.mouseModeSelector_.addEventListener('endtiming',this.timingTool_.onEndTiming.bind(this.timingTool_));this.mouseModeSelector_.addEventListener('exittiming',this.timingTool_.onExitTiming.bind(this.timingTool_));var m=tr.ui.b.MOUSE_SELECTOR_MODE;this.mouseModeSelector_.supportedModeMask=m.SELECTION|m.PANSCAN|m.ZOOM|m.TIMING;this.mouseModeSelector_.settingsKey='timelineTrackView.mouseModeSelector';this.mouseModeSelector_.setKeyCodeForMode(m.PANSCAN,'2'.charCodeAt(0));this.mouseModeSelector_.setKeyCodeForMode(m.SELECTION,'1'.charCodeAt(0));this.mouseModeSelector_.setKeyCodeForMode(m.ZOOM,'3'.charCodeAt(0));this.mouseModeSelector_.setKeyCodeForMode(m.TIMING,'4'.charCodeAt(0));this.mouseModeSelector_.setModifierForAlternateMode(m.SELECTION,tr.ui.b.MODIFIER.SHIFT);this.mouseModeSelector_.setModifierForAlternateMode(m.PANSCAN,tr.ui.b.MODIFIER.SPACE);},get brushingStateController(){return this.brushingStateController_;},set brushingStateController(brushingStateController){if(this.brushingStateController_){this.brushingStateController_.removeEventListener('change',this.onSelectionChanged_);}
+this.brushingStateController_=brushingStateController;if(this.brushingStateController_){this.brushingStateController_.addEventListener('change',this.onSelectionChanged_);}},set timelineView(view){this.timelineView_=view;},onSelectionChanged_:function(){this.showHintText_('Press \'m\' to mark current selection');this.viewport_.dispatchChangeEvent();},set selection(selection){throw new Error('DO NOT CALL THIS');},set highlight(highlight){throw new Error('DO NOT CALL THIS');},detach:function(){this.modelTrack_.detach();this.upperModelTrack_.detach();if(this.pollIfViewportAttachedInterval_){window.clearInterval(this.pollIfViewportAttachedInterval_);this.pollIfViewportAttachedInterval_=undefined;}
+this.viewport_.detach();},get viewport(){return this.viewport_;},get model(){return this.model_;},set model(model){if(!model){throw new Error('Model cannot be undefined');}
+var modelInstanceChanged=this.model_!==model;this.model_=model;this.modelTrack_.model=model;this.upperModelTrack_.model=model;if(modelInstanceChanged){this.pollIfViewportAttachedInterval_=window.setInterval(this.pollIfViewportAttached_.bind(this),250);}},get hasVisibleContent(){return this.modelTrack_.hasVisibleContent||this.upperModelTrack_.hasVisibleContent;},pollIfViewportAttached_:function(){if(!this.viewport_.isAttachedToDocumentOrInTestMode||this.viewport_.clientWidth===0){return;}
+window.addEventListener('resize',this.viewport_.dispatchChangeEvent);window.clearInterval(this.pollIfViewportAttachedInterval_);this.pollIfViewportAttachedInterval_=undefined;this.setInitialViewport_();},setInitialViewport_:function(){this.modelTrackContainer_.updateCanvasSizeIfNeeded_();var w=this.modelTrackContainer_.canvas.width;var min;var range;if(this.model_.bounds.isEmpty){min=0;range=1000;}else if(this.model_.bounds.range===0){min=this.model_.bounds.min;range=1000;}else{min=this.model_.bounds.min;range=this.model_.bounds.range;}
+var boost=range*0.15;this.displayTransform_.set(this.viewport_.currentDisplayTransform);this.displayTransform_.xSetWorldBounds(min-boost,min+range+boost,w);this.viewport_.setDisplayTransformImmediately(this.displayTransform_);},addAllEventsMatchingFilterToSelectionAsTask:function(filter,selection){var modelTrack=this.modelTrack_;var firstT=modelTrack.addAllEventsMatchingFilterToSelectionAsTask(filter,selection);var lastT=firstT.after(function(){this.upperModelTrack_.addAllEventsMatchingFilterToSelection(filter,selection);},this);return firstT;},onMouseMove_:function(e){if(this.isZooming_)return;this.storeLastMousePos_(e);},onTouchStart_:function(e){this.storeLastTouchPositions_(e);this.focusElements_();},onTouchMove_:function(e){e.preventDefault();this.onUpdateTransformForTouch_(e);},onTouchEnd_:function(e){this.storeLastTouchPositions_(e);this.focusElements_();},addHotKeys_:function(){this.addKeyDownHotKeys_();this.addKeyPressHotKeys_();},addKeyPressHotKey:function(dict){dict.eventType='keypress';dict.useCapture=false;dict.thisArg=this;var binding=new tr.ui.b.HotKey(dict);this.$.hotkey_controller.addHotKey(binding);},addKeyPressHotKeys_:function(){this.addKeyPressHotKey({keyCodes:['w'.charCodeAt(0),','.charCodeAt(0)],callback:function(e){this.zoomBy_(1.5,true);e.stopPropagation();}});this.addKeyPressHotKey({keyCodes:['s'.charCodeAt(0),'o'.charCodeAt(0)],callback:function(e){this.zoomBy_(1/1.5,true);e.stopPropagation();}});this.addKeyPressHotKey({keyCode:'g'.charCodeAt(0),callback:function(e){this.onGridToggle_(true);e.stopPropagation();}});this.addKeyPressHotKey({keyCode:'G'.charCodeAt(0),callback:function(e){this.onGridToggle_(false);e.stopPropagation();}});this.addKeyPressHotKey({keyCodes:['W'.charCodeAt(0),'<'.charCodeAt(0)],callback:function(e){this.zoomBy_(10,true);e.stopPropagation();}});this.addKeyPressHotKey({keyCodes:['S'.charCodeAt(0),'O'.charCodeAt(0)],callback:function(e){this.zoomBy_(1/10,true);e.stopPropagation();}});this.addKeyPressHotKey({keyCode:'a'.charCodeAt(0),callback:function(e){this.queueSmoothPan_(this.viewWidth_*0.3,0);e.stopPropagation();}});this.addKeyPressHotKey({keyCodes:['d'.charCodeAt(0),'e'.charCodeAt(0)],callback:function(e){this.queueSmoothPan_(this.viewWidth_*-0.3,0);e.stopPropagation();}});this.addKeyPressHotKey({keyCode:'A'.charCodeAt(0),callback:function(e){this.queueSmoothPan_(viewWidth*0.5,0);e.stopPropagation();}});this.addKeyPressHotKey({keyCode:'D'.charCodeAt(0),callback:function(e){this.queueSmoothPan_(viewWidth*-0.5,0);e.stopPropagation();}});this.addKeyPressHotKey({keyCode:'0'.charCodeAt(0),callback:function(e){this.setInitialViewport_();e.stopPropagation();}});this.addKeyPressHotKey({keyCode:'f'.charCodeAt(0),callback:function(e){this.zoomToSelection();e.stopPropagation();}});this.addKeyPressHotKey({keyCode:'m'.charCodeAt(0),callback:function(e){this.setCurrentSelectionAsInterestRange_();e.stopPropagation();}});this.addKeyPressHotKey({keyCode:'p'.charCodeAt(0),callback:function(e){this.selectPowerSamplesInCurrentTimeRange_();e.stopPropagation();}});this.addKeyPressHotKey({keyCode:'h'.charCodeAt(0),callback:function(e){this.toggleHighDetails_();e.stopPropagation();}});},get viewWidth_(){return this.modelTrackContainer_.canvas.clientWidth;},addKeyDownHotKeys_:function(){var addBinding=function(dict){dict.eventType='keydown';dict.useCapture=false;dict.thisArg=this;var binding=new tr.ui.b.HotKey(dict);this.$.hotkey_controller.addHotKey(binding);}.bind(this);addBinding({keyCode:37,callback:function(e){var curSel=this.brushingStateController_.selection;var sel=this.viewport.getShiftedSelection(curSel,-1);if(sel){this.brushingStateController.changeSelectionFromTimeline(sel);this.panToSelection();}else{this.queueSmoothPan_(this.viewWidth_*0.3,0);}
 e.preventDefault();e.stopPropagation();}});addBinding({keyCode:39,callback:function(e){var curSel=this.brushingStateController_.selection;var sel=this.viewport.getShiftedSelection(curSel,1);if(sel){this.brushingStateController.changeSelectionFromTimeline(sel);this.panToSelection();}else{this.queueSmoothPan_(-this.viewWidth_*0.3,0);}
-e.preventDefault();e.stopPropagation();}});},onDblClick_:function(e){if(this.mouseModeSelector_.mode!==tr.ui.b.MOUSE_SELECTOR_MODE.SELECTION)
-return;var curSelection=this.brushingStateController_.selection;if(!curSelection.length||!tr.b.getOnlyElement(curSelection).title)
-return;var selection=new tr.model.EventSet();var filter=new tr.c.ExactTitleFilter(tr.b.getOnlyElement(curSelection).title);this.modelTrack_.addAllEventsMatchingFilterToSelection(filter,selection);this.brushingStateController.changeSelectionFromTimeline(selection);},onMouseWheel_:function(e){if(!e.altKey)
-return;var delta=e.wheelDelta/120;var zoomScale=Math.pow(1.5,delta);this.zoomBy_(zoomScale);e.preventDefault();},onMouseDown_:function(e){if(this.mouseModeSelector_.mode!==tr.ui.b.MOUSE_SELECTOR_MODE.SELECTION)
-return;if(e.target!==this.rulerTrack_)
-return;this.dragBeginEvent_=undefined;if(this.xNavStringMarker_){this.model.removeAnnotation(this.xNavStringMarker_);this.xNavStringMarker_=undefined;}
-var dt=this.viewport_.currentDisplayTransform;tr.ui.b.trackMouseMovesUntilMouseUp(function(e){if(e.target===this.rulerTrack_)
-return;var relativePosition=this.extractRelativeMousePosition_(e);var loc=tr.model.Location.fromViewCoordinates(this.viewport_,relativePosition.x,relativePosition.y);if(!loc)
-return;if(this.guideLineAnnotation_===undefined){this.guideLineAnnotation_=new tr.model.XMarkerAnnotation(loc.xWorld);this.model.addAnnotation(this.guideLineAnnotation_);}else{this.guideLineAnnotation_.timestamp=loc.xWorld;this.modelTrackContainer_.invalidate();}
+e.preventDefault();e.stopPropagation();}});},onDblClick_:function(e){if(this.mouseModeSelector_.mode!==tr.ui.b.MOUSE_SELECTOR_MODE.SELECTION){return;}
+var curSelection=this.brushingStateController_.selection;if(!curSelection.length||!tr.b.getOnlyElement(curSelection).title){return;}
+var selection=new tr.model.EventSet();var filter=new tr.c.ExactTitleFilter(tr.b.getOnlyElement(curSelection).title);this.modelTrack_.addAllEventsMatchingFilterToSelection(filter,selection);this.brushingStateController.changeSelectionFromTimeline(selection);},onMouseWheel_:function(e){if(!e.altKey)return;var delta=e.wheelDelta/120;var zoomScale=Math.pow(1.5,delta);this.zoomBy_(zoomScale);e.preventDefault();},onMouseDown_:function(e){if(this.mouseModeSelector_.mode!==tr.ui.b.MOUSE_SELECTOR_MODE.SELECTION){return;}
+if(e.target!==this.rulerTrack_)return;this.dragBeginEvent_=undefined;if(this.xNavStringMarker_){this.model.removeAnnotation(this.xNavStringMarker_);this.xNavStringMarker_=undefined;}
+var dt=this.viewport_.currentDisplayTransform;tr.ui.b.trackMouseMovesUntilMouseUp(function(e){if(e.target===this.rulerTrack_)return;var relativePosition=this.extractRelativeMousePosition_(e);var loc=tr.model.Location.fromViewCoordinates(this.viewport_,relativePosition.x,relativePosition.y);if(!loc)return;if(this.guideLineAnnotation_===undefined){this.guideLineAnnotation_=new tr.model.XMarkerAnnotation(loc.xWorld);this.model.addAnnotation(this.guideLineAnnotation_);}else{this.guideLineAnnotation_.timestamp=loc.xWorld;this.modelTrackContainer_.invalidate();}
 var state=new tr.ui.b.UIState(loc,this.viewport_.currentDisplayTransform.scaleX);this.timelineView_.setFindCtlText(state.toUserFriendlyString(this.viewport_));}.bind(this),undefined,function onKeyUpDuringDrag(){if(this.dragBeginEvent_){this.setDragBoxPosition_(this.dragBoxXStart_,this.dragBoxYStart_,this.dragBoxXEnd_,this.dragBoxYEnd_);}}.bind(this));},queueSmoothPan_:function(viewDeltaX,deltaY){var deltaX=this.viewport_.currentDisplayTransform.xViewVectorToWorld(viewDeltaX);var animation=new tr.ui.TimelineDisplayTransformPanAnimation(deltaX,deltaY);this.viewport_.queueDisplayTransformAnimation(animation);},zoomBy_:function(scale,smooth){if(scale<=0){return;}
-smooth=!!smooth;var vp=this.viewport_;var pixelRatio=window.devicePixelRatio||1;var goalFocalPointXView=this.lastMouseViewPos_.x*pixelRatio;var goalFocalPointXWorld=vp.currentDisplayTransform.xViewToWorld(goalFocalPointXView);if(smooth){var animation=new tr.ui.TimelineDisplayTransformZoomToAnimation(goalFocalPointXWorld,goalFocalPointXView,vp.currentDisplayTransform.panY,scale);vp.queueDisplayTransformAnimation(animation);}else{this.displayTransform_.set(vp.currentDisplayTransform);this.displayTransform_.scaleX*=scale;this.displayTransform_.xPanWorldPosToViewPos(goalFocalPointXWorld,goalFocalPointXView,this.viewWidth_);vp.setDisplayTransformImmediately(this.displayTransform_);}},zoomToSelection:function(){if(!this.brushingStateController.selectionOfInterest.length)
-return;var bounds=this.brushingStateController.selectionOfInterest.bounds;if(!bounds.range)
-return;var worldCenter=bounds.center;var viewCenter=this.modelTrackContainer_.canvas.width/2;var adjustedWorldRange=bounds.range*1.25;var newScale=this.modelTrackContainer_.canvas.width/adjustedWorldRange;var zoomInRatio=newScale/this.viewport_.currentDisplayTransform.scaleX;var animation=new tr.ui.TimelineDisplayTransformZoomToAnimation(worldCenter,viewCenter,this.viewport_.currentDisplayTransform.panY,zoomInRatio);this.viewport_.queueDisplayTransformAnimation(animation);},panToSelection:function(){if(!this.brushingStateController.selectionOfInterest.length)
-return;var bounds=this.brushingStateController.selectionOfInterest.bounds;var worldCenter=bounds.center;var viewWidth=this.viewWidth_;var dt=this.viewport_.currentDisplayTransform;if(false&&!bounds.range){if(dt.xWorldToView(bounds.center)<0||dt.xWorldToView(bounds.center)>viewWidth){this.displayTransform_.set(dt);this.displayTransform_.xPanWorldPosToViewPos(worldCenter,'center',viewWidth);var deltaX=this.displayTransform_.panX-dt.panX;var animation=new tr.ui.TimelineDisplayTransformPanAnimation(deltaX,0);this.viewport_.queueDisplayTransformAnimation(animation);}
+smooth=!!smooth;var vp=this.viewport_;var pixelRatio=window.devicePixelRatio||1;var goalFocalPointXView=this.lastMouseViewPos_.x*pixelRatio;var goalFocalPointXWorld=vp.currentDisplayTransform.xViewToWorld(goalFocalPointXView);if(smooth){var animation=new tr.ui.TimelineDisplayTransformZoomToAnimation(goalFocalPointXWorld,goalFocalPointXView,vp.currentDisplayTransform.panY,scale);vp.queueDisplayTransformAnimation(animation);}else{this.displayTransform_.set(vp.currentDisplayTransform);this.displayTransform_.scaleX*=scale;this.displayTransform_.xPanWorldPosToViewPos(goalFocalPointXWorld,goalFocalPointXView,this.viewWidth_);vp.setDisplayTransformImmediately(this.displayTransform_);}},zoomToSelection:function(){if(!this.brushingStateController.selectionOfInterest.length)return;var bounds=this.brushingStateController.selectionOfInterest.bounds;if(!bounds.range)return;var worldCenter=bounds.center;var viewCenter=this.modelTrackContainer_.canvas.width/2;var adjustedWorldRange=bounds.range*1.25;var newScale=this.modelTrackContainer_.canvas.width/adjustedWorldRange;var zoomInRatio=newScale/this.viewport_.currentDisplayTransform.scaleX;var animation=new tr.ui.TimelineDisplayTransformZoomToAnimation(worldCenter,viewCenter,this.viewport_.currentDisplayTransform.panY,zoomInRatio);this.viewport_.queueDisplayTransformAnimation(animation);},panToSelection:function(){if(!this.brushingStateController.selectionOfInterest.length)return;var bounds=this.brushingStateController.selectionOfInterest.bounds;var worldCenter=bounds.center;var viewWidth=this.viewWidth_;var dt=this.viewport_.currentDisplayTransform;if(false&&!bounds.range){if(dt.xWorldToView(bounds.center)<0||dt.xWorldToView(bounds.center)>viewWidth){this.displayTransform_.set(dt);this.displayTransform_.xPanWorldPosToViewPos(worldCenter,'center',viewWidth);var deltaX=this.displayTransform_.panX-dt.panX;var animation=new tr.ui.TimelineDisplayTransformPanAnimation(deltaX,0);this.viewport_.queueDisplayTransformAnimation(animation);}
 return;}
-this.displayTransform_.set(dt);this.displayTransform_.xPanWorldBoundsIntoView(bounds.min,bounds.max,viewWidth);var deltaX=this.displayTransform_.panX-dt.panX;var animation=new tr.ui.TimelineDisplayTransformPanAnimation(deltaX,0);this.viewport_.queueDisplayTransformAnimation(animation);},navToPosition:function(uiState,showNavLine){var location=uiState.location;var scaleX=uiState.scaleX;var track=location.getContainingTrack(this.viewport_);var worldCenter=location.xWorld;var viewCenter=this.modelTrackContainer_.canvas.width/5;var zoomInRatio=scaleX/this.viewport_.currentDisplayTransform.scaleX;track.scrollIntoViewIfNeeded();var animation=new tr.ui.TimelineDisplayTransformZoomToAnimation(worldCenter,viewCenter,this.viewport_.currentDisplayTransform.panY,zoomInRatio);this.viewport_.queueDisplayTransformAnimation(animation);if(!showNavLine)
-return;if(this.xNavStringMarker_)
-this.model.removeAnnotation(this.xNavStringMarker_);this.xNavStringMarker_=new tr.model.XMarkerAnnotation(worldCenter);this.model.addAnnotation(this.xNavStringMarker_);},selectPowerSamplesInCurrentTimeRange_:function(){var selectionBounds=this.brushingStateController_.selection.bounds;if(this.model.device.powerSeries&&!selectionBounds.empty){var events=this.model.device.powerSeries.getSamplesWithinRange(selectionBounds.min,selectionBounds.max);var selection=new tr.model.EventSet(events);this.brushingStateController_.changeSelectionFromTimeline(selection);}},setCurrentSelectionAsInterestRange_:function(){var selectionBounds=this.brushingStateController_.selection.bounds;if(selectionBounds.empty){this.viewport_.interestRange.reset();return;}
-if(this.viewport_.interestRange.min==selectionBounds.min&&this.viewport_.interestRange.max==selectionBounds.max)
-this.viewport_.interestRange.reset();else
-this.viewport_.interestRange.set(selectionBounds);},toggleHighDetails_:function(){this.viewport_.highDetails=!this.viewport_.highDetails;},hideDragBox_:function(){this.$.drag_box.style.left='-1000px';this.$.drag_box.style.top='-1000px';this.$.drag_box.style.width=0;this.$.drag_box.style.height=0;},setDragBoxPosition_:function(xStart,yStart,xEnd,yEnd){var loY=Math.min(yStart,yEnd);var hiY=Math.max(yStart,yEnd);var loX=Math.min(xStart,xEnd);var hiX=Math.max(xStart,xEnd);var modelTrackRect=this.modelTrack_.getBoundingClientRect();var dragRect={left:loX,top:loY,width:hiX-loX,height:hiY-loY};dragRect.right=dragRect.left+dragRect.width;dragRect.bottom=dragRect.top+dragRect.height;var modelTrackContainerRect=this.modelTrackContainer_.getBoundingClientRect();var clipRect={left:modelTrackContainerRect.left,top:modelTrackContainerRect.top,right:modelTrackContainerRect.right,bottom:modelTrackContainerRect.bottom};var headingWidth=window.getComputedStyle(Polymer.dom(this).querySelector('tr-ui-heading')).width;var trackTitleWidth=parseInt(headingWidth);clipRect.left=clipRect.left+trackTitleWidth;var intersectRect_=function(r1,r2){if(r2.left>r1.right||r2.right<r1.left||r2.top>r1.bottom||r2.bottom<r1.top)
-return false;var results={};results.left=Math.max(r1.left,r2.left);results.top=Math.max(r1.top,r2.top);results.right=Math.min(r1.right,r2.right);results.bottom=Math.min(r1.bottom,r2.bottom);results.width=results.right-results.left;results.height=results.bottom-results.top;return results;};var finalDragBox=intersectRect_(clipRect,dragRect);this.$.drag_box.style.left=finalDragBox.left+'px';this.$.drag_box.style.width=finalDragBox.width+'px';this.$.drag_box.style.top=finalDragBox.top+'px';this.$.drag_box.style.height=finalDragBox.height+'px';this.$.drag_box.style.whiteSpace='nowrap';var pixelRatio=window.devicePixelRatio||1;var canv=this.modelTrackContainer_.canvas;var dt=this.viewport_.currentDisplayTransform;var loWX=dt.xViewToWorld((loX-canv.offsetLeft)*pixelRatio);var hiWX=dt.xViewToWorld((hiX-canv.offsetLeft)*pixelRatio);Polymer.dom(this.$.drag_box).textContent=tr.b.Unit.byName.timeDurationInMs.format(hiWX-loWX);var e=new tr.b.Event('selectionChanging');e.loWX=loWX;e.hiWX=hiWX;this.dispatchEvent(e);},onGridToggle_:function(left){var selection=this.brushingStateController_.selection;var tb=left?selection.bounds.min:selection.bounds.max;if(this.viewport_.gridEnabled&&this.viewport_.gridSide===left&&this.viewport_.gridInitialTimebase===tb){this.viewport_.gridside=undefined;this.viewport_.gridEnabled=false;this.viewport_.gridInitialTimebase=undefined;return;}
+this.displayTransform_.set(dt);this.displayTransform_.xPanWorldBoundsIntoView(bounds.min,bounds.max,viewWidth);var deltaX=this.displayTransform_.panX-dt.panX;var animation=new tr.ui.TimelineDisplayTransformPanAnimation(deltaX,0);this.viewport_.queueDisplayTransformAnimation(animation);},navToPosition:function(uiState,showNavLine){var location=uiState.location;var scaleX=uiState.scaleX;var track=location.getContainingTrack(this.viewport_);var worldCenter=location.xWorld;var viewCenter=this.modelTrackContainer_.canvas.width/5;var zoomInRatio=scaleX/this.viewport_.currentDisplayTransform.scaleX;track.scrollIntoViewIfNeeded();var animation=new tr.ui.TimelineDisplayTransformZoomToAnimation(worldCenter,viewCenter,this.viewport_.currentDisplayTransform.panY,zoomInRatio);this.viewport_.queueDisplayTransformAnimation(animation);if(!showNavLine)return;if(this.xNavStringMarker_){this.model.removeAnnotation(this.xNavStringMarker_);}
+this.xNavStringMarker_=new tr.model.XMarkerAnnotation(worldCenter);this.model.addAnnotation(this.xNavStringMarker_);},selectPowerSamplesInCurrentTimeRange_:function(){var selectionBounds=this.brushingStateController_.selection.bounds;if(this.model.device.powerSeries&&!selectionBounds.empty){var events=this.model.device.powerSeries.getSamplesWithinRange(selectionBounds.min,selectionBounds.max);var selection=new tr.model.EventSet(events);this.brushingStateController_.changeSelectionFromTimeline(selection);}},setCurrentSelectionAsInterestRange_:function(){var selectionBounds=this.brushingStateController_.selection.bounds;if(selectionBounds.empty){this.viewport_.interestRange.reset();return;}
+if(this.viewport_.interestRange.min===selectionBounds.min&&this.viewport_.interestRange.max===selectionBounds.max){this.viewport_.interestRange.reset();}else{this.viewport_.interestRange.set(selectionBounds);}},toggleHighDetails_:function(){this.viewport_.highDetails=!this.viewport_.highDetails;},hideDragBox_:function(){this.$.drag_box.style.left='-1000px';this.$.drag_box.style.top='-1000px';this.$.drag_box.style.width=0;this.$.drag_box.style.height=0;},setDragBoxPosition_:function(xStart,yStart,xEnd,yEnd){var loY=Math.min(yStart,yEnd);var hiY=Math.max(yStart,yEnd);var loX=Math.min(xStart,xEnd);var hiX=Math.max(xStart,xEnd);var modelTrackRect=this.modelTrack_.getBoundingClientRect();var dragRect={left:loX,top:loY,width:hiX-loX,height:hiY-loY};dragRect.right=dragRect.left+dragRect.width;dragRect.bottom=dragRect.top+dragRect.height;var modelTrackContainerRect=this.modelTrackContainer_.getBoundingClientRect();var clipRect={left:modelTrackContainerRect.left,top:modelTrackContainerRect.top,right:modelTrackContainerRect.right,bottom:modelTrackContainerRect.bottom};var headingWidth=window.getComputedStyle(Polymer.dom(this).querySelector('tr-ui-b-heading')).width;var trackTitleWidth=parseInt(headingWidth);clipRect.left=clipRect.left+trackTitleWidth;var intersectRect_=function(r1,r2){if(r2.left>r1.right||r2.right<r1.left||r2.top>r1.bottom||r2.bottom<r1.top){return false;}
+var results={};results.left=Math.max(r1.left,r2.left);results.top=Math.max(r1.top,r2.top);results.right=Math.min(r1.right,r2.right);results.bottom=Math.min(r1.bottom,r2.bottom);results.width=results.right-results.left;results.height=results.bottom-results.top;return results;};var finalDragBox=intersectRect_(clipRect,dragRect);this.$.drag_box.style.left=finalDragBox.left+'px';this.$.drag_box.style.width=finalDragBox.width+'px';this.$.drag_box.style.top=finalDragBox.top+'px';this.$.drag_box.style.height=finalDragBox.height+'px';this.$.drag_box.style.whiteSpace='nowrap';var pixelRatio=window.devicePixelRatio||1;var canv=this.modelTrackContainer_.canvas;var dt=this.viewport_.currentDisplayTransform;var loWX=dt.xViewToWorld((loX-canv.offsetLeft)*pixelRatio);var hiWX=dt.xViewToWorld((hiX-canv.offsetLeft)*pixelRatio);Polymer.dom(this.$.drag_box).textContent=tr.b.Unit.byName.timeDurationInMs.format(hiWX-loWX);var e=new tr.b.Event('selectionChanging');e.loWX=loWX;e.hiWX=hiWX;this.dispatchEvent(e);},onGridToggle_:function(left){var selection=this.brushingStateController_.selection;var tb=left?selection.bounds.min:selection.bounds.max;if(this.viewport_.gridEnabled&&this.viewport_.gridSide===left&&this.viewport_.gridInitialTimebase===tb){this.viewport_.gridside=undefined;this.viewport_.gridEnabled=false;this.viewport_.gridInitialTimebase=undefined;return;}
 var numIntervalsSinceStart=Math.ceil((tb-this.model_.bounds.min)/this.viewport_.gridStep_);this.viewport_.gridEnabled=true;this.viewport_.gridSide=left;this.viewport_.gridInitialTimebase=tb;this.viewport_.gridTimebase=tb-
 (numIntervalsSinceStart+1)*this.viewport_.gridStep_;},storeLastMousePos_:function(e){this.lastMouseViewPos_=this.extractRelativeMousePosition_(e);},storeLastTouchPositions_:function(e){this.lastTouchViewPositions_=this.extractRelativeTouchPositions_(e);},extractRelativeMousePosition_:function(e){var canv=this.modelTrackContainer_.canvas;return{x:e.clientX-canv.offsetLeft,y:e.clientY-canv.offsetTop};},extractRelativeTouchPositions_:function(e){var canv=this.modelTrackContainer_.canvas;var touches=[];for(var i=0;i<e.touches.length;++i){touches.push({x:e.touches[i].clientX-canv.offsetLeft,y:e.touches[i].clientY-canv.offsetTop});}
-return touches;},storeInitialMouseDownPos_:function(e){var position=this.extractRelativeMousePosition_(e);this.mouseViewPosAtMouseDown_.x=position.x;this.mouseViewPosAtMouseDown_.y=position.y;},focusElements_:function(){this.$.hotkey_controller.childRequestsGeneralFocus(this);},storeInitialInteractionPositionsAndFocus_:function(e){this.storeInitialMouseDownPos_(e);this.storeLastMousePos_(e);this.focusElements_();},onBeginPanScan_:function(e){var vp=this.viewport_;this.viewportDisplayTransformAtMouseDown_=vp.currentDisplayTransform.clone();this.isPanningAndScanning_=true;this.storeInitialInteractionPositionsAndFocus_(e);e.preventDefault();},onUpdatePanScan_:function(e){if(!this.isPanningAndScanning_)
-return;var viewWidth=this.viewWidth_;var pixelRatio=window.devicePixelRatio||1;var xDeltaView=pixelRatio*(this.lastMouseViewPos_.x-
+return touches;},storeInitialMouseDownPos_:function(e){var position=this.extractRelativeMousePosition_(e);this.mouseViewPosAtMouseDown_.x=position.x;this.mouseViewPosAtMouseDown_.y=position.y;},focusElements_:function(){this.$.hotkey_controller.childRequestsGeneralFocus(this);},storeInitialInteractionPositionsAndFocus_:function(e){this.storeInitialMouseDownPos_(e);this.storeLastMousePos_(e);this.focusElements_();},onBeginPanScan_:function(e){var vp=this.viewport_;this.viewportDisplayTransformAtMouseDown_=vp.currentDisplayTransform.clone();this.isPanningAndScanning_=true;this.storeInitialInteractionPositionsAndFocus_(e);e.preventDefault();},onUpdatePanScan_:function(e){if(!this.isPanningAndScanning_)return;var viewWidth=this.viewWidth_;var pixelRatio=window.devicePixelRatio||1;var xDeltaView=pixelRatio*(this.lastMouseViewPos_.x-
 this.mouseViewPosAtMouseDown_.x);var yDelta=this.lastMouseViewPos_.y-
-this.mouseViewPosAtMouseDown_.y;this.displayTransform_.set(this.viewportDisplayTransformAtMouseDown_);this.displayTransform_.incrementPanXInViewUnits(xDeltaView);this.displayTransform_.panY-=yDelta;this.viewport_.setDisplayTransformImmediately(this.displayTransform_);e.preventDefault();e.stopPropagation();this.storeLastMousePos_(e);},onEndPanScan_:function(e){this.isPanningAndScanning_=false;this.storeLastMousePos_(e);if(!e.isClick)
-e.preventDefault();},onBeginSelection_:function(e){var canv=this.modelTrackContainer_.canvas;var rect=this.modelTrack_.getBoundingClientRect();var canvRect=canv.getBoundingClientRect();var inside=rect&&e.clientX>=rect.left&&e.clientX<rect.right&&e.clientY>=rect.top&&e.clientY<rect.bottom&&e.clientX>=canvRect.left&&e.clientX<canvRect.right;if(!inside)
-return;this.dragBeginEvent_=e;this.storeInitialInteractionPositionsAndFocus_(e);e.preventDefault();},onUpdateSelection_:function(e){if(!this.dragBeginEvent_)
-return;this.dragBoxXStart_=this.dragBeginEvent_.clientX;this.dragBoxXEnd_=e.clientX;this.dragBoxYStart_=this.dragBeginEvent_.clientY;this.dragBoxYEnd_=e.clientY;this.setDragBoxPosition_(this.dragBoxXStart_,this.dragBoxYStart_,this.dragBoxXEnd_,this.dragBoxYEnd_);},onEndSelection_:function(e){e.preventDefault();if(!this.dragBeginEvent_)
-return;this.hideDragBox_();var eDown=this.dragBeginEvent_;this.dragBeginEvent_=undefined;var loY=Math.min(eDown.clientY,e.clientY);var hiY=Math.max(eDown.clientY,e.clientY);var loX=Math.min(eDown.clientX,e.clientX);var hiX=Math.max(eDown.clientX,e.clientX);var canv=this.modelTrackContainer_.canvas;var worldOffset=canv.getBoundingClientRect().left;var loVX=loX-worldOffset;var hiVX=hiX-worldOffset;var selection=new tr.model.EventSet();if(eDown.appendSelection){var previousSelection=this.brushingStateController_.selection;if(previousSelection!==undefined)
-selection.addEventSet(previousSelection);}
-this.modelTrack_.addIntersectingEventsInRangeToSelection(loVX,hiVX,loY,hiY,selection);this.brushingStateController_.changeSelectionFromTimeline(selection);},onBeginZoom_:function(e){this.isZooming_=true;this.storeInitialInteractionPositionsAndFocus_(e);e.preventDefault();},onUpdateZoom_:function(e){if(!this.isZooming_)
-return;var newPosition=this.extractRelativeMousePosition_(e);var zoomScaleValue=1+(this.lastMouseViewPos_.y-
-newPosition.y)*0.01;this.zoomBy_(zoomScaleValue,false);this.storeLastMousePos_(e);},onEndZoom_:function(e){this.isZooming_=false;if(!e.isClick)
-e.preventDefault();},computeTouchCenter_:function(positions){var xSum=0;var ySum=0;for(var i=0;i<positions.length;++i){xSum+=positions[i].x;ySum+=positions[i].y;}
+this.mouseViewPosAtMouseDown_.y;this.displayTransform_.set(this.viewportDisplayTransformAtMouseDown_);this.displayTransform_.incrementPanXInViewUnits(xDeltaView);this.displayTransform_.panY-=yDelta;this.viewport_.setDisplayTransformImmediately(this.displayTransform_);e.preventDefault();e.stopPropagation();this.storeLastMousePos_(e);},onEndPanScan_:function(e){this.isPanningAndScanning_=false;this.storeLastMousePos_(e);if(!e.isClick){e.preventDefault();}},onBeginSelection_:function(e){var canv=this.modelTrackContainer_.canvas;var rect=this.modelTrack_.getBoundingClientRect();var canvRect=canv.getBoundingClientRect();var inside=rect&&e.clientX>=rect.left&&e.clientX<rect.right&&e.clientY>=rect.top&&e.clientY<rect.bottom&&e.clientX>=canvRect.left&&e.clientX<canvRect.right;if(!inside)return;this.dragBeginEvent_=e;this.storeInitialInteractionPositionsAndFocus_(e);e.preventDefault();},onUpdateSelection_:function(e){if(!this.dragBeginEvent_)return;this.dragBoxXStart_=this.dragBeginEvent_.clientX;this.dragBoxXEnd_=e.clientX;this.dragBoxYStart_=this.dragBeginEvent_.clientY;this.dragBoxYEnd_=e.clientY;this.setDragBoxPosition_(this.dragBoxXStart_,this.dragBoxYStart_,this.dragBoxXEnd_,this.dragBoxYEnd_);},onEndSelection_:function(e){e.preventDefault();if(!this.dragBeginEvent_)return;this.hideDragBox_();var eDown=this.dragBeginEvent_;this.dragBeginEvent_=undefined;var loY=Math.min(eDown.clientY,e.clientY);var hiY=Math.max(eDown.clientY,e.clientY);var loX=Math.min(eDown.clientX,e.clientX);var hiX=Math.max(eDown.clientX,e.clientX);var canv=this.modelTrackContainer_.canvas;var worldOffset=canv.getBoundingClientRect().left;var loVX=loX-worldOffset;var hiVX=hiX-worldOffset;var selection=new tr.model.EventSet();if(eDown.appendSelection){var previousSelection=this.brushingStateController_.selection;if(previousSelection!==undefined){selection.addEventSet(previousSelection);}}
+this.modelTrack_.addIntersectingEventsInRangeToSelection(loVX,hiVX,loY,hiY,selection);this.brushingStateController_.changeSelectionFromTimeline(selection);},onBeginZoom_:function(e){this.isZooming_=true;this.storeInitialInteractionPositionsAndFocus_(e);e.preventDefault();},onUpdateZoom_:function(e){if(!this.isZooming_)return;var newPosition=this.extractRelativeMousePosition_(e);var zoomScaleValue=1+(this.lastMouseViewPos_.y-
+newPosition.y)*0.01;this.zoomBy_(zoomScaleValue,false);this.storeLastMousePos_(e);},onEndZoom_:function(e){this.isZooming_=false;if(!e.isClick){e.preventDefault();}},computeTouchCenter_:function(positions){var xSum=0;var ySum=0;for(var i=0;i<positions.length;++i){xSum+=positions[i].x;ySum+=positions[i].y;}
 return{x:xSum/positions.length,y:ySum/positions.length};},computeTouchSpan_:function(positions){var xMin=Number.MAX_VALUE;var yMin=Number.MAX_VALUE;var xMax=Number.MIN_VALUE;var yMax=Number.MIN_VALUE;for(var i=0;i<positions.length;++i){xMin=Math.min(xMin,positions[i].x);yMin=Math.min(yMin,positions[i].y);xMax=Math.max(xMax,positions[i].x);yMax=Math.max(yMax,positions[i].y);}
 return Math.sqrt((xMin-xMax)*(xMin-xMax)+
 (yMin-yMax)*(yMin-yMax));},onUpdateTransformForTouch_:function(e){var newPositions=this.extractRelativeTouchPositions_(e);var currentPositions=this.lastTouchViewPositions_;var newCenter=this.computeTouchCenter_(newPositions);var currentCenter=this.computeTouchCenter_(currentPositions);var newSpan=this.computeTouchSpan_(newPositions);var currentSpan=this.computeTouchSpan_(currentPositions);var vp=this.viewport_;var viewWidth=this.viewWidth_;var pixelRatio=window.devicePixelRatio||1;var xDelta=pixelRatio*(newCenter.x-currentCenter.x);var yDelta=newCenter.y-currentCenter.y;var zoomScaleValue=currentSpan>10?newSpan/currentSpan:1;var viewFocus=pixelRatio*newCenter.x;var worldFocus=vp.currentDisplayTransform.xViewToWorld(viewFocus);this.displayTransform_.set(vp.currentDisplayTransform);this.displayTransform_.scaleX*=zoomScaleValue;this.displayTransform_.xPanWorldPosToViewPos(worldFocus,viewFocus,viewWidth);this.displayTransform_.incrementPanXInViewUnits(xDelta);this.displayTransform_.panY-=yDelta;vp.setDisplayTransformImmediately(this.displayTransform_);this.storeLastTouchPositions_(e);},initHintText_:function(){this.$.hint_text.style.display='none';this.pendingHintTextClearTimeout_=undefined;},showHintText_:function(text){if(this.pendingHintTextClearTimeout_){window.clearTimeout(this.pendingHintTextClearTimeout_);this.pendingHintTextClearTimeout_=undefined;}
 this.pendingHintTextClearTimeout_=setTimeout(this.hideHintText_.bind(this),1000);Polymer.dom(this.$.hint_text).textContent=text;this.$.hint_text.style.display='';},hideHintText_:function(){this.pendingHintTextClearTimeout_=undefined;this.$.hint_text.style.display='none';}});'use strict';Polymer({is:'tr-ui-find-control',filterKeyDown:function(e){if(e.keyCode===27){var hkc=tr.b.getHotkeyControllerForElement(this);if(hkc){hkc.childRequestsBlur(this);}else{this.blur();}
-e.preventDefault();e.stopPropagation();return;}else if(e.keyCode===13){if(e.shiftKey)
-this.findPrevious();else
-this.findNext();}},filterBlur:function(e){this.updateHitCountEl();},filterFocus:function(e){this.$.filter.select();},filterMouseUp:function(e){e.preventDefault();},get controller(){return this.controller_;},set controller(c){this.controller_=c;this.updateHitCountEl();},focus:function(){this.$.filter.focus();},get hasFocus(){return this===document.activeElement;},filterTextChanged:function(){Polymer.dom(this.$.hitCount).textContent='';this.$.spinner.style.visibility='visible';this.controller.startFiltering(this.$.filter.value).then(function(){this.$.spinner.style.visibility='hidden';this.updateHitCountEl();}.bind(this));},findNext:function(){if(this.controller)
-this.controller.findNext();this.updateHitCountEl();},findPrevious:function(){if(this.controller)
-this.controller.findPrevious();this.updateHitCountEl();},updateHitCountEl:function(){if(!this.controller||this.$.filter.value.length===0){Polymer.dom(this.$.hitCount).textContent='';return;}
+e.preventDefault();e.stopPropagation();return;}else if(e.keyCode===13){if(e.shiftKey){this.findPrevious();}else{this.findNext();}}},filterBlur:function(e){this.updateHitCountEl();},filterFocus:function(e){this.$.filter.select();},filterMouseUp:function(e){e.preventDefault();},get controller(){return this.controller_;},set controller(c){this.controller_=c;this.updateHitCountEl();},focus:function(){this.$.filter.focus();},get hasFocus(){return this===document.activeElement;},filterTextChanged:function(){Polymer.dom(this.$.hitCount).textContent='';this.$.spinner.style.visibility='visible';this.$.spinner.style.animation='spin 1s linear infinite';this.controller.startFiltering(this.$.filter.value).then(function(){this.$.spinner.style.visibility='hidden';this.$.spinner.style.animation='';this.updateHitCountEl();}.bind(this));},findNext:function(){if(this.controller){this.controller.findNext();}
+this.updateHitCountEl();},findPrevious:function(){if(this.controller){this.controller.findPrevious();}
+this.updateHitCountEl();},updateHitCountEl:function(){if(!this.controller||this.$.filter.value.length===0){Polymer.dom(this.$.hitCount).textContent='';return;}
 var n=this.controller.filterHits.length;var i=n===0?-1:this.controller.currentHitIndex;Polymer.dom(this.$.hitCount).textContent=(i+1)+' of '+n;},setText:function(string){this.$.filter.value=string;}});'use strict';tr.exportTo('tr.e.tquery',function(){function Context(){this.event=undefined;this.ancestors=[];}
-Context.prototype={push:function(event){var ctx=new Context();ctx.ancestors=this.ancestors.slice();ctx.ancestors.push(event);return ctx;},pop:function(event){var ctx=new Context();ctx.event=this.ancestors[this.ancestors.length-1];ctx.ancestors=this.ancestors.slice(0,this.ancestors.length-1);return ctx;}};return{Context:Context};});'use strict';tr.exportTo('tr.e.tquery',function(){function Filter(){tr.c.ScriptingObject.call(this);}
-Filter.normalizeFilterExpression=function(filterExpression){if(filterExpression instanceof String||typeof(filterExpression)=='string'||filterExpression instanceof RegExp){var filter=new tr.e.tquery.FilterHasTitle(filterExpression);return filter;}
-return filterExpression;};Filter.prototype={__proto__:tr.c.ScriptingObject.prototype,evaluate:function(context){throw new Error('Not implemented');},matchValue_:function(value,expected){if(expected instanceof RegExp)
-return expected.test(value);else if(expected instanceof Function)
-return expected(value);return value===expected;}};return{Filter:Filter};});'use strict';tr.exportTo('tr.e.tquery',function(){function FilterAllOf(opt_subExpressions){tr.e.tquery.Filter.call(this);this.subExpressions=opt_subExpressions||[];}
-FilterAllOf.prototype={__proto__:tr.e.tquery.Filter.prototype,set subExpressions(exprs){this.subExpressions_=[];for(var i=0;i<exprs.length;i++){this.subExpressions_.push(tr.e.tquery.Filter.normalizeFilterExpression(exprs[i]));}},get subExpressions(){return this.subExpressions_;},evaluate:function(context){if(!this.subExpressions.length)
-return true;for(var i=0;i<this.subExpressions.length;i++){if(!this.subExpressions[i].evaluate(context))
-return false;}
+Context.prototype={push:function(event){var ctx=new Context();ctx.ancestors=this.ancestors.slice();ctx.ancestors.push(event);return ctx;},pop:function(event){var ctx=new Context();ctx.event=this.ancestors[this.ancestors.length-1];ctx.ancestors=this.ancestors.slice(0,this.ancestors.length-1);return ctx;}};return{Context,};});'use strict';tr.exportTo('tr.e.tquery',function(){function Filter(){tr.c.ScriptingObject.call(this);}
+Filter.normalizeFilterExpression=function(filterExpression){if(filterExpression instanceof String||typeof(filterExpression)==='string'||filterExpression instanceof RegExp){var filter=new tr.e.tquery.FilterHasTitle(filterExpression);return filter;}
+return filterExpression;};Filter.prototype={__proto__:tr.c.ScriptingObject.prototype,evaluate:function(context){throw new Error('Not implemented');},matchValue_:function(value,expected){if(expected instanceof RegExp){return expected.test(value);}else if(expected instanceof Function){return expected(value);}
+return value===expected;}};return{Filter,};});'use strict';tr.exportTo('tr.e.tquery',function(){function FilterAllOf(opt_subExpressions){tr.e.tquery.Filter.call(this);this.subExpressions=opt_subExpressions||[];}
+FilterAllOf.prototype={__proto__:tr.e.tquery.Filter.prototype,set subExpressions(exprs){this.subExpressions_=[];for(var i=0;i<exprs.length;i++){this.subExpressions_.push(tr.e.tquery.Filter.normalizeFilterExpression(exprs[i]));}},get subExpressions(){return this.subExpressions_;},evaluate:function(context){if(!this.subExpressions.length)return true;for(var i=0;i<this.subExpressions.length;i++){if(!this.subExpressions[i].evaluate(context)){return false;}}
 return true;}};tr.c.ScriptingObjectRegistry.register(function(){var exprs=[];for(var i=0;i<arguments.length;i++){exprs.push(arguments[i]);}
-return new FilterAllOf(exprs);},{name:'allOf'});return{FilterAllOf:FilterAllOf};});'use strict';tr.exportTo('tr.e.tquery',function(){function FilterNot(subExpression){tr.e.tquery.Filter.call(this);this.subExpression=subExpression;}
-FilterNot.prototype={__proto__:tr.e.tquery.Filter.prototype,set subExpression(expr){this.subExpression_=tr.e.tquery.Filter.normalizeFilterExpression(expr);},get subExpression(){return this.subExpression_;},evaluate:function(context){return!this.subExpression.evaluate(context);}};tr.c.ScriptingObjectRegistry.register(function(){var exprs=Array.prototype.slice.call(arguments);if(exprs.length!==1)
-throw new Error('not() must have exactly one subexpression');return new FilterNot(exprs[0]);},{name:'not'});return{FilterNot:FilterNot};});'use strict';tr.exportTo('tr.e.tquery',function(){function FilterAnyOf(opt_subExpressions){tr.e.tquery.Filter.call(this);this.subExpressions=opt_subExpressions||[];};FilterAnyOf.prototype={__proto__:tr.e.tquery.Filter.prototype,set subExpressions(exprs){this.subExpressions_=[];for(var i=0;i<exprs.length;i++){this.subExpressions_.push(tr.e.tquery.Filter.normalizeFilterExpression(exprs[i]));}},get subExpressions(){return this.subExpressions_;},evaluate:function(context){if(!this.subExpressions.length)
-return true;for(var i=0;i<this.subExpressions.length;i++){if(this.subExpressions[i].evaluate(context))
-return true;}
-return false;}};tr.c.ScriptingObjectRegistry.register(function(){var exprs=Array.prototype.slice.call(arguments);return new FilterAnyOf(exprs);},{name:'anyOf'});tr.c.ScriptingObjectRegistry.register(function(){var exprs=Array.prototype.slice.call(arguments);return new tr.e.tquery.FilterNot(new FilterAnyOf(exprs));},{name:'noneOf'});return{FilterAnyOf:FilterAnyOf};});'use strict';tr.exportTo('tr.e.tquery',function(){function FilterHasAncestor(opt_subExpression){this.subExpression=opt_subExpression;};FilterHasAncestor.prototype={__proto__:tr.e.tquery.Filter.prototype,set subExpression(expr){this.subExpression_=tr.e.tquery.Filter.normalizeFilterExpression(expr);},get subExpression(){return this.subExpression_;},evaluate:function(context){if(!this.subExpression)
-return context.ancestors.length>0;while(context.ancestors.length){context=context.pop();if(this.subExpression.evaluate(context))
-return true;}
-return false;}};tr.c.ScriptingObjectRegistry.register(function(subExpression){return new FilterHasAncestor(subExpression);},{name:'hasAncestor'});return{FilterHasAncestor:FilterHasAncestor};});'use strict';tr.exportTo('tr.e.tquery',function(){function FilterHasDuration(minValueOrExpected,opt_maxValue){if(minValueOrExpected!==undefined&&opt_maxValue!==undefined){this.minValue=minValueOrExpected;this.maxValue=opt_maxValue;}else{this.expected=minValueOrExpected;}};FilterHasDuration.prototype={__proto__:tr.e.tquery.Filter.prototype,evaluate:function(context){if(context.event.duration===undefined)
-return false;if(this.minValue!==undefined&&this.maxValue!==undefined){return context.event.duration>=this.minValue&&context.event.duration<=this.maxValue;}
-return this.matchValue_(context.event.duration,this.expected);}};tr.c.ScriptingObjectRegistry.register(function(minValueOrExpected,opt_maxValue){return new FilterHasDuration(minValueOrExpected,opt_maxValue);},{name:'hasDuration'});return{FilterHasDuration:FilterHasDuration};});'use strict';tr.exportTo('tr.e.tquery',function(){function FilterHasTitle(expected){tr.e.tquery.Filter.call(this);this.expected=expected;}
-FilterHasTitle.prototype={__proto__:tr.e.tquery.Filter.prototype,evaluate:function(context){return this.matchValue_(context.event.title,this.expected);}};tr.c.ScriptingObjectRegistry.register(function(expected){var filter=new tr.e.tquery.FilterHasTitle(expected);return filter;},{name:'hasTitle'});return{FilterHasTitle:FilterHasTitle};});'use strict';tr.exportTo('tr.e.tquery',function(){function FilterIsTopLevel(opt_subExpression){this.subExpression=opt_subExpression;}
-FilterIsTopLevel.prototype={__proto__:tr.e.tquery.Filter.prototype,set subExpression(expr){this.subExpression_=tr.e.tquery.Filter.normalizeFilterExpression(expr);},get subExpression(){return this.subExpression_;},evaluate:function(context){if(context.ancestors.length>0)
-return false;if(!this.subExpression)
-return true;return this.subExpression.evaluate(context);}};tr.c.ScriptingObjectRegistry.register(function(subExpression){return new FilterIsTopLevel(subExpression);},{name:'isTopLevel'});return{FilterIsTopLevel:FilterIsTopLevel};});'use strict';tr.exportTo('tr.e.tquery',function(){function addEventTreeToSelection(selection,event){selection.push(event);if(!event.subSlices)
-return;event.subSlices.forEach(addEventTreeToSelection.bind(undefined,selection));}
-function TQuery(model){tr.c.ScriptingObject.call(this);this.model_=model;this.parent_=undefined;this.filterExpression_=undefined;this.selection_=undefined;};TQuery.prototype={__proto__:tr.c.ScriptingObject.prototype,onModelChanged:function(model){this.model_=model;this.selection_=undefined;},get brushingStateController(){return this.brushingStateController_;},filter:function(filterExpression){var result=new TQuery(this.model_);result.parent_=this;result.filterExpression_=tr.e.tquery.Filter.normalizeFilterExpression(filterExpression);return result;},createFilterTaskGraph_:function(){var nodes=[];var node=this;while(node!==undefined){nodes.push(node);node=node.parent_;}
-var rootTask=new tr.b.Task();var lastTask=rootTask;for(var i=nodes.length-1;i>=0;i--){var node=nodes[i];if(node.selection_!==undefined)
-continue;node.selection_=new tr.model.EventSet();if(node.parent_===undefined){lastTask=lastTask.after(this.selectEverythingAsTask_(node.selection_));}else{var prevNode=nodes[i+1];lastTask=this.createFilterTaskForNode_(lastTask,node,prevNode);}}
-return{rootTask:rootTask,lastTask:lastTask,lastNode:node};},createFilterTaskForNode_:function(lastTask,node,prevNode){return lastTask.after(function(){node.evaluateFilterExpression_(prevNode.selection_,node.selection_);},this);},evaluateFilterExpression_:function(inputSelection,outputSelection){var seenEvents={};inputSelection.forEach(function(event){var context=new tr.e.tquery.Context();context.event=event;this.evaluateFilterExpressionForEvent_(context,inputSelection,outputSelection,seenEvents);}.bind(this));},evaluateFilterExpressionForEvent_:function(context,inputSelection,outputSelection,seenEvents){var event=context.event;if(inputSelection.contains(event)&&!seenEvents[event.guid]){seenEvents[event.guid]=true;if(!this.filterExpression_||this.filterExpression_.evaluate(context))
-outputSelection.push(event);}
-if(!event.subSlices)
-return;context=context.push(event);for(var i=0;i<event.subSlices.length;i++){context.event=event.subSlices[i];this.evaluateFilterExpressionForEvent_(context,inputSelection,outputSelection,seenEvents);}},selectEverythingAsTask_:function(selection){var filterTask=new tr.b.Task();for(var container of this.model_.getDescendantEventContainers()){filterTask.subTask(()=>{for(var event of container.childEvents())
-addEventTreeToSelection(selection,event);},this);}
+return new FilterAllOf(exprs);},{name:'allOf'});return{FilterAllOf,};});'use strict';tr.exportTo('tr.e.tquery',function(){function FilterNot(subExpression){tr.e.tquery.Filter.call(this);this.subExpression=subExpression;}
+FilterNot.prototype={__proto__:tr.e.tquery.Filter.prototype,set subExpression(expr){this.subExpression_=tr.e.tquery.Filter.normalizeFilterExpression(expr);},get subExpression(){return this.subExpression_;},evaluate:function(context){return!this.subExpression.evaluate(context);}};tr.c.ScriptingObjectRegistry.register(function(){var exprs=Array.prototype.slice.call(arguments);if(exprs.length!==1){throw new Error('not() must have exactly one subexpression');}
+return new FilterNot(exprs[0]);},{name:'not'});return{FilterNot,};});'use strict';tr.exportTo('tr.e.tquery',function(){function FilterAnyOf(opt_subExpressions){tr.e.tquery.Filter.call(this);this.subExpressions=opt_subExpressions||[];}
+FilterAnyOf.prototype={__proto__:tr.e.tquery.Filter.prototype,set subExpressions(exprs){this.subExpressions_=[];for(var i=0;i<exprs.length;i++){this.subExpressions_.push(tr.e.tquery.Filter.normalizeFilterExpression(exprs[i]));}},get subExpressions(){return this.subExpressions_;},evaluate:function(context){if(!this.subExpressions.length)return true;for(var i=0;i<this.subExpressions.length;i++){if(this.subExpressions[i].evaluate(context))return true;}
+return false;}};tr.c.ScriptingObjectRegistry.register(function(){var exprs=Array.prototype.slice.call(arguments);return new FilterAnyOf(exprs);},{name:'anyOf'});tr.c.ScriptingObjectRegistry.register(function(){var exprs=Array.prototype.slice.call(arguments);return new tr.e.tquery.FilterNot(new FilterAnyOf(exprs));},{name:'noneOf'});return{FilterAnyOf,};});'use strict';tr.exportTo('tr.e.tquery',function(){function FilterHasAncestor(opt_subExpression){this.subExpression=opt_subExpression;}
+FilterHasAncestor.prototype={__proto__:tr.e.tquery.Filter.prototype,set subExpression(expr){this.subExpression_=tr.e.tquery.Filter.normalizeFilterExpression(expr);},get subExpression(){return this.subExpression_;},evaluate:function(context){if(!this.subExpression){return context.ancestors.length>0;}
+while(context.ancestors.length){context=context.pop();if(this.subExpression.evaluate(context))return true;}
+return false;}};tr.c.ScriptingObjectRegistry.register(function(subExpression){return new FilterHasAncestor(subExpression);},{name:'hasAncestor'});return{FilterHasAncestor,};});'use strict';tr.exportTo('tr.e.tquery',function(){function FilterHasDuration(minValueOrExpected,opt_maxValue){if(minValueOrExpected!==undefined&&opt_maxValue!==undefined){this.minValue=minValueOrExpected;this.maxValue=opt_maxValue;}else{this.expected=minValueOrExpected;}}
+FilterHasDuration.prototype={__proto__:tr.e.tquery.Filter.prototype,evaluate:function(context){if(context.event.duration===undefined)return false;if(this.minValue!==undefined&&this.maxValue!==undefined){return context.event.duration>=this.minValue&&context.event.duration<=this.maxValue;}
+return this.matchValue_(context.event.duration,this.expected);}};tr.c.ScriptingObjectRegistry.register(function(minValueOrExpected,opt_maxValue){return new FilterHasDuration(minValueOrExpected,opt_maxValue);},{name:'hasDuration'});return{FilterHasDuration,};});'use strict';tr.exportTo('tr.e.tquery',function(){function FilterHasTitle(expected){tr.e.tquery.Filter.call(this);this.expected=expected;}
+FilterHasTitle.prototype={__proto__:tr.e.tquery.Filter.prototype,evaluate:function(context){return this.matchValue_(context.event.title,this.expected);}};tr.c.ScriptingObjectRegistry.register(function(expected){var filter=new tr.e.tquery.FilterHasTitle(expected);return filter;},{name:'hasTitle'});return{FilterHasTitle,};});'use strict';tr.exportTo('tr.e.tquery',function(){function FilterIsTopLevel(opt_subExpression){this.subExpression=opt_subExpression;}
+FilterIsTopLevel.prototype={__proto__:tr.e.tquery.Filter.prototype,set subExpression(expr){this.subExpression_=tr.e.tquery.Filter.normalizeFilterExpression(expr);},get subExpression(){return this.subExpression_;},evaluate:function(context){if(context.ancestors.length>0)return false;if(!this.subExpression)return true;return this.subExpression.evaluate(context);}};tr.c.ScriptingObjectRegistry.register(function(subExpression){return new FilterIsTopLevel(subExpression);},{name:'isTopLevel'});return{FilterIsTopLevel,};});'use strict';tr.exportTo('tr.e.tquery',function(){function addEventTreeToSelection(selection,event){selection.push(event);if(!event.subSlices)return;event.subSlices.forEach(addEventTreeToSelection.bind(undefined,selection));}
+function TQuery(model){tr.c.ScriptingObject.call(this);this.model_=model;this.parent_=undefined;this.filterExpression_=undefined;this.selection_=undefined;}
+TQuery.prototype={__proto__:tr.c.ScriptingObject.prototype,onModelChanged:function(model){this.model_=model;this.selection_=undefined;},get brushingStateController(){return this.brushingStateController_;},filter:function(filterExpression){var result=new TQuery(this.model_);result.parent_=this;result.filterExpression_=tr.e.tquery.Filter.normalizeFilterExpression(filterExpression);return result;},createFilterTaskGraph_:function(){let nodes=[this];while(nodes[nodes.length-1].parent_){nodes.push(nodes[nodes.length-1].parent_);}
+var rootTask=new tr.b.Task();var lastTask=rootTask;for(var i=nodes.length-1;i>=0;i--){var node=nodes[i];if(node.selection_!==undefined)continue;node.selection_=new tr.model.EventSet();if(node.parent_===undefined){lastTask=lastTask.after(this.selectEverythingAsTask_(node.selection_));}else{var prevNode=nodes[i+1];lastTask=this.createFilterTaskForNode_(lastTask,node,prevNode);}}
+return{rootTask:rootTask,lastTask:lastTask,lastNode:node};},createFilterTaskForNode_:function(lastTask,node,prevNode){return lastTask.after(function(){node.evaluateFilterExpression_(prevNode.selection_,node.selection_);},this);},evaluateFilterExpression_:function(inputSelection,outputSelection){var seenEvents={};inputSelection.forEach(function(event){var context=new tr.e.tquery.Context();context.event=event;this.evaluateFilterExpressionForEvent_(context,inputSelection,outputSelection,seenEvents);}.bind(this));},evaluateFilterExpressionForEvent_:function(context,inputSelection,outputSelection,seenEvents){var event=context.event;if(inputSelection.contains(event)&&!seenEvents[event.guid]){seenEvents[event.guid]=true;if(!this.filterExpression_||this.filterExpression_.evaluate(context)){outputSelection.push(event);}}
+if(!event.subSlices)return;context=context.push(event);for(var i=0;i<event.subSlices.length;i++){context.event=event.subSlices[i];this.evaluateFilterExpressionForEvent_(context,inputSelection,outputSelection,seenEvents);}},selectEverythingAsTask_:function(selection){var filterTask=new tr.b.Task();for(let container of this.model_.getDescendantEventContainers()){filterTask.subTask(()=>{for(var event of container.childEvents()){addEventTreeToSelection(selection,event);}},this);}
 return filterTask;},ready:function(){return new Promise(function(resolve,reject){var graph=this.createFilterTaskGraph_();graph.lastTask=graph.lastTask.after(function(){resolve(this.selection_);},this);tr.b.Task.RunWhenIdle(graph.rootTask);}.bind(this));},get selection(){if(this.selection_===undefined){var graph=this.createFilterTaskGraph_();tr.b.Task.RunSynchronously(graph.rootTask);}
-return this.selection_;}};tr.c.ScriptingObjectRegistry.register(new TQuery(),{name:'$t'});return{TQuery:TQuery};});'use strict';Polymer({is:'tr-ui-scripting-control',_isEnterKey:function(event){return event.keyCode!==229&&(event.key==='Enter'||event.keyIdentifier==='Enter');},_setFocused:function(focused){var promptEl=this.$.prompt;if(focused){promptEl.focus();Polymer.dom(this.$.root).classList.add('focused');if(promptEl.innerText.length>0){var sel=window.getSelection();sel.collapse(Polymer.dom(promptEl).firstChild,promptEl.innerText.length);}}else{promptEl.blur();Polymer.dom(this.$.root).classList.remove('focused');var parent=promptEl.parentElement;var nextEl=Polymer.dom(promptEl).nextSibling;promptEl.remove();Polymer.dom(parent).insertBefore(promptEl,nextEl);}},onConsoleFocus:function(e){e.stopPropagation();this._setFocused(true);},onConsoleBlur:function(e){e.stopPropagation();this._setFocused(false);},promptKeyDown:function(e){e.stopPropagation();if(!this._isEnterKey(e))
-return;e.preventDefault();var promptEl=this.$.prompt;var command=promptEl.innerText;if(command.length===0)
-return;promptEl.innerText='';this.addLine_(String.fromCharCode(187)+' '+command);try{var result=this.controller_.executeCommand(command);}catch(e){result=e.stack||e.stackTrace;}
+return this.selection_;}};tr.c.ScriptingObjectRegistry.register(new TQuery(),{name:'$t'});return{TQuery,};});'use strict';Polymer({is:'tr-ui-scripting-control',_isEnterKey:function(event){return event.keyCode!==229&&(event.key==='Enter'||event.keyIdentifier==='Enter');},_setFocused:function(focused){var promptEl=this.$.prompt;if(focused){promptEl.focus();Polymer.dom(this.$.root).classList.add('focused');if(promptEl.value.length>0){var sel=window.getSelection();sel.collapse(Polymer.dom(promptEl).firstChild,promptEl.value.length);}}else{promptEl.blur();Polymer.dom(this.$.root).classList.remove('focused');var parent=promptEl.parentElement;var nextEl=Polymer.dom(promptEl).nextSibling;promptEl.remove();Polymer.dom(parent).insertBefore(promptEl,nextEl);}},onConsoleFocus:function(e){e.stopPropagation();this._setFocused(true);},onConsoleBlur:function(e){e.stopPropagation();this._setFocused(false);},promptKeyDown:function(e){e.stopPropagation();if(!this._isEnterKey(e))return;e.preventDefault();var promptEl=this.$.prompt;var command=promptEl.value;if(command.length===0)return;promptEl.value='';this.addLine_(String.fromCharCode(187)+' '+command);try{var result=this.controller_.executeCommand(command);}catch(e){result=e.stack||e.stackTrace;}
 if(result instanceof tr.e.tquery.TQuery){result.ready().then(function(selection){this.addLine_(selection.length+' matches');this.controller_.brushingStateController.showScriptControlSelection(selection);}.bind(this));}else{this.addLine_(result);}
-promptEl.scrollIntoView();},addLine_:function(line){var historyEl=this.$.history;if(historyEl.innerText.length!==0)
-historyEl.innerText+='\n';historyEl.innerText+=line;},promptKeyPress:function(e){e.stopPropagation();},toggleVisibility:function(){var root=this.$.root;if(!this.visible){Polymer.dom(root).classList.remove('hidden');this._setFocused(true);}else{Polymer.dom(root).classList.add('hidden');this._setFocused(false);}},get hasFocus(){return this===document.activeElement;},get visible(){var root=this.$.root;return!Polymer.dom(root).classList.contains('hidden');},get controller(){return this.controller_;},set controller(c){this.controller_=c;}});'use strict';Polymer({is:'tr-ui-side-panel-container',ready:function(){this.activePanelContainer_=this.$.active_panel_container;this.tabStrip_=this.$.tab_strip;this.dragHandle_=this.$.side_panel_drag_handle;this.dragHandle_.horizontal=false;this.dragHandle_.target=this.activePanelContainer_;this.rangeOfInterest_=new tr.b.Range();this.brushingStateController_=undefined;this.onSelectionChanged_=this.onSelectionChanged_.bind(this);this.onModelChanged_=this.onModelChanged_.bind(this);},get brushingStateController(){return this.brushingStateController_;},set brushingStateController(brushingStateController){if(this.brushingStateController){this.brushingStateController_.removeEventListener('change',this.onSelectionChanged_);this.brushingStateController_.removeEventListener('model-changed',this.onModelChanged_);}
-this.brushingStateController_=brushingStateController;if(this.brushingStateController){this.brushingStateController_.addEventListener('change',this.onSelectionChanged_);this.brushingStateController_.addEventListener('model-changed',this.onModelChanged_);}},onSelectionChanged_:function(){if(this.activePanel)
-this.activePanel.selection=this.selection;},get model(){return this.brushingStateController_.model;},onModelChanged_:function(){this.activePanelType_=undefined;this.updateContents_();},get expanded(){this.hasAttribute('expanded');},get activePanel(){return this.activePanelContainer_.children[0];},get activePanelType(){return this.activePanelType_;},set activePanelType(panelType){if(this.model===undefined)
-throw new Error('Cannot activate panel without a model');var panel=undefined;if(panelType)
-panel=document.createElement(panelType);if(panel!==undefined&&!panel.supportsModel(this.model))
-throw new Error('Cannot activate panel: does not support this model');if(this.activePanelType){Polymer.dom(this.getLabelElementForPanelType_(this.activePanelType)).removeAttribute('selected');}
+promptEl.scrollIntoView();},addLine_:function(line){var historyEl=this.$.history;if(historyEl.innerText.length!==0){historyEl.innerText+='\n';}
+historyEl.innerText+=line;},promptKeyPress:function(e){e.stopPropagation();},toggleVisibility:function(){var root=this.$.root;if(!this.visible){Polymer.dom(root).classList.remove('hidden');this._setFocused(true);}else{Polymer.dom(root).classList.add('hidden');this._setFocused(false);}},get hasFocus(){return this===document.activeElement;},get visible(){var root=this.$.root;return!Polymer.dom(root).classList.contains('hidden');},get controller(){return this.controller_;},set controller(c){this.controller_=c;}});'use strict';Polymer({is:'tr-ui-side-panel-container',ready:function(){this.activePanelContainer_=this.$.active_panel_container;this.tabStrip_=this.$.tab_strip;this.dragHandle_=this.$.side_panel_drag_handle;this.dragHandle_.horizontal=false;this.dragHandle_.target=this.activePanelContainer_;this.rangeOfInterest_=new tr.b.math.Range();this.brushingStateController_=undefined;this.onSelectionChanged_=this.onSelectionChanged_.bind(this);this.onModelChanged_=this.onModelChanged_.bind(this);},get brushingStateController(){return this.brushingStateController_;},set brushingStateController(brushingStateController){if(this.brushingStateController){this.brushingStateController_.removeEventListener('change',this.onSelectionChanged_);this.brushingStateController_.removeEventListener('model-changed',this.onModelChanged_);}
+this.brushingStateController_=brushingStateController;if(this.brushingStateController){this.brushingStateController_.addEventListener('change',this.onSelectionChanged_);this.brushingStateController_.addEventListener('model-changed',this.onModelChanged_);}},onSelectionChanged_:function(){if(this.activePanel){this.activePanel.selection=this.selection;}},get model(){return this.brushingStateController_.model;},onModelChanged_:function(){this.activePanelType_=undefined;this.updateContents_();},get expanded(){this.hasAttribute('expanded');},get activePanel(){return this.activePanelContainer_.children[0];},get activePanelType(){return this.activePanelType_;},set activePanelType(panelType){if(this.model===undefined){throw new Error('Cannot activate panel without a model');}
+var panel=undefined;if(panelType){panel=document.createElement(panelType);}
+if(panel!==undefined&&!panel.supportsModel(this.model)){throw new Error('Cannot activate panel: does not support this model');}
+if(this.activePanelType){Polymer.dom(this.getLabelElementForPanelType_(this.activePanelType)).removeAttribute('selected');}
 if(this.activePanelType){this.getLabelElementForPanelType_(this.activePanelType).removeAttribute('selected');}
-if(this.activePanel)
-this.activePanelContainer_.removeChild(this.activePanel);if(panelType===undefined){Polymer.dom(this).removeAttribute('expanded');this.activePanelType_=undefined;return;}
-Polymer.dom(this.getLabelElementForPanelType_(panelType)).setAttribute('selected',true);Polymer.dom(this).setAttribute('expanded',true);Polymer.dom(this.activePanelContainer_).appendChild(panel);panel.rangeOfInterest=this.rangeOfInterest_;panel.selection=this.selection_;panel.model=this.model;this.activePanelType_=panelType;},getPanelTypeForConstructor_:function(constructor){for(var i=0;i<this.tabStrip_.children.length;i++){if(this.tabStrip_.children[i].panelType.constructor==constructor)
-return this.tabStrip_.children[i].panelType;}},getLabelElementForPanelType_:function(panelType){for(var i=0;i<this.tabStrip_.children.length;i++){if(this.tabStrip_.children[i].panelType==panelType)
-return this.tabStrip_.children[i];}
+if(this.activePanel){this.activePanelContainer_.removeChild(this.activePanel);}
+if(panelType===undefined){Polymer.dom(this).removeAttribute('expanded');this.activePanelType_=undefined;return;}
+Polymer.dom(this.getLabelElementForPanelType_(panelType)).setAttribute('selected',true);Polymer.dom(this).setAttribute('expanded',true);Polymer.dom(this.activePanelContainer_).appendChild(panel);panel.rangeOfInterest=this.rangeOfInterest_;panel.selection=this.selection_;panel.model=this.model;this.activePanelType_=panelType;},getPanelTypeForConstructor_:function(constructor){for(var i=0;i<this.tabStrip_.children.length;i++){if(this.tabStrip_.children[i].panelType.constructor===constructor){return this.tabStrip_.children[i].panelType;}}},getLabelElementForPanelType_:function(panelType){for(var i=0;i<this.tabStrip_.children.length;i++){if(this.tabStrip_.children[i].panelType===panelType){return this.tabStrip_.children[i];}}
 return undefined;},updateContents_:function(){var previouslyActivePanelType=this.activePanelType;Polymer.dom(this.tabStrip_).textContent='';var supportedPanelTypes=[];for(var panelTypeInfo of
-tr.ui.side_panel.SidePanelRegistry.getAllRegisteredTypeInfos()){var labelEl=document.createElement('tab-strip-label');var panel=panelTypeInfo.constructor();var panelType=panel.tagName;Polymer.dom(labelEl).textContent=panel.textLabel;labelEl.panelType=panelType;var supported=panel.supportsModel(this.model);if(this.model&&supported.supported){supportedPanelTypes.push(panelType);Polymer.dom(labelEl).setAttribute('enabled',true);labelEl.addEventListener('click',function(panelType){this.activePanelType=this.activePanelType===panelType?undefined:panelType;}.bind(this,panelType));}else{if(this.activePanel)
-this.activePanelContainer_.removeChild(this.activePanel);this.removeAttribute('expanded');}
+tr.ui.side_panel.SidePanelRegistry.getAllRegisteredTypeInfos()){var labelEl=document.createElement('tab-strip-label');var panel=panelTypeInfo.constructor();var panelType=panel.tagName;Polymer.dom(labelEl).textContent=panel.textLabel;labelEl.panelType=panelType;var supported=panel.supportsModel(this.model);if(this.model&&supported.supported){supportedPanelTypes.push(panelType);Polymer.dom(labelEl).setAttribute('enabled',true);labelEl.addEventListener('click',function(panelType){this.activePanelType=this.activePanelType===panelType?undefined:panelType;}.bind(this,panelType));}else{if(this.activePanel){this.activePanelContainer_.removeChild(this.activePanel);}
+this.removeAttribute('expanded');}
 Polymer.dom(this.tabStrip_).appendChild(labelEl);}
-if(previouslyActivePanelType&&supportedPanelTypes.indexOf(previouslyActivePanelType)!=-1){this.activePanelType=previouslyActivePanelType;Polymer.dom(this).setAttribute('expanded',true);}else{if(this.activePanel)
-Polymer.dom(this.activePanelContainer_).removeChild(this.activePanel);Polymer.dom(this).removeAttribute('expanded');}},get rangeOfInterest(){return this.rangeOfInterest_;},set rangeOfInterest(range){if(range==undefined)
-throw new Error('Must not be undefined');this.rangeOfInterest_=range;if(this.activePanel)
-this.activePanel.rangeOfInterest=range;}});'use strict';Polymer({is:'tr-ui-timeline-view-help-overlay',ready:function(){var mod=tr.isMac?'cmd ':'ctrl';var spans=Polymer.dom(this.root).querySelectorAll('span.mod');for(var i=0;i<spans.length;i++)
-Polymer.dom(spans[i]).textContent=mod;}});'use strict';tr.exportTo('tr.v',function(){function GenericTable(items){if(items!==undefined)
-this.items=items;else
-this.items=[];};GenericTable.prototype={};return{GenericTable:GenericTable};});'use strict';tr.exportTo('tr.v.ui',function(){var ArrayOfNumbersSummaryModes={AVERAGE_MODE:'average-mode',TOTAL_MODE:'total-mode'};return{ArrayOfNumbersSummaryModes:ArrayOfNumbersSummaryModes};});'use strict';Polymer({is:'tr-v-ui-array-of-numbers-span',created:function(){this.numbers_=undefined;this.summaryMode_=tr.v.ui.ArrayOfNumbersSummaryModes.AVERAGE_MODE;},get summaryMode(){return this.summaryMode_;},set summaryMode(summaryMode){this.summaryMode_=summaryMode;this.updateContents_();},get numbers(){return this.numbers_;},set numbers(numbers){if(numbers===undefined){this.numbers_=undefined;this.updateContents_();return;}
-if(!(numbers instanceof Array))
-throw new Error('Must provide an array');this.numbers_=numbers;this.updateContents_();},updateContents_:function(){if(this.numbers_===undefined){Polymer.dom(this.root).textContent='-';return;}
-var ArrayOfNumbersSummaryModes=tr.v.ui.ArrayOfNumbersSummaryModes;var value;if(this.summaryMode_===ArrayOfNumbersSummaryModes.AVERAGE_MODE)
-value=tr.b.Statistics.mean(this.numbers_);else
-value=tr.b.Statistics.sum(this.numbers_);var valueRounded=Math.round(value*1000.0)/1000.0;Polymer.dom(this.root).textContent=valueRounded;}});'use strict';tr.exportTo('tr.v.ui',function(){var TEXT_COLUMN_MODE=1;var NUMERIC_COLUMN_MODE=2;var ELEMENT_COLUMN_MODE=3;function isNumeric(value){if((typeof value)==='number')
-return true;else if(value instanceof Number)
-return true;return false;}
-function GenericTableViewTotalsItem(opt_values){if(opt_values!==undefined)
-this.values=opt_values;else
-this.values=[];}
-function GenericTableViewColumnDescriptor(fieldName,firstFieldValue){this.title=fieldName;this.fieldName=fieldName;this.updateModeGivenValue(firstFieldValue);}
-GenericTableViewColumnDescriptor.prototype={get columnMode(){return this.columnMode_;},get isInNumericMode(){return this.columnMode_===NUMERIC_COLUMN_MODE;},cmp:function(a,b){if(this.columnMode_===ELEMENT_COLUMN_MODE)
-return 0;return tr.b.comparePossiblyUndefinedValues(a,b,function(a,b){var vA=a[this.fieldName];var vB=b[this.fieldName];return tr.b.comparePossiblyUndefinedValues(vA,vB,function(vA,vB){if(vA.localeCompare)
-return vA.localeCompare(vB);return vA-vB;},this);},this);},updateModeGivenValue:function(fieldValue){if(this.columnMode_===undefined){if(fieldValue===undefined||fieldValue===null)
-return;if(isNumeric(fieldValue)){this.columnMode_=NUMERIC_COLUMN_MODE;return;}
-if(fieldValue instanceof HTMLElement){this.columnMode_=ELEMENT_COLUMN_MODE;return;}
-this.columnMode_=TEXT_COLUMN_MODE;return;}
-if(fieldValue===undefined||fieldValue===null)
-return;if(isNumeric(fieldValue))
-return;if(fieldValue instanceof HTMLElement){this.columnMode_=ELEMENT_COLUMN_MODE;return;}
-if(this.columnMode_===NUMERIC_COLUMN_MODE)
-this.columnMode_=TEXT_COLUMN_MODE;},value:function(item){var fieldValue=item[this.fieldName];if(fieldValue instanceof GenericTableViewTotalsItem){var span=document.createElement('tr-v-ui-array-of-numbers-span');span.summaryMode=tr.v.ui.ArrayOfNumbersSummaryModes.TOTAL_MODE;span.numbers=fieldValue.values;return span;}
-if(fieldValue===undefined)
-return'-';if(fieldValue instanceof HTMLElement)
-return fieldValue;if(fieldValue instanceof Object){var gov=document.createElement('tr-ui-a-generic-object-view');gov.object=fieldValue;return gov;}
-return fieldValue;}};Polymer({is:'tr-v-ui-generic-table-view',created:function(){this.items_=undefined;this.importantColumNames_=[];},get items(){return this.items_;},set items(itemsOrGenericTable){if(itemsOrGenericTable===undefined){this.items_=undefined;}else if(itemsOrGenericTable instanceof Array){this.items_=itemsOrGenericTable;}else if(itemsOrGenericTable instanceof tr.v.GenericTable){this.items_=itemsOrGenericTable.items;}
-this.updateContents_();},get importantColumNames(){return this.importantColumNames_;},set importantColumNames(importantColumNames){this.importantColumNames_=importantColumNames;this.updateContents_();},createColumns_:function(){var columnsByName={};this.items_.forEach(function(item){tr.b.iterItems(item,function(itemFieldName,itemFieldValue){var colDesc=columnsByName[itemFieldName];if(colDesc!==undefined){colDesc.updateModeGivenValue(itemFieldValue);return;}
-colDesc=new GenericTableViewColumnDescriptor(itemFieldName,itemFieldValue);columnsByName[itemFieldName]=colDesc;},this);},this);var columns=tr.b.dictionaryValues(columnsByName);if(columns.length===0)
-return undefined;var isColumnNameImportant={};var importantColumNames=this.importantColumNames||[];importantColumNames.forEach(function(icn){isColumnNameImportant[icn]=true;});columns.sort(function(a,b){var iA=isColumnNameImportant[a.title]?1:0;var iB=isColumnNameImportant[b.title]?1:0;if((iB-iA)!==0)
-return iB-iA;return a.title.localeCompare(b.title);});var colWidthPercentage;if(columns.length==1)
-colWidthPercentage='100%';else
-colWidthPercentage=(100/(columns.length-1)).toFixed(3)+'%';columns[0].width='250px';for(var i=1;i<columns.length;i++)
-columns[i].width=colWidthPercentage;return columns;},createFooterRowsIfNeeded_:function(columns){var hasColumnThatIsNumeric=columns.some(function(column){return column.isInNumericMode;});if(!hasColumnThatIsNumeric)
-return[];var totalsItems={};columns.forEach(function(column){if(!column.isInNumericMode)
-return;var totalsItem=new GenericTableViewTotalsItem();this.items_.forEach(function(item){var fieldValue=item[column.fieldName];if(fieldValue===undefined||fieldValue===null)
-return;totalsItem.values.push(fieldValue);});totalsItems[column.fieldName]=totalsItem;},this);return[totalsItems];},updateContents_:function(){var columns;if(this.items_!==undefined)
-columns=this.createColumns_();if(!columns){this.$.table.tableColumns=[];this.$.table.tableRows=[];this.$.table.footerRows=[];return;}
-this.$.table.tableColumns=columns;this.$.table.tableRows=this.items_;this.$.table.footerRows=this.createFooterRowsIfNeeded_(columns);this.$.table.rebuild();},get selectionMode(){return this.$.table.selectionMode;},set selectionMode(selectionMode){this.$.table.selectionMode=selectionMode;},get rowHighlightStyle(){return this.$.table.rowHighlightStyle;},set rowHighlightStyle(rowHighlightStyle){this.$.table.rowHighlightStyle=rowHighlightStyle;},get cellHighlightStyle(){return this.$.table.cellHighlightStyle;},set cellHighlightStyle(cellHighlightStyle){this.$.table.cellHighlightStyle=cellHighlightStyle;}});return{GenericTableViewTotalsItem:GenericTableViewTotalsItem,GenericTableViewColumnDescriptor:GenericTableViewColumnDescriptor};});'use strict';Polymer({is:'tr-ui-timeline-view-metadata-overlay',created:function(){this.metadata_=undefined;},get metadata(){return this.metadata_;},set metadata(metadata){this.metadata_=metadata;this.$.gtv.items=this.metadata_;}});'use strict';Polymer({is:'tr-v-ui-preferred-display-unit',ready:function(){this.preferredTimeDisplayMode_=undefined;},attached:function(){tr.b.Unit.didPreferredTimeDisplayUnitChange();},detached:function(){tr.b.Unit.didPreferredTimeDisplayUnitChange();},get preferredTimeDisplayMode(){return this.preferredTimeDisplayMode_;},set preferredTimeDisplayMode(v){if(this.preferredTimeDisplayMode_===v)
-return;this.preferredTimeDisplayMode_=v;tr.b.Unit.didPreferredTimeDisplayUnitChange();}});'use strict';Polymer({is:'tr-ui-timeline-view',attached:function(){this.async(function(){this.trackViewContainer_=Polymer.dom(this).querySelector('#track_view_container');if(!this.trackViewContainer_)
-console.error('missing trackviewContainer');});},ready:function(){this.tabIndex=0;this.titleEl_=this.$.title;this.leftControlsEl_=this.$.left_controls;this.rightControlsEl_=this.$.right_controls;this.collapsingControlsEl_=this.$.collapsing_controls;this.sidePanelContainer_=this.$.side_panel_container;this.brushingStateController_=new tr.c.BrushingStateController(this);this.findCtl_=this.$.view_find_control;this.findCtl_.controller=new tr.ui.FindController(this.brushingStateController_);this.scriptingCtl_=document.createElement('tr-ui-scripting-control');this.scriptingCtl_.controller=new tr.c.ScriptingController(this.brushingStateController_);this.sidePanelContainer_.brushingStateController=this.brushingStateController_;if(window.tr.metrics&&window.tr.metrics.sh&&window.tr.metrics.sh.SystemHealthMetric){this.railScoreSpan_=document.createElement('tr-metrics-ui-sh-system-health-span');Polymer.dom(this.rightControls).appendChild(this.railScoreSpan_);}else{this.railScoreSpan_=undefined;}
-this.optionsDropdown_=this.$.view_options_dropdown;Polymer.dom(this.optionsDropdown_.iconElement).textContent='View Options';this.showFlowEvents_=false;Polymer.dom(this.optionsDropdown_).appendChild(tr.ui.b.createCheckBox(this,'showFlowEvents','tr.ui.TimelineView.showFlowEvents',false,'Flow events'));this.highlightVSync_=false;this.highlightVSyncCheckbox_=tr.ui.b.createCheckBox(this,'highlightVSync','tr.ui.TimelineView.highlightVSync',false,'Highlight VSync');Polymer.dom(this.optionsDropdown_).appendChild(this.highlightVSyncCheckbox_);this.initMetadataButton_();this.initConsoleButton_();this.initHelpButton_();Polymer.dom(this.collapsingControls).appendChild(this.scriptingCtl_);this.dragEl_=this.$.drag_handle;this.analysisEl_=this.$.analysis;this.analysisEl_.brushingStateController=this.brushingStateController_;this.addEventListener('requestSelectionChange',function(e){var sc=this.brushingStateController_;sc.changeSelectionFromRequestSelectionChangeEvent(e.selection);}.bind(this));this.onViewportChanged_=this.onViewportChanged_.bind(this);this.bindKeyListeners_();this.dragEl_.target=this.analysisEl_;},get globalMode(){return this.hotkeyController.globalMode;},set globalMode(globalMode){globalMode=!!globalMode;this.brushingStateController_.historyEnabled=globalMode;this.hotkeyController.globalMode=globalMode;},get hotkeyController(){return this.$.hkc;},updateDocumentFavicon:function(){var hue;if(!this.model)
-hue='blue';else
-hue=this.model.faviconHue;var faviconData=tr.ui.b.FaviconsByHue[hue];if(faviconData===undefined)
-faviconData=tr.ui.b.FaviconsByHue['blue'];var link=Polymer.dom(document.head).querySelector('link[rel="shortcut icon"]');if(!link){link=document.createElement('link');link.rel='shortcut icon';Polymer.dom(document.head).appendChild(link);}
-link.href=faviconData;},get showFlowEvents(){return this.showFlowEvents_;},set showFlowEvents(showFlowEvents){this.showFlowEvents_=showFlowEvents;if(!this.trackView_)
-return;this.trackView_.viewport.showFlowEvents=showFlowEvents;},get highlightVSync(){return this.highlightVSync_;},set highlightVSync(highlightVSync){this.highlightVSync_=highlightVSync;if(!this.trackView_)
-return;this.trackView_.viewport.highlightVSync=highlightVSync;},initHelpButton_:function(){var helpButtonEl=this.$.view_help_button;var dlg=new tr.ui.b.Overlay();dlg.title='Chrome Tracing Help';dlg.visible=false;dlg.appendChild(document.createElement('tr-ui-timeline-view-help-overlay'));function onClick(e){dlg.visible=!dlg.visible;e.stopPropagation();}
+if(previouslyActivePanelType&&supportedPanelTypes.includes(previouslyActivePanelType)){this.activePanelType=previouslyActivePanelType;Polymer.dom(this).setAttribute('expanded',true);}else{if(this.activePanel){Polymer.dom(this.activePanelContainer_).removeChild(this.activePanel);}
+Polymer.dom(this).removeAttribute('expanded');}},get rangeOfInterest(){return this.rangeOfInterest_;},set rangeOfInterest(range){if(range===undefined){throw new Error('Must not be undefined');}
+this.rangeOfInterest_=range;if(this.activePanel){this.activePanel.rangeOfInterest=range;}}});'use strict';Polymer({is:'tr-ui-timeline-view-help-overlay',ready:function(){var mod=tr.isMac?'cmd ':'ctrl';var spans=Polymer.dom(this.root).querySelectorAll('span.mod');for(var i=0;i<spans.length;i++){Polymer.dom(spans[i]).textContent=mod;}}});'use strict';Polymer({is:'tr-ui-timeline-view-metadata-overlay',created(){this.metadata_=undefined;},ready(){this.$.table.tableColumns=[{title:'name',value:d=>d.name,},{title:'value',value:d=>{const gov=document.createElement('tr-ui-a-generic-object-view');gov.object=d.value;return gov;},}];},get metadata(){return this.metadata_;},set metadata(metadata){this.metadata_=metadata;this.$.table.tableRows=this.metadata_;this.$.table.rebuild();}});'use strict';Polymer({is:'tr-v-ui-preferred-display-unit',ready:function(){this.preferredTimeDisplayMode_=undefined;},attached:function(){tr.b.Unit.didPreferredTimeDisplayUnitChange();},detached:function(){tr.b.Unit.didPreferredTimeDisplayUnitChange();},get preferredTimeDisplayMode(){return this.preferredTimeDisplayMode_;},set preferredTimeDisplayMode(v){if(this.preferredTimeDisplayMode_===v)return;this.preferredTimeDisplayMode_=v;tr.b.Unit.didPreferredTimeDisplayUnitChange();}});'use strict';Polymer({is:'tr-ui-timeline-view',attached:function(){this.async(function(){this.trackViewContainer_=Polymer.dom(this).querySelector('#track_view_container');if(!this.trackViewContainer_){throw new Error('missing trackviewContainer');}});},ready:function(){this.tabIndex=0;this.titleEl_=this.$.title;this.leftControlsEl_=this.$.left_controls;this.rightControlsEl_=this.$.right_controls;this.collapsingControlsEl_=this.$.collapsing_controls;this.sidePanelContainer_=this.$.side_panel_container;this.brushingStateController_=new tr.c.BrushingStateController(this);this.findCtl_=this.$.view_find_control;this.findCtl_.controller=new tr.ui.FindController(this.brushingStateController_);this.scriptingCtl_=document.createElement('tr-ui-scripting-control');this.scriptingCtl_.controller=new tr.c.ScriptingController(this.brushingStateController_);this.sidePanelContainer_.brushingStateController=this.brushingStateController_;if(window.tr.metrics&&window.tr.metrics.sh&&window.tr.metrics.sh.SystemHealthMetric){this.railScoreSpan_=document.createElement('tr-metrics-ui-sh-system-health-span');Polymer.dom(this.rightControls).appendChild(this.railScoreSpan_);}else{this.railScoreSpan_=undefined;}
+this.optionsDropdown_=this.$.view_options_dropdown;Polymer.dom(this.optionsDropdown_.iconElement).textContent='View Options';this.showFlowEvents_=false;Polymer.dom(this.optionsDropdown_).appendChild(tr.ui.b.createCheckBox(this,'showFlowEvents','tr.ui.TimelineView.showFlowEvents',false,'Flow events'));this.highlightVSync_=false;this.highlightVSyncCheckbox_=tr.ui.b.createCheckBox(this,'highlightVSync','tr.ui.TimelineView.highlightVSync',false,'Highlight VSync');Polymer.dom(this.optionsDropdown_).appendChild(this.highlightVSyncCheckbox_);this.initMetadataButton_();this.initConsoleButton_();this.initHelpButton_();Polymer.dom(this.collapsingControls).appendChild(this.scriptingCtl_);this.dragEl_=this.$.drag_handle;this.analysisEl_=this.$.analysis;this.analysisEl_.brushingStateController=this.brushingStateController_;this.addEventListener('requestSelectionChange',function(e){var sc=this.brushingStateController_;sc.changeSelectionFromRequestSelectionChangeEvent(e.selection);}.bind(this));this.onViewportChanged_=this.onViewportChanged_.bind(this);this.bindKeyListeners_();this.dragEl_.target=this.analysisEl_;},get globalMode(){return this.hotkeyController.globalMode;},set globalMode(globalMode){globalMode=!!globalMode;this.brushingStateController_.historyEnabled=globalMode;this.hotkeyController.globalMode=globalMode;},get hotkeyController(){return this.$.hkc;},updateDocumentFavicon:function(){var hue;if(!this.model){hue='blue';}else{hue=this.model.faviconHue;}
+var faviconData=tr.ui.b.FaviconsByHue[hue];if(faviconData===undefined){faviconData=tr.ui.b.FaviconsByHue['blue'];}
+var link=Polymer.dom(document.head).querySelector('link[rel="shortcut icon"]');if(!link){link=document.createElement('link');link.rel='shortcut icon';Polymer.dom(document.head).appendChild(link);}
+link.href=faviconData;},get showFlowEvents(){return this.showFlowEvents_;},set showFlowEvents(showFlowEvents){this.showFlowEvents_=showFlowEvents;if(!this.trackView_)return;this.trackView_.viewport.showFlowEvents=showFlowEvents;},get highlightVSync(){return this.highlightVSync_;},set highlightVSync(highlightVSync){this.highlightVSync_=highlightVSync;if(!this.trackView_)return;this.trackView_.viewport.highlightVSync=highlightVSync;},initHelpButton_:function(){var helpButtonEl=this.$.view_help_button;var dlg=new tr.ui.b.Overlay();dlg.title='Chrome Tracing Help';dlg.visible=false;dlg.appendChild(document.createElement('tr-ui-timeline-view-help-overlay'));function onClick(e){dlg.visible=!dlg.visible;e.stopPropagation();}
 helpButtonEl.addEventListener('click',onClick.bind(this));},initConsoleButton_:function(){var toggleEl=this.$.view_console_button;function onClick(e){this.scriptingCtl_.toggleVisibility();e.stopPropagation();return false;}
 toggleEl.addEventListener('click',onClick.bind(this));},initMetadataButton_:function(){var showEl=this.$.view_metadata_button;function onClick(e){var dlg=new tr.ui.b.Overlay();dlg.title='Metadata for trace';var metadataOverlay=document.createElement('tr-ui-timeline-view-metadata-overlay');metadataOverlay.metadata=this.model.metadata;Polymer.dom(dlg).appendChild(metadataOverlay);dlg.visible=true;e.stopPropagation();return false;}
 showEl.addEventListener('click',onClick.bind(this));this.updateMetadataButtonVisibility_();},updateMetadataButtonVisibility_:function(){var showEl=this.$.view_metadata_button;showEl.style.display=(this.model&&this.model.metadata.length)?'':'none';},get leftControls(){return this.leftControlsEl_;},get rightControls(){return this.rightControlsEl_;},get collapsingControls(){return this.collapsingControlsEl_;},get viewTitle(){return Polymer.dom(this.titleEl_).textContent.substring(Polymer.dom(this.titleEl_).textContent.length-2);},set viewTitle(text){if(text===undefined){Polymer.dom(this.titleEl_).textContent='';this.titleEl_.hidden=true;return;}
-this.titleEl_.hidden=false;Polymer.dom(this.titleEl_).textContent=text;},get model(){if(this.trackView_)
-return this.trackView_.model;return undefined;},set model(model){var modelInstanceChanged=model!=this.model;var modelValid=model&&!model.bounds.isEmpty;var importWarningsEl=Polymer.dom(this.root).querySelector('#import-warnings');Polymer.dom(importWarningsEl).textContent='';if(modelInstanceChanged){if(this.railScoreSpan_)
-this.railScoreSpan_.model=undefined;Polymer.dom(this.trackViewContainer_).textContent='';if(this.trackView_){this.trackView_.viewport.removeEventListener('change',this.onViewportChanged_);this.trackView_.brushingStateController=undefined;this.trackView_.detach();this.trackView_=undefined;}
+this.titleEl_.hidden=false;Polymer.dom(this.titleEl_).textContent=text;},get model(){if(this.trackView_){return this.trackView_.model;}
+return undefined;},set model(model){var modelInstanceChanged=model!==this.model;var modelValid=model&&!model.bounds.isEmpty;var importWarningsEl=Polymer.dom(this.root).querySelector('#import-warnings');Polymer.dom(importWarningsEl).textContent='';if(modelInstanceChanged){if(this.railScoreSpan_){this.railScoreSpan_.model=undefined;}
+Polymer.dom(this.trackViewContainer_).textContent='';if(this.trackView_){this.trackView_.viewport.removeEventListener('change',this.onViewportChanged_);this.trackView_.brushingStateController=undefined;this.trackView_.detach();this.trackView_=undefined;}
 this.brushingStateController_.modelWillChange();}
 if(modelValid&&!this.trackView_){this.trackView_=document.createElement('tr-ui-timeline-track-view');this.trackView_.timelineView=this;this.trackView.brushingStateController=this.brushingStateController_;Polymer.dom(this.trackViewContainer_).appendChild(this.trackView_);this.trackView_.viewport.addEventListener('change',this.onViewportChanged_);}
-if(modelValid){this.trackView_.model=model;this.trackView_.viewport.showFlowEvents=this.showFlowEvents;this.trackView_.viewport.highlightVSync=this.highlightVSync;if(this.railScoreSpan_)
-this.railScoreSpan_.model=model;this.$.display_unit.preferredTimeDisplayMode=model.intrinsicTimeUnit;}
+if(modelValid){this.trackView_.model=model;this.trackView_.viewport.showFlowEvents=this.showFlowEvents;this.trackView_.viewport.highlightVSync=this.highlightVSync;if(this.railScoreSpan_){this.railScoreSpan_.model=model;}
+this.$.display_unit.preferredTimeDisplayMode=model.intrinsicTimeUnit;}
 if(model){model.importWarningsThatShouldBeShownToUser.forEach(function(importWarning){importWarningsEl.addMessage('Import Warning: '+importWarning.type+': '+
 importWarning.message);},this);}
-if(modelInstanceChanged){this.updateMetadataButtonVisibility_();this.brushingStateController_.modelDidChange();this.onViewportChanged_();}},get brushingStateController(){return this.brushingStateController_;},get trackView(){return this.trackView_;},get settings(){if(!this.settings_)
-this.settings_=new tr.b.Settings();return this.settings_;},set focusElement(value){throw new Error('This is deprecated. Please set globalMode to true.');},bindKeyListeners_:function(){var hkc=this.hotkeyController;hkc.addHotKey(new tr.ui.b.HotKey({eventType:'keypress',keyCode:'`'.charCodeAt(0),useCapture:true,thisArg:this,callback:function(e){this.scriptingCtl_.toggleVisibility();if(!this.scriptingCtl_.hasFocus)
-this.focus();e.stopPropagation();}}));hkc.addHotKey(new tr.ui.b.HotKey({eventType:'keypress',keyCode:'/'.charCodeAt(0),useCapture:true,thisArg:this,callback:function(e){if(this.scriptingCtl_.hasFocus)
-return;if(this.findCtl_.hasFocus)
-this.focus();else
-this.findCtl_.focus();e.preventDefault();e.stopPropagation();}}));hkc.addHotKey(new tr.ui.b.HotKey({eventType:'keypress',keyCode:'?'.charCodeAt(0),useCapture:false,thisArg:this,callback:function(e){this.$.view_help_button.click();e.stopPropagation();}}));hkc.addHotKey(new tr.ui.b.HotKey({eventType:'keypress',keyCode:'v'.charCodeAt(0),useCapture:false,thisArg:this,callback:function(e){this.toggleHighlightVSync_();e.stopPropagation();}}));},onViewportChanged_:function(e){var spc=this.sidePanelContainer_;if(!this.trackView_){spc.rangeOfInterest.reset();return;}
-var vr=this.trackView_.viewport.interestRange.asRangeObject();if(!spc.rangeOfInterest.equals(vr))
-spc.rangeOfInterest=vr;if(this.railScoreSpan_&&this.model)
-this.railScoreSpan_.model=this.model;},toggleHighlightVSync_:function(){this.highlightVSyncCheckbox_.checked=!this.highlightVSyncCheckbox_.checked;},setFindCtlText:function(string){this.findCtl_.setText(string);}});'use strict';tr.exportTo('tr.ui.e.highlighter',function(){var Highlighter=tr.ui.tracks.Highlighter;function VSyncHighlighter(viewport){Highlighter.call(this,viewport);this.times_=[];}
-VSyncHighlighter.VSYNC_HIGHLIGHT_COLOR=new tr.b.Color(0,0,255);VSyncHighlighter.VSYNC_HIGHLIGHT_ALPHA=0.1;VSyncHighlighter.VSYNC_DENSITY_TRANSPARENT=0.20;VSyncHighlighter.VSYNC_DENSITY_OPAQUE=0.10;VSyncHighlighter.VSYNC_DENSITY_RANGE=VSyncHighlighter.VSYNC_DENSITY_TRANSPARENT-
-VSyncHighlighter.VSYNC_DENSITY_OPAQUE;VSyncHighlighter.generateStripes=function(times,minTime,maxTime){if(times.length===0)
-return[];var stripes=[];var lowIndex=tr.b.findLowIndexInSortedArray(times,function(time){return time;},minTime);var highIndex=lowIndex-1;while(times[highIndex+1]<=maxTime){highIndex++;}
-for(var i=lowIndex-(lowIndex%2);i<=highIndex;i+=2){var left=i<lowIndex?minTime:times[i];var right=i+1>highIndex?maxTime:times[i+1];stripes.push([left,right]);}
-return stripes;}
-VSyncHighlighter.prototype={__proto__:Highlighter.prototype,processModel:function(model){this.times_=model.device.vSyncTimestamps;},drawHighlight:function(ctx,dt,viewLWorld,viewRWorld,viewHeight){if(!this.viewport_.highlightVSync){return;}
-var stripes=VSyncHighlighter.generateStripes(this.times_,viewLWorld,viewRWorld);if(stripes.length==0){return;}
-var stripeRange=stripes[stripes.length-1][1]-stripes[0][0];var stripeDensity=stripes.length/(dt.scaleX*stripeRange);var clampedStripeDensity=tr.b.clamp(stripeDensity,VSyncHighlighter.VSYNC_DENSITY_OPAQUE,VSyncHighlighter.VSYNC_DENSITY_TRANSPARENT);var opacity=(VSyncHighlighter.VSYNC_DENSITY_TRANSPARENT-clampedStripeDensity)/VSyncHighlighter.VSYNC_DENSITY_RANGE;if(opacity==0){return;}
-var pixelRatio=window.devicePixelRatio||1;var height=viewHeight*pixelRatio;var c=VSyncHighlighter.VSYNC_HIGHLIGHT_COLOR;ctx.fillStyle=c.toStringWithAlphaOverride(VSyncHighlighter.VSYNC_HIGHLIGHT_ALPHA*opacity);for(var i=0;i<stripes.length;i++){var xLeftView=dt.xWorldToView(stripes[i][0]);var xRightView=dt.xWorldToView(stripes[i][1]);ctx.fillRect(xLeftView,0,xRightView-xLeftView,height);}}};tr.ui.tracks.Highlighter.register(VSyncHighlighter);return{VSyncHighlighter:VSyncHighlighter};});'use strict';tr.exportTo('tr.ui.b',function(){function Row(title,data,groupingKeyFuncs,rowStatsConstructor){this.title=title;this.data_=data;if(groupingKeyFuncs===undefined)
-groupingKeyFuncs=[];this.groupingKeyFuncs_=groupingKeyFuncs;this.rowStatsConstructor_=rowStatsConstructor;this.subRowsBuilt_=false;this.subRows_=undefined;this.rowStats_=undefined;}
-Row.prototype={getCurrentGroupingKeyFunc_:function(){if(this.groupingKeyFuncs_.length===0)
-return undefined;return this.groupingKeyFuncs_[0];},get data(){return this.data_;},get rowStats(){if(this.rowStats_===undefined){this.rowStats_=new this.rowStatsConstructor_(this);}
-return this.rowStats_;},rebuildSubRowsIfNeeded_:function(){if(this.subRowsBuilt_)
-return;this.subRowsBuilt_=true;var groupingKeyFunc=this.getCurrentGroupingKeyFunc_();if(groupingKeyFunc===undefined){this.subRows_=undefined;return;}
-var dataByKey={};var hasValues=false;this.data_.forEach(function(datum){var key=groupingKeyFunc(datum);hasValues=hasValues||(key!==undefined);if(dataByKey[key]===undefined)
-dataByKey[key]=[];dataByKey[key].push(datum);});if(!hasValues){this.subRows_=undefined;return;}
-this.subRows_=[];for(var key in dataByKey){var row=new Row(key,dataByKey[key],this.groupingKeyFuncs_.slice(1),this.rowStatsConstructor_);this.subRows_.push(row);}},get isExpanded(){return(this.subRows&&(this.subRows.length>0)&&(this.subRows.length<5));},get subRows(){this.rebuildSubRowsIfNeeded_();return this.subRows_;}};Polymer({is:'tr-ui-b-grouping-table',created:function(){this.dataToGroup_=undefined;this.groupBy_=undefined;this.rowStatsConstructor_=undefined;},get tableColumns(){return this.$.table.tableColumns;},set tableColumns(tableColumns){this.$.table.tableColumns=tableColumns;},get tableRows(){return this.$.table.tableRows;},get sortColumnIndex(){return this.$.table.sortColumnIndex;},set sortColumnIndex(sortColumnIndex){this.$.table.sortColumnIndex=sortColumnIndex;},get sortDescending(){return this.$.table.sortDescending;},set sortDescending(sortDescending){this.$.table.sortDescending=sortDescending;},get selectionMode(){return this.$.table.selectionMode;},set selectionMode(selectionMode){this.$.table.selectionMode=selectionMode;},get rowHighlightStyle(){return this.$.table.rowHighlightStyle;},set rowHighlightStyle(rowHighlightStyle){this.$.table.rowHighlightStyle=rowHighlightStyle;},get cellHighlightStyle(){return this.$.table.cellHighlightStyle;},set cellHighlightStyle(cellHighlightStyle){this.$.table.cellHighlightStyle=cellHighlightStyle;},get selectedColumnIndex(){return this.$.table.selectedColumnIndex;},set selectedColumnIndex(selectedColumnIndex){this.$.table.selectedColumnIndex=selectedColumnIndex;},get selectedTableRow(){return this.$.table.selectedTableRow;},set selectedTableRow(selectedTableRow){this.$.table.selectedTableRow=selectedTableRow;},get groupBy(){return this.groupBy_;},set groupBy(groupBy){this.groupBy_=groupBy;this.updateContents_();},get dataToGroup(){return this.dataToGroup_;},set dataToGroup(dataToGroup){this.dataToGroup_=dataToGroup;this.updateContents_();},get rowStatsConstructor(){return this.rowStatsConstructor_;},set rowStatsConstructor(rowStatsConstructor){this.rowStatsConstructor_=rowStatsConstructor;this.updateContents_();},rebuild:function(){this.$.table.rebuild();},updateContents_:function(){var groupBy=this.groupBy_||[];var dataToGroup=this.dataToGroup_||[];var rowStatsConstructor=this.rowStatsConstructor_||function(){};var superRow=new Row('',dataToGroup,groupBy,rowStatsConstructor);this.$.table.tableRows=superRow.subRows||[];}});return{};});'use strict';tr.exportTo('tr.ui.b',function(){var THIS_DOC=document.currentScript.ownerDocument;Polymer({is:'tr-ui-b-grouping-table-groupby-picker-group',created:function(){this.picker_=undefined;this.group_=undefined;},ready:function(){this.setAttribute('draggable',true);this.addEventListener('mouseover',this.onMouseOver_.bind(this));this.addEventListener('mouseleave',this.onMouseLeave_.bind(this));this.addEventListener('dragstart',this.onDragStart_.bind(this));this.addEventListener('dragover',this.onDragOver_.bind(this));},set group(g){this.group_=g;this.$.key.textContent=g.label;},get key(){return this.group_.key;},get picker(){return this.picker_;},set picker(picker){this.picker_=picker;this.vertical=picker.vertical;},get vertical(){return this.getAttribute('vertical');},set vertical(vertical){if(vertical)
-this.setAttribute('vertical',true);else
-this.removeAttribute('vertical');},onMouseOver_:function(event){this.$.remove.style.visibility='visible';},onMouseLeave_:function(event){this.$.remove.style.visibility='hidden';},onDragStart_:function(event){event.dataTransfer.effectAllowed='move';this.setAttribute('dragging',true);},onDragOver_:function(event){event.preventDefault();event.dataTransfer.dropEffect='move';this.picker.clearDragIndicators_();if(this.picker.shouldDropBefore_(this,event)){this.classList.add('drop-before');if(this.previousElementSibling)
-this.previousElementSibling.classList.add('drop-after');}else{this.classList.add('drop-after');if(this.nextElementSibling)
-this.nextElementSibling.classList.add('drop-before');}
-return false;},remove_:function(event){var newKeys=this.picker.currentGroupKeys.slice();newKeys.splice(newKeys.indexOf(this.key),1);this.picker.currentGroupKeys=newKeys;}});Polymer({is:'tr-ui-b-grouping-table-groupby-picker',created:function(){this.currentGroupKeys_=undefined;this.defaultGroupKeys_=undefined;this.possibleGroups_=[];this.settingsKey_=[];},ready:function(){Polymer.dom(this.$.add_group.iconElement).textContent='Add another...';this.addEventListener('dragend',this.onDragEnd_.bind(this));this.addEventListener('drop',this.onDrop_.bind(this));},get defaultGroupKeys(){return this.defaultGroupKeys_;},set vertical(vertical){if(vertical)
-this.setAttribute('vertical',true);else
-this.removeAttribute('vertical');for(var groupEl of this.$.groups.childNodes)
-groupEl.vertical=vertical;},get vertical(){return this.getAttribute('vertical');},set defaultGroupKeys(defaultGroupKeys){this.defaultGroupKeys_=defaultGroupKeys;this.maybeInit_();},get possibleGroups(){return this.possibleGroups_;},set possibleGroups(possibleGroups){this.possibleGroups_=possibleGroups;this.maybeInit_();},get settingsKey(){return this.settingsKey_;},set settingsKey(settingsKey){this.settingsKey_=settingsKey;this.maybeInit_();},maybeInit_:function(){if(!this.settingsKey_||!this.defaultGroupKeys_||!this.possibleGroups_){return;}
-this.currentGroupKeys=tr.b.Settings.get(this.settingsKey_,this.defaultGroupKeys_);},get currentGroupKeys(){return this.currentGroupKeys_;},get currentGroups(){var groupsByKey={};for(var group of this.possibleGroups_)
-groupsByKey[group.key]=group;return this.currentGroupKeys.map(groupKey=>groupsByKey[groupKey]);},set currentGroupKeys(currentGroupKeys){if(this.currentGroupKeys_===currentGroupKeys)
-return;if(!(currentGroupKeys instanceof Array))
-throw new Error('Must be array');var availableGroupKeys=new Set();for(var group of this.possibleGroups_)
-availableGroupKeys.add(group.key);this.currentGroupKeys_=currentGroupKeys.filter(k=>availableGroupKeys.has(k));this.updateGroups_();tr.b.Settings.set(this.settingsKey_,this.currentGroupKeys_);this.dispatchEvent(new tr.b.Event('current-groups-changed'));},get draggingGroupElement(){for(var group of this.$.groups.children)
-if(group.getAttribute('dragging'))
-return group;return undefined;},shouldDropBefore_:function(groupEl,event){var dragBoxRect=this.draggingGroupElement.getBoundingClientRect();var dropBoxRect=groupEl.getBoundingClientRect();var dragVertRange=tr.b.Range.fromExplicitRange(dragBoxRect.top,dragBoxRect.bottom);var dropVertRange=tr.b.Range.fromExplicitRange(dropBoxRect.top,dropBoxRect.bottom);if(dragVertRange.intersectsRangeInclusive(dropVertRange))
-return event.clientX<((dropBoxRect.left+dropBoxRect.right)/2);return event.clientY<((dropBoxRect.top+dropBoxRect.bottom)/2);},clearDragIndicators_:function(){for(var groupEl of this.$.groups.children){groupEl.classList.remove('drop-before');groupEl.classList.remove('drop-after');}},onDragEnd_:function(event){this.clearDragIndicators_();for(var groupEl of this.$.groups.children)
-groupEl.removeAttribute('dragging');},onDrop_:function(event){event.stopPropagation();var draggingGroupEl=this.draggingGroupElement;var dropBeforeEl=undefined;var dropAfterEl=undefined;for(var groupEl of this.$.groups.children){if(groupEl.classList.contains('drop-before')){dropBeforeEl=groupEl;break;}
-if(groupEl.classList.contains('drop-after')){dropAfterEl=groupEl;break;}}
-if(!dropBeforeEl&&!dropAfterEl)
-return;this.$.groups.removeChild(draggingGroupEl);var lastGroupEl=this.$.groups.children[this.$.groups.children.length-1];if(dropBeforeEl)
-this.$.groups.insertBefore(draggingGroupEl,dropBeforeEl);else if(dropAfterEl===lastGroupEl)
-this.$.groups.appendChild(draggingGroupEl);else
-this.$.groups.insertBefore(draggingGroupEl,dropAfterEl.nextSibling);var currentGroupKeys=[];for(var group of this.$.groups.children)
-currentGroupKeys.push(group.key);this.currentGroupKeys=currentGroupKeys;return false;},updateGroups_:function(){Polymer.dom(this.$.groups).textContent='';Polymer.dom(this.$.add_group).textContent='';var unusedGroups={};var groupsByKey={};for(var group of this.possibleGroups_){unusedGroups[group.key]=group;groupsByKey[group.key]=group;}
-for(var key of this.currentGroupKeys_)
-delete unusedGroups[key];for(var key of this.currentGroupKeys_){var group=groupsByKey[key];var groupEl=document.createElement('tr-ui-b-grouping-table-groupby-picker-group');groupEl.picker=this;groupEl.group=group;Polymer.dom(this.$.groups).appendChild(groupEl);}
-tr.b.iterItems(unusedGroups,function(key,group){var groupEl=document.createElement('possible-group');Polymer.dom(groupEl).textContent=group.label;groupEl.addEventListener('click',function(){var newKeys=this.currentGroupKeys.slice();newKeys.push(key);this.currentGroupKeys=newKeys;this.$.add_group.close();}.bind(this));Polymer.dom(this.$.add_group).appendChild(groupEl);},this);if(tr.b.dictionaryLength(unusedGroups)==0){this.$.add_group.style.display='none';}else{this.$.add_group.style.display='';}}});return{};});'use strict';(function(){Polymer({is:'tr-ui-sp-file-size-stats-side-panel',behaviors:[tr.ui.behaviors.SidePanel],ready:function(){this.model_=undefined;this.selection_=new tr.model.EventSet();this.$.picker.settingsKey='tr-ui-sp-file-size-stats-side-panel-picker';this.$.picker.possibleGroups=[{key:'phase',label:'Event Type',dataFn:function(eventStat){return eventStat.phase;}},{key:'category',label:'Category',dataFn:function(eventStat){return eventStat.category;}},{key:'title',label:'Title',dataFn:function(eventStat){return eventStat.title;}}];this.$.picker.defaultGroupKeys=['phase','title'];this.$.picker.addEventListener('current-groups-changed',this.updateContents_.bind(this));},get textLabel(){return'File Size Stats';},supportsModel:function(m){if(!m){return{supported:false,reason:'No stats were collected for this file.'};}
+if(modelInstanceChanged){this.updateMetadataButtonVisibility_();this.brushingStateController_.modelDidChange();this.onViewportChanged_();}},get brushingStateController(){return this.brushingStateController_;},get trackView(){return this.trackView_;},get settings(){if(!this.settings_){this.settings_=new tr.b.Settings();}
+return this.settings_;},set focusElement(value){throw new Error('This is deprecated. Please set globalMode to true.');},bindKeyListeners_:function(){var hkc=this.hotkeyController;hkc.addHotKey(new tr.ui.b.HotKey({eventType:'keypress',keyCode:'`'.charCodeAt(0),useCapture:true,thisArg:this,callback:function(e){this.scriptingCtl_.toggleVisibility();if(!this.scriptingCtl_.hasFocus){this.focus();}
+e.stopPropagation();}}));hkc.addHotKey(new tr.ui.b.HotKey({eventType:'keypress',keyCode:'/'.charCodeAt(0),useCapture:true,thisArg:this,callback:function(e){if(this.scriptingCtl_.hasFocus)return;if(this.findCtl_.hasFocus){this.focus();}else{this.findCtl_.focus();}
+e.preventDefault();e.stopPropagation();}}));hkc.addHotKey(new tr.ui.b.HotKey({eventType:'keypress',keyCode:'?'.charCodeAt(0),useCapture:false,thisArg:this,callback:function(e){this.$.view_help_button.click();e.stopPropagation();}}));hkc.addHotKey(new tr.ui.b.HotKey({eventType:'keypress',keyCode:'v'.charCodeAt(0),useCapture:false,thisArg:this,callback:function(e){this.toggleHighlightVSync_();e.stopPropagation();}}));},onViewportChanged_:function(e){var spc=this.sidePanelContainer_;if(!this.trackView_){spc.rangeOfInterest.reset();return;}
+var vr=this.trackView_.viewport.interestRange.asRangeObject();if(!spc.rangeOfInterest.equals(vr)){spc.rangeOfInterest=vr;}
+if(this.railScoreSpan_&&this.model){this.railScoreSpan_.model=this.model;}},toggleHighlightVSync_:function(){this.highlightVSyncCheckbox_.checked=!this.highlightVSyncCheckbox_.checked;},setFindCtlText:function(string){this.findCtl_.setText(string);}});'use strict';tr.exportTo('tr.ui.b',function(){function Row(title,data,groupingKeyFuncs,rowStatsConstructor){this.title=title;this.data_=data;if(groupingKeyFuncs===undefined){groupingKeyFuncs=[];}
+this.groupingKeyFuncs_=groupingKeyFuncs;this.rowStatsConstructor_=rowStatsConstructor;this.subRowsBuilt_=false;this.subRows_=undefined;this.rowStats_=undefined;}
+Row.prototype={getCurrentGroupingKeyFunc_:function(){if(this.groupingKeyFuncs_.length===0)return undefined;return this.groupingKeyFuncs_[0];},get data(){return this.data_;},get rowStats(){if(this.rowStats_===undefined){this.rowStats_=new this.rowStatsConstructor_(this);}
+return this.rowStats_;},rebuildSubRowsIfNeeded_:function(){if(this.subRowsBuilt_)return;this.subRowsBuilt_=true;var groupingKeyFunc=this.getCurrentGroupingKeyFunc_();if(groupingKeyFunc===undefined){this.subRows_=undefined;return;}
+var dataByKey={};var hasValues=false;this.data_.forEach(function(datum){var key=groupingKeyFunc(datum);hasValues=hasValues||(key!==undefined);if(dataByKey[key]===undefined){dataByKey[key]=[];}
+dataByKey[key].push(datum);});if(!hasValues){this.subRows_=undefined;return;}
+this.subRows_=[];for(var key in dataByKey){var row=new Row(key,dataByKey[key],this.groupingKeyFuncs_.slice(1),this.rowStatsConstructor_);this.subRows_.push(row);}},get isExpanded(){return(this.subRows&&(this.subRows.length>0)&&(this.subRows.length<5));},get subRows(){this.rebuildSubRowsIfNeeded_();return this.subRows_;}};Polymer({is:'tr-ui-b-grouping-table',created:function(){this.dataToGroup_=undefined;this.groupBy_=undefined;this.rowStatsConstructor_=undefined;},get tableColumns(){return this.$.table.tableColumns;},set tableColumns(tableColumns){this.$.table.tableColumns=tableColumns;},get tableRows(){return this.$.table.tableRows;},get sortColumnIndex(){return this.$.table.sortColumnIndex;},set sortColumnIndex(sortColumnIndex){this.$.table.sortColumnIndex=sortColumnIndex;},get sortDescending(){return this.$.table.sortDescending;},set sortDescending(sortDescending){this.$.table.sortDescending=sortDescending;},get selectionMode(){return this.$.table.selectionMode;},set selectionMode(selectionMode){this.$.table.selectionMode=selectionMode;},get rowHighlightStyle(){return this.$.table.rowHighlightStyle;},set rowHighlightStyle(rowHighlightStyle){this.$.table.rowHighlightStyle=rowHighlightStyle;},get cellHighlightStyle(){return this.$.table.cellHighlightStyle;},set cellHighlightStyle(cellHighlightStyle){this.$.table.cellHighlightStyle=cellHighlightStyle;},get selectedColumnIndex(){return this.$.table.selectedColumnIndex;},set selectedColumnIndex(selectedColumnIndex){this.$.table.selectedColumnIndex=selectedColumnIndex;},get selectedTableRow(){return this.$.table.selectedTableRow;},set selectedTableRow(selectedTableRow){this.$.table.selectedTableRow=selectedTableRow;},get groupBy(){return this.groupBy_;},set groupBy(groupBy){this.groupBy_=groupBy;this.updateContents_();},get dataToGroup(){return this.dataToGroup_;},set dataToGroup(dataToGroup){this.dataToGroup_=dataToGroup;this.updateContents_();},get rowStatsConstructor(){return this.rowStatsConstructor_;},set rowStatsConstructor(rowStatsConstructor){this.rowStatsConstructor_=rowStatsConstructor;this.updateContents_();},rebuild:function(){this.$.table.rebuild();},updateContents_:function(){var groupBy=this.groupBy_||[];var dataToGroup=this.dataToGroup_||[];var rowStatsConstructor=this.rowStatsConstructor_||function(){};var superRow=new Row('',dataToGroup,groupBy,rowStatsConstructor);this.$.table.tableRows=superRow.subRows||[];}});return{};});'use strict';tr.exportTo('tr.ui.b',function(){var THIS_DOC=document.currentScript.ownerDocument;Polymer({is:'tr-ui-b-grouping-table-groupby-picker-group',created:function(){this.picker_=undefined;this.group_=undefined;},get picker(){return this.picker_;},set picker(picker){this.picker_=picker;},get group(){return this.group_;},set group(g){this.group_=g;this.$.label.textContent=g.label;},get enabled(){return this.$.enabled.checked;},set enabled(enabled){this.$.enabled.checked=enabled;if(!this.enabled){this.$.left.style.display='none';this.$.right.style.display='none';}},set isFirst(isFirst){this.$.left.style.display=(!this.enabled||isFirst)?'none':'inline';},set isLast(isLast){this.$.right.style.display=(!this.enabled||isLast)?'none':'inline';},moveLeft_:function(){this.picker.moveLeft_(this);},moveRight_:function(){this.picker.moveRight_(this);},onEnableChanged_:function(){if(!this.enabled){this.$.left.style.display='none';this.$.right.style.display='none';}
+this.picker.onEnableChanged_(this);}});Polymer({is:'tr-ui-b-grouping-table-groupby-picker',created:function(){this.settingsKey_=undefined;},get settingsKey(){return this.settingsKey_;},set settingsKey(settingsKey){this.settingsKey_=settingsKey;if(this.$.container.children.length){this.restoreSetting_();}},restoreSetting_:function(){this.currentGroupKeys=tr.b.Settings.get(this.settingsKey_,this.currentGroupKeys);},get possibleGroups(){return[...this.$.container.children].map(groupEl=>groupEl.group);},set possibleGroups(possibleGroups){Polymer.dom(this.$.container).textContent='';for(var i=0;i<possibleGroups.length;++i){var groupEl=document.createElement('tr-ui-b-grouping-table-groupby-picker-group');groupEl.picker=this;groupEl.group=possibleGroups[i];Polymer.dom(this.$.container).appendChild(groupEl);}
+this.restoreSetting_();this.updateFirstLast_();},updateFirstLast_:function(){var groupEls=this.$.container.children;var enabledGroupEls=[...groupEls].filter(el=>el.enabled);for(var i=0;i<enabledGroupEls.length;++i){enabledGroupEls[i].isFirst=i===0;enabledGroupEls[i].isLast=i===enabledGroupEls.length-1;}},get currentGroupKeys(){return this.currentGroups.map(group=>group.key);},get currentGroups(){var groups=[];for(var groupEl of this.$.container.children){if(groupEl.enabled){groups.push(groupEl.group);}}
+return groups;},set currentGroupKeys(newKeys){if(!tr.b.compareArrays(this.currentGroupKeys,newKeys,(x,y)=>x.localeCompare(y))){return;}
+var possibleGroups=new Map();for(var group of this.possibleGroups){possibleGroups.set(group.key,group);}
+var groupEls=this.$.container.children;var i=0;for(i=0;i<newKeys.length;++i){var group=possibleGroups.get(newKeys[i]);if(group===undefined){newKeys.splice(i,1);--i;continue;}
+groupEls[i].group=group;groupEls[i].enabled=true;possibleGroups.delete(newKeys[i]);}
+for(var group of possibleGroups.values()){groupEls[i].group=group;groupEls[i].enabled=false;++i;}
+this.updateFirstLast_();this.onCurrentGroupsChanged_();},moveLeft_:function(groupEl){var reference=groupEl.previousSibling;Polymer.dom(this.$.container).removeChild(groupEl);Polymer.dom(this.$.container).insertBefore(groupEl,reference);this.updateFirstLast_();if(groupEl.enabled){this.onCurrentGroupsChanged_();}},moveRight_:function(groupEl){var reference=groupEl.nextSibling.nextSibling;Polymer.dom(this.$.container).removeChild(groupEl);if(reference){Polymer.dom(this.$.container).insertBefore(groupEl,reference);}else{Polymer.dom(this.$.container).appendChild(groupEl);}
+this.updateFirstLast_();if(groupEl.enabled){this.onCurrentGroupsChanged_();}},onCurrentGroupsChanged_:function(){this.dispatchEvent(new tr.b.Event('current-groups-changed'));tr.b.Settings.set(this.settingsKey_,this.currentGroupKeys);},onEnableChanged_:function(groupEl){this.updateFirstLast_();this.onCurrentGroupsChanged_();}});return{};});'use strict';(function(){Polymer({is:'tr-ui-sp-file-size-stats-side-panel',behaviors:[tr.ui.behaviors.SidePanel],ready:function(){this.model_=undefined;this.selection_=new tr.model.EventSet();this.$.picker.settingsKey='tr-ui-sp-file-size-stats-side-panel-picker';this.$.picker.possibleGroups=[{key:'phase',label:'Event Type',dataFn:function(eventStat){return eventStat.phase;}},{key:'category',label:'Category',dataFn:function(eventStat){return eventStat.category;}},{key:'title',label:'Title',dataFn:function(eventStat){return eventStat.title;}}];if(this.$.picker.currentGroupKeys.length===0){this.$.picker.currentGroupKeys=['phase','title'];}
+this.$.picker.addEventListener('current-groups-changed',this.updateContents_.bind(this));},get textLabel(){return'File Size Stats';},supportsModel:function(m){if(!m){return{supported:false,reason:'No stats were collected for this file.'};}
 if(m.stats.allTraceEventStats.length===0){return{supported:false,reason:'No stats were collected for this file.'};}
-return{supported:true};},get model(){return this.model_;},set model(model){this.model_=model;this.updateContents_();},get rangeOfInterest(){return this.rangeOfInterest_;},set rangeOfInterest(rangeOfInterest){this.rangeOfInterest_=rangeOfInterest;},get selection(){return this.selection_;},set selection(selection){this.selection_=selection;},createColumns_:function(stats){var columns=[{title:'Title',value:function(row){var titleEl=document.createElement('span');Polymer.dom(titleEl).textContent=row.title;titleEl.style.textOverflow='ellipsis';return titleEl;},cmp:function(a,b){return a.title.localeCompare(b.title);},width:'400px'},{title:'Num Events',align:tr.ui.b.TableFormat.ColumnAlignment.RIGHT,value:function(row){return row.rowStats.numEvents;},cmp:function(a,b){return a.rowStats.numEvents-b.rowStats.numEvents;},width:'80px'}];if(stats&&stats.hasEventSizesinBytes){columns.push({title:'Bytes',align:tr.ui.b.TableFormat.ColumnAlignment.RIGHT,value:function(row){var value=new tr.v.ScalarNumeric(tr.b.Unit.byName.sizeInBytes,row.rowStats.totalEventSizeinBytes);var spanEl=tr.v.ui.createScalarSpan(value);return spanEl;},cmp:function(a,b){return a.rowStats.totalEventSizeinBytes-
+return{supported:true};},get model(){return this.model_;},set model(model){this.model_=model;this.updateContents_();},get rangeOfInterest(){return this.rangeOfInterest_;},set rangeOfInterest(rangeOfInterest){this.rangeOfInterest_=rangeOfInterest;},get selection(){return this.selection_;},set selection(selection){this.selection_=selection;},createColumns_:function(stats){var columns=[{title:'Title',value:function(row){var titleEl=document.createElement('span');Polymer.dom(titleEl).textContent=row.title;titleEl.style.textOverflow='ellipsis';return titleEl;},cmp:function(a,b){return a.title.localeCompare(b.title);},width:'400px'},{title:'Num Events',align:tr.ui.b.TableFormat.ColumnAlignment.RIGHT,value:function(row){return row.rowStats.numEvents;},cmp:function(a,b){return a.rowStats.numEvents-b.rowStats.numEvents;},width:'80px'}];if(stats&&stats.hasEventSizesinBytes){columns.push({title:'Bytes',value:function(row){var value=new tr.b.Scalar(tr.b.Unit.byName.sizeInBytes,row.rowStats.totalEventSizeinBytes);var spanEl=tr.v.ui.createScalarSpan(value);return spanEl;},cmp:function(a,b){return a.rowStats.totalEventSizeinBytes-
 b.rowStats.totalEventSizeinBytes;},width:'80px'});}
-return columns;},updateContents_:function(){var table=this.$.table;var columns=this.createColumns_(this.model.stats);table.rowStatsConstructor=function ModelStatsRowStats(row){var sum=tr.b.Statistics.sum(row.data,function(x){return x.numEvents;});var totalEventSizeinBytes=tr.b.Statistics.sum(row.data,function(x){return x.totalEventSizeinBytes;});return{numEvents:sum,totalEventSizeinBytes:totalEventSizeinBytes};};table.tableColumns=columns;table.sortColumnIndex=1;table.sortDescending=true;table.selectionMode=tr.ui.b.TableFormat.SelectionMode.ROW;table.groupBy=this.$.picker.currentGroups.map(function(group){return group.dataFn;});if(!this.model){table.dataToGroup=[];}else{table.dataToGroup=this.model.stats.allTraceEventStats;}
-this.$.table.rebuild();}});tr.ui.side_panel.SidePanelRegistry.register(function(){return document.createElement('tr-ui-sp-file-size-stats-side-panel');});})();'use strict';tr.exportTo('tr.mre',function(){var FunctionRegistry={allFunctions_:[],allFunctionsByName_:{},get allFunctions(){return this.allFunctions_;},get allFunctionsByName(){return this.allFunctionsByName_;}};FunctionRegistry.getFunction=function(name){return this.allFunctionsByName_[name];};FunctionRegistry.register=function(func){if(func.name==='')
-throw new Error('Registered functions must not be anonymous');if(this.allFunctionsByName[func.name]!==undefined)
-throw new Error('Function named '+func.name+'is already registered.');this.allFunctionsByName[func.name]=func;this.allFunctions.push(func);};function ModuleToLoad(href,filename){if((href!==undefined)?(filename!==undefined):(filename===undefined)){throw new Error('ModuleToLoad must specify exactly one of href or '+'filename');}
+return columns;},updateContents_:function(){var table=this.$.table;var columns=this.createColumns_(this.model.stats);table.rowStatsConstructor=function ModelStatsRowStats(row){var sum=tr.b.math.Statistics.sum(row.data,function(x){return x.numEvents;});var totalEventSizeinBytes=tr.b.math.Statistics.sum(row.data,x=>x.totalEventSizeinBytes);return{numEvents:sum,totalEventSizeinBytes:totalEventSizeinBytes};};table.tableColumns=columns;table.sortColumnIndex=1;table.sortDescending=true;table.selectionMode=tr.ui.b.TableFormat.SelectionMode.ROW;table.groupBy=this.$.picker.currentGroups.map(function(group){return group.dataFn;});if(!this.model){table.dataToGroup=[];}else{table.dataToGroup=this.model.stats.allTraceEventStats;}
+this.$.table.rebuild();}});tr.ui.side_panel.SidePanelRegistry.register(function(){return document.createElement('tr-ui-sp-file-size-stats-side-panel');});})();'use strict';tr.exportTo('tr.mre',function(){var FunctionRegistry={allFunctions_:[],allFunctionsByName_:{},get allFunctions(){return this.allFunctions_;},get allFunctionsByName(){return this.allFunctionsByName_;}};FunctionRegistry.getFunction=function(name){return this.allFunctionsByName_[name];};FunctionRegistry.register=function(func){if(func.name===''){throw new Error('Registered functions must not be anonymous');}
+if(this.allFunctionsByName[func.name]!==undefined){throw new Error('Function named '+func.name+'is already registered.');}
+this.allFunctionsByName[func.name]=func;this.allFunctions.push(func);};function ModuleToLoad(href,filename){if((href!==undefined)?(filename!==undefined):(filename===undefined)){throw new Error('ModuleToLoad must specify exactly one of href or '+'filename');}
 this.href=href;this.filename=filename;}
-ModuleToLoad.prototype={asDict:function(){if(this.href!==undefined)
-return{'href':this.href};return{'filename':this.filename};},toString:function(){if(this.href!==undefined)
-return'ModuleToLoad(href="'+this.href+'")';return'ModuleToLoad(filename="'+this.filename+'")';}};ModuleToLoad.fromDict=function(moduleDict){return new ModuleToLoad(moduleDict.href,moduleDict.filename);};function FunctionHandle(modulesToLoad,functionName,opt_options){if(!(modulesToLoad instanceof Array))
-throw new Error('modulesToLoad in FunctionHandle must be an array');if(typeof(functionName)!=='string')
-throw new Error('functionName in FunctionHandle must be a string');this.modulesToLoad=modulesToLoad;this.functionName=functionName;this.options_=opt_options;};FunctionHandle.prototype={get options(){return this.options_;},asDict:function(){return{'modules_to_load':this.modulesToLoad.map(function(m){return m.asDict();}),'function_name':this.functionName,'options':this.options_};},asUserFriendlyString:function(){var parts=this.modulesToLoad.map(function(mtl){return mtl.filename});parts.push(this.functionName);parts.push(JSON.stringify(this.options_));return parts.join(',');},hasHrefs:function(){for(var module in this.modulesToLoad){if(this.modulesToLoad[module].href!==undefined){return true;}}
+ModuleToLoad.prototype={asDict:function(){if(this.href!==undefined){return{'href':this.href};}
+return{'filename':this.filename};},toString:function(){if(this.href!==undefined){return'ModuleToLoad(href="'+this.href+'")';}
+return'ModuleToLoad(filename="'+this.filename+'")';}};ModuleToLoad.fromDict=function(moduleDict){return new ModuleToLoad(moduleDict.href,moduleDict.filename);};function FunctionHandle(modulesToLoad,functionName,opt_options){if(!(modulesToLoad instanceof Array)){throw new Error('modulesToLoad in FunctionHandle must be an array');}
+if(typeof(functionName)!=='string'){throw new Error('functionName in FunctionHandle must be a string');}
+this.modulesToLoad=modulesToLoad;this.functionName=functionName;this.options_=opt_options;}
+FunctionHandle.prototype={get options(){return this.options_;},asDict:function(){return{'modules_to_load':this.modulesToLoad.map(function(m){return m.asDict();}),'function_name':this.functionName,'options':this.options_};},asUserFriendlyString:function(){var parts=this.modulesToLoad.map(function(mtl){return mtl.filename;});parts.push(this.functionName);parts.push(JSON.stringify(this.options_));return parts.join(',');},hasHrefs:function(){for(var module in this.modulesToLoad){if(this.modulesToLoad[module].href!==undefined){return true;}}
 return false;},load:function(){if(this.hasHrefs()){var err=new Error('FunctionHandle named '+this.functionName+' specifies hrefs, which cannot be loaded.');err.name='FunctionLoadingError';throw err;}
 for(var module in this.modulesToLoad){var filename=this.modulesToLoad[module].filename;try{HTMLImportsLoader.loadHTMLFile(filename);}catch(err){err.name='FunctionLoadingError';throw err;}}
 var func=FunctionRegistry.getFunction(this.functionName);if(func===undefined){var err=new Error('No registered function named '+this.functionName);err.name='FunctionNotDefinedError';throw err;}
 return func;},toString:function(){var modulesToLoadStr=this.modulesToLoad.map(function(module){return module.toString();});return'FunctionHandle(modulesToLoad=['+modulesToLoadStr+'], '+'functionName="'+this.functionName+'", options="'+
 JSON.stringify(this.options_)+'")';}};FunctionHandle.loadFromFilename_=function(filename){try{var numFunctionsBefore=FunctionRegistry.allFunctions.length;HTMLImportsLoader.loadHTMLFile(filename);}catch(err){err.name='FunctionLoadingError';throw err;}
-var numFunctionsNow=FunctionRegistry.allFunctions.length;if(numFunctionsNow!==(numFunctionsBefore+1)){var err=new Error(filename+" didn't call FunctionRegistry.register");err.name='FunctionNotDefinedError';throw err;}
+var numFunctionsNow=FunctionRegistry.allFunctions.length;if(numFunctionsNow!==(numFunctionsBefore+1)){var err=new Error(filename+' didn\'t call FunctionRegistry.register');err.name='FunctionNotDefinedError';throw err;}
 return FunctionRegistry.allFunctions[numFunctionsNow-1];};FunctionHandle.fromDict=function(handleDict){var options=handleDict.options;if(handleDict.modules_to_load!==undefined){var modulesToLoad=handleDict.modules_to_load.map(function(module){return ModuleToLoad.fromDict(module);});}
-return new FunctionHandle(modulesToLoad,handleDict.function_name,options);};return{FunctionHandle:FunctionHandle,ModuleToLoad:ModuleToLoad,FunctionRegistry:FunctionRegistry};});'use strict';tr.exportTo('tr.metrics',function(){function runMetrics(model,options){if(options===undefined)
-throw new Error('Options are required.');var metricNames=options.metrics;if(!metricNames)
-throw new Error('Metric names should be specified.');var values=new tr.v.ValueSet();for(var metricName of metricNames){var metric=tr.metrics.MetricRegistry.findTypeInfoWithName(metricName);if(metric===undefined)
-throw new Error('"'+metricName+'" is not a registered metric.');metric.constructor(values,model,options);}
-return values;}
-function addIterationInfo(values,model){var iterationInfo=new tr.v.d.IterationInfo();for(var metadata of model.metadata){if(!metadata.value)
-continue;if(metadata.value['iteration-info'])
-iterationInfo.addInfo(metadata.value['iteration-info']);if(metadata.value['os-version'])
-iterationInfo.addInfo(metadata.value);}
-values.map(v=>iterationInfo.addToValue(v));}
-function metricMapFunction(result,model,options){var values=runMetrics(model,options);addIterationInfo(values,model);result.addPair('histograms',values.valueDicts);var scalarDicts=[];for(var value of values){for(var[statName,scalar]of value.statisticsScalars){scalarDicts.push({name:value.name+'_'+statName,numeric:scalar.asDict(),description:value.description,});}}
+return new FunctionHandle(modulesToLoad,handleDict.function_name,options);};return{FunctionHandle,ModuleToLoad,FunctionRegistry,};});'use strict';tr.exportTo('tr.metrics',function(){function runMetrics(model,options){if(options===undefined){throw new Error('Options are required.');}
+var metricNames=options.metrics;if(!metricNames){throw new Error('Metric names should be specified.');}
+var histograms=new tr.v.HistogramSet();for(var metricName of metricNames){var metric=tr.metrics.MetricRegistry.findTypeInfoWithName(metricName);if(metric===undefined){throw new Error('"'+metricName+'" is not a registered metric.');}
+metric.constructor(histograms,model,options);}
+return histograms;}
+function addTelemetryInfo(histograms,model){var telemetry=new tr.v.d.TelemetryInfo();for(var metadata of model.metadata){if(!metadata.value)continue;if(metadata.value.telemetry){telemetry.addInfo(metadata.value.telemetry);}}
+histograms.addSharedDiagnostic(tr.v.d.TelemetryInfo.NAME,telemetry);}
+function metricMapFunction(result,model,options){var histograms=runMetrics(model,options);addTelemetryInfo(histograms,model);result.addPair('histograms',histograms.asDicts());var scalarDicts=[];for(var value of histograms){for(var[statName,scalar]of value.statisticsScalars){scalarDicts.push({name:value.name+'_'+statName,numeric:scalar.asDict(),description:value.description,});}}
 result.addPair('scalars',scalarDicts);}
-tr.mre.FunctionRegistry.register(metricMapFunction);return{metricMapFunction:metricMapFunction,runMetrics:runMetrics};});'use strict';tr.exportTo('tr.mre',function(){function Failure(job,functionHandleString,traceCanonicalUrl,failureTypeName,description,stack){this.job=job;this.functionHandleString=functionHandleString;this.traceCanonicalUrl=traceCanonicalUrl;this.failureTypeName=failureTypeName;this.description=description;this.stack=stack;}
-Failure.prototype={asDict:function(){return{function_handle_string:this.functionHandleString,trace_canonical_url:this.traceCanonicalUrl,type:this.failureTypeName,description:this.description,stack:this.stack};}};Failure.fromDict=function(failureDict){return new Failure(undefined,failureDict.function_handle_string,failureDict.trace_canonical_url,failureDict.type,failureDict.description,failureDict.stack);};return{Failure:Failure};});'use strict';tr.exportTo('tr.mre',function(){class MreResult{constructor(failures,pairs){if(failures===undefined)
-failures=[];if(pairs===undefined)
-pairs={};this.failures=failures;this.pairs=pairs;}
+tr.mre.FunctionRegistry.register(metricMapFunction);return{metricMapFunction,runMetrics,};});'use strict';tr.exportTo('tr.mre',function(){function Failure(job,functionHandleString,traceCanonicalUrl,failureTypeName,description,stack){this.job=job;this.functionHandleString=functionHandleString;this.traceCanonicalUrl=traceCanonicalUrl;this.failureTypeName=failureTypeName;this.description=description;this.stack=stack;}
+Failure.prototype={asDict:function(){return{function_handle_string:this.functionHandleString,trace_canonical_url:this.traceCanonicalUrl,type:this.failureTypeName,description:this.description,stack:this.stack};}};Failure.fromDict=function(failureDict){return new Failure(undefined,failureDict.function_handle_string,failureDict.trace_canonical_url,failureDict.type,failureDict.description,failureDict.stack);};return{Failure,};});'use strict';tr.exportTo('tr.mre',function(){class MreResult{constructor(failures,pairs){if(failures===undefined){failures=[];}
+if(pairs===undefined){pairs={};}
+this.failures=failures;this.pairs=pairs;}
 addFailure(failure){this.failures.push(failure);}
-addPair(key,value){if(key in this.pairs)
-throw new Error('Key '+key+' already exists in result.');this.pairs[key]=value;}
-asDict(){var d={pairs:this.pairs};if(this.failures)
-d.failures=this.failures.map(function(f){return f.asDict();});return d;}
+addPair(key,value){if(key in this.pairs){throw new Error('Key '+key+' already exists in result.');}
+this.pairs[key]=value;}
+asDict(){var d={pairs:this.pairs};if(this.failures){d.failures=this.failures.map(function(f){return f.asDict();});}
+return d;}
 hadFailures(){return this.failures.length>0;}
-static fromDict(resultDict){if(resultDict.failures!==undefined)
-var failures=resultDict.failures.map(tr.mre.Failure.fromDict);var pairs=resultDict.pairs;return new MreResult(failures,pairs);}};return{MreResult:MreResult};});'use strict';tr.exportTo('tr.ui.b',function(){var ColorScheme=tr.b.ColorScheme;var ChartBase2DBrushX=tr.ui.b.ChartBase2DBrushX;function getSVGTextWidth(parentNode,text){var textNode=document.createElementNS('http://www.w3.org/2000/svg','text');textNode.setAttributeNS(null,'x',0);textNode.setAttributeNS(null,'y',0);textNode.setAttributeNS(null,'fill','black');textNode.appendChild(document.createTextNode(text));parentNode.appendChild(textNode);var widthPx=textNode.getComputedTextLength();parentNode.removeChild(textNode);return widthPx;}
-var ColumnChart=tr.ui.b.define('column-chart',ChartBase2DBrushX);ColumnChart.prototype={__proto__:ChartBase2DBrushX.prototype,decorate:function(){ChartBase2DBrushX.prototype.decorate.call(this);Polymer.dom(this).classList.add('column-chart');this.xCushion_=1;this.isStacked_=false;},set isStacked(stacked){this.isStacked_=true;this.updateContents_();},get isStacked(){return this.isStacked_;},isDatumFieldSeries_:function(fieldName){return fieldName!='x';},getXForDatum_:function(datum,index){return datum.x;},updateScales_:function(){if(this.data_.length===0)
-return;var xDifferences=0;var currentX=undefined;var previousX=undefined;var yRange=new tr.b.Range();this.data_.forEach(function(datum,index){previousX=currentX;currentX=this.getXForDatum_(datum,index);if(previousX!==undefined){xDifferences+=currentX-previousX;}
-for(var[key,series]of this.seriesByKey_){if(datum[key]!==undefined)
-yRange.addValue(datum[key]);}},this);var width=this.chartAreaSize.width;this.xScale_.range([0,width]);var domain=d3.extent(this.data_,this.getXForDatum_.bind(this));if(this.data_.length>1)
-this.xCushion_=xDifferences/(this.data_.length-1);this.xScale_.domain([domain[0],domain[1]+this.xCushion_]);this.yScale_.range([this.chartAreaSize.height,0]);this.yScale_.domain(this.getYScaleDomain_(yRange.min,yRange.max));},getYScaleDomain_:function(minValue,maxValue){if(!this.isStacked){return ChartBase2DBrushX.prototype.getYScaleDomain_.call(this,minValue,maxValue);}
-var range=new tr.b.Range();range.addValue(0);this.data_.forEach(function(datum,index){var sum=0;for(var[key,series]of this.seriesByKey_){if(datum[key]===undefined)
-continue;sum+=datum[key];}
-range.addValue(sum);},this);return[range.min,range.max];},getStackedRectsForDatum_:function(datum,index){var stacks=[];var bottom=this.yScale_.range()[0];var sum=0;for(var[key,series]of this.seriesByKey_){if(datum[key]===undefined||!this.isSeriesEnabled(key))
-continue;sum+=datum[key];var heightPx=bottom-this.yScale_(sum);bottom-=heightPx;stacks.push({key:key,value:datum[key],color:this.getDataSeries(key).color,heightPx:heightPx,topPx:bottom});}
-return stacks;},getRectsForDatum_:function(datum,index){if(this.isStacked)
-return this.getStackedRectsForDatum_(datum,index);var stacks=[];for(var[key,series]of this.seriesByKey_){if(datum[key]===undefined||!this.isSeriesEnabled(key))
-continue;var topPx=this.yScale_(Math.max(datum[key],this.getYScaleMin_()));stacks.push({key:key,value:datum[key],topPx:topPx,heightPx:this.yScale_.range()[0]-topPx,color:this.getDataSeries(key).color});}
-stacks.sort(function(a,b){return b.topPx-a.topPx;});return stacks;},drawHoverValueBox_:function(rect){var seriesKeys=[...this.seriesByKey_.keys()];var chartAreaSel=d3.select(this.chartAreaElement);chartAreaSel.selectAll('.hover').remove();var keyWidthPx=0;var keyHeightPx=0;if(seriesKeys.length>1){keyWidthPx=getSVGTextWidth(this.chartAreaElement,rect.key)+5;keyHeightPx=16;}
-var valueWidthPx=getSVGTextWidth(this.chartAreaElement,rect.value)+5;var valueHeightPx=16;var hoverLeftPx=rect.leftPx+(rect.widthPx/2);chartAreaSel.append('rect').attr('class','hover').attr('fill','white').attr('x',hoverLeftPx).attr('y',rect.topPx).attr('width',Math.max(keyWidthPx,valueWidthPx)).attr('height',keyHeightPx+valueHeightPx);if(seriesKeys.length>1){chartAreaSel.append('text').attr('class','hover').attr('fill',rect.color).attr('x',hoverLeftPx+2).attr('y',rect.topPx+keyHeightPx-3).text(rect.key);}
-chartAreaSel.append('text').attr('class','hover').attr('fill',rect.color).attr('x',hoverLeftPx+2).attr('y',rect.topPx+keyHeightPx+valueHeightPx-3).text(rect.value);},clearHoverValueBox_:function(){d3.select(this.chartAreaElement).selectAll('.hover').remove();},drawRect_:function(rect,sel){sel.append('rect').attr('fill',rect.color).attr('x',rect.leftPx).attr('y',rect.topPx).attr('width',rect.widthPx).attr('height',rect.heightPx).on('mouseenter',this.drawHoverValueBox_.bind(this,rect)).on('mouseleave',this.clearHoverValueBox_.bind(this));},updateDataContents_:function(dataSel){dataSel.selectAll('*').remove();var chartAreaSel=d3.select(this.chartAreaElement);var seriesKeys=[...this.seriesByKey_.keys()];var rectsSel=dataSel.selectAll('path').data(seriesKeys);this.data_.forEach(function(datum,index){var currentX=this.getXForDatum_(datum,index);var width=undefined;if(index<(this.data_.length-1)){var nextX=this.getXForDatum_(this.data_[index+1],index+1);width=nextX-currentX;}else{width=this.xCushion_;}
-for(var rect of this.getRectsForDatum_(datum,index)){rect.leftPx=this.xScale_(currentX);rect.rightPx=this.xScale_(currentX+width);rect.widthPx=rect.rightPx-rect.leftPx;this.drawRect_(rect,rectsSel.enter());}},this);rectsSel.exit().remove();}};return{ColumnChart:ColumnChart,getSVGTextWidth:getSVGTextWidth};});'use strict';Polymer({is:'tr-v-ui-breakdown-span',created:function(){this.diagnostic_=undefined;this.chart_=new tr.ui.b.ColumnChart();this.chart_.width=190;this.chart_.height=200;this.chart_.isStacked=true;this.chart_.hideXAxis=true;this.chart_.margin.top=10;this.chart_.margin.bottom=10;this.chart_.margin.right=120;this.minHeightPx_=40;},ready:function(){Polymer.dom(this.$.container).appendChild(this.chart_);this.$.drag_handle.target=this.$.container;this.$.drag_handle.addEventListener('drag-handle-resize',this.onResize_.bind(this));},onResize_:function(event){event.stopPropagation();var heightPx=parseInt(this.$.container.style.height);if(heightPx<this.minHeightPx_){heightPx=this.minHeightPx_;this.$.container.style.height=this.minHeightPx_+'px';}
-this.chart_.height=heightPx;},get diagnostic(){return this.diagnostic_;},set diagnostic(d){this.diagnostic_=d;this.updateContents_();},updateContents_:function(){if(!this.diagnostic_){this.chart_.data=[];return;}
-var data={x:0};this.minHeightPx_=20;for(var[name,value]of this.diagnostic){this.minHeightPx_+=20;var dataSeries=this.chart_.getDataSeries(name);dataSeries.optional=true;if(this.diagnostic.colorScheme===tr.v.d.COLOR_SCHEME_CHROME_USER_FRIENDLY_CATEGORY_DRIVER){var cat=name.split(' ');cat=cat[cat.length-1];var color=tr.e.chrome.ChromeUserFriendlyCategoryDriver.getColor(cat);var hsl=color.toHSL();hsl.l*=0.85;var highlightedColor=tr.b.Color.fromHSL(hsl);dataSeries.highlightedColor=highlightedColor;dataSeries.color=color;}
-if(value instanceof tr.v.Histogram){dataSeries.target=value;data[name]=value.sum;}else if(typeof value==='number'){data[name]=value;}else{throw new Error('unsupported value '+value);}}
-this.chart_.data=[data];}});'use strict';Polymer({is:'tr-v-ui-generic-diagnostic-span',ready:function(){this.diagnostic_=undefined;},get diagnostic(){return this.diagnostic_;},set diagnostic(d){this.diagnostic_=d;this.updateContents_();},updateContents_:function(){if(this.diagnostic===undefined){this.$.generic.object=undefined;return;}
-this.$.generic.object=this.diagnostic.value;}});'use strict';Polymer({is:'tr-v-ui-iteration-info-span',ready:function(){this.diagnostic_=undefined;this.$.table.showHeader=false;this.$.table.tableColumns=[{value:function(row){return row[0];},},{value:function(row){return row[1];}}];},get diagnostic(){return this.diagnostic_;},set diagnostic(d){this.diagnostic_=d;this.updateContents_();},updateContents_:function(){if(this.diagnostic===undefined){this.$.table.tableRows=[];return;}
-var rows=[['benchmark name',this.diagnostic.benchmarkName],['benchmark start',this.diagnostic.benchmarkStartString],['url',this.diagnostic.storyUrl],['story',this.diagnostic.storyDisplayName],['storyset repeat',this.diagnostic.storysetRepeatCounter],['story repeat',this.diagnostic.storyRepeatCounter],];if(this.diagnostic.label)
-rows.push(['label',this.diagnostic.label]);if(this.diagnostic.storyGroupingKeys&&(tr.b.dictionaryLength(this.diagnostic.storyGroupingKeys)>0)){var gov=document.createElement('tr-ui-a-generic-object-view');gov.object=this.diagnostic.storyGroupingKeys;rows.push(['grouping keys',gov]);}
-rows.sort((x,y)=>x[0].localeCompare(y[0]));this.$.table.tableRows=rows;}});'use strict';Polymer({is:'tr-v-ui-related-event-set-span',ready:function(){this.diagnostic_=undefined;},get diagnostic(){return this.diagnostic_;},set diagnostic(d){this.diagnostic_=d;this.updateContents_();},updateContents_:function(){Polymer.dom(this).textContent='';var events=new tr.model.EventSet([...this.diagnostic]);var link=document.createElement('tr-ui-a-analysis-link');var label=events.length+' events';if(events.length===1){var event=tr.b.getOnlyElement(events);label=event.title+' ';label+=tr.b.Unit.byName.timeDurationInMs.format(event.duration);}
-link.setSelectionAndContent(events,label);Polymer.dom(this).appendChild(link);}});'use strict';Polymer({is:'tr-v-ui-related-value-map-span',ready:function(){this.diagnostic_=undefined;},get diagnostic(){return this.diagnostic_;},set diagnostic(d){this.diagnostic_=d;this.updateContents_();},updateContents_:function(){Polymer.dom(this).textContent='';for(var[name,value]of this.diagnostic){var link=document.createElement('tr-ui-a-analysis-link');link.setSelectionAndContent(value,name);Polymer.dom(this).appendChild(link);Polymer.dom(this).appendChild(document.createElement('br'));}}});'use strict';Polymer({is:'tr-v-ui-related-value-set-span',ready:function(){this.diagnostic_=undefined;},get diagnostic(){return this.diagnostic_;},set diagnostic(d){this.diagnostic_=d;this.updateContents_();},updateContents_:function(){Polymer.dom(this).textContent='';for(var value of this.diagnostic){var link=document.createElement('tr-ui-a-analysis-link');link.setSelectionAndContent(value,value.name);Polymer.dom(this).appendChild(link);Polymer.dom(this).appendChild(document.createElement('br'));}}});'use strict';tr.exportTo('tr.v.ui',function(){function findElementNameForDiagnostic(diagnostic){var typeInfo=undefined;var curProto=diagnostic.constructor.prototype;while(curProto){typeInfo=tr.v.d.Diagnostic.findTypeInfo(curProto.constructor);if(typeInfo&&typeInfo.metadata.elementName)
-break;typeInfo=undefined;curProto=curProto.__proto__;}
-if(typeInfo===undefined){throw new Error(diagnostic.constructor.name+' or a base class must have a registered elementName');}
-var tagName=typeInfo.metadata.elementName;if(tr.ui.b.isUnknownElementName(tagName))
-throw new Error('Element not registered: '+tagName);return tagName;}
-function createDiagnosticSpan(diagnostic){var tagName=findElementNameForDiagnostic(diagnostic);var span=document.createElement(tagName);span.diagnostic=diagnostic;return span;}
-return{createDiagnosticSpan:createDiagnosticSpan};});'use strict';Polymer({is:'tr-v-ui-diagnostic-map-table',created:function(){this.diagnosticMaps_=undefined;},set diagnosticMaps(maps){this.diagnosticMaps_=maps;this.updateContents_();},updateContents_:function(){if(this.diagnosticMaps_===undefined||this.diagnosticMaps_.length===0){this.$.table.tableRows=[];this.$.table.tableColumns=[];return;}
-var columnTitles=new Set();for(var map of this.diagnosticMaps_)
-for(var[name,diagnostic]of map)
-columnTitles.add(name);columnTitles=[...columnTitles].sort();var columns=[];function makeColumn(title){return{title:title,value:function(map){return tr.v.ui.createDiagnosticSpan(map.get(title));}};}
-for(var title of columnTitles)
-columns.push(makeColumn(title));this.$.table.tableColumns=columns;this.$.table.tableRows=this.diagnosticMaps_;this.$.table.rebuild();}});'use strict';Polymer({is:'tr-v-ui-numeric-stats-span',ready:function(){this.numeric_=undefined;this.$.stats.showHeader=false;this.$.stats.tableColumns=[{value:function(row){return row.name;}},{align:tr.ui.b.TableFormat.ColumnAlignment.RIGHT,value:function(row){return tr.v.ui.createScalarSpan(row.value);}}];},get numeric(){return this.numeric_;},set numeric(n){this.numeric_=n;this.updateContents_();},updateContents_:function(){var rows=[];if(this.numeric_){for(var[statName,scalar]of this.numeric_.statisticsScalars){rows.push({name:statName,value:scalar});}}
-this.$.stats.tableRows=rows;this.$.stats.rebuild();}});'use strict';Polymer({is:'tr-v-ui-histogram-span',created:function(){this.histogram_=undefined;this.chart_=new tr.ui.b.ColumnChart();this.chart_.width=400;this.chart_.height=200;this.chart_.margin.right=2;this.mouseDownBin_=undefined;this.brushedBins_=[];this.chart_.addEventListener('item-mousedown',this.onMouseDown_.bind(this));this.chart_.addEventListener('item-mousemove',this.onMouseMove_.bind(this));this.chart_.addEventListener('item-mouseup',this.onMouseUp_.bind(this));this.chart_.hideLegend=true;this.chart_.margin.right=0;},ready:function(){Polymer.dom(this.$.chart).appendChild(this.chart_);},get brushedBins(){return this.brushedBins_;},updateBrushedRange_:function(currentX){this.brushedBins_=[this.histogram_.getBinForValue(currentX)];var r=new tr.b.Range();r.addValue(this.mouseDownX_);r.addValue(currentX);var centralMin=Number.MAX_VALUE;var centralMax=-Number.MAX_VALUE;this.histogram_.centralBins.forEach(function(bin){centralMin=Math.min(centralMin,bin.range.min);centralMax=Math.max(centralMax,bin.range.max);if((bin.range.max>r.min)&&(bin.range.min<r.max)&&(this.brushedBins_.indexOf(bin)<0))
-this.brushedBins_.push(bin);},this);if((this.histogram_.underflowBin.range.max>r.min)&&(this.brushedBins_.indexOf(this.histogram_.underflowBin)<0)){this.brushedBins_.push(this.histogram_.underflowBin);}
-if((this.histogram_.overflowBin.range.min<r.max)&&(this.brushedBins_.indexOf(this.histogram_.overflowBin)<0)){this.brushedBins_.push(this.histogram_.overflowBin);}
-this.brushedBins_.sort(function(a,b){return a.range.min-b.range.min;});var minBin=this.histogram_.getBinForValue(r.min);var maxBin=this.histogram_.getBinForValue(r.max);var binWidth=this.histogram_.centralBins[0].range.range;r.min=minBin?Math.max(centralMin-binWidth,minBin.range.min):centralMin-binWidth;r.max=maxBin?Math.min(centralMax+binWidth,maxBin.range.max):centralMax+binWidth;this.chart_.brushedRange=r;},onMouseDown_:function(chartEvent){chartEvent.stopPropagation();if(!this.histogram_)
-return;this.mouseDownX_=chartEvent.x;this.updateBrushedRange_(chartEvent.x);},onMouseMove_:function(chartEvent){chartEvent.stopPropagation();if(!this.histogram_)
-return;this.updateBrushedRange_(chartEvent.x);},onMouseUp_:function(chartEvent){chartEvent.stopPropagation();if(!this.histogram_)
-return;this.updateBrushedRange_(chartEvent.x);this.updateDiagnostics_(this.brushedBins);this.mouseDownX_=undefined;},updateDiagnostics_:function(bins){var maps=[];for(var bin of bins)
-for(var map of bin.diagnosticMaps)
-maps.push(map);if(maps.length===0){this.$.diagnostic_map_table.style.display='none';return;}
-this.$.diagnostic_map_table.diagnosticMaps=maps;this.$.diagnostic_map_table.style.display='block';},get histogram(){return this.histogram_;},set histogram(histogram){this.histogram_=histogram;this.updateContents_();},set isYLogScale(logScale){this.chart_.isYLogScale=logScale;},updateContents_:function(){this.$.chart.style.display=this.histogram_?'':'none';this.$.diagnostic_map_table.style.display='none';this.$.stats.numeric=this.histogram_;this.brushedBins_=[];if(!this.histogram_)
-return;if(this.histogram_.numValues<=1){this.$.container.style.display='none';}else{this.$.container.style.display='';var maximumBinValue=tr.b.Statistics.max(this.histogram_.allBins,(bin)=>bin.count);var chartData=[];var binWidth=this.histogram_.centralBins[0].range.range;this.histogram_.allBins.forEach(function(bin){var x=bin.range.min;if(x===-Number.MAX_VALUE){if(!bin.count)
-return;x=bin.range.max-binWidth;}
-chartData.push({x:x,y:bin.count});});chartData.sort((x,y)=>x.x-y.x);this.chart_.data=chartData;this.chart_.brushedRange=new tr.b.Range();}
-var occupiedBins=[];for(var bin of this.histogram.allBins){if(bin.count>0)
-occupiedBins.push(bin);}
-if(occupiedBins.length===1)
-this.updateDiagnostics_(occupiedBins);}});'use strict';tr.exportTo('tr.ui',function(){function makeStoryGroupingKeyLabelGetter(storyGroupingKey){return v=>tr.v.d.IterationInfo.getStoryGroupingKeyLabel(v,storyGroupingKey);}
-var getDisplayLabel=tr.v.ValueSet.GROUPINGS.DISPLAY_LABEL.dataFn;var DEFAULT_POSSIBLE_GROUPS=[];DEFAULT_POSSIBLE_GROUPS.push({key:tr.v.ValueSet.GROUPINGS.HISTOGRAM_NAME.key,label:tr.v.ValueSet.GROUPINGS.HISTOGRAM_NAME.label,dataFn:v=>v.shortName||v.name});tr.b.iterItems(tr.v.ValueSet.GROUPINGS,function(name,group){if(group!==tr.v.ValueSet.GROUPINGS.DISPLAY_LABEL&&group!==tr.v.ValueSet.GROUPINGS.HISTOGRAM_NAME)
-DEFAULT_POSSIBLE_GROUPS.push(group);});var SELECTED_VALUE_SETTINGS_KEY='tr-v-ui-value-set-table-value';var SHOW_ALL_SETTINGS_KEY='tr-v-ui-value-set-table-show-all';var UNMERGEABLE='(unmergeable)';function mergeCells(a,b){if(a===UNMERGEABLE||b===UNMERGEABLE||!a||!b||!a.canAddHistogram(b))
-return UNMERGEABLE;a=a.clone();a.addHistogram(b);return a;}
-function organizeValues(values,groupingCallbacks,level){if(groupingCallbacks.length===level){return values.reduce(mergeCells);}
-var groupedValues=tr.b.group(values,groupingCallbacks[level]);if(level>0&&level<(groupingCallbacks.length-1)&&tr.b.dictionaryLength(groupedValues)===1){return organizeValues(values,groupingCallbacks,level+1);}
-return tr.b.mapItems(groupedValues,(key,groupValues)=>organizeValues(groupValues,groupingCallbacks,level+1));}
-Polymer({is:'tr-v-ui-value-set-table',get tabLabel(){return'Table';},created:function(){this.values_=undefined;this.sourceValues_=[];this.rows_=undefined;this.columns_=undefined;this.updatingContents_=false;this.displayLabels_=undefined;this.referenceDisplayLabel_=undefined;},ready:function(){this.$.table.selectionMode=tr.ui.b.TableFormat.SelectionMode.CELL;this.$.table.addEventListener('selection-changed',this.onSelectionChanged_.bind(this));this.addEventListener('requestSelectionChange',this.onRelatedValueSelected_.bind(this));this.$.show_all.checked=tr.b.Settings.get(SHOW_ALL_SETTINGS_KEY,false);this.$.picker.settingsKey='tr-v-ui-value-set-table-groupby-picker';this.$.picker.possibleGroups=DEFAULT_POSSIBLE_GROUPS.slice();this.$.picker.defaultGroupKeys=[tr.v.ValueSet.GROUPINGS.HISTOGRAM_NAME.key,tr.v.ValueSet.GROUPINGS.STORY_NAME.key];this.$.picker.addEventListener('current-groups-changed',this.currentGroupsChanged_.bind(this));},set groupingKeys(keys){this.$.picker.currentGroupKeys=keys;},get groupingKeys(){return this.$.picker.currentGroupKeys;},get possibleGroupingKeys(){return this.$.picker.possibleGroups.map(g=>g.key);},currentGroupsChanged_:function(){if(this.updatingContents_)
-return;if(this.$.picker.currentGroups.length===0&&this.possibleGroupingKeys.length>0){this.$.picker.currentGroupKeys=[this.$.picker.possibleGroups[0].key];}
-var expansionStates=undefined;if(this.rows_)
-expansionStates=this.getExpansionStates_(this.rows_);this.updateContents_();if(expansionStates)
-this.setExpansionStates_(expansionStates,this.rows_);},onShowAllChange_:function(){if(this.updatingContents_)
-return;tr.b.Settings.set(SHOW_ALL_SETTINGS_KEY,this.$.show_all.checked);var expansionStates=this.getExpansionStates_(this.rows_);this.updateContents_();this.setExpansionStates_(expansionStates,this.rows_);},getExpansionStates_:function(rows){var states={};for(var i=0;i<rows.length;++i){var row=rows[i];if(row.subRows&&row.subRows.length&&this.$.table.getExpandedForTableRow(row)){states[i]=this.getExpansionStates_(row.subRows);}}
-return states;},setExpansionStates_:function(states,rows){for(var i=0;i<rows.length;++i){if(states[i]&&rows[i]&&rows[i].subRows&&rows[i].subRows.length>0){this.$.table.setExpandedForTableRow(rows[i],true);this.setExpansionStates_(states[i],rows[i].subRows);}}},onSearch_:function(){this.updateContents_();},rowMatchesSearch_:function(row){return row.name.indexOf(this.$.search.value)>=0;},onRelatedValueSelected_:function(event){var value=event.selection;if(!(value instanceof tr.v.Histogram))
-return;event.stopPropagation();var displayLabel=getDisplayLabel(value);var columnIndex=-1;for(var i=0;i<this.columns_.length;++i){if(this.columns_[i].title===displayLabel){columnIndex=i;break;}}
-if(columnIndex<0)
-return;var hierarchy=[];var found=false;function search(row){if(row.columns[displayLabel]===value){for(var hirow in hierarchy){this.$.table.setExpandedForTableRow(hirow,true);}
-found=true;this.$.table.selectedTableRow=row;this.$.table.selectedColumnIndex=columnIndex;return;}
-if(!row.subRows)
-return;hierarchy.push(row);row.subRows.forEach(search,this);hierarchy.pop(row);}
-this.rows_.forEach(search,this);if(found||this.$.show_all.checked)
-return;for(var test of this.values){if(this.sourceValues_.indexOf(test)>=0)
-continue;if(test===value){found=true;this.$.show_all.checked=true;this.onShowAllChange_();this.onRelatedValueSelected_(event);break;}}},onSelectionChanged_:function(){var row=this.$.table.selectedTableRow;var col=this.$.table.selectedColumnIndex;var cell=undefined;if(row&&col&&this.columns_)
-cell=row.columns[this.columns_[col].title];if(cell instanceof tr.v.Histogram){this.$.histogram.style.display='block';this.$.histogram.histogram=cell;tr.b.Settings.set(SELECTED_VALUE_SETTINGS_KEY,JSON.stringify({row:row.name,column:this.columns_[col].title}));}else{this.$.histogram.style.display='none';}},addDiagnosticSubRows_:function(value,row,column){for(var[name,diagnostic]of value.diagnostics){var foundSubRow=false;for(var subRow of row.subRows){if(subRow.name===name){foundSubRow=true;subRow.columns[column]=diagnostic;continue;}}
-if(foundSubRow)
-continue;var subRow={name:name,columns:{}};subRow.columns[column]=diagnostic;row.subRows.push(subRow);}},get values(){return this.values_;},set values(values){this.values_=values;this.sourceValues_=values?values.sourceValues:[];this.displayLabels_=undefined;this.referenceDisplayLabel_='';this.updateContents_();},get referenceDisplayLabel(){return this.referenceDisplayLabel_;},set referenceDisplayLabel(reference){this.referenceDisplayLabel_=reference;if(this.updatingContents_)
-return;this.$.table.selectedTableColumnIndex=this.referenceDisplayLabel?1+this.displayLabels.indexOf(this.referenceDisplayLabel):undefined;var expansionStates=this.getExpansionStates_(this.rows_);this.$.table.tableRows=this.rows_;this.setExpansionStates_(expansionStates,this.rows_);},updateReferenceColumnSelector_:function(){Polymer.dom(this.$.reference_column_container).textContent='';if(this.displayLabels.length<2)
-return;var options=[{value:'',label:'Select a reference column'}];for(var displayLabel of this.displayLabels)
-options.push({value:displayLabel,label:displayLabel});var settingsKey='tr-v-ui-value-set-table-reference-display-label';Polymer.dom(this.$.reference_column_container).appendChild(tr.ui.b.createSelector(this,'referenceDisplayLabel',settingsKey,'',options));},updateGroups_:function(){var groups=DEFAULT_POSSIBLE_GROUPS.filter(function(group){if(group.key===tr.v.ValueSet.GROUPINGS.HISTOGRAM_NAME.key)
-return true;var values=new Set();for(var value of this.values_){value=group.dataFn(value);if(!value)
-continue;values.add(value);if(values.size>1)
+static fromDict(resultDict){if(resultDict.failures!==undefined){var failures=resultDict.failures.map(tr.mre.Failure.fromDict);}
+var pairs=resultDict.pairs;return new MreResult(failures,pairs);}}
+return{MreResult,};});'use strict';tr.exportTo('tr.ui',function(){class NullBrushingStateController extends tr.c.BrushingStateController{constructor(){super(undefined);this.parentController=undefined;}
+dispatchChangeEvent_(){if(this.parentController)this.parentController.dispatchChangeEvent_();}
+get model(){if(!this.parentController)return undefined;return this.parentController.model;}
+get trackView(){if(!this.parentController)return undefined;return this.parentController.trackView;}
+get viewport(){if(!this.parentController)return undefined;return this.parentController.viewport;}
+get historyEnabled(){if(!this.parentController)return undefined;return this.parentController.historyEnabled;}
+set historyEnabled(historyEnabled){if(this.parentController){this.parentController.historyEnabled=historyEnabled;}}
+modelWillChange(){if(this.parentController)this.parentController.modelWillChange();}
+modelDidChange(){if(this.parentController)this.parentController.modelDidChange();}
+onUserInitiatedSelectionChange_(){if(this.parentController){this.parentController.onUserInitiatedSelectionChange_();}}
+onPopState_(e){if(this.parentController)this.parentController.onPopState_(e);}
+get selection(){if(!this.parentController)return undefined;return this.parentController.selection;}
+get findMatches(){if(!this.parentController)return undefined;return this.parentController.findMatches;}
+get selectionOfInterest(){if(!this.parentController)return undefined;return this.parentController.selectionOfInterest;}
+get currentBrushingState(){if(!this.parentController)return undefined;return this.parentController.currentBrushingState;}
+set currentBrushingState(newBrushingState){if(this.parentController){this.parentController.currentBrushingState=newBrushingState;}}
+addAllEventsMatchingFilterToSelectionAsTask(filter,selection){if(this.parentController){this.parentController.addAllEventsMatchingFilterToSelectionAsTask(filter,selection);}}
+findTextChangedTo(allPossibleMatches){if(this.parentController){this.parentController.findTextChangedTo(allPossibleMatches);}}
+findFocusChangedTo(currentFocus){if(this.parentController){this.parentController.findFocusChangedTo(currentFocus);}}
+findTextCleared(){if(this.parentController){this.parentController.findTextCleared();}}
+uiStateFromString(string){if(this.parentController){this.parentController.uiStateFromString(string);}}
+navToPosition(uiState,showNavLine){if(this.parentController){this.parentController.navToPosition(uiState,showNavLine);}}
+changeSelectionFromTimeline(selection){if(this.parentController){this.parentController.changeSelectionFromTimeline(selection);}}
+showScriptControlSelection(selection){if(this.parentController){this.parentController.showScriptControlSelection(selection);}}
+changeSelectionFromRequestSelectionChangeEvent(selection){if(this.parentController){this.parentController.changeSelectionFromRequestSelectionChangeEvent(selection);}}
+changeAnalysisViewRelatedEvents(eventSet){if(this.parentController&&(eventSet instanceof tr.model.EventSet)){this.parentController.changeAnalysisViewRelatedEvents(eventSet);}}
+changeAnalysisLinkHoveredEvents(eventSet){if(this.parentController&&(eventSet instanceof tr.model.EventSet)){this.parentController.changeAnalysisLinkHoveredEvents(eventSet);}}
+getViewSpecificBrushingState(viewId){if(this.parentController){this.parentController.getViewSpecificBrushingState(viewId);}}
+changeViewSpecificBrushingState(viewId,newState){if(this.parentController){this.parentController.changeViewSpecificBrushingState(viewId,newState);}}}
+return{NullBrushingStateController,};});'use strict';tr.exportTo('tr.v',function(){const CSV_ITERATION_INFO_NAMES=['benchmarkName','benchmarkStartString','label','osVersion','productVersion','storyDisplayName','storysetRepeatCounter',];class CSVBuilder{constructor(histograms){this.histograms_=histograms;this.table_=[];this.statisticsNames_=new Set();this.iterationInfoNames_=new Set();this.storyGroupingKeys_=new Set();}
+build(){this.prepare_();this.buildHeader_();for(let hist of this.histograms_){let row=[hist.name,hist.unit.unitString];this.table_.push(row);let stats=hist.statisticsScalars;for(let name of this.statisticsNames_){row.push(stats.has(name)?stats.get(name).value:'');}
+let iteration=tr.v.d.TelemetryInfo.getFromHistogram(hist);for(let name of this.iterationInfoNames_){if(iteration===undefined||iteration[name]===undefined){row.push('');}else{row.push(iteration[name]);}}
+for(let key of this.storyGroupingKeys_){if(iteration===undefined||iteration.storyGroupingKeys.get(key)===undefined){row.push('');}else{row.push(iteration.storyGroupingKeys.get(key));}}}}
+prepare_(){for(let hist of this.histograms_){for(const name of hist.statisticsNames){this.statisticsNames_.add(name);}
+let iteration=tr.v.d.TelemetryInfo.getFromHistogram(hist);if(iteration===undefined)continue;for(let name of CSV_ITERATION_INFO_NAMES){if(iteration[name]){this.iterationInfoNames_.add(name);}}
+for(let[key,value]of iteration.storyGroupingKeys){this.storyGroupingKeys_.add(key);}}}
+buildHeader_(){let header=['name','unit'];for(let name of this.statisticsNames_){header.push(name);}
+for(let name of this.iterationInfoNames_){header.push(name);}
+for(let key of this.storyGroupingKeys_){header.push(key);}
+this.table_.push(header);}
+toString(){let str='';for(let row of this.table_){for(let i=0;i<row.length;++i){if(i>0){str+=',';}
+let cell=''+row[i];if(cell.indexOf(',')>=0||cell.indexOf('"')>=0){cell='"'+cell.replace(/"/g,'""')+'"';}
+str+=cell;}
+str+='\n';}
+return str;}}
+return{CSVBuilder,};});'use strict';tr.exportTo('tr.ui',function(){Polymer({is:'tr-v-ui-histogram-set-table-cell',created(){this.histogram_=undefined;this.referenceHistogram_=undefined;this.histogramSpan_=undefined;this.overviewChart_=undefined;this.row_=undefined;this.displayStatistic_='avg';},get displayStatistic(){return this.displayStatistic_;},set displayStatistic(statName){if(statName===this.displayStatistic)return;this.displayStatistic_=statName;this.updateContents_();},ready(){this.addEventListener('click',this.onClick_.bind(this));},onClick_(event){event.stopPropagation();},set row(row){this.row_=row;},get row(){return this.row_;},get histogram(){return this.histogram_;},set histogram(h){this.histogram_=h;this.updateContents_();},set referenceHistogram(rh){this.referenceHistogram_=rh;this.updateContents_();},get referenceHistogram(){return this.referenceHistogram_;},get isHistogramOpen(){return this.histogramSpan_&&(this.$.histogram.style.display==='block');},set isHistogramOpen(open){if(!(this.histogram instanceof tr.v.Histogram)||(this.histogram.numValues===0)){return;}
+this.$.scalar.style.display=open?'none':'flex';this.$.open_histogram.style.display=open?'none':'block';this.$.close_histogram.style.display=open?'block':'none';this.$.histogram.style.display=open?'block':'none';this.row.nameCell.onHistogramOpenChange();if(open&&this.histogramSpan_===undefined){this.histogramSpan_=document.createElement('tr-v-ui-histogram-span');this.$.histogram.appendChild(this.histogramSpan_);this.histogramSpan_.referenceHistogram=this.referenceHistogram;this.histogramSpan_.histogram=this.histogram;}},openHistogram_(){this.isHistogramOpen=true;},closeHistogram_(){this.isHistogramOpen=false;},updateContents_(){let isOpen=this.isHistogramOpen;this.$.empty.style.display='none';this.$.unmergeable.style.display='none';this.$.scalar.style.display='none';this.$.histogram.style.display='none';this.$.close_histogram.style.display='none';this.$.open_histogram.style.visibility='hidden';if(!this.histogram){this.$.missing.style.display='block';return;}
+this.$.missing.style.display='none';if(this.histogram===tr.v.ui.UNMERGEABLE){this.$.unmergeable.style.display='block';return;}
+if(!(this.histogram instanceof tr.v.Histogram)){throw new Error('Invalid Histogram: '+this.histogram);}
+if(this.histogram.numValues===0){this.$.empty.style.display='block';return;}
+this.$.open_histogram.style.display='block';this.$.open_histogram.style.visibility='visible';this.$.scalar.style.display='flex';if((this.referenceHistogram instanceof tr.v.Histogram)&&(this.histogram.unit===this.referenceHistogram.unit)&&(this.referenceHistogram.numValues>0)){this.$.scalar.significance=this.histogram.getDifferenceSignificance(this.referenceHistogram);}
+const statName=this.histogram.getAvailableStatisticName(this.displayStatistic,this.referenceHistogram);const statisticScalar=this.histogram.getStatisticScalar(statName,this.referenceHistogram);this.$.scalar.setValueAndUnit(statisticScalar.value,statisticScalar.unit);this.isHistogramOpen=isOpen;},showOverview(){this.$.overview_container.style.display='block';if(this.overviewChart_!==undefined)return;let displayLabel;for(let[label,hist]of this.row.columns){if(hist===this.histogram){displayLabel=label;}}
+let data=[];let unitString;for(let subRow of this.row.subRows){let subHist=subRow.columns.get(displayLabel);if(subHist){data.push({x:subRow.name,y:subHist.average});unitString=subHist.unit.unitString;}}
+if(data.length<2)return;this.overviewChart_=new tr.ui.b.NameLineChart();this.$.overview_container.appendChild(this.overviewChart_);this.overviewChart_.hideLegend=true;this.overviewChart_.yAxisLabel=unitString;this.overviewChart_.overrideDataRange=this.row.overviewDataRange;this.overviewChart_.data=data;},hideOverview(){this.$.overview_container.style.display='none';}});return{};});'use strict';tr.exportTo('tr.v.ui',function(){const NAME_COLUMN_WIDTH_PX=300;Polymer({is:'tr-v-ui-histogram-set-table-name-cell',created(){this.row_=undefined;this.overviewChart_=undefined;},attached(){if(this.isOverflowing){this.dispatchEvent(new tr.b.Event('name-cell-overflow'));}},get row(){return this.row_;},set row(row){this.row_=row;Polymer.dom(this.$.name).textContent=this.row.name;this.title=this.row.name;if(this.row.description){this.title+='\n'+this.row.description;}
+let histogramCount=0;for(const cell of this.row.cells.values()){if(cell.histogram instanceof tr.v.Histogram&&cell.histogram.numValues>0){++histogramCount;}}
+if(histogramCount<=1){this.$.close_histograms.style.display='none';this.$.open_histograms.style.display='none';}},set constrainWidth(constrain){this.$.name.style.maxWidth=constrain?(this.nameWidthPx+'px'):'none';},get nameWidthPx(){return NAME_COLUMN_WIDTH_PX-(16*this.row.depth);},get isOverflowing(){return this.$.name.style.maxWidth!=='none'&&this.$.name.getBoundingClientRect().width===this.nameWidthPx;},hideOverview_(opt_event){if(opt_event){opt_event.stopPropagation();}
+this.$.overview_container.style.display='none';this.$.hide_overview.style.display='none';this.$.show_overview.style.display='block';for(let[displayLabel,cell]of this.row.cells){cell.hideOverview();}},showOverview_(opt_event){if(opt_event){opt_event.preventDefault();opt_event.stopPropagation();}
+this.$.overview_container.style.display='block';if(this.overviewChart_===undefined){let data=[];let unitString=undefined;for(let[displayLabel,hist]of this.row.columns){data.push({x:displayLabel,y:hist.average});unitString=hist.unit.unitString;}
+if(data.length<2){return;}
+this.overviewChart_=new tr.ui.b.NameLineChart();this.$.overview_container.appendChild(this.overviewChart_);this.overviewChart_.hideLegend=true;this.overviewChart_.yAxisLabel=unitString;this.overviewChart_.overrideDataRange=this.row.overviewDataRange;this.overviewChart_.data=data;}
+this.$.hide_overview.style.display='block';this.$.show_overview.style.display='none';for(let cell of this.row.cells.values()){cell.showOverview();}},openHistograms_(){for(let cell of this.row.cells.values()){cell.isHistogramOpen=true;}
+this.$.close_histograms.style.display='block';this.$.open_histograms.style.display='none';},closeHistograms_(){for(let cell of this.row.cells.values()){cell.isHistogramOpen=false;}
+this.$.open_histograms.style.display='block';this.$.close_histograms.style.display='none';},onHistogramOpenChange(){let cellCount=0;let openCellCount=0;for(let cell of this.row.cells.values()){if(!(cell.histogram instanceof tr.v.Histogram)||(cell.histogram.numValues===0)){continue;}
+++cellCount;if(cell.isHistogramOpen)++openCellCount;}
+if(cellCount<=1)return;const mostlyOpen=openCellCount>(cellCount/2);this.$.open_histograms.style.display=mostlyOpen?'none':'block';this.$.close_histograms.style.display=mostlyOpen?'block':'none';}});return{};});'use strict';tr.exportTo('tr.v.ui',function(){class HistogramSetTableRow{constructor(name){this.name=name;this.description='';this.depth=0;this.subRows=[];this.columns=new Map();this.nameCell_=undefined;this.cells=new Map();this.constrainNameColumnWidth_=false;this.overviewDataRange_=undefined;this.displayStatistic_='avg';this.doMergeRelationshipsForColumn_=new Map();}
+static filter(rows,histograms){let results=[];for(let row of rows){let filteredSubRows=[];if(row.subRows.length>0){filteredSubRows=HistogramSetTableRow.filter(row.subRows,histograms);if(filteredSubRows.length===0)continue;}else{let found=false;for(let testHist of row.columns.values()){if(!(testHist instanceof tr.v.Histogram))continue;if(histograms.lookupHistogram(testHist.guid)!==undefined){found=true;break;}
+let mergedFrom=testHist.diagnostics.get(tr.v.d.MERGED_FROM_DIAGNOSTIC_KEY);if(mergedFrom!==undefined){for(let origHist of mergedFrom){if(histograms.lookupHistogram(origHist.guid)!==undefined){found=true;break;}}}
+if(found)break;}
+if(!found)continue;}
+let clone=new HistogramSetTableRow(row.name);clone.description=row.description;clone.depth=row.depth;clone.subRows=filteredSubRows;clone.columns=row.columns;clone.nameCell_=row.nameCell_;clone.cells=row.cells;clone.constrainNameColumnWidth_=row.constrainNameColumnWidth_;clone.overviewDataRange_=row.overviewDataRange_;clone.displayStatistic_=row.displayStatistic_;results.push(clone);}
+return results;}
+static build(histogramArrayMap){const rootRows=[];HistogramSetTableRow.buildInternal_(histogramArrayMap,[],rootRows);const histograms=new tr.v.HistogramSet();for(const row of HistogramSetTableRow.walkAll(rootRows)){for(const hist of row.columns.values()){if(!(hist instanceof tr.v.Histogram))continue;histograms.addHistogram(hist);}}
+histograms.deduplicateDiagnostics();for(const row of HistogramSetTableRow.walkAll(rootRows)){for(const[name,hist]of row.columns){if(!(hist instanceof tr.v.Histogram))continue;if(!row.doMergeRelationshipsForColumn_.get(name))continue;hist.diagnostics.mergeRelationships(hist);}}
+for(const row of HistogramSetTableRow.walkAll(rootRows)){if(row.subRows.length)continue;for(const hist of row.columns.values()){if(!(hist instanceof tr.v.Histogram))continue;const mergedFrom=hist.diagnostics.get(tr.v.MERGED_FROM_DIAGNOSTIC_KEY);if(mergedFrom!==undefined){for(const other of mergedFrom){other.diagnostics.delete(tr.v.MERGED_TO_DIAGNOSTIC_KEY);}}}}
+for(const row of HistogramSetTableRow.walkAll(rootRows)){row.maybeRebin_();}
+return rootRows;}*walk(){yield this;for(const row of this.subRows)yield*row.walk();}
+static*walkAll(rootRows){for(const rootRow of rootRows)yield*rootRow.walk();}
+maybeRebin_(){const dataRange=new tr.b.math.Range();for(const hist of this.columns.values()){if(!(hist instanceof tr.v.Histogram))continue;if(hist.allBins.length>1)return;if(hist.numValues===0)continue;dataRange.addValue(hist.min);dataRange.addValue(hist.max);}
+dataRange.addValue(tr.b.math.lesserWholeNumber(dataRange.min));dataRange.addValue(tr.b.math.greaterWholeNumber(dataRange.max));if(dataRange.min===dataRange.max)return;const boundaries=tr.v.HistogramBinBoundaries.createLinear(dataRange.min,dataRange.max,tr.v.DEFAULT_REBINNED_COUNT);for(const[name,hist]of this.columns){if(!(hist instanceof tr.v.Histogram))continue;this.columns.set(name,hist.rebin(boundaries));}}
+static mergeHistogramDownHierarchy_(histogram,hierarchy,columnName){let groupingPath=undefined;for(let row of hierarchy){if(groupingPath!==undefined){groupingPath.push(row.name);}else if(row.name===histogram.name){groupingPath=[];}
+if(!row.description){row.description=histogram.description;}
+if(row.columns.get(columnName)===undefined){let clone=histogram.clone();if(groupingPath!==undefined){new tr.v.d.GroupingPath(groupingPath).addToHistogram(clone);}
+row.columns.set(columnName,clone);row.doMergeRelationshipsForColumn_.set(columnName,true);continue;}
+if(!(row.columns.get(columnName)instanceof tr.v.Histogram))continue;if(!row.columns.get(columnName).canAddHistogram(histogram)){row.columns.set(columnName,tr.v.ui.UNMERGEABLE);continue;}
+let merged=row.columns.get(columnName);if(merged.name!==histogram.name){row.doMergeRelationshipsForColumn_.set(name,false);}
+merged.addHistogram(histogram);}}
+static buildInternal_(histogramArrayMap,hierarchy,rootRows){for(let[name,histograms]of histogramArrayMap){if(histograms instanceof Array){for(let histogram of histograms){HistogramSetTableRow.mergeHistogramDownHierarchy_(histogram,hierarchy,name);}}else if(histograms instanceof Map){let row=new HistogramSetTableRow(name);row.depth=hierarchy.length;hierarchy.push(row);HistogramSetTableRow.buildInternal_(histograms,hierarchy,rootRows);hierarchy.pop();if(hierarchy.length===0){rootRows.push(row);}else{hierarchy[hierarchy.length-1].subRows.push(row);}}}}
+get nameCell(){if(this.nameCell_===undefined){this.nameCell_=document.createElement('tr-v-ui-histogram-set-table-name-cell');this.nameCell_.row=this;this.nameCell_.constrainWidth=this.constrainNameColumnWidth_;}
+return this.nameCell_;}
+set constrainNameColumnWidth(constrain){for(const row of this.walk()){row.constrainNameColumnWidth_=constrain;if(row.nameCell_!==undefined){row.nameCell_.constrainWidth=constrain;}}}
+get isNameCellOverflowing(){for(const row of this.walk()){if(row.nameCell.isOverflowing)return true;}
+return false;}
+get displayStatistic(){return this.displayStatistic_;}
+set displayStatistic(statName){for(const row of this.walk()){row.displayStatistic_=statName;for(let[displayLabel,cell]of row.cells){cell.displayStatistic=statName;}}}
+buildCell(displayLabel,referenceDisplayLabel){let cell=document.createElement('tr-v-ui-histogram-set-table-cell');cell.row=this;cell.histogram=this.columns.get(displayLabel);cell.displayStatistic=this.displayStatistic;if(referenceDisplayLabel&&referenceDisplayLabel!==displayLabel){cell.referenceHistogram=this.columns.get(referenceDisplayLabel);}
+this.cells.set(displayLabel,cell);return cell;}
+get overviewDataRange(){if(this.overviewDataRange_===undefined){this.overviewDataRange_=new tr.b.math.Range();for(let[displayLabel,hist]of this.columns){if(hist.average!==undefined){this.overviewDataRange_.addValue(hist.average);}
+for(let subRow of this.subRows){let subHist=subRow.columns.get(displayLabel);if(!(subHist instanceof tr.v.Histogram))continue;if(subHist.average===undefined)continue;this.overviewDataRange_.addValue(subHist.average);}}}
+return this.overviewDataRange_;}
+getLeafHistograms(histograms){for(const row of this.walk()){if(row.subRows.length)return;for(const hist of this.columns.values()){histograms.addHistogram(hist);}}}
+compareNames(other){return this.name.localeCompare(other.name);}
+compareCells(other,displayLabel,referenceDisplayLabel){let cellA=this.columns.get(displayLabel);let cellB=other.columns.get(displayLabel);if(!(cellA instanceof tr.v.Histogram)||!(cellB instanceof tr.v.Histogram)){return undefined;}
+let referenceCellA;let referenceCellB;if(referenceDisplayLabel&&referenceDisplayLabel!==displayLabel){referenceCellA=this.columns.get(referenceDisplayLabel);referenceCellB=other.columns.get(referenceDisplayLabel);}
+const statisticA=cellA.getAvailableStatisticName(this.displayStatistic,referenceCellA);const statisticB=cellB.getAvailableStatisticName(this.displayStatistic,referenceCellB);const valueA=cellA.getStatisticScalar(statisticA,referenceCellA).value;const valueB=cellB.getStatisticScalar(statisticB,referenceCellB).value;return valueA-valueB;}
+getExpansionStates(table){let states={expanded:table.getExpandedForTableRow(this),cells:new Map(),subRows:new Map(),};for(let[displayLabel,cell]of this.cells){if(cell.isHistogramOpen){states.cells.set(displayLabel,true);}}
+if(states.expanded){for(let i=0;i<this.subRows.length;++i){states.subRows.set(i,this.subRows[i].getExpansionStates(table));}}
+return states;}
+setExpansionStates(states,table){if(states.expanded){if(this.subRows.length){table.setExpandedForTableRow(this,true);for(let[index,subStates]of states.subRows){this.subRows[index].setExpansionStates(subStates,table);}}}
+for(let[displayLabel,isHistogramOpen]of states.cells){let cell=this.cells.get(displayLabel);if(cell){cell.isHistogramOpen=isHistogramOpen;}}}}
+return{HistogramSetTableRow,};});'use strict';tr.exportTo('tr.v.ui',function(){let getDisplayLabel=tr.v.HistogramSet.GROUPINGS.DISPLAY_LABEL.callback;const DEFAULT_POSSIBLE_GROUPS=[];DEFAULT_POSSIBLE_GROUPS.push(new tr.v.HistogramGrouping(tr.v.HistogramSet.GROUPINGS.HISTOGRAM_NAME.key,h=>h.shortName||h.name));for(var group of Object.values(tr.v.HistogramSet.GROUPINGS)){if(group!==tr.v.HistogramSet.GROUPINGS.DISPLAY_LABEL&&group!==tr.v.HistogramSet.GROUPINGS.HISTOGRAM_NAME){DEFAULT_POSSIBLE_GROUPS.push(group);}}
+const SHOW_ALL_SETTINGS_KEY='tr-v-ui-histogram-set-table-show-all';const CONSTRAIN_NAME_COLUMN_WIDTH_KEY='tr-v-ui-histogram-set-table-constrain-name-column-width';const DISPLAY_STATISTIC_KEY='tr-v-ui-histogram-set-table-statistic';const REFERENCE_DISPLAY_LABEL_KEY='tr-v-ui-histogram-set-table-reference-display-label';const UNMERGEABLE='(unmergeable)';const MIDLINE_HORIZONTAL_ELLIPSIS=String.fromCharCode(0x22ef);function escapeRegExp(str){return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,'\\$&');}
+Polymer({is:'tr-v-ui-histogram-set-table',get tabLabel(){return'Table';},created(){this.histograms_=undefined;this.sourceHistograms_=undefined;this.unfilteredRows_=undefined;this.rows_=undefined;this.columns_=undefined;this.updatingContents_=false;this.displayLabels_=undefined;this.displayStatistic_='avg';this.statNames_=undefined;this.referenceDisplayLabel_=undefined;this.constrainNameColumnWidth_=true;this.nameColumnTitle_=undefined;this.isDisplayed=false;},ready(){this.$.table.zebra=true;this.addEventListener('name-cell-overflow',this.onNameCellOverflow_.bind(this));this.addEventListener('requestSelectionChange',this.onRequestSelectionChange_.bind(this));this.$.show_all.checked=tr.b.Settings.get(SHOW_ALL_SETTINGS_KEY,false);this.$.picker.settingsKey='tr-v-ui-histogram-set-table-groupby-picker';this.$.picker.possibleGroups=DEFAULT_POSSIBLE_GROUPS.slice();if(this.$.picker.currentGroupKeys.length===0){this.$.picker.currentGroupKeys=[tr.v.HistogramSet.GROUPINGS.HISTOGRAM_NAME.key,tr.v.HistogramSet.GROUPINGS.STORY_NAME.key];}
+this.$.picker.addEventListener('current-groups-changed',this.currentGroupsChanged_.bind(this));},set groupingKeys(keys){this.$.picker.currentGroupKeys=keys;},get groupingKeys(){return this.$.picker.currentGroupKeys;},get possibleGroupingKeys(){return this.$.picker.possibleGroups.map(g=>g.key);},currentGroupsChanged_(){if(this.updatingContents_)return;if(this.$.picker.currentGroups.length===0&&this.possibleGroupingKeys.length>0){this.$.picker.currentGroupKeys=[this.$.picker.possibleGroups[0].key];}
+this.unfilteredRows_=tr.v.ui.HistogramSetTableRow.build(this.groupedHistograms);let expansionStates=undefined;if(this.rows_)expansionStates=this.getExpansionStates_();this.updateContents_();if(expansionStates)this.setExpansionStates_(expansionStates);},onShowAllChange_(){if(this.updatingContents_)return;tr.b.Settings.set(SHOW_ALL_SETTINGS_KEY,this.$.show_all.checked);let expansionStates=this.getExpansionStates_();this.updateContents_();this.setExpansionStates_(expansionStates);},getExpansionStates_(){let states=new Map();for(let i=0;i<this.rows_.length;++i){states.set(i,this.rows_[i].getExpansionStates(this.$.table));}
+return states;},setExpansionStates_(states){for(let i=0;i<this.rows_.length;++i){let rowStates=states.get(i);if(rowStates===undefined){continue;}
+this.rows_[i].setExpansionStates(rowStates,this.$.table);}},showOverview_(){let table=this.$.table;function recurse(row){row.nameCell.showOverview_();if(table.getExpandedForTableRow(row)){for(let subrow of row.subRows){recurse(subrow);}}}
+for(let i=0;i<this.rows_.length;++i){recurse(this.rows_[i]);}
+this.$.hide_overview.style.display='inline';this.$.show_overview.style.display='none';},hideOverview_(){let table=this.$.table;function recurse(row){row.nameCell.hideOverview_();if(table.getExpandedForTableRow(row)){for(let subrow of row.subRows){recurse(subrow);}}}
+for(let i=0;i<this.rows_.length;++i){recurse(this.rows_[i]);}
+this.$.hide_overview.style.display='none';this.$.show_overview.style.display='inline';},onSearch_(){this.updateContents_();},onRequestSelectionChange_(event){if(event.selection instanceof tr.model.EventSet)return;event.stopPropagation();let histogramNames=event.selection;histogramNames.sort();histogramNames=histogramNames.map(escapeRegExp);this.$.search.value='^('+histogramNames.join('|')+')$';this.$.show_all.checked=true;this.onShowAllChange_();this.$.search.focus();},set helpHref(href){this.$.help.href=href;this.$.help.style.display='inline';},get histograms(){return this.histograms_;},set histograms(histograms){this.histograms_=histograms;this.displayLabels_=undefined;this.statNames_=undefined;this.referenceDisplayLabel_=undefined;if(this.histograms_===undefined){this.unfilteredRows_=[];this.sourceHistograms_=new tr.v.HistogramSet();}else{this.updatingContents_=true;this.updateGroups_();this.updatingContents_=false;this.unfilteredRows_=tr.v.ui.HistogramSetTableRow.build(this.groupedHistograms);this.sourceHistograms_=this.histograms_.sourceHistograms;}
+this.maybeDisableShowAll_();this.updateContents_();},get referenceDisplayLabel(){return this.referenceDisplayLabel_;},set referenceDisplayLabel(reference){if(reference===this.referenceDisplayLabel)return;let prevReferenceDisplayLabel=this.referenceDisplayLabel;this.referenceDisplayLabel_=reference;if(this.updatingContents_)return;let select=this.$.reference_column_container.children[0];if(select){select.value=reference?reference:'Select a reference column';}
+this.$.table.selectedTableColumnIndex=this.referenceDisplayLabel?1+this.displayLabels.indexOf(this.referenceDisplayLabel):undefined;let expansionStates=this.getExpansionStates_();this.$.table.tableRows=this.rows_;this.setExpansionStates_(expansionStates);this.updateStatisticSelector_();if(prevReferenceDisplayLabel===''&&this.referenceDisplayLabel){this.displayStatistic=tr.v.DELTA+this.displayStatistic;}},get statNames(){if(this.statNames_===undefined){this.statNames_=new Set(['avg']);for(let hist of this.histograms){for(let statName of hist.statisticsNames){this.statNames_.add(statName);}}}
+return this.statNames_;},updateStatisticSelector_(){Polymer.dom(this.$.statistic_container).textContent='';let statNames=Array.from(this.statNames);if(this.referenceDisplayLabel){statNames.push.apply(statNames,tr.v.Histogram.getDeltaStatisticsNames(statNames));}
+if(statNames.indexOf(this.displayStatistic_)<0){this.displayStatistic_=statNames[0];}
+let options=[];for(let statName of statNames){options.push({value:statName,label:statName});}
+let selector=tr.ui.b.createSelector(this,'displayStatistic',DISPLAY_STATISTIC_KEY,this.displayStatistic,options);Polymer.dom(this.$.statistic_container).appendChild(selector);},get displayStatistic(){return this.displayStatistic_;},set displayStatistic(statName){if(statName===this.displayStatistic_)return;this.displayStatistic_=statName;let select=this.$.statistic_container.children[0];if(select&&select.value!==statName){select.value=statName;}
+if(this.rows_!==undefined){for(let row of this.rows_){row.displayStatistic=this.displayStatistic;}}
+let sortColumnIndex=this.$.table.sortColumnIndex;this.sortColumnIndex=undefined;this.$.table.rebuild();this.sortColumnIndex=sortColumnIndex;this.$.table.rebuild();},updateReferenceColumnSelector_(){Polymer.dom(this.$.reference_column_container).textContent='';if(this.displayLabels.length<2)return;let options=[{value:'',label:'Select a reference column'}];for(let displayLabel of this.displayLabels){options.push({value:displayLabel,label:displayLabel});}
+let selector=tr.ui.b.createSelector(this,'referenceDisplayLabel',REFERENCE_DISPLAY_LABEL_KEY,'',options);Polymer.dom(this.$.reference_column_container).appendChild(selector);},set sortColumnIndex(i){this.$.table.sortColumnIndex=i;},get sortColumnIndex(){return this.$.table.sortColumnIndex;},set sortDescending(d){this.$.table.sortDescending=d;},get sortDescending(){return this.$.table.sortDescending;},updateGroups_(){let groups=DEFAULT_POSSIBLE_GROUPS.filter(function(group){if(group.key===tr.v.HistogramSet.GROUPINGS.HISTOGRAM_NAME.key){return true;}
+let values=new Set();for(let hist of this.histograms_){hist=group.callback(hist);if(!hist)continue;values.add(hist);if(values.size>1)return true;}
+return false;},this);for(let storyGroupingKey of this.storyGroupingKeys){groups.push(new tr.v.HistogramGrouping('storyGroupingKey_'+storyGroupingKey,tr.v.d.TelemetryInfo.makeStoryGroupingKeyLabelGetter(storyGroupingKey),storyGroupingKey));}
+let groupingKeys=this.groupingKeys;if(groupingKeys.length===0&&groups.length>0){groupingKeys=[groups[0].key];}
+this.$.picker.possibleGroups=groups;this.$.picker.currentGroupKeys=groupingKeys;this.$.picker.style.display=(groups.length===1)?'none':'';},updateContents_(){if(this.updatingContents_)return;if(!this.histograms_||(this.histograms_.length===0)){this.$.container.style.display='';this.$.zero.style.display='';return;}
+this.updatingContents_=true;this.$.zero.style.display='none';this.$.container.style.display='flex';this.$.table.style.display='';this.$.container.style.maxHeight=(window.innerHeight-16)+'px';this.updateReferenceColumnSelector_();this.updateStatisticSelector_();this.rows_=tr.v.ui.HistogramSetTableRow.filter(this.unfilteredRows_,this.filteredHistograms);this.buildColumns_();this.$.table.tableColumns=this.columns_;this.$.table.tableRows=this.rows_;this.$.table.sortColumnIndex=0;this.$.table.rebuild();for(let row of this.rows_){row.constrainNameColumnWidth=this.constrainNameColumnWidth;}
+this.checkNameColumnOverflow_();for(let row of this.rows_){row.displayStatistic=this.displayStatistic;}
+this.$.table.selectedTableColumnIndex=this.referenceDisplayLabel?1+this.displayLabels.indexOf(this.referenceDisplayLabel):undefined;this.updatingContents_=false;},maybeDisableShowAll_(){let allHistogramsAreSource=!this.histograms||(this.histograms.length===this.sourceHistograms_.length);this.$.show_all.disabled=allHistogramsAreSource;if(this.$.show_all.disabled){this.$.show_all.checked=true;}},get storyGroupingKeys(){let keys=new Set();for(let value of this.histograms){let telemetry=tr.v.d.TelemetryInfo.getFromHistogram(value);if(!(telemetry instanceof tr.v.d.TelemetryInfo))continue;for(let[key,value]of telemetry.storyGroupingKeys){keys.add(key);}}
+return[...keys.values()].sort();},get filteredHistograms(){let histograms=this.$.show_all.checked?this.histograms:this.sourceHistograms_;if(this.$.search.value){let query=undefined;try{query=new RegExp(this.$.search.value);}catch(e){}
+if(query!==undefined){histograms=new tr.v.HistogramSet([...histograms].filter(hist=>hist.name.match(query)));}}
+return histograms;},get groupedHistograms(){let groupings=this.$.picker.currentGroups.slice();groupings.push(tr.v.HistogramSet.GROUPINGS.DISPLAY_LABEL);function canSkipGrouping(grouping,groupedHistograms){if(groupedHistograms.size>1)return false;if(grouping.key===groupings[0].key)return false;if(grouping.key===tr.v.HistogramSet.GROUPINGS.DISPLAY_LABEL.key){return false;}
 return true;}
-return false;},this);for(var storyGroupingKey of this.storyGroupingKeys){groups.push({key:'storyGroupingKey_'+storyGroupingKey,label:storyGroupingKey,dataFn:makeStoryGroupingKeyLabelGetter(storyGroupingKey)});}
-var groupingKeys=this.groupingKeys;this.$.picker.possibleGroups=groups;this.$.picker.currentGroupKeys=groupingKeys;this.$.picker.style.display=(groups.length===1)?'none':'';},updateContents_:function(){if(this.updatingContents_)
-return;if(!this.values_||(this.values_.length===0)){this.$.container.style.display='';this.$.zero.style.display='';return;}
-this.updatingContents_=true;this.$.zero.style.display='none';this.$.container.style.display='block';this.$.table.style.display='';this.$.histogram.style.display='none';this.updateReferenceColumnSelector_();this.updateGroups_();this.buildRows_();this.buildColumns_();this.$.table.tableColumns=this.columns_;this.$.table.tableRows=this.rows_;this.$.table.sortColumnIndex=0;this.$.table.rebuild();this.selectValue_();this.maybeDisableShowAll_();this.$.table.selectedTableColumnIndex=this.referenceDisplayLabel?1+this.displayLabels.indexOf(this.referenceDisplayLabel):undefined;this.updatingContents_=false;},maybeDisableShowAll_:function(){var allValuesAreSource=true;for(var value of this.values){if(this.sourceValues_.indexOf(value)<0){allValuesAreSource=false;break;}}
-this.$.show_all.disabled=allValuesAreSource;if(this.$.show_all.disabled){this.$.show_all.checked=true;}},selectValue_:function(){var selectedValue=tr.b.Settings.get(SELECTED_VALUE_SETTINGS_KEY,undefined);if(selectedValue){selectedValue=JSON.parse(selectedValue);for(var row of this.rows_){if(row.name===selectedValue.row){for(var coli=1;coli<this.columns_.length;++coli){var column=this.columns_[coli];if(column.title===selectedValue.column){this.$.table.selectedTableRow=row;this.$.table.selectedColumnIndex=coli;return;}}}}}
-this.$.table.selectedTableRow=this.rows_[0];this.$.table.selectedColumnIndex=1;},buildRow_:function(organizedValues,hierarchy){tr.b.iterItems(organizedValues,function(name,value){if(value instanceof tr.v.Histogram){for(var row of hierarchy){if(row.description===undefined)
-row.description=value.description;if(row.columns[name])
-row.columns[name]=mergeCells(value,row.columns[name]);else
-row.columns[name]=value;}
-var row=hierarchy[hierarchy.length-1];if(row)
-this.addDiagnosticSubRows_(value,row,name);}else if(value===UNMERGEABLE){var row=hierarchy[hierarchy.length-1];if(row)
-row.columns[name]=value;}else{var row={name:name,subRows:[],columns:{}};hierarchy.push(row);this.buildRow_(value,hierarchy);hierarchy.pop();if(hierarchy.length===0)
-this.rows_.push(row);else
-hierarchy[hierarchy.length-1].subRows.push(row);}},this);},get storyGroupingKeys(){var keys=new Set();for(var value of this.values){var iteration=tr.v.d.IterationInfo.getFromValue(value);if(!(iteration instanceof tr.v.d.IterationInfo)||!iteration.storyGroupingKeys)
-continue;for(var key in iteration.storyGroupingKeys)
-keys.add(key);}
-return[...keys.values()].sort();},get organizedValues_(){var showingValues=this.$.show_all.checked?this.values:this.sourceValues_;var values=[];for(var value of showingValues)
-values.push(value);var groupingCallbacks=[];for(var group of this.$.picker.currentGroups)
-groupingCallbacks.push(group.dataFn);groupingCallbacks.push(getDisplayLabel);return organizeValues(values,groupingCallbacks,0);},buildRows_:function(){this.rows_=[];var hierarchy=[];var organizedValues=this.organizedValues_;this.buildRow_(organizedValues,hierarchy);this.rows_=this.rows_.filter(this.rowMatchesSearch_.bind(this));},get startTimesForDisplayLabels(){var startTimesForDisplayLabels={};for(var value of this.values){var displayLabel=getDisplayLabel(value);startTimesForDisplayLabels[displayLabel]=Math.min(startTimesForDisplayLabels[displayLabel]||0,tr.v.d.IterationInfo.getField(value,'benchmarkStart',new Date(0)).getTime());}
-return startTimesForDisplayLabels;},get displayLabels(){if(this.displayLabels_===undefined){var startTimesForDisplayLabels=this.startTimesForDisplayLabels;this.displayLabels_=Object.keys(startTimesForDisplayLabels);this.displayLabels_.sort(function(a,b){return startTimesForDisplayLabels[a]-startTimesForDisplayLabels[b];});}
-return this.displayLabels_;},buildColumn_:function(displayLabel){function getValueForValue(value){return value instanceof tr.v.Histogram?value.average:value.value;}
-return{title:displayLabel,align:tr.ui.b.TableFormat.ColumnAlignment.RIGHT,supportsCellSelection:true,value:function(row){var cell=row.columns[displayLabel];if(cell===undefined)
-return'(missing)';if(cell===UNMERGEABLE)
-return cell;if(cell instanceof tr.v.Histogram){if(cell.numValues===0){return'(empty)';}
-if(this.referenceDisplayLabel&&this.referenceDisplayLabel!==displayLabel){var referenceCell=row.columns[this.referenceDisplayLabel];if(referenceCell instanceof tr.v.Histogram&&cell.unit===referenceCell.unit&&referenceCell.numValues>0){var significance=cell.getDifferenceSignificance(referenceCell);return tr.v.ui.createScalarSpan(getValueForValue(cell)-getValueForValue(referenceCell),{unit:cell.unit.correspondingDeltaUnit,significance:significance});}}
-return tr.v.ui.createScalarSpan(cell);}
-if(cell instanceof tr.v.d.Diagnostic){var span=tr.v.ui.createDiagnosticSpan(cell);span.addEventListener('click',(event)=>event.stopPropagation());span.style.textAlign='left';return span;}
-throw new Error('Invalid cell',cell);}.bind(this),cmp:function(rowA,rowB){var cellA=rowA.columns[displayLabel];var cellB=rowB.columns[displayLabel];if(!(cellA instanceof tr.v.Histogram)||!(cellB instanceof tr.v.Histogram)){return undefined;}
-var valueA=getValueForValue(cellA);var valueB=getValueForValue(cellB);if(this.referenceDisplayLabel&&this.referenceDisplayLabel!==displayLabel){var referenceCellA=rowA.columns[this.referenceDisplayLabel];var referenceCellB=rowB.columns[this.referenceDisplayLabel];if(referenceCellA instanceof tr.v.Histogram&&referenceCellB instanceof tr.v.Histogram&&cellA.unit===referenceCellA.unit&&cellB.unit===referenceCellB.unit){valueA-=getValueForValue(referenceCellA);valueB-=getValueForValue(referenceCellB);}}
-return valueA-valueB;}.bind(this)};},buildColumns_:function(){this.columns_=[{title:'Name',align:tr.ui.b.TableFormat.ColumnAlignment.LEFT,supportsCellSelection:false,value:function(row){var nameEl=document.createElement('span');Polymer.dom(nameEl).textContent=row.name;if(row.description)
-nameEl.title=row.description;nameEl.style.textOverflow='ellipsis';return nameEl;},cmp:(a,b)=>a.name.localeCompare(b.name)}];for(var displayLabel of this.displayLabels)
-this.columns_.push(this.buildColumn_(displayLabel));}});return{};});'use strict';tr.exportTo('tr.v.ui',function(){function NullBrushingStateController(){this.parentController=undefined;}
-NullBrushingStateController.prototype={__proto__:tr.c.BrushingStateController.prototype,dispatchChangeEvent_:function(){if(this.parentController)
-this.parentController.dispatchChangeEvent_();},get model(){if(!this.parentController)
-return undefined;return this.parentController.model;},get trackView(){if(!this.parentController)
-return undefined;return this.parentController.trackView;},get viewport(){if(!this.parentController)
-return undefined;return this.parentController.viewport;},get historyEnabled(){if(!this.parentController)
-return undefined;return this.parentController.historyEnabled;},set historyEnabled(historyEnabled){if(this.parentController)
-this.parentController.historyEnabled=historyEnabled;},modelWillChange:function(){if(this.parentController)
-this.parentController.modelWillChange();},modelDidChange:function(){if(this.parentController)
-this.parentController.modelDidChange();},onUserInitiatedSelectionChange_:function(){if(this.parentController)
-this.parentController.onUserInitiatedSelectionChange_();},onPopState_:function(e){if(this.parentController)
-this.parentController.onPopState_(e);},get selection(){if(!this.parentController)
-return undefined;return this.parentController.selection;},get findMatches(){if(!this.parentController)
-return undefined;return this.parentController.findMatches;},get selectionOfInterest(){if(!this.parentController)
-return undefined;return this.parentController.selectionOfInterest;},get currentBrushingState(){if(!this.parentController)
-return undefined;return this.parentController.currentBrushingState;},set currentBrushingState(newBrushingState){if(this.parentController)
-this.parentController.currentBrushingState=newBrushingState;},addAllEventsMatchingFilterToSelectionAsTask:function(filter,selection){if(this.parentController){this.parentController.addAllEventsMatchingFilterToSelectionAsTask(filter,selection);}},findTextChangedTo:function(allPossibleMatches){if(this.parentController)
-this.parentController.findTextChangedTo(allPossibleMatches);},findFocusChangedTo:function(currentFocus){if(this.parentController)
-this.parentController.findFocusChangedTo(currentFocus);},findTextCleared:function(){if(this.parentController)
-this.parentController.findTextCleared();},uiStateFromString:function(string){if(this.parentController)
-this.parentController.uiStateFromString(string);},navToPosition:function(uiState,showNavLine){if(this.parentController)
-this.parentController.navToPosition(uiState,showNavLine);},changeSelectionFromTimeline:function(selection){if(this.parentController)
-this.parentController.changeSelectionFromTimeline(selection);},showScriptControlSelection:function(selection){if(this.parentController)
-this.parentController.showScriptControlSelection(selection);},changeSelectionFromRequestSelectionChangeEvent:function(selection){if(this.parentController){this.parentController.changeSelectionFromRequestSelectionChangeEvent(selection);}},changeAnalysisViewRelatedEvents:function(eventSet){if(this.parentController&&(eventSet instanceof tr.model.EventSet))
-this.parentController.changeAnalysisViewRelatedEvents(eventSet);},changeAnalysisLinkHoveredEvents:function(eventSet){if(this.parentController&&(eventSet instanceof tr.model.EventSet))
-this.parentController.changeAnalysisLinkHoveredEvents(eventSet);},getViewSpecificBrushingState:function(viewId){if(this.parentController)
-this.parentController.getViewSpecificBrushingState(viewId);},changeViewSpecificBrushingState:function(viewId,newState){if(this.parentController)
-this.parentController.changeViewSpecificBrushingState(viewId,newState);}};Polymer({is:'tr-v-ui-value-set-view',ready:function(){this.brushingStateController=new NullBrushingStateController();},attached:function(){this.brushingStateController.parentController=tr.c.BrushingStateController.getControllerForElement(this.parentNode);},set values(values){this.$.valueSetTable.values=values;}});return{};});'use strict';tr.exportTo('tr.ui',function(){Polymer({is:'tr-ui-sp-metrics-side-panel',behaviors:[tr.ui.behaviors.SidePanel],ready:function(){this.model_=undefined;this.rangeOfInterest_=undefined;this.metricLatenciesMs_=[];this.metrics_=[];tr.metrics.MetricRegistry.getAllRegisteredTypeInfos().forEach(function(m){if(m.constructor.name==='sampleMetric')
-return;this.metrics_.push({label:m.constructor.name,value:m.constructor.name});},this);this.settingsKey_='metrics-side-panel-metric-name';this.currentMetricName_='systemHealthMetrics';var metricSelector=tr.ui.b.createSelector(this,'currentMetricName_',this.settingsKey_,this.currentMetricName_,this.metrics_);Polymer.dom(this.$.top_left_controls).appendChild(metricSelector);metricSelector.addEventListener('change',this.onMetricChange_.bind(this));this.currentMetricTypeInfo_=tr.metrics.MetricRegistry.findTypeInfoWithName(this.currentMetricName_);this.recomputeButton_=tr.ui.b.createButton('Recompute',this.onRecompute_,this);Polymer.dom(this.$.top_left_controls).appendChild(this.recomputeButton_);},get metricLatencyMs(){return tr.b.Statistics.mean(this.metricLatenciesMs_);},onMetricChange_:function(){this.currentMetricTypeInfo_=tr.metrics.MetricRegistry.findTypeInfoWithName(this.currentMetricName_);this.metricLatenciesMs_=[];this.updateContents_();},onRecompute_:function(){this.updateContents_();},get textLabel(){return'Metrics';},supportsModel:function(m){if(!m){return{supported:false,reason:'No model available'};}
-return{supported:true};},get model(){return this.model_;},set model(model){this.model_=model;this.updateContents_();},get selection(){},set selection(_){},get rangeOfInterest(){return this.rangeOfInterest_;},set rangeOfInterest(range){this.rangeOfInterest_=range;if(this.currentMetricTypeInfo_&&this.currentMetricTypeInfo_.metadata.supportsRangeOfInterest){if((this.metricLatencyMs===undefined)||(this.metricLatencyMs<100)){this.updateContents_();}else{this.recomputeButton_.style.background='red';}}},updateContents_:function(){this.style.width='';Polymer.dom(this.$.error).textContent='';this.$.results.style.display='none';if(!this.model_){Polymer.dom(this.$.error).textContent='Missing model';return;}
-var options={metrics:[this.currentMetricName_]};if(this.currentMetricTypeInfo_&&this.currentMetricTypeInfo_.metadata.supportsRangeOfInterest&&this.rangeOfInterest&&!this.rangeOfInterest.isEmpty)
-options.rangeOfInterest=this.rangeOfInterest;var startDate=new Date();try{var values=tr.metrics.runMetrics(this.model_,options);}catch(err){console.warn(this.currentMetricName_+' could not be computed for the current model:\n'+
-err.stack);Polymer.dom(this.$.error).textContent=err.message;return;}
-this.metricLatenciesMs_.push(new Date()-startDate);while(this.metricLatenciesMs_.length>20)
-this.metricLatenciesMs_.shift();this.recomputeButton_.style.background='';this.$.results.style.display='';this.$.results.values=values;tr.b.requestAnimationFrame(function(){var width=this.$.results.getBoundingClientRect().width+15;this.style.width=width+'px';},this);}});tr.ui.side_panel.SidePanelRegistry.register(function(){return document.createElement('tr-ui-sp-metrics-side-panel');});return{};});'use strict';Polymer({is:'tr-ui-e-s-alerts-side-panel',behaviors:[tr.ui.behaviors.SidePanel],ready:function(){this.rangeOfInterest_=new tr.b.Range();this.selection_=undefined;},get model(){return this.model_;},set model(model){this.model_=model;this.updateContents_();},set selection(selection){},set rangeOfInterest(rangeOfInterest){},selectAlertsOfType:function(alertTypeString){var alertsOfType=this.model_.alerts.filter(function(alert){return alert.title===alertTypeString;});var event=new tr.model.RequestSelectionChangeEvent();event.selection=new tr.model.EventSet(alertsOfType);this.dispatchEvent(event);},alertsByType_:function(alerts){var alertsByType={};alerts.forEach(function(alert){if(!alertsByType[alert.title])
-alertsByType[alert.title]=[];alertsByType[alert.title].push(alert);});return alertsByType;},alertsTableRows_:function(alertsByType){return Object.keys(alertsByType).map(function(key){return{alertType:key,count:alertsByType[key].length};});},alertsTableColumns_:function(){return[{title:'Alert type',value:function(row){return row.alertType;},width:'180px'},{title:'Count',width:'100%',value:function(row){return row.count;}}];},createAlertsTable_:function(alerts){var alertsByType=this.alertsByType_(alerts);var table=document.createElement('tr-ui-b-table');table.tableColumns=this.alertsTableColumns_();table.tableRows=this.alertsTableRows_(alertsByType);table.selectionMode=tr.ui.b.TableFormat.SelectionMode.ROW;table.addEventListener('selection-changed',function(e){var row=table.selectedTableRow;if(row)
-this.selectAlertsOfType(row.alertType);}.bind(this));return table;},updateContents_:function(){Polymer.dom(this.$.result_area).textContent='';if(this.model_===undefined)
-return;var panel=this.createAlertsTable_(this.model_.alerts);Polymer.dom(this.$.result_area).appendChild(panel);},supportsModel:function(m){if(m==undefined){return{supported:false,reason:'Unknown tracing model'};}else if(m.alerts.length===0){return{supported:false,reason:'No alerts in tracing model'};}
+return this.histograms.groupHistogramsRecursively(groupings,canSkipGrouping);},get startTimesForDisplayLabels(){let startTimesForDisplayLabels={};for(let value of this.histograms){let displayLabel=getDisplayLabel(value);startTimesForDisplayLabels[displayLabel]=Math.min(startTimesForDisplayLabels[displayLabel]||0,tr.v.d.TelemetryInfo.getField(value,'benchmarkStart',new Date(0)).getTime());}
+return startTimesForDisplayLabels;},get displayLabels(){if(this.displayLabels_===undefined){let startTimesForDisplayLabels=this.startTimesForDisplayLabels;this.displayLabels_=Object.keys(startTimesForDisplayLabels);this.displayLabels_.sort(function(a,b){return startTimesForDisplayLabels[a]-startTimesForDisplayLabels[b];});}
+return this.displayLabels_;},buildColumn_(displayLabel){let title=displayLabel;if(displayLabel.indexOf('\n')>0){title=document.createElement('div');for(let line of displayLabel.split('\n')){let lineDiv=document.createElement('div');lineDiv.appendChild(document.createTextNode(line));title.appendChild(lineDiv);}}
+return{title:title,value:row=>row.buildCell(displayLabel,this.referenceDisplayLabel),cmp:(rowA,rowB)=>rowA.compareCells(rowB,displayLabel,this.referenceDisplayLabel),};},get nameColumnTitle(){if(this.nameColumnTitle_===undefined){this.nameColumnTitle_=document.createElement('span');this.nameColumnTitle_.style.display='inline-flex';let nameEl=document.createElement('span');nameEl.textContent='Name';this.nameColumnTitle_.appendChild(nameEl);let toggleWidthEl=document.createElement('span');toggleWidthEl.style.fontWeight='bold';toggleWidthEl.style.background='#bbb';toggleWidthEl.style.color='#333';toggleWidthEl.style.padding='0px 3px';toggleWidthEl.style.marginRight='8px';toggleWidthEl.style.display='none';toggleWidthEl.textContent=MIDLINE_HORIZONTAL_ELLIPSIS;toggleWidthEl.addEventListener('click',this.toggleNameColumnWidth_.bind(this));this.nameColumnTitle_.appendChild(toggleWidthEl);}
+return this.nameColumnTitle_;},onNameCellOverflow_(){this.nameColumnTitle.children[0].style.width='275px';this.nameColumnTitle.children[1].style.display='block';this.constrainNameColumnWidth=tr.b.Settings.get(CONSTRAIN_NAME_COLUMN_WIDTH_KEY,true);},checkNameColumnOverflow_(){if(this.nameColumnTitle_===undefined)return;this.nameColumnTitle.children[0].style.width='';this.nameColumnTitle.children[1].style.display='none';if(this.rows_===undefined)return;for(const row of this.rows_){if(row.isNameCellOverflowing){this.onNameCellOverflow_();return;}}},displayed(){this.isDisplayed=true;this.checkNameColumnOverflow_();},get constrainNameColumnWidth(){return this.constrainNameColumnWidth_;},set constrainNameColumnWidth(c){if(this.constrainNameColumnWidth!==!!c){this.toggleNameColumnWidth_();}},toggleNameColumnWidth_(opt_event){if(opt_event){opt_event.stopPropagation();opt_event.preventDefault();}
+this.constrainNameColumnWidth_=!this.constrainNameColumnWidth;tr.b.Settings.set(CONSTRAIN_NAME_COLUMN_WIDTH_KEY,this.constrainNameColumnWidth);for(let row of this.rows_){row.constrainNameColumnWidth=this.constrainNameColumnWidth;}
+this.nameColumnTitle.children[1].style.filter=this.constrainNameColumnWidth?'':'invert(100%)';},get leafHistograms(){let histograms=new tr.v.HistogramSet();for(let row of this.rows_){row.getLeafHistograms(histograms);}
+return histograms;},downloadCSV_(){let anchor=document.createElement('a');let path=window.location.pathname.split('/');let basename=path[path.length-1].split('.')[0]||'histograms';anchor.download=basename+'.csv';let csv=new tr.v.CSVBuilder(this.leafHistograms);csv.build();let blob=new window.Blob([csv.toString()],{type:'text/csv'});anchor.href=window.URL.createObjectURL(blob);anchor.click();},buildColumns_(){this.columns_=[{title:this.nameColumnTitle,value:row=>row.nameCell,cmp:(a,b)=>a.compareNames(b),}];for(let displayLabel of this.displayLabels){this.columns_.push(this.buildColumn_(displayLabel));}}});return{CONSTRAIN_NAME_COLUMN_WIDTH_KEY,DISPLAY_STATISTIC_KEY,MIDLINE_HORIZONTAL_ELLIPSIS,REFERENCE_DISPLAY_LABEL_KEY,SHOW_ALL_SETTINGS_KEY,UNMERGEABLE,};});'use strict';tr.exportTo('tr.v.ui',function(){Polymer({is:'tr-v-ui-histogram-set-view',ready(){this.brushingStateController=new tr.ui.NullBrushingStateController();},attached(){this.brushingStateController.parentController=tr.c.BrushingStateController.getControllerForElement(this.parentNode);},set helpHref(href){this.$.histograms.helpHref=href;},set histograms(histograms){this.$.histograms.histograms=histograms;},get histograms(){return this.$.histograms.histograms;},displayed(){this.$.histograms.displayed();},});return{};});'use strict';tr.exportTo('tr.ui',function(){Polymer({is:'tr-ui-sp-metrics-side-panel',behaviors:[tr.ui.behaviors.SidePanel],ready:function(){this.model_=undefined;this.rangeOfInterest_=undefined;this.metricLatenciesMs_=[];this.metrics_=[];tr.metrics.MetricRegistry.getAllRegisteredTypeInfos().forEach(function(m){if(m.constructor.name==='sampleMetric')return;this.metrics_.push({label:m.constructor.name,value:m.constructor.name});},this);this.settingsKey_='metrics-side-panel-metric-name';this.currentMetricName_='responsivenessMetric';var metricSelector=tr.ui.b.createSelector(this,'currentMetricName_',this.settingsKey_,this.currentMetricName_,this.metrics_);Polymer.dom(this.$.top_left_controls).appendChild(metricSelector);metricSelector.addEventListener('change',this.onMetricChange_.bind(this));this.currentMetricTypeInfo_=tr.metrics.MetricRegistry.findTypeInfoWithName(this.currentMetricName_);this.recomputeButton_=tr.ui.b.createButton('Recompute',this.onRecompute_,this);Polymer.dom(this.$.top_left_controls).appendChild(this.recomputeButton_);},get metricLatencyMs(){return tr.b.math.Statistics.mean(this.metricLatenciesMs_);},onMetricChange_:function(){this.currentMetricTypeInfo_=tr.metrics.MetricRegistry.findTypeInfoWithName(this.currentMetricName_);this.metricLatenciesMs_=[];this.updateContents_();},onRecompute_:function(){this.updateContents_();},get textLabel(){return'Metrics';},supportsModel:function(m){if(!m){return{supported:false,reason:'No model available'};}
+return{supported:true};},get model(){return this.model_;},set model(model){this.model_=model;this.updateContents_();},get selection(){},set selection(_){},get rangeOfInterest(){return this.rangeOfInterest_;},set rangeOfInterest(range){this.rangeOfInterest_=range;if(this.currentMetricTypeInfo_&&this.currentMetricTypeInfo_.metadata.supportsRangeOfInterest){if((this.metricLatencyMs===undefined)||(this.metricLatencyMs<100)){this.updateContents_();}else{this.recomputeButton_.style.background='red';}}},updateContents_:function(){Polymer.dom(this.$.error).textContent='';this.$.results.style.display='none';if(!this.model_){Polymer.dom(this.$.error).textContent='Missing model';return;}
+var options={metrics:[this.currentMetricName_]};if(this.currentMetricTypeInfo_&&this.currentMetricTypeInfo_.metadata.supportsRangeOfInterest&&this.rangeOfInterest&&!this.rangeOfInterest.isEmpty){options.rangeOfInterest=this.rangeOfInterest;}
+var startDate=new Date();try{var histograms=tr.metrics.runMetrics(this.model_,options);}catch(err){Polymer.dom(this.$.error).textContent=err.message;return;}
+this.metricLatenciesMs_.push(new Date()-startDate);while(this.metricLatenciesMs_.length>20){this.metricLatenciesMs_.shift();}
+this.recomputeButton_.style.background='';this.$.results.style.display='';this.$.results.histograms=histograms;}});tr.ui.side_panel.SidePanelRegistry.register(function(){return document.createElement('tr-ui-sp-metrics-side-panel');});return{};});'use strict';Polymer({is:'tr-ui-e-s-alerts-side-panel',behaviors:[tr.ui.behaviors.SidePanel],ready:function(){this.rangeOfInterest_=new tr.b.math.Range();this.selection_=undefined;},get model(){return this.model_;},set model(model){this.model_=model;this.updateContents_();},set selection(selection){},set rangeOfInterest(rangeOfInterest){},selectAlertsOfType:function(alertTypeString){var alertsOfType=this.model_.alerts.filter(function(alert){return alert.title===alertTypeString;});var event=new tr.model.RequestSelectionChangeEvent();event.selection=new tr.model.EventSet(alertsOfType);this.dispatchEvent(event);},alertsByType_:function(alerts){var alertsByType={};alerts.forEach(function(alert){if(!alertsByType[alert.title]){alertsByType[alert.title]=[];}
+alertsByType[alert.title].push(alert);});return alertsByType;},alertsTableRows_:function(alertsByType){return Object.keys(alertsByType).map(function(key){return{alertType:key,count:alertsByType[key].length};});},alertsTableColumns_:function(){return[{title:'Alert type',value:function(row){return row.alertType;},width:'180px'},{title:'Count',width:'100%',value:function(row){return row.count;}}];},createAlertsTable_:function(alerts){var alertsByType=this.alertsByType_(alerts);var table=document.createElement('tr-ui-b-table');table.tableColumns=this.alertsTableColumns_();table.tableRows=this.alertsTableRows_(alertsByType);table.selectionMode=tr.ui.b.TableFormat.SelectionMode.ROW;table.addEventListener('selection-changed',function(e){var row=table.selectedTableRow;if(row){this.selectAlertsOfType(row.alertType);}}.bind(this));return table;},updateContents_:function(){Polymer.dom(this.$.result_area).textContent='';if(this.model_===undefined)return;var panel=this.createAlertsTable_(this.model_.alerts);Polymer.dom(this.$.result_area).appendChild(panel);},supportsModel:function(m){if(m===undefined){return{supported:false,reason:'Unknown tracing model'};}else if(m.alerts.length===0){return{supported:false,reason:'No alerts in tracing model'};}
 return{supported:true};},get textLabel(){return'Alerts';}});tr.ui.side_panel.SidePanelRegistry.register(function(){return document.createElement('tr-ui-e-s-alerts-side-panel');});
 </script>
 <!--CATAPULT_REV=NO_AUTO_UPDATE-->
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/systrace/systrace/test_data/atrace_extracted_threads b/sdk/platform-tools/systrace/catapult/systrace/systrace/test_data/atrace_extracted_threads
index bff5d1b..b649fbf 100644
--- a/sdk/platform-tools/systrace/catapult/systrace/systrace/test_data/atrace_extracted_threads
+++ b/sdk/platform-tools/systrace/catapult/systrace/systrace/test_data/atrace_extracted_threads
@@ -1 +1 @@
-{1: '/init', 2: 'kthreadd', 3: 'ksoftirqd/0', 7: 'kworker/u:0H', 8: 'migration/0', 13: 'khelper', 14: 'netns', 17: 'kworker/0:1H', 18: 'modem_notifier', 19: 'smd_channel_clo', 20: 'smsm_cb_wq', 21: 'kworker/u:1', 22: 'rpm-smd', 23: 'kworker/u:1H', 24: 'irq/317-earjack', 25: 'sync_supers', 26: 'bdi-default', 27: 'kblockd', 28: 'vmalloc', 29: 'khubd', 30: 'irq/102-msm_iom', 31: 'irq/102-msm_iom', 32: 'irq/102-msm_iom', 33: 'irq/79-msm_iomm', 34: 'irq/78-msm_iomm', 35: 'irq/78-msm_iomm', 36: 'irq/74-msm_iomm', 37: 'irq/75-msm_iomm', 38: 'irq/75-msm_iomm', 39: 'irq/75-msm_iomm', 40: 'irq/75-msm_iomm', 41: 'irq/273-msm_iom', 42: 'irq/273-msm_iom', 43: 'irq/97-msm_iomm', 44: 'irq/97-msm_iomm', 45: 'irq/97-msm_iomm', 46: 'l2cap', 47: 'a2mp', 48: 'cfg80211', 49: 'qmi', 50: 'nmea', 51: 'msm_ipc_router', 52: 'apr_driver', 54: 'kswapd0', 55: 'fsnotify_mark', 56: 'cifsiod', 57: 'crypto', 75: 'ad_calc_wq', 76: 'hdmi_tx_workq', 77: 'anx7808_work', 78: 'k_hsuart', 79: 'diag_wq', 80: 'diag_cntl_wq', 81: 'diag_dci_wq', 82: 'kgsl-3d0', 84: 'f9966000.spi', 88: 'usbnet', 89: 'irq/329-anx7808', 90: 'k_rmnet_mux_wor', 91: 'f_mtp', 92: 'file-storage', 93: 'uether', 94: 'synaptics_wq', 95: 'irq/362-s3350', 96: 'kworker/0:2', 97: 'msm_vidc_worker', 98: 'msm_vidc_worker', 99: 'msm_cpp_workque', 100: 'irq/350-bq51013', 102: 'dm_bufio_cache', 103: 'dbs_sync/0', 104: 'dbs_sync/1', 105: 'dbs_sync/2', 106: 'dbs_sync/3', 107: 'cfinteractive', 108: 'irq/170-msm_sdc', 109: 'binder', 110: 'usb_bam_wq', 111: 'krfcommd', 112: 'bam_dmux_rx', 113: 'bam_dmux_tx', 114: 'rq_stats', 115: 'deferwq', 117: 'irq/361-MAX1704', 119: 'mmcqd/1', 120: 'mmcqd/1rpmb', 121: 'wl_event_handle', 122: 'dhd_watchdog_th', 123: 'dhd_dpc', 124: 'dhd_rxf', 125: 'dhd_sysioc', 126: 'vibrator', 127: 'max1462x', 128: 'irq/310-maxim_m', 129: 'irq/311-maxim_m', 130: '/sbin/ueventd', 132: 'jbd2/mmcblk0p25', 133: 'ext4-dio-unwrit', 136: 'flush-179:0', 138: 'jbd2/mmcblk0p28', 139: 'ext4-dio-unwrit', 143: 'jbd2/mmcblk0p27', 144: 'ext4-dio-unwrit', 145: 'jbd2/mmcblk0p16', 146: 'ext4-dio-unwrit', 13678: 'Heap thread poo', 13679: 'Heap thread poo', 13680: 'Signal Catcher', 13681: 'JDWP', 169: '/system/bin/logd', 170: '/sbin/healthd', 171: '/system/bin/lmkd', 172: '/system/bin/servicemanager', 173: '/system/bin/vold', 174: 'IPCRTR', 175: 'sb-1', 177: 'ipc_rtr_q6_ipcr', 179: 'ngd_msm_ctrl_ng', 180: '/system/bin/surfaceflinger', 181: '/system/bin/rmt_storage', 182: '/system/bin/qseecomd', 183: 'msm_slim_qmi_cl', 184: 'msm_qmi_rtx_q', 185: '/system/bin/sh', 187: '/system/bin/subsystem_ramdump', 188: '/system/bin/netd', 189: '/system/bin/debuggerd', 191: '/system/bin/rild', 192: '/system/bin/drmserver', 194: '/system/bin/mediaserver', 195: '/system/bin/installd', 197: '/system/bin/keystore', 198: '/system/bin/bridgemgrd', 199: '/system/bin/qmuxd', 200: '/system/bin/netmgrd', 201: '/system/bin/sensors.qcom', 204: '/system/bin/thermal-engine-hh', 205: 'zygote', 206: '/system/bin/sdcard', 207: '/system/bin/mm-qcamera-daemon', 208: '/system/bin/time_daemon', 209: '/sbin/adbd', 210: 'adbd', 211: 'adbd', 212: 'adbd', 214: 'irq/288-wcd9xxx', 216: 'logd.reader', 217: 'logd.writer', 218: 'logd', 219: 'kauditd', 13690: 'pool-1-thread-1', 223: 'vold', 226: 'vold', 227: 'sdcard', 228: 'sdcard', 240: 'Binder_1', 242: 'DispSync', 243: 'Binder_2', 244: 'logd.auditd', 247: 'thermal-engine-', 250: 'thermal-engine-', 13695: 'org.chromium.chrome.shell', 252: 'thermal-engine-', 253: 'thermal-engine-', 254: 'thermal-engine-', 255: 'thermal-engine-', 257: 'thermal-engine-', 258: 'thermal-engine-', 259: 'thermal-engine-', 260: 'thermal-engine-', 261: 'thermal-engine-', 262: 'thermal-engine-', 263: 'thermal-engine-', 264: 'thermal-engine-', 265: 'thermal-engine-', 266: 'thermal-engine-', 267: 'thermal-engine-', 268: 'thermal-engine-', 269: 'thermal-engine-', 270: 'thermal-engine-', 272: 'thermal-engine-', 273: 'thermal-engine-', 275: 'thermal-engine-', 276: 'thermal-engine-', 277: 'thermal-engine-', 278: 'thermal-engine-', 280: 'thermal-engine-', 281: 'thermal-engine-', 282: 'thermal-engine-', 283: 'thermal-engine-', 284: 'thermal-engine-', 286: 'thermal-engine-', 287: 'thermal-engine-', 288: 'bridgemgrd', 289: 'netmgrd', 290: 'sensors.qcom', 292: 'sensors.qcom', 293: 'qmuxd', 295: 'thermal-engine-', 297: 'thermal-engine-', 299: 'thermal-engine-', 300: 'thermal-engine-', 301: 'thermal-engine-', 308: 'time_daemon', 311: 'msm_thermal:hot', 312: 'msm_thermal:fre', 13707: 'FinalizerDaemon', 335: 'rild', 13710: 'GCDaemon', 343: 'rild', 346: 'rild', 13711: 'Binder_1', 348: '/system/bin/qseecomd', 349: 'qseecomd', 351: 'qseecomd', 360: 'mdss_fb0', 361: 'hwcUeventThread', 362: 'hwcVsyncThread', 8569: 'Binder_4', 8572: 'Binder_5', 8575: 'Binder_5', 386: 'qseecomd', 387: 'qseecomd', 8580: 'Binder_6', 8581: 'Binder_5', 396: 'GL updater', 397: 'surfaceflinger', 398: 'EventThread', 399: 'surfaceflinger', 400: 'EventThread', 401: 'EventControl', 13721: 'Heap thread poo', 13722: 'Heap thread poo', 419: 'Binder_1', 13724: 'Signal Catcher', 13725: 'JDWP', 548: 'netd', 549: 'netd', 550: 'netd', 551: 'netd', 552: 'netd', 553: 'netd', 554: 'netd', 555: 'netd', 557: 'kworker/0:2H', 558: 'IPCRTR', 559: 'thermal-engine-', 560: 'sensors.qcom', 561: 'time_daemon', 562: 'ipc_rtr_smd_ipc', 563: 'sensors.qcom', 564: 'sensors.qcom', 571: 'rmt_storage', 572: 'rmt_storage', 573: 'rmt_storage', 574: 'rmt_storage', 575: 'Binder_3', 576: 'qmuxd', 577: 'qmuxd', 578: 'qmuxd', 579: 'qmuxd', 580: 'qmuxd', 581: 'qmuxd', 582: 'qmuxd', 583: 'qmuxd', 584: 'rild', 585: 'rild', 587: 'rild', 588: 'rild', 589: 'rild', 591: 'rild', 592: 'rild', 593: 'rild', 594: 'rild', 596: 'thermal-engine-', 597: 'time_daemon', 598: 'time_daemon', 599: 'time_daemon', 600: 'thermal-engine-', 601: 'thermal-engine-', 602: 'bridgemgrd', 603: 'bridgemgrd', 605: 'sensors.qcom', 614: 'sensors.qcom', 621: 'sensors.qcom', 622: 'sensors.qcom', 623: 'sensors.qcom', 624: 'sensors.qcom', 625: 'sensors.qcom', 626: 'sensors.qcom', 627: 'sensors.qcom', 628: 'sensors.qcom', 629: 'sensors.qcom', 13660: 'FinalizerWatchd', 633: 'sensors.qcom', 13760: 'FinalizerWatchd', 643: 'sensors.qcom', 8839: 'com.ushaqi.zhuishushenqi:pushservice', 650: 'sensors.qcom', 651: 'sensors.qcom', 8845: 'Heap thread poo', 8846: 'Heap thread poo', 8847: 'Heap thread poo', 8849: 'Signal Catcher', 8850: 'JDWP', 8851: 'ReferenceQueueD', 8852: 'FinalizerDaemon', 8853: 'FinalizerWatchd', 8854: 'HeapTrimmerDaem', 8855: 'GCDaemon', 8856: 'Binder_1', 8857: 'Binder_2', 8867: 'local_job_dispa', 8869: 'remote_job_disp', 8887: 'Upload Http Rec', 8890: 'Connection Cont', 13774: 'com.life360.android.safetymapd', 736: 'netmgrd', 746: 'netmgrd', 747: 'netmgrd', 748: 'netmgrd', 755: 'ApmTone', 756: 'ApmAudio', 757: 'ApmOutput', 758: 'mediaserver', 759: 'FastMixer', 760: 'sensors.qcom', 763: 'sensors.qcom', 764: 'system_server', 767: 'Heap thread poo', 768: 'Heap thread poo', 770: 'Heap thread poo', 8963: 'Smack Packet Re', 773: 'Signal Catcher', 774: 'JDWP', 775: 'ReferenceQueueD', 776: 'FinalizerDaemon', 777: 'FinalizerWatchd', 778: 'HeapTrimmerDaem', 779: 'GCDaemon', 780: 'Binder_1', 781: 'Binder_2', 782: 'system_server', 783: 'system_server', 784: 'sensors.qcom', 785: 'system_server', 786: 'system_server', 788: 'system_server', 789: 'system_server', 790: 'sensors.qcom', 791: 'system_server', 792: 'sensors.qcom', 793: 'system_server', 794: 'sensors.qcom', 795: 'system_server', 796: 'sensors.qcom', 797: 'system_server', 798: 'sensors.qcom', 799: 'system_server', 800: 'sensors.qcom', 801: 'system_server', 802: 'sensors.qcom', 803: 'system_server', 804: 'sensors.qcom', 805: 'system_server', 806: 'sensors.qcom', 807: 'system_server', 808: 'sensors.qcom', 809: 'system_server', 810: 'sensors.qcom', 811: 'system_server', 812: 'sensors.qcom', 813: 'system_server', 814: 'sensors.qcom', 815: 'system_server', 816: 'sensors.qcom', 817: 'system_server', 818: 'sensors.qcom', 819: 'system_server', 820: 'sensors.qcom', 821: 'system_server', 822: 'sensors.qcom', 823: 'system_server', 824: 'sensors.qcom', 826: 'SensorEventAckR', 827: 'SensorService', 828: 'android.bg', 829: 'ActivityManager', 830: 'FileObserver', 831: 'android.fg', 832: 'android.ui', 833: 'android.io', 834: 'android.display', 835: 'CpuTracker', 836: 'PowerManagerSer', 837: 'system_server', 838: 'system_server', 839: 'BatteryStats_wa', 840: 'PackageManager', 841: 'bridgemgrd', 842: 'PackageInstalle', 844: 'AlarmManager', 845: 'UEventObserver', 853: 'InputDispatcher', 854: 'InputReader', 857: 'MountService', 858: 'VoldConnector', 4239: 'FinalizerDaemon', 860: 'NetdConnector', 861: 'NetworkStats', 862: 'NetworkPolicy', 863: 'WifiP2pService', 864: 'WifiStateMachin', 865: 'WifiService', 866: 'ConnectivitySer', 867: 'NsdService', 868: 'mDnsConnector', 869: 'ranker', 870: 'AudioService', 871: 'AudioOut_2', 872: 'AudioOut_4', 873: 'FastMixer', 874: 'AudioOut_6', 878: 'Binder_1', 879: 'Binder_2', 882: 'WifiWatchdogSta', 883: 'WifiManager', 884: 'WifiScanningSer', 885: 'WifiRttService', 886: 'EthernetService', 887: 'backup', 888: '/system/bin/wpa_supplicant', 889: 'Thread-69', 892: 'LazyTaskWriterT', 893: 'UsbService host', 894: 'Thread-73', 915: 'com.android.systemui', 919: 'Heap thread poo', 920: 'Heap thread poo', 921: 'Heap thread poo', 925: 'Signal Catcher', 926: 'JDWP', 927: 'ReferenceQueueD', 928: 'FinalizerDaemon', 929: 'FinalizerWatchd', 930: 'HeapTrimmerDaem', 931: 'GCDaemon', 933: 'Binder_1', 934: 'Binder_2', 936: 'android.process.media', 942: 'Binder_3', 943: 'Heap thread poo', 944: 'Heap thread poo', 945: 'Heap thread poo', 947: 'Signal Catcher', 949: 'JDWP', 950: 'ReferenceQueueD', 951: 'FinalizerDaemon', 952: 'FinalizerWatchd', 953: 'HeapTrimmerDaem', 954: 'GCDaemon', 956: 'Binder_1', 957: 'Binder_2', 13671: 'sogou.mobile.explorer.hotwords', 964: 'SoundPool', 965: 'SoundPoolThread', 970: 'Recents-TaskRes', 13819: 'Binder_2', 1007: 'thumbs thread', 1020: 'MtpServer', 1078: 'SystemUI Storag', 1079: 'watchdog', 1094: 'SoundPool', 1095: 'SoundPoolThread', 1108: 'Binder_4', 1109: 'Binder_5', 1111: 'com.google.android.googlequicksearchbox:interactor', 1113: 'Heap thread poo', 1114: 'Heap thread poo', 1116: 'Heap thread poo', 1121: 'Signal Catcher', 1124: 'JDWP', 1125: 'ReferenceQueueD', 1126: 'FinalizerDaemon', 1127: 'FinalizerWatchd', 1128: 'HeapTrimmerDaem', 1129: 'GCDaemon', 1131: 'Binder_1', 1132: 'Binder_2', 1133: 'Binder_3', 1136: 'com.google.android.inputmethod.pinyin', 1142: 'Heap thread poo', 1143: 'Heap thread poo', 1144: 'Heap thread poo', 1145: 'Signal Catcher', 1146: 'JDWP', 1147: 'ReferenceQueueD', 1148: 'FinalizerDaemon', 1149: 'FinalizerWatchd', 1151: 'HeapTrimmerDaem', 1152: 'GCDaemon', 1153: 'Binder_1', 1154: 'Binder_2', 1186: 'FLP Service Cal', 1188: 'FLP Service Cal', 1191: 'NetworkTimeUpda', 1192: 'FLP Service Cal', 1199: 'com.android.nfc', 1208: 'Heap thread poo', 1209: 'Heap thread poo', 1210: 'Heap thread poo', 1211: 'Signal Catcher', 1212: 'JDWP', 1213: 'ReferenceQueueD', 1214: 'FinalizerDaemon', 1215: 'FinalizerWatchd', 1216: 'HeapTrimmerDaem', 1219: 'GCDaemon', 1220: 'Binder_1', 1221: 'Binder_2', 1226: 'Binder_6', 1233: 'Binder_7', 1234: 'com.redbend.vdmc', 1236: 'Heap thread poo', 1237: 'Heap thread poo', 1238: 'Heap thread poo', 1244: 'Signal Catcher', 1245: 'JDWP', 1246: 'ReferenceQueueD', 1247: 'FLP Service Cal', 1248: 'FinalizerDaemon', 1249: 'FinalizerWatchd', 1250: 'HeapTrimmerDaem', 1251: 'GCDaemon', 1252: 'Binder_1', 1256: 'Binder_8', 1257: 'Binder_2', 1260: 'WifiMonitor', 1271: 'Binder_9', 1274: 'com.android.phone', 1282: 'Heap thread poo', 1283: 'Heap thread poo', 1284: 'Heap thread poo', 1285: 'Signal Catcher', 1286: 'JDWP', 1287: 'ReferenceQueueD', 1288: 'FLP Service Cal', 1289: 'FLP Service Cal', 1290: 'FinalizerDaemon', 1291: 'FinalizerWatchd', 1292: 'HeapTrimmerDaem', 1293: 'GCDaemon', 1299: 'Binder_1', 1305: 'com.google.android.googlequicksearchbox', 1306: 'Heap thread poo', 1307: 'Heap thread poo', 1308: 'Heap thread poo', 1315: 'Binder_2', 1317: 'Signal Catcher', 1318: 'JDWP', 1319: 'FLP Service Cal', 1320: 'FLP Service Cal', 1322: 'ReferenceQueueD', 1323: 'FinalizerDaemon', 1324: 'FinalizerWatchd', 1330: 'GAThread', 1331: 'measurement-1', 1332: 'HeapTrimmerDaem', 1333: 'GCDaemon', 1334: 'Binder_1', 1335: 'Binder_2', 1336: 'pool-1-thread-1', 13684: 'FinalizerWatchd', 13880: 'Thread-1501', 1365: 'RILSender0', 1366: 'RILReceiver0', 1367: 'Thread-89', 1378: 'PhoneStatusBar', 1380: 'DcHandlerThread', 1381: 'WifiManager', 1385: 'AsyncTask #1', 1386: 'launcher-loader', 1388: 'AsyncTask #1', 1391: 'AsyncQueryWorke', 1392: 'GsmCellBroadcas', 1393: 'AsyncTask #1', 1394: 'GsmInboundSmsHa', 1395: 'AsyncTask #1', 1397: 'CellBroadcastHa', 1408: 'AsyncTask #1', 1409: 'AsyncTask #1', 1416: 'ConnectivityMan', 1417: 'CdmaInboundSmsH', 1418: 'CdmaServiceCate', 13890: 'Heap thread poo', 1425: 'AsyncTask #1', 1427: 'DcSwitchStateMa', 1428: 'Binder_3', 1429: 'SyncHandler-0', 1431: 'FlashlightContr', 1432: 'AsyncTask #2', 13892: 'Heap thread poo', 1434: 'AsyncTask #1', 1435: 'QSTileHost', 1438: 'AsyncTask #2', 1441: 'RenderThread', 1442: 'AsyncTask #3', 1443: 'AsyncTask #1', 13894: 'Signal Catcher', 1451: 'com.google.android.googlequicksearchbox:search', 1457: 'Heap thread poo', 1458: 'Heap thread poo', 1459: 'Heap thread poo', 1460: 'Signal Catcher', 1461: 'JDWP', 1462: 'ReferenceQueueD', 1463: 'FinalizerDaemon', 1464: 'FinalizerWatchd', 1466: 'HeapTrimmerDaem', 1468: 'GCDaemon', 1473: 'Binder_3', 1474: 'Binder_1', 1475: 'Binder_2', 1478: 'com.google.process.gapps', 1484: 'GELServices-0', 1485: 'Heap thread poo', 1486: 'Heap thread poo', 1487: 'Heap thread poo', 1488: 'Signal Catcher', 1489: 'JDWP', 1490: 'ReferenceQueueD', 1491: 'FinalizerDaemon', 1492: 'FinalizerWatchd', 1493: 'HeapTrimmerDaem', 1494: 'GCDaemon', 1495: 'Binder_1', 1496: 'Binder_2', 1497: 'Binder_3', 1501: 'Binder_4', 1503: 'AsyncTask #1', 1514: 'RenderThread', 1515: 'User-Facing Non', 1516: 'User-Facing Non', 1517: 'ervice.Executor', 1518: 'WifiManager', 1535: 'User-Facing Non', 1538: 'User-Facing Non', 1540: 'AsyncTask #3', 1553: 'User-Facing Non', 1560: 'IcingConnection', 1561: 'AsyncTask #1', 1563: 'Cat Telephony s', 1564: 'RilMessageDecod', 1565: 'hwuiTask1', 1566: 'hwuiTask2', 1567: 'Cat Icon Loader', 1573: 'Thread-55', 1574: 'Thread-56', 1575: 'Thread-57', 1577: 'SoundPool', 1578: 'SoundPoolThread', 1580: 'AudioRouter-0', 1593: 'sensors.qcom', 1600: 'sensors.qcom', 1613: 'Gservices', 1614: 'RefQueueWorker@', 1615: 'Gservices', 1616: 'RefQueueWorker@', 1618: 'GELServices-1', 1620: 'Gservices', 1621: 'GELServices-2', 1622: 'AsyncTask #2', 1626: 'AsyncFileStorag', 1629: 'GELServices-3', 1632: 'AsyncTask #4', 1633: 'AsyncTask #5', 1635: 'WifiManager', 1636: 'GELServices-4', 1637: 'AsyncTask #4', 1643: 'LocationOracleI', 1644: 'GL updater', 1646: 'GoogleApiClient', 1647: 'GELServices-5', 1654: 'Binder_A', 1664: 'GELServices-6', 1690: 'Binder_4', 1692: 'GL updater', 1693: 'NetworkMonitorN', 1695: 'DhcpStateMachin', 1700: '/system/bin/dhcpcd', 1764: 'Binder_3', 1766: 'GELServices-7', 1769: 'Binder_3', 1770: 'Gservices', 1772: 'RenderThread', 1773: 'RenderThread', 1774: 'RenderThread', 1775: 'RenderThread', 1781: 'AsyncTask #1', 1782: 'AsyncTask #2', 1807: 'RenderThread', 1810: 'ChromiumNet', 1811: 'DnsConfigServic', 1812: 'inotify_reader', 1815: 'Network File Th', 1816: 'SimpleCacheWork', 1817: 'SimpleCacheWork', 1823: 'Binder_4', 1824: 'Binder_5', 1873: 'com.google.android.gms', 1878: 'Heap thread poo', 1880: 'Heap thread poo', 1881: 'Heap thread poo', 1882: 'Signal Catcher', 1883: 'JDWP', 1884: 'ReferenceQueueD', 1885: 'FinalizerDaemon', 1886: 'FinalizerWatchd', 1887: 'HeapTrimmerDaem', 1888: 'GCDaemon', 1889: 'Binder_1', 1890: 'Binder_2', 13702: 'Heap thread poo', 1895: 'Gservices', 1898: 'measurement-1', 1900: 'AsyncTask #1', 1904: 'AsyncTask #2', 1949: 'com.google.android.gms.persistent', 1954: 'Heap thread poo', 1955: 'Heap thread poo', 1956: 'Heap thread poo', 1959: 'Signal Catcher', 1960: 'JDWP', 1961: 'ReferenceQueueD', 1962: 'FinalizerDaemon', 1963: 'FinalizerWatchd', 1964: 'HeapTrimmerDaem', 1965: 'GCDaemon', 1966: 'Binder_1', 1967: 'Binder_2', 1968: 'Gservices', 1973: 'IntentService[G', 1976: 'FlpThread', 1977: 'Binder_3', 1978: 'WifiManager', 1979: 'GeofencerStateM', 1980: 'LocationService', 1984: 'Binder_4', 1986: 'Binder_5', 1990: 'pool-4-thread-1', 1992: 'GmsCoreStatsSer', 1995: 'GoogleLocationS', 1996: 'Binder_4', 1997: 'Binder_5', 1998: 'GELServices-8', 2001: 'Binder_3', 2004: 'Thread-139', 2005: 'Thread-140', 2006: 'Thread-141', 2007: 'Thread-142', 2021: 'NetworkLocation', 2029: 'UlrDispatchingS', 2030: 'nlp-async-worke', 2031: '/system/bin/mpdecision', 2032: 'mpdecision', 2033: 'mpdecision', 2034: 'mpdecision', 2035: 'mpdecision', 2036: 'mpdecision', 2046: 'mpdecision', 2097: 'AsyncTask #3', 2124: 'SyncHandler-0', 2320: 'RemoteViewsCach', 2321: 'RemoteViewsAdap', 10584: 'pool-2-thread-1', 10591: 'RefQueueWorker@', 2497: 'WifiManager', 2509: 'picasa-uploads-', 2510: 'GCMWriter', 2512: 'AsyncTask #1', 2521: 'FitnessServiceF', 2522: 'FitRecordingBro', 13723: 'Heap thread poo', 2526: 'AsyncTask #1', 2530: 'NearbyMessagesB', 2536: 'GCMReader', 2547: 'pool-2-thread-1', 2647: 'com.qiyi.video.market', 2653: 'Heap thread poo', 2654: 'Heap thread poo', 2655: 'Heap thread poo', 2656: 'Signal Catcher', 2657: 'JDWP', 2658: 'ReferenceQueueD', 2659: 'FinalizerDaemon', 2660: 'FinalizerWatchd', 2661: 'HeapTrimmerDaem', 2662: 'GCDaemon', 2663: 'Binder_1', 2664: 'Binder_2', 2671: 'RefQueueWorker@', 2673: '.ProcessManager', 2675: 'Binder_3', 2677: 'Thread-208', 2679: 'pool-2-thread-1', 2680: 'WifiManager', 2682: 'Timer-0', 2683: 'Timer-1', 2710: 'AsyncTask #1', 2718: 'pool-3-thread-1', 2810: 'DownloadReceive', 2902: 'GELServices-9', 2905: 'PowerManagerSer', 2906: 'AsyncTask #2', 2915: 'AsyncTask #3', 2946: 'pool-7-thread-1', 3103: 'pool-4-thread-1', 3104: 'com.qiyi.video.market:pluginDownloadService', 3110: 'Heap thread poo', 3111: 'Heap thread poo', 3112: 'Heap thread poo', 3113: 'Signal Catcher', 3114: 'JDWP', 3115: 'ReferenceQueueD', 3116: 'FinalizerDaemon', 3117: 'FinalizerWatchd', 3118: 'HeapTrimmerDaem', 3119: 'GCDaemon', 3120: 'Binder_1', 3121: 'Binder_2', 3122: 'com.qiyi.video.market:bdservice_v1', 3128: 'Heap thread poo', 3129: 'Heap thread poo', 3130: 'Heap thread poo', 3131: 'Signal Catcher', 3132: 'JDWP', 3133: 'ReferenceQueueD', 3134: 'FinalizerDaemon', 3135: 'FinalizerWatchd', 3136: 'HeapTrimmerDaem', 3137: 'GCDaemon', 3138: 'Binder_1', 3139: 'Binder_2', 3141: 'RefQueueWorker@', 3145: 'RefQueueWorker@', 3154: 'com.qiyi.video.market:baiduLocation', 3163: 'Heap thread poo', 3164: 'Heap thread poo', 3165: 'Heap thread poo', 3166: 'Signal Catcher', 3167: 'JDWP', 3168: 'ReferenceQueueD', 3169: 'FinalizerDaemon', 3170: 'FinalizerWatchd', 3171: 'HeapTrimmerDaem', 3172: 'GCDaemon', 3173: 'Binder_1', 3174: 'Binder_2', 3177: 'RefQueueWorker@', 3179: 'com.tencent.mm:push', 3183: 'Heap thread poo', 3184: 'Heap thread poo', 3185: 'Heap thread poo', 3187: 'Signal Catcher', 3189: 'JDWP', 3190: 'ReferenceQueueD', 3191: 'FinalizerDaemon', 3192: 'FinalizerWatchd', 3193: 'HeapTrimmerDaem', 3194: 'GCDaemon', 3195: 'Binder_1', 3196: 'Binder_2', 3199: 'WifiManager', 3206: 'WifiManager', 3208: 'NanoHttpd Main ', 3210: 'THREAD_POOL_HAN', 3212: 'tencent.mm:push', 3216: 'FileObserver', 3217: 'com.baidu.searchbox:bdservice_v1', 3220: 'Heap thread poo', 3221: 'Heap thread poo', 3223: 'Heap thread poo', 3226: 'Signal Catcher', 3228: 'JDWP', 3229: 'ReferenceQueueD', 3230: 'FinalizerDaemon', 3231: 'FinalizerWatchd', 3233: 'HeapTrimmerDaem', 3234: 'GCDaemon', 3235: 'Binder_1', 3236: 'Binder_2', 3238: 'tencent.mm:push', 3239: 'default', 3240: 'WifiManager', 3257: 'pool-3-thread-1', 3260: 'com.baidu.searchbox:bdmoservice', 3264: 'Heap thread poo', 3265: 'Heap thread poo', 3266: 'Heap thread poo', 3269: 'Signal Catcher', 3270: 'JDWP', 3271: 'ReferenceQueueD', 3272: 'FinalizerDaemon', 3273: 'FinalizerWatchd', 3274: 'HeapTrimmerDaem', 3275: 'GCDaemon', 3276: 'Binder_1', 3277: 'Binder_2', 3303: 'AsyncTask #1', 3304: 'AsyncTask #2', 11634: 'kworker/u:0', 13754: 'Heap thread poo', 3518: 'PushService-Pus', 3519: 'PushService-Pus', 11779: 'kworker/0:3H', 3633: 'com.tencent.portfolio:push', 3636: 'Heap thread poo', 3638: 'Heap thread poo', 3639: 'Heap thread poo', 3642: 'Signal Catcher', 3643: 'JDWP', 3645: 'ReferenceQueueD', 3646: 'FinalizerDaemon', 3647: 'FinalizerWatchd', 3648: 'HeapTrimmerDaem', 3649: 'GCDaemon', 3650: 'Binder_1', 3651: 'Binder_2', 3661: 'TPPluginCenter ', 3663: 'pool-1-thread-1', 3665: 'MidService', 13761: 'HeapTrimmerDaem', 3667: 'pool-2-thread-1', 3668: 'push core threa', 3670: '.ProcessManager', 3672: 'Binder_3', 11865: 'pool-37-thread-', 3674: 'pool-4-thread-1', 3675: 'pool-3-thread-1', 3680: 'WifiManager', 13762: 'GCDaemon', 11928: 'kworker/0:1', 3738: 'NanoHttpd Main ', 12039: 'pool-1-thread-1', 12193: 'Background Bloc', 12207: 'User-Facing Blo', 12211: 'WorkerPool/1221', 12232: 'Background Non-', 12235: 'Background Bloc', 12236: 'Background Bloc', 12237: 'Background Non-', 4135: 'AsyncTask #2', 4159: 'AsyncTask #3', 4180: 'AsyncTask #2', 4184: 'AsyncTask #4', 12988: 'Binder_2', 4210: 'AsyncTask #5', 4221: 'AsyncTask #3', 4223: 'AsyncTask #4', 4226: 'AsyncTask #4', 4227: 'com.android.bluetooth', 4231: 'Heap thread poo', 4233: 'Heap thread poo', 4235: 'Heap thread poo', 4236: 'Signal Catcher', 4237: 'JDWP', 4238: 'ReferenceQueueD', 12431: 'kworker/u:2', 4240: 'FinalizerWatchd', 4241: 'HeapTrimmerDaem', 4242: 'GCDaemon', 4243: 'Binder_1', 4244: 'Binder_2', 12448: 'OkHttp Connecti', 12449: 'OkHttp Connecti', 4265: 'UsbDebuggingMan', 12484: 'AsyncTask #4', 13782: 'Heap thread poo', 4308: 'BluetoothAdapte', 4309: 'droid.bluetooth', 4311: 'bluedroid wake/', 4312: 'BT Service Call', 4315: 'BondStateMachin', 4316: 'Binder_3', 4317: 'Binder_4', 4318: 'HeadsetStateMac', 4320: 'BluetoothAvrcpH', 4321: 'A2dpStateMachin', 4322: 'A2DP-MEDIA', 4323: 'uipc-main', 4324: 'BluetoothHdpHan', 4325: 'droid.bluetooth', 4326: 'BluetoothAdvert', 4327: 'BluetoothScanMa', 4331: 'bluedroid wake/', 4333: 'bt_hc_worker', 4338: 'userial_read', 13784: 'Signal Catcher', 12576: 'AsyncTask #5', 13785: 'JDWP', 4390: 'pool-13-thread-', 4391: 'pool-18-thread-', 4392: 'pool-11-thread-', 4394: 'pool-25-thread-', 4395: 'pool-25-thread-', 4396: 'pool-25-thread-', 4397: 'pool-25-thread-', 4398: 'pool-14-thread-', 13786: 'ReferenceQueueD', 13787: 'FinalizerDaemon', 13788: 'FinalizerWatchd', 4478: 'BT Service Call', 4479: 'bt_hc_worker', 4480: 'Binder_4', 4481: 'bt_hc_worker', 4482: 'BluetoothMapAcc', 13789: 'HeapTrimmerDaem', 4521: 'MediaTracker bu', 14407: 'Binder_3', 6217: 'HeapTrimmerDaem', 4541: 'RefQueueWorker@', 13791: 'Binder_1', 4571: 'Stk App Service', 4597: 'com.qualcomm.qcrilmsgtunnel', 4603: 'Heap thread poo', 4604: 'Heap thread poo', 4605: 'Heap thread poo', 4606: 'Signal Catcher', 4607: 'JDWP', 4608: 'ReferenceQueueD', 4609: 'FinalizerDaemon', 4610: 'FinalizerWatchd', 4611: 'HeapTrimmerDaem', 4612: 'GCDaemon', 4613: 'Binder_1', 4614: 'Binder_2', 4615: 'QcRilReceiver', 4616: 'QcRilSender', 4735: 'pool-8-thread-1', 4749: 'CopresenceEvent', 4766: 'Icing-Pool-0', 4770: 'Binder_6', 4771: 'Icing-Worker-0', 12971: 'com.life360.android.safetymapd:service', 12977: 'Heap thread poo', 12978: 'Heap thread poo', 12979: 'Heap thread poo', 12980: 'Signal Catcher', 12981: 'JDWP', 12982: 'ReferenceQueueD', 12983: 'FinalizerDaemon', 12984: 'FinalizerWatchd', 12985: 'HeapTrimmerDaem', 12986: 'GCDaemon', 12987: 'Binder_1', 4796: 'Thread-200', 4797: 'Thread-201', 4798: 'Thread-202', 4799: 'Thread-203', 4800: 'Thread-204', 9001: 'Gservices', 13071: 'com.xianguo.tingguo', 13075: 'Heap thread poo', 13076: 'Heap thread poo', 13077: 'Heap thread poo', 13080: 'Signal Catcher', 13081: 'JDWP', 13082: 'ReferenceQueueD', 13083: 'FinalizerDaemon', 13084: 'FinalizerWatchd', 13085: 'HeapTrimmerDaem', 13086: 'GCDaemon', 13087: 'Binder_1', 13088: 'Binder_2', 13090: 'SoundPool', 13091: 'SoundPoolThread', 13099: 'WifiManager', 4917: 'Binder_3', 9024: 'GamesProviderWo', 13276: 'WifiManager', 13345: 'com.google.android.apps.photos', 13351: 'Heap thread poo', 13352: 'Heap thread poo', 13353: 'Heap thread poo', 13354: 'Signal Catcher', 13355: 'JDWP', 13356: 'ReferenceQueueD', 13357: 'FinalizerDaemon', 13358: 'FinalizerWatchd', 13359: 'HeapTrimmerDaem', 13360: 'GCDaemon', 13361: 'Binder_1', 13362: 'Binder_2', 5239: '.iqiyipushserviceGlobal', 5242: 'Heap thread poo', 5244: 'Heap thread poo', 5245: 'Heap thread poo', 5248: 'Signal Catcher', 5249: 'JDWP', 5250: 'ReferenceQueueD', 5251: 'FinalizerDaemon', 5252: 'FinalizerWatchd', 5253: 'HeapTrimmerDaem', 5254: 'GCDaemon', 5255: 'Binder_1', 5257: 'Binder_2', 5280: 'RefQueueWorker@', 5281: 'Binder_3', 5285: '.iqiyipushserviceGlobal', 13491: 'com.google.android.apps.plus', 13493: 'Heap thread poo', 13494: 'Heap thread poo', 13495: 'Heap thread poo', 13497: 'Signal Catcher', 13499: 'JDWP', 13502: 'ReferenceQueueD', 13503: 'FinalizerDaemon', 13504: 'FinalizerWatchd', 13505: 'HeapTrimmerDaem', 13506: 'GCDaemon', 13507: 'Binder_1', 13508: 'Binder_2', 13512: 'picasa-photo-pr', 5323: 'com.strava', 13516: 'android.process.acore', 5327: 'Heap thread poo', 13520: 'Heap thread poo', 5329: 'Heap thread poo', 13522: 'Heap thread poo', 5332: 'Signal Catcher', 5333: 'JDWP', 5334: 'ReferenceQueueD', 13527: 'ReferenceQueueD', 5336: 'FinalizerWatchd', 13529: 'FinalizerDaemon', 13530: 'FinalizerWatchd', 13531: 'HeapTrimmerDaem', 13532: 'GCDaemon', 13533: 'Binder_1', 13534: 'Binder_2', 13536: 'ContactsProvide', 13537: 'CallLogProvider', 13538: 'pool-2-thread-1', 5347: 'Queue', 5348: 'Queue', 5349: 'Queue', 5352: 'Crashlytics Exc', 5354: 'pool-3-thread-1', 5361: 'Micro Client Co', 5362: 'Micro Client Co', 5363: 'Micro Client Ca', 5364: 'Thread-584', 5365: 'Thread-585', 5366: 'Thread-586', 5367: 'pool-4-thread-1', 5369: 'Crashlytics Tra', 5372: 'Thread-593', 5373: 'Thread-594', 5374: 'Thread-595', 5375: 'Thread-596', 5376: 'pool-6-thread-1', 5377: 'Thread-598', 5378: 'Thread-599', 5379: 'Thread-600', 5381: 'Thread-602', 5383: 'Thread #1', 5384: 'AsyncTask #1', 5387: 'Thread-605', 5388: 'Thread-606', 5389: 'Thread-607', 5390: 'Thread-608', 5391: 'Thread-609', 5393: 'eNowAuthService', 5394: 'Thread #2', 5395: 'com.pandora.android', 5397: 'Heap thread poo', 5398: 'Heap thread poo', 5399: 'Heap thread poo', 5401: 'Signal Catcher', 5403: 'JDWP', 5407: 'ReferenceQueueD', 5408: 'FinalizerDaemon', 5409: 'FinalizerWatchd', 5410: 'HeapTrimmerDaem', 5411: 'GCDaemon', 5414: 'Binder_1', 5416: 'Binder_2', 13613: 'com.sohu.inputmethod.sogou:classic', 5422: 'Crashlytics Exc', 13616: 'Heap thread poo', 13617: 'Heap thread poo', 13618: 'Heap thread poo', 13620: 'Signal Catcher', 5429: 'pool-2-thread-1', 5430: 'AsyncTask #1', 13623: 'JDWP', 13624: 'ReferenceQueueD', 13625: 'FinalizerDaemon', 13626: 'FinalizerWatchd', 13627: 'HeapTrimmerDaem', 13628: 'GCDaemon', 5437: 'Crashlytics Tra', 13630: 'Binder_2', 5439: 'AsyncTask #2', 5440: 'pool-4-thread-1', 5443: 'PurchasingManag', 13636: 'Thread-1444', 5445: 'AsyncTask #3', 13638: 'Thread-1446', 13639: 'Thread-1447', 13641: 'WifiManager', 13647: 'com.sohu.inputmethod.sogou', 13651: 'Heap thread poo', 13652: 'Heap thread poo', 13653: 'Heap thread poo', 13656: 'Signal Catcher', 13657: 'JDWP', 13658: 'ReferenceQueueD', 13659: 'FinalizerDaemon', 5468: 'Okio Watchdog', 13661: 'HeapTrimmerDaem', 13662: 'GCDaemon', 13663: 'Binder_1', 13664: 'Binder_2', 5474: 'tunein.player', 5479: 'Heap thread poo', 5480: 'Heap thread poo', 5481: 'Heap thread poo', 5483: 'Signal Catcher', 5484: 'JDWP', 5485: 'ReferenceQueueD', 5486: 'FinalizerDaemon', 5487: 'FinalizerWatchd', 5488: 'HeapTrimmerDaem', 5489: 'GCDaemon', 5490: 'Binder_1', 13683: 'FinalizerDaemon', 5492: 'Binder_2', 13685: 'HeapTrimmerDaem', 13686: 'GCDaemon', 13687: 'Binder_1', 13688: 'Binder_2', 5498: 'Thread #3', 13691: 'pool-1-thread-2', 13692: 'pool-1-thread-3', 13694: 'Timer-0', 5503: 'geHandlerThread', 5504: 'GAThread', 5507: 'Crashlytics Exc', 13701: 'Heap thread poo', 5510: 'AsyncTask #1', 13703: 'Heap thread poo', 13704: 'Signal Catcher', 13705: 'JDWP', 13706: 'ReferenceQueueD', 5515: 'Crashlytics Tra', 13708: 'FinalizerWatchd', 13709: 'HeapTrimmerDaem', 5518: 'AsyncTask #2', 5519: 'com.dropbox.android', 13712: 'Binder_2', 13713: 'Binder_3', 13715: 'com.rolocule.motiontennis', 5525: 'Heap thread poo', 5526: 'Heap thread poo', 5527: 'Heap thread poo', 5528: 'Signal Catcher', 5529: 'JDWP', 5530: 'ReferenceQueueD', 5531: 'FinalizerDaemon', 5532: 'FinalizerWatchd', 5533: 'HeapTrimmerDaem', 5534: 'GCDaemon', 5535: 'Binder_1', 5536: 'Binder_2', 13731: 'ReferenceQueueD', 13732: 'FinalizerDaemon', 13733: 'FinalizerWatchd', 13734: 'HeapTrimmerDaem', 13735: 'GCDaemon', 13736: 'Binder_1', 13737: 'Binder_2', 13747: 'com.google.android.apps.chrome', 13751: 'Heap thread poo', 13752: 'Heap thread poo', 5562: 'Dropbox log upl', 5563: 'gandalf updater', 13756: 'Signal Catcher', 13757: 'JDWP', 13758: 'ReferenceQueueD', 13759: 'FinalizerDaemon', 5568: 'pool-10-thread-', 5569: 'DbxFileObserver', 5570: 'LocalThumbManag', 13763: 'Binder_1', 13764: 'Binder_2', 5574: 'local AsyncTask', 5575: 'remote AsyncTas', 5576: 'local AsyncTask', 5577: 'remote AsyncTas', 5578: 'Dropbox notif o', 5579: 'Dropbox notif s', 5580: 'Picasso-Stats', 5581: 'Picasso-Dispatc', 5582: 'Picasso-refQueu', 5583: 'gandalf updater', 5587: 'AcceptThreadSec', 13780: 'Heap thread poo', 13781: 'Heap thread poo', 5590: 'Binder_3', 13783: 'pool-1-thread-1', 5592: 'DbxFileObserver', 5593: 'dbxpool-34:r-th', 5594: 'dbxpool-32:au-t', 5595: 'dbxpool-38:a-th', 5596: 'Timer-0', 5597: 'dbxpool-6:a-thr', 13790: 'GCDaemon', 5599: 'Timer-1', 13792: 'Binder_2', 13796: 'rotating_file-t', 13801: 'com.android.vending', 5610: 'AsyncTask #4', 13807: 'Heap thread poo', 13808: 'Heap thread poo', 13809: 'Heap thread poo', 13811: 'Signal Catcher', 13812: 'JDWP', 13813: 'ReferenceQueueD', 13814: 'FinalizerDaemon', 13815: 'FinalizerWatchd', 13816: 'HeapTrimmerDaem', 13817: 'GCDaemon', 13818: 'Binder_1', 5627: 'Binder_3', 5633: 'Binder_5', 13828: 'Gservices', 5638: 'Timer-0', 13833: 'pool-1-thread-1', 13834: 'RefQueueWorker@', 13837: 'RefQueueWorker@', 13838: 'Thread-1482', 13839: 'Thread-1483', 13840: 'Thread-1484', 13843: 'download-manage', 13844: 'NetworkQualityQ', 13845: 'RefQueueWorker@', 13846: 'Thread-1489', 13847: 'Thread-1490', 13848: 'Thread-1491', 13849: 'Thread-1492', 13850: 'Thread-1493', 13851: 'PlayEventLogger', 13852: 'tentative-gc-ru', 13853: 'com.google.android.gms:car', 13856: 'Heap thread poo', 13857: 'Heap thread poo', 13858: 'Heap thread poo', 13862: 'libraries-threa', 13863: 'Signal Catcher', 13864: 'JDWP', 13865: 'ReferenceQueueD', 13866: 'FinalizerDaemon', 13867: 'FinalizerWatchd', 13868: 'HeapTrimmerDaem', 13869: 'GCDaemon', 13870: 'Binder_1', 13871: 'Binder_2', 13872: 'AsyncTask #1', 13873: 'Gservices', 13876: 'AsyncTask #2', 13877: 'AsyncTask #3', 13878: 'PlayEventLogger', 5688: 'com.devuni.flashlight:remote', 13881: 'Gservices', 5693: 'Heap thread poo', 5694: 'Heap thread poo', 5695: 'Heap thread poo', 5697: 'Signal Catcher', 5698: 'JDWP', 5699: 'ReferenceQueueD', 5700: 'FinalizerDaemon', 5701: 'FinalizerWatchd', 5702: 'HeapTrimmerDaem', 5703: 'GCDaemon', 5704: 'Binder_1', 5705: 'Binder_2', 13898: 'FinalizerWatchd', 13899: 'HeapTrimmerDaem', 13900: 'GCDaemon', 13901: 'Binder_1', 13902: 'Binder_2', 13903: 'Gservices', 13904: 'Binder_3', 13905: 'Binder_3', 5717: 'GL updater', 5718: 'RefQueueWorker@', 5719: 'AsyncTask #5', 13913: 'Binder_3', 13677: 'Heap thread poo', 5750: 'Thread-625', 5783: 'WifiManager', 5793: 'Binder_4', 5816: 'Binder_4', 5818: 'Binder_3', 14061: 'kworker/u:3', 14136: 'kworker/0:0H', 14356: 'com.google.android.apps.gcs', 14357: 'ReferenceQueueD', 14358: 'FinalizerDaemon', 14359: 'FinalizerWatchd', 14360: 'HeapTrimmerDaem', 14361: 'GCDaemon', 14362: 'Heap thread poo', 14363: 'Heap thread poo', 14364: 'Heap thread poo', 14365: 'Signal Catcher', 14366: 'JDWP', 14367: 'ReferenceQueueD', 14368: 'FinalizerDaemon', 14369: 'FinalizerWatchd', 14370: 'HeapTrimmerDaem', 14371: 'GCDaemon', 14372: 'Binder_1', 14373: 'Binder_2', 14375: 'Gservices', 14376: 'RefQueueWorker@', 14377: 'Thread-1495', 14378: 'Thread-1496', 14379: 'Thread-1497', 14380: 'Thread-1498', 14381: 'Thread-1499', 6201: 'pool-1-thread-1', 6202: 'com.tencent.mm', 6206: 'Heap thread poo', 6207: 'Heap thread poo', 6208: 'Heap thread poo', 14401: 'Thread-233', 6211: 'Signal Catcher', 6212: 'JDWP', 6213: 'ReferenceQueueD', 6214: 'FinalizerDaemon', 6215: 'FinalizerWatchd', 14409: 'OkHttp Connecti', 6218: 'GCDaemon', 6220: 'Binder_1', 6221: 'Binder_2', 6236: 'THREAD_POOL_HAN', 6237: 'com.tencent.mm', 6239: 'com.tencent.mm', 6240: 'MonitorHandlerT', 6241: '.ProcessManager', 6243: 'Binder_3', 6245: 'default', 6246: 'com.tencent.mm', 6247: 'MMHandlerThread', 14444: '/system/bin/sh', 14445: 'adbd', 14448: 'ps', 6257: 'Icing-Pool-1', 6258: 'Icing-Pool-2', 6259: 'Icing-Pool-3', 6269: 'MM_Thread_Pool_', 6270: 'MM_Thread_Pool_', 6271: 'MM_Thread_Pool_', 6272: 'MM_Thread_Pool_', 6273: 'MM_Thread_Pool_', 6274: 'MM_Thread_Pool_', 6276: 'ExdeviceHandler', 6277: 'MM_Thread_Pool_', 6279: 'RWCache_timeout', 6280: 'RWCache_timeout', 6282: 'MM_Thread_Pool_', 6284: 'downloadStateCh', 6288: 'WifiManager', 6289: 'refresh Notific', 6292: 'MM_Thread_Pool_', 6293: 'MM_Thread_Pool_', 6294: 'MM_Thread_Pool_', 6295: 'SearchDaemon', 6303: 'Binder_4', 6313: 'pool-2-thread-1', 6326: 'AsyncTask #5', 6373: 'RWCache_timeout', 6408: 'h', 6459: 'BluetoothPbapAc', 6473: 'pool-1-thread-1', 6477: 'BtOppRfcommList', 13682: 'ReferenceQueueD', 6481: 'AsyncTask #5', 6672: 'AsyncTask #2', 6673: 'pool-22-thread-', 6709: 'Binder_B', 6740: 'Binder_4', 6752: 'AsyncTask #3', 6917: 'Binder_6', 7091: 'Binder_5', 7150: 'default', 7173: 'Binder_3', 7196: 'UlrDispatchingS', 7230: 'default', 7231: 'MMHandlerThread', 7260: 'Thread-174', 7261: 'Thread-175', 7262: 'Thread-176', 7263: 'Thread-177', 7264: 'Thread-178', 13885: 'com.google.android.gms.wearable', 5328: 'Heap thread poo', 13521: 'Heap thread poo', 13525: 'Signal Catcher', 13526: 'JDWP', 5335: 'FinalizerDaemon', 13528: 'iu-sync-manager', 5337: 'HeapTrimmerDaem', 5338: 'GCDaemon', 5339: 'Binder_1', 5340: 'Binder_2', 5345: 'Queue', 5346: 'Queue', 7557: 'Binder_6', 13891: 'Heap thread poo', 7586: 'pool-4-thread-1', 13895: 'JDWP', 13896: 'ReferenceQueueD', 13897: 'FinalizerDaemon', 13629: 'Binder_1', 13635: 'Thread-1443', 5444: 'BluetoothServer', 13637: 'Thread-1445', 5446: 'AsyncTask #4'}
\ No newline at end of file
+{1: '/init', 2: 'kthreadd', 3: 'ksoftirqd/0', 7: 'kworker/u:0H', 8: 'migration/0', 13: 'khelper', 14: 'netns'}
diff --git a/sdk/platform-tools/systrace/catapult/systrace/systrace/test_data/atrace_ps_dump b/sdk/platform-tools/systrace/catapult/systrace/systrace/test_data/atrace_ps_dump
index 497918f..3c412fc 100644
--- a/sdk/platform-tools/systrace/catapult/systrace/systrace/test_data/atrace_ps_dump
+++ b/sdk/platform-tools/systrace/catapult/systrace/systrace/test_data/atrace_ps_dump
@@ -6,1527 +6,3 @@
 root      8     2     0      0     ffffffff 00000000 S migration/0
 root      13    2     0      0     ffffffff 00000000 S khelper
 root      14    2     0      0     ffffffff 00000000 S netns
-root      17    2     0      0     ffffffff 00000000 S kworker/0:1H
-root      18    2     0      0     ffffffff 00000000 S modem_notifier
-root      19    2     0      0     ffffffff 00000000 S smd_channel_clo
-root      20    2     0      0     ffffffff 00000000 S smsm_cb_wq
-root      21    2     0      0     ffffffff 00000000 S kworker/u:1
-root      22    2     0      0     ffffffff 00000000 S rpm-smd
-root      23    2     0      0     ffffffff 00000000 S kworker/u:1H
-root      24    2     0      0     ffffffff 00000000 S irq/317-earjack
-root      25    2     0      0     ffffffff 00000000 S sync_supers
-root      26    2     0      0     ffffffff 00000000 S bdi-default
-root      27    2     0      0     ffffffff 00000000 S kblockd
-root      28    2     0      0     ffffffff 00000000 S vmalloc
-root      29    2     0      0     ffffffff 00000000 S khubd
-root      30    2     0      0     ffffffff 00000000 S irq/102-msm_iom
-root      31    2     0      0     ffffffff 00000000 S irq/102-msm_iom
-root      32    2     0      0     ffffffff 00000000 S irq/102-msm_iom
-root      33    2     0      0     ffffffff 00000000 S irq/79-msm_iomm
-root      34    2     0      0     ffffffff 00000000 S irq/78-msm_iomm
-root      35    2     0      0     ffffffff 00000000 S irq/78-msm_iomm
-root      36    2     0      0     ffffffff 00000000 S irq/74-msm_iomm
-root      37    2     0      0     ffffffff 00000000 S irq/75-msm_iomm
-root      38    2     0      0     ffffffff 00000000 S irq/75-msm_iomm
-root      39    2     0      0     ffffffff 00000000 S irq/75-msm_iomm
-root      40    2     0      0     ffffffff 00000000 S irq/75-msm_iomm
-root      41    2     0      0     ffffffff 00000000 S irq/273-msm_iom
-root      42    2     0      0     ffffffff 00000000 S irq/273-msm_iom
-root      43    2     0      0     ffffffff 00000000 S irq/97-msm_iomm
-root      44    2     0      0     ffffffff 00000000 S irq/97-msm_iomm
-root      45    2     0      0     ffffffff 00000000 S irq/97-msm_iomm
-root      46    2     0      0     ffffffff 00000000 S l2cap
-root      47    2     0      0     ffffffff 00000000 S a2mp
-root      48    2     0      0     ffffffff 00000000 S cfg80211
-root      49    2     0      0     ffffffff 00000000 S qmi
-root      50    2     0      0     ffffffff 00000000 S nmea
-root      51    2     0      0     ffffffff 00000000 S msm_ipc_router
-root      52    2     0      0     ffffffff 00000000 S apr_driver
-root      54    2     0      0     ffffffff 00000000 S kswapd0
-root      55    2     0      0     ffffffff 00000000 S fsnotify_mark
-root      56    2     0      0     ffffffff 00000000 S cifsiod
-root      57    2     0      0     ffffffff 00000000 S crypto
-root      75    2     0      0     ffffffff 00000000 S ad_calc_wq
-root      76    2     0      0     ffffffff 00000000 S hdmi_tx_workq
-root      77    2     0      0     ffffffff 00000000 S anx7808_work
-root      78    2     0      0     ffffffff 00000000 S k_hsuart
-root      79    2     0      0     ffffffff 00000000 S diag_wq
-root      80    2     0      0     ffffffff 00000000 S diag_cntl_wq
-root      81    2     0      0     ffffffff 00000000 S diag_dci_wq
-root      82    2     0      0     ffffffff 00000000 S kgsl-3d0
-root      84    2     0      0     ffffffff 00000000 S f9966000.spi
-root      88    2     0      0     ffffffff 00000000 S usbnet
-root      89    2     0      0     ffffffff 00000000 S irq/329-anx7808
-root      90    2     0      0     ffffffff 00000000 S k_rmnet_mux_wor
-root      91    2     0      0     ffffffff 00000000 S f_mtp
-root      92    2     0      0     ffffffff 00000000 S file-storage
-root      93    2     0      0     ffffffff 00000000 S uether
-root      94    2     0      0     ffffffff 00000000 S synaptics_wq
-root      95    2     0      0     ffffffff 00000000 S irq/362-s3350
-root      96    2     0      0     ffffffff 00000000 S kworker/0:2
-root      97    2     0      0     ffffffff 00000000 S msm_vidc_worker
-root      98    2     0      0     ffffffff 00000000 S msm_vidc_worker
-root      99    2     0      0     ffffffff 00000000 S msm_cpp_workque
-root      100   2     0      0     ffffffff 00000000 S irq/350-bq51013
-root      102   2     0      0     ffffffff 00000000 S dm_bufio_cache
-root      103   2     0      0     ffffffff 00000000 D dbs_sync/0
-root      104   2     0      0     ffffffff 00000000 D dbs_sync/1
-root      105   2     0      0     ffffffff 00000000 D dbs_sync/2
-root      106   2     0      0     ffffffff 00000000 D dbs_sync/3
-root      107   2     0      0     ffffffff 00000000 S cfinteractive
-root      108   2     0      0     ffffffff 00000000 S irq/170-msm_sdc
-root      109   2     0      0     ffffffff 00000000 S binder
-root      110   2     0      0     ffffffff 00000000 S usb_bam_wq
-root      111   2     0      0     ffffffff 00000000 S krfcommd
-root      112   2     0      0     ffffffff 00000000 S bam_dmux_rx
-root      113   2     0      0     ffffffff 00000000 S bam_dmux_tx
-root      114   2     0      0     ffffffff 00000000 S rq_stats
-root      115   2     0      0     ffffffff 00000000 S deferwq
-root      117   2     0      0     ffffffff 00000000 S irq/361-MAX1704
-root      119   2     0      0     ffffffff 00000000 S mmcqd/1
-root      120   2     0      0     ffffffff 00000000 S mmcqd/1rpmb
-root      121   2     0      0     ffffffff 00000000 S wl_event_handle
-root      122   2     0      0     ffffffff 00000000 S dhd_watchdog_th
-root      123   2     0      0     ffffffff 00000000 S dhd_dpc
-root      124   2     0      0     ffffffff 00000000 S dhd_rxf
-root      125   2     0      0     ffffffff 00000000 S dhd_sysioc
-root      126   2     0      0     ffffffff 00000000 S vibrator
-root      127   2     0      0     ffffffff 00000000 S max1462x
-root      128   2     0      0     ffffffff 00000000 S irq/310-maxim_m
-root      129   2     0      0     ffffffff 00000000 S irq/311-maxim_m
-root      130   1     8780   576   ffffffff 00000000 S /sbin/ueventd
-root      132   2     0      0     ffffffff 00000000 S jbd2/mmcblk0p25
-root      133   2     0      0     ffffffff 00000000 S ext4-dio-unwrit
-root      136   2     0      0     ffffffff 00000000 S flush-179:0
-root      138   2     0      0     ffffffff 00000000 S jbd2/mmcblk0p28
-root      139   2     0      0     ffffffff 00000000 S ext4-dio-unwrit
-root      143   2     0      0     ffffffff 00000000 S jbd2/mmcblk0p27
-root      144   2     0      0     ffffffff 00000000 S ext4-dio-unwrit
-root      145   2     0      0     ffffffff 00000000 S jbd2/mmcblk0p16
-root      146   2     0      0     ffffffff 00000000 S ext4-dio-unwrit
-logd      169   1     18632  2740  ffffffff 00000000 S /system/bin/logd
-logd      216   169   18632  2740  ffffffff 00000000 S logd.reader
-logd      217   169   18632  2740  ffffffff 00000000 S logd.writer
-logd      218   169   18632  2740  ffffffff 00000000 S logd
-logd      244   169   18632  2740  ffffffff 00000000 S logd.auditd
-root      170   1     9832   304   ffffffff 00000000 S /sbin/healthd
-root      171   1     10620  1240  ffffffff 00000000 S /system/bin/lmkd
-system    172   1     9452   676   ffffffff 00000000 S /system/bin/servicemanager
-root      173   1     18028  1652  ffffffff 00000000 S /system/bin/vold
-root      223   173   18028  1652  ffffffff 00000000 S vold
-root      226   173   18028  1652  ffffffff 00000000 S vold
-root      174   2     0      0     ffffffff 00000000 S IPCRTR
-root      175   2     0      0     ffffffff 00000000 S sb-1
-root      177   2     0      0     ffffffff 00000000 S ipc_rtr_q6_ipcr
-root      179   2     0      0     ffffffff 00000000 S ngd_msm_ctrl_ng
-system    180   1     146792 9724  ffffffff 00000000 S /system/bin/surfaceflinger
-system    240   180   146792 9724  ffffffff 00000000 S Binder_1
-system    242   180   146792 9724  ffffffff 00000000 S DispSync
-system    243   180   146792 9724  ffffffff 00000000 S Binder_2
-system    361   180   146792 9724  ffffffff 00000000 S hwcUeventThread
-system    362   180   146792 9724  ffffffff 00000000 S hwcVsyncThread
-system    396   180   146792 9724  ffffffff 00000000 S GL updater
-system    397   180   146792 9724  ffffffff 00000000 S surfaceflinger
-system    398   180   146792 9724  ffffffff 00000000 S EventThread
-system    399   180   146792 9724  ffffffff 00000000 S surfaceflinger
-system    400   180   146792 9724  ffffffff 00000000 S EventThread
-system    401   180   146792 9724  ffffffff 00000000 S EventControl
-system    575   180   146792 9724  ffffffff 00000000 S Binder_3
-system    1501  180   146792 9724  ffffffff 00000000 S Binder_4
-system    5633  180   146792 9724  ffffffff 00000000 S Binder_5
-nobody    181   1     19792  1112  ffffffff 00000000 S /system/bin/rmt_storage
-nobody    571   181   19792  1112  ffffffff 00000000 S rmt_storage
-nobody    572   181   19792  1112  ffffffff 00000000 S rmt_storage
-nobody    573   181   19792  1112  ffffffff 00000000 S rmt_storage
-nobody    574   181   19792  1112  ffffffff 00000000 S rmt_storage
-system    182   1     11100  992   ffffffff 00000000 S /system/bin/qseecomd
-root      183   2     0      0     ffffffff 00000000 S msm_slim_qmi_cl
-root      184   2     0      0     ffffffff 00000000 S msm_qmi_rtx_q
-shell     185   1     9316   716   c047451c b6f58da8 S /system/bin/sh
-root      187   1     9200   368   ffffffff 00000000 S /system/bin/subsystem_ramdump
-root      188   1     22828  1404  ffffffff 00000000 S /system/bin/netd
-root      548   188   22828  1404  ffffffff 00000000 S netd
-root      549   188   22828  1404  ffffffff 00000000 S netd
-root      550   188   22828  1404  ffffffff 00000000 S netd
-root      551   188   22828  1404  ffffffff 00000000 S netd
-root      552   188   22828  1404  ffffffff 00000000 S netd
-root      553   188   22828  1404  ffffffff 00000000 S netd
-root      554   188   22828  1404  ffffffff 00000000 S netd
-root      555   188   22828  1404  ffffffff 00000000 S netd
-root      189   1     10048  848   ffffffff 00000000 S /system/bin/debuggerd
-radio     191   1     35988  4712  ffffffff 00000000 S /system/bin/rild
-radio     335   191   35988  4712  ffffffff 00000000 S rild
-radio     343   191   35988  4712  ffffffff 00000000 S rild
-radio     346   191   35988  4712  ffffffff 00000000 S rild
-radio     584   191   35988  4712  ffffffff 00000000 S rild
-radio     585   191   35988  4712  ffffffff 00000000 S rild
-radio     587   191   35988  4712  ffffffff 00000000 S rild
-radio     588   191   35988  4712  ffffffff 00000000 S rild
-radio     589   191   35988  4712  ffffffff 00000000 S rild
-radio     591   191   35988  4712  ffffffff 00000000 S rild
-radio     592   191   35988  4712  ffffffff 00000000 S rild
-radio     593   191   35988  4712  ffffffff 00000000 S rild
-radio     594   191   35988  4712  ffffffff 00000000 S rild
-drm       192   1     26084  3832  ffffffff 00000000 S /system/bin/drmserver
-drm       419   192   26084  3832  ffffffff 00000000 S Binder_1
-media     194   1     106516 8584  ffffffff 00000000 S /system/bin/mediaserver
-media     755   194   106516 8584  ffffffff 00000000 S ApmTone
-media     756   194   106516 8584  ffffffff 00000000 S ApmAudio
-media     757   194   106516 8584  ffffffff 00000000 S ApmOutput
-media     758   194   106516 8584  ffffffff 00000000 S mediaserver
-media     759   194   106516 8584  ffffffff 00000000 S FastMixer
-media     871   194   106516 8584  ffffffff 00000000 S AudioOut_2
-media     872   194   106516 8584  ffffffff 00000000 S AudioOut_4
-media     873   194   106516 8584  ffffffff 00000000 S FastMixer
-media     874   194   106516 8584  ffffffff 00000000 S AudioOut_6
-media     878   194   106516 8584  ffffffff 00000000 S Binder_1
-media     879   194   106516 8584  ffffffff 00000000 S Binder_2
-media     1133  194   106516 8584  ffffffff 00000000 S Binder_3
-install   195   1     9408   704   ffffffff 00000000 S /system/bin/installd
-keystore  197   1     12536  1848  ffffffff 00000000 S /system/bin/keystore
-radio     198   1     18856  636   ffffffff 00000000 S /system/bin/bridgemgrd
-radio     288   198   18856  636   ffffffff 00000000 S bridgemgrd
-radio     602   198   18856  636   ffffffff 00000000 S bridgemgrd
-radio     603   198   18856  636   ffffffff 00000000 S bridgemgrd
-radio     841   198   18856  636   ffffffff 00000000 S bridgemgrd
-radio     199   1     24060  732   ffffffff 00000000 S /system/bin/qmuxd
-radio     293   199   24060  732   ffffffff 00000000 S qmuxd
-radio     576   199   24060  732   ffffffff 00000000 S qmuxd
-radio     577   199   24060  732   ffffffff 00000000 S qmuxd
-radio     578   199   24060  732   ffffffff 00000000 S qmuxd
-radio     579   199   24060  732   ffffffff 00000000 S qmuxd
-radio     580   199   24060  732   ffffffff 00000000 S qmuxd
-radio     581   199   24060  732   ffffffff 00000000 S qmuxd
-radio     582   199   24060  732   ffffffff 00000000 S qmuxd
-radio     583   199   24060  732   ffffffff 00000000 S qmuxd
-radio     200   1     20036  996   ffffffff 00000000 S /system/bin/netmgrd
-radio     289   200   20036  996   ffffffff 00000000 S netmgrd
-radio     736   200   20036  996   ffffffff 00000000 S netmgrd
-radio     746   200   20036  996   ffffffff 00000000 S netmgrd
-radio     747   200   20036  996   ffffffff 00000000 S netmgrd
-radio     748   200   20036  996   ffffffff 00000000 S netmgrd
-nobody    201   1     59912  1748  ffffffff 00000000 S /system/bin/sensors.qcom
-nobody    290   201   59912  1748  ffffffff 00000000 S sensors.qcom
-nobody    292   201   59912  1748  ffffffff 00000000 S sensors.qcom
-nobody    560   201   59912  1748  ffffffff 00000000 S sensors.qcom
-nobody    563   201   59912  1748  ffffffff 00000000 S sensors.qcom
-nobody    564   201   59912  1748  ffffffff 00000000 S sensors.qcom
-nobody    605   201   59912  1748  ffffffff 00000000 S sensors.qcom
-nobody    614   201   59912  1748  ffffffff 00000000 S sensors.qcom
-nobody    621   201   59912  1748  ffffffff 00000000 S sensors.qcom
-nobody    622   201   59912  1748  ffffffff 00000000 S sensors.qcom
-nobody    623   201   59912  1748  ffffffff 00000000 S sensors.qcom
-nobody    624   201   59912  1748  ffffffff 00000000 S sensors.qcom
-nobody    625   201   59912  1748  ffffffff 00000000 S sensors.qcom
-nobody    626   201   59912  1748  ffffffff 00000000 S sensors.qcom
-nobody    627   201   59912  1748  ffffffff 00000000 S sensors.qcom
-nobody    628   201   59912  1748  ffffffff 00000000 S sensors.qcom
-nobody    629   201   59912  1748  ffffffff 00000000 S sensors.qcom
-nobody    633   201   59912  1748  ffffffff 00000000 S sensors.qcom
-nobody    643   201   59912  1748  ffffffff 00000000 S sensors.qcom
-nobody    650   201   59912  1748  ffffffff 00000000 S sensors.qcom
-nobody    651   201   59912  1748  ffffffff 00000000 S sensors.qcom
-nobody    760   201   59912  1748  ffffffff 00000000 S sensors.qcom
-nobody    763   201   59912  1748  ffffffff 00000000 S sensors.qcom
-nobody    784   201   59912  1748  ffffffff 00000000 S sensors.qcom
-nobody    790   201   59912  1748  ffffffff 00000000 S sensors.qcom
-nobody    792   201   59912  1748  ffffffff 00000000 S sensors.qcom
-nobody    794   201   59912  1748  ffffffff 00000000 S sensors.qcom
-nobody    796   201   59912  1748  ffffffff 00000000 S sensors.qcom
-nobody    798   201   59912  1748  ffffffff 00000000 S sensors.qcom
-nobody    800   201   59912  1748  ffffffff 00000000 S sensors.qcom
-nobody    802   201   59912  1748  ffffffff 00000000 S sensors.qcom
-nobody    804   201   59912  1748  ffffffff 00000000 S sensors.qcom
-nobody    806   201   59912  1748  ffffffff 00000000 S sensors.qcom
-nobody    808   201   59912  1748  ffffffff 00000000 S sensors.qcom
-nobody    810   201   59912  1748  ffffffff 00000000 S sensors.qcom
-nobody    812   201   59912  1748  ffffffff 00000000 S sensors.qcom
-nobody    814   201   59912  1748  ffffffff 00000000 S sensors.qcom
-nobody    816   201   59912  1748  ffffffff 00000000 S sensors.qcom
-nobody    818   201   59912  1748  ffffffff 00000000 S sensors.qcom
-nobody    820   201   59912  1748  ffffffff 00000000 S sensors.qcom
-nobody    822   201   59912  1748  ffffffff 00000000 S sensors.qcom
-nobody    824   201   59912  1748  ffffffff 00000000 S sensors.qcom
-nobody    1593  201   59912  1748  ffffffff 00000000 S sensors.qcom
-nobody    1600  201   59912  1748  ffffffff 00000000 S sensors.qcom
-root      204   1     58772  1524  ffffffff 00000000 S /system/bin/thermal-engine-hh
-root      247   204   58772  1524  ffffffff 00000000 S thermal-engine-
-root      250   204   58772  1524  ffffffff 00000000 S thermal-engine-
-root      252   204   58772  1524  ffffffff 00000000 S thermal-engine-
-root      253   204   58772  1524  ffffffff 00000000 S thermal-engine-
-root      254   204   58772  1524  ffffffff 00000000 S thermal-engine-
-root      255   204   58772  1524  ffffffff 00000000 S thermal-engine-
-root      257   204   58772  1524  ffffffff 00000000 S thermal-engine-
-root      258   204   58772  1524  ffffffff 00000000 S thermal-engine-
-root      259   204   58772  1524  ffffffff 00000000 S thermal-engine-
-root      260   204   58772  1524  ffffffff 00000000 S thermal-engine-
-root      261   204   58772  1524  ffffffff 00000000 S thermal-engine-
-root      262   204   58772  1524  ffffffff 00000000 S thermal-engine-
-root      263   204   58772  1524  ffffffff 00000000 S thermal-engine-
-root      264   204   58772  1524  ffffffff 00000000 S thermal-engine-
-root      265   204   58772  1524  ffffffff 00000000 S thermal-engine-
-root      266   204   58772  1524  ffffffff 00000000 S thermal-engine-
-root      267   204   58772  1524  ffffffff 00000000 S thermal-engine-
-root      268   204   58772  1524  ffffffff 00000000 S thermal-engine-
-root      269   204   58772  1524  ffffffff 00000000 S thermal-engine-
-root      270   204   58772  1524  ffffffff 00000000 S thermal-engine-
-root      272   204   58772  1524  ffffffff 00000000 S thermal-engine-
-root      273   204   58772  1524  ffffffff 00000000 S thermal-engine-
-root      275   204   58772  1524  ffffffff 00000000 S thermal-engine-
-root      276   204   58772  1524  ffffffff 00000000 S thermal-engine-
-root      277   204   58772  1524  ffffffff 00000000 S thermal-engine-
-root      278   204   58772  1524  ffffffff 00000000 S thermal-engine-
-root      280   204   58772  1524  ffffffff 00000000 S thermal-engine-
-root      281   204   58772  1524  ffffffff 00000000 S thermal-engine-
-root      282   204   58772  1524  ffffffff 00000000 S thermal-engine-
-root      283   204   58772  1524  ffffffff 00000000 S thermal-engine-
-root      284   204   58772  1524  ffffffff 00000000 S thermal-engine-
-root      286   204   58772  1524  ffffffff 00000000 S thermal-engine-
-root      287   204   58772  1524  ffffffff 00000000 S thermal-engine-
-root      295   204   58772  1524  ffffffff 00000000 S thermal-engine-
-root      297   204   58772  1524  ffffffff 00000000 S thermal-engine-
-root      299   204   58772  1524  ffffffff 00000000 S thermal-engine-
-root      300   204   58772  1524  ffffffff 00000000 S thermal-engine-
-root      301   204   58772  1524  ffffffff 00000000 S thermal-engine-
-root      559   204   58772  1524  ffffffff 00000000 S thermal-engine-
-root      596   204   58772  1524  ffffffff 00000000 S thermal-engine-
-root      600   204   58772  1524  ffffffff 00000000 S thermal-engine-
-root      601   204   58772  1524  ffffffff 00000000 S thermal-engine-
-root      205   1     1482684 53160 ffffffff 00000000 S zygote
-root      14357 205   1482684 53160 ffffffff 00000000 S ReferenceQueueD
-root      14358 205   1482684 53160 ffffffff 00000000 S FinalizerDaemon
-root      14359 205   1482684 53160 ffffffff 00000000 S FinalizerWatchd
-root      14360 205   1482684 53160 ffffffff 00000000 S HeapTrimmerDaem
-root      14361 205   1482684 53160 ffffffff 00000000 S GCDaemon
-media_rw  206   1     15400  5240  ffffffff 00000000 S /system/bin/sdcard
-media_rw  227   206   15400  5240  ffffffff 00000000 S sdcard
-media_rw  228   206   15400  5240  ffffffff 00000000 S sdcard
-camera    207   1     16300  4440  ffffffff 00000000 S /system/bin/mm-qcamera-daemon
-system    208   1     20500  1236  ffffffff 00000000 S /system/bin/time_daemon
-system    308   208   20500  1236  ffffffff 00000000 S time_daemon
-system    561   208   20500  1236  ffffffff 00000000 S time_daemon
-system    597   208   20500  1236  ffffffff 00000000 S time_daemon
-system    598   208   20500  1236  ffffffff 00000000 S time_daemon
-system    599   208   20500  1236  ffffffff 00000000 S time_daemon
-shell     209   1     16984  312   ffffffff 00000000 S /sbin/adbd
-shell     210   209   16984  312   ffffffff 00000000 S adbd
-shell     211   209   16984  312   ffffffff 00000000 S adbd
-shell     212   209   16984  308   ffffffff 00000000 S adbd
-shell     14445 209   16984  308   ffffffff 00000000 S adbd
-root      214   2     0      0     ffffffff 00000000 S irq/288-wcd9xxx
-root      219   2     0      0     ffffffff 00000000 S kauditd
-root      311   2     0      0     ffffffff 00000000 D msm_thermal:hot
-root      312   2     0      0     ffffffff 00000000 D msm_thermal:fre
-system    348   182   15288  564   ffffffff 00000000 S /system/bin/qseecomd
-system    349   348   15288  564   ffffffff 00000000 S qseecomd
-system    351   348   15288  564   ffffffff 00000000 S qseecomd
-system    386   348   15288  564   ffffffff 00000000 S qseecomd
-system    387   348   15288  564   ffffffff 00000000 S qseecomd
-root      360   2     0      0     ffffffff 00000000 D mdss_fb0
-root      557   2     0      0     ffffffff 00000000 S kworker/0:2H
-root      558   2     0      0     ffffffff 00000000 S IPCRTR
-root      562   2     0      0     ffffffff 00000000 S ipc_rtr_smd_ipc
-system    764   205   1701620 103200 ffffffff 00000000 S system_server
-system    767   764   1701620 103200 ffffffff 00000000 S Heap thread poo
-system    768   764   1701620 103200 ffffffff 00000000 S Heap thread poo
-system    770   764   1701620 103200 ffffffff 00000000 S Heap thread poo
-system    773   764   1701620 103200 ffffffff 00000000 S Signal Catcher
-system    774   764   1701620 103200 ffffffff 00000000 S JDWP
-system    775   764   1701620 103200 ffffffff 00000000 S ReferenceQueueD
-system    776   764   1701620 103200 ffffffff 00000000 S FinalizerDaemon
-system    777   764   1701620 103200 ffffffff 00000000 S FinalizerWatchd
-system    778   764   1701620 103200 ffffffff 00000000 S HeapTrimmerDaem
-system    779   764   1701620 103200 ffffffff 00000000 S GCDaemon
-system    780   764   1701620 103200 ffffffff 00000000 S Binder_1
-system    781   764   1701620 103200 ffffffff 00000000 S Binder_2
-system    782   764   1701620 103200 ffffffff 00000000 S system_server
-system    783   764   1701620 103200 ffffffff 00000000 S system_server
-system    785   764   1701620 103200 ffffffff 00000000 S system_server
-system    786   764   1701620 103200 ffffffff 00000000 S system_server
-system    788   764   1701620 103200 ffffffff 00000000 S system_server
-system    789   764   1701620 103200 ffffffff 00000000 S system_server
-system    791   764   1701620 103200 ffffffff 00000000 S system_server
-system    793   764   1701620 103200 ffffffff 00000000 S system_server
-system    795   764   1701620 103200 ffffffff 00000000 S system_server
-system    797   764   1701620 103200 ffffffff 00000000 S system_server
-system    799   764   1701620 103200 ffffffff 00000000 S system_server
-system    801   764   1701620 103200 ffffffff 00000000 S system_server
-system    803   764   1701620 103200 ffffffff 00000000 S system_server
-system    805   764   1701620 103200 ffffffff 00000000 S system_server
-system    807   764   1701620 103200 ffffffff 00000000 S system_server
-system    809   764   1701620 103200 ffffffff 00000000 S system_server
-system    811   764   1701620 103200 ffffffff 00000000 S system_server
-system    813   764   1701620 103200 ffffffff 00000000 S system_server
-system    815   764   1701620 103200 ffffffff 00000000 S system_server
-system    817   764   1701620 103200 ffffffff 00000000 S system_server
-system    819   764   1701620 103200 ffffffff 00000000 S system_server
-system    821   764   1701620 103200 ffffffff 00000000 S system_server
-system    823   764   1701620 103200 ffffffff 00000000 S system_server
-system    826   764   1701620 103200 ffffffff 00000000 S SensorEventAckR
-system    827   764   1701620 103200 ffffffff 00000000 S SensorService
-system    828   764   1701620 103200 ffffffff 00000000 S android.bg
-system    829   764   1701620 103200 ffffffff 00000000 S ActivityManager
-system    830   764   1701620 103200 ffffffff 00000000 S FileObserver
-system    831   764   1701620 103200 ffffffff 00000000 S android.fg
-system    832   764   1701620 103200 ffffffff 00000000 S android.ui
-system    833   764   1701620 103200 ffffffff 00000000 S android.io
-system    834   764   1701620 103200 ffffffff 00000000 S android.display
-system    835   764   1701620 103200 ffffffff 00000000 S CpuTracker
-system    836   764   1701620 103200 ffffffff 00000000 S PowerManagerSer
-system    837   764   1701620 103200 ffffffff 00000000 S system_server
-system    838   764   1701620 103200 ffffffff 00000000 S system_server
-system    839   764   1701620 103200 ffffffff 00000000 S BatteryStats_wa
-system    840   764   1701620 103200 ffffffff 00000000 S PackageManager
-system    842   764   1701620 103200 ffffffff 00000000 S PackageInstalle
-system    844   764   1701620 103200 ffffffff 00000000 S AlarmManager
-system    845   764   1701620 103200 ffffffff 00000000 S UEventObserver
-system    853   764   1701620 103200 ffffffff 00000000 S InputDispatcher
-system    854   764   1701620 103200 ffffffff 00000000 S InputReader
-system    857   764   1701620 103200 ffffffff 00000000 S MountService
-system    858   764   1701620 103200 ffffffff 00000000 S VoldConnector
-system    860   764   1701620 103200 ffffffff 00000000 S NetdConnector
-system    861   764   1701620 103200 ffffffff 00000000 S NetworkStats
-system    862   764   1701620 103200 ffffffff 00000000 S NetworkPolicy
-system    863   764   1701620 103200 ffffffff 00000000 S WifiP2pService
-system    864   764   1701620 103200 ffffffff 00000000 S WifiStateMachin
-system    865   764   1701620 103200 ffffffff 00000000 S WifiService
-system    866   764   1701620 103200 ffffffff 00000000 S ConnectivitySer
-system    867   764   1701620 103200 ffffffff 00000000 S NsdService
-system    868   764   1701620 103200 ffffffff 00000000 S mDnsConnector
-system    869   764   1701620 103200 ffffffff 00000000 S ranker
-system    870   764   1701620 103200 ffffffff 00000000 S AudioService
-system    882   764   1701620 103200 ffffffff 00000000 S WifiWatchdogSta
-system    883   764   1701620 103200 ffffffff 00000000 S WifiManager
-system    884   764   1701620 103200 ffffffff 00000000 S WifiScanningSer
-system    885   764   1701620 103200 ffffffff 00000000 S WifiRttService
-system    886   764   1701620 103200 ffffffff 00000000 S EthernetService
-system    887   764   1701620 103200 ffffffff 00000000 S backup
-system    889   764   1701620 103200 ffffffff 00000000 S Thread-69
-system    892   764   1701620 103200 ffffffff 00000000 S LazyTaskWriterT
-system    893   764   1701620 103200 ffffffff 00000000 S UsbService host
-system    894   764   1701620 103200 ffffffff 00000000 S Thread-73
-system    942   764   1701620 103200 ffffffff 00000000 S Binder_3
-system    1079  764   1701620 103200 ffffffff 00000000 S watchdog
-system    1094  764   1701620 103200 ffffffff 00000000 S SoundPool
-system    1095  764   1701620 103200 ffffffff 00000000 S SoundPoolThread
-system    1108  764   1701620 103200 ffffffff 00000000 S Binder_4
-system    1109  764   1701620 103200 ffffffff 00000000 S Binder_5
-system    1186  764   1701620 103200 ffffffff 00000000 S FLP Service Cal
-system    1188  764   1701620 103200 ffffffff 00000000 S FLP Service Cal
-system    1191  764   1701620 103200 ffffffff 00000000 S NetworkTimeUpda
-system    1192  764   1701620 103200 ffffffff 00000000 S FLP Service Cal
-system    1226  764   1701620 103200 ffffffff 00000000 S Binder_6
-system    1233  764   1701620 103200 ffffffff 00000000 S Binder_7
-system    1247  764   1701620 103200 ffffffff 00000000 S FLP Service Cal
-system    1256  764   1701620 103200 ffffffff 00000000 S Binder_8
-system    1260  764   1701620 103200 ffffffff 00000000 S WifiMonitor
-system    1271  764   1701620 103200 ffffffff 00000000 S Binder_9
-system    1288  764   1701620 103200 ffffffff 00000000 S FLP Service Cal
-system    1289  764   1701620 103200 ffffffff 00000000 S FLP Service Cal
-system    1319  764   1701620 103200 ffffffff 00000000 S FLP Service Cal
-system    1320  764   1701620 103200 ffffffff 00000000 S FLP Service Cal
-system    1367  764   1701620 103200 ffffffff 00000000 S Thread-89
-system    1391  764   1701620 103200 ffffffff 00000000 S AsyncQueryWorke
-system    1654  764   1701620 103200 ffffffff 00000000 S Binder_A
-system    1693  764   1701620 103200 ffffffff 00000000 S NetworkMonitorN
-system    1695  764   1701620 103200 ffffffff 00000000 S DhcpStateMachin
-system    1781  764   1701620 103200 ffffffff 00000000 S AsyncTask #1
-system    1782  764   1701620 103200 ffffffff 00000000 S AsyncTask #2
-system    2097  764   1701620 103200 ffffffff 00000000 S AsyncTask #3
-system    2124  764   1701620 103200 ffffffff 00000000 S SyncHandler-0
-system    2905  764   1701620 103200 ffffffff 00000000 S PowerManagerSer
-system    4226  764   1701620 103200 ffffffff 00000000 S AsyncTask #4
-system    4265  764   1701620 103200 ffffffff 00000000 S UsbDebuggingMan
-system    5717  764   1701620 103200 ffffffff 00000000 S GL updater
-system    6709  764   1701620 103200 ffffffff 00000000 S Binder_B
-wifi      888   1     12568  2672  ffffffff 00000000 S /system/bin/wpa_supplicant
-u0_a20    915   205   1616624 108684 ffffffff 00000000 S com.android.systemui
-u0_a20    919   915   1616624 108684 ffffffff 00000000 S Heap thread poo
-u0_a20    920   915   1616624 108684 ffffffff 00000000 S Heap thread poo
-u0_a20    921   915   1616624 108684 ffffffff 00000000 S Heap thread poo
-u0_a20    925   915   1616624 108684 ffffffff 00000000 S Signal Catcher
-u0_a20    926   915   1616624 108684 ffffffff 00000000 S JDWP
-u0_a20    927   915   1616624 108684 ffffffff 00000000 S ReferenceQueueD
-u0_a20    928   915   1616624 108684 ffffffff 00000000 S FinalizerDaemon
-u0_a20    929   915   1616624 108684 ffffffff 00000000 S FinalizerWatchd
-u0_a20    930   915   1616624 108684 ffffffff 00000000 S HeapTrimmerDaem
-u0_a20    931   915   1616624 108684 ffffffff 00000000 S GCDaemon
-u0_a20    933   915   1616624 108684 ffffffff 00000000 S Binder_1
-u0_a20    934   915   1616624 108684 ffffffff 00000000 S Binder_2
-u0_a20    964   915   1616624 108684 ffffffff 00000000 S SoundPool
-u0_a20    965   915   1616624 108684 ffffffff 00000000 S SoundPoolThread
-u0_a20    970   915   1616624 108684 ffffffff 00000000 S Recents-TaskRes
-u0_a20    1078  915   1616624 108684 ffffffff 00000000 S SystemUI Storag
-u0_a20    1378  915   1616624 108684 ffffffff 00000000 S PhoneStatusBar
-u0_a20    1381  915   1616624 108684 ffffffff 00000000 S WifiManager
-u0_a20    1416  915   1616624 108684 ffffffff 00000000 S ConnectivityMan
-u0_a20    1428  915   1616624 108684 ffffffff 00000000 S Binder_3
-u0_a20    1431  915   1616624 108684 ffffffff 00000000 S FlashlightContr
-u0_a20    1434  915   1616624 108684 ffffffff 00000000 S AsyncTask #1
-u0_a20    1435  915   1616624 108684 ffffffff 00000000 S QSTileHost
-u0_a20    1438  915   1616624 108684 ffffffff 00000000 S AsyncTask #2
-u0_a20    1441  915   1616624 108684 ffffffff 00000000 S RenderThread
-u0_a20    1442  915   1616624 108684 ffffffff 00000000 S AsyncTask #3
-u0_a20    1565  915   1616624 108684 ffffffff 00000000 S hwuiTask1
-u0_a20    1566  915   1616624 108684 ffffffff 00000000 S hwuiTask2
-u0_a20    1637  915   1616624 108684 ffffffff 00000000 S AsyncTask #4
-u0_a20    1692  915   1616624 108684 ffffffff 00000000 S GL updater
-u0_a20    1807  915   1616624 108684 ffffffff 00000000 S RenderThread
-u0_a20    4480  915   1616624 108684 ffffffff 00000000 S Binder_4
-u0_a6     936   205   1506908 56892 ffffffff 00000000 S android.process.media
-u0_a6     943   936   1506908 56892 ffffffff 00000000 S Heap thread poo
-u0_a6     944   936   1506908 56892 ffffffff 00000000 S Heap thread poo
-u0_a6     945   936   1506908 56892 ffffffff 00000000 S Heap thread poo
-u0_a6     947   936   1506908 56892 ffffffff 00000000 S Signal Catcher
-u0_a6     949   936   1506908 56892 ffffffff 00000000 S JDWP
-u0_a6     950   936   1506908 56892 ffffffff 00000000 S ReferenceQueueD
-u0_a6     951   936   1506908 56892 ffffffff 00000000 S FinalizerDaemon
-u0_a6     952   936   1506908 56892 ffffffff 00000000 S FinalizerWatchd
-u0_a6     953   936   1506908 56892 ffffffff 00000000 S HeapTrimmerDaem
-u0_a6     954   936   1506908 56892 ffffffff 00000000 S GCDaemon
-u0_a6     956   936   1506908 56892 ffffffff 00000000 S Binder_1
-u0_a6     957   936   1506908 56892 ffffffff 00000000 S Binder_2
-u0_a6     1007  936   1506908 56892 ffffffff 00000000 S thumbs thread
-u0_a6     1020  936   1506908 56892 ffffffff 00000000 S MtpServer
-u0_a6     2810  936   1506908 56892 ffffffff 00000000 S DownloadReceive
-u0_a6     4917  936   1506908 56892 ffffffff 00000000 S Binder_3
-u0_a6     5816  936   1506908 56892 ffffffff 00000000 S Binder_4
-u0_a6     8575  936   1506908 56892 ffffffff 00000000 S Binder_5
-u0_a22    1111  205   1526156 42532 ffffffff 00000000 S com.google.android.googlequicksearchbox:interactor
-u0_a22    1113  1111  1526156 42532 ffffffff 00000000 S Heap thread poo
-u0_a22    1114  1111  1526156 42532 ffffffff 00000000 S Heap thread poo
-u0_a22    1116  1111  1526156 42532 ffffffff 00000000 S Heap thread poo
-u0_a22    1121  1111  1526156 42532 ffffffff 00000000 S Signal Catcher
-u0_a22    1124  1111  1526156 42532 ffffffff 00000000 S JDWP
-u0_a22    1125  1111  1526156 42532 ffffffff 00000000 S ReferenceQueueD
-u0_a22    1126  1111  1526156 42532 ffffffff 00000000 S FinalizerDaemon
-u0_a22    1127  1111  1526156 42532 ffffffff 00000000 S FinalizerWatchd
-u0_a22    1128  1111  1526156 42532 ffffffff 00000000 S HeapTrimmerDaem
-u0_a22    1129  1111  1526156 42532 ffffffff 00000000 S GCDaemon
-u0_a22    1131  1111  1526156 42532 ffffffff 00000000 S Binder_1
-u0_a22    1132  1111  1526156 42532 ffffffff 00000000 S Binder_2
-u0_a22    1561  1111  1526156 42532 ffffffff 00000000 S AsyncTask #1
-u0_a51    1136  205   1515064 46788 ffffffff 00000000 S com.google.android.inputmethod.pinyin
-u0_a51    1142  1136  1515064 46788 ffffffff 00000000 S Heap thread poo
-u0_a51    1143  1136  1515064 46788 ffffffff 00000000 S Heap thread poo
-u0_a51    1144  1136  1515064 46788 ffffffff 00000000 S Heap thread poo
-u0_a51    1145  1136  1515064 46788 ffffffff 00000000 S Signal Catcher
-u0_a51    1146  1136  1515064 46788 ffffffff 00000000 S JDWP
-u0_a51    1147  1136  1515064 46788 ffffffff 00000000 S ReferenceQueueD
-u0_a51    1148  1136  1515064 46788 ffffffff 00000000 S FinalizerDaemon
-u0_a51    1149  1136  1515064 46788 ffffffff 00000000 S FinalizerWatchd
-u0_a51    1151  1136  1515064 46788 ffffffff 00000000 S HeapTrimmerDaem
-u0_a51    1152  1136  1515064 46788 ffffffff 00000000 S GCDaemon
-u0_a51    1153  1136  1515064 46788 ffffffff 00000000 S Binder_1
-u0_a51    1154  1136  1515064 46788 ffffffff 00000000 S Binder_2
-u0_a51    1330  1136  1515064 46788 ffffffff 00000000 S GAThread
-u0_a51    1331  1136  1515064 46788 ffffffff 00000000 S measurement-1
-u0_a51    1336  1136  1515064 46788 ffffffff 00000000 S pool-1-thread-1
-u0_a51    1503  1136  1515064 46788 ffffffff 00000000 S AsyncTask #1
-u0_a51    1622  1136  1515064 46788 ffffffff 00000000 S AsyncTask #2
-nfc       1199  205   1511808 46336 ffffffff 00000000 S com.android.nfc
-nfc       1208  1199  1511808 46336 ffffffff 00000000 S Heap thread poo
-nfc       1209  1199  1511808 46336 ffffffff 00000000 S Heap thread poo
-nfc       1210  1199  1511808 46336 ffffffff 00000000 S Heap thread poo
-nfc       1211  1199  1511808 46336 ffffffff 00000000 S Signal Catcher
-nfc       1212  1199  1511808 46336 ffffffff 00000000 S JDWP
-nfc       1213  1199  1511808 46336 ffffffff 00000000 S ReferenceQueueD
-nfc       1214  1199  1511808 46336 ffffffff 00000000 S FinalizerDaemon
-nfc       1215  1199  1511808 46336 ffffffff 00000000 S FinalizerWatchd
-nfc       1216  1199  1511808 46336 ffffffff 00000000 S HeapTrimmerDaem
-nfc       1219  1199  1511808 46336 ffffffff 00000000 S GCDaemon
-nfc       1220  1199  1511808 46336 ffffffff 00000000 S Binder_1
-nfc       1221  1199  1511808 46336 ffffffff 00000000 S Binder_2
-nfc       1385  1199  1511808 46336 ffffffff 00000000 S AsyncTask #1
-nfc       1388  1199  1511808 46336 ffffffff 00000000 S AsyncTask #1
-nfc       1393  1199  1511808 46336 ffffffff 00000000 S AsyncTask #1
-nfc       1408  1199  1511808 46336 ffffffff 00000000 S AsyncTask #1
-nfc       1409  1199  1511808 46336 ffffffff 00000000 S AsyncTask #1
-nfc       1425  1199  1511808 46336 ffffffff 00000000 S AsyncTask #1
-nfc       1573  1199  1511808 46336 ffffffff 00000000 S Thread-55
-nfc       1574  1199  1511808 46336 ffffffff 00000000 S Thread-56
-nfc       1575  1199  1511808 46336 ffffffff 00000000 S Thread-57
-nfc       1577  1199  1511808 46336 ffffffff 00000000 S SoundPool
-nfc       1578  1199  1511808 46336 ffffffff 00000000 S SoundPoolThread
-nfc       2906  1199  1511808 46336 ffffffff 00000000 S AsyncTask #2
-nfc       2915  1199  1511808 46336 ffffffff 00000000 S AsyncTask #3
-nfc       5610  1199  1511808 46336 ffffffff 00000000 S AsyncTask #4
-nfc       5719  1199  1511808 46336 ffffffff 00000000 S AsyncTask #5
-radio     1234  205   1493064 38832 ffffffff 00000000 S com.redbend.vdmc
-radio     1236  1234  1493064 38832 ffffffff 00000000 S Heap thread poo
-radio     1237  1234  1493064 38832 ffffffff 00000000 S Heap thread poo
-radio     1238  1234  1493064 38832 ffffffff 00000000 S Heap thread poo
-radio     1244  1234  1493064 38832 ffffffff 00000000 S Signal Catcher
-radio     1245  1234  1493064 38832 ffffffff 00000000 S JDWP
-radio     1246  1234  1493064 38832 ffffffff 00000000 S ReferenceQueueD
-radio     1248  1234  1493064 38832 ffffffff 00000000 S FinalizerDaemon
-radio     1249  1234  1493064 38832 ffffffff 00000000 S FinalizerWatchd
-radio     1250  1234  1493064 38832 ffffffff 00000000 S HeapTrimmerDaem
-radio     1251  1234  1493064 38832 ffffffff 00000000 S GCDaemon
-radio     1252  1234  1493064 38832 ffffffff 00000000 S Binder_1
-radio     1257  1234  1493064 38832 ffffffff 00000000 S Binder_2
-radio     1274  205   1525408 58916 ffffffff 00000000 S com.android.phone
-radio     1282  1274  1525408 58916 ffffffff 00000000 S Heap thread poo
-radio     1283  1274  1525408 58916 ffffffff 00000000 S Heap thread poo
-radio     1284  1274  1525408 58916 ffffffff 00000000 S Heap thread poo
-radio     1285  1274  1525408 58916 ffffffff 00000000 S Signal Catcher
-radio     1286  1274  1525408 58916 ffffffff 00000000 S JDWP
-radio     1287  1274  1525408 58916 ffffffff 00000000 S ReferenceQueueD
-radio     1290  1274  1525408 58916 ffffffff 00000000 S FinalizerDaemon
-radio     1291  1274  1525408 58916 ffffffff 00000000 S FinalizerWatchd
-radio     1292  1274  1525408 58916 ffffffff 00000000 S HeapTrimmerDaem
-radio     1293  1274  1525408 58916 ffffffff 00000000 S GCDaemon
-radio     1299  1274  1525408 58916 ffffffff 00000000 S Binder_1
-radio     1315  1274  1525408 58916 ffffffff 00000000 S Binder_2
-radio     1365  1274  1525408 58916 ffffffff 00000000 S RILSender0
-radio     1366  1274  1525408 58916 ffffffff 00000000 S RILReceiver0
-radio     1380  1274  1525408 58916 ffffffff 00000000 S DcHandlerThread
-radio     1392  1274  1525408 58916 ffffffff 00000000 S GsmCellBroadcas
-radio     1394  1274  1525408 58916 ffffffff 00000000 S GsmInboundSmsHa
-radio     1397  1274  1525408 58916 ffffffff 00000000 S CellBroadcastHa
-radio     1417  1274  1525408 58916 ffffffff 00000000 S CdmaInboundSmsH
-radio     1418  1274  1525408 58916 ffffffff 00000000 S CdmaServiceCate
-radio     1427  1274  1525408 58916 ffffffff 00000000 S DcSwitchStateMa
-radio     1429  1274  1525408 58916 ffffffff 00000000 S SyncHandler-0
-radio     1443  1274  1525408 58916 ffffffff 00000000 S AsyncTask #1
-radio     1473  1274  1525408 58916 ffffffff 00000000 S Binder_3
-radio     1517  1274  1525408 58916 ffffffff 00000000 S ervice.Executor
-radio     1518  1274  1525408 58916 ffffffff 00000000 S WifiManager
-radio     1563  1274  1525408 58916 ffffffff 00000000 S Cat Telephony s
-radio     1564  1274  1525408 58916 ffffffff 00000000 S RilMessageDecod
-radio     1567  1274  1525408 58916 ffffffff 00000000 S Cat Icon Loader
-radio     1690  1274  1525408 58916 ffffffff 00000000 S Binder_4
-radio     4571  1274  1525408 58916 ffffffff 00000000 S Stk App Service
-u0_a22    1305  205   1674592 127012 ffffffff 00000000 S com.google.android.googlequicksearchbox
-u0_a22    1306  1305  1674592 127012 ffffffff 00000000 S Heap thread poo
-u0_a22    1307  1305  1674592 127012 ffffffff 00000000 S Heap thread poo
-u0_a22    1308  1305  1674592 127012 ffffffff 00000000 S Heap thread poo
-u0_a22    1317  1305  1674592 127012 ffffffff 00000000 S Signal Catcher
-u0_a22    1318  1305  1674592 127012 ffffffff 00000000 S JDWP
-u0_a22    1322  1305  1674592 127012 ffffffff 00000000 S ReferenceQueueD
-u0_a22    1323  1305  1674592 127012 ffffffff 00000000 S FinalizerDaemon
-u0_a22    1324  1305  1674592 127012 ffffffff 00000000 S FinalizerWatchd
-u0_a22    1332  1305  1674592 127012 ffffffff 00000000 S HeapTrimmerDaem
-u0_a22    1333  1305  1674592 127012 ffffffff 00000000 S GCDaemon
-u0_a22    1334  1305  1674592 127012 ffffffff 00000000 S Binder_1
-u0_a22    1335  1305  1674592 127012 ffffffff 00000000 S Binder_2
-u0_a22    1386  1305  1674592 127012 ffffffff 00000000 S launcher-loader
-u0_a22    1395  1305  1674592 127012 ffffffff 00000000 S AsyncTask #1
-u0_a22    1432  1305  1674592 127012 ffffffff 00000000 S AsyncTask #2
-u0_a22    1484  1305  1674592 127012 ffffffff 00000000 S GELServices-0
-u0_a22    1514  1305  1674592 127012 ffffffff 00000000 S RenderThread
-u0_a22    1540  1305  1674592 127012 ffffffff 00000000 S AsyncTask #3
-u0_a22    1618  1305  1674592 127012 ffffffff 00000000 S GELServices-1
-u0_a22    1621  1305  1674592 127012 ffffffff 00000000 S GELServices-2
-u0_a22    1629  1305  1674592 127012 ffffffff 00000000 S GELServices-3
-u0_a22    1632  1305  1674592 127012 ffffffff 00000000 S AsyncTask #4
-u0_a22    1633  1305  1674592 127012 ffffffff 00000000 S AsyncTask #5
-u0_a22    1636  1305  1674592 127012 ffffffff 00000000 S GELServices-4
-u0_a22    1644  1305  1674592 127012 ffffffff 00000000 S GL updater
-u0_a22    1647  1305  1674592 127012 ffffffff 00000000 S GELServices-5
-u0_a22    1664  1305  1674592 127012 ffffffff 00000000 S GELServices-6
-u0_a22    1764  1305  1674592 127012 ffffffff 00000000 S Binder_3
-u0_a22    1766  1305  1674592 127012 ffffffff 00000000 S GELServices-7
-u0_a22    1772  1305  1674592 127012 ffffffff 00000000 S RenderThread
-u0_a22    1773  1305  1674592 127012 ffffffff 00000000 S RenderThread
-u0_a22    1774  1305  1674592 127012 ffffffff 00000000 S RenderThread
-u0_a22    1775  1305  1674592 127012 ffffffff 00000000 S RenderThread
-u0_a22    1998  1305  1674592 127012 ffffffff 00000000 S GELServices-8
-u0_a22    2320  1305  1674592 127012 ffffffff 00000000 S RemoteViewsCach
-u0_a22    2321  1305  1674592 127012 ffffffff 00000000 S RemoteViewsAdap
-u0_a22    2902  1305  1674592 127012 ffffffff 00000000 S GELServices-9
-u0_a22    1451  205   1584512 87716 ffffffff 00000000 S com.google.android.googlequicksearchbox:search
-u0_a22    1457  1451  1584512 87716 ffffffff 00000000 S Heap thread poo
-u0_a22    1458  1451  1584512 87716 ffffffff 00000000 S Heap thread poo
-u0_a22    1459  1451  1584512 87716 ffffffff 00000000 S Heap thread poo
-u0_a22    1460  1451  1584512 87716 ffffffff 00000000 S Signal Catcher
-u0_a22    1461  1451  1584512 87716 ffffffff 00000000 S JDWP
-u0_a22    1462  1451  1584512 87716 ffffffff 00000000 S ReferenceQueueD
-u0_a22    1463  1451  1584512 87716 ffffffff 00000000 S FinalizerDaemon
-u0_a22    1464  1451  1584512 87716 ffffffff 00000000 S FinalizerWatchd
-u0_a22    1466  1451  1584512 87716 ffffffff 00000000 S HeapTrimmerDaem
-u0_a22    1468  1451  1584512 87716 ffffffff 00000000 S GCDaemon
-u0_a22    1474  1451  1584512 87716 ffffffff 00000000 S Binder_1
-u0_a22    1475  1451  1584512 87716 ffffffff 00000000 S Binder_2
-u0_a22    1515  1451  1584512 87716 ffffffff 00000000 S User-Facing Non
-u0_a22    1516  1451  1584512 87716 ffffffff 00000000 S User-Facing Non
-u0_a22    1535  1451  1584512 87716 ffffffff 00000000 S User-Facing Non
-u0_a22    1538  1451  1584512 87716 ffffffff 00000000 S User-Facing Non
-u0_a22    1553  1451  1584512 87716 ffffffff 00000000 S User-Facing Non
-u0_a22    1560  1451  1584512 87716 ffffffff 00000000 S IcingConnection
-u0_a22    1580  1451  1584512 87716 ffffffff 00000000 S AudioRouter-0
-u0_a22    1626  1451  1584512 87716 ffffffff 00000000 S AsyncFileStorag
-u0_a22    1635  1451  1584512 87716 ffffffff 00000000 S WifiManager
-u0_a22    1643  1451  1584512 87716 ffffffff 00000000 S LocationOracleI
-u0_a22    1646  1451  1584512 87716 ffffffff 00000000 S GoogleApiClient
-u0_a22    1769  1451  1584512 87716 ffffffff 00000000 S Binder_3
-u0_a22    1770  1451  1584512 87716 ffffffff 00000000 S Gservices
-u0_a22    1810  1451  1584512 87716 ffffffff 00000000 S ChromiumNet
-u0_a22    1811  1451  1584512 87716 ffffffff 00000000 S DnsConfigServic
-u0_a22    1812  1451  1584512 87716 ffffffff 00000000 S inotify_reader
-u0_a22    1815  1451  1584512 87716 ffffffff 00000000 S Network File Th
-u0_a22    1816  1451  1584512 87716 ffffffff 00000000 S SimpleCacheWork
-u0_a22    1817  1451  1584512 87716 ffffffff 00000000 S SimpleCacheWork
-u0_a22    1823  1451  1584512 87716 ffffffff 00000000 S Binder_4
-u0_a22    1824  1451  1584512 87716 ffffffff 00000000 S Binder_5
-u0_a22    12193 1451  1584512 87716 ffffffff 00000000 S Background Bloc
-u0_a22    12207 1451  1584512 87716 ffffffff 00000000 S User-Facing Blo
-u0_a22    12211 1451  1584512 87716 ffffffff 00000000 S WorkerPool/1221
-u0_a22    12232 1451  1584512 87716 ffffffff 00000000 S Background Non-
-u0_a22    12235 1451  1584512 87716 ffffffff 00000000 S Background Bloc
-u0_a22    12236 1451  1584512 87716 ffffffff 00000000 S Background Bloc
-u0_a22    12237 1451  1584512 87716 ffffffff 00000000 S Background Non-
-u0_a8     1478  205   1613496 72932 ffffffff 00000000 S com.google.process.gapps
-u0_a8     1485  1478  1613496 72932 ffffffff 00000000 S Heap thread poo
-u0_a8     1486  1478  1613496 72932 ffffffff 00000000 S Heap thread poo
-u0_a8     1487  1478  1613496 72932 ffffffff 00000000 S Heap thread poo
-u0_a8     1488  1478  1613496 72932 ffffffff 00000000 S Signal Catcher
-u0_a8     1489  1478  1613496 72932 ffffffff 00000000 S JDWP
-u0_a8     1490  1478  1613496 72932 ffffffff 00000000 S ReferenceQueueD
-u0_a8     1491  1478  1613496 72932 ffffffff 00000000 S FinalizerDaemon
-u0_a8     1492  1478  1613496 72932 ffffffff 00000000 S FinalizerWatchd
-u0_a8     1493  1478  1613496 72932 ffffffff 00000000 S HeapTrimmerDaem
-u0_a8     1494  1478  1613496 72932 ffffffff 00000000 S GCDaemon
-u0_a8     1495  1478  1613496 72932 ffffffff 00000000 S Binder_1
-u0_a8     1496  1478  1613496 72932 ffffffff 00000000 S Binder_2
-u0_a8     1497  1478  1613496 72932 ffffffff 00000000 S Binder_3
-u0_a8     1613  1478  1613496 72932 ffffffff 00000000 S Gservices
-u0_a8     1614  1478  1613496 72932 ffffffff 00000000 S RefQueueWorker@
-u0_a8     1615  1478  1613496 72932 ffffffff 00000000 S Gservices
-u0_a8     1616  1478  1613496 72932 ffffffff 00000000 S RefQueueWorker@
-u0_a8     1620  1478  1613496 72932 ffffffff 00000000 S Gservices
-u0_a8     1996  1478  1613496 72932 ffffffff 00000000 S Binder_4
-u0_a8     1997  1478  1613496 72932 ffffffff 00000000 S Binder_5
-u0_a8     2510  1478  1613496 72932 ffffffff 00000000 S GCMWriter
-u0_a8     2512  1478  1613496 72932 ffffffff 00000000 S AsyncTask #1
-u0_a8     2536  1478  1613496 72932 ffffffff 00000000 S GCMReader
-u0_a8     2547  1478  1613496 72932 ffffffff 00000000 S pool-2-thread-1
-u0_a8     3680  1478  1613496 72932 ffffffff 00000000 S WifiManager
-u0_a8     4135  1478  1613496 72932 ffffffff 00000000 S AsyncTask #2
-u0_a8     4159  1478  1613496 72932 ffffffff 00000000 S AsyncTask #3
-u0_a8     4184  1478  1613496 72932 ffffffff 00000000 S AsyncTask #4
-u0_a8     4210  1478  1613496 72932 ffffffff 00000000 S AsyncTask #5
-u0_a8     4541  1478  1613496 72932 ffffffff 00000000 S RefQueueWorker@
-u0_a8     4735  1478  1613496 72932 ffffffff 00000000 S pool-8-thread-1
-u0_a8     4770  1478  1613496 72932 ffffffff 00000000 S Binder_6
-u0_a8     12448 1478  1613496 72932 ffffffff 00000000 S OkHttp Connecti
-u0_a8     14401 1478  1613496 72932 ffffffff 00000000 S Thread-233
-u0_a8     14409 1478  1613496 72932 ffffffff 00000000 S OkHttp Connecti
-dhcp      1700  1     9344   756   ffffffff 00000000 S /system/bin/dhcpcd
-u0_a8     1873  205   1756828 84724 ffffffff 00000000 S com.google.android.gms
-u0_a8     1878  1873  1756828 84724 ffffffff 00000000 S Heap thread poo
-u0_a8     1880  1873  1756828 84724 ffffffff 00000000 S Heap thread poo
-u0_a8     1881  1873  1756828 84724 ffffffff 00000000 S Heap thread poo
-u0_a8     1882  1873  1756828 84724 ffffffff 00000000 S Signal Catcher
-u0_a8     1883  1873  1756828 84724 ffffffff 00000000 S JDWP
-u0_a8     1884  1873  1756828 84724 ffffffff 00000000 S ReferenceQueueD
-u0_a8     1885  1873  1756828 84724 ffffffff 00000000 S FinalizerDaemon
-u0_a8     1886  1873  1756828 84724 ffffffff 00000000 S FinalizerWatchd
-u0_a8     1887  1873  1756828 84724 ffffffff 00000000 S HeapTrimmerDaem
-u0_a8     1888  1873  1756828 84724 ffffffff 00000000 S GCDaemon
-u0_a8     1889  1873  1756828 84724 ffffffff 00000000 S Binder_1
-u0_a8     1890  1873  1756828 84724 ffffffff 00000000 S Binder_2
-u0_a8     1895  1873  1756828 84724 ffffffff 00000000 S Gservices
-u0_a8     1898  1873  1756828 84724 ffffffff 00000000 S measurement-1
-u0_a8     1900  1873  1756828 84724 ffffffff 00000000 S AsyncTask #1
-u0_a8     1904  1873  1756828 84724 ffffffff 00000000 S AsyncTask #2
-u0_a8     2001  1873  1756828 84724 ffffffff 00000000 S Binder_3
-u0_a8     2497  1873  1756828 84724 ffffffff 00000000 S WifiManager
-u0_a8     2509  1873  1756828 84724 ffffffff 00000000 S picasa-uploads-
-u0_a8     2946  1873  1756828 84724 ffffffff 00000000 S pool-7-thread-1
-u0_a8     4390  1873  1756828 84724 ffffffff 00000000 S pool-13-thread-
-u0_a8     4391  1873  1756828 84724 ffffffff 00000000 S pool-18-thread-
-u0_a8     4392  1873  1756828 84724 ffffffff 00000000 S pool-11-thread-
-u0_a8     4394  1873  1756828 84724 ffffffff 00000000 S pool-25-thread-
-u0_a8     4395  1873  1756828 84724 ffffffff 00000000 S pool-25-thread-
-u0_a8     4396  1873  1756828 84724 ffffffff 00000000 S pool-25-thread-
-u0_a8     4397  1873  1756828 84724 ffffffff 00000000 S pool-25-thread-
-u0_a8     4398  1873  1756828 84724 ffffffff 00000000 S pool-14-thread-
-u0_a8     4521  1873  1756828 84724 ffffffff 00000000 S MediaTracker bu
-u0_a8     4766  1873  1756828 84724 ffffffff 00000000 S Icing-Pool-0
-u0_a8     4771  1873  1756828 84724 ffffffff 00000000 S Icing-Worker-0
-u0_a8     4796  1873  1756828 84724 ffffffff 00000000 S Thread-200
-u0_a8     4797  1873  1756828 84724 ffffffff 00000000 S Thread-201
-u0_a8     4798  1873  1756828 84724 ffffffff 00000000 S Thread-202
-u0_a8     4799  1873  1756828 84724 ffffffff 00000000 S Thread-203
-u0_a8     4800  1873  1756828 84724 ffffffff 00000000 S Thread-204
-u0_a8     5793  1873  1756828 84724 ffffffff 00000000 S Binder_4
-u0_a8     6257  1873  1756828 84724 ffffffff 00000000 S Icing-Pool-1
-u0_a8     6258  1873  1756828 84724 ffffffff 00000000 S Icing-Pool-2
-u0_a8     6259  1873  1756828 84724 ffffffff 00000000 S Icing-Pool-3
-u0_a8     6673  1873  1756828 84724 ffffffff 00000000 S pool-22-thread-
-u0_a8     8581  1873  1756828 84724 ffffffff 00000000 S Binder_5
-u0_a8     9001  1873  1756828 84724 ffffffff 00000000 S Gservices
-u0_a8     9024  1873  1756828 84724 ffffffff 00000000 S GamesProviderWo
-u0_a8     11865 1873  1756828 84724 ffffffff 00000000 S pool-37-thread-
-u0_a8     1949  205   1614008 81544 ffffffff 00000000 S com.google.android.gms.persistent
-u0_a8     1954  1949  1614008 81544 ffffffff 00000000 S Heap thread poo
-u0_a8     1955  1949  1614008 81544 ffffffff 00000000 S Heap thread poo
-u0_a8     1956  1949  1614008 81544 ffffffff 00000000 S Heap thread poo
-u0_a8     1959  1949  1614008 81544 ffffffff 00000000 S Signal Catcher
-u0_a8     1960  1949  1614008 81544 ffffffff 00000000 S JDWP
-u0_a8     1961  1949  1614008 81544 ffffffff 00000000 S ReferenceQueueD
-u0_a8     1962  1949  1614008 81544 ffffffff 00000000 S FinalizerDaemon
-u0_a8     1963  1949  1614008 81544 ffffffff 00000000 S FinalizerWatchd
-u0_a8     1964  1949  1614008 81544 ffffffff 00000000 S HeapTrimmerDaem
-u0_a8     1965  1949  1614008 81544 ffffffff 00000000 S GCDaemon
-u0_a8     1966  1949  1614008 81544 ffffffff 00000000 S Binder_1
-u0_a8     1967  1949  1614008 81544 ffffffff 00000000 S Binder_2
-u0_a8     1968  1949  1614008 81544 ffffffff 00000000 S Gservices
-u0_a8     1973  1949  1614008 81544 ffffffff 00000000 S IntentService[G
-u0_a8     1976  1949  1614008 81544 ffffffff 00000000 S FlpThread
-u0_a8     1977  1949  1614008 81544 ffffffff 00000000 S Binder_3
-u0_a8     1978  1949  1614008 81544 ffffffff 00000000 S WifiManager
-u0_a8     1979  1949  1614008 81544 ffffffff 00000000 S GeofencerStateM
-u0_a8     1980  1949  1614008 81544 ffffffff 00000000 S LocationService
-u0_a8     1984  1949  1614008 81544 ffffffff 00000000 S Binder_4
-u0_a8     1986  1949  1614008 81544 ffffffff 00000000 S Binder_5
-u0_a8     1990  1949  1614008 81544 ffffffff 00000000 S pool-4-thread-1
-u0_a8     1992  1949  1614008 81544 ffffffff 00000000 S GmsCoreStatsSer
-u0_a8     1995  1949  1614008 81544 ffffffff 00000000 S GoogleLocationS
-u0_a8     2004  1949  1614008 81544 ffffffff 00000000 S Thread-139
-u0_a8     2005  1949  1614008 81544 ffffffff 00000000 S Thread-140
-u0_a8     2006  1949  1614008 81544 ffffffff 00000000 S Thread-141
-u0_a8     2007  1949  1614008 81544 ffffffff 00000000 S Thread-142
-u0_a8     2021  1949  1614008 81544 ffffffff 00000000 S NetworkLocation
-u0_a8     2029  1949  1614008 81544 ffffffff 00000000 S UlrDispatchingS
-u0_a8     2030  1949  1614008 81544 ffffffff 00000000 S nlp-async-worke
-u0_a8     2521  1949  1614008 81544 ffffffff 00000000 S FitnessServiceF
-u0_a8     2522  1949  1614008 81544 ffffffff 00000000 S FitRecordingBro
-u0_a8     2526  1949  1614008 81544 ffffffff 00000000 S AsyncTask #1
-u0_a8     2530  1949  1614008 81544 ffffffff 00000000 S NearbyMessagesB
-u0_a8     4180  1949  1614008 81544 ffffffff 00000000 S AsyncTask #2
-u0_a8     4221  1949  1614008 81544 ffffffff 00000000 S AsyncTask #3
-u0_a8     4223  1949  1614008 81544 ffffffff 00000000 S AsyncTask #4
-u0_a8     4749  1949  1614008 81544 ffffffff 00000000 S CopresenceEvent
-u0_a8     6326  1949  1614008 81544 ffffffff 00000000 S AsyncTask #5
-u0_a8     6917  1949  1614008 81544 ffffffff 00000000 S Binder_6
-u0_a8     7196  1949  1614008 81544 ffffffff 00000000 S UlrDispatchingS
-u0_a8     7260  1949  1614008 81544 ffffffff 00000000 S Thread-174
-u0_a8     7261  1949  1614008 81544 ffffffff 00000000 S Thread-175
-u0_a8     7262  1949  1614008 81544 ffffffff 00000000 S Thread-176
-u0_a8     7263  1949  1614008 81544 ffffffff 00000000 S Thread-177
-u0_a8     7264  1949  1614008 81544 ffffffff 00000000 S Thread-178
-u0_a8     12449 1949  1614008 81544 ffffffff 00000000 S OkHttp Connecti
-root      2031  1     20256  880   ffffffff 00000000 S /system/bin/mpdecision
-root      2032  2031  20256  880   ffffffff 00000000 S mpdecision
-root      2033  2031  20256  880   ffffffff 00000000 S mpdecision
-root      2034  2031  20256  880   ffffffff 00000000 S mpdecision
-root      2035  2031  20256  880   ffffffff 00000000 S mpdecision
-root      2036  2031  20256  880   ffffffff 00000000 S mpdecision
-root      2046  2031  20256  880   ffffffff 00000000 S mpdecision
-u0_a193   2647  205   1541760 60840 ffffffff 00000000 S com.qiyi.video.market
-u0_a193   2653  2647  1541760 60840 ffffffff 00000000 S Heap thread poo
-u0_a193   2654  2647  1541760 60840 ffffffff 00000000 S Heap thread poo
-u0_a193   2655  2647  1541760 60840 ffffffff 00000000 S Heap thread poo
-u0_a193   2656  2647  1541760 60840 ffffffff 00000000 S Signal Catcher
-u0_a193   2657  2647  1541760 60840 ffffffff 00000000 S JDWP
-u0_a193   2658  2647  1541760 60840 ffffffff 00000000 S ReferenceQueueD
-u0_a193   2659  2647  1541760 60840 ffffffff 00000000 S FinalizerDaemon
-u0_a193   2660  2647  1541760 60840 ffffffff 00000000 S FinalizerWatchd
-u0_a193   2661  2647  1541760 60840 ffffffff 00000000 S HeapTrimmerDaem
-u0_a193   2662  2647  1541760 60840 ffffffff 00000000 S GCDaemon
-u0_a193   2663  2647  1541760 60840 ffffffff 00000000 S Binder_1
-u0_a193   2664  2647  1541760 60840 ffffffff 00000000 S Binder_2
-u0_a193   2671  2647  1541760 60840 ffffffff 00000000 S RefQueueWorker@
-u0_a193   2673  2647  1541760 60840 ffffffff 00000000 S .ProcessManager
-u0_a193   2675  2647  1541760 60840 ffffffff 00000000 S Binder_3
-u0_a193   2677  2647  1541760 60840 ffffffff 00000000 S Thread-208
-u0_a193   2679  2647  1541760 60840 ffffffff 00000000 S pool-2-thread-1
-u0_a193   2680  2647  1541760 60840 ffffffff 00000000 S WifiManager
-u0_a193   2682  2647  1541760 60840 ffffffff 00000000 S Timer-0
-u0_a193   2683  2647  1541760 60840 ffffffff 00000000 S Timer-1
-u0_a193   2710  2647  1541760 60840 ffffffff 00000000 S AsyncTask #1
-u0_a193   2718  2647  1541760 60840 ffffffff 00000000 S pool-3-thread-1
-u0_a193   3103  2647  1541760 60840 ffffffff 00000000 S pool-4-thread-1
-u0_a193   6672  2647  1541760 60840 ffffffff 00000000 S AsyncTask #2
-u0_a193   6752  2647  1541760 60840 ffffffff 00000000 S AsyncTask #3
-u0_a193   12484 2647  1541760 60840 ffffffff 00000000 S AsyncTask #4
-u0_a193   12576 2647  1541760 60840 ffffffff 00000000 S AsyncTask #5
-u0_a193   3104  205   1523132 46892 ffffffff 00000000 S com.qiyi.video.market:pluginDownloadService
-u0_a193   3110  3104  1523132 46892 ffffffff 00000000 S Heap thread poo
-u0_a193   3111  3104  1523132 46892 ffffffff 00000000 S Heap thread poo
-u0_a193   3112  3104  1523132 46892 ffffffff 00000000 S Heap thread poo
-u0_a193   3113  3104  1523132 46892 ffffffff 00000000 S Signal Catcher
-u0_a193   3114  3104  1523132 46892 ffffffff 00000000 S JDWP
-u0_a193   3115  3104  1523132 46892 ffffffff 00000000 S ReferenceQueueD
-u0_a193   3116  3104  1523132 46892 ffffffff 00000000 S FinalizerDaemon
-u0_a193   3117  3104  1523132 46892 ffffffff 00000000 S FinalizerWatchd
-u0_a193   3118  3104  1523132 46892 ffffffff 00000000 S HeapTrimmerDaem
-u0_a193   3119  3104  1523132 46892 ffffffff 00000000 S GCDaemon
-u0_a193   3120  3104  1523132 46892 ffffffff 00000000 S Binder_1
-u0_a193   3121  3104  1523132 46892 ffffffff 00000000 S Binder_2
-u0_a193   3141  3104  1523132 46892 ffffffff 00000000 S RefQueueWorker@
-u0_a193   3257  3104  1523132 46892 ffffffff 00000000 S pool-3-thread-1
-u0_a193   7173  3104  1523132 46892 ffffffff 00000000 S Binder_3
-u0_a193   3122  205   1538224 61140 ffffffff 00000000 S com.qiyi.video.market:bdservice_v1
-u0_a193   3128  3122  1538224 61140 ffffffff 00000000 S Heap thread poo
-u0_a193   3129  3122  1538224 61140 ffffffff 00000000 S Heap thread poo
-u0_a193   3130  3122  1538224 61140 ffffffff 00000000 S Heap thread poo
-u0_a193   3131  3122  1538224 61140 ffffffff 00000000 S Signal Catcher
-u0_a193   3132  3122  1538224 61140 ffffffff 00000000 S JDWP
-u0_a193   3133  3122  1538224 61140 ffffffff 00000000 S ReferenceQueueD
-u0_a193   3134  3122  1538224 61140 ffffffff 00000000 S FinalizerDaemon
-u0_a193   3135  3122  1538224 61140 ffffffff 00000000 S FinalizerWatchd
-u0_a193   3136  3122  1538224 61140 ffffffff 00000000 S HeapTrimmerDaem
-u0_a193   3137  3122  1538224 61140 ffffffff 00000000 S GCDaemon
-u0_a193   3138  3122  1538224 61140 ffffffff 00000000 S Binder_1
-u0_a193   3139  3122  1538224 61140 ffffffff 00000000 S Binder_2
-u0_a193   3145  3122  1538224 61140 ffffffff 00000000 S RefQueueWorker@
-u0_a193   3206  3122  1538224 61140 ffffffff 00000000 S WifiManager
-u0_a193   3208  3122  1538224 61140 ffffffff 00000000 S NanoHttpd Main 
-u0_a193   7586  3122  1538224 61140 ffffffff 00000000 S pool-4-thread-1
-u0_a193   10584 3122  1538224 61140 ffffffff 00000000 S pool-2-thread-1
-u0_a193   3154  205   1522116 53536 ffffffff 00000000 S com.qiyi.video.market:baiduLocation
-u0_a193   3163  3154  1522116 53536 ffffffff 00000000 S Heap thread poo
-u0_a193   3164  3154  1522116 53536 ffffffff 00000000 S Heap thread poo
-u0_a193   3165  3154  1522116 53536 ffffffff 00000000 S Heap thread poo
-u0_a193   3166  3154  1522116 53536 ffffffff 00000000 S Signal Catcher
-u0_a193   3167  3154  1522116 53536 ffffffff 00000000 S JDWP
-u0_a193   3168  3154  1522116 53536 ffffffff 00000000 S ReferenceQueueD
-u0_a193   3169  3154  1522116 53536 ffffffff 00000000 S FinalizerDaemon
-u0_a193   3170  3154  1522116 53536 ffffffff 00000000 S FinalizerWatchd
-u0_a193   3171  3154  1522116 53536 ffffffff 00000000 S HeapTrimmerDaem
-u0_a193   3172  3154  1522116 53536 ffffffff 00000000 S GCDaemon
-u0_a193   3173  3154  1522116 53536 ffffffff 00000000 S Binder_1
-u0_a193   3174  3154  1522116 53536 ffffffff 00000000 S Binder_2
-u0_a193   3177  3154  1522116 53536 ffffffff 00000000 S RefQueueWorker@
-u0_a193   3199  3154  1522116 53536 ffffffff 00000000 S WifiManager
-u0_a86    3179  205   1561816 58376 ffffffff 00000000 S com.tencent.mm:push
-u0_a86    3183  3179  1561816 58376 ffffffff 00000000 S Heap thread poo
-u0_a86    3184  3179  1561816 58376 ffffffff 00000000 S Heap thread poo
-u0_a86    3185  3179  1561816 58376 ffffffff 00000000 S Heap thread poo
-u0_a86    3187  3179  1561816 58376 ffffffff 00000000 S Signal Catcher
-u0_a86    3189  3179  1561816 58376 ffffffff 00000000 S JDWP
-u0_a86    3190  3179  1561816 58376 ffffffff 00000000 S ReferenceQueueD
-u0_a86    3191  3179  1561816 58376 ffffffff 00000000 S FinalizerDaemon
-u0_a86    3192  3179  1561816 58376 ffffffff 00000000 S FinalizerWatchd
-u0_a86    3193  3179  1561816 58376 ffffffff 00000000 S HeapTrimmerDaem
-u0_a86    3194  3179  1561816 58376 ffffffff 00000000 S GCDaemon
-u0_a86    3195  3179  1561816 58376 ffffffff 00000000 S Binder_1
-u0_a86    3196  3179  1561816 58376 ffffffff 00000000 S Binder_2
-u0_a86    3210  3179  1561816 58376 ffffffff 00000000 S THREAD_POOL_HAN
-u0_a86    3212  3179  1561816 58376 ffffffff 00000000 S tencent.mm:push
-u0_a86    3216  3179  1561816 58376 ffffffff 00000000 S FileObserver
-u0_a86    3238  3179  1561816 58376 ffffffff 00000000 S tencent.mm:push
-u0_a86    3239  3179  1561816 58376 ffffffff 00000000 S default
-u0_a86    3240  3179  1561816 58376 ffffffff 00000000 S WifiManager
-u0_a86    5627  3179  1561816 58376 ffffffff 00000000 S Binder_3
-u0_a86    7150  3179  1561816 58376 ffffffff 00000000 S default
-u0_a170   3217  205   1531688 52204 ffffffff 00000000 S com.baidu.searchbox:bdservice_v1
-u0_a170   3220  3217  1531688 52204 ffffffff 00000000 S Heap thread poo
-u0_a170   3221  3217  1531688 52204 ffffffff 00000000 S Heap thread poo
-u0_a170   3223  3217  1531688 52204 ffffffff 00000000 S Heap thread poo
-u0_a170   3226  3217  1531688 52204 ffffffff 00000000 S Signal Catcher
-u0_a170   3228  3217  1531688 52204 ffffffff 00000000 S JDWP
-u0_a170   3229  3217  1531688 52204 ffffffff 00000000 S ReferenceQueueD
-u0_a170   3230  3217  1531688 52204 ffffffff 00000000 S FinalizerDaemon
-u0_a170   3231  3217  1531688 52204 ffffffff 00000000 S FinalizerWatchd
-u0_a170   3233  3217  1531688 52204 ffffffff 00000000 S HeapTrimmerDaem
-u0_a170   3234  3217  1531688 52204 ffffffff 00000000 S GCDaemon
-u0_a170   3235  3217  1531688 52204 ffffffff 00000000 S Binder_1
-u0_a170   3236  3217  1531688 52204 ffffffff 00000000 S Binder_2
-u0_a170   3303  3217  1531688 52204 ffffffff 00000000 S AsyncTask #1
-u0_a170   3304  3217  1531688 52204 ffffffff 00000000 S AsyncTask #2
-u0_a170   3518  3217  1531688 52204 ffffffff 00000000 S PushService-Pus
-u0_a170   3519  3217  1531688 52204 ffffffff 00000000 S PushService-Pus
-u0_a170   6201  3217  1531688 52204 ffffffff 00000000 S pool-1-thread-1
-u0_a170   10591 3217  1531688 52204 ffffffff 00000000 S RefQueueWorker@
-u0_a170   3260  205   1533384 53212 ffffffff 00000000 S com.baidu.searchbox:bdmoservice
-u0_a170   3264  3260  1533384 53212 ffffffff 00000000 S Heap thread poo
-u0_a170   3265  3260  1533384 53212 ffffffff 00000000 S Heap thread poo
-u0_a170   3266  3260  1533384 53212 ffffffff 00000000 S Heap thread poo
-u0_a170   3269  3260  1533384 53212 ffffffff 00000000 S Signal Catcher
-u0_a170   3270  3260  1533384 53212 ffffffff 00000000 S JDWP
-u0_a170   3271  3260  1533384 53212 ffffffff 00000000 S ReferenceQueueD
-u0_a170   3272  3260  1533384 53212 ffffffff 00000000 S FinalizerDaemon
-u0_a170   3273  3260  1533384 53212 ffffffff 00000000 S FinalizerWatchd
-u0_a170   3274  3260  1533384 53212 ffffffff 00000000 S HeapTrimmerDaem
-u0_a170   3275  3260  1533384 53212 ffffffff 00000000 S GCDaemon
-u0_a170   3276  3260  1533384 53212 ffffffff 00000000 S Binder_1
-u0_a170   3277  3260  1533384 53212 ffffffff 00000000 S Binder_2
-u0_a170   3738  3260  1533384 53212 ffffffff 00000000 S NanoHttpd Main 
-u0_a170   5783  3260  1533384 53212 ffffffff 00000000 S WifiManager
-u0_a126   3633  205   1515740 46080 ffffffff 00000000 S com.tencent.portfolio:push
-u0_a126   3636  3633  1515740 46080 ffffffff 00000000 S Heap thread poo
-u0_a126   3638  3633  1515740 46080 ffffffff 00000000 S Heap thread poo
-u0_a126   3639  3633  1515740 46080 ffffffff 00000000 S Heap thread poo
-u0_a126   3642  3633  1515740 46080 ffffffff 00000000 S Signal Catcher
-u0_a126   3643  3633  1515740 46080 ffffffff 00000000 S JDWP
-u0_a126   3645  3633  1515740 46080 ffffffff 00000000 S ReferenceQueueD
-u0_a126   3646  3633  1515740 46080 ffffffff 00000000 S FinalizerDaemon
-u0_a126   3647  3633  1515740 46080 ffffffff 00000000 S FinalizerWatchd
-u0_a126   3648  3633  1515740 46080 ffffffff 00000000 S HeapTrimmerDaem
-u0_a126   3649  3633  1515740 46080 ffffffff 00000000 S GCDaemon
-u0_a126   3650  3633  1515740 46080 ffffffff 00000000 S Binder_1
-u0_a126   3651  3633  1515740 46080 ffffffff 00000000 S Binder_2
-u0_a126   3661  3633  1515740 46080 ffffffff 00000000 S TPPluginCenter 
-u0_a126   3663  3633  1515740 46080 ffffffff 00000000 S pool-1-thread-1
-u0_a126   3665  3633  1515740 46080 ffffffff 00000000 S MidService
-u0_a126   3667  3633  1515740 46080 ffffffff 00000000 S pool-2-thread-1
-u0_a126   3668  3633  1515740 46080 ffffffff 00000000 S push core threa
-u0_a126   3670  3633  1515740 46080 ffffffff 00000000 S .ProcessManager
-u0_a126   3672  3633  1515740 46080 ffffffff 00000000 S Binder_3
-u0_a126   3674  3633  1515740 46080 ffffffff 00000000 S pool-4-thread-1
-u0_a126   3675  3633  1515740 46080 ffffffff 00000000 S pool-3-thread-1
-u0_a126   5638  3633  1515740 46080 ffffffff 00000000 S Timer-0
-bluetooth 4227  205   1527652 48088 ffffffff 00000000 S com.android.bluetooth
-bluetooth 4231  4227  1527652 48088 ffffffff 00000000 S Heap thread poo
-bluetooth 4233  4227  1527652 48088 ffffffff 00000000 S Heap thread poo
-bluetooth 4235  4227  1527652 48088 ffffffff 00000000 S Heap thread poo
-bluetooth 4236  4227  1527652 48088 ffffffff 00000000 S Signal Catcher
-bluetooth 4237  4227  1527652 48088 ffffffff 00000000 S JDWP
-bluetooth 4238  4227  1527652 48088 ffffffff 00000000 S ReferenceQueueD
-bluetooth 4239  4227  1527652 48088 ffffffff 00000000 S FinalizerDaemon
-bluetooth 4240  4227  1527652 48088 ffffffff 00000000 S FinalizerWatchd
-bluetooth 4241  4227  1527652 48088 ffffffff 00000000 S HeapTrimmerDaem
-bluetooth 4242  4227  1527652 48088 ffffffff 00000000 S GCDaemon
-bluetooth 4243  4227  1527652 48088 ffffffff 00000000 S Binder_1
-bluetooth 4244  4227  1527652 48088 ffffffff 00000000 S Binder_2
-bluetooth 4308  4227  1527652 48088 ffffffff 00000000 S BluetoothAdapte
-bluetooth 4309  4227  1527652 48088 ffffffff 00000000 S droid.bluetooth
-bluetooth 4311  4227  1527652 48088 ffffffff 00000000 S bluedroid wake/
-bluetooth 4312  4227  1527652 48088 ffffffff 00000000 S BT Service Call
-bluetooth 4315  4227  1527652 48088 ffffffff 00000000 S BondStateMachin
-bluetooth 4316  4227  1527652 48088 ffffffff 00000000 S Binder_3
-bluetooth 4317  4227  1527652 48088 ffffffff 00000000 S Binder_4
-bluetooth 4318  4227  1527652 48088 ffffffff 00000000 S HeadsetStateMac
-bluetooth 4320  4227  1527652 48088 ffffffff 00000000 S BluetoothAvrcpH
-bluetooth 4321  4227  1527652 48088 ffffffff 00000000 S A2dpStateMachin
-bluetooth 4322  4227  1527652 48088 ffffffff 00000000 S A2DP-MEDIA
-bluetooth 4323  4227  1527652 48088 ffffffff 00000000 S uipc-main
-bluetooth 4324  4227  1527652 48088 ffffffff 00000000 S BluetoothHdpHan
-bluetooth 4325  4227  1527652 48088 ffffffff 00000000 S droid.bluetooth
-bluetooth 4326  4227  1527652 48088 ffffffff 00000000 S BluetoothAdvert
-bluetooth 4327  4227  1527652 48088 ffffffff 00000000 S BluetoothScanMa
-bluetooth 4331  4227  1527652 48088 ffffffff 00000000 S bluedroid wake/
-bluetooth 4333  4227  1527652 48088 ffffffff 00000000 S bt_hc_worker
-bluetooth 4338  4227  1527652 48088 ffffffff 00000000 S userial_read
-bluetooth 4478  4227  1527652 48088 ffffffff 00000000 S BT Service Call
-bluetooth 4479  4227  1527652 48088 ffffffff 00000000 S bt_hc_worker
-bluetooth 4481  4227  1527652 48088 ffffffff 00000000 S bt_hc_worker
-bluetooth 4482  4227  1527652 48088 ffffffff 00000000 S BluetoothMapAcc
-bluetooth 6459  4227  1527652 48088 ffffffff 00000000 S BluetoothPbapAc
-bluetooth 6473  4227  1527652 48088 ffffffff 00000000 S pool-1-thread-1
-bluetooth 6477  4227  1527652 48088 ffffffff 00000000 S BtOppRfcommList
-radio     4597  205   1493160 37460 ffffffff 00000000 S com.qualcomm.qcrilmsgtunnel
-radio     4603  4597  1493160 37460 ffffffff 00000000 S Heap thread poo
-radio     4604  4597  1493160 37460 ffffffff 00000000 S Heap thread poo
-radio     4605  4597  1493160 37460 ffffffff 00000000 S Heap thread poo
-radio     4606  4597  1493160 37460 ffffffff 00000000 S Signal Catcher
-radio     4607  4597  1493160 37460 ffffffff 00000000 S JDWP
-radio     4608  4597  1493160 37460 ffffffff 00000000 S ReferenceQueueD
-radio     4609  4597  1493160 37460 ffffffff 00000000 S FinalizerDaemon
-radio     4610  4597  1493160 37460 ffffffff 00000000 S FinalizerWatchd
-radio     4611  4597  1493160 37460 ffffffff 00000000 S HeapTrimmerDaem
-radio     4612  4597  1493160 37460 ffffffff 00000000 S GCDaemon
-radio     4613  4597  1493160 37460 ffffffff 00000000 S Binder_1
-radio     4614  4597  1493160 37460 ffffffff 00000000 S Binder_2
-radio     4615  4597  1493160 37460 ffffffff 00000000 S QcRilReceiver
-radio     4616  4597  1493160 37460 ffffffff 00000000 S QcRilSender
-u0_a193   5239  205   1528424 47860 ffffffff 00000000 S .iqiyipushserviceGlobal
-u0_a193   5242  5239  1528424 47860 ffffffff 00000000 S Heap thread poo
-u0_a193   5244  5239  1528424 47860 ffffffff 00000000 S Heap thread poo
-u0_a193   5245  5239  1528424 47860 ffffffff 00000000 S Heap thread poo
-u0_a193   5248  5239  1528424 47860 ffffffff 00000000 S Signal Catcher
-u0_a193   5249  5239  1528424 47860 ffffffff 00000000 S JDWP
-u0_a193   5250  5239  1528424 47860 ffffffff 00000000 S ReferenceQueueD
-u0_a193   5251  5239  1528424 47860 ffffffff 00000000 S FinalizerDaemon
-u0_a193   5252  5239  1528424 47860 ffffffff 00000000 S FinalizerWatchd
-u0_a193   5253  5239  1528424 47860 ffffffff 00000000 S HeapTrimmerDaem
-u0_a193   5254  5239  1528424 47860 ffffffff 00000000 S GCDaemon
-u0_a193   5255  5239  1528424 47860 ffffffff 00000000 S Binder_1
-u0_a193   5257  5239  1528424 47860 ffffffff 00000000 S Binder_2
-u0_a193   5280  5239  1528424 47860 ffffffff 00000000 S RefQueueWorker@
-u0_a193   5281  5239  1528424 47860 ffffffff 00000000 S Binder_3
-u0_a193   5361  5239  1528424 47860 ffffffff 00000000 S Micro Client Co
-u0_a193   5362  5239  1528424 47860 ffffffff 00000000 S Micro Client Co
-u0_a193   5363  5239  1528424 47860 ffffffff 00000000 S Micro Client Ca
-u0_a193   6740  5239  1528424 47860 ffffffff 00000000 S Binder_4
-u0_a193   7091  5239  1528424 47860 ffffffff 00000000 S Binder_5
-u0_a193   7557  5239  1528424 47860 ffffffff 00000000 S Binder_6
-u0_a193   5285  5239  1521088 34196 ffffffff 00000000 S .iqiyipushserviceGlobal
-u0_a90    5323  205   1557268 59988 ffffffff 00000000 S com.strava
-u0_a90    5327  5323  1557268 59988 ffffffff 00000000 S Heap thread poo
-u0_a90    5328  5323  1557268 59988 ffffffff 00000000 S Heap thread poo
-u0_a90    5329  5323  1557268 59988 ffffffff 00000000 S Heap thread poo
-u0_a90    5332  5323  1557268 59988 ffffffff 00000000 S Signal Catcher
-u0_a90    5333  5323  1557268 59988 ffffffff 00000000 S JDWP
-u0_a90    5334  5323  1557268 59988 ffffffff 00000000 S ReferenceQueueD
-u0_a90    5335  5323  1557268 59988 ffffffff 00000000 S FinalizerDaemon
-u0_a90    5336  5323  1557268 59988 ffffffff 00000000 S FinalizerWatchd
-u0_a90    5337  5323  1557268 59988 ffffffff 00000000 S HeapTrimmerDaem
-u0_a90    5338  5323  1557268 59988 ffffffff 00000000 S GCDaemon
-u0_a90    5339  5323  1557268 59988 ffffffff 00000000 S Binder_1
-u0_a90    5340  5323  1557268 59988 ffffffff 00000000 S Binder_2
-u0_a90    5345  5323  1557268 59988 ffffffff 00000000 S Queue
-u0_a90    5346  5323  1557268 59988 ffffffff 00000000 S Queue
-u0_a90    5347  5323  1557268 59988 ffffffff 00000000 S Queue
-u0_a90    5348  5323  1557268 59988 ffffffff 00000000 S Queue
-u0_a90    5349  5323  1557268 59988 ffffffff 00000000 S Queue
-u0_a90    5352  5323  1557268 59988 ffffffff 00000000 S Crashlytics Exc
-u0_a90    5354  5323  1557268 59988 ffffffff 00000000 S pool-3-thread-1
-u0_a90    5364  5323  1557268 59988 ffffffff 00000000 S Thread-584
-u0_a90    5365  5323  1557268 59988 ffffffff 00000000 S Thread-585
-u0_a90    5366  5323  1557268 59988 ffffffff 00000000 S Thread-586
-u0_a90    5367  5323  1557268 59988 ffffffff 00000000 S pool-4-thread-1
-u0_a90    5369  5323  1557268 59988 ffffffff 00000000 S Crashlytics Tra
-u0_a90    5372  5323  1557268 59988 ffffffff 00000000 S Thread-593
-u0_a90    5373  5323  1557268 59988 ffffffff 00000000 S Thread-594
-u0_a90    5374  5323  1557268 59988 ffffffff 00000000 S Thread-595
-u0_a90    5375  5323  1557268 59988 ffffffff 00000000 S Thread-596
-u0_a90    5376  5323  1557268 59988 ffffffff 00000000 S pool-6-thread-1
-u0_a90    5377  5323  1557268 59988 ffffffff 00000000 S Thread-598
-u0_a90    5378  5323  1557268 59988 ffffffff 00000000 S Thread-599
-u0_a90    5379  5323  1557268 59988 ffffffff 00000000 S Thread-600
-u0_a90    5381  5323  1557268 59988 ffffffff 00000000 S Thread-602
-u0_a90    5383  5323  1557268 59988 ffffffff 00000000 S Thread #1
-u0_a90    5384  5323  1557268 59988 ffffffff 00000000 S AsyncTask #1
-u0_a90    5387  5323  1557268 59988 ffffffff 00000000 S Thread-605
-u0_a90    5388  5323  1557268 59988 ffffffff 00000000 S Thread-606
-u0_a90    5389  5323  1557268 59988 ffffffff 00000000 S Thread-607
-u0_a90    5390  5323  1557268 59988 ffffffff 00000000 S Thread-608
-u0_a90    5391  5323  1557268 59988 ffffffff 00000000 S Thread-609
-u0_a90    5393  5323  1557268 59988 ffffffff 00000000 S eNowAuthService
-u0_a90    5394  5323  1557268 59988 ffffffff 00000000 S Thread #2
-u0_a90    5468  5323  1557268 59988 ffffffff 00000000 S Okio Watchdog
-u0_a90    5498  5323  1557268 59988 ffffffff 00000000 S Thread #3
-u0_a109   5395  205   1524968 53976 ffffffff 00000000 S com.pandora.android
-u0_a109   5397  5395  1524968 53976 ffffffff 00000000 S Heap thread poo
-u0_a109   5398  5395  1524968 53976 ffffffff 00000000 S Heap thread poo
-u0_a109   5399  5395  1524968 53976 ffffffff 00000000 S Heap thread poo
-u0_a109   5401  5395  1524968 53976 ffffffff 00000000 S Signal Catcher
-u0_a109   5403  5395  1524968 53976 ffffffff 00000000 S JDWP
-u0_a109   5407  5395  1524968 53976 ffffffff 00000000 S ReferenceQueueD
-u0_a109   5408  5395  1524968 53976 ffffffff 00000000 S FinalizerDaemon
-u0_a109   5409  5395  1524968 53976 ffffffff 00000000 S FinalizerWatchd
-u0_a109   5410  5395  1524968 53976 ffffffff 00000000 S HeapTrimmerDaem
-u0_a109   5411  5395  1524968 53976 ffffffff 00000000 S GCDaemon
-u0_a109   5414  5395  1524968 53976 ffffffff 00000000 S Binder_1
-u0_a109   5416  5395  1524968 53976 ffffffff 00000000 S Binder_2
-u0_a109   5422  5395  1524968 53976 ffffffff 00000000 S Crashlytics Exc
-u0_a109   5429  5395  1524968 53976 ffffffff 00000000 S pool-2-thread-1
-u0_a109   5430  5395  1524968 53976 ffffffff 00000000 S AsyncTask #1
-u0_a109   5437  5395  1524968 53976 ffffffff 00000000 S Crashlytics Tra
-u0_a109   5439  5395  1524968 53976 ffffffff 00000000 S AsyncTask #2
-u0_a109   5440  5395  1524968 53976 ffffffff 00000000 S pool-4-thread-1
-u0_a109   5443  5395  1524968 53976 ffffffff 00000000 S PurchasingManag
-u0_a109   5444  5395  1524968 53976 ffffffff 00000000 S BluetoothServer
-u0_a109   5445  5395  1524968 53976 ffffffff 00000000 S AsyncTask #3
-u0_a109   5446  5395  1524968 53976 ffffffff 00000000 S AsyncTask #4
-u0_a109   5590  5395  1524968 53976 ffffffff 00000000 S Binder_3
-u0_a109   6481  5395  1524968 53976 ffffffff 00000000 S AsyncTask #5
-u0_a110   5474  205   1525556 49828 ffffffff 00000000 S tunein.player
-u0_a110   5479  5474  1525556 49828 ffffffff 00000000 S Heap thread poo
-u0_a110   5480  5474  1525556 49828 ffffffff 00000000 S Heap thread poo
-u0_a110   5481  5474  1525556 49828 ffffffff 00000000 S Heap thread poo
-u0_a110   5483  5474  1525556 49828 ffffffff 00000000 S Signal Catcher
-u0_a110   5484  5474  1525556 49828 ffffffff 00000000 S JDWP
-u0_a110   5485  5474  1525556 49828 ffffffff 00000000 S ReferenceQueueD
-u0_a110   5486  5474  1525556 49828 ffffffff 00000000 S FinalizerDaemon
-u0_a110   5487  5474  1525556 49828 ffffffff 00000000 S FinalizerWatchd
-u0_a110   5488  5474  1525556 49828 ffffffff 00000000 S HeapTrimmerDaem
-u0_a110   5489  5474  1525556 49828 ffffffff 00000000 S GCDaemon
-u0_a110   5490  5474  1525556 49828 ffffffff 00000000 S Binder_1
-u0_a110   5492  5474  1525556 49828 ffffffff 00000000 S Binder_2
-u0_a110   5503  5474  1525556 49828 ffffffff 00000000 S geHandlerThread
-u0_a110   5504  5474  1525556 49828 ffffffff 00000000 S GAThread
-u0_a110   5507  5474  1525556 49828 ffffffff 00000000 S Crashlytics Exc
-u0_a110   5510  5474  1525556 49828 ffffffff 00000000 S AsyncTask #1
-u0_a110   5515  5474  1525556 49828 ffffffff 00000000 S Crashlytics Tra
-u0_a110   5518  5474  1525556 49828 ffffffff 00000000 S AsyncTask #2
-u0_a110   5587  5474  1525556 49828 ffffffff 00000000 S AcceptThreadSec
-u0_a88    5519  205   1556696 65876 ffffffff 00000000 S com.dropbox.android
-u0_a88    5525  5519  1556696 65876 ffffffff 00000000 S Heap thread poo
-u0_a88    5526  5519  1556696 65876 ffffffff 00000000 S Heap thread poo
-u0_a88    5527  5519  1556696 65876 ffffffff 00000000 S Heap thread poo
-u0_a88    5528  5519  1556696 65876 ffffffff 00000000 S Signal Catcher
-u0_a88    5529  5519  1556696 65876 ffffffff 00000000 S JDWP
-u0_a88    5530  5519  1556696 65876 ffffffff 00000000 S ReferenceQueueD
-u0_a88    5531  5519  1556696 65876 ffffffff 00000000 S FinalizerDaemon
-u0_a88    5532  5519  1556696 65876 ffffffff 00000000 S FinalizerWatchd
-u0_a88    5533  5519  1556696 65876 ffffffff 00000000 S HeapTrimmerDaem
-u0_a88    5534  5519  1556696 65876 ffffffff 00000000 S GCDaemon
-u0_a88    5535  5519  1556696 65876 ffffffff 00000000 S Binder_1
-u0_a88    5536  5519  1556696 65876 ffffffff 00000000 S Binder_2
-u0_a88    5562  5519  1556696 65876 ffffffff 00000000 S Dropbox log upl
-u0_a88    5563  5519  1556696 65876 ffffffff 00000000 S gandalf updater
-u0_a88    5568  5519  1556696 65876 ffffffff 00000000 S pool-10-thread-
-u0_a88    5569  5519  1556696 65876 ffffffff 00000000 S DbxFileObserver
-u0_a88    5570  5519  1556696 65876 ffffffff 00000000 S LocalThumbManag
-u0_a88    5574  5519  1556696 65876 ffffffff 00000000 S local AsyncTask
-u0_a88    5575  5519  1556696 65876 ffffffff 00000000 S remote AsyncTas
-u0_a88    5576  5519  1556696 65876 ffffffff 00000000 S local AsyncTask
-u0_a88    5577  5519  1556696 65876 ffffffff 00000000 S remote AsyncTas
-u0_a88    5578  5519  1556696 65876 ffffffff 00000000 S Dropbox notif o
-u0_a88    5579  5519  1556696 65876 ffffffff 00000000 S Dropbox notif s
-u0_a88    5580  5519  1556696 65876 ffffffff 00000000 S Picasso-Stats
-u0_a88    5581  5519  1556696 65876 ffffffff 00000000 S Picasso-Dispatc
-u0_a88    5582  5519  1556696 65876 ffffffff 00000000 S Picasso-refQueu
-u0_a88    5583  5519  1556696 65876 ffffffff 00000000 S gandalf updater
-u0_a88    5592  5519  1556696 65876 ffffffff 00000000 S DbxFileObserver
-u0_a88    5593  5519  1556696 65876 ffffffff 00000000 S dbxpool-34:r-th
-u0_a88    5594  5519  1556696 65876 ffffffff 00000000 S dbxpool-32:au-t
-u0_a88    5595  5519  1556696 65876 ffffffff 00000000 S dbxpool-38:a-th
-u0_a88    5596  5519  1556696 65876 ffffffff 00000000 S Timer-0
-u0_a88    5597  5519  1556696 65876 ffffffff 00000000 S dbxpool-6:a-thr
-u0_a88    5599  5519  1556696 65876 ffffffff 00000000 S Timer-1
-u0_a88    5718  5519  1556696 65876 ffffffff 00000000 S RefQueueWorker@
-u0_a88    5750  5519  1556696 65876 ffffffff 00000000 S Thread-625
-u0_a88    5818  5519  1556696 65876 ffffffff 00000000 S Binder_3
-u0_a88    8569  5519  1556696 65876 ffffffff 00000000 S Binder_4
-u0_a88    8572  5519  1556696 65876 ffffffff 00000000 S Binder_5
-u0_a88    8580  5519  1556696 65876 ffffffff 00000000 S Binder_6
-u0_a93    5688  205   1496212 39724 ffffffff 00000000 S com.devuni.flashlight:remote
-u0_a93    5693  5688  1496212 39724 ffffffff 00000000 S Heap thread poo
-u0_a93    5694  5688  1496212 39724 ffffffff 00000000 S Heap thread poo
-u0_a93    5695  5688  1496212 39724 ffffffff 00000000 S Heap thread poo
-u0_a93    5697  5688  1496212 39724 ffffffff 00000000 S Signal Catcher
-u0_a93    5698  5688  1496212 39724 ffffffff 00000000 S JDWP
-u0_a93    5699  5688  1496212 39724 ffffffff 00000000 S ReferenceQueueD
-u0_a93    5700  5688  1496212 39724 ffffffff 00000000 S FinalizerDaemon
-u0_a93    5701  5688  1496212 39724 ffffffff 00000000 S FinalizerWatchd
-u0_a93    5702  5688  1496212 39724 ffffffff 00000000 S HeapTrimmerDaem
-u0_a93    5703  5688  1496212 39724 ffffffff 00000000 S GCDaemon
-u0_a93    5704  5688  1496212 39724 ffffffff 00000000 S Binder_1
-u0_a93    5705  5688  1496212 39724 ffffffff 00000000 S Binder_2
-u0_a93    12039 5688  1496212 39724 ffffffff 00000000 S pool-1-thread-1
-u0_a86    6202  205   1809096 86876 ffffffff 00000000 S com.tencent.mm
-u0_a86    6206  6202  1809096 86876 ffffffff 00000000 S Heap thread poo
-u0_a86    6207  6202  1809096 86876 ffffffff 00000000 S Heap thread poo
-u0_a86    6208  6202  1809096 86876 ffffffff 00000000 S Heap thread poo
-u0_a86    6211  6202  1809096 86876 ffffffff 00000000 S Signal Catcher
-u0_a86    6212  6202  1809096 86876 ffffffff 00000000 S JDWP
-u0_a86    6213  6202  1809096 86876 ffffffff 00000000 S ReferenceQueueD
-u0_a86    6214  6202  1809096 86876 ffffffff 00000000 S FinalizerDaemon
-u0_a86    6215  6202  1809096 86876 ffffffff 00000000 S FinalizerWatchd
-u0_a86    6217  6202  1809096 86876 ffffffff 00000000 S HeapTrimmerDaem
-u0_a86    6218  6202  1809096 86876 ffffffff 00000000 S GCDaemon
-u0_a86    6220  6202  1809096 86876 ffffffff 00000000 S Binder_1
-u0_a86    6221  6202  1809096 86876 ffffffff 00000000 S Binder_2
-u0_a86    6236  6202  1809096 86876 ffffffff 00000000 S THREAD_POOL_HAN
-u0_a86    6237  6202  1809096 86876 ffffffff 00000000 S com.tencent.mm
-u0_a86    6239  6202  1809096 86876 ffffffff 00000000 S com.tencent.mm
-u0_a86    6240  6202  1809096 86876 ffffffff 00000000 S MonitorHandlerT
-u0_a86    6241  6202  1809096 86876 ffffffff 00000000 S .ProcessManager
-u0_a86    6243  6202  1809096 86876 ffffffff 00000000 S Binder_3
-u0_a86    6245  6202  1809096 86876 ffffffff 00000000 S default
-u0_a86    6246  6202  1809096 86876 ffffffff 00000000 S com.tencent.mm
-u0_a86    6247  6202  1809096 86876 ffffffff 00000000 S MMHandlerThread
-u0_a86    6269  6202  1809096 86876 ffffffff 00000000 S MM_Thread_Pool_
-u0_a86    6270  6202  1809096 86876 ffffffff 00000000 S MM_Thread_Pool_
-u0_a86    6271  6202  1809096 86876 ffffffff 00000000 S MM_Thread_Pool_
-u0_a86    6272  6202  1809096 86876 ffffffff 00000000 S MM_Thread_Pool_
-u0_a86    6273  6202  1809096 86876 ffffffff 00000000 S MM_Thread_Pool_
-u0_a86    6274  6202  1809096 86876 ffffffff 00000000 S MM_Thread_Pool_
-u0_a86    6276  6202  1809096 86876 ffffffff 00000000 S ExdeviceHandler
-u0_a86    6277  6202  1809096 86876 ffffffff 00000000 S MM_Thread_Pool_
-u0_a86    6279  6202  1809096 86876 ffffffff 00000000 S RWCache_timeout
-u0_a86    6280  6202  1809096 86876 ffffffff 00000000 S RWCache_timeout
-u0_a86    6282  6202  1809096 86876 ffffffff 00000000 S MM_Thread_Pool_
-u0_a86    6284  6202  1809096 86876 ffffffff 00000000 S downloadStateCh
-u0_a86    6288  6202  1809096 86876 ffffffff 00000000 S WifiManager
-u0_a86    6289  6202  1809096 86876 ffffffff 00000000 S refresh Notific
-u0_a86    6292  6202  1809096 86876 ffffffff 00000000 S MM_Thread_Pool_
-u0_a86    6293  6202  1809096 86876 ffffffff 00000000 S MM_Thread_Pool_
-u0_a86    6294  6202  1809096 86876 ffffffff 00000000 S MM_Thread_Pool_
-u0_a86    6295  6202  1809096 86876 ffffffff 00000000 S SearchDaemon
-u0_a86    6303  6202  1809096 86876 ffffffff 00000000 S Binder_4
-u0_a86    6313  6202  1809096 86876 ffffffff 00000000 S pool-2-thread-1
-u0_a86    6373  6202  1809096 86876 ffffffff 00000000 S RWCache_timeout
-u0_a86    6408  6202  1809096 86876 ffffffff 00000000 S h
-u0_a86    7230  6202  1809096 86876 ffffffff 00000000 S default
-u0_a86    7231  6202  1809096 86876 ffffffff 00000000 S MMHandlerThread
-u0_a191   8839  205   1510312 57352 ffffffff 00000000 S com.ushaqi.zhuishushenqi:pushservice
-u0_a191   8845  8839  1510312 57352 ffffffff 00000000 S Heap thread poo
-u0_a191   8846  8839  1510312 57352 ffffffff 00000000 S Heap thread poo
-u0_a191   8847  8839  1510312 57352 ffffffff 00000000 S Heap thread poo
-u0_a191   8849  8839  1510312 57352 ffffffff 00000000 S Signal Catcher
-u0_a191   8850  8839  1510312 57352 ffffffff 00000000 S JDWP
-u0_a191   8851  8839  1510312 57352 ffffffff 00000000 S ReferenceQueueD
-u0_a191   8852  8839  1510312 57352 ffffffff 00000000 S FinalizerDaemon
-u0_a191   8853  8839  1510312 57352 ffffffff 00000000 S FinalizerWatchd
-u0_a191   8854  8839  1510312 57352 ffffffff 00000000 S HeapTrimmerDaem
-u0_a191   8855  8839  1510312 57352 ffffffff 00000000 S GCDaemon
-u0_a191   8856  8839  1510312 57352 ffffffff 00000000 S Binder_1
-u0_a191   8857  8839  1510312 57352 ffffffff 00000000 S Binder_2
-u0_a191   8867  8839  1510312 57352 ffffffff 00000000 S local_job_dispa
-u0_a191   8869  8839  1510312 57352 ffffffff 00000000 S remote_job_disp
-u0_a191   8887  8839  1510312 57352 ffffffff 00000000 S Upload Http Rec
-u0_a191   8890  8839  1510312 57352 ffffffff 00000000 S Connection Cont
-u0_a191   8963  8839  1510312 57352 ffffffff 00000000 S Smack Packet Re
-root      11634 2     0      0     ffffffff 00000000 S kworker/u:0
-root      11779 2     0      0     ffffffff 00000000 S kworker/0:3H
-root      11928 2     0      0     ffffffff 00000000 S kworker/0:1
-root      12431 2     0      0     ffffffff 00000000 S kworker/u:2
-u0_a85    12971 205   1595348 59000 ffffffff 00000000 S com.life360.android.safetymapd:service
-u0_a85    12977 12971 1595348 59000 ffffffff 00000000 S Heap thread poo
-u0_a85    12978 12971 1595348 59000 ffffffff 00000000 S Heap thread poo
-u0_a85    12979 12971 1595348 59000 ffffffff 00000000 S Heap thread poo
-u0_a85    12980 12971 1595348 59000 ffffffff 00000000 S Signal Catcher
-u0_a85    12981 12971 1595348 59000 ffffffff 00000000 S JDWP
-u0_a85    12982 12971 1595348 59000 ffffffff 00000000 S ReferenceQueueD
-u0_a85    12983 12971 1595348 59000 ffffffff 00000000 S FinalizerDaemon
-u0_a85    12984 12971 1595348 59000 ffffffff 00000000 S FinalizerWatchd
-u0_a85    12985 12971 1595348 59000 ffffffff 00000000 S HeapTrimmerDaem
-u0_a85    12986 12971 1595348 59000 ffffffff 00000000 S GCDaemon
-u0_a85    12987 12971 1595348 59000 ffffffff 00000000 S Binder_1
-u0_a85    12988 12971 1595348 59000 ffffffff 00000000 S Binder_2
-u0_a85    13099 12971 1595348 59000 ffffffff 00000000 S WifiManager
-u0_a106   13071 205   1523392 47680 ffffffff 00000000 S com.xianguo.tingguo
-u0_a106   13075 13071 1523392 47680 ffffffff 00000000 S Heap thread poo
-u0_a106   13076 13071 1523392 47680 ffffffff 00000000 S Heap thread poo
-u0_a106   13077 13071 1523392 47680 ffffffff 00000000 S Heap thread poo
-u0_a106   13080 13071 1523392 47680 ffffffff 00000000 S Signal Catcher
-u0_a106   13081 13071 1523392 47680 ffffffff 00000000 S JDWP
-u0_a106   13082 13071 1523392 47680 ffffffff 00000000 S ReferenceQueueD
-u0_a106   13083 13071 1523392 47680 ffffffff 00000000 S FinalizerDaemon
-u0_a106   13084 13071 1523392 47680 ffffffff 00000000 S FinalizerWatchd
-u0_a106   13085 13071 1523392 47680 ffffffff 00000000 S HeapTrimmerDaem
-u0_a106   13086 13071 1523392 47680 ffffffff 00000000 S GCDaemon
-u0_a106   13087 13071 1523392 47680 ffffffff 00000000 S Binder_1
-u0_a106   13088 13071 1523392 47680 ffffffff 00000000 S Binder_2
-u0_a106   13090 13071 1523392 47680 ffffffff 00000000 S SoundPool
-u0_a106   13091 13071 1523392 47680 ffffffff 00000000 S SoundPoolThread
-u0_a106   13276 13071 1523392 47680 ffffffff 00000000 S WifiManager
-u0_a65    13345 205   1526244 52680 ffffffff 00000000 S com.google.android.apps.photos
-u0_a65    13351 13345 1526244 52680 ffffffff 00000000 S Heap thread poo
-u0_a65    13352 13345 1526244 52680 ffffffff 00000000 S Heap thread poo
-u0_a65    13353 13345 1526244 52680 ffffffff 00000000 S Heap thread poo
-u0_a65    13354 13345 1526244 52680 ffffffff 00000000 S Signal Catcher
-u0_a65    13355 13345 1526244 52680 ffffffff 00000000 S JDWP
-u0_a65    13356 13345 1526244 52680 ffffffff 00000000 S ReferenceQueueD
-u0_a65    13357 13345 1526244 52680 ffffffff 00000000 S FinalizerDaemon
-u0_a65    13358 13345 1526244 52680 ffffffff 00000000 S FinalizerWatchd
-u0_a65    13359 13345 1526244 52680 ffffffff 00000000 S HeapTrimmerDaem
-u0_a65    13360 13345 1526244 52680 ffffffff 00000000 S GCDaemon
-u0_a65    13361 13345 1526244 52680 ffffffff 00000000 S Binder_1
-u0_a65    13362 13345 1526244 52680 ffffffff 00000000 S Binder_2
-u0_a65    13783 13345 1526244 52680 ffffffff 00000000 S pool-1-thread-1
-u0_a65    13796 13345 1526244 52680 ffffffff 00000000 S rotating_file-t
-u0_a65    13904 13345 1526244 52680 ffffffff 00000000 S Binder_3
-u0_a67    13491 205   1567688 56576 ffffffff 00000000 S com.google.android.apps.plus
-u0_a67    13493 13491 1567688 56576 ffffffff 00000000 S Heap thread poo
-u0_a67    13494 13491 1567688 56576 ffffffff 00000000 S Heap thread poo
-u0_a67    13495 13491 1567688 56576 ffffffff 00000000 S Heap thread poo
-u0_a67    13497 13491 1567688 56576 ffffffff 00000000 S Signal Catcher
-u0_a67    13499 13491 1567688 56576 ffffffff 00000000 S JDWP
-u0_a67    13502 13491 1567688 56576 ffffffff 00000000 S ReferenceQueueD
-u0_a67    13503 13491 1567688 56576 ffffffff 00000000 S FinalizerDaemon
-u0_a67    13504 13491 1567688 56576 ffffffff 00000000 S FinalizerWatchd
-u0_a67    13505 13491 1567688 56576 ffffffff 00000000 S HeapTrimmerDaem
-u0_a67    13506 13491 1567688 56576 ffffffff 00000000 S GCDaemon
-u0_a67    13507 13491 1567688 56576 ffffffff 00000000 S Binder_1
-u0_a67    13508 13491 1567688 56576 ffffffff 00000000 S Binder_2
-u0_a67    13512 13491 1567688 56576 ffffffff 00000000 S picasa-photo-pr
-u0_a67    13528 13491 1567688 56576 ffffffff 00000000 S iu-sync-manager
-u0_a67    13538 13491 1567688 56576 ffffffff 00000000 S pool-2-thread-1
-u0_a67    13881 13491 1567688 56576 ffffffff 00000000 S Gservices
-u0_a4     13516 205   1503264 48612 ffffffff 00000000 S android.process.acore
-u0_a4     13520 13516 1503264 48612 ffffffff 00000000 S Heap thread poo
-u0_a4     13521 13516 1503264 48612 ffffffff 00000000 S Heap thread poo
-u0_a4     13522 13516 1503264 48612 ffffffff 00000000 S Heap thread poo
-u0_a4     13525 13516 1503264 48612 ffffffff 00000000 S Signal Catcher
-u0_a4     13526 13516 1503264 48612 ffffffff 00000000 S JDWP
-u0_a4     13527 13516 1503264 48612 ffffffff 00000000 S ReferenceQueueD
-u0_a4     13529 13516 1503264 48612 ffffffff 00000000 S FinalizerDaemon
-u0_a4     13530 13516 1503264 48612 ffffffff 00000000 S FinalizerWatchd
-u0_a4     13531 13516 1503264 48612 ffffffff 00000000 S HeapTrimmerDaem
-u0_a4     13532 13516 1503264 48612 ffffffff 00000000 S GCDaemon
-u0_a4     13533 13516 1503264 48612 ffffffff 00000000 S Binder_1
-u0_a4     13534 13516 1503264 48612 ffffffff 00000000 S Binder_2
-u0_a4     13536 13516 1503264 48612 ffffffff 00000000 S ContactsProvide
-u0_a4     13537 13516 1503264 48612 ffffffff 00000000 S CallLogProvider
-u0_a102   13613 205   1521420 45204 ffffffff 00000000 S com.sohu.inputmethod.sogou:classic
-u0_a102   13616 13613 1521420 45204 ffffffff 00000000 S Heap thread poo
-u0_a102   13617 13613 1521420 45204 ffffffff 00000000 S Heap thread poo
-u0_a102   13618 13613 1521420 45204 ffffffff 00000000 S Heap thread poo
-u0_a102   13620 13613 1521420 45204 ffffffff 00000000 S Signal Catcher
-u0_a102   13623 13613 1521420 45204 ffffffff 00000000 S JDWP
-u0_a102   13624 13613 1521420 45204 ffffffff 00000000 S ReferenceQueueD
-u0_a102   13625 13613 1521420 45204 ffffffff 00000000 S FinalizerDaemon
-u0_a102   13626 13613 1521420 45204 ffffffff 00000000 S FinalizerWatchd
-u0_a102   13627 13613 1521420 45204 ffffffff 00000000 S HeapTrimmerDaem
-u0_a102   13628 13613 1521420 45204 ffffffff 00000000 S GCDaemon
-u0_a102   13629 13613 1521420 45204 ffffffff 00000000 S Binder_1
-u0_a102   13630 13613 1521420 45204 ffffffff 00000000 S Binder_2
-u0_a102   13635 13613 1521420 45204 ffffffff 00000000 S Thread-1443
-u0_a102   13636 13613 1521420 45204 ffffffff 00000000 S Thread-1444
-u0_a102   13637 13613 1521420 45204 ffffffff 00000000 S Thread-1445
-u0_a102   13638 13613 1521420 45204 ffffffff 00000000 S Thread-1446
-u0_a102   13639 13613 1521420 45204 ffffffff 00000000 S Thread-1447
-u0_a102   13641 13613 1521420 45204 ffffffff 00000000 S WifiManager
-u0_a102   13905 13613 1521420 45204 ffffffff 00000000 S Binder_3
-u0_a102   13647 205   1514052 44264 ffffffff 00000000 S com.sohu.inputmethod.sogou
-u0_a102   13651 13647 1514052 44264 ffffffff 00000000 S Heap thread poo
-u0_a102   13652 13647 1514052 44264 ffffffff 00000000 S Heap thread poo
-u0_a102   13653 13647 1514052 44264 ffffffff 00000000 S Heap thread poo
-u0_a102   13656 13647 1514052 44264 ffffffff 00000000 S Signal Catcher
-u0_a102   13657 13647 1514052 44264 ffffffff 00000000 S JDWP
-u0_a102   13658 13647 1514052 44264 ffffffff 00000000 S ReferenceQueueD
-u0_a102   13659 13647 1514052 44264 ffffffff 00000000 S FinalizerDaemon
-u0_a102   13660 13647 1514052 44264 ffffffff 00000000 S FinalizerWatchd
-u0_a102   13661 13647 1514052 44264 ffffffff 00000000 S HeapTrimmerDaem
-u0_a102   13662 13647 1514052 44264 ffffffff 00000000 S GCDaemon
-u0_a102   13663 13647 1514052 44264 ffffffff 00000000 S Binder_1
-u0_a102   13664 13647 1514052 44264 ffffffff 00000000 S Binder_2
-u0_a102   13671 205   1519416 43248 ffffffff 00000000 S sogou.mobile.explorer.hotwords
-u0_a102   13677 13671 1519416 43248 ffffffff 00000000 S Heap thread poo
-u0_a102   13678 13671 1519416 43248 ffffffff 00000000 S Heap thread poo
-u0_a102   13679 13671 1519416 43248 ffffffff 00000000 S Heap thread poo
-u0_a102   13680 13671 1519416 43248 ffffffff 00000000 S Signal Catcher
-u0_a102   13681 13671 1519416 43248 ffffffff 00000000 S JDWP
-u0_a102   13682 13671 1519416 43248 ffffffff 00000000 S ReferenceQueueD
-u0_a102   13683 13671 1519416 43248 ffffffff 00000000 S FinalizerDaemon
-u0_a102   13684 13671 1519416 43248 ffffffff 00000000 S FinalizerWatchd
-u0_a102   13685 13671 1519416 43248 ffffffff 00000000 S HeapTrimmerDaem
-u0_a102   13686 13671 1519416 43248 ffffffff 00000000 S GCDaemon
-u0_a102   13687 13671 1519416 43248 ffffffff 00000000 S Binder_1
-u0_a102   13688 13671 1519416 43248 ffffffff 00000000 S Binder_2
-u0_a102   13690 13671 1519416 43248 ffffffff 00000000 S pool-1-thread-1
-u0_a102   13691 13671 1519416 43248 ffffffff 00000000 S pool-1-thread-2
-u0_a102   13692 13671 1519416 43248 ffffffff 00000000 S pool-1-thread-3
-u0_a102   13694 13671 1519416 43248 ffffffff 00000000 S Timer-0
-u0_a198   13695 205   1506040 40332 ffffffff 00000000 S org.chromium.chrome.shell
-u0_a198   13701 13695 1506040 40332 ffffffff 00000000 S Heap thread poo
-u0_a198   13702 13695 1506040 40332 ffffffff 00000000 S Heap thread poo
-u0_a198   13703 13695 1506040 40332 ffffffff 00000000 S Heap thread poo
-u0_a198   13704 13695 1506040 40332 ffffffff 00000000 S Signal Catcher
-u0_a198   13705 13695 1506040 40332 ffffffff 00000000 S JDWP
-u0_a198   13706 13695 1506040 40332 ffffffff 00000000 S ReferenceQueueD
-u0_a198   13707 13695 1506040 40332 ffffffff 00000000 S FinalizerDaemon
-u0_a198   13708 13695 1506040 40332 ffffffff 00000000 S FinalizerWatchd
-u0_a198   13709 13695 1506040 40332 ffffffff 00000000 S HeapTrimmerDaem
-u0_a198   13710 13695 1506040 40332 ffffffff 00000000 S GCDaemon
-u0_a198   13711 13695 1506040 40332 ffffffff 00000000 S Binder_1
-u0_a198   13712 13695 1506040 40332 ffffffff 00000000 S Binder_2
-u0_a198   13713 13695 1506040 40332 ffffffff 00000000 S Binder_3
-u0_a200   13715 205   1511344 38748 ffffffff 00000000 S com.rolocule.motiontennis
-u0_a200   13721 13715 1511344 38748 ffffffff 00000000 S Heap thread poo
-u0_a200   13722 13715 1511344 38748 ffffffff 00000000 S Heap thread poo
-u0_a200   13723 13715 1511344 38748 ffffffff 00000000 S Heap thread poo
-u0_a200   13724 13715 1511344 38748 ffffffff 00000000 S Signal Catcher
-u0_a200   13725 13715 1511344 38748 ffffffff 00000000 S JDWP
-u0_a200   13731 13715 1511344 38748 ffffffff 00000000 S ReferenceQueueD
-u0_a200   13732 13715 1511344 38748 ffffffff 00000000 S FinalizerDaemon
-u0_a200   13733 13715 1511344 38748 ffffffff 00000000 S FinalizerWatchd
-u0_a200   13734 13715 1511344 38748 ffffffff 00000000 S HeapTrimmerDaem
-u0_a200   13735 13715 1511344 38748 ffffffff 00000000 S GCDaemon
-u0_a200   13736 13715 1511344 38748 ffffffff 00000000 S Binder_1
-u0_a200   13737 13715 1511344 38748 ffffffff 00000000 S Binder_2
-u0_a175   13747 205   1510096 43460 ffffffff 00000000 S com.google.android.apps.chrome
-u0_a175   13751 13747 1510096 43460 ffffffff 00000000 S Heap thread poo
-u0_a175   13752 13747 1510096 43460 ffffffff 00000000 S Heap thread poo
-u0_a175   13754 13747 1510096 43460 ffffffff 00000000 S Heap thread poo
-u0_a175   13756 13747 1510096 43460 ffffffff 00000000 S Signal Catcher
-u0_a175   13757 13747 1510096 43460 ffffffff 00000000 S JDWP
-u0_a175   13758 13747 1510096 43460 ffffffff 00000000 S ReferenceQueueD
-u0_a175   13759 13747 1510096 43460 ffffffff 00000000 S FinalizerDaemon
-u0_a175   13760 13747 1510096 43460 ffffffff 00000000 S FinalizerWatchd
-u0_a175   13761 13747 1510096 43460 ffffffff 00000000 S HeapTrimmerDaem
-u0_a175   13762 13747 1510096 43460 ffffffff 00000000 S GCDaemon
-u0_a175   13763 13747 1510096 43460 ffffffff 00000000 S Binder_1
-u0_a175   13764 13747 1510096 43460 ffffffff 00000000 S Binder_2
-u0_a85    13774 205   1594212 50972 ffffffff 00000000 S com.life360.android.safetymapd
-u0_a85    13780 13774 1594212 50972 ffffffff 00000000 S Heap thread poo
-u0_a85    13781 13774 1594212 50972 ffffffff 00000000 S Heap thread poo
-u0_a85    13782 13774 1594212 50972 ffffffff 00000000 S Heap thread poo
-u0_a85    13784 13774 1594212 50972 ffffffff 00000000 S Signal Catcher
-u0_a85    13785 13774 1594212 50972 ffffffff 00000000 S JDWP
-u0_a85    13786 13774 1594212 50972 ffffffff 00000000 S ReferenceQueueD
-u0_a85    13787 13774 1594212 50972 ffffffff 00000000 S FinalizerDaemon
-u0_a85    13788 13774 1594212 50972 ffffffff 00000000 S FinalizerWatchd
-u0_a85    13789 13774 1594212 50972 ffffffff 00000000 S HeapTrimmerDaem
-u0_a85    13790 13774 1594212 50972 ffffffff 00000000 S GCDaemon
-u0_a85    13791 13774 1594212 50972 ffffffff 00000000 S Binder_1
-u0_a85    13792 13774 1594212 50972 ffffffff 00000000 S Binder_2
-u0_a16    13801 205   1538004 50644 ffffffff 00000000 S com.android.vending
-u0_a16    13807 13801 1538004 50644 ffffffff 00000000 S Heap thread poo
-u0_a16    13808 13801 1538004 50644 ffffffff 00000000 S Heap thread poo
-u0_a16    13809 13801 1538004 50644 ffffffff 00000000 S Heap thread poo
-u0_a16    13811 13801 1538004 50644 ffffffff 00000000 S Signal Catcher
-u0_a16    13812 13801 1538004 50644 ffffffff 00000000 S JDWP
-u0_a16    13813 13801 1538004 50644 ffffffff 00000000 S ReferenceQueueD
-u0_a16    13814 13801 1538004 50644 ffffffff 00000000 S FinalizerDaemon
-u0_a16    13815 13801 1538004 50644 ffffffff 00000000 S FinalizerWatchd
-u0_a16    13816 13801 1538004 50644 ffffffff 00000000 S HeapTrimmerDaem
-u0_a16    13817 13801 1538004 50644 ffffffff 00000000 S GCDaemon
-u0_a16    13818 13801 1538004 50644 ffffffff 00000000 S Binder_1
-u0_a16    13819 13801 1538004 50644 ffffffff 00000000 S Binder_2
-u0_a16    13828 13801 1538004 50644 ffffffff 00000000 S Gservices
-u0_a16    13833 13801 1538004 50644 ffffffff 00000000 S pool-1-thread-1
-u0_a16    13834 13801 1538004 50644 ffffffff 00000000 S RefQueueWorker@
-u0_a16    13837 13801 1538004 50644 ffffffff 00000000 S RefQueueWorker@
-u0_a16    13838 13801 1538004 50644 ffffffff 00000000 S Thread-1482
-u0_a16    13839 13801 1538004 50644 ffffffff 00000000 S Thread-1483
-u0_a16    13840 13801 1538004 50644 ffffffff 00000000 S Thread-1484
-u0_a16    13843 13801 1538004 50644 ffffffff 00000000 S download-manage
-u0_a16    13844 13801 1538004 50644 ffffffff 00000000 S NetworkQualityQ
-u0_a16    13845 13801 1538004 50644 ffffffff 00000000 S RefQueueWorker@
-u0_a16    13846 13801 1538004 50644 ffffffff 00000000 S Thread-1489
-u0_a16    13847 13801 1538004 50644 ffffffff 00000000 S Thread-1490
-u0_a16    13848 13801 1538004 50644 ffffffff 00000000 S Thread-1491
-u0_a16    13849 13801 1538004 50644 ffffffff 00000000 S Thread-1492
-u0_a16    13850 13801 1538004 50644 ffffffff 00000000 S Thread-1493
-u0_a16    13851 13801 1538004 50644 ffffffff 00000000 S PlayEventLogger
-u0_a16    13852 13801 1538004 50644 ffffffff 00000000 S tentative-gc-ru
-u0_a16    13862 13801 1538004 50644 ffffffff 00000000 S libraries-threa
-u0_a16    13872 13801 1538004 50644 ffffffff 00000000 S AsyncTask #1
-u0_a16    13876 13801 1538004 50644 ffffffff 00000000 S AsyncTask #2
-u0_a16    13877 13801 1538004 50644 ffffffff 00000000 S AsyncTask #3
-u0_a16    13878 13801 1538004 50644 ffffffff 00000000 S PlayEventLogger
-u0_a16    13880 13801 1538004 50644 ffffffff 00000000 S Thread-1501
-u0_a16    14407 13801 1538004 50644 ffffffff 00000000 S Binder_3
-u0_a8     13853 205   1573700 51720 ffffffff 00000000 S com.google.android.gms:car
-u0_a8     13856 13853 1573700 51720 ffffffff 00000000 S Heap thread poo
-u0_a8     13857 13853 1573700 51720 ffffffff 00000000 S Heap thread poo
-u0_a8     13858 13853 1573700 51720 ffffffff 00000000 S Heap thread poo
-u0_a8     13863 13853 1573700 51720 ffffffff 00000000 S Signal Catcher
-u0_a8     13864 13853 1573700 51720 ffffffff 00000000 S JDWP
-u0_a8     13865 13853 1573700 51720 ffffffff 00000000 S ReferenceQueueD
-u0_a8     13866 13853 1573700 51720 ffffffff 00000000 S FinalizerDaemon
-u0_a8     13867 13853 1573700 51720 ffffffff 00000000 S FinalizerWatchd
-u0_a8     13868 13853 1573700 51720 ffffffff 00000000 S HeapTrimmerDaem
-u0_a8     13869 13853 1573700 51720 ffffffff 00000000 S GCDaemon
-u0_a8     13870 13853 1573700 51720 ffffffff 00000000 S Binder_1
-u0_a8     13871 13853 1573700 51720 ffffffff 00000000 S Binder_2
-u0_a8     13873 13853 1573700 51720 ffffffff 00000000 S Gservices
-u0_a8     13913 13853 1573700 51720 ffffffff 00000000 S Binder_3
-u0_a8     13885 205   1572668 47460 ffffffff 00000000 S com.google.android.gms.wearable
-u0_a8     13890 13885 1572668 47460 ffffffff 00000000 S Heap thread poo
-u0_a8     13891 13885 1572668 47460 ffffffff 00000000 S Heap thread poo
-u0_a8     13892 13885 1572668 47460 ffffffff 00000000 S Heap thread poo
-u0_a8     13894 13885 1572668 47460 ffffffff 00000000 S Signal Catcher
-u0_a8     13895 13885 1572668 47460 ffffffff 00000000 S JDWP
-u0_a8     13896 13885 1572668 47460 ffffffff 00000000 S ReferenceQueueD
-u0_a8     13897 13885 1572668 47460 ffffffff 00000000 S FinalizerDaemon
-u0_a8     13898 13885 1572668 47460 ffffffff 00000000 S FinalizerWatchd
-u0_a8     13899 13885 1572668 47460 ffffffff 00000000 S HeapTrimmerDaem
-u0_a8     13900 13885 1572668 47460 ffffffff 00000000 S GCDaemon
-u0_a8     13901 13885 1572668 47460 ffffffff 00000000 S Binder_1
-u0_a8     13902 13885 1572668 47460 ffffffff 00000000 S Binder_2
-u0_a8     13903 13885 1572668 47460 ffffffff 00000000 S Gservices
-root      14061 2     0      0     ffffffff 00000000 S kworker/u:3
-root      14136 2     0      0     ffffffff 00000000 S kworker/0:0H
-u0_a101   14356 205   1503136 44308 ffffffff 00000000 S com.google.android.apps.gcs
-u0_a101   14362 14356 1503136 44308 ffffffff 00000000 S Heap thread poo
-u0_a101   14363 14356 1503136 44308 ffffffff 00000000 S Heap thread poo
-u0_a101   14364 14356 1503136 44308 ffffffff 00000000 S Heap thread poo
-u0_a101   14365 14356 1503136 44308 ffffffff 00000000 S Signal Catcher
-u0_a101   14366 14356 1503136 44308 ffffffff 00000000 S JDWP
-u0_a101   14367 14356 1503136 44308 ffffffff 00000000 S ReferenceQueueD
-u0_a101   14368 14356 1503136 44308 ffffffff 00000000 S FinalizerDaemon
-u0_a101   14369 14356 1503136 44308 ffffffff 00000000 S FinalizerWatchd
-u0_a101   14370 14356 1503136 44308 ffffffff 00000000 S HeapTrimmerDaem
-u0_a101   14371 14356 1503136 44308 ffffffff 00000000 S GCDaemon
-u0_a101   14372 14356 1503136 44308 ffffffff 00000000 S Binder_1
-u0_a101   14373 14356 1503136 44308 ffffffff 00000000 S Binder_2
-u0_a101   14375 14356 1503136 44308 ffffffff 00000000 S Gservices
-u0_a101   14376 14356 1503136 44308 ffffffff 00000000 S RefQueueWorker@
-u0_a101   14377 14356 1503136 44308 ffffffff 00000000 S Thread-1495
-u0_a101   14378 14356 1503136 44308 ffffffff 00000000 S Thread-1496
-u0_a101   14379 14356 1503136 44308 ffffffff 00000000 S Thread-1497
-u0_a101   14380 14356 1503136 44308 ffffffff 00000000 S Thread-1498
-u0_a101   14381 14356 1503136 44308 ffffffff 00000000 S Thread-1499
-shell     14444 209   9316   612   c01a863c b6eeee44 S /system/bin/sh
-shell     14448 14444 10672  768   00000000 b6ef0da8 R ps
diff --git a/sdk/platform-tools/systrace/catapult/systrace/systrace/test_data/atrace_ps_dump_2 b/sdk/platform-tools/systrace/catapult/systrace/systrace/test_data/atrace_ps_dump_2
new file mode 100644
index 0000000..20985c7
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/systrace/systrace/test_data/atrace_ps_dump_2
@@ -0,0 +1,11 @@
+adb server version (36) doesn't match this client (38); killing...
+* daemon not running. starting it now on port 5037 *
+* daemon started successfully *
+USER     PID   PPID  VSIZE  RSS     WCHAN    PC        NAME
+root      1     0     8784   712   ffffffff 00000000 S /init
+root      2     0     0      0     ffffffff 00000000 S kthreadd
+root      3     2     0      0     ffffffff 00000000 S ksoftirqd/0
+root      7     2     0      0     ffffffff 00000000 D kworker/u:0H
+root      8     2     0      0     ffffffff 00000000 S migration/0
+root      13    2     0      0     ffffffff 00000000 S khelper
+root      14    2     0      0     ffffffff 00000000 S netns
diff --git a/sdk/platform-tools/systrace/catapult/systrace/systrace/test_data/atrace_ps_dump_3 b/sdk/platform-tools/systrace/catapult/systrace/systrace/test_data/atrace_ps_dump_3
new file mode 100644
index 0000000..797c61f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/systrace/systrace/test_data/atrace_ps_dump_3
@@ -0,0 +1,11 @@
+adb server version (36) doesn't match this client (38); killing...
+* daemon not running. starting it now on port 5037 *
+* daemon started successfully *
+USER     TID   PPID  VSIZE  RSS     WCHAN    PC      S CMD
+root      1     0     8784   712   ffffffff 00000000 S /init
+root      2     0     0      0     ffffffff 00000000 S kthreadd
+root      3     2     0      0     ffffffff 00000000 S ksoftirqd/0
+root      7     2     0      0     ffffffff 00000000 D kworker/u:0H
+root      8     2     0      0     ffffffff 00000000 S migration/0
+root      13    2     0      0     ffffffff 00000000 S khelper
+root      14    2     0      0     ffffffff 00000000 S netns
diff --git a/sdk/platform-tools/systrace/catapult/systrace/systrace/tracing_agents/agents_unittest.py b/sdk/platform-tools/systrace/catapult/systrace/systrace/tracing_agents/agents_unittest.py
new file mode 100644
index 0000000..74498bd
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/systrace/systrace/tracing_agents/agents_unittest.py
@@ -0,0 +1,50 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from systrace import util
+
+from devil.android import device_utils
+from devil.android.sdk import intent
+from devil.android.sdk import keyevent
+
+
+class BaseAgentTest(unittest.TestCase):
+  def setUp(self):
+    devices = device_utils.DeviceUtils.HealthyDevices()
+    self.browser = 'stable'
+    self.package_info = util.get_supported_browsers()[self.browser]
+    self.device = devices[0]
+
+    curr_browser = self.GetChromeProcessID()
+    if curr_browser == None:
+      self.StartBrowser()
+
+  def tearDown(self):
+    # Stop the browser after each test to ensure that it doesn't interfere
+    # with subsequent tests, e.g. by holding the devtools socket open.
+    self.device.ForceStop(self.package_info.package)
+
+  def StartBrowser(self):
+    # Turn on the device screen.
+    self.device.SetScreen(True)
+
+    # Unlock device.
+    self.device.SendKeyEvent(keyevent.KEYCODE_MENU)
+
+    # Start browser.
+    self.device.StartActivity(
+      intent.Intent(activity=self.package_info.activity,
+                    package=self.package_info.package,
+                    data='about:blank',
+                    extras={'create_new_tab': True}),
+      blocking=True, force_stop=True)
+
+  def GetChromeProcessID(self):
+    chrome_processes = self.device.GetPids(self.package_info.package)
+    if (self.package_info.package in chrome_processes and
+        len(chrome_processes[self.package_info.package]) > 0):
+      return chrome_processes[self.package_info.package][0]
+    return None
diff --git a/sdk/platform-tools/systrace/catapult/systrace/systrace/tracing_agents/atrace_agent.py b/sdk/platform-tools/systrace/catapult/systrace/systrace/tracing_agents/atrace_agent.py
index 64be870..a22a6aa 100644
--- a/sdk/platform-tools/systrace/catapult/systrace/systrace/tracing_agents/atrace_agent.py
+++ b/sdk/platform-tools/systrace/catapult/systrace/systrace/tracing_agents/atrace_agent.py
@@ -5,18 +5,17 @@
 import optparse
 import py_utils
 import re
-import subprocess
 import sys
 import threading
 import zlib
 
 from devil.android import device_utils
+from devil.android.sdk import version_codes
 from py_trace_event import trace_time as trace_time_module
 from systrace import trace_result
 from systrace import tracing_agents
 from systrace import util
 
-
 # Text that ADB sends, but does not need to be displayed to the user.
 ADB_IGNORE_REGEXP = r'^capturing trace\.\.\. done|^capturing trace\.\.\.'
 # The number of seconds to wait on output from ADB.
@@ -25,7 +24,8 @@
 ATRACE_BASE_ARGS = ['atrace']
 # If a custom list of categories is not specified, traces will include
 # these categories (if available on the device).
-DEFAULT_CATEGORIES = 'sched gfx view dalvik webview input disk am wm'.split()
+DEFAULT_CATEGORIES = 'sched,freq,gfx,view,dalvik,webview,'\
+                     'input,disk,am,wm,rs,binder_driver'
 # The command to list trace categories.
 LIST_CATEGORIES_ARGS = ATRACE_BASE_ARGS + ['--list_categories']
 # Minimum number of seconds between displaying status updates.
@@ -34,10 +34,9 @@
 TRACE_START_REGEXP = r'TRACE\:'
 # Plain-text trace data should always start with this string.
 TRACE_TEXT_HEADER = '# tracer'
-# The property name for switching on and off tracing during boot.
-BOOTTRACE_PROP = 'persist.debug.atrace.boottrace'
-# The file path for specifying categories to be traced during boot.
-BOOTTRACE_CATEGORIES = '/data/misc/boottrace/categories'
+_FIX_THREAD_IDS = True
+_FIX_MISSING_TGIDS = True
+_FIX_CIRCULAR_TRACES = True
 
 
 def list_categories(config):
@@ -50,19 +49,34 @@
       config: Tracing config.
   """
   devutils = device_utils.DeviceUtils(config.device_serial_number)
-  print '\n'.join(devutils.RunShellCommand(LIST_CATEGORIES_ARGS))
+  categories = devutils.RunShellCommand(
+      LIST_CATEGORIES_ARGS, check_return=True)
+
+  device_sdk_version = util.get_device_sdk_version()
+  if device_sdk_version < version_codes.MARSHMALLOW:
+    # work around platform bug where rs tag would corrupt trace until M(Api23)
+    categories = [c for c in categories if not re.match(r'^\s*rs\s*-', c)]
+
+  print '\n'.join(categories)
   if not devutils.HasRoot():
     print '\nNOTE: more categories may be available with adb root\n'
 
 
-def get_available_categories(config):
+def get_available_categories(config, device_sdk_version):
   """Gets the list of atrace categories available for tracing.
   Args:
       config: Tracing config.
+      device_sdk_version: Sdk version int of device to be queried.
   """
   devutils = device_utils.DeviceUtils(config.device_serial_number)
-  categories_output = devutils.RunShellCommand(LIST_CATEGORIES_ARGS)
-  return [c.split('-')[0].strip() for c in categories_output]
+  categories_output = devutils.RunShellCommand(
+      LIST_CATEGORIES_ARGS, check_return=True)
+  categories = [c.split('-')[0].strip() for c in categories_output]
+
+  if device_sdk_version < version_codes.MARSHMALLOW:
+    # work around platform bug where rs tag would corrupt trace until M(Api23)
+    categories = [c for c in categories if c != 'rs']
+  return categories
 
 
 def try_create_agent(config):
@@ -76,19 +90,17 @@
   if config.from_file is not None:
     return None
 
-  # Check device SDK version.
-  device_sdk_version = util.get_device_sdk_version()
-  if device_sdk_version <= 17:
-    print ('Device SDK versions <= 17 not supported.\n'
-           'Your device SDK version is %d.' % device_sdk_version)
-    return None
-  if device_sdk_version <= 22 and config.boot:
-    print ('--boot option does not work on the device SDK '
-           'version 22 or before.\nYour device SDK version '
-           'is %d.' % device_sdk_version)
+  if not config.atrace_categories:
     return None
 
-  return BootAgent() if config.boot else AtraceAgent()
+  # Check device SDK version.
+  device_sdk_version = util.get_device_sdk_version()
+  if device_sdk_version < version_codes.JELLY_BEAN_MR2:
+    print ('Device SDK versions < 18 (Jellybean MR2) not supported.\n'
+           'Your device SDK version is %d.' % device_sdk_version)
+    return None
+
+  return AtraceAgent(device_sdk_version)
 
 def _construct_extra_atrace_args(config, categories):
   """Construct extra arguments (-a, -k, categories) for atrace command.
@@ -137,8 +149,9 @@
 
 class AtraceAgent(tracing_agents.TracingAgent):
 
-  def __init__(self):
+  def __init__(self, device_sdk_version):
     super(AtraceAgent, self).__init__()
+    self._device_sdk_version = device_sdk_version
     self._adb = None
     self._trace_data = None
     self._tracer_args = None
@@ -148,22 +161,29 @@
     self._config = None
     self._categories = None
 
+  def __repr__(self):
+    return 'atrace'
+
   @py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
   def StartAgentTracing(self, config, timeout=None):
+    assert config.atrace_categories, 'Atrace categories are missing!'
     self._config = config
     self._categories = config.atrace_categories
-    if not self._categories:
-      self._categories = DEFAULT_CATEGORIES
-    avail_cats = get_available_categories(config)
-    unavailable = [x for x in self._categories if x not in avail_cats]
-    self._categories = [x for x in self._categories if x in avail_cats]
+    if isinstance(self._categories, list):
+      self._categories = ','.join(self._categories)
+    avail_cats = get_available_categories(config, self._device_sdk_version)
+    unavailable = [x for x in self._categories.split(',') if
+        x not in avail_cats]
+    self._categories = [x for x in self._categories.split(',') if
+        x in avail_cats]
     if unavailable:
       print 'These categories are unavailable: ' + ' '.join(unavailable)
     self._device_utils = device_utils.DeviceUtils(config.device_serial_number)
     self._device_serial_number = config.device_serial_number
     self._tracer_args = _construct_atrace_args(config,
                                                self._categories)
-    self._device_utils.RunShellCommand(self._tracer_args + ['--async_start'])
+    self._device_utils.RunShellCommand(
+        self._tracer_args + ['--async_start'], check_return=True)
     return True
 
   def _collect_and_preprocess(self):
@@ -210,27 +230,28 @@
       shell.RunCommand(cmd, close=True)
       did_record_sync_marker_callback(t1, sync_id)
 
-  def _dump_trace(self):
-    """Dumps the atrace buffer and returns the dumped buffer."""
-    dump_cmd = self._tracer_args + ['--async_dump']
-    return self._device_utils.RunShellCommand(dump_cmd, raw_output=True)
-
   def _stop_trace(self):
     """Stops atrace.
 
-    Tries to stop the atrace asynchronously. Note that on some devices,
-    --async-stop does not work. Thus, this uses the fallback
-    method of running a zero-length synchronous trace if that fails.
-    """
-    self._device_utils.RunShellCommand(self._tracer_args + ['--async_stop'])
-    is_trace_enabled_cmd = ['cat', '/sys/kernel/debug/tracing/tracing_on']
-    trace_on = int(self._device_utils.RunShellCommand(is_trace_enabled_cmd)[0])
-    if trace_on:
-      self._device_utils.RunShellCommand(self._tracer_args + ['-t 0'])
+    Note that prior to Api 23, --async-stop may not actually stop tracing.
+    Thus, this uses a fallback method of running a zero-length synchronous
+    trace if tracing is still on."""
+    self._device_utils.RunShellCommand(
+        self._tracer_args + ['--async_stop'], check_return=True)
+    is_trace_enabled_file = '/sys/kernel/debug/tracing/tracing_on'
+
+    if self._device_sdk_version < version_codes.MARSHMALLOW:
+      if int(self._device_utils.ReadFile(is_trace_enabled_file)):
+        # tracing was incorrectly left on, disable it
+        self._device_utils.RunShellCommand(
+            self._tracer_args + ['-t 0'], check_return=True)
 
   def _collect_trace_data(self):
     """Reads the output from atrace and stops the trace."""
-    result = self._dump_trace()
+    dump_cmd = self._tracer_args + ['--async_dump']
+    result = self._device_utils.RunShellCommand(
+        dump_cmd, raw_output=True, check_return=True)
+
     data_start = re.search(TRACE_START_REGEXP, result)
     if data_start:
       data_start = data_start.end(0)
@@ -256,90 +277,50 @@
                             'written.')
       sys.exit(1)
 
-    if self._config.fix_threads:
+    if _FIX_THREAD_IDS:
       # Issue ps command to device and patch thread names
-      ps_dump = do_preprocess_adb_cmd('ps -t',
-                                      self._config.device_serial_number)
-      if ps_dump is not None:
-        thread_names = extract_thread_list(ps_dump)
-        trace_data = fix_thread_names(trace_data, thread_names)
+      # TODO(catapult:#3215): Migrate to device.GetPids()
+      ps_dump = self._device_utils.RunShellCommand(
+          'ps -T -o USER,TID,PPID,VSIZE,RSS,WCHAN,ADDR=PC,S,CMD || ps -t',
+          shell=True, check_return=True)
+      thread_names = extract_thread_list(ps_dump)
+      trace_data = fix_thread_names(trace_data, thread_names)
 
-    if self._config.fix_tgids:
+    if _FIX_MISSING_TGIDS:
       # Issue printf command to device and patch tgids
-      procfs_dump = do_preprocess_adb_cmd('printf "%s\n" ' +
-                                          '/proc/[0-9]*/task/[0-9]*',
-                                          self._config.device_serial_number)
-      if procfs_dump is not None:
-        pid2_tgid = extract_tgids(procfs_dump)
-        trace_data = fix_missing_tgids(trace_data, pid2_tgid)
+      procfs_dump = self._device_utils.RunShellCommand(
+          'printf "%s\n" /proc/[0-9]*/task/[0-9]*',
+          shell=True, check_return=True)
+      pid2_tgid = extract_tgids(procfs_dump)
+      trace_data = fix_missing_tgids(trace_data, pid2_tgid)
 
-    if self._config.fix_circular:
+    if _FIX_CIRCULAR_TRACES:
       trace_data = fix_circular_traces(trace_data)
 
     return trace_data
 
 
-class BootAgent(AtraceAgent):
-  """AtraceAgent that specializes in tracing the boot sequence."""
-
-  def __init__(self):
-    super(BootAgent, self).__init__()
-
-  @py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
-  def StartAgentTracing(self, config, timeout=None):
-    self._config = config
-    try:
-      setup_args = _construct_boot_setup_command(config)
-      subprocess.check_call(setup_args)
-    except OSError as error:
-      print >> sys.stderr, (
-          'The command "%s" failed with the following error:' %
-          ' '.join(setup_args))
-      print >> sys.stderr, '    ', error
-      sys.exit(1)
-
-  def _dump_trace(self): #called by StopAgentTracing
-    """Dumps the running trace asynchronously and returns the dumped trace."""
-    dump_cmd = _construct_boot_trace_command(self._config)
-    return self._device_utils.RunShellCommand(dump_cmd, raw_output=True)
-
-  def _stop_trace(self): # called by _collect_trace_data via StopAgentTracing
-    # pylint: disable=no-self-use
-    # This is a member function for consistency with AtraceAgent
-    pass # don't need to stop separately; already done in dump_trace
-
-def _construct_boot_setup_command(config):
-  echo_args = (['echo'] + config.atrace_categories +
-               ['>', BOOTTRACE_CATEGORIES])
-  setprop_args = ['setprop', BOOTTRACE_PROP, '1']
-  reboot_args = ['reboot']
-  return util.construct_adb_shell_command(
-      echo_args + ['&&'] + setprop_args + ['&&'] + reboot_args,
-      config.device_serial_number)
-
-def _construct_boot_trace_command(config):
-  atrace_args = ['atrace', '--async_stop']
-  setprop_args = ['setprop', BOOTTRACE_PROP, '0']
-  rm_args = ['rm', BOOTTRACE_CATEGORIES]
-  return util.construct_adb_shell_command(
-        atrace_args + ['&&'] + setprop_args + ['&&'] + rm_args,
-        config.device_serial_number)
-
-
-def extract_thread_list(trace_text):
+def extract_thread_list(trace_lines):
   """Removes the thread list from the given trace data.
 
   Args:
-    trace_text: The text portion of the trace
+    trace_lines: The text portion of the trace
 
   Returns:
     a map of thread ids to thread names
   """
 
   threads = {}
-  # start at line 1 to skip the top of the ps dump:
-  text = trace_text.splitlines()
-  for line in text[1:]:
+  # Assume any line that starts with USER is the header
+  header = -1
+  for i, line in enumerate(trace_lines):
+    cols = line.split()
+    if len(cols) >= 8 and cols[0] == 'USER':
+      header = i
+      break
+  if header == -1:
+    return threads
+  for line in trace_lines[header + 1:]:
     cols = line.split(None, 8)
     if len(cols) == 9:
       tid = int(cols[1])
@@ -349,18 +330,17 @@
   return threads
 
 
-def extract_tgids(trace_text):
+def extract_tgids(trace_lines):
   """Removes the procfs dump from the given trace text
 
   Args:
-    trace_text: The text portion of the trace
+    trace_lines: The text portion of the trace
 
   Returns:
     a map of pids to their tgid.
   """
   tgid_2pid = {}
-  text = trace_text.splitlines()
-  for line in text:
+  for line in trace_lines:
     result = re.match('^/proc/([0-9]+)/task/([0-9]+)', line)
     if result:
       parent_pid, tgid = result.group(1, 2)
@@ -495,41 +475,17 @@
     out = out[:end_of_header] + out[start_of_full_trace:]
   return out
 
-def do_preprocess_adb_cmd(command, serial):
-  """Run an ADB command for preprocessing of output.
-
-  Run an ADB command and get the results. This function is used for
-  running commands relating to preprocessing of output data.
-
-  Args:
-      command: Command to run.
-      serial: Serial number of device.
-  """
-
-  args = [command]
-  dump, ret_code = util.run_adb_shell(args, serial)
-  if ret_code != 0:
-    return None
-
-  dump = ''.join(dump)
-  return dump
-
 
 class AtraceConfig(tracing_agents.TracingConfig):
   def __init__(self, atrace_categories, trace_buf_size, kfuncs,
-               app_name, fix_threads, fix_tgids, fix_circular,
-               compress_trace_data, boot, from_file, device_serial_number,
-               trace_time, target):
+               app_name, compress_trace_data, from_file,
+               device_serial_number, trace_time, target):
     tracing_agents.TracingConfig.__init__(self)
     self.atrace_categories = atrace_categories
     self.trace_buf_size = trace_buf_size
     self.kfuncs = kfuncs
     self.app_name = app_name
-    self.fix_threads = fix_threads
-    self.fix_tgids = fix_tgids
-    self.fix_circular = fix_circular
     self.compress_trace_data = compress_trace_data
-    self.boot = boot
     self.from_file = from_file
     self.device_serial_number = device_serial_number
     self.trace_time = trace_time
@@ -544,25 +500,23 @@
   options.add_option('-k', '--ktrace', dest='kfuncs', action='store',
                      help='specify a comma-separated list of kernel functions '
                      'to trace')
-  options.add_option('-a', '--app', dest='app_name', default=None,
-                     type='string', action='store',
-                     help='enable application-level tracing for '
-                     'comma-separated list of app cmdlines')
   options.add_option('--no-compress', dest='compress_trace_data',
                      default=True, action='store_false',
                      help='Tell the device not to send the trace data in '
                      'compressed form.')
-  options.add_option('--boot', dest='boot', default=False, action='store_true',
-                     help='reboot the device with tracing during boot enabled.'
-                     'The report is created by hitting Ctrl+C after the device'
-                     'has booted up.')
+  options.add_option('-a', '--app', dest='app_name', default=None,
+                     type='string', action='store',
+                     help='enable application-level tracing for '
+                     'comma-separated list of app cmdlines')
+  options.add_option('--from-file', dest='from_file',
+                     action='store', help='read the trace from a '
+                     'file (compressed) rather than running a '
+                     'live trace')
   return options
 
 def get_config(options):
   return AtraceConfig(options.atrace_categories,
                       options.trace_buf_size, options.kfuncs,
-                      options.app_name, options.fix_threads,
-                      options.fix_tgids, options.fix_circular,
-                      options.compress_trace_data, options.boot,
+                      options.app_name, options.compress_trace_data,
                       options.from_file, options.device_serial_number,
                       options.trace_time, options.target)
diff --git a/sdk/platform-tools/systrace/catapult/systrace/systrace/tracing_agents/atrace_agent_unittest.py b/sdk/platform-tools/systrace/catapult/systrace/systrace/tracing_agents/atrace_agent_unittest.py
index 8bf6585..af30c92 100755
--- a/sdk/platform-tools/systrace/catapult/systrace/systrace/tracing_agents/atrace_agent_unittest.py
+++ b/sdk/platform-tools/systrace/catapult/systrace/systrace/tracing_agents/atrace_agent_unittest.py
@@ -11,8 +11,12 @@
 
 from systrace import decorators
 from systrace import run_systrace
+from systrace import util
 from systrace.tracing_agents import atrace_agent
 
+from devil.android import device_utils
+from devil.android.sdk import intent
+
 
 DEVICE_SERIAL = 'AG8404EC0444AGC'
 ATRACE_ARGS = ['atrace', '-z', '-t', '10', '-b', '4096']
@@ -23,16 +27,6 @@
                 DEVICE_SERIAL] + CATEGORIES
 TRACE_ARGS = (ATRACE_ARGS + CATEGORIES)
 
-STOP_FIX_UPS = ['atrace', '--no-fix-threads', '--no-fix-tgids']
-
-
-SYSTRACE_BOOT_CMD = (['./run_systrace.py', '--boot', '-e', DEVICE_SERIAL] +
-                     CATEGORIES)
-TRACE_BOOT_CMD = (ADB_SHELL +
-                  ['atrace', '--async_stop', '&&', 'setprop',
-                   'persist.debug.atrace.boottrace', '0', '&&',
-                   'rm', '/data/misc/boottrace/categories'])
-
 TEST_DIR = os.path.join(os.path.dirname(__file__), os.pardir, 'test_data')
 ATRACE_DATA = os.path.join(TEST_DIR, 'atrace_data')
 ATRACE_DATA_RAW = os.path.join(TEST_DIR, 'atrace_data_raw')
@@ -41,7 +35,8 @@
 ATRACE_DATA_WITH_THREAD_LIST = os.path.join(TEST_DIR,
                                             'atrace_data_with_thread_list')
 ATRACE_THREAD_NAMES = os.path.join(TEST_DIR, 'atrace_thread_names')
-ATRACE_THREAD_LIST = os.path.join(TEST_DIR, 'atrace_ps_dump')
+ATRACE_PS_DUMPS = [os.path.join(TEST_DIR, psdump) for psdump in
+        ['atrace_ps_dump', 'atrace_ps_dump_2', 'atrace_ps_dump_3']]
 ATRACE_EXTRACTED_THREADS = os.path.join(TEST_DIR, 'atrace_extracted_threads')
 ATRACE_PROCFS_DUMP = os.path.join(TEST_DIR, 'atrace_procfs_dump')
 ATRACE_EXTRACTED_TGIDS = os.path.join(TEST_DIR, 'atrace_extracted_tgids')
@@ -51,6 +46,50 @@
 
 class AtraceAgentTest(unittest.TestCase):
 
+  # TODO(washingtonp): These end-to-end tests do not work on the Trybot server
+  # because adb cannot be found on the Trybot servers. Figure out what the
+  # issue is and update this test.
+  @decorators.Disabled
+  def test_tracing(self):
+    TRACE_BUFFER_SIZE = '16384'
+    TRACE_TIME = '5'
+
+    devices = device_utils.DeviceUtils.HealthyDevices()
+    package_info = util.get_supported_browsers()['stable']
+    device = devices[0]
+    output_file_name = util.generate_random_filename_for_test()
+
+    try:
+      # Launch the browser before tracing.
+      device.StartActivity(
+          intent.Intent(activity=package_info.activity,
+                        package=package_info.package,
+                        data='about:blank',
+                        extras={'create_new_tab': True}),
+          blocking=True, force_stop=True)
+
+      # Run atrace agent.
+      run_systrace.main_impl(['./run_systrace.py',
+                              '-b',
+                              TRACE_BUFFER_SIZE,
+                              '-t',
+                              TRACE_TIME,
+                              '-o',
+                              output_file_name,
+                              '-e',
+                              str(device),
+                              '--atrace-categories=gfx,input,view'])
+
+      # Verify results.
+      with open(output_file_name, 'r') as f:
+        full_trace = f.read()
+        self.assertTrue('CPU#'in full_trace)
+    except:
+      raise
+    finally:
+      if os.path.exists(output_file_name):
+        os.remove(output_file_name)
+
   @decorators.HostOnlyTest
   def test_construct_atrace_args(self):
     options, categories = run_systrace.parse_options(SYSTRACE_CMD)
@@ -59,28 +98,14 @@
     self.assertEqual(' '.join(TRACE_ARGS), ' '.join(tracer_args))
 
   @decorators.HostOnlyTest
-  def test_preprocess_trace_data(self):
-    with contextlib.nested(open(ATRACE_DATA_STRIPPED, 'r'),
-                           open(ATRACE_DATA_RAW, 'r')) as (f1, f2):
-      atrace_data = f1.read()
-      atrace_data_raw = f2.read()
-      options, categories = run_systrace.parse_options(STOP_FIX_UPS)
-      agent = atrace_agent.AtraceAgent()
-      agent._config = options
-      agent._config.atrace_categories = categories
-      trace_data = agent._preprocess_trace_data(atrace_data_raw)
-      self.assertEqual(atrace_data, trace_data)
-
-  @decorators.HostOnlyTest
   def test_extract_thread_list(self):
-    with contextlib.nested(open(ATRACE_EXTRACTED_THREADS, 'r'),
-                           open(ATRACE_THREAD_LIST)) as (f1, f2):
-
-      atrace_result = f1.read()
-      ps_dump = f2.read()
-
-      thread_names = atrace_agent.extract_thread_list(ps_dump)
-      self.assertEqual(atrace_result, str(thread_names))
+    with open(ATRACE_EXTRACTED_THREADS, 'r') as expected_file:
+      expected = expected_file.read().strip()
+      for dump_file_name in ATRACE_PS_DUMPS:
+        with open(dump_file_name, 'r') as dump_file:
+          ps_dump = dump_file.read()
+          thread_names = atrace_agent.extract_thread_list(ps_dump.splitlines())
+          self.assertEqual(expected, str(thread_names))
 
   @decorators.HostOnlyTest
   def test_strip_and_decompress_trace(self):
@@ -116,7 +141,7 @@
       atrace_procfs_extracted = f2.read()
 
       tgids = eval(atrace_procfs_extracted)
-      result = atrace_agent.extract_tgids(atrace_procfs_dump)
+      result = atrace_agent.extract_tgids(atrace_procfs_dump.splitlines())
 
       self.assertEqual(result, tgids)
 
@@ -134,15 +159,6 @@
       self.assertEqual(res, fixed)
 
 
-class BootAgentTest(unittest.TestCase):
-
-  @decorators.HostOnlyTest
-  def test_boot(self):
-    options, _ = run_systrace.parse_options(SYSTRACE_BOOT_CMD)
-    tracer_args = atrace_agent._construct_boot_trace_command(options)
-    self.assertEqual(' '.join(TRACE_BOOT_CMD), ' '.join(tracer_args))
-
-
 if __name__ == "__main__":
   logging.getLogger().setLevel(logging.DEBUG)
   unittest.main(verbosity=2)
diff --git a/sdk/platform-tools/systrace/catapult/systrace/systrace/tracing_agents/atrace_from_file_agent.py b/sdk/platform-tools/systrace/catapult/systrace/systrace/tracing_agents/atrace_from_file_agent.py
index 77a468b..fadd96c 100644
--- a/sdk/platform-tools/systrace/catapult/systrace/systrace/tracing_agents/atrace_from_file_agent.py
+++ b/sdk/platform-tools/systrace/catapult/systrace/systrace/tracing_agents/atrace_from_file_agent.py
@@ -25,9 +25,9 @@
 
 
 class AtraceFromFileConfig(tracing_agents.TracingConfig):
-  def __init__(self, fix_circular, from_file):
+  def __init__(self, from_file):
     tracing_agents.TracingConfig.__init__(self)
-    self.fix_circular = fix_circular
+    self.fix_circular = True
     self.from_file = from_file
 
 def add_options(parser): # pylint: disable=unused-argument
@@ -36,7 +36,7 @@
   return None
 
 def get_config(options):
-  return AtraceFromFileConfig(options.fix_circular, options.from_file)
+  return AtraceFromFileConfig(options.from_file)
 
 
 class AtraceFromFileAgent(tracing_agents.TracingAgent):
@@ -44,7 +44,6 @@
     super(AtraceFromFileAgent, self).__init__()
     self._filename = os.path.expanduser(options.from_file)
     self._trace_data = False
-    self._fix_circular_traces = options.fix_circular
 
   @py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
   def StartAgentTracing(self, config, timeout=None):
@@ -73,10 +72,10 @@
     data = re.sub(ADB_IGNORE_REGEXP, '', result[data_start:])
     return self._preprocess_data(data)
 
+  # pylint: disable=no-self-use
   def _preprocess_data(self, data):
     # TODO: add fix_threads and fix_tgids options back in here
     # once we embed the dump data in the file (b/27504068)
     data = atrace_agent.strip_and_decompress_trace(data)
-    if self._fix_circular_traces:
-      data = atrace_agent.fix_circular_traces(data)
+    data = atrace_agent.fix_circular_traces(data)
     return data
diff --git a/sdk/platform-tools/systrace/catapult/systrace/systrace/tracing_agents/battor_trace_agent.py b/sdk/platform-tools/systrace/catapult/systrace/systrace/tracing_agents/battor_trace_agent.py
index 96137a6..a48ea87 100644
--- a/sdk/platform-tools/systrace/catapult/systrace/systrace/tracing_agents/battor_trace_agent.py
+++ b/sdk/platform-tools/systrace/catapult/systrace/systrace/tracing_agents/battor_trace_agent.py
@@ -2,7 +2,6 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from os import path
 import atexit
 import logging
 import optparse
@@ -12,6 +11,7 @@
 from devil.android import battery_utils
 from devil.android import device_utils
 from devil.utils import battor_device_mapping
+from devil.utils import find_usb_devices
 from py_trace_event import trace_time
 from systrace import trace_result
 from systrace import tracing_agents
@@ -21,50 +21,43 @@
   if config.from_file is not None:
     return None
   if config.battor:
-    return BattorTraceAgent()
+    return BattOrTraceAgent()
   return None
 
 
-class BattorConfig(tracing_agents.TracingConfig):
-  def __init__(self, battor_categories, hub_types, serial_map, battor_path,
-               update_map, battor, target, from_file):
+class BattOrConfig(tracing_agents.TracingConfig):
+  def __init__(self, battor_categories, serial_map, battor_path,
+               battor, target, from_file, device_serial_number):
     tracing_agents.TracingConfig.__init__(self)
     self.battor_categories = battor_categories
-    self.hub_types = hub_types
     self.serial_map = serial_map
     self.battor_path = battor_path
-    self.update_map = update_map
     self.battor = battor
     self.target = target
     self.from_file = from_file
+    self.device_serial_number = device_serial_number
 
 
 def add_options(parser):
-  options = optparse.OptionGroup(parser, 'Battor trace options')
+  options = optparse.OptionGroup(parser, 'BattOr trace options')
   options.add_option('--battor-categories', dest='battor_categories',
                      help='Select battor categories with a comma-delimited '
                      'list, e.g. --battor-categories=cat1,cat2,cat3')
-  options.add_option('--hubs', dest='hub_types', default='plugable_7port',
-                    help='List of hub types to check for for BattOr mapping. '
-                    'Used when updating mapping file.')
   options.add_option('--serial-map', dest='serial_map',
                     default='serial_map.json',
                     help='File containing pregenerated map of phone serial '
                     'numbers to BattOr serial numbers.')
-  options.add_option('--battor_path', dest='battor_path', default=None,
+  options.add_option('--battor-path', dest='battor_path', default=None,
                     type='string', help='specify a BattOr path to use')
-  options.add_option('--update-map', dest='update_map', default=False,
-                    action='store_true',
-                    help='force update of phone-to-BattOr map')
   options.add_option('--battor', dest='battor', default=False,
                     action='store_true', help='Use the BattOr tracing agent.')
   return options
 
 def get_config(options):
-  return BattorConfig(options.battor_categories, options.hub_types,
-                      options.serial_map, options.battor_path,
-                      options.update_map, options.battor, options.target,
-                      options.from_file)
+  return BattOrConfig(
+      options.battor_categories, options.serial_map, options.battor_path,
+      options.battor, options.target, options.from_file,
+      options.device_serial_number)
 
 def _reenable_charging_if_needed(battery):
   if not battery.GetCharging():
@@ -72,16 +65,27 @@
   logging.info('Charging status checked at exit.')
 
 
-class BattorTraceAgent(tracing_agents.TracingAgent):
+class BattOrTraceAgent(tracing_agents.TracingAgent):
   # Class representing tracing agent that gets data from a BattOr.
   # BattOrs are high-frequency power monitors used for battery testing.
   def __init__(self):
-    super(BattorTraceAgent, self).__init__()
+    super(BattOrTraceAgent, self).__init__()
     self._collection_process = None
     self._recording_error = None
     self._battor_wrapper = None
     self._battery_utils = None
 
+  @staticmethod
+  def _FindBattOrPath(config):
+    device_tree = find_usb_devices.GetBusNumberToDeviceTreeMap()
+    battors = battor_device_mapping.GetBattOrList(device_tree)
+    battor_path = config.battor_path
+    if not config.battor_path and not config.serial_map:
+      assert len(battors) == 1, ('Must specify BattOr path if there is not '
+                                 'exactly one')
+      battor_path = battors[0]
+    return battor_path
+
   @py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
   def StartAgentTracing(self, config, timeout=None):
     """Starts tracing.
@@ -91,14 +95,14 @@
 
     Raises:
         RuntimeError: If trace already in progress.
+        AssertionError: If There is no BattOr path given and more
+            than one BattOr is attached.
     """
-    if config.update_map or not path.isfile(config.serial_map):
-      battor_device_mapping.GenerateSerialMapFile(config.serial_map,
-                                                  config.hub_types)
-    self._battor_wrapper = battor_wrapper.BattorWrapper(
+    battor_path = self._FindBattOrPath(config)
+    self._battor_wrapper = battor_wrapper.BattOrWrapper(
         target_platform=config.target,
         android_device=config.device_serial_number,
-        battor_path=config.battor_path,
+        battor_path=battor_path,
         battor_map_file=config.serial_map)
 
     dev_utils = device_utils.DeviceUtils(config.device_serial_number)
diff --git a/sdk/platform-tools/systrace/catapult/systrace/systrace/tracing_agents/battor_trace_agent_unittest.py b/sdk/platform-tools/systrace/catapult/systrace/systrace/tracing_agents/battor_trace_agent_unittest.py
index c77163a..6347161 100755
--- a/sdk/platform-tools/systrace/catapult/systrace/systrace/tracing_agents/battor_trace_agent_unittest.py
+++ b/sdk/platform-tools/systrace/catapult/systrace/systrace/tracing_agents/battor_trace_agent_unittest.py
@@ -13,15 +13,14 @@
 from battor import battor_wrapper
 from devil.android import battery_utils
 from devil.utils import battor_device_mapping
+from devil.utils import find_usb_devices
 
 
 mock_opts = namedtuple('mock_opts', ['target', 'device_serial_number',
-                                     'hub_types', 'battor_path',
-                                     'update_map', 'serial_map'])
-OPTIONS = mock_opts('android', 'Phn2', ['plugable_7port'],
-                    None, False, __file__)
+                                     'battor_path', 'serial_map'])
+OPTIONS = mock_opts('android', 'Phn2', None, __file__)
 CATEGORIES = None
-
+_DEFAULT_BATTOR_LIST = ['dev/ttyUSB0']
 
 def raise_error(*args, **kwargs):
   del args
@@ -31,17 +30,22 @@
 battor_device_mapping.GenerateSerialMapFile = raise_error
 
 def setup_battor_test(StartShell_error, StartTracing_error,
-                      StopTracing_error, CollectTraceData_error):
-  wrapper = MockBattorWrapper(StartShell_error, StartTracing_error,
+                      StopTracing_error, CollectTraceData_error,
+                      battor_paths=None):
+  wrapper = MockBattOrWrapper(StartShell_error, StartTracing_error,
                               StopTracing_error, CollectTraceData_error)
   def wrapper_maker(*args, **kwargs):
     del args
     del kwargs
     return wrapper
-  battor_wrapper.BattorWrapper = wrapper_maker
+  battor_wrapper.BattOrWrapper = wrapper_maker
+  find_usb_devices.GetBusNumberToDeviceTreeMap = lambda: None
+  if battor_paths is None:
+    battor_paths = _DEFAULT_BATTOR_LIST
+  battor_device_mapping.GetBattOrList = lambda x: battor_paths
 
 
-class MockBattorWrapper(object):
+class MockBattOrWrapper(object):
   def __init__(self, StartShell_error=False, StartTracing_error=False,
                StopTracing_error=False, CollectTraceData_error=False):
     self._StartShell_error = StartShell_error
@@ -98,13 +102,13 @@
 battery_utils.BatteryUtils = MockBatteryUtils
 
 
-class BattorAgentTest(unittest.TestCase):
+class BattOrAgentTest(unittest.TestCase):
 
   @decorators.HostOnlyTest
   def test_trace_double_start(self):
     setup_battor_test(StartShell_error=False, StartTracing_error=False,
                       StopTracing_error=False, CollectTraceData_error=False)
-    agent = battor_trace_agent.BattorTraceAgent()
+    agent = battor_trace_agent.BattOrTraceAgent()
     agent.StartAgentTracing(OPTIONS, CATEGORIES)
     self.assertRaises(AssertionError,
                       lambda: agent.StartAgentTracing(OPTIONS, CATEGORIES))
@@ -113,7 +117,7 @@
   def test_trace_error_start_shell(self):
     setup_battor_test(StartShell_error=True, StartTracing_error=False,
                       StopTracing_error=False, CollectTraceData_error=False)
-    agent = battor_trace_agent.BattorTraceAgent()
+    agent = battor_trace_agent.BattOrTraceAgent()
     self.assertRaises(RuntimeError,
                       lambda: agent.StartAgentTracing(OPTIONS, CATEGORIES))
 
@@ -121,7 +125,7 @@
   def test_trace_error_start_tracing(self):
     setup_battor_test(StartShell_error=False, StartTracing_error=True,
                       StopTracing_error=False, CollectTraceData_error=False)
-    agent = battor_trace_agent.BattorTraceAgent()
+    agent = battor_trace_agent.BattOrTraceAgent()
     self.assertRaises(RuntimeError,
                       lambda: agent.StartAgentTracing(OPTIONS, CATEGORIES))
 
@@ -129,7 +133,7 @@
   def test_trace_error_stop_tracing(self):
     setup_battor_test(StartShell_error=False, StartTracing_error=False,
                       StopTracing_error=True, CollectTraceData_error=False)
-    agent = battor_trace_agent.BattorTraceAgent()
+    agent = battor_trace_agent.BattOrTraceAgent()
     agent.StartAgentTracing(OPTIONS, CATEGORIES)
     self.assertRaises(RuntimeError, agent.StopAgentTracing)
 
@@ -137,7 +141,7 @@
   def test_trace_error_get_results(self):
     setup_battor_test(StartShell_error=False, StartTracing_error=False,
                       StopTracing_error=False, CollectTraceData_error=True)
-    agent = battor_trace_agent.BattorTraceAgent()
+    agent = battor_trace_agent.BattOrTraceAgent()
     agent.StartAgentTracing(OPTIONS, CATEGORIES)
     agent.StopAgentTracing()
     self.assertRaises(RuntimeError, agent.GetResults)
@@ -146,12 +150,32 @@
   def test_trace_complete(self):
     setup_battor_test(StartShell_error=False, StartTracing_error=False,
                       StopTracing_error=False, CollectTraceData_error=False)
-    agent = battor_trace_agent.BattorTraceAgent()
+    agent = battor_trace_agent.BattOrTraceAgent()
     agent.StartAgentTracing(OPTIONS, CATEGORIES)
     agent.StopAgentTracing()
     x = agent.GetResults()
     self.assertEqual(x.raw_data, 'traceout1\ntraceout2')
 
+  @decorators.HostOnlyTest
+  def test_trace_error_no_battor(self):
+    setup_battor_test(StartShell_error=False, StartTracing_error=False,
+                      StopTracing_error=False, CollectTraceData_error=False,
+                      battor_paths=[])
+    agent = battor_trace_agent.BattOrTraceAgent()
+    options = mock_opts('android', 'Phn2', None, None)
+    with self.assertRaises(AssertionError):
+      agent.StartAgentTracing(options, CATEGORIES)
+
+  @decorators.HostOnlyTest
+  def test_trace_error_multiple_battors_no_battor_path(self):
+    setup_battor_test(StartShell_error=False, StartTracing_error=False,
+                      StopTracing_error=False, CollectTraceData_error=False,
+                      battor_paths=['a', 'b'])
+    agent = battor_trace_agent.BattOrTraceAgent()
+    options = mock_opts('android', 'Phn2', None, None)
+    with self.assertRaises(AssertionError):
+      agent.StartAgentTracing(options, CATEGORIES)
+
 
 if __name__ == "__main__":
   logging.getLogger().setLevel(logging.DEBUG)
diff --git a/sdk/platform-tools/systrace/catapult/systrace/systrace/tracing_agents/ftrace_agent.py b/sdk/platform-tools/systrace/catapult/systrace/systrace/tracing_agents/ftrace_agent.py
index 9b63c6a..cb1e7e1 100644
--- a/sdk/platform-tools/systrace/catapult/systrace/systrace/tracing_agents/ftrace_agent.py
+++ b/sdk/platform-tools/systrace/catapult/systrace/systrace/tracing_agents/ftrace_agent.py
@@ -100,15 +100,11 @@
 
 
 class FtraceConfig(tracing_agents.TracingConfig):
-  def __init__(self, ftrace_categories, target, trace_buf_size, fix_threads,
-               fix_tgids, fix_circular):
+  def __init__(self, ftrace_categories, target, trace_buf_size):
     tracing_agents.TracingConfig.__init__(self)
     self.ftrace_categories = ftrace_categories
     self.target = target
     self.trace_buf_size = trace_buf_size
-    self.fix_threads = fix_threads
-    self.fix_tgids = fix_tgids
-    self.fix_circular = fix_circular
 
 
 def add_options(parser):
@@ -121,8 +117,7 @@
 
 def get_config(options):
   return FtraceConfig(options.ftrace_categories, options.target,
-                      options.trace_buf_size, options.fix_threads,
-                      options.fix_tgids, options.fix_circular)
+                      options.trace_buf_size)
 
 
 class FtraceAgent(tracing_agents.TracingAgent):
@@ -195,12 +190,6 @@
     self._fio.writeFile(FT_TRACE_ON, '0')
     for category in self._categories:
       self._category_disable(category)
-    if self._config.fix_threads:
-      print "WARN: thread name fixing is not yet supported."
-    if self._config.fix_tgids:
-      print "WARN: tgid fixing is not yet supported."
-    if self._config.fix_circular:
-      print "WARN: circular buffer fixups are not yet supported."
     return True
 
   @py_utils.Timeout(tracing_agents.GET_RESULTS_TIMEOUT)
diff --git a/sdk/platform-tools/systrace/catapult/systrace/systrace/tracing_controller.py b/sdk/platform-tools/systrace/catapult/systrace/systrace/tracing_controller.py
index 6eac01a..e6f4d6d 100644
--- a/sdk/platform-tools/systrace/catapult/systrace/systrace/tracing_controller.py
+++ b/sdk/platform-tools/systrace/catapult/systrace/systrace/tracing_controller.py
@@ -269,13 +269,12 @@
 
 
 class TracingControllerConfig(tracing_agents.TracingConfig):
-  def __init__(self, output_file, trace_time, list_categories, write_json,
+  def __init__(self, output_file, trace_time, write_json,
                link_assets, asset_dir, timeout, collection_timeout,
                device_serial_number, target):
     tracing_agents.TracingConfig.__init__(self)
     self.output_file = output_file
     self.trace_time = trace_time
-    self.list_categories = list_categories
     self.write_json = write_json
     self.link_assets = link_assets
     self.asset_dir = asset_dir
@@ -287,12 +286,12 @@
 
 def GetControllerConfig(options):
   return TracingControllerConfig(options.output_file, options.trace_time,
-                                 options.list_categories, options.write_json,
+                                 options.write_json,
                                  options.link_assets, options.asset_dir,
                                  options.timeout, options.collection_timeout,
                                  options.device_serial_number, options.target)
 
 def GetChromeStartupControllerConfig(options):
-  return TracingControllerConfig(None, options.trace_time, None,
+  return TracingControllerConfig(None, options.trace_time,
                                  options.write_json, None, None, None, None,
                                  None, None)
diff --git a/sdk/platform-tools/systrace/catapult/systrace/systrace/util.py b/sdk/platform-tools/systrace/catapult/systrace/systrace/util.py
index 442ae4e..61dc475 100644
--- a/sdk/platform-tools/systrace/catapult/systrace/systrace/util.py
+++ b/sdk/platform-tools/systrace/catapult/systrace/systrace/util.py
@@ -9,6 +9,7 @@
 import subprocess
 import sys
 
+from devil.android.constants import chrome
 
 class OptionParserIgnoreErrors(optparse.OptionParser):
   """Wrapper for OptionParser that ignores errors and produces no output."""
@@ -150,3 +151,61 @@
               string.digits) for _ in range(10))
   return os.path.abspath(name)
 
+
+def get_supported_browsers():
+  """Returns the package names of all supported browsers."""
+  # Add aliases for backwards compatibility.
+  supported_browsers = {
+    'stable': chrome.PACKAGE_INFO['chrome_stable'],
+    'beta': chrome.PACKAGE_INFO['chrome_beta'],
+    'dev': chrome.PACKAGE_INFO['chrome_dev'],
+    'build': chrome.PACKAGE_INFO['chrome'],
+  }
+  supported_browsers.update(chrome.PACKAGE_INFO)
+  return supported_browsers
+
+
+def get_default_serial():
+  if 'ANDROID_SERIAL' in os.environ:
+    return os.environ['ANDROID_SERIAL']
+  return None
+
+
+def get_main_options(parser):
+  parser.add_option('-o', dest='output_file', help='write trace output to FILE',
+                    default=None, metavar='FILE')
+  parser.add_option('-t', '--time', dest='trace_time', type='int',
+                    help='trace for N seconds', metavar='N')
+  parser.add_option('-j', '--json', dest='write_json',
+                    default=False, action='store_true',
+                    help='write a JSON file')
+  parser.add_option('--link-assets', dest='link_assets', default=False,
+                    action='store_true',
+                    help='(deprecated)')
+  parser.add_option('--from-file', dest='from_file', action='store',
+                    help='read the trace from a file (compressed) rather than'
+                    'running a live trace')
+  parser.add_option('--asset-dir', dest='asset_dir', default='trace-viewer',
+                    type='string', help='(deprecated)')
+  parser.add_option('-e', '--serial', dest='device_serial_number',
+                    default=get_default_serial(),
+                    type='string', help='adb device serial number')
+  parser.add_option('--target', dest='target', default='android', type='string',
+                    help='choose tracing target (android or linux)')
+  parser.add_option('--timeout', dest='timeout', type='int',
+                    help='timeout for start and stop tracing (seconds)')
+  parser.add_option('--collection-timeout', dest='collection_timeout',
+                    type='int', help='timeout for data collection (seconds)')
+  parser.add_option('-a', '--app', dest='app_name', default=None,
+                    type='string', action='store',
+                    help='enable application-level tracing for '
+                    'comma-separated list of app cmdlines')
+  parser.add_option('-t', '--time', dest='trace_time', type='int',
+                    help='trace for N seconds', metavar='N')
+  parser.add_option('--target', dest='target', default='android',
+                    type='string', help='choose tracing target (android or '
+                    ' linux)')
+  parser.add_option('-b', '--buf-size', dest='trace_buf_size',
+                    type='int', help='use a trace buffer size '
+                    ' of N KB', metavar='N')
+  return parser
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/BUILD.gn b/sdk/platform-tools/systrace/catapult/telemetry/BUILD.gn
new file mode 100644
index 0000000..f786e3c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/BUILD.gn
@@ -0,0 +1,23 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+group("telemetry_test_support") {
+  # Generic telemetry deps. For now, just include the whole catapult directory.
+  # TODO(nednguyen, aiolos): only include what telemetry needs.
+  # https://github.com/catapult-project/catapult/issues/1953
+  data = [
+    "../",
+  ]
+}
+
+executable("bitmaptools") {
+  sources = [
+    "telemetry/internal/image_processing/bitmaptools.cc",
+  ]
+
+  deps = [
+    "//build/config/sanitizers:deps",
+    "//build/win:default_exe_manifest",
+  ]
+}
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/PRESUBMIT.py b/sdk/platform-tools/systrace/catapult/telemetry/PRESUBMIT.py
new file mode 100644
index 0000000..754847d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/PRESUBMIT.py
@@ -0,0 +1,137 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+def _CommonChecks(input_api, output_api):
+  results = []
+
+  results += input_api.RunTests(input_api.canned_checks.GetPylint(
+      input_api, output_api, extra_paths_list=_GetPathsToPrepend(input_api),
+      pylintrc='pylintrc'))
+  results += _CheckNoMoreUsageOfDeprecatedCode(
+      input_api, output_api, deprecated_code='GetChromiumSrcDir()',
+      crbug_number=511332)
+  return results
+
+
+def _RunArgs(args, input_api):
+  p = input_api.subprocess.Popen(args, stdout=input_api.subprocess.PIPE,
+                                 stderr=input_api.subprocess.STDOUT)
+  out, _ = p.communicate()
+  return (out, p.returncode)
+
+
+def _ValidateDependenciesFile(input_api, output_api, dependencies_path):
+  """ Check that binary_dependencies.json has valid format and content.
+
+  This check should only be done in CheckChangeOnUpload() only since it invokes
+  network I/O.
+  """
+  results = []
+  telemetry_dir = input_api.PresubmitLocalPath()
+  for f in input_api.AffectedFiles():
+    if not f.AbsoluteLocalPath() == dependencies_path:
+      continue
+    out, return_code = _RunArgs([
+        input_api.python_executable,
+        input_api.os_path.join(telemetry_dir, 'json_format'),
+        dependencies_path], input_api)
+    if return_code:
+      results.append(output_api.PresubmitError(
+           'Validating %s failed:' % dependencies_path, long_text=out))
+      break
+    out, return_code = _RunArgs([
+        input_api.python_executable,
+        input_api.os_path.join(telemetry_dir, 'validate_binary_dependencies'),
+        dependencies_path], input_api)
+    if return_code:
+      results.append(output_api.PresubmitError(
+          'Validating %s failed:' % dependencies_path, long_text=out))
+      break
+  return results
+
+
+def _CheckNoMoreUsageOfDeprecatedCode(
+    input_api, output_api, deprecated_code, crbug_number):
+  results = []
+  # These checks are not perfcet but should be good enough for most of our
+  # usecases.
+  def _IsAddedLine(line):
+    return line.startswith('+') and not line.startswith('+++ ')
+  def _IsRemovedLine(line):
+    return line.startswith('-') and not line.startswith('--- ')
+
+  presubmit_dir = input_api.os_path.join(
+      input_api.PresubmitLocalPath(), 'PRESUBMIT.py')
+
+  added_calls = 0
+  removed_calls = 0
+  for affected_file in input_api.AffectedFiles():
+    # Do not do the check on PRESUBMIT.py itself.
+    if affected_file.AbsoluteLocalPath() == presubmit_dir:
+      continue
+    for line in affected_file.GenerateScmDiff().splitlines():
+      if _IsAddedLine(line) and deprecated_code in line:
+        added_calls += 1
+      elif _IsRemovedLine(line) and deprecated_code in line:
+        removed_calls += 1
+
+  if added_calls > removed_calls:
+    results.append(output_api.PresubmitError(
+        'Your patch adds more instances of %s. Please see crbug.com/%i for'
+        'how to proceed.' % (deprecated_code, crbug_number)))
+  return results
+
+
+def _GetPathsToPrepend(input_api):
+  telemetry_dir = input_api.PresubmitLocalPath()
+  catapult_dir = input_api.os_path.join(telemetry_dir, '..')
+  return [
+      telemetry_dir,
+
+      input_api.os_path.join(telemetry_dir, 'third_party', 'altgraph'),
+      input_api.os_path.join(telemetry_dir, 'third_party', 'modulegraph'),
+      input_api.os_path.join(telemetry_dir, 'third_party', 'pexpect'),
+      input_api.os_path.join(telemetry_dir, 'third_party', 'png'),
+      input_api.os_path.join(telemetry_dir, 'third_party', 'web-page-replay'),
+      input_api.os_path.join(telemetry_dir, 'third_party', 'websocket-client'),
+
+      input_api.os_path.join(catapult_dir, 'common', 'py_utils'),
+      input_api.os_path.join(catapult_dir, 'dependency_manager'),
+      input_api.os_path.join(catapult_dir, 'devil'),
+      input_api.os_path.join(catapult_dir, 'systrace'),
+      input_api.os_path.join(catapult_dir, 'tracing'),
+      input_api.os_path.join(catapult_dir, 'common', 'battor'),
+      input_api.os_path.join(catapult_dir, 'common', 'py_trace_event'),
+
+      input_api.os_path.join(catapult_dir, 'third_party', 'mock'),
+      input_api.os_path.join(catapult_dir, 'third_party', 'pyfakefs'),
+      input_api.os_path.join(catapult_dir, 'third_party', 'pyserial'),
+      input_api.os_path.join(catapult_dir, 'third_party', 'typ'),
+  ]
+
+
+def _ValidateAllDependenciesFiles(input_api, output_api):
+  results = []
+  telemetry_dir = input_api.PresubmitLocalPath()
+  binary_dependencies = input_api.os_path.join(
+      telemetry_dir, 'telemetry', 'internal', 'binary_dependencies.json')
+  telemetry_unittest_dependencies = input_api.os_path.join(
+      telemetry_dir, 'telemetry', 'telemetry_unittest_deps.json')
+  for path in [binary_dependencies, telemetry_unittest_dependencies]:
+    results += _ValidateDependenciesFile(input_api, output_api, path)
+  return results
+
+
+def CheckChangeOnUpload(input_api, output_api):
+  results = []
+  results += _CommonChecks(input_api, output_api)
+  results += _ValidateAllDependenciesFiles(input_api, output_api)
+  return results
+
+
+def CheckChangeOnCommit(input_api, output_api):
+  results = []
+  results += _CommonChecks(input_api, output_api)
+  return results
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/README.md b/sdk/platform-tools/systrace/catapult/telemetry/README.md
new file mode 100644
index 0000000..cb61fba
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/README.md
@@ -0,0 +1,155 @@
+<!-- Copyright 2015 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file.
+-->
+
+# Telemetry
+
+Telemetry is the performance testing framework used by Chrome.  It allows you
+to perform arbitrary actions on a set of web pages (or any android application!)
+and report metrics about it.  The framework abstracts:
+
+*   Launching a browser with arbitrary flags on any platform.
+*   Opening a tab and navigating to the page under test.
+*   Launching an Android application with intents through ADB.
+*   Fetching data via the Inspector timeline and traces.
+*   Using [Web Page Replay](https://github.com/chromium/web-page-replay) to
+    cache real-world websites so they don’t change when used in benchmarks.
+
+## Design Principles
+
+*   Write one performance test that runs on major platforms - Windows, Mac,
+    Linux, Chrome OS, and Android for both Chrome and ContentShell.
+*   Run on browser binaries, without a full Chromium checkout, and without
+    having to build the browser yourself.
+*   Use Web Page Replay to get repeatable test results.
+*   Clean architecture for writing benchmarks that keeps measurements and use
+    cases separate.
+
+**Telemetry is designed for measuring performance rather than checking
+  correctness. If you want to check for correctness,
+  [browser tests](http://www.chromium.org/developers/testing/browser-tests) are
+  your friend.**
+
+**If you are a Chromium developer looking to add a new Telemetry benchmark to
+[`src/tools/perf/`](https://code.google.com/p/chromium/codesearch#chromium/src/tools/perf/),
+please make sure to read our
+[Benchmark Policy](https://docs.google.com/document/d/1bBKyYCW3VlUUPDpQE4xvrMFdA6tovQMZoqO9KCcmqqQ/preview)
+first.**
+
+## Code Concepts
+
+Telemetry provides two major functionality groups: those that provide test
+automation, and those that provide the capability to collect data.
+
+### Test Automation
+
+The test automation facilities of Telemetry provide Python wrappers for a number
+of different system concepts.
+
+*   _Platforms_ use a variety of libraries & tools to abstract away the OS
+    specific logic.
+*   _Browser_ wraps Chrome's
+    [DevTools Remote Debugging Protocol](https://developer.chrome.com/devtools/docs/remote-debugging)
+    to perform actions and extract information from the browser.
+*   _Android App_ is a Python wrapper around
+    [`adb shell`](http://developer.android.com/tools/help/adb.html).
+
+The Telemetry framework lives in
+[`src/third_party/catapult/telemetry/`](https://cs.chromium.org/chromium/src/third_party/catapult/telemetry/)
+and performance benchmarks that use Telemetry live in
+[`src/tools/perf/`](https://code.google.com/p/chromium/codesearch#chromium/src/tools/perf/).
+
+### Data Collection
+
+Telemetry offers a framework for collecting metrics that quantify the
+performance of automated actions in terms of benchmarks, measurements, and story
+sets.
+
+*   A
+    [_benchmark_](https://cs.chromium.org/chromium/src/third_party/catapult/telemetry/telemetry/benchmark.py)
+    combines a _measurement_ together with a _story set_, and optionally a set
+    of browser options.
+    *   We strongly discourage benchmark authors from using command-line flags
+        to specify the behavior of benchmarks, since benchmarks should be
+        cross-platform.
+    *   Benchmarks are discovered and run by the
+        [benchmark runner](https://cs.chromium.org/chromium/src/third_party/catapult/telemetry/telemetry/benchmark_runner.py),
+        which is wrapped by scripts like
+        [`run_benchmark`](https://code.google.com/p/chromium/codesearch#chromium/src/tools/perf/run_benchmark)
+        in `tools/perf`.
+*   A _measurement_ (called
+    [`StoryTest`](https://cs.chromium.org/chromium/src/third_party/catapult/telemetry/telemetry/web_perf/story_test.py)
+    in the code) is responsible for setting up and tearing down the testing
+    platform, and for collecting _metrics_ that quantify the application
+    scenario under test.
+    *   Measurements need to work with all story sets, to provide consistency
+        and prevent benchmark rot.
+    *   You probably don't need to override `StoryTest` (see "Timeline Based
+        Measurement" below). If you think you do, please talk to us.
+*   A
+    [_story set_](https://cs.chromium.org/chromium/src/third_party/catapult/telemetry/telemetry/story/story_set.py)
+    is a set of _stories_ together with a
+    [_shared state_](https://cs.chromium.org/chromium/src/third_party/catapult/telemetry/telemetry/story/shared_state.py)
+    that describes application-level configuration options.
+*   A
+    [_story_](https://cs.chromium.org/chromium/src/third_party/catapult/telemetry/telemetry/story/story.py)
+    is an application scenario and a set of actions to run in that scenario. In
+    the typical Chromium use case, this will be a web page together with actions
+    like scrolling, clicking, or executing JavaScript.
+*   A _metric_ describes how to collect data about the story run and compute
+    results.
+    *   New metrics should generally be
+        [timeline-based](https://cs.chromium.org/chromium/src/third_party/catapult/telemetry/telemetry/web_perf/metrics/timeline_based_metric.py).
+    *   Metrics can specify many different types of results, including numbers,
+        histograms, traces, and failures.
+*   _Timeline Based Measurement_ is a built-in `StoryTest` that runs all
+    available timeline-based metrics, and benchmarks that use it can filter
+    relevant results.
+
+## Next Steps
+
+*   [Run Telemetry benchmarks locally](/telemetry/docs/run_benchmarks_locally.md)
+*   [Record a story set](https://sites.google.com/a/chromium.org/dev/developers/telemetry/record_a_page_set)
+    with Web Page Replay
+*   [Add a measurement](https://sites.google.com/a/chromium.org/dev/developers/telemetry/add_a_measurement)
+*   [Feature guidelines](https://sites.google.com/a/chromium.org/dev/developers/telemetry/telemetry-feature-guidelines)
+*   [Profiling with Telemetry](https://sites.google.com/a/chromium.org/dev/developers/telemetry/profiling)
+*   [Profile generation](https://sites.google.com/a/chromium.org/dev/developers/telemetry/telemetry-profile-generation)
+*   [Telemetry unittests](https://sites.google.com/a/chromium.org/dev/developers/telemetry/telemetry-unittests)
+
+## Contact Us or Follow Along
+
+If you have questions, please email telemetry@chromium.org.
+
+You can keep up with Telemetry related discussions by joining the
+[telemetry group](https://groups.google.com/a/chromium.org/forum/#!forum/telemetry).
+
+[For Googlers](http://go/telemetry)
+
+## Frequently Asked Questions
+
+### I get an error when I try to use recorded story sets.
+
+The recordings are not included in the Chromium source tree. If you are a Google
+partner, run `gsutil config` to authenticate, then try running the test again.
+If you don't have `gsutil` installed on your machine, you can find it in
+`build/third_party/gsutil/gsutil`.
+
+If you are not a Google partner, you can run on live sites with
+--use-live-sites` or
+[record your own](http://dev.chromium.org/developers/telemetry/record_a_page_set)
+story set archive.
+
+### I get mysterious errors about device\_forwarder failing.
+
+Your forwarder binary may be outdated. If you have built the forwarder in
+src/out that one will be used. if there isn't anything there Telemetry will
+default to downloading a pre-built binary. Try re-building the forwarder, or
+alternatively wiping the contents of `src/out/` and running `run_benchmark`,
+which should download the latest binary.
+
+### I'm having problems with keychain prompts on Mac.
+
+Make sure that your keychain is
+[correctly configured](https://sites.google.com/a/chromium.org/dev/developers/telemetry/telemetry-mac-keychain-setup).
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/bin/README.chromium b/sdk/platform-tools/systrace/catapult/telemetry/bin/README.chromium
new file mode 100644
index 0000000..b2cd84a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/bin/README.chromium
@@ -0,0 +1,54 @@
+This directory contains prebuilt binaries used by Telemetry which allow it to
+be run without requiring any compilation.
+
+For usage instructions, see:
+http://www.chromium.org/developers/telemetry/upload_to_cloud_storage
+
+avconv:
+   version 0.8.9-4:0.8.9-0ubuntu0.12.04.1
+
+IEDriverServer binary:
+  Both 32-bit and 64-bit are of version 2.35.2.
+
+ipfw and ipfw_mod.ko:
+  Version 20120812
+
+perfhost_trusty:
+  Built from branch modified by vmiura on github. The git branch used is
+  "perf_tracing_changes" but in the directions below I have included the actual
+  hash of the checkout.
+
+  Make sure you have the proper libraries installed for symbol demangling:
+    shell> sudo apt-get install binutils-dev
+    shell> sudo apt-get install libiberty-dev
+
+  Directions for building perf:
+    shell> git clone https://github.com/vmiura/linux.git
+    shell> cd linux
+    shell> git checkout e1fe871e4a33712ad4964a70904d5d59188e3cc2
+    shell> cd tools/perf
+    shell> make
+    shell> ./perf test
+    Tests should mostly pass, except a few:
+     1: vmlinux symtab matches kallsyms                        : FAILED!
+     2: detect open syscall event                              : FAILED!
+     3: detect open syscall event on all cpus                  : FAILED!
+     4: read samples using the mmap interface                  : FAILED!
+     5: parse events tests                                     : FAILED!
+     [snip]
+     11: Check parsing of sched tracepoints fields              : FAILED!
+     12: Generate and check syscalls:sys_enter_open event fields: FAILED!
+     21: Test object code reading          :[kernel.kallsyms] ... FAILED!
+    shell> mv perf perfhost_trusty
+
+android/armeabi-v7a/perf:
+  Follow http://source.android.com/source/building.html
+  . build/envsetup.sh
+  lunch aosp_arm-user
+
+2013-09-26 - bulach - perf / perfhost / tcpdump:
+  git revert -n 93501d3 # issue with __strncpy_chk2
+  make -j32 perf perfhost tcpdump
+
+android/arm64-v8a/perf:
+  Same as above, with aarch64 architecture, from branch android-5.0.0_r2
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/bin/run_browser_tests b/sdk/platform-tools/systrace/catapult/telemetry/bin/run_browser_tests
new file mode 100755
index 0000000..8d60895
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/bin/run_browser_tests
@@ -0,0 +1,23 @@
+#!/usr/bin/env python
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import sys
+
+TELEMETRY_DIR = os.path.join(os.path.abspath(os.path.dirname(__file__)), '..')
+sys.path.append(TELEMETRY_DIR)
+from telemetry import project_config
+from telemetry.testing import browser_test_runner
+
+
+def main():
+  config = project_config.ProjectConfig(
+      top_level_dir=os.path.join(TELEMETRY_DIR, 'examples'),
+      benchmark_dirs=[os.path.join(TELEMETRY_DIR, 'examples', 'browser_tests')])
+  return browser_test_runner.Run(config, sys.argv[1:])
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/bin/run_tests b/sdk/platform-tools/systrace/catapult/telemetry/bin/run_tests
new file mode 100755
index 0000000..bebddaf
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/bin/run_tests
@@ -0,0 +1,24 @@
+#!/usr/bin/env python
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import sys
+
+TELEMETRY_DIR = os.path.join(os.path.abspath(os.path.dirname(__file__)), '..')
+sys.path.append(TELEMETRY_DIR)
+from telemetry import project_config
+from telemetry.testing import unittest_runner
+
+
+def main():
+  config = project_config.ProjectConfig(
+      top_level_dir=TELEMETRY_DIR,
+      client_configs=[
+          os.path.join(TELEMETRY_DIR,
+                       'telemetry', 'telemetry_unittest_deps.json')])
+  return unittest_runner.Run(config, disable_cloud_storage_io_during_test=True)
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/build/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/build/__init__.py
new file mode 100644
index 0000000..9228df8
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/build/__init__.py
@@ -0,0 +1,3 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/build/generate_telemetry_harness.sh b/sdk/platform-tools/systrace/catapult/telemetry/build/generate_telemetry_harness.sh
new file mode 100755
index 0000000..b1e7f05
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/build/generate_telemetry_harness.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# This is a script meant to be run by a bot to periodically release new versions
+# of the telemetry harness. It needs to be run from one level above src/ (such
+# as build/).
+
+src/tools/perf/find_dependencies \
+  src/tools/perf/run_benchmark \
+  src/tools/perf/record_wpr \
+  src/content/test/gpu/run_gpu_test.py \
+  --exclude=*/third_party/catapult/test_data/* \
+  -z $1
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/build/linux_setup_msr.py b/sdk/platform-tools/systrace/catapult/telemetry/build/linux_setup_msr.py
new file mode 100755
index 0000000..a61828b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/build/linux_setup_msr.py
@@ -0,0 +1,146 @@
+#!/usr/bin/env python
+#
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+#
+# This is a script developers can use to set-up their workstation to let
+# Telemetry read the CPU's Model Specific Registers in order to get power
+# measurements. It can check if reading from MSRs is possible as any user, but
+# must run as root to make changes. Not all changes are sticky, so one has to
+# re-run this script after each reboot.
+#
+# This script is currently Debian/Ubuntu specific.
+
+import os
+import subprocess
+import sys
+
+MSR_DEV_FILE_PATH = '/dev/cpu/0/msr'
+RDMSR_PATH = '/usr/sbin/rdmsr'
+
+def _Usage(prog_name):
+  """Print a help message."""
+  print 'Run "%s" as a regular user to check if reading from the MSR ' \
+      'is possible.' % prog_name
+  print 'Run "%s enable" as root to automatically set up reading from ' \
+      'the MSR.' % prog_name
+
+
+def _CheckMsrKernelModule():
+  """Return whether the 'msr' kernel module is loaded."""
+  proc = subprocess.Popen('/sbin/lsmod', stdout=subprocess.PIPE)
+  stdout = proc.communicate()[0]
+  ret = proc.wait()
+  if ret != 0:
+    raise OSError('lsmod failed')
+
+  if not any([line.startswith('msr ') for line in stdout.splitlines()]):
+    print 'Error: MSR module not loaded.'
+    return False
+
+  return True
+
+
+def _CheckMsrDevNodes():
+  """Check whether the MSR /dev files have the right permissions."""
+  if not os.path.exists(MSR_DEV_FILE_PATH):
+    print 'Error: %s does not exist.' % MSR_DEV_FILE_PATH
+    return False
+
+  if not os.access(MSR_DEV_FILE_PATH, os.R_OK):
+    print 'Error: Cannot read from %s' % MSR_DEV_FILE_PATH
+    return False
+
+  return True
+
+
+def _CheckRdmsr():
+  """Check and make sure /usr/sbin/rdmsr is set up correctly."""
+  if not os.access(RDMSR_PATH, os.X_OK):
+    print 'Error: %s missing or not executable.' % RDMSR_PATH
+    return False
+
+  proc = subprocess.Popen(['/sbin/getcap', RDMSR_PATH], stdout=subprocess.PIPE)
+  stdout = proc.communicate()[0]
+  ret = proc.wait()
+  if ret != 0:
+    raise OSError('getcap failed')
+
+  if not 'cap_sys_rawio+ep' in stdout:
+    print 'Error: /usr/sbin/rdmsr needs RAWIO capability.'
+    return False
+
+  return True
+
+
+def _RunAllChecks():
+  """Check to make sure it is possible to read from the MSRs."""
+  if os.geteuid() == 0:
+    print 'WARNING: Running as root, msr permission check likely inaccurate.'
+
+  has_dev_node = _CheckMsrDevNodes() if _CheckMsrKernelModule() else False
+  has_rdmsr = _CheckRdmsr()
+  return has_dev_node and has_rdmsr
+
+
+def _EnableMsr(prog_name):
+  """Do all the setup needed to pass _RunAllChecks().
+
+  Needs to run as root."""
+  if os.geteuid() != 0:
+    print 'Error: Must run "%s enable" as root.' % prog_name
+    return False
+
+  print 'Loading msr kernel module.'
+  ret = subprocess.call(['/sbin/modprobe', 'msr'])
+  if ret != 0:
+    print 'Error: Cannot load msr module.'
+    return False
+
+  print 'Running chmod on %s.' % MSR_DEV_FILE_PATH
+  ret = subprocess.call(['/bin/chmod', 'a+r', MSR_DEV_FILE_PATH])
+  if ret != 0:
+    print 'Error: Cannot chmod %s.' % MSR_DEV_FILE_PATH
+    return False
+
+  if not os.access(RDMSR_PATH, os.F_OK):
+    print 'Need to install the msr-tools package.'
+    ret = subprocess.call(['/usr/bin/apt-get', 'install', '-y', 'msr-tools'])
+    if ret != 0:
+      print 'Error: Did not successfully install msr-tools.'
+      return False
+
+  print 'Running setcap on %s.' % RDMSR_PATH
+  ret = subprocess.call(['/sbin/setcap', 'cap_sys_rawio+ep', RDMSR_PATH])
+  if ret != 0:
+    print 'Error: Cannot give /usr/sbin/rdmsr RAWIO capability.'
+    return False
+
+  return True
+
+
+def main(prog_name, argv):
+  if len(argv) == 0:
+    if _RunAllChecks():
+      print 'Check succeeded'
+      return 0
+
+    print 'Check failed, try running "%s enable" as root to fix.' % prog_name
+    return 1
+
+  if len(argv) == 1:
+    if argv[0] == 'enable':
+      return 0 if _EnableMsr(prog_name) else 1
+
+    print 'Error: Unknown sub-command %s' % argv[0]
+    _Usage(prog_name)
+    return 1
+
+  print 'Error: Bad number of arguments'
+  _Usage(prog_name)
+  return 1
+
+
+if '__main__' == __name__:
+  sys.exit(main(os.path.basename(sys.argv[0]), sys.argv[1:]))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/build/update_docs.py b/sdk/platform-tools/systrace/catapult/telemetry/build/update_docs.py
new file mode 100644
index 0000000..7f43b58
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/build/update_docs.py
@@ -0,0 +1,148 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import logging
+import optparse
+import os
+import pkgutil
+import pydoc
+import re
+import sys
+
+import telemetry
+from telemetry.core import util
+
+telemetry_dir = util.GetTelemetryDir()
+docs_dir = os.path.join(telemetry_dir, 'docs', 'pydoc')
+
+def RemoveAllDocs():
+  for dirname, _, filenames in os.walk(docs_dir):
+    for filename in filenames:
+      os.remove(os.path.join(dirname, filename))
+
+def GenerateHTMLForModule(module):
+  html = pydoc.html.page(pydoc.describe(module),
+                         pydoc.html.document(module, module.__name__))
+
+  # pydoc writes out html with links in a variety of funky ways. We need
+  # to fix them up.
+  assert not telemetry_dir.endswith(os.sep)
+  links = re.findall('(<a href="(.+?)">(.+?)</a>)', html)
+  for link_match in links:
+    link, href, link_text = link_match
+    if not href.startswith('file:'):
+      continue
+
+    new_href = href.replace('file:', '')
+    new_href = new_href.replace(telemetry_dir, '..')
+    new_href = new_href.replace(os.sep, '/')
+
+    new_link_text = link_text.replace(telemetry_dir + os.sep, '')
+
+    new_link = '<a href="%s">%s</a>' % (new_href, new_link_text)
+    html = html.replace(link, new_link)
+
+  # pydoc writes out html with absolute path file links. This is not suitable
+  # for checked in documentation. So, fix up the HTML after it is generated.
+  #html = re.sub('href="file:%s' % telemetry_dir, 'href="..', html)
+  #html = re.sub(telemetry_dir + os.sep, '', html)
+  return html
+
+def WriteHTMLForModule(module):
+  page = GenerateHTMLForModule(module)
+  path = os.path.join(docs_dir, '%s.html' % module.__name__)
+  with open(path, 'w') as f:
+    sys.stderr.write('Wrote %s\n' % os.path.relpath(path))
+    f.write(page)
+
+def GetAllModulesToDocument(module):
+  modules = [module]
+  for _, modname, _ in pkgutil.walk_packages(
+      module.__path__, module.__name__ + '.'):
+    if modname.endswith('_unittest'):
+      logging.debug("skipping %s due to being a unittest", modname)
+      continue
+
+    module = __import__(modname, fromlist=[""])
+    name, _ = os.path.splitext(module.__file__)
+    if not os.path.exists(name + '.py'):
+      logging.info("skipping %s due to being an orphan .pyc", module.__file__)
+      continue
+
+    modules.append(module)
+  return modules
+
+class AlreadyDocumentedModule(object):
+  def __init__(self, filename):
+    self.filename = filename
+
+  @property
+  def name(self):
+    basename = os.path.basename(self.filename)
+    return os.path.splitext(basename)[0]
+
+  @property
+  def contents(self):
+    with open(self.filename, 'r') as f:
+      return f.read()
+
+def GetAlreadyDocumentedModules():
+  modules = []
+  for dirname, _, filenames in os.walk(docs_dir):
+    for filename in filenames:
+      path = os.path.join(dirname, filename)
+      modules.append(AlreadyDocumentedModule(path))
+  return modules
+
+
+def IsUpdateDocsNeeded():
+  already_documented_modules = GetAlreadyDocumentedModules()
+  already_documented_modules_by_name = dict(
+    (module.name, module) for module in already_documented_modules)
+  current_modules = GetAllModulesToDocument(telemetry)
+
+  # Quick check: if the names of modules has changed, we definitely need
+  # an update.
+  already_documented_module_names = set(
+    m.name for m in already_documented_modules)
+
+  current_module_names = set([m.__name__ for m in current_modules])
+
+  if current_module_names != already_documented_module_names:
+    return True
+
+  # Generate the new docs and compare aganist the old. If changed, then a
+  # an update is needed.
+  for current_module in current_modules:
+    already_documented_module = already_documented_modules_by_name[
+      current_module.__name__]
+    current_html = GenerateHTMLForModule(current_module)
+    if current_html != already_documented_module.contents:
+      return True
+
+  return False
+
+def Main(args):
+  parser = optparse.OptionParser()
+  parser.add_option(
+      '-v', '--verbose', action='count', dest='verbosity',
+      help='Increase verbosity level (repeat as needed)')
+  options, args = parser.parse_args(args)
+  if options.verbosity >= 2:
+    logging.getLogger().setLevel(logging.DEBUG)
+  elif options.verbosity:
+    logging.getLogger().setLevel(logging.INFO)
+  else:
+    logging.getLogger().setLevel(logging.WARNING)
+
+  assert os.path.isdir(docs_dir), '%s does not exist' % docs_dir
+
+  RemoveAllDocs()
+
+  old_cwd = os.getcwd()
+  try:
+    os.chdir(telemetry_dir)
+    for module in GetAllModulesToDocument(telemetry):
+      WriteHTMLForModule(module)
+  finally:
+    os.chdir(old_cwd)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/cloud_storage b/sdk/platform-tools/systrace/catapult/telemetry/cloud_storage
new file mode 100755
index 0000000..37adbbc
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/cloud_storage
@@ -0,0 +1,200 @@
+#!/usr/bin/env python
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import argparse
+import logging
+import os
+import subprocess
+import sys
+
+from telemetry.core import util
+from telemetry.internal.util import command_line
+
+sys.path.insert(1, os.path.abspath(os.path.join(
+    util.GetCatapultDir(), 'common', 'py_utils')))
+from py_utils import cloud_storage
+
+
+BUCKETS = {bucket: easy_bucket_name for easy_bucket_name, bucket
+           in cloud_storage.BUCKET_ALIASES.iteritems()}
+
+
+def _GetPaths(path):
+  root, ext = os.path.splitext(path)
+  if ext == '.sha1':
+    file_path = root
+    hash_path = path
+  else:
+    file_path = path
+    hash_path = path + '.sha1'
+  return file_path, hash_path
+
+
+def _FindFilesInCloudStorage(files):
+  """Returns a dict of all files and which buckets they're in."""
+  # Preprocessing: get the contents of all buckets.
+  bucket_contents = {}
+  for bucket in BUCKETS:
+    try:
+      bucket_contents[bucket] = cloud_storage.List(bucket)
+    except (cloud_storage.PermissionError, cloud_storage.CredentialsError):
+      pass
+
+  # Check if each file is in the bucket contents.
+  file_buckets = {}
+  for path in files:
+    file_path, hash_path = _GetPaths(path)
+
+    if file_path in file_buckets:
+      # Ignore duplicates, if both data and sha1 file were in the file list.
+      continue
+    if not os.path.exists(hash_path):
+      # Probably got some non-Cloud Storage files in the file list. Ignore.
+      continue
+
+    file_hash = cloud_storage.ReadHash(hash_path)
+    file_buckets[file_path] = []
+    for bucket in BUCKETS:
+      if bucket in bucket_contents and file_hash in bucket_contents[bucket]:
+        file_buckets[file_path].append(bucket)
+
+  return file_buckets
+
+
+class Ls(command_line.Command):
+  """List which bucket each file is in."""
+
+  @classmethod
+  def AddCommandLineArgs(cls, parser):
+    parser.add_argument('-r', '--recursive', action='store_true')
+    parser.add_argument('paths', nargs='+')
+
+  @classmethod
+  def ProcessCommandLineArgs(cls, parser, args):
+    for path in args.paths:
+      if not os.path.exists(path):
+        parser.error('Path not found: %s' % path)
+
+  def Run(self, args):
+    def GetFilesInPaths(paths, recursive):
+      """If path is a dir, yields all files in path, otherwise just yields path.
+      If recursive is true, walks subdirectories recursively."""
+      for path in paths:
+        if not os.path.isdir(path):
+          yield path
+          continue
+
+        if recursive:
+          for root, _, filenames in os.walk(path):
+            for filename in filenames:
+              yield os.path.join(root, filename)
+        else:
+          for filename in os.listdir(path):
+            yield os.path.join(path, filename)
+
+    files = _FindFilesInCloudStorage(GetFilesInPaths(args.paths, args.recursive))
+
+    if not files:
+      print 'No files in Cloud Storage.'
+      return
+
+    for file_path, buckets in sorted(files.iteritems()):
+      if buckets:
+        buckets = [BUCKETS[bucket] for bucket in buckets]
+        print '%-11s  %s' % (','.join(buckets), file_path)
+      else:
+        print '%-11s  %s' % ('not found', file_path)
+
+
+class Mv(command_line.Command):
+  """Move files to the given bucket."""
+
+  @classmethod
+  def AddCommandLineArgs(cls, parser):
+    parser.add_argument('files', nargs='+')
+    parser.add_argument('bucket', choices=cloud_storage.BUCKET_ALIASES)
+
+  @classmethod
+  def ProcessCommandLineArgs(cls, parser, args):
+    args.bucket = cloud_storage.BUCKET_ALIASES[args.bucket]
+
+  def Run(self, args):
+    files = _FindFilesInCloudStorage(args.files)
+
+    for file_path, buckets in sorted(files.iteritems()):
+      if not buckets:
+        raise IOError('%s not found in Cloud Storage.' % file_path)
+
+    for file_path, buckets in sorted(files.iteritems()):
+      if args.bucket in buckets:
+        buckets.remove(args.bucket)
+      if not buckets:
+        logging.info('Skipping %s, no action needed.' % file_path)
+        continue
+
+      # Move to the target bucket.
+      file_hash = cloud_storage.ReadHash(file_path + '.sha1')
+      cloud_storage.Move(buckets.pop(), args.bucket, file_hash)
+
+      # Delete all additional copies.
+      for bucket in buckets:
+        cloud_storage.Delete(bucket, file_hash)
+
+
+class Rm(command_line.Command):
+  """Remove files from Cloud Storage."""
+
+  @classmethod
+  def AddCommandLineArgs(cls, parser):
+    parser.add_argument('files', nargs='+')
+
+  def Run(self, args):
+    files = _FindFilesInCloudStorage(args.files)
+    for file_path, buckets in sorted(files.iteritems()):
+      file_hash = cloud_storage.ReadHash(file_path + '.sha1')
+      for bucket in buckets:
+        cloud_storage.Delete(bucket, file_hash)
+
+
+class Upload(command_line.Command):
+  """Upload files to Cloud Storage."""
+
+  @classmethod
+  def AddCommandLineArgs(cls, parser):
+    parser.add_argument('files', nargs='+')
+    parser.add_argument('bucket', choices=cloud_storage.BUCKET_ALIASES)
+
+  @classmethod
+  def ProcessCommandLineArgs(cls, parser, args):
+    args.bucket = cloud_storage.BUCKET_ALIASES[args.bucket]
+
+    for path in args.files:
+      if not os.path.exists(path):
+        parser.error('File not found: %s' % path)
+
+  def Run(self, args):
+    for file_path in args.files:
+      file_hash = cloud_storage.CalculateHash(file_path)
+
+      # Create or update the hash file.
+      hash_path = file_path + '.sha1'
+      with open(hash_path, 'wb') as f:
+        f.write(file_hash)
+        f.flush()
+
+      # Add the data to Cloud Storage.
+      cloud_storage.Insert(args.bucket, file_hash, file_path)
+
+      # Add the hash file to the branch, for convenience. :)
+      subprocess.call(['git', 'add', hash_path])
+
+
+class CloudStorageCommand(command_line.SubcommandCommand):
+  commands = (Ls, Mv, Rm, Upload)
+
+
+if __name__ == '__main__':
+  logging.getLogger().setLevel(logging.INFO)
+  sys.exit(CloudStorageCommand.main())
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/api-deprecation-procedure.md b/sdk/platform-tools/systrace/catapult/telemetry/docs/api-deprecation-procedure.md
new file mode 100644
index 0000000..0023db2
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/api-deprecation-procedure.md
@@ -0,0 +1,46 @@
+# Telemetry API Deprecation Procedure
+
+## Procedure for hard deprecation:
+1. Determine a deprecation time-frame.
+2. Create documentation on suggested refactor/workarounds if applicable.
+3. Apply applicable warnings for users about the deprecation.
+4. Announce deprecation.
+5. (Optional) Audit important users for usage of deprecated code prior to deletion.
+6. Delete the offending code.
+
+## Determine deprecation time-frame.
+
+The default time-frame is 18 weeks. If the expected user refractors are expected to take more than a third of that time to complete, this timeframe may be extended on a case-by-case basis as appropriate prior to announcement.
+
+## Create documentation on suggested refactor/workarounds if applicable.
+
+If a large refactor is expected for the users to stop using the deprecated code, documentation with suggested alternatives should be created. This may include examples (placed in the examples folder in the appropriate location), a wiki page documenting the change and/or our suggested workarounds/refactors, and/or a link to any replacement features.
+
+## Apply applicable warnings for users about the deprecation.
+
+This should include any applicable documentation (or links there to), deprecation deadline (as determined in step 1).
+
+### For Python code:
+
+1. All functions and classes to be deprecated should use a deprecation decorator.
+   You must pass in the deprecation deadline as determined by step 1 (counted from time of the CL containing these changes) into the decorator. The decorator will contain a warning message using the DeprecationWarning category and outlining the following:
+   * A warning of the function being deprecated.
+   * The deadline to refactor their code before the function is deleted.
+   * An (externally available) email they can use to contact us requesting an extension to the proposed deletion date.
+   * A warning that the deadline will only rarely be extended, and only for cases with obvious need and significant forewarning.
+
+2. All functions should log links to any applicable documentation for refactoring, or references to replacement API.
+
+## Announce deprecation
+An email should be sent out to telemetry-announce@chromium.org announcing the API being deprecated, the reason(s) for the deprecation, the deprecation deadline, and include any (links to) documentation.
+Extension of deprecation deadline
+This should happen extremely rarely, if ever. If a user brings up extenuating circumstances, contributors may be asked to give input, and are welcome to make suggestions, on whether we should give an extension to the deadline. The final decision is at the discretion of the PM and TL/TLM for the team owning the portion of the codebase in question, and should take any blocked future work for the product, and timeliness of the request into account.
+
+## (Optional) Audit important users for usage of deprecated code prior to deletion.
+
+In some circumstances, it may be worthwhile to check chromium (or other important users) for usage of deprecated API’s prior to deletion. It may be done on a case by case basis as needed for deprecation of important features.
+
+## Delete the offending code.
+1. Commit CL deleting code.
+2. ???
+3. Profit.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.android.android_story.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.android.android_story.html
new file mode 100644
index 0000000..83abe1b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.android.android_story.html
@@ -0,0 +1,105 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.android.android_story</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.android.html"><font color="#ffffff">android</font></a>.android_story</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/android/android_story.py">telemetry/android/android_story.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.android.shared_android_state.html">telemetry.android.shared_android_state</a><br>
+</td><td width="25%" valign=top><a href="telemetry.story.html">telemetry.story</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.story.story.html#Story">telemetry.story.story.Story</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.android.android_story.html#AndroidStory">AndroidStory</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="AndroidStory">class <strong>AndroidStory</strong></a>(<a href="telemetry.story.story.html#Story">telemetry.story.story.Story</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.android.android_story.html#AndroidStory">AndroidStory</a></dd>
+<dd><a href="telemetry.story.story.html#Story">telemetry.story.story.Story</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="AndroidStory-Run"><strong>Run</strong></a>(self, shared_state)</dt><dd><tt>Execute&nbsp;the&nbsp;interactions&nbsp;with&nbsp;the&nbsp;applications.</tt></dd></dl>
+
+<dl><dt><a name="AndroidStory-__init__"><strong>__init__</strong></a>(self, start_intent, is_app_ready_predicate<font color="#909090">=None</font>, name<font color="#909090">=''</font>, labels<font color="#909090">=None</font>, is_local<font color="#909090">=False</font>)</dt><dd><tt>Creates&nbsp;a&nbsp;new&nbsp;story&nbsp;for&nbsp;Android&nbsp;app.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;start_intent:&nbsp;See&nbsp;AndroidPlatform.LaunchAndroidApplication.<br>
+&nbsp;&nbsp;is_app_ready_predicate:&nbsp;See&nbsp;AndroidPlatform.LaunchAndroidApplication.<br>
+&nbsp;&nbsp;name:&nbsp;See&nbsp;<a href="telemetry.story.story.html#Story">Story</a>.__init__.<br>
+&nbsp;&nbsp;labels:&nbsp;See&nbsp;<a href="telemetry.story.story.html#Story">Story</a>.__init__.<br>
+&nbsp;&nbsp;is_app_ready_predicate:&nbsp;See&nbsp;<a href="telemetry.story.story.html#Story">Story</a>.__init__.</tt></dd></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.story.story.html#Story">telemetry.story.story.Story</a>:<br>
+<dl><dt><a name="AndroidStory-AsDict"><strong>AsDict</strong></a>(self)</dt><dd><tt>Converts&nbsp;a&nbsp;story&nbsp;object&nbsp;to&nbsp;a&nbsp;dict&nbsp;suitable&nbsp;for&nbsp;JSON&nbsp;output.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.story.story.html#Story">telemetry.story.story.Story</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>display_name</strong></dt>
+</dl>
+<dl><dt><strong>file_safe_name</strong></dt>
+<dd><tt>A&nbsp;version&nbsp;of&nbsp;display_name&nbsp;that's&nbsp;safe&nbsp;to&nbsp;use&nbsp;as&nbsp;a&nbsp;filename.<br>
+&nbsp;<br>
+The&nbsp;default&nbsp;implementation&nbsp;sanitizes&nbsp;special&nbsp;characters&nbsp;with&nbsp;underscores,<br>
+but&nbsp;it's&nbsp;okay&nbsp;to&nbsp;override&nbsp;it&nbsp;with&nbsp;a&nbsp;more&nbsp;specific&nbsp;implementation&nbsp;in<br>
+subclasses.</tt></dd>
+</dl>
+<dl><dt><strong>id</strong></dt>
+</dl>
+<dl><dt><strong>is_local</strong></dt>
+<dd><tt>Returns&nbsp;True&nbsp;iff&nbsp;this&nbsp;story&nbsp;does&nbsp;not&nbsp;require&nbsp;network.</tt></dd>
+</dl>
+<dl><dt><strong>labels</strong></dt>
+</dl>
+<dl><dt><strong>make_javascript_deterministic</strong></dt>
+</dl>
+<dl><dt><strong>name</strong></dt>
+</dl>
+<dl><dt><strong>serving_dir</strong></dt>
+<dd><tt>Returns&nbsp;the&nbsp;absolute&nbsp;path&nbsp;to&nbsp;a&nbsp;directory&nbsp;with&nbsp;hash&nbsp;files&nbsp;to&nbsp;data&nbsp;that<br>
+should&nbsp;be&nbsp;updated&nbsp;from&nbsp;cloud&nbsp;storage,&nbsp;or&nbsp;None&nbsp;if&nbsp;no&nbsp;files&nbsp;need&nbsp;to&nbsp;be<br>
+updated.</tt></dd>
+</dl>
+<dl><dt><strong>shared_state_class</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.android.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.android.html
new file mode 100644
index 0000000..748edfe
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.android.html
@@ -0,0 +1,26 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: package telemetry.android</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.android</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/android/__init__.py">telemetry/android/__init__.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2015&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Package Contents</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.android.android_story.html">android_story</a><br>
+</td><td width="25%" valign=top><a href="telemetry.android.shared_android_state.html">shared_android_state</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.android.shared_android_state.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.android.shared_android_state.html
new file mode 100644
index 0000000..c285378
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.android.shared_android_state.html
@@ -0,0 +1,96 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.android.shared_android_state</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.android.html"><font color="#ffffff">android</font></a>.shared_android_state</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/android/shared_android_state.py">telemetry/android/shared_android_state.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.platform.android_device.html">telemetry.internal.platform.android_device</a><br>
+<a href="telemetry.core.android_platform.html">telemetry.core.android_platform</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.platform.html">telemetry.core.platform</a><br>
+<a href="telemetry.story.html">telemetry.story</a><br>
+</td><td width="25%" valign=top><a href="telemetry.web_perf.timeline_based_measurement.html">telemetry.web_perf.timeline_based_measurement</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.story.shared_state.html#SharedState">telemetry.story.shared_state.SharedState</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.android.shared_android_state.html#SharedAndroidState">SharedAndroidState</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="SharedAndroidState">class <strong>SharedAndroidState</strong></a>(<a href="telemetry.story.shared_state.html#SharedState">telemetry.story.shared_state.SharedState</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Manage&nbsp;test&nbsp;state/transitions&nbsp;across&nbsp;multiple&nbsp;android.AndroidStory's.<br>
+&nbsp;<br>
+WARNING:&nbsp;the&nbsp;class&nbsp;is&nbsp;not&nbsp;ready&nbsp;for&nbsp;public&nbsp;consumption.<br>
+Email&nbsp;telemetry@chromium.org&nbsp;if&nbsp;you&nbsp;feel&nbsp;like&nbsp;you&nbsp;must&nbsp;use&nbsp;it.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.android.shared_android_state.html#SharedAndroidState">SharedAndroidState</a></dd>
+<dd><a href="telemetry.story.shared_state.html#SharedState">telemetry.story.shared_state.SharedState</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="SharedAndroidState-CanRunStory"><strong>CanRunStory</strong></a>(self, story)</dt><dd><tt>This&nbsp;does&nbsp;not&nbsp;apply&nbsp;to&nbsp;android&nbsp;app&nbsp;stories.</tt></dd></dl>
+
+<dl><dt><a name="SharedAndroidState-DidRunStory"><strong>DidRunStory</strong></a>(self, results)</dt></dl>
+
+<dl><dt><a name="SharedAndroidState-RunStory"><strong>RunStory</strong></a>(self, results)</dt></dl>
+
+<dl><dt><a name="SharedAndroidState-TearDownState"><strong>TearDownState</strong></a>(self)</dt><dd><tt>Tear&nbsp;down&nbsp;anything&nbsp;created&nbsp;in&nbsp;the&nbsp;__init__&nbsp;method&nbsp;that&nbsp;is&nbsp;not&nbsp;needed.<br>
+&nbsp;<br>
+Currently,&nbsp;there&nbsp;is&nbsp;no&nbsp;clean-up&nbsp;needed&nbsp;from&nbsp;<a href="#SharedAndroidState">SharedAndroidState</a>.__init__.</tt></dd></dl>
+
+<dl><dt><a name="SharedAndroidState-WillRunStory"><strong>WillRunStory</strong></a>(self, story)</dt></dl>
+
+<dl><dt><a name="SharedAndroidState-__init__"><strong>__init__</strong></a>(self, test, finder_options, story_set)</dt><dd><tt>This&nbsp;method&nbsp;is&nbsp;styled&nbsp;on&nbsp;unittest.TestCase.setUpClass.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;test:&nbsp;a&nbsp;web_perf.TimelineBasedMeasurement&nbsp;instance.<br>
+&nbsp;&nbsp;options:&nbsp;a&nbsp;BrowserFinderOptions&nbsp;instance&nbsp;with&nbsp;command&nbsp;line&nbsp;options.<br>
+&nbsp;&nbsp;story_set:&nbsp;a&nbsp;story.StorySet&nbsp;instance.</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>app</strong></dt>
+</dl>
+<dl><dt><strong>platform</strong></dt>
+</dl>
+<hr>
+Data descriptors inherited from <a href="telemetry.story.shared_state.html#SharedState">telemetry.story.shared_state.SharedState</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.benchmark.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.benchmark.html
new file mode 100644
index 0000000..fb5de7b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.benchmark.html
@@ -0,0 +1,299 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.benchmark</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.benchmark</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/benchmark.py">telemetry/benchmark.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.util.command_line.html">telemetry.internal.util.command_line</a><br>
+<a href="telemetry.decorators.html">telemetry.decorators</a><br>
+</td><td width="25%" valign=top><a href="optparse.html">optparse</a><br>
+<a href="telemetry.page.page_test.html">telemetry.page.page_test</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.story_runner.html">telemetry.internal.story_runner</a><br>
+<a href="telemetry.web_perf.timeline_based_measurement.html">telemetry.web_perf.timeline_based_measurement</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.benchmark.html#BenchmarkMetadata">BenchmarkMetadata</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="exceptions.html#Exception">exceptions.Exception</a>(<a href="exceptions.html#BaseException">exceptions.BaseException</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.benchmark.html#InvalidOptionsError">InvalidOptionsError</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.util.command_line.html#Command">telemetry.internal.util.command_line.Command</a>(<a href="telemetry.internal.util.command_line.html#ArgumentHandlerMixIn">telemetry.internal.util.command_line.ArgumentHandlerMixIn</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.benchmark.html#Benchmark">Benchmark</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="Benchmark">class <strong>Benchmark</strong></a>(<a href="telemetry.internal.util.command_line.html#Command">telemetry.internal.util.command_line.Command</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Base&nbsp;class&nbsp;for&nbsp;a&nbsp;Telemetry&nbsp;benchmark.<br>
+&nbsp;<br>
+A&nbsp;benchmark&nbsp;packages&nbsp;a&nbsp;measurement&nbsp;and&nbsp;a&nbsp;PageSet&nbsp;together.<br>
+Benchmarks&nbsp;default&nbsp;to&nbsp;using&nbsp;TBM&nbsp;unless&nbsp;you&nbsp;override&nbsp;the&nbsp;value&nbsp;of<br>
+<a href="#Benchmark">Benchmark</a>.test,&nbsp;or&nbsp;override&nbsp;the&nbsp;CreatePageTest&nbsp;method.<br>
+&nbsp;<br>
+New&nbsp;benchmarks&nbsp;should&nbsp;override&nbsp;CreateStorySet.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.benchmark.html#Benchmark">Benchmark</a></dd>
+<dd><a href="telemetry.internal.util.command_line.html#Command">telemetry.internal.util.command_line.Command</a></dd>
+<dd><a href="telemetry.internal.util.command_line.html#ArgumentHandlerMixIn">telemetry.internal.util.command_line.ArgumentHandlerMixIn</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="Benchmark-CreatePageTest"><strong>CreatePageTest</strong></a>(self, options)</dt><dd><tt>Return&nbsp;the&nbsp;PageTest&nbsp;for&nbsp;this&nbsp;<a href="#Benchmark">Benchmark</a>.<br>
+&nbsp;<br>
+Override&nbsp;this&nbsp;method&nbsp;for&nbsp;PageTest&nbsp;tests.<br>
+Override,&nbsp;override&nbsp;CreateTimelineBasedMeasurementOptions&nbsp;to&nbsp;configure<br>
+TimelineBasedMeasurement&nbsp;tests.&nbsp;Do&nbsp;not&nbsp;override&nbsp;both&nbsp;methods.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;options:&nbsp;a&nbsp;browser_options.BrowserFinderOptions&nbsp;instance<br>
+Returns:<br>
+&nbsp;&nbsp;|<a href="#Benchmark-test">test</a>()|&nbsp;if&nbsp;|test|&nbsp;is&nbsp;a&nbsp;PageTest&nbsp;class.<br>
+&nbsp;&nbsp;Otherwise,&nbsp;a&nbsp;TimelineBasedMeasurement&nbsp;instance.</tt></dd></dl>
+
+<dl><dt><a name="Benchmark-CreateStorySet"><strong>CreateStorySet</strong></a>(self, options)</dt><dd><tt>Creates&nbsp;the&nbsp;instance&nbsp;of&nbsp;StorySet&nbsp;used&nbsp;to&nbsp;run&nbsp;the&nbsp;benchmark.<br>
+&nbsp;<br>
+Can&nbsp;be&nbsp;overridden&nbsp;by&nbsp;subclasses.</tt></dd></dl>
+
+<dl><dt><a name="Benchmark-CreateTimelineBasedMeasurementOptions"><strong>CreateTimelineBasedMeasurementOptions</strong></a>(self)</dt><dd><tt>Return&nbsp;the&nbsp;TimelineBasedMeasurementOptions&nbsp;for&nbsp;this&nbsp;<a href="#Benchmark">Benchmark</a>.<br>
+&nbsp;<br>
+Override&nbsp;this&nbsp;method&nbsp;to&nbsp;configure&nbsp;a&nbsp;TimelineBasedMeasurement&nbsp;benchmark.<br>
+Otherwise,&nbsp;override&nbsp;CreatePageTest&nbsp;for&nbsp;PageTest&nbsp;tests.&nbsp;Do&nbsp;not&nbsp;override<br>
+both&nbsp;methods.</tt></dd></dl>
+
+<dl><dt><a name="Benchmark-CustomizeBrowserOptions"><strong>CustomizeBrowserOptions</strong></a>(self, options)</dt><dd><tt>Add&nbsp;browser&nbsp;options&nbsp;that&nbsp;are&nbsp;required&nbsp;by&nbsp;this&nbsp;benchmark.</tt></dd></dl>
+
+<dl><dt><a name="Benchmark-GetMetadata"><strong>GetMetadata</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="Benchmark-GetTraceRerunCommands"><strong>GetTraceRerunCommands</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="Benchmark-Run"><strong>Run</strong></a>(self, finder_options)</dt><dd><tt>Do&nbsp;not&nbsp;override&nbsp;this&nbsp;method.</tt></dd></dl>
+
+<dl><dt><a name="Benchmark-SetupBenchmarkDebugTraceRerunOptions"><strong>SetupBenchmarkDebugTraceRerunOptions</strong></a>(self, tbm_options)</dt><dd><tt>Setup&nbsp;tracing&nbsp;categories&nbsp;associated&nbsp;with&nbsp;debug&nbsp;trace&nbsp;option.</tt></dd></dl>
+
+<dl><dt><a name="Benchmark-SetupBenchmarkDefaultTraceRerunOptions"><strong>SetupBenchmarkDefaultTraceRerunOptions</strong></a>(self, tbm_options)</dt><dd><tt>Setup&nbsp;tracing&nbsp;categories&nbsp;associated&nbsp;with&nbsp;default&nbsp;trace&nbsp;option.</tt></dd></dl>
+
+<dl><dt><a name="Benchmark-SetupTraceRerunOptions"><strong>SetupTraceRerunOptions</strong></a>(self, browser_options, tbm_options)</dt></dl>
+
+<dl><dt><a name="Benchmark-__init__"><strong>__init__</strong></a>(self, max_failures<font color="#909090">=None</font>)</dt><dd><tt>Creates&nbsp;a&nbsp;new&nbsp;<a href="#Benchmark">Benchmark</a>.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;max_failures:&nbsp;The&nbsp;number&nbsp;of&nbsp;story&nbsp;run's&nbsp;failures&nbsp;before&nbsp;bailing<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;from&nbsp;executing&nbsp;subsequent&nbsp;page&nbsp;runs.&nbsp;If&nbsp;None,&nbsp;we&nbsp;never&nbsp;bail.</tt></dd></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="Benchmark-AddCommandLineArgs"><strong>AddCommandLineArgs</strong></a>(cls, parser)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="Benchmark-HasTraceRerunDebugOption"><strong>HasTraceRerunDebugOption</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="Benchmark-Name"><strong>Name</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="Benchmark-ProcessCommandLineArgs"><strong>ProcessCommandLineArgs</strong></a>(cls, parser, args)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="Benchmark-SetArgumentDefaults"><strong>SetArgumentDefaults</strong></a>(cls, parser)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="Benchmark-ShouldDisable"><strong>ShouldDisable</strong></a>(cls, possible_browser)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Override&nbsp;this&nbsp;method&nbsp;to&nbsp;disable&nbsp;a&nbsp;benchmark&nbsp;under&nbsp;specific&nbsp;conditions.<br>
+&nbsp;<br>
+Supports&nbsp;logic&nbsp;too&nbsp;complex&nbsp;for&nbsp;simple&nbsp;Enabled&nbsp;and&nbsp;Disabled&nbsp;decorators.<br>
+Decorators&nbsp;are&nbsp;still&nbsp;respected&nbsp;in&nbsp;cases&nbsp;where&nbsp;this&nbsp;function&nbsp;returns&nbsp;False.</tt></dd></dl>
+
+<dl><dt><a name="Benchmark-ValueCanBeAddedPredicate"><strong>ValueCanBeAddedPredicate</strong></a>(cls, value, is_first_result)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Returns&nbsp;whether&nbsp;|value|&nbsp;can&nbsp;be&nbsp;added&nbsp;to&nbsp;the&nbsp;test&nbsp;results.<br>
+&nbsp;<br>
+Override&nbsp;this&nbsp;method&nbsp;to&nbsp;customize&nbsp;the&nbsp;logic&nbsp;of&nbsp;adding&nbsp;values&nbsp;to&nbsp;test<br>
+results.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;value:&nbsp;a&nbsp;value.Value&nbsp;instance&nbsp;(except&nbsp;failure.FailureValue,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;skip.SkipValue&nbsp;or&nbsp;trace.TraceValue&nbsp;which&nbsp;will&nbsp;always&nbsp;be&nbsp;added).<br>
+&nbsp;&nbsp;is_first_result:&nbsp;True&nbsp;if&nbsp;|value|&nbsp;is&nbsp;the&nbsp;first&nbsp;result&nbsp;for&nbsp;its<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;corresponding&nbsp;story.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;True&nbsp;if&nbsp;|value|&nbsp;should&nbsp;be&nbsp;added&nbsp;to&nbsp;the&nbsp;test&nbsp;results.<br>
+&nbsp;&nbsp;Otherwise,&nbsp;it&nbsp;returns&nbsp;False.</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>max_failures</strong></dt>
+</dl>
+<hr>
+Data and other attributes defined here:<br>
+<dl><dt><strong>options</strong> = {}</dl>
+
+<dl><dt><strong>test</strong> = &lt;class 'telemetry.web_perf.timeline_based_measurement.TimelineBasedMeasurement'&gt;<dd><tt>Collects&nbsp;multiple&nbsp;metrics&nbsp;based&nbsp;on&nbsp;their&nbsp;interaction&nbsp;records.<br>
+&nbsp;<br>
+A&nbsp;timeline&nbsp;based&nbsp;measurement&nbsp;shifts&nbsp;the&nbsp;burden&nbsp;of&nbsp;what&nbsp;metrics&nbsp;to&nbsp;collect&nbsp;onto<br>
+the&nbsp;story&nbsp;under&nbsp;test.&nbsp;Instead&nbsp;of&nbsp;the&nbsp;measurement<br>
+having&nbsp;a&nbsp;fixed&nbsp;set&nbsp;of&nbsp;values&nbsp;it&nbsp;collects,&nbsp;the&nbsp;story&nbsp;being&nbsp;tested<br>
+issues&nbsp;(via&nbsp;javascript)&nbsp;an&nbsp;Interaction&nbsp;record&nbsp;into&nbsp;the&nbsp;user&nbsp;timing&nbsp;API&nbsp;that<br>
+describing&nbsp;what&nbsp;is&nbsp;happening&nbsp;at&nbsp;that&nbsp;time,&nbsp;as&nbsp;well&nbsp;as&nbsp;a&nbsp;standardized&nbsp;set<br>
+of&nbsp;flags&nbsp;describing&nbsp;the&nbsp;semantics&nbsp;of&nbsp;the&nbsp;work&nbsp;being&nbsp;done.&nbsp;The<br>
+TimelineBasedMeasurement&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;collects&nbsp;a&nbsp;trace&nbsp;that&nbsp;includes&nbsp;both&nbsp;these<br>
+interaction&nbsp;records,&nbsp;and&nbsp;a&nbsp;user-chosen&nbsp;amount&nbsp;of&nbsp;performance&nbsp;data&nbsp;using<br>
+Telemetry's&nbsp;various&nbsp;timeline-producing&nbsp;APIs,&nbsp;tracing&nbsp;especially.<br>
+&nbsp;<br>
+It&nbsp;then&nbsp;passes&nbsp;the&nbsp;recorded&nbsp;timeline&nbsp;to&nbsp;different&nbsp;TimelineBasedMetrics&nbsp;based<br>
+on&nbsp;those&nbsp;flags.&nbsp;As&nbsp;an&nbsp;example,&nbsp;this&nbsp;allows&nbsp;a&nbsp;single&nbsp;story&nbsp;run&nbsp;to&nbsp;produce<br>
+load&nbsp;timing&nbsp;data,&nbsp;smoothness&nbsp;data,&nbsp;critical&nbsp;jank&nbsp;information&nbsp;and&nbsp;overall&nbsp;cpu<br>
+usage&nbsp;information.<br>
+&nbsp;<br>
+For&nbsp;information&nbsp;on&nbsp;how&nbsp;to&nbsp;mark&nbsp;up&nbsp;a&nbsp;page&nbsp;to&nbsp;work&nbsp;with<br>
+TimelineBasedMeasurement,&nbsp;refer&nbsp;to&nbsp;the<br>
+perf.metrics.timeline_interaction_record&nbsp;module.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;options:&nbsp;an&nbsp;instance&nbsp;of&nbsp;timeline_based_measurement.Options.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;results_wrapper:&nbsp;A&nbsp;class&nbsp;that&nbsp;has&nbsp;the&nbsp;__init__&nbsp;method&nbsp;takes&nbsp;in<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;the&nbsp;page_test_results&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;and&nbsp;the&nbsp;interaction&nbsp;record&nbsp;label.&nbsp;This<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class&nbsp;follows&nbsp;the&nbsp;ResultsWrapperInterface.&nbsp;Note:&nbsp;this&nbsp;class&nbsp;is&nbsp;not<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;supported&nbsp;long&nbsp;term&nbsp;and&nbsp;to&nbsp;be&nbsp;removed&nbsp;when&nbsp;crbug.com/453109&nbsp;is&nbsp;resolved.</tt></dl>
+
+<hr>
+Class methods inherited from <a href="telemetry.internal.util.command_line.html#Command">telemetry.internal.util.command_line.Command</a>:<br>
+<dl><dt><a name="Benchmark-Description"><strong>Description</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="Benchmark-main"><strong>main</strong></a>(cls, args<font color="#909090">=None</font>)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Main&nbsp;method&nbsp;to&nbsp;run&nbsp;this&nbsp;command&nbsp;as&nbsp;a&nbsp;standalone&nbsp;script.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.util.command_line.html#ArgumentHandlerMixIn">telemetry.internal.util.command_line.ArgumentHandlerMixIn</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="BenchmarkMetadata">class <strong>BenchmarkMetadata</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="BenchmarkMetadata-AsDict"><strong>AsDict</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="BenchmarkMetadata-__init__"><strong>__init__</strong></a>(self, name, description<font color="#909090">=''</font>, rerun_options<font color="#909090">=None</font>)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>description</strong></dt>
+</dl>
+<dl><dt><strong>name</strong></dt>
+</dl>
+<dl><dt><strong>rerun_options</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="InvalidOptionsError">class <strong>InvalidOptionsError</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Raised&nbsp;for&nbsp;invalid&nbsp;benchmark&nbsp;options.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.benchmark.html#InvalidOptionsError">InvalidOptionsError</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="InvalidOptionsError-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#InvalidOptionsError-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#InvalidOptionsError-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="InvalidOptionsError-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#InvalidOptionsError-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="InvalidOptionsError-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#InvalidOptionsError-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="InvalidOptionsError-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#InvalidOptionsError-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="InvalidOptionsError-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#InvalidOptionsError-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="InvalidOptionsError-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="InvalidOptionsError-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#InvalidOptionsError-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="InvalidOptionsError-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#InvalidOptionsError-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="InvalidOptionsError-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="InvalidOptionsError-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#InvalidOptionsError-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="InvalidOptionsError-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-AddCommandLineArgs"><strong>AddCommandLineArgs</strong></a>(parser)</dt></dl>
+ <dl><dt><a name="-ProcessCommandLineArgs"><strong>ProcessCommandLineArgs</strong></a>(parser, args)</dt></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.benchmark_runner.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.benchmark_runner.html
new file mode 100644
index 0000000..a6abee5
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.benchmark_runner.html
@@ -0,0 +1,223 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.benchmark_runner</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.benchmark_runner</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/benchmark_runner.py">telemetry/benchmark_runner.py</a></font></td></tr></table>
+    <p><tt>Parses&nbsp;the&nbsp;command&nbsp;line,&nbsp;discovers&nbsp;the&nbsp;appropriate&nbsp;benchmarks,&nbsp;and&nbsp;runs&nbsp;them.<br>
+&nbsp;<br>
+Handles&nbsp;benchmark&nbsp;configuration,&nbsp;but&nbsp;all&nbsp;the&nbsp;logic&nbsp;for<br>
+actually&nbsp;running&nbsp;the&nbsp;benchmark&nbsp;is&nbsp;in&nbsp;Benchmark&nbsp;and&nbsp;PageRunner.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.benchmark.html">telemetry.benchmark</a><br>
+<a href="telemetry.internal.util.binary_manager.html">telemetry.internal.util.binary_manager</a><br>
+<a href="telemetry.internal.browser.browser_finder.html">telemetry.internal.browser.browser_finder</a><br>
+<a href="telemetry.internal.browser.browser_options.html">telemetry.internal.browser.browser_options</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.util.command_line.html">telemetry.internal.util.command_line</a><br>
+<a href="telemetry.decorators.html">telemetry.decorators</a><br>
+<a href="telemetry.core.discover.html">telemetry.core.discover</a><br>
+<a href="hashlib.html">hashlib</a><br>
+</td><td width="25%" valign=top><a href="inspect.html">inspect</a><br>
+<a href="json.html">json</a><br>
+<a href="logging.html">logging</a><br>
+<a href="os.html">os</a><br>
+</td><td width="25%" valign=top><a href="telemetry.project_config.html">telemetry.project_config</a><br>
+<a href="telemetry.internal.util.ps_util.html">telemetry.internal.util.ps_util</a><br>
+<a href="sys.html">sys</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.util.command_line.html#OptparseCommand">telemetry.internal.util.command_line.OptparseCommand</a>(<a href="telemetry.internal.util.command_line.html#Command">telemetry.internal.util.command_line.Command</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.benchmark_runner.html#Help">Help</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.benchmark_runner.html#List">List</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.benchmark_runner.html#Run">Run</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="Help">class <strong>Help</strong></a>(<a href="telemetry.internal.util.command_line.html#OptparseCommand">telemetry.internal.util.command_line.OptparseCommand</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Display&nbsp;help&nbsp;information&nbsp;about&nbsp;a&nbsp;command<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.benchmark_runner.html#Help">Help</a></dd>
+<dd><a href="telemetry.internal.util.command_line.html#OptparseCommand">telemetry.internal.util.command_line.OptparseCommand</a></dd>
+<dd><a href="telemetry.internal.util.command_line.html#Command">telemetry.internal.util.command_line.Command</a></dd>
+<dd><a href="telemetry.internal.util.command_line.html#ArgumentHandlerMixIn">telemetry.internal.util.command_line.ArgumentHandlerMixIn</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="Help-Run"><strong>Run</strong></a>(self, args)</dt></dl>
+
+<hr>
+Data and other attributes defined here:<br>
+<dl><dt><strong>usage</strong> = '[command]'</dl>
+
+<hr>
+Class methods inherited from <a href="telemetry.internal.util.command_line.html#OptparseCommand">telemetry.internal.util.command_line.OptparseCommand</a>:<br>
+<dl><dt><a name="Help-AddCommandLineArgs"><strong>AddCommandLineArgs</strong></a>(cls, parser, environment)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="Help-CreateParser"><strong>CreateParser</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="Help-ProcessCommandLineArgs"><strong>ProcessCommandLineArgs</strong></a>(cls, parser, args, environment)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="Help-main"><strong>main</strong></a>(cls, args<font color="#909090">=None</font>)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Main&nbsp;method&nbsp;to&nbsp;run&nbsp;this&nbsp;command&nbsp;as&nbsp;a&nbsp;standalone&nbsp;script.</tt></dd></dl>
+
+<hr>
+Class methods inherited from <a href="telemetry.internal.util.command_line.html#Command">telemetry.internal.util.command_line.Command</a>:<br>
+<dl><dt><a name="Help-Description"><strong>Description</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="Help-Name"><strong>Name</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.util.command_line.html#ArgumentHandlerMixIn">telemetry.internal.util.command_line.ArgumentHandlerMixIn</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="List">class <strong>List</strong></a>(<a href="telemetry.internal.util.command_line.html#OptparseCommand">telemetry.internal.util.command_line.OptparseCommand</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Lists&nbsp;the&nbsp;available&nbsp;benchmarks<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.benchmark_runner.html#List">List</a></dd>
+<dd><a href="telemetry.internal.util.command_line.html#OptparseCommand">telemetry.internal.util.command_line.OptparseCommand</a></dd>
+<dd><a href="telemetry.internal.util.command_line.html#Command">telemetry.internal.util.command_line.Command</a></dd>
+<dd><a href="telemetry.internal.util.command_line.html#ArgumentHandlerMixIn">telemetry.internal.util.command_line.ArgumentHandlerMixIn</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="List-Run"><strong>Run</strong></a>(self, args)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="List-AddCommandLineArgs"><strong>AddCommandLineArgs</strong></a>(cls, parser, _)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="List-CreateParser"><strong>CreateParser</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="List-ProcessCommandLineArgs"><strong>ProcessCommandLineArgs</strong></a>(cls, parser, args, environment)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Data and other attributes defined here:<br>
+<dl><dt><strong>usage</strong> = '[benchmark_name] [&lt;options&gt;]'</dl>
+
+<hr>
+Class methods inherited from <a href="telemetry.internal.util.command_line.html#OptparseCommand">telemetry.internal.util.command_line.OptparseCommand</a>:<br>
+<dl><dt><a name="List-main"><strong>main</strong></a>(cls, args<font color="#909090">=None</font>)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Main&nbsp;method&nbsp;to&nbsp;run&nbsp;this&nbsp;command&nbsp;as&nbsp;a&nbsp;standalone&nbsp;script.</tt></dd></dl>
+
+<hr>
+Class methods inherited from <a href="telemetry.internal.util.command_line.html#Command">telemetry.internal.util.command_line.Command</a>:<br>
+<dl><dt><a name="List-Description"><strong>Description</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="List-Name"><strong>Name</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.util.command_line.html#ArgumentHandlerMixIn">telemetry.internal.util.command_line.ArgumentHandlerMixIn</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="Run">class <strong>Run</strong></a>(<a href="telemetry.internal.util.command_line.html#OptparseCommand">telemetry.internal.util.command_line.OptparseCommand</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt><a href="#Run">Run</a>&nbsp;one&nbsp;or&nbsp;more&nbsp;benchmarks&nbsp;(default)<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.benchmark_runner.html#Run">Run</a></dd>
+<dd><a href="telemetry.internal.util.command_line.html#OptparseCommand">telemetry.internal.util.command_line.OptparseCommand</a></dd>
+<dd><a href="telemetry.internal.util.command_line.html#Command">telemetry.internal.util.command_line.Command</a></dd>
+<dd><a href="telemetry.internal.util.command_line.html#ArgumentHandlerMixIn">telemetry.internal.util.command_line.ArgumentHandlerMixIn</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="Run-Run"><strong>Run</strong></a>(self, args)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="Run-AddCommandLineArgs"><strong>AddCommandLineArgs</strong></a>(cls, parser, environment)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="Run-CreateParser"><strong>CreateParser</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="Run-ProcessCommandLineArgs"><strong>ProcessCommandLineArgs</strong></a>(cls, parser, args, environment)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Data and other attributes defined here:<br>
+<dl><dt><strong>usage</strong> = 'benchmark_name [page_set] [&lt;options&gt;]'</dl>
+
+<hr>
+Class methods inherited from <a href="telemetry.internal.util.command_line.html#OptparseCommand">telemetry.internal.util.command_line.OptparseCommand</a>:<br>
+<dl><dt><a name="Run-main"><strong>main</strong></a>(cls, args<font color="#909090">=None</font>)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Main&nbsp;method&nbsp;to&nbsp;run&nbsp;this&nbsp;command&nbsp;as&nbsp;a&nbsp;standalone&nbsp;script.</tt></dd></dl>
+
+<hr>
+Class methods inherited from <a href="telemetry.internal.util.command_line.html#Command">telemetry.internal.util.command_line.Command</a>:<br>
+<dl><dt><a name="Run-Description"><strong>Description</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="Run-Name"><strong>Name</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.util.command_line.html#ArgumentHandlerMixIn">telemetry.internal.util.command_line.ArgumentHandlerMixIn</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-GetBenchmarkByName"><strong>GetBenchmarkByName</strong></a>(name, environment)</dt></dl>
+ <dl><dt><a name="-PrintBenchmarkList"><strong>PrintBenchmarkList</strong></a>(benchmarks, possible_browser, output_pipe<font color="#909090">=&lt;open file '&lt;stdout&gt;', mode 'w'&gt;</font>)</dt><dd><tt>Print&nbsp;benchmarks&nbsp;that&nbsp;are&nbsp;not&nbsp;filtered&nbsp;in&nbsp;the&nbsp;same&nbsp;order&nbsp;of&nbsp;benchmarks&nbsp;in<br>
+the&nbsp;|benchmarks|&nbsp;list.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;benchmarks:&nbsp;the&nbsp;list&nbsp;of&nbsp;benchmarks&nbsp;to&nbsp;be&nbsp;printed&nbsp;(in&nbsp;the&nbsp;same&nbsp;order&nbsp;of&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;list).<br>
+&nbsp;&nbsp;possible_browser:&nbsp;the&nbsp;possible_browser&nbsp;instance&nbsp;that's&nbsp;used&nbsp;for&nbsp;checking<br>
+&nbsp;&nbsp;&nbsp;&nbsp;which&nbsp;benchmarks&nbsp;are&nbsp;enabled.<br>
+&nbsp;&nbsp;output_pipe:&nbsp;the&nbsp;stream&nbsp;in&nbsp;which&nbsp;benchmarks&nbsp;are&nbsp;printed&nbsp;on.</tt></dd></dl>
+ <dl><dt><a name="-main"><strong>main</strong></a>(environment)</dt></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.core.android_action_runner.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.core.android_action_runner.html
new file mode 100644
index 0000000..ec50ae5
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.core.android_action_runner.html
@@ -0,0 +1,191 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.core.android_action_runner</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.core.html"><font color="#ffffff">core</font></a>.android_action_runner</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/core/android_action_runner.py">telemetry/core/android_action_runner.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2015&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top><a href="time.html">time</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.util.html">telemetry.core.util</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.core.android_action_runner.html#AndroidActionRunner">AndroidActionRunner</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="exceptions.html#Exception">exceptions.Exception</a>(<a href="exceptions.html#BaseException">exceptions.BaseException</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.core.android_action_runner.html#ActionNotSupported">ActionNotSupported</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ActionNotSupported">class <strong>ActionNotSupported</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.core.android_action_runner.html#ActionNotSupported">ActionNotSupported</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="ActionNotSupported-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#ActionNotSupported-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#ActionNotSupported-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="ActionNotSupported-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#ActionNotSupported-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="ActionNotSupported-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#ActionNotSupported-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="ActionNotSupported-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#ActionNotSupported-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="ActionNotSupported-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#ActionNotSupported-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="ActionNotSupported-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="ActionNotSupported-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#ActionNotSupported-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="ActionNotSupported-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#ActionNotSupported-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="ActionNotSupported-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="ActionNotSupported-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#ActionNotSupported-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="ActionNotSupported-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="AndroidActionRunner">class <strong>AndroidActionRunner</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Provides&nbsp;an&nbsp;API&nbsp;for&nbsp;interacting&nbsp;with&nbsp;an&nbsp;android&nbsp;device.<br>
+&nbsp;<br>
+This&nbsp;makes&nbsp;use&nbsp;of&nbsp;functionality&nbsp;provided&nbsp;by&nbsp;the&nbsp;android&nbsp;input&nbsp;command.&nbsp;None<br>
+of&nbsp;the&nbsp;gestures&nbsp;here&nbsp;are&nbsp;guaranteed&nbsp;to&nbsp;be&nbsp;performant&nbsp;for&nbsp;telemetry&nbsp;tests&nbsp;and<br>
+there&nbsp;is&nbsp;no&nbsp;official&nbsp;support&nbsp;for&nbsp;this&nbsp;API.<br>
+&nbsp;<br>
+TODO(ariblue):&nbsp;Replace&nbsp;this&nbsp;API&nbsp;with&nbsp;a&nbsp;better&nbsp;implementation&nbsp;for&nbsp;interacting<br>
+with&nbsp;native&nbsp;components.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="AndroidActionRunner-InputKeyEvent"><strong>InputKeyEvent</strong></a>(self, key)</dt><dd><tt>Send&nbsp;a&nbsp;single&nbsp;key&nbsp;input&nbsp;to&nbsp;the&nbsp;device.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;key:&nbsp;A&nbsp;key&nbsp;code&nbsp;number&nbsp;or&nbsp;name&nbsp;that&nbsp;will&nbsp;be&nbsp;sent&nbsp;to&nbsp;the&nbsp;device</tt></dd></dl>
+
+<dl><dt><a name="AndroidActionRunner-InputPress"><strong>InputPress</strong></a>(self)</dt><dd><tt>Perform&nbsp;a&nbsp;press&nbsp;input.</tt></dd></dl>
+
+<dl><dt><a name="AndroidActionRunner-InputRoll"><strong>InputRoll</strong></a>(self, dx, dy)</dt><dd><tt>Perform&nbsp;a&nbsp;roll&nbsp;input.&nbsp;This&nbsp;sends&nbsp;a&nbsp;simple&nbsp;zero-pressure&nbsp;move&nbsp;event.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;dx:&nbsp;Change&nbsp;in&nbsp;the&nbsp;x&nbsp;coordinate&nbsp;due&nbsp;to&nbsp;move.<br>
+&nbsp;&nbsp;dy:&nbsp;Change&nbsp;in&nbsp;the&nbsp;y&nbsp;coordinate&nbsp;due&nbsp;to&nbsp;move.</tt></dd></dl>
+
+<dl><dt><a name="AndroidActionRunner-InputSwipe"><strong>InputSwipe</strong></a>(self, left_start_coord, top_start_coord, left_end_coord, top_end_coord, duration)</dt><dd><tt>Perform&nbsp;a&nbsp;swipe&nbsp;input.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;left_start_coord:&nbsp;The&nbsp;horizontal&nbsp;starting&nbsp;coordinate&nbsp;of&nbsp;the&nbsp;gesture<br>
+&nbsp;&nbsp;top_start_coord:&nbsp;The&nbsp;vertical&nbsp;starting&nbsp;coordinate&nbsp;of&nbsp;the&nbsp;gesture<br>
+&nbsp;&nbsp;left_end_coord:&nbsp;The&nbsp;horizontal&nbsp;ending&nbsp;coordinate&nbsp;of&nbsp;the&nbsp;gesture<br>
+&nbsp;&nbsp;top_end_coord:&nbsp;The&nbsp;vertical&nbsp;ending&nbsp;coordinate&nbsp;of&nbsp;the&nbsp;gesture<br>
+&nbsp;&nbsp;duration:&nbsp;The&nbsp;length&nbsp;of&nbsp;time&nbsp;of&nbsp;the&nbsp;swipe&nbsp;in&nbsp;milliseconds</tt></dd></dl>
+
+<dl><dt><a name="AndroidActionRunner-InputTap"><strong>InputTap</strong></a>(self, x_coord, y_coord)</dt><dd><tt>Perform&nbsp;a&nbsp;tap&nbsp;input&nbsp;at&nbsp;the&nbsp;given&nbsp;coordinates.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;x_coord:&nbsp;The&nbsp;x&nbsp;coordinate&nbsp;of&nbsp;the&nbsp;tap&nbsp;event.<br>
+&nbsp;&nbsp;y_coord:&nbsp;The&nbsp;y&nbsp;coordinate&nbsp;of&nbsp;the&nbsp;tap&nbsp;event.</tt></dd></dl>
+
+<dl><dt><a name="AndroidActionRunner-InputText"><strong>InputText</strong></a>(self, string)</dt><dd><tt>Convert&nbsp;the&nbsp;characters&nbsp;of&nbsp;the&nbsp;string&nbsp;into&nbsp;key&nbsp;events&nbsp;and&nbsp;send&nbsp;to&nbsp;device.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;string:&nbsp;The&nbsp;string&nbsp;to&nbsp;send&nbsp;to&nbsp;the&nbsp;device.</tt></dd></dl>
+
+<dl><dt><a name="AndroidActionRunner-SmoothScrollBy"><strong>SmoothScrollBy</strong></a>(self, left_start_coord, top_start_coord, direction, scroll_distance)</dt><dd><tt>Perfrom&nbsp;gesture&nbsp;to&nbsp;scroll&nbsp;down&nbsp;on&nbsp;the&nbsp;android&nbsp;device.</tt></dd></dl>
+
+<dl><dt><a name="AndroidActionRunner-TurnScreenOff"><strong>TurnScreenOff</strong></a>(self)</dt><dd><tt>If&nbsp;device&nbsp;screen&nbsp;is&nbsp;on,&nbsp;turn&nbsp;screen&nbsp;off.<br>
+If&nbsp;the&nbsp;screen&nbsp;is&nbsp;already&nbsp;off,&nbsp;log&nbsp;a&nbsp;warning&nbsp;and&nbsp;return&nbsp;immediately.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;Timeout:&nbsp;If&nbsp;the&nbsp;screen&nbsp;is&nbsp;on&nbsp;and&nbsp;device&nbsp;fails&nbsp;to&nbsp;turn&nbsp;screen&nbsp;off.</tt></dd></dl>
+
+<dl><dt><a name="AndroidActionRunner-TurnScreenOn"><strong>TurnScreenOn</strong></a>(self)</dt><dd><tt>If&nbsp;device&nbsp;screen&nbsp;is&nbsp;off,&nbsp;turn&nbsp;screen&nbsp;on.<br>
+If&nbsp;the&nbsp;screen&nbsp;is&nbsp;already&nbsp;on,&nbsp;log&nbsp;a&nbsp;warning&nbsp;and&nbsp;return&nbsp;immediately.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;Timeout:&nbsp;If&nbsp;the&nbsp;screen&nbsp;is&nbsp;off&nbsp;and&nbsp;device&nbsp;fails&nbsp;to&nbsp;turn&nbsp;screen&nbsp;on.</tt></dd></dl>
+
+<dl><dt><a name="AndroidActionRunner-UnlockScreen"><strong>UnlockScreen</strong></a>(self)</dt><dd><tt>If&nbsp;device&nbsp;screen&nbsp;is&nbsp;locked,&nbsp;unlocks&nbsp;it.<br>
+If&nbsp;the&nbsp;device&nbsp;is&nbsp;not&nbsp;locked,&nbsp;log&nbsp;a&nbsp;warning&nbsp;and&nbsp;return&nbsp;immediately.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;Timeout:&nbsp;If&nbsp;device&nbsp;fails&nbsp;to&nbsp;unlock&nbsp;screen.</tt></dd></dl>
+
+<dl><dt><a name="AndroidActionRunner-Wait"><strong>Wait</strong></a>(self, seconds)</dt><dd><tt>Wait&nbsp;for&nbsp;the&nbsp;number&nbsp;of&nbsp;seconds&nbsp;specified.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;seconds:&nbsp;The&nbsp;number&nbsp;of&nbsp;seconds&nbsp;to&nbsp;wait.</tt></dd></dl>
+
+<dl><dt><a name="AndroidActionRunner-__init__"><strong>__init__</strong></a>(self, platform_backend)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.core.android_platform.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.core.android_platform.html
new file mode 100644
index 0000000..8a56589
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.core.android_platform.html
@@ -0,0 +1,275 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.core.android_platform</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.core.html"><font color="#ffffff">core</font></a>.android_platform</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/core/android_platform.py">telemetry/core/android_platform.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.core.android_action_runner.html">telemetry.core.android_action_runner</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.app.android_app.html">telemetry.internal.app.android_app</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.backends.android_app_backend.html">telemetry.internal.backends.android_app_backend</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.platform.html">telemetry.core.platform</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.core.platform.html#Platform">telemetry.core.platform.Platform</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.core.android_platform.html#AndroidPlatform">AndroidPlatform</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="AndroidPlatform">class <strong>AndroidPlatform</strong></a>(<a href="telemetry.core.platform.html#Platform">telemetry.core.platform.Platform</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.core.android_platform.html#AndroidPlatform">AndroidPlatform</a></dd>
+<dd><a href="telemetry.core.platform.html#Platform">telemetry.core.platform.Platform</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="AndroidPlatform-LaunchAndroidApplication"><strong>LaunchAndroidApplication</strong></a>(self, start_intent, is_app_ready_predicate<font color="#909090">=None</font>, app_has_webviews<font color="#909090">=True</font>)</dt><dd><tt>Launches&nbsp;an&nbsp;Android&nbsp;application&nbsp;given&nbsp;the&nbsp;intent.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;start_intent:&nbsp;The&nbsp;intent&nbsp;to&nbsp;use&nbsp;to&nbsp;start&nbsp;the&nbsp;app.<br>
+&nbsp;&nbsp;is_app_ready_predicate:&nbsp;A&nbsp;predicate&nbsp;function&nbsp;to&nbsp;determine<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;whether&nbsp;the&nbsp;app&nbsp;is&nbsp;ready.&nbsp;This&nbsp;is&nbsp;a&nbsp;function&nbsp;that&nbsp;takes&nbsp;an<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;AndroidApp&nbsp;instance&nbsp;and&nbsp;return&nbsp;a&nbsp;boolean.&nbsp;When&nbsp;it&nbsp;is&nbsp;not&nbsp;passed&nbsp;in,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;the&nbsp;app&nbsp;is&nbsp;ready&nbsp;when&nbsp;the&nbsp;intent&nbsp;to&nbsp;launch&nbsp;it&nbsp;is&nbsp;completed.<br>
+&nbsp;&nbsp;app_has_webviews:&nbsp;A&nbsp;boolean&nbsp;indicating&nbsp;whether&nbsp;the&nbsp;app&nbsp;is&nbsp;expected&nbsp;to<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;contain&nbsp;any&nbsp;WebViews.&nbsp;If&nbsp;True,&nbsp;the&nbsp;app&nbsp;will&nbsp;be&nbsp;launched&nbsp;with<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;appropriate&nbsp;webview&nbsp;flags,&nbsp;and&nbsp;the&nbsp;GetWebViews&nbsp;method&nbsp;of&nbsp;the&nbsp;returned<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;object&nbsp;may&nbsp;be&nbsp;used&nbsp;to&nbsp;access&nbsp;them.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;A&nbsp;reference&nbsp;to&nbsp;the&nbsp;android_app&nbsp;launched.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatform-__init__"><strong>__init__</strong></a>(self, platform_backend)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>android_action_runner</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.core.platform.html#Platform">telemetry.core.platform.Platform</a>:<br>
+<dl><dt><a name="AndroidPlatform-CanCaptureVideo"><strong>CanCaptureVideo</strong></a>(self)</dt><dd><tt>Returns&nbsp;a&nbsp;bool&nbsp;indicating&nbsp;whether&nbsp;the&nbsp;platform&nbsp;supports&nbsp;video&nbsp;capture.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatform-CanFlushIndividualFilesFromSystemCache"><strong>CanFlushIndividualFilesFromSystemCache</strong></a>(self)</dt><dd><tt>Returns&nbsp;true&nbsp;if&nbsp;the&nbsp;disk&nbsp;cache&nbsp;can&nbsp;be&nbsp;flushed&nbsp;for&nbsp;specific&nbsp;files.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatform-CanLaunchApplication"><strong>CanLaunchApplication</strong></a>(self, application)</dt><dd><tt>Returns&nbsp;whether&nbsp;the&nbsp;platform&nbsp;can&nbsp;launch&nbsp;the&nbsp;given&nbsp;application.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatform-CanMeasurePerApplicationPower"><strong>CanMeasurePerApplicationPower</strong></a>(self)</dt><dd><tt>Returns&nbsp;True&nbsp;if&nbsp;the&nbsp;power&nbsp;monitor&nbsp;can&nbsp;measure&nbsp;power&nbsp;for&nbsp;the&nbsp;target<br>
+application&nbsp;in&nbsp;isolation.&nbsp;False&nbsp;if&nbsp;power&nbsp;measurement&nbsp;is&nbsp;for&nbsp;full&nbsp;system<br>
+energy&nbsp;consumption.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatform-CanMonitorNetworkData"><strong>CanMonitorNetworkData</strong></a>(self)</dt><dd><tt>Returns&nbsp;true&nbsp;if&nbsp;network&nbsp;data&nbsp;can&nbsp;be&nbsp;retrieved,&nbsp;false&nbsp;otherwise.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatform-CanMonitorPower"><strong>CanMonitorPower</strong></a>(self)</dt><dd><tt>Returns&nbsp;True&nbsp;iff&nbsp;power&nbsp;can&nbsp;be&nbsp;monitored&nbsp;asynchronously&nbsp;via<br>
+<a href="#AndroidPlatform-StartMonitoringPower">StartMonitoringPower</a>()&nbsp;and&nbsp;<a href="#AndroidPlatform-StopMonitoringPower">StopMonitoringPower</a>().</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatform-CanMonitorThermalThrottling"><strong>CanMonitorThermalThrottling</strong></a>(self)</dt><dd><tt>Platforms&nbsp;may&nbsp;be&nbsp;able&nbsp;to&nbsp;detect&nbsp;thermal&nbsp;throttling.<br>
+&nbsp;<br>
+Some&nbsp;fan-less&nbsp;computers&nbsp;go&nbsp;into&nbsp;a&nbsp;reduced&nbsp;performance&nbsp;mode&nbsp;when&nbsp;their&nbsp;heat<br>
+exceeds&nbsp;a&nbsp;certain&nbsp;threshold.&nbsp;Performance&nbsp;tests&nbsp;in&nbsp;particular&nbsp;should&nbsp;use&nbsp;this<br>
+API&nbsp;to&nbsp;detect&nbsp;if&nbsp;this&nbsp;has&nbsp;happened&nbsp;and&nbsp;interpret&nbsp;results&nbsp;accordingly.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatform-CanTakeScreenshot"><strong>CanTakeScreenshot</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidPlatform-CooperativelyShutdown"><strong>CooperativelyShutdown</strong></a>(self, proc, app_name)</dt><dd><tt>Cooperatively&nbsp;shut&nbsp;down&nbsp;the&nbsp;given&nbsp;process&nbsp;from&nbsp;subprocess.Popen.<br>
+&nbsp;<br>
+Currently&nbsp;this&nbsp;is&nbsp;only&nbsp;implemented&nbsp;on&nbsp;Windows.&nbsp;See<br>
+crbug.com/424024&nbsp;for&nbsp;background&nbsp;on&nbsp;why&nbsp;it&nbsp;was&nbsp;added.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;proc:&nbsp;a&nbsp;process&nbsp;object&nbsp;returned&nbsp;from&nbsp;subprocess.Popen.<br>
+&nbsp;&nbsp;app_name:&nbsp;on&nbsp;Windows,&nbsp;is&nbsp;the&nbsp;prefix&nbsp;of&nbsp;the&nbsp;application's&nbsp;window<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class&nbsp;name&nbsp;that&nbsp;should&nbsp;be&nbsp;searched&nbsp;for.&nbsp;This&nbsp;helps&nbsp;ensure<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;that&nbsp;only&nbsp;the&nbsp;application's&nbsp;windows&nbsp;are&nbsp;closed.<br>
+&nbsp;<br>
+Returns&nbsp;True&nbsp;if&nbsp;it&nbsp;is&nbsp;believed&nbsp;the&nbsp;attempt&nbsp;succeeded.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatform-FlushDnsCache"><strong>FlushDnsCache</strong></a>(self)</dt><dd><tt>Flushes&nbsp;the&nbsp;OS's&nbsp;DNS&nbsp;cache&nbsp;completely.<br>
+&nbsp;<br>
+This&nbsp;function&nbsp;may&nbsp;require&nbsp;root&nbsp;or&nbsp;administrator&nbsp;access.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatform-FlushEntireSystemCache"><strong>FlushEntireSystemCache</strong></a>(self)</dt><dd><tt>Flushes&nbsp;the&nbsp;OS's&nbsp;file&nbsp;cache&nbsp;completely.<br>
+&nbsp;<br>
+This&nbsp;function&nbsp;may&nbsp;require&nbsp;root&nbsp;or&nbsp;administrator&nbsp;access.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatform-FlushSystemCacheForDirectory"><strong>FlushSystemCacheForDirectory</strong></a>(self, directory)</dt><dd><tt>Flushes&nbsp;the&nbsp;OS's&nbsp;file&nbsp;cache&nbsp;for&nbsp;the&nbsp;specified&nbsp;directory.<br>
+&nbsp;<br>
+This&nbsp;function&nbsp;does&nbsp;not&nbsp;require&nbsp;root&nbsp;or&nbsp;administrator&nbsp;access.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatform-GetArchName"><strong>GetArchName</strong></a>(self)</dt><dd><tt>Returns&nbsp;a&nbsp;string&nbsp;description&nbsp;of&nbsp;the&nbsp;<a href="telemetry.core.platform.html#Platform">Platform</a>&nbsp;architecture.<br>
+&nbsp;<br>
+Examples:&nbsp;x86_64&nbsp;(posix),&nbsp;AMD64&nbsp;(win),&nbsp;armeabi-v7a,&nbsp;x86</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatform-GetDeviceTypeName"><strong>GetDeviceTypeName</strong></a>(self)</dt><dd><tt>Returns&nbsp;a&nbsp;string&nbsp;description&nbsp;of&nbsp;the&nbsp;<a href="telemetry.core.platform.html#Platform">Platform</a>&nbsp;device,&nbsp;or&nbsp;None.<br>
+&nbsp;<br>
+Examples:&nbsp;Nexus&nbsp;7,&nbsp;Nexus&nbsp;6,&nbsp;Desktop</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatform-GetNetworkData"><strong>GetNetworkData</strong></a>(self, browser)</dt><dd><tt>Get&nbsp;current&nbsp;network&nbsp;data.<br>
+Returns:<br>
+&nbsp;&nbsp;Tuple&nbsp;of&nbsp;(sent_data,&nbsp;received_data)&nbsp;in&nbsp;kb&nbsp;if&nbsp;data&nbsp;can&nbsp;be&nbsp;found,<br>
+&nbsp;&nbsp;None&nbsp;otherwise.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatform-GetOSName"><strong>GetOSName</strong></a>(self)</dt><dd><tt>Returns&nbsp;a&nbsp;string&nbsp;description&nbsp;of&nbsp;the&nbsp;<a href="telemetry.core.platform.html#Platform">Platform</a>&nbsp;OS.<br>
+&nbsp;<br>
+Examples:&nbsp;WIN,&nbsp;MAC,&nbsp;LINUX,&nbsp;CHROMEOS</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatform-GetOSVersionName"><strong>GetOSVersionName</strong></a>(self)</dt><dd><tt>Returns&nbsp;a&nbsp;logically&nbsp;sortable,&nbsp;string-like&nbsp;description&nbsp;of&nbsp;the&nbsp;<a href="telemetry.core.platform.html#Platform">Platform</a>&nbsp;OS<br>
+version.<br>
+&nbsp;<br>
+Examples:&nbsp;VISTA,&nbsp;WIN7,&nbsp;LION,&nbsp;MOUNTAINLION</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatform-GetOSVersionNumber"><strong>GetOSVersionNumber</strong></a>(self)</dt><dd><tt>Returns&nbsp;an&nbsp;integer&nbsp;description&nbsp;of&nbsp;the&nbsp;<a href="telemetry.core.platform.html#Platform">Platform</a>&nbsp;OS&nbsp;major&nbsp;version.<br>
+&nbsp;<br>
+Examples:&nbsp;On&nbsp;Mac,&nbsp;13&nbsp;for&nbsp;Mavericks,&nbsp;14&nbsp;for&nbsp;Yosemite.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatform-HasBeenThermallyThrottled"><strong>HasBeenThermallyThrottled</strong></a>(self)</dt><dd><tt>Returns&nbsp;True&nbsp;if&nbsp;the&nbsp;device&nbsp;has&nbsp;been&nbsp;thermally&nbsp;throttled.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatform-InstallApplication"><strong>InstallApplication</strong></a>(self, application)</dt><dd><tt>Installs&nbsp;the&nbsp;given&nbsp;application.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatform-IsApplicationRunning"><strong>IsApplicationRunning</strong></a>(self, application)</dt><dd><tt>Returns&nbsp;whether&nbsp;an&nbsp;application&nbsp;is&nbsp;currently&nbsp;running.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatform-IsCooperativeShutdownSupported"><strong>IsCooperativeShutdownSupported</strong></a>(self)</dt><dd><tt>Indicates&nbsp;whether&nbsp;CooperativelyShutdown,&nbsp;below,&nbsp;is&nbsp;supported.<br>
+It&nbsp;is&nbsp;not&nbsp;necessary&nbsp;to&nbsp;implement&nbsp;it&nbsp;on&nbsp;all&nbsp;platforms.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatform-IsMonitoringPower"><strong>IsMonitoringPower</strong></a>(self)</dt><dd><tt>Returns&nbsp;true&nbsp;if&nbsp;power&nbsp;is&nbsp;currently&nbsp;being&nbsp;monitored,&nbsp;false&nbsp;otherwise.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatform-IsThermallyThrottled"><strong>IsThermallyThrottled</strong></a>(self)</dt><dd><tt>Returns&nbsp;True&nbsp;if&nbsp;the&nbsp;device&nbsp;is&nbsp;currently&nbsp;thermally&nbsp;throttled.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatform-LaunchApplication"><strong>LaunchApplication</strong></a>(self, application, parameters<font color="#909090">=None</font>, elevate_privilege<font color="#909090">=False</font>)</dt><dd><tt>"Launches&nbsp;the&nbsp;given&nbsp;|application|&nbsp;with&nbsp;a&nbsp;list&nbsp;of&nbsp;|parameters|&nbsp;on&nbsp;the&nbsp;OS.<br>
+&nbsp;<br>
+Set&nbsp;|elevate_privilege|&nbsp;to&nbsp;launch&nbsp;the&nbsp;application&nbsp;with&nbsp;root&nbsp;or&nbsp;admin&nbsp;rights.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;A&nbsp;popen&nbsp;style&nbsp;process&nbsp;handle&nbsp;for&nbsp;host&nbsp;platforms.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatform-SetHTTPServerDirectories"><strong>SetHTTPServerDirectories</strong></a>(self, paths)</dt><dd><tt>Returns&nbsp;True&nbsp;if&nbsp;the&nbsp;HTTP&nbsp;server&nbsp;was&nbsp;started,&nbsp;False&nbsp;otherwise.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatform-StartLocalServer"><strong>StartLocalServer</strong></a>(self, server)</dt><dd><tt>Starts&nbsp;a&nbsp;LocalServer&nbsp;and&nbsp;associates&nbsp;it&nbsp;with&nbsp;this&nbsp;platform.<br>
+|server.Close()|&nbsp;should&nbsp;be&nbsp;called&nbsp;manually&nbsp;to&nbsp;close&nbsp;the&nbsp;started&nbsp;server.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatform-StartMonitoringPower"><strong>StartMonitoringPower</strong></a>(self, browser)</dt><dd><tt>Starts&nbsp;monitoring&nbsp;power&nbsp;utilization&nbsp;statistics.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;browser:&nbsp;The&nbsp;browser&nbsp;to&nbsp;monitor.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatform-StartVideoCapture"><strong>StartVideoCapture</strong></a>(self, min_bitrate_mbps)</dt><dd><tt>Starts&nbsp;capturing&nbsp;video.<br>
+&nbsp;<br>
+Outer&nbsp;framing&nbsp;may&nbsp;be&nbsp;included&nbsp;(from&nbsp;the&nbsp;OS,&nbsp;browser&nbsp;window,&nbsp;and&nbsp;webcam).<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;min_bitrate_mbps:&nbsp;The&nbsp;minimum&nbsp;capture&nbsp;bitrate&nbsp;in&nbsp;MegaBits&nbsp;Per&nbsp;Second.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The&nbsp;platform&nbsp;is&nbsp;free&nbsp;to&nbsp;deliver&nbsp;a&nbsp;higher&nbsp;bitrate&nbsp;if&nbsp;it&nbsp;can&nbsp;do&nbsp;so<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;without&nbsp;increasing&nbsp;overhead.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;ValueError&nbsp;if&nbsp;the&nbsp;required&nbsp;|min_bitrate_mbps|&nbsp;can't&nbsp;be&nbsp;achieved.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatform-StopAllLocalServers"><strong>StopAllLocalServers</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidPlatform-StopMonitoringPower"><strong>StopMonitoringPower</strong></a>(self)</dt><dd><tt>Stops&nbsp;monitoring&nbsp;power&nbsp;utilization&nbsp;and&nbsp;returns&nbsp;stats<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;None&nbsp;if&nbsp;power&nbsp;measurement&nbsp;failed&nbsp;for&nbsp;some&nbsp;reason,&nbsp;otherwise&nbsp;a&nbsp;dict&nbsp;of<br>
+&nbsp;&nbsp;power&nbsp;utilization&nbsp;statistics&nbsp;containing:&nbsp;{<br>
+&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;An&nbsp;identifier&nbsp;for&nbsp;the&nbsp;data&nbsp;provider.&nbsp;Allows&nbsp;to&nbsp;evaluate&nbsp;the&nbsp;precision<br>
+&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;of&nbsp;the&nbsp;data.&nbsp;Example&nbsp;values:&nbsp;monsoon,&nbsp;powermetrics,&nbsp;ds2784<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'identifier':&nbsp;identifier,<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;The&nbsp;instantaneous&nbsp;power&nbsp;(voltage&nbsp;*&nbsp;current)&nbsp;reading&nbsp;in&nbsp;milliwatts&nbsp;at<br>
+&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;each&nbsp;sample.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'power_samples_mw':&nbsp;&nbsp;[mw0,&nbsp;mw1,&nbsp;...,&nbsp;mwN],<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;The&nbsp;full&nbsp;system&nbsp;energy&nbsp;consumption&nbsp;during&nbsp;the&nbsp;sampling&nbsp;period&nbsp;in<br>
+&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;milliwatt&nbsp;hours.&nbsp;May&nbsp;be&nbsp;estimated&nbsp;by&nbsp;integrating&nbsp;power&nbsp;samples&nbsp;or&nbsp;may<br>
+&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;be&nbsp;exact&nbsp;on&nbsp;supported&nbsp;hardware.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'energy_consumption_mwh':&nbsp;mwh,<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;The&nbsp;target&nbsp;application's&nbsp;energy&nbsp;consumption&nbsp;during&nbsp;the&nbsp;sampling&nbsp;period<br>
+&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;in&nbsp;milliwatt&nbsp;hours.&nbsp;Should&nbsp;be&nbsp;returned&nbsp;iff<br>
+&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;<a href="#AndroidPlatform-CanMeasurePerApplicationPower">CanMeasurePerApplicationPower</a>()&nbsp;return&nbsp;true.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'application_energy_consumption_mwh':&nbsp;mwh,<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;A&nbsp;platform-specific&nbsp;dictionary&nbsp;of&nbsp;additional&nbsp;details&nbsp;about&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;utilization&nbsp;of&nbsp;individual&nbsp;hardware&nbsp;components.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;component_utilization:&nbsp;{<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;...<br>
+&nbsp;&nbsp;&nbsp;&nbsp;}<br>
+&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;<a href="telemetry.core.platform.html#Platform">Platform</a>-specific&nbsp;data&nbsp;not&nbsp;attributed&nbsp;to&nbsp;any&nbsp;particular&nbsp;hardware<br>
+&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;component.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;platform_info:&nbsp;{<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;Device-specific&nbsp;onboard&nbsp;temperature&nbsp;sensor.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'average_temperature_c':&nbsp;c,<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;...<br>
+&nbsp;&nbsp;&nbsp;&nbsp;}<br>
+&nbsp;<br>
+&nbsp;&nbsp;}</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatform-StopVideoCapture"><strong>StopVideoCapture</strong></a>(self)</dt><dd><tt>Stops&nbsp;capturing&nbsp;video.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;A&nbsp;telemetry.core.video.Video&nbsp;object.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatform-TakeScreenshot"><strong>TakeScreenshot</strong></a>(self, file_path)</dt><dd><tt>Takes&nbsp;a&nbsp;screenshot&nbsp;of&nbsp;the&nbsp;platform&nbsp;and&nbsp;save&nbsp;to&nbsp;|file_path|.<br>
+&nbsp;<br>
+Note&nbsp;that&nbsp;this&nbsp;method&nbsp;may&nbsp;not&nbsp;be&nbsp;supported&nbsp;on&nbsp;all&nbsp;platform,&nbsp;so&nbsp;check&nbsp;with<br>
+CanTakeScreenshot&nbsp;before&nbsp;calling&nbsp;this.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;file_path:&nbsp;Where&nbsp;to&nbsp;save&nbsp;the&nbsp;screenshot&nbsp;to.&nbsp;If&nbsp;the&nbsp;platform&nbsp;is&nbsp;remote,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;|file_path|&nbsp;is&nbsp;the&nbsp;path&nbsp;on&nbsp;the&nbsp;host&nbsp;platform.<br>
+&nbsp;<br>
+Returns&nbsp;True&nbsp;if&nbsp;it&nbsp;is&nbsp;believed&nbsp;the&nbsp;attempt&nbsp;succeeded.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.core.platform.html#Platform">telemetry.core.platform.Platform</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>http_server</strong></dt>
+</dl>
+<dl><dt><strong>is_host_platform</strong></dt>
+</dl>
+<dl><dt><strong>local_servers</strong></dt>
+<dd><tt>Returns&nbsp;the&nbsp;currently&nbsp;running&nbsp;local&nbsp;servers.</tt></dd>
+</dl>
+<dl><dt><strong>network_controller</strong></dt>
+<dd><tt>Control&nbsp;network&nbsp;settings&nbsp;and&nbsp;servers&nbsp;to&nbsp;simulate&nbsp;the&nbsp;Web.</tt></dd>
+</dl>
+<dl><dt><strong>tracing_controller</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.core.cros_interface.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.core.cros_interface.html
new file mode 100644
index 0000000..1baf490
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.core.cros_interface.html
@@ -0,0 +1,355 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.core.cros_interface</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.core.html"><font color="#ffffff">core</font></a>.cros_interface</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/core/cros_interface.py">telemetry/core/cros_interface.py</a></font></td></tr></table>
+    <p><tt>A&nbsp;wrapper&nbsp;around&nbsp;ssh&nbsp;for&nbsp;common&nbsp;operations&nbsp;on&nbsp;a&nbsp;CrOS-based&nbsp;device</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="logging.html">logging</a><br>
+<a href="os.html">os</a><br>
+</td><td width="25%" valign=top><a href="re.html">re</a><br>
+<a href="shutil.html">shutil</a><br>
+</td><td width="25%" valign=top><a href="stat.html">stat</a><br>
+<a href="subprocess.html">subprocess</a><br>
+</td><td width="25%" valign=top><a href="tempfile.html">tempfile</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.core.cros_interface.html#CrOSInterface">CrOSInterface</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="exceptions.html#Exception">exceptions.Exception</a>(<a href="exceptions.html#BaseException">exceptions.BaseException</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.core.cros_interface.html#LoginException">LoginException</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.core.cros_interface.html#DNSFailureException">DNSFailureException</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.core.cros_interface.html#KeylessLoginRequiredException">KeylessLoginRequiredException</a>
+</font></dt></dl>
+</dd>
+</dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="CrOSInterface">class <strong>CrOSInterface</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="CrOSInterface-Chown"><strong>Chown</strong></a>(self, filename)</dt></dl>
+
+<dl><dt><a name="CrOSInterface-CloseConnection"><strong>CloseConnection</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrOSInterface-CryptohomePath"><strong>CryptohomePath</strong></a>(self, user)</dt><dd><tt>Returns&nbsp;the&nbsp;cryptohome&nbsp;mount&nbsp;point&nbsp;for&nbsp;|user|.</tt></dd></dl>
+
+<dl><dt><a name="CrOSInterface-FileExistsOnDevice"><strong>FileExistsOnDevice</strong></a>(self, file_name)</dt></dl>
+
+<dl><dt><a name="CrOSInterface-FilesystemMountedAt"><strong>FilesystemMountedAt</strong></a>(self, path)</dt><dd><tt>Returns&nbsp;the&nbsp;filesystem&nbsp;mounted&nbsp;at&nbsp;|path|</tt></dd></dl>
+
+<dl><dt><a name="CrOSInterface-FormSSHCommandLine"><strong>FormSSHCommandLine</strong></a>(self, args, extra_ssh_args<font color="#909090">=None</font>)</dt><dd><tt>Constructs&nbsp;a&nbsp;subprocess-suitable&nbsp;command&nbsp;line&nbsp;for&nbsp;`ssh'.</tt></dd></dl>
+
+<dl><dt><a name="CrOSInterface-GetChromePid"><strong>GetChromePid</strong></a>(self)</dt><dd><tt>Returns&nbsp;pid&nbsp;of&nbsp;main&nbsp;chrome&nbsp;browser&nbsp;process.</tt></dd></dl>
+
+<dl><dt><a name="CrOSInterface-GetChromeProcess"><strong>GetChromeProcess</strong></a>(self)</dt><dd><tt>Locates&nbsp;the&nbsp;the&nbsp;main&nbsp;chrome&nbsp;browser&nbsp;process.<br>
+&nbsp;<br>
+Chrome&nbsp;on&nbsp;cros&nbsp;is&nbsp;usually&nbsp;in&nbsp;/opt/google/chrome,&nbsp;but&nbsp;could&nbsp;be&nbsp;in<br>
+/usr/local/&nbsp;for&nbsp;developer&nbsp;workflows&nbsp;-&nbsp;debug&nbsp;chrome&nbsp;is&nbsp;too&nbsp;large&nbsp;to&nbsp;fit&nbsp;on<br>
+rootfs.<br>
+&nbsp;<br>
+Chrome&nbsp;spawns&nbsp;multiple&nbsp;processes&nbsp;for&nbsp;renderers.&nbsp;pids&nbsp;wrap&nbsp;around&nbsp;after&nbsp;they<br>
+are&nbsp;exhausted&nbsp;so&nbsp;looking&nbsp;for&nbsp;the&nbsp;smallest&nbsp;pid&nbsp;is&nbsp;not&nbsp;always&nbsp;correct.&nbsp;We<br>
+locate&nbsp;the&nbsp;session_manager's&nbsp;pid,&nbsp;and&nbsp;look&nbsp;for&nbsp;the&nbsp;chrome&nbsp;process&nbsp;that's&nbsp;an<br>
+immediate&nbsp;child.&nbsp;This&nbsp;is&nbsp;the&nbsp;main&nbsp;browser&nbsp;process.</tt></dd></dl>
+
+<dl><dt><a name="CrOSInterface-GetFile"><strong>GetFile</strong></a>(self, filename, destfile<font color="#909090">=None</font>)</dt><dd><tt>Copies&nbsp;a&nbsp;local&nbsp;file&nbsp;|filename|&nbsp;to&nbsp;|destfile|&nbsp;on&nbsp;the&nbsp;device.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;filename:&nbsp;The&nbsp;name&nbsp;of&nbsp;the&nbsp;local&nbsp;source&nbsp;file.<br>
+&nbsp;&nbsp;destfile:&nbsp;The&nbsp;name&nbsp;of&nbsp;the&nbsp;file&nbsp;to&nbsp;copy&nbsp;to,&nbsp;and&nbsp;if&nbsp;it&nbsp;is&nbsp;not&nbsp;specified<br>
+&nbsp;&nbsp;&nbsp;&nbsp;then&nbsp;it&nbsp;is&nbsp;the&nbsp;basename&nbsp;of&nbsp;the&nbsp;source&nbsp;file.</tt></dd></dl>
+
+<dl><dt><a name="CrOSInterface-GetFileContents"><strong>GetFileContents</strong></a>(self, filename)</dt><dd><tt>Get&nbsp;the&nbsp;contents&nbsp;of&nbsp;a&nbsp;file&nbsp;on&nbsp;the&nbsp;device.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;filename:&nbsp;The&nbsp;name&nbsp;of&nbsp;the&nbsp;file&nbsp;on&nbsp;the&nbsp;device.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;A&nbsp;string&nbsp;containing&nbsp;the&nbsp;contents&nbsp;of&nbsp;the&nbsp;file.</tt></dd></dl>
+
+<dl><dt><a name="CrOSInterface-GetRemotePort"><strong>GetRemotePort</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrOSInterface-IsCryptohomeMounted"><strong>IsCryptohomeMounted</strong></a>(self, username, is_guest)</dt><dd><tt>Returns&nbsp;True&nbsp;iff&nbsp;|user|'s&nbsp;cryptohome&nbsp;is&nbsp;mounted.</tt></dd></dl>
+
+<dl><dt><a name="CrOSInterface-IsHTTPServerRunningOnPort"><strong>IsHTTPServerRunningOnPort</strong></a>(self, port)</dt></dl>
+
+<dl><dt><a name="CrOSInterface-IsServiceRunning"><strong>IsServiceRunning</strong></a>(self, service_name)</dt></dl>
+
+<dl><dt><a name="CrOSInterface-KillAllMatching"><strong>KillAllMatching</strong></a>(self, predicate)</dt></dl>
+
+<dl><dt><a name="CrOSInterface-ListProcesses"><strong>ListProcesses</strong></a>(self)</dt><dd><tt>Returns&nbsp;(pid,&nbsp;cmd,&nbsp;ppid,&nbsp;state)&nbsp;of&nbsp;all&nbsp;processes&nbsp;on&nbsp;the&nbsp;device.</tt></dd></dl>
+
+<dl><dt><a name="CrOSInterface-PushContents"><strong>PushContents</strong></a>(self, text, remote_filename)</dt></dl>
+
+<dl><dt><a name="CrOSInterface-PushFile"><strong>PushFile</strong></a>(self, filename, remote_filename)</dt></dl>
+
+<dl><dt><a name="CrOSInterface-RestartUI"><strong>RestartUI</strong></a>(self, clear_enterprise_policy)</dt></dl>
+
+<dl><dt><a name="CrOSInterface-RmRF"><strong>RmRF</strong></a>(self, filename)</dt></dl>
+
+<dl><dt><a name="CrOSInterface-RunCmdOnDevice"><strong>RunCmdOnDevice</strong></a>(self, args, cwd<font color="#909090">=None</font>, quiet<font color="#909090">=False</font>)</dt></dl>
+
+<dl><dt><a name="CrOSInterface-TakeScreenShot"><strong>TakeScreenShot</strong></a>(self, screenshot_prefix)</dt><dd><tt>Takes&nbsp;a&nbsp;screenshot,&nbsp;useful&nbsp;for&nbsp;debugging&nbsp;failures.</tt></dd></dl>
+
+<dl><dt><a name="CrOSInterface-TryLogin"><strong>TryLogin</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrOSInterface-__enter__"><strong>__enter__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrOSInterface-__exit__"><strong>__exit__</strong></a>(self, *args)</dt></dl>
+
+<dl><dt><a name="CrOSInterface-__init__"><strong>__init__</strong></a>(self, hostname<font color="#909090">=None</font>, ssh_port<font color="#909090">=None</font>, ssh_identity<font color="#909090">=None</font>)</dt><dd><tt>#&nbsp;pylint:&nbsp;disable=R0923</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>hostname</strong></dt>
+</dl>
+<dl><dt><strong>local</strong></dt>
+</dl>
+<dl><dt><strong>ssh_port</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="DNSFailureException">class <strong>DNSFailureException</strong></a>(<a href="telemetry.core.cros_interface.html#LoginException">LoginException</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.core.cros_interface.html#DNSFailureException">DNSFailureException</a></dd>
+<dd><a href="telemetry.core.cros_interface.html#LoginException">LoginException</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors inherited from <a href="telemetry.core.cros_interface.html#LoginException">LoginException</a>:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="DNSFailureException-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#DNSFailureException-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#DNSFailureException-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="DNSFailureException-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#DNSFailureException-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="DNSFailureException-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#DNSFailureException-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="DNSFailureException-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#DNSFailureException-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="DNSFailureException-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#DNSFailureException-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="DNSFailureException-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="DNSFailureException-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#DNSFailureException-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="DNSFailureException-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#DNSFailureException-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="DNSFailureException-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="DNSFailureException-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#DNSFailureException-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="DNSFailureException-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="KeylessLoginRequiredException">class <strong>KeylessLoginRequiredException</strong></a>(<a href="telemetry.core.cros_interface.html#LoginException">LoginException</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.core.cros_interface.html#KeylessLoginRequiredException">KeylessLoginRequiredException</a></dd>
+<dd><a href="telemetry.core.cros_interface.html#LoginException">LoginException</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors inherited from <a href="telemetry.core.cros_interface.html#LoginException">LoginException</a>:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="KeylessLoginRequiredException-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#KeylessLoginRequiredException-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#KeylessLoginRequiredException-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="KeylessLoginRequiredException-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#KeylessLoginRequiredException-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="KeylessLoginRequiredException-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#KeylessLoginRequiredException-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="KeylessLoginRequiredException-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#KeylessLoginRequiredException-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="KeylessLoginRequiredException-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#KeylessLoginRequiredException-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="KeylessLoginRequiredException-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="KeylessLoginRequiredException-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#KeylessLoginRequiredException-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="KeylessLoginRequiredException-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#KeylessLoginRequiredException-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="KeylessLoginRequiredException-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="KeylessLoginRequiredException-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#KeylessLoginRequiredException-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="KeylessLoginRequiredException-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="LoginException">class <strong>LoginException</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.core.cros_interface.html#LoginException">LoginException</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="LoginException-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#LoginException-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#LoginException-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="LoginException-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#LoginException-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="LoginException-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#LoginException-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="LoginException-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#LoginException-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="LoginException-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#LoginException-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="LoginException-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="LoginException-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#LoginException-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="LoginException-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#LoginException-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="LoginException-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="LoginException-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#LoginException-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="LoginException-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-GetAllCmdOutput"><strong>GetAllCmdOutput</strong></a>(args, cwd<font color="#909090">=None</font>, quiet<font color="#909090">=False</font>)</dt><dd><tt>Open&nbsp;a&nbsp;subprocess&nbsp;to&nbsp;execute&nbsp;a&nbsp;program&nbsp;and&nbsp;returns&nbsp;its&nbsp;output.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;args:&nbsp;A&nbsp;string&nbsp;or&nbsp;a&nbsp;sequence&nbsp;of&nbsp;program&nbsp;arguments.&nbsp;The&nbsp;program&nbsp;to&nbsp;execute&nbsp;is<br>
+&nbsp;&nbsp;&nbsp;&nbsp;the&nbsp;string&nbsp;or&nbsp;the&nbsp;first&nbsp;item&nbsp;in&nbsp;the&nbsp;args&nbsp;sequence.<br>
+&nbsp;&nbsp;cwd:&nbsp;If&nbsp;not&nbsp;None,&nbsp;the&nbsp;subprocess's&nbsp;current&nbsp;directory&nbsp;will&nbsp;be&nbsp;changed&nbsp;to<br>
+&nbsp;&nbsp;&nbsp;&nbsp;|cwd|&nbsp;before&nbsp;it's&nbsp;executed.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;Captures&nbsp;and&nbsp;returns&nbsp;the&nbsp;command's&nbsp;stdout.<br>
+&nbsp;&nbsp;Prints&nbsp;the&nbsp;command's&nbsp;stderr&nbsp;to&nbsp;logger&nbsp;(which&nbsp;defaults&nbsp;to&nbsp;stdout).</tt></dd></dl>
+ <dl><dt><a name="-HasSSH"><strong>HasSSH</strong></a>()</dt></dl>
+ <dl><dt><a name="-RunCmd"><strong>RunCmd</strong></a>(args, cwd<font color="#909090">=None</font>, quiet<font color="#909090">=False</font>)</dt><dd><tt>Opens&nbsp;a&nbsp;subprocess&nbsp;to&nbsp;execute&nbsp;a&nbsp;program&nbsp;and&nbsp;returns&nbsp;its&nbsp;return&nbsp;value.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;args:&nbsp;A&nbsp;string&nbsp;or&nbsp;a&nbsp;sequence&nbsp;of&nbsp;program&nbsp;arguments.&nbsp;The&nbsp;program&nbsp;to&nbsp;execute&nbsp;is<br>
+&nbsp;&nbsp;&nbsp;&nbsp;the&nbsp;string&nbsp;or&nbsp;the&nbsp;first&nbsp;item&nbsp;in&nbsp;the&nbsp;args&nbsp;sequence.<br>
+&nbsp;&nbsp;cwd:&nbsp;If&nbsp;not&nbsp;None,&nbsp;the&nbsp;subprocess's&nbsp;current&nbsp;directory&nbsp;will&nbsp;be&nbsp;changed&nbsp;to<br>
+&nbsp;&nbsp;&nbsp;&nbsp;|cwd|&nbsp;before&nbsp;it's&nbsp;executed.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;Return&nbsp;code&nbsp;from&nbsp;the&nbsp;command&nbsp;execution.</tt></dd></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.core.discover.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.core.discover.html
new file mode 100644
index 0000000..5912099
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.core.discover.html
@@ -0,0 +1,75 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.core.discover</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.core.html"><font color="#ffffff">core</font></a>.discover</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/core/discover.py">telemetry/core/discover.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2012&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.util.camel_case.html">telemetry.internal.util.camel_case</a><br>
+<a href="telemetry.internal.util.classes.html">telemetry.internal.util.classes</a><br>
+</td><td width="25%" valign=top><a href="telemetry.decorators.html">telemetry.decorators</a><br>
+<a href="fnmatch.html">fnmatch</a><br>
+</td><td width="25%" valign=top><a href="inspect.html">inspect</a><br>
+<a href="os.html">os</a><br>
+</td><td width="25%" valign=top><a href="re.html">re</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-DiscoverClasses"><strong>DiscoverClasses</strong></a>(*args, **kwargs)</dt><dd><tt>Discover&nbsp;all&nbsp;classes&nbsp;in&nbsp;|start_dir|&nbsp;which&nbsp;subclass&nbsp;|base_class|.<br>
+&nbsp;<br>
+Base&nbsp;classes&nbsp;that&nbsp;contain&nbsp;subclasses&nbsp;are&nbsp;ignored&nbsp;by&nbsp;default.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;start_dir:&nbsp;The&nbsp;directory&nbsp;to&nbsp;recursively&nbsp;search.<br>
+&nbsp;&nbsp;top_level_dir:&nbsp;The&nbsp;top&nbsp;level&nbsp;of&nbsp;the&nbsp;package,&nbsp;for&nbsp;importing.<br>
+&nbsp;&nbsp;base_class:&nbsp;The&nbsp;base&nbsp;class&nbsp;to&nbsp;search&nbsp;for.<br>
+&nbsp;&nbsp;pattern:&nbsp;Unix&nbsp;shell-style&nbsp;pattern&nbsp;for&nbsp;filtering&nbsp;the&nbsp;filenames&nbsp;to&nbsp;import.<br>
+&nbsp;&nbsp;index_by_class_name:&nbsp;If&nbsp;True,&nbsp;use&nbsp;class&nbsp;name&nbsp;converted&nbsp;to<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lowercase_with_underscores&nbsp;instead&nbsp;of&nbsp;module&nbsp;name&nbsp;in&nbsp;return&nbsp;dict&nbsp;keys.<br>
+&nbsp;&nbsp;directly_constructable:&nbsp;If&nbsp;True,&nbsp;will&nbsp;only&nbsp;return&nbsp;classes&nbsp;that&nbsp;can&nbsp;be<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;constructed&nbsp;without&nbsp;arguments<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;dict&nbsp;of&nbsp;{module_name:&nbsp;class}&nbsp;or&nbsp;{underscored_class_name:&nbsp;class}</tt></dd></dl>
+ <dl><dt><a name="-DiscoverClassesInModule"><strong>DiscoverClassesInModule</strong></a>(*args, **kwargs)</dt><dd><tt>Discover&nbsp;all&nbsp;classes&nbsp;in&nbsp;|module|&nbsp;which&nbsp;subclass&nbsp;|base_class|.<br>
+&nbsp;<br>
+Base&nbsp;classes&nbsp;that&nbsp;contain&nbsp;subclasses&nbsp;are&nbsp;ignored&nbsp;by&nbsp;default.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;module:&nbsp;The&nbsp;module&nbsp;to&nbsp;search.<br>
+&nbsp;&nbsp;base_class:&nbsp;The&nbsp;base&nbsp;class&nbsp;to&nbsp;search&nbsp;for.<br>
+&nbsp;&nbsp;index_by_class_name:&nbsp;If&nbsp;True,&nbsp;use&nbsp;class&nbsp;name&nbsp;converted&nbsp;to<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lowercase_with_underscores&nbsp;instead&nbsp;of&nbsp;module&nbsp;name&nbsp;in&nbsp;return&nbsp;dict&nbsp;keys.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;dict&nbsp;of&nbsp;{module_name:&nbsp;class}&nbsp;or&nbsp;{underscored_class_name:&nbsp;class}</tt></dd></dl>
+ <dl><dt><a name="-DiscoverModules"><strong>DiscoverModules</strong></a>(*args, **kwargs)</dt><dd><tt>Discover&nbsp;all&nbsp;modules&nbsp;in&nbsp;|start_dir|&nbsp;which&nbsp;match&nbsp;|pattern|.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;start_dir:&nbsp;The&nbsp;directory&nbsp;to&nbsp;recursively&nbsp;search.<br>
+&nbsp;&nbsp;top_level_dir:&nbsp;The&nbsp;top&nbsp;level&nbsp;of&nbsp;the&nbsp;package,&nbsp;for&nbsp;importing.<br>
+&nbsp;&nbsp;pattern:&nbsp;Unix&nbsp;shell-style&nbsp;pattern&nbsp;for&nbsp;filtering&nbsp;the&nbsp;filenames&nbsp;to&nbsp;import.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;list&nbsp;of&nbsp;modules.</tt></dd></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.core.exceptions.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.core.exceptions.html
new file mode 100644
index 0000000..744db81
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.core.exceptions.html
@@ -0,0 +1,1241 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.core.exceptions</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.core.html"><font color="#ffffff">core</font></a>.exceptions</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/core/exceptions.py">telemetry/core/exceptions.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="sys.html">sys</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="exceptions.html#Exception">exceptions.Exception</a>(<a href="exceptions.html#BaseException">exceptions.BaseException</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.core.exceptions.html#Error">Error</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.core.exceptions.html#AndroidDeviceParsingError">AndroidDeviceParsingError</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.core.exceptions.html#AppCrashException">AppCrashException</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.core.exceptions.html#BrowserGoneException">BrowserGoneException</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.core.exceptions.html#BrowserConnectionGoneException">BrowserConnectionGoneException</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="telemetry.core.exceptions.html#DevtoolsTargetCrashException">DevtoolsTargetCrashException</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="telemetry.core.exceptions.html#EvaluateException">EvaluateException</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.core.exceptions.html#InitializationError">InitializationError</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.core.exceptions.html#IntentionalException">IntentionalException</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.core.exceptions.html#LoginException">LoginException</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.core.exceptions.html#PackageDetectionError">PackageDetectionError</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.core.exceptions.html#PathMissingError">PathMissingError</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.core.exceptions.html#PlatformError">PlatformError</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.core.exceptions.html#ProcessGoneException">ProcessGoneException</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.core.exceptions.html#ProfilingException">ProfilingException</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.core.exceptions.html#TimeoutException">TimeoutException</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.core.exceptions.html#UnknownPackageError">UnknownPackageError</a>
+</font></dt></dl>
+</dd>
+</dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="AndroidDeviceParsingError">class <strong>AndroidDeviceParsingError</strong></a>(<a href="telemetry.core.exceptions.html#Error">Error</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Represents&nbsp;an&nbsp;error&nbsp;when&nbsp;parsing&nbsp;output&nbsp;from&nbsp;an&nbsp;android&nbsp;device<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.core.exceptions.html#AndroidDeviceParsingError">AndroidDeviceParsingError</a></dd>
+<dd><a href="telemetry.core.exceptions.html#Error">Error</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.core.exceptions.html#Error">Error</a>:<br>
+<dl><dt><a name="AndroidDeviceParsingError-AddDebuggingMessage"><strong>AddDebuggingMessage</strong></a>(self, msg)</dt><dd><tt>Adds&nbsp;a&nbsp;message&nbsp;to&nbsp;the&nbsp;description&nbsp;of&nbsp;the&nbsp;exception.<br>
+&nbsp;<br>
+Many&nbsp;Telemetry&nbsp;exceptions&nbsp;arise&nbsp;from&nbsp;failures&nbsp;in&nbsp;another&nbsp;application.&nbsp;These<br>
+failures&nbsp;are&nbsp;difficult&nbsp;to&nbsp;pinpoint.&nbsp;This&nbsp;method&nbsp;allows&nbsp;Telemetry&nbsp;classes&nbsp;to<br>
+append&nbsp;useful&nbsp;debugging&nbsp;information&nbsp;to&nbsp;the&nbsp;exception.&nbsp;This&nbsp;method&nbsp;also&nbsp;logs<br>
+information&nbsp;about&nbsp;the&nbsp;location&nbsp;from&nbsp;where&nbsp;it&nbsp;was&nbsp;called.</tt></dd></dl>
+
+<dl><dt><a name="AndroidDeviceParsingError-__init__"><strong>__init__</strong></a>(self, msg<font color="#909090">=''</font>)</dt></dl>
+
+<dl><dt><a name="AndroidDeviceParsingError-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.core.exceptions.html#Error">Error</a>:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#AndroidDeviceParsingError-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;object&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="AndroidDeviceParsingError-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#AndroidDeviceParsingError-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="AndroidDeviceParsingError-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#AndroidDeviceParsingError-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="AndroidDeviceParsingError-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#AndroidDeviceParsingError-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="AndroidDeviceParsingError-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#AndroidDeviceParsingError-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="AndroidDeviceParsingError-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="AndroidDeviceParsingError-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#AndroidDeviceParsingError-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="AndroidDeviceParsingError-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#AndroidDeviceParsingError-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="AndroidDeviceParsingError-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="AndroidDeviceParsingError-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="AppCrashException">class <strong>AppCrashException</strong></a>(<a href="telemetry.core.exceptions.html#Error">Error</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.core.exceptions.html#AppCrashException">AppCrashException</a></dd>
+<dd><a href="telemetry.core.exceptions.html#Error">Error</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="AppCrashException-__init__"><strong>__init__</strong></a>(self, app<font color="#909090">=None</font>, msg<font color="#909090">=''</font>)</dt></dl>
+
+<dl><dt><a name="AppCrashException-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.core.exceptions.html#Error">Error</a>:<br>
+<dl><dt><a name="AppCrashException-AddDebuggingMessage"><strong>AddDebuggingMessage</strong></a>(self, msg)</dt><dd><tt>Adds&nbsp;a&nbsp;message&nbsp;to&nbsp;the&nbsp;description&nbsp;of&nbsp;the&nbsp;exception.<br>
+&nbsp;<br>
+Many&nbsp;Telemetry&nbsp;exceptions&nbsp;arise&nbsp;from&nbsp;failures&nbsp;in&nbsp;another&nbsp;application.&nbsp;These<br>
+failures&nbsp;are&nbsp;difficult&nbsp;to&nbsp;pinpoint.&nbsp;This&nbsp;method&nbsp;allows&nbsp;Telemetry&nbsp;classes&nbsp;to<br>
+append&nbsp;useful&nbsp;debugging&nbsp;information&nbsp;to&nbsp;the&nbsp;exception.&nbsp;This&nbsp;method&nbsp;also&nbsp;logs<br>
+information&nbsp;about&nbsp;the&nbsp;location&nbsp;from&nbsp;where&nbsp;it&nbsp;was&nbsp;called.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.core.exceptions.html#Error">Error</a>:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#AppCrashException-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;object&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="AppCrashException-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#AppCrashException-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="AppCrashException-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#AppCrashException-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="AppCrashException-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#AppCrashException-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="AppCrashException-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#AppCrashException-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="AppCrashException-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="AppCrashException-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#AppCrashException-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="AppCrashException-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#AppCrashException-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="AppCrashException-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="AppCrashException-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="BrowserConnectionGoneException">class <strong>BrowserConnectionGoneException</strong></a>(<a href="telemetry.core.exceptions.html#BrowserGoneException">BrowserGoneException</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Represents&nbsp;a&nbsp;browser&nbsp;that&nbsp;still&nbsp;exists&nbsp;but&nbsp;cannot&nbsp;be&nbsp;reached.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.core.exceptions.html#BrowserConnectionGoneException">BrowserConnectionGoneException</a></dd>
+<dd><a href="telemetry.core.exceptions.html#BrowserGoneException">BrowserGoneException</a></dd>
+<dd><a href="telemetry.core.exceptions.html#AppCrashException">AppCrashException</a></dd>
+<dd><a href="telemetry.core.exceptions.html#Error">Error</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="BrowserConnectionGoneException-__init__"><strong>__init__</strong></a>(self, app, msg<font color="#909090">='Browser exists but the connection is gone'</font>)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.core.exceptions.html#AppCrashException">AppCrashException</a>:<br>
+<dl><dt><a name="BrowserConnectionGoneException-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.core.exceptions.html#Error">Error</a>:<br>
+<dl><dt><a name="BrowserConnectionGoneException-AddDebuggingMessage"><strong>AddDebuggingMessage</strong></a>(self, msg)</dt><dd><tt>Adds&nbsp;a&nbsp;message&nbsp;to&nbsp;the&nbsp;description&nbsp;of&nbsp;the&nbsp;exception.<br>
+&nbsp;<br>
+Many&nbsp;Telemetry&nbsp;exceptions&nbsp;arise&nbsp;from&nbsp;failures&nbsp;in&nbsp;another&nbsp;application.&nbsp;These<br>
+failures&nbsp;are&nbsp;difficult&nbsp;to&nbsp;pinpoint.&nbsp;This&nbsp;method&nbsp;allows&nbsp;Telemetry&nbsp;classes&nbsp;to<br>
+append&nbsp;useful&nbsp;debugging&nbsp;information&nbsp;to&nbsp;the&nbsp;exception.&nbsp;This&nbsp;method&nbsp;also&nbsp;logs<br>
+information&nbsp;about&nbsp;the&nbsp;location&nbsp;from&nbsp;where&nbsp;it&nbsp;was&nbsp;called.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.core.exceptions.html#Error">Error</a>:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#BrowserConnectionGoneException-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;object&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="BrowserConnectionGoneException-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#BrowserConnectionGoneException-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="BrowserConnectionGoneException-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#BrowserConnectionGoneException-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="BrowserConnectionGoneException-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#BrowserConnectionGoneException-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="BrowserConnectionGoneException-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#BrowserConnectionGoneException-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="BrowserConnectionGoneException-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="BrowserConnectionGoneException-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#BrowserConnectionGoneException-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="BrowserConnectionGoneException-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#BrowserConnectionGoneException-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="BrowserConnectionGoneException-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="BrowserConnectionGoneException-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="BrowserGoneException">class <strong>BrowserGoneException</strong></a>(<a href="telemetry.core.exceptions.html#AppCrashException">AppCrashException</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Represents&nbsp;a&nbsp;crash&nbsp;of&nbsp;the&nbsp;entire&nbsp;browser.<br>
+&nbsp;<br>
+In&nbsp;this&nbsp;state,&nbsp;all&nbsp;bets&nbsp;are&nbsp;pretty&nbsp;much&nbsp;off.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.core.exceptions.html#BrowserGoneException">BrowserGoneException</a></dd>
+<dd><a href="telemetry.core.exceptions.html#AppCrashException">AppCrashException</a></dd>
+<dd><a href="telemetry.core.exceptions.html#Error">Error</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="BrowserGoneException-__init__"><strong>__init__</strong></a>(self, app, msg<font color="#909090">='Browser crashed'</font>)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.core.exceptions.html#AppCrashException">AppCrashException</a>:<br>
+<dl><dt><a name="BrowserGoneException-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.core.exceptions.html#Error">Error</a>:<br>
+<dl><dt><a name="BrowserGoneException-AddDebuggingMessage"><strong>AddDebuggingMessage</strong></a>(self, msg)</dt><dd><tt>Adds&nbsp;a&nbsp;message&nbsp;to&nbsp;the&nbsp;description&nbsp;of&nbsp;the&nbsp;exception.<br>
+&nbsp;<br>
+Many&nbsp;Telemetry&nbsp;exceptions&nbsp;arise&nbsp;from&nbsp;failures&nbsp;in&nbsp;another&nbsp;application.&nbsp;These<br>
+failures&nbsp;are&nbsp;difficult&nbsp;to&nbsp;pinpoint.&nbsp;This&nbsp;method&nbsp;allows&nbsp;Telemetry&nbsp;classes&nbsp;to<br>
+append&nbsp;useful&nbsp;debugging&nbsp;information&nbsp;to&nbsp;the&nbsp;exception.&nbsp;This&nbsp;method&nbsp;also&nbsp;logs<br>
+information&nbsp;about&nbsp;the&nbsp;location&nbsp;from&nbsp;where&nbsp;it&nbsp;was&nbsp;called.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.core.exceptions.html#Error">Error</a>:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#BrowserGoneException-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;object&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="BrowserGoneException-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#BrowserGoneException-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="BrowserGoneException-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#BrowserGoneException-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="BrowserGoneException-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#BrowserGoneException-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="BrowserGoneException-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#BrowserGoneException-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="BrowserGoneException-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="BrowserGoneException-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#BrowserGoneException-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="BrowserGoneException-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#BrowserGoneException-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="BrowserGoneException-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="BrowserGoneException-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="DevtoolsTargetCrashException">class <strong>DevtoolsTargetCrashException</strong></a>(<a href="telemetry.core.exceptions.html#AppCrashException">AppCrashException</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Represents&nbsp;a&nbsp;crash&nbsp;of&nbsp;the&nbsp;current&nbsp;devtools&nbsp;target&nbsp;but&nbsp;not&nbsp;the&nbsp;overall&nbsp;app.<br>
+&nbsp;<br>
+This&nbsp;can&nbsp;be&nbsp;a&nbsp;tab&nbsp;or&nbsp;a&nbsp;WebView.&nbsp;In&nbsp;this&nbsp;state,&nbsp;the&nbsp;tab/WebView&nbsp;is<br>
+gone,&nbsp;but&nbsp;the&nbsp;underlying&nbsp;browser&nbsp;is&nbsp;still&nbsp;alive.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.core.exceptions.html#DevtoolsTargetCrashException">DevtoolsTargetCrashException</a></dd>
+<dd><a href="telemetry.core.exceptions.html#AppCrashException">AppCrashException</a></dd>
+<dd><a href="telemetry.core.exceptions.html#Error">Error</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="DevtoolsTargetCrashException-__init__"><strong>__init__</strong></a>(self, app, msg<font color="#909090">='Devtools target crashed'</font>)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.core.exceptions.html#AppCrashException">AppCrashException</a>:<br>
+<dl><dt><a name="DevtoolsTargetCrashException-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.core.exceptions.html#Error">Error</a>:<br>
+<dl><dt><a name="DevtoolsTargetCrashException-AddDebuggingMessage"><strong>AddDebuggingMessage</strong></a>(self, msg)</dt><dd><tt>Adds&nbsp;a&nbsp;message&nbsp;to&nbsp;the&nbsp;description&nbsp;of&nbsp;the&nbsp;exception.<br>
+&nbsp;<br>
+Many&nbsp;Telemetry&nbsp;exceptions&nbsp;arise&nbsp;from&nbsp;failures&nbsp;in&nbsp;another&nbsp;application.&nbsp;These<br>
+failures&nbsp;are&nbsp;difficult&nbsp;to&nbsp;pinpoint.&nbsp;This&nbsp;method&nbsp;allows&nbsp;Telemetry&nbsp;classes&nbsp;to<br>
+append&nbsp;useful&nbsp;debugging&nbsp;information&nbsp;to&nbsp;the&nbsp;exception.&nbsp;This&nbsp;method&nbsp;also&nbsp;logs<br>
+information&nbsp;about&nbsp;the&nbsp;location&nbsp;from&nbsp;where&nbsp;it&nbsp;was&nbsp;called.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.core.exceptions.html#Error">Error</a>:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#DevtoolsTargetCrashException-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;object&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="DevtoolsTargetCrashException-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#DevtoolsTargetCrashException-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="DevtoolsTargetCrashException-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#DevtoolsTargetCrashException-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="DevtoolsTargetCrashException-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#DevtoolsTargetCrashException-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="DevtoolsTargetCrashException-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#DevtoolsTargetCrashException-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="DevtoolsTargetCrashException-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="DevtoolsTargetCrashException-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#DevtoolsTargetCrashException-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="DevtoolsTargetCrashException-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#DevtoolsTargetCrashException-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="DevtoolsTargetCrashException-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="DevtoolsTargetCrashException-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="Error">class <strong>Error</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Base&nbsp;class&nbsp;for&nbsp;Telemetry&nbsp;exceptions.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.core.exceptions.html#Error">Error</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="Error-AddDebuggingMessage"><strong>AddDebuggingMessage</strong></a>(self, msg)</dt><dd><tt>Adds&nbsp;a&nbsp;message&nbsp;to&nbsp;the&nbsp;description&nbsp;of&nbsp;the&nbsp;exception.<br>
+&nbsp;<br>
+Many&nbsp;Telemetry&nbsp;exceptions&nbsp;arise&nbsp;from&nbsp;failures&nbsp;in&nbsp;another&nbsp;application.&nbsp;These<br>
+failures&nbsp;are&nbsp;difficult&nbsp;to&nbsp;pinpoint.&nbsp;This&nbsp;method&nbsp;allows&nbsp;Telemetry&nbsp;classes&nbsp;to<br>
+append&nbsp;useful&nbsp;debugging&nbsp;information&nbsp;to&nbsp;the&nbsp;exception.&nbsp;This&nbsp;method&nbsp;also&nbsp;logs<br>
+information&nbsp;about&nbsp;the&nbsp;location&nbsp;from&nbsp;where&nbsp;it&nbsp;was&nbsp;called.</tt></dd></dl>
+
+<dl><dt><a name="Error-__init__"><strong>__init__</strong></a>(self, msg<font color="#909090">=''</font>)</dt></dl>
+
+<dl><dt><a name="Error-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#Error-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;object&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="Error-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#Error-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="Error-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#Error-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="Error-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#Error-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="Error-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#Error-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="Error-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="Error-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#Error-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="Error-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#Error-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="Error-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="Error-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="EvaluateException">class <strong>EvaluateException</strong></a>(<a href="telemetry.core.exceptions.html#Error">Error</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.core.exceptions.html#EvaluateException">EvaluateException</a></dd>
+<dd><a href="telemetry.core.exceptions.html#Error">Error</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.core.exceptions.html#Error">Error</a>:<br>
+<dl><dt><a name="EvaluateException-AddDebuggingMessage"><strong>AddDebuggingMessage</strong></a>(self, msg)</dt><dd><tt>Adds&nbsp;a&nbsp;message&nbsp;to&nbsp;the&nbsp;description&nbsp;of&nbsp;the&nbsp;exception.<br>
+&nbsp;<br>
+Many&nbsp;Telemetry&nbsp;exceptions&nbsp;arise&nbsp;from&nbsp;failures&nbsp;in&nbsp;another&nbsp;application.&nbsp;These<br>
+failures&nbsp;are&nbsp;difficult&nbsp;to&nbsp;pinpoint.&nbsp;This&nbsp;method&nbsp;allows&nbsp;Telemetry&nbsp;classes&nbsp;to<br>
+append&nbsp;useful&nbsp;debugging&nbsp;information&nbsp;to&nbsp;the&nbsp;exception.&nbsp;This&nbsp;method&nbsp;also&nbsp;logs<br>
+information&nbsp;about&nbsp;the&nbsp;location&nbsp;from&nbsp;where&nbsp;it&nbsp;was&nbsp;called.</tt></dd></dl>
+
+<dl><dt><a name="EvaluateException-__init__"><strong>__init__</strong></a>(self, msg<font color="#909090">=''</font>)</dt></dl>
+
+<dl><dt><a name="EvaluateException-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.core.exceptions.html#Error">Error</a>:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#EvaluateException-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;object&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="EvaluateException-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#EvaluateException-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="EvaluateException-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#EvaluateException-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="EvaluateException-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#EvaluateException-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="EvaluateException-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#EvaluateException-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="EvaluateException-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="EvaluateException-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#EvaluateException-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="EvaluateException-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#EvaluateException-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="EvaluateException-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="EvaluateException-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="InitializationError">class <strong>InitializationError</strong></a>(<a href="telemetry.core.exceptions.html#Error">Error</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.core.exceptions.html#InitializationError">InitializationError</a></dd>
+<dd><a href="telemetry.core.exceptions.html#Error">Error</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="InitializationError-__init__"><strong>__init__</strong></a>(self, string)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.core.exceptions.html#Error">Error</a>:<br>
+<dl><dt><a name="InitializationError-AddDebuggingMessage"><strong>AddDebuggingMessage</strong></a>(self, msg)</dt><dd><tt>Adds&nbsp;a&nbsp;message&nbsp;to&nbsp;the&nbsp;description&nbsp;of&nbsp;the&nbsp;exception.<br>
+&nbsp;<br>
+Many&nbsp;Telemetry&nbsp;exceptions&nbsp;arise&nbsp;from&nbsp;failures&nbsp;in&nbsp;another&nbsp;application.&nbsp;These<br>
+failures&nbsp;are&nbsp;difficult&nbsp;to&nbsp;pinpoint.&nbsp;This&nbsp;method&nbsp;allows&nbsp;Telemetry&nbsp;classes&nbsp;to<br>
+append&nbsp;useful&nbsp;debugging&nbsp;information&nbsp;to&nbsp;the&nbsp;exception.&nbsp;This&nbsp;method&nbsp;also&nbsp;logs<br>
+information&nbsp;about&nbsp;the&nbsp;location&nbsp;from&nbsp;where&nbsp;it&nbsp;was&nbsp;called.</tt></dd></dl>
+
+<dl><dt><a name="InitializationError-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.core.exceptions.html#Error">Error</a>:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#InitializationError-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;object&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="InitializationError-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#InitializationError-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="InitializationError-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#InitializationError-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="InitializationError-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#InitializationError-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="InitializationError-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#InitializationError-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="InitializationError-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="InitializationError-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#InitializationError-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="InitializationError-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#InitializationError-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="InitializationError-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="InitializationError-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="IntentionalException">class <strong>IntentionalException</strong></a>(<a href="telemetry.core.exceptions.html#Error">Error</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Represent&nbsp;an&nbsp;exception&nbsp;raised&nbsp;by&nbsp;a&nbsp;unittest&nbsp;which&nbsp;is&nbsp;not&nbsp;printed.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.core.exceptions.html#IntentionalException">IntentionalException</a></dd>
+<dd><a href="telemetry.core.exceptions.html#Error">Error</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.core.exceptions.html#Error">Error</a>:<br>
+<dl><dt><a name="IntentionalException-AddDebuggingMessage"><strong>AddDebuggingMessage</strong></a>(self, msg)</dt><dd><tt>Adds&nbsp;a&nbsp;message&nbsp;to&nbsp;the&nbsp;description&nbsp;of&nbsp;the&nbsp;exception.<br>
+&nbsp;<br>
+Many&nbsp;Telemetry&nbsp;exceptions&nbsp;arise&nbsp;from&nbsp;failures&nbsp;in&nbsp;another&nbsp;application.&nbsp;These<br>
+failures&nbsp;are&nbsp;difficult&nbsp;to&nbsp;pinpoint.&nbsp;This&nbsp;method&nbsp;allows&nbsp;Telemetry&nbsp;classes&nbsp;to<br>
+append&nbsp;useful&nbsp;debugging&nbsp;information&nbsp;to&nbsp;the&nbsp;exception.&nbsp;This&nbsp;method&nbsp;also&nbsp;logs<br>
+information&nbsp;about&nbsp;the&nbsp;location&nbsp;from&nbsp;where&nbsp;it&nbsp;was&nbsp;called.</tt></dd></dl>
+
+<dl><dt><a name="IntentionalException-__init__"><strong>__init__</strong></a>(self, msg<font color="#909090">=''</font>)</dt></dl>
+
+<dl><dt><a name="IntentionalException-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.core.exceptions.html#Error">Error</a>:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#IntentionalException-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;object&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="IntentionalException-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#IntentionalException-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="IntentionalException-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#IntentionalException-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="IntentionalException-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#IntentionalException-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="IntentionalException-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#IntentionalException-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="IntentionalException-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="IntentionalException-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#IntentionalException-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="IntentionalException-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#IntentionalException-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="IntentionalException-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="IntentionalException-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="LoginException">class <strong>LoginException</strong></a>(<a href="telemetry.core.exceptions.html#Error">Error</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.core.exceptions.html#LoginException">LoginException</a></dd>
+<dd><a href="telemetry.core.exceptions.html#Error">Error</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.core.exceptions.html#Error">Error</a>:<br>
+<dl><dt><a name="LoginException-AddDebuggingMessage"><strong>AddDebuggingMessage</strong></a>(self, msg)</dt><dd><tt>Adds&nbsp;a&nbsp;message&nbsp;to&nbsp;the&nbsp;description&nbsp;of&nbsp;the&nbsp;exception.<br>
+&nbsp;<br>
+Many&nbsp;Telemetry&nbsp;exceptions&nbsp;arise&nbsp;from&nbsp;failures&nbsp;in&nbsp;another&nbsp;application.&nbsp;These<br>
+failures&nbsp;are&nbsp;difficult&nbsp;to&nbsp;pinpoint.&nbsp;This&nbsp;method&nbsp;allows&nbsp;Telemetry&nbsp;classes&nbsp;to<br>
+append&nbsp;useful&nbsp;debugging&nbsp;information&nbsp;to&nbsp;the&nbsp;exception.&nbsp;This&nbsp;method&nbsp;also&nbsp;logs<br>
+information&nbsp;about&nbsp;the&nbsp;location&nbsp;from&nbsp;where&nbsp;it&nbsp;was&nbsp;called.</tt></dd></dl>
+
+<dl><dt><a name="LoginException-__init__"><strong>__init__</strong></a>(self, msg<font color="#909090">=''</font>)</dt></dl>
+
+<dl><dt><a name="LoginException-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.core.exceptions.html#Error">Error</a>:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#LoginException-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;object&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="LoginException-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#LoginException-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="LoginException-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#LoginException-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="LoginException-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#LoginException-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="LoginException-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#LoginException-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="LoginException-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="LoginException-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#LoginException-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="LoginException-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#LoginException-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="LoginException-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="LoginException-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="PackageDetectionError">class <strong>PackageDetectionError</strong></a>(<a href="telemetry.core.exceptions.html#Error">Error</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Represents&nbsp;an&nbsp;error&nbsp;when&nbsp;parsing&nbsp;an&nbsp;Android&nbsp;APK's&nbsp;package.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.core.exceptions.html#PackageDetectionError">PackageDetectionError</a></dd>
+<dd><a href="telemetry.core.exceptions.html#Error">Error</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.core.exceptions.html#Error">Error</a>:<br>
+<dl><dt><a name="PackageDetectionError-AddDebuggingMessage"><strong>AddDebuggingMessage</strong></a>(self, msg)</dt><dd><tt>Adds&nbsp;a&nbsp;message&nbsp;to&nbsp;the&nbsp;description&nbsp;of&nbsp;the&nbsp;exception.<br>
+&nbsp;<br>
+Many&nbsp;Telemetry&nbsp;exceptions&nbsp;arise&nbsp;from&nbsp;failures&nbsp;in&nbsp;another&nbsp;application.&nbsp;These<br>
+failures&nbsp;are&nbsp;difficult&nbsp;to&nbsp;pinpoint.&nbsp;This&nbsp;method&nbsp;allows&nbsp;Telemetry&nbsp;classes&nbsp;to<br>
+append&nbsp;useful&nbsp;debugging&nbsp;information&nbsp;to&nbsp;the&nbsp;exception.&nbsp;This&nbsp;method&nbsp;also&nbsp;logs<br>
+information&nbsp;about&nbsp;the&nbsp;location&nbsp;from&nbsp;where&nbsp;it&nbsp;was&nbsp;called.</tt></dd></dl>
+
+<dl><dt><a name="PackageDetectionError-__init__"><strong>__init__</strong></a>(self, msg<font color="#909090">=''</font>)</dt></dl>
+
+<dl><dt><a name="PackageDetectionError-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.core.exceptions.html#Error">Error</a>:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#PackageDetectionError-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;object&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="PackageDetectionError-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#PackageDetectionError-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="PackageDetectionError-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#PackageDetectionError-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="PackageDetectionError-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#PackageDetectionError-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="PackageDetectionError-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#PackageDetectionError-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="PackageDetectionError-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="PackageDetectionError-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#PackageDetectionError-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="PackageDetectionError-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#PackageDetectionError-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="PackageDetectionError-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="PackageDetectionError-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="PathMissingError">class <strong>PathMissingError</strong></a>(<a href="telemetry.core.exceptions.html#Error">Error</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Represents&nbsp;an&nbsp;exception&nbsp;thrown&nbsp;when&nbsp;an&nbsp;expected&nbsp;path&nbsp;doesn't&nbsp;exist.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.core.exceptions.html#PathMissingError">PathMissingError</a></dd>
+<dd><a href="telemetry.core.exceptions.html#Error">Error</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.core.exceptions.html#Error">Error</a>:<br>
+<dl><dt><a name="PathMissingError-AddDebuggingMessage"><strong>AddDebuggingMessage</strong></a>(self, msg)</dt><dd><tt>Adds&nbsp;a&nbsp;message&nbsp;to&nbsp;the&nbsp;description&nbsp;of&nbsp;the&nbsp;exception.<br>
+&nbsp;<br>
+Many&nbsp;Telemetry&nbsp;exceptions&nbsp;arise&nbsp;from&nbsp;failures&nbsp;in&nbsp;another&nbsp;application.&nbsp;These<br>
+failures&nbsp;are&nbsp;difficult&nbsp;to&nbsp;pinpoint.&nbsp;This&nbsp;method&nbsp;allows&nbsp;Telemetry&nbsp;classes&nbsp;to<br>
+append&nbsp;useful&nbsp;debugging&nbsp;information&nbsp;to&nbsp;the&nbsp;exception.&nbsp;This&nbsp;method&nbsp;also&nbsp;logs<br>
+information&nbsp;about&nbsp;the&nbsp;location&nbsp;from&nbsp;where&nbsp;it&nbsp;was&nbsp;called.</tt></dd></dl>
+
+<dl><dt><a name="PathMissingError-__init__"><strong>__init__</strong></a>(self, msg<font color="#909090">=''</font>)</dt></dl>
+
+<dl><dt><a name="PathMissingError-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.core.exceptions.html#Error">Error</a>:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#PathMissingError-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;object&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="PathMissingError-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#PathMissingError-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="PathMissingError-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#PathMissingError-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="PathMissingError-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#PathMissingError-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="PathMissingError-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#PathMissingError-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="PathMissingError-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="PathMissingError-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#PathMissingError-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="PathMissingError-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#PathMissingError-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="PathMissingError-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="PathMissingError-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="PlatformError">class <strong>PlatformError</strong></a>(<a href="telemetry.core.exceptions.html#Error">Error</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Represents&nbsp;an&nbsp;exception&nbsp;thrown&nbsp;when&nbsp;constructing&nbsp;platform.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.core.exceptions.html#PlatformError">PlatformError</a></dd>
+<dd><a href="telemetry.core.exceptions.html#Error">Error</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.core.exceptions.html#Error">Error</a>:<br>
+<dl><dt><a name="PlatformError-AddDebuggingMessage"><strong>AddDebuggingMessage</strong></a>(self, msg)</dt><dd><tt>Adds&nbsp;a&nbsp;message&nbsp;to&nbsp;the&nbsp;description&nbsp;of&nbsp;the&nbsp;exception.<br>
+&nbsp;<br>
+Many&nbsp;Telemetry&nbsp;exceptions&nbsp;arise&nbsp;from&nbsp;failures&nbsp;in&nbsp;another&nbsp;application.&nbsp;These<br>
+failures&nbsp;are&nbsp;difficult&nbsp;to&nbsp;pinpoint.&nbsp;This&nbsp;method&nbsp;allows&nbsp;Telemetry&nbsp;classes&nbsp;to<br>
+append&nbsp;useful&nbsp;debugging&nbsp;information&nbsp;to&nbsp;the&nbsp;exception.&nbsp;This&nbsp;method&nbsp;also&nbsp;logs<br>
+information&nbsp;about&nbsp;the&nbsp;location&nbsp;from&nbsp;where&nbsp;it&nbsp;was&nbsp;called.</tt></dd></dl>
+
+<dl><dt><a name="PlatformError-__init__"><strong>__init__</strong></a>(self, msg<font color="#909090">=''</font>)</dt></dl>
+
+<dl><dt><a name="PlatformError-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.core.exceptions.html#Error">Error</a>:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#PlatformError-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;object&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="PlatformError-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#PlatformError-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="PlatformError-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#PlatformError-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="PlatformError-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#PlatformError-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="PlatformError-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#PlatformError-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="PlatformError-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="PlatformError-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#PlatformError-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="PlatformError-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#PlatformError-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="PlatformError-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="PlatformError-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ProcessGoneException">class <strong>ProcessGoneException</strong></a>(<a href="telemetry.core.exceptions.html#Error">Error</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Represents&nbsp;a&nbsp;process&nbsp;that&nbsp;no&nbsp;longer&nbsp;exists&nbsp;for&nbsp;an&nbsp;unknown&nbsp;reason.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.core.exceptions.html#ProcessGoneException">ProcessGoneException</a></dd>
+<dd><a href="telemetry.core.exceptions.html#Error">Error</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.core.exceptions.html#Error">Error</a>:<br>
+<dl><dt><a name="ProcessGoneException-AddDebuggingMessage"><strong>AddDebuggingMessage</strong></a>(self, msg)</dt><dd><tt>Adds&nbsp;a&nbsp;message&nbsp;to&nbsp;the&nbsp;description&nbsp;of&nbsp;the&nbsp;exception.<br>
+&nbsp;<br>
+Many&nbsp;Telemetry&nbsp;exceptions&nbsp;arise&nbsp;from&nbsp;failures&nbsp;in&nbsp;another&nbsp;application.&nbsp;These<br>
+failures&nbsp;are&nbsp;difficult&nbsp;to&nbsp;pinpoint.&nbsp;This&nbsp;method&nbsp;allows&nbsp;Telemetry&nbsp;classes&nbsp;to<br>
+append&nbsp;useful&nbsp;debugging&nbsp;information&nbsp;to&nbsp;the&nbsp;exception.&nbsp;This&nbsp;method&nbsp;also&nbsp;logs<br>
+information&nbsp;about&nbsp;the&nbsp;location&nbsp;from&nbsp;where&nbsp;it&nbsp;was&nbsp;called.</tt></dd></dl>
+
+<dl><dt><a name="ProcessGoneException-__init__"><strong>__init__</strong></a>(self, msg<font color="#909090">=''</font>)</dt></dl>
+
+<dl><dt><a name="ProcessGoneException-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.core.exceptions.html#Error">Error</a>:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#ProcessGoneException-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;object&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="ProcessGoneException-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#ProcessGoneException-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="ProcessGoneException-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#ProcessGoneException-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="ProcessGoneException-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#ProcessGoneException-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="ProcessGoneException-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#ProcessGoneException-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="ProcessGoneException-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="ProcessGoneException-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#ProcessGoneException-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="ProcessGoneException-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#ProcessGoneException-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="ProcessGoneException-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="ProcessGoneException-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ProfilingException">class <strong>ProfilingException</strong></a>(<a href="telemetry.core.exceptions.html#Error">Error</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.core.exceptions.html#ProfilingException">ProfilingException</a></dd>
+<dd><a href="telemetry.core.exceptions.html#Error">Error</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.core.exceptions.html#Error">Error</a>:<br>
+<dl><dt><a name="ProfilingException-AddDebuggingMessage"><strong>AddDebuggingMessage</strong></a>(self, msg)</dt><dd><tt>Adds&nbsp;a&nbsp;message&nbsp;to&nbsp;the&nbsp;description&nbsp;of&nbsp;the&nbsp;exception.<br>
+&nbsp;<br>
+Many&nbsp;Telemetry&nbsp;exceptions&nbsp;arise&nbsp;from&nbsp;failures&nbsp;in&nbsp;another&nbsp;application.&nbsp;These<br>
+failures&nbsp;are&nbsp;difficult&nbsp;to&nbsp;pinpoint.&nbsp;This&nbsp;method&nbsp;allows&nbsp;Telemetry&nbsp;classes&nbsp;to<br>
+append&nbsp;useful&nbsp;debugging&nbsp;information&nbsp;to&nbsp;the&nbsp;exception.&nbsp;This&nbsp;method&nbsp;also&nbsp;logs<br>
+information&nbsp;about&nbsp;the&nbsp;location&nbsp;from&nbsp;where&nbsp;it&nbsp;was&nbsp;called.</tt></dd></dl>
+
+<dl><dt><a name="ProfilingException-__init__"><strong>__init__</strong></a>(self, msg<font color="#909090">=''</font>)</dt></dl>
+
+<dl><dt><a name="ProfilingException-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.core.exceptions.html#Error">Error</a>:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#ProfilingException-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;object&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="ProfilingException-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#ProfilingException-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="ProfilingException-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#ProfilingException-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="ProfilingException-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#ProfilingException-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="ProfilingException-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#ProfilingException-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="ProfilingException-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="ProfilingException-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#ProfilingException-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="ProfilingException-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#ProfilingException-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="ProfilingException-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="ProfilingException-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TimeoutException">class <strong>TimeoutException</strong></a>(<a href="telemetry.core.exceptions.html#Error">Error</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>The&nbsp;operation&nbsp;failed&nbsp;to&nbsp;complete&nbsp;because&nbsp;of&nbsp;a&nbsp;timeout.<br>
+&nbsp;<br>
+It&nbsp;is&nbsp;possible&nbsp;that&nbsp;waiting&nbsp;for&nbsp;a&nbsp;longer&nbsp;period&nbsp;of&nbsp;time&nbsp;would&nbsp;result&nbsp;in&nbsp;a<br>
+successful&nbsp;operation.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.core.exceptions.html#TimeoutException">TimeoutException</a></dd>
+<dd><a href="telemetry.core.exceptions.html#Error">Error</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.core.exceptions.html#Error">Error</a>:<br>
+<dl><dt><a name="TimeoutException-AddDebuggingMessage"><strong>AddDebuggingMessage</strong></a>(self, msg)</dt><dd><tt>Adds&nbsp;a&nbsp;message&nbsp;to&nbsp;the&nbsp;description&nbsp;of&nbsp;the&nbsp;exception.<br>
+&nbsp;<br>
+Many&nbsp;Telemetry&nbsp;exceptions&nbsp;arise&nbsp;from&nbsp;failures&nbsp;in&nbsp;another&nbsp;application.&nbsp;These<br>
+failures&nbsp;are&nbsp;difficult&nbsp;to&nbsp;pinpoint.&nbsp;This&nbsp;method&nbsp;allows&nbsp;Telemetry&nbsp;classes&nbsp;to<br>
+append&nbsp;useful&nbsp;debugging&nbsp;information&nbsp;to&nbsp;the&nbsp;exception.&nbsp;This&nbsp;method&nbsp;also&nbsp;logs<br>
+information&nbsp;about&nbsp;the&nbsp;location&nbsp;from&nbsp;where&nbsp;it&nbsp;was&nbsp;called.</tt></dd></dl>
+
+<dl><dt><a name="TimeoutException-__init__"><strong>__init__</strong></a>(self, msg<font color="#909090">=''</font>)</dt></dl>
+
+<dl><dt><a name="TimeoutException-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.core.exceptions.html#Error">Error</a>:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#TimeoutException-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;object&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="TimeoutException-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#TimeoutException-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="TimeoutException-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#TimeoutException-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="TimeoutException-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#TimeoutException-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="TimeoutException-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#TimeoutException-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="TimeoutException-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="TimeoutException-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#TimeoutException-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="TimeoutException-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#TimeoutException-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="TimeoutException-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="TimeoutException-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="UnknownPackageError">class <strong>UnknownPackageError</strong></a>(<a href="telemetry.core.exceptions.html#Error">Error</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Represents&nbsp;an&nbsp;exception&nbsp;when&nbsp;encountering&nbsp;an&nbsp;unsupported&nbsp;Android&nbsp;APK.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.core.exceptions.html#UnknownPackageError">UnknownPackageError</a></dd>
+<dd><a href="telemetry.core.exceptions.html#Error">Error</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.core.exceptions.html#Error">Error</a>:<br>
+<dl><dt><a name="UnknownPackageError-AddDebuggingMessage"><strong>AddDebuggingMessage</strong></a>(self, msg)</dt><dd><tt>Adds&nbsp;a&nbsp;message&nbsp;to&nbsp;the&nbsp;description&nbsp;of&nbsp;the&nbsp;exception.<br>
+&nbsp;<br>
+Many&nbsp;Telemetry&nbsp;exceptions&nbsp;arise&nbsp;from&nbsp;failures&nbsp;in&nbsp;another&nbsp;application.&nbsp;These<br>
+failures&nbsp;are&nbsp;difficult&nbsp;to&nbsp;pinpoint.&nbsp;This&nbsp;method&nbsp;allows&nbsp;Telemetry&nbsp;classes&nbsp;to<br>
+append&nbsp;useful&nbsp;debugging&nbsp;information&nbsp;to&nbsp;the&nbsp;exception.&nbsp;This&nbsp;method&nbsp;also&nbsp;logs<br>
+information&nbsp;about&nbsp;the&nbsp;location&nbsp;from&nbsp;where&nbsp;it&nbsp;was&nbsp;called.</tt></dd></dl>
+
+<dl><dt><a name="UnknownPackageError-__init__"><strong>__init__</strong></a>(self, msg<font color="#909090">=''</font>)</dt></dl>
+
+<dl><dt><a name="UnknownPackageError-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.core.exceptions.html#Error">Error</a>:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#UnknownPackageError-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;object&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="UnknownPackageError-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#UnknownPackageError-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="UnknownPackageError-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#UnknownPackageError-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="UnknownPackageError-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#UnknownPackageError-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="UnknownPackageError-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#UnknownPackageError-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="UnknownPackageError-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="UnknownPackageError-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#UnknownPackageError-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="UnknownPackageError-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#UnknownPackageError-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="UnknownPackageError-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="UnknownPackageError-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.core.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.core.html
new file mode 100644
index 0000000..d941689
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.core.html
@@ -0,0 +1,44 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: package telemetry.core</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.core</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/core/__init__.py">telemetry/core/__init__.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2012&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Package Contents</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.core.android_action_runner.html">android_action_runner</a><br>
+<a href="telemetry.core.android_platform.html">android_platform</a><br>
+<a href="telemetry.core.cros_interface.html">cros_interface</a><br>
+<a href="telemetry.core.cros_interface_unittest.html">cros_interface_unittest</a><br>
+<a href="telemetry.core.discover.html">discover</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.discover_unittest.html">discover_unittest</a><br>
+<a href="telemetry.core.exceptions.html">exceptions</a><br>
+<a href="telemetry.core.local_server.html">local_server</a><br>
+<a href="telemetry.core.local_server_unittest.html">local_server_unittest</a><br>
+<a href="telemetry.core.memory_cache_http_server.html">memory_cache_http_server</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.memory_cache_http_server_unittest.html">memory_cache_http_server_unittest</a><br>
+<a href="telemetry.core.network_controller.html">network_controller</a><br>
+<a href="telemetry.core.os_version.html">os_version</a><br>
+<a href="telemetry.core.platform.html">platform</a><br>
+<a href="telemetry.core.platform_unittest.html">platform_unittest</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.profiling_controller.html">profiling_controller</a><br>
+<a href="telemetry.core.tracing_controller.html">tracing_controller</a><br>
+<a href="telemetry.core.tracing_controller_unittest.html">tracing_controller_unittest</a><br>
+<a href="telemetry.core.util.html">util</a><br>
+<a href="telemetry.core.util_unittest.html">util_unittest</a><br>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.core.local_server.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.core.local_server.html
new file mode 100644
index 0000000..6f9cd22
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.core.local_server.html
@@ -0,0 +1,241 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.core.local_server</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.core.html"><font color="#ffffff">core</font></a>.local_server</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/core/local_server.py">telemetry/core/local_server.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="collections.html">collections</a><br>
+<a href="telemetry.internal.forwarders.html">telemetry.internal.forwarders</a><br>
+</td><td width="25%" valign=top><a href="json.html">json</a><br>
+<a href="os.html">os</a><br>
+</td><td width="25%" valign=top><a href="re.html">re</a><br>
+<a href="subprocess.html">subprocess</a><br>
+</td><td width="25%" valign=top><a href="sys.html">sys</a><br>
+<a href="telemetry.core.util.html">telemetry.core.util</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.core.local_server.html#LocalServer">LocalServer</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.core.local_server.html#LocalServerBackend">LocalServerBackend</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.core.local_server.html#LocalServerController">LocalServerController</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#tuple">__builtin__.tuple</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.core.local_server.html#NamedPort">NamedPort</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="LocalServer">class <strong>LocalServer</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="LocalServer-Close"><strong>Close</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="LocalServer-GetBackendStartupArgs"><strong>GetBackendStartupArgs</strong></a>(self)</dt><dd><tt>Returns&nbsp;whatever&nbsp;arguments&nbsp;are&nbsp;required&nbsp;to&nbsp;start&nbsp;up&nbsp;the&nbsp;backend</tt></dd></dl>
+
+<dl><dt><a name="LocalServer-Start"><strong>Start</strong></a>(self, local_server_controller)</dt></dl>
+
+<dl><dt><a name="LocalServer-__del__"><strong>__del__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="LocalServer-__enter__"><strong>__enter__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="LocalServer-__exit__"><strong>__exit__</strong></a>(self, *args)</dt></dl>
+
+<dl><dt><a name="LocalServer-__init__"><strong>__init__</strong></a>(self, server_backend_class)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>is_running</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="LocalServerBackend">class <strong>LocalServerBackend</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="LocalServerBackend-ServeForever"><strong>ServeForever</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="LocalServerBackend-StartAndGetNamedPorts"><strong>StartAndGetNamedPorts</strong></a>(self, args)</dt><dd><tt>Starts&nbsp;the&nbsp;actual&nbsp;server&nbsp;and&nbsp;obtains&nbsp;any&nbsp;sockets&nbsp;on&nbsp;which&nbsp;it<br>
+should&nbsp;listen.<br>
+&nbsp;<br>
+Returns&nbsp;a&nbsp;list&nbsp;of&nbsp;<a href="#NamedPort">NamedPort</a>&nbsp;on&nbsp;which&nbsp;this&nbsp;backend&nbsp;is&nbsp;listening.</tt></dd></dl>
+
+<dl><dt><a name="LocalServerBackend-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="LocalServerController">class <strong>LocalServerController</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Manages&nbsp;the&nbsp;list&nbsp;of&nbsp;running&nbsp;servers<br>
+&nbsp;<br>
+This&nbsp;class&nbsp;manages&nbsp;the&nbsp;running&nbsp;servers,&nbsp;but&nbsp;also&nbsp;provides&nbsp;an&nbsp;isolation&nbsp;layer<br>
+to&nbsp;prevent&nbsp;<a href="#LocalServer">LocalServer</a>&nbsp;subclasses&nbsp;from&nbsp;accessing&nbsp;the&nbsp;browser&nbsp;backend&nbsp;directly.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="LocalServerController-Close"><strong>Close</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="LocalServerController-CreateForwarder"><strong>CreateForwarder</strong></a>(self, port_pairs)</dt></dl>
+
+<dl><dt><a name="LocalServerController-GetRemotePort"><strong>GetRemotePort</strong></a>(self, port)</dt></dl>
+
+<dl><dt><a name="LocalServerController-GetRunningServer"><strong>GetRunningServer</strong></a>(self, server_class, default_value)</dt></dl>
+
+<dl><dt><a name="LocalServerController-ServerDidClose"><strong>ServerDidClose</strong></a>(self, server)</dt></dl>
+
+<dl><dt><a name="LocalServerController-StartServer"><strong>StartServer</strong></a>(self, server)</dt></dl>
+
+<dl><dt><a name="LocalServerController-__init__"><strong>__init__</strong></a>(self, platform_backend)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>local_servers</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="NamedPort">class <strong>NamedPort</strong></a>(<a href="__builtin__.html#tuple">__builtin__.tuple</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt><a href="#NamedPort">NamedPort</a>(name,&nbsp;port)<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.core.local_server.html#NamedPort">NamedPort</a></dd>
+<dd><a href="__builtin__.html#tuple">__builtin__.tuple</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="NamedPort-__getnewargs__"><strong>__getnewargs__</strong></a>(self)</dt><dd><tt>Return&nbsp;self&nbsp;as&nbsp;a&nbsp;plain&nbsp;<a href="__builtin__.html#tuple">tuple</a>.&nbsp;&nbsp;Used&nbsp;by&nbsp;copy&nbsp;and&nbsp;pickle.</tt></dd></dl>
+
+<dl><dt><a name="NamedPort-__getstate__"><strong>__getstate__</strong></a>(self)</dt><dd><tt>Exclude&nbsp;the&nbsp;OrderedDict&nbsp;from&nbsp;pickling</tt></dd></dl>
+
+<dl><dt><a name="NamedPort-__repr__"><strong>__repr__</strong></a>(self)</dt><dd><tt>Return&nbsp;a&nbsp;nicely&nbsp;formatted&nbsp;representation&nbsp;string</tt></dd></dl>
+
+<dl><dt><a name="NamedPort-_asdict"><strong>_asdict</strong></a>(self)</dt><dd><tt>Return&nbsp;a&nbsp;new&nbsp;OrderedDict&nbsp;which&nbsp;maps&nbsp;field&nbsp;names&nbsp;to&nbsp;their&nbsp;values</tt></dd></dl>
+
+<dl><dt><a name="NamedPort-_replace"><strong>_replace</strong></a>(_self, **kwds)</dt><dd><tt>Return&nbsp;a&nbsp;new&nbsp;<a href="#NamedPort">NamedPort</a>&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;replacing&nbsp;specified&nbsp;fields&nbsp;with&nbsp;new&nbsp;values</tt></dd></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="NamedPort-_make"><strong>_make</strong></a>(cls, iterable, new<font color="#909090">=&lt;built-in method __new__ of type object&gt;</font>, len<font color="#909090">=&lt;built-in function len&gt;</font>)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Make&nbsp;a&nbsp;new&nbsp;<a href="#NamedPort">NamedPort</a>&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;from&nbsp;a&nbsp;sequence&nbsp;or&nbsp;iterable</tt></dd></dl>
+
+<hr>
+Static methods defined here:<br>
+<dl><dt><a name="NamedPort-__new__"><strong>__new__</strong></a>(_cls, name, port)</dt><dd><tt>Create&nbsp;new&nbsp;instance&nbsp;of&nbsp;<a href="#NamedPort">NamedPort</a>(name,&nbsp;port)</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>Return&nbsp;a&nbsp;new&nbsp;OrderedDict&nbsp;which&nbsp;maps&nbsp;field&nbsp;names&nbsp;to&nbsp;their&nbsp;values</tt></dd>
+</dl>
+<dl><dt><strong>name</strong></dt>
+<dd><tt>Alias&nbsp;for&nbsp;field&nbsp;number&nbsp;0</tt></dd>
+</dl>
+<dl><dt><strong>port</strong></dt>
+<dd><tt>Alias&nbsp;for&nbsp;field&nbsp;number&nbsp;1</tt></dd>
+</dl>
+<hr>
+Data and other attributes defined here:<br>
+<dl><dt><strong>_fields</strong> = ('name', 'port')</dl>
+
+<hr>
+Methods inherited from <a href="__builtin__.html#tuple">__builtin__.tuple</a>:<br>
+<dl><dt><a name="NamedPort-__add__"><strong>__add__</strong></a>(...)</dt><dd><tt>x.<a href="#NamedPort-__add__">__add__</a>(y)&nbsp;&lt;==&gt;&nbsp;x+y</tt></dd></dl>
+
+<dl><dt><a name="NamedPort-__contains__"><strong>__contains__</strong></a>(...)</dt><dd><tt>x.<a href="#NamedPort-__contains__">__contains__</a>(y)&nbsp;&lt;==&gt;&nbsp;y&nbsp;in&nbsp;x</tt></dd></dl>
+
+<dl><dt><a name="NamedPort-__eq__"><strong>__eq__</strong></a>(...)</dt><dd><tt>x.<a href="#NamedPort-__eq__">__eq__</a>(y)&nbsp;&lt;==&gt;&nbsp;x==y</tt></dd></dl>
+
+<dl><dt><a name="NamedPort-__ge__"><strong>__ge__</strong></a>(...)</dt><dd><tt>x.<a href="#NamedPort-__ge__">__ge__</a>(y)&nbsp;&lt;==&gt;&nbsp;x&gt;=y</tt></dd></dl>
+
+<dl><dt><a name="NamedPort-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#NamedPort-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="NamedPort-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#NamedPort-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="NamedPort-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#NamedPort-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="NamedPort-__gt__"><strong>__gt__</strong></a>(...)</dt><dd><tt>x.<a href="#NamedPort-__gt__">__gt__</a>(y)&nbsp;&lt;==&gt;&nbsp;x&gt;y</tt></dd></dl>
+
+<dl><dt><a name="NamedPort-__hash__"><strong>__hash__</strong></a>(...)</dt><dd><tt>x.<a href="#NamedPort-__hash__">__hash__</a>()&nbsp;&lt;==&gt;&nbsp;hash(x)</tt></dd></dl>
+
+<dl><dt><a name="NamedPort-__iter__"><strong>__iter__</strong></a>(...)</dt><dd><tt>x.<a href="#NamedPort-__iter__">__iter__</a>()&nbsp;&lt;==&gt;&nbsp;iter(x)</tt></dd></dl>
+
+<dl><dt><a name="NamedPort-__le__"><strong>__le__</strong></a>(...)</dt><dd><tt>x.<a href="#NamedPort-__le__">__le__</a>(y)&nbsp;&lt;==&gt;&nbsp;x&lt;=y</tt></dd></dl>
+
+<dl><dt><a name="NamedPort-__len__"><strong>__len__</strong></a>(...)</dt><dd><tt>x.<a href="#NamedPort-__len__">__len__</a>()&nbsp;&lt;==&gt;&nbsp;len(x)</tt></dd></dl>
+
+<dl><dt><a name="NamedPort-__lt__"><strong>__lt__</strong></a>(...)</dt><dd><tt>x.<a href="#NamedPort-__lt__">__lt__</a>(y)&nbsp;&lt;==&gt;&nbsp;x&lt;y</tt></dd></dl>
+
+<dl><dt><a name="NamedPort-__mul__"><strong>__mul__</strong></a>(...)</dt><dd><tt>x.<a href="#NamedPort-__mul__">__mul__</a>(n)&nbsp;&lt;==&gt;&nbsp;x*n</tt></dd></dl>
+
+<dl><dt><a name="NamedPort-__ne__"><strong>__ne__</strong></a>(...)</dt><dd><tt>x.<a href="#NamedPort-__ne__">__ne__</a>(y)&nbsp;&lt;==&gt;&nbsp;x!=y</tt></dd></dl>
+
+<dl><dt><a name="NamedPort-__rmul__"><strong>__rmul__</strong></a>(...)</dt><dd><tt>x.<a href="#NamedPort-__rmul__">__rmul__</a>(n)&nbsp;&lt;==&gt;&nbsp;n*x</tt></dd></dl>
+
+<dl><dt><a name="NamedPort-__sizeof__"><strong>__sizeof__</strong></a>(...)</dt><dd><tt>T.<a href="#NamedPort-__sizeof__">__sizeof__</a>()&nbsp;--&nbsp;size&nbsp;of&nbsp;T&nbsp;in&nbsp;memory,&nbsp;in&nbsp;bytes</tt></dd></dl>
+
+<dl><dt><a name="NamedPort-count"><strong>count</strong></a>(...)</dt><dd><tt>T.<a href="#NamedPort-count">count</a>(value)&nbsp;-&gt;&nbsp;integer&nbsp;--&nbsp;return&nbsp;number&nbsp;of&nbsp;occurrences&nbsp;of&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="NamedPort-index"><strong>index</strong></a>(...)</dt><dd><tt>T.<a href="#NamedPort-index">index</a>(value,&nbsp;[start,&nbsp;[stop]])&nbsp;-&gt;&nbsp;integer&nbsp;--&nbsp;return&nbsp;first&nbsp;index&nbsp;of&nbsp;value.<br>
+Raises&nbsp;ValueError&nbsp;if&nbsp;the&nbsp;value&nbsp;is&nbsp;not&nbsp;present.</tt></dd></dl>
+
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.core.memory_cache_http_server.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.core.memory_cache_http_server.html
new file mode 100644
index 0000000..1a50b5e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.core.memory_cache_http_server.html
@@ -0,0 +1,527 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.core.memory_cache_http_server</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.core.html"><font color="#ffffff">core</font></a>.memory_cache_http_server</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/core/memory_cache_http_server.py">telemetry/core/memory_cache_http_server.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2012&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="BaseHTTPServer.html">BaseHTTPServer</a><br>
+<a href="SimpleHTTPServer.html">SimpleHTTPServer</a><br>
+<a href="SocketServer.html">SocketServer</a><br>
+</td><td width="25%" valign=top><a href="StringIO.html">StringIO</a><br>
+<a href="errno.html">errno</a><br>
+<a href="gzip.html">gzip</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.local_server.html">telemetry.core.local_server</a><br>
+<a href="mimetypes.html">mimetypes</a><br>
+<a href="os.html">os</a><br>
+</td><td width="25%" valign=top><a href="socket.html">socket</a><br>
+<a href="sys.html">sys</a><br>
+<a href="urlparse.html">urlparse</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="SimpleHTTPServer.html#SimpleHTTPRequestHandler">SimpleHTTPServer.SimpleHTTPRequestHandler</a>(<a href="BaseHTTPServer.html#BaseHTTPRequestHandler">BaseHTTPServer.BaseHTTPRequestHandler</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.core.memory_cache_http_server.html#MemoryCacheHTTPRequestHandler">MemoryCacheHTTPRequestHandler</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#tuple">__builtin__.tuple</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.core.memory_cache_http_server.html#ByteRange">ByteRange</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.core.memory_cache_http_server.html#ResourceAndRange">ResourceAndRange</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="telemetry.core.local_server.html#LocalServer">telemetry.core.local_server.LocalServer</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.core.memory_cache_http_server.html#MemoryCacheHTTPServer">MemoryCacheHTTPServer</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="telemetry.core.local_server.html#LocalServerBackend">telemetry.core.local_server.LocalServerBackend</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.core.memory_cache_http_server.html#MemoryCacheHTTPServerBackend">MemoryCacheHTTPServerBackend</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ByteRange">class <strong>ByteRange</strong></a>(<a href="__builtin__.html#tuple">__builtin__.tuple</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt><a href="#ByteRange">ByteRange</a>(from_byte,&nbsp;to_byte)<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.core.memory_cache_http_server.html#ByteRange">ByteRange</a></dd>
+<dd><a href="__builtin__.html#tuple">__builtin__.tuple</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="ByteRange-__getnewargs__"><strong>__getnewargs__</strong></a>(self)</dt><dd><tt>Return&nbsp;self&nbsp;as&nbsp;a&nbsp;plain&nbsp;<a href="__builtin__.html#tuple">tuple</a>.&nbsp;&nbsp;Used&nbsp;by&nbsp;copy&nbsp;and&nbsp;pickle.</tt></dd></dl>
+
+<dl><dt><a name="ByteRange-__getstate__"><strong>__getstate__</strong></a>(self)</dt><dd><tt>Exclude&nbsp;the&nbsp;OrderedDict&nbsp;from&nbsp;pickling</tt></dd></dl>
+
+<dl><dt><a name="ByteRange-__repr__"><strong>__repr__</strong></a>(self)</dt><dd><tt>Return&nbsp;a&nbsp;nicely&nbsp;formatted&nbsp;representation&nbsp;string</tt></dd></dl>
+
+<dl><dt><a name="ByteRange-_asdict"><strong>_asdict</strong></a>(self)</dt><dd><tt>Return&nbsp;a&nbsp;new&nbsp;OrderedDict&nbsp;which&nbsp;maps&nbsp;field&nbsp;names&nbsp;to&nbsp;their&nbsp;values</tt></dd></dl>
+
+<dl><dt><a name="ByteRange-_replace"><strong>_replace</strong></a>(_self, **kwds)</dt><dd><tt>Return&nbsp;a&nbsp;new&nbsp;<a href="#ByteRange">ByteRange</a>&nbsp;object&nbsp;replacing&nbsp;specified&nbsp;fields&nbsp;with&nbsp;new&nbsp;values</tt></dd></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="ByteRange-_make"><strong>_make</strong></a>(cls, iterable, new<font color="#909090">=&lt;built-in method __new__ of type object&gt;</font>, len<font color="#909090">=&lt;built-in function len&gt;</font>)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Make&nbsp;a&nbsp;new&nbsp;<a href="#ByteRange">ByteRange</a>&nbsp;object&nbsp;from&nbsp;a&nbsp;sequence&nbsp;or&nbsp;iterable</tt></dd></dl>
+
+<hr>
+Static methods defined here:<br>
+<dl><dt><a name="ByteRange-__new__"><strong>__new__</strong></a>(_cls, from_byte, to_byte)</dt><dd><tt>Create&nbsp;new&nbsp;instance&nbsp;of&nbsp;<a href="#ByteRange">ByteRange</a>(from_byte,&nbsp;to_byte)</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>Return&nbsp;a&nbsp;new&nbsp;OrderedDict&nbsp;which&nbsp;maps&nbsp;field&nbsp;names&nbsp;to&nbsp;their&nbsp;values</tt></dd>
+</dl>
+<dl><dt><strong>from_byte</strong></dt>
+<dd><tt>Alias&nbsp;for&nbsp;field&nbsp;number&nbsp;0</tt></dd>
+</dl>
+<dl><dt><strong>to_byte</strong></dt>
+<dd><tt>Alias&nbsp;for&nbsp;field&nbsp;number&nbsp;1</tt></dd>
+</dl>
+<hr>
+Data and other attributes defined here:<br>
+<dl><dt><strong>_fields</strong> = ('from_byte', 'to_byte')</dl>
+
+<hr>
+Methods inherited from <a href="__builtin__.html#tuple">__builtin__.tuple</a>:<br>
+<dl><dt><a name="ByteRange-__add__"><strong>__add__</strong></a>(...)</dt><dd><tt>x.<a href="#ByteRange-__add__">__add__</a>(y)&nbsp;&lt;==&gt;&nbsp;x+y</tt></dd></dl>
+
+<dl><dt><a name="ByteRange-__contains__"><strong>__contains__</strong></a>(...)</dt><dd><tt>x.<a href="#ByteRange-__contains__">__contains__</a>(y)&nbsp;&lt;==&gt;&nbsp;y&nbsp;in&nbsp;x</tt></dd></dl>
+
+<dl><dt><a name="ByteRange-__eq__"><strong>__eq__</strong></a>(...)</dt><dd><tt>x.<a href="#ByteRange-__eq__">__eq__</a>(y)&nbsp;&lt;==&gt;&nbsp;x==y</tt></dd></dl>
+
+<dl><dt><a name="ByteRange-__ge__"><strong>__ge__</strong></a>(...)</dt><dd><tt>x.<a href="#ByteRange-__ge__">__ge__</a>(y)&nbsp;&lt;==&gt;&nbsp;x&gt;=y</tt></dd></dl>
+
+<dl><dt><a name="ByteRange-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#ByteRange-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="ByteRange-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#ByteRange-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="ByteRange-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#ByteRange-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="ByteRange-__gt__"><strong>__gt__</strong></a>(...)</dt><dd><tt>x.<a href="#ByteRange-__gt__">__gt__</a>(y)&nbsp;&lt;==&gt;&nbsp;x&gt;y</tt></dd></dl>
+
+<dl><dt><a name="ByteRange-__hash__"><strong>__hash__</strong></a>(...)</dt><dd><tt>x.<a href="#ByteRange-__hash__">__hash__</a>()&nbsp;&lt;==&gt;&nbsp;hash(x)</tt></dd></dl>
+
+<dl><dt><a name="ByteRange-__iter__"><strong>__iter__</strong></a>(...)</dt><dd><tt>x.<a href="#ByteRange-__iter__">__iter__</a>()&nbsp;&lt;==&gt;&nbsp;iter(x)</tt></dd></dl>
+
+<dl><dt><a name="ByteRange-__le__"><strong>__le__</strong></a>(...)</dt><dd><tt>x.<a href="#ByteRange-__le__">__le__</a>(y)&nbsp;&lt;==&gt;&nbsp;x&lt;=y</tt></dd></dl>
+
+<dl><dt><a name="ByteRange-__len__"><strong>__len__</strong></a>(...)</dt><dd><tt>x.<a href="#ByteRange-__len__">__len__</a>()&nbsp;&lt;==&gt;&nbsp;len(x)</tt></dd></dl>
+
+<dl><dt><a name="ByteRange-__lt__"><strong>__lt__</strong></a>(...)</dt><dd><tt>x.<a href="#ByteRange-__lt__">__lt__</a>(y)&nbsp;&lt;==&gt;&nbsp;x&lt;y</tt></dd></dl>
+
+<dl><dt><a name="ByteRange-__mul__"><strong>__mul__</strong></a>(...)</dt><dd><tt>x.<a href="#ByteRange-__mul__">__mul__</a>(n)&nbsp;&lt;==&gt;&nbsp;x*n</tt></dd></dl>
+
+<dl><dt><a name="ByteRange-__ne__"><strong>__ne__</strong></a>(...)</dt><dd><tt>x.<a href="#ByteRange-__ne__">__ne__</a>(y)&nbsp;&lt;==&gt;&nbsp;x!=y</tt></dd></dl>
+
+<dl><dt><a name="ByteRange-__rmul__"><strong>__rmul__</strong></a>(...)</dt><dd><tt>x.<a href="#ByteRange-__rmul__">__rmul__</a>(n)&nbsp;&lt;==&gt;&nbsp;n*x</tt></dd></dl>
+
+<dl><dt><a name="ByteRange-__sizeof__"><strong>__sizeof__</strong></a>(...)</dt><dd><tt>T.<a href="#ByteRange-__sizeof__">__sizeof__</a>()&nbsp;--&nbsp;size&nbsp;of&nbsp;T&nbsp;in&nbsp;memory,&nbsp;in&nbsp;bytes</tt></dd></dl>
+
+<dl><dt><a name="ByteRange-count"><strong>count</strong></a>(...)</dt><dd><tt>T.<a href="#ByteRange-count">count</a>(value)&nbsp;-&gt;&nbsp;integer&nbsp;--&nbsp;return&nbsp;number&nbsp;of&nbsp;occurrences&nbsp;of&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="ByteRange-index"><strong>index</strong></a>(...)</dt><dd><tt>T.<a href="#ByteRange-index">index</a>(value,&nbsp;[start,&nbsp;[stop]])&nbsp;-&gt;&nbsp;integer&nbsp;--&nbsp;return&nbsp;first&nbsp;index&nbsp;of&nbsp;value.<br>
+Raises&nbsp;ValueError&nbsp;if&nbsp;the&nbsp;value&nbsp;is&nbsp;not&nbsp;present.</tt></dd></dl>
+
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="MemoryCacheHTTPRequestHandler">class <strong>MemoryCacheHTTPRequestHandler</strong></a>(<a href="SimpleHTTPServer.html#SimpleHTTPRequestHandler">SimpleHTTPServer.SimpleHTTPRequestHandler</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.core.memory_cache_http_server.html#MemoryCacheHTTPRequestHandler">MemoryCacheHTTPRequestHandler</a></dd>
+<dd><a href="SimpleHTTPServer.html#SimpleHTTPRequestHandler">SimpleHTTPServer.SimpleHTTPRequestHandler</a></dd>
+<dd><a href="BaseHTTPServer.html#BaseHTTPRequestHandler">BaseHTTPServer.BaseHTTPRequestHandler</a></dd>
+<dd><a href="SocketServer.html#StreamRequestHandler">SocketServer.StreamRequestHandler</a></dd>
+<dd><a href="SocketServer.html#BaseRequestHandler">SocketServer.BaseRequestHandler</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="MemoryCacheHTTPRequestHandler-GetByteRange"><strong>GetByteRange</strong></a>(self, total_num_of_bytes)</dt><dd><tt>Parse&nbsp;the&nbsp;header&nbsp;and&nbsp;get&nbsp;the&nbsp;range&nbsp;values&nbsp;specified.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;total_num_of_bytes:&nbsp;Total&nbsp;#&nbsp;of&nbsp;bytes&nbsp;in&nbsp;requested&nbsp;resource,<br>
+&nbsp;&nbsp;used&nbsp;to&nbsp;calculate&nbsp;upper&nbsp;range&nbsp;limit.<br>
+Returns:<br>
+&nbsp;&nbsp;A&nbsp;<a href="#ByteRange">ByteRange</a>&nbsp;namedtuple&nbsp;object&nbsp;with&nbsp;the&nbsp;requested&nbsp;byte-range&nbsp;values.<br>
+&nbsp;&nbsp;If&nbsp;no&nbsp;Range&nbsp;is&nbsp;explicitly&nbsp;requested&nbsp;or&nbsp;there&nbsp;is&nbsp;a&nbsp;failure&nbsp;parsing,<br>
+&nbsp;&nbsp;return&nbsp;None.<br>
+&nbsp;&nbsp;If&nbsp;range&nbsp;specified&nbsp;is&nbsp;in&nbsp;the&nbsp;format&nbsp;"N-",&nbsp;return&nbsp;N-END.&nbsp;Refer&nbsp;to<br>
+&nbsp;&nbsp;<a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html">http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html</a>&nbsp;for&nbsp;details.<br>
+&nbsp;&nbsp;If&nbsp;upper&nbsp;range&nbsp;limit&nbsp;is&nbsp;greater&nbsp;than&nbsp;total&nbsp;#&nbsp;of&nbsp;bytes,&nbsp;return&nbsp;upper&nbsp;index.</tt></dd></dl>
+
+<dl><dt><a name="MemoryCacheHTTPRequestHandler-SendHead"><strong>SendHead</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MemoryCacheHTTPRequestHandler-do_GET"><strong>do_GET</strong></a>(self)</dt><dd><tt>Serve&nbsp;a&nbsp;GET&nbsp;request.</tt></dd></dl>
+
+<dl><dt><a name="MemoryCacheHTTPRequestHandler-do_HEAD"><strong>do_HEAD</strong></a>(self)</dt><dd><tt>Serve&nbsp;a&nbsp;HEAD&nbsp;request.</tt></dd></dl>
+
+<dl><dt><a name="MemoryCacheHTTPRequestHandler-handle"><strong>handle</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MemoryCacheHTTPRequestHandler-log_error"><strong>log_error</strong></a>(self, fmt, *args)</dt></dl>
+
+<dl><dt><a name="MemoryCacheHTTPRequestHandler-log_request"><strong>log_request</strong></a>(self, code<font color="#909090">='-'</font>, size<font color="#909090">='-'</font>)</dt></dl>
+
+<hr>
+Data and other attributes defined here:<br>
+<dl><dt><strong>protocol_version</strong> = 'HTTP/1.1'</dl>
+
+<dl><dt><strong>wbufsize</strong> = -1</dl>
+
+<hr>
+Methods inherited from <a href="SimpleHTTPServer.html#SimpleHTTPRequestHandler">SimpleHTTPServer.SimpleHTTPRequestHandler</a>:<br>
+<dl><dt><a name="MemoryCacheHTTPRequestHandler-copyfile"><strong>copyfile</strong></a>(self, source, outputfile)</dt><dd><tt>Copy&nbsp;all&nbsp;data&nbsp;between&nbsp;two&nbsp;file&nbsp;objects.<br>
+&nbsp;<br>
+The&nbsp;SOURCE&nbsp;argument&nbsp;is&nbsp;a&nbsp;file&nbsp;object&nbsp;open&nbsp;for&nbsp;reading<br>
+(or&nbsp;anything&nbsp;with&nbsp;a&nbsp;read()&nbsp;method)&nbsp;and&nbsp;the&nbsp;DESTINATION<br>
+argument&nbsp;is&nbsp;a&nbsp;file&nbsp;object&nbsp;open&nbsp;for&nbsp;writing&nbsp;(or<br>
+anything&nbsp;with&nbsp;a&nbsp;write()&nbsp;method).<br>
+&nbsp;<br>
+The&nbsp;only&nbsp;reason&nbsp;for&nbsp;overriding&nbsp;this&nbsp;would&nbsp;be&nbsp;to&nbsp;change<br>
+the&nbsp;block&nbsp;size&nbsp;or&nbsp;perhaps&nbsp;to&nbsp;replace&nbsp;newlines&nbsp;by&nbsp;CRLF<br>
+--&nbsp;note&nbsp;however&nbsp;that&nbsp;this&nbsp;the&nbsp;default&nbsp;server&nbsp;uses&nbsp;this<br>
+to&nbsp;copy&nbsp;binary&nbsp;data&nbsp;as&nbsp;well.</tt></dd></dl>
+
+<dl><dt><a name="MemoryCacheHTTPRequestHandler-guess_type"><strong>guess_type</strong></a>(self, path)</dt><dd><tt>Guess&nbsp;the&nbsp;type&nbsp;of&nbsp;a&nbsp;file.<br>
+&nbsp;<br>
+Argument&nbsp;is&nbsp;a&nbsp;PATH&nbsp;(a&nbsp;filename).<br>
+&nbsp;<br>
+Return&nbsp;value&nbsp;is&nbsp;a&nbsp;string&nbsp;of&nbsp;the&nbsp;form&nbsp;type/subtype,<br>
+usable&nbsp;for&nbsp;a&nbsp;MIME&nbsp;Content-type&nbsp;header.<br>
+&nbsp;<br>
+The&nbsp;default&nbsp;implementation&nbsp;looks&nbsp;the&nbsp;file's&nbsp;extension<br>
+up&nbsp;in&nbsp;the&nbsp;table&nbsp;self.<strong>extensions_map</strong>,&nbsp;using&nbsp;application/octet-stream<br>
+as&nbsp;a&nbsp;default;&nbsp;however&nbsp;it&nbsp;would&nbsp;be&nbsp;permissible&nbsp;(if<br>
+slow)&nbsp;to&nbsp;look&nbsp;inside&nbsp;the&nbsp;data&nbsp;to&nbsp;make&nbsp;a&nbsp;better&nbsp;guess.</tt></dd></dl>
+
+<dl><dt><a name="MemoryCacheHTTPRequestHandler-list_directory"><strong>list_directory</strong></a>(self, path)</dt><dd><tt>Helper&nbsp;to&nbsp;produce&nbsp;a&nbsp;directory&nbsp;listing&nbsp;(absent&nbsp;index.html).<br>
+&nbsp;<br>
+Return&nbsp;value&nbsp;is&nbsp;either&nbsp;a&nbsp;file&nbsp;object,&nbsp;or&nbsp;None&nbsp;(indicating&nbsp;an<br>
+error).&nbsp;&nbsp;In&nbsp;either&nbsp;case,&nbsp;the&nbsp;headers&nbsp;are&nbsp;sent,&nbsp;making&nbsp;the<br>
+interface&nbsp;the&nbsp;same&nbsp;as&nbsp;for&nbsp;<a href="#MemoryCacheHTTPRequestHandler-send_head">send_head</a>().</tt></dd></dl>
+
+<dl><dt><a name="MemoryCacheHTTPRequestHandler-send_head"><strong>send_head</strong></a>(self)</dt><dd><tt>Common&nbsp;code&nbsp;for&nbsp;GET&nbsp;and&nbsp;HEAD&nbsp;commands.<br>
+&nbsp;<br>
+This&nbsp;sends&nbsp;the&nbsp;response&nbsp;code&nbsp;and&nbsp;MIME&nbsp;headers.<br>
+&nbsp;<br>
+Return&nbsp;value&nbsp;is&nbsp;either&nbsp;a&nbsp;file&nbsp;object&nbsp;(which&nbsp;has&nbsp;to&nbsp;be&nbsp;copied<br>
+to&nbsp;the&nbsp;outputfile&nbsp;by&nbsp;the&nbsp;caller&nbsp;unless&nbsp;the&nbsp;command&nbsp;was&nbsp;HEAD,<br>
+and&nbsp;must&nbsp;be&nbsp;closed&nbsp;by&nbsp;the&nbsp;caller&nbsp;under&nbsp;all&nbsp;circumstances),&nbsp;or<br>
+None,&nbsp;in&nbsp;which&nbsp;case&nbsp;the&nbsp;caller&nbsp;has&nbsp;nothing&nbsp;further&nbsp;to&nbsp;do.</tt></dd></dl>
+
+<dl><dt><a name="MemoryCacheHTTPRequestHandler-translate_path"><strong>translate_path</strong></a>(self, path)</dt><dd><tt>Translate&nbsp;a&nbsp;/-separated&nbsp;PATH&nbsp;to&nbsp;the&nbsp;local&nbsp;filename&nbsp;syntax.<br>
+&nbsp;<br>
+Components&nbsp;that&nbsp;mean&nbsp;special&nbsp;things&nbsp;to&nbsp;the&nbsp;local&nbsp;file&nbsp;system<br>
+(e.g.&nbsp;drive&nbsp;or&nbsp;directory&nbsp;names)&nbsp;are&nbsp;ignored.&nbsp;&nbsp;(XXX&nbsp;They&nbsp;should<br>
+probably&nbsp;be&nbsp;diagnosed.)</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="SimpleHTTPServer.html#SimpleHTTPRequestHandler">SimpleHTTPServer.SimpleHTTPRequestHandler</a>:<br>
+<dl><dt><strong>extensions_map</strong> = {'': 'application/octet-stream', '.%': 'application/x-trash', '.323': 'text/h323', '.3gp': 'video/3gpp', '.7z': 'application/x-7z-compressed', '.a': 'application/octet-stream', '.abw': 'application/x-abiword', '.ai': 'application/postscript', '.aif': 'audio/x-aiff', '.aifc': 'audio/x-aiff', ...}</dl>
+
+<dl><dt><strong>server_version</strong> = 'SimpleHTTP/0.6'</dl>
+
+<hr>
+Methods inherited from <a href="BaseHTTPServer.html#BaseHTTPRequestHandler">BaseHTTPServer.BaseHTTPRequestHandler</a>:<br>
+<dl><dt><a name="MemoryCacheHTTPRequestHandler-address_string"><strong>address_string</strong></a>(self)</dt><dd><tt>Return&nbsp;the&nbsp;client&nbsp;address&nbsp;formatted&nbsp;for&nbsp;logging.<br>
+&nbsp;<br>
+This&nbsp;version&nbsp;looks&nbsp;up&nbsp;the&nbsp;full&nbsp;hostname&nbsp;using&nbsp;gethostbyaddr(),<br>
+and&nbsp;tries&nbsp;to&nbsp;find&nbsp;a&nbsp;name&nbsp;that&nbsp;contains&nbsp;at&nbsp;least&nbsp;one&nbsp;dot.</tt></dd></dl>
+
+<dl><dt><a name="MemoryCacheHTTPRequestHandler-date_time_string"><strong>date_time_string</strong></a>(self, timestamp<font color="#909090">=None</font>)</dt><dd><tt>Return&nbsp;the&nbsp;current&nbsp;date&nbsp;and&nbsp;time&nbsp;formatted&nbsp;for&nbsp;a&nbsp;message&nbsp;header.</tt></dd></dl>
+
+<dl><dt><a name="MemoryCacheHTTPRequestHandler-end_headers"><strong>end_headers</strong></a>(self)</dt><dd><tt>Send&nbsp;the&nbsp;blank&nbsp;line&nbsp;ending&nbsp;the&nbsp;MIME&nbsp;headers.</tt></dd></dl>
+
+<dl><dt><a name="MemoryCacheHTTPRequestHandler-handle_one_request"><strong>handle_one_request</strong></a>(self)</dt><dd><tt>Handle&nbsp;a&nbsp;single&nbsp;HTTP&nbsp;request.<br>
+&nbsp;<br>
+You&nbsp;normally&nbsp;don't&nbsp;need&nbsp;to&nbsp;override&nbsp;this&nbsp;method;&nbsp;see&nbsp;the&nbsp;class<br>
+__doc__&nbsp;string&nbsp;for&nbsp;information&nbsp;on&nbsp;how&nbsp;to&nbsp;handle&nbsp;specific&nbsp;HTTP<br>
+commands&nbsp;such&nbsp;as&nbsp;GET&nbsp;and&nbsp;POST.</tt></dd></dl>
+
+<dl><dt><a name="MemoryCacheHTTPRequestHandler-log_date_time_string"><strong>log_date_time_string</strong></a>(self)</dt><dd><tt>Return&nbsp;the&nbsp;current&nbsp;time&nbsp;formatted&nbsp;for&nbsp;logging.</tt></dd></dl>
+
+<dl><dt><a name="MemoryCacheHTTPRequestHandler-log_message"><strong>log_message</strong></a>(self, format, *args)</dt><dd><tt>Log&nbsp;an&nbsp;arbitrary&nbsp;message.<br>
+&nbsp;<br>
+This&nbsp;is&nbsp;used&nbsp;by&nbsp;all&nbsp;other&nbsp;logging&nbsp;functions.&nbsp;&nbsp;Override<br>
+it&nbsp;if&nbsp;you&nbsp;have&nbsp;specific&nbsp;logging&nbsp;wishes.<br>
+&nbsp;<br>
+The&nbsp;first&nbsp;argument,&nbsp;FORMAT,&nbsp;is&nbsp;a&nbsp;format&nbsp;string&nbsp;for&nbsp;the<br>
+message&nbsp;to&nbsp;be&nbsp;logged.&nbsp;&nbsp;If&nbsp;the&nbsp;format&nbsp;string&nbsp;contains<br>
+any&nbsp;%&nbsp;escapes&nbsp;requiring&nbsp;parameters,&nbsp;they&nbsp;should&nbsp;be<br>
+specified&nbsp;as&nbsp;subsequent&nbsp;arguments&nbsp;(it's&nbsp;just&nbsp;like<br>
+printf!).<br>
+&nbsp;<br>
+The&nbsp;client&nbsp;ip&nbsp;address&nbsp;and&nbsp;current&nbsp;date/time&nbsp;are&nbsp;prefixed&nbsp;to&nbsp;every<br>
+message.</tt></dd></dl>
+
+<dl><dt><a name="MemoryCacheHTTPRequestHandler-parse_request"><strong>parse_request</strong></a>(self)</dt><dd><tt>Parse&nbsp;a&nbsp;request&nbsp;(internal).<br>
+&nbsp;<br>
+The&nbsp;request&nbsp;should&nbsp;be&nbsp;stored&nbsp;in&nbsp;self.<strong>raw_requestline</strong>;&nbsp;the&nbsp;results<br>
+are&nbsp;in&nbsp;self.<strong>command</strong>,&nbsp;self.<strong>path</strong>,&nbsp;self.<strong>request_version</strong>&nbsp;and<br>
+self.<strong>headers</strong>.<br>
+&nbsp;<br>
+Return&nbsp;True&nbsp;for&nbsp;success,&nbsp;False&nbsp;for&nbsp;failure;&nbsp;on&nbsp;failure,&nbsp;an<br>
+error&nbsp;is&nbsp;sent&nbsp;back.</tt></dd></dl>
+
+<dl><dt><a name="MemoryCacheHTTPRequestHandler-send_error"><strong>send_error</strong></a>(self, code, message<font color="#909090">=None</font>)</dt><dd><tt>Send&nbsp;and&nbsp;log&nbsp;an&nbsp;error&nbsp;reply.<br>
+&nbsp;<br>
+Arguments&nbsp;are&nbsp;the&nbsp;error&nbsp;code,&nbsp;and&nbsp;a&nbsp;detailed&nbsp;message.<br>
+The&nbsp;detailed&nbsp;message&nbsp;defaults&nbsp;to&nbsp;the&nbsp;short&nbsp;entry&nbsp;matching&nbsp;the<br>
+response&nbsp;code.<br>
+&nbsp;<br>
+This&nbsp;sends&nbsp;an&nbsp;error&nbsp;response&nbsp;(so&nbsp;it&nbsp;must&nbsp;be&nbsp;called&nbsp;before&nbsp;any<br>
+output&nbsp;has&nbsp;been&nbsp;generated),&nbsp;logs&nbsp;the&nbsp;error,&nbsp;and&nbsp;finally&nbsp;sends<br>
+a&nbsp;piece&nbsp;of&nbsp;HTML&nbsp;explaining&nbsp;the&nbsp;error&nbsp;to&nbsp;the&nbsp;user.</tt></dd></dl>
+
+<dl><dt><a name="MemoryCacheHTTPRequestHandler-send_header"><strong>send_header</strong></a>(self, keyword, value)</dt><dd><tt>Send&nbsp;a&nbsp;MIME&nbsp;header.</tt></dd></dl>
+
+<dl><dt><a name="MemoryCacheHTTPRequestHandler-send_response"><strong>send_response</strong></a>(self, code, message<font color="#909090">=None</font>)</dt><dd><tt>Send&nbsp;the&nbsp;response&nbsp;header&nbsp;and&nbsp;log&nbsp;the&nbsp;response&nbsp;code.<br>
+&nbsp;<br>
+Also&nbsp;send&nbsp;two&nbsp;standard&nbsp;headers&nbsp;with&nbsp;the&nbsp;server&nbsp;software<br>
+version&nbsp;and&nbsp;the&nbsp;current&nbsp;date.</tt></dd></dl>
+
+<dl><dt><a name="MemoryCacheHTTPRequestHandler-version_string"><strong>version_string</strong></a>(self)</dt><dd><tt>Return&nbsp;the&nbsp;server&nbsp;software&nbsp;version&nbsp;string.</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="BaseHTTPServer.html#BaseHTTPRequestHandler">BaseHTTPServer.BaseHTTPRequestHandler</a>:<br>
+<dl><dt><strong>MessageClass</strong> = &lt;class mimetools.Message&gt;</dl>
+
+<dl><dt><strong>default_request_version</strong> = 'HTTP/0.9'</dl>
+
+<dl><dt><strong>error_content_type</strong> = 'text/html'</dl>
+
+<dl><dt><strong>error_message_format</strong> = '&lt;head&gt;<font color="#c040c0">\n</font>&lt;title&gt;Error response&lt;/title&gt;<font color="#c040c0">\n</font>&lt;/head&gt;<font color="#c040c0">\n</font>&lt;bo...ode explanation: %(code)s = %(explain)s.<font color="#c040c0">\n</font>&lt;/body&gt;<font color="#c040c0">\n</font>'</dl>
+
+<dl><dt><strong>monthname</strong> = [None, 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']</dl>
+
+<dl><dt><strong>responses</strong> = {100: ('Continue', 'Request received, please continue'), 101: ('Switching Protocols', 'Switching to new protocol; obey Upgrade header'), 200: ('OK', 'Request fulfilled, document follows'), 201: ('Created', 'Document created, URL follows'), 202: ('Accepted', 'Request accepted, processing continues off-line'), 203: ('Non-Authoritative Information', 'Request fulfilled from cache'), 204: ('No Content', 'Request fulfilled, nothing follows'), 205: ('Reset Content', 'Clear input form for further input.'), 206: ('Partial Content', 'Partial content follows.'), 300: ('Multiple Choices', 'Object has several resources -- see URI list'), ...}</dl>
+
+<dl><dt><strong>sys_version</strong> = 'Python/2.7.6'</dl>
+
+<dl><dt><strong>weekdayname</strong> = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']</dl>
+
+<hr>
+Methods inherited from <a href="SocketServer.html#StreamRequestHandler">SocketServer.StreamRequestHandler</a>:<br>
+<dl><dt><a name="MemoryCacheHTTPRequestHandler-finish"><strong>finish</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MemoryCacheHTTPRequestHandler-setup"><strong>setup</strong></a>(self)</dt></dl>
+
+<hr>
+Data and other attributes inherited from <a href="SocketServer.html#StreamRequestHandler">SocketServer.StreamRequestHandler</a>:<br>
+<dl><dt><strong>disable_nagle_algorithm</strong> = False</dl>
+
+<dl><dt><strong>rbufsize</strong> = -1</dl>
+
+<dl><dt><strong>timeout</strong> = None</dl>
+
+<hr>
+Methods inherited from <a href="SocketServer.html#BaseRequestHandler">SocketServer.BaseRequestHandler</a>:<br>
+<dl><dt><a name="MemoryCacheHTTPRequestHandler-__init__"><strong>__init__</strong></a>(self, request, client_address, server)</dt></dl>
+
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="MemoryCacheHTTPServer">class <strong>MemoryCacheHTTPServer</strong></a>(<a href="telemetry.core.local_server.html#LocalServer">telemetry.core.local_server.LocalServer</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.core.memory_cache_http_server.html#MemoryCacheHTTPServer">MemoryCacheHTTPServer</a></dd>
+<dd><a href="telemetry.core.local_server.html#LocalServer">telemetry.core.local_server.LocalServer</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="MemoryCacheHTTPServer-GetBackendStartupArgs"><strong>GetBackendStartupArgs</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MemoryCacheHTTPServer-UrlOf"><strong>UrlOf</strong></a>(self, path)</dt></dl>
+
+<dl><dt><a name="MemoryCacheHTTPServer-__init__"><strong>__init__</strong></a>(self, paths)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>paths</strong></dt>
+</dl>
+<dl><dt><strong>url</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.core.local_server.html#LocalServer">telemetry.core.local_server.LocalServer</a>:<br>
+<dl><dt><a name="MemoryCacheHTTPServer-Close"><strong>Close</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MemoryCacheHTTPServer-Start"><strong>Start</strong></a>(self, local_server_controller)</dt></dl>
+
+<dl><dt><a name="MemoryCacheHTTPServer-__del__"><strong>__del__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MemoryCacheHTTPServer-__enter__"><strong>__enter__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MemoryCacheHTTPServer-__exit__"><strong>__exit__</strong></a>(self, *args)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.core.local_server.html#LocalServer">telemetry.core.local_server.LocalServer</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>is_running</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="MemoryCacheHTTPServerBackend">class <strong>MemoryCacheHTTPServerBackend</strong></a>(<a href="telemetry.core.local_server.html#LocalServerBackend">telemetry.core.local_server.LocalServerBackend</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.core.memory_cache_http_server.html#MemoryCacheHTTPServerBackend">MemoryCacheHTTPServerBackend</a></dd>
+<dd><a href="telemetry.core.local_server.html#LocalServerBackend">telemetry.core.local_server.LocalServerBackend</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="MemoryCacheHTTPServerBackend-ServeForever"><strong>ServeForever</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MemoryCacheHTTPServerBackend-StartAndGetNamedPorts"><strong>StartAndGetNamedPorts</strong></a>(self, args)</dt></dl>
+
+<dl><dt><a name="MemoryCacheHTTPServerBackend-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.core.local_server.html#LocalServerBackend">telemetry.core.local_server.LocalServerBackend</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ResourceAndRange">class <strong>ResourceAndRange</strong></a>(<a href="__builtin__.html#tuple">__builtin__.tuple</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt><a href="#ResourceAndRange">ResourceAndRange</a>(resource,&nbsp;byte_range)<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.core.memory_cache_http_server.html#ResourceAndRange">ResourceAndRange</a></dd>
+<dd><a href="__builtin__.html#tuple">__builtin__.tuple</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="ResourceAndRange-__getnewargs__"><strong>__getnewargs__</strong></a>(self)</dt><dd><tt>Return&nbsp;self&nbsp;as&nbsp;a&nbsp;plain&nbsp;<a href="__builtin__.html#tuple">tuple</a>.&nbsp;&nbsp;Used&nbsp;by&nbsp;copy&nbsp;and&nbsp;pickle.</tt></dd></dl>
+
+<dl><dt><a name="ResourceAndRange-__getstate__"><strong>__getstate__</strong></a>(self)</dt><dd><tt>Exclude&nbsp;the&nbsp;OrderedDict&nbsp;from&nbsp;pickling</tt></dd></dl>
+
+<dl><dt><a name="ResourceAndRange-__repr__"><strong>__repr__</strong></a>(self)</dt><dd><tt>Return&nbsp;a&nbsp;nicely&nbsp;formatted&nbsp;representation&nbsp;string</tt></dd></dl>
+
+<dl><dt><a name="ResourceAndRange-_asdict"><strong>_asdict</strong></a>(self)</dt><dd><tt>Return&nbsp;a&nbsp;new&nbsp;OrderedDict&nbsp;which&nbsp;maps&nbsp;field&nbsp;names&nbsp;to&nbsp;their&nbsp;values</tt></dd></dl>
+
+<dl><dt><a name="ResourceAndRange-_replace"><strong>_replace</strong></a>(_self, **kwds)</dt><dd><tt>Return&nbsp;a&nbsp;new&nbsp;<a href="#ResourceAndRange">ResourceAndRange</a>&nbsp;object&nbsp;replacing&nbsp;specified&nbsp;fields&nbsp;with&nbsp;new&nbsp;values</tt></dd></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="ResourceAndRange-_make"><strong>_make</strong></a>(cls, iterable, new<font color="#909090">=&lt;built-in method __new__ of type object&gt;</font>, len<font color="#909090">=&lt;built-in function len&gt;</font>)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Make&nbsp;a&nbsp;new&nbsp;<a href="#ResourceAndRange">ResourceAndRange</a>&nbsp;object&nbsp;from&nbsp;a&nbsp;sequence&nbsp;or&nbsp;iterable</tt></dd></dl>
+
+<hr>
+Static methods defined here:<br>
+<dl><dt><a name="ResourceAndRange-__new__"><strong>__new__</strong></a>(_cls, resource, byte_range)</dt><dd><tt>Create&nbsp;new&nbsp;instance&nbsp;of&nbsp;<a href="#ResourceAndRange">ResourceAndRange</a>(resource,&nbsp;byte_range)</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>Return&nbsp;a&nbsp;new&nbsp;OrderedDict&nbsp;which&nbsp;maps&nbsp;field&nbsp;names&nbsp;to&nbsp;their&nbsp;values</tt></dd>
+</dl>
+<dl><dt><strong>byte_range</strong></dt>
+<dd><tt>Alias&nbsp;for&nbsp;field&nbsp;number&nbsp;1</tt></dd>
+</dl>
+<dl><dt><strong>resource</strong></dt>
+<dd><tt>Alias&nbsp;for&nbsp;field&nbsp;number&nbsp;0</tt></dd>
+</dl>
+<hr>
+Data and other attributes defined here:<br>
+<dl><dt><strong>_fields</strong> = ('resource', 'byte_range')</dl>
+
+<hr>
+Methods inherited from <a href="__builtin__.html#tuple">__builtin__.tuple</a>:<br>
+<dl><dt><a name="ResourceAndRange-__add__"><strong>__add__</strong></a>(...)</dt><dd><tt>x.<a href="#ResourceAndRange-__add__">__add__</a>(y)&nbsp;&lt;==&gt;&nbsp;x+y</tt></dd></dl>
+
+<dl><dt><a name="ResourceAndRange-__contains__"><strong>__contains__</strong></a>(...)</dt><dd><tt>x.<a href="#ResourceAndRange-__contains__">__contains__</a>(y)&nbsp;&lt;==&gt;&nbsp;y&nbsp;in&nbsp;x</tt></dd></dl>
+
+<dl><dt><a name="ResourceAndRange-__eq__"><strong>__eq__</strong></a>(...)</dt><dd><tt>x.<a href="#ResourceAndRange-__eq__">__eq__</a>(y)&nbsp;&lt;==&gt;&nbsp;x==y</tt></dd></dl>
+
+<dl><dt><a name="ResourceAndRange-__ge__"><strong>__ge__</strong></a>(...)</dt><dd><tt>x.<a href="#ResourceAndRange-__ge__">__ge__</a>(y)&nbsp;&lt;==&gt;&nbsp;x&gt;=y</tt></dd></dl>
+
+<dl><dt><a name="ResourceAndRange-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#ResourceAndRange-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="ResourceAndRange-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#ResourceAndRange-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="ResourceAndRange-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#ResourceAndRange-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="ResourceAndRange-__gt__"><strong>__gt__</strong></a>(...)</dt><dd><tt>x.<a href="#ResourceAndRange-__gt__">__gt__</a>(y)&nbsp;&lt;==&gt;&nbsp;x&gt;y</tt></dd></dl>
+
+<dl><dt><a name="ResourceAndRange-__hash__"><strong>__hash__</strong></a>(...)</dt><dd><tt>x.<a href="#ResourceAndRange-__hash__">__hash__</a>()&nbsp;&lt;==&gt;&nbsp;hash(x)</tt></dd></dl>
+
+<dl><dt><a name="ResourceAndRange-__iter__"><strong>__iter__</strong></a>(...)</dt><dd><tt>x.<a href="#ResourceAndRange-__iter__">__iter__</a>()&nbsp;&lt;==&gt;&nbsp;iter(x)</tt></dd></dl>
+
+<dl><dt><a name="ResourceAndRange-__le__"><strong>__le__</strong></a>(...)</dt><dd><tt>x.<a href="#ResourceAndRange-__le__">__le__</a>(y)&nbsp;&lt;==&gt;&nbsp;x&lt;=y</tt></dd></dl>
+
+<dl><dt><a name="ResourceAndRange-__len__"><strong>__len__</strong></a>(...)</dt><dd><tt>x.<a href="#ResourceAndRange-__len__">__len__</a>()&nbsp;&lt;==&gt;&nbsp;len(x)</tt></dd></dl>
+
+<dl><dt><a name="ResourceAndRange-__lt__"><strong>__lt__</strong></a>(...)</dt><dd><tt>x.<a href="#ResourceAndRange-__lt__">__lt__</a>(y)&nbsp;&lt;==&gt;&nbsp;x&lt;y</tt></dd></dl>
+
+<dl><dt><a name="ResourceAndRange-__mul__"><strong>__mul__</strong></a>(...)</dt><dd><tt>x.<a href="#ResourceAndRange-__mul__">__mul__</a>(n)&nbsp;&lt;==&gt;&nbsp;x*n</tt></dd></dl>
+
+<dl><dt><a name="ResourceAndRange-__ne__"><strong>__ne__</strong></a>(...)</dt><dd><tt>x.<a href="#ResourceAndRange-__ne__">__ne__</a>(y)&nbsp;&lt;==&gt;&nbsp;x!=y</tt></dd></dl>
+
+<dl><dt><a name="ResourceAndRange-__rmul__"><strong>__rmul__</strong></a>(...)</dt><dd><tt>x.<a href="#ResourceAndRange-__rmul__">__rmul__</a>(n)&nbsp;&lt;==&gt;&nbsp;n*x</tt></dd></dl>
+
+<dl><dt><a name="ResourceAndRange-__sizeof__"><strong>__sizeof__</strong></a>(...)</dt><dd><tt>T.<a href="#ResourceAndRange-__sizeof__">__sizeof__</a>()&nbsp;--&nbsp;size&nbsp;of&nbsp;T&nbsp;in&nbsp;memory,&nbsp;in&nbsp;bytes</tt></dd></dl>
+
+<dl><dt><a name="ResourceAndRange-count"><strong>count</strong></a>(...)</dt><dd><tt>T.<a href="#ResourceAndRange-count">count</a>(value)&nbsp;-&gt;&nbsp;integer&nbsp;--&nbsp;return&nbsp;number&nbsp;of&nbsp;occurrences&nbsp;of&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="ResourceAndRange-index"><strong>index</strong></a>(...)</dt><dd><tt>T.<a href="#ResourceAndRange-index">index</a>(value,&nbsp;[start,&nbsp;[stop]])&nbsp;-&gt;&nbsp;integer&nbsp;--&nbsp;return&nbsp;first&nbsp;index&nbsp;of&nbsp;value.<br>
+Raises&nbsp;ValueError&nbsp;if&nbsp;the&nbsp;value&nbsp;is&nbsp;not&nbsp;present.</tt></dd></dl>
+
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.core.network_controller.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.core.network_controller.html
new file mode 100644
index 0000000..01e3e24
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.core.network_controller.html
@@ -0,0 +1,62 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.core.network_controller</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.core.html"><font color="#ffffff">core</font></a>.network_controller</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/core/network_controller.py">telemetry/core/network_controller.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.core.network_controller.html#NetworkController">NetworkController</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="NetworkController">class <strong>NetworkController</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Control&nbsp;network&nbsp;settings&nbsp;and&nbsp;servers&nbsp;to&nbsp;simulate&nbsp;the&nbsp;Web.<br>
+&nbsp;<br>
+Network&nbsp;changes&nbsp;include&nbsp;forwarding&nbsp;device&nbsp;ports&nbsp;to&nbsp;host&nbsp;platform&nbsp;ports.<br>
+Web&nbsp;Page&nbsp;Replay&nbsp;is&nbsp;used&nbsp;to&nbsp;record&nbsp;and&nbsp;replay&nbsp;HTTP/HTTPS&nbsp;responses.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="NetworkController-SetReplayArgs"><strong>SetReplayArgs</strong></a>(self, archive_path, wpr_mode, netsim, extra_wpr_args, make_javascript_deterministic<font color="#909090">=False</font>)</dt><dd><tt>Save&nbsp;the&nbsp;arguments&nbsp;needed&nbsp;for&nbsp;replay.</tt></dd></dl>
+
+<dl><dt><a name="NetworkController-UpdateReplayForExistingBrowser"><strong>UpdateReplayForExistingBrowser</strong></a>(self)</dt><dd><tt>Restart&nbsp;replay&nbsp;if&nbsp;needed&nbsp;for&nbsp;an&nbsp;existing&nbsp;browser.<br>
+&nbsp;<br>
+TODO(slamm):&nbsp;Drop&nbsp;this&nbsp;method&nbsp;when&nbsp;the&nbsp;browser_backend&nbsp;dependencies&nbsp;are<br>
+moved&nbsp;to&nbsp;the&nbsp;platform.&nbsp;https://crbug.com/423962</tt></dd></dl>
+
+<dl><dt><a name="NetworkController-__init__"><strong>__init__</strong></a>(self, network_controller_backend)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.core.os_version.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.core.os_version.html
new file mode 100644
index 0000000..bb21b3f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.core.os_version.html
@@ -0,0 +1,350 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.core.os_version</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.core.html"><font color="#ffffff">core</font></a>.os_version</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/core/os_version.py">telemetry/core/os_version.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#str">__builtin__.str</a>(<a href="__builtin__.html#basestring">__builtin__.basestring</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.core.os_version.html#OSVersion">OSVersion</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="OSVersion">class <strong>OSVersion</strong></a>(<a href="__builtin__.html#str">__builtin__.str</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>#&nbsp;pylint:&nbsp;disable=W0212<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.core.os_version.html#OSVersion">OSVersion</a></dd>
+<dd><a href="__builtin__.html#str">__builtin__.str</a></dd>
+<dd><a href="__builtin__.html#basestring">__builtin__.basestring</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="OSVersion-__ge__"><strong>__ge__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="OSVersion-__gt__"><strong>__gt__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="OSVersion-__le__"><strong>__le__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="OSVersion-__lt__"><strong>__lt__</strong></a>(self, other)</dt></dl>
+
+<hr>
+Static methods defined here:<br>
+<dl><dt><a name="OSVersion-__new__"><strong>__new__</strong></a>(cls, friendly_name, sortable_name, *args, **kwargs)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="__builtin__.html#str">__builtin__.str</a>:<br>
+<dl><dt><a name="OSVersion-__add__"><strong>__add__</strong></a>(...)</dt><dd><tt>x.<a href="#OSVersion-__add__">__add__</a>(y)&nbsp;&lt;==&gt;&nbsp;x+y</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-__contains__"><strong>__contains__</strong></a>(...)</dt><dd><tt>x.<a href="#OSVersion-__contains__">__contains__</a>(y)&nbsp;&lt;==&gt;&nbsp;y&nbsp;in&nbsp;x</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-__eq__"><strong>__eq__</strong></a>(...)</dt><dd><tt>x.<a href="#OSVersion-__eq__">__eq__</a>(y)&nbsp;&lt;==&gt;&nbsp;x==y</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-__format__"><strong>__format__</strong></a>(...)</dt><dd><tt>S.<a href="#OSVersion-__format__">__format__</a>(format_spec)&nbsp;-&gt;&nbsp;string<br>
+&nbsp;<br>
+Return&nbsp;a&nbsp;formatted&nbsp;version&nbsp;of&nbsp;S&nbsp;as&nbsp;described&nbsp;by&nbsp;format_spec.</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#OSVersion-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#OSVersion-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-__getnewargs__"><strong>__getnewargs__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="OSVersion-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#OSVersion-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-__hash__"><strong>__hash__</strong></a>(...)</dt><dd><tt>x.<a href="#OSVersion-__hash__">__hash__</a>()&nbsp;&lt;==&gt;&nbsp;hash(x)</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-__len__"><strong>__len__</strong></a>(...)</dt><dd><tt>x.<a href="#OSVersion-__len__">__len__</a>()&nbsp;&lt;==&gt;&nbsp;len(x)</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-__mod__"><strong>__mod__</strong></a>(...)</dt><dd><tt>x.<a href="#OSVersion-__mod__">__mod__</a>(y)&nbsp;&lt;==&gt;&nbsp;x%y</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-__mul__"><strong>__mul__</strong></a>(...)</dt><dd><tt>x.<a href="#OSVersion-__mul__">__mul__</a>(n)&nbsp;&lt;==&gt;&nbsp;x*n</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-__ne__"><strong>__ne__</strong></a>(...)</dt><dd><tt>x.<a href="#OSVersion-__ne__">__ne__</a>(y)&nbsp;&lt;==&gt;&nbsp;x!=y</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#OSVersion-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-__rmod__"><strong>__rmod__</strong></a>(...)</dt><dd><tt>x.<a href="#OSVersion-__rmod__">__rmod__</a>(y)&nbsp;&lt;==&gt;&nbsp;y%x</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-__rmul__"><strong>__rmul__</strong></a>(...)</dt><dd><tt>x.<a href="#OSVersion-__rmul__">__rmul__</a>(n)&nbsp;&lt;==&gt;&nbsp;n*x</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-__sizeof__"><strong>__sizeof__</strong></a>(...)</dt><dd><tt>S.<a href="#OSVersion-__sizeof__">__sizeof__</a>()&nbsp;-&gt;&nbsp;size&nbsp;of&nbsp;S&nbsp;in&nbsp;memory,&nbsp;in&nbsp;bytes</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#OSVersion-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;<a href="__builtin__.html#str">str</a>(x)</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-capitalize"><strong>capitalize</strong></a>(...)</dt><dd><tt>S.<a href="#OSVersion-capitalize">capitalize</a>()&nbsp;-&gt;&nbsp;string<br>
+&nbsp;<br>
+Return&nbsp;a&nbsp;copy&nbsp;of&nbsp;the&nbsp;string&nbsp;S&nbsp;with&nbsp;only&nbsp;its&nbsp;first&nbsp;character<br>
+capitalized.</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-center"><strong>center</strong></a>(...)</dt><dd><tt>S.<a href="#OSVersion-center">center</a>(width[,&nbsp;fillchar])&nbsp;-&gt;&nbsp;string<br>
+&nbsp;<br>
+Return&nbsp;S&nbsp;centered&nbsp;in&nbsp;a&nbsp;string&nbsp;of&nbsp;length&nbsp;width.&nbsp;Padding&nbsp;is<br>
+done&nbsp;using&nbsp;the&nbsp;specified&nbsp;fill&nbsp;character&nbsp;(default&nbsp;is&nbsp;a&nbsp;space)</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-count"><strong>count</strong></a>(...)</dt><dd><tt>S.<a href="#OSVersion-count">count</a>(sub[,&nbsp;start[,&nbsp;end]])&nbsp;-&gt;&nbsp;int<br>
+&nbsp;<br>
+Return&nbsp;the&nbsp;number&nbsp;of&nbsp;non-overlapping&nbsp;occurrences&nbsp;of&nbsp;substring&nbsp;sub&nbsp;in<br>
+string&nbsp;S[start:end].&nbsp;&nbsp;Optional&nbsp;arguments&nbsp;start&nbsp;and&nbsp;end&nbsp;are&nbsp;interpreted<br>
+as&nbsp;in&nbsp;slice&nbsp;notation.</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-decode"><strong>decode</strong></a>(...)</dt><dd><tt>S.<a href="#OSVersion-decode">decode</a>([encoding[,errors]])&nbsp;-&gt;&nbsp;object<br>
+&nbsp;<br>
+Decodes&nbsp;S&nbsp;using&nbsp;the&nbsp;codec&nbsp;registered&nbsp;for&nbsp;encoding.&nbsp;encoding&nbsp;defaults<br>
+to&nbsp;the&nbsp;default&nbsp;encoding.&nbsp;errors&nbsp;may&nbsp;be&nbsp;given&nbsp;to&nbsp;set&nbsp;a&nbsp;different&nbsp;error<br>
+handling&nbsp;scheme.&nbsp;Default&nbsp;is&nbsp;'strict'&nbsp;meaning&nbsp;that&nbsp;encoding&nbsp;errors&nbsp;raise<br>
+a&nbsp;UnicodeDecodeError.&nbsp;Other&nbsp;possible&nbsp;values&nbsp;are&nbsp;'ignore'&nbsp;and&nbsp;'replace'<br>
+as&nbsp;well&nbsp;as&nbsp;any&nbsp;other&nbsp;name&nbsp;registered&nbsp;with&nbsp;codecs.register_error&nbsp;that&nbsp;is<br>
+able&nbsp;to&nbsp;handle&nbsp;UnicodeDecodeErrors.</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-encode"><strong>encode</strong></a>(...)</dt><dd><tt>S.<a href="#OSVersion-encode">encode</a>([encoding[,errors]])&nbsp;-&gt;&nbsp;object<br>
+&nbsp;<br>
+Encodes&nbsp;S&nbsp;using&nbsp;the&nbsp;codec&nbsp;registered&nbsp;for&nbsp;encoding.&nbsp;encoding&nbsp;defaults<br>
+to&nbsp;the&nbsp;default&nbsp;encoding.&nbsp;errors&nbsp;may&nbsp;be&nbsp;given&nbsp;to&nbsp;set&nbsp;a&nbsp;different&nbsp;error<br>
+handling&nbsp;scheme.&nbsp;Default&nbsp;is&nbsp;'strict'&nbsp;meaning&nbsp;that&nbsp;encoding&nbsp;errors&nbsp;raise<br>
+a&nbsp;UnicodeEncodeError.&nbsp;Other&nbsp;possible&nbsp;values&nbsp;are&nbsp;'ignore',&nbsp;'replace'&nbsp;and<br>
+'xmlcharrefreplace'&nbsp;as&nbsp;well&nbsp;as&nbsp;any&nbsp;other&nbsp;name&nbsp;registered&nbsp;with<br>
+codecs.register_error&nbsp;that&nbsp;is&nbsp;able&nbsp;to&nbsp;handle&nbsp;UnicodeEncodeErrors.</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-endswith"><strong>endswith</strong></a>(...)</dt><dd><tt>S.<a href="#OSVersion-endswith">endswith</a>(suffix[,&nbsp;start[,&nbsp;end]])&nbsp;-&gt;&nbsp;bool<br>
+&nbsp;<br>
+Return&nbsp;True&nbsp;if&nbsp;S&nbsp;ends&nbsp;with&nbsp;the&nbsp;specified&nbsp;suffix,&nbsp;False&nbsp;otherwise.<br>
+With&nbsp;optional&nbsp;start,&nbsp;test&nbsp;S&nbsp;beginning&nbsp;at&nbsp;that&nbsp;position.<br>
+With&nbsp;optional&nbsp;end,&nbsp;stop&nbsp;comparing&nbsp;S&nbsp;at&nbsp;that&nbsp;position.<br>
+suffix&nbsp;can&nbsp;also&nbsp;be&nbsp;a&nbsp;tuple&nbsp;of&nbsp;strings&nbsp;to&nbsp;try.</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-expandtabs"><strong>expandtabs</strong></a>(...)</dt><dd><tt>S.<a href="#OSVersion-expandtabs">expandtabs</a>([tabsize])&nbsp;-&gt;&nbsp;string<br>
+&nbsp;<br>
+Return&nbsp;a&nbsp;copy&nbsp;of&nbsp;S&nbsp;where&nbsp;all&nbsp;tab&nbsp;characters&nbsp;are&nbsp;expanded&nbsp;using&nbsp;spaces.<br>
+If&nbsp;tabsize&nbsp;is&nbsp;not&nbsp;given,&nbsp;a&nbsp;tab&nbsp;size&nbsp;of&nbsp;8&nbsp;characters&nbsp;is&nbsp;assumed.</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-find"><strong>find</strong></a>(...)</dt><dd><tt>S.<a href="#OSVersion-find">find</a>(sub&nbsp;[,start&nbsp;[,end]])&nbsp;-&gt;&nbsp;int<br>
+&nbsp;<br>
+Return&nbsp;the&nbsp;lowest&nbsp;index&nbsp;in&nbsp;S&nbsp;where&nbsp;substring&nbsp;sub&nbsp;is&nbsp;found,<br>
+such&nbsp;that&nbsp;sub&nbsp;is&nbsp;contained&nbsp;within&nbsp;S[start:end].&nbsp;&nbsp;Optional<br>
+arguments&nbsp;start&nbsp;and&nbsp;end&nbsp;are&nbsp;interpreted&nbsp;as&nbsp;in&nbsp;slice&nbsp;notation.<br>
+&nbsp;<br>
+Return&nbsp;-1&nbsp;on&nbsp;failure.</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-format"><strong>format</strong></a>(...)</dt><dd><tt>S.<a href="#OSVersion-format">format</a>(*args,&nbsp;**kwargs)&nbsp;-&gt;&nbsp;string<br>
+&nbsp;<br>
+Return&nbsp;a&nbsp;formatted&nbsp;version&nbsp;of&nbsp;S,&nbsp;using&nbsp;substitutions&nbsp;from&nbsp;args&nbsp;and&nbsp;kwargs.<br>
+The&nbsp;substitutions&nbsp;are&nbsp;identified&nbsp;by&nbsp;braces&nbsp;('{'&nbsp;and&nbsp;'}').</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-index"><strong>index</strong></a>(...)</dt><dd><tt>S.<a href="#OSVersion-index">index</a>(sub&nbsp;[,start&nbsp;[,end]])&nbsp;-&gt;&nbsp;int<br>
+&nbsp;<br>
+Like&nbsp;S.<a href="#OSVersion-find">find</a>()&nbsp;but&nbsp;raise&nbsp;ValueError&nbsp;when&nbsp;the&nbsp;substring&nbsp;is&nbsp;not&nbsp;found.</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-isalnum"><strong>isalnum</strong></a>(...)</dt><dd><tt>S.<a href="#OSVersion-isalnum">isalnum</a>()&nbsp;-&gt;&nbsp;bool<br>
+&nbsp;<br>
+Return&nbsp;True&nbsp;if&nbsp;all&nbsp;characters&nbsp;in&nbsp;S&nbsp;are&nbsp;alphanumeric<br>
+and&nbsp;there&nbsp;is&nbsp;at&nbsp;least&nbsp;one&nbsp;character&nbsp;in&nbsp;S,&nbsp;False&nbsp;otherwise.</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-isalpha"><strong>isalpha</strong></a>(...)</dt><dd><tt>S.<a href="#OSVersion-isalpha">isalpha</a>()&nbsp;-&gt;&nbsp;bool<br>
+&nbsp;<br>
+Return&nbsp;True&nbsp;if&nbsp;all&nbsp;characters&nbsp;in&nbsp;S&nbsp;are&nbsp;alphabetic<br>
+and&nbsp;there&nbsp;is&nbsp;at&nbsp;least&nbsp;one&nbsp;character&nbsp;in&nbsp;S,&nbsp;False&nbsp;otherwise.</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-isdigit"><strong>isdigit</strong></a>(...)</dt><dd><tt>S.<a href="#OSVersion-isdigit">isdigit</a>()&nbsp;-&gt;&nbsp;bool<br>
+&nbsp;<br>
+Return&nbsp;True&nbsp;if&nbsp;all&nbsp;characters&nbsp;in&nbsp;S&nbsp;are&nbsp;digits<br>
+and&nbsp;there&nbsp;is&nbsp;at&nbsp;least&nbsp;one&nbsp;character&nbsp;in&nbsp;S,&nbsp;False&nbsp;otherwise.</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-islower"><strong>islower</strong></a>(...)</dt><dd><tt>S.<a href="#OSVersion-islower">islower</a>()&nbsp;-&gt;&nbsp;bool<br>
+&nbsp;<br>
+Return&nbsp;True&nbsp;if&nbsp;all&nbsp;cased&nbsp;characters&nbsp;in&nbsp;S&nbsp;are&nbsp;lowercase&nbsp;and&nbsp;there&nbsp;is<br>
+at&nbsp;least&nbsp;one&nbsp;cased&nbsp;character&nbsp;in&nbsp;S,&nbsp;False&nbsp;otherwise.</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-isspace"><strong>isspace</strong></a>(...)</dt><dd><tt>S.<a href="#OSVersion-isspace">isspace</a>()&nbsp;-&gt;&nbsp;bool<br>
+&nbsp;<br>
+Return&nbsp;True&nbsp;if&nbsp;all&nbsp;characters&nbsp;in&nbsp;S&nbsp;are&nbsp;whitespace<br>
+and&nbsp;there&nbsp;is&nbsp;at&nbsp;least&nbsp;one&nbsp;character&nbsp;in&nbsp;S,&nbsp;False&nbsp;otherwise.</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-istitle"><strong>istitle</strong></a>(...)</dt><dd><tt>S.<a href="#OSVersion-istitle">istitle</a>()&nbsp;-&gt;&nbsp;bool<br>
+&nbsp;<br>
+Return&nbsp;True&nbsp;if&nbsp;S&nbsp;is&nbsp;a&nbsp;titlecased&nbsp;string&nbsp;and&nbsp;there&nbsp;is&nbsp;at&nbsp;least&nbsp;one<br>
+character&nbsp;in&nbsp;S,&nbsp;i.e.&nbsp;uppercase&nbsp;characters&nbsp;may&nbsp;only&nbsp;follow&nbsp;uncased<br>
+characters&nbsp;and&nbsp;lowercase&nbsp;characters&nbsp;only&nbsp;cased&nbsp;ones.&nbsp;Return&nbsp;False<br>
+otherwise.</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-isupper"><strong>isupper</strong></a>(...)</dt><dd><tt>S.<a href="#OSVersion-isupper">isupper</a>()&nbsp;-&gt;&nbsp;bool<br>
+&nbsp;<br>
+Return&nbsp;True&nbsp;if&nbsp;all&nbsp;cased&nbsp;characters&nbsp;in&nbsp;S&nbsp;are&nbsp;uppercase&nbsp;and&nbsp;there&nbsp;is<br>
+at&nbsp;least&nbsp;one&nbsp;cased&nbsp;character&nbsp;in&nbsp;S,&nbsp;False&nbsp;otherwise.</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-join"><strong>join</strong></a>(...)</dt><dd><tt>S.<a href="#OSVersion-join">join</a>(iterable)&nbsp;-&gt;&nbsp;string<br>
+&nbsp;<br>
+Return&nbsp;a&nbsp;string&nbsp;which&nbsp;is&nbsp;the&nbsp;concatenation&nbsp;of&nbsp;the&nbsp;strings&nbsp;in&nbsp;the<br>
+iterable.&nbsp;&nbsp;The&nbsp;separator&nbsp;between&nbsp;elements&nbsp;is&nbsp;S.</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-ljust"><strong>ljust</strong></a>(...)</dt><dd><tt>S.<a href="#OSVersion-ljust">ljust</a>(width[,&nbsp;fillchar])&nbsp;-&gt;&nbsp;string<br>
+&nbsp;<br>
+Return&nbsp;S&nbsp;left-justified&nbsp;in&nbsp;a&nbsp;string&nbsp;of&nbsp;length&nbsp;width.&nbsp;Padding&nbsp;is<br>
+done&nbsp;using&nbsp;the&nbsp;specified&nbsp;fill&nbsp;character&nbsp;(default&nbsp;is&nbsp;a&nbsp;space).</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-lower"><strong>lower</strong></a>(...)</dt><dd><tt>S.<a href="#OSVersion-lower">lower</a>()&nbsp;-&gt;&nbsp;string<br>
+&nbsp;<br>
+Return&nbsp;a&nbsp;copy&nbsp;of&nbsp;the&nbsp;string&nbsp;S&nbsp;converted&nbsp;to&nbsp;lowercase.</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-lstrip"><strong>lstrip</strong></a>(...)</dt><dd><tt>S.<a href="#OSVersion-lstrip">lstrip</a>([chars])&nbsp;-&gt;&nbsp;string&nbsp;or&nbsp;unicode<br>
+&nbsp;<br>
+Return&nbsp;a&nbsp;copy&nbsp;of&nbsp;the&nbsp;string&nbsp;S&nbsp;with&nbsp;leading&nbsp;whitespace&nbsp;removed.<br>
+If&nbsp;chars&nbsp;is&nbsp;given&nbsp;and&nbsp;not&nbsp;None,&nbsp;remove&nbsp;characters&nbsp;in&nbsp;chars&nbsp;instead.<br>
+If&nbsp;chars&nbsp;is&nbsp;unicode,&nbsp;S&nbsp;will&nbsp;be&nbsp;converted&nbsp;to&nbsp;unicode&nbsp;before&nbsp;stripping</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-partition"><strong>partition</strong></a>(...)</dt><dd><tt>S.<a href="#OSVersion-partition">partition</a>(sep)&nbsp;-&gt;&nbsp;(head,&nbsp;sep,&nbsp;tail)<br>
+&nbsp;<br>
+Search&nbsp;for&nbsp;the&nbsp;separator&nbsp;sep&nbsp;in&nbsp;S,&nbsp;and&nbsp;return&nbsp;the&nbsp;part&nbsp;before&nbsp;it,<br>
+the&nbsp;separator&nbsp;itself,&nbsp;and&nbsp;the&nbsp;part&nbsp;after&nbsp;it.&nbsp;&nbsp;If&nbsp;the&nbsp;separator&nbsp;is&nbsp;not<br>
+found,&nbsp;return&nbsp;S&nbsp;and&nbsp;two&nbsp;empty&nbsp;strings.</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-replace"><strong>replace</strong></a>(...)</dt><dd><tt>S.<a href="#OSVersion-replace">replace</a>(old,&nbsp;new[,&nbsp;count])&nbsp;-&gt;&nbsp;string<br>
+&nbsp;<br>
+Return&nbsp;a&nbsp;copy&nbsp;of&nbsp;string&nbsp;S&nbsp;with&nbsp;all&nbsp;occurrences&nbsp;of&nbsp;substring<br>
+old&nbsp;replaced&nbsp;by&nbsp;new.&nbsp;&nbsp;If&nbsp;the&nbsp;optional&nbsp;argument&nbsp;count&nbsp;is<br>
+given,&nbsp;only&nbsp;the&nbsp;first&nbsp;count&nbsp;occurrences&nbsp;are&nbsp;replaced.</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-rfind"><strong>rfind</strong></a>(...)</dt><dd><tt>S.<a href="#OSVersion-rfind">rfind</a>(sub&nbsp;[,start&nbsp;[,end]])&nbsp;-&gt;&nbsp;int<br>
+&nbsp;<br>
+Return&nbsp;the&nbsp;highest&nbsp;index&nbsp;in&nbsp;S&nbsp;where&nbsp;substring&nbsp;sub&nbsp;is&nbsp;found,<br>
+such&nbsp;that&nbsp;sub&nbsp;is&nbsp;contained&nbsp;within&nbsp;S[start:end].&nbsp;&nbsp;Optional<br>
+arguments&nbsp;start&nbsp;and&nbsp;end&nbsp;are&nbsp;interpreted&nbsp;as&nbsp;in&nbsp;slice&nbsp;notation.<br>
+&nbsp;<br>
+Return&nbsp;-1&nbsp;on&nbsp;failure.</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-rindex"><strong>rindex</strong></a>(...)</dt><dd><tt>S.<a href="#OSVersion-rindex">rindex</a>(sub&nbsp;[,start&nbsp;[,end]])&nbsp;-&gt;&nbsp;int<br>
+&nbsp;<br>
+Like&nbsp;S.<a href="#OSVersion-rfind">rfind</a>()&nbsp;but&nbsp;raise&nbsp;ValueError&nbsp;when&nbsp;the&nbsp;substring&nbsp;is&nbsp;not&nbsp;found.</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-rjust"><strong>rjust</strong></a>(...)</dt><dd><tt>S.<a href="#OSVersion-rjust">rjust</a>(width[,&nbsp;fillchar])&nbsp;-&gt;&nbsp;string<br>
+&nbsp;<br>
+Return&nbsp;S&nbsp;right-justified&nbsp;in&nbsp;a&nbsp;string&nbsp;of&nbsp;length&nbsp;width.&nbsp;Padding&nbsp;is<br>
+done&nbsp;using&nbsp;the&nbsp;specified&nbsp;fill&nbsp;character&nbsp;(default&nbsp;is&nbsp;a&nbsp;space)</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-rpartition"><strong>rpartition</strong></a>(...)</dt><dd><tt>S.<a href="#OSVersion-rpartition">rpartition</a>(sep)&nbsp;-&gt;&nbsp;(head,&nbsp;sep,&nbsp;tail)<br>
+&nbsp;<br>
+Search&nbsp;for&nbsp;the&nbsp;separator&nbsp;sep&nbsp;in&nbsp;S,&nbsp;starting&nbsp;at&nbsp;the&nbsp;end&nbsp;of&nbsp;S,&nbsp;and&nbsp;return<br>
+the&nbsp;part&nbsp;before&nbsp;it,&nbsp;the&nbsp;separator&nbsp;itself,&nbsp;and&nbsp;the&nbsp;part&nbsp;after&nbsp;it.&nbsp;&nbsp;If&nbsp;the<br>
+separator&nbsp;is&nbsp;not&nbsp;found,&nbsp;return&nbsp;two&nbsp;empty&nbsp;strings&nbsp;and&nbsp;S.</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-rsplit"><strong>rsplit</strong></a>(...)</dt><dd><tt>S.<a href="#OSVersion-rsplit">rsplit</a>([sep&nbsp;[,maxsplit]])&nbsp;-&gt;&nbsp;list&nbsp;of&nbsp;strings<br>
+&nbsp;<br>
+Return&nbsp;a&nbsp;list&nbsp;of&nbsp;the&nbsp;words&nbsp;in&nbsp;the&nbsp;string&nbsp;S,&nbsp;using&nbsp;sep&nbsp;as&nbsp;the<br>
+delimiter&nbsp;string,&nbsp;starting&nbsp;at&nbsp;the&nbsp;end&nbsp;of&nbsp;the&nbsp;string&nbsp;and&nbsp;working<br>
+to&nbsp;the&nbsp;front.&nbsp;&nbsp;If&nbsp;maxsplit&nbsp;is&nbsp;given,&nbsp;at&nbsp;most&nbsp;maxsplit&nbsp;splits&nbsp;are<br>
+done.&nbsp;If&nbsp;sep&nbsp;is&nbsp;not&nbsp;specified&nbsp;or&nbsp;is&nbsp;None,&nbsp;any&nbsp;whitespace&nbsp;string<br>
+is&nbsp;a&nbsp;separator.</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-rstrip"><strong>rstrip</strong></a>(...)</dt><dd><tt>S.<a href="#OSVersion-rstrip">rstrip</a>([chars])&nbsp;-&gt;&nbsp;string&nbsp;or&nbsp;unicode<br>
+&nbsp;<br>
+Return&nbsp;a&nbsp;copy&nbsp;of&nbsp;the&nbsp;string&nbsp;S&nbsp;with&nbsp;trailing&nbsp;whitespace&nbsp;removed.<br>
+If&nbsp;chars&nbsp;is&nbsp;given&nbsp;and&nbsp;not&nbsp;None,&nbsp;remove&nbsp;characters&nbsp;in&nbsp;chars&nbsp;instead.<br>
+If&nbsp;chars&nbsp;is&nbsp;unicode,&nbsp;S&nbsp;will&nbsp;be&nbsp;converted&nbsp;to&nbsp;unicode&nbsp;before&nbsp;stripping</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-split"><strong>split</strong></a>(...)</dt><dd><tt>S.<a href="#OSVersion-split">split</a>([sep&nbsp;[,maxsplit]])&nbsp;-&gt;&nbsp;list&nbsp;of&nbsp;strings<br>
+&nbsp;<br>
+Return&nbsp;a&nbsp;list&nbsp;of&nbsp;the&nbsp;words&nbsp;in&nbsp;the&nbsp;string&nbsp;S,&nbsp;using&nbsp;sep&nbsp;as&nbsp;the<br>
+delimiter&nbsp;string.&nbsp;&nbsp;If&nbsp;maxsplit&nbsp;is&nbsp;given,&nbsp;at&nbsp;most&nbsp;maxsplit<br>
+splits&nbsp;are&nbsp;done.&nbsp;If&nbsp;sep&nbsp;is&nbsp;not&nbsp;specified&nbsp;or&nbsp;is&nbsp;None,&nbsp;any<br>
+whitespace&nbsp;string&nbsp;is&nbsp;a&nbsp;separator&nbsp;and&nbsp;empty&nbsp;strings&nbsp;are&nbsp;removed<br>
+from&nbsp;the&nbsp;result.</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-splitlines"><strong>splitlines</strong></a>(...)</dt><dd><tt>S.<a href="#OSVersion-splitlines">splitlines</a>(keepends=False)&nbsp;-&gt;&nbsp;list&nbsp;of&nbsp;strings<br>
+&nbsp;<br>
+Return&nbsp;a&nbsp;list&nbsp;of&nbsp;the&nbsp;lines&nbsp;in&nbsp;S,&nbsp;breaking&nbsp;at&nbsp;line&nbsp;boundaries.<br>
+Line&nbsp;breaks&nbsp;are&nbsp;not&nbsp;included&nbsp;in&nbsp;the&nbsp;resulting&nbsp;list&nbsp;unless&nbsp;keepends<br>
+is&nbsp;given&nbsp;and&nbsp;true.</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-startswith"><strong>startswith</strong></a>(...)</dt><dd><tt>S.<a href="#OSVersion-startswith">startswith</a>(prefix[,&nbsp;start[,&nbsp;end]])&nbsp;-&gt;&nbsp;bool<br>
+&nbsp;<br>
+Return&nbsp;True&nbsp;if&nbsp;S&nbsp;starts&nbsp;with&nbsp;the&nbsp;specified&nbsp;prefix,&nbsp;False&nbsp;otherwise.<br>
+With&nbsp;optional&nbsp;start,&nbsp;test&nbsp;S&nbsp;beginning&nbsp;at&nbsp;that&nbsp;position.<br>
+With&nbsp;optional&nbsp;end,&nbsp;stop&nbsp;comparing&nbsp;S&nbsp;at&nbsp;that&nbsp;position.<br>
+prefix&nbsp;can&nbsp;also&nbsp;be&nbsp;a&nbsp;tuple&nbsp;of&nbsp;strings&nbsp;to&nbsp;try.</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-strip"><strong>strip</strong></a>(...)</dt><dd><tt>S.<a href="#OSVersion-strip">strip</a>([chars])&nbsp;-&gt;&nbsp;string&nbsp;or&nbsp;unicode<br>
+&nbsp;<br>
+Return&nbsp;a&nbsp;copy&nbsp;of&nbsp;the&nbsp;string&nbsp;S&nbsp;with&nbsp;leading&nbsp;and&nbsp;trailing<br>
+whitespace&nbsp;removed.<br>
+If&nbsp;chars&nbsp;is&nbsp;given&nbsp;and&nbsp;not&nbsp;None,&nbsp;remove&nbsp;characters&nbsp;in&nbsp;chars&nbsp;instead.<br>
+If&nbsp;chars&nbsp;is&nbsp;unicode,&nbsp;S&nbsp;will&nbsp;be&nbsp;converted&nbsp;to&nbsp;unicode&nbsp;before&nbsp;stripping</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-swapcase"><strong>swapcase</strong></a>(...)</dt><dd><tt>S.<a href="#OSVersion-swapcase">swapcase</a>()&nbsp;-&gt;&nbsp;string<br>
+&nbsp;<br>
+Return&nbsp;a&nbsp;copy&nbsp;of&nbsp;the&nbsp;string&nbsp;S&nbsp;with&nbsp;uppercase&nbsp;characters<br>
+converted&nbsp;to&nbsp;lowercase&nbsp;and&nbsp;vice&nbsp;versa.</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-title"><strong>title</strong></a>(...)</dt><dd><tt>S.<a href="#OSVersion-title">title</a>()&nbsp;-&gt;&nbsp;string<br>
+&nbsp;<br>
+Return&nbsp;a&nbsp;titlecased&nbsp;version&nbsp;of&nbsp;S,&nbsp;i.e.&nbsp;words&nbsp;start&nbsp;with&nbsp;uppercase<br>
+characters,&nbsp;all&nbsp;remaining&nbsp;cased&nbsp;characters&nbsp;have&nbsp;lowercase.</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-translate"><strong>translate</strong></a>(...)</dt><dd><tt>S.<a href="#OSVersion-translate">translate</a>(table&nbsp;[,deletechars])&nbsp;-&gt;&nbsp;string<br>
+&nbsp;<br>
+Return&nbsp;a&nbsp;copy&nbsp;of&nbsp;the&nbsp;string&nbsp;S,&nbsp;where&nbsp;all&nbsp;characters&nbsp;occurring<br>
+in&nbsp;the&nbsp;optional&nbsp;argument&nbsp;deletechars&nbsp;are&nbsp;removed,&nbsp;and&nbsp;the<br>
+remaining&nbsp;characters&nbsp;have&nbsp;been&nbsp;mapped&nbsp;through&nbsp;the&nbsp;given<br>
+translation&nbsp;table,&nbsp;which&nbsp;must&nbsp;be&nbsp;a&nbsp;string&nbsp;of&nbsp;length&nbsp;256&nbsp;or&nbsp;None.<br>
+If&nbsp;the&nbsp;table&nbsp;argument&nbsp;is&nbsp;None,&nbsp;no&nbsp;translation&nbsp;is&nbsp;applied&nbsp;and<br>
+the&nbsp;operation&nbsp;simply&nbsp;removes&nbsp;the&nbsp;characters&nbsp;in&nbsp;deletechars.</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-upper"><strong>upper</strong></a>(...)</dt><dd><tt>S.<a href="#OSVersion-upper">upper</a>()&nbsp;-&gt;&nbsp;string<br>
+&nbsp;<br>
+Return&nbsp;a&nbsp;copy&nbsp;of&nbsp;the&nbsp;string&nbsp;S&nbsp;converted&nbsp;to&nbsp;uppercase.</tt></dd></dl>
+
+<dl><dt><a name="OSVersion-zfill"><strong>zfill</strong></a>(...)</dt><dd><tt>S.<a href="#OSVersion-zfill">zfill</a>(width)&nbsp;-&gt;&nbsp;string<br>
+&nbsp;<br>
+Pad&nbsp;a&nbsp;numeric&nbsp;string&nbsp;S&nbsp;with&nbsp;zeros&nbsp;on&nbsp;the&nbsp;left,&nbsp;to&nbsp;fill&nbsp;a&nbsp;field<br>
+of&nbsp;the&nbsp;specified&nbsp;width.&nbsp;&nbsp;The&nbsp;string&nbsp;S&nbsp;is&nbsp;never&nbsp;truncated.</tt></dd></dl>
+
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>ELCAPITAN</strong> = 'elcapitan'<br>
+<strong>LEOPARD</strong> = 'leopard'<br>
+<strong>LION</strong> = 'lion'<br>
+<strong>MAVERICKS</strong> = 'mavericks'<br>
+<strong>MOUNTAINLION</strong> = 'mountainlion'<br>
+<strong>SNOWLEOPARD</strong> = 'snowleopard'<br>
+<strong>VISTA</strong> = 'vista'<br>
+<strong>WIN7</strong> = 'win7'<br>
+<strong>WIN8</strong> = 'win8'<br>
+<strong>XP</strong> = 'xp'<br>
+<strong>YOSEMITE</strong> = 'yosemite'</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.core.platform.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.core.platform.html
new file mode 100644
index 0000000..46f4e38
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.core.platform.html
@@ -0,0 +1,269 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.core.platform</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.core.html"><font color="#ffffff">core</font></a>.platform</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/core/platform.py">telemetry/core/platform.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2012&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.core.discover.html">telemetry.core.discover</a><br>
+<a href="telemetry.core.local_server.html">telemetry.core.local_server</a><br>
+<a href="telemetry.core.memory_cache_http_server.html">telemetry.core.memory_cache_http_server</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.network_controller.html">telemetry.core.network_controller</a><br>
+<a href="os.html">os</a><br>
+<a href="telemetry.internal.platform.platform_backend.html">telemetry.internal.platform.platform_backend</a><br>
+</td><td width="25%" valign=top><a href="logging.html">logging</a><br>
+<a href="telemetry.core.tracing_controller.html">telemetry.core.tracing_controller</a><br>
+<a href="telemetry.core.util.html">telemetry.core.util</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.core.platform.html#Platform">Platform</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="Platform">class <strong>Platform</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>The&nbsp;platform&nbsp;that&nbsp;the&nbsp;target&nbsp;browser&nbsp;is&nbsp;running&nbsp;on.<br>
+&nbsp;<br>
+Provides&nbsp;a&nbsp;limited&nbsp;interface&nbsp;to&nbsp;interact&nbsp;with&nbsp;the&nbsp;platform&nbsp;itself,&nbsp;where<br>
+possible.&nbsp;It's&nbsp;important&nbsp;to&nbsp;note&nbsp;that&nbsp;platforms&nbsp;may&nbsp;not&nbsp;provide&nbsp;a&nbsp;specific<br>
+API,&nbsp;so&nbsp;check&nbsp;with&nbsp;IsFooBar()&nbsp;for&nbsp;availability.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="Platform-CanCaptureVideo"><strong>CanCaptureVideo</strong></a>(self)</dt><dd><tt>Returns&nbsp;a&nbsp;bool&nbsp;indicating&nbsp;whether&nbsp;the&nbsp;platform&nbsp;supports&nbsp;video&nbsp;capture.</tt></dd></dl>
+
+<dl><dt><a name="Platform-CanFlushIndividualFilesFromSystemCache"><strong>CanFlushIndividualFilesFromSystemCache</strong></a>(self)</dt><dd><tt>Returns&nbsp;true&nbsp;if&nbsp;the&nbsp;disk&nbsp;cache&nbsp;can&nbsp;be&nbsp;flushed&nbsp;for&nbsp;specific&nbsp;files.</tt></dd></dl>
+
+<dl><dt><a name="Platform-CanLaunchApplication"><strong>CanLaunchApplication</strong></a>(self, application)</dt><dd><tt>Returns&nbsp;whether&nbsp;the&nbsp;platform&nbsp;can&nbsp;launch&nbsp;the&nbsp;given&nbsp;application.</tt></dd></dl>
+
+<dl><dt><a name="Platform-CanMeasurePerApplicationPower"><strong>CanMeasurePerApplicationPower</strong></a>(self)</dt><dd><tt>Returns&nbsp;True&nbsp;if&nbsp;the&nbsp;power&nbsp;monitor&nbsp;can&nbsp;measure&nbsp;power&nbsp;for&nbsp;the&nbsp;target<br>
+application&nbsp;in&nbsp;isolation.&nbsp;False&nbsp;if&nbsp;power&nbsp;measurement&nbsp;is&nbsp;for&nbsp;full&nbsp;system<br>
+energy&nbsp;consumption.</tt></dd></dl>
+
+<dl><dt><a name="Platform-CanMonitorNetworkData"><strong>CanMonitorNetworkData</strong></a>(self)</dt><dd><tt>Returns&nbsp;true&nbsp;if&nbsp;network&nbsp;data&nbsp;can&nbsp;be&nbsp;retrieved,&nbsp;false&nbsp;otherwise.</tt></dd></dl>
+
+<dl><dt><a name="Platform-CanMonitorPower"><strong>CanMonitorPower</strong></a>(self)</dt><dd><tt>Returns&nbsp;True&nbsp;iff&nbsp;power&nbsp;can&nbsp;be&nbsp;monitored&nbsp;asynchronously&nbsp;via<br>
+<a href="#Platform-StartMonitoringPower">StartMonitoringPower</a>()&nbsp;and&nbsp;<a href="#Platform-StopMonitoringPower">StopMonitoringPower</a>().</tt></dd></dl>
+
+<dl><dt><a name="Platform-CanMonitorThermalThrottling"><strong>CanMonitorThermalThrottling</strong></a>(self)</dt><dd><tt>Platforms&nbsp;may&nbsp;be&nbsp;able&nbsp;to&nbsp;detect&nbsp;thermal&nbsp;throttling.<br>
+&nbsp;<br>
+Some&nbsp;fan-less&nbsp;computers&nbsp;go&nbsp;into&nbsp;a&nbsp;reduced&nbsp;performance&nbsp;mode&nbsp;when&nbsp;their&nbsp;heat<br>
+exceeds&nbsp;a&nbsp;certain&nbsp;threshold.&nbsp;Performance&nbsp;tests&nbsp;in&nbsp;particular&nbsp;should&nbsp;use&nbsp;this<br>
+API&nbsp;to&nbsp;detect&nbsp;if&nbsp;this&nbsp;has&nbsp;happened&nbsp;and&nbsp;interpret&nbsp;results&nbsp;accordingly.</tt></dd></dl>
+
+<dl><dt><a name="Platform-CanTakeScreenshot"><strong>CanTakeScreenshot</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="Platform-CooperativelyShutdown"><strong>CooperativelyShutdown</strong></a>(self, proc, app_name)</dt><dd><tt>Cooperatively&nbsp;shut&nbsp;down&nbsp;the&nbsp;given&nbsp;process&nbsp;from&nbsp;subprocess.Popen.<br>
+&nbsp;<br>
+Currently&nbsp;this&nbsp;is&nbsp;only&nbsp;implemented&nbsp;on&nbsp;Windows.&nbsp;See<br>
+crbug.com/424024&nbsp;for&nbsp;background&nbsp;on&nbsp;why&nbsp;it&nbsp;was&nbsp;added.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;proc:&nbsp;a&nbsp;process&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;returned&nbsp;from&nbsp;subprocess.Popen.<br>
+&nbsp;&nbsp;app_name:&nbsp;on&nbsp;Windows,&nbsp;is&nbsp;the&nbsp;prefix&nbsp;of&nbsp;the&nbsp;application's&nbsp;window<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class&nbsp;name&nbsp;that&nbsp;should&nbsp;be&nbsp;searched&nbsp;for.&nbsp;This&nbsp;helps&nbsp;ensure<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;that&nbsp;only&nbsp;the&nbsp;application's&nbsp;windows&nbsp;are&nbsp;closed.<br>
+&nbsp;<br>
+Returns&nbsp;True&nbsp;if&nbsp;it&nbsp;is&nbsp;believed&nbsp;the&nbsp;attempt&nbsp;succeeded.</tt></dd></dl>
+
+<dl><dt><a name="Platform-FlushDnsCache"><strong>FlushDnsCache</strong></a>(self)</dt><dd><tt>Flushes&nbsp;the&nbsp;OS's&nbsp;DNS&nbsp;cache&nbsp;completely.<br>
+&nbsp;<br>
+This&nbsp;function&nbsp;may&nbsp;require&nbsp;root&nbsp;or&nbsp;administrator&nbsp;access.</tt></dd></dl>
+
+<dl><dt><a name="Platform-FlushEntireSystemCache"><strong>FlushEntireSystemCache</strong></a>(self)</dt><dd><tt>Flushes&nbsp;the&nbsp;OS's&nbsp;file&nbsp;cache&nbsp;completely.<br>
+&nbsp;<br>
+This&nbsp;function&nbsp;may&nbsp;require&nbsp;root&nbsp;or&nbsp;administrator&nbsp;access.</tt></dd></dl>
+
+<dl><dt><a name="Platform-FlushSystemCacheForDirectory"><strong>FlushSystemCacheForDirectory</strong></a>(self, directory)</dt><dd><tt>Flushes&nbsp;the&nbsp;OS's&nbsp;file&nbsp;cache&nbsp;for&nbsp;the&nbsp;specified&nbsp;directory.<br>
+&nbsp;<br>
+This&nbsp;function&nbsp;does&nbsp;not&nbsp;require&nbsp;root&nbsp;or&nbsp;administrator&nbsp;access.</tt></dd></dl>
+
+<dl><dt><a name="Platform-GetArchName"><strong>GetArchName</strong></a>(self)</dt><dd><tt>Returns&nbsp;a&nbsp;string&nbsp;description&nbsp;of&nbsp;the&nbsp;<a href="#Platform">Platform</a>&nbsp;architecture.<br>
+&nbsp;<br>
+Examples:&nbsp;x86_64&nbsp;(posix),&nbsp;AMD64&nbsp;(win),&nbsp;armeabi-v7a,&nbsp;x86</tt></dd></dl>
+
+<dl><dt><a name="Platform-GetDeviceTypeName"><strong>GetDeviceTypeName</strong></a>(self)</dt><dd><tt>Returns&nbsp;a&nbsp;string&nbsp;description&nbsp;of&nbsp;the&nbsp;<a href="#Platform">Platform</a>&nbsp;device,&nbsp;or&nbsp;None.<br>
+&nbsp;<br>
+Examples:&nbsp;Nexus&nbsp;7,&nbsp;Nexus&nbsp;6,&nbsp;Desktop</tt></dd></dl>
+
+<dl><dt><a name="Platform-GetNetworkData"><strong>GetNetworkData</strong></a>(self, browser)</dt><dd><tt>Get&nbsp;current&nbsp;network&nbsp;data.<br>
+Returns:<br>
+&nbsp;&nbsp;Tuple&nbsp;of&nbsp;(sent_data,&nbsp;received_data)&nbsp;in&nbsp;kb&nbsp;if&nbsp;data&nbsp;can&nbsp;be&nbsp;found,<br>
+&nbsp;&nbsp;None&nbsp;otherwise.</tt></dd></dl>
+
+<dl><dt><a name="Platform-GetOSName"><strong>GetOSName</strong></a>(self)</dt><dd><tt>Returns&nbsp;a&nbsp;string&nbsp;description&nbsp;of&nbsp;the&nbsp;<a href="#Platform">Platform</a>&nbsp;OS.<br>
+&nbsp;<br>
+Examples:&nbsp;WIN,&nbsp;MAC,&nbsp;LINUX,&nbsp;CHROMEOS</tt></dd></dl>
+
+<dl><dt><a name="Platform-GetOSVersionName"><strong>GetOSVersionName</strong></a>(self)</dt><dd><tt>Returns&nbsp;a&nbsp;logically&nbsp;sortable,&nbsp;string-like&nbsp;description&nbsp;of&nbsp;the&nbsp;<a href="#Platform">Platform</a>&nbsp;OS<br>
+version.<br>
+&nbsp;<br>
+Examples:&nbsp;VISTA,&nbsp;WIN7,&nbsp;LION,&nbsp;MOUNTAINLION</tt></dd></dl>
+
+<dl><dt><a name="Platform-GetOSVersionNumber"><strong>GetOSVersionNumber</strong></a>(self)</dt><dd><tt>Returns&nbsp;an&nbsp;integer&nbsp;description&nbsp;of&nbsp;the&nbsp;<a href="#Platform">Platform</a>&nbsp;OS&nbsp;major&nbsp;version.<br>
+&nbsp;<br>
+Examples:&nbsp;On&nbsp;Mac,&nbsp;13&nbsp;for&nbsp;Mavericks,&nbsp;14&nbsp;for&nbsp;Yosemite.</tt></dd></dl>
+
+<dl><dt><a name="Platform-HasBeenThermallyThrottled"><strong>HasBeenThermallyThrottled</strong></a>(self)</dt><dd><tt>Returns&nbsp;True&nbsp;if&nbsp;the&nbsp;device&nbsp;has&nbsp;been&nbsp;thermally&nbsp;throttled.</tt></dd></dl>
+
+<dl><dt><a name="Platform-InstallApplication"><strong>InstallApplication</strong></a>(self, application)</dt><dd><tt>Installs&nbsp;the&nbsp;given&nbsp;application.</tt></dd></dl>
+
+<dl><dt><a name="Platform-IsApplicationRunning"><strong>IsApplicationRunning</strong></a>(self, application)</dt><dd><tt>Returns&nbsp;whether&nbsp;an&nbsp;application&nbsp;is&nbsp;currently&nbsp;running.</tt></dd></dl>
+
+<dl><dt><a name="Platform-IsCooperativeShutdownSupported"><strong>IsCooperativeShutdownSupported</strong></a>(self)</dt><dd><tt>Indicates&nbsp;whether&nbsp;CooperativelyShutdown,&nbsp;below,&nbsp;is&nbsp;supported.<br>
+It&nbsp;is&nbsp;not&nbsp;necessary&nbsp;to&nbsp;implement&nbsp;it&nbsp;on&nbsp;all&nbsp;platforms.</tt></dd></dl>
+
+<dl><dt><a name="Platform-IsMonitoringPower"><strong>IsMonitoringPower</strong></a>(self)</dt><dd><tt>Returns&nbsp;true&nbsp;if&nbsp;power&nbsp;is&nbsp;currently&nbsp;being&nbsp;monitored,&nbsp;false&nbsp;otherwise.</tt></dd></dl>
+
+<dl><dt><a name="Platform-IsThermallyThrottled"><strong>IsThermallyThrottled</strong></a>(self)</dt><dd><tt>Returns&nbsp;True&nbsp;if&nbsp;the&nbsp;device&nbsp;is&nbsp;currently&nbsp;thermally&nbsp;throttled.</tt></dd></dl>
+
+<dl><dt><a name="Platform-LaunchApplication"><strong>LaunchApplication</strong></a>(self, application, parameters<font color="#909090">=None</font>, elevate_privilege<font color="#909090">=False</font>)</dt><dd><tt>"Launches&nbsp;the&nbsp;given&nbsp;|application|&nbsp;with&nbsp;a&nbsp;list&nbsp;of&nbsp;|parameters|&nbsp;on&nbsp;the&nbsp;OS.<br>
+&nbsp;<br>
+Set&nbsp;|elevate_privilege|&nbsp;to&nbsp;launch&nbsp;the&nbsp;application&nbsp;with&nbsp;root&nbsp;or&nbsp;admin&nbsp;rights.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;A&nbsp;popen&nbsp;style&nbsp;process&nbsp;handle&nbsp;for&nbsp;host&nbsp;platforms.</tt></dd></dl>
+
+<dl><dt><a name="Platform-SetHTTPServerDirectories"><strong>SetHTTPServerDirectories</strong></a>(self, paths)</dt><dd><tt>Returns&nbsp;True&nbsp;if&nbsp;the&nbsp;HTTP&nbsp;server&nbsp;was&nbsp;started,&nbsp;False&nbsp;otherwise.</tt></dd></dl>
+
+<dl><dt><a name="Platform-StartLocalServer"><strong>StartLocalServer</strong></a>(self, server)</dt><dd><tt>Starts&nbsp;a&nbsp;LocalServer&nbsp;and&nbsp;associates&nbsp;it&nbsp;with&nbsp;this&nbsp;platform.<br>
+|server.Close()|&nbsp;should&nbsp;be&nbsp;called&nbsp;manually&nbsp;to&nbsp;close&nbsp;the&nbsp;started&nbsp;server.</tt></dd></dl>
+
+<dl><dt><a name="Platform-StartMonitoringPower"><strong>StartMonitoringPower</strong></a>(self, browser)</dt><dd><tt>Starts&nbsp;monitoring&nbsp;power&nbsp;utilization&nbsp;statistics.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;browser:&nbsp;The&nbsp;browser&nbsp;to&nbsp;monitor.</tt></dd></dl>
+
+<dl><dt><a name="Platform-StartVideoCapture"><strong>StartVideoCapture</strong></a>(self, min_bitrate_mbps)</dt><dd><tt>Starts&nbsp;capturing&nbsp;video.<br>
+&nbsp;<br>
+Outer&nbsp;framing&nbsp;may&nbsp;be&nbsp;included&nbsp;(from&nbsp;the&nbsp;OS,&nbsp;browser&nbsp;window,&nbsp;and&nbsp;webcam).<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;min_bitrate_mbps:&nbsp;The&nbsp;minimum&nbsp;capture&nbsp;bitrate&nbsp;in&nbsp;MegaBits&nbsp;Per&nbsp;Second.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The&nbsp;platform&nbsp;is&nbsp;free&nbsp;to&nbsp;deliver&nbsp;a&nbsp;higher&nbsp;bitrate&nbsp;if&nbsp;it&nbsp;can&nbsp;do&nbsp;so<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;without&nbsp;increasing&nbsp;overhead.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;ValueError&nbsp;if&nbsp;the&nbsp;required&nbsp;|min_bitrate_mbps|&nbsp;can't&nbsp;be&nbsp;achieved.</tt></dd></dl>
+
+<dl><dt><a name="Platform-StopAllLocalServers"><strong>StopAllLocalServers</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="Platform-StopMonitoringPower"><strong>StopMonitoringPower</strong></a>(self)</dt><dd><tt>Stops&nbsp;monitoring&nbsp;power&nbsp;utilization&nbsp;and&nbsp;returns&nbsp;stats<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;None&nbsp;if&nbsp;power&nbsp;measurement&nbsp;failed&nbsp;for&nbsp;some&nbsp;reason,&nbsp;otherwise&nbsp;a&nbsp;dict&nbsp;of<br>
+&nbsp;&nbsp;power&nbsp;utilization&nbsp;statistics&nbsp;containing:&nbsp;{<br>
+&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;An&nbsp;identifier&nbsp;for&nbsp;the&nbsp;data&nbsp;provider.&nbsp;Allows&nbsp;to&nbsp;evaluate&nbsp;the&nbsp;precision<br>
+&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;of&nbsp;the&nbsp;data.&nbsp;Example&nbsp;values:&nbsp;monsoon,&nbsp;powermetrics,&nbsp;ds2784<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'identifier':&nbsp;identifier,<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;The&nbsp;instantaneous&nbsp;power&nbsp;(voltage&nbsp;*&nbsp;current)&nbsp;reading&nbsp;in&nbsp;milliwatts&nbsp;at<br>
+&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;each&nbsp;sample.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'power_samples_mw':&nbsp;&nbsp;[mw0,&nbsp;mw1,&nbsp;...,&nbsp;mwN],<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;The&nbsp;full&nbsp;system&nbsp;energy&nbsp;consumption&nbsp;during&nbsp;the&nbsp;sampling&nbsp;period&nbsp;in<br>
+&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;milliwatt&nbsp;hours.&nbsp;May&nbsp;be&nbsp;estimated&nbsp;by&nbsp;integrating&nbsp;power&nbsp;samples&nbsp;or&nbsp;may<br>
+&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;be&nbsp;exact&nbsp;on&nbsp;supported&nbsp;hardware.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'energy_consumption_mwh':&nbsp;mwh,<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;The&nbsp;target&nbsp;application's&nbsp;energy&nbsp;consumption&nbsp;during&nbsp;the&nbsp;sampling&nbsp;period<br>
+&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;in&nbsp;milliwatt&nbsp;hours.&nbsp;Should&nbsp;be&nbsp;returned&nbsp;iff<br>
+&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;<a href="#Platform-CanMeasurePerApplicationPower">CanMeasurePerApplicationPower</a>()&nbsp;return&nbsp;true.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'application_energy_consumption_mwh':&nbsp;mwh,<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;A&nbsp;platform-specific&nbsp;dictionary&nbsp;of&nbsp;additional&nbsp;details&nbsp;about&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;utilization&nbsp;of&nbsp;individual&nbsp;hardware&nbsp;components.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;component_utilization:&nbsp;{<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;...<br>
+&nbsp;&nbsp;&nbsp;&nbsp;}<br>
+&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;<a href="#Platform">Platform</a>-specific&nbsp;data&nbsp;not&nbsp;attributed&nbsp;to&nbsp;any&nbsp;particular&nbsp;hardware<br>
+&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;component.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;platform_info:&nbsp;{<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;Device-specific&nbsp;onboard&nbsp;temperature&nbsp;sensor.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'average_temperature_c':&nbsp;c,<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;...<br>
+&nbsp;&nbsp;&nbsp;&nbsp;}<br>
+&nbsp;<br>
+&nbsp;&nbsp;}</tt></dd></dl>
+
+<dl><dt><a name="Platform-StopVideoCapture"><strong>StopVideoCapture</strong></a>(self)</dt><dd><tt>Stops&nbsp;capturing&nbsp;video.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;A&nbsp;telemetry.core.video.Video&nbsp;<a href="__builtin__.html#object">object</a>.</tt></dd></dl>
+
+<dl><dt><a name="Platform-TakeScreenshot"><strong>TakeScreenshot</strong></a>(self, file_path)</dt><dd><tt>Takes&nbsp;a&nbsp;screenshot&nbsp;of&nbsp;the&nbsp;platform&nbsp;and&nbsp;save&nbsp;to&nbsp;|file_path|.<br>
+&nbsp;<br>
+Note&nbsp;that&nbsp;this&nbsp;method&nbsp;may&nbsp;not&nbsp;be&nbsp;supported&nbsp;on&nbsp;all&nbsp;platform,&nbsp;so&nbsp;check&nbsp;with<br>
+CanTakeScreenshot&nbsp;before&nbsp;calling&nbsp;this.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;file_path:&nbsp;Where&nbsp;to&nbsp;save&nbsp;the&nbsp;screenshot&nbsp;to.&nbsp;If&nbsp;the&nbsp;platform&nbsp;is&nbsp;remote,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;|file_path|&nbsp;is&nbsp;the&nbsp;path&nbsp;on&nbsp;the&nbsp;host&nbsp;platform.<br>
+&nbsp;<br>
+Returns&nbsp;True&nbsp;if&nbsp;it&nbsp;is&nbsp;believed&nbsp;the&nbsp;attempt&nbsp;succeeded.</tt></dd></dl>
+
+<dl><dt><a name="Platform-__init__"><strong>__init__</strong></a>(self, platform_backend)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>http_server</strong></dt>
+</dl>
+<dl><dt><strong>is_host_platform</strong></dt>
+</dl>
+<dl><dt><strong>local_servers</strong></dt>
+<dd><tt>Returns&nbsp;the&nbsp;currently&nbsp;running&nbsp;local&nbsp;servers.</tt></dd>
+</dl>
+<dl><dt><strong>network_controller</strong></dt>
+<dd><tt>Control&nbsp;network&nbsp;settings&nbsp;and&nbsp;servers&nbsp;to&nbsp;simulate&nbsp;the&nbsp;Web.</tt></dd>
+</dl>
+<dl><dt><strong>tracing_controller</strong></dt>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-GetHostPlatform"><strong>GetHostPlatform</strong></a>()</dt></dl>
+ <dl><dt><a name="-GetPlatformForDevice"><strong>GetPlatformForDevice</strong></a>(device, finder_options, logging<font color="#909090">=&lt;module 'logging' from '/usr/lib/python2.7/logging/__init__.pyc'&gt;</font>)</dt><dd><tt>Returns&nbsp;a&nbsp;platform&nbsp;instance&nbsp;for&nbsp;the&nbsp;device.<br>
+Args:<br>
+&nbsp;&nbsp;device:&nbsp;a&nbsp;device.Device&nbsp;instance.</tt></dd></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.core.profiling_controller.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.core.profiling_controller.html
new file mode 100644
index 0000000..c5d4b2c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.core.profiling_controller.html
@@ -0,0 +1,54 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.core.profiling_controller</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.core.html"><font color="#ffffff">core</font></a>.profiling_controller</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/core/profiling_controller.py">telemetry/core/profiling_controller.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.core.profiling_controller.html#ProfilingController">ProfilingController</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ProfilingController">class <strong>ProfilingController</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="ProfilingController-Start"><strong>Start</strong></a>(self, profiler_name, base_output_file)</dt></dl>
+
+<dl><dt><a name="ProfilingController-Stop"><strong>Stop</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="ProfilingController-__init__"><strong>__init__</strong></a>(self, profiling_controller_backend)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.core.tracing_controller.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.core.tracing_controller.html
new file mode 100644
index 0000000..4557f4b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.core.tracing_controller.html
@@ -0,0 +1,72 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.core.tracing_controller</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.core.html"><font color="#ffffff">core</font></a>.tracing_controller</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/core/tracing_controller.py">telemetry/core/tracing_controller.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.core.tracing_controller.html#TracingController">TracingController</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TracingController">class <strong>TracingController</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="TracingController-IsChromeTracingSupported"><strong>IsChromeTracingSupported</strong></a>(self)</dt><dd><tt>Returns&nbsp;whether&nbsp;chrome&nbsp;tracing&nbsp;is&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="TracingController-Start"><strong>Start</strong></a>(self, trace_options, category_filter, timeout<font color="#909090">=10</font>)</dt><dd><tt>Starts&nbsp;tracing.<br>
+&nbsp;<br>
+trace_options&nbsp;specifies&nbsp;which&nbsp;tracing&nbsp;systems&nbsp;to&nbsp;activate.&nbsp;Category&nbsp;filter<br>
+allows&nbsp;fine-tuning&nbsp;of&nbsp;the&nbsp;data&nbsp;that&nbsp;are&nbsp;collected&nbsp;by&nbsp;the&nbsp;selected&nbsp;tracing<br>
+systems.<br>
+&nbsp;<br>
+Some&nbsp;tracers&nbsp;are&nbsp;process-specific,&nbsp;e.g.&nbsp;chrome&nbsp;tracing,&nbsp;but&nbsp;are&nbsp;not<br>
+guaranteed&nbsp;to&nbsp;be&nbsp;supported.&nbsp;In&nbsp;order&nbsp;to&nbsp;support&nbsp;tracing&nbsp;of&nbsp;these&nbsp;kinds&nbsp;of<br>
+tracers,&nbsp;Start&nbsp;will&nbsp;succeed&nbsp;*always*,&nbsp;even&nbsp;if&nbsp;the&nbsp;tracing&nbsp;systems&nbsp;you&nbsp;have<br>
+requested&nbsp;are&nbsp;not&nbsp;supported.<br>
+&nbsp;<br>
+If&nbsp;you&nbsp;absolutely&nbsp;require&nbsp;a&nbsp;particular&nbsp;tracer&nbsp;to&nbsp;exist,&nbsp;then&nbsp;check<br>
+for&nbsp;its&nbsp;support&nbsp;after&nbsp;you&nbsp;have&nbsp;started&nbsp;the&nbsp;process&nbsp;in&nbsp;question.&nbsp;Or,&nbsp;have<br>
+your&nbsp;code&nbsp;fail&nbsp;gracefully&nbsp;when&nbsp;the&nbsp;data&nbsp;you&nbsp;require&nbsp;is&nbsp;not&nbsp;present&nbsp;in&nbsp;the<br>
+resulting&nbsp;trace.</tt></dd></dl>
+
+<dl><dt><a name="TracingController-Stop"><strong>Stop</strong></a>(self)</dt><dd><tt>Stops&nbsp;tracing&nbsp;and&nbsp;returns&nbsp;a&nbsp;TraceValue.</tt></dd></dl>
+
+<dl><dt><a name="TracingController-__init__"><strong>__init__</strong></a>(self, tracing_controller_backend)</dt><dd><tt>Provides&nbsp;control&nbsp;of&nbsp;the&nbsp;tracing&nbsp;systems&nbsp;supported&nbsp;by&nbsp;telemetry.</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>is_tracing_running</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.core.util.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.core.util.html
new file mode 100644
index 0000000..1c76b79
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.core.util.html
@@ -0,0 +1,104 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.core.util</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.core.html"><font color="#ffffff">core</font></a>.util</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/core/util.py">telemetry/core/util.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2012&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+<a href="glob.html">glob</a><br>
+<a href="imp.html">imp</a><br>
+</td><td width="25%" valign=top><a href="inspect.html">inspect</a><br>
+<a href="logging.html">logging</a><br>
+<a href="os.html">os</a><br>
+</td><td width="25%" valign=top><a href="socket.html">socket</a><br>
+<a href="sys.html">sys</a><br>
+<a href="time.html">time</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.core.util.html#PortKeeper">PortKeeper</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="PortKeeper">class <strong>PortKeeper</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Port&nbsp;keeper&nbsp;hold&nbsp;an&nbsp;available&nbsp;port&nbsp;on&nbsp;the&nbsp;system.<br>
+&nbsp;<br>
+Before&nbsp;actually&nbsp;use&nbsp;the&nbsp;port,&nbsp;you&nbsp;must&nbsp;call&nbsp;<a href="#PortKeeper-Release">Release</a>().<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="PortKeeper-Release"><strong>Release</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PortKeeper-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>port</strong></dt>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-GetBaseDir"><strong>GetBaseDir</strong></a>()</dt></dl>
+ <dl><dt><a name="-GetBuildDirectories"><strong>GetBuildDirectories</strong></a>()</dt><dd><tt>Yields&nbsp;all&nbsp;combination&nbsp;of&nbsp;Chromium&nbsp;build&nbsp;output&nbsp;directories.</tt></dd></dl>
+ <dl><dt><a name="-GetChromiumSrcDir"><strong>GetChromiumSrcDir</strong></a>()</dt></dl>
+ <dl><dt><a name="-GetPythonPageSetModule"><strong>GetPythonPageSetModule</strong></a>(file_path)</dt></dl>
+ <dl><dt><a name="-GetSequentialFileName"><strong>GetSequentialFileName</strong></a>(base_name)</dt><dd><tt>Returns&nbsp;the&nbsp;next&nbsp;sequential&nbsp;file&nbsp;name&nbsp;based&nbsp;on&nbsp;|base_name|&nbsp;and&nbsp;the<br>
+existing&nbsp;files.&nbsp;base_name&nbsp;should&nbsp;not&nbsp;contain&nbsp;extension.<br>
+e.g:&nbsp;if&nbsp;base_name&nbsp;is&nbsp;/tmp/test,&nbsp;and&nbsp;/tmp/test_000.json,<br>
+/tmp/test_001.mp3&nbsp;exist,&nbsp;this&nbsp;returns&nbsp;/tmp/test_002.&nbsp;In&nbsp;case&nbsp;no<br>
+other&nbsp;sequential&nbsp;file&nbsp;name&nbsp;exist,&nbsp;this&nbsp;will&nbsp;return&nbsp;/tmp/test_000</tt></dd></dl>
+ <dl><dt><a name="-GetTelemetryDir"><strong>GetTelemetryDir</strong></a>()</dt></dl>
+ <dl><dt><a name="-GetTelemetryThirdPartyDir"><strong>GetTelemetryThirdPartyDir</strong></a>()</dt></dl>
+ <dl><dt><a name="-GetUnittestDataDir"><strong>GetUnittestDataDir</strong></a>()</dt></dl>
+ <dl><dt><a name="-GetUnreservedAvailableLocalPort"><strong>GetUnreservedAvailableLocalPort</strong></a>()</dt><dd><tt>Returns&nbsp;an&nbsp;available&nbsp;port&nbsp;on&nbsp;the&nbsp;system.<br>
+&nbsp;<br>
+WARNING:&nbsp;This&nbsp;method&nbsp;does&nbsp;not&nbsp;reserve&nbsp;the&nbsp;port&nbsp;it&nbsp;returns,&nbsp;so&nbsp;it&nbsp;may&nbsp;be&nbsp;used<br>
+by&nbsp;something&nbsp;else&nbsp;before&nbsp;you&nbsp;get&nbsp;to&nbsp;use&nbsp;it.&nbsp;This&nbsp;can&nbsp;lead&nbsp;to&nbsp;flake.</tt></dd></dl>
+ <dl><dt><a name="-IsRunningOnCrosDevice"><strong>IsRunningOnCrosDevice</strong></a>()</dt><dd><tt>Returns&nbsp;True&nbsp;if&nbsp;we're&nbsp;on&nbsp;a&nbsp;ChromeOS&nbsp;device.</tt></dd></dl>
+ <dl><dt><a name="-WaitFor"><strong>WaitFor</strong></a>(condition, timeout)</dt><dd><tt>Waits&nbsp;for&nbsp;up&nbsp;to&nbsp;|timeout|&nbsp;secs&nbsp;for&nbsp;the&nbsp;function&nbsp;|condition|&nbsp;to&nbsp;return&nbsp;True.<br>
+&nbsp;<br>
+Polling&nbsp;frequency&nbsp;is&nbsp;(elapsed_time&nbsp;/&nbsp;10),&nbsp;with&nbsp;a&nbsp;min&nbsp;of&nbsp;.1s&nbsp;and&nbsp;max&nbsp;of&nbsp;5s.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;Result&nbsp;of&nbsp;|condition|&nbsp;function&nbsp;(if&nbsp;present).</tt></dd></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.decorators.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.decorators.html
new file mode 100644
index 0000000..b203e50
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.decorators.html
@@ -0,0 +1,117 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.decorators</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.decorators</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/decorators.py">telemetry/decorators.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.<br>
+#&nbsp;pylint:&nbsp;disable=W0212</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="datetime.html">datetime</a><br>
+<a href="functools.html">functools</a><br>
+</td><td width="25%" valign=top><a href="inspect.html">inspect</a><br>
+<a href="os.html">os</a><br>
+</td><td width="25%" valign=top><a href="types.html">types</a><br>
+<a href="warnings.html">warnings</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.decorators.html#Deprecated">Deprecated</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="Deprecated">class <strong>Deprecated</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="Deprecated-__call__"><strong>__call__</strong></a>(self, target)</dt></dl>
+
+<dl><dt><a name="Deprecated-__init__"><strong>__init__</strong></a>(self, year, month, day, extra_guidance<font color="#909090">=''</font>)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-Cache"><strong>Cache</strong></a>(obj)</dt><dd><tt>Decorator&nbsp;for&nbsp;caching&nbsp;read-only&nbsp;properties.<br>
+&nbsp;<br>
+Example&nbsp;usage&nbsp;(always&nbsp;returns&nbsp;the&nbsp;same&nbsp;Foo&nbsp;instance):<br>
+&nbsp;&nbsp;@Cache<br>
+&nbsp;&nbsp;def&nbsp;CreateFoo():<br>
+&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;Foo()<br>
+&nbsp;<br>
+If&nbsp;CreateFoo()&nbsp;accepts&nbsp;parameters,&nbsp;a&nbsp;separate&nbsp;cached&nbsp;value&nbsp;is&nbsp;maintained<br>
+for&nbsp;each&nbsp;unique&nbsp;parameter&nbsp;combination.<br>
+&nbsp;<br>
+Cached&nbsp;methods&nbsp;maintain&nbsp;their&nbsp;cache&nbsp;for&nbsp;the&nbsp;lifetime&nbsp;of&nbsp;the&nbsp;/instance/,&nbsp;while<br>
+cached&nbsp;functions&nbsp;maintain&nbsp;their&nbsp;cache&nbsp;for&nbsp;the&nbsp;lifetime&nbsp;of&nbsp;the&nbsp;/module/.</tt></dd></dl>
+ <dl><dt><a name="-Disabled"><strong>Disabled</strong></a>(*args)</dt><dd><tt>Decorator&nbsp;for&nbsp;disabling&nbsp;tests/benchmarks.<br>
+&nbsp;<br>
+&nbsp;<br>
+If&nbsp;args&nbsp;are&nbsp;given,&nbsp;the&nbsp;test&nbsp;will&nbsp;be&nbsp;disabled&nbsp;if&nbsp;ANY&nbsp;of&nbsp;the&nbsp;args&nbsp;match&nbsp;the<br>
+browser&nbsp;type,&nbsp;OS&nbsp;name&nbsp;or&nbsp;OS&nbsp;version:<br>
+&nbsp;&nbsp;@<a href="#-Disabled">Disabled</a>('canary')&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;Disabled&nbsp;for&nbsp;canary&nbsp;browsers<br>
+&nbsp;&nbsp;@<a href="#-Disabled">Disabled</a>('win')&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;Disabled&nbsp;on&nbsp;Windows.<br>
+&nbsp;&nbsp;@<a href="#-Disabled">Disabled</a>('win',&nbsp;'linux')&nbsp;&nbsp;#&nbsp;Disabled&nbsp;on&nbsp;both&nbsp;Windows&nbsp;and&nbsp;Linux.<br>
+&nbsp;&nbsp;@<a href="#-Disabled">Disabled</a>('mavericks')&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;Disabled&nbsp;on&nbsp;Mac&nbsp;Mavericks&nbsp;(10.9)&nbsp;only.<br>
+&nbsp;&nbsp;@<a href="#-Disabled">Disabled</a>('all')&nbsp;&nbsp;#&nbsp;Unconditionally&nbsp;disabled.</tt></dd></dl>
+ <dl><dt><a name="-Enabled"><strong>Enabled</strong></a>(*args)</dt><dd><tt>Decorator&nbsp;for&nbsp;enabling&nbsp;tests/benchmarks.<br>
+&nbsp;<br>
+The&nbsp;test&nbsp;will&nbsp;be&nbsp;enabled&nbsp;if&nbsp;ANY&nbsp;of&nbsp;the&nbsp;args&nbsp;match&nbsp;the&nbsp;browser&nbsp;type,&nbsp;OS&nbsp;name<br>
+or&nbsp;OS&nbsp;version:<br>
+&nbsp;&nbsp;@<a href="#-Enabled">Enabled</a>('canary')&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;Enabled&nbsp;only&nbsp;for&nbsp;canary&nbsp;browsers<br>
+&nbsp;&nbsp;@<a href="#-Enabled">Enabled</a>('win')&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;Enabled&nbsp;only&nbsp;on&nbsp;Windows.<br>
+&nbsp;&nbsp;@<a href="#-Enabled">Enabled</a>('win',&nbsp;'linux')&nbsp;&nbsp;#&nbsp;Enabled&nbsp;only&nbsp;on&nbsp;Windows&nbsp;or&nbsp;Linux.<br>
+&nbsp;&nbsp;@<a href="#-Enabled">Enabled</a>('mavericks')&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;Enabled&nbsp;only&nbsp;on&nbsp;Mac&nbsp;Mavericks&nbsp;(10.9).</tt></dd></dl>
+ <dl><dt><a name="-IsEnabled"><strong>IsEnabled</strong></a>(test, possible_browser)</dt><dd><tt>Returns&nbsp;True&nbsp;iff&nbsp;|test|&nbsp;is&nbsp;enabled&nbsp;given&nbsp;the&nbsp;|possible_browser|.<br>
+&nbsp;<br>
+Use&nbsp;to&nbsp;respect&nbsp;the&nbsp;@Enabled&nbsp;/&nbsp;@Disabled&nbsp;decorators.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;test:&nbsp;A&nbsp;function&nbsp;or&nbsp;class&nbsp;that&nbsp;may&nbsp;contain&nbsp;_disabled_strings&nbsp;and/or<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_enabled_strings&nbsp;attributes.<br>
+&nbsp;&nbsp;possible_browser:&nbsp;A&nbsp;PossibleBrowser&nbsp;to&nbsp;check&nbsp;whether&nbsp;|test|&nbsp;may&nbsp;run&nbsp;against.</tt></dd></dl>
+ <dl><dt><a name="-Isolated"><strong>Isolated</strong></a>(*args)</dt><dd><tt>Decorator&nbsp;for&nbsp;noting&nbsp;that&nbsp;tests&nbsp;must&nbsp;be&nbsp;run&nbsp;in&nbsp;isolation.<br>
+&nbsp;<br>
+The&nbsp;test&nbsp;will&nbsp;be&nbsp;run&nbsp;by&nbsp;itself&nbsp;(not&nbsp;concurrently&nbsp;with&nbsp;any&nbsp;other&nbsp;tests)<br>
+if&nbsp;ANY&nbsp;of&nbsp;the&nbsp;args&nbsp;match&nbsp;the&nbsp;browser&nbsp;type,&nbsp;OS&nbsp;name,&nbsp;or&nbsp;OS&nbsp;version.</tt></dd></dl>
+ <dl><dt><a name="-ShouldBeIsolated"><strong>ShouldBeIsolated</strong></a>(test, possible_browser)</dt></dl>
+ <dl><dt><a name="-ShouldSkip"><strong>ShouldSkip</strong></a>(test, possible_browser)</dt><dd><tt>Returns&nbsp;whether&nbsp;the&nbsp;test&nbsp;should&nbsp;be&nbsp;skipped&nbsp;and&nbsp;the&nbsp;reason&nbsp;for&nbsp;it.</tt></dd></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.html
new file mode 100644
index 0000000..e1962d2
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.html
@@ -0,0 +1,44 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: package telemetry</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong>telemetry</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/__init__.py">telemetry/__init__.py</a></font></td></tr></table>
+    <p><tt>A&nbsp;library&nbsp;for&nbsp;cross-platform&nbsp;browser&nbsp;tests.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Package Contents</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.android.html"><strong>android</strong>&nbsp;(package)</a><br>
+<a href="telemetry.benchmark.html">benchmark</a><br>
+<a href="telemetry.benchmark_run_unittest.html">benchmark_run_unittest</a><br>
+<a href="telemetry.benchmark_runner.html">benchmark_runner</a><br>
+<a href="telemetry.benchmark_runner_unittest.html">benchmark_runner_unittest</a><br>
+<a href="telemetry.benchmark_unittest.html">benchmark_unittest</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.html"><strong>core</strong>&nbsp;(package)</a><br>
+<a href="telemetry.decorators.html">decorators</a><br>
+<a href="telemetry.decorators_unittest.html">decorators_unittest</a><br>
+<a href="telemetry.internal.html"><strong>internal</strong>&nbsp;(package)</a><br>
+<a href="telemetry.page.html"><strong>page</strong>&nbsp;(package)</a><br>
+<a href="telemetry.project_config.html">project_config</a><br>
+</td><td width="25%" valign=top><a href="telemetry.record_wpr.html">record_wpr</a><br>
+<a href="telemetry.record_wpr_unittest.html">record_wpr_unittest</a><br>
+<a href="telemetry.story.html"><strong>story</strong>&nbsp;(package)</a><br>
+<a href="telemetry.telemetry_dependencies_unittest.html">telemetry_dependencies_unittest</a><br>
+<a href="telemetry.testing.html"><strong>testing</strong>&nbsp;(package)</a><br>
+<a href="telemetry.timeline.html"><strong>timeline</strong>&nbsp;(package)</a><br>
+</td><td width="25%" valign=top><a href="telemetry.util.html"><strong>util</strong>&nbsp;(package)</a><br>
+<a href="telemetry.value.html"><strong>value</strong>&nbsp;(package)</a><br>
+<a href="telemetry.web_perf.html"><strong>web_perf</strong>&nbsp;(package)</a><br>
+<a href="telemetry.wpr.html"><strong>wpr</strong>&nbsp;(package)</a><br>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.drag.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.drag.html
new file mode 100644
index 0000000..3169961
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.drag.html
@@ -0,0 +1,84 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.actions.drag</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.actions.html"><font color="#ffffff">actions</font></a>.drag</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/actions/drag.py">telemetry/internal/actions/drag.py</a></font></td></tr></table>
+    <p><tt>A&nbsp;Telemetry&nbsp;page_action&nbsp;that&nbsp;performs&nbsp;the&nbsp;"drag"&nbsp;action&nbsp;on&nbsp;pages.<br>
+&nbsp;<br>
+Action&nbsp;parameters&nbsp;are:<br>
+-&nbsp;selector:&nbsp;If&nbsp;no&nbsp;selector&nbsp;is&nbsp;defined&nbsp;then&nbsp;the&nbsp;action&nbsp;attempts&nbsp;to&nbsp;drag&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;document&nbsp;element&nbsp;on&nbsp;the&nbsp;page.<br>
+-&nbsp;element_function:&nbsp;CSS&nbsp;selector&nbsp;used&nbsp;to&nbsp;evaluate&nbsp;callback&nbsp;when&nbsp;test&nbsp;completes<br>
+-&nbsp;text:&nbsp;The&nbsp;element&nbsp;with&nbsp;exact&nbsp;text&nbsp;is&nbsp;selected.<br>
+-&nbsp;left_start_ratio:&nbsp;ratio&nbsp;of&nbsp;start&nbsp;point's&nbsp;left&nbsp;coordinate&nbsp;to&nbsp;the&nbsp;element<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;width.<br>
+-&nbsp;top_start_ratio:&nbsp;ratio&nbsp;of&nbsp;start&nbsp;point's&nbsp;top&nbsp;coordinate&nbsp;to&nbsp;the&nbsp;element&nbsp;height.<br>
+-&nbsp;left_end_ratio:&nbsp;ratio&nbsp;of&nbsp;end&nbsp;point's&nbsp;left&nbsp;coordinate&nbsp;to&nbsp;the&nbsp;element&nbsp;width.<br>
+-&nbsp;left_end_ratio:&nbsp;ratio&nbsp;of&nbsp;end&nbsp;point's&nbsp;top&nbsp;coordinate&nbsp;to&nbsp;the&nbsp;element&nbsp;height.<br>
+-&nbsp;speed_in_pixels_per_second:&nbsp;speed&nbsp;of&nbsp;the&nbsp;drag&nbsp;gesture&nbsp;in&nbsp;pixels&nbsp;per&nbsp;second.<br>
+-&nbsp;use_touch:&nbsp;boolean&nbsp;value&nbsp;to&nbsp;specify&nbsp;if&nbsp;gesture&nbsp;should&nbsp;use&nbsp;touch&nbsp;input&nbsp;or&nbsp;not.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="os.html">os</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.actions.page_action.html">telemetry.internal.actions.page_action</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.actions.drag.html#DragAction">DragAction</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="DragAction">class <strong>DragAction</strong></a>(<a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.actions.drag.html#DragAction">DragAction</a></dd>
+<dd><a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="DragAction-RunAction"><strong>RunAction</strong></a>(self, tab)</dt></dl>
+
+<dl><dt><a name="DragAction-WillRunAction"><strong>WillRunAction</strong></a>(self, tab)</dt></dl>
+
+<dl><dt><a name="DragAction-__init__"><strong>__init__</strong></a>(self, selector<font color="#909090">=None</font>, text<font color="#909090">=None</font>, element_function<font color="#909090">=None</font>, left_start_ratio<font color="#909090">=None</font>, top_start_ratio<font color="#909090">=None</font>, left_end_ratio<font color="#909090">=None</font>, top_end_ratio<font color="#909090">=None</font>, speed_in_pixels_per_second<font color="#909090">=800</font>, use_touch<font color="#909090">=False</font>, synthetic_gesture_source<font color="#909090">='DEFAULT'</font>)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>:<br>
+<dl><dt><a name="DragAction-CleanUp"><strong>CleanUp</strong></a>(self, tab)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.html
new file mode 100644
index 0000000..0a52825
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.html
@@ -0,0 +1,52 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: package telemetry.internal.actions</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.actions</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/actions/__init__.py">telemetry/internal/actions/__init__.py</a></font></td></tr></table>
+    <p></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Package Contents</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.actions.action_runner_unittest.html">action_runner_unittest</a><br>
+<a href="telemetry.internal.actions.drag.html">drag</a><br>
+<a href="telemetry.internal.actions.drag_unittest.html">drag_unittest</a><br>
+<a href="telemetry.internal.actions.javascript_click.html">javascript_click</a><br>
+<a href="telemetry.internal.actions.load_media.html">load_media</a><br>
+<a href="telemetry.internal.actions.load_media_unittest.html">load_media_unittest</a><br>
+<a href="telemetry.internal.actions.loop.html">loop</a><br>
+<a href="telemetry.internal.actions.loop_unittest.html">loop_unittest</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.actions.media_action.html">media_action</a><br>
+<a href="telemetry.internal.actions.mouse_click.html">mouse_click</a><br>
+<a href="telemetry.internal.actions.mouse_click_unittest.html">mouse_click_unittest</a><br>
+<a href="telemetry.internal.actions.navigate.html">navigate</a><br>
+<a href="telemetry.internal.actions.navigate_unittest.html">navigate_unittest</a><br>
+<a href="telemetry.internal.actions.page_action.html">page_action</a><br>
+<a href="telemetry.internal.actions.page_action_unittest.html">page_action_unittest</a><br>
+<a href="telemetry.internal.actions.pinch.html">pinch</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.actions.pinch_unittest.html">pinch_unittest</a><br>
+<a href="telemetry.internal.actions.play.html">play</a><br>
+<a href="telemetry.internal.actions.play_unittest.html">play_unittest</a><br>
+<a href="telemetry.internal.actions.repaint_continuously.html">repaint_continuously</a><br>
+<a href="telemetry.internal.actions.repeatable_scroll.html">repeatable_scroll</a><br>
+<a href="telemetry.internal.actions.repeatable_scroll_unittest.html">repeatable_scroll_unittest</a><br>
+<a href="telemetry.internal.actions.scroll.html">scroll</a><br>
+<a href="telemetry.internal.actions.scroll_bounce.html">scroll_bounce</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.actions.scroll_unittest.html">scroll_unittest</a><br>
+<a href="telemetry.internal.actions.seek.html">seek</a><br>
+<a href="telemetry.internal.actions.seek_unittest.html">seek_unittest</a><br>
+<a href="telemetry.internal.actions.swipe.html">swipe</a><br>
+<a href="telemetry.internal.actions.tap.html">tap</a><br>
+<a href="telemetry.internal.actions.wait.html">wait</a><br>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.javascript_click.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.javascript_click.html
new file mode 100644
index 0000000..4765ecc
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.javascript_click.html
@@ -0,0 +1,73 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.actions.javascript_click</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.actions.html"><font color="#ffffff">actions</font></a>.javascript_click</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/actions/javascript_click.py">telemetry/internal/actions/javascript_click.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.actions.page_action.html">telemetry.internal.actions.page_action</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.actions.javascript_click.html#ClickElementAction">ClickElementAction</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ClickElementAction">class <strong>ClickElementAction</strong></a>(<a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.actions.javascript_click.html#ClickElementAction">ClickElementAction</a></dd>
+<dd><a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="ClickElementAction-RunAction"><strong>RunAction</strong></a>(self, tab)</dt></dl>
+
+<dl><dt><a name="ClickElementAction-__init__"><strong>__init__</strong></a>(self, selector<font color="#909090">=None</font>, text<font color="#909090">=None</font>, element_function<font color="#909090">=None</font>)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>:<br>
+<dl><dt><a name="ClickElementAction-CleanUp"><strong>CleanUp</strong></a>(self, tab)</dt></dl>
+
+<dl><dt><a name="ClickElementAction-WillRunAction"><strong>WillRunAction</strong></a>(self, tab)</dt><dd><tt>Override&nbsp;to&nbsp;do&nbsp;action-specific&nbsp;setup&nbsp;before<br>
+Test.WillRunAction&nbsp;is&nbsp;called.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.load_media.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.load_media.html
new file mode 100644
index 0000000..7830363
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.load_media.html
@@ -0,0 +1,92 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.actions.load_media</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.actions.html"><font color="#ffffff">actions</font></a>.load_media</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/actions/load_media.py">telemetry/internal/actions/load_media.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2015&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.actions.media_action.html">telemetry.internal.actions.media_action</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.actions.page_action.html">telemetry.internal.actions.page_action</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.actions.media_action.html#MediaAction">telemetry.internal.actions.media_action.MediaAction</a>(<a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.actions.load_media.html#LoadMediaAction">LoadMediaAction</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="LoadMediaAction">class <strong>LoadMediaAction</strong></a>(<a href="telemetry.internal.actions.media_action.html#MediaAction">telemetry.internal.actions.media_action.MediaAction</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>For&nbsp;calling&nbsp;load()&nbsp;on&nbsp;media&nbsp;elements&nbsp;and&nbsp;waiting&nbsp;for&nbsp;an&nbsp;event&nbsp;to&nbsp;fire.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.actions.load_media.html#LoadMediaAction">LoadMediaAction</a></dd>
+<dd><a href="telemetry.internal.actions.media_action.html#MediaAction">telemetry.internal.actions.media_action.MediaAction</a></dd>
+<dd><a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="LoadMediaAction-RunAction"><strong>RunAction</strong></a>(self, tab)</dt></dl>
+
+<dl><dt><a name="LoadMediaAction-WillRunAction"><strong>WillRunAction</strong></a>(self, tab)</dt><dd><tt>Load&nbsp;the&nbsp;JS&nbsp;code&nbsp;prior&nbsp;to&nbsp;running&nbsp;the&nbsp;action.</tt></dd></dl>
+
+<dl><dt><a name="LoadMediaAction-__init__"><strong>__init__</strong></a>(self, selector<font color="#909090">=None</font>, timeout_in_seconds<font color="#909090">=0</font>, event_to_await<font color="#909090">='canplaythrough'</font>)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.actions.media_action.html#MediaAction">telemetry.internal.actions.media_action.MediaAction</a>:<br>
+<dl><dt><a name="LoadMediaAction-HasEventCompletedOrError"><strong>HasEventCompletedOrError</strong></a>(self, tab, selector, event_name)</dt></dl>
+
+<dl><dt><a name="LoadMediaAction-LoadJS"><strong>LoadJS</strong></a>(self, tab, js_file_name)</dt><dd><tt>Loads&nbsp;and&nbsp;executes&nbsp;a&nbsp;JS&nbsp;file&nbsp;in&nbsp;the&nbsp;tab.</tt></dd></dl>
+
+<dl><dt><a name="LoadMediaAction-WaitForEvent"><strong>WaitForEvent</strong></a>(self, tab, selector, event_name, timeout_in_seconds)</dt><dd><tt>Halts&nbsp;media&nbsp;action&nbsp;until&nbsp;the&nbsp;selector's&nbsp;event&nbsp;is&nbsp;fired.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;tab:&nbsp;The&nbsp;tab&nbsp;to&nbsp;check&nbsp;for&nbsp;event&nbsp;on.<br>
+&nbsp;&nbsp;selector:&nbsp;Media&nbsp;element&nbsp;selector.<br>
+&nbsp;&nbsp;event_name:&nbsp;Name&nbsp;of&nbsp;the&nbsp;event&nbsp;to&nbsp;check&nbsp;if&nbsp;fired&nbsp;or&nbsp;not.<br>
+&nbsp;&nbsp;timeout_in_seconds:&nbsp;Timeout&nbsp;to&nbsp;check&nbsp;for&nbsp;event,&nbsp;throws&nbsp;an&nbsp;exception&nbsp;if<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;not&nbsp;fired.</tt></dd></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>:<br>
+<dl><dt><a name="LoadMediaAction-CleanUp"><strong>CleanUp</strong></a>(self, tab)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.loop.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.loop.html
new file mode 100644
index 0000000..ca62f46
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.loop.html
@@ -0,0 +1,95 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.actions.loop</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.actions.html"><font color="#ffffff">actions</font></a>.loop</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/actions/loop.py">telemetry/internal/actions/loop.py</a></font></td></tr></table>
+    <p><tt>A&nbsp;Telemetry&nbsp;page_action&nbsp;that&nbsp;loops&nbsp;media&nbsp;playback.<br>
+&nbsp;<br>
+Action&nbsp;parameters&nbsp;are:<br>
+-&nbsp;loop_count:&nbsp;The&nbsp;number&nbsp;of&nbsp;times&nbsp;to&nbsp;loop&nbsp;media.<br>
+-&nbsp;selector:&nbsp;If&nbsp;no&nbsp;selector&nbsp;is&nbsp;defined&nbsp;then&nbsp;the&nbsp;action&nbsp;attempts&nbsp;to&nbsp;loop&nbsp;the&nbsp;first<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;media&nbsp;element&nbsp;on&nbsp;the&nbsp;page.&nbsp;If&nbsp;'all'&nbsp;then&nbsp;loop&nbsp;all&nbsp;media&nbsp;elements.<br>
+-&nbsp;timeout_in_seconds:&nbsp;Timeout&nbsp;to&nbsp;wait&nbsp;for&nbsp;media&nbsp;to&nbsp;loop.&nbsp;Default&nbsp;is<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;60&nbsp;sec&nbsp;x&nbsp;loop_count.&nbsp;0&nbsp;means&nbsp;do&nbsp;not&nbsp;wait.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.actions.media_action.html">telemetry.internal.actions.media_action</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.actions.page_action.html">telemetry.internal.actions.page_action</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.actions.media_action.html#MediaAction">telemetry.internal.actions.media_action.MediaAction</a>(<a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.actions.loop.html#LoopAction">LoopAction</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="LoopAction">class <strong>LoopAction</strong></a>(<a href="telemetry.internal.actions.media_action.html#MediaAction">telemetry.internal.actions.media_action.MediaAction</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.actions.loop.html#LoopAction">LoopAction</a></dd>
+<dd><a href="telemetry.internal.actions.media_action.html#MediaAction">telemetry.internal.actions.media_action.MediaAction</a></dd>
+<dd><a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="LoopAction-RunAction"><strong>RunAction</strong></a>(self, tab)</dt></dl>
+
+<dl><dt><a name="LoopAction-WillRunAction"><strong>WillRunAction</strong></a>(self, tab)</dt><dd><tt>Load&nbsp;the&nbsp;media&nbsp;metrics&nbsp;JS&nbsp;code&nbsp;prior&nbsp;to&nbsp;running&nbsp;the&nbsp;action.</tt></dd></dl>
+
+<dl><dt><a name="LoopAction-__init__"><strong>__init__</strong></a>(self, loop_count, selector<font color="#909090">=None</font>, timeout_in_seconds<font color="#909090">=None</font>)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.actions.media_action.html#MediaAction">telemetry.internal.actions.media_action.MediaAction</a>:<br>
+<dl><dt><a name="LoopAction-HasEventCompletedOrError"><strong>HasEventCompletedOrError</strong></a>(self, tab, selector, event_name)</dt></dl>
+
+<dl><dt><a name="LoopAction-LoadJS"><strong>LoadJS</strong></a>(self, tab, js_file_name)</dt><dd><tt>Loads&nbsp;and&nbsp;executes&nbsp;a&nbsp;JS&nbsp;file&nbsp;in&nbsp;the&nbsp;tab.</tt></dd></dl>
+
+<dl><dt><a name="LoopAction-WaitForEvent"><strong>WaitForEvent</strong></a>(self, tab, selector, event_name, timeout_in_seconds)</dt><dd><tt>Halts&nbsp;media&nbsp;action&nbsp;until&nbsp;the&nbsp;selector's&nbsp;event&nbsp;is&nbsp;fired.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;tab:&nbsp;The&nbsp;tab&nbsp;to&nbsp;check&nbsp;for&nbsp;event&nbsp;on.<br>
+&nbsp;&nbsp;selector:&nbsp;Media&nbsp;element&nbsp;selector.<br>
+&nbsp;&nbsp;event_name:&nbsp;Name&nbsp;of&nbsp;the&nbsp;event&nbsp;to&nbsp;check&nbsp;if&nbsp;fired&nbsp;or&nbsp;not.<br>
+&nbsp;&nbsp;timeout_in_seconds:&nbsp;Timeout&nbsp;to&nbsp;check&nbsp;for&nbsp;event,&nbsp;throws&nbsp;an&nbsp;exception&nbsp;if<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;not&nbsp;fired.</tt></dd></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>:<br>
+<dl><dt><a name="LoopAction-CleanUp"><strong>CleanUp</strong></a>(self, tab)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.media_action.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.media_action.html
new file mode 100644
index 0000000..80b6ea3
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.media_action.html
@@ -0,0 +1,84 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.actions.media_action</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.actions.html"><font color="#ffffff">actions</font></a>.media_action</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/actions/media_action.py">telemetry/internal/actions/media_action.py</a></font></td></tr></table>
+    <p><tt>Common&nbsp;media&nbsp;action&nbsp;functions.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top><a href="os.html">os</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.actions.page_action.html">telemetry.internal.actions.page_action</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.util.html">telemetry.core.util</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.actions.media_action.html#MediaAction">MediaAction</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="MediaAction">class <strong>MediaAction</strong></a>(<a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.actions.media_action.html#MediaAction">MediaAction</a></dd>
+<dd><a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="MediaAction-HasEventCompletedOrError"><strong>HasEventCompletedOrError</strong></a>(self, tab, selector, event_name)</dt></dl>
+
+<dl><dt><a name="MediaAction-LoadJS"><strong>LoadJS</strong></a>(self, tab, js_file_name)</dt><dd><tt>Loads&nbsp;and&nbsp;executes&nbsp;a&nbsp;JS&nbsp;file&nbsp;in&nbsp;the&nbsp;tab.</tt></dd></dl>
+
+<dl><dt><a name="MediaAction-RunAction"><strong>RunAction</strong></a>(self, tab)</dt></dl>
+
+<dl><dt><a name="MediaAction-WaitForEvent"><strong>WaitForEvent</strong></a>(self, tab, selector, event_name, timeout_in_seconds)</dt><dd><tt>Halts&nbsp;media&nbsp;action&nbsp;until&nbsp;the&nbsp;selector's&nbsp;event&nbsp;is&nbsp;fired.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;tab:&nbsp;The&nbsp;tab&nbsp;to&nbsp;check&nbsp;for&nbsp;event&nbsp;on.<br>
+&nbsp;&nbsp;selector:&nbsp;Media&nbsp;element&nbsp;selector.<br>
+&nbsp;&nbsp;event_name:&nbsp;Name&nbsp;of&nbsp;the&nbsp;event&nbsp;to&nbsp;check&nbsp;if&nbsp;fired&nbsp;or&nbsp;not.<br>
+&nbsp;&nbsp;timeout_in_seconds:&nbsp;Timeout&nbsp;to&nbsp;check&nbsp;for&nbsp;event,&nbsp;throws&nbsp;an&nbsp;exception&nbsp;if<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;not&nbsp;fired.</tt></dd></dl>
+
+<dl><dt><a name="MediaAction-WillRunAction"><strong>WillRunAction</strong></a>(self, tab)</dt><dd><tt>Loads&nbsp;the&nbsp;common&nbsp;media&nbsp;action&nbsp;JS&nbsp;code&nbsp;prior&nbsp;to&nbsp;running&nbsp;the&nbsp;action.</tt></dd></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>:<br>
+<dl><dt><a name="MediaAction-CleanUp"><strong>CleanUp</strong></a>(self, tab)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.mouse_click.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.mouse_click.html
new file mode 100644
index 0000000..0d2d5b5
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.mouse_click.html
@@ -0,0 +1,81 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.actions.mouse_click</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.actions.html"><font color="#ffffff">actions</font></a>.mouse_click</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/actions/mouse_click.py">telemetry/internal/actions/mouse_click.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2015&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="os.html">os</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.actions.page_action.html">telemetry.internal.actions.page_action</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.actions.mouse_click.html#MouseClickAction">MouseClickAction</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="MouseClickAction">class <strong>MouseClickAction</strong></a>(<a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.actions.mouse_click.html#MouseClickAction">MouseClickAction</a></dd>
+<dd><a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="MouseClickAction-RunAction"><strong>RunAction</strong></a>(self, tab)</dt></dl>
+
+<dl><dt><a name="MouseClickAction-WillRunAction"><strong>WillRunAction</strong></a>(self, tab)</dt><dd><tt>Load&nbsp;the&nbsp;mouse&nbsp;click&nbsp;JS&nbsp;code&nbsp;prior&nbsp;to&nbsp;running&nbsp;the&nbsp;action.</tt></dd></dl>
+
+<dl><dt><a name="MouseClickAction-__init__"><strong>__init__</strong></a>(self, selector<font color="#909090">=None</font>)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>:<br>
+<dl><dt><a name="MouseClickAction-CleanUp"><strong>CleanUp</strong></a>(self, tab)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-read_js"><strong>read_js</strong></a>()</dt></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.navigate.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.navigate.html
new file mode 100644
index 0000000..3c11b2a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.navigate.html
@@ -0,0 +1,74 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.actions.navigate</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.actions.html"><font color="#ffffff">actions</font></a>.navigate</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/actions/navigate.py">telemetry/internal/actions/navigate.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.actions.page_action.html">telemetry.internal.actions.page_action</a><br>
+</td><td width="25%" valign=top><a href="time.html">time</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.actions.navigate.html#NavigateAction">NavigateAction</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="NavigateAction">class <strong>NavigateAction</strong></a>(<a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.actions.navigate.html#NavigateAction">NavigateAction</a></dd>
+<dd><a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="NavigateAction-RunAction"><strong>RunAction</strong></a>(self, tab)</dt></dl>
+
+<dl><dt><a name="NavigateAction-__init__"><strong>__init__</strong></a>(self, url, script_to_evaluate_on_commit<font color="#909090">=None</font>, timeout_in_seconds<font color="#909090">=60</font>)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>:<br>
+<dl><dt><a name="NavigateAction-CleanUp"><strong>CleanUp</strong></a>(self, tab)</dt></dl>
+
+<dl><dt><a name="NavigateAction-WillRunAction"><strong>WillRunAction</strong></a>(self, tab)</dt><dd><tt>Override&nbsp;to&nbsp;do&nbsp;action-specific&nbsp;setup&nbsp;before<br>
+Test.WillRunAction&nbsp;is&nbsp;called.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.page_action.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.page_action.html
new file mode 100644
index 0000000..40ba46f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.page_action.html
@@ -0,0 +1,235 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.actions.page_action</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.actions.html"><font color="#ffffff">actions</font></a>.page_action</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/actions/page_action.py">telemetry/internal/actions/page_action.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2012&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.decorators.html">telemetry.decorators</a><br>
+</td><td width="25%" valign=top><a href="re.html">re</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.actions.page_action.html#PageAction">PageAction</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="exceptions.html#Exception">exceptions.Exception</a>(<a href="exceptions.html#BaseException">exceptions.BaseException</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.actions.page_action.html#PageActionFailed">PageActionFailed</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.internal.actions.page_action.html#PageActionNotSupported">PageActionNotSupported</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="PageAction">class <strong>PageAction</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Represents&nbsp;an&nbsp;action&nbsp;that&nbsp;a&nbsp;user&nbsp;might&nbsp;try&nbsp;to&nbsp;perform&nbsp;to&nbsp;a&nbsp;page.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="PageAction-CleanUp"><strong>CleanUp</strong></a>(self, tab)</dt></dl>
+
+<dl><dt><a name="PageAction-RunAction"><strong>RunAction</strong></a>(self, tab)</dt></dl>
+
+<dl><dt><a name="PageAction-WillRunAction"><strong>WillRunAction</strong></a>(self, tab)</dt><dd><tt>Override&nbsp;to&nbsp;do&nbsp;action-specific&nbsp;setup&nbsp;before<br>
+Test.WillRunAction&nbsp;is&nbsp;called.</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="PageActionFailed">class <strong>PageActionFailed</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.actions.page_action.html#PageActionFailed">PageActionFailed</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="PageActionFailed-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#PageActionFailed-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#PageActionFailed-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="PageActionFailed-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#PageActionFailed-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="PageActionFailed-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#PageActionFailed-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="PageActionFailed-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#PageActionFailed-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="PageActionFailed-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#PageActionFailed-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="PageActionFailed-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="PageActionFailed-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#PageActionFailed-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="PageActionFailed-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#PageActionFailed-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="PageActionFailed-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="PageActionFailed-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#PageActionFailed-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="PageActionFailed-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="PageActionNotSupported">class <strong>PageActionNotSupported</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.actions.page_action.html#PageActionNotSupported">PageActionNotSupported</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="PageActionNotSupported-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#PageActionNotSupported-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#PageActionNotSupported-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="PageActionNotSupported-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#PageActionNotSupported-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="PageActionNotSupported-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#PageActionNotSupported-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="PageActionNotSupported-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#PageActionNotSupported-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="PageActionNotSupported-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#PageActionNotSupported-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="PageActionNotSupported-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="PageActionNotSupported-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#PageActionNotSupported-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="PageActionNotSupported-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#PageActionNotSupported-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="PageActionNotSupported-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="PageActionNotSupported-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#PageActionNotSupported-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="PageActionNotSupported-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-EvaluateCallbackWithElement"><strong>EvaluateCallbackWithElement</strong></a>(tab, callback_js, selector<font color="#909090">=None</font>, text<font color="#909090">=None</font>, element_function<font color="#909090">=None</font>, wait<font color="#909090">=False</font>, timeout_in_seconds<font color="#909090">=60</font>)</dt><dd><tt>Evaluates&nbsp;the&nbsp;JavaScript&nbsp;callback&nbsp;with&nbsp;the&nbsp;given&nbsp;element.<br>
+&nbsp;<br>
+The&nbsp;element&nbsp;may&nbsp;be&nbsp;selected&nbsp;via&nbsp;selector,&nbsp;text,&nbsp;or&nbsp;element_function.<br>
+Only&nbsp;one&nbsp;of&nbsp;these&nbsp;arguments&nbsp;must&nbsp;be&nbsp;specified.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;The&nbsp;callback's&nbsp;return&nbsp;value,&nbsp;if&nbsp;any.&nbsp;The&nbsp;return&nbsp;value&nbsp;must&nbsp;be<br>
+&nbsp;&nbsp;convertible&nbsp;to&nbsp;JSON.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;tab:&nbsp;A&nbsp;telemetry.core.Tab&nbsp;<a href="__builtin__.html#object">object</a>.<br>
+&nbsp;&nbsp;callback_js:&nbsp;The&nbsp;JavaScript&nbsp;callback&nbsp;to&nbsp;call&nbsp;(as&nbsp;string).<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The&nbsp;callback&nbsp;receive&nbsp;2&nbsp;parameters:&nbsp;the&nbsp;element,&nbsp;and&nbsp;information<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string&nbsp;about&nbsp;what&nbsp;method&nbsp;was&nbsp;used&nbsp;to&nbsp;retrieve&nbsp;the&nbsp;element.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Example:&nbsp;'''<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;function(element,&nbsp;info)&nbsp;{<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(!element)&nbsp;{<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw&nbsp;Error('Can&nbsp;not&nbsp;find&nbsp;element:&nbsp;'&nbsp;+&nbsp;info);<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;element.click()<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}'''<br>
+&nbsp;&nbsp;selector:&nbsp;A&nbsp;CSS&nbsp;selector&nbsp;describing&nbsp;the&nbsp;element.<br>
+&nbsp;&nbsp;text:&nbsp;The&nbsp;element&nbsp;must&nbsp;contains&nbsp;this&nbsp;exact&nbsp;text.<br>
+&nbsp;&nbsp;element_function:&nbsp;A&nbsp;JavaScript&nbsp;function&nbsp;(as&nbsp;string)&nbsp;that&nbsp;is&nbsp;used<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;to&nbsp;retrieve&nbsp;the&nbsp;element.&nbsp;For&nbsp;example:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'(function()&nbsp;{&nbsp;return&nbsp;foo.element;&nbsp;})()'.<br>
+&nbsp;&nbsp;wait:&nbsp;Whether&nbsp;to&nbsp;wait&nbsp;for&nbsp;the&nbsp;return&nbsp;value&nbsp;to&nbsp;be&nbsp;true.<br>
+&nbsp;&nbsp;timeout_in_seconds:&nbsp;The&nbsp;timeout&nbsp;for&nbsp;wait&nbsp;(if&nbsp;waiting).</tt></dd></dl>
+ <dl><dt><a name="-IsGestureSourceTypeSupported"><strong>IsGestureSourceTypeSupported</strong></a>(*args, **kwargs)</dt></dl>
+</td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>GESTURE_SOURCE_DEFAULT</strong> = 'DEFAULT'<br>
+<strong>GESTURE_SOURCE_MOUSE</strong> = 'MOUSE'<br>
+<strong>GESTURE_SOURCE_TOUCH</strong> = 'TOUCH'<br>
+<strong>SUPPORTED_GESTURE_SOURCES</strong> = ('DEFAULT', 'MOUSE', 'TOUCH')</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.pinch.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.pinch.html
new file mode 100644
index 0000000..c046f17
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.pinch.html
@@ -0,0 +1,73 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.actions.pinch</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.actions.html"><font color="#ffffff">actions</font></a>.pinch</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/actions/pinch.py">telemetry/internal/actions/pinch.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="os.html">os</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.actions.page_action.html">telemetry.internal.actions.page_action</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.actions.pinch.html#PinchAction">PinchAction</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="PinchAction">class <strong>PinchAction</strong></a>(<a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.actions.pinch.html#PinchAction">PinchAction</a></dd>
+<dd><a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="PinchAction-RunAction"><strong>RunAction</strong></a>(self, tab)</dt></dl>
+
+<dl><dt><a name="PinchAction-WillRunAction"><strong>WillRunAction</strong></a>(self, tab)</dt></dl>
+
+<dl><dt><a name="PinchAction-__init__"><strong>__init__</strong></a>(self, selector<font color="#909090">=None</font>, text<font color="#909090">=None</font>, element_function<font color="#909090">=None</font>, left_anchor_ratio<font color="#909090">=0.5</font>, top_anchor_ratio<font color="#909090">=0.5</font>, scale_factor<font color="#909090">=None</font>, speed_in_pixels_per_second<font color="#909090">=800</font>, synthetic_gesture_source<font color="#909090">='DEFAULT'</font>)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>:<br>
+<dl><dt><a name="PinchAction-CleanUp"><strong>CleanUp</strong></a>(self, tab)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.play.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.play.html
new file mode 100644
index 0000000..c810155
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.play.html
@@ -0,0 +1,96 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.actions.play</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.actions.html"><font color="#ffffff">actions</font></a>.play</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/actions/play.py">telemetry/internal/actions/play.py</a></font></td></tr></table>
+    <p><tt>A&nbsp;Telemetry&nbsp;page_action&nbsp;that&nbsp;performs&nbsp;the&nbsp;"play"&nbsp;action&nbsp;on&nbsp;media&nbsp;elements.<br>
+&nbsp;<br>
+Media&nbsp;elements&nbsp;can&nbsp;be&nbsp;specified&nbsp;by&nbsp;a&nbsp;selector&nbsp;argument.&nbsp;If&nbsp;no&nbsp;selector&nbsp;is<br>
+defined&nbsp;then&nbsp;then&nbsp;the&nbsp;action&nbsp;attempts&nbsp;to&nbsp;play&nbsp;the&nbsp;first&nbsp;video&nbsp;element&nbsp;or&nbsp;audio<br>
+element&nbsp;on&nbsp;the&nbsp;page.&nbsp;A&nbsp;selector&nbsp;can&nbsp;also&nbsp;be&nbsp;'all'&nbsp;to&nbsp;play&nbsp;all&nbsp;media&nbsp;elements.<br>
+&nbsp;<br>
+Other&nbsp;arguments&nbsp;to&nbsp;use&nbsp;are:&nbsp;playing_event_timeout_in_seconds&nbsp;and<br>
+ended_event_timeout_in_seconds,&nbsp;which&nbsp;forces&nbsp;the&nbsp;action&nbsp;to&nbsp;wait&nbsp;until<br>
+playing&nbsp;and&nbsp;ended&nbsp;events&nbsp;get&nbsp;fired&nbsp;respectively.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.actions.media_action.html">telemetry.internal.actions.media_action</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.actions.page_action.html">telemetry.internal.actions.page_action</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.actions.media_action.html#MediaAction">telemetry.internal.actions.media_action.MediaAction</a>(<a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.actions.play.html#PlayAction">PlayAction</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="PlayAction">class <strong>PlayAction</strong></a>(<a href="telemetry.internal.actions.media_action.html#MediaAction">telemetry.internal.actions.media_action.MediaAction</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.actions.play.html#PlayAction">PlayAction</a></dd>
+<dd><a href="telemetry.internal.actions.media_action.html#MediaAction">telemetry.internal.actions.media_action.MediaAction</a></dd>
+<dd><a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="PlayAction-RunAction"><strong>RunAction</strong></a>(self, tab)</dt></dl>
+
+<dl><dt><a name="PlayAction-WillRunAction"><strong>WillRunAction</strong></a>(self, tab)</dt><dd><tt>Load&nbsp;the&nbsp;media&nbsp;metrics&nbsp;JS&nbsp;code&nbsp;prior&nbsp;to&nbsp;running&nbsp;the&nbsp;action.</tt></dd></dl>
+
+<dl><dt><a name="PlayAction-__init__"><strong>__init__</strong></a>(self, selector<font color="#909090">=None</font>, playing_event_timeout_in_seconds<font color="#909090">=0</font>, ended_event_timeout_in_seconds<font color="#909090">=0</font>)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.actions.media_action.html#MediaAction">telemetry.internal.actions.media_action.MediaAction</a>:<br>
+<dl><dt><a name="PlayAction-HasEventCompletedOrError"><strong>HasEventCompletedOrError</strong></a>(self, tab, selector, event_name)</dt></dl>
+
+<dl><dt><a name="PlayAction-LoadJS"><strong>LoadJS</strong></a>(self, tab, js_file_name)</dt><dd><tt>Loads&nbsp;and&nbsp;executes&nbsp;a&nbsp;JS&nbsp;file&nbsp;in&nbsp;the&nbsp;tab.</tt></dd></dl>
+
+<dl><dt><a name="PlayAction-WaitForEvent"><strong>WaitForEvent</strong></a>(self, tab, selector, event_name, timeout_in_seconds)</dt><dd><tt>Halts&nbsp;media&nbsp;action&nbsp;until&nbsp;the&nbsp;selector's&nbsp;event&nbsp;is&nbsp;fired.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;tab:&nbsp;The&nbsp;tab&nbsp;to&nbsp;check&nbsp;for&nbsp;event&nbsp;on.<br>
+&nbsp;&nbsp;selector:&nbsp;Media&nbsp;element&nbsp;selector.<br>
+&nbsp;&nbsp;event_name:&nbsp;Name&nbsp;of&nbsp;the&nbsp;event&nbsp;to&nbsp;check&nbsp;if&nbsp;fired&nbsp;or&nbsp;not.<br>
+&nbsp;&nbsp;timeout_in_seconds:&nbsp;Timeout&nbsp;to&nbsp;check&nbsp;for&nbsp;event,&nbsp;throws&nbsp;an&nbsp;exception&nbsp;if<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;not&nbsp;fired.</tt></dd></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>:<br>
+<dl><dt><a name="PlayAction-CleanUp"><strong>CleanUp</strong></a>(self, tab)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.repaint_continuously.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.repaint_continuously.html
new file mode 100644
index 0000000..4a1e7dc
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.repaint_continuously.html
@@ -0,0 +1,79 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.actions.repaint_continuously</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.actions.html"><font color="#ffffff">actions</font></a>.repaint_continuously</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/actions/repaint_continuously.py">telemetry/internal/actions/repaint_continuously.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.actions.page_action.html">telemetry.internal.actions.page_action</a><br>
+</td><td width="25%" valign=top><a href="time.html">time</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.util.html">telemetry.core.util</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.actions.repaint_continuously.html#RepaintContinuouslyAction">RepaintContinuouslyAction</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="RepaintContinuouslyAction">class <strong>RepaintContinuouslyAction</strong></a>(<a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Continuously&nbsp;repaints&nbsp;the&nbsp;visible&nbsp;content&nbsp;by&nbsp;requesting&nbsp;animation&nbsp;frames<br>
+until&nbsp;self.<strong>seconds</strong>&nbsp;have&nbsp;elapsed&nbsp;AND&nbsp;at&nbsp;least&nbsp;three&nbsp;RAFs&nbsp;have&nbsp;been&nbsp;fired.&nbsp;Times<br>
+out&nbsp;after&nbsp;max(60,&nbsp;self.<strong>seconds</strong>),&nbsp;if&nbsp;less&nbsp;than&nbsp;three&nbsp;RAFs&nbsp;were&nbsp;fired.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.actions.repaint_continuously.html#RepaintContinuouslyAction">RepaintContinuouslyAction</a></dd>
+<dd><a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="RepaintContinuouslyAction-RunAction"><strong>RunAction</strong></a>(self, tab)</dt></dl>
+
+<dl><dt><a name="RepaintContinuouslyAction-__init__"><strong>__init__</strong></a>(self, seconds)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>:<br>
+<dl><dt><a name="RepaintContinuouslyAction-CleanUp"><strong>CleanUp</strong></a>(self, tab)</dt></dl>
+
+<dl><dt><a name="RepaintContinuouslyAction-WillRunAction"><strong>WillRunAction</strong></a>(self, tab)</dt><dd><tt>Override&nbsp;to&nbsp;do&nbsp;action-specific&nbsp;setup&nbsp;before<br>
+Test.WillRunAction&nbsp;is&nbsp;called.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.repeatable_scroll.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.repeatable_scroll.html
new file mode 100644
index 0000000..22179ba
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.repeatable_scroll.html
@@ -0,0 +1,73 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.actions.repeatable_scroll</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.actions.html"><font color="#ffffff">actions</font></a>.repeatable_scroll</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/actions/repeatable_scroll.py">telemetry/internal/actions/repeatable_scroll.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2015&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.actions.page_action.html">telemetry.internal.actions.page_action</a><br>
+</td><td width="25%" valign=top><a href="telemetry.web_perf.timeline_interaction_record.html">telemetry.web_perf.timeline_interaction_record</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.actions.repeatable_scroll.html#RepeatableScrollAction">RepeatableScrollAction</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="RepeatableScrollAction">class <strong>RepeatableScrollAction</strong></a>(<a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.actions.repeatable_scroll.html#RepeatableScrollAction">RepeatableScrollAction</a></dd>
+<dd><a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="RepeatableScrollAction-RunAction"><strong>RunAction</strong></a>(self, tab)</dt></dl>
+
+<dl><dt><a name="RepeatableScrollAction-WillRunAction"><strong>WillRunAction</strong></a>(self, tab)</dt></dl>
+
+<dl><dt><a name="RepeatableScrollAction-__init__"><strong>__init__</strong></a>(self, x_scroll_distance_ratio<font color="#909090">=0.0</font>, y_scroll_distance_ratio<font color="#909090">=0.5</font>, repeat_count<font color="#909090">=0</font>, repeat_delay_ms<font color="#909090">=250</font>)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>:<br>
+<dl><dt><a name="RepeatableScrollAction-CleanUp"><strong>CleanUp</strong></a>(self, tab)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.scroll.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.scroll.html
new file mode 100644
index 0000000..4240094
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.scroll.html
@@ -0,0 +1,74 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.actions.scroll</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.actions.html"><font color="#ffffff">actions</font></a>.scroll</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/actions/scroll.py">telemetry/internal/actions/scroll.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2012&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="os.html">os</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.actions.page_action.html">telemetry.internal.actions.page_action</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.actions.scroll.html#ScrollAction">ScrollAction</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ScrollAction">class <strong>ScrollAction</strong></a>(<a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.actions.scroll.html#ScrollAction">ScrollAction</a></dd>
+<dd><a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="ScrollAction-RunAction"><strong>RunAction</strong></a>(self, tab)</dt></dl>
+
+<dl><dt><a name="ScrollAction-WillRunAction"><strong>WillRunAction</strong></a>(self, tab)</dt></dl>
+
+<dl><dt><a name="ScrollAction-__init__"><strong>__init__</strong></a>(self, selector<font color="#909090">=None</font>, text<font color="#909090">=None</font>, element_function<font color="#909090">=None</font>, left_start_ratio<font color="#909090">=0.5</font>, top_start_ratio<font color="#909090">=0.5</font>, direction<font color="#909090">='down'</font>, distance<font color="#909090">=None</font>, distance_expr<font color="#909090">=None</font>, speed_in_pixels_per_second<font color="#909090">=800</font>, use_touch<font color="#909090">=False</font>, synthetic_gesture_source<font color="#909090">='DEFAULT'</font>)</dt><dd><tt>#&nbsp;TODO(chrishenry):&nbsp;Ignore&nbsp;attributes,&nbsp;to&nbsp;be&nbsp;deleted&nbsp;when&nbsp;usage&nbsp;in<br>
+#&nbsp;other&nbsp;repo&nbsp;is&nbsp;cleaned&nbsp;up.</tt></dd></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>:<br>
+<dl><dt><a name="ScrollAction-CleanUp"><strong>CleanUp</strong></a>(self, tab)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.scroll_bounce.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.scroll_bounce.html
new file mode 100644
index 0000000..bb44b14
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.scroll_bounce.html
@@ -0,0 +1,73 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.actions.scroll_bounce</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.actions.html"><font color="#ffffff">actions</font></a>.scroll_bounce</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/actions/scroll_bounce.py">telemetry/internal/actions/scroll_bounce.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="os.html">os</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.actions.page_action.html">telemetry.internal.actions.page_action</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.actions.scroll_bounce.html#ScrollBounceAction">ScrollBounceAction</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ScrollBounceAction">class <strong>ScrollBounceAction</strong></a>(<a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.actions.scroll_bounce.html#ScrollBounceAction">ScrollBounceAction</a></dd>
+<dd><a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="ScrollBounceAction-RunAction"><strong>RunAction</strong></a>(self, tab)</dt></dl>
+
+<dl><dt><a name="ScrollBounceAction-WillRunAction"><strong>WillRunAction</strong></a>(self, tab)</dt></dl>
+
+<dl><dt><a name="ScrollBounceAction-__init__"><strong>__init__</strong></a>(self, selector<font color="#909090">=None</font>, text<font color="#909090">=None</font>, element_function<font color="#909090">=None</font>, left_start_ratio<font color="#909090">=0.5</font>, top_start_ratio<font color="#909090">=0.5</font>, direction<font color="#909090">='down'</font>, distance<font color="#909090">=100</font>, overscroll<font color="#909090">=10</font>, repeat_count<font color="#909090">=10</font>, speed_in_pixels_per_second<font color="#909090">=400</font>, synthetic_gesture_source<font color="#909090">='DEFAULT'</font>)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>:<br>
+<dl><dt><a name="ScrollBounceAction-CleanUp"><strong>CleanUp</strong></a>(self, tab)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.seek.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.seek.html
new file mode 100644
index 0000000..42b7d31
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.seek.html
@@ -0,0 +1,100 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.actions.seek</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.actions.html"><font color="#ffffff">actions</font></a>.seek</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/actions/seek.py">telemetry/internal/actions/seek.py</a></font></td></tr></table>
+    <p><tt>A&nbsp;Telemetry&nbsp;page_action&nbsp;that&nbsp;performs&nbsp;the&nbsp;"seek"&nbsp;action&nbsp;on&nbsp;media&nbsp;elements.<br>
+&nbsp;<br>
+Action&nbsp;parameters&nbsp;are:<br>
+-&nbsp;seconds:&nbsp;The&nbsp;media&nbsp;time&nbsp;to&nbsp;seek&nbsp;to.&nbsp;Test&nbsp;fails&nbsp;if&nbsp;not&nbsp;provided.<br>
+-&nbsp;selector:&nbsp;If&nbsp;no&nbsp;selector&nbsp;is&nbsp;defined&nbsp;then&nbsp;the&nbsp;action&nbsp;attempts&nbsp;to&nbsp;seek&nbsp;the&nbsp;first<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;media&nbsp;element&nbsp;on&nbsp;the&nbsp;page.&nbsp;If&nbsp;'all'&nbsp;then&nbsp;seek&nbsp;all&nbsp;media&nbsp;elements.<br>
+-&nbsp;timeout_in_seconds:&nbsp;Maximum&nbsp;waiting&nbsp;time&nbsp;for&nbsp;the&nbsp;"seeked"&nbsp;event<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(dispatched&nbsp;when&nbsp;the&nbsp;seeked&nbsp;operation&nbsp;completes)<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;to&nbsp;be&nbsp;fired.&nbsp;&nbsp;0&nbsp;means&nbsp;do&nbsp;not&nbsp;wait.<br>
+-&nbsp;log_time:&nbsp;If&nbsp;true&nbsp;the&nbsp;seek&nbsp;time&nbsp;is&nbsp;recorded,&nbsp;otherwise&nbsp;media<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;measurement&nbsp;will&nbsp;not&nbsp;be&nbsp;aware&nbsp;of&nbsp;the&nbsp;seek&nbsp;action.&nbsp;Used&nbsp;to<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;perform&nbsp;multiple&nbsp;seeks.&nbsp;Default&nbsp;true.<br>
+-&nbsp;label:&nbsp;A&nbsp;suffix&nbsp;string&nbsp;to&nbsp;name&nbsp;the&nbsp;seek&nbsp;perf&nbsp;measurement.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.actions.media_action.html">telemetry.internal.actions.media_action</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.actions.page_action.html">telemetry.internal.actions.page_action</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.actions.media_action.html#MediaAction">telemetry.internal.actions.media_action.MediaAction</a>(<a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.actions.seek.html#SeekAction">SeekAction</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="SeekAction">class <strong>SeekAction</strong></a>(<a href="telemetry.internal.actions.media_action.html#MediaAction">telemetry.internal.actions.media_action.MediaAction</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.actions.seek.html#SeekAction">SeekAction</a></dd>
+<dd><a href="telemetry.internal.actions.media_action.html#MediaAction">telemetry.internal.actions.media_action.MediaAction</a></dd>
+<dd><a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="SeekAction-RunAction"><strong>RunAction</strong></a>(self, tab)</dt></dl>
+
+<dl><dt><a name="SeekAction-WillRunAction"><strong>WillRunAction</strong></a>(self, tab)</dt><dd><tt>Load&nbsp;the&nbsp;media&nbsp;metrics&nbsp;JS&nbsp;code&nbsp;prior&nbsp;to&nbsp;running&nbsp;the&nbsp;action.</tt></dd></dl>
+
+<dl><dt><a name="SeekAction-__init__"><strong>__init__</strong></a>(self, seconds, selector<font color="#909090">=None</font>, timeout_in_seconds<font color="#909090">=0</font>, log_time<font color="#909090">=True</font>, label<font color="#909090">=''</font>)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.actions.media_action.html#MediaAction">telemetry.internal.actions.media_action.MediaAction</a>:<br>
+<dl><dt><a name="SeekAction-HasEventCompletedOrError"><strong>HasEventCompletedOrError</strong></a>(self, tab, selector, event_name)</dt></dl>
+
+<dl><dt><a name="SeekAction-LoadJS"><strong>LoadJS</strong></a>(self, tab, js_file_name)</dt><dd><tt>Loads&nbsp;and&nbsp;executes&nbsp;a&nbsp;JS&nbsp;file&nbsp;in&nbsp;the&nbsp;tab.</tt></dd></dl>
+
+<dl><dt><a name="SeekAction-WaitForEvent"><strong>WaitForEvent</strong></a>(self, tab, selector, event_name, timeout_in_seconds)</dt><dd><tt>Halts&nbsp;media&nbsp;action&nbsp;until&nbsp;the&nbsp;selector's&nbsp;event&nbsp;is&nbsp;fired.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;tab:&nbsp;The&nbsp;tab&nbsp;to&nbsp;check&nbsp;for&nbsp;event&nbsp;on.<br>
+&nbsp;&nbsp;selector:&nbsp;Media&nbsp;element&nbsp;selector.<br>
+&nbsp;&nbsp;event_name:&nbsp;Name&nbsp;of&nbsp;the&nbsp;event&nbsp;to&nbsp;check&nbsp;if&nbsp;fired&nbsp;or&nbsp;not.<br>
+&nbsp;&nbsp;timeout_in_seconds:&nbsp;Timeout&nbsp;to&nbsp;check&nbsp;for&nbsp;event,&nbsp;throws&nbsp;an&nbsp;exception&nbsp;if<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;not&nbsp;fired.</tt></dd></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>:<br>
+<dl><dt><a name="SeekAction-CleanUp"><strong>CleanUp</strong></a>(self, tab)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.swipe.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.swipe.html
new file mode 100644
index 0000000..fe542d5
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.swipe.html
@@ -0,0 +1,73 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.actions.swipe</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.actions.html"><font color="#ffffff">actions</font></a>.swipe</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/actions/swipe.py">telemetry/internal/actions/swipe.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="os.html">os</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.actions.page_action.html">telemetry.internal.actions.page_action</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.actions.swipe.html#SwipeAction">SwipeAction</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="SwipeAction">class <strong>SwipeAction</strong></a>(<a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.actions.swipe.html#SwipeAction">SwipeAction</a></dd>
+<dd><a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="SwipeAction-RunAction"><strong>RunAction</strong></a>(self, tab)</dt></dl>
+
+<dl><dt><a name="SwipeAction-WillRunAction"><strong>WillRunAction</strong></a>(self, tab)</dt></dl>
+
+<dl><dt><a name="SwipeAction-__init__"><strong>__init__</strong></a>(self, selector<font color="#909090">=None</font>, text<font color="#909090">=None</font>, element_function<font color="#909090">=None</font>, left_start_ratio<font color="#909090">=0.5</font>, top_start_ratio<font color="#909090">=0.5</font>, direction<font color="#909090">='left'</font>, distance<font color="#909090">=100</font>, speed_in_pixels_per_second<font color="#909090">=800</font>, synthetic_gesture_source<font color="#909090">='DEFAULT'</font>)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>:<br>
+<dl><dt><a name="SwipeAction-CleanUp"><strong>CleanUp</strong></a>(self, tab)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.tap.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.tap.html
new file mode 100644
index 0000000..166ac78
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.tap.html
@@ -0,0 +1,75 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.actions.tap</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.actions.html"><font color="#ffffff">actions</font></a>.tap</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/actions/tap.py">telemetry/internal/actions/tap.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="os.html">os</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.actions.page_action.html">telemetry.internal.actions.page_action</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.actions.tap.html#TapAction">TapAction</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TapAction">class <strong>TapAction</strong></a>(<a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.actions.tap.html#TapAction">TapAction</a></dd>
+<dd><a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="TapAction-HasElementSelector"><strong>HasElementSelector</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TapAction-RunAction"><strong>RunAction</strong></a>(self, tab)</dt></dl>
+
+<dl><dt><a name="TapAction-WillRunAction"><strong>WillRunAction</strong></a>(self, tab)</dt></dl>
+
+<dl><dt><a name="TapAction-__init__"><strong>__init__</strong></a>(self, selector<font color="#909090">=None</font>, text<font color="#909090">=None</font>, element_function<font color="#909090">=None</font>, left_position_percentage<font color="#909090">=0.5</font>, top_position_percentage<font color="#909090">=0.5</font>, duration_ms<font color="#909090">=50</font>, synthetic_gesture_source<font color="#909090">='DEFAULT'</font>)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>:<br>
+<dl><dt><a name="TapAction-CleanUp"><strong>CleanUp</strong></a>(self, tab)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.wait.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.wait.html
new file mode 100644
index 0000000..6294c76
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.actions.wait.html
@@ -0,0 +1,73 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.actions.wait</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.actions.html"><font color="#ffffff">actions</font></a>.wait</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/actions/wait.py">telemetry/internal/actions/wait.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2012&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.actions.page_action.html">telemetry.internal.actions.page_action</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.actions.wait.html#WaitForElementAction">WaitForElementAction</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="WaitForElementAction">class <strong>WaitForElementAction</strong></a>(<a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.actions.wait.html#WaitForElementAction">WaitForElementAction</a></dd>
+<dd><a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="WaitForElementAction-RunAction"><strong>RunAction</strong></a>(self, tab)</dt></dl>
+
+<dl><dt><a name="WaitForElementAction-__init__"><strong>__init__</strong></a>(self, selector<font color="#909090">=None</font>, text<font color="#909090">=None</font>, element_function<font color="#909090">=None</font>, timeout_in_seconds<font color="#909090">=60</font>)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>:<br>
+<dl><dt><a name="WaitForElementAction-CleanUp"><strong>CleanUp</strong></a>(self, tab)</dt></dl>
+
+<dl><dt><a name="WaitForElementAction-WillRunAction"><strong>WillRunAction</strong></a>(self, tab)</dt><dd><tt>Override&nbsp;to&nbsp;do&nbsp;action-specific&nbsp;setup&nbsp;before<br>
+Test.WillRunAction&nbsp;is&nbsp;called.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.actions.page_action.html#PageAction">telemetry.internal.actions.page_action.PageAction</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.app.android_app.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.app.android_app.html
new file mode 100644
index 0000000..4aa1e0d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.app.android_app.html
@@ -0,0 +1,93 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.app.android_app</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.app.html"><font color="#ffffff">app</font></a>.android_app</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/app/android_app.py">telemetry/internal/app/android_app.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2012&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.app.html">telemetry.internal.app</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.app.html#App">telemetry.internal.app.App</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.app.android_app.html#AndroidApp">AndroidApp</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="AndroidApp">class <strong>AndroidApp</strong></a>(<a href="telemetry.internal.app.html#App">telemetry.internal.app.App</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;running&nbsp;android&nbsp;app&nbsp;instance&nbsp;that&nbsp;can&nbsp;be&nbsp;controlled&nbsp;in&nbsp;a&nbsp;limited&nbsp;way.<br>
+&nbsp;<br>
+Be&nbsp;sure&nbsp;to&nbsp;clean&nbsp;up&nbsp;after&nbsp;yourself&nbsp;by&nbsp;calling&nbsp;<a href="#AndroidApp-Close">Close</a>()&nbsp;when&nbsp;you&nbsp;are&nbsp;done&nbsp;with<br>
+the&nbsp;app.&nbsp;Or&nbsp;better&nbsp;yet:<br>
+&nbsp;&nbsp;with&nbsp;possible_android_app.Create(options)&nbsp;as&nbsp;android_app:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;...&nbsp;do&nbsp;all&nbsp;your&nbsp;operations&nbsp;on&nbsp;android_app&nbsp;here<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.app.android_app.html#AndroidApp">AndroidApp</a></dd>
+<dd><a href="telemetry.internal.app.html#App">telemetry.internal.app.App</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="AndroidApp-Close"><strong>Close</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidApp-GetProcess"><strong>GetProcess</strong></a>(self, subprocess_name)</dt><dd><tt>Returns&nbsp;the&nbsp;process&nbsp;with&nbsp;the&nbsp;specified&nbsp;subprocess&nbsp;name.</tt></dd></dl>
+
+<dl><dt><a name="AndroidApp-GetProcesses"><strong>GetProcesses</strong></a>(self)</dt><dd><tt>Returns&nbsp;the&nbsp;current&nbsp;set&nbsp;of&nbsp;processes&nbsp;belonging&nbsp;to&nbsp;this&nbsp;app.</tt></dd></dl>
+
+<dl><dt><a name="AndroidApp-GetWebViews"><strong>GetWebViews</strong></a>(self)</dt><dd><tt>Returns&nbsp;the&nbsp;set&nbsp;of&nbsp;all&nbsp;WebViews&nbsp;belonging&nbsp;to&nbsp;all&nbsp;processes&nbsp;of&nbsp;the&nbsp;app.</tt></dd></dl>
+
+<dl><dt><a name="AndroidApp-__init__"><strong>__init__</strong></a>(self, app_backend, platform_backend)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.app.html#App">telemetry.internal.app.App</a>:<br>
+<dl><dt><a name="AndroidApp-GetStackTrace"><strong>GetStackTrace</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidApp-GetStandardOutput"><strong>GetStandardOutput</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidApp-__enter__"><strong>__enter__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidApp-__exit__"><strong>__exit__</strong></a>(self, *args)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.app.html#App">telemetry.internal.app.App</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>app_type</strong></dt>
+</dl>
+<dl><dt><strong>platform</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.app.android_process.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.app.android_process.html
new file mode 100644
index 0000000..993c2d5
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.app.android_process.html
@@ -0,0 +1,132 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.app.android_process</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.app.html"><font color="#ffffff">app</font></a>.android_process</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/app/android_process.py">telemetry/internal/app/android_process.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2015&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.backends.chrome_inspector.devtools_client_backend.html">telemetry.internal.backends.chrome_inspector.devtools_client_backend</a><br>
+</td><td width="25%" valign=top><a href="devil.android.ports.html">devil.android.ports</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.browser.web_contents.html">telemetry.internal.browser.web_contents</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.app.android_process.html#AndroidProcess">AndroidProcess</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="exceptions.html#Exception">exceptions.Exception</a>(<a href="exceptions.html#BaseException">exceptions.BaseException</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.app.android_process.html#WebViewNotFoundException">WebViewNotFoundException</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="AndroidProcess">class <strong>AndroidProcess</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Represents&nbsp;a&nbsp;single&nbsp;android&nbsp;process.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="AndroidProcess-GetWebViews"><strong>GetWebViews</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidProcess-__init__"><strong>__init__</strong></a>(self, app_backend, pid, name)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>name</strong></dt>
+</dl>
+<dl><dt><strong>pid</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="WebViewNotFoundException">class <strong>WebViewNotFoundException</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.app.android_process.html#WebViewNotFoundException">WebViewNotFoundException</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="WebViewNotFoundException-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#WebViewNotFoundException-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#WebViewNotFoundException-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="WebViewNotFoundException-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#WebViewNotFoundException-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="WebViewNotFoundException-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#WebViewNotFoundException-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="WebViewNotFoundException-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#WebViewNotFoundException-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="WebViewNotFoundException-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#WebViewNotFoundException-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="WebViewNotFoundException-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="WebViewNotFoundException-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#WebViewNotFoundException-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="WebViewNotFoundException-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#WebViewNotFoundException-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="WebViewNotFoundException-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="WebViewNotFoundException-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#WebViewNotFoundException-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="WebViewNotFoundException-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.app.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.app.html
new file mode 100644
index 0000000..460b3ff
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.app.html
@@ -0,0 +1,82 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: package telemetry.internal.app</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.app</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/app/__init__.py">telemetry/internal/app/__init__.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2012&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Package Contents</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.app.android_app.html">android_app</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.app.android_app_unittest.html">android_app_unittest</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.app.android_process.html">android_process</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.app.possible_app.html">possible_app</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.app.html#App">App</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="App">class <strong>App</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;running&nbsp;application&nbsp;instance&nbsp;that&nbsp;can&nbsp;be&nbsp;controlled&nbsp;in&nbsp;a&nbsp;limited&nbsp;way.<br>
+&nbsp;<br>
+Be&nbsp;sure&nbsp;to&nbsp;clean&nbsp;up&nbsp;after&nbsp;yourself&nbsp;by&nbsp;calling&nbsp;<a href="#App-Close">Close</a>()&nbsp;when&nbsp;you&nbsp;are&nbsp;done&nbsp;with<br>
+the&nbsp;app.&nbsp;Or&nbsp;better&nbsp;yet:<br>
+&nbsp;&nbsp;with&nbsp;possible_app.Create(options)&nbsp;as&nbsp;app:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;...&nbsp;do&nbsp;all&nbsp;your&nbsp;operations&nbsp;on&nbsp;app&nbsp;here<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="App-Close"><strong>Close</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="App-GetStackTrace"><strong>GetStackTrace</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="App-GetStandardOutput"><strong>GetStandardOutput</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="App-__enter__"><strong>__enter__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="App-__exit__"><strong>__exit__</strong></a>(self, *args)</dt></dl>
+
+<dl><dt><a name="App-__init__"><strong>__init__</strong></a>(self, app_backend, platform_backend)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>app_type</strong></dt>
+</dl>
+<dl><dt><strong>platform</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.app.possible_app.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.app.possible_app.html
new file mode 100644
index 0000000..da762aa
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.app.possible_app.html
@@ -0,0 +1,67 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.app.possible_app</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.app.html"><font color="#ffffff">app</font></a>.possible_app</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/app/possible_app.py">telemetry/internal/app/possible_app.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2012&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.app.possible_app.html#PossibleApp">PossibleApp</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="PossibleApp">class <strong>PossibleApp</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;factory&nbsp;class&nbsp;that&nbsp;can&nbsp;be&nbsp;used&nbsp;to&nbsp;create&nbsp;a&nbsp;running&nbsp;instance&nbsp;of&nbsp;app.<br>
+&nbsp;<br>
+Call&nbsp;<a href="#PossibleApp-Create">Create</a>()&nbsp;to&nbsp;launch&nbsp;the&nbsp;app&nbsp;and&nbsp;begin&nbsp;manipulating&nbsp;it.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="PossibleApp-Create"><strong>Create</strong></a>(self, finder_options)</dt></dl>
+
+<dl><dt><a name="PossibleApp-SupportsOptions"><strong>SupportsOptions</strong></a>(self, finder_options)</dt><dd><tt>Tests&nbsp;for&nbsp;extension&nbsp;support.</tt></dd></dl>
+
+<dl><dt><a name="PossibleApp-__init__"><strong>__init__</strong></a>(self, app_type, target_os)</dt></dl>
+
+<dl><dt><a name="PossibleApp-__repr__"><strong>__repr__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>app_type</strong></dt>
+</dl>
+<dl><dt><strong>platform</strong></dt>
+</dl>
+<dl><dt><strong>target_os</strong></dt>
+<dd><tt>Target&nbsp;OS,&nbsp;the&nbsp;app&nbsp;will&nbsp;run&nbsp;on.</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.android_app_backend.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.android_app_backend.html
new file mode 100644
index 0000000..d2c2a06
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.android_app_backend.html
@@ -0,0 +1,111 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.android_app_backend</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.android_app_backend</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/android_app_backend.py">telemetry/internal/backends/android_app_backend.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2012&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.backends.android_browser_backend_settings.html">telemetry.internal.backends.android_browser_backend_settings</a><br>
+<a href="telemetry.internal.backends.android_command_line_backend.html">telemetry.internal.backends.android_command_line_backend</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.app.android_process.html">telemetry.internal.app.android_process</a><br>
+<a href="telemetry.internal.backends.app_backend.html">telemetry.internal.backends.app_backend</a><br>
+</td><td width="25%" valign=top><a href="re.html">re</a><br>
+<a href="telemetry.core.util.html">telemetry.core.util</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.app_backend.html#AppBackend">telemetry.internal.backends.app_backend.AppBackend</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.android_app_backend.html#AndroidAppBackend">AndroidAppBackend</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="AndroidAppBackend">class <strong>AndroidAppBackend</strong></a>(<a href="telemetry.internal.backends.app_backend.html#AppBackend">telemetry.internal.backends.app_backend.AppBackend</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.android_app_backend.html#AndroidAppBackend">AndroidAppBackend</a></dd>
+<dd><a href="telemetry.internal.backends.app_backend.html#AppBackend">telemetry.internal.backends.app_backend.AppBackend</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="AndroidAppBackend-Close"><strong>Close</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidAppBackend-GetProcess"><strong>GetProcess</strong></a>(self, subprocess_name)</dt></dl>
+
+<dl><dt><a name="AndroidAppBackend-GetProcesses"><strong>GetProcesses</strong></a>(self, process_filter<font color="#909090">=None</font>)</dt></dl>
+
+<dl><dt><a name="AndroidAppBackend-GetStackTrace"><strong>GetStackTrace</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidAppBackend-GetStandardOutput"><strong>GetStandardOutput</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidAppBackend-GetWebViews"><strong>GetWebViews</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidAppBackend-GetWebviewStartupArgs"><strong>GetWebviewStartupArgs</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidAppBackend-IsAppRunning"><strong>IsAppRunning</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidAppBackend-Start"><strong>Start</strong></a>(self)</dt><dd><tt>Start&nbsp;an&nbsp;Android&nbsp;app&nbsp;and&nbsp;wait&nbsp;for&nbsp;it&nbsp;to&nbsp;finish&nbsp;launching.<br>
+&nbsp;<br>
+If&nbsp;the&nbsp;app&nbsp;has&nbsp;webviews,&nbsp;the&nbsp;app&nbsp;is&nbsp;launched&nbsp;with&nbsp;the&nbsp;suitable<br>
+command&nbsp;line&nbsp;arguments.<br>
+&nbsp;<br>
+AppStory&nbsp;derivations&nbsp;can&nbsp;customize&nbsp;the&nbsp;wait-for-ready-state&nbsp;to&nbsp;wait<br>
+for&nbsp;a&nbsp;more&nbsp;specific&nbsp;event&nbsp;if&nbsp;needed.</tt></dd></dl>
+
+<dl><dt><a name="AndroidAppBackend-__init__"><strong>__init__</strong></a>(self, android_platform_backend, start_intent, is_app_ready_predicate<font color="#909090">=None</font>, app_has_webviews<font color="#909090">=True</font>)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>device</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.internal.backends.app_backend.html#AppBackend">telemetry.internal.backends.app_backend.AppBackend</a>:<br>
+<dl><dt><a name="AndroidAppBackend-SetApp"><strong>SetApp</strong></a>(self, app)</dt></dl>
+
+<dl><dt><a name="AndroidAppBackend-__del__"><strong>__del__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.backends.app_backend.html#AppBackend">telemetry.internal.backends.app_backend.AppBackend</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>app</strong></dt>
+</dl>
+<dl><dt><strong>app_type</strong></dt>
+</dl>
+<dl><dt><strong>pid</strong></dt>
+</dl>
+<dl><dt><strong>platform_backend</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.android_browser_backend_settings.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.android_browser_backend_settings.html
new file mode 100644
index 0000000..771d1e0
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.android_browser_backend_settings.html
@@ -0,0 +1,247 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.android_browser_backend_settings</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.android_browser_backend_settings</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/android_browser_backend_settings.py">telemetry/internal/backends/android_browser_backend_settings.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+</td><td width="25%" valign=top><a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top><a href="time.html">time</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.android_browser_backend_settings.html#AndroidBrowserBackendSettings">AndroidBrowserBackendSettings</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.android_browser_backend_settings.html#ChromeBackendSettings">ChromeBackendSettings</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.internal.backends.android_browser_backend_settings.html#ContentShellBackendSettings">ContentShellBackendSettings</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.internal.backends.android_browser_backend_settings.html#WebviewBackendSettings">WebviewBackendSettings</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.android_browser_backend_settings.html#WebviewShellBackendSettings">WebviewShellBackendSettings</a>
+</font></dt></dl>
+</dd>
+</dl>
+</dd>
+</dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="AndroidBrowserBackendSettings">class <strong>AndroidBrowserBackendSettings</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="AndroidBrowserBackendSettings-GetCommandLineFile"><strong>GetCommandLineFile</strong></a>(self, is_user_debug_build)</dt></dl>
+
+<dl><dt><a name="AndroidBrowserBackendSettings-GetDevtoolsRemotePort"><strong>GetDevtoolsRemotePort</strong></a>(self, device)</dt></dl>
+
+<dl><dt><a name="AndroidBrowserBackendSettings-__init__"><strong>__init__</strong></a>(self, activity, cmdline_file, package, pseudo_exec_name, supports_tab_control)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>activity</strong></dt>
+</dl>
+<dl><dt><strong>package</strong></dt>
+</dl>
+<dl><dt><strong>profile_ignore_list</strong></dt>
+</dl>
+<dl><dt><strong>pseudo_exec_name</strong></dt>
+</dl>
+<dl><dt><strong>supports_tab_control</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ChromeBackendSettings">class <strong>ChromeBackendSettings</strong></a>(<a href="telemetry.internal.backends.android_browser_backend_settings.html#AndroidBrowserBackendSettings">AndroidBrowserBackendSettings</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.android_browser_backend_settings.html#ChromeBackendSettings">ChromeBackendSettings</a></dd>
+<dd><a href="telemetry.internal.backends.android_browser_backend_settings.html#AndroidBrowserBackendSettings">AndroidBrowserBackendSettings</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="ChromeBackendSettings-GetCommandLineFile"><strong>GetCommandLineFile</strong></a>(self, is_user_debug_build)</dt></dl>
+
+<dl><dt><a name="ChromeBackendSettings-GetDevtoolsRemotePort"><strong>GetDevtoolsRemotePort</strong></a>(self, device)</dt></dl>
+
+<dl><dt><a name="ChromeBackendSettings-__init__"><strong>__init__</strong></a>(self, package)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.backends.android_browser_backend_settings.html#AndroidBrowserBackendSettings">AndroidBrowserBackendSettings</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>activity</strong></dt>
+</dl>
+<dl><dt><strong>package</strong></dt>
+</dl>
+<dl><dt><strong>profile_ignore_list</strong></dt>
+</dl>
+<dl><dt><strong>pseudo_exec_name</strong></dt>
+</dl>
+<dl><dt><strong>supports_tab_control</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ContentShellBackendSettings">class <strong>ContentShellBackendSettings</strong></a>(<a href="telemetry.internal.backends.android_browser_backend_settings.html#AndroidBrowserBackendSettings">AndroidBrowserBackendSettings</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.android_browser_backend_settings.html#ContentShellBackendSettings">ContentShellBackendSettings</a></dd>
+<dd><a href="telemetry.internal.backends.android_browser_backend_settings.html#AndroidBrowserBackendSettings">AndroidBrowserBackendSettings</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="ContentShellBackendSettings-GetDevtoolsRemotePort"><strong>GetDevtoolsRemotePort</strong></a>(self, device)</dt></dl>
+
+<dl><dt><a name="ContentShellBackendSettings-__init__"><strong>__init__</strong></a>(self, package)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.backends.android_browser_backend_settings.html#AndroidBrowserBackendSettings">AndroidBrowserBackendSettings</a>:<br>
+<dl><dt><a name="ContentShellBackendSettings-GetCommandLineFile"><strong>GetCommandLineFile</strong></a>(self, is_user_debug_build)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.backends.android_browser_backend_settings.html#AndroidBrowserBackendSettings">AndroidBrowserBackendSettings</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>activity</strong></dt>
+</dl>
+<dl><dt><strong>package</strong></dt>
+</dl>
+<dl><dt><strong>profile_ignore_list</strong></dt>
+</dl>
+<dl><dt><strong>pseudo_exec_name</strong></dt>
+</dl>
+<dl><dt><strong>supports_tab_control</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="WebviewBackendSettings">class <strong>WebviewBackendSettings</strong></a>(<a href="telemetry.internal.backends.android_browser_backend_settings.html#AndroidBrowserBackendSettings">AndroidBrowserBackendSettings</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.android_browser_backend_settings.html#WebviewBackendSettings">WebviewBackendSettings</a></dd>
+<dd><a href="telemetry.internal.backends.android_browser_backend_settings.html#AndroidBrowserBackendSettings">AndroidBrowserBackendSettings</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="WebviewBackendSettings-GetDevtoolsRemotePort"><strong>GetDevtoolsRemotePort</strong></a>(self, device)</dt></dl>
+
+<dl><dt><a name="WebviewBackendSettings-__init__"><strong>__init__</strong></a>(self, package, activity<font color="#909090">='org.chromium.webview_shell.TelemetryActivity'</font>, cmdline_file<font color="#909090">='/data/local/tmp/webview-command-line'</font>)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.backends.android_browser_backend_settings.html#AndroidBrowserBackendSettings">AndroidBrowserBackendSettings</a>:<br>
+<dl><dt><a name="WebviewBackendSettings-GetCommandLineFile"><strong>GetCommandLineFile</strong></a>(self, is_user_debug_build)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.backends.android_browser_backend_settings.html#AndroidBrowserBackendSettings">AndroidBrowserBackendSettings</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>activity</strong></dt>
+</dl>
+<dl><dt><strong>package</strong></dt>
+</dl>
+<dl><dt><strong>profile_ignore_list</strong></dt>
+</dl>
+<dl><dt><strong>pseudo_exec_name</strong></dt>
+</dl>
+<dl><dt><strong>supports_tab_control</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="WebviewShellBackendSettings">class <strong>WebviewShellBackendSettings</strong></a>(<a href="telemetry.internal.backends.android_browser_backend_settings.html#WebviewBackendSettings">WebviewBackendSettings</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.android_browser_backend_settings.html#WebviewShellBackendSettings">WebviewShellBackendSettings</a></dd>
+<dd><a href="telemetry.internal.backends.android_browser_backend_settings.html#WebviewBackendSettings">WebviewBackendSettings</a></dd>
+<dd><a href="telemetry.internal.backends.android_browser_backend_settings.html#AndroidBrowserBackendSettings">AndroidBrowserBackendSettings</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="WebviewShellBackendSettings-__init__"><strong>__init__</strong></a>(self, package)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.backends.android_browser_backend_settings.html#WebviewBackendSettings">WebviewBackendSettings</a>:<br>
+<dl><dt><a name="WebviewShellBackendSettings-GetDevtoolsRemotePort"><strong>GetDevtoolsRemotePort</strong></a>(self, device)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.backends.android_browser_backend_settings.html#AndroidBrowserBackendSettings">AndroidBrowserBackendSettings</a>:<br>
+<dl><dt><a name="WebviewShellBackendSettings-GetCommandLineFile"><strong>GetCommandLineFile</strong></a>(self, is_user_debug_build)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.backends.android_browser_backend_settings.html#AndroidBrowserBackendSettings">AndroidBrowserBackendSettings</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>activity</strong></dt>
+</dl>
+<dl><dt><strong>package</strong></dt>
+</dl>
+<dl><dt><strong>profile_ignore_list</strong></dt>
+</dl>
+<dl><dt><strong>pseudo_exec_name</strong></dt>
+</dl>
+<dl><dt><strong>supports_tab_control</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.android_command_line_backend.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.android_command_line_backend.html
new file mode 100644
index 0000000..acb7d5d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.android_command_line_backend.html
@@ -0,0 +1,74 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.android_command_line_backend</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.android_command_line_backend</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/android_command_line_backend.py">telemetry/internal/backends/android_command_line_backend.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="devil.android.device_errors.html">devil.android.device_errors</a><br>
+</td><td width="25%" valign=top><a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top><a href="pipes.html">pipes</a><br>
+</td><td width="25%" valign=top><a href="sys.html">sys</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.android_command_line_backend.html#SetUpCommandLineFlags">SetUpCommandLineFlags</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="SetUpCommandLineFlags">class <strong>SetUpCommandLineFlags</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;context&nbsp;manager&nbsp;for&nbsp;setting&nbsp;up&nbsp;the&nbsp;android&nbsp;command&nbsp;line&nbsp;flags.<br>
+&nbsp;<br>
+This&nbsp;provides&nbsp;a&nbsp;readable&nbsp;way&nbsp;of&nbsp;using&nbsp;the&nbsp;android&nbsp;command&nbsp;line&nbsp;backend&nbsp;class.<br>
+Example&nbsp;usage:<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;with&nbsp;android_command_line_backend.<a href="#SetUpCommandLineFlags">SetUpCommandLineFlags</a>(<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;device,&nbsp;backend_settings,&nbsp;startup_args):<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;Something&nbsp;to&nbsp;run&nbsp;while&nbsp;the&nbsp;command&nbsp;line&nbsp;flags&nbsp;are&nbsp;set&nbsp;appropriately.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="SetUpCommandLineFlags-__enter__"><strong>__enter__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="SetUpCommandLineFlags-__exit__"><strong>__exit__</strong></a>(self, *args)</dt></dl>
+
+<dl><dt><a name="SetUpCommandLineFlags-__init__"><strong>__init__</strong></a>(self, device, backend_settings, startup_args)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.app_backend.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.app_backend.html
new file mode 100644
index 0000000..cfa3b69
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.app_backend.html
@@ -0,0 +1,72 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.app_backend</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.app_backend</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/app_backend.py">telemetry/internal/backends/app_backend.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2012&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.app_backend.html#AppBackend">AppBackend</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="AppBackend">class <strong>AppBackend</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="AppBackend-Close"><strong>Close</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AppBackend-GetStackTrace"><strong>GetStackTrace</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AppBackend-GetStandardOutput"><strong>GetStandardOutput</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AppBackend-IsAppRunning"><strong>IsAppRunning</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AppBackend-SetApp"><strong>SetApp</strong></a>(self, app)</dt></dl>
+
+<dl><dt><a name="AppBackend-Start"><strong>Start</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AppBackend-__del__"><strong>__del__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AppBackend-__init__"><strong>__init__</strong></a>(self, app_type, platform_backend)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>app</strong></dt>
+</dl>
+<dl><dt><strong>app_type</strong></dt>
+</dl>
+<dl><dt><strong>pid</strong></dt>
+</dl>
+<dl><dt><strong>platform_backend</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.browser_backend.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.browser_backend.html
new file mode 100644
index 0000000..1555523
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.browser_backend.html
@@ -0,0 +1,218 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.browser_backend</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.browser_backend</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/browser_backend.py">telemetry/internal/backends/browser_backend.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.backends.app_backend.html">telemetry.internal.backends.app_backend</a><br>
+<a href="catapult_base.cloud_storage.html">catapult_base.cloud_storage</a><br>
+</td><td width="25%" valign=top><a href="telemetry.decorators.html">telemetry.decorators</a><br>
+<a href="telemetry.core.platform.html">telemetry.core.platform</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.profiling_controller_backend.html">telemetry.internal.platform.profiling_controller_backend</a><br>
+<a href="sys.html">sys</a><br>
+</td><td width="25%" valign=top><a href="uuid.html">uuid</a><br>
+<a href="telemetry.internal.browser.web_contents.html">telemetry.internal.browser.web_contents</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="exceptions.html#Exception">exceptions.Exception</a>(<a href="exceptions.html#BaseException">exceptions.BaseException</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.browser_backend.html#ExtensionsNotSupportedException">ExtensionsNotSupportedException</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.app_backend.html#AppBackend">telemetry.internal.backends.app_backend.AppBackend</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.browser_backend.html#BrowserBackend">BrowserBackend</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="BrowserBackend">class <strong>BrowserBackend</strong></a>(<a href="telemetry.internal.backends.app_backend.html#AppBackend">telemetry.internal.backends.app_backend.AppBackend</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;base&nbsp;class&nbsp;for&nbsp;browser&nbsp;backends.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.browser_backend.html#BrowserBackend">BrowserBackend</a></dd>
+<dd><a href="telemetry.internal.backends.app_backend.html#AppBackend">telemetry.internal.backends.app_backend.AppBackend</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="BrowserBackend-DumpMemory"><strong>DumpMemory</strong></a>(self, timeout<font color="#909090">=90</font>)</dt></dl>
+
+<dl><dt><a name="BrowserBackend-GetStackTrace"><strong>GetStackTrace</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="BrowserBackend-GetStandardOutput"><strong>GetStandardOutput</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="BrowserBackend-GetSystemInfo"><strong>GetSystemInfo</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="BrowserBackend-IsAppRunning"><strong>IsAppRunning</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="BrowserBackend-IsBrowserRunning"><strong>IsBrowserRunning</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="BrowserBackend-SetBrowser"><strong>SetBrowser</strong></a>(self, browser)</dt></dl>
+
+<dl><dt><a name="BrowserBackend-SetMemoryPressureNotificationsSuppressed"><strong>SetMemoryPressureNotificationsSuppressed</strong></a>(self, suppressed, timeout<font color="#909090">=90</font>)</dt></dl>
+
+<dl><dt><a name="BrowserBackend-SimulateMemoryPressureNotification"><strong>SimulateMemoryPressureNotification</strong></a>(self, pressure_level, timeout<font color="#909090">=90</font>)</dt></dl>
+
+<dl><dt><a name="BrowserBackend-Start"><strong>Start</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="BrowserBackend-StartTracing"><strong>StartTracing</strong></a>(self, trace_options, custom_categories<font color="#909090">=None</font>, timeout<font color="#909090">=90</font>)</dt></dl>
+
+<dl><dt><a name="BrowserBackend-StopTracing"><strong>StopTracing</strong></a>(self, trace_data_builder)</dt></dl>
+
+<dl><dt><a name="BrowserBackend-UploadLogsToCloudStorage"><strong>UploadLogsToCloudStorage</strong></a>(self)</dt><dd><tt>Uploading&nbsp;log&nbsp;files&nbsp;produce&nbsp;by&nbsp;this&nbsp;browser&nbsp;instance&nbsp;to&nbsp;cloud&nbsp;storage.<br>
+&nbsp;<br>
+Check&nbsp;supports_uploading_logs&nbsp;before&nbsp;calling&nbsp;this&nbsp;method.</tt></dd></dl>
+
+<dl><dt><a name="BrowserBackend-__init__"><strong>__init__</strong></a>(self, platform_backend, supports_extensions, browser_options, tab_list_backend)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>browser</strong></dt>
+</dl>
+<dl><dt><strong>browser_type</strong></dt>
+</dl>
+<dl><dt><strong>log_file_path</strong></dt>
+</dl>
+<dl><dt><strong>profiling_controller_backend</strong></dt>
+</dl>
+<dl><dt><strong>should_ignore_certificate_errors</strong></dt>
+</dl>
+<dl><dt><strong>supports_cpu_metrics</strong></dt>
+</dl>
+<dl><dt><strong>supports_extensions</strong></dt>
+<dd><tt>True&nbsp;if&nbsp;this&nbsp;browser&nbsp;backend&nbsp;supports&nbsp;extensions.</tt></dd>
+</dl>
+<dl><dt><strong>supports_memory_dumping</strong></dt>
+</dl>
+<dl><dt><strong>supports_memory_metrics</strong></dt>
+</dl>
+<dl><dt><strong>supports_overriding_memory_pressure_notifications</strong></dt>
+</dl>
+<dl><dt><strong>supports_power_metrics</strong></dt>
+</dl>
+<dl><dt><strong>supports_system_info</strong></dt>
+</dl>
+<dl><dt><strong>supports_tab_control</strong></dt>
+</dl>
+<dl><dt><strong>supports_tracing</strong></dt>
+</dl>
+<dl><dt><strong>supports_uploading_logs</strong></dt>
+</dl>
+<dl><dt><strong>tab_list_backend</strong></dt>
+</dl>
+<dl><dt><strong>wpr_mode</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.internal.backends.app_backend.html#AppBackend">telemetry.internal.backends.app_backend.AppBackend</a>:<br>
+<dl><dt><a name="BrowserBackend-Close"><strong>Close</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="BrowserBackend-SetApp"><strong>SetApp</strong></a>(self, app)</dt></dl>
+
+<dl><dt><a name="BrowserBackend-__del__"><strong>__del__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.backends.app_backend.html#AppBackend">telemetry.internal.backends.app_backend.AppBackend</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>app</strong></dt>
+</dl>
+<dl><dt><strong>app_type</strong></dt>
+</dl>
+<dl><dt><strong>pid</strong></dt>
+</dl>
+<dl><dt><strong>platform_backend</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ExtensionsNotSupportedException">class <strong>ExtensionsNotSupportedException</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.browser_backend.html#ExtensionsNotSupportedException">ExtensionsNotSupportedException</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="ExtensionsNotSupportedException-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#ExtensionsNotSupportedException-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#ExtensionsNotSupportedException-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;object&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="ExtensionsNotSupportedException-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#ExtensionsNotSupportedException-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="ExtensionsNotSupportedException-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#ExtensionsNotSupportedException-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="ExtensionsNotSupportedException-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#ExtensionsNotSupportedException-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="ExtensionsNotSupportedException-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#ExtensionsNotSupportedException-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="ExtensionsNotSupportedException-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="ExtensionsNotSupportedException-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#ExtensionsNotSupportedException-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="ExtensionsNotSupportedException-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#ExtensionsNotSupportedException-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="ExtensionsNotSupportedException-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="ExtensionsNotSupportedException-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#ExtensionsNotSupportedException-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="ExtensionsNotSupportedException-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.android_browser_backend.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.android_browser_backend.html
new file mode 100644
index 0000000..1a6ac27
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.android_browser_backend.html
@@ -0,0 +1,194 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.chrome.android_browser_backend</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.<a href="telemetry.internal.backends.chrome.html"><font color="#ffffff">chrome</font></a>.android_browser_backend</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/chrome/android_browser_backend.py">telemetry/internal/backends/chrome/android_browser_backend.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.backends.android_command_line_backend.html">telemetry.internal.backends.android_command_line_backend</a><br>
+<a href="telemetry.internal.platform.android_platform_backend.html">telemetry.internal.platform.android_platform_backend</a><br>
+<a href="telemetry.internal.backends.browser_backend.html">telemetry.internal.backends.browser_backend</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.backends.chrome.chrome_browser_backend.html">telemetry.internal.backends.chrome.chrome_browser_backend</a><br>
+<a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+<a href="telemetry.internal.forwarders.html">telemetry.internal.forwarders</a><br>
+</td><td width="25%" valign=top><a href="devil.android.sdk.intent.html">devil.android.sdk.intent</a><br>
+<a href="logging.html">logging</a><br>
+<a href="subprocess.html">subprocess</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.util.html">telemetry.core.util</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome.chrome_browser_backend.html#ChromeBrowserBackend">telemetry.internal.backends.chrome.chrome_browser_backend.ChromeBrowserBackend</a>(<a href="telemetry.internal.backends.browser_backend.html#BrowserBackend">telemetry.internal.backends.browser_backend.BrowserBackend</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome.android_browser_backend.html#AndroidBrowserBackend">AndroidBrowserBackend</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="AndroidBrowserBackend">class <strong>AndroidBrowserBackend</strong></a>(<a href="telemetry.internal.backends.chrome.chrome_browser_backend.html#ChromeBrowserBackend">telemetry.internal.backends.chrome.chrome_browser_backend.ChromeBrowserBackend</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>The&nbsp;backend&nbsp;for&nbsp;controlling&nbsp;a&nbsp;browser&nbsp;instance&nbsp;running&nbsp;on&nbsp;Android.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.chrome.android_browser_backend.html#AndroidBrowserBackend">AndroidBrowserBackend</a></dd>
+<dd><a href="telemetry.internal.backends.chrome.chrome_browser_backend.html#ChromeBrowserBackend">telemetry.internal.backends.chrome.chrome_browser_backend.ChromeBrowserBackend</a></dd>
+<dd><a href="telemetry.internal.backends.browser_backend.html#BrowserBackend">telemetry.internal.backends.browser_backend.BrowserBackend</a></dd>
+<dd><a href="telemetry.internal.backends.app_backend.html#AppBackend">telemetry.internal.backends.app_backend.AppBackend</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="AndroidBrowserBackend-Close"><strong>Close</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidBrowserBackend-GetBrowserStartupArgs"><strong>GetBrowserStartupArgs</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidBrowserBackend-GetStackTrace"><strong>GetStackTrace</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidBrowserBackend-GetStandardOutput"><strong>GetStandardOutput</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidBrowserBackend-IsBrowserRunning"><strong>IsBrowserRunning</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidBrowserBackend-Start"><strong>Start</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidBrowserBackend-__del__"><strong>__del__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidBrowserBackend-__init__"><strong>__init__</strong></a>(self, android_platform_backend, browser_options, backend_settings, output_profile_path, extensions_to_load, target_arch)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>activity</strong></dt>
+</dl>
+<dl><dt><strong>browser_directory</strong></dt>
+</dl>
+<dl><dt><strong>device</strong></dt>
+</dl>
+<dl><dt><strong>log_file_path</strong></dt>
+</dl>
+<dl><dt><strong>package</strong></dt>
+</dl>
+<dl><dt><strong>pid</strong></dt>
+</dl>
+<dl><dt><strong>profile_directory</strong></dt>
+</dl>
+<dl><dt><strong>should_ignore_certificate_errors</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.internal.backends.chrome.chrome_browser_backend.html#ChromeBrowserBackend">telemetry.internal.backends.chrome.chrome_browser_backend.ChromeBrowserBackend</a>:<br>
+<dl><dt><a name="AndroidBrowserBackend-DumpMemory"><strong>DumpMemory</strong></a>(self, timeout<font color="#909090">=90</font>)</dt></dl>
+
+<dl><dt><a name="AndroidBrowserBackend-GetProcessName"><strong>GetProcessName</strong></a>(self, cmd_line)</dt><dd><tt>Returns&nbsp;a&nbsp;user-friendly&nbsp;name&nbsp;for&nbsp;the&nbsp;process&nbsp;of&nbsp;the&nbsp;given&nbsp;|cmd_line|.</tt></dd></dl>
+
+<dl><dt><a name="AndroidBrowserBackend-GetReplayBrowserStartupArgs"><strong>GetReplayBrowserStartupArgs</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidBrowserBackend-GetSystemInfo"><strong>GetSystemInfo</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidBrowserBackend-HasBrowserFinishedLaunching"><strong>HasBrowserFinishedLaunching</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidBrowserBackend-SetMemoryPressureNotificationsSuppressed"><strong>SetMemoryPressureNotificationsSuppressed</strong></a>(self, suppressed, timeout<font color="#909090">=90</font>)</dt></dl>
+
+<dl><dt><a name="AndroidBrowserBackend-SimulateMemoryPressureNotification"><strong>SimulateMemoryPressureNotification</strong></a>(self, pressure_level, timeout<font color="#909090">=90</font>)</dt></dl>
+
+<dl><dt><a name="AndroidBrowserBackend-StartTracing"><strong>StartTracing</strong></a>(self, trace_options, custom_categories<font color="#909090">=None</font>, timeout<font color="#909090">=90</font>)</dt><dd><tt>Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;trace_options:&nbsp;An&nbsp;tracing_options.TracingOptions&nbsp;instance.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;custom_categories:&nbsp;An&nbsp;optional&nbsp;string&nbsp;containing&nbsp;a&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;comma&nbsp;separated&nbsp;categories&nbsp;that&nbsp;will&nbsp;be&nbsp;traced<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;instead&nbsp;of&nbsp;the&nbsp;default&nbsp;category&nbsp;set.&nbsp;&nbsp;Example:&nbsp;use<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"webkit,cc,disabled-by-default-cc.debug"&nbsp;to&nbsp;trace&nbsp;only<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;those&nbsp;three&nbsp;event&nbsp;categories.</tt></dd></dl>
+
+<dl><dt><a name="AndroidBrowserBackend-StopTracing"><strong>StopTracing</strong></a>(self, trace_data_builder)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.backends.chrome.chrome_browser_backend.html#ChromeBrowserBackend">telemetry.internal.backends.chrome.chrome_browser_backend.ChromeBrowserBackend</a>:<br>
+<dl><dt><strong>devtools_client</strong></dt>
+</dl>
+<dl><dt><strong>extension_backend</strong></dt>
+</dl>
+<dl><dt><strong>supports_cpu_metrics</strong></dt>
+</dl>
+<dl><dt><strong>supports_memory_dumping</strong></dt>
+</dl>
+<dl><dt><strong>supports_memory_metrics</strong></dt>
+</dl>
+<dl><dt><strong>supports_overriding_memory_pressure_notifications</strong></dt>
+</dl>
+<dl><dt><strong>supports_power_metrics</strong></dt>
+</dl>
+<dl><dt><strong>supports_system_info</strong></dt>
+</dl>
+<dl><dt><strong>supports_tab_control</strong></dt>
+</dl>
+<dl><dt><strong>supports_tracing</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.internal.backends.browser_backend.html#BrowserBackend">telemetry.internal.backends.browser_backend.BrowserBackend</a>:<br>
+<dl><dt><a name="AndroidBrowserBackend-IsAppRunning"><strong>IsAppRunning</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidBrowserBackend-SetBrowser"><strong>SetBrowser</strong></a>(self, browser)</dt></dl>
+
+<dl><dt><a name="AndroidBrowserBackend-UploadLogsToCloudStorage"><strong>UploadLogsToCloudStorage</strong></a>(self)</dt><dd><tt>Uploading&nbsp;log&nbsp;files&nbsp;produce&nbsp;by&nbsp;this&nbsp;browser&nbsp;instance&nbsp;to&nbsp;cloud&nbsp;storage.<br>
+&nbsp;<br>
+Check&nbsp;supports_uploading_logs&nbsp;before&nbsp;calling&nbsp;this&nbsp;method.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.backends.browser_backend.html#BrowserBackend">telemetry.internal.backends.browser_backend.BrowserBackend</a>:<br>
+<dl><dt><strong>browser</strong></dt>
+</dl>
+<dl><dt><strong>browser_type</strong></dt>
+</dl>
+<dl><dt><strong>profiling_controller_backend</strong></dt>
+</dl>
+<dl><dt><strong>supports_extensions</strong></dt>
+<dd><tt>True&nbsp;if&nbsp;this&nbsp;browser&nbsp;backend&nbsp;supports&nbsp;extensions.</tt></dd>
+</dl>
+<dl><dt><strong>supports_uploading_logs</strong></dt>
+</dl>
+<dl><dt><strong>tab_list_backend</strong></dt>
+</dl>
+<dl><dt><strong>wpr_mode</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.internal.backends.app_backend.html#AppBackend">telemetry.internal.backends.app_backend.AppBackend</a>:<br>
+<dl><dt><a name="AndroidBrowserBackend-SetApp"><strong>SetApp</strong></a>(self, app)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.backends.app_backend.html#AppBackend">telemetry.internal.backends.app_backend.AppBackend</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>app</strong></dt>
+</dl>
+<dl><dt><strong>app_type</strong></dt>
+</dl>
+<dl><dt><strong>platform_backend</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.android_browser_finder.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.android_browser_finder.html
new file mode 100644
index 0000000..f8020b1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.android_browser_finder.html
@@ -0,0 +1,132 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.chrome.android_browser_finder</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.<a href="telemetry.internal.backends.chrome.html"><font color="#ffffff">chrome</font></a>.android_browser_finder</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/chrome/android_browser_finder.py">telemetry/internal/backends/chrome/android_browser_finder.py</a></font></td></tr></table>
+    <p><tt>Finds&nbsp;android&nbsp;browsers&nbsp;that&nbsp;can&nbsp;be&nbsp;controlled&nbsp;by&nbsp;telemetry.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.backends.chrome.android_browser_backend.html">telemetry.internal.backends.chrome.android_browser_backend</a><br>
+<a href="telemetry.internal.backends.android_browser_backend_settings.html">telemetry.internal.backends.android_browser_backend_settings</a><br>
+<a href="telemetry.internal.platform.android_device.html">telemetry.internal.platform.android_device</a><br>
+<a href="devil.android.apk_helper.html">devil.android.apk_helper</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.browser.browser.html">telemetry.internal.browser.browser</a><br>
+<a href="telemetry.decorators.html">telemetry.decorators</a><br>
+<a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+<a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top><a href="os.html">os</a><br>
+<a href="telemetry.core.platform.html">telemetry.core.platform</a><br>
+<a href="telemetry.internal.browser.possible_browser.html">telemetry.internal.browser.possible_browser</a><br>
+<a href="sys.html">sys</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.util.html">telemetry.core.util</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.browser.possible_browser.html#PossibleBrowser">telemetry.internal.browser.possible_browser.PossibleBrowser</a>(<a href="telemetry.internal.app.possible_app.html#PossibleApp">telemetry.internal.app.possible_app.PossibleApp</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome.android_browser_finder.html#PossibleAndroidBrowser">PossibleAndroidBrowser</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="PossibleAndroidBrowser">class <strong>PossibleAndroidBrowser</strong></a>(<a href="telemetry.internal.browser.possible_browser.html#PossibleBrowser">telemetry.internal.browser.possible_browser.PossibleBrowser</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;launchable&nbsp;android&nbsp;browser&nbsp;instance.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.chrome.android_browser_finder.html#PossibleAndroidBrowser">PossibleAndroidBrowser</a></dd>
+<dd><a href="telemetry.internal.browser.possible_browser.html#PossibleBrowser">telemetry.internal.browser.possible_browser.PossibleBrowser</a></dd>
+<dd><a href="telemetry.internal.app.possible_app.html#PossibleApp">telemetry.internal.app.possible_app.PossibleApp</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="PossibleAndroidBrowser-Create"><strong>Create</strong></a>(self, finder_options)</dt></dl>
+
+<dl><dt><a name="PossibleAndroidBrowser-HaveLocalAPK"><strong>HaveLocalAPK</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PossibleAndroidBrowser-SupportsOptions"><strong>SupportsOptions</strong></a>(self, finder_options)</dt></dl>
+
+<dl><dt><a name="PossibleAndroidBrowser-UpdateExecutableIfNeeded"><strong>UpdateExecutableIfNeeded</strong></a>(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="PossibleAndroidBrowser-__init__"><strong>__init__</strong></a>(self, browser_type, finder_options, android_platform, backend_settings, apk_name)</dt></dl>
+
+<dl><dt><a name="PossibleAndroidBrowser-__repr__"><strong>__repr__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PossibleAndroidBrowser-last_modification_time"><strong>last_modification_time</strong></a>(self)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.browser.possible_browser.html#PossibleBrowser">telemetry.internal.browser.possible_browser.PossibleBrowser</a>:<br>
+<dl><dt><a name="PossibleAndroidBrowser-IsRemote"><strong>IsRemote</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PossibleAndroidBrowser-RunRemote"><strong>RunRemote</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PossibleAndroidBrowser-SetCredentialsPath"><strong>SetCredentialsPath</strong></a>(self, credentials_path)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.browser.possible_browser.html#PossibleBrowser">telemetry.internal.browser.possible_browser.PossibleBrowser</a>:<br>
+<dl><dt><strong>browser_type</strong></dt>
+</dl>
+<dl><dt><strong>supports_tab_control</strong></dt>
+</dl>
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.app.possible_app.html#PossibleApp">telemetry.internal.app.possible_app.PossibleApp</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>app_type</strong></dt>
+</dl>
+<dl><dt><strong>platform</strong></dt>
+</dl>
+<dl><dt><strong>target_os</strong></dt>
+<dd><tt>Target&nbsp;OS,&nbsp;the&nbsp;app&nbsp;will&nbsp;run&nbsp;on.</tt></dd>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-CanFindAvailableBrowsers"><strong>CanFindAvailableBrowsers</strong></a>()</dt></dl>
+ <dl><dt><a name="-CanPossiblyHandlePath"><strong>CanPossiblyHandlePath</strong></a>(target_path)</dt></dl>
+ <dl><dt><a name="-FindAllAvailableBrowsers"><strong>FindAllAvailableBrowsers</strong></a>(finder_options, device)</dt><dd><tt>Finds&nbsp;all&nbsp;the&nbsp;possible&nbsp;browsers&nbsp;on&nbsp;one&nbsp;device.<br>
+&nbsp;<br>
+The&nbsp;device&nbsp;is&nbsp;either&nbsp;the&nbsp;only&nbsp;device&nbsp;on&nbsp;the&nbsp;host&nbsp;platform,<br>
+or&nbsp;|finder_options|&nbsp;specifies&nbsp;a&nbsp;particular&nbsp;device.</tt></dd></dl>
+ <dl><dt><a name="-FindAllBrowserTypes"><strong>FindAllBrowserTypes</strong></a>(_options)</dt></dl>
+ <dl><dt><a name="-SelectDefaultBrowser"><strong>SelectDefaultBrowser</strong></a>(possible_browsers)</dt><dd><tt>Return&nbsp;the&nbsp;newest&nbsp;possible&nbsp;browser.</tt></dd></dl>
+</td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>CHROME_PACKAGE_NAMES</strong> = {'android-chrome': ['com.google.android.apps.chrome', &lt;class 'telemetry.internal.backends.android_browser_backend_settings.ChromeBackendSettings'&gt;, 'Chrome.apk'], 'android-chrome-beta': ['com.chrome.beta', &lt;class 'telemetry.internal.backends.android_browser_backend_settings.ChromeBackendSettings'&gt;, None], 'android-chrome-canary': ['com.chrome.canary', &lt;class 'telemetry.internal.backends.android_browser_backend_settings.ChromeBackendSettings'&gt;, None], 'android-chrome-dev': ['com.chrome.dev', &lt;class 'telemetry.internal.backends.android_browser_backend_settings.ChromeBackendSettings'&gt;, None], 'android-chrome-work': ['com.chrome.work', &lt;class 'telemetry.internal.backends.android_browser_backend_settings.ChromeBackendSettings'&gt;, None], 'android-chromium': ['org.chromium.chrome', &lt;class 'telemetry.internal.backends.android_browser_backend_settings.ChromeBackendSettings'&gt;, 'ChromePublic.apk'], 'android-content-shell': ['org.chromium.content_shell_apk', &lt;class 'telemetry.internal.backends.android_browser_backend_settings.ContentShellBackendSettings'&gt;, 'ContentShell.apk'], 'android-jb-system-chrome': ['com.android.chrome', &lt;class 'telemetry.internal.backends.android_browser_backend_settings.ChromeBackendSettings'&gt;, None], 'android-webview': ['org.chromium.webview_shell', &lt;class 'telemetry.internal.backends.android_browser_backend_settings.WebviewBackendSettings'&gt;, None], 'android-webview-shell': ['org.chromium.android_webview.shell', &lt;class 'telemetry.internal.backends.android_browser_backend_settings.WebviewShellBackendSettings'&gt;, 'AndroidWebView.apk']}</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.chrome_browser_backend.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.chrome_browser_backend.html
new file mode 100644
index 0000000..c599ca9
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.chrome_browser_backend.html
@@ -0,0 +1,191 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.chrome.chrome_browser_backend</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.<a href="telemetry.internal.backends.chrome.html"><font color="#ffffff">chrome</font></a>.chrome_browser_backend</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/chrome/chrome_browser_backend.py">telemetry/internal/backends/chrome/chrome_browser_backend.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.backends.browser_backend.html">telemetry.internal.backends.browser_backend</a><br>
+<a href="telemetry.decorators.html">telemetry.decorators</a><br>
+<a href="telemetry.internal.backends.chrome_inspector.devtools_client_backend.html">telemetry.internal.backends.chrome_inspector.devtools_client_backend</a><br>
+<a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+<a href="telemetry.internal.backends.chrome.extension_backend.html">telemetry.internal.backends.chrome.extension_backend</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.forwarders.html">telemetry.internal.forwarders</a><br>
+<a href="logging.html">logging</a><br>
+<a href="telemetry.testing.options_for_unittests.html">telemetry.testing.options_for_unittests</a><br>
+<a href="pprint.html">pprint</a><br>
+<a href="shlex.html">shlex</a><br>
+</td><td width="25%" valign=top><a href="sys.html">sys</a><br>
+<a href="telemetry.internal.backends.chrome.system_info_backend.html">telemetry.internal.backends.chrome.system_info_backend</a><br>
+<a href="telemetry.internal.backends.chrome.tab_list_backend.html">telemetry.internal.backends.chrome.tab_list_backend</a><br>
+<a href="telemetry.internal.browser.user_agent.html">telemetry.internal.browser.user_agent</a><br>
+<a href="telemetry.core.util.html">telemetry.core.util</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.browser.web_contents.html">telemetry.internal.browser.web_contents</a><br>
+<a href="telemetry.util.wpr_modes.html">telemetry.util.wpr_modes</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.browser_backend.html#BrowserBackend">telemetry.internal.backends.browser_backend.BrowserBackend</a>(<a href="telemetry.internal.backends.app_backend.html#AppBackend">telemetry.internal.backends.app_backend.AppBackend</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome.chrome_browser_backend.html#ChromeBrowserBackend">ChromeBrowserBackend</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ChromeBrowserBackend">class <strong>ChromeBrowserBackend</strong></a>(<a href="telemetry.internal.backends.browser_backend.html#BrowserBackend">telemetry.internal.backends.browser_backend.BrowserBackend</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>An&nbsp;abstract&nbsp;class&nbsp;for&nbsp;chrome&nbsp;browser&nbsp;backends.&nbsp;Provides&nbsp;basic&nbsp;functionality<br>
+once&nbsp;a&nbsp;remote-debugger&nbsp;port&nbsp;has&nbsp;been&nbsp;established.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.chrome.chrome_browser_backend.html#ChromeBrowserBackend">ChromeBrowserBackend</a></dd>
+<dd><a href="telemetry.internal.backends.browser_backend.html#BrowserBackend">telemetry.internal.backends.browser_backend.BrowserBackend</a></dd>
+<dd><a href="telemetry.internal.backends.app_backend.html#AppBackend">telemetry.internal.backends.app_backend.AppBackend</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="ChromeBrowserBackend-Close"><strong>Close</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="ChromeBrowserBackend-DumpMemory"><strong>DumpMemory</strong></a>(self, timeout<font color="#909090">=90</font>)</dt></dl>
+
+<dl><dt><a name="ChromeBrowserBackend-GetBrowserStartupArgs"><strong>GetBrowserStartupArgs</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="ChromeBrowserBackend-GetProcessName"><strong>GetProcessName</strong></a>(self, cmd_line)</dt><dd><tt>Returns&nbsp;a&nbsp;user-friendly&nbsp;name&nbsp;for&nbsp;the&nbsp;process&nbsp;of&nbsp;the&nbsp;given&nbsp;|cmd_line|.</tt></dd></dl>
+
+<dl><dt><a name="ChromeBrowserBackend-GetReplayBrowserStartupArgs"><strong>GetReplayBrowserStartupArgs</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="ChromeBrowserBackend-GetSystemInfo"><strong>GetSystemInfo</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="ChromeBrowserBackend-HasBrowserFinishedLaunching"><strong>HasBrowserFinishedLaunching</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="ChromeBrowserBackend-SetMemoryPressureNotificationsSuppressed"><strong>SetMemoryPressureNotificationsSuppressed</strong></a>(self, suppressed, timeout<font color="#909090">=90</font>)</dt></dl>
+
+<dl><dt><a name="ChromeBrowserBackend-SimulateMemoryPressureNotification"><strong>SimulateMemoryPressureNotification</strong></a>(self, pressure_level, timeout<font color="#909090">=90</font>)</dt></dl>
+
+<dl><dt><a name="ChromeBrowserBackend-StartTracing"><strong>StartTracing</strong></a>(self, trace_options, custom_categories<font color="#909090">=None</font>, timeout<font color="#909090">=90</font>)</dt><dd><tt>Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;trace_options:&nbsp;An&nbsp;tracing_options.TracingOptions&nbsp;instance.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;custom_categories:&nbsp;An&nbsp;optional&nbsp;string&nbsp;containing&nbsp;a&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;comma&nbsp;separated&nbsp;categories&nbsp;that&nbsp;will&nbsp;be&nbsp;traced<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;instead&nbsp;of&nbsp;the&nbsp;default&nbsp;category&nbsp;set.&nbsp;&nbsp;Example:&nbsp;use<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"webkit,cc,disabled-by-default-cc.debug"&nbsp;to&nbsp;trace&nbsp;only<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;those&nbsp;three&nbsp;event&nbsp;categories.</tt></dd></dl>
+
+<dl><dt><a name="ChromeBrowserBackend-StopTracing"><strong>StopTracing</strong></a>(self, trace_data_builder)</dt></dl>
+
+<dl><dt><a name="ChromeBrowserBackend-__init__"><strong>__init__</strong></a>(self, platform_backend, supports_tab_control, supports_extensions, browser_options, output_profile_path, extensions_to_load)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>browser_directory</strong></dt>
+</dl>
+<dl><dt><strong>devtools_client</strong></dt>
+</dl>
+<dl><dt><strong>extension_backend</strong></dt>
+</dl>
+<dl><dt><strong>profile_directory</strong></dt>
+</dl>
+<dl><dt><strong>supports_cpu_metrics</strong></dt>
+</dl>
+<dl><dt><strong>supports_memory_dumping</strong></dt>
+</dl>
+<dl><dt><strong>supports_memory_metrics</strong></dt>
+</dl>
+<dl><dt><strong>supports_overriding_memory_pressure_notifications</strong></dt>
+</dl>
+<dl><dt><strong>supports_power_metrics</strong></dt>
+</dl>
+<dl><dt><strong>supports_system_info</strong></dt>
+</dl>
+<dl><dt><strong>supports_tab_control</strong></dt>
+</dl>
+<dl><dt><strong>supports_tracing</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.internal.backends.browser_backend.html#BrowserBackend">telemetry.internal.backends.browser_backend.BrowserBackend</a>:<br>
+<dl><dt><a name="ChromeBrowserBackend-GetStackTrace"><strong>GetStackTrace</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="ChromeBrowserBackend-GetStandardOutput"><strong>GetStandardOutput</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="ChromeBrowserBackend-IsAppRunning"><strong>IsAppRunning</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="ChromeBrowserBackend-IsBrowserRunning"><strong>IsBrowserRunning</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="ChromeBrowserBackend-SetBrowser"><strong>SetBrowser</strong></a>(self, browser)</dt></dl>
+
+<dl><dt><a name="ChromeBrowserBackend-Start"><strong>Start</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="ChromeBrowserBackend-UploadLogsToCloudStorage"><strong>UploadLogsToCloudStorage</strong></a>(self)</dt><dd><tt>Uploading&nbsp;log&nbsp;files&nbsp;produce&nbsp;by&nbsp;this&nbsp;browser&nbsp;instance&nbsp;to&nbsp;cloud&nbsp;storage.<br>
+&nbsp;<br>
+Check&nbsp;supports_uploading_logs&nbsp;before&nbsp;calling&nbsp;this&nbsp;method.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.backends.browser_backend.html#BrowserBackend">telemetry.internal.backends.browser_backend.BrowserBackend</a>:<br>
+<dl><dt><strong>browser</strong></dt>
+</dl>
+<dl><dt><strong>browser_type</strong></dt>
+</dl>
+<dl><dt><strong>log_file_path</strong></dt>
+</dl>
+<dl><dt><strong>profiling_controller_backend</strong></dt>
+</dl>
+<dl><dt><strong>should_ignore_certificate_errors</strong></dt>
+</dl>
+<dl><dt><strong>supports_extensions</strong></dt>
+<dd><tt>True&nbsp;if&nbsp;this&nbsp;browser&nbsp;backend&nbsp;supports&nbsp;extensions.</tt></dd>
+</dl>
+<dl><dt><strong>supports_uploading_logs</strong></dt>
+</dl>
+<dl><dt><strong>tab_list_backend</strong></dt>
+</dl>
+<dl><dt><strong>wpr_mode</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.internal.backends.app_backend.html#AppBackend">telemetry.internal.backends.app_backend.AppBackend</a>:<br>
+<dl><dt><a name="ChromeBrowserBackend-SetApp"><strong>SetApp</strong></a>(self, app)</dt></dl>
+
+<dl><dt><a name="ChromeBrowserBackend-__del__"><strong>__del__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.backends.app_backend.html#AppBackend">telemetry.internal.backends.app_backend.AppBackend</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>app</strong></dt>
+</dl>
+<dl><dt><strong>app_type</strong></dt>
+</dl>
+<dl><dt><strong>pid</strong></dt>
+</dl>
+<dl><dt><strong>platform_backend</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.cros_browser_backend.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.cros_browser_backend.html
new file mode 100644
index 0000000..b10d981
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.cros_browser_backend.html
@@ -0,0 +1,191 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.chrome.cros_browser_backend</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.<a href="telemetry.internal.backends.chrome.html"><font color="#ffffff">chrome</font></a>.cros_browser_backend</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/chrome/cros_browser_backend.py">telemetry/internal/backends/chrome/cros_browser_backend.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.backends.chrome.chrome_browser_backend.html">telemetry.internal.backends.chrome.chrome_browser_backend</a><br>
+<a href="telemetry.decorators.html">telemetry.decorators</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+<a href="telemetry.internal.forwarders.html">telemetry.internal.forwarders</a><br>
+</td><td width="25%" valign=top><a href="logging.html">logging</a><br>
+<a href="telemetry.internal.backends.chrome.misc_web_contents_backend.html">telemetry.internal.backends.chrome.misc_web_contents_backend</a><br>
+</td><td width="25%" valign=top><a href="os.html">os</a><br>
+<a href="telemetry.core.util.html">telemetry.core.util</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome.chrome_browser_backend.html#ChromeBrowserBackend">telemetry.internal.backends.chrome.chrome_browser_backend.ChromeBrowserBackend</a>(<a href="telemetry.internal.backends.browser_backend.html#BrowserBackend">telemetry.internal.backends.browser_backend.BrowserBackend</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome.cros_browser_backend.html#CrOSBrowserBackend">CrOSBrowserBackend</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="CrOSBrowserBackend">class <strong>CrOSBrowserBackend</strong></a>(<a href="telemetry.internal.backends.chrome.chrome_browser_backend.html#ChromeBrowserBackend">telemetry.internal.backends.chrome.chrome_browser_backend.ChromeBrowserBackend</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.chrome.cros_browser_backend.html#CrOSBrowserBackend">CrOSBrowserBackend</a></dd>
+<dd><a href="telemetry.internal.backends.chrome.chrome_browser_backend.html#ChromeBrowserBackend">telemetry.internal.backends.chrome.chrome_browser_backend.ChromeBrowserBackend</a></dd>
+<dd><a href="telemetry.internal.backends.browser_backend.html#BrowserBackend">telemetry.internal.backends.browser_backend.BrowserBackend</a></dd>
+<dd><a href="telemetry.internal.backends.app_backend.html#AppBackend">telemetry.internal.backends.app_backend.AppBackend</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="CrOSBrowserBackend-Close"><strong>Close</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrOSBrowserBackend-GetBrowserStartupArgs"><strong>GetBrowserStartupArgs</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrOSBrowserBackend-GetStackTrace"><strong>GetStackTrace</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrOSBrowserBackend-GetStandardOutput"><strong>GetStandardOutput</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrOSBrowserBackend-IsBrowserRunning"><strong>IsBrowserRunning</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrOSBrowserBackend-Start"><strong>Start</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrOSBrowserBackend-__del__"><strong>__del__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrOSBrowserBackend-__init__"><strong>__init__</strong></a>(self, cros_platform_backend, browser_options, cri, is_guest, extensions_to_load)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>browser_directory</strong></dt>
+</dl>
+<dl><dt><strong>log_file_path</strong></dt>
+</dl>
+<dl><dt><strong>misc_web_contents_backend</strong></dt>
+<dd><tt>Access&nbsp;to&nbsp;chrome://oobe/login&nbsp;page.</tt></dd>
+</dl>
+<dl><dt><strong>oobe</strong></dt>
+</dl>
+<dl><dt><strong>oobe_exists</strong></dt>
+</dl>
+<dl><dt><strong>pid</strong></dt>
+</dl>
+<dl><dt><strong>profile_directory</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.internal.backends.chrome.chrome_browser_backend.html#ChromeBrowserBackend">telemetry.internal.backends.chrome.chrome_browser_backend.ChromeBrowserBackend</a>:<br>
+<dl><dt><a name="CrOSBrowserBackend-DumpMemory"><strong>DumpMemory</strong></a>(self, timeout<font color="#909090">=90</font>)</dt></dl>
+
+<dl><dt><a name="CrOSBrowserBackend-GetProcessName"><strong>GetProcessName</strong></a>(self, cmd_line)</dt><dd><tt>Returns&nbsp;a&nbsp;user-friendly&nbsp;name&nbsp;for&nbsp;the&nbsp;process&nbsp;of&nbsp;the&nbsp;given&nbsp;|cmd_line|.</tt></dd></dl>
+
+<dl><dt><a name="CrOSBrowserBackend-GetReplayBrowserStartupArgs"><strong>GetReplayBrowserStartupArgs</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrOSBrowserBackend-GetSystemInfo"><strong>GetSystemInfo</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrOSBrowserBackend-HasBrowserFinishedLaunching"><strong>HasBrowserFinishedLaunching</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrOSBrowserBackend-SetMemoryPressureNotificationsSuppressed"><strong>SetMemoryPressureNotificationsSuppressed</strong></a>(self, suppressed, timeout<font color="#909090">=90</font>)</dt></dl>
+
+<dl><dt><a name="CrOSBrowserBackend-SimulateMemoryPressureNotification"><strong>SimulateMemoryPressureNotification</strong></a>(self, pressure_level, timeout<font color="#909090">=90</font>)</dt></dl>
+
+<dl><dt><a name="CrOSBrowserBackend-StartTracing"><strong>StartTracing</strong></a>(self, trace_options, custom_categories<font color="#909090">=None</font>, timeout<font color="#909090">=90</font>)</dt><dd><tt>Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;trace_options:&nbsp;An&nbsp;tracing_options.TracingOptions&nbsp;instance.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;custom_categories:&nbsp;An&nbsp;optional&nbsp;string&nbsp;containing&nbsp;a&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;comma&nbsp;separated&nbsp;categories&nbsp;that&nbsp;will&nbsp;be&nbsp;traced<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;instead&nbsp;of&nbsp;the&nbsp;default&nbsp;category&nbsp;set.&nbsp;&nbsp;Example:&nbsp;use<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"webkit,cc,disabled-by-default-cc.debug"&nbsp;to&nbsp;trace&nbsp;only<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;those&nbsp;three&nbsp;event&nbsp;categories.</tt></dd></dl>
+
+<dl><dt><a name="CrOSBrowserBackend-StopTracing"><strong>StopTracing</strong></a>(self, trace_data_builder)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.backends.chrome.chrome_browser_backend.html#ChromeBrowserBackend">telemetry.internal.backends.chrome.chrome_browser_backend.ChromeBrowserBackend</a>:<br>
+<dl><dt><strong>devtools_client</strong></dt>
+</dl>
+<dl><dt><strong>extension_backend</strong></dt>
+</dl>
+<dl><dt><strong>supports_cpu_metrics</strong></dt>
+</dl>
+<dl><dt><strong>supports_memory_dumping</strong></dt>
+</dl>
+<dl><dt><strong>supports_memory_metrics</strong></dt>
+</dl>
+<dl><dt><strong>supports_overriding_memory_pressure_notifications</strong></dt>
+</dl>
+<dl><dt><strong>supports_power_metrics</strong></dt>
+</dl>
+<dl><dt><strong>supports_system_info</strong></dt>
+</dl>
+<dl><dt><strong>supports_tab_control</strong></dt>
+</dl>
+<dl><dt><strong>supports_tracing</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.internal.backends.browser_backend.html#BrowserBackend">telemetry.internal.backends.browser_backend.BrowserBackend</a>:<br>
+<dl><dt><a name="CrOSBrowserBackend-IsAppRunning"><strong>IsAppRunning</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrOSBrowserBackend-SetBrowser"><strong>SetBrowser</strong></a>(self, browser)</dt></dl>
+
+<dl><dt><a name="CrOSBrowserBackend-UploadLogsToCloudStorage"><strong>UploadLogsToCloudStorage</strong></a>(self)</dt><dd><tt>Uploading&nbsp;log&nbsp;files&nbsp;produce&nbsp;by&nbsp;this&nbsp;browser&nbsp;instance&nbsp;to&nbsp;cloud&nbsp;storage.<br>
+&nbsp;<br>
+Check&nbsp;supports_uploading_logs&nbsp;before&nbsp;calling&nbsp;this&nbsp;method.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.backends.browser_backend.html#BrowserBackend">telemetry.internal.backends.browser_backend.BrowserBackend</a>:<br>
+<dl><dt><strong>browser</strong></dt>
+</dl>
+<dl><dt><strong>browser_type</strong></dt>
+</dl>
+<dl><dt><strong>profiling_controller_backend</strong></dt>
+</dl>
+<dl><dt><strong>should_ignore_certificate_errors</strong></dt>
+</dl>
+<dl><dt><strong>supports_extensions</strong></dt>
+<dd><tt>True&nbsp;if&nbsp;this&nbsp;browser&nbsp;backend&nbsp;supports&nbsp;extensions.</tt></dd>
+</dl>
+<dl><dt><strong>supports_uploading_logs</strong></dt>
+</dl>
+<dl><dt><strong>tab_list_backend</strong></dt>
+</dl>
+<dl><dt><strong>wpr_mode</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.internal.backends.app_backend.html#AppBackend">telemetry.internal.backends.app_backend.AppBackend</a>:<br>
+<dl><dt><a name="CrOSBrowserBackend-SetApp"><strong>SetApp</strong></a>(self, app)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.backends.app_backend.html#AppBackend">telemetry.internal.backends.app_backend.AppBackend</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>app</strong></dt>
+</dl>
+<dl><dt><strong>app_type</strong></dt>
+</dl>
+<dl><dt><strong>platform_backend</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.cros_browser_finder.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.cros_browser_finder.html
new file mode 100644
index 0000000..aed268e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.cros_browser_finder.html
@@ -0,0 +1,115 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.chrome.cros_browser_finder</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.<a href="telemetry.internal.backends.chrome.html"><font color="#ffffff">chrome</font></a>.cros_browser_finder</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/chrome/cros_browser_finder.py">telemetry/internal/backends/chrome/cros_browser_finder.py</a></font></td></tr></table>
+    <p><tt>Finds&nbsp;CrOS&nbsp;browsers&nbsp;that&nbsp;can&nbsp;be&nbsp;controlled&nbsp;by&nbsp;telemetry.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.browser.browser.html">telemetry.internal.browser.browser</a><br>
+<a href="telemetry.internal.browser.browser_finder_exceptions.html">telemetry.internal.browser.browser_finder_exceptions</a><br>
+<a href="telemetry.internal.backends.chrome.cros_browser_backend.html">telemetry.internal.backends.chrome.cros_browser_backend</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.backends.chrome.cros_browser_with_oobe.html">telemetry.internal.backends.chrome.cros_browser_with_oobe</a><br>
+<a href="telemetry.internal.platform.cros_device.html">telemetry.internal.platform.cros_device</a><br>
+<a href="telemetry.core.cros_interface.html">telemetry.core.cros_interface</a><br>
+</td><td width="25%" valign=top><a href="logging.html">logging</a><br>
+<a href="telemetry.core.platform.html">telemetry.core.platform</a><br>
+<a href="telemetry.internal.browser.possible_browser.html">telemetry.internal.browser.possible_browser</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.browser.possible_browser.html#PossibleBrowser">telemetry.internal.browser.possible_browser.PossibleBrowser</a>(<a href="telemetry.internal.app.possible_app.html#PossibleApp">telemetry.internal.app.possible_app.PossibleApp</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome.cros_browser_finder.html#PossibleCrOSBrowser">PossibleCrOSBrowser</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="PossibleCrOSBrowser">class <strong>PossibleCrOSBrowser</strong></a>(<a href="telemetry.internal.browser.possible_browser.html#PossibleBrowser">telemetry.internal.browser.possible_browser.PossibleBrowser</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;launchable&nbsp;CrOS&nbsp;browser&nbsp;instance.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.chrome.cros_browser_finder.html#PossibleCrOSBrowser">PossibleCrOSBrowser</a></dd>
+<dd><a href="telemetry.internal.browser.possible_browser.html#PossibleBrowser">telemetry.internal.browser.possible_browser.PossibleBrowser</a></dd>
+<dd><a href="telemetry.internal.app.possible_app.html#PossibleApp">telemetry.internal.app.possible_app.PossibleApp</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="PossibleCrOSBrowser-Create"><strong>Create</strong></a>(self, finder_options)</dt></dl>
+
+<dl><dt><a name="PossibleCrOSBrowser-SupportsOptions"><strong>SupportsOptions</strong></a>(self, finder_options)</dt></dl>
+
+<dl><dt><a name="PossibleCrOSBrowser-UpdateExecutableIfNeeded"><strong>UpdateExecutableIfNeeded</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PossibleCrOSBrowser-__init__"><strong>__init__</strong></a>(self, browser_type, finder_options, cros_platform, is_guest)</dt></dl>
+
+<dl><dt><a name="PossibleCrOSBrowser-__repr__"><strong>__repr__</strong></a>(self)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.browser.possible_browser.html#PossibleBrowser">telemetry.internal.browser.possible_browser.PossibleBrowser</a>:<br>
+<dl><dt><a name="PossibleCrOSBrowser-IsRemote"><strong>IsRemote</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PossibleCrOSBrowser-RunRemote"><strong>RunRemote</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PossibleCrOSBrowser-SetCredentialsPath"><strong>SetCredentialsPath</strong></a>(self, credentials_path)</dt></dl>
+
+<dl><dt><a name="PossibleCrOSBrowser-last_modification_time"><strong>last_modification_time</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.browser.possible_browser.html#PossibleBrowser">telemetry.internal.browser.possible_browser.PossibleBrowser</a>:<br>
+<dl><dt><strong>browser_type</strong></dt>
+</dl>
+<dl><dt><strong>supports_tab_control</strong></dt>
+</dl>
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.app.possible_app.html#PossibleApp">telemetry.internal.app.possible_app.PossibleApp</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>app_type</strong></dt>
+</dl>
+<dl><dt><strong>platform</strong></dt>
+</dl>
+<dl><dt><strong>target_os</strong></dt>
+<dd><tt>Target&nbsp;OS,&nbsp;the&nbsp;app&nbsp;will&nbsp;run&nbsp;on.</tt></dd>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-CanFindAvailableBrowsers"><strong>CanFindAvailableBrowsers</strong></a>(finder_options)</dt></dl>
+ <dl><dt><a name="-FindAllAvailableBrowsers"><strong>FindAllAvailableBrowsers</strong></a>(finder_options, device)</dt><dd><tt>Finds&nbsp;all&nbsp;available&nbsp;CrOS&nbsp;browsers,&nbsp;locally&nbsp;and&nbsp;remotely.</tt></dd></dl>
+ <dl><dt><a name="-FindAllBrowserTypes"><strong>FindAllBrowserTypes</strong></a>(_)</dt></dl>
+ <dl><dt><a name="-SelectDefaultBrowser"><strong>SelectDefaultBrowser</strong></a>(possible_browsers)</dt></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.cros_browser_with_oobe.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.cros_browser_with_oobe.html
new file mode 100644
index 0000000..5565f52
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.cros_browser_with_oobe.html
@@ -0,0 +1,183 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.chrome.cros_browser_with_oobe</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.<a href="telemetry.internal.backends.chrome.html"><font color="#ffffff">chrome</font></a>.cros_browser_with_oobe</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/chrome/cros_browser_with_oobe.py">telemetry/internal/backends/chrome/cros_browser_with_oobe.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.browser.browser.html">telemetry.internal.browser.browser</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.backends.chrome.cros_browser_backend.html">telemetry.internal.backends.chrome.cros_browser_backend</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.browser.browser.html#Browser">telemetry.internal.browser.browser.Browser</a>(<a href="telemetry.internal.app.html#App">telemetry.internal.app.App</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome.cros_browser_with_oobe.html#CrOSBrowserWithOOBE">CrOSBrowserWithOOBE</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="CrOSBrowserWithOOBE">class <strong>CrOSBrowserWithOOBE</strong></a>(<a href="telemetry.internal.browser.browser.html#Browser">telemetry.internal.browser.browser.Browser</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Cros-specific&nbsp;browser.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.chrome.cros_browser_with_oobe.html#CrOSBrowserWithOOBE">CrOSBrowserWithOOBE</a></dd>
+<dd><a href="telemetry.internal.browser.browser.html#Browser">telemetry.internal.browser.browser.Browser</a></dd>
+<dd><a href="telemetry.internal.app.html#App">telemetry.internal.app.App</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="CrOSBrowserWithOOBE-__init__"><strong>__init__</strong></a>(self, backend, platform_backend, credentials_path)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>oobe</strong></dt>
+<dd><tt>The&nbsp;login&nbsp;webui&nbsp;(also&nbsp;serves&nbsp;as&nbsp;ui&nbsp;for&nbsp;screenlock&nbsp;and<br>
+out-of-box-experience).</tt></dd>
+</dl>
+<dl><dt><strong>oobe_exists</strong></dt>
+<dd><tt>True&nbsp;if&nbsp;the&nbsp;login/oobe/screenlock&nbsp;webui&nbsp;exists.&nbsp;This&nbsp;is&nbsp;more&nbsp;lightweight<br>
+than&nbsp;accessing&nbsp;the&nbsp;oobe&nbsp;property.</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.internal.browser.browser.html#Browser">telemetry.internal.browser.browser.Browser</a>:<br>
+<dl><dt><a name="CrOSBrowserWithOOBE-Close"><strong>Close</strong></a>(self)</dt><dd><tt>Closes&nbsp;this&nbsp;browser.</tt></dd></dl>
+
+<dl><dt><a name="CrOSBrowserWithOOBE-DumpMemory"><strong>DumpMemory</strong></a>(self, timeout<font color="#909090">=90</font>)</dt></dl>
+
+<dl><dt><a name="CrOSBrowserWithOOBE-GetStackTrace"><strong>GetStackTrace</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrOSBrowserWithOOBE-GetStandardOutput"><strong>GetStandardOutput</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrOSBrowserWithOOBE-GetSystemInfo"><strong>GetSystemInfo</strong></a>(self)</dt><dd><tt>Returns&nbsp;low-level&nbsp;information&nbsp;about&nbsp;the&nbsp;system,&nbsp;if&nbsp;available.<br>
+&nbsp;<br>
+See&nbsp;the&nbsp;documentation&nbsp;of&nbsp;the&nbsp;SystemInfo&nbsp;class&nbsp;for&nbsp;more&nbsp;details.</tt></dd></dl>
+
+<dl><dt><a name="CrOSBrowserWithOOBE-SetMemoryPressureNotificationsSuppressed"><strong>SetMemoryPressureNotificationsSuppressed</strong></a>(self, suppressed, timeout<font color="#909090">=90</font>)</dt></dl>
+
+<dl><dt><a name="CrOSBrowserWithOOBE-SimulateMemoryPressureNotification"><strong>SimulateMemoryPressureNotification</strong></a>(self, pressure_level, timeout<font color="#909090">=90</font>)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.browser.browser.html#Browser">telemetry.internal.browser.browser.Browser</a>:<br>
+<dl><dt><strong>browser_type</strong></dt>
+</dl>
+<dl><dt><strong>cpu_stats</strong></dt>
+<dd><tt>Returns&nbsp;a&nbsp;dict&nbsp;of&nbsp;cpu&nbsp;statistics&nbsp;for&nbsp;the&nbsp;system.<br>
+{&nbsp;'Browser':&nbsp;{<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'CpuProcessTime':&nbsp;S,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'TotalTime':&nbsp;T<br>
+&nbsp;&nbsp;},<br>
+&nbsp;&nbsp;'Gpu':&nbsp;{<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'CpuProcessTime':&nbsp;S,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'TotalTime':&nbsp;T<br>
+&nbsp;&nbsp;},<br>
+&nbsp;&nbsp;'Renderer':&nbsp;{<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'CpuProcessTime':&nbsp;S,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'TotalTime':&nbsp;T<br>
+&nbsp;&nbsp;}<br>
+}<br>
+Any&nbsp;of&nbsp;the&nbsp;above&nbsp;keys&nbsp;may&nbsp;be&nbsp;missing&nbsp;on&nbsp;a&nbsp;per-platform&nbsp;basis.</tt></dd>
+</dl>
+<dl><dt><strong>extensions</strong></dt>
+</dl>
+<dl><dt><strong>foreground_tab</strong></dt>
+</dl>
+<dl><dt><strong>memory_stats</strong></dt>
+<dd><tt>Returns&nbsp;a&nbsp;dict&nbsp;of&nbsp;memory&nbsp;statistics&nbsp;for&nbsp;the&nbsp;browser:<br>
+{&nbsp;'Browser':&nbsp;{<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'VM':&nbsp;R,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'VMPeak':&nbsp;S,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'WorkingSetSize':&nbsp;T,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'WorkingSetSizePeak':&nbsp;U,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'ProportionalSetSize':&nbsp;V,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'PrivateDirty':&nbsp;W<br>
+&nbsp;&nbsp;},<br>
+&nbsp;&nbsp;'Gpu':&nbsp;{<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'VM':&nbsp;R,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'VMPeak':&nbsp;S,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'WorkingSetSize':&nbsp;T,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'WorkingSetSizePeak':&nbsp;U,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'ProportionalSetSize':&nbsp;V,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'PrivateDirty':&nbsp;W<br>
+&nbsp;&nbsp;},<br>
+&nbsp;&nbsp;'Renderer':&nbsp;{<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'VM':&nbsp;R,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'VMPeak':&nbsp;S,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'WorkingSetSize':&nbsp;T,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'WorkingSetSizePeak':&nbsp;U,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'ProportionalSetSize':&nbsp;V,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'PrivateDirty':&nbsp;W<br>
+&nbsp;&nbsp;},<br>
+&nbsp;&nbsp;'SystemCommitCharge':&nbsp;X,<br>
+&nbsp;&nbsp;'SystemTotalPhysicalMemory':&nbsp;Y,<br>
+&nbsp;&nbsp;'ProcessCount':&nbsp;Z,<br>
+}<br>
+Any&nbsp;of&nbsp;the&nbsp;above&nbsp;keys&nbsp;may&nbsp;be&nbsp;missing&nbsp;on&nbsp;a&nbsp;per-platform&nbsp;basis.</tt></dd>
+</dl>
+<dl><dt><strong>profiling_controller</strong></dt>
+</dl>
+<dl><dt><strong>supports_cpu_metrics</strong></dt>
+</dl>
+<dl><dt><strong>supports_extensions</strong></dt>
+</dl>
+<dl><dt><strong>supports_memory_dumping</strong></dt>
+</dl>
+<dl><dt><strong>supports_memory_metrics</strong></dt>
+</dl>
+<dl><dt><strong>supports_overriding_memory_pressure_notifications</strong></dt>
+</dl>
+<dl><dt><strong>supports_power_metrics</strong></dt>
+</dl>
+<dl><dt><strong>supports_system_info</strong></dt>
+</dl>
+<dl><dt><strong>supports_tab_control</strong></dt>
+</dl>
+<dl><dt><strong>tabs</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.internal.app.html#App">telemetry.internal.app.App</a>:<br>
+<dl><dt><a name="CrOSBrowserWithOOBE-__enter__"><strong>__enter__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrOSBrowserWithOOBE-__exit__"><strong>__exit__</strong></a>(self, *args)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.app.html#App">telemetry.internal.app.App</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>app_type</strong></dt>
+</dl>
+<dl><dt><strong>platform</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.cros_test_case.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.cros_test_case.html
new file mode 100644
index 0000000..b12b369
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.cros_test_case.html
@@ -0,0 +1,339 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.chrome.cros_test_case</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.<a href="telemetry.internal.backends.chrome.html"><font color="#ffffff">chrome</font></a>.cros_test_case</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/chrome/cros_test_case.py">telemetry/internal/backends/chrome/cros_test_case.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.browser.browser_finder.html">telemetry.internal.browser.browser_finder</a><br>
+<a href="telemetry.core.cros_interface.html">telemetry.core.cros_interface</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.browser.extension_to_load.html">telemetry.internal.browser.extension_to_load</a><br>
+<a href="telemetry.testing.options_for_unittests.html">telemetry.testing.options_for_unittests</a><br>
+</td><td width="25%" valign=top><a href="os.html">os</a><br>
+<a href="unittest.html">unittest</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.util.html">telemetry.core.util</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="unittest.case.html#TestCase">unittest.case.TestCase</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome.cros_test_case.html#CrOSTestCase">CrOSTestCase</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="CrOSTestCase">class <strong>CrOSTestCase</strong></a>(<a href="unittest.case.html#TestCase">unittest.case.TestCase</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.chrome.cros_test_case.html#CrOSTestCase">CrOSTestCase</a></dd>
+<dd><a href="unittest.case.html#TestCase">unittest.case.TestCase</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="CrOSTestCase-setUp"><strong>setUp</strong></a>(self)</dt></dl>
+
+<hr>
+Methods inherited from <a href="unittest.case.html#TestCase">unittest.case.TestCase</a>:<br>
+<dl><dt><a name="CrOSTestCase-__call__"><strong>__call__</strong></a>(self, *args, **kwds)</dt></dl>
+
+<dl><dt><a name="CrOSTestCase-__eq__"><strong>__eq__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="CrOSTestCase-__hash__"><strong>__hash__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrOSTestCase-__init__"><strong>__init__</strong></a>(self, methodName<font color="#909090">='runTest'</font>)</dt><dd><tt>Create&nbsp;an&nbsp;instance&nbsp;of&nbsp;the&nbsp;class&nbsp;that&nbsp;will&nbsp;use&nbsp;the&nbsp;named&nbsp;test<br>
+method&nbsp;when&nbsp;executed.&nbsp;Raises&nbsp;a&nbsp;ValueError&nbsp;if&nbsp;the&nbsp;instance&nbsp;does<br>
+not&nbsp;have&nbsp;a&nbsp;method&nbsp;with&nbsp;the&nbsp;specified&nbsp;name.</tt></dd></dl>
+
+<dl><dt><a name="CrOSTestCase-__ne__"><strong>__ne__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="CrOSTestCase-__repr__"><strong>__repr__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrOSTestCase-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrOSTestCase-addCleanup"><strong>addCleanup</strong></a>(self, function, *args, **kwargs)</dt><dd><tt>Add&nbsp;a&nbsp;function,&nbsp;with&nbsp;arguments,&nbsp;to&nbsp;be&nbsp;called&nbsp;when&nbsp;the&nbsp;test&nbsp;is<br>
+completed.&nbsp;Functions&nbsp;added&nbsp;are&nbsp;called&nbsp;on&nbsp;a&nbsp;LIFO&nbsp;basis&nbsp;and&nbsp;are<br>
+called&nbsp;after&nbsp;tearDown&nbsp;on&nbsp;test&nbsp;failure&nbsp;or&nbsp;success.<br>
+&nbsp;<br>
+Cleanup&nbsp;items&nbsp;are&nbsp;called&nbsp;even&nbsp;if&nbsp;setUp&nbsp;fails&nbsp;(unlike&nbsp;tearDown).</tt></dd></dl>
+
+<dl><dt><a name="CrOSTestCase-addTypeEqualityFunc"><strong>addTypeEqualityFunc</strong></a>(self, typeobj, function)</dt><dd><tt>Add&nbsp;a&nbsp;type&nbsp;specific&nbsp;assertEqual&nbsp;style&nbsp;function&nbsp;to&nbsp;compare&nbsp;a&nbsp;type.<br>
+&nbsp;<br>
+This&nbsp;method&nbsp;is&nbsp;for&nbsp;use&nbsp;by&nbsp;<a href="unittest.case.html#TestCase">TestCase</a>&nbsp;subclasses&nbsp;that&nbsp;need&nbsp;to&nbsp;register<br>
+their&nbsp;own&nbsp;type&nbsp;equality&nbsp;functions&nbsp;to&nbsp;provide&nbsp;nicer&nbsp;error&nbsp;messages.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;typeobj:&nbsp;The&nbsp;data&nbsp;type&nbsp;to&nbsp;call&nbsp;this&nbsp;function&nbsp;on&nbsp;when&nbsp;both&nbsp;values<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;are&nbsp;of&nbsp;the&nbsp;same&nbsp;type&nbsp;in&nbsp;<a href="#CrOSTestCase-assertEqual">assertEqual</a>().<br>
+&nbsp;&nbsp;&nbsp;&nbsp;function:&nbsp;The&nbsp;callable&nbsp;taking&nbsp;two&nbsp;arguments&nbsp;and&nbsp;an&nbsp;optional<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;msg=&nbsp;argument&nbsp;that&nbsp;raises&nbsp;self.<strong>failureException</strong>&nbsp;with&nbsp;a<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;useful&nbsp;error&nbsp;message&nbsp;when&nbsp;the&nbsp;two&nbsp;arguments&nbsp;are&nbsp;not&nbsp;equal.</tt></dd></dl>
+
+<dl><dt><a name="CrOSTestCase-assertAlmostEqual"><strong>assertAlmostEqual</strong></a>(self, first, second, places<font color="#909090">=None</font>, msg<font color="#909090">=None</font>, delta<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;unequal&nbsp;as&nbsp;determined&nbsp;by&nbsp;their<br>
+difference&nbsp;rounded&nbsp;to&nbsp;the&nbsp;given&nbsp;number&nbsp;of&nbsp;decimal&nbsp;places<br>
+(default&nbsp;7)&nbsp;and&nbsp;comparing&nbsp;to&nbsp;zero,&nbsp;or&nbsp;by&nbsp;comparing&nbsp;that&nbsp;the<br>
+between&nbsp;the&nbsp;two&nbsp;objects&nbsp;is&nbsp;more&nbsp;than&nbsp;the&nbsp;given&nbsp;delta.<br>
+&nbsp;<br>
+Note&nbsp;that&nbsp;decimal&nbsp;places&nbsp;(from&nbsp;zero)&nbsp;are&nbsp;usually&nbsp;not&nbsp;the&nbsp;same<br>
+as&nbsp;significant&nbsp;digits&nbsp;(measured&nbsp;from&nbsp;the&nbsp;most&nbsp;signficant&nbsp;digit).<br>
+&nbsp;<br>
+If&nbsp;the&nbsp;two&nbsp;objects&nbsp;compare&nbsp;equal&nbsp;then&nbsp;they&nbsp;will&nbsp;automatically<br>
+compare&nbsp;almost&nbsp;equal.</tt></dd></dl>
+
+<dl><dt><a name="CrOSTestCase-assertAlmostEquals"><strong>assertAlmostEquals</strong></a> = assertAlmostEqual(self, first, second, places<font color="#909090">=None</font>, msg<font color="#909090">=None</font>, delta<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;unequal&nbsp;as&nbsp;determined&nbsp;by&nbsp;their<br>
+difference&nbsp;rounded&nbsp;to&nbsp;the&nbsp;given&nbsp;number&nbsp;of&nbsp;decimal&nbsp;places<br>
+(default&nbsp;7)&nbsp;and&nbsp;comparing&nbsp;to&nbsp;zero,&nbsp;or&nbsp;by&nbsp;comparing&nbsp;that&nbsp;the<br>
+between&nbsp;the&nbsp;two&nbsp;objects&nbsp;is&nbsp;more&nbsp;than&nbsp;the&nbsp;given&nbsp;delta.<br>
+&nbsp;<br>
+Note&nbsp;that&nbsp;decimal&nbsp;places&nbsp;(from&nbsp;zero)&nbsp;are&nbsp;usually&nbsp;not&nbsp;the&nbsp;same<br>
+as&nbsp;significant&nbsp;digits&nbsp;(measured&nbsp;from&nbsp;the&nbsp;most&nbsp;signficant&nbsp;digit).<br>
+&nbsp;<br>
+If&nbsp;the&nbsp;two&nbsp;objects&nbsp;compare&nbsp;equal&nbsp;then&nbsp;they&nbsp;will&nbsp;automatically<br>
+compare&nbsp;almost&nbsp;equal.</tt></dd></dl>
+
+<dl><dt><a name="CrOSTestCase-assertDictContainsSubset"><strong>assertDictContainsSubset</strong></a>(self, expected, actual, msg<font color="#909090">=None</font>)</dt><dd><tt>Checks&nbsp;whether&nbsp;actual&nbsp;is&nbsp;a&nbsp;superset&nbsp;of&nbsp;expected.</tt></dd></dl>
+
+<dl><dt><a name="CrOSTestCase-assertDictEqual"><strong>assertDictEqual</strong></a>(self, d1, d2, msg<font color="#909090">=None</font>)</dt></dl>
+
+<dl><dt><a name="CrOSTestCase-assertEqual"><strong>assertEqual</strong></a>(self, first, second, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;unequal&nbsp;as&nbsp;determined&nbsp;by&nbsp;the&nbsp;'=='<br>
+operator.</tt></dd></dl>
+
+<dl><dt><a name="CrOSTestCase-assertEquals"><strong>assertEquals</strong></a> = assertEqual(self, first, second, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;unequal&nbsp;as&nbsp;determined&nbsp;by&nbsp;the&nbsp;'=='<br>
+operator.</tt></dd></dl>
+
+<dl><dt><a name="CrOSTestCase-assertFalse"><strong>assertFalse</strong></a>(self, expr, msg<font color="#909090">=None</font>)</dt><dd><tt>Check&nbsp;that&nbsp;the&nbsp;expression&nbsp;is&nbsp;false.</tt></dd></dl>
+
+<dl><dt><a name="CrOSTestCase-assertGreater"><strong>assertGreater</strong></a>(self, a, b, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#CrOSTestCase-assertTrue">assertTrue</a>(a&nbsp;&gt;&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="CrOSTestCase-assertGreaterEqual"><strong>assertGreaterEqual</strong></a>(self, a, b, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#CrOSTestCase-assertTrue">assertTrue</a>(a&nbsp;&gt;=&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="CrOSTestCase-assertIn"><strong>assertIn</strong></a>(self, member, container, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#CrOSTestCase-assertTrue">assertTrue</a>(a&nbsp;in&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="CrOSTestCase-assertIs"><strong>assertIs</strong></a>(self, expr1, expr2, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#CrOSTestCase-assertTrue">assertTrue</a>(a&nbsp;is&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="CrOSTestCase-assertIsInstance"><strong>assertIsInstance</strong></a>(self, obj, cls, msg<font color="#909090">=None</font>)</dt><dd><tt>Same&nbsp;as&nbsp;<a href="#CrOSTestCase-assertTrue">assertTrue</a>(isinstance(obj,&nbsp;cls)),&nbsp;with&nbsp;a&nbsp;nicer<br>
+default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="CrOSTestCase-assertIsNone"><strong>assertIsNone</strong></a>(self, obj, msg<font color="#909090">=None</font>)</dt><dd><tt>Same&nbsp;as&nbsp;<a href="#CrOSTestCase-assertTrue">assertTrue</a>(obj&nbsp;is&nbsp;None),&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="CrOSTestCase-assertIsNot"><strong>assertIsNot</strong></a>(self, expr1, expr2, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#CrOSTestCase-assertTrue">assertTrue</a>(a&nbsp;is&nbsp;not&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="CrOSTestCase-assertIsNotNone"><strong>assertIsNotNone</strong></a>(self, obj, msg<font color="#909090">=None</font>)</dt><dd><tt>Included&nbsp;for&nbsp;symmetry&nbsp;with&nbsp;assertIsNone.</tt></dd></dl>
+
+<dl><dt><a name="CrOSTestCase-assertItemsEqual"><strong>assertItemsEqual</strong></a>(self, expected_seq, actual_seq, msg<font color="#909090">=None</font>)</dt><dd><tt>An&nbsp;unordered&nbsp;sequence&nbsp;specific&nbsp;comparison.&nbsp;It&nbsp;asserts&nbsp;that<br>
+actual_seq&nbsp;and&nbsp;expected_seq&nbsp;have&nbsp;the&nbsp;same&nbsp;element&nbsp;counts.<br>
+Equivalent&nbsp;to::<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#CrOSTestCase-assertEqual">assertEqual</a>(Counter(iter(actual_seq)),<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Counter(iter(expected_seq)))<br>
+&nbsp;<br>
+Asserts&nbsp;that&nbsp;each&nbsp;element&nbsp;has&nbsp;the&nbsp;same&nbsp;count&nbsp;in&nbsp;both&nbsp;sequences.<br>
+Example:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;[0,&nbsp;1,&nbsp;1]&nbsp;and&nbsp;[1,&nbsp;0,&nbsp;1]&nbsp;compare&nbsp;equal.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;[0,&nbsp;0,&nbsp;1]&nbsp;and&nbsp;[0,&nbsp;1]&nbsp;compare&nbsp;unequal.</tt></dd></dl>
+
+<dl><dt><a name="CrOSTestCase-assertLess"><strong>assertLess</strong></a>(self, a, b, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#CrOSTestCase-assertTrue">assertTrue</a>(a&nbsp;&lt;&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="CrOSTestCase-assertLessEqual"><strong>assertLessEqual</strong></a>(self, a, b, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#CrOSTestCase-assertTrue">assertTrue</a>(a&nbsp;&lt;=&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="CrOSTestCase-assertListEqual"><strong>assertListEqual</strong></a>(self, list1, list2, msg<font color="#909090">=None</font>)</dt><dd><tt>A&nbsp;list-specific&nbsp;equality&nbsp;assertion.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;list1:&nbsp;The&nbsp;first&nbsp;list&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;list2:&nbsp;The&nbsp;second&nbsp;list&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;msg:&nbsp;Optional&nbsp;message&nbsp;to&nbsp;use&nbsp;on&nbsp;failure&nbsp;instead&nbsp;of&nbsp;a&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;differences.</tt></dd></dl>
+
+<dl><dt><a name="CrOSTestCase-assertMultiLineEqual"><strong>assertMultiLineEqual</strong></a>(self, first, second, msg<font color="#909090">=None</font>)</dt><dd><tt>Assert&nbsp;that&nbsp;two&nbsp;multi-line&nbsp;strings&nbsp;are&nbsp;equal.</tt></dd></dl>
+
+<dl><dt><a name="CrOSTestCase-assertNotAlmostEqual"><strong>assertNotAlmostEqual</strong></a>(self, first, second, places<font color="#909090">=None</font>, msg<font color="#909090">=None</font>, delta<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;equal&nbsp;as&nbsp;determined&nbsp;by&nbsp;their<br>
+difference&nbsp;rounded&nbsp;to&nbsp;the&nbsp;given&nbsp;number&nbsp;of&nbsp;decimal&nbsp;places<br>
+(default&nbsp;7)&nbsp;and&nbsp;comparing&nbsp;to&nbsp;zero,&nbsp;or&nbsp;by&nbsp;comparing&nbsp;that&nbsp;the<br>
+between&nbsp;the&nbsp;two&nbsp;objects&nbsp;is&nbsp;less&nbsp;than&nbsp;the&nbsp;given&nbsp;delta.<br>
+&nbsp;<br>
+Note&nbsp;that&nbsp;decimal&nbsp;places&nbsp;(from&nbsp;zero)&nbsp;are&nbsp;usually&nbsp;not&nbsp;the&nbsp;same<br>
+as&nbsp;significant&nbsp;digits&nbsp;(measured&nbsp;from&nbsp;the&nbsp;most&nbsp;signficant&nbsp;digit).<br>
+&nbsp;<br>
+Objects&nbsp;that&nbsp;are&nbsp;equal&nbsp;automatically&nbsp;fail.</tt></dd></dl>
+
+<dl><dt><a name="CrOSTestCase-assertNotAlmostEquals"><strong>assertNotAlmostEquals</strong></a> = assertNotAlmostEqual(self, first, second, places<font color="#909090">=None</font>, msg<font color="#909090">=None</font>, delta<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;equal&nbsp;as&nbsp;determined&nbsp;by&nbsp;their<br>
+difference&nbsp;rounded&nbsp;to&nbsp;the&nbsp;given&nbsp;number&nbsp;of&nbsp;decimal&nbsp;places<br>
+(default&nbsp;7)&nbsp;and&nbsp;comparing&nbsp;to&nbsp;zero,&nbsp;or&nbsp;by&nbsp;comparing&nbsp;that&nbsp;the<br>
+between&nbsp;the&nbsp;two&nbsp;objects&nbsp;is&nbsp;less&nbsp;than&nbsp;the&nbsp;given&nbsp;delta.<br>
+&nbsp;<br>
+Note&nbsp;that&nbsp;decimal&nbsp;places&nbsp;(from&nbsp;zero)&nbsp;are&nbsp;usually&nbsp;not&nbsp;the&nbsp;same<br>
+as&nbsp;significant&nbsp;digits&nbsp;(measured&nbsp;from&nbsp;the&nbsp;most&nbsp;signficant&nbsp;digit).<br>
+&nbsp;<br>
+Objects&nbsp;that&nbsp;are&nbsp;equal&nbsp;automatically&nbsp;fail.</tt></dd></dl>
+
+<dl><dt><a name="CrOSTestCase-assertNotEqual"><strong>assertNotEqual</strong></a>(self, first, second, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;equal&nbsp;as&nbsp;determined&nbsp;by&nbsp;the&nbsp;'!='<br>
+operator.</tt></dd></dl>
+
+<dl><dt><a name="CrOSTestCase-assertNotEquals"><strong>assertNotEquals</strong></a> = assertNotEqual(self, first, second, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;equal&nbsp;as&nbsp;determined&nbsp;by&nbsp;the&nbsp;'!='<br>
+operator.</tt></dd></dl>
+
+<dl><dt><a name="CrOSTestCase-assertNotIn"><strong>assertNotIn</strong></a>(self, member, container, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#CrOSTestCase-assertTrue">assertTrue</a>(a&nbsp;not&nbsp;in&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="CrOSTestCase-assertNotIsInstance"><strong>assertNotIsInstance</strong></a>(self, obj, cls, msg<font color="#909090">=None</font>)</dt><dd><tt>Included&nbsp;for&nbsp;symmetry&nbsp;with&nbsp;assertIsInstance.</tt></dd></dl>
+
+<dl><dt><a name="CrOSTestCase-assertNotRegexpMatches"><strong>assertNotRegexpMatches</strong></a>(self, text, unexpected_regexp, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;the&nbsp;test&nbsp;if&nbsp;the&nbsp;text&nbsp;matches&nbsp;the&nbsp;regular&nbsp;expression.</tt></dd></dl>
+
+<dl><dt><a name="CrOSTestCase-assertRaises"><strong>assertRaises</strong></a>(self, excClass, callableObj<font color="#909090">=None</font>, *args, **kwargs)</dt><dd><tt>Fail&nbsp;unless&nbsp;an&nbsp;exception&nbsp;of&nbsp;class&nbsp;excClass&nbsp;is&nbsp;raised<br>
+by&nbsp;callableObj&nbsp;when&nbsp;invoked&nbsp;with&nbsp;arguments&nbsp;args&nbsp;and&nbsp;keyword<br>
+arguments&nbsp;kwargs.&nbsp;If&nbsp;a&nbsp;different&nbsp;type&nbsp;of&nbsp;exception&nbsp;is<br>
+raised,&nbsp;it&nbsp;will&nbsp;not&nbsp;be&nbsp;caught,&nbsp;and&nbsp;the&nbsp;test&nbsp;case&nbsp;will&nbsp;be<br>
+deemed&nbsp;to&nbsp;have&nbsp;suffered&nbsp;an&nbsp;error,&nbsp;exactly&nbsp;as&nbsp;for&nbsp;an<br>
+unexpected&nbsp;exception.<br>
+&nbsp;<br>
+If&nbsp;called&nbsp;with&nbsp;callableObj&nbsp;omitted&nbsp;or&nbsp;None,&nbsp;will&nbsp;return&nbsp;a<br>
+context&nbsp;object&nbsp;used&nbsp;like&nbsp;this::<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;with&nbsp;<a href="#CrOSTestCase-assertRaises">assertRaises</a>(SomeException):<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;do_something()<br>
+&nbsp;<br>
+The&nbsp;context&nbsp;manager&nbsp;keeps&nbsp;a&nbsp;reference&nbsp;to&nbsp;the&nbsp;exception&nbsp;as<br>
+the&nbsp;'exception'&nbsp;attribute.&nbsp;This&nbsp;allows&nbsp;you&nbsp;to&nbsp;inspect&nbsp;the<br>
+exception&nbsp;after&nbsp;the&nbsp;assertion::<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;with&nbsp;<a href="#CrOSTestCase-assertRaises">assertRaises</a>(SomeException)&nbsp;as&nbsp;cm:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;do_something()<br>
+&nbsp;&nbsp;&nbsp;&nbsp;the_exception&nbsp;=&nbsp;cm.exception<br>
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#CrOSTestCase-assertEqual">assertEqual</a>(the_exception.error_code,&nbsp;3)</tt></dd></dl>
+
+<dl><dt><a name="CrOSTestCase-assertRaisesRegexp"><strong>assertRaisesRegexp</strong></a>(self, expected_exception, expected_regexp, callable_obj<font color="#909090">=None</font>, *args, **kwargs)</dt><dd><tt>Asserts&nbsp;that&nbsp;the&nbsp;message&nbsp;in&nbsp;a&nbsp;raised&nbsp;exception&nbsp;matches&nbsp;a&nbsp;regexp.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;expected_exception:&nbsp;Exception&nbsp;class&nbsp;expected&nbsp;to&nbsp;be&nbsp;raised.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;expected_regexp:&nbsp;Regexp&nbsp;(re&nbsp;pattern&nbsp;object&nbsp;or&nbsp;string)&nbsp;expected<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;to&nbsp;be&nbsp;found&nbsp;in&nbsp;error&nbsp;message.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;callable_obj:&nbsp;Function&nbsp;to&nbsp;be&nbsp;called.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;args:&nbsp;Extra&nbsp;args.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;kwargs:&nbsp;Extra&nbsp;kwargs.</tt></dd></dl>
+
+<dl><dt><a name="CrOSTestCase-assertRegexpMatches"><strong>assertRegexpMatches</strong></a>(self, text, expected_regexp, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;the&nbsp;test&nbsp;unless&nbsp;the&nbsp;text&nbsp;matches&nbsp;the&nbsp;regular&nbsp;expression.</tt></dd></dl>
+
+<dl><dt><a name="CrOSTestCase-assertSequenceEqual"><strong>assertSequenceEqual</strong></a>(self, seq1, seq2, msg<font color="#909090">=None</font>, seq_type<font color="#909090">=None</font>)</dt><dd><tt>An&nbsp;equality&nbsp;assertion&nbsp;for&nbsp;ordered&nbsp;sequences&nbsp;(like&nbsp;lists&nbsp;and&nbsp;tuples).<br>
+&nbsp;<br>
+For&nbsp;the&nbsp;purposes&nbsp;of&nbsp;this&nbsp;function,&nbsp;a&nbsp;valid&nbsp;ordered&nbsp;sequence&nbsp;type&nbsp;is&nbsp;one<br>
+which&nbsp;can&nbsp;be&nbsp;indexed,&nbsp;has&nbsp;a&nbsp;length,&nbsp;and&nbsp;has&nbsp;an&nbsp;equality&nbsp;operator.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;seq1:&nbsp;The&nbsp;first&nbsp;sequence&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;seq2:&nbsp;The&nbsp;second&nbsp;sequence&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;seq_type:&nbsp;The&nbsp;expected&nbsp;datatype&nbsp;of&nbsp;the&nbsp;sequences,&nbsp;or&nbsp;None&nbsp;if&nbsp;no<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;datatype&nbsp;should&nbsp;be&nbsp;enforced.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;msg:&nbsp;Optional&nbsp;message&nbsp;to&nbsp;use&nbsp;on&nbsp;failure&nbsp;instead&nbsp;of&nbsp;a&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;differences.</tt></dd></dl>
+
+<dl><dt><a name="CrOSTestCase-assertSetEqual"><strong>assertSetEqual</strong></a>(self, set1, set2, msg<font color="#909090">=None</font>)</dt><dd><tt>A&nbsp;set-specific&nbsp;equality&nbsp;assertion.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;set1:&nbsp;The&nbsp;first&nbsp;set&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;set2:&nbsp;The&nbsp;second&nbsp;set&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;msg:&nbsp;Optional&nbsp;message&nbsp;to&nbsp;use&nbsp;on&nbsp;failure&nbsp;instead&nbsp;of&nbsp;a&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;differences.<br>
+&nbsp;<br>
+assertSetEqual&nbsp;uses&nbsp;ducktyping&nbsp;to&nbsp;support&nbsp;different&nbsp;types&nbsp;of&nbsp;sets,&nbsp;and<br>
+is&nbsp;optimized&nbsp;for&nbsp;sets&nbsp;specifically&nbsp;(parameters&nbsp;must&nbsp;support&nbsp;a<br>
+difference&nbsp;method).</tt></dd></dl>
+
+<dl><dt><a name="CrOSTestCase-assertTrue"><strong>assertTrue</strong></a>(self, expr, msg<font color="#909090">=None</font>)</dt><dd><tt>Check&nbsp;that&nbsp;the&nbsp;expression&nbsp;is&nbsp;true.</tt></dd></dl>
+
+<dl><dt><a name="CrOSTestCase-assertTupleEqual"><strong>assertTupleEqual</strong></a>(self, tuple1, tuple2, msg<font color="#909090">=None</font>)</dt><dd><tt>A&nbsp;tuple-specific&nbsp;equality&nbsp;assertion.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;tuple1:&nbsp;The&nbsp;first&nbsp;tuple&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;tuple2:&nbsp;The&nbsp;second&nbsp;tuple&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;msg:&nbsp;Optional&nbsp;message&nbsp;to&nbsp;use&nbsp;on&nbsp;failure&nbsp;instead&nbsp;of&nbsp;a&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;differences.</tt></dd></dl>
+
+<dl><dt><a name="CrOSTestCase-assert_"><strong>assert_</strong></a> = assertTrue(self, expr, msg<font color="#909090">=None</font>)</dt><dd><tt>Check&nbsp;that&nbsp;the&nbsp;expression&nbsp;is&nbsp;true.</tt></dd></dl>
+
+<dl><dt><a name="CrOSTestCase-countTestCases"><strong>countTestCases</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrOSTestCase-debug"><strong>debug</strong></a>(self)</dt><dd><tt>Run&nbsp;the&nbsp;test&nbsp;without&nbsp;collecting&nbsp;errors&nbsp;in&nbsp;a&nbsp;TestResult</tt></dd></dl>
+
+<dl><dt><a name="CrOSTestCase-defaultTestResult"><strong>defaultTestResult</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrOSTestCase-doCleanups"><strong>doCleanups</strong></a>(self)</dt><dd><tt>Execute&nbsp;all&nbsp;cleanup&nbsp;functions.&nbsp;Normally&nbsp;called&nbsp;for&nbsp;you&nbsp;after<br>
+tearDown.</tt></dd></dl>
+
+<dl><dt><a name="CrOSTestCase-fail"><strong>fail</strong></a>(self, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;immediately,&nbsp;with&nbsp;the&nbsp;given&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="CrOSTestCase-failIf"><strong>failIf</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="CrOSTestCase-failIfAlmostEqual"><strong>failIfAlmostEqual</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="CrOSTestCase-failIfEqual"><strong>failIfEqual</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="CrOSTestCase-failUnless"><strong>failUnless</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="CrOSTestCase-failUnlessAlmostEqual"><strong>failUnlessAlmostEqual</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="CrOSTestCase-failUnlessEqual"><strong>failUnlessEqual</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="CrOSTestCase-failUnlessRaises"><strong>failUnlessRaises</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="CrOSTestCase-id"><strong>id</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrOSTestCase-run"><strong>run</strong></a>(self, result<font color="#909090">=None</font>)</dt></dl>
+
+<dl><dt><a name="CrOSTestCase-shortDescription"><strong>shortDescription</strong></a>(self)</dt><dd><tt>Returns&nbsp;a&nbsp;one-line&nbsp;description&nbsp;of&nbsp;the&nbsp;test,&nbsp;or&nbsp;None&nbsp;if&nbsp;no<br>
+description&nbsp;has&nbsp;been&nbsp;provided.<br>
+&nbsp;<br>
+The&nbsp;default&nbsp;implementation&nbsp;of&nbsp;this&nbsp;method&nbsp;returns&nbsp;the&nbsp;first&nbsp;line&nbsp;of<br>
+the&nbsp;specified&nbsp;test&nbsp;method's&nbsp;docstring.</tt></dd></dl>
+
+<dl><dt><a name="CrOSTestCase-skipTest"><strong>skipTest</strong></a>(self, reason)</dt><dd><tt>Skip&nbsp;this&nbsp;test.</tt></dd></dl>
+
+<dl><dt><a name="CrOSTestCase-tearDown"><strong>tearDown</strong></a>(self)</dt><dd><tt>Hook&nbsp;method&nbsp;for&nbsp;deconstructing&nbsp;the&nbsp;test&nbsp;fixture&nbsp;after&nbsp;testing&nbsp;it.</tt></dd></dl>
+
+<hr>
+Class methods inherited from <a href="unittest.case.html#TestCase">unittest.case.TestCase</a>:<br>
+<dl><dt><a name="CrOSTestCase-setUpClass"><strong>setUpClass</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Hook&nbsp;method&nbsp;for&nbsp;setting&nbsp;up&nbsp;class&nbsp;fixture&nbsp;before&nbsp;running&nbsp;tests&nbsp;in&nbsp;the&nbsp;class.</tt></dd></dl>
+
+<dl><dt><a name="CrOSTestCase-tearDownClass"><strong>tearDownClass</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Hook&nbsp;method&nbsp;for&nbsp;deconstructing&nbsp;the&nbsp;class&nbsp;fixture&nbsp;after&nbsp;running&nbsp;all&nbsp;tests&nbsp;in&nbsp;the&nbsp;class.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="unittest.case.html#TestCase">unittest.case.TestCase</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="unittest.case.html#TestCase">unittest.case.TestCase</a>:<br>
+<dl><dt><strong>failureException</strong> = &lt;type 'exceptions.AssertionError'&gt;<dd><tt>Assertion&nbsp;failed.</tt></dl>
+
+<dl><dt><strong>longMessage</strong> = False</dl>
+
+<dl><dt><strong>maxDiff</strong> = 640</dl>
+
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.crx_id.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.crx_id.html
new file mode 100644
index 0000000..08c924a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.crx_id.html
@@ -0,0 +1,43 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.chrome.crx_id</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.<a href="telemetry.internal.backends.chrome.html"><font color="#ffffff">chrome</font></a>.crx_id</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/chrome/crx_id.py">telemetry/internal/backends/chrome/crx_id.py</a></font></td></tr></table>
+    <p><tt>Read&nbsp;a&nbsp;CRX&nbsp;file&nbsp;and&nbsp;write&nbsp;out&nbsp;the&nbsp;App&nbsp;ID&nbsp;and&nbsp;the&nbsp;Full&nbsp;Hash&nbsp;of&nbsp;the&nbsp;ID.<br>
+See:&nbsp;<a href="http://code.google.com/chrome/extensions/crx.html">http://code.google.com/chrome/extensions/crx.html</a><br>
+and&nbsp;'<a href="http://stackoverflow.com/questions/">http://stackoverflow.com/questions/</a>'<br>
+&nbsp;&nbsp;+&nbsp;'1882981/google-chrome-alphanumeric-hashes-to-identify-extensions'<br>
+for&nbsp;docs&nbsp;on&nbsp;the&nbsp;format.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="base64.html">base64</a><br>
+</td><td width="25%" valign=top><a href="hashlib.html">hashlib</a><br>
+</td><td width="25%" valign=top><a href="json.html">json</a><br>
+</td><td width="25%" valign=top><a href="os.html">os</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-GetCRXAppID"><strong>GetCRXAppID</strong></a>(filename, from_file_path<font color="#909090">=False</font>, is_win_path<font color="#909090">=False</font>)</dt></dl>
+ <dl><dt><a name="-GetCRXHash"><strong>GetCRXHash</strong></a>(filename, from_file_path<font color="#909090">=False</font>, is_win_path<font color="#909090">=False</font>)</dt></dl>
+ <dl><dt><a name="-GetPublicKey"><strong>GetPublicKey</strong></a>(filename, from_file_path, is_win_path<font color="#909090">=False</font>)</dt></dl>
+ <dl><dt><a name="-GetPublicKeyFromPath"><strong>GetPublicKeyFromPath</strong></a>(filepath, is_win_path<font color="#909090">=False</font>)</dt></dl>
+ <dl><dt><a name="-GetPublicKeyPacked"><strong>GetPublicKeyPacked</strong></a>(f)</dt></dl>
+ <dl><dt><a name="-GetPublicKeyUnpacked"><strong>GetPublicKeyUnpacked</strong></a>(f, filepath)</dt></dl>
+ <dl><dt><a name="-HasPublicKey"><strong>HasPublicKey</strong></a>(filename)</dt></dl>
+ <dl><dt><a name="-HexTo256"><strong>HexTo256</strong></a>(hex_chars)</dt><dd><tt>Convert&nbsp;bytes&nbsp;to&nbsp;pairs&nbsp;of&nbsp;hex&nbsp;digits.&nbsp;E.g.,&nbsp;
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.desktop_browser_backend.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.desktop_browser_backend.html
new file mode 100644
index 0000000..29637bb
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.desktop_browser_backend.html
@@ -0,0 +1,206 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.chrome.desktop_browser_backend</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.<a href="telemetry.internal.backends.chrome.html"><font color="#ffffff">chrome</font></a>.desktop_browser_backend</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/chrome/desktop_browser_backend.py">telemetry/internal/backends/chrome/desktop_browser_backend.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.util.binary_manager.html">telemetry.internal.util.binary_manager</a><br>
+<a href="telemetry.internal.backends.browser_backend.html">telemetry.internal.backends.browser_backend</a><br>
+<a href="telemetry.internal.backends.chrome.chrome_browser_backend.html">telemetry.internal.backends.chrome.chrome_browser_backend</a><br>
+<a href="catapult_base.cloud_storage.html">catapult_base.cloud_storage</a><br>
+<a href="datetime.html">datetime</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+<a href="glob.html">glob</a><br>
+<a href="heapq.html">heapq</a><br>
+<a href="logging.html">logging</a><br>
+<a href="os.html">os</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.util.path.html">telemetry.internal.util.path</a><br>
+<a href="random.html">random</a><br>
+<a href="re.html">re</a><br>
+<a href="shutil.html">shutil</a><br>
+<a href="subprocess.html">subprocess</a><br>
+</td><td width="25%" valign=top><a href="sys.html">sys</a><br>
+<a href="tempfile.html">tempfile</a><br>
+<a href="time.html">time</a><br>
+<a href="telemetry.core.util.html">telemetry.core.util</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome.chrome_browser_backend.html#ChromeBrowserBackend">telemetry.internal.backends.chrome.chrome_browser_backend.ChromeBrowserBackend</a>(<a href="telemetry.internal.backends.browser_backend.html#BrowserBackend">telemetry.internal.backends.browser_backend.BrowserBackend</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome.desktop_browser_backend.html#DesktopBrowserBackend">DesktopBrowserBackend</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="DesktopBrowserBackend">class <strong>DesktopBrowserBackend</strong></a>(<a href="telemetry.internal.backends.chrome.chrome_browser_backend.html#ChromeBrowserBackend">telemetry.internal.backends.chrome.chrome_browser_backend.ChromeBrowserBackend</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>The&nbsp;backend&nbsp;for&nbsp;controlling&nbsp;a&nbsp;locally-executed&nbsp;browser&nbsp;instance,&nbsp;on&nbsp;Linux,<br>
+Mac&nbsp;or&nbsp;Windows.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.chrome.desktop_browser_backend.html#DesktopBrowserBackend">DesktopBrowserBackend</a></dd>
+<dd><a href="telemetry.internal.backends.chrome.chrome_browser_backend.html#ChromeBrowserBackend">telemetry.internal.backends.chrome.chrome_browser_backend.ChromeBrowserBackend</a></dd>
+<dd><a href="telemetry.internal.backends.browser_backend.html#BrowserBackend">telemetry.internal.backends.browser_backend.BrowserBackend</a></dd>
+<dd><a href="telemetry.internal.backends.app_backend.html#AppBackend">telemetry.internal.backends.app_backend.AppBackend</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="DesktopBrowserBackend-Close"><strong>Close</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DesktopBrowserBackend-GetBrowserStartupArgs"><strong>GetBrowserStartupArgs</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DesktopBrowserBackend-GetStackTrace"><strong>GetStackTrace</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DesktopBrowserBackend-GetStandardOutput"><strong>GetStandardOutput</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DesktopBrowserBackend-HasBrowserFinishedLaunching"><strong>HasBrowserFinishedLaunching</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DesktopBrowserBackend-IsBrowserRunning"><strong>IsBrowserRunning</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DesktopBrowserBackend-Start"><strong>Start</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DesktopBrowserBackend-__del__"><strong>__del__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DesktopBrowserBackend-__init__"><strong>__init__</strong></a>(self, desktop_platform_backend, browser_options, executable, flash_path, is_content_shell, browser_directory, output_profile_path, extensions_to_load)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>browser_directory</strong></dt>
+</dl>
+<dl><dt><strong>log_file_path</strong></dt>
+</dl>
+<dl><dt><strong>pid</strong></dt>
+</dl>
+<dl><dt><strong>profile_directory</strong></dt>
+</dl>
+<dl><dt><strong>supports_uploading_logs</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.internal.backends.chrome.chrome_browser_backend.html#ChromeBrowserBackend">telemetry.internal.backends.chrome.chrome_browser_backend.ChromeBrowserBackend</a>:<br>
+<dl><dt><a name="DesktopBrowserBackend-DumpMemory"><strong>DumpMemory</strong></a>(self, timeout<font color="#909090">=90</font>)</dt></dl>
+
+<dl><dt><a name="DesktopBrowserBackend-GetProcessName"><strong>GetProcessName</strong></a>(self, cmd_line)</dt><dd><tt>Returns&nbsp;a&nbsp;user-friendly&nbsp;name&nbsp;for&nbsp;the&nbsp;process&nbsp;of&nbsp;the&nbsp;given&nbsp;|cmd_line|.</tt></dd></dl>
+
+<dl><dt><a name="DesktopBrowserBackend-GetReplayBrowserStartupArgs"><strong>GetReplayBrowserStartupArgs</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DesktopBrowserBackend-GetSystemInfo"><strong>GetSystemInfo</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DesktopBrowserBackend-SetMemoryPressureNotificationsSuppressed"><strong>SetMemoryPressureNotificationsSuppressed</strong></a>(self, suppressed, timeout<font color="#909090">=90</font>)</dt></dl>
+
+<dl><dt><a name="DesktopBrowserBackend-SimulateMemoryPressureNotification"><strong>SimulateMemoryPressureNotification</strong></a>(self, pressure_level, timeout<font color="#909090">=90</font>)</dt></dl>
+
+<dl><dt><a name="DesktopBrowserBackend-StartTracing"><strong>StartTracing</strong></a>(self, trace_options, custom_categories<font color="#909090">=None</font>, timeout<font color="#909090">=90</font>)</dt><dd><tt>Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;trace_options:&nbsp;An&nbsp;tracing_options.TracingOptions&nbsp;instance.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;custom_categories:&nbsp;An&nbsp;optional&nbsp;string&nbsp;containing&nbsp;a&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;comma&nbsp;separated&nbsp;categories&nbsp;that&nbsp;will&nbsp;be&nbsp;traced<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;instead&nbsp;of&nbsp;the&nbsp;default&nbsp;category&nbsp;set.&nbsp;&nbsp;Example:&nbsp;use<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"webkit,cc,disabled-by-default-cc.debug"&nbsp;to&nbsp;trace&nbsp;only<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;those&nbsp;three&nbsp;event&nbsp;categories.</tt></dd></dl>
+
+<dl><dt><a name="DesktopBrowserBackend-StopTracing"><strong>StopTracing</strong></a>(self, trace_data_builder)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.backends.chrome.chrome_browser_backend.html#ChromeBrowserBackend">telemetry.internal.backends.chrome.chrome_browser_backend.ChromeBrowserBackend</a>:<br>
+<dl><dt><strong>devtools_client</strong></dt>
+</dl>
+<dl><dt><strong>extension_backend</strong></dt>
+</dl>
+<dl><dt><strong>supports_cpu_metrics</strong></dt>
+</dl>
+<dl><dt><strong>supports_memory_dumping</strong></dt>
+</dl>
+<dl><dt><strong>supports_memory_metrics</strong></dt>
+</dl>
+<dl><dt><strong>supports_overriding_memory_pressure_notifications</strong></dt>
+</dl>
+<dl><dt><strong>supports_power_metrics</strong></dt>
+</dl>
+<dl><dt><strong>supports_system_info</strong></dt>
+</dl>
+<dl><dt><strong>supports_tab_control</strong></dt>
+</dl>
+<dl><dt><strong>supports_tracing</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.internal.backends.browser_backend.html#BrowserBackend">telemetry.internal.backends.browser_backend.BrowserBackend</a>:<br>
+<dl><dt><a name="DesktopBrowserBackend-IsAppRunning"><strong>IsAppRunning</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DesktopBrowserBackend-SetBrowser"><strong>SetBrowser</strong></a>(self, browser)</dt></dl>
+
+<dl><dt><a name="DesktopBrowserBackend-UploadLogsToCloudStorage"><strong>UploadLogsToCloudStorage</strong></a>(self)</dt><dd><tt>Uploading&nbsp;log&nbsp;files&nbsp;produce&nbsp;by&nbsp;this&nbsp;browser&nbsp;instance&nbsp;to&nbsp;cloud&nbsp;storage.<br>
+&nbsp;<br>
+Check&nbsp;supports_uploading_logs&nbsp;before&nbsp;calling&nbsp;this&nbsp;method.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.backends.browser_backend.html#BrowserBackend">telemetry.internal.backends.browser_backend.BrowserBackend</a>:<br>
+<dl><dt><strong>browser</strong></dt>
+</dl>
+<dl><dt><strong>browser_type</strong></dt>
+</dl>
+<dl><dt><strong>profiling_controller_backend</strong></dt>
+</dl>
+<dl><dt><strong>should_ignore_certificate_errors</strong></dt>
+</dl>
+<dl><dt><strong>supports_extensions</strong></dt>
+<dd><tt>True&nbsp;if&nbsp;this&nbsp;browser&nbsp;backend&nbsp;supports&nbsp;extensions.</tt></dd>
+</dl>
+<dl><dt><strong>tab_list_backend</strong></dt>
+</dl>
+<dl><dt><strong>wpr_mode</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.internal.backends.app_backend.html#AppBackend">telemetry.internal.backends.app_backend.AppBackend</a>:<br>
+<dl><dt><a name="DesktopBrowserBackend-SetApp"><strong>SetApp</strong></a>(self, app)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.backends.app_backend.html#AppBackend">telemetry.internal.backends.app_backend.AppBackend</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>app</strong></dt>
+</dl>
+<dl><dt><strong>app_type</strong></dt>
+</dl>
+<dl><dt><strong>platform_backend</strong></dt>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-ParseCrashpadDateTime"><strong>ParseCrashpadDateTime</strong></a>(date_time_str)</dt></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.desktop_browser_finder.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.desktop_browser_finder.html
new file mode 100644
index 0000000..124af91
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.desktop_browser_finder.html
@@ -0,0 +1,117 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.chrome.desktop_browser_finder</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.<a href="telemetry.internal.backends.chrome.html"><font color="#ffffff">chrome</font></a>.desktop_browser_finder</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/chrome/desktop_browser_finder.py">telemetry/internal/backends/chrome/desktop_browser_finder.py</a></font></td></tr></table>
+    <p><tt>Finds&nbsp;desktop&nbsp;browsers&nbsp;that&nbsp;can&nbsp;be&nbsp;controlled&nbsp;by&nbsp;telemetry.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.browser.browser.html">telemetry.internal.browser.browser</a><br>
+<a href="telemetry.internal.backends.chrome.desktop_browser_backend.html">telemetry.internal.backends.chrome.desktop_browser_backend</a><br>
+<a href="telemetry.internal.platform.desktop_device.html">telemetry.internal.platform.desktop_device</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+<a href="logging.html">logging</a><br>
+<a href="os.html">os</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.util.path.html">telemetry.internal.util.path</a><br>
+<a href="telemetry.core.platform.html">telemetry.core.platform</a><br>
+<a href="telemetry.internal.browser.possible_browser.html">telemetry.internal.browser.possible_browser</a><br>
+</td><td width="25%" valign=top><a href="sys.html">sys</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.browser.possible_browser.html#PossibleBrowser">telemetry.internal.browser.possible_browser.PossibleBrowser</a>(<a href="telemetry.internal.app.possible_app.html#PossibleApp">telemetry.internal.app.possible_app.PossibleApp</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome.desktop_browser_finder.html#PossibleDesktopBrowser">PossibleDesktopBrowser</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="PossibleDesktopBrowser">class <strong>PossibleDesktopBrowser</strong></a>(<a href="telemetry.internal.browser.possible_browser.html#PossibleBrowser">telemetry.internal.browser.possible_browser.PossibleBrowser</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;desktop&nbsp;browser&nbsp;that&nbsp;can&nbsp;be&nbsp;controlled.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.chrome.desktop_browser_finder.html#PossibleDesktopBrowser">PossibleDesktopBrowser</a></dd>
+<dd><a href="telemetry.internal.browser.possible_browser.html#PossibleBrowser">telemetry.internal.browser.possible_browser.PossibleBrowser</a></dd>
+<dd><a href="telemetry.internal.app.possible_app.html#PossibleApp">telemetry.internal.app.possible_app.PossibleApp</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="PossibleDesktopBrowser-Create"><strong>Create</strong></a>(self, finder_options)</dt></dl>
+
+<dl><dt><a name="PossibleDesktopBrowser-SupportsOptions"><strong>SupportsOptions</strong></a>(self, finder_options)</dt></dl>
+
+<dl><dt><a name="PossibleDesktopBrowser-UpdateExecutableIfNeeded"><strong>UpdateExecutableIfNeeded</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PossibleDesktopBrowser-__init__"><strong>__init__</strong></a>(self, browser_type, finder_options, executable, flash_path, is_content_shell, browser_directory, is_local_build<font color="#909090">=False</font>)</dt></dl>
+
+<dl><dt><a name="PossibleDesktopBrowser-__repr__"><strong>__repr__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PossibleDesktopBrowser-last_modification_time"><strong>last_modification_time</strong></a>(self)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.browser.possible_browser.html#PossibleBrowser">telemetry.internal.browser.possible_browser.PossibleBrowser</a>:<br>
+<dl><dt><a name="PossibleDesktopBrowser-IsRemote"><strong>IsRemote</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PossibleDesktopBrowser-RunRemote"><strong>RunRemote</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PossibleDesktopBrowser-SetCredentialsPath"><strong>SetCredentialsPath</strong></a>(self, credentials_path)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.browser.possible_browser.html#PossibleBrowser">telemetry.internal.browser.possible_browser.PossibleBrowser</a>:<br>
+<dl><dt><strong>browser_type</strong></dt>
+</dl>
+<dl><dt><strong>supports_tab_control</strong></dt>
+</dl>
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.app.possible_app.html#PossibleApp">telemetry.internal.app.possible_app.PossibleApp</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>app_type</strong></dt>
+</dl>
+<dl><dt><strong>platform</strong></dt>
+</dl>
+<dl><dt><strong>target_os</strong></dt>
+<dd><tt>Target&nbsp;OS,&nbsp;the&nbsp;app&nbsp;will&nbsp;run&nbsp;on.</tt></dd>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-CanFindAvailableBrowsers"><strong>CanFindAvailableBrowsers</strong></a>()</dt></dl>
+ <dl><dt><a name="-CanPossiblyHandlePath"><strong>CanPossiblyHandlePath</strong></a>(target_path)</dt></dl>
+ <dl><dt><a name="-FindAllAvailableBrowsers"><strong>FindAllAvailableBrowsers</strong></a>(finder_options, device)</dt><dd><tt>Finds&nbsp;all&nbsp;the&nbsp;desktop&nbsp;browsers&nbsp;available&nbsp;on&nbsp;this&nbsp;machine.</tt></dd></dl>
+ <dl><dt><a name="-FindAllBrowserTypes"><strong>FindAllBrowserTypes</strong></a>(_)</dt></dl>
+ <dl><dt><a name="-SelectDefaultBrowser"><strong>SelectDefaultBrowser</strong></a>(possible_browsers)</dt></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.extension_backend.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.extension_backend.html
new file mode 100644
index 0000000..ca4bb7f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.extension_backend.html
@@ -0,0 +1,221 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.chrome.extension_backend</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.<a href="telemetry.internal.backends.chrome.html"><font color="#ffffff">chrome</font></a>.extension_backend</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/chrome/extension_backend.py">telemetry/internal/backends/chrome/extension_backend.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="collections.html">collections</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.browser.extension_page.html">telemetry.internal.browser.extension_page</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.backends.chrome_inspector.inspector_backend_list.html">telemetry.internal.backends.chrome_inspector.inspector_backend_list</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="_abcoll.html#Mapping">_abcoll.Mapping</a>(<a href="_abcoll.html#Sized">_abcoll.Sized</a>, <a href="_abcoll.html#Iterable">_abcoll.Iterable</a>, <a href="_abcoll.html#Container">_abcoll.Container</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome.extension_backend.html#ExtensionBackendDict">ExtensionBackendDict</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome_inspector.inspector_backend_list.html#InspectorBackendList">telemetry.internal.backends.chrome_inspector.inspector_backend_list.InspectorBackendList</a>(<a href="_abcoll.html#Sequence">_abcoll.Sequence</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome.extension_backend.html#ExtensionBackendList">ExtensionBackendList</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ExtensionBackendDict">class <strong>ExtensionBackendDict</strong></a>(<a href="_abcoll.html#Mapping">_abcoll.Mapping</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;dynamic&nbsp;mapping&nbsp;of&nbsp;extension_id&nbsp;to&nbsp;extension_page.ExtensionPages.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.chrome.extension_backend.html#ExtensionBackendDict">ExtensionBackendDict</a></dd>
+<dd><a href="_abcoll.html#Mapping">_abcoll.Mapping</a></dd>
+<dd><a href="_abcoll.html#Sized">_abcoll.Sized</a></dd>
+<dd><a href="_abcoll.html#Iterable">_abcoll.Iterable</a></dd>
+<dd><a href="_abcoll.html#Container">_abcoll.Container</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="ExtensionBackendDict-ContextIdToExtensionId"><strong>ContextIdToExtensionId</strong></a>(self, context_id)</dt></dl>
+
+<dl><dt><a name="ExtensionBackendDict-__getitem__"><strong>__getitem__</strong></a>(self, extension_id)</dt></dl>
+
+<dl><dt><a name="ExtensionBackendDict-__init__"><strong>__init__</strong></a>(self, browser_backend)</dt></dl>
+
+<dl><dt><a name="ExtensionBackendDict-__iter__"><strong>__iter__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="ExtensionBackendDict-__len__"><strong>__len__</strong></a>(self)</dt></dl>
+
+<hr>
+Data and other attributes defined here:<br>
+<dl><dt><strong>__abstractmethods__</strong> = frozenset([])</dl>
+
+<hr>
+Methods inherited from <a href="_abcoll.html#Mapping">_abcoll.Mapping</a>:<br>
+<dl><dt><a name="ExtensionBackendDict-__contains__"><strong>__contains__</strong></a>(self, key)</dt></dl>
+
+<dl><dt><a name="ExtensionBackendDict-__eq__"><strong>__eq__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="ExtensionBackendDict-__ne__"><strong>__ne__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="ExtensionBackendDict-get"><strong>get</strong></a>(self, key, default<font color="#909090">=None</font>)</dt><dd><tt>D.<a href="#ExtensionBackendDict-get">get</a>(k[,d])&nbsp;-&gt;&nbsp;D[k]&nbsp;if&nbsp;k&nbsp;in&nbsp;D,&nbsp;else&nbsp;d.&nbsp;&nbsp;d&nbsp;defaults&nbsp;to&nbsp;None.</tt></dd></dl>
+
+<dl><dt><a name="ExtensionBackendDict-items"><strong>items</strong></a>(self)</dt><dd><tt>D.<a href="#ExtensionBackendDict-items">items</a>()&nbsp;-&gt;&nbsp;list&nbsp;of&nbsp;D's&nbsp;(key,&nbsp;value)&nbsp;pairs,&nbsp;as&nbsp;2-tuples</tt></dd></dl>
+
+<dl><dt><a name="ExtensionBackendDict-iteritems"><strong>iteritems</strong></a>(self)</dt><dd><tt>D.<a href="#ExtensionBackendDict-iteritems">iteritems</a>()&nbsp;-&gt;&nbsp;an&nbsp;iterator&nbsp;over&nbsp;the&nbsp;(key,&nbsp;value)&nbsp;items&nbsp;of&nbsp;D</tt></dd></dl>
+
+<dl><dt><a name="ExtensionBackendDict-iterkeys"><strong>iterkeys</strong></a>(self)</dt><dd><tt>D.<a href="#ExtensionBackendDict-iterkeys">iterkeys</a>()&nbsp;-&gt;&nbsp;an&nbsp;iterator&nbsp;over&nbsp;the&nbsp;keys&nbsp;of&nbsp;D</tt></dd></dl>
+
+<dl><dt><a name="ExtensionBackendDict-itervalues"><strong>itervalues</strong></a>(self)</dt><dd><tt>D.<a href="#ExtensionBackendDict-itervalues">itervalues</a>()&nbsp;-&gt;&nbsp;an&nbsp;iterator&nbsp;over&nbsp;the&nbsp;values&nbsp;of&nbsp;D</tt></dd></dl>
+
+<dl><dt><a name="ExtensionBackendDict-keys"><strong>keys</strong></a>(self)</dt><dd><tt>D.<a href="#ExtensionBackendDict-keys">keys</a>()&nbsp;-&gt;&nbsp;list&nbsp;of&nbsp;D's&nbsp;keys</tt></dd></dl>
+
+<dl><dt><a name="ExtensionBackendDict-values"><strong>values</strong></a>(self)</dt><dd><tt>D.<a href="#ExtensionBackendDict-values">values</a>()&nbsp;-&gt;&nbsp;list&nbsp;of&nbsp;D's&nbsp;values</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="_abcoll.html#Mapping">_abcoll.Mapping</a>:<br>
+<dl><dt><strong>__hash__</strong> = None</dl>
+
+<hr>
+Class methods inherited from <a href="_abcoll.html#Sized">_abcoll.Sized</a>:<br>
+<dl><dt><a name="ExtensionBackendDict-__subclasshook__"><strong>__subclasshook__</strong></a>(cls, C)<font color="#909090"><font face="helvetica, arial"> from <a href="abc.html#ABCMeta">abc.ABCMeta</a></font></font></dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="_abcoll.html#Sized">_abcoll.Sized</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="_abcoll.html#Sized">_abcoll.Sized</a>:<br>
+<dl><dt><strong>__metaclass__</strong> = &lt;class 'abc.ABCMeta'&gt;<dd><tt>Metaclass&nbsp;for&nbsp;defining&nbsp;Abstract&nbsp;Base&nbsp;Classes&nbsp;(ABCs).<br>
+&nbsp;<br>
+Use&nbsp;this&nbsp;metaclass&nbsp;to&nbsp;create&nbsp;an&nbsp;ABC.&nbsp;&nbsp;An&nbsp;ABC&nbsp;can&nbsp;be&nbsp;subclassed<br>
+directly,&nbsp;and&nbsp;then&nbsp;acts&nbsp;as&nbsp;a&nbsp;mix-in&nbsp;class.&nbsp;&nbsp;You&nbsp;can&nbsp;also&nbsp;register<br>
+unrelated&nbsp;concrete&nbsp;classes&nbsp;(even&nbsp;built-in&nbsp;classes)&nbsp;and&nbsp;unrelated<br>
+ABCs&nbsp;as&nbsp;'virtual&nbsp;subclasses'&nbsp;--&nbsp;these&nbsp;and&nbsp;their&nbsp;descendants&nbsp;will<br>
+be&nbsp;considered&nbsp;subclasses&nbsp;of&nbsp;the&nbsp;registering&nbsp;ABC&nbsp;by&nbsp;the&nbsp;built-in<br>
+issubclass()&nbsp;function,&nbsp;but&nbsp;the&nbsp;registering&nbsp;ABC&nbsp;won't&nbsp;show&nbsp;up&nbsp;in<br>
+their&nbsp;MRO&nbsp;(Method&nbsp;Resolution&nbsp;Order)&nbsp;nor&nbsp;will&nbsp;method<br>
+implementations&nbsp;defined&nbsp;by&nbsp;the&nbsp;registering&nbsp;ABC&nbsp;be&nbsp;callable&nbsp;(not<br>
+even&nbsp;via&nbsp;super()).</tt></dl>
+
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ExtensionBackendList">class <strong>ExtensionBackendList</strong></a>(<a href="telemetry.internal.backends.chrome_inspector.inspector_backend_list.html#InspectorBackendList">telemetry.internal.backends.chrome_inspector.inspector_backend_list.InspectorBackendList</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;dynamic&nbsp;sequence&nbsp;of&nbsp;extension_page.ExtensionPages.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.chrome.extension_backend.html#ExtensionBackendList">ExtensionBackendList</a></dd>
+<dd><a href="telemetry.internal.backends.chrome_inspector.inspector_backend_list.html#InspectorBackendList">telemetry.internal.backends.chrome_inspector.inspector_backend_list.InspectorBackendList</a></dd>
+<dd><a href="_abcoll.html#Sequence">_abcoll.Sequence</a></dd>
+<dd><a href="_abcoll.html#Sized">_abcoll.Sized</a></dd>
+<dd><a href="_abcoll.html#Iterable">_abcoll.Iterable</a></dd>
+<dd><a href="_abcoll.html#Container">_abcoll.Container</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="ExtensionBackendList-CreateWrapper"><strong>CreateWrapper</strong></a>(self, inspector_backend)</dt></dl>
+
+<dl><dt><a name="ExtensionBackendList-ShouldIncludeContext"><strong>ShouldIncludeContext</strong></a>(self, context)</dt></dl>
+
+<dl><dt><a name="ExtensionBackendList-__init__"><strong>__init__</strong></a>(self, browser_backend)</dt></dl>
+
+<hr>
+Data and other attributes defined here:<br>
+<dl><dt><strong>__abstractmethods__</strong> = frozenset([])</dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.backends.chrome_inspector.inspector_backend_list.html#InspectorBackendList">telemetry.internal.backends.chrome_inspector.inspector_backend_list.InspectorBackendList</a>:<br>
+<dl><dt><a name="ExtensionBackendList-GetBackendFromContextId"><strong>GetBackendFromContextId</strong></a>(self, context_id)</dt></dl>
+
+<dl><dt><a name="ExtensionBackendList-GetContextInfo"><strong>GetContextInfo</strong></a>(self, context_id)</dt></dl>
+
+<dl><dt><a name="ExtensionBackendList-GetTabById"><strong>GetTabById</strong></a>(self, identifier)</dt></dl>
+
+<dl><dt><a name="ExtensionBackendList-IterContextIds"><strong>IterContextIds</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="ExtensionBackendList-__getitem__"><strong>__getitem__</strong></a>(self, index)</dt><dd><tt>#&nbsp;TODO(nednguyen):&nbsp;Remove&nbsp;this&nbsp;method&nbsp;and&nbsp;turn&nbsp;inspector_backend_list&nbsp;API&nbsp;to<br>
+#&nbsp;dictionary-like&nbsp;API&nbsp;(crbug.com/398467)</tt></dd></dl>
+
+<dl><dt><a name="ExtensionBackendList-__iter__"><strong>__iter__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="ExtensionBackendList-__len__"><strong>__len__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.backends.chrome_inspector.inspector_backend_list.html#InspectorBackendList">telemetry.internal.backends.chrome_inspector.inspector_backend_list.InspectorBackendList</a>:<br>
+<dl><dt><strong>app</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="_abcoll.html#Sequence">_abcoll.Sequence</a>:<br>
+<dl><dt><a name="ExtensionBackendList-__contains__"><strong>__contains__</strong></a>(self, value)</dt></dl>
+
+<dl><dt><a name="ExtensionBackendList-__reversed__"><strong>__reversed__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="ExtensionBackendList-count"><strong>count</strong></a>(self, value)</dt><dd><tt>S.<a href="#ExtensionBackendList-count">count</a>(value)&nbsp;-&gt;&nbsp;integer&nbsp;--&nbsp;return&nbsp;number&nbsp;of&nbsp;occurrences&nbsp;of&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="ExtensionBackendList-index"><strong>index</strong></a>(self, value)</dt><dd><tt>S.<a href="#ExtensionBackendList-index">index</a>(value)&nbsp;-&gt;&nbsp;integer&nbsp;--&nbsp;return&nbsp;first&nbsp;index&nbsp;of&nbsp;value.<br>
+Raises&nbsp;ValueError&nbsp;if&nbsp;the&nbsp;value&nbsp;is&nbsp;not&nbsp;present.</tt></dd></dl>
+
+<hr>
+Class methods inherited from <a href="_abcoll.html#Sized">_abcoll.Sized</a>:<br>
+<dl><dt><a name="ExtensionBackendList-__subclasshook__"><strong>__subclasshook__</strong></a>(cls, C)<font color="#909090"><font face="helvetica, arial"> from <a href="abc.html#ABCMeta">abc.ABCMeta</a></font></font></dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="_abcoll.html#Sized">_abcoll.Sized</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="_abcoll.html#Sized">_abcoll.Sized</a>:<br>
+<dl><dt><strong>__metaclass__</strong> = &lt;class 'abc.ABCMeta'&gt;<dd><tt>Metaclass&nbsp;for&nbsp;defining&nbsp;Abstract&nbsp;Base&nbsp;Classes&nbsp;(ABCs).<br>
+&nbsp;<br>
+Use&nbsp;this&nbsp;metaclass&nbsp;to&nbsp;create&nbsp;an&nbsp;ABC.&nbsp;&nbsp;An&nbsp;ABC&nbsp;can&nbsp;be&nbsp;subclassed<br>
+directly,&nbsp;and&nbsp;then&nbsp;acts&nbsp;as&nbsp;a&nbsp;mix-in&nbsp;class.&nbsp;&nbsp;You&nbsp;can&nbsp;also&nbsp;register<br>
+unrelated&nbsp;concrete&nbsp;classes&nbsp;(even&nbsp;built-in&nbsp;classes)&nbsp;and&nbsp;unrelated<br>
+ABCs&nbsp;as&nbsp;'virtual&nbsp;subclasses'&nbsp;--&nbsp;these&nbsp;and&nbsp;their&nbsp;descendants&nbsp;will<br>
+be&nbsp;considered&nbsp;subclasses&nbsp;of&nbsp;the&nbsp;registering&nbsp;ABC&nbsp;by&nbsp;the&nbsp;built-in<br>
+issubclass()&nbsp;function,&nbsp;but&nbsp;the&nbsp;registering&nbsp;ABC&nbsp;won't&nbsp;show&nbsp;up&nbsp;in<br>
+their&nbsp;MRO&nbsp;(Method&nbsp;Resolution&nbsp;Order)&nbsp;nor&nbsp;will&nbsp;method<br>
+implementations&nbsp;defined&nbsp;by&nbsp;the&nbsp;registering&nbsp;ABC&nbsp;be&nbsp;callable&nbsp;(not<br>
+even&nbsp;via&nbsp;super()).</tt></dl>
+
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.html
new file mode 100644
index 0000000..3af1eec
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.html
@@ -0,0 +1,49 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: package telemetry.internal.backends.chrome</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.chrome</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/chrome/__init__.py">telemetry/internal/backends/chrome/__init__.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Package Contents</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.backends.chrome.android_browser_backend.html">android_browser_backend</a><br>
+<a href="telemetry.internal.backends.chrome.android_browser_finder.html">android_browser_finder</a><br>
+<a href="telemetry.internal.backends.chrome.android_browser_finder_unittest.html">android_browser_finder_unittest</a><br>
+<a href="telemetry.internal.backends.chrome.chrome_browser_backend.html">chrome_browser_backend</a><br>
+<a href="telemetry.internal.backends.chrome.chrome_browser_backend_unittest.html">chrome_browser_backend_unittest</a><br>
+<a href="telemetry.internal.backends.chrome.cros_browser_backend.html">cros_browser_backend</a><br>
+<a href="telemetry.internal.backends.chrome.cros_browser_finder.html">cros_browser_finder</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.backends.chrome.cros_browser_finder_unittest.html">cros_browser_finder_unittest</a><br>
+<a href="telemetry.internal.backends.chrome.cros_browser_with_oobe.html">cros_browser_with_oobe</a><br>
+<a href="telemetry.internal.backends.chrome.cros_test_case.html">cros_test_case</a><br>
+<a href="telemetry.internal.backends.chrome.cros_unittest.html">cros_unittest</a><br>
+<a href="telemetry.internal.backends.chrome.crx_id.html">crx_id</a><br>
+<a href="telemetry.internal.backends.chrome.crx_id_unittest.html">crx_id_unittest</a><br>
+<a href="telemetry.internal.backends.chrome.desktop_browser_backend.html">desktop_browser_backend</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.backends.chrome.desktop_browser_finder.html">desktop_browser_finder</a><br>
+<a href="telemetry.internal.backends.chrome.desktop_browser_finder_unittest.html">desktop_browser_finder_unittest</a><br>
+<a href="telemetry.internal.backends.chrome.extension_backend.html">extension_backend</a><br>
+<a href="telemetry.internal.backends.chrome.ios_browser_backend.html">ios_browser_backend</a><br>
+<a href="telemetry.internal.backends.chrome.ios_browser_finder.html">ios_browser_finder</a><br>
+<a href="telemetry.internal.backends.chrome.ios_browser_finder_unittest.html">ios_browser_finder_unittest</a><br>
+<a href="telemetry.internal.backends.chrome.misc_web_contents_backend.html">misc_web_contents_backend</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.backends.chrome.oobe.html">oobe</a><br>
+<a href="telemetry.internal.backends.chrome.system_info_backend.html">system_info_backend</a><br>
+<a href="telemetry.internal.backends.chrome.tab_list_backend.html">tab_list_backend</a><br>
+<a href="telemetry.internal.backends.chrome.tab_list_backend_unittest.html">tab_list_backend_unittest</a><br>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.ios_browser_backend.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.ios_browser_backend.html
new file mode 100644
index 0000000..f798df7
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.ios_browser_backend.html
@@ -0,0 +1,192 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.chrome.ios_browser_backend</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.<a href="telemetry.internal.backends.chrome.html"><font color="#ffffff">chrome</font></a>.ios_browser_backend</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/chrome/ios_browser_backend.py">telemetry/internal/backends/chrome/ios_browser_backend.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.backends.chrome.chrome_browser_backend.html">telemetry.internal.backends.chrome.chrome_browser_backend</a><br>
+<a href="contextlib.html">contextlib</a><br>
+<a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+</td><td width="25%" valign=top><a href="json.html">json</a><br>
+<a href="logging.html">logging</a><br>
+<a href="re.html">re</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.backends.chrome.system_info_backend.html">telemetry.internal.backends.chrome.system_info_backend</a><br>
+<a href="urllib2.html">urllib2</a><br>
+<a href="telemetry.core.util.html">telemetry.core.util</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome.chrome_browser_backend.html#ChromeBrowserBackend">telemetry.internal.backends.chrome.chrome_browser_backend.ChromeBrowserBackend</a>(<a href="telemetry.internal.backends.browser_backend.html#BrowserBackend">telemetry.internal.backends.browser_backend.BrowserBackend</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome.ios_browser_backend.html#IosBrowserBackend">IosBrowserBackend</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="IosBrowserBackend">class <strong>IosBrowserBackend</strong></a>(<a href="telemetry.internal.backends.chrome.chrome_browser_backend.html#ChromeBrowserBackend">telemetry.internal.backends.chrome.chrome_browser_backend.ChromeBrowserBackend</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.chrome.ios_browser_backend.html#IosBrowserBackend">IosBrowserBackend</a></dd>
+<dd><a href="telemetry.internal.backends.chrome.chrome_browser_backend.html#ChromeBrowserBackend">telemetry.internal.backends.chrome.chrome_browser_backend.ChromeBrowserBackend</a></dd>
+<dd><a href="telemetry.internal.backends.browser_backend.html#BrowserBackend">telemetry.internal.backends.browser_backend.BrowserBackend</a></dd>
+<dd><a href="telemetry.internal.backends.app_backend.html#AppBackend">telemetry.internal.backends.app_backend.AppBackend</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="IosBrowserBackend-GetBrowserStartupArgs"><strong>GetBrowserStartupArgs</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="IosBrowserBackend-GetDeviceUrls"><strong>GetDeviceUrls</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="IosBrowserBackend-GetStackTrace"><strong>GetStackTrace</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="IosBrowserBackend-GetStandardOutput"><strong>GetStandardOutput</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="IosBrowserBackend-GetSystemInfo"><strong>GetSystemInfo</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="IosBrowserBackend-GetWebSocketDebuggerUrls"><strong>GetWebSocketDebuggerUrls</strong></a>(self, device_urls)</dt><dd><tt>Get&nbsp;a&nbsp;list&nbsp;of&nbsp;the&nbsp;websocket&nbsp;debugger&nbsp;URLs&nbsp;to&nbsp;communicate&nbsp;with<br>
+all&nbsp;running&nbsp;UIWebViews.</tt></dd></dl>
+
+<dl><dt><a name="IosBrowserBackend-HasBrowserFinishedLaunching"><strong>HasBrowserFinishedLaunching</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="IosBrowserBackend-IsBrowserRunning"><strong>IsBrowserRunning</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="IosBrowserBackend-Start"><strong>Start</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="IosBrowserBackend-UpdateRunningBrowsersInfo"><strong>UpdateRunningBrowsersInfo</strong></a>(self)</dt><dd><tt>Refresh&nbsp;to&nbsp;match&nbsp;current&nbsp;state&nbsp;of&nbsp;the&nbsp;running&nbsp;browser.</tt></dd></dl>
+
+<dl><dt><a name="IosBrowserBackend-__init__"><strong>__init__</strong></a>(self, ios_platform_backend, browser_options)</dt></dl>
+
+<dl><dt><a name="IosBrowserBackend-extension_backend"><strong>extension_backend</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>browser_directory</strong></dt>
+</dl>
+<dl><dt><strong>profile_directory</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.internal.backends.chrome.chrome_browser_backend.html#ChromeBrowserBackend">telemetry.internal.backends.chrome.chrome_browser_backend.ChromeBrowserBackend</a>:<br>
+<dl><dt><a name="IosBrowserBackend-Close"><strong>Close</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="IosBrowserBackend-DumpMemory"><strong>DumpMemory</strong></a>(self, timeout<font color="#909090">=90</font>)</dt></dl>
+
+<dl><dt><a name="IosBrowserBackend-GetProcessName"><strong>GetProcessName</strong></a>(self, cmd_line)</dt><dd><tt>Returns&nbsp;a&nbsp;user-friendly&nbsp;name&nbsp;for&nbsp;the&nbsp;process&nbsp;of&nbsp;the&nbsp;given&nbsp;|cmd_line|.</tt></dd></dl>
+
+<dl><dt><a name="IosBrowserBackend-GetReplayBrowserStartupArgs"><strong>GetReplayBrowserStartupArgs</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="IosBrowserBackend-SetMemoryPressureNotificationsSuppressed"><strong>SetMemoryPressureNotificationsSuppressed</strong></a>(self, suppressed, timeout<font color="#909090">=90</font>)</dt></dl>
+
+<dl><dt><a name="IosBrowserBackend-SimulateMemoryPressureNotification"><strong>SimulateMemoryPressureNotification</strong></a>(self, pressure_level, timeout<font color="#909090">=90</font>)</dt></dl>
+
+<dl><dt><a name="IosBrowserBackend-StartTracing"><strong>StartTracing</strong></a>(self, trace_options, custom_categories<font color="#909090">=None</font>, timeout<font color="#909090">=90</font>)</dt><dd><tt>Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;trace_options:&nbsp;An&nbsp;tracing_options.TracingOptions&nbsp;instance.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;custom_categories:&nbsp;An&nbsp;optional&nbsp;string&nbsp;containing&nbsp;a&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;comma&nbsp;separated&nbsp;categories&nbsp;that&nbsp;will&nbsp;be&nbsp;traced<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;instead&nbsp;of&nbsp;the&nbsp;default&nbsp;category&nbsp;set.&nbsp;&nbsp;Example:&nbsp;use<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"webkit,cc,disabled-by-default-cc.debug"&nbsp;to&nbsp;trace&nbsp;only<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;those&nbsp;three&nbsp;event&nbsp;categories.</tt></dd></dl>
+
+<dl><dt><a name="IosBrowserBackend-StopTracing"><strong>StopTracing</strong></a>(self, trace_data_builder)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.backends.chrome.chrome_browser_backend.html#ChromeBrowserBackend">telemetry.internal.backends.chrome.chrome_browser_backend.ChromeBrowserBackend</a>:<br>
+<dl><dt><strong>devtools_client</strong></dt>
+</dl>
+<dl><dt><strong>supports_cpu_metrics</strong></dt>
+</dl>
+<dl><dt><strong>supports_memory_dumping</strong></dt>
+</dl>
+<dl><dt><strong>supports_memory_metrics</strong></dt>
+</dl>
+<dl><dt><strong>supports_overriding_memory_pressure_notifications</strong></dt>
+</dl>
+<dl><dt><strong>supports_power_metrics</strong></dt>
+</dl>
+<dl><dt><strong>supports_system_info</strong></dt>
+</dl>
+<dl><dt><strong>supports_tab_control</strong></dt>
+</dl>
+<dl><dt><strong>supports_tracing</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.internal.backends.browser_backend.html#BrowserBackend">telemetry.internal.backends.browser_backend.BrowserBackend</a>:<br>
+<dl><dt><a name="IosBrowserBackend-IsAppRunning"><strong>IsAppRunning</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="IosBrowserBackend-SetBrowser"><strong>SetBrowser</strong></a>(self, browser)</dt></dl>
+
+<dl><dt><a name="IosBrowserBackend-UploadLogsToCloudStorage"><strong>UploadLogsToCloudStorage</strong></a>(self)</dt><dd><tt>Uploading&nbsp;log&nbsp;files&nbsp;produce&nbsp;by&nbsp;this&nbsp;browser&nbsp;instance&nbsp;to&nbsp;cloud&nbsp;storage.<br>
+&nbsp;<br>
+Check&nbsp;supports_uploading_logs&nbsp;before&nbsp;calling&nbsp;this&nbsp;method.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.backends.browser_backend.html#BrowserBackend">telemetry.internal.backends.browser_backend.BrowserBackend</a>:<br>
+<dl><dt><strong>browser</strong></dt>
+</dl>
+<dl><dt><strong>browser_type</strong></dt>
+</dl>
+<dl><dt><strong>log_file_path</strong></dt>
+</dl>
+<dl><dt><strong>profiling_controller_backend</strong></dt>
+</dl>
+<dl><dt><strong>should_ignore_certificate_errors</strong></dt>
+</dl>
+<dl><dt><strong>supports_extensions</strong></dt>
+<dd><tt>True&nbsp;if&nbsp;this&nbsp;browser&nbsp;backend&nbsp;supports&nbsp;extensions.</tt></dd>
+</dl>
+<dl><dt><strong>supports_uploading_logs</strong></dt>
+</dl>
+<dl><dt><strong>tab_list_backend</strong></dt>
+</dl>
+<dl><dt><strong>wpr_mode</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.internal.backends.app_backend.html#AppBackend">telemetry.internal.backends.app_backend.AppBackend</a>:<br>
+<dl><dt><a name="IosBrowserBackend-SetApp"><strong>SetApp</strong></a>(self, app)</dt></dl>
+
+<dl><dt><a name="IosBrowserBackend-__del__"><strong>__del__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.backends.app_backend.html#AppBackend">telemetry.internal.backends.app_backend.AppBackend</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>app</strong></dt>
+</dl>
+<dl><dt><strong>app_type</strong></dt>
+</dl>
+<dl><dt><strong>pid</strong></dt>
+</dl>
+<dl><dt><strong>platform_backend</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.ios_browser_finder.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.ios_browser_finder.html
new file mode 100644
index 0000000..d8dc94c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.ios_browser_finder.html
@@ -0,0 +1,124 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.chrome.ios_browser_finder</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.<a href="telemetry.internal.backends.chrome.html"><font color="#ffffff">chrome</font></a>.ios_browser_finder</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/chrome/ios_browser_finder.py">telemetry/internal/backends/chrome/ios_browser_finder.py</a></font></td></tr></table>
+    <p><tt>Finds&nbsp;iOS&nbsp;browsers&nbsp;that&nbsp;can&nbsp;be&nbsp;controlled&nbsp;by&nbsp;telemetry.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.browser.browser.html">telemetry.internal.browser.browser</a><br>
+<a href="telemetry.internal.backends.chrome_inspector.inspector_backend.html">telemetry.internal.backends.chrome_inspector.inspector_backend</a><br>
+<a href="telemetry.internal.backends.chrome.ios_browser_backend.html">telemetry.internal.backends.chrome.ios_browser_backend</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.ios_device.html">telemetry.internal.platform.ios_device</a><br>
+<a href="telemetry.internal.platform.ios_platform_backend.html">telemetry.internal.platform.ios_platform_backend</a><br>
+<a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.platform.html">telemetry.core.platform</a><br>
+<a href="telemetry.internal.browser.possible_browser.html">telemetry.internal.browser.possible_browser</a><br>
+<a href="re.html">re</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.browser.possible_browser.html#PossibleBrowser">telemetry.internal.browser.possible_browser.PossibleBrowser</a>(<a href="telemetry.internal.app.possible_app.html#PossibleApp">telemetry.internal.app.possible_app.PossibleApp</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome.ios_browser_finder.html#PossibleIOSBrowser">PossibleIOSBrowser</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="PossibleIOSBrowser">class <strong>PossibleIOSBrowser</strong></a>(<a href="telemetry.internal.browser.possible_browser.html#PossibleBrowser">telemetry.internal.browser.possible_browser.PossibleBrowser</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;running&nbsp;iOS&nbsp;browser&nbsp;instance.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.chrome.ios_browser_finder.html#PossibleIOSBrowser">PossibleIOSBrowser</a></dd>
+<dd><a href="telemetry.internal.browser.possible_browser.html#PossibleBrowser">telemetry.internal.browser.possible_browser.PossibleBrowser</a></dd>
+<dd><a href="telemetry.internal.app.possible_app.html#PossibleApp">telemetry.internal.app.possible_app.PossibleApp</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="PossibleIOSBrowser-Create"><strong>Create</strong></a>(self, finder_options)</dt><dd><tt>#&nbsp;TODO(baxley):&nbsp;Implement&nbsp;the&nbsp;following&nbsp;methods&nbsp;for&nbsp;iOS.</tt></dd></dl>
+
+<dl><dt><a name="PossibleIOSBrowser-SupportsOptions"><strong>SupportsOptions</strong></a>(self, finder_options)</dt></dl>
+
+<dl><dt><a name="PossibleIOSBrowser-UpdateExecutableIfNeeded"><strong>UpdateExecutableIfNeeded</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PossibleIOSBrowser-__init__"><strong>__init__</strong></a>(self, browser_type, _)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.browser.possible_browser.html#PossibleBrowser">telemetry.internal.browser.possible_browser.PossibleBrowser</a>:<br>
+<dl><dt><a name="PossibleIOSBrowser-IsRemote"><strong>IsRemote</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PossibleIOSBrowser-RunRemote"><strong>RunRemote</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PossibleIOSBrowser-SetCredentialsPath"><strong>SetCredentialsPath</strong></a>(self, credentials_path)</dt></dl>
+
+<dl><dt><a name="PossibleIOSBrowser-__repr__"><strong>__repr__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PossibleIOSBrowser-last_modification_time"><strong>last_modification_time</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.browser.possible_browser.html#PossibleBrowser">telemetry.internal.browser.possible_browser.PossibleBrowser</a>:<br>
+<dl><dt><strong>browser_type</strong></dt>
+</dl>
+<dl><dt><strong>supports_tab_control</strong></dt>
+</dl>
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.app.possible_app.html#PossibleApp">telemetry.internal.app.possible_app.PossibleApp</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>app_type</strong></dt>
+</dl>
+<dl><dt><strong>platform</strong></dt>
+</dl>
+<dl><dt><strong>target_os</strong></dt>
+<dd><tt>Target&nbsp;OS,&nbsp;the&nbsp;app&nbsp;will&nbsp;run&nbsp;on.</tt></dd>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-CanFindAvailableBrowsers"><strong>CanFindAvailableBrowsers</strong></a>()</dt></dl>
+ <dl><dt><a name="-FindAllAvailableBrowsers"><strong>FindAllAvailableBrowsers</strong></a>(finder_options, device)</dt><dd><tt>Find&nbsp;all&nbsp;running&nbsp;iOS&nbsp;browsers&nbsp;on&nbsp;connected&nbsp;devices.</tt></dd></dl>
+ <dl><dt><a name="-FindAllBrowserTypes"><strong>FindAllBrowserTypes</strong></a>(_)</dt></dl>
+ <dl><dt><a name="-SelectDefaultBrowser"><strong>SelectDefaultBrowser</strong></a>(_)</dt></dl>
+</td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>DEVICE_LIST_URL</strong> = 'http://127.0.0.1:9221/json'<br>
+<strong>IOS_BROWSERS</strong> = {'CriOS': 'ios-chrome', 'Version': 'ios-safari'}<br>
+<strong>IOS_WEBKIT_DEBUG_PROXY</strong> = 'ios_webkit_debug_proxy'</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.misc_web_contents_backend.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.misc_web_contents_backend.html
new file mode 100644
index 0000000..b3066c7
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.misc_web_contents_backend.html
@@ -0,0 +1,139 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.chrome.misc_web_contents_backend</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.<a href="telemetry.internal.backends.chrome.html"><font color="#ffffff">chrome</font></a>.misc_web_contents_backend</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/chrome/misc_web_contents_backend.py">telemetry/internal/backends/chrome/misc_web_contents_backend.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.backends.chrome_inspector.inspector_backend_list.html">telemetry.internal.backends.chrome_inspector.inspector_backend_list</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.backends.chrome.oobe.html">telemetry.internal.backends.chrome.oobe</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome_inspector.inspector_backend_list.html#InspectorBackendList">telemetry.internal.backends.chrome_inspector.inspector_backend_list.InspectorBackendList</a>(<a href="_abcoll.html#Sequence">_abcoll.Sequence</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome.misc_web_contents_backend.html#MiscWebContentsBackend">MiscWebContentsBackend</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="MiscWebContentsBackend">class <strong>MiscWebContentsBackend</strong></a>(<a href="telemetry.internal.backends.chrome_inspector.inspector_backend_list.html#InspectorBackendList">telemetry.internal.backends.chrome_inspector.inspector_backend_list.InspectorBackendList</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;dynamic&nbsp;sequence&nbsp;of&nbsp;web&nbsp;contents&nbsp;not&nbsp;related&nbsp;to&nbsp;tabs&nbsp;and&nbsp;extensions.<br>
+&nbsp;<br>
+Provides&nbsp;acccess&nbsp;to&nbsp;chrome://oobe/login&nbsp;page.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.chrome.misc_web_contents_backend.html#MiscWebContentsBackend">MiscWebContentsBackend</a></dd>
+<dd><a href="telemetry.internal.backends.chrome_inspector.inspector_backend_list.html#InspectorBackendList">telemetry.internal.backends.chrome_inspector.inspector_backend_list.InspectorBackendList</a></dd>
+<dd><a href="_abcoll.html#Sequence">_abcoll.Sequence</a></dd>
+<dd><a href="_abcoll.html#Sized">_abcoll.Sized</a></dd>
+<dd><a href="_abcoll.html#Iterable">_abcoll.Iterable</a></dd>
+<dd><a href="_abcoll.html#Container">_abcoll.Container</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="MiscWebContentsBackend-CreateWrapper"><strong>CreateWrapper</strong></a>(self, inspector_backend)</dt></dl>
+
+<dl><dt><a name="MiscWebContentsBackend-GetOobe"><strong>GetOobe</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MiscWebContentsBackend-ShouldIncludeContext"><strong>ShouldIncludeContext</strong></a>(self, context)</dt></dl>
+
+<dl><dt><a name="MiscWebContentsBackend-__init__"><strong>__init__</strong></a>(self, browser_backend)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>oobe_exists</strong></dt>
+<dd><tt>Lightweight&nbsp;property&nbsp;to&nbsp;determine&nbsp;if&nbsp;the&nbsp;oobe&nbsp;webui&nbsp;is&nbsp;visible.</tt></dd>
+</dl>
+<hr>
+Data and other attributes defined here:<br>
+<dl><dt><strong>__abstractmethods__</strong> = frozenset([])</dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.backends.chrome_inspector.inspector_backend_list.html#InspectorBackendList">telemetry.internal.backends.chrome_inspector.inspector_backend_list.InspectorBackendList</a>:<br>
+<dl><dt><a name="MiscWebContentsBackend-GetBackendFromContextId"><strong>GetBackendFromContextId</strong></a>(self, context_id)</dt></dl>
+
+<dl><dt><a name="MiscWebContentsBackend-GetContextInfo"><strong>GetContextInfo</strong></a>(self, context_id)</dt></dl>
+
+<dl><dt><a name="MiscWebContentsBackend-GetTabById"><strong>GetTabById</strong></a>(self, identifier)</dt></dl>
+
+<dl><dt><a name="MiscWebContentsBackend-IterContextIds"><strong>IterContextIds</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MiscWebContentsBackend-__getitem__"><strong>__getitem__</strong></a>(self, index)</dt><dd><tt>#&nbsp;TODO(nednguyen):&nbsp;Remove&nbsp;this&nbsp;method&nbsp;and&nbsp;turn&nbsp;inspector_backend_list&nbsp;API&nbsp;to<br>
+#&nbsp;dictionary-like&nbsp;API&nbsp;(crbug.com/398467)</tt></dd></dl>
+
+<dl><dt><a name="MiscWebContentsBackend-__iter__"><strong>__iter__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MiscWebContentsBackend-__len__"><strong>__len__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.backends.chrome_inspector.inspector_backend_list.html#InspectorBackendList">telemetry.internal.backends.chrome_inspector.inspector_backend_list.InspectorBackendList</a>:<br>
+<dl><dt><strong>app</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="_abcoll.html#Sequence">_abcoll.Sequence</a>:<br>
+<dl><dt><a name="MiscWebContentsBackend-__contains__"><strong>__contains__</strong></a>(self, value)</dt></dl>
+
+<dl><dt><a name="MiscWebContentsBackend-__reversed__"><strong>__reversed__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MiscWebContentsBackend-count"><strong>count</strong></a>(self, value)</dt><dd><tt>S.<a href="#MiscWebContentsBackend-count">count</a>(value)&nbsp;-&gt;&nbsp;integer&nbsp;--&nbsp;return&nbsp;number&nbsp;of&nbsp;occurrences&nbsp;of&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="MiscWebContentsBackend-index"><strong>index</strong></a>(self, value)</dt><dd><tt>S.<a href="#MiscWebContentsBackend-index">index</a>(value)&nbsp;-&gt;&nbsp;integer&nbsp;--&nbsp;return&nbsp;first&nbsp;index&nbsp;of&nbsp;value.<br>
+Raises&nbsp;ValueError&nbsp;if&nbsp;the&nbsp;value&nbsp;is&nbsp;not&nbsp;present.</tt></dd></dl>
+
+<hr>
+Class methods inherited from <a href="_abcoll.html#Sized">_abcoll.Sized</a>:<br>
+<dl><dt><a name="MiscWebContentsBackend-__subclasshook__"><strong>__subclasshook__</strong></a>(cls, C)<font color="#909090"><font face="helvetica, arial"> from <a href="abc.html#ABCMeta">abc.ABCMeta</a></font></font></dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="_abcoll.html#Sized">_abcoll.Sized</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="_abcoll.html#Sized">_abcoll.Sized</a>:<br>
+<dl><dt><strong>__metaclass__</strong> = &lt;class 'abc.ABCMeta'&gt;<dd><tt>Metaclass&nbsp;for&nbsp;defining&nbsp;Abstract&nbsp;Base&nbsp;Classes&nbsp;(ABCs).<br>
+&nbsp;<br>
+Use&nbsp;this&nbsp;metaclass&nbsp;to&nbsp;create&nbsp;an&nbsp;ABC.&nbsp;&nbsp;An&nbsp;ABC&nbsp;can&nbsp;be&nbsp;subclassed<br>
+directly,&nbsp;and&nbsp;then&nbsp;acts&nbsp;as&nbsp;a&nbsp;mix-in&nbsp;class.&nbsp;&nbsp;You&nbsp;can&nbsp;also&nbsp;register<br>
+unrelated&nbsp;concrete&nbsp;classes&nbsp;(even&nbsp;built-in&nbsp;classes)&nbsp;and&nbsp;unrelated<br>
+ABCs&nbsp;as&nbsp;'virtual&nbsp;subclasses'&nbsp;--&nbsp;these&nbsp;and&nbsp;their&nbsp;descendants&nbsp;will<br>
+be&nbsp;considered&nbsp;subclasses&nbsp;of&nbsp;the&nbsp;registering&nbsp;ABC&nbsp;by&nbsp;the&nbsp;built-in<br>
+issubclass()&nbsp;function,&nbsp;but&nbsp;the&nbsp;registering&nbsp;ABC&nbsp;won't&nbsp;show&nbsp;up&nbsp;in<br>
+their&nbsp;MRO&nbsp;(Method&nbsp;Resolution&nbsp;Order)&nbsp;nor&nbsp;will&nbsp;method<br>
+implementations&nbsp;defined&nbsp;by&nbsp;the&nbsp;registering&nbsp;ABC&nbsp;be&nbsp;callable&nbsp;(not<br>
+even&nbsp;via&nbsp;super()).</tt></dl>
+
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.oobe.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.oobe.html
new file mode 100644
index 0000000..57e61f2
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.oobe.html
@@ -0,0 +1,246 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.chrome.oobe</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.<a href="telemetry.internal.backends.chrome.html"><font color="#ffffff">chrome</font></a>.oobe</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/chrome/oobe.py">telemetry/internal/backends/chrome/oobe.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+</td><td width="25%" valign=top><a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.util.html">telemetry.core.util</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.browser.web_contents.html">telemetry.internal.browser.web_contents</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.browser.web_contents.html#WebContents">telemetry.internal.browser.web_contents.WebContents</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome.oobe.html#Oobe">Oobe</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="Oobe">class <strong>Oobe</strong></a>(<a href="telemetry.internal.browser.web_contents.html#WebContents">telemetry.internal.browser.web_contents.WebContents</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.chrome.oobe.html#Oobe">Oobe</a></dd>
+<dd><a href="telemetry.internal.browser.web_contents.html#WebContents">telemetry.internal.browser.web_contents.WebContents</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="Oobe-NavigateFakeLogin"><strong>NavigateFakeLogin</strong></a>(self, username, password, gaia_id)</dt><dd><tt>Fake&nbsp;user&nbsp;login.</tt></dd></dl>
+
+<dl><dt><a name="Oobe-NavigateGaiaLogin"><strong>NavigateGaiaLogin</strong></a>(self, username, password, enterprise_enroll<font color="#909090">=False</font>, for_user_triggered_enrollment<font color="#909090">=False</font>)</dt><dd><tt>Logs&nbsp;in&nbsp;using&nbsp;the&nbsp;GAIA&nbsp;webview&nbsp;or&nbsp;IFrame,&nbsp;whichever&nbsp;is<br>
+present.&nbsp;|enterprise_enroll|&nbsp;allows&nbsp;for&nbsp;enterprise&nbsp;enrollment.<br>
+|for_user_triggered_enrollment|&nbsp;should&nbsp;be&nbsp;False&nbsp;for&nbsp;remora&nbsp;enrollment.</tt></dd></dl>
+
+<dl><dt><a name="Oobe-NavigateGuestLogin"><strong>NavigateGuestLogin</strong></a>(self)</dt><dd><tt>Logs&nbsp;in&nbsp;as&nbsp;guest.</tt></dd></dl>
+
+<dl><dt><a name="Oobe-__init__"><strong>__init__</strong></a>(self, inspector_backend)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.browser.web_contents.html#WebContents">telemetry.internal.browser.web_contents.WebContents</a>:<br>
+<dl><dt><a name="Oobe-CloseConnections"><strong>CloseConnections</strong></a>(self)</dt><dd><tt>Closes&nbsp;all&nbsp;TCP&nbsp;sockets&nbsp;held&nbsp;open&nbsp;by&nbsp;the&nbsp;browser.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException&nbsp;if&nbsp;the&nbsp;tab&nbsp;is&nbsp;not&nbsp;alive.</tt></dd></dl>
+
+<dl><dt><a name="Oobe-EnableAllContexts"><strong>EnableAllContexts</strong></a>(self)</dt><dd><tt>Enable&nbsp;all&nbsp;contexts&nbsp;in&nbsp;a&nbsp;page.&nbsp;Returns&nbsp;the&nbsp;number&nbsp;of&nbsp;available&nbsp;contexts.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.WebSocketDisconnected<br>
+&nbsp;&nbsp;exceptions.TimeoutException<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException</tt></dd></dl>
+
+<dl><dt><a name="Oobe-EvaluateJavaScript"><strong>EvaluateJavaScript</strong></a>(self, expr, timeout<font color="#909090">=90</font>)</dt><dd><tt>Evalutes&nbsp;expr&nbsp;in&nbsp;JavaScript&nbsp;and&nbsp;returns&nbsp;the&nbsp;JSONized&nbsp;result.<br>
+&nbsp;<br>
+Consider&nbsp;using&nbsp;ExecuteJavaScript&nbsp;for&nbsp;cases&nbsp;where&nbsp;the&nbsp;result&nbsp;of&nbsp;the<br>
+expression&nbsp;is&nbsp;not&nbsp;needed.<br>
+&nbsp;<br>
+If&nbsp;evaluation&nbsp;throws&nbsp;in&nbsp;JavaScript,&nbsp;a&nbsp;Python&nbsp;EvaluateException&nbsp;will<br>
+be&nbsp;raised.<br>
+&nbsp;<br>
+If&nbsp;the&nbsp;result&nbsp;of&nbsp;the&nbsp;evaluation&nbsp;cannot&nbsp;be&nbsp;JSONized,&nbsp;then&nbsp;an<br>
+EvaluationException&nbsp;will&nbsp;be&nbsp;raised.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.Error:&nbsp;See&nbsp;<a href="#Oobe-EvaluateJavaScriptInContext">EvaluateJavaScriptInContext</a>()&nbsp;for&nbsp;a&nbsp;detailed&nbsp;list<br>
+&nbsp;&nbsp;of&nbsp;possible&nbsp;exceptions.</tt></dd></dl>
+
+<dl><dt><a name="Oobe-EvaluateJavaScriptInContext"><strong>EvaluateJavaScriptInContext</strong></a>(self, expr, context_id, timeout<font color="#909090">=90</font>)</dt><dd><tt>Similar&nbsp;to&nbsp;ExecuteJavaScript,&nbsp;except&nbsp;context_id&nbsp;can&nbsp;refer&nbsp;to&nbsp;an&nbsp;iframe.<br>
+The&nbsp;main&nbsp;page&nbsp;has&nbsp;context_id=1,&nbsp;the&nbsp;first&nbsp;iframe&nbsp;context_id=2,&nbsp;etc.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.EvaluateException<br>
+&nbsp;&nbsp;exceptions.WebSocketDisconnected<br>
+&nbsp;&nbsp;exceptions.TimeoutException<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException</tt></dd></dl>
+
+<dl><dt><a name="Oobe-ExecuteJavaScript"><strong>ExecuteJavaScript</strong></a>(self, statement, timeout<font color="#909090">=90</font>)</dt><dd><tt>Executes&nbsp;statement&nbsp;in&nbsp;JavaScript.&nbsp;Does&nbsp;not&nbsp;return&nbsp;the&nbsp;result.<br>
+&nbsp;<br>
+If&nbsp;the&nbsp;statement&nbsp;failed&nbsp;to&nbsp;evaluate,&nbsp;EvaluateException&nbsp;will&nbsp;be&nbsp;raised.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.Error:&nbsp;See&nbsp;<a href="#Oobe-ExecuteJavaScriptInContext">ExecuteJavaScriptInContext</a>()&nbsp;for&nbsp;a&nbsp;detailed&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;possible&nbsp;exceptions.</tt></dd></dl>
+
+<dl><dt><a name="Oobe-ExecuteJavaScriptInContext"><strong>ExecuteJavaScriptInContext</strong></a>(self, expr, context_id, timeout<font color="#909090">=90</font>)</dt><dd><tt>Similar&nbsp;to&nbsp;ExecuteJavaScript,&nbsp;except&nbsp;context_id&nbsp;can&nbsp;refer&nbsp;to&nbsp;an&nbsp;iframe.<br>
+The&nbsp;main&nbsp;page&nbsp;has&nbsp;context_id=1,&nbsp;the&nbsp;first&nbsp;iframe&nbsp;context_id=2,&nbsp;etc.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.EvaluateException<br>
+&nbsp;&nbsp;exceptions.WebSocketDisconnected<br>
+&nbsp;&nbsp;exceptions.TimeoutException<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException</tt></dd></dl>
+
+<dl><dt><a name="Oobe-GetUrl"><strong>GetUrl</strong></a>(self)</dt><dd><tt>Returns&nbsp;the&nbsp;URL&nbsp;to&nbsp;which&nbsp;the&nbsp;<a href="telemetry.internal.browser.web_contents.html#WebContents">WebContents</a>&nbsp;is&nbsp;connected.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.Error:&nbsp;If&nbsp;there&nbsp;is&nbsp;an&nbsp;error&nbsp;in&nbsp;inspector&nbsp;backend&nbsp;connection.</tt></dd></dl>
+
+<dl><dt><a name="Oobe-GetWebviewContexts"><strong>GetWebviewContexts</strong></a>(self)</dt><dd><tt>Returns&nbsp;a&nbsp;list&nbsp;of&nbsp;webview&nbsp;contexts&nbsp;within&nbsp;the&nbsp;current&nbsp;inspector&nbsp;backend.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;A&nbsp;list&nbsp;of&nbsp;<a href="telemetry.internal.browser.web_contents.html#WebContents">WebContents</a>&nbsp;objects&nbsp;representing&nbsp;the&nbsp;webview&nbsp;contexts.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.Error:&nbsp;If&nbsp;there&nbsp;is&nbsp;an&nbsp;error&nbsp;in&nbsp;inspector&nbsp;backend&nbsp;connection.</tt></dd></dl>
+
+<dl><dt><a name="Oobe-HasReachedQuiescence"><strong>HasReachedQuiescence</strong></a>(self)</dt><dd><tt>Determine&nbsp;whether&nbsp;the&nbsp;page&nbsp;has&nbsp;reached&nbsp;quiescence&nbsp;after&nbsp;loading.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;True&nbsp;if&nbsp;2&nbsp;seconds&nbsp;have&nbsp;passed&nbsp;since&nbsp;last&nbsp;resource&nbsp;received,&nbsp;false<br>
+&nbsp;&nbsp;otherwise.<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.Error:&nbsp;See&nbsp;<a href="#Oobe-EvaluateJavaScript">EvaluateJavaScript</a>()&nbsp;for&nbsp;a&nbsp;detailed&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;possible&nbsp;exceptions.</tt></dd></dl>
+
+<dl><dt><a name="Oobe-IsAlive"><strong>IsAlive</strong></a>(self)</dt><dd><tt>Whether&nbsp;the&nbsp;<a href="telemetry.internal.browser.web_contents.html#WebContents">WebContents</a>&nbsp;is&nbsp;still&nbsp;operating&nbsp;normally.<br>
+&nbsp;<br>
+Since&nbsp;<a href="telemetry.internal.browser.web_contents.html#WebContents">WebContents</a>&nbsp;function&nbsp;asynchronously,&nbsp;this&nbsp;method&nbsp;does&nbsp;not&nbsp;guarantee<br>
+that&nbsp;the&nbsp;<a href="telemetry.internal.browser.web_contents.html#WebContents">WebContents</a>&nbsp;will&nbsp;still&nbsp;be&nbsp;alive&nbsp;at&nbsp;any&nbsp;point&nbsp;in&nbsp;the&nbsp;future.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;A&nbsp;boolean&nbsp;indicating&nbsp;whether&nbsp;the&nbsp;<a href="telemetry.internal.browser.web_contents.html#WebContents">WebContents</a>&nbsp;is&nbsp;opearting&nbsp;normally.</tt></dd></dl>
+
+<dl><dt><a name="Oobe-Navigate"><strong>Navigate</strong></a>(self, url, script_to_evaluate_on_commit<font color="#909090">=None</font>, timeout<font color="#909090">=90</font>)</dt><dd><tt>Navigates&nbsp;to&nbsp;url.<br>
+&nbsp;<br>
+If&nbsp;|script_to_evaluate_on_commit|&nbsp;is&nbsp;given,&nbsp;the&nbsp;script&nbsp;source&nbsp;string&nbsp;will&nbsp;be<br>
+evaluated&nbsp;when&nbsp;the&nbsp;navigation&nbsp;is&nbsp;committed.&nbsp;This&nbsp;is&nbsp;after&nbsp;the&nbsp;context&nbsp;of<br>
+the&nbsp;page&nbsp;exists,&nbsp;but&nbsp;before&nbsp;any&nbsp;script&nbsp;on&nbsp;the&nbsp;page&nbsp;itself&nbsp;has&nbsp;executed.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.TimeoutException<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException</tt></dd></dl>
+
+<dl><dt><a name="Oobe-StartTimelineRecording"><strong>StartTimelineRecording</strong></a>(self)</dt><dd><tt>Starts&nbsp;timeline&nbsp;recording.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.TimeoutException<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException</tt></dd></dl>
+
+<dl><dt><a name="Oobe-StopTimelineRecording"><strong>StopTimelineRecording</strong></a>(self)</dt><dd><tt>Stops&nbsp;timeline&nbsp;recording.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.TimeoutException<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException</tt></dd></dl>
+
+<dl><dt><a name="Oobe-SynthesizeScrollGesture"><strong>SynthesizeScrollGesture</strong></a>(self, x<font color="#909090">=100</font>, y<font color="#909090">=800</font>, xDistance<font color="#909090">=0</font>, yDistance<font color="#909090">=-500</font>, xOverscroll<font color="#909090">=None</font>, yOverscroll<font color="#909090">=None</font>, preventFling<font color="#909090">=True</font>, speed<font color="#909090">=None</font>, gestureSourceType<font color="#909090">=None</font>, repeatCount<font color="#909090">=None</font>, repeatDelayMs<font color="#909090">=None</font>, interactionMarkerName<font color="#909090">=None</font>)</dt><dd><tt>Runs&nbsp;an&nbsp;inspector&nbsp;command&nbsp;that&nbsp;causes&nbsp;a&nbsp;repeatable&nbsp;browser&nbsp;driven&nbsp;scroll.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;x:&nbsp;X&nbsp;coordinate&nbsp;of&nbsp;the&nbsp;start&nbsp;of&nbsp;the&nbsp;gesture&nbsp;in&nbsp;CSS&nbsp;pixels.<br>
+&nbsp;&nbsp;y:&nbsp;Y&nbsp;coordinate&nbsp;of&nbsp;the&nbsp;start&nbsp;of&nbsp;the&nbsp;gesture&nbsp;in&nbsp;CSS&nbsp;pixels.<br>
+&nbsp;&nbsp;xDistance:&nbsp;Distance&nbsp;to&nbsp;scroll&nbsp;along&nbsp;the&nbsp;X&nbsp;axis&nbsp;(positive&nbsp;to&nbsp;scroll&nbsp;left).<br>
+&nbsp;&nbsp;yDistance:&nbsp;Ddistance&nbsp;to&nbsp;scroll&nbsp;along&nbsp;the&nbsp;Y&nbsp;axis&nbsp;(positive&nbsp;to&nbsp;scroll&nbsp;up).<br>
+&nbsp;&nbsp;xOverscroll:&nbsp;Number&nbsp;of&nbsp;additional&nbsp;pixels&nbsp;to&nbsp;scroll&nbsp;back&nbsp;along&nbsp;the&nbsp;X&nbsp;axis.<br>
+&nbsp;&nbsp;xOverscroll:&nbsp;Number&nbsp;of&nbsp;additional&nbsp;pixels&nbsp;to&nbsp;scroll&nbsp;back&nbsp;along&nbsp;the&nbsp;Y&nbsp;axis.<br>
+&nbsp;&nbsp;preventFling:&nbsp;Prevents&nbsp;a&nbsp;fling&nbsp;gesture.<br>
+&nbsp;&nbsp;speed:&nbsp;Swipe&nbsp;speed&nbsp;in&nbsp;pixels&nbsp;per&nbsp;second.<br>
+&nbsp;&nbsp;gestureSourceType:&nbsp;Which&nbsp;type&nbsp;of&nbsp;input&nbsp;events&nbsp;to&nbsp;be&nbsp;generated.<br>
+&nbsp;&nbsp;repeatCount:&nbsp;Number&nbsp;of&nbsp;additional&nbsp;repeats&nbsp;beyond&nbsp;the&nbsp;first&nbsp;scroll.<br>
+&nbsp;&nbsp;repeatDelayMs:&nbsp;Number&nbsp;of&nbsp;milliseconds&nbsp;delay&nbsp;between&nbsp;each&nbsp;repeat.<br>
+&nbsp;&nbsp;interactionMarkerName:&nbsp;The&nbsp;name&nbsp;of&nbsp;the&nbsp;interaction&nbsp;markers&nbsp;to&nbsp;generate.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.TimeoutException<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException</tt></dd></dl>
+
+<dl><dt><a name="Oobe-WaitForDocumentReadyStateToBeComplete"><strong>WaitForDocumentReadyStateToBeComplete</strong></a>(self, timeout<font color="#909090">=90</font>)</dt><dd><tt>Waits&nbsp;for&nbsp;the&nbsp;document&nbsp;to&nbsp;finish&nbsp;loading.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.Error:&nbsp;See&nbsp;<a href="#Oobe-WaitForJavaScriptExpression">WaitForJavaScriptExpression</a>()&nbsp;for&nbsp;a&nbsp;detailed&nbsp;list<br>
+&nbsp;&nbsp;of&nbsp;possible&nbsp;exceptions.</tt></dd></dl>
+
+<dl><dt><a name="Oobe-WaitForDocumentReadyStateToBeInteractiveOrBetter"><strong>WaitForDocumentReadyStateToBeInteractiveOrBetter</strong></a>(self, timeout<font color="#909090">=90</font>)</dt><dd><tt>Waits&nbsp;for&nbsp;the&nbsp;document&nbsp;to&nbsp;be&nbsp;interactive.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.Error:&nbsp;See&nbsp;<a href="#Oobe-WaitForJavaScriptExpression">WaitForJavaScriptExpression</a>()&nbsp;for&nbsp;a&nbsp;detailed&nbsp;list<br>
+&nbsp;&nbsp;of&nbsp;possible&nbsp;exceptions.</tt></dd></dl>
+
+<dl><dt><a name="Oobe-WaitForJavaScriptExpression"><strong>WaitForJavaScriptExpression</strong></a>(self, expr, timeout, dump_page_state_on_timeout<font color="#909090">=True</font>)</dt><dd><tt>Waits&nbsp;for&nbsp;the&nbsp;given&nbsp;JavaScript&nbsp;expression&nbsp;to&nbsp;be&nbsp;True.<br>
+&nbsp;<br>
+This&nbsp;method&nbsp;is&nbsp;robust&nbsp;against&nbsp;any&nbsp;given&nbsp;Evaluation&nbsp;timing&nbsp;out.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;expr:&nbsp;The&nbsp;expression&nbsp;to&nbsp;evaluate.<br>
+&nbsp;&nbsp;timeout:&nbsp;The&nbsp;number&nbsp;of&nbsp;seconds&nbsp;to&nbsp;wait&nbsp;for&nbsp;the&nbsp;expression&nbsp;to&nbsp;be&nbsp;True.<br>
+&nbsp;&nbsp;dump_page_state_on_timeout:&nbsp;Whether&nbsp;to&nbsp;provide&nbsp;additional&nbsp;information&nbsp;on<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;the&nbsp;page&nbsp;state&nbsp;if&nbsp;a&nbsp;TimeoutException&nbsp;is&nbsp;thrown.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.TimeoutException:&nbsp;On&nbsp;a&nbsp;timeout.<br>
+&nbsp;&nbsp;exceptions.Error:&nbsp;See&nbsp;<a href="#Oobe-EvaluateJavaScript">EvaluateJavaScript</a>()&nbsp;for&nbsp;a&nbsp;detailed&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;possible&nbsp;exceptions.</tt></dd></dl>
+
+<dl><dt><a name="Oobe-WaitForNavigate"><strong>WaitForNavigate</strong></a>(self, timeout<font color="#909090">=90</font>)</dt><dd><tt>Waits&nbsp;for&nbsp;the&nbsp;navigation&nbsp;to&nbsp;complete.<br>
+&nbsp;<br>
+The&nbsp;current&nbsp;page&nbsp;is&nbsp;expect&nbsp;to&nbsp;be&nbsp;in&nbsp;a&nbsp;navigation.<br>
+This&nbsp;function&nbsp;returns&nbsp;when&nbsp;the&nbsp;navigation&nbsp;is&nbsp;complete&nbsp;or&nbsp;when<br>
+the&nbsp;timeout&nbsp;has&nbsp;been&nbsp;exceeded.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.TimeoutException<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.browser.web_contents.html#WebContents">telemetry.internal.browser.web_contents.WebContents</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>id</strong></dt>
+<dd><tt>Return&nbsp;the&nbsp;unique&nbsp;id&nbsp;string&nbsp;for&nbsp;this&nbsp;tab&nbsp;object.</tt></dd>
+</dl>
+<dl><dt><strong>message_output_stream</strong></dt>
+</dl>
+<dl><dt><strong>timeline_model</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.system_info_backend.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.system_info_backend.html
new file mode 100644
index 0000000..ea93819
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.system_info_backend.html
@@ -0,0 +1,62 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.chrome.system_info_backend</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.<a href="telemetry.internal.backends.chrome.html"><font color="#ffffff">chrome</font></a>.system_info_backend</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/chrome/system_info_backend.py">telemetry/internal/backends/chrome/system_info_backend.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.util.camel_case.html">telemetry.internal.util.camel_case</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.backends.chrome_inspector.inspector_websocket.html">telemetry.internal.backends.chrome_inspector.inspector_websocket</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.system_info.html">telemetry.internal.platform.system_info</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome.system_info_backend.html#SystemInfoBackend">SystemInfoBackend</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="SystemInfoBackend">class <strong>SystemInfoBackend</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="SystemInfoBackend-GetSystemInfo"><strong>GetSystemInfo</strong></a>(self, timeout<font color="#909090">=10</font>)</dt></dl>
+
+<dl><dt><a name="SystemInfoBackend-__init__"><strong>__init__</strong></a>(self, devtools_port, devtools_page<font color="#909090">=None</font>)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.tab_list_backend.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.tab_list_backend.html
new file mode 100644
index 0000000..4cba6df
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome.tab_list_backend.html
@@ -0,0 +1,229 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.chrome.tab_list_backend</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.<a href="telemetry.internal.backends.chrome.html"><font color="#ffffff">chrome</font></a>.tab_list_backend</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/chrome/tab_list_backend.py">telemetry/internal/backends/chrome/tab_list_backend.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+<a href="telemetry.internal.backends.chrome_inspector.inspector_backend_list.html">telemetry.internal.backends.chrome_inspector.inspector_backend_list</a><br>
+</td><td width="25%" valign=top><a href="json.html">json</a><br>
+<a href="telemetry.internal.browser.tab.html">telemetry.internal.browser.tab</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.util.html">telemetry.core.util</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.core.exceptions.html#Error">telemetry.core.exceptions.Error</a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome.tab_list_backend.html#TabUnexpectedResponseException">TabUnexpectedResponseException</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome_inspector.inspector_backend_list.html#InspectorBackendList">telemetry.internal.backends.chrome_inspector.inspector_backend_list.InspectorBackendList</a>(<a href="_abcoll.html#Sequence">_abcoll.Sequence</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome.tab_list_backend.html#TabListBackend">TabListBackend</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TabListBackend">class <strong>TabListBackend</strong></a>(<a href="telemetry.internal.backends.chrome_inspector.inspector_backend_list.html#InspectorBackendList">telemetry.internal.backends.chrome_inspector.inspector_backend_list.InspectorBackendList</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;dynamic&nbsp;sequence&nbsp;of&nbsp;tab.Tabs&nbsp;in&nbsp;UI&nbsp;order.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.chrome.tab_list_backend.html#TabListBackend">TabListBackend</a></dd>
+<dd><a href="telemetry.internal.backends.chrome_inspector.inspector_backend_list.html#InspectorBackendList">telemetry.internal.backends.chrome_inspector.inspector_backend_list.InspectorBackendList</a></dd>
+<dd><a href="_abcoll.html#Sequence">_abcoll.Sequence</a></dd>
+<dd><a href="_abcoll.html#Sized">_abcoll.Sized</a></dd>
+<dd><a href="_abcoll.html#Iterable">_abcoll.Iterable</a></dd>
+<dd><a href="_abcoll.html#Container">_abcoll.Container</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="TabListBackend-ActivateTab"><strong>ActivateTab</strong></a>(self, tab_id, timeout<font color="#909090">=30</font>)</dt><dd><tt>Activates&nbsp;the&nbsp;tab&nbsp;with&nbsp;the&nbsp;given&nbsp;debugger_url.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;devtools_http.DevToolsClientConnectionError<br>
+&nbsp;&nbsp;devtools_client_backend.TabNotFoundError<br>
+&nbsp;&nbsp;<a href="#TabUnexpectedResponseException">TabUnexpectedResponseException</a></tt></dd></dl>
+
+<dl><dt><a name="TabListBackend-CloseTab"><strong>CloseTab</strong></a>(self, tab_id, timeout<font color="#909090">=300</font>)</dt><dd><tt>Closes&nbsp;the&nbsp;tab&nbsp;with&nbsp;the&nbsp;given&nbsp;debugger_url.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;devtools_http.DevToolsClientConnectionError<br>
+&nbsp;&nbsp;devtools_client_backend.TabNotFoundError<br>
+&nbsp;&nbsp;<a href="#TabUnexpectedResponseException">TabUnexpectedResponseException</a><br>
+&nbsp;&nbsp;exceptions.TimeoutException</tt></dd></dl>
+
+<dl><dt><a name="TabListBackend-CreateWrapper"><strong>CreateWrapper</strong></a>(self, inspector_backend)</dt></dl>
+
+<dl><dt><a name="TabListBackend-Get"><strong>Get</strong></a>(self, index, ret)</dt><dd><tt>Returns&nbsp;self[index]&nbsp;if&nbsp;it&nbsp;exists,&nbsp;or&nbsp;ret&nbsp;if&nbsp;index&nbsp;is&nbsp;out&nbsp;of&nbsp;bounds.</tt></dd></dl>
+
+<dl><dt><a name="TabListBackend-New"><strong>New</strong></a>(self, timeout)</dt><dd><tt>Makes&nbsp;a&nbsp;new&nbsp;tab.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;A&nbsp;Tab&nbsp;object.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;devtools_http.DevToolsClientConnectionError</tt></dd></dl>
+
+<dl><dt><a name="TabListBackend-ShouldIncludeContext"><strong>ShouldIncludeContext</strong></a>(self, context)</dt></dl>
+
+<dl><dt><a name="TabListBackend-__init__"><strong>__init__</strong></a>(self, browser_backend)</dt></dl>
+
+<hr>
+Data and other attributes defined here:<br>
+<dl><dt><strong>__abstractmethods__</strong> = frozenset([])</dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.backends.chrome_inspector.inspector_backend_list.html#InspectorBackendList">telemetry.internal.backends.chrome_inspector.inspector_backend_list.InspectorBackendList</a>:<br>
+<dl><dt><a name="TabListBackend-GetBackendFromContextId"><strong>GetBackendFromContextId</strong></a>(self, context_id)</dt></dl>
+
+<dl><dt><a name="TabListBackend-GetContextInfo"><strong>GetContextInfo</strong></a>(self, context_id)</dt></dl>
+
+<dl><dt><a name="TabListBackend-GetTabById"><strong>GetTabById</strong></a>(self, identifier)</dt></dl>
+
+<dl><dt><a name="TabListBackend-IterContextIds"><strong>IterContextIds</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TabListBackend-__getitem__"><strong>__getitem__</strong></a>(self, index)</dt><dd><tt>#&nbsp;TODO(nednguyen):&nbsp;Remove&nbsp;this&nbsp;method&nbsp;and&nbsp;turn&nbsp;inspector_backend_list&nbsp;API&nbsp;to<br>
+#&nbsp;dictionary-like&nbsp;API&nbsp;(crbug.com/398467)</tt></dd></dl>
+
+<dl><dt><a name="TabListBackend-__iter__"><strong>__iter__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TabListBackend-__len__"><strong>__len__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.backends.chrome_inspector.inspector_backend_list.html#InspectorBackendList">telemetry.internal.backends.chrome_inspector.inspector_backend_list.InspectorBackendList</a>:<br>
+<dl><dt><strong>app</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="_abcoll.html#Sequence">_abcoll.Sequence</a>:<br>
+<dl><dt><a name="TabListBackend-__contains__"><strong>__contains__</strong></a>(self, value)</dt></dl>
+
+<dl><dt><a name="TabListBackend-__reversed__"><strong>__reversed__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TabListBackend-count"><strong>count</strong></a>(self, value)</dt><dd><tt>S.<a href="#TabListBackend-count">count</a>(value)&nbsp;-&gt;&nbsp;integer&nbsp;--&nbsp;return&nbsp;number&nbsp;of&nbsp;occurrences&nbsp;of&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="TabListBackend-index"><strong>index</strong></a>(self, value)</dt><dd><tt>S.<a href="#TabListBackend-index">index</a>(value)&nbsp;-&gt;&nbsp;integer&nbsp;--&nbsp;return&nbsp;first&nbsp;index&nbsp;of&nbsp;value.<br>
+Raises&nbsp;ValueError&nbsp;if&nbsp;the&nbsp;value&nbsp;is&nbsp;not&nbsp;present.</tt></dd></dl>
+
+<hr>
+Class methods inherited from <a href="_abcoll.html#Sized">_abcoll.Sized</a>:<br>
+<dl><dt><a name="TabListBackend-__subclasshook__"><strong>__subclasshook__</strong></a>(cls, C)<font color="#909090"><font face="helvetica, arial"> from <a href="abc.html#ABCMeta">abc.ABCMeta</a></font></font></dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="_abcoll.html#Sized">_abcoll.Sized</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="_abcoll.html#Sized">_abcoll.Sized</a>:<br>
+<dl><dt><strong>__metaclass__</strong> = &lt;class 'abc.ABCMeta'&gt;<dd><tt>Metaclass&nbsp;for&nbsp;defining&nbsp;Abstract&nbsp;Base&nbsp;Classes&nbsp;(ABCs).<br>
+&nbsp;<br>
+Use&nbsp;this&nbsp;metaclass&nbsp;to&nbsp;create&nbsp;an&nbsp;ABC.&nbsp;&nbsp;An&nbsp;ABC&nbsp;can&nbsp;be&nbsp;subclassed<br>
+directly,&nbsp;and&nbsp;then&nbsp;acts&nbsp;as&nbsp;a&nbsp;mix-in&nbsp;class.&nbsp;&nbsp;You&nbsp;can&nbsp;also&nbsp;register<br>
+unrelated&nbsp;concrete&nbsp;classes&nbsp;(even&nbsp;built-in&nbsp;classes)&nbsp;and&nbsp;unrelated<br>
+ABCs&nbsp;as&nbsp;'virtual&nbsp;subclasses'&nbsp;--&nbsp;these&nbsp;and&nbsp;their&nbsp;descendants&nbsp;will<br>
+be&nbsp;considered&nbsp;subclasses&nbsp;of&nbsp;the&nbsp;registering&nbsp;ABC&nbsp;by&nbsp;the&nbsp;built-in<br>
+issubclass()&nbsp;function,&nbsp;but&nbsp;the&nbsp;registering&nbsp;ABC&nbsp;won't&nbsp;show&nbsp;up&nbsp;in<br>
+their&nbsp;MRO&nbsp;(Method&nbsp;Resolution&nbsp;Order)&nbsp;nor&nbsp;will&nbsp;method<br>
+implementations&nbsp;defined&nbsp;by&nbsp;the&nbsp;registering&nbsp;ABC&nbsp;be&nbsp;callable&nbsp;(not<br>
+even&nbsp;via&nbsp;super()).</tt></dl>
+
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TabUnexpectedResponseException">class <strong>TabUnexpectedResponseException</strong></a>(<a href="telemetry.core.exceptions.html#Error">telemetry.core.exceptions.Error</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.chrome.tab_list_backend.html#TabUnexpectedResponseException">TabUnexpectedResponseException</a></dd>
+<dd><a href="telemetry.core.exceptions.html#Error">telemetry.core.exceptions.Error</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.core.exceptions.html#Error">telemetry.core.exceptions.Error</a>:<br>
+<dl><dt><a name="TabUnexpectedResponseException-AddDebuggingMessage"><strong>AddDebuggingMessage</strong></a>(self, msg)</dt><dd><tt>Adds&nbsp;a&nbsp;message&nbsp;to&nbsp;the&nbsp;description&nbsp;of&nbsp;the&nbsp;exception.<br>
+&nbsp;<br>
+Many&nbsp;Telemetry&nbsp;exceptions&nbsp;arise&nbsp;from&nbsp;failures&nbsp;in&nbsp;another&nbsp;application.&nbsp;These<br>
+failures&nbsp;are&nbsp;difficult&nbsp;to&nbsp;pinpoint.&nbsp;This&nbsp;method&nbsp;allows&nbsp;Telemetry&nbsp;classes&nbsp;to<br>
+append&nbsp;useful&nbsp;debugging&nbsp;information&nbsp;to&nbsp;the&nbsp;exception.&nbsp;This&nbsp;method&nbsp;also&nbsp;logs<br>
+information&nbsp;about&nbsp;the&nbsp;location&nbsp;from&nbsp;where&nbsp;it&nbsp;was&nbsp;called.</tt></dd></dl>
+
+<dl><dt><a name="TabUnexpectedResponseException-__init__"><strong>__init__</strong></a>(self, msg<font color="#909090">=''</font>)</dt></dl>
+
+<dl><dt><a name="TabUnexpectedResponseException-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.core.exceptions.html#Error">telemetry.core.exceptions.Error</a>:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#TabUnexpectedResponseException-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;object&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="TabUnexpectedResponseException-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#TabUnexpectedResponseException-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="TabUnexpectedResponseException-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#TabUnexpectedResponseException-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="TabUnexpectedResponseException-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#TabUnexpectedResponseException-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="TabUnexpectedResponseException-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#TabUnexpectedResponseException-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="TabUnexpectedResponseException-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="TabUnexpectedResponseException-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#TabUnexpectedResponseException-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="TabUnexpectedResponseException-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#TabUnexpectedResponseException-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="TabUnexpectedResponseException-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="TabUnexpectedResponseException-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome_inspector.devtools_client_backend.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome_inspector.devtools_client_backend.html
new file mode 100644
index 0000000..ff536e3
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome_inspector.devtools_client_backend.html
@@ -0,0 +1,284 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.chrome_inspector.devtools_client_backend</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.<a href="telemetry.internal.backends.chrome_inspector.html"><font color="#ffffff">chrome_inspector</font></a>.devtools_client_backend</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/chrome_inspector/devtools_client_backend.py">telemetry/internal/backends/chrome_inspector/devtools_client_backend.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.backends.browser_backend.html">telemetry.internal.backends.browser_backend</a><br>
+<a href="telemetry.internal.platform.tracing_agent.chrome_tracing_agent.html">telemetry.internal.platform.tracing_agent.chrome_tracing_agent</a><br>
+<a href="telemetry.internal.platform.tracing_agent.chrome_tracing_devtools_manager.html">telemetry.internal.platform.tracing_agent.chrome_tracing_devtools_manager</a><br>
+<a href="telemetry.decorators.html">telemetry.decorators</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.backends.chrome_inspector.devtools_http.html">telemetry.internal.backends.chrome_inspector.devtools_http</a><br>
+<a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+<a href="telemetry.internal.backends.chrome_inspector.inspector_backend.html">telemetry.internal.backends.chrome_inspector.inspector_backend</a><br>
+<a href="telemetry.internal.backends.chrome_inspector.inspector_websocket.html">telemetry.internal.backends.chrome_inspector.inspector_websocket</a><br>
+</td><td width="25%" valign=top><a href="logging.html">logging</a><br>
+<a href="telemetry.internal.backends.chrome_inspector.memory_backend.html">telemetry.internal.backends.chrome_inspector.memory_backend</a><br>
+<a href="re.html">re</a><br>
+<a href="socket.html">socket</a><br>
+</td><td width="25%" valign=top><a href="sys.html">sys</a><br>
+<a href="telemetry.timeline.trace_data.html">telemetry.timeline.trace_data</a><br>
+<a href="telemetry.internal.backends.chrome_inspector.tracing_backend.html">telemetry.internal.backends.chrome_inspector.tracing_backend</a><br>
+<a href="telemetry.internal.backends.chrome_inspector.websocket.html">telemetry.internal.backends.chrome_inspector.websocket</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome_inspector.devtools_client_backend.html#DevToolsClientBackend">DevToolsClientBackend</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="telemetry.core.exceptions.html#Error">telemetry.core.exceptions.Error</a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome_inspector.devtools_client_backend.html#TabNotFoundError">TabNotFoundError</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="DevToolsClientBackend">class <strong>DevToolsClientBackend</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>An&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;that&nbsp;communicates&nbsp;with&nbsp;Chrome's&nbsp;devtools.<br>
+&nbsp;<br>
+This&nbsp;class&nbsp;owns&nbsp;a&nbsp;map&nbsp;of&nbsp;InspectorBackends.&nbsp;It&nbsp;is&nbsp;responsible&nbsp;for&nbsp;creating<br>
+them&nbsp;and&nbsp;destroying&nbsp;them.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="DevToolsClientBackend-ActivateTab"><strong>ActivateTab</strong></a>(self, tab_id, timeout)</dt><dd><tt>Activates&nbsp;the&nbsp;tab&nbsp;with&nbsp;the&nbsp;given&nbsp;id.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;devtools_http.DevToolsClientConnectionError<br>
+&nbsp;&nbsp;<a href="#TabNotFoundError">TabNotFoundError</a></tt></dd></dl>
+
+<dl><dt><a name="DevToolsClientBackend-Close"><strong>Close</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DevToolsClientBackend-CloseTab"><strong>CloseTab</strong></a>(self, tab_id, timeout)</dt><dd><tt>Closes&nbsp;the&nbsp;tab&nbsp;with&nbsp;the&nbsp;given&nbsp;id.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;devtools_http.DevToolsClientConnectionError<br>
+&nbsp;&nbsp;<a href="#TabNotFoundError">TabNotFoundError</a></tt></dd></dl>
+
+<dl><dt><a name="DevToolsClientBackend-DumpMemory"><strong>DumpMemory</strong></a>(self, timeout<font color="#909090">=30</font>)</dt><dd><tt>Dumps&nbsp;memory.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;GUID&nbsp;of&nbsp;the&nbsp;generated&nbsp;dump&nbsp;if&nbsp;successful,&nbsp;None&nbsp;otherwise.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;TracingTimeoutException:&nbsp;If&nbsp;more&nbsp;than&nbsp;|timeout|&nbsp;seconds&nbsp;has&nbsp;passed<br>
+&nbsp;&nbsp;since&nbsp;the&nbsp;last&nbsp;time&nbsp;any&nbsp;data&nbsp;is&nbsp;received.<br>
+&nbsp;&nbsp;TracingUnrecoverableException:&nbsp;If&nbsp;there&nbsp;is&nbsp;a&nbsp;websocket&nbsp;error.<br>
+&nbsp;&nbsp;TracingUnexpectedResponseException:&nbsp;If&nbsp;the&nbsp;response&nbsp;contains&nbsp;an&nbsp;error<br>
+&nbsp;&nbsp;or&nbsp;does&nbsp;not&nbsp;contain&nbsp;the&nbsp;expected&nbsp;result.</tt></dd></dl>
+
+<dl><dt><a name="DevToolsClientBackend-GetChromeBranchNumber"><strong>GetChromeBranchNumber</strong></a>(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="DevToolsClientBackend-GetUpdatedInspectableContexts"><strong>GetUpdatedInspectableContexts</strong></a>(self)</dt><dd><tt>Returns&nbsp;an&nbsp;updated&nbsp;instance&nbsp;of&nbsp;_DevToolsContextMapBackend.</tt></dd></dl>
+
+<dl><dt><a name="DevToolsClientBackend-GetUrl"><strong>GetUrl</strong></a>(self, tab_id)</dt><dd><tt>Returns&nbsp;the&nbsp;URL&nbsp;of&nbsp;the&nbsp;tab&nbsp;with&nbsp;|tab_id|,&nbsp;as&nbsp;reported&nbsp;by&nbsp;devtools.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;devtools_http.DevToolsClientConnectionError</tt></dd></dl>
+
+<dl><dt><a name="DevToolsClientBackend-IsAlive"><strong>IsAlive</strong></a>(self)</dt><dd><tt>Whether&nbsp;the&nbsp;DevTools&nbsp;server&nbsp;is&nbsp;available&nbsp;and&nbsp;connectable.</tt></dd></dl>
+
+<dl><dt><a name="DevToolsClientBackend-IsChromeTracingSupported"><strong>IsChromeTracingSupported</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DevToolsClientBackend-IsInspectable"><strong>IsInspectable</strong></a>(self, tab_id)</dt><dd><tt>Whether&nbsp;the&nbsp;tab&nbsp;with&nbsp;|tab_id|&nbsp;is&nbsp;inspectable,&nbsp;as&nbsp;reported&nbsp;by&nbsp;devtools.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;devtools_http.DevToolsClientConnectionError</tt></dd></dl>
+
+<dl><dt><a name="DevToolsClientBackend-RequestNewTab"><strong>RequestNewTab</strong></a>(self, timeout)</dt><dd><tt>Creates&nbsp;a&nbsp;new&nbsp;tab.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;A&nbsp;JSON&nbsp;string&nbsp;as&nbsp;returned&nbsp;by&nbsp;DevTools.&nbsp;Example:<br>
+&nbsp;&nbsp;{<br>
+&nbsp;&nbsp;&nbsp;&nbsp;"description":&nbsp;"",<br>
+&nbsp;&nbsp;&nbsp;&nbsp;"devtoolsFrontendUrl":<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"/devtools/inspector.html?ws=host:port/devtools/page/id-string",<br>
+&nbsp;&nbsp;&nbsp;&nbsp;"id":&nbsp;"id-string",<br>
+&nbsp;&nbsp;&nbsp;&nbsp;"title":&nbsp;"Page&nbsp;Title",<br>
+&nbsp;&nbsp;&nbsp;&nbsp;"type":&nbsp;"page",<br>
+&nbsp;&nbsp;&nbsp;&nbsp;"url":&nbsp;"url",<br>
+&nbsp;&nbsp;&nbsp;&nbsp;"webSocketDebuggerUrl":&nbsp;"ws://host:port/devtools/page/id-string"<br>
+&nbsp;&nbsp;}<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;devtools_http.DevToolsClientConnectionError</tt></dd></dl>
+
+<dl><dt><a name="DevToolsClientBackend-SetMemoryPressureNotificationsSuppressed"><strong>SetMemoryPressureNotificationsSuppressed</strong></a>(self, suppressed, timeout<font color="#909090">=30</font>)</dt><dd><tt>Enable/disable&nbsp;suppressing&nbsp;memory&nbsp;pressure&nbsp;notifications.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;suppressed:&nbsp;If&nbsp;true,&nbsp;memory&nbsp;pressure&nbsp;notifications&nbsp;will&nbsp;be&nbsp;suppressed.<br>
+&nbsp;&nbsp;timeout:&nbsp;The&nbsp;timeout&nbsp;in&nbsp;seconds.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;MemoryTimeoutException:&nbsp;If&nbsp;more&nbsp;than&nbsp;|timeout|&nbsp;seconds&nbsp;has&nbsp;passed<br>
+&nbsp;&nbsp;since&nbsp;the&nbsp;last&nbsp;time&nbsp;any&nbsp;data&nbsp;is&nbsp;received.<br>
+&nbsp;&nbsp;MemoryUnrecoverableException:&nbsp;If&nbsp;there&nbsp;is&nbsp;a&nbsp;websocket&nbsp;error.<br>
+&nbsp;&nbsp;MemoryUnexpectedResponseException:&nbsp;If&nbsp;the&nbsp;response&nbsp;contains&nbsp;an&nbsp;error<br>
+&nbsp;&nbsp;or&nbsp;does&nbsp;not&nbsp;contain&nbsp;the&nbsp;expected&nbsp;result.</tt></dd></dl>
+
+<dl><dt><a name="DevToolsClientBackend-SimulateMemoryPressureNotification"><strong>SimulateMemoryPressureNotification</strong></a>(self, pressure_level, timeout<font color="#909090">=30</font>)</dt><dd><tt>Simulate&nbsp;a&nbsp;memory&nbsp;pressure&nbsp;notification.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;pressure&nbsp;level:&nbsp;The&nbsp;memory&nbsp;pressure&nbsp;level&nbsp;of&nbsp;the&nbsp;notification&nbsp;('moderate'<br>
+&nbsp;&nbsp;or&nbsp;'critical').<br>
+&nbsp;&nbsp;timeout:&nbsp;The&nbsp;timeout&nbsp;in&nbsp;seconds.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;MemoryTimeoutException:&nbsp;If&nbsp;more&nbsp;than&nbsp;|timeout|&nbsp;seconds&nbsp;has&nbsp;passed<br>
+&nbsp;&nbsp;since&nbsp;the&nbsp;last&nbsp;time&nbsp;any&nbsp;data&nbsp;is&nbsp;received.<br>
+&nbsp;&nbsp;MemoryUnrecoverableException:&nbsp;If&nbsp;there&nbsp;is&nbsp;a&nbsp;websocket&nbsp;error.<br>
+&nbsp;&nbsp;MemoryUnexpectedResponseException:&nbsp;If&nbsp;the&nbsp;response&nbsp;contains&nbsp;an&nbsp;error<br>
+&nbsp;&nbsp;or&nbsp;does&nbsp;not&nbsp;contain&nbsp;the&nbsp;expected&nbsp;result.</tt></dd></dl>
+
+<dl><dt><a name="DevToolsClientBackend-StartChromeTracing"><strong>StartChromeTracing</strong></a>(self, trace_options, custom_categories<font color="#909090">=None</font>, timeout<font color="#909090">=10</font>)</dt><dd><tt>Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;trace_options:&nbsp;An&nbsp;tracing_options.TracingOptions&nbsp;instance.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;custom_categories:&nbsp;An&nbsp;optional&nbsp;string&nbsp;containing&nbsp;a&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;comma&nbsp;separated&nbsp;categories&nbsp;that&nbsp;will&nbsp;be&nbsp;traced<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;instead&nbsp;of&nbsp;the&nbsp;default&nbsp;category&nbsp;set.&nbsp;&nbsp;Example:&nbsp;use<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"webkit,cc,disabled-by-default-cc.debug"&nbsp;to&nbsp;trace&nbsp;only<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;those&nbsp;three&nbsp;event&nbsp;categories.</tt></dd></dl>
+
+<dl><dt><a name="DevToolsClientBackend-StopChromeTracing"><strong>StopChromeTracing</strong></a>(self, trace_data_builder, timeout<font color="#909090">=30</font>)</dt></dl>
+
+<dl><dt><a name="DevToolsClientBackend-__init__"><strong>__init__</strong></a>(self, devtools_port, remote_devtools_port, app_backend)</dt><dd><tt>Creates&nbsp;a&nbsp;new&nbsp;<a href="#DevToolsClientBackend">DevToolsClientBackend</a>.<br>
+&nbsp;<br>
+A&nbsp;DevTools&nbsp;agent&nbsp;must&nbsp;exist&nbsp;on&nbsp;the&nbsp;given&nbsp;devtools_port.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;devtools_port:&nbsp;The&nbsp;port&nbsp;to&nbsp;use&nbsp;to&nbsp;connect&nbsp;to&nbsp;DevTools&nbsp;agent.<br>
+&nbsp;&nbsp;remote_devtools_port:&nbsp;In&nbsp;some&nbsp;cases&nbsp;(e.g.,&nbsp;app&nbsp;running&nbsp;on<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Android&nbsp;device,&nbsp;devtools_port&nbsp;is&nbsp;the&nbsp;forwarded&nbsp;port&nbsp;on&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;host&nbsp;platform.&nbsp;We&nbsp;also&nbsp;need&nbsp;to&nbsp;know&nbsp;the&nbsp;remote_devtools_port<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;so&nbsp;that&nbsp;we&nbsp;can&nbsp;uniquely&nbsp;identify&nbsp;the&nbsp;DevTools&nbsp;agent.<br>
+&nbsp;&nbsp;app_backend:&nbsp;For&nbsp;the&nbsp;app&nbsp;that&nbsp;contains&nbsp;the&nbsp;DevTools&nbsp;agent.</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>is_tracing_running</strong></dt>
+</dl>
+<dl><dt><strong>remote_port</strong></dt>
+</dl>
+<dl><dt><strong>support_startup_tracing</strong></dt>
+</dl>
+<dl><dt><strong>supports_overriding_memory_pressure_notifications</strong></dt>
+</dl>
+<dl><dt><strong>supports_tracing</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TabNotFoundError">class <strong>TabNotFoundError</strong></a>(<a href="telemetry.core.exceptions.html#Error">telemetry.core.exceptions.Error</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.chrome_inspector.devtools_client_backend.html#TabNotFoundError">TabNotFoundError</a></dd>
+<dd><a href="telemetry.core.exceptions.html#Error">telemetry.core.exceptions.Error</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.core.exceptions.html#Error">telemetry.core.exceptions.Error</a>:<br>
+<dl><dt><a name="TabNotFoundError-AddDebuggingMessage"><strong>AddDebuggingMessage</strong></a>(self, msg)</dt><dd><tt>Adds&nbsp;a&nbsp;message&nbsp;to&nbsp;the&nbsp;description&nbsp;of&nbsp;the&nbsp;exception.<br>
+&nbsp;<br>
+Many&nbsp;Telemetry&nbsp;exceptions&nbsp;arise&nbsp;from&nbsp;failures&nbsp;in&nbsp;another&nbsp;application.&nbsp;These<br>
+failures&nbsp;are&nbsp;difficult&nbsp;to&nbsp;pinpoint.&nbsp;This&nbsp;method&nbsp;allows&nbsp;Telemetry&nbsp;classes&nbsp;to<br>
+append&nbsp;useful&nbsp;debugging&nbsp;information&nbsp;to&nbsp;the&nbsp;exception.&nbsp;This&nbsp;method&nbsp;also&nbsp;logs<br>
+information&nbsp;about&nbsp;the&nbsp;location&nbsp;from&nbsp;where&nbsp;it&nbsp;was&nbsp;called.</tt></dd></dl>
+
+<dl><dt><a name="TabNotFoundError-__init__"><strong>__init__</strong></a>(self, msg<font color="#909090">=''</font>)</dt></dl>
+
+<dl><dt><a name="TabNotFoundError-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.core.exceptions.html#Error">telemetry.core.exceptions.Error</a>:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#TabNotFoundError-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="TabNotFoundError-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#TabNotFoundError-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="TabNotFoundError-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#TabNotFoundError-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="TabNotFoundError-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#TabNotFoundError-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="TabNotFoundError-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#TabNotFoundError-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="TabNotFoundError-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="TabNotFoundError-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#TabNotFoundError-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="TabNotFoundError-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#TabNotFoundError-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="TabNotFoundError-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="TabNotFoundError-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-IsDevToolsAgentAvailable"><strong>IsDevToolsAgentAvailable</strong></a>(port, app_backend)</dt><dd><tt>Returns&nbsp;True&nbsp;if&nbsp;a&nbsp;DevTools&nbsp;agent&nbsp;is&nbsp;available&nbsp;on&nbsp;the&nbsp;given&nbsp;port.</tt></dd></dl>
+</td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>BROWSER_INSPECTOR_WEBSOCKET_URL</strong> = 'ws://127.0.0.1:%i/devtools/browser'</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome_inspector.devtools_http.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome_inspector.devtools_http.html
new file mode 100644
index 0000000..03dfd8b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome_inspector.devtools_http.html
@@ -0,0 +1,240 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.chrome_inspector.devtools_http</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.<a href="telemetry.internal.backends.chrome_inspector.html"><font color="#ffffff">chrome_inspector</font></a>.devtools_http</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/chrome_inspector/devtools_http.py">telemetry/internal/backends/chrome_inspector/devtools_http.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="errno.html">errno</a><br>
+<a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+</td><td width="25%" valign=top><a href="httplib.html">httplib</a><br>
+<a href="json.html">json</a><br>
+</td><td width="25%" valign=top><a href="socket.html">socket</a><br>
+<a href="sys.html">sys</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome_inspector.devtools_http.html#DevToolsHttp">DevToolsHttp</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="telemetry.core.exceptions.html#Error">telemetry.core.exceptions.Error</a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome_inspector.devtools_http.html#DevToolsClientConnectionError">DevToolsClientConnectionError</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome_inspector.devtools_http.html#DevToolsClientUrlError">DevToolsClientUrlError</a>
+</font></dt></dl>
+</dd>
+</dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="DevToolsClientConnectionError">class <strong>DevToolsClientConnectionError</strong></a>(<a href="telemetry.core.exceptions.html#Error">telemetry.core.exceptions.Error</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.chrome_inspector.devtools_http.html#DevToolsClientConnectionError">DevToolsClientConnectionError</a></dd>
+<dd><a href="telemetry.core.exceptions.html#Error">telemetry.core.exceptions.Error</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.core.exceptions.html#Error">telemetry.core.exceptions.Error</a>:<br>
+<dl><dt><a name="DevToolsClientConnectionError-AddDebuggingMessage"><strong>AddDebuggingMessage</strong></a>(self, msg)</dt><dd><tt>Adds&nbsp;a&nbsp;message&nbsp;to&nbsp;the&nbsp;description&nbsp;of&nbsp;the&nbsp;exception.<br>
+&nbsp;<br>
+Many&nbsp;Telemetry&nbsp;exceptions&nbsp;arise&nbsp;from&nbsp;failures&nbsp;in&nbsp;another&nbsp;application.&nbsp;These<br>
+failures&nbsp;are&nbsp;difficult&nbsp;to&nbsp;pinpoint.&nbsp;This&nbsp;method&nbsp;allows&nbsp;Telemetry&nbsp;classes&nbsp;to<br>
+append&nbsp;useful&nbsp;debugging&nbsp;information&nbsp;to&nbsp;the&nbsp;exception.&nbsp;This&nbsp;method&nbsp;also&nbsp;logs<br>
+information&nbsp;about&nbsp;the&nbsp;location&nbsp;from&nbsp;where&nbsp;it&nbsp;was&nbsp;called.</tt></dd></dl>
+
+<dl><dt><a name="DevToolsClientConnectionError-__init__"><strong>__init__</strong></a>(self, msg<font color="#909090">=''</font>)</dt></dl>
+
+<dl><dt><a name="DevToolsClientConnectionError-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.core.exceptions.html#Error">telemetry.core.exceptions.Error</a>:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#DevToolsClientConnectionError-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="DevToolsClientConnectionError-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#DevToolsClientConnectionError-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="DevToolsClientConnectionError-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#DevToolsClientConnectionError-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="DevToolsClientConnectionError-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#DevToolsClientConnectionError-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="DevToolsClientConnectionError-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#DevToolsClientConnectionError-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="DevToolsClientConnectionError-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="DevToolsClientConnectionError-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#DevToolsClientConnectionError-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="DevToolsClientConnectionError-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#DevToolsClientConnectionError-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="DevToolsClientConnectionError-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="DevToolsClientConnectionError-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="DevToolsClientUrlError">class <strong>DevToolsClientUrlError</strong></a>(<a href="telemetry.internal.backends.chrome_inspector.devtools_http.html#DevToolsClientConnectionError">DevToolsClientConnectionError</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.chrome_inspector.devtools_http.html#DevToolsClientUrlError">DevToolsClientUrlError</a></dd>
+<dd><a href="telemetry.internal.backends.chrome_inspector.devtools_http.html#DevToolsClientConnectionError">DevToolsClientConnectionError</a></dd>
+<dd><a href="telemetry.core.exceptions.html#Error">telemetry.core.exceptions.Error</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.core.exceptions.html#Error">telemetry.core.exceptions.Error</a>:<br>
+<dl><dt><a name="DevToolsClientUrlError-AddDebuggingMessage"><strong>AddDebuggingMessage</strong></a>(self, msg)</dt><dd><tt>Adds&nbsp;a&nbsp;message&nbsp;to&nbsp;the&nbsp;description&nbsp;of&nbsp;the&nbsp;exception.<br>
+&nbsp;<br>
+Many&nbsp;Telemetry&nbsp;exceptions&nbsp;arise&nbsp;from&nbsp;failures&nbsp;in&nbsp;another&nbsp;application.&nbsp;These<br>
+failures&nbsp;are&nbsp;difficult&nbsp;to&nbsp;pinpoint.&nbsp;This&nbsp;method&nbsp;allows&nbsp;Telemetry&nbsp;classes&nbsp;to<br>
+append&nbsp;useful&nbsp;debugging&nbsp;information&nbsp;to&nbsp;the&nbsp;exception.&nbsp;This&nbsp;method&nbsp;also&nbsp;logs<br>
+information&nbsp;about&nbsp;the&nbsp;location&nbsp;from&nbsp;where&nbsp;it&nbsp;was&nbsp;called.</tt></dd></dl>
+
+<dl><dt><a name="DevToolsClientUrlError-__init__"><strong>__init__</strong></a>(self, msg<font color="#909090">=''</font>)</dt></dl>
+
+<dl><dt><a name="DevToolsClientUrlError-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.core.exceptions.html#Error">telemetry.core.exceptions.Error</a>:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#DevToolsClientUrlError-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="DevToolsClientUrlError-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#DevToolsClientUrlError-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="DevToolsClientUrlError-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#DevToolsClientUrlError-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="DevToolsClientUrlError-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#DevToolsClientUrlError-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="DevToolsClientUrlError-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#DevToolsClientUrlError-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="DevToolsClientUrlError-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="DevToolsClientUrlError-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#DevToolsClientUrlError-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="DevToolsClientUrlError-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#DevToolsClientUrlError-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="DevToolsClientUrlError-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="DevToolsClientUrlError-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="DevToolsHttp">class <strong>DevToolsHttp</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;helper&nbsp;class&nbsp;to&nbsp;send&nbsp;and&nbsp;parse&nbsp;DevTools&nbsp;HTTP&nbsp;requests.<br>
+&nbsp;<br>
+This&nbsp;class&nbsp;maintains&nbsp;a&nbsp;persistent&nbsp;http&nbsp;connection&nbsp;to&nbsp;Chrome&nbsp;devtools.<br>
+Ideally,&nbsp;owners&nbsp;of&nbsp;instances&nbsp;of&nbsp;this&nbsp;class&nbsp;should&nbsp;call&nbsp;<a href="#DevToolsHttp-Disconnect">Disconnect</a>()&nbsp;before<br>
+disposing&nbsp;of&nbsp;the&nbsp;instance.&nbsp;Otherwise,&nbsp;the&nbsp;connection&nbsp;will&nbsp;not&nbsp;be&nbsp;closed&nbsp;until<br>
+the&nbsp;instance&nbsp;is&nbsp;garbage&nbsp;collected.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="DevToolsHttp-Disconnect"><strong>Disconnect</strong></a>(self)</dt><dd><tt>Closes&nbsp;the&nbsp;HTTP&nbsp;connection.</tt></dd></dl>
+
+<dl><dt><a name="DevToolsHttp-Request"><strong>Request</strong></a>(self, path, timeout<font color="#909090">=30</font>)</dt><dd><tt>Sends&nbsp;a&nbsp;request&nbsp;to&nbsp;Chrome&nbsp;devtools.<br>
+&nbsp;<br>
+This&nbsp;method&nbsp;lazily&nbsp;creates&nbsp;an&nbsp;HTTP&nbsp;connection,&nbsp;if&nbsp;one&nbsp;does&nbsp;not&nbsp;already<br>
+exist.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;path:&nbsp;The&nbsp;DevTools&nbsp;URL&nbsp;path,&nbsp;without&nbsp;the&nbsp;/json/&nbsp;prefix.<br>
+&nbsp;&nbsp;timeout:&nbsp;Timeout&nbsp;defaults&nbsp;to&nbsp;30&nbsp;seconds.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;<a href="#DevToolsClientConnectionError">DevToolsClientConnectionError</a>:&nbsp;If&nbsp;the&nbsp;connection&nbsp;fails.</tt></dd></dl>
+
+<dl><dt><a name="DevToolsHttp-RequestJson"><strong>RequestJson</strong></a>(self, path, timeout<font color="#909090">=30</font>)</dt><dd><tt>Sends&nbsp;a&nbsp;request&nbsp;and&nbsp;parse&nbsp;the&nbsp;response&nbsp;as&nbsp;JSON.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;path:&nbsp;The&nbsp;DevTools&nbsp;URL&nbsp;path,&nbsp;without&nbsp;the&nbsp;/json/&nbsp;prefix.<br>
+&nbsp;&nbsp;timeout:&nbsp;Timeout&nbsp;defaults&nbsp;to&nbsp;30&nbsp;seconds.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;<a href="#DevToolsClientConnectionError">DevToolsClientConnectionError</a>:&nbsp;If&nbsp;the&nbsp;connection&nbsp;fails.<br>
+&nbsp;&nbsp;ValueError:&nbsp;If&nbsp;the&nbsp;response&nbsp;is&nbsp;not&nbsp;a&nbsp;valid&nbsp;JSON.</tt></dd></dl>
+
+<dl><dt><a name="DevToolsHttp-__del__"><strong>__del__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DevToolsHttp-__init__"><strong>__init__</strong></a>(self, devtools_port)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome_inspector.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome_inspector.html
new file mode 100644
index 0000000..0fc43b0
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome_inspector.html
@@ -0,0 +1,47 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: package telemetry.internal.backends.chrome_inspector</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.chrome_inspector</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/chrome_inspector/__init__.py">telemetry/internal/backends/chrome_inspector/__init__.py</a></font></td></tr></table>
+    <p><tt>This&nbsp;package&nbsp;contains&nbsp;classes&nbsp;and&nbsp;methods&nbsp;for&nbsp;controlling&nbsp;the&nbsp;chrome<br>
+devtool.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Package Contents</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.backends.chrome_inspector.devtools_client_backend.html">devtools_client_backend</a><br>
+<a href="telemetry.internal.backends.chrome_inspector.devtools_client_backend_unittest.html">devtools_client_backend_unittest</a><br>
+<a href="telemetry.internal.backends.chrome_inspector.devtools_http.html">devtools_http</a><br>
+<a href="telemetry.internal.backends.chrome_inspector.devtools_http_unittest.html">devtools_http_unittest</a><br>
+<a href="telemetry.internal.backends.chrome_inspector.inspector_backend.html">inspector_backend</a><br>
+<a href="telemetry.internal.backends.chrome_inspector.inspector_backend_list.html">inspector_backend_list</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.backends.chrome_inspector.inspector_console.html">inspector_console</a><br>
+<a href="telemetry.internal.backends.chrome_inspector.inspector_console_unittest.html">inspector_console_unittest</a><br>
+<a href="telemetry.internal.backends.chrome_inspector.inspector_memory.html">inspector_memory</a><br>
+<a href="telemetry.internal.backends.chrome_inspector.inspector_memory_unittest.html">inspector_memory_unittest</a><br>
+<a href="telemetry.internal.backends.chrome_inspector.inspector_network.html">inspector_network</a><br>
+<a href="telemetry.internal.backends.chrome_inspector.inspector_network_unittest.html">inspector_network_unittest</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.backends.chrome_inspector.inspector_page.html">inspector_page</a><br>
+<a href="telemetry.internal.backends.chrome_inspector.inspector_page_unittest.html">inspector_page_unittest</a><br>
+<a href="telemetry.internal.backends.chrome_inspector.inspector_runtime.html">inspector_runtime</a><br>
+<a href="telemetry.internal.backends.chrome_inspector.inspector_runtime_unittest.html">inspector_runtime_unittest</a><br>
+<a href="telemetry.internal.backends.chrome_inspector.inspector_websocket.html">inspector_websocket</a><br>
+<a href="telemetry.internal.backends.chrome_inspector.inspector_websocket_unittest.html">inspector_websocket_unittest</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.backends.chrome_inspector.memory_backend.html">memory_backend</a><br>
+<a href="telemetry.internal.backends.chrome_inspector.memory_backend_unittest.html">memory_backend_unittest</a><br>
+<a href="telemetry.internal.backends.chrome_inspector.tracing_backend.html">tracing_backend</a><br>
+<a href="telemetry.internal.backends.chrome_inspector.tracing_backend_unittest.html">tracing_backend_unittest</a><br>
+<a href="telemetry.internal.backends.chrome_inspector.websocket.html">websocket</a><br>
+<a href="telemetry.internal.backends.chrome_inspector.websocket_unittest.html">websocket_unittest</a><br>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome_inspector.inspector_backend.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome_inspector.inspector_backend.html
new file mode 100644
index 0000000..cab059e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome_inspector.inspector_backend.html
@@ -0,0 +1,177 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.chrome_inspector.inspector_backend</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.<a href="telemetry.internal.backends.chrome_inspector.html"><font color="#ffffff">chrome_inspector</font></a>.inspector_backend</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/chrome_inspector/inspector_backend.py">telemetry/internal/backends/chrome_inspector/inspector_backend.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.decorators.html">telemetry.decorators</a><br>
+<a href="telemetry.internal.backends.chrome_inspector.devtools_http.html">telemetry.internal.backends.chrome_inspector.devtools_http</a><br>
+<a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+<a href="functools.html">functools</a><br>
+<a href="telemetry.internal.backends.chrome_inspector.inspector_console.html">telemetry.internal.backends.chrome_inspector.inspector_console</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.backends.chrome_inspector.inspector_memory.html">telemetry.internal.backends.chrome_inspector.inspector_memory</a><br>
+<a href="telemetry.internal.backends.chrome_inspector.inspector_network.html">telemetry.internal.backends.chrome_inspector.inspector_network</a><br>
+<a href="telemetry.internal.backends.chrome_inspector.inspector_page.html">telemetry.internal.backends.chrome_inspector.inspector_page</a><br>
+<a href="telemetry.internal.backends.chrome_inspector.inspector_runtime.html">telemetry.internal.backends.chrome_inspector.inspector_runtime</a><br>
+<a href="telemetry.internal.backends.chrome_inspector.inspector_websocket.html">telemetry.internal.backends.chrome_inspector.inspector_websocket</a><br>
+</td><td width="25%" valign=top><a href="logging.html">logging</a><br>
+<a href="os.html">os</a><br>
+<a href="socket.html">socket</a><br>
+<a href="sys.html">sys</a><br>
+<a href="telemetry.timeline.model.html">telemetry.timeline.model</a><br>
+</td><td width="25%" valign=top><a href="telemetry.timeline.trace_data.html">telemetry.timeline.trace_data</a><br>
+<a href="telemetry.internal.backends.chrome_inspector.websocket.html">telemetry.internal.backends.chrome_inspector.websocket</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome_inspector.inspector_backend.html#InspectorBackend">InspectorBackend</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="InspectorBackend">class <strong>InspectorBackend</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Class&nbsp;for&nbsp;communicating&nbsp;with&nbsp;a&nbsp;devtools&nbsp;client.<br>
+&nbsp;<br>
+The&nbsp;owner&nbsp;of&nbsp;an&nbsp;instance&nbsp;of&nbsp;this&nbsp;class&nbsp;is&nbsp;responsible&nbsp;for&nbsp;calling<br>
+<a href="#InspectorBackend-Disconnect">Disconnect</a>()&nbsp;before&nbsp;disposing&nbsp;of&nbsp;the&nbsp;instance.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="InspectorBackend-ClearCache"><strong>ClearCache</strong></a>(inspector_backend, *args, **kwargs)</dt></dl>
+
+<dl><dt><a name="InspectorBackend-CollectGarbage"><strong>CollectGarbage</strong></a>(inspector_backend, *args, **kwargs)</dt></dl>
+
+<dl><dt><a name="InspectorBackend-Disconnect"><strong>Disconnect</strong></a>(self)</dt><dd><tt>Disconnects&nbsp;the&nbsp;inspector&nbsp;websocket.<br>
+&nbsp;<br>
+This&nbsp;method&nbsp;intentionally&nbsp;leaves&nbsp;the&nbsp;self.<strong>_websocket</strong>&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;around,&nbsp;so&nbsp;that<br>
+future&nbsp;calls&nbsp;it&nbsp;to&nbsp;it&nbsp;will&nbsp;fail&nbsp;with&nbsp;a&nbsp;relevant&nbsp;error.</tt></dd></dl>
+
+<dl><dt><a name="InspectorBackend-EnableAllContexts"><strong>EnableAllContexts</strong></a>(inspector_backend, *args, **kwargs)</dt><dd><tt>Allows&nbsp;access&nbsp;to&nbsp;iframes.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.WebSocketDisconnected<br>
+&nbsp;&nbsp;exceptions.TimeoutException<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException</tt></dd></dl>
+
+<dl><dt><a name="InspectorBackend-EvaluateJavaScript"><strong>EvaluateJavaScript</strong></a>(inspector_backend, *args, **kwargs)</dt><dd><tt>Evaluates&nbsp;a&nbsp;javascript&nbsp;expression&nbsp;and&nbsp;returns&nbsp;the&nbsp;result.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.EvaluateException<br>
+&nbsp;&nbsp;exceptions.WebSocketDisconnected<br>
+&nbsp;&nbsp;exceptions.TimeoutException<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException</tt></dd></dl>
+
+<dl><dt><a name="InspectorBackend-ExecuteJavaScript"><strong>ExecuteJavaScript</strong></a>(inspector_backend, *args, **kwargs)</dt><dd><tt>Executes&nbsp;a&nbsp;javascript&nbsp;expression&nbsp;without&nbsp;returning&nbsp;the&nbsp;result.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.EvaluateException<br>
+&nbsp;&nbsp;exceptions.WebSocketDisconnected<br>
+&nbsp;&nbsp;exceptions.TimeoutException<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException</tt></dd></dl>
+
+<dl><dt><a name="InspectorBackend-GetCookieByName"><strong>GetCookieByName</strong></a>(inspector_backend, *args, **kwargs)</dt></dl>
+
+<dl><dt><a name="InspectorBackend-GetDOMStats"><strong>GetDOMStats</strong></a>(inspector_backend, *args, **kwargs)</dt><dd><tt>Gets&nbsp;memory&nbsp;stats&nbsp;from&nbsp;the&nbsp;DOM.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;inspector_memory.InspectorMemoryException<br>
+&nbsp;&nbsp;exceptions.TimeoutException<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException</tt></dd></dl>
+
+<dl><dt><a name="InspectorBackend-GetWebviewInspectorBackends"><strong>GetWebviewInspectorBackends</strong></a>(self)</dt><dd><tt>Returns&nbsp;a&nbsp;list&nbsp;of&nbsp;<a href="#InspectorBackend">InspectorBackend</a>&nbsp;instances&nbsp;associated&nbsp;with&nbsp;webviews.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;devtools_http.DevToolsClientConnectionError</tt></dd></dl>
+
+<dl><dt><a name="InspectorBackend-IsInspectable"><strong>IsInspectable</strong></a>(self)</dt><dd><tt>Whether&nbsp;the&nbsp;tab&nbsp;is&nbsp;inspectable,&nbsp;as&nbsp;reported&nbsp;by&nbsp;devtools.</tt></dd></dl>
+
+<dl><dt><a name="InspectorBackend-Navigate"><strong>Navigate</strong></a>(inspector_backend, *args, **kwargs)</dt></dl>
+
+<dl><dt><a name="InspectorBackend-Screenshot"><strong>Screenshot</strong></a>(inspector_backend, *args, **kwargs)</dt></dl>
+
+<dl><dt><a name="InspectorBackend-StartTimelineRecording"><strong>StartTimelineRecording</strong></a>(inspector_backend, *args, **kwargs)</dt></dl>
+
+<dl><dt><a name="InspectorBackend-StopTimelineRecording"><strong>StopTimelineRecording</strong></a>(inspector_backend, *args, **kwargs)</dt></dl>
+
+<dl><dt><a name="InspectorBackend-SynthesizeScrollGesture"><strong>SynthesizeScrollGesture</strong></a>(inspector_backend, *args, **kwargs)</dt><dd><tt>Runs&nbsp;an&nbsp;inspector&nbsp;command&nbsp;that&nbsp;causes&nbsp;a&nbsp;repeatable&nbsp;browser&nbsp;driven&nbsp;scroll.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;x:&nbsp;X&nbsp;coordinate&nbsp;of&nbsp;the&nbsp;start&nbsp;of&nbsp;the&nbsp;gesture&nbsp;in&nbsp;CSS&nbsp;pixels.<br>
+&nbsp;&nbsp;y:&nbsp;Y&nbsp;coordinate&nbsp;of&nbsp;the&nbsp;start&nbsp;of&nbsp;the&nbsp;gesture&nbsp;in&nbsp;CSS&nbsp;pixels.<br>
+&nbsp;&nbsp;xDistance:&nbsp;Distance&nbsp;to&nbsp;scroll&nbsp;along&nbsp;the&nbsp;X&nbsp;axis&nbsp;(positive&nbsp;to&nbsp;scroll&nbsp;left).<br>
+&nbsp;&nbsp;yDistance:&nbsp;Distance&nbsp;to&nbsp;scroll&nbsp;along&nbsp;the&nbsp;Y&nbsp;axis&nbsp;(positive&nbsp;to&nbsp;scroll&nbsp;up).<br>
+&nbsp;&nbsp;xOverscroll:&nbsp;Number&nbsp;of&nbsp;additional&nbsp;pixels&nbsp;to&nbsp;scroll&nbsp;back&nbsp;along&nbsp;the&nbsp;X&nbsp;axis.<br>
+&nbsp;&nbsp;xOverscroll:&nbsp;Number&nbsp;of&nbsp;additional&nbsp;pixels&nbsp;to&nbsp;scroll&nbsp;back&nbsp;along&nbsp;the&nbsp;Y&nbsp;axis.<br>
+&nbsp;&nbsp;preventFling:&nbsp;Prevents&nbsp;a&nbsp;fling&nbsp;gesture.<br>
+&nbsp;&nbsp;speed:&nbsp;Swipe&nbsp;speed&nbsp;in&nbsp;pixels&nbsp;per&nbsp;second.<br>
+&nbsp;&nbsp;gestureSourceType:&nbsp;Which&nbsp;type&nbsp;of&nbsp;input&nbsp;events&nbsp;to&nbsp;be&nbsp;generated.<br>
+&nbsp;&nbsp;repeatCount:&nbsp;Number&nbsp;of&nbsp;additional&nbsp;repeats&nbsp;beyond&nbsp;the&nbsp;first&nbsp;scroll.<br>
+&nbsp;&nbsp;repeatDelayMs:&nbsp;Number&nbsp;of&nbsp;milliseconds&nbsp;delay&nbsp;between&nbsp;each&nbsp;repeat.<br>
+&nbsp;&nbsp;interactionMarkerName:&nbsp;The&nbsp;name&nbsp;of&nbsp;the&nbsp;interaction&nbsp;markers&nbsp;to&nbsp;generate.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.TimeoutException<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException</tt></dd></dl>
+
+<dl><dt><a name="InspectorBackend-WaitForNavigate"><strong>WaitForNavigate</strong></a>(inspector_backend, *args, **kwargs)</dt></dl>
+
+<dl><dt><a name="InspectorBackend-__del__"><strong>__del__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="InspectorBackend-__init__"><strong>__init__</strong></a>(self, app, devtools_client, context, timeout<font color="#909090">=60</font>)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>app</strong></dt>
+</dl>
+<dl><dt><strong>debugger_url</strong></dt>
+</dl>
+<dl><dt><strong>id</strong></dt>
+</dl>
+<dl><dt><strong>message_output_stream</strong></dt>
+</dl>
+<dl><dt><strong>screenshot_supported</strong></dt>
+</dl>
+<dl><dt><strong>timeline_model</strong></dt>
+</dl>
+<dl><dt><strong>url</strong></dt>
+<dd><tt>Returns&nbsp;the&nbsp;URL&nbsp;of&nbsp;the&nbsp;tab,&nbsp;as&nbsp;reported&nbsp;by&nbsp;devtools.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;devtools_http.DevToolsClientConnectionError</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome_inspector.inspector_backend_list.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome_inspector.inspector_backend_list.html
new file mode 100644
index 0000000..6424181
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome_inspector.inspector_backend_list.html
@@ -0,0 +1,141 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.chrome_inspector.inspector_backend_list</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.<a href="telemetry.internal.backends.chrome_inspector.html"><font color="#ffffff">chrome_inspector</font></a>.inspector_backend_list</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/chrome_inspector/inspector_backend_list.py">telemetry/internal/backends/chrome_inspector/inspector_backend_list.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="collections.html">collections</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="_abcoll.html#Sequence">_abcoll.Sequence</a>(<a href="_abcoll.html#Sized">_abcoll.Sized</a>, <a href="_abcoll.html#Iterable">_abcoll.Iterable</a>, <a href="_abcoll.html#Container">_abcoll.Container</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome_inspector.inspector_backend_list.html#InspectorBackendList">InspectorBackendList</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="InspectorBackendList">class <strong>InspectorBackendList</strong></a>(<a href="_abcoll.html#Sequence">_abcoll.Sequence</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;dynamic&nbsp;sequence&nbsp;of&nbsp;active&nbsp;InspectorBackends.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.chrome_inspector.inspector_backend_list.html#InspectorBackendList">InspectorBackendList</a></dd>
+<dd><a href="_abcoll.html#Sequence">_abcoll.Sequence</a></dd>
+<dd><a href="_abcoll.html#Sized">_abcoll.Sized</a></dd>
+<dd><a href="_abcoll.html#Iterable">_abcoll.Iterable</a></dd>
+<dd><a href="_abcoll.html#Container">_abcoll.Container</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="InspectorBackendList-CreateWrapper"><strong>CreateWrapper</strong></a>(self, inspector_backend_instance)</dt><dd><tt>Override&nbsp;to&nbsp;return&nbsp;the&nbsp;wrapper&nbsp;API&nbsp;over&nbsp;InspectorBackend.<br>
+&nbsp;<br>
+The&nbsp;wrapper&nbsp;API&nbsp;is&nbsp;the&nbsp;public&nbsp;interface&nbsp;for&nbsp;InspectorBackend.&nbsp;It<br>
+may&nbsp;expose&nbsp;whatever&nbsp;methods&nbsp;are&nbsp;desired&nbsp;on&nbsp;top&nbsp;of&nbsp;that&nbsp;backend.</tt></dd></dl>
+
+<dl><dt><a name="InspectorBackendList-GetBackendFromContextId"><strong>GetBackendFromContextId</strong></a>(self, context_id)</dt></dl>
+
+<dl><dt><a name="InspectorBackendList-GetContextInfo"><strong>GetContextInfo</strong></a>(self, context_id)</dt></dl>
+
+<dl><dt><a name="InspectorBackendList-GetTabById"><strong>GetTabById</strong></a>(self, identifier)</dt></dl>
+
+<dl><dt><a name="InspectorBackendList-IterContextIds"><strong>IterContextIds</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="InspectorBackendList-ShouldIncludeContext"><strong>ShouldIncludeContext</strong></a>(self, _)</dt><dd><tt>Override&nbsp;this&nbsp;method&nbsp;to&nbsp;control&nbsp;which&nbsp;contexts&nbsp;are&nbsp;included.</tt></dd></dl>
+
+<dl><dt><a name="InspectorBackendList-__getitem__"><strong>__getitem__</strong></a>(self, index)</dt><dd><tt>#&nbsp;TODO(nednguyen):&nbsp;Remove&nbsp;this&nbsp;method&nbsp;and&nbsp;turn&nbsp;inspector_backend_list&nbsp;API&nbsp;to<br>
+#&nbsp;dictionary-like&nbsp;API&nbsp;(crbug.com/398467)</tt></dd></dl>
+
+<dl><dt><a name="InspectorBackendList-__init__"><strong>__init__</strong></a>(self, browser_backend)</dt><dd><tt>Constructor.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;browser_backend:&nbsp;The&nbsp;BrowserBackend&nbsp;instance&nbsp;to&nbsp;query&nbsp;for<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;InspectorBackends.</tt></dd></dl>
+
+<dl><dt><a name="InspectorBackendList-__iter__"><strong>__iter__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="InspectorBackendList-__len__"><strong>__len__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>app</strong></dt>
+</dl>
+<hr>
+Data and other attributes defined here:<br>
+<dl><dt><strong>__abstractmethods__</strong> = frozenset([])</dl>
+
+<hr>
+Methods inherited from <a href="_abcoll.html#Sequence">_abcoll.Sequence</a>:<br>
+<dl><dt><a name="InspectorBackendList-__contains__"><strong>__contains__</strong></a>(self, value)</dt></dl>
+
+<dl><dt><a name="InspectorBackendList-__reversed__"><strong>__reversed__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="InspectorBackendList-count"><strong>count</strong></a>(self, value)</dt><dd><tt>S.<a href="#InspectorBackendList-count">count</a>(value)&nbsp;-&gt;&nbsp;integer&nbsp;--&nbsp;return&nbsp;number&nbsp;of&nbsp;occurrences&nbsp;of&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="InspectorBackendList-index"><strong>index</strong></a>(self, value)</dt><dd><tt>S.<a href="#InspectorBackendList-index">index</a>(value)&nbsp;-&gt;&nbsp;integer&nbsp;--&nbsp;return&nbsp;first&nbsp;index&nbsp;of&nbsp;value.<br>
+Raises&nbsp;ValueError&nbsp;if&nbsp;the&nbsp;value&nbsp;is&nbsp;not&nbsp;present.</tt></dd></dl>
+
+<hr>
+Class methods inherited from <a href="_abcoll.html#Sized">_abcoll.Sized</a>:<br>
+<dl><dt><a name="InspectorBackendList-__subclasshook__"><strong>__subclasshook__</strong></a>(cls, C)<font color="#909090"><font face="helvetica, arial"> from <a href="abc.html#ABCMeta">abc.ABCMeta</a></font></font></dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="_abcoll.html#Sized">_abcoll.Sized</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="_abcoll.html#Sized">_abcoll.Sized</a>:<br>
+<dl><dt><strong>__metaclass__</strong> = &lt;class 'abc.ABCMeta'&gt;<dd><tt>Metaclass&nbsp;for&nbsp;defining&nbsp;Abstract&nbsp;Base&nbsp;Classes&nbsp;(ABCs).<br>
+&nbsp;<br>
+Use&nbsp;this&nbsp;metaclass&nbsp;to&nbsp;create&nbsp;an&nbsp;ABC.&nbsp;&nbsp;An&nbsp;ABC&nbsp;can&nbsp;be&nbsp;subclassed<br>
+directly,&nbsp;and&nbsp;then&nbsp;acts&nbsp;as&nbsp;a&nbsp;mix-in&nbsp;class.&nbsp;&nbsp;You&nbsp;can&nbsp;also&nbsp;register<br>
+unrelated&nbsp;concrete&nbsp;classes&nbsp;(even&nbsp;built-in&nbsp;classes)&nbsp;and&nbsp;unrelated<br>
+ABCs&nbsp;as&nbsp;'virtual&nbsp;subclasses'&nbsp;--&nbsp;these&nbsp;and&nbsp;their&nbsp;descendants&nbsp;will<br>
+be&nbsp;considered&nbsp;subclasses&nbsp;of&nbsp;the&nbsp;registering&nbsp;ABC&nbsp;by&nbsp;the&nbsp;built-in<br>
+issubclass()&nbsp;function,&nbsp;but&nbsp;the&nbsp;registering&nbsp;ABC&nbsp;won't&nbsp;show&nbsp;up&nbsp;in<br>
+their&nbsp;MRO&nbsp;(Method&nbsp;Resolution&nbsp;Order)&nbsp;nor&nbsp;will&nbsp;method<br>
+implementations&nbsp;defined&nbsp;by&nbsp;the&nbsp;registering&nbsp;ABC&nbsp;be&nbsp;callable&nbsp;(not<br>
+even&nbsp;via&nbsp;super()).</tt></dl>
+
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-DebuggerUrlToId"><strong>DebuggerUrlToId</strong></a>(debugger_url)</dt></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome_inspector.inspector_console.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome_inspector.inspector_console.html
new file mode 100644
index 0000000..7ce3e88
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome_inspector.inspector_console.html
@@ -0,0 +1,52 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.chrome_inspector.inspector_console</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.<a href="telemetry.internal.backends.chrome_inspector.html"><font color="#ffffff">chrome_inspector</font></a>.inspector_console</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/chrome_inspector/inspector_console.py">telemetry/internal/backends/chrome_inspector/inspector_console.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome_inspector.inspector_console.html#InspectorConsole">InspectorConsole</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="InspectorConsole">class <strong>InspectorConsole</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="InspectorConsole-__init__"><strong>__init__</strong></a>(self, inspector_websocket)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>message_output_stream</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome_inspector.inspector_memory.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome_inspector.inspector_memory.html
new file mode 100644
index 0000000..d3648d1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome_inspector.inspector_memory.html
@@ -0,0 +1,148 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.chrome_inspector.inspector_memory</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.<a href="telemetry.internal.backends.chrome_inspector.html"><font color="#ffffff">chrome_inspector</font></a>.inspector_memory</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/chrome_inspector/inspector_memory.py">telemetry/internal/backends/chrome_inspector/inspector_memory.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+</td><td width="25%" valign=top><a href="json.html">json</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome_inspector.inspector_memory.html#InspectorMemory">InspectorMemory</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="telemetry.core.exceptions.html#Error">telemetry.core.exceptions.Error</a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome_inspector.inspector_memory.html#InspectorMemoryException">InspectorMemoryException</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="InspectorMemory">class <strong>InspectorMemory</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Communicates&nbsp;with&nbsp;the&nbsp;remote&nbsp;inspector's&nbsp;Memory&nbsp;domain.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="InspectorMemory-GetDOMCounters"><strong>GetDOMCounters</strong></a>(self, timeout)</dt><dd><tt>Retrieves&nbsp;DOM&nbsp;element&nbsp;counts.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;timeout:&nbsp;The&nbsp;number&nbsp;of&nbsp;seconds&nbsp;to&nbsp;wait&nbsp;for&nbsp;the&nbsp;inspector&nbsp;backend&nbsp;to<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;service&nbsp;the&nbsp;request&nbsp;before&nbsp;timing&nbsp;out.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;A&nbsp;dictionary&nbsp;containing&nbsp;the&nbsp;counts&nbsp;associated&nbsp;with&nbsp;"nodes",&nbsp;"documents",<br>
+&nbsp;&nbsp;and&nbsp;"jsEventListeners".<br>
+Raises:<br>
+&nbsp;&nbsp;<a href="#InspectorMemoryException">InspectorMemoryException</a><br>
+&nbsp;&nbsp;websocket.WebSocketException<br>
+&nbsp;&nbsp;socket.error<br>
+&nbsp;&nbsp;exceptions.WebSocketDisconnected</tt></dd></dl>
+
+<dl><dt><a name="InspectorMemory-__init__"><strong>__init__</strong></a>(self, inspector_websocket)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="InspectorMemoryException">class <strong>InspectorMemoryException</strong></a>(<a href="telemetry.core.exceptions.html#Error">telemetry.core.exceptions.Error</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.chrome_inspector.inspector_memory.html#InspectorMemoryException">InspectorMemoryException</a></dd>
+<dd><a href="telemetry.core.exceptions.html#Error">telemetry.core.exceptions.Error</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.core.exceptions.html#Error">telemetry.core.exceptions.Error</a>:<br>
+<dl><dt><a name="InspectorMemoryException-AddDebuggingMessage"><strong>AddDebuggingMessage</strong></a>(self, msg)</dt><dd><tt>Adds&nbsp;a&nbsp;message&nbsp;to&nbsp;the&nbsp;description&nbsp;of&nbsp;the&nbsp;exception.<br>
+&nbsp;<br>
+Many&nbsp;Telemetry&nbsp;exceptions&nbsp;arise&nbsp;from&nbsp;failures&nbsp;in&nbsp;another&nbsp;application.&nbsp;These<br>
+failures&nbsp;are&nbsp;difficult&nbsp;to&nbsp;pinpoint.&nbsp;This&nbsp;method&nbsp;allows&nbsp;Telemetry&nbsp;classes&nbsp;to<br>
+append&nbsp;useful&nbsp;debugging&nbsp;information&nbsp;to&nbsp;the&nbsp;exception.&nbsp;This&nbsp;method&nbsp;also&nbsp;logs<br>
+information&nbsp;about&nbsp;the&nbsp;location&nbsp;from&nbsp;where&nbsp;it&nbsp;was&nbsp;called.</tt></dd></dl>
+
+<dl><dt><a name="InspectorMemoryException-__init__"><strong>__init__</strong></a>(self, msg<font color="#909090">=''</font>)</dt></dl>
+
+<dl><dt><a name="InspectorMemoryException-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.core.exceptions.html#Error">telemetry.core.exceptions.Error</a>:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#InspectorMemoryException-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="InspectorMemoryException-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#InspectorMemoryException-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="InspectorMemoryException-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#InspectorMemoryException-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="InspectorMemoryException-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#InspectorMemoryException-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="InspectorMemoryException-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#InspectorMemoryException-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="InspectorMemoryException-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="InspectorMemoryException-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#InspectorMemoryException-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="InspectorMemoryException-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#InspectorMemoryException-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="InspectorMemoryException-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="InspectorMemoryException-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome_inspector.inspector_network.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome_inspector.inspector_network.html
new file mode 100644
index 0000000..62e02a3
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome_inspector.inspector_network.html
@@ -0,0 +1,209 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.chrome_inspector.inspector_network</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.<a href="telemetry.internal.backends.chrome_inspector.html"><font color="#ffffff">chrome_inspector</font></a>.inspector_network</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/chrome_inspector/inspector_network.py">telemetry/internal/backends/chrome_inspector/inspector_network.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+</td><td width="25%" valign=top><a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome_inspector.inspector_network.html#InspectorNetwork">InspectorNetwork</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome_inspector.inspector_network.html#InspectorNetworkResponseData">InspectorNetworkResponseData</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome_inspector.inspector_network.html#TimelineRecorder">TimelineRecorder</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="exceptions.html#Exception">exceptions.Exception</a>(<a href="exceptions.html#BaseException">exceptions.BaseException</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome_inspector.inspector_network.html#InspectorNetworkException">InspectorNetworkException</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="InspectorNetwork">class <strong>InspectorNetwork</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="InspectorNetwork-ClearCache"><strong>ClearCache</strong></a>(self, timeout<font color="#909090">=60</font>)</dt><dd><tt>Clears&nbsp;the&nbsp;browser's&nbsp;disk&nbsp;and&nbsp;memory&nbsp;cache.</tt></dd></dl>
+
+<dl><dt><a name="InspectorNetwork-ClearResponseData"><strong>ClearResponseData</strong></a>(self)</dt><dd><tt>Clears&nbsp;recorded&nbsp;HTTP&nbsp;responses.</tt></dd></dl>
+
+<dl><dt><a name="InspectorNetwork-GetHTTPResponseBody"><strong>GetHTTPResponseBody</strong></a>(self, request_id, timeout<font color="#909090">=60</font>)</dt></dl>
+
+<dl><dt><a name="InspectorNetwork-GetResponseData"><strong>GetResponseData</strong></a>(self)</dt><dd><tt>Returns&nbsp;all&nbsp;recorded&nbsp;HTTP&nbsp;responses.</tt></dd></dl>
+
+<dl><dt><a name="InspectorNetwork-HTTPResponseServedFromCache"><strong>HTTPResponseServedFromCache</strong></a>(self, request_id)</dt></dl>
+
+<dl><dt><a name="InspectorNetwork-StartMonitoringNetwork"><strong>StartMonitoringNetwork</strong></a>(self)</dt><dd><tt>Starts&nbsp;monitoring&nbsp;network&nbsp;notifications&nbsp;and&nbsp;recording&nbsp;HTTP&nbsp;responses.</tt></dd></dl>
+
+<dl><dt><a name="InspectorNetwork-StopMonitoringNetwork"><strong>StopMonitoringNetwork</strong></a>(self)</dt><dd><tt>Stops&nbsp;monitoring&nbsp;network&nbsp;notifications&nbsp;and&nbsp;recording&nbsp;HTTP&nbsp;responses.</tt></dd></dl>
+
+<dl><dt><a name="InspectorNetwork-__init__"><strong>__init__</strong></a>(self, inspector_websocket)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>timeline_recorder</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="InspectorNetworkException">class <strong>InspectorNetworkException</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.chrome_inspector.inspector_network.html#InspectorNetworkException">InspectorNetworkException</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="InspectorNetworkException-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#InspectorNetworkException-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#InspectorNetworkException-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="InspectorNetworkException-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#InspectorNetworkException-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="InspectorNetworkException-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#InspectorNetworkException-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="InspectorNetworkException-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#InspectorNetworkException-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="InspectorNetworkException-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#InspectorNetworkException-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="InspectorNetworkException-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="InspectorNetworkException-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#InspectorNetworkException-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="InspectorNetworkException-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#InspectorNetworkException-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="InspectorNetworkException-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="InspectorNetworkException-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#InspectorNetworkException-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="InspectorNetworkException-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="InspectorNetworkResponseData">class <strong>InspectorNetworkResponseData</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="InspectorNetworkResponseData-AsTimelineEvent"><strong>AsTimelineEvent</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="InspectorNetworkResponseData-GetBody"><strong>GetBody</strong></a>(self, timeout<font color="#909090">=60</font>)</dt></dl>
+
+<dl><dt><a name="InspectorNetworkResponseData-GetHeader"><strong>GetHeader</strong></a>(self, name)</dt></dl>
+
+<dl><dt><a name="InspectorNetworkResponseData-__init__"><strong>__init__</strong></a>(self, inspector_network, params)</dt></dl>
+
+<dl><dt><a name="InspectorNetworkResponseData-status_text"><strong>status_text</strong></a>(self)</dt></dl>
+
+<hr>
+Static methods defined here:<br>
+<dl><dt><a name="InspectorNetworkResponseData-FromTimelineEvent"><strong>FromTimelineEvent</strong></a>(event)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>headers</strong></dt>
+</dl>
+<dl><dt><strong>request_headers</strong></dt>
+</dl>
+<dl><dt><strong>request_id</strong></dt>
+</dl>
+<dl><dt><strong>served_from_cache</strong></dt>
+</dl>
+<dl><dt><strong>status</strong></dt>
+</dl>
+<dl><dt><strong>timestamp</strong></dt>
+</dl>
+<dl><dt><strong>timing</strong></dt>
+</dl>
+<dl><dt><strong>url</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TimelineRecorder">class <strong>TimelineRecorder</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="TimelineRecorder-Start"><strong>Start</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TimelineRecorder-Stop"><strong>Stop</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TimelineRecorder-__init__"><strong>__init__</strong></a>(self, inspector_network)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome_inspector.inspector_page.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome_inspector.inspector_page.html
new file mode 100644
index 0000000..b0e110d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome_inspector.inspector_page.html
@@ -0,0 +1,82 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.chrome_inspector.inspector_page</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.<a href="telemetry.internal.backends.chrome_inspector.html"><font color="#ffffff">chrome_inspector</font></a>.inspector_page</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/chrome_inspector/inspector_page.py">telemetry/internal/backends/chrome_inspector/inspector_page.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.util.image_util.html">telemetry.util.image_util</a><br>
+</td><td width="25%" valign=top><a href="time.html">time</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome_inspector.inspector_page.html#InspectorPage">InspectorPage</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="InspectorPage">class <strong>InspectorPage</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Class&nbsp;that&nbsp;controls&nbsp;a&nbsp;page&nbsp;connected&nbsp;by&nbsp;an&nbsp;inspector_websocket.<br>
+&nbsp;<br>
+This&nbsp;class&nbsp;provides&nbsp;utility&nbsp;methods&nbsp;for&nbsp;controlling&nbsp;a&nbsp;page&nbsp;connected&nbsp;by&nbsp;an<br>
+inspector_websocket.&nbsp;It&nbsp;does&nbsp;not&nbsp;perform&nbsp;any&nbsp;exception&nbsp;handling.&nbsp;All<br>
+inspector_websocket&nbsp;exceptions&nbsp;must&nbsp;be&nbsp;handled&nbsp;by&nbsp;the&nbsp;caller.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="InspectorPage-CaptureScreenshot"><strong>CaptureScreenshot</strong></a>(self, timeout<font color="#909090">=60</font>)</dt></dl>
+
+<dl><dt><a name="InspectorPage-CollectGarbage"><strong>CollectGarbage</strong></a>(self, timeout<font color="#909090">=60</font>)</dt></dl>
+
+<dl><dt><a name="InspectorPage-GetCookieByName"><strong>GetCookieByName</strong></a>(self, name, timeout<font color="#909090">=60</font>)</dt><dd><tt>Returns&nbsp;the&nbsp;value&nbsp;of&nbsp;the&nbsp;cookie&nbsp;by&nbsp;the&nbsp;given&nbsp;|name|.</tt></dd></dl>
+
+<dl><dt><a name="InspectorPage-Navigate"><strong>Navigate</strong></a>(self, url, script_to_evaluate_on_commit<font color="#909090">=None</font>, timeout<font color="#909090">=60</font>)</dt><dd><tt>Navigates&nbsp;to&nbsp;|url|.<br>
+&nbsp;<br>
+If&nbsp;|script_to_evaluate_on_commit|&nbsp;is&nbsp;given,&nbsp;the&nbsp;script&nbsp;source&nbsp;string&nbsp;will&nbsp;be<br>
+evaluated&nbsp;when&nbsp;the&nbsp;navigation&nbsp;is&nbsp;committed.&nbsp;This&nbsp;is&nbsp;after&nbsp;the&nbsp;context&nbsp;of<br>
+the&nbsp;page&nbsp;exists,&nbsp;but&nbsp;before&nbsp;any&nbsp;script&nbsp;on&nbsp;the&nbsp;page&nbsp;itself&nbsp;has&nbsp;executed.</tt></dd></dl>
+
+<dl><dt><a name="InspectorPage-WaitForNavigate"><strong>WaitForNavigate</strong></a>(self, timeout<font color="#909090">=60</font>)</dt><dd><tt>Waits&nbsp;for&nbsp;the&nbsp;navigation&nbsp;to&nbsp;complete.<br>
+&nbsp;<br>
+The&nbsp;current&nbsp;page&nbsp;is&nbsp;expect&nbsp;to&nbsp;be&nbsp;in&nbsp;a&nbsp;navigation.&nbsp;This&nbsp;function&nbsp;returns<br>
+when&nbsp;the&nbsp;navigation&nbsp;is&nbsp;complete&nbsp;or&nbsp;when&nbsp;the&nbsp;timeout&nbsp;has&nbsp;been&nbsp;exceeded.</tt></dd></dl>
+
+<dl><dt><a name="InspectorPage-__init__"><strong>__init__</strong></a>(self, inspector_websocket, timeout<font color="#909090">=60</font>)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome_inspector.inspector_runtime.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome_inspector.inspector_runtime.html
new file mode 100644
index 0000000..bcbf564
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome_inspector.inspector_runtime.html
@@ -0,0 +1,85 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.chrome_inspector.inspector_runtime</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.<a href="telemetry.internal.backends.chrome_inspector.html"><font color="#ffffff">chrome_inspector</font></a>.inspector_runtime</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/chrome_inspector/inspector_runtime.py">telemetry/internal/backends/chrome_inspector/inspector_runtime.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome_inspector.inspector_runtime.html#InspectorRuntime">InspectorRuntime</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="InspectorRuntime">class <strong>InspectorRuntime</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="InspectorRuntime-EnableAllContexts"><strong>EnableAllContexts</strong></a>(self)</dt><dd><tt>Allow&nbsp;access&nbsp;to&nbsp;iframes.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.WebSocketDisconnected<br>
+&nbsp;&nbsp;websocket.WebSocketException<br>
+&nbsp;&nbsp;socket.error</tt></dd></dl>
+
+<dl><dt><a name="InspectorRuntime-Evaluate"><strong>Evaluate</strong></a>(self, expr, context_id, timeout)</dt><dd><tt>Evaluates&nbsp;a&nbsp;javascript&nbsp;expression&nbsp;and&nbsp;returns&nbsp;the&nbsp;result.<br>
+&nbsp;<br>
+|context_id|&nbsp;can&nbsp;refer&nbsp;to&nbsp;an&nbsp;iframe.&nbsp;The&nbsp;main&nbsp;page&nbsp;has&nbsp;context_id=1,&nbsp;the<br>
+first&nbsp;iframe&nbsp;context_id=2,&nbsp;etc.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.EvaluateException<br>
+&nbsp;&nbsp;exceptions.WebSocketDisconnected<br>
+&nbsp;&nbsp;websocket.WebSocketException<br>
+&nbsp;&nbsp;socket.error</tt></dd></dl>
+
+<dl><dt><a name="InspectorRuntime-Execute"><strong>Execute</strong></a>(self, expr, context_id, timeout)</dt></dl>
+
+<dl><dt><a name="InspectorRuntime-RunInspectorCommand"><strong>RunInspectorCommand</strong></a>(self, command, timeout)</dt><dd><tt>Runs&nbsp;an&nbsp;inspector&nbsp;command.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.WebSocketDisconnected<br>
+&nbsp;&nbsp;websocket.WebSocketException<br>
+&nbsp;&nbsp;socket.error</tt></dd></dl>
+
+<dl><dt><a name="InspectorRuntime-__init__"><strong>__init__</strong></a>(self, inspector_websocket)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome_inspector.inspector_websocket.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome_inspector.inspector_websocket.html
new file mode 100644
index 0000000..9c1ddda
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome_inspector.inspector_websocket.html
@@ -0,0 +1,200 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.chrome_inspector.inspector_websocket</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.<a href="telemetry.internal.backends.chrome_inspector.html"><font color="#ffffff">chrome_inspector</font></a>.inspector_websocket</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/chrome_inspector/inspector_websocket.py">telemetry/internal/backends/chrome_inspector/inspector_websocket.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="errno.html">errno</a><br>
+<a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+</td><td width="25%" valign=top><a href="json.html">json</a><br>
+<a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top><a href="socket.html">socket</a><br>
+<a href="time.html">time</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.backends.chrome_inspector.websocket.html">telemetry.internal.backends.chrome_inspector.websocket</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome_inspector.inspector_websocket.html#InspectorWebsocket">InspectorWebsocket</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="telemetry.core.exceptions.html#Error">telemetry.core.exceptions.Error</a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome_inspector.inspector_websocket.html#WebSocketDisconnected">WebSocketDisconnected</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="InspectorWebsocket">class <strong>InspectorWebsocket</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="InspectorWebsocket-AsyncRequest"><strong>AsyncRequest</strong></a>(self, req, callback)</dt><dd><tt>Sends&nbsp;an&nbsp;async&nbsp;request&nbsp;and&nbsp;returns&nbsp;immediately.<br>
+&nbsp;<br>
+Response&nbsp;will&nbsp;be&nbsp;handled&nbsp;in&nbsp;the&nbsp;|callback|&nbsp;later&nbsp;when&nbsp;DispatchNotifications<br>
+is&nbsp;invoked.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;callback:&nbsp;a&nbsp;function&nbsp;that&nbsp;takes&nbsp;inspector's&nbsp;response&nbsp;as&nbsp;the&nbsp;argument.</tt></dd></dl>
+
+<dl><dt><a name="InspectorWebsocket-Connect"><strong>Connect</strong></a>(self, url, timeout<font color="#909090">=10</font>)</dt><dd><tt>Connects&nbsp;the&nbsp;websocket.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;websocket.WebSocketException<br>
+&nbsp;&nbsp;socket.error</tt></dd></dl>
+
+<dl><dt><a name="InspectorWebsocket-Disconnect"><strong>Disconnect</strong></a>(self)</dt><dd><tt>Disconnects&nbsp;the&nbsp;inspector&nbsp;websocket.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;websocket.WebSocketException<br>
+&nbsp;&nbsp;socket.error</tt></dd></dl>
+
+<dl><dt><a name="InspectorWebsocket-DispatchNotifications"><strong>DispatchNotifications</strong></a>(self, timeout<font color="#909090">=10</font>)</dt><dd><tt>Waits&nbsp;for&nbsp;responses&nbsp;from&nbsp;the&nbsp;websocket,&nbsp;dispatching&nbsp;them&nbsp;as&nbsp;necessary.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;websocket.WebSocketException:&nbsp;<a href="telemetry.core.exceptions.html#Error">Error</a>&nbsp;from&nbsp;websocket&nbsp;library.<br>
+&nbsp;&nbsp;socket.error:&nbsp;<a href="telemetry.core.exceptions.html#Error">Error</a>&nbsp;from&nbsp;websocket&nbsp;library.<br>
+&nbsp;&nbsp;exceptions.<a href="#WebSocketDisconnected">WebSocketDisconnected</a>:&nbsp;The&nbsp;socket&nbsp;was&nbsp;disconnected.</tt></dd></dl>
+
+<dl><dt><a name="InspectorWebsocket-RegisterDomain"><strong>RegisterDomain</strong></a>(self, domain_name, notification_handler)</dt><dd><tt>Registers&nbsp;a&nbsp;given&nbsp;domain&nbsp;for&nbsp;handling&nbsp;notification&nbsp;methods.<br>
+&nbsp;<br>
+For&nbsp;example,&nbsp;given&nbsp;inspector_backend:<br>
+&nbsp;&nbsp;&nbsp;def&nbsp;OnConsoleNotification(msg):<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;msg['method']&nbsp;==&nbsp;'Console.messageAdded':<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print&nbsp;msg['params']['message']<br>
+&nbsp;&nbsp;&nbsp;inspector_backend.<a href="#InspectorWebsocket-RegisterDomain">RegisterDomain</a>('Console',&nbsp;OnConsoleNotification)<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;domain_name:&nbsp;The&nbsp;devtools&nbsp;domain&nbsp;name.&nbsp;E.g.,&nbsp;'Tracing',&nbsp;'Memory',&nbsp;'Page'.<br>
+&nbsp;&nbsp;notification_handler:&nbsp;Handler&nbsp;for&nbsp;devtools&nbsp;notification.&nbsp;Will&nbsp;be<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;called&nbsp;if&nbsp;a&nbsp;devtools&nbsp;notification&nbsp;with&nbsp;matching&nbsp;domain&nbsp;is&nbsp;received<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;via&nbsp;DispatchNotifications.&nbsp;The&nbsp;handler&nbsp;accepts&nbsp;a&nbsp;single&nbsp;paramater:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;the&nbsp;JSON&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;representing&nbsp;the&nbsp;notification.</tt></dd></dl>
+
+<dl><dt><a name="InspectorWebsocket-SendAndIgnoreResponse"><strong>SendAndIgnoreResponse</strong></a>(self, req)</dt><dd><tt>Sends&nbsp;a&nbsp;request&nbsp;without&nbsp;waiting&nbsp;for&nbsp;a&nbsp;response.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;websocket.WebSocketException:&nbsp;<a href="telemetry.core.exceptions.html#Error">Error</a>&nbsp;from&nbsp;websocket&nbsp;library.<br>
+&nbsp;&nbsp;socket.error:&nbsp;<a href="telemetry.core.exceptions.html#Error">Error</a>&nbsp;from&nbsp;websocket&nbsp;library.<br>
+&nbsp;&nbsp;exceptions.<a href="#WebSocketDisconnected">WebSocketDisconnected</a>:&nbsp;The&nbsp;socket&nbsp;was&nbsp;disconnected.</tt></dd></dl>
+
+<dl><dt><a name="InspectorWebsocket-SyncRequest"><strong>SyncRequest</strong></a>(self, req, timeout<font color="#909090">=10</font>)</dt><dd><tt>Sends&nbsp;a&nbsp;request&nbsp;and&nbsp;waits&nbsp;for&nbsp;a&nbsp;response.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;websocket.WebSocketException:&nbsp;<a href="telemetry.core.exceptions.html#Error">Error</a>&nbsp;from&nbsp;websocket&nbsp;library.<br>
+&nbsp;&nbsp;socket.error:&nbsp;<a href="telemetry.core.exceptions.html#Error">Error</a>&nbsp;from&nbsp;websocket&nbsp;library.<br>
+&nbsp;&nbsp;exceptions.<a href="#WebSocketDisconnected">WebSocketDisconnected</a>:&nbsp;The&nbsp;socket&nbsp;was&nbsp;disconnected.</tt></dd></dl>
+
+<dl><dt><a name="InspectorWebsocket-UnregisterDomain"><strong>UnregisterDomain</strong></a>(self, domain_name)</dt><dd><tt>Unregisters&nbsp;a&nbsp;previously&nbsp;registered&nbsp;domain.</tt></dd></dl>
+
+<dl><dt><a name="InspectorWebsocket-__init__"><strong>__init__</strong></a>(self)</dt><dd><tt>Create&nbsp;a&nbsp;websocket&nbsp;handler&nbsp;for&nbsp;communicating&nbsp;with&nbsp;Inspectors.</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes defined here:<br>
+<dl><dt><strong>METHOD_NOT_FOUND_CODE</strong> = -32601</dl>
+
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="WebSocketDisconnected">class <strong>WebSocketDisconnected</strong></a>(<a href="telemetry.core.exceptions.html#Error">telemetry.core.exceptions.Error</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>An&nbsp;attempt&nbsp;was&nbsp;made&nbsp;to&nbsp;use&nbsp;a&nbsp;web&nbsp;socket&nbsp;after&nbsp;it&nbsp;had&nbsp;been&nbsp;disconnected.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.chrome_inspector.inspector_websocket.html#WebSocketDisconnected">WebSocketDisconnected</a></dd>
+<dd><a href="telemetry.core.exceptions.html#Error">telemetry.core.exceptions.Error</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.core.exceptions.html#Error">telemetry.core.exceptions.Error</a>:<br>
+<dl><dt><a name="WebSocketDisconnected-AddDebuggingMessage"><strong>AddDebuggingMessage</strong></a>(self, msg)</dt><dd><tt>Adds&nbsp;a&nbsp;message&nbsp;to&nbsp;the&nbsp;description&nbsp;of&nbsp;the&nbsp;exception.<br>
+&nbsp;<br>
+Many&nbsp;Telemetry&nbsp;exceptions&nbsp;arise&nbsp;from&nbsp;failures&nbsp;in&nbsp;another&nbsp;application.&nbsp;These<br>
+failures&nbsp;are&nbsp;difficult&nbsp;to&nbsp;pinpoint.&nbsp;This&nbsp;method&nbsp;allows&nbsp;Telemetry&nbsp;classes&nbsp;to<br>
+append&nbsp;useful&nbsp;debugging&nbsp;information&nbsp;to&nbsp;the&nbsp;exception.&nbsp;This&nbsp;method&nbsp;also&nbsp;logs<br>
+information&nbsp;about&nbsp;the&nbsp;location&nbsp;from&nbsp;where&nbsp;it&nbsp;was&nbsp;called.</tt></dd></dl>
+
+<dl><dt><a name="WebSocketDisconnected-__init__"><strong>__init__</strong></a>(self, msg<font color="#909090">=''</font>)</dt></dl>
+
+<dl><dt><a name="WebSocketDisconnected-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.core.exceptions.html#Error">telemetry.core.exceptions.Error</a>:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#WebSocketDisconnected-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="WebSocketDisconnected-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#WebSocketDisconnected-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="WebSocketDisconnected-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#WebSocketDisconnected-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="WebSocketDisconnected-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#WebSocketDisconnected-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="WebSocketDisconnected-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#WebSocketDisconnected-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="WebSocketDisconnected-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="WebSocketDisconnected-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#WebSocketDisconnected-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="WebSocketDisconnected-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#WebSocketDisconnected-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="WebSocketDisconnected-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="WebSocketDisconnected-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome_inspector.memory_backend.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome_inspector.memory_backend.html
new file mode 100644
index 0000000..2258e4e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome_inspector.memory_backend.html
@@ -0,0 +1,274 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.chrome_inspector.memory_backend</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.<a href="telemetry.internal.backends.chrome_inspector.html"><font color="#ffffff">chrome_inspector</font></a>.memory_backend</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/chrome_inspector/memory_backend.py">telemetry/internal/backends/chrome_inspector/memory_backend.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2015&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.backends.chrome_inspector.inspector_websocket.html">telemetry.internal.backends.chrome_inspector.inspector_websocket</a><br>
+<a href="json.html">json</a><br>
+</td><td width="25%" valign=top><a href="logging.html">logging</a><br>
+<a href="socket.html">socket</a><br>
+</td><td width="25%" valign=top><a href="traceback.html">traceback</a><br>
+<a href="telemetry.internal.backends.chrome_inspector.websocket.html">telemetry.internal.backends.chrome_inspector.websocket</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome_inspector.memory_backend.html#MemoryBackend">MemoryBackend</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="exceptions.html#Exception">exceptions.Exception</a>(<a href="exceptions.html#BaseException">exceptions.BaseException</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome_inspector.memory_backend.html#MemoryTimeoutException">MemoryTimeoutException</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome_inspector.memory_backend.html#MemoryUnexpectedResponseException">MemoryUnexpectedResponseException</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome_inspector.memory_backend.html#MemoryUnrecoverableException">MemoryUnrecoverableException</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="MemoryBackend">class <strong>MemoryBackend</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="MemoryBackend-Close"><strong>Close</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MemoryBackend-SetMemoryPressureNotificationsSuppressed"><strong>SetMemoryPressureNotificationsSuppressed</strong></a>(self, suppressed, timeout<font color="#909090">=30</font>)</dt><dd><tt>Enable/disable&nbsp;suppressing&nbsp;memory&nbsp;pressure&nbsp;notifications.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;suppressed:&nbsp;If&nbsp;true,&nbsp;memory&nbsp;pressure&nbsp;notifications&nbsp;will&nbsp;be&nbsp;suppressed.<br>
+&nbsp;&nbsp;timeout:&nbsp;The&nbsp;timeout&nbsp;in&nbsp;seconds.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;<a href="#MemoryTimeoutException">MemoryTimeoutException</a>:&nbsp;If&nbsp;more&nbsp;than&nbsp;|timeout|&nbsp;seconds&nbsp;has&nbsp;passed<br>
+&nbsp;&nbsp;since&nbsp;the&nbsp;last&nbsp;time&nbsp;any&nbsp;data&nbsp;is&nbsp;received.<br>
+&nbsp;&nbsp;<a href="#MemoryUnrecoverableException">MemoryUnrecoverableException</a>:&nbsp;If&nbsp;there&nbsp;is&nbsp;a&nbsp;websocket&nbsp;error.<br>
+&nbsp;&nbsp;<a href="#MemoryUnexpectedResponseException">MemoryUnexpectedResponseException</a>:&nbsp;If&nbsp;the&nbsp;response&nbsp;contains&nbsp;an&nbsp;error<br>
+&nbsp;&nbsp;or&nbsp;does&nbsp;not&nbsp;contain&nbsp;the&nbsp;expected&nbsp;result.</tt></dd></dl>
+
+<dl><dt><a name="MemoryBackend-SimulateMemoryPressureNotification"><strong>SimulateMemoryPressureNotification</strong></a>(self, pressure_level, timeout<font color="#909090">=30</font>)</dt><dd><tt>Simulate&nbsp;a&nbsp;memory&nbsp;pressure&nbsp;notification.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;pressure&nbsp;level:&nbsp;The&nbsp;memory&nbsp;pressure&nbsp;level&nbsp;of&nbsp;the&nbsp;notification&nbsp;('moderate'<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;or&nbsp;'critical').<br>
+&nbsp;&nbsp;timeout:&nbsp;The&nbsp;timeout&nbsp;in&nbsp;seconds.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;<a href="#MemoryTimeoutException">MemoryTimeoutException</a>:&nbsp;If&nbsp;more&nbsp;than&nbsp;|timeout|&nbsp;seconds&nbsp;has&nbsp;passed<br>
+&nbsp;&nbsp;since&nbsp;the&nbsp;last&nbsp;time&nbsp;any&nbsp;data&nbsp;is&nbsp;received.<br>
+&nbsp;&nbsp;<a href="#MemoryUnrecoverableException">MemoryUnrecoverableException</a>:&nbsp;If&nbsp;there&nbsp;is&nbsp;a&nbsp;websocket&nbsp;error.<br>
+&nbsp;&nbsp;<a href="#MemoryUnexpectedResponseException">MemoryUnexpectedResponseException</a>:&nbsp;If&nbsp;the&nbsp;response&nbsp;contains&nbsp;an&nbsp;error<br>
+&nbsp;&nbsp;or&nbsp;does&nbsp;not&nbsp;contain&nbsp;the&nbsp;expected&nbsp;result.</tt></dd></dl>
+
+<dl><dt><a name="MemoryBackend-__init__"><strong>__init__</strong></a>(self, inspector_socket)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="MemoryTimeoutException">class <strong>MemoryTimeoutException</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.chrome_inspector.memory_backend.html#MemoryTimeoutException">MemoryTimeoutException</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="MemoryTimeoutException-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#MemoryTimeoutException-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#MemoryTimeoutException-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="MemoryTimeoutException-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#MemoryTimeoutException-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="MemoryTimeoutException-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#MemoryTimeoutException-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="MemoryTimeoutException-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#MemoryTimeoutException-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="MemoryTimeoutException-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#MemoryTimeoutException-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="MemoryTimeoutException-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="MemoryTimeoutException-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#MemoryTimeoutException-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="MemoryTimeoutException-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#MemoryTimeoutException-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="MemoryTimeoutException-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="MemoryTimeoutException-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#MemoryTimeoutException-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="MemoryTimeoutException-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="MemoryUnexpectedResponseException">class <strong>MemoryUnexpectedResponseException</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.chrome_inspector.memory_backend.html#MemoryUnexpectedResponseException">MemoryUnexpectedResponseException</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="MemoryUnexpectedResponseException-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#MemoryUnexpectedResponseException-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#MemoryUnexpectedResponseException-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="MemoryUnexpectedResponseException-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#MemoryUnexpectedResponseException-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="MemoryUnexpectedResponseException-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#MemoryUnexpectedResponseException-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="MemoryUnexpectedResponseException-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#MemoryUnexpectedResponseException-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="MemoryUnexpectedResponseException-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#MemoryUnexpectedResponseException-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="MemoryUnexpectedResponseException-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="MemoryUnexpectedResponseException-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#MemoryUnexpectedResponseException-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="MemoryUnexpectedResponseException-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#MemoryUnexpectedResponseException-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="MemoryUnexpectedResponseException-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="MemoryUnexpectedResponseException-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#MemoryUnexpectedResponseException-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="MemoryUnexpectedResponseException-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="MemoryUnrecoverableException">class <strong>MemoryUnrecoverableException</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.chrome_inspector.memory_backend.html#MemoryUnrecoverableException">MemoryUnrecoverableException</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="MemoryUnrecoverableException-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#MemoryUnrecoverableException-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#MemoryUnrecoverableException-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="MemoryUnrecoverableException-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#MemoryUnrecoverableException-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="MemoryUnrecoverableException-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#MemoryUnrecoverableException-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="MemoryUnrecoverableException-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#MemoryUnrecoverableException-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="MemoryUnrecoverableException-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#MemoryUnrecoverableException-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="MemoryUnrecoverableException-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="MemoryUnrecoverableException-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#MemoryUnrecoverableException-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="MemoryUnrecoverableException-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#MemoryUnrecoverableException-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="MemoryUnrecoverableException-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="MemoryUnrecoverableException-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#MemoryUnrecoverableException-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="MemoryUnrecoverableException-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome_inspector.tracing_backend.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome_inspector.tracing_backend.html
new file mode 100644
index 0000000..fbd8de1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome_inspector.tracing_backend.html
@@ -0,0 +1,392 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.chrome_inspector.tracing_backend</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.<a href="telemetry.internal.backends.chrome_inspector.html"><font color="#ffffff">chrome_inspector</font></a>.tracing_backend</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/chrome_inspector/tracing_backend.py">telemetry/internal/backends/chrome_inspector/tracing_backend.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.decorators.html">telemetry.decorators</a><br>
+<a href="telemetry.internal.backends.chrome_inspector.inspector_websocket.html">telemetry.internal.backends.chrome_inspector.inspector_websocket</a><br>
+</td><td width="25%" valign=top><a href="json.html">json</a><br>
+<a href="socket.html">socket</a><br>
+</td><td width="25%" valign=top><a href="time.html">time</a><br>
+<a href="telemetry.timeline.trace_data.html">telemetry.timeline.trace_data</a><br>
+</td><td width="25%" valign=top><a href="traceback.html">traceback</a><br>
+<a href="telemetry.internal.backends.chrome_inspector.websocket.html">telemetry.internal.backends.chrome_inspector.websocket</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome_inspector.tracing_backend.html#TracingBackend">TracingBackend</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="exceptions.html#Exception">exceptions.Exception</a>(<a href="exceptions.html#BaseException">exceptions.BaseException</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome_inspector.tracing_backend.html#TracingHasNotRunException">TracingHasNotRunException</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome_inspector.tracing_backend.html#TracingTimeoutException">TracingTimeoutException</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome_inspector.tracing_backend.html#TracingUnexpectedResponseException">TracingUnexpectedResponseException</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome_inspector.tracing_backend.html#TracingUnrecoverableException">TracingUnrecoverableException</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.internal.backends.chrome_inspector.tracing_backend.html#TracingUnsupportedException">TracingUnsupportedException</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TracingBackend">class <strong>TracingBackend</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="TracingBackend-Close"><strong>Close</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TracingBackend-DumpMemory"><strong>DumpMemory</strong></a>(self, timeout<font color="#909090">=30</font>)</dt><dd><tt>Dumps&nbsp;memory.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;GUID&nbsp;of&nbsp;the&nbsp;generated&nbsp;dump&nbsp;if&nbsp;successful,&nbsp;None&nbsp;otherwise.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;<a href="#TracingTimeoutException">TracingTimeoutException</a>:&nbsp;If&nbsp;more&nbsp;than&nbsp;|timeout|&nbsp;seconds&nbsp;has&nbsp;passed<br>
+&nbsp;&nbsp;since&nbsp;the&nbsp;last&nbsp;time&nbsp;any&nbsp;data&nbsp;is&nbsp;received.<br>
+&nbsp;&nbsp;<a href="#TracingUnrecoverableException">TracingUnrecoverableException</a>:&nbsp;If&nbsp;there&nbsp;is&nbsp;a&nbsp;websocket&nbsp;error.<br>
+&nbsp;&nbsp;<a href="#TracingUnexpectedResponseException">TracingUnexpectedResponseException</a>:&nbsp;If&nbsp;the&nbsp;response&nbsp;contains&nbsp;an&nbsp;error<br>
+&nbsp;&nbsp;or&nbsp;does&nbsp;not&nbsp;contain&nbsp;the&nbsp;expected&nbsp;result.</tt></dd></dl>
+
+<dl><dt><a name="TracingBackend-IsTracingSupported"><strong>IsTracingSupported</strong></a>(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="TracingBackend-StartTracing"><strong>StartTracing</strong></a>(self, trace_options, custom_categories<font color="#909090">=None</font>, timeout<font color="#909090">=10</font>)</dt><dd><tt>When&nbsp;first&nbsp;called,&nbsp;starts&nbsp;tracing,&nbsp;and&nbsp;returns&nbsp;True.<br>
+&nbsp;<br>
+If&nbsp;called&nbsp;during&nbsp;tracing,&nbsp;tracing&nbsp;is&nbsp;unchanged,&nbsp;and&nbsp;it&nbsp;returns&nbsp;False.</tt></dd></dl>
+
+<dl><dt><a name="TracingBackend-StopTracing"><strong>StopTracing</strong></a>(self, trace_data_builder, timeout<font color="#909090">=30</font>)</dt><dd><tt>Stops&nbsp;tracing&nbsp;and&nbsp;pushes&nbsp;results&nbsp;to&nbsp;the&nbsp;supplied&nbsp;TraceDataBuilder.<br>
+&nbsp;<br>
+If&nbsp;this&nbsp;is&nbsp;called&nbsp;after&nbsp;tracing&nbsp;has&nbsp;been&nbsp;stopped,&nbsp;trace&nbsp;data&nbsp;from&nbsp;the&nbsp;last<br>
+tracing&nbsp;run&nbsp;is&nbsp;pushed.</tt></dd></dl>
+
+<dl><dt><a name="TracingBackend-__init__"><strong>__init__</strong></a>(self, inspector_socket, is_tracing_running<font color="#909090">=False</font>)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>is_tracing_running</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TracingHasNotRunException">class <strong>TracingHasNotRunException</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.chrome_inspector.tracing_backend.html#TracingHasNotRunException">TracingHasNotRunException</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="TracingHasNotRunException-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingHasNotRunException-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#TracingHasNotRunException-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="TracingHasNotRunException-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingHasNotRunException-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="TracingHasNotRunException-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingHasNotRunException-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="TracingHasNotRunException-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingHasNotRunException-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="TracingHasNotRunException-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingHasNotRunException-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="TracingHasNotRunException-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="TracingHasNotRunException-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingHasNotRunException-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="TracingHasNotRunException-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingHasNotRunException-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="TracingHasNotRunException-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="TracingHasNotRunException-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingHasNotRunException-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="TracingHasNotRunException-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TracingTimeoutException">class <strong>TracingTimeoutException</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.chrome_inspector.tracing_backend.html#TracingTimeoutException">TracingTimeoutException</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="TracingTimeoutException-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingTimeoutException-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#TracingTimeoutException-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="TracingTimeoutException-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingTimeoutException-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="TracingTimeoutException-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingTimeoutException-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="TracingTimeoutException-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingTimeoutException-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="TracingTimeoutException-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingTimeoutException-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="TracingTimeoutException-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="TracingTimeoutException-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingTimeoutException-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="TracingTimeoutException-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingTimeoutException-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="TracingTimeoutException-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="TracingTimeoutException-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingTimeoutException-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="TracingTimeoutException-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TracingUnexpectedResponseException">class <strong>TracingUnexpectedResponseException</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.chrome_inspector.tracing_backend.html#TracingUnexpectedResponseException">TracingUnexpectedResponseException</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="TracingUnexpectedResponseException-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingUnexpectedResponseException-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#TracingUnexpectedResponseException-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="TracingUnexpectedResponseException-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingUnexpectedResponseException-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="TracingUnexpectedResponseException-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingUnexpectedResponseException-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="TracingUnexpectedResponseException-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingUnexpectedResponseException-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="TracingUnexpectedResponseException-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingUnexpectedResponseException-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="TracingUnexpectedResponseException-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="TracingUnexpectedResponseException-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingUnexpectedResponseException-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="TracingUnexpectedResponseException-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingUnexpectedResponseException-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="TracingUnexpectedResponseException-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="TracingUnexpectedResponseException-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingUnexpectedResponseException-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="TracingUnexpectedResponseException-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TracingUnrecoverableException">class <strong>TracingUnrecoverableException</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.chrome_inspector.tracing_backend.html#TracingUnrecoverableException">TracingUnrecoverableException</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="TracingUnrecoverableException-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingUnrecoverableException-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#TracingUnrecoverableException-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="TracingUnrecoverableException-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingUnrecoverableException-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="TracingUnrecoverableException-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingUnrecoverableException-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="TracingUnrecoverableException-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingUnrecoverableException-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="TracingUnrecoverableException-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingUnrecoverableException-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="TracingUnrecoverableException-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="TracingUnrecoverableException-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingUnrecoverableException-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="TracingUnrecoverableException-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingUnrecoverableException-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="TracingUnrecoverableException-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="TracingUnrecoverableException-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingUnrecoverableException-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="TracingUnrecoverableException-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TracingUnsupportedException">class <strong>TracingUnsupportedException</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.chrome_inspector.tracing_backend.html#TracingUnsupportedException">TracingUnsupportedException</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="TracingUnsupportedException-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingUnsupportedException-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#TracingUnsupportedException-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="TracingUnsupportedException-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingUnsupportedException-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="TracingUnsupportedException-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingUnsupportedException-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="TracingUnsupportedException-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingUnsupportedException-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="TracingUnsupportedException-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingUnsupportedException-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="TracingUnsupportedException-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="TracingUnsupportedException-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingUnsupportedException-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="TracingUnsupportedException-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingUnsupportedException-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="TracingUnsupportedException-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="TracingUnsupportedException-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingUnsupportedException-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="TracingUnsupportedException-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome_inspector.websocket.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome_inspector.websocket.html
new file mode 100644
index 0000000..8c20ae9
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.chrome_inspector.websocket.html
@@ -0,0 +1,40 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.chrome_inspector.websocket</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.<a href="telemetry.internal.backends.chrome_inspector.html"><font color="#ffffff">chrome_inspector</font></a>.websocket</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/chrome_inspector/websocket.py">telemetry/internal/backends/chrome_inspector/websocket.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="socket.html">socket</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-create_connection"><strong>create_connection</strong></a>(*args, **kwargs)</dt></dl>
+</td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>absolute_import</strong> = _Feature((2, 5, 0, 'alpha', 1), (3, 0, 0, 'alpha', 0), 16384)</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.codepen_credentials_backend.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.codepen_credentials_backend.html
new file mode 100644
index 0000000..27388f2
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.codepen_credentials_backend.html
@@ -0,0 +1,92 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.codepen_credentials_backend</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.codepen_credentials_backend</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/codepen_credentials_backend.py">telemetry/internal/backends/codepen_credentials_backend.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.backends.form_based_credentials_backend.html">telemetry.internal.backends.form_based_credentials_backend</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.form_based_credentials_backend.html#FormBasedCredentialsBackend">telemetry.internal.backends.form_based_credentials_backend.FormBasedCredentialsBackend</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.codepen_credentials_backend.html#CodePenCredentialsBackend">CodePenCredentialsBackend</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="CodePenCredentialsBackend">class <strong>CodePenCredentialsBackend</strong></a>(<a href="telemetry.internal.backends.form_based_credentials_backend.html#FormBasedCredentialsBackend">telemetry.internal.backends.form_based_credentials_backend.FormBasedCredentialsBackend</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.codepen_credentials_backend.html#CodePenCredentialsBackend">CodePenCredentialsBackend</a></dd>
+<dd><a href="telemetry.internal.backends.form_based_credentials_backend.html#FormBasedCredentialsBackend">telemetry.internal.backends.form_based_credentials_backend.FormBasedCredentialsBackend</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>credentials_type</strong></dt>
+</dl>
+<dl><dt><strong>logged_in_javascript</strong></dt>
+<dd><tt>Evaluates&nbsp;to&nbsp;true&nbsp;iff&nbsp;already&nbsp;logged&nbsp;in.</tt></dd>
+</dl>
+<dl><dt><strong>login_button_javascript</strong></dt>
+</dl>
+<dl><dt><strong>login_form_id</strong></dt>
+</dl>
+<dl><dt><strong>login_input_id</strong></dt>
+</dl>
+<dl><dt><strong>password_input_id</strong></dt>
+</dl>
+<dl><dt><strong>url</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.internal.backends.form_based_credentials_backend.html#FormBasedCredentialsBackend">telemetry.internal.backends.form_based_credentials_backend.FormBasedCredentialsBackend</a>:<br>
+<dl><dt><a name="CodePenCredentialsBackend-IsAlreadyLoggedIn"><strong>IsAlreadyLoggedIn</strong></a>(self, tab)</dt></dl>
+
+<dl><dt><a name="CodePenCredentialsBackend-IsLoggedIn"><strong>IsLoggedIn</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CodePenCredentialsBackend-LoginNeeded"><strong>LoginNeeded</strong></a>(self, tab, action_runner, config)</dt><dd><tt>Logs&nbsp;in&nbsp;to&nbsp;a&nbsp;test&nbsp;account.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;RuntimeError:&nbsp;if&nbsp;could&nbsp;not&nbsp;get&nbsp;credential&nbsp;information.</tt></dd></dl>
+
+<dl><dt><a name="CodePenCredentialsBackend-LoginNoLongerNeeded"><strong>LoginNoLongerNeeded</strong></a>(self, tab)</dt></dl>
+
+<dl><dt><a name="CodePenCredentialsBackend-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.backends.form_based_credentials_backend.html#FormBasedCredentialsBackend">telemetry.internal.backends.form_based_credentials_backend.FormBasedCredentialsBackend</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.facebook_credentials_backend.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.facebook_credentials_backend.html
new file mode 100644
index 0000000..8f1cd05
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.facebook_credentials_backend.html
@@ -0,0 +1,156 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.facebook_credentials_backend</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.facebook_credentials_backend</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/facebook_credentials_backend.py">telemetry/internal/backends/facebook_credentials_backend.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.backends.form_based_credentials_backend.html">telemetry.internal.backends.form_based_credentials_backend</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.form_based_credentials_backend.html#FormBasedCredentialsBackend">telemetry.internal.backends.form_based_credentials_backend.FormBasedCredentialsBackend</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.facebook_credentials_backend.html#FacebookCredentialsBackend">FacebookCredentialsBackend</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.facebook_credentials_backend.html#FacebookCredentialsBackend2">FacebookCredentialsBackend2</a>
+</font></dt></dl>
+</dd>
+</dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="FacebookCredentialsBackend">class <strong>FacebookCredentialsBackend</strong></a>(<a href="telemetry.internal.backends.form_based_credentials_backend.html#FormBasedCredentialsBackend">telemetry.internal.backends.form_based_credentials_backend.FormBasedCredentialsBackend</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.facebook_credentials_backend.html#FacebookCredentialsBackend">FacebookCredentialsBackend</a></dd>
+<dd><a href="telemetry.internal.backends.form_based_credentials_backend.html#FormBasedCredentialsBackend">telemetry.internal.backends.form_based_credentials_backend.FormBasedCredentialsBackend</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>credentials_type</strong></dt>
+</dl>
+<dl><dt><strong>logged_in_javascript</strong></dt>
+<dd><tt>Evaluates&nbsp;to&nbsp;true&nbsp;iff&nbsp;already&nbsp;logged&nbsp;in.</tt></dd>
+</dl>
+<dl><dt><strong>login_form_id</strong></dt>
+</dl>
+<dl><dt><strong>login_input_id</strong></dt>
+</dl>
+<dl><dt><strong>password_input_id</strong></dt>
+</dl>
+<dl><dt><strong>url</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.internal.backends.form_based_credentials_backend.html#FormBasedCredentialsBackend">telemetry.internal.backends.form_based_credentials_backend.FormBasedCredentialsBackend</a>:<br>
+<dl><dt><a name="FacebookCredentialsBackend-IsAlreadyLoggedIn"><strong>IsAlreadyLoggedIn</strong></a>(self, tab)</dt></dl>
+
+<dl><dt><a name="FacebookCredentialsBackend-IsLoggedIn"><strong>IsLoggedIn</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="FacebookCredentialsBackend-LoginNeeded"><strong>LoginNeeded</strong></a>(self, tab, action_runner, config)</dt><dd><tt>Logs&nbsp;in&nbsp;to&nbsp;a&nbsp;test&nbsp;account.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;RuntimeError:&nbsp;if&nbsp;could&nbsp;not&nbsp;get&nbsp;credential&nbsp;information.</tt></dd></dl>
+
+<dl><dt><a name="FacebookCredentialsBackend-LoginNoLongerNeeded"><strong>LoginNoLongerNeeded</strong></a>(self, tab)</dt></dl>
+
+<dl><dt><a name="FacebookCredentialsBackend-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.backends.form_based_credentials_backend.html#FormBasedCredentialsBackend">telemetry.internal.backends.form_based_credentials_backend.FormBasedCredentialsBackend</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>login_button_javascript</strong></dt>
+<dd><tt>Some&nbsp;sites&nbsp;have&nbsp;custom&nbsp;JS&nbsp;to&nbsp;log&nbsp;in.</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="FacebookCredentialsBackend2">class <strong>FacebookCredentialsBackend2</strong></a>(<a href="telemetry.internal.backends.facebook_credentials_backend.html#FacebookCredentialsBackend">FacebookCredentialsBackend</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Facebook&nbsp;credential&nbsp;backend&nbsp;for&nbsp;https&nbsp;client.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.facebook_credentials_backend.html#FacebookCredentialsBackend2">FacebookCredentialsBackend2</a></dd>
+<dd><a href="telemetry.internal.backends.facebook_credentials_backend.html#FacebookCredentialsBackend">FacebookCredentialsBackend</a></dd>
+<dd><a href="telemetry.internal.backends.form_based_credentials_backend.html#FormBasedCredentialsBackend">telemetry.internal.backends.form_based_credentials_backend.FormBasedCredentialsBackend</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>credentials_type</strong></dt>
+</dl>
+<dl><dt><strong>url</strong></dt>
+</dl>
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.backends.facebook_credentials_backend.html#FacebookCredentialsBackend">FacebookCredentialsBackend</a>:<br>
+<dl><dt><strong>logged_in_javascript</strong></dt>
+<dd><tt>Evaluates&nbsp;to&nbsp;true&nbsp;iff&nbsp;already&nbsp;logged&nbsp;in.</tt></dd>
+</dl>
+<dl><dt><strong>login_form_id</strong></dt>
+</dl>
+<dl><dt><strong>login_input_id</strong></dt>
+</dl>
+<dl><dt><strong>password_input_id</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.internal.backends.form_based_credentials_backend.html#FormBasedCredentialsBackend">telemetry.internal.backends.form_based_credentials_backend.FormBasedCredentialsBackend</a>:<br>
+<dl><dt><a name="FacebookCredentialsBackend2-IsAlreadyLoggedIn"><strong>IsAlreadyLoggedIn</strong></a>(self, tab)</dt></dl>
+
+<dl><dt><a name="FacebookCredentialsBackend2-IsLoggedIn"><strong>IsLoggedIn</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="FacebookCredentialsBackend2-LoginNeeded"><strong>LoginNeeded</strong></a>(self, tab, action_runner, config)</dt><dd><tt>Logs&nbsp;in&nbsp;to&nbsp;a&nbsp;test&nbsp;account.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;RuntimeError:&nbsp;if&nbsp;could&nbsp;not&nbsp;get&nbsp;credential&nbsp;information.</tt></dd></dl>
+
+<dl><dt><a name="FacebookCredentialsBackend2-LoginNoLongerNeeded"><strong>LoginNoLongerNeeded</strong></a>(self, tab)</dt></dl>
+
+<dl><dt><a name="FacebookCredentialsBackend2-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.backends.form_based_credentials_backend.html#FormBasedCredentialsBackend">telemetry.internal.backends.form_based_credentials_backend.FormBasedCredentialsBackend</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>login_button_javascript</strong></dt>
+<dd><tt>Some&nbsp;sites&nbsp;have&nbsp;custom&nbsp;JS&nbsp;to&nbsp;log&nbsp;in.</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.form_based_credentials_backend.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.form_based_credentials_backend.html
new file mode 100644
index 0000000..109b617
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.form_based_credentials_backend.html
@@ -0,0 +1,86 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.form_based_credentials_backend</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.form_based_credentials_backend</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/form_based_credentials_backend.py">telemetry/internal/backends/form_based_credentials_backend.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+</td><td width="25%" valign=top><a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.form_based_credentials_backend.html#FormBasedCredentialsBackend">FormBasedCredentialsBackend</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="FormBasedCredentialsBackend">class <strong>FormBasedCredentialsBackend</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="FormBasedCredentialsBackend-IsAlreadyLoggedIn"><strong>IsAlreadyLoggedIn</strong></a>(self, tab)</dt></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackend-IsLoggedIn"><strong>IsLoggedIn</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackend-LoginNeeded"><strong>LoginNeeded</strong></a>(self, tab, action_runner, config)</dt><dd><tt>Logs&nbsp;in&nbsp;to&nbsp;a&nbsp;test&nbsp;account.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;RuntimeError:&nbsp;if&nbsp;could&nbsp;not&nbsp;get&nbsp;credential&nbsp;information.</tt></dd></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackend-LoginNoLongerNeeded"><strong>LoginNoLongerNeeded</strong></a>(self, tab)</dt></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackend-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>credentials_type</strong></dt>
+</dl>
+<dl><dt><strong>logged_in_javascript</strong></dt>
+<dd><tt>Evaluates&nbsp;to&nbsp;true&nbsp;iff&nbsp;already&nbsp;logged&nbsp;in.</tt></dd>
+</dl>
+<dl><dt><strong>login_button_javascript</strong></dt>
+<dd><tt>Some&nbsp;sites&nbsp;have&nbsp;custom&nbsp;JS&nbsp;to&nbsp;log&nbsp;in.</tt></dd>
+</dl>
+<dl><dt><strong>login_form_id</strong></dt>
+</dl>
+<dl><dt><strong>login_input_id</strong></dt>
+</dl>
+<dl><dt><strong>password_input_id</strong></dt>
+</dl>
+<dl><dt><strong>url</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.form_based_credentials_backend_unittest_base.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.form_based_credentials_backend_unittest_base.html
new file mode 100644
index 0000000..00d7328
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.form_based_credentials_backend_unittest_base.html
@@ -0,0 +1,336 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.form_based_credentials_backend_unittest_base</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.form_based_credentials_backend_unittest_base</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/form_based_credentials_backend_unittest_base.py">telemetry/internal/backends/form_based_credentials_backend_unittest_base.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.testing.simple_mock.html">telemetry.testing.simple_mock</a><br>
+</td><td width="25%" valign=top><a href="unittest.html">unittest</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="unittest.case.html#TestCase">unittest.case.TestCase</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.form_based_credentials_backend_unittest_base.html#FormBasedCredentialsBackendUnitTestBase">FormBasedCredentialsBackendUnitTestBase</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="FormBasedCredentialsBackendUnitTestBase">class <strong>FormBasedCredentialsBackendUnitTestBase</strong></a>(<a href="unittest.case.html#TestCase">unittest.case.TestCase</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.form_based_credentials_backend_unittest_base.html#FormBasedCredentialsBackendUnitTestBase">FormBasedCredentialsBackendUnitTestBase</a></dd>
+<dd><a href="unittest.case.html#TestCase">unittest.case.TestCase</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-setUp"><strong>setUp</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-testLoginUsingMock"><strong>testLoginUsingMock</strong></a>(self)</dt></dl>
+
+<hr>
+Methods inherited from <a href="unittest.case.html#TestCase">unittest.case.TestCase</a>:<br>
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-__call__"><strong>__call__</strong></a>(self, *args, **kwds)</dt></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-__eq__"><strong>__eq__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-__hash__"><strong>__hash__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-__init__"><strong>__init__</strong></a>(self, methodName<font color="#909090">='runTest'</font>)</dt><dd><tt>Create&nbsp;an&nbsp;instance&nbsp;of&nbsp;the&nbsp;class&nbsp;that&nbsp;will&nbsp;use&nbsp;the&nbsp;named&nbsp;test<br>
+method&nbsp;when&nbsp;executed.&nbsp;Raises&nbsp;a&nbsp;ValueError&nbsp;if&nbsp;the&nbsp;instance&nbsp;does<br>
+not&nbsp;have&nbsp;a&nbsp;method&nbsp;with&nbsp;the&nbsp;specified&nbsp;name.</tt></dd></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-__ne__"><strong>__ne__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-__repr__"><strong>__repr__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-addCleanup"><strong>addCleanup</strong></a>(self, function, *args, **kwargs)</dt><dd><tt>Add&nbsp;a&nbsp;function,&nbsp;with&nbsp;arguments,&nbsp;to&nbsp;be&nbsp;called&nbsp;when&nbsp;the&nbsp;test&nbsp;is<br>
+completed.&nbsp;Functions&nbsp;added&nbsp;are&nbsp;called&nbsp;on&nbsp;a&nbsp;LIFO&nbsp;basis&nbsp;and&nbsp;are<br>
+called&nbsp;after&nbsp;tearDown&nbsp;on&nbsp;test&nbsp;failure&nbsp;or&nbsp;success.<br>
+&nbsp;<br>
+Cleanup&nbsp;items&nbsp;are&nbsp;called&nbsp;even&nbsp;if&nbsp;setUp&nbsp;fails&nbsp;(unlike&nbsp;tearDown).</tt></dd></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-addTypeEqualityFunc"><strong>addTypeEqualityFunc</strong></a>(self, typeobj, function)</dt><dd><tt>Add&nbsp;a&nbsp;type&nbsp;specific&nbsp;assertEqual&nbsp;style&nbsp;function&nbsp;to&nbsp;compare&nbsp;a&nbsp;type.<br>
+&nbsp;<br>
+This&nbsp;method&nbsp;is&nbsp;for&nbsp;use&nbsp;by&nbsp;<a href="unittest.case.html#TestCase">TestCase</a>&nbsp;subclasses&nbsp;that&nbsp;need&nbsp;to&nbsp;register<br>
+their&nbsp;own&nbsp;type&nbsp;equality&nbsp;functions&nbsp;to&nbsp;provide&nbsp;nicer&nbsp;error&nbsp;messages.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;typeobj:&nbsp;The&nbsp;data&nbsp;type&nbsp;to&nbsp;call&nbsp;this&nbsp;function&nbsp;on&nbsp;when&nbsp;both&nbsp;values<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;are&nbsp;of&nbsp;the&nbsp;same&nbsp;type&nbsp;in&nbsp;<a href="#FormBasedCredentialsBackendUnitTestBase-assertEqual">assertEqual</a>().<br>
+&nbsp;&nbsp;&nbsp;&nbsp;function:&nbsp;The&nbsp;callable&nbsp;taking&nbsp;two&nbsp;arguments&nbsp;and&nbsp;an&nbsp;optional<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;msg=&nbsp;argument&nbsp;that&nbsp;raises&nbsp;self.<strong>failureException</strong>&nbsp;with&nbsp;a<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;useful&nbsp;error&nbsp;message&nbsp;when&nbsp;the&nbsp;two&nbsp;arguments&nbsp;are&nbsp;not&nbsp;equal.</tt></dd></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-assertAlmostEqual"><strong>assertAlmostEqual</strong></a>(self, first, second, places<font color="#909090">=None</font>, msg<font color="#909090">=None</font>, delta<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;unequal&nbsp;as&nbsp;determined&nbsp;by&nbsp;their<br>
+difference&nbsp;rounded&nbsp;to&nbsp;the&nbsp;given&nbsp;number&nbsp;of&nbsp;decimal&nbsp;places<br>
+(default&nbsp;7)&nbsp;and&nbsp;comparing&nbsp;to&nbsp;zero,&nbsp;or&nbsp;by&nbsp;comparing&nbsp;that&nbsp;the<br>
+between&nbsp;the&nbsp;two&nbsp;objects&nbsp;is&nbsp;more&nbsp;than&nbsp;the&nbsp;given&nbsp;delta.<br>
+&nbsp;<br>
+Note&nbsp;that&nbsp;decimal&nbsp;places&nbsp;(from&nbsp;zero)&nbsp;are&nbsp;usually&nbsp;not&nbsp;the&nbsp;same<br>
+as&nbsp;significant&nbsp;digits&nbsp;(measured&nbsp;from&nbsp;the&nbsp;most&nbsp;signficant&nbsp;digit).<br>
+&nbsp;<br>
+If&nbsp;the&nbsp;two&nbsp;objects&nbsp;compare&nbsp;equal&nbsp;then&nbsp;they&nbsp;will&nbsp;automatically<br>
+compare&nbsp;almost&nbsp;equal.</tt></dd></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-assertAlmostEquals"><strong>assertAlmostEquals</strong></a> = assertAlmostEqual(self, first, second, places<font color="#909090">=None</font>, msg<font color="#909090">=None</font>, delta<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;unequal&nbsp;as&nbsp;determined&nbsp;by&nbsp;their<br>
+difference&nbsp;rounded&nbsp;to&nbsp;the&nbsp;given&nbsp;number&nbsp;of&nbsp;decimal&nbsp;places<br>
+(default&nbsp;7)&nbsp;and&nbsp;comparing&nbsp;to&nbsp;zero,&nbsp;or&nbsp;by&nbsp;comparing&nbsp;that&nbsp;the<br>
+between&nbsp;the&nbsp;two&nbsp;objects&nbsp;is&nbsp;more&nbsp;than&nbsp;the&nbsp;given&nbsp;delta.<br>
+&nbsp;<br>
+Note&nbsp;that&nbsp;decimal&nbsp;places&nbsp;(from&nbsp;zero)&nbsp;are&nbsp;usually&nbsp;not&nbsp;the&nbsp;same<br>
+as&nbsp;significant&nbsp;digits&nbsp;(measured&nbsp;from&nbsp;the&nbsp;most&nbsp;signficant&nbsp;digit).<br>
+&nbsp;<br>
+If&nbsp;the&nbsp;two&nbsp;objects&nbsp;compare&nbsp;equal&nbsp;then&nbsp;they&nbsp;will&nbsp;automatically<br>
+compare&nbsp;almost&nbsp;equal.</tt></dd></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-assertDictContainsSubset"><strong>assertDictContainsSubset</strong></a>(self, expected, actual, msg<font color="#909090">=None</font>)</dt><dd><tt>Checks&nbsp;whether&nbsp;actual&nbsp;is&nbsp;a&nbsp;superset&nbsp;of&nbsp;expected.</tt></dd></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-assertDictEqual"><strong>assertDictEqual</strong></a>(self, d1, d2, msg<font color="#909090">=None</font>)</dt></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-assertEqual"><strong>assertEqual</strong></a>(self, first, second, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;unequal&nbsp;as&nbsp;determined&nbsp;by&nbsp;the&nbsp;'=='<br>
+operator.</tt></dd></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-assertEquals"><strong>assertEquals</strong></a> = assertEqual(self, first, second, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;unequal&nbsp;as&nbsp;determined&nbsp;by&nbsp;the&nbsp;'=='<br>
+operator.</tt></dd></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-assertFalse"><strong>assertFalse</strong></a>(self, expr, msg<font color="#909090">=None</font>)</dt><dd><tt>Check&nbsp;that&nbsp;the&nbsp;expression&nbsp;is&nbsp;false.</tt></dd></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-assertGreater"><strong>assertGreater</strong></a>(self, a, b, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#FormBasedCredentialsBackendUnitTestBase-assertTrue">assertTrue</a>(a&nbsp;&gt;&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-assertGreaterEqual"><strong>assertGreaterEqual</strong></a>(self, a, b, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#FormBasedCredentialsBackendUnitTestBase-assertTrue">assertTrue</a>(a&nbsp;&gt;=&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-assertIn"><strong>assertIn</strong></a>(self, member, container, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#FormBasedCredentialsBackendUnitTestBase-assertTrue">assertTrue</a>(a&nbsp;in&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-assertIs"><strong>assertIs</strong></a>(self, expr1, expr2, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#FormBasedCredentialsBackendUnitTestBase-assertTrue">assertTrue</a>(a&nbsp;is&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-assertIsInstance"><strong>assertIsInstance</strong></a>(self, obj, cls, msg<font color="#909090">=None</font>)</dt><dd><tt>Same&nbsp;as&nbsp;<a href="#FormBasedCredentialsBackendUnitTestBase-assertTrue">assertTrue</a>(isinstance(obj,&nbsp;cls)),&nbsp;with&nbsp;a&nbsp;nicer<br>
+default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-assertIsNone"><strong>assertIsNone</strong></a>(self, obj, msg<font color="#909090">=None</font>)</dt><dd><tt>Same&nbsp;as&nbsp;<a href="#FormBasedCredentialsBackendUnitTestBase-assertTrue">assertTrue</a>(obj&nbsp;is&nbsp;None),&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-assertIsNot"><strong>assertIsNot</strong></a>(self, expr1, expr2, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#FormBasedCredentialsBackendUnitTestBase-assertTrue">assertTrue</a>(a&nbsp;is&nbsp;not&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-assertIsNotNone"><strong>assertIsNotNone</strong></a>(self, obj, msg<font color="#909090">=None</font>)</dt><dd><tt>Included&nbsp;for&nbsp;symmetry&nbsp;with&nbsp;assertIsNone.</tt></dd></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-assertItemsEqual"><strong>assertItemsEqual</strong></a>(self, expected_seq, actual_seq, msg<font color="#909090">=None</font>)</dt><dd><tt>An&nbsp;unordered&nbsp;sequence&nbsp;specific&nbsp;comparison.&nbsp;It&nbsp;asserts&nbsp;that<br>
+actual_seq&nbsp;and&nbsp;expected_seq&nbsp;have&nbsp;the&nbsp;same&nbsp;element&nbsp;counts.<br>
+Equivalent&nbsp;to::<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#FormBasedCredentialsBackendUnitTestBase-assertEqual">assertEqual</a>(Counter(iter(actual_seq)),<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Counter(iter(expected_seq)))<br>
+&nbsp;<br>
+Asserts&nbsp;that&nbsp;each&nbsp;element&nbsp;has&nbsp;the&nbsp;same&nbsp;count&nbsp;in&nbsp;both&nbsp;sequences.<br>
+Example:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;[0,&nbsp;1,&nbsp;1]&nbsp;and&nbsp;[1,&nbsp;0,&nbsp;1]&nbsp;compare&nbsp;equal.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;[0,&nbsp;0,&nbsp;1]&nbsp;and&nbsp;[0,&nbsp;1]&nbsp;compare&nbsp;unequal.</tt></dd></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-assertLess"><strong>assertLess</strong></a>(self, a, b, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#FormBasedCredentialsBackendUnitTestBase-assertTrue">assertTrue</a>(a&nbsp;&lt;&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-assertLessEqual"><strong>assertLessEqual</strong></a>(self, a, b, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#FormBasedCredentialsBackendUnitTestBase-assertTrue">assertTrue</a>(a&nbsp;&lt;=&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-assertListEqual"><strong>assertListEqual</strong></a>(self, list1, list2, msg<font color="#909090">=None</font>)</dt><dd><tt>A&nbsp;list-specific&nbsp;equality&nbsp;assertion.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;list1:&nbsp;The&nbsp;first&nbsp;list&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;list2:&nbsp;The&nbsp;second&nbsp;list&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;msg:&nbsp;Optional&nbsp;message&nbsp;to&nbsp;use&nbsp;on&nbsp;failure&nbsp;instead&nbsp;of&nbsp;a&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;differences.</tt></dd></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-assertMultiLineEqual"><strong>assertMultiLineEqual</strong></a>(self, first, second, msg<font color="#909090">=None</font>)</dt><dd><tt>Assert&nbsp;that&nbsp;two&nbsp;multi-line&nbsp;strings&nbsp;are&nbsp;equal.</tt></dd></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-assertNotAlmostEqual"><strong>assertNotAlmostEqual</strong></a>(self, first, second, places<font color="#909090">=None</font>, msg<font color="#909090">=None</font>, delta<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;equal&nbsp;as&nbsp;determined&nbsp;by&nbsp;their<br>
+difference&nbsp;rounded&nbsp;to&nbsp;the&nbsp;given&nbsp;number&nbsp;of&nbsp;decimal&nbsp;places<br>
+(default&nbsp;7)&nbsp;and&nbsp;comparing&nbsp;to&nbsp;zero,&nbsp;or&nbsp;by&nbsp;comparing&nbsp;that&nbsp;the<br>
+between&nbsp;the&nbsp;two&nbsp;objects&nbsp;is&nbsp;less&nbsp;than&nbsp;the&nbsp;given&nbsp;delta.<br>
+&nbsp;<br>
+Note&nbsp;that&nbsp;decimal&nbsp;places&nbsp;(from&nbsp;zero)&nbsp;are&nbsp;usually&nbsp;not&nbsp;the&nbsp;same<br>
+as&nbsp;significant&nbsp;digits&nbsp;(measured&nbsp;from&nbsp;the&nbsp;most&nbsp;signficant&nbsp;digit).<br>
+&nbsp;<br>
+Objects&nbsp;that&nbsp;are&nbsp;equal&nbsp;automatically&nbsp;fail.</tt></dd></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-assertNotAlmostEquals"><strong>assertNotAlmostEquals</strong></a> = assertNotAlmostEqual(self, first, second, places<font color="#909090">=None</font>, msg<font color="#909090">=None</font>, delta<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;equal&nbsp;as&nbsp;determined&nbsp;by&nbsp;their<br>
+difference&nbsp;rounded&nbsp;to&nbsp;the&nbsp;given&nbsp;number&nbsp;of&nbsp;decimal&nbsp;places<br>
+(default&nbsp;7)&nbsp;and&nbsp;comparing&nbsp;to&nbsp;zero,&nbsp;or&nbsp;by&nbsp;comparing&nbsp;that&nbsp;the<br>
+between&nbsp;the&nbsp;two&nbsp;objects&nbsp;is&nbsp;less&nbsp;than&nbsp;the&nbsp;given&nbsp;delta.<br>
+&nbsp;<br>
+Note&nbsp;that&nbsp;decimal&nbsp;places&nbsp;(from&nbsp;zero)&nbsp;are&nbsp;usually&nbsp;not&nbsp;the&nbsp;same<br>
+as&nbsp;significant&nbsp;digits&nbsp;(measured&nbsp;from&nbsp;the&nbsp;most&nbsp;signficant&nbsp;digit).<br>
+&nbsp;<br>
+Objects&nbsp;that&nbsp;are&nbsp;equal&nbsp;automatically&nbsp;fail.</tt></dd></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-assertNotEqual"><strong>assertNotEqual</strong></a>(self, first, second, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;equal&nbsp;as&nbsp;determined&nbsp;by&nbsp;the&nbsp;'!='<br>
+operator.</tt></dd></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-assertNotEquals"><strong>assertNotEquals</strong></a> = assertNotEqual(self, first, second, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;equal&nbsp;as&nbsp;determined&nbsp;by&nbsp;the&nbsp;'!='<br>
+operator.</tt></dd></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-assertNotIn"><strong>assertNotIn</strong></a>(self, member, container, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#FormBasedCredentialsBackendUnitTestBase-assertTrue">assertTrue</a>(a&nbsp;not&nbsp;in&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-assertNotIsInstance"><strong>assertNotIsInstance</strong></a>(self, obj, cls, msg<font color="#909090">=None</font>)</dt><dd><tt>Included&nbsp;for&nbsp;symmetry&nbsp;with&nbsp;assertIsInstance.</tt></dd></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-assertNotRegexpMatches"><strong>assertNotRegexpMatches</strong></a>(self, text, unexpected_regexp, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;the&nbsp;test&nbsp;if&nbsp;the&nbsp;text&nbsp;matches&nbsp;the&nbsp;regular&nbsp;expression.</tt></dd></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-assertRaises"><strong>assertRaises</strong></a>(self, excClass, callableObj<font color="#909090">=None</font>, *args, **kwargs)</dt><dd><tt>Fail&nbsp;unless&nbsp;an&nbsp;exception&nbsp;of&nbsp;class&nbsp;excClass&nbsp;is&nbsp;raised<br>
+by&nbsp;callableObj&nbsp;when&nbsp;invoked&nbsp;with&nbsp;arguments&nbsp;args&nbsp;and&nbsp;keyword<br>
+arguments&nbsp;kwargs.&nbsp;If&nbsp;a&nbsp;different&nbsp;type&nbsp;of&nbsp;exception&nbsp;is<br>
+raised,&nbsp;it&nbsp;will&nbsp;not&nbsp;be&nbsp;caught,&nbsp;and&nbsp;the&nbsp;test&nbsp;case&nbsp;will&nbsp;be<br>
+deemed&nbsp;to&nbsp;have&nbsp;suffered&nbsp;an&nbsp;error,&nbsp;exactly&nbsp;as&nbsp;for&nbsp;an<br>
+unexpected&nbsp;exception.<br>
+&nbsp;<br>
+If&nbsp;called&nbsp;with&nbsp;callableObj&nbsp;omitted&nbsp;or&nbsp;None,&nbsp;will&nbsp;return&nbsp;a<br>
+context&nbsp;object&nbsp;used&nbsp;like&nbsp;this::<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;with&nbsp;<a href="#FormBasedCredentialsBackendUnitTestBase-assertRaises">assertRaises</a>(SomeException):<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;do_something()<br>
+&nbsp;<br>
+The&nbsp;context&nbsp;manager&nbsp;keeps&nbsp;a&nbsp;reference&nbsp;to&nbsp;the&nbsp;exception&nbsp;as<br>
+the&nbsp;'exception'&nbsp;attribute.&nbsp;This&nbsp;allows&nbsp;you&nbsp;to&nbsp;inspect&nbsp;the<br>
+exception&nbsp;after&nbsp;the&nbsp;assertion::<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;with&nbsp;<a href="#FormBasedCredentialsBackendUnitTestBase-assertRaises">assertRaises</a>(SomeException)&nbsp;as&nbsp;cm:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;do_something()<br>
+&nbsp;&nbsp;&nbsp;&nbsp;the_exception&nbsp;=&nbsp;cm.exception<br>
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#FormBasedCredentialsBackendUnitTestBase-assertEqual">assertEqual</a>(the_exception.error_code,&nbsp;3)</tt></dd></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-assertRaisesRegexp"><strong>assertRaisesRegexp</strong></a>(self, expected_exception, expected_regexp, callable_obj<font color="#909090">=None</font>, *args, **kwargs)</dt><dd><tt>Asserts&nbsp;that&nbsp;the&nbsp;message&nbsp;in&nbsp;a&nbsp;raised&nbsp;exception&nbsp;matches&nbsp;a&nbsp;regexp.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;expected_exception:&nbsp;Exception&nbsp;class&nbsp;expected&nbsp;to&nbsp;be&nbsp;raised.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;expected_regexp:&nbsp;Regexp&nbsp;(re&nbsp;pattern&nbsp;object&nbsp;or&nbsp;string)&nbsp;expected<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;to&nbsp;be&nbsp;found&nbsp;in&nbsp;error&nbsp;message.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;callable_obj:&nbsp;Function&nbsp;to&nbsp;be&nbsp;called.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;args:&nbsp;Extra&nbsp;args.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;kwargs:&nbsp;Extra&nbsp;kwargs.</tt></dd></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-assertRegexpMatches"><strong>assertRegexpMatches</strong></a>(self, text, expected_regexp, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;the&nbsp;test&nbsp;unless&nbsp;the&nbsp;text&nbsp;matches&nbsp;the&nbsp;regular&nbsp;expression.</tt></dd></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-assertSequenceEqual"><strong>assertSequenceEqual</strong></a>(self, seq1, seq2, msg<font color="#909090">=None</font>, seq_type<font color="#909090">=None</font>)</dt><dd><tt>An&nbsp;equality&nbsp;assertion&nbsp;for&nbsp;ordered&nbsp;sequences&nbsp;(like&nbsp;lists&nbsp;and&nbsp;tuples).<br>
+&nbsp;<br>
+For&nbsp;the&nbsp;purposes&nbsp;of&nbsp;this&nbsp;function,&nbsp;a&nbsp;valid&nbsp;ordered&nbsp;sequence&nbsp;type&nbsp;is&nbsp;one<br>
+which&nbsp;can&nbsp;be&nbsp;indexed,&nbsp;has&nbsp;a&nbsp;length,&nbsp;and&nbsp;has&nbsp;an&nbsp;equality&nbsp;operator.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;seq1:&nbsp;The&nbsp;first&nbsp;sequence&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;seq2:&nbsp;The&nbsp;second&nbsp;sequence&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;seq_type:&nbsp;The&nbsp;expected&nbsp;datatype&nbsp;of&nbsp;the&nbsp;sequences,&nbsp;or&nbsp;None&nbsp;if&nbsp;no<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;datatype&nbsp;should&nbsp;be&nbsp;enforced.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;msg:&nbsp;Optional&nbsp;message&nbsp;to&nbsp;use&nbsp;on&nbsp;failure&nbsp;instead&nbsp;of&nbsp;a&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;differences.</tt></dd></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-assertSetEqual"><strong>assertSetEqual</strong></a>(self, set1, set2, msg<font color="#909090">=None</font>)</dt><dd><tt>A&nbsp;set-specific&nbsp;equality&nbsp;assertion.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;set1:&nbsp;The&nbsp;first&nbsp;set&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;set2:&nbsp;The&nbsp;second&nbsp;set&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;msg:&nbsp;Optional&nbsp;message&nbsp;to&nbsp;use&nbsp;on&nbsp;failure&nbsp;instead&nbsp;of&nbsp;a&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;differences.<br>
+&nbsp;<br>
+assertSetEqual&nbsp;uses&nbsp;ducktyping&nbsp;to&nbsp;support&nbsp;different&nbsp;types&nbsp;of&nbsp;sets,&nbsp;and<br>
+is&nbsp;optimized&nbsp;for&nbsp;sets&nbsp;specifically&nbsp;(parameters&nbsp;must&nbsp;support&nbsp;a<br>
+difference&nbsp;method).</tt></dd></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-assertTrue"><strong>assertTrue</strong></a>(self, expr, msg<font color="#909090">=None</font>)</dt><dd><tt>Check&nbsp;that&nbsp;the&nbsp;expression&nbsp;is&nbsp;true.</tt></dd></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-assertTupleEqual"><strong>assertTupleEqual</strong></a>(self, tuple1, tuple2, msg<font color="#909090">=None</font>)</dt><dd><tt>A&nbsp;tuple-specific&nbsp;equality&nbsp;assertion.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;tuple1:&nbsp;The&nbsp;first&nbsp;tuple&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;tuple2:&nbsp;The&nbsp;second&nbsp;tuple&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;msg:&nbsp;Optional&nbsp;message&nbsp;to&nbsp;use&nbsp;on&nbsp;failure&nbsp;instead&nbsp;of&nbsp;a&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;differences.</tt></dd></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-assert_"><strong>assert_</strong></a> = assertTrue(self, expr, msg<font color="#909090">=None</font>)</dt><dd><tt>Check&nbsp;that&nbsp;the&nbsp;expression&nbsp;is&nbsp;true.</tt></dd></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-countTestCases"><strong>countTestCases</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-debug"><strong>debug</strong></a>(self)</dt><dd><tt>Run&nbsp;the&nbsp;test&nbsp;without&nbsp;collecting&nbsp;errors&nbsp;in&nbsp;a&nbsp;TestResult</tt></dd></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-defaultTestResult"><strong>defaultTestResult</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-doCleanups"><strong>doCleanups</strong></a>(self)</dt><dd><tt>Execute&nbsp;all&nbsp;cleanup&nbsp;functions.&nbsp;Normally&nbsp;called&nbsp;for&nbsp;you&nbsp;after<br>
+tearDown.</tt></dd></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-fail"><strong>fail</strong></a>(self, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;immediately,&nbsp;with&nbsp;the&nbsp;given&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-failIf"><strong>failIf</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-failIfAlmostEqual"><strong>failIfAlmostEqual</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-failIfEqual"><strong>failIfEqual</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-failUnless"><strong>failUnless</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-failUnlessAlmostEqual"><strong>failUnlessAlmostEqual</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-failUnlessEqual"><strong>failUnlessEqual</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-failUnlessRaises"><strong>failUnlessRaises</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-id"><strong>id</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-run"><strong>run</strong></a>(self, result<font color="#909090">=None</font>)</dt></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-shortDescription"><strong>shortDescription</strong></a>(self)</dt><dd><tt>Returns&nbsp;a&nbsp;one-line&nbsp;description&nbsp;of&nbsp;the&nbsp;test,&nbsp;or&nbsp;None&nbsp;if&nbsp;no<br>
+description&nbsp;has&nbsp;been&nbsp;provided.<br>
+&nbsp;<br>
+The&nbsp;default&nbsp;implementation&nbsp;of&nbsp;this&nbsp;method&nbsp;returns&nbsp;the&nbsp;first&nbsp;line&nbsp;of<br>
+the&nbsp;specified&nbsp;test&nbsp;method's&nbsp;docstring.</tt></dd></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-skipTest"><strong>skipTest</strong></a>(self, reason)</dt><dd><tt>Skip&nbsp;this&nbsp;test.</tt></dd></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-tearDown"><strong>tearDown</strong></a>(self)</dt><dd><tt>Hook&nbsp;method&nbsp;for&nbsp;deconstructing&nbsp;the&nbsp;test&nbsp;fixture&nbsp;after&nbsp;testing&nbsp;it.</tt></dd></dl>
+
+<hr>
+Class methods inherited from <a href="unittest.case.html#TestCase">unittest.case.TestCase</a>:<br>
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-setUpClass"><strong>setUpClass</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Hook&nbsp;method&nbsp;for&nbsp;setting&nbsp;up&nbsp;class&nbsp;fixture&nbsp;before&nbsp;running&nbsp;tests&nbsp;in&nbsp;the&nbsp;class.</tt></dd></dl>
+
+<dl><dt><a name="FormBasedCredentialsBackendUnitTestBase-tearDownClass"><strong>tearDownClass</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Hook&nbsp;method&nbsp;for&nbsp;deconstructing&nbsp;the&nbsp;class&nbsp;fixture&nbsp;after&nbsp;running&nbsp;all&nbsp;tests&nbsp;in&nbsp;the&nbsp;class.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="unittest.case.html#TestCase">unittest.case.TestCase</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="unittest.case.html#TestCase">unittest.case.TestCase</a>:<br>
+<dl><dt><strong>failureException</strong> = &lt;type 'exceptions.AssertionError'&gt;<dd><tt>Assertion&nbsp;failed.</tt></dl>
+
+<dl><dt><strong>longMessage</strong> = False</dl>
+
+<dl><dt><strong>maxDiff</strong> = 640</dl>
+
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.google_credentials_backend.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.google_credentials_backend.html
new file mode 100644
index 0000000..389a886
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.google_credentials_backend.html
@@ -0,0 +1,156 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.google_credentials_backend</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.google_credentials_backend</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/google_credentials_backend.py">telemetry/internal/backends/google_credentials_backend.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.backends.form_based_credentials_backend.html">telemetry.internal.backends.form_based_credentials_backend</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.form_based_credentials_backend.html#FormBasedCredentialsBackend">telemetry.internal.backends.form_based_credentials_backend.FormBasedCredentialsBackend</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.google_credentials_backend.html#GoogleCredentialsBackend">GoogleCredentialsBackend</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.google_credentials_backend.html#GoogleCredentialsBackend2">GoogleCredentialsBackend2</a>
+</font></dt></dl>
+</dd>
+</dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="GoogleCredentialsBackend">class <strong>GoogleCredentialsBackend</strong></a>(<a href="telemetry.internal.backends.form_based_credentials_backend.html#FormBasedCredentialsBackend">telemetry.internal.backends.form_based_credentials_backend.FormBasedCredentialsBackend</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.google_credentials_backend.html#GoogleCredentialsBackend">GoogleCredentialsBackend</a></dd>
+<dd><a href="telemetry.internal.backends.form_based_credentials_backend.html#FormBasedCredentialsBackend">telemetry.internal.backends.form_based_credentials_backend.FormBasedCredentialsBackend</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>credentials_type</strong></dt>
+</dl>
+<dl><dt><strong>logged_in_javascript</strong></dt>
+<dd><tt>Evaluates&nbsp;to&nbsp;true&nbsp;iff&nbsp;already&nbsp;logged&nbsp;in.</tt></dd>
+</dl>
+<dl><dt><strong>login_form_id</strong></dt>
+</dl>
+<dl><dt><strong>login_input_id</strong></dt>
+</dl>
+<dl><dt><strong>password_input_id</strong></dt>
+</dl>
+<dl><dt><strong>url</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.internal.backends.form_based_credentials_backend.html#FormBasedCredentialsBackend">telemetry.internal.backends.form_based_credentials_backend.FormBasedCredentialsBackend</a>:<br>
+<dl><dt><a name="GoogleCredentialsBackend-IsAlreadyLoggedIn"><strong>IsAlreadyLoggedIn</strong></a>(self, tab)</dt></dl>
+
+<dl><dt><a name="GoogleCredentialsBackend-IsLoggedIn"><strong>IsLoggedIn</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="GoogleCredentialsBackend-LoginNeeded"><strong>LoginNeeded</strong></a>(self, tab, action_runner, config)</dt><dd><tt>Logs&nbsp;in&nbsp;to&nbsp;a&nbsp;test&nbsp;account.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;RuntimeError:&nbsp;if&nbsp;could&nbsp;not&nbsp;get&nbsp;credential&nbsp;information.</tt></dd></dl>
+
+<dl><dt><a name="GoogleCredentialsBackend-LoginNoLongerNeeded"><strong>LoginNoLongerNeeded</strong></a>(self, tab)</dt></dl>
+
+<dl><dt><a name="GoogleCredentialsBackend-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.backends.form_based_credentials_backend.html#FormBasedCredentialsBackend">telemetry.internal.backends.form_based_credentials_backend.FormBasedCredentialsBackend</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>login_button_javascript</strong></dt>
+<dd><tt>Some&nbsp;sites&nbsp;have&nbsp;custom&nbsp;JS&nbsp;to&nbsp;log&nbsp;in.</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="GoogleCredentialsBackend2">class <strong>GoogleCredentialsBackend2</strong></a>(<a href="telemetry.internal.backends.google_credentials_backend.html#GoogleCredentialsBackend">GoogleCredentialsBackend</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Google&nbsp;credential&nbsp;backend&nbsp;for&nbsp;google2&nbsp;credential.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.google_credentials_backend.html#GoogleCredentialsBackend2">GoogleCredentialsBackend2</a></dd>
+<dd><a href="telemetry.internal.backends.google_credentials_backend.html#GoogleCredentialsBackend">GoogleCredentialsBackend</a></dd>
+<dd><a href="telemetry.internal.backends.form_based_credentials_backend.html#FormBasedCredentialsBackend">telemetry.internal.backends.form_based_credentials_backend.FormBasedCredentialsBackend</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>credentials_type</strong></dt>
+</dl>
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.backends.google_credentials_backend.html#GoogleCredentialsBackend">GoogleCredentialsBackend</a>:<br>
+<dl><dt><strong>logged_in_javascript</strong></dt>
+<dd><tt>Evaluates&nbsp;to&nbsp;true&nbsp;iff&nbsp;already&nbsp;logged&nbsp;in.</tt></dd>
+</dl>
+<dl><dt><strong>login_form_id</strong></dt>
+</dl>
+<dl><dt><strong>login_input_id</strong></dt>
+</dl>
+<dl><dt><strong>password_input_id</strong></dt>
+</dl>
+<dl><dt><strong>url</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.internal.backends.form_based_credentials_backend.html#FormBasedCredentialsBackend">telemetry.internal.backends.form_based_credentials_backend.FormBasedCredentialsBackend</a>:<br>
+<dl><dt><a name="GoogleCredentialsBackend2-IsAlreadyLoggedIn"><strong>IsAlreadyLoggedIn</strong></a>(self, tab)</dt></dl>
+
+<dl><dt><a name="GoogleCredentialsBackend2-IsLoggedIn"><strong>IsLoggedIn</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="GoogleCredentialsBackend2-LoginNeeded"><strong>LoginNeeded</strong></a>(self, tab, action_runner, config)</dt><dd><tt>Logs&nbsp;in&nbsp;to&nbsp;a&nbsp;test&nbsp;account.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;RuntimeError:&nbsp;if&nbsp;could&nbsp;not&nbsp;get&nbsp;credential&nbsp;information.</tt></dd></dl>
+
+<dl><dt><a name="GoogleCredentialsBackend2-LoginNoLongerNeeded"><strong>LoginNoLongerNeeded</strong></a>(self, tab)</dt></dl>
+
+<dl><dt><a name="GoogleCredentialsBackend2-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.backends.form_based_credentials_backend.html#FormBasedCredentialsBackend">telemetry.internal.backends.form_based_credentials_backend.FormBasedCredentialsBackend</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>login_button_javascript</strong></dt>
+<dd><tt>Some&nbsp;sites&nbsp;have&nbsp;custom&nbsp;JS&nbsp;to&nbsp;log&nbsp;in.</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.html
new file mode 100644
index 0000000..59435ea
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.html
@@ -0,0 +1,43 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: package telemetry.internal.backends</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.backends</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/__init__.py">telemetry/internal/backends/__init__.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Package Contents</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.backends.android_app_backend.html">android_app_backend</a><br>
+<a href="telemetry.internal.backends.android_browser_backend_settings.html">android_browser_backend_settings</a><br>
+<a href="telemetry.internal.backends.android_command_line_backend.html">android_command_line_backend</a><br>
+<a href="telemetry.internal.backends.android_command_line_backend_unittest.html">android_command_line_backend_unittest</a><br>
+<a href="telemetry.internal.backends.app_backend.html">app_backend</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.backends.browser_backend.html">browser_backend</a><br>
+<a href="telemetry.internal.backends.browser_backend_unittest.html">browser_backend_unittest</a><br>
+<a href="telemetry.internal.backends.chrome.html"><strong>chrome</strong>&nbsp;(package)</a><br>
+<a href="telemetry.internal.backends.chrome_inspector.html"><strong>chrome_inspector</strong>&nbsp;(package)</a><br>
+<a href="telemetry.internal.backends.codepen_credentials_backend.html">codepen_credentials_backend</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.backends.codepen_credentials_backend_unittest.html">codepen_credentials_backend_unittest</a><br>
+<a href="telemetry.internal.backends.facebook_credentials_backend.html">facebook_credentials_backend</a><br>
+<a href="telemetry.internal.backends.facebook_credentials_backend_unittest.html">facebook_credentials_backend_unittest</a><br>
+<a href="telemetry.internal.backends.form_based_credentials_backend.html">form_based_credentials_backend</a><br>
+<a href="telemetry.internal.backends.form_based_credentials_backend_unittest_base.html">form_based_credentials_backend_unittest_base</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.backends.google_credentials_backend.html">google_credentials_backend</a><br>
+<a href="telemetry.internal.backends.google_credentials_backend_unittest.html">google_credentials_backend_unittest</a><br>
+<a href="telemetry.internal.backends.mandoline.html"><strong>mandoline</strong>&nbsp;(package)</a><br>
+<a href="telemetry.internal.backends.remote.html"><strong>remote</strong>&nbsp;(package)</a><br>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.mandoline.android.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.mandoline.android.html
new file mode 100644
index 0000000..f64be17
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.mandoline.android.html
@@ -0,0 +1,94 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.mandoline.android</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.<a href="telemetry.internal.backends.mandoline.html"><font color="#ffffff">mandoline</font></a>.android</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/mandoline/android.py">telemetry/internal/backends/mandoline/android.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="devil.android.apk_helper.html">devil.android.apk_helper</a><br>
+<a href="atexit.html">atexit</a><br>
+<a href="devil.base_error.html">devil.base_error</a><br>
+<a href="pylib.constants.html">pylib.constants</a><br>
+</td><td width="25%" valign=top><a href="devil.android.device_errors.html">devil.android.device_errors</a><br>
+<a href="devil.android.device_utils.html">devil.android.device_utils</a><br>
+<a href="logging.html">logging</a><br>
+<a href="os.html">os</a><br>
+</td><td width="25%" valign=top><a href="signal.html">signal</a><br>
+<a href="subprocess.html">subprocess</a><br>
+<a href="sys.html">sys</a><br>
+<a href="threading.html">threading</a><br>
+</td><td width="25%" valign=top><a href="time.html">time</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.mandoline.android.html#AndroidShell">AndroidShell</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="AndroidShell">class <strong>AndroidShell</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Used&nbsp;to&nbsp;set&nbsp;up&nbsp;and&nbsp;run&nbsp;a&nbsp;given&nbsp;mojo&nbsp;shell&nbsp;binary&nbsp;on&nbsp;an&nbsp;Android&nbsp;device.<br>
+|config|&nbsp;is&nbsp;the&nbsp;mopy.config.Config&nbsp;for&nbsp;the&nbsp;build.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="AndroidShell-InitShell"><strong>InitShell</strong></a>(self, device<font color="#909090">=None</font>)</dt><dd><tt>Runs&nbsp;adb&nbsp;as&nbsp;root,&nbsp;and&nbsp;installs&nbsp;the&nbsp;apk&nbsp;as&nbsp;needed.&nbsp;&nbsp;|device|&nbsp;is&nbsp;the&nbsp;target<br>
+device&nbsp;to&nbsp;run&nbsp;on,&nbsp;if&nbsp;multiple&nbsp;devices&nbsp;are&nbsp;connected.&nbsp;Returns&nbsp;0&nbsp;on&nbsp;success&nbsp;or<br>
+a&nbsp;non-zero&nbsp;exit&nbsp;code&nbsp;on&nbsp;a&nbsp;terminal&nbsp;failure.</tt></dd></dl>
+
+<dl><dt><a name="AndroidShell-ShowLogs"><strong>ShowLogs</strong></a>(self, stdout<font color="#909090">=&lt;open file '&lt;stdout&gt;', mode 'w'&gt;</font>)</dt><dd><tt>Displays&nbsp;the&nbsp;mojo&nbsp;shell&nbsp;logs&nbsp;and&nbsp;returns&nbsp;the&nbsp;process&nbsp;reading&nbsp;the&nbsp;logs.</tt></dd></dl>
+
+<dl><dt><a name="AndroidShell-StartActivity"><strong>StartActivity</strong></a>(self, activity_name, arguments, stdout, on_fifo_closed, temp_gdb_dir<font color="#909090">=None</font>)</dt><dd><tt>Starts&nbsp;the&nbsp;shell&nbsp;with&nbsp;the&nbsp;given&nbsp;|arguments|,&nbsp;directing&nbsp;output&nbsp;to&nbsp;|stdout|.<br>
+|on_fifo_closed|&nbsp;will&nbsp;be&nbsp;run&nbsp;if&nbsp;the&nbsp;FIFO&nbsp;can't&nbsp;be&nbsp;found&nbsp;or&nbsp;when&nbsp;it's&nbsp;closed.<br>
+|temp_gdb_dir|&nbsp;is&nbsp;set&nbsp;to&nbsp;a&nbsp;location&nbsp;with&nbsp;appropriate&nbsp;symlinks&nbsp;for&nbsp;gdb&nbsp;to<br>
+find&nbsp;when&nbsp;attached&nbsp;to&nbsp;the&nbsp;device's&nbsp;remote&nbsp;process&nbsp;on&nbsp;startup.</tt></dd></dl>
+
+<dl><dt><a name="AndroidShell-__init__"><strong>__init__</strong></a>(self, config, chrome_root)</dt></dl>
+
+<dl><dt><a name="AndroidShell-kill"><strong>kill</strong></a>(self)</dt><dd><tt>Stops&nbsp;the&nbsp;mojo&nbsp;shell;&nbsp;matches&nbsp;the&nbsp;Popen.kill&nbsp;method&nbsp;signature.</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>LOGCAT_TAGS</strong> = ['AndroidHandler', 'MojoFileHelper', 'MojoMain', 'MojoShellActivity', 'MojoShellApplication', 'chromium']<br>
+<strong>MAPPING_PREFIX</strong> = '--map-origin='</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.mandoline.android_mandoline_backend.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.mandoline.android_mandoline_backend.html
new file mode 100644
index 0000000..a65bf6f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.mandoline.android_mandoline_backend.html
@@ -0,0 +1,189 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.mandoline.android_mandoline_backend</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.<a href="telemetry.internal.backends.mandoline.html"><font color="#ffffff">mandoline</font></a>.android_mandoline_backend</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/mandoline/android_mandoline_backend.py">telemetry/internal/backends/mandoline/android_mandoline_backend.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2015&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.backends.mandoline.android.html">telemetry.internal.backends.mandoline.android</a><br>
+<a href="telemetry.internal.platform.android_platform_backend.html">telemetry.internal.platform.android_platform_backend</a><br>
+<a href="telemetry.internal.backends.mandoline.config.html">telemetry.internal.backends.mandoline.config</a><br>
+</td><td width="25%" valign=top><a href="pylib.constants.html">pylib.constants</a><br>
+<a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+<a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.backends.mandoline.mandoline_browser_backend.html">telemetry.internal.backends.mandoline.mandoline_browser_backend</a><br>
+<a href="os.html">os</a><br>
+<a href="random.html">random</a><br>
+</td><td width="25%" valign=top><a href="re.html">re</a><br>
+<a href="sys.html">sys</a><br>
+<a href="telemetry.core.util.html">telemetry.core.util</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.mandoline.mandoline_browser_backend.html#MandolineBrowserBackend">telemetry.internal.backends.mandoline.mandoline_browser_backend.MandolineBrowserBackend</a>(<a href="telemetry.internal.backends.browser_backend.html#BrowserBackend">telemetry.internal.backends.browser_backend.BrowserBackend</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.mandoline.android_mandoline_backend.html#AndroidMandolineBackend">AndroidMandolineBackend</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="AndroidMandolineBackend">class <strong>AndroidMandolineBackend</strong></a>(<a href="telemetry.internal.backends.mandoline.mandoline_browser_backend.html#MandolineBrowserBackend">telemetry.internal.backends.mandoline.mandoline_browser_backend.MandolineBrowserBackend</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>The&nbsp;backend&nbsp;for&nbsp;controlling&nbsp;a&nbsp;mandoline&nbsp;browser&nbsp;instance&nbsp;running&nbsp;on<br>
+Android.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.mandoline.android_mandoline_backend.html#AndroidMandolineBackend">AndroidMandolineBackend</a></dd>
+<dd><a href="telemetry.internal.backends.mandoline.mandoline_browser_backend.html#MandolineBrowserBackend">telemetry.internal.backends.mandoline.mandoline_browser_backend.MandolineBrowserBackend</a></dd>
+<dd><a href="telemetry.internal.backends.browser_backend.html#BrowserBackend">telemetry.internal.backends.browser_backend.BrowserBackend</a></dd>
+<dd><a href="telemetry.internal.backends.app_backend.html#AppBackend">telemetry.internal.backends.app_backend.AppBackend</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="AndroidMandolineBackend-Close"><strong>Close</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidMandolineBackend-GetBrowserStartupArgs"><strong>GetBrowserStartupArgs</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidMandolineBackend-GetStackTrace"><strong>GetStackTrace</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidMandolineBackend-GetStandardOutput"><strong>GetStandardOutput</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidMandolineBackend-IsBrowserRunning"><strong>IsBrowserRunning</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidMandolineBackend-Start"><strong>Start</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidMandolineBackend-__del__"><strong>__del__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidMandolineBackend-__init__"><strong>__init__</strong></a>(self, android_platform_backend, browser_options, target_arch, browser_type, build_path, package, chrome_root)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>activity</strong></dt>
+</dl>
+<dl><dt><strong>browser_directory</strong></dt>
+</dl>
+<dl><dt><strong>device</strong></dt>
+</dl>
+<dl><dt><strong>package</strong></dt>
+</dl>
+<dl><dt><strong>pid</strong></dt>
+</dl>
+<dl><dt><strong>profile_directory</strong></dt>
+</dl>
+<dl><dt><strong>should_ignore_certificate_errors</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.internal.backends.mandoline.mandoline_browser_backend.html#MandolineBrowserBackend">telemetry.internal.backends.mandoline.mandoline_browser_backend.MandolineBrowserBackend</a>:<br>
+<dl><dt><a name="AndroidMandolineBackend-GetProcessName"><strong>GetProcessName</strong></a>(self, cmd_line)</dt><dd><tt>Returns&nbsp;a&nbsp;user-friendly&nbsp;name&nbsp;for&nbsp;the&nbsp;process&nbsp;of&nbsp;the&nbsp;given&nbsp;|cmd_line|.</tt></dd></dl>
+
+<dl><dt><a name="AndroidMandolineBackend-GetReplayBrowserStartupArgs"><strong>GetReplayBrowserStartupArgs</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidMandolineBackend-HasBrowserFinishedLaunching"><strong>HasBrowserFinishedLaunching</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.backends.mandoline.mandoline_browser_backend.html#MandolineBrowserBackend">telemetry.internal.backends.mandoline.mandoline_browser_backend.MandolineBrowserBackend</a>:<br>
+<dl><dt><strong>devtools_client</strong></dt>
+</dl>
+<dl><dt><strong>supports_cpu_metrics</strong></dt>
+</dl>
+<dl><dt><strong>supports_memory_metrics</strong></dt>
+</dl>
+<dl><dt><strong>supports_power_metrics</strong></dt>
+</dl>
+<dl><dt><strong>supports_system_info</strong></dt>
+</dl>
+<dl><dt><strong>supports_tab_control</strong></dt>
+</dl>
+<dl><dt><strong>supports_tracing</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.internal.backends.browser_backend.html#BrowserBackend">telemetry.internal.backends.browser_backend.BrowserBackend</a>:<br>
+<dl><dt><a name="AndroidMandolineBackend-DumpMemory"><strong>DumpMemory</strong></a>(self, timeout<font color="#909090">=90</font>)</dt></dl>
+
+<dl><dt><a name="AndroidMandolineBackend-GetSystemInfo"><strong>GetSystemInfo</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidMandolineBackend-IsAppRunning"><strong>IsAppRunning</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidMandolineBackend-SetBrowser"><strong>SetBrowser</strong></a>(self, browser)</dt></dl>
+
+<dl><dt><a name="AndroidMandolineBackend-SetMemoryPressureNotificationsSuppressed"><strong>SetMemoryPressureNotificationsSuppressed</strong></a>(self, suppressed, timeout<font color="#909090">=90</font>)</dt></dl>
+
+<dl><dt><a name="AndroidMandolineBackend-SimulateMemoryPressureNotification"><strong>SimulateMemoryPressureNotification</strong></a>(self, pressure_level, timeout<font color="#909090">=90</font>)</dt></dl>
+
+<dl><dt><a name="AndroidMandolineBackend-StartTracing"><strong>StartTracing</strong></a>(self, trace_options, custom_categories<font color="#909090">=None</font>, timeout<font color="#909090">=90</font>)</dt></dl>
+
+<dl><dt><a name="AndroidMandolineBackend-StopTracing"><strong>StopTracing</strong></a>(self, trace_data_builder)</dt></dl>
+
+<dl><dt><a name="AndroidMandolineBackend-UploadLogsToCloudStorage"><strong>UploadLogsToCloudStorage</strong></a>(self)</dt><dd><tt>Uploading&nbsp;log&nbsp;files&nbsp;produce&nbsp;by&nbsp;this&nbsp;browser&nbsp;instance&nbsp;to&nbsp;cloud&nbsp;storage.<br>
+&nbsp;<br>
+Check&nbsp;supports_uploading_logs&nbsp;before&nbsp;calling&nbsp;this&nbsp;method.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.backends.browser_backend.html#BrowserBackend">telemetry.internal.backends.browser_backend.BrowserBackend</a>:<br>
+<dl><dt><strong>browser</strong></dt>
+</dl>
+<dl><dt><strong>browser_type</strong></dt>
+</dl>
+<dl><dt><strong>log_file_path</strong></dt>
+</dl>
+<dl><dt><strong>profiling_controller_backend</strong></dt>
+</dl>
+<dl><dt><strong>supports_extensions</strong></dt>
+<dd><tt>True&nbsp;if&nbsp;this&nbsp;browser&nbsp;backend&nbsp;supports&nbsp;extensions.</tt></dd>
+</dl>
+<dl><dt><strong>supports_memory_dumping</strong></dt>
+</dl>
+<dl><dt><strong>supports_overriding_memory_pressure_notifications</strong></dt>
+</dl>
+<dl><dt><strong>supports_uploading_logs</strong></dt>
+</dl>
+<dl><dt><strong>tab_list_backend</strong></dt>
+</dl>
+<dl><dt><strong>wpr_mode</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.internal.backends.app_backend.html#AppBackend">telemetry.internal.backends.app_backend.AppBackend</a>:<br>
+<dl><dt><a name="AndroidMandolineBackend-SetApp"><strong>SetApp</strong></a>(self, app)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.backends.app_backend.html#AppBackend">telemetry.internal.backends.app_backend.AppBackend</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>app</strong></dt>
+</dl>
+<dl><dt><strong>app_type</strong></dt>
+</dl>
+<dl><dt><strong>platform_backend</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.mandoline.android_mandoline_finder.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.mandoline.android_mandoline_finder.html
new file mode 100644
index 0000000..6131199
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.mandoline.android_mandoline_finder.html
@@ -0,0 +1,120 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.mandoline.android_mandoline_finder</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.<a href="telemetry.internal.backends.mandoline.html"><font color="#ffffff">mandoline</font></a>.android_mandoline_finder</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/mandoline/android_mandoline_finder.py">telemetry/internal/backends/mandoline/android_mandoline_finder.py</a></font></td></tr></table>
+    <p><tt>Finds&nbsp;android&nbsp;mandoline&nbsp;browsers&nbsp;that&nbsp;can&nbsp;be&nbsp;controlled&nbsp;by&nbsp;telemetry.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.platform.android_device.html">telemetry.internal.platform.android_device</a><br>
+<a href="telemetry.internal.backends.mandoline.android_mandoline_backend.html">telemetry.internal.backends.mandoline.android_mandoline_backend</a><br>
+<a href="devil.android.apk_helper.html">devil.android.apk_helper</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.browser.browser.html">telemetry.internal.browser.browser</a><br>
+<a href="logging.html">logging</a><br>
+<a href="os.html">os</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.util.path.html">telemetry.internal.util.path</a><br>
+<a href="telemetry.core.platform.html">telemetry.core.platform</a><br>
+<a href="telemetry.internal.browser.possible_browser.html">telemetry.internal.browser.possible_browser</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.browser.possible_browser.html#PossibleBrowser">telemetry.internal.browser.possible_browser.PossibleBrowser</a>(<a href="telemetry.internal.app.possible_app.html#PossibleApp">telemetry.internal.app.possible_app.PossibleApp</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.mandoline.android_mandoline_finder.html#PossibleAndroidMandolineBrowser">PossibleAndroidMandolineBrowser</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="PossibleAndroidMandolineBrowser">class <strong>PossibleAndroidMandolineBrowser</strong></a>(<a href="telemetry.internal.browser.possible_browser.html#PossibleBrowser">telemetry.internal.browser.possible_browser.PossibleBrowser</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;launchable&nbsp;android&nbsp;mandoline&nbsp;browser&nbsp;instance.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.mandoline.android_mandoline_finder.html#PossibleAndroidMandolineBrowser">PossibleAndroidMandolineBrowser</a></dd>
+<dd><a href="telemetry.internal.browser.possible_browser.html#PossibleBrowser">telemetry.internal.browser.possible_browser.PossibleBrowser</a></dd>
+<dd><a href="telemetry.internal.app.possible_app.html#PossibleApp">telemetry.internal.app.possible_app.PossibleApp</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="PossibleAndroidMandolineBrowser-Create"><strong>Create</strong></a>(self, finder_options)</dt></dl>
+
+<dl><dt><a name="PossibleAndroidMandolineBrowser-HaveLocalAPK"><strong>HaveLocalAPK</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PossibleAndroidMandolineBrowser-SupportsOptions"><strong>SupportsOptions</strong></a>(self, finder_options)</dt></dl>
+
+<dl><dt><a name="PossibleAndroidMandolineBrowser-UpdateExecutableIfNeeded"><strong>UpdateExecutableIfNeeded</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PossibleAndroidMandolineBrowser-__init__"><strong>__init__</strong></a>(self, browser_type, finder_options, android_platform, build_path, local_apk)</dt></dl>
+
+<dl><dt><a name="PossibleAndroidMandolineBrowser-__repr__"><strong>__repr__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PossibleAndroidMandolineBrowser-last_modification_time"><strong>last_modification_time</strong></a>(self)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.browser.possible_browser.html#PossibleBrowser">telemetry.internal.browser.possible_browser.PossibleBrowser</a>:<br>
+<dl><dt><a name="PossibleAndroidMandolineBrowser-IsRemote"><strong>IsRemote</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PossibleAndroidMandolineBrowser-RunRemote"><strong>RunRemote</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PossibleAndroidMandolineBrowser-SetCredentialsPath"><strong>SetCredentialsPath</strong></a>(self, credentials_path)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.browser.possible_browser.html#PossibleBrowser">telemetry.internal.browser.possible_browser.PossibleBrowser</a>:<br>
+<dl><dt><strong>browser_type</strong></dt>
+</dl>
+<dl><dt><strong>supports_tab_control</strong></dt>
+</dl>
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.app.possible_app.html#PossibleApp">telemetry.internal.app.possible_app.PossibleApp</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>app_type</strong></dt>
+</dl>
+<dl><dt><strong>platform</strong></dt>
+</dl>
+<dl><dt><strong>target_os</strong></dt>
+<dd><tt>Target&nbsp;OS,&nbsp;the&nbsp;app&nbsp;will&nbsp;run&nbsp;on.</tt></dd>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-CanFindAvailableBrowsers"><strong>CanFindAvailableBrowsers</strong></a>()</dt></dl>
+ <dl><dt><a name="-FindAllAvailableBrowsers"><strong>FindAllAvailableBrowsers</strong></a>(finder_options, device)</dt><dd><tt>Finds&nbsp;all&nbsp;the&nbsp;possible&nbsp;browsers&nbsp;to&nbsp;run&nbsp;on&nbsp;the&nbsp;device.<br>
+&nbsp;<br>
+The&nbsp;device&nbsp;is&nbsp;either&nbsp;the&nbsp;only&nbsp;device&nbsp;on&nbsp;the&nbsp;host&nbsp;platform,<br>
+or&nbsp;|finder_options|&nbsp;specifies&nbsp;a&nbsp;particular&nbsp;device.</tt></dd></dl>
+ <dl><dt><a name="-FindAllBrowserTypes"><strong>FindAllBrowserTypes</strong></a>(_options)</dt></dl>
+ <dl><dt><a name="-SelectDefaultBrowser"><strong>SelectDefaultBrowser</strong></a>(possible_browsers)</dt><dd><tt>Returns&nbsp;the&nbsp;newest&nbsp;possible&nbsp;browser.</tt></dd></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.mandoline.config.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.mandoline.config.html
new file mode 100644
index 0000000..08beced
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.mandoline.config.html
@@ -0,0 +1,112 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.mandoline.config</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.<a href="telemetry.internal.backends.mandoline.html"><font color="#ffffff">mandoline</font></a>.config</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/mandoline/config.py">telemetry/internal/backends/mandoline/config.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="ast.html">ast</a><br>
+<a href="os.html">os</a><br>
+</td><td width="25%" valign=top><a href="platform.html">platform</a><br>
+<a href="re.html">re</a><br>
+</td><td width="25%" valign=top><a href="sys.html">sys</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.mandoline.config.html#Config">Config</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="Config">class <strong>Config</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;<a href="#Config">Config</a>&nbsp;contains&nbsp;a&nbsp;dictionary&nbsp;that&nbsp;species&nbsp;a&nbsp;build&nbsp;configuration.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="Config-__init__"><strong>__init__</strong></a>(self, build_dir<font color="#909090">=None</font>, target_os<font color="#909090">=None</font>, target_cpu<font color="#909090">=None</font>, is_debug<font color="#909090">=None</font>, is_verbose<font color="#909090">=None</font>, apk_name<font color="#909090">='MojoRunner.apk'</font>)</dt><dd><tt>Function&nbsp;arguments&nbsp;take&nbsp;precedence&nbsp;over&nbsp;GN&nbsp;args&nbsp;and&nbsp;default&nbsp;values.</tt></dd></dl>
+
+<hr>
+Static methods defined here:<br>
+<dl><dt><a name="Config-GetHostCPU"><strong>GetHostCPU</strong></a>()</dt></dl>
+
+<dl><dt><a name="Config-GetHostOS"><strong>GetHostOS</strong></a>()</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>apk_name</strong></dt>
+<dd><tt>Name&nbsp;of&nbsp;the&nbsp;APK&nbsp;file&nbsp;to&nbsp;run</tt></dd>
+</dl>
+<dl><dt><strong>build_dir</strong></dt>
+<dd><tt>Build&nbsp;directory&nbsp;path.</tt></dd>
+</dl>
+<dl><dt><strong>dcheck_always_on</strong></dt>
+<dd><tt>DCHECK&nbsp;and&nbsp;MOJO_DCHECK&nbsp;are&nbsp;fatal&nbsp;even&nbsp;in&nbsp;release&nbsp;builds</tt></dd>
+</dl>
+<dl><dt><strong>is_asan</strong></dt>
+<dd><tt>Is&nbsp;ASAN&nbsp;build?</tt></dd>
+</dl>
+<dl><dt><strong>is_debug</strong></dt>
+<dd><tt>Is&nbsp;Debug&nbsp;build?</tt></dd>
+</dl>
+<dl><dt><strong>is_verbose</strong></dt>
+<dd><tt>Should&nbsp;print&nbsp;additional&nbsp;logging&nbsp;information?</tt></dd>
+</dl>
+<dl><dt><strong>target_cpu</strong></dt>
+<dd><tt>CPU&nbsp;arch&nbsp;of&nbsp;the&nbsp;build/test&nbsp;target.</tt></dd>
+</dl>
+<dl><dt><strong>target_os</strong></dt>
+<dd><tt>OS&nbsp;of&nbsp;the&nbsp;build/test&nbsp;target.</tt></dd>
+</dl>
+<hr>
+Data and other attributes defined here:<br>
+<dl><dt><strong>ARCH_ARM</strong> = 'arm'</dl>
+
+<dl><dt><strong>ARCH_X64</strong> = 'x64'</dl>
+
+<dl><dt><strong>ARCH_X86</strong> = 'x86'</dl>
+
+<dl><dt><strong>OS_ANDROID</strong> = 'android'</dl>
+
+<dl><dt><strong>OS_CHROMEOS</strong> = 'chromeos'</dl>
+
+<dl><dt><strong>OS_LINUX</strong> = 'linux'</dl>
+
+<dl><dt><strong>OS_MAC</strong> = 'mac'</dl>
+
+<dl><dt><strong>OS_WINDOWS</strong> = 'windows'</dl>
+
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.mandoline.desktop_mandoline_backend.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.mandoline.desktop_mandoline_backend.html
new file mode 100644
index 0000000..a587529
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.mandoline.desktop_mandoline_backend.html
@@ -0,0 +1,180 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.mandoline.desktop_mandoline_backend</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.<a href="telemetry.internal.backends.mandoline.html"><font color="#ffffff">mandoline</font></a>.desktop_mandoline_backend</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/mandoline/desktop_mandoline_backend.py">telemetry/internal/backends/mandoline/desktop_mandoline_backend.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2015&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+<a href="logging.html">logging</a><br>
+<a href="telemetry.internal.backends.mandoline.mandoline_browser_backend.html">telemetry.internal.backends.mandoline.mandoline_browser_backend</a><br>
+</td><td width="25%" valign=top><a href="os.html">os</a><br>
+<a href="shutil.html">shutil</a><br>
+<a href="subprocess.html">subprocess</a><br>
+</td><td width="25%" valign=top><a href="sys.html">sys</a><br>
+<a href="tempfile.html">tempfile</a><br>
+<a href="telemetry.core.util.html">telemetry.core.util</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.mandoline.mandoline_browser_backend.html#MandolineBrowserBackend">telemetry.internal.backends.mandoline.mandoline_browser_backend.MandolineBrowserBackend</a>(<a href="telemetry.internal.backends.browser_backend.html#BrowserBackend">telemetry.internal.backends.browser_backend.BrowserBackend</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.mandoline.desktop_mandoline_backend.html#DesktopMandolineBackend">DesktopMandolineBackend</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="DesktopMandolineBackend">class <strong>DesktopMandolineBackend</strong></a>(<a href="telemetry.internal.backends.mandoline.mandoline_browser_backend.html#MandolineBrowserBackend">telemetry.internal.backends.mandoline.mandoline_browser_backend.MandolineBrowserBackend</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>The&nbsp;backend&nbsp;for&nbsp;controlling&nbsp;a&nbsp;locally-executed&nbsp;browser&nbsp;instance,&nbsp;on&nbsp;Linux<br>
+or&nbsp;Windows.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.mandoline.desktop_mandoline_backend.html#DesktopMandolineBackend">DesktopMandolineBackend</a></dd>
+<dd><a href="telemetry.internal.backends.mandoline.mandoline_browser_backend.html#MandolineBrowserBackend">telemetry.internal.backends.mandoline.mandoline_browser_backend.MandolineBrowserBackend</a></dd>
+<dd><a href="telemetry.internal.backends.browser_backend.html#BrowserBackend">telemetry.internal.backends.browser_backend.BrowserBackend</a></dd>
+<dd><a href="telemetry.internal.backends.app_backend.html#AppBackend">telemetry.internal.backends.app_backend.AppBackend</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="DesktopMandolineBackend-Close"><strong>Close</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DesktopMandolineBackend-GetBrowserStartupArgs"><strong>GetBrowserStartupArgs</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DesktopMandolineBackend-GetStackTrace"><strong>GetStackTrace</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DesktopMandolineBackend-GetStandardOutput"><strong>GetStandardOutput</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DesktopMandolineBackend-HasBrowserFinishedLaunching"><strong>HasBrowserFinishedLaunching</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DesktopMandolineBackend-IsBrowserRunning"><strong>IsBrowserRunning</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DesktopMandolineBackend-Start"><strong>Start</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DesktopMandolineBackend-__del__"><strong>__del__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DesktopMandolineBackend-__init__"><strong>__init__</strong></a>(self, desktop_platform_backend, browser_options, executable, browser_directory)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>browser_directory</strong></dt>
+</dl>
+<dl><dt><strong>pid</strong></dt>
+</dl>
+<dl><dt><strong>profile_directory</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.internal.backends.mandoline.mandoline_browser_backend.html#MandolineBrowserBackend">telemetry.internal.backends.mandoline.mandoline_browser_backend.MandolineBrowserBackend</a>:<br>
+<dl><dt><a name="DesktopMandolineBackend-GetProcessName"><strong>GetProcessName</strong></a>(self, cmd_line)</dt><dd><tt>Returns&nbsp;a&nbsp;user-friendly&nbsp;name&nbsp;for&nbsp;the&nbsp;process&nbsp;of&nbsp;the&nbsp;given&nbsp;|cmd_line|.</tt></dd></dl>
+
+<dl><dt><a name="DesktopMandolineBackend-GetReplayBrowserStartupArgs"><strong>GetReplayBrowserStartupArgs</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.backends.mandoline.mandoline_browser_backend.html#MandolineBrowserBackend">telemetry.internal.backends.mandoline.mandoline_browser_backend.MandolineBrowserBackend</a>:<br>
+<dl><dt><strong>devtools_client</strong></dt>
+</dl>
+<dl><dt><strong>supports_cpu_metrics</strong></dt>
+</dl>
+<dl><dt><strong>supports_memory_metrics</strong></dt>
+</dl>
+<dl><dt><strong>supports_power_metrics</strong></dt>
+</dl>
+<dl><dt><strong>supports_system_info</strong></dt>
+</dl>
+<dl><dt><strong>supports_tab_control</strong></dt>
+</dl>
+<dl><dt><strong>supports_tracing</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.internal.backends.browser_backend.html#BrowserBackend">telemetry.internal.backends.browser_backend.BrowserBackend</a>:<br>
+<dl><dt><a name="DesktopMandolineBackend-DumpMemory"><strong>DumpMemory</strong></a>(self, timeout<font color="#909090">=90</font>)</dt></dl>
+
+<dl><dt><a name="DesktopMandolineBackend-GetSystemInfo"><strong>GetSystemInfo</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DesktopMandolineBackend-IsAppRunning"><strong>IsAppRunning</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DesktopMandolineBackend-SetBrowser"><strong>SetBrowser</strong></a>(self, browser)</dt></dl>
+
+<dl><dt><a name="DesktopMandolineBackend-SetMemoryPressureNotificationsSuppressed"><strong>SetMemoryPressureNotificationsSuppressed</strong></a>(self, suppressed, timeout<font color="#909090">=90</font>)</dt></dl>
+
+<dl><dt><a name="DesktopMandolineBackend-SimulateMemoryPressureNotification"><strong>SimulateMemoryPressureNotification</strong></a>(self, pressure_level, timeout<font color="#909090">=90</font>)</dt></dl>
+
+<dl><dt><a name="DesktopMandolineBackend-StartTracing"><strong>StartTracing</strong></a>(self, trace_options, custom_categories<font color="#909090">=None</font>, timeout<font color="#909090">=90</font>)</dt></dl>
+
+<dl><dt><a name="DesktopMandolineBackend-StopTracing"><strong>StopTracing</strong></a>(self, trace_data_builder)</dt></dl>
+
+<dl><dt><a name="DesktopMandolineBackend-UploadLogsToCloudStorage"><strong>UploadLogsToCloudStorage</strong></a>(self)</dt><dd><tt>Uploading&nbsp;log&nbsp;files&nbsp;produce&nbsp;by&nbsp;this&nbsp;browser&nbsp;instance&nbsp;to&nbsp;cloud&nbsp;storage.<br>
+&nbsp;<br>
+Check&nbsp;supports_uploading_logs&nbsp;before&nbsp;calling&nbsp;this&nbsp;method.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.backends.browser_backend.html#BrowserBackend">telemetry.internal.backends.browser_backend.BrowserBackend</a>:<br>
+<dl><dt><strong>browser</strong></dt>
+</dl>
+<dl><dt><strong>browser_type</strong></dt>
+</dl>
+<dl><dt><strong>log_file_path</strong></dt>
+</dl>
+<dl><dt><strong>profiling_controller_backend</strong></dt>
+</dl>
+<dl><dt><strong>should_ignore_certificate_errors</strong></dt>
+</dl>
+<dl><dt><strong>supports_extensions</strong></dt>
+<dd><tt>True&nbsp;if&nbsp;this&nbsp;browser&nbsp;backend&nbsp;supports&nbsp;extensions.</tt></dd>
+</dl>
+<dl><dt><strong>supports_memory_dumping</strong></dt>
+</dl>
+<dl><dt><strong>supports_overriding_memory_pressure_notifications</strong></dt>
+</dl>
+<dl><dt><strong>supports_uploading_logs</strong></dt>
+</dl>
+<dl><dt><strong>tab_list_backend</strong></dt>
+</dl>
+<dl><dt><strong>wpr_mode</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.internal.backends.app_backend.html#AppBackend">telemetry.internal.backends.app_backend.AppBackend</a>:<br>
+<dl><dt><a name="DesktopMandolineBackend-SetApp"><strong>SetApp</strong></a>(self, app)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.backends.app_backend.html#AppBackend">telemetry.internal.backends.app_backend.AppBackend</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>app</strong></dt>
+</dl>
+<dl><dt><strong>app_type</strong></dt>
+</dl>
+<dl><dt><strong>platform_backend</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.mandoline.desktop_mandoline_finder.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.mandoline.desktop_mandoline_finder.html
new file mode 100644
index 0000000..5b881aa
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.mandoline.desktop_mandoline_finder.html
@@ -0,0 +1,117 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.mandoline.desktop_mandoline_finder</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.<a href="telemetry.internal.backends.mandoline.html"><font color="#ffffff">mandoline</font></a>.desktop_mandoline_finder</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/mandoline/desktop_mandoline_finder.py">telemetry/internal/backends/mandoline/desktop_mandoline_finder.py</a></font></td></tr></table>
+    <p><tt>Finds&nbsp;desktop&nbsp;mandoline&nbsp;browsers&nbsp;that&nbsp;can&nbsp;be&nbsp;controlled&nbsp;by&nbsp;telemetry.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.browser.browser.html">telemetry.internal.browser.browser</a><br>
+<a href="telemetry.internal.platform.desktop_device.html">telemetry.internal.platform.desktop_device</a><br>
+<a href="telemetry.internal.backends.mandoline.desktop_mandoline_backend.html">telemetry.internal.backends.mandoline.desktop_mandoline_backend</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+<a href="logging.html">logging</a><br>
+<a href="os.html">os</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.util.path.html">telemetry.internal.util.path</a><br>
+<a href="telemetry.core.platform.html">telemetry.core.platform</a><br>
+<a href="telemetry.internal.browser.possible_browser.html">telemetry.internal.browser.possible_browser</a><br>
+</td><td width="25%" valign=top><a href="sys.html">sys</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.browser.possible_browser.html#PossibleBrowser">telemetry.internal.browser.possible_browser.PossibleBrowser</a>(<a href="telemetry.internal.app.possible_app.html#PossibleApp">telemetry.internal.app.possible_app.PossibleApp</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.mandoline.desktop_mandoline_finder.html#PossibleDesktopMandolineBrowser">PossibleDesktopMandolineBrowser</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="PossibleDesktopMandolineBrowser">class <strong>PossibleDesktopMandolineBrowser</strong></a>(<a href="telemetry.internal.browser.possible_browser.html#PossibleBrowser">telemetry.internal.browser.possible_browser.PossibleBrowser</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;desktop&nbsp;mandoline&nbsp;browser&nbsp;that&nbsp;can&nbsp;be&nbsp;controlled.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.mandoline.desktop_mandoline_finder.html#PossibleDesktopMandolineBrowser">PossibleDesktopMandolineBrowser</a></dd>
+<dd><a href="telemetry.internal.browser.possible_browser.html#PossibleBrowser">telemetry.internal.browser.possible_browser.PossibleBrowser</a></dd>
+<dd><a href="telemetry.internal.app.possible_app.html#PossibleApp">telemetry.internal.app.possible_app.PossibleApp</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="PossibleDesktopMandolineBrowser-Create"><strong>Create</strong></a>(self, finder_options)</dt></dl>
+
+<dl><dt><a name="PossibleDesktopMandolineBrowser-SupportsOptions"><strong>SupportsOptions</strong></a>(self, finder_options)</dt></dl>
+
+<dl><dt><a name="PossibleDesktopMandolineBrowser-UpdateExecutableIfNeeded"><strong>UpdateExecutableIfNeeded</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PossibleDesktopMandolineBrowser-__init__"><strong>__init__</strong></a>(self, browser_type, finder_options, executable, browser_directory)</dt></dl>
+
+<dl><dt><a name="PossibleDesktopMandolineBrowser-__repr__"><strong>__repr__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PossibleDesktopMandolineBrowser-last_modification_time"><strong>last_modification_time</strong></a>(self)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.browser.possible_browser.html#PossibleBrowser">telemetry.internal.browser.possible_browser.PossibleBrowser</a>:<br>
+<dl><dt><a name="PossibleDesktopMandolineBrowser-IsRemote"><strong>IsRemote</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PossibleDesktopMandolineBrowser-RunRemote"><strong>RunRemote</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PossibleDesktopMandolineBrowser-SetCredentialsPath"><strong>SetCredentialsPath</strong></a>(self, credentials_path)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.browser.possible_browser.html#PossibleBrowser">telemetry.internal.browser.possible_browser.PossibleBrowser</a>:<br>
+<dl><dt><strong>browser_type</strong></dt>
+</dl>
+<dl><dt><strong>supports_tab_control</strong></dt>
+</dl>
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.app.possible_app.html#PossibleApp">telemetry.internal.app.possible_app.PossibleApp</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>app_type</strong></dt>
+</dl>
+<dl><dt><strong>platform</strong></dt>
+</dl>
+<dl><dt><strong>target_os</strong></dt>
+<dd><tt>Target&nbsp;OS,&nbsp;the&nbsp;app&nbsp;will&nbsp;run&nbsp;on.</tt></dd>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-CanFindAvailableBrowsers"><strong>CanFindAvailableBrowsers</strong></a>()</dt></dl>
+ <dl><dt><a name="-CanPossiblyHandlePath"><strong>CanPossiblyHandlePath</strong></a>(target_path)</dt></dl>
+ <dl><dt><a name="-FindAllAvailableBrowsers"><strong>FindAllAvailableBrowsers</strong></a>(finder_options, device)</dt><dd><tt>Finds&nbsp;all&nbsp;the&nbsp;desktop&nbsp;mandoline&nbsp;browsers&nbsp;available&nbsp;on&nbsp;this&nbsp;machine.</tt></dd></dl>
+ <dl><dt><a name="-FindAllBrowserTypes"><strong>FindAllBrowserTypes</strong></a>(_)</dt></dl>
+ <dl><dt><a name="-SelectDefaultBrowser"><strong>SelectDefaultBrowser</strong></a>(possible_browsers)</dt></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.mandoline.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.mandoline.html
new file mode 100644
index 0000000..05bed22
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.mandoline.html
@@ -0,0 +1,33 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: package telemetry.internal.backends.mandoline</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.mandoline</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/mandoline/__init__.py">telemetry/internal/backends/mandoline/__init__.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2015&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Package Contents</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.backends.mandoline.android.html">android</a><br>
+<a href="telemetry.internal.backends.mandoline.android_mandoline_backend.html">android_mandoline_backend</a><br>
+<a href="telemetry.internal.backends.mandoline.android_mandoline_finder.html">android_mandoline_finder</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.backends.mandoline.config.html">config</a><br>
+<a href="telemetry.internal.backends.mandoline.desktop_mandoline_backend.html">desktop_mandoline_backend</a><br>
+<a href="telemetry.internal.backends.mandoline.desktop_mandoline_finder.html">desktop_mandoline_finder</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.backends.mandoline.desktop_mandoline_finder_unittest.html">desktop_mandoline_finder_unittest</a><br>
+<a href="telemetry.internal.backends.mandoline.mandoline_browser_backend.html">mandoline_browser_backend</a><br>
+<a href="telemetry.internal.backends.mandoline.paths.html">paths</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.mandoline.mandoline_browser_backend.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.mandoline.mandoline_browser_backend.html
new file mode 100644
index 0000000..cfb90e8
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.mandoline.mandoline_browser_backend.html
@@ -0,0 +1,175 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.mandoline.mandoline_browser_backend</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.<a href="telemetry.internal.backends.mandoline.html"><font color="#ffffff">mandoline</font></a>.mandoline_browser_backend</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/mandoline/mandoline_browser_backend.py">telemetry/internal/backends/mandoline/mandoline_browser_backend.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2015&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.backends.browser_backend.html">telemetry.internal.backends.browser_backend</a><br>
+<a href="telemetry.internal.backends.chrome_inspector.devtools_client_backend.html">telemetry.internal.backends.chrome_inspector.devtools_client_backend</a><br>
+<a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.forwarders.html">telemetry.internal.forwarders</a><br>
+<a href="logging.html">logging</a><br>
+<a href="re.html">re</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.backends.chrome.tab_list_backend.html">telemetry.internal.backends.chrome.tab_list_backend</a><br>
+<a href="telemetry.core.util.html">telemetry.core.util</a><br>
+<a href="telemetry.util.wpr_modes.html">telemetry.util.wpr_modes</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.browser_backend.html#BrowserBackend">telemetry.internal.backends.browser_backend.BrowserBackend</a>(<a href="telemetry.internal.backends.app_backend.html#AppBackend">telemetry.internal.backends.app_backend.AppBackend</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.mandoline.mandoline_browser_backend.html#MandolineBrowserBackend">MandolineBrowserBackend</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="MandolineBrowserBackend">class <strong>MandolineBrowserBackend</strong></a>(<a href="telemetry.internal.backends.browser_backend.html#BrowserBackend">telemetry.internal.backends.browser_backend.BrowserBackend</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>An&nbsp;abstract&nbsp;class&nbsp;for&nbsp;mandoline&nbsp;browser&nbsp;backends.&nbsp;Provides&nbsp;basic<br>
+functionality&nbsp;once&nbsp;a&nbsp;remote-debugger&nbsp;port&nbsp;has&nbsp;been&nbsp;established.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.mandoline.mandoline_browser_backend.html#MandolineBrowserBackend">MandolineBrowserBackend</a></dd>
+<dd><a href="telemetry.internal.backends.browser_backend.html#BrowserBackend">telemetry.internal.backends.browser_backend.BrowserBackend</a></dd>
+<dd><a href="telemetry.internal.backends.app_backend.html#AppBackend">telemetry.internal.backends.app_backend.AppBackend</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="MandolineBrowserBackend-Close"><strong>Close</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MandolineBrowserBackend-GetBrowserStartupArgs"><strong>GetBrowserStartupArgs</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MandolineBrowserBackend-GetProcessName"><strong>GetProcessName</strong></a>(self, cmd_line)</dt><dd><tt>Returns&nbsp;a&nbsp;user-friendly&nbsp;name&nbsp;for&nbsp;the&nbsp;process&nbsp;of&nbsp;the&nbsp;given&nbsp;|cmd_line|.</tt></dd></dl>
+
+<dl><dt><a name="MandolineBrowserBackend-GetReplayBrowserStartupArgs"><strong>GetReplayBrowserStartupArgs</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MandolineBrowserBackend-HasBrowserFinishedLaunching"><strong>HasBrowserFinishedLaunching</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MandolineBrowserBackend-__init__"><strong>__init__</strong></a>(self, platform_backend, browser_options)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>browser_directory</strong></dt>
+</dl>
+<dl><dt><strong>devtools_client</strong></dt>
+</dl>
+<dl><dt><strong>profile_directory</strong></dt>
+</dl>
+<dl><dt><strong>supports_cpu_metrics</strong></dt>
+</dl>
+<dl><dt><strong>supports_memory_metrics</strong></dt>
+</dl>
+<dl><dt><strong>supports_power_metrics</strong></dt>
+</dl>
+<dl><dt><strong>supports_system_info</strong></dt>
+</dl>
+<dl><dt><strong>supports_tab_control</strong></dt>
+</dl>
+<dl><dt><strong>supports_tracing</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.internal.backends.browser_backend.html#BrowserBackend">telemetry.internal.backends.browser_backend.BrowserBackend</a>:<br>
+<dl><dt><a name="MandolineBrowserBackend-DumpMemory"><strong>DumpMemory</strong></a>(self, timeout<font color="#909090">=90</font>)</dt></dl>
+
+<dl><dt><a name="MandolineBrowserBackend-GetStackTrace"><strong>GetStackTrace</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MandolineBrowserBackend-GetStandardOutput"><strong>GetStandardOutput</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MandolineBrowserBackend-GetSystemInfo"><strong>GetSystemInfo</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MandolineBrowserBackend-IsAppRunning"><strong>IsAppRunning</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MandolineBrowserBackend-IsBrowserRunning"><strong>IsBrowserRunning</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MandolineBrowserBackend-SetBrowser"><strong>SetBrowser</strong></a>(self, browser)</dt></dl>
+
+<dl><dt><a name="MandolineBrowserBackend-SetMemoryPressureNotificationsSuppressed"><strong>SetMemoryPressureNotificationsSuppressed</strong></a>(self, suppressed, timeout<font color="#909090">=90</font>)</dt></dl>
+
+<dl><dt><a name="MandolineBrowserBackend-SimulateMemoryPressureNotification"><strong>SimulateMemoryPressureNotification</strong></a>(self, pressure_level, timeout<font color="#909090">=90</font>)</dt></dl>
+
+<dl><dt><a name="MandolineBrowserBackend-Start"><strong>Start</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MandolineBrowserBackend-StartTracing"><strong>StartTracing</strong></a>(self, trace_options, custom_categories<font color="#909090">=None</font>, timeout<font color="#909090">=90</font>)</dt></dl>
+
+<dl><dt><a name="MandolineBrowserBackend-StopTracing"><strong>StopTracing</strong></a>(self, trace_data_builder)</dt></dl>
+
+<dl><dt><a name="MandolineBrowserBackend-UploadLogsToCloudStorage"><strong>UploadLogsToCloudStorage</strong></a>(self)</dt><dd><tt>Uploading&nbsp;log&nbsp;files&nbsp;produce&nbsp;by&nbsp;this&nbsp;browser&nbsp;instance&nbsp;to&nbsp;cloud&nbsp;storage.<br>
+&nbsp;<br>
+Check&nbsp;supports_uploading_logs&nbsp;before&nbsp;calling&nbsp;this&nbsp;method.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.backends.browser_backend.html#BrowserBackend">telemetry.internal.backends.browser_backend.BrowserBackend</a>:<br>
+<dl><dt><strong>browser</strong></dt>
+</dl>
+<dl><dt><strong>browser_type</strong></dt>
+</dl>
+<dl><dt><strong>log_file_path</strong></dt>
+</dl>
+<dl><dt><strong>profiling_controller_backend</strong></dt>
+</dl>
+<dl><dt><strong>should_ignore_certificate_errors</strong></dt>
+</dl>
+<dl><dt><strong>supports_extensions</strong></dt>
+<dd><tt>True&nbsp;if&nbsp;this&nbsp;browser&nbsp;backend&nbsp;supports&nbsp;extensions.</tt></dd>
+</dl>
+<dl><dt><strong>supports_memory_dumping</strong></dt>
+</dl>
+<dl><dt><strong>supports_overriding_memory_pressure_notifications</strong></dt>
+</dl>
+<dl><dt><strong>supports_uploading_logs</strong></dt>
+</dl>
+<dl><dt><strong>tab_list_backend</strong></dt>
+</dl>
+<dl><dt><strong>wpr_mode</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.internal.backends.app_backend.html#AppBackend">telemetry.internal.backends.app_backend.AppBackend</a>:<br>
+<dl><dt><a name="MandolineBrowserBackend-SetApp"><strong>SetApp</strong></a>(self, app)</dt></dl>
+
+<dl><dt><a name="MandolineBrowserBackend-__del__"><strong>__del__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.backends.app_backend.html#AppBackend">telemetry.internal.backends.app_backend.AppBackend</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>app</strong></dt>
+</dl>
+<dl><dt><strong>app_type</strong></dt>
+</dl>
+<dl><dt><strong>pid</strong></dt>
+</dl>
+<dl><dt><strong>platform_backend</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.mandoline.paths.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.mandoline.paths.html
new file mode 100644
index 0000000..839c9c1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.mandoline.paths.html
@@ -0,0 +1,64 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.mandoline.paths</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.<a href="telemetry.internal.backends.mandoline.html"><font color="#ffffff">mandoline</font></a>.paths</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/mandoline/paths.py">telemetry/internal/backends/mandoline/paths.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="os.html">os</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.mandoline.paths.html#Paths">Paths</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="Paths">class <strong>Paths</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Provides&nbsp;commonly&nbsp;used&nbsp;paths<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="Paths-RelPath"><strong>RelPath</strong></a>(self, path)</dt><dd><tt>Returns&nbsp;the&nbsp;given&nbsp;path,&nbsp;relative&nbsp;to&nbsp;the&nbsp;current&nbsp;directory.</tt></dd></dl>
+
+<dl><dt><a name="Paths-SrcRelPath"><strong>SrcRelPath</strong></a>(self, path)</dt><dd><tt>Returns&nbsp;the&nbsp;given&nbsp;path,&nbsp;relative&nbsp;to&nbsp;self.<strong>src_root</strong>.</tt></dd></dl>
+
+<dl><dt><a name="Paths-__init__"><strong>__init__</strong></a>(self, config, chrome_root)</dt><dd><tt>Generate&nbsp;paths&nbsp;to&nbsp;binary&nbsp;artifacts&nbsp;from&nbsp;a&nbsp;Config&nbsp;<a href="__builtin__.html#object">object</a>.</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.remote.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.remote.html
new file mode 100644
index 0000000..76f7e88
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.remote.html
@@ -0,0 +1,26 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: package telemetry.internal.backends.remote</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.remote</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/remote/__init__.py">telemetry/internal/backends/remote/__init__.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Package Contents</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.backends.remote.trybot_browser_finder.html">trybot_browser_finder</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.backends.remote.trybot_browser_finder_unittest.html">trybot_browser_finder_unittest</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.remote.trybot_browser_finder.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.remote.trybot_browser_finder.html
new file mode 100644
index 0000000..9e5b6c5
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.backends.remote.trybot_browser_finder.html
@@ -0,0 +1,200 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.backends.remote.trybot_browser_finder</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.backends.html"><font color="#ffffff">backends</font></a>.<a href="telemetry.internal.backends.remote.html"><font color="#ffffff">remote</font></a>.trybot_browser_finder</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/backends/remote/trybot_browser_finder.py">telemetry/internal/backends/remote/trybot_browser_finder.py</a></font></td></tr></table>
+    <p><tt>Finds&nbsp;perf&nbsp;trybots&nbsp;that&nbsp;can&nbsp;run&nbsp;telemetry&nbsp;tests.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.decorators.html">telemetry.decorators</a><br>
+<a href="json.html">json</a><br>
+<a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top><a href="os.html">os</a><br>
+<a href="telemetry.core.platform.html">telemetry.core.platform</a><br>
+<a href="telemetry.internal.browser.possible_browser.html">telemetry.internal.browser.possible_browser</a><br>
+</td><td width="25%" valign=top><a href="re.html">re</a><br>
+<a href="subprocess.html">subprocess</a><br>
+<a href="sys.html">sys</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.trybot_device.html">telemetry.internal.platform.trybot_device</a><br>
+<a href="urllib2.html">urllib2</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="exceptions.html#Exception">exceptions.Exception</a>(<a href="exceptions.html#BaseException">exceptions.BaseException</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.remote.trybot_browser_finder.html#TrybotError">TrybotError</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.browser.possible_browser.html#PossibleBrowser">telemetry.internal.browser.possible_browser.PossibleBrowser</a>(<a href="telemetry.internal.app.possible_app.html#PossibleApp">telemetry.internal.app.possible_app.PossibleApp</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.backends.remote.trybot_browser_finder.html#PossibleTrybotBrowser">PossibleTrybotBrowser</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="PossibleTrybotBrowser">class <strong>PossibleTrybotBrowser</strong></a>(<a href="telemetry.internal.browser.possible_browser.html#PossibleBrowser">telemetry.internal.browser.possible_browser.PossibleBrowser</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;script&nbsp;that&nbsp;sends&nbsp;a&nbsp;job&nbsp;to&nbsp;a&nbsp;trybot.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.remote.trybot_browser_finder.html#PossibleTrybotBrowser">PossibleTrybotBrowser</a></dd>
+<dd><a href="telemetry.internal.browser.possible_browser.html#PossibleBrowser">telemetry.internal.browser.possible_browser.PossibleBrowser</a></dd>
+<dd><a href="telemetry.internal.app.possible_app.html#PossibleApp">telemetry.internal.app.possible_app.PossibleApp</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="PossibleTrybotBrowser-Create"><strong>Create</strong></a>(self, finder_options)</dt></dl>
+
+<dl><dt><a name="PossibleTrybotBrowser-IsRemote"><strong>IsRemote</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PossibleTrybotBrowser-RunRemote"><strong>RunRemote</strong></a>(self)</dt><dd><tt>Sends&nbsp;a&nbsp;tryjob&nbsp;to&nbsp;a&nbsp;perf&nbsp;trybot.<br>
+&nbsp;<br>
+This&nbsp;creates&nbsp;a&nbsp;branch,&nbsp;telemetry-tryjob,&nbsp;switches&nbsp;to&nbsp;that&nbsp;branch,&nbsp;edits<br>
+the&nbsp;bisect&nbsp;config,&nbsp;commits&nbsp;it,&nbsp;uploads&nbsp;the&nbsp;CL&nbsp;to&nbsp;rietveld,&nbsp;and&nbsp;runs&nbsp;a<br>
+tryjob&nbsp;on&nbsp;the&nbsp;given&nbsp;bot.</tt></dd></dl>
+
+<dl><dt><a name="PossibleTrybotBrowser-SupportsOptions"><strong>SupportsOptions</strong></a>(self, finder_options)</dt></dl>
+
+<dl><dt><a name="PossibleTrybotBrowser-__init__"><strong>__init__</strong></a>(self, browser_type, _)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.browser.possible_browser.html#PossibleBrowser">telemetry.internal.browser.possible_browser.PossibleBrowser</a>:<br>
+<dl><dt><a name="PossibleTrybotBrowser-SetCredentialsPath"><strong>SetCredentialsPath</strong></a>(self, credentials_path)</dt></dl>
+
+<dl><dt><a name="PossibleTrybotBrowser-UpdateExecutableIfNeeded"><strong>UpdateExecutableIfNeeded</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PossibleTrybotBrowser-__repr__"><strong>__repr__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PossibleTrybotBrowser-last_modification_time"><strong>last_modification_time</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.browser.possible_browser.html#PossibleBrowser">telemetry.internal.browser.possible_browser.PossibleBrowser</a>:<br>
+<dl><dt><strong>browser_type</strong></dt>
+</dl>
+<dl><dt><strong>supports_tab_control</strong></dt>
+</dl>
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.app.possible_app.html#PossibleApp">telemetry.internal.app.possible_app.PossibleApp</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>app_type</strong></dt>
+</dl>
+<dl><dt><strong>platform</strong></dt>
+</dl>
+<dl><dt><strong>target_os</strong></dt>
+<dd><tt>Target&nbsp;OS,&nbsp;the&nbsp;app&nbsp;will&nbsp;run&nbsp;on.</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TrybotError">class <strong>TrybotError</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.backends.remote.trybot_browser_finder.html#TrybotError">TrybotError</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="TrybotError-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="TrybotError-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#TrybotError-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#TrybotError-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;object&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="TrybotError-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#TrybotError-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="TrybotError-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#TrybotError-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="TrybotError-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#TrybotError-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="TrybotError-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#TrybotError-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="TrybotError-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="TrybotError-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#TrybotError-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="TrybotError-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#TrybotError-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="TrybotError-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="TrybotError-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-CanFindAvailableBrowsers"><strong>CanFindAvailableBrowsers</strong></a>()</dt></dl>
+ <dl><dt><a name="-FindAllAvailableBrowsers"><strong>FindAllAvailableBrowsers</strong></a>(finder_options, device)</dt><dd><tt>Find&nbsp;all&nbsp;perf&nbsp;trybots&nbsp;on&nbsp;tryserver.chromium.perf.</tt></dd></dl>
+ <dl><dt><a name="-FindAllBrowserTypes"><strong>FindAllBrowserTypes</strong></a>(finder_options)</dt></dl>
+ <dl><dt><a name="-SelectDefaultBrowser"><strong>SelectDefaultBrowser</strong></a>(_)</dt></dl>
+</td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>BLINK_CONFIG_FILENAME</strong> = 'Tools/run-perf-test.cfg'<br>
+<strong>CHROMIUM_CONFIG_FILENAME</strong> = 'tools/run-perf-test.cfg'<br>
+<strong>ERROR</strong> = 2<br>
+<strong>EXCLUDED_BOTS</strong> = set(['android_arm64_perf_bisect_builder', 'android_perf_bisect_builder', 'linux_perf_bisect_builder', 'linux_perf_bisector', 'linux_perf_tester', 'mac_perf_bisect_builder', ...])<br>
+<strong>INCLUDE_BOTS</strong> = ['trybot-all', 'trybot-all-win', 'trybot-all-mac', 'trybot-all-linux', 'trybot-all-android']<br>
+<strong>NO_CHANGES</strong> = 1<br>
+<strong>SUCCESS</strong> = 0</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.browser.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.browser.html
new file mode 100644
index 0000000..53aff2e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.browser.html
@@ -0,0 +1,189 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.browser.browser</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.browser.html"><font color="#ffffff">browser</font></a>.browser</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/browser/browser.py">telemetry/internal/browser/browser.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2012&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.app.html">telemetry.internal.app</a><br>
+<a href="telemetry.internal.backends.browser_backend.html">telemetry.internal.backends.browser_backend</a><br>
+<a href="telemetry.internal.browser.browser_credentials.html">telemetry.internal.browser.browser_credentials</a><br>
+<a href="catapult_base.cloud_storage.html">catapult_base.cloud_storage</a><br>
+</td><td width="25%" valign=top><a href="telemetry.decorators.html">telemetry.decorators</a><br>
+<a href="telemetry.internal.util.exception_formatter.html">telemetry.internal.util.exception_formatter</a><br>
+<a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+<a href="telemetry.internal.browser.extension_dict.html">telemetry.internal.browser.extension_dict</a><br>
+</td><td width="25%" valign=top><a href="logging.html">logging</a><br>
+<a href="telemetry.core.profiling_controller.html">telemetry.core.profiling_controller</a><br>
+<a href="sys.html">sys</a><br>
+<a href="telemetry.internal.browser.tab_list.html">telemetry.internal.browser.tab_list</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.browser.web_contents.html">telemetry.internal.browser.web_contents</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.app.html#App">telemetry.internal.app.App</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.browser.browser.html#Browser">Browser</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="Browser">class <strong>Browser</strong></a>(<a href="telemetry.internal.app.html#App">telemetry.internal.app.App</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;running&nbsp;browser&nbsp;instance&nbsp;that&nbsp;can&nbsp;be&nbsp;controlled&nbsp;in&nbsp;a&nbsp;limited&nbsp;way.<br>
+&nbsp;<br>
+To&nbsp;create&nbsp;a&nbsp;browser&nbsp;instance,&nbsp;use&nbsp;browser_finder.FindBrowser.<br>
+&nbsp;<br>
+Be&nbsp;sure&nbsp;to&nbsp;clean&nbsp;up&nbsp;after&nbsp;yourself&nbsp;by&nbsp;calling&nbsp;<a href="#Browser-Close">Close</a>()&nbsp;when&nbsp;you&nbsp;are&nbsp;done&nbsp;with<br>
+the&nbsp;browser.&nbsp;Or&nbsp;better&nbsp;yet:<br>
+&nbsp;&nbsp;browser_to_create&nbsp;=&nbsp;FindBrowser(options)<br>
+&nbsp;&nbsp;with&nbsp;browser_to_create.Create(options)&nbsp;as&nbsp;browser:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;...&nbsp;do&nbsp;all&nbsp;your&nbsp;operations&nbsp;on&nbsp;browser&nbsp;here<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.browser.browser.html#Browser">Browser</a></dd>
+<dd><a href="telemetry.internal.app.html#App">telemetry.internal.app.App</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="Browser-Close"><strong>Close</strong></a>(self)</dt><dd><tt>Closes&nbsp;this&nbsp;browser.</tt></dd></dl>
+
+<dl><dt><a name="Browser-DumpMemory"><strong>DumpMemory</strong></a>(self, timeout<font color="#909090">=90</font>)</dt></dl>
+
+<dl><dt><a name="Browser-GetStackTrace"><strong>GetStackTrace</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="Browser-GetStandardOutput"><strong>GetStandardOutput</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="Browser-GetSystemInfo"><strong>GetSystemInfo</strong></a>(self)</dt><dd><tt>Returns&nbsp;low-level&nbsp;information&nbsp;about&nbsp;the&nbsp;system,&nbsp;if&nbsp;available.<br>
+&nbsp;<br>
+See&nbsp;the&nbsp;documentation&nbsp;of&nbsp;the&nbsp;SystemInfo&nbsp;class&nbsp;for&nbsp;more&nbsp;details.</tt></dd></dl>
+
+<dl><dt><a name="Browser-SetMemoryPressureNotificationsSuppressed"><strong>SetMemoryPressureNotificationsSuppressed</strong></a>(self, suppressed, timeout<font color="#909090">=90</font>)</dt></dl>
+
+<dl><dt><a name="Browser-SimulateMemoryPressureNotification"><strong>SimulateMemoryPressureNotification</strong></a>(self, pressure_level, timeout<font color="#909090">=90</font>)</dt></dl>
+
+<dl><dt><a name="Browser-__init__"><strong>__init__</strong></a>(self, backend, platform_backend, credentials_path)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>browser_type</strong></dt>
+</dl>
+<dl><dt><strong>cpu_stats</strong></dt>
+<dd><tt>Returns&nbsp;a&nbsp;dict&nbsp;of&nbsp;cpu&nbsp;statistics&nbsp;for&nbsp;the&nbsp;system.<br>
+{&nbsp;'Browser':&nbsp;{<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'CpuProcessTime':&nbsp;S,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'TotalTime':&nbsp;T<br>
+&nbsp;&nbsp;},<br>
+&nbsp;&nbsp;'Gpu':&nbsp;{<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'CpuProcessTime':&nbsp;S,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'TotalTime':&nbsp;T<br>
+&nbsp;&nbsp;},<br>
+&nbsp;&nbsp;'Renderer':&nbsp;{<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'CpuProcessTime':&nbsp;S,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'TotalTime':&nbsp;T<br>
+&nbsp;&nbsp;}<br>
+}<br>
+Any&nbsp;of&nbsp;the&nbsp;above&nbsp;keys&nbsp;may&nbsp;be&nbsp;missing&nbsp;on&nbsp;a&nbsp;per-platform&nbsp;basis.</tt></dd>
+</dl>
+<dl><dt><strong>extensions</strong></dt>
+</dl>
+<dl><dt><strong>foreground_tab</strong></dt>
+</dl>
+<dl><dt><strong>memory_stats</strong></dt>
+<dd><tt>Returns&nbsp;a&nbsp;dict&nbsp;of&nbsp;memory&nbsp;statistics&nbsp;for&nbsp;the&nbsp;browser:<br>
+{&nbsp;'Browser':&nbsp;{<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'VM':&nbsp;R,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'VMPeak':&nbsp;S,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'WorkingSetSize':&nbsp;T,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'WorkingSetSizePeak':&nbsp;U,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'ProportionalSetSize':&nbsp;V,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'PrivateDirty':&nbsp;W<br>
+&nbsp;&nbsp;},<br>
+&nbsp;&nbsp;'Gpu':&nbsp;{<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'VM':&nbsp;R,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'VMPeak':&nbsp;S,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'WorkingSetSize':&nbsp;T,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'WorkingSetSizePeak':&nbsp;U,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'ProportionalSetSize':&nbsp;V,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'PrivateDirty':&nbsp;W<br>
+&nbsp;&nbsp;},<br>
+&nbsp;&nbsp;'Renderer':&nbsp;{<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'VM':&nbsp;R,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'VMPeak':&nbsp;S,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'WorkingSetSize':&nbsp;T,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'WorkingSetSizePeak':&nbsp;U,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'ProportionalSetSize':&nbsp;V,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'PrivateDirty':&nbsp;W<br>
+&nbsp;&nbsp;},<br>
+&nbsp;&nbsp;'SystemCommitCharge':&nbsp;X,<br>
+&nbsp;&nbsp;'SystemTotalPhysicalMemory':&nbsp;Y,<br>
+&nbsp;&nbsp;'ProcessCount':&nbsp;Z,<br>
+}<br>
+Any&nbsp;of&nbsp;the&nbsp;above&nbsp;keys&nbsp;may&nbsp;be&nbsp;missing&nbsp;on&nbsp;a&nbsp;per-platform&nbsp;basis.</tt></dd>
+</dl>
+<dl><dt><strong>profiling_controller</strong></dt>
+</dl>
+<dl><dt><strong>supports_cpu_metrics</strong></dt>
+</dl>
+<dl><dt><strong>supports_extensions</strong></dt>
+</dl>
+<dl><dt><strong>supports_memory_dumping</strong></dt>
+</dl>
+<dl><dt><strong>supports_memory_metrics</strong></dt>
+</dl>
+<dl><dt><strong>supports_overriding_memory_pressure_notifications</strong></dt>
+</dl>
+<dl><dt><strong>supports_power_metrics</strong></dt>
+</dl>
+<dl><dt><strong>supports_system_info</strong></dt>
+</dl>
+<dl><dt><strong>supports_tab_control</strong></dt>
+</dl>
+<dl><dt><strong>tabs</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.internal.app.html#App">telemetry.internal.app.App</a>:<br>
+<dl><dt><a name="Browser-__enter__"><strong>__enter__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="Browser-__exit__"><strong>__exit__</strong></a>(self, *args)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.app.html#App">telemetry.internal.app.App</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>app_type</strong></dt>
+</dl>
+<dl><dt><strong>platform</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.browser_credentials.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.browser_credentials.html
new file mode 100644
index 0000000..5933292
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.browser_credentials.html
@@ -0,0 +1,147 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.browser.browser_credentials</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.browser.html"><font color="#ffffff">browser</font></a>.browser_credentials</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/browser/browser_credentials.py">telemetry/internal/browser/browser_credentials.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2012&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.backends.codepen_credentials_backend.html">telemetry.internal.backends.codepen_credentials_backend</a><br>
+<a href="telemetry.internal.backends.facebook_credentials_backend.html">telemetry.internal.backends.facebook_credentials_backend</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.backends.google_credentials_backend.html">telemetry.internal.backends.google_credentials_backend</a><br>
+<a href="json.html">json</a><br>
+</td><td width="25%" valign=top><a href="logging.html">logging</a><br>
+<a href="telemetry.testing.options_for_unittests.html">telemetry.testing.options_for_unittests</a><br>
+</td><td width="25%" valign=top><a href="os.html">os</a><br>
+<a href="telemetry.core.util.html">telemetry.core.util</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.browser.browser_credentials.html#BrowserCredentials">BrowserCredentials</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="exceptions.html#Exception">exceptions.Exception</a>(<a href="exceptions.html#BaseException">exceptions.BaseException</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.browser.browser_credentials.html#CredentialsError">CredentialsError</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="BrowserCredentials">class <strong>BrowserCredentials</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="BrowserCredentials-Add"><strong>Add</strong></a>(self, credentials_type, data)</dt></dl>
+
+<dl><dt><a name="BrowserCredentials-AddBackend"><strong>AddBackend</strong></a>(self, backend)</dt></dl>
+
+<dl><dt><a name="BrowserCredentials-CanLogin"><strong>CanLogin</strong></a>(self, credentials_type)</dt></dl>
+
+<dl><dt><a name="BrowserCredentials-IsLoggedIn"><strong>IsLoggedIn</strong></a>(self, credentials_type)</dt></dl>
+
+<dl><dt><a name="BrowserCredentials-LoginNeeded"><strong>LoginNeeded</strong></a>(self, tab, credentials_type)</dt></dl>
+
+<dl><dt><a name="BrowserCredentials-LoginNoLongerNeeded"><strong>LoginNoLongerNeeded</strong></a>(self, tab, credentials_type)</dt></dl>
+
+<dl><dt><a name="BrowserCredentials-WarnIfMissingCredentials"><strong>WarnIfMissingCredentials</strong></a>(self, page)</dt></dl>
+
+<dl><dt><a name="BrowserCredentials-__init__"><strong>__init__</strong></a>(self, backends<font color="#909090">=None</font>)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>credentials_path</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="CredentialsError">class <strong>CredentialsError</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Error&nbsp;that&nbsp;can&nbsp;be&nbsp;thrown&nbsp;when&nbsp;logging&nbsp;in.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.browser.browser_credentials.html#CredentialsError">CredentialsError</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="CredentialsError-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#CredentialsError-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#CredentialsError-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="CredentialsError-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#CredentialsError-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="CredentialsError-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#CredentialsError-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="CredentialsError-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#CredentialsError-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="CredentialsError-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#CredentialsError-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="CredentialsError-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="CredentialsError-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#CredentialsError-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="CredentialsError-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#CredentialsError-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="CredentialsError-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="CredentialsError-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#CredentialsError-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="CredentialsError-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.browser_finder.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.browser_finder.html
new file mode 100644
index 0000000..77c1c86
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.browser_finder.html
@@ -0,0 +1,80 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.browser.browser_finder</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.browser.html"><font color="#ffffff">browser</font></a>.browser_finder</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/browser/browser_finder.py">telemetry/internal/browser/browser_finder.py</a></font></td></tr></table>
+    <p><tt>Finds&nbsp;browsers&nbsp;that&nbsp;can&nbsp;be&nbsp;controlled&nbsp;by&nbsp;telemetry.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.backends.chrome.android_browser_finder.html">telemetry.internal.backends.chrome.android_browser_finder</a><br>
+<a href="telemetry.internal.backends.mandoline.android_mandoline_finder.html">telemetry.internal.backends.mandoline.android_mandoline_finder</a><br>
+<a href="telemetry.internal.browser.browser_finder_exceptions.html">telemetry.internal.browser.browser_finder_exceptions</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.backends.chrome.cros_browser_finder.html">telemetry.internal.backends.chrome.cros_browser_finder</a><br>
+<a href="telemetry.decorators.html">telemetry.decorators</a><br>
+<a href="telemetry.internal.backends.chrome.desktop_browser_finder.html">telemetry.internal.backends.chrome.desktop_browser_finder</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.backends.mandoline.desktop_mandoline_finder.html">telemetry.internal.backends.mandoline.desktop_mandoline_finder</a><br>
+<a href="telemetry.internal.platform.device_finder.html">telemetry.internal.platform.device_finder</a><br>
+<a href="telemetry.internal.backends.chrome.ios_browser_finder.html">telemetry.internal.backends.chrome.ios_browser_finder</a><br>
+</td><td width="25%" valign=top><a href="logging.html">logging</a><br>
+<a href="operator.html">operator</a><br>
+<a href="telemetry.internal.backends.remote.trybot_browser_finder.html">telemetry.internal.backends.remote.trybot_browser_finder</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-FindAllBrowserTypes"><strong>FindAllBrowserTypes</strong></a>(options)</dt></dl>
+ <dl><dt><a name="-FindBrowser"><strong>FindBrowser</strong></a>(*args, **kwargs)</dt><dd><tt>Finds&nbsp;the&nbsp;best&nbsp;PossibleBrowser&nbsp;object&nbsp;given&nbsp;a&nbsp;BrowserOptions&nbsp;object.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;A&nbsp;BrowserOptions&nbsp;object.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;A&nbsp;PossibleBrowser&nbsp;object.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;BrowserFinderException:&nbsp;Options&nbsp;improperly&nbsp;set,&nbsp;or&nbsp;an&nbsp;error&nbsp;occurred.</tt></dd></dl>
+ <dl><dt><a name="-GetAllAvailableBrowserTypes"><strong>GetAllAvailableBrowserTypes</strong></a>(*args, **kwargs)</dt><dd><tt>Returns&nbsp;a&nbsp;list&nbsp;of&nbsp;available&nbsp;browser&nbsp;types.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;options:&nbsp;A&nbsp;BrowserOptions&nbsp;object.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;A&nbsp;list&nbsp;of&nbsp;browser&nbsp;type&nbsp;strings.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;BrowserFinderException:&nbsp;Options&nbsp;are&nbsp;improperly&nbsp;set,&nbsp;or&nbsp;an&nbsp;error&nbsp;occurred.</tt></dd></dl>
+ <dl><dt><a name="-GetAllAvailableBrowsers"><strong>GetAllAvailableBrowsers</strong></a>(*args, **kwargs)</dt><dd><tt>Returns&nbsp;a&nbsp;list&nbsp;of&nbsp;available&nbsp;browsers&nbsp;on&nbsp;the&nbsp;device.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;options:&nbsp;A&nbsp;BrowserOptions&nbsp;object.<br>
+&nbsp;&nbsp;device:&nbsp;The&nbsp;target&nbsp;device,&nbsp;which&nbsp;can&nbsp;be&nbsp;None.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;A&nbsp;list&nbsp;of&nbsp;browser&nbsp;instances.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;BrowserFinderException:&nbsp;Options&nbsp;are&nbsp;improperly&nbsp;set,&nbsp;or&nbsp;an&nbsp;error&nbsp;occurred.</tt></dd></dl>
+</td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>BROWSER_FINDERS</strong> = [&lt;module 'telemetry.internal.backends.chrome.desk...rnal/backends/chrome/desktop_browser_finder.pyc'&gt;, &lt;module 'telemetry.internal.backends.chrome.andr...rnal/backends/chrome/android_browser_finder.pyc'&gt;, &lt;module 'telemetry.internal.backends.chrome.cros...nternal/backends/chrome/cros_browser_finder.pyc'&gt;, &lt;module 'telemetry.internal.backends.chrome.ios_...internal/backends/chrome/ios_browser_finder.pyc'&gt;, &lt;module 'telemetry.internal.backends.remote.tryb...ernal/backends/remote/trybot_browser_finder.pyc'&gt;, &lt;module 'telemetry.internal.backends.mandoline.d...backends/mandoline/desktop_mandoline_finder.pyc'&gt;, &lt;module 'telemetry.internal.backends.mandoline.a...backends/mandoline/android_mandoline_finder.pyc'&gt;]</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.browser_finder_exceptions.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.browser_finder_exceptions.html
new file mode 100644
index 0000000..f796cc5
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.browser_finder_exceptions.html
@@ -0,0 +1,149 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.browser.browser_finder_exceptions</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.browser.html"><font color="#ffffff">browser</font></a>.browser_finder_exceptions</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/browser/browser_finder_exceptions.py">telemetry/internal/browser/browser_finder_exceptions.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="exceptions.html#Exception">exceptions.Exception</a>(<a href="exceptions.html#BaseException">exceptions.BaseException</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.browser.browser_finder_exceptions.html#BrowserFinderException">BrowserFinderException</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.internal.browser.browser_finder_exceptions.html#BrowserTypeRequiredException">BrowserTypeRequiredException</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="BrowserFinderException">class <strong>BrowserFinderException</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.browser.browser_finder_exceptions.html#BrowserFinderException">BrowserFinderException</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="BrowserFinderException-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#BrowserFinderException-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#BrowserFinderException-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;object&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="BrowserFinderException-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#BrowserFinderException-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="BrowserFinderException-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#BrowserFinderException-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="BrowserFinderException-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#BrowserFinderException-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="BrowserFinderException-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#BrowserFinderException-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="BrowserFinderException-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="BrowserFinderException-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#BrowserFinderException-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="BrowserFinderException-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#BrowserFinderException-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="BrowserFinderException-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="BrowserFinderException-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#BrowserFinderException-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="BrowserFinderException-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="BrowserTypeRequiredException">class <strong>BrowserTypeRequiredException</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.browser.browser_finder_exceptions.html#BrowserTypeRequiredException">BrowserTypeRequiredException</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="BrowserTypeRequiredException-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#BrowserTypeRequiredException-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#BrowserTypeRequiredException-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;object&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="BrowserTypeRequiredException-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#BrowserTypeRequiredException-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="BrowserTypeRequiredException-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#BrowserTypeRequiredException-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="BrowserTypeRequiredException-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#BrowserTypeRequiredException-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="BrowserTypeRequiredException-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#BrowserTypeRequiredException-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="BrowserTypeRequiredException-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="BrowserTypeRequiredException-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#BrowserTypeRequiredException-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="BrowserTypeRequiredException-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#BrowserTypeRequiredException-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="BrowserTypeRequiredException-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="BrowserTypeRequiredException-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#BrowserTypeRequiredException-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="BrowserTypeRequiredException-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.browser_info.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.browser_info.html
new file mode 100644
index 0000000..0d5c7f4
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.browser_info.html
@@ -0,0 +1,65 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.browser.browser_info</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.browser.html"><font color="#ffffff">browser</font></a>.browser_info</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/browser/browser_info.py">telemetry/internal/browser/browser_info.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.browser.browser_info.html#BrowserInfo">BrowserInfo</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="BrowserInfo">class <strong>BrowserInfo</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;wrapper&nbsp;around&nbsp;browser&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;that&nbsp;allows&nbsp;looking&nbsp;up&nbsp;infos&nbsp;of&nbsp;the<br>
+browser.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="BrowserInfo-HasDiagonalScrollingSupport"><strong>HasDiagonalScrollingSupport</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="BrowserInfo-HasFlingGestureSupport"><strong>HasFlingGestureSupport</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="BrowserInfo-HasRepeatableSynthesizeScrollGesture"><strong>HasRepeatableSynthesizeScrollGesture</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="BrowserInfo-HasWebGLSupport"><strong>HasWebGLSupport</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="BrowserInfo-__init__"><strong>__init__</strong></a>(self, browser)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>browser</strong></dt>
+</dl>
+<dl><dt><strong>browser_type</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.browser_options.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.browser_options.html
new file mode 100644
index 0000000..c31e571
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.browser_options.html
@@ -0,0 +1,245 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.browser.browser_options</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.browser.html"><font color="#ffffff">browser</font></a>.browser_options</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/browser/browser_options.py">telemetry/internal/browser/browser_options.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2012&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.browser.browser_finder.html">telemetry.internal.browser.browser_finder</a><br>
+<a href="telemetry.internal.browser.browser_finder_exceptions.html">telemetry.internal.browser.browser_finder_exceptions</a><br>
+<a href="catapult_base.cloud_storage.html">catapult_base.cloud_storage</a><br>
+<a href="copy.html">copy</a><br>
+<a href="telemetry.internal.platform.device_finder.html">telemetry.internal.platform.device_finder</a><br>
+</td><td width="25%" valign=top><a href="logging.html">logging</a><br>
+<a href="net_configs.html">net_configs</a><br>
+<a href="optparse.html">optparse</a><br>
+<a href="os.html">os</a><br>
+<a href="telemetry.core.platform.html">telemetry.core.platform</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.browser.profile_types.html">telemetry.internal.browser.profile_types</a><br>
+<a href="telemetry.internal.platform.profiler.profiler_finder.html">telemetry.internal.platform.profiler.profiler_finder</a><br>
+<a href="shlex.html">shlex</a><br>
+<a href="socket.html">socket</a><br>
+<a href="sys.html">sys</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.util.html">telemetry.core.util</a><br>
+<a href="telemetry.util.wpr_modes.html">telemetry.util.wpr_modes</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.browser.browser_options.html#BrowserOptions">BrowserOptions</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.browser.browser_options.html#ChromeBrowserOptions">ChromeBrowserOptions</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.browser.browser_options.html#CrosBrowserOptions">CrosBrowserOptions</a>
+</font></dt></dl>
+</dd>
+</dl>
+</dd>
+</dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="optparse.html#Values">optparse.Values</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.browser.browser_options.html#BrowserFinderOptions">BrowserFinderOptions</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="BrowserFinderOptions">class <strong>BrowserFinderOptions</strong></a>(<a href="optparse.html#Values">optparse.Values</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Options&nbsp;to&nbsp;be&nbsp;used&nbsp;for&nbsp;discovering&nbsp;a&nbsp;browser.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="BrowserFinderOptions-AppendExtraBrowserArgs"><strong>AppendExtraBrowserArgs</strong></a>(self, args)</dt></dl>
+
+<dl><dt><a name="BrowserFinderOptions-Copy"><strong>Copy</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="BrowserFinderOptions-CreateParser"><strong>CreateParser</strong></a>(self, *args, **kwargs)</dt></dl>
+
+<dl><dt><a name="BrowserFinderOptions-MergeDefaultValues"><strong>MergeDefaultValues</strong></a>(self, defaults)</dt></dl>
+
+<dl><dt><a name="BrowserFinderOptions-__init__"><strong>__init__</strong></a>(self, browser_type<font color="#909090">=None</font>)</dt></dl>
+
+<dl><dt><a name="BrowserFinderOptions-__repr__"><strong>__repr__</strong></a>(self)</dt></dl>
+
+<hr>
+Methods inherited from <a href="optparse.html#Values">optparse.Values</a>:<br>
+<dl><dt><a name="BrowserFinderOptions-__cmp__"><strong>__cmp__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="BrowserFinderOptions-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="BrowserFinderOptions-ensure_value"><strong>ensure_value</strong></a>(self, attr, value)</dt></dl>
+
+<dl><dt><a name="BrowserFinderOptions-read_file"><strong>read_file</strong></a>(self, filename, mode<font color="#909090">='careful'</font>)</dt></dl>
+
+<dl><dt><a name="BrowserFinderOptions-read_module"><strong>read_module</strong></a>(self, modname, mode<font color="#909090">='careful'</font>)</dt></dl>
+
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="BrowserOptions">class <strong>BrowserOptions</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Options&nbsp;to&nbsp;be&nbsp;used&nbsp;for&nbsp;launching&nbsp;a&nbsp;browser.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="BrowserOptions-AppendExtraBrowserArgs"><strong>AppendExtraBrowserArgs</strong></a>(self, args)</dt></dl>
+
+<dl><dt><a name="BrowserOptions-IsCrosBrowserOptions"><strong>IsCrosBrowserOptions</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="BrowserOptions-UpdateFromParseResults"><strong>UpdateFromParseResults</strong></a>(self, finder_options)</dt><dd><tt>Copies&nbsp;our&nbsp;options&nbsp;from&nbsp;finder_options</tt></dd></dl>
+
+<dl><dt><a name="BrowserOptions-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="BrowserOptions-__repr__"><strong>__repr__</strong></a>(self)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="BrowserOptions-AddCommandLineArgs"><strong>AddCommandLineArgs</strong></a>(cls, parser)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>browser_startup_timeout</strong></dt>
+</dl>
+<dl><dt><strong>extra_browser_args</strong></dt>
+</dl>
+<dl><dt><strong>finder_options</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ChromeBrowserOptions">class <strong>ChromeBrowserOptions</strong></a>(<a href="telemetry.internal.browser.browser_options.html#BrowserOptions">BrowserOptions</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Chrome-specific&nbsp;browser&nbsp;options.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.browser.browser_options.html#ChromeBrowserOptions">ChromeBrowserOptions</a></dd>
+<dd><a href="telemetry.internal.browser.browser_options.html#BrowserOptions">BrowserOptions</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="ChromeBrowserOptions-__init__"><strong>__init__</strong></a>(self, br_options)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.browser.browser_options.html#BrowserOptions">BrowserOptions</a>:<br>
+<dl><dt><a name="ChromeBrowserOptions-AppendExtraBrowserArgs"><strong>AppendExtraBrowserArgs</strong></a>(self, args)</dt></dl>
+
+<dl><dt><a name="ChromeBrowserOptions-IsCrosBrowserOptions"><strong>IsCrosBrowserOptions</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="ChromeBrowserOptions-UpdateFromParseResults"><strong>UpdateFromParseResults</strong></a>(self, finder_options)</dt><dd><tt>Copies&nbsp;our&nbsp;options&nbsp;from&nbsp;finder_options</tt></dd></dl>
+
+<dl><dt><a name="ChromeBrowserOptions-__repr__"><strong>__repr__</strong></a>(self)</dt></dl>
+
+<hr>
+Class methods inherited from <a href="telemetry.internal.browser.browser_options.html#BrowserOptions">BrowserOptions</a>:<br>
+<dl><dt><a name="ChromeBrowserOptions-AddCommandLineArgs"><strong>AddCommandLineArgs</strong></a>(cls, parser)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.browser.browser_options.html#BrowserOptions">BrowserOptions</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>browser_startup_timeout</strong></dt>
+</dl>
+<dl><dt><strong>extra_browser_args</strong></dt>
+</dl>
+<dl><dt><strong>finder_options</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="CrosBrowserOptions">class <strong>CrosBrowserOptions</strong></a>(<a href="telemetry.internal.browser.browser_options.html#ChromeBrowserOptions">ChromeBrowserOptions</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>ChromeOS-specific&nbsp;browser&nbsp;options.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.browser.browser_options.html#CrosBrowserOptions">CrosBrowserOptions</a></dd>
+<dd><a href="telemetry.internal.browser.browser_options.html#ChromeBrowserOptions">ChromeBrowserOptions</a></dd>
+<dd><a href="telemetry.internal.browser.browser_options.html#BrowserOptions">BrowserOptions</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="CrosBrowserOptions-IsCrosBrowserOptions"><strong>IsCrosBrowserOptions</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrosBrowserOptions-__init__"><strong>__init__</strong></a>(self, br_options)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.browser.browser_options.html#BrowserOptions">BrowserOptions</a>:<br>
+<dl><dt><a name="CrosBrowserOptions-AppendExtraBrowserArgs"><strong>AppendExtraBrowserArgs</strong></a>(self, args)</dt></dl>
+
+<dl><dt><a name="CrosBrowserOptions-UpdateFromParseResults"><strong>UpdateFromParseResults</strong></a>(self, finder_options)</dt><dd><tt>Copies&nbsp;our&nbsp;options&nbsp;from&nbsp;finder_options</tt></dd></dl>
+
+<dl><dt><a name="CrosBrowserOptions-__repr__"><strong>__repr__</strong></a>(self)</dt></dl>
+
+<hr>
+Class methods inherited from <a href="telemetry.internal.browser.browser_options.html#BrowserOptions">BrowserOptions</a>:<br>
+<dl><dt><a name="CrosBrowserOptions-AddCommandLineArgs"><strong>AddCommandLineArgs</strong></a>(cls, parser)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.browser.browser_options.html#BrowserOptions">BrowserOptions</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>browser_startup_timeout</strong></dt>
+</dl>
+<dl><dt><strong>extra_browser_args</strong></dt>
+</dl>
+<dl><dt><strong>finder_options</strong></dt>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-CreateChromeBrowserOptions"><strong>CreateChromeBrowserOptions</strong></a>(br_options)</dt></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.extension_dict.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.extension_dict.html
new file mode 100644
index 0000000..ea84cab
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.extension_dict.html
@@ -0,0 +1,70 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.browser.extension_dict</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.browser.html"><font color="#ffffff">browser</font></a>.extension_dict</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/browser/extension_dict.py">telemetry/internal/browser/extension_dict.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2012&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.browser.extension_to_load.html">telemetry.internal.browser.extension_to_load</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.browser.extension_dict.html#ExtensionDict">ExtensionDict</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ExtensionDict">class <strong>ExtensionDict</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Dictionary&nbsp;of&nbsp;ExtensionPage&nbsp;instances,&nbsp;with&nbsp;extension_id&nbsp;as&nbsp;key.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="ExtensionDict-GetByExtensionId"><strong>GetByExtensionId</strong></a>(self, extension_id)</dt><dd><tt>Returns&nbsp;a&nbsp;list&nbsp;of&nbsp;extensions&nbsp;given&nbsp;an&nbsp;extension&nbsp;id.&nbsp;This&nbsp;is&nbsp;useful&nbsp;for<br>
+connecting&nbsp;to&nbsp;built-in&nbsp;apps&nbsp;and&nbsp;component&nbsp;extensions.</tt></dd></dl>
+
+<dl><dt><a name="ExtensionDict-__contains__"><strong>__contains__</strong></a>(self, load_extension)</dt><dd><tt>Checks&nbsp;if&nbsp;this&nbsp;ExtensionToLoad&nbsp;instance&nbsp;has&nbsp;been&nbsp;loaded</tt></dd></dl>
+
+<dl><dt><a name="ExtensionDict-__getitem__"><strong>__getitem__</strong></a>(self, load_extension)</dt><dd><tt>Given&nbsp;an&nbsp;ExtensionToLoad&nbsp;instance,&nbsp;returns&nbsp;the&nbsp;corresponding<br>
+ExtensionPage&nbsp;instance.</tt></dd></dl>
+
+<dl><dt><a name="ExtensionDict-__init__"><strong>__init__</strong></a>(self, extension_backend)</dt></dl>
+
+<dl><dt><a name="ExtensionDict-keys"><strong>keys</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.extension_page.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.extension_page.html
new file mode 100644
index 0000000..adbac7d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.extension_page.html
@@ -0,0 +1,250 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.browser.extension_page</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.browser.html"><font color="#ffffff">browser</font></a>.extension_page</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/browser/extension_page.py">telemetry/internal/browser/extension_page.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2012&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="re.html">re</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.browser.web_contents.html">telemetry.internal.browser.web_contents</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.browser.web_contents.html#WebContents">telemetry.internal.browser.web_contents.WebContents</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.browser.extension_page.html#ExtensionPage">ExtensionPage</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ExtensionPage">class <strong>ExtensionPage</strong></a>(<a href="telemetry.internal.browser.web_contents.html#WebContents">telemetry.internal.browser.web_contents.WebContents</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Represents&nbsp;an&nbsp;extension&nbsp;page&nbsp;in&nbsp;the&nbsp;browser<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.browser.extension_page.html#ExtensionPage">ExtensionPage</a></dd>
+<dd><a href="telemetry.internal.browser.web_contents.html#WebContents">telemetry.internal.browser.web_contents.WebContents</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="ExtensionPage-Reload"><strong>Reload</strong></a>(self)</dt><dd><tt>Reloading&nbsp;an&nbsp;extension&nbsp;page&nbsp;is&nbsp;used&nbsp;as&nbsp;a&nbsp;workaround&nbsp;for&nbsp;an&nbsp;extension<br>
+binding&nbsp;bug&nbsp;for&nbsp;old&nbsp;versions&nbsp;of&nbsp;Chrome&nbsp;(crbug.com/263162).&nbsp;After&nbsp;Navigate<br>
+returns,&nbsp;we&nbsp;are&nbsp;guaranteed&nbsp;that&nbsp;the&nbsp;inspected&nbsp;page&nbsp;is&nbsp;in&nbsp;the&nbsp;correct&nbsp;state.</tt></dd></dl>
+
+<dl><dt><a name="ExtensionPage-__init__"><strong>__init__</strong></a>(self, inspector_backend)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.browser.web_contents.html#WebContents">telemetry.internal.browser.web_contents.WebContents</a>:<br>
+<dl><dt><a name="ExtensionPage-CloseConnections"><strong>CloseConnections</strong></a>(self)</dt><dd><tt>Closes&nbsp;all&nbsp;TCP&nbsp;sockets&nbsp;held&nbsp;open&nbsp;by&nbsp;the&nbsp;browser.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException&nbsp;if&nbsp;the&nbsp;tab&nbsp;is&nbsp;not&nbsp;alive.</tt></dd></dl>
+
+<dl><dt><a name="ExtensionPage-EnableAllContexts"><strong>EnableAllContexts</strong></a>(self)</dt><dd><tt>Enable&nbsp;all&nbsp;contexts&nbsp;in&nbsp;a&nbsp;page.&nbsp;Returns&nbsp;the&nbsp;number&nbsp;of&nbsp;available&nbsp;contexts.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.WebSocketDisconnected<br>
+&nbsp;&nbsp;exceptions.TimeoutException<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException</tt></dd></dl>
+
+<dl><dt><a name="ExtensionPage-EvaluateJavaScript"><strong>EvaluateJavaScript</strong></a>(self, expr, timeout<font color="#909090">=90</font>)</dt><dd><tt>Evalutes&nbsp;expr&nbsp;in&nbsp;JavaScript&nbsp;and&nbsp;returns&nbsp;the&nbsp;JSONized&nbsp;result.<br>
+&nbsp;<br>
+Consider&nbsp;using&nbsp;ExecuteJavaScript&nbsp;for&nbsp;cases&nbsp;where&nbsp;the&nbsp;result&nbsp;of&nbsp;the<br>
+expression&nbsp;is&nbsp;not&nbsp;needed.<br>
+&nbsp;<br>
+If&nbsp;evaluation&nbsp;throws&nbsp;in&nbsp;JavaScript,&nbsp;a&nbsp;Python&nbsp;EvaluateException&nbsp;will<br>
+be&nbsp;raised.<br>
+&nbsp;<br>
+If&nbsp;the&nbsp;result&nbsp;of&nbsp;the&nbsp;evaluation&nbsp;cannot&nbsp;be&nbsp;JSONized,&nbsp;then&nbsp;an<br>
+EvaluationException&nbsp;will&nbsp;be&nbsp;raised.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.Error:&nbsp;See&nbsp;<a href="#ExtensionPage-EvaluateJavaScriptInContext">EvaluateJavaScriptInContext</a>()&nbsp;for&nbsp;a&nbsp;detailed&nbsp;list<br>
+&nbsp;&nbsp;of&nbsp;possible&nbsp;exceptions.</tt></dd></dl>
+
+<dl><dt><a name="ExtensionPage-EvaluateJavaScriptInContext"><strong>EvaluateJavaScriptInContext</strong></a>(self, expr, context_id, timeout<font color="#909090">=90</font>)</dt><dd><tt>Similar&nbsp;to&nbsp;ExecuteJavaScript,&nbsp;except&nbsp;context_id&nbsp;can&nbsp;refer&nbsp;to&nbsp;an&nbsp;iframe.<br>
+The&nbsp;main&nbsp;page&nbsp;has&nbsp;context_id=1,&nbsp;the&nbsp;first&nbsp;iframe&nbsp;context_id=2,&nbsp;etc.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.EvaluateException<br>
+&nbsp;&nbsp;exceptions.WebSocketDisconnected<br>
+&nbsp;&nbsp;exceptions.TimeoutException<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException</tt></dd></dl>
+
+<dl><dt><a name="ExtensionPage-ExecuteJavaScript"><strong>ExecuteJavaScript</strong></a>(self, statement, timeout<font color="#909090">=90</font>)</dt><dd><tt>Executes&nbsp;statement&nbsp;in&nbsp;JavaScript.&nbsp;Does&nbsp;not&nbsp;return&nbsp;the&nbsp;result.<br>
+&nbsp;<br>
+If&nbsp;the&nbsp;statement&nbsp;failed&nbsp;to&nbsp;evaluate,&nbsp;EvaluateException&nbsp;will&nbsp;be&nbsp;raised.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.Error:&nbsp;See&nbsp;<a href="#ExtensionPage-ExecuteJavaScriptInContext">ExecuteJavaScriptInContext</a>()&nbsp;for&nbsp;a&nbsp;detailed&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;possible&nbsp;exceptions.</tt></dd></dl>
+
+<dl><dt><a name="ExtensionPage-ExecuteJavaScriptInContext"><strong>ExecuteJavaScriptInContext</strong></a>(self, expr, context_id, timeout<font color="#909090">=90</font>)</dt><dd><tt>Similar&nbsp;to&nbsp;ExecuteJavaScript,&nbsp;except&nbsp;context_id&nbsp;can&nbsp;refer&nbsp;to&nbsp;an&nbsp;iframe.<br>
+The&nbsp;main&nbsp;page&nbsp;has&nbsp;context_id=1,&nbsp;the&nbsp;first&nbsp;iframe&nbsp;context_id=2,&nbsp;etc.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.EvaluateException<br>
+&nbsp;&nbsp;exceptions.WebSocketDisconnected<br>
+&nbsp;&nbsp;exceptions.TimeoutException<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException</tt></dd></dl>
+
+<dl><dt><a name="ExtensionPage-GetUrl"><strong>GetUrl</strong></a>(self)</dt><dd><tt>Returns&nbsp;the&nbsp;URL&nbsp;to&nbsp;which&nbsp;the&nbsp;<a href="telemetry.internal.browser.web_contents.html#WebContents">WebContents</a>&nbsp;is&nbsp;connected.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.Error:&nbsp;If&nbsp;there&nbsp;is&nbsp;an&nbsp;error&nbsp;in&nbsp;inspector&nbsp;backend&nbsp;connection.</tt></dd></dl>
+
+<dl><dt><a name="ExtensionPage-GetWebviewContexts"><strong>GetWebviewContexts</strong></a>(self)</dt><dd><tt>Returns&nbsp;a&nbsp;list&nbsp;of&nbsp;webview&nbsp;contexts&nbsp;within&nbsp;the&nbsp;current&nbsp;inspector&nbsp;backend.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;A&nbsp;list&nbsp;of&nbsp;<a href="telemetry.internal.browser.web_contents.html#WebContents">WebContents</a>&nbsp;objects&nbsp;representing&nbsp;the&nbsp;webview&nbsp;contexts.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.Error:&nbsp;If&nbsp;there&nbsp;is&nbsp;an&nbsp;error&nbsp;in&nbsp;inspector&nbsp;backend&nbsp;connection.</tt></dd></dl>
+
+<dl><dt><a name="ExtensionPage-HasReachedQuiescence"><strong>HasReachedQuiescence</strong></a>(self)</dt><dd><tt>Determine&nbsp;whether&nbsp;the&nbsp;page&nbsp;has&nbsp;reached&nbsp;quiescence&nbsp;after&nbsp;loading.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;True&nbsp;if&nbsp;2&nbsp;seconds&nbsp;have&nbsp;passed&nbsp;since&nbsp;last&nbsp;resource&nbsp;received,&nbsp;false<br>
+&nbsp;&nbsp;otherwise.<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.Error:&nbsp;See&nbsp;<a href="#ExtensionPage-EvaluateJavaScript">EvaluateJavaScript</a>()&nbsp;for&nbsp;a&nbsp;detailed&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;possible&nbsp;exceptions.</tt></dd></dl>
+
+<dl><dt><a name="ExtensionPage-IsAlive"><strong>IsAlive</strong></a>(self)</dt><dd><tt>Whether&nbsp;the&nbsp;<a href="telemetry.internal.browser.web_contents.html#WebContents">WebContents</a>&nbsp;is&nbsp;still&nbsp;operating&nbsp;normally.<br>
+&nbsp;<br>
+Since&nbsp;<a href="telemetry.internal.browser.web_contents.html#WebContents">WebContents</a>&nbsp;function&nbsp;asynchronously,&nbsp;this&nbsp;method&nbsp;does&nbsp;not&nbsp;guarantee<br>
+that&nbsp;the&nbsp;<a href="telemetry.internal.browser.web_contents.html#WebContents">WebContents</a>&nbsp;will&nbsp;still&nbsp;be&nbsp;alive&nbsp;at&nbsp;any&nbsp;point&nbsp;in&nbsp;the&nbsp;future.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;A&nbsp;boolean&nbsp;indicating&nbsp;whether&nbsp;the&nbsp;<a href="telemetry.internal.browser.web_contents.html#WebContents">WebContents</a>&nbsp;is&nbsp;opearting&nbsp;normally.</tt></dd></dl>
+
+<dl><dt><a name="ExtensionPage-Navigate"><strong>Navigate</strong></a>(self, url, script_to_evaluate_on_commit<font color="#909090">=None</font>, timeout<font color="#909090">=90</font>)</dt><dd><tt>Navigates&nbsp;to&nbsp;url.<br>
+&nbsp;<br>
+If&nbsp;|script_to_evaluate_on_commit|&nbsp;is&nbsp;given,&nbsp;the&nbsp;script&nbsp;source&nbsp;string&nbsp;will&nbsp;be<br>
+evaluated&nbsp;when&nbsp;the&nbsp;navigation&nbsp;is&nbsp;committed.&nbsp;This&nbsp;is&nbsp;after&nbsp;the&nbsp;context&nbsp;of<br>
+the&nbsp;page&nbsp;exists,&nbsp;but&nbsp;before&nbsp;any&nbsp;script&nbsp;on&nbsp;the&nbsp;page&nbsp;itself&nbsp;has&nbsp;executed.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.TimeoutException<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException</tt></dd></dl>
+
+<dl><dt><a name="ExtensionPage-StartTimelineRecording"><strong>StartTimelineRecording</strong></a>(self)</dt><dd><tt>Starts&nbsp;timeline&nbsp;recording.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.TimeoutException<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException</tt></dd></dl>
+
+<dl><dt><a name="ExtensionPage-StopTimelineRecording"><strong>StopTimelineRecording</strong></a>(self)</dt><dd><tt>Stops&nbsp;timeline&nbsp;recording.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.TimeoutException<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException</tt></dd></dl>
+
+<dl><dt><a name="ExtensionPage-SynthesizeScrollGesture"><strong>SynthesizeScrollGesture</strong></a>(self, x<font color="#909090">=100</font>, y<font color="#909090">=800</font>, xDistance<font color="#909090">=0</font>, yDistance<font color="#909090">=-500</font>, xOverscroll<font color="#909090">=None</font>, yOverscroll<font color="#909090">=None</font>, preventFling<font color="#909090">=True</font>, speed<font color="#909090">=None</font>, gestureSourceType<font color="#909090">=None</font>, repeatCount<font color="#909090">=None</font>, repeatDelayMs<font color="#909090">=None</font>, interactionMarkerName<font color="#909090">=None</font>)</dt><dd><tt>Runs&nbsp;an&nbsp;inspector&nbsp;command&nbsp;that&nbsp;causes&nbsp;a&nbsp;repeatable&nbsp;browser&nbsp;driven&nbsp;scroll.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;x:&nbsp;X&nbsp;coordinate&nbsp;of&nbsp;the&nbsp;start&nbsp;of&nbsp;the&nbsp;gesture&nbsp;in&nbsp;CSS&nbsp;pixels.<br>
+&nbsp;&nbsp;y:&nbsp;Y&nbsp;coordinate&nbsp;of&nbsp;the&nbsp;start&nbsp;of&nbsp;the&nbsp;gesture&nbsp;in&nbsp;CSS&nbsp;pixels.<br>
+&nbsp;&nbsp;xDistance:&nbsp;Distance&nbsp;to&nbsp;scroll&nbsp;along&nbsp;the&nbsp;X&nbsp;axis&nbsp;(positive&nbsp;to&nbsp;scroll&nbsp;left).<br>
+&nbsp;&nbsp;yDistance:&nbsp;Ddistance&nbsp;to&nbsp;scroll&nbsp;along&nbsp;the&nbsp;Y&nbsp;axis&nbsp;(positive&nbsp;to&nbsp;scroll&nbsp;up).<br>
+&nbsp;&nbsp;xOverscroll:&nbsp;Number&nbsp;of&nbsp;additional&nbsp;pixels&nbsp;to&nbsp;scroll&nbsp;back&nbsp;along&nbsp;the&nbsp;X&nbsp;axis.<br>
+&nbsp;&nbsp;xOverscroll:&nbsp;Number&nbsp;of&nbsp;additional&nbsp;pixels&nbsp;to&nbsp;scroll&nbsp;back&nbsp;along&nbsp;the&nbsp;Y&nbsp;axis.<br>
+&nbsp;&nbsp;preventFling:&nbsp;Prevents&nbsp;a&nbsp;fling&nbsp;gesture.<br>
+&nbsp;&nbsp;speed:&nbsp;Swipe&nbsp;speed&nbsp;in&nbsp;pixels&nbsp;per&nbsp;second.<br>
+&nbsp;&nbsp;gestureSourceType:&nbsp;Which&nbsp;type&nbsp;of&nbsp;input&nbsp;events&nbsp;to&nbsp;be&nbsp;generated.<br>
+&nbsp;&nbsp;repeatCount:&nbsp;Number&nbsp;of&nbsp;additional&nbsp;repeats&nbsp;beyond&nbsp;the&nbsp;first&nbsp;scroll.<br>
+&nbsp;&nbsp;repeatDelayMs:&nbsp;Number&nbsp;of&nbsp;milliseconds&nbsp;delay&nbsp;between&nbsp;each&nbsp;repeat.<br>
+&nbsp;&nbsp;interactionMarkerName:&nbsp;The&nbsp;name&nbsp;of&nbsp;the&nbsp;interaction&nbsp;markers&nbsp;to&nbsp;generate.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.TimeoutException<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException</tt></dd></dl>
+
+<dl><dt><a name="ExtensionPage-WaitForDocumentReadyStateToBeComplete"><strong>WaitForDocumentReadyStateToBeComplete</strong></a>(self, timeout<font color="#909090">=90</font>)</dt><dd><tt>Waits&nbsp;for&nbsp;the&nbsp;document&nbsp;to&nbsp;finish&nbsp;loading.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.Error:&nbsp;See&nbsp;<a href="#ExtensionPage-WaitForJavaScriptExpression">WaitForJavaScriptExpression</a>()&nbsp;for&nbsp;a&nbsp;detailed&nbsp;list<br>
+&nbsp;&nbsp;of&nbsp;possible&nbsp;exceptions.</tt></dd></dl>
+
+<dl><dt><a name="ExtensionPage-WaitForDocumentReadyStateToBeInteractiveOrBetter"><strong>WaitForDocumentReadyStateToBeInteractiveOrBetter</strong></a>(self, timeout<font color="#909090">=90</font>)</dt><dd><tt>Waits&nbsp;for&nbsp;the&nbsp;document&nbsp;to&nbsp;be&nbsp;interactive.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.Error:&nbsp;See&nbsp;<a href="#ExtensionPage-WaitForJavaScriptExpression">WaitForJavaScriptExpression</a>()&nbsp;for&nbsp;a&nbsp;detailed&nbsp;list<br>
+&nbsp;&nbsp;of&nbsp;possible&nbsp;exceptions.</tt></dd></dl>
+
+<dl><dt><a name="ExtensionPage-WaitForJavaScriptExpression"><strong>WaitForJavaScriptExpression</strong></a>(self, expr, timeout, dump_page_state_on_timeout<font color="#909090">=True</font>)</dt><dd><tt>Waits&nbsp;for&nbsp;the&nbsp;given&nbsp;JavaScript&nbsp;expression&nbsp;to&nbsp;be&nbsp;True.<br>
+&nbsp;<br>
+This&nbsp;method&nbsp;is&nbsp;robust&nbsp;against&nbsp;any&nbsp;given&nbsp;Evaluation&nbsp;timing&nbsp;out.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;expr:&nbsp;The&nbsp;expression&nbsp;to&nbsp;evaluate.<br>
+&nbsp;&nbsp;timeout:&nbsp;The&nbsp;number&nbsp;of&nbsp;seconds&nbsp;to&nbsp;wait&nbsp;for&nbsp;the&nbsp;expression&nbsp;to&nbsp;be&nbsp;True.<br>
+&nbsp;&nbsp;dump_page_state_on_timeout:&nbsp;Whether&nbsp;to&nbsp;provide&nbsp;additional&nbsp;information&nbsp;on<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;the&nbsp;page&nbsp;state&nbsp;if&nbsp;a&nbsp;TimeoutException&nbsp;is&nbsp;thrown.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.TimeoutException:&nbsp;On&nbsp;a&nbsp;timeout.<br>
+&nbsp;&nbsp;exceptions.Error:&nbsp;See&nbsp;<a href="#ExtensionPage-EvaluateJavaScript">EvaluateJavaScript</a>()&nbsp;for&nbsp;a&nbsp;detailed&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;possible&nbsp;exceptions.</tt></dd></dl>
+
+<dl><dt><a name="ExtensionPage-WaitForNavigate"><strong>WaitForNavigate</strong></a>(self, timeout<font color="#909090">=90</font>)</dt><dd><tt>Waits&nbsp;for&nbsp;the&nbsp;navigation&nbsp;to&nbsp;complete.<br>
+&nbsp;<br>
+The&nbsp;current&nbsp;page&nbsp;is&nbsp;expect&nbsp;to&nbsp;be&nbsp;in&nbsp;a&nbsp;navigation.<br>
+This&nbsp;function&nbsp;returns&nbsp;when&nbsp;the&nbsp;navigation&nbsp;is&nbsp;complete&nbsp;or&nbsp;when<br>
+the&nbsp;timeout&nbsp;has&nbsp;been&nbsp;exceeded.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.TimeoutException<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.browser.web_contents.html#WebContents">telemetry.internal.browser.web_contents.WebContents</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>id</strong></dt>
+<dd><tt>Return&nbsp;the&nbsp;unique&nbsp;id&nbsp;string&nbsp;for&nbsp;this&nbsp;tab&nbsp;object.</tt></dd>
+</dl>
+<dl><dt><strong>message_output_stream</strong></dt>
+</dl>
+<dl><dt><strong>timeline_model</strong></dt>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-UrlToExtensionId"><strong>UrlToExtensionId</strong></a>(url)</dt></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.extension_to_load.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.extension_to_load.html
new file mode 100644
index 0000000..33162f9
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.extension_to_load.html
@@ -0,0 +1,195 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.browser.extension_to_load</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.browser.html"><font color="#ffffff">browser</font></a>.extension_to_load</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/browser/extension_to_load.py">telemetry/internal/browser/extension_to_load.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.backends.chrome.crx_id.html">telemetry.internal.backends.chrome.crx_id</a><br>
+</td><td width="25%" valign=top><a href="os.html">os</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.browser.extension_to_load.html#ExtensionToLoad">ExtensionToLoad</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="exceptions.html#Exception">exceptions.Exception</a>(<a href="exceptions.html#BaseException">exceptions.BaseException</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.browser.extension_to_load.html#ExtensionPathNonExistentException">ExtensionPathNonExistentException</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.internal.browser.extension_to_load.html#MissingPublicKeyException">MissingPublicKeyException</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ExtensionPathNonExistentException">class <strong>ExtensionPathNonExistentException</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.browser.extension_to_load.html#ExtensionPathNonExistentException">ExtensionPathNonExistentException</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="ExtensionPathNonExistentException-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#ExtensionPathNonExistentException-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#ExtensionPathNonExistentException-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="ExtensionPathNonExistentException-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#ExtensionPathNonExistentException-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="ExtensionPathNonExistentException-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#ExtensionPathNonExistentException-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="ExtensionPathNonExistentException-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#ExtensionPathNonExistentException-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="ExtensionPathNonExistentException-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#ExtensionPathNonExistentException-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="ExtensionPathNonExistentException-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="ExtensionPathNonExistentException-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#ExtensionPathNonExistentException-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="ExtensionPathNonExistentException-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#ExtensionPathNonExistentException-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="ExtensionPathNonExistentException-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="ExtensionPathNonExistentException-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#ExtensionPathNonExistentException-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="ExtensionPathNonExistentException-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ExtensionToLoad">class <strong>ExtensionToLoad</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="ExtensionToLoad-__init__"><strong>__init__</strong></a>(self, path, browser_type, is_component<font color="#909090">=False</font>)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>extension_id</strong></dt>
+<dd><tt>Unique&nbsp;extension&nbsp;id&nbsp;of&nbsp;this&nbsp;extension.</tt></dd>
+</dl>
+<dl><dt><strong>is_component</strong></dt>
+<dd><tt>Whether&nbsp;this&nbsp;extension&nbsp;should&nbsp;be&nbsp;loaded&nbsp;as&nbsp;a&nbsp;component&nbsp;extension.</tt></dd>
+</dl>
+<dl><dt><strong>local_path</strong></dt>
+<dd><tt>Path&nbsp;to&nbsp;extension&nbsp;destination&nbsp;directory,&nbsp;for&nbsp;remote&nbsp;instances&nbsp;of<br>
+chrome</tt></dd>
+</dl>
+<dl><dt><strong>path</strong></dt>
+<dd><tt>Path&nbsp;to&nbsp;extension&nbsp;source&nbsp;directory.</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="MissingPublicKeyException">class <strong>MissingPublicKeyException</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.browser.extension_to_load.html#MissingPublicKeyException">MissingPublicKeyException</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="MissingPublicKeyException-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#MissingPublicKeyException-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#MissingPublicKeyException-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="MissingPublicKeyException-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#MissingPublicKeyException-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="MissingPublicKeyException-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#MissingPublicKeyException-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="MissingPublicKeyException-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#MissingPublicKeyException-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="MissingPublicKeyException-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#MissingPublicKeyException-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="MissingPublicKeyException-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="MissingPublicKeyException-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#MissingPublicKeyException-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="MissingPublicKeyException-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#MissingPublicKeyException-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="MissingPublicKeyException-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="MissingPublicKeyException-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#MissingPublicKeyException-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="MissingPublicKeyException-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.html
new file mode 100644
index 0000000..cb39678
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.html
@@ -0,0 +1,46 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: package telemetry.internal.browser</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.browser</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/browser/__init__.py">telemetry/internal/browser/__init__.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2015&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Package Contents</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.browser.browser.html">browser</a><br>
+<a href="telemetry.internal.browser.browser_credentials.html">browser_credentials</a><br>
+<a href="telemetry.internal.browser.browser_credentials_unittest.html">browser_credentials_unittest</a><br>
+<a href="telemetry.internal.browser.browser_finder.html">browser_finder</a><br>
+<a href="telemetry.internal.browser.browser_finder_exceptions.html">browser_finder_exceptions</a><br>
+<a href="telemetry.internal.browser.browser_info.html">browser_info</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.browser.browser_options.html">browser_options</a><br>
+<a href="telemetry.internal.browser.browser_options_unittest.html">browser_options_unittest</a><br>
+<a href="telemetry.internal.browser.browser_unittest.html">browser_unittest</a><br>
+<a href="telemetry.internal.browser.extension_dict.html">extension_dict</a><br>
+<a href="telemetry.internal.browser.extension_page.html">extension_page</a><br>
+<a href="telemetry.internal.browser.extension_to_load.html">extension_to_load</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.browser.extension_unittest.html">extension_unittest</a><br>
+<a href="telemetry.internal.browser.possible_browser.html">possible_browser</a><br>
+<a href="telemetry.internal.browser.profile_types.html">profile_types</a><br>
+<a href="telemetry.internal.browser.profile_types_unittest.html">profile_types_unittest</a><br>
+<a href="telemetry.internal.browser.tab.html">tab</a><br>
+<a href="telemetry.internal.browser.tab_list.html">tab_list</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.browser.tab_unittest.html">tab_unittest</a><br>
+<a href="telemetry.internal.browser.user_agent.html">user_agent</a><br>
+<a href="telemetry.internal.browser.user_agent_unittest.html">user_agent_unittest</a><br>
+<a href="telemetry.internal.browser.web_contents.html">web_contents</a><br>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.possible_browser.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.possible_browser.html
new file mode 100644
index 0000000..2f77507
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.possible_browser.html
@@ -0,0 +1,97 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.browser.possible_browser</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.browser.html"><font color="#ffffff">browser</font></a>.possible_browser</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/browser/possible_browser.py">telemetry/internal/browser/possible_browser.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2012&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.app.possible_app.html">telemetry.internal.app.possible_app</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.app.possible_app.html#PossibleApp">telemetry.internal.app.possible_app.PossibleApp</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.browser.possible_browser.html#PossibleBrowser">PossibleBrowser</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="PossibleBrowser">class <strong>PossibleBrowser</strong></a>(<a href="telemetry.internal.app.possible_app.html#PossibleApp">telemetry.internal.app.possible_app.PossibleApp</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;browser&nbsp;that&nbsp;can&nbsp;be&nbsp;controlled.<br>
+&nbsp;<br>
+Call&nbsp;<a href="#PossibleBrowser-Create">Create</a>()&nbsp;to&nbsp;launch&nbsp;the&nbsp;browser&nbsp;and&nbsp;begin&nbsp;manipulating&nbsp;it..<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.browser.possible_browser.html#PossibleBrowser">PossibleBrowser</a></dd>
+<dd><a href="telemetry.internal.app.possible_app.html#PossibleApp">telemetry.internal.app.possible_app.PossibleApp</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="PossibleBrowser-Create"><strong>Create</strong></a>(self, finder_options)</dt></dl>
+
+<dl><dt><a name="PossibleBrowser-IsRemote"><strong>IsRemote</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PossibleBrowser-RunRemote"><strong>RunRemote</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PossibleBrowser-SetCredentialsPath"><strong>SetCredentialsPath</strong></a>(self, credentials_path)</dt></dl>
+
+<dl><dt><a name="PossibleBrowser-SupportsOptions"><strong>SupportsOptions</strong></a>(self, finder_options)</dt><dd><tt>Tests&nbsp;for&nbsp;extension&nbsp;support.</tt></dd></dl>
+
+<dl><dt><a name="PossibleBrowser-UpdateExecutableIfNeeded"><strong>UpdateExecutableIfNeeded</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PossibleBrowser-__init__"><strong>__init__</strong></a>(self, browser_type, target_os, supports_tab_control)</dt></dl>
+
+<dl><dt><a name="PossibleBrowser-__repr__"><strong>__repr__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PossibleBrowser-last_modification_time"><strong>last_modification_time</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>browser_type</strong></dt>
+</dl>
+<dl><dt><strong>supports_tab_control</strong></dt>
+</dl>
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.app.possible_app.html#PossibleApp">telemetry.internal.app.possible_app.PossibleApp</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>app_type</strong></dt>
+</dl>
+<dl><dt><strong>platform</strong></dt>
+</dl>
+<dl><dt><strong>target_os</strong></dt>
+<dd><tt>Target&nbsp;OS,&nbsp;the&nbsp;app&nbsp;will&nbsp;run&nbsp;on.</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.profile_types.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.profile_types.html
new file mode 100644
index 0000000..2311ceb
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.profile_types.html
@@ -0,0 +1,46 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.browser.profile_types</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.browser.html"><font color="#ffffff">browser</font></a>.profile_types</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/browser/profile_types.py">telemetry/internal/browser/profile_types.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="os.html">os</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.util.html">telemetry.core.util</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-GetProfileDir"><strong>GetProfileDir</strong></a>(profile_type)</dt><dd><tt>Given&nbsp;a&nbsp;|profile_type|&nbsp;(as&nbsp;returned&nbsp;by&nbsp;<a href="#-GetProfileTypes">GetProfileTypes</a>()),&nbsp;return&nbsp;the<br>
+directory&nbsp;to&nbsp;use&nbsp;for&nbsp;that&nbsp;profile&nbsp;or&nbsp;None&nbsp;if&nbsp;the&nbsp;profile&nbsp;doesn't&nbsp;need&nbsp;a<br>
+profile&nbsp;directory&nbsp;(e.g.&nbsp;using&nbsp;the&nbsp;browser&nbsp;default&nbsp;profile).</tt></dd></dl>
+ <dl><dt><a name="-GetProfileTypes"><strong>GetProfileTypes</strong></a>()</dt><dd><tt>Returns&nbsp;a&nbsp;list&nbsp;of&nbsp;all&nbsp;command&nbsp;line&nbsp;options&nbsp;that&nbsp;can&nbsp;be&nbsp;specified&nbsp;for<br>
+profile&nbsp;type.</tt></dd></dl>
+</td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>BASE_PROFILE_TYPES</strong> = ['clean', 'default']<br>
+<strong>PROFILE_TYPE_MAPPING</strong> = {'power_user': 'extension_webrequest', 'typical_user': 'content_scripts1'}</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.tab.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.tab.html
new file mode 100644
index 0000000..2133051
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.tab.html
@@ -0,0 +1,395 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.browser.tab</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.browser.html"><font color="#ffffff">browser</font></a>.tab</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/browser/tab.py">telemetry/internal/browser/tab.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2012&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.image_processing.video.html">telemetry.internal.image_processing.video</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.browser.web_contents.html">telemetry.internal.browser.web_contents</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.browser.web_contents.html#WebContents">telemetry.internal.browser.web_contents.WebContents</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.browser.tab.html#Tab">Tab</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="Tab">class <strong>Tab</strong></a>(<a href="telemetry.internal.browser.web_contents.html#WebContents">telemetry.internal.browser.web_contents.WebContents</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Represents&nbsp;a&nbsp;tab&nbsp;in&nbsp;the&nbsp;browser<br>
+&nbsp;<br>
+The&nbsp;important&nbsp;parts&nbsp;of&nbsp;the&nbsp;<a href="#Tab">Tab</a>&nbsp;object&nbsp;are&nbsp;in&nbsp;the&nbsp;runtime&nbsp;and&nbsp;page&nbsp;objects.<br>
+E.g.:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;Navigates&nbsp;the&nbsp;tab&nbsp;to&nbsp;a&nbsp;given&nbsp;url.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;tab.<a href="#Tab-Navigate">Navigate</a>('<a href="http://www.google.com/">http://www.google.com/</a>')<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;Evaluates&nbsp;1+1&nbsp;in&nbsp;the&nbsp;tab's&nbsp;JavaScript&nbsp;context.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;tab.Evaluate('1+1')<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.browser.tab.html#Tab">Tab</a></dd>
+<dd><a href="telemetry.internal.browser.web_contents.html#WebContents">telemetry.internal.browser.web_contents.WebContents</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="Tab-Activate"><strong>Activate</strong></a>(self)</dt><dd><tt>Brings&nbsp;this&nbsp;tab&nbsp;to&nbsp;the&nbsp;foreground&nbsp;asynchronously.<br>
+&nbsp;<br>
+Not&nbsp;all&nbsp;browsers&nbsp;or&nbsp;browser&nbsp;versions&nbsp;support&nbsp;this&nbsp;method.<br>
+Be&nbsp;sure&nbsp;to&nbsp;check&nbsp;browser.supports_tab_control.<br>
+&nbsp;<br>
+Please&nbsp;note:&nbsp;this&nbsp;is&nbsp;asynchronous.&nbsp;There&nbsp;is&nbsp;a&nbsp;delay&nbsp;between&nbsp;this&nbsp;call<br>
+and&nbsp;the&nbsp;page's&nbsp;documentVisibilityState&nbsp;becoming&nbsp;'visible',&nbsp;and&nbsp;yet&nbsp;more<br>
+delay&nbsp;until&nbsp;the&nbsp;actual&nbsp;tab&nbsp;is&nbsp;visible&nbsp;to&nbsp;the&nbsp;user.&nbsp;None&nbsp;of&nbsp;these&nbsp;delays<br>
+are&nbsp;included&nbsp;in&nbsp;this&nbsp;call.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;devtools_http.DevToolsClientConnectionError<br>
+&nbsp;&nbsp;devtools_client_backend.TabNotFoundError<br>
+&nbsp;&nbsp;tab_list_backend.TabUnexpectedResponseException</tt></dd></dl>
+
+<dl><dt><a name="Tab-ClearCache"><strong>ClearCache</strong></a>(self, force)</dt><dd><tt>Clears&nbsp;the&nbsp;browser's&nbsp;networking&nbsp;related&nbsp;disk,&nbsp;memory&nbsp;and&nbsp;other&nbsp;caches.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;force:&nbsp;Iff&nbsp;true,&nbsp;navigates&nbsp;to&nbsp;about:blank&nbsp;which&nbsp;destroys&nbsp;the&nbsp;previous<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;renderer,&nbsp;ensuring&nbsp;that&nbsp;even&nbsp;"live"&nbsp;resources&nbsp;in&nbsp;the&nbsp;memory&nbsp;cache&nbsp;are<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cleared.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.EvaluateException<br>
+&nbsp;&nbsp;exceptions.WebSocketDisconnected<br>
+&nbsp;&nbsp;exceptions.TimeoutException<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException<br>
+&nbsp;&nbsp;errors.DeviceUnresponsiveError</tt></dd></dl>
+
+<dl><dt><a name="Tab-ClearHighlight"><strong>ClearHighlight</strong></a>(self, color)</dt><dd><tt>Clears&nbsp;a&nbsp;highlight&nbsp;of&nbsp;the&nbsp;given&nbsp;bitmap.RgbaColor.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.EvaluateException<br>
+&nbsp;&nbsp;exceptions.WebSocketDisconnected<br>
+&nbsp;&nbsp;exceptions.TimeoutException<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException</tt></dd></dl>
+
+<dl><dt><a name="Tab-Close"><strong>Close</strong></a>(self)</dt><dd><tt>Closes&nbsp;this&nbsp;tab.<br>
+&nbsp;<br>
+Not&nbsp;all&nbsp;browsers&nbsp;or&nbsp;browser&nbsp;versions&nbsp;support&nbsp;this&nbsp;method.<br>
+Be&nbsp;sure&nbsp;to&nbsp;check&nbsp;browser.supports_tab_control.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;devtools_http.DevToolsClientConnectionError<br>
+&nbsp;&nbsp;devtools_client_backend.TabNotFoundError<br>
+&nbsp;&nbsp;tab_list_backend.TabUnexpectedResponseException<br>
+&nbsp;&nbsp;exceptions.TimeoutException</tt></dd></dl>
+
+<dl><dt><a name="Tab-CollectGarbage"><strong>CollectGarbage</strong></a>(self)</dt><dd><tt>Forces&nbsp;a&nbsp;garbage&nbsp;collection.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.WebSocketDisconnected<br>
+&nbsp;&nbsp;exceptions.TimeoutException<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException</tt></dd></dl>
+
+<dl><dt><a name="Tab-GetCookieByName"><strong>GetCookieByName</strong></a>(self, name, timeout<font color="#909090">=60</font>)</dt><dd><tt>Returns&nbsp;the&nbsp;value&nbsp;of&nbsp;the&nbsp;cookie&nbsp;by&nbsp;the&nbsp;given&nbsp;|name|.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.WebSocketDisconnected<br>
+&nbsp;&nbsp;exceptions.TimeoutException<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException</tt></dd></dl>
+
+<dl><dt><a name="Tab-Highlight"><strong>Highlight</strong></a>(self, color)</dt><dd><tt>Synchronously&nbsp;highlights&nbsp;entire&nbsp;tab&nbsp;contents&nbsp;with&nbsp;the&nbsp;given&nbsp;RgbaColor.<br>
+&nbsp;<br>
+TODO(tonyg):&nbsp;It&nbsp;is&nbsp;possible&nbsp;that&nbsp;the&nbsp;z-index&nbsp;hack&nbsp;here&nbsp;might&nbsp;not&nbsp;work&nbsp;for<br>
+all&nbsp;pages.&nbsp;If&nbsp;this&nbsp;happens,&nbsp;DevTools&nbsp;also&nbsp;provides&nbsp;a&nbsp;method&nbsp;for&nbsp;this.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.EvaluateException<br>
+&nbsp;&nbsp;exceptions.WebSocketDisconnected<br>
+&nbsp;&nbsp;exceptions.TimeoutException<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException</tt></dd></dl>
+
+<dl><dt><a name="Tab-Screenshot"><strong>Screenshot</strong></a>(self, timeout<font color="#909090">=60</font>)</dt><dd><tt>Capture&nbsp;a&nbsp;screenshot&nbsp;of&nbsp;the&nbsp;tab's&nbsp;contents.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;A&nbsp;telemetry.core.Bitmap.<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.WebSocketDisconnected<br>
+&nbsp;&nbsp;exceptions.TimeoutException<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException</tt></dd></dl>
+
+<dl><dt><a name="Tab-StartVideoCapture"><strong>StartVideoCapture</strong></a>(self, min_bitrate_mbps, highlight_bitmap<font color="#909090">=RgbaColor(r=222, g=100, b=13, a=255)</font>)</dt><dd><tt>Starts&nbsp;capturing&nbsp;video&nbsp;of&nbsp;the&nbsp;tab's&nbsp;contents.<br>
+&nbsp;<br>
+This&nbsp;works&nbsp;by&nbsp;flashing&nbsp;the&nbsp;entire&nbsp;tab&nbsp;contents&nbsp;to&nbsp;a&nbsp;arbitrary&nbsp;color&nbsp;and&nbsp;then<br>
+starting&nbsp;video&nbsp;recording.&nbsp;When&nbsp;the&nbsp;frames&nbsp;are&nbsp;processed,&nbsp;we&nbsp;can&nbsp;look&nbsp;for<br>
+that&nbsp;flash&nbsp;as&nbsp;the&nbsp;content&nbsp;bounds.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;min_bitrate_mbps:&nbsp;The&nbsp;minimum&nbsp;caputre&nbsp;bitrate&nbsp;in&nbsp;MegaBits&nbsp;Per&nbsp;Second.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The&nbsp;platform&nbsp;is&nbsp;free&nbsp;to&nbsp;deliver&nbsp;a&nbsp;higher&nbsp;bitrate&nbsp;if&nbsp;it&nbsp;can&nbsp;do&nbsp;so<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;without&nbsp;increasing&nbsp;overhead.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.EvaluateException<br>
+&nbsp;&nbsp;exceptions.WebSocketDisconnected<br>
+&nbsp;&nbsp;exceptions.TimeoutException<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException<br>
+&nbsp;&nbsp;ValueError:&nbsp;If&nbsp;the&nbsp;required&nbsp;|min_bitrate_mbps|&nbsp;can't&nbsp;be&nbsp;achieved.</tt></dd></dl>
+
+<dl><dt><a name="Tab-StopVideoCapture"><strong>StopVideoCapture</strong></a>(self)</dt><dd><tt>Stops&nbsp;recording&nbsp;video&nbsp;of&nbsp;the&nbsp;tab's&nbsp;contents.<br>
+&nbsp;<br>
+This&nbsp;looks&nbsp;for&nbsp;the&nbsp;initial&nbsp;color&nbsp;flash&nbsp;in&nbsp;the&nbsp;first&nbsp;frame&nbsp;to&nbsp;establish&nbsp;the<br>
+tab&nbsp;content&nbsp;boundaries&nbsp;and&nbsp;then&nbsp;omits&nbsp;all&nbsp;frames&nbsp;displaying&nbsp;the&nbsp;flash.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;video:&nbsp;A&nbsp;video&nbsp;object&nbsp;which&nbsp;is&nbsp;a&nbsp;telemetry.core.Video</tt></dd></dl>
+
+<dl><dt><a name="Tab-__init__"><strong>__init__</strong></a>(self, inspector_backend, tab_list_backend, browser)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>browser</strong></dt>
+<dd><tt>The&nbsp;browser&nbsp;in&nbsp;which&nbsp;this&nbsp;tab&nbsp;resides.</tt></dd>
+</dl>
+<dl><dt><strong>dom_stats</strong></dt>
+<dd><tt>A&nbsp;dictionary&nbsp;populated&nbsp;with&nbsp;measured&nbsp;DOM&nbsp;statistics.<br>
+&nbsp;<br>
+Currently&nbsp;this&nbsp;dictionary&nbsp;contains:<br>
+{<br>
+&nbsp;&nbsp;'document_count':&nbsp;integer,<br>
+&nbsp;&nbsp;'node_count':&nbsp;integer,<br>
+&nbsp;&nbsp;'event_listener_count':&nbsp;integer<br>
+}<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;inspector_memory.InspectorMemoryException<br>
+&nbsp;&nbsp;exceptions.TimeoutException<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException</tt></dd>
+</dl>
+<dl><dt><strong>is_video_capture_running</strong></dt>
+</dl>
+<dl><dt><strong>screenshot_supported</strong></dt>
+<dd><tt>True&nbsp;if&nbsp;the&nbsp;browser&nbsp;instance&nbsp;is&nbsp;capable&nbsp;of&nbsp;capturing&nbsp;screenshots.</tt></dd>
+</dl>
+<dl><dt><strong>url</strong></dt>
+<dd><tt>Returns&nbsp;the&nbsp;URL&nbsp;of&nbsp;the&nbsp;tab,&nbsp;as&nbsp;reported&nbsp;by&nbsp;devtools.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;devtools_http.DevToolsClientConnectionError</tt></dd>
+</dl>
+<dl><dt><strong>video_capture_supported</strong></dt>
+<dd><tt>True&nbsp;if&nbsp;the&nbsp;browser&nbsp;instance&nbsp;is&nbsp;capable&nbsp;of&nbsp;capturing&nbsp;video.</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.internal.browser.web_contents.html#WebContents">telemetry.internal.browser.web_contents.WebContents</a>:<br>
+<dl><dt><a name="Tab-CloseConnections"><strong>CloseConnections</strong></a>(self)</dt><dd><tt>Closes&nbsp;all&nbsp;TCP&nbsp;sockets&nbsp;held&nbsp;open&nbsp;by&nbsp;the&nbsp;browser.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException&nbsp;if&nbsp;the&nbsp;tab&nbsp;is&nbsp;not&nbsp;alive.</tt></dd></dl>
+
+<dl><dt><a name="Tab-EnableAllContexts"><strong>EnableAllContexts</strong></a>(self)</dt><dd><tt>Enable&nbsp;all&nbsp;contexts&nbsp;in&nbsp;a&nbsp;page.&nbsp;Returns&nbsp;the&nbsp;number&nbsp;of&nbsp;available&nbsp;contexts.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.WebSocketDisconnected<br>
+&nbsp;&nbsp;exceptions.TimeoutException<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException</tt></dd></dl>
+
+<dl><dt><a name="Tab-EvaluateJavaScript"><strong>EvaluateJavaScript</strong></a>(self, expr, timeout<font color="#909090">=90</font>)</dt><dd><tt>Evalutes&nbsp;expr&nbsp;in&nbsp;JavaScript&nbsp;and&nbsp;returns&nbsp;the&nbsp;JSONized&nbsp;result.<br>
+&nbsp;<br>
+Consider&nbsp;using&nbsp;ExecuteJavaScript&nbsp;for&nbsp;cases&nbsp;where&nbsp;the&nbsp;result&nbsp;of&nbsp;the<br>
+expression&nbsp;is&nbsp;not&nbsp;needed.<br>
+&nbsp;<br>
+If&nbsp;evaluation&nbsp;throws&nbsp;in&nbsp;JavaScript,&nbsp;a&nbsp;Python&nbsp;EvaluateException&nbsp;will<br>
+be&nbsp;raised.<br>
+&nbsp;<br>
+If&nbsp;the&nbsp;result&nbsp;of&nbsp;the&nbsp;evaluation&nbsp;cannot&nbsp;be&nbsp;JSONized,&nbsp;then&nbsp;an<br>
+EvaluationException&nbsp;will&nbsp;be&nbsp;raised.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.Error:&nbsp;See&nbsp;<a href="#Tab-EvaluateJavaScriptInContext">EvaluateJavaScriptInContext</a>()&nbsp;for&nbsp;a&nbsp;detailed&nbsp;list<br>
+&nbsp;&nbsp;of&nbsp;possible&nbsp;exceptions.</tt></dd></dl>
+
+<dl><dt><a name="Tab-EvaluateJavaScriptInContext"><strong>EvaluateJavaScriptInContext</strong></a>(self, expr, context_id, timeout<font color="#909090">=90</font>)</dt><dd><tt>Similar&nbsp;to&nbsp;ExecuteJavaScript,&nbsp;except&nbsp;context_id&nbsp;can&nbsp;refer&nbsp;to&nbsp;an&nbsp;iframe.<br>
+The&nbsp;main&nbsp;page&nbsp;has&nbsp;context_id=1,&nbsp;the&nbsp;first&nbsp;iframe&nbsp;context_id=2,&nbsp;etc.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.EvaluateException<br>
+&nbsp;&nbsp;exceptions.WebSocketDisconnected<br>
+&nbsp;&nbsp;exceptions.TimeoutException<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException</tt></dd></dl>
+
+<dl><dt><a name="Tab-ExecuteJavaScript"><strong>ExecuteJavaScript</strong></a>(self, statement, timeout<font color="#909090">=90</font>)</dt><dd><tt>Executes&nbsp;statement&nbsp;in&nbsp;JavaScript.&nbsp;Does&nbsp;not&nbsp;return&nbsp;the&nbsp;result.<br>
+&nbsp;<br>
+If&nbsp;the&nbsp;statement&nbsp;failed&nbsp;to&nbsp;evaluate,&nbsp;EvaluateException&nbsp;will&nbsp;be&nbsp;raised.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.Error:&nbsp;See&nbsp;<a href="#Tab-ExecuteJavaScriptInContext">ExecuteJavaScriptInContext</a>()&nbsp;for&nbsp;a&nbsp;detailed&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;possible&nbsp;exceptions.</tt></dd></dl>
+
+<dl><dt><a name="Tab-ExecuteJavaScriptInContext"><strong>ExecuteJavaScriptInContext</strong></a>(self, expr, context_id, timeout<font color="#909090">=90</font>)</dt><dd><tt>Similar&nbsp;to&nbsp;ExecuteJavaScript,&nbsp;except&nbsp;context_id&nbsp;can&nbsp;refer&nbsp;to&nbsp;an&nbsp;iframe.<br>
+The&nbsp;main&nbsp;page&nbsp;has&nbsp;context_id=1,&nbsp;the&nbsp;first&nbsp;iframe&nbsp;context_id=2,&nbsp;etc.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.EvaluateException<br>
+&nbsp;&nbsp;exceptions.WebSocketDisconnected<br>
+&nbsp;&nbsp;exceptions.TimeoutException<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException</tt></dd></dl>
+
+<dl><dt><a name="Tab-GetUrl"><strong>GetUrl</strong></a>(self)</dt><dd><tt>Returns&nbsp;the&nbsp;URL&nbsp;to&nbsp;which&nbsp;the&nbsp;<a href="telemetry.internal.browser.web_contents.html#WebContents">WebContents</a>&nbsp;is&nbsp;connected.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.Error:&nbsp;If&nbsp;there&nbsp;is&nbsp;an&nbsp;error&nbsp;in&nbsp;inspector&nbsp;backend&nbsp;connection.</tt></dd></dl>
+
+<dl><dt><a name="Tab-GetWebviewContexts"><strong>GetWebviewContexts</strong></a>(self)</dt><dd><tt>Returns&nbsp;a&nbsp;list&nbsp;of&nbsp;webview&nbsp;contexts&nbsp;within&nbsp;the&nbsp;current&nbsp;inspector&nbsp;backend.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;A&nbsp;list&nbsp;of&nbsp;<a href="telemetry.internal.browser.web_contents.html#WebContents">WebContents</a>&nbsp;objects&nbsp;representing&nbsp;the&nbsp;webview&nbsp;contexts.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.Error:&nbsp;If&nbsp;there&nbsp;is&nbsp;an&nbsp;error&nbsp;in&nbsp;inspector&nbsp;backend&nbsp;connection.</tt></dd></dl>
+
+<dl><dt><a name="Tab-HasReachedQuiescence"><strong>HasReachedQuiescence</strong></a>(self)</dt><dd><tt>Determine&nbsp;whether&nbsp;the&nbsp;page&nbsp;has&nbsp;reached&nbsp;quiescence&nbsp;after&nbsp;loading.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;True&nbsp;if&nbsp;2&nbsp;seconds&nbsp;have&nbsp;passed&nbsp;since&nbsp;last&nbsp;resource&nbsp;received,&nbsp;false<br>
+&nbsp;&nbsp;otherwise.<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.Error:&nbsp;See&nbsp;<a href="#Tab-EvaluateJavaScript">EvaluateJavaScript</a>()&nbsp;for&nbsp;a&nbsp;detailed&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;possible&nbsp;exceptions.</tt></dd></dl>
+
+<dl><dt><a name="Tab-IsAlive"><strong>IsAlive</strong></a>(self)</dt><dd><tt>Whether&nbsp;the&nbsp;<a href="telemetry.internal.browser.web_contents.html#WebContents">WebContents</a>&nbsp;is&nbsp;still&nbsp;operating&nbsp;normally.<br>
+&nbsp;<br>
+Since&nbsp;<a href="telemetry.internal.browser.web_contents.html#WebContents">WebContents</a>&nbsp;function&nbsp;asynchronously,&nbsp;this&nbsp;method&nbsp;does&nbsp;not&nbsp;guarantee<br>
+that&nbsp;the&nbsp;<a href="telemetry.internal.browser.web_contents.html#WebContents">WebContents</a>&nbsp;will&nbsp;still&nbsp;be&nbsp;alive&nbsp;at&nbsp;any&nbsp;point&nbsp;in&nbsp;the&nbsp;future.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;A&nbsp;boolean&nbsp;indicating&nbsp;whether&nbsp;the&nbsp;<a href="telemetry.internal.browser.web_contents.html#WebContents">WebContents</a>&nbsp;is&nbsp;opearting&nbsp;normally.</tt></dd></dl>
+
+<dl><dt><a name="Tab-Navigate"><strong>Navigate</strong></a>(self, url, script_to_evaluate_on_commit<font color="#909090">=None</font>, timeout<font color="#909090">=90</font>)</dt><dd><tt>Navigates&nbsp;to&nbsp;url.<br>
+&nbsp;<br>
+If&nbsp;|script_to_evaluate_on_commit|&nbsp;is&nbsp;given,&nbsp;the&nbsp;script&nbsp;source&nbsp;string&nbsp;will&nbsp;be<br>
+evaluated&nbsp;when&nbsp;the&nbsp;navigation&nbsp;is&nbsp;committed.&nbsp;This&nbsp;is&nbsp;after&nbsp;the&nbsp;context&nbsp;of<br>
+the&nbsp;page&nbsp;exists,&nbsp;but&nbsp;before&nbsp;any&nbsp;script&nbsp;on&nbsp;the&nbsp;page&nbsp;itself&nbsp;has&nbsp;executed.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.TimeoutException<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException</tt></dd></dl>
+
+<dl><dt><a name="Tab-StartTimelineRecording"><strong>StartTimelineRecording</strong></a>(self)</dt><dd><tt>Starts&nbsp;timeline&nbsp;recording.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.TimeoutException<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException</tt></dd></dl>
+
+<dl><dt><a name="Tab-StopTimelineRecording"><strong>StopTimelineRecording</strong></a>(self)</dt><dd><tt>Stops&nbsp;timeline&nbsp;recording.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.TimeoutException<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException</tt></dd></dl>
+
+<dl><dt><a name="Tab-SynthesizeScrollGesture"><strong>SynthesizeScrollGesture</strong></a>(self, x<font color="#909090">=100</font>, y<font color="#909090">=800</font>, xDistance<font color="#909090">=0</font>, yDistance<font color="#909090">=-500</font>, xOverscroll<font color="#909090">=None</font>, yOverscroll<font color="#909090">=None</font>, preventFling<font color="#909090">=True</font>, speed<font color="#909090">=None</font>, gestureSourceType<font color="#909090">=None</font>, repeatCount<font color="#909090">=None</font>, repeatDelayMs<font color="#909090">=None</font>, interactionMarkerName<font color="#909090">=None</font>)</dt><dd><tt>Runs&nbsp;an&nbsp;inspector&nbsp;command&nbsp;that&nbsp;causes&nbsp;a&nbsp;repeatable&nbsp;browser&nbsp;driven&nbsp;scroll.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;x:&nbsp;X&nbsp;coordinate&nbsp;of&nbsp;the&nbsp;start&nbsp;of&nbsp;the&nbsp;gesture&nbsp;in&nbsp;CSS&nbsp;pixels.<br>
+&nbsp;&nbsp;y:&nbsp;Y&nbsp;coordinate&nbsp;of&nbsp;the&nbsp;start&nbsp;of&nbsp;the&nbsp;gesture&nbsp;in&nbsp;CSS&nbsp;pixels.<br>
+&nbsp;&nbsp;xDistance:&nbsp;Distance&nbsp;to&nbsp;scroll&nbsp;along&nbsp;the&nbsp;X&nbsp;axis&nbsp;(positive&nbsp;to&nbsp;scroll&nbsp;left).<br>
+&nbsp;&nbsp;yDistance:&nbsp;Ddistance&nbsp;to&nbsp;scroll&nbsp;along&nbsp;the&nbsp;Y&nbsp;axis&nbsp;(positive&nbsp;to&nbsp;scroll&nbsp;up).<br>
+&nbsp;&nbsp;xOverscroll:&nbsp;Number&nbsp;of&nbsp;additional&nbsp;pixels&nbsp;to&nbsp;scroll&nbsp;back&nbsp;along&nbsp;the&nbsp;X&nbsp;axis.<br>
+&nbsp;&nbsp;xOverscroll:&nbsp;Number&nbsp;of&nbsp;additional&nbsp;pixels&nbsp;to&nbsp;scroll&nbsp;back&nbsp;along&nbsp;the&nbsp;Y&nbsp;axis.<br>
+&nbsp;&nbsp;preventFling:&nbsp;Prevents&nbsp;a&nbsp;fling&nbsp;gesture.<br>
+&nbsp;&nbsp;speed:&nbsp;Swipe&nbsp;speed&nbsp;in&nbsp;pixels&nbsp;per&nbsp;second.<br>
+&nbsp;&nbsp;gestureSourceType:&nbsp;Which&nbsp;type&nbsp;of&nbsp;input&nbsp;events&nbsp;to&nbsp;be&nbsp;generated.<br>
+&nbsp;&nbsp;repeatCount:&nbsp;Number&nbsp;of&nbsp;additional&nbsp;repeats&nbsp;beyond&nbsp;the&nbsp;first&nbsp;scroll.<br>
+&nbsp;&nbsp;repeatDelayMs:&nbsp;Number&nbsp;of&nbsp;milliseconds&nbsp;delay&nbsp;between&nbsp;each&nbsp;repeat.<br>
+&nbsp;&nbsp;interactionMarkerName:&nbsp;The&nbsp;name&nbsp;of&nbsp;the&nbsp;interaction&nbsp;markers&nbsp;to&nbsp;generate.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.TimeoutException<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException</tt></dd></dl>
+
+<dl><dt><a name="Tab-WaitForDocumentReadyStateToBeComplete"><strong>WaitForDocumentReadyStateToBeComplete</strong></a>(self, timeout<font color="#909090">=90</font>)</dt><dd><tt>Waits&nbsp;for&nbsp;the&nbsp;document&nbsp;to&nbsp;finish&nbsp;loading.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.Error:&nbsp;See&nbsp;<a href="#Tab-WaitForJavaScriptExpression">WaitForJavaScriptExpression</a>()&nbsp;for&nbsp;a&nbsp;detailed&nbsp;list<br>
+&nbsp;&nbsp;of&nbsp;possible&nbsp;exceptions.</tt></dd></dl>
+
+<dl><dt><a name="Tab-WaitForDocumentReadyStateToBeInteractiveOrBetter"><strong>WaitForDocumentReadyStateToBeInteractiveOrBetter</strong></a>(self, timeout<font color="#909090">=90</font>)</dt><dd><tt>Waits&nbsp;for&nbsp;the&nbsp;document&nbsp;to&nbsp;be&nbsp;interactive.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.Error:&nbsp;See&nbsp;<a href="#Tab-WaitForJavaScriptExpression">WaitForJavaScriptExpression</a>()&nbsp;for&nbsp;a&nbsp;detailed&nbsp;list<br>
+&nbsp;&nbsp;of&nbsp;possible&nbsp;exceptions.</tt></dd></dl>
+
+<dl><dt><a name="Tab-WaitForJavaScriptExpression"><strong>WaitForJavaScriptExpression</strong></a>(self, expr, timeout, dump_page_state_on_timeout<font color="#909090">=True</font>)</dt><dd><tt>Waits&nbsp;for&nbsp;the&nbsp;given&nbsp;JavaScript&nbsp;expression&nbsp;to&nbsp;be&nbsp;True.<br>
+&nbsp;<br>
+This&nbsp;method&nbsp;is&nbsp;robust&nbsp;against&nbsp;any&nbsp;given&nbsp;Evaluation&nbsp;timing&nbsp;out.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;expr:&nbsp;The&nbsp;expression&nbsp;to&nbsp;evaluate.<br>
+&nbsp;&nbsp;timeout:&nbsp;The&nbsp;number&nbsp;of&nbsp;seconds&nbsp;to&nbsp;wait&nbsp;for&nbsp;the&nbsp;expression&nbsp;to&nbsp;be&nbsp;True.<br>
+&nbsp;&nbsp;dump_page_state_on_timeout:&nbsp;Whether&nbsp;to&nbsp;provide&nbsp;additional&nbsp;information&nbsp;on<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;the&nbsp;page&nbsp;state&nbsp;if&nbsp;a&nbsp;TimeoutException&nbsp;is&nbsp;thrown.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.TimeoutException:&nbsp;On&nbsp;a&nbsp;timeout.<br>
+&nbsp;&nbsp;exceptions.Error:&nbsp;See&nbsp;<a href="#Tab-EvaluateJavaScript">EvaluateJavaScript</a>()&nbsp;for&nbsp;a&nbsp;detailed&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;possible&nbsp;exceptions.</tt></dd></dl>
+
+<dl><dt><a name="Tab-WaitForNavigate"><strong>WaitForNavigate</strong></a>(self, timeout<font color="#909090">=90</font>)</dt><dd><tt>Waits&nbsp;for&nbsp;the&nbsp;navigation&nbsp;to&nbsp;complete.<br>
+&nbsp;<br>
+The&nbsp;current&nbsp;page&nbsp;is&nbsp;expect&nbsp;to&nbsp;be&nbsp;in&nbsp;a&nbsp;navigation.<br>
+This&nbsp;function&nbsp;returns&nbsp;when&nbsp;the&nbsp;navigation&nbsp;is&nbsp;complete&nbsp;or&nbsp;when<br>
+the&nbsp;timeout&nbsp;has&nbsp;been&nbsp;exceeded.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.TimeoutException<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.browser.web_contents.html#WebContents">telemetry.internal.browser.web_contents.WebContents</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>id</strong></dt>
+<dd><tt>Return&nbsp;the&nbsp;unique&nbsp;id&nbsp;string&nbsp;for&nbsp;this&nbsp;tab&nbsp;object.</tt></dd>
+</dl>
+<dl><dt><strong>message_output_stream</strong></dt>
+</dl>
+<dl><dt><strong>timeline_model</strong></dt>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>DEFAULT_TAB_TIMEOUT</strong> = 60</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.tab_list.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.tab_list.html
new file mode 100644
index 0000000..cccfea5
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.tab_list.html
@@ -0,0 +1,64 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.browser.tab_list</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.browser.html"><font color="#ffffff">browser</font></a>.tab_list</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/browser/tab_list.py">telemetry/internal/browser/tab_list.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2012&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.browser.tab_list.html#TabList">TabList</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TabList">class <strong>TabList</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>#&nbsp;Copyright&nbsp;2012&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="TabList-GetTabById"><strong>GetTabById</strong></a>(self, identifier)</dt><dd><tt>The&nbsp;identifier&nbsp;of&nbsp;a&nbsp;tab&nbsp;can&nbsp;be&nbsp;accessed&nbsp;with&nbsp;tab.id.</tt></dd></dl>
+
+<dl><dt><a name="TabList-New"><strong>New</strong></a>(self, timeout<font color="#909090">=300</font>)</dt></dl>
+
+<dl><dt><a name="TabList-__getitem__"><strong>__getitem__</strong></a>(self, index)</dt></dl>
+
+<dl><dt><a name="TabList-__init__"><strong>__init__</strong></a>(self, tab_list_backend)</dt></dl>
+
+<dl><dt><a name="TabList-__iter__"><strong>__iter__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TabList-__len__"><strong>__len__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.user_agent.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.user_agent.html
new file mode 100644
index 0000000..cdc875e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.user_agent.html
@@ -0,0 +1,34 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.browser.user_agent</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.browser.html"><font color="#ffffff">browser</font></a>.user_agent</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/browser/user_agent.py">telemetry/internal/browser/user_agent.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2012&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-GetChromeUserAgentArgumentFromType"><strong>GetChromeUserAgentArgumentFromType</strong></a>(user_agent_type)</dt><dd><tt>Returns&nbsp;a&nbsp;chrome&nbsp;user&nbsp;agent&nbsp;based&nbsp;on&nbsp;a&nbsp;user&nbsp;agent&nbsp;type.<br>
+This&nbsp;is&nbsp;derived&nbsp;from:<br>
+https://developers.google.com/chrome/mobile/docs/user-agent</tt></dd></dl>
+</td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>UA_TYPE_MAPPING</strong> = {'desktop': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) A...TML, like Gecko) Chrome/40.0.2194.2 Safari/537.36', 'mobile': 'Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus ...ke Gecko) Chrome/40.0.2194.2 Mobile Safari/535.36', 'tablet': 'Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus ...TML, like Gecko) Chrome/40.0.2194.2 Safari/535.36', 'tablet_10_inch': 'Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus ...TML, like Gecko) Chrome/40.0.2194.2 Safari/535.36'}</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.web_contents.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.web_contents.html
new file mode 100644
index 0000000..0a7a99e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.browser.web_contents.html
@@ -0,0 +1,238 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.browser.web_contents</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.browser.html"><font color="#ffffff">browser</font></a>.web_contents</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/browser/web_contents.py">telemetry/internal/browser/web_contents.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2012&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+</td><td width="25%" valign=top><a href="os.html">os</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.util.html">telemetry.core.util</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.browser.web_contents.html#WebContents">WebContents</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="WebContents">class <strong>WebContents</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Represents&nbsp;web&nbsp;contents&nbsp;in&nbsp;the&nbsp;browser<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="WebContents-CloseConnections"><strong>CloseConnections</strong></a>(self)</dt><dd><tt>Closes&nbsp;all&nbsp;TCP&nbsp;sockets&nbsp;held&nbsp;open&nbsp;by&nbsp;the&nbsp;browser.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException&nbsp;if&nbsp;the&nbsp;tab&nbsp;is&nbsp;not&nbsp;alive.</tt></dd></dl>
+
+<dl><dt><a name="WebContents-EnableAllContexts"><strong>EnableAllContexts</strong></a>(self)</dt><dd><tt>Enable&nbsp;all&nbsp;contexts&nbsp;in&nbsp;a&nbsp;page.&nbsp;Returns&nbsp;the&nbsp;number&nbsp;of&nbsp;available&nbsp;contexts.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.WebSocketDisconnected<br>
+&nbsp;&nbsp;exceptions.TimeoutException<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException</tt></dd></dl>
+
+<dl><dt><a name="WebContents-EvaluateJavaScript"><strong>EvaluateJavaScript</strong></a>(self, expr, timeout<font color="#909090">=90</font>)</dt><dd><tt>Evalutes&nbsp;expr&nbsp;in&nbsp;JavaScript&nbsp;and&nbsp;returns&nbsp;the&nbsp;JSONized&nbsp;result.<br>
+&nbsp;<br>
+Consider&nbsp;using&nbsp;ExecuteJavaScript&nbsp;for&nbsp;cases&nbsp;where&nbsp;the&nbsp;result&nbsp;of&nbsp;the<br>
+expression&nbsp;is&nbsp;not&nbsp;needed.<br>
+&nbsp;<br>
+If&nbsp;evaluation&nbsp;throws&nbsp;in&nbsp;JavaScript,&nbsp;a&nbsp;Python&nbsp;EvaluateException&nbsp;will<br>
+be&nbsp;raised.<br>
+&nbsp;<br>
+If&nbsp;the&nbsp;result&nbsp;of&nbsp;the&nbsp;evaluation&nbsp;cannot&nbsp;be&nbsp;JSONized,&nbsp;then&nbsp;an<br>
+EvaluationException&nbsp;will&nbsp;be&nbsp;raised.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.Error:&nbsp;See&nbsp;<a href="#WebContents-EvaluateJavaScriptInContext">EvaluateJavaScriptInContext</a>()&nbsp;for&nbsp;a&nbsp;detailed&nbsp;list<br>
+&nbsp;&nbsp;of&nbsp;possible&nbsp;exceptions.</tt></dd></dl>
+
+<dl><dt><a name="WebContents-EvaluateJavaScriptInContext"><strong>EvaluateJavaScriptInContext</strong></a>(self, expr, context_id, timeout<font color="#909090">=90</font>)</dt><dd><tt>Similar&nbsp;to&nbsp;ExecuteJavaScript,&nbsp;except&nbsp;context_id&nbsp;can&nbsp;refer&nbsp;to&nbsp;an&nbsp;iframe.<br>
+The&nbsp;main&nbsp;page&nbsp;has&nbsp;context_id=1,&nbsp;the&nbsp;first&nbsp;iframe&nbsp;context_id=2,&nbsp;etc.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.EvaluateException<br>
+&nbsp;&nbsp;exceptions.WebSocketDisconnected<br>
+&nbsp;&nbsp;exceptions.TimeoutException<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException</tt></dd></dl>
+
+<dl><dt><a name="WebContents-ExecuteJavaScript"><strong>ExecuteJavaScript</strong></a>(self, statement, timeout<font color="#909090">=90</font>)</dt><dd><tt>Executes&nbsp;statement&nbsp;in&nbsp;JavaScript.&nbsp;Does&nbsp;not&nbsp;return&nbsp;the&nbsp;result.<br>
+&nbsp;<br>
+If&nbsp;the&nbsp;statement&nbsp;failed&nbsp;to&nbsp;evaluate,&nbsp;EvaluateException&nbsp;will&nbsp;be&nbsp;raised.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.Error:&nbsp;See&nbsp;<a href="#WebContents-ExecuteJavaScriptInContext">ExecuteJavaScriptInContext</a>()&nbsp;for&nbsp;a&nbsp;detailed&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;possible&nbsp;exceptions.</tt></dd></dl>
+
+<dl><dt><a name="WebContents-ExecuteJavaScriptInContext"><strong>ExecuteJavaScriptInContext</strong></a>(self, expr, context_id, timeout<font color="#909090">=90</font>)</dt><dd><tt>Similar&nbsp;to&nbsp;ExecuteJavaScript,&nbsp;except&nbsp;context_id&nbsp;can&nbsp;refer&nbsp;to&nbsp;an&nbsp;iframe.<br>
+The&nbsp;main&nbsp;page&nbsp;has&nbsp;context_id=1,&nbsp;the&nbsp;first&nbsp;iframe&nbsp;context_id=2,&nbsp;etc.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.EvaluateException<br>
+&nbsp;&nbsp;exceptions.WebSocketDisconnected<br>
+&nbsp;&nbsp;exceptions.TimeoutException<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException</tt></dd></dl>
+
+<dl><dt><a name="WebContents-GetUrl"><strong>GetUrl</strong></a>(self)</dt><dd><tt>Returns&nbsp;the&nbsp;URL&nbsp;to&nbsp;which&nbsp;the&nbsp;<a href="#WebContents">WebContents</a>&nbsp;is&nbsp;connected.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.Error:&nbsp;If&nbsp;there&nbsp;is&nbsp;an&nbsp;error&nbsp;in&nbsp;inspector&nbsp;backend&nbsp;connection.</tt></dd></dl>
+
+<dl><dt><a name="WebContents-GetWebviewContexts"><strong>GetWebviewContexts</strong></a>(self)</dt><dd><tt>Returns&nbsp;a&nbsp;list&nbsp;of&nbsp;webview&nbsp;contexts&nbsp;within&nbsp;the&nbsp;current&nbsp;inspector&nbsp;backend.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;A&nbsp;list&nbsp;of&nbsp;<a href="#WebContents">WebContents</a>&nbsp;objects&nbsp;representing&nbsp;the&nbsp;webview&nbsp;contexts.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.Error:&nbsp;If&nbsp;there&nbsp;is&nbsp;an&nbsp;error&nbsp;in&nbsp;inspector&nbsp;backend&nbsp;connection.</tt></dd></dl>
+
+<dl><dt><a name="WebContents-HasReachedQuiescence"><strong>HasReachedQuiescence</strong></a>(self)</dt><dd><tt>Determine&nbsp;whether&nbsp;the&nbsp;page&nbsp;has&nbsp;reached&nbsp;quiescence&nbsp;after&nbsp;loading.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;True&nbsp;if&nbsp;2&nbsp;seconds&nbsp;have&nbsp;passed&nbsp;since&nbsp;last&nbsp;resource&nbsp;received,&nbsp;false<br>
+&nbsp;&nbsp;otherwise.<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.Error:&nbsp;See&nbsp;<a href="#WebContents-EvaluateJavaScript">EvaluateJavaScript</a>()&nbsp;for&nbsp;a&nbsp;detailed&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;possible&nbsp;exceptions.</tt></dd></dl>
+
+<dl><dt><a name="WebContents-IsAlive"><strong>IsAlive</strong></a>(self)</dt><dd><tt>Whether&nbsp;the&nbsp;<a href="#WebContents">WebContents</a>&nbsp;is&nbsp;still&nbsp;operating&nbsp;normally.<br>
+&nbsp;<br>
+Since&nbsp;<a href="#WebContents">WebContents</a>&nbsp;function&nbsp;asynchronously,&nbsp;this&nbsp;method&nbsp;does&nbsp;not&nbsp;guarantee<br>
+that&nbsp;the&nbsp;<a href="#WebContents">WebContents</a>&nbsp;will&nbsp;still&nbsp;be&nbsp;alive&nbsp;at&nbsp;any&nbsp;point&nbsp;in&nbsp;the&nbsp;future.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;A&nbsp;boolean&nbsp;indicating&nbsp;whether&nbsp;the&nbsp;<a href="#WebContents">WebContents</a>&nbsp;is&nbsp;opearting&nbsp;normally.</tt></dd></dl>
+
+<dl><dt><a name="WebContents-Navigate"><strong>Navigate</strong></a>(self, url, script_to_evaluate_on_commit<font color="#909090">=None</font>, timeout<font color="#909090">=90</font>)</dt><dd><tt>Navigates&nbsp;to&nbsp;url.<br>
+&nbsp;<br>
+If&nbsp;|script_to_evaluate_on_commit|&nbsp;is&nbsp;given,&nbsp;the&nbsp;script&nbsp;source&nbsp;string&nbsp;will&nbsp;be<br>
+evaluated&nbsp;when&nbsp;the&nbsp;navigation&nbsp;is&nbsp;committed.&nbsp;This&nbsp;is&nbsp;after&nbsp;the&nbsp;context&nbsp;of<br>
+the&nbsp;page&nbsp;exists,&nbsp;but&nbsp;before&nbsp;any&nbsp;script&nbsp;on&nbsp;the&nbsp;page&nbsp;itself&nbsp;has&nbsp;executed.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.TimeoutException<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException</tt></dd></dl>
+
+<dl><dt><a name="WebContents-StartTimelineRecording"><strong>StartTimelineRecording</strong></a>(self)</dt><dd><tt>Starts&nbsp;timeline&nbsp;recording.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.TimeoutException<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException</tt></dd></dl>
+
+<dl><dt><a name="WebContents-StopTimelineRecording"><strong>StopTimelineRecording</strong></a>(self)</dt><dd><tt>Stops&nbsp;timeline&nbsp;recording.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.TimeoutException<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException</tt></dd></dl>
+
+<dl><dt><a name="WebContents-SynthesizeScrollGesture"><strong>SynthesizeScrollGesture</strong></a>(self, x<font color="#909090">=100</font>, y<font color="#909090">=800</font>, xDistance<font color="#909090">=0</font>, yDistance<font color="#909090">=-500</font>, xOverscroll<font color="#909090">=None</font>, yOverscroll<font color="#909090">=None</font>, preventFling<font color="#909090">=True</font>, speed<font color="#909090">=None</font>, gestureSourceType<font color="#909090">=None</font>, repeatCount<font color="#909090">=None</font>, repeatDelayMs<font color="#909090">=None</font>, interactionMarkerName<font color="#909090">=None</font>)</dt><dd><tt>Runs&nbsp;an&nbsp;inspector&nbsp;command&nbsp;that&nbsp;causes&nbsp;a&nbsp;repeatable&nbsp;browser&nbsp;driven&nbsp;scroll.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;x:&nbsp;X&nbsp;coordinate&nbsp;of&nbsp;the&nbsp;start&nbsp;of&nbsp;the&nbsp;gesture&nbsp;in&nbsp;CSS&nbsp;pixels.<br>
+&nbsp;&nbsp;y:&nbsp;Y&nbsp;coordinate&nbsp;of&nbsp;the&nbsp;start&nbsp;of&nbsp;the&nbsp;gesture&nbsp;in&nbsp;CSS&nbsp;pixels.<br>
+&nbsp;&nbsp;xDistance:&nbsp;Distance&nbsp;to&nbsp;scroll&nbsp;along&nbsp;the&nbsp;X&nbsp;axis&nbsp;(positive&nbsp;to&nbsp;scroll&nbsp;left).<br>
+&nbsp;&nbsp;yDistance:&nbsp;Ddistance&nbsp;to&nbsp;scroll&nbsp;along&nbsp;the&nbsp;Y&nbsp;axis&nbsp;(positive&nbsp;to&nbsp;scroll&nbsp;up).<br>
+&nbsp;&nbsp;xOverscroll:&nbsp;Number&nbsp;of&nbsp;additional&nbsp;pixels&nbsp;to&nbsp;scroll&nbsp;back&nbsp;along&nbsp;the&nbsp;X&nbsp;axis.<br>
+&nbsp;&nbsp;xOverscroll:&nbsp;Number&nbsp;of&nbsp;additional&nbsp;pixels&nbsp;to&nbsp;scroll&nbsp;back&nbsp;along&nbsp;the&nbsp;Y&nbsp;axis.<br>
+&nbsp;&nbsp;preventFling:&nbsp;Prevents&nbsp;a&nbsp;fling&nbsp;gesture.<br>
+&nbsp;&nbsp;speed:&nbsp;Swipe&nbsp;speed&nbsp;in&nbsp;pixels&nbsp;per&nbsp;second.<br>
+&nbsp;&nbsp;gestureSourceType:&nbsp;Which&nbsp;type&nbsp;of&nbsp;input&nbsp;events&nbsp;to&nbsp;be&nbsp;generated.<br>
+&nbsp;&nbsp;repeatCount:&nbsp;Number&nbsp;of&nbsp;additional&nbsp;repeats&nbsp;beyond&nbsp;the&nbsp;first&nbsp;scroll.<br>
+&nbsp;&nbsp;repeatDelayMs:&nbsp;Number&nbsp;of&nbsp;milliseconds&nbsp;delay&nbsp;between&nbsp;each&nbsp;repeat.<br>
+&nbsp;&nbsp;interactionMarkerName:&nbsp;The&nbsp;name&nbsp;of&nbsp;the&nbsp;interaction&nbsp;markers&nbsp;to&nbsp;generate.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.TimeoutException<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException</tt></dd></dl>
+
+<dl><dt><a name="WebContents-WaitForDocumentReadyStateToBeComplete"><strong>WaitForDocumentReadyStateToBeComplete</strong></a>(self, timeout<font color="#909090">=90</font>)</dt><dd><tt>Waits&nbsp;for&nbsp;the&nbsp;document&nbsp;to&nbsp;finish&nbsp;loading.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.Error:&nbsp;See&nbsp;<a href="#WebContents-WaitForJavaScriptExpression">WaitForJavaScriptExpression</a>()&nbsp;for&nbsp;a&nbsp;detailed&nbsp;list<br>
+&nbsp;&nbsp;of&nbsp;possible&nbsp;exceptions.</tt></dd></dl>
+
+<dl><dt><a name="WebContents-WaitForDocumentReadyStateToBeInteractiveOrBetter"><strong>WaitForDocumentReadyStateToBeInteractiveOrBetter</strong></a>(self, timeout<font color="#909090">=90</font>)</dt><dd><tt>Waits&nbsp;for&nbsp;the&nbsp;document&nbsp;to&nbsp;be&nbsp;interactive.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.Error:&nbsp;See&nbsp;<a href="#WebContents-WaitForJavaScriptExpression">WaitForJavaScriptExpression</a>()&nbsp;for&nbsp;a&nbsp;detailed&nbsp;list<br>
+&nbsp;&nbsp;of&nbsp;possible&nbsp;exceptions.</tt></dd></dl>
+
+<dl><dt><a name="WebContents-WaitForJavaScriptExpression"><strong>WaitForJavaScriptExpression</strong></a>(self, expr, timeout, dump_page_state_on_timeout<font color="#909090">=True</font>)</dt><dd><tt>Waits&nbsp;for&nbsp;the&nbsp;given&nbsp;JavaScript&nbsp;expression&nbsp;to&nbsp;be&nbsp;True.<br>
+&nbsp;<br>
+This&nbsp;method&nbsp;is&nbsp;robust&nbsp;against&nbsp;any&nbsp;given&nbsp;Evaluation&nbsp;timing&nbsp;out.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;expr:&nbsp;The&nbsp;expression&nbsp;to&nbsp;evaluate.<br>
+&nbsp;&nbsp;timeout:&nbsp;The&nbsp;number&nbsp;of&nbsp;seconds&nbsp;to&nbsp;wait&nbsp;for&nbsp;the&nbsp;expression&nbsp;to&nbsp;be&nbsp;True.<br>
+&nbsp;&nbsp;dump_page_state_on_timeout:&nbsp;Whether&nbsp;to&nbsp;provide&nbsp;additional&nbsp;information&nbsp;on<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;the&nbsp;page&nbsp;state&nbsp;if&nbsp;a&nbsp;TimeoutException&nbsp;is&nbsp;thrown.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.TimeoutException:&nbsp;On&nbsp;a&nbsp;timeout.<br>
+&nbsp;&nbsp;exceptions.Error:&nbsp;See&nbsp;<a href="#WebContents-EvaluateJavaScript">EvaluateJavaScript</a>()&nbsp;for&nbsp;a&nbsp;detailed&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;possible&nbsp;exceptions.</tt></dd></dl>
+
+<dl><dt><a name="WebContents-WaitForNavigate"><strong>WaitForNavigate</strong></a>(self, timeout<font color="#909090">=90</font>)</dt><dd><tt>Waits&nbsp;for&nbsp;the&nbsp;navigation&nbsp;to&nbsp;complete.<br>
+&nbsp;<br>
+The&nbsp;current&nbsp;page&nbsp;is&nbsp;expect&nbsp;to&nbsp;be&nbsp;in&nbsp;a&nbsp;navigation.<br>
+This&nbsp;function&nbsp;returns&nbsp;when&nbsp;the&nbsp;navigation&nbsp;is&nbsp;complete&nbsp;or&nbsp;when<br>
+the&nbsp;timeout&nbsp;has&nbsp;been&nbsp;exceeded.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;exceptions.TimeoutException<br>
+&nbsp;&nbsp;exceptions.DevtoolsTargetCrashException</tt></dd></dl>
+
+<dl><dt><a name="WebContents-__init__"><strong>__init__</strong></a>(self, inspector_backend)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>id</strong></dt>
+<dd><tt>Return&nbsp;the&nbsp;unique&nbsp;id&nbsp;string&nbsp;for&nbsp;this&nbsp;tab&nbsp;object.</tt></dd>
+</dl>
+<dl><dt><strong>message_output_stream</strong></dt>
+</dl>
+<dl><dt><strong>timeline_model</strong></dt>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>DEFAULT_WEB_CONTENTS_TIMEOUT</strong> = 90</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.forwarders.android_forwarder.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.forwarders.android_forwarder.html
new file mode 100644
index 0000000..eff075e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.forwarders.android_forwarder.html
@@ -0,0 +1,202 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.forwarders.android_forwarder</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.forwarders.html"><font color="#ffffff">forwarders</font></a>.android_forwarder</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/forwarders/android_forwarder.py">telemetry/internal/forwarders/android_forwarder.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.platform.android_device.html">telemetry.internal.platform.android_device</a><br>
+<a href="atexit.html">atexit</a><br>
+<a href="telemetry.internal.util.binary_manager.html">telemetry.internal.util.binary_manager</a><br>
+<a href="devil.android.device_errors.html">devil.android.device_errors</a><br>
+</td><td width="25%" valign=top><a href="devil.android.device_utils.html">devil.android.device_utils</a><br>
+<a href="pylib.forwarder.html">pylib.forwarder</a><br>
+<a href="telemetry.internal.forwarders.html">telemetry.internal.forwarders</a><br>
+<a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top><a href="os.html">os</a><br>
+<a href="telemetry.core.platform.html">telemetry.core.platform</a><br>
+<a href="re.html">re</a><br>
+<a href="socket.html">socket</a><br>
+</td><td width="25%" valign=top><a href="struct.html">struct</a><br>
+<a href="subprocess.html">subprocess</a><br>
+<a href="telemetry.core.util.html">telemetry.core.util</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.forwarders.android_forwarder.html#AndroidRndisConfigurator">AndroidRndisConfigurator</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.forwarders.html#Forwarder">telemetry.internal.forwarders.Forwarder</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.forwarders.android_forwarder.html#AndroidForwarder">AndroidForwarder</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.internal.forwarders.android_forwarder.html#AndroidRndisForwarder">AndroidRndisForwarder</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.forwarders.html#ForwarderFactory">telemetry.internal.forwarders.ForwarderFactory</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.forwarders.android_forwarder.html#AndroidForwarderFactory">AndroidForwarderFactory</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="AndroidForwarder">class <strong>AndroidForwarder</strong></a>(<a href="telemetry.internal.forwarders.html#Forwarder">telemetry.internal.forwarders.Forwarder</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.forwarders.android_forwarder.html#AndroidForwarder">AndroidForwarder</a></dd>
+<dd><a href="telemetry.internal.forwarders.html#Forwarder">telemetry.internal.forwarders.Forwarder</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="AndroidForwarder-Close"><strong>Close</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidForwarder-__init__"><strong>__init__</strong></a>(self, device, port_pairs)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.forwarders.html#Forwarder">telemetry.internal.forwarders.Forwarder</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>host_ip</strong></dt>
+</dl>
+<dl><dt><strong>host_port</strong></dt>
+</dl>
+<dl><dt><strong>port_pairs</strong></dt>
+</dl>
+<dl><dt><strong>url</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="AndroidForwarderFactory">class <strong>AndroidForwarderFactory</strong></a>(<a href="telemetry.internal.forwarders.html#ForwarderFactory">telemetry.internal.forwarders.ForwarderFactory</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.forwarders.android_forwarder.html#AndroidForwarderFactory">AndroidForwarderFactory</a></dd>
+<dd><a href="telemetry.internal.forwarders.html#ForwarderFactory">telemetry.internal.forwarders.ForwarderFactory</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="AndroidForwarderFactory-Create"><strong>Create</strong></a>(self, port_pairs)</dt></dl>
+
+<dl><dt><a name="AndroidForwarderFactory-__init__"><strong>__init__</strong></a>(self, device, use_rndis)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>does_forwarder_override_dns</strong></dt>
+</dl>
+<dl><dt><strong>host_ip</strong></dt>
+</dl>
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.forwarders.html#ForwarderFactory">telemetry.internal.forwarders.ForwarderFactory</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="AndroidRndisConfigurator">class <strong>AndroidRndisConfigurator</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Configures&nbsp;a&nbsp;linux&nbsp;host&nbsp;to&nbsp;connect&nbsp;to&nbsp;an&nbsp;android&nbsp;device&nbsp;via&nbsp;RNDIS.<br>
+&nbsp;<br>
+Note&nbsp;that&nbsp;we&nbsp;intentionally&nbsp;leave&nbsp;RNDIS&nbsp;running&nbsp;on&nbsp;the&nbsp;device.&nbsp;This&nbsp;is<br>
+because&nbsp;the&nbsp;setup&nbsp;is&nbsp;slow&nbsp;and&nbsp;potentially&nbsp;flaky&nbsp;and&nbsp;leaving&nbsp;it&nbsp;running<br>
+doesn't&nbsp;seem&nbsp;to&nbsp;interfere&nbsp;with&nbsp;any&nbsp;other&nbsp;developer&nbsp;or&nbsp;bot&nbsp;use-cases.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="AndroidRndisConfigurator-OverrideRoutingPolicy"><strong>OverrideRoutingPolicy</strong></a>(self)</dt><dd><tt>Override&nbsp;any&nbsp;routing&nbsp;policy&nbsp;that&nbsp;could&nbsp;prevent<br>
+packets&nbsp;from&nbsp;reaching&nbsp;the&nbsp;rndis&nbsp;interface</tt></dd></dl>
+
+<dl><dt><a name="AndroidRndisConfigurator-RestoreRoutingPolicy"><strong>RestoreRoutingPolicy</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidRndisConfigurator-__init__"><strong>__init__</strong></a>(self, device)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>host_ip</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="AndroidRndisForwarder">class <strong>AndroidRndisForwarder</strong></a>(<a href="telemetry.internal.forwarders.html#Forwarder">telemetry.internal.forwarders.Forwarder</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Forwards&nbsp;traffic&nbsp;using&nbsp;RNDIS.&nbsp;Assumes&nbsp;the&nbsp;device&nbsp;has&nbsp;root&nbsp;access.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.forwarders.android_forwarder.html#AndroidRndisForwarder">AndroidRndisForwarder</a></dd>
+<dd><a href="telemetry.internal.forwarders.html#Forwarder">telemetry.internal.forwarders.Forwarder</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="AndroidRndisForwarder-Close"><strong>Close</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidRndisForwarder-__init__"><strong>__init__</strong></a>(self, device, rndis_configurator, port_pairs)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>host_ip</strong></dt>
+</dl>
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.forwarders.html#Forwarder">telemetry.internal.forwarders.Forwarder</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>host_port</strong></dt>
+</dl>
+<dl><dt><strong>port_pairs</strong></dt>
+</dl>
+<dl><dt><strong>url</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.forwarders.cros_forwarder.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.forwarders.cros_forwarder.html
new file mode 100644
index 0000000..7c7b9ae
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.forwarders.cros_forwarder.html
@@ -0,0 +1,116 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.forwarders.cros_forwarder</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.forwarders.html"><font color="#ffffff">forwarders</font></a>.cros_forwarder</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/forwarders/cros_forwarder.py">telemetry/internal/forwarders/cros_forwarder.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.forwarders.do_nothing_forwarder.html">telemetry.internal.forwarders.do_nothing_forwarder</a><br>
+<a href="telemetry.internal.forwarders.html">telemetry.internal.forwarders</a><br>
+</td><td width="25%" valign=top><a href="logging.html">logging</a><br>
+<a href="subprocess.html">subprocess</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.util.html">telemetry.core.util</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.forwarders.html#Forwarder">telemetry.internal.forwarders.Forwarder</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.forwarders.cros_forwarder.html#CrOsSshForwarder">CrOsSshForwarder</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.forwarders.html#ForwarderFactory">telemetry.internal.forwarders.ForwarderFactory</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.forwarders.cros_forwarder.html#CrOsForwarderFactory">CrOsForwarderFactory</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="CrOsForwarderFactory">class <strong>CrOsForwarderFactory</strong></a>(<a href="telemetry.internal.forwarders.html#ForwarderFactory">telemetry.internal.forwarders.ForwarderFactory</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.forwarders.cros_forwarder.html#CrOsForwarderFactory">CrOsForwarderFactory</a></dd>
+<dd><a href="telemetry.internal.forwarders.html#ForwarderFactory">telemetry.internal.forwarders.ForwarderFactory</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="CrOsForwarderFactory-Create"><strong>Create</strong></a>(self, port_pairs, use_remote_port_forwarding<font color="#909090">=True</font>)</dt><dd><tt>#&nbsp;pylint:&nbsp;disable=arguments-differ</tt></dd></dl>
+
+<dl><dt><a name="CrOsForwarderFactory-__init__"><strong>__init__</strong></a>(self, cri)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.forwarders.html#ForwarderFactory">telemetry.internal.forwarders.ForwarderFactory</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>does_forwarder_override_dns</strong></dt>
+</dl>
+<dl><dt><strong>host_ip</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="CrOsSshForwarder">class <strong>CrOsSshForwarder</strong></a>(<a href="telemetry.internal.forwarders.html#Forwarder">telemetry.internal.forwarders.Forwarder</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.forwarders.cros_forwarder.html#CrOsSshForwarder">CrOsSshForwarder</a></dd>
+<dd><a href="telemetry.internal.forwarders.html#Forwarder">telemetry.internal.forwarders.Forwarder</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="CrOsSshForwarder-Close"><strong>Close</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrOsSshForwarder-__init__"><strong>__init__</strong></a>(self, cri, use_remote_port_forwarding, port_pairs)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>host_port</strong></dt>
+</dl>
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.forwarders.html#Forwarder">telemetry.internal.forwarders.Forwarder</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>host_ip</strong></dt>
+</dl>
+<dl><dt><strong>port_pairs</strong></dt>
+</dl>
+<dl><dt><strong>url</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.forwarders.do_nothing_forwarder.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.forwarders.do_nothing_forwarder.html
new file mode 100644
index 0000000..ad535fc
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.forwarders.do_nothing_forwarder.html
@@ -0,0 +1,316 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.forwarders.do_nothing_forwarder</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.forwarders.html"><font color="#ffffff">forwarders</font></a>.do_nothing_forwarder</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/forwarders/do_nothing_forwarder.py">telemetry/internal/forwarders/do_nothing_forwarder.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="contextlib.html">contextlib</a><br>
+<a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.forwarders.html">telemetry.internal.forwarders</a><br>
+<a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top><a href="socket.html">socket</a><br>
+<a href="telemetry.core.util.html">telemetry.core.util</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="exceptions.html#Exception">exceptions.Exception</a>(<a href="exceptions.html#BaseException">exceptions.BaseException</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.forwarders.do_nothing_forwarder.html#Error">Error</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.forwarders.do_nothing_forwarder.html#ConnectionError">ConnectionError</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.internal.forwarders.do_nothing_forwarder.html#PortsMismatchError">PortsMismatchError</a>
+</font></dt></dl>
+</dd>
+</dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.forwarders.html#Forwarder">telemetry.internal.forwarders.Forwarder</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.forwarders.do_nothing_forwarder.html#DoNothingForwarder">DoNothingForwarder</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.forwarders.html#ForwarderFactory">telemetry.internal.forwarders.ForwarderFactory</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.forwarders.do_nothing_forwarder.html#DoNothingForwarderFactory">DoNothingForwarderFactory</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ConnectionError">class <strong>ConnectionError</strong></a>(<a href="telemetry.internal.forwarders.do_nothing_forwarder.html#Error">Error</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Raised&nbsp;when&nbsp;unable&nbsp;to&nbsp;connect&nbsp;to&nbsp;local&nbsp;TCP&nbsp;ports.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.forwarders.do_nothing_forwarder.html#ConnectionError">ConnectionError</a></dd>
+<dd><a href="telemetry.internal.forwarders.do_nothing_forwarder.html#Error">Error</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.forwarders.do_nothing_forwarder.html#Error">Error</a>:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="ConnectionError-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#ConnectionError-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#ConnectionError-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;object&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="ConnectionError-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#ConnectionError-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="ConnectionError-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#ConnectionError-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="ConnectionError-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#ConnectionError-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="ConnectionError-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#ConnectionError-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="ConnectionError-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="ConnectionError-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#ConnectionError-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="ConnectionError-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#ConnectionError-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="ConnectionError-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="ConnectionError-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#ConnectionError-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="ConnectionError-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="DoNothingForwarder">class <strong>DoNothingForwarder</strong></a>(<a href="telemetry.internal.forwarders.html#Forwarder">telemetry.internal.forwarders.Forwarder</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Check&nbsp;that&nbsp;no&nbsp;forwarding&nbsp;is&nbsp;needed&nbsp;for&nbsp;the&nbsp;given&nbsp;port&nbsp;pairs.<br>
+&nbsp;<br>
+The&nbsp;local&nbsp;and&nbsp;remote&nbsp;ports&nbsp;must&nbsp;be&nbsp;equal.&nbsp;Otherwise,&nbsp;the&nbsp;"do&nbsp;nothing"<br>
+forwarder&nbsp;does&nbsp;not&nbsp;make&nbsp;sense.&nbsp;(Raises&nbsp;<a href="#PortsMismatchError">PortsMismatchError</a>.)<br>
+&nbsp;<br>
+Also,&nbsp;check&nbsp;that&nbsp;all&nbsp;TCP&nbsp;ports&nbsp;support&nbsp;connections.&nbsp;&nbsp;(Raises&nbsp;<a href="#ConnectionError">ConnectionError</a>.)<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.forwarders.do_nothing_forwarder.html#DoNothingForwarder">DoNothingForwarder</a></dd>
+<dd><a href="telemetry.internal.forwarders.html#Forwarder">telemetry.internal.forwarders.Forwarder</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="DoNothingForwarder-__init__"><strong>__init__</strong></a>(self, port_pairs)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.forwarders.html#Forwarder">telemetry.internal.forwarders.Forwarder</a>:<br>
+<dl><dt><a name="DoNothingForwarder-Close"><strong>Close</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.forwarders.html#Forwarder">telemetry.internal.forwarders.Forwarder</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>host_ip</strong></dt>
+</dl>
+<dl><dt><strong>host_port</strong></dt>
+</dl>
+<dl><dt><strong>port_pairs</strong></dt>
+</dl>
+<dl><dt><strong>url</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="DoNothingForwarderFactory">class <strong>DoNothingForwarderFactory</strong></a>(<a href="telemetry.internal.forwarders.html#ForwarderFactory">telemetry.internal.forwarders.ForwarderFactory</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.forwarders.do_nothing_forwarder.html#DoNothingForwarderFactory">DoNothingForwarderFactory</a></dd>
+<dd><a href="telemetry.internal.forwarders.html#ForwarderFactory">telemetry.internal.forwarders.ForwarderFactory</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="DoNothingForwarderFactory-Create"><strong>Create</strong></a>(self, port_pairs)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.forwarders.html#ForwarderFactory">telemetry.internal.forwarders.ForwarderFactory</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>does_forwarder_override_dns</strong></dt>
+</dl>
+<dl><dt><strong>host_ip</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="Error">class <strong>Error</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Base&nbsp;class&nbsp;for&nbsp;exceptions&nbsp;in&nbsp;this&nbsp;module.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.forwarders.do_nothing_forwarder.html#Error">Error</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="Error-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#Error-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#Error-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;object&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="Error-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#Error-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="Error-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#Error-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="Error-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#Error-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="Error-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#Error-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="Error-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="Error-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#Error-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="Error-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#Error-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="Error-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="Error-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#Error-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="Error-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="PortsMismatchError">class <strong>PortsMismatchError</strong></a>(<a href="telemetry.internal.forwarders.do_nothing_forwarder.html#Error">Error</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Raised&nbsp;when&nbsp;local&nbsp;and&nbsp;remote&nbsp;ports&nbsp;are&nbsp;not&nbsp;equal.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.forwarders.do_nothing_forwarder.html#PortsMismatchError">PortsMismatchError</a></dd>
+<dd><a href="telemetry.internal.forwarders.do_nothing_forwarder.html#Error">Error</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.forwarders.do_nothing_forwarder.html#Error">Error</a>:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="PortsMismatchError-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#PortsMismatchError-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#PortsMismatchError-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;object&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="PortsMismatchError-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#PortsMismatchError-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="PortsMismatchError-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#PortsMismatchError-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="PortsMismatchError-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#PortsMismatchError-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="PortsMismatchError-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#PortsMismatchError-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="PortsMismatchError-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="PortsMismatchError-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#PortsMismatchError-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="PortsMismatchError-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#PortsMismatchError-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="PortsMismatchError-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="PortsMismatchError-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#PortsMismatchError-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="PortsMismatchError-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.forwarders.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.forwarders.html
new file mode 100644
index 0000000..bf3f7d5
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.forwarders.html
@@ -0,0 +1,293 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: package telemetry.internal.forwarders</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.forwarders</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/forwarders/__init__.py">telemetry/internal/forwarders/__init__.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Package Contents</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.forwarders.android_forwarder.html">android_forwarder</a><br>
+<a href="telemetry.internal.forwarders.cros_forwarder.html">cros_forwarder</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.forwarders.cros_forwarder_unittest.html">cros_forwarder_unittest</a><br>
+<a href="telemetry.internal.forwarders.do_nothing_forwarder.html">do_nothing_forwarder</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.forwarders.do_nothing_forwarder_unittest.html">do_nothing_forwarder_unittest</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.forwarders.html#Forwarder">Forwarder</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.internal.forwarders.html#ForwarderFactory">ForwarderFactory</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#tuple">__builtin__.tuple</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.forwarders.html#PortPair">PortPair</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.internal.forwarders.html#PortPairs">PortPairs</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="Forwarder">class <strong>Forwarder</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="Forwarder-Close"><strong>Close</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="Forwarder-__init__"><strong>__init__</strong></a>(self, port_pairs)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>host_ip</strong></dt>
+</dl>
+<dl><dt><strong>host_port</strong></dt>
+</dl>
+<dl><dt><strong>port_pairs</strong></dt>
+</dl>
+<dl><dt><strong>url</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ForwarderFactory">class <strong>ForwarderFactory</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="ForwarderFactory-Create"><strong>Create</strong></a>(self, port_pairs)</dt><dd><tt>Creates&nbsp;a&nbsp;forwarder&nbsp;that&nbsp;maps&nbsp;remote&nbsp;(device)&nbsp;&lt;-&gt;&nbsp;local&nbsp;(host)&nbsp;ports.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;port_pairs:&nbsp;A&nbsp;<a href="#PortPairs">PortPairs</a>&nbsp;instance&nbsp;that&nbsp;consists&nbsp;of&nbsp;a&nbsp;<a href="#PortPair">PortPair</a>&nbsp;mapping<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;each&nbsp;protocol.&nbsp;http&nbsp;is&nbsp;required.&nbsp;https&nbsp;and&nbsp;dns&nbsp;may&nbsp;be&nbsp;None.</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>does_forwarder_override_dns</strong></dt>
+</dl>
+<dl><dt><strong>host_ip</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="PortPair">class <strong>PortPair</strong></a>(<a href="__builtin__.html#tuple">__builtin__.tuple</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt><a href="#PortPair">PortPair</a>(local_port,&nbsp;remote_port)<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.forwarders.html#PortPair">PortPair</a></dd>
+<dd><a href="__builtin__.html#tuple">__builtin__.tuple</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="PortPair-__getnewargs__"><strong>__getnewargs__</strong></a>(self)</dt><dd><tt>Return&nbsp;self&nbsp;as&nbsp;a&nbsp;plain&nbsp;<a href="__builtin__.html#tuple">tuple</a>.&nbsp;&nbsp;Used&nbsp;by&nbsp;copy&nbsp;and&nbsp;pickle.</tt></dd></dl>
+
+<dl><dt><a name="PortPair-__getstate__"><strong>__getstate__</strong></a>(self)</dt><dd><tt>Exclude&nbsp;the&nbsp;OrderedDict&nbsp;from&nbsp;pickling</tt></dd></dl>
+
+<dl><dt><a name="PortPair-__repr__"><strong>__repr__</strong></a>(self)</dt><dd><tt>Return&nbsp;a&nbsp;nicely&nbsp;formatted&nbsp;representation&nbsp;string</tt></dd></dl>
+
+<dl><dt><a name="PortPair-_asdict"><strong>_asdict</strong></a>(self)</dt><dd><tt>Return&nbsp;a&nbsp;new&nbsp;OrderedDict&nbsp;which&nbsp;maps&nbsp;field&nbsp;names&nbsp;to&nbsp;their&nbsp;values</tt></dd></dl>
+
+<dl><dt><a name="PortPair-_replace"><strong>_replace</strong></a>(_self, **kwds)</dt><dd><tt>Return&nbsp;a&nbsp;new&nbsp;<a href="#PortPair">PortPair</a>&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;replacing&nbsp;specified&nbsp;fields&nbsp;with&nbsp;new&nbsp;values</tt></dd></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="PortPair-_make"><strong>_make</strong></a>(cls, iterable, new<font color="#909090">=&lt;built-in method __new__ of type object&gt;</font>, len<font color="#909090">=&lt;built-in function len&gt;</font>)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Make&nbsp;a&nbsp;new&nbsp;<a href="#PortPair">PortPair</a>&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;from&nbsp;a&nbsp;sequence&nbsp;or&nbsp;iterable</tt></dd></dl>
+
+<hr>
+Static methods defined here:<br>
+<dl><dt><a name="PortPair-__new__"><strong>__new__</strong></a>(_cls, local_port, remote_port)</dt><dd><tt>Create&nbsp;new&nbsp;instance&nbsp;of&nbsp;<a href="#PortPair">PortPair</a>(local_port,&nbsp;remote_port)</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>Return&nbsp;a&nbsp;new&nbsp;OrderedDict&nbsp;which&nbsp;maps&nbsp;field&nbsp;names&nbsp;to&nbsp;their&nbsp;values</tt></dd>
+</dl>
+<dl><dt><strong>local_port</strong></dt>
+<dd><tt>Alias&nbsp;for&nbsp;field&nbsp;number&nbsp;0</tt></dd>
+</dl>
+<dl><dt><strong>remote_port</strong></dt>
+<dd><tt>Alias&nbsp;for&nbsp;field&nbsp;number&nbsp;1</tt></dd>
+</dl>
+<hr>
+Data and other attributes defined here:<br>
+<dl><dt><strong>_fields</strong> = ('local_port', 'remote_port')</dl>
+
+<hr>
+Methods inherited from <a href="__builtin__.html#tuple">__builtin__.tuple</a>:<br>
+<dl><dt><a name="PortPair-__add__"><strong>__add__</strong></a>(...)</dt><dd><tt>x.<a href="#PortPair-__add__">__add__</a>(y)&nbsp;&lt;==&gt;&nbsp;x+y</tt></dd></dl>
+
+<dl><dt><a name="PortPair-__contains__"><strong>__contains__</strong></a>(...)</dt><dd><tt>x.<a href="#PortPair-__contains__">__contains__</a>(y)&nbsp;&lt;==&gt;&nbsp;y&nbsp;in&nbsp;x</tt></dd></dl>
+
+<dl><dt><a name="PortPair-__eq__"><strong>__eq__</strong></a>(...)</dt><dd><tt>x.<a href="#PortPair-__eq__">__eq__</a>(y)&nbsp;&lt;==&gt;&nbsp;x==y</tt></dd></dl>
+
+<dl><dt><a name="PortPair-__ge__"><strong>__ge__</strong></a>(...)</dt><dd><tt>x.<a href="#PortPair-__ge__">__ge__</a>(y)&nbsp;&lt;==&gt;&nbsp;x&gt;=y</tt></dd></dl>
+
+<dl><dt><a name="PortPair-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#PortPair-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="PortPair-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#PortPair-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="PortPair-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#PortPair-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="PortPair-__gt__"><strong>__gt__</strong></a>(...)</dt><dd><tt>x.<a href="#PortPair-__gt__">__gt__</a>(y)&nbsp;&lt;==&gt;&nbsp;x&gt;y</tt></dd></dl>
+
+<dl><dt><a name="PortPair-__hash__"><strong>__hash__</strong></a>(...)</dt><dd><tt>x.<a href="#PortPair-__hash__">__hash__</a>()&nbsp;&lt;==&gt;&nbsp;hash(x)</tt></dd></dl>
+
+<dl><dt><a name="PortPair-__iter__"><strong>__iter__</strong></a>(...)</dt><dd><tt>x.<a href="#PortPair-__iter__">__iter__</a>()&nbsp;&lt;==&gt;&nbsp;iter(x)</tt></dd></dl>
+
+<dl><dt><a name="PortPair-__le__"><strong>__le__</strong></a>(...)</dt><dd><tt>x.<a href="#PortPair-__le__">__le__</a>(y)&nbsp;&lt;==&gt;&nbsp;x&lt;=y</tt></dd></dl>
+
+<dl><dt><a name="PortPair-__len__"><strong>__len__</strong></a>(...)</dt><dd><tt>x.<a href="#PortPair-__len__">__len__</a>()&nbsp;&lt;==&gt;&nbsp;len(x)</tt></dd></dl>
+
+<dl><dt><a name="PortPair-__lt__"><strong>__lt__</strong></a>(...)</dt><dd><tt>x.<a href="#PortPair-__lt__">__lt__</a>(y)&nbsp;&lt;==&gt;&nbsp;x&lt;y</tt></dd></dl>
+
+<dl><dt><a name="PortPair-__mul__"><strong>__mul__</strong></a>(...)</dt><dd><tt>x.<a href="#PortPair-__mul__">__mul__</a>(n)&nbsp;&lt;==&gt;&nbsp;x*n</tt></dd></dl>
+
+<dl><dt><a name="PortPair-__ne__"><strong>__ne__</strong></a>(...)</dt><dd><tt>x.<a href="#PortPair-__ne__">__ne__</a>(y)&nbsp;&lt;==&gt;&nbsp;x!=y</tt></dd></dl>
+
+<dl><dt><a name="PortPair-__rmul__"><strong>__rmul__</strong></a>(...)</dt><dd><tt>x.<a href="#PortPair-__rmul__">__rmul__</a>(n)&nbsp;&lt;==&gt;&nbsp;n*x</tt></dd></dl>
+
+<dl><dt><a name="PortPair-__sizeof__"><strong>__sizeof__</strong></a>(...)</dt><dd><tt>T.<a href="#PortPair-__sizeof__">__sizeof__</a>()&nbsp;--&nbsp;size&nbsp;of&nbsp;T&nbsp;in&nbsp;memory,&nbsp;in&nbsp;bytes</tt></dd></dl>
+
+<dl><dt><a name="PortPair-count"><strong>count</strong></a>(...)</dt><dd><tt>T.<a href="#PortPair-count">count</a>(value)&nbsp;-&gt;&nbsp;integer&nbsp;--&nbsp;return&nbsp;number&nbsp;of&nbsp;occurrences&nbsp;of&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="PortPair-index"><strong>index</strong></a>(...)</dt><dd><tt>T.<a href="#PortPair-index">index</a>(value,&nbsp;[start,&nbsp;[stop]])&nbsp;-&gt;&nbsp;integer&nbsp;--&nbsp;return&nbsp;first&nbsp;index&nbsp;of&nbsp;value.<br>
+Raises&nbsp;ValueError&nbsp;if&nbsp;the&nbsp;value&nbsp;is&nbsp;not&nbsp;present.</tt></dd></dl>
+
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="PortPairs">class <strong>PortPairs</strong></a>(<a href="__builtin__.html#tuple">__builtin__.tuple</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt><a href="#PortPairs">PortPairs</a>(http,&nbsp;https,&nbsp;dns)<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.forwarders.html#PortPairs">PortPairs</a></dd>
+<dd><a href="__builtin__.html#tuple">__builtin__.tuple</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="PortPairs-__getnewargs__"><strong>__getnewargs__</strong></a>(self)</dt><dd><tt>Return&nbsp;self&nbsp;as&nbsp;a&nbsp;plain&nbsp;<a href="__builtin__.html#tuple">tuple</a>.&nbsp;&nbsp;Used&nbsp;by&nbsp;copy&nbsp;and&nbsp;pickle.</tt></dd></dl>
+
+<dl><dt><a name="PortPairs-__getstate__"><strong>__getstate__</strong></a>(self)</dt><dd><tt>Exclude&nbsp;the&nbsp;OrderedDict&nbsp;from&nbsp;pickling</tt></dd></dl>
+
+<dl><dt><a name="PortPairs-__repr__"><strong>__repr__</strong></a>(self)</dt><dd><tt>Return&nbsp;a&nbsp;nicely&nbsp;formatted&nbsp;representation&nbsp;string</tt></dd></dl>
+
+<dl><dt><a name="PortPairs-_asdict"><strong>_asdict</strong></a>(self)</dt><dd><tt>Return&nbsp;a&nbsp;new&nbsp;OrderedDict&nbsp;which&nbsp;maps&nbsp;field&nbsp;names&nbsp;to&nbsp;their&nbsp;values</tt></dd></dl>
+
+<dl><dt><a name="PortPairs-_replace"><strong>_replace</strong></a>(_self, **kwds)</dt><dd><tt>Return&nbsp;a&nbsp;new&nbsp;<a href="#PortPairs">PortPairs</a>&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;replacing&nbsp;specified&nbsp;fields&nbsp;with&nbsp;new&nbsp;values</tt></dd></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="PortPairs-_make"><strong>_make</strong></a>(cls, iterable, new<font color="#909090">=&lt;built-in method __new__ of type object&gt;</font>, len<font color="#909090">=&lt;built-in function len&gt;</font>)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Make&nbsp;a&nbsp;new&nbsp;<a href="#PortPairs">PortPairs</a>&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;from&nbsp;a&nbsp;sequence&nbsp;or&nbsp;iterable</tt></dd></dl>
+
+<hr>
+Static methods defined here:<br>
+<dl><dt><a name="PortPairs-__new__"><strong>__new__</strong></a>(_cls, http, https, dns)</dt><dd><tt>Create&nbsp;new&nbsp;instance&nbsp;of&nbsp;<a href="#PortPairs">PortPairs</a>(http,&nbsp;https,&nbsp;dns)</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>Return&nbsp;a&nbsp;new&nbsp;OrderedDict&nbsp;which&nbsp;maps&nbsp;field&nbsp;names&nbsp;to&nbsp;their&nbsp;values</tt></dd>
+</dl>
+<dl><dt><strong>dns</strong></dt>
+<dd><tt>Alias&nbsp;for&nbsp;field&nbsp;number&nbsp;2</tt></dd>
+</dl>
+<dl><dt><strong>http</strong></dt>
+<dd><tt>Alias&nbsp;for&nbsp;field&nbsp;number&nbsp;0</tt></dd>
+</dl>
+<dl><dt><strong>https</strong></dt>
+<dd><tt>Alias&nbsp;for&nbsp;field&nbsp;number&nbsp;1</tt></dd>
+</dl>
+<hr>
+Data and other attributes defined here:<br>
+<dl><dt><strong>_fields</strong> = ('http', 'https', 'dns')</dl>
+
+<hr>
+Methods inherited from <a href="__builtin__.html#tuple">__builtin__.tuple</a>:<br>
+<dl><dt><a name="PortPairs-__add__"><strong>__add__</strong></a>(...)</dt><dd><tt>x.<a href="#PortPairs-__add__">__add__</a>(y)&nbsp;&lt;==&gt;&nbsp;x+y</tt></dd></dl>
+
+<dl><dt><a name="PortPairs-__contains__"><strong>__contains__</strong></a>(...)</dt><dd><tt>x.<a href="#PortPairs-__contains__">__contains__</a>(y)&nbsp;&lt;==&gt;&nbsp;y&nbsp;in&nbsp;x</tt></dd></dl>
+
+<dl><dt><a name="PortPairs-__eq__"><strong>__eq__</strong></a>(...)</dt><dd><tt>x.<a href="#PortPairs-__eq__">__eq__</a>(y)&nbsp;&lt;==&gt;&nbsp;x==y</tt></dd></dl>
+
+<dl><dt><a name="PortPairs-__ge__"><strong>__ge__</strong></a>(...)</dt><dd><tt>x.<a href="#PortPairs-__ge__">__ge__</a>(y)&nbsp;&lt;==&gt;&nbsp;x&gt;=y</tt></dd></dl>
+
+<dl><dt><a name="PortPairs-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#PortPairs-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="PortPairs-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#PortPairs-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="PortPairs-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#PortPairs-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="PortPairs-__gt__"><strong>__gt__</strong></a>(...)</dt><dd><tt>x.<a href="#PortPairs-__gt__">__gt__</a>(y)&nbsp;&lt;==&gt;&nbsp;x&gt;y</tt></dd></dl>
+
+<dl><dt><a name="PortPairs-__hash__"><strong>__hash__</strong></a>(...)</dt><dd><tt>x.<a href="#PortPairs-__hash__">__hash__</a>()&nbsp;&lt;==&gt;&nbsp;hash(x)</tt></dd></dl>
+
+<dl><dt><a name="PortPairs-__iter__"><strong>__iter__</strong></a>(...)</dt><dd><tt>x.<a href="#PortPairs-__iter__">__iter__</a>()&nbsp;&lt;==&gt;&nbsp;iter(x)</tt></dd></dl>
+
+<dl><dt><a name="PortPairs-__le__"><strong>__le__</strong></a>(...)</dt><dd><tt>x.<a href="#PortPairs-__le__">__le__</a>(y)&nbsp;&lt;==&gt;&nbsp;x&lt;=y</tt></dd></dl>
+
+<dl><dt><a name="PortPairs-__len__"><strong>__len__</strong></a>(...)</dt><dd><tt>x.<a href="#PortPairs-__len__">__len__</a>()&nbsp;&lt;==&gt;&nbsp;len(x)</tt></dd></dl>
+
+<dl><dt><a name="PortPairs-__lt__"><strong>__lt__</strong></a>(...)</dt><dd><tt>x.<a href="#PortPairs-__lt__">__lt__</a>(y)&nbsp;&lt;==&gt;&nbsp;x&lt;y</tt></dd></dl>
+
+<dl><dt><a name="PortPairs-__mul__"><strong>__mul__</strong></a>(...)</dt><dd><tt>x.<a href="#PortPairs-__mul__">__mul__</a>(n)&nbsp;&lt;==&gt;&nbsp;x*n</tt></dd></dl>
+
+<dl><dt><a name="PortPairs-__ne__"><strong>__ne__</strong></a>(...)</dt><dd><tt>x.<a href="#PortPairs-__ne__">__ne__</a>(y)&nbsp;&lt;==&gt;&nbsp;x!=y</tt></dd></dl>
+
+<dl><dt><a name="PortPairs-__rmul__"><strong>__rmul__</strong></a>(...)</dt><dd><tt>x.<a href="#PortPairs-__rmul__">__rmul__</a>(n)&nbsp;&lt;==&gt;&nbsp;n*x</tt></dd></dl>
+
+<dl><dt><a name="PortPairs-__sizeof__"><strong>__sizeof__</strong></a>(...)</dt><dd><tt>T.<a href="#PortPairs-__sizeof__">__sizeof__</a>()&nbsp;--&nbsp;size&nbsp;of&nbsp;T&nbsp;in&nbsp;memory,&nbsp;in&nbsp;bytes</tt></dd></dl>
+
+<dl><dt><a name="PortPairs-count"><strong>count</strong></a>(...)</dt><dd><tt>T.<a href="#PortPairs-count">count</a>(value)&nbsp;-&gt;&nbsp;integer&nbsp;--&nbsp;return&nbsp;number&nbsp;of&nbsp;occurrences&nbsp;of&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="PortPairs-index"><strong>index</strong></a>(...)</dt><dd><tt>T.<a href="#PortPairs-index">index</a>(value,&nbsp;[start,&nbsp;[stop]])&nbsp;-&gt;&nbsp;integer&nbsp;--&nbsp;return&nbsp;first&nbsp;index&nbsp;of&nbsp;value.<br>
+Raises&nbsp;ValueError&nbsp;if&nbsp;the&nbsp;value&nbsp;is&nbsp;not&nbsp;present.</tt></dd></dl>
+
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.html
new file mode 100644
index 0000000..cd82947
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.html
@@ -0,0 +1,36 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: package telemetry.internal</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.internal</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/__init__.py">telemetry/internal/__init__.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2015&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Package Contents</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.actions.html"><strong>actions</strong>&nbsp;(package)</a><br>
+<a href="telemetry.internal.app.html"><strong>app</strong>&nbsp;(package)</a><br>
+<a href="telemetry.internal.backends.html"><strong>backends</strong>&nbsp;(package)</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.browser.html"><strong>browser</strong>&nbsp;(package)</a><br>
+<a href="telemetry.internal.forwarders.html"><strong>forwarders</strong>&nbsp;(package)</a><br>
+<a href="telemetry.internal.image_processing.html"><strong>image_processing</strong>&nbsp;(package)</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.html"><strong>platform</strong>&nbsp;(package)</a><br>
+<a href="telemetry.internal.results.html"><strong>results</strong>&nbsp;(package)</a><br>
+<a href="telemetry.internal.story_runner.html">story_runner</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.story_runner_unittest.html">story_runner_unittest</a><br>
+<a href="telemetry.internal.testing.html"><strong>testing</strong>&nbsp;(package)</a><br>
+<a href="telemetry.internal.util.html"><strong>util</strong>&nbsp;(package)</a><br>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.image_processing._bitmap.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.image_processing._bitmap.html
new file mode 100644
index 0000000..2c3f377
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.image_processing._bitmap.html
@@ -0,0 +1,97 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.image_processing._bitmap</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.image_processing.html"><font color="#ffffff">image_processing</font></a>._bitmap</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/image_processing/_bitmap.py">telemetry/internal/image_processing/_bitmap.py</a></font></td></tr></table>
+    <p><tt><a href="#Bitmap">Bitmap</a>&nbsp;is&nbsp;a&nbsp;basic&nbsp;wrapper&nbsp;for&nbsp;image&nbsp;pixels.&nbsp;It&nbsp;includes&nbsp;some&nbsp;basic&nbsp;processing<br>
+tools:&nbsp;crop,&nbsp;find&nbsp;bounding&nbsp;box&nbsp;of&nbsp;a&nbsp;color&nbsp;and&nbsp;compute&nbsp;histogram&nbsp;of&nbsp;color&nbsp;values.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="array.html">array</a><br>
+<a href="telemetry.internal.util.binary_manager.html">telemetry.internal.util.binary_manager</a><br>
+<a href="cStringIO.html">cStringIO</a><br>
+</td><td width="25%" valign=top><a href="telemetry.util.color_histogram.html">telemetry.util.color_histogram</a><br>
+<a href="telemetry.core.platform.html">telemetry.core.platform</a><br>
+<a href="png.html">png</a><br>
+</td><td width="25%" valign=top><a href="telemetry.util.rgba_color.html">telemetry.util.rgba_color</a><br>
+<a href="struct.html">struct</a><br>
+<a href="subprocess.html">subprocess</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.image_processing._bitmap.html#Bitmap">Bitmap</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="Bitmap">class <strong>Bitmap</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Utilities&nbsp;for&nbsp;parsing&nbsp;and&nbsp;inspecting&nbsp;a&nbsp;bitmap.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="Bitmap-ColorHistogram"><strong>ColorHistogram</strong></a>(self, ignore_color<font color="#909090">=None</font>, tolerance<font color="#909090">=0</font>)</dt></dl>
+
+<dl><dt><a name="Bitmap-Crop"><strong>Crop</strong></a>(self, left, top, width, height)</dt></dl>
+
+<dl><dt><a name="Bitmap-Diff"><strong>Diff</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="Bitmap-GetBoundingBox"><strong>GetBoundingBox</strong></a>(self, color, tolerance<font color="#909090">=0</font>)</dt></dl>
+
+<dl><dt><a name="Bitmap-GetPixelColor"><strong>GetPixelColor</strong></a>(self, x, y)</dt></dl>
+
+<dl><dt><a name="Bitmap-IsEqual"><strong>IsEqual</strong></a>(self, other, tolerance<font color="#909090">=0</font>)</dt></dl>
+
+<dl><dt><a name="Bitmap-WritePngFile"><strong>WritePngFile</strong></a>(self, path)</dt></dl>
+
+<dl><dt><a name="Bitmap-__init__"><strong>__init__</strong></a>(self, bpp, width, height, pixels, metadata<font color="#909090">=None</font>)</dt></dl>
+
+<hr>
+Static methods defined here:<br>
+<dl><dt><a name="Bitmap-FromPng"><strong>FromPng</strong></a>(png_data)</dt></dl>
+
+<dl><dt><a name="Bitmap-FromPngFile"><strong>FromPngFile</strong></a>(path)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>bpp</strong></dt>
+</dl>
+<dl><dt><strong>height</strong></dt>
+</dl>
+<dl><dt><strong>metadata</strong></dt>
+</dl>
+<dl><dt><strong>pixels</strong></dt>
+</dl>
+<dl><dt><strong>width</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.image_processing.cv_util.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.image_processing.cv_util.html
new file mode 100644
index 0000000..68c1a5e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.image_processing.cv_util.html
@@ -0,0 +1,50 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.image_processing.cv_util</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.image_processing.html"><font color="#ffffff">image_processing</font></a>.cv_util</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/image_processing/cv_util.py">telemetry/internal/image_processing/cv_util.py</a></font></td></tr></table>
+    <p><tt>This&nbsp;module&nbsp;provides&nbsp;implementations&nbsp;of&nbsp;common&nbsp;computer&nbsp;Vision&nbsp;operations.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.util.external_modules.html">telemetry.internal.util.external_modules</a><br>
+</td><td width="25%" valign=top><a href="numpy.html">numpy</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-AreLinesOrthogonal"><strong>AreLinesOrthogonal</strong></a>(line1, line2, tolerance)</dt><dd><tt>Returns&nbsp;true&nbsp;if&nbsp;lines&nbsp;are&nbsp;within&nbsp;tolerance&nbsp;radians&nbsp;of&nbsp;being&nbsp;orthogonal.</tt></dd></dl>
+ <dl><dt><a name="-ExtendLines"><strong>ExtendLines</strong></a>(lines, length)</dt><dd><tt>Extends&nbsp;lines&nbsp;in&nbsp;an&nbsp;array&nbsp;to&nbsp;a&nbsp;given&nbsp;length,&nbsp;maintaining&nbsp;the&nbsp;center<br>
+point.&nbsp;Does&nbsp;not&nbsp;necessarily&nbsp;maintain&nbsp;point&nbsp;order.</tt></dd></dl>
+ <dl><dt><a name="-FindLineIntersection"><strong>FindLineIntersection</strong></a>(line1, line2)</dt><dd><tt>If&nbsp;the&nbsp;line&nbsp;segments&nbsp;intersect,&nbsp;returns&nbsp;True&nbsp;and&nbsp;their&nbsp;intersection.<br>
+Otherwise,&nbsp;returns&nbsp;False&nbsp;and&nbsp;the&nbsp;intersection&nbsp;of&nbsp;the&nbsp;line&nbsp;segments&nbsp;if&nbsp;they<br>
+were&nbsp;to&nbsp;be&nbsp;extended.</tt></dd></dl>
+ <dl><dt><a name="-IsPointApproxOnLine"><strong>IsPointApproxOnLine</strong></a>(point, line, tolerance<font color="#909090">=1</font>)</dt><dd><tt>Approximates&nbsp;distance&nbsp;between&nbsp;point&nbsp;and&nbsp;line&nbsp;for&nbsp;small&nbsp;distances&nbsp;using<br>
+the&nbsp;determinant&nbsp;and&nbsp;checks&nbsp;whether&nbsp;it's&nbsp;within&nbsp;the&nbsp;tolerance.&nbsp;Tolerance&nbsp;is<br>
+an&nbsp;approximate&nbsp;distance&nbsp;in&nbsp;pixels,&nbsp;precision&nbsp;decreases&nbsp;with&nbsp;distance.</tt></dd></dl>
+ <dl><dt><a name="-SqDistance"><strong>SqDistance</strong></a>(point1, point2)</dt><dd><tt>Computes&nbsp;the&nbsp;square&nbsp;of&nbsp;the&nbsp;distance&nbsp;between&nbsp;two&nbsp;points.</tt></dd></dl>
+ <dl><dt><a name="-SqDistances"><strong>SqDistances</strong></a>(points1, points2)</dt><dd><tt>Computes&nbsp;the&nbsp;square&nbsp;of&nbsp;the&nbsp;distance&nbsp;between&nbsp;two&nbsp;sets&nbsp;of&nbsp;points,&nbsp;or&nbsp;a<br>
+set&nbsp;of&nbsp;points&nbsp;and&nbsp;a&nbsp;point.</tt></dd></dl>
+</td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>division</strong> = _Feature((2, 2, 0, 'alpha', 2), (3, 0, 0, 'alpha', 0), 8192)</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.image_processing.fake_frame_generator.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.image_processing.fake_frame_generator.html
new file mode 100644
index 0000000..249a327
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.image_processing.fake_frame_generator.html
@@ -0,0 +1,114 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.image_processing.fake_frame_generator</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.image_processing.html"><font color="#ffffff">image_processing</font></a>.fake_frame_generator</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/image_processing/fake_frame_generator.py">telemetry/internal/image_processing/fake_frame_generator.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.util.external_modules.html">telemetry.internal.util.external_modules</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.image_processing.frame_generator.html">telemetry.internal.image_processing.frame_generator</a><br>
+</td><td width="25%" valign=top><a href="numpy.html">numpy</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.image_processing.frame_generator.html#FrameGenerator">telemetry.internal.image_processing.frame_generator.FrameGenerator</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.image_processing.fake_frame_generator.html#FakeFrameGenerator">FakeFrameGenerator</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="FakeFrameGenerator">class <strong>FakeFrameGenerator</strong></a>(<a href="telemetry.internal.image_processing.frame_generator.html#FrameGenerator">telemetry.internal.image_processing.frame_generator.FrameGenerator</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Fakes&nbsp;a&nbsp;Frame&nbsp;Generator,&nbsp;for&nbsp;testing.<br>
+&nbsp;<br>
+Attributes:<br>
+&nbsp;&nbsp;_frame_index:&nbsp;A&nbsp;frame&nbsp;read&nbsp;counter.<br>
+&nbsp;&nbsp;_timestamps:&nbsp;A&nbsp;generator&nbsp;of&nbsp;timestamps&nbsp;to&nbsp;return,&nbsp;or&nbsp;None.<br>
+&nbsp;&nbsp;_timestamp:&nbsp;The&nbsp;current&nbsp;timestamp.<br>
+&nbsp;&nbsp;_dimensions:&nbsp;The&nbsp;dimensions&nbsp;to&nbsp;return.<br>
+&nbsp;&nbsp;_channels:&nbsp;The&nbsp;number&nbsp;of&nbsp;color&nbsp;channels&nbsp;to&nbsp;return&nbsp;in&nbsp;the&nbsp;generated&nbsp;frames.<br>
+&nbsp;&nbsp;_frames:&nbsp;The&nbsp;number&nbsp;of&nbsp;frames&nbsp;to&nbsp;return&nbsp;before&nbsp;fake&nbsp;EOF.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.image_processing.fake_frame_generator.html#FakeFrameGenerator">FakeFrameGenerator</a></dd>
+<dd><a href="telemetry.internal.image_processing.frame_generator.html#FrameGenerator">telemetry.internal.image_processing.frame_generator.FrameGenerator</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="FakeFrameGenerator-__init__"><strong>__init__</strong></a>(self, frames<font color="#909090">=1e+16</font>, dimensions<font color="#909090">=(320, 240)</font>, channels<font color="#909090">=3</font>, timestamps<font color="#909090">=&lt;generator object &lt;genexpr&gt;&gt;</font>)</dt><dd><tt>Initializes&nbsp;the&nbsp;<a href="#FakeFrameGenerator">FakeFrameGenerator</a>&nbsp;object.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;frames:&nbsp;int,&nbsp;The&nbsp;number&nbsp;of&nbsp;frames&nbsp;to&nbsp;return&nbsp;before&nbsp;fake&nbsp;EOF.<br>
+&nbsp;&nbsp;dimensions:&nbsp;(int,&nbsp;int),&nbsp;The&nbsp;dimensions&nbsp;to&nbsp;return.<br>
+&nbsp;&nbsp;timestamps:&nbsp;generator,&nbsp;A&nbsp;generator&nbsp;of&nbsp;timestamps&nbsp;to&nbsp;return.&nbsp;The&nbsp;default<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;value&nbsp;is&nbsp;an&nbsp;infinite&nbsp;0&nbsp;generator.<br>
+&nbsp;&nbsp;channels:&nbsp;int,&nbsp;The&nbsp;number&nbsp;of&nbsp;color&nbsp;channels&nbsp;to&nbsp;return&nbsp;in&nbsp;the&nbsp;generated<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;frames,&nbsp;1&nbsp;for&nbsp;greyscale,&nbsp;3&nbsp;for&nbsp;RGB.</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>CurrentFrameNumber</strong></dt>
+</dl>
+<dl><dt><strong>CurrentTimestamp</strong></dt>
+</dl>
+<dl><dt><strong>Dimensions</strong></dt>
+</dl>
+<hr>
+Data and other attributes defined here:<br>
+<dl><dt><strong>__abstractmethods__</strong> = frozenset([])</dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.image_processing.frame_generator.html#FrameGenerator">telemetry.internal.image_processing.frame_generator.FrameGenerator</a>:<br>
+<dl><dt><strong>Generator</strong></dt>
+<dd><tt>Returns:<br>
+A&nbsp;reference&nbsp;to&nbsp;the&nbsp;created&nbsp;generator.</tt></dd>
+</dl>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="telemetry.internal.image_processing.frame_generator.html#FrameGenerator">telemetry.internal.image_processing.frame_generator.FrameGenerator</a>:<br>
+<dl><dt><strong>__metaclass__</strong> = &lt;class 'abc.ABCMeta'&gt;<dd><tt>Metaclass&nbsp;for&nbsp;defining&nbsp;Abstract&nbsp;Base&nbsp;Classes&nbsp;(ABCs).<br>
+&nbsp;<br>
+Use&nbsp;this&nbsp;metaclass&nbsp;to&nbsp;create&nbsp;an&nbsp;ABC.&nbsp;&nbsp;An&nbsp;ABC&nbsp;can&nbsp;be&nbsp;subclassed<br>
+directly,&nbsp;and&nbsp;then&nbsp;acts&nbsp;as&nbsp;a&nbsp;mix-in&nbsp;class.&nbsp;&nbsp;You&nbsp;can&nbsp;also&nbsp;register<br>
+unrelated&nbsp;concrete&nbsp;classes&nbsp;(even&nbsp;built-in&nbsp;classes)&nbsp;and&nbsp;unrelated<br>
+ABCs&nbsp;as&nbsp;'virtual&nbsp;subclasses'&nbsp;--&nbsp;these&nbsp;and&nbsp;their&nbsp;descendants&nbsp;will<br>
+be&nbsp;considered&nbsp;subclasses&nbsp;of&nbsp;the&nbsp;registering&nbsp;ABC&nbsp;by&nbsp;the&nbsp;built-in<br>
+issubclass()&nbsp;function,&nbsp;but&nbsp;the&nbsp;registering&nbsp;ABC&nbsp;won't&nbsp;show&nbsp;up&nbsp;in<br>
+their&nbsp;MRO&nbsp;(Method&nbsp;Resolution&nbsp;Order)&nbsp;nor&nbsp;will&nbsp;method<br>
+implementations&nbsp;defined&nbsp;by&nbsp;the&nbsp;registering&nbsp;ABC&nbsp;be&nbsp;callable&nbsp;(not<br>
+even&nbsp;via&nbsp;super()).</tt></dl>
+
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.image_processing.frame_generator.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.image_processing.frame_generator.html
new file mode 100644
index 0000000..8cfa3cc
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.image_processing.frame_generator.html
@@ -0,0 +1,160 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.image_processing.frame_generator</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.image_processing.html"><font color="#ffffff">image_processing</font></a>.frame_generator</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/image_processing/frame_generator.py">telemetry/internal/image_processing/frame_generator.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="abc.html">abc</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.image_processing.frame_generator.html#FrameGenerator">FrameGenerator</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="exceptions.html#Exception">exceptions.Exception</a>(<a href="exceptions.html#BaseException">exceptions.BaseException</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.image_processing.frame_generator.html#FrameReadError">FrameReadError</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="FrameGenerator">class <strong>FrameGenerator</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Defines&nbsp;an&nbsp;interface&nbsp;for&nbsp;reading&nbsp;input&nbsp;frames.<br>
+&nbsp;<br>
+Attributes:<br>
+&nbsp;&nbsp;_generator:&nbsp;A&nbsp;reference&nbsp;to&nbsp;the&nbsp;created&nbsp;generator.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="FrameGenerator-__init__"><strong>__init__</strong></a>(self)</dt><dd><tt>Initializes&nbsp;the&nbsp;<a href="#FrameGenerator">FrameGenerator</a>&nbsp;<a href="__builtin__.html#object">object</a>.</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>CurrentFrameNumber</strong></dt>
+<dd><tt>Returns:<br>
+int,&nbsp;The&nbsp;frame&nbsp;index&nbsp;of&nbsp;the&nbsp;current&nbsp;frame.</tt></dd>
+</dl>
+<dl><dt><strong>CurrentTimestamp</strong></dt>
+<dd><tt>Returns:<br>
+float,&nbsp;The&nbsp;timestamp&nbsp;of&nbsp;the&nbsp;current&nbsp;frame&nbsp;in&nbsp;milliseconds.</tt></dd>
+</dl>
+<dl><dt><strong>Dimensions</strong></dt>
+<dd><tt>Returns:<br>
+The&nbsp;dimensions&nbsp;of&nbsp;the&nbsp;frame&nbsp;sequence&nbsp;as&nbsp;a&nbsp;tuple&nbsp;int&nbsp;(width,&nbsp;height).<br>
+This&nbsp;value&nbsp;should&nbsp;be&nbsp;constant&nbsp;across&nbsp;frames.</tt></dd>
+</dl>
+<dl><dt><strong>Generator</strong></dt>
+<dd><tt>Returns:<br>
+A&nbsp;reference&nbsp;to&nbsp;the&nbsp;created&nbsp;generator.</tt></dd>
+</dl>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes defined here:<br>
+<dl><dt><strong>__abstractmethods__</strong> = frozenset(['CurrentFrameNumber', 'CurrentTimestamp', 'Dimensions', '_CreateGenerator'])</dl>
+
+<dl><dt><strong>__metaclass__</strong> = &lt;class 'abc.ABCMeta'&gt;<dd><tt>Metaclass&nbsp;for&nbsp;defining&nbsp;Abstract&nbsp;Base&nbsp;Classes&nbsp;(ABCs).<br>
+&nbsp;<br>
+Use&nbsp;this&nbsp;metaclass&nbsp;to&nbsp;create&nbsp;an&nbsp;ABC.&nbsp;&nbsp;An&nbsp;ABC&nbsp;can&nbsp;be&nbsp;subclassed<br>
+directly,&nbsp;and&nbsp;then&nbsp;acts&nbsp;as&nbsp;a&nbsp;mix-in&nbsp;class.&nbsp;&nbsp;You&nbsp;can&nbsp;also&nbsp;register<br>
+unrelated&nbsp;concrete&nbsp;classes&nbsp;(even&nbsp;built-in&nbsp;classes)&nbsp;and&nbsp;unrelated<br>
+ABCs&nbsp;as&nbsp;'virtual&nbsp;subclasses'&nbsp;--&nbsp;these&nbsp;and&nbsp;their&nbsp;descendants&nbsp;will<br>
+be&nbsp;considered&nbsp;subclasses&nbsp;of&nbsp;the&nbsp;registering&nbsp;ABC&nbsp;by&nbsp;the&nbsp;built-in<br>
+issubclass()&nbsp;function,&nbsp;but&nbsp;the&nbsp;registering&nbsp;ABC&nbsp;won't&nbsp;show&nbsp;up&nbsp;in<br>
+their&nbsp;MRO&nbsp;(Method&nbsp;Resolution&nbsp;Order)&nbsp;nor&nbsp;will&nbsp;method<br>
+implementations&nbsp;defined&nbsp;by&nbsp;the&nbsp;registering&nbsp;ABC&nbsp;be&nbsp;callable&nbsp;(not<br>
+even&nbsp;via&nbsp;super()).</tt></dl>
+
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="FrameReadError">class <strong>FrameReadError</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.image_processing.frame_generator.html#FrameReadError">FrameReadError</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="FrameReadError-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#FrameReadError-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#FrameReadError-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="FrameReadError-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#FrameReadError-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="FrameReadError-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#FrameReadError-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="FrameReadError-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#FrameReadError-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="FrameReadError-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#FrameReadError-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="FrameReadError-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="FrameReadError-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#FrameReadError-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="FrameReadError-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#FrameReadError-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="FrameReadError-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="FrameReadError-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#FrameReadError-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="FrameReadError-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.image_processing.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.image_processing.html
new file mode 100644
index 0000000..5d9a36a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.image_processing.html
@@ -0,0 +1,37 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: package telemetry.internal.image_processing</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.image_processing</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/image_processing/__init__.py">telemetry/internal/image_processing/__init__.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Package Contents</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.image_processing._bitmap.html">_bitmap</a><br>
+<a href="telemetry.internal.image_processing.cv_util.html">cv_util</a><br>
+<a href="telemetry.internal.image_processing.cv_util_unittest.html">cv_util_unittest</a><br>
+<a href="telemetry.internal.image_processing.fake_frame_generator.html">fake_frame_generator</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.image_processing.frame_generator.html">frame_generator</a><br>
+<a href="telemetry.internal.image_processing.image_util_bitmap_impl.html">image_util_bitmap_impl</a><br>
+<a href="telemetry.internal.image_processing.image_util_numpy_impl.html">image_util_numpy_impl</a><br>
+<a href="telemetry.internal.image_processing.screen_finder.html">screen_finder</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.image_processing.screen_finder_unittest.html">screen_finder_unittest</a><br>
+<a href="telemetry.internal.image_processing.video.html">video</a><br>
+<a href="telemetry.internal.image_processing.video_file_frame_generator.html">video_file_frame_generator</a><br>
+<a href="telemetry.internal.image_processing.video_file_frame_generator_unittest.html">video_file_frame_generator_unittest</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.image_processing.video_unittest.html">video_unittest</a><br>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.image_processing.image_util_bitmap_impl.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.image_processing.image_util_bitmap_impl.html
new file mode 100644
index 0000000..b92e1c8
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.image_processing.image_util_bitmap_impl.html
@@ -0,0 +1,53 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.image_processing.image_util_bitmap_impl</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.image_processing.html"><font color="#ffffff">image_processing</font></a>.image_util_bitmap_impl</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/image_processing/image_util_bitmap_impl.py">telemetry/internal/image_processing/image_util_bitmap_impl.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.image_processing._bitmap.html">telemetry.internal.image_processing._bitmap</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-AreEqual"><strong>AreEqual</strong></a>(bitmap1, bitmap2, tolerance, _)</dt></dl>
+ <dl><dt><a name="-Channels"><strong>Channels</strong></a>(bitmap)</dt></dl>
+ <dl><dt><a name="-Crop"><strong>Crop</strong></a>(bitmap, left, top, width, height)</dt></dl>
+ <dl><dt><a name="-Diff"><strong>Diff</strong></a>(bitmap1, bitmap2)</dt></dl>
+ <dl><dt><a name="-FromPng"><strong>FromPng</strong></a>(png_data)</dt></dl>
+ <dl><dt><a name="-FromPngFile"><strong>FromPngFile</strong></a>(path)</dt></dl>
+ <dl><dt><a name="-FromRGBPixels"><strong>FromRGBPixels</strong></a>(width, height, pixels, bpp)</dt></dl>
+ <dl><dt><a name="-GetBoundingBox"><strong>GetBoundingBox</strong></a>(bitmap, color, tolerance)</dt></dl>
+ <dl><dt><a name="-GetColorHistogram"><strong>GetColorHistogram</strong></a>(bitmap, ignore_color, tolerance)</dt></dl>
+ <dl><dt><a name="-GetPixelColor"><strong>GetPixelColor</strong></a>(bitmap, x, y)</dt></dl>
+ <dl><dt><a name="-Height"><strong>Height</strong></a>(bitmap)</dt></dl>
+ <dl><dt><a name="-Pixels"><strong>Pixels</strong></a>(bitmap)</dt></dl>
+ <dl><dt><a name="-Width"><strong>Width</strong></a>(bitmap)</dt></dl>
+ <dl><dt><a name="-WritePngFile"><strong>WritePngFile</strong></a>(bitmap, path)</dt></dl>
+</td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>division</strong> = _Feature((2, 2, 0, 'alpha', 2), (3, 0, 0, 'alpha', 0), 8192)</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.image_processing.image_util_numpy_impl.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.image_processing.image_util_numpy_impl.html
new file mode 100644
index 0000000..c5786d5
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.image_processing.image_util_numpy_impl.html
@@ -0,0 +1,58 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.image_processing.image_util_numpy_impl</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.image_processing.html"><font color="#ffffff">image_processing</font></a>.image_util_numpy_impl</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/image_processing/image_util_numpy_impl.py">telemetry/internal/image_processing/image_util_numpy_impl.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.util.color_histogram.html">telemetry.util.color_histogram</a><br>
+<a href="cv2.html">cv2</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.util.external_modules.html">telemetry.internal.util.external_modules</a><br>
+<a href="numpy.html">numpy</a><br>
+</td><td width="25%" valign=top><a href="png.html">png</a><br>
+<a href="telemetry.util.rgba_color.html">telemetry.util.rgba_color</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-AreEqual"><strong>AreEqual</strong></a>(image1, image2, tolerance, likely_equal)</dt></dl>
+ <dl><dt><a name="-Channels"><strong>Channels</strong></a>(image)</dt></dl>
+ <dl><dt><a name="-Crop"><strong>Crop</strong></a>(image, left, top, width, height)</dt></dl>
+ <dl><dt><a name="-Diff"><strong>Diff</strong></a>(image1, image2)</dt></dl>
+ <dl><dt><a name="-FromPng"><strong>FromPng</strong></a>(png_data)</dt></dl>
+ <dl><dt><a name="-FromPngFile"><strong>FromPngFile</strong></a>(path)</dt></dl>
+ <dl><dt><a name="-FromRGBPixels"><strong>FromRGBPixels</strong></a>(width, height, pixels, bpp)</dt></dl>
+ <dl><dt><a name="-GetBoundingBox"><strong>GetBoundingBox</strong></a>(image, color, tolerance)</dt></dl>
+ <dl><dt><a name="-GetColorHistogram"><strong>GetColorHistogram</strong></a>(image, ignore_color, tolerance)</dt></dl>
+ <dl><dt><a name="-GetPixelColor"><strong>GetPixelColor</strong></a>(image, x, y)</dt></dl>
+ <dl><dt><a name="-Height"><strong>Height</strong></a>(image)</dt></dl>
+ <dl><dt><a name="-Pixels"><strong>Pixels</strong></a>(image)</dt></dl>
+ <dl><dt><a name="-Width"><strong>Width</strong></a>(image)</dt></dl>
+ <dl><dt><a name="-WritePngFile"><strong>WritePngFile</strong></a>(image, path)</dt></dl>
+</td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>division</strong> = _Feature((2, 2, 0, 'alpha', 2), (3, 0, 0, 'alpha', 0), 8192)</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.image_processing.screen_finder.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.image_processing.screen_finder.html
new file mode 100644
index 0000000..a635519
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.image_processing.screen_finder.html
@@ -0,0 +1,168 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.image_processing.screen_finder</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.image_processing.html"><font color="#ffffff">image_processing</font></a>.screen_finder</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/image_processing/screen_finder.py">telemetry/internal/image_processing/screen_finder.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.<br>
+#<br>
+#&nbsp;This&nbsp;script&nbsp;attempts&nbsp;to&nbsp;detect&nbsp;the&nbsp;region&nbsp;of&nbsp;a&nbsp;camera's&nbsp;field&nbsp;of&nbsp;view&nbsp;that<br>
+#&nbsp;contains&nbsp;the&nbsp;screen&nbsp;of&nbsp;the&nbsp;device&nbsp;we&nbsp;are&nbsp;testing.<br>
+#<br>
+#&nbsp;Usage:&nbsp;./screen_finder.py&nbsp;path_to_video&nbsp;0&nbsp;0&nbsp;--verbose</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="copy.html">copy</a><br>
+<a href="cv2.html">cv2</a><br>
+<a href="telemetry.internal.image_processing.cv_util.html">telemetry.internal.image_processing.cv_util</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.util.external_modules.html">telemetry.internal.util.external_modules</a><br>
+<a href="telemetry.internal.image_processing.frame_generator.html">telemetry.internal.image_processing.frame_generator</a><br>
+<a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top><a href="numpy.html">numpy</a><br>
+<a href="os.html">os</a><br>
+<a href="sys.html">sys</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.image_processing.video_file_frame_generator.html">telemetry.internal.image_processing.video_file_frame_generator</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.image_processing.screen_finder.html#ScreenFinder">ScreenFinder</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ScreenFinder">class <strong>ScreenFinder</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Finds&nbsp;and&nbsp;extracts&nbsp;device&nbsp;screens&nbsp;from&nbsp;video.<br>
+&nbsp;<br>
+Sample&nbsp;Usage:<br>
+&nbsp;&nbsp;sf&nbsp;=&nbsp;<a href="#ScreenFinder">ScreenFinder</a>(sys.argv[1])<br>
+&nbsp;&nbsp;while&nbsp;sf.<a href="#ScreenFinder-HasNext">HasNext</a>():<br>
+&nbsp;&nbsp;&nbsp;&nbsp;ret,&nbsp;screen&nbsp;=&nbsp;sf.<a href="#ScreenFinder-GetNext">GetNext</a>()<br>
+&nbsp;<br>
+Attributes:<br>
+&nbsp;&nbsp;_lost_corners:&nbsp;Each&nbsp;index&nbsp;represents&nbsp;whether&nbsp;or&nbsp;not&nbsp;we&nbsp;lost&nbsp;track&nbsp;of&nbsp;that<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;corner&nbsp;on&nbsp;the&nbsp;previous&nbsp;frame.&nbsp;Ordered&nbsp;by&nbsp;[top-right,&nbsp;top-left,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;bottom-left,&nbsp;bottom-right]<br>
+&nbsp;&nbsp;_frame:&nbsp;An&nbsp;unmodified&nbsp;copy&nbsp;of&nbsp;the&nbsp;frame&nbsp;we're&nbsp;currently&nbsp;processing.<br>
+&nbsp;&nbsp;_frame_debug:&nbsp;A&nbsp;copy&nbsp;of&nbsp;the&nbsp;frame&nbsp;we're&nbsp;currently&nbsp;processing,&nbsp;may&nbsp;be<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;modified&nbsp;at&nbsp;any&nbsp;time,&nbsp;used&nbsp;for&nbsp;debugging.<br>
+&nbsp;&nbsp;_frame_grey:&nbsp;A&nbsp;greyscale&nbsp;copy&nbsp;of&nbsp;the&nbsp;frame&nbsp;we're&nbsp;currently&nbsp;processing.<br>
+&nbsp;&nbsp;_frame_edges:&nbsp;A&nbsp;Canny&nbsp;Edge&nbsp;detected&nbsp;copy&nbsp;of&nbsp;the&nbsp;frame&nbsp;we're&nbsp;currently<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;processing.<br>
+&nbsp;&nbsp;_screen_size:&nbsp;The&nbsp;size&nbsp;of&nbsp;device&nbsp;screen&nbsp;in&nbsp;the&nbsp;video&nbsp;when&nbsp;first&nbsp;detected.<br>
+&nbsp;&nbsp;_avg_corners:&nbsp;Exponentially&nbsp;weighted&nbsp;average&nbsp;of&nbsp;the&nbsp;previous&nbsp;corner<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;locations.<br>
+&nbsp;&nbsp;_prev_corners:&nbsp;The&nbsp;location&nbsp;of&nbsp;the&nbsp;corners&nbsp;in&nbsp;the&nbsp;previous&nbsp;frame.<br>
+&nbsp;&nbsp;_lost_corner_frames:&nbsp;A&nbsp;counter&nbsp;of&nbsp;the&nbsp;number&nbsp;of&nbsp;successive&nbsp;frames&nbsp;in&nbsp;which<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;we've&nbsp;lost&nbsp;a&nbsp;corner&nbsp;location.<br>
+&nbsp;&nbsp;_border:&nbsp;See&nbsp;|border|&nbsp;above.<br>
+&nbsp;&nbsp;_min_line_length:&nbsp;The&nbsp;minimum&nbsp;length&nbsp;a&nbsp;line&nbsp;must&nbsp;be&nbsp;before&nbsp;we&nbsp;consider&nbsp;it<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;a&nbsp;possible&nbsp;screen&nbsp;edge.<br>
+&nbsp;&nbsp;_frame_generator:&nbsp;See&nbsp;|frame_generator|&nbsp;above.<br>
+&nbsp;&nbsp;_width,&nbsp;_height:&nbsp;The&nbsp;width&nbsp;and&nbsp;height&nbsp;of&nbsp;the&nbsp;frame.<br>
+&nbsp;&nbsp;_anglesp5,&nbsp;_anglesm5:&nbsp;The&nbsp;angles&nbsp;for&nbsp;each&nbsp;point&nbsp;we&nbsp;look&nbsp;at&nbsp;in&nbsp;the&nbsp;grid<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when&nbsp;computing&nbsp;brightness,&nbsp;constant&nbsp;across&nbsp;frames.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="ScreenFinder-GetNext"><strong>GetNext</strong></a>(self)</dt><dd><tt>Gets&nbsp;the&nbsp;next&nbsp;screen&nbsp;image.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;A&nbsp;numpy&nbsp;matrix&nbsp;containing&nbsp;the&nbsp;screen&nbsp;surrounded&nbsp;by&nbsp;the&nbsp;number&nbsp;of&nbsp;border<br>
+&nbsp;&nbsp;pixels&nbsp;specified&nbsp;in&nbsp;initialization,&nbsp;and&nbsp;the&nbsp;location&nbsp;of&nbsp;the&nbsp;detected<br>
+&nbsp;&nbsp;screen&nbsp;corners&nbsp;in&nbsp;the&nbsp;current&nbsp;frame,&nbsp;if&nbsp;a&nbsp;screen&nbsp;is&nbsp;found.&nbsp;The&nbsp;returned<br>
+&nbsp;&nbsp;screen&nbsp;is&nbsp;guaranteed&nbsp;to&nbsp;be&nbsp;the&nbsp;same&nbsp;size&nbsp;at&nbsp;each&nbsp;frame.<br>
+&nbsp;&nbsp;'None'&nbsp;and&nbsp;'None'&nbsp;if&nbsp;no&nbsp;screen&nbsp;was&nbsp;found&nbsp;on&nbsp;the&nbsp;current&nbsp;frame.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;FrameReadError:&nbsp;An&nbsp;error&nbsp;occurred&nbsp;in&nbsp;the&nbsp;FrameGenerator.<br>
+&nbsp;&nbsp;RuntimeError:&nbsp;This&nbsp;method&nbsp;was&nbsp;called&nbsp;when&nbsp;no&nbsp;frames&nbsp;were&nbsp;available.</tt></dd></dl>
+
+<dl><dt><a name="ScreenFinder-HasNext"><strong>HasNext</strong></a>(self)</dt><dd><tt>True&nbsp;if&nbsp;there&nbsp;are&nbsp;more&nbsp;frames&nbsp;available&nbsp;to&nbsp;process.</tt></dd></dl>
+
+<dl><dt><a name="ScreenFinder-__init__"><strong>__init__</strong></a>(self, frame_generator, border<font color="#909090">=5</font>)</dt><dd><tt>Initializes&nbsp;the&nbsp;<a href="#ScreenFinder">ScreenFinder</a>&nbsp;<a href="__builtin__.html#object">object</a>.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;frame_generator:&nbsp;FrameGenerator,&nbsp;An&nbsp;initialized&nbsp;Video&nbsp;Frame&nbsp;Generator.<br>
+&nbsp;&nbsp;border:&nbsp;int,&nbsp;number&nbsp;of&nbsp;pixels&nbsp;of&nbsp;border&nbsp;to&nbsp;be&nbsp;kept&nbsp;when&nbsp;cropping&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;detected&nbsp;screen.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;FrameReadError:&nbsp;The&nbsp;frame&nbsp;generator&nbsp;may&nbsp;output&nbsp;a&nbsp;read&nbsp;error&nbsp;during<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;initialization.</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes defined here:<br>
+<dl><dt><strong>CANNY_HYSTERESIS_THRESH_HIGH</strong> = 500</dl>
+
+<dl><dt><strong>CANNY_HYSTERESIS_THRESH_LOW</strong> = 300</dl>
+
+<dl><dt><strong>CORNER_AVERAGE_WEIGHT</strong> = 0.5</dl>
+
+<dl><dt><strong>CornerData</strong> = &lt;class 'telemetry.internal.image_processing.screen_finder.CornerData'&gt;</dl>
+
+<dl><dt><strong>DEBUG</strong> = False</dl>
+
+<dl><dt><strong>MAX_INTERFRAME_MOTION</strong> = 25</dl>
+
+<dl><dt><strong>MIN_CORNER_ABSOLUTE_BRIGHTNESS</strong> = 60</dl>
+
+<dl><dt><strong>MIN_RELATIVE_BRIGHTNESS_FACTOR</strong> = 1.5</dl>
+
+<dl><dt><strong>MIN_SCREEN_WIDTH</strong> = 40</dl>
+
+<dl><dt><strong>RESET_AFTER_N_BAD_FRAMES</strong> = 2</dl>
+
+<dl><dt><strong>SMALL_ANGLE</strong> = 0.08726646259971647</dl>
+
+<dl><dt><strong>ScreenNotFoundError</strong> = &lt;class 'telemetry.internal.image_processing.screen_finder.ScreenNotFoundError'&gt;</dl>
+
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-main"><strong>main</strong></a>()</dt></dl>
+</td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>division</strong> = _Feature((2, 2, 0, 'alpha', 2), (3, 0, 0, 'alpha', 0), 8192)</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.image_processing.video.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.image_processing.video.html
new file mode 100644
index 0000000..03367f9
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.image_processing.video.html
@@ -0,0 +1,152 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.image_processing.video</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.image_processing.html"><font color="#ffffff">image_processing</font></a>.video</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/image_processing/video.py">telemetry/internal/image_processing/video.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="catapult_base.cloud_storage.html">catapult_base.cloud_storage</a><br>
+<a href="telemetry.util.image_util.html">telemetry.util.image_util</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.platform.html">telemetry.core.platform</a><br>
+<a href="telemetry.util.rgba_color.html">telemetry.util.rgba_color</a><br>
+</td><td width="25%" valign=top><a href="subprocess.html">subprocess</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.image_processing.video.html#Video">Video</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="exceptions.html#Exception">exceptions.Exception</a>(<a href="exceptions.html#BaseException">exceptions.BaseException</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.image_processing.video.html#BoundingBoxNotFoundException">BoundingBoxNotFoundException</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="BoundingBoxNotFoundException">class <strong>BoundingBoxNotFoundException</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.image_processing.video.html#BoundingBoxNotFoundException">BoundingBoxNotFoundException</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="BoundingBoxNotFoundException-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#BoundingBoxNotFoundException-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#BoundingBoxNotFoundException-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="BoundingBoxNotFoundException-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#BoundingBoxNotFoundException-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="BoundingBoxNotFoundException-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#BoundingBoxNotFoundException-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="BoundingBoxNotFoundException-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#BoundingBoxNotFoundException-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="BoundingBoxNotFoundException-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#BoundingBoxNotFoundException-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="BoundingBoxNotFoundException-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="BoundingBoxNotFoundException-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#BoundingBoxNotFoundException-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="BoundingBoxNotFoundException-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#BoundingBoxNotFoundException-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="BoundingBoxNotFoundException-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="BoundingBoxNotFoundException-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#BoundingBoxNotFoundException-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="BoundingBoxNotFoundException-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="Video">class <strong>Video</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Utilities&nbsp;for&nbsp;storing&nbsp;and&nbsp;interacting&nbsp;with&nbsp;the&nbsp;video&nbsp;capture.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="Video-GetVideoFrameIter"><strong>GetVideoFrameIter</strong></a>(self)</dt><dd><tt>Returns&nbsp;the&nbsp;iteration&nbsp;for&nbsp;processing&nbsp;the&nbsp;video&nbsp;capture.<br>
+&nbsp;<br>
+This&nbsp;looks&nbsp;for&nbsp;the&nbsp;initial&nbsp;color&nbsp;flash&nbsp;in&nbsp;the&nbsp;first&nbsp;frame&nbsp;to&nbsp;establish&nbsp;the<br>
+tab&nbsp;content&nbsp;boundaries&nbsp;and&nbsp;then&nbsp;omits&nbsp;all&nbsp;frames&nbsp;displaying&nbsp;the&nbsp;flash.<br>
+&nbsp;<br>
+Yields:<br>
+&nbsp;&nbsp;(time_ms,&nbsp;image)&nbsp;tuples&nbsp;representing&nbsp;each&nbsp;video&nbsp;keyframe.&nbsp;Only&nbsp;the&nbsp;first<br>
+&nbsp;&nbsp;frame&nbsp;is&nbsp;a&nbsp;run&nbsp;of&nbsp;sequential&nbsp;duplicate&nbsp;bitmaps&nbsp;is&nbsp;typically&nbsp;included.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;time_ms&nbsp;is&nbsp;milliseconds&nbsp;since&nbsp;navigationStart.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;image&nbsp;may&nbsp;be&nbsp;a&nbsp;telemetry.core.Bitmap,&nbsp;or&nbsp;a&nbsp;numpy&nbsp;array&nbsp;depending&nbsp;on<br>
+&nbsp;&nbsp;&nbsp;&nbsp;whether&nbsp;numpy&nbsp;is&nbsp;installed.</tt></dd></dl>
+
+<dl><dt><a name="Video-UploadToCloudStorage"><strong>UploadToCloudStorage</strong></a>(self, bucket, target_path)</dt><dd><tt>Uploads&nbsp;video&nbsp;file&nbsp;to&nbsp;cloud&nbsp;storage.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;target_path:&nbsp;Path&nbsp;indicating&nbsp;where&nbsp;to&nbsp;store&nbsp;the&nbsp;file&nbsp;in&nbsp;cloud&nbsp;storage.</tt></dd></dl>
+
+<dl><dt><a name="Video-__init__"><strong>__init__</strong></a>(self, video_file_obj)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>HIGHLIGHT_ORANGE_FRAME</strong> = RgbaColor(r=222, g=100, b=13, a=255)</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.image_processing.video_file_frame_generator.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.image_processing.video_file_frame_generator.html
new file mode 100644
index 0000000..72fbbe5
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.image_processing.video_file_frame_generator.html
@@ -0,0 +1,118 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.image_processing.video_file_frame_generator</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.image_processing.html"><font color="#ffffff">image_processing</font></a>.video_file_frame_generator</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/image_processing/video_file_frame_generator.py">telemetry/internal/image_processing/video_file_frame_generator.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="cv2.html">cv2</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.util.external_modules.html">telemetry.internal.util.external_modules</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.image_processing.frame_generator.html">telemetry.internal.image_processing.frame_generator</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.image_processing.frame_generator.html#FrameGenerator">telemetry.internal.image_processing.frame_generator.FrameGenerator</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.image_processing.video_file_frame_generator.html#VideoFileFrameGenerator">VideoFileFrameGenerator</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="VideoFileFrameGenerator">class <strong>VideoFileFrameGenerator</strong></a>(<a href="telemetry.internal.image_processing.frame_generator.html#FrameGenerator">telemetry.internal.image_processing.frame_generator.FrameGenerator</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Provides&nbsp;a&nbsp;Frame&nbsp;Generator&nbsp;for&nbsp;a&nbsp;video&nbsp;file.<br>
+&nbsp;<br>
+Sample&nbsp;Usage:<br>
+&nbsp;&nbsp;generator&nbsp;=&nbsp;<a href="#VideoFileFrameGenerator">VideoFileFrameGenerator</a>(sys.argv[1]).GetGenerator()<br>
+&nbsp;&nbsp;for&nbsp;frame&nbsp;in&nbsp;generator:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;Do&nbsp;something<br>
+&nbsp;<br>
+Attributes:<br>
+&nbsp;&nbsp;_capture:&nbsp;The&nbsp;openCV&nbsp;video&nbsp;capture.<br>
+&nbsp;&nbsp;_frame_count:&nbsp;The&nbsp;number&nbsp;of&nbsp;frames&nbsp;in&nbsp;the&nbsp;video&nbsp;capture.<br>
+&nbsp;&nbsp;_frame_index:&nbsp;The&nbsp;frame&nbsp;number&nbsp;of&nbsp;the&nbsp;current&nbsp;frame.<br>
+&nbsp;&nbsp;_timestamp:&nbsp;The&nbsp;timestamp&nbsp;of&nbsp;the&nbsp;current&nbsp;frame.<br>
+&nbsp;&nbsp;_dimensions:&nbsp;The&nbsp;dimensions&nbsp;of&nbsp;the&nbsp;video&nbsp;capture.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.image_processing.video_file_frame_generator.html#VideoFileFrameGenerator">VideoFileFrameGenerator</a></dd>
+<dd><a href="telemetry.internal.image_processing.frame_generator.html#FrameGenerator">telemetry.internal.image_processing.frame_generator.FrameGenerator</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="VideoFileFrameGenerator-__init__"><strong>__init__</strong></a>(self, video_filename, start_frame_index<font color="#909090">=0</font>)</dt><dd><tt>Initializes&nbsp;the&nbsp;<a href="#VideoFileFrameGenerator">VideoFileFrameGenerator</a>&nbsp;object.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;video_filename:&nbsp;str,&nbsp;The&nbsp;path&nbsp;to&nbsp;the&nbsp;video&nbsp;file.<br>
+&nbsp;&nbsp;start_frame_index:&nbsp;int,&nbsp;The&nbsp;number&nbsp;of&nbsp;frames&nbsp;to&nbsp;skip&nbsp;at&nbsp;the&nbsp;start&nbsp;of&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;file.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;FrameReadError:&nbsp;A&nbsp;read&nbsp;error&nbsp;occurred&nbsp;during&nbsp;initialization.</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>CurrentFrameNumber</strong></dt>
+</dl>
+<dl><dt><strong>CurrentTimestamp</strong></dt>
+</dl>
+<dl><dt><strong>Dimensions</strong></dt>
+</dl>
+<hr>
+Data and other attributes defined here:<br>
+<dl><dt><strong>__abstractmethods__</strong> = frozenset([])</dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.image_processing.frame_generator.html#FrameGenerator">telemetry.internal.image_processing.frame_generator.FrameGenerator</a>:<br>
+<dl><dt><strong>Generator</strong></dt>
+<dd><tt>Returns:<br>
+A&nbsp;reference&nbsp;to&nbsp;the&nbsp;created&nbsp;generator.</tt></dd>
+</dl>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="telemetry.internal.image_processing.frame_generator.html#FrameGenerator">telemetry.internal.image_processing.frame_generator.FrameGenerator</a>:<br>
+<dl><dt><strong>__metaclass__</strong> = &lt;class 'abc.ABCMeta'&gt;<dd><tt>Metaclass&nbsp;for&nbsp;defining&nbsp;Abstract&nbsp;Base&nbsp;Classes&nbsp;(ABCs).<br>
+&nbsp;<br>
+Use&nbsp;this&nbsp;metaclass&nbsp;to&nbsp;create&nbsp;an&nbsp;ABC.&nbsp;&nbsp;An&nbsp;ABC&nbsp;can&nbsp;be&nbsp;subclassed<br>
+directly,&nbsp;and&nbsp;then&nbsp;acts&nbsp;as&nbsp;a&nbsp;mix-in&nbsp;class.&nbsp;&nbsp;You&nbsp;can&nbsp;also&nbsp;register<br>
+unrelated&nbsp;concrete&nbsp;classes&nbsp;(even&nbsp;built-in&nbsp;classes)&nbsp;and&nbsp;unrelated<br>
+ABCs&nbsp;as&nbsp;'virtual&nbsp;subclasses'&nbsp;--&nbsp;these&nbsp;and&nbsp;their&nbsp;descendants&nbsp;will<br>
+be&nbsp;considered&nbsp;subclasses&nbsp;of&nbsp;the&nbsp;registering&nbsp;ABC&nbsp;by&nbsp;the&nbsp;built-in<br>
+issubclass()&nbsp;function,&nbsp;but&nbsp;the&nbsp;registering&nbsp;ABC&nbsp;won't&nbsp;show&nbsp;up&nbsp;in<br>
+their&nbsp;MRO&nbsp;(Method&nbsp;Resolution&nbsp;Order)&nbsp;nor&nbsp;will&nbsp;method<br>
+implementations&nbsp;defined&nbsp;by&nbsp;the&nbsp;registering&nbsp;ABC&nbsp;be&nbsp;callable&nbsp;(not<br>
+even&nbsp;via&nbsp;super()).</tt></dl>
+
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.android_device.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.android_device.html
new file mode 100644
index 0000000..8a13a01
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.android_device.html
@@ -0,0 +1,111 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.android_device</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.android_device</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/android_device.py">telemetry/internal/platform/android_device.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="pylib.constants.html">pylib.constants</a><br>
+<a href="telemetry.internal.platform.device.html">telemetry.internal.platform.device</a><br>
+<a href="devil.android.device_blacklist.html">devil.android.device_blacklist</a><br>
+</td><td width="25%" valign=top><a href="devil.android.device_errors.html">devil.android.device_errors</a><br>
+<a href="devil.android.device_utils.html">devil.android.device_utils</a><br>
+<a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.profiler.monsoon.html">telemetry.internal.platform.profiler.monsoon</a><br>
+<a href="os.html">os</a><br>
+<a href="re.html">re</a><br>
+</td><td width="25%" valign=top><a href="subprocess.html">subprocess</a><br>
+<a href="telemetry.core.util.html">telemetry.core.util</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.device.html#Device">telemetry.internal.platform.device.Device</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.android_device.html#AndroidDevice">AndroidDevice</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="AndroidDevice">class <strong>AndroidDevice</strong></a>(<a href="telemetry.internal.platform.device.html#Device">telemetry.internal.platform.device.Device</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Class&nbsp;represents&nbsp;information&nbsp;for&nbsp;connecting&nbsp;to&nbsp;an&nbsp;android&nbsp;device.<br>
+&nbsp;<br>
+Attributes:<br>
+&nbsp;&nbsp;device_id:&nbsp;the&nbsp;device's&nbsp;serial&nbsp;string&nbsp;created&nbsp;by&nbsp;adb&nbsp;to&nbsp;uniquely<br>
+&nbsp;&nbsp;&nbsp;&nbsp;identify&nbsp;an&nbsp;emulator/device&nbsp;instance.&nbsp;This&nbsp;string&nbsp;can&nbsp;be&nbsp;found&nbsp;by&nbsp;running<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'adb&nbsp;devices'&nbsp;command<br>
+&nbsp;&nbsp;enable_performance_mode:&nbsp;when&nbsp;this&nbsp;is&nbsp;set&nbsp;to&nbsp;True,&nbsp;android&nbsp;platform&nbsp;will&nbsp;be<br>
+&nbsp;&nbsp;set&nbsp;to&nbsp;high&nbsp;performance&nbsp;mode&nbsp;after&nbsp;browser&nbsp;is&nbsp;started.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.android_device.html#AndroidDevice">AndroidDevice</a></dd>
+<dd><a href="telemetry.internal.platform.device.html#Device">telemetry.internal.platform.device.Device</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="AndroidDevice-__init__"><strong>__init__</strong></a>(self, device_id, enable_performance_mode<font color="#909090">=True</font>)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="AndroidDevice-GetAllConnectedDevices"><strong>GetAllConnectedDevices</strong></a>(cls, blacklist)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>device_id</strong></dt>
+</dl>
+<dl><dt><strong>enable_performance_mode</strong></dt>
+</dl>
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.device.html#Device">telemetry.internal.platform.device.Device</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>guid</strong></dt>
+</dl>
+<dl><dt><strong>name</strong></dt>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-CanDiscoverDevices"><strong>CanDiscoverDevices</strong></a>()</dt><dd><tt>Returns&nbsp;true&nbsp;if&nbsp;devices&nbsp;are&nbsp;discoverable&nbsp;via&nbsp;adb.</tt></dd></dl>
+ <dl><dt><a name="-FindAllAvailableDevices"><strong>FindAllAvailableDevices</strong></a>(options)</dt><dd><tt>Returns&nbsp;a&nbsp;list&nbsp;of&nbsp;available&nbsp;devices.</tt></dd></dl>
+ <dl><dt><a name="-GetDevice"><strong>GetDevice</strong></a>(finder_options)</dt><dd><tt>Return&nbsp;a&nbsp;Platform&nbsp;instance&nbsp;for&nbsp;the&nbsp;device&nbsp;specified&nbsp;by&nbsp;|finder_options|.</tt></dd></dl>
+ <dl><dt><a name="-GetDeviceSerials"><strong>GetDeviceSerials</strong></a>(blacklist)</dt><dd><tt>Return&nbsp;the&nbsp;list&nbsp;of&nbsp;device&nbsp;serials&nbsp;of&nbsp;healthy&nbsp;devices.<br>
+&nbsp;<br>
+If&nbsp;a&nbsp;preferred&nbsp;device&nbsp;has&nbsp;been&nbsp;set&nbsp;with&nbsp;ANDROID_SERIAL,&nbsp;it&nbsp;will&nbsp;be&nbsp;first&nbsp;in<br>
+the&nbsp;returned&nbsp;list.&nbsp;The&nbsp;arguments&nbsp;specify&nbsp;what&nbsp;devices&nbsp;to&nbsp;include&nbsp;in&nbsp;the&nbsp;list.</tt></dd></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.android_platform_backend.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.android_platform_backend.html
new file mode 100644
index 0000000..5c3c211
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.android_platform_backend.html
@@ -0,0 +1,381 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.android_platform_backend</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.android_platform_backend</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/android_platform_backend.py">telemetry/internal/platform/android_platform_backend.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="adb_install_cert.html">adb_install_cert</a><br>
+<a href="telemetry.internal.platform.android_device.html">telemetry.internal.platform.android_device</a><br>
+<a href="telemetry.internal.platform.power_monitor.android_dumpsys_power_monitor.html">telemetry.internal.platform.power_monitor.android_dumpsys_power_monitor</a><br>
+<a href="telemetry.internal.forwarders.android_forwarder.html">telemetry.internal.forwarders.android_forwarder</a><br>
+<a href="telemetry.internal.platform.power_monitor.android_fuelgauge_power_monitor.html">telemetry.internal.platform.power_monitor.android_fuelgauge_power_monitor</a><br>
+<a href="telemetry.core.android_platform.html">telemetry.core.android_platform</a><br>
+<a href="telemetry.internal.platform.profiler.android_prebuilt_profiler_helper.html">telemetry.internal.platform.profiler.android_prebuilt_profiler_helper</a><br>
+<a href="telemetry.internal.platform.power_monitor.android_temperature_monitor.html">telemetry.internal.platform.power_monitor.android_temperature_monitor</a><br>
+<a href="devil.android.battery_utils.html">devil.android.battery_utils</a><br>
+<a href="telemetry.internal.util.binary_manager.html">telemetry.internal.util.binary_manager</a><br>
+</td><td width="25%" valign=top><a href="devil.android.perf.cache_control.html">devil.android.perf.cache_control</a><br>
+<a href="certutils.html">certutils</a><br>
+<a href="pylib.constants.html">pylib.constants</a><br>
+<a href="telemetry.decorators.html">telemetry.decorators</a><br>
+<a href="devil.android.device_errors.html">devil.android.device_errors</a><br>
+<a href="devil.android.device_utils.html">devil.android.device_utils</a><br>
+<a href="telemetry.internal.util.exception_formatter.html">telemetry.internal.util.exception_formatter</a><br>
+<a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+<a href="telemetry.internal.util.external_modules.html">telemetry.internal.util.external_modules</a><br>
+<a href="telemetry.internal.platform.linux_based_platform_backend.html">telemetry.internal.platform.linux_based_platform_backend</a><br>
+</td><td width="25%" valign=top><a href="logging.html">logging</a><br>
+<a href="telemetry.internal.platform.power_monitor.monsoon_power_monitor.html">telemetry.internal.platform.power_monitor.monsoon_power_monitor</a><br>
+<a href="os.html">os</a><br>
+<a href="devil.android.perf.perf_control.html">devil.android.perf.perf_control</a><br>
+<a href="telemetry.core.platform.html">telemetry.core.platform</a><br>
+<a href="platformsettings.html">platformsettings</a><br>
+<a href="telemetry.internal.platform.power_monitor.power_monitor_controller.html">telemetry.internal.platform.power_monitor.power_monitor_controller</a><br>
+<a href="psutil.html">psutil</a><br>
+<a href="re.html">re</a><br>
+<a href="pylib.screenshot.html">pylib.screenshot</a><br>
+</td><td width="25%" valign=top><a href="shutil.html">shutil</a><br>
+<a href="stat.html">stat</a><br>
+<a href="subprocess.html">subprocess</a><br>
+<a href="devil.android.perf.surface_stats_collector.html">devil.android.perf.surface_stats_collector</a><br>
+<a href="telemetry.internal.platform.power_monitor.sysfs_power_monitor.html">telemetry.internal.platform.power_monitor.sysfs_power_monitor</a><br>
+<a href="tempfile.html">tempfile</a><br>
+<a href="devil.android.perf.thermal_throttle.html">devil.android.perf.thermal_throttle</a><br>
+<a href="telemetry.core.util.html">telemetry.core.util</a><br>
+<a href="devil.android.sdk.version_codes.html">devil.android.sdk.version_codes</a><br>
+<a href="telemetry.internal.image_processing.video.html">telemetry.internal.image_processing.video</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.linux_based_platform_backend.html#LinuxBasedPlatformBackend">telemetry.internal.platform.linux_based_platform_backend.LinuxBasedPlatformBackend</a>(<a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">telemetry.internal.platform.platform_backend.PlatformBackend</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.android_platform_backend.html#AndroidPlatformBackend">AndroidPlatformBackend</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="AndroidPlatformBackend">class <strong>AndroidPlatformBackend</strong></a>(<a href="telemetry.internal.platform.linux_based_platform_backend.html#LinuxBasedPlatformBackend">telemetry.internal.platform.linux_based_platform_backend.LinuxBasedPlatformBackend</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.android_platform_backend.html#AndroidPlatformBackend">AndroidPlatformBackend</a></dd>
+<dd><a href="telemetry.internal.platform.linux_based_platform_backend.html#LinuxBasedPlatformBackend">telemetry.internal.platform.linux_based_platform_backend.LinuxBasedPlatformBackend</a></dd>
+<dd><a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">telemetry.internal.platform.platform_backend.PlatformBackend</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="AndroidPlatformBackend-CanCaptureVideo"><strong>CanCaptureVideo</strong></a>(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-CanFlushIndividualFilesFromSystemCache"><strong>CanFlushIndividualFilesFromSystemCache</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-CanLaunchApplication"><strong>CanLaunchApplication</strong></a>(self, application)</dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-CanMonitorNetworkData"><strong>CanMonitorNetworkData</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-CanMonitorPower"><strong>CanMonitorPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-CanMonitorThermalThrottling"><strong>CanMonitorThermalThrottling</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-CanTakeScreenshot"><strong>CanTakeScreenshot</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-DismissCrashDialogIfNeeded"><strong>DismissCrashDialogIfNeeded</strong></a>(self)</dt><dd><tt>Dismiss&nbsp;any&nbsp;error&nbsp;dialogs.<br>
+&nbsp;<br>
+Limit&nbsp;the&nbsp;number&nbsp;in&nbsp;case&nbsp;we&nbsp;have&nbsp;an&nbsp;error&nbsp;loop&nbsp;or&nbsp;we&nbsp;are&nbsp;failing&nbsp;to&nbsp;dismiss.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-FlushDnsCache"><strong>FlushDnsCache</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-FlushEntireSystemCache"><strong>FlushEntireSystemCache</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-FlushSystemCacheForDirectory"><strong>FlushSystemCacheForDirectory</strong></a>(self, directory)</dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-ForwardHostToDevice"><strong>ForwardHostToDevice</strong></a>(self, host_port, device_port)</dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-GetArchName"><strong>GetArchName</strong></a>(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-GetChildPids"><strong>GetChildPids</strong></a>(self, pid)</dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-GetCommandLine"><strong>GetCommandLine</strong></a>(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-GetCpuStats"><strong>GetCpuStats</strong></a>(self, pid)</dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-GetCpuTimestamp"><strong>GetCpuTimestamp</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-GetDeviceTypeName"><strong>GetDeviceTypeName</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-GetFileContents"><strong>GetFileContents</strong></a>(self, fname)</dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-GetMemoryStats"><strong>GetMemoryStats</strong></a>(self, pid)</dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-GetNetworkData"><strong>GetNetworkData</strong></a>(self, browser)</dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-GetOSName"><strong>GetOSName</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-GetOSVersionName"><strong>GetOSVersionName</strong></a>(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-GetPsOutput"><strong>GetPsOutput</strong></a>(self, columns, pid<font color="#909090">=None</font>)</dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-GetStackTrace"><strong>GetStackTrace</strong></a>(self, target_arch)</dt><dd><tt>Returns&nbsp;stack&nbsp;trace.<br>
+&nbsp;<br>
+The&nbsp;stack&nbsp;trace&nbsp;consists&nbsp;of&nbsp;raw&nbsp;logcat&nbsp;dump,&nbsp;logcat&nbsp;dump&nbsp;with&nbsp;symbols,<br>
+and&nbsp;stack&nbsp;info&nbsp;from&nbsp;tomstone&nbsp;files.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;target_arch:&nbsp;String&nbsp;specifying&nbsp;device&nbsp;architecture&nbsp;(eg.&nbsp;arm,&nbsp;arm64,&nbsp;mips,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;x86,&nbsp;x86_64)</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-GetStandardOutput"><strong>GetStandardOutput</strong></a>(self, number_of_lines<font color="#909090">=500</font>)</dt><dd><tt>Returns&nbsp;most&nbsp;recent&nbsp;lines&nbsp;of&nbsp;logcat&nbsp;dump.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;number_of_lines:&nbsp;Number&nbsp;of&nbsp;lines&nbsp;of&nbsp;log&nbsp;to&nbsp;return.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-HasBeenThermallyThrottled"><strong>HasBeenThermallyThrottled</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-InstallApplication"><strong>InstallApplication</strong></a>(self, application)</dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-InstallTestCa"><strong>InstallTestCa</strong></a>(self)</dt><dd><tt>Install&nbsp;a&nbsp;randomly&nbsp;generated&nbsp;root&nbsp;CA&nbsp;on&nbsp;the&nbsp;android&nbsp;device.<br>
+&nbsp;<br>
+This&nbsp;allows&nbsp;transparent&nbsp;HTTPS&nbsp;testing&nbsp;with&nbsp;WPR&nbsp;server&nbsp;without&nbsp;need<br>
+to&nbsp;tweak&nbsp;application&nbsp;network&nbsp;stack.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-IsAppRunning"><strong>IsAppRunning</strong></a>(self, process_name)</dt><dd><tt>Determine&nbsp;if&nbsp;the&nbsp;given&nbsp;process&nbsp;is&nbsp;running.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;process_name:&nbsp;The&nbsp;full&nbsp;package&nbsp;name&nbsp;string&nbsp;of&nbsp;the&nbsp;process.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-IsApplicationRunning"><strong>IsApplicationRunning</strong></a>(self, application)</dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-IsDisplayTracingSupported"><strong>IsDisplayTracingSupported</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-IsScreenLocked"><strong>IsScreenLocked</strong></a>(self)</dt><dd><tt>Determines&nbsp;if&nbsp;device&nbsp;screen&nbsp;is&nbsp;locked.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-IsScreenOn"><strong>IsScreenOn</strong></a>(self)</dt><dd><tt>Determines&nbsp;if&nbsp;device&nbsp;screen&nbsp;is&nbsp;on.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-IsThermallyThrottled"><strong>IsThermallyThrottled</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-KillApplication"><strong>KillApplication</strong></a>(self, application)</dt><dd><tt>Kill&nbsp;the&nbsp;given&nbsp;|application|.<br>
+&nbsp;<br>
+Might&nbsp;be&nbsp;used&nbsp;instead&nbsp;of&nbsp;ForceStop&nbsp;for&nbsp;efficiency&nbsp;reasons.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;application:&nbsp;The&nbsp;full&nbsp;package&nbsp;name&nbsp;string&nbsp;of&nbsp;the&nbsp;application&nbsp;to&nbsp;kill.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-LaunchApplication"><strong>LaunchApplication</strong></a>(self, application, parameters<font color="#909090">=None</font>, elevate_privilege<font color="#909090">=False</font>)</dt><dd><tt>Launches&nbsp;the&nbsp;given&nbsp;|application|&nbsp;with&nbsp;a&nbsp;list&nbsp;of&nbsp;|parameters|&nbsp;on&nbsp;the&nbsp;OS.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;application:&nbsp;The&nbsp;full&nbsp;package&nbsp;name&nbsp;string&nbsp;of&nbsp;the&nbsp;application&nbsp;to&nbsp;launch.<br>
+&nbsp;&nbsp;parameters:&nbsp;A&nbsp;list&nbsp;of&nbsp;parameters&nbsp;to&nbsp;be&nbsp;passed&nbsp;to&nbsp;the&nbsp;ActivityManager.<br>
+&nbsp;&nbsp;elevate_privilege:&nbsp;Currently&nbsp;unimplemented&nbsp;on&nbsp;Android.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-PathExists"><strong>PathExists</strong></a>(self, device_path, timeout<font color="#909090">=None</font>, retries<font color="#909090">=None</font>)</dt><dd><tt>Return&nbsp;whether&nbsp;the&nbsp;given&nbsp;path&nbsp;exists&nbsp;on&nbsp;the&nbsp;device.<br>
+This&nbsp;method&nbsp;is&nbsp;the&nbsp;same&nbsp;as<br>
+devil.android.device_utils.DeviceUtils.PathExists.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-PullProfile"><strong>PullProfile</strong></a>(self, package, output_profile_path)</dt><dd><tt>Copy&nbsp;application&nbsp;profile&nbsp;from&nbsp;device&nbsp;to&nbsp;host&nbsp;machine.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;package:&nbsp;The&nbsp;full&nbsp;package&nbsp;name&nbsp;string&nbsp;of&nbsp;the&nbsp;application&nbsp;for&nbsp;which&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;profile&nbsp;is&nbsp;to&nbsp;be&nbsp;copied.<br>
+&nbsp;&nbsp;output_profile_dir:&nbsp;Location&nbsp;where&nbsp;profile&nbsp;to&nbsp;be&nbsp;stored&nbsp;on&nbsp;host&nbsp;machine.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-PurgeUnpinnedMemory"><strong>PurgeUnpinnedMemory</strong></a>(self)</dt><dd><tt>Purges&nbsp;the&nbsp;unpinned&nbsp;ashmem&nbsp;memory&nbsp;for&nbsp;the&nbsp;whole&nbsp;system.<br>
+&nbsp;<br>
+This&nbsp;can&nbsp;be&nbsp;used&nbsp;to&nbsp;make&nbsp;memory&nbsp;measurements&nbsp;more&nbsp;stable.&nbsp;Requires&nbsp;root.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-PushProfile"><strong>PushProfile</strong></a>(self, package, new_profile_dir)</dt><dd><tt>Replace&nbsp;application&nbsp;profile&nbsp;with&nbsp;files&nbsp;found&nbsp;on&nbsp;host&nbsp;machine.<br>
+&nbsp;<br>
+Pushing&nbsp;the&nbsp;profile&nbsp;is&nbsp;slow,&nbsp;so&nbsp;we&nbsp;don't&nbsp;want&nbsp;to&nbsp;do&nbsp;it&nbsp;every&nbsp;time.<br>
+Avoid&nbsp;this&nbsp;by&nbsp;pushing&nbsp;to&nbsp;a&nbsp;safe&nbsp;location&nbsp;using&nbsp;PushChangedFiles,&nbsp;and<br>
+then&nbsp;copying&nbsp;into&nbsp;the&nbsp;correct&nbsp;location&nbsp;on&nbsp;each&nbsp;test&nbsp;run.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;package:&nbsp;The&nbsp;full&nbsp;package&nbsp;name&nbsp;string&nbsp;of&nbsp;the&nbsp;application&nbsp;for&nbsp;which&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;profile&nbsp;is&nbsp;to&nbsp;be&nbsp;updated.<br>
+&nbsp;&nbsp;new_profile_dir:&nbsp;Location&nbsp;where&nbsp;profile&nbsp;to&nbsp;be&nbsp;pushed&nbsp;is&nbsp;stored&nbsp;on&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;host&nbsp;machine.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-RemoveProfile"><strong>RemoveProfile</strong></a>(self, package, ignore_list)</dt><dd><tt>Delete&nbsp;application&nbsp;profile&nbsp;on&nbsp;device.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;package:&nbsp;The&nbsp;full&nbsp;package&nbsp;name&nbsp;string&nbsp;of&nbsp;the&nbsp;application&nbsp;for&nbsp;which&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;profile&nbsp;is&nbsp;to&nbsp;be&nbsp;deleted.<br>
+&nbsp;&nbsp;ignore_list:&nbsp;List&nbsp;of&nbsp;files&nbsp;to&nbsp;keep.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-RemoveTestCa"><strong>RemoveTestCa</strong></a>(self)</dt><dd><tt>Remove&nbsp;root&nbsp;CA&nbsp;generated&nbsp;by&nbsp;previous&nbsp;call&nbsp;to&nbsp;<a href="#AndroidPlatformBackend-InstallTestCa">InstallTestCa</a>().<br>
+&nbsp;<br>
+Removes&nbsp;the&nbsp;test&nbsp;root&nbsp;certificate&nbsp;from&nbsp;both&nbsp;the&nbsp;device&nbsp;and&nbsp;host&nbsp;machine.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-RunCommand"><strong>RunCommand</strong></a>(self, command)</dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-SetDebugApp"><strong>SetDebugApp</strong></a>(self, package)</dt><dd><tt>Set&nbsp;application&nbsp;to&nbsp;debugging.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;package:&nbsp;The&nbsp;full&nbsp;package&nbsp;name&nbsp;string&nbsp;of&nbsp;the&nbsp;application.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-SetFullPerformanceModeEnabled"><strong>SetFullPerformanceModeEnabled</strong></a>(self, enabled)</dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-SetGraphicsMemoryTrackingEnabled"><strong>SetGraphicsMemoryTrackingEnabled</strong></a>(self, enabled)</dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-SetRelaxSslCheck"><strong>SetRelaxSslCheck</strong></a>(self, value)</dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-StartDisplayTracing"><strong>StartDisplayTracing</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-StartMonitoringPower"><strong>StartMonitoringPower</strong></a>(self, browser)</dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-StartVideoCapture"><strong>StartVideoCapture</strong></a>(self, min_bitrate_mbps)</dt><dd><tt>Starts&nbsp;the&nbsp;video&nbsp;capture&nbsp;at&nbsp;specified&nbsp;bitrate.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-StopApplication"><strong>StopApplication</strong></a>(self, application)</dt><dd><tt>Stop&nbsp;the&nbsp;given&nbsp;|application|.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;application:&nbsp;The&nbsp;full&nbsp;package&nbsp;name&nbsp;string&nbsp;of&nbsp;the&nbsp;application&nbsp;to&nbsp;stop.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-StopDisplayTracing"><strong>StopDisplayTracing</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-StopForwardingHost"><strong>StopForwardingHost</strong></a>(self, host_port)</dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-StopMonitoringPower"><strong>StopMonitoringPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-StopVideoCapture"><strong>StopVideoCapture</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-TakeScreenshot"><strong>TakeScreenshot</strong></a>(self, file_path)</dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-__init__"><strong>__init__</strong></a>(self, device, finder_options)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="AndroidPlatformBackend-CreatePlatformForDevice"><strong>CreatePlatformForDevice</strong></a>(cls, device, finder_options)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-SupportsDevice"><strong>SupportsDevice</strong></a>(cls, device)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Static methods defined here:<br>
+<dl><dt><a name="AndroidPlatformBackend-ParseCStateSample"><strong>ParseCStateSample</strong></a>(sample)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>device</strong></dt>
+</dl>
+<dl><dt><strong>forwarder_factory</strong></dt>
+</dl>
+<dl><dt><strong>is_test_ca_installed</strong></dt>
+</dl>
+<dl><dt><strong>is_video_capture_running</strong></dt>
+</dl>
+<dl><dt><strong>log_file_path</strong></dt>
+</dl>
+<dl><dt><strong>use_rndis_forwarder</strong></dt>
+</dl>
+<dl><dt><strong>wpr_ca_cert_path</strong></dt>
+<dd><tt>Path&nbsp;to&nbsp;root&nbsp;certificate&nbsp;installed&nbsp;on&nbsp;browser&nbsp;(or&nbsp;None).<br>
+&nbsp;<br>
+If&nbsp;this&nbsp;is&nbsp;set,&nbsp;web&nbsp;page&nbsp;replay&nbsp;will&nbsp;use&nbsp;it&nbsp;to&nbsp;sign&nbsp;HTTPS&nbsp;responses.</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.internal.platform.linux_based_platform_backend.html#LinuxBasedPlatformBackend">telemetry.internal.platform.linux_based_platform_backend.LinuxBasedPlatformBackend</a>:<br>
+<dl><dt><a name="AndroidPlatformBackend-GetClockTicks"><strong>GetClockTicks</strong></a>(*args, **kwargs)</dt><dd><tt>Returns&nbsp;the&nbsp;number&nbsp;of&nbsp;clock&nbsp;ticks&nbsp;per&nbsp;second.<br>
+&nbsp;<br>
+The&nbsp;proper&nbsp;way&nbsp;is&nbsp;to&nbsp;call&nbsp;os.sysconf('SC_CLK_TCK')&nbsp;but&nbsp;that&nbsp;is&nbsp;not&nbsp;easy&nbsp;to<br>
+do&nbsp;on&nbsp;Android/CrOS.&nbsp;In&nbsp;practice,&nbsp;nearly&nbsp;all&nbsp;Linux&nbsp;machines&nbsp;have&nbsp;a&nbsp;USER_HZ<br>
+of&nbsp;100,&nbsp;so&nbsp;just&nbsp;return&nbsp;that.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-GetSystemCommitCharge"><strong>GetSystemCommitCharge</strong></a>(self)</dt><dd><tt>#&nbsp;Get&nbsp;the&nbsp;commit&nbsp;charge&nbsp;in&nbsp;kB.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-GetSystemTotalPhysicalMemory"><strong>GetSystemTotalPhysicalMemory</strong></a>(*args, **kwargs)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">telemetry.internal.platform.platform_backend.PlatformBackend</a>:<br>
+<dl><dt><a name="AndroidPlatformBackend-CanMeasurePerApplicationPower"><strong>CanMeasurePerApplicationPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-CooperativelyShutdown"><strong>CooperativelyShutdown</strong></a>(self, proc, app_name)</dt><dd><tt>Cooperatively&nbsp;shut&nbsp;down&nbsp;the&nbsp;given&nbsp;process&nbsp;from&nbsp;subprocess.Popen.<br>
+&nbsp;<br>
+Currently&nbsp;this&nbsp;is&nbsp;only&nbsp;implemented&nbsp;on&nbsp;Windows.&nbsp;See<br>
+crbug.com/424024&nbsp;for&nbsp;background&nbsp;on&nbsp;why&nbsp;it&nbsp;was&nbsp;added.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;proc:&nbsp;a&nbsp;process&nbsp;object&nbsp;returned&nbsp;from&nbsp;subprocess.Popen.<br>
+&nbsp;&nbsp;app_name:&nbsp;on&nbsp;Windows,&nbsp;is&nbsp;the&nbsp;prefix&nbsp;of&nbsp;the&nbsp;application's&nbsp;window<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class&nbsp;name&nbsp;that&nbsp;should&nbsp;be&nbsp;searched&nbsp;for.&nbsp;This&nbsp;helps&nbsp;ensure<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;that&nbsp;only&nbsp;the&nbsp;application's&nbsp;windows&nbsp;are&nbsp;closed.<br>
+&nbsp;<br>
+Returns&nbsp;True&nbsp;if&nbsp;it&nbsp;is&nbsp;believed&nbsp;the&nbsp;attempt&nbsp;succeeded.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-DidCreateBrowser"><strong>DidCreateBrowser</strong></a>(self, browser, browser_backend)</dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-DidStartBrowser"><strong>DidStartBrowser</strong></a>(self, browser, browser_backend)</dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-GetRemotePort"><strong>GetRemotePort</strong></a>(self, port)</dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-InitPlatformBackend"><strong>InitPlatformBackend</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-IsCooperativeShutdownSupported"><strong>IsCooperativeShutdownSupported</strong></a>(self)</dt><dd><tt>Indicates&nbsp;whether&nbsp;CooperativelyShutdown,&nbsp;below,&nbsp;is&nbsp;supported.<br>
+It&nbsp;is&nbsp;not&nbsp;necessary&nbsp;to&nbsp;implement&nbsp;it&nbsp;on&nbsp;all&nbsp;platforms.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-ReadMsr"><strong>ReadMsr</strong></a>(self, msr_number, start<font color="#909090">=0</font>, length<font color="#909090">=64</font>)</dt><dd><tt>Read&nbsp;a&nbsp;CPU&nbsp;model-specific&nbsp;register&nbsp;(MSR).<br>
+&nbsp;<br>
+Which&nbsp;MSRs&nbsp;are&nbsp;available&nbsp;depends&nbsp;on&nbsp;the&nbsp;CPU&nbsp;model.<br>
+On&nbsp;systems&nbsp;with&nbsp;multiple&nbsp;CPUs,&nbsp;this&nbsp;function&nbsp;may&nbsp;run&nbsp;on&nbsp;any&nbsp;CPU.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;msr_number:&nbsp;The&nbsp;number&nbsp;of&nbsp;the&nbsp;register&nbsp;to&nbsp;read.<br>
+&nbsp;&nbsp;start:&nbsp;The&nbsp;least&nbsp;significant&nbsp;bit&nbsp;to&nbsp;read,&nbsp;zero-indexed.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(Said&nbsp;another&nbsp;way,&nbsp;the&nbsp;number&nbsp;of&nbsp;bits&nbsp;to&nbsp;right-shift&nbsp;the&nbsp;MSR&nbsp;value.)<br>
+&nbsp;&nbsp;length:&nbsp;The&nbsp;number&nbsp;of&nbsp;bits&nbsp;to&nbsp;read.&nbsp;MSRs&nbsp;are&nbsp;64&nbsp;bits,&nbsp;even&nbsp;on&nbsp;32-bit&nbsp;CPUs.</tt></dd></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-SetPlatform"><strong>SetPlatform</strong></a>(self, platform)</dt></dl>
+
+<dl><dt><a name="AndroidPlatformBackend-WillCloseBrowser"><strong>WillCloseBrowser</strong></a>(self, browser, browser_backend)</dt></dl>
+
+<hr>
+Class methods inherited from <a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">telemetry.internal.platform.platform_backend.PlatformBackend</a>:<br>
+<dl><dt><a name="AndroidPlatformBackend-IsPlatformBackendForHost"><strong>IsPlatformBackendForHost</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Returns&nbsp;whether&nbsp;this&nbsp;platform&nbsp;backend&nbsp;is&nbsp;the&nbsp;platform&nbsp;backend&nbsp;to&nbsp;be&nbsp;used<br>
+for&nbsp;the&nbsp;host&nbsp;device&nbsp;which&nbsp;telemetry&nbsp;is&nbsp;running&nbsp;on.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">telemetry.internal.platform.platform_backend.PlatformBackend</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>is_host_platform</strong></dt>
+</dl>
+<dl><dt><strong>network_controller_backend</strong></dt>
+</dl>
+<dl><dt><strong>platform</strong></dt>
+</dl>
+<dl><dt><strong>running_browser_backends</strong></dt>
+</dl>
+<dl><dt><strong>tracing_controller_backend</strong></dt>
+</dl>
+<dl><dt><strong>wpr_http_device_port</strong></dt>
+</dl>
+<dl><dt><strong>wpr_https_device_port</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.cros_device.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.cros_device.html
new file mode 100644
index 0000000..026a1e3
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.cros_device.html
@@ -0,0 +1,92 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.cros_device</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.cros_device</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/cros_device.py">telemetry/internal/platform/cros_device.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.core.cros_interface.html">telemetry.core.cros_interface</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.device.html">telemetry.internal.platform.device</a><br>
+</td><td width="25%" valign=top><a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.platform.html">telemetry.core.platform</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.device.html#Device">telemetry.internal.platform.device.Device</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.cros_device.html#CrOSDevice">CrOSDevice</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="CrOSDevice">class <strong>CrOSDevice</strong></a>(<a href="telemetry.internal.platform.device.html#Device">telemetry.internal.platform.device.Device</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.cros_device.html#CrOSDevice">CrOSDevice</a></dd>
+<dd><a href="telemetry.internal.platform.device.html#Device">telemetry.internal.platform.device.Device</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="CrOSDevice-__init__"><strong>__init__</strong></a>(self, host_name, ssh_port, ssh_identity<font color="#909090">=None</font>)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="CrOSDevice-GetAllConnectedDevices"><strong>GetAllConnectedDevices</strong></a>(cls, blacklist)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>host_name</strong></dt>
+</dl>
+<dl><dt><strong>ssh_identity</strong></dt>
+</dl>
+<dl><dt><strong>ssh_port</strong></dt>
+</dl>
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.device.html#Device">telemetry.internal.platform.device.Device</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>guid</strong></dt>
+</dl>
+<dl><dt><strong>name</strong></dt>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-FindAllAvailableDevices"><strong>FindAllAvailableDevices</strong></a>(options)</dt><dd><tt>Returns&nbsp;a&nbsp;list&nbsp;of&nbsp;available&nbsp;device&nbsp;types.</tt></dd></dl>
+ <dl><dt><a name="-IsRunningOnCrOS"><strong>IsRunningOnCrOS</strong></a>()</dt></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.cros_platform_backend.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.cros_platform_backend.html
new file mode 100644
index 0000000..926b020
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.cros_platform_backend.html
@@ -0,0 +1,246 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.cros_platform_backend</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.cros_platform_backend</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/cros_platform_backend.py">telemetry/internal/platform/cros_platform_backend.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.platform.cros_device.html">telemetry.internal.platform.cros_device</a><br>
+<a href="telemetry.internal.forwarders.cros_forwarder.html">telemetry.internal.forwarders.cros_forwarder</a><br>
+<a href="telemetry.core.cros_interface.html">telemetry.core.cros_interface</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.power_monitor.cros_power_monitor.html">telemetry.internal.platform.power_monitor.cros_power_monitor</a><br>
+<a href="telemetry.internal.platform.linux_based_platform_backend.html">telemetry.internal.platform.linux_based_platform_backend</a><br>
+<a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.platform.html">telemetry.core.platform</a><br>
+<a href="telemetry.internal.util.ps_util.html">telemetry.internal.util.ps_util</a><br>
+<a href="telemetry.core.util.html">telemetry.core.util</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.linux_based_platform_backend.html#LinuxBasedPlatformBackend">telemetry.internal.platform.linux_based_platform_backend.LinuxBasedPlatformBackend</a>(<a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">telemetry.internal.platform.platform_backend.PlatformBackend</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.cros_platform_backend.html#CrosPlatformBackend">CrosPlatformBackend</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="CrosPlatformBackend">class <strong>CrosPlatformBackend</strong></a>(<a href="telemetry.internal.platform.linux_based_platform_backend.html#LinuxBasedPlatformBackend">telemetry.internal.platform.linux_based_platform_backend.LinuxBasedPlatformBackend</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.cros_platform_backend.html#CrosPlatformBackend">CrosPlatformBackend</a></dd>
+<dd><a href="telemetry.internal.platform.linux_based_platform_backend.html#LinuxBasedPlatformBackend">telemetry.internal.platform.linux_based_platform_backend.LinuxBasedPlatformBackend</a></dd>
+<dd><a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">telemetry.internal.platform.platform_backend.PlatformBackend</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="CrosPlatformBackend-CanFlushIndividualFilesFromSystemCache"><strong>CanFlushIndividualFilesFromSystemCache</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrosPlatformBackend-CanMonitorPower"><strong>CanMonitorPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrosPlatformBackend-FlushEntireSystemCache"><strong>FlushEntireSystemCache</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrosPlatformBackend-FlushSystemCacheForDirectory"><strong>FlushSystemCacheForDirectory</strong></a>(self, directory)</dt></dl>
+
+<dl><dt><a name="CrosPlatformBackend-GetChildPids"><strong>GetChildPids</strong></a>(self, pid)</dt><dd><tt>Returns&nbsp;a&nbsp;list&nbsp;of&nbsp;child&nbsp;pids&nbsp;of&nbsp;|pid|.</tt></dd></dl>
+
+<dl><dt><a name="CrosPlatformBackend-GetCommandLine"><strong>GetCommandLine</strong></a>(self, pid)</dt></dl>
+
+<dl><dt><a name="CrosPlatformBackend-GetFileContents"><strong>GetFileContents</strong></a>(self, filename)</dt></dl>
+
+<dl><dt><a name="CrosPlatformBackend-GetOSName"><strong>GetOSName</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrosPlatformBackend-GetOSVersionName"><strong>GetOSVersionName</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrosPlatformBackend-GetPsOutput"><strong>GetPsOutput</strong></a>(self, columns, pid<font color="#909090">=None</font>)</dt></dl>
+
+<dl><dt><a name="CrosPlatformBackend-GetRemotePort"><strong>GetRemotePort</strong></a>(self, port)</dt></dl>
+
+<dl><dt><a name="CrosPlatformBackend-HasBeenThermallyThrottled"><strong>HasBeenThermallyThrottled</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrosPlatformBackend-IsThermallyThrottled"><strong>IsThermallyThrottled</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrosPlatformBackend-PathExists"><strong>PathExists</strong></a>(self, path, timeout<font color="#909090">=None</font>, retries<font color="#909090">=None</font>)</dt></dl>
+
+<dl><dt><a name="CrosPlatformBackend-RunCommand"><strong>RunCommand</strong></a>(self, args)</dt></dl>
+
+<dl><dt><a name="CrosPlatformBackend-StartMonitoringPower"><strong>StartMonitoringPower</strong></a>(self, browser)</dt></dl>
+
+<dl><dt><a name="CrosPlatformBackend-StopMonitoringPower"><strong>StopMonitoringPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrosPlatformBackend-__init__"><strong>__init__</strong></a>(self, device<font color="#909090">=None</font>)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="CrosPlatformBackend-CreatePlatformForDevice"><strong>CreatePlatformForDevice</strong></a>(cls, device, finder_options)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="CrosPlatformBackend-IsPlatformBackendForHost"><strong>IsPlatformBackendForHost</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="CrosPlatformBackend-SupportsDevice"><strong>SupportsDevice</strong></a>(cls, device)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Static methods defined here:<br>
+<dl><dt><a name="CrosPlatformBackend-ParseCStateSample"><strong>ParseCStateSample</strong></a>(sample)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>cri</strong></dt>
+</dl>
+<dl><dt><strong>forwarder_factory</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.internal.platform.linux_based_platform_backend.html#LinuxBasedPlatformBackend">telemetry.internal.platform.linux_based_platform_backend.LinuxBasedPlatformBackend</a>:<br>
+<dl><dt><a name="CrosPlatformBackend-GetClockTicks"><strong>GetClockTicks</strong></a>(*args, **kwargs)</dt><dd><tt>Returns&nbsp;the&nbsp;number&nbsp;of&nbsp;clock&nbsp;ticks&nbsp;per&nbsp;second.<br>
+&nbsp;<br>
+The&nbsp;proper&nbsp;way&nbsp;is&nbsp;to&nbsp;call&nbsp;os.sysconf('SC_CLK_TCK')&nbsp;but&nbsp;that&nbsp;is&nbsp;not&nbsp;easy&nbsp;to<br>
+do&nbsp;on&nbsp;Android/CrOS.&nbsp;In&nbsp;practice,&nbsp;nearly&nbsp;all&nbsp;Linux&nbsp;machines&nbsp;have&nbsp;a&nbsp;USER_HZ<br>
+of&nbsp;100,&nbsp;so&nbsp;just&nbsp;return&nbsp;that.</tt></dd></dl>
+
+<dl><dt><a name="CrosPlatformBackend-GetCpuStats"><strong>GetCpuStats</strong></a>(self, pid)</dt></dl>
+
+<dl><dt><a name="CrosPlatformBackend-GetCpuTimestamp"><strong>GetCpuTimestamp</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrosPlatformBackend-GetMemoryStats"><strong>GetMemoryStats</strong></a>(self, pid)</dt></dl>
+
+<dl><dt><a name="CrosPlatformBackend-GetSystemCommitCharge"><strong>GetSystemCommitCharge</strong></a>(self)</dt><dd><tt>#&nbsp;Get&nbsp;the&nbsp;commit&nbsp;charge&nbsp;in&nbsp;kB.</tt></dd></dl>
+
+<dl><dt><a name="CrosPlatformBackend-GetSystemTotalPhysicalMemory"><strong>GetSystemTotalPhysicalMemory</strong></a>(*args, **kwargs)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">telemetry.internal.platform.platform_backend.PlatformBackend</a>:<br>
+<dl><dt><a name="CrosPlatformBackend-CanCaptureVideo"><strong>CanCaptureVideo</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrosPlatformBackend-CanLaunchApplication"><strong>CanLaunchApplication</strong></a>(self, application)</dt></dl>
+
+<dl><dt><a name="CrosPlatformBackend-CanMeasurePerApplicationPower"><strong>CanMeasurePerApplicationPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrosPlatformBackend-CanMonitorNetworkData"><strong>CanMonitorNetworkData</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrosPlatformBackend-CanMonitorThermalThrottling"><strong>CanMonitorThermalThrottling</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrosPlatformBackend-CanTakeScreenshot"><strong>CanTakeScreenshot</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrosPlatformBackend-CooperativelyShutdown"><strong>CooperativelyShutdown</strong></a>(self, proc, app_name)</dt><dd><tt>Cooperatively&nbsp;shut&nbsp;down&nbsp;the&nbsp;given&nbsp;process&nbsp;from&nbsp;subprocess.Popen.<br>
+&nbsp;<br>
+Currently&nbsp;this&nbsp;is&nbsp;only&nbsp;implemented&nbsp;on&nbsp;Windows.&nbsp;See<br>
+crbug.com/424024&nbsp;for&nbsp;background&nbsp;on&nbsp;why&nbsp;it&nbsp;was&nbsp;added.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;proc:&nbsp;a&nbsp;process&nbsp;object&nbsp;returned&nbsp;from&nbsp;subprocess.Popen.<br>
+&nbsp;&nbsp;app_name:&nbsp;on&nbsp;Windows,&nbsp;is&nbsp;the&nbsp;prefix&nbsp;of&nbsp;the&nbsp;application's&nbsp;window<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class&nbsp;name&nbsp;that&nbsp;should&nbsp;be&nbsp;searched&nbsp;for.&nbsp;This&nbsp;helps&nbsp;ensure<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;that&nbsp;only&nbsp;the&nbsp;application's&nbsp;windows&nbsp;are&nbsp;closed.<br>
+&nbsp;<br>
+Returns&nbsp;True&nbsp;if&nbsp;it&nbsp;is&nbsp;believed&nbsp;the&nbsp;attempt&nbsp;succeeded.</tt></dd></dl>
+
+<dl><dt><a name="CrosPlatformBackend-DidCreateBrowser"><strong>DidCreateBrowser</strong></a>(self, browser, browser_backend)</dt></dl>
+
+<dl><dt><a name="CrosPlatformBackend-DidStartBrowser"><strong>DidStartBrowser</strong></a>(self, browser, browser_backend)</dt></dl>
+
+<dl><dt><a name="CrosPlatformBackend-FlushDnsCache"><strong>FlushDnsCache</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrosPlatformBackend-GetArchName"><strong>GetArchName</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrosPlatformBackend-GetDeviceTypeName"><strong>GetDeviceTypeName</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrosPlatformBackend-GetNetworkData"><strong>GetNetworkData</strong></a>(self, browser)</dt></dl>
+
+<dl><dt><a name="CrosPlatformBackend-InitPlatformBackend"><strong>InitPlatformBackend</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrosPlatformBackend-InstallApplication"><strong>InstallApplication</strong></a>(self, application)</dt></dl>
+
+<dl><dt><a name="CrosPlatformBackend-IsApplicationRunning"><strong>IsApplicationRunning</strong></a>(self, application)</dt></dl>
+
+<dl><dt><a name="CrosPlatformBackend-IsCooperativeShutdownSupported"><strong>IsCooperativeShutdownSupported</strong></a>(self)</dt><dd><tt>Indicates&nbsp;whether&nbsp;CooperativelyShutdown,&nbsp;below,&nbsp;is&nbsp;supported.<br>
+It&nbsp;is&nbsp;not&nbsp;necessary&nbsp;to&nbsp;implement&nbsp;it&nbsp;on&nbsp;all&nbsp;platforms.</tt></dd></dl>
+
+<dl><dt><a name="CrosPlatformBackend-IsDisplayTracingSupported"><strong>IsDisplayTracingSupported</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrosPlatformBackend-LaunchApplication"><strong>LaunchApplication</strong></a>(self, application, parameters<font color="#909090">=None</font>, elevate_privilege<font color="#909090">=False</font>)</dt></dl>
+
+<dl><dt><a name="CrosPlatformBackend-PurgeUnpinnedMemory"><strong>PurgeUnpinnedMemory</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrosPlatformBackend-ReadMsr"><strong>ReadMsr</strong></a>(self, msr_number, start<font color="#909090">=0</font>, length<font color="#909090">=64</font>)</dt><dd><tt>Read&nbsp;a&nbsp;CPU&nbsp;model-specific&nbsp;register&nbsp;(MSR).<br>
+&nbsp;<br>
+Which&nbsp;MSRs&nbsp;are&nbsp;available&nbsp;depends&nbsp;on&nbsp;the&nbsp;CPU&nbsp;model.<br>
+On&nbsp;systems&nbsp;with&nbsp;multiple&nbsp;CPUs,&nbsp;this&nbsp;function&nbsp;may&nbsp;run&nbsp;on&nbsp;any&nbsp;CPU.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;msr_number:&nbsp;The&nbsp;number&nbsp;of&nbsp;the&nbsp;register&nbsp;to&nbsp;read.<br>
+&nbsp;&nbsp;start:&nbsp;The&nbsp;least&nbsp;significant&nbsp;bit&nbsp;to&nbsp;read,&nbsp;zero-indexed.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(Said&nbsp;another&nbsp;way,&nbsp;the&nbsp;number&nbsp;of&nbsp;bits&nbsp;to&nbsp;right-shift&nbsp;the&nbsp;MSR&nbsp;value.)<br>
+&nbsp;&nbsp;length:&nbsp;The&nbsp;number&nbsp;of&nbsp;bits&nbsp;to&nbsp;read.&nbsp;MSRs&nbsp;are&nbsp;64&nbsp;bits,&nbsp;even&nbsp;on&nbsp;32-bit&nbsp;CPUs.</tt></dd></dl>
+
+<dl><dt><a name="CrosPlatformBackend-SetFullPerformanceModeEnabled"><strong>SetFullPerformanceModeEnabled</strong></a>(self, enabled)</dt></dl>
+
+<dl><dt><a name="CrosPlatformBackend-SetPlatform"><strong>SetPlatform</strong></a>(self, platform)</dt></dl>
+
+<dl><dt><a name="CrosPlatformBackend-StartDisplayTracing"><strong>StartDisplayTracing</strong></a>(self)</dt><dd><tt>Start&nbsp;gathering&nbsp;a&nbsp;trace&nbsp;with&nbsp;frame&nbsp;timestamps&nbsp;close&nbsp;to&nbsp;physical<br>
+display.</tt></dd></dl>
+
+<dl><dt><a name="CrosPlatformBackend-StartVideoCapture"><strong>StartVideoCapture</strong></a>(self, min_bitrate_mbps)</dt></dl>
+
+<dl><dt><a name="CrosPlatformBackend-StopDisplayTracing"><strong>StopDisplayTracing</strong></a>(self)</dt><dd><tt>Stop&nbsp;gathering&nbsp;a&nbsp;trace&nbsp;with&nbsp;frame&nbsp;timestamps&nbsp;close&nbsp;to&nbsp;physical&nbsp;display.<br>
+&nbsp;<br>
+Returns&nbsp;a&nbsp;raw&nbsp;tracing&nbsp;events&nbsp;that&nbsp;contains&nbsp;the&nbsp;timestamps&nbsp;of&nbsp;physical<br>
+display.</tt></dd></dl>
+
+<dl><dt><a name="CrosPlatformBackend-StopVideoCapture"><strong>StopVideoCapture</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrosPlatformBackend-TakeScreenshot"><strong>TakeScreenshot</strong></a>(self, file_path)</dt></dl>
+
+<dl><dt><a name="CrosPlatformBackend-WillCloseBrowser"><strong>WillCloseBrowser</strong></a>(self, browser, browser_backend)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">telemetry.internal.platform.platform_backend.PlatformBackend</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>is_host_platform</strong></dt>
+</dl>
+<dl><dt><strong>is_video_capture_running</strong></dt>
+</dl>
+<dl><dt><strong>network_controller_backend</strong></dt>
+</dl>
+<dl><dt><strong>platform</strong></dt>
+</dl>
+<dl><dt><strong>running_browser_backends</strong></dt>
+</dl>
+<dl><dt><strong>tracing_controller_backend</strong></dt>
+</dl>
+<dl><dt><strong>wpr_ca_cert_path</strong></dt>
+</dl>
+<dl><dt><strong>wpr_http_device_port</strong></dt>
+</dl>
+<dl><dt><strong>wpr_https_device_port</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.desktop_device.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.desktop_device.html
new file mode 100644
index 0000000..8f6cfc8
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.desktop_device.html
@@ -0,0 +1,81 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.desktop_device</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.desktop_device</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/desktop_device.py">telemetry/internal/platform/desktop_device.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.platform.device.html">telemetry.internal.platform.device</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.platform.html">telemetry.core.platform</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.device.html#Device">telemetry.internal.platform.device.Device</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.desktop_device.html#DesktopDevice">DesktopDevice</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="DesktopDevice">class <strong>DesktopDevice</strong></a>(<a href="telemetry.internal.platform.device.html#Device">telemetry.internal.platform.device.Device</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.desktop_device.html#DesktopDevice">DesktopDevice</a></dd>
+<dd><a href="telemetry.internal.platform.device.html#Device">telemetry.internal.platform.device.Device</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="DesktopDevice-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="DesktopDevice-GetAllConnectedDevices"><strong>GetAllConnectedDevices</strong></a>(cls, blacklist)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.device.html#Device">telemetry.internal.platform.device.Device</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>guid</strong></dt>
+</dl>
+<dl><dt><strong>name</strong></dt>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-FindAllAvailableDevices"><strong>FindAllAvailableDevices</strong></a>(_)</dt><dd><tt>Returns&nbsp;a&nbsp;list&nbsp;of&nbsp;available&nbsp;devices.</tt></dd></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.desktop_platform_backend.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.desktop_platform_backend.html
new file mode 100644
index 0000000..fe3c387
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.desktop_platform_backend.html
@@ -0,0 +1,233 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.desktop_platform_backend</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.desktop_platform_backend</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/desktop_platform_backend.py">telemetry/internal/platform/desktop_platform_backend.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.util.binary_manager.html">telemetry.internal.util.binary_manager</a><br>
+</td><td width="25%" valign=top><a href="os.html">os</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.platform_backend.html">telemetry.internal.platform.platform_backend</a><br>
+</td><td width="25%" valign=top><a href="subprocess.html">subprocess</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">telemetry.internal.platform.platform_backend.PlatformBackend</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.desktop_platform_backend.html#DesktopPlatformBackend">DesktopPlatformBackend</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="DesktopPlatformBackend">class <strong>DesktopPlatformBackend</strong></a>(<a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">telemetry.internal.platform.platform_backend.PlatformBackend</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.desktop_platform_backend.html#DesktopPlatformBackend">DesktopPlatformBackend</a></dd>
+<dd><a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">telemetry.internal.platform.platform_backend.PlatformBackend</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="DesktopPlatformBackend-FlushSystemCacheForDirectory"><strong>FlushSystemCacheForDirectory</strong></a>(self, directory)</dt></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-GetDeviceTypeName"><strong>GetDeviceTypeName</strong></a>(self)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">telemetry.internal.platform.platform_backend.PlatformBackend</a>:<br>
+<dl><dt><a name="DesktopPlatformBackend-CanCaptureVideo"><strong>CanCaptureVideo</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-CanFlushIndividualFilesFromSystemCache"><strong>CanFlushIndividualFilesFromSystemCache</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-CanLaunchApplication"><strong>CanLaunchApplication</strong></a>(self, application)</dt></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-CanMeasurePerApplicationPower"><strong>CanMeasurePerApplicationPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-CanMonitorNetworkData"><strong>CanMonitorNetworkData</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-CanMonitorPower"><strong>CanMonitorPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-CanMonitorThermalThrottling"><strong>CanMonitorThermalThrottling</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-CanTakeScreenshot"><strong>CanTakeScreenshot</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-CooperativelyShutdown"><strong>CooperativelyShutdown</strong></a>(self, proc, app_name)</dt><dd><tt>Cooperatively&nbsp;shut&nbsp;down&nbsp;the&nbsp;given&nbsp;process&nbsp;from&nbsp;subprocess.Popen.<br>
+&nbsp;<br>
+Currently&nbsp;this&nbsp;is&nbsp;only&nbsp;implemented&nbsp;on&nbsp;Windows.&nbsp;See<br>
+crbug.com/424024&nbsp;for&nbsp;background&nbsp;on&nbsp;why&nbsp;it&nbsp;was&nbsp;added.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;proc:&nbsp;a&nbsp;process&nbsp;object&nbsp;returned&nbsp;from&nbsp;subprocess.Popen.<br>
+&nbsp;&nbsp;app_name:&nbsp;on&nbsp;Windows,&nbsp;is&nbsp;the&nbsp;prefix&nbsp;of&nbsp;the&nbsp;application's&nbsp;window<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class&nbsp;name&nbsp;that&nbsp;should&nbsp;be&nbsp;searched&nbsp;for.&nbsp;This&nbsp;helps&nbsp;ensure<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;that&nbsp;only&nbsp;the&nbsp;application's&nbsp;windows&nbsp;are&nbsp;closed.<br>
+&nbsp;<br>
+Returns&nbsp;True&nbsp;if&nbsp;it&nbsp;is&nbsp;believed&nbsp;the&nbsp;attempt&nbsp;succeeded.</tt></dd></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-DidCreateBrowser"><strong>DidCreateBrowser</strong></a>(self, browser, browser_backend)</dt></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-DidStartBrowser"><strong>DidStartBrowser</strong></a>(self, browser, browser_backend)</dt></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-FlushDnsCache"><strong>FlushDnsCache</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-FlushEntireSystemCache"><strong>FlushEntireSystemCache</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-GetArchName"><strong>GetArchName</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-GetChildPids"><strong>GetChildPids</strong></a>(self, pid)</dt></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-GetCommandLine"><strong>GetCommandLine</strong></a>(self, pid)</dt></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-GetCpuStats"><strong>GetCpuStats</strong></a>(self, pid)</dt></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-GetCpuTimestamp"><strong>GetCpuTimestamp</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-GetMemoryStats"><strong>GetMemoryStats</strong></a>(self, pid)</dt></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-GetNetworkData"><strong>GetNetworkData</strong></a>(self, browser)</dt></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-GetOSName"><strong>GetOSName</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-GetOSVersionName"><strong>GetOSVersionName</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-GetRemotePort"><strong>GetRemotePort</strong></a>(self, port)</dt></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-GetSystemCommitCharge"><strong>GetSystemCommitCharge</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-GetSystemTotalPhysicalMemory"><strong>GetSystemTotalPhysicalMemory</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-HasBeenThermallyThrottled"><strong>HasBeenThermallyThrottled</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-InitPlatformBackend"><strong>InitPlatformBackend</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-InstallApplication"><strong>InstallApplication</strong></a>(self, application)</dt></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-IsApplicationRunning"><strong>IsApplicationRunning</strong></a>(self, application)</dt></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-IsCooperativeShutdownSupported"><strong>IsCooperativeShutdownSupported</strong></a>(self)</dt><dd><tt>Indicates&nbsp;whether&nbsp;CooperativelyShutdown,&nbsp;below,&nbsp;is&nbsp;supported.<br>
+It&nbsp;is&nbsp;not&nbsp;necessary&nbsp;to&nbsp;implement&nbsp;it&nbsp;on&nbsp;all&nbsp;platforms.</tt></dd></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-IsDisplayTracingSupported"><strong>IsDisplayTracingSupported</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-IsThermallyThrottled"><strong>IsThermallyThrottled</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-LaunchApplication"><strong>LaunchApplication</strong></a>(self, application, parameters<font color="#909090">=None</font>, elevate_privilege<font color="#909090">=False</font>)</dt></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-PathExists"><strong>PathExists</strong></a>(self, path, timeout<font color="#909090">=None</font>, retries<font color="#909090">=None</font>)</dt><dd><tt>Tests&nbsp;whether&nbsp;the&nbsp;given&nbsp;path&nbsp;exists&nbsp;on&nbsp;the&nbsp;target&nbsp;platform.<br>
+Args:<br>
+&nbsp;&nbsp;path:&nbsp;path&nbsp;in&nbsp;request.<br>
+&nbsp;&nbsp;timeout:&nbsp;timeout.<br>
+&nbsp;&nbsp;retries:&nbsp;num&nbsp;of&nbsp;retries.<br>
+Return:<br>
+&nbsp;&nbsp;Whether&nbsp;the&nbsp;path&nbsp;exists&nbsp;on&nbsp;the&nbsp;target&nbsp;platform.</tt></dd></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-PurgeUnpinnedMemory"><strong>PurgeUnpinnedMemory</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-ReadMsr"><strong>ReadMsr</strong></a>(self, msr_number, start<font color="#909090">=0</font>, length<font color="#909090">=64</font>)</dt><dd><tt>Read&nbsp;a&nbsp;CPU&nbsp;model-specific&nbsp;register&nbsp;(MSR).<br>
+&nbsp;<br>
+Which&nbsp;MSRs&nbsp;are&nbsp;available&nbsp;depends&nbsp;on&nbsp;the&nbsp;CPU&nbsp;model.<br>
+On&nbsp;systems&nbsp;with&nbsp;multiple&nbsp;CPUs,&nbsp;this&nbsp;function&nbsp;may&nbsp;run&nbsp;on&nbsp;any&nbsp;CPU.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;msr_number:&nbsp;The&nbsp;number&nbsp;of&nbsp;the&nbsp;register&nbsp;to&nbsp;read.<br>
+&nbsp;&nbsp;start:&nbsp;The&nbsp;least&nbsp;significant&nbsp;bit&nbsp;to&nbsp;read,&nbsp;zero-indexed.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(Said&nbsp;another&nbsp;way,&nbsp;the&nbsp;number&nbsp;of&nbsp;bits&nbsp;to&nbsp;right-shift&nbsp;the&nbsp;MSR&nbsp;value.)<br>
+&nbsp;&nbsp;length:&nbsp;The&nbsp;number&nbsp;of&nbsp;bits&nbsp;to&nbsp;read.&nbsp;MSRs&nbsp;are&nbsp;64&nbsp;bits,&nbsp;even&nbsp;on&nbsp;32-bit&nbsp;CPUs.</tt></dd></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-SetFullPerformanceModeEnabled"><strong>SetFullPerformanceModeEnabled</strong></a>(self, enabled)</dt></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-SetPlatform"><strong>SetPlatform</strong></a>(self, platform)</dt></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-StartDisplayTracing"><strong>StartDisplayTracing</strong></a>(self)</dt><dd><tt>Start&nbsp;gathering&nbsp;a&nbsp;trace&nbsp;with&nbsp;frame&nbsp;timestamps&nbsp;close&nbsp;to&nbsp;physical<br>
+display.</tt></dd></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-StartMonitoringPower"><strong>StartMonitoringPower</strong></a>(self, browser)</dt></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-StartVideoCapture"><strong>StartVideoCapture</strong></a>(self, min_bitrate_mbps)</dt></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-StopDisplayTracing"><strong>StopDisplayTracing</strong></a>(self)</dt><dd><tt>Stop&nbsp;gathering&nbsp;a&nbsp;trace&nbsp;with&nbsp;frame&nbsp;timestamps&nbsp;close&nbsp;to&nbsp;physical&nbsp;display.<br>
+&nbsp;<br>
+Returns&nbsp;a&nbsp;raw&nbsp;tracing&nbsp;events&nbsp;that&nbsp;contains&nbsp;the&nbsp;timestamps&nbsp;of&nbsp;physical<br>
+display.</tt></dd></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-StopMonitoringPower"><strong>StopMonitoringPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-StopVideoCapture"><strong>StopVideoCapture</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-TakeScreenshot"><strong>TakeScreenshot</strong></a>(self, file_path)</dt></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-WillCloseBrowser"><strong>WillCloseBrowser</strong></a>(self, browser, browser_backend)</dt></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-__init__"><strong>__init__</strong></a>(self, device<font color="#909090">=None</font>)</dt><dd><tt>Initalize&nbsp;an&nbsp;instance&nbsp;of&nbsp;<a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">PlatformBackend</a>&nbsp;from&nbsp;a&nbsp;device&nbsp;optionally.<br>
+Call&nbsp;sites&nbsp;need&nbsp;to&nbsp;use&nbsp;SupportsDevice&nbsp;before&nbsp;intialization&nbsp;to&nbsp;check<br>
+whether&nbsp;this&nbsp;platform&nbsp;backend&nbsp;supports&nbsp;the&nbsp;device.<br>
+If&nbsp;device&nbsp;is&nbsp;None,&nbsp;this&nbsp;constructor&nbsp;returns&nbsp;the&nbsp;host&nbsp;platform&nbsp;backend<br>
+which&nbsp;telemetry&nbsp;is&nbsp;running&nbsp;on.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;device:&nbsp;an&nbsp;instance&nbsp;of&nbsp;telemetry.core.platform.device.Device.</tt></dd></dl>
+
+<hr>
+Class methods inherited from <a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">telemetry.internal.platform.platform_backend.PlatformBackend</a>:<br>
+<dl><dt><a name="DesktopPlatformBackend-CreatePlatformForDevice"><strong>CreatePlatformForDevice</strong></a>(cls, device, finder_options)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-IsPlatformBackendForHost"><strong>IsPlatformBackendForHost</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Returns&nbsp;whether&nbsp;this&nbsp;platform&nbsp;backend&nbsp;is&nbsp;the&nbsp;platform&nbsp;backend&nbsp;to&nbsp;be&nbsp;used<br>
+for&nbsp;the&nbsp;host&nbsp;device&nbsp;which&nbsp;telemetry&nbsp;is&nbsp;running&nbsp;on.</tt></dd></dl>
+
+<dl><dt><a name="DesktopPlatformBackend-SupportsDevice"><strong>SupportsDevice</strong></a>(cls, device)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Returns&nbsp;whether&nbsp;this&nbsp;platform&nbsp;backend&nbsp;supports&nbsp;intialization&nbsp;from&nbsp;the<br>
+device.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">telemetry.internal.platform.platform_backend.PlatformBackend</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>forwarder_factory</strong></dt>
+</dl>
+<dl><dt><strong>is_host_platform</strong></dt>
+</dl>
+<dl><dt><strong>is_video_capture_running</strong></dt>
+</dl>
+<dl><dt><strong>network_controller_backend</strong></dt>
+</dl>
+<dl><dt><strong>platform</strong></dt>
+</dl>
+<dl><dt><strong>running_browser_backends</strong></dt>
+</dl>
+<dl><dt><strong>tracing_controller_backend</strong></dt>
+</dl>
+<dl><dt><strong>wpr_ca_cert_path</strong></dt>
+</dl>
+<dl><dt><strong>wpr_http_device_port</strong></dt>
+</dl>
+<dl><dt><strong>wpr_https_device_port</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.device.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.device.html
new file mode 100644
index 0000000..e8459be
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.device.html
@@ -0,0 +1,68 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.device</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.device</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/device.py">telemetry/internal/platform/device.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.device.html#Device">Device</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="Device">class <strong>Device</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;base&nbsp;class&nbsp;of&nbsp;devices.<br>
+A&nbsp;device&nbsp;instance&nbsp;contains&nbsp;all&nbsp;the&nbsp;necessary&nbsp;information&nbsp;for&nbsp;constructing<br>
+a&nbsp;platform&nbsp;backend&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;for&nbsp;remote&nbsp;platforms.<br>
+&nbsp;<br>
+Attributes:<br>
+&nbsp;&nbsp;name:&nbsp;A&nbsp;device&nbsp;name&nbsp;string&nbsp;in&nbsp;human-understandable&nbsp;term.<br>
+&nbsp;&nbsp;guid:&nbsp;A&nbsp;unique&nbsp;id&nbsp;of&nbsp;the&nbsp;device.&nbsp;Subclass&nbsp;of&nbsp;device&nbsp;must&nbsp;specify&nbsp;this<br>
+&nbsp;&nbsp;&nbsp;&nbsp;id&nbsp;properly&nbsp;so&nbsp;that&nbsp;device&nbsp;objects&nbsp;to&nbsp;a&nbsp;same&nbsp;actual&nbsp;device&nbsp;must&nbsp;have&nbsp;same<br>
+&nbsp;&nbsp;&nbsp;&nbsp;guid.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="Device-__init__"><strong>__init__</strong></a>(self, name, guid)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="Device-GetAllConnectedDevices"><strong>GetAllConnectedDevices</strong></a>(cls, blacklist)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>guid</strong></dt>
+</dl>
+<dl><dt><strong>name</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.device_finder.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.device_finder.html
new file mode 100644
index 0000000..3a37b75
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.device_finder.html
@@ -0,0 +1,42 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.device_finder</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.device_finder</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/device_finder.py">telemetry/internal/platform/device_finder.py</a></font></td></tr></table>
+    <p><tt>Finds&nbsp;devices&nbsp;that&nbsp;can&nbsp;be&nbsp;controlled&nbsp;by&nbsp;telemetry.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.platform.android_device.html">telemetry.internal.platform.android_device</a><br>
+<a href="telemetry.internal.platform.cros_device.html">telemetry.internal.platform.cros_device</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.desktop_device.html">telemetry.internal.platform.desktop_device</a><br>
+<a href="telemetry.internal.platform.ios_device.html">telemetry.internal.platform.ios_device</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.trybot_device.html">telemetry.internal.platform.trybot_device</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-GetDevicesMatchingOptions"><strong>GetDevicesMatchingOptions</strong></a>(options)</dt><dd><tt>Returns&nbsp;a&nbsp;list&nbsp;of&nbsp;devices&nbsp;matching&nbsp;the&nbsp;options.</tt></dd></dl>
+</td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>DEVICES</strong> = [&lt;module 'telemetry.internal.platform.android_dev.../telemetry/internal/platform/android_device.pyc'&gt;, &lt;module 'telemetry.internal.platform.cros_device...try/telemetry/internal/platform/cros_device.pyc'&gt;, &lt;module 'telemetry.internal.platform.desktop_dev.../telemetry/internal/platform/desktop_device.pyc'&gt;, &lt;module 'telemetry.internal.platform.ios_device'...etry/telemetry/internal/platform/ios_device.pyc'&gt;, &lt;module 'telemetry.internal.platform.trybot_devi...y/telemetry/internal/platform/trybot_device.pyc'&gt;]</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.gpu_device.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.gpu_device.html
new file mode 100644
index 0000000..34f3fc9
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.gpu_device.html
@@ -0,0 +1,96 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.gpu_device</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.gpu_device</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/gpu_device.py">telemetry/internal/platform/gpu_device.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.gpu_device.html#GPUDevice">GPUDevice</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="GPUDevice">class <strong>GPUDevice</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Provides&nbsp;information&nbsp;about&nbsp;an&nbsp;individual&nbsp;GPU&nbsp;device.<br>
+&nbsp;<br>
+On&nbsp;platforms&nbsp;which&nbsp;support&nbsp;them,&nbsp;the&nbsp;vendor_id&nbsp;and&nbsp;device_id&nbsp;are<br>
+PCI&nbsp;IDs.&nbsp;On&nbsp;other&nbsp;platforms,&nbsp;the&nbsp;vendor_string&nbsp;and&nbsp;device_string<br>
+are&nbsp;platform-dependent&nbsp;strings.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="GPUDevice-__init__"><strong>__init__</strong></a>(self, vendor_id, device_id, vendor_string, device_string)</dt></dl>
+
+<dl><dt><a name="GPUDevice-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="GPUDevice-FromDict"><strong>FromDict</strong></a>(cls, attrs)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Constructs&nbsp;a&nbsp;<a href="#GPUDevice">GPUDevice</a>&nbsp;from&nbsp;a&nbsp;dictionary.&nbsp;Requires&nbsp;the<br>
+following&nbsp;attributes&nbsp;to&nbsp;be&nbsp;present&nbsp;in&nbsp;the&nbsp;dictionary:<br>
+&nbsp;<br>
+&nbsp;&nbsp;vendor_id<br>
+&nbsp;&nbsp;device_id<br>
+&nbsp;&nbsp;vendor_string<br>
+&nbsp;&nbsp;device_string<br>
+&nbsp;<br>
+Raises&nbsp;an&nbsp;exception&nbsp;if&nbsp;any&nbsp;attributes&nbsp;are&nbsp;missing.</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>device_id</strong></dt>
+<dd><tt>The&nbsp;GPU&nbsp;device's&nbsp;PCI&nbsp;ID&nbsp;as&nbsp;a&nbsp;number,&nbsp;or&nbsp;0&nbsp;if&nbsp;not&nbsp;available.<br>
+&nbsp;<br>
+Most&nbsp;desktop&nbsp;machines&nbsp;supply&nbsp;this&nbsp;information&nbsp;rather&nbsp;than&nbsp;the<br>
+vendor&nbsp;and&nbsp;device&nbsp;strings.</tt></dd>
+</dl>
+<dl><dt><strong>device_string</strong></dt>
+<dd><tt>The&nbsp;GPU&nbsp;device's&nbsp;name&nbsp;as&nbsp;a&nbsp;string,&nbsp;or&nbsp;the&nbsp;empty&nbsp;string&nbsp;if&nbsp;not<br>
+available.<br>
+&nbsp;<br>
+Most&nbsp;mobile&nbsp;devices&nbsp;supply&nbsp;this&nbsp;information&nbsp;rather&nbsp;than&nbsp;the&nbsp;PCI<br>
+IDs.</tt></dd>
+</dl>
+<dl><dt><strong>vendor_id</strong></dt>
+<dd><tt>The&nbsp;GPU&nbsp;vendor's&nbsp;PCI&nbsp;ID&nbsp;as&nbsp;a&nbsp;number,&nbsp;or&nbsp;0&nbsp;if&nbsp;not&nbsp;available.<br>
+&nbsp;<br>
+Most&nbsp;desktop&nbsp;machines&nbsp;supply&nbsp;this&nbsp;information&nbsp;rather&nbsp;than&nbsp;the<br>
+vendor&nbsp;and&nbsp;device&nbsp;strings.</tt></dd>
+</dl>
+<dl><dt><strong>vendor_string</strong></dt>
+<dd><tt>The&nbsp;GPU&nbsp;vendor's&nbsp;name&nbsp;as&nbsp;a&nbsp;string,&nbsp;or&nbsp;the&nbsp;empty&nbsp;string&nbsp;if&nbsp;not<br>
+available.<br>
+&nbsp;<br>
+Most&nbsp;mobile&nbsp;devices&nbsp;supply&nbsp;this&nbsp;information&nbsp;rather&nbsp;than&nbsp;the&nbsp;PCI<br>
+IDs.</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.gpu_info.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.gpu_info.html
new file mode 100644
index 0000000..45b8d04
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.gpu_info.html
@@ -0,0 +1,93 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.gpu_info</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.gpu_info</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/gpu_info.py">telemetry/internal/platform/gpu_info.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.platform.gpu_device.html">telemetry.internal.platform.gpu_device</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.gpu_info.html#GPUInfo">GPUInfo</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="GPUInfo">class <strong>GPUInfo</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Provides&nbsp;information&nbsp;about&nbsp;the&nbsp;GPUs&nbsp;on&nbsp;the&nbsp;system.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="GPUInfo-__init__"><strong>__init__</strong></a>(self, device_array, aux_attributes, feature_status, driver_bug_workarounds)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="GPUInfo-FromDict"><strong>FromDict</strong></a>(cls, attrs)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Constructs&nbsp;a&nbsp;<a href="#GPUInfo">GPUInfo</a>&nbsp;from&nbsp;a&nbsp;dictionary&nbsp;of&nbsp;attributes.<br>
+&nbsp;<br>
+Attributes&nbsp;currently&nbsp;required&nbsp;to&nbsp;be&nbsp;present&nbsp;in&nbsp;the&nbsp;dictionary:<br>
+&nbsp;&nbsp;devices&nbsp;(array&nbsp;of&nbsp;dictionaries,&nbsp;each&nbsp;of&nbsp;which&nbsp;contains<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GPUDevice's&nbsp;required&nbsp;attributes)</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>aux_attributes</strong></dt>
+<dd><tt>Returns&nbsp;a&nbsp;dictionary&nbsp;of&nbsp;auxiliary,&nbsp;optional,&nbsp;attributes.<br>
+&nbsp;<br>
+On&nbsp;the&nbsp;Chrome&nbsp;browser,&nbsp;for&nbsp;example,&nbsp;this&nbsp;dictionary&nbsp;contains:<br>
+&nbsp;&nbsp;optimus&nbsp;(boolean)<br>
+&nbsp;&nbsp;amd_switchable&nbsp;(boolean)<br>
+&nbsp;&nbsp;lenovo_dcute&nbsp;(boolean)<br>
+&nbsp;&nbsp;driver_vendor&nbsp;(string)<br>
+&nbsp;&nbsp;driver_version&nbsp;(string)<br>
+&nbsp;&nbsp;driver_date&nbsp;(string)<br>
+&nbsp;&nbsp;gl_version_string&nbsp;(string)<br>
+&nbsp;&nbsp;gl_vendor&nbsp;(string)<br>
+&nbsp;&nbsp;gl_renderer&nbsp;(string)<br>
+&nbsp;&nbsp;gl_extensions&nbsp;(string)<br>
+&nbsp;&nbsp;display_link_version&nbsp;(string)</tt></dd>
+</dl>
+<dl><dt><strong>devices</strong></dt>
+<dd><tt>An&nbsp;array&nbsp;of&nbsp;GPUDevices.&nbsp;Element&nbsp;0&nbsp;is&nbsp;the&nbsp;primary&nbsp;GPU&nbsp;on&nbsp;the&nbsp;system.</tt></dd>
+</dl>
+<dl><dt><strong>driver_bug_workarounds</strong></dt>
+<dd><tt>Returns&nbsp;an&nbsp;optional&nbsp;array&nbsp;of&nbsp;driver&nbsp;bug&nbsp;workarounds.</tt></dd>
+</dl>
+<dl><dt><strong>feature_status</strong></dt>
+<dd><tt>Returns&nbsp;an&nbsp;optional&nbsp;dictionary&nbsp;of&nbsp;graphics&nbsp;features&nbsp;and&nbsp;their&nbsp;status.</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.html
new file mode 100644
index 0000000..2e717fd
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.html
@@ -0,0 +1,63 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: package telemetry.internal.platform</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.platform</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/__init__.py">telemetry/internal/platform/__init__.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2015&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Package Contents</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.platform.android_device.html">android_device</a><br>
+<a href="telemetry.internal.platform.android_device_unittest.html">android_device_unittest</a><br>
+<a href="telemetry.internal.platform.android_platform_backend.html">android_platform_backend</a><br>
+<a href="telemetry.internal.platform.android_platform_backend_unittest.html">android_platform_backend_unittest</a><br>
+<a href="telemetry.internal.platform.cros_device.html">cros_device</a><br>
+<a href="telemetry.internal.platform.cros_platform_backend.html">cros_platform_backend</a><br>
+<a href="telemetry.internal.platform.cros_platform_backend_unittest.html">cros_platform_backend_unittest</a><br>
+<a href="telemetry.internal.platform.desktop_device.html">desktop_device</a><br>
+<a href="telemetry.internal.platform.desktop_platform_backend.html">desktop_platform_backend</a><br>
+<a href="telemetry.internal.platform.device.html">device</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.device_finder.html">device_finder</a><br>
+<a href="telemetry.internal.platform.gpu_device.html">gpu_device</a><br>
+<a href="telemetry.internal.platform.gpu_device_unittest.html">gpu_device_unittest</a><br>
+<a href="telemetry.internal.platform.gpu_info.html">gpu_info</a><br>
+<a href="telemetry.internal.platform.gpu_info_unittest.html">gpu_info_unittest</a><br>
+<a href="telemetry.internal.platform.ios_device.html">ios_device</a><br>
+<a href="telemetry.internal.platform.ios_platform_backend.html">ios_platform_backend</a><br>
+<a href="telemetry.internal.platform.linux_based_platform_backend.html">linux_based_platform_backend</a><br>
+<a href="telemetry.internal.platform.linux_based_platform_backend_unittest.html">linux_based_platform_backend_unittest</a><br>
+<a href="telemetry.internal.platform.linux_platform_backend.html">linux_platform_backend</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.linux_platform_backend_unittest.html">linux_platform_backend_unittest</a><br>
+<a href="telemetry.internal.platform.mac_platform_backend.html">mac_platform_backend</a><br>
+<a href="telemetry.internal.platform.mac_platform_backend_unittest.html">mac_platform_backend_unittest</a><br>
+<a href="telemetry.internal.platform.msr_server_win.html">msr_server_win</a><br>
+<a href="telemetry.internal.platform.network_controller_backend.html">network_controller_backend</a><br>
+<a href="telemetry.internal.platform.network_controller_backend_unittest.html">network_controller_backend_unittest</a><br>
+<a href="telemetry.internal.platform.platform_backend.html">platform_backend</a><br>
+<a href="telemetry.internal.platform.platform_backend_unittest.html">platform_backend_unittest</a><br>
+<a href="telemetry.internal.platform.posix_platform_backend.html">posix_platform_backend</a><br>
+<a href="telemetry.internal.platform.posix_platform_backend_unittest.html">posix_platform_backend_unittest</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.power_monitor.html"><strong>power_monitor</strong>&nbsp;(package)</a><br>
+<a href="telemetry.internal.platform.profiler.html"><strong>profiler</strong>&nbsp;(package)</a><br>
+<a href="telemetry.internal.platform.profiling_controller_backend.html">profiling_controller_backend</a><br>
+<a href="telemetry.internal.platform.system_info.html">system_info</a><br>
+<a href="telemetry.internal.platform.system_info_unittest.html">system_info_unittest</a><br>
+<a href="telemetry.internal.platform.tracing_agent.html"><strong>tracing_agent</strong>&nbsp;(package)</a><br>
+<a href="telemetry.internal.platform.tracing_controller_backend.html">tracing_controller_backend</a><br>
+<a href="telemetry.internal.platform.trybot_device.html">trybot_device</a><br>
+<a href="telemetry.internal.platform.win_platform_backend.html">win_platform_backend</a><br>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.ios_device.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.ios_device.html
new file mode 100644
index 0000000..679e5e8
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.ios_device.html
@@ -0,0 +1,92 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.ios_device</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.ios_device</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/ios_device.py">telemetry/internal/platform/ios_device.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.platform.device.html">telemetry.internal.platform.device</a><br>
+<a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top><a href="os.html">os</a><br>
+<a href="telemetry.core.platform.html">telemetry.core.platform</a><br>
+</td><td width="25%" valign=top><a href="re.html">re</a><br>
+<a href="subprocess.html">subprocess</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.device.html#Device">telemetry.internal.platform.device.Device</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.ios_device.html#IOSDevice">IOSDevice</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="IOSDevice">class <strong>IOSDevice</strong></a>(<a href="telemetry.internal.platform.device.html#Device">telemetry.internal.platform.device.Device</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.ios_device.html#IOSDevice">IOSDevice</a></dd>
+<dd><a href="telemetry.internal.platform.device.html#Device">telemetry.internal.platform.device.Device</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="IOSDevice-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="IOSDevice-GetAllConnectedDevices"><strong>GetAllConnectedDevices</strong></a>(cls, blacklist)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.device.html#Device">telemetry.internal.platform.device.Device</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>guid</strong></dt>
+</dl>
+<dl><dt><strong>name</strong></dt>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-FindAllAvailableDevices"><strong>FindAllAvailableDevices</strong></a>(options)</dt><dd><tt>Returns&nbsp;a&nbsp;list&nbsp;of&nbsp;available&nbsp;devices.</tt></dd></dl>
+</td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>IOSSIM_BUILD_DIRECTORIES</strong> = ['Debug-iphonesimulator', 'Profile-iphonesimulator', 'Release-iphonesimulator']</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.ios_platform_backend.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.ios_platform_backend.html
new file mode 100644
index 0000000..41566e8
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.ios_platform_backend.html
@@ -0,0 +1,244 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.ios_platform_backend</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.ios_platform_backend</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/ios_platform_backend.py">telemetry/internal/platform/ios_platform_backend.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.posix_platform_backend.html">telemetry.internal.platform.posix_platform_backend</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.posix_platform_backend.html#PosixPlatformBackend">telemetry.internal.platform.posix_platform_backend.PosixPlatformBackend</a>(<a href="telemetry.internal.platform.desktop_platform_backend.html#DesktopPlatformBackend">telemetry.internal.platform.desktop_platform_backend.DesktopPlatformBackend</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.ios_platform_backend.html#IosPlatformBackend">IosPlatformBackend</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="IosPlatformBackend">class <strong>IosPlatformBackend</strong></a>(<a href="telemetry.internal.platform.posix_platform_backend.html#PosixPlatformBackend">telemetry.internal.platform.posix_platform_backend.PosixPlatformBackend</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>#TODO(baxley):&nbsp;Put&nbsp;in&nbsp;real&nbsp;values.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.ios_platform_backend.html#IosPlatformBackend">IosPlatformBackend</a></dd>
+<dd><a href="telemetry.internal.platform.posix_platform_backend.html#PosixPlatformBackend">telemetry.internal.platform.posix_platform_backend.PosixPlatformBackend</a></dd>
+<dd><a href="telemetry.internal.platform.desktop_platform_backend.html#DesktopPlatformBackend">telemetry.internal.platform.desktop_platform_backend.DesktopPlatformBackend</a></dd>
+<dd><a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">telemetry.internal.platform.platform_backend.PlatformBackend</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="IosPlatformBackend-CanMonitorPower"><strong>CanMonitorPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="IosPlatformBackend-CanMonitorThermalThrottling"><strong>CanMonitorThermalThrottling</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="IosPlatformBackend-FlushDnsCache"><strong>FlushDnsCache</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="IosPlatformBackend-FlushEntireSystemCache"><strong>FlushEntireSystemCache</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="IosPlatformBackend-GetOSName"><strong>GetOSName</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="IosPlatformBackend-GetOSVersionName"><strong>GetOSVersionName</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="IosPlatformBackend-GetSystemTotalPhysicalMemory"><strong>GetSystemTotalPhysicalMemory</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="IosPlatformBackend-HasBeenThermallyThrottled"><strong>HasBeenThermallyThrottled</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="IosPlatformBackend-InstallApplication"><strong>InstallApplication</strong></a>(self, application)</dt></dl>
+
+<dl><dt><a name="IosPlatformBackend-IsThermallyThrottled"><strong>IsThermallyThrottled</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="IosPlatformBackend-SetFullPerformanceModeEnabled"><strong>SetFullPerformanceModeEnabled</strong></a>(self, enabled)</dt></dl>
+
+<dl><dt><a name="IosPlatformBackend-StartMonitoringPower"><strong>StartMonitoringPower</strong></a>(self, browser)</dt></dl>
+
+<dl><dt><a name="IosPlatformBackend-StopMonitoringPower"><strong>StopMonitoringPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="IosPlatformBackend-StopVideoCapture"><strong>StopVideoCapture</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="IosPlatformBackend-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.platform.posix_platform_backend.html#PosixPlatformBackend">telemetry.internal.platform.posix_platform_backend.PosixPlatformBackend</a>:<br>
+<dl><dt><a name="IosPlatformBackend-CanLaunchApplication"><strong>CanLaunchApplication</strong></a>(self, application)</dt></dl>
+
+<dl><dt><a name="IosPlatformBackend-GetChildPids"><strong>GetChildPids</strong></a>(self, pid)</dt><dd><tt>Returns&nbsp;a&nbsp;list&nbsp;of&nbsp;child&nbsp;pids&nbsp;of&nbsp;|pid|.</tt></dd></dl>
+
+<dl><dt><a name="IosPlatformBackend-GetCommandLine"><strong>GetCommandLine</strong></a>(self, pid)</dt></dl>
+
+<dl><dt><a name="IosPlatformBackend-GetFileContents"><strong>GetFileContents</strong></a>(self, path)</dt></dl>
+
+<dl><dt><a name="IosPlatformBackend-GetPsOutput"><strong>GetPsOutput</strong></a>(self, columns, pid<font color="#909090">=None</font>)</dt><dd><tt>Returns&nbsp;output&nbsp;of&nbsp;the&nbsp;'ps'&nbsp;command&nbsp;as&nbsp;a&nbsp;list&nbsp;of&nbsp;lines.<br>
+Subclass&nbsp;should&nbsp;override&nbsp;this&nbsp;function.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;columns:&nbsp;A&nbsp;list&nbsp;of&nbsp;require&nbsp;columns,&nbsp;e.g.,&nbsp;['pid',&nbsp;'pss'].<br>
+&nbsp;&nbsp;pid:&nbsp;If&nbsp;not&nbsp;None,&nbsp;returns&nbsp;only&nbsp;the&nbsp;information&nbsp;of&nbsp;the&nbsp;process<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;with&nbsp;the&nbsp;pid.</tt></dd></dl>
+
+<dl><dt><a name="IosPlatformBackend-IsApplicationRunning"><strong>IsApplicationRunning</strong></a>(self, application)</dt></dl>
+
+<dl><dt><a name="IosPlatformBackend-LaunchApplication"><strong>LaunchApplication</strong></a>(self, application, parameters<font color="#909090">=None</font>, elevate_privilege<font color="#909090">=False</font>)</dt></dl>
+
+<dl><dt><a name="IosPlatformBackend-RunCommand"><strong>RunCommand</strong></a>(self, args)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.platform.desktop_platform_backend.html#DesktopPlatformBackend">telemetry.internal.platform.desktop_platform_backend.DesktopPlatformBackend</a>:<br>
+<dl><dt><a name="IosPlatformBackend-FlushSystemCacheForDirectory"><strong>FlushSystemCacheForDirectory</strong></a>(self, directory)</dt></dl>
+
+<dl><dt><a name="IosPlatformBackend-GetDeviceTypeName"><strong>GetDeviceTypeName</strong></a>(self)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">telemetry.internal.platform.platform_backend.PlatformBackend</a>:<br>
+<dl><dt><a name="IosPlatformBackend-CanCaptureVideo"><strong>CanCaptureVideo</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="IosPlatformBackend-CanFlushIndividualFilesFromSystemCache"><strong>CanFlushIndividualFilesFromSystemCache</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="IosPlatformBackend-CanMeasurePerApplicationPower"><strong>CanMeasurePerApplicationPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="IosPlatformBackend-CanMonitorNetworkData"><strong>CanMonitorNetworkData</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="IosPlatformBackend-CanTakeScreenshot"><strong>CanTakeScreenshot</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="IosPlatformBackend-CooperativelyShutdown"><strong>CooperativelyShutdown</strong></a>(self, proc, app_name)</dt><dd><tt>Cooperatively&nbsp;shut&nbsp;down&nbsp;the&nbsp;given&nbsp;process&nbsp;from&nbsp;subprocess.Popen.<br>
+&nbsp;<br>
+Currently&nbsp;this&nbsp;is&nbsp;only&nbsp;implemented&nbsp;on&nbsp;Windows.&nbsp;See<br>
+crbug.com/424024&nbsp;for&nbsp;background&nbsp;on&nbsp;why&nbsp;it&nbsp;was&nbsp;added.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;proc:&nbsp;a&nbsp;process&nbsp;object&nbsp;returned&nbsp;from&nbsp;subprocess.Popen.<br>
+&nbsp;&nbsp;app_name:&nbsp;on&nbsp;Windows,&nbsp;is&nbsp;the&nbsp;prefix&nbsp;of&nbsp;the&nbsp;application's&nbsp;window<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class&nbsp;name&nbsp;that&nbsp;should&nbsp;be&nbsp;searched&nbsp;for.&nbsp;This&nbsp;helps&nbsp;ensure<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;that&nbsp;only&nbsp;the&nbsp;application's&nbsp;windows&nbsp;are&nbsp;closed.<br>
+&nbsp;<br>
+Returns&nbsp;True&nbsp;if&nbsp;it&nbsp;is&nbsp;believed&nbsp;the&nbsp;attempt&nbsp;succeeded.</tt></dd></dl>
+
+<dl><dt><a name="IosPlatformBackend-DidCreateBrowser"><strong>DidCreateBrowser</strong></a>(self, browser, browser_backend)</dt></dl>
+
+<dl><dt><a name="IosPlatformBackend-DidStartBrowser"><strong>DidStartBrowser</strong></a>(self, browser, browser_backend)</dt></dl>
+
+<dl><dt><a name="IosPlatformBackend-GetArchName"><strong>GetArchName</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="IosPlatformBackend-GetCpuStats"><strong>GetCpuStats</strong></a>(self, pid)</dt></dl>
+
+<dl><dt><a name="IosPlatformBackend-GetCpuTimestamp"><strong>GetCpuTimestamp</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="IosPlatformBackend-GetMemoryStats"><strong>GetMemoryStats</strong></a>(self, pid)</dt></dl>
+
+<dl><dt><a name="IosPlatformBackend-GetNetworkData"><strong>GetNetworkData</strong></a>(self, browser)</dt></dl>
+
+<dl><dt><a name="IosPlatformBackend-GetRemotePort"><strong>GetRemotePort</strong></a>(self, port)</dt></dl>
+
+<dl><dt><a name="IosPlatformBackend-GetSystemCommitCharge"><strong>GetSystemCommitCharge</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="IosPlatformBackend-InitPlatformBackend"><strong>InitPlatformBackend</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="IosPlatformBackend-IsCooperativeShutdownSupported"><strong>IsCooperativeShutdownSupported</strong></a>(self)</dt><dd><tt>Indicates&nbsp;whether&nbsp;CooperativelyShutdown,&nbsp;below,&nbsp;is&nbsp;supported.<br>
+It&nbsp;is&nbsp;not&nbsp;necessary&nbsp;to&nbsp;implement&nbsp;it&nbsp;on&nbsp;all&nbsp;platforms.</tt></dd></dl>
+
+<dl><dt><a name="IosPlatformBackend-IsDisplayTracingSupported"><strong>IsDisplayTracingSupported</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="IosPlatformBackend-PathExists"><strong>PathExists</strong></a>(self, path, timeout<font color="#909090">=None</font>, retries<font color="#909090">=None</font>)</dt><dd><tt>Tests&nbsp;whether&nbsp;the&nbsp;given&nbsp;path&nbsp;exists&nbsp;on&nbsp;the&nbsp;target&nbsp;platform.<br>
+Args:<br>
+&nbsp;&nbsp;path:&nbsp;path&nbsp;in&nbsp;request.<br>
+&nbsp;&nbsp;timeout:&nbsp;timeout.<br>
+&nbsp;&nbsp;retries:&nbsp;num&nbsp;of&nbsp;retries.<br>
+Return:<br>
+&nbsp;&nbsp;Whether&nbsp;the&nbsp;path&nbsp;exists&nbsp;on&nbsp;the&nbsp;target&nbsp;platform.</tt></dd></dl>
+
+<dl><dt><a name="IosPlatformBackend-PurgeUnpinnedMemory"><strong>PurgeUnpinnedMemory</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="IosPlatformBackend-ReadMsr"><strong>ReadMsr</strong></a>(self, msr_number, start<font color="#909090">=0</font>, length<font color="#909090">=64</font>)</dt><dd><tt>Read&nbsp;a&nbsp;CPU&nbsp;model-specific&nbsp;register&nbsp;(MSR).<br>
+&nbsp;<br>
+Which&nbsp;MSRs&nbsp;are&nbsp;available&nbsp;depends&nbsp;on&nbsp;the&nbsp;CPU&nbsp;model.<br>
+On&nbsp;systems&nbsp;with&nbsp;multiple&nbsp;CPUs,&nbsp;this&nbsp;function&nbsp;may&nbsp;run&nbsp;on&nbsp;any&nbsp;CPU.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;msr_number:&nbsp;The&nbsp;number&nbsp;of&nbsp;the&nbsp;register&nbsp;to&nbsp;read.<br>
+&nbsp;&nbsp;start:&nbsp;The&nbsp;least&nbsp;significant&nbsp;bit&nbsp;to&nbsp;read,&nbsp;zero-indexed.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(Said&nbsp;another&nbsp;way,&nbsp;the&nbsp;number&nbsp;of&nbsp;bits&nbsp;to&nbsp;right-shift&nbsp;the&nbsp;MSR&nbsp;value.)<br>
+&nbsp;&nbsp;length:&nbsp;The&nbsp;number&nbsp;of&nbsp;bits&nbsp;to&nbsp;read.&nbsp;MSRs&nbsp;are&nbsp;64&nbsp;bits,&nbsp;even&nbsp;on&nbsp;32-bit&nbsp;CPUs.</tt></dd></dl>
+
+<dl><dt><a name="IosPlatformBackend-SetPlatform"><strong>SetPlatform</strong></a>(self, platform)</dt></dl>
+
+<dl><dt><a name="IosPlatformBackend-StartDisplayTracing"><strong>StartDisplayTracing</strong></a>(self)</dt><dd><tt>Start&nbsp;gathering&nbsp;a&nbsp;trace&nbsp;with&nbsp;frame&nbsp;timestamps&nbsp;close&nbsp;to&nbsp;physical<br>
+display.</tt></dd></dl>
+
+<dl><dt><a name="IosPlatformBackend-StartVideoCapture"><strong>StartVideoCapture</strong></a>(self, min_bitrate_mbps)</dt></dl>
+
+<dl><dt><a name="IosPlatformBackend-StopDisplayTracing"><strong>StopDisplayTracing</strong></a>(self)</dt><dd><tt>Stop&nbsp;gathering&nbsp;a&nbsp;trace&nbsp;with&nbsp;frame&nbsp;timestamps&nbsp;close&nbsp;to&nbsp;physical&nbsp;display.<br>
+&nbsp;<br>
+Returns&nbsp;a&nbsp;raw&nbsp;tracing&nbsp;events&nbsp;that&nbsp;contains&nbsp;the&nbsp;timestamps&nbsp;of&nbsp;physical<br>
+display.</tt></dd></dl>
+
+<dl><dt><a name="IosPlatformBackend-TakeScreenshot"><strong>TakeScreenshot</strong></a>(self, file_path)</dt></dl>
+
+<dl><dt><a name="IosPlatformBackend-WillCloseBrowser"><strong>WillCloseBrowser</strong></a>(self, browser, browser_backend)</dt></dl>
+
+<hr>
+Class methods inherited from <a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">telemetry.internal.platform.platform_backend.PlatformBackend</a>:<br>
+<dl><dt><a name="IosPlatformBackend-CreatePlatformForDevice"><strong>CreatePlatformForDevice</strong></a>(cls, device, finder_options)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="IosPlatformBackend-IsPlatformBackendForHost"><strong>IsPlatformBackendForHost</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Returns&nbsp;whether&nbsp;this&nbsp;platform&nbsp;backend&nbsp;is&nbsp;the&nbsp;platform&nbsp;backend&nbsp;to&nbsp;be&nbsp;used<br>
+for&nbsp;the&nbsp;host&nbsp;device&nbsp;which&nbsp;telemetry&nbsp;is&nbsp;running&nbsp;on.</tt></dd></dl>
+
+<dl><dt><a name="IosPlatformBackend-SupportsDevice"><strong>SupportsDevice</strong></a>(cls, device)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Returns&nbsp;whether&nbsp;this&nbsp;platform&nbsp;backend&nbsp;supports&nbsp;intialization&nbsp;from&nbsp;the<br>
+device.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">telemetry.internal.platform.platform_backend.PlatformBackend</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>forwarder_factory</strong></dt>
+</dl>
+<dl><dt><strong>is_host_platform</strong></dt>
+</dl>
+<dl><dt><strong>is_video_capture_running</strong></dt>
+</dl>
+<dl><dt><strong>network_controller_backend</strong></dt>
+</dl>
+<dl><dt><strong>platform</strong></dt>
+</dl>
+<dl><dt><strong>running_browser_backends</strong></dt>
+</dl>
+<dl><dt><strong>tracing_controller_backend</strong></dt>
+</dl>
+<dl><dt><strong>wpr_ca_cert_path</strong></dt>
+</dl>
+<dl><dt><strong>wpr_http_device_port</strong></dt>
+</dl>
+<dl><dt><strong>wpr_https_device_port</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.linux_based_platform_backend.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.linux_based_platform_backend.html
new file mode 100644
index 0000000..bba8356
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.linux_based_platform_backend.html
@@ -0,0 +1,278 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.linux_based_platform_backend</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.linux_based_platform_backend</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/linux_based_platform_backend.py">telemetry/internal/platform/linux_based_platform_backend.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.decorators.html">telemetry.decorators</a><br>
+<a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.platform_backend.html">telemetry.internal.platform.platform_backend</a><br>
+<a href="re.html">re</a><br>
+</td><td width="25%" valign=top><a href="resource.html">resource</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">telemetry.internal.platform.platform_backend.PlatformBackend</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.linux_based_platform_backend.html#LinuxBasedPlatformBackend">LinuxBasedPlatformBackend</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="LinuxBasedPlatformBackend">class <strong>LinuxBasedPlatformBackend</strong></a>(<a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">telemetry.internal.platform.platform_backend.PlatformBackend</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Abstract&nbsp;platform&nbsp;containing&nbsp;functionality&nbsp;shared&nbsp;by&nbsp;all&nbsp;Linux&nbsp;based&nbsp;OSes.<br>
+&nbsp;<br>
+This&nbsp;includes&nbsp;Android&nbsp;and&nbsp;ChromeOS.<br>
+&nbsp;<br>
+Subclasses&nbsp;must&nbsp;implement&nbsp;RunCommand,&nbsp;GetFileContents,&nbsp;GetPsOutput,&nbsp;and<br>
+ParseCStateSample.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.linux_based_platform_backend.html#LinuxBasedPlatformBackend">LinuxBasedPlatformBackend</a></dd>
+<dd><a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">telemetry.internal.platform.platform_backend.PlatformBackend</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="LinuxBasedPlatformBackend-GetClockTicks"><strong>GetClockTicks</strong></a>(*args, **kwargs)</dt><dd><tt>Returns&nbsp;the&nbsp;number&nbsp;of&nbsp;clock&nbsp;ticks&nbsp;per&nbsp;second.<br>
+&nbsp;<br>
+The&nbsp;proper&nbsp;way&nbsp;is&nbsp;to&nbsp;call&nbsp;os.sysconf('SC_CLK_TCK')&nbsp;but&nbsp;that&nbsp;is&nbsp;not&nbsp;easy&nbsp;to<br>
+do&nbsp;on&nbsp;Android/CrOS.&nbsp;In&nbsp;practice,&nbsp;nearly&nbsp;all&nbsp;Linux&nbsp;machines&nbsp;have&nbsp;a&nbsp;USER_HZ<br>
+of&nbsp;100,&nbsp;so&nbsp;just&nbsp;return&nbsp;that.</tt></dd></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-GetCpuStats"><strong>GetCpuStats</strong></a>(self, pid)</dt></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-GetCpuTimestamp"><strong>GetCpuTimestamp</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-GetFileContents"><strong>GetFileContents</strong></a>(self, filename)</dt></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-GetMemoryStats"><strong>GetMemoryStats</strong></a>(self, pid)</dt></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-GetPsOutput"><strong>GetPsOutput</strong></a>(self, columns, pid<font color="#909090">=None</font>)</dt></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-GetSystemCommitCharge"><strong>GetSystemCommitCharge</strong></a>(self)</dt><dd><tt>#&nbsp;Get&nbsp;the&nbsp;commit&nbsp;charge&nbsp;in&nbsp;kB.</tt></dd></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-GetSystemTotalPhysicalMemory"><strong>GetSystemTotalPhysicalMemory</strong></a>(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-RunCommand"><strong>RunCommand</strong></a>(self, cmd)</dt><dd><tt>Runs&nbsp;the&nbsp;specified&nbsp;command.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;cmd:&nbsp;A&nbsp;list&nbsp;of&nbsp;program&nbsp;arguments&nbsp;or&nbsp;the&nbsp;path&nbsp;string&nbsp;of&nbsp;the&nbsp;program.<br>
+Returns:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;A&nbsp;string&nbsp;whose&nbsp;content&nbsp;is&nbsp;the&nbsp;output&nbsp;of&nbsp;the&nbsp;command.</tt></dd></dl>
+
+<hr>
+Static methods defined here:<br>
+<dl><dt><a name="LinuxBasedPlatformBackend-ParseCStateSample"><strong>ParseCStateSample</strong></a>(sample)</dt><dd><tt>Parse&nbsp;a&nbsp;single&nbsp;c-state&nbsp;residency&nbsp;sample.<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sample:&nbsp;A&nbsp;sample&nbsp;of&nbsp;c-state&nbsp;residency&nbsp;times&nbsp;to&nbsp;be&nbsp;parsed.&nbsp;Organized&nbsp;as<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;a&nbsp;dictionary&nbsp;mapping&nbsp;CPU&nbsp;name&nbsp;to&nbsp;a&nbsp;string&nbsp;containing&nbsp;all&nbsp;c-state<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;names,&nbsp;the&nbsp;times&nbsp;in&nbsp;each&nbsp;state,&nbsp;the&nbsp;latency&nbsp;of&nbsp;each&nbsp;state,&nbsp;and&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;time&nbsp;at&nbsp;which&nbsp;the&nbsp;sample&nbsp;was&nbsp;taken&nbsp;all&nbsp;separated&nbsp;by&nbsp;newlines.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Ex:&nbsp;{'cpu0':&nbsp;'C0<br>
+C1<br>
+5000<br>
+2000<br>
+20<br>
+30<br>
+1406673171'}<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;Returns:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Dictionary&nbsp;associating&nbsp;a&nbsp;c-state&nbsp;with&nbsp;a&nbsp;time.</tt></dd></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">telemetry.internal.platform.platform_backend.PlatformBackend</a>:<br>
+<dl><dt><a name="LinuxBasedPlatformBackend-CanCaptureVideo"><strong>CanCaptureVideo</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-CanFlushIndividualFilesFromSystemCache"><strong>CanFlushIndividualFilesFromSystemCache</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-CanLaunchApplication"><strong>CanLaunchApplication</strong></a>(self, application)</dt></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-CanMeasurePerApplicationPower"><strong>CanMeasurePerApplicationPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-CanMonitorNetworkData"><strong>CanMonitorNetworkData</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-CanMonitorPower"><strong>CanMonitorPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-CanMonitorThermalThrottling"><strong>CanMonitorThermalThrottling</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-CanTakeScreenshot"><strong>CanTakeScreenshot</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-CooperativelyShutdown"><strong>CooperativelyShutdown</strong></a>(self, proc, app_name)</dt><dd><tt>Cooperatively&nbsp;shut&nbsp;down&nbsp;the&nbsp;given&nbsp;process&nbsp;from&nbsp;subprocess.Popen.<br>
+&nbsp;<br>
+Currently&nbsp;this&nbsp;is&nbsp;only&nbsp;implemented&nbsp;on&nbsp;Windows.&nbsp;See<br>
+crbug.com/424024&nbsp;for&nbsp;background&nbsp;on&nbsp;why&nbsp;it&nbsp;was&nbsp;added.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;proc:&nbsp;a&nbsp;process&nbsp;object&nbsp;returned&nbsp;from&nbsp;subprocess.Popen.<br>
+&nbsp;&nbsp;app_name:&nbsp;on&nbsp;Windows,&nbsp;is&nbsp;the&nbsp;prefix&nbsp;of&nbsp;the&nbsp;application's&nbsp;window<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class&nbsp;name&nbsp;that&nbsp;should&nbsp;be&nbsp;searched&nbsp;for.&nbsp;This&nbsp;helps&nbsp;ensure<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;that&nbsp;only&nbsp;the&nbsp;application's&nbsp;windows&nbsp;are&nbsp;closed.<br>
+&nbsp;<br>
+Returns&nbsp;True&nbsp;if&nbsp;it&nbsp;is&nbsp;believed&nbsp;the&nbsp;attempt&nbsp;succeeded.</tt></dd></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-DidCreateBrowser"><strong>DidCreateBrowser</strong></a>(self, browser, browser_backend)</dt></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-DidStartBrowser"><strong>DidStartBrowser</strong></a>(self, browser, browser_backend)</dt></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-FlushDnsCache"><strong>FlushDnsCache</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-FlushEntireSystemCache"><strong>FlushEntireSystemCache</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-FlushSystemCacheForDirectory"><strong>FlushSystemCacheForDirectory</strong></a>(self, directory)</dt></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-GetArchName"><strong>GetArchName</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-GetChildPids"><strong>GetChildPids</strong></a>(self, pid)</dt></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-GetCommandLine"><strong>GetCommandLine</strong></a>(self, pid)</dt></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-GetDeviceTypeName"><strong>GetDeviceTypeName</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-GetNetworkData"><strong>GetNetworkData</strong></a>(self, browser)</dt></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-GetOSName"><strong>GetOSName</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-GetOSVersionName"><strong>GetOSVersionName</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-GetRemotePort"><strong>GetRemotePort</strong></a>(self, port)</dt></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-HasBeenThermallyThrottled"><strong>HasBeenThermallyThrottled</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-InitPlatformBackend"><strong>InitPlatformBackend</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-InstallApplication"><strong>InstallApplication</strong></a>(self, application)</dt></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-IsApplicationRunning"><strong>IsApplicationRunning</strong></a>(self, application)</dt></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-IsCooperativeShutdownSupported"><strong>IsCooperativeShutdownSupported</strong></a>(self)</dt><dd><tt>Indicates&nbsp;whether&nbsp;CooperativelyShutdown,&nbsp;below,&nbsp;is&nbsp;supported.<br>
+It&nbsp;is&nbsp;not&nbsp;necessary&nbsp;to&nbsp;implement&nbsp;it&nbsp;on&nbsp;all&nbsp;platforms.</tt></dd></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-IsDisplayTracingSupported"><strong>IsDisplayTracingSupported</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-IsThermallyThrottled"><strong>IsThermallyThrottled</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-LaunchApplication"><strong>LaunchApplication</strong></a>(self, application, parameters<font color="#909090">=None</font>, elevate_privilege<font color="#909090">=False</font>)</dt></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-PathExists"><strong>PathExists</strong></a>(self, path, timeout<font color="#909090">=None</font>, retries<font color="#909090">=None</font>)</dt><dd><tt>Tests&nbsp;whether&nbsp;the&nbsp;given&nbsp;path&nbsp;exists&nbsp;on&nbsp;the&nbsp;target&nbsp;platform.<br>
+Args:<br>
+&nbsp;&nbsp;path:&nbsp;path&nbsp;in&nbsp;request.<br>
+&nbsp;&nbsp;timeout:&nbsp;timeout.<br>
+&nbsp;&nbsp;retries:&nbsp;num&nbsp;of&nbsp;retries.<br>
+Return:<br>
+&nbsp;&nbsp;Whether&nbsp;the&nbsp;path&nbsp;exists&nbsp;on&nbsp;the&nbsp;target&nbsp;platform.</tt></dd></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-PurgeUnpinnedMemory"><strong>PurgeUnpinnedMemory</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-ReadMsr"><strong>ReadMsr</strong></a>(self, msr_number, start<font color="#909090">=0</font>, length<font color="#909090">=64</font>)</dt><dd><tt>Read&nbsp;a&nbsp;CPU&nbsp;model-specific&nbsp;register&nbsp;(MSR).<br>
+&nbsp;<br>
+Which&nbsp;MSRs&nbsp;are&nbsp;available&nbsp;depends&nbsp;on&nbsp;the&nbsp;CPU&nbsp;model.<br>
+On&nbsp;systems&nbsp;with&nbsp;multiple&nbsp;CPUs,&nbsp;this&nbsp;function&nbsp;may&nbsp;run&nbsp;on&nbsp;any&nbsp;CPU.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;msr_number:&nbsp;The&nbsp;number&nbsp;of&nbsp;the&nbsp;register&nbsp;to&nbsp;read.<br>
+&nbsp;&nbsp;start:&nbsp;The&nbsp;least&nbsp;significant&nbsp;bit&nbsp;to&nbsp;read,&nbsp;zero-indexed.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(Said&nbsp;another&nbsp;way,&nbsp;the&nbsp;number&nbsp;of&nbsp;bits&nbsp;to&nbsp;right-shift&nbsp;the&nbsp;MSR&nbsp;value.)<br>
+&nbsp;&nbsp;length:&nbsp;The&nbsp;number&nbsp;of&nbsp;bits&nbsp;to&nbsp;read.&nbsp;MSRs&nbsp;are&nbsp;64&nbsp;bits,&nbsp;even&nbsp;on&nbsp;32-bit&nbsp;CPUs.</tt></dd></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-SetFullPerformanceModeEnabled"><strong>SetFullPerformanceModeEnabled</strong></a>(self, enabled)</dt></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-SetPlatform"><strong>SetPlatform</strong></a>(self, platform)</dt></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-StartDisplayTracing"><strong>StartDisplayTracing</strong></a>(self)</dt><dd><tt>Start&nbsp;gathering&nbsp;a&nbsp;trace&nbsp;with&nbsp;frame&nbsp;timestamps&nbsp;close&nbsp;to&nbsp;physical<br>
+display.</tt></dd></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-StartMonitoringPower"><strong>StartMonitoringPower</strong></a>(self, browser)</dt></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-StartVideoCapture"><strong>StartVideoCapture</strong></a>(self, min_bitrate_mbps)</dt></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-StopDisplayTracing"><strong>StopDisplayTracing</strong></a>(self)</dt><dd><tt>Stop&nbsp;gathering&nbsp;a&nbsp;trace&nbsp;with&nbsp;frame&nbsp;timestamps&nbsp;close&nbsp;to&nbsp;physical&nbsp;display.<br>
+&nbsp;<br>
+Returns&nbsp;a&nbsp;raw&nbsp;tracing&nbsp;events&nbsp;that&nbsp;contains&nbsp;the&nbsp;timestamps&nbsp;of&nbsp;physical<br>
+display.</tt></dd></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-StopMonitoringPower"><strong>StopMonitoringPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-StopVideoCapture"><strong>StopVideoCapture</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-TakeScreenshot"><strong>TakeScreenshot</strong></a>(self, file_path)</dt></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-WillCloseBrowser"><strong>WillCloseBrowser</strong></a>(self, browser, browser_backend)</dt></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-__init__"><strong>__init__</strong></a>(self, device<font color="#909090">=None</font>)</dt><dd><tt>Initalize&nbsp;an&nbsp;instance&nbsp;of&nbsp;<a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">PlatformBackend</a>&nbsp;from&nbsp;a&nbsp;device&nbsp;optionally.<br>
+Call&nbsp;sites&nbsp;need&nbsp;to&nbsp;use&nbsp;SupportsDevice&nbsp;before&nbsp;intialization&nbsp;to&nbsp;check<br>
+whether&nbsp;this&nbsp;platform&nbsp;backend&nbsp;supports&nbsp;the&nbsp;device.<br>
+If&nbsp;device&nbsp;is&nbsp;None,&nbsp;this&nbsp;constructor&nbsp;returns&nbsp;the&nbsp;host&nbsp;platform&nbsp;backend<br>
+which&nbsp;telemetry&nbsp;is&nbsp;running&nbsp;on.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;device:&nbsp;an&nbsp;instance&nbsp;of&nbsp;telemetry.core.platform.device.Device.</tt></dd></dl>
+
+<hr>
+Class methods inherited from <a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">telemetry.internal.platform.platform_backend.PlatformBackend</a>:<br>
+<dl><dt><a name="LinuxBasedPlatformBackend-CreatePlatformForDevice"><strong>CreatePlatformForDevice</strong></a>(cls, device, finder_options)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-IsPlatformBackendForHost"><strong>IsPlatformBackendForHost</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Returns&nbsp;whether&nbsp;this&nbsp;platform&nbsp;backend&nbsp;is&nbsp;the&nbsp;platform&nbsp;backend&nbsp;to&nbsp;be&nbsp;used<br>
+for&nbsp;the&nbsp;host&nbsp;device&nbsp;which&nbsp;telemetry&nbsp;is&nbsp;running&nbsp;on.</tt></dd></dl>
+
+<dl><dt><a name="LinuxBasedPlatformBackend-SupportsDevice"><strong>SupportsDevice</strong></a>(cls, device)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Returns&nbsp;whether&nbsp;this&nbsp;platform&nbsp;backend&nbsp;supports&nbsp;intialization&nbsp;from&nbsp;the<br>
+device.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">telemetry.internal.platform.platform_backend.PlatformBackend</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>forwarder_factory</strong></dt>
+</dl>
+<dl><dt><strong>is_host_platform</strong></dt>
+</dl>
+<dl><dt><strong>is_video_capture_running</strong></dt>
+</dl>
+<dl><dt><strong>network_controller_backend</strong></dt>
+</dl>
+<dl><dt><strong>platform</strong></dt>
+</dl>
+<dl><dt><strong>running_browser_backends</strong></dt>
+</dl>
+<dl><dt><strong>tracing_controller_backend</strong></dt>
+</dl>
+<dl><dt><strong>wpr_ca_cert_path</strong></dt>
+</dl>
+<dl><dt><strong>wpr_http_device_port</strong></dt>
+</dl>
+<dl><dt><strong>wpr_https_device_port</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.linux_platform_backend.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.linux_platform_backend.html
new file mode 100644
index 0000000..9539096
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.linux_platform_backend.html
@@ -0,0 +1,280 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.linux_platform_backend</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.linux_platform_backend</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/linux_platform_backend.py">telemetry/internal/platform/linux_platform_backend.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.util.binary_manager.html">telemetry.internal.util.binary_manager</a><br>
+<a href="catapult_base.cloud_storage.html">catapult_base.cloud_storage</a><br>
+<a href="telemetry.decorators.html">telemetry.decorators</a><br>
+<a href="telemetry.internal.platform.linux_based_platform_backend.html">telemetry.internal.platform.linux_based_platform_backend</a><br>
+</td><td width="25%" valign=top><a href="logging.html">logging</a><br>
+<a href="telemetry.internal.platform.power_monitor.msr_power_monitor.html">telemetry.internal.platform.power_monitor.msr_power_monitor</a><br>
+<a href="os.html">os</a><br>
+<a href="telemetry.core.os_version.html">telemetry.core.os_version</a><br>
+</td><td width="25%" valign=top><a href="platform.html">platform</a><br>
+<a href="telemetry.internal.platform.posix_platform_backend.html">telemetry.internal.platform.posix_platform_backend</a><br>
+<a href="subprocess.html">subprocess</a><br>
+<a href="sys.html">sys</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.util.html">telemetry.core.util</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.linux_based_platform_backend.html#LinuxBasedPlatformBackend">telemetry.internal.platform.linux_based_platform_backend.LinuxBasedPlatformBackend</a>(<a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">telemetry.internal.platform.platform_backend.PlatformBackend</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.linux_platform_backend.html#LinuxPlatformBackend">LinuxPlatformBackend</a>(<a href="telemetry.internal.platform.posix_platform_backend.html#PosixPlatformBackend">telemetry.internal.platform.posix_platform_backend.PosixPlatformBackend</a>, <a href="telemetry.internal.platform.linux_based_platform_backend.html#LinuxBasedPlatformBackend">telemetry.internal.platform.linux_based_platform_backend.LinuxBasedPlatformBackend</a>)
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.posix_platform_backend.html#PosixPlatformBackend">telemetry.internal.platform.posix_platform_backend.PosixPlatformBackend</a>(<a href="telemetry.internal.platform.desktop_platform_backend.html#DesktopPlatformBackend">telemetry.internal.platform.desktop_platform_backend.DesktopPlatformBackend</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.linux_platform_backend.html#LinuxPlatformBackend">LinuxPlatformBackend</a>(<a href="telemetry.internal.platform.posix_platform_backend.html#PosixPlatformBackend">telemetry.internal.platform.posix_platform_backend.PosixPlatformBackend</a>, <a href="telemetry.internal.platform.linux_based_platform_backend.html#LinuxBasedPlatformBackend">telemetry.internal.platform.linux_based_platform_backend.LinuxBasedPlatformBackend</a>)
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="LinuxPlatformBackend">class <strong>LinuxPlatformBackend</strong></a>(<a href="telemetry.internal.platform.posix_platform_backend.html#PosixPlatformBackend">telemetry.internal.platform.posix_platform_backend.PosixPlatformBackend</a>, <a href="telemetry.internal.platform.linux_based_platform_backend.html#LinuxBasedPlatformBackend">telemetry.internal.platform.linux_based_platform_backend.LinuxBasedPlatformBackend</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.linux_platform_backend.html#LinuxPlatformBackend">LinuxPlatformBackend</a></dd>
+<dd><a href="telemetry.internal.platform.posix_platform_backend.html#PosixPlatformBackend">telemetry.internal.platform.posix_platform_backend.PosixPlatformBackend</a></dd>
+<dd><a href="telemetry.internal.platform.desktop_platform_backend.html#DesktopPlatformBackend">telemetry.internal.platform.desktop_platform_backend.DesktopPlatformBackend</a></dd>
+<dd><a href="telemetry.internal.platform.linux_based_platform_backend.html#LinuxBasedPlatformBackend">telemetry.internal.platform.linux_based_platform_backend.LinuxBasedPlatformBackend</a></dd>
+<dd><a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">telemetry.internal.platform.platform_backend.PlatformBackend</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="LinuxPlatformBackend-CanFlushIndividualFilesFromSystemCache"><strong>CanFlushIndividualFilesFromSystemCache</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-CanLaunchApplication"><strong>CanLaunchApplication</strong></a>(self, application)</dt></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-CanMeasurePerApplicationPower"><strong>CanMeasurePerApplicationPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-CanMonitorPower"><strong>CanMonitorPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-FlushEntireSystemCache"><strong>FlushEntireSystemCache</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-GetArchName"><strong>GetArchName</strong></a>(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-GetOSName"><strong>GetOSName</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-GetOSVersionName"><strong>GetOSVersionName</strong></a>(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-HasBeenThermallyThrottled"><strong>HasBeenThermallyThrottled</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-InstallApplication"><strong>InstallApplication</strong></a>(self, application)</dt></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-IsThermallyThrottled"><strong>IsThermallyThrottled</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-ReadMsr"><strong>ReadMsr</strong></a>(self, msr_number, start<font color="#909090">=0</font>, length<font color="#909090">=64</font>)</dt></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-StartMonitoringPower"><strong>StartMonitoringPower</strong></a>(self, browser)</dt></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-StopMonitoringPower"><strong>StopMonitoringPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="LinuxPlatformBackend-IsPlatformBackendForHost"><strong>IsPlatformBackendForHost</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.platform.posix_platform_backend.html#PosixPlatformBackend">telemetry.internal.platform.posix_platform_backend.PosixPlatformBackend</a>:<br>
+<dl><dt><a name="LinuxPlatformBackend-GetChildPids"><strong>GetChildPids</strong></a>(self, pid)</dt><dd><tt>Returns&nbsp;a&nbsp;list&nbsp;of&nbsp;child&nbsp;pids&nbsp;of&nbsp;|pid|.</tt></dd></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-GetCommandLine"><strong>GetCommandLine</strong></a>(self, pid)</dt></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-GetFileContents"><strong>GetFileContents</strong></a>(self, path)</dt></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-GetPsOutput"><strong>GetPsOutput</strong></a>(self, columns, pid<font color="#909090">=None</font>)</dt><dd><tt>Returns&nbsp;output&nbsp;of&nbsp;the&nbsp;'ps'&nbsp;command&nbsp;as&nbsp;a&nbsp;list&nbsp;of&nbsp;lines.<br>
+Subclass&nbsp;should&nbsp;override&nbsp;this&nbsp;function.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;columns:&nbsp;A&nbsp;list&nbsp;of&nbsp;require&nbsp;columns,&nbsp;e.g.,&nbsp;['pid',&nbsp;'pss'].<br>
+&nbsp;&nbsp;pid:&nbsp;If&nbsp;not&nbsp;None,&nbsp;returns&nbsp;only&nbsp;the&nbsp;information&nbsp;of&nbsp;the&nbsp;process<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;with&nbsp;the&nbsp;pid.</tt></dd></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-IsApplicationRunning"><strong>IsApplicationRunning</strong></a>(self, application)</dt></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-LaunchApplication"><strong>LaunchApplication</strong></a>(self, application, parameters<font color="#909090">=None</font>, elevate_privilege<font color="#909090">=False</font>)</dt></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-RunCommand"><strong>RunCommand</strong></a>(self, args)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.platform.desktop_platform_backend.html#DesktopPlatformBackend">telemetry.internal.platform.desktop_platform_backend.DesktopPlatformBackend</a>:<br>
+<dl><dt><a name="LinuxPlatformBackend-FlushSystemCacheForDirectory"><strong>FlushSystemCacheForDirectory</strong></a>(self, directory)</dt></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-GetDeviceTypeName"><strong>GetDeviceTypeName</strong></a>(self)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.platform.linux_based_platform_backend.html#LinuxBasedPlatformBackend">telemetry.internal.platform.linux_based_platform_backend.LinuxBasedPlatformBackend</a>:<br>
+<dl><dt><a name="LinuxPlatformBackend-GetClockTicks"><strong>GetClockTicks</strong></a>(*args, **kwargs)</dt><dd><tt>Returns&nbsp;the&nbsp;number&nbsp;of&nbsp;clock&nbsp;ticks&nbsp;per&nbsp;second.<br>
+&nbsp;<br>
+The&nbsp;proper&nbsp;way&nbsp;is&nbsp;to&nbsp;call&nbsp;os.sysconf('SC_CLK_TCK')&nbsp;but&nbsp;that&nbsp;is&nbsp;not&nbsp;easy&nbsp;to<br>
+do&nbsp;on&nbsp;Android/CrOS.&nbsp;In&nbsp;practice,&nbsp;nearly&nbsp;all&nbsp;Linux&nbsp;machines&nbsp;have&nbsp;a&nbsp;USER_HZ<br>
+of&nbsp;100,&nbsp;so&nbsp;just&nbsp;return&nbsp;that.</tt></dd></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-GetCpuStats"><strong>GetCpuStats</strong></a>(self, pid)</dt></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-GetCpuTimestamp"><strong>GetCpuTimestamp</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-GetMemoryStats"><strong>GetMemoryStats</strong></a>(self, pid)</dt></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-GetSystemCommitCharge"><strong>GetSystemCommitCharge</strong></a>(self)</dt><dd><tt>#&nbsp;Get&nbsp;the&nbsp;commit&nbsp;charge&nbsp;in&nbsp;kB.</tt></dd></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-GetSystemTotalPhysicalMemory"><strong>GetSystemTotalPhysicalMemory</strong></a>(*args, **kwargs)</dt></dl>
+
+<hr>
+Static methods inherited from <a href="telemetry.internal.platform.linux_based_platform_backend.html#LinuxBasedPlatformBackend">telemetry.internal.platform.linux_based_platform_backend.LinuxBasedPlatformBackend</a>:<br>
+<dl><dt><a name="LinuxPlatformBackend-ParseCStateSample"><strong>ParseCStateSample</strong></a>(sample)</dt><dd><tt>Parse&nbsp;a&nbsp;single&nbsp;c-state&nbsp;residency&nbsp;sample.<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sample:&nbsp;A&nbsp;sample&nbsp;of&nbsp;c-state&nbsp;residency&nbsp;times&nbsp;to&nbsp;be&nbsp;parsed.&nbsp;Organized&nbsp;as<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;a&nbsp;dictionary&nbsp;mapping&nbsp;CPU&nbsp;name&nbsp;to&nbsp;a&nbsp;string&nbsp;containing&nbsp;all&nbsp;c-state<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;names,&nbsp;the&nbsp;times&nbsp;in&nbsp;each&nbsp;state,&nbsp;the&nbsp;latency&nbsp;of&nbsp;each&nbsp;state,&nbsp;and&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;time&nbsp;at&nbsp;which&nbsp;the&nbsp;sample&nbsp;was&nbsp;taken&nbsp;all&nbsp;separated&nbsp;by&nbsp;newlines.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Ex:&nbsp;{'cpu0':&nbsp;'C0<br>
+C1<br>
+5000<br>
+2000<br>
+20<br>
+30<br>
+1406673171'}<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;Returns:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Dictionary&nbsp;associating&nbsp;a&nbsp;c-state&nbsp;with&nbsp;a&nbsp;time.</tt></dd></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">telemetry.internal.platform.platform_backend.PlatformBackend</a>:<br>
+<dl><dt><a name="LinuxPlatformBackend-CanCaptureVideo"><strong>CanCaptureVideo</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-CanMonitorNetworkData"><strong>CanMonitorNetworkData</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-CanMonitorThermalThrottling"><strong>CanMonitorThermalThrottling</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-CanTakeScreenshot"><strong>CanTakeScreenshot</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-CooperativelyShutdown"><strong>CooperativelyShutdown</strong></a>(self, proc, app_name)</dt><dd><tt>Cooperatively&nbsp;shut&nbsp;down&nbsp;the&nbsp;given&nbsp;process&nbsp;from&nbsp;subprocess.Popen.<br>
+&nbsp;<br>
+Currently&nbsp;this&nbsp;is&nbsp;only&nbsp;implemented&nbsp;on&nbsp;Windows.&nbsp;See<br>
+crbug.com/424024&nbsp;for&nbsp;background&nbsp;on&nbsp;why&nbsp;it&nbsp;was&nbsp;added.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;proc:&nbsp;a&nbsp;process&nbsp;object&nbsp;returned&nbsp;from&nbsp;subprocess.Popen.<br>
+&nbsp;&nbsp;app_name:&nbsp;on&nbsp;Windows,&nbsp;is&nbsp;the&nbsp;prefix&nbsp;of&nbsp;the&nbsp;application's&nbsp;window<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class&nbsp;name&nbsp;that&nbsp;should&nbsp;be&nbsp;searched&nbsp;for.&nbsp;This&nbsp;helps&nbsp;ensure<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;that&nbsp;only&nbsp;the&nbsp;application's&nbsp;windows&nbsp;are&nbsp;closed.<br>
+&nbsp;<br>
+Returns&nbsp;True&nbsp;if&nbsp;it&nbsp;is&nbsp;believed&nbsp;the&nbsp;attempt&nbsp;succeeded.</tt></dd></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-DidCreateBrowser"><strong>DidCreateBrowser</strong></a>(self, browser, browser_backend)</dt></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-DidStartBrowser"><strong>DidStartBrowser</strong></a>(self, browser, browser_backend)</dt></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-FlushDnsCache"><strong>FlushDnsCache</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-GetNetworkData"><strong>GetNetworkData</strong></a>(self, browser)</dt></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-GetRemotePort"><strong>GetRemotePort</strong></a>(self, port)</dt></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-InitPlatformBackend"><strong>InitPlatformBackend</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-IsCooperativeShutdownSupported"><strong>IsCooperativeShutdownSupported</strong></a>(self)</dt><dd><tt>Indicates&nbsp;whether&nbsp;CooperativelyShutdown,&nbsp;below,&nbsp;is&nbsp;supported.<br>
+It&nbsp;is&nbsp;not&nbsp;necessary&nbsp;to&nbsp;implement&nbsp;it&nbsp;on&nbsp;all&nbsp;platforms.</tt></dd></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-IsDisplayTracingSupported"><strong>IsDisplayTracingSupported</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-PathExists"><strong>PathExists</strong></a>(self, path, timeout<font color="#909090">=None</font>, retries<font color="#909090">=None</font>)</dt><dd><tt>Tests&nbsp;whether&nbsp;the&nbsp;given&nbsp;path&nbsp;exists&nbsp;on&nbsp;the&nbsp;target&nbsp;platform.<br>
+Args:<br>
+&nbsp;&nbsp;path:&nbsp;path&nbsp;in&nbsp;request.<br>
+&nbsp;&nbsp;timeout:&nbsp;timeout.<br>
+&nbsp;&nbsp;retries:&nbsp;num&nbsp;of&nbsp;retries.<br>
+Return:<br>
+&nbsp;&nbsp;Whether&nbsp;the&nbsp;path&nbsp;exists&nbsp;on&nbsp;the&nbsp;target&nbsp;platform.</tt></dd></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-PurgeUnpinnedMemory"><strong>PurgeUnpinnedMemory</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-SetFullPerformanceModeEnabled"><strong>SetFullPerformanceModeEnabled</strong></a>(self, enabled)</dt></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-SetPlatform"><strong>SetPlatform</strong></a>(self, platform)</dt></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-StartDisplayTracing"><strong>StartDisplayTracing</strong></a>(self)</dt><dd><tt>Start&nbsp;gathering&nbsp;a&nbsp;trace&nbsp;with&nbsp;frame&nbsp;timestamps&nbsp;close&nbsp;to&nbsp;physical<br>
+display.</tt></dd></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-StartVideoCapture"><strong>StartVideoCapture</strong></a>(self, min_bitrate_mbps)</dt></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-StopDisplayTracing"><strong>StopDisplayTracing</strong></a>(self)</dt><dd><tt>Stop&nbsp;gathering&nbsp;a&nbsp;trace&nbsp;with&nbsp;frame&nbsp;timestamps&nbsp;close&nbsp;to&nbsp;physical&nbsp;display.<br>
+&nbsp;<br>
+Returns&nbsp;a&nbsp;raw&nbsp;tracing&nbsp;events&nbsp;that&nbsp;contains&nbsp;the&nbsp;timestamps&nbsp;of&nbsp;physical<br>
+display.</tt></dd></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-StopVideoCapture"><strong>StopVideoCapture</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-TakeScreenshot"><strong>TakeScreenshot</strong></a>(self, file_path)</dt></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-WillCloseBrowser"><strong>WillCloseBrowser</strong></a>(self, browser, browser_backend)</dt></dl>
+
+<hr>
+Class methods inherited from <a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">telemetry.internal.platform.platform_backend.PlatformBackend</a>:<br>
+<dl><dt><a name="LinuxPlatformBackend-CreatePlatformForDevice"><strong>CreatePlatformForDevice</strong></a>(cls, device, finder_options)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="LinuxPlatformBackend-SupportsDevice"><strong>SupportsDevice</strong></a>(cls, device)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Returns&nbsp;whether&nbsp;this&nbsp;platform&nbsp;backend&nbsp;supports&nbsp;intialization&nbsp;from&nbsp;the<br>
+device.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">telemetry.internal.platform.platform_backend.PlatformBackend</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>forwarder_factory</strong></dt>
+</dl>
+<dl><dt><strong>is_host_platform</strong></dt>
+</dl>
+<dl><dt><strong>is_video_capture_running</strong></dt>
+</dl>
+<dl><dt><strong>network_controller_backend</strong></dt>
+</dl>
+<dl><dt><strong>platform</strong></dt>
+</dl>
+<dl><dt><strong>running_browser_backends</strong></dt>
+</dl>
+<dl><dt><strong>tracing_controller_backend</strong></dt>
+</dl>
+<dl><dt><strong>wpr_ca_cert_path</strong></dt>
+</dl>
+<dl><dt><strong>wpr_http_device_port</strong></dt>
+</dl>
+<dl><dt><strong>wpr_https_device_port</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.mac_platform_backend.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.mac_platform_backend.html
new file mode 100644
index 0000000..f5772ee
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.mac_platform_backend.html
@@ -0,0 +1,253 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.mac_platform_backend</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.mac_platform_backend</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/mac_platform_backend.py">telemetry/internal/platform/mac_platform_backend.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="ctypes.html">ctypes</a><br>
+<a href="telemetry.decorators.html">telemetry.decorators</a><br>
+<a href="os.html">os</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.os_version.html">telemetry.core.os_version</a><br>
+<a href="platform.html">platform</a><br>
+<a href="telemetry.internal.platform.posix_platform_backend.html">telemetry.internal.platform.posix_platform_backend</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.power_monitor.powermetrics_power_monitor.html">telemetry.internal.platform.power_monitor.powermetrics_power_monitor</a><br>
+<a href="telemetry.util.process_statistic_timeline_data.html">telemetry.util.process_statistic_timeline_data</a><br>
+<a href="resource.html">resource</a><br>
+</td><td width="25%" valign=top><a href="subprocess.html">subprocess</a><br>
+<a href="sys.html">sys</a><br>
+<a href="time.html">time</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.posix_platform_backend.html#PosixPlatformBackend">telemetry.internal.platform.posix_platform_backend.PosixPlatformBackend</a>(<a href="telemetry.internal.platform.desktop_platform_backend.html#DesktopPlatformBackend">telemetry.internal.platform.desktop_platform_backend.DesktopPlatformBackend</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.mac_platform_backend.html#MacPlatformBackend">MacPlatformBackend</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="MacPlatformBackend">class <strong>MacPlatformBackend</strong></a>(<a href="telemetry.internal.platform.posix_platform_backend.html#PosixPlatformBackend">telemetry.internal.platform.posix_platform_backend.PosixPlatformBackend</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.mac_platform_backend.html#MacPlatformBackend">MacPlatformBackend</a></dd>
+<dd><a href="telemetry.internal.platform.posix_platform_backend.html#PosixPlatformBackend">telemetry.internal.platform.posix_platform_backend.PosixPlatformBackend</a></dd>
+<dd><a href="telemetry.internal.platform.desktop_platform_backend.html#DesktopPlatformBackend">telemetry.internal.platform.desktop_platform_backend.DesktopPlatformBackend</a></dd>
+<dd><a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">telemetry.internal.platform.platform_backend.PlatformBackend</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="MacPlatformBackend-CanFlushIndividualFilesFromSystemCache"><strong>CanFlushIndividualFilesFromSystemCache</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MacPlatformBackend-CanMeasurePerApplicationPower"><strong>CanMeasurePerApplicationPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MacPlatformBackend-CanMonitorPower"><strong>CanMonitorPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MacPlatformBackend-CanTakeScreenshot"><strong>CanTakeScreenshot</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MacPlatformBackend-FlushEntireSystemCache"><strong>FlushEntireSystemCache</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MacPlatformBackend-GetArchName"><strong>GetArchName</strong></a>(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="MacPlatformBackend-GetCpuStats"><strong>GetCpuStats</strong></a>(self, pid)</dt><dd><tt>Returns&nbsp;a&nbsp;dict&nbsp;of&nbsp;cpu&nbsp;statistics&nbsp;for&nbsp;the&nbsp;process&nbsp;represented&nbsp;by&nbsp;|pid|.</tt></dd></dl>
+
+<dl><dt><a name="MacPlatformBackend-GetCpuTimestamp"><strong>GetCpuTimestamp</strong></a>(self)</dt><dd><tt>Return&nbsp;current&nbsp;timestamp&nbsp;in&nbsp;seconds.</tt></dd></dl>
+
+<dl><dt><a name="MacPlatformBackend-GetMemoryStats"><strong>GetMemoryStats</strong></a>(self, pid)</dt></dl>
+
+<dl><dt><a name="MacPlatformBackend-GetOSName"><strong>GetOSName</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MacPlatformBackend-GetOSVersionName"><strong>GetOSVersionName</strong></a>(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="MacPlatformBackend-GetSystemCommitCharge"><strong>GetSystemCommitCharge</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MacPlatformBackend-GetSystemTotalPhysicalMemory"><strong>GetSystemTotalPhysicalMemory</strong></a>(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="MacPlatformBackend-HasBeenThermallyThrottled"><strong>HasBeenThermallyThrottled</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MacPlatformBackend-IsThermallyThrottled"><strong>IsThermallyThrottled</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MacPlatformBackend-PurgeUnpinnedMemory"><strong>PurgeUnpinnedMemory</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MacPlatformBackend-StartMonitoringPower"><strong>StartMonitoringPower</strong></a>(self, browser)</dt></dl>
+
+<dl><dt><a name="MacPlatformBackend-StopMonitoringPower"><strong>StopMonitoringPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MacPlatformBackend-TakeScreenshot"><strong>TakeScreenshot</strong></a>(self, file_path)</dt></dl>
+
+<dl><dt><a name="MacPlatformBackend-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="MacPlatformBackend-IsPlatformBackendForHost"><strong>IsPlatformBackendForHost</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.platform.posix_platform_backend.html#PosixPlatformBackend">telemetry.internal.platform.posix_platform_backend.PosixPlatformBackend</a>:<br>
+<dl><dt><a name="MacPlatformBackend-CanLaunchApplication"><strong>CanLaunchApplication</strong></a>(self, application)</dt></dl>
+
+<dl><dt><a name="MacPlatformBackend-GetChildPids"><strong>GetChildPids</strong></a>(self, pid)</dt><dd><tt>Returns&nbsp;a&nbsp;list&nbsp;of&nbsp;child&nbsp;pids&nbsp;of&nbsp;|pid|.</tt></dd></dl>
+
+<dl><dt><a name="MacPlatformBackend-GetCommandLine"><strong>GetCommandLine</strong></a>(self, pid)</dt></dl>
+
+<dl><dt><a name="MacPlatformBackend-GetFileContents"><strong>GetFileContents</strong></a>(self, path)</dt></dl>
+
+<dl><dt><a name="MacPlatformBackend-GetPsOutput"><strong>GetPsOutput</strong></a>(self, columns, pid<font color="#909090">=None</font>)</dt><dd><tt>Returns&nbsp;output&nbsp;of&nbsp;the&nbsp;'ps'&nbsp;command&nbsp;as&nbsp;a&nbsp;list&nbsp;of&nbsp;lines.<br>
+Subclass&nbsp;should&nbsp;override&nbsp;this&nbsp;function.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;columns:&nbsp;A&nbsp;list&nbsp;of&nbsp;require&nbsp;columns,&nbsp;e.g.,&nbsp;['pid',&nbsp;'pss'].<br>
+&nbsp;&nbsp;pid:&nbsp;If&nbsp;not&nbsp;None,&nbsp;returns&nbsp;only&nbsp;the&nbsp;information&nbsp;of&nbsp;the&nbsp;process<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;with&nbsp;the&nbsp;pid.</tt></dd></dl>
+
+<dl><dt><a name="MacPlatformBackend-IsApplicationRunning"><strong>IsApplicationRunning</strong></a>(self, application)</dt></dl>
+
+<dl><dt><a name="MacPlatformBackend-LaunchApplication"><strong>LaunchApplication</strong></a>(self, application, parameters<font color="#909090">=None</font>, elevate_privilege<font color="#909090">=False</font>)</dt></dl>
+
+<dl><dt><a name="MacPlatformBackend-RunCommand"><strong>RunCommand</strong></a>(self, args)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.platform.desktop_platform_backend.html#DesktopPlatformBackend">telemetry.internal.platform.desktop_platform_backend.DesktopPlatformBackend</a>:<br>
+<dl><dt><a name="MacPlatformBackend-FlushSystemCacheForDirectory"><strong>FlushSystemCacheForDirectory</strong></a>(self, directory)</dt></dl>
+
+<dl><dt><a name="MacPlatformBackend-GetDeviceTypeName"><strong>GetDeviceTypeName</strong></a>(self)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">telemetry.internal.platform.platform_backend.PlatformBackend</a>:<br>
+<dl><dt><a name="MacPlatformBackend-CanCaptureVideo"><strong>CanCaptureVideo</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MacPlatformBackend-CanMonitorNetworkData"><strong>CanMonitorNetworkData</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MacPlatformBackend-CanMonitorThermalThrottling"><strong>CanMonitorThermalThrottling</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MacPlatformBackend-CooperativelyShutdown"><strong>CooperativelyShutdown</strong></a>(self, proc, app_name)</dt><dd><tt>Cooperatively&nbsp;shut&nbsp;down&nbsp;the&nbsp;given&nbsp;process&nbsp;from&nbsp;subprocess.Popen.<br>
+&nbsp;<br>
+Currently&nbsp;this&nbsp;is&nbsp;only&nbsp;implemented&nbsp;on&nbsp;Windows.&nbsp;See<br>
+crbug.com/424024&nbsp;for&nbsp;background&nbsp;on&nbsp;why&nbsp;it&nbsp;was&nbsp;added.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;proc:&nbsp;a&nbsp;process&nbsp;object&nbsp;returned&nbsp;from&nbsp;subprocess.Popen.<br>
+&nbsp;&nbsp;app_name:&nbsp;on&nbsp;Windows,&nbsp;is&nbsp;the&nbsp;prefix&nbsp;of&nbsp;the&nbsp;application's&nbsp;window<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class&nbsp;name&nbsp;that&nbsp;should&nbsp;be&nbsp;searched&nbsp;for.&nbsp;This&nbsp;helps&nbsp;ensure<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;that&nbsp;only&nbsp;the&nbsp;application's&nbsp;windows&nbsp;are&nbsp;closed.<br>
+&nbsp;<br>
+Returns&nbsp;True&nbsp;if&nbsp;it&nbsp;is&nbsp;believed&nbsp;the&nbsp;attempt&nbsp;succeeded.</tt></dd></dl>
+
+<dl><dt><a name="MacPlatformBackend-DidCreateBrowser"><strong>DidCreateBrowser</strong></a>(self, browser, browser_backend)</dt></dl>
+
+<dl><dt><a name="MacPlatformBackend-DidStartBrowser"><strong>DidStartBrowser</strong></a>(self, browser, browser_backend)</dt></dl>
+
+<dl><dt><a name="MacPlatformBackend-FlushDnsCache"><strong>FlushDnsCache</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MacPlatformBackend-GetNetworkData"><strong>GetNetworkData</strong></a>(self, browser)</dt></dl>
+
+<dl><dt><a name="MacPlatformBackend-GetRemotePort"><strong>GetRemotePort</strong></a>(self, port)</dt></dl>
+
+<dl><dt><a name="MacPlatformBackend-InitPlatformBackend"><strong>InitPlatformBackend</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MacPlatformBackend-InstallApplication"><strong>InstallApplication</strong></a>(self, application)</dt></dl>
+
+<dl><dt><a name="MacPlatformBackend-IsCooperativeShutdownSupported"><strong>IsCooperativeShutdownSupported</strong></a>(self)</dt><dd><tt>Indicates&nbsp;whether&nbsp;CooperativelyShutdown,&nbsp;below,&nbsp;is&nbsp;supported.<br>
+It&nbsp;is&nbsp;not&nbsp;necessary&nbsp;to&nbsp;implement&nbsp;it&nbsp;on&nbsp;all&nbsp;platforms.</tt></dd></dl>
+
+<dl><dt><a name="MacPlatformBackend-IsDisplayTracingSupported"><strong>IsDisplayTracingSupported</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MacPlatformBackend-PathExists"><strong>PathExists</strong></a>(self, path, timeout<font color="#909090">=None</font>, retries<font color="#909090">=None</font>)</dt><dd><tt>Tests&nbsp;whether&nbsp;the&nbsp;given&nbsp;path&nbsp;exists&nbsp;on&nbsp;the&nbsp;target&nbsp;platform.<br>
+Args:<br>
+&nbsp;&nbsp;path:&nbsp;path&nbsp;in&nbsp;request.<br>
+&nbsp;&nbsp;timeout:&nbsp;timeout.<br>
+&nbsp;&nbsp;retries:&nbsp;num&nbsp;of&nbsp;retries.<br>
+Return:<br>
+&nbsp;&nbsp;Whether&nbsp;the&nbsp;path&nbsp;exists&nbsp;on&nbsp;the&nbsp;target&nbsp;platform.</tt></dd></dl>
+
+<dl><dt><a name="MacPlatformBackend-ReadMsr"><strong>ReadMsr</strong></a>(self, msr_number, start<font color="#909090">=0</font>, length<font color="#909090">=64</font>)</dt><dd><tt>Read&nbsp;a&nbsp;CPU&nbsp;model-specific&nbsp;register&nbsp;(MSR).<br>
+&nbsp;<br>
+Which&nbsp;MSRs&nbsp;are&nbsp;available&nbsp;depends&nbsp;on&nbsp;the&nbsp;CPU&nbsp;model.<br>
+On&nbsp;systems&nbsp;with&nbsp;multiple&nbsp;CPUs,&nbsp;this&nbsp;function&nbsp;may&nbsp;run&nbsp;on&nbsp;any&nbsp;CPU.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;msr_number:&nbsp;The&nbsp;number&nbsp;of&nbsp;the&nbsp;register&nbsp;to&nbsp;read.<br>
+&nbsp;&nbsp;start:&nbsp;The&nbsp;least&nbsp;significant&nbsp;bit&nbsp;to&nbsp;read,&nbsp;zero-indexed.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(Said&nbsp;another&nbsp;way,&nbsp;the&nbsp;number&nbsp;of&nbsp;bits&nbsp;to&nbsp;right-shift&nbsp;the&nbsp;MSR&nbsp;value.)<br>
+&nbsp;&nbsp;length:&nbsp;The&nbsp;number&nbsp;of&nbsp;bits&nbsp;to&nbsp;read.&nbsp;MSRs&nbsp;are&nbsp;64&nbsp;bits,&nbsp;even&nbsp;on&nbsp;32-bit&nbsp;CPUs.</tt></dd></dl>
+
+<dl><dt><a name="MacPlatformBackend-SetFullPerformanceModeEnabled"><strong>SetFullPerformanceModeEnabled</strong></a>(self, enabled)</dt></dl>
+
+<dl><dt><a name="MacPlatformBackend-SetPlatform"><strong>SetPlatform</strong></a>(self, platform)</dt></dl>
+
+<dl><dt><a name="MacPlatformBackend-StartDisplayTracing"><strong>StartDisplayTracing</strong></a>(self)</dt><dd><tt>Start&nbsp;gathering&nbsp;a&nbsp;trace&nbsp;with&nbsp;frame&nbsp;timestamps&nbsp;close&nbsp;to&nbsp;physical<br>
+display.</tt></dd></dl>
+
+<dl><dt><a name="MacPlatformBackend-StartVideoCapture"><strong>StartVideoCapture</strong></a>(self, min_bitrate_mbps)</dt></dl>
+
+<dl><dt><a name="MacPlatformBackend-StopDisplayTracing"><strong>StopDisplayTracing</strong></a>(self)</dt><dd><tt>Stop&nbsp;gathering&nbsp;a&nbsp;trace&nbsp;with&nbsp;frame&nbsp;timestamps&nbsp;close&nbsp;to&nbsp;physical&nbsp;display.<br>
+&nbsp;<br>
+Returns&nbsp;a&nbsp;raw&nbsp;tracing&nbsp;events&nbsp;that&nbsp;contains&nbsp;the&nbsp;timestamps&nbsp;of&nbsp;physical<br>
+display.</tt></dd></dl>
+
+<dl><dt><a name="MacPlatformBackend-StopVideoCapture"><strong>StopVideoCapture</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MacPlatformBackend-WillCloseBrowser"><strong>WillCloseBrowser</strong></a>(self, browser, browser_backend)</dt></dl>
+
+<hr>
+Class methods inherited from <a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">telemetry.internal.platform.platform_backend.PlatformBackend</a>:<br>
+<dl><dt><a name="MacPlatformBackend-CreatePlatformForDevice"><strong>CreatePlatformForDevice</strong></a>(cls, device, finder_options)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="MacPlatformBackend-SupportsDevice"><strong>SupportsDevice</strong></a>(cls, device)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Returns&nbsp;whether&nbsp;this&nbsp;platform&nbsp;backend&nbsp;supports&nbsp;intialization&nbsp;from&nbsp;the<br>
+device.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">telemetry.internal.platform.platform_backend.PlatformBackend</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>forwarder_factory</strong></dt>
+</dl>
+<dl><dt><strong>is_host_platform</strong></dt>
+</dl>
+<dl><dt><strong>is_video_capture_running</strong></dt>
+</dl>
+<dl><dt><strong>network_controller_backend</strong></dt>
+</dl>
+<dl><dt><strong>platform</strong></dt>
+</dl>
+<dl><dt><strong>running_browser_backends</strong></dt>
+</dl>
+<dl><dt><strong>tracing_controller_backend</strong></dt>
+</dl>
+<dl><dt><strong>wpr_ca_cert_path</strong></dt>
+</dl>
+<dl><dt><strong>wpr_http_device_port</strong></dt>
+</dl>
+<dl><dt><strong>wpr_https_device_port</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.msr_server_win.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.msr_server_win.html
new file mode 100644
index 0000000..3f42f64
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.msr_server_win.html
@@ -0,0 +1,184 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.msr_server_win</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.msr_server_win</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/msr_server_win.py">telemetry/internal/platform/msr_server_win.py</a></font></td></tr></table>
+    <p><tt>A&nbsp;server&nbsp;that&nbsp;serves&nbsp;MSR&nbsp;values&nbsp;over&nbsp;TCP.&nbsp;Takes&nbsp;a&nbsp;port&nbsp;as&nbsp;its&nbsp;sole&nbsp;parameter.<br>
+&nbsp;<br>
+The&nbsp;reference&nbsp;client&nbsp;for&nbsp;this&nbsp;server&nbsp;is&nbsp;msr_power_monitor.MsrPowerMonitor.<br>
+&nbsp;<br>
+Must&nbsp;be&nbsp;run&nbsp;as&nbsp;Administrator.&nbsp;We&nbsp;use&nbsp;TCP&nbsp;instead&nbsp;of&nbsp;named&nbsp;pipes&nbsp;or&nbsp;another&nbsp;IPC<br>
+to&nbsp;avoid&nbsp;dealing&nbsp;with&nbsp;the&nbsp;pipe&nbsp;security&nbsp;mechanisms.&nbsp;We&nbsp;take&nbsp;the&nbsp;port&nbsp;as&nbsp;a<br>
+parameter&nbsp;instead&nbsp;of&nbsp;choosing&nbsp;one,&nbsp;because&nbsp;it's&nbsp;hard&nbsp;to&nbsp;communicate&nbsp;the&nbsp;port<br>
+number&nbsp;across&nbsp;integrity&nbsp;levels.<br>
+&nbsp;<br>
+Requires&nbsp;WinRing0&nbsp;to&nbsp;be&nbsp;installed&nbsp;in&nbsp;the&nbsp;Python&nbsp;directory.<br>
+msr_power_monitor.MsrPowerMonitor&nbsp;does&nbsp;this&nbsp;if&nbsp;needed.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="SocketServer.html">SocketServer</a><br>
+<a href="argparse.html">argparse</a><br>
+</td><td width="25%" valign=top><a href="ctypes.html">ctypes</a><br>
+<a href="os.html">os</a><br>
+</td><td width="25%" valign=top><a href="struct.html">struct</a><br>
+<a href="sys.html">sys</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="SocketServer.html#StreamRequestHandler">SocketServer.StreamRequestHandler</a>(<a href="SocketServer.html#BaseRequestHandler">SocketServer.BaseRequestHandler</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.msr_server_win.html#MsrRequestHandler">MsrRequestHandler</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="exceptions.html#OSError">exceptions.OSError</a>(<a href="exceptions.html#EnvironmentError">exceptions.EnvironmentError</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.msr_server_win.html#WinRing0Error">WinRing0Error</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="MsrRequestHandler">class <strong>MsrRequestHandler</strong></a>(<a href="SocketServer.html#StreamRequestHandler">SocketServer.StreamRequestHandler</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.msr_server_win.html#MsrRequestHandler">MsrRequestHandler</a></dd>
+<dd><a href="SocketServer.html#StreamRequestHandler">SocketServer.StreamRequestHandler</a></dd>
+<dd><a href="SocketServer.html#BaseRequestHandler">SocketServer.BaseRequestHandler</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="MsrRequestHandler-handle"><strong>handle</strong></a>(self)</dt></dl>
+
+<hr>
+Methods inherited from <a href="SocketServer.html#StreamRequestHandler">SocketServer.StreamRequestHandler</a>:<br>
+<dl><dt><a name="MsrRequestHandler-finish"><strong>finish</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MsrRequestHandler-setup"><strong>setup</strong></a>(self)</dt></dl>
+
+<hr>
+Data and other attributes inherited from <a href="SocketServer.html#StreamRequestHandler">SocketServer.StreamRequestHandler</a>:<br>
+<dl><dt><strong>disable_nagle_algorithm</strong> = False</dl>
+
+<dl><dt><strong>rbufsize</strong> = -1</dl>
+
+<dl><dt><strong>timeout</strong> = None</dl>
+
+<dl><dt><strong>wbufsize</strong> = 0</dl>
+
+<hr>
+Methods inherited from <a href="SocketServer.html#BaseRequestHandler">SocketServer.BaseRequestHandler</a>:<br>
+<dl><dt><a name="MsrRequestHandler-__init__"><strong>__init__</strong></a>(self, request, client_address, server)</dt></dl>
+
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="WinRing0Error">class <strong>WinRing0Error</strong></a>(<a href="exceptions.html#OSError">exceptions.OSError</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.msr_server_win.html#WinRing0Error">WinRing0Error</a></dd>
+<dd><a href="exceptions.html#OSError">exceptions.OSError</a></dd>
+<dd><a href="exceptions.html#EnvironmentError">exceptions.EnvironmentError</a></dd>
+<dd><a href="exceptions.html#StandardError">exceptions.StandardError</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#OSError">exceptions.OSError</a>:<br>
+<dl><dt><a name="WinRing0Error-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#WinRing0Error-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#OSError">exceptions.OSError</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#WinRing0Error-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;object&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#EnvironmentError">exceptions.EnvironmentError</a>:<br>
+<dl><dt><a name="WinRing0Error-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="WinRing0Error-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#WinRing0Error-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#EnvironmentError">exceptions.EnvironmentError</a>:<br>
+<dl><dt><strong>errno</strong></dt>
+<dd><tt>exception&nbsp;errno</tt></dd>
+</dl>
+<dl><dt><strong>filename</strong></dt>
+<dd><tt>exception&nbsp;filename</tt></dd>
+</dl>
+<dl><dt><strong>strerror</strong></dt>
+<dd><tt>exception&nbsp;strerror</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="WinRing0Error-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#WinRing0Error-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="WinRing0Error-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#WinRing0Error-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="WinRing0Error-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#WinRing0Error-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="WinRing0Error-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#WinRing0Error-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="WinRing0Error-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#WinRing0Error-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="WinRing0Error-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#WinRing0Error-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="WinRing0Error-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="WinRing0Error-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-main"><strong>main</strong></a>()</dt></dl>
+</td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>WINRING0_STATUS_MESSAGES</strong> = ('No error', 'Unsupported platform', 'Driver not loaded. You may need to run as Administrator', 'Driver not found', 'Driver unloaded by other process', 'Driver not loaded because of executing on Network Drive', 'Unknown error')</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.network_controller_backend.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.network_controller_backend.html
new file mode 100644
index 0000000..2525f4d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.network_controller_backend.html
@@ -0,0 +1,225 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.network_controller_backend</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.network_controller_backend</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/network_controller_backend.py">telemetry/internal/platform/network_controller_backend.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.forwarders.html">telemetry.internal.forwarders</a><br>
+</td><td width="25%" valign=top><a href="os.html">os</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.util.webpagereplay.html">telemetry.internal.util.webpagereplay</a><br>
+</td><td width="25%" valign=top><a href="telemetry.util.wpr_modes.html">telemetry.util.wpr_modes</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.network_controller_backend.html#NetworkControllerBackend">NetworkControllerBackend</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="exceptions.html#Exception">exceptions.Exception</a>(<a href="exceptions.html#BaseException">exceptions.BaseException</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.network_controller_backend.html#ArchiveDoesNotExistError">ArchiveDoesNotExistError</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.internal.platform.network_controller_backend.html#ReplayAndBrowserPortsError">ReplayAndBrowserPortsError</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ArchiveDoesNotExistError">class <strong>ArchiveDoesNotExistError</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Raised&nbsp;when&nbsp;the&nbsp;archive&nbsp;path&nbsp;does&nbsp;not&nbsp;exist&nbsp;for&nbsp;replay&nbsp;mode.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.network_controller_backend.html#ArchiveDoesNotExistError">ArchiveDoesNotExistError</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="ArchiveDoesNotExistError-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#ArchiveDoesNotExistError-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#ArchiveDoesNotExistError-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="ArchiveDoesNotExistError-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#ArchiveDoesNotExistError-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="ArchiveDoesNotExistError-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#ArchiveDoesNotExistError-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="ArchiveDoesNotExistError-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#ArchiveDoesNotExistError-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="ArchiveDoesNotExistError-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#ArchiveDoesNotExistError-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="ArchiveDoesNotExistError-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="ArchiveDoesNotExistError-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#ArchiveDoesNotExistError-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="ArchiveDoesNotExistError-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#ArchiveDoesNotExistError-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="ArchiveDoesNotExistError-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="ArchiveDoesNotExistError-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#ArchiveDoesNotExistError-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="ArchiveDoesNotExistError-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="NetworkControllerBackend">class <strong>NetworkControllerBackend</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Control&nbsp;network&nbsp;settings&nbsp;and&nbsp;servers&nbsp;to&nbsp;simulate&nbsp;the&nbsp;Web.<br>
+&nbsp;<br>
+Network&nbsp;changes&nbsp;include&nbsp;forwarding&nbsp;device&nbsp;ports&nbsp;to&nbsp;host&nbsp;platform&nbsp;ports.<br>
+Web&nbsp;Page&nbsp;Replay&nbsp;is&nbsp;used&nbsp;to&nbsp;record&nbsp;and&nbsp;replay&nbsp;HTTP/HTTPS&nbsp;responses.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="NetworkControllerBackend-SetReplayArgs"><strong>SetReplayArgs</strong></a>(self, archive_path, wpr_mode, netsim, extra_wpr_args, make_javascript_deterministic<font color="#909090">=False</font>)</dt><dd><tt>Save&nbsp;the&nbsp;arguments&nbsp;needed&nbsp;for&nbsp;replay.<br>
+&nbsp;<br>
+To&nbsp;make&nbsp;the&nbsp;settings&nbsp;effective,&nbsp;this&nbsp;call&nbsp;must&nbsp;be&nbsp;followed&nbsp;by&nbsp;a&nbsp;call<br>
+to&nbsp;UpdateReplay.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;archive_path:&nbsp;a&nbsp;path&nbsp;to&nbsp;a&nbsp;specific&nbsp;WPR&nbsp;archive.<br>
+&nbsp;&nbsp;wpr_mode:&nbsp;one&nbsp;of&nbsp;wpr_modes.WPR_OFF,&nbsp;wpr_modes.WPR_APPEND,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;wpr_modes.WPR_REPLAY,&nbsp;or&nbsp;wpr_modes.WPR_RECORD.<br>
+&nbsp;&nbsp;netsim:&nbsp;a&nbsp;net_config&nbsp;string&nbsp;('dialup',&nbsp;'3g',&nbsp;'dsl',&nbsp;'cable',&nbsp;or&nbsp;'fios').<br>
+&nbsp;&nbsp;extra_wpr_args:&nbsp;a&nbsp;list&nbsp;of&nbsp;addtional&nbsp;replay&nbsp;args&nbsp;(or&nbsp;an&nbsp;empty&nbsp;list).<br>
+&nbsp;&nbsp;make_javascript_deterministic:&nbsp;True&nbsp;if&nbsp;replay&nbsp;should&nbsp;inject&nbsp;a&nbsp;script<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;to&nbsp;make&nbsp;JavaScript&nbsp;behave&nbsp;deterministically&nbsp;(e.g.,&nbsp;override&nbsp;Date()).</tt></dd></dl>
+
+<dl><dt><a name="NetworkControllerBackend-StopReplay"><strong>StopReplay</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="NetworkControllerBackend-UpdateReplay"><strong>UpdateReplay</strong></a>(self, browser_backend<font color="#909090">=None</font>)</dt><dd><tt>Start&nbsp;or&nbsp;reuse&nbsp;Web&nbsp;Page&nbsp;Replay.<br>
+&nbsp;<br>
+UpdateReplay&nbsp;must&nbsp;be&nbsp;called&nbsp;after&nbsp;every&nbsp;call&nbsp;to&nbsp;SetReplayArgs.<br>
+&nbsp;<br>
+TODO(slamm):&nbsp;Update&nbsp;replay&nbsp;in&nbsp;SetReplayArgs&nbsp;once&nbsp;the&nbsp;browser_backend<br>
+&nbsp;&nbsp;&nbsp;&nbsp;dependencies&nbsp;move&nbsp;to&nbsp;platform.&nbsp;https://crbug.com/423962<br>
+&nbsp;&nbsp;&nbsp;&nbsp;browser_backend&nbsp;properties&nbsp;used:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;Input:&nbsp;wpr_port_pairs<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;Output:&nbsp;wpr_port_pairs&nbsp;(browser&nbsp;uses&nbsp;for&nbsp;--testing-fixed-*&nbsp;flags).<br>
+Args:<br>
+&nbsp;&nbsp;browser_backend:&nbsp;instance&nbsp;of&nbsp;telemetry.core.backends.browser_backend</tt></dd></dl>
+
+<dl><dt><a name="NetworkControllerBackend-__init__"><strong>__init__</strong></a>(self, platform_backend)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>wpr_http_device_port</strong></dt>
+</dl>
+<dl><dt><strong>wpr_https_device_port</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ReplayAndBrowserPortsError">class <strong>ReplayAndBrowserPortsError</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Raised&nbsp;an&nbsp;existing&nbsp;browser&nbsp;would&nbsp;get&nbsp;different&nbsp;remote&nbsp;replay&nbsp;ports.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.network_controller_backend.html#ReplayAndBrowserPortsError">ReplayAndBrowserPortsError</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="ReplayAndBrowserPortsError-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#ReplayAndBrowserPortsError-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#ReplayAndBrowserPortsError-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="ReplayAndBrowserPortsError-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#ReplayAndBrowserPortsError-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="ReplayAndBrowserPortsError-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#ReplayAndBrowserPortsError-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="ReplayAndBrowserPortsError-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#ReplayAndBrowserPortsError-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="ReplayAndBrowserPortsError-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#ReplayAndBrowserPortsError-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="ReplayAndBrowserPortsError-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="ReplayAndBrowserPortsError-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#ReplayAndBrowserPortsError-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="ReplayAndBrowserPortsError-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#ReplayAndBrowserPortsError-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="ReplayAndBrowserPortsError-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="ReplayAndBrowserPortsError-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#ReplayAndBrowserPortsError-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="ReplayAndBrowserPortsError-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.platform_backend.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.platform_backend.html
new file mode 100644
index 0000000..41c9bfa
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.platform_backend.html
@@ -0,0 +1,225 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.platform_backend</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.platform_backend</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/platform_backend.py">telemetry/internal/platform/platform_backend.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.forwarders.do_nothing_forwarder.html">telemetry.internal.forwarders.do_nothing_forwarder</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.network_controller_backend.html">telemetry.internal.platform.network_controller_backend</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.tracing_controller_backend.html">telemetry.internal.platform.tracing_controller_backend</a><br>
+</td><td width="25%" valign=top><a href="weakref.html">weakref</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">PlatformBackend</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="PlatformBackend">class <strong>PlatformBackend</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="PlatformBackend-CanCaptureVideo"><strong>CanCaptureVideo</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PlatformBackend-CanFlushIndividualFilesFromSystemCache"><strong>CanFlushIndividualFilesFromSystemCache</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PlatformBackend-CanLaunchApplication"><strong>CanLaunchApplication</strong></a>(self, application)</dt></dl>
+
+<dl><dt><a name="PlatformBackend-CanMeasurePerApplicationPower"><strong>CanMeasurePerApplicationPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PlatformBackend-CanMonitorNetworkData"><strong>CanMonitorNetworkData</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PlatformBackend-CanMonitorPower"><strong>CanMonitorPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PlatformBackend-CanMonitorThermalThrottling"><strong>CanMonitorThermalThrottling</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PlatformBackend-CanTakeScreenshot"><strong>CanTakeScreenshot</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PlatformBackend-CooperativelyShutdown"><strong>CooperativelyShutdown</strong></a>(self, proc, app_name)</dt><dd><tt>Cooperatively&nbsp;shut&nbsp;down&nbsp;the&nbsp;given&nbsp;process&nbsp;from&nbsp;subprocess.Popen.<br>
+&nbsp;<br>
+Currently&nbsp;this&nbsp;is&nbsp;only&nbsp;implemented&nbsp;on&nbsp;Windows.&nbsp;See<br>
+crbug.com/424024&nbsp;for&nbsp;background&nbsp;on&nbsp;why&nbsp;it&nbsp;was&nbsp;added.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;proc:&nbsp;a&nbsp;process&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;returned&nbsp;from&nbsp;subprocess.Popen.<br>
+&nbsp;&nbsp;app_name:&nbsp;on&nbsp;Windows,&nbsp;is&nbsp;the&nbsp;prefix&nbsp;of&nbsp;the&nbsp;application's&nbsp;window<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class&nbsp;name&nbsp;that&nbsp;should&nbsp;be&nbsp;searched&nbsp;for.&nbsp;This&nbsp;helps&nbsp;ensure<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;that&nbsp;only&nbsp;the&nbsp;application's&nbsp;windows&nbsp;are&nbsp;closed.<br>
+&nbsp;<br>
+Returns&nbsp;True&nbsp;if&nbsp;it&nbsp;is&nbsp;believed&nbsp;the&nbsp;attempt&nbsp;succeeded.</tt></dd></dl>
+
+<dl><dt><a name="PlatformBackend-DidCreateBrowser"><strong>DidCreateBrowser</strong></a>(self, browser, browser_backend)</dt></dl>
+
+<dl><dt><a name="PlatformBackend-DidStartBrowser"><strong>DidStartBrowser</strong></a>(self, browser, browser_backend)</dt></dl>
+
+<dl><dt><a name="PlatformBackend-FlushDnsCache"><strong>FlushDnsCache</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PlatformBackend-FlushEntireSystemCache"><strong>FlushEntireSystemCache</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PlatformBackend-FlushSystemCacheForDirectory"><strong>FlushSystemCacheForDirectory</strong></a>(self, directory)</dt></dl>
+
+<dl><dt><a name="PlatformBackend-GetArchName"><strong>GetArchName</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PlatformBackend-GetChildPids"><strong>GetChildPids</strong></a>(self, pid)</dt></dl>
+
+<dl><dt><a name="PlatformBackend-GetCommandLine"><strong>GetCommandLine</strong></a>(self, pid)</dt></dl>
+
+<dl><dt><a name="PlatformBackend-GetCpuStats"><strong>GetCpuStats</strong></a>(self, pid)</dt></dl>
+
+<dl><dt><a name="PlatformBackend-GetCpuTimestamp"><strong>GetCpuTimestamp</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PlatformBackend-GetDeviceTypeName"><strong>GetDeviceTypeName</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PlatformBackend-GetMemoryStats"><strong>GetMemoryStats</strong></a>(self, pid)</dt></dl>
+
+<dl><dt><a name="PlatformBackend-GetNetworkData"><strong>GetNetworkData</strong></a>(self, browser)</dt></dl>
+
+<dl><dt><a name="PlatformBackend-GetOSName"><strong>GetOSName</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PlatformBackend-GetOSVersionName"><strong>GetOSVersionName</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PlatformBackend-GetRemotePort"><strong>GetRemotePort</strong></a>(self, port)</dt></dl>
+
+<dl><dt><a name="PlatformBackend-GetSystemCommitCharge"><strong>GetSystemCommitCharge</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PlatformBackend-GetSystemTotalPhysicalMemory"><strong>GetSystemTotalPhysicalMemory</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PlatformBackend-HasBeenThermallyThrottled"><strong>HasBeenThermallyThrottled</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PlatformBackend-InitPlatformBackend"><strong>InitPlatformBackend</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PlatformBackend-InstallApplication"><strong>InstallApplication</strong></a>(self, application)</dt></dl>
+
+<dl><dt><a name="PlatformBackend-IsApplicationRunning"><strong>IsApplicationRunning</strong></a>(self, application)</dt></dl>
+
+<dl><dt><a name="PlatformBackend-IsCooperativeShutdownSupported"><strong>IsCooperativeShutdownSupported</strong></a>(self)</dt><dd><tt>Indicates&nbsp;whether&nbsp;CooperativelyShutdown,&nbsp;below,&nbsp;is&nbsp;supported.<br>
+It&nbsp;is&nbsp;not&nbsp;necessary&nbsp;to&nbsp;implement&nbsp;it&nbsp;on&nbsp;all&nbsp;platforms.</tt></dd></dl>
+
+<dl><dt><a name="PlatformBackend-IsDisplayTracingSupported"><strong>IsDisplayTracingSupported</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PlatformBackend-IsThermallyThrottled"><strong>IsThermallyThrottled</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PlatformBackend-LaunchApplication"><strong>LaunchApplication</strong></a>(self, application, parameters<font color="#909090">=None</font>, elevate_privilege<font color="#909090">=False</font>)</dt></dl>
+
+<dl><dt><a name="PlatformBackend-PathExists"><strong>PathExists</strong></a>(self, path, timeout<font color="#909090">=None</font>, retries<font color="#909090">=None</font>)</dt><dd><tt>Tests&nbsp;whether&nbsp;the&nbsp;given&nbsp;path&nbsp;exists&nbsp;on&nbsp;the&nbsp;target&nbsp;platform.<br>
+Args:<br>
+&nbsp;&nbsp;path:&nbsp;path&nbsp;in&nbsp;request.<br>
+&nbsp;&nbsp;timeout:&nbsp;timeout.<br>
+&nbsp;&nbsp;retries:&nbsp;num&nbsp;of&nbsp;retries.<br>
+Return:<br>
+&nbsp;&nbsp;Whether&nbsp;the&nbsp;path&nbsp;exists&nbsp;on&nbsp;the&nbsp;target&nbsp;platform.</tt></dd></dl>
+
+<dl><dt><a name="PlatformBackend-PurgeUnpinnedMemory"><strong>PurgeUnpinnedMemory</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PlatformBackend-ReadMsr"><strong>ReadMsr</strong></a>(self, msr_number, start<font color="#909090">=0</font>, length<font color="#909090">=64</font>)</dt><dd><tt>Read&nbsp;a&nbsp;CPU&nbsp;model-specific&nbsp;register&nbsp;(MSR).<br>
+&nbsp;<br>
+Which&nbsp;MSRs&nbsp;are&nbsp;available&nbsp;depends&nbsp;on&nbsp;the&nbsp;CPU&nbsp;model.<br>
+On&nbsp;systems&nbsp;with&nbsp;multiple&nbsp;CPUs,&nbsp;this&nbsp;function&nbsp;may&nbsp;run&nbsp;on&nbsp;any&nbsp;CPU.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;msr_number:&nbsp;The&nbsp;number&nbsp;of&nbsp;the&nbsp;register&nbsp;to&nbsp;read.<br>
+&nbsp;&nbsp;start:&nbsp;The&nbsp;least&nbsp;significant&nbsp;bit&nbsp;to&nbsp;read,&nbsp;zero-indexed.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(Said&nbsp;another&nbsp;way,&nbsp;the&nbsp;number&nbsp;of&nbsp;bits&nbsp;to&nbsp;right-shift&nbsp;the&nbsp;MSR&nbsp;value.)<br>
+&nbsp;&nbsp;length:&nbsp;The&nbsp;number&nbsp;of&nbsp;bits&nbsp;to&nbsp;read.&nbsp;MSRs&nbsp;are&nbsp;64&nbsp;bits,&nbsp;even&nbsp;on&nbsp;32-bit&nbsp;CPUs.</tt></dd></dl>
+
+<dl><dt><a name="PlatformBackend-SetFullPerformanceModeEnabled"><strong>SetFullPerformanceModeEnabled</strong></a>(self, enabled)</dt></dl>
+
+<dl><dt><a name="PlatformBackend-SetPlatform"><strong>SetPlatform</strong></a>(self, platform)</dt></dl>
+
+<dl><dt><a name="PlatformBackend-StartDisplayTracing"><strong>StartDisplayTracing</strong></a>(self)</dt><dd><tt>Start&nbsp;gathering&nbsp;a&nbsp;trace&nbsp;with&nbsp;frame&nbsp;timestamps&nbsp;close&nbsp;to&nbsp;physical<br>
+display.</tt></dd></dl>
+
+<dl><dt><a name="PlatformBackend-StartMonitoringPower"><strong>StartMonitoringPower</strong></a>(self, browser)</dt></dl>
+
+<dl><dt><a name="PlatformBackend-StartVideoCapture"><strong>StartVideoCapture</strong></a>(self, min_bitrate_mbps)</dt></dl>
+
+<dl><dt><a name="PlatformBackend-StopDisplayTracing"><strong>StopDisplayTracing</strong></a>(self)</dt><dd><tt>Stop&nbsp;gathering&nbsp;a&nbsp;trace&nbsp;with&nbsp;frame&nbsp;timestamps&nbsp;close&nbsp;to&nbsp;physical&nbsp;display.<br>
+&nbsp;<br>
+Returns&nbsp;a&nbsp;raw&nbsp;tracing&nbsp;events&nbsp;that&nbsp;contains&nbsp;the&nbsp;timestamps&nbsp;of&nbsp;physical<br>
+display.</tt></dd></dl>
+
+<dl><dt><a name="PlatformBackend-StopMonitoringPower"><strong>StopMonitoringPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PlatformBackend-StopVideoCapture"><strong>StopVideoCapture</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PlatformBackend-TakeScreenshot"><strong>TakeScreenshot</strong></a>(self, file_path)</dt></dl>
+
+<dl><dt><a name="PlatformBackend-WillCloseBrowser"><strong>WillCloseBrowser</strong></a>(self, browser, browser_backend)</dt></dl>
+
+<dl><dt><a name="PlatformBackend-__init__"><strong>__init__</strong></a>(self, device<font color="#909090">=None</font>)</dt><dd><tt>Initalize&nbsp;an&nbsp;instance&nbsp;of&nbsp;<a href="#PlatformBackend">PlatformBackend</a>&nbsp;from&nbsp;a&nbsp;device&nbsp;optionally.<br>
+Call&nbsp;sites&nbsp;need&nbsp;to&nbsp;use&nbsp;SupportsDevice&nbsp;before&nbsp;intialization&nbsp;to&nbsp;check<br>
+whether&nbsp;this&nbsp;platform&nbsp;backend&nbsp;supports&nbsp;the&nbsp;device.<br>
+If&nbsp;device&nbsp;is&nbsp;None,&nbsp;this&nbsp;constructor&nbsp;returns&nbsp;the&nbsp;host&nbsp;platform&nbsp;backend<br>
+which&nbsp;telemetry&nbsp;is&nbsp;running&nbsp;on.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;device:&nbsp;an&nbsp;instance&nbsp;of&nbsp;telemetry.core.platform.device.Device.</tt></dd></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="PlatformBackend-CreatePlatformForDevice"><strong>CreatePlatformForDevice</strong></a>(cls, device, finder_options)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="PlatformBackend-IsPlatformBackendForHost"><strong>IsPlatformBackendForHost</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Returns&nbsp;whether&nbsp;this&nbsp;platform&nbsp;backend&nbsp;is&nbsp;the&nbsp;platform&nbsp;backend&nbsp;to&nbsp;be&nbsp;used<br>
+for&nbsp;the&nbsp;host&nbsp;device&nbsp;which&nbsp;telemetry&nbsp;is&nbsp;running&nbsp;on.</tt></dd></dl>
+
+<dl><dt><a name="PlatformBackend-SupportsDevice"><strong>SupportsDevice</strong></a>(cls, device)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Returns&nbsp;whether&nbsp;this&nbsp;platform&nbsp;backend&nbsp;supports&nbsp;intialization&nbsp;from&nbsp;the<br>
+device.</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>forwarder_factory</strong></dt>
+</dl>
+<dl><dt><strong>is_host_platform</strong></dt>
+</dl>
+<dl><dt><strong>is_video_capture_running</strong></dt>
+</dl>
+<dl><dt><strong>network_controller_backend</strong></dt>
+</dl>
+<dl><dt><strong>platform</strong></dt>
+</dl>
+<dl><dt><strong>running_browser_backends</strong></dt>
+</dl>
+<dl><dt><strong>tracing_controller_backend</strong></dt>
+</dl>
+<dl><dt><strong>wpr_ca_cert_path</strong></dt>
+</dl>
+<dl><dt><strong>wpr_http_device_port</strong></dt>
+</dl>
+<dl><dt><strong>wpr_https_device_port</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.posix_platform_backend.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.posix_platform_backend.html
new file mode 100644
index 0000000..cabca16
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.posix_platform_backend.html
@@ -0,0 +1,253 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.posix_platform_backend</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.posix_platform_backend</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/posix_platform_backend.py">telemetry/internal/platform/posix_platform_backend.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.platform.desktop_platform_backend.html">telemetry.internal.platform.desktop_platform_backend</a><br>
+<a href="distutils.html">distutils</a><br>
+<a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top><a href="os.html">os</a><br>
+<a href="telemetry.internal.util.ps_util.html">telemetry.internal.util.ps_util</a><br>
+<a href="re.html">re</a><br>
+</td><td width="25%" valign=top><a href="stat.html">stat</a><br>
+<a href="subprocess.html">subprocess</a><br>
+<a href="sys.html">sys</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.desktop_platform_backend.html#DesktopPlatformBackend">telemetry.internal.platform.desktop_platform_backend.DesktopPlatformBackend</a>(<a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">telemetry.internal.platform.platform_backend.PlatformBackend</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.posix_platform_backend.html#PosixPlatformBackend">PosixPlatformBackend</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="PosixPlatformBackend">class <strong>PosixPlatformBackend</strong></a>(<a href="telemetry.internal.platform.desktop_platform_backend.html#DesktopPlatformBackend">telemetry.internal.platform.desktop_platform_backend.DesktopPlatformBackend</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.posix_platform_backend.html#PosixPlatformBackend">PosixPlatformBackend</a></dd>
+<dd><a href="telemetry.internal.platform.desktop_platform_backend.html#DesktopPlatformBackend">telemetry.internal.platform.desktop_platform_backend.DesktopPlatformBackend</a></dd>
+<dd><a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">telemetry.internal.platform.platform_backend.PlatformBackend</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="PosixPlatformBackend-CanLaunchApplication"><strong>CanLaunchApplication</strong></a>(self, application)</dt></dl>
+
+<dl><dt><a name="PosixPlatformBackend-GetChildPids"><strong>GetChildPids</strong></a>(self, pid)</dt><dd><tt>Returns&nbsp;a&nbsp;list&nbsp;of&nbsp;child&nbsp;pids&nbsp;of&nbsp;|pid|.</tt></dd></dl>
+
+<dl><dt><a name="PosixPlatformBackend-GetCommandLine"><strong>GetCommandLine</strong></a>(self, pid)</dt></dl>
+
+<dl><dt><a name="PosixPlatformBackend-GetFileContents"><strong>GetFileContents</strong></a>(self, path)</dt></dl>
+
+<dl><dt><a name="PosixPlatformBackend-GetPsOutput"><strong>GetPsOutput</strong></a>(self, columns, pid<font color="#909090">=None</font>)</dt><dd><tt>Returns&nbsp;output&nbsp;of&nbsp;the&nbsp;'ps'&nbsp;command&nbsp;as&nbsp;a&nbsp;list&nbsp;of&nbsp;lines.<br>
+Subclass&nbsp;should&nbsp;override&nbsp;this&nbsp;function.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;columns:&nbsp;A&nbsp;list&nbsp;of&nbsp;require&nbsp;columns,&nbsp;e.g.,&nbsp;['pid',&nbsp;'pss'].<br>
+&nbsp;&nbsp;pid:&nbsp;If&nbsp;not&nbsp;None,&nbsp;returns&nbsp;only&nbsp;the&nbsp;information&nbsp;of&nbsp;the&nbsp;process<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;with&nbsp;the&nbsp;pid.</tt></dd></dl>
+
+<dl><dt><a name="PosixPlatformBackend-IsApplicationRunning"><strong>IsApplicationRunning</strong></a>(self, application)</dt></dl>
+
+<dl><dt><a name="PosixPlatformBackend-LaunchApplication"><strong>LaunchApplication</strong></a>(self, application, parameters<font color="#909090">=None</font>, elevate_privilege<font color="#909090">=False</font>)</dt></dl>
+
+<dl><dt><a name="PosixPlatformBackend-RunCommand"><strong>RunCommand</strong></a>(self, args)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.platform.desktop_platform_backend.html#DesktopPlatformBackend">telemetry.internal.platform.desktop_platform_backend.DesktopPlatformBackend</a>:<br>
+<dl><dt><a name="PosixPlatformBackend-FlushSystemCacheForDirectory"><strong>FlushSystemCacheForDirectory</strong></a>(self, directory)</dt></dl>
+
+<dl><dt><a name="PosixPlatformBackend-GetDeviceTypeName"><strong>GetDeviceTypeName</strong></a>(self)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">telemetry.internal.platform.platform_backend.PlatformBackend</a>:<br>
+<dl><dt><a name="PosixPlatformBackend-CanCaptureVideo"><strong>CanCaptureVideo</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PosixPlatformBackend-CanFlushIndividualFilesFromSystemCache"><strong>CanFlushIndividualFilesFromSystemCache</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PosixPlatformBackend-CanMeasurePerApplicationPower"><strong>CanMeasurePerApplicationPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PosixPlatformBackend-CanMonitorNetworkData"><strong>CanMonitorNetworkData</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PosixPlatformBackend-CanMonitorPower"><strong>CanMonitorPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PosixPlatformBackend-CanMonitorThermalThrottling"><strong>CanMonitorThermalThrottling</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PosixPlatformBackend-CanTakeScreenshot"><strong>CanTakeScreenshot</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PosixPlatformBackend-CooperativelyShutdown"><strong>CooperativelyShutdown</strong></a>(self, proc, app_name)</dt><dd><tt>Cooperatively&nbsp;shut&nbsp;down&nbsp;the&nbsp;given&nbsp;process&nbsp;from&nbsp;subprocess.Popen.<br>
+&nbsp;<br>
+Currently&nbsp;this&nbsp;is&nbsp;only&nbsp;implemented&nbsp;on&nbsp;Windows.&nbsp;See<br>
+crbug.com/424024&nbsp;for&nbsp;background&nbsp;on&nbsp;why&nbsp;it&nbsp;was&nbsp;added.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;proc:&nbsp;a&nbsp;process&nbsp;object&nbsp;returned&nbsp;from&nbsp;subprocess.Popen.<br>
+&nbsp;&nbsp;app_name:&nbsp;on&nbsp;Windows,&nbsp;is&nbsp;the&nbsp;prefix&nbsp;of&nbsp;the&nbsp;application's&nbsp;window<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class&nbsp;name&nbsp;that&nbsp;should&nbsp;be&nbsp;searched&nbsp;for.&nbsp;This&nbsp;helps&nbsp;ensure<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;that&nbsp;only&nbsp;the&nbsp;application's&nbsp;windows&nbsp;are&nbsp;closed.<br>
+&nbsp;<br>
+Returns&nbsp;True&nbsp;if&nbsp;it&nbsp;is&nbsp;believed&nbsp;the&nbsp;attempt&nbsp;succeeded.</tt></dd></dl>
+
+<dl><dt><a name="PosixPlatformBackend-DidCreateBrowser"><strong>DidCreateBrowser</strong></a>(self, browser, browser_backend)</dt></dl>
+
+<dl><dt><a name="PosixPlatformBackend-DidStartBrowser"><strong>DidStartBrowser</strong></a>(self, browser, browser_backend)</dt></dl>
+
+<dl><dt><a name="PosixPlatformBackend-FlushDnsCache"><strong>FlushDnsCache</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PosixPlatformBackend-FlushEntireSystemCache"><strong>FlushEntireSystemCache</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PosixPlatformBackend-GetArchName"><strong>GetArchName</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PosixPlatformBackend-GetCpuStats"><strong>GetCpuStats</strong></a>(self, pid)</dt></dl>
+
+<dl><dt><a name="PosixPlatformBackend-GetCpuTimestamp"><strong>GetCpuTimestamp</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PosixPlatformBackend-GetMemoryStats"><strong>GetMemoryStats</strong></a>(self, pid)</dt></dl>
+
+<dl><dt><a name="PosixPlatformBackend-GetNetworkData"><strong>GetNetworkData</strong></a>(self, browser)</dt></dl>
+
+<dl><dt><a name="PosixPlatformBackend-GetOSName"><strong>GetOSName</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PosixPlatformBackend-GetOSVersionName"><strong>GetOSVersionName</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PosixPlatformBackend-GetRemotePort"><strong>GetRemotePort</strong></a>(self, port)</dt></dl>
+
+<dl><dt><a name="PosixPlatformBackend-GetSystemCommitCharge"><strong>GetSystemCommitCharge</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PosixPlatformBackend-GetSystemTotalPhysicalMemory"><strong>GetSystemTotalPhysicalMemory</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PosixPlatformBackend-HasBeenThermallyThrottled"><strong>HasBeenThermallyThrottled</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PosixPlatformBackend-InitPlatformBackend"><strong>InitPlatformBackend</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PosixPlatformBackend-InstallApplication"><strong>InstallApplication</strong></a>(self, application)</dt></dl>
+
+<dl><dt><a name="PosixPlatformBackend-IsCooperativeShutdownSupported"><strong>IsCooperativeShutdownSupported</strong></a>(self)</dt><dd><tt>Indicates&nbsp;whether&nbsp;CooperativelyShutdown,&nbsp;below,&nbsp;is&nbsp;supported.<br>
+It&nbsp;is&nbsp;not&nbsp;necessary&nbsp;to&nbsp;implement&nbsp;it&nbsp;on&nbsp;all&nbsp;platforms.</tt></dd></dl>
+
+<dl><dt><a name="PosixPlatformBackend-IsDisplayTracingSupported"><strong>IsDisplayTracingSupported</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PosixPlatformBackend-IsThermallyThrottled"><strong>IsThermallyThrottled</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PosixPlatformBackend-PathExists"><strong>PathExists</strong></a>(self, path, timeout<font color="#909090">=None</font>, retries<font color="#909090">=None</font>)</dt><dd><tt>Tests&nbsp;whether&nbsp;the&nbsp;given&nbsp;path&nbsp;exists&nbsp;on&nbsp;the&nbsp;target&nbsp;platform.<br>
+Args:<br>
+&nbsp;&nbsp;path:&nbsp;path&nbsp;in&nbsp;request.<br>
+&nbsp;&nbsp;timeout:&nbsp;timeout.<br>
+&nbsp;&nbsp;retries:&nbsp;num&nbsp;of&nbsp;retries.<br>
+Return:<br>
+&nbsp;&nbsp;Whether&nbsp;the&nbsp;path&nbsp;exists&nbsp;on&nbsp;the&nbsp;target&nbsp;platform.</tt></dd></dl>
+
+<dl><dt><a name="PosixPlatformBackend-PurgeUnpinnedMemory"><strong>PurgeUnpinnedMemory</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PosixPlatformBackend-ReadMsr"><strong>ReadMsr</strong></a>(self, msr_number, start<font color="#909090">=0</font>, length<font color="#909090">=64</font>)</dt><dd><tt>Read&nbsp;a&nbsp;CPU&nbsp;model-specific&nbsp;register&nbsp;(MSR).<br>
+&nbsp;<br>
+Which&nbsp;MSRs&nbsp;are&nbsp;available&nbsp;depends&nbsp;on&nbsp;the&nbsp;CPU&nbsp;model.<br>
+On&nbsp;systems&nbsp;with&nbsp;multiple&nbsp;CPUs,&nbsp;this&nbsp;function&nbsp;may&nbsp;run&nbsp;on&nbsp;any&nbsp;CPU.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;msr_number:&nbsp;The&nbsp;number&nbsp;of&nbsp;the&nbsp;register&nbsp;to&nbsp;read.<br>
+&nbsp;&nbsp;start:&nbsp;The&nbsp;least&nbsp;significant&nbsp;bit&nbsp;to&nbsp;read,&nbsp;zero-indexed.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(Said&nbsp;another&nbsp;way,&nbsp;the&nbsp;number&nbsp;of&nbsp;bits&nbsp;to&nbsp;right-shift&nbsp;the&nbsp;MSR&nbsp;value.)<br>
+&nbsp;&nbsp;length:&nbsp;The&nbsp;number&nbsp;of&nbsp;bits&nbsp;to&nbsp;read.&nbsp;MSRs&nbsp;are&nbsp;64&nbsp;bits,&nbsp;even&nbsp;on&nbsp;32-bit&nbsp;CPUs.</tt></dd></dl>
+
+<dl><dt><a name="PosixPlatformBackend-SetFullPerformanceModeEnabled"><strong>SetFullPerformanceModeEnabled</strong></a>(self, enabled)</dt></dl>
+
+<dl><dt><a name="PosixPlatformBackend-SetPlatform"><strong>SetPlatform</strong></a>(self, platform)</dt></dl>
+
+<dl><dt><a name="PosixPlatformBackend-StartDisplayTracing"><strong>StartDisplayTracing</strong></a>(self)</dt><dd><tt>Start&nbsp;gathering&nbsp;a&nbsp;trace&nbsp;with&nbsp;frame&nbsp;timestamps&nbsp;close&nbsp;to&nbsp;physical<br>
+display.</tt></dd></dl>
+
+<dl><dt><a name="PosixPlatformBackend-StartMonitoringPower"><strong>StartMonitoringPower</strong></a>(self, browser)</dt></dl>
+
+<dl><dt><a name="PosixPlatformBackend-StartVideoCapture"><strong>StartVideoCapture</strong></a>(self, min_bitrate_mbps)</dt></dl>
+
+<dl><dt><a name="PosixPlatformBackend-StopDisplayTracing"><strong>StopDisplayTracing</strong></a>(self)</dt><dd><tt>Stop&nbsp;gathering&nbsp;a&nbsp;trace&nbsp;with&nbsp;frame&nbsp;timestamps&nbsp;close&nbsp;to&nbsp;physical&nbsp;display.<br>
+&nbsp;<br>
+Returns&nbsp;a&nbsp;raw&nbsp;tracing&nbsp;events&nbsp;that&nbsp;contains&nbsp;the&nbsp;timestamps&nbsp;of&nbsp;physical<br>
+display.</tt></dd></dl>
+
+<dl><dt><a name="PosixPlatformBackend-StopMonitoringPower"><strong>StopMonitoringPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PosixPlatformBackend-StopVideoCapture"><strong>StopVideoCapture</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PosixPlatformBackend-TakeScreenshot"><strong>TakeScreenshot</strong></a>(self, file_path)</dt></dl>
+
+<dl><dt><a name="PosixPlatformBackend-WillCloseBrowser"><strong>WillCloseBrowser</strong></a>(self, browser, browser_backend)</dt></dl>
+
+<dl><dt><a name="PosixPlatformBackend-__init__"><strong>__init__</strong></a>(self, device<font color="#909090">=None</font>)</dt><dd><tt>Initalize&nbsp;an&nbsp;instance&nbsp;of&nbsp;PlatformBackend&nbsp;from&nbsp;a&nbsp;device&nbsp;optionally.<br>
+Call&nbsp;sites&nbsp;need&nbsp;to&nbsp;use&nbsp;SupportsDevice&nbsp;before&nbsp;intialization&nbsp;to&nbsp;check<br>
+whether&nbsp;this&nbsp;platform&nbsp;backend&nbsp;supports&nbsp;the&nbsp;device.<br>
+If&nbsp;device&nbsp;is&nbsp;None,&nbsp;this&nbsp;constructor&nbsp;returns&nbsp;the&nbsp;host&nbsp;platform&nbsp;backend<br>
+which&nbsp;telemetry&nbsp;is&nbsp;running&nbsp;on.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;device:&nbsp;an&nbsp;instance&nbsp;of&nbsp;telemetry.core.platform.device.Device.</tt></dd></dl>
+
+<hr>
+Class methods inherited from <a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">telemetry.internal.platform.platform_backend.PlatformBackend</a>:<br>
+<dl><dt><a name="PosixPlatformBackend-CreatePlatformForDevice"><strong>CreatePlatformForDevice</strong></a>(cls, device, finder_options)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="PosixPlatformBackend-IsPlatformBackendForHost"><strong>IsPlatformBackendForHost</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Returns&nbsp;whether&nbsp;this&nbsp;platform&nbsp;backend&nbsp;is&nbsp;the&nbsp;platform&nbsp;backend&nbsp;to&nbsp;be&nbsp;used<br>
+for&nbsp;the&nbsp;host&nbsp;device&nbsp;which&nbsp;telemetry&nbsp;is&nbsp;running&nbsp;on.</tt></dd></dl>
+
+<dl><dt><a name="PosixPlatformBackend-SupportsDevice"><strong>SupportsDevice</strong></a>(cls, device)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Returns&nbsp;whether&nbsp;this&nbsp;platform&nbsp;backend&nbsp;supports&nbsp;intialization&nbsp;from&nbsp;the<br>
+device.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">telemetry.internal.platform.platform_backend.PlatformBackend</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>forwarder_factory</strong></dt>
+</dl>
+<dl><dt><strong>is_host_platform</strong></dt>
+</dl>
+<dl><dt><strong>is_video_capture_running</strong></dt>
+</dl>
+<dl><dt><strong>network_controller_backend</strong></dt>
+</dl>
+<dl><dt><strong>platform</strong></dt>
+</dl>
+<dl><dt><strong>running_browser_backends</strong></dt>
+</dl>
+<dl><dt><strong>tracing_controller_backend</strong></dt>
+</dl>
+<dl><dt><strong>wpr_ca_cert_path</strong></dt>
+</dl>
+<dl><dt><strong>wpr_http_device_port</strong></dt>
+</dl>
+<dl><dt><strong>wpr_https_device_port</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.power_monitor.android_dumpsys_power_monitor.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.power_monitor.android_dumpsys_power_monitor.html
new file mode 100644
index 0000000..42a5df9
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.power_monitor.android_dumpsys_power_monitor.html
@@ -0,0 +1,91 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.power_monitor.android_dumpsys_power_monitor</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.<a href="telemetry.internal.platform.power_monitor.html"><font color="#ffffff">power_monitor</font></a>.android_dumpsys_power_monitor</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/power_monitor/android_dumpsys_power_monitor.py">telemetry/internal/platform/power_monitor/android_dumpsys_power_monitor.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="csv.html">csv</a><br>
+</td><td width="25%" valign=top><a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.power_monitor.html">telemetry.internal.platform.power_monitor</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.power_monitor.android_dumpsys_power_monitor.html#DumpsysPowerMonitor">DumpsysPowerMonitor</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="DumpsysPowerMonitor">class <strong>DumpsysPowerMonitor</strong></a>(<a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt><a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">PowerMonitor</a>&nbsp;that&nbsp;relies&nbsp;on&nbsp;the&nbsp;dumpsys&nbsp;batterystats&nbsp;to&nbsp;monitor&nbsp;the&nbsp;power<br>
+consumption&nbsp;of&nbsp;a&nbsp;single&nbsp;android&nbsp;application.&nbsp;This&nbsp;measure&nbsp;uses&nbsp;a&nbsp;heuristic<br>
+and&nbsp;is&nbsp;the&nbsp;same&nbsp;information&nbsp;end-users&nbsp;see&nbsp;with&nbsp;the&nbsp;battery&nbsp;application.<br>
+Available&nbsp;on&nbsp;Android&nbsp;L&nbsp;and&nbsp;higher&nbsp;releases.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.power_monitor.android_dumpsys_power_monitor.html#DumpsysPowerMonitor">DumpsysPowerMonitor</a></dd>
+<dd><a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="DumpsysPowerMonitor-CanMonitorPower"><strong>CanMonitorPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DumpsysPowerMonitor-StartMonitoringPower"><strong>StartMonitoringPower</strong></a>(self, browser)</dt></dl>
+
+<dl><dt><a name="DumpsysPowerMonitor-StopMonitoringPower"><strong>StopMonitoringPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DumpsysPowerMonitor-__init__"><strong>__init__</strong></a>(self, battery, platform_backend)</dt><dd><tt>Constructor.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;battery:&nbsp;A&nbsp;BatteryUtil&nbsp;instance.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;platform_backend:&nbsp;A&nbsp;LinuxBasedPlatformBackend&nbsp;instance.</tt></dd></dl>
+
+<hr>
+Static methods defined here:<br>
+<dl><dt><a name="DumpsysPowerMonitor-ProcessPowerData"><strong>ProcessPowerData</strong></a>(power_data, voltage, package)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a>:<br>
+<dl><dt><a name="DumpsysPowerMonitor-CanMeasurePerApplicationPower"><strong>CanMeasurePerApplicationPower</strong></a>(self)</dt><dd><tt>Returns&nbsp;True&nbsp;if&nbsp;the&nbsp;power&nbsp;monitor&nbsp;can&nbsp;measure&nbsp;power&nbsp;for&nbsp;the&nbsp;target<br>
+application&nbsp;in&nbsp;isolation.&nbsp;False&nbsp;if&nbsp;power&nbsp;measurement&nbsp;is&nbsp;for&nbsp;full&nbsp;system<br>
+energy&nbsp;consumption.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.power_monitor.android_fuelgauge_power_monitor.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.power_monitor.android_fuelgauge_power_monitor.html
new file mode 100644
index 0000000..dd28987
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.power_monitor.android_fuelgauge_power_monitor.html
@@ -0,0 +1,88 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.power_monitor.android_fuelgauge_power_monitor</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.<a href="telemetry.internal.platform.power_monitor.html"><font color="#ffffff">power_monitor</font></a>.android_fuelgauge_power_monitor</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/power_monitor/android_fuelgauge_power_monitor.py">telemetry/internal/platform/power_monitor/android_fuelgauge_power_monitor.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.power_monitor.html">telemetry.internal.platform.power_monitor</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.power_monitor.android_fuelgauge_power_monitor.html#FuelGaugePowerMonitor">FuelGaugePowerMonitor</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="FuelGaugePowerMonitor">class <strong>FuelGaugePowerMonitor</strong></a>(<a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt><a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">PowerMonitor</a>&nbsp;that&nbsp;relies&nbsp;on&nbsp;the&nbsp;fuel&nbsp;gauge&nbsp;chips&nbsp;to&nbsp;monitor&nbsp;the&nbsp;power<br>
+consumption&nbsp;of&nbsp;a&nbsp;android&nbsp;device.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.power_monitor.android_fuelgauge_power_monitor.html#FuelGaugePowerMonitor">FuelGaugePowerMonitor</a></dd>
+<dd><a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="FuelGaugePowerMonitor-CanMonitorPower"><strong>CanMonitorPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="FuelGaugePowerMonitor-StartMonitoringPower"><strong>StartMonitoringPower</strong></a>(self, browser)</dt></dl>
+
+<dl><dt><a name="FuelGaugePowerMonitor-StopMonitoringPower"><strong>StopMonitoringPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="FuelGaugePowerMonitor-__init__"><strong>__init__</strong></a>(self, battery, platform_backend)</dt><dd><tt>Constructor.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;battery:&nbsp;A&nbsp;BatteryUtil&nbsp;instance.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;platform_backend:&nbsp;A&nbsp;LinuxBasedPlatformBackend&nbsp;instance.</tt></dd></dl>
+
+<hr>
+Static methods defined here:<br>
+<dl><dt><a name="FuelGaugePowerMonitor-ProcessPowerData"><strong>ProcessPowerData</strong></a>(voltage, fuel_gauge_delta)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a>:<br>
+<dl><dt><a name="FuelGaugePowerMonitor-CanMeasurePerApplicationPower"><strong>CanMeasurePerApplicationPower</strong></a>(self)</dt><dd><tt>Returns&nbsp;True&nbsp;if&nbsp;the&nbsp;power&nbsp;monitor&nbsp;can&nbsp;measure&nbsp;power&nbsp;for&nbsp;the&nbsp;target<br>
+application&nbsp;in&nbsp;isolation.&nbsp;False&nbsp;if&nbsp;power&nbsp;measurement&nbsp;is&nbsp;for&nbsp;full&nbsp;system<br>
+energy&nbsp;consumption.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.power_monitor.android_temperature_monitor.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.power_monitor.android_temperature_monitor.html
new file mode 100644
index 0000000..b663ab9
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.power_monitor.android_temperature_monitor.html
@@ -0,0 +1,79 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.power_monitor.android_temperature_monitor</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.<a href="telemetry.internal.platform.power_monitor.html"><font color="#ffffff">power_monitor</font></a>.android_temperature_monitor</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/power_monitor/android_temperature_monitor.py">telemetry/internal/platform/power_monitor/android_temperature_monitor.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="devil.android.device_errors.html">devil.android.device_errors</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.power_monitor.html">telemetry.internal.platform.power_monitor</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.power_monitor.android_temperature_monitor.html#AndroidTemperatureMonitor">AndroidTemperatureMonitor</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="AndroidTemperatureMonitor">class <strong>AndroidTemperatureMonitor</strong></a>(<a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Returns&nbsp;temperature&nbsp;results&nbsp;in&nbsp;power&nbsp;monitor&nbsp;dictionary&nbsp;format.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.power_monitor.android_temperature_monitor.html#AndroidTemperatureMonitor">AndroidTemperatureMonitor</a></dd>
+<dd><a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="AndroidTemperatureMonitor-CanMonitorPower"><strong>CanMonitorPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidTemperatureMonitor-StartMonitoringPower"><strong>StartMonitoringPower</strong></a>(self, browser)</dt></dl>
+
+<dl><dt><a name="AndroidTemperatureMonitor-StopMonitoringPower"><strong>StopMonitoringPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidTemperatureMonitor-__init__"><strong>__init__</strong></a>(self, device)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a>:<br>
+<dl><dt><a name="AndroidTemperatureMonitor-CanMeasurePerApplicationPower"><strong>CanMeasurePerApplicationPower</strong></a>(self)</dt><dd><tt>Returns&nbsp;True&nbsp;if&nbsp;the&nbsp;power&nbsp;monitor&nbsp;can&nbsp;measure&nbsp;power&nbsp;for&nbsp;the&nbsp;target<br>
+application&nbsp;in&nbsp;isolation.&nbsp;False&nbsp;if&nbsp;power&nbsp;measurement&nbsp;is&nbsp;for&nbsp;full&nbsp;system<br>
+energy&nbsp;consumption.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.power_monitor.cros_power_monitor.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.power_monitor.cros_power_monitor.html
new file mode 100644
index 0000000..e3688ed
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.power_monitor.cros_power_monitor.html
@@ -0,0 +1,171 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.power_monitor.cros_power_monitor</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.<a href="telemetry.internal.platform.power_monitor.html"><font color="#ffffff">power_monitor</font></a>.cros_power_monitor</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/power_monitor/cros_power_monitor.py">telemetry/internal/platform/power_monitor/cros_power_monitor.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="collections.html">collections</a><br>
+</td><td width="25%" valign=top><a href="telemetry.decorators.html">telemetry.decorators</a><br>
+</td><td width="25%" valign=top><a href="re.html">re</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.power_monitor.sysfs_power_monitor.html">telemetry.internal.platform.power_monitor.sysfs_power_monitor</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.power_monitor.sysfs_power_monitor.html#SysfsPowerMonitor">telemetry.internal.platform.power_monitor.sysfs_power_monitor.SysfsPowerMonitor</a>(<a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.power_monitor.cros_power_monitor.html#CrosPowerMonitor">CrosPowerMonitor</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="CrosPowerMonitor">class <strong>CrosPowerMonitor</strong></a>(<a href="telemetry.internal.platform.power_monitor.sysfs_power_monitor.html#SysfsPowerMonitor">telemetry.internal.platform.power_monitor.sysfs_power_monitor.SysfsPowerMonitor</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>PowerMonitor&nbsp;that&nbsp;relies&nbsp;on&nbsp;'dump_power_status'&nbsp;to&nbsp;monitor&nbsp;power<br>
+consumption&nbsp;of&nbsp;a&nbsp;single&nbsp;ChromeOS&nbsp;application.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.power_monitor.cros_power_monitor.html#CrosPowerMonitor">CrosPowerMonitor</a></dd>
+<dd><a href="telemetry.internal.platform.power_monitor.sysfs_power_monitor.html#SysfsPowerMonitor">telemetry.internal.platform.power_monitor.sysfs_power_monitor.SysfsPowerMonitor</a></dd>
+<dd><a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="CrosPowerMonitor-CanMonitorPower"><strong>CanMonitorPower</strong></a>(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="CrosPowerMonitor-StartMonitoringPower"><strong>StartMonitoringPower</strong></a>(self, browser)</dt></dl>
+
+<dl><dt><a name="CrosPowerMonitor-StopMonitoringPower"><strong>StopMonitoringPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CrosPowerMonitor-__init__"><strong>__init__</strong></a>(self, platform_backend)</dt><dd><tt>Constructor.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;platform_backend:&nbsp;A&nbsp;LinuxBasedPlatformBackend&nbsp;object.<br>
+&nbsp;<br>
+Attributes:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;_initial_power:&nbsp;The&nbsp;result&nbsp;of&nbsp;'dump_power_status'&nbsp;before&nbsp;the&nbsp;test.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;_start_time:&nbsp;The&nbsp;epoch&nbsp;time&nbsp;at&nbsp;which&nbsp;the&nbsp;test&nbsp;starts&nbsp;executing.</tt></dd></dl>
+
+<hr>
+Static methods defined here:<br>
+<dl><dt><a name="CrosPowerMonitor-IsOnBatteryPower"><strong>IsOnBatteryPower</strong></a>(status, board)</dt><dd><tt>Determines&nbsp;if&nbsp;the&nbsp;devices&nbsp;is&nbsp;being&nbsp;charged.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;status:&nbsp;The&nbsp;parsed&nbsp;result&nbsp;of&nbsp;'dump_power_status'<br>
+&nbsp;&nbsp;&nbsp;&nbsp;board:&nbsp;The&nbsp;name&nbsp;of&nbsp;the&nbsp;board&nbsp;running&nbsp;the&nbsp;test.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;True&nbsp;if&nbsp;the&nbsp;device&nbsp;is&nbsp;on&nbsp;battery&nbsp;power;&nbsp;False&nbsp;otherwise.</tt></dd></dl>
+
+<dl><dt><a name="CrosPowerMonitor-ParsePower"><strong>ParsePower</strong></a>(initial_stats, final_stats, length_h)</dt><dd><tt>Parse&nbsp;output&nbsp;of&nbsp;'dump_power_status'<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;initial_stats:&nbsp;The&nbsp;output&nbsp;of&nbsp;'dump_power_status'&nbsp;before&nbsp;the&nbsp;test.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;final_stats:&nbsp;The&nbsp;output&nbsp;of&nbsp;'dump_power_status'&nbsp;after&nbsp;the&nbsp;test.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;length_h:&nbsp;The&nbsp;length&nbsp;of&nbsp;the&nbsp;test&nbsp;in&nbsp;hours.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;Dictionary&nbsp;in&nbsp;the&nbsp;format&nbsp;returned&nbsp;by&nbsp;<a href="#CrosPowerMonitor-StopMonitoringPower">StopMonitoringPower</a>().</tt></dd></dl>
+
+<dl><dt><a name="CrosPowerMonitor-ParsePowerStatus"><strong>ParsePowerStatus</strong></a>(sample)</dt><dd><tt>Parses&nbsp;'dump_power_status'&nbsp;command&nbsp;output.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;sample:&nbsp;The&nbsp;output&nbsp;of&nbsp;'dump_power_status'<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;Dictionary&nbsp;containing&nbsp;all&nbsp;fields&nbsp;from&nbsp;'dump_power_status'</tt></dd></dl>
+
+<dl><dt><a name="CrosPowerMonitor-SplitSample"><strong>SplitSample</strong></a>(sample)</dt><dd><tt>Splits&nbsp;a&nbsp;power&nbsp;and&nbsp;time&nbsp;sample&nbsp;into&nbsp;the&nbsp;two&nbsp;separate&nbsp;values.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;sample:&nbsp;The&nbsp;result&nbsp;of&nbsp;calling&nbsp;'dump_power_status;&nbsp;date&nbsp;+%s'&nbsp;on&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;device.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;A&nbsp;tuple&nbsp;of&nbsp;power&nbsp;sample&nbsp;and&nbsp;epoch&nbsp;time&nbsp;of&nbsp;the&nbsp;sample.</tt></dd></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.platform.power_monitor.sysfs_power_monitor.html#SysfsPowerMonitor">telemetry.internal.platform.power_monitor.sysfs_power_monitor.SysfsPowerMonitor</a>:<br>
+<dl><dt><a name="CrosPowerMonitor-GetCpuFreq"><strong>GetCpuFreq</strong></a>(self)</dt><dd><tt>Retrieve&nbsp;CPU&nbsp;frequency&nbsp;times&nbsp;from&nbsp;the&nbsp;device.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;Dictionary&nbsp;containing&nbsp;frequency&nbsp;times&nbsp;for&nbsp;each&nbsp;CPU.</tt></dd></dl>
+
+<dl><dt><a name="CrosPowerMonitor-GetCpuState"><strong>GetCpuState</strong></a>(self)</dt><dd><tt>Retrieve&nbsp;CPU&nbsp;c-state&nbsp;residency&nbsp;times&nbsp;from&nbsp;the&nbsp;device.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;Dictionary&nbsp;containing&nbsp;c-state&nbsp;residency&nbsp;times&nbsp;for&nbsp;each&nbsp;CPU.</tt></dd></dl>
+
+<hr>
+Static methods inherited from <a href="telemetry.internal.platform.power_monitor.sysfs_power_monitor.html#SysfsPowerMonitor">telemetry.internal.platform.power_monitor.sysfs_power_monitor.SysfsPowerMonitor</a>:<br>
+<dl><dt><a name="CrosPowerMonitor-CombineResults"><strong>CombineResults</strong></a>(cpu_stats, power_stats)</dt><dd><tt>Add&nbsp;frequency&nbsp;and&nbsp;c-state&nbsp;residency&nbsp;data&nbsp;to&nbsp;the&nbsp;power&nbsp;data.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;cpu_stats:&nbsp;Dictionary&nbsp;containing&nbsp;CPU&nbsp;statistics.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;power_stats:&nbsp;Dictionary&nbsp;containing&nbsp;power&nbsp;statistics.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;Dictionary&nbsp;in&nbsp;the&nbsp;format&nbsp;returned&nbsp;by&nbsp;StopMonitoringPower.</tt></dd></dl>
+
+<dl><dt><a name="CrosPowerMonitor-ComputeCpuStats"><strong>ComputeCpuStats</strong></a>(initial, final)</dt><dd><tt>Parse&nbsp;the&nbsp;CPU&nbsp;c-state&nbsp;and&nbsp;frequency&nbsp;values&nbsp;saved&nbsp;during&nbsp;monitoring.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;initial:&nbsp;The&nbsp;parsed&nbsp;dictionary&nbsp;of&nbsp;initial&nbsp;statistics&nbsp;to&nbsp;be&nbsp;converted<br>
+&nbsp;&nbsp;&nbsp;&nbsp;into&nbsp;percentages.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;final:&nbsp;The&nbsp;parsed&nbsp;dictionary&nbsp;of&nbsp;final&nbsp;statistics&nbsp;to&nbsp;be&nbsp;converted<br>
+&nbsp;&nbsp;&nbsp;&nbsp;into&nbsp;percentages.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;Dictionary&nbsp;containing&nbsp;percentages&nbsp;for&nbsp;each&nbsp;CPU&nbsp;as&nbsp;well&nbsp;as&nbsp;an&nbsp;average<br>
+&nbsp;&nbsp;&nbsp;&nbsp;across&nbsp;all&nbsp;CPUs.</tt></dd></dl>
+
+<dl><dt><a name="CrosPowerMonitor-ParseFreqSample"><strong>ParseFreqSample</strong></a>(sample)</dt><dd><tt>Parse&nbsp;a&nbsp;single&nbsp;frequency&nbsp;sample.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;sample:&nbsp;The&nbsp;single&nbsp;sample&nbsp;of&nbsp;frequency&nbsp;data&nbsp;to&nbsp;be&nbsp;parsed.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;A&nbsp;dictionary&nbsp;associating&nbsp;a&nbsp;frequency&nbsp;with&nbsp;a&nbsp;time.</tt></dd></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a>:<br>
+<dl><dt><a name="CrosPowerMonitor-CanMeasurePerApplicationPower"><strong>CanMeasurePerApplicationPower</strong></a>(self)</dt><dd><tt>Returns&nbsp;True&nbsp;if&nbsp;the&nbsp;power&nbsp;monitor&nbsp;can&nbsp;measure&nbsp;power&nbsp;for&nbsp;the&nbsp;target<br>
+application&nbsp;in&nbsp;isolation.&nbsp;False&nbsp;if&nbsp;power&nbsp;measurement&nbsp;is&nbsp;for&nbsp;full&nbsp;system<br>
+energy&nbsp;consumption.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.power_monitor.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.power_monitor.html
new file mode 100644
index 0000000..bc7461c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.power_monitor.html
@@ -0,0 +1,92 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: package telemetry.internal.platform.power_monitor</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.power_monitor</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/power_monitor/__init__.py">telemetry/internal/platform/power_monitor/__init__.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Package Contents</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.platform.power_monitor.android_dumpsys_power_monitor.html">android_dumpsys_power_monitor</a><br>
+<a href="telemetry.internal.platform.power_monitor.android_dumpsys_power_monitor_unittest.html">android_dumpsys_power_monitor_unittest</a><br>
+<a href="telemetry.internal.platform.power_monitor.android_fuelgauge_power_monitor.html">android_fuelgauge_power_monitor</a><br>
+<a href="telemetry.internal.platform.power_monitor.android_fuelgauge_power_monitor_unittest.html">android_fuelgauge_power_monitor_unittest</a><br>
+<a href="telemetry.internal.platform.power_monitor.android_temperature_monitor.html">android_temperature_monitor</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.power_monitor.android_temperature_monitor_unittest.html">android_temperature_monitor_unittest</a><br>
+<a href="telemetry.internal.platform.power_monitor.cros_power_monitor.html">cros_power_monitor</a><br>
+<a href="telemetry.internal.platform.power_monitor.cros_power_monitor_unittest.html">cros_power_monitor_unittest</a><br>
+<a href="telemetry.internal.platform.power_monitor.monsoon_power_monitor.html">monsoon_power_monitor</a><br>
+<a href="telemetry.internal.platform.power_monitor.monsoon_power_monitor_unittest.html">monsoon_power_monitor_unittest</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.power_monitor.msr_power_monitor.html">msr_power_monitor</a><br>
+<a href="telemetry.internal.platform.power_monitor.msr_power_monitor_unittest.html">msr_power_monitor_unittest</a><br>
+<a href="telemetry.internal.platform.power_monitor.power_monitor_controller.html">power_monitor_controller</a><br>
+<a href="telemetry.internal.platform.power_monitor.power_monitor_controller_unittest.html">power_monitor_controller_unittest</a><br>
+<a href="telemetry.internal.platform.power_monitor.powermetrics_power_monitor.html">powermetrics_power_monitor</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.power_monitor.powermetrics_power_monitor_unittest.html">powermetrics_power_monitor_unittest</a><br>
+<a href="telemetry.internal.platform.power_monitor.sysfs_power_monitor.html">sysfs_power_monitor</a><br>
+<a href="telemetry.internal.platform.power_monitor.sysfs_power_monitor_unittest.html">sysfs_power_monitor_unittest</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">PowerMonitor</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="PowerMonitor">class <strong>PowerMonitor</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;power&nbsp;profiler.<br>
+&nbsp;<br>
+Provides&nbsp;an&nbsp;interface&nbsp;to&nbsp;register&nbsp;power&nbsp;consumption&nbsp;during&nbsp;a&nbsp;test.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="PowerMonitor-CanMeasurePerApplicationPower"><strong>CanMeasurePerApplicationPower</strong></a>(self)</dt><dd><tt>Returns&nbsp;True&nbsp;if&nbsp;the&nbsp;power&nbsp;monitor&nbsp;can&nbsp;measure&nbsp;power&nbsp;for&nbsp;the&nbsp;target<br>
+application&nbsp;in&nbsp;isolation.&nbsp;False&nbsp;if&nbsp;power&nbsp;measurement&nbsp;is&nbsp;for&nbsp;full&nbsp;system<br>
+energy&nbsp;consumption.</tt></dd></dl>
+
+<dl><dt><a name="PowerMonitor-CanMonitorPower"><strong>CanMonitorPower</strong></a>(self)</dt><dd><tt>Returns&nbsp;True&nbsp;iff&nbsp;power&nbsp;can&nbsp;be&nbsp;monitored&nbsp;asynchronously&nbsp;via<br>
+<a href="#PowerMonitor-StartMonitoringPower">StartMonitoringPower</a>()&nbsp;and&nbsp;<a href="#PowerMonitor-StopMonitoringPower">StopMonitoringPower</a>().</tt></dd></dl>
+
+<dl><dt><a name="PowerMonitor-StartMonitoringPower"><strong>StartMonitoringPower</strong></a>(self, browser)</dt><dd><tt>Starts&nbsp;monitoring&nbsp;power&nbsp;utilization&nbsp;statistics.<br>
+&nbsp;<br>
+See&nbsp;Platform#StartMonitoringPower&nbsp;for&nbsp;the&nbsp;arguments&nbsp;format.</tt></dd></dl>
+
+<dl><dt><a name="PowerMonitor-StopMonitoringPower"><strong>StopMonitoringPower</strong></a>(self)</dt><dd><tt>Stops&nbsp;monitoring&nbsp;power&nbsp;utilization&nbsp;and&nbsp;returns&nbsp;collects&nbsp;stats<br>
+&nbsp;<br>
+See&nbsp;Platform#StopMonitoringPower&nbsp;for&nbsp;the&nbsp;return&nbsp;format.</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.power_monitor.monsoon_power_monitor.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.power_monitor.monsoon_power_monitor.html
new file mode 100644
index 0000000..fa24e9c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.power_monitor.monsoon_power_monitor.html
@@ -0,0 +1,89 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.power_monitor.monsoon_power_monitor</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.<a href="telemetry.internal.platform.power_monitor.html"><font color="#ffffff">power_monitor</font></a>.monsoon_power_monitor</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/power_monitor/monsoon_power_monitor.py">telemetry/internal/platform/power_monitor/monsoon_power_monitor.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+<a href="json.html">json</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.profiler.monsoon.html">telemetry.internal.platform.profiler.monsoon</a><br>
+<a href="multiprocessing.html">multiprocessing</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.power_monitor.html">telemetry.internal.platform.power_monitor</a><br>
+<a href="tempfile.html">tempfile</a><br>
+</td><td width="25%" valign=top><a href="time.html">time</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.power_monitor.monsoon_power_monitor.html#MonsoonPowerMonitor">MonsoonPowerMonitor</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="MonsoonPowerMonitor">class <strong>MonsoonPowerMonitor</strong></a>(<a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.power_monitor.monsoon_power_monitor.html#MonsoonPowerMonitor">MonsoonPowerMonitor</a></dd>
+<dd><a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="MonsoonPowerMonitor-CanMonitorPower"><strong>CanMonitorPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MonsoonPowerMonitor-StartMonitoringPower"><strong>StartMonitoringPower</strong></a>(self, browser)</dt></dl>
+
+<dl><dt><a name="MonsoonPowerMonitor-StopMonitoringPower"><strong>StopMonitoringPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MonsoonPowerMonitor-__init__"><strong>__init__</strong></a>(self, _, platform_backend)</dt></dl>
+
+<hr>
+Static methods defined here:<br>
+<dl><dt><a name="MonsoonPowerMonitor-ParseSamplingOutput"><strong>ParseSamplingOutput</strong></a>(powermonitor_output)</dt><dd><tt>Parse&nbsp;the&nbsp;output&nbsp;of&nbsp;of&nbsp;the&nbsp;samples&nbsp;collector&nbsp;process.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;Dictionary&nbsp;in&nbsp;the&nbsp;format&nbsp;returned&nbsp;by&nbsp;<a href="#MonsoonPowerMonitor-StopMonitoringPower">StopMonitoringPower</a>().</tt></dd></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a>:<br>
+<dl><dt><a name="MonsoonPowerMonitor-CanMeasurePerApplicationPower"><strong>CanMeasurePerApplicationPower</strong></a>(self)</dt><dd><tt>Returns&nbsp;True&nbsp;if&nbsp;the&nbsp;power&nbsp;monitor&nbsp;can&nbsp;measure&nbsp;power&nbsp;for&nbsp;the&nbsp;target<br>
+application&nbsp;in&nbsp;isolation.&nbsp;False&nbsp;if&nbsp;power&nbsp;measurement&nbsp;is&nbsp;for&nbsp;full&nbsp;system<br>
+energy&nbsp;consumption.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.power_monitor.msr_power_monitor.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.power_monitor.msr_power_monitor.html
new file mode 100644
index 0000000..b050d36
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.power_monitor.msr_power_monitor.html
@@ -0,0 +1,177 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.power_monitor.msr_power_monitor</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.<a href="telemetry.internal.platform.power_monitor.html"><font color="#ffffff">power_monitor</font></a>.msr_power_monitor</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/power_monitor/msr_power_monitor.py">telemetry/internal/platform/power_monitor/msr_power_monitor.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.decorators.html">telemetry.decorators</a><br>
+<a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top><a href="platform.html">platform</a><br>
+<a href="telemetry.internal.platform.power_monitor.html">telemetry.internal.platform.power_monitor</a><br>
+</td><td width="25%" valign=top><a href="re.html">re</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.power_monitor.msr_power_monitor.html#MsrPowerMonitor">MsrPowerMonitor</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.power_monitor.msr_power_monitor.html#MsrPowerMonitorLinux">MsrPowerMonitorLinux</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.internal.platform.power_monitor.msr_power_monitor.html#MsrPowerMonitorWin">MsrPowerMonitorWin</a>
+</font></dt></dl>
+</dd>
+</dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="MsrPowerMonitor">class <strong>MsrPowerMonitor</strong></a>(<a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.power_monitor.msr_power_monitor.html#MsrPowerMonitor">MsrPowerMonitor</a></dd>
+<dd><a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="MsrPowerMonitor-CanMonitorPower"><strong>CanMonitorPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MsrPowerMonitor-StartMonitoringPower"><strong>StartMonitoringPower</strong></a>(self, browser)</dt></dl>
+
+<dl><dt><a name="MsrPowerMonitor-StopMonitoringPower"><strong>StopMonitoringPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MsrPowerMonitor-__init__"><strong>__init__</strong></a>(self, backend)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a>:<br>
+<dl><dt><a name="MsrPowerMonitor-CanMeasurePerApplicationPower"><strong>CanMeasurePerApplicationPower</strong></a>(self)</dt><dd><tt>Returns&nbsp;True&nbsp;if&nbsp;the&nbsp;power&nbsp;monitor&nbsp;can&nbsp;measure&nbsp;power&nbsp;for&nbsp;the&nbsp;target<br>
+application&nbsp;in&nbsp;isolation.&nbsp;False&nbsp;if&nbsp;power&nbsp;measurement&nbsp;is&nbsp;for&nbsp;full&nbsp;system<br>
+energy&nbsp;consumption.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="MsrPowerMonitorLinux">class <strong>MsrPowerMonitorLinux</strong></a>(<a href="telemetry.internal.platform.power_monitor.msr_power_monitor.html#MsrPowerMonitor">MsrPowerMonitor</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.power_monitor.msr_power_monitor.html#MsrPowerMonitorLinux">MsrPowerMonitorLinux</a></dd>
+<dd><a href="telemetry.internal.platform.power_monitor.msr_power_monitor.html#MsrPowerMonitor">MsrPowerMonitor</a></dd>
+<dd><a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="MsrPowerMonitorLinux-CanMonitorPower"><strong>CanMonitorPower</strong></a>(self)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.platform.power_monitor.msr_power_monitor.html#MsrPowerMonitor">MsrPowerMonitor</a>:<br>
+<dl><dt><a name="MsrPowerMonitorLinux-StartMonitoringPower"><strong>StartMonitoringPower</strong></a>(self, browser)</dt></dl>
+
+<dl><dt><a name="MsrPowerMonitorLinux-StopMonitoringPower"><strong>StopMonitoringPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MsrPowerMonitorLinux-__init__"><strong>__init__</strong></a>(self, backend)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a>:<br>
+<dl><dt><a name="MsrPowerMonitorLinux-CanMeasurePerApplicationPower"><strong>CanMeasurePerApplicationPower</strong></a>(self)</dt><dd><tt>Returns&nbsp;True&nbsp;if&nbsp;the&nbsp;power&nbsp;monitor&nbsp;can&nbsp;measure&nbsp;power&nbsp;for&nbsp;the&nbsp;target<br>
+application&nbsp;in&nbsp;isolation.&nbsp;False&nbsp;if&nbsp;power&nbsp;measurement&nbsp;is&nbsp;for&nbsp;full&nbsp;system<br>
+energy&nbsp;consumption.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="MsrPowerMonitorWin">class <strong>MsrPowerMonitorWin</strong></a>(<a href="telemetry.internal.platform.power_monitor.msr_power_monitor.html#MsrPowerMonitor">MsrPowerMonitor</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.power_monitor.msr_power_monitor.html#MsrPowerMonitorWin">MsrPowerMonitorWin</a></dd>
+<dd><a href="telemetry.internal.platform.power_monitor.msr_power_monitor.html#MsrPowerMonitor">MsrPowerMonitor</a></dd>
+<dd><a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="MsrPowerMonitorWin-CanMonitorPower"><strong>CanMonitorPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MsrPowerMonitorWin-StopMonitoringPower"><strong>StopMonitoringPower</strong></a>(self)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.platform.power_monitor.msr_power_monitor.html#MsrPowerMonitor">MsrPowerMonitor</a>:<br>
+<dl><dt><a name="MsrPowerMonitorWin-StartMonitoringPower"><strong>StartMonitoringPower</strong></a>(self, browser)</dt></dl>
+
+<dl><dt><a name="MsrPowerMonitorWin-__init__"><strong>__init__</strong></a>(self, backend)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a>:<br>
+<dl><dt><a name="MsrPowerMonitorWin-CanMeasurePerApplicationPower"><strong>CanMeasurePerApplicationPower</strong></a>(self)</dt><dd><tt>Returns&nbsp;True&nbsp;if&nbsp;the&nbsp;power&nbsp;monitor&nbsp;can&nbsp;measure&nbsp;power&nbsp;for&nbsp;the&nbsp;target<br>
+application&nbsp;in&nbsp;isolation.&nbsp;False&nbsp;if&nbsp;power&nbsp;measurement&nbsp;is&nbsp;for&nbsp;full&nbsp;system<br>
+energy&nbsp;consumption.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>IA32_PACKAGE_THERM_STATUS</strong> = 433<br>
+<strong>IA32_TEMPERATURE_TARGET</strong> = 418<br>
+<strong>MSR_DRAM_ENERGY_STATUS</strong> = 1561<br>
+<strong>MSR_PKG_ENERGY_STATUS</strong> = 1553<br>
+<strong>MSR_PP0_ENERGY_STATUS</strong> = 1593<br>
+<strong>MSR_PP1_ENERGY_STATUS</strong> = 1601<br>
+<strong>MSR_RAPL_POWER_UNIT</strong> = 1542</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.power_monitor.power_monitor_controller.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.power_monitor.power_monitor_controller.html
new file mode 100644
index 0000000..e1ba28c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.power_monitor.power_monitor_controller.html
@@ -0,0 +1,81 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.power_monitor.power_monitor_controller</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.<a href="telemetry.internal.platform.power_monitor.html"><font color="#ffffff">power_monitor</font></a>.power_monitor_controller</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/power_monitor/power_monitor_controller.py">telemetry/internal/platform/power_monitor/power_monitor_controller.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="atexit.html">atexit</a><br>
+</td><td width="25%" valign=top><a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.power_monitor.html">telemetry.internal.platform.power_monitor</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.power_monitor.power_monitor_controller.html#PowerMonitorController">PowerMonitorController</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="PowerMonitorController">class <strong>PowerMonitorController</strong></a>(<a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt><a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">PowerMonitor</a>&nbsp;that&nbsp;acts&nbsp;as&nbsp;facade&nbsp;for&nbsp;a&nbsp;list&nbsp;of&nbsp;<a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">PowerMonitor</a>&nbsp;objects&nbsp;and&nbsp;uses<br>
+the&nbsp;first&nbsp;available&nbsp;one.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.power_monitor.power_monitor_controller.html#PowerMonitorController">PowerMonitorController</a></dd>
+<dd><a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="PowerMonitorController-CanMonitorPower"><strong>CanMonitorPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PowerMonitorController-StartMonitoringPower"><strong>StartMonitoringPower</strong></a>(self, browser)</dt></dl>
+
+<dl><dt><a name="PowerMonitorController-StopMonitoringPower"><strong>StopMonitoringPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PowerMonitorController-__init__"><strong>__init__</strong></a>(self, power_monitors, battery)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a>:<br>
+<dl><dt><a name="PowerMonitorController-CanMeasurePerApplicationPower"><strong>CanMeasurePerApplicationPower</strong></a>(self)</dt><dd><tt>Returns&nbsp;True&nbsp;if&nbsp;the&nbsp;power&nbsp;monitor&nbsp;can&nbsp;measure&nbsp;power&nbsp;for&nbsp;the&nbsp;target<br>
+application&nbsp;in&nbsp;isolation.&nbsp;False&nbsp;if&nbsp;power&nbsp;measurement&nbsp;is&nbsp;for&nbsp;full&nbsp;system<br>
+energy&nbsp;consumption.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.power_monitor.powermetrics_power_monitor.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.power_monitor.powermetrics_power_monitor.html
new file mode 100644
index 0000000..7698b43
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.power_monitor.powermetrics_power_monitor.html
@@ -0,0 +1,98 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.power_monitor.powermetrics_power_monitor</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.<a href="telemetry.internal.platform.power_monitor.html"><font color="#ffffff">power_monitor</font></a>.powermetrics_power_monitor</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/power_monitor/powermetrics_power_monitor.py">telemetry/internal/platform/power_monitor/powermetrics_power_monitor.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="collections.html">collections</a><br>
+<a href="telemetry.decorators.html">telemetry.decorators</a><br>
+<a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top><a href="os.html">os</a><br>
+<a href="telemetry.core.os_version.html">telemetry.core.os_version</a><br>
+<a href="plistlib.html">plistlib</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.power_monitor.html">telemetry.internal.platform.power_monitor</a><br>
+<a href="shutil.html">shutil</a><br>
+<a href="tempfile.html">tempfile</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.util.html">telemetry.core.util</a><br>
+<a href="xml.html">xml</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.power_monitor.powermetrics_power_monitor.html#PowerMetricsPowerMonitor">PowerMetricsPowerMonitor</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="PowerMetricsPowerMonitor">class <strong>PowerMetricsPowerMonitor</strong></a>(<a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.power_monitor.powermetrics_power_monitor.html#PowerMetricsPowerMonitor">PowerMetricsPowerMonitor</a></dd>
+<dd><a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="PowerMetricsPowerMonitor-CanMonitorPower"><strong>CanMonitorPower</strong></a>(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="PowerMetricsPowerMonitor-StartMonitoringPower"><strong>StartMonitoringPower</strong></a>(self, browser)</dt></dl>
+
+<dl><dt><a name="PowerMetricsPowerMonitor-StopMonitoringPower"><strong>StopMonitoringPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PowerMetricsPowerMonitor-__init__"><strong>__init__</strong></a>(self, backend)</dt></dl>
+
+<hr>
+Static methods defined here:<br>
+<dl><dt><a name="PowerMetricsPowerMonitor-ParsePowerMetricsOutput"><strong>ParsePowerMetricsOutput</strong></a>(powermetrics_output)</dt><dd><tt>Parse&nbsp;output&nbsp;of&nbsp;powermetrics&nbsp;command&nbsp;line&nbsp;utility.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;Dictionary&nbsp;in&nbsp;the&nbsp;format&nbsp;returned&nbsp;by&nbsp;<a href="#PowerMetricsPowerMonitor-StopMonitoringPower">StopMonitoringPower</a>()&nbsp;or&nbsp;None<br>
+&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;|powermetrics_output|&nbsp;is&nbsp;empty&nbsp;-&nbsp;crbug.com/353250&nbsp;.</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>binary_path</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a>:<br>
+<dl><dt><a name="PowerMetricsPowerMonitor-CanMeasurePerApplicationPower"><strong>CanMeasurePerApplicationPower</strong></a>(self)</dt><dd><tt>Returns&nbsp;True&nbsp;if&nbsp;the&nbsp;power&nbsp;monitor&nbsp;can&nbsp;measure&nbsp;power&nbsp;for&nbsp;the&nbsp;target<br>
+application&nbsp;in&nbsp;isolation.&nbsp;False&nbsp;if&nbsp;power&nbsp;measurement&nbsp;is&nbsp;for&nbsp;full&nbsp;system<br>
+energy&nbsp;consumption.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.power_monitor.sysfs_power_monitor.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.power_monitor.sysfs_power_monitor.html
new file mode 100644
index 0000000..82bec24
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.power_monitor.sysfs_power_monitor.html
@@ -0,0 +1,147 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.power_monitor.sysfs_power_monitor</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.<a href="telemetry.internal.platform.power_monitor.html"><font color="#ffffff">power_monitor</font></a>.sysfs_power_monitor</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/power_monitor/sysfs_power_monitor.py">telemetry/internal/platform/power_monitor/sysfs_power_monitor.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="collections.html">collections</a><br>
+<a href="telemetry.decorators.html">telemetry.decorators</a><br>
+</td><td width="25%" valign=top><a href="logging.html">logging</a><br>
+<a href="os.html">os</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.power_monitor.html">telemetry.internal.platform.power_monitor</a><br>
+<a href="re.html">re</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.power_monitor.sysfs_power_monitor.html#SysfsPowerMonitor">SysfsPowerMonitor</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="SysfsPowerMonitor">class <strong>SysfsPowerMonitor</strong></a>(<a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt><a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">PowerMonitor</a>&nbsp;that&nbsp;relies&nbsp;on&nbsp;sysfs&nbsp;to&nbsp;monitor&nbsp;CPU&nbsp;statistics&nbsp;on&nbsp;several<br>
+different&nbsp;platforms.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.power_monitor.sysfs_power_monitor.html#SysfsPowerMonitor">SysfsPowerMonitor</a></dd>
+<dd><a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="SysfsPowerMonitor-CanMonitorPower"><strong>CanMonitorPower</strong></a>(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="SysfsPowerMonitor-GetCpuFreq"><strong>GetCpuFreq</strong></a>(self)</dt><dd><tt>Retrieve&nbsp;CPU&nbsp;frequency&nbsp;times&nbsp;from&nbsp;the&nbsp;device.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;Dictionary&nbsp;containing&nbsp;frequency&nbsp;times&nbsp;for&nbsp;each&nbsp;CPU.</tt></dd></dl>
+
+<dl><dt><a name="SysfsPowerMonitor-GetCpuState"><strong>GetCpuState</strong></a>(self)</dt><dd><tt>Retrieve&nbsp;CPU&nbsp;c-state&nbsp;residency&nbsp;times&nbsp;from&nbsp;the&nbsp;device.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;Dictionary&nbsp;containing&nbsp;c-state&nbsp;residency&nbsp;times&nbsp;for&nbsp;each&nbsp;CPU.</tt></dd></dl>
+
+<dl><dt><a name="SysfsPowerMonitor-StartMonitoringPower"><strong>StartMonitoringPower</strong></a>(self, _browser)</dt></dl>
+
+<dl><dt><a name="SysfsPowerMonitor-StopMonitoringPower"><strong>StopMonitoringPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="SysfsPowerMonitor-__init__"><strong>__init__</strong></a>(self, linux_based_platform_backend, standalone<font color="#909090">=False</font>)</dt><dd><tt>Constructor.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;linux_based_platform_backend:&nbsp;A&nbsp;LinuxBasedPlatformBackend&nbsp;object.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;standalone:&nbsp;If&nbsp;it&nbsp;is&nbsp;not&nbsp;wrapping&nbsp;another&nbsp;monitor,&nbsp;set&nbsp;to&nbsp;True.<br>
+&nbsp;<br>
+Attributes:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;_cpus:&nbsp;A&nbsp;list&nbsp;of&nbsp;the&nbsp;CPUs&nbsp;on&nbsp;the&nbsp;target&nbsp;device.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;_end_time:&nbsp;The&nbsp;time&nbsp;the&nbsp;test&nbsp;stopped&nbsp;monitoring&nbsp;power.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;_final_cstate:&nbsp;The&nbsp;c-state&nbsp;residency&nbsp;times&nbsp;after&nbsp;the&nbsp;test.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;_final_freq:&nbsp;The&nbsp;CPU&nbsp;frequency&nbsp;times&nbsp;after&nbsp;the&nbsp;test.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;_initial_cstate:&nbsp;The&nbsp;c-state&nbsp;residency&nbsp;times&nbsp;before&nbsp;the&nbsp;test.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;_initial_freq:&nbsp;The&nbsp;CPU&nbsp;frequency&nbsp;times&nbsp;before&nbsp;the&nbsp;test.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;_platform:&nbsp;A&nbsp;LinuxBasedPlatformBackend&nbsp;object&nbsp;associated&nbsp;with&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;target&nbsp;platform.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;_start_time:&nbsp;The&nbsp;time&nbsp;the&nbsp;test&nbsp;started&nbsp;monitoring&nbsp;power.</tt></dd></dl>
+
+<hr>
+Static methods defined here:<br>
+<dl><dt><a name="SysfsPowerMonitor-CombineResults"><strong>CombineResults</strong></a>(cpu_stats, power_stats)</dt><dd><tt>Add&nbsp;frequency&nbsp;and&nbsp;c-state&nbsp;residency&nbsp;data&nbsp;to&nbsp;the&nbsp;power&nbsp;data.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;cpu_stats:&nbsp;Dictionary&nbsp;containing&nbsp;CPU&nbsp;statistics.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;power_stats:&nbsp;Dictionary&nbsp;containing&nbsp;power&nbsp;statistics.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;Dictionary&nbsp;in&nbsp;the&nbsp;format&nbsp;returned&nbsp;by&nbsp;StopMonitoringPower.</tt></dd></dl>
+
+<dl><dt><a name="SysfsPowerMonitor-ComputeCpuStats"><strong>ComputeCpuStats</strong></a>(initial, final)</dt><dd><tt>Parse&nbsp;the&nbsp;CPU&nbsp;c-state&nbsp;and&nbsp;frequency&nbsp;values&nbsp;saved&nbsp;during&nbsp;monitoring.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;initial:&nbsp;The&nbsp;parsed&nbsp;dictionary&nbsp;of&nbsp;initial&nbsp;statistics&nbsp;to&nbsp;be&nbsp;converted<br>
+&nbsp;&nbsp;&nbsp;&nbsp;into&nbsp;percentages.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;final:&nbsp;The&nbsp;parsed&nbsp;dictionary&nbsp;of&nbsp;final&nbsp;statistics&nbsp;to&nbsp;be&nbsp;converted<br>
+&nbsp;&nbsp;&nbsp;&nbsp;into&nbsp;percentages.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;Dictionary&nbsp;containing&nbsp;percentages&nbsp;for&nbsp;each&nbsp;CPU&nbsp;as&nbsp;well&nbsp;as&nbsp;an&nbsp;average<br>
+&nbsp;&nbsp;&nbsp;&nbsp;across&nbsp;all&nbsp;CPUs.</tt></dd></dl>
+
+<dl><dt><a name="SysfsPowerMonitor-ParseFreqSample"><strong>ParseFreqSample</strong></a>(sample)</dt><dd><tt>Parse&nbsp;a&nbsp;single&nbsp;frequency&nbsp;sample.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;sample:&nbsp;The&nbsp;single&nbsp;sample&nbsp;of&nbsp;frequency&nbsp;data&nbsp;to&nbsp;be&nbsp;parsed.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;A&nbsp;dictionary&nbsp;associating&nbsp;a&nbsp;frequency&nbsp;with&nbsp;a&nbsp;time.</tt></dd></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a>:<br>
+<dl><dt><a name="SysfsPowerMonitor-CanMeasurePerApplicationPower"><strong>CanMeasurePerApplicationPower</strong></a>(self)</dt><dd><tt>Returns&nbsp;True&nbsp;if&nbsp;the&nbsp;power&nbsp;monitor&nbsp;can&nbsp;measure&nbsp;power&nbsp;for&nbsp;the&nbsp;target<br>
+application&nbsp;in&nbsp;isolation.&nbsp;False&nbsp;if&nbsp;power&nbsp;measurement&nbsp;is&nbsp;for&nbsp;full&nbsp;system<br>
+energy&nbsp;consumption.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.power_monitor.html#PowerMonitor">telemetry.internal.platform.power_monitor.PowerMonitor</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>CPU_PATH</strong> = '/sys/devices/system/cpu/'</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.android_prebuilt_profiler_helper.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.android_prebuilt_profiler_helper.html
new file mode 100644
index 0000000..967106d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.android_prebuilt_profiler_helper.html
@@ -0,0 +1,35 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.profiler.android_prebuilt_profiler_helper</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.<a href="telemetry.internal.platform.profiler.html"><font color="#ffffff">profiler</font></a>.android_prebuilt_profiler_helper</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/profiler/android_prebuilt_profiler_helper.py">telemetry/internal/platform/profiler/android_prebuilt_profiler_helper.py</a></font></td></tr></table>
+    <p><tt>Android-specific,&nbsp;installs&nbsp;pre-built&nbsp;profilers.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.util.binary_manager.html">telemetry.internal.util.binary_manager</a><br>
+</td><td width="25%" valign=top><a href="telemetry.decorators.html">telemetry.decorators</a><br>
+</td><td width="25%" valign=top><a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top><a href="os.html">os</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-GetDevicePath"><strong>GetDevicePath</strong></a>(profiler_binary)</dt></dl>
+ <dl><dt><a name="-InstallOnDevice"><strong>InstallOnDevice</strong></a>(*args, **kwargs)</dt></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.android_profiling_helper.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.android_profiling_helper.html
new file mode 100644
index 0000000..fed15b2
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.android_profiling_helper.html
@@ -0,0 +1,91 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.profiler.android_profiling_helper</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.<a href="telemetry.internal.platform.profiler.html"><font color="#ffffff">profiler</font></a>.android_profiling_helper</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/profiler/android_profiling_helper.py">telemetry/internal/platform/profiler/android_profiling_helper.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.platform.profiler.android_prebuilt_profiler_helper.html">telemetry.internal.platform.profiler.android_prebuilt_profiler_helper</a><br>
+<a href="telemetry.internal.util.binary_manager.html">telemetry.internal.util.binary_manager</a><br>
+<a href="telemetry.decorators.html">telemetry.decorators</a><br>
+<a href="glob.html">glob</a><br>
+</td><td width="25%" valign=top><a href="hashlib.html">hashlib</a><br>
+<a href="logging.html">logging</a><br>
+<a href="devil.android.md5sum.html">devil.android.md5sum</a><br>
+<a href="os.html">os</a><br>
+</td><td width="25%" valign=top><a href="platform.html">platform</a><br>
+<a href="re.html">re</a><br>
+<a href="shutil.html">shutil</a><br>
+<a href="sqlite3.html">sqlite3</a><br>
+</td><td width="25%" valign=top><a href="subprocess.html">subprocess</a><br>
+<a href="telemetry.core.platform.html">telemetry.core.platform</a><br>
+<a href="telemetry.core.util.html">telemetry.core.util</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-CreateSymFs"><strong>CreateSymFs</strong></a>(device, symfs_dir, libraries, use_symlinks<font color="#909090">=True</font>)</dt><dd><tt>Creates&nbsp;a&nbsp;symfs&nbsp;directory&nbsp;to&nbsp;be&nbsp;used&nbsp;for&nbsp;symbolizing&nbsp;profiles.<br>
+&nbsp;<br>
+Prepares&nbsp;a&nbsp;set&nbsp;of&nbsp;files&nbsp;("symfs")&nbsp;to&nbsp;be&nbsp;used&nbsp;with&nbsp;profilers&nbsp;such&nbsp;as&nbsp;perf&nbsp;for<br>
+converting&nbsp;binary&nbsp;addresses&nbsp;into&nbsp;human&nbsp;readable&nbsp;function&nbsp;names.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;device:&nbsp;DeviceUtils&nbsp;instance&nbsp;identifying&nbsp;the&nbsp;target&nbsp;device.<br>
+&nbsp;&nbsp;symfs_dir:&nbsp;Path&nbsp;where&nbsp;the&nbsp;symfs&nbsp;should&nbsp;be&nbsp;created.<br>
+&nbsp;&nbsp;libraries:&nbsp;Set&nbsp;of&nbsp;library&nbsp;file&nbsp;names&nbsp;that&nbsp;should&nbsp;be&nbsp;included&nbsp;in&nbsp;the&nbsp;symfs.<br>
+&nbsp;&nbsp;use_symlinks:&nbsp;If&nbsp;True,&nbsp;link&nbsp;instead&nbsp;of&nbsp;copy&nbsp;unstripped&nbsp;libraries&nbsp;into&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;symfs.&nbsp;This&nbsp;will&nbsp;speed&nbsp;up&nbsp;the&nbsp;operation,&nbsp;but&nbsp;the&nbsp;resulting&nbsp;symfs&nbsp;will&nbsp;no<br>
+&nbsp;&nbsp;&nbsp;&nbsp;longer&nbsp;be&nbsp;valid&nbsp;if&nbsp;the&nbsp;linked&nbsp;files&nbsp;are&nbsp;modified,&nbsp;e.g.,&nbsp;by&nbsp;rebuilding.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;The&nbsp;absolute&nbsp;path&nbsp;to&nbsp;the&nbsp;kernel&nbsp;symbols&nbsp;within&nbsp;the&nbsp;created&nbsp;symfs.</tt></dd></dl>
+ <dl><dt><a name="-GetPerfhostName"><strong>GetPerfhostName</strong></a>(*args, **kwargs)</dt></dl>
+ <dl><dt><a name="-GetRequiredLibrariesForPerfProfile"><strong>GetRequiredLibrariesForPerfProfile</strong></a>(profile_file)</dt><dd><tt>Returns&nbsp;the&nbsp;set&nbsp;of&nbsp;libraries&nbsp;necessary&nbsp;to&nbsp;symbolize&nbsp;a&nbsp;given&nbsp;perf&nbsp;profile.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;profile_file:&nbsp;Path&nbsp;to&nbsp;perf&nbsp;profile&nbsp;to&nbsp;analyse.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;A&nbsp;set&nbsp;of&nbsp;required&nbsp;library&nbsp;file&nbsp;names.</tt></dd></dl>
+ <dl><dt><a name="-GetRequiredLibrariesForVTuneProfile"><strong>GetRequiredLibrariesForVTuneProfile</strong></a>(profile_file)</dt><dd><tt>Returns&nbsp;the&nbsp;set&nbsp;of&nbsp;libraries&nbsp;necessary&nbsp;to&nbsp;symbolize&nbsp;a&nbsp;given&nbsp;VTune&nbsp;profile.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;profile_file:&nbsp;Path&nbsp;to&nbsp;VTune&nbsp;profile&nbsp;to&nbsp;analyse.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;A&nbsp;set&nbsp;of&nbsp;required&nbsp;library&nbsp;file&nbsp;names.</tt></dd></dl>
+ <dl><dt><a name="-GetToolchainBinaryPath"><strong>GetToolchainBinaryPath</strong></a>(library_file, binary_name)</dt><dd><tt>Return&nbsp;the&nbsp;path&nbsp;to&nbsp;an&nbsp;Android&nbsp;toolchain&nbsp;binary&nbsp;on&nbsp;the&nbsp;host.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;library_file:&nbsp;ELF&nbsp;library&nbsp;which&nbsp;is&nbsp;used&nbsp;to&nbsp;identify&nbsp;the&nbsp;used&nbsp;ABI,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;architecture&nbsp;and&nbsp;toolchain.<br>
+&nbsp;&nbsp;binary_name:&nbsp;Binary&nbsp;to&nbsp;search&nbsp;for,&nbsp;e.g.,&nbsp;'objdump'<br>
+Returns:<br>
+&nbsp;&nbsp;Full&nbsp;path&nbsp;to&nbsp;binary&nbsp;or&nbsp;None&nbsp;if&nbsp;the&nbsp;binary&nbsp;was&nbsp;not&nbsp;found.</tt></dd></dl>
+ <dl><dt><a name="-PrepareDeviceForPerf"><strong>PrepareDeviceForPerf</strong></a>(device)</dt><dd><tt>Set&nbsp;up&nbsp;a&nbsp;device&nbsp;for&nbsp;running&nbsp;perf.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;device:&nbsp;DeviceUtils&nbsp;instance&nbsp;identifying&nbsp;the&nbsp;target&nbsp;device.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;The&nbsp;path&nbsp;to&nbsp;the&nbsp;installed&nbsp;perf&nbsp;binary&nbsp;on&nbsp;the&nbsp;device.</tt></dd></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.android_screen_recorder_profiler.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.android_screen_recorder_profiler.html
new file mode 100644
index 0000000..b0fd553
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.android_screen_recorder_profiler.html
@@ -0,0 +1,82 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.profiler.android_screen_recorder_profiler</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.<a href="telemetry.internal.platform.profiler.html"><font color="#ffffff">profiler</font></a>.android_screen_recorder_profiler</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/profiler/android_screen_recorder_profiler.py">telemetry/internal/platform/profiler/android_screen_recorder_profiler.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.backends.chrome.android_browser_finder.html">telemetry.internal.backends.chrome.android_browser_finder</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.profiler.html">telemetry.internal.platform.profiler</a><br>
+</td><td width="25%" valign=top><a href="pylib.screenshot.html">pylib.screenshot</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.profiler.android_screen_recorder_profiler.html#AndroidScreenRecordingProfiler">AndroidScreenRecordingProfiler</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="AndroidScreenRecordingProfiler">class <strong>AndroidScreenRecordingProfiler</strong></a>(<a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Captures&nbsp;a&nbsp;screen&nbsp;recording&nbsp;on&nbsp;Android.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.profiler.android_screen_recorder_profiler.html#AndroidScreenRecordingProfiler">AndroidScreenRecordingProfiler</a></dd>
+<dd><a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="AndroidScreenRecordingProfiler-CollectProfile"><strong>CollectProfile</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidScreenRecordingProfiler-__init__"><strong>__init__</strong></a>(self, browser_backend, platform_backend, output_path, state)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="AndroidScreenRecordingProfiler-is_supported"><strong>is_supported</strong></a>(cls, browser_type)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="AndroidScreenRecordingProfiler-name"><strong>name</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Class methods inherited from <a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>:<br>
+<dl><dt><a name="AndroidScreenRecordingProfiler-CustomizeBrowserOptions"><strong>CustomizeBrowserOptions</strong></a>(cls, browser_type, options)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Override&nbsp;to&nbsp;customize&nbsp;the&nbsp;Browser's&nbsp;options&nbsp;before&nbsp;it&nbsp;is&nbsp;created.</tt></dd></dl>
+
+<dl><dt><a name="AndroidScreenRecordingProfiler-WillCloseBrowser"><strong>WillCloseBrowser</strong></a>(cls, browser_backend, platform_backend)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Called&nbsp;before&nbsp;the&nbsp;browser&nbsp;is&nbsp;stopped.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.android_systrace_profiler.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.android_systrace_profiler.html
new file mode 100644
index 0000000..eeb0060
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.android_systrace_profiler.html
@@ -0,0 +1,88 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.profiler.android_systrace_profiler</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.<a href="telemetry.internal.platform.profiler.html"><font color="#ffffff">profiler</font></a>.android_systrace_profiler</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/profiler/android_systrace_profiler.py">telemetry/internal/platform/profiler/android_systrace_profiler.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="StringIO.html">StringIO</a><br>
+<a href="telemetry.internal.backends.chrome.android_browser_finder.html">telemetry.internal.backends.chrome.android_browser_finder</a><br>
+<a href="os.html">os</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.profiler.html">telemetry.internal.platform.profiler</a><br>
+<a href="subprocess.html">subprocess</a><br>
+<a href="telemetry.timeline.trace_data.html">telemetry.timeline.trace_data</a><br>
+</td><td width="25%" valign=top><a href="telemetry.timeline.tracing_options.html">telemetry.timeline.tracing_options</a><br>
+<a href="telemetry.core.util.html">telemetry.core.util</a><br>
+<a href="zipfile.html">zipfile</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.profiler.android_systrace_profiler.html#AndroidSystraceProfiler">AndroidSystraceProfiler</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="AndroidSystraceProfiler">class <strong>AndroidSystraceProfiler</strong></a>(<a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Collects&nbsp;a&nbsp;Systrace&nbsp;on&nbsp;Android.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.profiler.android_systrace_profiler.html#AndroidSystraceProfiler">AndroidSystraceProfiler</a></dd>
+<dd><a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="AndroidSystraceProfiler-CollectProfile"><strong>CollectProfile</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidSystraceProfiler-__init__"><strong>__init__</strong></a>(self, browser_backend, platform_backend, output_path, state, device<font color="#909090">=None</font>)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="AndroidSystraceProfiler-is_supported"><strong>is_supported</strong></a>(cls, browser_type)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="AndroidSystraceProfiler-name"><strong>name</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Class methods inherited from <a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>:<br>
+<dl><dt><a name="AndroidSystraceProfiler-CustomizeBrowserOptions"><strong>CustomizeBrowserOptions</strong></a>(cls, browser_type, options)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Override&nbsp;to&nbsp;customize&nbsp;the&nbsp;Browser's&nbsp;options&nbsp;before&nbsp;it&nbsp;is&nbsp;created.</tt></dd></dl>
+
+<dl><dt><a name="AndroidSystraceProfiler-WillCloseBrowser"><strong>WillCloseBrowser</strong></a>(cls, browser_backend, platform_backend)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Called&nbsp;before&nbsp;the&nbsp;browser&nbsp;is&nbsp;stopped.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.android_traceview_profiler.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.android_traceview_profiler.html
new file mode 100644
index 0000000..38987a7
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.android_traceview_profiler.html
@@ -0,0 +1,85 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.profiler.android_traceview_profiler</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.<a href="telemetry.internal.platform.profiler.html"><font color="#ffffff">profiler</font></a>.android_traceview_profiler</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/profiler/android_traceview_profiler.py">telemetry/internal/platform/profiler/android_traceview_profiler.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.backends.chrome.android_browser_finder.html">telemetry.internal.backends.chrome.android_browser_finder</a><br>
+<a href="devil.android.device_errors.html">devil.android.device_errors</a><br>
+</td><td width="25%" valign=top><a href="logging.html">logging</a><br>
+<a href="os.html">os</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.profiler.html">telemetry.internal.platform.profiler</a><br>
+<a href="telemetry.core.util.html">telemetry.core.util</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.profiler.android_traceview_profiler.html#AndroidTraceviewProfiler">AndroidTraceviewProfiler</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="AndroidTraceviewProfiler">class <strong>AndroidTraceviewProfiler</strong></a>(<a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Collects&nbsp;a&nbsp;Traceview&nbsp;on&nbsp;Android.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.profiler.android_traceview_profiler.html#AndroidTraceviewProfiler">AndroidTraceviewProfiler</a></dd>
+<dd><a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="AndroidTraceviewProfiler-CollectProfile"><strong>CollectProfile</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AndroidTraceviewProfiler-__init__"><strong>__init__</strong></a>(self, browser_backend, platform_backend, output_path, state)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="AndroidTraceviewProfiler-is_supported"><strong>is_supported</strong></a>(cls, browser_type)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="AndroidTraceviewProfiler-name"><strong>name</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Class methods inherited from <a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>:<br>
+<dl><dt><a name="AndroidTraceviewProfiler-CustomizeBrowserOptions"><strong>CustomizeBrowserOptions</strong></a>(cls, browser_type, options)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Override&nbsp;to&nbsp;customize&nbsp;the&nbsp;Browser's&nbsp;options&nbsp;before&nbsp;it&nbsp;is&nbsp;created.</tt></dd></dl>
+
+<dl><dt><a name="AndroidTraceviewProfiler-WillCloseBrowser"><strong>WillCloseBrowser</strong></a>(cls, browser_backend, platform_backend)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Called&nbsp;before&nbsp;the&nbsp;browser&nbsp;is&nbsp;stopped.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.html
new file mode 100644
index 0000000..36d9229
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.html
@@ -0,0 +1,105 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: package telemetry.internal.platform.profiler</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.profiler</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/profiler/__init__.py">telemetry/internal/platform/profiler/__init__.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Package Contents</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.platform.profiler.android_prebuilt_profiler_helper.html">android_prebuilt_profiler_helper</a><br>
+<a href="telemetry.internal.platform.profiler.android_profiling_helper.html">android_profiling_helper</a><br>
+<a href="telemetry.internal.platform.profiler.android_profiling_helper_unittest.html">android_profiling_helper_unittest</a><br>
+<a href="telemetry.internal.platform.profiler.android_screen_recorder_profiler.html">android_screen_recorder_profiler</a><br>
+<a href="telemetry.internal.platform.profiler.android_screen_recorder_profiler_unittest.html">android_screen_recorder_profiler_unittest</a><br>
+<a href="telemetry.internal.platform.profiler.android_systrace_profiler.html">android_systrace_profiler</a><br>
+<a href="telemetry.internal.platform.profiler.android_systrace_profiler_unittest.html">android_systrace_profiler_unittest</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.profiler.android_traceview_profiler.html">android_traceview_profiler</a><br>
+<a href="telemetry.internal.platform.profiler.iprofiler_profiler.html">iprofiler_profiler</a><br>
+<a href="telemetry.internal.platform.profiler.java_heap_profiler.html">java_heap_profiler</a><br>
+<a href="telemetry.internal.platform.profiler.monsoon.html">monsoon</a><br>
+<a href="telemetry.internal.platform.profiler.monsoon_profiler.html">monsoon_profiler</a><br>
+<a href="telemetry.internal.platform.profiler.netlog_profiler.html">netlog_profiler</a><br>
+<a href="telemetry.internal.platform.profiler.oomkiller_profiler.html">oomkiller_profiler</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.profiler.perf_profiler.html">perf_profiler</a><br>
+<a href="telemetry.internal.platform.profiler.perf_profiler_unittest.html">perf_profiler_unittest</a><br>
+<a href="telemetry.internal.platform.profiler.profiler_finder.html">profiler_finder</a><br>
+<a href="telemetry.internal.platform.profiler.sample_profiler.html">sample_profiler</a><br>
+<a href="telemetry.internal.platform.profiler.strace_profiler.html">strace_profiler</a><br>
+<a href="telemetry.internal.platform.profiler.tcmalloc_heap_profiler.html">tcmalloc_heap_profiler</a><br>
+<a href="telemetry.internal.platform.profiler.tcpdump_profiler.html">tcpdump_profiler</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.profiler.trace_profiler.html">trace_profiler</a><br>
+<a href="telemetry.internal.platform.profiler.trace_profiler_unittest.html">trace_profiler_unittest</a><br>
+<a href="telemetry.internal.platform.profiler.v8_profiler.html">v8_profiler</a><br>
+<a href="telemetry.internal.platform.profiler.vtune_profiler.html">vtune_profiler</a><br>
+<a href="telemetry.internal.platform.profiler.vtune_profiler_unittest.html">vtune_profiler_unittest</a><br>
+<a href="telemetry.internal.platform.profiler.win_pgo_profiler.html">win_pgo_profiler</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.profiler.html#Profiler">Profiler</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="Profiler">class <strong>Profiler</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;sampling&nbsp;profiler&nbsp;provided&nbsp;by&nbsp;the&nbsp;platform.<br>
+&nbsp;<br>
+A&nbsp;profiler&nbsp;is&nbsp;started&nbsp;on&nbsp;its&nbsp;constructor,&nbsp;and&nbsp;should<br>
+gather&nbsp;data&nbsp;until&nbsp;<a href="#Profiler-CollectProfile">CollectProfile</a>().<br>
+The&nbsp;life&nbsp;cycle&nbsp;is&nbsp;normally&nbsp;tied&nbsp;to&nbsp;a&nbsp;single&nbsp;page,<br>
+i.e.,&nbsp;multiple&nbsp;profilers&nbsp;will&nbsp;be&nbsp;created&nbsp;for&nbsp;a&nbsp;page&nbsp;set.<br>
+<a href="#Profiler-WillCloseBrowser">WillCloseBrowser</a>()&nbsp;is&nbsp;called&nbsp;right&nbsp;before&nbsp;the&nbsp;browser<br>
+is&nbsp;closed&nbsp;to&nbsp;allow&nbsp;any&nbsp;further&nbsp;cleanup.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="Profiler-CollectProfile"><strong>CollectProfile</strong></a>(self)</dt><dd><tt>Collect&nbsp;the&nbsp;profile&nbsp;from&nbsp;the&nbsp;profiler.</tt></dd></dl>
+
+<dl><dt><a name="Profiler-__init__"><strong>__init__</strong></a>(self, browser_backend, platform_backend, output_path, state)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="Profiler-CustomizeBrowserOptions"><strong>CustomizeBrowserOptions</strong></a>(cls, browser_type, options)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Override&nbsp;to&nbsp;customize&nbsp;the&nbsp;Browser's&nbsp;options&nbsp;before&nbsp;it&nbsp;is&nbsp;created.</tt></dd></dl>
+
+<dl><dt><a name="Profiler-WillCloseBrowser"><strong>WillCloseBrowser</strong></a>(cls, browser_backend, platform_backend)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Called&nbsp;before&nbsp;the&nbsp;browser&nbsp;is&nbsp;stopped.</tt></dd></dl>
+
+<dl><dt><a name="Profiler-is_supported"><strong>is_supported</strong></a>(cls, browser_type)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>True&nbsp;iff&nbsp;this&nbsp;profiler&nbsp;is&nbsp;currently&nbsp;supported&nbsp;by&nbsp;the&nbsp;platform.</tt></dd></dl>
+
+<dl><dt><a name="Profiler-name"><strong>name</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>User-friendly&nbsp;name&nbsp;of&nbsp;this&nbsp;profiler.</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.iprofiler_profiler.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.iprofiler_profiler.html
new file mode 100644
index 0000000..3a406de
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.iprofiler_profiler.html
@@ -0,0 +1,84 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.profiler.iprofiler_profiler</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.<a href="telemetry.internal.platform.profiler.html"><font color="#ffffff">profiler</font></a>.iprofiler_profiler</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/profiler/iprofiler_profiler.py">telemetry/internal/platform/profiler/iprofiler_profiler.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+<a href="os.html">os</a><br>
+</td><td width="25%" valign=top><a href="pexpect.html">pexpect</a><br>
+<a href="telemetry.internal.platform.profiler.html">telemetry.internal.platform.profiler</a><br>
+</td><td width="25%" valign=top><a href="signal.html">signal</a><br>
+<a href="sys.html">sys</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.util.html">telemetry.core.util</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.profiler.iprofiler_profiler.html#IprofilerProfiler">IprofilerProfiler</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="IprofilerProfiler">class <strong>IprofilerProfiler</strong></a>(<a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.profiler.iprofiler_profiler.html#IprofilerProfiler">IprofilerProfiler</a></dd>
+<dd><a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="IprofilerProfiler-CollectProfile"><strong>CollectProfile</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="IprofilerProfiler-__init__"><strong>__init__</strong></a>(self, browser_backend, platform_backend, output_path, state)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="IprofilerProfiler-is_supported"><strong>is_supported</strong></a>(cls, browser_type)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="IprofilerProfiler-name"><strong>name</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Class methods inherited from <a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>:<br>
+<dl><dt><a name="IprofilerProfiler-CustomizeBrowserOptions"><strong>CustomizeBrowserOptions</strong></a>(cls, browser_type, options)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Override&nbsp;to&nbsp;customize&nbsp;the&nbsp;Browser's&nbsp;options&nbsp;before&nbsp;it&nbsp;is&nbsp;created.</tt></dd></dl>
+
+<dl><dt><a name="IprofilerProfiler-WillCloseBrowser"><strong>WillCloseBrowser</strong></a>(cls, browser_backend, platform_backend)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Called&nbsp;before&nbsp;the&nbsp;browser&nbsp;is&nbsp;stopped.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.java_heap_profiler.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.java_heap_profiler.html
new file mode 100644
index 0000000..250501b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.java_heap_profiler.html
@@ -0,0 +1,88 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.profiler.java_heap_profiler</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.<a href="telemetry.internal.platform.profiler.html"><font color="#ffffff">profiler</font></a>.java_heap_profiler</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/profiler/java_heap_profiler.py">telemetry/internal/platform/profiler/java_heap_profiler.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.backends.chrome.android_browser_finder.html">telemetry.internal.backends.chrome.android_browser_finder</a><br>
+<a href="pylib.constants.html">pylib.constants</a><br>
+<a href="devil.android.device_errors.html">devil.android.device_errors</a><br>
+</td><td width="25%" valign=top><a href="logging.html">logging</a><br>
+<a href="os.html">os</a><br>
+<a href="telemetry.internal.platform.profiler.html">telemetry.internal.platform.profiler</a><br>
+</td><td width="25%" valign=top><a href="subprocess.html">subprocess</a><br>
+<a href="threading.html">threading</a><br>
+<a href="telemetry.core.util.html">telemetry.core.util</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.profiler.java_heap_profiler.html#JavaHeapProfiler">JavaHeapProfiler</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="JavaHeapProfiler">class <strong>JavaHeapProfiler</strong></a>(<a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Android-specific,&nbsp;trigger&nbsp;and&nbsp;fetch&nbsp;java&nbsp;heap&nbsp;dumps.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.profiler.java_heap_profiler.html#JavaHeapProfiler">JavaHeapProfiler</a></dd>
+<dd><a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="JavaHeapProfiler-CollectProfile"><strong>CollectProfile</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="JavaHeapProfiler-__init__"><strong>__init__</strong></a>(self, browser_backend, platform_backend, output_path, state)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="JavaHeapProfiler-is_supported"><strong>is_supported</strong></a>(cls, browser_type)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="JavaHeapProfiler-name"><strong>name</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Class methods inherited from <a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>:<br>
+<dl><dt><a name="JavaHeapProfiler-CustomizeBrowserOptions"><strong>CustomizeBrowserOptions</strong></a>(cls, browser_type, options)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Override&nbsp;to&nbsp;customize&nbsp;the&nbsp;Browser's&nbsp;options&nbsp;before&nbsp;it&nbsp;is&nbsp;created.</tt></dd></dl>
+
+<dl><dt><a name="JavaHeapProfiler-WillCloseBrowser"><strong>WillCloseBrowser</strong></a>(cls, browser_backend, platform_backend)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Called&nbsp;before&nbsp;the&nbsp;browser&nbsp;is&nbsp;stopped.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.monsoon.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.monsoon.html
new file mode 100644
index 0000000..9b8a4df
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.monsoon.html
@@ -0,0 +1,194 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.profiler.monsoon</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.<a href="telemetry.internal.platform.profiler.html"><font color="#ffffff">profiler</font></a>.monsoon</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/profiler/monsoon.py">telemetry/internal/platform/profiler/monsoon.py</a></font></td></tr></table>
+    <p><tt>Interface&nbsp;for&nbsp;a&nbsp;USB-connected&nbsp;<a href="#Monsoon">Monsoon</a>&nbsp;power&nbsp;meter.<br>
+&nbsp;<br>
+<a href="http://msoon.com/LabEquipment/PowerMonitor/">http://msoon.com/LabEquipment/PowerMonitor/</a><br>
+Currently&nbsp;Unix-only.&nbsp;Relies&nbsp;on&nbsp;fcntl,&nbsp;/dev,&nbsp;and&nbsp;/tmp.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="collections.html">collections</a><br>
+<a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top><a href="os.html">os</a><br>
+<a href="select.html">select</a><br>
+</td><td width="25%" valign=top><a href="serial.html">serial</a><br>
+<a href="struct.html">struct</a><br>
+</td><td width="25%" valign=top><a href="time.html">time</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.profiler.monsoon.html#Monsoon">Monsoon</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#tuple">__builtin__.tuple</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.profiler.monsoon.html#Power">Power</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="Monsoon">class <strong>Monsoon</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Provides&nbsp;a&nbsp;simple&nbsp;class&nbsp;to&nbsp;use&nbsp;the&nbsp;power&nbsp;meter.<br>
+&nbsp;<br>
+mon&nbsp;=&nbsp;monsoon.<a href="#Monsoon">Monsoon</a>()<br>
+mon.<a href="#Monsoon-SetVoltage">SetVoltage</a>(3.7)<br>
+mon.<a href="#Monsoon-StartDataCollection">StartDataCollection</a>()<br>
+mydata&nbsp;=&nbsp;[]<br>
+while&nbsp;len(mydata)&nbsp;&lt;&nbsp;1000:<br>
+&nbsp;&nbsp;mydata.extend(mon.<a href="#Monsoon-CollectData">CollectData</a>())<br>
+mon.<a href="#Monsoon-StopDataCollection">StopDataCollection</a>()<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="Monsoon-CollectData"><strong>CollectData</strong></a>(self)</dt><dd><tt>Return&nbsp;some&nbsp;current&nbsp;samples.&nbsp;&nbsp;Call&nbsp;<a href="#Monsoon-StartDataCollection">StartDataCollection</a>()&nbsp;first.</tt></dd></dl>
+
+<dl><dt><a name="Monsoon-GetStatus"><strong>GetStatus</strong></a>(self)</dt><dd><tt>Requests&nbsp;and&nbsp;waits&nbsp;for&nbsp;status.&nbsp;&nbsp;Returns&nbsp;status&nbsp;dictionary.</tt></dd></dl>
+
+<dl><dt><a name="Monsoon-SetMaxCurrent"><strong>SetMaxCurrent</strong></a>(self, a)</dt><dd><tt>Set&nbsp;the&nbsp;max&nbsp;output&nbsp;current.&nbsp;the&nbsp;unit&nbsp;of&nbsp;|a|&nbsp;:&nbsp;Amperes</tt></dd></dl>
+
+<dl><dt><a name="Monsoon-SetStartupCurrent"><strong>SetStartupCurrent</strong></a>(self, a)</dt><dd><tt>Set&nbsp;the&nbsp;max&nbsp;startup&nbsp;output&nbsp;current.&nbsp;the&nbsp;unit&nbsp;of&nbsp;|a|&nbsp;:&nbsp;Amperes</tt></dd></dl>
+
+<dl><dt><a name="Monsoon-SetUsbPassthrough"><strong>SetUsbPassthrough</strong></a>(self, val)</dt><dd><tt>Set&nbsp;the&nbsp;USB&nbsp;passthrough&nbsp;mode:&nbsp;0&nbsp;=&nbsp;off,&nbsp;1&nbsp;=&nbsp;on,&nbsp;&nbsp;2&nbsp;=&nbsp;auto.</tt></dd></dl>
+
+<dl><dt><a name="Monsoon-SetVoltage"><strong>SetVoltage</strong></a>(self, v)</dt><dd><tt>Set&nbsp;the&nbsp;output&nbsp;voltage,&nbsp;0&nbsp;to&nbsp;disable.</tt></dd></dl>
+
+<dl><dt><a name="Monsoon-StartDataCollection"><strong>StartDataCollection</strong></a>(self)</dt><dd><tt>Tell&nbsp;the&nbsp;device&nbsp;to&nbsp;start&nbsp;collecting&nbsp;and&nbsp;sending&nbsp;measurement&nbsp;data.</tt></dd></dl>
+
+<dl><dt><a name="Monsoon-StopDataCollection"><strong>StopDataCollection</strong></a>(self)</dt><dd><tt>Tell&nbsp;the&nbsp;device&nbsp;to&nbsp;stop&nbsp;collecting&nbsp;measurement&nbsp;data.</tt></dd></dl>
+
+<dl><dt><a name="Monsoon-__init__"><strong>__init__</strong></a>(self, device<font color="#909090">=None</font>, serialno<font color="#909090">=None</font>, wait<font color="#909090">=True</font>)</dt><dd><tt>Establish&nbsp;a&nbsp;connection&nbsp;to&nbsp;a&nbsp;<a href="#Monsoon">Monsoon</a>.<br>
+&nbsp;<br>
+By&nbsp;default,&nbsp;opens&nbsp;the&nbsp;first&nbsp;available&nbsp;port,&nbsp;waiting&nbsp;if&nbsp;none&nbsp;are&nbsp;ready.<br>
+A&nbsp;particular&nbsp;port&nbsp;can&nbsp;be&nbsp;specified&nbsp;with&nbsp;'device',&nbsp;or&nbsp;a&nbsp;particular&nbsp;<a href="#Monsoon">Monsoon</a><br>
+can&nbsp;be&nbsp;specified&nbsp;with&nbsp;'serialno'&nbsp;(using&nbsp;the&nbsp;number&nbsp;printed&nbsp;on&nbsp;its&nbsp;back).<br>
+With&nbsp;wait=False,&nbsp;IOError&nbsp;is&nbsp;thrown&nbsp;if&nbsp;a&nbsp;device&nbsp;is&nbsp;not&nbsp;immediately&nbsp;available.</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="Power">class <strong>Power</strong></a>(<a href="__builtin__.html#tuple">__builtin__.tuple</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt><a href="#Power">Power</a>(amps,&nbsp;volts)<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.profiler.monsoon.html#Power">Power</a></dd>
+<dd><a href="__builtin__.html#tuple">__builtin__.tuple</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="Power-__getnewargs__"><strong>__getnewargs__</strong></a>(self)</dt><dd><tt>Return&nbsp;self&nbsp;as&nbsp;a&nbsp;plain&nbsp;<a href="__builtin__.html#tuple">tuple</a>.&nbsp;&nbsp;Used&nbsp;by&nbsp;copy&nbsp;and&nbsp;pickle.</tt></dd></dl>
+
+<dl><dt><a name="Power-__getstate__"><strong>__getstate__</strong></a>(self)</dt><dd><tt>Exclude&nbsp;the&nbsp;OrderedDict&nbsp;from&nbsp;pickling</tt></dd></dl>
+
+<dl><dt><a name="Power-__repr__"><strong>__repr__</strong></a>(self)</dt><dd><tt>Return&nbsp;a&nbsp;nicely&nbsp;formatted&nbsp;representation&nbsp;string</tt></dd></dl>
+
+<dl><dt><a name="Power-_asdict"><strong>_asdict</strong></a>(self)</dt><dd><tt>Return&nbsp;a&nbsp;new&nbsp;OrderedDict&nbsp;which&nbsp;maps&nbsp;field&nbsp;names&nbsp;to&nbsp;their&nbsp;values</tt></dd></dl>
+
+<dl><dt><a name="Power-_replace"><strong>_replace</strong></a>(_self, **kwds)</dt><dd><tt>Return&nbsp;a&nbsp;new&nbsp;<a href="#Power">Power</a>&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;replacing&nbsp;specified&nbsp;fields&nbsp;with&nbsp;new&nbsp;values</tt></dd></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="Power-_make"><strong>_make</strong></a>(cls, iterable, new<font color="#909090">=&lt;built-in method __new__ of type object&gt;</font>, len<font color="#909090">=&lt;built-in function len&gt;</font>)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Make&nbsp;a&nbsp;new&nbsp;<a href="#Power">Power</a>&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;from&nbsp;a&nbsp;sequence&nbsp;or&nbsp;iterable</tt></dd></dl>
+
+<hr>
+Static methods defined here:<br>
+<dl><dt><a name="Power-__new__"><strong>__new__</strong></a>(_cls, amps, volts)</dt><dd><tt>Create&nbsp;new&nbsp;instance&nbsp;of&nbsp;<a href="#Power">Power</a>(amps,&nbsp;volts)</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>Return&nbsp;a&nbsp;new&nbsp;OrderedDict&nbsp;which&nbsp;maps&nbsp;field&nbsp;names&nbsp;to&nbsp;their&nbsp;values</tt></dd>
+</dl>
+<dl><dt><strong>amps</strong></dt>
+<dd><tt>Alias&nbsp;for&nbsp;field&nbsp;number&nbsp;0</tt></dd>
+</dl>
+<dl><dt><strong>volts</strong></dt>
+<dd><tt>Alias&nbsp;for&nbsp;field&nbsp;number&nbsp;1</tt></dd>
+</dl>
+<hr>
+Data and other attributes defined here:<br>
+<dl><dt><strong>_fields</strong> = ('amps', 'volts')</dl>
+
+<hr>
+Methods inherited from <a href="__builtin__.html#tuple">__builtin__.tuple</a>:<br>
+<dl><dt><a name="Power-__add__"><strong>__add__</strong></a>(...)</dt><dd><tt>x.<a href="#Power-__add__">__add__</a>(y)&nbsp;&lt;==&gt;&nbsp;x+y</tt></dd></dl>
+
+<dl><dt><a name="Power-__contains__"><strong>__contains__</strong></a>(...)</dt><dd><tt>x.<a href="#Power-__contains__">__contains__</a>(y)&nbsp;&lt;==&gt;&nbsp;y&nbsp;in&nbsp;x</tt></dd></dl>
+
+<dl><dt><a name="Power-__eq__"><strong>__eq__</strong></a>(...)</dt><dd><tt>x.<a href="#Power-__eq__">__eq__</a>(y)&nbsp;&lt;==&gt;&nbsp;x==y</tt></dd></dl>
+
+<dl><dt><a name="Power-__ge__"><strong>__ge__</strong></a>(...)</dt><dd><tt>x.<a href="#Power-__ge__">__ge__</a>(y)&nbsp;&lt;==&gt;&nbsp;x&gt;=y</tt></dd></dl>
+
+<dl><dt><a name="Power-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#Power-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="Power-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#Power-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="Power-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#Power-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="Power-__gt__"><strong>__gt__</strong></a>(...)</dt><dd><tt>x.<a href="#Power-__gt__">__gt__</a>(y)&nbsp;&lt;==&gt;&nbsp;x&gt;y</tt></dd></dl>
+
+<dl><dt><a name="Power-__hash__"><strong>__hash__</strong></a>(...)</dt><dd><tt>x.<a href="#Power-__hash__">__hash__</a>()&nbsp;&lt;==&gt;&nbsp;hash(x)</tt></dd></dl>
+
+<dl><dt><a name="Power-__iter__"><strong>__iter__</strong></a>(...)</dt><dd><tt>x.<a href="#Power-__iter__">__iter__</a>()&nbsp;&lt;==&gt;&nbsp;iter(x)</tt></dd></dl>
+
+<dl><dt><a name="Power-__le__"><strong>__le__</strong></a>(...)</dt><dd><tt>x.<a href="#Power-__le__">__le__</a>(y)&nbsp;&lt;==&gt;&nbsp;x&lt;=y</tt></dd></dl>
+
+<dl><dt><a name="Power-__len__"><strong>__len__</strong></a>(...)</dt><dd><tt>x.<a href="#Power-__len__">__len__</a>()&nbsp;&lt;==&gt;&nbsp;len(x)</tt></dd></dl>
+
+<dl><dt><a name="Power-__lt__"><strong>__lt__</strong></a>(...)</dt><dd><tt>x.<a href="#Power-__lt__">__lt__</a>(y)&nbsp;&lt;==&gt;&nbsp;x&lt;y</tt></dd></dl>
+
+<dl><dt><a name="Power-__mul__"><strong>__mul__</strong></a>(...)</dt><dd><tt>x.<a href="#Power-__mul__">__mul__</a>(n)&nbsp;&lt;==&gt;&nbsp;x*n</tt></dd></dl>
+
+<dl><dt><a name="Power-__ne__"><strong>__ne__</strong></a>(...)</dt><dd><tt>x.<a href="#Power-__ne__">__ne__</a>(y)&nbsp;&lt;==&gt;&nbsp;x!=y</tt></dd></dl>
+
+<dl><dt><a name="Power-__rmul__"><strong>__rmul__</strong></a>(...)</dt><dd><tt>x.<a href="#Power-__rmul__">__rmul__</a>(n)&nbsp;&lt;==&gt;&nbsp;n*x</tt></dd></dl>
+
+<dl><dt><a name="Power-__sizeof__"><strong>__sizeof__</strong></a>(...)</dt><dd><tt>T.<a href="#Power-__sizeof__">__sizeof__</a>()&nbsp;--&nbsp;size&nbsp;of&nbsp;T&nbsp;in&nbsp;memory,&nbsp;in&nbsp;bytes</tt></dd></dl>
+
+<dl><dt><a name="Power-count"><strong>count</strong></a>(...)</dt><dd><tt>T.<a href="#Power-count">count</a>(value)&nbsp;-&gt;&nbsp;integer&nbsp;--&nbsp;return&nbsp;number&nbsp;of&nbsp;occurrences&nbsp;of&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="Power-index"><strong>index</strong></a>(...)</dt><dd><tt>T.<a href="#Power-index">index</a>(value,&nbsp;[start,&nbsp;[stop]])&nbsp;-&gt;&nbsp;integer&nbsp;--&nbsp;return&nbsp;first&nbsp;index&nbsp;of&nbsp;value.<br>
+Raises&nbsp;ValueError&nbsp;if&nbsp;the&nbsp;value&nbsp;is&nbsp;not&nbsp;present.</tt></dd></dl>
+
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.monsoon_profiler.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.monsoon_profiler.html
new file mode 100644
index 0000000..a360bb1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.monsoon_profiler.html
@@ -0,0 +1,85 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.profiler.monsoon_profiler</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.<a href="telemetry.internal.platform.profiler.html"><font color="#ffffff">profiler</font></a>.monsoon_profiler</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/profiler/monsoon_profiler.py">telemetry/internal/platform/profiler/monsoon_profiler.py</a></font></td></tr></table>
+    <p><tt><a href="telemetry.internal.platform.profiler.html#Profiler">Profiler</a>&nbsp;using&nbsp;data&nbsp;collected&nbsp;from&nbsp;a&nbsp;Monsoon&nbsp;power&nbsp;meter.<br>
+&nbsp;<br>
+<a href="http://msoon.com/LabEquipment/PowerMonitor/">http://msoon.com/LabEquipment/PowerMonitor/</a><br>
+Data&nbsp;collected&nbsp;is&nbsp;a&nbsp;namedtuple&nbsp;of&nbsp;(amps,&nbsp;volts),&nbsp;at&nbsp;5000&nbsp;samples/second.<br>
+Output&nbsp;graph&nbsp;plots&nbsp;power&nbsp;in&nbsp;watts&nbsp;over&nbsp;time&nbsp;in&nbsp;seconds.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="csv.html">csv</a><br>
+<a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.profiler.monsoon.html">telemetry.internal.platform.profiler.monsoon</a><br>
+<a href="multiprocessing.html">multiprocessing</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.profiler.html">telemetry.internal.platform.profiler</a><br>
+<a href="telemetry.util.statistics.html">telemetry.util.statistics</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.profiler.monsoon_profiler.html#MonsoonProfiler">MonsoonProfiler</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="MonsoonProfiler">class <strong>MonsoonProfiler</strong></a>(<a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.profiler.monsoon_profiler.html#MonsoonProfiler">MonsoonProfiler</a></dd>
+<dd><a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="MonsoonProfiler-CollectProfile"><strong>CollectProfile</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MonsoonProfiler-__init__"><strong>__init__</strong></a>(self, browser_backend, platform_backend, output_path, state)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="MonsoonProfiler-is_supported"><strong>is_supported</strong></a>(cls, browser_type)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="MonsoonProfiler-name"><strong>name</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Class methods inherited from <a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>:<br>
+<dl><dt><a name="MonsoonProfiler-CustomizeBrowserOptions"><strong>CustomizeBrowserOptions</strong></a>(cls, browser_type, options)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Override&nbsp;to&nbsp;customize&nbsp;the&nbsp;Browser's&nbsp;options&nbsp;before&nbsp;it&nbsp;is&nbsp;created.</tt></dd></dl>
+
+<dl><dt><a name="MonsoonProfiler-WillCloseBrowser"><strong>WillCloseBrowser</strong></a>(cls, browser_backend, platform_backend)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Called&nbsp;before&nbsp;the&nbsp;browser&nbsp;is&nbsp;stopped.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.netlog_profiler.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.netlog_profiler.html
new file mode 100644
index 0000000..60b7031
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.netlog_profiler.html
@@ -0,0 +1,82 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.profiler.netlog_profiler</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.<a href="telemetry.internal.platform.profiler.html"><font color="#ffffff">profiler</font></a>.netlog_profiler</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/profiler/netlog_profiler.py">telemetry/internal/platform/profiler/netlog_profiler.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.profiler.html">telemetry.internal.platform.profiler</a><br>
+</td><td width="25%" valign=top><a href="tempfile.html">tempfile</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.profiler.netlog_profiler.html#NetLogProfiler">NetLogProfiler</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="NetLogProfiler">class <strong>NetLogProfiler</strong></a>(<a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.profiler.netlog_profiler.html#NetLogProfiler">NetLogProfiler</a></dd>
+<dd><a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="NetLogProfiler-CollectProfile"><strong>CollectProfile</strong></a>(self)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="NetLogProfiler-CustomizeBrowserOptions"><strong>CustomizeBrowserOptions</strong></a>(cls, browser_type, options)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="NetLogProfiler-is_supported"><strong>is_supported</strong></a>(cls, browser_type)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="NetLogProfiler-name"><strong>name</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>:<br>
+<dl><dt><a name="NetLogProfiler-__init__"><strong>__init__</strong></a>(self, browser_backend, platform_backend, output_path, state)</dt></dl>
+
+<hr>
+Class methods inherited from <a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>:<br>
+<dl><dt><a name="NetLogProfiler-WillCloseBrowser"><strong>WillCloseBrowser</strong></a>(cls, browser_backend, platform_backend)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Called&nbsp;before&nbsp;the&nbsp;browser&nbsp;is&nbsp;stopped.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.oomkiller_profiler.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.oomkiller_profiler.html
new file mode 100644
index 0000000..98adf55
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.oomkiller_profiler.html
@@ -0,0 +1,151 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.profiler.oomkiller_profiler</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.<a href="telemetry.internal.platform.profiler.html"><font color="#ffffff">profiler</font></a>.oomkiller_profiler</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/profiler/oomkiller_profiler.py">telemetry/internal/platform/profiler/oomkiller_profiler.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.backends.chrome.android_browser_finder.html">telemetry.internal.backends.chrome.android_browser_finder</a><br>
+<a href="telemetry.internal.util.binary_manager.html">telemetry.internal.util.binary_manager</a><br>
+</td><td width="25%" valign=top><a href="devil.android.sdk.intent.html">devil.android.sdk.intent</a><br>
+<a href="os.html">os</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.profiler.html">telemetry.internal.platform.profiler</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="exceptions.html#Exception">exceptions.Exception</a>(<a href="exceptions.html#BaseException">exceptions.BaseException</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.profiler.oomkiller_profiler.html#UnableToFindApplicationException">UnableToFindApplicationException</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.profiler.oomkiller_profiler.html#OOMKillerProfiler">OOMKillerProfiler</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="OOMKillerProfiler">class <strong>OOMKillerProfiler</strong></a>(<a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Android-specific,&nbsp;Launch&nbsp;the&nbsp;music&nbsp;application&nbsp;and&nbsp;check&nbsp;it&nbsp;is&nbsp;still&nbsp;alive<br>
+at&nbsp;the&nbsp;end&nbsp;of&nbsp;the&nbsp;run.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.profiler.oomkiller_profiler.html#OOMKillerProfiler">OOMKillerProfiler</a></dd>
+<dd><a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="OOMKillerProfiler-CollectProfile"><strong>CollectProfile</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="OOMKillerProfiler-__init__"><strong>__init__</strong></a>(self, browser_backend, platform_backend, output_path, state)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="OOMKillerProfiler-WillCloseBrowser"><strong>WillCloseBrowser</strong></a>(cls, browser_backend, platform_backend)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="OOMKillerProfiler-is_supported"><strong>is_supported</strong></a>(cls, browser_type)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="OOMKillerProfiler-name"><strong>name</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Class methods inherited from <a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>:<br>
+<dl><dt><a name="OOMKillerProfiler-CustomizeBrowserOptions"><strong>CustomizeBrowserOptions</strong></a>(cls, browser_type, options)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Override&nbsp;to&nbsp;customize&nbsp;the&nbsp;Browser's&nbsp;options&nbsp;before&nbsp;it&nbsp;is&nbsp;created.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="UnableToFindApplicationException">class <strong>UnableToFindApplicationException</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt><a href="exceptions.html#Exception">Exception</a>&nbsp;when&nbsp;unable&nbsp;to&nbsp;find&nbsp;a&nbsp;launched&nbsp;application<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.profiler.oomkiller_profiler.html#UnableToFindApplicationException">UnableToFindApplicationException</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="UnableToFindApplicationException-__init__"><strong>__init__</strong></a>(self, application)</dt></dl>
+
+<dl><dt><a name="UnableToFindApplicationException-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#UnableToFindApplicationException-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;object&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="UnableToFindApplicationException-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#UnableToFindApplicationException-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="UnableToFindApplicationException-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#UnableToFindApplicationException-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="UnableToFindApplicationException-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#UnableToFindApplicationException-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="UnableToFindApplicationException-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#UnableToFindApplicationException-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="UnableToFindApplicationException-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="UnableToFindApplicationException-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#UnableToFindApplicationException-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="UnableToFindApplicationException-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#UnableToFindApplicationException-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="UnableToFindApplicationException-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="UnableToFindApplicationException-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.perf_profiler.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.perf_profiler.html
new file mode 100644
index 0000000..0cea1c7
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.perf_profiler.html
@@ -0,0 +1,93 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.profiler.perf_profiler</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.<a href="telemetry.internal.platform.profiler.html"><font color="#ffffff">profiler</font></a>.perf_profiler</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/profiler/perf_profiler.py">telemetry/internal/platform/profiler/perf_profiler.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.platform.profiler.android_profiling_helper.html">telemetry.internal.platform.profiler.android_profiling_helper</a><br>
+<a href="telemetry.internal.util.binary_manager.html">telemetry.internal.util.binary_manager</a><br>
+<a href="devil.android.device_errors.html">devil.android.device_errors</a><br>
+<a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top><a href="os.html">os</a><br>
+<a href="devil.android.perf.perf_control.html">devil.android.perf.perf_control</a><br>
+<a href="telemetry.core.platform.html">telemetry.core.platform</a><br>
+<a href="telemetry.internal.platform.profiler.html">telemetry.internal.platform.profiler</a><br>
+</td><td width="25%" valign=top><a href="re.html">re</a><br>
+<a href="signal.html">signal</a><br>
+<a href="subprocess.html">subprocess</a><br>
+<a href="sys.html">sys</a><br>
+</td><td width="25%" valign=top><a href="tempfile.html">tempfile</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.profiler.perf_profiler.html#PerfProfiler">PerfProfiler</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="PerfProfiler">class <strong>PerfProfiler</strong></a>(<a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.profiler.perf_profiler.html#PerfProfiler">PerfProfiler</a></dd>
+<dd><a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="PerfProfiler-CollectProfile"><strong>CollectProfile</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PerfProfiler-__init__"><strong>__init__</strong></a>(self, browser_backend, platform_backend, output_path, state)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="PerfProfiler-CustomizeBrowserOptions"><strong>CustomizeBrowserOptions</strong></a>(cls, browser_type, options)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="PerfProfiler-GetTopSamples"><strong>GetTopSamples</strong></a>(cls, file_name, number)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Parses&nbsp;the&nbsp;perf&nbsp;generated&nbsp;profile&nbsp;in&nbsp;|file_name|&nbsp;and&nbsp;returns&nbsp;a<br>
+{function:&nbsp;period}&nbsp;dict&nbsp;of&nbsp;the&nbsp;|number|&nbsp;hottests&nbsp;functions.</tt></dd></dl>
+
+<dl><dt><a name="PerfProfiler-is_supported"><strong>is_supported</strong></a>(cls, browser_type)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="PerfProfiler-name"><strong>name</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Class methods inherited from <a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>:<br>
+<dl><dt><a name="PerfProfiler-WillCloseBrowser"><strong>WillCloseBrowser</strong></a>(cls, browser_backend, platform_backend)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Called&nbsp;before&nbsp;the&nbsp;browser&nbsp;is&nbsp;stopped.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.profiler_finder.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.profiler_finder.html
new file mode 100644
index 0000000..ba02cbf
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.profiler_finder.html
@@ -0,0 +1,37 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.profiler.profiler_finder</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.<a href="telemetry.internal.platform.profiler.html"><font color="#ffffff">profiler</font></a>.profiler_finder</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/profiler/profiler_finder.py">telemetry/internal/platform/profiler/profiler_finder.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.core.discover.html">telemetry.core.discover</a><br>
+</td><td width="25%" valign=top><a href="os.html">os</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.profiler.html">telemetry.internal.platform.profiler</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.util.html">telemetry.core.util</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-FindProfiler"><strong>FindProfiler</strong></a>(name)</dt></dl>
+ <dl><dt><a name="-GetAllAvailableProfilers"><strong>GetAllAvailableProfilers</strong></a>()</dt></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.sample_profiler.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.sample_profiler.html
new file mode 100644
index 0000000..f6823e4
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.sample_profiler.html
@@ -0,0 +1,84 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.profiler.sample_profiler</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.<a href="telemetry.internal.platform.profiler.html"><font color="#ffffff">profiler</font></a>.sample_profiler</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/profiler/sample_profiler.py">telemetry/internal/platform/profiler/sample_profiler.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+<a href="telemetry.internal.platform.profiler.html">telemetry.internal.platform.profiler</a><br>
+</td><td width="25%" valign=top><a href="signal.html">signal</a><br>
+<a href="subprocess.html">subprocess</a><br>
+</td><td width="25%" valign=top><a href="sys.html">sys</a><br>
+<a href="tempfile.html">tempfile</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.util.html">telemetry.core.util</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.profiler.sample_profiler.html#SampleProfiler">SampleProfiler</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="SampleProfiler">class <strong>SampleProfiler</strong></a>(<a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.profiler.sample_profiler.html#SampleProfiler">SampleProfiler</a></dd>
+<dd><a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="SampleProfiler-CollectProfile"><strong>CollectProfile</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="SampleProfiler-__init__"><strong>__init__</strong></a>(self, browser_backend, platform_backend, output_path, state)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="SampleProfiler-is_supported"><strong>is_supported</strong></a>(cls, browser_type)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="SampleProfiler-name"><strong>name</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Class methods inherited from <a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>:<br>
+<dl><dt><a name="SampleProfiler-CustomizeBrowserOptions"><strong>CustomizeBrowserOptions</strong></a>(cls, browser_type, options)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Override&nbsp;to&nbsp;customize&nbsp;the&nbsp;Browser's&nbsp;options&nbsp;before&nbsp;it&nbsp;is&nbsp;created.</tt></dd></dl>
+
+<dl><dt><a name="SampleProfiler-WillCloseBrowser"><strong>WillCloseBrowser</strong></a>(cls, browser_backend, platform_backend)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Called&nbsp;before&nbsp;the&nbsp;browser&nbsp;is&nbsp;stopped.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.strace_profiler.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.strace_profiler.html
new file mode 100644
index 0000000..117ee86
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.strace_profiler.html
@@ -0,0 +1,87 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.profiler.strace_profiler</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.<a href="telemetry.internal.platform.profiler.html"><font color="#ffffff">profiler</font></a>.strace_profiler</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/profiler/strace_profiler.py">telemetry/internal/platform/profiler/strace_profiler.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="json.html">json</a><br>
+<a href="logging.html">logging</a><br>
+<a href="telemetry.timeline.model.html">telemetry.timeline.model</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.profiler.html">telemetry.internal.platform.profiler</a><br>
+<a href="re.html">re</a><br>
+<a href="signal.html">signal</a><br>
+</td><td width="25%" valign=top><a href="subprocess.html">subprocess</a><br>
+<a href="sys.html">sys</a><br>
+<a href="tempfile.html">tempfile</a><br>
+</td><td width="25%" valign=top><a href="telemetry.timeline.trace_data.html">telemetry.timeline.trace_data</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.profiler.strace_profiler.html#StraceProfiler">StraceProfiler</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="StraceProfiler">class <strong>StraceProfiler</strong></a>(<a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.profiler.strace_profiler.html#StraceProfiler">StraceProfiler</a></dd>
+<dd><a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="StraceProfiler-CollectProfile"><strong>CollectProfile</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="StraceProfiler-__init__"><strong>__init__</strong></a>(self, browser_backend, platform_backend, output_path, state)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="StraceProfiler-CustomizeBrowserOptions"><strong>CustomizeBrowserOptions</strong></a>(cls, browser_type, options)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="StraceProfiler-is_supported"><strong>is_supported</strong></a>(cls, browser_type)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="StraceProfiler-name"><strong>name</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Class methods inherited from <a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>:<br>
+<dl><dt><a name="StraceProfiler-WillCloseBrowser"><strong>WillCloseBrowser</strong></a>(cls, browser_backend, platform_backend)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Called&nbsp;before&nbsp;the&nbsp;browser&nbsp;is&nbsp;stopped.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.tcmalloc_heap_profiler.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.tcmalloc_heap_profiler.html
new file mode 100644
index 0000000..bb392e1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.tcmalloc_heap_profiler.html
@@ -0,0 +1,82 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.profiler.tcmalloc_heap_profiler</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.<a href="telemetry.internal.platform.profiler.html"><font color="#ffffff">profiler</font></a>.tcmalloc_heap_profiler</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/profiler/tcmalloc_heap_profiler.py">telemetry/internal/platform/profiler/tcmalloc_heap_profiler.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.backends.chrome.android_browser_finder.html">telemetry.internal.backends.chrome.android_browser_finder</a><br>
+<a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top><a href="os.html">os</a><br>
+<a href="telemetry.internal.platform.profiler.html">telemetry.internal.platform.profiler</a><br>
+</td><td width="25%" valign=top><a href="sys.html">sys</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.profiler.tcmalloc_heap_profiler.html#TCMallocHeapProfiler">TCMallocHeapProfiler</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TCMallocHeapProfiler">class <strong>TCMallocHeapProfiler</strong></a>(<a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;Factory&nbsp;to&nbsp;instantiate&nbsp;the&nbsp;platform-specific&nbsp;profiler.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.profiler.tcmalloc_heap_profiler.html#TCMallocHeapProfiler">TCMallocHeapProfiler</a></dd>
+<dd><a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="TCMallocHeapProfiler-CollectProfile"><strong>CollectProfile</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TCMallocHeapProfiler-__init__"><strong>__init__</strong></a>(self, browser_backend, platform_backend, output_path, state)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="TCMallocHeapProfiler-CustomizeBrowserOptions"><strong>CustomizeBrowserOptions</strong></a>(cls, browser_type, options)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="TCMallocHeapProfiler-WillCloseBrowser"><strong>WillCloseBrowser</strong></a>(cls, browser_backend, platform_backend)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="TCMallocHeapProfiler-is_supported"><strong>is_supported</strong></a>(cls, browser_type)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="TCMallocHeapProfiler-name"><strong>name</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.tcpdump_profiler.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.tcpdump_profiler.html
new file mode 100644
index 0000000..4b0f44c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.tcpdump_profiler.html
@@ -0,0 +1,87 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.profiler.tcpdump_profiler</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.<a href="telemetry.internal.platform.profiler.html"><font color="#ffffff">profiler</font></a>.tcpdump_profiler</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/profiler/tcpdump_profiler.py">telemetry/internal/platform/profiler/tcpdump_profiler.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.platform.profiler.android_prebuilt_profiler_helper.html">telemetry.internal.platform.profiler.android_prebuilt_profiler_helper</a><br>
+<a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top><a href="os.html">os</a><br>
+<a href="telemetry.internal.platform.profiler.html">telemetry.internal.platform.profiler</a><br>
+</td><td width="25%" valign=top><a href="signal.html">signal</a><br>
+<a href="subprocess.html">subprocess</a><br>
+</td><td width="25%" valign=top><a href="sys.html">sys</a><br>
+<a href="tempfile.html">tempfile</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.profiler.tcpdump_profiler.html#TCPDumpProfiler">TCPDumpProfiler</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TCPDumpProfiler">class <strong>TCPDumpProfiler</strong></a>(<a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;Factory&nbsp;to&nbsp;instantiate&nbsp;the&nbsp;platform-specific&nbsp;profiler.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.profiler.tcpdump_profiler.html#TCPDumpProfiler">TCPDumpProfiler</a></dd>
+<dd><a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="TCPDumpProfiler-CollectProfile"><strong>CollectProfile</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TCPDumpProfiler-__init__"><strong>__init__</strong></a>(self, browser_backend, platform_backend, output_path, state)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="TCPDumpProfiler-is_supported"><strong>is_supported</strong></a>(cls, browser_type)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="TCPDumpProfiler-name"><strong>name</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Class methods inherited from <a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>:<br>
+<dl><dt><a name="TCPDumpProfiler-CustomizeBrowserOptions"><strong>CustomizeBrowserOptions</strong></a>(cls, browser_type, options)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Override&nbsp;to&nbsp;customize&nbsp;the&nbsp;Browser's&nbsp;options&nbsp;before&nbsp;it&nbsp;is&nbsp;created.</tt></dd></dl>
+
+<dl><dt><a name="TCPDumpProfiler-WillCloseBrowser"><strong>WillCloseBrowser</strong></a>(cls, browser_backend, platform_backend)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Called&nbsp;before&nbsp;the&nbsp;browser&nbsp;is&nbsp;stopped.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.trace_profiler.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.trace_profiler.html
new file mode 100644
index 0000000..38b36e0
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.trace_profiler.html
@@ -0,0 +1,175 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.profiler.trace_profiler</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.<a href="telemetry.internal.platform.profiler.html"><font color="#ffffff">profiler</font></a>.trace_profiler</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/profiler/trace_profiler.py">telemetry/internal/platform/profiler/trace_profiler.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="StringIO.html">StringIO</a><br>
+<a href="os.html">os</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.profiler.html">telemetry.internal.platform.profiler</a><br>
+<a href="telemetry.timeline.trace_data.html">telemetry.timeline.trace_data</a><br>
+</td><td width="25%" valign=top><a href="telemetry.timeline.tracing_options.html">telemetry.timeline.tracing_options</a><br>
+<a href="zipfile.html">zipfile</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.profiler.trace_profiler.html#TraceProfiler">TraceProfiler</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.profiler.trace_profiler.html#TraceAllProfiler">TraceAllProfiler</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.internal.platform.profiler.trace_profiler.html#TraceDetailedProfiler">TraceDetailedProfiler</a>
+</font></dt></dl>
+</dd>
+</dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TraceAllProfiler">class <strong>TraceAllProfiler</strong></a>(<a href="telemetry.internal.platform.profiler.trace_profiler.html#TraceProfiler">TraceProfiler</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.profiler.trace_profiler.html#TraceAllProfiler">TraceAllProfiler</a></dd>
+<dd><a href="telemetry.internal.platform.profiler.trace_profiler.html#TraceProfiler">TraceProfiler</a></dd>
+<dd><a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="TraceAllProfiler-__init__"><strong>__init__</strong></a>(self, browser_backend, platform_backend, output_path, state)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="TraceAllProfiler-name"><strong>name</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.platform.profiler.trace_profiler.html#TraceProfiler">TraceProfiler</a>:<br>
+<dl><dt><a name="TraceAllProfiler-CollectProfile"><strong>CollectProfile</strong></a>(self)</dt></dl>
+
+<hr>
+Class methods inherited from <a href="telemetry.internal.platform.profiler.trace_profiler.html#TraceProfiler">TraceProfiler</a>:<br>
+<dl><dt><a name="TraceAllProfiler-is_supported"><strong>is_supported</strong></a>(cls, browser_type)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Class methods inherited from <a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>:<br>
+<dl><dt><a name="TraceAllProfiler-CustomizeBrowserOptions"><strong>CustomizeBrowserOptions</strong></a>(cls, browser_type, options)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Override&nbsp;to&nbsp;customize&nbsp;the&nbsp;Browser's&nbsp;options&nbsp;before&nbsp;it&nbsp;is&nbsp;created.</tt></dd></dl>
+
+<dl><dt><a name="TraceAllProfiler-WillCloseBrowser"><strong>WillCloseBrowser</strong></a>(cls, browser_backend, platform_backend)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Called&nbsp;before&nbsp;the&nbsp;browser&nbsp;is&nbsp;stopped.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TraceDetailedProfiler">class <strong>TraceDetailedProfiler</strong></a>(<a href="telemetry.internal.platform.profiler.trace_profiler.html#TraceProfiler">TraceProfiler</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.profiler.trace_profiler.html#TraceDetailedProfiler">TraceDetailedProfiler</a></dd>
+<dd><a href="telemetry.internal.platform.profiler.trace_profiler.html#TraceProfiler">TraceProfiler</a></dd>
+<dd><a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="TraceDetailedProfiler-__init__"><strong>__init__</strong></a>(self, browser_backend, platform_backend, output_path, state)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="TraceDetailedProfiler-name"><strong>name</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.platform.profiler.trace_profiler.html#TraceProfiler">TraceProfiler</a>:<br>
+<dl><dt><a name="TraceDetailedProfiler-CollectProfile"><strong>CollectProfile</strong></a>(self)</dt></dl>
+
+<hr>
+Class methods inherited from <a href="telemetry.internal.platform.profiler.trace_profiler.html#TraceProfiler">TraceProfiler</a>:<br>
+<dl><dt><a name="TraceDetailedProfiler-is_supported"><strong>is_supported</strong></a>(cls, browser_type)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Class methods inherited from <a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>:<br>
+<dl><dt><a name="TraceDetailedProfiler-CustomizeBrowserOptions"><strong>CustomizeBrowserOptions</strong></a>(cls, browser_type, options)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Override&nbsp;to&nbsp;customize&nbsp;the&nbsp;Browser's&nbsp;options&nbsp;before&nbsp;it&nbsp;is&nbsp;created.</tt></dd></dl>
+
+<dl><dt><a name="TraceDetailedProfiler-WillCloseBrowser"><strong>WillCloseBrowser</strong></a>(cls, browser_backend, platform_backend)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Called&nbsp;before&nbsp;the&nbsp;browser&nbsp;is&nbsp;stopped.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TraceProfiler">class <strong>TraceProfiler</strong></a>(<a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.profiler.trace_profiler.html#TraceProfiler">TraceProfiler</a></dd>
+<dd><a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="TraceProfiler-CollectProfile"><strong>CollectProfile</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TraceProfiler-__init__"><strong>__init__</strong></a>(self, browser_backend, platform_backend, output_path, state, categories<font color="#909090">=None</font>)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="TraceProfiler-is_supported"><strong>is_supported</strong></a>(cls, browser_type)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="TraceProfiler-name"><strong>name</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Class methods inherited from <a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>:<br>
+<dl><dt><a name="TraceProfiler-CustomizeBrowserOptions"><strong>CustomizeBrowserOptions</strong></a>(cls, browser_type, options)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Override&nbsp;to&nbsp;customize&nbsp;the&nbsp;Browser's&nbsp;options&nbsp;before&nbsp;it&nbsp;is&nbsp;created.</tt></dd></dl>
+
+<dl><dt><a name="TraceProfiler-WillCloseBrowser"><strong>WillCloseBrowser</strong></a>(cls, browser_backend, platform_backend)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Called&nbsp;before&nbsp;the&nbsp;browser&nbsp;is&nbsp;stopped.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.v8_profiler.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.v8_profiler.html
new file mode 100644
index 0000000..ff563d4
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.v8_profiler.html
@@ -0,0 +1,83 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.profiler.v8_profiler</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.<a href="telemetry.internal.platform.profiler.html"><font color="#ffffff">profiler</font></a>.v8_profiler</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/profiler/v8_profiler.py">telemetry/internal/platform/profiler/v8_profiler.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.profiler.html">telemetry.internal.platform.profiler</a><br>
+</td><td width="25%" valign=top><a href="re.html">re</a><br>
+</td><td width="25%" valign=top><a href="tempfile.html">tempfile</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.profiler.v8_profiler.html#V8Profiler">V8Profiler</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="V8Profiler">class <strong>V8Profiler</strong></a>(<a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.profiler.v8_profiler.html#V8Profiler">V8Profiler</a></dd>
+<dd><a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="V8Profiler-CollectProfile"><strong>CollectProfile</strong></a>(self)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="V8Profiler-CustomizeBrowserOptions"><strong>CustomizeBrowserOptions</strong></a>(cls, browser_type, options)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="V8Profiler-is_supported"><strong>is_supported</strong></a>(cls, browser_type)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="V8Profiler-name"><strong>name</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>:<br>
+<dl><dt><a name="V8Profiler-__init__"><strong>__init__</strong></a>(self, browser_backend, platform_backend, output_path, state)</dt></dl>
+
+<hr>
+Class methods inherited from <a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>:<br>
+<dl><dt><a name="V8Profiler-WillCloseBrowser"><strong>WillCloseBrowser</strong></a>(cls, browser_backend, platform_backend)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Called&nbsp;before&nbsp;the&nbsp;browser&nbsp;is&nbsp;stopped.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.vtune_profiler.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.vtune_profiler.html
new file mode 100644
index 0000000..ebafcb6
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.vtune_profiler.html
@@ -0,0 +1,85 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.profiler.vtune_profiler</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.<a href="telemetry.internal.platform.profiler.html"><font color="#ffffff">profiler</font></a>.vtune_profiler</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/profiler/vtune_profiler.py">telemetry/internal/platform/profiler/vtune_profiler.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.platform.profiler.android_profiling_helper.html">telemetry.internal.platform.profiler.android_profiling_helper</a><br>
+<a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+</td><td width="25%" valign=top><a href="logging.html">logging</a><br>
+<a href="os.html">os</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.profiler.html">telemetry.internal.platform.profiler</a><br>
+<a href="subprocess.html">subprocess</a><br>
+</td><td width="25%" valign=top><a href="sys.html">sys</a><br>
+<a href="tempfile.html">tempfile</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.profiler.vtune_profiler.html#VTuneProfiler">VTuneProfiler</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="VTuneProfiler">class <strong>VTuneProfiler</strong></a>(<a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.profiler.vtune_profiler.html#VTuneProfiler">VTuneProfiler</a></dd>
+<dd><a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="VTuneProfiler-CollectProfile"><strong>CollectProfile</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="VTuneProfiler-__init__"><strong>__init__</strong></a>(self, browser_backend, platform_backend, output_path, state)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="VTuneProfiler-CustomizeBrowserOptions"><strong>CustomizeBrowserOptions</strong></a>(cls, browser_type, options)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="VTuneProfiler-is_supported"><strong>is_supported</strong></a>(cls, browser_type)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="VTuneProfiler-name"><strong>name</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Class methods inherited from <a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>:<br>
+<dl><dt><a name="VTuneProfiler-WillCloseBrowser"><strong>WillCloseBrowser</strong></a>(cls, browser_backend, platform_backend)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Called&nbsp;before&nbsp;the&nbsp;browser&nbsp;is&nbsp;stopped.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.win_pgo_profiler.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.win_pgo_profiler.html
new file mode 100644
index 0000000..09063e4
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiler.win_pgo_profiler.html
@@ -0,0 +1,85 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.profiler.win_pgo_profiler</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.<a href="telemetry.internal.platform.profiler.html"><font color="#ffffff">profiler</font></a>.win_pgo_profiler</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/profiler/win_pgo_profiler.py">telemetry/internal/platform/profiler/win_pgo_profiler.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="glob.html">glob</a><br>
+<a href="os.html">os</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.profiler.html">telemetry.internal.platform.profiler</a><br>
+<a href="subprocess.html">subprocess</a><br>
+</td><td width="25%" valign=top><a href="sys.html">sys</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.profiler.win_pgo_profiler.html#WinPGOProfiler">WinPGOProfiler</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="WinPGOProfiler">class <strong>WinPGOProfiler</strong></a>(<a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;profiler&nbsp;that&nbsp;run&nbsp;the&nbsp;Visual&nbsp;Studio&nbsp;PGO&nbsp;utility&nbsp;'pgosweep.exe'&nbsp;before<br>
+terminating&nbsp;a&nbsp;browser&nbsp;or&nbsp;a&nbsp;renderer&nbsp;process.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.profiler.win_pgo_profiler.html#WinPGOProfiler">WinPGOProfiler</a></dd>
+<dd><a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="WinPGOProfiler-CollectProfile"><strong>CollectProfile</strong></a>(self)</dt><dd><tt>Collect&nbsp;the&nbsp;profile&nbsp;data&nbsp;for&nbsp;the&nbsp;current&nbsp;processes.</tt></dd></dl>
+
+<dl><dt><a name="WinPGOProfiler-__init__"><strong>__init__</strong></a>(self, browser_backend, platform_backend, output_path, state)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="WinPGOProfiler-CustomizeBrowserOptions"><strong>CustomizeBrowserOptions</strong></a>(cls, browser_type, options)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="WinPGOProfiler-is_supported"><strong>is_supported</strong></a>(cls, browser_type)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="WinPGOProfiler-name"><strong>name</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Class methods inherited from <a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>:<br>
+<dl><dt><a name="WinPGOProfiler-WillCloseBrowser"><strong>WillCloseBrowser</strong></a>(cls, browser_backend, platform_backend)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Called&nbsp;before&nbsp;the&nbsp;browser&nbsp;is&nbsp;stopped.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.profiler.html#Profiler">telemetry.internal.platform.profiler.Profiler</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiling_controller_backend.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiling_controller_backend.html
new file mode 100644
index 0000000..b0abf19
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.profiling_controller_backend.html
@@ -0,0 +1,68 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.profiling_controller_backend</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.profiling_controller_backend</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/profiling_controller_backend.py">telemetry/internal/platform/profiling_controller_backend.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.platform.profiler.profiler_finder.html">telemetry.internal.platform.profiler.profiler_finder</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.profiling_controller_backend.html#ProfilingControllerBackend">ProfilingControllerBackend</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ProfilingControllerBackend">class <strong>ProfilingControllerBackend</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="ProfilingControllerBackend-Start"><strong>Start</strong></a>(self, profiler_name, base_output_file)</dt><dd><tt>Starts&nbsp;profiling&nbsp;using&nbsp;|profiler_name|.&nbsp;Results&nbsp;are&nbsp;saved&nbsp;to<br>
+|base_output_file|.&lt;process_name&gt;.</tt></dd></dl>
+
+<dl><dt><a name="ProfilingControllerBackend-Stop"><strong>Stop</strong></a>(self)</dt><dd><tt>Stops&nbsp;all&nbsp;active&nbsp;profilers&nbsp;and&nbsp;saves&nbsp;their&nbsp;results.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;A&nbsp;list&nbsp;of&nbsp;filenames&nbsp;produced&nbsp;by&nbsp;the&nbsp;profiler.</tt></dd></dl>
+
+<dl><dt><a name="ProfilingControllerBackend-WillCloseBrowser"><strong>WillCloseBrowser</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="ProfilingControllerBackend-__init__"><strong>__init__</strong></a>(self, platform_backend, browser_backend)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.system_info.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.system_info.html
new file mode 100644
index 0000000..d20e988
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.system_info.html
@@ -0,0 +1,81 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.system_info</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.system_info</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/system_info.py">telemetry/internal/platform/system_info.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.platform.gpu_info.html">telemetry.internal.platform.gpu_info</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.system_info.html#SystemInfo">SystemInfo</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="SystemInfo">class <strong>SystemInfo</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Provides&nbsp;low-level&nbsp;system&nbsp;information.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="SystemInfo-__init__"><strong>__init__</strong></a>(self, model_name, gpu_dict)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="SystemInfo-FromDict"><strong>FromDict</strong></a>(cls, attrs)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Constructs&nbsp;a&nbsp;<a href="#SystemInfo">SystemInfo</a>&nbsp;from&nbsp;a&nbsp;dictionary&nbsp;of&nbsp;attributes.<br>
+Attributes&nbsp;currently&nbsp;required&nbsp;to&nbsp;be&nbsp;present&nbsp;in&nbsp;the&nbsp;dictionary:<br>
+&nbsp;<br>
+&nbsp;&nbsp;model_name&nbsp;(string):&nbsp;a&nbsp;platform-dependent&nbsp;string<br>
+&nbsp;&nbsp;&nbsp;&nbsp;describing&nbsp;the&nbsp;model&nbsp;of&nbsp;machine,&nbsp;or&nbsp;the&nbsp;empty&nbsp;string&nbsp;if&nbsp;not<br>
+&nbsp;&nbsp;&nbsp;&nbsp;supported.<br>
+&nbsp;&nbsp;gpu&nbsp;(<a href="__builtin__.html#object">object</a>&nbsp;containing&nbsp;GPUInfo's&nbsp;required&nbsp;attributes)</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>gpu</strong></dt>
+<dd><tt>A&nbsp;GPUInfo&nbsp;object&nbsp;describing&nbsp;the&nbsp;graphics&nbsp;processor(s)&nbsp;on&nbsp;the&nbsp;system.</tt></dd>
+</dl>
+<dl><dt><strong>model_name</strong></dt>
+<dd><tt>A&nbsp;string&nbsp;describing&nbsp;the&nbsp;machine&nbsp;model.<br>
+&nbsp;<br>
+This&nbsp;is&nbsp;a&nbsp;highly&nbsp;platform-dependent&nbsp;value&nbsp;and&nbsp;not&nbsp;currently<br>
+specified&nbsp;for&nbsp;any&nbsp;machine&nbsp;type&nbsp;aside&nbsp;from&nbsp;Macs.&nbsp;On&nbsp;Mac&nbsp;OS,&nbsp;this<br>
+is&nbsp;the&nbsp;model&nbsp;identifier,&nbsp;reformatted&nbsp;slightly;&nbsp;for&nbsp;example,<br>
+'MacBookPro&nbsp;10.1'.</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.tracing_agent.chrome_tracing_agent.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.tracing_agent.chrome_tracing_agent.html
new file mode 100644
index 0000000..150718c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.tracing_agent.chrome_tracing_agent.html
@@ -0,0 +1,211 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.tracing_agent.chrome_tracing_agent</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.<a href="telemetry.internal.platform.tracing_agent.html"><font color="#ffffff">tracing_agent</font></a>.chrome_tracing_agent</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/tracing_agent/chrome_tracing_agent.py">telemetry/internal/platform/tracing_agent/chrome_tracing_agent.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2015&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.platform.tracing_agent.chrome_tracing_devtools_manager.html">telemetry.internal.platform.tracing_agent.chrome_tracing_devtools_manager</a><br>
+<a href="os.html">os</a><br>
+<a href="shutil.html">shutil</a><br>
+</td><td width="25%" valign=top><a href="stat.html">stat</a><br>
+<a href="sys.html">sys</a><br>
+<a href="tempfile.html">tempfile</a><br>
+</td><td width="25%" valign=top><a href="traceback.html">traceback</a><br>
+<a href="telemetry.internal.platform.tracing_agent.html">telemetry.internal.platform.tracing_agent</a><br>
+<a href="telemetry.timeline.tracing_config.html">telemetry.timeline.tracing_config</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="exceptions.html#Exception">exceptions.Exception</a>(<a href="exceptions.html#BaseException">exceptions.BaseException</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.tracing_agent.chrome_tracing_agent.html#ChromeTracingStartedError">ChromeTracingStartedError</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.internal.platform.tracing_agent.chrome_tracing_agent.html#ChromeTracingStoppedError">ChromeTracingStoppedError</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.tracing_agent.html#TracingAgent">telemetry.internal.platform.tracing_agent.TracingAgent</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.tracing_agent.chrome_tracing_agent.html#ChromeTracingAgent">ChromeTracingAgent</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ChromeTracingAgent">class <strong>ChromeTracingAgent</strong></a>(<a href="telemetry.internal.platform.tracing_agent.html#TracingAgent">telemetry.internal.platform.tracing_agent.TracingAgent</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.tracing_agent.chrome_tracing_agent.html#ChromeTracingAgent">ChromeTracingAgent</a></dd>
+<dd><a href="telemetry.internal.platform.tracing_agent.html#TracingAgent">telemetry.internal.platform.tracing_agent.TracingAgent</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="ChromeTracingAgent-Start"><strong>Start</strong></a>(self, trace_options, category_filter, timeout)</dt></dl>
+
+<dl><dt><a name="ChromeTracingAgent-Stop"><strong>Stop</strong></a>(self, trace_data_builder)</dt></dl>
+
+<dl><dt><a name="ChromeTracingAgent-__init__"><strong>__init__</strong></a>(self, platform_backend)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="ChromeTracingAgent-IsStartupTracingSupported"><strong>IsStartupTracingSupported</strong></a>(cls, platform_backend)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="ChromeTracingAgent-IsSupported"><strong>IsSupported</strong></a>(cls, platform_backend)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>trace_config</strong></dt>
+</dl>
+<dl><dt><strong>trace_config_file</strong></dt>
+</dl>
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.tracing_agent.html#TracingAgent">telemetry.internal.platform.tracing_agent.TracingAgent</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ChromeTracingStartedError">class <strong>ChromeTracingStartedError</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.tracing_agent.chrome_tracing_agent.html#ChromeTracingStartedError">ChromeTracingStartedError</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="ChromeTracingStartedError-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#ChromeTracingStartedError-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#ChromeTracingStartedError-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;object&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="ChromeTracingStartedError-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#ChromeTracingStartedError-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="ChromeTracingStartedError-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#ChromeTracingStartedError-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="ChromeTracingStartedError-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#ChromeTracingStartedError-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="ChromeTracingStartedError-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#ChromeTracingStartedError-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="ChromeTracingStartedError-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="ChromeTracingStartedError-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#ChromeTracingStartedError-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="ChromeTracingStartedError-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#ChromeTracingStartedError-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="ChromeTracingStartedError-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="ChromeTracingStartedError-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#ChromeTracingStartedError-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="ChromeTracingStartedError-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ChromeTracingStoppedError">class <strong>ChromeTracingStoppedError</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.tracing_agent.chrome_tracing_agent.html#ChromeTracingStoppedError">ChromeTracingStoppedError</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="ChromeTracingStoppedError-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#ChromeTracingStoppedError-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#ChromeTracingStoppedError-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;object&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="ChromeTracingStoppedError-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#ChromeTracingStoppedError-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="ChromeTracingStoppedError-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#ChromeTracingStoppedError-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="ChromeTracingStoppedError-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#ChromeTracingStoppedError-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="ChromeTracingStoppedError-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#ChromeTracingStoppedError-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="ChromeTracingStoppedError-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="ChromeTracingStoppedError-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#ChromeTracingStoppedError-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="ChromeTracingStoppedError-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#ChromeTracingStoppedError-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="ChromeTracingStoppedError-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="ChromeTracingStoppedError-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#ChromeTracingStoppedError-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="ChromeTracingStoppedError-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.tracing_agent.chrome_tracing_devtools_manager.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.tracing_agent.chrome_tracing_devtools_manager.html
new file mode 100644
index 0000000..ef6e46e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.tracing_agent.chrome_tracing_devtools_manager.html
@@ -0,0 +1,30 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.tracing_agent.chrome_tracing_devtools_manager</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.<a href="telemetry.internal.platform.tracing_agent.html"><font color="#ffffff">tracing_agent</font></a>.chrome_tracing_devtools_manager</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/tracing_agent/chrome_tracing_devtools_manager.py">telemetry/internal/platform/tracing_agent/chrome_tracing_devtools_manager.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2015&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-GetActiveDevToolsClients"><strong>GetActiveDevToolsClients</strong></a>(platform_backend)</dt><dd><tt>Get&nbsp;DevTools&nbsp;clients&nbsp;that&nbsp;are&nbsp;still&nbsp;connectable.</tt></dd></dl>
+ <dl><dt><a name="-GetDevToolsClients"><strong>GetDevToolsClients</strong></a>(platform_backend)</dt><dd><tt>Get&nbsp;DevTools&nbsp;clients&nbsp;including&nbsp;the&nbsp;ones&nbsp;that&nbsp;are&nbsp;no&nbsp;longer&nbsp;connectable.</tt></dd></dl>
+ <dl><dt><a name="-IsSupported"><strong>IsSupported</strong></a>(platform_backend)</dt></dl>
+ <dl><dt><a name="-RegisterDevToolsClient"><strong>RegisterDevToolsClient</strong></a>(devtools_client_backend, platform_backend)</dt><dd><tt>Register&nbsp;DevTools&nbsp;client<br>
+&nbsp;<br>
+This&nbsp;should&nbsp;only&nbsp;be&nbsp;called&nbsp;from&nbsp;DevToolsClientBackend&nbsp;when&nbsp;it&nbsp;is&nbsp;initialized.</tt></dd></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.tracing_agent.display_tracing_agent.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.tracing_agent.display_tracing_agent.html
new file mode 100644
index 0000000..1707a83
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.tracing_agent.display_tracing_agent.html
@@ -0,0 +1,73 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.tracing_agent.display_tracing_agent</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.<a href="telemetry.internal.platform.tracing_agent.html"><font color="#ffffff">tracing_agent</font></a>.display_tracing_agent</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/tracing_agent/display_tracing_agent.py">telemetry/internal/platform/tracing_agent/display_tracing_agent.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.timeline.trace_data.html">telemetry.timeline.trace_data</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.tracing_agent.html">telemetry.internal.platform.tracing_agent</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.tracing_agent.html#TracingAgent">telemetry.internal.platform.tracing_agent.TracingAgent</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.tracing_agent.display_tracing_agent.html#DisplayTracingAgent">DisplayTracingAgent</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="DisplayTracingAgent">class <strong>DisplayTracingAgent</strong></a>(<a href="telemetry.internal.platform.tracing_agent.html#TracingAgent">telemetry.internal.platform.tracing_agent.TracingAgent</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.tracing_agent.display_tracing_agent.html#DisplayTracingAgent">DisplayTracingAgent</a></dd>
+<dd><a href="telemetry.internal.platform.tracing_agent.html#TracingAgent">telemetry.internal.platform.tracing_agent.TracingAgent</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="DisplayTracingAgent-Start"><strong>Start</strong></a>(self, trace_options, category_filter, _timeout)</dt></dl>
+
+<dl><dt><a name="DisplayTracingAgent-Stop"><strong>Stop</strong></a>(self, trace_data_builder)</dt></dl>
+
+<dl><dt><a name="DisplayTracingAgent-__init__"><strong>__init__</strong></a>(self, platform_backend)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="DisplayTracingAgent-IsSupported"><strong>IsSupported</strong></a>(cls, platform_backend)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.tracing_agent.html#TracingAgent">telemetry.internal.platform.tracing_agent.TracingAgent</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.tracing_agent.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.tracing_agent.html
new file mode 100644
index 0000000..dbf6213
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.tracing_agent.html
@@ -0,0 +1,96 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: package telemetry.internal.platform.tracing_agent</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.tracing_agent</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/tracing_agent/__init__.py">telemetry/internal/platform/tracing_agent/__init__.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Package Contents</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.platform.tracing_agent.chrome_tracing_agent.html">chrome_tracing_agent</a><br>
+<a href="telemetry.internal.platform.tracing_agent.chrome_tracing_agent_unittest.html">chrome_tracing_agent_unittest</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.tracing_agent.chrome_tracing_devtools_manager.html">chrome_tracing_devtools_manager</a><br>
+<a href="telemetry.internal.platform.tracing_agent.display_tracing_agent.html">display_tracing_agent</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.tracing_agent.display_tracing_agent_unittest.html">display_tracing_agent_unittest</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.tracing_agent.html#TracingAgent">TracingAgent</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TracingAgent">class <strong>TracingAgent</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;tracing&nbsp;agent&nbsp;provided&nbsp;by&nbsp;the&nbsp;platform.<br>
+&nbsp;<br>
+A&nbsp;tracing&nbsp;agent&nbsp;can&nbsp;gather&nbsp;data&nbsp;with&nbsp;<a href="#TracingAgent-Start">Start</a>()&nbsp;until&nbsp;<a href="#TracingAgent-Stop">Stop</a>().<br>
+Before&nbsp;constructing&nbsp;an&nbsp;<a href="#TracingAgent">TracingAgent</a>,&nbsp;check&nbsp;whether&nbsp;it's&nbsp;supported&nbsp;on&nbsp;the<br>
+platform&nbsp;with&nbsp;IsSupported&nbsp;method&nbsp;first.<br>
+&nbsp;<br>
+NOTE:&nbsp;All&nbsp;subclasses&nbsp;of&nbsp;<a href="#TracingAgent">TracingAgent</a>&nbsp;must&nbsp;not&nbsp;change&nbsp;the&nbsp;constructor's<br>
+parameters&nbsp;so&nbsp;the&nbsp;agents&nbsp;can&nbsp;be&nbsp;dynamically&nbsp;constructed&nbsp;in<br>
+tracing_controller_backend.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="TracingAgent-Start"><strong>Start</strong></a>(self, trace_options, category_filter, timeout)</dt><dd><tt>Override&nbsp;to&nbsp;add&nbsp;tracing&nbsp;agent's&nbsp;custom&nbsp;logic&nbsp;to&nbsp;start&nbsp;tracing.<br>
+&nbsp;<br>
+Depending&nbsp;on&nbsp;trace_options&nbsp;and&nbsp;category_filter,&nbsp;the&nbsp;tracing&nbsp;agent&nbsp;may&nbsp;choose<br>
+to&nbsp;start&nbsp;or&nbsp;not&nbsp;start&nbsp;tracing.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;trace_options:&nbsp;an&nbsp;instance&nbsp;of&nbsp;tracing_options.TracingOptions&nbsp;that<br>
+&nbsp;&nbsp;&nbsp;&nbsp;control&nbsp;which&nbsp;core&nbsp;tracing&nbsp;systems&nbsp;should&nbsp;be&nbsp;enabled.<br>
+&nbsp;&nbsp;category_filter:&nbsp;an&nbsp;instance&nbsp;of<br>
+&nbsp;&nbsp;&nbsp;&nbsp;tracing_category_filter.TracingCategoryFilter<br>
+&nbsp;&nbsp;timeout:&nbsp;number&nbsp;of&nbsp;seconds&nbsp;that&nbsp;this&nbsp;tracing&nbsp;agent&nbsp;should&nbsp;try&nbsp;to&nbsp;start<br>
+&nbsp;&nbsp;&nbsp;&nbsp;tracing&nbsp;until&nbsp;time&nbsp;out.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;True&nbsp;if&nbsp;tracing&nbsp;agent&nbsp;started&nbsp;succesfully.</tt></dd></dl>
+
+<dl><dt><a name="TracingAgent-Stop"><strong>Stop</strong></a>(self, trace_data_builder)</dt><dd><tt>Override&nbsp;to&nbsp;add&nbsp;tracing&nbsp;agent's&nbsp;custom&nbsp;logic&nbsp;to&nbsp;stop&nbsp;tracing.<br>
+&nbsp;<br>
+<a href="#TracingAgent-Stop">Stop</a>()&nbsp;should&nbsp;guarantee&nbsp;tracing&nbsp;is&nbsp;stopped,&nbsp;even&nbsp;if&nbsp;there&nbsp;may&nbsp;be&nbsp;exception.</tt></dd></dl>
+
+<dl><dt><a name="TracingAgent-__init__"><strong>__init__</strong></a>(self, platform_backend)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="TracingAgent-IsSupported"><strong>IsSupported</strong></a>(cls, _platform_backend)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.tracing_controller_backend.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.tracing_controller_backend.html
new file mode 100644
index 0000000..1059365
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.tracing_controller_backend.html
@@ -0,0 +1,143 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.tracing_controller_backend</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.tracing_controller_backend</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/tracing_controller_backend.py">telemetry/internal/platform/tracing_controller_backend.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.platform.tracing_agent.chrome_tracing_agent.html">telemetry.internal.platform.tracing_agent.chrome_tracing_agent</a><br>
+<a href="telemetry.core.discover.html">telemetry.core.discover</a><br>
+<a href="os.html">os</a><br>
+</td><td width="25%" valign=top><a href="sys.html">sys</a><br>
+<a href="telemetry.timeline.trace_data.html">telemetry.timeline.trace_data</a><br>
+<a href="traceback.html">traceback</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.tracing_agent.html">telemetry.internal.platform.tracing_agent</a><br>
+<a href="telemetry.timeline.tracing_category_filter.html">telemetry.timeline.tracing_category_filter</a><br>
+<a href="telemetry.timeline.tracing_options.html">telemetry.timeline.tracing_options</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.util.html">telemetry.core.util</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.tracing_controller_backend.html#TracingControllerBackend">TracingControllerBackend</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="exceptions.html#Exception">exceptions.Exception</a>(<a href="exceptions.html#BaseException">exceptions.BaseException</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.tracing_controller_backend.html#TracingControllerStoppedError">TracingControllerStoppedError</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TracingControllerBackend">class <strong>TracingControllerBackend</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="TracingControllerBackend-GetChromeTraceConfig"><strong>GetChromeTraceConfig</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TracingControllerBackend-GetChromeTraceConfigFile"><strong>GetChromeTraceConfigFile</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TracingControllerBackend-IsChromeTracingSupported"><strong>IsChromeTracingSupported</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TracingControllerBackend-Start"><strong>Start</strong></a>(self, trace_options, category_filter, timeout)</dt></dl>
+
+<dl><dt><a name="TracingControllerBackend-Stop"><strong>Stop</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TracingControllerBackend-__init__"><strong>__init__</strong></a>(self, platform_backend)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>is_tracing_running</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TracingControllerStoppedError">class <strong>TracingControllerStoppedError</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.tracing_controller_backend.html#TracingControllerStoppedError">TracingControllerStoppedError</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="TracingControllerStoppedError-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingControllerStoppedError-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#TracingControllerStoppedError-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="TracingControllerStoppedError-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingControllerStoppedError-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="TracingControllerStoppedError-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingControllerStoppedError-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="TracingControllerStoppedError-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingControllerStoppedError-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="TracingControllerStoppedError-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingControllerStoppedError-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="TracingControllerStoppedError-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="TracingControllerStoppedError-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingControllerStoppedError-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="TracingControllerStoppedError-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingControllerStoppedError-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="TracingControllerStoppedError-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="TracingControllerStoppedError-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#TracingControllerStoppedError-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="TracingControllerStoppedError-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.trybot_device.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.trybot_device.html
new file mode 100644
index 0000000..34b3388
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.trybot_device.html
@@ -0,0 +1,80 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.trybot_device</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.trybot_device</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/trybot_device.py">telemetry/internal/platform/trybot_device.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.platform.device.html">telemetry.internal.platform.device</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.device.html#Device">telemetry.internal.platform.device.Device</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.trybot_device.html#TrybotDevice">TrybotDevice</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TrybotDevice">class <strong>TrybotDevice</strong></a>(<a href="telemetry.internal.platform.device.html#Device">telemetry.internal.platform.device.Device</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.trybot_device.html#TrybotDevice">TrybotDevice</a></dd>
+<dd><a href="telemetry.internal.platform.device.html#Device">telemetry.internal.platform.device.Device</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="TrybotDevice-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="TrybotDevice-GetAllConnectedDevices"><strong>GetAllConnectedDevices</strong></a>(cls, blacklist)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.device.html#Device">telemetry.internal.platform.device.Device</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>guid</strong></dt>
+</dl>
+<dl><dt><strong>name</strong></dt>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-FindAllAvailableDevices"><strong>FindAllAvailableDevices</strong></a>(_)</dt><dd><tt>Returns&nbsp;a&nbsp;list&nbsp;of&nbsp;available&nbsp;devices.</tt></dd></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.win_platform_backend.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.win_platform_backend.html
new file mode 100644
index 0000000..d08b9ea
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.platform.win_platform_backend.html
@@ -0,0 +1,261 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.platform.win_platform_backend</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.platform.html"><font color="#ffffff">platform</font></a>.win_platform_backend</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/platform/win_platform_backend.py">telemetry/internal/platform/win_platform_backend.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="atexit.html">atexit</a><br>
+<a href="catapult_base.cloud_storage.html">catapult_base.cloud_storage</a><br>
+<a href="collections.html">collections</a><br>
+<a href="contextlib.html">contextlib</a><br>
+<a href="ctypes.html">ctypes</a><br>
+<a href="telemetry.decorators.html">telemetry.decorators</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.desktop_platform_backend.html">telemetry.internal.platform.desktop_platform_backend</a><br>
+<a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+<a href="logging.html">logging</a><br>
+<a href="telemetry.internal.platform.power_monitor.msr_power_monitor.html">telemetry.internal.platform.power_monitor.msr_power_monitor</a><br>
+<a href="os.html">os</a><br>
+<a href="telemetry.core.os_version.html">telemetry.core.os_version</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.util.path.html">telemetry.internal.util.path</a><br>
+<a href="platform.html">platform</a><br>
+<a href="re.html">re</a><br>
+<a href="socket.html">socket</a><br>
+<a href="struct.html">struct</a><br>
+<a href="subprocess.html">subprocess</a><br>
+</td><td width="25%" valign=top><a href="sys.html">sys</a><br>
+<a href="time.html">time</a><br>
+<a href="telemetry.core.util.html">telemetry.core.util</a><br>
+<a href="zipfile.html">zipfile</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.desktop_platform_backend.html#DesktopPlatformBackend">telemetry.internal.platform.desktop_platform_backend.DesktopPlatformBackend</a>(<a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">telemetry.internal.platform.platform_backend.PlatformBackend</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.win_platform_backend.html#WinPlatformBackend">WinPlatformBackend</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="WinPlatformBackend">class <strong>WinPlatformBackend</strong></a>(<a href="telemetry.internal.platform.desktop_platform_backend.html#DesktopPlatformBackend">telemetry.internal.platform.desktop_platform_backend.DesktopPlatformBackend</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.platform.win_platform_backend.html#WinPlatformBackend">WinPlatformBackend</a></dd>
+<dd><a href="telemetry.internal.platform.desktop_platform_backend.html#DesktopPlatformBackend">telemetry.internal.platform.desktop_platform_backend.DesktopPlatformBackend</a></dd>
+<dd><a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">telemetry.internal.platform.platform_backend.PlatformBackend</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="WinPlatformBackend-CanFlushIndividualFilesFromSystemCache"><strong>CanFlushIndividualFilesFromSystemCache</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-CanMeasurePerApplicationPower"><strong>CanMeasurePerApplicationPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-CanMonitorPower"><strong>CanMonitorPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-CloseMsrServer"><strong>CloseMsrServer</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-CooperativelyShutdown"><strong>CooperativelyShutdown</strong></a>(self, proc, app_name)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-GetArchName"><strong>GetArchName</strong></a>(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-GetChildPids"><strong>GetChildPids</strong></a>(self, pid)</dt><dd><tt>Retunds&nbsp;a&nbsp;list&nbsp;of&nbsp;child&nbsp;pids&nbsp;of&nbsp;|pid|.</tt></dd></dl>
+
+<dl><dt><a name="WinPlatformBackend-GetCommandLine"><strong>GetCommandLine</strong></a>(self, pid)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-GetCpuStats"><strong>GetCpuStats</strong></a>(self, pid)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-GetCpuTimestamp"><strong>GetCpuTimestamp</strong></a>(self)</dt><dd><tt>Return&nbsp;current&nbsp;timestamp&nbsp;in&nbsp;seconds.</tt></dd></dl>
+
+<dl><dt><a name="WinPlatformBackend-GetMemoryStats"><strong>GetMemoryStats</strong></a>(self, pid)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-GetOSName"><strong>GetOSName</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-GetOSVersionName"><strong>GetOSVersionName</strong></a>(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-GetSystemCommitCharge"><strong>GetSystemCommitCharge</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-GetSystemProcessInfo"><strong>GetSystemProcessInfo</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-GetSystemTotalPhysicalMemory"><strong>GetSystemTotalPhysicalMemory</strong></a>(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-HasBeenThermallyThrottled"><strong>HasBeenThermallyThrottled</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-IsCooperativeShutdownSupported"><strong>IsCooperativeShutdownSupported</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-IsCurrentProcessElevated"><strong>IsCurrentProcessElevated</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-IsThermallyThrottled"><strong>IsThermallyThrottled</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-KillProcess"><strong>KillProcess</strong></a>(self, pid, kill_process_tree<font color="#909090">=False</font>)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-LaunchApplication"><strong>LaunchApplication</strong></a>(self, application, parameters<font color="#909090">=None</font>, elevate_privilege<font color="#909090">=False</font>)</dt><dd><tt>Launch&nbsp;an&nbsp;application.&nbsp;Returns&nbsp;a&nbsp;PyHANDLE&nbsp;object.</tt></dd></dl>
+
+<dl><dt><a name="WinPlatformBackend-ReadMsr"><strong>ReadMsr</strong></a>(self, msr_number, start<font color="#909090">=0</font>, length<font color="#909090">=64</font>)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-StartMonitoringPower"><strong>StartMonitoringPower</strong></a>(self, browser)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-StopMonitoringPower"><strong>StopMonitoringPower</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-__del__"><strong>__del__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-close"><strong>close</strong></a>(self)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="WinPlatformBackend-IsPlatformBackendForHost"><strong>IsPlatformBackendForHost</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.platform.desktop_platform_backend.html#DesktopPlatformBackend">telemetry.internal.platform.desktop_platform_backend.DesktopPlatformBackend</a>:<br>
+<dl><dt><a name="WinPlatformBackend-FlushSystemCacheForDirectory"><strong>FlushSystemCacheForDirectory</strong></a>(self, directory)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-GetDeviceTypeName"><strong>GetDeviceTypeName</strong></a>(self)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">telemetry.internal.platform.platform_backend.PlatformBackend</a>:<br>
+<dl><dt><a name="WinPlatformBackend-CanCaptureVideo"><strong>CanCaptureVideo</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-CanLaunchApplication"><strong>CanLaunchApplication</strong></a>(self, application)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-CanMonitorNetworkData"><strong>CanMonitorNetworkData</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-CanMonitorThermalThrottling"><strong>CanMonitorThermalThrottling</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-CanTakeScreenshot"><strong>CanTakeScreenshot</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-DidCreateBrowser"><strong>DidCreateBrowser</strong></a>(self, browser, browser_backend)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-DidStartBrowser"><strong>DidStartBrowser</strong></a>(self, browser, browser_backend)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-FlushDnsCache"><strong>FlushDnsCache</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-FlushEntireSystemCache"><strong>FlushEntireSystemCache</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-GetNetworkData"><strong>GetNetworkData</strong></a>(self, browser)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-GetRemotePort"><strong>GetRemotePort</strong></a>(self, port)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-InitPlatformBackend"><strong>InitPlatformBackend</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-InstallApplication"><strong>InstallApplication</strong></a>(self, application)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-IsApplicationRunning"><strong>IsApplicationRunning</strong></a>(self, application)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-IsDisplayTracingSupported"><strong>IsDisplayTracingSupported</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-PathExists"><strong>PathExists</strong></a>(self, path, timeout<font color="#909090">=None</font>, retries<font color="#909090">=None</font>)</dt><dd><tt>Tests&nbsp;whether&nbsp;the&nbsp;given&nbsp;path&nbsp;exists&nbsp;on&nbsp;the&nbsp;target&nbsp;platform.<br>
+Args:<br>
+&nbsp;&nbsp;path:&nbsp;path&nbsp;in&nbsp;request.<br>
+&nbsp;&nbsp;timeout:&nbsp;timeout.<br>
+&nbsp;&nbsp;retries:&nbsp;num&nbsp;of&nbsp;retries.<br>
+Return:<br>
+&nbsp;&nbsp;Whether&nbsp;the&nbsp;path&nbsp;exists&nbsp;on&nbsp;the&nbsp;target&nbsp;platform.</tt></dd></dl>
+
+<dl><dt><a name="WinPlatformBackend-PurgeUnpinnedMemory"><strong>PurgeUnpinnedMemory</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-SetFullPerformanceModeEnabled"><strong>SetFullPerformanceModeEnabled</strong></a>(self, enabled)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-SetPlatform"><strong>SetPlatform</strong></a>(self, platform)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-StartDisplayTracing"><strong>StartDisplayTracing</strong></a>(self)</dt><dd><tt>Start&nbsp;gathering&nbsp;a&nbsp;trace&nbsp;with&nbsp;frame&nbsp;timestamps&nbsp;close&nbsp;to&nbsp;physical<br>
+display.</tt></dd></dl>
+
+<dl><dt><a name="WinPlatformBackend-StartVideoCapture"><strong>StartVideoCapture</strong></a>(self, min_bitrate_mbps)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-StopDisplayTracing"><strong>StopDisplayTracing</strong></a>(self)</dt><dd><tt>Stop&nbsp;gathering&nbsp;a&nbsp;trace&nbsp;with&nbsp;frame&nbsp;timestamps&nbsp;close&nbsp;to&nbsp;physical&nbsp;display.<br>
+&nbsp;<br>
+Returns&nbsp;a&nbsp;raw&nbsp;tracing&nbsp;events&nbsp;that&nbsp;contains&nbsp;the&nbsp;timestamps&nbsp;of&nbsp;physical<br>
+display.</tt></dd></dl>
+
+<dl><dt><a name="WinPlatformBackend-StopVideoCapture"><strong>StopVideoCapture</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-TakeScreenshot"><strong>TakeScreenshot</strong></a>(self, file_path)</dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-WillCloseBrowser"><strong>WillCloseBrowser</strong></a>(self, browser, browser_backend)</dt></dl>
+
+<hr>
+Class methods inherited from <a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">telemetry.internal.platform.platform_backend.PlatformBackend</a>:<br>
+<dl><dt><a name="WinPlatformBackend-CreatePlatformForDevice"><strong>CreatePlatformForDevice</strong></a>(cls, device, finder_options)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="WinPlatformBackend-SupportsDevice"><strong>SupportsDevice</strong></a>(cls, device)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Returns&nbsp;whether&nbsp;this&nbsp;platform&nbsp;backend&nbsp;supports&nbsp;intialization&nbsp;from&nbsp;the<br>
+device.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.platform_backend.html#PlatformBackend">telemetry.internal.platform.platform_backend.PlatformBackend</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>forwarder_factory</strong></dt>
+</dl>
+<dl><dt><strong>is_host_platform</strong></dt>
+</dl>
+<dl><dt><strong>is_video_capture_running</strong></dt>
+</dl>
+<dl><dt><strong>network_controller_backend</strong></dt>
+</dl>
+<dl><dt><strong>platform</strong></dt>
+</dl>
+<dl><dt><strong>running_browser_backends</strong></dt>
+</dl>
+<dl><dt><strong>tracing_controller_backend</strong></dt>
+</dl>
+<dl><dt><strong>wpr_ca_cert_path</strong></dt>
+</dl>
+<dl><dt><strong>wpr_http_device_port</strong></dt>
+</dl>
+<dl><dt><strong>wpr_https_device_port</strong></dt>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-TerminateProcess"><strong>TerminateProcess</strong></a>(process_handle)</dt></dl>
+</td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>pywintypes</strong> = None<br>
+<strong>shell</strong> = None<br>
+<strong>shellcon</strong> = None<br>
+<strong>win32api</strong> = None<br>
+<strong>win32con</strong> = None<br>
+<strong>win32gui</strong> = None<br>
+<strong>win32process</strong> = None<br>
+<strong>win32security</strong> = None</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.results.buildbot_output_formatter.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.results.buildbot_output_formatter.html
new file mode 100644
index 0000000..7b4addb
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.results.buildbot_output_formatter.html
@@ -0,0 +1,75 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.results.buildbot_output_formatter</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.results.html"><font color="#ffffff">results</font></a>.buildbot_output_formatter</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/results/buildbot_output_formatter.py">telemetry/internal/results/buildbot_output_formatter.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.decorators.html">telemetry.decorators</a><br>
+<a href="telemetry.internal.results.output_formatter.html">telemetry.internal.results.output_formatter</a><br>
+</td><td width="25%" valign=top><a href="telemetry.util.perf_tests_helper.html">telemetry.util.perf_tests_helper</a><br>
+<a href="telemetry.value.summary.html">telemetry.value.summary</a><br>
+</td><td width="25%" valign=top><a href="telemetry.value.html">telemetry.value</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.results.output_formatter.html#OutputFormatter">telemetry.internal.results.output_formatter.OutputFormatter</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.results.buildbot_output_formatter.html#BuildbotOutputFormatter">BuildbotOutputFormatter</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="BuildbotOutputFormatter">class <strong>BuildbotOutputFormatter</strong></a>(<a href="telemetry.internal.results.output_formatter.html#OutputFormatter">telemetry.internal.results.output_formatter.OutputFormatter</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.results.buildbot_output_formatter.html#BuildbotOutputFormatter">BuildbotOutputFormatter</a></dd>
+<dd><a href="telemetry.internal.results.output_formatter.html#OutputFormatter">telemetry.internal.results.output_formatter.OutputFormatter</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="BuildbotOutputFormatter-Format"><strong>Format</strong></a>(self, page_test_results)</dt><dd><tt>Print&nbsp;summary&nbsp;data&nbsp;in&nbsp;a&nbsp;format&nbsp;expected&nbsp;by&nbsp;buildbot&nbsp;for&nbsp;perf&nbsp;dashboards.<br>
+&nbsp;<br>
+If&nbsp;any&nbsp;failed&nbsp;pages&nbsp;exist,&nbsp;only&nbsp;output&nbsp;individual&nbsp;page&nbsp;results,&nbsp;and&nbsp;do<br>
+not&nbsp;output&nbsp;any&nbsp;average&nbsp;data.</tt></dd></dl>
+
+<dl><dt><a name="BuildbotOutputFormatter-__init__"><strong>__init__</strong></a>(*args, **kwargs)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.results.output_formatter.html#OutputFormatter">telemetry.internal.results.output_formatter.OutputFormatter</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>output_stream</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.results.chart_json_output_formatter.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.results.chart_json_output_formatter.html
new file mode 100644
index 0000000..2953e7f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.results.chart_json_output_formatter.html
@@ -0,0 +1,99 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.results.chart_json_output_formatter</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.results.html"><font color="#ffffff">results</font></a>.chart_json_output_formatter</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/results/chart_json_output_formatter.py">telemetry/internal/results/chart_json_output_formatter.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="collections.html">collections</a><br>
+<a href="itertools.html">itertools</a><br>
+</td><td width="25%" valign=top><a href="json.html">json</a><br>
+<a href="telemetry.internal.results.output_formatter.html">telemetry.internal.results.output_formatter</a><br>
+</td><td width="25%" valign=top><a href="telemetry.value.summary.html">telemetry.value.summary</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.results.output_formatter.html#OutputFormatter">telemetry.internal.results.output_formatter.OutputFormatter</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.results.chart_json_output_formatter.html#ChartJsonOutputFormatter">ChartJsonOutputFormatter</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ChartJsonOutputFormatter">class <strong>ChartJsonOutputFormatter</strong></a>(<a href="telemetry.internal.results.output_formatter.html#OutputFormatter">telemetry.internal.results.output_formatter.OutputFormatter</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>#&nbsp;TODO(eakuefner):&nbsp;Transition&nbsp;this&nbsp;to&nbsp;translate&nbsp;Telemetry&nbsp;JSON.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.results.chart_json_output_formatter.html#ChartJsonOutputFormatter">ChartJsonOutputFormatter</a></dd>
+<dd><a href="telemetry.internal.results.output_formatter.html#OutputFormatter">telemetry.internal.results.output_formatter.OutputFormatter</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="ChartJsonOutputFormatter-Format"><strong>Format</strong></a>(self, page_test_results)</dt></dl>
+
+<dl><dt><a name="ChartJsonOutputFormatter-__init__"><strong>__init__</strong></a>(self, output_stream, benchmark_metadata)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.results.output_formatter.html#OutputFormatter">telemetry.internal.results.output_formatter.OutputFormatter</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>output_stream</strong></dt>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-ResultsAsChartDict"><strong>ResultsAsChartDict</strong></a>(benchmark_metadata, page_specific_values, summary_values)</dt><dd><tt>Produces&nbsp;a&nbsp;dict&nbsp;for&nbsp;serialization&nbsp;to&nbsp;Chart&nbsp;JSON&nbsp;format&nbsp;from&nbsp;raw&nbsp;values.<br>
+&nbsp;<br>
+Chart&nbsp;JSON&nbsp;is&nbsp;a&nbsp;transformation&nbsp;of&nbsp;the&nbsp;basic&nbsp;Telemetry&nbsp;JSON&nbsp;format&nbsp;that<br>
+removes&nbsp;the&nbsp;page&nbsp;map,&nbsp;summarizes&nbsp;the&nbsp;raw&nbsp;values,&nbsp;and&nbsp;organizes&nbsp;the&nbsp;results<br>
+by&nbsp;chart&nbsp;and&nbsp;trace&nbsp;name.&nbsp;This&nbsp;function&nbsp;takes&nbsp;the&nbsp;key&nbsp;pieces&nbsp;of&nbsp;data&nbsp;needed&nbsp;to<br>
+perform&nbsp;this&nbsp;transformation&nbsp;(namely,&nbsp;lists&nbsp;of&nbsp;values&nbsp;and&nbsp;a&nbsp;benchmark&nbsp;metadata<br>
+object)&nbsp;and&nbsp;processes&nbsp;them&nbsp;into&nbsp;a&nbsp;dict&nbsp;which&nbsp;can&nbsp;be&nbsp;serialized&nbsp;using&nbsp;the&nbsp;json<br>
+module.<br>
+&nbsp;<br>
+Design&nbsp;doc&nbsp;for&nbsp;schema:&nbsp;<a href="http://goo.gl/kOtf1Y">http://goo.gl/kOtf1Y</a><br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;page_specific_values:&nbsp;list&nbsp;of&nbsp;page-specific&nbsp;values<br>
+&nbsp;&nbsp;summary_values:&nbsp;list&nbsp;of&nbsp;summary&nbsp;values<br>
+&nbsp;&nbsp;benchmark_metadata:&nbsp;a&nbsp;benchmark.BenchmarkMetadata&nbsp;object<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;A&nbsp;Chart&nbsp;JSON&nbsp;dict&nbsp;corresponding&nbsp;to&nbsp;the&nbsp;given&nbsp;data.</tt></dd></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.results.csv_pivot_table_output_formatter.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.results.csv_pivot_table_output_formatter.html
new file mode 100644
index 0000000..ccde6e6
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.results.csv_pivot_table_output_formatter.html
@@ -0,0 +1,88 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.results.csv_pivot_table_output_formatter</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.results.html"><font color="#ffffff">results</font></a>.csv_pivot_table_output_formatter</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/results/csv_pivot_table_output_formatter.py">telemetry/internal/results/csv_pivot_table_output_formatter.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="csv.html">csv</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.results.output_formatter.html">telemetry.internal.results.output_formatter</a><br>
+</td><td width="25%" valign=top><a href="telemetry.value.scalar.html">telemetry.value.scalar</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.results.output_formatter.html#OutputFormatter">telemetry.internal.results.output_formatter.OutputFormatter</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.results.csv_pivot_table_output_formatter.html#CsvPivotTableOutputFormatter">CsvPivotTableOutputFormatter</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="CsvPivotTableOutputFormatter">class <strong>CsvPivotTableOutputFormatter</strong></a>(<a href="telemetry.internal.results.output_formatter.html#OutputFormatter">telemetry.internal.results.output_formatter.OutputFormatter</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Output&nbsp;the&nbsp;results&nbsp;as&nbsp;CSV&nbsp;suitable&nbsp;for&nbsp;reading&nbsp;into&nbsp;a&nbsp;spreadsheet.<br>
+&nbsp;<br>
+This&nbsp;will&nbsp;write&nbsp;a&nbsp;header&nbsp;row,&nbsp;and&nbsp;one&nbsp;row&nbsp;for&nbsp;each&nbsp;value.&nbsp;Each&nbsp;value&nbsp;row<br>
+contains&nbsp;the&nbsp;value&nbsp;and&nbsp;unit,&nbsp;identifies&nbsp;the&nbsp;value&nbsp;(story_set,&nbsp;page,&nbsp;name),&nbsp;and<br>
+(optionally)&nbsp;data&nbsp;from&nbsp;--output-trace-tag.&nbsp;This&nbsp;format&nbsp;matches&nbsp;what<br>
+spreadsheet&nbsp;programs&nbsp;expect&nbsp;as&nbsp;input&nbsp;for&nbsp;a&nbsp;"pivot&nbsp;table".<br>
+&nbsp;<br>
+A&nbsp;trace&nbsp;tag&nbsp;(--output-trace-tag)&nbsp;can&nbsp;be&nbsp;used&nbsp;to&nbsp;tag&nbsp;each&nbsp;value,&nbsp;to&nbsp;allow<br>
+easy&nbsp;combination&nbsp;of&nbsp;the&nbsp;resulting&nbsp;CSVs&nbsp;from&nbsp;several&nbsp;runs.<br>
+If&nbsp;the&nbsp;trace_tag&nbsp;contains&nbsp;a&nbsp;comma,&nbsp;it&nbsp;will&nbsp;be&nbsp;written&nbsp;as&nbsp;several<br>
+comma-separated&nbsp;values.<br>
+&nbsp;<br>
+This&nbsp;class&nbsp;only&nbsp;processes&nbsp;scalar&nbsp;values.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.results.csv_pivot_table_output_formatter.html#CsvPivotTableOutputFormatter">CsvPivotTableOutputFormatter</a></dd>
+<dd><a href="telemetry.internal.results.output_formatter.html#OutputFormatter">telemetry.internal.results.output_formatter.OutputFormatter</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="CsvPivotTableOutputFormatter-Format"><strong>Format</strong></a>(self, page_test_results)</dt></dl>
+
+<dl><dt><a name="CsvPivotTableOutputFormatter-__init__"><strong>__init__</strong></a>(self, output_stream, trace_tag<font color="#909090">=''</font>)</dt></dl>
+
+<hr>
+Data and other attributes defined here:<br>
+<dl><dt><strong>FIELDS</strong> = ['story_set', 'page', 'name', 'value', 'units', 'run_index']</dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.results.output_formatter.html#OutputFormatter">telemetry.internal.results.output_formatter.OutputFormatter</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>output_stream</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.results.gtest_progress_reporter.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.results.gtest_progress_reporter.html
new file mode 100644
index 0000000..e96b455
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.results.gtest_progress_reporter.html
@@ -0,0 +1,84 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.results.gtest_progress_reporter</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.results.html"><font color="#ffffff">results</font></a>.gtest_progress_reporter</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/results/gtest_progress_reporter.py">telemetry/internal/results/gtest_progress_reporter.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.value.failure.html">telemetry.value.failure</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.results.progress_reporter.html">telemetry.internal.results.progress_reporter</a><br>
+</td><td width="25%" valign=top><a href="telemetry.value.skip.html">telemetry.value.skip</a><br>
+</td><td width="25%" valign=top><a href="time.html">time</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.results.progress_reporter.html#ProgressReporter">telemetry.internal.results.progress_reporter.ProgressReporter</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.results.gtest_progress_reporter.html#GTestProgressReporter">GTestProgressReporter</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="GTestProgressReporter">class <strong>GTestProgressReporter</strong></a>(<a href="telemetry.internal.results.progress_reporter.html#ProgressReporter">telemetry.internal.results.progress_reporter.ProgressReporter</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;progress&nbsp;reporter&nbsp;that&nbsp;outputs&nbsp;the&nbsp;progress&nbsp;report&nbsp;in&nbsp;gtest&nbsp;style.<br>
+&nbsp;<br>
+Be&nbsp;careful&nbsp;each&nbsp;print&nbsp;should&nbsp;only&nbsp;handle&nbsp;one&nbsp;string.&nbsp;Otherwise,&nbsp;the&nbsp;output<br>
+might&nbsp;be&nbsp;interrupted&nbsp;by&nbsp;Chrome&nbsp;logging,&nbsp;and&nbsp;the&nbsp;output&nbsp;interpretation&nbsp;might<br>
+be&nbsp;incorrect.&nbsp;For&nbsp;example:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;print&nbsp;&gt;&gt;&nbsp;self.<strong>_output_stream</strong>,&nbsp;"[&nbsp;OK&nbsp;]",&nbsp;testname<br>
+should&nbsp;be&nbsp;written&nbsp;as<br>
+&nbsp;&nbsp;&nbsp;&nbsp;print&nbsp;&gt;&gt;&nbsp;self.<strong>_output_stream</strong>,&nbsp;"[&nbsp;OK&nbsp;]&nbsp;%s"&nbsp;%&nbsp;testname<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.results.gtest_progress_reporter.html#GTestProgressReporter">GTestProgressReporter</a></dd>
+<dd><a href="telemetry.internal.results.progress_reporter.html#ProgressReporter">telemetry.internal.results.progress_reporter.ProgressReporter</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="GTestProgressReporter-DidAddValue"><strong>DidAddValue</strong></a>(self, value)</dt></dl>
+
+<dl><dt><a name="GTestProgressReporter-DidFinishAllTests"><strong>DidFinishAllTests</strong></a>(self, page_test_results)</dt></dl>
+
+<dl><dt><a name="GTestProgressReporter-DidRunPage"><strong>DidRunPage</strong></a>(self, page_test_results)</dt></dl>
+
+<dl><dt><a name="GTestProgressReporter-WillRunPage"><strong>WillRunPage</strong></a>(self, page_test_results)</dt></dl>
+
+<dl><dt><a name="GTestProgressReporter-__init__"><strong>__init__</strong></a>(self, output_stream, output_skipped_tests_summary<font color="#909090">=False</font>)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.results.progress_reporter.html#ProgressReporter">telemetry.internal.results.progress_reporter.ProgressReporter</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.results.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.results.html
new file mode 100644
index 0000000..a17726b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.results.html
@@ -0,0 +1,43 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: package telemetry.internal.results</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.results</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/results/__init__.py">telemetry/internal/results/__init__.py</a></font></td></tr></table>
+    <p><tt>The&nbsp;PageTestResults&nbsp;hierarchy&nbsp;provides&nbsp;a&nbsp;way&nbsp;of&nbsp;representing&nbsp;the&nbsp;results&nbsp;of<br>
+running&nbsp;the&nbsp;test&nbsp;or&nbsp;measurement&nbsp;on&nbsp;pages.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Package Contents</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.results.base_test_results_unittest.html">base_test_results_unittest</a><br>
+<a href="telemetry.internal.results.buildbot_output_formatter.html">buildbot_output_formatter</a><br>
+<a href="telemetry.internal.results.buildbot_output_formatter_unittest.html">buildbot_output_formatter_unittest</a><br>
+<a href="telemetry.internal.results.chart_json_output_formatter.html">chart_json_output_formatter</a><br>
+<a href="telemetry.internal.results.chart_json_output_formatter_unittest.html">chart_json_output_formatter_unittest</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.results.csv_pivot_table_output_formatter.html">csv_pivot_table_output_formatter</a><br>
+<a href="telemetry.internal.results.csv_pivot_table_output_formatter_unittest.html">csv_pivot_table_output_formatter_unittest</a><br>
+<a href="telemetry.internal.results.gtest_progress_reporter.html">gtest_progress_reporter</a><br>
+<a href="telemetry.internal.results.gtest_progress_reporter_unittest.html">gtest_progress_reporter_unittest</a><br>
+<a href="telemetry.internal.results.html_output_formatter.html">html_output_formatter</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.results.html_output_formatter_unittest.html">html_output_formatter_unittest</a><br>
+<a href="telemetry.internal.results.json_output_formatter.html">json_output_formatter</a><br>
+<a href="telemetry.internal.results.json_output_formatter_unittest.html">json_output_formatter_unittest</a><br>
+<a href="telemetry.internal.results.output_formatter.html">output_formatter</a><br>
+<a href="telemetry.internal.results.page_test_results.html">page_test_results</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.results.page_test_results_unittest.html">page_test_results_unittest</a><br>
+<a href="telemetry.internal.results.progress_reporter.html">progress_reporter</a><br>
+<a href="telemetry.internal.results.results_options.html">results_options</a><br>
+<a href="telemetry.internal.results.story_run.html">story_run</a><br>
+<a href="telemetry.internal.results.story_run_unittest.html">story_run_unittest</a><br>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.results.html_output_formatter.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.results.html_output_formatter.html
new file mode 100644
index 0000000..170dfc6
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.results.html_output_formatter.html
@@ -0,0 +1,84 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.results.html_output_formatter</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.results.html"><font color="#ffffff">results</font></a>.html_output_formatter</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/results/html_output_formatter.py">telemetry/internal/results/html_output_formatter.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.results.chart_json_output_formatter.html">telemetry.internal.results.chart_json_output_formatter</a><br>
+<a href="catapult_base.cloud_storage.html">catapult_base.cloud_storage</a><br>
+<a href="datetime.html">datetime</a><br>
+</td><td width="25%" valign=top><a href="json.html">json</a><br>
+<a href="telemetry.value.list_of_scalar_values.html">telemetry.value.list_of_scalar_values</a><br>
+<a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top><a href="os.html">os</a><br>
+<a href="telemetry.internal.results.output_formatter.html">telemetry.internal.results.output_formatter</a><br>
+<a href="re.html">re</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.util.html">telemetry.core.util</a><br>
+<a href="telemetry.value.html">telemetry.value</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.results.output_formatter.html#OutputFormatter">telemetry.internal.results.output_formatter.OutputFormatter</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.results.html_output_formatter.html#HtmlOutputFormatter">HtmlOutputFormatter</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="HtmlOutputFormatter">class <strong>HtmlOutputFormatter</strong></a>(<a href="telemetry.internal.results.output_formatter.html#OutputFormatter">telemetry.internal.results.output_formatter.OutputFormatter</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>#&nbsp;TODO(eakuefner):&nbsp;rewrite&nbsp;template&nbsp;to&nbsp;use&nbsp;Telemetry&nbsp;JSON&nbsp;directly<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.results.html_output_formatter.html#HtmlOutputFormatter">HtmlOutputFormatter</a></dd>
+<dd><a href="telemetry.internal.results.output_formatter.html#OutputFormatter">telemetry.internal.results.output_formatter.OutputFormatter</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="HtmlOutputFormatter-Format"><strong>Format</strong></a>(self, page_test_results)</dt></dl>
+
+<dl><dt><a name="HtmlOutputFormatter-GetCombinedResults"><strong>GetCombinedResults</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="HtmlOutputFormatter-GetResults"><strong>GetResults</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="HtmlOutputFormatter-__init__"><strong>__init__</strong></a>(self, output_stream, metadata, reset_results, upload_results, browser_type, results_label<font color="#909090">=None</font>)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.results.output_formatter.html#OutputFormatter">telemetry.internal.results.output_formatter.OutputFormatter</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>output_stream</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.results.json_output_formatter.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.results.json_output_formatter.html
new file mode 100644
index 0000000..f1d4dc9
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.results.json_output_formatter.html
@@ -0,0 +1,90 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.results.json_output_formatter</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.results.html"><font color="#ffffff">results</font></a>.json_output_formatter</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/results/json_output_formatter.py">telemetry/internal/results/json_output_formatter.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="json.html">json</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.results.output_formatter.html">telemetry.internal.results.output_formatter</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.results.output_formatter.html#OutputFormatter">telemetry.internal.results.output_formatter.OutputFormatter</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.results.json_output_formatter.html#JsonOutputFormatter">JsonOutputFormatter</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="JsonOutputFormatter">class <strong>JsonOutputFormatter</strong></a>(<a href="telemetry.internal.results.output_formatter.html#OutputFormatter">telemetry.internal.results.output_formatter.OutputFormatter</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.results.json_output_formatter.html#JsonOutputFormatter">JsonOutputFormatter</a></dd>
+<dd><a href="telemetry.internal.results.output_formatter.html#OutputFormatter">telemetry.internal.results.output_formatter.OutputFormatter</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="JsonOutputFormatter-Format"><strong>Format</strong></a>(self, page_test_results)</dt></dl>
+
+<dl><dt><a name="JsonOutputFormatter-__init__"><strong>__init__</strong></a>(self, output_stream, benchmark_metadata)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>benchmark_metadata</strong></dt>
+</dl>
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.results.output_formatter.html#OutputFormatter">telemetry.internal.results.output_formatter.OutputFormatter</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>output_stream</strong></dt>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-ResultsAsDict"><strong>ResultsAsDict</strong></a>(page_test_results, benchmark_metadata)</dt><dd><tt>Takes&nbsp;PageTestResults&nbsp;to&nbsp;a&nbsp;dict&nbsp;serializable&nbsp;to&nbsp;JSON.<br>
+&nbsp;<br>
+To&nbsp;serialize&nbsp;results&nbsp;as&nbsp;JSON&nbsp;we&nbsp;first&nbsp;convert&nbsp;them&nbsp;to&nbsp;a&nbsp;dict&nbsp;that&nbsp;can&nbsp;be<br>
+serialized&nbsp;by&nbsp;the&nbsp;json&nbsp;module.&nbsp;It&nbsp;also&nbsp;requires&nbsp;a&nbsp;benchmark_metadat&nbsp;object<br>
+for&nbsp;metadata&nbsp;to&nbsp;be&nbsp;integrated&nbsp;into&nbsp;the&nbsp;results&nbsp;(currently&nbsp;the&nbsp;benchmark<br>
+name).&nbsp;This&nbsp;function&nbsp;will&nbsp;also&nbsp;output&nbsp;trace&nbsp;files&nbsp;if&nbsp;they&nbsp;exist.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;page_test_results:&nbsp;a&nbsp;PageTestResults&nbsp;object<br>
+&nbsp;&nbsp;benchmark_metadata:&nbsp;a&nbsp;benchmark.BenchmarkMetadata&nbsp;object</tt></dd></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.results.output_formatter.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.results.output_formatter.html
new file mode 100644
index 0000000..2588947
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.results.output_formatter.html
@@ -0,0 +1,72 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.results.output_formatter</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.results.html"><font color="#ffffff">results</font></a>.output_formatter</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/results/output_formatter.py">telemetry/internal/results/output_formatter.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.results.output_formatter.html#OutputFormatter">OutputFormatter</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="OutputFormatter">class <strong>OutputFormatter</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;formatter&nbsp;for&nbsp;PageTestResults.<br>
+&nbsp;<br>
+An&nbsp;<a href="#OutputFormatter">OutputFormatter</a>&nbsp;takes&nbsp;PageTestResults,&nbsp;formats&nbsp;the&nbsp;results<br>
+(telemetry.value.Value&nbsp;instances),&nbsp;and&nbsp;output&nbsp;the&nbsp;formatted&nbsp;results<br>
+in&nbsp;the&nbsp;given&nbsp;output&nbsp;stream.<br>
+&nbsp;<br>
+Examples&nbsp;of&nbsp;output&nbsp;formatter:&nbsp;CsvOutputFormatter&nbsp;produces&nbsp;results&nbsp;in<br>
+CSV&nbsp;format.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="OutputFormatter-Format"><strong>Format</strong></a>(self, page_test_results)</dt><dd><tt>Formats&nbsp;the&nbsp;given&nbsp;PageTestResults&nbsp;into&nbsp;the&nbsp;output&nbsp;stream.<br>
+&nbsp;<br>
+This&nbsp;will&nbsp;be&nbsp;called&nbsp;once&nbsp;at&nbsp;the&nbsp;end&nbsp;of&nbsp;a&nbsp;benchmark.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;page_test_results:&nbsp;A&nbsp;PageTestResults&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;containing&nbsp;all&nbsp;results<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;from&nbsp;the&nbsp;current&nbsp;benchmark&nbsp;run.</tt></dd></dl>
+
+<dl><dt><a name="OutputFormatter-__init__"><strong>__init__</strong></a>(self, output_stream)</dt><dd><tt>Constructs&nbsp;a&nbsp;new&nbsp;formatter&nbsp;that&nbsp;writes&nbsp;to&nbsp;the&nbsp;output_stream.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;output_stream:&nbsp;The&nbsp;stream&nbsp;to&nbsp;write&nbsp;the&nbsp;formatted&nbsp;output&nbsp;to.</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>output_stream</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.results.page_test_results.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.results.page_test_results.html
new file mode 100644
index 0000000..45c0ee5
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.results.page_test_results.html
@@ -0,0 +1,152 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.results.page_test_results</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.results.html"><font color="#ffffff">results</font></a>.page_test_results</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/results/page_test_results.py">telemetry/internal/results/page_test_results.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="catapult_base.cloud_storage.html">catapult_base.cloud_storage</a><br>
+<a href="collections.html">collections</a><br>
+<a href="copy.html">copy</a><br>
+<a href="datetime.html">datetime</a><br>
+</td><td width="25%" valign=top><a href="telemetry.value.failure.html">telemetry.value.failure</a><br>
+<a href="telemetry.internal.results.json_output_formatter.html">telemetry.internal.results.json_output_formatter</a><br>
+<a href="logging.html">logging</a><br>
+<a href="random.html">random</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.results.progress_reporter.html">telemetry.internal.results.progress_reporter</a><br>
+<a href="telemetry.value.skip.html">telemetry.value.skip</a><br>
+<a href="telemetry.internal.results.story_run.html">telemetry.internal.results.story_run</a><br>
+<a href="sys.html">sys</a><br>
+</td><td width="25%" valign=top><a href="telemetry.value.trace.html">telemetry.value.trace</a><br>
+<a href="traceback.html">traceback</a><br>
+<a href="telemetry.value.html">telemetry.value</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.results.page_test_results.html#PageTestResults">PageTestResults</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="PageTestResults">class <strong>PageTestResults</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="PageTestResults-AddProfilingFile"><strong>AddProfilingFile</strong></a>(self, page, file_handle)</dt></dl>
+
+<dl><dt><a name="PageTestResults-AddSummaryValue"><strong>AddSummaryValue</strong></a>(self, value)</dt></dl>
+
+<dl><dt><a name="PageTestResults-AddValue"><strong>AddValue</strong></a>(self, value)</dt></dl>
+
+<dl><dt><a name="PageTestResults-CleanUp"><strong>CleanUp</strong></a>(self)</dt><dd><tt>Clean&nbsp;up&nbsp;any&nbsp;TraceValues&nbsp;contained&nbsp;within&nbsp;this&nbsp;results&nbsp;<a href="__builtin__.html#object">object</a>.</tt></dd></dl>
+
+<dl><dt><a name="PageTestResults-DidRunPage"><strong>DidRunPage</strong></a>(self, page)</dt><dd><tt>Args:<br>
+&nbsp;&nbsp;page:&nbsp;The&nbsp;current&nbsp;page&nbsp;under&nbsp;test.</tt></dd></dl>
+
+<dl><dt><a name="PageTestResults-FindAllPageSpecificValuesFromIRNamed"><strong>FindAllPageSpecificValuesFromIRNamed</strong></a>(self, tir_label, value_name)</dt></dl>
+
+<dl><dt><a name="PageTestResults-FindAllPageSpecificValuesNamed"><strong>FindAllPageSpecificValuesNamed</strong></a>(self, value_name)</dt></dl>
+
+<dl><dt><a name="PageTestResults-FindAllTraceValues"><strong>FindAllTraceValues</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PageTestResults-FindPageSpecificValuesForPage"><strong>FindPageSpecificValuesForPage</strong></a>(self, page, value_name)</dt></dl>
+
+<dl><dt><a name="PageTestResults-FindValues"><strong>FindValues</strong></a>(self, predicate)</dt><dd><tt>Finds&nbsp;all&nbsp;values&nbsp;matching&nbsp;the&nbsp;specified&nbsp;predicate.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;predicate:&nbsp;A&nbsp;function&nbsp;that&nbsp;takes&nbsp;a&nbsp;Value&nbsp;and&nbsp;returns&nbsp;a&nbsp;bool.<br>
+Returns:<br>
+&nbsp;&nbsp;A&nbsp;list&nbsp;of&nbsp;values&nbsp;matching&nbsp;|predicate|.</tt></dd></dl>
+
+<dl><dt><a name="PageTestResults-PrintSummary"><strong>PrintSummary</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PageTestResults-UploadProfilingFilesToCloud"><strong>UploadProfilingFilesToCloud</strong></a>(self, bucket)</dt></dl>
+
+<dl><dt><a name="PageTestResults-UploadTraceFilesToCloud"><strong>UploadTraceFilesToCloud</strong></a>(self, bucket)</dt></dl>
+
+<dl><dt><a name="PageTestResults-WillRunPage"><strong>WillRunPage</strong></a>(self, page)</dt></dl>
+
+<dl><dt><a name="PageTestResults-__copy__"><strong>__copy__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PageTestResults-__enter__"><strong>__enter__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PageTestResults-__exit__"><strong>__exit__</strong></a>(self, _, __, ___)</dt></dl>
+
+<dl><dt><a name="PageTestResults-__init__"><strong>__init__</strong></a>(self, output_formatters<font color="#909090">=None</font>, progress_reporter<font color="#909090">=None</font>, trace_tag<font color="#909090">=''</font>, output_dir<font color="#909090">=None</font>, value_can_be_added_predicate<font color="#909090">=&lt;function &lt;lambda&gt;&gt;</font>)</dt><dd><tt>Args:<br>
+&nbsp;&nbsp;output_formatters:&nbsp;A&nbsp;list&nbsp;of&nbsp;output&nbsp;formatters.&nbsp;The&nbsp;output<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;formatters&nbsp;are&nbsp;typically&nbsp;used&nbsp;to&nbsp;format&nbsp;the&nbsp;test&nbsp;results,&nbsp;such<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;as&nbsp;CsvPivotTableOutputFormatter,&nbsp;which&nbsp;output&nbsp;the&nbsp;test&nbsp;results&nbsp;as&nbsp;CSV.<br>
+&nbsp;&nbsp;progress_reporter:&nbsp;An&nbsp;instance&nbsp;of&nbsp;progress_reporter.ProgressReporter,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;to&nbsp;be&nbsp;used&nbsp;to&nbsp;output&nbsp;test&nbsp;status/results&nbsp;progressively.<br>
+&nbsp;&nbsp;trace_tag:&nbsp;A&nbsp;string&nbsp;to&nbsp;append&nbsp;to&nbsp;the&nbsp;buildbot&nbsp;trace&nbsp;name.&nbsp;Currently&nbsp;only<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;used&nbsp;for&nbsp;buildbot.<br>
+&nbsp;&nbsp;output_dir:&nbsp;A&nbsp;string&nbsp;specified&nbsp;the&nbsp;directory&nbsp;where&nbsp;to&nbsp;store&nbsp;the&nbsp;test<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;artifacts,&nbsp;e.g:&nbsp;trace,&nbsp;videos,...<br>
+&nbsp;&nbsp;value_can_be_added_predicate:&nbsp;A&nbsp;function&nbsp;that&nbsp;takes&nbsp;two&nbsp;arguments:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;a&nbsp;value.Value&nbsp;instance&nbsp;(except&nbsp;failure.FailureValue,&nbsp;skip.SkipValue<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;or&nbsp;trace.TraceValue)&nbsp;and&nbsp;a&nbsp;boolean&nbsp;(True&nbsp;when&nbsp;the&nbsp;value&nbsp;is&nbsp;part&nbsp;of<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;the&nbsp;first&nbsp;result&nbsp;for&nbsp;the&nbsp;story).&nbsp;It&nbsp;returns&nbsp;True&nbsp;if&nbsp;the&nbsp;value<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;can&nbsp;be&nbsp;added&nbsp;to&nbsp;the&nbsp;test&nbsp;results&nbsp;and&nbsp;False&nbsp;otherwise.</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>all_page_runs</strong></dt>
+</dl>
+<dl><dt><strong>all_page_specific_values</strong></dt>
+</dl>
+<dl><dt><strong>all_summary_values</strong></dt>
+</dl>
+<dl><dt><strong>current_page</strong></dt>
+</dl>
+<dl><dt><strong>current_page_run</strong></dt>
+</dl>
+<dl><dt><strong>failures</strong></dt>
+</dl>
+<dl><dt><strong>pages_that_failed</strong></dt>
+<dd><tt>Returns&nbsp;the&nbsp;set&nbsp;of&nbsp;failed&nbsp;pages.</tt></dd>
+</dl>
+<dl><dt><strong>pages_that_succeeded</strong></dt>
+<dd><tt>Returns&nbsp;the&nbsp;set&nbsp;of&nbsp;pages&nbsp;that&nbsp;succeeded.</tt></dd>
+</dl>
+<dl><dt><strong>pages_to_profiling_files</strong></dt>
+</dl>
+<dl><dt><strong>pages_to_profiling_files_cloud_url</strong></dt>
+</dl>
+<dl><dt><strong>serialized_trace_file_ids_to_paths</strong></dt>
+</dl>
+<dl><dt><strong>skipped_values</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.results.progress_reporter.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.results.progress_reporter.html
new file mode 100644
index 0000000..6d9ae69
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.results.progress_reporter.html
@@ -0,0 +1,65 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.results.progress_reporter</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.results.html"><font color="#ffffff">results</font></a>.progress_reporter</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/results/progress_reporter.py">telemetry/internal/results/progress_reporter.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.results.progress_reporter.html#ProgressReporter">ProgressReporter</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ProgressReporter">class <strong>ProgressReporter</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;class&nbsp;that&nbsp;reports&nbsp;progress&nbsp;of&nbsp;a&nbsp;benchmark.<br>
+&nbsp;<br>
+The&nbsp;reporter&nbsp;produces&nbsp;output&nbsp;whenever&nbsp;a&nbsp;significant&nbsp;event&nbsp;happens<br>
+during&nbsp;the&nbsp;progress&nbsp;of&nbsp;a&nbsp;benchmark,&nbsp;including&nbsp;(but&nbsp;not&nbsp;limited&nbsp;to):<br>
+when&nbsp;a&nbsp;page&nbsp;run&nbsp;is&nbsp;started,&nbsp;when&nbsp;a&nbsp;page&nbsp;run&nbsp;finished,&nbsp;any&nbsp;failures<br>
+during&nbsp;a&nbsp;page&nbsp;run.<br>
+&nbsp;<br>
+The&nbsp;default&nbsp;implementation&nbsp;outputs&nbsp;nothing.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="ProgressReporter-DidAddValue"><strong>DidAddValue</strong></a>(self, value)</dt></dl>
+
+<dl><dt><a name="ProgressReporter-DidFinishAllTests"><strong>DidFinishAllTests</strong></a>(self, page_test_results)</dt></dl>
+
+<dl><dt><a name="ProgressReporter-DidRunPage"><strong>DidRunPage</strong></a>(self, page_test_results)</dt></dl>
+
+<dl><dt><a name="ProgressReporter-WillRunPage"><strong>WillRunPage</strong></a>(self, page_test_results)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.results.results_options.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.results.results_options.html
new file mode 100644
index 0000000..a565e54
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.results.results_options.html
@@ -0,0 +1,48 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.results.results_options</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.results.html"><font color="#ffffff">results</font></a>.results_options</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/results/results_options.py">telemetry/internal/results/results_options.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.results.buildbot_output_formatter.html">telemetry.internal.results.buildbot_output_formatter</a><br>
+<a href="telemetry.internal.results.chart_json_output_formatter.html">telemetry.internal.results.chart_json_output_formatter</a><br>
+<a href="catapult_base.cloud_storage.html">catapult_base.cloud_storage</a><br>
+<a href="telemetry.internal.results.csv_pivot_table_output_formatter.html">telemetry.internal.results.csv_pivot_table_output_formatter</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.results.gtest_progress_reporter.html">telemetry.internal.results.gtest_progress_reporter</a><br>
+<a href="telemetry.internal.results.html_output_formatter.html">telemetry.internal.results.html_output_formatter</a><br>
+<a href="telemetry.internal.results.json_output_formatter.html">telemetry.internal.results.json_output_formatter</a><br>
+<a href="optparse.html">optparse</a><br>
+</td><td width="25%" valign=top><a href="os.html">os</a><br>
+<a href="telemetry.internal.results.page_test_results.html">telemetry.internal.results.page_test_results</a><br>
+<a href="telemetry.internal.results.progress_reporter.html">telemetry.internal.results.progress_reporter</a><br>
+<a href="sys.html">sys</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.util.html">telemetry.core.util</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-AddResultsOptions"><strong>AddResultsOptions</strong></a>(parser)</dt></dl>
+ <dl><dt><a name="-CreateResults"><strong>CreateResults</strong></a>(benchmark_metadata, options, value_can_be_added_predicate<font color="#909090">=&lt;function &lt;lambda&gt;&gt;</font>)</dt><dd><tt>Args:<br>
+&nbsp;&nbsp;options:&nbsp;Contains&nbsp;the&nbsp;options&nbsp;specified&nbsp;in&nbsp;AddResultsOptions.</tt></dd></dl>
+ <dl><dt><a name="-ProcessCommandLineArgs"><strong>ProcessCommandLineArgs</strong></a>(parser, args)</dt></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.results.story_run.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.results.story_run.html
new file mode 100644
index 0000000..f703b68
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.results.story_run.html
@@ -0,0 +1,83 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.results.story_run</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.results.html"><font color="#ffffff">results</font></a>.story_run</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/results/story_run.py">telemetry/internal/results/story_run.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2015&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.value.failure.html">telemetry.value.failure</a><br>
+</td><td width="25%" valign=top><a href="telemetry.value.skip.html">telemetry.value.skip</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.results.story_run.html#StoryRun">StoryRun</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="StoryRun">class <strong>StoryRun</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="StoryRun-AddValue"><strong>AddValue</strong></a>(self, value)</dt></dl>
+
+<dl><dt><a name="StoryRun-__init__"><strong>__init__</strong></a>(self, story)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>failed</strong></dt>
+<dd><tt>Whether&nbsp;the&nbsp;current&nbsp;run&nbsp;failed.<br>
+&nbsp;<br>
+To&nbsp;be&nbsp;precise:&nbsp;returns&nbsp;true&nbsp;if&nbsp;there&nbsp;is&nbsp;a&nbsp;FailureValue&nbsp;but&nbsp;not<br>
+SkipValue&nbsp;in&nbsp;self.<strong>values</strong>.</tt></dd>
+</dl>
+<dl><dt><strong>ok</strong></dt>
+<dd><tt>Whether&nbsp;the&nbsp;current&nbsp;run&nbsp;is&nbsp;still&nbsp;ok.<br>
+&nbsp;<br>
+To&nbsp;be&nbsp;precise:&nbsp;returns&nbsp;true&nbsp;if&nbsp;there&nbsp;is&nbsp;neither&nbsp;FailureValue&nbsp;nor<br>
+SkipValue&nbsp;in&nbsp;self.<strong>values</strong>.</tt></dd>
+</dl>
+<dl><dt><strong>skipped</strong></dt>
+<dd><tt>Whether&nbsp;the&nbsp;current&nbsp;run&nbsp;is&nbsp;being&nbsp;skipped.<br>
+&nbsp;<br>
+To&nbsp;be&nbsp;precise:&nbsp;returns&nbsp;true&nbsp;if&nbsp;there&nbsp;is&nbsp;any&nbsp;SkipValue&nbsp;in&nbsp;self.<strong>values</strong>.</tt></dd>
+</dl>
+<dl><dt><strong>story</strong></dt>
+</dl>
+<dl><dt><strong>values</strong></dt>
+<dd><tt>The&nbsp;values&nbsp;that&nbsp;correspond&nbsp;to&nbsp;this&nbsp;story&nbsp;run.</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.story_runner.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.story_runner.html
new file mode 100644
index 0000000..dabb2a7
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.story_runner.html
@@ -0,0 +1,178 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.story_runner</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.story_runner</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/story_runner.py">telemetry/internal/story_runner.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.browser.browser_finder.html">telemetry.internal.browser.browser_finder</a><br>
+<a href="catapult_base.cloud_storage.html">catapult_base.cloud_storage</a><br>
+<a href="telemetry.internal.util.exception_formatter.html">telemetry.internal.util.exception_formatter</a><br>
+<a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+<a href="telemetry.value.failure.html">telemetry.value.failure</a><br>
+</td><td width="25%" valign=top><a href="logging.html">logging</a><br>
+<a href="optparse.html">optparse</a><br>
+<a href="os.html">os</a><br>
+<a href="telemetry.page.html">telemetry.page</a><br>
+<a href="telemetry.internal.actions.page_action.html">telemetry.internal.actions.page_action</a><br>
+</td><td width="25%" valign=top><a href="telemetry.page.page_test.html">telemetry.page.page_test</a><br>
+<a href="telemetry.internal.results.results_options.html">telemetry.internal.results.results_options</a><br>
+<a href="telemetry.value.skip.html">telemetry.value.skip</a><br>
+<a href="telemetry.story.html">telemetry.story</a><br>
+<a href="telemetry.web_perf.story_test.html">telemetry.web_perf.story_test</a><br>
+</td><td width="25%" valign=top><a href="sys.html">sys</a><br>
+<a href="time.html">time</a><br>
+<a href="telemetry.util.wpr_modes.html">telemetry.util.wpr_modes</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.story_runner.html#StoryGroup">StoryGroup</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="exceptions.html#Exception">exceptions.Exception</a>(<a href="exceptions.html#BaseException">exceptions.BaseException</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.story_runner.html#ArchiveError">ArchiveError</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ArchiveError">class <strong>ArchiveError</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.story_runner.html#ArchiveError">ArchiveError</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="ArchiveError-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#ArchiveError-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#ArchiveError-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="ArchiveError-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#ArchiveError-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="ArchiveError-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#ArchiveError-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="ArchiveError-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#ArchiveError-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="ArchiveError-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#ArchiveError-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="ArchiveError-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="ArchiveError-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#ArchiveError-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="ArchiveError-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#ArchiveError-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="ArchiveError-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="ArchiveError-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#ArchiveError-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="ArchiveError-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="StoryGroup">class <strong>StoryGroup</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="StoryGroup-AddStory"><strong>AddStory</strong></a>(self, story)</dt></dl>
+
+<dl><dt><a name="StoryGroup-__init__"><strong>__init__</strong></a>(self, shared_state_class)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>shared_state_class</strong></dt>
+</dl>
+<dl><dt><strong>stories</strong></dt>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-AddCommandLineArgs"><strong>AddCommandLineArgs</strong></a>(parser)</dt></dl>
+ <dl><dt><a name="-ProcessCommandLineArgs"><strong>ProcessCommandLineArgs</strong></a>(parser, args)</dt></dl>
+ <dl><dt><a name="-Run"><strong>Run</strong></a>(test, story_set, finder_options, results, max_failures<font color="#909090">=None</font>)</dt><dd><tt>Runs&nbsp;a&nbsp;given&nbsp;test&nbsp;against&nbsp;a&nbsp;given&nbsp;page_set&nbsp;with&nbsp;the&nbsp;given&nbsp;options.<br>
+&nbsp;<br>
+Stop&nbsp;execution&nbsp;for&nbsp;unexpected&nbsp;exceptions&nbsp;such&nbsp;as&nbsp;KeyboardInterrupt.<br>
+We&nbsp;"white&nbsp;list"&nbsp;certain&nbsp;exceptions&nbsp;for&nbsp;which&nbsp;the&nbsp;story&nbsp;runner<br>
+can&nbsp;continue&nbsp;running&nbsp;the&nbsp;remaining&nbsp;stories.</tt></dd></dl>
+ <dl><dt><a name="-RunBenchmark"><strong>RunBenchmark</strong></a>(benchmark, finder_options)</dt><dd><tt>Run&nbsp;this&nbsp;test&nbsp;with&nbsp;the&nbsp;given&nbsp;options.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;The&nbsp;number&nbsp;of&nbsp;failure&nbsp;values&nbsp;(up&nbsp;to&nbsp;254)&nbsp;or&nbsp;255&nbsp;if&nbsp;there&nbsp;is&nbsp;an&nbsp;uncaught<br>
+&nbsp;&nbsp;exception.</tt></dd></dl>
+ <dl><dt><a name="-StoriesGroupedByStateClass"><strong>StoriesGroupedByStateClass</strong></a>(story_set, allow_multiple_groups)</dt><dd><tt>Returns&nbsp;a&nbsp;list&nbsp;of&nbsp;story&nbsp;groups&nbsp;which&nbsp;each&nbsp;contains&nbsp;stories&nbsp;with<br>
+the&nbsp;same&nbsp;shared_state_class.<br>
+&nbsp;<br>
+Example:<br>
+&nbsp;&nbsp;Assume&nbsp;A1,&nbsp;A2,&nbsp;A3&nbsp;are&nbsp;stories&nbsp;with&nbsp;same&nbsp;shared&nbsp;story&nbsp;class,&nbsp;and<br>
+&nbsp;&nbsp;similar&nbsp;for&nbsp;B1,&nbsp;B2.<br>
+&nbsp;&nbsp;If&nbsp;their&nbsp;orders&nbsp;in&nbsp;story&nbsp;set&nbsp;is&nbsp;A1&nbsp;A2&nbsp;B1&nbsp;B2&nbsp;A3,&nbsp;then&nbsp;the&nbsp;grouping&nbsp;will<br>
+&nbsp;&nbsp;be&nbsp;[A1&nbsp;A2]&nbsp;[B1&nbsp;B2]&nbsp;[A3].<br>
+&nbsp;<br>
+It's&nbsp;purposefully&nbsp;done&nbsp;this&nbsp;way&nbsp;to&nbsp;make&nbsp;sure&nbsp;that&nbsp;order&nbsp;of<br>
+stories&nbsp;are&nbsp;the&nbsp;same&nbsp;of&nbsp;that&nbsp;defined&nbsp;in&nbsp;story_set.&nbsp;It's&nbsp;recommended&nbsp;that<br>
+stories&nbsp;with&nbsp;the&nbsp;same&nbsp;states&nbsp;should&nbsp;be&nbsp;arranged&nbsp;next&nbsp;to&nbsp;each&nbsp;others&nbsp;in<br>
+story&nbsp;sets&nbsp;to&nbsp;reduce&nbsp;the&nbsp;overhead&nbsp;of&nbsp;setting&nbsp;up&nbsp;&amp;&nbsp;tearing&nbsp;down&nbsp;the<br>
+shared&nbsp;story&nbsp;state.</tt></dd></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.testing.discoverable_classes.another_discover_dummyclass.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.testing.discoverable_classes.another_discover_dummyclass.html
new file mode 100644
index 0000000..b821f2f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.testing.discoverable_classes.another_discover_dummyclass.html
@@ -0,0 +1,220 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.testing.discoverable_classes.another_discover_dummyclass</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.testing.html"><font color="#ffffff">testing</font></a>.<a href="telemetry.internal.testing.discoverable_classes.html"><font color="#ffffff">discoverable_classes</font></a>.another_discover_dummyclass</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/testing/discoverable_classes/another_discover_dummyclass.py">telemetry/internal/testing/discoverable_classes/another_discover_dummyclass.py</a></font></td></tr></table>
+    <p><tt>More&nbsp;dummy&nbsp;exception&nbsp;subclasses&nbsp;used&nbsp;by&nbsp;core/discover.py's&nbsp;unit&nbsp;tests.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.testing.discoverable_classes.discover_dummyclass.html">telemetry.internal.testing.discoverable_classes.discover_dummyclass</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.testing.discoverable_classes.another_discover_dummyclass.html#_PrivateDummyException">_PrivateDummyException</a>(<a href="telemetry.internal.testing.discoverable_classes.discover_dummyclass.html#DummyException">telemetry.internal.testing.discoverable_classes.discover_dummyclass.DummyException</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.testing.discoverable_classes.another_discover_dummyclass.html#DummyExceptionImpl1">DummyExceptionImpl1</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.internal.testing.discoverable_classes.another_discover_dummyclass.html#DummyExceptionImpl2">DummyExceptionImpl2</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.internal.testing.discoverable_classes.another_discover_dummyclass.html#DummyExceptionWithParameterImpl1">DummyExceptionWithParameterImpl1</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="DummyExceptionImpl1">class <strong>DummyExceptionImpl1</strong></a>(<a href="telemetry.internal.testing.discoverable_classes.another_discover_dummyclass.html#_PrivateDummyException">_PrivateDummyException</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.testing.discoverable_classes.another_discover_dummyclass.html#DummyExceptionImpl1">DummyExceptionImpl1</a></dd>
+<dd><a href="telemetry.internal.testing.discoverable_classes.another_discover_dummyclass.html#_PrivateDummyException">_PrivateDummyException</a></dd>
+<dd><a href="telemetry.internal.testing.discoverable_classes.discover_dummyclass.html#DummyException">telemetry.internal.testing.discoverable_classes.discover_dummyclass.DummyException</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="DummyExceptionImpl1-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.testing.discoverable_classes.discover_dummyclass.html#DummyException">telemetry.internal.testing.discoverable_classes.discover_dummyclass.DummyException</a>:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#DummyExceptionImpl1-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;object&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="DummyExceptionImpl1-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#DummyExceptionImpl1-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="DummyExceptionImpl1-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#DummyExceptionImpl1-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="DummyExceptionImpl1-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#DummyExceptionImpl1-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="DummyExceptionImpl1-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#DummyExceptionImpl1-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="DummyExceptionImpl1-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="DummyExceptionImpl1-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#DummyExceptionImpl1-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="DummyExceptionImpl1-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#DummyExceptionImpl1-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="DummyExceptionImpl1-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="DummyExceptionImpl1-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#DummyExceptionImpl1-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="DummyExceptionImpl1-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="DummyExceptionImpl2">class <strong>DummyExceptionImpl2</strong></a>(<a href="telemetry.internal.testing.discoverable_classes.another_discover_dummyclass.html#_PrivateDummyException">_PrivateDummyException</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.testing.discoverable_classes.another_discover_dummyclass.html#DummyExceptionImpl2">DummyExceptionImpl2</a></dd>
+<dd><a href="telemetry.internal.testing.discoverable_classes.another_discover_dummyclass.html#_PrivateDummyException">_PrivateDummyException</a></dd>
+<dd><a href="telemetry.internal.testing.discoverable_classes.discover_dummyclass.html#DummyException">telemetry.internal.testing.discoverable_classes.discover_dummyclass.DummyException</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="DummyExceptionImpl2-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.testing.discoverable_classes.discover_dummyclass.html#DummyException">telemetry.internal.testing.discoverable_classes.discover_dummyclass.DummyException</a>:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#DummyExceptionImpl2-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;object&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="DummyExceptionImpl2-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#DummyExceptionImpl2-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="DummyExceptionImpl2-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#DummyExceptionImpl2-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="DummyExceptionImpl2-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#DummyExceptionImpl2-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="DummyExceptionImpl2-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#DummyExceptionImpl2-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="DummyExceptionImpl2-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="DummyExceptionImpl2-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#DummyExceptionImpl2-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="DummyExceptionImpl2-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#DummyExceptionImpl2-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="DummyExceptionImpl2-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="DummyExceptionImpl2-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#DummyExceptionImpl2-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="DummyExceptionImpl2-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="DummyExceptionWithParameterImpl1">class <strong>DummyExceptionWithParameterImpl1</strong></a>(<a href="telemetry.internal.testing.discoverable_classes.another_discover_dummyclass.html#_PrivateDummyException">_PrivateDummyException</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.testing.discoverable_classes.another_discover_dummyclass.html#DummyExceptionWithParameterImpl1">DummyExceptionWithParameterImpl1</a></dd>
+<dd><a href="telemetry.internal.testing.discoverable_classes.another_discover_dummyclass.html#_PrivateDummyException">_PrivateDummyException</a></dd>
+<dd><a href="telemetry.internal.testing.discoverable_classes.discover_dummyclass.html#DummyException">telemetry.internal.testing.discoverable_classes.discover_dummyclass.DummyException</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="DummyExceptionWithParameterImpl1-__init__"><strong>__init__</strong></a>(self, parameter)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.testing.discoverable_classes.discover_dummyclass.html#DummyException">telemetry.internal.testing.discoverable_classes.discover_dummyclass.DummyException</a>:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#DummyExceptionWithParameterImpl1-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;object&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="DummyExceptionWithParameterImpl1-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#DummyExceptionWithParameterImpl1-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="DummyExceptionWithParameterImpl1-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#DummyExceptionWithParameterImpl1-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="DummyExceptionWithParameterImpl1-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#DummyExceptionWithParameterImpl1-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="DummyExceptionWithParameterImpl1-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#DummyExceptionWithParameterImpl1-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="DummyExceptionWithParameterImpl1-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="DummyExceptionWithParameterImpl1-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#DummyExceptionWithParameterImpl1-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="DummyExceptionWithParameterImpl1-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#DummyExceptionWithParameterImpl1-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="DummyExceptionWithParameterImpl1-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="DummyExceptionWithParameterImpl1-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#DummyExceptionWithParameterImpl1-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="DummyExceptionWithParameterImpl1-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.testing.discoverable_classes.discover_dummyclass.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.testing.discoverable_classes.discover_dummyclass.html
new file mode 100644
index 0000000..8349ad3
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.testing.discoverable_classes.discover_dummyclass.html
@@ -0,0 +1,88 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.testing.discoverable_classes.discover_dummyclass</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.testing.html"><font color="#ffffff">testing</font></a>.<a href="telemetry.internal.testing.discoverable_classes.html"><font color="#ffffff">discoverable_classes</font></a>.discover_dummyclass</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/testing/discoverable_classes/discover_dummyclass.py">telemetry/internal/testing/discoverable_classes/discover_dummyclass.py</a></font></td></tr></table>
+    <p><tt>A&nbsp;dummy&nbsp;exception&nbsp;subclass&nbsp;used&nbsp;by&nbsp;core/discover.py's&nbsp;unit&nbsp;tests.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="exceptions.html#Exception">exceptions.Exception</a>(<a href="exceptions.html#BaseException">exceptions.BaseException</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.testing.discoverable_classes.discover_dummyclass.html#DummyException">DummyException</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="DummyException">class <strong>DummyException</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.testing.discoverable_classes.discover_dummyclass.html#DummyException">DummyException</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="DummyException-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#DummyException-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;object&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="DummyException-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#DummyException-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="DummyException-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#DummyException-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="DummyException-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#DummyException-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="DummyException-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#DummyException-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="DummyException-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="DummyException-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#DummyException-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="DummyException-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#DummyException-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="DummyException-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="DummyException-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#DummyException-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="DummyException-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.testing.discoverable_classes.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.testing.discoverable_classes.html
new file mode 100644
index 0000000..c04239e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.testing.discoverable_classes.html
@@ -0,0 +1,27 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: package telemetry.internal.testing.discoverable_classes</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.testing.html"><font color="#ffffff">testing</font></a>.discoverable_classes</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/testing/discoverable_classes/__init__.py">telemetry/internal/testing/discoverable_classes/__init__.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Package Contents</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.testing.discoverable_classes.another_discover_dummyclass.html">another_discover_dummyclass</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.testing.discoverable_classes.discover_dummyclass.html">discover_dummyclass</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.testing.discoverable_classes.parameter_discover_dummyclass.html">parameter_discover_dummyclass</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.testing.discoverable_classes.parameter_discover_dummyclass.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.testing.discoverable_classes.parameter_discover_dummyclass.html
new file mode 100644
index 0000000..ab5a37d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.testing.discoverable_classes.parameter_discover_dummyclass.html
@@ -0,0 +1,97 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.testing.discoverable_classes.parameter_discover_dummyclass</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.testing.html"><font color="#ffffff">testing</font></a>.<a href="telemetry.internal.testing.discoverable_classes.html"><font color="#ffffff">discoverable_classes</font></a>.parameter_discover_dummyclass</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/testing/discoverable_classes/parameter_discover_dummyclass.py">telemetry/internal/testing/discoverable_classes/parameter_discover_dummyclass.py</a></font></td></tr></table>
+    <p><tt>A&nbsp;dummy&nbsp;exception&nbsp;subclass&nbsp;used&nbsp;by&nbsp;core/discover.py's&nbsp;unit&nbsp;tests.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.testing.discoverable_classes.discover_dummyclass.html">telemetry.internal.testing.discoverable_classes.discover_dummyclass</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.testing.discoverable_classes.discover_dummyclass.html#DummyException">telemetry.internal.testing.discoverable_classes.discover_dummyclass.DummyException</a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.testing.discoverable_classes.parameter_discover_dummyclass.html#DummyExceptionWithParameterImpl2">DummyExceptionWithParameterImpl2</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="DummyExceptionWithParameterImpl2">class <strong>DummyExceptionWithParameterImpl2</strong></a>(<a href="telemetry.internal.testing.discoverable_classes.discover_dummyclass.html#DummyException">telemetry.internal.testing.discoverable_classes.discover_dummyclass.DummyException</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.testing.discoverable_classes.parameter_discover_dummyclass.html#DummyExceptionWithParameterImpl2">DummyExceptionWithParameterImpl2</a></dd>
+<dd><a href="telemetry.internal.testing.discoverable_classes.discover_dummyclass.html#DummyException">telemetry.internal.testing.discoverable_classes.discover_dummyclass.DummyException</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="DummyExceptionWithParameterImpl2-__init__"><strong>__init__</strong></a>(self, parameter1, parameter2)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.testing.discoverable_classes.discover_dummyclass.html#DummyException">telemetry.internal.testing.discoverable_classes.discover_dummyclass.DummyException</a>:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#DummyExceptionWithParameterImpl2-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;object&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="DummyExceptionWithParameterImpl2-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#DummyExceptionWithParameterImpl2-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="DummyExceptionWithParameterImpl2-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#DummyExceptionWithParameterImpl2-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="DummyExceptionWithParameterImpl2-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#DummyExceptionWithParameterImpl2-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="DummyExceptionWithParameterImpl2-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#DummyExceptionWithParameterImpl2-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="DummyExceptionWithParameterImpl2-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="DummyExceptionWithParameterImpl2-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#DummyExceptionWithParameterImpl2-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="DummyExceptionWithParameterImpl2-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#DummyExceptionWithParameterImpl2-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="DummyExceptionWithParameterImpl2-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="DummyExceptionWithParameterImpl2-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#DummyExceptionWithParameterImpl2-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="DummyExceptionWithParameterImpl2-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.testing.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.testing.html
new file mode 100644
index 0000000..b7291d0
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.testing.html
@@ -0,0 +1,26 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: package telemetry.internal.testing</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.testing</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/testing/__init__.py">telemetry/internal/testing/__init__.py</a></font></td></tr></table>
+    <p></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Package Contents</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.testing.discoverable_classes.html"><strong>discoverable_classes</strong>&nbsp;(package)</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.testing.page_sets.html"><strong>page_sets</strong>&nbsp;(package)</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.testing.pages.html"><strong>pages</strong>&nbsp;(package)</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.testing.system_stub_test_module.html">system_stub_test_module</a><br>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.testing.page_sets.example_domain.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.testing.page_sets.example_domain.html
new file mode 100644
index 0000000..788e175
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.testing.page_sets.example_domain.html
@@ -0,0 +1,129 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.testing.page_sets.example_domain</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.testing.html"><font color="#ffffff">testing</font></a>.<a href="telemetry.internal.testing.page_sets.html"><font color="#ffffff">page_sets</font></a>.example_domain</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/testing/page_sets/example_domain.py">telemetry/internal/testing/page_sets/example_domain.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.page.page.html">telemetry.page.page</a><br>
+</td><td width="25%" valign=top><a href="telemetry.story.html">telemetry.story</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.story.story_set.html#StorySet">telemetry.story.story_set.StorySet</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.testing.page_sets.example_domain.html#ExampleDomainPageSet">ExampleDomainPageSet</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ExampleDomainPageSet">class <strong>ExampleDomainPageSet</strong></a>(<a href="telemetry.story.story_set.html#StorySet">telemetry.story.story_set.StorySet</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.testing.page_sets.example_domain.html#ExampleDomainPageSet">ExampleDomainPageSet</a></dd>
+<dd><a href="telemetry.story.story_set.html#StorySet">telemetry.story.story_set.StorySet</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="ExampleDomainPageSet-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.story.story_set.html#StorySet">telemetry.story.story_set.StorySet</a>:<br>
+<dl><dt><a name="ExampleDomainPageSet-AddStory"><strong>AddStory</strong></a>(self, story)</dt></dl>
+
+<dl><dt><a name="ExampleDomainPageSet-RemoveStory"><strong>RemoveStory</strong></a>(self, story)</dt><dd><tt>Removes&nbsp;a&nbsp;Story.<br>
+&nbsp;<br>
+Allows&nbsp;the&nbsp;stories&nbsp;to&nbsp;be&nbsp;filtered.</tt></dd></dl>
+
+<dl><dt><a name="ExampleDomainPageSet-WprFilePathForStory"><strong>WprFilePathForStory</strong></a>(self, story)</dt><dd><tt>Convenient&nbsp;function&nbsp;to&nbsp;retrieve&nbsp;WPR&nbsp;archive&nbsp;file&nbsp;path.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;story:&nbsp;The&nbsp;Story&nbsp;to&nbsp;look&nbsp;up.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;The&nbsp;WPR&nbsp;archive&nbsp;file&nbsp;path&nbsp;for&nbsp;the&nbsp;given&nbsp;Story,&nbsp;if&nbsp;found.<br>
+&nbsp;&nbsp;Otherwise,&nbsp;None.</tt></dd></dl>
+
+<dl><dt><a name="ExampleDomainPageSet-__getitem__"><strong>__getitem__</strong></a>(self, key)</dt></dl>
+
+<dl><dt><a name="ExampleDomainPageSet-__iter__"><strong>__iter__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="ExampleDomainPageSet-__len__"><strong>__len__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="ExampleDomainPageSet-__setitem__"><strong>__setitem__</strong></a>(self, key, value)</dt></dl>
+
+<hr>
+Class methods inherited from <a href="telemetry.story.story_set.html#StorySet">telemetry.story.story_set.StorySet</a>:<br>
+<dl><dt><a name="ExampleDomainPageSet-Description"><strong>Description</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Return&nbsp;a&nbsp;string&nbsp;explaining&nbsp;in&nbsp;human-understandable&nbsp;terms&nbsp;what&nbsp;this<br>
+story&nbsp;represents.<br>
+Note&nbsp;that&nbsp;this&nbsp;should&nbsp;be&nbsp;a&nbsp;classmethod&nbsp;so&nbsp;the&nbsp;benchmark_runner&nbsp;script&nbsp;can<br>
+display&nbsp;stories'&nbsp;names&nbsp;along&nbsp;with&nbsp;their&nbsp;descriptions&nbsp;in&nbsp;the&nbsp;list&nbsp;command.</tt></dd></dl>
+
+<dl><dt><a name="ExampleDomainPageSet-Name"><strong>Name</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Returns&nbsp;the&nbsp;string&nbsp;name&nbsp;of&nbsp;this&nbsp;<a href="telemetry.story.story_set.html#StorySet">StorySet</a>.<br>
+Note&nbsp;that&nbsp;this&nbsp;should&nbsp;be&nbsp;a&nbsp;classmethod&nbsp;so&nbsp;the&nbsp;benchmark_runner&nbsp;script&nbsp;can<br>
+match&nbsp;the&nbsp;story&nbsp;class&nbsp;with&nbsp;its&nbsp;name&nbsp;specified&nbsp;in&nbsp;the&nbsp;run&nbsp;command:<br>
+'Run&nbsp;&lt;User&nbsp;story&nbsp;test&nbsp;name&gt;&nbsp;&lt;User&nbsp;story&nbsp;class&nbsp;name&gt;'</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.story.story_set.html#StorySet">telemetry.story.story_set.StorySet</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>allow_mixed_story_states</strong></dt>
+<dd><tt>True&nbsp;iff&nbsp;Stories&nbsp;are&nbsp;allowed&nbsp;to&nbsp;have&nbsp;different&nbsp;StoryState&nbsp;classes.<br>
+&nbsp;<br>
+There&nbsp;are&nbsp;no&nbsp;checks&nbsp;in&nbsp;place&nbsp;for&nbsp;determining&nbsp;if&nbsp;SharedStates&nbsp;are<br>
+being&nbsp;assigned&nbsp;correctly&nbsp;to&nbsp;all&nbsp;Stories&nbsp;in&nbsp;a&nbsp;given&nbsp;StorySet.&nbsp;The<br>
+majority&nbsp;of&nbsp;test&nbsp;cases&nbsp;should&nbsp;not&nbsp;need&nbsp;the&nbsp;ability&nbsp;to&nbsp;have&nbsp;multiple<br>
+SharedStates,&nbsp;which&nbsp;usually&nbsp;implies&nbsp;you&nbsp;should&nbsp;be&nbsp;writing&nbsp;multiple<br>
+benchmarks&nbsp;instead.&nbsp;We&nbsp;provide&nbsp;errors&nbsp;to&nbsp;avoid&nbsp;accidentally&nbsp;assigning<br>
+or&nbsp;defaulting&nbsp;to&nbsp;the&nbsp;wrong&nbsp;SharedState.<br>
+Override&nbsp;at&nbsp;your&nbsp;own&nbsp;risk.&nbsp;Here&nbsp;be&nbsp;dragons.</tt></dd>
+</dl>
+<dl><dt><strong>archive_data_file</strong></dt>
+</dl>
+<dl><dt><strong>base_dir</strong></dt>
+<dd><tt>The&nbsp;base&nbsp;directory&nbsp;to&nbsp;resolve&nbsp;archive_data_file.<br>
+&nbsp;<br>
+This&nbsp;defaults&nbsp;to&nbsp;the&nbsp;directory&nbsp;containing&nbsp;the&nbsp;StorySet&nbsp;instance's&nbsp;class.</tt></dd>
+</dl>
+<dl><dt><strong>bucket</strong></dt>
+</dl>
+<dl><dt><strong>file_path</strong></dt>
+</dl>
+<dl><dt><strong>serving_dirs</strong></dt>
+</dl>
+<dl><dt><strong>wpr_archive_info</strong></dt>
+<dd><tt>Lazily&nbsp;constructs&nbsp;wpr_archive_info&nbsp;if&nbsp;it's&nbsp;not&nbsp;set&nbsp;and&nbsp;returns&nbsp;it.</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.testing.page_sets.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.testing.page_sets.html
new file mode 100644
index 0000000..5ce64b4
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.testing.page_sets.html
@@ -0,0 +1,23 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: package telemetry.internal.testing.page_sets</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.testing.html"><font color="#ffffff">testing</font></a>.page_sets</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/testing/page_sets/__init__.py">telemetry/internal/testing/page_sets/__init__.py</a></font></td></tr></table>
+    <p></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Package Contents</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.testing.page_sets.example_domain.html">example_domain</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.testing.pages.external_page.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.testing.pages.external_page.html
new file mode 100644
index 0000000..5f0c00c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.testing.pages.external_page.html
@@ -0,0 +1,130 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.testing.pages.external_page</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.testing.html"><font color="#ffffff">testing</font></a>.<a href="telemetry.internal.testing.pages.html"><font color="#ffffff">pages</font></a>.external_page</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/testing/pages/external_page.py">telemetry/internal/testing/pages/external_page.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.page.html#Page">telemetry.page.Page</a>(<a href="telemetry.story.story.html#Story">telemetry.story.story.Story</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.testing.pages.external_page.html#ExternalPage">ExternalPage</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ExternalPage">class <strong>ExternalPage</strong></a>(<a href="telemetry.page.html#Page">telemetry.page.Page</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.testing.pages.external_page.html#ExternalPage">ExternalPage</a></dd>
+<dd><a href="telemetry.page.html#Page">telemetry.page.Page</a></dd>
+<dd><a href="telemetry.story.story.html#Story">telemetry.story.story.Story</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="ExternalPage-__init__"><strong>__init__</strong></a>(self, ps)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.page.html#Page">telemetry.page.Page</a>:<br>
+<dl><dt><a name="ExternalPage-AddCustomizeBrowserOptions"><strong>AddCustomizeBrowserOptions</strong></a>(self, options)</dt><dd><tt>Inherit&nbsp;page&nbsp;overrides&nbsp;this&nbsp;to&nbsp;add&nbsp;customized&nbsp;browser&nbsp;options.</tt></dd></dl>
+
+<dl><dt><a name="ExternalPage-AsDict"><strong>AsDict</strong></a>(self)</dt><dd><tt>Converts&nbsp;a&nbsp;page&nbsp;object&nbsp;to&nbsp;a&nbsp;dict&nbsp;suitable&nbsp;for&nbsp;JSON&nbsp;output.</tt></dd></dl>
+
+<dl><dt><a name="ExternalPage-GetSyntheticDelayCategories"><strong>GetSyntheticDelayCategories</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="ExternalPage-Run"><strong>Run</strong></a>(self, shared_state)</dt></dl>
+
+<dl><dt><a name="ExternalPage-RunNavigateSteps"><strong>RunNavigateSteps</strong></a>(self, action_runner)</dt></dl>
+
+<dl><dt><a name="ExternalPage-RunPageInteractions"><strong>RunPageInteractions</strong></a>(self, action_runner)</dt><dd><tt>Override&nbsp;this&nbsp;to&nbsp;define&nbsp;custom&nbsp;interactions&nbsp;with&nbsp;the&nbsp;page.<br>
+e.g:<br>
+&nbsp;&nbsp;def&nbsp;<a href="#ExternalPage-RunPageInteractions">RunPageInteractions</a>(self,&nbsp;action_runner):<br>
+&nbsp;&nbsp;&nbsp;&nbsp;action_runner.ScrollPage()<br>
+&nbsp;&nbsp;&nbsp;&nbsp;action_runner.TapElement(text='Next')</tt></dd></dl>
+
+<dl><dt><a name="ExternalPage-__cmp__"><strong>__cmp__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="ExternalPage-__lt__"><strong>__lt__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="ExternalPage-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.page.html#Page">telemetry.page.Page</a>:<br>
+<dl><dt><strong>base_dir</strong></dt>
+</dl>
+<dl><dt><strong>credentials_path</strong></dt>
+</dl>
+<dl><dt><strong>display_name</strong></dt>
+</dl>
+<dl><dt><strong>file_path</strong></dt>
+<dd><tt>Returns&nbsp;the&nbsp;path&nbsp;of&nbsp;the&nbsp;file,&nbsp;stripping&nbsp;the&nbsp;scheme&nbsp;and&nbsp;query&nbsp;string.</tt></dd>
+</dl>
+<dl><dt><strong>file_path_url</strong></dt>
+<dd><tt>Returns&nbsp;the&nbsp;file&nbsp;path,&nbsp;including&nbsp;the&nbsp;params,&nbsp;query,&nbsp;and&nbsp;fragment.</tt></dd>
+</dl>
+<dl><dt><strong>file_path_url_with_scheme</strong></dt>
+</dl>
+<dl><dt><strong>is_file</strong></dt>
+<dd><tt>Returns&nbsp;True&nbsp;iff&nbsp;this&nbsp;URL&nbsp;points&nbsp;to&nbsp;a&nbsp;file.</tt></dd>
+</dl>
+<dl><dt><strong>page_set</strong></dt>
+</dl>
+<dl><dt><strong>serving_dir</strong></dt>
+</dl>
+<dl><dt><strong>startup_url</strong></dt>
+</dl>
+<dl><dt><strong>story_set</strong></dt>
+</dl>
+<dl><dt><strong>url</strong></dt>
+</dl>
+<hr>
+Data descriptors inherited from <a href="telemetry.story.story.html#Story">telemetry.story.story.Story</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>file_safe_name</strong></dt>
+<dd><tt>A&nbsp;version&nbsp;of&nbsp;display_name&nbsp;that's&nbsp;safe&nbsp;to&nbsp;use&nbsp;as&nbsp;a&nbsp;filename.<br>
+&nbsp;<br>
+The&nbsp;default&nbsp;implementation&nbsp;sanitizes&nbsp;special&nbsp;characters&nbsp;with&nbsp;underscores,<br>
+but&nbsp;it's&nbsp;okay&nbsp;to&nbsp;override&nbsp;it&nbsp;with&nbsp;a&nbsp;more&nbsp;specific&nbsp;implementation&nbsp;in<br>
+subclasses.</tt></dd>
+</dl>
+<dl><dt><strong>id</strong></dt>
+</dl>
+<dl><dt><strong>is_local</strong></dt>
+<dd><tt>Returns&nbsp;True&nbsp;iff&nbsp;this&nbsp;story&nbsp;does&nbsp;not&nbsp;require&nbsp;network.</tt></dd>
+</dl>
+<dl><dt><strong>labels</strong></dt>
+</dl>
+<dl><dt><strong>make_javascript_deterministic</strong></dt>
+</dl>
+<dl><dt><strong>name</strong></dt>
+</dl>
+<dl><dt><strong>shared_state_class</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.testing.pages.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.testing.pages.html
new file mode 100644
index 0000000..a21c90c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.testing.pages.html
@@ -0,0 +1,23 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: package telemetry.internal.testing.pages</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.testing.html"><font color="#ffffff">testing</font></a>.pages</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/testing/pages/__init__.py">telemetry/internal/testing/pages/__init__.py</a></font></td></tr></table>
+    <p></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Package Contents</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.testing.pages.external_page.html">external_page</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.testing.system_stub_test_module.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.testing.system_stub_test_module.html
new file mode 100644
index 0000000..f5969be
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.testing.system_stub_test_module.html
@@ -0,0 +1,54 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.testing.system_stub_test_module</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.testing.html"><font color="#ffffff">testing</font></a>.system_stub_test_module</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/testing/system_stub_test_module.py">telemetry/internal/testing/system_stub_test_module.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2015&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.testing.system_stub_test_module.html#SystemStubTest">SystemStubTest</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="SystemStubTest">class <strong>SystemStubTest</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>#&nbsp;Copyright&nbsp;2015&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Static methods defined here:<br>
+<dl><dt><a name="SystemStubTest-TestOpen"><strong>TestOpen</strong></a>(file_path)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.util.binary_manager.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.util.binary_manager.html
new file mode 100644
index 0000000..3ad540f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.util.binary_manager.html
@@ -0,0 +1,49 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.util.binary_manager</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.util.html"><font color="#ffffff">util</font></a>.binary_manager</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/util/binary_manager.py">telemetry/internal/util/binary_manager.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2015&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="catapult_base.dependency_manager.html">catapult_base.dependency_manager</a><br>
+<a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+</td><td width="25%" valign=top><a href="logging.html">logging</a><br>
+<a href="os.html">os</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.util.html">telemetry.core.util</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-FetchPath"><strong>FetchPath</strong></a>(binary_name, arch, platform)</dt><dd><tt>Return&nbsp;a&nbsp;path&nbsp;to&nbsp;the&nbsp;appropriate&nbsp;executable&nbsp;for&nbsp;&lt;binary_name&gt;,&nbsp;downloading<br>
+from&nbsp;cloud&nbsp;storage&nbsp;if&nbsp;needed,&nbsp;or&nbsp;None&nbsp;if&nbsp;it&nbsp;cannot&nbsp;be&nbsp;found.</tt></dd></dl>
+ <dl><dt><a name="-InitDependencyManager"><strong>InitDependencyManager</strong></a>(environment_config)</dt></dl>
+ <dl><dt><a name="-LocalPath"><strong>LocalPath</strong></a>(binary_name, arch, platform)</dt><dd><tt>Return&nbsp;a&nbsp;local&nbsp;path&nbsp;to&nbsp;the&nbsp;given&nbsp;binary&nbsp;name,&nbsp;or&nbsp;None&nbsp;if&nbsp;an&nbsp;executable<br>
+cannot&nbsp;be&nbsp;found.&nbsp;Will&nbsp;not&nbsp;download&nbsp;the&nbsp;executable.</tt></dd></dl>
+ <dl><dt><a name="-NeedsInit"><strong>NeedsInit</strong></a>()</dt></dl>
+</td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>TELEMETRY_PROJECT_CONFIG</strong> = '/usr/local/google/home/nednguyen/projects/chromi...metry/telemetry/internal/binary_dependencies.json'</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.util.bootstrap.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.util.bootstrap.html
new file mode 100644
index 0000000..c025b3c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.util.bootstrap.html
@@ -0,0 +1,124 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.util.bootstrap</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.util.html"><font color="#ffffff">util</font></a>.bootstrap</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/util/bootstrap.py">telemetry/internal/util/bootstrap.py</a></font></td></tr></table>
+    <p><tt>Bootstrap&nbsp;Chrome&nbsp;Telemetry&nbsp;by&nbsp;downloading&nbsp;all&nbsp;its&nbsp;files&nbsp;from&nbsp;SVN&nbsp;servers.<br>
+&nbsp;<br>
+Requires&nbsp;a&nbsp;DEPS&nbsp;file&nbsp;to&nbsp;specify&nbsp;which&nbsp;directories&nbsp;on&nbsp;which&nbsp;SVN&nbsp;servers<br>
+are&nbsp;required&nbsp;to&nbsp;run&nbsp;Telemetry.&nbsp;Format&nbsp;of&nbsp;that&nbsp;DEPS&nbsp;file&nbsp;is&nbsp;a&nbsp;subset&nbsp;of&nbsp;the<br>
+normal&nbsp;DEPS&nbsp;file&nbsp;format[1];&nbsp;currently&nbsp;only&nbsp;only&nbsp;the&nbsp;"deps"&nbsp;dictionary&nbsp;is<br>
+supported&nbsp;and&nbsp;nothing&nbsp;else.<br>
+&nbsp;<br>
+Fetches&nbsp;all&nbsp;files&nbsp;in&nbsp;the&nbsp;specified&nbsp;directories&nbsp;using&nbsp;WebDAV&nbsp;(SVN&nbsp;is&nbsp;WebDAV&nbsp;under<br>
+the&nbsp;hood).<br>
+&nbsp;<br>
+[1]&nbsp;<a href="http://dev.chromium.org/developers/how-tos/depottools#TOC-DEPS-file">http://dev.chromium.org/developers/how-tos/depottools#TOC-DEPS-file</a></tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="imp.html">imp</a><br>
+<a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top><a href="os.html">os</a><br>
+<a href="urllib.html">urllib</a><br>
+</td><td width="25%" valign=top><a href="urlparse.html">urlparse</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.util.bootstrap.html#DAVClientWrapper">DAVClientWrapper</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="DAVClientWrapper">class <strong>DAVClientWrapper</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Knows&nbsp;how&nbsp;to&nbsp;retrieve&nbsp;subdirectories&nbsp;and&nbsp;files&nbsp;from&nbsp;WebDAV/SVN&nbsp;servers.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="DAVClientWrapper-GetDirList"><strong>GetDirList</strong></a>(self, path)</dt><dd><tt>Returns&nbsp;string&nbsp;names&nbsp;of&nbsp;all&nbsp;files&nbsp;and&nbsp;subdirs&nbsp;of&nbsp;path&nbsp;on&nbsp;the&nbsp;server.</tt></dd></dl>
+
+<dl><dt><a name="DAVClientWrapper-IsFile"><strong>IsFile</strong></a>(self, path)</dt><dd><tt>Returns&nbsp;True&nbsp;if&nbsp;the&nbsp;path&nbsp;is&nbsp;a&nbsp;file&nbsp;on&nbsp;the&nbsp;server,&nbsp;False&nbsp;if&nbsp;directory.</tt></dd></dl>
+
+<dl><dt><a name="DAVClientWrapper-Traverse"><strong>Traverse</strong></a>(self, src_path, dst_path)</dt><dd><tt>Walks&nbsp;the&nbsp;directory&nbsp;hierarchy&nbsp;pointed&nbsp;to&nbsp;by&nbsp;src_path&nbsp;download&nbsp;all&nbsp;files.<br>
+&nbsp;<br>
+Recursively&nbsp;walks&nbsp;src_path&nbsp;and&nbsp;saves&nbsp;all&nbsp;files&nbsp;and&nbsp;subfolders&nbsp;into<br>
+dst_path.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;src_path:&nbsp;string&nbsp;path&nbsp;on&nbsp;SVN&nbsp;server&nbsp;to&nbsp;save&nbsp;(absolute&nbsp;path&nbsp;on&nbsp;server).<br>
+&nbsp;&nbsp;dest_path:&nbsp;string&nbsp;local&nbsp;path&nbsp;(relative&nbsp;or&nbsp;absolute)&nbsp;to&nbsp;save&nbsp;to.</tt></dd></dl>
+
+<dl><dt><a name="DAVClientWrapper-__init__"><strong>__init__</strong></a>(self, root_url)</dt><dd><tt>Initialize&nbsp;SVN&nbsp;server&nbsp;root_url,&nbsp;save&nbsp;files&nbsp;to&nbsp;local&nbsp;dest_dir.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;root_url:&nbsp;string&nbsp;url&nbsp;of&nbsp;SVN/WebDAV&nbsp;server</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-DownloadDeps"><strong>DownloadDeps</strong></a>(destination_dir, url)</dt><dd><tt>Saves&nbsp;all&nbsp;the&nbsp;dependencies&nbsp;in&nbsp;deps_path.<br>
+&nbsp;<br>
+Opens&nbsp;and&nbsp;reads&nbsp;url,&nbsp;assuming&nbsp;the&nbsp;contents&nbsp;are&nbsp;in&nbsp;the&nbsp;simple&nbsp;DEPS-like&nbsp;file<br>
+format&nbsp;specified&nbsp;in&nbsp;the&nbsp;header&nbsp;of&nbsp;this&nbsp;file,&nbsp;then&nbsp;download&nbsp;all<br>
+files/directories&nbsp;listed&nbsp;to&nbsp;the&nbsp;destination_dir.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;destination_dir:&nbsp;String&nbsp;path&nbsp;to&nbsp;directory&nbsp;to&nbsp;download&nbsp;files&nbsp;into.<br>
+&nbsp;&nbsp;url:&nbsp;URL&nbsp;containing&nbsp;deps&nbsp;information&nbsp;to&nbsp;be&nbsp;evaluated.</tt></dd></dl>
+ <dl><dt><a name="-ListAllDepsPaths"><strong>ListAllDepsPaths</strong></a>(deps_file)</dt><dd><tt>Recursively&nbsp;returns&nbsp;a&nbsp;list&nbsp;of&nbsp;all&nbsp;paths&nbsp;indicated&nbsp;in&nbsp;this&nbsp;deps&nbsp;file.<br>
+&nbsp;<br>
+Note&nbsp;that&nbsp;this&nbsp;discards&nbsp;information&nbsp;about&nbsp;where&nbsp;path&nbsp;dependencies&nbsp;come&nbsp;from,<br>
+so&nbsp;this&nbsp;is&nbsp;only&nbsp;useful&nbsp;in&nbsp;the&nbsp;context&nbsp;of&nbsp;a&nbsp;Chromium&nbsp;source&nbsp;checkout&nbsp;that&nbsp;has<br>
+already&nbsp;fetched&nbsp;all&nbsp;dependencies.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;deps_file:&nbsp;File&nbsp;containing&nbsp;deps&nbsp;information&nbsp;to&nbsp;be&nbsp;evaluated,&nbsp;in&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;format&nbsp;given&nbsp;in&nbsp;the&nbsp;header&nbsp;of&nbsp;this&nbsp;file.<br>
+Returns:<br>
+&nbsp;&nbsp;A&nbsp;list&nbsp;of&nbsp;string&nbsp;paths&nbsp;starting&nbsp;under&nbsp;src&nbsp;that&nbsp;are&nbsp;required&nbsp;by&nbsp;the<br>
+&nbsp;&nbsp;given&nbsp;deps&nbsp;file,&nbsp;and&nbsp;all&nbsp;of&nbsp;its&nbsp;sub-dependencies.&nbsp;This&nbsp;amounts&nbsp;to<br>
+&nbsp;&nbsp;the&nbsp;keys&nbsp;of&nbsp;the&nbsp;'deps'&nbsp;dictionary.</tt></dd></dl>
+</td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>davclient</strong> = None</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.util.camel_case.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.util.camel_case.html
new file mode 100644
index 0000000..5aeb7cc
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.util.camel_case.html
@@ -0,0 +1,36 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.util.camel_case</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.util.html"><font color="#ffffff">util</font></a>.camel_case</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/util/camel_case.py">telemetry/internal/util/camel_case.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="re.html">re</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-ToUnderscore"><strong>ToUnderscore</strong></a>(obj)</dt><dd><tt>Converts&nbsp;a&nbsp;string,&nbsp;list,&nbsp;or&nbsp;dict&nbsp;from&nbsp;camelCase&nbsp;to&nbsp;lower_with_underscores.<br>
+&nbsp;<br>
+Descends&nbsp;recursively&nbsp;into&nbsp;lists&nbsp;and&nbsp;dicts,&nbsp;converting&nbsp;all&nbsp;dict&nbsp;keys.<br>
+Returns&nbsp;a&nbsp;newly&nbsp;allocated&nbsp;object&nbsp;of&nbsp;the&nbsp;same&nbsp;structure&nbsp;as&nbsp;the&nbsp;input.</tt></dd></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.util.classes.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.util.classes.html
new file mode 100644
index 0000000..edac79e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.util.classes.html
@@ -0,0 +1,33 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.util.classes</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.util.html"><font color="#ffffff">util</font></a>.classes</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/util/classes.py">telemetry/internal/util/classes.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="inspect.html">inspect</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-IsDirectlyConstructable"><strong>IsDirectlyConstructable</strong></a>(cls)</dt><dd><tt>Returns&nbsp;True&nbsp;if&nbsp;instance&nbsp;of&nbsp;|cls|&nbsp;can&nbsp;be&nbsp;construct&nbsp;without&nbsp;arguments.</tt></dd></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.util.command_line.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.util.command_line.html
new file mode 100644
index 0000000..e72ea18
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.util.command_line.html
@@ -0,0 +1,243 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.util.command_line</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.util.html"><font color="#ffffff">util</font></a>.command_line</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/util/command_line.py">telemetry/internal/util/command_line.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="argparse.html">argparse</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.util.camel_case.html">telemetry.internal.util.camel_case</a><br>
+</td><td width="25%" valign=top><a href="difflib.html">difflib</a><br>
+</td><td width="25%" valign=top><a href="optparse.html">optparse</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.util.command_line.html#ArgumentHandlerMixIn">ArgumentHandlerMixIn</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.util.command_line.html#Command">Command</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.util.command_line.html#OptparseCommand">OptparseCommand</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.internal.util.command_line.html#SubcommandCommand">SubcommandCommand</a>
+</font></dt></dl>
+</dd>
+</dl>
+</dd>
+</dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ArgumentHandlerMixIn">class <strong>ArgumentHandlerMixIn</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;structured&nbsp;way&nbsp;to&nbsp;handle&nbsp;command-line&nbsp;arguments.<br>
+&nbsp;<br>
+In&nbsp;AddCommandLineArgs,&nbsp;add&nbsp;command-line&nbsp;arguments.<br>
+In&nbsp;ProcessCommandLineArgs,&nbsp;validate&nbsp;them&nbsp;and&nbsp;store&nbsp;them&nbsp;in&nbsp;a&nbsp;private&nbsp;class<br>
+variable.&nbsp;This&nbsp;way,&nbsp;each&nbsp;class&nbsp;encapsulates&nbsp;its&nbsp;own&nbsp;arguments,&nbsp;without&nbsp;needing<br>
+to&nbsp;pass&nbsp;an&nbsp;arguments&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;around&nbsp;everywhere.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Class methods defined here:<br>
+<dl><dt><a name="ArgumentHandlerMixIn-AddCommandLineArgs"><strong>AddCommandLineArgs</strong></a>(cls, parser)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Override&nbsp;to&nbsp;accept&nbsp;custom&nbsp;command-line&nbsp;arguments.</tt></dd></dl>
+
+<dl><dt><a name="ArgumentHandlerMixIn-ProcessCommandLineArgs"><strong>ProcessCommandLineArgs</strong></a>(cls, parser, args)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Override&nbsp;to&nbsp;process&nbsp;command-line&nbsp;arguments.<br>
+&nbsp;<br>
+We&nbsp;pass&nbsp;in&nbsp;parser&nbsp;so&nbsp;we&nbsp;can&nbsp;call&nbsp;parser.error().</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="Command">class <strong>Command</strong></a>(<a href="telemetry.internal.util.command_line.html#ArgumentHandlerMixIn">ArgumentHandlerMixIn</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>An&nbsp;abstraction&nbsp;for&nbsp;things&nbsp;that&nbsp;run&nbsp;from&nbsp;the&nbsp;command-line.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.util.command_line.html#Command">Command</a></dd>
+<dd><a href="telemetry.internal.util.command_line.html#ArgumentHandlerMixIn">ArgumentHandlerMixIn</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="Command-Run"><strong>Run</strong></a>(self, args)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="Command-Description"><strong>Description</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="Command-Name"><strong>Name</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="Command-main"><strong>main</strong></a>(cls, args<font color="#909090">=None</font>)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Main&nbsp;method&nbsp;to&nbsp;run&nbsp;this&nbsp;command&nbsp;as&nbsp;a&nbsp;standalone&nbsp;script.</tt></dd></dl>
+
+<hr>
+Class methods inherited from <a href="telemetry.internal.util.command_line.html#ArgumentHandlerMixIn">ArgumentHandlerMixIn</a>:<br>
+<dl><dt><a name="Command-AddCommandLineArgs"><strong>AddCommandLineArgs</strong></a>(cls, parser)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Override&nbsp;to&nbsp;accept&nbsp;custom&nbsp;command-line&nbsp;arguments.</tt></dd></dl>
+
+<dl><dt><a name="Command-ProcessCommandLineArgs"><strong>ProcessCommandLineArgs</strong></a>(cls, parser, args)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Override&nbsp;to&nbsp;process&nbsp;command-line&nbsp;arguments.<br>
+&nbsp;<br>
+We&nbsp;pass&nbsp;in&nbsp;parser&nbsp;so&nbsp;we&nbsp;can&nbsp;call&nbsp;parser.error().</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.util.command_line.html#ArgumentHandlerMixIn">ArgumentHandlerMixIn</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="OptparseCommand">class <strong>OptparseCommand</strong></a>(<a href="telemetry.internal.util.command_line.html#Command">Command</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>#&nbsp;TODO:&nbsp;Convert&nbsp;everything&nbsp;to&nbsp;argparse.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.util.command_line.html#OptparseCommand">OptparseCommand</a></dd>
+<dd><a href="telemetry.internal.util.command_line.html#Command">Command</a></dd>
+<dd><a href="telemetry.internal.util.command_line.html#ArgumentHandlerMixIn">ArgumentHandlerMixIn</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="OptparseCommand-Run"><strong>Run</strong></a>(self, args)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="OptparseCommand-AddCommandLineArgs"><strong>AddCommandLineArgs</strong></a>(cls, parser, environment)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="OptparseCommand-CreateParser"><strong>CreateParser</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="OptparseCommand-ProcessCommandLineArgs"><strong>ProcessCommandLineArgs</strong></a>(cls, parser, args, environment)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="OptparseCommand-main"><strong>main</strong></a>(cls, args<font color="#909090">=None</font>)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Main&nbsp;method&nbsp;to&nbsp;run&nbsp;this&nbsp;command&nbsp;as&nbsp;a&nbsp;standalone&nbsp;script.</tt></dd></dl>
+
+<hr>
+Data and other attributes defined here:<br>
+<dl><dt><strong>usage</strong> = ''</dl>
+
+<hr>
+Class methods inherited from <a href="telemetry.internal.util.command_line.html#Command">Command</a>:<br>
+<dl><dt><a name="OptparseCommand-Description"><strong>Description</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="OptparseCommand-Name"><strong>Name</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.util.command_line.html#ArgumentHandlerMixIn">ArgumentHandlerMixIn</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="SubcommandCommand">class <strong>SubcommandCommand</strong></a>(<a href="telemetry.internal.util.command_line.html#Command">Command</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Combines&nbsp;Commands&nbsp;into&nbsp;one&nbsp;big&nbsp;command&nbsp;with&nbsp;sub-commands.<br>
+&nbsp;<br>
+E.g.&nbsp;"svn&nbsp;checkout",&nbsp;"svn&nbsp;update",&nbsp;and&nbsp;"svn&nbsp;commit"&nbsp;are&nbsp;separate&nbsp;sub-commands.<br>
+&nbsp;<br>
+Example&nbsp;usage:<br>
+&nbsp;&nbsp;class&nbsp;MyCommand(command_line.<a href="#SubcommandCommand">SubcommandCommand</a>):<br>
+&nbsp;&nbsp;&nbsp;&nbsp;commands&nbsp;=&nbsp;(Help,&nbsp;List,&nbsp;Run)<br>
+&nbsp;<br>
+&nbsp;&nbsp;if&nbsp;__name__&nbsp;==&nbsp;'__main__':<br>
+&nbsp;&nbsp;&nbsp;&nbsp;sys.exit(MyCommand.<a href="#SubcommandCommand-main">main</a>())<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.util.command_line.html#SubcommandCommand">SubcommandCommand</a></dd>
+<dd><a href="telemetry.internal.util.command_line.html#Command">Command</a></dd>
+<dd><a href="telemetry.internal.util.command_line.html#ArgumentHandlerMixIn">ArgumentHandlerMixIn</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="SubcommandCommand-Run"><strong>Run</strong></a>(self, args)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="SubcommandCommand-AddCommandLineArgs"><strong>AddCommandLineArgs</strong></a>(cls, parser)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="SubcommandCommand-ProcessCommandLineArgs"><strong>ProcessCommandLineArgs</strong></a>(cls, parser, args)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Data and other attributes defined here:<br>
+<dl><dt><strong>commands</strong> = ()</dl>
+
+<hr>
+Class methods inherited from <a href="telemetry.internal.util.command_line.html#Command">Command</a>:<br>
+<dl><dt><a name="SubcommandCommand-Description"><strong>Description</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="SubcommandCommand-Name"><strong>Name</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="SubcommandCommand-main"><strong>main</strong></a>(cls, args<font color="#909090">=None</font>)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Main&nbsp;method&nbsp;to&nbsp;run&nbsp;this&nbsp;command&nbsp;as&nbsp;a&nbsp;standalone&nbsp;script.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.util.command_line.html#ArgumentHandlerMixIn">ArgumentHandlerMixIn</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-GetMostLikelyMatchedObject"><strong>GetMostLikelyMatchedObject</strong></a>(objects, target_name, name_func<font color="#909090">=&lt;function &lt;lambda&gt;&gt;</font>, matched_score_threshold<font color="#909090">=0.4</font>)</dt><dd><tt>Matches&nbsp;objects&nbsp;whose&nbsp;names&nbsp;are&nbsp;most&nbsp;likely&nbsp;matched&nbsp;with&nbsp;target.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;objects:&nbsp;list&nbsp;of&nbsp;objects&nbsp;to&nbsp;match.<br>
+&nbsp;&nbsp;target_name:&nbsp;name&nbsp;to&nbsp;match.<br>
+&nbsp;&nbsp;name_func:&nbsp;function&nbsp;to&nbsp;get&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;name&nbsp;to&nbsp;match.&nbsp;Default&nbsp;bypass.<br>
+&nbsp;&nbsp;matched_score_threshold:&nbsp;threshold&nbsp;of&nbsp;likelihood&nbsp;to&nbsp;match.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;A&nbsp;list&nbsp;of&nbsp;objects&nbsp;whose&nbsp;names&nbsp;are&nbsp;likely&nbsp;target_name.</tt></dd></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.util.exception_formatter.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.util.exception_formatter.html
new file mode 100644
index 0000000..f740f7f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.util.exception_formatter.html
@@ -0,0 +1,37 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.util.exception_formatter</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.util.html"><font color="#ffffff">util</font></a>.exception_formatter</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/util/exception_formatter.py">telemetry/internal/util/exception_formatter.py</a></font></td></tr></table>
+    <p><tt>Print&nbsp;prettier&nbsp;and&nbsp;more&nbsp;detailed&nbsp;exceptions.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+<a href="math.html">math</a><br>
+</td><td width="25%" valign=top><a href="os.html">os</a><br>
+<a href="sys.html">sys</a><br>
+</td><td width="25%" valign=top><a href="traceback.html">traceback</a><br>
+<a href="telemetry.core.util.html">telemetry.core.util</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-PrintFormattedException"><strong>PrintFormattedException</strong></a>(exception_class<font color="#909090">=None</font>, exception<font color="#909090">=None</font>, tb<font color="#909090">=None</font>, msg<font color="#909090">=None</font>)</dt></dl>
+ <dl><dt><a name="-PrintFormattedFrame"><strong>PrintFormattedFrame</strong></a>(frame, exception_string<font color="#909090">=None</font>)</dt></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.util.external_modules.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.util.external_modules.html
new file mode 100644
index 0000000..88bc19c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.util.external_modules.html
@@ -0,0 +1,50 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.util.external_modules</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.util.html"><font color="#ffffff">util</font></a>.external_modules</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/util/external_modules.py">telemetry/internal/util/external_modules.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="importlib.html">importlib</a><br>
+</td><td width="25%" valign=top><a href="distutils.version.html">distutils.version</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-ImportOptionalModule"><strong>ImportOptionalModule</strong></a>(module)</dt><dd><tt>Tries&nbsp;to&nbsp;import&nbsp;the&nbsp;desired&nbsp;module.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;The&nbsp;module&nbsp;if&nbsp;successful,&nbsp;None&nbsp;if&nbsp;not.</tt></dd></dl>
+ <dl><dt><a name="-ImportRequiredModule"><strong>ImportRequiredModule</strong></a>(module)</dt><dd><tt>Tries&nbsp;to&nbsp;import&nbsp;the&nbsp;desired&nbsp;module.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;The&nbsp;module&nbsp;on&nbsp;success,&nbsp;raises&nbsp;error&nbsp;on&nbsp;failure.<br>
+Raises:<br>
+&nbsp;&nbsp;ImportError:&nbsp;The&nbsp;import&nbsp;failed.</tt></dd></dl>
+</td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>MODULES</strong> = {'cv2': (StrictVersion ('2.4.8'), StrictVersion ('3.0')), 'numpy': (StrictVersion ('1.6.1'), None), 'psutil': (StrictVersion ('0.5'), None)}</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.util.file_handle.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.util.file_handle.html
new file mode 100644
index 0000000..6d0a692
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.util.file_handle.html
@@ -0,0 +1,95 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.util.file_handle</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.util.html"><font color="#ffffff">util</font></a>.file_handle</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/util/file_handle.py">telemetry/internal/util/file_handle.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="os.html">os</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.util.file_handle.html#FileHandle">FileHandle</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="FileHandle">class <strong>FileHandle</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="FileHandle-GetAbsPath"><strong>GetAbsPath</strong></a>(self)</dt><dd><tt>Returns&nbsp;the&nbsp;path&nbsp;to&nbsp;the&nbsp;pointed-to&nbsp;file&nbsp;relative&nbsp;to&nbsp;the&nbsp;given&nbsp;start&nbsp;path.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;start:&nbsp;A&nbsp;string&nbsp;representing&nbsp;a&nbsp;starting&nbsp;path.<br>
+Returns:<br>
+&nbsp;&nbsp;A&nbsp;string&nbsp;giving&nbsp;the&nbsp;relative&nbsp;path&nbsp;from&nbsp;path&nbsp;to&nbsp;this&nbsp;file.</tt></dd></dl>
+
+<dl><dt><a name="FileHandle-__init__"><strong>__init__</strong></a>(self, temp_file<font color="#909090">=None</font>, absolute_path<font color="#909090">=None</font>)</dt><dd><tt>Constructs&nbsp;a&nbsp;<a href="#FileHandle">FileHandle</a>&nbsp;<a href="__builtin__.html#object">object</a>.<br>
+&nbsp;<br>
+This&nbsp;constructor&nbsp;should&nbsp;not&nbsp;be&nbsp;used&nbsp;by&nbsp;the&nbsp;user;&nbsp;rather&nbsp;it&nbsp;is&nbsp;preferred&nbsp;to<br>
+use&nbsp;the&nbsp;module-level&nbsp;GetAbsPath&nbsp;and&nbsp;FromTempFile&nbsp;functions.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;temp_file:&nbsp;An&nbsp;instance&nbsp;of&nbsp;a&nbsp;temporary&nbsp;file&nbsp;<a href="__builtin__.html#object">object</a>.<br>
+&nbsp;&nbsp;absolute_path:&nbsp;A&nbsp;path;&nbsp;should&nbsp;not&nbsp;be&nbsp;passed&nbsp;if&nbsp;tempfile&nbsp;is&nbsp;and&nbsp;vice-versa.<br>
+&nbsp;&nbsp;extension:&nbsp;A&nbsp;string&nbsp;that&nbsp;specifies&nbsp;the&nbsp;file&nbsp;extension.&nbsp;It&nbsp;must&nbsp;starts&nbsp;with<br>
+&nbsp;&nbsp;&nbsp;&nbsp;".".</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>extension</strong></dt>
+</dl>
+<dl><dt><strong>id</strong></dt>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-FromFilePath"><strong>FromFilePath</strong></a>(path)</dt><dd><tt>Constructs&nbsp;a&nbsp;<a href="#FileHandle">FileHandle</a>&nbsp;from&nbsp;an&nbsp;absolute&nbsp;file&nbsp;path.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;path:&nbsp;A&nbsp;string&nbsp;giving&nbsp;the&nbsp;absolute&nbsp;path&nbsp;to&nbsp;a&nbsp;file.<br>
+Returns:<br>
+&nbsp;&nbsp;A&nbsp;<a href="#FileHandle">FileHandle</a>&nbsp;referring&nbsp;to&nbsp;the&nbsp;file&nbsp;at&nbsp;the&nbsp;specified&nbsp;path.</tt></dd></dl>
+ <dl><dt><a name="-FromTempFile"><strong>FromTempFile</strong></a>(temp_file)</dt><dd><tt>Constructs&nbsp;a&nbsp;<a href="#FileHandle">FileHandle</a>&nbsp;pointing&nbsp;to&nbsp;a&nbsp;temporary&nbsp;file.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;A&nbsp;<a href="#FileHandle">FileHandle</a>&nbsp;referring&nbsp;to&nbsp;a&nbsp;named&nbsp;temporary&nbsp;file.</tt></dd></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.util.find_dependencies.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.util.find_dependencies.html
new file mode 100644
index 0000000..5187ee5
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.util.find_dependencies.html
@@ -0,0 +1,123 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.util.find_dependencies</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.util.html"><font color="#ffffff">util</font></a>.find_dependencies</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/util/find_dependencies.py">telemetry/internal/util/find_dependencies.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.benchmark.html">telemetry.benchmark</a><br>
+<a href="telemetry.internal.util.bootstrap.html">telemetry.internal.util.bootstrap</a><br>
+<a href="telemetry.internal.util.command_line.html">telemetry.internal.util.command_line</a><br>
+<a href="telemetry.core.discover.html">telemetry.core.discover</a><br>
+</td><td width="25%" valign=top><a href="fnmatch.html">fnmatch</a><br>
+<a href="imp.html">imp</a><br>
+<a href="logging.html">logging</a><br>
+<a href="modulegraph.modulegraph.html">modulegraph.modulegraph</a><br>
+</td><td width="25%" valign=top><a href="optparse.html">optparse</a><br>
+<a href="os.html">os</a><br>
+<a href="telemetry.internal.util.path.html">telemetry.internal.util.path</a><br>
+<a href="telemetry.internal.util.path_set.html">telemetry.internal.util.path_set</a><br>
+</td><td width="25%" valign=top><a href="sys.html">sys</a><br>
+<a href="zipfile.html">zipfile</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.util.command_line.html#OptparseCommand">telemetry.internal.util.command_line.OptparseCommand</a>(<a href="telemetry.internal.util.command_line.html#Command">telemetry.internal.util.command_line.Command</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.util.find_dependencies.html#FindDependenciesCommand">FindDependenciesCommand</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="FindDependenciesCommand">class <strong>FindDependenciesCommand</strong></a>(<a href="telemetry.internal.util.command_line.html#OptparseCommand">telemetry.internal.util.command_line.OptparseCommand</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Prints&nbsp;all&nbsp;dependencies<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.util.find_dependencies.html#FindDependenciesCommand">FindDependenciesCommand</a></dd>
+<dd><a href="telemetry.internal.util.command_line.html#OptparseCommand">telemetry.internal.util.command_line.OptparseCommand</a></dd>
+<dd><a href="telemetry.internal.util.command_line.html#Command">telemetry.internal.util.command_line.Command</a></dd>
+<dd><a href="telemetry.internal.util.command_line.html#ArgumentHandlerMixIn">telemetry.internal.util.command_line.ArgumentHandlerMixIn</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="FindDependenciesCommand-Run"><strong>Run</strong></a>(self, args)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="FindDependenciesCommand-AddCommandLineArgs"><strong>AddCommandLineArgs</strong></a>(cls, parser, _)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="FindDependenciesCommand-ProcessCommandLineArgs"><strong>ProcessCommandLineArgs</strong></a>(cls, parser, args, _)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Class methods inherited from <a href="telemetry.internal.util.command_line.html#OptparseCommand">telemetry.internal.util.command_line.OptparseCommand</a>:<br>
+<dl><dt><a name="FindDependenciesCommand-CreateParser"><strong>CreateParser</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="FindDependenciesCommand-main"><strong>main</strong></a>(cls, args<font color="#909090">=None</font>)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Main&nbsp;method&nbsp;to&nbsp;run&nbsp;this&nbsp;command&nbsp;as&nbsp;a&nbsp;standalone&nbsp;script.</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="telemetry.internal.util.command_line.html#OptparseCommand">telemetry.internal.util.command_line.OptparseCommand</a>:<br>
+<dl><dt><strong>usage</strong> = ''</dl>
+
+<hr>
+Class methods inherited from <a href="telemetry.internal.util.command_line.html#Command">telemetry.internal.util.command_line.Command</a>:<br>
+<dl><dt><a name="FindDependenciesCommand-Description"><strong>Description</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="FindDependenciesCommand-Name"><strong>Name</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.util.command_line.html#ArgumentHandlerMixIn">telemetry.internal.util.command_line.ArgumentHandlerMixIn</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-FindBootstrapDependencies"><strong>FindBootstrapDependencies</strong></a>(base_dir)</dt></dl>
+ <dl><dt><a name="-FindDependencies"><strong>FindDependencies</strong></a>(target_paths, options)</dt></dl>
+ <dl><dt><a name="-FindExcludedFiles"><strong>FindExcludedFiles</strong></a>(files, options)</dt></dl>
+ <dl><dt><a name="-FindPageSetDependencies"><strong>FindPageSetDependencies</strong></a>(base_dir)</dt></dl>
+ <dl><dt><a name="-FindPythonDependencies"><strong>FindPythonDependencies</strong></a>(module_path)</dt></dl>
+ <dl><dt><a name="-ZipDependencies"><strong>ZipDependencies</strong></a>(target_paths, dependencies, options)</dt></dl>
+</td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>DEPS_FILE</strong> = 'bootstrap_deps'</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.util.global_hooks.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.util.global_hooks.html
new file mode 100644
index 0000000..10681f4
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.util.global_hooks.html
@@ -0,0 +1,36 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.util.global_hooks</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.util.html"><font color="#ffffff">util</font></a>.global_hooks</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/util/global_hooks.py">telemetry/internal/util/global_hooks.py</a></font></td></tr></table>
+    <p><tt>Hooks&nbsp;that&nbsp;apply&nbsp;globally&nbsp;to&nbsp;all&nbsp;scripts&nbsp;that&nbsp;import&nbsp;or&nbsp;use&nbsp;Telemetry.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.util.exception_formatter.html">telemetry.internal.util.exception_formatter</a><br>
+</td><td width="25%" valign=top><a href="signal.html">signal</a><br>
+</td><td width="25%" valign=top><a href="sys.html">sys</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-InstallHooks"><strong>InstallHooks</strong></a>()</dt></dl>
+ <dl><dt><a name="-InstallStackDumpOnSigusr1"><strong>InstallStackDumpOnSigusr1</strong></a>()</dt><dd><tt>Catch&nbsp;SIGUSR1&nbsp;and&nbsp;print&nbsp;a&nbsp;stack&nbsp;trace.</tt></dd></dl>
+ <dl><dt><a name="-InstallTerminationHook"><strong>InstallTerminationHook</strong></a>()</dt><dd><tt>Catch&nbsp;SIGTERM,&nbsp;print&nbsp;a&nbsp;stack&nbsp;trace,&nbsp;and&nbsp;exit.</tt></dd></dl>
+ <dl><dt><a name="-InstallUnhandledExceptionFormatter"><strong>InstallUnhandledExceptionFormatter</strong></a>()</dt><dd><tt>Print&nbsp;prettier&nbsp;exceptions&nbsp;that&nbsp;also&nbsp;contain&nbsp;the&nbsp;stack&nbsp;frame's&nbsp;locals.</tt></dd></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.util.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.util.html
new file mode 100644
index 0000000..bc906a0
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.util.html
@@ -0,0 +1,47 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: package telemetry.internal.util</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.util</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/util/__init__.py">telemetry/internal/util/__init__.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2015&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Package Contents</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.util.binary_manager.html">binary_manager</a><br>
+<a href="telemetry.internal.util.binary_manager_unittest.html">binary_manager_unittest</a><br>
+<a href="telemetry.internal.util.bootstrap.html">bootstrap</a><br>
+<a href="telemetry.internal.util.camel_case.html">camel_case</a><br>
+<a href="telemetry.internal.util.camel_case_unittest.html">camel_case_unittest</a><br>
+<a href="telemetry.internal.util.classes.html">classes</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.util.classes_unittest.html">classes_unittest</a><br>
+<a href="telemetry.internal.util.command_line.html">command_line</a><br>
+<a href="telemetry.internal.util.command_line_unittest.html">command_line_unittest</a><br>
+<a href="telemetry.internal.util.exception_formatter.html">exception_formatter</a><br>
+<a href="telemetry.internal.util.external_modules.html">external_modules</a><br>
+<a href="telemetry.internal.util.file_handle.html">file_handle</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.util.file_handle_unittest.html">file_handle_unittest</a><br>
+<a href="telemetry.internal.util.find_dependencies.html">find_dependencies</a><br>
+<a href="telemetry.internal.util.find_dependencies_unittest.html">find_dependencies_unittest</a><br>
+<a href="telemetry.internal.util.global_hooks.html">global_hooks</a><br>
+<a href="telemetry.internal.util.path.html">path</a><br>
+<a href="telemetry.internal.util.path_set.html">path_set</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.util.path_set_unittest.html">path_set_unittest</a><br>
+<a href="telemetry.internal.util.path_unittest.html">path_unittest</a><br>
+<a href="telemetry.internal.util.ps_util.html">ps_util</a><br>
+<a href="telemetry.internal.util.webpagereplay.html">webpagereplay</a><br>
+<a href="telemetry.internal.util.webpagereplay_unittest.html">webpagereplay_unittest</a><br>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.util.path.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.util.path.html
new file mode 100644
index 0000000..a1c3389
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.util.path.html
@@ -0,0 +1,42 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.util.path</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.util.html"><font color="#ffffff">util</font></a>.path</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/util/path.py">telemetry/internal/util/path.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="os.html">os</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.util.html">telemetry.core.util</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-FindInstalledWindowsApplication"><strong>FindInstalledWindowsApplication</strong></a>(application_path)</dt><dd><tt>Search&nbsp;common&nbsp;Windows&nbsp;installation&nbsp;directories&nbsp;for&nbsp;an&nbsp;application.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;application_path:&nbsp;Path&nbsp;to&nbsp;application&nbsp;relative&nbsp;from&nbsp;installation&nbsp;location.<br>
+Returns:<br>
+&nbsp;&nbsp;A&nbsp;string&nbsp;representing&nbsp;the&nbsp;full&nbsp;path,&nbsp;or&nbsp;None&nbsp;if&nbsp;not&nbsp;found.</tt></dd></dl>
+ <dl><dt><a name="-IsExecutable"><strong>IsExecutable</strong></a>(path)</dt></dl>
+ <dl><dt><a name="-IsSubpath"><strong>IsSubpath</strong></a>(subpath, superpath)</dt><dd><tt>Returns&nbsp;True&nbsp;iff&nbsp;subpath&nbsp;is&nbsp;or&nbsp;is&nbsp;in&nbsp;superpath.</tt></dd></dl>
+ <dl><dt><a name="-ListFiles"><strong>ListFiles</strong></a>(base_directory, should_include_dir<font color="#909090">=&lt;function &lt;lambda&gt;&gt;</font>, should_include_file<font color="#909090">=&lt;function &lt;lambda&gt;&gt;</font>)</dt></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.util.path_set.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.util.path_set.html
new file mode 100644
index 0000000..1e96faa
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.util.path_set.html
@@ -0,0 +1,150 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.util.path_set</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.util.html"><font color="#ffffff">util</font></a>.path_set</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/util/path_set.py">telemetry/internal/util/path_set.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="collections.html">collections</a><br>
+</td><td width="25%" valign=top><a href="os.html">os</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="_abcoll.html#MutableSet">_abcoll.MutableSet</a>(<a href="_abcoll.html#Set">_abcoll.Set</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.util.path_set.html#PathSet">PathSet</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="PathSet">class <strong>PathSet</strong></a>(<a href="_abcoll.html#MutableSet">_abcoll.MutableSet</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;set&nbsp;of&nbsp;paths.<br>
+&nbsp;<br>
+All&nbsp;mutation&nbsp;methods&nbsp;can&nbsp;take&nbsp;both&nbsp;directories&nbsp;or&nbsp;individual&nbsp;files,&nbsp;but&nbsp;the<br>
+iterator&nbsp;yields&nbsp;the&nbsp;individual&nbsp;files.&nbsp;All&nbsp;paths&nbsp;are&nbsp;automatically&nbsp;normalized.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.util.path_set.html#PathSet">PathSet</a></dd>
+<dd><a href="_abcoll.html#MutableSet">_abcoll.MutableSet</a></dd>
+<dd><a href="_abcoll.html#Set">_abcoll.Set</a></dd>
+<dd><a href="_abcoll.html#Sized">_abcoll.Sized</a></dd>
+<dd><a href="_abcoll.html#Iterable">_abcoll.Iterable</a></dd>
+<dd><a href="_abcoll.html#Container">_abcoll.Container</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="PathSet-__contains__"><strong>__contains__</strong></a>(self, path)</dt></dl>
+
+<dl><dt><a name="PathSet-__init__"><strong>__init__</strong></a>(self, iterable<font color="#909090">=None</font>)</dt></dl>
+
+<dl><dt><a name="PathSet-__iter__"><strong>__iter__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PathSet-__len__"><strong>__len__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PathSet-add"><strong>add</strong></a>(self, path)</dt></dl>
+
+<dl><dt><a name="PathSet-discard"><strong>discard</strong></a>(self, path)</dt></dl>
+
+<hr>
+Data and other attributes defined here:<br>
+<dl><dt><strong>__abstractmethods__</strong> = frozenset([])</dl>
+
+<hr>
+Methods inherited from <a href="_abcoll.html#MutableSet">_abcoll.MutableSet</a>:<br>
+<dl><dt><a name="PathSet-__iand__"><strong>__iand__</strong></a>(self, it)</dt></dl>
+
+<dl><dt><a name="PathSet-__ior__"><strong>__ior__</strong></a>(self, it)</dt></dl>
+
+<dl><dt><a name="PathSet-__isub__"><strong>__isub__</strong></a>(self, it)</dt></dl>
+
+<dl><dt><a name="PathSet-__ixor__"><strong>__ixor__</strong></a>(self, it)</dt></dl>
+
+<dl><dt><a name="PathSet-clear"><strong>clear</strong></a>(self)</dt><dd><tt>This&nbsp;is&nbsp;slow&nbsp;(creates&nbsp;N&nbsp;new&nbsp;iterators!)&nbsp;but&nbsp;effective.</tt></dd></dl>
+
+<dl><dt><a name="PathSet-pop"><strong>pop</strong></a>(self)</dt><dd><tt>Return&nbsp;the&nbsp;popped&nbsp;value.&nbsp;&nbsp;Raise&nbsp;KeyError&nbsp;if&nbsp;empty.</tt></dd></dl>
+
+<dl><dt><a name="PathSet-remove"><strong>remove</strong></a>(self, value)</dt><dd><tt>Remove&nbsp;an&nbsp;element.&nbsp;If&nbsp;not&nbsp;a&nbsp;member,&nbsp;raise&nbsp;a&nbsp;KeyError.</tt></dd></dl>
+
+<hr>
+Methods inherited from <a href="_abcoll.html#Set">_abcoll.Set</a>:<br>
+<dl><dt><a name="PathSet-__and__"><strong>__and__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="PathSet-__eq__"><strong>__eq__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="PathSet-__ge__"><strong>__ge__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="PathSet-__gt__"><strong>__gt__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="PathSet-__le__"><strong>__le__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="PathSet-__lt__"><strong>__lt__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="PathSet-__ne__"><strong>__ne__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="PathSet-__or__"><strong>__or__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="PathSet-__sub__"><strong>__sub__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="PathSet-__xor__"><strong>__xor__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="PathSet-isdisjoint"><strong>isdisjoint</strong></a>(self, other)</dt><dd><tt>Return&nbsp;True&nbsp;if&nbsp;two&nbsp;sets&nbsp;have&nbsp;a&nbsp;null&nbsp;intersection.</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="_abcoll.html#Set">_abcoll.Set</a>:<br>
+<dl><dt><strong>__hash__</strong> = None</dl>
+
+<hr>
+Class methods inherited from <a href="_abcoll.html#Sized">_abcoll.Sized</a>:<br>
+<dl><dt><a name="PathSet-__subclasshook__"><strong>__subclasshook__</strong></a>(cls, C)<font color="#909090"><font face="helvetica, arial"> from <a href="abc.html#ABCMeta">abc.ABCMeta</a></font></font></dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="_abcoll.html#Sized">_abcoll.Sized</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="_abcoll.html#Sized">_abcoll.Sized</a>:<br>
+<dl><dt><strong>__metaclass__</strong> = &lt;class 'abc.ABCMeta'&gt;<dd><tt>Metaclass&nbsp;for&nbsp;defining&nbsp;Abstract&nbsp;Base&nbsp;Classes&nbsp;(ABCs).<br>
+&nbsp;<br>
+Use&nbsp;this&nbsp;metaclass&nbsp;to&nbsp;create&nbsp;an&nbsp;ABC.&nbsp;&nbsp;An&nbsp;ABC&nbsp;can&nbsp;be&nbsp;subclassed<br>
+directly,&nbsp;and&nbsp;then&nbsp;acts&nbsp;as&nbsp;a&nbsp;mix-in&nbsp;class.&nbsp;&nbsp;You&nbsp;can&nbsp;also&nbsp;register<br>
+unrelated&nbsp;concrete&nbsp;classes&nbsp;(even&nbsp;built-in&nbsp;classes)&nbsp;and&nbsp;unrelated<br>
+ABCs&nbsp;as&nbsp;'virtual&nbsp;subclasses'&nbsp;--&nbsp;these&nbsp;and&nbsp;their&nbsp;descendants&nbsp;will<br>
+be&nbsp;considered&nbsp;subclasses&nbsp;of&nbsp;the&nbsp;registering&nbsp;ABC&nbsp;by&nbsp;the&nbsp;built-in<br>
+issubclass()&nbsp;function,&nbsp;but&nbsp;the&nbsp;registering&nbsp;ABC&nbsp;won't&nbsp;show&nbsp;up&nbsp;in<br>
+their&nbsp;MRO&nbsp;(Method&nbsp;Resolution&nbsp;Order)&nbsp;nor&nbsp;will&nbsp;method<br>
+implementations&nbsp;defined&nbsp;by&nbsp;the&nbsp;registering&nbsp;ABC&nbsp;be&nbsp;callable&nbsp;(not<br>
+even&nbsp;via&nbsp;super()).</tt></dl>
+
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.util.ps_util.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.util.ps_util.html
new file mode 100644
index 0000000..753536a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.util.ps_util.html
@@ -0,0 +1,51 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.util.ps_util</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.util.html"><font color="#ffffff">util</font></a>.ps_util</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/util/ps_util.py">telemetry/internal/util/ps_util.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="atexit.html">atexit</a><br>
+</td><td width="25%" valign=top><a href="inspect.html">inspect</a><br>
+</td><td width="25%" valign=top><a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top><a href="os.html">os</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-EnableListingStrayProcessesUponExitHook"><strong>EnableListingStrayProcessesUponExitHook</strong></a>()</dt></dl>
+ <dl><dt><a name="-GetChildPids"><strong>GetChildPids</strong></a>(processes, pid)</dt><dd><tt>Returns&nbsp;all&nbsp;child&nbsp;processes&nbsp;of&nbsp;|pid|&nbsp;from&nbsp;the&nbsp;given&nbsp;|processes|&nbsp;list.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;processes:&nbsp;A&nbsp;tuple&nbsp;of&nbsp;(pid,&nbsp;ppid,&nbsp;state)&nbsp;as&nbsp;generated&nbsp;by&nbsp;ps.<br>
+&nbsp;&nbsp;pid:&nbsp;The&nbsp;pid&nbsp;for&nbsp;which&nbsp;to&nbsp;get&nbsp;children.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;A&nbsp;list&nbsp;of&nbsp;child&nbsp;pids.</tt></dd></dl>
+ <dl><dt><a name="-GetPsOutputWithPlatformBackend"><strong>GetPsOutputWithPlatformBackend</strong></a>(platform_backend, columns, pid)</dt><dd><tt>Returns&nbsp;output&nbsp;of&nbsp;the&nbsp;'ps'&nbsp;command&nbsp;as&nbsp;a&nbsp;list&nbsp;of&nbsp;lines.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;platform_backend:&nbsp;The&nbsp;platform&nbsp;backend&nbsp;(LinuxBasedPlatformBackend&nbsp;or<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PosixPlatformBackend).<br>
+&nbsp;&nbsp;columns:&nbsp;A&nbsp;list&nbsp;of&nbsp;require&nbsp;columns,&nbsp;e.g.,&nbsp;['pid',&nbsp;'pss'].<br>
+&nbsp;&nbsp;pid:&nbsp;If&nbsp;not&nbsp;None,&nbsp;returns&nbsp;only&nbsp;the&nbsp;information&nbsp;of&nbsp;the&nbsp;process&nbsp;with&nbsp;the&nbsp;pid.</tt></dd></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.util.webpagereplay.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.util.webpagereplay.html
new file mode 100644
index 0000000..0eec669
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.internal.util.webpagereplay.html
@@ -0,0 +1,294 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.internal.util.webpagereplay</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.internal.html"><font color="#ffffff">internal</font></a>.<a href="telemetry.internal.util.html"><font color="#ffffff">util</font></a>.webpagereplay</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/internal/util/webpagereplay.py">telemetry/internal/util/webpagereplay.py</a></font></td></tr></table>
+    <p><tt>Start&nbsp;and&nbsp;stop&nbsp;Web&nbsp;Page&nbsp;Replay.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="atexit.html">atexit</a><br>
+<a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+<a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top><a href="os.html">os</a><br>
+<a href="re.html">re</a><br>
+<a href="signal.html">signal</a><br>
+</td><td width="25%" valign=top><a href="subprocess.html">subprocess</a><br>
+<a href="sys.html">sys</a><br>
+<a href="tempfile.html">tempfile</a><br>
+</td><td width="25%" valign=top><a href="urllib.html">urllib</a><br>
+<a href="telemetry.core.util.html">telemetry.core.util</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.util.webpagereplay.html#ReplayServer">ReplayServer</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="exceptions.html#Exception">exceptions.Exception</a>(<a href="exceptions.html#BaseException">exceptions.BaseException</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.util.webpagereplay.html#ReplayError">ReplayError</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.util.webpagereplay.html#ReplayNotFoundError">ReplayNotFoundError</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.internal.util.webpagereplay.html#ReplayNotStartedError">ReplayNotStartedError</a>
+</font></dt></dl>
+</dd>
+</dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ReplayError">class <strong>ReplayError</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Catch-all&nbsp;exception&nbsp;for&nbsp;the&nbsp;module.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.util.webpagereplay.html#ReplayError">ReplayError</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="ReplayError-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#ReplayError-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#ReplayError-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="ReplayError-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#ReplayError-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="ReplayError-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#ReplayError-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="ReplayError-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#ReplayError-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="ReplayError-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#ReplayError-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="ReplayError-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="ReplayError-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#ReplayError-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="ReplayError-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#ReplayError-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="ReplayError-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="ReplayError-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#ReplayError-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="ReplayError-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ReplayNotFoundError">class <strong>ReplayNotFoundError</strong></a>(<a href="telemetry.internal.util.webpagereplay.html#ReplayError">ReplayError</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.util.webpagereplay.html#ReplayNotFoundError">ReplayNotFoundError</a></dd>
+<dd><a href="telemetry.internal.util.webpagereplay.html#ReplayError">ReplayError</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="ReplayNotFoundError-__init__"><strong>__init__</strong></a>(self, label, path)</dt></dl>
+
+<dl><dt><a name="ReplayNotFoundError-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.util.webpagereplay.html#ReplayError">ReplayError</a>:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#ReplayNotFoundError-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="ReplayNotFoundError-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#ReplayNotFoundError-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="ReplayNotFoundError-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#ReplayNotFoundError-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="ReplayNotFoundError-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#ReplayNotFoundError-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="ReplayNotFoundError-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#ReplayNotFoundError-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="ReplayNotFoundError-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="ReplayNotFoundError-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#ReplayNotFoundError-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="ReplayNotFoundError-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#ReplayNotFoundError-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="ReplayNotFoundError-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="ReplayNotFoundError-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ReplayNotStartedError">class <strong>ReplayNotStartedError</strong></a>(<a href="telemetry.internal.util.webpagereplay.html#ReplayError">ReplayError</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.internal.util.webpagereplay.html#ReplayNotStartedError">ReplayNotStartedError</a></dd>
+<dd><a href="telemetry.internal.util.webpagereplay.html#ReplayError">ReplayError</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.util.webpagereplay.html#ReplayError">ReplayError</a>:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="ReplayNotStartedError-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#ReplayNotStartedError-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#ReplayNotStartedError-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="ReplayNotStartedError-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#ReplayNotStartedError-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="ReplayNotStartedError-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#ReplayNotStartedError-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="ReplayNotStartedError-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#ReplayNotStartedError-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="ReplayNotStartedError-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#ReplayNotStartedError-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="ReplayNotStartedError-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="ReplayNotStartedError-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#ReplayNotStartedError-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="ReplayNotStartedError-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#ReplayNotStartedError-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="ReplayNotStartedError-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="ReplayNotStartedError-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#ReplayNotStartedError-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="ReplayNotStartedError-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ReplayServer">class <strong>ReplayServer</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Start&nbsp;and&nbsp;Stop&nbsp;Web&nbsp;Page&nbsp;Replay.<br>
+&nbsp;<br>
+Web&nbsp;Page&nbsp;Replay&nbsp;is&nbsp;a&nbsp;proxy&nbsp;that&nbsp;can&nbsp;record&nbsp;and&nbsp;"replay"&nbsp;web&nbsp;pages&nbsp;with<br>
+simulated&nbsp;network&nbsp;characteristics&nbsp;--&nbsp;without&nbsp;having&nbsp;to&nbsp;edit&nbsp;the&nbsp;pages<br>
+by&nbsp;hand.&nbsp;With&nbsp;WPR,&nbsp;tests&nbsp;can&nbsp;use&nbsp;"real"&nbsp;web&nbsp;content,&nbsp;and&nbsp;catch<br>
+performance&nbsp;issues&nbsp;that&nbsp;may&nbsp;result&nbsp;from&nbsp;introducing&nbsp;network&nbsp;delays&nbsp;and<br>
+bandwidth&nbsp;throttling.<br>
+&nbsp;<br>
+Example:<br>
+&nbsp;&nbsp;&nbsp;with&nbsp;<a href="#ReplayServer">ReplayServer</a>(archive_path):<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;NavigateToURL(start_url)<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WaitUntil(...)<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="ReplayServer-StartServer"><strong>StartServer</strong></a>(self)</dt><dd><tt>Start&nbsp;Web&nbsp;Page&nbsp;Replay&nbsp;and&nbsp;verify&nbsp;that&nbsp;it&nbsp;started.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;(HTTP_PORT,&nbsp;HTTPS_PORT,&nbsp;DNS_PORT)&nbsp;&nbsp;#&nbsp;DNS_PORT&nbsp;is&nbsp;None&nbsp;if&nbsp;unused.<br>
+Raises:<br>
+&nbsp;&nbsp;<a href="#ReplayNotStartedError">ReplayNotStartedError</a>:&nbsp;if&nbsp;Replay&nbsp;start-up&nbsp;fails.</tt></dd></dl>
+
+<dl><dt><a name="ReplayServer-StopServer"><strong>StopServer</strong></a>(self)</dt><dd><tt>Stop&nbsp;Web&nbsp;Page&nbsp;Replay.</tt></dd></dl>
+
+<dl><dt><a name="ReplayServer-__enter__"><strong>__enter__</strong></a>(self)</dt><dd><tt>Add&nbsp;support&nbsp;for&nbsp;with-statement.</tt></dd></dl>
+
+<dl><dt><a name="ReplayServer-__exit__"><strong>__exit__</strong></a>(self, unused_exc_type, unused_exc_val, unused_exc_tb)</dt><dd><tt>Add&nbsp;support&nbsp;for&nbsp;with-statement.</tt></dd></dl>
+
+<dl><dt><a name="ReplayServer-__init__"><strong>__init__</strong></a>(self, archive_path, replay_host, http_port, https_port, dns_port, replay_options)</dt><dd><tt>Initialize&nbsp;<a href="#ReplayServer">ReplayServer</a>.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;archive_path:&nbsp;a&nbsp;path&nbsp;to&nbsp;a&nbsp;specific&nbsp;WPR&nbsp;archive&nbsp;(required).<br>
+&nbsp;&nbsp;replay_host:&nbsp;the&nbsp;hostname&nbsp;to&nbsp;serve&nbsp;traffic.<br>
+&nbsp;&nbsp;http_port:&nbsp;an&nbsp;integer&nbsp;port&nbsp;on&nbsp;which&nbsp;to&nbsp;serve&nbsp;HTTP&nbsp;traffic.&nbsp;May&nbsp;be&nbsp;zero<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;to&nbsp;let&nbsp;the&nbsp;OS&nbsp;choose&nbsp;an&nbsp;available&nbsp;port.<br>
+&nbsp;&nbsp;https_port:&nbsp;an&nbsp;integer&nbsp;port&nbsp;on&nbsp;which&nbsp;to&nbsp;serve&nbsp;HTTPS&nbsp;traffic.&nbsp;May&nbsp;be&nbsp;zero<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;to&nbsp;let&nbsp;the&nbsp;OS&nbsp;choose&nbsp;an&nbsp;available&nbsp;port.<br>
+&nbsp;&nbsp;dns_port:&nbsp;an&nbsp;integer&nbsp;port&nbsp;on&nbsp;which&nbsp;to&nbsp;serve&nbsp;DNS&nbsp;traffic.&nbsp;May&nbsp;be&nbsp;zero<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;to&nbsp;let&nbsp;the&nbsp;OS&nbsp;choose&nbsp;an&nbsp;available&nbsp;port.&nbsp;If&nbsp;None&nbsp;DNS&nbsp;forwarding&nbsp;is<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;disabled.<br>
+&nbsp;&nbsp;replay_options:&nbsp;an&nbsp;iterable&nbsp;of&nbsp;options&nbsp;strings&nbsp;to&nbsp;forward&nbsp;to&nbsp;replay.py.</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.page.action_runner.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.page.action_runner.html
new file mode 100644
index 0000000..5ea1353
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.page.action_runner.html
@@ -0,0 +1,536 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.page.action_runner</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.page.html"><font color="#ffffff">page</font></a>.action_runner</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/page/action_runner.py">telemetry/page/action_runner.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top><a href="time.html">time</a><br>
+</td><td width="25%" valign=top><a href="telemetry.web_perf.timeline_interaction_record.html">telemetry.web_perf.timeline_interaction_record</a><br>
+</td><td width="25%" valign=top><a href="urlparse.html">urlparse</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.page.action_runner.html#ActionRunner">ActionRunner</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.page.action_runner.html#Interaction">Interaction</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ActionRunner">class <strong>ActionRunner</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="ActionRunner-ClickElement"><strong>ClickElement</strong></a>(self, selector<font color="#909090">=None</font>, text<font color="#909090">=None</font>, element_function<font color="#909090">=None</font>)</dt><dd><tt>Click&nbsp;an&nbsp;element.<br>
+&nbsp;<br>
+The&nbsp;element&nbsp;may&nbsp;be&nbsp;selected&nbsp;via&nbsp;selector,&nbsp;text,&nbsp;or&nbsp;element_function.<br>
+Only&nbsp;one&nbsp;of&nbsp;these&nbsp;arguments&nbsp;must&nbsp;be&nbsp;specified.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;selector:&nbsp;A&nbsp;CSS&nbsp;selector&nbsp;describing&nbsp;the&nbsp;element.<br>
+&nbsp;&nbsp;text:&nbsp;The&nbsp;element&nbsp;must&nbsp;contains&nbsp;this&nbsp;exact&nbsp;text.<br>
+&nbsp;&nbsp;element_function:&nbsp;A&nbsp;JavaScript&nbsp;function&nbsp;(as&nbsp;string)&nbsp;that&nbsp;is&nbsp;used<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;to&nbsp;retrieve&nbsp;the&nbsp;element.&nbsp;For&nbsp;example:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'(function()&nbsp;{&nbsp;return&nbsp;foo.element;&nbsp;})()'.</tt></dd></dl>
+
+<dl><dt><a name="ActionRunner-CreateGestureInteraction"><strong>CreateGestureInteraction</strong></a>(self, label, repeatable<font color="#909090">=False</font>)</dt><dd><tt>Create&nbsp;an&nbsp;action.<a href="#Interaction">Interaction</a>&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;that&nbsp;issues&nbsp;gesture-based<br>
+interaction&nbsp;record.<br>
+&nbsp;<br>
+This&nbsp;is&nbsp;similar&nbsp;to&nbsp;normal&nbsp;interaction&nbsp;record,&nbsp;but&nbsp;it&nbsp;will<br>
+auto-narrow&nbsp;the&nbsp;interaction&nbsp;time&nbsp;period&nbsp;to&nbsp;only&nbsp;include&nbsp;the<br>
+synthetic&nbsp;gesture&nbsp;event&nbsp;output&nbsp;by&nbsp;Chrome.&nbsp;This&nbsp;is&nbsp;typically&nbsp;use&nbsp;to<br>
+reduce&nbsp;noise&nbsp;in&nbsp;gesture-based&nbsp;analysis&nbsp;(e.g.,&nbsp;analysis&nbsp;for&nbsp;a<br>
+swipe/scroll).<br>
+&nbsp;<br>
+The&nbsp;interaction&nbsp;record&nbsp;label&nbsp;will&nbsp;be&nbsp;prepended&nbsp;with&nbsp;'Gesture_'.<br>
+&nbsp;<br>
+e.g:<br>
+&nbsp;&nbsp;with&nbsp;action_runner.<a href="#ActionRunner-CreateGestureInteraction">CreateGestureInteraction</a>('Scroll-1'):<br>
+&nbsp;&nbsp;&nbsp;&nbsp;action_runner.<a href="#ActionRunner-ScrollPage">ScrollPage</a>()<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;label:&nbsp;A&nbsp;label&nbsp;for&nbsp;this&nbsp;particular&nbsp;interaction.&nbsp;This&nbsp;can&nbsp;be&nbsp;any<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;user-defined&nbsp;string,&nbsp;but&nbsp;must&nbsp;not&nbsp;contain&nbsp;'/'.<br>
+&nbsp;&nbsp;repeatable:&nbsp;Whether&nbsp;other&nbsp;interactions&nbsp;may&nbsp;use&nbsp;the&nbsp;same&nbsp;logical&nbsp;name<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;as&nbsp;this&nbsp;interaction.&nbsp;All&nbsp;interactions&nbsp;with&nbsp;the&nbsp;same&nbsp;logical&nbsp;name&nbsp;must<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;have&nbsp;the&nbsp;same&nbsp;flags.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;An&nbsp;instance&nbsp;of&nbsp;action_runner.<a href="#Interaction">Interaction</a></tt></dd></dl>
+
+<dl><dt><a name="ActionRunner-CreateInteraction"><strong>CreateInteraction</strong></a>(self, label, repeatable<font color="#909090">=False</font>)</dt><dd><tt>Create&nbsp;an&nbsp;action.<a href="#Interaction">Interaction</a>&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;that&nbsp;issues&nbsp;interaction&nbsp;record.<br>
+&nbsp;<br>
+An&nbsp;interaction&nbsp;record&nbsp;is&nbsp;a&nbsp;labeled&nbsp;time&nbsp;period&nbsp;containing<br>
+interaction&nbsp;that&nbsp;developers&nbsp;care&nbsp;about.&nbsp;Each&nbsp;set&nbsp;of&nbsp;metrics<br>
+specified&nbsp;in&nbsp;flags&nbsp;will&nbsp;be&nbsp;calculated&nbsp;for&nbsp;this&nbsp;time&nbsp;period.<br>
+&nbsp;<br>
+To&nbsp;mark&nbsp;the&nbsp;start&nbsp;of&nbsp;interaction&nbsp;record,&nbsp;call&nbsp;Begin()&nbsp;method&nbsp;on&nbsp;the&nbsp;returned<br>
+<a href="__builtin__.html#object">object</a>.&nbsp;To&nbsp;mark&nbsp;the&nbsp;finish&nbsp;of&nbsp;interaction&nbsp;record,&nbsp;call&nbsp;End()&nbsp;method&nbsp;on<br>
+it.&nbsp;Or&nbsp;better&nbsp;yet,&nbsp;use&nbsp;the&nbsp;with&nbsp;statement&nbsp;to&nbsp;create&nbsp;an<br>
+interaction&nbsp;record&nbsp;that&nbsp;covers&nbsp;the&nbsp;actions&nbsp;in&nbsp;the&nbsp;with&nbsp;block.<br>
+&nbsp;<br>
+e.g:<br>
+&nbsp;&nbsp;with&nbsp;action_runner.<a href="#ActionRunner-CreateInteraction">CreateInteraction</a>('Animation-1'):<br>
+&nbsp;&nbsp;&nbsp;&nbsp;action_runner.<a href="#ActionRunner-TapElement">TapElement</a>(...)<br>
+&nbsp;&nbsp;&nbsp;&nbsp;action_runner.<a href="#ActionRunner-WaitForJavaScriptCondition">WaitForJavaScriptCondition</a>(...)<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;label:&nbsp;A&nbsp;label&nbsp;for&nbsp;this&nbsp;particular&nbsp;interaction.&nbsp;This&nbsp;can&nbsp;be&nbsp;any<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;user-defined&nbsp;string,&nbsp;but&nbsp;must&nbsp;not&nbsp;contain&nbsp;'/'.<br>
+&nbsp;&nbsp;repeatable:&nbsp;Whether&nbsp;other&nbsp;interactions&nbsp;may&nbsp;use&nbsp;the&nbsp;same&nbsp;logical&nbsp;name<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;as&nbsp;this&nbsp;interaction.&nbsp;All&nbsp;interactions&nbsp;with&nbsp;the&nbsp;same&nbsp;logical&nbsp;name&nbsp;must<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;have&nbsp;the&nbsp;same&nbsp;flags.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;An&nbsp;instance&nbsp;of&nbsp;action_runner.<a href="#Interaction">Interaction</a></tt></dd></dl>
+
+<dl><dt><a name="ActionRunner-DragPage"><strong>DragPage</strong></a>(self, left_start_ratio, top_start_ratio, left_end_ratio, top_end_ratio, speed_in_pixels_per_second<font color="#909090">=800</font>, use_touch<font color="#909090">=False</font>, selector<font color="#909090">=None</font>, text<font color="#909090">=None</font>, element_function<font color="#909090">=None</font>)</dt><dd><tt>Perform&nbsp;a&nbsp;drag&nbsp;gesture&nbsp;on&nbsp;the&nbsp;page.<br>
+&nbsp;<br>
+You&nbsp;should&nbsp;specify&nbsp;a&nbsp;start&nbsp;and&nbsp;an&nbsp;end&nbsp;point&nbsp;in&nbsp;ratios&nbsp;of&nbsp;page&nbsp;width&nbsp;and<br>
+height&nbsp;(see&nbsp;drag.js&nbsp;for&nbsp;full&nbsp;implementation).<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;left_start_ratio:&nbsp;The&nbsp;horizontal&nbsp;starting&nbsp;coordinate&nbsp;of&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gesture,&nbsp;as&nbsp;a&nbsp;ratio&nbsp;of&nbsp;the&nbsp;visible&nbsp;bounding&nbsp;rectangle&nbsp;for<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;document.body.<br>
+&nbsp;&nbsp;top_start_ratio:&nbsp;The&nbsp;vertical&nbsp;starting&nbsp;coordinate&nbsp;of&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gesture,&nbsp;as&nbsp;a&nbsp;ratio&nbsp;of&nbsp;the&nbsp;visible&nbsp;bounding&nbsp;rectangle&nbsp;for<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;document.body.<br>
+&nbsp;&nbsp;left_end_ratio:&nbsp;The&nbsp;horizontal&nbsp;ending&nbsp;coordinate&nbsp;of&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gesture,&nbsp;as&nbsp;a&nbsp;ratio&nbsp;of&nbsp;the&nbsp;visible&nbsp;bounding&nbsp;rectangle&nbsp;for<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;document.body.<br>
+&nbsp;&nbsp;top_end_ratio:&nbsp;The&nbsp;vertical&nbsp;ending&nbsp;coordinate&nbsp;of&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gesture,&nbsp;as&nbsp;a&nbsp;ratio&nbsp;of&nbsp;the&nbsp;visible&nbsp;bounding&nbsp;rectangle&nbsp;for<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;document.body.<br>
+&nbsp;&nbsp;speed_in_pixels_per_second:&nbsp;The&nbsp;speed&nbsp;of&nbsp;the&nbsp;gesture&nbsp;(in&nbsp;pixels/s).<br>
+&nbsp;&nbsp;use_touch:&nbsp;Whether&nbsp;dragging&nbsp;should&nbsp;be&nbsp;done&nbsp;with&nbsp;touch&nbsp;input.</tt></dd></dl>
+
+<dl><dt><a name="ActionRunner-EvaluateJavaScript"><strong>EvaluateJavaScript</strong></a>(self, expression)</dt><dd><tt>Returns&nbsp;the&nbsp;evaluation&nbsp;result&nbsp;of&nbsp;the&nbsp;given&nbsp;JavaScript&nbsp;expression.<br>
+&nbsp;<br>
+The&nbsp;evaluation&nbsp;results&nbsp;must&nbsp;be&nbsp;convertible&nbsp;to&nbsp;JSON.&nbsp;If&nbsp;the&nbsp;result<br>
+is&nbsp;not&nbsp;needed,&nbsp;use&nbsp;ExecuteJavaScript&nbsp;instead.<br>
+&nbsp;<br>
+Example:&nbsp;num&nbsp;=&nbsp;runner.<a href="#ActionRunner-EvaluateJavaScript">EvaluateJavaScript</a>('document.location.href')<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;expression:&nbsp;The&nbsp;expression&nbsp;to&nbsp;evaluate&nbsp;(provided&nbsp;as&nbsp;string).<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;EvaluationException:&nbsp;The&nbsp;statement&nbsp;expression&nbsp;failed&nbsp;to&nbsp;execute<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;or&nbsp;the&nbsp;evaluation&nbsp;result&nbsp;can&nbsp;not&nbsp;be&nbsp;JSON-ized.</tt></dd></dl>
+
+<dl><dt><a name="ActionRunner-ExecuteJavaScript"><strong>ExecuteJavaScript</strong></a>(self, statement)</dt><dd><tt>Executes&nbsp;a&nbsp;given&nbsp;JavaScript&nbsp;expression.&nbsp;Does&nbsp;not&nbsp;return&nbsp;the&nbsp;result.<br>
+&nbsp;<br>
+Example:&nbsp;runner.<a href="#ActionRunner-ExecuteJavaScript">ExecuteJavaScript</a>('var&nbsp;foo&nbsp;=&nbsp;1;');<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;statement:&nbsp;The&nbsp;statement&nbsp;to&nbsp;execute&nbsp;(provided&nbsp;as&nbsp;string).<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;EvaluationException:&nbsp;The&nbsp;statement&nbsp;failed&nbsp;to&nbsp;execute.</tt></dd></dl>
+
+<dl><dt><a name="ActionRunner-ForceGarbageCollection"><strong>ForceGarbageCollection</strong></a>(self)</dt><dd><tt>Forces&nbsp;JavaScript&nbsp;garbage&nbsp;collection&nbsp;on&nbsp;the&nbsp;page.</tt></dd></dl>
+
+<dl><dt><a name="ActionRunner-LoadMedia"><strong>LoadMedia</strong></a>(self, selector<font color="#909090">=None</font>, event_timeout_in_seconds<font color="#909090">=0</font>, event_to_await<font color="#909090">='canplaythrough'</font>)</dt><dd><tt>Invokes&nbsp;load()&nbsp;on&nbsp;media&nbsp;elements&nbsp;and&nbsp;awaits&nbsp;an&nbsp;event.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;selector:&nbsp;A&nbsp;CSS&nbsp;selector&nbsp;describing&nbsp;the&nbsp;element.&nbsp;If&nbsp;none&nbsp;is<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;specified,&nbsp;play&nbsp;the&nbsp;first&nbsp;media&nbsp;element&nbsp;on&nbsp;the&nbsp;page.&nbsp;If&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;selector&nbsp;matches&nbsp;more&nbsp;than&nbsp;1&nbsp;media&nbsp;element,&nbsp;all&nbsp;of&nbsp;them&nbsp;will<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;be&nbsp;played.<br>
+&nbsp;&nbsp;event_timeout_in_seconds:&nbsp;Maximum&nbsp;waiting&nbsp;time&nbsp;for&nbsp;the&nbsp;event&nbsp;to&nbsp;be&nbsp;fired.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0&nbsp;means&nbsp;do&nbsp;not&nbsp;wait.<br>
+&nbsp;&nbsp;event_to_await:&nbsp;Which&nbsp;event&nbsp;to&nbsp;await.&nbsp;For&nbsp;example:&nbsp;'canplaythrough'&nbsp;or<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'loadedmetadata'.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;TimeoutException:&nbsp;If&nbsp;the&nbsp;maximum&nbsp;waiting&nbsp;time&nbsp;is&nbsp;exceeded.</tt></dd></dl>
+
+<dl><dt><a name="ActionRunner-LoopMedia"><strong>LoopMedia</strong></a>(self, loop_count, selector<font color="#909090">=None</font>, timeout_in_seconds<font color="#909090">=None</font>)</dt><dd><tt>Loops&nbsp;a&nbsp;media&nbsp;playback.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;loop_count:&nbsp;The&nbsp;number&nbsp;of&nbsp;times&nbsp;to&nbsp;loop&nbsp;the&nbsp;playback.<br>
+&nbsp;&nbsp;selector:&nbsp;A&nbsp;CSS&nbsp;selector&nbsp;describing&nbsp;the&nbsp;element.&nbsp;If&nbsp;none&nbsp;is<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;specified,&nbsp;loop&nbsp;the&nbsp;first&nbsp;media&nbsp;element&nbsp;on&nbsp;the&nbsp;page.&nbsp;If&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;selector&nbsp;matches&nbsp;more&nbsp;than&nbsp;1&nbsp;media&nbsp;element,&nbsp;all&nbsp;of&nbsp;them&nbsp;will<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;be&nbsp;looped.<br>
+&nbsp;&nbsp;timeout_in_seconds:&nbsp;Maximum&nbsp;waiting&nbsp;time&nbsp;for&nbsp;the&nbsp;looped&nbsp;playback&nbsp;to<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;complete.&nbsp;0&nbsp;means&nbsp;do&nbsp;not&nbsp;wait.&nbsp;None&nbsp;(the&nbsp;default)&nbsp;means&nbsp;to<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;wait&nbsp;loop_count&nbsp;*&nbsp;60&nbsp;seconds.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;TimeoutException:&nbsp;If&nbsp;the&nbsp;maximum&nbsp;waiting&nbsp;time&nbsp;is&nbsp;exceeded.</tt></dd></dl>
+
+<dl><dt><a name="ActionRunner-MouseClick"><strong>MouseClick</strong></a>(self, selector<font color="#909090">=None</font>)</dt><dd><tt>Mouse&nbsp;click&nbsp;the&nbsp;given&nbsp;element.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;selector:&nbsp;A&nbsp;CSS&nbsp;selector&nbsp;describing&nbsp;the&nbsp;element.</tt></dd></dl>
+
+<dl><dt><a name="ActionRunner-Navigate"><strong>Navigate</strong></a>(self, url, script_to_evaluate_on_commit<font color="#909090">=None</font>, timeout_in_seconds<font color="#909090">=60</font>)</dt><dd><tt>Navigates&nbsp;to&nbsp;url.<br>
+&nbsp;<br>
+If&nbsp;|script_to_evaluate_on_commit|&nbsp;is&nbsp;given,&nbsp;the&nbsp;script&nbsp;source&nbsp;string&nbsp;will&nbsp;be<br>
+evaluated&nbsp;when&nbsp;the&nbsp;navigation&nbsp;is&nbsp;committed.&nbsp;This&nbsp;is&nbsp;after&nbsp;the&nbsp;context&nbsp;of<br>
+the&nbsp;page&nbsp;exists,&nbsp;but&nbsp;before&nbsp;any&nbsp;script&nbsp;on&nbsp;the&nbsp;page&nbsp;itself&nbsp;has&nbsp;executed.</tt></dd></dl>
+
+<dl><dt><a name="ActionRunner-PauseInteractive"><strong>PauseInteractive</strong></a>(self)</dt><dd><tt>Pause&nbsp;the&nbsp;page&nbsp;execution&nbsp;and&nbsp;wait&nbsp;for&nbsp;terminal&nbsp;interaction.<br>
+&nbsp;<br>
+This&nbsp;is&nbsp;typically&nbsp;used&nbsp;for&nbsp;debugging.&nbsp;You&nbsp;can&nbsp;use&nbsp;this&nbsp;to&nbsp;pause<br>
+the&nbsp;page&nbsp;execution&nbsp;and&nbsp;inspect&nbsp;the&nbsp;browser&nbsp;state&nbsp;before<br>
+continuing.</tt></dd></dl>
+
+<dl><dt><a name="ActionRunner-PinchElement"><strong>PinchElement</strong></a>(self, selector<font color="#909090">=None</font>, text<font color="#909090">=None</font>, element_function<font color="#909090">=None</font>, left_anchor_ratio<font color="#909090">=0.5</font>, top_anchor_ratio<font color="#909090">=0.5</font>, scale_factor<font color="#909090">=None</font>, speed_in_pixels_per_second<font color="#909090">=800</font>)</dt><dd><tt>Perform&nbsp;the&nbsp;pinch&nbsp;gesture&nbsp;on&nbsp;an&nbsp;element.<br>
+&nbsp;<br>
+It&nbsp;computes&nbsp;the&nbsp;pinch&nbsp;gesture&nbsp;automatically&nbsp;based&nbsp;on&nbsp;the&nbsp;anchor<br>
+coordinate&nbsp;and&nbsp;the&nbsp;scale&nbsp;factor.&nbsp;The&nbsp;scale&nbsp;factor&nbsp;is&nbsp;the&nbsp;ratio&nbsp;of<br>
+of&nbsp;the&nbsp;final&nbsp;span&nbsp;and&nbsp;the&nbsp;initial&nbsp;span&nbsp;of&nbsp;the&nbsp;gesture.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;selector:&nbsp;A&nbsp;CSS&nbsp;selector&nbsp;describing&nbsp;the&nbsp;element.<br>
+&nbsp;&nbsp;text:&nbsp;The&nbsp;element&nbsp;must&nbsp;contains&nbsp;this&nbsp;exact&nbsp;text.<br>
+&nbsp;&nbsp;element_function:&nbsp;A&nbsp;JavaScript&nbsp;function&nbsp;(as&nbsp;string)&nbsp;that&nbsp;is&nbsp;used<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;to&nbsp;retrieve&nbsp;the&nbsp;element.&nbsp;For&nbsp;example:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'function()&nbsp;{&nbsp;return&nbsp;foo.element;&nbsp;}'.<br>
+&nbsp;&nbsp;left_anchor_ratio:&nbsp;The&nbsp;horizontal&nbsp;pinch&nbsp;anchor&nbsp;coordinate&nbsp;of&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gesture,&nbsp;as&nbsp;a&nbsp;ratio&nbsp;of&nbsp;the&nbsp;visible&nbsp;bounding&nbsp;rectangle&nbsp;for<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;the&nbsp;element.<br>
+&nbsp;&nbsp;top_anchor_ratio:&nbsp;The&nbsp;vertical&nbsp;pinch&nbsp;anchor&nbsp;coordinate&nbsp;of&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gesture,&nbsp;as&nbsp;a&nbsp;ratio&nbsp;of&nbsp;the&nbsp;visible&nbsp;bounding&nbsp;rectangle&nbsp;for<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;the&nbsp;element.<br>
+&nbsp;&nbsp;scale_factor:&nbsp;The&nbsp;ratio&nbsp;of&nbsp;the&nbsp;final&nbsp;span&nbsp;to&nbsp;the&nbsp;initial&nbsp;span.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The&nbsp;default&nbsp;scale&nbsp;factor&nbsp;is<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3.0&nbsp;/&nbsp;(window.outerWidth/window.innerWidth).<br>
+&nbsp;&nbsp;speed_in_pixels_per_second:&nbsp;The&nbsp;speed&nbsp;of&nbsp;the&nbsp;gesture&nbsp;(in&nbsp;pixels/s).</tt></dd></dl>
+
+<dl><dt><a name="ActionRunner-PinchPage"><strong>PinchPage</strong></a>(self, left_anchor_ratio<font color="#909090">=0.5</font>, top_anchor_ratio<font color="#909090">=0.5</font>, scale_factor<font color="#909090">=None</font>, speed_in_pixels_per_second<font color="#909090">=800</font>)</dt><dd><tt>Perform&nbsp;the&nbsp;pinch&nbsp;gesture&nbsp;on&nbsp;the&nbsp;page.<br>
+&nbsp;<br>
+It&nbsp;computes&nbsp;the&nbsp;pinch&nbsp;gesture&nbsp;automatically&nbsp;based&nbsp;on&nbsp;the&nbsp;anchor<br>
+coordinate&nbsp;and&nbsp;the&nbsp;scale&nbsp;factor.&nbsp;The&nbsp;scale&nbsp;factor&nbsp;is&nbsp;the&nbsp;ratio&nbsp;of<br>
+of&nbsp;the&nbsp;final&nbsp;span&nbsp;and&nbsp;the&nbsp;initial&nbsp;span&nbsp;of&nbsp;the&nbsp;gesture.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;left_anchor_ratio:&nbsp;The&nbsp;horizontal&nbsp;pinch&nbsp;anchor&nbsp;coordinate&nbsp;of&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gesture,&nbsp;as&nbsp;a&nbsp;ratio&nbsp;of&nbsp;the&nbsp;visible&nbsp;bounding&nbsp;rectangle&nbsp;for<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;document.body.<br>
+&nbsp;&nbsp;top_anchor_ratio:&nbsp;The&nbsp;vertical&nbsp;pinch&nbsp;anchor&nbsp;coordinate&nbsp;of&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gesture,&nbsp;as&nbsp;a&nbsp;ratio&nbsp;of&nbsp;the&nbsp;visible&nbsp;bounding&nbsp;rectangle&nbsp;for<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;document.body.<br>
+&nbsp;&nbsp;scale_factor:&nbsp;The&nbsp;ratio&nbsp;of&nbsp;the&nbsp;final&nbsp;span&nbsp;to&nbsp;the&nbsp;initial&nbsp;span.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The&nbsp;default&nbsp;scale&nbsp;factor&nbsp;is<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3.0&nbsp;/&nbsp;(window.outerWidth/window.innerWidth).<br>
+&nbsp;&nbsp;speed_in_pixels_per_second:&nbsp;The&nbsp;speed&nbsp;of&nbsp;the&nbsp;gesture&nbsp;(in&nbsp;pixels/s).</tt></dd></dl>
+
+<dl><dt><a name="ActionRunner-PlayMedia"><strong>PlayMedia</strong></a>(self, selector<font color="#909090">=None</font>, playing_event_timeout_in_seconds<font color="#909090">=0</font>, ended_event_timeout_in_seconds<font color="#909090">=0</font>)</dt><dd><tt>Invokes&nbsp;the&nbsp;"play"&nbsp;action&nbsp;on&nbsp;media&nbsp;elements&nbsp;(such&nbsp;as&nbsp;video).<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;selector:&nbsp;A&nbsp;CSS&nbsp;selector&nbsp;describing&nbsp;the&nbsp;element.&nbsp;If&nbsp;none&nbsp;is<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;specified,&nbsp;play&nbsp;the&nbsp;first&nbsp;media&nbsp;element&nbsp;on&nbsp;the&nbsp;page.&nbsp;If&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;selector&nbsp;matches&nbsp;more&nbsp;than&nbsp;1&nbsp;media&nbsp;element,&nbsp;all&nbsp;of&nbsp;them&nbsp;will<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;be&nbsp;played.<br>
+&nbsp;&nbsp;playing_event_timeout_in_seconds:&nbsp;Maximum&nbsp;waiting&nbsp;time&nbsp;for&nbsp;the&nbsp;"playing"<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;event&nbsp;(dispatched&nbsp;when&nbsp;the&nbsp;media&nbsp;begins&nbsp;to&nbsp;play)&nbsp;to&nbsp;be&nbsp;fired.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0&nbsp;means&nbsp;do&nbsp;not&nbsp;wait.<br>
+&nbsp;&nbsp;ended_event_timeout_in_seconds:&nbsp;Maximum&nbsp;waiting&nbsp;time&nbsp;for&nbsp;the&nbsp;"ended"<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;event&nbsp;(dispatched&nbsp;when&nbsp;playback&nbsp;completes)&nbsp;to&nbsp;be&nbsp;fired.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0&nbsp;means&nbsp;do&nbsp;not&nbsp;wait.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;TimeoutException:&nbsp;If&nbsp;the&nbsp;maximum&nbsp;waiting&nbsp;time&nbsp;is&nbsp;exceeded.</tt></dd></dl>
+
+<dl><dt><a name="ActionRunner-ReloadPage"><strong>ReloadPage</strong></a>(self)</dt><dd><tt>Reloads&nbsp;the&nbsp;page.</tt></dd></dl>
+
+<dl><dt><a name="ActionRunner-RepaintContinuously"><strong>RepaintContinuously</strong></a>(self, seconds)</dt><dd><tt>Continuously&nbsp;repaints&nbsp;the&nbsp;visible&nbsp;content.<br>
+&nbsp;<br>
+It&nbsp;does&nbsp;this&nbsp;by&nbsp;requesting&nbsp;animation&nbsp;frames&nbsp;until&nbsp;the&nbsp;given&nbsp;number<br>
+of&nbsp;seconds&nbsp;have&nbsp;elapsed&nbsp;AND&nbsp;at&nbsp;least&nbsp;three&nbsp;RAFs&nbsp;have&nbsp;been<br>
+fired.&nbsp;Times&nbsp;out&nbsp;after&nbsp;max(60,&nbsp;self.<strong>seconds</strong>),&nbsp;if&nbsp;less&nbsp;than&nbsp;three<br>
+RAFs&nbsp;were&nbsp;fired.</tt></dd></dl>
+
+<dl><dt><a name="ActionRunner-RepeatableBrowserDrivenScroll"><strong>RepeatableBrowserDrivenScroll</strong></a>(self, x_scroll_distance_ratio<font color="#909090">=0.0</font>, y_scroll_distance_ratio<font color="#909090">=0.5</font>, repeat_count<font color="#909090">=0</font>, repeat_delay_ms<font color="#909090">=250</font>)</dt><dd><tt>Perform&nbsp;a&nbsp;browser&nbsp;driven&nbsp;repeatable&nbsp;scroll&nbsp;gesture.<br>
+&nbsp;<br>
+The&nbsp;scroll&nbsp;gesture&nbsp;is&nbsp;driven&nbsp;from&nbsp;the&nbsp;browser,&nbsp;this&nbsp;is&nbsp;useful&nbsp;because&nbsp;the<br>
+main&nbsp;thread&nbsp;often&nbsp;isn't&nbsp;resposive&nbsp;but&nbsp;the&nbsp;browser&nbsp;process&nbsp;usually&nbsp;is,&nbsp;so&nbsp;the<br>
+delay&nbsp;between&nbsp;the&nbsp;scroll&nbsp;gestures&nbsp;should&nbsp;be&nbsp;consistent.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;x_scroll_distance_ratio:&nbsp;The&nbsp;horizontal&nbsp;lenght&nbsp;of&nbsp;the&nbsp;scroll&nbsp;as&nbsp;a&nbsp;fraction<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;of&nbsp;the&nbsp;screen&nbsp;width.<br>
+&nbsp;&nbsp;y_scroll_distance_ratio:&nbsp;The&nbsp;vertical&nbsp;lenght&nbsp;of&nbsp;the&nbsp;scroll&nbsp;as&nbsp;a&nbsp;fraction<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;of&nbsp;the&nbsp;screen&nbsp;height.<br>
+&nbsp;&nbsp;repeat_count:&nbsp;The&nbsp;number&nbsp;of&nbsp;additional&nbsp;times&nbsp;to&nbsp;repeat&nbsp;the&nbsp;gesture.<br>
+&nbsp;&nbsp;repeat_delay_ms:&nbsp;The&nbsp;delay&nbsp;in&nbsp;milliseconds&nbsp;between&nbsp;each&nbsp;scroll&nbsp;gesture.</tt></dd></dl>
+
+<dl><dt><a name="ActionRunner-ScrollBounceElement"><strong>ScrollBounceElement</strong></a>(self, selector<font color="#909090">=None</font>, text<font color="#909090">=None</font>, element_function<font color="#909090">=None</font>, left_start_ratio<font color="#909090">=0.5</font>, top_start_ratio<font color="#909090">=0.5</font>, direction<font color="#909090">='down'</font>, distance<font color="#909090">=100</font>, overscroll<font color="#909090">=10</font>, repeat_count<font color="#909090">=10</font>, speed_in_pixels_per_second<font color="#909090">=400</font>)</dt><dd><tt>Perform&nbsp;scroll&nbsp;bounce&nbsp;gesture&nbsp;on&nbsp;the&nbsp;element.<br>
+&nbsp;<br>
+This&nbsp;gesture&nbsp;scrolls&nbsp;on&nbsp;the&nbsp;element&nbsp;by&nbsp;the&nbsp;number&nbsp;of&nbsp;pixels&nbsp;specified&nbsp;in<br>
+distance,&nbsp;in&nbsp;the&nbsp;given&nbsp;direction,&nbsp;followed&nbsp;by&nbsp;a&nbsp;scroll&nbsp;by<br>
+(distance&nbsp;+&nbsp;overscroll)&nbsp;pixels&nbsp;in&nbsp;the&nbsp;opposite&nbsp;direction.<br>
+The&nbsp;above&nbsp;gesture&nbsp;is&nbsp;repeated&nbsp;repeat_count&nbsp;times.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;selector:&nbsp;A&nbsp;CSS&nbsp;selector&nbsp;describing&nbsp;the&nbsp;element.<br>
+&nbsp;&nbsp;text:&nbsp;The&nbsp;element&nbsp;must&nbsp;contains&nbsp;this&nbsp;exact&nbsp;text.<br>
+&nbsp;&nbsp;element_function:&nbsp;A&nbsp;JavaScript&nbsp;function&nbsp;(as&nbsp;string)&nbsp;that&nbsp;is&nbsp;used<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;to&nbsp;retrieve&nbsp;the&nbsp;element.&nbsp;For&nbsp;example:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'function()&nbsp;{&nbsp;return&nbsp;foo.element;&nbsp;}'.<br>
+&nbsp;&nbsp;left_start_ratio:&nbsp;The&nbsp;horizontal&nbsp;starting&nbsp;coordinate&nbsp;of&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gesture,&nbsp;as&nbsp;a&nbsp;ratio&nbsp;of&nbsp;the&nbsp;visible&nbsp;bounding&nbsp;rectangle&nbsp;for<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;document.body.<br>
+&nbsp;&nbsp;top_start_ratio:&nbsp;The&nbsp;vertical&nbsp;starting&nbsp;coordinate&nbsp;of&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gesture,&nbsp;as&nbsp;a&nbsp;ratio&nbsp;of&nbsp;the&nbsp;visible&nbsp;bounding&nbsp;rectangle&nbsp;for<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;document.body.<br>
+&nbsp;&nbsp;direction:&nbsp;The&nbsp;direction&nbsp;of&nbsp;scroll,&nbsp;either&nbsp;'left',&nbsp;'right',<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'up',&nbsp;'down',&nbsp;'upleft',&nbsp;'upright',&nbsp;'downleft',&nbsp;or&nbsp;'downright'<br>
+&nbsp;&nbsp;distance:&nbsp;The&nbsp;distance&nbsp;to&nbsp;scroll&nbsp;(in&nbsp;pixel).<br>
+&nbsp;&nbsp;overscroll:&nbsp;The&nbsp;number&nbsp;of&nbsp;additional&nbsp;pixels&nbsp;to&nbsp;scroll&nbsp;back,&nbsp;in<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;addition&nbsp;to&nbsp;the&nbsp;givendistance.<br>
+&nbsp;&nbsp;repeat_count:&nbsp;How&nbsp;often&nbsp;we&nbsp;want&nbsp;to&nbsp;repeat&nbsp;the&nbsp;full&nbsp;gesture.<br>
+&nbsp;&nbsp;speed_in_pixels_per_second:&nbsp;The&nbsp;speed&nbsp;of&nbsp;the&nbsp;gesture&nbsp;(in&nbsp;pixels/s).</tt></dd></dl>
+
+<dl><dt><a name="ActionRunner-ScrollBouncePage"><strong>ScrollBouncePage</strong></a>(self, left_start_ratio<font color="#909090">=0.5</font>, top_start_ratio<font color="#909090">=0.5</font>, direction<font color="#909090">='down'</font>, distance<font color="#909090">=100</font>, overscroll<font color="#909090">=10</font>, repeat_count<font color="#909090">=10</font>, speed_in_pixels_per_second<font color="#909090">=400</font>)</dt><dd><tt>Perform&nbsp;scroll&nbsp;bounce&nbsp;gesture&nbsp;on&nbsp;the&nbsp;page.<br>
+&nbsp;<br>
+This&nbsp;gesture&nbsp;scrolls&nbsp;the&nbsp;page&nbsp;by&nbsp;the&nbsp;number&nbsp;of&nbsp;pixels&nbsp;specified&nbsp;in<br>
+distance,&nbsp;in&nbsp;the&nbsp;given&nbsp;direction,&nbsp;followed&nbsp;by&nbsp;a&nbsp;scroll&nbsp;by<br>
+(distance&nbsp;+&nbsp;overscroll)&nbsp;pixels&nbsp;in&nbsp;the&nbsp;opposite&nbsp;direction.<br>
+The&nbsp;above&nbsp;gesture&nbsp;is&nbsp;repeated&nbsp;repeat_count&nbsp;times.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;left_start_ratio:&nbsp;The&nbsp;horizontal&nbsp;starting&nbsp;coordinate&nbsp;of&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gesture,&nbsp;as&nbsp;a&nbsp;ratio&nbsp;of&nbsp;the&nbsp;visible&nbsp;bounding&nbsp;rectangle&nbsp;for<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;document.body.<br>
+&nbsp;&nbsp;top_start_ratio:&nbsp;The&nbsp;vertical&nbsp;starting&nbsp;coordinate&nbsp;of&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gesture,&nbsp;as&nbsp;a&nbsp;ratio&nbsp;of&nbsp;the&nbsp;visible&nbsp;bounding&nbsp;rectangle&nbsp;for<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;document.body.<br>
+&nbsp;&nbsp;direction:&nbsp;The&nbsp;direction&nbsp;of&nbsp;scroll,&nbsp;either&nbsp;'left',&nbsp;'right',<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'up',&nbsp;'down',&nbsp;'upleft',&nbsp;'upright',&nbsp;'downleft',&nbsp;or&nbsp;'downright'<br>
+&nbsp;&nbsp;distance:&nbsp;The&nbsp;distance&nbsp;to&nbsp;scroll&nbsp;(in&nbsp;pixel).<br>
+&nbsp;&nbsp;overscroll:&nbsp;The&nbsp;number&nbsp;of&nbsp;additional&nbsp;pixels&nbsp;to&nbsp;scroll&nbsp;back,&nbsp;in<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;addition&nbsp;to&nbsp;the&nbsp;givendistance.<br>
+&nbsp;&nbsp;repeat_count:&nbsp;How&nbsp;often&nbsp;we&nbsp;want&nbsp;to&nbsp;repeat&nbsp;the&nbsp;full&nbsp;gesture.<br>
+&nbsp;&nbsp;speed_in_pixels_per_second:&nbsp;The&nbsp;speed&nbsp;of&nbsp;the&nbsp;gesture&nbsp;(in&nbsp;pixels/s).</tt></dd></dl>
+
+<dl><dt><a name="ActionRunner-ScrollElement"><strong>ScrollElement</strong></a>(self, selector<font color="#909090">=None</font>, text<font color="#909090">=None</font>, element_function<font color="#909090">=None</font>, left_start_ratio<font color="#909090">=0.5</font>, top_start_ratio<font color="#909090">=0.5</font>, direction<font color="#909090">='down'</font>, distance<font color="#909090">=None</font>, distance_expr<font color="#909090">=None</font>, speed_in_pixels_per_second<font color="#909090">=800</font>, use_touch<font color="#909090">=False</font>, synthetic_gesture_source<font color="#909090">='DEFAULT'</font>)</dt><dd><tt>Perform&nbsp;scroll&nbsp;gesture&nbsp;on&nbsp;the&nbsp;element.<br>
+&nbsp;<br>
+The&nbsp;element&nbsp;may&nbsp;be&nbsp;selected&nbsp;via&nbsp;selector,&nbsp;text,&nbsp;or&nbsp;element_function.<br>
+Only&nbsp;one&nbsp;of&nbsp;these&nbsp;arguments&nbsp;must&nbsp;be&nbsp;specified.<br>
+&nbsp;<br>
+You&nbsp;may&nbsp;specify&nbsp;distance&nbsp;or&nbsp;distance_expr,&nbsp;but&nbsp;not&nbsp;both.&nbsp;If<br>
+neither&nbsp;is&nbsp;specified,&nbsp;the&nbsp;default&nbsp;scroll&nbsp;distance&nbsp;is&nbsp;variable<br>
+depending&nbsp;on&nbsp;direction&nbsp;(see&nbsp;scroll.js&nbsp;for&nbsp;full&nbsp;implementation).<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;selector:&nbsp;A&nbsp;CSS&nbsp;selector&nbsp;describing&nbsp;the&nbsp;element.<br>
+&nbsp;&nbsp;text:&nbsp;The&nbsp;element&nbsp;must&nbsp;contains&nbsp;this&nbsp;exact&nbsp;text.<br>
+&nbsp;&nbsp;element_function:&nbsp;A&nbsp;JavaScript&nbsp;function&nbsp;(as&nbsp;string)&nbsp;that&nbsp;is&nbsp;used<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;to&nbsp;retrieve&nbsp;the&nbsp;element.&nbsp;For&nbsp;example:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'function()&nbsp;{&nbsp;return&nbsp;foo.element;&nbsp;}'.<br>
+&nbsp;&nbsp;left_start_ratio:&nbsp;The&nbsp;horizontal&nbsp;starting&nbsp;coordinate&nbsp;of&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gesture,&nbsp;as&nbsp;a&nbsp;ratio&nbsp;of&nbsp;the&nbsp;visible&nbsp;bounding&nbsp;rectangle&nbsp;for<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;the&nbsp;element.<br>
+&nbsp;&nbsp;top_start_ratio:&nbsp;The&nbsp;vertical&nbsp;starting&nbsp;coordinate&nbsp;of&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gesture,&nbsp;as&nbsp;a&nbsp;ratio&nbsp;of&nbsp;the&nbsp;visible&nbsp;bounding&nbsp;rectangle&nbsp;for<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;the&nbsp;element.<br>
+&nbsp;&nbsp;direction:&nbsp;The&nbsp;direction&nbsp;of&nbsp;scroll,&nbsp;either&nbsp;'left',&nbsp;'right',<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'up',&nbsp;'down',&nbsp;'upleft',&nbsp;'upright',&nbsp;'downleft',&nbsp;or&nbsp;'downright'<br>
+&nbsp;&nbsp;distance:&nbsp;The&nbsp;distance&nbsp;to&nbsp;scroll&nbsp;(in&nbsp;pixel).<br>
+&nbsp;&nbsp;distance_expr:&nbsp;A&nbsp;JavaScript&nbsp;expression&nbsp;(as&nbsp;string)&nbsp;that&nbsp;can&nbsp;be<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;evaluated&nbsp;to&nbsp;compute&nbsp;scroll&nbsp;distance.&nbsp;Example:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'window.scrollTop'&nbsp;or&nbsp;'(function()&nbsp;{&nbsp;return&nbsp;crazyMath();&nbsp;})()'.<br>
+&nbsp;&nbsp;speed_in_pixels_per_second:&nbsp;The&nbsp;speed&nbsp;of&nbsp;the&nbsp;gesture&nbsp;(in&nbsp;pixels/s).<br>
+&nbsp;&nbsp;use_touch:&nbsp;Whether&nbsp;scrolling&nbsp;should&nbsp;be&nbsp;done&nbsp;with&nbsp;touch&nbsp;input.<br>
+&nbsp;&nbsp;synthetic_gesture_source:&nbsp;the&nbsp;source&nbsp;input&nbsp;device&nbsp;type&nbsp;for&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;synthetic&nbsp;gesture:&nbsp;'DEFAULT',&nbsp;'TOUCH'&nbsp;or&nbsp;'MOUSE'.</tt></dd></dl>
+
+<dl><dt><a name="ActionRunner-ScrollPage"><strong>ScrollPage</strong></a>(self, left_start_ratio<font color="#909090">=0.5</font>, top_start_ratio<font color="#909090">=0.5</font>, direction<font color="#909090">='down'</font>, distance<font color="#909090">=None</font>, distance_expr<font color="#909090">=None</font>, speed_in_pixels_per_second<font color="#909090">=800</font>, use_touch<font color="#909090">=False</font>, synthetic_gesture_source<font color="#909090">='DEFAULT'</font>)</dt><dd><tt>Perform&nbsp;scroll&nbsp;gesture&nbsp;on&nbsp;the&nbsp;page.<br>
+&nbsp;<br>
+You&nbsp;may&nbsp;specify&nbsp;distance&nbsp;or&nbsp;distance_expr,&nbsp;but&nbsp;not&nbsp;both.&nbsp;If<br>
+neither&nbsp;is&nbsp;specified,&nbsp;the&nbsp;default&nbsp;scroll&nbsp;distance&nbsp;is&nbsp;variable<br>
+depending&nbsp;on&nbsp;direction&nbsp;(see&nbsp;scroll.js&nbsp;for&nbsp;full&nbsp;implementation).<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;left_start_ratio:&nbsp;The&nbsp;horizontal&nbsp;starting&nbsp;coordinate&nbsp;of&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gesture,&nbsp;as&nbsp;a&nbsp;ratio&nbsp;of&nbsp;the&nbsp;visible&nbsp;bounding&nbsp;rectangle&nbsp;for<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;document.body.<br>
+&nbsp;&nbsp;top_start_ratio:&nbsp;The&nbsp;vertical&nbsp;starting&nbsp;coordinate&nbsp;of&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gesture,&nbsp;as&nbsp;a&nbsp;ratio&nbsp;of&nbsp;the&nbsp;visible&nbsp;bounding&nbsp;rectangle&nbsp;for<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;document.body.<br>
+&nbsp;&nbsp;direction:&nbsp;The&nbsp;direction&nbsp;of&nbsp;scroll,&nbsp;either&nbsp;'left',&nbsp;'right',<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'up',&nbsp;'down',&nbsp;'upleft',&nbsp;'upright',&nbsp;'downleft',&nbsp;or&nbsp;'downright'<br>
+&nbsp;&nbsp;distance:&nbsp;The&nbsp;distance&nbsp;to&nbsp;scroll&nbsp;(in&nbsp;pixel).<br>
+&nbsp;&nbsp;distance_expr:&nbsp;A&nbsp;JavaScript&nbsp;expression&nbsp;(as&nbsp;string)&nbsp;that&nbsp;can&nbsp;be<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;evaluated&nbsp;to&nbsp;compute&nbsp;scroll&nbsp;distance.&nbsp;Example:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'window.scrollTop'&nbsp;or&nbsp;'(function()&nbsp;{&nbsp;return&nbsp;crazyMath();&nbsp;})()'.<br>
+&nbsp;&nbsp;speed_in_pixels_per_second:&nbsp;The&nbsp;speed&nbsp;of&nbsp;the&nbsp;gesture&nbsp;(in&nbsp;pixels/s).<br>
+&nbsp;&nbsp;use_touch:&nbsp;Whether&nbsp;scrolling&nbsp;should&nbsp;be&nbsp;done&nbsp;with&nbsp;touch&nbsp;input.<br>
+&nbsp;&nbsp;synthetic_gesture_source:&nbsp;the&nbsp;source&nbsp;input&nbsp;device&nbsp;type&nbsp;for&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;synthetic&nbsp;gesture:&nbsp;'DEFAULT',&nbsp;'TOUCH'&nbsp;or&nbsp;'MOUSE'.</tt></dd></dl>
+
+<dl><dt><a name="ActionRunner-SeekMedia"><strong>SeekMedia</strong></a>(self, seconds, selector<font color="#909090">=None</font>, timeout_in_seconds<font color="#909090">=0</font>, log_time<font color="#909090">=True</font>, label<font color="#909090">=''</font>)</dt><dd><tt>Performs&nbsp;a&nbsp;seek&nbsp;action&nbsp;on&nbsp;media&nbsp;elements&nbsp;(such&nbsp;as&nbsp;video).<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;seconds:&nbsp;The&nbsp;media&nbsp;time&nbsp;to&nbsp;seek&nbsp;to.<br>
+&nbsp;&nbsp;selector:&nbsp;A&nbsp;CSS&nbsp;selector&nbsp;describing&nbsp;the&nbsp;element.&nbsp;If&nbsp;none&nbsp;is<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;specified,&nbsp;seek&nbsp;the&nbsp;first&nbsp;media&nbsp;element&nbsp;on&nbsp;the&nbsp;page.&nbsp;If&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;selector&nbsp;matches&nbsp;more&nbsp;than&nbsp;1&nbsp;media&nbsp;element,&nbsp;all&nbsp;of&nbsp;them&nbsp;will<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;be&nbsp;seeked.<br>
+&nbsp;&nbsp;timeout_in_seconds:&nbsp;Maximum&nbsp;waiting&nbsp;time&nbsp;for&nbsp;the&nbsp;"seeked"&nbsp;event<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(dispatched&nbsp;when&nbsp;the&nbsp;seeked&nbsp;operation&nbsp;completes)&nbsp;to&nbsp;be<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fired.&nbsp;&nbsp;0&nbsp;means&nbsp;do&nbsp;not&nbsp;wait.<br>
+&nbsp;&nbsp;log_time:&nbsp;Whether&nbsp;to&nbsp;log&nbsp;the&nbsp;seek&nbsp;time&nbsp;for&nbsp;the&nbsp;perf<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;measurement.&nbsp;Useful&nbsp;when&nbsp;performing&nbsp;multiple&nbsp;seek.<br>
+&nbsp;&nbsp;label:&nbsp;A&nbsp;suffix&nbsp;string&nbsp;to&nbsp;name&nbsp;the&nbsp;seek&nbsp;perf&nbsp;measurement.<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;TimeoutException:&nbsp;If&nbsp;the&nbsp;maximum&nbsp;waiting&nbsp;time&nbsp;is&nbsp;exceeded.</tt></dd></dl>
+
+<dl><dt><a name="ActionRunner-SwipeElement"><strong>SwipeElement</strong></a>(self, selector<font color="#909090">=None</font>, text<font color="#909090">=None</font>, element_function<font color="#909090">=None</font>, left_start_ratio<font color="#909090">=0.5</font>, top_start_ratio<font color="#909090">=0.5</font>, direction<font color="#909090">='left'</font>, distance<font color="#909090">=100</font>, speed_in_pixels_per_second<font color="#909090">=800</font>)</dt><dd><tt>Perform&nbsp;swipe&nbsp;gesture&nbsp;on&nbsp;the&nbsp;element.<br>
+&nbsp;<br>
+The&nbsp;element&nbsp;may&nbsp;be&nbsp;selected&nbsp;via&nbsp;selector,&nbsp;text,&nbsp;or&nbsp;element_function.<br>
+Only&nbsp;one&nbsp;of&nbsp;these&nbsp;arguments&nbsp;must&nbsp;be&nbsp;specified.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;selector:&nbsp;A&nbsp;CSS&nbsp;selector&nbsp;describing&nbsp;the&nbsp;element.<br>
+&nbsp;&nbsp;text:&nbsp;The&nbsp;element&nbsp;must&nbsp;contains&nbsp;this&nbsp;exact&nbsp;text.<br>
+&nbsp;&nbsp;element_function:&nbsp;A&nbsp;JavaScript&nbsp;function&nbsp;(as&nbsp;string)&nbsp;that&nbsp;is&nbsp;used<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;to&nbsp;retrieve&nbsp;the&nbsp;element.&nbsp;For&nbsp;example:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'function()&nbsp;{&nbsp;return&nbsp;foo.element;&nbsp;}'.<br>
+&nbsp;&nbsp;left_start_ratio:&nbsp;The&nbsp;horizontal&nbsp;starting&nbsp;coordinate&nbsp;of&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gesture,&nbsp;as&nbsp;a&nbsp;ratio&nbsp;of&nbsp;the&nbsp;visible&nbsp;bounding&nbsp;rectangle&nbsp;for<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;the&nbsp;element.<br>
+&nbsp;&nbsp;top_start_ratio:&nbsp;The&nbsp;vertical&nbsp;starting&nbsp;coordinate&nbsp;of&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gesture,&nbsp;as&nbsp;a&nbsp;ratio&nbsp;of&nbsp;the&nbsp;visible&nbsp;bounding&nbsp;rectangle&nbsp;for<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;the&nbsp;element.<br>
+&nbsp;&nbsp;direction:&nbsp;The&nbsp;direction&nbsp;of&nbsp;swipe,&nbsp;either&nbsp;'left',&nbsp;'right',<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'up',&nbsp;or&nbsp;'down'<br>
+&nbsp;&nbsp;distance:&nbsp;The&nbsp;distance&nbsp;to&nbsp;swipe&nbsp;(in&nbsp;pixel).<br>
+&nbsp;&nbsp;speed_in_pixels_per_second:&nbsp;The&nbsp;speed&nbsp;of&nbsp;the&nbsp;gesture&nbsp;(in&nbsp;pixels/s).</tt></dd></dl>
+
+<dl><dt><a name="ActionRunner-SwipePage"><strong>SwipePage</strong></a>(self, left_start_ratio<font color="#909090">=0.5</font>, top_start_ratio<font color="#909090">=0.5</font>, direction<font color="#909090">='left'</font>, distance<font color="#909090">=100</font>, speed_in_pixels_per_second<font color="#909090">=800</font>)</dt><dd><tt>Perform&nbsp;swipe&nbsp;gesture&nbsp;on&nbsp;the&nbsp;page.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;left_start_ratio:&nbsp;The&nbsp;horizontal&nbsp;starting&nbsp;coordinate&nbsp;of&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gesture,&nbsp;as&nbsp;a&nbsp;ratio&nbsp;of&nbsp;the&nbsp;visible&nbsp;bounding&nbsp;rectangle&nbsp;for<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;document.body.<br>
+&nbsp;&nbsp;top_start_ratio:&nbsp;The&nbsp;vertical&nbsp;starting&nbsp;coordinate&nbsp;of&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gesture,&nbsp;as&nbsp;a&nbsp;ratio&nbsp;of&nbsp;the&nbsp;visible&nbsp;bounding&nbsp;rectangle&nbsp;for<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;document.body.<br>
+&nbsp;&nbsp;direction:&nbsp;The&nbsp;direction&nbsp;of&nbsp;swipe,&nbsp;either&nbsp;'left',&nbsp;'right',<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'up',&nbsp;or&nbsp;'down'<br>
+&nbsp;&nbsp;distance:&nbsp;The&nbsp;distance&nbsp;to&nbsp;swipe&nbsp;(in&nbsp;pixel).<br>
+&nbsp;&nbsp;speed_in_pixels_per_second:&nbsp;The&nbsp;speed&nbsp;of&nbsp;the&nbsp;gesture&nbsp;(in&nbsp;pixels/s).</tt></dd></dl>
+
+<dl><dt><a name="ActionRunner-TapElement"><strong>TapElement</strong></a>(self, selector<font color="#909090">=None</font>, text<font color="#909090">=None</font>, element_function<font color="#909090">=None</font>)</dt><dd><tt>Tap&nbsp;an&nbsp;element.<br>
+&nbsp;<br>
+The&nbsp;element&nbsp;may&nbsp;be&nbsp;selected&nbsp;via&nbsp;selector,&nbsp;text,&nbsp;or&nbsp;element_function.<br>
+Only&nbsp;one&nbsp;of&nbsp;these&nbsp;arguments&nbsp;must&nbsp;be&nbsp;specified.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;selector:&nbsp;A&nbsp;CSS&nbsp;selector&nbsp;describing&nbsp;the&nbsp;element.<br>
+&nbsp;&nbsp;text:&nbsp;The&nbsp;element&nbsp;must&nbsp;contains&nbsp;this&nbsp;exact&nbsp;text.<br>
+&nbsp;&nbsp;element_function:&nbsp;A&nbsp;JavaScript&nbsp;function&nbsp;(as&nbsp;string)&nbsp;that&nbsp;is&nbsp;used<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;to&nbsp;retrieve&nbsp;the&nbsp;element.&nbsp;For&nbsp;example:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'(function()&nbsp;{&nbsp;return&nbsp;foo.element;&nbsp;})()'.</tt></dd></dl>
+
+<dl><dt><a name="ActionRunner-Wait"><strong>Wait</strong></a>(self, seconds)</dt><dd><tt>Wait&nbsp;for&nbsp;the&nbsp;number&nbsp;of&nbsp;seconds&nbsp;specified.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;seconds:&nbsp;The&nbsp;number&nbsp;of&nbsp;seconds&nbsp;to&nbsp;wait.</tt></dd></dl>
+
+<dl><dt><a name="ActionRunner-WaitForElement"><strong>WaitForElement</strong></a>(self, selector<font color="#909090">=None</font>, text<font color="#909090">=None</font>, element_function<font color="#909090">=None</font>, timeout_in_seconds<font color="#909090">=60</font>)</dt><dd><tt>Wait&nbsp;for&nbsp;an&nbsp;element&nbsp;to&nbsp;appear&nbsp;in&nbsp;the&nbsp;document.<br>
+&nbsp;<br>
+The&nbsp;element&nbsp;may&nbsp;be&nbsp;selected&nbsp;via&nbsp;selector,&nbsp;text,&nbsp;or&nbsp;element_function.<br>
+Only&nbsp;one&nbsp;of&nbsp;these&nbsp;arguments&nbsp;must&nbsp;be&nbsp;specified.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;selector:&nbsp;A&nbsp;CSS&nbsp;selector&nbsp;describing&nbsp;the&nbsp;element.<br>
+&nbsp;&nbsp;text:&nbsp;The&nbsp;element&nbsp;must&nbsp;contains&nbsp;this&nbsp;exact&nbsp;text.<br>
+&nbsp;&nbsp;element_function:&nbsp;A&nbsp;JavaScript&nbsp;function&nbsp;(as&nbsp;string)&nbsp;that&nbsp;is&nbsp;used<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;to&nbsp;retrieve&nbsp;the&nbsp;element.&nbsp;For&nbsp;example:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'(function()&nbsp;{&nbsp;return&nbsp;foo.element;&nbsp;})()'.<br>
+&nbsp;&nbsp;timeout_in_seconds:&nbsp;The&nbsp;timeout&nbsp;in&nbsp;seconds&nbsp;(default&nbsp;to&nbsp;60).</tt></dd></dl>
+
+<dl><dt><a name="ActionRunner-WaitForJavaScriptCondition"><strong>WaitForJavaScriptCondition</strong></a>(self, condition, timeout_in_seconds<font color="#909090">=60</font>)</dt><dd><tt>Wait&nbsp;for&nbsp;a&nbsp;JavaScript&nbsp;condition&nbsp;to&nbsp;become&nbsp;true.<br>
+&nbsp;<br>
+Example:&nbsp;runner.<a href="#ActionRunner-WaitForJavaScriptCondition">WaitForJavaScriptCondition</a>('window.foo&nbsp;==&nbsp;10');<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;condition:&nbsp;The&nbsp;JavaScript&nbsp;condition&nbsp;(as&nbsp;string).<br>
+&nbsp;&nbsp;timeout_in_seconds:&nbsp;The&nbsp;timeout&nbsp;in&nbsp;seconds&nbsp;(default&nbsp;to&nbsp;60).</tt></dd></dl>
+
+<dl><dt><a name="ActionRunner-WaitForNavigate"><strong>WaitForNavigate</strong></a>(self, timeout_in_seconds_seconds<font color="#909090">=60</font>)</dt></dl>
+
+<dl><dt><a name="ActionRunner-__init__"><strong>__init__</strong></a>(self, tab, skip_waits<font color="#909090">=False</font>)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>tab</strong></dt>
+<dd><tt>Returns&nbsp;the&nbsp;tab&nbsp;on&nbsp;which&nbsp;actions&nbsp;are&nbsp;performed.</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="Interaction">class <strong>Interaction</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="Interaction-Begin"><strong>Begin</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="Interaction-End"><strong>End</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="Interaction-__enter__"><strong>__enter__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="Interaction-__exit__"><strong>__exit__</strong></a>(self, exc_type, exc_value, traceback)</dt></dl>
+
+<dl><dt><a name="Interaction-__init__"><strong>__init__</strong></a>(self, action_runner, label, flags)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>GESTURE_SOURCE_DEFAULT</strong> = 'DEFAULT'<br>
+<strong>SUPPORTED_GESTURE_SOURCES</strong> = ('DEFAULT', 'MOUSE', 'TOUCH')</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.page.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.page.html
new file mode 100644
index 0000000..e403666
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.page.html
@@ -0,0 +1,142 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: package telemetry.page</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.page</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/page/__init__.py">telemetry/page/__init__.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2012&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Package Contents</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.page.action_runner.html">action_runner</a><br>
+<a href="telemetry.page.page.html">page</a><br>
+</td><td width="25%" valign=top><a href="telemetry.page.page_run_end_to_end_unittest.html">page_run_end_to_end_unittest</a><br>
+<a href="telemetry.page.page_test.html">page_test</a><br>
+</td><td width="25%" valign=top><a href="telemetry.page.page_test_unittest.html">page_test_unittest</a><br>
+<a href="telemetry.page.page_unittest.html">page_unittest</a><br>
+</td><td width="25%" valign=top><a href="telemetry.page.shared_page_state.html">shared_page_state</a><br>
+<a href="telemetry.page.shared_page_state_unittest.html">shared_page_state_unittest</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.story.story.html#Story">telemetry.story.story.Story</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.page.html#Page">Page</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="Page">class <strong>Page</strong></a>(<a href="telemetry.story.story.html#Story">telemetry.story.story.Story</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.page.html#Page">Page</a></dd>
+<dd><a href="telemetry.story.story.html#Story">telemetry.story.story.Story</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="Page-AddCustomizeBrowserOptions"><strong>AddCustomizeBrowserOptions</strong></a>(self, options)</dt><dd><tt>Inherit&nbsp;page&nbsp;overrides&nbsp;this&nbsp;to&nbsp;add&nbsp;customized&nbsp;browser&nbsp;options.</tt></dd></dl>
+
+<dl><dt><a name="Page-AsDict"><strong>AsDict</strong></a>(self)</dt><dd><tt>Converts&nbsp;a&nbsp;page&nbsp;object&nbsp;to&nbsp;a&nbsp;dict&nbsp;suitable&nbsp;for&nbsp;JSON&nbsp;output.</tt></dd></dl>
+
+<dl><dt><a name="Page-GetSyntheticDelayCategories"><strong>GetSyntheticDelayCategories</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="Page-Run"><strong>Run</strong></a>(self, shared_state)</dt></dl>
+
+<dl><dt><a name="Page-RunNavigateSteps"><strong>RunNavigateSteps</strong></a>(self, action_runner)</dt></dl>
+
+<dl><dt><a name="Page-RunPageInteractions"><strong>RunPageInteractions</strong></a>(self, action_runner)</dt><dd><tt>Override&nbsp;this&nbsp;to&nbsp;define&nbsp;custom&nbsp;interactions&nbsp;with&nbsp;the&nbsp;page.<br>
+e.g:<br>
+&nbsp;&nbsp;def&nbsp;<a href="#Page-RunPageInteractions">RunPageInteractions</a>(self,&nbsp;action_runner):<br>
+&nbsp;&nbsp;&nbsp;&nbsp;action_runner.ScrollPage()<br>
+&nbsp;&nbsp;&nbsp;&nbsp;action_runner.TapElement(text='Next')</tt></dd></dl>
+
+<dl><dt><a name="Page-__cmp__"><strong>__cmp__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="Page-__init__"><strong>__init__</strong></a>(self, url, page_set<font color="#909090">=None</font>, base_dir<font color="#909090">=None</font>, name<font color="#909090">=''</font>, credentials_path<font color="#909090">=None</font>, credentials_bucket<font color="#909090">='chromium-telemetry'</font>, labels<font color="#909090">=None</font>, startup_url<font color="#909090">=''</font>, make_javascript_deterministic<font color="#909090">=True</font>, shared_page_state_class<font color="#909090">=&lt;class 'telemetry.page.shared_page_state.SharedPageState'&gt;</font>)</dt></dl>
+
+<dl><dt><a name="Page-__lt__"><strong>__lt__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="Page-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>base_dir</strong></dt>
+</dl>
+<dl><dt><strong>credentials_path</strong></dt>
+</dl>
+<dl><dt><strong>display_name</strong></dt>
+</dl>
+<dl><dt><strong>file_path</strong></dt>
+<dd><tt>Returns&nbsp;the&nbsp;path&nbsp;of&nbsp;the&nbsp;file,&nbsp;stripping&nbsp;the&nbsp;scheme&nbsp;and&nbsp;query&nbsp;string.</tt></dd>
+</dl>
+<dl><dt><strong>file_path_url</strong></dt>
+<dd><tt>Returns&nbsp;the&nbsp;file&nbsp;path,&nbsp;including&nbsp;the&nbsp;params,&nbsp;query,&nbsp;and&nbsp;fragment.</tt></dd>
+</dl>
+<dl><dt><strong>file_path_url_with_scheme</strong></dt>
+</dl>
+<dl><dt><strong>is_file</strong></dt>
+<dd><tt>Returns&nbsp;True&nbsp;iff&nbsp;this&nbsp;URL&nbsp;points&nbsp;to&nbsp;a&nbsp;file.</tt></dd>
+</dl>
+<dl><dt><strong>page_set</strong></dt>
+</dl>
+<dl><dt><strong>serving_dir</strong></dt>
+</dl>
+<dl><dt><strong>startup_url</strong></dt>
+</dl>
+<dl><dt><strong>story_set</strong></dt>
+</dl>
+<dl><dt><strong>url</strong></dt>
+</dl>
+<hr>
+Data descriptors inherited from <a href="telemetry.story.story.html#Story">telemetry.story.story.Story</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>file_safe_name</strong></dt>
+<dd><tt>A&nbsp;version&nbsp;of&nbsp;display_name&nbsp;that's&nbsp;safe&nbsp;to&nbsp;use&nbsp;as&nbsp;a&nbsp;filename.<br>
+&nbsp;<br>
+The&nbsp;default&nbsp;implementation&nbsp;sanitizes&nbsp;special&nbsp;characters&nbsp;with&nbsp;underscores,<br>
+but&nbsp;it's&nbsp;okay&nbsp;to&nbsp;override&nbsp;it&nbsp;with&nbsp;a&nbsp;more&nbsp;specific&nbsp;implementation&nbsp;in<br>
+subclasses.</tt></dd>
+</dl>
+<dl><dt><strong>id</strong></dt>
+</dl>
+<dl><dt><strong>is_local</strong></dt>
+<dd><tt>Returns&nbsp;True&nbsp;iff&nbsp;this&nbsp;story&nbsp;does&nbsp;not&nbsp;require&nbsp;network.</tt></dd>
+</dl>
+<dl><dt><strong>labels</strong></dt>
+</dl>
+<dl><dt><strong>make_javascript_deterministic</strong></dt>
+</dl>
+<dl><dt><strong>name</strong></dt>
+</dl>
+<dl><dt><strong>shared_state_class</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.page.page.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.page.page.html
new file mode 100644
index 0000000..8ef184a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.page.page.html
@@ -0,0 +1,25 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.page.page</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.page.html"><font color="#ffffff">page</font></a>.page</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/page/page.py">telemetry/page/page.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2012&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.page.html">telemetry.page</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.page.page_test.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.page.page_test.html
new file mode 100644
index 0000000..b335e1c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.page.page_test.html
@@ -0,0 +1,350 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.page.page_test</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.page.html"><font color="#ffffff">page</font></a>.page_test</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/page/page_test.py">telemetry/page/page_test.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2012&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.page.action_runner.html">telemetry.page.action_runner</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+</td><td width="25%" valign=top><a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.page.page_test.html#PageTest">PageTest</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="exceptions.html#Exception">exceptions.Exception</a>(<a href="exceptions.html#BaseException">exceptions.BaseException</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.page.page_test.html#MultiTabTestAppCrashError">MultiTabTestAppCrashError</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.page.page_test.html#TestNotSupportedOnPlatformError">TestNotSupportedOnPlatformError</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="telemetry.web_perf.story_test.html#Failure">telemetry.web_perf.story_test.Failure</a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.page.page_test.html#MeasurementFailure">MeasurementFailure</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="MeasurementFailure">class <strong>MeasurementFailure</strong></a>(<a href="telemetry.web_perf.story_test.html#Failure">telemetry.web_perf.story_test.Failure</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt><a href="#PageTest">PageTest</a>&nbsp;<a href="exceptions.html#Exception">Exception</a>&nbsp;raised&nbsp;when&nbsp;an&nbsp;undesired&nbsp;but&nbsp;designed-for&nbsp;problem.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.page.page_test.html#MeasurementFailure">MeasurementFailure</a></dd>
+<dd><a href="telemetry.web_perf.story_test.html#Failure">telemetry.web_perf.story_test.Failure</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors inherited from <a href="telemetry.web_perf.story_test.html#Failure">telemetry.web_perf.story_test.Failure</a>:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="MeasurementFailure-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#MeasurementFailure-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#MeasurementFailure-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="MeasurementFailure-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#MeasurementFailure-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="MeasurementFailure-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#MeasurementFailure-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="MeasurementFailure-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#MeasurementFailure-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="MeasurementFailure-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#MeasurementFailure-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="MeasurementFailure-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="MeasurementFailure-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#MeasurementFailure-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="MeasurementFailure-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#MeasurementFailure-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="MeasurementFailure-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="MeasurementFailure-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#MeasurementFailure-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="MeasurementFailure-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="MultiTabTestAppCrashError">class <strong>MultiTabTestAppCrashError</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt><a href="#PageTest">PageTest</a>&nbsp;<a href="exceptions.html#Exception">Exception</a>&nbsp;raised&nbsp;after&nbsp;browser&nbsp;or&nbsp;tab&nbsp;crash&nbsp;for&nbsp;multi-tab&nbsp;tests.<br>
+&nbsp;<br>
+Used&nbsp;to&nbsp;abort&nbsp;the&nbsp;test&nbsp;rather&nbsp;than&nbsp;try&nbsp;to&nbsp;recover&nbsp;from&nbsp;an&nbsp;unknown&nbsp;state.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.page.page_test.html#MultiTabTestAppCrashError">MultiTabTestAppCrashError</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="MultiTabTestAppCrashError-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#MultiTabTestAppCrashError-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#MultiTabTestAppCrashError-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="MultiTabTestAppCrashError-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#MultiTabTestAppCrashError-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="MultiTabTestAppCrashError-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#MultiTabTestAppCrashError-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="MultiTabTestAppCrashError-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#MultiTabTestAppCrashError-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="MultiTabTestAppCrashError-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#MultiTabTestAppCrashError-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="MultiTabTestAppCrashError-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="MultiTabTestAppCrashError-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#MultiTabTestAppCrashError-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="MultiTabTestAppCrashError-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#MultiTabTestAppCrashError-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="MultiTabTestAppCrashError-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="MultiTabTestAppCrashError-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#MultiTabTestAppCrashError-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="MultiTabTestAppCrashError-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="PageTest">class <strong>PageTest</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;class&nbsp;styled&nbsp;on&nbsp;unittest.TestCase&nbsp;for&nbsp;creating&nbsp;page-specific&nbsp;tests.<br>
+&nbsp;<br>
+Test&nbsp;should&nbsp;override&nbsp;ValidateAndMeasurePage&nbsp;to&nbsp;perform&nbsp;test<br>
+validation&nbsp;and&nbsp;page&nbsp;measurement&nbsp;as&nbsp;necessary.<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;class&nbsp;BodyChildElementMeasurement(<a href="#PageTest">PageTest</a>):<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;<a href="#PageTest-ValidateAndMeasurePage">ValidateAndMeasurePage</a>(self,&nbsp;page,&nbsp;tab,&nbsp;results):<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;body_child_count&nbsp;=&nbsp;tab.EvaluateJavaScript(<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'document.body.children.length')<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;results.AddValue(scalar.ScalarValue(<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;page,&nbsp;'body_children',&nbsp;'count',&nbsp;body_child_count))<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="PageTest-CustomizeBrowserOptions"><strong>CustomizeBrowserOptions</strong></a>(self, options)</dt><dd><tt>Override&nbsp;to&nbsp;add&nbsp;test-specific&nbsp;options&nbsp;to&nbsp;the&nbsp;BrowserOptions&nbsp;<a href="__builtin__.html#object">object</a></tt></dd></dl>
+
+<dl><dt><a name="PageTest-DidNavigateToPage"><strong>DidNavigateToPage</strong></a>(self, page, tab)</dt><dd><tt>Override&nbsp;to&nbsp;do&nbsp;operations&nbsp;right&nbsp;after&nbsp;the&nbsp;page&nbsp;is&nbsp;navigated&nbsp;and&nbsp;after<br>
+all&nbsp;waiting&nbsp;for&nbsp;completion&nbsp;has&nbsp;occurred.</tt></dd></dl>
+
+<dl><dt><a name="PageTest-DidRunPage"><strong>DidRunPage</strong></a>(self, platform)</dt><dd><tt>Called&nbsp;after&nbsp;the&nbsp;test&nbsp;run&nbsp;method&nbsp;was&nbsp;run,&nbsp;even&nbsp;if&nbsp;it&nbsp;failed.</tt></dd></dl>
+
+<dl><dt><a name="PageTest-DidStartBrowser"><strong>DidStartBrowser</strong></a>(self, browser)</dt><dd><tt>Override&nbsp;to&nbsp;customize&nbsp;the&nbsp;browser&nbsp;right&nbsp;after&nbsp;it&nbsp;has&nbsp;launched.</tt></dd></dl>
+
+<dl><dt><a name="PageTest-RestartBrowserBeforeEachPage"><strong>RestartBrowserBeforeEachPage</strong></a>(self)</dt><dd><tt>Should&nbsp;the&nbsp;browser&nbsp;be&nbsp;restarted&nbsp;for&nbsp;the&nbsp;page?<br>
+&nbsp;<br>
+This&nbsp;returns&nbsp;true&nbsp;if&nbsp;the&nbsp;test&nbsp;needs&nbsp;to&nbsp;unconditionally&nbsp;restart&nbsp;the<br>
+browser&nbsp;for&nbsp;each&nbsp;page.&nbsp;It&nbsp;may&nbsp;be&nbsp;called&nbsp;before&nbsp;the&nbsp;browser&nbsp;is&nbsp;started.</tt></dd></dl>
+
+<dl><dt><a name="PageTest-RunNavigateSteps"><strong>RunNavigateSteps</strong></a>(self, page, tab)</dt><dd><tt>Navigates&nbsp;the&nbsp;tab&nbsp;to&nbsp;the&nbsp;page&nbsp;URL&nbsp;attribute.<br>
+&nbsp;<br>
+Runs&nbsp;the&nbsp;'navigate_steps'&nbsp;page&nbsp;attribute&nbsp;as&nbsp;a&nbsp;compound&nbsp;action.</tt></dd></dl>
+
+<dl><dt><a name="PageTest-SetOptions"><strong>SetOptions</strong></a>(self, options)</dt><dd><tt>Sets&nbsp;the&nbsp;BrowserFinderOptions&nbsp;instance&nbsp;to&nbsp;use.</tt></dd></dl>
+
+<dl><dt><a name="PageTest-StopBrowserAfterPage"><strong>StopBrowserAfterPage</strong></a>(self, browser, page)</dt><dd><tt>Should&nbsp;the&nbsp;browser&nbsp;be&nbsp;stopped&nbsp;after&nbsp;the&nbsp;page&nbsp;is&nbsp;run?<br>
+&nbsp;<br>
+This&nbsp;is&nbsp;called&nbsp;after&nbsp;a&nbsp;page&nbsp;is&nbsp;run&nbsp;to&nbsp;decide&nbsp;whether&nbsp;the&nbsp;browser&nbsp;needs&nbsp;to<br>
+be&nbsp;stopped&nbsp;to&nbsp;clean&nbsp;up&nbsp;its&nbsp;state.&nbsp;If&nbsp;it&nbsp;is&nbsp;stopped,&nbsp;then&nbsp;it&nbsp;will&nbsp;be<br>
+restarted&nbsp;to&nbsp;run&nbsp;the&nbsp;next&nbsp;page.<br>
+&nbsp;<br>
+A&nbsp;test&nbsp;that&nbsp;overrides&nbsp;this&nbsp;can&nbsp;look&nbsp;at&nbsp;both&nbsp;the&nbsp;page&nbsp;and&nbsp;the&nbsp;browser&nbsp;to<br>
+decide&nbsp;whether&nbsp;it&nbsp;needs&nbsp;to&nbsp;stop&nbsp;the&nbsp;browser.</tt></dd></dl>
+
+<dl><dt><a name="PageTest-TabForPage"><strong>TabForPage</strong></a>(self, page, browser)</dt><dd><tt>Override&nbsp;to&nbsp;select&nbsp;a&nbsp;different&nbsp;tab&nbsp;for&nbsp;the&nbsp;page.&nbsp;&nbsp;For&nbsp;instance,&nbsp;to<br>
+create&nbsp;a&nbsp;new&nbsp;tab&nbsp;for&nbsp;every&nbsp;page,&nbsp;return&nbsp;browser.tabs.New().</tt></dd></dl>
+
+<dl><dt><a name="PageTest-ValidateAndMeasurePage"><strong>ValidateAndMeasurePage</strong></a>(self, page, tab, results)</dt><dd><tt>Override&nbsp;to&nbsp;check&nbsp;test&nbsp;assertions&nbsp;and&nbsp;perform&nbsp;measurement.<br>
+&nbsp;<br>
+When&nbsp;adding&nbsp;measurement&nbsp;results,&nbsp;call&nbsp;results.AddValue(...)&nbsp;for<br>
+each&nbsp;result.&nbsp;Raise&nbsp;an&nbsp;exception&nbsp;or&nbsp;add&nbsp;a&nbsp;failure.FailureValue&nbsp;on<br>
+failure.&nbsp;page_test.py&nbsp;also&nbsp;provides&nbsp;several&nbsp;base&nbsp;exception&nbsp;classes<br>
+to&nbsp;use.<br>
+&nbsp;<br>
+Prefer&nbsp;metric&nbsp;value&nbsp;names&nbsp;that&nbsp;are&nbsp;in&nbsp;accordance&nbsp;with&nbsp;python<br>
+variable&nbsp;style.&nbsp;e.g.,&nbsp;metric_name.&nbsp;The&nbsp;name&nbsp;'url'&nbsp;must&nbsp;not&nbsp;be&nbsp;used.<br>
+&nbsp;<br>
+Put&nbsp;together:<br>
+&nbsp;&nbsp;def&nbsp;<a href="#PageTest-ValidateAndMeasurePage">ValidateAndMeasurePage</a>(self,&nbsp;page,&nbsp;tab,&nbsp;results):<br>
+&nbsp;&nbsp;&nbsp;&nbsp;res&nbsp;=&nbsp;tab.EvaluateJavaScript('2+2')<br>
+&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;res&nbsp;!=&nbsp;4:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raise&nbsp;<a href="exceptions.html#Exception">Exception</a>('Oh,&nbsp;wow.')<br>
+&nbsp;&nbsp;&nbsp;&nbsp;results.AddValue(scalar.ScalarValue(<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;page,&nbsp;'two_plus_two',&nbsp;'count',&nbsp;res))<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;page:&nbsp;A&nbsp;telemetry.page.Page&nbsp;instance.<br>
+&nbsp;&nbsp;tab:&nbsp;A&nbsp;telemetry.core.Tab&nbsp;instance.<br>
+&nbsp;&nbsp;results:&nbsp;A&nbsp;telemetry.results.PageTestResults&nbsp;instance.</tt></dd></dl>
+
+<dl><dt><a name="PageTest-WillNavigateToPage"><strong>WillNavigateToPage</strong></a>(self, page, tab)</dt><dd><tt>Override&nbsp;to&nbsp;do&nbsp;operations&nbsp;before&nbsp;the&nbsp;page&nbsp;is&nbsp;navigated,&nbsp;notably&nbsp;Telemetry<br>
+will&nbsp;already&nbsp;have&nbsp;performed&nbsp;the&nbsp;following&nbsp;operations&nbsp;on&nbsp;the&nbsp;browser&nbsp;before<br>
+calling&nbsp;this&nbsp;function:<br>
+*&nbsp;Ensure&nbsp;only&nbsp;one&nbsp;tab&nbsp;is&nbsp;open.<br>
+*&nbsp;Call&nbsp;WaitForDocumentReadyStateToComplete&nbsp;on&nbsp;the&nbsp;tab.</tt></dd></dl>
+
+<dl><dt><a name="PageTest-WillStartBrowser"><strong>WillStartBrowser</strong></a>(self, platform)</dt><dd><tt>Override&nbsp;to&nbsp;manipulate&nbsp;the&nbsp;browser&nbsp;environment&nbsp;before&nbsp;it&nbsp;launches.</tt></dd></dl>
+
+<dl><dt><a name="PageTest-__init__"><strong>__init__</strong></a>(self, needs_browser_restart_after_each_page<font color="#909090">=False</font>, clear_cache_before_each_run<font color="#909090">=False</font>)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>clear_cache_before_each_run</strong></dt>
+<dd><tt>When&nbsp;set&nbsp;to&nbsp;True,&nbsp;the&nbsp;browser's&nbsp;disk&nbsp;and&nbsp;memory&nbsp;cache&nbsp;will&nbsp;be&nbsp;cleared<br>
+before&nbsp;each&nbsp;run.</tt></dd>
+</dl>
+<dl><dt><strong>close_tabs_before_run</strong></dt>
+<dd><tt>When&nbsp;set&nbsp;to&nbsp;True,&nbsp;all&nbsp;tabs&nbsp;are&nbsp;closed&nbsp;before&nbsp;running&nbsp;the&nbsp;test&nbsp;for&nbsp;the<br>
+first&nbsp;time.</tt></dd>
+</dl>
+<dl><dt><strong>is_multi_tab_test</strong></dt>
+<dd><tt>Returns&nbsp;True&nbsp;if&nbsp;the&nbsp;test&nbsp;opens&nbsp;multiple&nbsp;tabs.<br>
+&nbsp;<br>
+If&nbsp;the&nbsp;test&nbsp;overrides&nbsp;TabForPage,&nbsp;it&nbsp;is&nbsp;deemed&nbsp;a&nbsp;multi-tab&nbsp;test.<br>
+Multi-tab&nbsp;tests&nbsp;do&nbsp;not&nbsp;retry&nbsp;after&nbsp;tab&nbsp;or&nbsp;browser&nbsp;crashes,&nbsp;whereas,<br>
+single-tab&nbsp;tests&nbsp;too.&nbsp;That&nbsp;is&nbsp;because&nbsp;the&nbsp;state&nbsp;of&nbsp;multi-tab&nbsp;tests<br>
+(e.g.,&nbsp;how&nbsp;many&nbsp;tabs&nbsp;are&nbsp;open,&nbsp;etc.)&nbsp;is&nbsp;unknown&nbsp;after&nbsp;crashes.</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TestNotSupportedOnPlatformError">class <strong>TestNotSupportedOnPlatformError</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt><a href="#PageTest">PageTest</a>&nbsp;<a href="exceptions.html#Exception">Exception</a>&nbsp;raised&nbsp;when&nbsp;a&nbsp;required&nbsp;feature&nbsp;is&nbsp;unavailable.<br>
+&nbsp;<br>
+The&nbsp;feature&nbsp;required&nbsp;to&nbsp;run&nbsp;the&nbsp;test&nbsp;could&nbsp;be&nbsp;part&nbsp;of&nbsp;the&nbsp;platform,<br>
+hardware&nbsp;configuration,&nbsp;or&nbsp;browser.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.page.page_test.html#TestNotSupportedOnPlatformError">TestNotSupportedOnPlatformError</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="TestNotSupportedOnPlatformError-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#TestNotSupportedOnPlatformError-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#TestNotSupportedOnPlatformError-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="TestNotSupportedOnPlatformError-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#TestNotSupportedOnPlatformError-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="TestNotSupportedOnPlatformError-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#TestNotSupportedOnPlatformError-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="TestNotSupportedOnPlatformError-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#TestNotSupportedOnPlatformError-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="TestNotSupportedOnPlatformError-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#TestNotSupportedOnPlatformError-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="TestNotSupportedOnPlatformError-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="TestNotSupportedOnPlatformError-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#TestNotSupportedOnPlatformError-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="TestNotSupportedOnPlatformError-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#TestNotSupportedOnPlatformError-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="TestNotSupportedOnPlatformError-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="TestNotSupportedOnPlatformError-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#TestNotSupportedOnPlatformError-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="TestNotSupportedOnPlatformError-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.page.shared_page_state.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.page.shared_page_state.html
new file mode 100644
index 0000000..326d143
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.page.shared_page_state.html
@@ -0,0 +1,385 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.page.shared_page_state</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.page.html"><font color="#ffffff">page</font></a>.shared_page_state</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/page/shared_page_state.py">telemetry/page/shared_page_state.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.browser.browser_finder.html">telemetry.internal.browser.browser_finder</a><br>
+<a href="telemetry.internal.browser.browser_finder_exceptions.html">telemetry.internal.browser.browser_finder_exceptions</a><br>
+<a href="telemetry.internal.browser.browser_info.html">telemetry.internal.browser.browser_info</a><br>
+<a href="catapult_base.cloud_storage.html">catapult_base.cloud_storage</a><br>
+<a href="telemetry.decorators.html">telemetry.decorators</a><br>
+<a href="telemetry.internal.util.exception_formatter.html">telemetry.internal.util.exception_formatter</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+<a href="telemetry.internal.util.file_handle.html">telemetry.internal.util.file_handle</a><br>
+<a href="telemetry.util.image_util.html">telemetry.util.image_util</a><br>
+<a href="logging.html">logging</a><br>
+<a href="os.html">os</a><br>
+<a href="telemetry.page.page_test.html">telemetry.page.page_test</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.platform.profiler.profiler_finder.html">telemetry.internal.platform.profiler.profiler_finder</a><br>
+<a href="shutil.html">shutil</a><br>
+<a href="telemetry.story.html">telemetry.story</a><br>
+<a href="sys.html">sys</a><br>
+<a href="tempfile.html">tempfile</a><br>
+<a href="telemetry.web_perf.timeline_based_measurement.html">telemetry.web_perf.timeline_based_measurement</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.util.html">telemetry.core.util</a><br>
+<a href="telemetry.util.wpr_modes.html">telemetry.util.wpr_modes</a><br>
+<a href="zipfile.html">zipfile</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.story.shared_state.html#SharedState">telemetry.story.shared_state.SharedState</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.page.shared_page_state.html#SharedPageState">SharedPageState</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.page.shared_page_state.html#Shared10InchTabletPageState">Shared10InchTabletPageState</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.page.shared_page_state.html#SharedDesktopPageState">SharedDesktopPageState</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.page.shared_page_state.html#SharedMobilePageState">SharedMobilePageState</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.page.shared_page_state.html#SharedTabletPageState">SharedTabletPageState</a>
+</font></dt></dl>
+</dd>
+</dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="Shared10InchTabletPageState">class <strong>Shared10InchTabletPageState</strong></a>(<a href="telemetry.page.shared_page_state.html#SharedPageState">SharedPageState</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.page.shared_page_state.html#Shared10InchTabletPageState">Shared10InchTabletPageState</a></dd>
+<dd><a href="telemetry.page.shared_page_state.html#SharedPageState">SharedPageState</a></dd>
+<dd><a href="telemetry.story.shared_state.html#SharedState">telemetry.story.shared_state.SharedState</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.page.shared_page_state.html#SharedPageState">SharedPageState</a>:<br>
+<dl><dt><a name="Shared10InchTabletPageState-CanRunOnBrowser"><strong>CanRunOnBrowser</strong></a>(self, browser_info, page)</dt><dd><tt>Override&nbsp;this&nbsp;to&nbsp;return&nbsp;whether&nbsp;the&nbsp;browser&nbsp;brought&nbsp;up&nbsp;by&nbsp;this&nbsp;state<br>
+instance&nbsp;is&nbsp;suitable&nbsp;for&nbsp;running&nbsp;the&nbsp;given&nbsp;page.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;browser_info:&nbsp;an&nbsp;instance&nbsp;of&nbsp;telemetry.core.browser_info.BrowserInfo<br>
+&nbsp;&nbsp;page:&nbsp;an&nbsp;instance&nbsp;of&nbsp;telemetry.page.Page</tt></dd></dl>
+
+<dl><dt><a name="Shared10InchTabletPageState-CanRunStory"><strong>CanRunStory</strong></a>(self, page)</dt></dl>
+
+<dl><dt><a name="Shared10InchTabletPageState-DidRunStory"><strong>DidRunStory</strong></a>(self, results)</dt></dl>
+
+<dl><dt><a name="Shared10InchTabletPageState-GetPregeneratedProfileArchiveDir"><strong>GetPregeneratedProfileArchiveDir</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="Shared10InchTabletPageState-RunStory"><strong>RunStory</strong></a>(self, results)</dt></dl>
+
+<dl><dt><a name="Shared10InchTabletPageState-SetPregeneratedProfileArchiveDir"><strong>SetPregeneratedProfileArchiveDir</strong></a>(self, archive_path)</dt><dd><tt>Benchmarks&nbsp;can&nbsp;set&nbsp;a&nbsp;pre-generated&nbsp;profile&nbsp;archive&nbsp;to&nbsp;indicate&nbsp;that&nbsp;when<br>
+Chrome&nbsp;is&nbsp;launched,&nbsp;it&nbsp;should&nbsp;have&nbsp;a&nbsp;--user-data-dir&nbsp;set&nbsp;to&nbsp;the<br>
+pregenerated&nbsp;profile,&nbsp;rather&nbsp;than&nbsp;to&nbsp;an&nbsp;empty&nbsp;profile.<br>
+&nbsp;<br>
+If&nbsp;the&nbsp;benchmark&nbsp;is&nbsp;invoked&nbsp;with&nbsp;the&nbsp;option&nbsp;--profile-dir=&lt;dir&gt;,&nbsp;that<br>
+option&nbsp;overrides&nbsp;this&nbsp;value.</tt></dd></dl>
+
+<dl><dt><a name="Shared10InchTabletPageState-TearDownState"><strong>TearDownState</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="Shared10InchTabletPageState-WillRunStory"><strong>WillRunStory</strong></a>(self, page)</dt></dl>
+
+<dl><dt><a name="Shared10InchTabletPageState-__init__"><strong>__init__</strong></a>(self, test, finder_options, story_set)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.page.shared_page_state.html#SharedPageState">SharedPageState</a>:<br>
+<dl><dt><strong>browser</strong></dt>
+</dl>
+<dl><dt><strong>current_page</strong></dt>
+</dl>
+<dl><dt><strong>current_tab</strong></dt>
+</dl>
+<dl><dt><strong>page_test</strong></dt>
+</dl>
+<dl><dt><strong>platform</strong></dt>
+</dl>
+<hr>
+Data descriptors inherited from <a href="telemetry.story.shared_state.html#SharedState">telemetry.story.shared_state.SharedState</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="SharedDesktopPageState">class <strong>SharedDesktopPageState</strong></a>(<a href="telemetry.page.shared_page_state.html#SharedPageState">SharedPageState</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.page.shared_page_state.html#SharedDesktopPageState">SharedDesktopPageState</a></dd>
+<dd><a href="telemetry.page.shared_page_state.html#SharedPageState">SharedPageState</a></dd>
+<dd><a href="telemetry.story.shared_state.html#SharedState">telemetry.story.shared_state.SharedState</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.page.shared_page_state.html#SharedPageState">SharedPageState</a>:<br>
+<dl><dt><a name="SharedDesktopPageState-CanRunOnBrowser"><strong>CanRunOnBrowser</strong></a>(self, browser_info, page)</dt><dd><tt>Override&nbsp;this&nbsp;to&nbsp;return&nbsp;whether&nbsp;the&nbsp;browser&nbsp;brought&nbsp;up&nbsp;by&nbsp;this&nbsp;state<br>
+instance&nbsp;is&nbsp;suitable&nbsp;for&nbsp;running&nbsp;the&nbsp;given&nbsp;page.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;browser_info:&nbsp;an&nbsp;instance&nbsp;of&nbsp;telemetry.core.browser_info.BrowserInfo<br>
+&nbsp;&nbsp;page:&nbsp;an&nbsp;instance&nbsp;of&nbsp;telemetry.page.Page</tt></dd></dl>
+
+<dl><dt><a name="SharedDesktopPageState-CanRunStory"><strong>CanRunStory</strong></a>(self, page)</dt></dl>
+
+<dl><dt><a name="SharedDesktopPageState-DidRunStory"><strong>DidRunStory</strong></a>(self, results)</dt></dl>
+
+<dl><dt><a name="SharedDesktopPageState-GetPregeneratedProfileArchiveDir"><strong>GetPregeneratedProfileArchiveDir</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="SharedDesktopPageState-RunStory"><strong>RunStory</strong></a>(self, results)</dt></dl>
+
+<dl><dt><a name="SharedDesktopPageState-SetPregeneratedProfileArchiveDir"><strong>SetPregeneratedProfileArchiveDir</strong></a>(self, archive_path)</dt><dd><tt>Benchmarks&nbsp;can&nbsp;set&nbsp;a&nbsp;pre-generated&nbsp;profile&nbsp;archive&nbsp;to&nbsp;indicate&nbsp;that&nbsp;when<br>
+Chrome&nbsp;is&nbsp;launched,&nbsp;it&nbsp;should&nbsp;have&nbsp;a&nbsp;--user-data-dir&nbsp;set&nbsp;to&nbsp;the<br>
+pregenerated&nbsp;profile,&nbsp;rather&nbsp;than&nbsp;to&nbsp;an&nbsp;empty&nbsp;profile.<br>
+&nbsp;<br>
+If&nbsp;the&nbsp;benchmark&nbsp;is&nbsp;invoked&nbsp;with&nbsp;the&nbsp;option&nbsp;--profile-dir=&lt;dir&gt;,&nbsp;that<br>
+option&nbsp;overrides&nbsp;this&nbsp;value.</tt></dd></dl>
+
+<dl><dt><a name="SharedDesktopPageState-TearDownState"><strong>TearDownState</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="SharedDesktopPageState-WillRunStory"><strong>WillRunStory</strong></a>(self, page)</dt></dl>
+
+<dl><dt><a name="SharedDesktopPageState-__init__"><strong>__init__</strong></a>(self, test, finder_options, story_set)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.page.shared_page_state.html#SharedPageState">SharedPageState</a>:<br>
+<dl><dt><strong>browser</strong></dt>
+</dl>
+<dl><dt><strong>current_page</strong></dt>
+</dl>
+<dl><dt><strong>current_tab</strong></dt>
+</dl>
+<dl><dt><strong>page_test</strong></dt>
+</dl>
+<dl><dt><strong>platform</strong></dt>
+</dl>
+<hr>
+Data descriptors inherited from <a href="telemetry.story.shared_state.html#SharedState">telemetry.story.shared_state.SharedState</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="SharedMobilePageState">class <strong>SharedMobilePageState</strong></a>(<a href="telemetry.page.shared_page_state.html#SharedPageState">SharedPageState</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.page.shared_page_state.html#SharedMobilePageState">SharedMobilePageState</a></dd>
+<dd><a href="telemetry.page.shared_page_state.html#SharedPageState">SharedPageState</a></dd>
+<dd><a href="telemetry.story.shared_state.html#SharedState">telemetry.story.shared_state.SharedState</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.page.shared_page_state.html#SharedPageState">SharedPageState</a>:<br>
+<dl><dt><a name="SharedMobilePageState-CanRunOnBrowser"><strong>CanRunOnBrowser</strong></a>(self, browser_info, page)</dt><dd><tt>Override&nbsp;this&nbsp;to&nbsp;return&nbsp;whether&nbsp;the&nbsp;browser&nbsp;brought&nbsp;up&nbsp;by&nbsp;this&nbsp;state<br>
+instance&nbsp;is&nbsp;suitable&nbsp;for&nbsp;running&nbsp;the&nbsp;given&nbsp;page.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;browser_info:&nbsp;an&nbsp;instance&nbsp;of&nbsp;telemetry.core.browser_info.BrowserInfo<br>
+&nbsp;&nbsp;page:&nbsp;an&nbsp;instance&nbsp;of&nbsp;telemetry.page.Page</tt></dd></dl>
+
+<dl><dt><a name="SharedMobilePageState-CanRunStory"><strong>CanRunStory</strong></a>(self, page)</dt></dl>
+
+<dl><dt><a name="SharedMobilePageState-DidRunStory"><strong>DidRunStory</strong></a>(self, results)</dt></dl>
+
+<dl><dt><a name="SharedMobilePageState-GetPregeneratedProfileArchiveDir"><strong>GetPregeneratedProfileArchiveDir</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="SharedMobilePageState-RunStory"><strong>RunStory</strong></a>(self, results)</dt></dl>
+
+<dl><dt><a name="SharedMobilePageState-SetPregeneratedProfileArchiveDir"><strong>SetPregeneratedProfileArchiveDir</strong></a>(self, archive_path)</dt><dd><tt>Benchmarks&nbsp;can&nbsp;set&nbsp;a&nbsp;pre-generated&nbsp;profile&nbsp;archive&nbsp;to&nbsp;indicate&nbsp;that&nbsp;when<br>
+Chrome&nbsp;is&nbsp;launched,&nbsp;it&nbsp;should&nbsp;have&nbsp;a&nbsp;--user-data-dir&nbsp;set&nbsp;to&nbsp;the<br>
+pregenerated&nbsp;profile,&nbsp;rather&nbsp;than&nbsp;to&nbsp;an&nbsp;empty&nbsp;profile.<br>
+&nbsp;<br>
+If&nbsp;the&nbsp;benchmark&nbsp;is&nbsp;invoked&nbsp;with&nbsp;the&nbsp;option&nbsp;--profile-dir=&lt;dir&gt;,&nbsp;that<br>
+option&nbsp;overrides&nbsp;this&nbsp;value.</tt></dd></dl>
+
+<dl><dt><a name="SharedMobilePageState-TearDownState"><strong>TearDownState</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="SharedMobilePageState-WillRunStory"><strong>WillRunStory</strong></a>(self, page)</dt></dl>
+
+<dl><dt><a name="SharedMobilePageState-__init__"><strong>__init__</strong></a>(self, test, finder_options, story_set)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.page.shared_page_state.html#SharedPageState">SharedPageState</a>:<br>
+<dl><dt><strong>browser</strong></dt>
+</dl>
+<dl><dt><strong>current_page</strong></dt>
+</dl>
+<dl><dt><strong>current_tab</strong></dt>
+</dl>
+<dl><dt><strong>page_test</strong></dt>
+</dl>
+<dl><dt><strong>platform</strong></dt>
+</dl>
+<hr>
+Data descriptors inherited from <a href="telemetry.story.shared_state.html#SharedState">telemetry.story.shared_state.SharedState</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="SharedPageState">class <strong>SharedPageState</strong></a>(<a href="telemetry.story.shared_state.html#SharedState">telemetry.story.shared_state.SharedState</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>This&nbsp;class&nbsp;contains&nbsp;all&nbsp;specific&nbsp;logic&nbsp;necessary&nbsp;to&nbsp;run&nbsp;a&nbsp;Chrome&nbsp;browser<br>
+benchmark.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.page.shared_page_state.html#SharedPageState">SharedPageState</a></dd>
+<dd><a href="telemetry.story.shared_state.html#SharedState">telemetry.story.shared_state.SharedState</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="SharedPageState-CanRunOnBrowser"><strong>CanRunOnBrowser</strong></a>(self, browser_info, page)</dt><dd><tt>Override&nbsp;this&nbsp;to&nbsp;return&nbsp;whether&nbsp;the&nbsp;browser&nbsp;brought&nbsp;up&nbsp;by&nbsp;this&nbsp;state<br>
+instance&nbsp;is&nbsp;suitable&nbsp;for&nbsp;running&nbsp;the&nbsp;given&nbsp;page.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;browser_info:&nbsp;an&nbsp;instance&nbsp;of&nbsp;telemetry.core.browser_info.BrowserInfo<br>
+&nbsp;&nbsp;page:&nbsp;an&nbsp;instance&nbsp;of&nbsp;telemetry.page.Page</tt></dd></dl>
+
+<dl><dt><a name="SharedPageState-CanRunStory"><strong>CanRunStory</strong></a>(self, page)</dt></dl>
+
+<dl><dt><a name="SharedPageState-DidRunStory"><strong>DidRunStory</strong></a>(self, results)</dt></dl>
+
+<dl><dt><a name="SharedPageState-GetPregeneratedProfileArchiveDir"><strong>GetPregeneratedProfileArchiveDir</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="SharedPageState-RunStory"><strong>RunStory</strong></a>(self, results)</dt></dl>
+
+<dl><dt><a name="SharedPageState-SetPregeneratedProfileArchiveDir"><strong>SetPregeneratedProfileArchiveDir</strong></a>(self, archive_path)</dt><dd><tt>Benchmarks&nbsp;can&nbsp;set&nbsp;a&nbsp;pre-generated&nbsp;profile&nbsp;archive&nbsp;to&nbsp;indicate&nbsp;that&nbsp;when<br>
+Chrome&nbsp;is&nbsp;launched,&nbsp;it&nbsp;should&nbsp;have&nbsp;a&nbsp;--user-data-dir&nbsp;set&nbsp;to&nbsp;the<br>
+pregenerated&nbsp;profile,&nbsp;rather&nbsp;than&nbsp;to&nbsp;an&nbsp;empty&nbsp;profile.<br>
+&nbsp;<br>
+If&nbsp;the&nbsp;benchmark&nbsp;is&nbsp;invoked&nbsp;with&nbsp;the&nbsp;option&nbsp;--profile-dir=&lt;dir&gt;,&nbsp;that<br>
+option&nbsp;overrides&nbsp;this&nbsp;value.</tt></dd></dl>
+
+<dl><dt><a name="SharedPageState-TearDownState"><strong>TearDownState</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="SharedPageState-WillRunStory"><strong>WillRunStory</strong></a>(self, page)</dt></dl>
+
+<dl><dt><a name="SharedPageState-__init__"><strong>__init__</strong></a>(self, test, finder_options, story_set)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>browser</strong></dt>
+</dl>
+<dl><dt><strong>current_page</strong></dt>
+</dl>
+<dl><dt><strong>current_tab</strong></dt>
+</dl>
+<dl><dt><strong>page_test</strong></dt>
+</dl>
+<dl><dt><strong>platform</strong></dt>
+</dl>
+<hr>
+Data descriptors inherited from <a href="telemetry.story.shared_state.html#SharedState">telemetry.story.shared_state.SharedState</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="SharedTabletPageState">class <strong>SharedTabletPageState</strong></a>(<a href="telemetry.page.shared_page_state.html#SharedPageState">SharedPageState</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.page.shared_page_state.html#SharedTabletPageState">SharedTabletPageState</a></dd>
+<dd><a href="telemetry.page.shared_page_state.html#SharedPageState">SharedPageState</a></dd>
+<dd><a href="telemetry.story.shared_state.html#SharedState">telemetry.story.shared_state.SharedState</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.page.shared_page_state.html#SharedPageState">SharedPageState</a>:<br>
+<dl><dt><a name="SharedTabletPageState-CanRunOnBrowser"><strong>CanRunOnBrowser</strong></a>(self, browser_info, page)</dt><dd><tt>Override&nbsp;this&nbsp;to&nbsp;return&nbsp;whether&nbsp;the&nbsp;browser&nbsp;brought&nbsp;up&nbsp;by&nbsp;this&nbsp;state<br>
+instance&nbsp;is&nbsp;suitable&nbsp;for&nbsp;running&nbsp;the&nbsp;given&nbsp;page.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;browser_info:&nbsp;an&nbsp;instance&nbsp;of&nbsp;telemetry.core.browser_info.BrowserInfo<br>
+&nbsp;&nbsp;page:&nbsp;an&nbsp;instance&nbsp;of&nbsp;telemetry.page.Page</tt></dd></dl>
+
+<dl><dt><a name="SharedTabletPageState-CanRunStory"><strong>CanRunStory</strong></a>(self, page)</dt></dl>
+
+<dl><dt><a name="SharedTabletPageState-DidRunStory"><strong>DidRunStory</strong></a>(self, results)</dt></dl>
+
+<dl><dt><a name="SharedTabletPageState-GetPregeneratedProfileArchiveDir"><strong>GetPregeneratedProfileArchiveDir</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="SharedTabletPageState-RunStory"><strong>RunStory</strong></a>(self, results)</dt></dl>
+
+<dl><dt><a name="SharedTabletPageState-SetPregeneratedProfileArchiveDir"><strong>SetPregeneratedProfileArchiveDir</strong></a>(self, archive_path)</dt><dd><tt>Benchmarks&nbsp;can&nbsp;set&nbsp;a&nbsp;pre-generated&nbsp;profile&nbsp;archive&nbsp;to&nbsp;indicate&nbsp;that&nbsp;when<br>
+Chrome&nbsp;is&nbsp;launched,&nbsp;it&nbsp;should&nbsp;have&nbsp;a&nbsp;--user-data-dir&nbsp;set&nbsp;to&nbsp;the<br>
+pregenerated&nbsp;profile,&nbsp;rather&nbsp;than&nbsp;to&nbsp;an&nbsp;empty&nbsp;profile.<br>
+&nbsp;<br>
+If&nbsp;the&nbsp;benchmark&nbsp;is&nbsp;invoked&nbsp;with&nbsp;the&nbsp;option&nbsp;--profile-dir=&lt;dir&gt;,&nbsp;that<br>
+option&nbsp;overrides&nbsp;this&nbsp;value.</tt></dd></dl>
+
+<dl><dt><a name="SharedTabletPageState-TearDownState"><strong>TearDownState</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="SharedTabletPageState-WillRunStory"><strong>WillRunStory</strong></a>(self, page)</dt></dl>
+
+<dl><dt><a name="SharedTabletPageState-__init__"><strong>__init__</strong></a>(self, test, finder_options, story_set)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.page.shared_page_state.html#SharedPageState">SharedPageState</a>:<br>
+<dl><dt><strong>browser</strong></dt>
+</dl>
+<dl><dt><strong>current_page</strong></dt>
+</dl>
+<dl><dt><strong>current_tab</strong></dt>
+</dl>
+<dl><dt><strong>page_test</strong></dt>
+</dl>
+<dl><dt><strong>platform</strong></dt>
+</dl>
+<hr>
+Data descriptors inherited from <a href="telemetry.story.shared_state.html#SharedState">telemetry.story.shared_state.SharedState</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.project_config.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.project_config.html
new file mode 100644
index 0000000..111ea3e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.project_config.html
@@ -0,0 +1,73 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.project_config</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.project_config</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/project_config.py">telemetry/project_config.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.project_config.html#ProjectConfig">ProjectConfig</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ProjectConfig">class <strong>ProjectConfig</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Contains&nbsp;information&nbsp;about&nbsp;the&nbsp;benchmark&nbsp;runtime&nbsp;environment.<br>
+&nbsp;<br>
+Attributes:<br>
+&nbsp;&nbsp;top_level_dir:&nbsp;A&nbsp;dir&nbsp;that&nbsp;contains&nbsp;benchmark,&nbsp;page&nbsp;test,&nbsp;and/or&nbsp;story<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;set&nbsp;dirs&nbsp;and&nbsp;associated&nbsp;artifacts.<br>
+&nbsp;&nbsp;benchmark_dirs:&nbsp;A&nbsp;list&nbsp;of&nbsp;dirs&nbsp;containing&nbsp;benchmarks.<br>
+&nbsp;&nbsp;benchmark_aliases:&nbsp;A&nbsp;dict&nbsp;of&nbsp;name:alias&nbsp;string&nbsp;pairs&nbsp;to&nbsp;be&nbsp;matched&nbsp;against<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exactly&nbsp;during&nbsp;benchmark&nbsp;selection.<br>
+&nbsp;&nbsp;client_config:&nbsp;A&nbsp;path&nbsp;to&nbsp;a&nbsp;ProjectDependencies&nbsp;json&nbsp;file.<br>
+&nbsp;&nbsp;default_chrome_root:&nbsp;A&nbsp;path&nbsp;to&nbsp;chromium&nbsp;source&nbsp;directory.&nbsp;Many&nbsp;telemetry<br>
+&nbsp;&nbsp;&nbsp;&nbsp;features&nbsp;depend&nbsp;on&nbsp;chromium&nbsp;source&nbsp;tree's&nbsp;presence&nbsp;and&nbsp;those&nbsp;won't&nbsp;work<br>
+&nbsp;&nbsp;&nbsp;&nbsp;in&nbsp;case&nbsp;this&nbsp;is&nbsp;not&nbsp;specified.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="ProjectConfig-__init__"><strong>__init__</strong></a>(self, top_level_dir, benchmark_dirs<font color="#909090">=None</font>, benchmark_aliases<font color="#909090">=None</font>, client_config<font color="#909090">=None</font>, default_chrome_root<font color="#909090">=None</font>)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>benchmark_aliases</strong></dt>
+</dl>
+<dl><dt><strong>benchmark_dirs</strong></dt>
+</dl>
+<dl><dt><strong>client_config</strong></dt>
+</dl>
+<dl><dt><strong>default_chrome_root</strong></dt>
+</dl>
+<dl><dt><strong>top_level_dir</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.record_wpr.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.record_wpr.html
new file mode 100644
index 0000000..a762abd
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.record_wpr.html
@@ -0,0 +1,173 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.record_wpr</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.record_wpr</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/record_wpr.py">telemetry/record_wpr.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2012&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="argparse.html">argparse</a><br>
+<a href="telemetry.benchmark.html">telemetry.benchmark</a><br>
+<a href="telemetry.internal.util.binary_manager.html">telemetry.internal.util.binary_manager</a><br>
+<a href="telemetry.internal.browser.browser_options.html">telemetry.internal.browser.browser_options</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.util.command_line.html">telemetry.internal.util.command_line</a><br>
+<a href="telemetry.core.discover.html">telemetry.core.discover</a><br>
+<a href="logging.html">logging</a><br>
+<a href="telemetry.page.page_test.html">telemetry.page.page_test</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.results.results_options.html">telemetry.internal.results.results_options</a><br>
+<a href="telemetry.story.html">telemetry.story</a><br>
+<a href="telemetry.internal.story_runner.html">telemetry.internal.story_runner</a><br>
+<a href="sys.html">sys</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.util.html">telemetry.core.util</a><br>
+<a href="telemetry.util.wpr_modes.html">telemetry.util.wpr_modes</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.record_wpr.html#WprRecorder">WprRecorder</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="telemetry.page.page_test.html#PageTest">telemetry.page.page_test.PageTest</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.record_wpr.html#RecorderPageTest">RecorderPageTest</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="RecorderPageTest">class <strong>RecorderPageTest</strong></a>(<a href="telemetry.page.page_test.html#PageTest">telemetry.page.page_test.PageTest</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.record_wpr.html#RecorderPageTest">RecorderPageTest</a></dd>
+<dd><a href="telemetry.page.page_test.html#PageTest">telemetry.page.page_test.PageTest</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="RecorderPageTest-CleanUpAfterPage"><strong>CleanUpAfterPage</strong></a>(self, page, tab)</dt></dl>
+
+<dl><dt><a name="RecorderPageTest-CustomizeBrowserOptions"><strong>CustomizeBrowserOptions</strong></a>(self, options)</dt></dl>
+
+<dl><dt><a name="RecorderPageTest-DidNavigateToPage"><strong>DidNavigateToPage</strong></a>(self, page, tab)</dt></dl>
+
+<dl><dt><a name="RecorderPageTest-DidStartBrowser"><strong>DidStartBrowser</strong></a>(self, browser)</dt></dl>
+
+<dl><dt><a name="RecorderPageTest-RunNavigateSteps"><strong>RunNavigateSteps</strong></a>(self, page, tab)</dt></dl>
+
+<dl><dt><a name="RecorderPageTest-ValidateAndMeasurePage"><strong>ValidateAndMeasurePage</strong></a>(self, page, tab, results)</dt></dl>
+
+<dl><dt><a name="RecorderPageTest-WillNavigateToPage"><strong>WillNavigateToPage</strong></a>(self, page, tab)</dt><dd><tt>Override&nbsp;to&nbsp;ensure&nbsp;all&nbsp;resources&nbsp;are&nbsp;fetched&nbsp;from&nbsp;network.</tt></dd></dl>
+
+<dl><dt><a name="RecorderPageTest-WillStartBrowser"><strong>WillStartBrowser</strong></a>(self, browser)</dt></dl>
+
+<dl><dt><a name="RecorderPageTest-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.page.page_test.html#PageTest">telemetry.page.page_test.PageTest</a>:<br>
+<dl><dt><a name="RecorderPageTest-DidRunPage"><strong>DidRunPage</strong></a>(self, platform)</dt><dd><tt>Called&nbsp;after&nbsp;the&nbsp;test&nbsp;run&nbsp;method&nbsp;was&nbsp;run,&nbsp;even&nbsp;if&nbsp;it&nbsp;failed.</tt></dd></dl>
+
+<dl><dt><a name="RecorderPageTest-RestartBrowserBeforeEachPage"><strong>RestartBrowserBeforeEachPage</strong></a>(self)</dt><dd><tt>Should&nbsp;the&nbsp;browser&nbsp;be&nbsp;restarted&nbsp;for&nbsp;the&nbsp;page?<br>
+&nbsp;<br>
+This&nbsp;returns&nbsp;true&nbsp;if&nbsp;the&nbsp;test&nbsp;needs&nbsp;to&nbsp;unconditionally&nbsp;restart&nbsp;the<br>
+browser&nbsp;for&nbsp;each&nbsp;page.&nbsp;It&nbsp;may&nbsp;be&nbsp;called&nbsp;before&nbsp;the&nbsp;browser&nbsp;is&nbsp;started.</tt></dd></dl>
+
+<dl><dt><a name="RecorderPageTest-SetOptions"><strong>SetOptions</strong></a>(self, options)</dt><dd><tt>Sets&nbsp;the&nbsp;BrowserFinderOptions&nbsp;instance&nbsp;to&nbsp;use.</tt></dd></dl>
+
+<dl><dt><a name="RecorderPageTest-StopBrowserAfterPage"><strong>StopBrowserAfterPage</strong></a>(self, browser, page)</dt><dd><tt>Should&nbsp;the&nbsp;browser&nbsp;be&nbsp;stopped&nbsp;after&nbsp;the&nbsp;page&nbsp;is&nbsp;run?<br>
+&nbsp;<br>
+This&nbsp;is&nbsp;called&nbsp;after&nbsp;a&nbsp;page&nbsp;is&nbsp;run&nbsp;to&nbsp;decide&nbsp;whether&nbsp;the&nbsp;browser&nbsp;needs&nbsp;to<br>
+be&nbsp;stopped&nbsp;to&nbsp;clean&nbsp;up&nbsp;its&nbsp;state.&nbsp;If&nbsp;it&nbsp;is&nbsp;stopped,&nbsp;then&nbsp;it&nbsp;will&nbsp;be<br>
+restarted&nbsp;to&nbsp;run&nbsp;the&nbsp;next&nbsp;page.<br>
+&nbsp;<br>
+A&nbsp;test&nbsp;that&nbsp;overrides&nbsp;this&nbsp;can&nbsp;look&nbsp;at&nbsp;both&nbsp;the&nbsp;page&nbsp;and&nbsp;the&nbsp;browser&nbsp;to<br>
+decide&nbsp;whether&nbsp;it&nbsp;needs&nbsp;to&nbsp;stop&nbsp;the&nbsp;browser.</tt></dd></dl>
+
+<dl><dt><a name="RecorderPageTest-TabForPage"><strong>TabForPage</strong></a>(self, page, browser)</dt><dd><tt>Override&nbsp;to&nbsp;select&nbsp;a&nbsp;different&nbsp;tab&nbsp;for&nbsp;the&nbsp;page.&nbsp;&nbsp;For&nbsp;instance,&nbsp;to<br>
+create&nbsp;a&nbsp;new&nbsp;tab&nbsp;for&nbsp;every&nbsp;page,&nbsp;return&nbsp;browser.tabs.New().</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.page.page_test.html#PageTest">telemetry.page.page_test.PageTest</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>clear_cache_before_each_run</strong></dt>
+<dd><tt>When&nbsp;set&nbsp;to&nbsp;True,&nbsp;the&nbsp;browser's&nbsp;disk&nbsp;and&nbsp;memory&nbsp;cache&nbsp;will&nbsp;be&nbsp;cleared<br>
+before&nbsp;each&nbsp;run.</tt></dd>
+</dl>
+<dl><dt><strong>close_tabs_before_run</strong></dt>
+<dd><tt>When&nbsp;set&nbsp;to&nbsp;True,&nbsp;all&nbsp;tabs&nbsp;are&nbsp;closed&nbsp;before&nbsp;running&nbsp;the&nbsp;test&nbsp;for&nbsp;the<br>
+first&nbsp;time.</tt></dd>
+</dl>
+<dl><dt><strong>is_multi_tab_test</strong></dt>
+<dd><tt>Returns&nbsp;True&nbsp;if&nbsp;the&nbsp;test&nbsp;opens&nbsp;multiple&nbsp;tabs.<br>
+&nbsp;<br>
+If&nbsp;the&nbsp;test&nbsp;overrides&nbsp;TabForPage,&nbsp;it&nbsp;is&nbsp;deemed&nbsp;a&nbsp;multi-tab&nbsp;test.<br>
+Multi-tab&nbsp;tests&nbsp;do&nbsp;not&nbsp;retry&nbsp;after&nbsp;tab&nbsp;or&nbsp;browser&nbsp;crashes,&nbsp;whereas,<br>
+single-tab&nbsp;tests&nbsp;too.&nbsp;That&nbsp;is&nbsp;because&nbsp;the&nbsp;state&nbsp;of&nbsp;multi-tab&nbsp;tests<br>
+(e.g.,&nbsp;how&nbsp;many&nbsp;tabs&nbsp;are&nbsp;open,&nbsp;etc.)&nbsp;is&nbsp;unknown&nbsp;after&nbsp;crashes.</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="WprRecorder">class <strong>WprRecorder</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="WprRecorder-CreateResults"><strong>CreateResults</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="WprRecorder-HandleResults"><strong>HandleResults</strong></a>(self, results, upload_to_cloud_storage)</dt></dl>
+
+<dl><dt><a name="WprRecorder-Record"><strong>Record</strong></a>(self, results)</dt></dl>
+
+<dl><dt><a name="WprRecorder-__init__"><strong>__init__</strong></a>(self, base_dir, target, args<font color="#909090">=None</font>)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>options</strong></dt>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-Main"><strong>Main</strong></a>(base_dir)</dt><dd><tt>#&nbsp;TODO(nednguyen):&nbsp;use&nbsp;benchmark.Environment&nbsp;instead&nbsp;of&nbsp;base_dir&nbsp;for&nbsp;discovering<br>
+#&nbsp;benchmark&nbsp;&amp;&nbsp;story&nbsp;classes.</tt></dd></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.story.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.story.html
new file mode 100644
index 0000000..af7ef2b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.story.html
@@ -0,0 +1,40 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: package telemetry.story</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.story</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/story/__init__.py">telemetry/story/__init__.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2015&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Package Contents</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.story.shared_state.html">shared_state</a><br>
+<a href="telemetry.story.story.html">story</a><br>
+</td><td width="25%" valign=top><a href="telemetry.story.story_filter.html">story_filter</a><br>
+<a href="telemetry.story.story_filter_unittest.html">story_filter_unittest</a><br>
+</td><td width="25%" valign=top><a href="telemetry.story.story_set.html">story_set</a><br>
+<a href="telemetry.story.story_set_unittest.html">story_set_unittest</a><br>
+</td><td width="25%" valign=top><a href="telemetry.story.story_unittest.html">story_unittest</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>INTERNAL_BUCKET</strong> = 'chrome-telemetry'<br>
+<strong>PARTNER_BUCKET</strong> = 'chrome-partner-telemetry'<br>
+<strong>PUBLIC_BUCKET</strong> = 'chromium-telemetry'</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.story.shared_state.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.story.shared_state.html
new file mode 100644
index 0000000..b078cb0
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.story.shared_state.html
@@ -0,0 +1,88 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.story.shared_state</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.story.html"><font color="#ffffff">story</font></a>.shared_state</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/story/shared_state.py">telemetry/story/shared_state.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.story.shared_state.html#SharedState">SharedState</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="SharedState">class <strong>SharedState</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;class&nbsp;that&nbsp;manages&nbsp;the&nbsp;test&nbsp;state&nbsp;across&nbsp;multiple&nbsp;stories.<br>
+It's&nbsp;styled&nbsp;on&nbsp;unittest.TestCase&nbsp;for&nbsp;handling&nbsp;test&nbsp;setup&nbsp;&amp;&nbsp;teardown&nbsp;logic.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="SharedState-CanRunStory"><strong>CanRunStory</strong></a>(self, story)</dt><dd><tt>Indicate&nbsp;whether&nbsp;the&nbsp;story&nbsp;can&nbsp;be&nbsp;run&nbsp;in&nbsp;the&nbsp;current&nbsp;configuration.<br>
+This&nbsp;is&nbsp;called&nbsp;after&nbsp;WillRunStory&nbsp;and&nbsp;before&nbsp;RunStory.&nbsp;Return&nbsp;True<br>
+if&nbsp;the&nbsp;story&nbsp;should&nbsp;be&nbsp;run,&nbsp;and&nbsp;False&nbsp;if&nbsp;it&nbsp;should&nbsp;be&nbsp;skipped.<br>
+Most&nbsp;subclasses&nbsp;will&nbsp;probably&nbsp;want&nbsp;to&nbsp;override&nbsp;this&nbsp;to&nbsp;always<br>
+return&nbsp;True.<br>
+Args:<br>
+&nbsp;&nbsp;story:&nbsp;a&nbsp;story.Story&nbsp;instance.</tt></dd></dl>
+
+<dl><dt><a name="SharedState-DidRunStory"><strong>DidRunStory</strong></a>(self, results)</dt><dd><tt>Override&nbsp;to&nbsp;do&nbsp;any&nbsp;action&nbsp;after&nbsp;running&nbsp;each&nbsp;of&nbsp;all&nbsp;stories&nbsp;that<br>
+share&nbsp;this&nbsp;same&nbsp;state.<br>
+This&nbsp;method&nbsp;is&nbsp;styled&nbsp;on&nbsp;unittest.TestCase.tearDown.</tt></dd></dl>
+
+<dl><dt><a name="SharedState-RunStory"><strong>RunStory</strong></a>(self, results)</dt><dd><tt>Override&nbsp;to&nbsp;do&nbsp;any&nbsp;action&nbsp;before&nbsp;running&nbsp;each&nbsp;one&nbsp;of&nbsp;all&nbsp;stories<br>
+that&nbsp;share&nbsp;this&nbsp;same&nbsp;state.<br>
+This&nbsp;method&nbsp;is&nbsp;styled&nbsp;on&nbsp;unittest.TestCase.run.</tt></dd></dl>
+
+<dl><dt><a name="SharedState-TearDownState"><strong>TearDownState</strong></a>(self)</dt><dd><tt>Override&nbsp;to&nbsp;do&nbsp;any&nbsp;action&nbsp;after&nbsp;running&nbsp;multiple&nbsp;stories&nbsp;that<br>
+share&nbsp;this&nbsp;same&nbsp;state.<br>
+This&nbsp;method&nbsp;is&nbsp;styled&nbsp;on&nbsp;unittest.TestCase.tearDownClass.</tt></dd></dl>
+
+<dl><dt><a name="SharedState-WillRunStory"><strong>WillRunStory</strong></a>(self, story)</dt><dd><tt>Override&nbsp;to&nbsp;do&nbsp;any&nbsp;action&nbsp;before&nbsp;running&nbsp;each&nbsp;one&nbsp;of&nbsp;all&nbsp;stories<br>
+that&nbsp;share&nbsp;this&nbsp;same&nbsp;state.<br>
+This&nbsp;method&nbsp;is&nbsp;styled&nbsp;on&nbsp;unittest.TestCase.setUp.</tt></dd></dl>
+
+<dl><dt><a name="SharedState-__init__"><strong>__init__</strong></a>(self, test, options, story_set)</dt><dd><tt>This&nbsp;method&nbsp;is&nbsp;styled&nbsp;on&nbsp;unittest.TestCase.setUpClass.<br>
+Override&nbsp;to&nbsp;do&nbsp;any&nbsp;action&nbsp;before&nbsp;running&nbsp;stories&nbsp;that<br>
+share&nbsp;this&nbsp;same&nbsp;state.<br>
+Args:<br>
+&nbsp;&nbsp;test:&nbsp;a&nbsp;page_test.PageTest&nbsp;or&nbsp;story_test.StoryTest&nbsp;instance.<br>
+&nbsp;&nbsp;options:&nbsp;a&nbsp;BrowserFinderOptions&nbsp;instance&nbsp;that&nbsp;contains&nbsp;command&nbsp;line<br>
+&nbsp;&nbsp;&nbsp;&nbsp;options.<br>
+&nbsp;&nbsp;story_set:&nbsp;a&nbsp;story.StorySet&nbsp;instance.</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>platform</strong></dt>
+<dd><tt>Override&nbsp;to&nbsp;return&nbsp;the&nbsp;platform&nbsp;which&nbsp;stories&nbsp;that&nbsp;share&nbsp;this&nbsp;same<br>
+state&nbsp;will&nbsp;be&nbsp;run&nbsp;on.</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.story.story.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.story.story.html
new file mode 100644
index 0000000..de69c84
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.story.story.html
@@ -0,0 +1,111 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.story.story</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.story.html"><font color="#ffffff">story</font></a>.story</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/story/story.py">telemetry/story/story.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2015&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="re.html">re</a><br>
+</td><td width="25%" valign=top><a href="telemetry.story.shared_state.html">telemetry.story.shared_state</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.story.story.html#Story">Story</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="Story">class <strong>Story</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;class&nbsp;styled&nbsp;on&nbsp;unittest.TestCase&nbsp;for&nbsp;creating&nbsp;story&nbsp;tests.<br>
+&nbsp;<br>
+Tests&nbsp;should&nbsp;override&nbsp;Run&nbsp;to&nbsp;maybe&nbsp;start&nbsp;the&nbsp;application&nbsp;and&nbsp;perform&nbsp;actions<br>
+on&nbsp;it.&nbsp;To&nbsp;share&nbsp;state&nbsp;between&nbsp;different&nbsp;tests,&nbsp;one&nbsp;can&nbsp;define&nbsp;a<br>
+shared_state&nbsp;which&nbsp;contains&nbsp;hooks&nbsp;that&nbsp;will&nbsp;be&nbsp;called&nbsp;before&nbsp;and<br>
+after&nbsp;mutiple&nbsp;stories&nbsp;run&nbsp;and&nbsp;in&nbsp;between&nbsp;runs.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;shared_state_class:&nbsp;subclass&nbsp;of&nbsp;telemetry.story.shared_state.SharedState.<br>
+&nbsp;&nbsp;name:&nbsp;string&nbsp;name&nbsp;of&nbsp;this&nbsp;story&nbsp;that&nbsp;can&nbsp;be&nbsp;used&nbsp;for&nbsp;identifying&nbsp;this&nbsp;story<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;in&nbsp;results&nbsp;output.<br>
+&nbsp;&nbsp;labels:&nbsp;A&nbsp;list&nbsp;or&nbsp;set&nbsp;of&nbsp;string&nbsp;labels&nbsp;that&nbsp;are&nbsp;used&nbsp;for&nbsp;filtering.&nbsp;See<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;story.story_filter&nbsp;for&nbsp;more&nbsp;information.<br>
+&nbsp;&nbsp;is_local:&nbsp;If&nbsp;True,&nbsp;the&nbsp;story&nbsp;does&nbsp;not&nbsp;require&nbsp;network.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="Story-AsDict"><strong>AsDict</strong></a>(self)</dt><dd><tt>Converts&nbsp;a&nbsp;story&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;to&nbsp;a&nbsp;dict&nbsp;suitable&nbsp;for&nbsp;JSON&nbsp;output.</tt></dd></dl>
+
+<dl><dt><a name="Story-Run"><strong>Run</strong></a>(self, shared_state)</dt><dd><tt>Execute&nbsp;the&nbsp;interactions&nbsp;with&nbsp;the&nbsp;applications&nbsp;and/or&nbsp;platforms.</tt></dd></dl>
+
+<dl><dt><a name="Story-__init__"><strong>__init__</strong></a>(self, shared_state_class, name<font color="#909090">=''</font>, labels<font color="#909090">=None</font>, is_local<font color="#909090">=False</font>, make_javascript_deterministic<font color="#909090">=True</font>)</dt><dd><tt>Args:<br>
+&nbsp;&nbsp;make_javascript_deterministic:&nbsp;Whether&nbsp;JavaScript&nbsp;performed&nbsp;on<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;the&nbsp;page&nbsp;is&nbsp;made&nbsp;deterministic&nbsp;across&nbsp;multiple&nbsp;runs.&nbsp;This<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;requires&nbsp;that&nbsp;the&nbsp;web&nbsp;content&nbsp;is&nbsp;served&nbsp;via&nbsp;Web&nbsp;Page&nbsp;Replay<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;to&nbsp;take&nbsp;effect.&nbsp;This&nbsp;setting&nbsp;does&nbsp;not&nbsp;affect&nbsp;stories&nbsp;containing&nbsp;no&nbsp;web<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;content&nbsp;or&nbsp;where&nbsp;the&nbsp;HTTP&nbsp;MIME&nbsp;type&nbsp;is&nbsp;not&nbsp;text/html.See&nbsp;also:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_InjectScripts&nbsp;method&nbsp;in&nbsp;third_party/webpagereplay/httpclient.py.</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>display_name</strong></dt>
+</dl>
+<dl><dt><strong>file_safe_name</strong></dt>
+<dd><tt>A&nbsp;version&nbsp;of&nbsp;display_name&nbsp;that's&nbsp;safe&nbsp;to&nbsp;use&nbsp;as&nbsp;a&nbsp;filename.<br>
+&nbsp;<br>
+The&nbsp;default&nbsp;implementation&nbsp;sanitizes&nbsp;special&nbsp;characters&nbsp;with&nbsp;underscores,<br>
+but&nbsp;it's&nbsp;okay&nbsp;to&nbsp;override&nbsp;it&nbsp;with&nbsp;a&nbsp;more&nbsp;specific&nbsp;implementation&nbsp;in<br>
+subclasses.</tt></dd>
+</dl>
+<dl><dt><strong>id</strong></dt>
+</dl>
+<dl><dt><strong>is_local</strong></dt>
+<dd><tt>Returns&nbsp;True&nbsp;iff&nbsp;this&nbsp;story&nbsp;does&nbsp;not&nbsp;require&nbsp;network.</tt></dd>
+</dl>
+<dl><dt><strong>labels</strong></dt>
+</dl>
+<dl><dt><strong>make_javascript_deterministic</strong></dt>
+</dl>
+<dl><dt><strong>name</strong></dt>
+</dl>
+<dl><dt><strong>serving_dir</strong></dt>
+<dd><tt>Returns&nbsp;the&nbsp;absolute&nbsp;path&nbsp;to&nbsp;a&nbsp;directory&nbsp;with&nbsp;hash&nbsp;files&nbsp;to&nbsp;data&nbsp;that<br>
+should&nbsp;be&nbsp;updated&nbsp;from&nbsp;cloud&nbsp;storage,&nbsp;or&nbsp;None&nbsp;if&nbsp;no&nbsp;files&nbsp;need&nbsp;to&nbsp;be<br>
+updated.</tt></dd>
+</dl>
+<dl><dt><strong>shared_state_class</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.story.story_filter.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.story.story_filter.html
new file mode 100644
index 0000000..6ed1bd2
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.story.story_filter.html
@@ -0,0 +1,72 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.story.story_filter</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.story.html"><font color="#ffffff">story</font></a>.story_filter</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/story/story_filter.py">telemetry/story/story_filter.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.util.command_line.html">telemetry.internal.util.command_line</a><br>
+</td><td width="25%" valign=top><a href="optparse.html">optparse</a><br>
+</td><td width="25%" valign=top><a href="re.html">re</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.util.command_line.html#ArgumentHandlerMixIn">telemetry.internal.util.command_line.ArgumentHandlerMixIn</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.story.story_filter.html#StoryFilter">StoryFilter</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="StoryFilter">class <strong>StoryFilter</strong></a>(<a href="telemetry.internal.util.command_line.html#ArgumentHandlerMixIn">telemetry.internal.util.command_line.ArgumentHandlerMixIn</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Filters&nbsp;stories&nbsp;in&nbsp;the&nbsp;story&nbsp;set&nbsp;based&nbsp;on&nbsp;command-line&nbsp;flags.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.story.story_filter.html#StoryFilter">StoryFilter</a></dd>
+<dd><a href="telemetry.internal.util.command_line.html#ArgumentHandlerMixIn">telemetry.internal.util.command_line.ArgumentHandlerMixIn</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="StoryFilter-AddCommandLineArgs"><strong>AddCommandLineArgs</strong></a>(cls, parser)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="StoryFilter-IsSelected"><strong>IsSelected</strong></a>(cls, story)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="StoryFilter-ProcessCommandLineArgs"><strong>ProcessCommandLineArgs</strong></a>(cls, parser, args)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.util.command_line.html#ArgumentHandlerMixIn">telemetry.internal.util.command_line.ArgumentHandlerMixIn</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.story.story_set.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.story.story_set.html
new file mode 100644
index 0000000..73e8ec6
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.story.story_set.html
@@ -0,0 +1,139 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.story.story_set</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.story.html"><font color="#ffffff">story</font></a>.story_set</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/story/story_set.py">telemetry/story/story_set.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.wpr.archive_info.html">telemetry.wpr.archive_info</a><br>
+</td><td width="25%" valign=top><a href="inspect.html">inspect</a><br>
+</td><td width="25%" valign=top><a href="os.html">os</a><br>
+</td><td width="25%" valign=top><a href="telemetry.story.story.html">telemetry.story.story</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.story.story_set.html#StorySet">StorySet</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="StorySet">class <strong>StorySet</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;collection&nbsp;of&nbsp;stories.<br>
+&nbsp;<br>
+A&nbsp;typical&nbsp;usage&nbsp;of&nbsp;<a href="#StorySet">StorySet</a>&nbsp;would&nbsp;be&nbsp;to&nbsp;subclass&nbsp;it&nbsp;and&nbsp;then&nbsp;call<br>
+AddStory&nbsp;for&nbsp;each&nbsp;Story.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="StorySet-AddStory"><strong>AddStory</strong></a>(self, story)</dt></dl>
+
+<dl><dt><a name="StorySet-RemoveStory"><strong>RemoveStory</strong></a>(self, story)</dt><dd><tt>Removes&nbsp;a&nbsp;Story.<br>
+&nbsp;<br>
+Allows&nbsp;the&nbsp;stories&nbsp;to&nbsp;be&nbsp;filtered.</tt></dd></dl>
+
+<dl><dt><a name="StorySet-WprFilePathForStory"><strong>WprFilePathForStory</strong></a>(self, story)</dt><dd><tt>Convenient&nbsp;function&nbsp;to&nbsp;retrieve&nbsp;WPR&nbsp;archive&nbsp;file&nbsp;path.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;story:&nbsp;The&nbsp;Story&nbsp;to&nbsp;look&nbsp;up.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;The&nbsp;WPR&nbsp;archive&nbsp;file&nbsp;path&nbsp;for&nbsp;the&nbsp;given&nbsp;Story,&nbsp;if&nbsp;found.<br>
+&nbsp;&nbsp;Otherwise,&nbsp;None.</tt></dd></dl>
+
+<dl><dt><a name="StorySet-__getitem__"><strong>__getitem__</strong></a>(self, key)</dt></dl>
+
+<dl><dt><a name="StorySet-__init__"><strong>__init__</strong></a>(self, archive_data_file<font color="#909090">=''</font>, cloud_storage_bucket<font color="#909090">=None</font>, base_dir<font color="#909090">=None</font>, serving_dirs<font color="#909090">=None</font>)</dt><dd><tt>Creates&nbsp;a&nbsp;new&nbsp;<a href="#StorySet">StorySet</a>.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;archive_data_file:&nbsp;The&nbsp;path&nbsp;to&nbsp;Web&nbsp;Page&nbsp;Replay's&nbsp;archive&nbsp;data,&nbsp;relative<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;to&nbsp;self.<strong>base_dir</strong>.<br>
+&nbsp;&nbsp;cloud_storage_bucket:&nbsp;The&nbsp;cloud&nbsp;storage&nbsp;bucket&nbsp;used&nbsp;to&nbsp;download<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Web&nbsp;Page&nbsp;Replay's&nbsp;archive&nbsp;data.&nbsp;Valid&nbsp;values&nbsp;are:&nbsp;None,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;story.PUBLIC_BUCKET,&nbsp;story.PARTNER_BUCKET,&nbsp;or&nbsp;story.INTERNAL_BUCKET<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(defined&nbsp;in&nbsp;telemetry.util.cloud_storage).<br>
+&nbsp;&nbsp;serving_dirs:&nbsp;A&nbsp;set&nbsp;of&nbsp;paths,&nbsp;relative&nbsp;to&nbsp;self.<strong>base_dir</strong>,&nbsp;to&nbsp;directories<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;containing&nbsp;hash&nbsp;files&nbsp;for&nbsp;non-wpr&nbsp;archive&nbsp;data&nbsp;stored&nbsp;in&nbsp;cloud<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;storage.</tt></dd></dl>
+
+<dl><dt><a name="StorySet-__iter__"><strong>__iter__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="StorySet-__len__"><strong>__len__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="StorySet-__setitem__"><strong>__setitem__</strong></a>(self, key, value)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="StorySet-Description"><strong>Description</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Return&nbsp;a&nbsp;string&nbsp;explaining&nbsp;in&nbsp;human-understandable&nbsp;terms&nbsp;what&nbsp;this<br>
+story&nbsp;represents.<br>
+Note&nbsp;that&nbsp;this&nbsp;should&nbsp;be&nbsp;a&nbsp;classmethod&nbsp;so&nbsp;the&nbsp;benchmark_runner&nbsp;script&nbsp;can<br>
+display&nbsp;stories'&nbsp;names&nbsp;along&nbsp;with&nbsp;their&nbsp;descriptions&nbsp;in&nbsp;the&nbsp;list&nbsp;command.</tt></dd></dl>
+
+<dl><dt><a name="StorySet-Name"><strong>Name</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Returns&nbsp;the&nbsp;string&nbsp;name&nbsp;of&nbsp;this&nbsp;<a href="#StorySet">StorySet</a>.<br>
+Note&nbsp;that&nbsp;this&nbsp;should&nbsp;be&nbsp;a&nbsp;classmethod&nbsp;so&nbsp;the&nbsp;benchmark_runner&nbsp;script&nbsp;can<br>
+match&nbsp;the&nbsp;story&nbsp;class&nbsp;with&nbsp;its&nbsp;name&nbsp;specified&nbsp;in&nbsp;the&nbsp;run&nbsp;command:<br>
+'Run&nbsp;&lt;User&nbsp;story&nbsp;test&nbsp;name&gt;&nbsp;&lt;User&nbsp;story&nbsp;class&nbsp;name&gt;'</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>allow_mixed_story_states</strong></dt>
+<dd><tt>True&nbsp;iff&nbsp;Stories&nbsp;are&nbsp;allowed&nbsp;to&nbsp;have&nbsp;different&nbsp;StoryState&nbsp;classes.<br>
+&nbsp;<br>
+There&nbsp;are&nbsp;no&nbsp;checks&nbsp;in&nbsp;place&nbsp;for&nbsp;determining&nbsp;if&nbsp;SharedStates&nbsp;are<br>
+being&nbsp;assigned&nbsp;correctly&nbsp;to&nbsp;all&nbsp;Stories&nbsp;in&nbsp;a&nbsp;given&nbsp;StorySet.&nbsp;The<br>
+majority&nbsp;of&nbsp;test&nbsp;cases&nbsp;should&nbsp;not&nbsp;need&nbsp;the&nbsp;ability&nbsp;to&nbsp;have&nbsp;multiple<br>
+SharedStates,&nbsp;which&nbsp;usually&nbsp;implies&nbsp;you&nbsp;should&nbsp;be&nbsp;writing&nbsp;multiple<br>
+benchmarks&nbsp;instead.&nbsp;We&nbsp;provide&nbsp;errors&nbsp;to&nbsp;avoid&nbsp;accidentally&nbsp;assigning<br>
+or&nbsp;defaulting&nbsp;to&nbsp;the&nbsp;wrong&nbsp;SharedState.<br>
+Override&nbsp;at&nbsp;your&nbsp;own&nbsp;risk.&nbsp;Here&nbsp;be&nbsp;dragons.</tt></dd>
+</dl>
+<dl><dt><strong>archive_data_file</strong></dt>
+</dl>
+<dl><dt><strong>base_dir</strong></dt>
+<dd><tt>The&nbsp;base&nbsp;directory&nbsp;to&nbsp;resolve&nbsp;archive_data_file.<br>
+&nbsp;<br>
+This&nbsp;defaults&nbsp;to&nbsp;the&nbsp;directory&nbsp;containing&nbsp;the&nbsp;StorySet&nbsp;instance's&nbsp;class.</tt></dd>
+</dl>
+<dl><dt><strong>bucket</strong></dt>
+</dl>
+<dl><dt><strong>file_path</strong></dt>
+</dl>
+<dl><dt><strong>serving_dirs</strong></dt>
+</dl>
+<dl><dt><strong>wpr_archive_info</strong></dt>
+<dd><tt>Lazily&nbsp;constructs&nbsp;wpr_archive_info&nbsp;if&nbsp;it's&nbsp;not&nbsp;set&nbsp;and&nbsp;returns&nbsp;it.</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.browser_test_case.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.browser_test_case.html
new file mode 100644
index 0000000..05e5d4f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.browser_test_case.html
@@ -0,0 +1,355 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.testing.browser_test_case</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.testing.html"><font color="#ffffff">testing</font></a>.browser_test_case</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/testing/browser_test_case.py">telemetry/testing/browser_test_case.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.browser.browser_finder.html">telemetry.internal.browser.browser_finder</a><br>
+<a href="telemetry.testing.options_for_unittests.html">telemetry.testing.options_for_unittests</a><br>
+</td><td width="25%" valign=top><a href="os.html">os</a><br>
+<a href="telemetry.internal.util.path.html">telemetry.internal.util.path</a><br>
+</td><td width="25%" valign=top><a href="unittest.html">unittest</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="unittest.case.html#TestCase">unittest.case.TestCase</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.testing.browser_test_case.html#BrowserTestCase">BrowserTestCase</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="BrowserTestCase">class <strong>BrowserTestCase</strong></a>(<a href="unittest.case.html#TestCase">unittest.case.TestCase</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.testing.browser_test_case.html#BrowserTestCase">BrowserTestCase</a></dd>
+<dd><a href="unittest.case.html#TestCase">unittest.case.TestCase</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="BrowserTestCase-CustomizeBrowserOptions"><strong>CustomizeBrowserOptions</strong></a>(cls, options)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Override&nbsp;to&nbsp;add&nbsp;test-specific&nbsp;options&nbsp;to&nbsp;the&nbsp;BrowserOptions&nbsp;object</tt></dd></dl>
+
+<dl><dt><a name="BrowserTestCase-UrlOfUnittestFile"><strong>UrlOfUnittestFile</strong></a>(cls, filename)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="BrowserTestCase-setUpClass"><strong>setUpClass</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="BrowserTestCase-tearDownClass"><strong>tearDownClass</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Methods inherited from <a href="unittest.case.html#TestCase">unittest.case.TestCase</a>:<br>
+<dl><dt><a name="BrowserTestCase-__call__"><strong>__call__</strong></a>(self, *args, **kwds)</dt></dl>
+
+<dl><dt><a name="BrowserTestCase-__eq__"><strong>__eq__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="BrowserTestCase-__hash__"><strong>__hash__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="BrowserTestCase-__init__"><strong>__init__</strong></a>(self, methodName<font color="#909090">='runTest'</font>)</dt><dd><tt>Create&nbsp;an&nbsp;instance&nbsp;of&nbsp;the&nbsp;class&nbsp;that&nbsp;will&nbsp;use&nbsp;the&nbsp;named&nbsp;test<br>
+method&nbsp;when&nbsp;executed.&nbsp;Raises&nbsp;a&nbsp;ValueError&nbsp;if&nbsp;the&nbsp;instance&nbsp;does<br>
+not&nbsp;have&nbsp;a&nbsp;method&nbsp;with&nbsp;the&nbsp;specified&nbsp;name.</tt></dd></dl>
+
+<dl><dt><a name="BrowserTestCase-__ne__"><strong>__ne__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="BrowserTestCase-__repr__"><strong>__repr__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="BrowserTestCase-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="BrowserTestCase-addCleanup"><strong>addCleanup</strong></a>(self, function, *args, **kwargs)</dt><dd><tt>Add&nbsp;a&nbsp;function,&nbsp;with&nbsp;arguments,&nbsp;to&nbsp;be&nbsp;called&nbsp;when&nbsp;the&nbsp;test&nbsp;is<br>
+completed.&nbsp;Functions&nbsp;added&nbsp;are&nbsp;called&nbsp;on&nbsp;a&nbsp;LIFO&nbsp;basis&nbsp;and&nbsp;are<br>
+called&nbsp;after&nbsp;tearDown&nbsp;on&nbsp;test&nbsp;failure&nbsp;or&nbsp;success.<br>
+&nbsp;<br>
+Cleanup&nbsp;items&nbsp;are&nbsp;called&nbsp;even&nbsp;if&nbsp;setUp&nbsp;fails&nbsp;(unlike&nbsp;tearDown).</tt></dd></dl>
+
+<dl><dt><a name="BrowserTestCase-addTypeEqualityFunc"><strong>addTypeEqualityFunc</strong></a>(self, typeobj, function)</dt><dd><tt>Add&nbsp;a&nbsp;type&nbsp;specific&nbsp;assertEqual&nbsp;style&nbsp;function&nbsp;to&nbsp;compare&nbsp;a&nbsp;type.<br>
+&nbsp;<br>
+This&nbsp;method&nbsp;is&nbsp;for&nbsp;use&nbsp;by&nbsp;<a href="unittest.case.html#TestCase">TestCase</a>&nbsp;subclasses&nbsp;that&nbsp;need&nbsp;to&nbsp;register<br>
+their&nbsp;own&nbsp;type&nbsp;equality&nbsp;functions&nbsp;to&nbsp;provide&nbsp;nicer&nbsp;error&nbsp;messages.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;typeobj:&nbsp;The&nbsp;data&nbsp;type&nbsp;to&nbsp;call&nbsp;this&nbsp;function&nbsp;on&nbsp;when&nbsp;both&nbsp;values<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;are&nbsp;of&nbsp;the&nbsp;same&nbsp;type&nbsp;in&nbsp;<a href="#BrowserTestCase-assertEqual">assertEqual</a>().<br>
+&nbsp;&nbsp;&nbsp;&nbsp;function:&nbsp;The&nbsp;callable&nbsp;taking&nbsp;two&nbsp;arguments&nbsp;and&nbsp;an&nbsp;optional<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;msg=&nbsp;argument&nbsp;that&nbsp;raises&nbsp;self.<strong>failureException</strong>&nbsp;with&nbsp;a<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;useful&nbsp;error&nbsp;message&nbsp;when&nbsp;the&nbsp;two&nbsp;arguments&nbsp;are&nbsp;not&nbsp;equal.</tt></dd></dl>
+
+<dl><dt><a name="BrowserTestCase-assertAlmostEqual"><strong>assertAlmostEqual</strong></a>(self, first, second, places<font color="#909090">=None</font>, msg<font color="#909090">=None</font>, delta<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;unequal&nbsp;as&nbsp;determined&nbsp;by&nbsp;their<br>
+difference&nbsp;rounded&nbsp;to&nbsp;the&nbsp;given&nbsp;number&nbsp;of&nbsp;decimal&nbsp;places<br>
+(default&nbsp;7)&nbsp;and&nbsp;comparing&nbsp;to&nbsp;zero,&nbsp;or&nbsp;by&nbsp;comparing&nbsp;that&nbsp;the<br>
+between&nbsp;the&nbsp;two&nbsp;objects&nbsp;is&nbsp;more&nbsp;than&nbsp;the&nbsp;given&nbsp;delta.<br>
+&nbsp;<br>
+Note&nbsp;that&nbsp;decimal&nbsp;places&nbsp;(from&nbsp;zero)&nbsp;are&nbsp;usually&nbsp;not&nbsp;the&nbsp;same<br>
+as&nbsp;significant&nbsp;digits&nbsp;(measured&nbsp;from&nbsp;the&nbsp;most&nbsp;signficant&nbsp;digit).<br>
+&nbsp;<br>
+If&nbsp;the&nbsp;two&nbsp;objects&nbsp;compare&nbsp;equal&nbsp;then&nbsp;they&nbsp;will&nbsp;automatically<br>
+compare&nbsp;almost&nbsp;equal.</tt></dd></dl>
+
+<dl><dt><a name="BrowserTestCase-assertAlmostEquals"><strong>assertAlmostEquals</strong></a> = assertAlmostEqual(self, first, second, places<font color="#909090">=None</font>, msg<font color="#909090">=None</font>, delta<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;unequal&nbsp;as&nbsp;determined&nbsp;by&nbsp;their<br>
+difference&nbsp;rounded&nbsp;to&nbsp;the&nbsp;given&nbsp;number&nbsp;of&nbsp;decimal&nbsp;places<br>
+(default&nbsp;7)&nbsp;and&nbsp;comparing&nbsp;to&nbsp;zero,&nbsp;or&nbsp;by&nbsp;comparing&nbsp;that&nbsp;the<br>
+between&nbsp;the&nbsp;two&nbsp;objects&nbsp;is&nbsp;more&nbsp;than&nbsp;the&nbsp;given&nbsp;delta.<br>
+&nbsp;<br>
+Note&nbsp;that&nbsp;decimal&nbsp;places&nbsp;(from&nbsp;zero)&nbsp;are&nbsp;usually&nbsp;not&nbsp;the&nbsp;same<br>
+as&nbsp;significant&nbsp;digits&nbsp;(measured&nbsp;from&nbsp;the&nbsp;most&nbsp;signficant&nbsp;digit).<br>
+&nbsp;<br>
+If&nbsp;the&nbsp;two&nbsp;objects&nbsp;compare&nbsp;equal&nbsp;then&nbsp;they&nbsp;will&nbsp;automatically<br>
+compare&nbsp;almost&nbsp;equal.</tt></dd></dl>
+
+<dl><dt><a name="BrowserTestCase-assertDictContainsSubset"><strong>assertDictContainsSubset</strong></a>(self, expected, actual, msg<font color="#909090">=None</font>)</dt><dd><tt>Checks&nbsp;whether&nbsp;actual&nbsp;is&nbsp;a&nbsp;superset&nbsp;of&nbsp;expected.</tt></dd></dl>
+
+<dl><dt><a name="BrowserTestCase-assertDictEqual"><strong>assertDictEqual</strong></a>(self, d1, d2, msg<font color="#909090">=None</font>)</dt></dl>
+
+<dl><dt><a name="BrowserTestCase-assertEqual"><strong>assertEqual</strong></a>(self, first, second, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;unequal&nbsp;as&nbsp;determined&nbsp;by&nbsp;the&nbsp;'=='<br>
+operator.</tt></dd></dl>
+
+<dl><dt><a name="BrowserTestCase-assertEquals"><strong>assertEquals</strong></a> = assertEqual(self, first, second, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;unequal&nbsp;as&nbsp;determined&nbsp;by&nbsp;the&nbsp;'=='<br>
+operator.</tt></dd></dl>
+
+<dl><dt><a name="BrowserTestCase-assertFalse"><strong>assertFalse</strong></a>(self, expr, msg<font color="#909090">=None</font>)</dt><dd><tt>Check&nbsp;that&nbsp;the&nbsp;expression&nbsp;is&nbsp;false.</tt></dd></dl>
+
+<dl><dt><a name="BrowserTestCase-assertGreater"><strong>assertGreater</strong></a>(self, a, b, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#BrowserTestCase-assertTrue">assertTrue</a>(a&nbsp;&gt;&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="BrowserTestCase-assertGreaterEqual"><strong>assertGreaterEqual</strong></a>(self, a, b, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#BrowserTestCase-assertTrue">assertTrue</a>(a&nbsp;&gt;=&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="BrowserTestCase-assertIn"><strong>assertIn</strong></a>(self, member, container, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#BrowserTestCase-assertTrue">assertTrue</a>(a&nbsp;in&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="BrowserTestCase-assertIs"><strong>assertIs</strong></a>(self, expr1, expr2, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#BrowserTestCase-assertTrue">assertTrue</a>(a&nbsp;is&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="BrowserTestCase-assertIsInstance"><strong>assertIsInstance</strong></a>(self, obj, cls, msg<font color="#909090">=None</font>)</dt><dd><tt>Same&nbsp;as&nbsp;<a href="#BrowserTestCase-assertTrue">assertTrue</a>(isinstance(obj,&nbsp;cls)),&nbsp;with&nbsp;a&nbsp;nicer<br>
+default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="BrowserTestCase-assertIsNone"><strong>assertIsNone</strong></a>(self, obj, msg<font color="#909090">=None</font>)</dt><dd><tt>Same&nbsp;as&nbsp;<a href="#BrowserTestCase-assertTrue">assertTrue</a>(obj&nbsp;is&nbsp;None),&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="BrowserTestCase-assertIsNot"><strong>assertIsNot</strong></a>(self, expr1, expr2, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#BrowserTestCase-assertTrue">assertTrue</a>(a&nbsp;is&nbsp;not&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="BrowserTestCase-assertIsNotNone"><strong>assertIsNotNone</strong></a>(self, obj, msg<font color="#909090">=None</font>)</dt><dd><tt>Included&nbsp;for&nbsp;symmetry&nbsp;with&nbsp;assertIsNone.</tt></dd></dl>
+
+<dl><dt><a name="BrowserTestCase-assertItemsEqual"><strong>assertItemsEqual</strong></a>(self, expected_seq, actual_seq, msg<font color="#909090">=None</font>)</dt><dd><tt>An&nbsp;unordered&nbsp;sequence&nbsp;specific&nbsp;comparison.&nbsp;It&nbsp;asserts&nbsp;that<br>
+actual_seq&nbsp;and&nbsp;expected_seq&nbsp;have&nbsp;the&nbsp;same&nbsp;element&nbsp;counts.<br>
+Equivalent&nbsp;to::<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#BrowserTestCase-assertEqual">assertEqual</a>(Counter(iter(actual_seq)),<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Counter(iter(expected_seq)))<br>
+&nbsp;<br>
+Asserts&nbsp;that&nbsp;each&nbsp;element&nbsp;has&nbsp;the&nbsp;same&nbsp;count&nbsp;in&nbsp;both&nbsp;sequences.<br>
+Example:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;[0,&nbsp;1,&nbsp;1]&nbsp;and&nbsp;[1,&nbsp;0,&nbsp;1]&nbsp;compare&nbsp;equal.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;[0,&nbsp;0,&nbsp;1]&nbsp;and&nbsp;[0,&nbsp;1]&nbsp;compare&nbsp;unequal.</tt></dd></dl>
+
+<dl><dt><a name="BrowserTestCase-assertLess"><strong>assertLess</strong></a>(self, a, b, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#BrowserTestCase-assertTrue">assertTrue</a>(a&nbsp;&lt;&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="BrowserTestCase-assertLessEqual"><strong>assertLessEqual</strong></a>(self, a, b, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#BrowserTestCase-assertTrue">assertTrue</a>(a&nbsp;&lt;=&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="BrowserTestCase-assertListEqual"><strong>assertListEqual</strong></a>(self, list1, list2, msg<font color="#909090">=None</font>)</dt><dd><tt>A&nbsp;list-specific&nbsp;equality&nbsp;assertion.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;list1:&nbsp;The&nbsp;first&nbsp;list&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;list2:&nbsp;The&nbsp;second&nbsp;list&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;msg:&nbsp;Optional&nbsp;message&nbsp;to&nbsp;use&nbsp;on&nbsp;failure&nbsp;instead&nbsp;of&nbsp;a&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;differences.</tt></dd></dl>
+
+<dl><dt><a name="BrowserTestCase-assertMultiLineEqual"><strong>assertMultiLineEqual</strong></a>(self, first, second, msg<font color="#909090">=None</font>)</dt><dd><tt>Assert&nbsp;that&nbsp;two&nbsp;multi-line&nbsp;strings&nbsp;are&nbsp;equal.</tt></dd></dl>
+
+<dl><dt><a name="BrowserTestCase-assertNotAlmostEqual"><strong>assertNotAlmostEqual</strong></a>(self, first, second, places<font color="#909090">=None</font>, msg<font color="#909090">=None</font>, delta<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;equal&nbsp;as&nbsp;determined&nbsp;by&nbsp;their<br>
+difference&nbsp;rounded&nbsp;to&nbsp;the&nbsp;given&nbsp;number&nbsp;of&nbsp;decimal&nbsp;places<br>
+(default&nbsp;7)&nbsp;and&nbsp;comparing&nbsp;to&nbsp;zero,&nbsp;or&nbsp;by&nbsp;comparing&nbsp;that&nbsp;the<br>
+between&nbsp;the&nbsp;two&nbsp;objects&nbsp;is&nbsp;less&nbsp;than&nbsp;the&nbsp;given&nbsp;delta.<br>
+&nbsp;<br>
+Note&nbsp;that&nbsp;decimal&nbsp;places&nbsp;(from&nbsp;zero)&nbsp;are&nbsp;usually&nbsp;not&nbsp;the&nbsp;same<br>
+as&nbsp;significant&nbsp;digits&nbsp;(measured&nbsp;from&nbsp;the&nbsp;most&nbsp;signficant&nbsp;digit).<br>
+&nbsp;<br>
+Objects&nbsp;that&nbsp;are&nbsp;equal&nbsp;automatically&nbsp;fail.</tt></dd></dl>
+
+<dl><dt><a name="BrowserTestCase-assertNotAlmostEquals"><strong>assertNotAlmostEquals</strong></a> = assertNotAlmostEqual(self, first, second, places<font color="#909090">=None</font>, msg<font color="#909090">=None</font>, delta<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;equal&nbsp;as&nbsp;determined&nbsp;by&nbsp;their<br>
+difference&nbsp;rounded&nbsp;to&nbsp;the&nbsp;given&nbsp;number&nbsp;of&nbsp;decimal&nbsp;places<br>
+(default&nbsp;7)&nbsp;and&nbsp;comparing&nbsp;to&nbsp;zero,&nbsp;or&nbsp;by&nbsp;comparing&nbsp;that&nbsp;the<br>
+between&nbsp;the&nbsp;two&nbsp;objects&nbsp;is&nbsp;less&nbsp;than&nbsp;the&nbsp;given&nbsp;delta.<br>
+&nbsp;<br>
+Note&nbsp;that&nbsp;decimal&nbsp;places&nbsp;(from&nbsp;zero)&nbsp;are&nbsp;usually&nbsp;not&nbsp;the&nbsp;same<br>
+as&nbsp;significant&nbsp;digits&nbsp;(measured&nbsp;from&nbsp;the&nbsp;most&nbsp;signficant&nbsp;digit).<br>
+&nbsp;<br>
+Objects&nbsp;that&nbsp;are&nbsp;equal&nbsp;automatically&nbsp;fail.</tt></dd></dl>
+
+<dl><dt><a name="BrowserTestCase-assertNotEqual"><strong>assertNotEqual</strong></a>(self, first, second, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;equal&nbsp;as&nbsp;determined&nbsp;by&nbsp;the&nbsp;'!='<br>
+operator.</tt></dd></dl>
+
+<dl><dt><a name="BrowserTestCase-assertNotEquals"><strong>assertNotEquals</strong></a> = assertNotEqual(self, first, second, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;equal&nbsp;as&nbsp;determined&nbsp;by&nbsp;the&nbsp;'!='<br>
+operator.</tt></dd></dl>
+
+<dl><dt><a name="BrowserTestCase-assertNotIn"><strong>assertNotIn</strong></a>(self, member, container, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#BrowserTestCase-assertTrue">assertTrue</a>(a&nbsp;not&nbsp;in&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="BrowserTestCase-assertNotIsInstance"><strong>assertNotIsInstance</strong></a>(self, obj, cls, msg<font color="#909090">=None</font>)</dt><dd><tt>Included&nbsp;for&nbsp;symmetry&nbsp;with&nbsp;assertIsInstance.</tt></dd></dl>
+
+<dl><dt><a name="BrowserTestCase-assertNotRegexpMatches"><strong>assertNotRegexpMatches</strong></a>(self, text, unexpected_regexp, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;the&nbsp;test&nbsp;if&nbsp;the&nbsp;text&nbsp;matches&nbsp;the&nbsp;regular&nbsp;expression.</tt></dd></dl>
+
+<dl><dt><a name="BrowserTestCase-assertRaises"><strong>assertRaises</strong></a>(self, excClass, callableObj<font color="#909090">=None</font>, *args, **kwargs)</dt><dd><tt>Fail&nbsp;unless&nbsp;an&nbsp;exception&nbsp;of&nbsp;class&nbsp;excClass&nbsp;is&nbsp;raised<br>
+by&nbsp;callableObj&nbsp;when&nbsp;invoked&nbsp;with&nbsp;arguments&nbsp;args&nbsp;and&nbsp;keyword<br>
+arguments&nbsp;kwargs.&nbsp;If&nbsp;a&nbsp;different&nbsp;type&nbsp;of&nbsp;exception&nbsp;is<br>
+raised,&nbsp;it&nbsp;will&nbsp;not&nbsp;be&nbsp;caught,&nbsp;and&nbsp;the&nbsp;test&nbsp;case&nbsp;will&nbsp;be<br>
+deemed&nbsp;to&nbsp;have&nbsp;suffered&nbsp;an&nbsp;error,&nbsp;exactly&nbsp;as&nbsp;for&nbsp;an<br>
+unexpected&nbsp;exception.<br>
+&nbsp;<br>
+If&nbsp;called&nbsp;with&nbsp;callableObj&nbsp;omitted&nbsp;or&nbsp;None,&nbsp;will&nbsp;return&nbsp;a<br>
+context&nbsp;object&nbsp;used&nbsp;like&nbsp;this::<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;with&nbsp;<a href="#BrowserTestCase-assertRaises">assertRaises</a>(SomeException):<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;do_something()<br>
+&nbsp;<br>
+The&nbsp;context&nbsp;manager&nbsp;keeps&nbsp;a&nbsp;reference&nbsp;to&nbsp;the&nbsp;exception&nbsp;as<br>
+the&nbsp;'exception'&nbsp;attribute.&nbsp;This&nbsp;allows&nbsp;you&nbsp;to&nbsp;inspect&nbsp;the<br>
+exception&nbsp;after&nbsp;the&nbsp;assertion::<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;with&nbsp;<a href="#BrowserTestCase-assertRaises">assertRaises</a>(SomeException)&nbsp;as&nbsp;cm:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;do_something()<br>
+&nbsp;&nbsp;&nbsp;&nbsp;the_exception&nbsp;=&nbsp;cm.exception<br>
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#BrowserTestCase-assertEqual">assertEqual</a>(the_exception.error_code,&nbsp;3)</tt></dd></dl>
+
+<dl><dt><a name="BrowserTestCase-assertRaisesRegexp"><strong>assertRaisesRegexp</strong></a>(self, expected_exception, expected_regexp, callable_obj<font color="#909090">=None</font>, *args, **kwargs)</dt><dd><tt>Asserts&nbsp;that&nbsp;the&nbsp;message&nbsp;in&nbsp;a&nbsp;raised&nbsp;exception&nbsp;matches&nbsp;a&nbsp;regexp.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;expected_exception:&nbsp;Exception&nbsp;class&nbsp;expected&nbsp;to&nbsp;be&nbsp;raised.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;expected_regexp:&nbsp;Regexp&nbsp;(re&nbsp;pattern&nbsp;object&nbsp;or&nbsp;string)&nbsp;expected<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;to&nbsp;be&nbsp;found&nbsp;in&nbsp;error&nbsp;message.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;callable_obj:&nbsp;Function&nbsp;to&nbsp;be&nbsp;called.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;args:&nbsp;Extra&nbsp;args.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;kwargs:&nbsp;Extra&nbsp;kwargs.</tt></dd></dl>
+
+<dl><dt><a name="BrowserTestCase-assertRegexpMatches"><strong>assertRegexpMatches</strong></a>(self, text, expected_regexp, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;the&nbsp;test&nbsp;unless&nbsp;the&nbsp;text&nbsp;matches&nbsp;the&nbsp;regular&nbsp;expression.</tt></dd></dl>
+
+<dl><dt><a name="BrowserTestCase-assertSequenceEqual"><strong>assertSequenceEqual</strong></a>(self, seq1, seq2, msg<font color="#909090">=None</font>, seq_type<font color="#909090">=None</font>)</dt><dd><tt>An&nbsp;equality&nbsp;assertion&nbsp;for&nbsp;ordered&nbsp;sequences&nbsp;(like&nbsp;lists&nbsp;and&nbsp;tuples).<br>
+&nbsp;<br>
+For&nbsp;the&nbsp;purposes&nbsp;of&nbsp;this&nbsp;function,&nbsp;a&nbsp;valid&nbsp;ordered&nbsp;sequence&nbsp;type&nbsp;is&nbsp;one<br>
+which&nbsp;can&nbsp;be&nbsp;indexed,&nbsp;has&nbsp;a&nbsp;length,&nbsp;and&nbsp;has&nbsp;an&nbsp;equality&nbsp;operator.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;seq1:&nbsp;The&nbsp;first&nbsp;sequence&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;seq2:&nbsp;The&nbsp;second&nbsp;sequence&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;seq_type:&nbsp;The&nbsp;expected&nbsp;datatype&nbsp;of&nbsp;the&nbsp;sequences,&nbsp;or&nbsp;None&nbsp;if&nbsp;no<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;datatype&nbsp;should&nbsp;be&nbsp;enforced.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;msg:&nbsp;Optional&nbsp;message&nbsp;to&nbsp;use&nbsp;on&nbsp;failure&nbsp;instead&nbsp;of&nbsp;a&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;differences.</tt></dd></dl>
+
+<dl><dt><a name="BrowserTestCase-assertSetEqual"><strong>assertSetEqual</strong></a>(self, set1, set2, msg<font color="#909090">=None</font>)</dt><dd><tt>A&nbsp;set-specific&nbsp;equality&nbsp;assertion.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;set1:&nbsp;The&nbsp;first&nbsp;set&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;set2:&nbsp;The&nbsp;second&nbsp;set&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;msg:&nbsp;Optional&nbsp;message&nbsp;to&nbsp;use&nbsp;on&nbsp;failure&nbsp;instead&nbsp;of&nbsp;a&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;differences.<br>
+&nbsp;<br>
+assertSetEqual&nbsp;uses&nbsp;ducktyping&nbsp;to&nbsp;support&nbsp;different&nbsp;types&nbsp;of&nbsp;sets,&nbsp;and<br>
+is&nbsp;optimized&nbsp;for&nbsp;sets&nbsp;specifically&nbsp;(parameters&nbsp;must&nbsp;support&nbsp;a<br>
+difference&nbsp;method).</tt></dd></dl>
+
+<dl><dt><a name="BrowserTestCase-assertTrue"><strong>assertTrue</strong></a>(self, expr, msg<font color="#909090">=None</font>)</dt><dd><tt>Check&nbsp;that&nbsp;the&nbsp;expression&nbsp;is&nbsp;true.</tt></dd></dl>
+
+<dl><dt><a name="BrowserTestCase-assertTupleEqual"><strong>assertTupleEqual</strong></a>(self, tuple1, tuple2, msg<font color="#909090">=None</font>)</dt><dd><tt>A&nbsp;tuple-specific&nbsp;equality&nbsp;assertion.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;tuple1:&nbsp;The&nbsp;first&nbsp;tuple&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;tuple2:&nbsp;The&nbsp;second&nbsp;tuple&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;msg:&nbsp;Optional&nbsp;message&nbsp;to&nbsp;use&nbsp;on&nbsp;failure&nbsp;instead&nbsp;of&nbsp;a&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;differences.</tt></dd></dl>
+
+<dl><dt><a name="BrowserTestCase-assert_"><strong>assert_</strong></a> = assertTrue(self, expr, msg<font color="#909090">=None</font>)</dt><dd><tt>Check&nbsp;that&nbsp;the&nbsp;expression&nbsp;is&nbsp;true.</tt></dd></dl>
+
+<dl><dt><a name="BrowserTestCase-countTestCases"><strong>countTestCases</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="BrowserTestCase-debug"><strong>debug</strong></a>(self)</dt><dd><tt>Run&nbsp;the&nbsp;test&nbsp;without&nbsp;collecting&nbsp;errors&nbsp;in&nbsp;a&nbsp;TestResult</tt></dd></dl>
+
+<dl><dt><a name="BrowserTestCase-defaultTestResult"><strong>defaultTestResult</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="BrowserTestCase-doCleanups"><strong>doCleanups</strong></a>(self)</dt><dd><tt>Execute&nbsp;all&nbsp;cleanup&nbsp;functions.&nbsp;Normally&nbsp;called&nbsp;for&nbsp;you&nbsp;after<br>
+tearDown.</tt></dd></dl>
+
+<dl><dt><a name="BrowserTestCase-fail"><strong>fail</strong></a>(self, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;immediately,&nbsp;with&nbsp;the&nbsp;given&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="BrowserTestCase-failIf"><strong>failIf</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="BrowserTestCase-failIfAlmostEqual"><strong>failIfAlmostEqual</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="BrowserTestCase-failIfEqual"><strong>failIfEqual</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="BrowserTestCase-failUnless"><strong>failUnless</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="BrowserTestCase-failUnlessAlmostEqual"><strong>failUnlessAlmostEqual</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="BrowserTestCase-failUnlessEqual"><strong>failUnlessEqual</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="BrowserTestCase-failUnlessRaises"><strong>failUnlessRaises</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="BrowserTestCase-id"><strong>id</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="BrowserTestCase-run"><strong>run</strong></a>(self, result<font color="#909090">=None</font>)</dt></dl>
+
+<dl><dt><a name="BrowserTestCase-setUp"><strong>setUp</strong></a>(self)</dt><dd><tt>Hook&nbsp;method&nbsp;for&nbsp;setting&nbsp;up&nbsp;the&nbsp;test&nbsp;fixture&nbsp;before&nbsp;exercising&nbsp;it.</tt></dd></dl>
+
+<dl><dt><a name="BrowserTestCase-shortDescription"><strong>shortDescription</strong></a>(self)</dt><dd><tt>Returns&nbsp;a&nbsp;one-line&nbsp;description&nbsp;of&nbsp;the&nbsp;test,&nbsp;or&nbsp;None&nbsp;if&nbsp;no<br>
+description&nbsp;has&nbsp;been&nbsp;provided.<br>
+&nbsp;<br>
+The&nbsp;default&nbsp;implementation&nbsp;of&nbsp;this&nbsp;method&nbsp;returns&nbsp;the&nbsp;first&nbsp;line&nbsp;of<br>
+the&nbsp;specified&nbsp;test&nbsp;method's&nbsp;docstring.</tt></dd></dl>
+
+<dl><dt><a name="BrowserTestCase-skipTest"><strong>skipTest</strong></a>(self, reason)</dt><dd><tt>Skip&nbsp;this&nbsp;test.</tt></dd></dl>
+
+<dl><dt><a name="BrowserTestCase-tearDown"><strong>tearDown</strong></a>(self)</dt><dd><tt>Hook&nbsp;method&nbsp;for&nbsp;deconstructing&nbsp;the&nbsp;test&nbsp;fixture&nbsp;after&nbsp;testing&nbsp;it.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="unittest.case.html#TestCase">unittest.case.TestCase</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="unittest.case.html#TestCase">unittest.case.TestCase</a>:<br>
+<dl><dt><strong>failureException</strong> = &lt;type 'exceptions.AssertionError'&gt;<dd><tt>Assertion&nbsp;failed.</tt></dl>
+
+<dl><dt><strong>longMessage</strong> = False</dl>
+
+<dl><dt><strong>maxDiff</strong> = 640</dl>
+
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-teardown_browser"><strong>teardown_browser</strong></a>()</dt></dl>
+</td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>current_browser</strong> = None<br>
+<strong>current_browser_options</strong> = None</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.disabled_cases.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.disabled_cases.html
new file mode 100644
index 0000000..d3982bb
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.disabled_cases.html
@@ -0,0 +1,363 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.testing.disabled_cases</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.testing.html"><font color="#ffffff">testing</font></a>.disabled_cases</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/testing/disabled_cases.py">telemetry/testing/disabled_cases.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.decorators.html">telemetry.decorators</a><br>
+</td><td width="25%" valign=top><a href="unittest.html">unittest</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="unittest.case.html#TestCase">unittest.case.TestCase</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.testing.disabled_cases.html#DisabledCases">DisabledCases</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="DisabledCases">class <strong>DisabledCases</strong></a>(<a href="unittest.case.html#TestCase">unittest.case.TestCase</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>#&nbsp;These&nbsp;are&nbsp;not&nbsp;real&nbsp;unittests.<br>
+#&nbsp;They&nbsp;are&nbsp;merely&nbsp;to&nbsp;test&nbsp;our&nbsp;Enable/Disable&nbsp;annotations.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.testing.disabled_cases.html#DisabledCases">DisabledCases</a></dd>
+<dd><a href="unittest.case.html#TestCase">unittest.case.TestCase</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="DisabledCases-testAllDisabled"><strong>testAllDisabled</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DisabledCases-testAllEnabled"><strong>testAllEnabled</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DisabledCases-testChromeOSOnly"><strong>testChromeOSOnly</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DisabledCases-testHasTabs"><strong>testHasTabs</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DisabledCases-testMacOnly"><strong>testMacOnly</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DisabledCases-testMavericksOnly"><strong>testMavericksOnly</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DisabledCases-testNoChromeOS"><strong>testNoChromeOS</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DisabledCases-testNoMac"><strong>testNoMac</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DisabledCases-testNoMavericks"><strong>testNoMavericks</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DisabledCases-testNoSystem"><strong>testNoSystem</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DisabledCases-testNoWinLinux"><strong>testNoWinLinux</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DisabledCases-testSystemOnly"><strong>testSystemOnly</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DisabledCases-testWinOrLinuxOnly"><strong>testWinOrLinuxOnly</strong></a>(self)</dt></dl>
+
+<hr>
+Methods inherited from <a href="unittest.case.html#TestCase">unittest.case.TestCase</a>:<br>
+<dl><dt><a name="DisabledCases-__call__"><strong>__call__</strong></a>(self, *args, **kwds)</dt></dl>
+
+<dl><dt><a name="DisabledCases-__eq__"><strong>__eq__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="DisabledCases-__hash__"><strong>__hash__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DisabledCases-__init__"><strong>__init__</strong></a>(self, methodName<font color="#909090">='runTest'</font>)</dt><dd><tt>Create&nbsp;an&nbsp;instance&nbsp;of&nbsp;the&nbsp;class&nbsp;that&nbsp;will&nbsp;use&nbsp;the&nbsp;named&nbsp;test<br>
+method&nbsp;when&nbsp;executed.&nbsp;Raises&nbsp;a&nbsp;ValueError&nbsp;if&nbsp;the&nbsp;instance&nbsp;does<br>
+not&nbsp;have&nbsp;a&nbsp;method&nbsp;with&nbsp;the&nbsp;specified&nbsp;name.</tt></dd></dl>
+
+<dl><dt><a name="DisabledCases-__ne__"><strong>__ne__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="DisabledCases-__repr__"><strong>__repr__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DisabledCases-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DisabledCases-addCleanup"><strong>addCleanup</strong></a>(self, function, *args, **kwargs)</dt><dd><tt>Add&nbsp;a&nbsp;function,&nbsp;with&nbsp;arguments,&nbsp;to&nbsp;be&nbsp;called&nbsp;when&nbsp;the&nbsp;test&nbsp;is<br>
+completed.&nbsp;Functions&nbsp;added&nbsp;are&nbsp;called&nbsp;on&nbsp;a&nbsp;LIFO&nbsp;basis&nbsp;and&nbsp;are<br>
+called&nbsp;after&nbsp;tearDown&nbsp;on&nbsp;test&nbsp;failure&nbsp;or&nbsp;success.<br>
+&nbsp;<br>
+Cleanup&nbsp;items&nbsp;are&nbsp;called&nbsp;even&nbsp;if&nbsp;setUp&nbsp;fails&nbsp;(unlike&nbsp;tearDown).</tt></dd></dl>
+
+<dl><dt><a name="DisabledCases-addTypeEqualityFunc"><strong>addTypeEqualityFunc</strong></a>(self, typeobj, function)</dt><dd><tt>Add&nbsp;a&nbsp;type&nbsp;specific&nbsp;assertEqual&nbsp;style&nbsp;function&nbsp;to&nbsp;compare&nbsp;a&nbsp;type.<br>
+&nbsp;<br>
+This&nbsp;method&nbsp;is&nbsp;for&nbsp;use&nbsp;by&nbsp;<a href="unittest.case.html#TestCase">TestCase</a>&nbsp;subclasses&nbsp;that&nbsp;need&nbsp;to&nbsp;register<br>
+their&nbsp;own&nbsp;type&nbsp;equality&nbsp;functions&nbsp;to&nbsp;provide&nbsp;nicer&nbsp;error&nbsp;messages.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;typeobj:&nbsp;The&nbsp;data&nbsp;type&nbsp;to&nbsp;call&nbsp;this&nbsp;function&nbsp;on&nbsp;when&nbsp;both&nbsp;values<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;are&nbsp;of&nbsp;the&nbsp;same&nbsp;type&nbsp;in&nbsp;<a href="#DisabledCases-assertEqual">assertEqual</a>().<br>
+&nbsp;&nbsp;&nbsp;&nbsp;function:&nbsp;The&nbsp;callable&nbsp;taking&nbsp;two&nbsp;arguments&nbsp;and&nbsp;an&nbsp;optional<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;msg=&nbsp;argument&nbsp;that&nbsp;raises&nbsp;self.<strong>failureException</strong>&nbsp;with&nbsp;a<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;useful&nbsp;error&nbsp;message&nbsp;when&nbsp;the&nbsp;two&nbsp;arguments&nbsp;are&nbsp;not&nbsp;equal.</tt></dd></dl>
+
+<dl><dt><a name="DisabledCases-assertAlmostEqual"><strong>assertAlmostEqual</strong></a>(self, first, second, places<font color="#909090">=None</font>, msg<font color="#909090">=None</font>, delta<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;unequal&nbsp;as&nbsp;determined&nbsp;by&nbsp;their<br>
+difference&nbsp;rounded&nbsp;to&nbsp;the&nbsp;given&nbsp;number&nbsp;of&nbsp;decimal&nbsp;places<br>
+(default&nbsp;7)&nbsp;and&nbsp;comparing&nbsp;to&nbsp;zero,&nbsp;or&nbsp;by&nbsp;comparing&nbsp;that&nbsp;the<br>
+between&nbsp;the&nbsp;two&nbsp;objects&nbsp;is&nbsp;more&nbsp;than&nbsp;the&nbsp;given&nbsp;delta.<br>
+&nbsp;<br>
+Note&nbsp;that&nbsp;decimal&nbsp;places&nbsp;(from&nbsp;zero)&nbsp;are&nbsp;usually&nbsp;not&nbsp;the&nbsp;same<br>
+as&nbsp;significant&nbsp;digits&nbsp;(measured&nbsp;from&nbsp;the&nbsp;most&nbsp;signficant&nbsp;digit).<br>
+&nbsp;<br>
+If&nbsp;the&nbsp;two&nbsp;objects&nbsp;compare&nbsp;equal&nbsp;then&nbsp;they&nbsp;will&nbsp;automatically<br>
+compare&nbsp;almost&nbsp;equal.</tt></dd></dl>
+
+<dl><dt><a name="DisabledCases-assertAlmostEquals"><strong>assertAlmostEquals</strong></a> = assertAlmostEqual(self, first, second, places<font color="#909090">=None</font>, msg<font color="#909090">=None</font>, delta<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;unequal&nbsp;as&nbsp;determined&nbsp;by&nbsp;their<br>
+difference&nbsp;rounded&nbsp;to&nbsp;the&nbsp;given&nbsp;number&nbsp;of&nbsp;decimal&nbsp;places<br>
+(default&nbsp;7)&nbsp;and&nbsp;comparing&nbsp;to&nbsp;zero,&nbsp;or&nbsp;by&nbsp;comparing&nbsp;that&nbsp;the<br>
+between&nbsp;the&nbsp;two&nbsp;objects&nbsp;is&nbsp;more&nbsp;than&nbsp;the&nbsp;given&nbsp;delta.<br>
+&nbsp;<br>
+Note&nbsp;that&nbsp;decimal&nbsp;places&nbsp;(from&nbsp;zero)&nbsp;are&nbsp;usually&nbsp;not&nbsp;the&nbsp;same<br>
+as&nbsp;significant&nbsp;digits&nbsp;(measured&nbsp;from&nbsp;the&nbsp;most&nbsp;signficant&nbsp;digit).<br>
+&nbsp;<br>
+If&nbsp;the&nbsp;two&nbsp;objects&nbsp;compare&nbsp;equal&nbsp;then&nbsp;they&nbsp;will&nbsp;automatically<br>
+compare&nbsp;almost&nbsp;equal.</tt></dd></dl>
+
+<dl><dt><a name="DisabledCases-assertDictContainsSubset"><strong>assertDictContainsSubset</strong></a>(self, expected, actual, msg<font color="#909090">=None</font>)</dt><dd><tt>Checks&nbsp;whether&nbsp;actual&nbsp;is&nbsp;a&nbsp;superset&nbsp;of&nbsp;expected.</tt></dd></dl>
+
+<dl><dt><a name="DisabledCases-assertDictEqual"><strong>assertDictEqual</strong></a>(self, d1, d2, msg<font color="#909090">=None</font>)</dt></dl>
+
+<dl><dt><a name="DisabledCases-assertEqual"><strong>assertEqual</strong></a>(self, first, second, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;unequal&nbsp;as&nbsp;determined&nbsp;by&nbsp;the&nbsp;'=='<br>
+operator.</tt></dd></dl>
+
+<dl><dt><a name="DisabledCases-assertEquals"><strong>assertEquals</strong></a> = assertEqual(self, first, second, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;unequal&nbsp;as&nbsp;determined&nbsp;by&nbsp;the&nbsp;'=='<br>
+operator.</tt></dd></dl>
+
+<dl><dt><a name="DisabledCases-assertFalse"><strong>assertFalse</strong></a>(self, expr, msg<font color="#909090">=None</font>)</dt><dd><tt>Check&nbsp;that&nbsp;the&nbsp;expression&nbsp;is&nbsp;false.</tt></dd></dl>
+
+<dl><dt><a name="DisabledCases-assertGreater"><strong>assertGreater</strong></a>(self, a, b, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#DisabledCases-assertTrue">assertTrue</a>(a&nbsp;&gt;&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="DisabledCases-assertGreaterEqual"><strong>assertGreaterEqual</strong></a>(self, a, b, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#DisabledCases-assertTrue">assertTrue</a>(a&nbsp;&gt;=&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="DisabledCases-assertIn"><strong>assertIn</strong></a>(self, member, container, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#DisabledCases-assertTrue">assertTrue</a>(a&nbsp;in&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="DisabledCases-assertIs"><strong>assertIs</strong></a>(self, expr1, expr2, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#DisabledCases-assertTrue">assertTrue</a>(a&nbsp;is&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="DisabledCases-assertIsInstance"><strong>assertIsInstance</strong></a>(self, obj, cls, msg<font color="#909090">=None</font>)</dt><dd><tt>Same&nbsp;as&nbsp;<a href="#DisabledCases-assertTrue">assertTrue</a>(isinstance(obj,&nbsp;cls)),&nbsp;with&nbsp;a&nbsp;nicer<br>
+default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="DisabledCases-assertIsNone"><strong>assertIsNone</strong></a>(self, obj, msg<font color="#909090">=None</font>)</dt><dd><tt>Same&nbsp;as&nbsp;<a href="#DisabledCases-assertTrue">assertTrue</a>(obj&nbsp;is&nbsp;None),&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="DisabledCases-assertIsNot"><strong>assertIsNot</strong></a>(self, expr1, expr2, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#DisabledCases-assertTrue">assertTrue</a>(a&nbsp;is&nbsp;not&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="DisabledCases-assertIsNotNone"><strong>assertIsNotNone</strong></a>(self, obj, msg<font color="#909090">=None</font>)</dt><dd><tt>Included&nbsp;for&nbsp;symmetry&nbsp;with&nbsp;assertIsNone.</tt></dd></dl>
+
+<dl><dt><a name="DisabledCases-assertItemsEqual"><strong>assertItemsEqual</strong></a>(self, expected_seq, actual_seq, msg<font color="#909090">=None</font>)</dt><dd><tt>An&nbsp;unordered&nbsp;sequence&nbsp;specific&nbsp;comparison.&nbsp;It&nbsp;asserts&nbsp;that<br>
+actual_seq&nbsp;and&nbsp;expected_seq&nbsp;have&nbsp;the&nbsp;same&nbsp;element&nbsp;counts.<br>
+Equivalent&nbsp;to::<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#DisabledCases-assertEqual">assertEqual</a>(Counter(iter(actual_seq)),<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Counter(iter(expected_seq)))<br>
+&nbsp;<br>
+Asserts&nbsp;that&nbsp;each&nbsp;element&nbsp;has&nbsp;the&nbsp;same&nbsp;count&nbsp;in&nbsp;both&nbsp;sequences.<br>
+Example:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;[0,&nbsp;1,&nbsp;1]&nbsp;and&nbsp;[1,&nbsp;0,&nbsp;1]&nbsp;compare&nbsp;equal.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;[0,&nbsp;0,&nbsp;1]&nbsp;and&nbsp;[0,&nbsp;1]&nbsp;compare&nbsp;unequal.</tt></dd></dl>
+
+<dl><dt><a name="DisabledCases-assertLess"><strong>assertLess</strong></a>(self, a, b, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#DisabledCases-assertTrue">assertTrue</a>(a&nbsp;&lt;&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="DisabledCases-assertLessEqual"><strong>assertLessEqual</strong></a>(self, a, b, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#DisabledCases-assertTrue">assertTrue</a>(a&nbsp;&lt;=&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="DisabledCases-assertListEqual"><strong>assertListEqual</strong></a>(self, list1, list2, msg<font color="#909090">=None</font>)</dt><dd><tt>A&nbsp;list-specific&nbsp;equality&nbsp;assertion.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;list1:&nbsp;The&nbsp;first&nbsp;list&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;list2:&nbsp;The&nbsp;second&nbsp;list&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;msg:&nbsp;Optional&nbsp;message&nbsp;to&nbsp;use&nbsp;on&nbsp;failure&nbsp;instead&nbsp;of&nbsp;a&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;differences.</tt></dd></dl>
+
+<dl><dt><a name="DisabledCases-assertMultiLineEqual"><strong>assertMultiLineEqual</strong></a>(self, first, second, msg<font color="#909090">=None</font>)</dt><dd><tt>Assert&nbsp;that&nbsp;two&nbsp;multi-line&nbsp;strings&nbsp;are&nbsp;equal.</tt></dd></dl>
+
+<dl><dt><a name="DisabledCases-assertNotAlmostEqual"><strong>assertNotAlmostEqual</strong></a>(self, first, second, places<font color="#909090">=None</font>, msg<font color="#909090">=None</font>, delta<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;equal&nbsp;as&nbsp;determined&nbsp;by&nbsp;their<br>
+difference&nbsp;rounded&nbsp;to&nbsp;the&nbsp;given&nbsp;number&nbsp;of&nbsp;decimal&nbsp;places<br>
+(default&nbsp;7)&nbsp;and&nbsp;comparing&nbsp;to&nbsp;zero,&nbsp;or&nbsp;by&nbsp;comparing&nbsp;that&nbsp;the<br>
+between&nbsp;the&nbsp;two&nbsp;objects&nbsp;is&nbsp;less&nbsp;than&nbsp;the&nbsp;given&nbsp;delta.<br>
+&nbsp;<br>
+Note&nbsp;that&nbsp;decimal&nbsp;places&nbsp;(from&nbsp;zero)&nbsp;are&nbsp;usually&nbsp;not&nbsp;the&nbsp;same<br>
+as&nbsp;significant&nbsp;digits&nbsp;(measured&nbsp;from&nbsp;the&nbsp;most&nbsp;signficant&nbsp;digit).<br>
+&nbsp;<br>
+Objects&nbsp;that&nbsp;are&nbsp;equal&nbsp;automatically&nbsp;fail.</tt></dd></dl>
+
+<dl><dt><a name="DisabledCases-assertNotAlmostEquals"><strong>assertNotAlmostEquals</strong></a> = assertNotAlmostEqual(self, first, second, places<font color="#909090">=None</font>, msg<font color="#909090">=None</font>, delta<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;equal&nbsp;as&nbsp;determined&nbsp;by&nbsp;their<br>
+difference&nbsp;rounded&nbsp;to&nbsp;the&nbsp;given&nbsp;number&nbsp;of&nbsp;decimal&nbsp;places<br>
+(default&nbsp;7)&nbsp;and&nbsp;comparing&nbsp;to&nbsp;zero,&nbsp;or&nbsp;by&nbsp;comparing&nbsp;that&nbsp;the<br>
+between&nbsp;the&nbsp;two&nbsp;objects&nbsp;is&nbsp;less&nbsp;than&nbsp;the&nbsp;given&nbsp;delta.<br>
+&nbsp;<br>
+Note&nbsp;that&nbsp;decimal&nbsp;places&nbsp;(from&nbsp;zero)&nbsp;are&nbsp;usually&nbsp;not&nbsp;the&nbsp;same<br>
+as&nbsp;significant&nbsp;digits&nbsp;(measured&nbsp;from&nbsp;the&nbsp;most&nbsp;signficant&nbsp;digit).<br>
+&nbsp;<br>
+Objects&nbsp;that&nbsp;are&nbsp;equal&nbsp;automatically&nbsp;fail.</tt></dd></dl>
+
+<dl><dt><a name="DisabledCases-assertNotEqual"><strong>assertNotEqual</strong></a>(self, first, second, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;equal&nbsp;as&nbsp;determined&nbsp;by&nbsp;the&nbsp;'!='<br>
+operator.</tt></dd></dl>
+
+<dl><dt><a name="DisabledCases-assertNotEquals"><strong>assertNotEquals</strong></a> = assertNotEqual(self, first, second, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;equal&nbsp;as&nbsp;determined&nbsp;by&nbsp;the&nbsp;'!='<br>
+operator.</tt></dd></dl>
+
+<dl><dt><a name="DisabledCases-assertNotIn"><strong>assertNotIn</strong></a>(self, member, container, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#DisabledCases-assertTrue">assertTrue</a>(a&nbsp;not&nbsp;in&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="DisabledCases-assertNotIsInstance"><strong>assertNotIsInstance</strong></a>(self, obj, cls, msg<font color="#909090">=None</font>)</dt><dd><tt>Included&nbsp;for&nbsp;symmetry&nbsp;with&nbsp;assertIsInstance.</tt></dd></dl>
+
+<dl><dt><a name="DisabledCases-assertNotRegexpMatches"><strong>assertNotRegexpMatches</strong></a>(self, text, unexpected_regexp, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;the&nbsp;test&nbsp;if&nbsp;the&nbsp;text&nbsp;matches&nbsp;the&nbsp;regular&nbsp;expression.</tt></dd></dl>
+
+<dl><dt><a name="DisabledCases-assertRaises"><strong>assertRaises</strong></a>(self, excClass, callableObj<font color="#909090">=None</font>, *args, **kwargs)</dt><dd><tt>Fail&nbsp;unless&nbsp;an&nbsp;exception&nbsp;of&nbsp;class&nbsp;excClass&nbsp;is&nbsp;raised<br>
+by&nbsp;callableObj&nbsp;when&nbsp;invoked&nbsp;with&nbsp;arguments&nbsp;args&nbsp;and&nbsp;keyword<br>
+arguments&nbsp;kwargs.&nbsp;If&nbsp;a&nbsp;different&nbsp;type&nbsp;of&nbsp;exception&nbsp;is<br>
+raised,&nbsp;it&nbsp;will&nbsp;not&nbsp;be&nbsp;caught,&nbsp;and&nbsp;the&nbsp;test&nbsp;case&nbsp;will&nbsp;be<br>
+deemed&nbsp;to&nbsp;have&nbsp;suffered&nbsp;an&nbsp;error,&nbsp;exactly&nbsp;as&nbsp;for&nbsp;an<br>
+unexpected&nbsp;exception.<br>
+&nbsp;<br>
+If&nbsp;called&nbsp;with&nbsp;callableObj&nbsp;omitted&nbsp;or&nbsp;None,&nbsp;will&nbsp;return&nbsp;a<br>
+context&nbsp;object&nbsp;used&nbsp;like&nbsp;this::<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;with&nbsp;<a href="#DisabledCases-assertRaises">assertRaises</a>(SomeException):<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;do_something()<br>
+&nbsp;<br>
+The&nbsp;context&nbsp;manager&nbsp;keeps&nbsp;a&nbsp;reference&nbsp;to&nbsp;the&nbsp;exception&nbsp;as<br>
+the&nbsp;'exception'&nbsp;attribute.&nbsp;This&nbsp;allows&nbsp;you&nbsp;to&nbsp;inspect&nbsp;the<br>
+exception&nbsp;after&nbsp;the&nbsp;assertion::<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;with&nbsp;<a href="#DisabledCases-assertRaises">assertRaises</a>(SomeException)&nbsp;as&nbsp;cm:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;do_something()<br>
+&nbsp;&nbsp;&nbsp;&nbsp;the_exception&nbsp;=&nbsp;cm.exception<br>
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#DisabledCases-assertEqual">assertEqual</a>(the_exception.error_code,&nbsp;3)</tt></dd></dl>
+
+<dl><dt><a name="DisabledCases-assertRaisesRegexp"><strong>assertRaisesRegexp</strong></a>(self, expected_exception, expected_regexp, callable_obj<font color="#909090">=None</font>, *args, **kwargs)</dt><dd><tt>Asserts&nbsp;that&nbsp;the&nbsp;message&nbsp;in&nbsp;a&nbsp;raised&nbsp;exception&nbsp;matches&nbsp;a&nbsp;regexp.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;expected_exception:&nbsp;Exception&nbsp;class&nbsp;expected&nbsp;to&nbsp;be&nbsp;raised.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;expected_regexp:&nbsp;Regexp&nbsp;(re&nbsp;pattern&nbsp;object&nbsp;or&nbsp;string)&nbsp;expected<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;to&nbsp;be&nbsp;found&nbsp;in&nbsp;error&nbsp;message.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;callable_obj:&nbsp;Function&nbsp;to&nbsp;be&nbsp;called.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;args:&nbsp;Extra&nbsp;args.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;kwargs:&nbsp;Extra&nbsp;kwargs.</tt></dd></dl>
+
+<dl><dt><a name="DisabledCases-assertRegexpMatches"><strong>assertRegexpMatches</strong></a>(self, text, expected_regexp, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;the&nbsp;test&nbsp;unless&nbsp;the&nbsp;text&nbsp;matches&nbsp;the&nbsp;regular&nbsp;expression.</tt></dd></dl>
+
+<dl><dt><a name="DisabledCases-assertSequenceEqual"><strong>assertSequenceEqual</strong></a>(self, seq1, seq2, msg<font color="#909090">=None</font>, seq_type<font color="#909090">=None</font>)</dt><dd><tt>An&nbsp;equality&nbsp;assertion&nbsp;for&nbsp;ordered&nbsp;sequences&nbsp;(like&nbsp;lists&nbsp;and&nbsp;tuples).<br>
+&nbsp;<br>
+For&nbsp;the&nbsp;purposes&nbsp;of&nbsp;this&nbsp;function,&nbsp;a&nbsp;valid&nbsp;ordered&nbsp;sequence&nbsp;type&nbsp;is&nbsp;one<br>
+which&nbsp;can&nbsp;be&nbsp;indexed,&nbsp;has&nbsp;a&nbsp;length,&nbsp;and&nbsp;has&nbsp;an&nbsp;equality&nbsp;operator.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;seq1:&nbsp;The&nbsp;first&nbsp;sequence&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;seq2:&nbsp;The&nbsp;second&nbsp;sequence&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;seq_type:&nbsp;The&nbsp;expected&nbsp;datatype&nbsp;of&nbsp;the&nbsp;sequences,&nbsp;or&nbsp;None&nbsp;if&nbsp;no<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;datatype&nbsp;should&nbsp;be&nbsp;enforced.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;msg:&nbsp;Optional&nbsp;message&nbsp;to&nbsp;use&nbsp;on&nbsp;failure&nbsp;instead&nbsp;of&nbsp;a&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;differences.</tt></dd></dl>
+
+<dl><dt><a name="DisabledCases-assertSetEqual"><strong>assertSetEqual</strong></a>(self, set1, set2, msg<font color="#909090">=None</font>)</dt><dd><tt>A&nbsp;set-specific&nbsp;equality&nbsp;assertion.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;set1:&nbsp;The&nbsp;first&nbsp;set&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;set2:&nbsp;The&nbsp;second&nbsp;set&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;msg:&nbsp;Optional&nbsp;message&nbsp;to&nbsp;use&nbsp;on&nbsp;failure&nbsp;instead&nbsp;of&nbsp;a&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;differences.<br>
+&nbsp;<br>
+assertSetEqual&nbsp;uses&nbsp;ducktyping&nbsp;to&nbsp;support&nbsp;different&nbsp;types&nbsp;of&nbsp;sets,&nbsp;and<br>
+is&nbsp;optimized&nbsp;for&nbsp;sets&nbsp;specifically&nbsp;(parameters&nbsp;must&nbsp;support&nbsp;a<br>
+difference&nbsp;method).</tt></dd></dl>
+
+<dl><dt><a name="DisabledCases-assertTrue"><strong>assertTrue</strong></a>(self, expr, msg<font color="#909090">=None</font>)</dt><dd><tt>Check&nbsp;that&nbsp;the&nbsp;expression&nbsp;is&nbsp;true.</tt></dd></dl>
+
+<dl><dt><a name="DisabledCases-assertTupleEqual"><strong>assertTupleEqual</strong></a>(self, tuple1, tuple2, msg<font color="#909090">=None</font>)</dt><dd><tt>A&nbsp;tuple-specific&nbsp;equality&nbsp;assertion.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;tuple1:&nbsp;The&nbsp;first&nbsp;tuple&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;tuple2:&nbsp;The&nbsp;second&nbsp;tuple&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;msg:&nbsp;Optional&nbsp;message&nbsp;to&nbsp;use&nbsp;on&nbsp;failure&nbsp;instead&nbsp;of&nbsp;a&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;differences.</tt></dd></dl>
+
+<dl><dt><a name="DisabledCases-assert_"><strong>assert_</strong></a> = assertTrue(self, expr, msg<font color="#909090">=None</font>)</dt><dd><tt>Check&nbsp;that&nbsp;the&nbsp;expression&nbsp;is&nbsp;true.</tt></dd></dl>
+
+<dl><dt><a name="DisabledCases-countTestCases"><strong>countTestCases</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DisabledCases-debug"><strong>debug</strong></a>(self)</dt><dd><tt>Run&nbsp;the&nbsp;test&nbsp;without&nbsp;collecting&nbsp;errors&nbsp;in&nbsp;a&nbsp;TestResult</tt></dd></dl>
+
+<dl><dt><a name="DisabledCases-defaultTestResult"><strong>defaultTestResult</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DisabledCases-doCleanups"><strong>doCleanups</strong></a>(self)</dt><dd><tt>Execute&nbsp;all&nbsp;cleanup&nbsp;functions.&nbsp;Normally&nbsp;called&nbsp;for&nbsp;you&nbsp;after<br>
+tearDown.</tt></dd></dl>
+
+<dl><dt><a name="DisabledCases-fail"><strong>fail</strong></a>(self, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;immediately,&nbsp;with&nbsp;the&nbsp;given&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="DisabledCases-failIf"><strong>failIf</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="DisabledCases-failIfAlmostEqual"><strong>failIfAlmostEqual</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="DisabledCases-failIfEqual"><strong>failIfEqual</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="DisabledCases-failUnless"><strong>failUnless</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="DisabledCases-failUnlessAlmostEqual"><strong>failUnlessAlmostEqual</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="DisabledCases-failUnlessEqual"><strong>failUnlessEqual</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="DisabledCases-failUnlessRaises"><strong>failUnlessRaises</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="DisabledCases-id"><strong>id</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="DisabledCases-run"><strong>run</strong></a>(self, result<font color="#909090">=None</font>)</dt></dl>
+
+<dl><dt><a name="DisabledCases-setUp"><strong>setUp</strong></a>(self)</dt><dd><tt>Hook&nbsp;method&nbsp;for&nbsp;setting&nbsp;up&nbsp;the&nbsp;test&nbsp;fixture&nbsp;before&nbsp;exercising&nbsp;it.</tt></dd></dl>
+
+<dl><dt><a name="DisabledCases-shortDescription"><strong>shortDescription</strong></a>(self)</dt><dd><tt>Returns&nbsp;a&nbsp;one-line&nbsp;description&nbsp;of&nbsp;the&nbsp;test,&nbsp;or&nbsp;None&nbsp;if&nbsp;no<br>
+description&nbsp;has&nbsp;been&nbsp;provided.<br>
+&nbsp;<br>
+The&nbsp;default&nbsp;implementation&nbsp;of&nbsp;this&nbsp;method&nbsp;returns&nbsp;the&nbsp;first&nbsp;line&nbsp;of<br>
+the&nbsp;specified&nbsp;test&nbsp;method's&nbsp;docstring.</tt></dd></dl>
+
+<dl><dt><a name="DisabledCases-skipTest"><strong>skipTest</strong></a>(self, reason)</dt><dd><tt>Skip&nbsp;this&nbsp;test.</tt></dd></dl>
+
+<dl><dt><a name="DisabledCases-tearDown"><strong>tearDown</strong></a>(self)</dt><dd><tt>Hook&nbsp;method&nbsp;for&nbsp;deconstructing&nbsp;the&nbsp;test&nbsp;fixture&nbsp;after&nbsp;testing&nbsp;it.</tt></dd></dl>
+
+<hr>
+Class methods inherited from <a href="unittest.case.html#TestCase">unittest.case.TestCase</a>:<br>
+<dl><dt><a name="DisabledCases-setUpClass"><strong>setUpClass</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Hook&nbsp;method&nbsp;for&nbsp;setting&nbsp;up&nbsp;class&nbsp;fixture&nbsp;before&nbsp;running&nbsp;tests&nbsp;in&nbsp;the&nbsp;class.</tt></dd></dl>
+
+<dl><dt><a name="DisabledCases-tearDownClass"><strong>tearDownClass</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Hook&nbsp;method&nbsp;for&nbsp;deconstructing&nbsp;the&nbsp;class&nbsp;fixture&nbsp;after&nbsp;running&nbsp;all&nbsp;tests&nbsp;in&nbsp;the&nbsp;class.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="unittest.case.html#TestCase">unittest.case.TestCase</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="unittest.case.html#TestCase">unittest.case.TestCase</a>:<br>
+<dl><dt><strong>failureException</strong> = &lt;type 'exceptions.AssertionError'&gt;<dd><tt>Assertion&nbsp;failed.</tt></dl>
+
+<dl><dt><strong>longMessage</strong> = False</dl>
+
+<dl><dt><strong>maxDiff</strong> = 640</dl>
+
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.fakes.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.fakes.html
new file mode 100644
index 0000000..bc998a1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.fakes.html
@@ -0,0 +1,367 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: package telemetry.testing.fakes</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.testing.html"><font color="#ffffff">testing</font></a>.fakes</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/testing/fakes/__init__.py">telemetry/testing/fakes/__init__.py</a></font></td></tr></table>
+    <p><tt>Provides&nbsp;fakes&nbsp;for&nbsp;several&nbsp;of&nbsp;Telemetry's&nbsp;internal&nbsp;objects.<br>
+&nbsp;<br>
+These&nbsp;allow&nbsp;code&nbsp;like&nbsp;story_runner&nbsp;and&nbsp;Benchmark&nbsp;to&nbsp;be&nbsp;run&nbsp;and&nbsp;tested<br>
+without&nbsp;compiling&nbsp;or&nbsp;starting&nbsp;a&nbsp;browser.&nbsp;Class&nbsp;names&nbsp;prepended&nbsp;with&nbsp;an<br>
+underscore&nbsp;are&nbsp;intended&nbsp;to&nbsp;be&nbsp;implementation&nbsp;details,&nbsp;and&nbsp;should&nbsp;not<br>
+be&nbsp;subclassed;&nbsp;however,&nbsp;some,&nbsp;like&nbsp;_FakeBrowser,&nbsp;have&nbsp;public&nbsp;APIs&nbsp;that<br>
+may&nbsp;need&nbsp;to&nbsp;be&nbsp;called&nbsp;in&nbsp;tests.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Package Contents</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.testing.fakes.html#FakeHTTPServer">FakeHTTPServer</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.testing.fakes.html#FakeInspectorWebsocket">FakeInspectorWebsocket</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.testing.fakes.html#FakePlatform">FakePlatform</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.testing.fakes.html#FakeLinuxPlatform">FakeLinuxPlatform</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="telemetry.testing.fakes.html#FakePossibleBrowser">FakePossibleBrowser</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.platform.system_info.html#SystemInfo">telemetry.internal.platform.system_info.SystemInfo</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.testing.fakes.html#FakeSystemInfo">FakeSystemInfo</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="telemetry.page.shared_page_state.html#SharedPageState">telemetry.page.shared_page_state.SharedPageState</a>(<a href="telemetry.story.shared_state.html#SharedState">telemetry.story.shared_state.SharedState</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.testing.fakes.html#FakeSharedPageState">FakeSharedPageState</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="FakeHTTPServer">class <strong>FakeHTTPServer</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="FakeHTTPServer-UrlOf"><strong>UrlOf</strong></a>(self, url)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="FakeInspectorWebsocket">class <strong>FakeInspectorWebsocket</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="FakeInspectorWebsocket-AddAsyncResponse"><strong>AddAsyncResponse</strong></a>(self, method, result, time)</dt></dl>
+
+<dl><dt><a name="FakeInspectorWebsocket-AddEvent"><strong>AddEvent</strong></a>(self, method, params, time)</dt></dl>
+
+<dl><dt><a name="FakeInspectorWebsocket-AddResponseHandler"><strong>AddResponseHandler</strong></a>(self, method, handler)</dt></dl>
+
+<dl><dt><a name="FakeInspectorWebsocket-AsyncRequest"><strong>AsyncRequest</strong></a>(self, request, callback)</dt></dl>
+
+<dl><dt><a name="FakeInspectorWebsocket-Connect"><strong>Connect</strong></a>(self, _)</dt></dl>
+
+<dl><dt><a name="FakeInspectorWebsocket-DispatchNotifications"><strong>DispatchNotifications</strong></a>(self, timeout)</dt></dl>
+
+<dl><dt><a name="FakeInspectorWebsocket-RegisterDomain"><strong>RegisterDomain</strong></a>(self, _, handler)</dt></dl>
+
+<dl><dt><a name="FakeInspectorWebsocket-SendAndIgnoreResponse"><strong>SendAndIgnoreResponse</strong></a>(self, request)</dt></dl>
+
+<dl><dt><a name="FakeInspectorWebsocket-SyncRequest"><strong>SyncRequest</strong></a>(self, request, *_args, **_kwargs)</dt></dl>
+
+<dl><dt><a name="FakeInspectorWebsocket-__init__"><strong>__init__</strong></a>(self, mock_timer)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="FakeLinuxPlatform">class <strong>FakeLinuxPlatform</strong></a>(<a href="telemetry.testing.fakes.html#FakePlatform">FakePlatform</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.testing.fakes.html#FakeLinuxPlatform">FakeLinuxPlatform</a></dd>
+<dd><a href="telemetry.testing.fakes.html#FakePlatform">FakePlatform</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="FakeLinuxPlatform-CanTakeScreenshot"><strong>CanTakeScreenshot</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="FakeLinuxPlatform-GetArchName"><strong>GetArchName</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="FakeLinuxPlatform-GetDeviceTypeName"><strong>GetDeviceTypeName</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="FakeLinuxPlatform-GetOSName"><strong>GetOSName</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="FakeLinuxPlatform-GetOSVersionName"><strong>GetOSVersionName</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="FakeLinuxPlatform-SetHTTPServerDirectories"><strong>SetHTTPServerDirectories</strong></a>(self, paths)</dt></dl>
+
+<dl><dt><a name="FakeLinuxPlatform-TakeScreenshot"><strong>TakeScreenshot</strong></a>(self, file_path)</dt></dl>
+
+<dl><dt><a name="FakeLinuxPlatform-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>is_host_platform</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.testing.fakes.html#FakePlatform">FakePlatform</a>:<br>
+<dl><dt><a name="FakeLinuxPlatform-CanMonitorThermalThrottling"><strong>CanMonitorThermalThrottling</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="FakeLinuxPlatform-HasBeenThermallyThrottled"><strong>HasBeenThermallyThrottled</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="FakeLinuxPlatform-IsThermallyThrottled"><strong>IsThermallyThrottled</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="FakeLinuxPlatform-StopAllLocalServers"><strong>StopAllLocalServers</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.testing.fakes.html#FakePlatform">FakePlatform</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>network_controller</strong></dt>
+</dl>
+<dl><dt><strong>tracing_controller</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="FakePlatform">class <strong>FakePlatform</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="FakePlatform-CanMonitorThermalThrottling"><strong>CanMonitorThermalThrottling</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="FakePlatform-GetArchName"><strong>GetArchName</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="FakePlatform-GetDeviceTypeName"><strong>GetDeviceTypeName</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="FakePlatform-GetOSName"><strong>GetOSName</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="FakePlatform-GetOSVersionName"><strong>GetOSVersionName</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="FakePlatform-HasBeenThermallyThrottled"><strong>HasBeenThermallyThrottled</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="FakePlatform-IsThermallyThrottled"><strong>IsThermallyThrottled</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="FakePlatform-StopAllLocalServers"><strong>StopAllLocalServers</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>is_host_platform</strong></dt>
+</dl>
+<dl><dt><strong>network_controller</strong></dt>
+</dl>
+<dl><dt><strong>tracing_controller</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="FakePossibleBrowser">class <strong>FakePossibleBrowser</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="FakePossibleBrowser-Create"><strong>Create</strong></a>(self, finder_options)</dt></dl>
+
+<dl><dt><a name="FakePossibleBrowser-IsRemote"><strong>IsRemote</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="FakePossibleBrowser-SetCredentialsPath"><strong>SetCredentialsPath</strong></a>(self, _)</dt></dl>
+
+<dl><dt><a name="FakePossibleBrowser-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>platform</strong></dt>
+<dd><tt>The&nbsp;platform&nbsp;object&nbsp;from&nbsp;the&nbsp;returned&nbsp;browser.<br>
+&nbsp;<br>
+To&nbsp;change&nbsp;this&nbsp;or&nbsp;set&nbsp;it&nbsp;up,&nbsp;change&nbsp;the&nbsp;returned&nbsp;browser's<br>
+platform.</tt></dd>
+</dl>
+<dl><dt><strong>returned_browser</strong></dt>
+<dd><tt>The&nbsp;browser&nbsp;object&nbsp;that&nbsp;will&nbsp;be&nbsp;returned&nbsp;through&nbsp;later&nbsp;API&nbsp;calls.</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="FakeSharedPageState">class <strong>FakeSharedPageState</strong></a>(<a href="telemetry.page.shared_page_state.html#SharedPageState">telemetry.page.shared_page_state.SharedPageState</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.testing.fakes.html#FakeSharedPageState">FakeSharedPageState</a></dd>
+<dd><a href="telemetry.page.shared_page_state.html#SharedPageState">telemetry.page.shared_page_state.SharedPageState</a></dd>
+<dd><a href="telemetry.story.shared_state.html#SharedState">telemetry.story.shared_state.SharedState</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="FakeSharedPageState-ConfigurePossibleBrowser"><strong>ConfigurePossibleBrowser</strong></a>(self, possible_browser)</dt><dd><tt>Override&nbsp;this&nbsp;to&nbsp;configure&nbsp;the&nbsp;PossibleBrowser.<br>
+&nbsp;<br>
+Can&nbsp;make&nbsp;changes&nbsp;to&nbsp;the&nbsp;browser's&nbsp;configuration&nbsp;here&nbsp;via&nbsp;e.g.:<br>
+&nbsp;&nbsp;&nbsp;possible_browser.returned_browser.returned_system_info&nbsp;=&nbsp;...</tt></dd></dl>
+
+<dl><dt><a name="FakeSharedPageState-DidRunStory"><strong>DidRunStory</strong></a>(self, results)</dt></dl>
+
+<dl><dt><a name="FakeSharedPageState-__init__"><strong>__init__</strong></a>(self, test, finder_options, story_set)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.page.shared_page_state.html#SharedPageState">telemetry.page.shared_page_state.SharedPageState</a>:<br>
+<dl><dt><a name="FakeSharedPageState-CanRunOnBrowser"><strong>CanRunOnBrowser</strong></a>(self, browser_info, page)</dt><dd><tt>Override&nbsp;this&nbsp;to&nbsp;return&nbsp;whether&nbsp;the&nbsp;browser&nbsp;brought&nbsp;up&nbsp;by&nbsp;this&nbsp;state<br>
+instance&nbsp;is&nbsp;suitable&nbsp;for&nbsp;running&nbsp;the&nbsp;given&nbsp;page.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;browser_info:&nbsp;an&nbsp;instance&nbsp;of&nbsp;telemetry.core.browser_info.BrowserInfo<br>
+&nbsp;&nbsp;page:&nbsp;an&nbsp;instance&nbsp;of&nbsp;telemetry.page.Page</tt></dd></dl>
+
+<dl><dt><a name="FakeSharedPageState-CanRunStory"><strong>CanRunStory</strong></a>(self, page)</dt></dl>
+
+<dl><dt><a name="FakeSharedPageState-GetPregeneratedProfileArchiveDir"><strong>GetPregeneratedProfileArchiveDir</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="FakeSharedPageState-RunStory"><strong>RunStory</strong></a>(self, results)</dt></dl>
+
+<dl><dt><a name="FakeSharedPageState-SetPregeneratedProfileArchiveDir"><strong>SetPregeneratedProfileArchiveDir</strong></a>(self, archive_path)</dt><dd><tt>Benchmarks&nbsp;can&nbsp;set&nbsp;a&nbsp;pre-generated&nbsp;profile&nbsp;archive&nbsp;to&nbsp;indicate&nbsp;that&nbsp;when<br>
+Chrome&nbsp;is&nbsp;launched,&nbsp;it&nbsp;should&nbsp;have&nbsp;a&nbsp;--user-data-dir&nbsp;set&nbsp;to&nbsp;the<br>
+pregenerated&nbsp;profile,&nbsp;rather&nbsp;than&nbsp;to&nbsp;an&nbsp;empty&nbsp;profile.<br>
+&nbsp;<br>
+If&nbsp;the&nbsp;benchmark&nbsp;is&nbsp;invoked&nbsp;with&nbsp;the&nbsp;option&nbsp;--profile-dir=&lt;dir&gt;,&nbsp;that<br>
+option&nbsp;overrides&nbsp;this&nbsp;value.</tt></dd></dl>
+
+<dl><dt><a name="FakeSharedPageState-TearDownState"><strong>TearDownState</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="FakeSharedPageState-WillRunStory"><strong>WillRunStory</strong></a>(self, page)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.page.shared_page_state.html#SharedPageState">telemetry.page.shared_page_state.SharedPageState</a>:<br>
+<dl><dt><strong>browser</strong></dt>
+</dl>
+<dl><dt><strong>current_page</strong></dt>
+</dl>
+<dl><dt><strong>current_tab</strong></dt>
+</dl>
+<dl><dt><strong>page_test</strong></dt>
+</dl>
+<dl><dt><strong>platform</strong></dt>
+</dl>
+<hr>
+Data descriptors inherited from <a href="telemetry.story.shared_state.html#SharedState">telemetry.story.shared_state.SharedState</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="FakeSystemInfo">class <strong>FakeSystemInfo</strong></a>(<a href="telemetry.internal.platform.system_info.html#SystemInfo">telemetry.internal.platform.system_info.SystemInfo</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.testing.fakes.html#FakeSystemInfo">FakeSystemInfo</a></dd>
+<dd><a href="telemetry.internal.platform.system_info.html#SystemInfo">telemetry.internal.platform.system_info.SystemInfo</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="FakeSystemInfo-__init__"><strong>__init__</strong></a>(self, model_name<font color="#909090">=''</font>, gpu_dict<font color="#909090">=None</font>)</dt></dl>
+
+<hr>
+Class methods inherited from <a href="telemetry.internal.platform.system_info.html#SystemInfo">telemetry.internal.platform.system_info.SystemInfo</a>:<br>
+<dl><dt><a name="FakeSystemInfo-FromDict"><strong>FromDict</strong></a>(cls, attrs)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Constructs&nbsp;a&nbsp;<a href="telemetry.internal.platform.system_info.html#SystemInfo">SystemInfo</a>&nbsp;from&nbsp;a&nbsp;dictionary&nbsp;of&nbsp;attributes.<br>
+Attributes&nbsp;currently&nbsp;required&nbsp;to&nbsp;be&nbsp;present&nbsp;in&nbsp;the&nbsp;dictionary:<br>
+&nbsp;<br>
+&nbsp;&nbsp;model_name&nbsp;(string):&nbsp;a&nbsp;platform-dependent&nbsp;string<br>
+&nbsp;&nbsp;&nbsp;&nbsp;describing&nbsp;the&nbsp;model&nbsp;of&nbsp;machine,&nbsp;or&nbsp;the&nbsp;empty&nbsp;string&nbsp;if&nbsp;not<br>
+&nbsp;&nbsp;&nbsp;&nbsp;supported.<br>
+&nbsp;&nbsp;gpu&nbsp;(<a href="__builtin__.html#object">object</a>&nbsp;containing&nbsp;GPUInfo's&nbsp;required&nbsp;attributes)</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.platform.system_info.html#SystemInfo">telemetry.internal.platform.system_info.SystemInfo</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>gpu</strong></dt>
+<dd><tt>A&nbsp;GPUInfo&nbsp;object&nbsp;describing&nbsp;the&nbsp;graphics&nbsp;processor(s)&nbsp;on&nbsp;the&nbsp;system.</tt></dd>
+</dl>
+<dl><dt><strong>model_name</strong></dt>
+<dd><tt>A&nbsp;string&nbsp;describing&nbsp;the&nbsp;machine&nbsp;model.<br>
+&nbsp;<br>
+This&nbsp;is&nbsp;a&nbsp;highly&nbsp;platform-dependent&nbsp;value&nbsp;and&nbsp;not&nbsp;currently<br>
+specified&nbsp;for&nbsp;any&nbsp;machine&nbsp;type&nbsp;aside&nbsp;from&nbsp;Macs.&nbsp;On&nbsp;Mac&nbsp;OS,&nbsp;this<br>
+is&nbsp;the&nbsp;model&nbsp;identifier,&nbsp;reformatted&nbsp;slightly;&nbsp;for&nbsp;example,<br>
+'MacBookPro&nbsp;10.1'.</tt></dd>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-CreateBrowserFinderOptions"><strong>CreateBrowserFinderOptions</strong></a>(browser_type<font color="#909090">=None</font>)</dt><dd><tt>Creates&nbsp;fake&nbsp;browser&nbsp;finder&nbsp;options&nbsp;for&nbsp;discovering&nbsp;a&nbsp;browser.</tt></dd></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.gtest_progress_reporter.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.gtest_progress_reporter.html
new file mode 100644
index 0000000..99b8a57
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.gtest_progress_reporter.html
@@ -0,0 +1,90 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.testing.gtest_progress_reporter</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.testing.html"><font color="#ffffff">testing</font></a>.gtest_progress_reporter</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/testing/gtest_progress_reporter.py">telemetry/testing/gtest_progress_reporter.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.util.exception_formatter.html">telemetry.internal.util.exception_formatter</a><br>
+<a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top><a href="telemetry.testing.progress_reporter.html">telemetry.testing.progress_reporter</a><br>
+<a href="time.html">time</a><br>
+</td><td width="25%" valign=top><a href="unittest.html">unittest</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.testing.progress_reporter.html#ProgressReporter">telemetry.testing.progress_reporter.ProgressReporter</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.testing.gtest_progress_reporter.html#GTestProgressReporter">GTestProgressReporter</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="GTestProgressReporter">class <strong>GTestProgressReporter</strong></a>(<a href="telemetry.testing.progress_reporter.html#ProgressReporter">telemetry.testing.progress_reporter.ProgressReporter</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.testing.gtest_progress_reporter.html#GTestProgressReporter">GTestProgressReporter</a></dd>
+<dd><a href="telemetry.testing.progress_reporter.html#ProgressReporter">telemetry.testing.progress_reporter.ProgressReporter</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="GTestProgressReporter-Error"><strong>Error</strong></a>(self, test, err)</dt></dl>
+
+<dl><dt><a name="GTestProgressReporter-Failure"><strong>Failure</strong></a>(self, test, err)</dt></dl>
+
+<dl><dt><a name="GTestProgressReporter-Skip"><strong>Skip</strong></a>(self, test, reason)</dt></dl>
+
+<dl><dt><a name="GTestProgressReporter-StartTest"><strong>StartTest</strong></a>(self, test)</dt></dl>
+
+<dl><dt><a name="GTestProgressReporter-StartTestSuite"><strong>StartTestSuite</strong></a>(self, suite)</dt></dl>
+
+<dl><dt><a name="GTestProgressReporter-StopTestRun"><strong>StopTestRun</strong></a>(self, result)</dt></dl>
+
+<dl><dt><a name="GTestProgressReporter-StopTestSuite"><strong>StopTestSuite</strong></a>(self, suite)</dt></dl>
+
+<dl><dt><a name="GTestProgressReporter-Success"><strong>Success</strong></a>(self, test)</dt></dl>
+
+<dl><dt><a name="GTestProgressReporter-__init__"><strong>__init__</strong></a>(self, output_stream)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.testing.progress_reporter.html#ProgressReporter">telemetry.testing.progress_reporter.ProgressReporter</a>:<br>
+<dl><dt><a name="GTestProgressReporter-StartTestRun"><strong>StartTestRun</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="GTestProgressReporter-StopTest"><strong>StopTest</strong></a>(self, test)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.testing.progress_reporter.html#ProgressReporter">telemetry.testing.progress_reporter.ProgressReporter</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.html
new file mode 100644
index 0000000..e27124c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.html
@@ -0,0 +1,47 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: package telemetry.testing</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.testing</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/testing/__init__.py">telemetry/testing/__init__.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Package Contents</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.testing.browser_test_case.html">browser_test_case</a><br>
+<a href="telemetry.testing.decorators_unittest.html">decorators_unittest</a><br>
+<a href="telemetry.testing.disabled_cases.html">disabled_cases</a><br>
+<a href="telemetry.testing.fakes.html"><strong>fakes</strong>&nbsp;(package)</a><br>
+<a href="telemetry.testing.gtest_progress_reporter.html">gtest_progress_reporter</a><br>
+<a href="telemetry.testing.gtest_progress_reporter_unittest.html">gtest_progress_reporter_unittest</a><br>
+</td><td width="25%" valign=top><a href="telemetry.testing.internal.html"><strong>internal</strong>&nbsp;(package)</a><br>
+<a href="telemetry.testing.options_for_unittests.html">options_for_unittests</a><br>
+<a href="telemetry.testing.page_test_test_case.html">page_test_test_case</a><br>
+<a href="telemetry.testing.progress_reporter.html">progress_reporter</a><br>
+<a href="telemetry.testing.progress_reporter_unittest.html">progress_reporter_unittest</a><br>
+<a href="telemetry.testing.run_chromeos_tests.html">run_chromeos_tests</a><br>
+</td><td width="25%" valign=top><a href="telemetry.testing.run_tests.html">run_tests</a><br>
+<a href="telemetry.testing.run_tests_unittest.html">run_tests_unittest</a><br>
+<a href="telemetry.testing.simple_mock.html">simple_mock</a><br>
+<a href="telemetry.testing.simple_mock_unittest.html">simple_mock_unittest</a><br>
+<a href="telemetry.testing.story_set_smoke_test.html">story_set_smoke_test</a><br>
+<a href="telemetry.testing.stream.html">stream</a><br>
+</td><td width="25%" valign=top><a href="telemetry.testing.system_stub.html">system_stub</a><br>
+<a href="telemetry.testing.system_stub_unittest.html">system_stub_unittest</a><br>
+<a href="telemetry.testing.tab_test_case.html">tab_test_case</a><br>
+<a href="telemetry.testing.test_page_test_results.html">test_page_test_results</a><br>
+<a href="telemetry.testing.unittest_runner.html">unittest_runner</a><br>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.internal.fake_gpu_info.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.internal.fake_gpu_info.html
new file mode 100644
index 0000000..7407598
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.internal.fake_gpu_info.html
@@ -0,0 +1,24 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.testing.internal.fake_gpu_info</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.testing.html"><font color="#ffffff">testing</font></a>.<a href="telemetry.testing.internal.html"><font color="#ffffff">internal</font></a>.fake_gpu_info</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/testing/internal/fake_gpu_info.py">telemetry/testing/internal/fake_gpu_info.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2015&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>FAKE_GPU_INFO</strong> = {'aux_attributes': {'adapter_luid': 0.0, 'amd_switchable': False, 'basic_info_state': 1, 'can_lose_context': False, 'context_info_state': 1, 'direct_rendering': True, 'driver_date': '', 'driver_vendor': 'NVIDIA', 'driver_version': '331.79', 'gl_extensions': 'GL_AMD_multi_draw_indirect GL_ARB_arrays_of_arra..._depth_texture GL_SGIX_shadow GL_SUN_slice_accum ', ...}, 'devices': [{'device_id': 3576.0, 'device_string': '', 'vendor_id': 4318.0, 'vendor_string': ''}], 'driver_bug_workarounds': ['clear_uniforms_before_first_program_use', 'disable_gl_path_rendering', 'init_gl_position_in_vertex_shader', 'init_vertex_attributes', 'remove_pow_with_constant_exponent', 'scalarize_vec_and_mat_constructor_args', 'use_current_program_after_successful_link', 'use_virtualized_gl_contexts'], 'feature_status': {'2d_canvas': 'unavailable_software', 'flash_3d': 'enabled', 'flash_stage3d': 'enabled', 'flash_stage3d_baseline': 'enabled', 'gpu_compositing': 'enabled', 'multiple_raster_threads': 'enabled_on', 'rasterization': 'disabled_software', 'video_decode': 'unavailable_software', 'video_encode': 'enabled', 'webgl': 'enabled'}}</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.internal.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.internal.html
new file mode 100644
index 0000000..df23179
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.internal.html
@@ -0,0 +1,25 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: package telemetry.testing.internal</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.testing.html"><font color="#ffffff">testing</font></a>.internal</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/testing/internal/__init__.py">telemetry/testing/internal/__init__.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2015&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Package Contents</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.testing.internal.fake_gpu_info.html">fake_gpu_info</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.options_for_unittests.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.options_for_unittests.html
new file mode 100644
index 0000000..f07a4df
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.options_for_unittests.html
@@ -0,0 +1,32 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.testing.options_for_unittests</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.testing.html"><font color="#ffffff">testing</font></a>.options_for_unittests</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/testing/options_for_unittests.py">telemetry/testing/options_for_unittests.py</a></font></td></tr></table>
+    <p><tt>This&nbsp;module&nbsp;provides&nbsp;the&nbsp;global&nbsp;variable&nbsp;options_for_unittests.<br>
+&nbsp;<br>
+This&nbsp;is&nbsp;set&nbsp;to&nbsp;a&nbsp;BrowserOptions&nbsp;object&nbsp;by&nbsp;the&nbsp;test&nbsp;harness,&nbsp;or&nbsp;None<br>
+if&nbsp;unit&nbsp;tests&nbsp;are&nbsp;not&nbsp;running.<br>
+&nbsp;<br>
+This&nbsp;allows&nbsp;multiple&nbsp;unit&nbsp;tests&nbsp;to&nbsp;use&nbsp;a&nbsp;specific<br>
+browser,&nbsp;in&nbsp;face&nbsp;of&nbsp;multiple&nbsp;options.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-AreSet"><strong>AreSet</strong></a>()</dt></dl>
+ <dl><dt><a name="-GetCopy"><strong>GetCopy</strong></a>()</dt></dl>
+ <dl><dt><a name="-Pop"><strong>Pop</strong></a>()</dt></dl>
+ <dl><dt><a name="-Push"><strong>Push</strong></a>(options)</dt></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.page_test_test_case.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.page_test_test_case.html
new file mode 100644
index 0000000..6cad5ca
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.page_test_test_case.html
@@ -0,0 +1,490 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.testing.page_test_test_case</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.testing.html"><font color="#ffffff">testing</font></a>.page_test_test_case</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/testing/page_test_test_case.py">telemetry/testing/page_test_test_case.py</a></font></td></tr></table>
+    <p><tt>Provide&nbsp;a&nbsp;<a href="unittest.case.html#TestCase">TestCase</a>&nbsp;base&nbsp;class&nbsp;for&nbsp;PageTest&nbsp;subclasses'&nbsp;unittests.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.benchmark.html">telemetry.benchmark</a><br>
+<a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+<a href="telemetry.testing.options_for_unittests.html">telemetry.testing.options_for_unittests</a><br>
+</td><td width="25%" valign=top><a href="telemetry.page.page.html">telemetry.page.page</a><br>
+<a href="telemetry.page.page_test.html">telemetry.page.page_test</a><br>
+<a href="telemetry.internal.results.results_options.html">telemetry.internal.results.results_options</a><br>
+</td><td width="25%" valign=top><a href="telemetry.story.html">telemetry.story</a><br>
+<a href="telemetry.internal.story_runner.html">telemetry.internal.story_runner</a><br>
+<a href="unittest.html">unittest</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.util.html">telemetry.core.util</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.benchmark.html#BenchmarkMetadata">telemetry.benchmark.BenchmarkMetadata</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.testing.page_test_test_case.html#EmptyMetadataForTest">EmptyMetadataForTest</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="telemetry.page.html#Page">telemetry.page.Page</a>(<a href="telemetry.story.story.html#Story">telemetry.story.story.Story</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.testing.page_test_test_case.html#BasicTestPage">BasicTestPage</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="unittest.case.html#TestCase">unittest.case.TestCase</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.testing.page_test_test_case.html#PageTestTestCase">PageTestTestCase</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="BasicTestPage">class <strong>BasicTestPage</strong></a>(<a href="telemetry.page.html#Page">telemetry.page.Page</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.testing.page_test_test_case.html#BasicTestPage">BasicTestPage</a></dd>
+<dd><a href="telemetry.page.html#Page">telemetry.page.Page</a></dd>
+<dd><a href="telemetry.story.story.html#Story">telemetry.story.story.Story</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="BasicTestPage-RunPageInteractions"><strong>RunPageInteractions</strong></a>(self, action_runner)</dt></dl>
+
+<dl><dt><a name="BasicTestPage-__init__"><strong>__init__</strong></a>(self, url, story_set, base_dir)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.page.html#Page">telemetry.page.Page</a>:<br>
+<dl><dt><a name="BasicTestPage-AddCustomizeBrowserOptions"><strong>AddCustomizeBrowserOptions</strong></a>(self, options)</dt><dd><tt>Inherit&nbsp;page&nbsp;overrides&nbsp;this&nbsp;to&nbsp;add&nbsp;customized&nbsp;browser&nbsp;options.</tt></dd></dl>
+
+<dl><dt><a name="BasicTestPage-AsDict"><strong>AsDict</strong></a>(self)</dt><dd><tt>Converts&nbsp;a&nbsp;page&nbsp;object&nbsp;to&nbsp;a&nbsp;dict&nbsp;suitable&nbsp;for&nbsp;JSON&nbsp;output.</tt></dd></dl>
+
+<dl><dt><a name="BasicTestPage-GetSyntheticDelayCategories"><strong>GetSyntheticDelayCategories</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="BasicTestPage-Run"><strong>Run</strong></a>(self, shared_state)</dt></dl>
+
+<dl><dt><a name="BasicTestPage-RunNavigateSteps"><strong>RunNavigateSteps</strong></a>(self, action_runner)</dt></dl>
+
+<dl><dt><a name="BasicTestPage-__cmp__"><strong>__cmp__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="BasicTestPage-__lt__"><strong>__lt__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="BasicTestPage-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.page.html#Page">telemetry.page.Page</a>:<br>
+<dl><dt><strong>base_dir</strong></dt>
+</dl>
+<dl><dt><strong>credentials_path</strong></dt>
+</dl>
+<dl><dt><strong>display_name</strong></dt>
+</dl>
+<dl><dt><strong>file_path</strong></dt>
+<dd><tt>Returns&nbsp;the&nbsp;path&nbsp;of&nbsp;the&nbsp;file,&nbsp;stripping&nbsp;the&nbsp;scheme&nbsp;and&nbsp;query&nbsp;string.</tt></dd>
+</dl>
+<dl><dt><strong>file_path_url</strong></dt>
+<dd><tt>Returns&nbsp;the&nbsp;file&nbsp;path,&nbsp;including&nbsp;the&nbsp;params,&nbsp;query,&nbsp;and&nbsp;fragment.</tt></dd>
+</dl>
+<dl><dt><strong>file_path_url_with_scheme</strong></dt>
+</dl>
+<dl><dt><strong>is_file</strong></dt>
+<dd><tt>Returns&nbsp;True&nbsp;iff&nbsp;this&nbsp;URL&nbsp;points&nbsp;to&nbsp;a&nbsp;file.</tt></dd>
+</dl>
+<dl><dt><strong>page_set</strong></dt>
+</dl>
+<dl><dt><strong>serving_dir</strong></dt>
+</dl>
+<dl><dt><strong>startup_url</strong></dt>
+</dl>
+<dl><dt><strong>story_set</strong></dt>
+</dl>
+<dl><dt><strong>url</strong></dt>
+</dl>
+<hr>
+Data descriptors inherited from <a href="telemetry.story.story.html#Story">telemetry.story.story.Story</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>file_safe_name</strong></dt>
+<dd><tt>A&nbsp;version&nbsp;of&nbsp;display_name&nbsp;that's&nbsp;safe&nbsp;to&nbsp;use&nbsp;as&nbsp;a&nbsp;filename.<br>
+&nbsp;<br>
+The&nbsp;default&nbsp;implementation&nbsp;sanitizes&nbsp;special&nbsp;characters&nbsp;with&nbsp;underscores,<br>
+but&nbsp;it's&nbsp;okay&nbsp;to&nbsp;override&nbsp;it&nbsp;with&nbsp;a&nbsp;more&nbsp;specific&nbsp;implementation&nbsp;in<br>
+subclasses.</tt></dd>
+</dl>
+<dl><dt><strong>id</strong></dt>
+</dl>
+<dl><dt><strong>is_local</strong></dt>
+<dd><tt>Returns&nbsp;True&nbsp;iff&nbsp;this&nbsp;story&nbsp;does&nbsp;not&nbsp;require&nbsp;network.</tt></dd>
+</dl>
+<dl><dt><strong>labels</strong></dt>
+</dl>
+<dl><dt><strong>make_javascript_deterministic</strong></dt>
+</dl>
+<dl><dt><strong>name</strong></dt>
+</dl>
+<dl><dt><strong>shared_state_class</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="EmptyMetadataForTest">class <strong>EmptyMetadataForTest</strong></a>(<a href="telemetry.benchmark.html#BenchmarkMetadata">telemetry.benchmark.BenchmarkMetadata</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.testing.page_test_test_case.html#EmptyMetadataForTest">EmptyMetadataForTest</a></dd>
+<dd><a href="telemetry.benchmark.html#BenchmarkMetadata">telemetry.benchmark.BenchmarkMetadata</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="EmptyMetadataForTest-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.benchmark.html#BenchmarkMetadata">telemetry.benchmark.BenchmarkMetadata</a>:<br>
+<dl><dt><a name="EmptyMetadataForTest-AsDict"><strong>AsDict</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.benchmark.html#BenchmarkMetadata">telemetry.benchmark.BenchmarkMetadata</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>description</strong></dt>
+</dl>
+<dl><dt><strong>name</strong></dt>
+</dl>
+<dl><dt><strong>rerun_options</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="PageTestTestCase">class <strong>PageTestTestCase</strong></a>(<a href="unittest.case.html#TestCase">unittest.case.TestCase</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;base&nbsp;class&nbsp;to&nbsp;simplify&nbsp;writing&nbsp;unit&nbsp;tests&nbsp;for&nbsp;PageTest&nbsp;subclasses.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.testing.page_test_test_case.html#PageTestTestCase">PageTestTestCase</a></dd>
+<dd><a href="unittest.case.html#TestCase">unittest.case.TestCase</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="PageTestTestCase-CreateEmptyPageSet"><strong>CreateEmptyPageSet</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PageTestTestCase-CreateStorySetFromFileInUnittestDataDir"><strong>CreateStorySetFromFileInUnittestDataDir</strong></a>(self, test_filename)</dt></dl>
+
+<dl><dt><a name="PageTestTestCase-RunMeasurement"><strong>RunMeasurement</strong></a>(self, measurement, ps, options<font color="#909090">=None</font>)</dt><dd><tt>Runs&nbsp;a&nbsp;measurement&nbsp;against&nbsp;a&nbsp;pageset,&nbsp;returning&nbsp;the&nbsp;rows&nbsp;its&nbsp;outputs.</tt></dd></dl>
+
+<dl><dt><a name="PageTestTestCase-TestTracingCleanedUp"><strong>TestTracingCleanedUp</strong></a>(self, measurement_class, options<font color="#909090">=None</font>)</dt></dl>
+
+<hr>
+Methods inherited from <a href="unittest.case.html#TestCase">unittest.case.TestCase</a>:<br>
+<dl><dt><a name="PageTestTestCase-__call__"><strong>__call__</strong></a>(self, *args, **kwds)</dt></dl>
+
+<dl><dt><a name="PageTestTestCase-__eq__"><strong>__eq__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="PageTestTestCase-__hash__"><strong>__hash__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PageTestTestCase-__init__"><strong>__init__</strong></a>(self, methodName<font color="#909090">='runTest'</font>)</dt><dd><tt>Create&nbsp;an&nbsp;instance&nbsp;of&nbsp;the&nbsp;class&nbsp;that&nbsp;will&nbsp;use&nbsp;the&nbsp;named&nbsp;test<br>
+method&nbsp;when&nbsp;executed.&nbsp;Raises&nbsp;a&nbsp;ValueError&nbsp;if&nbsp;the&nbsp;instance&nbsp;does<br>
+not&nbsp;have&nbsp;a&nbsp;method&nbsp;with&nbsp;the&nbsp;specified&nbsp;name.</tt></dd></dl>
+
+<dl><dt><a name="PageTestTestCase-__ne__"><strong>__ne__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="PageTestTestCase-__repr__"><strong>__repr__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PageTestTestCase-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PageTestTestCase-addCleanup"><strong>addCleanup</strong></a>(self, function, *args, **kwargs)</dt><dd><tt>Add&nbsp;a&nbsp;function,&nbsp;with&nbsp;arguments,&nbsp;to&nbsp;be&nbsp;called&nbsp;when&nbsp;the&nbsp;test&nbsp;is<br>
+completed.&nbsp;Functions&nbsp;added&nbsp;are&nbsp;called&nbsp;on&nbsp;a&nbsp;LIFO&nbsp;basis&nbsp;and&nbsp;are<br>
+called&nbsp;after&nbsp;tearDown&nbsp;on&nbsp;test&nbsp;failure&nbsp;or&nbsp;success.<br>
+&nbsp;<br>
+Cleanup&nbsp;items&nbsp;are&nbsp;called&nbsp;even&nbsp;if&nbsp;setUp&nbsp;fails&nbsp;(unlike&nbsp;tearDown).</tt></dd></dl>
+
+<dl><dt><a name="PageTestTestCase-addTypeEqualityFunc"><strong>addTypeEqualityFunc</strong></a>(self, typeobj, function)</dt><dd><tt>Add&nbsp;a&nbsp;type&nbsp;specific&nbsp;assertEqual&nbsp;style&nbsp;function&nbsp;to&nbsp;compare&nbsp;a&nbsp;type.<br>
+&nbsp;<br>
+This&nbsp;method&nbsp;is&nbsp;for&nbsp;use&nbsp;by&nbsp;<a href="unittest.case.html#TestCase">TestCase</a>&nbsp;subclasses&nbsp;that&nbsp;need&nbsp;to&nbsp;register<br>
+their&nbsp;own&nbsp;type&nbsp;equality&nbsp;functions&nbsp;to&nbsp;provide&nbsp;nicer&nbsp;error&nbsp;messages.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;typeobj:&nbsp;The&nbsp;data&nbsp;type&nbsp;to&nbsp;call&nbsp;this&nbsp;function&nbsp;on&nbsp;when&nbsp;both&nbsp;values<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;are&nbsp;of&nbsp;the&nbsp;same&nbsp;type&nbsp;in&nbsp;<a href="#PageTestTestCase-assertEqual">assertEqual</a>().<br>
+&nbsp;&nbsp;&nbsp;&nbsp;function:&nbsp;The&nbsp;callable&nbsp;taking&nbsp;two&nbsp;arguments&nbsp;and&nbsp;an&nbsp;optional<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;msg=&nbsp;argument&nbsp;that&nbsp;raises&nbsp;self.<strong>failureException</strong>&nbsp;with&nbsp;a<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;useful&nbsp;error&nbsp;message&nbsp;when&nbsp;the&nbsp;two&nbsp;arguments&nbsp;are&nbsp;not&nbsp;equal.</tt></dd></dl>
+
+<dl><dt><a name="PageTestTestCase-assertAlmostEqual"><strong>assertAlmostEqual</strong></a>(self, first, second, places<font color="#909090">=None</font>, msg<font color="#909090">=None</font>, delta<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;unequal&nbsp;as&nbsp;determined&nbsp;by&nbsp;their<br>
+difference&nbsp;rounded&nbsp;to&nbsp;the&nbsp;given&nbsp;number&nbsp;of&nbsp;decimal&nbsp;places<br>
+(default&nbsp;7)&nbsp;and&nbsp;comparing&nbsp;to&nbsp;zero,&nbsp;or&nbsp;by&nbsp;comparing&nbsp;that&nbsp;the<br>
+between&nbsp;the&nbsp;two&nbsp;objects&nbsp;is&nbsp;more&nbsp;than&nbsp;the&nbsp;given&nbsp;delta.<br>
+&nbsp;<br>
+Note&nbsp;that&nbsp;decimal&nbsp;places&nbsp;(from&nbsp;zero)&nbsp;are&nbsp;usually&nbsp;not&nbsp;the&nbsp;same<br>
+as&nbsp;significant&nbsp;digits&nbsp;(measured&nbsp;from&nbsp;the&nbsp;most&nbsp;signficant&nbsp;digit).<br>
+&nbsp;<br>
+If&nbsp;the&nbsp;two&nbsp;objects&nbsp;compare&nbsp;equal&nbsp;then&nbsp;they&nbsp;will&nbsp;automatically<br>
+compare&nbsp;almost&nbsp;equal.</tt></dd></dl>
+
+<dl><dt><a name="PageTestTestCase-assertAlmostEquals"><strong>assertAlmostEquals</strong></a> = assertAlmostEqual(self, first, second, places<font color="#909090">=None</font>, msg<font color="#909090">=None</font>, delta<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;unequal&nbsp;as&nbsp;determined&nbsp;by&nbsp;their<br>
+difference&nbsp;rounded&nbsp;to&nbsp;the&nbsp;given&nbsp;number&nbsp;of&nbsp;decimal&nbsp;places<br>
+(default&nbsp;7)&nbsp;and&nbsp;comparing&nbsp;to&nbsp;zero,&nbsp;or&nbsp;by&nbsp;comparing&nbsp;that&nbsp;the<br>
+between&nbsp;the&nbsp;two&nbsp;objects&nbsp;is&nbsp;more&nbsp;than&nbsp;the&nbsp;given&nbsp;delta.<br>
+&nbsp;<br>
+Note&nbsp;that&nbsp;decimal&nbsp;places&nbsp;(from&nbsp;zero)&nbsp;are&nbsp;usually&nbsp;not&nbsp;the&nbsp;same<br>
+as&nbsp;significant&nbsp;digits&nbsp;(measured&nbsp;from&nbsp;the&nbsp;most&nbsp;signficant&nbsp;digit).<br>
+&nbsp;<br>
+If&nbsp;the&nbsp;two&nbsp;objects&nbsp;compare&nbsp;equal&nbsp;then&nbsp;they&nbsp;will&nbsp;automatically<br>
+compare&nbsp;almost&nbsp;equal.</tt></dd></dl>
+
+<dl><dt><a name="PageTestTestCase-assertDictContainsSubset"><strong>assertDictContainsSubset</strong></a>(self, expected, actual, msg<font color="#909090">=None</font>)</dt><dd><tt>Checks&nbsp;whether&nbsp;actual&nbsp;is&nbsp;a&nbsp;superset&nbsp;of&nbsp;expected.</tt></dd></dl>
+
+<dl><dt><a name="PageTestTestCase-assertDictEqual"><strong>assertDictEqual</strong></a>(self, d1, d2, msg<font color="#909090">=None</font>)</dt></dl>
+
+<dl><dt><a name="PageTestTestCase-assertEqual"><strong>assertEqual</strong></a>(self, first, second, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;unequal&nbsp;as&nbsp;determined&nbsp;by&nbsp;the&nbsp;'=='<br>
+operator.</tt></dd></dl>
+
+<dl><dt><a name="PageTestTestCase-assertEquals"><strong>assertEquals</strong></a> = assertEqual(self, first, second, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;unequal&nbsp;as&nbsp;determined&nbsp;by&nbsp;the&nbsp;'=='<br>
+operator.</tt></dd></dl>
+
+<dl><dt><a name="PageTestTestCase-assertFalse"><strong>assertFalse</strong></a>(self, expr, msg<font color="#909090">=None</font>)</dt><dd><tt>Check&nbsp;that&nbsp;the&nbsp;expression&nbsp;is&nbsp;false.</tt></dd></dl>
+
+<dl><dt><a name="PageTestTestCase-assertGreater"><strong>assertGreater</strong></a>(self, a, b, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#PageTestTestCase-assertTrue">assertTrue</a>(a&nbsp;&gt;&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="PageTestTestCase-assertGreaterEqual"><strong>assertGreaterEqual</strong></a>(self, a, b, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#PageTestTestCase-assertTrue">assertTrue</a>(a&nbsp;&gt;=&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="PageTestTestCase-assertIn"><strong>assertIn</strong></a>(self, member, container, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#PageTestTestCase-assertTrue">assertTrue</a>(a&nbsp;in&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="PageTestTestCase-assertIs"><strong>assertIs</strong></a>(self, expr1, expr2, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#PageTestTestCase-assertTrue">assertTrue</a>(a&nbsp;is&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="PageTestTestCase-assertIsInstance"><strong>assertIsInstance</strong></a>(self, obj, cls, msg<font color="#909090">=None</font>)</dt><dd><tt>Same&nbsp;as&nbsp;<a href="#PageTestTestCase-assertTrue">assertTrue</a>(isinstance(obj,&nbsp;cls)),&nbsp;with&nbsp;a&nbsp;nicer<br>
+default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="PageTestTestCase-assertIsNone"><strong>assertIsNone</strong></a>(self, obj, msg<font color="#909090">=None</font>)</dt><dd><tt>Same&nbsp;as&nbsp;<a href="#PageTestTestCase-assertTrue">assertTrue</a>(obj&nbsp;is&nbsp;None),&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="PageTestTestCase-assertIsNot"><strong>assertIsNot</strong></a>(self, expr1, expr2, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#PageTestTestCase-assertTrue">assertTrue</a>(a&nbsp;is&nbsp;not&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="PageTestTestCase-assertIsNotNone"><strong>assertIsNotNone</strong></a>(self, obj, msg<font color="#909090">=None</font>)</dt><dd><tt>Included&nbsp;for&nbsp;symmetry&nbsp;with&nbsp;assertIsNone.</tt></dd></dl>
+
+<dl><dt><a name="PageTestTestCase-assertItemsEqual"><strong>assertItemsEqual</strong></a>(self, expected_seq, actual_seq, msg<font color="#909090">=None</font>)</dt><dd><tt>An&nbsp;unordered&nbsp;sequence&nbsp;specific&nbsp;comparison.&nbsp;It&nbsp;asserts&nbsp;that<br>
+actual_seq&nbsp;and&nbsp;expected_seq&nbsp;have&nbsp;the&nbsp;same&nbsp;element&nbsp;counts.<br>
+Equivalent&nbsp;to::<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#PageTestTestCase-assertEqual">assertEqual</a>(Counter(iter(actual_seq)),<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Counter(iter(expected_seq)))<br>
+&nbsp;<br>
+Asserts&nbsp;that&nbsp;each&nbsp;element&nbsp;has&nbsp;the&nbsp;same&nbsp;count&nbsp;in&nbsp;both&nbsp;sequences.<br>
+Example:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;[0,&nbsp;1,&nbsp;1]&nbsp;and&nbsp;[1,&nbsp;0,&nbsp;1]&nbsp;compare&nbsp;equal.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;[0,&nbsp;0,&nbsp;1]&nbsp;and&nbsp;[0,&nbsp;1]&nbsp;compare&nbsp;unequal.</tt></dd></dl>
+
+<dl><dt><a name="PageTestTestCase-assertLess"><strong>assertLess</strong></a>(self, a, b, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#PageTestTestCase-assertTrue">assertTrue</a>(a&nbsp;&lt;&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="PageTestTestCase-assertLessEqual"><strong>assertLessEqual</strong></a>(self, a, b, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#PageTestTestCase-assertTrue">assertTrue</a>(a&nbsp;&lt;=&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="PageTestTestCase-assertListEqual"><strong>assertListEqual</strong></a>(self, list1, list2, msg<font color="#909090">=None</font>)</dt><dd><tt>A&nbsp;list-specific&nbsp;equality&nbsp;assertion.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;list1:&nbsp;The&nbsp;first&nbsp;list&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;list2:&nbsp;The&nbsp;second&nbsp;list&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;msg:&nbsp;Optional&nbsp;message&nbsp;to&nbsp;use&nbsp;on&nbsp;failure&nbsp;instead&nbsp;of&nbsp;a&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;differences.</tt></dd></dl>
+
+<dl><dt><a name="PageTestTestCase-assertMultiLineEqual"><strong>assertMultiLineEqual</strong></a>(self, first, second, msg<font color="#909090">=None</font>)</dt><dd><tt>Assert&nbsp;that&nbsp;two&nbsp;multi-line&nbsp;strings&nbsp;are&nbsp;equal.</tt></dd></dl>
+
+<dl><dt><a name="PageTestTestCase-assertNotAlmostEqual"><strong>assertNotAlmostEqual</strong></a>(self, first, second, places<font color="#909090">=None</font>, msg<font color="#909090">=None</font>, delta<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;equal&nbsp;as&nbsp;determined&nbsp;by&nbsp;their<br>
+difference&nbsp;rounded&nbsp;to&nbsp;the&nbsp;given&nbsp;number&nbsp;of&nbsp;decimal&nbsp;places<br>
+(default&nbsp;7)&nbsp;and&nbsp;comparing&nbsp;to&nbsp;zero,&nbsp;or&nbsp;by&nbsp;comparing&nbsp;that&nbsp;the<br>
+between&nbsp;the&nbsp;two&nbsp;objects&nbsp;is&nbsp;less&nbsp;than&nbsp;the&nbsp;given&nbsp;delta.<br>
+&nbsp;<br>
+Note&nbsp;that&nbsp;decimal&nbsp;places&nbsp;(from&nbsp;zero)&nbsp;are&nbsp;usually&nbsp;not&nbsp;the&nbsp;same<br>
+as&nbsp;significant&nbsp;digits&nbsp;(measured&nbsp;from&nbsp;the&nbsp;most&nbsp;signficant&nbsp;digit).<br>
+&nbsp;<br>
+Objects&nbsp;that&nbsp;are&nbsp;equal&nbsp;automatically&nbsp;fail.</tt></dd></dl>
+
+<dl><dt><a name="PageTestTestCase-assertNotAlmostEquals"><strong>assertNotAlmostEquals</strong></a> = assertNotAlmostEqual(self, first, second, places<font color="#909090">=None</font>, msg<font color="#909090">=None</font>, delta<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;equal&nbsp;as&nbsp;determined&nbsp;by&nbsp;their<br>
+difference&nbsp;rounded&nbsp;to&nbsp;the&nbsp;given&nbsp;number&nbsp;of&nbsp;decimal&nbsp;places<br>
+(default&nbsp;7)&nbsp;and&nbsp;comparing&nbsp;to&nbsp;zero,&nbsp;or&nbsp;by&nbsp;comparing&nbsp;that&nbsp;the<br>
+between&nbsp;the&nbsp;two&nbsp;objects&nbsp;is&nbsp;less&nbsp;than&nbsp;the&nbsp;given&nbsp;delta.<br>
+&nbsp;<br>
+Note&nbsp;that&nbsp;decimal&nbsp;places&nbsp;(from&nbsp;zero)&nbsp;are&nbsp;usually&nbsp;not&nbsp;the&nbsp;same<br>
+as&nbsp;significant&nbsp;digits&nbsp;(measured&nbsp;from&nbsp;the&nbsp;most&nbsp;signficant&nbsp;digit).<br>
+&nbsp;<br>
+Objects&nbsp;that&nbsp;are&nbsp;equal&nbsp;automatically&nbsp;fail.</tt></dd></dl>
+
+<dl><dt><a name="PageTestTestCase-assertNotEqual"><strong>assertNotEqual</strong></a>(self, first, second, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;equal&nbsp;as&nbsp;determined&nbsp;by&nbsp;the&nbsp;'!='<br>
+operator.</tt></dd></dl>
+
+<dl><dt><a name="PageTestTestCase-assertNotEquals"><strong>assertNotEquals</strong></a> = assertNotEqual(self, first, second, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;equal&nbsp;as&nbsp;determined&nbsp;by&nbsp;the&nbsp;'!='<br>
+operator.</tt></dd></dl>
+
+<dl><dt><a name="PageTestTestCase-assertNotIn"><strong>assertNotIn</strong></a>(self, member, container, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#PageTestTestCase-assertTrue">assertTrue</a>(a&nbsp;not&nbsp;in&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="PageTestTestCase-assertNotIsInstance"><strong>assertNotIsInstance</strong></a>(self, obj, cls, msg<font color="#909090">=None</font>)</dt><dd><tt>Included&nbsp;for&nbsp;symmetry&nbsp;with&nbsp;assertIsInstance.</tt></dd></dl>
+
+<dl><dt><a name="PageTestTestCase-assertNotRegexpMatches"><strong>assertNotRegexpMatches</strong></a>(self, text, unexpected_regexp, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;the&nbsp;test&nbsp;if&nbsp;the&nbsp;text&nbsp;matches&nbsp;the&nbsp;regular&nbsp;expression.</tt></dd></dl>
+
+<dl><dt><a name="PageTestTestCase-assertRaises"><strong>assertRaises</strong></a>(self, excClass, callableObj<font color="#909090">=None</font>, *args, **kwargs)</dt><dd><tt>Fail&nbsp;unless&nbsp;an&nbsp;exception&nbsp;of&nbsp;class&nbsp;excClass&nbsp;is&nbsp;raised<br>
+by&nbsp;callableObj&nbsp;when&nbsp;invoked&nbsp;with&nbsp;arguments&nbsp;args&nbsp;and&nbsp;keyword<br>
+arguments&nbsp;kwargs.&nbsp;If&nbsp;a&nbsp;different&nbsp;type&nbsp;of&nbsp;exception&nbsp;is<br>
+raised,&nbsp;it&nbsp;will&nbsp;not&nbsp;be&nbsp;caught,&nbsp;and&nbsp;the&nbsp;test&nbsp;case&nbsp;will&nbsp;be<br>
+deemed&nbsp;to&nbsp;have&nbsp;suffered&nbsp;an&nbsp;error,&nbsp;exactly&nbsp;as&nbsp;for&nbsp;an<br>
+unexpected&nbsp;exception.<br>
+&nbsp;<br>
+If&nbsp;called&nbsp;with&nbsp;callableObj&nbsp;omitted&nbsp;or&nbsp;None,&nbsp;will&nbsp;return&nbsp;a<br>
+context&nbsp;object&nbsp;used&nbsp;like&nbsp;this::<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;with&nbsp;<a href="#PageTestTestCase-assertRaises">assertRaises</a>(SomeException):<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;do_something()<br>
+&nbsp;<br>
+The&nbsp;context&nbsp;manager&nbsp;keeps&nbsp;a&nbsp;reference&nbsp;to&nbsp;the&nbsp;exception&nbsp;as<br>
+the&nbsp;'exception'&nbsp;attribute.&nbsp;This&nbsp;allows&nbsp;you&nbsp;to&nbsp;inspect&nbsp;the<br>
+exception&nbsp;after&nbsp;the&nbsp;assertion::<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;with&nbsp;<a href="#PageTestTestCase-assertRaises">assertRaises</a>(SomeException)&nbsp;as&nbsp;cm:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;do_something()<br>
+&nbsp;&nbsp;&nbsp;&nbsp;the_exception&nbsp;=&nbsp;cm.exception<br>
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#PageTestTestCase-assertEqual">assertEqual</a>(the_exception.error_code,&nbsp;3)</tt></dd></dl>
+
+<dl><dt><a name="PageTestTestCase-assertRaisesRegexp"><strong>assertRaisesRegexp</strong></a>(self, expected_exception, expected_regexp, callable_obj<font color="#909090">=None</font>, *args, **kwargs)</dt><dd><tt>Asserts&nbsp;that&nbsp;the&nbsp;message&nbsp;in&nbsp;a&nbsp;raised&nbsp;exception&nbsp;matches&nbsp;a&nbsp;regexp.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;expected_exception:&nbsp;Exception&nbsp;class&nbsp;expected&nbsp;to&nbsp;be&nbsp;raised.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;expected_regexp:&nbsp;Regexp&nbsp;(re&nbsp;pattern&nbsp;object&nbsp;or&nbsp;string)&nbsp;expected<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;to&nbsp;be&nbsp;found&nbsp;in&nbsp;error&nbsp;message.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;callable_obj:&nbsp;Function&nbsp;to&nbsp;be&nbsp;called.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;args:&nbsp;Extra&nbsp;args.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;kwargs:&nbsp;Extra&nbsp;kwargs.</tt></dd></dl>
+
+<dl><dt><a name="PageTestTestCase-assertRegexpMatches"><strong>assertRegexpMatches</strong></a>(self, text, expected_regexp, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;the&nbsp;test&nbsp;unless&nbsp;the&nbsp;text&nbsp;matches&nbsp;the&nbsp;regular&nbsp;expression.</tt></dd></dl>
+
+<dl><dt><a name="PageTestTestCase-assertSequenceEqual"><strong>assertSequenceEqual</strong></a>(self, seq1, seq2, msg<font color="#909090">=None</font>, seq_type<font color="#909090">=None</font>)</dt><dd><tt>An&nbsp;equality&nbsp;assertion&nbsp;for&nbsp;ordered&nbsp;sequences&nbsp;(like&nbsp;lists&nbsp;and&nbsp;tuples).<br>
+&nbsp;<br>
+For&nbsp;the&nbsp;purposes&nbsp;of&nbsp;this&nbsp;function,&nbsp;a&nbsp;valid&nbsp;ordered&nbsp;sequence&nbsp;type&nbsp;is&nbsp;one<br>
+which&nbsp;can&nbsp;be&nbsp;indexed,&nbsp;has&nbsp;a&nbsp;length,&nbsp;and&nbsp;has&nbsp;an&nbsp;equality&nbsp;operator.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;seq1:&nbsp;The&nbsp;first&nbsp;sequence&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;seq2:&nbsp;The&nbsp;second&nbsp;sequence&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;seq_type:&nbsp;The&nbsp;expected&nbsp;datatype&nbsp;of&nbsp;the&nbsp;sequences,&nbsp;or&nbsp;None&nbsp;if&nbsp;no<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;datatype&nbsp;should&nbsp;be&nbsp;enforced.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;msg:&nbsp;Optional&nbsp;message&nbsp;to&nbsp;use&nbsp;on&nbsp;failure&nbsp;instead&nbsp;of&nbsp;a&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;differences.</tt></dd></dl>
+
+<dl><dt><a name="PageTestTestCase-assertSetEqual"><strong>assertSetEqual</strong></a>(self, set1, set2, msg<font color="#909090">=None</font>)</dt><dd><tt>A&nbsp;set-specific&nbsp;equality&nbsp;assertion.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;set1:&nbsp;The&nbsp;first&nbsp;set&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;set2:&nbsp;The&nbsp;second&nbsp;set&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;msg:&nbsp;Optional&nbsp;message&nbsp;to&nbsp;use&nbsp;on&nbsp;failure&nbsp;instead&nbsp;of&nbsp;a&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;differences.<br>
+&nbsp;<br>
+assertSetEqual&nbsp;uses&nbsp;ducktyping&nbsp;to&nbsp;support&nbsp;different&nbsp;types&nbsp;of&nbsp;sets,&nbsp;and<br>
+is&nbsp;optimized&nbsp;for&nbsp;sets&nbsp;specifically&nbsp;(parameters&nbsp;must&nbsp;support&nbsp;a<br>
+difference&nbsp;method).</tt></dd></dl>
+
+<dl><dt><a name="PageTestTestCase-assertTrue"><strong>assertTrue</strong></a>(self, expr, msg<font color="#909090">=None</font>)</dt><dd><tt>Check&nbsp;that&nbsp;the&nbsp;expression&nbsp;is&nbsp;true.</tt></dd></dl>
+
+<dl><dt><a name="PageTestTestCase-assertTupleEqual"><strong>assertTupleEqual</strong></a>(self, tuple1, tuple2, msg<font color="#909090">=None</font>)</dt><dd><tt>A&nbsp;tuple-specific&nbsp;equality&nbsp;assertion.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;tuple1:&nbsp;The&nbsp;first&nbsp;tuple&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;tuple2:&nbsp;The&nbsp;second&nbsp;tuple&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;msg:&nbsp;Optional&nbsp;message&nbsp;to&nbsp;use&nbsp;on&nbsp;failure&nbsp;instead&nbsp;of&nbsp;a&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;differences.</tt></dd></dl>
+
+<dl><dt><a name="PageTestTestCase-assert_"><strong>assert_</strong></a> = assertTrue(self, expr, msg<font color="#909090">=None</font>)</dt><dd><tt>Check&nbsp;that&nbsp;the&nbsp;expression&nbsp;is&nbsp;true.</tt></dd></dl>
+
+<dl><dt><a name="PageTestTestCase-countTestCases"><strong>countTestCases</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PageTestTestCase-debug"><strong>debug</strong></a>(self)</dt><dd><tt>Run&nbsp;the&nbsp;test&nbsp;without&nbsp;collecting&nbsp;errors&nbsp;in&nbsp;a&nbsp;TestResult</tt></dd></dl>
+
+<dl><dt><a name="PageTestTestCase-defaultTestResult"><strong>defaultTestResult</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PageTestTestCase-doCleanups"><strong>doCleanups</strong></a>(self)</dt><dd><tt>Execute&nbsp;all&nbsp;cleanup&nbsp;functions.&nbsp;Normally&nbsp;called&nbsp;for&nbsp;you&nbsp;after<br>
+tearDown.</tt></dd></dl>
+
+<dl><dt><a name="PageTestTestCase-fail"><strong>fail</strong></a>(self, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;immediately,&nbsp;with&nbsp;the&nbsp;given&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="PageTestTestCase-failIf"><strong>failIf</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="PageTestTestCase-failIfAlmostEqual"><strong>failIfAlmostEqual</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="PageTestTestCase-failIfEqual"><strong>failIfEqual</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="PageTestTestCase-failUnless"><strong>failUnless</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="PageTestTestCase-failUnlessAlmostEqual"><strong>failUnlessAlmostEqual</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="PageTestTestCase-failUnlessEqual"><strong>failUnlessEqual</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="PageTestTestCase-failUnlessRaises"><strong>failUnlessRaises</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="PageTestTestCase-id"><strong>id</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="PageTestTestCase-run"><strong>run</strong></a>(self, result<font color="#909090">=None</font>)</dt></dl>
+
+<dl><dt><a name="PageTestTestCase-setUp"><strong>setUp</strong></a>(self)</dt><dd><tt>Hook&nbsp;method&nbsp;for&nbsp;setting&nbsp;up&nbsp;the&nbsp;test&nbsp;fixture&nbsp;before&nbsp;exercising&nbsp;it.</tt></dd></dl>
+
+<dl><dt><a name="PageTestTestCase-shortDescription"><strong>shortDescription</strong></a>(self)</dt><dd><tt>Returns&nbsp;a&nbsp;one-line&nbsp;description&nbsp;of&nbsp;the&nbsp;test,&nbsp;or&nbsp;None&nbsp;if&nbsp;no<br>
+description&nbsp;has&nbsp;been&nbsp;provided.<br>
+&nbsp;<br>
+The&nbsp;default&nbsp;implementation&nbsp;of&nbsp;this&nbsp;method&nbsp;returns&nbsp;the&nbsp;first&nbsp;line&nbsp;of<br>
+the&nbsp;specified&nbsp;test&nbsp;method's&nbsp;docstring.</tt></dd></dl>
+
+<dl><dt><a name="PageTestTestCase-skipTest"><strong>skipTest</strong></a>(self, reason)</dt><dd><tt>Skip&nbsp;this&nbsp;test.</tt></dd></dl>
+
+<dl><dt><a name="PageTestTestCase-tearDown"><strong>tearDown</strong></a>(self)</dt><dd><tt>Hook&nbsp;method&nbsp;for&nbsp;deconstructing&nbsp;the&nbsp;test&nbsp;fixture&nbsp;after&nbsp;testing&nbsp;it.</tt></dd></dl>
+
+<hr>
+Class methods inherited from <a href="unittest.case.html#TestCase">unittest.case.TestCase</a>:<br>
+<dl><dt><a name="PageTestTestCase-setUpClass"><strong>setUpClass</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Hook&nbsp;method&nbsp;for&nbsp;setting&nbsp;up&nbsp;class&nbsp;fixture&nbsp;before&nbsp;running&nbsp;tests&nbsp;in&nbsp;the&nbsp;class.</tt></dd></dl>
+
+<dl><dt><a name="PageTestTestCase-tearDownClass"><strong>tearDownClass</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Hook&nbsp;method&nbsp;for&nbsp;deconstructing&nbsp;the&nbsp;class&nbsp;fixture&nbsp;after&nbsp;running&nbsp;all&nbsp;tests&nbsp;in&nbsp;the&nbsp;class.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="unittest.case.html#TestCase">unittest.case.TestCase</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="unittest.case.html#TestCase">unittest.case.TestCase</a>:<br>
+<dl><dt><strong>failureException</strong> = &lt;type 'exceptions.AssertionError'&gt;<dd><tt>Assertion&nbsp;failed.</tt></dl>
+
+<dl><dt><strong>longMessage</strong> = False</dl>
+
+<dl><dt><strong>maxDiff</strong> = 640</dl>
+
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.progress_reporter.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.progress_reporter.html
new file mode 100644
index 0000000..8f92ace
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.progress_reporter.html
@@ -0,0 +1,229 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.testing.progress_reporter</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.testing.html"><font color="#ffffff">testing</font></a>.progress_reporter</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/testing/progress_reporter.py">telemetry/testing/progress_reporter.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.testing.options_for_unittests.html">telemetry.testing.options_for_unittests</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.util.path.html">telemetry.internal.util.path</a><br>
+</td><td width="25%" valign=top><a href="sys.html">sys</a><br>
+</td><td width="25%" valign=top><a href="unittest.html">unittest</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.testing.progress_reporter.html#ProgressReporter">ProgressReporter</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.testing.progress_reporter.html#TestRunner">TestRunner</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="unittest.result.html#TestResult">unittest.result.TestResult</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.testing.progress_reporter.html#TestResult">TestResult</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="unittest.suite.html#TestSuite">unittest.suite.TestSuite</a>(<a href="unittest.suite.html#BaseTestSuite">unittest.suite.BaseTestSuite</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.testing.progress_reporter.html#TestSuite">TestSuite</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ProgressReporter">class <strong>ProgressReporter</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="ProgressReporter-Error"><strong>Error</strong></a>(self, test, err)</dt></dl>
+
+<dl><dt><a name="ProgressReporter-Failure"><strong>Failure</strong></a>(self, test, err)</dt></dl>
+
+<dl><dt><a name="ProgressReporter-Skip"><strong>Skip</strong></a>(self, test, reason)</dt></dl>
+
+<dl><dt><a name="ProgressReporter-StartTest"><strong>StartTest</strong></a>(self, test)</dt></dl>
+
+<dl><dt><a name="ProgressReporter-StartTestRun"><strong>StartTestRun</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="ProgressReporter-StartTestSuite"><strong>StartTestSuite</strong></a>(self, suite)</dt></dl>
+
+<dl><dt><a name="ProgressReporter-StopTest"><strong>StopTest</strong></a>(self, test)</dt></dl>
+
+<dl><dt><a name="ProgressReporter-StopTestRun"><strong>StopTestRun</strong></a>(self, result)</dt></dl>
+
+<dl><dt><a name="ProgressReporter-StopTestSuite"><strong>StopTestSuite</strong></a>(self, suite)</dt></dl>
+
+<dl><dt><a name="ProgressReporter-Success"><strong>Success</strong></a>(self, test)</dt></dl>
+
+<dl><dt><a name="ProgressReporter-__init__"><strong>__init__</strong></a>(self, output_stream)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TestResult">class <strong>TestResult</strong></a>(<a href="unittest.result.html#TestResult">unittest.result.TestResult</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.testing.progress_reporter.html#TestResult">TestResult</a></dd>
+<dd><a href="unittest.result.html#TestResult">unittest.result.TestResult</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="TestResult-__init__"><strong>__init__</strong></a>(self, progress_reporters)</dt></dl>
+
+<dl><dt><a name="TestResult-addError"><strong>addError</strong></a>(self, test, err)</dt></dl>
+
+<dl><dt><a name="TestResult-addFailure"><strong>addFailure</strong></a>(self, test, err)</dt></dl>
+
+<dl><dt><a name="TestResult-addSkip"><strong>addSkip</strong></a>(self, test, reason)</dt></dl>
+
+<dl><dt><a name="TestResult-addSuccess"><strong>addSuccess</strong></a>(self, test)</dt></dl>
+
+<dl><dt><a name="TestResult-startTest"><strong>startTest</strong></a>(self, test)</dt></dl>
+
+<dl><dt><a name="TestResult-startTestRun"><strong>startTestRun</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TestResult-startTestSuite"><strong>startTestSuite</strong></a>(self, suite)</dt></dl>
+
+<dl><dt><a name="TestResult-stopTest"><strong>stopTest</strong></a>(self, test)</dt></dl>
+
+<dl><dt><a name="TestResult-stopTestRun"><strong>stopTestRun</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TestResult-stopTestSuite"><strong>stopTestSuite</strong></a>(self, suite)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>failures_and_errors</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="unittest.result.html#TestResult">unittest.result.TestResult</a>:<br>
+<dl><dt><a name="TestResult-__repr__"><strong>__repr__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TestResult-addExpectedFailure"><strong>addExpectedFailure</strong></a>(self, test, err)</dt><dd><tt>Called&nbsp;when&nbsp;an&nbsp;expected&nbsp;failure/error&nbsp;occured.</tt></dd></dl>
+
+<dl><dt><a name="TestResult-addUnexpectedSuccess"><strong>addUnexpectedSuccess</strong></a>(self, *args, **kw)</dt><dd><tt>Called&nbsp;when&nbsp;a&nbsp;test&nbsp;was&nbsp;expected&nbsp;to&nbsp;fail,&nbsp;but&nbsp;succeed.</tt></dd></dl>
+
+<dl><dt><a name="TestResult-printErrors"><strong>printErrors</strong></a>(self)</dt><dd><tt>Called&nbsp;by&nbsp;<a href="#TestRunner">TestRunner</a>&nbsp;after&nbsp;test&nbsp;run</tt></dd></dl>
+
+<dl><dt><a name="TestResult-stop"><strong>stop</strong></a>(self)</dt><dd><tt>Indicates&nbsp;that&nbsp;the&nbsp;tests&nbsp;should&nbsp;be&nbsp;aborted</tt></dd></dl>
+
+<dl><dt><a name="TestResult-wasSuccessful"><strong>wasSuccessful</strong></a>(self)</dt><dd><tt>Tells&nbsp;whether&nbsp;or&nbsp;not&nbsp;this&nbsp;result&nbsp;was&nbsp;a&nbsp;success</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="unittest.result.html#TestResult">unittest.result.TestResult</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TestRunner">class <strong>TestRunner</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="TestRunner-run"><strong>run</strong></a>(self, test, progress_reporters, repeat_count, args)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TestSuite">class <strong>TestSuite</strong></a>(<a href="unittest.suite.html#TestSuite">unittest.suite.TestSuite</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt><a href="#TestSuite">TestSuite</a>&nbsp;that&nbsp;can&nbsp;delegate&nbsp;start&nbsp;and&nbsp;stop&nbsp;calls&nbsp;to&nbsp;a&nbsp;<a href="#TestResult">TestResult</a>&nbsp;<a href="__builtin__.html#object">object</a>.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.testing.progress_reporter.html#TestSuite">TestSuite</a></dd>
+<dd><a href="unittest.suite.html#TestSuite">unittest.suite.TestSuite</a></dd>
+<dd><a href="unittest.suite.html#BaseTestSuite">unittest.suite.BaseTestSuite</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="TestSuite-run"><strong>run</strong></a>(self, result)</dt></dl>
+
+<hr>
+Methods inherited from <a href="unittest.suite.html#TestSuite">unittest.suite.TestSuite</a>:<br>
+<dl><dt><a name="TestSuite-debug"><strong>debug</strong></a>(self)</dt><dd><tt>Run&nbsp;the&nbsp;tests&nbsp;without&nbsp;collecting&nbsp;errors&nbsp;in&nbsp;a&nbsp;<a href="#TestResult">TestResult</a></tt></dd></dl>
+
+<hr>
+Methods inherited from <a href="unittest.suite.html#BaseTestSuite">unittest.suite.BaseTestSuite</a>:<br>
+<dl><dt><a name="TestSuite-__call__"><strong>__call__</strong></a>(self, *args, **kwds)</dt></dl>
+
+<dl><dt><a name="TestSuite-__eq__"><strong>__eq__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="TestSuite-__init__"><strong>__init__</strong></a>(self, tests<font color="#909090">=()</font>)</dt></dl>
+
+<dl><dt><a name="TestSuite-__iter__"><strong>__iter__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TestSuite-__ne__"><strong>__ne__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="TestSuite-__repr__"><strong>__repr__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TestSuite-addTest"><strong>addTest</strong></a>(self, test)</dt></dl>
+
+<dl><dt><a name="TestSuite-addTests"><strong>addTests</strong></a>(self, tests)</dt></dl>
+
+<dl><dt><a name="TestSuite-countTestCases"><strong>countTestCases</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="unittest.suite.html#BaseTestSuite">unittest.suite.BaseTestSuite</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="unittest.suite.html#BaseTestSuite">unittest.suite.BaseTestSuite</a>:<br>
+<dl><dt><strong>__hash__</strong> = None</dl>
+
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.run_chromeos_tests.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.run_chromeos_tests.html
new file mode 100644
index 0000000..ad96cd2
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.run_chromeos_tests.html
@@ -0,0 +1,39 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.testing.run_chromeos_tests</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.testing.html"><font color="#ffffff">testing</font></a>.run_chromeos_tests</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/testing/run_chromeos_tests.py">telemetry/testing/run_chromeos_tests.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top><a href="telemetry.testing.run_tests.html">telemetry.testing.run_tests</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-RunChromeOSTests"><strong>RunChromeOSTests</strong></a>(browser_type, tests_to_run)</dt><dd><tt>Run&nbsp;ChromeOS&nbsp;tests.<br>
+Args:<br>
+&nbsp;&nbsp;|browser_type|:&nbsp;string&nbsp;specifies&nbsp;which&nbsp;browser&nbsp;type&nbsp;to&nbsp;use.<br>
+&nbsp;&nbsp;|tests_to_run|:&nbsp;a&nbsp;list&nbsp;of&nbsp;tuples&nbsp;(top_level_dir,&nbsp;unit_tests),&nbsp;whereas<br>
+&nbsp;&nbsp;&nbsp;&nbsp;|top_level_dir|&nbsp;specifies&nbsp;the&nbsp;top&nbsp;level&nbsp;directory&nbsp;for&nbsp;running&nbsp;tests,&nbsp;and<br>
+&nbsp;&nbsp;&nbsp;&nbsp;|unit_tests|&nbsp;is&nbsp;a&nbsp;list&nbsp;of&nbsp;string&nbsp;test&nbsp;names&nbsp;to&nbsp;run.</tt></dd></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.run_tests.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.run_tests.html
new file mode 100644
index 0000000..ab9085d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.run_tests.html
@@ -0,0 +1,112 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.testing.run_tests</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.testing.html"><font color="#ffffff">testing</font></a>.run_tests</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/testing/run_tests.py">telemetry/testing/run_tests.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2012&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.util.binary_manager.html">telemetry.internal.util.binary_manager</a><br>
+<a href="telemetry.internal.browser.browser_finder.html">telemetry.internal.browser.browser_finder</a><br>
+<a href="telemetry.internal.browser.browser_finder_exceptions.html">telemetry.internal.browser.browser_finder_exceptions</a><br>
+<a href="telemetry.internal.browser.browser_options.html">telemetry.internal.browser.browser_options</a><br>
+</td><td width="25%" valign=top><a href="telemetry.testing.browser_test_case.html">telemetry.testing.browser_test_case</a><br>
+<a href="telemetry.internal.util.command_line.html">telemetry.internal.util.command_line</a><br>
+<a href="telemetry.decorators.html">telemetry.decorators</a><br>
+<a href="telemetry.internal.platform.device_finder.html">telemetry.internal.platform.device_finder</a><br>
+</td><td width="25%" valign=top><a href="logging.html">logging</a><br>
+<a href="telemetry.testing.options_for_unittests.html">telemetry.testing.options_for_unittests</a><br>
+<a href="telemetry.core.platform.html">telemetry.core.platform</a><br>
+<a href="telemetry.internal.util.ps_util.html">telemetry.internal.util.ps_util</a><br>
+</td><td width="25%" valign=top><a href="sys.html">sys</a><br>
+<a href="typ.html">typ</a><br>
+<a href="telemetry.core.util.html">telemetry.core.util</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.util.command_line.html#OptparseCommand">telemetry.internal.util.command_line.OptparseCommand</a>(<a href="telemetry.internal.util.command_line.html#Command">telemetry.internal.util.command_line.Command</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.testing.run_tests.html#RunTestsCommand">RunTestsCommand</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="RunTestsCommand">class <strong>RunTestsCommand</strong></a>(<a href="telemetry.internal.util.command_line.html#OptparseCommand">telemetry.internal.util.command_line.OptparseCommand</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Run&nbsp;unit&nbsp;tests<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.testing.run_tests.html#RunTestsCommand">RunTestsCommand</a></dd>
+<dd><a href="telemetry.internal.util.command_line.html#OptparseCommand">telemetry.internal.util.command_line.OptparseCommand</a></dd>
+<dd><a href="telemetry.internal.util.command_line.html#Command">telemetry.internal.util.command_line.Command</a></dd>
+<dd><a href="telemetry.internal.util.command_line.html#ArgumentHandlerMixIn">telemetry.internal.util.command_line.ArgumentHandlerMixIn</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="RunTestsCommand-Run"><strong>Run</strong></a>(self, args)</dt></dl>
+
+<dl><dt><a name="RunTestsCommand-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="RunTestsCommand-AddCommandLineArgs"><strong>AddCommandLineArgs</strong></a>(cls, parser, _)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="RunTestsCommand-CreateParser"><strong>CreateParser</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="RunTestsCommand-ProcessCommandLineArgs"><strong>ProcessCommandLineArgs</strong></a>(cls, parser, args, _)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="RunTestsCommand-main"><strong>main</strong></a>(cls, args<font color="#909090">=None</font>, stream<font color="#909090">=None</font>)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Data and other attributes defined here:<br>
+<dl><dt><strong>usage</strong> = '[test_name ...] [&lt;options&gt;]'</dl>
+
+<hr>
+Class methods inherited from <a href="telemetry.internal.util.command_line.html#Command">telemetry.internal.util.command_line.Command</a>:<br>
+<dl><dt><a name="RunTestsCommand-Description"><strong>Description</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="RunTestsCommand-Name"><strong>Name</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.util.command_line.html#ArgumentHandlerMixIn">telemetry.internal.util.command_line.ArgumentHandlerMixIn</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-GetClassifier"><strong>GetClassifier</strong></a>(args, possible_browser)</dt></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.simple_mock.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.simple_mock.html
new file mode 100644
index 0000000..e3a991f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.simple_mock.html
@@ -0,0 +1,142 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.testing.simple_mock</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.testing.html"><font color="#ffffff">testing</font></a>.simple_mock</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/testing/simple_mock.py">telemetry/testing/simple_mock.py</a></font></td></tr></table>
+    <p><tt>A&nbsp;very&nbsp;very&nbsp;simple&nbsp;mock&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;harness.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.testing.simple_mock.html#MockFunctionCall">MockFunctionCall</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.testing.simple_mock.html#MockObject">MockObject</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.testing.simple_mock.html#MockTimer">MockTimer</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.testing.simple_mock.html#MockTrace">MockTrace</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="MockFunctionCall">class <strong>MockFunctionCall</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="MockFunctionCall-VerifyEquals"><strong>VerifyEquals</strong></a>(self, got)</dt></dl>
+
+<dl><dt><a name="MockFunctionCall-WhenCalled"><strong>WhenCalled</strong></a>(self, handler)</dt></dl>
+
+<dl><dt><a name="MockFunctionCall-WillReturn"><strong>WillReturn</strong></a>(self, value)</dt></dl>
+
+<dl><dt><a name="MockFunctionCall-WithArgs"><strong>WithArgs</strong></a>(self, *args)</dt></dl>
+
+<dl><dt><a name="MockFunctionCall-__init__"><strong>__init__</strong></a>(self, name)</dt></dl>
+
+<dl><dt><a name="MockFunctionCall-__repr__"><strong>__repr__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="MockObject">class <strong>MockObject</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="MockObject-ExpectCall"><strong>ExpectCall</strong></a>(self, func_name, *args)</dt></dl>
+
+<dl><dt><a name="MockObject-SetAttribute"><strong>SetAttribute</strong></a>(self, name, value)</dt></dl>
+
+<dl><dt><a name="MockObject-__init__"><strong>__init__</strong></a>(self, parent_mock<font color="#909090">=None</font>)</dt></dl>
+
+<dl><dt><a name="MockObject-__setattr__"><strong>__setattr__</strong></a>(self, name, value)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="MockTimer">class <strong>MockTimer</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;mock&nbsp;timer&nbsp;to&nbsp;fake&nbsp;out&nbsp;the&nbsp;timing&nbsp;for&nbsp;a&nbsp;module.<br>
+Args:<br>
+&nbsp;&nbsp;module:&nbsp;module&nbsp;to&nbsp;fake&nbsp;out&nbsp;the&nbsp;time<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="MockTimer-Restore"><strong>Restore</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MockTimer-SetTime"><strong>SetTime</strong></a>(self, time)</dt></dl>
+
+<dl><dt><a name="MockTimer-__del__"><strong>__del__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MockTimer-__init__"><strong>__init__</strong></a>(self, module<font color="#909090">=None</font>)</dt></dl>
+
+<dl><dt><a name="MockTimer-sleep"><strong>sleep</strong></a>(self, time)</dt></dl>
+
+<dl><dt><a name="MockTimer-time"><strong>time</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="MockTrace">class <strong>MockTrace</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="MockTrace-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>DONT_CARE</strong> = ''</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.story_set_smoke_test.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.story_set_smoke_test.html
new file mode 100644
index 0000000..22dd8ef
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.story_set_smoke_test.html
@@ -0,0 +1,360 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.testing.story_set_smoke_test</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.testing.html"><font color="#ffffff">testing</font></a>.story_set_smoke_test</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/testing/story_set_smoke_test.py">telemetry/testing/story_set_smoke_test.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.wpr.archive_info.html">telemetry.wpr.archive_info</a><br>
+<a href="telemetry.internal.browser.browser_credentials.html">telemetry.internal.browser.browser_credentials</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.discover.html">telemetry.core.discover</a><br>
+<a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top><a href="os.html">os</a><br>
+<a href="telemetry.page.html">telemetry.page</a><br>
+</td><td width="25%" valign=top><a href="telemetry.story.html">telemetry.story</a><br>
+<a href="unittest.html">unittest</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="unittest.case.html#TestCase">unittest.case.TestCase</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.testing.story_set_smoke_test.html#StorySetSmokeTest">StorySetSmokeTest</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="StorySetSmokeTest">class <strong>StorySetSmokeTest</strong></a>(<a href="unittest.case.html#TestCase">unittest.case.TestCase</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.testing.story_set_smoke_test.html#StorySetSmokeTest">StorySetSmokeTest</a></dd>
+<dd><a href="unittest.case.html#TestCase">unittest.case.TestCase</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="StorySetSmokeTest-CheckArchive"><strong>CheckArchive</strong></a>(self, story_set)</dt><dd><tt>Verify&nbsp;that&nbsp;all&nbsp;URLs&nbsp;of&nbsp;pages&nbsp;in&nbsp;story_set&nbsp;have&nbsp;an&nbsp;associated&nbsp;archive.</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-CheckAttributes"><strong>CheckAttributes</strong></a>(self, story_set)</dt><dd><tt>Verify&nbsp;that&nbsp;story_set&nbsp;and&nbsp;its&nbsp;stories&nbsp;base&nbsp;attributes&nbsp;have&nbsp;the&nbsp;right<br>
+types.</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-CheckAttributesOfStoryBasicAttributes"><strong>CheckAttributesOfStoryBasicAttributes</strong></a>(self, story)</dt></dl>
+
+<dl><dt><a name="StorySetSmokeTest-CheckAttributesOfStorySetBasicAttributes"><strong>CheckAttributesOfStorySetBasicAttributes</strong></a>(self, story_set)</dt></dl>
+
+<dl><dt><a name="StorySetSmokeTest-CheckCredentials"><strong>CheckCredentials</strong></a>(self, story_set)</dt><dd><tt>Verify&nbsp;that&nbsp;all&nbsp;pages&nbsp;in&nbsp;story_set&nbsp;use&nbsp;proper&nbsp;credentials</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-CheckSharedStates"><strong>CheckSharedStates</strong></a>(self, story_set)</dt></dl>
+
+<dl><dt><a name="StorySetSmokeTest-GetAllStorySetClasses"><strong>GetAllStorySetClasses</strong></a>(self, story_sets_dir, top_level_dir)</dt></dl>
+
+<dl><dt><a name="StorySetSmokeTest-RunSmokeTest"><strong>RunSmokeTest</strong></a>(self, story_sets_dir, top_level_dir)</dt><dd><tt>Run&nbsp;smoke&nbsp;test&nbsp;on&nbsp;all&nbsp;story&nbsp;sets&nbsp;in&nbsp;story_sets_dir.<br>
+&nbsp;<br>
+Subclass&nbsp;of&nbsp;<a href="#StorySetSmokeTest">StorySetSmokeTest</a>&nbsp;is&nbsp;supposed&nbsp;to&nbsp;call&nbsp;this&nbsp;in&nbsp;some&nbsp;test<br>
+method&nbsp;to&nbsp;run&nbsp;smoke&nbsp;test.</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-setUp"><strong>setUp</strong></a>(self)</dt></dl>
+
+<hr>
+Methods inherited from <a href="unittest.case.html#TestCase">unittest.case.TestCase</a>:<br>
+<dl><dt><a name="StorySetSmokeTest-__call__"><strong>__call__</strong></a>(self, *args, **kwds)</dt></dl>
+
+<dl><dt><a name="StorySetSmokeTest-__eq__"><strong>__eq__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="StorySetSmokeTest-__hash__"><strong>__hash__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="StorySetSmokeTest-__init__"><strong>__init__</strong></a>(self, methodName<font color="#909090">='runTest'</font>)</dt><dd><tt>Create&nbsp;an&nbsp;instance&nbsp;of&nbsp;the&nbsp;class&nbsp;that&nbsp;will&nbsp;use&nbsp;the&nbsp;named&nbsp;test<br>
+method&nbsp;when&nbsp;executed.&nbsp;Raises&nbsp;a&nbsp;ValueError&nbsp;if&nbsp;the&nbsp;instance&nbsp;does<br>
+not&nbsp;have&nbsp;a&nbsp;method&nbsp;with&nbsp;the&nbsp;specified&nbsp;name.</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-__ne__"><strong>__ne__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="StorySetSmokeTest-__repr__"><strong>__repr__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="StorySetSmokeTest-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="StorySetSmokeTest-addCleanup"><strong>addCleanup</strong></a>(self, function, *args, **kwargs)</dt><dd><tt>Add&nbsp;a&nbsp;function,&nbsp;with&nbsp;arguments,&nbsp;to&nbsp;be&nbsp;called&nbsp;when&nbsp;the&nbsp;test&nbsp;is<br>
+completed.&nbsp;Functions&nbsp;added&nbsp;are&nbsp;called&nbsp;on&nbsp;a&nbsp;LIFO&nbsp;basis&nbsp;and&nbsp;are<br>
+called&nbsp;after&nbsp;tearDown&nbsp;on&nbsp;test&nbsp;failure&nbsp;or&nbsp;success.<br>
+&nbsp;<br>
+Cleanup&nbsp;items&nbsp;are&nbsp;called&nbsp;even&nbsp;if&nbsp;setUp&nbsp;fails&nbsp;(unlike&nbsp;tearDown).</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-addTypeEqualityFunc"><strong>addTypeEqualityFunc</strong></a>(self, typeobj, function)</dt><dd><tt>Add&nbsp;a&nbsp;type&nbsp;specific&nbsp;assertEqual&nbsp;style&nbsp;function&nbsp;to&nbsp;compare&nbsp;a&nbsp;type.<br>
+&nbsp;<br>
+This&nbsp;method&nbsp;is&nbsp;for&nbsp;use&nbsp;by&nbsp;<a href="unittest.case.html#TestCase">TestCase</a>&nbsp;subclasses&nbsp;that&nbsp;need&nbsp;to&nbsp;register<br>
+their&nbsp;own&nbsp;type&nbsp;equality&nbsp;functions&nbsp;to&nbsp;provide&nbsp;nicer&nbsp;error&nbsp;messages.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;typeobj:&nbsp;The&nbsp;data&nbsp;type&nbsp;to&nbsp;call&nbsp;this&nbsp;function&nbsp;on&nbsp;when&nbsp;both&nbsp;values<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;are&nbsp;of&nbsp;the&nbsp;same&nbsp;type&nbsp;in&nbsp;<a href="#StorySetSmokeTest-assertEqual">assertEqual</a>().<br>
+&nbsp;&nbsp;&nbsp;&nbsp;function:&nbsp;The&nbsp;callable&nbsp;taking&nbsp;two&nbsp;arguments&nbsp;and&nbsp;an&nbsp;optional<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;msg=&nbsp;argument&nbsp;that&nbsp;raises&nbsp;self.<strong>failureException</strong>&nbsp;with&nbsp;a<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;useful&nbsp;error&nbsp;message&nbsp;when&nbsp;the&nbsp;two&nbsp;arguments&nbsp;are&nbsp;not&nbsp;equal.</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-assertAlmostEqual"><strong>assertAlmostEqual</strong></a>(self, first, second, places<font color="#909090">=None</font>, msg<font color="#909090">=None</font>, delta<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;unequal&nbsp;as&nbsp;determined&nbsp;by&nbsp;their<br>
+difference&nbsp;rounded&nbsp;to&nbsp;the&nbsp;given&nbsp;number&nbsp;of&nbsp;decimal&nbsp;places<br>
+(default&nbsp;7)&nbsp;and&nbsp;comparing&nbsp;to&nbsp;zero,&nbsp;or&nbsp;by&nbsp;comparing&nbsp;that&nbsp;the<br>
+between&nbsp;the&nbsp;two&nbsp;objects&nbsp;is&nbsp;more&nbsp;than&nbsp;the&nbsp;given&nbsp;delta.<br>
+&nbsp;<br>
+Note&nbsp;that&nbsp;decimal&nbsp;places&nbsp;(from&nbsp;zero)&nbsp;are&nbsp;usually&nbsp;not&nbsp;the&nbsp;same<br>
+as&nbsp;significant&nbsp;digits&nbsp;(measured&nbsp;from&nbsp;the&nbsp;most&nbsp;signficant&nbsp;digit).<br>
+&nbsp;<br>
+If&nbsp;the&nbsp;two&nbsp;objects&nbsp;compare&nbsp;equal&nbsp;then&nbsp;they&nbsp;will&nbsp;automatically<br>
+compare&nbsp;almost&nbsp;equal.</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-assertAlmostEquals"><strong>assertAlmostEquals</strong></a> = assertAlmostEqual(self, first, second, places<font color="#909090">=None</font>, msg<font color="#909090">=None</font>, delta<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;unequal&nbsp;as&nbsp;determined&nbsp;by&nbsp;their<br>
+difference&nbsp;rounded&nbsp;to&nbsp;the&nbsp;given&nbsp;number&nbsp;of&nbsp;decimal&nbsp;places<br>
+(default&nbsp;7)&nbsp;and&nbsp;comparing&nbsp;to&nbsp;zero,&nbsp;or&nbsp;by&nbsp;comparing&nbsp;that&nbsp;the<br>
+between&nbsp;the&nbsp;two&nbsp;objects&nbsp;is&nbsp;more&nbsp;than&nbsp;the&nbsp;given&nbsp;delta.<br>
+&nbsp;<br>
+Note&nbsp;that&nbsp;decimal&nbsp;places&nbsp;(from&nbsp;zero)&nbsp;are&nbsp;usually&nbsp;not&nbsp;the&nbsp;same<br>
+as&nbsp;significant&nbsp;digits&nbsp;(measured&nbsp;from&nbsp;the&nbsp;most&nbsp;signficant&nbsp;digit).<br>
+&nbsp;<br>
+If&nbsp;the&nbsp;two&nbsp;objects&nbsp;compare&nbsp;equal&nbsp;then&nbsp;they&nbsp;will&nbsp;automatically<br>
+compare&nbsp;almost&nbsp;equal.</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-assertDictContainsSubset"><strong>assertDictContainsSubset</strong></a>(self, expected, actual, msg<font color="#909090">=None</font>)</dt><dd><tt>Checks&nbsp;whether&nbsp;actual&nbsp;is&nbsp;a&nbsp;superset&nbsp;of&nbsp;expected.</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-assertDictEqual"><strong>assertDictEqual</strong></a>(self, d1, d2, msg<font color="#909090">=None</font>)</dt></dl>
+
+<dl><dt><a name="StorySetSmokeTest-assertEqual"><strong>assertEqual</strong></a>(self, first, second, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;unequal&nbsp;as&nbsp;determined&nbsp;by&nbsp;the&nbsp;'=='<br>
+operator.</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-assertEquals"><strong>assertEquals</strong></a> = assertEqual(self, first, second, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;unequal&nbsp;as&nbsp;determined&nbsp;by&nbsp;the&nbsp;'=='<br>
+operator.</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-assertFalse"><strong>assertFalse</strong></a>(self, expr, msg<font color="#909090">=None</font>)</dt><dd><tt>Check&nbsp;that&nbsp;the&nbsp;expression&nbsp;is&nbsp;false.</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-assertGreater"><strong>assertGreater</strong></a>(self, a, b, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#StorySetSmokeTest-assertTrue">assertTrue</a>(a&nbsp;&gt;&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-assertGreaterEqual"><strong>assertGreaterEqual</strong></a>(self, a, b, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#StorySetSmokeTest-assertTrue">assertTrue</a>(a&nbsp;&gt;=&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-assertIn"><strong>assertIn</strong></a>(self, member, container, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#StorySetSmokeTest-assertTrue">assertTrue</a>(a&nbsp;in&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-assertIs"><strong>assertIs</strong></a>(self, expr1, expr2, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#StorySetSmokeTest-assertTrue">assertTrue</a>(a&nbsp;is&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-assertIsInstance"><strong>assertIsInstance</strong></a>(self, obj, cls, msg<font color="#909090">=None</font>)</dt><dd><tt>Same&nbsp;as&nbsp;<a href="#StorySetSmokeTest-assertTrue">assertTrue</a>(isinstance(obj,&nbsp;cls)),&nbsp;with&nbsp;a&nbsp;nicer<br>
+default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-assertIsNone"><strong>assertIsNone</strong></a>(self, obj, msg<font color="#909090">=None</font>)</dt><dd><tt>Same&nbsp;as&nbsp;<a href="#StorySetSmokeTest-assertTrue">assertTrue</a>(obj&nbsp;is&nbsp;None),&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-assertIsNot"><strong>assertIsNot</strong></a>(self, expr1, expr2, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#StorySetSmokeTest-assertTrue">assertTrue</a>(a&nbsp;is&nbsp;not&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-assertIsNotNone"><strong>assertIsNotNone</strong></a>(self, obj, msg<font color="#909090">=None</font>)</dt><dd><tt>Included&nbsp;for&nbsp;symmetry&nbsp;with&nbsp;assertIsNone.</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-assertItemsEqual"><strong>assertItemsEqual</strong></a>(self, expected_seq, actual_seq, msg<font color="#909090">=None</font>)</dt><dd><tt>An&nbsp;unordered&nbsp;sequence&nbsp;specific&nbsp;comparison.&nbsp;It&nbsp;asserts&nbsp;that<br>
+actual_seq&nbsp;and&nbsp;expected_seq&nbsp;have&nbsp;the&nbsp;same&nbsp;element&nbsp;counts.<br>
+Equivalent&nbsp;to::<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#StorySetSmokeTest-assertEqual">assertEqual</a>(Counter(iter(actual_seq)),<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Counter(iter(expected_seq)))<br>
+&nbsp;<br>
+Asserts&nbsp;that&nbsp;each&nbsp;element&nbsp;has&nbsp;the&nbsp;same&nbsp;count&nbsp;in&nbsp;both&nbsp;sequences.<br>
+Example:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;[0,&nbsp;1,&nbsp;1]&nbsp;and&nbsp;[1,&nbsp;0,&nbsp;1]&nbsp;compare&nbsp;equal.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;[0,&nbsp;0,&nbsp;1]&nbsp;and&nbsp;[0,&nbsp;1]&nbsp;compare&nbsp;unequal.</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-assertLess"><strong>assertLess</strong></a>(self, a, b, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#StorySetSmokeTest-assertTrue">assertTrue</a>(a&nbsp;&lt;&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-assertLessEqual"><strong>assertLessEqual</strong></a>(self, a, b, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#StorySetSmokeTest-assertTrue">assertTrue</a>(a&nbsp;&lt;=&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-assertListEqual"><strong>assertListEqual</strong></a>(self, list1, list2, msg<font color="#909090">=None</font>)</dt><dd><tt>A&nbsp;list-specific&nbsp;equality&nbsp;assertion.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;list1:&nbsp;The&nbsp;first&nbsp;list&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;list2:&nbsp;The&nbsp;second&nbsp;list&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;msg:&nbsp;Optional&nbsp;message&nbsp;to&nbsp;use&nbsp;on&nbsp;failure&nbsp;instead&nbsp;of&nbsp;a&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;differences.</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-assertMultiLineEqual"><strong>assertMultiLineEqual</strong></a>(self, first, second, msg<font color="#909090">=None</font>)</dt><dd><tt>Assert&nbsp;that&nbsp;two&nbsp;multi-line&nbsp;strings&nbsp;are&nbsp;equal.</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-assertNotAlmostEqual"><strong>assertNotAlmostEqual</strong></a>(self, first, second, places<font color="#909090">=None</font>, msg<font color="#909090">=None</font>, delta<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;equal&nbsp;as&nbsp;determined&nbsp;by&nbsp;their<br>
+difference&nbsp;rounded&nbsp;to&nbsp;the&nbsp;given&nbsp;number&nbsp;of&nbsp;decimal&nbsp;places<br>
+(default&nbsp;7)&nbsp;and&nbsp;comparing&nbsp;to&nbsp;zero,&nbsp;or&nbsp;by&nbsp;comparing&nbsp;that&nbsp;the<br>
+between&nbsp;the&nbsp;two&nbsp;objects&nbsp;is&nbsp;less&nbsp;than&nbsp;the&nbsp;given&nbsp;delta.<br>
+&nbsp;<br>
+Note&nbsp;that&nbsp;decimal&nbsp;places&nbsp;(from&nbsp;zero)&nbsp;are&nbsp;usually&nbsp;not&nbsp;the&nbsp;same<br>
+as&nbsp;significant&nbsp;digits&nbsp;(measured&nbsp;from&nbsp;the&nbsp;most&nbsp;signficant&nbsp;digit).<br>
+&nbsp;<br>
+Objects&nbsp;that&nbsp;are&nbsp;equal&nbsp;automatically&nbsp;fail.</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-assertNotAlmostEquals"><strong>assertNotAlmostEquals</strong></a> = assertNotAlmostEqual(self, first, second, places<font color="#909090">=None</font>, msg<font color="#909090">=None</font>, delta<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;equal&nbsp;as&nbsp;determined&nbsp;by&nbsp;their<br>
+difference&nbsp;rounded&nbsp;to&nbsp;the&nbsp;given&nbsp;number&nbsp;of&nbsp;decimal&nbsp;places<br>
+(default&nbsp;7)&nbsp;and&nbsp;comparing&nbsp;to&nbsp;zero,&nbsp;or&nbsp;by&nbsp;comparing&nbsp;that&nbsp;the<br>
+between&nbsp;the&nbsp;two&nbsp;objects&nbsp;is&nbsp;less&nbsp;than&nbsp;the&nbsp;given&nbsp;delta.<br>
+&nbsp;<br>
+Note&nbsp;that&nbsp;decimal&nbsp;places&nbsp;(from&nbsp;zero)&nbsp;are&nbsp;usually&nbsp;not&nbsp;the&nbsp;same<br>
+as&nbsp;significant&nbsp;digits&nbsp;(measured&nbsp;from&nbsp;the&nbsp;most&nbsp;signficant&nbsp;digit).<br>
+&nbsp;<br>
+Objects&nbsp;that&nbsp;are&nbsp;equal&nbsp;automatically&nbsp;fail.</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-assertNotEqual"><strong>assertNotEqual</strong></a>(self, first, second, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;equal&nbsp;as&nbsp;determined&nbsp;by&nbsp;the&nbsp;'!='<br>
+operator.</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-assertNotEquals"><strong>assertNotEquals</strong></a> = assertNotEqual(self, first, second, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;equal&nbsp;as&nbsp;determined&nbsp;by&nbsp;the&nbsp;'!='<br>
+operator.</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-assertNotIn"><strong>assertNotIn</strong></a>(self, member, container, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#StorySetSmokeTest-assertTrue">assertTrue</a>(a&nbsp;not&nbsp;in&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-assertNotIsInstance"><strong>assertNotIsInstance</strong></a>(self, obj, cls, msg<font color="#909090">=None</font>)</dt><dd><tt>Included&nbsp;for&nbsp;symmetry&nbsp;with&nbsp;assertIsInstance.</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-assertNotRegexpMatches"><strong>assertNotRegexpMatches</strong></a>(self, text, unexpected_regexp, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;the&nbsp;test&nbsp;if&nbsp;the&nbsp;text&nbsp;matches&nbsp;the&nbsp;regular&nbsp;expression.</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-assertRaises"><strong>assertRaises</strong></a>(self, excClass, callableObj<font color="#909090">=None</font>, *args, **kwargs)</dt><dd><tt>Fail&nbsp;unless&nbsp;an&nbsp;exception&nbsp;of&nbsp;class&nbsp;excClass&nbsp;is&nbsp;raised<br>
+by&nbsp;callableObj&nbsp;when&nbsp;invoked&nbsp;with&nbsp;arguments&nbsp;args&nbsp;and&nbsp;keyword<br>
+arguments&nbsp;kwargs.&nbsp;If&nbsp;a&nbsp;different&nbsp;type&nbsp;of&nbsp;exception&nbsp;is<br>
+raised,&nbsp;it&nbsp;will&nbsp;not&nbsp;be&nbsp;caught,&nbsp;and&nbsp;the&nbsp;test&nbsp;case&nbsp;will&nbsp;be<br>
+deemed&nbsp;to&nbsp;have&nbsp;suffered&nbsp;an&nbsp;error,&nbsp;exactly&nbsp;as&nbsp;for&nbsp;an<br>
+unexpected&nbsp;exception.<br>
+&nbsp;<br>
+If&nbsp;called&nbsp;with&nbsp;callableObj&nbsp;omitted&nbsp;or&nbsp;None,&nbsp;will&nbsp;return&nbsp;a<br>
+context&nbsp;object&nbsp;used&nbsp;like&nbsp;this::<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;with&nbsp;<a href="#StorySetSmokeTest-assertRaises">assertRaises</a>(SomeException):<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;do_something()<br>
+&nbsp;<br>
+The&nbsp;context&nbsp;manager&nbsp;keeps&nbsp;a&nbsp;reference&nbsp;to&nbsp;the&nbsp;exception&nbsp;as<br>
+the&nbsp;'exception'&nbsp;attribute.&nbsp;This&nbsp;allows&nbsp;you&nbsp;to&nbsp;inspect&nbsp;the<br>
+exception&nbsp;after&nbsp;the&nbsp;assertion::<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;with&nbsp;<a href="#StorySetSmokeTest-assertRaises">assertRaises</a>(SomeException)&nbsp;as&nbsp;cm:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;do_something()<br>
+&nbsp;&nbsp;&nbsp;&nbsp;the_exception&nbsp;=&nbsp;cm.exception<br>
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#StorySetSmokeTest-assertEqual">assertEqual</a>(the_exception.error_code,&nbsp;3)</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-assertRaisesRegexp"><strong>assertRaisesRegexp</strong></a>(self, expected_exception, expected_regexp, callable_obj<font color="#909090">=None</font>, *args, **kwargs)</dt><dd><tt>Asserts&nbsp;that&nbsp;the&nbsp;message&nbsp;in&nbsp;a&nbsp;raised&nbsp;exception&nbsp;matches&nbsp;a&nbsp;regexp.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;expected_exception:&nbsp;Exception&nbsp;class&nbsp;expected&nbsp;to&nbsp;be&nbsp;raised.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;expected_regexp:&nbsp;Regexp&nbsp;(re&nbsp;pattern&nbsp;object&nbsp;or&nbsp;string)&nbsp;expected<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;to&nbsp;be&nbsp;found&nbsp;in&nbsp;error&nbsp;message.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;callable_obj:&nbsp;Function&nbsp;to&nbsp;be&nbsp;called.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;args:&nbsp;Extra&nbsp;args.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;kwargs:&nbsp;Extra&nbsp;kwargs.</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-assertRegexpMatches"><strong>assertRegexpMatches</strong></a>(self, text, expected_regexp, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;the&nbsp;test&nbsp;unless&nbsp;the&nbsp;text&nbsp;matches&nbsp;the&nbsp;regular&nbsp;expression.</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-assertSequenceEqual"><strong>assertSequenceEqual</strong></a>(self, seq1, seq2, msg<font color="#909090">=None</font>, seq_type<font color="#909090">=None</font>)</dt><dd><tt>An&nbsp;equality&nbsp;assertion&nbsp;for&nbsp;ordered&nbsp;sequences&nbsp;(like&nbsp;lists&nbsp;and&nbsp;tuples).<br>
+&nbsp;<br>
+For&nbsp;the&nbsp;purposes&nbsp;of&nbsp;this&nbsp;function,&nbsp;a&nbsp;valid&nbsp;ordered&nbsp;sequence&nbsp;type&nbsp;is&nbsp;one<br>
+which&nbsp;can&nbsp;be&nbsp;indexed,&nbsp;has&nbsp;a&nbsp;length,&nbsp;and&nbsp;has&nbsp;an&nbsp;equality&nbsp;operator.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;seq1:&nbsp;The&nbsp;first&nbsp;sequence&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;seq2:&nbsp;The&nbsp;second&nbsp;sequence&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;seq_type:&nbsp;The&nbsp;expected&nbsp;datatype&nbsp;of&nbsp;the&nbsp;sequences,&nbsp;or&nbsp;None&nbsp;if&nbsp;no<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;datatype&nbsp;should&nbsp;be&nbsp;enforced.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;msg:&nbsp;Optional&nbsp;message&nbsp;to&nbsp;use&nbsp;on&nbsp;failure&nbsp;instead&nbsp;of&nbsp;a&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;differences.</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-assertSetEqual"><strong>assertSetEqual</strong></a>(self, set1, set2, msg<font color="#909090">=None</font>)</dt><dd><tt>A&nbsp;set-specific&nbsp;equality&nbsp;assertion.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;set1:&nbsp;The&nbsp;first&nbsp;set&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;set2:&nbsp;The&nbsp;second&nbsp;set&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;msg:&nbsp;Optional&nbsp;message&nbsp;to&nbsp;use&nbsp;on&nbsp;failure&nbsp;instead&nbsp;of&nbsp;a&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;differences.<br>
+&nbsp;<br>
+assertSetEqual&nbsp;uses&nbsp;ducktyping&nbsp;to&nbsp;support&nbsp;different&nbsp;types&nbsp;of&nbsp;sets,&nbsp;and<br>
+is&nbsp;optimized&nbsp;for&nbsp;sets&nbsp;specifically&nbsp;(parameters&nbsp;must&nbsp;support&nbsp;a<br>
+difference&nbsp;method).</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-assertTrue"><strong>assertTrue</strong></a>(self, expr, msg<font color="#909090">=None</font>)</dt><dd><tt>Check&nbsp;that&nbsp;the&nbsp;expression&nbsp;is&nbsp;true.</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-assertTupleEqual"><strong>assertTupleEqual</strong></a>(self, tuple1, tuple2, msg<font color="#909090">=None</font>)</dt><dd><tt>A&nbsp;tuple-specific&nbsp;equality&nbsp;assertion.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;tuple1:&nbsp;The&nbsp;first&nbsp;tuple&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;tuple2:&nbsp;The&nbsp;second&nbsp;tuple&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;msg:&nbsp;Optional&nbsp;message&nbsp;to&nbsp;use&nbsp;on&nbsp;failure&nbsp;instead&nbsp;of&nbsp;a&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;differences.</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-assert_"><strong>assert_</strong></a> = assertTrue(self, expr, msg<font color="#909090">=None</font>)</dt><dd><tt>Check&nbsp;that&nbsp;the&nbsp;expression&nbsp;is&nbsp;true.</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-countTestCases"><strong>countTestCases</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="StorySetSmokeTest-debug"><strong>debug</strong></a>(self)</dt><dd><tt>Run&nbsp;the&nbsp;test&nbsp;without&nbsp;collecting&nbsp;errors&nbsp;in&nbsp;a&nbsp;TestResult</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-defaultTestResult"><strong>defaultTestResult</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="StorySetSmokeTest-doCleanups"><strong>doCleanups</strong></a>(self)</dt><dd><tt>Execute&nbsp;all&nbsp;cleanup&nbsp;functions.&nbsp;Normally&nbsp;called&nbsp;for&nbsp;you&nbsp;after<br>
+tearDown.</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-fail"><strong>fail</strong></a>(self, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;immediately,&nbsp;with&nbsp;the&nbsp;given&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-failIf"><strong>failIf</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="StorySetSmokeTest-failIfAlmostEqual"><strong>failIfAlmostEqual</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="StorySetSmokeTest-failIfEqual"><strong>failIfEqual</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="StorySetSmokeTest-failUnless"><strong>failUnless</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="StorySetSmokeTest-failUnlessAlmostEqual"><strong>failUnlessAlmostEqual</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="StorySetSmokeTest-failUnlessEqual"><strong>failUnlessEqual</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="StorySetSmokeTest-failUnlessRaises"><strong>failUnlessRaises</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="StorySetSmokeTest-id"><strong>id</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="StorySetSmokeTest-run"><strong>run</strong></a>(self, result<font color="#909090">=None</font>)</dt></dl>
+
+<dl><dt><a name="StorySetSmokeTest-shortDescription"><strong>shortDescription</strong></a>(self)</dt><dd><tt>Returns&nbsp;a&nbsp;one-line&nbsp;description&nbsp;of&nbsp;the&nbsp;test,&nbsp;or&nbsp;None&nbsp;if&nbsp;no<br>
+description&nbsp;has&nbsp;been&nbsp;provided.<br>
+&nbsp;<br>
+The&nbsp;default&nbsp;implementation&nbsp;of&nbsp;this&nbsp;method&nbsp;returns&nbsp;the&nbsp;first&nbsp;line&nbsp;of<br>
+the&nbsp;specified&nbsp;test&nbsp;method's&nbsp;docstring.</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-skipTest"><strong>skipTest</strong></a>(self, reason)</dt><dd><tt>Skip&nbsp;this&nbsp;test.</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-tearDown"><strong>tearDown</strong></a>(self)</dt><dd><tt>Hook&nbsp;method&nbsp;for&nbsp;deconstructing&nbsp;the&nbsp;test&nbsp;fixture&nbsp;after&nbsp;testing&nbsp;it.</tt></dd></dl>
+
+<hr>
+Class methods inherited from <a href="unittest.case.html#TestCase">unittest.case.TestCase</a>:<br>
+<dl><dt><a name="StorySetSmokeTest-setUpClass"><strong>setUpClass</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Hook&nbsp;method&nbsp;for&nbsp;setting&nbsp;up&nbsp;class&nbsp;fixture&nbsp;before&nbsp;running&nbsp;tests&nbsp;in&nbsp;the&nbsp;class.</tt></dd></dl>
+
+<dl><dt><a name="StorySetSmokeTest-tearDownClass"><strong>tearDownClass</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Hook&nbsp;method&nbsp;for&nbsp;deconstructing&nbsp;the&nbsp;class&nbsp;fixture&nbsp;after&nbsp;running&nbsp;all&nbsp;tests&nbsp;in&nbsp;the&nbsp;class.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="unittest.case.html#TestCase">unittest.case.TestCase</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="unittest.case.html#TestCase">unittest.case.TestCase</a>:<br>
+<dl><dt><strong>failureException</strong> = &lt;type 'exceptions.AssertionError'&gt;<dd><tt>Assertion&nbsp;failed.</tt></dl>
+
+<dl><dt><strong>longMessage</strong> = False</dl>
+
+<dl><dt><strong>maxDiff</strong> = 640</dl>
+
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.stream.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.stream.html
new file mode 100644
index 0000000..c0fc4a0
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.stream.html
@@ -0,0 +1,56 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.testing.stream</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.testing.html"><font color="#ffffff">testing</font></a>.stream</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/testing/stream.py">telemetry/testing/stream.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2015&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.testing.stream.html#TestOutputStream">TestOutputStream</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TestOutputStream">class <strong>TestOutputStream</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="TestOutputStream-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TestOutputStream-flush"><strong>flush</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TestOutputStream-write"><strong>write</strong></a>(self, data)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>output_data</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.system_stub.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.system_stub.html
new file mode 100644
index 0000000..c6c9055
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.system_stub.html
@@ -0,0 +1,446 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.testing.system_stub</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.testing.html"><font color="#ffffff">testing</font></a>.system_stub</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/testing/system_stub.py">telemetry/testing/system_stub.py</a></font></td></tr></table>
+    <p><tt>Provides&nbsp;stubs&nbsp;for&nbsp;os,&nbsp;sys&nbsp;and&nbsp;subprocess&nbsp;for&nbsp;testing<br>
+&nbsp;<br>
+This&nbsp;test&nbsp;allows&nbsp;one&nbsp;to&nbsp;test&nbsp;code&nbsp;that&nbsp;itself&nbsp;uses&nbsp;os,&nbsp;sys,&nbsp;and&nbsp;subprocess.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="ntpath.html">ntpath</a><br>
+<a href="os.html">os</a><br>
+</td><td width="25%" valign=top><a href="posixpath.html">posixpath</a><br>
+<a href="re.html">re</a><br>
+</td><td width="25%" valign=top><a href="shlex.html">shlex</a><br>
+<a href="sys.html">sys</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.testing.system_stub.html#AdbDevice">AdbDevice</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.testing.system_stub.html#AdbInstallCertStub">AdbInstallCertStub</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.testing.system_stub.html#CertUtilsStub">CertUtilsStub</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.testing.system_stub.html#CloudStorageModuleStub">CloudStorageModuleStub</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.testing.system_stub.html#LoggingStub">LoggingStub</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.testing.system_stub.html#OpenFunctionStub">OpenFunctionStub</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.testing.system_stub.html#OsModuleStub">OsModuleStub</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.testing.system_stub.html#Override">Override</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.testing.system_stub.html#PerfControlModuleStub">PerfControlModuleStub</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.testing.system_stub.html#PlatformSettingsStub">PlatformSettingsStub</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.testing.system_stub.html#RawInputFunctionStub">RawInputFunctionStub</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.testing.system_stub.html#SubprocessModuleStub">SubprocessModuleStub</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.testing.system_stub.html#SysModuleStub">SysModuleStub</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.testing.system_stub.html#ThermalThrottleModuleStub">ThermalThrottleModuleStub</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="AdbDevice">class <strong>AdbDevice</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="AdbDevice-FileExists"><strong>FileExists</strong></a>(self, _)</dt></dl>
+
+<dl><dt><a name="AdbDevice-GetProp"><strong>GetProp</strong></a>(self, property_name)</dt></dl>
+
+<dl><dt><a name="AdbDevice-HasRoot"><strong>HasRoot</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AdbDevice-NeedsSU"><strong>NeedsSU</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AdbDevice-ReadFile"><strong>ReadFile</strong></a>(self, device_path, as_root<font color="#909090">=False</font>)</dt></dl>
+
+<dl><dt><a name="AdbDevice-RunShellCommand"><strong>RunShellCommand</strong></a>(self, args, **_kwargs)</dt></dl>
+
+<dl><dt><a name="AdbDevice-SetProp"><strong>SetProp</strong></a>(self, property_name, property_value)</dt></dl>
+
+<dl><dt><a name="AdbDevice-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="AdbInstallCertStub">class <strong>AdbInstallCertStub</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes defined here:<br>
+<dl><dt><strong>AndroidCertInstaller</strong> = &lt;class 'telemetry.testing.system_stub.AndroidCertInstaller'&gt;</dl>
+
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="CertUtilsStub">class <strong>CertUtilsStub</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Static methods defined here:<br>
+<dl><dt><a name="CertUtilsStub-generate_dummy_ca_cert"><strong>generate_dummy_ca_cert</strong></a>()</dt></dl>
+
+<dl><dt><a name="CertUtilsStub-write_dummy_ca_cert"><strong>write_dummy_ca_cert</strong></a>(_ca_cert_str, _key_str, cert_path)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes defined here:<br>
+<dl><dt><strong>openssl_import_error</strong> = None</dl>
+
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="CloudStorageModuleStub">class <strong>CloudStorageModuleStub</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="CloudStorageModuleStub-CalculateHash"><strong>CalculateHash</strong></a>(self, file_path)</dt></dl>
+
+<dl><dt><a name="CloudStorageModuleStub-ChangeRemoteHashForTesting"><strong>ChangeRemoteHashForTesting</strong></a>(self, bucket, remote_path, new_hash)</dt></dl>
+
+<dl><dt><a name="CloudStorageModuleStub-CheckPermissionLevelForBucket"><strong>CheckPermissionLevelForBucket</strong></a>(self, bucket)</dt></dl>
+
+<dl><dt><a name="CloudStorageModuleStub-Exists"><strong>Exists</strong></a>(self, bucket, remote_path)</dt></dl>
+
+<dl><dt><a name="CloudStorageModuleStub-Get"><strong>Get</strong></a>(self, bucket, remote_path, local_path)</dt></dl>
+
+<dl><dt><a name="CloudStorageModuleStub-GetFilesInDirectoryIfChanged"><strong>GetFilesInDirectoryIfChanged</strong></a>(self, directory, bucket)</dt></dl>
+
+<dl><dt><a name="CloudStorageModuleStub-GetHelper"><strong>GetHelper</strong></a>(self, bucket, remote_path, local_path, only_if_changed)</dt></dl>
+
+<dl><dt><a name="CloudStorageModuleStub-GetIfChanged"><strong>GetIfChanged</strong></a>(self, local_path, bucket<font color="#909090">=None</font>)</dt></dl>
+
+<dl><dt><a name="CloudStorageModuleStub-GetLocalDataFiles"><strong>GetLocalDataFiles</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CloudStorageModuleStub-GetLocalHashFiles"><strong>GetLocalHashFiles</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CloudStorageModuleStub-GetRemotePathsForTesting"><strong>GetRemotePathsForTesting</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="CloudStorageModuleStub-Insert"><strong>Insert</strong></a>(self, bucket, remote_path, local_path)</dt></dl>
+
+<dl><dt><a name="CloudStorageModuleStub-List"><strong>List</strong></a>(self, bucket)</dt></dl>
+
+<dl><dt><a name="CloudStorageModuleStub-ReadHash"><strong>ReadHash</strong></a>(self, hash_path)</dt></dl>
+
+<dl><dt><a name="CloudStorageModuleStub-SetCalculatedHashesForTesting"><strong>SetCalculatedHashesForTesting</strong></a>(self, calculated_hash_dictionary)</dt><dd><tt>#&nbsp;Set&nbsp;a&nbsp;dictionary&nbsp;of&nbsp;data&nbsp;files&nbsp;and&nbsp;their&nbsp;"calculated"&nbsp;hashes.</tt></dd></dl>
+
+<dl><dt><a name="CloudStorageModuleStub-SetHashFileContentsForTesting"><strong>SetHashFileContentsForTesting</strong></a>(self, hash_file_dictionary)</dt><dd><tt>#&nbsp;Set&nbsp;a&nbsp;dictionary&nbsp;of&nbsp;hash&nbsp;files&nbsp;and&nbsp;the&nbsp;hashes&nbsp;they&nbsp;should&nbsp;contain.</tt></dd></dl>
+
+<dl><dt><a name="CloudStorageModuleStub-SetPermissionLevelForTesting"><strong>SetPermissionLevelForTesting</strong></a>(self, permission_level)</dt></dl>
+
+<dl><dt><a name="CloudStorageModuleStub-SetRemotePathsForTesting"><strong>SetRemotePathsForTesting</strong></a>(self, remote_path_dict<font color="#909090">=None</font>)</dt></dl>
+
+<dl><dt><a name="CloudStorageModuleStub-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes defined here:<br>
+<dl><dt><strong>BUCKET_ALIASES</strong> = {'internal': 'chrome-telemetry', 'partner': 'chrome-partner-telemetry', 'public': 'chromium-telemetry'}</dl>
+
+<dl><dt><strong>CREDENTIALS_ERROR_PERMISSION</strong> = -1</dl>
+
+<dl><dt><strong>CloudStorageError</strong> = &lt;class 'telemetry.testing.system_stub.CloudStorageError'&gt;</dl>
+
+<dl><dt><strong>CredentialsError</strong> = &lt;class 'telemetry.testing.system_stub.CredentialsError'&gt;</dl>
+
+<dl><dt><strong>INTERNAL_BUCKET</strong> = 'chrome-telemetry'</dl>
+
+<dl><dt><strong>INTERNAL_PERMISSION</strong> = 2</dl>
+
+<dl><dt><strong>NotFoundError</strong> = &lt;class 'telemetry.testing.system_stub.NotFoundError'&gt;</dl>
+
+<dl><dt><strong>PARTNER_BUCKET</strong> = 'chrome-partner-telemetry'</dl>
+
+<dl><dt><strong>PARTNER_PERMISSION</strong> = 1</dl>
+
+<dl><dt><strong>PUBLIC_BUCKET</strong> = 'chromium-telemetry'</dl>
+
+<dl><dt><strong>PUBLIC_PERMISSION</strong> = 0</dl>
+
+<dl><dt><strong>PermissionError</strong> = &lt;class 'telemetry.testing.system_stub.PermissionError'&gt;</dl>
+
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="LoggingStub">class <strong>LoggingStub</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="LoggingStub-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="LoggingStub-error"><strong>error</strong></a>(self, msg, *args)</dt></dl>
+
+<dl><dt><a name="LoggingStub-info"><strong>info</strong></a>(self, msg, *args)</dt></dl>
+
+<dl><dt><a name="LoggingStub-warn"><strong>warn</strong></a>(self, msg, *args)</dt></dl>
+
+<dl><dt><a name="LoggingStub-warning"><strong>warning</strong></a>(self, msg, *args)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="OpenFunctionStub">class <strong>OpenFunctionStub</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="OpenFunctionStub-__call__"><strong>__call__</strong></a>(self, name, *args, **kwargs)</dt></dl>
+
+<dl><dt><a name="OpenFunctionStub-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes defined here:<br>
+<dl><dt><strong>FileStub</strong> = &lt;class 'telemetry.testing.system_stub.FileStub'&gt;</dl>
+
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="OsModuleStub">class <strong>OsModuleStub</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="OsModuleStub-__init__"><strong>__init__</strong></a>(self, sys_module<font color="#909090">=&lt;module 'sys' (built-in)&gt;</font>)</dt></dl>
+
+<dl><dt><a name="OsModuleStub-access"><strong>access</strong></a>(self, path, _)</dt></dl>
+
+<dl><dt><a name="OsModuleStub-chdir"><strong>chdir</strong></a>(self, path)</dt></dl>
+
+<dl><dt><a name="OsModuleStub-getenv"><strong>getenv</strong></a>(self, name, value<font color="#909090">=None</font>)</dt></dl>
+
+<dl><dt><a name="OsModuleStub-walk"><strong>walk</strong></a>(self, top)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes defined here:<br>
+<dl><dt><strong>OsEnvironModuleStub</strong> = &lt;class 'telemetry.testing.system_stub.OsEnvironModuleStub'&gt;</dl>
+
+<dl><dt><strong>OsPathModuleStub</strong> = &lt;class 'telemetry.testing.system_stub.OsPathModuleStub'&gt;</dl>
+
+<dl><dt><strong>X_OK</strong> = 1</dl>
+
+<dl><dt><strong>pathsep</strong> = ':'</dl>
+
+<dl><dt><strong>sep</strong> = '/'</dl>
+
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="Override">class <strong>Override</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="Override-Restore"><strong>Restore</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="Override-__del__"><strong>__del__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="Override-__init__"><strong>__init__</strong></a>(self, base_module, module_list)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="PerfControlModuleStub">class <strong>PerfControlModuleStub</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="PerfControlModuleStub-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes defined here:<br>
+<dl><dt><strong>PerfControlStub</strong> = &lt;class 'telemetry.testing.system_stub.PerfControlStub'&gt;</dl>
+
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="PlatformSettingsStub">class <strong>PlatformSettingsStub</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Static methods defined here:<br>
+<dl><dt><a name="PlatformSettingsStub-HasSniSupport"><strong>HasSniSupport</strong></a>()</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="RawInputFunctionStub">class <strong>RawInputFunctionStub</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="RawInputFunctionStub-__call__"><strong>__call__</strong></a>(self, name, *args, **kwargs)</dt></dl>
+
+<dl><dt><a name="RawInputFunctionStub-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="SubprocessModuleStub">class <strong>SubprocessModuleStub</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="SubprocessModuleStub-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="SubprocessModuleStub-call"><strong>call</strong></a>(self, *args, **kwargs)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes defined here:<br>
+<dl><dt><strong>PopenStub</strong> = &lt;class 'telemetry.testing.system_stub.PopenStub'&gt;</dl>
+
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="SysModuleStub">class <strong>SysModuleStub</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="SysModuleStub-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ThermalThrottleModuleStub">class <strong>ThermalThrottleModuleStub</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="ThermalThrottleModuleStub-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes defined here:<br>
+<dl><dt><strong>ThermalThrottleStub</strong> = &lt;class 'telemetry.testing.system_stub.ThermalThrottleStub'&gt;</dl>
+
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.tab_test_case.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.tab_test_case.html
new file mode 100644
index 0000000..b649ee2
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.tab_test_case.html
@@ -0,0 +1,345 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.testing.tab_test_case</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.testing.html"><font color="#ffffff">testing</font></a>.tab_test_case</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/testing/tab_test_case.py">telemetry/testing/tab_test_case.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2012&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.testing.browser_test_case.html">telemetry.testing.browser_test_case</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.testing.browser_test_case.html#BrowserTestCase">telemetry.testing.browser_test_case.BrowserTestCase</a>(<a href="unittest.case.html#TestCase">unittest.case.TestCase</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.testing.tab_test_case.html#TabTestCase">TabTestCase</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TabTestCase">class <strong>TabTestCase</strong></a>(<a href="telemetry.testing.browser_test_case.html#BrowserTestCase">telemetry.testing.browser_test_case.BrowserTestCase</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.testing.tab_test_case.html#TabTestCase">TabTestCase</a></dd>
+<dd><a href="telemetry.testing.browser_test_case.html#BrowserTestCase">telemetry.testing.browser_test_case.BrowserTestCase</a></dd>
+<dd><a href="unittest.case.html#TestCase">unittest.case.TestCase</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="TabTestCase-Navigate"><strong>Navigate</strong></a>(self, filename, script_to_evaluate_on_commit<font color="#909090">=None</font>)</dt><dd><tt>Navigates&nbsp;|tab|&nbsp;to&nbsp;|filename|&nbsp;in&nbsp;the&nbsp;unittest&nbsp;data&nbsp;directory.<br>
+&nbsp;<br>
+Also&nbsp;sets&nbsp;up&nbsp;http&nbsp;server&nbsp;to&nbsp;point&nbsp;to&nbsp;the&nbsp;unittest&nbsp;data&nbsp;directory.</tt></dd></dl>
+
+<dl><dt><a name="TabTestCase-__init__"><strong>__init__</strong></a>(self, *args)</dt></dl>
+
+<dl><dt><a name="TabTestCase-setUp"><strong>setUp</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>tabs</strong></dt>
+</dl>
+<hr>
+Class methods inherited from <a href="telemetry.testing.browser_test_case.html#BrowserTestCase">telemetry.testing.browser_test_case.BrowserTestCase</a>:<br>
+<dl><dt><a name="TabTestCase-CustomizeBrowserOptions"><strong>CustomizeBrowserOptions</strong></a>(cls, options)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Override&nbsp;to&nbsp;add&nbsp;test-specific&nbsp;options&nbsp;to&nbsp;the&nbsp;BrowserOptions&nbsp;object</tt></dd></dl>
+
+<dl><dt><a name="TabTestCase-UrlOfUnittestFile"><strong>UrlOfUnittestFile</strong></a>(cls, filename)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="TabTestCase-setUpClass"><strong>setUpClass</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="TabTestCase-tearDownClass"><strong>tearDownClass</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Methods inherited from <a href="unittest.case.html#TestCase">unittest.case.TestCase</a>:<br>
+<dl><dt><a name="TabTestCase-__call__"><strong>__call__</strong></a>(self, *args, **kwds)</dt></dl>
+
+<dl><dt><a name="TabTestCase-__eq__"><strong>__eq__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="TabTestCase-__hash__"><strong>__hash__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TabTestCase-__ne__"><strong>__ne__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="TabTestCase-__repr__"><strong>__repr__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TabTestCase-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TabTestCase-addCleanup"><strong>addCleanup</strong></a>(self, function, *args, **kwargs)</dt><dd><tt>Add&nbsp;a&nbsp;function,&nbsp;with&nbsp;arguments,&nbsp;to&nbsp;be&nbsp;called&nbsp;when&nbsp;the&nbsp;test&nbsp;is<br>
+completed.&nbsp;Functions&nbsp;added&nbsp;are&nbsp;called&nbsp;on&nbsp;a&nbsp;LIFO&nbsp;basis&nbsp;and&nbsp;are<br>
+called&nbsp;after&nbsp;tearDown&nbsp;on&nbsp;test&nbsp;failure&nbsp;or&nbsp;success.<br>
+&nbsp;<br>
+Cleanup&nbsp;items&nbsp;are&nbsp;called&nbsp;even&nbsp;if&nbsp;setUp&nbsp;fails&nbsp;(unlike&nbsp;tearDown).</tt></dd></dl>
+
+<dl><dt><a name="TabTestCase-addTypeEqualityFunc"><strong>addTypeEqualityFunc</strong></a>(self, typeobj, function)</dt><dd><tt>Add&nbsp;a&nbsp;type&nbsp;specific&nbsp;assertEqual&nbsp;style&nbsp;function&nbsp;to&nbsp;compare&nbsp;a&nbsp;type.<br>
+&nbsp;<br>
+This&nbsp;method&nbsp;is&nbsp;for&nbsp;use&nbsp;by&nbsp;TestCase&nbsp;subclasses&nbsp;that&nbsp;need&nbsp;to&nbsp;register<br>
+their&nbsp;own&nbsp;type&nbsp;equality&nbsp;functions&nbsp;to&nbsp;provide&nbsp;nicer&nbsp;error&nbsp;messages.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;typeobj:&nbsp;The&nbsp;data&nbsp;type&nbsp;to&nbsp;call&nbsp;this&nbsp;function&nbsp;on&nbsp;when&nbsp;both&nbsp;values<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;are&nbsp;of&nbsp;the&nbsp;same&nbsp;type&nbsp;in&nbsp;<a href="#TabTestCase-assertEqual">assertEqual</a>().<br>
+&nbsp;&nbsp;&nbsp;&nbsp;function:&nbsp;The&nbsp;callable&nbsp;taking&nbsp;two&nbsp;arguments&nbsp;and&nbsp;an&nbsp;optional<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;msg=&nbsp;argument&nbsp;that&nbsp;raises&nbsp;self.<strong>failureException</strong>&nbsp;with&nbsp;a<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;useful&nbsp;error&nbsp;message&nbsp;when&nbsp;the&nbsp;two&nbsp;arguments&nbsp;are&nbsp;not&nbsp;equal.</tt></dd></dl>
+
+<dl><dt><a name="TabTestCase-assertAlmostEqual"><strong>assertAlmostEqual</strong></a>(self, first, second, places<font color="#909090">=None</font>, msg<font color="#909090">=None</font>, delta<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;unequal&nbsp;as&nbsp;determined&nbsp;by&nbsp;their<br>
+difference&nbsp;rounded&nbsp;to&nbsp;the&nbsp;given&nbsp;number&nbsp;of&nbsp;decimal&nbsp;places<br>
+(default&nbsp;7)&nbsp;and&nbsp;comparing&nbsp;to&nbsp;zero,&nbsp;or&nbsp;by&nbsp;comparing&nbsp;that&nbsp;the<br>
+between&nbsp;the&nbsp;two&nbsp;objects&nbsp;is&nbsp;more&nbsp;than&nbsp;the&nbsp;given&nbsp;delta.<br>
+&nbsp;<br>
+Note&nbsp;that&nbsp;decimal&nbsp;places&nbsp;(from&nbsp;zero)&nbsp;are&nbsp;usually&nbsp;not&nbsp;the&nbsp;same<br>
+as&nbsp;significant&nbsp;digits&nbsp;(measured&nbsp;from&nbsp;the&nbsp;most&nbsp;signficant&nbsp;digit).<br>
+&nbsp;<br>
+If&nbsp;the&nbsp;two&nbsp;objects&nbsp;compare&nbsp;equal&nbsp;then&nbsp;they&nbsp;will&nbsp;automatically<br>
+compare&nbsp;almost&nbsp;equal.</tt></dd></dl>
+
+<dl><dt><a name="TabTestCase-assertAlmostEquals"><strong>assertAlmostEquals</strong></a> = assertAlmostEqual(self, first, second, places<font color="#909090">=None</font>, msg<font color="#909090">=None</font>, delta<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;unequal&nbsp;as&nbsp;determined&nbsp;by&nbsp;their<br>
+difference&nbsp;rounded&nbsp;to&nbsp;the&nbsp;given&nbsp;number&nbsp;of&nbsp;decimal&nbsp;places<br>
+(default&nbsp;7)&nbsp;and&nbsp;comparing&nbsp;to&nbsp;zero,&nbsp;or&nbsp;by&nbsp;comparing&nbsp;that&nbsp;the<br>
+between&nbsp;the&nbsp;two&nbsp;objects&nbsp;is&nbsp;more&nbsp;than&nbsp;the&nbsp;given&nbsp;delta.<br>
+&nbsp;<br>
+Note&nbsp;that&nbsp;decimal&nbsp;places&nbsp;(from&nbsp;zero)&nbsp;are&nbsp;usually&nbsp;not&nbsp;the&nbsp;same<br>
+as&nbsp;significant&nbsp;digits&nbsp;(measured&nbsp;from&nbsp;the&nbsp;most&nbsp;signficant&nbsp;digit).<br>
+&nbsp;<br>
+If&nbsp;the&nbsp;two&nbsp;objects&nbsp;compare&nbsp;equal&nbsp;then&nbsp;they&nbsp;will&nbsp;automatically<br>
+compare&nbsp;almost&nbsp;equal.</tt></dd></dl>
+
+<dl><dt><a name="TabTestCase-assertDictContainsSubset"><strong>assertDictContainsSubset</strong></a>(self, expected, actual, msg<font color="#909090">=None</font>)</dt><dd><tt>Checks&nbsp;whether&nbsp;actual&nbsp;is&nbsp;a&nbsp;superset&nbsp;of&nbsp;expected.</tt></dd></dl>
+
+<dl><dt><a name="TabTestCase-assertDictEqual"><strong>assertDictEqual</strong></a>(self, d1, d2, msg<font color="#909090">=None</font>)</dt></dl>
+
+<dl><dt><a name="TabTestCase-assertEqual"><strong>assertEqual</strong></a>(self, first, second, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;unequal&nbsp;as&nbsp;determined&nbsp;by&nbsp;the&nbsp;'=='<br>
+operator.</tt></dd></dl>
+
+<dl><dt><a name="TabTestCase-assertEquals"><strong>assertEquals</strong></a> = assertEqual(self, first, second, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;unequal&nbsp;as&nbsp;determined&nbsp;by&nbsp;the&nbsp;'=='<br>
+operator.</tt></dd></dl>
+
+<dl><dt><a name="TabTestCase-assertFalse"><strong>assertFalse</strong></a>(self, expr, msg<font color="#909090">=None</font>)</dt><dd><tt>Check&nbsp;that&nbsp;the&nbsp;expression&nbsp;is&nbsp;false.</tt></dd></dl>
+
+<dl><dt><a name="TabTestCase-assertGreater"><strong>assertGreater</strong></a>(self, a, b, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#TabTestCase-assertTrue">assertTrue</a>(a&nbsp;&gt;&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="TabTestCase-assertGreaterEqual"><strong>assertGreaterEqual</strong></a>(self, a, b, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#TabTestCase-assertTrue">assertTrue</a>(a&nbsp;&gt;=&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="TabTestCase-assertIn"><strong>assertIn</strong></a>(self, member, container, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#TabTestCase-assertTrue">assertTrue</a>(a&nbsp;in&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="TabTestCase-assertIs"><strong>assertIs</strong></a>(self, expr1, expr2, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#TabTestCase-assertTrue">assertTrue</a>(a&nbsp;is&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="TabTestCase-assertIsInstance"><strong>assertIsInstance</strong></a>(self, obj, cls, msg<font color="#909090">=None</font>)</dt><dd><tt>Same&nbsp;as&nbsp;<a href="#TabTestCase-assertTrue">assertTrue</a>(isinstance(obj,&nbsp;cls)),&nbsp;with&nbsp;a&nbsp;nicer<br>
+default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="TabTestCase-assertIsNone"><strong>assertIsNone</strong></a>(self, obj, msg<font color="#909090">=None</font>)</dt><dd><tt>Same&nbsp;as&nbsp;<a href="#TabTestCase-assertTrue">assertTrue</a>(obj&nbsp;is&nbsp;None),&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="TabTestCase-assertIsNot"><strong>assertIsNot</strong></a>(self, expr1, expr2, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#TabTestCase-assertTrue">assertTrue</a>(a&nbsp;is&nbsp;not&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="TabTestCase-assertIsNotNone"><strong>assertIsNotNone</strong></a>(self, obj, msg<font color="#909090">=None</font>)</dt><dd><tt>Included&nbsp;for&nbsp;symmetry&nbsp;with&nbsp;assertIsNone.</tt></dd></dl>
+
+<dl><dt><a name="TabTestCase-assertItemsEqual"><strong>assertItemsEqual</strong></a>(self, expected_seq, actual_seq, msg<font color="#909090">=None</font>)</dt><dd><tt>An&nbsp;unordered&nbsp;sequence&nbsp;specific&nbsp;comparison.&nbsp;It&nbsp;asserts&nbsp;that<br>
+actual_seq&nbsp;and&nbsp;expected_seq&nbsp;have&nbsp;the&nbsp;same&nbsp;element&nbsp;counts.<br>
+Equivalent&nbsp;to::<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#TabTestCase-assertEqual">assertEqual</a>(Counter(iter(actual_seq)),<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Counter(iter(expected_seq)))<br>
+&nbsp;<br>
+Asserts&nbsp;that&nbsp;each&nbsp;element&nbsp;has&nbsp;the&nbsp;same&nbsp;count&nbsp;in&nbsp;both&nbsp;sequences.<br>
+Example:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;[0,&nbsp;1,&nbsp;1]&nbsp;and&nbsp;[1,&nbsp;0,&nbsp;1]&nbsp;compare&nbsp;equal.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;[0,&nbsp;0,&nbsp;1]&nbsp;and&nbsp;[0,&nbsp;1]&nbsp;compare&nbsp;unequal.</tt></dd></dl>
+
+<dl><dt><a name="TabTestCase-assertLess"><strong>assertLess</strong></a>(self, a, b, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#TabTestCase-assertTrue">assertTrue</a>(a&nbsp;&lt;&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="TabTestCase-assertLessEqual"><strong>assertLessEqual</strong></a>(self, a, b, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#TabTestCase-assertTrue">assertTrue</a>(a&nbsp;&lt;=&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="TabTestCase-assertListEqual"><strong>assertListEqual</strong></a>(self, list1, list2, msg<font color="#909090">=None</font>)</dt><dd><tt>A&nbsp;list-specific&nbsp;equality&nbsp;assertion.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;list1:&nbsp;The&nbsp;first&nbsp;list&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;list2:&nbsp;The&nbsp;second&nbsp;list&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;msg:&nbsp;Optional&nbsp;message&nbsp;to&nbsp;use&nbsp;on&nbsp;failure&nbsp;instead&nbsp;of&nbsp;a&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;differences.</tt></dd></dl>
+
+<dl><dt><a name="TabTestCase-assertMultiLineEqual"><strong>assertMultiLineEqual</strong></a>(self, first, second, msg<font color="#909090">=None</font>)</dt><dd><tt>Assert&nbsp;that&nbsp;two&nbsp;multi-line&nbsp;strings&nbsp;are&nbsp;equal.</tt></dd></dl>
+
+<dl><dt><a name="TabTestCase-assertNotAlmostEqual"><strong>assertNotAlmostEqual</strong></a>(self, first, second, places<font color="#909090">=None</font>, msg<font color="#909090">=None</font>, delta<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;equal&nbsp;as&nbsp;determined&nbsp;by&nbsp;their<br>
+difference&nbsp;rounded&nbsp;to&nbsp;the&nbsp;given&nbsp;number&nbsp;of&nbsp;decimal&nbsp;places<br>
+(default&nbsp;7)&nbsp;and&nbsp;comparing&nbsp;to&nbsp;zero,&nbsp;or&nbsp;by&nbsp;comparing&nbsp;that&nbsp;the<br>
+between&nbsp;the&nbsp;two&nbsp;objects&nbsp;is&nbsp;less&nbsp;than&nbsp;the&nbsp;given&nbsp;delta.<br>
+&nbsp;<br>
+Note&nbsp;that&nbsp;decimal&nbsp;places&nbsp;(from&nbsp;zero)&nbsp;are&nbsp;usually&nbsp;not&nbsp;the&nbsp;same<br>
+as&nbsp;significant&nbsp;digits&nbsp;(measured&nbsp;from&nbsp;the&nbsp;most&nbsp;signficant&nbsp;digit).<br>
+&nbsp;<br>
+Objects&nbsp;that&nbsp;are&nbsp;equal&nbsp;automatically&nbsp;fail.</tt></dd></dl>
+
+<dl><dt><a name="TabTestCase-assertNotAlmostEquals"><strong>assertNotAlmostEquals</strong></a> = assertNotAlmostEqual(self, first, second, places<font color="#909090">=None</font>, msg<font color="#909090">=None</font>, delta<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;equal&nbsp;as&nbsp;determined&nbsp;by&nbsp;their<br>
+difference&nbsp;rounded&nbsp;to&nbsp;the&nbsp;given&nbsp;number&nbsp;of&nbsp;decimal&nbsp;places<br>
+(default&nbsp;7)&nbsp;and&nbsp;comparing&nbsp;to&nbsp;zero,&nbsp;or&nbsp;by&nbsp;comparing&nbsp;that&nbsp;the<br>
+between&nbsp;the&nbsp;two&nbsp;objects&nbsp;is&nbsp;less&nbsp;than&nbsp;the&nbsp;given&nbsp;delta.<br>
+&nbsp;<br>
+Note&nbsp;that&nbsp;decimal&nbsp;places&nbsp;(from&nbsp;zero)&nbsp;are&nbsp;usually&nbsp;not&nbsp;the&nbsp;same<br>
+as&nbsp;significant&nbsp;digits&nbsp;(measured&nbsp;from&nbsp;the&nbsp;most&nbsp;signficant&nbsp;digit).<br>
+&nbsp;<br>
+Objects&nbsp;that&nbsp;are&nbsp;equal&nbsp;automatically&nbsp;fail.</tt></dd></dl>
+
+<dl><dt><a name="TabTestCase-assertNotEqual"><strong>assertNotEqual</strong></a>(self, first, second, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;equal&nbsp;as&nbsp;determined&nbsp;by&nbsp;the&nbsp;'!='<br>
+operator.</tt></dd></dl>
+
+<dl><dt><a name="TabTestCase-assertNotEquals"><strong>assertNotEquals</strong></a> = assertNotEqual(self, first, second, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;if&nbsp;the&nbsp;two&nbsp;objects&nbsp;are&nbsp;equal&nbsp;as&nbsp;determined&nbsp;by&nbsp;the&nbsp;'!='<br>
+operator.</tt></dd></dl>
+
+<dl><dt><a name="TabTestCase-assertNotIn"><strong>assertNotIn</strong></a>(self, member, container, msg<font color="#909090">=None</font>)</dt><dd><tt>Just&nbsp;like&nbsp;<a href="#TabTestCase-assertTrue">assertTrue</a>(a&nbsp;not&nbsp;in&nbsp;b),&nbsp;but&nbsp;with&nbsp;a&nbsp;nicer&nbsp;default&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="TabTestCase-assertNotIsInstance"><strong>assertNotIsInstance</strong></a>(self, obj, cls, msg<font color="#909090">=None</font>)</dt><dd><tt>Included&nbsp;for&nbsp;symmetry&nbsp;with&nbsp;assertIsInstance.</tt></dd></dl>
+
+<dl><dt><a name="TabTestCase-assertNotRegexpMatches"><strong>assertNotRegexpMatches</strong></a>(self, text, unexpected_regexp, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;the&nbsp;test&nbsp;if&nbsp;the&nbsp;text&nbsp;matches&nbsp;the&nbsp;regular&nbsp;expression.</tt></dd></dl>
+
+<dl><dt><a name="TabTestCase-assertRaises"><strong>assertRaises</strong></a>(self, excClass, callableObj<font color="#909090">=None</font>, *args, **kwargs)</dt><dd><tt>Fail&nbsp;unless&nbsp;an&nbsp;exception&nbsp;of&nbsp;class&nbsp;excClass&nbsp;is&nbsp;raised<br>
+by&nbsp;callableObj&nbsp;when&nbsp;invoked&nbsp;with&nbsp;arguments&nbsp;args&nbsp;and&nbsp;keyword<br>
+arguments&nbsp;kwargs.&nbsp;If&nbsp;a&nbsp;different&nbsp;type&nbsp;of&nbsp;exception&nbsp;is<br>
+raised,&nbsp;it&nbsp;will&nbsp;not&nbsp;be&nbsp;caught,&nbsp;and&nbsp;the&nbsp;test&nbsp;case&nbsp;will&nbsp;be<br>
+deemed&nbsp;to&nbsp;have&nbsp;suffered&nbsp;an&nbsp;error,&nbsp;exactly&nbsp;as&nbsp;for&nbsp;an<br>
+unexpected&nbsp;exception.<br>
+&nbsp;<br>
+If&nbsp;called&nbsp;with&nbsp;callableObj&nbsp;omitted&nbsp;or&nbsp;None,&nbsp;will&nbsp;return&nbsp;a<br>
+context&nbsp;object&nbsp;used&nbsp;like&nbsp;this::<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;with&nbsp;<a href="#TabTestCase-assertRaises">assertRaises</a>(SomeException):<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;do_something()<br>
+&nbsp;<br>
+The&nbsp;context&nbsp;manager&nbsp;keeps&nbsp;a&nbsp;reference&nbsp;to&nbsp;the&nbsp;exception&nbsp;as<br>
+the&nbsp;'exception'&nbsp;attribute.&nbsp;This&nbsp;allows&nbsp;you&nbsp;to&nbsp;inspect&nbsp;the<br>
+exception&nbsp;after&nbsp;the&nbsp;assertion::<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;with&nbsp;<a href="#TabTestCase-assertRaises">assertRaises</a>(SomeException)&nbsp;as&nbsp;cm:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;do_something()<br>
+&nbsp;&nbsp;&nbsp;&nbsp;the_exception&nbsp;=&nbsp;cm.exception<br>
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#TabTestCase-assertEqual">assertEqual</a>(the_exception.error_code,&nbsp;3)</tt></dd></dl>
+
+<dl><dt><a name="TabTestCase-assertRaisesRegexp"><strong>assertRaisesRegexp</strong></a>(self, expected_exception, expected_regexp, callable_obj<font color="#909090">=None</font>, *args, **kwargs)</dt><dd><tt>Asserts&nbsp;that&nbsp;the&nbsp;message&nbsp;in&nbsp;a&nbsp;raised&nbsp;exception&nbsp;matches&nbsp;a&nbsp;regexp.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;expected_exception:&nbsp;Exception&nbsp;class&nbsp;expected&nbsp;to&nbsp;be&nbsp;raised.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;expected_regexp:&nbsp;Regexp&nbsp;(re&nbsp;pattern&nbsp;object&nbsp;or&nbsp;string)&nbsp;expected<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;to&nbsp;be&nbsp;found&nbsp;in&nbsp;error&nbsp;message.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;callable_obj:&nbsp;Function&nbsp;to&nbsp;be&nbsp;called.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;args:&nbsp;Extra&nbsp;args.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;kwargs:&nbsp;Extra&nbsp;kwargs.</tt></dd></dl>
+
+<dl><dt><a name="TabTestCase-assertRegexpMatches"><strong>assertRegexpMatches</strong></a>(self, text, expected_regexp, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;the&nbsp;test&nbsp;unless&nbsp;the&nbsp;text&nbsp;matches&nbsp;the&nbsp;regular&nbsp;expression.</tt></dd></dl>
+
+<dl><dt><a name="TabTestCase-assertSequenceEqual"><strong>assertSequenceEqual</strong></a>(self, seq1, seq2, msg<font color="#909090">=None</font>, seq_type<font color="#909090">=None</font>)</dt><dd><tt>An&nbsp;equality&nbsp;assertion&nbsp;for&nbsp;ordered&nbsp;sequences&nbsp;(like&nbsp;lists&nbsp;and&nbsp;tuples).<br>
+&nbsp;<br>
+For&nbsp;the&nbsp;purposes&nbsp;of&nbsp;this&nbsp;function,&nbsp;a&nbsp;valid&nbsp;ordered&nbsp;sequence&nbsp;type&nbsp;is&nbsp;one<br>
+which&nbsp;can&nbsp;be&nbsp;indexed,&nbsp;has&nbsp;a&nbsp;length,&nbsp;and&nbsp;has&nbsp;an&nbsp;equality&nbsp;operator.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;seq1:&nbsp;The&nbsp;first&nbsp;sequence&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;seq2:&nbsp;The&nbsp;second&nbsp;sequence&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;seq_type:&nbsp;The&nbsp;expected&nbsp;datatype&nbsp;of&nbsp;the&nbsp;sequences,&nbsp;or&nbsp;None&nbsp;if&nbsp;no<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;datatype&nbsp;should&nbsp;be&nbsp;enforced.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;msg:&nbsp;Optional&nbsp;message&nbsp;to&nbsp;use&nbsp;on&nbsp;failure&nbsp;instead&nbsp;of&nbsp;a&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;differences.</tt></dd></dl>
+
+<dl><dt><a name="TabTestCase-assertSetEqual"><strong>assertSetEqual</strong></a>(self, set1, set2, msg<font color="#909090">=None</font>)</dt><dd><tt>A&nbsp;set-specific&nbsp;equality&nbsp;assertion.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;set1:&nbsp;The&nbsp;first&nbsp;set&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;set2:&nbsp;The&nbsp;second&nbsp;set&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;msg:&nbsp;Optional&nbsp;message&nbsp;to&nbsp;use&nbsp;on&nbsp;failure&nbsp;instead&nbsp;of&nbsp;a&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;differences.<br>
+&nbsp;<br>
+assertSetEqual&nbsp;uses&nbsp;ducktyping&nbsp;to&nbsp;support&nbsp;different&nbsp;types&nbsp;of&nbsp;sets,&nbsp;and<br>
+is&nbsp;optimized&nbsp;for&nbsp;sets&nbsp;specifically&nbsp;(parameters&nbsp;must&nbsp;support&nbsp;a<br>
+difference&nbsp;method).</tt></dd></dl>
+
+<dl><dt><a name="TabTestCase-assertTrue"><strong>assertTrue</strong></a>(self, expr, msg<font color="#909090">=None</font>)</dt><dd><tt>Check&nbsp;that&nbsp;the&nbsp;expression&nbsp;is&nbsp;true.</tt></dd></dl>
+
+<dl><dt><a name="TabTestCase-assertTupleEqual"><strong>assertTupleEqual</strong></a>(self, tuple1, tuple2, msg<font color="#909090">=None</font>)</dt><dd><tt>A&nbsp;tuple-specific&nbsp;equality&nbsp;assertion.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;tuple1:&nbsp;The&nbsp;first&nbsp;tuple&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;tuple2:&nbsp;The&nbsp;second&nbsp;tuple&nbsp;to&nbsp;compare.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;msg:&nbsp;Optional&nbsp;message&nbsp;to&nbsp;use&nbsp;on&nbsp;failure&nbsp;instead&nbsp;of&nbsp;a&nbsp;list&nbsp;of<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;differences.</tt></dd></dl>
+
+<dl><dt><a name="TabTestCase-assert_"><strong>assert_</strong></a> = assertTrue(self, expr, msg<font color="#909090">=None</font>)</dt><dd><tt>Check&nbsp;that&nbsp;the&nbsp;expression&nbsp;is&nbsp;true.</tt></dd></dl>
+
+<dl><dt><a name="TabTestCase-countTestCases"><strong>countTestCases</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TabTestCase-debug"><strong>debug</strong></a>(self)</dt><dd><tt>Run&nbsp;the&nbsp;test&nbsp;without&nbsp;collecting&nbsp;errors&nbsp;in&nbsp;a&nbsp;TestResult</tt></dd></dl>
+
+<dl><dt><a name="TabTestCase-defaultTestResult"><strong>defaultTestResult</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TabTestCase-doCleanups"><strong>doCleanups</strong></a>(self)</dt><dd><tt>Execute&nbsp;all&nbsp;cleanup&nbsp;functions.&nbsp;Normally&nbsp;called&nbsp;for&nbsp;you&nbsp;after<br>
+tearDown.</tt></dd></dl>
+
+<dl><dt><a name="TabTestCase-fail"><strong>fail</strong></a>(self, msg<font color="#909090">=None</font>)</dt><dd><tt>Fail&nbsp;immediately,&nbsp;with&nbsp;the&nbsp;given&nbsp;message.</tt></dd></dl>
+
+<dl><dt><a name="TabTestCase-failIf"><strong>failIf</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="TabTestCase-failIfAlmostEqual"><strong>failIfAlmostEqual</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="TabTestCase-failIfEqual"><strong>failIfEqual</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="TabTestCase-failUnless"><strong>failUnless</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="TabTestCase-failUnlessAlmostEqual"><strong>failUnlessAlmostEqual</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="TabTestCase-failUnlessEqual"><strong>failUnlessEqual</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="TabTestCase-failUnlessRaises"><strong>failUnlessRaises</strong></a> = deprecated_func(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="TabTestCase-id"><strong>id</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TabTestCase-run"><strong>run</strong></a>(self, result<font color="#909090">=None</font>)</dt></dl>
+
+<dl><dt><a name="TabTestCase-shortDescription"><strong>shortDescription</strong></a>(self)</dt><dd><tt>Returns&nbsp;a&nbsp;one-line&nbsp;description&nbsp;of&nbsp;the&nbsp;test,&nbsp;or&nbsp;None&nbsp;if&nbsp;no<br>
+description&nbsp;has&nbsp;been&nbsp;provided.<br>
+&nbsp;<br>
+The&nbsp;default&nbsp;implementation&nbsp;of&nbsp;this&nbsp;method&nbsp;returns&nbsp;the&nbsp;first&nbsp;line&nbsp;of<br>
+the&nbsp;specified&nbsp;test&nbsp;method's&nbsp;docstring.</tt></dd></dl>
+
+<dl><dt><a name="TabTestCase-skipTest"><strong>skipTest</strong></a>(self, reason)</dt><dd><tt>Skip&nbsp;this&nbsp;test.</tt></dd></dl>
+
+<dl><dt><a name="TabTestCase-tearDown"><strong>tearDown</strong></a>(self)</dt><dd><tt>Hook&nbsp;method&nbsp;for&nbsp;deconstructing&nbsp;the&nbsp;test&nbsp;fixture&nbsp;after&nbsp;testing&nbsp;it.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="unittest.case.html#TestCase">unittest.case.TestCase</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="unittest.case.html#TestCase">unittest.case.TestCase</a>:<br>
+<dl><dt><strong>failureException</strong> = &lt;type 'exceptions.AssertionError'&gt;<dd><tt>Assertion&nbsp;failed.</tt></dl>
+
+<dl><dt><strong>longMessage</strong> = False</dl>
+
+<dl><dt><strong>maxDiff</strong> = 640</dl>
+
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.test_page_test_results.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.test_page_test_results.html
new file mode 100644
index 0000000..0766204
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.test_page_test_results.html
@@ -0,0 +1,143 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.testing.test_page_test_results</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.testing.html"><font color="#ffffff">testing</font></a>.test_page_test_results</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/testing/test_page_test_results.py">telemetry/testing/test_page_test_results.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.value.list_of_scalar_values.html">telemetry.value.list_of_scalar_values</a><br>
+</td><td width="25%" valign=top><a href="telemetry.page.page.html">telemetry.page.page</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.results.page_test_results.html">telemetry.internal.results.page_test_results</a><br>
+</td><td width="25%" valign=top><a href="telemetry.value.scalar.html">telemetry.value.scalar</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.internal.results.page_test_results.html#PageTestResults">telemetry.internal.results.page_test_results.PageTestResults</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.testing.test_page_test_results.html#TestPageTestResults">TestPageTestResults</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TestPageTestResults">class <strong>TestPageTestResults</strong></a>(<a href="telemetry.internal.results.page_test_results.html#PageTestResults">telemetry.internal.results.page_test_results.PageTestResults</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.testing.test_page_test_results.html#TestPageTestResults">TestPageTestResults</a></dd>
+<dd><a href="telemetry.internal.results.page_test_results.html#PageTestResults">telemetry.internal.results.page_test_results.PageTestResults</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="TestPageTestResults-AssertHasPageSpecificListOfScalarValues"><strong>AssertHasPageSpecificListOfScalarValues</strong></a>(self, name, units, expected_values)</dt></dl>
+
+<dl><dt><a name="TestPageTestResults-AssertHasPageSpecificScalarValue"><strong>AssertHasPageSpecificScalarValue</strong></a>(self, name, units, expected_value)</dt></dl>
+
+<dl><dt><a name="TestPageTestResults-GetPageSpecificValueNamed"><strong>GetPageSpecificValueNamed</strong></a>(self, name)</dt></dl>
+
+<dl><dt><a name="TestPageTestResults-__init__"><strong>__init__</strong></a>(self, test)</dt></dl>
+
+<dl><dt><a name="TestPageTestResults-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.internal.results.page_test_results.html#PageTestResults">telemetry.internal.results.page_test_results.PageTestResults</a>:<br>
+<dl><dt><a name="TestPageTestResults-AddProfilingFile"><strong>AddProfilingFile</strong></a>(self, page, file_handle)</dt></dl>
+
+<dl><dt><a name="TestPageTestResults-AddSummaryValue"><strong>AddSummaryValue</strong></a>(self, value)</dt></dl>
+
+<dl><dt><a name="TestPageTestResults-AddValue"><strong>AddValue</strong></a>(self, value)</dt></dl>
+
+<dl><dt><a name="TestPageTestResults-CleanUp"><strong>CleanUp</strong></a>(self)</dt><dd><tt>Clean&nbsp;up&nbsp;any&nbsp;TraceValues&nbsp;contained&nbsp;within&nbsp;this&nbsp;results&nbsp;object.</tt></dd></dl>
+
+<dl><dt><a name="TestPageTestResults-DidRunPage"><strong>DidRunPage</strong></a>(self, page)</dt><dd><tt>Args:<br>
+&nbsp;&nbsp;page:&nbsp;The&nbsp;current&nbsp;page&nbsp;under&nbsp;test.</tt></dd></dl>
+
+<dl><dt><a name="TestPageTestResults-FindAllPageSpecificValuesFromIRNamed"><strong>FindAllPageSpecificValuesFromIRNamed</strong></a>(self, tir_label, value_name)</dt></dl>
+
+<dl><dt><a name="TestPageTestResults-FindAllPageSpecificValuesNamed"><strong>FindAllPageSpecificValuesNamed</strong></a>(self, value_name)</dt></dl>
+
+<dl><dt><a name="TestPageTestResults-FindAllTraceValues"><strong>FindAllTraceValues</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TestPageTestResults-FindPageSpecificValuesForPage"><strong>FindPageSpecificValuesForPage</strong></a>(self, page, value_name)</dt></dl>
+
+<dl><dt><a name="TestPageTestResults-FindValues"><strong>FindValues</strong></a>(self, predicate)</dt><dd><tt>Finds&nbsp;all&nbsp;values&nbsp;matching&nbsp;the&nbsp;specified&nbsp;predicate.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;predicate:&nbsp;A&nbsp;function&nbsp;that&nbsp;takes&nbsp;a&nbsp;Value&nbsp;and&nbsp;returns&nbsp;a&nbsp;bool.<br>
+Returns:<br>
+&nbsp;&nbsp;A&nbsp;list&nbsp;of&nbsp;values&nbsp;matching&nbsp;|predicate|.</tt></dd></dl>
+
+<dl><dt><a name="TestPageTestResults-PrintSummary"><strong>PrintSummary</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TestPageTestResults-UploadProfilingFilesToCloud"><strong>UploadProfilingFilesToCloud</strong></a>(self, bucket)</dt></dl>
+
+<dl><dt><a name="TestPageTestResults-UploadTraceFilesToCloud"><strong>UploadTraceFilesToCloud</strong></a>(self, bucket)</dt></dl>
+
+<dl><dt><a name="TestPageTestResults-WillRunPage"><strong>WillRunPage</strong></a>(self, page)</dt></dl>
+
+<dl><dt><a name="TestPageTestResults-__copy__"><strong>__copy__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TestPageTestResults-__enter__"><strong>__enter__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TestPageTestResults-__exit__"><strong>__exit__</strong></a>(self, _, __, ___)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.internal.results.page_test_results.html#PageTestResults">telemetry.internal.results.page_test_results.PageTestResults</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>all_page_runs</strong></dt>
+</dl>
+<dl><dt><strong>all_page_specific_values</strong></dt>
+</dl>
+<dl><dt><strong>all_summary_values</strong></dt>
+</dl>
+<dl><dt><strong>current_page</strong></dt>
+</dl>
+<dl><dt><strong>current_page_run</strong></dt>
+</dl>
+<dl><dt><strong>failures</strong></dt>
+</dl>
+<dl><dt><strong>pages_that_failed</strong></dt>
+<dd><tt>Returns&nbsp;the&nbsp;set&nbsp;of&nbsp;failed&nbsp;pages.</tt></dd>
+</dl>
+<dl><dt><strong>pages_that_succeeded</strong></dt>
+<dd><tt>Returns&nbsp;the&nbsp;set&nbsp;of&nbsp;pages&nbsp;that&nbsp;succeeded.</tt></dd>
+</dl>
+<dl><dt><strong>pages_to_profiling_files</strong></dt>
+</dl>
+<dl><dt><strong>pages_to_profiling_files_cloud_url</strong></dt>
+</dl>
+<dl><dt><strong>serialized_trace_file_ids_to_paths</strong></dt>
+</dl>
+<dl><dt><strong>skipped_values</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.unittest_runner.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.unittest_runner.html
new file mode 100644
index 0000000..b34d2d2
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.testing.unittest_runner.html
@@ -0,0 +1,36 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.testing.unittest_runner</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.testing.html"><font color="#ffffff">testing</font></a>.unittest_runner</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/testing/unittest_runner.py">telemetry/testing/unittest_runner.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2015&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="os.html">os</a><br>
+</td><td width="25%" valign=top><a href="subprocess.html">subprocess</a><br>
+</td><td width="25%" valign=top><a href="sys.html">sys</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.util.html">telemetry.core.util</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-Run"><strong>Run</strong></a>(project_config, no_browser<font color="#909090">=False</font>, stream<font color="#909090">=None</font>)</dt></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.async_slice.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.async_slice.html
new file mode 100644
index 0000000..0ce0b23
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.async_slice.html
@@ -0,0 +1,85 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.timeline.async_slice</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.timeline.html"><font color="#ffffff">timeline</font></a>.async_slice</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/timeline/async_slice.py">telemetry/timeline/async_slice.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.timeline.event.html">telemetry.timeline.event</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.timeline.event.html#TimelineEvent">telemetry.timeline.event.TimelineEvent</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.timeline.async_slice.html#AsyncSlice">AsyncSlice</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="AsyncSlice">class <strong>AsyncSlice</strong></a>(<a href="telemetry.timeline.event.html#TimelineEvent">telemetry.timeline.event.TimelineEvent</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>An&nbsp;<a href="#AsyncSlice">AsyncSlice</a>&nbsp;represents&nbsp;an&nbsp;interval&nbsp;of&nbsp;time&nbsp;during&nbsp;which&nbsp;an<br>
+asynchronous&nbsp;operation&nbsp;is&nbsp;in&nbsp;progress.&nbsp;An&nbsp;<a href="#AsyncSlice">AsyncSlice</a>&nbsp;consumes&nbsp;no&nbsp;CPU&nbsp;time<br>
+itself&nbsp;and&nbsp;so&nbsp;is&nbsp;only&nbsp;associated&nbsp;with&nbsp;Threads&nbsp;at&nbsp;its&nbsp;start&nbsp;and&nbsp;end&nbsp;point.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.timeline.async_slice.html#AsyncSlice">AsyncSlice</a></dd>
+<dd><a href="telemetry.timeline.event.html#TimelineEvent">telemetry.timeline.event.TimelineEvent</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="AsyncSlice-AddSubSlice"><strong>AddSubSlice</strong></a>(self, sub_slice)</dt></dl>
+
+<dl><dt><a name="AsyncSlice-IterEventsInThisContainerRecrusively"><strong>IterEventsInThisContainerRecrusively</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="AsyncSlice-__init__"><strong>__init__</strong></a>(self, category, name, timestamp, args<font color="#909090">=None</font>, duration<font color="#909090">=0</font>, start_thread<font color="#909090">=None</font>, end_thread<font color="#909090">=None</font>, thread_start<font color="#909090">=None</font>, thread_duration<font color="#909090">=None</font>)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.timeline.event.html#TimelineEvent">telemetry.timeline.event.TimelineEvent</a>:<br>
+<dl><dt><a name="AsyncSlice-__repr__"><strong>__repr__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.timeline.event.html#TimelineEvent">telemetry.timeline.event.TimelineEvent</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>end</strong></dt>
+</dl>
+<dl><dt><strong>has_thread_timestamps</strong></dt>
+</dl>
+<dl><dt><strong>thread_end</strong></dt>
+<dd><tt>Thread-specific&nbsp;CPU&nbsp;time&nbsp;when&nbsp;this&nbsp;event&nbsp;ended.<br>
+&nbsp;<br>
+May&nbsp;be&nbsp;None&nbsp;if&nbsp;the&nbsp;trace&nbsp;event&nbsp;didn't&nbsp;have&nbsp;thread&nbsp;time&nbsp;data.</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.bounds.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.bounds.html
new file mode 100644
index 0000000..6c705b4
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.bounds.html
@@ -0,0 +1,88 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.timeline.bounds</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.timeline.html"><font color="#ffffff">timeline</font></a>.bounds</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/timeline/bounds.py">telemetry/timeline/bounds.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.timeline.bounds.html#Bounds">Bounds</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="Bounds">class <strong>Bounds</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Represents&nbsp;a&nbsp;min-max&nbsp;bounds.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="Bounds-AddBounds"><strong>AddBounds</strong></a>(self, bounds)</dt></dl>
+
+<dl><dt><a name="Bounds-AddEvent"><strong>AddEvent</strong></a>(self, event)</dt></dl>
+
+<dl><dt><a name="Bounds-AddValue"><strong>AddValue</strong></a>(self, value)</dt></dl>
+
+<dl><dt><a name="Bounds-Contains"><strong>Contains</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="Bounds-ContainsInterval"><strong>ContainsInterval</strong></a>(self, start, end)</dt></dl>
+
+<dl><dt><a name="Bounds-Intersects"><strong>Intersects</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="Bounds-Reset"><strong>Reset</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="Bounds-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="Bounds-__repr__"><strong>__repr__</strong></a>(self)</dt></dl>
+
+<hr>
+Static methods defined here:<br>
+<dl><dt><a name="Bounds-CompareByMinTimes"><strong>CompareByMinTimes</strong></a>(a, b)</dt></dl>
+
+<dl><dt><a name="Bounds-CreateFromEvent"><strong>CreateFromEvent</strong></a>(event)</dt></dl>
+
+<dl><dt><a name="Bounds-GetOverlap"><strong>GetOverlap</strong></a>(first_bounds_min, first_bounds_max, second_bounds_min, second_bounds_max)</dt></dl>
+
+<dl><dt><a name="Bounds-GetOverlapBetweenBounds"><strong>GetOverlapBetweenBounds</strong></a>(first_bounds, second_bounds)</dt><dd><tt>Compute&nbsp;the&nbsp;overlap&nbsp;duration&nbsp;between&nbsp;first_bounds&nbsp;and&nbsp;second_bounds.</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>bounds</strong></dt>
+</dl>
+<dl><dt><strong>center</strong></dt>
+</dl>
+<dl><dt><strong>is_empty</strong></dt>
+</dl>
+<dl><dt><strong>max</strong></dt>
+</dl>
+<dl><dt><strong>min</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.counter.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.counter.html
new file mode 100644
index 0000000..321bffe
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.counter.html
@@ -0,0 +1,165 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.timeline.counter</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.timeline.html"><font color="#ffffff">timeline</font></a>.counter</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/timeline/counter.py">telemetry/timeline/counter.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.timeline.event_container.html">telemetry.timeline.event_container</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.timeline.counter.html#CounterSample">CounterSample</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="telemetry.timeline.event_container.html#TimelineEventContainer">telemetry.timeline.event_container.TimelineEventContainer</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.timeline.counter.html#Counter">Counter</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="Counter">class <strong>Counter</strong></a>(<a href="telemetry.timeline.event_container.html#TimelineEventContainer">telemetry.timeline.event_container.TimelineEventContainer</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Stores&nbsp;all&nbsp;the&nbsp;samples&nbsp;for&nbsp;a&nbsp;given&nbsp;counter.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.timeline.counter.html#Counter">Counter</a></dd>
+<dd><a href="telemetry.timeline.event_container.html#TimelineEventContainer">telemetry.timeline.event_container.TimelineEventContainer</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="Counter-FinalizeImport"><strong>FinalizeImport</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="Counter-IterChildContainers"><strong>IterChildContainers</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="Counter-IterEventsInThisContainer"><strong>IterEventsInThisContainer</strong></a>(self, event_type_predicate, event_predicate)</dt></dl>
+
+<dl><dt><a name="Counter-__init__"><strong>__init__</strong></a>(self, parent, category, name)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>num_samples</strong></dt>
+</dl>
+<dl><dt><strong>num_series</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.timeline.event_container.html#TimelineEventContainer">telemetry.timeline.event_container.TimelineEventContainer</a>:<br>
+<dl><dt><a name="Counter-GetAllEvents"><strong>GetAllEvents</strong></a>(self, recursive<font color="#909090">=True</font>)</dt><dd><tt>#&nbsp;List&nbsp;versions.&nbsp;These&nbsp;should&nbsp;always&nbsp;be&nbsp;simple&nbsp;expressions&nbsp;that&nbsp;list()&nbsp;on<br>
+#&nbsp;an&nbsp;underlying&nbsp;iter&nbsp;method.</tt></dd></dl>
+
+<dl><dt><a name="Counter-GetAllEventsOfName"><strong>GetAllEventsOfName</strong></a>(self, name, recursive<font color="#909090">=True</font>)</dt></dl>
+
+<dl><dt><a name="Counter-GetAllToplevelSlicesOfName"><strong>GetAllToplevelSlicesOfName</strong></a>(self, name, recursive<font color="#909090">=True</font>)</dt></dl>
+
+<dl><dt><a name="Counter-IterAllAsyncSlicesOfName"><strong>IterAllAsyncSlicesOfName</strong></a>(self, name, recursive<font color="#909090">=True</font>)</dt></dl>
+
+<dl><dt><a name="Counter-IterAllAsyncSlicesStartsWithName"><strong>IterAllAsyncSlicesStartsWithName</strong></a>(self, name, recursive<font color="#909090">=True</font>)</dt></dl>
+
+<dl><dt><a name="Counter-IterAllEvents"><strong>IterAllEvents</strong></a>(self, recursive<font color="#909090">=True</font>, event_type_predicate<font color="#909090">=&lt;function &lt;lambda&gt;&gt;</font>, event_predicate<font color="#909090">=&lt;function &lt;lambda&gt;&gt;</font>)</dt><dd><tt>Iterates&nbsp;all&nbsp;events&nbsp;in&nbsp;this&nbsp;container,&nbsp;pre-filtered&nbsp;by&nbsp;two&nbsp;predicates.<br>
+&nbsp;<br>
+Only&nbsp;events&nbsp;with&nbsp;a&nbsp;type&nbsp;matching&nbsp;event_type_predicate&nbsp;AND&nbsp;matching&nbsp;event<br>
+event_predicate&nbsp;will&nbsp;be&nbsp;yielded.<br>
+&nbsp;<br>
+event_type_predicate&nbsp;is&nbsp;given&nbsp;an&nbsp;actual&nbsp;type&nbsp;<a href="__builtin__.html#object">object</a>,&nbsp;e.g.:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;event_type_predicate(slice_module.Slice)<br>
+&nbsp;<br>
+event_predicate&nbsp;is&nbsp;given&nbsp;actual&nbsp;events:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;event_predicate(thread.slices[7])</tt></dd></dl>
+
+<dl><dt><a name="Counter-IterAllEventsOfName"><strong>IterAllEventsOfName</strong></a>(self, name, recursive<font color="#909090">=True</font>)</dt><dd><tt>#&nbsp;Helper&nbsp;functions&nbsp;for&nbsp;finding&nbsp;common&nbsp;kinds&nbsp;of&nbsp;events.&nbsp;Must&nbsp;always&nbsp;take&nbsp;an<br>
+#&nbsp;optinal&nbsp;recurisve&nbsp;parameter&nbsp;and&nbsp;be&nbsp;implemented&nbsp;in&nbsp;terms&nbsp;fo&nbsp;IterAllEvents.</tt></dd></dl>
+
+<dl><dt><a name="Counter-IterAllFlowEvents"><strong>IterAllFlowEvents</strong></a>(self, recursive<font color="#909090">=True</font>)</dt></dl>
+
+<dl><dt><a name="Counter-IterAllSlices"><strong>IterAllSlices</strong></a>(self, recursive<font color="#909090">=True</font>)</dt></dl>
+
+<dl><dt><a name="Counter-IterAllSlicesInRange"><strong>IterAllSlicesInRange</strong></a>(self, start, end, recursive<font color="#909090">=True</font>)</dt></dl>
+
+<dl><dt><a name="Counter-IterAllSlicesOfName"><strong>IterAllSlicesOfName</strong></a>(self, name, recursive<font color="#909090">=True</font>)</dt></dl>
+
+<dl><dt><a name="Counter-IterAllToplevelSlicesOfName"><strong>IterAllToplevelSlicesOfName</strong></a>(self, name, recursive<font color="#909090">=True</font>)</dt></dl>
+
+<hr>
+Static methods inherited from <a href="telemetry.timeline.event_container.html#TimelineEventContainer">telemetry.timeline.event_container.TimelineEventContainer</a>:<br>
+<dl><dt><a name="Counter-IsAsyncSlice"><strong>IsAsyncSlice</strong></a>(t)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.timeline.event_container.html#TimelineEventContainer">telemetry.timeline.event_container.TimelineEventContainer</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="CounterSample">class <strong>CounterSample</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>#&nbsp;Doesn't&nbsp;inherit&nbsp;from&nbsp;TimelineEvent&nbsp;because&nbsp;its&nbsp;only&nbsp;a&nbsp;temporary&nbsp;wrapper&nbsp;of&nbsp;a<br>
+#&nbsp;counter&nbsp;sample&nbsp;into&nbsp;an&nbsp;event.&nbsp;During&nbsp;stable&nbsp;operation,&nbsp;the&nbsp;samples&nbsp;are&nbsp;stored<br>
+#&nbsp;a&nbsp;dense&nbsp;array&nbsp;of&nbsp;values&nbsp;rather&nbsp;than&nbsp;in&nbsp;the&nbsp;long-form&nbsp;done&nbsp;by&nbsp;an&nbsp;Event.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="CounterSample-__init__"><strong>__init__</strong></a>(self, counter, sample_index)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>category</strong></dt>
+</dl>
+<dl><dt><strong>duration</strong></dt>
+</dl>
+<dl><dt><strong>end</strong></dt>
+</dl>
+<dl><dt><strong>name</strong></dt>
+</dl>
+<dl><dt><strong>start</strong></dt>
+</dl>
+<dl><dt><strong>thread_duration</strong></dt>
+</dl>
+<dl><dt><strong>thread_end</strong></dt>
+</dl>
+<dl><dt><strong>thread_start</strong></dt>
+</dl>
+<dl><dt><strong>value</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.event.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.event.html
new file mode 100644
index 0000000..659e231
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.event.html
@@ -0,0 +1,70 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.timeline.event</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.timeline.html"><font color="#ffffff">timeline</font></a>.event</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/timeline/event.py">telemetry/timeline/event.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.timeline.event.html#TimelineEvent">TimelineEvent</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TimelineEvent">class <strong>TimelineEvent</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Represents&nbsp;a&nbsp;timeline&nbsp;event.<br>
+&nbsp;<br>
+thread_start,&nbsp;thread_duration&nbsp;and&nbsp;thread_end&nbsp;are&nbsp;the&nbsp;start&nbsp;time,&nbsp;duration<br>
+and&nbsp;end&nbsp;time&nbsp;of&nbsp;this&nbsp;event&nbsp;as&nbsp;measured&nbsp;by&nbsp;the&nbsp;thread-specific&nbsp;CPU&nbsp;clock<br>
+(ticking&nbsp;when&nbsp;the&nbsp;thread&nbsp;is&nbsp;actually&nbsp;scheduled).&nbsp;Thread&nbsp;time&nbsp;is&nbsp;optional<br>
+on&nbsp;trace&nbsp;events&nbsp;and&nbsp;the&nbsp;corresponding&nbsp;attributes&nbsp;in&nbsp;<a href="#TimelineEvent">TimelineEvent</a>&nbsp;will&nbsp;be<br>
+set&nbsp;to&nbsp;None&nbsp;(not&nbsp;0)&nbsp;if&nbsp;not&nbsp;present.&nbsp;Users&nbsp;of&nbsp;this&nbsp;class&nbsp;need&nbsp;to&nbsp;properly<br>
+handle&nbsp;this&nbsp;case.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="TimelineEvent-__init__"><strong>__init__</strong></a>(self, category, name, start, duration, thread_start<font color="#909090">=None</font>, thread_duration<font color="#909090">=None</font>, args<font color="#909090">=None</font>)</dt></dl>
+
+<dl><dt><a name="TimelineEvent-__repr__"><strong>__repr__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>end</strong></dt>
+</dl>
+<dl><dt><strong>has_thread_timestamps</strong></dt>
+</dl>
+<dl><dt><strong>thread_end</strong></dt>
+<dd><tt>Thread-specific&nbsp;CPU&nbsp;time&nbsp;when&nbsp;this&nbsp;event&nbsp;ended.<br>
+&nbsp;<br>
+May&nbsp;be&nbsp;None&nbsp;if&nbsp;the&nbsp;trace&nbsp;event&nbsp;didn't&nbsp;have&nbsp;thread&nbsp;time&nbsp;data.</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.event_container.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.event_container.html
new file mode 100644
index 0000000..11f6ea2
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.event_container.html
@@ -0,0 +1,118 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.timeline.event_container</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.timeline.html"><font color="#ffffff">timeline</font></a>.event_container</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/timeline/event_container.py">telemetry/timeline/event_container.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.timeline.async_slice.html">telemetry.timeline.async_slice</a><br>
+</td><td width="25%" valign=top><a href="telemetry.timeline.flow_event.html">telemetry.timeline.flow_event</a><br>
+</td><td width="25%" valign=top><a href="telemetry.timeline.slice.html">telemetry.timeline.slice</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.timeline.event_container.html#TimelineEventContainer">TimelineEventContainer</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TimelineEventContainer">class <strong>TimelineEventContainer</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Represents&nbsp;a&nbsp;container&nbsp;for&nbsp;events.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="TimelineEventContainer-GetAllEvents"><strong>GetAllEvents</strong></a>(self, recursive<font color="#909090">=True</font>)</dt><dd><tt>#&nbsp;List&nbsp;versions.&nbsp;These&nbsp;should&nbsp;always&nbsp;be&nbsp;simple&nbsp;expressions&nbsp;that&nbsp;list()&nbsp;on<br>
+#&nbsp;an&nbsp;underlying&nbsp;iter&nbsp;method.</tt></dd></dl>
+
+<dl><dt><a name="TimelineEventContainer-GetAllEventsOfName"><strong>GetAllEventsOfName</strong></a>(self, name, recursive<font color="#909090">=True</font>)</dt></dl>
+
+<dl><dt><a name="TimelineEventContainer-GetAllToplevelSlicesOfName"><strong>GetAllToplevelSlicesOfName</strong></a>(self, name, recursive<font color="#909090">=True</font>)</dt></dl>
+
+<dl><dt><a name="TimelineEventContainer-IterAllAsyncSlicesOfName"><strong>IterAllAsyncSlicesOfName</strong></a>(self, name, recursive<font color="#909090">=True</font>)</dt></dl>
+
+<dl><dt><a name="TimelineEventContainer-IterAllAsyncSlicesStartsWithName"><strong>IterAllAsyncSlicesStartsWithName</strong></a>(self, name, recursive<font color="#909090">=True</font>)</dt></dl>
+
+<dl><dt><a name="TimelineEventContainer-IterAllEvents"><strong>IterAllEvents</strong></a>(self, recursive<font color="#909090">=True</font>, event_type_predicate<font color="#909090">=&lt;function &lt;lambda&gt;&gt;</font>, event_predicate<font color="#909090">=&lt;function &lt;lambda&gt;&gt;</font>)</dt><dd><tt>Iterates&nbsp;all&nbsp;events&nbsp;in&nbsp;this&nbsp;container,&nbsp;pre-filtered&nbsp;by&nbsp;two&nbsp;predicates.<br>
+&nbsp;<br>
+Only&nbsp;events&nbsp;with&nbsp;a&nbsp;type&nbsp;matching&nbsp;event_type_predicate&nbsp;AND&nbsp;matching&nbsp;event<br>
+event_predicate&nbsp;will&nbsp;be&nbsp;yielded.<br>
+&nbsp;<br>
+event_type_predicate&nbsp;is&nbsp;given&nbsp;an&nbsp;actual&nbsp;type&nbsp;<a href="__builtin__.html#object">object</a>,&nbsp;e.g.:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;event_type_predicate(slice_module.Slice)<br>
+&nbsp;<br>
+event_predicate&nbsp;is&nbsp;given&nbsp;actual&nbsp;events:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;event_predicate(thread.slices[7])</tt></dd></dl>
+
+<dl><dt><a name="TimelineEventContainer-IterAllEventsOfName"><strong>IterAllEventsOfName</strong></a>(self, name, recursive<font color="#909090">=True</font>)</dt><dd><tt>#&nbsp;Helper&nbsp;functions&nbsp;for&nbsp;finding&nbsp;common&nbsp;kinds&nbsp;of&nbsp;events.&nbsp;Must&nbsp;always&nbsp;take&nbsp;an<br>
+#&nbsp;optinal&nbsp;recurisve&nbsp;parameter&nbsp;and&nbsp;be&nbsp;implemented&nbsp;in&nbsp;terms&nbsp;fo&nbsp;IterAllEvents.</tt></dd></dl>
+
+<dl><dt><a name="TimelineEventContainer-IterAllFlowEvents"><strong>IterAllFlowEvents</strong></a>(self, recursive<font color="#909090">=True</font>)</dt></dl>
+
+<dl><dt><a name="TimelineEventContainer-IterAllSlices"><strong>IterAllSlices</strong></a>(self, recursive<font color="#909090">=True</font>)</dt></dl>
+
+<dl><dt><a name="TimelineEventContainer-IterAllSlicesInRange"><strong>IterAllSlicesInRange</strong></a>(self, start, end, recursive<font color="#909090">=True</font>)</dt></dl>
+
+<dl><dt><a name="TimelineEventContainer-IterAllSlicesOfName"><strong>IterAllSlicesOfName</strong></a>(self, name, recursive<font color="#909090">=True</font>)</dt></dl>
+
+<dl><dt><a name="TimelineEventContainer-IterAllToplevelSlicesOfName"><strong>IterAllToplevelSlicesOfName</strong></a>(self, name, recursive<font color="#909090">=True</font>)</dt></dl>
+
+<dl><dt><a name="TimelineEventContainer-IterChildContainers"><strong>IterChildContainers</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TimelineEventContainer-IterEventsInThisContainer"><strong>IterEventsInThisContainer</strong></a>(self, event_type_predicate, event_predicate)</dt><dd><tt>Iterates&nbsp;all&nbsp;the&nbsp;TimelineEvents&nbsp;in&nbsp;this&nbsp;container.<br>
+&nbsp;<br>
+Only&nbsp;events&nbsp;with&nbsp;a&nbsp;type&nbsp;matching&nbsp;event_type_predicate&nbsp;AND&nbsp;matching&nbsp;event<br>
+event_predicate&nbsp;will&nbsp;be&nbsp;yielded.<br>
+&nbsp;<br>
+event_type_predicate&nbsp;is&nbsp;given&nbsp;an&nbsp;actual&nbsp;type&nbsp;<a href="__builtin__.html#object">object</a>,&nbsp;e.g.:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;event_type_predicate(slice_module.Slice)<br>
+&nbsp;<br>
+event_predicate&nbsp;is&nbsp;given&nbsp;actual&nbsp;events:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;event_predicate(thread.slices[7])<br>
+&nbsp;<br>
+DO&nbsp;NOT&nbsp;ASSUME&nbsp;that&nbsp;the&nbsp;event_type_predicate&nbsp;will&nbsp;be&nbsp;called&nbsp;for&nbsp;every&nbsp;event<br>
+found.&nbsp;The&nbsp;relative&nbsp;calling&nbsp;order&nbsp;of&nbsp;the&nbsp;two&nbsp;is&nbsp;left&nbsp;up&nbsp;to&nbsp;the&nbsp;implementer<br>
+of&nbsp;the&nbsp;method.</tt></dd></dl>
+
+<dl><dt><a name="TimelineEventContainer-__init__"><strong>__init__</strong></a>(self, name, parent)</dt></dl>
+
+<hr>
+Static methods defined here:<br>
+<dl><dt><a name="TimelineEventContainer-IsAsyncSlice"><strong>IsAsyncSlice</strong></a>(t)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.flow_event.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.flow_event.html
new file mode 100644
index 0000000..9dbdf97
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.flow_event.html
@@ -0,0 +1,80 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.timeline.flow_event</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.timeline.html"><font color="#ffffff">timeline</font></a>.flow_event</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/timeline/flow_event.py">telemetry/timeline/flow_event.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.timeline.event.html">telemetry.timeline.event</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.timeline.event.html#TimelineEvent">telemetry.timeline.event.TimelineEvent</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.timeline.flow_event.html#FlowEvent">FlowEvent</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="FlowEvent">class <strong>FlowEvent</strong></a>(<a href="telemetry.timeline.event.html#TimelineEvent">telemetry.timeline.event.TimelineEvent</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;<a href="#FlowEvent">FlowEvent</a>&nbsp;represents&nbsp;an&nbsp;interval&nbsp;of&nbsp;time&nbsp;plus&nbsp;parameters&nbsp;associated<br>
+with&nbsp;that&nbsp;interval.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.timeline.flow_event.html#FlowEvent">FlowEvent</a></dd>
+<dd><a href="telemetry.timeline.event.html#TimelineEvent">telemetry.timeline.event.TimelineEvent</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="FlowEvent-__init__"><strong>__init__</strong></a>(self, category, event_id, name, start, args<font color="#909090">=None</font>)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.timeline.event.html#TimelineEvent">telemetry.timeline.event.TimelineEvent</a>:<br>
+<dl><dt><a name="FlowEvent-__repr__"><strong>__repr__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.timeline.event.html#TimelineEvent">telemetry.timeline.event.TimelineEvent</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>end</strong></dt>
+</dl>
+<dl><dt><strong>has_thread_timestamps</strong></dt>
+</dl>
+<dl><dt><strong>thread_end</strong></dt>
+<dd><tt>Thread-specific&nbsp;CPU&nbsp;time&nbsp;when&nbsp;this&nbsp;event&nbsp;ended.<br>
+&nbsp;<br>
+May&nbsp;be&nbsp;None&nbsp;if&nbsp;the&nbsp;trace&nbsp;event&nbsp;didn't&nbsp;have&nbsp;thread&nbsp;time&nbsp;data.</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.html
new file mode 100644
index 0000000..b7b797c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.html
@@ -0,0 +1,56 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: package telemetry.timeline</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.timeline</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/timeline/__init__.py">telemetry/timeline/__init__.py</a></font></td></tr></table>
+    <p></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Package Contents</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.timeline.async_slice.html">async_slice</a><br>
+<a href="telemetry.timeline.bounds.html">bounds</a><br>
+<a href="telemetry.timeline.bounds_unittest.html">bounds_unittest</a><br>
+<a href="telemetry.timeline.counter.html">counter</a><br>
+<a href="telemetry.timeline.counter_unittest.html">counter_unittest</a><br>
+<a href="telemetry.timeline.event.html">event</a><br>
+<a href="telemetry.timeline.event_container.html">event_container</a><br>
+<a href="telemetry.timeline.event_unittest.html">event_unittest</a><br>
+<a href="telemetry.timeline.flow_event.html">flow_event</a><br>
+</td><td width="25%" valign=top><a href="telemetry.timeline.importer.html">importer</a><br>
+<a href="telemetry.timeline.inspector_importer.html">inspector_importer</a><br>
+<a href="telemetry.timeline.inspector_importer_unittest.html">inspector_importer_unittest</a><br>
+<a href="telemetry.timeline.memory_dump_event.html">memory_dump_event</a><br>
+<a href="telemetry.timeline.memory_dump_event_unittest.html">memory_dump_event_unittest</a><br>
+<a href="telemetry.timeline.model.html">model</a><br>
+<a href="telemetry.timeline.model_unittest.html">model_unittest</a><br>
+<a href="telemetry.timeline.process.html">process</a><br>
+<a href="telemetry.timeline.sample.html">sample</a><br>
+</td><td width="25%" valign=top><a href="telemetry.timeline.slice.html">slice</a><br>
+<a href="telemetry.timeline.slice_unittest.html">slice_unittest</a><br>
+<a href="telemetry.timeline.surface_flinger_importer.html">surface_flinger_importer</a><br>
+<a href="telemetry.timeline.tab_id_importer.html">tab_id_importer</a><br>
+<a href="telemetry.timeline.tab_id_importer_unittest.html">tab_id_importer_unittest</a><br>
+<a href="telemetry.timeline.thread.html">thread</a><br>
+<a href="telemetry.timeline.thread_unittest.html">thread_unittest</a><br>
+<a href="telemetry.timeline.trace_data.html">trace_data</a><br>
+<a href="telemetry.timeline.trace_data_unittest.html">trace_data_unittest</a><br>
+</td><td width="25%" valign=top><a href="telemetry.timeline.trace_event_importer.html">trace_event_importer</a><br>
+<a href="telemetry.timeline.trace_event_importer_unittest.html">trace_event_importer_unittest</a><br>
+<a href="telemetry.timeline.tracing_category_filter.html">tracing_category_filter</a><br>
+<a href="telemetry.timeline.tracing_category_filter_unittest.html">tracing_category_filter_unittest</a><br>
+<a href="telemetry.timeline.tracing_config.html">tracing_config</a><br>
+<a href="telemetry.timeline.tracing_config_unittest.html">tracing_config_unittest</a><br>
+<a href="telemetry.timeline.tracing_options.html">tracing_options</a><br>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.importer.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.importer.html
new file mode 100644
index 0000000..b330a1b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.importer.html
@@ -0,0 +1,61 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.timeline.importer</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.timeline.html"><font color="#ffffff">timeline</font></a>.importer</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/timeline/importer.py">telemetry/timeline/importer.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.timeline.importer.html#TimelineImporter">TimelineImporter</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TimelineImporter">class <strong>TimelineImporter</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Reads&nbsp;TraceData&nbsp;and&nbsp;populates&nbsp;timeline&nbsp;model&nbsp;with&nbsp;what&nbsp;it&nbsp;finds.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="TimelineImporter-FinalizeImport"><strong>FinalizeImport</strong></a>(self)</dt><dd><tt>Called&nbsp;after&nbsp;all&nbsp;other&nbsp;importers&nbsp;for&nbsp;the&nbsp;model&nbsp;are&nbsp;run.</tt></dd></dl>
+
+<dl><dt><a name="TimelineImporter-ImportEvents"><strong>ImportEvents</strong></a>(self)</dt><dd><tt>Processes&nbsp;the&nbsp;event&nbsp;data&nbsp;in&nbsp;the&nbsp;wrapper&nbsp;and&nbsp;creates&nbsp;and&nbsp;adds<br>
+new&nbsp;timeline&nbsp;events&nbsp;to&nbsp;the&nbsp;model</tt></dd></dl>
+
+<dl><dt><a name="TimelineImporter-__init__"><strong>__init__</strong></a>(self, model, trace_data, import_order)</dt></dl>
+
+<hr>
+Static methods defined here:<br>
+<dl><dt><a name="TimelineImporter-GetSupportedPart"><strong>GetSupportedPart</strong></a>()</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.inspector_importer.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.inspector_importer.html
new file mode 100644
index 0000000..fe808e8
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.inspector_importer.html
@@ -0,0 +1,77 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.timeline.inspector_importer</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.timeline.html"><font color="#ffffff">timeline</font></a>.inspector_importer</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/timeline/inspector_importer.py">telemetry/timeline/inspector_importer.py</a></font></td></tr></table>
+    <p><tt>Imports&nbsp;event&nbsp;data&nbsp;obtained&nbsp;from&nbsp;the&nbsp;inspector's&nbsp;timeline.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.timeline.importer.html">telemetry.timeline.importer</a><br>
+</td><td width="25%" valign=top><a href="telemetry.timeline.thread.html">telemetry.timeline.thread</a><br>
+</td><td width="25%" valign=top><a href="telemetry.timeline.trace_data.html">telemetry.timeline.trace_data</a><br>
+</td><td width="25%" valign=top><a href="telemetry.timeline.slice.html">telemetry.timeline.slice</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.timeline.importer.html#TimelineImporter">telemetry.timeline.importer.TimelineImporter</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.timeline.inspector_importer.html#InspectorTimelineImporter">InspectorTimelineImporter</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="InspectorTimelineImporter">class <strong>InspectorTimelineImporter</strong></a>(<a href="telemetry.timeline.importer.html#TimelineImporter">telemetry.timeline.importer.TimelineImporter</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.timeline.inspector_importer.html#InspectorTimelineImporter">InspectorTimelineImporter</a></dd>
+<dd><a href="telemetry.timeline.importer.html#TimelineImporter">telemetry.timeline.importer.TimelineImporter</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="InspectorTimelineImporter-FinalizeImport"><strong>FinalizeImport</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="InspectorTimelineImporter-ImportEvents"><strong>ImportEvents</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="InspectorTimelineImporter-__init__"><strong>__init__</strong></a>(self, model, trace_data)</dt></dl>
+
+<hr>
+Static methods defined here:<br>
+<dl><dt><a name="InspectorTimelineImporter-AddRawEventToThreadRecursive"><strong>AddRawEventToThreadRecursive</strong></a>(thread, raw_inspector_event)</dt></dl>
+
+<dl><dt><a name="InspectorTimelineImporter-GetSupportedPart"><strong>GetSupportedPart</strong></a>()</dt></dl>
+
+<dl><dt><a name="InspectorTimelineImporter-RawEventToTimelineEvent"><strong>RawEventToTimelineEvent</strong></a>(raw_inspector_event)</dt><dd><tt>Converts&nbsp;raw_inspector_event&nbsp;to&nbsp;TimelineEvent.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.timeline.importer.html#TimelineImporter">telemetry.timeline.importer.TimelineImporter</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.memory_dump_event.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.memory_dump_event.html
new file mode 100644
index 0000000..52c8034
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.memory_dump_event.html
@@ -0,0 +1,231 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.timeline.memory_dump_event</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.timeline.html"><font color="#ffffff">timeline</font></a>.memory_dump_event</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/timeline/memory_dump_event.py">telemetry/timeline/memory_dump_event.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2015&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="posixpath.html">posixpath</a><br>
+</td><td width="25%" valign=top><a href="re.html">re</a><br>
+</td><td width="25%" valign=top><a href="telemetry.timeline.event.html">telemetry.timeline.event</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.timeline.memory_dump_event.html#GlobalMemoryDump">GlobalMemoryDump</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.timeline.memory_dump_event.html#MemoryBucket">MemoryBucket</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.timeline.memory_dump_event.html#MmapCategory">MmapCategory</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="telemetry.timeline.event.html#TimelineEvent">telemetry.timeline.event.TimelineEvent</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.timeline.memory_dump_event.html#ProcessMemoryDumpEvent">ProcessMemoryDumpEvent</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="GlobalMemoryDump">class <strong>GlobalMemoryDump</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Object&nbsp;to&nbsp;aggregate&nbsp;individual&nbsp;process&nbsp;dumps&nbsp;with&nbsp;the&nbsp;same&nbsp;dump&nbsp;id.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;process_dumps:&nbsp;A&nbsp;sequence&nbsp;of&nbsp;<a href="#ProcessMemoryDumpEvent">ProcessMemoryDumpEvent</a>&nbsp;objects,&nbsp;all&nbsp;sharing<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;the&nbsp;same&nbsp;global&nbsp;dump&nbsp;id.<br>
+&nbsp;<br>
+Attributes:<br>
+&nbsp;&nbsp;dump_id:&nbsp;A&nbsp;string&nbsp;identifying&nbsp;this&nbsp;dump.<br>
+&nbsp;&nbsp;has_mmaps:&nbsp;True&nbsp;if&nbsp;the&nbsp;memory&nbsp;dump&nbsp;has&nbsp;mmaps&nbsp;information.&nbsp;If&nbsp;False&nbsp;then<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GetMemoryUsage&nbsp;will&nbsp;report&nbsp;all&nbsp;zeros.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="GlobalMemoryDump-GetMemoryUsage"><strong>GetMemoryUsage</strong></a>(self)</dt><dd><tt>Get&nbsp;the&nbsp;aggregated&nbsp;memory&nbsp;usage&nbsp;over&nbsp;all&nbsp;processes&nbsp;in&nbsp;this&nbsp;dump.</tt></dd></dl>
+
+<dl><dt><a name="GlobalMemoryDump-IterProcessMemoryDumps"><strong>IterProcessMemoryDumps</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="GlobalMemoryDump-__init__"><strong>__init__</strong></a>(self, process_dumps)</dt></dl>
+
+<dl><dt><a name="GlobalMemoryDump-__repr__"><strong>__repr__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>duration</strong></dt>
+</dl>
+<dl><dt><strong>end</strong></dt>
+</dl>
+<dl><dt><strong>start</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="MemoryBucket">class <strong>MemoryBucket</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Simple&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;to&nbsp;hold&nbsp;and&nbsp;aggregate&nbsp;memory&nbsp;values.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="MemoryBucket-AddRegion"><strong>AddRegion</strong></a>(self, byte_stats)</dt></dl>
+
+<dl><dt><a name="MemoryBucket-GetValue"><strong>GetValue</strong></a>(self, name)</dt></dl>
+
+<dl><dt><a name="MemoryBucket-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="MemoryBucket-__repr__"><strong>__repr__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="MmapCategory">class <strong>MmapCategory</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="MmapCategory-GetMatchingChild"><strong>GetMatchingChild</strong></a>(self, mapped_file)</dt><dd><tt>Get&nbsp;the&nbsp;first&nbsp;matching&nbsp;sub-category&nbsp;for&nbsp;a&nbsp;given&nbsp;mapped&nbsp;file.<br>
+&nbsp;<br>
+Returns&nbsp;None&nbsp;if&nbsp;the&nbsp;category&nbsp;has&nbsp;no&nbsp;children,&nbsp;or&nbsp;the&nbsp;DefaultCategory&nbsp;if<br>
+it&nbsp;does&nbsp;have&nbsp;children&nbsp;but&nbsp;none&nbsp;of&nbsp;them&nbsp;match.</tt></dd></dl>
+
+<dl><dt><a name="MmapCategory-Match"><strong>Match</strong></a>(self, mapped_file)</dt><dd><tt>Test&nbsp;whether&nbsp;a&nbsp;mapped&nbsp;file&nbsp;matches&nbsp;this&nbsp;category.</tt></dd></dl>
+
+<dl><dt><a name="MmapCategory-__init__"><strong>__init__</strong></a>(self, name, file_pattern, children<font color="#909090">=None</font>)</dt><dd><tt>A&nbsp;(sub)category&nbsp;for&nbsp;classifying&nbsp;memory&nbsp;maps.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;name:&nbsp;A&nbsp;string&nbsp;to&nbsp;identify&nbsp;the&nbsp;category.<br>
+&nbsp;&nbsp;file_pattern:&nbsp;A&nbsp;regex&nbsp;pattern,&nbsp;the&nbsp;category&nbsp;will&nbsp;aggregate&nbsp;memory&nbsp;usage<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;all&nbsp;mapped&nbsp;files&nbsp;matching&nbsp;this&nbsp;pattern.<br>
+&nbsp;&nbsp;children:&nbsp;A&nbsp;list&nbsp;of&nbsp;<a href="#MmapCategory">MmapCategory</a>&nbsp;objects,&nbsp;used&nbsp;to&nbsp;sub-categorize&nbsp;memory<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;usage.</tt></dd></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="MmapCategory-DefaultCategory"><strong>DefaultCategory</strong></a>(cls)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>An&nbsp;implicit&nbsp;'Others'&nbsp;match-all&nbsp;category&nbsp;with&nbsp;no&nbsp;children.</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ProcessMemoryDumpEvent">class <strong>ProcessMemoryDumpEvent</strong></a>(<a href="telemetry.timeline.event.html#TimelineEvent">telemetry.timeline.event.TimelineEvent</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;memory&nbsp;dump&nbsp;event&nbsp;belonging&nbsp;to&nbsp;a&nbsp;single&nbsp;timeline.Process&nbsp;<a href="__builtin__.html#object">object</a>.<br>
+&nbsp;<br>
+It's&nbsp;a&nbsp;subclass&nbsp;of&nbsp;telemetry's&nbsp;<a href="telemetry.timeline.event.html#TimelineEvent">TimelineEvent</a>&nbsp;so&nbsp;it&nbsp;can&nbsp;be&nbsp;included&nbsp;in<br>
+the&nbsp;stream&nbsp;of&nbsp;events&nbsp;contained&nbsp;in&nbsp;timeline.model&nbsp;objects,&nbsp;and&nbsp;have&nbsp;its<br>
+timing&nbsp;correlated&nbsp;with&nbsp;that&nbsp;of&nbsp;other&nbsp;events&nbsp;in&nbsp;the&nbsp;model.<br>
+&nbsp;<br>
+Properties:<br>
+&nbsp;&nbsp;dump_id:&nbsp;A&nbsp;string&nbsp;to&nbsp;identify&nbsp;events&nbsp;belonging&nbsp;to&nbsp;the&nbsp;same&nbsp;global&nbsp;dump.<br>
+&nbsp;&nbsp;process:&nbsp;The&nbsp;timeline.Process&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;that&nbsp;owns&nbsp;this&nbsp;memory&nbsp;dump&nbsp;event.<br>
+&nbsp;&nbsp;has_mmaps:&nbsp;True&nbsp;if&nbsp;the&nbsp;memory&nbsp;dump&nbsp;has&nbsp;mmaps&nbsp;information.&nbsp;If&nbsp;False&nbsp;then<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GetMemoryUsage&nbsp;will&nbsp;report&nbsp;all&nbsp;zeros.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.timeline.memory_dump_event.html#ProcessMemoryDumpEvent">ProcessMemoryDumpEvent</a></dd>
+<dd><a href="telemetry.timeline.event.html#TimelineEvent">telemetry.timeline.event.TimelineEvent</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="ProcessMemoryDumpEvent-GetMemoryBucket"><strong>GetMemoryBucket</strong></a>(self, path)</dt><dd><tt>Return&nbsp;the&nbsp;<a href="#MemoryBucket">MemoryBucket</a>&nbsp;associated&nbsp;with&nbsp;a&nbsp;category&nbsp;path.<br>
+&nbsp;<br>
+An&nbsp;empty&nbsp;bucket&nbsp;will&nbsp;be&nbsp;created&nbsp;if&nbsp;the&nbsp;path&nbsp;does&nbsp;not&nbsp;already&nbsp;exist.<br>
+&nbsp;<br>
+path:&nbsp;A&nbsp;string&nbsp;with&nbsp;path&nbsp;in&nbsp;the&nbsp;classification&nbsp;tree,&nbsp;e.g.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'/Android/Java&nbsp;runtime/Cache'.&nbsp;Note:&nbsp;no&nbsp;trailing&nbsp;slash,&nbsp;except&nbsp;for<br>
+&nbsp;&nbsp;&nbsp;&nbsp;the&nbsp;root&nbsp;path&nbsp;'/'.</tt></dd></dl>
+
+<dl><dt><a name="ProcessMemoryDumpEvent-GetMemoryUsage"><strong>GetMemoryUsage</strong></a>(self)</dt><dd><tt>Get&nbsp;a&nbsp;dictionary&nbsp;with&nbsp;the&nbsp;memory&nbsp;usage&nbsp;of&nbsp;this&nbsp;process.</tt></dd></dl>
+
+<dl><dt><a name="ProcessMemoryDumpEvent-GetMemoryValue"><strong>GetMemoryValue</strong></a>(self, category_path, discount_tracing<font color="#909090">=False</font>)</dt><dd><tt>Return&nbsp;a&nbsp;specific&nbsp;value&nbsp;from&nbsp;within&nbsp;a&nbsp;<a href="#MemoryBucket">MemoryBucket</a>.<br>
+&nbsp;<br>
+category_path:&nbsp;A&nbsp;string&nbsp;composed&nbsp;of&nbsp;a&nbsp;path&nbsp;in&nbsp;the&nbsp;classification&nbsp;tree,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;followed&nbsp;by&nbsp;a&nbsp;'.',&nbsp;followed&nbsp;by&nbsp;a&nbsp;specific&nbsp;bucket&nbsp;value,&nbsp;e.g.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;'/Android/Java&nbsp;runtime/Cache.private_dirty_resident'.<br>
+discount_tracing:&nbsp;A&nbsp;boolean&nbsp;indicating&nbsp;whether&nbsp;the&nbsp;returned&nbsp;value&nbsp;should<br>
+&nbsp;&nbsp;&nbsp;&nbsp;be&nbsp;discounted&nbsp;by&nbsp;the&nbsp;resident&nbsp;size&nbsp;of&nbsp;the&nbsp;tracing&nbsp;allocator.</tt></dd></dl>
+
+<dl><dt><a name="ProcessMemoryDumpEvent-__init__"><strong>__init__</strong></a>(self, process, event)</dt></dl>
+
+<dl><dt><a name="ProcessMemoryDumpEvent-__repr__"><strong>__repr__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>process_name</strong></dt>
+</dl>
+<hr>
+Data descriptors inherited from <a href="telemetry.timeline.event.html#TimelineEvent">telemetry.timeline.event.TimelineEvent</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>end</strong></dt>
+</dl>
+<dl><dt><strong>has_thread_timestamps</strong></dt>
+</dl>
+<dl><dt><strong>thread_end</strong></dt>
+<dd><tt>Thread-specific&nbsp;CPU&nbsp;time&nbsp;when&nbsp;this&nbsp;event&nbsp;ended.<br>
+&nbsp;<br>
+May&nbsp;be&nbsp;None&nbsp;if&nbsp;the&nbsp;trace&nbsp;event&nbsp;didn't&nbsp;have&nbsp;thread&nbsp;time&nbsp;data.</tt></dd>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>BUCKET_ATTRS</strong> = {'private_clean_resident': 'pc', 'private_dirty_resident': 'pd', 'proportional_resident': 'pss', 'shared_clean_resident': 'sc', 'shared_dirty_resident': 'sd', 'swapped': 'sw'}<br>
+<strong>MMAPS_METRICS</strong> = {'mmaps_ashmem': ('/Android/Ashmem.proportional_resident', False), 'mmaps_java_heap': ('/Android/Java runtime/Spaces.proportional_resident', False), 'mmaps_native_heap': ('/Native heap.proportional_resident', True), 'mmaps_overall_pss': ('/.proportional_resident', True), 'mmaps_private_dirty': ('/.private_dirty_resident', True)}<br>
+<strong>ROOT_CATEGORY</strong> = &lt;telemetry.timeline.memory_dump_event.MmapCategory object&gt;</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.model.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.model.html
new file mode 100644
index 0000000..a11a530
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.model.html
@@ -0,0 +1,314 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.timeline.model</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.timeline.html"><font color="#ffffff">timeline</font></a>.model</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/timeline/model.py">telemetry/timeline/model.py</a></font></td></tr></table>
+    <p><tt>A&nbsp;container&nbsp;for&nbsp;timeline-based&nbsp;events&nbsp;and&nbsp;traces&nbsp;and&nbsp;can&nbsp;handle&nbsp;importing<br>
+raw&nbsp;event&nbsp;data&nbsp;from&nbsp;different&nbsp;sources.&nbsp;This&nbsp;model&nbsp;closely&nbsp;resembles&nbsp;that&nbsp;in&nbsp;the<br>
+trace_viewer&nbsp;project:<br>
+https://code.google.com/p/trace-viewer/</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.timeline.async_slice.html">telemetry.timeline.async_slice</a><br>
+<a href="telemetry.timeline.bounds.html">telemetry.timeline.bounds</a><br>
+<a href="telemetry.timeline.event_container.html">telemetry.timeline.event_container</a><br>
+</td><td width="25%" valign=top><a href="telemetry.timeline.inspector_importer.html">telemetry.timeline.inspector_importer</a><br>
+<a href="telemetry.timeline.process.html">telemetry.timeline.process</a><br>
+<a href="telemetry.timeline.slice.html">telemetry.timeline.slice</a><br>
+</td><td width="25%" valign=top><a href="telemetry.timeline.surface_flinger_importer.html">telemetry.timeline.surface_flinger_importer</a><br>
+<a href="telemetry.timeline.tab_id_importer.html">telemetry.timeline.tab_id_importer</a><br>
+<a href="telemetry.timeline.trace_data.html">telemetry.timeline.trace_data</a><br>
+</td><td width="25%" valign=top><a href="telemetry.timeline.trace_event_importer.html">telemetry.timeline.trace_event_importer</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="exceptions.html#Exception">exceptions.Exception</a>(<a href="exceptions.html#BaseException">exceptions.BaseException</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.timeline.model.html#MarkerMismatchError">MarkerMismatchError</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.timeline.model.html#MarkerOverlapError">MarkerOverlapError</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="telemetry.timeline.event_container.html#TimelineEventContainer">telemetry.timeline.event_container.TimelineEventContainer</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.timeline.model.html#TimelineModel">TimelineModel</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="MarkerMismatchError">class <strong>MarkerMismatchError</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.timeline.model.html#MarkerMismatchError">MarkerMismatchError</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="MarkerMismatchError-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#MarkerMismatchError-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;object&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="MarkerMismatchError-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#MarkerMismatchError-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="MarkerMismatchError-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#MarkerMismatchError-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="MarkerMismatchError-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#MarkerMismatchError-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="MarkerMismatchError-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#MarkerMismatchError-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="MarkerMismatchError-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="MarkerMismatchError-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#MarkerMismatchError-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="MarkerMismatchError-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#MarkerMismatchError-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="MarkerMismatchError-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="MarkerMismatchError-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#MarkerMismatchError-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="MarkerMismatchError-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="MarkerOverlapError">class <strong>MarkerOverlapError</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.timeline.model.html#MarkerOverlapError">MarkerOverlapError</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="MarkerOverlapError-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#MarkerOverlapError-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;object&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="MarkerOverlapError-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#MarkerOverlapError-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="MarkerOverlapError-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#MarkerOverlapError-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="MarkerOverlapError-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#MarkerOverlapError-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="MarkerOverlapError-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#MarkerOverlapError-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="MarkerOverlapError-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="MarkerOverlapError-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#MarkerOverlapError-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="MarkerOverlapError-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#MarkerOverlapError-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="MarkerOverlapError-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="MarkerOverlapError-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#MarkerOverlapError-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="MarkerOverlapError-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TimelineModel">class <strong>TimelineModel</strong></a>(<a href="telemetry.timeline.event_container.html#TimelineEventContainer">telemetry.timeline.event_container.TimelineEventContainer</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.timeline.model.html#TimelineModel">TimelineModel</a></dd>
+<dd><a href="telemetry.timeline.event_container.html#TimelineEventContainer">telemetry.timeline.event_container.TimelineEventContainer</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="TimelineModel-AddMappingFromTabIdToRendererThread"><strong>AddMappingFromTabIdToRendererThread</strong></a>(self, tab_id, renderer_thread)</dt></dl>
+
+<dl><dt><a name="TimelineModel-FinalizeImport"><strong>FinalizeImport</strong></a>(self, shift_world_to_zero<font color="#909090">=False</font>, importers<font color="#909090">=None</font>)</dt></dl>
+
+<dl><dt><a name="TimelineModel-FindTimelineMarkers"><strong>FindTimelineMarkers</strong></a>(self, timeline_marker_names)</dt><dd><tt>Find&nbsp;the&nbsp;timeline&nbsp;events&nbsp;with&nbsp;the&nbsp;given&nbsp;names.<br>
+&nbsp;<br>
+If&nbsp;the&nbsp;number&nbsp;and&nbsp;order&nbsp;of&nbsp;events&nbsp;found&nbsp;does&nbsp;not&nbsp;match&nbsp;the&nbsp;names,<br>
+raise&nbsp;an&nbsp;error.</tt></dd></dl>
+
+<dl><dt><a name="TimelineModel-GetAllProcesses"><strong>GetAllProcesses</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TimelineModel-GetAllThreads"><strong>GetAllThreads</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TimelineModel-GetOrCreateProcess"><strong>GetOrCreateProcess</strong></a>(self, pid)</dt></dl>
+
+<dl><dt><a name="TimelineModel-GetRendererProcessFromTabId"><strong>GetRendererProcessFromTabId</strong></a>(self, tab_id)</dt></dl>
+
+<dl><dt><a name="TimelineModel-GetRendererThreadFromTabId"><strong>GetRendererThreadFromTabId</strong></a>(self, tab_id)</dt></dl>
+
+<dl><dt><a name="TimelineModel-ImportTraces"><strong>ImportTraces</strong></a>(self, trace_data, shift_world_to_zero<font color="#909090">=True</font>)</dt><dd><tt>Populates&nbsp;the&nbsp;model&nbsp;with&nbsp;the&nbsp;provided&nbsp;trace&nbsp;data.<br>
+&nbsp;<br>
+trace_data&nbsp;must&nbsp;be&nbsp;an&nbsp;instance&nbsp;of&nbsp;TraceData.<br>
+&nbsp;<br>
+Passing&nbsp;shift_world_to_zero=True&nbsp;causes&nbsp;the&nbsp;events&nbsp;to&nbsp;be&nbsp;shifted&nbsp;such&nbsp;that<br>
+the&nbsp;first&nbsp;event&nbsp;starts&nbsp;at&nbsp;time&nbsp;0.</tt></dd></dl>
+
+<dl><dt><a name="TimelineModel-IterChildContainers"><strong>IterChildContainers</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TimelineModel-IterGlobalMemoryDumps"><strong>IterGlobalMemoryDumps</strong></a>(self)</dt><dd><tt>Iterate&nbsp;over&nbsp;the&nbsp;memory&nbsp;dump&nbsp;events&nbsp;of&nbsp;this&nbsp;model.</tt></dd></dl>
+
+<dl><dt><a name="TimelineModel-SetGlobalMemoryDumps"><strong>SetGlobalMemoryDumps</strong></a>(self, global_memory_dumps)</dt><dd><tt>Populates&nbsp;the&nbsp;model&nbsp;with&nbsp;a&nbsp;sequence&nbsp;of&nbsp;GlobalMemoryDump&nbsp;objects.</tt></dd></dl>
+
+<dl><dt><a name="TimelineModel-ShiftWorldToZero"><strong>ShiftWorldToZero</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TimelineModel-UpdateBounds"><strong>UpdateBounds</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TimelineModel-__init__"><strong>__init__</strong></a>(self, trace_data<font color="#909090">=None</font>, shift_world_to_zero<font color="#909090">=True</font>)</dt><dd><tt>Initializes&nbsp;a&nbsp;<a href="#TimelineModel">TimelineModel</a>.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;trace_data:&nbsp;trace_data.TraceData&nbsp;containing&nbsp;events&nbsp;to&nbsp;import<br>
+&nbsp;&nbsp;&nbsp;&nbsp;shift_world_to_zero:&nbsp;If&nbsp;true,&nbsp;the&nbsp;events&nbsp;will&nbsp;be&nbsp;shifted&nbsp;such&nbsp;that&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;first&nbsp;event&nbsp;starts&nbsp;at&nbsp;time&nbsp;0.</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>bounds</strong></dt>
+</dl>
+<dl><dt><strong>browser_process</strong></dt>
+</dl>
+<dl><dt><strong>gpu_process</strong></dt>
+</dl>
+<dl><dt><strong>processes</strong></dt>
+</dl>
+<dl><dt><strong>surface_flinger_process</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.timeline.event_container.html#TimelineEventContainer">telemetry.timeline.event_container.TimelineEventContainer</a>:<br>
+<dl><dt><a name="TimelineModel-GetAllEvents"><strong>GetAllEvents</strong></a>(self, recursive<font color="#909090">=True</font>)</dt><dd><tt>#&nbsp;List&nbsp;versions.&nbsp;These&nbsp;should&nbsp;always&nbsp;be&nbsp;simple&nbsp;expressions&nbsp;that&nbsp;list()&nbsp;on<br>
+#&nbsp;an&nbsp;underlying&nbsp;iter&nbsp;method.</tt></dd></dl>
+
+<dl><dt><a name="TimelineModel-GetAllEventsOfName"><strong>GetAllEventsOfName</strong></a>(self, name, recursive<font color="#909090">=True</font>)</dt></dl>
+
+<dl><dt><a name="TimelineModel-GetAllToplevelSlicesOfName"><strong>GetAllToplevelSlicesOfName</strong></a>(self, name, recursive<font color="#909090">=True</font>)</dt></dl>
+
+<dl><dt><a name="TimelineModel-IterAllAsyncSlicesOfName"><strong>IterAllAsyncSlicesOfName</strong></a>(self, name, recursive<font color="#909090">=True</font>)</dt></dl>
+
+<dl><dt><a name="TimelineModel-IterAllAsyncSlicesStartsWithName"><strong>IterAllAsyncSlicesStartsWithName</strong></a>(self, name, recursive<font color="#909090">=True</font>)</dt></dl>
+
+<dl><dt><a name="TimelineModel-IterAllEvents"><strong>IterAllEvents</strong></a>(self, recursive<font color="#909090">=True</font>, event_type_predicate<font color="#909090">=&lt;function &lt;lambda&gt;&gt;</font>, event_predicate<font color="#909090">=&lt;function &lt;lambda&gt;&gt;</font>)</dt><dd><tt>Iterates&nbsp;all&nbsp;events&nbsp;in&nbsp;this&nbsp;container,&nbsp;pre-filtered&nbsp;by&nbsp;two&nbsp;predicates.<br>
+&nbsp;<br>
+Only&nbsp;events&nbsp;with&nbsp;a&nbsp;type&nbsp;matching&nbsp;event_type_predicate&nbsp;AND&nbsp;matching&nbsp;event<br>
+event_predicate&nbsp;will&nbsp;be&nbsp;yielded.<br>
+&nbsp;<br>
+event_type_predicate&nbsp;is&nbsp;given&nbsp;an&nbsp;actual&nbsp;type&nbsp;object,&nbsp;e.g.:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;event_type_predicate(slice_module.Slice)<br>
+&nbsp;<br>
+event_predicate&nbsp;is&nbsp;given&nbsp;actual&nbsp;events:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;event_predicate(thread.slices[7])</tt></dd></dl>
+
+<dl><dt><a name="TimelineModel-IterAllEventsOfName"><strong>IterAllEventsOfName</strong></a>(self, name, recursive<font color="#909090">=True</font>)</dt><dd><tt>#&nbsp;Helper&nbsp;functions&nbsp;for&nbsp;finding&nbsp;common&nbsp;kinds&nbsp;of&nbsp;events.&nbsp;Must&nbsp;always&nbsp;take&nbsp;an<br>
+#&nbsp;optinal&nbsp;recurisve&nbsp;parameter&nbsp;and&nbsp;be&nbsp;implemented&nbsp;in&nbsp;terms&nbsp;fo&nbsp;IterAllEvents.</tt></dd></dl>
+
+<dl><dt><a name="TimelineModel-IterAllFlowEvents"><strong>IterAllFlowEvents</strong></a>(self, recursive<font color="#909090">=True</font>)</dt></dl>
+
+<dl><dt><a name="TimelineModel-IterAllSlices"><strong>IterAllSlices</strong></a>(self, recursive<font color="#909090">=True</font>)</dt></dl>
+
+<dl><dt><a name="TimelineModel-IterAllSlicesInRange"><strong>IterAllSlicesInRange</strong></a>(self, start, end, recursive<font color="#909090">=True</font>)</dt></dl>
+
+<dl><dt><a name="TimelineModel-IterAllSlicesOfName"><strong>IterAllSlicesOfName</strong></a>(self, name, recursive<font color="#909090">=True</font>)</dt></dl>
+
+<dl><dt><a name="TimelineModel-IterAllToplevelSlicesOfName"><strong>IterAllToplevelSlicesOfName</strong></a>(self, name, recursive<font color="#909090">=True</font>)</dt></dl>
+
+<dl><dt><a name="TimelineModel-IterEventsInThisContainer"><strong>IterEventsInThisContainer</strong></a>(self, event_type_predicate, event_predicate)</dt><dd><tt>Iterates&nbsp;all&nbsp;the&nbsp;TimelineEvents&nbsp;in&nbsp;this&nbsp;container.<br>
+&nbsp;<br>
+Only&nbsp;events&nbsp;with&nbsp;a&nbsp;type&nbsp;matching&nbsp;event_type_predicate&nbsp;AND&nbsp;matching&nbsp;event<br>
+event_predicate&nbsp;will&nbsp;be&nbsp;yielded.<br>
+&nbsp;<br>
+event_type_predicate&nbsp;is&nbsp;given&nbsp;an&nbsp;actual&nbsp;type&nbsp;object,&nbsp;e.g.:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;event_type_predicate(slice_module.Slice)<br>
+&nbsp;<br>
+event_predicate&nbsp;is&nbsp;given&nbsp;actual&nbsp;events:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;event_predicate(thread.slices[7])<br>
+&nbsp;<br>
+DO&nbsp;NOT&nbsp;ASSUME&nbsp;that&nbsp;the&nbsp;event_type_predicate&nbsp;will&nbsp;be&nbsp;called&nbsp;for&nbsp;every&nbsp;event<br>
+found.&nbsp;The&nbsp;relative&nbsp;calling&nbsp;order&nbsp;of&nbsp;the&nbsp;two&nbsp;is&nbsp;left&nbsp;up&nbsp;to&nbsp;the&nbsp;implementer<br>
+of&nbsp;the&nbsp;method.</tt></dd></dl>
+
+<hr>
+Static methods inherited from <a href="telemetry.timeline.event_container.html#TimelineEventContainer">telemetry.timeline.event_container.TimelineEventContainer</a>:<br>
+<dl><dt><a name="TimelineModel-IsAsyncSlice"><strong>IsAsyncSlice</strong></a>(t)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.timeline.event_container.html#TimelineEventContainer">telemetry.timeline.event_container.TimelineEventContainer</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-IsSliceOrAsyncSlice"><strong>IsSliceOrAsyncSlice</strong></a>(t)</dt></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.process.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.process.html
new file mode 100644
index 0000000..e3bda6e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.process.html
@@ -0,0 +1,139 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.timeline.process</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.timeline.html"><font color="#ffffff">timeline</font></a>.process</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/timeline/process.py">telemetry/timeline/process.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.timeline.event_container.html">telemetry.timeline.event_container</a><br>
+<a href="telemetry.timeline.event.html">telemetry.timeline.event</a><br>
+</td><td width="25%" valign=top><a href="telemetry.timeline.memory_dump_event.html">telemetry.timeline.memory_dump_event</a><br>
+<a href="telemetry.timeline.counter.html">telemetry.timeline.counter</a><br>
+</td><td width="25%" valign=top><a href="telemetry.timeline.thread.html">telemetry.timeline.thread</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.timeline.event_container.html#TimelineEventContainer">telemetry.timeline.event_container.TimelineEventContainer</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.timeline.process.html#Process">Process</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="Process">class <strong>Process</strong></a>(<a href="telemetry.timeline.event_container.html#TimelineEventContainer">telemetry.timeline.event_container.TimelineEventContainer</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>The&nbsp;<a href="#Process">Process</a>&nbsp;represents&nbsp;a&nbsp;single&nbsp;userland&nbsp;process&nbsp;in&nbsp;the&nbsp;trace.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.timeline.process.html#Process">Process</a></dd>
+<dd><a href="telemetry.timeline.event_container.html#TimelineEventContainer">telemetry.timeline.event_container.TimelineEventContainer</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="Process-AddMemoryDumpEvent"><strong>AddMemoryDumpEvent</strong></a>(self, memory_dump)</dt><dd><tt>Add&nbsp;a&nbsp;ProcessMemoryDumpEvent&nbsp;to&nbsp;this&nbsp;process.</tt></dd></dl>
+
+<dl><dt><a name="Process-AutoCloseOpenSlices"><strong>AutoCloseOpenSlices</strong></a>(self, max_timestamp, thread_time_bounds)</dt></dl>
+
+<dl><dt><a name="Process-FinalizeImport"><strong>FinalizeImport</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="Process-GetCounter"><strong>GetCounter</strong></a>(self, category, name)</dt></dl>
+
+<dl><dt><a name="Process-GetOrCreateCounter"><strong>GetOrCreateCounter</strong></a>(self, category, name)</dt></dl>
+
+<dl><dt><a name="Process-GetOrCreateThread"><strong>GetOrCreateThread</strong></a>(self, tid)</dt></dl>
+
+<dl><dt><a name="Process-IterChildContainers"><strong>IterChildContainers</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="Process-IterEventsInThisContainer"><strong>IterEventsInThisContainer</strong></a>(self, event_type_predicate, event_predicate)</dt></dl>
+
+<dl><dt><a name="Process-SetTraceBufferOverflowTimestamp"><strong>SetTraceBufferOverflowTimestamp</strong></a>(self, timestamp)</dt></dl>
+
+<dl><dt><a name="Process-__init__"><strong>__init__</strong></a>(self, parent, pid)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>counters</strong></dt>
+</dl>
+<dl><dt><strong>threads</strong></dt>
+</dl>
+<dl><dt><strong>trace_buffer_did_overflow</strong></dt>
+</dl>
+<dl><dt><strong>trace_buffer_overflow_event</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.timeline.event_container.html#TimelineEventContainer">telemetry.timeline.event_container.TimelineEventContainer</a>:<br>
+<dl><dt><a name="Process-GetAllEvents"><strong>GetAllEvents</strong></a>(self, recursive<font color="#909090">=True</font>)</dt><dd><tt>#&nbsp;List&nbsp;versions.&nbsp;These&nbsp;should&nbsp;always&nbsp;be&nbsp;simple&nbsp;expressions&nbsp;that&nbsp;list()&nbsp;on<br>
+#&nbsp;an&nbsp;underlying&nbsp;iter&nbsp;method.</tt></dd></dl>
+
+<dl><dt><a name="Process-GetAllEventsOfName"><strong>GetAllEventsOfName</strong></a>(self, name, recursive<font color="#909090">=True</font>)</dt></dl>
+
+<dl><dt><a name="Process-GetAllToplevelSlicesOfName"><strong>GetAllToplevelSlicesOfName</strong></a>(self, name, recursive<font color="#909090">=True</font>)</dt></dl>
+
+<dl><dt><a name="Process-IterAllAsyncSlicesOfName"><strong>IterAllAsyncSlicesOfName</strong></a>(self, name, recursive<font color="#909090">=True</font>)</dt></dl>
+
+<dl><dt><a name="Process-IterAllAsyncSlicesStartsWithName"><strong>IterAllAsyncSlicesStartsWithName</strong></a>(self, name, recursive<font color="#909090">=True</font>)</dt></dl>
+
+<dl><dt><a name="Process-IterAllEvents"><strong>IterAllEvents</strong></a>(self, recursive<font color="#909090">=True</font>, event_type_predicate<font color="#909090">=&lt;function &lt;lambda&gt;&gt;</font>, event_predicate<font color="#909090">=&lt;function &lt;lambda&gt;&gt;</font>)</dt><dd><tt>Iterates&nbsp;all&nbsp;events&nbsp;in&nbsp;this&nbsp;container,&nbsp;pre-filtered&nbsp;by&nbsp;two&nbsp;predicates.<br>
+&nbsp;<br>
+Only&nbsp;events&nbsp;with&nbsp;a&nbsp;type&nbsp;matching&nbsp;event_type_predicate&nbsp;AND&nbsp;matching&nbsp;event<br>
+event_predicate&nbsp;will&nbsp;be&nbsp;yielded.<br>
+&nbsp;<br>
+event_type_predicate&nbsp;is&nbsp;given&nbsp;an&nbsp;actual&nbsp;type&nbsp;object,&nbsp;e.g.:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;event_type_predicate(slice_module.Slice)<br>
+&nbsp;<br>
+event_predicate&nbsp;is&nbsp;given&nbsp;actual&nbsp;events:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;event_predicate(thread.slices[7])</tt></dd></dl>
+
+<dl><dt><a name="Process-IterAllEventsOfName"><strong>IterAllEventsOfName</strong></a>(self, name, recursive<font color="#909090">=True</font>)</dt><dd><tt>#&nbsp;Helper&nbsp;functions&nbsp;for&nbsp;finding&nbsp;common&nbsp;kinds&nbsp;of&nbsp;events.&nbsp;Must&nbsp;always&nbsp;take&nbsp;an<br>
+#&nbsp;optinal&nbsp;recurisve&nbsp;parameter&nbsp;and&nbsp;be&nbsp;implemented&nbsp;in&nbsp;terms&nbsp;fo&nbsp;IterAllEvents.</tt></dd></dl>
+
+<dl><dt><a name="Process-IterAllFlowEvents"><strong>IterAllFlowEvents</strong></a>(self, recursive<font color="#909090">=True</font>)</dt></dl>
+
+<dl><dt><a name="Process-IterAllSlices"><strong>IterAllSlices</strong></a>(self, recursive<font color="#909090">=True</font>)</dt></dl>
+
+<dl><dt><a name="Process-IterAllSlicesInRange"><strong>IterAllSlicesInRange</strong></a>(self, start, end, recursive<font color="#909090">=True</font>)</dt></dl>
+
+<dl><dt><a name="Process-IterAllSlicesOfName"><strong>IterAllSlicesOfName</strong></a>(self, name, recursive<font color="#909090">=True</font>)</dt></dl>
+
+<dl><dt><a name="Process-IterAllToplevelSlicesOfName"><strong>IterAllToplevelSlicesOfName</strong></a>(self, name, recursive<font color="#909090">=True</font>)</dt></dl>
+
+<hr>
+Static methods inherited from <a href="telemetry.timeline.event_container.html#TimelineEventContainer">telemetry.timeline.event_container.TimelineEventContainer</a>:<br>
+<dl><dt><a name="Process-IsAsyncSlice"><strong>IsAsyncSlice</strong></a>(t)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.timeline.event_container.html#TimelineEventContainer">telemetry.timeline.event_container.TimelineEventContainer</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.sample.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.sample.html
new file mode 100644
index 0000000..2206d48
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.sample.html
@@ -0,0 +1,85 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.timeline.sample</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.timeline.html"><font color="#ffffff">timeline</font></a>.sample</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/timeline/sample.py">telemetry/timeline/sample.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.timeline.event.html">telemetry.timeline.event</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.timeline.event.html#TimelineEvent">telemetry.timeline.event.TimelineEvent</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.timeline.sample.html#Sample">Sample</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="Sample">class <strong>Sample</strong></a>(<a href="telemetry.timeline.event.html#TimelineEvent">telemetry.timeline.event.TimelineEvent</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;<a href="#Sample">Sample</a>&nbsp;represents&nbsp;a&nbsp;sample&nbsp;taken&nbsp;at&nbsp;an&nbsp;instant&nbsp;in&nbsp;time<br>
+plus&nbsp;parameters&nbsp;associated&nbsp;with&nbsp;that&nbsp;sample.<br>
+&nbsp;<br>
+NOTE:&nbsp;The&nbsp;<a href="#Sample">Sample</a>&nbsp;class&nbsp;implements&nbsp;the&nbsp;same&nbsp;interface&nbsp;as<br>
+Slice.&nbsp;These&nbsp;must&nbsp;be&nbsp;kept&nbsp;in&nbsp;sync.<br>
+&nbsp;<br>
+All&nbsp;time&nbsp;units&nbsp;are&nbsp;stored&nbsp;in&nbsp;milliseconds.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.timeline.sample.html#Sample">Sample</a></dd>
+<dd><a href="telemetry.timeline.event.html#TimelineEvent">telemetry.timeline.event.TimelineEvent</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="Sample-__init__"><strong>__init__</strong></a>(self, parent_thread, category, name, timestamp, args<font color="#909090">=None</font>)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.timeline.event.html#TimelineEvent">telemetry.timeline.event.TimelineEvent</a>:<br>
+<dl><dt><a name="Sample-__repr__"><strong>__repr__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.timeline.event.html#TimelineEvent">telemetry.timeline.event.TimelineEvent</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>end</strong></dt>
+</dl>
+<dl><dt><strong>has_thread_timestamps</strong></dt>
+</dl>
+<dl><dt><strong>thread_end</strong></dt>
+<dd><tt>Thread-specific&nbsp;CPU&nbsp;time&nbsp;when&nbsp;this&nbsp;event&nbsp;ended.<br>
+&nbsp;<br>
+May&nbsp;be&nbsp;None&nbsp;if&nbsp;the&nbsp;trace&nbsp;event&nbsp;didn't&nbsp;have&nbsp;thread&nbsp;time&nbsp;data.</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.slice.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.slice.html
new file mode 100644
index 0000000..4e63552
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.slice.html
@@ -0,0 +1,103 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.timeline.slice</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.timeline.html"><font color="#ffffff">timeline</font></a>.slice</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/timeline/slice.py">telemetry/timeline/slice.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.timeline.event.html">telemetry.timeline.event</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.timeline.event.html#TimelineEvent">telemetry.timeline.event.TimelineEvent</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.timeline.slice.html#Slice">Slice</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="Slice">class <strong>Slice</strong></a>(<a href="telemetry.timeline.event.html#TimelineEvent">telemetry.timeline.event.TimelineEvent</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;<a href="#Slice">Slice</a>&nbsp;represents&nbsp;an&nbsp;interval&nbsp;of&nbsp;time&nbsp;plus&nbsp;parameters&nbsp;associated<br>
+with&nbsp;that&nbsp;interval.<br>
+&nbsp;<br>
+NOTE:&nbsp;The&nbsp;Sample&nbsp;class&nbsp;implements&nbsp;the&nbsp;same&nbsp;interface&nbsp;as<br>
+<a href="#Slice">Slice</a>.&nbsp;These&nbsp;must&nbsp;be&nbsp;kept&nbsp;in&nbsp;sync.<br>
+&nbsp;<br>
+All&nbsp;time&nbsp;units&nbsp;are&nbsp;stored&nbsp;in&nbsp;milliseconds.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.timeline.slice.html#Slice">Slice</a></dd>
+<dd><a href="telemetry.timeline.event.html#TimelineEvent">telemetry.timeline.event.TimelineEvent</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="Slice-AddSubSlice"><strong>AddSubSlice</strong></a>(self, sub_slice)</dt></dl>
+
+<dl><dt><a name="Slice-GetAllSubSlices"><strong>GetAllSubSlices</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="Slice-GetAllSubSlicesOfName"><strong>GetAllSubSlicesOfName</strong></a>(self, name)</dt></dl>
+
+<dl><dt><a name="Slice-IterEventsInThisContainerRecrusively"><strong>IterEventsInThisContainerRecrusively</strong></a>(self, stack<font color="#909090">=None</font>)</dt></dl>
+
+<dl><dt><a name="Slice-__init__"><strong>__init__</strong></a>(self, parent_thread, category, name, timestamp, duration<font color="#909090">=0</font>, thread_timestamp<font color="#909090">=None</font>, thread_duration<font color="#909090">=None</font>, args<font color="#909090">=None</font>)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>self_thread_time</strong></dt>
+<dd><tt>Thread&nbsp;(scheduled)&nbsp;time&nbsp;spent&nbsp;in&nbsp;this&nbsp;function&nbsp;less&nbsp;any&nbsp;thread&nbsp;time&nbsp;spent<br>
+in&nbsp;child&nbsp;events.&nbsp;Returns&nbsp;None&nbsp;if&nbsp;the&nbsp;slice&nbsp;or&nbsp;any&nbsp;of&nbsp;its&nbsp;children&nbsp;does&nbsp;not<br>
+have&nbsp;a&nbsp;thread_duration&nbsp;value.</tt></dd>
+</dl>
+<dl><dt><strong>self_time</strong></dt>
+<dd><tt>Time&nbsp;spent&nbsp;in&nbsp;this&nbsp;function&nbsp;less&nbsp;any&nbsp;time&nbsp;spent&nbsp;in&nbsp;child&nbsp;events.</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.timeline.event.html#TimelineEvent">telemetry.timeline.event.TimelineEvent</a>:<br>
+<dl><dt><a name="Slice-__repr__"><strong>__repr__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.timeline.event.html#TimelineEvent">telemetry.timeline.event.TimelineEvent</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>end</strong></dt>
+</dl>
+<dl><dt><strong>has_thread_timestamps</strong></dt>
+</dl>
+<dl><dt><strong>thread_end</strong></dt>
+<dd><tt>Thread-specific&nbsp;CPU&nbsp;time&nbsp;when&nbsp;this&nbsp;event&nbsp;ended.<br>
+&nbsp;<br>
+May&nbsp;be&nbsp;None&nbsp;if&nbsp;the&nbsp;trace&nbsp;event&nbsp;didn't&nbsp;have&nbsp;thread&nbsp;time&nbsp;data.</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.surface_flinger_importer.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.surface_flinger_importer.html
new file mode 100644
index 0000000..0e98a27
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.surface_flinger_importer.html
@@ -0,0 +1,74 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.timeline.surface_flinger_importer</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.timeline.html"><font color="#ffffff">timeline</font></a>.surface_flinger_importer</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/timeline/surface_flinger_importer.py">telemetry/timeline/surface_flinger_importer.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.timeline.importer.html">telemetry.timeline.importer</a><br>
+</td><td width="25%" valign=top><a href="telemetry.timeline.trace_data.html">telemetry.timeline.trace_data</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.timeline.importer.html#TimelineImporter">telemetry.timeline.importer.TimelineImporter</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.timeline.surface_flinger_importer.html#SurfaceFlingerTimelineImporter">SurfaceFlingerTimelineImporter</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="SurfaceFlingerTimelineImporter">class <strong>SurfaceFlingerTimelineImporter</strong></a>(<a href="telemetry.timeline.importer.html#TimelineImporter">telemetry.timeline.importer.TimelineImporter</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.timeline.surface_flinger_importer.html#SurfaceFlingerTimelineImporter">SurfaceFlingerTimelineImporter</a></dd>
+<dd><a href="telemetry.timeline.importer.html#TimelineImporter">telemetry.timeline.importer.TimelineImporter</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="SurfaceFlingerTimelineImporter-FinalizeImport"><strong>FinalizeImport</strong></a>(self)</dt><dd><tt>Called&nbsp;by&nbsp;the&nbsp;Model&nbsp;after&nbsp;all&nbsp;other&nbsp;importers&nbsp;have&nbsp;imported&nbsp;their<br>
+events.</tt></dd></dl>
+
+<dl><dt><a name="SurfaceFlingerTimelineImporter-ImportEvents"><strong>ImportEvents</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="SurfaceFlingerTimelineImporter-__init__"><strong>__init__</strong></a>(self, model, trace_data)</dt></dl>
+
+<hr>
+Static methods defined here:<br>
+<dl><dt><a name="SurfaceFlingerTimelineImporter-GetSupportedPart"><strong>GetSupportedPart</strong></a>()</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.timeline.importer.html#TimelineImporter">telemetry.timeline.importer.TimelineImporter</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.tab_id_importer.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.tab_id_importer.html
new file mode 100644
index 0000000..5eaad09
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.tab_id_importer.html
@@ -0,0 +1,138 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.timeline.tab_id_importer</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.timeline.html"><font color="#ffffff">timeline</font></a>.tab_id_importer</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/timeline/tab_id_importer.py">telemetry/timeline/tab_id_importer.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.timeline.importer.html">telemetry.timeline.importer</a><br>
+</td><td width="25%" valign=top><a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top><a href="telemetry.timeline.trace_data.html">telemetry.timeline.trace_data</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="exceptions.html#Exception">exceptions.Exception</a>(<a href="exceptions.html#BaseException">exceptions.BaseException</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.timeline.tab_id_importer.html#TraceBufferOverflowException">TraceBufferOverflowException</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="telemetry.timeline.importer.html#TimelineImporter">telemetry.timeline.importer.TimelineImporter</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.timeline.tab_id_importer.html#TabIdImporter">TabIdImporter</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TabIdImporter">class <strong>TabIdImporter</strong></a>(<a href="telemetry.timeline.importer.html#TimelineImporter">telemetry.timeline.importer.TimelineImporter</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.timeline.tab_id_importer.html#TabIdImporter">TabIdImporter</a></dd>
+<dd><a href="telemetry.timeline.importer.html#TimelineImporter">telemetry.timeline.importer.TimelineImporter</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="TabIdImporter-FinalizeImport"><strong>FinalizeImport</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TabIdImporter-ImportEvents"><strong>ImportEvents</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TabIdImporter-__init__"><strong>__init__</strong></a>(self, model, trace_data)</dt></dl>
+
+<hr>
+Static methods defined here:<br>
+<dl><dt><a name="TabIdImporter-GetSupportedPart"><strong>GetSupportedPart</strong></a>()</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.timeline.importer.html#TimelineImporter">telemetry.timeline.importer.TimelineImporter</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TraceBufferOverflowException">class <strong>TraceBufferOverflowException</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.timeline.tab_id_importer.html#TraceBufferOverflowException">TraceBufferOverflowException</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="TraceBufferOverflowException-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#TraceBufferOverflowException-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#TraceBufferOverflowException-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;object&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="TraceBufferOverflowException-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#TraceBufferOverflowException-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="TraceBufferOverflowException-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#TraceBufferOverflowException-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="TraceBufferOverflowException-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#TraceBufferOverflowException-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="TraceBufferOverflowException-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#TraceBufferOverflowException-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="TraceBufferOverflowException-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="TraceBufferOverflowException-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#TraceBufferOverflowException-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="TraceBufferOverflowException-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#TraceBufferOverflowException-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="TraceBufferOverflowException-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="TraceBufferOverflowException-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#TraceBufferOverflowException-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="TraceBufferOverflowException-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.thread.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.thread.html
new file mode 100644
index 0000000..09b8513
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.thread.html
@@ -0,0 +1,168 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.timeline.thread</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.timeline.html"><font color="#ffffff">timeline</font></a>.thread</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/timeline/thread.py">telemetry/timeline/thread.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.timeline.async_slice.html">telemetry.timeline.async_slice</a><br>
+<a href="telemetry.timeline.event_container.html">telemetry.timeline.event_container</a><br>
+</td><td width="25%" valign=top><a href="telemetry.timeline.flow_event.html">telemetry.timeline.flow_event</a><br>
+<a href="telemetry.timeline.sample.html">telemetry.timeline.sample</a><br>
+</td><td width="25%" valign=top><a href="telemetry.timeline.slice.html">telemetry.timeline.slice</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.timeline.event_container.html#TimelineEventContainer">telemetry.timeline.event_container.TimelineEventContainer</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.timeline.thread.html#Thread">Thread</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="Thread">class <strong>Thread</strong></a>(<a href="telemetry.timeline.event_container.html#TimelineEventContainer">telemetry.timeline.event_container.TimelineEventContainer</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;<a href="#Thread">Thread</a>&nbsp;stores&nbsp;all&nbsp;the&nbsp;trace&nbsp;events&nbsp;collected&nbsp;for&nbsp;a&nbsp;particular<br>
+thread.&nbsp;We&nbsp;organize&nbsp;the&nbsp;synchronous&nbsp;slices&nbsp;on&nbsp;a&nbsp;thread&nbsp;by&nbsp;"subrows,"&nbsp;where<br>
+subrow&nbsp;0&nbsp;has&nbsp;all&nbsp;the&nbsp;root&nbsp;slices,&nbsp;subrow&nbsp;1&nbsp;those&nbsp;nested&nbsp;1&nbsp;deep,&nbsp;and&nbsp;so&nbsp;on.<br>
+The&nbsp;asynchronous&nbsp;slices&nbsp;are&nbsp;stored&nbsp;in&nbsp;an&nbsp;AsyncSliceGroup&nbsp;object.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.timeline.thread.html#Thread">Thread</a></dd>
+<dd><a href="telemetry.timeline.event_container.html#TimelineEventContainer">telemetry.timeline.event_container.TimelineEventContainer</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="Thread-AddAsyncSlice"><strong>AddAsyncSlice</strong></a>(self, async_slice)</dt></dl>
+
+<dl><dt><a name="Thread-AddFlowEvent"><strong>AddFlowEvent</strong></a>(self, flow_event)</dt></dl>
+
+<dl><dt><a name="Thread-AddSample"><strong>AddSample</strong></a>(self, category, name, timestamp, args<font color="#909090">=None</font>)</dt></dl>
+
+<dl><dt><a name="Thread-AutoCloseOpenSlices"><strong>AutoCloseOpenSlices</strong></a>(self, max_timestamp, max_thread_timestamp)</dt></dl>
+
+<dl><dt><a name="Thread-BeginSlice"><strong>BeginSlice</strong></a>(self, category, name, timestamp, thread_timestamp<font color="#909090">=None</font>, args<font color="#909090">=None</font>)</dt><dd><tt>Opens&nbsp;a&nbsp;new&nbsp;slice&nbsp;for&nbsp;the&nbsp;thread.<br>
+Calls&nbsp;to&nbsp;beginSlice&nbsp;and&nbsp;endSlice&nbsp;must&nbsp;be&nbsp;made&nbsp;with<br>
+non-monotonically-decreasing&nbsp;timestamps.<br>
+&nbsp;<br>
+*&nbsp;category:&nbsp;Category&nbsp;to&nbsp;which&nbsp;the&nbsp;slice&nbsp;belongs.<br>
+*&nbsp;name:&nbsp;Name&nbsp;of&nbsp;the&nbsp;slice&nbsp;to&nbsp;add.<br>
+*&nbsp;timestamp:&nbsp;The&nbsp;timetsamp&nbsp;of&nbsp;the&nbsp;slice,&nbsp;in&nbsp;milliseconds.<br>
+*&nbsp;thread_timestamp:&nbsp;<a href="#Thread">Thread</a>&nbsp;specific&nbsp;clock&nbsp;(scheduled)&nbsp;timestamp&nbsp;of&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;slice,&nbsp;in&nbsp;milliseconds.<br>
+*&nbsp;args:&nbsp;Arguments&nbsp;associated&nbsp;with<br>
+&nbsp;<br>
+Returns&nbsp;newly&nbsp;opened&nbsp;slice</tt></dd></dl>
+
+<dl><dt><a name="Thread-EndSlice"><strong>EndSlice</strong></a>(self, end_timestamp, end_thread_timestamp<font color="#909090">=None</font>)</dt><dd><tt>Ends&nbsp;the&nbsp;last&nbsp;begun&nbsp;slice&nbsp;in&nbsp;this&nbsp;group&nbsp;and&nbsp;pushes&nbsp;it&nbsp;onto&nbsp;the&nbsp;slice<br>
+array.<br>
+&nbsp;<br>
+*&nbsp;end_timestamp:&nbsp;Timestamp&nbsp;when&nbsp;the&nbsp;slice&nbsp;ended&nbsp;in&nbsp;milliseconds<br>
+*&nbsp;end_thread_timestamp:&nbsp;Timestamp&nbsp;when&nbsp;the&nbsp;scheduled&nbsp;time&nbsp;of&nbsp;the&nbsp;slice&nbsp;ended<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;in&nbsp;milliseconds<br>
+&nbsp;<br>
+returns&nbsp;completed&nbsp;slice.</tt></dd></dl>
+
+<dl><dt><a name="Thread-FinalizeImport"><strong>FinalizeImport</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="Thread-IsTimestampValidForBeginOrEnd"><strong>IsTimestampValidForBeginOrEnd</strong></a>(self, timestamp)</dt></dl>
+
+<dl><dt><a name="Thread-IterChildContainers"><strong>IterChildContainers</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="Thread-IterEventsInThisContainer"><strong>IterEventsInThisContainer</strong></a>(self, event_type_predicate, event_predicate)</dt></dl>
+
+<dl><dt><a name="Thread-PushCompleteSlice"><strong>PushCompleteSlice</strong></a>(self, category, name, timestamp, duration, thread_timestamp, thread_duration, args<font color="#909090">=None</font>)</dt></dl>
+
+<dl><dt><a name="Thread-PushSlice"><strong>PushSlice</strong></a>(self, new_slice)</dt></dl>
+
+<dl><dt><a name="Thread-__init__"><strong>__init__</strong></a>(self, process, tid)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>all_slices</strong></dt>
+</dl>
+<dl><dt><strong>async_slices</strong></dt>
+</dl>
+<dl><dt><strong>open_slice_count</strong></dt>
+</dl>
+<dl><dt><strong>samples</strong></dt>
+</dl>
+<dl><dt><strong>toplevel_slices</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.timeline.event_container.html#TimelineEventContainer">telemetry.timeline.event_container.TimelineEventContainer</a>:<br>
+<dl><dt><a name="Thread-GetAllEvents"><strong>GetAllEvents</strong></a>(self, recursive<font color="#909090">=True</font>)</dt><dd><tt>#&nbsp;List&nbsp;versions.&nbsp;These&nbsp;should&nbsp;always&nbsp;be&nbsp;simple&nbsp;expressions&nbsp;that&nbsp;list()&nbsp;on<br>
+#&nbsp;an&nbsp;underlying&nbsp;iter&nbsp;method.</tt></dd></dl>
+
+<dl><dt><a name="Thread-GetAllEventsOfName"><strong>GetAllEventsOfName</strong></a>(self, name, recursive<font color="#909090">=True</font>)</dt></dl>
+
+<dl><dt><a name="Thread-GetAllToplevelSlicesOfName"><strong>GetAllToplevelSlicesOfName</strong></a>(self, name, recursive<font color="#909090">=True</font>)</dt></dl>
+
+<dl><dt><a name="Thread-IterAllAsyncSlicesOfName"><strong>IterAllAsyncSlicesOfName</strong></a>(self, name, recursive<font color="#909090">=True</font>)</dt></dl>
+
+<dl><dt><a name="Thread-IterAllAsyncSlicesStartsWithName"><strong>IterAllAsyncSlicesStartsWithName</strong></a>(self, name, recursive<font color="#909090">=True</font>)</dt></dl>
+
+<dl><dt><a name="Thread-IterAllEvents"><strong>IterAllEvents</strong></a>(self, recursive<font color="#909090">=True</font>, event_type_predicate<font color="#909090">=&lt;function &lt;lambda&gt;&gt;</font>, event_predicate<font color="#909090">=&lt;function &lt;lambda&gt;&gt;</font>)</dt><dd><tt>Iterates&nbsp;all&nbsp;events&nbsp;in&nbsp;this&nbsp;container,&nbsp;pre-filtered&nbsp;by&nbsp;two&nbsp;predicates.<br>
+&nbsp;<br>
+Only&nbsp;events&nbsp;with&nbsp;a&nbsp;type&nbsp;matching&nbsp;event_type_predicate&nbsp;AND&nbsp;matching&nbsp;event<br>
+event_predicate&nbsp;will&nbsp;be&nbsp;yielded.<br>
+&nbsp;<br>
+event_type_predicate&nbsp;is&nbsp;given&nbsp;an&nbsp;actual&nbsp;type&nbsp;object,&nbsp;e.g.:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;event_type_predicate(slice_module.Slice)<br>
+&nbsp;<br>
+event_predicate&nbsp;is&nbsp;given&nbsp;actual&nbsp;events:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;event_predicate(thread.slices[7])</tt></dd></dl>
+
+<dl><dt><a name="Thread-IterAllEventsOfName"><strong>IterAllEventsOfName</strong></a>(self, name, recursive<font color="#909090">=True</font>)</dt><dd><tt>#&nbsp;Helper&nbsp;functions&nbsp;for&nbsp;finding&nbsp;common&nbsp;kinds&nbsp;of&nbsp;events.&nbsp;Must&nbsp;always&nbsp;take&nbsp;an<br>
+#&nbsp;optinal&nbsp;recurisve&nbsp;parameter&nbsp;and&nbsp;be&nbsp;implemented&nbsp;in&nbsp;terms&nbsp;fo&nbsp;IterAllEvents.</tt></dd></dl>
+
+<dl><dt><a name="Thread-IterAllFlowEvents"><strong>IterAllFlowEvents</strong></a>(self, recursive<font color="#909090">=True</font>)</dt></dl>
+
+<dl><dt><a name="Thread-IterAllSlices"><strong>IterAllSlices</strong></a>(self, recursive<font color="#909090">=True</font>)</dt></dl>
+
+<dl><dt><a name="Thread-IterAllSlicesInRange"><strong>IterAllSlicesInRange</strong></a>(self, start, end, recursive<font color="#909090">=True</font>)</dt></dl>
+
+<dl><dt><a name="Thread-IterAllSlicesOfName"><strong>IterAllSlicesOfName</strong></a>(self, name, recursive<font color="#909090">=True</font>)</dt></dl>
+
+<dl><dt><a name="Thread-IterAllToplevelSlicesOfName"><strong>IterAllToplevelSlicesOfName</strong></a>(self, name, recursive<font color="#909090">=True</font>)</dt></dl>
+
+<hr>
+Static methods inherited from <a href="telemetry.timeline.event_container.html#TimelineEventContainer">telemetry.timeline.event_container.TimelineEventContainer</a>:<br>
+<dl><dt><a name="Thread-IsAsyncSlice"><strong>IsAsyncSlice</strong></a>(t)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.timeline.event_container.html#TimelineEventContainer">telemetry.timeline.event_container.TimelineEventContainer</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.trace_data.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.trace_data.html
new file mode 100644
index 0000000..26ed691
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.trace_data.html
@@ -0,0 +1,232 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.timeline.trace_data</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.timeline.html"><font color="#ffffff">timeline</font></a>.trace_data</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/timeline/trace_data.py">telemetry/timeline/trace_data.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="json.html">json</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.timeline.trace_data.html#TraceData">TraceData</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.timeline.trace_data.html#TraceDataBuilder">TraceDataBuilder</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.timeline.trace_data.html#TraceDataPart">TraceDataPart</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="exceptions.html#Exception">exceptions.Exception</a>(<a href="exceptions.html#BaseException">exceptions.BaseException</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.timeline.trace_data.html#NonSerializableTraceData">NonSerializableTraceData</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="NonSerializableTraceData">class <strong>NonSerializableTraceData</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Raised&nbsp;when&nbsp;raw&nbsp;trace&nbsp;data&nbsp;cannot&nbsp;be&nbsp;serialized&nbsp;to&nbsp;<a href="#TraceData">TraceData</a>.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.timeline.trace_data.html#NonSerializableTraceData">NonSerializableTraceData</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="NonSerializableTraceData-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#NonSerializableTraceData-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#NonSerializableTraceData-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="NonSerializableTraceData-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#NonSerializableTraceData-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="NonSerializableTraceData-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#NonSerializableTraceData-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="NonSerializableTraceData-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#NonSerializableTraceData-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="NonSerializableTraceData-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#NonSerializableTraceData-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="NonSerializableTraceData-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="NonSerializableTraceData-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#NonSerializableTraceData-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="NonSerializableTraceData-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#NonSerializableTraceData-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="NonSerializableTraceData-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="NonSerializableTraceData-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#NonSerializableTraceData-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="NonSerializableTraceData-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TraceData">class <strong>TraceData</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Validates,&nbsp;parses,&nbsp;and&nbsp;serializes&nbsp;raw&nbsp;data.<br>
+&nbsp;<br>
+NOTE:&nbsp;raw&nbsp;data&nbsp;must&nbsp;only&nbsp;include&nbsp;primitive&nbsp;objects!<br>
+By&nbsp;design,&nbsp;<a href="#TraceData">TraceData</a>&nbsp;must&nbsp;contain&nbsp;only&nbsp;data&nbsp;that&nbsp;is&nbsp;BOTH&nbsp;json-serializable<br>
+to&nbsp;a&nbsp;file,&nbsp;AND&nbsp;restorable&nbsp;once&nbsp;again&nbsp;from&nbsp;that&nbsp;file&nbsp;into&nbsp;<a href="#TraceData">TraceData</a>&nbsp;without<br>
+assistance&nbsp;from&nbsp;other&nbsp;classes.<br>
+&nbsp;<br>
+Raw&nbsp;data&nbsp;can&nbsp;be&nbsp;one&nbsp;of&nbsp;three&nbsp;standard&nbsp;trace_event&nbsp;formats:<br>
+1.&nbsp;Trace&nbsp;container&nbsp;format:&nbsp;a&nbsp;json-parseable&nbsp;dict.<br>
+2.&nbsp;A&nbsp;json-parseable&nbsp;array:&nbsp;assumed&nbsp;to&nbsp;be&nbsp;chrome&nbsp;trace&nbsp;data.<br>
+3.&nbsp;A&nbsp;json-parseable&nbsp;array&nbsp;missing&nbsp;the&nbsp;final&nbsp;']':&nbsp;assumed&nbsp;to&nbsp;be&nbsp;chrome&nbsp;trace<br>
+&nbsp;&nbsp;&nbsp;data.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="TraceData-GetEventsFor"><strong>GetEventsFor</strong></a>(self, part)</dt></dl>
+
+<dl><dt><a name="TraceData-HasEventsFor"><strong>HasEventsFor</strong></a>(self, part)</dt></dl>
+
+<dl><dt><a name="TraceData-Serialize"><strong>Serialize</strong></a>(self, f, gzip_result<font color="#909090">=False</font>)</dt><dd><tt>Serializes&nbsp;the&nbsp;trace&nbsp;result&nbsp;to&nbsp;a&nbsp;file-like&nbsp;<a href="__builtin__.html#object">object</a>.<br>
+&nbsp;<br>
+Always&nbsp;writes&nbsp;in&nbsp;the&nbsp;trace&nbsp;container&nbsp;format.</tt></dd></dl>
+
+<dl><dt><a name="TraceData-__init__"><strong>__init__</strong></a>(self, raw_data<font color="#909090">=None</font>)</dt><dd><tt>Creates&nbsp;<a href="#TraceData">TraceData</a>&nbsp;from&nbsp;the&nbsp;given&nbsp;data.</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>active_parts</strong></dt>
+</dl>
+<dl><dt><strong>events_are_safely_mutable</strong></dt>
+<dd><tt>Returns&nbsp;true&nbsp;if&nbsp;the&nbsp;events&nbsp;in&nbsp;this&nbsp;value&nbsp;are&nbsp;completely&nbsp;sealed.<br>
+&nbsp;<br>
+Some&nbsp;importers&nbsp;want&nbsp;to&nbsp;take&nbsp;complex&nbsp;fields&nbsp;out&nbsp;of&nbsp;the&nbsp;TraceData&nbsp;and&nbsp;add<br>
+them&nbsp;to&nbsp;the&nbsp;model,&nbsp;changing&nbsp;them&nbsp;subtly&nbsp;as&nbsp;they&nbsp;do&nbsp;so.&nbsp;If&nbsp;the&nbsp;TraceData<br>
+was&nbsp;constructed&nbsp;with&nbsp;data&nbsp;that&nbsp;is&nbsp;shared&nbsp;with&nbsp;something&nbsp;outside&nbsp;the&nbsp;trace<br>
+data,&nbsp;for&nbsp;instance&nbsp;a&nbsp;test&nbsp;harness,&nbsp;then&nbsp;this&nbsp;mutation&nbsp;is&nbsp;unexpected.&nbsp;But,<br>
+if&nbsp;the&nbsp;values&nbsp;are&nbsp;sealed,&nbsp;then&nbsp;mutating&nbsp;the&nbsp;events&nbsp;is&nbsp;a&nbsp;lot&nbsp;faster.<br>
+&nbsp;<br>
+We&nbsp;know&nbsp;if&nbsp;events&nbsp;are&nbsp;sealed&nbsp;if&nbsp;the&nbsp;value&nbsp;came&nbsp;from&nbsp;a&nbsp;string,&nbsp;or&nbsp;if&nbsp;the<br>
+value&nbsp;came&nbsp;from&nbsp;a&nbsp;TraceDataBuilder.</tt></dd>
+</dl>
+<dl><dt><strong>metadata_records</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TraceDataBuilder">class <strong>TraceDataBuilder</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt><a href="#TraceDataBuilder">TraceDataBuilder</a>&nbsp;helps&nbsp;build&nbsp;up&nbsp;a&nbsp;trace&nbsp;from&nbsp;multiple&nbsp;trace&nbsp;agents.<br>
+&nbsp;<br>
+<a href="#TraceData">TraceData</a>&nbsp;is&nbsp;supposed&nbsp;to&nbsp;be&nbsp;immutable,&nbsp;but&nbsp;it&nbsp;is&nbsp;useful&nbsp;during&nbsp;recording&nbsp;to<br>
+have&nbsp;a&nbsp;mutable&nbsp;version.&nbsp;That&nbsp;is&nbsp;<a href="#TraceDataBuilder">TraceDataBuilder</a>.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="TraceDataBuilder-AddEventsTo"><strong>AddEventsTo</strong></a>(self, part, events)</dt><dd><tt>Note:&nbsp;this&nbsp;won't&nbsp;work&nbsp;when&nbsp;called&nbsp;from&nbsp;multiple&nbsp;browsers.<br>
+&nbsp;<br>
+Each&nbsp;browser's&nbsp;trace_event_impl&nbsp;zeros&nbsp;its&nbsp;timestamps&nbsp;when&nbsp;it&nbsp;writes&nbsp;them<br>
+out&nbsp;and&nbsp;doesn't&nbsp;write&nbsp;a&nbsp;timebase&nbsp;that&nbsp;can&nbsp;be&nbsp;used&nbsp;to&nbsp;re-sync&nbsp;them.</tt></dd></dl>
+
+<dl><dt><a name="TraceDataBuilder-AsData"><strong>AsData</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TraceDataBuilder-HasEventsFor"><strong>HasEventsFor</strong></a>(self, part)</dt></dl>
+
+<dl><dt><a name="TraceDataBuilder-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TraceDataPart">class <strong>TraceDataPart</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt><a href="#TraceData">TraceData</a>&nbsp;can&nbsp;have&nbsp;a&nbsp;variety&nbsp;of&nbsp;events.<br>
+&nbsp;<br>
+These&nbsp;are&nbsp;called&nbsp;"parts"&nbsp;and&nbsp;are&nbsp;accessed&nbsp;by&nbsp;the&nbsp;following&nbsp;fixed&nbsp;field&nbsp;names.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="TraceDataPart-__init__"><strong>__init__</strong></a>(self, raw_field_name)</dt></dl>
+
+<dl><dt><a name="TraceDataPart-__repr__"><strong>__repr__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>raw_field_name</strong></dt>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>ALL_TRACE_PARTS</strong> = set([TraceDataPart("traceEvents"), TraceDataPart("inspectorTimelineEvents"), TraceDataPart("surfaceFlinger"), TraceDataPart("tabIds")])<br>
+<strong>CHROME_TRACE_PART</strong> = TraceDataPart("traceEvents")<br>
+<strong>INSPECTOR_TRACE_PART</strong> = TraceDataPart("inspectorTimelineEvents")<br>
+<strong>SURFACE_FLINGER_PART</strong> = TraceDataPart("surfaceFlinger")<br>
+<strong>TAB_ID_PART</strong> = TraceDataPart("tabIds")</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.trace_event_importer.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.trace_event_importer.html
new file mode 100644
index 0000000..897503f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.trace_event_importer.html
@@ -0,0 +1,81 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.timeline.trace_event_importer</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.timeline.html"><font color="#ffffff">timeline</font></a>.trace_event_importer</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/timeline/trace_event_importer.py">telemetry/timeline/trace_event_importer.py</a></font></td></tr></table>
+    <p><tt>TraceEventImporter&nbsp;imports&nbsp;TraceEvent-formatted&nbsp;data<br>
+into&nbsp;the&nbsp;provided&nbsp;model.<br>
+This&nbsp;is&nbsp;a&nbsp;port&nbsp;of&nbsp;the&nbsp;trace&nbsp;event&nbsp;importer&nbsp;from<br>
+https://code.google.com/p/trace-viewer/</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="collections.html">collections</a><br>
+<a href="copy.html">copy</a><br>
+</td><td width="25%" valign=top><a href="telemetry.timeline.importer.html">telemetry.timeline.importer</a><br>
+<a href="telemetry.timeline.memory_dump_event.html">telemetry.timeline.memory_dump_event</a><br>
+</td><td width="25%" valign=top><a href="telemetry.timeline.trace_data.html">telemetry.timeline.trace_data</a><br>
+<a href="telemetry.timeline.async_slice.html">telemetry.timeline.async_slice</a><br>
+</td><td width="25%" valign=top><a href="telemetry.timeline.flow_event.html">telemetry.timeline.flow_event</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.timeline.importer.html#TimelineImporter">telemetry.timeline.importer.TimelineImporter</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.timeline.trace_event_importer.html#TraceEventTimelineImporter">TraceEventTimelineImporter</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TraceEventTimelineImporter">class <strong>TraceEventTimelineImporter</strong></a>(<a href="telemetry.timeline.importer.html#TimelineImporter">telemetry.timeline.importer.TimelineImporter</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.timeline.trace_event_importer.html#TraceEventTimelineImporter">TraceEventTimelineImporter</a></dd>
+<dd><a href="telemetry.timeline.importer.html#TimelineImporter">telemetry.timeline.importer.TimelineImporter</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="TraceEventTimelineImporter-FinalizeImport"><strong>FinalizeImport</strong></a>(self)</dt><dd><tt>Called&nbsp;by&nbsp;the&nbsp;Model&nbsp;after&nbsp;all&nbsp;other&nbsp;importers&nbsp;have&nbsp;imported&nbsp;their<br>
+events.</tt></dd></dl>
+
+<dl><dt><a name="TraceEventTimelineImporter-ImportEvents"><strong>ImportEvents</strong></a>(self)</dt><dd><tt>Walks&nbsp;through&nbsp;the&nbsp;events_&nbsp;list&nbsp;and&nbsp;outputs&nbsp;the&nbsp;structures&nbsp;discovered&nbsp;to<br>
+model_.</tt></dd></dl>
+
+<dl><dt><a name="TraceEventTimelineImporter-__init__"><strong>__init__</strong></a>(self, model, trace_data)</dt></dl>
+
+<hr>
+Static methods defined here:<br>
+<dl><dt><a name="TraceEventTimelineImporter-GetSupportedPart"><strong>GetSupportedPart</strong></a>()</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.timeline.importer.html#TimelineImporter">telemetry.timeline.importer.TimelineImporter</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.tracing_category_filter.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.tracing_category_filter.html
new file mode 100644
index 0000000..2d4bea6
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.tracing_category_filter.html
@@ -0,0 +1,107 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.timeline.tracing_category_filter</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.timeline.html"><font color="#ffffff">timeline</font></a>.tracing_category_filter</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/timeline/tracing_category_filter.py">telemetry/timeline/tracing_category_filter.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="re.html">re</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.timeline.tracing_category_filter.html#TracingCategoryFilter">TracingCategoryFilter</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TracingCategoryFilter">class <strong>TracingCategoryFilter</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;set&nbsp;of&nbsp;included&nbsp;and&nbsp;excluded&nbsp;categories&nbsp;that&nbsp;should&nbsp;be&nbsp;traced.<br>
+&nbsp;<br>
+The&nbsp;TraceCategoryFilter&nbsp;allows&nbsp;fine&nbsp;tuning&nbsp;of&nbsp;what&nbsp;data&nbsp;is&nbsp;traced.&nbsp;Basic<br>
+choice&nbsp;of&nbsp;which&nbsp;tracers&nbsp;to&nbsp;use&nbsp;is&nbsp;done&nbsp;by&nbsp;TracingOptions.<br>
+&nbsp;<br>
+Providing&nbsp;filter_string=None&nbsp;gives&nbsp;the&nbsp;default&nbsp;category&nbsp;filter,&nbsp;which&nbsp;leaves<br>
+what&nbsp;to&nbsp;trace&nbsp;up&nbsp;to&nbsp;the&nbsp;individual&nbsp;trace&nbsp;systems.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="TracingCategoryFilter-AddExcludedCategory"><strong>AddExcludedCategory</strong></a>(self, category_glob)</dt><dd><tt>Explicitly&nbsp;disables&nbsp;anything&nbsp;matching&nbsp;category_glob.</tt></dd></dl>
+
+<dl><dt><a name="TracingCategoryFilter-AddIncludedCategory"><strong>AddIncludedCategory</strong></a>(self, category_glob)</dt><dd><tt>Explicitly&nbsp;enables&nbsp;anything&nbsp;matching&nbsp;category_glob.</tt></dd></dl>
+
+<dl><dt><a name="TracingCategoryFilter-AddSyntheticDelay"><strong>AddSyntheticDelay</strong></a>(self, delay)</dt></dl>
+
+<dl><dt><a name="TracingCategoryFilter-GetDictForChromeTracing"><strong>GetDictForChromeTracing</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TracingCategoryFilter-IsSubset"><strong>IsSubset</strong></a>(self, other)</dt><dd><tt>Determine&nbsp;if&nbsp;filter&nbsp;A&nbsp;(self)&nbsp;is&nbsp;a&nbsp;subset&nbsp;of&nbsp;filter&nbsp;B&nbsp;(other).<br>
+Returns&nbsp;True&nbsp;if&nbsp;A&nbsp;is&nbsp;a&nbsp;subset&nbsp;of&nbsp;B,&nbsp;False&nbsp;if&nbsp;A&nbsp;is&nbsp;not&nbsp;a&nbsp;subset&nbsp;of&nbsp;B,<br>
+and&nbsp;None&nbsp;if&nbsp;we&nbsp;can't&nbsp;tell&nbsp;for&nbsp;sure.</tt></dd></dl>
+
+<dl><dt><a name="TracingCategoryFilter-__init__"><strong>__init__</strong></a>(self, filter_string<font color="#909090">=None</font>)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>disabled_by_default_categories</strong></dt>
+</dl>
+<dl><dt><strong>excluded_categories</strong></dt>
+</dl>
+<dl><dt><strong>filter_string</strong></dt>
+</dl>
+<dl><dt><strong>included_categories</strong></dt>
+</dl>
+<dl><dt><strong>stable_filter_string</strong></dt>
+</dl>
+<dl><dt><strong>synthetic_delays</strong></dt>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-CreateDebugOverheadFilter"><strong>CreateDebugOverheadFilter</strong></a>()</dt><dd><tt>Returns&nbsp;a&nbsp;filter&nbsp;with&nbsp;as&nbsp;many&nbsp;traces&nbsp;enabled&nbsp;as&nbsp;is&nbsp;useful.</tt></dd></dl>
+ <dl><dt><a name="-CreateMinimalOverheadFilter"><strong>CreateMinimalOverheadFilter</strong></a>()</dt><dd><tt>Returns&nbsp;a&nbsp;filter&nbsp;with&nbsp;the&nbsp;best-effort&nbsp;amount&nbsp;of&nbsp;overhead.</tt></dd></dl>
+ <dl><dt><a name="-CreateNoOverheadFilter"><strong>CreateNoOverheadFilter</strong></a>()</dt><dd><tt>Returns&nbsp;a&nbsp;filter&nbsp;with&nbsp;the&nbsp;least&nbsp;overhead&nbsp;possible.<br>
+&nbsp;<br>
+This&nbsp;contains&nbsp;no&nbsp;sub-traces&nbsp;of&nbsp;thread&nbsp;tasks,&nbsp;so&nbsp;it's&nbsp;only&nbsp;useful&nbsp;for<br>
+capturing&nbsp;the&nbsp;cpu-time&nbsp;spent&nbsp;on&nbsp;threads&nbsp;(as&nbsp;well&nbsp;as&nbsp;needed&nbsp;benchmark<br>
+traces).<br>
+&nbsp;<br>
+FIXME:&nbsp;Remove&nbsp;webkit.console&nbsp;when&nbsp;blink.console&nbsp;lands&nbsp;in&nbsp;chromium&nbsp;and<br>
+the&nbsp;ref&nbsp;builds&nbsp;are&nbsp;updated.&nbsp;crbug.com/386847</tt></dd></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.tracing_config.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.tracing_config.html
new file mode 100644
index 0000000..2208080
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.tracing_config.html
@@ -0,0 +1,69 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.timeline.tracing_config</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.timeline.html"><font color="#ffffff">timeline</font></a>.tracing_config</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/timeline/tracing_config.py">telemetry/timeline/tracing_config.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2015&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="json.html">json</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.timeline.tracing_config.html#TracingConfig">TracingConfig</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TracingConfig">class <strong>TracingConfig</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Tracing&nbsp;config&nbsp;is&nbsp;the&nbsp;configuration&nbsp;for&nbsp;Chrome&nbsp;tracing.<br>
+&nbsp;<br>
+This&nbsp;produces&nbsp;the&nbsp;trace&nbsp;config&nbsp;JSON&nbsp;string&nbsp;for&nbsp;Chrome&nbsp;tracing.&nbsp;For&nbsp;the&nbsp;details<br>
+about&nbsp;the&nbsp;JSON&nbsp;string&nbsp;format,&nbsp;see&nbsp;base/trace_event/trace_config.h.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="TracingConfig-GetTraceConfigJsonString"><strong>GetTraceConfigJsonString</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TracingConfig-__init__"><strong>__init__</strong></a>(self, tracing_options, tracing_category_filter)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>tracing_category_filter</strong></dt>
+</dl>
+<dl><dt><strong>tracing_options</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.tracing_options.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.tracing_options.html
new file mode 100644
index 0000000..9db89e5
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.timeline.tracing_options.html
@@ -0,0 +1,91 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.timeline.tracing_options</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.timeline.html"><font color="#ffffff">timeline</font></a>.tracing_options</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/timeline/tracing_options.py">telemetry/timeline/tracing_options.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.timeline.tracing_options.html#TracingOptions">TracingOptions</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TracingOptions">class <strong>TracingOptions</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Tracing&nbsp;options&nbsp;control&nbsp;which&nbsp;core&nbsp;tracing&nbsp;systems&nbsp;should&nbsp;be&nbsp;enabled.<br>
+&nbsp;<br>
+This&nbsp;simply&nbsp;turns&nbsp;on&nbsp;those&nbsp;systems.&nbsp;If&nbsp;those&nbsp;systems&nbsp;have&nbsp;additional&nbsp;options,<br>
+e.g.&nbsp;what&nbsp;to&nbsp;trace,&nbsp;then&nbsp;they&nbsp;are&nbsp;typically&nbsp;configured&nbsp;by&nbsp;adding<br>
+categories&nbsp;to&nbsp;the&nbsp;TracingCategoryFilter.<br>
+&nbsp;<br>
+Options:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;enable_chrome_trace:&nbsp;a&nbsp;boolean&nbsp;that&nbsp;specifies&nbsp;whether&nbsp;to&nbsp;enable<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;chrome&nbsp;tracing.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;enable_platform_display_trace:&nbsp;a&nbsp;boolean&nbsp;that&nbsp;specifies&nbsp;whether&nbsp;to<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;platform&nbsp;display&nbsp;tracing.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;enable_android_graphics_memtrack:&nbsp;a&nbsp;boolean&nbsp;that&nbsp;specifies&nbsp;whether<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;to&nbsp;enable&nbsp;the&nbsp;memtrack_helper&nbsp;daemon&nbsp;to&nbsp;track&nbsp;graphics&nbsp;memory&nbsp;on<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Android&nbsp;(see&nbsp;goo.gl/4Y30p9).&nbsp;Doesn't&nbsp;have&nbsp;any&nbsp;effects&nbsp;on&nbsp;other&nbsp;OSs.<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;The&nbsp;following&nbsp;ones&nbsp;are&nbsp;specific&nbsp;to&nbsp;chrome&nbsp;tracing.&nbsp;See<br>
+&nbsp;&nbsp;&nbsp;&nbsp;base/trace_event/trace_config.h&nbsp;for&nbsp;more&nbsp;information.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;record_mode:&nbsp;can&nbsp;be&nbsp;any&nbsp;mode&nbsp;in&nbsp;RECORD_MODES.&nbsp;This&nbsp;corresponds&nbsp;to<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;record&nbsp;modes&nbsp;in&nbsp;chrome.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;enable_systrace:&nbsp;a&nbsp;boolean&nbsp;that&nbsp;specifies&nbsp;whether&nbsp;to&nbsp;enable&nbsp;systrace.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="TracingOptions-GetDictForChromeTracing"><strong>GetDictForChromeTracing</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TracingOptions-GetTraceOptionsStringForChromeDevtool"><strong>GetTraceOptionsStringForChromeDevtool</strong></a>(self)</dt><dd><tt>Map&nbsp;Chrome&nbsp;tracing&nbsp;options&nbsp;in&nbsp;Telemetry&nbsp;to&nbsp;the&nbsp;DevTools&nbsp;API&nbsp;string.</tt></dd></dl>
+
+<dl><dt><a name="TracingOptions-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>enable_systrace</strong></dt>
+</dl>
+<dl><dt><strong>record_mode</strong></dt>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>ECHO_TO_CONSOLE</strong> = 'trace-to-console'<br>
+<strong>ENABLE_SYSTRACE</strong> = 'enable-systrace'<br>
+<strong>RECORD_AS_MUCH_AS_POSSIBLE</strong> = 'record-as-much-as-possible'<br>
+<strong>RECORD_CONTINUOUSLY</strong> = 'record-continuously'<br>
+<strong>RECORD_MODES</strong> = ['record-until-full', 'record-continuously', 'record-as-much-as-possible', 'trace-to-console']<br>
+<strong>RECORD_UNTIL_FULL</strong> = 'record-until-full'</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.util.color_histogram.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.util.color_histogram.html
new file mode 100644
index 0000000..f52aaba
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.util.color_histogram.html
@@ -0,0 +1,159 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.util.color_histogram</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.util.html"><font color="#ffffff">util</font></a>.color_histogram</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/util/color_histogram.py">telemetry/util/color_histogram.py</a></font></td></tr></table>
+    <p><tt>Color&nbsp;Histograms&nbsp;and&nbsp;implementations&nbsp;of&nbsp;functions&nbsp;operating&nbsp;on&nbsp;them.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="collections.html">collections</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.util.external_modules.html">telemetry.internal.util.external_modules</a><br>
+</td><td width="25%" valign=top><a href="numpy.html">numpy</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial">ColorHistogram(<a href="__builtin__.html#tuple">__builtin__.tuple</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.util.color_histogram.html#ColorHistogram">ColorHistogram</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ColorHistogram">class <strong>ColorHistogram</strong></a>(ColorHistogram)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.util.color_histogram.html#ColorHistogram">ColorHistogram</a></dd>
+<dd>ColorHistogram</dd>
+<dd><a href="__builtin__.html#tuple">__builtin__.tuple</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="ColorHistogram-Distance"><strong>Distance</strong></a>(self, other)</dt></dl>
+
+<hr>
+Static methods defined here:<br>
+<dl><dt><a name="ColorHistogram-__new__"><strong>__new__</strong></a>(cls, r, g, b, default_color<font color="#909090">=None</font>)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from ColorHistogram:<br>
+<dl><dt><a name="ColorHistogram-__getnewargs__"><strong>__getnewargs__</strong></a>(self)</dt><dd><tt>Return&nbsp;self&nbsp;as&nbsp;a&nbsp;plain&nbsp;tuple.&nbsp;&nbsp;Used&nbsp;by&nbsp;copy&nbsp;and&nbsp;pickle.</tt></dd></dl>
+
+<dl><dt><a name="ColorHistogram-__getstate__"><strong>__getstate__</strong></a>(self)</dt><dd><tt>Exclude&nbsp;the&nbsp;OrderedDict&nbsp;from&nbsp;pickling</tt></dd></dl>
+
+<dl><dt><a name="ColorHistogram-__repr__"><strong>__repr__</strong></a>(self)</dt><dd><tt>Return&nbsp;a&nbsp;nicely&nbsp;formatted&nbsp;representation&nbsp;string</tt></dd></dl>
+
+<dl><dt><a name="ColorHistogram-_asdict"><strong>_asdict</strong></a>(self)</dt><dd><tt>Return&nbsp;a&nbsp;new&nbsp;OrderedDict&nbsp;which&nbsp;maps&nbsp;field&nbsp;names&nbsp;to&nbsp;their&nbsp;values</tt></dd></dl>
+
+<dl><dt><a name="ColorHistogram-_replace"><strong>_replace</strong></a>(_self, **kwds)</dt><dd><tt>Return&nbsp;a&nbsp;new&nbsp;<a href="#ColorHistogram">ColorHistogram</a>&nbsp;object&nbsp;replacing&nbsp;specified&nbsp;fields&nbsp;with&nbsp;new&nbsp;values</tt></dd></dl>
+
+<hr>
+Class methods inherited from ColorHistogram:<br>
+<dl><dt><a name="ColorHistogram-_make"><strong>_make</strong></a>(cls, iterable, new<font color="#909090">=&lt;built-in method __new__ of type object&gt;</font>, len<font color="#909090">=&lt;built-in function len&gt;</font>)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Make&nbsp;a&nbsp;new&nbsp;<a href="#ColorHistogram">ColorHistogram</a>&nbsp;object&nbsp;from&nbsp;a&nbsp;sequence&nbsp;or&nbsp;iterable</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from ColorHistogram:<br>
+<dl><dt><strong>b</strong></dt>
+<dd><tt>Alias&nbsp;for&nbsp;field&nbsp;number&nbsp;2</tt></dd>
+</dl>
+<dl><dt><strong>default_color</strong></dt>
+<dd><tt>Alias&nbsp;for&nbsp;field&nbsp;number&nbsp;3</tt></dd>
+</dl>
+<dl><dt><strong>g</strong></dt>
+<dd><tt>Alias&nbsp;for&nbsp;field&nbsp;number&nbsp;1</tt></dd>
+</dl>
+<dl><dt><strong>r</strong></dt>
+<dd><tt>Alias&nbsp;for&nbsp;field&nbsp;number&nbsp;0</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from ColorHistogram:<br>
+<dl><dt><strong>_fields</strong> = ('r', 'g', 'b', 'default_color')</dl>
+
+<hr>
+Methods inherited from <a href="__builtin__.html#tuple">__builtin__.tuple</a>:<br>
+<dl><dt><a name="ColorHistogram-__add__"><strong>__add__</strong></a>(...)</dt><dd><tt>x.<a href="#ColorHistogram-__add__">__add__</a>(y)&nbsp;&lt;==&gt;&nbsp;x+y</tt></dd></dl>
+
+<dl><dt><a name="ColorHistogram-__contains__"><strong>__contains__</strong></a>(...)</dt><dd><tt>x.<a href="#ColorHistogram-__contains__">__contains__</a>(y)&nbsp;&lt;==&gt;&nbsp;y&nbsp;in&nbsp;x</tt></dd></dl>
+
+<dl><dt><a name="ColorHistogram-__eq__"><strong>__eq__</strong></a>(...)</dt><dd><tt>x.<a href="#ColorHistogram-__eq__">__eq__</a>(y)&nbsp;&lt;==&gt;&nbsp;x==y</tt></dd></dl>
+
+<dl><dt><a name="ColorHistogram-__ge__"><strong>__ge__</strong></a>(...)</dt><dd><tt>x.<a href="#ColorHistogram-__ge__">__ge__</a>(y)&nbsp;&lt;==&gt;&nbsp;x&gt;=y</tt></dd></dl>
+
+<dl><dt><a name="ColorHistogram-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#ColorHistogram-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="ColorHistogram-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#ColorHistogram-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="ColorHistogram-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#ColorHistogram-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="ColorHistogram-__gt__"><strong>__gt__</strong></a>(...)</dt><dd><tt>x.<a href="#ColorHistogram-__gt__">__gt__</a>(y)&nbsp;&lt;==&gt;&nbsp;x&gt;y</tt></dd></dl>
+
+<dl><dt><a name="ColorHistogram-__hash__"><strong>__hash__</strong></a>(...)</dt><dd><tt>x.<a href="#ColorHistogram-__hash__">__hash__</a>()&nbsp;&lt;==&gt;&nbsp;hash(x)</tt></dd></dl>
+
+<dl><dt><a name="ColorHistogram-__iter__"><strong>__iter__</strong></a>(...)</dt><dd><tt>x.<a href="#ColorHistogram-__iter__">__iter__</a>()&nbsp;&lt;==&gt;&nbsp;iter(x)</tt></dd></dl>
+
+<dl><dt><a name="ColorHistogram-__le__"><strong>__le__</strong></a>(...)</dt><dd><tt>x.<a href="#ColorHistogram-__le__">__le__</a>(y)&nbsp;&lt;==&gt;&nbsp;x&lt;=y</tt></dd></dl>
+
+<dl><dt><a name="ColorHistogram-__len__"><strong>__len__</strong></a>(...)</dt><dd><tt>x.<a href="#ColorHistogram-__len__">__len__</a>()&nbsp;&lt;==&gt;&nbsp;len(x)</tt></dd></dl>
+
+<dl><dt><a name="ColorHistogram-__lt__"><strong>__lt__</strong></a>(...)</dt><dd><tt>x.<a href="#ColorHistogram-__lt__">__lt__</a>(y)&nbsp;&lt;==&gt;&nbsp;x&lt;y</tt></dd></dl>
+
+<dl><dt><a name="ColorHistogram-__mul__"><strong>__mul__</strong></a>(...)</dt><dd><tt>x.<a href="#ColorHistogram-__mul__">__mul__</a>(n)&nbsp;&lt;==&gt;&nbsp;x*n</tt></dd></dl>
+
+<dl><dt><a name="ColorHistogram-__ne__"><strong>__ne__</strong></a>(...)</dt><dd><tt>x.<a href="#ColorHistogram-__ne__">__ne__</a>(y)&nbsp;&lt;==&gt;&nbsp;x!=y</tt></dd></dl>
+
+<dl><dt><a name="ColorHistogram-__rmul__"><strong>__rmul__</strong></a>(...)</dt><dd><tt>x.<a href="#ColorHistogram-__rmul__">__rmul__</a>(n)&nbsp;&lt;==&gt;&nbsp;n*x</tt></dd></dl>
+
+<dl><dt><a name="ColorHistogram-__sizeof__"><strong>__sizeof__</strong></a>(...)</dt><dd><tt>T.<a href="#ColorHistogram-__sizeof__">__sizeof__</a>()&nbsp;--&nbsp;size&nbsp;of&nbsp;T&nbsp;in&nbsp;memory,&nbsp;in&nbsp;bytes</tt></dd></dl>
+
+<dl><dt><a name="ColorHistogram-count"><strong>count</strong></a>(...)</dt><dd><tt>T.<a href="#ColorHistogram-count">count</a>(value)&nbsp;-&gt;&nbsp;integer&nbsp;--&nbsp;return&nbsp;number&nbsp;of&nbsp;occurrences&nbsp;of&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="ColorHistogram-index"><strong>index</strong></a>(...)</dt><dd><tt>T.<a href="#ColorHistogram-index">index</a>(value,&nbsp;[start,&nbsp;[stop]])&nbsp;-&gt;&nbsp;integer&nbsp;--&nbsp;return&nbsp;first&nbsp;index&nbsp;of&nbsp;value.<br>
+Raises&nbsp;ValueError&nbsp;if&nbsp;the&nbsp;value&nbsp;is&nbsp;not&nbsp;present.</tt></dd></dl>
+
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-HistogramDistance"><strong>HistogramDistance</strong></a>(hist1, hist2, default_color<font color="#909090">=None</font>)</dt><dd><tt>Earth&nbsp;mover's&nbsp;distance.<br>
+<a href="http://en.wikipedia.org/wiki/Earth_mover's_distance">http://en.wikipedia.org/wiki/Earth_mover's_distance</a></tt></dd></dl>
+</td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>division</strong> = _Feature((2, 2, 0, 'alpha', 2), (3, 0, 0, 'alpha', 0), 8192)</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.util.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.util.html
new file mode 100644
index 0000000..b8777e7
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.util.html
@@ -0,0 +1,36 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: package telemetry.util</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.util</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/util/__init__.py">telemetry/util/__init__.py</a></font></td></tr></table>
+    <p><tt>A&nbsp;library&nbsp;for&nbsp;bootstrapping&nbsp;Telemetry&nbsp;preformance&nbsp;testing.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Package Contents</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.util.color_histogram.html">color_histogram</a><br>
+<a href="telemetry.util.color_histogram_unittest.html">color_histogram_unittest</a><br>
+<a href="telemetry.util.image_util.html">image_util</a><br>
+<a href="telemetry.util.image_util_unittest.html">image_util_unittest</a><br>
+</td><td width="25%" valign=top><a href="telemetry.util.mac.html"><strong>mac</strong>&nbsp;(package)</a><br>
+<a href="telemetry.util.perf_result_data_type.html">perf_result_data_type</a><br>
+<a href="telemetry.util.perf_tests_helper.html">perf_tests_helper</a><br>
+<a href="telemetry.util.perf_tests_results_helper.html">perf_tests_results_helper</a><br>
+</td><td width="25%" valign=top><a href="telemetry.util.process_statistic_timeline_data.html">process_statistic_timeline_data</a><br>
+<a href="telemetry.util.process_statistic_timeline_data_unittest.html">process_statistic_timeline_data_unittest</a><br>
+<a href="telemetry.util.rgba_color.html">rgba_color</a><br>
+<a href="telemetry.util.statistics.html">statistics</a><br>
+</td><td width="25%" valign=top><a href="telemetry.util.statistics_unittest.html">statistics_unittest</a><br>
+<a href="telemetry.util.wpr_modes.html">wpr_modes</a><br>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.util.image_util.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.util.image_util.html
new file mode 100644
index 0000000..6e94ebe
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.util.image_util.html
@@ -0,0 +1,90 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.util.image_util</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.util.html"><font color="#ffffff">util</font></a>.image_util</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/util/image_util.py">telemetry/util/image_util.py</a></font></td></tr></table>
+    <p><tt>Provides&nbsp;implementations&nbsp;of&nbsp;basic&nbsp;image&nbsp;processing&nbsp;functions.<br>
+&nbsp;<br>
+Implements&nbsp;basic&nbsp;image&nbsp;processing&nbsp;functions,&nbsp;such&nbsp;as&nbsp;reading/writing&nbsp;images,<br>
+cropping,&nbsp;finding&nbsp;the&nbsp;bounding&nbsp;box&nbsp;of&nbsp;a&nbsp;color&nbsp;and&nbsp;diffing&nbsp;images.<br>
+&nbsp;<br>
+When&nbsp;numpy&nbsp;is&nbsp;present,&nbsp;image_util_numpy_impl&nbsp;is&nbsp;used&nbsp;for&nbsp;the&nbsp;implementation&nbsp;of<br>
+this&nbsp;interface.&nbsp;The&nbsp;old&nbsp;bitmap&nbsp;implementation&nbsp;(image_util_bitmap_impl)&nbsp;is&nbsp;used<br>
+as&nbsp;a&nbsp;fallback&nbsp;when&nbsp;numpy&nbsp;is&nbsp;not&nbsp;present.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="base64.html">base64</a><br>
+<a href="telemetry.internal.util.external_modules.html">telemetry.internal.util.external_modules</a><br>
+</td><td width="25%" valign=top><a href="telemetry.internal.image_processing.image_util_numpy_impl.html">telemetry.internal.image_processing.image_util_numpy_impl</a><br>
+<a href="telemetry.internal.image_processing.image_util_numpy_impl.html">telemetry.internal.image_processing.image_util_numpy_impl</a><br>
+</td><td width="25%" valign=top><a href="numpy.html">numpy</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-AreEqual"><strong>AreEqual</strong></a>(image1, image2, tolerance<font color="#909090">=0</font>, likely_equal<font color="#909090">=True</font>)</dt><dd><tt>Determines&nbsp;whether&nbsp;two&nbsp;images&nbsp;are&nbsp;identical&nbsp;within&nbsp;a&nbsp;given&nbsp;tolerance.<br>
+Setting&nbsp;likely_equal&nbsp;to&nbsp;False&nbsp;enables&nbsp;short-circuit&nbsp;equality&nbsp;testing,&nbsp;which<br>
+is&nbsp;about&nbsp;2-3x&nbsp;slower&nbsp;for&nbsp;equal&nbsp;images,&nbsp;but&nbsp;can&nbsp;be&nbsp;image&nbsp;height&nbsp;times&nbsp;faster<br>
+if&nbsp;the&nbsp;images&nbsp;are&nbsp;not&nbsp;equal.</tt></dd></dl>
+ <dl><dt><a name="-Channels"><strong>Channels</strong></a>(image)</dt><dd><tt>Number&nbsp;of&nbsp;color&nbsp;channels&nbsp;in&nbsp;the&nbsp;image.</tt></dd></dl>
+ <dl><dt><a name="-Crop"><strong>Crop</strong></a>(image, left, top, width, height)</dt><dd><tt>Crops&nbsp;the&nbsp;current&nbsp;image&nbsp;down&nbsp;to&nbsp;the&nbsp;specified&nbsp;box.</tt></dd></dl>
+ <dl><dt><a name="-Diff"><strong>Diff</strong></a>(image1, image2)</dt><dd><tt>Returns&nbsp;a&nbsp;new&nbsp;image&nbsp;that&nbsp;represents&nbsp;the&nbsp;difference&nbsp;between&nbsp;this&nbsp;image<br>
+and&nbsp;another&nbsp;image.</tt></dd></dl>
+ <dl><dt><a name="-FromBase64Png"><strong>FromBase64Png</strong></a>(base64_png)</dt><dd><tt>Create&nbsp;an&nbsp;image&nbsp;from&nbsp;raw&nbsp;PNG&nbsp;data&nbsp;encoded&nbsp;in&nbsp;base64.</tt></dd></dl>
+ <dl><dt><a name="-FromPng"><strong>FromPng</strong></a>(png_data)</dt><dd><tt>Create&nbsp;an&nbsp;image&nbsp;from&nbsp;raw&nbsp;PNG&nbsp;data.</tt></dd></dl>
+ <dl><dt><a name="-FromPngFile"><strong>FromPngFile</strong></a>(path)</dt><dd><tt>Create&nbsp;an&nbsp;image&nbsp;from&nbsp;a&nbsp;PNG&nbsp;file.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;path:&nbsp;The&nbsp;path&nbsp;to&nbsp;the&nbsp;PNG&nbsp;file.</tt></dd></dl>
+ <dl><dt><a name="-FromRGBPixels"><strong>FromRGBPixels</strong></a>(width, height, pixels, bpp<font color="#909090">=3</font>)</dt><dd><tt>Create&nbsp;an&nbsp;image&nbsp;from&nbsp;an&nbsp;array&nbsp;of&nbsp;rgb&nbsp;pixels.<br>
+&nbsp;<br>
+Ignores&nbsp;alpha&nbsp;channel&nbsp;if&nbsp;present.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;width,&nbsp;height:&nbsp;int,&nbsp;the&nbsp;width&nbsp;and&nbsp;height&nbsp;of&nbsp;the&nbsp;image.<br>
+&nbsp;&nbsp;pixels:&nbsp;The&nbsp;flat&nbsp;array&nbsp;of&nbsp;pixels&nbsp;in&nbsp;the&nbsp;form&nbsp;of&nbsp;[r,g,b[,a],r,g,b[,a],...]<br>
+&nbsp;&nbsp;bpp:&nbsp;3&nbsp;for&nbsp;RGB,&nbsp;4&nbsp;for&nbsp;RGBA.</tt></dd></dl>
+ <dl><dt><a name="-GetBoundingBox"><strong>GetBoundingBox</strong></a>(image, color, tolerance<font color="#909090">=0</font>)</dt><dd><tt>Finds&nbsp;the&nbsp;minimum&nbsp;box&nbsp;surrounding&nbsp;all&nbsp;occurrences&nbsp;of&nbsp;bgr&nbsp;|color|.<br>
+&nbsp;<br>
+Ignores&nbsp;the&nbsp;alpha&nbsp;channel.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;color:&nbsp;RbgaColor,&nbsp;bounding&nbsp;box&nbsp;color.<br>
+&nbsp;&nbsp;tolerance:&nbsp;int,&nbsp;per-channel&nbsp;tolerance&nbsp;for&nbsp;the&nbsp;bounding&nbsp;box&nbsp;color.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;(top,&nbsp;left,&nbsp;width,&nbsp;height),&nbsp;match_count</tt></dd></dl>
+ <dl><dt><a name="-GetColorHistogram"><strong>GetColorHistogram</strong></a>(image, ignore_color<font color="#909090">=None</font>, tolerance<font color="#909090">=0</font>)</dt><dd><tt>Computes&nbsp;a&nbsp;histogram&nbsp;of&nbsp;the&nbsp;pixel&nbsp;colors&nbsp;in&nbsp;this&nbsp;image.<br>
+Args:<br>
+&nbsp;&nbsp;ignore_color:&nbsp;An&nbsp;RgbaColor&nbsp;to&nbsp;exclude&nbsp;from&nbsp;the&nbsp;bucket&nbsp;counts.<br>
+&nbsp;&nbsp;tolerance:&nbsp;A&nbsp;tolerance&nbsp;for&nbsp;the&nbsp;ignore_color.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;A&nbsp;ColorHistogram&nbsp;namedtuple&nbsp;with&nbsp;256&nbsp;integers&nbsp;in&nbsp;each&nbsp;field:&nbsp;r,&nbsp;g,&nbsp;and&nbsp;b.</tt></dd></dl>
+ <dl><dt><a name="-GetPixelColor"><strong>GetPixelColor</strong></a>(image, x, y)</dt><dd><tt>Returns&nbsp;a&nbsp;RgbaColor&nbsp;for&nbsp;the&nbsp;pixel&nbsp;at&nbsp;(x,&nbsp;y).</tt></dd></dl>
+ <dl><dt><a name="-Height"><strong>Height</strong></a>(image)</dt><dd><tt>Height&nbsp;of&nbsp;the&nbsp;image.</tt></dd></dl>
+ <dl><dt><a name="-Pixels"><strong>Pixels</strong></a>(image)</dt><dd><tt>Flat&nbsp;RGB&nbsp;pixel&nbsp;array&nbsp;of&nbsp;the&nbsp;image.</tt></dd></dl>
+ <dl><dt><a name="-Width"><strong>Width</strong></a>(image)</dt><dd><tt>Width&nbsp;of&nbsp;the&nbsp;image.</tt></dd></dl>
+ <dl><dt><a name="-WritePngFile"><strong>WritePngFile</strong></a>(image, path)</dt><dd><tt>Write&nbsp;an&nbsp;image&nbsp;to&nbsp;a&nbsp;PNG&nbsp;file.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;image:&nbsp;an&nbsp;image&nbsp;object.<br>
+&nbsp;&nbsp;path:&nbsp;The&nbsp;path&nbsp;to&nbsp;the&nbsp;PNG&nbsp;file.&nbsp;Must&nbsp;end&nbsp;in&nbsp;'png'&nbsp;or&nbsp;an<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;AssertionError&nbsp;will&nbsp;be&nbsp;raised.</tt></dd></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.util.mac.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.util.mac.html
new file mode 100644
index 0000000..6d999cf
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.util.mac.html
@@ -0,0 +1,25 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: package telemetry.util.mac</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.util.html"><font color="#ffffff">util</font></a>.mac</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/util/mac/__init__.py">telemetry/util/mac/__init__.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Package Contents</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.util.mac.keychain_helper.html">keychain_helper</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.util.mac.keychain_helper.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.util.mac.keychain_helper.html
new file mode 100644
index 0000000..870469d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.util.mac.keychain_helper.html
@@ -0,0 +1,43 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.util.mac.keychain_helper</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.util.html"><font color="#ffffff">util</font></a>.<a href="telemetry.util.mac.html"><font color="#ffffff">mac</font></a>.keychain_helper</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/util/mac/keychain_helper.py">telemetry/util/mac/keychain_helper.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.internal.util.binary_manager.html">telemetry.internal.util.binary_manager</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.os_version.html">telemetry.core.os_version</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.platform.html">telemetry.core.platform</a><br>
+</td><td width="25%" valign=top><a href="subprocess.html">subprocess</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-DoesKeychainHaveTimeout"><strong>DoesKeychainHaveTimeout</strong></a>()</dt><dd><tt>Returns&nbsp;True&nbsp;if&nbsp;the&nbsp;keychain&nbsp;will&nbsp;lock&nbsp;itself&nbsp;have&nbsp;a&nbsp;period&nbsp;of&nbsp;time.<br>
+&nbsp;<br>
+This&nbsp;method&nbsp;will&nbsp;trigger&nbsp;a&nbsp;blocking,&nbsp;modal&nbsp;dialog&nbsp;if&nbsp;the&nbsp;keychain&nbsp;is<br>
+locked.</tt></dd></dl>
+ <dl><dt><a name="-IsKeychainConfiguredForBotsWithChrome"><strong>IsKeychainConfiguredForBotsWithChrome</strong></a>()</dt></dl>
+ <dl><dt><a name="-IsKeychainConfiguredForBotsWithChromium"><strong>IsKeychainConfiguredForBotsWithChromium</strong></a>()</dt></dl>
+ <dl><dt><a name="-IsKeychainLocked"><strong>IsKeychainLocked</strong></a>()</dt><dd><tt>Returns&nbsp;True&nbsp;if&nbsp;the&nbsp;keychain&nbsp;is&nbsp;locked,&nbsp;or&nbsp;if&nbsp;there&nbsp;is&nbsp;an&nbsp;error&nbsp;determining<br>
+the&nbsp;keychain&nbsp;state.</tt></dd></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.util.perf_result_data_type.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.util.perf_result_data_type.html
new file mode 100644
index 0000000..5265b99
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.util.perf_result_data_type.html
@@ -0,0 +1,38 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.util.perf_result_data_type</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.util.html"><font color="#ffffff">util</font></a>.perf_result_data_type</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/util/perf_result_data_type.py">telemetry/util/perf_result_data_type.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-IsHistogram"><strong>IsHistogram</strong></a>(datatype)</dt></dl>
+ <dl><dt><a name="-IsValidType"><strong>IsValidType</strong></a>(datatype)</dt></dl>
+</td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>ALL_TYPES</strong> = ['default', 'unimportant', 'histogram', 'unimportant-histogram', 'informational']<br>
+<strong>DEFAULT</strong> = 'default'<br>
+<strong>HISTOGRAM</strong> = 'histogram'<br>
+<strong>INFORMATIONAL</strong> = 'informational'<br>
+<strong>UNIMPORTANT</strong> = 'unimportant'<br>
+<strong>UNIMPORTANT_HISTOGRAM</strong> = 'unimportant-histogram'</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.util.perf_tests_helper.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.util.perf_tests_helper.html
new file mode 100644
index 0000000..6feac66
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.util.perf_tests_helper.html
@@ -0,0 +1,25 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.util.perf_tests_helper</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.util.html"><font color="#ffffff">util</font></a>.perf_tests_helper</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/util/perf_tests_helper.py">telemetry/util/perf_tests_helper.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.util.perf_tests_results_helper.html">telemetry.util.perf_tests_results_helper</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.util.perf_tests_results_helper.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.util.perf_tests_results_helper.html
new file mode 100644
index 0000000..e8bb07b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.util.perf_tests_results_helper.html
@@ -0,0 +1,68 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.util.perf_tests_results_helper</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.util.html"><font color="#ffffff">util</font></a>.perf_tests_results_helper</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/util/perf_tests_results_helper.py">telemetry/util/perf_tests_results_helper.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="json.html">json</a><br>
+<a href="math.html">math</a><br>
+</td><td width="25%" valign=top><a href="telemetry.util.perf_result_data_type.html">telemetry.util.perf_result_data_type</a><br>
+<a href="re.html">re</a><br>
+</td><td width="25%" valign=top><a href="sys.html">sys</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-FlattenList"><strong>FlattenList</strong></a>(values)</dt><dd><tt>Returns&nbsp;a&nbsp;simple&nbsp;list&nbsp;without&nbsp;sub-lists.</tt></dd></dl>
+ <dl><dt><a name="-GeomMeanAndStdDevFromHistogram"><strong>GeomMeanAndStdDevFromHistogram</strong></a>(histogram_json)</dt></dl>
+ <dl><dt><a name="-PrintPages"><strong>PrintPages</strong></a>(page_list)</dt><dd><tt>Prints&nbsp;list&nbsp;of&nbsp;pages&nbsp;to&nbsp;stdout&nbsp;in&nbsp;the&nbsp;format&nbsp;required&nbsp;by&nbsp;perf&nbsp;tests.</tt></dd></dl>
+ <dl><dt><a name="-PrintPerfResult"><strong>PrintPerfResult</strong></a>(measurement, trace, values, units, result_type<font color="#909090">='default'</font>, print_to_stdout<font color="#909090">=True</font>)</dt><dd><tt>Prints&nbsp;numerical&nbsp;data&nbsp;to&nbsp;stdout&nbsp;in&nbsp;the&nbsp;format&nbsp;required&nbsp;by&nbsp;perf&nbsp;tests.<br>
+&nbsp;<br>
+The&nbsp;string&nbsp;args&nbsp;may&nbsp;be&nbsp;empty&nbsp;but&nbsp;they&nbsp;must&nbsp;not&nbsp;contain&nbsp;any&nbsp;colons&nbsp;(:)&nbsp;or<br>
+equals&nbsp;signs&nbsp;(=).<br>
+This&nbsp;is&nbsp;parsed&nbsp;by&nbsp;the&nbsp;buildbot&nbsp;using:<br>
+<a href="http://src.chromium.org/viewvc/chrome/trunk/tools/build/scripts/slave/process_log_utils.py">http://src.chromium.org/viewvc/chrome/trunk/tools/build/scripts/slave/process_log_utils.py</a><br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;measurement:&nbsp;A&nbsp;description&nbsp;of&nbsp;the&nbsp;quantity&nbsp;being&nbsp;measured,&nbsp;e.g.&nbsp;"vm_peak".<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;On&nbsp;the&nbsp;dashboard,&nbsp;this&nbsp;maps&nbsp;to&nbsp;a&nbsp;particular&nbsp;graph.&nbsp;Mandatory.<br>
+&nbsp;&nbsp;trace:&nbsp;A&nbsp;description&nbsp;of&nbsp;the&nbsp;particular&nbsp;data&nbsp;point,&nbsp;e.g.&nbsp;"reference".<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;On&nbsp;the&nbsp;dashboard,&nbsp;this&nbsp;maps&nbsp;to&nbsp;a&nbsp;particular&nbsp;"line"&nbsp;in&nbsp;the&nbsp;graph.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Mandatory.<br>
+&nbsp;&nbsp;values:&nbsp;A&nbsp;list&nbsp;of&nbsp;numeric&nbsp;measured&nbsp;values.&nbsp;An&nbsp;N-dimensional&nbsp;list&nbsp;will&nbsp;be<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;flattened&nbsp;and&nbsp;treated&nbsp;as&nbsp;a&nbsp;simple&nbsp;list.<br>
+&nbsp;&nbsp;units:&nbsp;A&nbsp;description&nbsp;of&nbsp;the&nbsp;units&nbsp;of&nbsp;measure,&nbsp;e.g.&nbsp;"bytes".<br>
+&nbsp;&nbsp;result_type:&nbsp;Accepts&nbsp;values&nbsp;of&nbsp;perf_result_data_type.ALL_TYPES.<br>
+&nbsp;&nbsp;print_to_stdout:&nbsp;If&nbsp;True,&nbsp;prints&nbsp;the&nbsp;output&nbsp;in&nbsp;stdout&nbsp;instead&nbsp;of&nbsp;returning<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;the&nbsp;output&nbsp;to&nbsp;caller.<br>
+&nbsp;<br>
+&nbsp;&nbsp;Returns:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;of&nbsp;the&nbsp;formated&nbsp;perf&nbsp;result.</tt></dd></dl>
+</td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>RESULT_TYPES</strong> = {'default': '*RESULT ', 'histogram': '*HISTOGRAM ', 'informational': '', 'unimportant': 'RESULT ', 'unimportant-histogram': 'HISTOGRAM '}</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.util.process_statistic_timeline_data.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.util.process_statistic_timeline_data.html
new file mode 100644
index 0000000..3695c0c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.util.process_statistic_timeline_data.html
@@ -0,0 +1,114 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.util.process_statistic_timeline_data</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.util.html"><font color="#ffffff">util</font></a>.process_statistic_timeline_data</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/util/process_statistic_timeline_data.py">telemetry/util/process_statistic_timeline_data.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.util.process_statistic_timeline_data.html#ProcessStatisticTimelineData">ProcessStatisticTimelineData</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.util.process_statistic_timeline_data.html#IdleWakeupTimelineData">IdleWakeupTimelineData</a>
+</font></dt></dl>
+</dd>
+</dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="IdleWakeupTimelineData">class <strong>IdleWakeupTimelineData</strong></a>(<a href="telemetry.util.process_statistic_timeline_data.html#ProcessStatisticTimelineData">ProcessStatisticTimelineData</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;<a href="#ProcessStatisticTimelineData">ProcessStatisticTimelineData</a>&nbsp;to&nbsp;hold&nbsp;idle&nbsp;wakeups.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.util.process_statistic_timeline_data.html#IdleWakeupTimelineData">IdleWakeupTimelineData</a></dd>
+<dd><a href="telemetry.util.process_statistic_timeline_data.html#ProcessStatisticTimelineData">ProcessStatisticTimelineData</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.util.process_statistic_timeline_data.html#ProcessStatisticTimelineData">ProcessStatisticTimelineData</a>:<br>
+<dl><dt><a name="IdleWakeupTimelineData-__add__"><strong>__add__</strong></a>(self, other)</dt><dd><tt>The&nbsp;result&nbsp;contains&nbsp;pids&nbsp;from&nbsp;both&nbsp;|self|&nbsp;and&nbsp;|other|,&nbsp;if&nbsp;duplicate<br>
+pids&nbsp;are&nbsp;found&nbsp;between&nbsp;objects,&nbsp;an&nbsp;error&nbsp;will&nbsp;occur.</tt></dd></dl>
+
+<dl><dt><a name="IdleWakeupTimelineData-__init__"><strong>__init__</strong></a>(self, pid, value)</dt></dl>
+
+<dl><dt><a name="IdleWakeupTimelineData-__sub__"><strong>__sub__</strong></a>(self, other)</dt><dd><tt>The&nbsp;results&nbsp;of&nbsp;subtraction&nbsp;is&nbsp;an&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;holding&nbsp;only&nbsp;the&nbsp;pids&nbsp;contained<br>
+in&nbsp;|self|.<br>
+&nbsp;<br>
+The&nbsp;motivation&nbsp;is&nbsp;that&nbsp;some&nbsp;processes&nbsp;may&nbsp;have&nbsp;died&nbsp;between&nbsp;two&nbsp;consecutive<br>
+measurements.&nbsp;The&nbsp;desired&nbsp;behavior&nbsp;is&nbsp;to&nbsp;only&nbsp;make&nbsp;calculations&nbsp;based&nbsp;on<br>
+the&nbsp;processes&nbsp;that&nbsp;are&nbsp;alive&nbsp;at&nbsp;the&nbsp;end&nbsp;of&nbsp;the&nbsp;second&nbsp;measurement.</tt></dd></dl>
+
+<dl><dt><a name="IdleWakeupTimelineData-total_sum"><strong>total_sum</strong></a>(self)</dt><dd><tt>Returns&nbsp;the&nbsp;sum&nbsp;of&nbsp;all&nbsp;values&nbsp;contained&nbsp;by&nbsp;this&nbsp;<a href="__builtin__.html#object">object</a>.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.util.process_statistic_timeline_data.html#ProcessStatisticTimelineData">ProcessStatisticTimelineData</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>value_by_pid</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ProcessStatisticTimelineData">class <strong>ProcessStatisticTimelineData</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Holds&nbsp;value&nbsp;of&nbsp;a&nbsp;stat&nbsp;for&nbsp;one&nbsp;or&nbsp;more&nbsp;processes.<br>
+&nbsp;<br>
+This&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;can&nbsp;hold&nbsp;a&nbsp;value&nbsp;for&nbsp;more&nbsp;than&nbsp;one&nbsp;pid&nbsp;by&nbsp;adding&nbsp;another<br>
+<a href="__builtin__.html#object">object</a>.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="ProcessStatisticTimelineData-__add__"><strong>__add__</strong></a>(self, other)</dt><dd><tt>The&nbsp;result&nbsp;contains&nbsp;pids&nbsp;from&nbsp;both&nbsp;|self|&nbsp;and&nbsp;|other|,&nbsp;if&nbsp;duplicate<br>
+pids&nbsp;are&nbsp;found&nbsp;between&nbsp;objects,&nbsp;an&nbsp;error&nbsp;will&nbsp;occur.</tt></dd></dl>
+
+<dl><dt><a name="ProcessStatisticTimelineData-__init__"><strong>__init__</strong></a>(self, pid, value)</dt></dl>
+
+<dl><dt><a name="ProcessStatisticTimelineData-__sub__"><strong>__sub__</strong></a>(self, other)</dt><dd><tt>The&nbsp;results&nbsp;of&nbsp;subtraction&nbsp;is&nbsp;an&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;holding&nbsp;only&nbsp;the&nbsp;pids&nbsp;contained<br>
+in&nbsp;|self|.<br>
+&nbsp;<br>
+The&nbsp;motivation&nbsp;is&nbsp;that&nbsp;some&nbsp;processes&nbsp;may&nbsp;have&nbsp;died&nbsp;between&nbsp;two&nbsp;consecutive<br>
+measurements.&nbsp;The&nbsp;desired&nbsp;behavior&nbsp;is&nbsp;to&nbsp;only&nbsp;make&nbsp;calculations&nbsp;based&nbsp;on<br>
+the&nbsp;processes&nbsp;that&nbsp;are&nbsp;alive&nbsp;at&nbsp;the&nbsp;end&nbsp;of&nbsp;the&nbsp;second&nbsp;measurement.</tt></dd></dl>
+
+<dl><dt><a name="ProcessStatisticTimelineData-total_sum"><strong>total_sum</strong></a>(self)</dt><dd><tt>Returns&nbsp;the&nbsp;sum&nbsp;of&nbsp;all&nbsp;values&nbsp;contained&nbsp;by&nbsp;this&nbsp;<a href="__builtin__.html#object">object</a>.</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>value_by_pid</strong></dt>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.util.rgba_color.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.util.rgba_color.html
new file mode 100644
index 0000000..737d51f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.util.rgba_color.html
@@ -0,0 +1,160 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.util.rgba_color</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.util.html"><font color="#ffffff">util</font></a>.rgba_color</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/util/rgba_color.py">telemetry/util/rgba_color.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="collections.html">collections</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial">RgbaColor(<a href="__builtin__.html#tuple">__builtin__.tuple</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.util.rgba_color.html#RgbaColor">RgbaColor</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="RgbaColor">class <strong>RgbaColor</strong></a>(RgbaColor)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Encapsulates&nbsp;an&nbsp;RGBA&nbsp;color&nbsp;retrieved&nbsp;from&nbsp;an&nbsp;image.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.util.rgba_color.html#RgbaColor">RgbaColor</a></dd>
+<dd>RgbaColor</dd>
+<dd><a href="__builtin__.html#tuple">__builtin__.tuple</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="RgbaColor-AssertIsRGB"><strong>AssertIsRGB</strong></a>(self, r, g, b, tolerance<font color="#909090">=0</font>)</dt></dl>
+
+<dl><dt><a name="RgbaColor-AssertIsRGBA"><strong>AssertIsRGBA</strong></a>(self, r, g, b, a, tolerance<font color="#909090">=0</font>)</dt></dl>
+
+<dl><dt><a name="RgbaColor-IsEqual"><strong>IsEqual</strong></a>(self, expected_color, tolerance<font color="#909090">=0</font>)</dt><dd><tt>Verifies&nbsp;that&nbsp;the&nbsp;color&nbsp;is&nbsp;within&nbsp;a&nbsp;given&nbsp;tolerance&nbsp;of<br>
+the&nbsp;expected&nbsp;color.</tt></dd></dl>
+
+<dl><dt><a name="RgbaColor-__int__"><strong>__int__</strong></a>(self)</dt></dl>
+
+<hr>
+Static methods defined here:<br>
+<dl><dt><a name="RgbaColor-__new__"><strong>__new__</strong></a>(cls, r, g, b, a<font color="#909090">=255</font>)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from RgbaColor:<br>
+<dl><dt><a name="RgbaColor-__getnewargs__"><strong>__getnewargs__</strong></a>(self)</dt><dd><tt>Return&nbsp;self&nbsp;as&nbsp;a&nbsp;plain&nbsp;tuple.&nbsp;&nbsp;Used&nbsp;by&nbsp;copy&nbsp;and&nbsp;pickle.</tt></dd></dl>
+
+<dl><dt><a name="RgbaColor-__getstate__"><strong>__getstate__</strong></a>(self)</dt><dd><tt>Exclude&nbsp;the&nbsp;OrderedDict&nbsp;from&nbsp;pickling</tt></dd></dl>
+
+<dl><dt><a name="RgbaColor-__repr__"><strong>__repr__</strong></a>(self)</dt><dd><tt>Return&nbsp;a&nbsp;nicely&nbsp;formatted&nbsp;representation&nbsp;string</tt></dd></dl>
+
+<dl><dt><a name="RgbaColor-_asdict"><strong>_asdict</strong></a>(self)</dt><dd><tt>Return&nbsp;a&nbsp;new&nbsp;OrderedDict&nbsp;which&nbsp;maps&nbsp;field&nbsp;names&nbsp;to&nbsp;their&nbsp;values</tt></dd></dl>
+
+<dl><dt><a name="RgbaColor-_replace"><strong>_replace</strong></a>(_self, **kwds)</dt><dd><tt>Return&nbsp;a&nbsp;new&nbsp;<a href="#RgbaColor">RgbaColor</a>&nbsp;object&nbsp;replacing&nbsp;specified&nbsp;fields&nbsp;with&nbsp;new&nbsp;values</tt></dd></dl>
+
+<hr>
+Class methods inherited from RgbaColor:<br>
+<dl><dt><a name="RgbaColor-_make"><strong>_make</strong></a>(cls, iterable, new<font color="#909090">=&lt;built-in method __new__ of type object&gt;</font>, len<font color="#909090">=&lt;built-in function len&gt;</font>)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Make&nbsp;a&nbsp;new&nbsp;<a href="#RgbaColor">RgbaColor</a>&nbsp;object&nbsp;from&nbsp;a&nbsp;sequence&nbsp;or&nbsp;iterable</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from RgbaColor:<br>
+<dl><dt><strong>a</strong></dt>
+<dd><tt>Alias&nbsp;for&nbsp;field&nbsp;number&nbsp;3</tt></dd>
+</dl>
+<dl><dt><strong>b</strong></dt>
+<dd><tt>Alias&nbsp;for&nbsp;field&nbsp;number&nbsp;2</tt></dd>
+</dl>
+<dl><dt><strong>g</strong></dt>
+<dd><tt>Alias&nbsp;for&nbsp;field&nbsp;number&nbsp;1</tt></dd>
+</dl>
+<dl><dt><strong>r</strong></dt>
+<dd><tt>Alias&nbsp;for&nbsp;field&nbsp;number&nbsp;0</tt></dd>
+</dl>
+<hr>
+Data and other attributes inherited from RgbaColor:<br>
+<dl><dt><strong>_fields</strong> = ('r', 'g', 'b', 'a')</dl>
+
+<hr>
+Methods inherited from <a href="__builtin__.html#tuple">__builtin__.tuple</a>:<br>
+<dl><dt><a name="RgbaColor-__add__"><strong>__add__</strong></a>(...)</dt><dd><tt>x.<a href="#RgbaColor-__add__">__add__</a>(y)&nbsp;&lt;==&gt;&nbsp;x+y</tt></dd></dl>
+
+<dl><dt><a name="RgbaColor-__contains__"><strong>__contains__</strong></a>(...)</dt><dd><tt>x.<a href="#RgbaColor-__contains__">__contains__</a>(y)&nbsp;&lt;==&gt;&nbsp;y&nbsp;in&nbsp;x</tt></dd></dl>
+
+<dl><dt><a name="RgbaColor-__eq__"><strong>__eq__</strong></a>(...)</dt><dd><tt>x.<a href="#RgbaColor-__eq__">__eq__</a>(y)&nbsp;&lt;==&gt;&nbsp;x==y</tt></dd></dl>
+
+<dl><dt><a name="RgbaColor-__ge__"><strong>__ge__</strong></a>(...)</dt><dd><tt>x.<a href="#RgbaColor-__ge__">__ge__</a>(y)&nbsp;&lt;==&gt;&nbsp;x&gt;=y</tt></dd></dl>
+
+<dl><dt><a name="RgbaColor-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#RgbaColor-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="RgbaColor-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#RgbaColor-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="RgbaColor-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#RgbaColor-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="RgbaColor-__gt__"><strong>__gt__</strong></a>(...)</dt><dd><tt>x.<a href="#RgbaColor-__gt__">__gt__</a>(y)&nbsp;&lt;==&gt;&nbsp;x&gt;y</tt></dd></dl>
+
+<dl><dt><a name="RgbaColor-__hash__"><strong>__hash__</strong></a>(...)</dt><dd><tt>x.<a href="#RgbaColor-__hash__">__hash__</a>()&nbsp;&lt;==&gt;&nbsp;hash(x)</tt></dd></dl>
+
+<dl><dt><a name="RgbaColor-__iter__"><strong>__iter__</strong></a>(...)</dt><dd><tt>x.<a href="#RgbaColor-__iter__">__iter__</a>()&nbsp;&lt;==&gt;&nbsp;iter(x)</tt></dd></dl>
+
+<dl><dt><a name="RgbaColor-__le__"><strong>__le__</strong></a>(...)</dt><dd><tt>x.<a href="#RgbaColor-__le__">__le__</a>(y)&nbsp;&lt;==&gt;&nbsp;x&lt;=y</tt></dd></dl>
+
+<dl><dt><a name="RgbaColor-__len__"><strong>__len__</strong></a>(...)</dt><dd><tt>x.<a href="#RgbaColor-__len__">__len__</a>()&nbsp;&lt;==&gt;&nbsp;len(x)</tt></dd></dl>
+
+<dl><dt><a name="RgbaColor-__lt__"><strong>__lt__</strong></a>(...)</dt><dd><tt>x.<a href="#RgbaColor-__lt__">__lt__</a>(y)&nbsp;&lt;==&gt;&nbsp;x&lt;y</tt></dd></dl>
+
+<dl><dt><a name="RgbaColor-__mul__"><strong>__mul__</strong></a>(...)</dt><dd><tt>x.<a href="#RgbaColor-__mul__">__mul__</a>(n)&nbsp;&lt;==&gt;&nbsp;x*n</tt></dd></dl>
+
+<dl><dt><a name="RgbaColor-__ne__"><strong>__ne__</strong></a>(...)</dt><dd><tt>x.<a href="#RgbaColor-__ne__">__ne__</a>(y)&nbsp;&lt;==&gt;&nbsp;x!=y</tt></dd></dl>
+
+<dl><dt><a name="RgbaColor-__rmul__"><strong>__rmul__</strong></a>(...)</dt><dd><tt>x.<a href="#RgbaColor-__rmul__">__rmul__</a>(n)&nbsp;&lt;==&gt;&nbsp;n*x</tt></dd></dl>
+
+<dl><dt><a name="RgbaColor-__sizeof__"><strong>__sizeof__</strong></a>(...)</dt><dd><tt>T.<a href="#RgbaColor-__sizeof__">__sizeof__</a>()&nbsp;--&nbsp;size&nbsp;of&nbsp;T&nbsp;in&nbsp;memory,&nbsp;in&nbsp;bytes</tt></dd></dl>
+
+<dl><dt><a name="RgbaColor-count"><strong>count</strong></a>(...)</dt><dd><tt>T.<a href="#RgbaColor-count">count</a>(value)&nbsp;-&gt;&nbsp;integer&nbsp;--&nbsp;return&nbsp;number&nbsp;of&nbsp;occurrences&nbsp;of&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="RgbaColor-index"><strong>index</strong></a>(...)</dt><dd><tt>T.<a href="#RgbaColor-index">index</a>(value,&nbsp;[start,&nbsp;[stop]])&nbsp;-&gt;&nbsp;integer&nbsp;--&nbsp;return&nbsp;first&nbsp;index&nbsp;of&nbsp;value.<br>
+Raises&nbsp;ValueError&nbsp;if&nbsp;the&nbsp;value&nbsp;is&nbsp;not&nbsp;present.</tt></dd></dl>
+
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>WEB_PAGE_TEST_ORANGE</strong> = RgbaColor(r=222, g=100, b=13, a=255)<br>
+<strong>WHITE</strong> = RgbaColor(r=255, g=255, b=255, a=255)</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.util.statistics.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.util.statistics.html
new file mode 100644
index 0000000..5f939a5
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.util.statistics.html
@@ -0,0 +1,137 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.util.statistics</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.util.html"><font color="#ffffff">util</font></a>.statistics</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/util/statistics.py">telemetry/util/statistics.py</a></font></td></tr></table>
+    <p><tt>A&nbsp;collection&nbsp;of&nbsp;statistical&nbsp;utility&nbsp;functions&nbsp;to&nbsp;be&nbsp;used&nbsp;by&nbsp;metrics.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="math.html">math</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-ArithmeticMean"><strong>ArithmeticMean</strong></a>(data)</dt><dd><tt>Calculates&nbsp;arithmetic&nbsp;mean.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;data:&nbsp;A&nbsp;list&nbsp;of&nbsp;samples.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;The&nbsp;arithmetic&nbsp;mean&nbsp;value,&nbsp;or&nbsp;0&nbsp;if&nbsp;the&nbsp;list&nbsp;is&nbsp;empty.</tt></dd></dl>
+ <dl><dt><a name="-Clamp"><strong>Clamp</strong></a>(value, low<font color="#909090">=0.0</font>, high<font color="#909090">=1.0</font>)</dt><dd><tt>Clamp&nbsp;a&nbsp;value&nbsp;between&nbsp;some&nbsp;low&nbsp;and&nbsp;high&nbsp;value.</tt></dd></dl>
+ <dl><dt><a name="-Discrepancy"><strong>Discrepancy</strong></a>(samples, location_count<font color="#909090">=None</font>)</dt><dd><tt>Computes&nbsp;the&nbsp;discrepancy&nbsp;of&nbsp;a&nbsp;set&nbsp;of&nbsp;1D&nbsp;samples&nbsp;from&nbsp;the&nbsp;interval&nbsp;[0,1].<br>
+&nbsp;<br>
+The&nbsp;samples&nbsp;must&nbsp;be&nbsp;sorted.&nbsp;We&nbsp;define&nbsp;the&nbsp;discrepancy&nbsp;of&nbsp;an&nbsp;empty&nbsp;set<br>
+of&nbsp;samples&nbsp;to&nbsp;be&nbsp;zero.<br>
+&nbsp;<br>
+<a href="http://en.wikipedia.org/wiki/Low-discrepancy_sequence">http://en.wikipedia.org/wiki/Low-discrepancy_sequence</a><br>
+<a href="http://mathworld.wolfram.com/Discrepancy.html">http://mathworld.wolfram.com/Discrepancy.html</a></tt></dd></dl>
+ <dl><dt><a name="-DivideIfPossibleOrZero"><strong>DivideIfPossibleOrZero</strong></a>(numerator, denominator)</dt><dd><tt>Returns&nbsp;the&nbsp;quotient,&nbsp;or&nbsp;zero&nbsp;if&nbsp;the&nbsp;denominator&nbsp;is&nbsp;zero.</tt></dd></dl>
+ <dl><dt><a name="-DurationsDiscrepancy"><strong>DurationsDiscrepancy</strong></a>(durations, absolute<font color="#909090">=True</font>, location_count<font color="#909090">=None</font>)</dt><dd><tt>A&nbsp;discrepancy&nbsp;based&nbsp;metric&nbsp;for&nbsp;measuring&nbsp;duration&nbsp;jank.<br>
+&nbsp;<br>
+DurationsDiscrepancy&nbsp;computes&nbsp;a&nbsp;jank&nbsp;metric&nbsp;which&nbsp;measures&nbsp;how&nbsp;irregular&nbsp;a<br>
+given&nbsp;sequence&nbsp;of&nbsp;intervals&nbsp;is.&nbsp;In&nbsp;order&nbsp;to&nbsp;minimize&nbsp;jank,&nbsp;each&nbsp;duration<br>
+should&nbsp;be&nbsp;equally&nbsp;long.&nbsp;This&nbsp;is&nbsp;similar&nbsp;to&nbsp;how&nbsp;timestamp&nbsp;jank&nbsp;works,<br>
+and&nbsp;we&nbsp;therefore&nbsp;reuse&nbsp;the&nbsp;timestamp&nbsp;discrepancy&nbsp;function&nbsp;above&nbsp;to&nbsp;compute&nbsp;a<br>
+similar&nbsp;duration&nbsp;discrepancy&nbsp;number.<br>
+&nbsp;<br>
+Because&nbsp;timestamp&nbsp;discrepancy&nbsp;is&nbsp;defined&nbsp;in&nbsp;terms&nbsp;of&nbsp;timestamps,&nbsp;we&nbsp;first<br>
+convert&nbsp;the&nbsp;list&nbsp;of&nbsp;durations&nbsp;to&nbsp;monotonically&nbsp;increasing&nbsp;timestamps.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;durations:&nbsp;List&nbsp;of&nbsp;interval&nbsp;lengths&nbsp;in&nbsp;milliseconds.<br>
+&nbsp;&nbsp;absolute:&nbsp;See&nbsp;TimestampsDiscrepancy.<br>
+&nbsp;&nbsp;interval_multiplier:&nbsp;See&nbsp;TimestampsDiscrepancy.</tt></dd></dl>
+ <dl><dt><a name="-GeneralizedMean"><strong>GeneralizedMean</strong></a>(values, exponent)</dt><dd><tt>See&nbsp;<a href="http://en.wikipedia.org/wiki/Generalized_mean">http://en.wikipedia.org/wiki/Generalized_mean</a></tt></dd></dl>
+ <dl><dt><a name="-GeometricMean"><strong>GeometricMean</strong></a>(values)</dt><dd><tt>Compute&nbsp;a&nbsp;rounded&nbsp;geometric&nbsp;mean&nbsp;from&nbsp;an&nbsp;array&nbsp;of&nbsp;values.</tt></dd></dl>
+ <dl><dt><a name="-Median"><strong>Median</strong></a>(values)</dt><dd><tt>Gets&nbsp;the&nbsp;median&nbsp;of&nbsp;a&nbsp;list&nbsp;of&nbsp;values.</tt></dd></dl>
+ <dl><dt><a name="-NormalizeSamples"><strong>NormalizeSamples</strong></a>(samples)</dt><dd><tt>Sorts&nbsp;the&nbsp;samples,&nbsp;and&nbsp;map&nbsp;them&nbsp;linearly&nbsp;to&nbsp;the&nbsp;range&nbsp;[0,1].<br>
+&nbsp;<br>
+They're&nbsp;mapped&nbsp;such&nbsp;that&nbsp;for&nbsp;the&nbsp;N&nbsp;samples,&nbsp;the&nbsp;first&nbsp;sample&nbsp;is&nbsp;0.5/N&nbsp;and&nbsp;the<br>
+last&nbsp;sample&nbsp;is&nbsp;(N-0.5)/N.<br>
+&nbsp;<br>
+Background:&nbsp;The&nbsp;discrepancy&nbsp;of&nbsp;the&nbsp;sample&nbsp;set&nbsp;i/(N-1);&nbsp;i=0,&nbsp;...,&nbsp;N-1&nbsp;is&nbsp;2/N,<br>
+twice&nbsp;the&nbsp;discrepancy&nbsp;of&nbsp;the&nbsp;sample&nbsp;set&nbsp;(i+1/2)/N;&nbsp;i=0,&nbsp;...,&nbsp;N-1.&nbsp;In&nbsp;our&nbsp;case<br>
+we&nbsp;don't&nbsp;want&nbsp;to&nbsp;distinguish&nbsp;between&nbsp;these&nbsp;two&nbsp;cases,&nbsp;as&nbsp;our&nbsp;original&nbsp;domain<br>
+is&nbsp;not&nbsp;bounded&nbsp;(it&nbsp;is&nbsp;for&nbsp;Monte&nbsp;Carlo&nbsp;integration,&nbsp;where&nbsp;discrepancy&nbsp;was<br>
+first&nbsp;used).</tt></dd></dl>
+ <dl><dt><a name="-Percentile"><strong>Percentile</strong></a>(values, percentile)</dt><dd><tt>Calculates&nbsp;the&nbsp;value&nbsp;below&nbsp;which&nbsp;a&nbsp;given&nbsp;percentage&nbsp;of&nbsp;values&nbsp;fall.<br>
+&nbsp;<br>
+For&nbsp;example,&nbsp;if&nbsp;17%&nbsp;of&nbsp;the&nbsp;values&nbsp;are&nbsp;less&nbsp;than&nbsp;5.0,&nbsp;then&nbsp;5.0&nbsp;is&nbsp;the&nbsp;17th<br>
+percentile&nbsp;for&nbsp;this&nbsp;set&nbsp;of&nbsp;values.&nbsp;When&nbsp;the&nbsp;percentage&nbsp;doesn't&nbsp;exactly<br>
+match&nbsp;a&nbsp;rank&nbsp;in&nbsp;the&nbsp;list&nbsp;of&nbsp;values,&nbsp;the&nbsp;percentile&nbsp;is&nbsp;computed&nbsp;using&nbsp;linear<br>
+interpolation&nbsp;between&nbsp;closest&nbsp;ranks.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;values:&nbsp;A&nbsp;list&nbsp;of&nbsp;numerical&nbsp;values.<br>
+&nbsp;&nbsp;percentile:&nbsp;A&nbsp;number&nbsp;between&nbsp;0&nbsp;and&nbsp;100.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;The&nbsp;Nth&nbsp;percentile&nbsp;for&nbsp;the&nbsp;list&nbsp;of&nbsp;values,&nbsp;where&nbsp;N&nbsp;is&nbsp;the&nbsp;given&nbsp;percentage.</tt></dd></dl>
+ <dl><dt><a name="-StandardDeviation"><strong>StandardDeviation</strong></a>(data)</dt><dd><tt>Calculates&nbsp;the&nbsp;standard&nbsp;deviation.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;data:&nbsp;A&nbsp;list&nbsp;of&nbsp;samples.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;The&nbsp;standard&nbsp;deviation&nbsp;of&nbsp;the&nbsp;samples&nbsp;provided.</tt></dd></dl>
+ <dl><dt><a name="-TimestampsDiscrepancy"><strong>TimestampsDiscrepancy</strong></a>(timestamps, absolute<font color="#909090">=True</font>, location_count<font color="#909090">=None</font>)</dt><dd><tt>A&nbsp;discrepancy&nbsp;based&nbsp;metric&nbsp;for&nbsp;measuring&nbsp;timestamp&nbsp;jank.<br>
+&nbsp;<br>
+TimestampsDiscrepancy&nbsp;quantifies&nbsp;the&nbsp;largest&nbsp;area&nbsp;of&nbsp;jank&nbsp;observed&nbsp;in&nbsp;a&nbsp;series<br>
+of&nbsp;timestamps.&nbsp;&nbsp;Note&nbsp;that&nbsp;this&nbsp;is&nbsp;different&nbsp;from&nbsp;metrics&nbsp;based&nbsp;on&nbsp;the<br>
+max_time_interval.&nbsp;For&nbsp;example,&nbsp;the&nbsp;time&nbsp;stamp&nbsp;series&nbsp;A&nbsp;=&nbsp;[0,1,2,3,5,6]&nbsp;and<br>
+B&nbsp;=&nbsp;[0,1,2,3,5,7]&nbsp;have&nbsp;the&nbsp;same&nbsp;max_time_interval&nbsp;=&nbsp;2,&nbsp;but<br>
+<a href="#-Discrepancy">Discrepancy</a>(B)&nbsp;&gt;&nbsp;<a href="#-Discrepancy">Discrepancy</a>(A).<br>
+&nbsp;<br>
+Two&nbsp;variants&nbsp;of&nbsp;discrepancy&nbsp;can&nbsp;be&nbsp;computed:<br>
+&nbsp;<br>
+Relative&nbsp;discrepancy&nbsp;is&nbsp;following&nbsp;the&nbsp;original&nbsp;definition&nbsp;of<br>
+discrepancy.&nbsp;It&nbsp;characterized&nbsp;the&nbsp;largest&nbsp;area&nbsp;of&nbsp;jank,&nbsp;relative&nbsp;to&nbsp;the<br>
+duration&nbsp;of&nbsp;the&nbsp;entire&nbsp;time&nbsp;stamp&nbsp;series.&nbsp;&nbsp;We&nbsp;normalize&nbsp;the&nbsp;raw&nbsp;results,<br>
+because&nbsp;the&nbsp;best&nbsp;case&nbsp;discrepancy&nbsp;for&nbsp;a&nbsp;set&nbsp;of&nbsp;N&nbsp;samples&nbsp;is&nbsp;1/N&nbsp;(for<br>
+equally&nbsp;spaced&nbsp;samples),&nbsp;and&nbsp;we&nbsp;want&nbsp;our&nbsp;metric&nbsp;to&nbsp;report&nbsp;0.0&nbsp;in&nbsp;that<br>
+case.<br>
+&nbsp;<br>
+Absolute&nbsp;discrepancy&nbsp;also&nbsp;characterizes&nbsp;the&nbsp;largest&nbsp;area&nbsp;of&nbsp;jank,&nbsp;but&nbsp;its<br>
+value&nbsp;wouldn't&nbsp;change&nbsp;(except&nbsp;for&nbsp;imprecisions&nbsp;due&nbsp;to&nbsp;a&nbsp;low<br>
+|interval_multiplier|)&nbsp;if&nbsp;additional&nbsp;'good'&nbsp;intervals&nbsp;were&nbsp;added&nbsp;to&nbsp;an<br>
+exisiting&nbsp;list&nbsp;of&nbsp;time&nbsp;stamps.&nbsp;&nbsp;Its&nbsp;range&nbsp;is&nbsp;[0,inf]&nbsp;and&nbsp;the&nbsp;unit&nbsp;is<br>
+milliseconds.<br>
+&nbsp;<br>
+The&nbsp;time&nbsp;stamp&nbsp;series&nbsp;C&nbsp;=&nbsp;[0,2,3,4]&nbsp;and&nbsp;D&nbsp;=&nbsp;[0,2,3,4,5]&nbsp;have&nbsp;the&nbsp;same<br>
+absolute&nbsp;discrepancy,&nbsp;but&nbsp;D&nbsp;has&nbsp;lower&nbsp;relative&nbsp;discrepancy&nbsp;than&nbsp;C.<br>
+&nbsp;<br>
+|timestamps|&nbsp;may&nbsp;be&nbsp;a&nbsp;list&nbsp;of&nbsp;lists&nbsp;S&nbsp;=&nbsp;[S_1,&nbsp;S_2,&nbsp;...,&nbsp;S_N],&nbsp;where&nbsp;each<br>
+S_i&nbsp;is&nbsp;a&nbsp;time&nbsp;stamp&nbsp;series.&nbsp;In&nbsp;that&nbsp;case,&nbsp;the&nbsp;discrepancy&nbsp;D(S)&nbsp;is:<br>
+D(S)&nbsp;=&nbsp;max(D(S_1),&nbsp;D(S_2),&nbsp;...,&nbsp;D(S_N))</tt></dd></dl>
+ <dl><dt><a name="-Total"><strong>Total</strong></a>(data)</dt><dd><tt>Returns&nbsp;the&nbsp;float&nbsp;value&nbsp;of&nbsp;a&nbsp;number&nbsp;or&nbsp;the&nbsp;sum&nbsp;of&nbsp;a&nbsp;list.</tt></dd></dl>
+ <dl><dt><a name="-TrapezoidalRule"><strong>TrapezoidalRule</strong></a>(data, dx)</dt><dd><tt>Calculate&nbsp;the&nbsp;integral&nbsp;according&nbsp;to&nbsp;the&nbsp;trapezoidal&nbsp;rule<br>
+&nbsp;<br>
+TrapezoidalRule&nbsp;approximates&nbsp;the&nbsp;definite&nbsp;integral&nbsp;of&nbsp;f&nbsp;from&nbsp;a&nbsp;to&nbsp;b&nbsp;by<br>
+the&nbsp;composite&nbsp;trapezoidal&nbsp;rule,&nbsp;using&nbsp;n&nbsp;subintervals.<br>
+<a href="http://en.wikipedia.org/wiki/Trapezoidal_rule#Uniform_grid">http://en.wikipedia.org/wiki/Trapezoidal_rule#Uniform_grid</a><br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;data:&nbsp;A&nbsp;list&nbsp;of&nbsp;samples<br>
+&nbsp;&nbsp;dx:&nbsp;The&nbsp;uniform&nbsp;distance&nbsp;along&nbsp;the&nbsp;x&nbsp;axis&nbsp;between&nbsp;any&nbsp;two&nbsp;samples<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;The&nbsp;area&nbsp;under&nbsp;the&nbsp;curve&nbsp;defined&nbsp;by&nbsp;the&nbsp;samples&nbsp;and&nbsp;the&nbsp;uniform&nbsp;distance<br>
+&nbsp;&nbsp;according&nbsp;to&nbsp;the&nbsp;trapezoidal&nbsp;rule.</tt></dd></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.util.wpr_modes.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.util.wpr_modes.html
new file mode 100644
index 0000000..dcbd842
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.util.wpr_modes.html
@@ -0,0 +1,27 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.util.wpr_modes</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.util.html"><font color="#ffffff">util</font></a>.wpr_modes</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/util/wpr_modes.py">telemetry/util/wpr_modes.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2012&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>WPR_APPEND</strong> = 'wpr-append'<br>
+<strong>WPR_OFF</strong> = 'wpr-off'<br>
+<strong>WPR_RECORD</strong> = 'wpr-record'<br>
+<strong>WPR_REPLAY</strong> = 'wpr-replay'</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.value.failure.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.value.failure.html
new file mode 100644
index 0000000..68868c8
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.value.failure.html
@@ -0,0 +1,151 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.value.failure</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.value.html"><font color="#ffffff">value</font></a>.failure</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/value/failure.py">telemetry/value/failure.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="sys.html">sys</a><br>
+</td><td width="25%" valign=top><a href="traceback.html">traceback</a><br>
+</td><td width="25%" valign=top><a href="telemetry.value.html">telemetry.value</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.value.html#Value">telemetry.value.Value</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.value.failure.html#FailureValue">FailureValue</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="FailureValue">class <strong>FailureValue</strong></a>(<a href="telemetry.value.html#Value">telemetry.value.Value</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.value.failure.html#FailureValue">FailureValue</a></dd>
+<dd><a href="telemetry.value.html#Value">telemetry.value.Value</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="FailureValue-AsDict"><strong>AsDict</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="FailureValue-GetBuildbotDataType"><strong>GetBuildbotDataType</strong></a>(self, output_context)</dt></dl>
+
+<dl><dt><a name="FailureValue-GetBuildbotValue"><strong>GetBuildbotValue</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="FailureValue-GetChartAndTraceNameForPerPageResult"><strong>GetChartAndTraceNameForPerPageResult</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="FailureValue-GetRepresentativeNumber"><strong>GetRepresentativeNumber</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="FailureValue-GetRepresentativeString"><strong>GetRepresentativeString</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="FailureValue-__init__"><strong>__init__</strong></a>(self, page, exc_info, description<font color="#909090">=None</font>, tir_label<font color="#909090">=None</font>)</dt><dd><tt>A&nbsp;value&nbsp;representing&nbsp;a&nbsp;failure&nbsp;when&nbsp;running&nbsp;the&nbsp;page.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;page:&nbsp;The&nbsp;page&nbsp;where&nbsp;this&nbsp;failure&nbsp;occurs.<br>
+&nbsp;&nbsp;exc_info:&nbsp;The&nbsp;exception&nbsp;info&nbsp;(sys.<a href="#FailureValue-exc_info">exc_info</a>())&nbsp;corresponding&nbsp;to<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this&nbsp;failure.</tt></dd></dl>
+
+<dl><dt><a name="FailureValue-__repr__"><strong>__repr__</strong></a>(self)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="FailureValue-FromMessage"><strong>FromMessage</strong></a>(cls, page, message)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Creates&nbsp;a&nbsp;failure&nbsp;value&nbsp;for&nbsp;a&nbsp;given&nbsp;string&nbsp;message.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;page:&nbsp;The&nbsp;page&nbsp;where&nbsp;this&nbsp;failure&nbsp;occurs.<br>
+&nbsp;&nbsp;message:&nbsp;A&nbsp;string&nbsp;message&nbsp;describing&nbsp;the&nbsp;failure.</tt></dd></dl>
+
+<dl><dt><a name="FailureValue-MergeLikeValuesFromDifferentPages"><strong>MergeLikeValuesFromDifferentPages</strong></a>(cls, values)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="FailureValue-MergeLikeValuesFromSamePage"><strong>MergeLikeValuesFromSamePage</strong></a>(cls, values)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Static methods defined here:<br>
+<dl><dt><a name="FailureValue-FromDict"><strong>FromDict</strong></a>(value_dict, page_dict)</dt></dl>
+
+<dl><dt><a name="FailureValue-GetJSONTypeName"><strong>GetJSONTypeName</strong></a>()</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>exc_info</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.value.html#Value">telemetry.value.Value</a>:<br>
+<dl><dt><a name="FailureValue-AsDictWithoutBaseClassEntries"><strong>AsDictWithoutBaseClassEntries</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="FailureValue-GetChartAndTraceNameForComputedSummaryResult"><strong>GetChartAndTraceNameForComputedSummaryResult</strong></a>(self, trace_tag)</dt></dl>
+
+<dl><dt><a name="FailureValue-IsMergableWith"><strong>IsMergableWith</strong></a>(self, that)</dt></dl>
+
+<dl><dt><a name="FailureValue-__eq__"><strong>__eq__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="FailureValue-__hash__"><strong>__hash__</strong></a>(self)</dt></dl>
+
+<hr>
+Static methods inherited from <a href="telemetry.value.html#Value">telemetry.value.Value</a>:<br>
+<dl><dt><a name="FailureValue-GetConstructorKwArgs"><strong>GetConstructorKwArgs</strong></a>(value_dict, page_dict)</dt><dd><tt>Produces&nbsp;constructor&nbsp;arguments&nbsp;from&nbsp;a&nbsp;value&nbsp;dict&nbsp;and&nbsp;a&nbsp;page&nbsp;dict.<br>
+&nbsp;<br>
+Takes&nbsp;a&nbsp;dict&nbsp;parsed&nbsp;from&nbsp;JSON&nbsp;and&nbsp;an&nbsp;index&nbsp;of&nbsp;pages&nbsp;and&nbsp;recovers&nbsp;the<br>
+keyword&nbsp;arguments&nbsp;to&nbsp;be&nbsp;passed&nbsp;to&nbsp;the&nbsp;constructor&nbsp;for&nbsp;deserializing&nbsp;the<br>
+dict.<br>
+&nbsp;<br>
+value_dict:&nbsp;a&nbsp;dictionary&nbsp;produced&nbsp;by&nbsp;<a href="#FailureValue-AsDict">AsDict</a>()&nbsp;on&nbsp;a&nbsp;value&nbsp;subclass.<br>
+page_dict:&nbsp;a&nbsp;dictionary&nbsp;mapping&nbsp;IDs&nbsp;to&nbsp;page&nbsp;objects.</tt></dd></dl>
+
+<dl><dt><a name="FailureValue-ListOfValuesFromListOfDicts"><strong>ListOfValuesFromListOfDicts</strong></a>(value_dicts, page_dict)</dt><dd><tt>Takes&nbsp;a&nbsp;list&nbsp;of&nbsp;value&nbsp;dicts&nbsp;to&nbsp;values.<br>
+&nbsp;<br>
+Given&nbsp;a&nbsp;list&nbsp;of&nbsp;value&nbsp;dicts&nbsp;produced&nbsp;by&nbsp;AsDict,&nbsp;this&nbsp;method<br>
+deserializes&nbsp;the&nbsp;dicts&nbsp;given&nbsp;a&nbsp;dict&nbsp;mapping&nbsp;page&nbsp;IDs&nbsp;to&nbsp;pages.<br>
+This&nbsp;method&nbsp;performs&nbsp;memoization&nbsp;for&nbsp;deserializing&nbsp;a&nbsp;list&nbsp;of&nbsp;values<br>
+efficiently,&nbsp;where&nbsp;FromDict&nbsp;is&nbsp;meant&nbsp;to&nbsp;handle&nbsp;one-offs.<br>
+&nbsp;<br>
+values:&nbsp;a&nbsp;list&nbsp;of&nbsp;value&nbsp;dicts&nbsp;produced&nbsp;by&nbsp;<a href="#FailureValue-AsDict">AsDict</a>()&nbsp;on&nbsp;a&nbsp;value&nbsp;subclass.<br>
+page_dict:&nbsp;a&nbsp;dictionary&nbsp;mapping&nbsp;IDs&nbsp;to&nbsp;page&nbsp;objects.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.value.html#Value">telemetry.value.Value</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>name_suffix</strong></dt>
+<dd><tt>Returns&nbsp;the&nbsp;string&nbsp;after&nbsp;a&nbsp;.&nbsp;in&nbsp;the&nbsp;name,&nbsp;or&nbsp;the&nbsp;full&nbsp;name&nbsp;otherwise.</tt></dd>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-GetStringFromExcInfo"><strong>GetStringFromExcInfo</strong></a>(exc_info)</dt></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.value.histogram.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.value.histogram.html
new file mode 100644
index 0000000..9b6c3c1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.value.histogram.html
@@ -0,0 +1,167 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.value.histogram</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.value.html"><font color="#ffffff">value</font></a>.histogram</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/value/histogram.py">telemetry/value/histogram.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.value.histogram_util.html">telemetry.value.histogram_util</a><br>
+<a href="json.html">json</a><br>
+</td><td width="25%" valign=top><a href="telemetry.util.perf_tests_helper.html">telemetry.util.perf_tests_helper</a><br>
+<a href="telemetry.value.summarizable.html">telemetry.value.summarizable</a><br>
+</td><td width="25%" valign=top><a href="telemetry.value.html">telemetry.value</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.value.histogram.html#HistogramValueBucket">HistogramValueBucket</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="telemetry.value.summarizable.html#SummarizableValue">telemetry.value.summarizable.SummarizableValue</a>(<a href="telemetry.value.html#Value">telemetry.value.Value</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.value.histogram.html#HistogramValue">HistogramValue</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="HistogramValue">class <strong>HistogramValue</strong></a>(<a href="telemetry.value.summarizable.html#SummarizableValue">telemetry.value.summarizable.SummarizableValue</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.value.histogram.html#HistogramValue">HistogramValue</a></dd>
+<dd><a href="telemetry.value.summarizable.html#SummarizableValue">telemetry.value.summarizable.SummarizableValue</a></dd>
+<dd><a href="telemetry.value.html#Value">telemetry.value.Value</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="HistogramValue-AsDict"><strong>AsDict</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="HistogramValue-GetBuildbotDataType"><strong>GetBuildbotDataType</strong></a>(self, output_context)</dt></dl>
+
+<dl><dt><a name="HistogramValue-GetBuildbotValue"><strong>GetBuildbotValue</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="HistogramValue-GetRepresentativeNumber"><strong>GetRepresentativeNumber</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="HistogramValue-GetRepresentativeString"><strong>GetRepresentativeString</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="HistogramValue-ToJSONString"><strong>ToJSONString</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="HistogramValue-__init__"><strong>__init__</strong></a>(self, page, name, units, raw_value<font color="#909090">=None</font>, raw_value_json<font color="#909090">=None</font>, important<font color="#909090">=True</font>, description<font color="#909090">=None</font>, tir_label<font color="#909090">=None</font>, improvement_direction<font color="#909090">=None</font>)</dt></dl>
+
+<dl><dt><a name="HistogramValue-__repr__"><strong>__repr__</strong></a>(self)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="HistogramValue-MergeLikeValuesFromDifferentPages"><strong>MergeLikeValuesFromDifferentPages</strong></a>(cls, values)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="HistogramValue-MergeLikeValuesFromSamePage"><strong>MergeLikeValuesFromSamePage</strong></a>(cls, values)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Static methods defined here:<br>
+<dl><dt><a name="HistogramValue-FromDict"><strong>FromDict</strong></a>(value_dict, page_dict)</dt></dl>
+
+<dl><dt><a name="HistogramValue-GetJSONTypeName"><strong>GetJSONTypeName</strong></a>()</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.value.summarizable.html#SummarizableValue">telemetry.value.summarizable.SummarizableValue</a>:<br>
+<dl><dt><a name="HistogramValue-AsDictWithoutBaseClassEntries"><strong>AsDictWithoutBaseClassEntries</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.value.summarizable.html#SummarizableValue">telemetry.value.summarizable.SummarizableValue</a>:<br>
+<dl><dt><strong>improvement_direction</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.value.html#Value">telemetry.value.Value</a>:<br>
+<dl><dt><a name="HistogramValue-GetChartAndTraceNameForComputedSummaryResult"><strong>GetChartAndTraceNameForComputedSummaryResult</strong></a>(self, trace_tag)</dt></dl>
+
+<dl><dt><a name="HistogramValue-GetChartAndTraceNameForPerPageResult"><strong>GetChartAndTraceNameForPerPageResult</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="HistogramValue-IsMergableWith"><strong>IsMergableWith</strong></a>(self, that)</dt></dl>
+
+<dl><dt><a name="HistogramValue-__eq__"><strong>__eq__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="HistogramValue-__hash__"><strong>__hash__</strong></a>(self)</dt></dl>
+
+<hr>
+Static methods inherited from <a href="telemetry.value.html#Value">telemetry.value.Value</a>:<br>
+<dl><dt><a name="HistogramValue-GetConstructorKwArgs"><strong>GetConstructorKwArgs</strong></a>(value_dict, page_dict)</dt><dd><tt>Produces&nbsp;constructor&nbsp;arguments&nbsp;from&nbsp;a&nbsp;value&nbsp;dict&nbsp;and&nbsp;a&nbsp;page&nbsp;dict.<br>
+&nbsp;<br>
+Takes&nbsp;a&nbsp;dict&nbsp;parsed&nbsp;from&nbsp;JSON&nbsp;and&nbsp;an&nbsp;index&nbsp;of&nbsp;pages&nbsp;and&nbsp;recovers&nbsp;the<br>
+keyword&nbsp;arguments&nbsp;to&nbsp;be&nbsp;passed&nbsp;to&nbsp;the&nbsp;constructor&nbsp;for&nbsp;deserializing&nbsp;the<br>
+dict.<br>
+&nbsp;<br>
+value_dict:&nbsp;a&nbsp;dictionary&nbsp;produced&nbsp;by&nbsp;<a href="#HistogramValue-AsDict">AsDict</a>()&nbsp;on&nbsp;a&nbsp;value&nbsp;subclass.<br>
+page_dict:&nbsp;a&nbsp;dictionary&nbsp;mapping&nbsp;IDs&nbsp;to&nbsp;page&nbsp;objects.</tt></dd></dl>
+
+<dl><dt><a name="HistogramValue-ListOfValuesFromListOfDicts"><strong>ListOfValuesFromListOfDicts</strong></a>(value_dicts, page_dict)</dt><dd><tt>Takes&nbsp;a&nbsp;list&nbsp;of&nbsp;value&nbsp;dicts&nbsp;to&nbsp;values.<br>
+&nbsp;<br>
+Given&nbsp;a&nbsp;list&nbsp;of&nbsp;value&nbsp;dicts&nbsp;produced&nbsp;by&nbsp;AsDict,&nbsp;this&nbsp;method<br>
+deserializes&nbsp;the&nbsp;dicts&nbsp;given&nbsp;a&nbsp;dict&nbsp;mapping&nbsp;page&nbsp;IDs&nbsp;to&nbsp;pages.<br>
+This&nbsp;method&nbsp;performs&nbsp;memoization&nbsp;for&nbsp;deserializing&nbsp;a&nbsp;list&nbsp;of&nbsp;values<br>
+efficiently,&nbsp;where&nbsp;FromDict&nbsp;is&nbsp;meant&nbsp;to&nbsp;handle&nbsp;one-offs.<br>
+&nbsp;<br>
+values:&nbsp;a&nbsp;list&nbsp;of&nbsp;value&nbsp;dicts&nbsp;produced&nbsp;by&nbsp;<a href="#HistogramValue-AsDict">AsDict</a>()&nbsp;on&nbsp;a&nbsp;value&nbsp;subclass.<br>
+page_dict:&nbsp;a&nbsp;dictionary&nbsp;mapping&nbsp;IDs&nbsp;to&nbsp;page&nbsp;objects.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.value.html#Value">telemetry.value.Value</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>name_suffix</strong></dt>
+<dd><tt>Returns&nbsp;the&nbsp;string&nbsp;after&nbsp;a&nbsp;.&nbsp;in&nbsp;the&nbsp;name,&nbsp;or&nbsp;the&nbsp;full&nbsp;name&nbsp;otherwise.</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="HistogramValueBucket">class <strong>HistogramValueBucket</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="HistogramValueBucket-AsDict"><strong>AsDict</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="HistogramValueBucket-ToJSONString"><strong>ToJSONString</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="HistogramValueBucket-__init__"><strong>__init__</strong></a>(self, low, high, count<font color="#909090">=0</font>)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.value.histogram_util.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.value.histogram_util.html
new file mode 100644
index 0000000..745510d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.value.histogram_util.html
@@ -0,0 +1,60 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.value.histogram_util</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.value.html"><font color="#ffffff">value</font></a>.histogram_util</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/value/histogram_util.py">telemetry/value/histogram_util.py</a></font></td></tr></table>
+    <p><tt>This&nbsp;is&nbsp;a&nbsp;helper&nbsp;module&nbsp;to&nbsp;get&nbsp;and&nbsp;manipulate&nbsp;histogram&nbsp;data.<br>
+&nbsp;<br>
+The&nbsp;histogram&nbsp;data&nbsp;is&nbsp;the&nbsp;same&nbsp;data&nbsp;as&nbsp;is&nbsp;visible&nbsp;from&nbsp;"chrome://histograms".<br>
+More&nbsp;information&nbsp;can&nbsp;be&nbsp;found&nbsp;at:&nbsp;chromium/src/base/metrics/histogram.h</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="collections.html">collections</a><br>
+</td><td width="25%" valign=top><a href="telemetry.core.exceptions.html">telemetry.core.exceptions</a><br>
+</td><td width="25%" valign=top><a href="json.html">json</a><br>
+</td><td width="25%" valign=top><a href="logging.html">logging</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-AddHistograms"><strong>AddHistograms</strong></a>(histogram_jsons)</dt><dd><tt>Adds&nbsp;histograms&nbsp;together.&nbsp;Used&nbsp;for&nbsp;aggregating&nbsp;data.<br>
+&nbsp;<br>
+The&nbsp;parameter&nbsp;is&nbsp;a&nbsp;list&nbsp;of&nbsp;json&nbsp;serializations&nbsp;and&nbsp;the&nbsp;returned&nbsp;result&nbsp;is&nbsp;a<br>
+json&nbsp;serialization&nbsp;too.<br>
+&nbsp;<br>
+Note&nbsp;that&nbsp;the&nbsp;histograms&nbsp;to&nbsp;be&nbsp;added&nbsp;together&nbsp;are&nbsp;typically&nbsp;from&nbsp;different<br>
+processes.</tt></dd></dl>
+ <dl><dt><a name="-CustomizeBrowserOptions"><strong>CustomizeBrowserOptions</strong></a>(options)</dt><dd><tt>Allows&nbsp;histogram&nbsp;collection.</tt></dd></dl>
+ <dl><dt><a name="-GetHistogram"><strong>GetHistogram</strong></a>(histogram_type, histogram_name, tab)</dt><dd><tt>Get&nbsp;a&nbsp;json&nbsp;serialization&nbsp;of&nbsp;a&nbsp;histogram.</tt></dd></dl>
+ <dl><dt><a name="-GetHistogramBucketsFromJson"><strong>GetHistogramBucketsFromJson</strong></a>(histogram_json)</dt></dl>
+ <dl><dt><a name="-GetHistogramBucketsFromRawValue"><strong>GetHistogramBucketsFromRawValue</strong></a>(raw_value)</dt></dl>
+ <dl><dt><a name="-GetHistogramCount"><strong>GetHistogramCount</strong></a>(histogram_type, histogram_name, tab)</dt><dd><tt>Get&nbsp;the&nbsp;count&nbsp;of&nbsp;events&nbsp;for&nbsp;the&nbsp;given&nbsp;histograms.</tt></dd></dl>
+ <dl><dt><a name="-GetHistogramSum"><strong>GetHistogramSum</strong></a>(histogram_type, histogram_name, tab)</dt><dd><tt>Get&nbsp;the&nbsp;sum&nbsp;of&nbsp;events&nbsp;for&nbsp;the&nbsp;given&nbsp;histograms.</tt></dd></dl>
+ <dl><dt><a name="-SubtractHistogram"><strong>SubtractHistogram</strong></a>(histogram_json, start_histogram_json)</dt><dd><tt>Subtracts&nbsp;a&nbsp;previous&nbsp;histogram&nbsp;from&nbsp;a&nbsp;histogram.<br>
+&nbsp;<br>
+Both&nbsp;parameters&nbsp;and&nbsp;the&nbsp;returned&nbsp;result&nbsp;are&nbsp;json&nbsp;serializations.</tt></dd></dl>
+</td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>BROWSER_HISTOGRAM</strong> = 'browser_histogram'<br>
+<strong>RENDERER_HISTOGRAM</strong> = 'renderer_histogram'</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.value.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.value.html
new file mode 100644
index 0000000..8d397fd
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.value.html
@@ -0,0 +1,235 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: package telemetry.value</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.value</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/value/__init__.py">telemetry/value/__init__.py</a></font></td></tr></table>
+    <p><tt>The&nbsp;<a href="#Value">Value</a>&nbsp;hierarchy&nbsp;provides&nbsp;a&nbsp;way&nbsp;of&nbsp;representing&nbsp;the&nbsp;values&nbsp;measurements<br>
+produce&nbsp;such&nbsp;that&nbsp;they&nbsp;can&nbsp;be&nbsp;merged&nbsp;across&nbsp;runs,&nbsp;grouped&nbsp;by&nbsp;page,&nbsp;and&nbsp;output<br>
+to&nbsp;different&nbsp;targets.<br>
+&nbsp;<br>
+The&nbsp;core&nbsp;<a href="#Value">Value</a>&nbsp;concept&nbsp;provides&nbsp;the&nbsp;basic&nbsp;functionality:<br>
+-&nbsp;association&nbsp;with&nbsp;a&nbsp;page,&nbsp;may&nbsp;be&nbsp;none<br>
+-&nbsp;naming&nbsp;and&nbsp;units<br>
+-&nbsp;importance&nbsp;tracking&nbsp;[whether&nbsp;a&nbsp;value&nbsp;will&nbsp;show&nbsp;up&nbsp;on&nbsp;a&nbsp;waterfall&nbsp;or&nbsp;output<br>
+&nbsp;&nbsp;file&nbsp;by&nbsp;default]<br>
+-&nbsp;other&nbsp;metadata,&nbsp;such&nbsp;as&nbsp;a&nbsp;description&nbsp;of&nbsp;what&nbsp;was&nbsp;measured<br>
+-&nbsp;default&nbsp;conversion&nbsp;to&nbsp;scalar&nbsp;and&nbsp;string<br>
+-&nbsp;merging&nbsp;properties<br>
+&nbsp;<br>
+A&nbsp;page&nbsp;may&nbsp;actually&nbsp;run&nbsp;a&nbsp;few&nbsp;times&nbsp;during&nbsp;a&nbsp;single&nbsp;telemetry&nbsp;session.<br>
+Downstream&nbsp;consumers&nbsp;of&nbsp;test&nbsp;results&nbsp;typically&nbsp;want&nbsp;to&nbsp;group&nbsp;these&nbsp;runs<br>
+together,&nbsp;then&nbsp;compute&nbsp;summary&nbsp;statistics&nbsp;across&nbsp;runs.&nbsp;<a href="#Value">Value</a>&nbsp;provides&nbsp;the<br>
+Merge*&nbsp;family&nbsp;of&nbsp;methods&nbsp;for&nbsp;this&nbsp;kind&nbsp;of&nbsp;aggregation.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Package Contents</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.value.failure.html">failure</a><br>
+<a href="telemetry.value.failure_unittest.html">failure_unittest</a><br>
+<a href="telemetry.value.histogram.html">histogram</a><br>
+<a href="telemetry.value.histogram_unittest.html">histogram_unittest</a><br>
+<a href="telemetry.value.histogram_util.html">histogram_util</a><br>
+<a href="telemetry.value.histogram_util_unittest.html">histogram_util_unittest</a><br>
+<a href="telemetry.value.improvement_direction.html">improvement_direction</a><br>
+</td><td width="25%" valign=top><a href="telemetry.value.list_of_scalar_values.html">list_of_scalar_values</a><br>
+<a href="telemetry.value.list_of_scalar_values_unittest.html">list_of_scalar_values_unittest</a><br>
+<a href="telemetry.value.list_of_string_values.html">list_of_string_values</a><br>
+<a href="telemetry.value.list_of_string_values_unittest.html">list_of_string_values_unittest</a><br>
+<a href="telemetry.value.merge_values.html">merge_values</a><br>
+<a href="telemetry.value.merge_values_unittest.html">merge_values_unittest</a><br>
+<a href="telemetry.value.none_values.html">none_values</a><br>
+</td><td width="25%" valign=top><a href="telemetry.value.scalar.html">scalar</a><br>
+<a href="telemetry.value.scalar_unittest.html">scalar_unittest</a><br>
+<a href="telemetry.value.skip.html">skip</a><br>
+<a href="telemetry.value.skip_unittest.html">skip_unittest</a><br>
+<a href="telemetry.value.string.html">string</a><br>
+<a href="telemetry.value.string_unittest.html">string_unittest</a><br>
+<a href="telemetry.value.summarizable.html">summarizable</a><br>
+</td><td width="25%" valign=top><a href="telemetry.value.summary.html">summary</a><br>
+<a href="telemetry.value.summary_unittest.html">summary_unittest</a><br>
+<a href="telemetry.value.trace.html">trace</a><br>
+<a href="telemetry.value.trace_unittest.html">trace_unittest</a><br>
+<a href="telemetry.value.value_unittest.html">value_unittest</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.value.html#Value">Value</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="Value">class <strong>Value</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>An&nbsp;abstract&nbsp;value&nbsp;produced&nbsp;by&nbsp;a&nbsp;telemetry&nbsp;page&nbsp;test.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="Value-AsDict"><strong>AsDict</strong></a>(self)</dt><dd><tt>Pre-serializes&nbsp;a&nbsp;value&nbsp;to&nbsp;a&nbsp;dict&nbsp;for&nbsp;output&nbsp;as&nbsp;JSON.</tt></dd></dl>
+
+<dl><dt><a name="Value-AsDictWithoutBaseClassEntries"><strong>AsDictWithoutBaseClassEntries</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="Value-GetBuildbotDataType"><strong>GetBuildbotDataType</strong></a>(self, output_context)</dt><dd><tt>Returns&nbsp;the&nbsp;buildbot's&nbsp;equivalent&nbsp;data_type.<br>
+&nbsp;<br>
+This&nbsp;should&nbsp;be&nbsp;one&nbsp;of&nbsp;the&nbsp;values&nbsp;accepted&nbsp;by&nbsp;perf_tests_results_helper.py.</tt></dd></dl>
+
+<dl><dt><a name="Value-GetBuildbotValue"><strong>GetBuildbotValue</strong></a>(self)</dt><dd><tt>Returns&nbsp;the&nbsp;buildbot's&nbsp;equivalent&nbsp;value.</tt></dd></dl>
+
+<dl><dt><a name="Value-GetChartAndTraceNameForComputedSummaryResult"><strong>GetChartAndTraceNameForComputedSummaryResult</strong></a>(self, trace_tag)</dt></dl>
+
+<dl><dt><a name="Value-GetChartAndTraceNameForPerPageResult"><strong>GetChartAndTraceNameForPerPageResult</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="Value-GetRepresentativeNumber"><strong>GetRepresentativeNumber</strong></a>(self)</dt><dd><tt>Gets&nbsp;a&nbsp;single&nbsp;scalar&nbsp;value&nbsp;that&nbsp;best-represents&nbsp;this&nbsp;value.<br>
+&nbsp;<br>
+Returns&nbsp;None&nbsp;if&nbsp;not&nbsp;possible.</tt></dd></dl>
+
+<dl><dt><a name="Value-GetRepresentativeString"><strong>GetRepresentativeString</strong></a>(self)</dt><dd><tt>Gets&nbsp;a&nbsp;string&nbsp;value&nbsp;that&nbsp;best-represents&nbsp;this&nbsp;value.<br>
+&nbsp;<br>
+Returns&nbsp;None&nbsp;if&nbsp;not&nbsp;possible.</tt></dd></dl>
+
+<dl><dt><a name="Value-IsMergableWith"><strong>IsMergableWith</strong></a>(self, that)</dt></dl>
+
+<dl><dt><a name="Value-__eq__"><strong>__eq__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="Value-__hash__"><strong>__hash__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="Value-__init__"><strong>__init__</strong></a>(self, page, name, units, important, description, tir_label)</dt><dd><tt>A&nbsp;generic&nbsp;<a href="#Value">Value</a>&nbsp;<a href="__builtin__.html#object">object</a>.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;page:&nbsp;A&nbsp;Page&nbsp;<a href="__builtin__.html#object">object</a>,&nbsp;may&nbsp;be&nbsp;given&nbsp;as&nbsp;None&nbsp;to&nbsp;indicate&nbsp;that&nbsp;the&nbsp;value<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;represents&nbsp;results&nbsp;for&nbsp;multiple&nbsp;pages.<br>
+&nbsp;&nbsp;name:&nbsp;A&nbsp;value&nbsp;name&nbsp;string,&nbsp;may&nbsp;contain&nbsp;a&nbsp;dot.&nbsp;Values&nbsp;from&nbsp;the&nbsp;same&nbsp;test<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;with&nbsp;the&nbsp;same&nbsp;prefix&nbsp;before&nbsp;the&nbsp;dot&nbsp;may&nbsp;be&nbsp;considered&nbsp;to&nbsp;belong&nbsp;to<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;the&nbsp;same&nbsp;chart.<br>
+&nbsp;&nbsp;units:&nbsp;A&nbsp;units&nbsp;string.<br>
+&nbsp;&nbsp;important:&nbsp;Whether&nbsp;the&nbsp;value&nbsp;is&nbsp;"important".&nbsp;Causes&nbsp;the&nbsp;value&nbsp;to&nbsp;appear<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;by&nbsp;default&nbsp;in&nbsp;downstream&nbsp;UIs.<br>
+&nbsp;&nbsp;description:&nbsp;A&nbsp;string&nbsp;explaining&nbsp;in&nbsp;human-understandable&nbsp;terms&nbsp;what&nbsp;this<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;value&nbsp;represents.<br>
+&nbsp;&nbsp;tir_label:&nbsp;The&nbsp;string&nbsp;label&nbsp;of&nbsp;the&nbsp;TimelineInteractionRecord&nbsp;with<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;which&nbsp;this&nbsp;value&nbsp;is&nbsp;associated.</tt></dd></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="Value-MergeLikeValuesFromDifferentPages"><strong>MergeLikeValuesFromDifferentPages</strong></a>(cls, values)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Combines&nbsp;the&nbsp;provided&nbsp;values&nbsp;into&nbsp;a&nbsp;single&nbsp;compound&nbsp;value.<br>
+&nbsp;<br>
+When&nbsp;a&nbsp;full&nbsp;pageset&nbsp;runs,&nbsp;a&nbsp;single&nbsp;value_name&nbsp;will&nbsp;usually&nbsp;end&nbsp;up&nbsp;getting<br>
+collected&nbsp;for&nbsp;multiple&nbsp;pages.&nbsp;For&nbsp;instance,&nbsp;we&nbsp;may&nbsp;end&nbsp;up&nbsp;with<br>
+&nbsp;&nbsp;&nbsp;[ScalarValue(page1,&nbsp;'a',&nbsp;&nbsp;1),<br>
+&nbsp;&nbsp;&nbsp;&nbsp;ScalarValue(page2,&nbsp;'a',&nbsp;&nbsp;2)]<br>
+&nbsp;<br>
+This&nbsp;function&nbsp;takes&nbsp;in&nbsp;the&nbsp;values&nbsp;of&nbsp;the&nbsp;same&nbsp;name,&nbsp;but&nbsp;across&nbsp;multiple<br>
+pages,&nbsp;and&nbsp;produces&nbsp;a&nbsp;single&nbsp;summary&nbsp;result&nbsp;value.&nbsp;In&nbsp;this&nbsp;instance,&nbsp;it<br>
+could&nbsp;produce&nbsp;a&nbsp;ScalarValue(None,&nbsp;'a',&nbsp;1.5)&nbsp;to&nbsp;indicate&nbsp;averaging,&nbsp;or&nbsp;even<br>
+ListOfScalarValues(None,&nbsp;'a',&nbsp;[1,&nbsp;2])&nbsp;if&nbsp;concatenated&nbsp;output&nbsp;was&nbsp;desired.<br>
+&nbsp;<br>
+Some&nbsp;results&nbsp;are&nbsp;so&nbsp;specific&nbsp;to&nbsp;a&nbsp;page&nbsp;that&nbsp;they&nbsp;make&nbsp;no&nbsp;sense&nbsp;when<br>
+aggregated&nbsp;across&nbsp;pages.&nbsp;If&nbsp;merging&nbsp;values&nbsp;of&nbsp;this&nbsp;type&nbsp;across&nbsp;pages&nbsp;is<br>
+non-sensical,&nbsp;this&nbsp;method&nbsp;may&nbsp;return&nbsp;None.</tt></dd></dl>
+
+<dl><dt><a name="Value-MergeLikeValuesFromSamePage"><strong>MergeLikeValuesFromSamePage</strong></a>(cls, values)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Combines&nbsp;the&nbsp;provided&nbsp;list&nbsp;of&nbsp;values&nbsp;into&nbsp;a&nbsp;single&nbsp;compound&nbsp;value.<br>
+&nbsp;<br>
+When&nbsp;a&nbsp;page&nbsp;runs&nbsp;multiple&nbsp;times,&nbsp;it&nbsp;may&nbsp;produce&nbsp;multiple&nbsp;values.&nbsp;This<br>
+function&nbsp;is&nbsp;given&nbsp;the&nbsp;same-named&nbsp;values&nbsp;across&nbsp;the&nbsp;multiple&nbsp;runs,&nbsp;and&nbsp;has<br>
+the&nbsp;responsibility&nbsp;of&nbsp;producing&nbsp;a&nbsp;single&nbsp;result.<br>
+&nbsp;<br>
+It&nbsp;must&nbsp;return&nbsp;a&nbsp;single&nbsp;<a href="#Value">Value</a>.&nbsp;If&nbsp;merging&nbsp;does&nbsp;not&nbsp;make&nbsp;sense,&nbsp;the<br>
+implementation&nbsp;must&nbsp;pick&nbsp;a&nbsp;representative&nbsp;value&nbsp;from&nbsp;one&nbsp;of&nbsp;the&nbsp;runs.<br>
+&nbsp;<br>
+For&nbsp;instance,&nbsp;it&nbsp;may&nbsp;be&nbsp;given<br>
+&nbsp;&nbsp;&nbsp;&nbsp;[ScalarValue(page,&nbsp;'a',&nbsp;1),&nbsp;ScalarValue(page,&nbsp;'a',&nbsp;2)]<br>
+and&nbsp;it&nbsp;might&nbsp;produce<br>
+&nbsp;&nbsp;&nbsp;&nbsp;ListOfScalarValues(page,&nbsp;'a',&nbsp;[1,&nbsp;2])</tt></dd></dl>
+
+<hr>
+Static methods defined here:<br>
+<dl><dt><a name="Value-FromDict"><strong>FromDict</strong></a>(value_dict, page_dict)</dt><dd><tt>Produces&nbsp;a&nbsp;value&nbsp;from&nbsp;a&nbsp;value&nbsp;dict&nbsp;and&nbsp;a&nbsp;page&nbsp;dict.<br>
+&nbsp;<br>
+<a href="#Value">Value</a>&nbsp;dicts&nbsp;are&nbsp;produced&nbsp;by&nbsp;serialization&nbsp;to&nbsp;JSON,&nbsp;and&nbsp;must&nbsp;be&nbsp;accompanied<br>
+by&nbsp;a&nbsp;dict&nbsp;mapping&nbsp;page&nbsp;IDs&nbsp;to&nbsp;pages,&nbsp;also&nbsp;produced&nbsp;by&nbsp;serialization,&nbsp;in<br>
+order&nbsp;to&nbsp;be&nbsp;completely&nbsp;deserialized.&nbsp;If&nbsp;deserializing&nbsp;multiple&nbsp;values,&nbsp;use<br>
+ListOfValuesFromListOfDicts&nbsp;instead.<br>
+&nbsp;<br>
+value_dict:&nbsp;a&nbsp;dictionary&nbsp;produced&nbsp;by&nbsp;<a href="#Value-AsDict">AsDict</a>()&nbsp;on&nbsp;a&nbsp;value&nbsp;subclass.<br>
+page_dict:&nbsp;a&nbsp;dictionary&nbsp;mapping&nbsp;IDs&nbsp;to&nbsp;page&nbsp;objects.</tt></dd></dl>
+
+<dl><dt><a name="Value-GetConstructorKwArgs"><strong>GetConstructorKwArgs</strong></a>(value_dict, page_dict)</dt><dd><tt>Produces&nbsp;constructor&nbsp;arguments&nbsp;from&nbsp;a&nbsp;value&nbsp;dict&nbsp;and&nbsp;a&nbsp;page&nbsp;dict.<br>
+&nbsp;<br>
+Takes&nbsp;a&nbsp;dict&nbsp;parsed&nbsp;from&nbsp;JSON&nbsp;and&nbsp;an&nbsp;index&nbsp;of&nbsp;pages&nbsp;and&nbsp;recovers&nbsp;the<br>
+keyword&nbsp;arguments&nbsp;to&nbsp;be&nbsp;passed&nbsp;to&nbsp;the&nbsp;constructor&nbsp;for&nbsp;deserializing&nbsp;the<br>
+dict.<br>
+&nbsp;<br>
+value_dict:&nbsp;a&nbsp;dictionary&nbsp;produced&nbsp;by&nbsp;<a href="#Value-AsDict">AsDict</a>()&nbsp;on&nbsp;a&nbsp;value&nbsp;subclass.<br>
+page_dict:&nbsp;a&nbsp;dictionary&nbsp;mapping&nbsp;IDs&nbsp;to&nbsp;page&nbsp;objects.</tt></dd></dl>
+
+<dl><dt><a name="Value-GetJSONTypeName"><strong>GetJSONTypeName</strong></a>()</dt><dd><tt>Gets&nbsp;the&nbsp;typename&nbsp;for&nbsp;serialization&nbsp;to&nbsp;JSON&nbsp;using&nbsp;AsDict.</tt></dd></dl>
+
+<dl><dt><a name="Value-ListOfValuesFromListOfDicts"><strong>ListOfValuesFromListOfDicts</strong></a>(value_dicts, page_dict)</dt><dd><tt>Takes&nbsp;a&nbsp;list&nbsp;of&nbsp;value&nbsp;dicts&nbsp;to&nbsp;values.<br>
+&nbsp;<br>
+Given&nbsp;a&nbsp;list&nbsp;of&nbsp;value&nbsp;dicts&nbsp;produced&nbsp;by&nbsp;AsDict,&nbsp;this&nbsp;method<br>
+deserializes&nbsp;the&nbsp;dicts&nbsp;given&nbsp;a&nbsp;dict&nbsp;mapping&nbsp;page&nbsp;IDs&nbsp;to&nbsp;pages.<br>
+This&nbsp;method&nbsp;performs&nbsp;memoization&nbsp;for&nbsp;deserializing&nbsp;a&nbsp;list&nbsp;of&nbsp;values<br>
+efficiently,&nbsp;where&nbsp;FromDict&nbsp;is&nbsp;meant&nbsp;to&nbsp;handle&nbsp;one-offs.<br>
+&nbsp;<br>
+values:&nbsp;a&nbsp;list&nbsp;of&nbsp;value&nbsp;dicts&nbsp;produced&nbsp;by&nbsp;<a href="#Value-AsDict">AsDict</a>()&nbsp;on&nbsp;a&nbsp;value&nbsp;subclass.<br>
+page_dict:&nbsp;a&nbsp;dictionary&nbsp;mapping&nbsp;IDs&nbsp;to&nbsp;page&nbsp;objects.</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>name_suffix</strong></dt>
+<dd><tt>Returns&nbsp;the&nbsp;string&nbsp;after&nbsp;a&nbsp;.&nbsp;in&nbsp;the&nbsp;name,&nbsp;or&nbsp;the&nbsp;full&nbsp;name&nbsp;otherwise.</tt></dd>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-ValueNameFromTraceAndChartName"><strong>ValueNameFromTraceAndChartName</strong></a>(trace_name, chart_name<font color="#909090">=None</font>)</dt><dd><tt>Mangles&nbsp;a&nbsp;trace&nbsp;name&nbsp;plus&nbsp;optional&nbsp;chart&nbsp;name&nbsp;into&nbsp;a&nbsp;standard&nbsp;string.<br>
+&nbsp;<br>
+A&nbsp;value&nbsp;might&nbsp;just&nbsp;be&nbsp;a&nbsp;bareword&nbsp;name,&nbsp;e.g.&nbsp;numPixels.&nbsp;In&nbsp;that&nbsp;case,&nbsp;its<br>
+chart&nbsp;may&nbsp;be&nbsp;None.<br>
+&nbsp;<br>
+But,&nbsp;a&nbsp;value&nbsp;might&nbsp;also&nbsp;be&nbsp;intended&nbsp;for&nbsp;display&nbsp;with&nbsp;other&nbsp;values,&nbsp;in&nbsp;which<br>
+case&nbsp;the&nbsp;chart&nbsp;name&nbsp;indicates&nbsp;that&nbsp;grouping.&nbsp;So,&nbsp;you&nbsp;might&nbsp;have<br>
+screen.numPixels,&nbsp;screen.resolution,&nbsp;where&nbsp;chartName='screen'.</tt></dd></dl>
+</td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>COMPUTED_PER_PAGE_SUMMARY_OUTPUT_CONTEXT</strong> = 'merged-pages-result-output-context'<br>
+<strong>CONCATENATE</strong> = 'concatenate'<br>
+<strong>PER_PAGE_RESULT_OUTPUT_CONTEXT</strong> = 'per-page-result-output-context'<br>
+<strong>PICK_FIRST</strong> = 'pick-first'<br>
+<strong>SUMMARY_RESULT_OUTPUT_CONTEXT</strong> = 'summary-result-output-context'</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.value.improvement_direction.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.value.improvement_direction.html
new file mode 100644
index 0000000..8f4b222
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.value.improvement_direction.html
@@ -0,0 +1,33 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.value.improvement_direction</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.value.html"><font color="#ffffff">value</font></a>.improvement_direction</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/value/improvement_direction.py">telemetry/value/improvement_direction.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-IsValid"><strong>IsValid</strong></a>(improvement_direction)</dt></dl>
+</td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>DOWN</strong> = 'down'<br>
+<strong>UP</strong> = 'up'</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.value.list_of_scalar_values.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.value.list_of_scalar_values.html
new file mode 100644
index 0000000..ec9dc88
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.value.list_of_scalar_values.html
@@ -0,0 +1,173 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.value.list_of_scalar_values</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.value.html"><font color="#ffffff">value</font></a>.list_of_scalar_values</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/value/list_of_scalar_values.py">telemetry/value/list_of_scalar_values.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="math.html">math</a><br>
+<a href="telemetry.value.none_values.html">telemetry.value.none_values</a><br>
+</td><td width="25%" valign=top><a href="numbers.html">numbers</a><br>
+<a href="telemetry.value.summarizable.html">telemetry.value.summarizable</a><br>
+</td><td width="25%" valign=top><a href="telemetry.value.html">telemetry.value</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.value.summarizable.html#SummarizableValue">telemetry.value.summarizable.SummarizableValue</a>(<a href="telemetry.value.html#Value">telemetry.value.Value</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.value.list_of_scalar_values.html#ListOfScalarValues">ListOfScalarValues</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ListOfScalarValues">class <strong>ListOfScalarValues</strong></a>(<a href="telemetry.value.summarizable.html#SummarizableValue">telemetry.value.summarizable.SummarizableValue</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt><a href="#ListOfScalarValues">ListOfScalarValues</a>&nbsp;represents&nbsp;a&nbsp;list&nbsp;of&nbsp;numbers.<br>
+&nbsp;<br>
+By&nbsp;default,&nbsp;std&nbsp;is&nbsp;the&nbsp;standard&nbsp;deviation&nbsp;of&nbsp;all&nbsp;numbers&nbsp;in&nbsp;the&nbsp;list.&nbsp;Std&nbsp;can<br>
+also&nbsp;be&nbsp;specified&nbsp;in&nbsp;the&nbsp;constructor&nbsp;if&nbsp;the&nbsp;numbers&nbsp;are&nbsp;not&nbsp;from&nbsp;the&nbsp;same<br>
+population.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.value.list_of_scalar_values.html#ListOfScalarValues">ListOfScalarValues</a></dd>
+<dd><a href="telemetry.value.summarizable.html#SummarizableValue">telemetry.value.summarizable.SummarizableValue</a></dd>
+<dd><a href="telemetry.value.html#Value">telemetry.value.Value</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="ListOfScalarValues-AsDict"><strong>AsDict</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="ListOfScalarValues-GetBuildbotDataType"><strong>GetBuildbotDataType</strong></a>(self, output_context)</dt></dl>
+
+<dl><dt><a name="ListOfScalarValues-GetBuildbotValue"><strong>GetBuildbotValue</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="ListOfScalarValues-GetRepresentativeNumber"><strong>GetRepresentativeNumber</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="ListOfScalarValues-GetRepresentativeString"><strong>GetRepresentativeString</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="ListOfScalarValues-IsMergableWith"><strong>IsMergableWith</strong></a>(self, that)</dt></dl>
+
+<dl><dt><a name="ListOfScalarValues-__init__"><strong>__init__</strong></a>(self, page, name, units, values, important<font color="#909090">=True</font>, description<font color="#909090">=None</font>, tir_label<font color="#909090">=None</font>, none_value_reason<font color="#909090">=None</font>, std<font color="#909090">=None</font>, same_page_merge_policy<font color="#909090">='concatenate'</font>, improvement_direction<font color="#909090">=None</font>)</dt></dl>
+
+<dl><dt><a name="ListOfScalarValues-__repr__"><strong>__repr__</strong></a>(self)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="ListOfScalarValues-MergeLikeValuesFromDifferentPages"><strong>MergeLikeValuesFromDifferentPages</strong></a>(cls, values)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="ListOfScalarValues-MergeLikeValuesFromSamePage"><strong>MergeLikeValuesFromSamePage</strong></a>(cls, values)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Static methods defined here:<br>
+<dl><dt><a name="ListOfScalarValues-FromDict"><strong>FromDict</strong></a>(value_dict, page_dict)</dt></dl>
+
+<dl><dt><a name="ListOfScalarValues-GetJSONTypeName"><strong>GetJSONTypeName</strong></a>()</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>std</strong></dt>
+</dl>
+<dl><dt><strong>variance</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.value.summarizable.html#SummarizableValue">telemetry.value.summarizable.SummarizableValue</a>:<br>
+<dl><dt><a name="ListOfScalarValues-AsDictWithoutBaseClassEntries"><strong>AsDictWithoutBaseClassEntries</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.value.summarizable.html#SummarizableValue">telemetry.value.summarizable.SummarizableValue</a>:<br>
+<dl><dt><strong>improvement_direction</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.value.html#Value">telemetry.value.Value</a>:<br>
+<dl><dt><a name="ListOfScalarValues-GetChartAndTraceNameForComputedSummaryResult"><strong>GetChartAndTraceNameForComputedSummaryResult</strong></a>(self, trace_tag)</dt></dl>
+
+<dl><dt><a name="ListOfScalarValues-GetChartAndTraceNameForPerPageResult"><strong>GetChartAndTraceNameForPerPageResult</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="ListOfScalarValues-__eq__"><strong>__eq__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="ListOfScalarValues-__hash__"><strong>__hash__</strong></a>(self)</dt></dl>
+
+<hr>
+Static methods inherited from <a href="telemetry.value.html#Value">telemetry.value.Value</a>:<br>
+<dl><dt><a name="ListOfScalarValues-GetConstructorKwArgs"><strong>GetConstructorKwArgs</strong></a>(value_dict, page_dict)</dt><dd><tt>Produces&nbsp;constructor&nbsp;arguments&nbsp;from&nbsp;a&nbsp;value&nbsp;dict&nbsp;and&nbsp;a&nbsp;page&nbsp;dict.<br>
+&nbsp;<br>
+Takes&nbsp;a&nbsp;dict&nbsp;parsed&nbsp;from&nbsp;JSON&nbsp;and&nbsp;an&nbsp;index&nbsp;of&nbsp;pages&nbsp;and&nbsp;recovers&nbsp;the<br>
+keyword&nbsp;arguments&nbsp;to&nbsp;be&nbsp;passed&nbsp;to&nbsp;the&nbsp;constructor&nbsp;for&nbsp;deserializing&nbsp;the<br>
+dict.<br>
+&nbsp;<br>
+value_dict:&nbsp;a&nbsp;dictionary&nbsp;produced&nbsp;by&nbsp;<a href="#ListOfScalarValues-AsDict">AsDict</a>()&nbsp;on&nbsp;a&nbsp;value&nbsp;subclass.<br>
+page_dict:&nbsp;a&nbsp;dictionary&nbsp;mapping&nbsp;IDs&nbsp;to&nbsp;page&nbsp;objects.</tt></dd></dl>
+
+<dl><dt><a name="ListOfScalarValues-ListOfValuesFromListOfDicts"><strong>ListOfValuesFromListOfDicts</strong></a>(value_dicts, page_dict)</dt><dd><tt>Takes&nbsp;a&nbsp;list&nbsp;of&nbsp;value&nbsp;dicts&nbsp;to&nbsp;values.<br>
+&nbsp;<br>
+Given&nbsp;a&nbsp;list&nbsp;of&nbsp;value&nbsp;dicts&nbsp;produced&nbsp;by&nbsp;AsDict,&nbsp;this&nbsp;method<br>
+deserializes&nbsp;the&nbsp;dicts&nbsp;given&nbsp;a&nbsp;dict&nbsp;mapping&nbsp;page&nbsp;IDs&nbsp;to&nbsp;pages.<br>
+This&nbsp;method&nbsp;performs&nbsp;memoization&nbsp;for&nbsp;deserializing&nbsp;a&nbsp;list&nbsp;of&nbsp;values<br>
+efficiently,&nbsp;where&nbsp;FromDict&nbsp;is&nbsp;meant&nbsp;to&nbsp;handle&nbsp;one-offs.<br>
+&nbsp;<br>
+values:&nbsp;a&nbsp;list&nbsp;of&nbsp;value&nbsp;dicts&nbsp;produced&nbsp;by&nbsp;<a href="#ListOfScalarValues-AsDict">AsDict</a>()&nbsp;on&nbsp;a&nbsp;value&nbsp;subclass.<br>
+page_dict:&nbsp;a&nbsp;dictionary&nbsp;mapping&nbsp;IDs&nbsp;to&nbsp;page&nbsp;objects.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.value.html#Value">telemetry.value.Value</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>name_suffix</strong></dt>
+<dd><tt>Returns&nbsp;the&nbsp;string&nbsp;after&nbsp;a&nbsp;.&nbsp;in&nbsp;the&nbsp;name,&nbsp;or&nbsp;the&nbsp;full&nbsp;name&nbsp;otherwise.</tt></dd>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-PooledStandardDeviation"><strong>PooledStandardDeviation</strong></a>(list_of_samples, list_of_variances<font color="#909090">=None</font>)</dt><dd><tt>Compute&nbsp;standard&nbsp;deviation&nbsp;for&nbsp;a&nbsp;list&nbsp;of&nbsp;samples.<br>
+&nbsp;<br>
+See:&nbsp;https://en.wikipedia.org/wiki/Pooled_variance&nbsp;for&nbsp;the&nbsp;formula.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;list_of_samples:&nbsp;a&nbsp;list&nbsp;of&nbsp;lists,&nbsp;each&nbsp;is&nbsp;a&nbsp;list&nbsp;of&nbsp;numbers.<br>
+&nbsp;&nbsp;list_of_variances:&nbsp;a&nbsp;list&nbsp;of&nbsp;numbers,&nbsp;the&nbsp;i-th&nbsp;element&nbsp;is&nbsp;the&nbsp;variance&nbsp;of<br>
+&nbsp;&nbsp;&nbsp;&nbsp;the&nbsp;i-th&nbsp;sample&nbsp;in&nbsp;list_of_samples.&nbsp;If&nbsp;this&nbsp;is&nbsp;None,&nbsp;we&nbsp;use<br>
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#-Variance">Variance</a>(sample)&nbsp;to&nbsp;get&nbsp;the&nbsp;variance&nbsp;of&nbsp;the&nbsp;i-th&nbsp;sample.</tt></dd></dl>
+ <dl><dt><a name="-StandardDeviation"><strong>StandardDeviation</strong></a>(sample)</dt><dd><tt>Compute&nbsp;standard&nbsp;deviation&nbsp;for&nbsp;a&nbsp;list&nbsp;of&nbsp;numbers.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;sample:&nbsp;a&nbsp;list&nbsp;of&nbsp;numbers.</tt></dd></dl>
+ <dl><dt><a name="-Variance"><strong>Variance</strong></a>(sample)</dt><dd><tt>Compute&nbsp;the&nbsp;population&nbsp;variance.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;sample:&nbsp;a&nbsp;list&nbsp;of&nbsp;numbers.</tt></dd></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.value.merge_values.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.value.merge_values.html
new file mode 100644
index 0000000..2939fca
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.value.merge_values.html
@@ -0,0 +1,90 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.value.merge_values</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.value.html"><font color="#ffffff">value</font></a>.merge_values</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/value/merge_values.py">telemetry/value/merge_values.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.value.failure.html">telemetry.value.failure</a><br>
+</td><td width="25%" valign=top><a href="telemetry.value.skip.html">telemetry.value.skip</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-DefaultKeyFunc"><strong>DefaultKeyFunc</strong></a>(value)</dt><dd><tt>Keys&nbsp;values&nbsp;in&nbsp;a&nbsp;standard&nbsp;way&nbsp;for&nbsp;grouping&nbsp;in&nbsp;merging&nbsp;and&nbsp;summary.<br>
+&nbsp;<br>
+Merging&nbsp;and&nbsp;summarization&nbsp;can&nbsp;be&nbsp;parameterized&nbsp;by&nbsp;a&nbsp;function&nbsp;that&nbsp;groups<br>
+values&nbsp;into&nbsp;equivalence&nbsp;classes.&nbsp;Any&nbsp;function&nbsp;that&nbsp;returns&nbsp;a&nbsp;comparable<br>
+object&nbsp;can&nbsp;be&nbsp;used&nbsp;as&nbsp;a&nbsp;key_func,&nbsp;but&nbsp;merge_values&nbsp;and&nbsp;summary&nbsp;both&nbsp;use&nbsp;this<br>
+function&nbsp;by&nbsp;default,&nbsp;to&nbsp;allow&nbsp;the&nbsp;default&nbsp;grouping&nbsp;to&nbsp;change&nbsp;as&nbsp;Telemtry&nbsp;does.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;value:&nbsp;A&nbsp;Telemetry&nbsp;Value&nbsp;instance<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;A&nbsp;comparable&nbsp;object&nbsp;used&nbsp;to&nbsp;group&nbsp;values.</tt></dd></dl>
+ <dl><dt><a name="-GroupStably"><strong>GroupStably</strong></a>(all_values, key_func)</dt><dd><tt>Groups&nbsp;an&nbsp;array&nbsp;by&nbsp;key_func,&nbsp;with&nbsp;the&nbsp;groups&nbsp;returned&nbsp;in&nbsp;a&nbsp;stable&nbsp;order.<br>
+&nbsp;<br>
+Returns&nbsp;a&nbsp;list&nbsp;of&nbsp;groups.</tt></dd></dl>
+ <dl><dt><a name="-MergeLikeValuesFromDifferentPages"><strong>MergeLikeValuesFromDifferentPages</strong></a>(all_values, key_func<font color="#909090">=&lt;function DefaultKeyFunc&gt;</font>)</dt><dd><tt>Merges&nbsp;values&nbsp;that&nbsp;measure&nbsp;the&nbsp;same&nbsp;thing&nbsp;on&nbsp;different&nbsp;pages.<br>
+&nbsp;<br>
+After&nbsp;using&nbsp;MergeLikeValuesFromSamePage,&nbsp;one&nbsp;still&nbsp;ends&nbsp;up&nbsp;with&nbsp;values&nbsp;from<br>
+different&nbsp;pages:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ScalarValue(page1,&nbsp;'x',&nbsp;1,&nbsp;'foo')<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ScalarValue(page1,&nbsp;'y',&nbsp;30,&nbsp;'bar')<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ScalarValue(page2,&nbsp;'x',&nbsp;2,&nbsp;'foo')<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ScalarValue(page2,&nbsp;'y',&nbsp;40,&nbsp;'baz')<br>
+&nbsp;<br>
+This&nbsp;function&nbsp;will&nbsp;group&nbsp;values&nbsp;with&nbsp;the&nbsp;same&nbsp;name&nbsp;and&nbsp;tir_label&nbsp;together:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ListOfScalarValues(None,&nbsp;'x',&nbsp;[1,&nbsp;2],&nbsp;'foo')<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ListOfScalarValues(None,&nbsp;'y',&nbsp;[30],&nbsp;'bar')<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ListOfScalarValues(None,&nbsp;'y',&nbsp;[40],&nbsp;'baz')<br>
+&nbsp;<br>
+The&nbsp;workhorse&nbsp;of&nbsp;this&nbsp;code&nbsp;is&nbsp;Value.MergeLikeValuesFromDifferentPages.<br>
+&nbsp;<br>
+Not&nbsp;all&nbsp;values&nbsp;that&nbsp;go&nbsp;into&nbsp;this&nbsp;function&nbsp;will&nbsp;come&nbsp;out:&nbsp;not&nbsp;every&nbsp;value&nbsp;can<br>
+be&nbsp;merged&nbsp;across&nbsp;pages.&nbsp;Values&nbsp;whose&nbsp;MergeLikeValuesFromDifferentPages&nbsp;returns<br>
+None&nbsp;will&nbsp;be&nbsp;omitted&nbsp;from&nbsp;the&nbsp;results.<br>
+&nbsp;<br>
+This&nbsp;requires&nbsp;(but&nbsp;assumes)&nbsp;that&nbsp;the&nbsp;values&nbsp;passed&nbsp;in&nbsp;with&nbsp;the&nbsp;same&nbsp;name&nbsp;pass<br>
+the&nbsp;Value.IsMergableWith&nbsp;test.&nbsp;If&nbsp;this&nbsp;is&nbsp;not&nbsp;obeyed,&nbsp;the&nbsp;results<br>
+will&nbsp;be&nbsp;undefined.</tt></dd></dl>
+ <dl><dt><a name="-MergeLikeValuesFromSamePage"><strong>MergeLikeValuesFromSamePage</strong></a>(all_values, key_func<font color="#909090">=&lt;function DefaultKeyFunc&gt;</font>)</dt><dd><tt>Merges&nbsp;values&nbsp;that&nbsp;measure&nbsp;the&nbsp;same&nbsp;thing&nbsp;on&nbsp;the&nbsp;same&nbsp;page.<br>
+&nbsp;<br>
+A&nbsp;page&nbsp;may&nbsp;end&nbsp;up&nbsp;being&nbsp;measured&nbsp;multiple&nbsp;times,&nbsp;meaning&nbsp;that&nbsp;we&nbsp;may&nbsp;end&nbsp;up<br>
+with&nbsp;something&nbsp;like&nbsp;this:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ScalarValue(page1,&nbsp;'x',&nbsp;1,&nbsp;'foo')<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ScalarValue(page2,&nbsp;'x',&nbsp;4,&nbsp;'bar')<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ScalarValue(page1,&nbsp;'x',&nbsp;2,&nbsp;'foo')<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ScalarValue(page2,&nbsp;'x',&nbsp;5,&nbsp;'baz')<br>
+&nbsp;<br>
+This&nbsp;function&nbsp;will&nbsp;produce:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ListOfScalarValues(page1,&nbsp;'x',&nbsp;[1,&nbsp;2],&nbsp;'foo')<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ListOfScalarValues(page2,&nbsp;'x',&nbsp;[4],&nbsp;'bar')<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ListOfScalarValues(page2,&nbsp;'x',&nbsp;[5],&nbsp;'baz')<br>
+&nbsp;<br>
+The&nbsp;workhorse&nbsp;of&nbsp;this&nbsp;code&nbsp;is&nbsp;Value.MergeLikeValuesFromSamePage.<br>
+&nbsp;<br>
+This&nbsp;requires&nbsp;(but&nbsp;assumes)&nbsp;that&nbsp;the&nbsp;values&nbsp;passed&nbsp;in&nbsp;with&nbsp;the&nbsp;same&nbsp;grouping<br>
+key&nbsp;pass&nbsp;the&nbsp;Value.IsMergableWith&nbsp;test.&nbsp;If&nbsp;this&nbsp;is&nbsp;not&nbsp;obeyed,&nbsp;the<br>
+results&nbsp;will&nbsp;be&nbsp;undefined.</tt></dd></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.value.none_values.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.value.none_values.html
new file mode 100644
index 0000000..f2df036
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.value.none_values.html
@@ -0,0 +1,168 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.value.none_values</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.value.html"><font color="#ffffff">value</font></a>.none_values</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/value/none_values.py">telemetry/value/none_values.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="exceptions.html#Exception">exceptions.Exception</a>(<a href="exceptions.html#BaseException">exceptions.BaseException</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.value.none_values.html#NoneValueMissingReason">NoneValueMissingReason</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.value.none_values.html#ValueMustHaveNoneValue">ValueMustHaveNoneValue</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="NoneValueMissingReason">class <strong>NoneValueMissingReason</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.value.none_values.html#NoneValueMissingReason">NoneValueMissingReason</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="NoneValueMissingReason-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#NoneValueMissingReason-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#NoneValueMissingReason-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;object&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="NoneValueMissingReason-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#NoneValueMissingReason-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="NoneValueMissingReason-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#NoneValueMissingReason-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="NoneValueMissingReason-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#NoneValueMissingReason-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="NoneValueMissingReason-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#NoneValueMissingReason-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="NoneValueMissingReason-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="NoneValueMissingReason-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#NoneValueMissingReason-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="NoneValueMissingReason-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#NoneValueMissingReason-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="NoneValueMissingReason-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="NoneValueMissingReason-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#NoneValueMissingReason-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="NoneValueMissingReason-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ValueMustHaveNoneValue">class <strong>ValueMustHaveNoneValue</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.value.none_values.html#ValueMustHaveNoneValue">ValueMustHaveNoneValue</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="ValueMustHaveNoneValue-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#ValueMustHaveNoneValue-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#ValueMustHaveNoneValue-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;object&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="ValueMustHaveNoneValue-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#ValueMustHaveNoneValue-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="ValueMustHaveNoneValue-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#ValueMustHaveNoneValue-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="ValueMustHaveNoneValue-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#ValueMustHaveNoneValue-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="ValueMustHaveNoneValue-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#ValueMustHaveNoneValue-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="ValueMustHaveNoneValue-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="ValueMustHaveNoneValue-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#ValueMustHaveNoneValue-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="ValueMustHaveNoneValue-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#ValueMustHaveNoneValue-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="ValueMustHaveNoneValue-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="ValueMustHaveNoneValue-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#ValueMustHaveNoneValue-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="ValueMustHaveNoneValue-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-ValidateNoneValueReason"><strong>ValidateNoneValueReason</strong></a>(value, none_value_reason)</dt><dd><tt>Ensures&nbsp;that&nbsp;the&nbsp;none_value_reason&nbsp;is&nbsp;appropriate&nbsp;for&nbsp;the&nbsp;given&nbsp;value.<br>
+&nbsp;<br>
+There&nbsp;is&nbsp;a&nbsp;logical&nbsp;equality&nbsp;between&nbsp;having&nbsp;a&nbsp;value&nbsp;of&nbsp;None&nbsp;and&nbsp;having&nbsp;a<br>
+reason&nbsp;for&nbsp;being&nbsp;None.&nbsp;That&nbsp;is&nbsp;to&nbsp;say,&nbsp;value&nbsp;is&nbsp;None&nbsp;if&nbsp;and&nbsp;only&nbsp;if<br>
+none_value_reason&nbsp;is&nbsp;a&nbsp;string.</tt></dd></dl>
+</td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>MERGE_FAILURE_REASON</strong> = 'Merging values containing a None value results in a None value.'</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.value.scalar.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.value.scalar.html
new file mode 100644
index 0000000..1ffc353
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.value.scalar.html
@@ -0,0 +1,141 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.value.scalar</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.value.html"><font color="#ffffff">value</font></a>.scalar</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/value/scalar.py">telemetry/value/scalar.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.value.list_of_scalar_values.html">telemetry.value.list_of_scalar_values</a><br>
+<a href="telemetry.value.none_values.html">telemetry.value.none_values</a><br>
+</td><td width="25%" valign=top><a href="numbers.html">numbers</a><br>
+<a href="telemetry.value.summarizable.html">telemetry.value.summarizable</a><br>
+</td><td width="25%" valign=top><a href="telemetry.value.html">telemetry.value</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.value.summarizable.html#SummarizableValue">telemetry.value.summarizable.SummarizableValue</a>(<a href="telemetry.value.html#Value">telemetry.value.Value</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.value.scalar.html#ScalarValue">ScalarValue</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ScalarValue">class <strong>ScalarValue</strong></a>(<a href="telemetry.value.summarizable.html#SummarizableValue">telemetry.value.summarizable.SummarizableValue</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.value.scalar.html#ScalarValue">ScalarValue</a></dd>
+<dd><a href="telemetry.value.summarizable.html#SummarizableValue">telemetry.value.summarizable.SummarizableValue</a></dd>
+<dd><a href="telemetry.value.html#Value">telemetry.value.Value</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="ScalarValue-AsDict"><strong>AsDict</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="ScalarValue-GetBuildbotDataType"><strong>GetBuildbotDataType</strong></a>(self, output_context)</dt></dl>
+
+<dl><dt><a name="ScalarValue-GetBuildbotValue"><strong>GetBuildbotValue</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="ScalarValue-GetRepresentativeNumber"><strong>GetRepresentativeNumber</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="ScalarValue-GetRepresentativeString"><strong>GetRepresentativeString</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="ScalarValue-__init__"><strong>__init__</strong></a>(self, page, name, units, value, important<font color="#909090">=True</font>, description<font color="#909090">=None</font>, tir_label<font color="#909090">=None</font>, none_value_reason<font color="#909090">=None</font>, improvement_direction<font color="#909090">=None</font>)</dt><dd><tt>A&nbsp;single&nbsp;value&nbsp;(float&nbsp;or&nbsp;integer)&nbsp;result&nbsp;from&nbsp;a&nbsp;test.<br>
+&nbsp;<br>
+A&nbsp;test&nbsp;that&nbsp;counts&nbsp;the&nbsp;number&nbsp;of&nbsp;DOM&nbsp;elements&nbsp;in&nbsp;a&nbsp;page&nbsp;might&nbsp;produce&nbsp;a<br>
+scalar&nbsp;value:<br>
+&nbsp;&nbsp;&nbsp;<a href="#ScalarValue">ScalarValue</a>(page,&nbsp;'num_dom_elements',&nbsp;'count',&nbsp;num_elements)</tt></dd></dl>
+
+<dl><dt><a name="ScalarValue-__repr__"><strong>__repr__</strong></a>(self)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="ScalarValue-MergeLikeValuesFromDifferentPages"><strong>MergeLikeValuesFromDifferentPages</strong></a>(cls, values)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="ScalarValue-MergeLikeValuesFromSamePage"><strong>MergeLikeValuesFromSamePage</strong></a>(cls, values)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Static methods defined here:<br>
+<dl><dt><a name="ScalarValue-FromDict"><strong>FromDict</strong></a>(value_dict, page_dict)</dt></dl>
+
+<dl><dt><a name="ScalarValue-GetJSONTypeName"><strong>GetJSONTypeName</strong></a>()</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.value.summarizable.html#SummarizableValue">telemetry.value.summarizable.SummarizableValue</a>:<br>
+<dl><dt><a name="ScalarValue-AsDictWithoutBaseClassEntries"><strong>AsDictWithoutBaseClassEntries</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.value.summarizable.html#SummarizableValue">telemetry.value.summarizable.SummarizableValue</a>:<br>
+<dl><dt><strong>improvement_direction</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.value.html#Value">telemetry.value.Value</a>:<br>
+<dl><dt><a name="ScalarValue-GetChartAndTraceNameForComputedSummaryResult"><strong>GetChartAndTraceNameForComputedSummaryResult</strong></a>(self, trace_tag)</dt></dl>
+
+<dl><dt><a name="ScalarValue-GetChartAndTraceNameForPerPageResult"><strong>GetChartAndTraceNameForPerPageResult</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="ScalarValue-IsMergableWith"><strong>IsMergableWith</strong></a>(self, that)</dt></dl>
+
+<dl><dt><a name="ScalarValue-__eq__"><strong>__eq__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="ScalarValue-__hash__"><strong>__hash__</strong></a>(self)</dt></dl>
+
+<hr>
+Static methods inherited from <a href="telemetry.value.html#Value">telemetry.value.Value</a>:<br>
+<dl><dt><a name="ScalarValue-GetConstructorKwArgs"><strong>GetConstructorKwArgs</strong></a>(value_dict, page_dict)</dt><dd><tt>Produces&nbsp;constructor&nbsp;arguments&nbsp;from&nbsp;a&nbsp;value&nbsp;dict&nbsp;and&nbsp;a&nbsp;page&nbsp;dict.<br>
+&nbsp;<br>
+Takes&nbsp;a&nbsp;dict&nbsp;parsed&nbsp;from&nbsp;JSON&nbsp;and&nbsp;an&nbsp;index&nbsp;of&nbsp;pages&nbsp;and&nbsp;recovers&nbsp;the<br>
+keyword&nbsp;arguments&nbsp;to&nbsp;be&nbsp;passed&nbsp;to&nbsp;the&nbsp;constructor&nbsp;for&nbsp;deserializing&nbsp;the<br>
+dict.<br>
+&nbsp;<br>
+value_dict:&nbsp;a&nbsp;dictionary&nbsp;produced&nbsp;by&nbsp;<a href="#ScalarValue-AsDict">AsDict</a>()&nbsp;on&nbsp;a&nbsp;value&nbsp;subclass.<br>
+page_dict:&nbsp;a&nbsp;dictionary&nbsp;mapping&nbsp;IDs&nbsp;to&nbsp;page&nbsp;objects.</tt></dd></dl>
+
+<dl><dt><a name="ScalarValue-ListOfValuesFromListOfDicts"><strong>ListOfValuesFromListOfDicts</strong></a>(value_dicts, page_dict)</dt><dd><tt>Takes&nbsp;a&nbsp;list&nbsp;of&nbsp;value&nbsp;dicts&nbsp;to&nbsp;values.<br>
+&nbsp;<br>
+Given&nbsp;a&nbsp;list&nbsp;of&nbsp;value&nbsp;dicts&nbsp;produced&nbsp;by&nbsp;AsDict,&nbsp;this&nbsp;method<br>
+deserializes&nbsp;the&nbsp;dicts&nbsp;given&nbsp;a&nbsp;dict&nbsp;mapping&nbsp;page&nbsp;IDs&nbsp;to&nbsp;pages.<br>
+This&nbsp;method&nbsp;performs&nbsp;memoization&nbsp;for&nbsp;deserializing&nbsp;a&nbsp;list&nbsp;of&nbsp;values<br>
+efficiently,&nbsp;where&nbsp;FromDict&nbsp;is&nbsp;meant&nbsp;to&nbsp;handle&nbsp;one-offs.<br>
+&nbsp;<br>
+values:&nbsp;a&nbsp;list&nbsp;of&nbsp;value&nbsp;dicts&nbsp;produced&nbsp;by&nbsp;<a href="#ScalarValue-AsDict">AsDict</a>()&nbsp;on&nbsp;a&nbsp;value&nbsp;subclass.<br>
+page_dict:&nbsp;a&nbsp;dictionary&nbsp;mapping&nbsp;IDs&nbsp;to&nbsp;page&nbsp;objects.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.value.html#Value">telemetry.value.Value</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>name_suffix</strong></dt>
+<dd><tt>Returns&nbsp;the&nbsp;string&nbsp;after&nbsp;a&nbsp;.&nbsp;in&nbsp;the&nbsp;name,&nbsp;or&nbsp;the&nbsp;full&nbsp;name&nbsp;otherwise.</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.value.skip.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.value.skip.html
new file mode 100644
index 0000000..13b67eb
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.value.skip.html
@@ -0,0 +1,134 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.value.skip</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.value.html"><font color="#ffffff">value</font></a>.skip</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/value/skip.py">telemetry/value/skip.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.value.html">telemetry.value</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.value.html#Value">telemetry.value.Value</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.value.skip.html#SkipValue">SkipValue</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="SkipValue">class <strong>SkipValue</strong></a>(<a href="telemetry.value.html#Value">telemetry.value.Value</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.value.skip.html#SkipValue">SkipValue</a></dd>
+<dd><a href="telemetry.value.html#Value">telemetry.value.Value</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="SkipValue-AsDict"><strong>AsDict</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="SkipValue-GetBuildbotDataType"><strong>GetBuildbotDataType</strong></a>(self, output_context)</dt></dl>
+
+<dl><dt><a name="SkipValue-GetBuildbotValue"><strong>GetBuildbotValue</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="SkipValue-GetChartAndTraceNameForPerPageResult"><strong>GetChartAndTraceNameForPerPageResult</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="SkipValue-GetRepresentativeNumber"><strong>GetRepresentativeNumber</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="SkipValue-GetRepresentativeString"><strong>GetRepresentativeString</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="SkipValue-__init__"><strong>__init__</strong></a>(self, page, reason, description<font color="#909090">=None</font>)</dt><dd><tt>A&nbsp;value&nbsp;representing&nbsp;a&nbsp;skipped&nbsp;page.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;page:&nbsp;The&nbsp;skipped&nbsp;page&nbsp;object.<br>
+&nbsp;&nbsp;reason:&nbsp;The&nbsp;string&nbsp;reason&nbsp;the&nbsp;page&nbsp;was&nbsp;skipped.</tt></dd></dl>
+
+<dl><dt><a name="SkipValue-__repr__"><strong>__repr__</strong></a>(self)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="SkipValue-MergeLikeValuesFromDifferentPages"><strong>MergeLikeValuesFromDifferentPages</strong></a>(cls, values)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="SkipValue-MergeLikeValuesFromSamePage"><strong>MergeLikeValuesFromSamePage</strong></a>(cls, values)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Static methods defined here:<br>
+<dl><dt><a name="SkipValue-FromDict"><strong>FromDict</strong></a>(value_dict, page_dict)</dt></dl>
+
+<dl><dt><a name="SkipValue-GetJSONTypeName"><strong>GetJSONTypeName</strong></a>()</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>reason</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.value.html#Value">telemetry.value.Value</a>:<br>
+<dl><dt><a name="SkipValue-AsDictWithoutBaseClassEntries"><strong>AsDictWithoutBaseClassEntries</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="SkipValue-GetChartAndTraceNameForComputedSummaryResult"><strong>GetChartAndTraceNameForComputedSummaryResult</strong></a>(self, trace_tag)</dt></dl>
+
+<dl><dt><a name="SkipValue-IsMergableWith"><strong>IsMergableWith</strong></a>(self, that)</dt></dl>
+
+<dl><dt><a name="SkipValue-__eq__"><strong>__eq__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="SkipValue-__hash__"><strong>__hash__</strong></a>(self)</dt></dl>
+
+<hr>
+Static methods inherited from <a href="telemetry.value.html#Value">telemetry.value.Value</a>:<br>
+<dl><dt><a name="SkipValue-GetConstructorKwArgs"><strong>GetConstructorKwArgs</strong></a>(value_dict, page_dict)</dt><dd><tt>Produces&nbsp;constructor&nbsp;arguments&nbsp;from&nbsp;a&nbsp;value&nbsp;dict&nbsp;and&nbsp;a&nbsp;page&nbsp;dict.<br>
+&nbsp;<br>
+Takes&nbsp;a&nbsp;dict&nbsp;parsed&nbsp;from&nbsp;JSON&nbsp;and&nbsp;an&nbsp;index&nbsp;of&nbsp;pages&nbsp;and&nbsp;recovers&nbsp;the<br>
+keyword&nbsp;arguments&nbsp;to&nbsp;be&nbsp;passed&nbsp;to&nbsp;the&nbsp;constructor&nbsp;for&nbsp;deserializing&nbsp;the<br>
+dict.<br>
+&nbsp;<br>
+value_dict:&nbsp;a&nbsp;dictionary&nbsp;produced&nbsp;by&nbsp;<a href="#SkipValue-AsDict">AsDict</a>()&nbsp;on&nbsp;a&nbsp;value&nbsp;subclass.<br>
+page_dict:&nbsp;a&nbsp;dictionary&nbsp;mapping&nbsp;IDs&nbsp;to&nbsp;page&nbsp;objects.</tt></dd></dl>
+
+<dl><dt><a name="SkipValue-ListOfValuesFromListOfDicts"><strong>ListOfValuesFromListOfDicts</strong></a>(value_dicts, page_dict)</dt><dd><tt>Takes&nbsp;a&nbsp;list&nbsp;of&nbsp;value&nbsp;dicts&nbsp;to&nbsp;values.<br>
+&nbsp;<br>
+Given&nbsp;a&nbsp;list&nbsp;of&nbsp;value&nbsp;dicts&nbsp;produced&nbsp;by&nbsp;AsDict,&nbsp;this&nbsp;method<br>
+deserializes&nbsp;the&nbsp;dicts&nbsp;given&nbsp;a&nbsp;dict&nbsp;mapping&nbsp;page&nbsp;IDs&nbsp;to&nbsp;pages.<br>
+This&nbsp;method&nbsp;performs&nbsp;memoization&nbsp;for&nbsp;deserializing&nbsp;a&nbsp;list&nbsp;of&nbsp;values<br>
+efficiently,&nbsp;where&nbsp;FromDict&nbsp;is&nbsp;meant&nbsp;to&nbsp;handle&nbsp;one-offs.<br>
+&nbsp;<br>
+values:&nbsp;a&nbsp;list&nbsp;of&nbsp;value&nbsp;dicts&nbsp;produced&nbsp;by&nbsp;<a href="#SkipValue-AsDict">AsDict</a>()&nbsp;on&nbsp;a&nbsp;value&nbsp;subclass.<br>
+page_dict:&nbsp;a&nbsp;dictionary&nbsp;mapping&nbsp;IDs&nbsp;to&nbsp;page&nbsp;objects.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.value.html#Value">telemetry.value.Value</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>name_suffix</strong></dt>
+<dd><tt>Returns&nbsp;the&nbsp;string&nbsp;after&nbsp;a&nbsp;.&nbsp;in&nbsp;the&nbsp;name,&nbsp;or&nbsp;the&nbsp;full&nbsp;name&nbsp;otherwise.</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.value.summarizable.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.value.summarizable.html
new file mode 100644
index 0000000..61d3ba0
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.value.summarizable.html
@@ -0,0 +1,142 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.value.summarizable</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.value.html"><font color="#ffffff">value</font></a>.summarizable</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/value/summarizable.py">telemetry/value/summarizable.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.value.html">telemetry.value</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.value.html#Value">telemetry.value.Value</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.value.summarizable.html#SummarizableValue">SummarizableValue</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="SummarizableValue">class <strong>SummarizableValue</strong></a>(<a href="telemetry.value.html#Value">telemetry.value.Value</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.value.summarizable.html#SummarizableValue">SummarizableValue</a></dd>
+<dd><a href="telemetry.value.html#Value">telemetry.value.Value</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="SummarizableValue-AsDict"><strong>AsDict</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="SummarizableValue-AsDictWithoutBaseClassEntries"><strong>AsDictWithoutBaseClassEntries</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="SummarizableValue-GetBuildbotDataType"><strong>GetBuildbotDataType</strong></a>(self, output_context)</dt><dd><tt>Returns&nbsp;the&nbsp;buildbot's&nbsp;equivalent&nbsp;data_type.<br>
+&nbsp;<br>
+This&nbsp;should&nbsp;be&nbsp;one&nbsp;of&nbsp;the&nbsp;values&nbsp;accepted&nbsp;by&nbsp;perf_tests_results_helper.py.</tt></dd></dl>
+
+<dl><dt><a name="SummarizableValue-GetBuildbotValue"><strong>GetBuildbotValue</strong></a>(self)</dt><dd><tt>Returns&nbsp;the&nbsp;buildbot's&nbsp;equivalent&nbsp;value.</tt></dd></dl>
+
+<dl><dt><a name="SummarizableValue-GetRepresentativeNumber"><strong>GetRepresentativeNumber</strong></a>(self)</dt><dd><tt>Gets&nbsp;a&nbsp;single&nbsp;scalar&nbsp;value&nbsp;that&nbsp;best-represents&nbsp;this&nbsp;value.<br>
+&nbsp;<br>
+Returns&nbsp;None&nbsp;if&nbsp;not&nbsp;possible.</tt></dd></dl>
+
+<dl><dt><a name="SummarizableValue-GetRepresentativeString"><strong>GetRepresentativeString</strong></a>(self)</dt><dd><tt>Gets&nbsp;a&nbsp;string&nbsp;value&nbsp;that&nbsp;best-represents&nbsp;this&nbsp;value.<br>
+&nbsp;<br>
+Returns&nbsp;None&nbsp;if&nbsp;not&nbsp;possible.</tt></dd></dl>
+
+<dl><dt><a name="SummarizableValue-__init__"><strong>__init__</strong></a>(self, page, name, units, important, description, tir_label, improvement_direction)</dt><dd><tt>A&nbsp;summarizable&nbsp;value&nbsp;result&nbsp;from&nbsp;a&nbsp;test.</tt></dd></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="SummarizableValue-MergeLikeValuesFromDifferentPages"><strong>MergeLikeValuesFromDifferentPages</strong></a>(cls, values)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="SummarizableValue-MergeLikeValuesFromSamePage"><strong>MergeLikeValuesFromSamePage</strong></a>(cls, values)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Static methods defined here:<br>
+<dl><dt><a name="SummarizableValue-GetJSONTypeName"><strong>GetJSONTypeName</strong></a>()</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>improvement_direction</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.value.html#Value">telemetry.value.Value</a>:<br>
+<dl><dt><a name="SummarizableValue-GetChartAndTraceNameForComputedSummaryResult"><strong>GetChartAndTraceNameForComputedSummaryResult</strong></a>(self, trace_tag)</dt></dl>
+
+<dl><dt><a name="SummarizableValue-GetChartAndTraceNameForPerPageResult"><strong>GetChartAndTraceNameForPerPageResult</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="SummarizableValue-IsMergableWith"><strong>IsMergableWith</strong></a>(self, that)</dt></dl>
+
+<dl><dt><a name="SummarizableValue-__eq__"><strong>__eq__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="SummarizableValue-__hash__"><strong>__hash__</strong></a>(self)</dt></dl>
+
+<hr>
+Static methods inherited from <a href="telemetry.value.html#Value">telemetry.value.Value</a>:<br>
+<dl><dt><a name="SummarizableValue-FromDict"><strong>FromDict</strong></a>(value_dict, page_dict)</dt><dd><tt>Produces&nbsp;a&nbsp;value&nbsp;from&nbsp;a&nbsp;value&nbsp;dict&nbsp;and&nbsp;a&nbsp;page&nbsp;dict.<br>
+&nbsp;<br>
+<a href="telemetry.value.html#Value">Value</a>&nbsp;dicts&nbsp;are&nbsp;produced&nbsp;by&nbsp;serialization&nbsp;to&nbsp;JSON,&nbsp;and&nbsp;must&nbsp;be&nbsp;accompanied<br>
+by&nbsp;a&nbsp;dict&nbsp;mapping&nbsp;page&nbsp;IDs&nbsp;to&nbsp;pages,&nbsp;also&nbsp;produced&nbsp;by&nbsp;serialization,&nbsp;in<br>
+order&nbsp;to&nbsp;be&nbsp;completely&nbsp;deserialized.&nbsp;If&nbsp;deserializing&nbsp;multiple&nbsp;values,&nbsp;use<br>
+ListOfValuesFromListOfDicts&nbsp;instead.<br>
+&nbsp;<br>
+value_dict:&nbsp;a&nbsp;dictionary&nbsp;produced&nbsp;by&nbsp;<a href="#SummarizableValue-AsDict">AsDict</a>()&nbsp;on&nbsp;a&nbsp;value&nbsp;subclass.<br>
+page_dict:&nbsp;a&nbsp;dictionary&nbsp;mapping&nbsp;IDs&nbsp;to&nbsp;page&nbsp;objects.</tt></dd></dl>
+
+<dl><dt><a name="SummarizableValue-GetConstructorKwArgs"><strong>GetConstructorKwArgs</strong></a>(value_dict, page_dict)</dt><dd><tt>Produces&nbsp;constructor&nbsp;arguments&nbsp;from&nbsp;a&nbsp;value&nbsp;dict&nbsp;and&nbsp;a&nbsp;page&nbsp;dict.<br>
+&nbsp;<br>
+Takes&nbsp;a&nbsp;dict&nbsp;parsed&nbsp;from&nbsp;JSON&nbsp;and&nbsp;an&nbsp;index&nbsp;of&nbsp;pages&nbsp;and&nbsp;recovers&nbsp;the<br>
+keyword&nbsp;arguments&nbsp;to&nbsp;be&nbsp;passed&nbsp;to&nbsp;the&nbsp;constructor&nbsp;for&nbsp;deserializing&nbsp;the<br>
+dict.<br>
+&nbsp;<br>
+value_dict:&nbsp;a&nbsp;dictionary&nbsp;produced&nbsp;by&nbsp;<a href="#SummarizableValue-AsDict">AsDict</a>()&nbsp;on&nbsp;a&nbsp;value&nbsp;subclass.<br>
+page_dict:&nbsp;a&nbsp;dictionary&nbsp;mapping&nbsp;IDs&nbsp;to&nbsp;page&nbsp;objects.</tt></dd></dl>
+
+<dl><dt><a name="SummarizableValue-ListOfValuesFromListOfDicts"><strong>ListOfValuesFromListOfDicts</strong></a>(value_dicts, page_dict)</dt><dd><tt>Takes&nbsp;a&nbsp;list&nbsp;of&nbsp;value&nbsp;dicts&nbsp;to&nbsp;values.<br>
+&nbsp;<br>
+Given&nbsp;a&nbsp;list&nbsp;of&nbsp;value&nbsp;dicts&nbsp;produced&nbsp;by&nbsp;AsDict,&nbsp;this&nbsp;method<br>
+deserializes&nbsp;the&nbsp;dicts&nbsp;given&nbsp;a&nbsp;dict&nbsp;mapping&nbsp;page&nbsp;IDs&nbsp;to&nbsp;pages.<br>
+This&nbsp;method&nbsp;performs&nbsp;memoization&nbsp;for&nbsp;deserializing&nbsp;a&nbsp;list&nbsp;of&nbsp;values<br>
+efficiently,&nbsp;where&nbsp;FromDict&nbsp;is&nbsp;meant&nbsp;to&nbsp;handle&nbsp;one-offs.<br>
+&nbsp;<br>
+values:&nbsp;a&nbsp;list&nbsp;of&nbsp;value&nbsp;dicts&nbsp;produced&nbsp;by&nbsp;<a href="#SummarizableValue-AsDict">AsDict</a>()&nbsp;on&nbsp;a&nbsp;value&nbsp;subclass.<br>
+page_dict:&nbsp;a&nbsp;dictionary&nbsp;mapping&nbsp;IDs&nbsp;to&nbsp;page&nbsp;objects.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.value.html#Value">telemetry.value.Value</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>name_suffix</strong></dt>
+<dd><tt>Returns&nbsp;the&nbsp;string&nbsp;after&nbsp;a&nbsp;.&nbsp;in&nbsp;the&nbsp;name,&nbsp;or&nbsp;the&nbsp;full&nbsp;name&nbsp;otherwise.</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.value.summary.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.value.summary.html
new file mode 100644
index 0000000..8d2b9eb
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.value.summary.html
@@ -0,0 +1,94 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.value.summary</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.value.html"><font color="#ffffff">value</font></a>.summary</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/value/summary.py">telemetry/value/summary.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.value.failure.html">telemetry.value.failure</a><br>
+</td><td width="25%" valign=top><a href="telemetry.value.merge_values.html">telemetry.value.merge_values</a><br>
+</td><td width="25%" valign=top><a href="telemetry.value.skip.html">telemetry.value.skip</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.value.summary.html#Summary">Summary</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="Summary">class <strong>Summary</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Computes&nbsp;summary&nbsp;values&nbsp;from&nbsp;the&nbsp;per-page-run&nbsp;values&nbsp;produced&nbsp;by&nbsp;a&nbsp;test.<br>
+&nbsp;<br>
+Some&nbsp;telemetry&nbsp;benchmark&nbsp;repeat&nbsp;a&nbsp;number&nbsp;of&nbsp;times&nbsp;in&nbsp;order&nbsp;to&nbsp;get&nbsp;a&nbsp;reliable<br>
+measurement.&nbsp;The&nbsp;test&nbsp;does&nbsp;not&nbsp;have&nbsp;to&nbsp;handle&nbsp;merging&nbsp;of&nbsp;these&nbsp;runs:<br>
+summarizer&nbsp;does&nbsp;it&nbsp;for&nbsp;you.<br>
+&nbsp;<br>
+For&nbsp;instance,&nbsp;if&nbsp;two&nbsp;pages&nbsp;run,&nbsp;3&nbsp;and&nbsp;1&nbsp;time&nbsp;respectively:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;ScalarValue(page1,&nbsp;'foo',&nbsp;units='ms',&nbsp;1)<br>
+&nbsp;&nbsp;&nbsp;&nbsp;ScalarValue(page1,&nbsp;'foo',&nbsp;units='ms',&nbsp;1)<br>
+&nbsp;&nbsp;&nbsp;&nbsp;ScalarValue(page1,&nbsp;'foo',&nbsp;units='ms',&nbsp;1)<br>
+&nbsp;&nbsp;&nbsp;&nbsp;ScalarValue(page2,&nbsp;'foo',&nbsp;units='ms',&nbsp;2)<br>
+&nbsp;<br>
+Then&nbsp;summarizer&nbsp;will&nbsp;produce&nbsp;two&nbsp;sets&nbsp;of&nbsp;values.&nbsp;First,<br>
+computed_per_page_values:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;[<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ListOfScalarValues(page1,&nbsp;'foo',&nbsp;units='ms',&nbsp;[1,1,1])],<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ListOfScalarValues(page2,&nbsp;'foo',&nbsp;units='ms',&nbsp;[2])]<br>
+&nbsp;&nbsp;&nbsp;&nbsp;]<br>
+&nbsp;<br>
+In&nbsp;addition,&nbsp;it&nbsp;will&nbsp;produce&nbsp;a&nbsp;summary&nbsp;value:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;[<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ListOfScalarValues(page=None,&nbsp;'foo',&nbsp;units='ms',&nbsp;[1,1,1,2])]<br>
+&nbsp;&nbsp;&nbsp;&nbsp;]<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="Summary-__init__"><strong>__init__</strong></a>(self, all_page_specific_values, key_func<font color="#909090">=&lt;function DefaultKeyFunc&gt;</font>)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>computed_per_page_values</strong></dt>
+</dl>
+<dl><dt><strong>computed_summary_values</strong></dt>
+</dl>
+<dl><dt><strong>interleaved_computed_per_page_values_and_summaries</strong></dt>
+<dd><tt>Returns&nbsp;the&nbsp;computed&nbsp;per&nbsp;page&nbsp;values&nbsp;and&nbsp;summary&nbsp;values&nbsp;interleaved.<br>
+&nbsp;<br>
+All&nbsp;the&nbsp;results&nbsp;for&nbsp;a&nbsp;given&nbsp;name&nbsp;are&nbsp;printed&nbsp;together.&nbsp;First&nbsp;per&nbsp;page<br>
+values,&nbsp;then&nbsp;summary&nbsp;values.</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.value.trace.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.value.trace.html
new file mode 100644
index 0000000..3e1353a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.value.trace.html
@@ -0,0 +1,168 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.value.trace</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.value.html"><font color="#ffffff">value</font></a>.trace</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/value/trace.py">telemetry/value/trace.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="StringIO.html">StringIO</a><br>
+<a href="catapult_base.cloud_storage.html">catapult_base.cloud_storage</a><br>
+<a href="datetime.html">datetime</a><br>
+<a href="telemetry.internal.util.file_handle.html">telemetry.internal.util.file_handle</a><br>
+</td><td width="25%" valign=top><a href="logging.html">logging</a><br>
+<a href="os.html">os</a><br>
+<a href="random.html">random</a><br>
+<a href="shutil.html">shutil</a><br>
+</td><td width="25%" valign=top><a href="sys.html">sys</a><br>
+<a href="tempfile.html">tempfile</a><br>
+<a href="tracing_build.trace2html.html">tracing_build.trace2html</a><br>
+<a href="telemetry.timeline.trace_data.html">telemetry.timeline.trace_data</a><br>
+</td><td width="25%" valign=top><a href="telemetry.value.html">telemetry.value</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.value.html#Value">telemetry.value.Value</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.value.trace.html#TraceValue">TraceValue</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TraceValue">class <strong>TraceValue</strong></a>(<a href="telemetry.value.html#Value">telemetry.value.Value</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.value.trace.html#TraceValue">TraceValue</a></dd>
+<dd><a href="telemetry.value.html#Value">telemetry.value.Value</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="TraceValue-AsDict"><strong>AsDict</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TraceValue-CleanUp"><strong>CleanUp</strong></a>(self)</dt><dd><tt>Cleans&nbsp;up&nbsp;tempfile&nbsp;after&nbsp;it&nbsp;is&nbsp;no&nbsp;longer&nbsp;needed.<br>
+&nbsp;<br>
+A&nbsp;cleaned&nbsp;up&nbsp;<a href="#TraceValue">TraceValue</a>&nbsp;cannot&nbsp;be&nbsp;used&nbsp;for&nbsp;further&nbsp;operations.&nbsp;<a href="#TraceValue-CleanUp">CleanUp</a>()<br>
+may&nbsp;be&nbsp;called&nbsp;more&nbsp;than&nbsp;once&nbsp;without&nbsp;error.</tt></dd></dl>
+
+<dl><dt><a name="TraceValue-GetBuildbotDataType"><strong>GetBuildbotDataType</strong></a>(self, output_context)</dt></dl>
+
+<dl><dt><a name="TraceValue-GetBuildbotValue"><strong>GetBuildbotValue</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TraceValue-GetRepresentativeNumber"><strong>GetRepresentativeNumber</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TraceValue-GetRepresentativeString"><strong>GetRepresentativeString</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TraceValue-Serialize"><strong>Serialize</strong></a>(self, dir_path)</dt></dl>
+
+<dl><dt><a name="TraceValue-UploadToCloud"><strong>UploadToCloud</strong></a>(self, bucket)</dt></dl>
+
+<dl><dt><a name="TraceValue-__enter__"><strong>__enter__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TraceValue-__exit__"><strong>__exit__</strong></a>(self, _, __, ___)</dt></dl>
+
+<dl><dt><a name="TraceValue-__init__"><strong>__init__</strong></a>(self, page, trace_data, important<font color="#909090">=False</font>, description<font color="#909090">=None</font>)</dt><dd><tt>A&nbsp;value&nbsp;that&nbsp;contains&nbsp;a&nbsp;TraceData&nbsp;object&nbsp;and&nbsp;knows&nbsp;how&nbsp;to<br>
+output&nbsp;it.<br>
+&nbsp;<br>
+Adding&nbsp;TraceValues&nbsp;and&nbsp;outputting&nbsp;as&nbsp;JSON&nbsp;will&nbsp;produce&nbsp;a&nbsp;directory&nbsp;full&nbsp;of<br>
+HTML&nbsp;files&nbsp;called&nbsp;trace_files.&nbsp;Outputting&nbsp;as&nbsp;chart&nbsp;JSON&nbsp;will&nbsp;also&nbsp;produce<br>
+an&nbsp;index,&nbsp;files.html,&nbsp;linking&nbsp;to&nbsp;each&nbsp;of&nbsp;these&nbsp;files.</tt></dd></dl>
+
+<dl><dt><a name="TraceValue-__repr__"><strong>__repr__</strong></a>(self)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="TraceValue-MergeLikeValuesFromDifferentPages"><strong>MergeLikeValuesFromDifferentPages</strong></a>(cls, values)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<dl><dt><a name="TraceValue-MergeLikeValuesFromSamePage"><strong>MergeLikeValuesFromSamePage</strong></a>(cls, values)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Static methods defined here:<br>
+<dl><dt><a name="TraceValue-GetJSONTypeName"><strong>GetJSONTypeName</strong></a>()</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>cleaned_up</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.value.html#Value">telemetry.value.Value</a>:<br>
+<dl><dt><a name="TraceValue-AsDictWithoutBaseClassEntries"><strong>AsDictWithoutBaseClassEntries</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TraceValue-GetChartAndTraceNameForComputedSummaryResult"><strong>GetChartAndTraceNameForComputedSummaryResult</strong></a>(self, trace_tag)</dt></dl>
+
+<dl><dt><a name="TraceValue-GetChartAndTraceNameForPerPageResult"><strong>GetChartAndTraceNameForPerPageResult</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="TraceValue-IsMergableWith"><strong>IsMergableWith</strong></a>(self, that)</dt></dl>
+
+<dl><dt><a name="TraceValue-__eq__"><strong>__eq__</strong></a>(self, other)</dt></dl>
+
+<dl><dt><a name="TraceValue-__hash__"><strong>__hash__</strong></a>(self)</dt></dl>
+
+<hr>
+Static methods inherited from <a href="telemetry.value.html#Value">telemetry.value.Value</a>:<br>
+<dl><dt><a name="TraceValue-FromDict"><strong>FromDict</strong></a>(value_dict, page_dict)</dt><dd><tt>Produces&nbsp;a&nbsp;value&nbsp;from&nbsp;a&nbsp;value&nbsp;dict&nbsp;and&nbsp;a&nbsp;page&nbsp;dict.<br>
+&nbsp;<br>
+<a href="telemetry.value.html#Value">Value</a>&nbsp;dicts&nbsp;are&nbsp;produced&nbsp;by&nbsp;serialization&nbsp;to&nbsp;JSON,&nbsp;and&nbsp;must&nbsp;be&nbsp;accompanied<br>
+by&nbsp;a&nbsp;dict&nbsp;mapping&nbsp;page&nbsp;IDs&nbsp;to&nbsp;pages,&nbsp;also&nbsp;produced&nbsp;by&nbsp;serialization,&nbsp;in<br>
+order&nbsp;to&nbsp;be&nbsp;completely&nbsp;deserialized.&nbsp;If&nbsp;deserializing&nbsp;multiple&nbsp;values,&nbsp;use<br>
+ListOfValuesFromListOfDicts&nbsp;instead.<br>
+&nbsp;<br>
+value_dict:&nbsp;a&nbsp;dictionary&nbsp;produced&nbsp;by&nbsp;<a href="#TraceValue-AsDict">AsDict</a>()&nbsp;on&nbsp;a&nbsp;value&nbsp;subclass.<br>
+page_dict:&nbsp;a&nbsp;dictionary&nbsp;mapping&nbsp;IDs&nbsp;to&nbsp;page&nbsp;objects.</tt></dd></dl>
+
+<dl><dt><a name="TraceValue-GetConstructorKwArgs"><strong>GetConstructorKwArgs</strong></a>(value_dict, page_dict)</dt><dd><tt>Produces&nbsp;constructor&nbsp;arguments&nbsp;from&nbsp;a&nbsp;value&nbsp;dict&nbsp;and&nbsp;a&nbsp;page&nbsp;dict.<br>
+&nbsp;<br>
+Takes&nbsp;a&nbsp;dict&nbsp;parsed&nbsp;from&nbsp;JSON&nbsp;and&nbsp;an&nbsp;index&nbsp;of&nbsp;pages&nbsp;and&nbsp;recovers&nbsp;the<br>
+keyword&nbsp;arguments&nbsp;to&nbsp;be&nbsp;passed&nbsp;to&nbsp;the&nbsp;constructor&nbsp;for&nbsp;deserializing&nbsp;the<br>
+dict.<br>
+&nbsp;<br>
+value_dict:&nbsp;a&nbsp;dictionary&nbsp;produced&nbsp;by&nbsp;<a href="#TraceValue-AsDict">AsDict</a>()&nbsp;on&nbsp;a&nbsp;value&nbsp;subclass.<br>
+page_dict:&nbsp;a&nbsp;dictionary&nbsp;mapping&nbsp;IDs&nbsp;to&nbsp;page&nbsp;objects.</tt></dd></dl>
+
+<dl><dt><a name="TraceValue-ListOfValuesFromListOfDicts"><strong>ListOfValuesFromListOfDicts</strong></a>(value_dicts, page_dict)</dt><dd><tt>Takes&nbsp;a&nbsp;list&nbsp;of&nbsp;value&nbsp;dicts&nbsp;to&nbsp;values.<br>
+&nbsp;<br>
+Given&nbsp;a&nbsp;list&nbsp;of&nbsp;value&nbsp;dicts&nbsp;produced&nbsp;by&nbsp;AsDict,&nbsp;this&nbsp;method<br>
+deserializes&nbsp;the&nbsp;dicts&nbsp;given&nbsp;a&nbsp;dict&nbsp;mapping&nbsp;page&nbsp;IDs&nbsp;to&nbsp;pages.<br>
+This&nbsp;method&nbsp;performs&nbsp;memoization&nbsp;for&nbsp;deserializing&nbsp;a&nbsp;list&nbsp;of&nbsp;values<br>
+efficiently,&nbsp;where&nbsp;FromDict&nbsp;is&nbsp;meant&nbsp;to&nbsp;handle&nbsp;one-offs.<br>
+&nbsp;<br>
+values:&nbsp;a&nbsp;list&nbsp;of&nbsp;value&nbsp;dicts&nbsp;produced&nbsp;by&nbsp;<a href="#TraceValue-AsDict">AsDict</a>()&nbsp;on&nbsp;a&nbsp;value&nbsp;subclass.<br>
+page_dict:&nbsp;a&nbsp;dictionary&nbsp;mapping&nbsp;IDs&nbsp;to&nbsp;page&nbsp;objects.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.value.html#Value">telemetry.value.Value</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>name_suffix</strong></dt>
+<dd><tt>Returns&nbsp;the&nbsp;string&nbsp;after&nbsp;a&nbsp;.&nbsp;in&nbsp;the&nbsp;name,&nbsp;or&nbsp;the&nbsp;full&nbsp;name&nbsp;otherwise.</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.html
new file mode 100644
index 0000000..1a609e9
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.html
@@ -0,0 +1,33 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: package telemetry.web_perf</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.web_perf</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/web_perf/__init__.py">telemetry/web_perf/__init__.py</a></font></td></tr></table>
+    <p><tt>The&nbsp;web_perf&nbsp;module&nbsp;provides&nbsp;utilities&nbsp;and&nbsp;measurements&nbsp;for&nbsp;benchmarking&nbsp;web<br>
+app's&nbsp;performance.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Package Contents</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.web_perf.metrics.html"><strong>metrics</strong>&nbsp;(package)</a><br>
+<a href="telemetry.web_perf.smooth_gesture_util.html">smooth_gesture_util</a><br>
+<a href="telemetry.web_perf.smooth_gesture_util_unittest.html">smooth_gesture_util_unittest</a><br>
+</td><td width="25%" valign=top><a href="telemetry.web_perf.story_test.html">story_test</a><br>
+<a href="telemetry.web_perf.timeline_based_measurement.html">timeline_based_measurement</a><br>
+<a href="telemetry.web_perf.timeline_based_measurement_unittest.html">timeline_based_measurement_unittest</a><br>
+</td><td width="25%" valign=top><a href="telemetry.web_perf.timeline_based_page_test.html">timeline_based_page_test</a><br>
+<a href="telemetry.web_perf.timeline_based_page_test_unittest.html">timeline_based_page_test_unittest</a><br>
+<a href="telemetry.web_perf.timeline_interaction_record.html">timeline_interaction_record</a><br>
+</td><td width="25%" valign=top><a href="telemetry.web_perf.timeline_interaction_record_unittest.html">timeline_interaction_record_unittest</a><br>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.blob_timeline.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.blob_timeline.html
new file mode 100644
index 0000000..5bd7910
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.blob_timeline.html
@@ -0,0 +1,104 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.web_perf.metrics.blob_timeline</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.web_perf.html"><font color="#ffffff">web_perf</font></a>.<a href="telemetry.web_perf.metrics.html"><font color="#ffffff">metrics</font></a>.blob_timeline</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/web_perf/metrics/blob_timeline.py">telemetry/web_perf/metrics/blob_timeline.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2015&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.value.improvement_direction.html">telemetry.value.improvement_direction</a><br>
+</td><td width="25%" valign=top><a href="telemetry.value.list_of_scalar_values.html">telemetry.value.list_of_scalar_values</a><br>
+</td><td width="25%" valign=top><a href="telemetry.web_perf.metrics.timeline_based_metric.html">telemetry.web_perf.metrics.timeline_based_metric</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.web_perf.metrics.blob_timeline.html#BlobTimelineMetric">BlobTimelineMetric</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="BlobTimelineMetric">class <strong>BlobTimelineMetric</strong></a>(<a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt><a href="#BlobTimelineMetric">BlobTimelineMetric</a>&nbsp;reports&nbsp;timing&nbsp;information&nbsp;about&nbsp;blob&nbsp;storage.<br>
+&nbsp;<br>
+The&nbsp;following&nbsp;metrics&nbsp;are&nbsp;added&nbsp;to&nbsp;the&nbsp;results:<br>
+&nbsp;&nbsp;*&nbsp;blob&nbsp;write&nbsp;times&nbsp;(blob_writes)<br>
+&nbsp;&nbsp;*&nbsp;blob&nbsp;read&nbsp;times&nbsp;(blob_reads)<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.web_perf.metrics.blob_timeline.html#BlobTimelineMetric">BlobTimelineMetric</a></dd>
+<dd><a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="BlobTimelineMetric-AddResults"><strong>AddResults</strong></a>(self, model, renderer_thread, interactions, results)</dt></dl>
+
+<dl><dt><a name="BlobTimelineMetric-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Static methods defined here:<br>
+<dl><dt><a name="BlobTimelineMetric-IsEventInInteraction"><strong>IsEventInInteraction</strong></a>(event, interaction)</dt></dl>
+
+<dl><dt><a name="BlobTimelineMetric-IsReadEvent"><strong>IsReadEvent</strong></a>(event)</dt></dl>
+
+<dl><dt><a name="BlobTimelineMetric-IsWriteEvent"><strong>IsWriteEvent</strong></a>(event)</dt></dl>
+
+<dl><dt><a name="BlobTimelineMetric-ThreadDurationIfPresent"><strong>ThreadDurationIfPresent</strong></a>(event)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a>:<br>
+<dl><dt><a name="BlobTimelineMetric-AddWholeTraceResults"><strong>AddWholeTraceResults</strong></a>(self, model, results)</dt><dd><tt>Computes&nbsp;and&nbsp;adds&nbsp;metrics&nbsp;corresponding&nbsp;to&nbsp;the&nbsp;entire&nbsp;trace.<br>
+&nbsp;<br>
+Override&nbsp;this&nbsp;method&nbsp;to&nbsp;compute&nbsp;results&nbsp;that&nbsp;correspond&nbsp;to&nbsp;the&nbsp;whole&nbsp;trace.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;model:&nbsp;An&nbsp;instance&nbsp;of&nbsp;telemetry.timeline.model.TimelineModel.<br>
+&nbsp;&nbsp;results:&nbsp;An&nbsp;instance&nbsp;of&nbsp;page.PageTestResults.</tt></dd></dl>
+
+<dl><dt><a name="BlobTimelineMetric-VerifyNonOverlappedRecords"><strong>VerifyNonOverlappedRecords</strong></a>(self, interaction_records)</dt><dd><tt>This&nbsp;raises&nbsp;exceptions&nbsp;if&nbsp;interaction_records&nbsp;contain&nbsp;overlapped&nbsp;ranges.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>READ_EVENT_NAME</strong> = 'BlobRequest'<br>
+<strong>WRITE_EVENT_NAME</strong> = 'Registry::RegisterBlob'</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.gpu_timeline.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.gpu_timeline.html
new file mode 100644
index 0000000..596a628
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.gpu_timeline.html
@@ -0,0 +1,112 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.web_perf.metrics.gpu_timeline</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.web_perf.html"><font color="#ffffff">web_perf</font></a>.<a href="telemetry.web_perf.metrics.html"><font color="#ffffff">metrics</font></a>.gpu_timeline</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/web_perf/metrics/gpu_timeline.py">telemetry/web_perf/metrics/gpu_timeline.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2015&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="collections.html">collections</a><br>
+<a href="telemetry.value.improvement_direction.html">telemetry.value.improvement_direction</a><br>
+</td><td width="25%" valign=top><a href="telemetry.value.list_of_scalar_values.html">telemetry.value.list_of_scalar_values</a><br>
+<a href="math.html">math</a><br>
+</td><td width="25%" valign=top><a href="telemetry.timeline.model.html">telemetry.timeline.model</a><br>
+<a href="telemetry.value.scalar.html">telemetry.value.scalar</a><br>
+</td><td width="25%" valign=top><a href="sys.html">sys</a><br>
+<a href="telemetry.web_perf.metrics.timeline_based_metric.html">telemetry.web_perf.metrics.timeline_based_metric</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.web_perf.metrics.gpu_timeline.html#GPUTimelineMetric">GPUTimelineMetric</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="GPUTimelineMetric">class <strong>GPUTimelineMetric</strong></a>(<a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Computes&nbsp;GPU&nbsp;based&nbsp;metrics.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.web_perf.metrics.gpu_timeline.html#GPUTimelineMetric">GPUTimelineMetric</a></dd>
+<dd><a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="GPUTimelineMetric-AddResults"><strong>AddResults</strong></a>(self, model, _, interaction_records, results)</dt></dl>
+
+<dl><dt><a name="GPUTimelineMetric-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a>:<br>
+<dl><dt><a name="GPUTimelineMetric-AddWholeTraceResults"><strong>AddWholeTraceResults</strong></a>(self, model, results)</dt><dd><tt>Computes&nbsp;and&nbsp;adds&nbsp;metrics&nbsp;corresponding&nbsp;to&nbsp;the&nbsp;entire&nbsp;trace.<br>
+&nbsp;<br>
+Override&nbsp;this&nbsp;method&nbsp;to&nbsp;compute&nbsp;results&nbsp;that&nbsp;correspond&nbsp;to&nbsp;the&nbsp;whole&nbsp;trace.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;model:&nbsp;An&nbsp;instance&nbsp;of&nbsp;telemetry.timeline.model.TimelineModel.<br>
+&nbsp;&nbsp;results:&nbsp;An&nbsp;instance&nbsp;of&nbsp;page.PageTestResults.</tt></dd></dl>
+
+<dl><dt><a name="GPUTimelineMetric-VerifyNonOverlappedRecords"><strong>VerifyNonOverlappedRecords</strong></a>(self, interaction_records)</dt><dd><tt>This&nbsp;raises&nbsp;exceptions&nbsp;if&nbsp;interaction_records&nbsp;contain&nbsp;overlapped&nbsp;ranges.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-TimelineName"><strong>TimelineName</strong></a>(name, source_type, value_type)</dt><dd><tt>Constructs&nbsp;the&nbsp;standard&nbsp;name&nbsp;given&nbsp;in&nbsp;the&nbsp;timeline.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;name:&nbsp;The&nbsp;name&nbsp;of&nbsp;the&nbsp;timeline,&nbsp;for&nbsp;example&nbsp;"total",&nbsp;or&nbsp;"render_compositor".<br>
+&nbsp;&nbsp;source_type:&nbsp;One&nbsp;of&nbsp;"cpu",&nbsp;"gpu"&nbsp;or&nbsp;None.&nbsp;None&nbsp;is&nbsp;only&nbsp;used&nbsp;for&nbsp;total&nbsp;times.<br>
+&nbsp;&nbsp;value_type:&nbsp;the&nbsp;type&nbsp;of&nbsp;value.&nbsp;For&nbsp;example&nbsp;"mean",&nbsp;"stddev"...etc.</tt></dd></dl>
+</td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>DEVICE_FRAME_END_MARKER</strong> = ('disabled-by-default-gpu.device', 'SwapBuffer')<br>
+<strong>SERVICE_FRAME_END_MARKER</strong> = ('disabled-by-default-gpu.service', 'SwapBuffer')<br>
+<strong>TOPLEVEL_DEVICE_CATEGORY</strong> = 'disabled-by-default-gpu.device'<br>
+<strong>TOPLEVEL_GL_CATEGORY</strong> = 'gpu_toplevel'<br>
+<strong>TOPLEVEL_SERVICE_CATEGORY</strong> = 'disabled-by-default-gpu.service'<br>
+<strong>TRACKED_GL_CONTEXT_NAME</strong> = {'BrowserCompositor': 'browser_compositor', 'Compositor': 'browser_compositor', 'RenderCompositor': 'render_compositor'}</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.html
new file mode 100644
index 0000000..40c9200
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.html
@@ -0,0 +1,51 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: package telemetry.web_perf.metrics</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.web_perf.html"><font color="#ffffff">web_perf</font></a>.metrics</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/web_perf/metrics/__init__.py">telemetry/web_perf/metrics/__init__.py</a></font></td></tr></table>
+    <p><tt>The&nbsp;web_perf.metrics&nbsp;module&nbsp;provides&nbsp;metrics&nbsp;for&nbsp;analyzing&nbsp;web&nbsp;performance.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Package Contents</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.web_perf.metrics.blob_timeline.html">blob_timeline</a><br>
+<a href="telemetry.web_perf.metrics.blob_timeline_unittest.html">blob_timeline_unittest</a><br>
+<a href="telemetry.web_perf.metrics.gpu_timeline.html">gpu_timeline</a><br>
+<a href="telemetry.web_perf.metrics.gpu_timeline_unittest.html">gpu_timeline_unittest</a><br>
+<a href="telemetry.web_perf.metrics.indexeddb_timeline.html">indexeddb_timeline</a><br>
+<a href="telemetry.web_perf.metrics.layout.html">layout</a><br>
+<a href="telemetry.web_perf.metrics.mainthread_jank_stats.html">mainthread_jank_stats</a><br>
+<a href="telemetry.web_perf.metrics.mainthread_jank_stats_unittest.html">mainthread_jank_stats_unittest</a><br>
+</td><td width="25%" valign=top><a href="telemetry.web_perf.metrics.memory_timeline.html">memory_timeline</a><br>
+<a href="telemetry.web_perf.metrics.memory_timeline_unittest.html">memory_timeline_unittest</a><br>
+<a href="telemetry.web_perf.metrics.rendering_frame.html">rendering_frame</a><br>
+<a href="telemetry.web_perf.metrics.rendering_frame_unittest.html">rendering_frame_unittest</a><br>
+<a href="telemetry.web_perf.metrics.rendering_stats.html">rendering_stats</a><br>
+<a href="telemetry.web_perf.metrics.rendering_stats_unittest.html">rendering_stats_unittest</a><br>
+<a href="telemetry.web_perf.metrics.responsiveness_metric.html">responsiveness_metric</a><br>
+<a href="telemetry.web_perf.metrics.single_event.html">single_event</a><br>
+</td><td width="25%" valign=top><a href="telemetry.web_perf.metrics.single_event_unittest.html">single_event_unittest</a><br>
+<a href="telemetry.web_perf.metrics.smoothness.html">smoothness</a><br>
+<a href="telemetry.web_perf.metrics.smoothness_unittest.html">smoothness_unittest</a><br>
+<a href="telemetry.web_perf.metrics.text_selection.html">text_selection</a><br>
+<a href="telemetry.web_perf.metrics.timeline_based_metric.html">timeline_based_metric</a><br>
+<a href="telemetry.web_perf.metrics.timeline_based_metric_unittest.html">timeline_based_metric_unittest</a><br>
+<a href="telemetry.web_perf.metrics.trace_event_stats.html">trace_event_stats</a><br>
+<a href="telemetry.web_perf.metrics.trace_event_stats_unittest.html">trace_event_stats_unittest</a><br>
+</td><td width="25%" valign=top><a href="telemetry.web_perf.metrics.v8_gc_latency.html">v8_gc_latency</a><br>
+<a href="telemetry.web_perf.metrics.v8_gc_latency_unittest.html">v8_gc_latency_unittest</a><br>
+<a href="telemetry.web_perf.metrics.webrtc_rendering_stats.html">webrtc_rendering_stats</a><br>
+<a href="telemetry.web_perf.metrics.webrtc_rendering_stats_unittest.html">webrtc_rendering_stats_unittest</a><br>
+<a href="telemetry.web_perf.metrics.webrtc_rendering_timeline.html">webrtc_rendering_timeline</a><br>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.indexeddb_timeline.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.indexeddb_timeline.html
new file mode 100644
index 0000000..7509b9b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.indexeddb_timeline.html
@@ -0,0 +1,80 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.web_perf.metrics.indexeddb_timeline</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.web_perf.html"><font color="#ffffff">web_perf</font></a>.<a href="telemetry.web_perf.metrics.html"><font color="#ffffff">metrics</font></a>.indexeddb_timeline</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/web_perf/metrics/indexeddb_timeline.py">telemetry/web_perf/metrics/indexeddb_timeline.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2015&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.web_perf.metrics.timeline_based_metric.html">telemetry.web_perf.metrics.timeline_based_metric</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.web_perf.metrics.indexeddb_timeline.html#IndexedDBTimelineMetric">IndexedDBTimelineMetric</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="IndexedDBTimelineMetric">class <strong>IndexedDBTimelineMetric</strong></a>(<a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Metrics&nbsp;for&nbsp;IndexedDB&nbsp;operations.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.web_perf.metrics.indexeddb_timeline.html#IndexedDBTimelineMetric">IndexedDBTimelineMetric</a></dd>
+<dd><a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="IndexedDBTimelineMetric-AddResults"><strong>AddResults</strong></a>(self, model, renderer_process, interactions, results)</dt></dl>
+
+<dl><dt><a name="IndexedDBTimelineMetric-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a>:<br>
+<dl><dt><a name="IndexedDBTimelineMetric-AddWholeTraceResults"><strong>AddWholeTraceResults</strong></a>(self, model, results)</dt><dd><tt>Computes&nbsp;and&nbsp;adds&nbsp;metrics&nbsp;corresponding&nbsp;to&nbsp;the&nbsp;entire&nbsp;trace.<br>
+&nbsp;<br>
+Override&nbsp;this&nbsp;method&nbsp;to&nbsp;compute&nbsp;results&nbsp;that&nbsp;correspond&nbsp;to&nbsp;the&nbsp;whole&nbsp;trace.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;model:&nbsp;An&nbsp;instance&nbsp;of&nbsp;telemetry.timeline.model.TimelineModel.<br>
+&nbsp;&nbsp;results:&nbsp;An&nbsp;instance&nbsp;of&nbsp;page.PageTestResults.</tt></dd></dl>
+
+<dl><dt><a name="IndexedDBTimelineMetric-VerifyNonOverlappedRecords"><strong>VerifyNonOverlappedRecords</strong></a>(self, interaction_records)</dt><dd><tt>This&nbsp;raises&nbsp;exceptions&nbsp;if&nbsp;interaction_records&nbsp;contain&nbsp;overlapped&nbsp;ranges.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.layout.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.layout.html
new file mode 100644
index 0000000..470bac7
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.layout.html
@@ -0,0 +1,96 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.web_perf.metrics.layout</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.web_perf.html"><font color="#ffffff">web_perf</font></a>.<a href="telemetry.web_perf.metrics.html"><font color="#ffffff">metrics</font></a>.layout</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/web_perf/metrics/layout.py">telemetry/web_perf/metrics/layout.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.web_perf.metrics.single_event.html">telemetry.web_perf.metrics.single_event</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.web_perf.metrics.single_event.html#_SingleEventMetric">telemetry.web_perf.metrics.single_event._SingleEventMetric</a>(<a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.web_perf.metrics.layout.html#LayoutMetric">LayoutMetric</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="LayoutMetric">class <strong>LayoutMetric</strong></a>(<a href="telemetry.web_perf.metrics.single_event.html#_SingleEventMetric">telemetry.web_perf.metrics.single_event._SingleEventMetric</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Reports&nbsp;directly&nbsp;durations&nbsp;of&nbsp;FrameView::performLayout&nbsp;events.<br>
+&nbsp;<br>
+&nbsp;&nbsp;layout:&nbsp;Durations&nbsp;of&nbsp;FrameView::performLayout&nbsp;events&nbsp;that&nbsp;were&nbsp;caused&nbsp;by&nbsp;and<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;start&nbsp;during&nbsp;user&nbsp;interaction.<br>
+&nbsp;<br>
+Layout&nbsp;happens&nbsp;no&nbsp;more&nbsp;than&nbsp;once&nbsp;per&nbsp;frame,&nbsp;so&nbsp;per-frame-ness&nbsp;is&nbsp;implied.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.web_perf.metrics.layout.html#LayoutMetric">LayoutMetric</a></dd>
+<dd><a href="telemetry.web_perf.metrics.single_event.html#_SingleEventMetric">telemetry.web_perf.metrics.single_event._SingleEventMetric</a></dd>
+<dd><a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="LayoutMetric-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.web_perf.metrics.single_event.html#_SingleEventMetric">telemetry.web_perf.metrics.single_event._SingleEventMetric</a>:<br>
+<dl><dt><a name="LayoutMetric-AddResults"><strong>AddResults</strong></a>(self, _model, renderer_thread, interactions, results)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a>:<br>
+<dl><dt><a name="LayoutMetric-AddWholeTraceResults"><strong>AddWholeTraceResults</strong></a>(self, model, results)</dt><dd><tt>Computes&nbsp;and&nbsp;adds&nbsp;metrics&nbsp;corresponding&nbsp;to&nbsp;the&nbsp;entire&nbsp;trace.<br>
+&nbsp;<br>
+Override&nbsp;this&nbsp;method&nbsp;to&nbsp;compute&nbsp;results&nbsp;that&nbsp;correspond&nbsp;to&nbsp;the&nbsp;whole&nbsp;trace.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;model:&nbsp;An&nbsp;instance&nbsp;of&nbsp;telemetry.timeline.model.TimelineModel.<br>
+&nbsp;&nbsp;results:&nbsp;An&nbsp;instance&nbsp;of&nbsp;page.PageTestResults.</tt></dd></dl>
+
+<dl><dt><a name="LayoutMetric-VerifyNonOverlappedRecords"><strong>VerifyNonOverlappedRecords</strong></a>(self, interaction_records)</dt><dd><tt>This&nbsp;raises&nbsp;exceptions&nbsp;if&nbsp;interaction_records&nbsp;contain&nbsp;overlapped&nbsp;ranges.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>EVENT_NAME</strong> = 'FrameView::performLayout'<br>
+<strong>METRIC_NAME</strong> = 'layout'</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.mainthread_jank_stats.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.mainthread_jank_stats.html
new file mode 100644
index 0000000..9b1d0cc
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.mainthread_jank_stats.html
@@ -0,0 +1,74 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.web_perf.metrics.mainthread_jank_stats</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.web_perf.html"><font color="#ffffff">web_perf</font></a>.<a href="telemetry.web_perf.metrics.html"><font color="#ffffff">metrics</font></a>.mainthread_jank_stats</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/web_perf/metrics/mainthread_jank_stats.py">telemetry/web_perf/metrics/mainthread_jank_stats.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.web_perf.metrics.mainthread_jank_stats.html#MainthreadJankStats">MainthreadJankStats</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="MainthreadJankStats">class <strong>MainthreadJankStats</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Utility&nbsp;class&nbsp;for&nbsp;extracting&nbsp;main&nbsp;thread&nbsp;jank&nbsp;statistics&nbsp;from&nbsp;the&nbsp;timeline<br>
+(or&nbsp;other&nbsp;loggin&nbsp;facilities),&nbsp;and&nbsp;providing&nbsp;them&nbsp;in&nbsp;a&nbsp;common&nbsp;format&nbsp;to<br>
+classes&nbsp;that&nbsp;compute&nbsp;benchmark&nbsp;metrics&nbsp;from&nbsp;this&nbsp;data.<br>
+&nbsp;<br>
+&nbsp;&nbsp;total_big_jank_thread_time&nbsp;is&nbsp;the&nbsp;total&nbsp;thread&nbsp;duration&nbsp;of&nbsp;all&nbsp;top<br>
+&nbsp;&nbsp;slices&nbsp;whose&nbsp;thread&nbsp;time&nbsp;ranges&nbsp;overlapped&nbsp;with&nbsp;any&nbsp;thread&nbsp;time&nbsp;ranges&nbsp;of<br>
+&nbsp;&nbsp;the&nbsp;records&nbsp;and&nbsp;the&nbsp;overlapped&nbsp;thread&nbsp;duration&nbsp;is&nbsp;greater&nbsp;than&nbsp;or&nbsp;equal<br>
+&nbsp;&nbsp;USER_PERCEIVABLE_DELAY_THRESHOLD_MS.<br>
+&nbsp;<br>
+&nbsp;&nbsp;biggest_jank_thread_time&nbsp;is&nbsp;the&nbsp;biggest&nbsp;thread&nbsp;duration&nbsp;of&nbsp;all<br>
+&nbsp;&nbsp;top&nbsp;slices&nbsp;whose&nbsp;thread&nbsp;time&nbsp;ranges&nbsp;overlapped&nbsp;with&nbsp;any&nbsp;of&nbsp;records'&nbsp;thread<br>
+&nbsp;&nbsp;time&nbsp;ranges.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="MainthreadJankStats-__init__"><strong>__init__</strong></a>(self, renderer_thread, interaction_records)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>biggest_jank_thread_time</strong></dt>
+</dl>
+<dl><dt><strong>total_big_jank_thread_time</strong></dt>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>USER_PERCEIVABLE_DELAY_THRESHOLD_MS</strong> = 50</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.memory_timeline.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.memory_timeline.html
new file mode 100644
index 0000000..a1d2520
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.memory_timeline.html
@@ -0,0 +1,91 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.web_perf.metrics.memory_timeline</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.web_perf.html"><font color="#ffffff">web_perf</font></a>.<a href="telemetry.web_perf.metrics.html"><font color="#ffffff">metrics</font></a>.memory_timeline</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/web_perf/metrics/memory_timeline.py">telemetry/web_perf/metrics/memory_timeline.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2015&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="collections.html">collections</a><br>
+<a href="telemetry.value.improvement_direction.html">telemetry.value.improvement_direction</a><br>
+</td><td width="25%" valign=top><a href="telemetry.value.list_of_scalar_values.html">telemetry.value.list_of_scalar_values</a><br>
+<a href="telemetry.timeline.memory_dump_event.html">telemetry.timeline.memory_dump_event</a><br>
+</td><td width="25%" valign=top><a href="telemetry.web_perf.metrics.timeline_based_metric.html">telemetry.web_perf.metrics.timeline_based_metric</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.web_perf.metrics.memory_timeline.html#MemoryTimelineMetric">MemoryTimelineMetric</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="MemoryTimelineMetric">class <strong>MemoryTimelineMetric</strong></a>(<a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt><a href="#MemoryTimelineMetric">MemoryTimelineMetric</a>&nbsp;reports&nbsp;summary&nbsp;stats&nbsp;from&nbsp;memory&nbsp;dump&nbsp;events.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.web_perf.metrics.memory_timeline.html#MemoryTimelineMetric">MemoryTimelineMetric</a></dd>
+<dd><a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="MemoryTimelineMetric-AddResults"><strong>AddResults</strong></a>(self, model, _renderer_thread, interactions, results)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a>:<br>
+<dl><dt><a name="MemoryTimelineMetric-AddWholeTraceResults"><strong>AddWholeTraceResults</strong></a>(self, model, results)</dt><dd><tt>Computes&nbsp;and&nbsp;adds&nbsp;metrics&nbsp;corresponding&nbsp;to&nbsp;the&nbsp;entire&nbsp;trace.<br>
+&nbsp;<br>
+Override&nbsp;this&nbsp;method&nbsp;to&nbsp;compute&nbsp;results&nbsp;that&nbsp;correspond&nbsp;to&nbsp;the&nbsp;whole&nbsp;trace.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;model:&nbsp;An&nbsp;instance&nbsp;of&nbsp;telemetry.timeline.model.TimelineModel.<br>
+&nbsp;&nbsp;results:&nbsp;An&nbsp;instance&nbsp;of&nbsp;page.PageTestResults.</tt></dd></dl>
+
+<dl><dt><a name="MemoryTimelineMetric-VerifyNonOverlappedRecords"><strong>VerifyNonOverlappedRecords</strong></a>(self, interaction_records)</dt><dd><tt>This&nbsp;raises&nbsp;exceptions&nbsp;if&nbsp;interaction_records&nbsp;contain&nbsp;overlapped&nbsp;ranges.</tt></dd></dl>
+
+<dl><dt><a name="MemoryTimelineMetric-__init__"><strong>__init__</strong></a>(self)</dt><dd><tt>Computes&nbsp;metrics&nbsp;from&nbsp;a&nbsp;telemetry.timeline&nbsp;Model&nbsp;and&nbsp;a&nbsp;range</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>DEFAULT_METRICS</strong> = ['mmaps_ashmem', 'mmaps_private_dirty', 'mmaps_overall_pss', 'mmaps_native_heap', 'mmaps_java_heap']</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.rendering_frame.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.rendering_frame.html
new file mode 100644
index 0000000..f3e3688
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.rendering_frame.html
@@ -0,0 +1,206 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.web_perf.metrics.rendering_frame</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.web_perf.html"><font color="#ffffff">web_perf</font></a>.<a href="telemetry.web_perf.metrics.html"><font color="#ffffff">metrics</font></a>.rendering_frame</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/web_perf/metrics/rendering_frame.py">telemetry/web_perf/metrics/rendering_frame.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.timeline.bounds.html">telemetry.timeline.bounds</a><br>
+</td><td width="25%" valign=top><a href="telemetry.timeline.slice.html">telemetry.timeline.slice</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.web_perf.metrics.rendering_frame.html#RenderingFrame">RenderingFrame</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="exceptions.html#Exception">exceptions.Exception</a>(<a href="exceptions.html#BaseException">exceptions.BaseException</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.web_perf.metrics.rendering_frame.html#MissingData">MissingData</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.web_perf.metrics.rendering_frame.html#NoBeginFrameIdException">NoBeginFrameIdException</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="MissingData">class <strong>MissingData</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.web_perf.metrics.rendering_frame.html#MissingData">MissingData</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="MissingData-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#MissingData-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#MissingData-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="MissingData-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#MissingData-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="MissingData-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#MissingData-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="MissingData-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#MissingData-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="MissingData-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#MissingData-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="MissingData-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="MissingData-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#MissingData-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="MissingData-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#MissingData-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="MissingData-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="MissingData-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#MissingData-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="MissingData-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="NoBeginFrameIdException">class <strong>NoBeginFrameIdException</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.web_perf.metrics.rendering_frame.html#NoBeginFrameIdException">NoBeginFrameIdException</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="NoBeginFrameIdException-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#NoBeginFrameIdException-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#NoBeginFrameIdException-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="NoBeginFrameIdException-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#NoBeginFrameIdException-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="NoBeginFrameIdException-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#NoBeginFrameIdException-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="NoBeginFrameIdException-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#NoBeginFrameIdException-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="NoBeginFrameIdException-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#NoBeginFrameIdException-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="NoBeginFrameIdException-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="NoBeginFrameIdException-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#NoBeginFrameIdException-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="NoBeginFrameIdException-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#NoBeginFrameIdException-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="NoBeginFrameIdException-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="NoBeginFrameIdException-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#NoBeginFrameIdException-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="NoBeginFrameIdException-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="RenderingFrame">class <strong>RenderingFrame</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Object&nbsp;with&nbsp;information&nbsp;about&nbsp;the&nbsp;triggering&nbsp;of&nbsp;a&nbsp;BeginMainFrame&nbsp;event.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="RenderingFrame-__init__"><strong>__init__</strong></a>(self, events)</dt></dl>
+
+<hr>
+Static methods defined here:<br>
+<dl><dt><a name="RenderingFrame-IsEventUseful"><strong>IsEventUseful</strong></a>(event)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>bounds</strong></dt>
+</dl>
+<dl><dt><strong>queueing_duration</strong></dt>
+</dl>
+<hr>
+Data and other attributes defined here:<br>
+<dl><dt><strong>begin_main_frame_event</strong> = 'ThreadProxy::BeginMainFrame'</dl>
+
+<dl><dt><strong>send_begin_frame_event</strong> = 'ThreadProxy::ScheduledActionSendBeginMainFrame'</dl>
+
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-GetFrameEventsInsideRange"><strong>GetFrameEventsInsideRange</strong></a>(renderer_process, timeline_range)</dt><dd><tt>Returns&nbsp;RenderingFrames&nbsp;for&nbsp;all&nbsp;relevant&nbsp;events&nbsp;in&nbsp;the&nbsp;timeline_range.</tt></dd></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.rendering_stats.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.rendering_stats.html
new file mode 100644
index 0000000..721b672
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.rendering_stats.html
@@ -0,0 +1,120 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.web_perf.metrics.rendering_stats</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.web_perf.html"><font color="#ffffff">web_perf</font></a>.<a href="telemetry.web_perf.metrics.html"><font color="#ffffff">metrics</font></a>.rendering_stats</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/web_perf/metrics/rendering_stats.py">telemetry/web_perf/metrics/rendering_stats.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="itertools.html">itertools</a><br>
+</td><td width="25%" valign=top><a href="telemetry.web_perf.metrics.rendering_frame.html">telemetry.web_perf.metrics.rendering_frame</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.web_perf.metrics.rendering_stats.html#RenderingStats">RenderingStats</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="RenderingStats">class <strong>RenderingStats</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="RenderingStats-__init__"><strong>__init__</strong></a>(self, renderer_process, browser_process, surface_flinger_process, timeline_ranges)</dt><dd><tt>Utility&nbsp;class&nbsp;for&nbsp;extracting&nbsp;rendering&nbsp;statistics&nbsp;from&nbsp;the&nbsp;timeline&nbsp;(or<br>
+other&nbsp;loggin&nbsp;facilities),&nbsp;and&nbsp;providing&nbsp;them&nbsp;in&nbsp;a&nbsp;common&nbsp;format&nbsp;to&nbsp;classes<br>
+that&nbsp;compute&nbsp;benchmark&nbsp;metrics&nbsp;from&nbsp;this&nbsp;data.<br>
+&nbsp;<br>
+Stats&nbsp;are&nbsp;lists&nbsp;of&nbsp;lists&nbsp;of&nbsp;numbers.&nbsp;The&nbsp;outer&nbsp;list&nbsp;stores&nbsp;one&nbsp;list&nbsp;per<br>
+timeline&nbsp;range.<br>
+&nbsp;<br>
+All&nbsp;*_time&nbsp;values&nbsp;are&nbsp;measured&nbsp;in&nbsp;milliseconds.</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-ComputeEventLatencies"><strong>ComputeEventLatencies</strong></a>(input_events)</dt><dd><tt>Compute&nbsp;input&nbsp;event&nbsp;latencies.<br>
+&nbsp;<br>
+Input&nbsp;event&nbsp;latency&nbsp;is&nbsp;the&nbsp;time&nbsp;from&nbsp;when&nbsp;the&nbsp;input&nbsp;event&nbsp;is&nbsp;created&nbsp;to<br>
+when&nbsp;its&nbsp;resulted&nbsp;page&nbsp;is&nbsp;swap&nbsp;buffered.<br>
+Input&nbsp;event&nbsp;on&nbsp;differnt&nbsp;platforms&nbsp;uses&nbsp;different&nbsp;LatencyInfo&nbsp;component&nbsp;to<br>
+record&nbsp;its&nbsp;creation&nbsp;timestamp.&nbsp;We&nbsp;go&nbsp;through&nbsp;the&nbsp;following&nbsp;component&nbsp;list<br>
+to&nbsp;find&nbsp;the&nbsp;creation&nbsp;timestamp:<br>
+1.&nbsp;INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT&nbsp;--&nbsp;when&nbsp;event&nbsp;is&nbsp;created&nbsp;in&nbsp;OS<br>
+2.&nbsp;INPUT_EVENT_LATENCY_UI_COMPONENT&nbsp;--&nbsp;when&nbsp;event&nbsp;reaches&nbsp;Chrome<br>
+3.&nbsp;INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT&nbsp;--&nbsp;when&nbsp;event&nbsp;reaches&nbsp;RenderWidget<br>
+&nbsp;<br>
+If&nbsp;the&nbsp;latency&nbsp;starts&nbsp;with&nbsp;a<br>
+LATENCY_BEGIN_SCROLL_UPDATE_MAIN_COMPONENT&nbsp;component,&nbsp;then&nbsp;it&nbsp;is<br>
+classified&nbsp;as&nbsp;a&nbsp;scroll&nbsp;update&nbsp;instead&nbsp;of&nbsp;a&nbsp;normal&nbsp;input&nbsp;latency&nbsp;measure.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;A&nbsp;list&nbsp;sorted&nbsp;by&nbsp;increasing&nbsp;start&nbsp;time&nbsp;of&nbsp;latencies&nbsp;which&nbsp;are&nbsp;tuples&nbsp;of<br>
+&nbsp;&nbsp;(input_event_name,&nbsp;latency_in_ms).</tt></dd></dl>
+ <dl><dt><a name="-GetLatencyEvents"><strong>GetLatencyEvents</strong></a>(process, timeline_range)</dt><dd><tt>Get&nbsp;LatencyInfo&nbsp;trace&nbsp;events&nbsp;from&nbsp;the&nbsp;process's&nbsp;trace&nbsp;buffer&nbsp;that&nbsp;are<br>
+&nbsp;&nbsp;&nbsp;within&nbsp;the&nbsp;timeline_range.<br>
+&nbsp;<br>
+Input&nbsp;events&nbsp;dump&nbsp;their&nbsp;LatencyInfo&nbsp;into&nbsp;trace&nbsp;buffer&nbsp;as&nbsp;async&nbsp;trace&nbsp;event<br>
+of&nbsp;name&nbsp;starting&nbsp;with&nbsp;"InputLatency".&nbsp;Non-input&nbsp;events&nbsp;with&nbsp;name&nbsp;starting<br>
+with&nbsp;"Latency".&nbsp;The&nbsp;trace&nbsp;event&nbsp;has&nbsp;a&nbsp;memeber&nbsp;'data'&nbsp;containing&nbsp;its&nbsp;latency<br>
+history.</tt></dd></dl>
+ <dl><dt><a name="-GetTimestampEventName"><strong>GetTimestampEventName</strong></a>(process)</dt><dd><tt>Returns&nbsp;the&nbsp;name&nbsp;of&nbsp;the&nbsp;events&nbsp;used&nbsp;to&nbsp;count&nbsp;frame&nbsp;timestamps.</tt></dd></dl>
+ <dl><dt><a name="-HasRenderingStats"><strong>HasRenderingStats</strong></a>(process)</dt><dd><tt>Returns&nbsp;True&nbsp;if&nbsp;the&nbsp;process&nbsp;contains&nbsp;at&nbsp;least&nbsp;one<br>
+BenchmarkInstrumentation::*<a href="#RenderingStats">RenderingStats</a>&nbsp;event&nbsp;with&nbsp;a&nbsp;frame.</tt></dd></dl>
+</td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>APPROXIMATED_PIXEL_ERROR</strong> = 'approximated_pixel_percentages'<br>
+<strong>APPROXIMATED_VISIBLE_CONTENT_DATA</strong> = 'approximated_visible_content_area'<br>
+<strong>BEGIN_COMP_NAME</strong> = 'INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT'<br>
+<strong>BEGIN_SCROLL_UPDATE_COMP_NAME</strong> = 'LATENCY_BEGIN_SCROLL_LISTENER_UPDATE_MAIN_COMPONENT'<br>
+<strong>CHECKERBOARDED_PIXEL_ERROR</strong> = 'checkerboarded_pixel_percentages'<br>
+<strong>CHECKERBOARDED_VISIBLE_CONTENT_DATA</strong> = 'checkerboarded_visible_content_area'<br>
+<strong>END_COMP_NAME</strong> = 'INPUT_EVENT_GPU_SWAP_BUFFER_COMPONENT'<br>
+<strong>FORWARD_SCROLL_UPDATE_COMP_NAME</strong> = 'INPUT_EVENT_LATENCY_FORWARD_SCROLL_UPDATE_TO_MAIN_COMPONENT'<br>
+<strong>GESTURE_SCROLL_UPDATE_EVENT_NAME</strong> = 'InputLatency::GestureScrollUpdate'<br>
+<strong>MAIN_THREAD_SCROLL_UPDATE_EVENT_NAME</strong> = 'Latency::ScrollUpdate'<br>
+<strong>ORIGINAL_COMP_NAME</strong> = 'INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT'<br>
+<strong>UI_COMP_NAME</strong> = 'INPUT_EVENT_LATENCY_UI_COMPONENT'<br>
+<strong>VISIBLE_CONTENT_DATA</strong> = 'visible_content_area'</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.responsiveness_metric.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.responsiveness_metric.html
new file mode 100644
index 0000000..0d8d702
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.responsiveness_metric.html
@@ -0,0 +1,96 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.web_perf.metrics.responsiveness_metric</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.web_perf.html"><font color="#ffffff">web_perf</font></a>.<a href="telemetry.web_perf.metrics.html"><font color="#ffffff">metrics</font></a>.responsiveness_metric</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/web_perf/metrics/responsiveness_metric.py">telemetry/web_perf/metrics/responsiveness_metric.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.value.improvement_direction.html">telemetry.value.improvement_direction</a><br>
+<a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top><a href="telemetry.web_perf.metrics.mainthread_jank_stats.html">telemetry.web_perf.metrics.mainthread_jank_stats</a><br>
+<a href="telemetry.value.scalar.html">telemetry.value.scalar</a><br>
+</td><td width="25%" valign=top><a href="telemetry.web_perf.metrics.timeline_based_metric.html">telemetry.web_perf.metrics.timeline_based_metric</a><br>
+<a href="telemetry.web_perf.timeline_interaction_record.html">telemetry.web_perf.timeline_interaction_record</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.web_perf.metrics.responsiveness_metric.html#ResponsivenessMetric">ResponsivenessMetric</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ResponsivenessMetric">class <strong>ResponsivenessMetric</strong></a>(<a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Computes&nbsp;metrics&nbsp;that&nbsp;measure&nbsp;respsonsiveness&nbsp;on&nbsp;the&nbsp;record&nbsp;ranges.<br>
+&nbsp;<br>
+&nbsp;total_big_jank_thread_time&nbsp;is&nbsp;the&nbsp;total&nbsp;thread&nbsp;duration&nbsp;of&nbsp;all&nbsp;top<br>
+&nbsp;slices&nbsp;whose&nbsp;thread&nbsp;time&nbsp;ranges&nbsp;overlapped&nbsp;with&nbsp;any&nbsp;thread&nbsp;time&nbsp;ranges&nbsp;of<br>
+&nbsp;the&nbsp;records&nbsp;and&nbsp;the&nbsp;overlapped&nbsp;thread&nbsp;duration&nbsp;is&nbsp;greater&nbsp;than&nbsp;or&nbsp;equal<br>
+&nbsp;USER_PERCEIVABLE_DELAY_THRESHOLD_MS.<br>
+&nbsp;<br>
+&nbsp;biggest_jank_thread_time&nbsp;is&nbsp;the&nbsp;biggest&nbsp;thread&nbsp;duration&nbsp;of&nbsp;all<br>
+&nbsp;top&nbsp;slices&nbsp;whose&nbsp;thread&nbsp;time&nbsp;ranges&nbsp;overlapped&nbsp;with&nbsp;any&nbsp;of&nbsp;records'&nbsp;thread<br>
+&nbsp;time&nbsp;ranges.<br>
+&nbsp;<br>
+All&nbsp;*_time&nbsp;values&nbsp;are&nbsp;measured&nbsp;in&nbsp;milliseconds.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.web_perf.metrics.responsiveness_metric.html#ResponsivenessMetric">ResponsivenessMetric</a></dd>
+<dd><a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="ResponsivenessMetric-AddResults"><strong>AddResults</strong></a>(self, _, renderer_thread, interaction_records, results)</dt></dl>
+
+<dl><dt><a name="ResponsivenessMetric-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a>:<br>
+<dl><dt><a name="ResponsivenessMetric-AddWholeTraceResults"><strong>AddWholeTraceResults</strong></a>(self, model, results)</dt><dd><tt>Computes&nbsp;and&nbsp;adds&nbsp;metrics&nbsp;corresponding&nbsp;to&nbsp;the&nbsp;entire&nbsp;trace.<br>
+&nbsp;<br>
+Override&nbsp;this&nbsp;method&nbsp;to&nbsp;compute&nbsp;results&nbsp;that&nbsp;correspond&nbsp;to&nbsp;the&nbsp;whole&nbsp;trace.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;model:&nbsp;An&nbsp;instance&nbsp;of&nbsp;telemetry.timeline.model.TimelineModel.<br>
+&nbsp;&nbsp;results:&nbsp;An&nbsp;instance&nbsp;of&nbsp;page.PageTestResults.</tt></dd></dl>
+
+<dl><dt><a name="ResponsivenessMetric-VerifyNonOverlappedRecords"><strong>VerifyNonOverlappedRecords</strong></a>(self, interaction_records)</dt><dd><tt>This&nbsp;raises&nbsp;exceptions&nbsp;if&nbsp;interaction_records&nbsp;contain&nbsp;overlapped&nbsp;ranges.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.single_event.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.single_event.html
new file mode 100644
index 0000000..7f4e43b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.single_event.html
@@ -0,0 +1,27 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.web_perf.metrics.single_event</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.web_perf.html"><font color="#ffffff">web_perf</font></a>.<a href="telemetry.web_perf.metrics.html"><font color="#ffffff">metrics</font></a>.single_event</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/web_perf/metrics/single_event.py">telemetry/web_perf/metrics/single_event.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.value.improvement_direction.html">telemetry.value.improvement_direction</a><br>
+</td><td width="25%" valign=top><a href="telemetry.value.list_of_scalar_values.html">telemetry.value.list_of_scalar_values</a><br>
+</td><td width="25%" valign=top><a href="telemetry.web_perf.metrics.timeline_based_metric.html">telemetry.web_perf.metrics.timeline_based_metric</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.smoothness.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.smoothness.html
new file mode 100644
index 0000000..5081ad7
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.smoothness.html
@@ -0,0 +1,112 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.web_perf.metrics.smoothness</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.web_perf.html"><font color="#ffffff">web_perf</font></a>.<a href="telemetry.web_perf.metrics.html"><font color="#ffffff">metrics</font></a>.smoothness</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/web_perf/metrics/smoothness.py">telemetry/web_perf/metrics/smoothness.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.value.improvement_direction.html">telemetry.value.improvement_direction</a><br>
+<a href="telemetry.value.list_of_scalar_values.html">telemetry.value.list_of_scalar_values</a><br>
+</td><td width="25%" valign=top><a href="logging.html">logging</a><br>
+<a href="telemetry.util.perf_tests_helper.html">telemetry.util.perf_tests_helper</a><br>
+</td><td width="25%" valign=top><a href="telemetry.web_perf.metrics.rendering_stats.html">telemetry.web_perf.metrics.rendering_stats</a><br>
+<a href="telemetry.value.scalar.html">telemetry.value.scalar</a><br>
+</td><td width="25%" valign=top><a href="telemetry.util.statistics.html">telemetry.util.statistics</a><br>
+<a href="telemetry.web_perf.metrics.timeline_based_metric.html">telemetry.web_perf.metrics.timeline_based_metric</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.web_perf.metrics.smoothness.html#SmoothnessMetric">SmoothnessMetric</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="SmoothnessMetric">class <strong>SmoothnessMetric</strong></a>(<a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Computes&nbsp;metrics&nbsp;that&nbsp;measure&nbsp;smoothness&nbsp;of&nbsp;animations&nbsp;over&nbsp;given&nbsp;ranges.<br>
+&nbsp;<br>
+Animations&nbsp;are&nbsp;typically&nbsp;considered&nbsp;smooth&nbsp;if&nbsp;the&nbsp;frame&nbsp;rates&nbsp;are&nbsp;close&nbsp;to<br>
+60&nbsp;frames&nbsp;per&nbsp;second&nbsp;(fps)&nbsp;and&nbsp;uniformly&nbsp;distributed&nbsp;over&nbsp;the&nbsp;sequence.&nbsp;To<br>
+determine&nbsp;if&nbsp;a&nbsp;timeline&nbsp;range&nbsp;contains&nbsp;a&nbsp;smooth&nbsp;animation,&nbsp;we&nbsp;update&nbsp;the<br>
+results&nbsp;object&nbsp;with&nbsp;several&nbsp;representative&nbsp;metrics:<br>
+&nbsp;<br>
+&nbsp;&nbsp;frame_times:&nbsp;A&nbsp;list&nbsp;of&nbsp;raw&nbsp;frame&nbsp;times<br>
+&nbsp;&nbsp;mean_frame_time:&nbsp;The&nbsp;arithmetic&nbsp;mean&nbsp;of&nbsp;frame&nbsp;times<br>
+&nbsp;&nbsp;percentage_smooth:&nbsp;Percentage&nbsp;of&nbsp;frames&nbsp;that&nbsp;were&nbsp;hitting&nbsp;60&nbsp;FPS.<br>
+&nbsp;&nbsp;frame_time_discrepancy:&nbsp;The&nbsp;absolute&nbsp;discrepancy&nbsp;of&nbsp;frame&nbsp;timestamps<br>
+&nbsp;&nbsp;mean_pixels_approximated:&nbsp;The&nbsp;mean&nbsp;percentage&nbsp;of&nbsp;pixels&nbsp;approximated<br>
+&nbsp;&nbsp;queueing_durations:&nbsp;The&nbsp;queueing&nbsp;delay&nbsp;between&nbsp;compositor&nbsp;&amp;&nbsp;main&nbsp;threads<br>
+&nbsp;<br>
+Note&nbsp;that&nbsp;if&nbsp;any&nbsp;of&nbsp;the&nbsp;interaction&nbsp;records&nbsp;provided&nbsp;to&nbsp;AddResults&nbsp;have&nbsp;less<br>
+than&nbsp;2&nbsp;frames,&nbsp;we&nbsp;will&nbsp;return&nbsp;telemetry&nbsp;values&nbsp;with&nbsp;None&nbsp;values&nbsp;for&nbsp;each&nbsp;of<br>
+the&nbsp;smoothness&nbsp;metrics.&nbsp;Similarly,&nbsp;older&nbsp;browsers&nbsp;without&nbsp;support&nbsp;for<br>
+tracking&nbsp;the&nbsp;BeginMainFrame&nbsp;events&nbsp;will&nbsp;report&nbsp;a&nbsp;ListOfScalarValues&nbsp;with&nbsp;a<br>
+None&nbsp;value&nbsp;for&nbsp;the&nbsp;queueing&nbsp;duration&nbsp;metric.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.web_perf.metrics.smoothness.html#SmoothnessMetric">SmoothnessMetric</a></dd>
+<dd><a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="SmoothnessMetric-AddResults"><strong>AddResults</strong></a>(self, model, renderer_thread, interaction_records, results)</dt></dl>
+
+<dl><dt><a name="SmoothnessMetric-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a>:<br>
+<dl><dt><a name="SmoothnessMetric-AddWholeTraceResults"><strong>AddWholeTraceResults</strong></a>(self, model, results)</dt><dd><tt>Computes&nbsp;and&nbsp;adds&nbsp;metrics&nbsp;corresponding&nbsp;to&nbsp;the&nbsp;entire&nbsp;trace.<br>
+&nbsp;<br>
+Override&nbsp;this&nbsp;method&nbsp;to&nbsp;compute&nbsp;results&nbsp;that&nbsp;correspond&nbsp;to&nbsp;the&nbsp;whole&nbsp;trace.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;model:&nbsp;An&nbsp;instance&nbsp;of&nbsp;telemetry.timeline.model.TimelineModel.<br>
+&nbsp;&nbsp;results:&nbsp;An&nbsp;instance&nbsp;of&nbsp;page.PageTestResults.</tt></dd></dl>
+
+<dl><dt><a name="SmoothnessMetric-VerifyNonOverlappedRecords"><strong>VerifyNonOverlappedRecords</strong></a>(self, interaction_records)</dt><dd><tt>This&nbsp;raises&nbsp;exceptions&nbsp;if&nbsp;interaction_records&nbsp;contain&nbsp;overlapped&nbsp;ranges.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>NOT_ENOUGH_FRAMES_MESSAGE</strong> = "Not enough frames for smoothness metrics (at lea...der extremely slow<font color="#c040c0">\n</font>- Pages that can't be scrolled"</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.text_selection.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.text_selection.html
new file mode 100644
index 0000000..9418ac5
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.text_selection.html
@@ -0,0 +1,92 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.web_perf.metrics.text_selection</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.web_perf.html"><font color="#ffffff">web_perf</font></a>.<a href="telemetry.web_perf.metrics.html"><font color="#ffffff">metrics</font></a>.text_selection</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/web_perf/metrics/text_selection.py">telemetry/web_perf/metrics/text_selection.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.web_perf.metrics.single_event.html">telemetry.web_perf.metrics.single_event</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.web_perf.metrics.single_event.html#_SingleEventMetric">telemetry.web_perf.metrics.single_event._SingleEventMetric</a>(<a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.web_perf.metrics.text_selection.html#TextSelectionMetric">TextSelectionMetric</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TextSelectionMetric">class <strong>TextSelectionMetric</strong></a>(<a href="telemetry.web_perf.metrics.single_event.html#_SingleEventMetric">telemetry.web_perf.metrics.single_event._SingleEventMetric</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Reports&nbsp;directly&nbsp;durations&nbsp;of&nbsp;WebLocalFrameImpl::moveRangeSelectionExtent<br>
+events&nbsp;associated&nbsp;with&nbsp;moving&nbsp;a&nbsp;selection&nbsp;extent.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.web_perf.metrics.text_selection.html#TextSelectionMetric">TextSelectionMetric</a></dd>
+<dd><a href="telemetry.web_perf.metrics.single_event.html#_SingleEventMetric">telemetry.web_perf.metrics.single_event._SingleEventMetric</a></dd>
+<dd><a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="TextSelectionMetric-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.web_perf.metrics.single_event.html#_SingleEventMetric">telemetry.web_perf.metrics.single_event._SingleEventMetric</a>:<br>
+<dl><dt><a name="TextSelectionMetric-AddResults"><strong>AddResults</strong></a>(self, _model, renderer_thread, interactions, results)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a>:<br>
+<dl><dt><a name="TextSelectionMetric-AddWholeTraceResults"><strong>AddWholeTraceResults</strong></a>(self, model, results)</dt><dd><tt>Computes&nbsp;and&nbsp;adds&nbsp;metrics&nbsp;corresponding&nbsp;to&nbsp;the&nbsp;entire&nbsp;trace.<br>
+&nbsp;<br>
+Override&nbsp;this&nbsp;method&nbsp;to&nbsp;compute&nbsp;results&nbsp;that&nbsp;correspond&nbsp;to&nbsp;the&nbsp;whole&nbsp;trace.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;model:&nbsp;An&nbsp;instance&nbsp;of&nbsp;telemetry.timeline.model.TimelineModel.<br>
+&nbsp;&nbsp;results:&nbsp;An&nbsp;instance&nbsp;of&nbsp;page.PageTestResults.</tt></dd></dl>
+
+<dl><dt><a name="TextSelectionMetric-VerifyNonOverlappedRecords"><strong>VerifyNonOverlappedRecords</strong></a>(self, interaction_records)</dt><dd><tt>This&nbsp;raises&nbsp;exceptions&nbsp;if&nbsp;interaction_records&nbsp;contain&nbsp;overlapped&nbsp;ranges.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>EVENT_NAME</strong> = 'WebLocalFrameImpl::moveRangeSelectionExtent'<br>
+<strong>METRIC_NAME</strong> = 'text-selection'</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.timeline_based_metric.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.timeline_based_metric.html
new file mode 100644
index 0000000..c0f1ed6
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.timeline_based_metric.html
@@ -0,0 +1,157 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.web_perf.metrics.timeline_based_metric</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.web_perf.html"><font color="#ffffff">web_perf</font></a>.<a href="telemetry.web_perf.metrics.html"><font color="#ffffff">metrics</font></a>.timeline_based_metric</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/web_perf/metrics/timeline_based_metric.py">telemetry/web_perf/metrics/timeline_based_metric.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">TimelineBasedMetric</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="exceptions.html#Exception">exceptions.Exception</a>(<a href="exceptions.html#BaseException">exceptions.BaseException</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetricException">TimelineBasedMetricException</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TimelineBasedMetric">class <strong>TimelineBasedMetric</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="TimelineBasedMetric-AddResults"><strong>AddResults</strong></a>(self, model, renderer_thread, interaction_records, results)</dt><dd><tt>Computes&nbsp;and&nbsp;adds&nbsp;metrics&nbsp;for&nbsp;the&nbsp;interaction_records'&nbsp;time&nbsp;ranges.<br>
+&nbsp;<br>
+The&nbsp;override&nbsp;of&nbsp;this&nbsp;method&nbsp;should&nbsp;compute&nbsp;results&nbsp;on&nbsp;the&nbsp;data&nbsp;**only**<br>
+within&nbsp;the&nbsp;interaction_records'&nbsp;start&nbsp;and&nbsp;end&nbsp;time&nbsp;ranges.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;model:&nbsp;An&nbsp;instance&nbsp;of&nbsp;telemetry.timeline.model.TimelineModel.<br>
+&nbsp;&nbsp;interaction_records:&nbsp;A&nbsp;list&nbsp;of&nbsp;instances&nbsp;of&nbsp;TimelineInteractionRecord.&nbsp;If<br>
+&nbsp;&nbsp;&nbsp;&nbsp;the&nbsp;override&nbsp;of&nbsp;this&nbsp;method&nbsp;doesn't&nbsp;support&nbsp;overlapped&nbsp;ranges,&nbsp;use<br>
+&nbsp;&nbsp;&nbsp;&nbsp;VerifyNonOverlappedRecords&nbsp;to&nbsp;check&nbsp;that&nbsp;no&nbsp;records&nbsp;are&nbsp;overlapped.<br>
+&nbsp;&nbsp;results:&nbsp;An&nbsp;instance&nbsp;of&nbsp;page.PageTestResults.</tt></dd></dl>
+
+<dl><dt><a name="TimelineBasedMetric-AddWholeTraceResults"><strong>AddWholeTraceResults</strong></a>(self, model, results)</dt><dd><tt>Computes&nbsp;and&nbsp;adds&nbsp;metrics&nbsp;corresponding&nbsp;to&nbsp;the&nbsp;entire&nbsp;trace.<br>
+&nbsp;<br>
+Override&nbsp;this&nbsp;method&nbsp;to&nbsp;compute&nbsp;results&nbsp;that&nbsp;correspond&nbsp;to&nbsp;the&nbsp;whole&nbsp;trace.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;model:&nbsp;An&nbsp;instance&nbsp;of&nbsp;telemetry.timeline.model.TimelineModel.<br>
+&nbsp;&nbsp;results:&nbsp;An&nbsp;instance&nbsp;of&nbsp;page.PageTestResults.</tt></dd></dl>
+
+<dl><dt><a name="TimelineBasedMetric-VerifyNonOverlappedRecords"><strong>VerifyNonOverlappedRecords</strong></a>(self, interaction_records)</dt><dd><tt>This&nbsp;raises&nbsp;exceptions&nbsp;if&nbsp;interaction_records&nbsp;contain&nbsp;overlapped&nbsp;ranges.</tt></dd></dl>
+
+<dl><dt><a name="TimelineBasedMetric-__init__"><strong>__init__</strong></a>(self)</dt><dd><tt>Computes&nbsp;metrics&nbsp;from&nbsp;a&nbsp;telemetry.timeline&nbsp;Model&nbsp;and&nbsp;a&nbsp;range</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TimelineBasedMetricException">class <strong>TimelineBasedMetricException</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt><a href="exceptions.html#Exception">Exception</a>&nbsp;that&nbsp;can&nbsp;be&nbsp;thrown&nbsp;from&nbsp;metrics&nbsp;that&nbsp;implements<br>
+<a href="#TimelineBasedMetric">TimelineBasedMetric</a>&nbsp;to&nbsp;indicate&nbsp;a&nbsp;problem&nbsp;arised&nbsp;when&nbsp;computing&nbsp;the&nbsp;metric.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetricException">TimelineBasedMetricException</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="TimelineBasedMetricException-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#TimelineBasedMetricException-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#TimelineBasedMetricException-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="TimelineBasedMetricException-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#TimelineBasedMetricException-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="TimelineBasedMetricException-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#TimelineBasedMetricException-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="TimelineBasedMetricException-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#TimelineBasedMetricException-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="TimelineBasedMetricException-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#TimelineBasedMetricException-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="TimelineBasedMetricException-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="TimelineBasedMetricException-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#TimelineBasedMetricException-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="TimelineBasedMetricException-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#TimelineBasedMetricException-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="TimelineBasedMetricException-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="TimelineBasedMetricException-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#TimelineBasedMetricException-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="TimelineBasedMetricException-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-IsEventInInteractions"><strong>IsEventInInteractions</strong></a>(event, interaction_records)</dt><dd><tt>Return&nbsp;True&nbsp;if&nbsp;event&nbsp;is&nbsp;in&nbsp;any&nbsp;of&nbsp;the&nbsp;interaction&nbsp;records'&nbsp;time&nbsp;range.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;event:&nbsp;an&nbsp;instance&nbsp;of&nbsp;telemetry.timeline.event.TimelineEvent.<br>
+&nbsp;&nbsp;interaction_records:&nbsp;a&nbsp;list&nbsp;of&nbsp;interaction&nbsp;records,&nbsp;whereas&nbsp;each&nbsp;record&nbsp;is<br>
+&nbsp;&nbsp;&nbsp;&nbsp;an&nbsp;instance&nbsp;of<br>
+&nbsp;&nbsp;&nbsp;&nbsp;telemetry.web_perf.timeline_interaction_record.TimelineInteractionRecord.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;True&nbsp;if&nbsp;|event|'s&nbsp;start&nbsp;&amp;&nbsp;end&nbsp;time&nbsp;is&nbsp;in&nbsp;any&nbsp;of&nbsp;the&nbsp;|interaction_records|'s<br>
+&nbsp;&nbsp;time&nbsp;range.</tt></dd></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.trace_event_stats.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.trace_event_stats.html
new file mode 100644
index 0000000..013c785
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.trace_event_stats.html
@@ -0,0 +1,107 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.web_perf.metrics.trace_event_stats</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.web_perf.html"><font color="#ffffff">web_perf</font></a>.<a href="telemetry.web_perf.metrics.html"><font color="#ffffff">metrics</font></a>.trace_event_stats</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/web_perf/metrics/trace_event_stats.py">telemetry/web_perf/metrics/trace_event_stats.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2015&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="collections.html">collections</a><br>
+</td><td width="25%" valign=top><a href="telemetry.value.list_of_scalar_values.html">telemetry.value.list_of_scalar_values</a><br>
+</td><td width="25%" valign=top><a href="telemetry.value.scalar.html">telemetry.value.scalar</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.web_perf.metrics.trace_event_stats.html#TraceEventStats">TraceEventStats</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.web_perf.metrics.trace_event_stats.html#TraceEventStatsInput">TraceEventStatsInput</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TraceEventStats">class <strong>TraceEventStats</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Reports&nbsp;durations&nbsp;and&nbsp;counts&nbsp;of&nbsp;given&nbsp;trace&nbsp;events.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="TraceEventStats-AddInput"><strong>AddInput</strong></a>(self, trace_event_aggregator_input)</dt></dl>
+
+<dl><dt><a name="TraceEventStats-AddResults"><strong>AddResults</strong></a>(self, model, renderer_process, interactions, results)</dt></dl>
+
+<dl><dt><a name="TraceEventStats-__init__"><strong>__init__</strong></a>(self, trace_event_aggregator_inputs<font color="#909090">=None</font>)</dt></dl>
+
+<hr>
+Static methods defined here:<br>
+<dl><dt><a name="TraceEventStats-ThreadDurationIfPresent"><strong>ThreadDurationIfPresent</strong></a>(event)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TraceEventStatsInput">class <strong>TraceEventStatsInput</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Input&nbsp;for&nbsp;the&nbsp;<a href="#TraceEventStats">TraceEventStats</a>.<br>
+Using&nbsp;this&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;<a href="#TraceEventStats">TraceEventStats</a>&nbsp;will&nbsp;include&nbsp;two&nbsp;metrics,&nbsp;one&nbsp;with&nbsp;a<br>
+list&nbsp;of&nbsp;times&nbsp;of&nbsp;the&nbsp;given&nbsp;event,&nbsp;and&nbsp;one&nbsp;for&nbsp;the&nbsp;count&nbsp;of&nbsp;the&nbsp;events,&nbsp;named<br>
+`metric_name&nbsp;+&nbsp;'-count'`.<br>
+Args:<br>
+&nbsp;&nbsp;event_category:&nbsp;The&nbsp;category&nbsp;of&nbsp;the&nbsp;event&nbsp;to&nbsp;track.<br>
+&nbsp;&nbsp;event_name:&nbsp;The&nbsp;name&nbsp;of&nbsp;the&nbsp;event&nbsp;to&nbsp;track.<br>
+&nbsp;&nbsp;metric_name:&nbsp;The&nbsp;name&nbsp;of&nbsp;the&nbsp;metric&nbsp;name,&nbsp;which&nbsp;accumulates&nbsp;all&nbsp;of&nbsp;the<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;times&nbsp;of&nbsp;the&nbsp;events.<br>
+&nbsp;&nbsp;metric_description:&nbsp;Description&nbsp;of&nbsp;the&nbsp;metric.<br>
+&nbsp;&nbsp;units:&nbsp;Units&nbsp;for&nbsp;the&nbsp;metric.<br>
+&nbsp;&nbsp;process_name:&nbsp;(optional)&nbsp;The&nbsp;name&nbsp;of&nbsp;the&nbsp;process&nbsp;to&nbsp;inspect&nbsp;for&nbsp;the&nbsp;trace<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;events.&nbsp;Defaults&nbsp;to&nbsp;'Renderer'.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="TraceEventStatsInput-__init__"><strong>__init__</strong></a>(self, event_category, event_name, metric_name, metric_description, units, process_name<font color="#909090">='Renderer'</font>)</dt></dl>
+
+<hr>
+Static methods defined here:<br>
+<dl><dt><a name="TraceEventStatsInput-GetEventId"><strong>GetEventId</strong></a>(event_category, event_name)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.v8_gc_latency.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.v8_gc_latency.html
new file mode 100644
index 0000000..15c3257
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.v8_gc_latency.html
@@ -0,0 +1,110 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.web_perf.metrics.v8_gc_latency</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.web_perf.html"><font color="#ffffff">web_perf</font></a>.<a href="telemetry.web_perf.metrics.html"><font color="#ffffff">metrics</font></a>.v8_gc_latency</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/web_perf/metrics/v8_gc_latency.py">telemetry/web_perf/metrics/v8_gc_latency.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2015&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.value.improvement_direction.html">telemetry.value.improvement_direction</a><br>
+<a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top><a href="telemetry.value.scalar.html">telemetry.value.scalar</a><br>
+<a href="telemetry.util.statistics.html">telemetry.util.statistics</a><br>
+</td><td width="25%" valign=top><a href="telemetry.web_perf.metrics.timeline_based_metric.html">telemetry.web_perf.metrics.timeline_based_metric</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.web_perf.metrics.v8_gc_latency.html#V8EventStat">V8EventStat</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.web_perf.metrics.v8_gc_latency.html#V8GCLatency">V8GCLatency</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="V8EventStat">class <strong>V8EventStat</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="V8EventStat-__init__"><strong>__init__</strong></a>(self, src_event_name, result_name, result_description)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>percentage_thread_duration_during_idle</strong></dt>
+</dl>
+<dl><dt><strong>thread_duration_outside_idle</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="V8GCLatency">class <strong>V8GCLatency</strong></a>(<a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.web_perf.metrics.v8_gc_latency.html#V8GCLatency">V8GCLatency</a></dd>
+<dd><a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="V8GCLatency-AddResults"><strong>AddResults</strong></a>(self, model, renderer_thread, interaction_records, results)</dt></dl>
+
+<dl><dt><a name="V8GCLatency-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a>:<br>
+<dl><dt><a name="V8GCLatency-AddWholeTraceResults"><strong>AddWholeTraceResults</strong></a>(self, model, results)</dt><dd><tt>Computes&nbsp;and&nbsp;adds&nbsp;metrics&nbsp;corresponding&nbsp;to&nbsp;the&nbsp;entire&nbsp;trace.<br>
+&nbsp;<br>
+Override&nbsp;this&nbsp;method&nbsp;to&nbsp;compute&nbsp;results&nbsp;that&nbsp;correspond&nbsp;to&nbsp;the&nbsp;whole&nbsp;trace.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;model:&nbsp;An&nbsp;instance&nbsp;of&nbsp;telemetry.timeline.model.TimelineModel.<br>
+&nbsp;&nbsp;results:&nbsp;An&nbsp;instance&nbsp;of&nbsp;page.PageTestResults.</tt></dd></dl>
+
+<dl><dt><a name="V8GCLatency-VerifyNonOverlappedRecords"><strong>VerifyNonOverlappedRecords</strong></a>(self, interaction_records)</dt><dd><tt>This&nbsp;raises&nbsp;exceptions&nbsp;if&nbsp;interaction_records&nbsp;contain&nbsp;overlapped&nbsp;ranges.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.webrtc_rendering_stats.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.webrtc_rendering_stats.html
new file mode 100644
index 0000000..3334d5f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.webrtc_rendering_stats.html
@@ -0,0 +1,98 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.web_perf.metrics.webrtc_rendering_stats</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.web_perf.html"><font color="#ffffff">web_perf</font></a>.<a href="telemetry.web_perf.metrics.html"><font color="#ffffff">metrics</font></a>.webrtc_rendering_stats</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/web_perf/metrics/webrtc_rendering_stats.py">telemetry/web_perf/metrics/webrtc_rendering_stats.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2015&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="logging.html">logging</a><br>
+</td><td width="25%" valign=top><a href="telemetry.util.statistics.html">telemetry.util.statistics</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.web_perf.metrics.webrtc_rendering_stats.html#TimeStats">TimeStats</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.web_perf.metrics.webrtc_rendering_stats.html#WebMediaPlayerMsRenderingStats">WebMediaPlayerMsRenderingStats</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TimeStats">class <strong>TimeStats</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Stats&nbsp;container&nbsp;for&nbsp;webrtc&nbsp;rendering&nbsp;metrics.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="TimeStats-__init__"><strong>__init__</strong></a>(self, drift_time<font color="#909090">=None</font>, mean_drift_time<font color="#909090">=None</font>, std_dev_drift_time<font color="#909090">=None</font>, percent_badly_out_of_sync<font color="#909090">=None</font>, percent_out_of_sync<font color="#909090">=None</font>, smoothness_score<font color="#909090">=None</font>, freezing_score<font color="#909090">=None</font>, rendering_length_error<font color="#909090">=None</font>, fps<font color="#909090">=None</font>, frame_distribution<font color="#909090">=None</font>)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="WebMediaPlayerMsRenderingStats">class <strong>WebMediaPlayerMsRenderingStats</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Analyzes&nbsp;events&nbsp;of&nbsp;WebMediaPlayerMs&nbsp;type.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="WebMediaPlayerMsRenderingStats-GetTimeStats"><strong>GetTimeStats</strong></a>(self)</dt><dd><tt>Calculate&nbsp;time&nbsp;stamp&nbsp;stats&nbsp;for&nbsp;all&nbsp;remote&nbsp;stream&nbsp;events.</tt></dd></dl>
+
+<dl><dt><a name="WebMediaPlayerMsRenderingStats-__init__"><strong>__init__</strong></a>(self, events)</dt><dd><tt>Save&nbsp;relevant&nbsp;events&nbsp;according&nbsp;to&nbsp;their&nbsp;stream.</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>ACTUAL_RENDER_BEGIN</strong> = 'Actual Render Begin'<br>
+<strong>ACTUAL_RENDER_END</strong> = 'Actual Render End'<br>
+<strong>DISPLAY_HERTZ</strong> = 60.0<br>
+<strong>FROZEN_THRESHOLD</strong> = 6<br>
+<strong>IDEAL_RENDER_INSTANT</strong> = 'Ideal Render Instant'<br>
+<strong>SERIAL</strong> = 'Serial'<br>
+<strong>SEVERITY</strong> = 3<br>
+<strong>VSYNC_DURATION</strong> = 16666.666666666668</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.webrtc_rendering_timeline.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.webrtc_rendering_timeline.html
new file mode 100644
index 0000000..d9cdd5d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.metrics.webrtc_rendering_timeline.html
@@ -0,0 +1,104 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.web_perf.metrics.webrtc_rendering_timeline</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.web_perf.html"><font color="#ffffff">web_perf</font></a>.<a href="telemetry.web_perf.metrics.html"><font color="#ffffff">metrics</font></a>.webrtc_rendering_timeline</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/web_perf/metrics/webrtc_rendering_timeline.py">telemetry/web_perf/metrics/webrtc_rendering_timeline.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2015&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.value.improvement_direction.html">telemetry.value.improvement_direction</a><br>
+<a href="telemetry.value.list_of_scalar_values.html">telemetry.value.list_of_scalar_values</a><br>
+</td><td width="25%" valign=top><a href="telemetry.value.scalar.html">telemetry.value.scalar</a><br>
+<a href="telemetry.web_perf.metrics.webrtc_rendering_stats.html">telemetry.web_perf.metrics.webrtc_rendering_stats</a><br>
+</td><td width="25%" valign=top><a href="telemetry.web_perf.metrics.timeline_based_metric.html">telemetry.web_perf.metrics.timeline_based_metric</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.web_perf.metrics.webrtc_rendering_timeline.html#WebRtcRenderingTimelineMetric">WebRtcRenderingTimelineMetric</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="WebRtcRenderingTimelineMetric">class <strong>WebRtcRenderingTimelineMetric</strong></a>(<a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>WebrtcRenderingTimelineMetric&nbsp;calculates&nbsp;metric&nbsp;for&nbsp;WebMediaPlayerMS.<br>
+&nbsp;<br>
+The&nbsp;following&nbsp;metrics&nbsp;are&nbsp;added&nbsp;to&nbsp;the&nbsp;results:<br>
+&nbsp;&nbsp;WebRTCRendering_drift_time&nbsp;us<br>
+&nbsp;&nbsp;WebRTCRendering_percent_badly_out_of_sync&nbsp;%<br>
+&nbsp;&nbsp;WebRTCRendering_percent_out_of_sync&nbsp;%<br>
+&nbsp;&nbsp;WebRTCRendering_fps&nbsp;FPS<br>
+&nbsp;&nbsp;WebRTCRendering_smoothness_score&nbsp;%<br>
+&nbsp;&nbsp;WebRTCRendering_freezing_score&nbsp;%<br>
+&nbsp;&nbsp;WebRTCRendering_rendering_length_error&nbsp;%<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.web_perf.metrics.webrtc_rendering_timeline.html#WebRtcRenderingTimelineMetric">WebRtcRenderingTimelineMetric</a></dd>
+<dd><a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="WebRtcRenderingTimelineMetric-AddResults"><strong>AddResults</strong></a>(self, model, renderer_thread, interactions, results)</dt><dd><tt>Adding&nbsp;metrics&nbsp;to&nbsp;the&nbsp;results.</tt></dd></dl>
+
+<dl><dt><a name="WebRtcRenderingTimelineMetric-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Static methods defined here:<br>
+<dl><dt><a name="WebRtcRenderingTimelineMetric-IsMediaPlayerMSEvent"><strong>IsMediaPlayerMSEvent</strong></a>(event)</dt><dd><tt>Verify&nbsp;that&nbsp;the&nbsp;event&nbsp;is&nbsp;a&nbsp;webmediaplayerMS&nbsp;event.</tt></dd></dl>
+
+<hr>
+Methods inherited from <a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a>:<br>
+<dl><dt><a name="WebRtcRenderingTimelineMetric-AddWholeTraceResults"><strong>AddWholeTraceResults</strong></a>(self, model, results)</dt><dd><tt>Computes&nbsp;and&nbsp;adds&nbsp;metrics&nbsp;corresponding&nbsp;to&nbsp;the&nbsp;entire&nbsp;trace.<br>
+&nbsp;<br>
+Override&nbsp;this&nbsp;method&nbsp;to&nbsp;compute&nbsp;results&nbsp;that&nbsp;correspond&nbsp;to&nbsp;the&nbsp;whole&nbsp;trace.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;model:&nbsp;An&nbsp;instance&nbsp;of&nbsp;telemetry.timeline.model.TimelineModel.<br>
+&nbsp;&nbsp;results:&nbsp;An&nbsp;instance&nbsp;of&nbsp;page.PageTestResults.</tt></dd></dl>
+
+<dl><dt><a name="WebRtcRenderingTimelineMetric-VerifyNonOverlappedRecords"><strong>VerifyNonOverlappedRecords</strong></a>(self, interaction_records)</dt><dd><tt>This&nbsp;raises&nbsp;exceptions&nbsp;if&nbsp;interaction_records&nbsp;contain&nbsp;overlapped&nbsp;ranges.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.web_perf.metrics.timeline_based_metric.html#TimelineBasedMetric">telemetry.web_perf.metrics.timeline_based_metric.TimelineBasedMetric</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>WEB_MEDIA_PLAYER_MS_EVENT</strong> = 'WebMediaPlayerMS::UpdateCurrentFrame'</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.smooth_gesture_util.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.smooth_gesture_util.html
new file mode 100644
index 0000000..06743f8
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.smooth_gesture_util.html
@@ -0,0 +1,42 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.web_perf.smooth_gesture_util</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.web_perf.html"><font color="#ffffff">web_perf</font></a>.smooth_gesture_util</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/web_perf/smooth_gesture_util.py">telemetry/web_perf/smooth_gesture_util.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="copy.html">copy</a><br>
+</td><td width="25%" valign=top><a href="telemetry.web_perf.timeline_interaction_record.html">telemetry.web_perf.timeline_interaction_record</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-GetAdjustedInteractionIfContainGesture"><strong>GetAdjustedInteractionIfContainGesture</strong></a>(timeline, interaction_record)</dt><dd><tt>Returns&nbsp;a&nbsp;new&nbsp;interaction&nbsp;record&nbsp;if&nbsp;interaction_record&nbsp;contains&nbsp;geture<br>
+whose&nbsp;time&nbsp;range&nbsp;that&nbsp;overlaps&nbsp;with&nbsp;interaction_record's&nbsp;range.&nbsp;If&nbsp;not,<br>
+returns&nbsp;a&nbsp;clone&nbsp;of&nbsp;original&nbsp;interaction_record.<br>
+The&nbsp;synthetic&nbsp;gesture&nbsp;controller&nbsp;inserts&nbsp;a&nbsp;trace&nbsp;marker&nbsp;to&nbsp;precisely<br>
+demarcate&nbsp;when&nbsp;the&nbsp;gesture&nbsp;was&nbsp;running.&nbsp;We&nbsp;check&nbsp;for&nbsp;overlap,&nbsp;not&nbsp;inclusion,<br>
+because&nbsp;gesture_actions&nbsp;can&nbsp;start/end&nbsp;slightly&nbsp;outside&nbsp;the&nbsp;telemetry&nbsp;markers<br>
+on&nbsp;Windows.&nbsp;This&nbsp;problem&nbsp;is&nbsp;probably&nbsp;caused&nbsp;by&nbsp;a&nbsp;race&nbsp;condition&nbsp;between<br>
+the&nbsp;browser&nbsp;and&nbsp;renderer&nbsp;process&nbsp;submitting&nbsp;the&nbsp;trace&nbsp;events&nbsp;for&nbsp;the<br>
+markers.</tt></dd></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.story_test.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.story_test.html
new file mode 100644
index 0000000..762e837
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.story_test.html
@@ -0,0 +1,144 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.web_perf.story_test</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.web_perf.html"><font color="#ffffff">web_perf</font></a>.story_test</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/web_perf/story_test.py">telemetry/web_perf/story_test.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2015&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.web_perf.story_test.html#StoryTest">StoryTest</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="exceptions.html#Exception">exceptions.Exception</a>(<a href="exceptions.html#BaseException">exceptions.BaseException</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.web_perf.story_test.html#Failure">Failure</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="Failure">class <strong>Failure</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt><a href="#StoryTest">StoryTest</a>&nbsp;<a href="exceptions.html#Exception">Exception</a>&nbsp;raised&nbsp;when&nbsp;an&nbsp;undesired&nbsp;but&nbsp;designed-for&nbsp;problem.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.web_perf.story_test.html#Failure">Failure</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="Failure-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#Failure-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#Failure-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="Failure-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#Failure-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="Failure-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#Failure-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="Failure-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#Failure-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="Failure-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#Failure-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="Failure-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="Failure-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#Failure-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="Failure-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#Failure-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="Failure-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="Failure-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#Failure-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="Failure-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="StoryTest">class <strong>StoryTest</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;class&nbsp;for&nbsp;creating&nbsp;story&nbsp;tests.<br>
+&nbsp;<br>
+The&nbsp;overall&nbsp;test&nbsp;run&nbsp;control&nbsp;flow&nbsp;follows&nbsp;this&nbsp;order:<br>
+&nbsp;&nbsp;test.WillRunStory<br>
+&nbsp;&nbsp;state.WillRunStory<br>
+&nbsp;&nbsp;state.RunStory<br>
+&nbsp;&nbsp;test.Measure<br>
+&nbsp;&nbsp;state.DidRunStory<br>
+&nbsp;&nbsp;test.DidRunStory<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="StoryTest-DidRunStory"><strong>DidRunStory</strong></a>(self, platform)</dt><dd><tt>Override&nbsp;to&nbsp;do&nbsp;any&nbsp;action&nbsp;after&nbsp;running&nbsp;the&nbsp;story,&nbsp;e.g.,&nbsp;clean&nbsp;up.<br>
+&nbsp;<br>
+This&nbsp;is&nbsp;run&nbsp;after&nbsp;state.DidRunStory.&nbsp;And&nbsp;this&nbsp;is&nbsp;always&nbsp;called&nbsp;even&nbsp;if&nbsp;the<br>
+test&nbsp;run&nbsp;failed.<br>
+Args:<br>
+&nbsp;&nbsp;platform:&nbsp;The&nbsp;platform&nbsp;that&nbsp;the&nbsp;story&nbsp;will&nbsp;run&nbsp;on.</tt></dd></dl>
+
+<dl><dt><a name="StoryTest-Measure"><strong>Measure</strong></a>(self, platform, results)</dt><dd><tt>Override&nbsp;to&nbsp;take&nbsp;the&nbsp;measurement.<br>
+&nbsp;<br>
+This&nbsp;is&nbsp;run&nbsp;only&nbsp;if&nbsp;state.RunStory&nbsp;is&nbsp;successful.<br>
+Args:<br>
+&nbsp;&nbsp;platform:&nbsp;The&nbsp;platform&nbsp;that&nbsp;the&nbsp;story&nbsp;will&nbsp;run&nbsp;on.<br>
+&nbsp;&nbsp;results:&nbsp;The&nbsp;results&nbsp;of&nbsp;running&nbsp;the&nbsp;story.</tt></dd></dl>
+
+<dl><dt><a name="StoryTest-WillRunStory"><strong>WillRunStory</strong></a>(self, platform)</dt><dd><tt>Override&nbsp;to&nbsp;do&nbsp;any&nbsp;action&nbsp;before&nbsp;running&nbsp;the&nbsp;story.<br>
+&nbsp;<br>
+This&nbsp;is&nbsp;run&nbsp;before&nbsp;state.WillRunStory.<br>
+Args:<br>
+&nbsp;&nbsp;platform:&nbsp;The&nbsp;platform&nbsp;that&nbsp;the&nbsp;story&nbsp;will&nbsp;run&nbsp;on.</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.timeline_based_measurement.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.timeline_based_measurement.html
new file mode 100644
index 0000000..0761e8f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.timeline_based_measurement.html
@@ -0,0 +1,269 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.web_perf.timeline_based_measurement</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.web_perf.html"><font color="#ffffff">web_perf</font></a>.timeline_based_measurement</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/web_perf/timeline_based_measurement.py">telemetry/web_perf/timeline_based_measurement.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.web_perf.metrics.blob_timeline.html">telemetry.web_perf.metrics.blob_timeline</a><br>
+<a href="collections.html">collections</a><br>
+<a href="telemetry.web_perf.metrics.gpu_timeline.html">telemetry.web_perf.metrics.gpu_timeline</a><br>
+<a href="telemetry.web_perf.metrics.indexeddb_timeline.html">telemetry.web_perf.metrics.indexeddb_timeline</a><br>
+<a href="telemetry.web_perf.metrics.layout.html">telemetry.web_perf.metrics.layout</a><br>
+</td><td width="25%" valign=top><a href="logging.html">logging</a><br>
+<a href="telemetry.web_perf.metrics.memory_timeline.html">telemetry.web_perf.metrics.memory_timeline</a><br>
+<a href="telemetry.timeline.model.html">telemetry.timeline.model</a><br>
+<a href="telemetry.web_perf.metrics.responsiveness_metric.html">telemetry.web_perf.metrics.responsiveness_metric</a><br>
+<a href="telemetry.web_perf.smooth_gesture_util.html">telemetry.web_perf.smooth_gesture_util</a><br>
+</td><td width="25%" valign=top><a href="telemetry.web_perf.metrics.smoothness.html">telemetry.web_perf.metrics.smoothness</a><br>
+<a href="telemetry.web_perf.story_test.html">telemetry.web_perf.story_test</a><br>
+<a href="telemetry.web_perf.metrics.text_selection.html">telemetry.web_perf.metrics.text_selection</a><br>
+<a href="telemetry.web_perf.metrics.timeline_based_metric.html">telemetry.web_perf.metrics.timeline_based_metric</a><br>
+<a href="telemetry.web_perf.timeline_interaction_record.html">telemetry.web_perf.timeline_interaction_record</a><br>
+</td><td width="25%" valign=top><a href="telemetry.value.trace.html">telemetry.value.trace</a><br>
+<a href="telemetry.timeline.tracing_category_filter.html">telemetry.timeline.tracing_category_filter</a><br>
+<a href="telemetry.timeline.tracing_options.html">telemetry.timeline.tracing_options</a><br>
+<a href="telemetry.web_perf.metrics.webrtc_rendering_timeline.html">telemetry.web_perf.metrics.webrtc_rendering_timeline</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.web_perf.timeline_based_measurement.html#Options">Options</a>
+</font></dt><dt><font face="helvetica, arial"><a href="telemetry.web_perf.timeline_based_measurement.html#ResultsWrapperInterface">ResultsWrapperInterface</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="exceptions.html#Exception">exceptions.Exception</a>(<a href="exceptions.html#BaseException">exceptions.BaseException</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.web_perf.timeline_based_measurement.html#InvalidInteractions">InvalidInteractions</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="telemetry.web_perf.story_test.html#StoryTest">telemetry.web_perf.story_test.StoryTest</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.web_perf.timeline_based_measurement.html#TimelineBasedMeasurement">TimelineBasedMeasurement</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="InvalidInteractions">class <strong>InvalidInteractions</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.web_perf.timeline_based_measurement.html#InvalidInteractions">InvalidInteractions</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="InvalidInteractions-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#InvalidInteractions-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#InvalidInteractions-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="InvalidInteractions-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#InvalidInteractions-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="InvalidInteractions-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#InvalidInteractions-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="InvalidInteractions-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#InvalidInteractions-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="InvalidInteractions-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#InvalidInteractions-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="InvalidInteractions-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="InvalidInteractions-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#InvalidInteractions-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="InvalidInteractions-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#InvalidInteractions-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="InvalidInteractions-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="InvalidInteractions-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#InvalidInteractions-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="InvalidInteractions-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="Options">class <strong>Options</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>A&nbsp;class&nbsp;to&nbsp;be&nbsp;used&nbsp;to&nbsp;configure&nbsp;<a href="#TimelineBasedMeasurement">TimelineBasedMeasurement</a>.<br>
+&nbsp;<br>
+This&nbsp;is&nbsp;created&nbsp;and&nbsp;returned&nbsp;by<br>
+Benchmark.CreateTimelineBasedMeasurementOptions.<br>
+&nbsp;<br>
+By&nbsp;default,&nbsp;all&nbsp;the&nbsp;timeline&nbsp;based&nbsp;metrics&nbsp;in&nbsp;telemetry/web_perf/metrics&nbsp;are<br>
+used&nbsp;(see&nbsp;_GetAllTimelineBasedMetrics&nbsp;above).<br>
+To&nbsp;customize&nbsp;your&nbsp;metric&nbsp;needs,&nbsp;use&nbsp;<a href="#Options-SetTimelineBasedMetrics">SetTimelineBasedMetrics</a>().<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="Options-ExtendTraceCategoryFilter"><strong>ExtendTraceCategoryFilter</strong></a>(self, filters)</dt></dl>
+
+<dl><dt><a name="Options-GetTimelineBasedMetrics"><strong>GetTimelineBasedMetrics</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="Options-SetTimelineBasedMetrics"><strong>SetTimelineBasedMetrics</strong></a>(self, metrics)</dt></dl>
+
+<dl><dt><a name="Options-__init__"><strong>__init__</strong></a>(self, overhead_level<font color="#909090">='no-overhead'</font>)</dt><dd><tt>As&nbsp;the&nbsp;amount&nbsp;of&nbsp;instrumentation&nbsp;increases,&nbsp;so&nbsp;does&nbsp;the&nbsp;overhead.<br>
+The&nbsp;user&nbsp;of&nbsp;the&nbsp;measurement&nbsp;chooses&nbsp;the&nbsp;overhead&nbsp;level&nbsp;that&nbsp;is&nbsp;appropriate,<br>
+and&nbsp;the&nbsp;tracing&nbsp;is&nbsp;filtered&nbsp;accordingly.<br>
+&nbsp;<br>
+overhead_level:&nbsp;Can&nbsp;either&nbsp;be&nbsp;a&nbsp;custom&nbsp;TracingCategoryFilter&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;or<br>
+&nbsp;&nbsp;&nbsp;&nbsp;one&nbsp;of&nbsp;NO_OVERHEAD_LEVEL,&nbsp;MINIMAL_OVERHEAD_LEVEL&nbsp;or<br>
+&nbsp;&nbsp;&nbsp;&nbsp;DEBUG_OVERHEAD_LEVEL.</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>category_filter</strong></dt>
+</dl>
+<dl><dt><strong>tracing_options</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ResultsWrapperInterface">class <strong>ResultsWrapperInterface</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>#&nbsp;TODO(nednguyen):&nbsp;Get&nbsp;rid&nbsp;of&nbsp;this&nbsp;results&nbsp;wrapper&nbsp;hack&nbsp;after&nbsp;we&nbsp;add&nbsp;interaction<br>
+#&nbsp;record&nbsp;to&nbsp;telemetry&nbsp;value&nbsp;system&nbsp;(crbug.com/453109)<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="ResultsWrapperInterface-AddValue"><strong>AddValue</strong></a>(self, value)</dt></dl>
+
+<dl><dt><a name="ResultsWrapperInterface-SetResults"><strong>SetResults</strong></a>(self, results)</dt></dl>
+
+<dl><dt><a name="ResultsWrapperInterface-SetTirLabel"><strong>SetTirLabel</strong></a>(self, tir_label)</dt></dl>
+
+<dl><dt><a name="ResultsWrapperInterface-__init__"><strong>__init__</strong></a>(self)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>current_page</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TimelineBasedMeasurement">class <strong>TimelineBasedMeasurement</strong></a>(<a href="telemetry.web_perf.story_test.html#StoryTest">telemetry.web_perf.story_test.StoryTest</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Collects&nbsp;multiple&nbsp;metrics&nbsp;based&nbsp;on&nbsp;their&nbsp;interaction&nbsp;records.<br>
+&nbsp;<br>
+A&nbsp;timeline&nbsp;based&nbsp;measurement&nbsp;shifts&nbsp;the&nbsp;burden&nbsp;of&nbsp;what&nbsp;metrics&nbsp;to&nbsp;collect&nbsp;onto<br>
+the&nbsp;story&nbsp;under&nbsp;test.&nbsp;Instead&nbsp;of&nbsp;the&nbsp;measurement<br>
+having&nbsp;a&nbsp;fixed&nbsp;set&nbsp;of&nbsp;values&nbsp;it&nbsp;collects,&nbsp;the&nbsp;story&nbsp;being&nbsp;tested<br>
+issues&nbsp;(via&nbsp;javascript)&nbsp;an&nbsp;Interaction&nbsp;record&nbsp;into&nbsp;the&nbsp;user&nbsp;timing&nbsp;API&nbsp;that<br>
+describing&nbsp;what&nbsp;is&nbsp;happening&nbsp;at&nbsp;that&nbsp;time,&nbsp;as&nbsp;well&nbsp;as&nbsp;a&nbsp;standardized&nbsp;set<br>
+of&nbsp;flags&nbsp;describing&nbsp;the&nbsp;semantics&nbsp;of&nbsp;the&nbsp;work&nbsp;being&nbsp;done.&nbsp;The<br>
+<a href="#TimelineBasedMeasurement">TimelineBasedMeasurement</a>&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;collects&nbsp;a&nbsp;trace&nbsp;that&nbsp;includes&nbsp;both&nbsp;these<br>
+interaction&nbsp;records,&nbsp;and&nbsp;a&nbsp;user-chosen&nbsp;amount&nbsp;of&nbsp;performance&nbsp;data&nbsp;using<br>
+Telemetry's&nbsp;various&nbsp;timeline-producing&nbsp;APIs,&nbsp;tracing&nbsp;especially.<br>
+&nbsp;<br>
+It&nbsp;then&nbsp;passes&nbsp;the&nbsp;recorded&nbsp;timeline&nbsp;to&nbsp;different&nbsp;TimelineBasedMetrics&nbsp;based<br>
+on&nbsp;those&nbsp;flags.&nbsp;As&nbsp;an&nbsp;example,&nbsp;this&nbsp;allows&nbsp;a&nbsp;single&nbsp;story&nbsp;run&nbsp;to&nbsp;produce<br>
+load&nbsp;timing&nbsp;data,&nbsp;smoothness&nbsp;data,&nbsp;critical&nbsp;jank&nbsp;information&nbsp;and&nbsp;overall&nbsp;cpu<br>
+usage&nbsp;information.<br>
+&nbsp;<br>
+For&nbsp;information&nbsp;on&nbsp;how&nbsp;to&nbsp;mark&nbsp;up&nbsp;a&nbsp;page&nbsp;to&nbsp;work&nbsp;with<br>
+<a href="#TimelineBasedMeasurement">TimelineBasedMeasurement</a>,&nbsp;refer&nbsp;to&nbsp;the<br>
+perf.metrics.timeline_interaction_record&nbsp;module.<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;&nbsp;&nbsp;options:&nbsp;an&nbsp;instance&nbsp;of&nbsp;timeline_based_measurement.<a href="#Options">Options</a>.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;results_wrapper:&nbsp;A&nbsp;class&nbsp;that&nbsp;has&nbsp;the&nbsp;__init__&nbsp;method&nbsp;takes&nbsp;in<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;the&nbsp;page_test_results&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;and&nbsp;the&nbsp;interaction&nbsp;record&nbsp;label.&nbsp;This<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class&nbsp;follows&nbsp;the&nbsp;<a href="#ResultsWrapperInterface">ResultsWrapperInterface</a>.&nbsp;Note:&nbsp;this&nbsp;class&nbsp;is&nbsp;not<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;supported&nbsp;long&nbsp;term&nbsp;and&nbsp;to&nbsp;be&nbsp;removed&nbsp;when&nbsp;crbug.com/453109&nbsp;is&nbsp;resolved.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.web_perf.timeline_based_measurement.html#TimelineBasedMeasurement">TimelineBasedMeasurement</a></dd>
+<dd><a href="telemetry.web_perf.story_test.html#StoryTest">telemetry.web_perf.story_test.StoryTest</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="TimelineBasedMeasurement-DidRunStory"><strong>DidRunStory</strong></a>(self, platform)</dt><dd><tt>Clean&nbsp;up&nbsp;after&nbsp;running&nbsp;the&nbsp;story.</tt></dd></dl>
+
+<dl><dt><a name="TimelineBasedMeasurement-Measure"><strong>Measure</strong></a>(self, platform, results)</dt><dd><tt>Collect&nbsp;all&nbsp;possible&nbsp;metrics&nbsp;and&nbsp;added&nbsp;them&nbsp;to&nbsp;results.</tt></dd></dl>
+
+<dl><dt><a name="TimelineBasedMeasurement-WillRunStory"><strong>WillRunStory</strong></a>(self, platform)</dt><dd><tt>Configure&nbsp;and&nbsp;start&nbsp;tracing.</tt></dd></dl>
+
+<dl><dt><a name="TimelineBasedMeasurement-__init__"><strong>__init__</strong></a>(self, options, results_wrapper<font color="#909090">=None</font>)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.web_perf.story_test.html#StoryTest">telemetry.web_perf.story_test.StoryTest</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>ALL_OVERHEAD_LEVELS</strong> = ['no-overhead', 'minimal-overhead', 'debug-overhead']<br>
+<strong>DEBUG_OVERHEAD_LEVEL</strong> = 'debug-overhead'<br>
+<strong>MINIMAL_OVERHEAD_LEVEL</strong> = 'minimal-overhead'<br>
+<strong>NO_OVERHEAD_LEVEL</strong> = 'no-overhead'</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.timeline_based_page_test.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.timeline_based_page_test.html
new file mode 100644
index 0000000..e3cbb38
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.timeline_based_page_test.html
@@ -0,0 +1,136 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.web_perf.timeline_based_page_test</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.web_perf.html"><font color="#ffffff">web_perf</font></a>.timeline_based_page_test</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/web_perf/timeline_based_page_test.py">telemetry/web_perf/timeline_based_page_test.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;(c)&nbsp;2015&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.page.page_test.html">telemetry.page.page_test</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="telemetry.page.page_test.html#PageTest">telemetry.page.page_test.PageTest</a>(<a href="__builtin__.html#object">__builtin__.object</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.web_perf.timeline_based_page_test.html#TimelineBasedPageTest">TimelineBasedPageTest</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TimelineBasedPageTest">class <strong>TimelineBasedPageTest</strong></a>(<a href="telemetry.page.page_test.html#PageTest">telemetry.page.page_test.PageTest</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Page&nbsp;test&nbsp;that&nbsp;collects&nbsp;metrics&nbsp;with&nbsp;TimelineBasedMeasurement.<br>
+&nbsp;<br>
+WillRunStory(),&nbsp;Measure()&nbsp;and&nbsp;DidRunStory()&nbsp;are&nbsp;all&nbsp;done&nbsp;in&nbsp;story_runner<br>
+explicitly.&nbsp;We&nbsp;still&nbsp;need&nbsp;this&nbsp;wrapper&nbsp;around&nbsp;<a href="telemetry.page.page_test.html#PageTest">PageTest</a>&nbsp;because&nbsp;it&nbsp;executes<br>
+some&nbsp;browser&nbsp;related&nbsp;functions&nbsp;in&nbsp;the&nbsp;parent&nbsp;class,&nbsp;which&nbsp;is&nbsp;needed&nbsp;by<br>
+Timeline&nbsp;Based&nbsp;Measurement&nbsp;benchmarks.&nbsp;This&nbsp;class&nbsp;will&nbsp;be&nbsp;removed&nbsp;after<br>
+page_test's&nbsp;hooks&nbsp;are&nbsp;fully&nbsp;removed.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.web_perf.timeline_based_page_test.html#TimelineBasedPageTest">TimelineBasedPageTest</a></dd>
+<dd><a href="telemetry.page.page_test.html#PageTest">telemetry.page.page_test.PageTest</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="TimelineBasedPageTest-ValidateAndMeasurePage"><strong>ValidateAndMeasurePage</strong></a>(self, page, tab, results)</dt><dd><tt>Collect&nbsp;all&nbsp;possible&nbsp;metrics&nbsp;and&nbsp;added&nbsp;them&nbsp;to&nbsp;results.</tt></dd></dl>
+
+<dl><dt><a name="TimelineBasedPageTest-__init__"><strong>__init__</strong></a>(self, tbm)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>measurement</strong></dt>
+</dl>
+<hr>
+Methods inherited from <a href="telemetry.page.page_test.html#PageTest">telemetry.page.page_test.PageTest</a>:<br>
+<dl><dt><a name="TimelineBasedPageTest-CustomizeBrowserOptions"><strong>CustomizeBrowserOptions</strong></a>(self, options)</dt><dd><tt>Override&nbsp;to&nbsp;add&nbsp;test-specific&nbsp;options&nbsp;to&nbsp;the&nbsp;BrowserOptions&nbsp;object</tt></dd></dl>
+
+<dl><dt><a name="TimelineBasedPageTest-DidNavigateToPage"><strong>DidNavigateToPage</strong></a>(self, page, tab)</dt><dd><tt>Override&nbsp;to&nbsp;do&nbsp;operations&nbsp;right&nbsp;after&nbsp;the&nbsp;page&nbsp;is&nbsp;navigated&nbsp;and&nbsp;after<br>
+all&nbsp;waiting&nbsp;for&nbsp;completion&nbsp;has&nbsp;occurred.</tt></dd></dl>
+
+<dl><dt><a name="TimelineBasedPageTest-DidRunPage"><strong>DidRunPage</strong></a>(self, platform)</dt><dd><tt>Called&nbsp;after&nbsp;the&nbsp;test&nbsp;run&nbsp;method&nbsp;was&nbsp;run,&nbsp;even&nbsp;if&nbsp;it&nbsp;failed.</tt></dd></dl>
+
+<dl><dt><a name="TimelineBasedPageTest-DidStartBrowser"><strong>DidStartBrowser</strong></a>(self, browser)</dt><dd><tt>Override&nbsp;to&nbsp;customize&nbsp;the&nbsp;browser&nbsp;right&nbsp;after&nbsp;it&nbsp;has&nbsp;launched.</tt></dd></dl>
+
+<dl><dt><a name="TimelineBasedPageTest-RestartBrowserBeforeEachPage"><strong>RestartBrowserBeforeEachPage</strong></a>(self)</dt><dd><tt>Should&nbsp;the&nbsp;browser&nbsp;be&nbsp;restarted&nbsp;for&nbsp;the&nbsp;page?<br>
+&nbsp;<br>
+This&nbsp;returns&nbsp;true&nbsp;if&nbsp;the&nbsp;test&nbsp;needs&nbsp;to&nbsp;unconditionally&nbsp;restart&nbsp;the<br>
+browser&nbsp;for&nbsp;each&nbsp;page.&nbsp;It&nbsp;may&nbsp;be&nbsp;called&nbsp;before&nbsp;the&nbsp;browser&nbsp;is&nbsp;started.</tt></dd></dl>
+
+<dl><dt><a name="TimelineBasedPageTest-RunNavigateSteps"><strong>RunNavigateSteps</strong></a>(self, page, tab)</dt><dd><tt>Navigates&nbsp;the&nbsp;tab&nbsp;to&nbsp;the&nbsp;page&nbsp;URL&nbsp;attribute.<br>
+&nbsp;<br>
+Runs&nbsp;the&nbsp;'navigate_steps'&nbsp;page&nbsp;attribute&nbsp;as&nbsp;a&nbsp;compound&nbsp;action.</tt></dd></dl>
+
+<dl><dt><a name="TimelineBasedPageTest-SetOptions"><strong>SetOptions</strong></a>(self, options)</dt><dd><tt>Sets&nbsp;the&nbsp;BrowserFinderOptions&nbsp;instance&nbsp;to&nbsp;use.</tt></dd></dl>
+
+<dl><dt><a name="TimelineBasedPageTest-StopBrowserAfterPage"><strong>StopBrowserAfterPage</strong></a>(self, browser, page)</dt><dd><tt>Should&nbsp;the&nbsp;browser&nbsp;be&nbsp;stopped&nbsp;after&nbsp;the&nbsp;page&nbsp;is&nbsp;run?<br>
+&nbsp;<br>
+This&nbsp;is&nbsp;called&nbsp;after&nbsp;a&nbsp;page&nbsp;is&nbsp;run&nbsp;to&nbsp;decide&nbsp;whether&nbsp;the&nbsp;browser&nbsp;needs&nbsp;to<br>
+be&nbsp;stopped&nbsp;to&nbsp;clean&nbsp;up&nbsp;its&nbsp;state.&nbsp;If&nbsp;it&nbsp;is&nbsp;stopped,&nbsp;then&nbsp;it&nbsp;will&nbsp;be<br>
+restarted&nbsp;to&nbsp;run&nbsp;the&nbsp;next&nbsp;page.<br>
+&nbsp;<br>
+A&nbsp;test&nbsp;that&nbsp;overrides&nbsp;this&nbsp;can&nbsp;look&nbsp;at&nbsp;both&nbsp;the&nbsp;page&nbsp;and&nbsp;the&nbsp;browser&nbsp;to<br>
+decide&nbsp;whether&nbsp;it&nbsp;needs&nbsp;to&nbsp;stop&nbsp;the&nbsp;browser.</tt></dd></dl>
+
+<dl><dt><a name="TimelineBasedPageTest-TabForPage"><strong>TabForPage</strong></a>(self, page, browser)</dt><dd><tt>Override&nbsp;to&nbsp;select&nbsp;a&nbsp;different&nbsp;tab&nbsp;for&nbsp;the&nbsp;page.&nbsp;&nbsp;For&nbsp;instance,&nbsp;to<br>
+create&nbsp;a&nbsp;new&nbsp;tab&nbsp;for&nbsp;every&nbsp;page,&nbsp;return&nbsp;browser.tabs.New().</tt></dd></dl>
+
+<dl><dt><a name="TimelineBasedPageTest-WillNavigateToPage"><strong>WillNavigateToPage</strong></a>(self, page, tab)</dt><dd><tt>Override&nbsp;to&nbsp;do&nbsp;operations&nbsp;before&nbsp;the&nbsp;page&nbsp;is&nbsp;navigated,&nbsp;notably&nbsp;Telemetry<br>
+will&nbsp;already&nbsp;have&nbsp;performed&nbsp;the&nbsp;following&nbsp;operations&nbsp;on&nbsp;the&nbsp;browser&nbsp;before<br>
+calling&nbsp;this&nbsp;function:<br>
+*&nbsp;Ensure&nbsp;only&nbsp;one&nbsp;tab&nbsp;is&nbsp;open.<br>
+*&nbsp;Call&nbsp;WaitForDocumentReadyStateToComplete&nbsp;on&nbsp;the&nbsp;tab.</tt></dd></dl>
+
+<dl><dt><a name="TimelineBasedPageTest-WillStartBrowser"><strong>WillStartBrowser</strong></a>(self, platform)</dt><dd><tt>Override&nbsp;to&nbsp;manipulate&nbsp;the&nbsp;browser&nbsp;environment&nbsp;before&nbsp;it&nbsp;launches.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from <a href="telemetry.page.page_test.html#PageTest">telemetry.page.page_test.PageTest</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>clear_cache_before_each_run</strong></dt>
+<dd><tt>When&nbsp;set&nbsp;to&nbsp;True,&nbsp;the&nbsp;browser's&nbsp;disk&nbsp;and&nbsp;memory&nbsp;cache&nbsp;will&nbsp;be&nbsp;cleared<br>
+before&nbsp;each&nbsp;run.</tt></dd>
+</dl>
+<dl><dt><strong>close_tabs_before_run</strong></dt>
+<dd><tt>When&nbsp;set&nbsp;to&nbsp;True,&nbsp;all&nbsp;tabs&nbsp;are&nbsp;closed&nbsp;before&nbsp;running&nbsp;the&nbsp;test&nbsp;for&nbsp;the<br>
+first&nbsp;time.</tt></dd>
+</dl>
+<dl><dt><strong>is_multi_tab_test</strong></dt>
+<dd><tt>Returns&nbsp;True&nbsp;if&nbsp;the&nbsp;test&nbsp;opens&nbsp;multiple&nbsp;tabs.<br>
+&nbsp;<br>
+If&nbsp;the&nbsp;test&nbsp;overrides&nbsp;TabForPage,&nbsp;it&nbsp;is&nbsp;deemed&nbsp;a&nbsp;multi-tab&nbsp;test.<br>
+Multi-tab&nbsp;tests&nbsp;do&nbsp;not&nbsp;retry&nbsp;after&nbsp;tab&nbsp;or&nbsp;browser&nbsp;crashes,&nbsp;whereas,<br>
+single-tab&nbsp;tests&nbsp;too.&nbsp;That&nbsp;is&nbsp;because&nbsp;the&nbsp;state&nbsp;of&nbsp;multi-tab&nbsp;tests<br>
+(e.g.,&nbsp;how&nbsp;many&nbsp;tabs&nbsp;are&nbsp;open,&nbsp;etc.)&nbsp;is&nbsp;unknown&nbsp;after&nbsp;crashes.</tt></dd>
+</dl>
+</td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.timeline_interaction_record.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.timeline_interaction_record.html
new file mode 100644
index 0000000..34a1639
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.web_perf.timeline_interaction_record.html
@@ -0,0 +1,317 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.web_perf.timeline_interaction_record</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.web_perf.html"><font color="#ffffff">web_perf</font></a>.timeline_interaction_record</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/web_perf/timeline_interaction_record.py">telemetry/web_perf/timeline_interaction_record.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.decorators.html">telemetry.decorators</a><br>
+</td><td width="25%" valign=top><a href="re.html">re</a><br>
+</td><td width="25%" valign=top><a href="telemetry.timeline.bounds.html">telemetry.timeline.bounds</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.web_perf.timeline_interaction_record.html#TimelineInteractionRecord">TimelineInteractionRecord</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="exceptions.html#Exception">exceptions.Exception</a>(<a href="exceptions.html#BaseException">exceptions.BaseException</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.web_perf.timeline_interaction_record.html#ThreadTimeRangeOverlappedException">ThreadTimeRangeOverlappedException</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.web_perf.timeline_interaction_record.html#NoThreadTimeDataException">NoThreadTimeDataException</a>
+</font></dt></dl>
+</dd>
+</dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="NoThreadTimeDataException">class <strong>NoThreadTimeDataException</strong></a>(<a href="telemetry.web_perf.timeline_interaction_record.html#ThreadTimeRangeOverlappedException">ThreadTimeRangeOverlappedException</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt><a href="exceptions.html#Exception">Exception</a>&nbsp;that&nbsp;can&nbsp;be&nbsp;thrown&nbsp;if&nbsp;there&nbsp;is&nbsp;not&nbsp;sufficient&nbsp;thread&nbsp;time&nbsp;data<br>
+to&nbsp;compute&nbsp;the&nbsp;overlapped&nbsp;thread&nbsp;time&nbsp;range.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.web_perf.timeline_interaction_record.html#NoThreadTimeDataException">NoThreadTimeDataException</a></dd>
+<dd><a href="telemetry.web_perf.timeline_interaction_record.html#ThreadTimeRangeOverlappedException">ThreadTimeRangeOverlappedException</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors inherited from <a href="telemetry.web_perf.timeline_interaction_record.html#ThreadTimeRangeOverlappedException">ThreadTimeRangeOverlappedException</a>:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="NoThreadTimeDataException-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#NoThreadTimeDataException-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#NoThreadTimeDataException-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="NoThreadTimeDataException-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#NoThreadTimeDataException-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="NoThreadTimeDataException-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#NoThreadTimeDataException-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="NoThreadTimeDataException-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#NoThreadTimeDataException-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="NoThreadTimeDataException-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#NoThreadTimeDataException-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="NoThreadTimeDataException-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="NoThreadTimeDataException-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#NoThreadTimeDataException-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="NoThreadTimeDataException-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#NoThreadTimeDataException-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="NoThreadTimeDataException-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="NoThreadTimeDataException-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#NoThreadTimeDataException-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="NoThreadTimeDataException-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ThreadTimeRangeOverlappedException">class <strong>ThreadTimeRangeOverlappedException</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt><a href="exceptions.html#Exception">Exception</a>&nbsp;that&nbsp;can&nbsp;be&nbsp;thrown&nbsp;when&nbsp;computing&nbsp;overlapped&nbsp;thread&nbsp;time&nbsp;range<br>
+with&nbsp;other&nbsp;events.<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.web_perf.timeline_interaction_record.html#ThreadTimeRangeOverlappedException">ThreadTimeRangeOverlappedException</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="ThreadTimeRangeOverlappedException-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#ThreadTimeRangeOverlappedException-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#ThreadTimeRangeOverlappedException-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="ThreadTimeRangeOverlappedException-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#ThreadTimeRangeOverlappedException-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="ThreadTimeRangeOverlappedException-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#ThreadTimeRangeOverlappedException-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="ThreadTimeRangeOverlappedException-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#ThreadTimeRangeOverlappedException-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="ThreadTimeRangeOverlappedException-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#ThreadTimeRangeOverlappedException-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="ThreadTimeRangeOverlappedException-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="ThreadTimeRangeOverlappedException-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#ThreadTimeRangeOverlappedException-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="ThreadTimeRangeOverlappedException-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#ThreadTimeRangeOverlappedException-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="ThreadTimeRangeOverlappedException-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="ThreadTimeRangeOverlappedException-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#ThreadTimeRangeOverlappedException-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="ThreadTimeRangeOverlappedException-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="TimelineInteractionRecord">class <strong>TimelineInteractionRecord</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
+<td colspan=2><tt>Represents&nbsp;an&nbsp;interaction&nbsp;that&nbsp;took&nbsp;place&nbsp;during&nbsp;a&nbsp;timeline&nbsp;recording.<br>
+&nbsp;<br>
+As&nbsp;a&nbsp;page&nbsp;runs,&nbsp;typically&nbsp;a&nbsp;number&nbsp;of&nbsp;different&nbsp;(simulated)&nbsp;user&nbsp;interactions<br>
+take&nbsp;place.&nbsp;For&nbsp;instance,&nbsp;a&nbsp;user&nbsp;might&nbsp;click&nbsp;a&nbsp;button&nbsp;in&nbsp;a&nbsp;mail&nbsp;app&nbsp;causing&nbsp;a<br>
+popup&nbsp;to&nbsp;animate&nbsp;in.&nbsp;Then&nbsp;they&nbsp;might&nbsp;press&nbsp;another&nbsp;button&nbsp;that&nbsp;sends&nbsp;data&nbsp;to&nbsp;a<br>
+server&nbsp;and&nbsp;simultaneously&nbsp;closes&nbsp;the&nbsp;popup&nbsp;without&nbsp;an&nbsp;animation.&nbsp;These&nbsp;are&nbsp;two<br>
+interactions.<br>
+&nbsp;<br>
+From&nbsp;the&nbsp;point&nbsp;of&nbsp;view&nbsp;of&nbsp;the&nbsp;page,&nbsp;each&nbsp;interaction&nbsp;might&nbsp;have&nbsp;a&nbsp;different<br>
+label:&nbsp;ClickComposeButton&nbsp;and&nbsp;SendEmail,&nbsp;for&nbsp;instance.&nbsp;From&nbsp;the&nbsp;point<br>
+of&nbsp;view&nbsp;of&nbsp;the&nbsp;benchmarking&nbsp;harness,&nbsp;the&nbsp;labels&nbsp;aren't&nbsp;so&nbsp;interesting&nbsp;as&nbsp;what<br>
+the&nbsp;performance&nbsp;expectations&nbsp;are&nbsp;for&nbsp;that&nbsp;interaction:&nbsp;was&nbsp;it&nbsp;loading<br>
+resources&nbsp;from&nbsp;the&nbsp;network?&nbsp;was&nbsp;there&nbsp;an&nbsp;animation?<br>
+&nbsp;<br>
+Determining&nbsp;these&nbsp;things&nbsp;is&nbsp;hard&nbsp;to&nbsp;do,&nbsp;simply&nbsp;by&nbsp;observing&nbsp;the&nbsp;state&nbsp;given&nbsp;to<br>
+a&nbsp;page&nbsp;from&nbsp;javascript.&nbsp;There&nbsp;are&nbsp;hints,&nbsp;for&nbsp;instance&nbsp;if&nbsp;network&nbsp;requests&nbsp;are<br>
+sent,&nbsp;or&nbsp;if&nbsp;a&nbsp;CSS&nbsp;animation&nbsp;is&nbsp;pending.&nbsp;But&nbsp;this&nbsp;is&nbsp;by&nbsp;no&nbsp;means&nbsp;a&nbsp;complete<br>
+story.<br>
+&nbsp;<br>
+Instead,&nbsp;we&nbsp;expect&nbsp;pages&nbsp;to&nbsp;mark&nbsp;up&nbsp;the&nbsp;timeline&nbsp;what&nbsp;they&nbsp;are&nbsp;doing,&nbsp;with<br>
+label&nbsp;and&nbsp;flags&nbsp;indicating&nbsp;the&nbsp;semantics&nbsp;of&nbsp;that&nbsp;interaction.&nbsp;This<br>
+is&nbsp;currently&nbsp;done&nbsp;by&nbsp;pushing&nbsp;markers&nbsp;into&nbsp;the&nbsp;console.time/timeEnd&nbsp;API:&nbsp;this<br>
+for&nbsp;instance&nbsp;can&nbsp;be&nbsp;issued&nbsp;in&nbsp;JS:<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;var&nbsp;str&nbsp;=&nbsp;'Interaction.SendEmail';<br>
+&nbsp;&nbsp;&nbsp;console.time(str);<br>
+&nbsp;&nbsp;&nbsp;setTimeout(function()&nbsp;{<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;console.timeEnd(str);<br>
+&nbsp;&nbsp;&nbsp;},&nbsp;1000);<br>
+&nbsp;<br>
+When&nbsp;run&nbsp;with&nbsp;perf.measurements.timeline_based_measurement&nbsp;running,&nbsp;this&nbsp;will<br>
+then&nbsp;cause&nbsp;a&nbsp;<a href="#TimelineInteractionRecord">TimelineInteractionRecord</a>&nbsp;to&nbsp;be&nbsp;created&nbsp;for&nbsp;this&nbsp;range&nbsp;with<br>
+all&nbsp;metrics&nbsp;reported&nbsp;for&nbsp;the&nbsp;marked&nbsp;up&nbsp;1000ms&nbsp;time-range.<br>
+&nbsp;<br>
+The&nbsp;valid&nbsp;interaction&nbsp;flags&nbsp;are:<br>
+&nbsp;&nbsp;&nbsp;*&nbsp;repeatable:&nbsp;Allows&nbsp;other&nbsp;interactions&nbsp;to&nbsp;use&nbsp;the&nbsp;same&nbsp;label<br>&nbsp;</tt></td></tr>
+<tr><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="TimelineInteractionRecord-GetBounds"><strong>GetBounds</strong></a>(*args, **kwargs)</dt></dl>
+
+<dl><dt><a name="TimelineInteractionRecord-GetOverlappedThreadTimeForSlice"><strong>GetOverlappedThreadTimeForSlice</strong></a>(self, timeline_slice)</dt><dd><tt>Get&nbsp;the&nbsp;thread&nbsp;duration&nbsp;of&nbsp;timeline_slice&nbsp;that&nbsp;overlaps&nbsp;with&nbsp;this&nbsp;record.<br>
+&nbsp;<br>
+There&nbsp;are&nbsp;two&nbsp;cases&nbsp;:<br>
+&nbsp;<br>
+Case&nbsp;1:&nbsp;timeline_slice&nbsp;runs&nbsp;in&nbsp;the&nbsp;same&nbsp;thread&nbsp;as&nbsp;the&nbsp;record.<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;timeline_slice&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;]<br>
+&nbsp;&nbsp;THREAD&nbsp;1&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;record&nbsp;starts&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;record&nbsp;ends<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(relative&nbsp;order&nbsp;in&nbsp;thread&nbsp;time)<br>
+&nbsp;<br>
+&nbsp;&nbsp;As&nbsp;the&nbsp;thread&nbsp;timestamps&nbsp;in&nbsp;timeline_slice&nbsp;and&nbsp;record&nbsp;are&nbsp;consistent,&nbsp;we<br>
+&nbsp;&nbsp;simply&nbsp;use&nbsp;them&nbsp;to&nbsp;compute&nbsp;the&nbsp;overlap.<br>
+&nbsp;<br>
+Case&nbsp;2:&nbsp;timeline_slice&nbsp;runs&nbsp;in&nbsp;a&nbsp;different&nbsp;thread&nbsp;from&nbsp;the&nbsp;record's.<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br>
+&nbsp;&nbsp;THREAD&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;timeline_slice&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;]<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br>
+&nbsp;&nbsp;THREAD&nbsp;1&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;record&nbsp;starts&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;record&nbsp;ends<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(relative&nbsp;order&nbsp;in&nbsp;wall-time)<br>
+&nbsp;<br>
+&nbsp;&nbsp;Unlike&nbsp;case&nbsp;1,&nbsp;thread&nbsp;timestamps&nbsp;of&nbsp;a&nbsp;thread&nbsp;are&nbsp;measured&nbsp;by&nbsp;its<br>
+&nbsp;&nbsp;thread-specific&nbsp;clock,&nbsp;which&nbsp;is&nbsp;inconsistent&nbsp;with&nbsp;that&nbsp;of&nbsp;the&nbsp;other<br>
+&nbsp;&nbsp;thread,&nbsp;and&nbsp;thus&nbsp;can't&nbsp;be&nbsp;used&nbsp;to&nbsp;compute&nbsp;the&nbsp;overlapped&nbsp;thread&nbsp;duration.<br>
+&nbsp;&nbsp;Hence,&nbsp;we&nbsp;use&nbsp;a&nbsp;heuristic&nbsp;to&nbsp;compute&nbsp;the&nbsp;overlap&nbsp;(see<br>
+&nbsp;&nbsp;_GetOverlappedThreadTimeForSliceInDifferentThread&nbsp;for&nbsp;more&nbsp;details)<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;timeline_slice:&nbsp;An&nbsp;instance&nbsp;of&nbsp;telemetry.timeline.slice.Slice</tt></dd></dl>
+
+<dl><dt><a name="TimelineInteractionRecord-__init__"><strong>__init__</strong></a>(self, label, start, end, async_event<font color="#909090">=None</font>, flags<font color="#909090">=None</font>)</dt></dl>
+
+<dl><dt><a name="TimelineInteractionRecord-__repr__"><strong>__repr__</strong></a>(self)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="TimelineInteractionRecord-FromAsyncEvent"><strong>FromAsyncEvent</strong></a>(cls, async_event)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt><dd><tt>Construct&nbsp;an&nbsp;timeline_interaction_record&nbsp;from&nbsp;an&nbsp;async&nbsp;event.<br>
+Args:<br>
+&nbsp;&nbsp;async_event:&nbsp;An&nbsp;instance&nbsp;of<br>
+&nbsp;&nbsp;&nbsp;&nbsp;telemetry.timeline.async_slices.AsyncSlice</tt></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>end</strong></dt>
+</dl>
+<dl><dt><strong>label</strong></dt>
+</dl>
+<dl><dt><strong>repeatable</strong></dt>
+</dl>
+<dl><dt><strong>start</strong></dt>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-GetJavaScriptMarker"><strong>GetJavaScriptMarker</strong></a>(label, flags)</dt><dd><tt>Computes&nbsp;the&nbsp;marker&nbsp;string&nbsp;of&nbsp;an&nbsp;interaction&nbsp;record.<br>
+&nbsp;<br>
+This&nbsp;marker&nbsp;string&nbsp;can&nbsp;be&nbsp;used&nbsp;with&nbsp;JavaScript&nbsp;API&nbsp;console.time()<br>
+and&nbsp;console.timeEnd()&nbsp;to&nbsp;mark&nbsp;the&nbsp;beginning&nbsp;and&nbsp;end&nbsp;of&nbsp;the<br>
+interaction&nbsp;record..<br>
+&nbsp;<br>
+Args:<br>
+&nbsp;&nbsp;label:&nbsp;The&nbsp;label&nbsp;used&nbsp;to&nbsp;identify&nbsp;the&nbsp;interaction&nbsp;record.<br>
+&nbsp;&nbsp;flags:&nbsp;the&nbsp;flags&nbsp;for&nbsp;the&nbsp;interaction&nbsp;record&nbsp;see&nbsp;FLAGS&nbsp;above.<br>
+&nbsp;<br>
+Returns:<br>
+&nbsp;&nbsp;The&nbsp;interaction&nbsp;record&nbsp;marker&nbsp;string&nbsp;(e.g.,&nbsp;Interaction.Label/flag1,flag2).<br>
+&nbsp;<br>
+Raises:<br>
+&nbsp;&nbsp;AssertionError:&nbsp;If&nbsp;one&nbsp;or&nbsp;more&nbsp;of&nbsp;the&nbsp;flags&nbsp;is&nbsp;unrecognized.</tt></dd></dl>
+ <dl><dt><a name="-IsTimelineInteractionRecord"><strong>IsTimelineInteractionRecord</strong></a>(event_name)</dt></dl>
+</td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>FLAGS</strong> = ['repeatable']<br>
+<strong>REPEATABLE</strong> = 'repeatable'</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.wpr.archive_info.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.wpr.archive_info.html
new file mode 100644
index 0000000..300d278
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.wpr.archive_info.html
@@ -0,0 +1,157 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module telemetry.wpr.archive_info</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.<a href="telemetry.wpr.html"><font color="#ffffff">wpr</font></a>.archive_info</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/wpr/archive_info.py">telemetry/wpr/archive_info.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2013&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="catapult_base.cloud_storage.html">catapult_base.cloud_storage</a><br>
+<a href="json.html">json</a><br>
+</td><td width="25%" valign=top><a href="logging.html">logging</a><br>
+<a href="os.html">os</a><br>
+</td><td width="25%" valign=top><a href="re.html">re</a><br>
+<a href="shutil.html">shutil</a><br>
+</td><td width="25%" valign=top><a href="tempfile.html">tempfile</a><br>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.wpr.archive_info.html#WprArchiveInfo">WprArchiveInfo</a>
+</font></dt></dl>
+</dd>
+<dt><font face="helvetica, arial"><a href="exceptions.html#Exception">exceptions.Exception</a>(<a href="exceptions.html#BaseException">exceptions.BaseException</a>)
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="telemetry.wpr.archive_info.html#ArchiveError">ArchiveError</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="ArchiveError">class <strong>ArchiveError</strong></a>(<a href="exceptions.html#Exception">exceptions.Exception</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt>Method resolution order:</dt>
+<dd><a href="telemetry.wpr.archive_info.html#ArchiveError">ArchiveError</a></dd>
+<dd><a href="exceptions.html#Exception">exceptions.Exception</a></dd>
+<dd><a href="exceptions.html#BaseException">exceptions.BaseException</a></dd>
+<dd><a href="__builtin__.html#object">__builtin__.object</a></dd>
+</dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<hr>
+Methods inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><a name="ArchiveError-__init__"><strong>__init__</strong></a>(...)</dt><dd><tt>x.<a href="#ArchiveError-__init__">__init__</a>(...)&nbsp;initializes&nbsp;x;&nbsp;see&nbsp;help(type(x))&nbsp;for&nbsp;signature</tt></dd></dl>
+
+<hr>
+Data and other attributes inherited from <a href="exceptions.html#Exception">exceptions.Exception</a>:<br>
+<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href="#ArchiveError-__new__">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>
+
+<hr>
+Methods inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><a name="ArchiveError-__delattr__"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href="#ArchiveError-__delattr__">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="ArchiveError-__getattribute__"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href="#ArchiveError-__getattribute__">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>
+
+<dl><dt><a name="ArchiveError-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#ArchiveError-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
+
+<dl><dt><a name="ArchiveError-__getslice__"><strong>__getslice__</strong></a>(...)</dt><dd><tt>x.<a href="#ArchiveError-__getslice__">__getslice__</a>(i,&nbsp;j)&nbsp;&lt;==&gt;&nbsp;x[i:j]<br>
+&nbsp;<br>
+Use&nbsp;of&nbsp;negative&nbsp;indices&nbsp;is&nbsp;not&nbsp;supported.</tt></dd></dl>
+
+<dl><dt><a name="ArchiveError-__reduce__"><strong>__reduce__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="ArchiveError-__repr__"><strong>__repr__</strong></a>(...)</dt><dd><tt>x.<a href="#ArchiveError-__repr__">__repr__</a>()&nbsp;&lt;==&gt;&nbsp;repr(x)</tt></dd></dl>
+
+<dl><dt><a name="ArchiveError-__setattr__"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href="#ArchiveError-__setattr__">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>
+
+<dl><dt><a name="ArchiveError-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
+
+<dl><dt><a name="ArchiveError-__str__"><strong>__str__</strong></a>(...)</dt><dd><tt>x.<a href="#ArchiveError-__str__">__str__</a>()&nbsp;&lt;==&gt;&nbsp;str(x)</tt></dd></dl>
+
+<dl><dt><a name="ArchiveError-__unicode__"><strong>__unicode__</strong></a>(...)</dt></dl>
+
+<hr>
+Data descriptors inherited from <a href="exceptions.html#BaseException">exceptions.BaseException</a>:<br>
+<dl><dt><strong>__dict__</strong></dt>
+</dl>
+<dl><dt><strong>args</strong></dt>
+</dl>
+<dl><dt><strong>message</strong></dt>
+</dl>
+</td></tr></table> <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="WprArchiveInfo">class <strong>WprArchiveInfo</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+    
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="WprArchiveInfo-AddNewTemporaryRecording"><strong>AddNewTemporaryRecording</strong></a>(self, temp_wpr_file_path<font color="#909090">=None</font>)</dt></dl>
+
+<dl><dt><a name="WprArchiveInfo-AddRecordedStories"><strong>AddRecordedStories</strong></a>(self, stories, upload_to_cloud_storage<font color="#909090">=False</font>)</dt></dl>
+
+<dl><dt><a name="WprArchiveInfo-DownloadArchivesIfNeeded"><strong>DownloadArchivesIfNeeded</strong></a>(self)</dt><dd><tt>Downloads&nbsp;archives&nbsp;iff&nbsp;the&nbsp;Archive&nbsp;has&nbsp;a&nbsp;bucket&nbsp;parameter&nbsp;and&nbsp;the&nbsp;user<br>
+has&nbsp;permission&nbsp;to&nbsp;access&nbsp;the&nbsp;bucket.<br>
+&nbsp;<br>
+Raises&nbsp;cloud&nbsp;storage&nbsp;Permissions&nbsp;or&nbsp;Credentials&nbsp;error&nbsp;when&nbsp;there&nbsp;is&nbsp;no<br>
+local&nbsp;copy&nbsp;of&nbsp;the&nbsp;archive&nbsp;and&nbsp;the&nbsp;user&nbsp;doesn't&nbsp;have&nbsp;permission&nbsp;to&nbsp;access<br>
+the&nbsp;archive's&nbsp;bucket.<br>
+&nbsp;<br>
+Warns&nbsp;when&nbsp;a&nbsp;bucket&nbsp;is&nbsp;not&nbsp;specified&nbsp;or&nbsp;when&nbsp;the&nbsp;user&nbsp;doesn't&nbsp;have<br>
+permission&nbsp;to&nbsp;access&nbsp;the&nbsp;archive's&nbsp;bucket&nbsp;but&nbsp;a&nbsp;local&nbsp;copy&nbsp;of&nbsp;the&nbsp;archive<br>
+exists.</tt></dd></dl>
+
+<dl><dt><a name="WprArchiveInfo-WprFilePathForStory"><strong>WprFilePathForStory</strong></a>(self, story)</dt></dl>
+
+<dl><dt><a name="WprArchiveInfo-__init__"><strong>__init__</strong></a>(self, file_path, data, bucket)</dt></dl>
+
+<hr>
+Class methods defined here:<br>
+<dl><dt><a name="WprArchiveInfo-FromFile"><strong>FromFile</strong></a>(cls, file_path, bucket)<font color="#909090"><font face="helvetica, arial"> from <a href="__builtin__.html#type">__builtin__.type</a></font></font></dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-AssertValidCloudStorageBucket"><strong>AssertValidCloudStorageBucket</strong></a>(bucket)</dt></dl>
+</td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.wpr.html b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.wpr.html
new file mode 100644
index 0000000..70e4a8b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/pydoc/telemetry.wpr.html
@@ -0,0 +1,26 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: package telemetry.wpr</title>
+<meta charset="utf-8">
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="telemetry.html"><font color="#ffffff">telemetry</font></a>.wpr</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="../telemetry/wpr/__init__.py">telemetry/wpr/__init__.py</a></font></td></tr></table>
+    <p><tt>#&nbsp;Copyright&nbsp;2014&nbsp;The&nbsp;Chromium&nbsp;Authors.&nbsp;All&nbsp;rights&nbsp;reserved.<br>
+#&nbsp;Use&nbsp;of&nbsp;this&nbsp;source&nbsp;code&nbsp;is&nbsp;governed&nbsp;by&nbsp;a&nbsp;BSD-style&nbsp;license&nbsp;that&nbsp;can&nbsp;be<br>
+#&nbsp;found&nbsp;in&nbsp;the&nbsp;LICENSE&nbsp;file.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Package Contents</strong></big></font></td></tr>
+    
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="telemetry.wpr.archive_info.html">archive_info</a><br>
+</td><td width="25%" valign=top><a href="telemetry.wpr.archive_info_unittest.html">archive_info_unittest</a><br>
+</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table>
+</body></html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/docs/run_benchmarks_locally.md b/sdk/platform-tools/systrace/catapult/telemetry/docs/run_benchmarks_locally.md
new file mode 100644
index 0000000..c59a4b0
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/docs/run_benchmarks_locally.md
@@ -0,0 +1,116 @@
+<!-- Copyright 2015 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file.
+-->
+
+# Telemetry: Run Benchmarks Locally
+
+## Set Up
+
+If you don't have a Chromium checkout, download the
+[latest Telemetry archive](https://storage.googleapis.com/chromium-telemetry/snapshots/telemetry.zip).
+Unzip the archive. If you're running on Mac OS X, you're all set! For
+Windows, Linux, Android, or Chrome OS, read on.
+
+#### Windows
+
+Some benchmarks require you to have
+[pywin32](http://sourceforge.net/projects/pywin32/files/pywin32/Build%20219/).
+Be sure to install a version that matches the version and bitness of the Python
+you have installed.
+
+#### Linux
+
+Telemetry on Linux tries to scan for attached Android devices with
+[adb](https://developer.android.com/tools/help/adb.html).
+The included adb binary is 32-bit. On 64-bit machines, you need to install the
+libstdc++6:i386 package.
+
+#### Android
+
+Running on Android is supported with a Linux or Mac OS X host. Windows is not
+yet supported. There are also a few additional steps to set up:
+
+  1. Telemetry requires [adb](http://developer.android.com/tools/help/adb.html).
+     If you're running from the zip archive, adb is already included. But if
+     you're running with a Chromium checkout, ensure your .gclient file contains
+     target\_os = ['android'], then resync your code.
+  2. If running from an OS X host, you need to run ADB as root. First, you need
+     to install a "userdebug" build of Android on your device. Then run adb
+     root. Sometimes you may also need to run adb remount.
+  3. Enable [debugging over USB](http://developer.android.com/tools/device.html)
+     on your device.
+  4. You can get the name of your device with `adb devices` and use it with
+     Telemetry via --device=<device\_name>.
+
+#### Chrome OS
+
+See [Running Telemetry on Chrome OS](http://www.chromium.org/developers/telemetry/running-telemetry-on-chrome-os).
+
+## Benchmark Commands
+
+Telemetry benchmarks can be run with run\_benchmark.
+
+In the Telemetry zip archive, this is located at `telemetry/run_benchmark`.
+
+In the Chromium source tree, this is located at `src/tools/perf/run_benchmark`.
+
+#### Running a benchmark
+
+List the available benchmarks with `telemetry/run_benchmark list`.
+
+Here's an example for running a particular benchmark:
+
+`telemetry/run_benchmark --browser=canary smoothness.top_25_smooth`
+
+#### Running on another browser
+
+To list available browsers, use:
+
+`telemetry/run_benchmark --browser=list`
+
+For ease of use, you can use default system browsers on desktop:
+
+`telemetry/run_benchmark --browser=system`
+
+and on Android:
+
+`telemetry/run_benchmark --browser=android-system-chrome`
+
+If you're running telemetry from within a Chromium checkout, the release and
+debug browsers are what's built in out/Release and out/Debug, respectively.
+
+To run a specific browser executable:
+
+`telemetry/run_benchmark --browser=exact --browser-executable=/path/to/binary`
+
+To run on a Chromebook:
+
+`telemetry/run_benchmark --browser=cros-chrome --remote=[ip_address]`
+
+#### Options
+
+To see all options, run:
+
+`telemetry/run_benchmark run --help`
+
+Use --pageset-repeat to run the test repeatedly. For example:
+
+`telemetry/run_benchmark smoothness.top_25 --pageset-repeat=30`
+
+If you want to re-generate HTML results and add label, you can do this locally
+by using the parameters `--reset-results --results-label="foo"`
+
+`telemetry/run_benchmark smoothness.top_25 --reset-results
+--results-label="foo"`
+
+####Comparing Two Runs
+
+`telemetry/run_benchmark some_test --browser-executable=path/to/version/1
+--reset-results --results-label="Version 1"`
+
+`telemetry/run_benchmark some_test --browser-executable=path/to/version/2
+--results-label="Version 2"`
+
+The results will be written to in the `results.html` file in the same location
+of the `run_benchmark` script.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/examples/benchmarks/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/examples/benchmarks/__init__.py
new file mode 100644
index 0000000..b7bc89c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/examples/benchmarks/__init__.py
@@ -0,0 +1,11 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import sys
+
+# Since this is an example of telemetry benchmarks in top level telemetry
+# folder, we include the top level telemetry dir to sys.path so we can import
+# from telemetry directly in the other modules.
+sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/examples/benchmarks/simple_story_set.py b/sdk/platform-tools/systrace/catapult/telemetry/examples/benchmarks/simple_story_set.py
new file mode 100644
index 0000000..f33434d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/examples/benchmarks/simple_story_set.py
@@ -0,0 +1,39 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry import story
+from telemetry import page
+
+
+class ExamplePage(page.Page):
+
+  def __init__(self, page_set):
+    super(ExamplePage, self).__init__(
+        url='https://google.com/search?q=lemon',
+        page_set=page_set)
+
+  def RunPageInteractions(self, action_runner):
+    # To see all the web APIs that action_runner supports, see:
+    # telemetry.page.action_runner module.
+
+    action_runner.Wait(0.5)
+    # Create interaction record will create a region of interest in tracing that
+    # cover the wait, tap, and scroll actions nested in the block below.
+    with action_runner.CreateInteraction('Scroll-And-Tap'):
+      action_runner.Wait(0.3)
+      action_runner.ScrollPage()
+      action_runner.TapElement(text='Next')
+    action_runner.Wait(1)
+    with action_runner.CreateInteraction('Scroll'):
+      action_runner.ScrollPage()
+    with action_runner.CreateInteraction('Wait-two'):
+      action_runner.Wait(1)
+
+
+class SimpleStorySet(story.StorySet):
+  def __init__(self):
+    super(SimpleStorySet, self).__init__(
+        archive_data_file='data/simple_story_set.json',
+        cloud_storage_bucket=story.PARTNER_BUCKET)
+    self.AddStory(ExamplePage(self))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/examples/benchmarks/tbm_benchmark.py b/sdk/platform-tools/systrace/catapult/telemetry/examples/benchmarks/tbm_benchmark.py
new file mode 100644
index 0000000..f7d1845
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/examples/benchmarks/tbm_benchmark.py
@@ -0,0 +1,21 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+from telemetry import benchmark
+from telemetry.web_perf import timeline_based_measurement
+
+from benchmarks import simple_story_set
+
+class TBMSample(benchmark.Benchmark):
+
+  def CreateStorySet(self, options):
+    return simple_story_set.SimpleStorySet()
+
+  def CreateTimelineBasedMeasurementOptions(self):
+    options = timeline_based_measurement.Options()
+    options.SetTimelineBasedMetrics(['sample_metric.html'])
+    return options
+
+  @classmethod
+  def Name(cls):
+    return 'tbm_sample.tbm_sample'
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/examples/browser_tests/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/examples/browser_tests/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/examples/browser_tests/__init__.py
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/examples/browser_tests/failed_tests.py b/sdk/platform-tools/systrace/catapult/telemetry/examples/browser_tests/failed_tests.py
new file mode 100644
index 0000000..516d181
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/examples/browser_tests/failed_tests.py
@@ -0,0 +1,47 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import sys
+
+from telemetry.testing import serially_executed_browser_test_case
+
+
+class SetUpClassFailedTest(
+    serially_executed_browser_test_case.SeriallyExecutedBrowserTestCase):
+
+  @classmethod
+  def setUpClass(cls):
+    raise Exception
+
+  @classmethod
+  def GenerateTestCases_DummyTest(cls, options):
+    del options  # Unused.
+    for i in xrange(0, 3):
+      yield 'dummy_test_%i' % i, ()
+
+  def DummyTest(self):
+    pass
+
+
+class TearDownClassFailedTest(
+    serially_executed_browser_test_case.SeriallyExecutedBrowserTestCase):
+
+  @classmethod
+  def tearDownClass(cls):
+    raise Exception
+
+  @classmethod
+  def GenerateTestCases_DummyTest(cls, options):
+    del options  # Unused.
+    for i in xrange(0, 3):
+      yield 'dummy_test_%i' % i, ()
+
+  def DummyTest(self):
+    pass
+
+
+def load_tests(loader, tests, pattern):
+  del loader, tests, pattern  # Unused.
+  return serially_executed_browser_test_case.LoadAllTestsInModule(
+      sys.modules[__name__])
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/examples/browser_tests/pages/page_with_clickables.html b/sdk/platform-tools/systrace/catapult/telemetry/examples/browser_tests/pages/page_with_clickables.html
new file mode 100644
index 0000000..e33c120
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/examples/browser_tests/pages/page_with_clickables.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+</head>
+<body>
+<div id="test">Click/tap me</div>
+<script>
+'use strict';
+var el = document.getElementById('test');
+var valueSettableByTest = 0;
+var valueToTest = 0;
+el.addEventListener('click', function() {
+  valueToTest = valueSettableByTest;
+});
+</script>
+</body>
+</html>
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/examples/browser_tests/pages/page_with_link.html b/sdk/platform-tools/systrace/catapult/telemetry/examples/browser_tests/pages/page_with_link.html
new file mode 100644
index 0000000..dc3c008
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/examples/browser_tests/pages/page_with_link.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+</head>
+<body>
+<a id="clickme" href="blank.html">Click me</a>
+</body>
+</html>
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/examples/browser_tests/process_tests.py b/sdk/platform-tools/systrace/catapult/telemetry/examples/browser_tests/process_tests.py
new file mode 100644
index 0000000..ada3517
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/examples/browser_tests/process_tests.py
@@ -0,0 +1,53 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import sys
+
+from telemetry.testing import serially_executed_browser_test_case
+
+
+class FailIfSetUpProcessCalledTwice(
+    serially_executed_browser_test_case.SeriallyExecutedBrowserTestCase):
+  count = 0
+
+  @classmethod
+  def SetUpProcess(cls):
+    cls.count += 1
+    if cls.count >= 2:
+      assert False, 'This should not be called more than once'
+
+  @classmethod
+  def GenerateTestCases_DummyTest(cls, options):
+    del options  # Unused.
+    for i in xrange(0, 3):
+      yield 'Dummy_%i' % i, ()
+
+  def DummyTest(self):
+    pass
+
+
+class FailIfTearDownProcessCalledTwice(
+    serially_executed_browser_test_case.SeriallyExecutedBrowserTestCase):
+  count = 0
+
+  @classmethod
+  def TearDownProcess(cls):
+    cls.count += 1
+    if cls.count >= 2:
+      assert False, 'This should not be called more than once'
+
+  @classmethod
+  def GenerateTestCases_DummyTest(cls, options):
+    del options  # Unused.
+    for i in xrange(0, 3):
+      yield 'Dummy_%i' % i, ()
+
+  def DummyTest(self):
+    pass
+
+
+def load_tests(loader, tests, pattern):
+  del loader, tests, pattern  # Unused.
+  return serially_executed_browser_test_case.LoadAllTestsInModule(
+      sys.modules[__name__])
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/examples/browser_tests/sample_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/examples/browser_tests/sample_unittest.py
new file mode 100644
index 0000000..fcae258
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/examples/browser_tests/sample_unittest.py
@@ -0,0 +1,12 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+class SampleUnittest(unittest.TestCase):
+  def testPassing(self):
+    self.assertTrue(1 + 1)
+
+  def testFailing(self):
+    self.assertFalse(True or False)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/examples/browser_tests/simple_browser_test.py b/sdk/platform-tools/systrace/catapult/telemetry/examples/browser_tests/simple_browser_test.py
new file mode 100644
index 0000000..82df3aa
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/examples/browser_tests/simple_browser_test.py
@@ -0,0 +1,75 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import sys
+import os
+
+from telemetry.testing import serially_executed_browser_test_case
+
+
+def ConvertPathToTestName(url):
+  return url.replace('.', '_')
+
+
+class SimpleBrowserTest(
+    serially_executed_browser_test_case.SeriallyExecutedBrowserTestCase):
+
+  @classmethod
+  def GenerateTestCases_JavascriptTest(cls, options):
+    del options  # unused
+    for path in ['page_with_link.html', 'page_with_clickables.html']:
+      yield 'add_1_and_2_' + ConvertPathToTestName(path), (path, 1, 2, 3)
+
+  @classmethod
+  def SetUpProcess(cls):
+    super(cls, SimpleBrowserTest).SetUpProcess()
+    cls.SetBrowserOptions(cls._finder_options)
+    cls.StartBrowser()
+    cls.action_runner = cls.browser.tabs[0].action_runner
+    cls.SetStaticServerDirs(
+        [os.path.join(os.path.abspath(__file__), '..', 'pages')])
+
+  def JavascriptTest(self, file_path, num_1, num_2, expected_sum):
+    url = self.UrlOfStaticFilePath(file_path)
+    self.action_runner.Navigate(url)
+    actual_sum = self.action_runner.EvaluateJavaScript(
+        '{{ num_1 }} + {{ num_2 }}', num_1=num_1, num_2=num_2)
+    self.assertEquals(expected_sum, actual_sum)
+
+  def TestClickablePage(self):
+    url = self.UrlOfStaticFilePath('page_with_clickables.html')
+    self.action_runner.Navigate(url)
+    self.action_runner.ExecuteJavaScript('valueSettableByTest = 1997')
+    self.action_runner.ClickElement(text='Click/tap me')
+    self.assertEqual(
+        1997, self.action_runner.EvaluateJavaScript('valueToTest'))
+
+  def TestAndroidUI(self):
+    if self.platform.GetOSName() != 'android':
+      self.skipTest('The test is for android only')
+    url = self.UrlOfStaticFilePath('page_with_clickables.html')
+    # Nativgate to page_with_clickables.html
+    self.action_runner.Navigate(url)
+    # Click on history
+    self.platform.system_ui.WaitForUiNode(
+        resource_id='com.google.android.apps.chrome:id/menu_button')
+    self.platform.system_ui.GetUiNode(
+        resource_id='com.google.android.apps.chrome:id/menu_button').Tap()
+    self.platform.system_ui.WaitForUiNode(content_desc='History')
+    self.platform.system_ui.GetUiNode(content_desc='History').Tap()
+    # Click on the first entry of the history (page_with_clickables.html)
+    self.action_runner.WaitForElement('#id-0')
+    self.action_runner.ClickElement('#id-0')
+    # Verify that the page's js is interactable
+    self.action_runner.WaitForElement(text='Click/tap me')
+    self.action_runner.ExecuteJavaScript('valueSettableByTest = 1997')
+    self.action_runner.ClickElement(text='Click/tap me')
+    self.assertEqual(
+        1997, self.action_runner.EvaluateJavaScript('valueToTest'))
+
+
+def load_tests(loader, tests, pattern):
+  del loader, tests, pattern  # Unused.
+  return serially_executed_browser_test_case.LoadAllTestsInModule(
+      sys.modules[__name__])
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/examples/browser_tests/simple_numeric_test.py b/sdk/platform-tools/systrace/catapult/telemetry/examples/browser_tests/simple_numeric_test.py
new file mode 100644
index 0000000..e301a1b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/examples/browser_tests/simple_numeric_test.py
@@ -0,0 +1,82 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import string
+import sys
+import time
+
+from telemetry.testing import serially_executed_browser_test_case
+
+
+_prev_test_name = None
+
+class SimpleTest(
+    serially_executed_browser_test_case.SeriallyExecutedBrowserTestCase):
+
+  @classmethod
+  def AddCommandlineArgs(cls, parser):
+    parser.add_option('--adder-sum', type=int, default=5)
+
+  def setUp(self):
+    self.extra = 5
+
+  @classmethod
+  def GenerateTestCases_AdderTest(cls, options):
+    yield 'add_1_and_2', (1, 2, options.adder_sum)
+    yield 'add_2_and_3', (2, 3, options.adder_sum)
+    yield 'add_7_and_3', (7, 3, options.adder_sum)
+    # Filtered out in browser_test_runner_unittest.py
+    yield 'dontrun_add_1_and_2', (1, 2, options.adder_sum)
+
+  @classmethod
+  def GenerateTestCases_AlphabeticalTest(cls, options):
+    del options  # unused
+    prefix = 'Alphabetical_'
+    test_names = []
+    for character in string.lowercase[:26]:
+      test_names.append(prefix + character)
+    for character in string.uppercase[:26]:
+      test_names.append(prefix + character)
+    for num in xrange(20):
+      test_names.append(prefix + str(num))
+
+    # Shuffle |test_names| so the tests will be generated in a random order.
+    test_names = (test_names[25:40] + test_names[40:70] + test_names[:25] +
+                  test_names[70:])
+    for t in test_names:
+      yield t, ()
+
+  def AlphabeticalTest(self):
+    test_name = self.id()
+    global _prev_test_name
+    self.assertLess(_prev_test_name, test_name)
+    _prev_test_name = test_name
+
+  def AdderTest(self, a, b, partial_sum):
+    self.assertEqual(a + b, partial_sum)
+
+  @classmethod
+  def GenerateTestCases_MultiplierTest(cls, options):
+    del options  # unused
+    yield 'multiplier_simple', (10, 2, 4)
+    yield 'multiplier_simple_2', (2, 3, 5)
+    yield 'multiplier_simple_3', (10, 3, 6)
+    # Filtered out in browser_test_runner_unittest.py
+    yield 'dontrun_multiplier_simple', (10, 2, 4)
+
+  def MultiplierTest(self, a, b, partial_sum):
+    self.assertEqual(a * b, partial_sum * self.extra)
+
+  def TestSimple(self):
+    time.sleep(0.5)
+    self.assertEqual(1, self.extra)
+
+  def TestException(self):
+    raise Exception('Expected exception')
+
+
+def load_tests(loader, tests, pattern):
+  del loader, tests, pattern  # Unused.
+  return serially_executed_browser_test_case.LoadAllTestsInModule(
+      sys.modules[__name__])
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/examples/browser_tests/simple_sharding_test.py b/sdk/platform-tools/systrace/catapult/telemetry/examples/browser_tests/simple_sharding_test.py
new file mode 100644
index 0000000..fdb78a9
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/examples/browser_tests/simple_sharding_test.py
@@ -0,0 +1,35 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import sys
+
+from telemetry.testing import serially_executed_browser_test_case
+
+
+class SimpleShardingTest(
+    serially_executed_browser_test_case.SeriallyExecutedBrowserTestCase):
+
+  def Test1(self):
+    pass
+
+  def Test2(self):
+    pass
+
+  def Test3(self):
+    pass
+
+  @classmethod
+  def GenerateTestCases_PassingTest(cls, options):
+    del options  # unused
+    for i in xrange(10):
+      yield 'passing_test_' + str(i), (i,)
+
+  def PassingTest(self, a):
+    self.assertGreaterEqual(a, 0)
+
+
+def load_tests(loader, tests, pattern):
+  del loader, tests, pattern  # Unused.
+  return serially_executed_browser_test_case.LoadAllTestsInModule(
+      sys.modules[__name__])
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/examples/credentials_example.json b/sdk/platform-tools/systrace/catapult/telemetry/examples/credentials_example.json
new file mode 100644
index 0000000..b3978b6
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/examples/credentials_example.json
@@ -0,0 +1,10 @@
+{
+  "google": {
+    "username": "<your google account here>",
+    "password": "<your google password here>"
+  },
+  "facebook": {
+    "username": "<your google account here>",
+    "password": "<your google password here>"
+  }
+}
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/examples/run_benchmark b/sdk/platform-tools/systrace/catapult/telemetry/examples/run_benchmark
new file mode 100755
index 0000000..f24a386
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/examples/run_benchmark
@@ -0,0 +1,20 @@
+#!/usr/bin/env python
+# Copyright (c) 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import os
+import sys
+
+sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
+from telemetry import benchmark_runner
+from telemetry import project_config
+
+def main():
+  top_level_dir = os.path.dirname(__file__)
+  config = project_config.ProjectConfig(
+      top_level_dir=top_level_dir,
+      benchmark_dirs=[os.path.join(top_level_dir, 'benchmarks')])
+  return benchmark_runner.main(config)
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/json_format b/sdk/platform-tools/systrace/catapult/telemetry/json_format
new file mode 100755
index 0000000..401594d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/json_format
@@ -0,0 +1,58 @@
+#!/usr/bin/env python
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import argparse
+import json
+import os
+import sys
+
+
+def GetFormattedJSONString(file_path):
+ with open(file_path, 'r') as f:
+    json_obj = json.load(f)
+    file_content = f.read()
+ return json.dumps(
+     json_obj, indent=2, sort_keys=True, separators=(',', ': '))
+
+
+def ValidateJSONFormat(file_path):
+ with open(file_path, 'r') as f:
+    file_content = f.read()
+ if file_content != GetFormattedJSONString(file_path):
+   raise Exception(
+       'Reformat your JSON file by running: %s --format %s' %
+       (__file__, file_path))
+ print >> sys.stdout, ('%s passes the JSON format validation' % file_path)
+
+
+def Format(file_path):
+  formatted_JSON_string = GetFormattedJSONString(file_path)
+  with open(file_path, 'w') as f:
+    f.write(formatted_JSON_string)
+
+
+def Main(args):
+  description = """A JSON formatting tool.
+
+  This is a tool that validate and reformats JSON file so that it complies with
+  a certain style. The JSON style imposed by this tool is:
+    * JSON array elements and object members are indented with 2 spaces.
+    * Dictionaries objects are sorted by key.
+    * Items are sperated by ', ' and ': '.
+  """
+  parser = argparse.ArgumentParser(
+      description=description, formatter_class=argparse.RawTextHelpFormatter)
+  parser.add_argument('file_path', type=str, help='The path to JSON file.')
+  parser.add_argument('--format', action='store_true', default=False,
+                      help='Format the JSON file.')
+  options = parser.parse_args(args)
+  if options.format:
+    Format(options.file_path)
+    return 0
+  ValidateJSONFormat(options.file_path)
+  return 0
+
+
+if __name__ == '__main__':
+  sys.exit(Main(sys.argv[1:]))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/list_telemetry_unittests b/sdk/platform-tools/systrace/catapult/telemetry/list_telemetry_unittests
new file mode 100755
index 0000000..8f420c6
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/list_telemetry_unittests
@@ -0,0 +1,70 @@
+#!/usr/bin/env python
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import argparse
+import sys
+
+
+def _ExtractQueuedTestName(line):
+  _, test_name, _ = line.split(' ')
+  return test_name
+
+
+def _ExtractPassedTestNameAndTime(line):
+  _, test_name, _, test_time_string = line.split(' ')
+  if test_time_string.endswith(':'):
+    test_time = float(test_time_string[:-2])
+  else:
+    test_time = float(test_time_string[:-1])
+  return test_name, test_time
+
+
+def _IsQueued(line):
+  return line.endswith(' queued')
+
+
+def _IsPassed(line):
+  return 'passed' in line.split(' ')
+
+
+def _ProcessLogFile(file_path):
+  passed_unittests = []
+  queued_unittests = []
+  with open(file_path, 'r') as f:
+    for line in f:
+      line = line.strip()
+      if not line.startswith('['):
+        continue
+      if _IsQueued(line):
+        queued_unittests.append(_ExtractQueuedTestName(line))
+      elif _IsPassed(line):
+        passed_unittests.append(_ExtractPassedTestNameAndTime(line))
+  queued_unittests.sort()
+  passed_unittests.sort(key=lambda v: -v[1])
+  return queued_unittests, passed_unittests
+
+
+def main(args):
+  parser = argparse.ArgumentParser(
+      description=('Process telemetry unittests log to print out passed '
+                   'or queued tests.'))
+  parser.add_argument(
+      'filepath', help='path to log file of telemetry unittest')
+  parser.add_argument(
+      '-q', '--list-queued-tests', action='store_true',
+      help='Also list all the queued telemetry unittests')
+  options = parser.parse_args(args)
+  queued_unittests, passed_unittests = _ProcessLogFile(options.filepath)
+  print 'All passed telemetry unittests:\n'
+  for test, time in passed_unittests:
+      print test, time
+  if options.list_queued_tests:
+    print 'All queued telemetry unittests:\n'
+    print '\n'.join(queued_unittests)
+  return 0
+
+
+if __name__ == '__main__':
+  sys.exit(main(sys.argv[1:]))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/pylintrc b/sdk/platform-tools/systrace/catapult/telemetry/pylintrc
new file mode 100644
index 0000000..7be28e2
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/pylintrc
@@ -0,0 +1,75 @@
+[MESSAGES CONTROL]
+
+# Disable the message, report, category or checker with the given id(s).
+# TODO(telemetry-team): Shrink this list to as small as possible.
+disable=
+  design,
+  similarities,
+
+  abstract-class-little-used,
+  abstract-class-not-used,
+  bad-builtin,
+  bad-continuation,
+  broad-except,
+  fixme,
+  global-statement,
+  interface-not-implemented,
+  invalid-name,
+  locally-disabled,
+  locally-enabled,
+  logging-not-lazy,
+  missing-docstring,
+  no-member,
+  no-self-use,
+  protected-access,
+  star-args,
+  too-many-function-args
+
+
+[REPORTS]
+
+# Don't write out full reports, just messages.
+reports=no
+
+
+[BASIC]
+
+# Regular expression which should only match correct function names.
+function-rgx=^(?:(?P<exempt>setUp|tearDown|setUpModule|tearDownModule)|(?P<camel_case>_?[A-Z][a-zA-Z0-9]*))$
+
+# Regular expression which should only match correct method names.
+method-rgx=^(?:(?P<exempt>_[a-z0-9_]+__|get|post|runTest|setUp|tearDown|setUpTestCase|tearDownTestCase|setupSelf|tearDownClass|setUpClass)|(?P<camel_case>(_{0,2}|test|assert)[A-Z][a-zA-Z0-9_]*))$
+
+# Regular expression which should only match correct argument names.
+argument-rgx=^[a-z][a-z0-9_]*$
+
+# Regular expression which should only match correct variable names.
+variable-rgx=^[a-z][a-z0-9_]*$
+
+# Good variable names which should always be accepted, separated by a comma.
+good-names=main,_
+
+# List of builtins function names that should not be used, separated by a comma.
+bad-functions=apply,input,reduce
+
+
+[VARIABLES]
+
+# Tells wether we should check for unused import in __init__ files.
+init-import=no
+
+# A regular expression matching names used for dummy variables (i.e. not used).
+dummy-variables-rgx=^\*{0,2}(_$|unused_)
+
+
+[TYPECHECK]
+
+# Tells wether missing members accessed in mixin class should be ignored. A
+# mixin class is detected if its name ends with "mixin" (case insensitive).
+ignore-mixin-members=yes
+
+
+[FORMAT]
+
+# We use two spaces for indents, instead of the usual four spaces or tab.
+indent-string='  '
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/support/html_output/results-template.html b/sdk/platform-tools/systrace/catapult/telemetry/support/html_output/results-template.html
new file mode 100644
index 0000000..a6a5655
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/support/html_output/results-template.html
@@ -0,0 +1,1488 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Telemetry Performance Test Results</title>
+<style type="text/css">
+
+section {
+    background: white;
+    padding: 10px;
+    position: relative;
+}
+
+.collapsed:before {
+    color: #ccc;
+    content: '\25B8\00A0';
+}
+
+.expanded:before {
+    color: #eee;
+    content: '\25BE\00A0';
+}
+
+.line-plots {
+    padding-left: 25px;
+}
+
+.line-plots > div {
+    display: inline-block;
+    width: 90px;
+    height: 40px;
+    margin-right: 10px;
+}
+
+.lage-line-plots {
+    padding-left: 25px;
+}
+
+.large-line-plots > div, .histogram-plots > div {
+    display: inline-block;
+    width: 400px;
+    height: 200px;
+    margin-right: 10px;
+}
+
+.large-line-plot-labels > div, .histogram-plot-labels > div {
+    display: inline-block;
+    width: 400px;
+    height: 11px;
+    margin-right: 10px;
+    color: #545454;
+    text-align: center;
+    font-size: 11px;
+}
+
+.closeButton {
+    display: inline-block;
+    background: #eee;
+    background: linear-gradient(rgb(220, 220, 220), rgb(255, 255, 255));
+    border: inset 1px #ddd;
+    border-radius: 4px;
+    float: right;
+    font-size: small;
+    -webkit-user-select: none;
+    font-weight: bold;
+    padding: 1px 4px;
+}
+
+.closeButton:hover {
+    background: #F09C9C;
+}
+
+.label {
+    cursor: text;
+}
+
+.label:hover {
+    background: #ffcc66;
+}
+
+section h1 {
+    text-align: center;
+    font-size: 1em;
+}
+
+section .tooltip {
+    position: absolute;
+    text-align: center;
+    background: #ffcc66;
+    border-radius: 5px;
+    padding: 0px 5px;
+}
+
+body {
+    padding: 0px;
+    margin: 0px;
+    font-family: sans-serif;
+}
+
+table {
+    background: white;
+    width: 100%;
+}
+
+table, td, th {
+    border-collapse: collapse;
+    padding: 5px;
+    white-space: nowrap;
+}
+
+.highlight:hover {
+   color: #202020;
+   background: #e0e0e0;
+}
+
+.nestedRow {
+    background: #f8f8f8;
+}
+
+.importantNestedRow {
+    background: #e0e0e0;
+    font-weight: bold;
+}
+
+table td {
+    position: relative;
+}
+
+th, td {
+    cursor: pointer;
+    cursor: hand;
+}
+
+th {
+    background: #e6eeee;
+    background: linear-gradient(rgb(244, 244, 244), rgb(217, 217, 217));
+    border: 1px solid #ccc;
+}
+
+th.sortUp:after {
+    content: ' \25BE';
+}
+
+th.sortDown:after {
+    content: ' \25B4';
+}
+
+td.comparison, td.result {
+    text-align: right;
+}
+
+td.better {
+    color: #6c6;
+}
+
+td.fadeOut {
+    opacity: 0.5;
+}
+
+td.unknown {
+    color: #ccc;
+}
+
+td.worse {
+    color: #c66;
+}
+
+td.reference {
+    font-style: italic;
+    font-weight: bold;
+    color: #444;
+}
+
+td.missing {
+    color: #ccc;
+    text-align: center;
+}
+
+td.missingReference {
+    color: #ccc;
+    text-align: center;
+    font-style: italic;
+}
+
+.checkbox {
+    display: inline-block;
+    background: #eee;
+    background: linear-gradient(rgb(220, 220, 220), rgb(200, 200, 200));
+    border: inset 1px #ddd;
+    border-radius: 5px;
+    margin: 10px;
+    font-size: small;
+    cursor: pointer;
+    cursor: hand;
+    -webkit-user-select: none;
+    font-weight: bold;
+}
+
+.checkbox span {
+    display: inline-block;
+    line-height: 100%;
+    padding: 5px 8px;
+    border: outset 1px transparent;
+}
+
+.checkbox .checked {
+    background: #e6eeee;
+    background: linear-gradient(rgb(255, 255, 255), rgb(235, 235, 235));
+    border: outset 1px #eee;
+    border-radius: 5px;
+}
+
+.openAllButton {
+    display: inline-block;
+    colour: #6c6
+    background: #eee;
+    background: linear-gradient(rgb(220, 220, 220), rgb(255, 255, 255));
+    border: inset 1px #ddd;
+    border-radius: 5px;
+    float: left;
+    font-size: small;
+    -webkit-user-select: none;
+    font-weight: bold;
+    padding: 1px 4px;
+}
+
+.openAllButton:hover {
+    background: #60f060;
+}
+
+.closeAllButton {
+    display: inline-block;
+    colour: #c66
+    background: #eee;
+    background: linear-gradient(rgb(220, 220, 220),rgb(255, 255, 255));
+    border: inset 1px #ddd;
+    border-radius: 5px;
+    float: left;
+    font-size: small;
+    -webkit-user-select: none;
+    font-weight: bold;
+    padding: 1px 4px;
+}
+
+.closeAllButton:hover {
+    background: #f04040;
+}
+
+</style>
+</head>
+<body onload="init()">
+<div style="padding: 0 10px; white-space: nowrap;">
+Result <span id="time-memory" class="checkbox"></span>
+Reference <span id="reference" class="checkbox"></span>
+Style <span id="scatter-line" class="checkbox"><span class="checked">Scatter</span><span>Line</span></span>
+<span class="checkbox"><span class="checked" id="undelete">Undelete</span></span><br>
+Run your test with --reset-results to clear all runs
+</div>
+<table id="container"></table>
+<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script>
+<script>
+%plugins%
+</script>
+<script>
+
+var EXPANDED = true;
+var COLLAPSED = false;
+var SMALLEST_PERCENT_DISPLAYED = 0.01;
+var INVISIBLE = false;
+var VISIBLE = true;
+var COMPARISON_SUFFIX = '_compare';
+var SORT_DOWN_CLASS = 'sortDown';
+var SORT_UP_CLASS = 'sortUp';
+var BETTER_CLASS = 'better';
+var WORSE_CLASS = 'worse';
+var UNKNOWN_CLASS = 'unknown'
+// px Indentation for graphs
+var GRAPH_INDENT = 64;
+var PADDING_UNDER_GRAPH = 5;
+// px Indentation for nested children left-margins
+var INDENTATION = 40;
+
+function TestResult(metric, values, associatedRun, std, degreesOfFreedom) {
+    if (values) {
+        if (values[0] instanceof Array) {
+            var flattenedValues = [];
+            for (var i = 0; i < values.length; i++)
+                flattenedValues = flattenedValues.concat(values[i]);
+            values = flattenedValues;
+        }
+
+        if (jQuery.type(values[0]) === 'string') {
+            try {
+                var current = JSON.parse(values[0]);
+                if (current.params.type === 'HISTOGRAM') {
+                    this.histogramValues = current;
+                    // Histogram results have no values (per se). Instead we calculate
+                    // the values from the histogram bins.
+                    var values = [];
+                    var buckets = current.buckets
+                    for (var i = 0; i < buckets.length; i++) {
+                        var bucket = buckets[i];
+                        var bucket_mean = (bucket.high + bucket.low) / 2;
+                        for (var b = 0; b < bucket.count; b++) {
+                            values.push(bucket_mean);
+                        }
+                    }
+                }
+            }
+            catch (e) {
+                console.error(e, e.stack);
+            }
+        }
+    } else {
+        values = [];
+    }
+
+    this.test = function() { return metric; }
+    this.values = function() { return values.map(function(value) { return metric.scalingFactor() * value; }); }
+    this.unscaledMean = function() { return Statistics.sum(values) / values.length; }
+    this.mean = function() { return metric.scalingFactor() * this.unscaledMean(); }
+    this.min = function() { return metric.scalingFactor() * Statistics.min(values); }
+    this.max = function() { return metric.scalingFactor() * Statistics.max(values); }
+    this.confidenceIntervalDelta = function() {
+        if (std !== undefined) {
+            return metric.scalingFactor() * Statistics.confidenceIntervalDeltaFromStd(0.95, values.length,
+                std, degreesOfFreedom);
+        }
+        return metric.scalingFactor() * Statistics.confidenceIntervalDelta(0.95, values.length,
+            Statistics.sum(values), Statistics.squareSum(values));
+    }
+    this.confidenceIntervalDeltaRatio = function() { return this.confidenceIntervalDelta() / this.mean(); }
+    this.percentDifference = function(other) {
+        if (other === undefined) {
+            return undefined;
+        }
+        return (other.unscaledMean() - this.unscaledMean()) / this.unscaledMean();
+    }
+    this.isStatisticallySignificant = function(other) {
+        if (other === undefined) {
+            return false;
+        }
+        var diff = Math.abs(other.mean() - this.mean());
+        return diff > this.confidenceIntervalDelta() && diff > other.confidenceIntervalDelta();
+    }
+    this.run = function() { return associatedRun; }
+}
+
+function TestRun(entry) {
+    this.id = function() { return entry['buildTime'].replace(/[:.-]/g,''); }
+    this.label = function() {
+        if (labelKey in localStorage)
+            return localStorage[labelKey];
+        return entry['label'];
+    }
+    this.setLabel = function(label) { localStorage[labelKey] = label; }
+    this.isHidden = function() { return localStorage[hiddenKey]; }
+    this.hide = function() { localStorage[hiddenKey] = true; }
+    this.show = function() { localStorage.removeItem(hiddenKey); }
+    this.description = function() {
+        return new Date(entry['buildTime']).toLocaleString() + '\n' + entry['platform'] + ' ' + this.label();
+    }
+
+    var labelKey = 'telemetry_label_' + this.id();
+    var hiddenKey = 'telemetry_hide_' + this.id();
+}
+
+function PerfTestMetric(name, metric, unit, isImportant) {
+    var testResults = [];
+    var cachedUnit = null;
+    var cachedScalingFactor = null;
+
+    // We can't do this in TestResult because all results for each test need to share the same unit and the same scaling factor.
+    function computeScalingFactorIfNeeded() {
+        // FIXME: We shouldn't be adjusting units on every test result.
+        // We can only do this on the first test.
+        if (!testResults.length || cachedUnit)
+            return;
+
+        var mean = testResults[0].unscaledMean(); // FIXME: We should look at all values.
+        var kilo = unit == 'bytes' ? 1024 : 1000;
+        if (mean > 10 * kilo * kilo && unit != 'ms') {
+            cachedScalingFactor = 1 / kilo / kilo;
+            cachedUnit = 'M ' + unit;
+        } else if (mean > 10 * kilo) {
+            cachedScalingFactor = 1 / kilo;
+            cachedUnit = unit == 'ms' ? 's' : ('K ' + unit);
+        } else {
+            cachedScalingFactor = 1;
+            cachedUnit = unit;
+        }
+    }
+
+    this.name = function() { return name + ':' + metric; }
+    this.isImportant = isImportant;
+    this.isMemoryTest = function() {
+        return (unit == 'kb' ||
+                unit == 'KB' ||
+                unit == 'MB' ||
+                unit == 'bytes' ||
+                unit == 'count' ||
+                !metric.indexOf('V8.'));
+    }
+    this.addResult = function(newResult) {
+        testResults.push(newResult);
+        cachedUnit = null;
+        cachedScalingFactor = null;
+    }
+    this.results = function() { return testResults; }
+    this.scalingFactor = function() {
+        computeScalingFactorIfNeeded();
+        return cachedScalingFactor;
+    }
+    this.unit = function() {
+        computeScalingFactorIfNeeded();
+        return cachedUnit;
+    }
+    this.biggerIsBetter = function() {
+        if (window.unitToBiggerIsBetter == undefined) {
+            window.unitToBiggerIsBetter = {};
+            var units = JSON.parse(document.getElementById('units-json').textContent);
+            for (var u in units) {
+                if (units[u].improvement_direction == 'up') {
+                    window.unitToBiggerIsBetter[u] = true;
+                }
+            }
+        }
+        return window.unitToBiggerIsBetter[unit];
+    }
+}
+
+function UndeleteManager() {
+    var key = 'telemetry_undeleteIds'
+    var undeleteIds = localStorage[key];
+    if (undeleteIds) {
+        undeleteIds = JSON.parse(undeleteIds);
+    } else {
+        undeleteIds = [];
+    }
+
+    this.ondelete = function(id) {
+        undeleteIds.push(id);
+        localStorage[key] = JSON.stringify(undeleteIds);
+    }
+    this.undeleteMostRecent = function() {
+        if (!this.mostRecentlyDeletedId())
+            return;
+        undeleteIds.pop();
+        localStorage[key] = JSON.stringify(undeleteIds);
+    }
+    this.mostRecentlyDeletedId = function() {
+        if (!undeleteIds.length)
+            return undefined;
+        return undeleteIds[undeleteIds.length-1];
+    }
+}
+var undeleteManager = new UndeleteManager();
+
+var plotColor = 'rgb(230,50,50)';
+var subpointsPlotOptions = {
+    lines: {show:true, lineWidth: 0},
+    color: plotColor,
+    points: {show: true, radius: 1},
+    bars: {show: false}};
+
+var mainPlotOptions = {
+    xaxis: {
+        min: -0.5,
+        tickSize: 1,
+    },
+    crosshair: { mode: 'y' },
+    series: { shadowSize: 0 },
+    bars: {show: true, align: 'center', barWidth: 0.5},
+    lines: { show: false },
+    points: { show: true },
+    grid: {
+        borderWidth: 1,
+        borderColor: '#ccc',
+        backgroundColor: '#fff',
+        hoverable: true,
+        autoHighlight: false,
+    }
+};
+
+var linePlotOptions = {
+    yaxis: { show: false },
+    xaxis: { show: false },
+    lines: { show: true },
+    grid: { borderWidth: 1, borderColor: '#ccc' },
+    colors: [ plotColor ]
+};
+
+var largeLinePlotOptions = {
+    xaxis: {
+        show: true,
+        tickDecimals: 0,
+    },
+    lines: { show: true },
+    grid: { borderWidth: 1, borderColor: '#ccc' },
+    colors: [ plotColor ]
+};
+
+var histogramPlotOptions = {
+    bars: {show: true, fill: 1}
+};
+
+function createPlot(container, test, useLargeLinePlots) {
+    if (test.results()[0].histogramValues) {
+        var section = $('<section><div class="histogram-plots"></div>'
+            + '<div class="histogram-plot-labels"></div>'
+            + '<span class="tooltip"></span></section>');
+        $(container).append(section);
+        attachHistogramPlots(test, section.children('.histogram-plots'));
+    }
+    else if (useLargeLinePlots) {
+        var section = $('<section><div class="large-line-plots"></div>'
+            + '<div class="large-line-plot-labels"></div>'
+            + '<span class="tooltip"></span></section>');
+        $(container).append(section);
+        attachLinePlots(test, section.children('.large-line-plots'), useLargeLinePlots);
+        attachLinePlotLabels(test, section.children('.large-line-plot-labels'));
+    } else {
+        var section = $('<section><div class="plot"></div><div class="line-plots"></div>'
+            + '<span class="tooltip"></span></section>');
+        section.children('.plot').css({'width': (100 * test.results().length + 25) + 'px', 'height': '300px'});
+        $(container).append(section);
+
+        var plotContainer = section.children('.plot');
+        var minIsZero = true;
+        attachPlot(test, plotContainer, minIsZero);
+
+        attachLinePlots(test, section.children('.line-plots'), useLargeLinePlots);
+
+        var tooltip = section.children('.tooltip');
+        plotContainer.bind('plothover', function(event, position, item) {
+            if (item) {
+                var postfix = item.series.id ? ' (' + item.series.id + ')' : '';
+                tooltip.html(item.datapoint[1].toPrecision(4) + postfix);
+                var sectionOffset = $(section).offset();
+                tooltip.css({left: item.pageX - sectionOffset.left - tooltip.outerWidth() / 2, top: item.pageY - sectionOffset.top + 10});
+                tooltip.fadeIn(200);
+            } else
+                tooltip.hide();
+        });
+        plotContainer.mouseout(function() {
+            tooltip.hide();
+        });
+        plotContainer.click(function(event) {
+            event.preventDefault();
+            minIsZero = !minIsZero;
+            attachPlot(test, plotContainer, minIsZero);
+        });
+    }
+    return section;
+}
+
+function attachLinePlots(test, container, useLargeLinePlots) {
+    var results = test.results();
+    var attachedPlot = false;
+
+    if (useLargeLinePlots) {
+        var maximum = 0;
+        for (var i = 0; i < results.length; i++) {
+            var values = results[i].values();
+            if (!values)
+                continue;
+            var local_max = Math.max.apply(Math, values);
+            if (local_max > maximum)
+                maximum = local_max;
+        }
+    }
+
+    for (var i = 0; i < results.length; i++) {
+        container.append('<div></div>');
+        var values = results[i].values();
+        if (!values)
+            continue;
+        attachedPlot = true;
+
+        if (useLargeLinePlots) {
+            var options = $.extend(true, {}, largeLinePlotOptions,
+                               {yaxis: {min: 0.0, max: maximum},
+                                xaxis: {min: 0.0, max: values.length - 1},
+                                points: {show: (values.length < 2) ? true : false}});
+        } else {
+            var options = $.extend(true, {}, linePlotOptions,
+                               {yaxis: {min: Math.min.apply(Math, values) * 0.9, max: Math.max.apply(Math, values) * 1.1},
+                                xaxis: {min: -0.5, max: values.length - 0.5},
+                                points: {show: (values.length < 2) ? true : false}});
+        }
+        $.plot(container.children().last(), [values.map(function(value, index) { return [index, value]; })], options);
+    }
+    if (!attachedPlot)
+        container.children().remove();
+}
+
+function attachHistogramPlots(test, container) {
+    var results = test.results();
+    var attachedPlot = false;
+
+    for (var i = 0; i < results.length; i++) {
+        container.append('<div></div>');
+        var histogram = results[i].histogramValues
+        if (!histogram)
+            continue;
+        attachedPlot = true;
+
+        var buckets = histogram.buckets
+        var bucket;
+        var max_count = 0;
+        for (var j = 0; j < buckets.length; j++) {
+            bucket = buckets[j];
+            max_count = Math.max(max_count, bucket.count);
+        }
+        var xmax = bucket.high * 1.1;
+        var ymax = max_count * 1.1;
+
+        var options = $.extend(true, {}, histogramPlotOptions,
+                           {yaxis: {min: 0.0, max: ymax},
+                            xaxis: {min: histogram.params.min, max: xmax}});
+        var plot = $.plot(container.children().last(), [[]], options);
+        // Flot only supports fixed with bars and our histogram's buckets are
+        // variable width, so we need to do our own bar drawing.
+        var ctx = plot.getCanvas().getContext("2d");
+        ctx.lineWidth="1";
+        ctx.fillStyle = "rgba(255, 0, 0, 0.2)";
+        ctx.strokeStyle="red";
+        for (var j = 0; j < buckets.length; j++) {
+            bucket = buckets[j];
+            var bl = plot.pointOffset({ x: bucket.low, y: 0});
+            var tr = plot.pointOffset({ x: bucket.high, y: bucket.count});
+            ctx.fillRect(bl.left, bl.top, tr.left - bl.left, tr.top - bl.top);
+            ctx.strokeRect(bl.left, bl.top, tr.left - bl.left, tr.top - bl.top);
+        }
+    }
+    if (!attachedPlot)
+        container.children().remove();
+}
+
+function attachLinePlotLabels(test, container) {
+    var results = test.results();
+    var attachedPlot = false;
+    for (var i = 0; i < results.length; i++) {
+        container.append('<div>' + results[i].run().label() + '</div>');
+    }
+}
+
+function attachPlot(test, plotContainer, minIsZero) {
+    var results = test.results();
+
+    var values = results.reduce(function(values, result, index) {
+        var newValues = result.values();
+        return newValues ? values.concat(newValues.map(function(value) { return [index, value]; })) : values;
+    }, []);
+
+    var plotData = [$.extend(true, {}, subpointsPlotOptions, {data: values})];
+    plotData.push({id: '&mu;', data: results.map(function(result, index) { return [index, result.mean()]; }), color: plotColor});
+
+    var overallMax = Statistics.max(results.map(function(result, index) { return result.max(); }));
+    var overallMin = Statistics.min(results.map(function(result, index) { return result.min(); }));
+    var margin = (overallMax - overallMin) * 0.1;
+    var currentPlotOptions = $.extend(true, {}, mainPlotOptions, {yaxis: {
+        min: minIsZero ? 0 : overallMin - margin,
+        max: minIsZero ? overallMax * 1.1 : overallMax + margin}});
+
+    currentPlotOptions.xaxis.max = results.length - 0.5;
+    currentPlotOptions.xaxis.ticks = results.map(function(result, index) { return [index, result.run().label()]; });
+
+    $.plot(plotContainer, plotData, currentPlotOptions);
+}
+
+function toFixedWidthPrecision(value) {
+    var decimal = value.toFixed(2);
+    return decimal;
+}
+
+function formatPercentage(fraction) {
+    var percentage = fraction * 100;
+    return (fraction * 100).toFixed(2) + '%';
+}
+
+function setUpSortClicks(runs)
+{
+    $('#nameColumn').click(sortByName);
+
+    $('#unitColumn').click(sortByUnit);
+
+    runs.forEach(function(run) {
+        $('#' + run.id()).click(sortByResult);
+        $('#' + run.id() + COMPARISON_SUFFIX).click(sortByReference);
+     });
+}
+
+function TestTypeSelector(tests) {
+    this.recognizers = {
+        'Time': function(test) { return !test.isMemoryTest(); },
+        'Memory': function(test) { return test.isMemoryTest(); }
+    };
+    this.testTypeNames = this.generateUsedTestTypeNames(tests);
+    // Default to selecting the first test-type name in the list.
+    this.testTypeName = this.testTypeNames[0];
+}
+
+TestTypeSelector.prototype = {
+    set testTypeName(testTypeName) {
+        this._testTypeName = testTypeName;
+        this.shouldShowTest = this.recognizers[testTypeName];
+    },
+
+    generateUsedTestTypeNames: function(allTests) {
+        var testTypeNames = [];
+
+        for (var recognizedTestName in this.recognizers) {
+            var recognizes = this.recognizers[recognizedTestName];
+            for (var testName in allTests) {
+                var test = allTests[testName];
+                if (recognizes(test)) {
+                    testTypeNames.push(recognizedTestName);
+                    break;
+                }
+            }
+        }
+
+        if (testTypeNames.length === 0) {
+            // No test types we recognize, add 'No Results' with a dummy recognizer.
+            var noResults = 'No Results';
+            this.recognizers[noResults] = function() { return false; };
+            testTypeNames.push(noResults);
+        } else if (testTypeNames.length > 1) {
+            // We have more than one test type, so add 'All' with a recognizer that always succeeds.
+            var allResults = 'All';
+            this.recognizers[allResults] = function() { return true; };
+            testTypeNames.push(allResults);
+        }
+
+        return testTypeNames;
+    },
+
+    buildButtonHTMLForUsedTestTypes: function() {
+        var selectedTestTypeName = this._testTypeName;
+        // Build spans for all recognised test names with the selected test highlighted.
+        return this.testTypeNames.map(function(testTypeName) {
+            var classAttribute = testTypeName === selectedTestTypeName ? ' class=checked' : '';
+            return '<span' + classAttribute + '>' + testTypeName + '</span>';
+        }).join('');
+    }
+};
+
+var topLevelRows;
+var allTableRows;
+
+function displayTable(tests, runs, testTypeSelector, referenceIndex, useLargeLinePlots) {
+    var resultHeaders = runs.map(function(run, index) {
+         var header = '<th id="' + run.id() + '" ' +
+                          'colspan=2 ' +
+                          'title="' + run.description() + '">' +
+                      '<span class="label" ' +
+                          'title="Edit run label">' +
+                          run.label() +
+                      '</span>' +
+                      '<div class="closeButton" ' +
+                          'title="Delete run">' +
+                          '&times;' +
+                      '</div>' +
+                  '</th>';
+                if (index !== referenceIndex) {
+                  header += '<th id="' + run.id() + COMPARISON_SUFFIX + '" ' +
+                                'title="Sort by better/worse">' +
+                                '&Delta;' +
+                                '</th>';
+                }
+         return header;
+    });
+
+    resultHeaders = resultHeaders.join('');
+
+    htmlString = '<thead>' +
+                     '<tr>' +
+                         '<th id="nameColumn">' +
+                             '<div class="openAllButton" ' +
+                                  'title="Open all rows or graphs">' +
+                                 'Open All' +
+                              '</div>' +
+                              '<div class="closeAllButton" ' +
+                                   'title="Close all rows">' +
+                                  'Close All' +
+                              '</div>' +
+                              'Test' +
+                          '</th>' +
+                          '<th id="unitColumn">' +
+                              'Unit' +
+                          '</th>' +
+                          resultHeaders +
+                     '</tr>' +
+                 '</head>' +
+                 '<tbody>' +
+                 '</tbody>';
+
+    $('#container').html(htmlString);
+
+    var testNames = [];
+    for (testName in tests)
+        testNames.push(testName);
+
+    allTableRows = [];
+    testNames.forEach(function(testName) {
+        var test = tests[testName];
+        if (testTypeSelector.shouldShowTest(test)) {
+            allTableRows.push(new TableRow(runs, test, referenceIndex, useLargeLinePlots));
+        }
+    });
+
+    // Build a list of top level rows with attached children
+    topLevelRows = [];
+    allTableRows.forEach(function(row) {
+        // Add us to top level if we are a top-level row...
+        if (row.hasNoURL) {
+            topLevelRows.push(row);
+            // Add a duplicate child row that holds the graph for the parent
+            var graphHolder = new TableRow(runs, row.test, referenceIndex, useLargeLinePlots);
+            graphHolder.isImportant = true;
+            graphHolder.URL = 'Summary';
+            graphHolder.hideRowData();
+            allTableRows.push(graphHolder);
+            row.addNestedChild(graphHolder);
+            return;
+        }
+
+        // ...or add us to our parent if we have one ...
+        for (var i = 0; i < allTableRows.length; i++) {
+            if (allTableRows[i].isParentOf(row)) {
+                allTableRows[i].addNestedChild(row);
+                return;
+            }
+        }
+
+        // ...otherwise this result is orphaned, display it at top level with a graph
+        row.hasGraph = true;
+        topLevelRows.push(row);
+    });
+
+    buildTable(topLevelRows);
+
+    $('.closeButton').click(function(event) {
+        for (var i = 0; i < runs.length; i++) {
+            if (runs[i].id() == event.target.parentNode.id) {
+                runs[i].hide();
+                undeleteManager.ondelete(runs[i].id());
+                location.reload();
+                break;
+            }
+        }
+        event.stopPropagation();
+    });
+
+    $('.closeAllButton').click(function(event) {
+        for (var i = 0; i < allTableRows.length; i++) {
+            allTableRows[i].closeRow();
+        }
+        event.stopPropagation();
+    });
+
+    $('.openAllButton').click(function(event) {
+        for (var i = 0; i < topLevelRows.length; i++) {
+            topLevelRows[i].openRow();
+        }
+        event.stopPropagation();
+    });
+
+    setUpSortClicks(runs);
+
+    $('.label').click(function(event) {
+        for (var i = 0; i < runs.length; i++) {
+            if (runs[i].id() == event.target.parentNode.id) {
+                $(event.target).replaceWith('<input id="labelEditor" type="text" value="' + runs[i].label()  + '">');
+                $('#labelEditor').focusout(function() {
+                    runs[i].setLabel(this.value);
+                    location.reload();
+                });
+                $('#labelEditor').keypress(function(event) {
+                    if (event.which == 13) {
+                        runs[i].setLabel(this.value);
+                        location.reload();
+                    }
+                });
+                $('#labelEditor').click(function(event) {
+                    event.stopPropagation();
+                });
+                $('#labelEditor').mousedown(function(event) {
+                    event.stopPropagation();
+                });
+                $('#labelEditor').select();
+                break;
+            }
+        }
+        event.stopPropagation();
+    });
+}
+
+function validForSorting(row) {
+    return ($.type(row.sortValue) === 'string') || !isNaN(row.sortValue);
+}
+
+var sortDirection = 1;
+
+function sortRows(rows) {
+    rows.sort(
+        function(rowA,rowB) {
+            if (validForSorting(rowA) !== validForSorting(rowB)) {
+                // Sort valid values upwards when compared to invalid
+                if (validForSorting(rowA)) {
+                    return -1;
+                }
+                if (validForSorting(rowB)) {
+                    return 1;
+                }
+            }
+
+            // Some rows always sort to the top
+            if (rowA.isImportant) {
+                return -1;
+            }
+            if (rowB.isImportant) {
+                return 1;
+            }
+
+            if (rowA.sortValue === rowB.sortValue) {
+                // Sort identical values by name to keep the sort stable,
+                // always keep name alphabetical (even if a & b sort values
+                // are invalid)
+                return rowA.test.name() > rowB.test.name() ? 1 : -1;
+            }
+
+            return rowA.sortValue > rowB.sortValue ? sortDirection : -sortDirection;
+         } );
+
+    // Sort the rows' children
+    rows.forEach(function(row) {
+        sortRows(row.children);
+    });
+}
+
+function buildTable(rows) {
+   rows.forEach(function(row) {
+     row.removeFromPage();
+   });
+
+   sortRows(rows);
+
+   rows.forEach(function(row) {
+     row.addToPage();
+   });
+}
+
+var activeSortHeaderElement = undefined;
+var columnSortDirection = {};
+
+function determineColumnSortDirection(element) {
+  columnDirection = columnSortDirection[element.id];
+
+  if (columnDirection === undefined) {
+    // First time we've sorted this row, default to down
+    columnSortDirection[element.id] = SORT_DOWN_CLASS;
+  } else if (element === activeSortHeaderElement) {
+    // Clicking on same header again, swap direction
+    columnSortDirection[element.id] = (columnDirection === SORT_UP_CLASS) ? SORT_DOWN_CLASS : SORT_UP_CLASS;
+  }
+}
+
+function updateSortDirection(element) {
+    // Remove old header's sort arrow
+    if (activeSortHeaderElement !== undefined) {
+        activeSortHeaderElement.classList.remove(columnSortDirection[activeSortHeaderElement.id]);
+    }
+
+    determineColumnSortDirection(element);
+
+    sortDirection = (columnSortDirection[element.id] === SORT_UP_CLASS) ? 1 : -1;
+
+    // Add new header's sort arrow
+    element.classList.add(columnSortDirection[element.id]);
+    activeSortHeaderElement = element;
+}
+
+function sortByName(event) {
+    updateSortDirection(event.toElement);
+
+    allTableRows.forEach(function(row) {
+       row.prepareToSortByName();
+    });
+
+    buildTable(topLevelRows);
+}
+
+function sortByUnit(event) {
+    updateSortDirection(event.toElement);
+
+    allTableRows.forEach(function(row) {
+        row.prepareToSortByUnit();
+    });
+
+    buildTable(topLevelRows);
+}
+
+function sortByResult(event) {
+    updateSortDirection(event.toElement);
+
+    var runId = event.target.id;
+
+    allTableRows.forEach(function(row) {
+        row.prepareToSortByTestResults(runId);
+    });
+
+    buildTable(topLevelRows);
+}
+
+function sortByReference(event) {
+    updateSortDirection(event.toElement);
+
+    // The element ID has _compare appended to allow us to set up a click event
+    // remove the _compare to return a useful Id
+    var runIdWithCompare = event.target.id;
+    var runId = runIdWithCompare.split('_')[0];
+
+    allTableRows.forEach(function(row) {
+        row.prepareToSortRelativeToReference(runId);
+    });
+
+    buildTable(topLevelRows);
+}
+
+function linearRegression(points) {
+    // Implement http://www.easycalculation.com/statistics/learn-correlation.php.
+    // x = magnitude
+    // y = iterations
+    var sumX = 0;
+    var sumY = 0;
+    var sumXSquared = 0;
+    var sumYSquared = 0;
+    var sumXTimesY = 0;
+
+    for (var i = 0; i < points.length; i++) {
+        var x = i;
+        var y = points[i];
+        sumX += x;
+        sumY += y;
+        sumXSquared += x * x;
+        sumYSquared += y * y;
+        sumXTimesY += x * y;
+    }
+
+    var r = (points.length * sumXTimesY - sumX * sumY) /
+        Math.sqrt((points.length * sumXSquared - sumX * sumX) *
+                  (points.length * sumYSquared - sumY * sumY));
+
+    if (isNaN(r) || r == Math.Infinity)
+        r = 0;
+
+    var slope = (points.length * sumXTimesY - sumX * sumY) / (points.length * sumXSquared - sumX * sumX);
+    var intercept = sumY / points.length - slope * sumX / points.length;
+    return {slope: slope, intercept: intercept, rSquared: r * r};
+}
+
+var warningSign = '<svg viewBox="0 0 100 100" style="width: 18px; height: 18px; vertical-align: bottom;" version="1.1">'
+    + '<polygon fill="red" points="50,10 90,80 10,80 50,10" stroke="red" stroke-width="10" stroke-linejoin="round" />'
+    + '<polygon fill="white" points="47,30 48,29, 50, 28.7, 52,29 53,30 50,60" stroke="white" stroke-width="10" stroke-linejoin="round" />'
+    + '<circle cx="50" cy="73" r="6" fill="white" />'
+    + '</svg>';
+
+function TableRow(runs, test, referenceIndex, useLargeLinePlots) {
+    this.runs = runs;
+    this.test = test;
+    this.referenceIndex = referenceIndex;
+    this.useLargeLinePlots = useLargeLinePlots;
+    this.children = [];
+
+    this.tableRow = $('<tr class="highlight">' +
+                            '<td class="test collapsed" >' +
+                                this.test.name() +
+                            '</td>' +
+                            '<td class="unit">' +
+                                this.test.unit() +
+                            '</td>' +
+                      '</tr>');
+
+    var runIndex = 0;
+    var results = this.test.results();
+    var referenceResult = undefined;
+
+    this.resultIndexMap = {};
+    for (var i = 0; i < results.length; i++) {
+        while (this.runs[runIndex] !== results[i].run())
+            runIndex++;
+        if (runIndex === this.referenceIndex)
+            referenceResult = results[i];
+        this.resultIndexMap[runIndex] = i;
+    }
+    for (var i = 0; i < this.runs.length; i++) {
+        var resultIndex = this.resultIndexMap[i];
+        if (resultIndex === undefined)
+            this.tableRow.append(this.markupForMissingRun(i == this.referenceIndex));
+        else
+            this.tableRow.append(this.markupForRun(results[resultIndex], referenceResult));
+    }
+
+    // Use the test name (without URL) to bind parents and their children
+    var nameAndURL = this.test.name().split('.');
+    var benchmarkName = nameAndURL.shift();
+    this.testName = nameAndURL.shift();
+    this.hasNoURL = (nameAndURL.length === 0);
+
+    if (!this.hasNoURL) {
+        // Re-join the URL
+        this.URL = nameAndURL.join('.');
+    }
+
+    this.isImportant = false;
+    this.hasGraph = false;
+    this.currentIndentationClass = ''
+    this.indentLevel = 0;
+    this.setRowNestedState(COLLAPSED);
+    this.setVisibility(VISIBLE);
+    this.prepareToSortByName();
+}
+
+TableRow.prototype.hideRowData = function() {
+    data = this.tableRow.children('td');
+
+    for (index in data) {
+        if (index > 0) {
+            // Blank out everything except the test name
+            data[index].innerHTML = '';
+        }
+    }
+}
+
+TableRow.prototype.prepareToSortByTestResults = function(runId) {
+    var testResults = this.test.results();
+    // Find the column in this row that matches the runId and prepare to
+    // sort by the mean of that test.
+    for (index in testResults) {
+        sourceId = testResults[index].run().id();
+        if (runId === sourceId) {
+            this.sortValue = testResults[index].mean();
+            return;
+        }
+    }
+    // This row doesn't have any results for the passed runId
+    this.sortValue = undefined;
+}
+
+TableRow.prototype.prepareToSortRelativeToReference = function(runId) {
+    var testResults = this.test.results();
+
+    // Get index of test results that correspond to the reference column.
+    var remappedReferenceIndex = this.resultIndexMap[this.referenceIndex];
+
+    if (remappedReferenceIndex === undefined) {
+        // This test has no results in the reference run.
+        this.sortValue = undefined;
+        return;
+    }
+
+    otherResults = testResults[remappedReferenceIndex];
+
+    // Find the column in this row that matches the runId and prepare to
+    // sort by the difference from the reference.
+    for (index in testResults) {
+        sourceId = testResults[index].run().id();
+        if (runId === sourceId) {
+            this.sortValue = testResults[index].percentDifference(otherResults);
+            if (this.test.biggerIsBetter()) {
+                // For this test bigger is not better
+                this.sortValue = -this.sortValue;
+            }
+            return;
+        }
+    }
+    // This row doesn't have any results for the passed runId
+    this.sortValue = undefined;
+}
+
+TableRow.prototype.prepareToSortByUnit = function() {
+    this.sortValue = this.test.unit().toLowerCase();
+}
+
+TableRow.prototype.prepareToSortByName = function() {
+    this.sortValue = this.test.name().toLowerCase();
+}
+
+TableRow.prototype.isParentOf = function(row) {
+    return this.hasNoURL && (this.testName === row.testName);
+}
+
+TableRow.prototype.addNestedChild = function(child) {
+    this.children.push(child);
+
+    // Indent child one step in from parent
+    child.indentLevel = this.indentLevel + INDENTATION;
+    child.hasGraph = true;
+    // Start child off as hidden (i.e. collapsed inside parent)
+    child.setVisibility(INVISIBLE);
+    child.updateIndentation();
+    // Show URL in the title column
+    child.tableRow.children()[0].innerHTML = child.URL;
+    // Set up class to change background colour of nested rows
+    if (child.isImportant) {
+        child.tableRow.addClass('importantNestedRow');
+    } else {
+        child.tableRow.addClass('nestedRow');
+    }
+}
+
+TableRow.prototype.setVisibility = function(visibility) {
+     this.visibility = visibility;
+     this.tableRow[0].style.display = (visibility === INVISIBLE) ? 'none' : '';
+}
+
+TableRow.prototype.setRowNestedState = function(newState) {
+    this.rowState = newState;
+    this.updateIndentation();
+}
+
+TableRow.prototype.updateIndentation = function() {
+    var element = this.tableRow.children('td').first();
+
+    element.removeClass(this.currentIndentationClass);
+
+    this.currentIndentationClass = (this.rowState === COLLAPSED) ? 'collapsed' : 'expanded';
+
+    element[0].style.marginLeft = this.indentLevel.toString() + 'px';
+    element[0].style.float = 'left';
+
+    element.addClass(this.currentIndentationClass);
+}
+
+TableRow.prototype.addToPage = function() {
+    $('#container').children('tbody').last().append(this.tableRow);
+
+    // Set up click callback
+    var owningObject = this;
+    this.tableRow.click(function(event) {
+        event.preventDefault();
+        owningObject.toggle();
+    });
+
+    // Add children to the page too
+    this.children.forEach(function(child) {
+        child.addToPage();
+    });
+}
+
+TableRow.prototype.removeFromPage = function() {
+    // Remove children
+    this.children.forEach(function(child) {
+        child.removeFromPage();
+    });
+    // Remove us
+    this.tableRow.remove();
+}
+
+
+TableRow.prototype.markupForRun = function(result, referenceResult) {
+    var comparisonCell = '';
+    var shouldCompare = result !== referenceResult;
+    if (shouldCompare) {
+        var comparisonText = '';
+        var className = '';
+
+        if (referenceResult) {
+            var percentDifference = referenceResult.percentDifference(result);
+            if (isNaN(percentDifference)) {
+                comparisonText = 'Unknown';
+                className = UNKNOWN_CLASS;
+            } else if (Math.abs(percentDifference) < SMALLEST_PERCENT_DISPLAYED) {
+                comparisonText = 'Equal';
+                // Show equal values in green
+                className = BETTER_CLASS;
+            } else {
+                var better = this.test.biggerIsBetter() ? percentDifference > 0 : percentDifference < 0;
+                comparisonText = formatPercentage(Math.abs(percentDifference)) + (better ? ' Better' : ' Worse');
+                className = better ? BETTER_CLASS : WORSE_CLASS;
+            }
+
+            if (!referenceResult.isStatisticallySignificant(result)) {
+                // Put result in brackets and fade if not statistically significant
+                className += ' fadeOut';
+                comparisonText = '(' + comparisonText + ')';
+            }
+        }
+        comparisonCell = '<td class="comparison ' + className + '">' + comparisonText + '</td>';
+    }
+
+    var values = result.values();
+    var warning = '';
+    var regressionAnalysis = '';
+    if (result.histogramValues) {
+        // Don't calculate regression result for histograms.
+    } else if (values && values.length > 3) {
+        regressionResult = linearRegression(values);
+        regressionAnalysis = 'slope=' + toFixedWidthPrecision(regressionResult.slope)
+            + ', R^2=' + toFixedWidthPrecision(regressionResult.rSquared);
+        if (regressionResult.rSquared > 0.6 && Math.abs(regressionResult.slope) > 0.01) {
+            warning = ' <span class="regression-warning" title="Detected a time dependency with ' + regressionAnalysis + '">' + warningSign + ' </span>';
+        }
+    }
+
+    var referenceClass = shouldCompare ? '' : 'reference';
+
+    var statistics = '&sigma;=' + toFixedWidthPrecision(result.confidenceIntervalDelta()) + ', min=' + toFixedWidthPrecision(result.min())
+     + ', max=' + toFixedWidthPrecision(result.max()) + '\n' + regressionAnalysis;
+
+    var confidence;
+    if (isNaN(result.confidenceIntervalDeltaRatio())) {
+        // Don't bother showing +- Nan as it is meaningless
+        confidence = '';
+    } else {
+        confidence = '&plusmn; ' + formatPercentage(result.confidenceIntervalDeltaRatio());
+    }
+
+    return '<td class="result ' + referenceClass + '" title="' + statistics + '">' + toFixedWidthPrecision(result.mean())
+        + '</td><td class="confidenceIntervalDelta ' + referenceClass + '" title="' + statistics + '">' + confidence + warning + '</td>' + comparisonCell;
+}
+
+TableRow.prototype.markupForMissingRun = function(isReference) {
+    if (isReference) {
+        return '<td colspan=2 class="missingReference">Missing</td>';
+    }
+    return '<td colspan=3 class="missing">Missing</td>';
+}
+
+TableRow.prototype.openRow = function() {
+    if (this.rowState === EXPANDED) {
+        // If we're already expanded, open our children instead
+        this.children.forEach(function(child) {
+            child.openRow();
+        });
+        return;
+    }
+
+    this.setRowNestedState(EXPANDED);
+
+    if (this.hasGraph) {
+        var firstCell = this.tableRow.children('td').first();
+        var plot = createPlot(firstCell, this.test, this.useLargeLinePlots);
+        plot.css({'position': 'absolute', 'z-index': 2});
+        var offset = this.tableRow.offset();
+        offset.left += GRAPH_INDENT;
+        offset.top += this.tableRow.outerHeight();
+        plot.offset(offset);
+        this.tableRow.children('td').css({'padding-bottom': plot.outerHeight() + PADDING_UNDER_GRAPH});
+    }
+
+    this.children.forEach(function(child) {
+        child.setVisibility(VISIBLE);
+    });
+
+    if (this.children.length === 1) {
+        // If we only have a single child...
+        var child = this.children[0];
+        if (child.isImportant) {
+          // ... and it is important (i.e. the summary row) just open it when
+          // parent is opened to save needless clicking
+          child.openRow();
+        }
+    }
+}
+
+TableRow.prototype.closeRow = function() {
+    if (this.rowState === COLLAPSED) {
+        return;
+    }
+
+    this.setRowNestedState(COLLAPSED);
+
+    if (this.hasGraph) {
+        var firstCell = this.tableRow.children('td').first();
+        firstCell.children('section').remove();
+        this.tableRow.children('td').css({'padding-bottom': ''});
+    }
+
+    this.children.forEach(function(child) {
+        // Make children invisible, but leave their collapsed status alone
+        child.setVisibility(INVISIBLE);
+    });
+}
+
+TableRow.prototype.toggle = function() {
+    if (this.rowState === EXPANDED) {
+        this.closeRow();
+    } else {
+        this.openRow();
+    }
+    return false;
+}
+
+function init() {
+    var runs = [];
+    var metrics = {};
+    var deletedRunsById = {};
+    $.each(JSON.parse(document.getElementById('results-json').textContent), function(index, entry) {
+        var run = new TestRun(entry);
+        if (run.isHidden()) {
+            deletedRunsById[run.id()] = run;
+            return;
+        }
+
+        runs.push(run);
+
+        function addTests(tests) {
+            for (var testName in tests) {
+                var rawMetrics = tests[testName].metrics;
+
+                for (var metricName in rawMetrics) {
+                    var fullMetricName = testName + ':' + metricName;
+                    var metric = metrics[fullMetricName];
+                    if (!metric) {
+                        metric = new PerfTestMetric(testName, metricName, rawMetrics[metricName].units, rawMetrics[metricName].important);
+                        metrics[fullMetricName] = metric;
+                    }
+                    // std & degrees_of_freedom could be undefined
+                    metric.addResult(
+                        new TestResult(metric, rawMetrics[metricName].current,
+                            run, rawMetrics[metricName]['std'], rawMetrics[metricName]['degrees_of_freedom']));
+                }
+            }
+        }
+
+        addTests(entry.tests);
+    });
+
+    var useLargeLinePlots = false;
+    var referenceIndex = 0;
+
+    var testTypeSelector = new TestTypeSelector(metrics);
+    var buttonHTML = testTypeSelector.buildButtonHTMLForUsedTestTypes();
+    $('#time-memory').append(buttonHTML);
+
+    $('#scatter-line').bind('change', function(event, checkedElement) {
+        useLargeLinePlots = checkedElement.textContent == 'Line';
+        displayTable(metrics, runs, testTypeSelector, referenceIndex, useLargeLinePlots);
+    });
+
+    runs.map(function(run, index) {
+        $('#reference').append('<span value="' + index + '"' + (index == referenceIndex ? ' class="checked"' : '') + ' title="' + run.description() + '">' + run.label() + '</span>');
+    })
+
+    $('#time-memory').bind('change', function(event, checkedElement) {
+        testTypeSelector.testTypeName = checkedElement.textContent;
+        displayTable(metrics, runs, testTypeSelector, referenceIndex, useLargeLinePlots);
+    });
+
+    $('#reference').bind('change', function(event, checkedElement) {
+        referenceIndex = parseInt(checkedElement.getAttribute('value'));
+        displayTable(metrics, runs, testTypeSelector, referenceIndex, useLargeLinePlots);
+    });
+
+    displayTable(metrics, runs, testTypeSelector, referenceIndex, useLargeLinePlots);
+
+    $('.checkbox').each(function(index, checkbox) {
+        $(checkbox).children('span').click(function(event) {
+            if ($(this).hasClass('checked'))
+                return;
+            $(checkbox).children('span').removeClass('checked');
+            $(this).addClass('checked');
+            $(checkbox).trigger('change', $(this));
+        });
+    });
+
+    runToUndelete = deletedRunsById[undeleteManager.mostRecentlyDeletedId()];
+
+    if (runToUndelete) {
+        $('#undelete').html('Undelete ' + runToUndelete.label());
+        $('#undelete').attr('title', runToUndelete.description());
+        $('#undelete').click(function(event) {
+            runToUndelete.show();
+            undeleteManager.undeleteMostRecent();
+            location.reload();
+        });
+    } else {
+        $('#undelete').hide();
+    }
+}
+
+</script>
+<script id="results-json" type="application/json">%json_results%</script>
+<script id="units-json" type="application/json">%json_units%</script>
+</body>
+</html>
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry.gyp b/sdk/platform-tools/systrace/catapult/telemetry/telemetry.gyp
new file mode 100644
index 0000000..02ecd8d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry.gyp
@@ -0,0 +1,16 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+  'targets': [
+    {
+      'target_name': 'bitmaptools',
+      'type': 'executable',
+      'sources': [
+        'telemetry/internal/image_processing/bitmaptools.cc',
+      ],
+      'toolsets': ['host'],
+    },
+  ],
+}
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry.isolate b/sdk/platform-tools/systrace/catapult/telemetry/telemetry.isolate
new file mode 100644
index 0000000..829efec
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry.isolate
@@ -0,0 +1,19 @@
+# Copyright (c) 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+{
+  'conditions': [
+    ['OS=="android" or OS=="linux" or OS=="mac" or OS=="win"', {
+      'variables': {
+        'files': [
+          # For now, just include the whole catapult directory.
+          # TODO(nednguyen, aiolos): only include what telemetry needs.
+          # https://github.com/catapult-project/catapult/issues/1953
+          '../',
+          # For Telemetry's screenshot support.
+          '<(PRODUCT_DIR)/bitmaptools<(EXECUTABLE_SUFFIX)',
+        ],
+      },
+    }],
+  ]
+}
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/__init__.py
new file mode 100644
index 0000000..e91b2f8
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/__init__.py
@@ -0,0 +1,65 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""A library for cross-platform browser tests."""
+import os
+import sys
+
+
+# Ensure Python >= 2.7.
+if sys.version_info < (2, 7):
+  print >> sys.stderr, 'Need Python 2.7 or greater.'
+  sys.exit(-1)
+
+
+def _JoinPath(*path_parts):
+  return os.path.abspath(os.path.join(*path_parts))
+
+
+def _AddDirToPythonPath(*path_parts):
+  path = _JoinPath(*path_parts)
+  assert os.path.isdir(path), 'Not a valid path: %s' % path
+  if path not in sys.path:
+    # Some call sites that use Telemetry assume that sys.path[0] is the
+    # directory containing the script, so we add these extra paths to right
+    # after sys.path[0].
+    sys.path.insert(1, path)
+
+
+# Add Catapult dependencies to our path.
+# util depends on py_utils, so we can't use it to get the catapult dir.
+_CATAPULT_DIR = os.path.join(
+    os.path.dirname(os.path.abspath(__file__)), '..', '..')
+_AddDirToPythonPath(_CATAPULT_DIR, 'common', 'py_utils')
+_AddDirToPythonPath(_CATAPULT_DIR, 'dependency_manager')
+_AddDirToPythonPath(_CATAPULT_DIR, 'devil')
+_AddDirToPythonPath(_CATAPULT_DIR, 'systrace')
+_AddDirToPythonPath(_CATAPULT_DIR, 'tracing')
+_AddDirToPythonPath(_CATAPULT_DIR, 'common', 'py_trace_event')
+_AddDirToPythonPath(_CATAPULT_DIR, 'common', 'battor')
+_AddDirToPythonPath(_CATAPULT_DIR, 'tracing', 'tracing_build')
+_AddDirToPythonPath(_CATAPULT_DIR, 'third_party', 'py_vulcanize')
+
+
+from telemetry.core import util
+from telemetry.internal.util import global_hooks
+
+# Add Catapult third party dependencies into our path.
+_AddDirToPythonPath(util.GetCatapultThirdPartyDir(), 'typ')
+# Required by websocket-client.
+_AddDirToPythonPath(util.GetCatapultThirdPartyDir(), 'six')
+
+# Add Telemetry third party dependencies into our path.
+_AddDirToPythonPath(util.GetTelemetryThirdPartyDir(), 'altgraph')
+_AddDirToPythonPath(util.GetTelemetryThirdPartyDir(), 'mock')
+_AddDirToPythonPath(util.GetTelemetryThirdPartyDir(), 'modulegraph')
+_AddDirToPythonPath(util.GetTelemetryThirdPartyDir(), 'mox3')
+_AddDirToPythonPath(util.GetTelemetryThirdPartyDir(), 'png')
+_AddDirToPythonPath(util.GetTelemetryThirdPartyDir(), 'pyfakefs')
+_AddDirToPythonPath(util.GetTelemetryThirdPartyDir(), 'pyserial')
+_AddDirToPythonPath(util.GetTelemetryThirdPartyDir(), 'web-page-replay')
+_AddDirToPythonPath(util.GetTelemetryThirdPartyDir(), 'websocket-client')
+
+# Install Telemtry global hooks.
+global_hooks.InstallHooks()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/android/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/android/__init__.py
new file mode 100644
index 0000000..6bd5e3a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/android/__init__.py
@@ -0,0 +1,6 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.android.android_story import AndroidStory
+from telemetry.android.shared_android_state import SharedAndroidState
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/android/android_story.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/android/android_story.py
new file mode 100644
index 0000000..9949e79
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/android/android_story.py
@@ -0,0 +1,28 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.android import shared_android_state
+from telemetry import story
+
+class AndroidStory(story.Story):
+  def __init__(self, start_intent, is_app_ready_predicate=None,
+               name='', tags=None, is_local=False):
+    """Creates a new story for Android app.
+
+    Args:
+      start_intent: See AndroidPlatform.LaunchAndroidApplication.
+      is_app_ready_predicate: See AndroidPlatform.LaunchAndroidApplication.
+      name: See Story.__init__.
+      tags: See Story.__init__
+      is_app_ready_predicate: See Story.__init__.
+    """
+    super(AndroidStory, self).__init__(
+        shared_android_state.SharedAndroidState, name=name, tags=tags,
+        is_local=is_local)
+    self.start_intent = start_intent
+    self.is_app_ready_predicate = is_app_ready_predicate
+
+  def Run(self, shared_state):
+    """Execute the interactions with the applications."""
+    raise NotImplementedError
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/android/shared_android_state.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/android/shared_android_state.py
new file mode 100644
index 0000000..d5a4e69
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/android/shared_android_state.py
@@ -0,0 +1,82 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+from telemetry.core import android_platform
+from telemetry.core import platform
+from telemetry.internal.platform import android_device
+from telemetry import story as story_module
+from telemetry.web_perf import timeline_based_measurement
+
+
+class SharedAndroidState(story_module.SharedState):
+  """Manage test state/transitions across multiple android.AndroidStory's.
+
+  WARNING: the class is not ready for public consumption.
+  Email telemetry@chromium.org if you feel like you must use it.
+  """
+
+  def __init__(self, test, finder_options, story_set):
+    """This method is styled on unittest.TestCase.setUpClass.
+
+    Args:
+      test: a web_perf.TimelineBasedMeasurement instance.
+      options: a BrowserFinderOptions instance with command line options.
+      story_set: a story.StorySet instance.
+    """
+    super(SharedAndroidState, self).__init__(test, finder_options, story_set)
+    if not isinstance(
+        test, timeline_based_measurement.TimelineBasedMeasurement):
+      raise ValueError(
+          'SharedAndroidState only accepts TimelineBasedMeasurement tests'
+          ' (not %s).' % test.__class__)
+    self._test = test
+    self._finder_options = finder_options
+    self._android_app = None
+    self._current_story = None
+    device = android_device.GetDevice(finder_options)
+    assert device, 'Android device required.'
+    self._android_platform = platform.GetPlatformForDevice(
+        device, finder_options)
+    assert self._android_platform, 'Unable to create android platform.'
+    assert isinstance(
+        self._android_platform, android_platform.AndroidPlatform)
+
+  @property
+  def app(self):
+    return self._android_app
+
+  @property
+  def platform(self):
+    return self._android_platform
+
+  def WillRunStory(self, story):
+    assert not self._android_app
+    self._current_story = story
+    self._android_app = self._android_platform.LaunchAndroidApplication(
+        story.start_intent, story.is_app_ready_predicate)
+    self._test.WillRunStory(self._android_platform.tracing_controller)
+
+  def CanRunStory(self, story):
+    """This does not apply to android app stories."""
+    return True
+
+  def RunStory(self, results):
+    self._current_story.Run(self)
+    self._test.Measure(self._android_platform.tracing_controller, results)
+
+  def DidRunStory(self, results):
+    self._test.DidRunStory(self._android_platform.tracing_controller)
+    if self._android_app:
+      self._android_app.Close()
+      self._android_app = None
+
+  def TearDownState(self):
+    """Tear down anything created in the __init__ method that is not needed.
+
+    Currently, there is no clean-up needed from SharedAndroidState.__init__.
+    """
+    pass
+
+  def DumpStateUponFailure(self, story, results):
+    # TODO: Dump the state of the Android app.
+    pass
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/benchmark.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/benchmark.py
new file mode 100644
index 0000000..3cf78ff
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/benchmark.py
@@ -0,0 +1,274 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import optparse
+
+from telemetry import decorators
+from telemetry.internal import story_runner
+from telemetry.internal.util import command_line
+from telemetry.page import legacy_page_test
+from telemetry.web_perf import timeline_based_measurement
+
+Disabled = decorators.Disabled
+Enabled = decorators.Enabled
+Owner = decorators.Owner
+
+class InvalidOptionsError(Exception):
+  """Raised for invalid benchmark options."""
+  pass
+
+
+class BenchmarkMetadata(object):
+  def __init__(self, name, description='', rerun_options=None):
+    self._name = name
+    self._description = description
+    self._rerun_options = rerun_options
+
+  @property
+  def name(self):
+    return self._name
+
+  @property
+  def description(self):
+    return self._description
+
+  @property
+  def rerun_options(self):
+    return self._rerun_options
+
+  def AsDict(self):
+    return {
+      'type': 'telemetry_benchmark',
+      'name': self._name,
+      'description': self._description,
+      'rerun_options': self._rerun_options,
+    }
+
+
+class Benchmark(command_line.Command):
+  """Base class for a Telemetry benchmark.
+
+  A benchmark packages a measurement and a PageSet together.
+  Benchmarks default to using TBM unless you override the value of
+  Benchmark.test, or override the CreatePageTest method.
+
+  New benchmarks should override CreateStorySet.
+  """
+  options = {}
+  page_set = None
+  test = timeline_based_measurement.TimelineBasedMeasurement
+
+  def __init__(self, max_failures=None):
+    """Creates a new Benchmark.
+
+    Args:
+      max_failures: The number of story run's failures before bailing
+          from executing subsequent page runs. If None, we never bail.
+    """
+    self._max_failures = max_failures
+    self._has_original_tbm_options = (
+        self.CreateTimelineBasedMeasurementOptions.__func__ ==
+        Benchmark.CreateTimelineBasedMeasurementOptions.__func__)
+    has_original_create_page_test = (
+        self.CreatePageTest.__func__ == Benchmark.CreatePageTest.__func__)
+    assert self._has_original_tbm_options or has_original_create_page_test, (
+        'Cannot override both CreatePageTest and '
+        'CreateTimelineBasedMeasurementOptions.')
+
+  # pylint: disable=unused-argument
+  @classmethod
+  def ShouldDisable(cls, possible_browser):
+    """Override this method to disable a benchmark under specific conditions.
+
+     Supports logic too complex for simple Enabled and Disabled decorators.
+     Decorators are still respected in cases where this function returns False.
+     """
+    return False
+
+  def Run(self, finder_options):
+    """Do not override this method."""
+    return story_runner.RunBenchmark(self, finder_options)
+
+  @property
+  def max_failures(self):
+    return self._max_failures
+
+  @classmethod
+  def Name(cls):
+    return '%s.%s' % (cls.__module__.split('.')[-1], cls.__name__)
+
+  @classmethod
+  def ShouldTearDownStateAfterEachStoryRun(cls):
+    """Override to specify whether to tear down state after each story run.
+
+    Tearing down all states after each story run, e.g., clearing profiles,
+    stopping the browser, stopping local server, etc. So the browser will not be
+    reused among multiple stories. This is particularly useful to get the
+    startup part of launching the browser in each story.
+
+    This should only be used by TimelineBasedMeasurement (TBM) benchmarks, but
+    not by PageTest based benchmarks.
+    """
+    return True
+
+  # NOTE: this is a temporary workaround for crbug.com/645329, do not rely on
+  # this as a stable public API as we may remove this without public notice.
+  @classmethod
+  def IsShouldTearDownStateAfterEachStoryRunOverriden(cls):
+    return (cls.ShouldTearDownStateAfterEachStoryRun.__func__ !=
+            Benchmark.ShouldTearDownStateAfterEachStoryRun.__func__)
+
+  @classmethod
+  def ShouldTearDownStateAfterEachStorySetRun(cls):
+    """Override to specify whether to tear down state after each story set run.
+
+    Defaults to True in order to reset the state and make individual story set
+    repeats more independent of each other. The intended effect is to average
+    out noise in measurements between repeats.
+
+    Long running benchmarks willing to stess test the browser and have it run
+    for long periods of time may switch this value to False.
+
+    This should only be used by TimelineBasedMeasurement (TBM) benchmarks, but
+    not by PageTest based benchmarks.
+    """
+    return True
+
+  @classmethod
+  def AddCommandLineArgs(cls, parser):
+    group = optparse.OptionGroup(parser, '%s test options' % cls.Name())
+    cls.AddBenchmarkCommandLineArgs(group)
+
+    if cls.HasTraceRerunDebugOption():
+      group.add_option(
+          '--rerun-with-debug-trace',
+          action='store_true',
+          help='Rerun option that enables more extensive tracing.')
+
+    if group.option_list:
+      parser.add_option_group(group)
+
+  @classmethod
+  def AddBenchmarkCommandLineArgs(cls, group):
+    del group  # unused
+
+  @classmethod
+  def HasTraceRerunDebugOption(cls):
+    return False
+
+  def GetTraceRerunCommands(self):
+    if self.HasTraceRerunDebugOption():
+      return [['Debug Trace', '--rerun-with-debug-trace']]
+    return []
+
+  def SetupTraceRerunOptions(self, browser_options, tbm_options):
+    if self.HasTraceRerunDebugOption():
+      if browser_options.rerun_with_debug_trace:
+        self.SetupBenchmarkDebugTraceRerunOptions(tbm_options)
+      else:
+        self.SetupBenchmarkDefaultTraceRerunOptions(tbm_options)
+
+  def SetupBenchmarkDefaultTraceRerunOptions(self, tbm_options):
+    """Setup tracing categories associated with default trace option."""
+
+  def SetupBenchmarkDebugTraceRerunOptions(self, tbm_options):
+    """Setup tracing categories associated with debug trace option."""
+
+  @classmethod
+  def SetArgumentDefaults(cls, parser):
+    default_values = parser.get_default_values()
+    invalid_options = [
+        o for o in cls.options if not hasattr(default_values, o)]
+    if invalid_options:
+      raise InvalidOptionsError('Invalid benchmark options: %s',
+                                ', '.join(invalid_options))
+    parser.set_defaults(**cls.options)
+
+  @classmethod
+  def ProcessCommandLineArgs(cls, parser, args):
+    pass
+
+  # pylint: disable=unused-argument
+  @classmethod
+  def ValueCanBeAddedPredicate(cls, value, is_first_result):
+    """Returns whether |value| can be added to the test results.
+
+    Override this method to customize the logic of adding values to test
+    results.
+
+    Args:
+      value: a value.Value instance (except failure.FailureValue,
+        skip.SkipValue or trace.TraceValue which will always be added).
+      is_first_result: True if |value| is the first result for its
+          corresponding story.
+
+    Returns:
+      True if |value| should be added to the test results.
+      Otherwise, it returns False.
+    """
+    return True
+
+  def CustomizeBrowserOptions(self, options):
+    """Add browser options that are required by this benchmark."""
+
+  def GetMetadata(self):
+    return BenchmarkMetadata(
+        self.Name(), self.__doc__, self.GetTraceRerunCommands())
+
+  def CreateTimelineBasedMeasurementOptions(self):
+    """Return the TimelineBasedMeasurementOptions for this Benchmark.
+
+    Override this method to configure a TimelineBasedMeasurement benchmark.
+    Otherwise, override CreatePageTest for PageTest tests. Do not override
+    both methods.
+    """
+    return timeline_based_measurement.Options()
+
+  def CreatePageTest(self, options):  # pylint: disable=unused-argument
+    """Return the PageTest for this Benchmark.
+
+    Override this method for PageTest tests.
+    Override, override CreateTimelineBasedMeasurementOptions to configure
+    TimelineBasedMeasurement tests. Do not override both methods.
+
+    Args:
+      options: a browser_options.BrowserFinderOptions instance
+    Returns:
+      |test()| if |test| is a PageTest class.
+      Otherwise, a TimelineBasedMeasurement instance.
+    """
+    is_page_test = issubclass(self.test, legacy_page_test.LegacyPageTest)
+    is_tbm = self.test == timeline_based_measurement.TimelineBasedMeasurement
+    if not is_page_test and not is_tbm:
+      raise TypeError('"%s" is not a PageTest or a TimelineBasedMeasurement.' %
+                      self.test.__name__)
+    if is_page_test:
+      assert self._has_original_tbm_options, (
+          'Cannot override CreateTimelineBasedMeasurementOptions '
+          'with a PageTest.')
+      return self.test()  # pylint: disable=no-value-for-parameter
+
+    opts = self.CreateTimelineBasedMeasurementOptions()
+    self.SetupTraceRerunOptions(options, opts)
+    return timeline_based_measurement.TimelineBasedMeasurement(opts)
+
+  def CreateStorySet(self, options):
+    """Creates the instance of StorySet used to run the benchmark.
+
+    Can be overridden by subclasses.
+    """
+    del options  # unused
+    # TODO(aiolos, nednguyen, eakufner): replace class attribute page_set with
+    # story_set.
+    if not self.page_set:
+      raise NotImplementedError('This test has no "page_set" attribute.')
+    return self.page_set()  # pylint: disable=not-callable
+
+
+def AddCommandLineArgs(parser):
+  story_runner.AddCommandLineArgs(parser)
+
+
+def ProcessCommandLineArgs(parser, args):
+  story_runner.ProcessCommandLineArgs(parser, args)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/benchmark_run_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/benchmark_run_unittest.py
new file mode 100644
index 0000000..ab6b741
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/benchmark_run_unittest.py
@@ -0,0 +1,114 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from telemetry import benchmark as benchmark_module
+from telemetry import page as page_module
+from telemetry.page import legacy_page_test
+from telemetry import story as story_module
+from telemetry.testing import fakes
+import mock
+
+
+# pylint: disable=abstract-method
+class DummyPageTest(legacy_page_test.LegacyPageTest):
+  def __init__(self):
+    super(DummyPageTest, self).__init__()
+    # Without disabling the above warning, this complains that
+    # ValidateAndMeasurePage is abstract; but defining it complains
+    # that its definition is overridden here.
+    self.ValidateAndMeasurePage = mock.Mock()
+
+
+# More end-to-end tests of Benchmark, shared_page_state and associated
+# classes using telemetry.testing.fakes, to avoid needing to construct
+# a real browser instance.
+
+class FakePage(page_module.Page):
+  def __init__(self, page_set):
+    super(FakePage, self).__init__(
+      url='http://nonexistentserver.com/nonexistentpage.html',
+      page_set=page_set,
+      shared_page_state_class=fakes.FakeSharedPageState)
+    self.RunNavigateSteps = mock.Mock()
+    self.RunPageInteractions = mock.Mock()
+
+class FakeBenchmark(benchmark_module.Benchmark):
+  def __init__(self, max_failures=None):
+    super(FakeBenchmark, self).__init__(max_failures)
+    self._fake_pages = []
+    self._fake_story_set = story_module.StorySet()
+    self._created_story_set = False
+    self.validator = DummyPageTest()
+
+  def CreatePageTest(self, options):
+    return self.validator
+
+  def GetFakeStorySet(self):
+    return self._fake_story_set
+
+  def AddFakePage(self, page):
+    if self._created_story_set:
+      raise Exception('Can not add any more fake pages')
+    self._fake_pages.append(page)
+
+  def CreateStorySet(self, options):
+    if self._created_story_set:
+      raise Exception('Can only create the story set once per FakeBenchmark')
+    for page in self._fake_pages:
+      self._fake_story_set.AddStory(page)
+    self._created_story_set = True
+    return self._fake_story_set
+
+
+class FailingPage(FakePage):
+  def __init__(self, page_set):
+    super(FailingPage, self).__init__(page_set)
+    self.RunNavigateSteps.side_effect = Exception('Deliberate exception')
+
+
+class BenchmarkRunTest(unittest.TestCase):
+  def setupBenchmark(self):
+    finder_options = fakes.CreateBrowserFinderOptions()
+    finder_options.browser_options.platform = fakes.FakeLinuxPlatform()
+    finder_options.output_formats = ['none']
+    finder_options.suppress_gtest_report = True
+    finder_options.output_dir = None
+    finder_options.upload_bucket = 'public'
+    finder_options.upload_results = False
+    benchmarkclass = FakeBenchmark
+    parser = finder_options.CreateParser()
+    benchmark_module.AddCommandLineArgs(parser)
+    benchmarkclass.AddCommandLineArgs(parser)
+    options, _ = parser.parse_args([])
+    benchmark_module.ProcessCommandLineArgs(parser, options)
+    benchmarkclass.ProcessCommandLineArgs(parser, options)
+    benchmark = benchmarkclass()
+    return benchmark, finder_options
+
+  def testPassingPage(self):
+    benchmark, finder_options = self.setupBenchmark()
+    manager = mock.Mock()
+    page = FakePage(benchmark.GetFakeStorySet())
+    page.RunNavigateSteps = manager.page.RunNavigateSteps
+    page.RunPageInteractions = manager.page.RunPageInteractions
+    benchmark.validator.ValidateAndMeasurePage = (
+      manager.validator.ValidateAndMeasurePage)
+    benchmark.AddFakePage(page)
+    self.assertEqual(benchmark.Run(finder_options), 0,
+                     'Test should run with no errors')
+    expected = [mock.call.page.RunNavigateSteps(mock.ANY),
+                mock.call.page.RunPageInteractions(mock.ANY),
+                mock.call.validator.ValidateAndMeasurePage(
+                  page, mock.ANY, mock.ANY)]
+    self.assertTrue(manager.mock_calls == expected)
+
+
+  def testFailingPage(self):
+    benchmark, finder_options = self.setupBenchmark()
+    page = FailingPage(benchmark.GetFakeStorySet())
+    benchmark.AddFakePage(page)
+    self.assertNotEqual(benchmark.Run(finder_options), 0, 'Test should fail')
+    self.assertFalse(page.RunPageInteractions.called)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/benchmark_runner.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/benchmark_runner.py
new file mode 100644
index 0000000..ae5020e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/benchmark_runner.py
@@ -0,0 +1,434 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Parses the command line, discovers the appropriate benchmarks, and runs them.
+
+Handles benchmark configuration, but all the logic for
+actually running the benchmark is in Benchmark and PageRunner."""
+
+import argparse
+import json
+import logging
+import os
+import sys
+
+from telemetry import benchmark
+from telemetry.core import discover
+from telemetry import decorators
+from telemetry.internal.browser import browser_finder
+from telemetry.internal.browser import browser_options
+from telemetry.internal.util import binary_manager
+from telemetry.internal.util import command_line
+from telemetry.internal.util import ps_util
+from telemetry.util import matching
+from telemetry.util import bot_utils
+
+
+# Right now, we only have one of each of our power perf bots. This means that
+# all eligible Telemetry benchmarks are run unsharded, which results in very
+# long (12h) cycle times. We'd like to reduce the number of tests that we run
+# on each bot drastically until we get more of the same hardware to shard tests
+# with, but we can't do so until we've verified that the hardware configuration
+# is a viable one for Chrome Telemetry tests. This is done by seeing at least
+# one all-green test run. As this happens for each bot, we'll add it to this
+# whitelist, making it eligible to run only BattOr power tests.
+GOOD_POWER_PERF_BOT_WHITELIST = [
+  "Mac Power Dual-GPU Perf",
+  "Mac Power Low-End Perf"
+]
+
+
+DEFAULT_LOG_FORMAT = (
+  '(%(levelname)s) %(asctime)s %(module)s.%(funcName)s:%(lineno)d  '
+  '%(message)s')
+
+
+def _IsBenchmarkEnabled(benchmark_class, possible_browser):
+  return (issubclass(benchmark_class, benchmark.Benchmark) and
+          decorators.IsBenchmarkEnabled(benchmark_class, possible_browser))
+
+
+def PrintBenchmarkList(benchmarks, possible_browser, output_pipe=sys.stdout):
+  """ Print benchmarks that are not filtered in the same order of benchmarks in
+  the |benchmarks| list.
+
+  Args:
+    benchmarks: the list of benchmarks to be printed (in the same order of the
+      list).
+    possible_browser: the possible_browser instance that's used for checking
+      which benchmarks are enabled.
+    output_pipe: the stream in which benchmarks are printed on.
+  """
+  if not benchmarks:
+    print >> output_pipe, 'No benchmarks found!'
+    return
+
+  bad_benchmark = next(
+    (b for b in benchmarks if not issubclass(b, benchmark.Benchmark)), None)
+  assert bad_benchmark is None, (
+    '|benchmarks| param contains non benchmark class: %s' % bad_benchmark)
+
+  # Align the benchmark names to the longest one.
+  format_string = '  %%-%ds %%s' % max(len(b.Name()) for b in benchmarks)
+  disabled_benchmarks = []
+
+  print >> output_pipe, 'Available benchmarks %sare:' % (
+      'for %s ' % possible_browser.browser_type if possible_browser else '')
+
+  # Sort the benchmarks by benchmark name.
+  benchmarks = sorted(benchmarks, key=lambda b: b.Name())
+  for b in benchmarks:
+    if not possible_browser or _IsBenchmarkEnabled(b, possible_browser):
+      print >> output_pipe, format_string % (b.Name(), b.Description())
+    else:
+      disabled_benchmarks.append(b)
+
+  if disabled_benchmarks:
+    print >> output_pipe, (
+        '\nDisabled benchmarks for %s are (force run with -d):' %
+        possible_browser.browser_type)
+    for b in disabled_benchmarks:
+      print >> output_pipe, format_string % (b.Name(), b.Description())
+  print >> output_pipe, (
+      'Pass --browser to list benchmarks for another browser.\n')
+
+
+class Help(command_line.OptparseCommand):
+  """Display help information about a command"""
+
+  usage = '[command]'
+
+  def __init__(self, commands):
+    self._all_commands = commands
+
+  def Run(self, args):
+    if len(args.positional_args) == 1:
+      commands = _MatchingCommands(args.positional_args[0], self._all_commands)
+      if len(commands) == 1:
+        command = commands[0]
+        parser = command.CreateParser()
+        command.AddCommandLineArgs(parser, None)
+        parser.print_help()
+        return 0
+
+    print >> sys.stderr, ('usage: %s [command] [<options>]' % _ScriptName())
+    print >> sys.stderr, 'Available commands are:'
+    for command in self._all_commands:
+      print >> sys.stderr, '  %-10s %s' % (
+          command.Name(), command.Description())
+    print >> sys.stderr, ('"%s help <command>" to see usage information '
+                          'for a specific command.' % _ScriptName())
+    return 0
+
+
+class List(command_line.OptparseCommand):
+  """Lists the available benchmarks"""
+
+  usage = '[benchmark_name] [<options>]'
+
+  @classmethod
+  def CreateParser(cls):
+    options = browser_options.BrowserFinderOptions()
+    parser = options.CreateParser('%%prog %s %s' % (cls.Name(), cls.usage))
+    return parser
+
+  @classmethod
+  def AddCommandLineArgs(cls, parser, _):
+    parser.add_option('-j', '--json-output-file', type='string')
+    parser.add_option('-n', '--num-shards', type='int', default=1)
+
+  @classmethod
+  def ProcessCommandLineArgs(cls, parser, args, environment):
+    if not args.positional_args:
+      args.benchmarks = _Benchmarks(environment)
+    elif len(args.positional_args) == 1:
+      args.benchmarks = _MatchBenchmarkName(args.positional_args[0],
+                                            environment, exact_matches=False)
+    else:
+      parser.error('Must provide at most one benchmark name.')
+
+  def Run(self, args):
+    # Set at least log info level for List command.
+    # TODO(nedn): remove this once crbug.com/656224 is resolved. The recipe
+    # should be change to use verbose logging instead.
+    logging.getLogger().setLevel(logging.INFO)
+    possible_browser = browser_finder.FindBrowser(args)
+    if args.browser_type in (
+        'release', 'release_x64', 'debug', 'debug_x64', 'canary',
+        'android-chromium', 'android-chrome'):
+      args.browser_type = 'reference'
+      possible_reference_browser = browser_finder.FindBrowser(args)
+    else:
+      possible_reference_browser = None
+    if args.json_output_file:
+      with open(args.json_output_file, 'w') as f:
+        f.write(_GetJsonBenchmarkList(possible_browser,
+                                      possible_reference_browser,
+                                      args.benchmarks, args.num_shards))
+    else:
+      PrintBenchmarkList(args.benchmarks, possible_browser)
+    return 0
+
+
+class Run(command_line.OptparseCommand):
+  """Run one or more benchmarks (default)"""
+
+  usage = 'benchmark_name [page_set] [<options>]'
+
+  @classmethod
+  def CreateParser(cls):
+    options = browser_options.BrowserFinderOptions()
+    parser = options.CreateParser('%%prog %s %s' % (cls.Name(), cls.usage))
+    return parser
+
+  @classmethod
+  def AddCommandLineArgs(cls, parser, environment):
+    benchmark.AddCommandLineArgs(parser)
+
+    # Allow benchmarks to add their own command line options.
+    matching_benchmarks = []
+    for arg in sys.argv[1:]:
+      matching_benchmarks += _MatchBenchmarkName(arg, environment)
+
+    if matching_benchmarks:
+      # TODO(dtu): After move to argparse, add command-line args for all
+      # benchmarks to subparser. Using subparsers will avoid duplicate
+      # arguments.
+      matching_benchmark = matching_benchmarks.pop()
+      matching_benchmark.AddCommandLineArgs(parser)
+      # The benchmark's options override the defaults!
+      matching_benchmark.SetArgumentDefaults(parser)
+
+  @classmethod
+  def ProcessCommandLineArgs(cls, parser, args, environment):
+    all_benchmarks = _Benchmarks(environment)
+    if not args.positional_args:
+      possible_browser = (
+          browser_finder.FindBrowser(args) if args.browser_type else None)
+      PrintBenchmarkList(all_benchmarks, possible_browser)
+      sys.exit(-1)
+
+    input_benchmark_name = args.positional_args[0]
+    matching_benchmarks = _MatchBenchmarkName(input_benchmark_name, environment)
+    if not matching_benchmarks:
+      print >> sys.stderr, 'No benchmark named "%s".' % input_benchmark_name
+      print >> sys.stderr
+      most_likely_matched_benchmarks = matching.GetMostLikelyMatchedObject(
+          all_benchmarks, input_benchmark_name, lambda x: x.Name())
+      if most_likely_matched_benchmarks:
+        print >> sys.stderr, 'Do you mean any of those benchmarks below?'
+        PrintBenchmarkList(most_likely_matched_benchmarks, None, sys.stderr)
+      sys.exit(-1)
+
+    if len(matching_benchmarks) > 1:
+      print >> sys.stderr, ('Multiple benchmarks named "%s".' %
+                            input_benchmark_name)
+      print >> sys.stderr, 'Did you mean one of these?'
+      print >> sys.stderr
+      PrintBenchmarkList(matching_benchmarks, None, sys.stderr)
+      sys.exit(-1)
+
+    benchmark_class = matching_benchmarks.pop()
+    if len(args.positional_args) > 1:
+      parser.error('Too many arguments.')
+
+    assert issubclass(benchmark_class, benchmark.Benchmark), (
+        'Trying to run a non-Benchmark?!')
+
+    benchmark.ProcessCommandLineArgs(parser, args)
+    benchmark_class.ProcessCommandLineArgs(parser, args)
+
+    cls._benchmark = benchmark_class
+
+  def Run(self, args):
+    return min(255, self._benchmark().Run(args))
+
+
+def _ScriptName():
+  return os.path.basename(sys.argv[0])
+
+
+def _MatchingCommands(string, commands):
+  return [command for command in commands
+         if command.Name().startswith(string)]
+
+@decorators.Cache
+def _Benchmarks(environment):
+  benchmarks = []
+  for search_dir in environment.benchmark_dirs:
+    benchmarks += discover.DiscoverClasses(search_dir,
+                                           environment.top_level_dir,
+                                           benchmark.Benchmark,
+                                           index_by_class_name=True).values()
+  return benchmarks
+
+def _MatchBenchmarkName(input_benchmark_name, environment, exact_matches=True):
+  def _Matches(input_string, search_string):
+    if search_string.startswith(input_string):
+      return True
+    for part in search_string.split('.'):
+      if part.startswith(input_string):
+        return True
+    return False
+
+  # Exact matching.
+  if exact_matches:
+    # Don't add aliases to search dict, only allow exact matching for them.
+    if input_benchmark_name in environment.benchmark_aliases:
+      exact_match = environment.benchmark_aliases[input_benchmark_name]
+    else:
+      exact_match = input_benchmark_name
+
+    for benchmark_class in _Benchmarks(environment):
+      if exact_match == benchmark_class.Name():
+        return [benchmark_class]
+    return []
+
+  # Fuzzy matching.
+  return [benchmark_class for benchmark_class in _Benchmarks(environment)
+          if _Matches(input_benchmark_name, benchmark_class.Name())]
+
+
+def GetBenchmarkByName(name, environment):
+  matched = _MatchBenchmarkName(name, environment, exact_matches=True)
+  # With exact_matches, len(matched) is either 0 or 1.
+  if len(matched) == 0:
+    return None
+  return matched[0]
+
+
+def _GetJsonBenchmarkList(possible_browser, possible_reference_browser,
+                          benchmark_classes, num_shards):
+  """Returns a list of all enabled benchmarks in a JSON format expected by
+  buildbots.
+
+  JSON format:
+  { "version": <int>,
+    "steps": {
+      <string>: {
+        "device_affinity": <int>,
+        "cmd": <string>,
+        "perf_dashboard_id": <string>,
+      },
+      ...
+    }
+  }
+  """
+  # TODO(charliea): Remove this once we have more power perf bots.
+  only_run_battor_benchmarks = False
+  print 'Environment variables: ', os.environ
+  if os.environ.get('BUILDBOT_BUILDERNAME') in GOOD_POWER_PERF_BOT_WHITELIST:
+    only_run_battor_benchmarks = True
+
+  output = {
+    'version': 1,
+    'steps': {
+    }
+  }
+  for benchmark_class in benchmark_classes:
+    if not _IsBenchmarkEnabled(benchmark_class, possible_browser):
+      continue
+
+    base_name = benchmark_class.Name()
+    # TODO(charliea): Remove this once we have more power perf bots.
+    # Only run battor power benchmarks to reduce the cycle time of this bot.
+    # TODO(rnephew): Enable media.* and power.* tests when Mac BattOr issue
+    # is solved.
+    if only_run_battor_benchmarks and not base_name.startswith('battor'):
+      continue
+    base_cmd = [sys.executable, os.path.realpath(sys.argv[0]),
+                '-v', '--output-format=chartjson', '--upload-results',
+                base_name]
+    perf_dashboard_id = base_name
+
+    device_affinity = bot_utils.GetDeviceAffinity(num_shards, base_name)
+
+    output['steps'][base_name] = {
+      'cmd': ' '.join(base_cmd + [
+            '--browser=%s' % possible_browser.browser_type]),
+      'device_affinity': device_affinity,
+      'perf_dashboard_id': perf_dashboard_id,
+    }
+    if (possible_reference_browser and
+        _IsBenchmarkEnabled(benchmark_class, possible_reference_browser)):
+      output['steps'][base_name + '.reference'] = {
+        'cmd': ' '.join(base_cmd + [
+              '--browser=reference', '--output-trace-tag=_ref']),
+        'device_affinity': device_affinity,
+        'perf_dashboard_id': perf_dashboard_id,
+      }
+
+  return json.dumps(output, indent=2, sort_keys=True)
+
+
+def main(environment, extra_commands=None, **log_config_kwargs):
+  # The log level is set in browser_options.
+  log_config_kwargs.pop('level', None)
+  log_config_kwargs.setdefault('format', DEFAULT_LOG_FORMAT)
+  logging.basicConfig(**log_config_kwargs)
+
+  ps_util.EnableListingStrayProcessesUponExitHook()
+
+  # Get the command name from the command line.
+  if len(sys.argv) > 1 and sys.argv[1] == '--help':
+    sys.argv[1] = 'help'
+
+  command_name = 'run'
+  for arg in sys.argv[1:]:
+    if not arg.startswith('-'):
+      command_name = arg
+      break
+
+  # TODO(eakuefner): Remove this hack after we port to argparse.
+  if command_name == 'help' and len(sys.argv) > 2 and sys.argv[2] == 'run':
+    command_name = 'run'
+    sys.argv[2] = '--help'
+
+  if extra_commands is None:
+    extra_commands = []
+  all_commands = [Help, List, Run] + extra_commands
+
+  # Validate and interpret the command name.
+  commands = _MatchingCommands(command_name, all_commands)
+  if len(commands) > 1:
+    print >> sys.stderr, ('"%s" is not a %s command. Did you mean one of these?'
+                          % (command_name, _ScriptName()))
+    for command in commands:
+      print >> sys.stderr, '  %-10s %s' % (
+          command.Name(), command.Description())
+    return 1
+  if commands:
+    command = commands[0]
+  else:
+    command = Run
+
+  binary_manager.InitDependencyManager(environment.client_configs)
+
+  # Parse and run the command.
+  parser = command.CreateParser()
+  command.AddCommandLineArgs(parser, environment)
+
+  # Set the default chrome root variable.
+  parser.set_defaults(chrome_root=environment.default_chrome_root)
+
+
+  if isinstance(parser, argparse.ArgumentParser):
+    commandline_args = sys.argv[1:]
+    options, args = parser.parse_known_args(commandline_args[1:])
+    command.ProcessCommandLineArgs(parser, options, args, environment)
+  else:
+    options, args = parser.parse_args()
+    if commands:
+      args = args[1:]
+    options.positional_args = args
+    command.ProcessCommandLineArgs(parser, options, environment)
+
+  if command == Help:
+    command_instance = command(all_commands)
+  else:
+    command_instance = command()
+  if isinstance(command_instance, command_line.OptparseCommand):
+    return command_instance.Run(options)
+  else:
+    return command_instance.Run(options, args)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/benchmark_runner_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/benchmark_runner_unittest.py
new file mode 100644
index 0000000..618b6d5
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/benchmark_runner_unittest.py
@@ -0,0 +1,116 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from telemetry import benchmark
+from telemetry import benchmark_runner
+from telemetry.testing import stream
+import mock
+
+
+class BenchmarkFoo(benchmark.Benchmark):
+  """ Benchmark Foo for testing."""
+
+  @classmethod
+  def Name(cls):
+    return 'FooBenchmark'
+
+
+class BenchmarkBar(benchmark.Benchmark):
+  """ Benchmark Bar for testing long description line."""
+
+  @classmethod
+  def Name(cls):
+    return 'BarBenchmarkkkkk'
+
+class UnusualBenchmark(benchmark.Benchmark):
+  @classmethod
+  def Name(cls):
+    return 'I have a very unusual name'
+
+
+class BenchmarkRunnerUnittest(unittest.TestCase):
+  def setUp(self):
+    self._stream = stream.TestOutputStream()
+    self._mock_possible_browser = mock.MagicMock()
+    self._mock_possible_browser.browser_type = 'TestBrowser'
+
+  def testPrintBenchmarkListWithNoDisabledBenchmark(self):
+    expected_printed_stream = (
+        'Available benchmarks for TestBrowser are:\n'
+        '  BarBenchmarkkkkk  Benchmark Bar for testing long description line.\n'
+        '  FooBenchmark      Benchmark Foo for testing.\n'
+        'Pass --browser to list benchmarks for another browser.\n\n')
+    with mock.patch('telemetry.benchmark_runner.decorators') as mock_module:
+      mock_module.IsEnabled.return_value = (True, None)
+      benchmark_runner.PrintBenchmarkList(
+        [BenchmarkFoo, BenchmarkBar], self._mock_possible_browser, self._stream)
+      self.assertEquals(expected_printed_stream, self._stream.output_data)
+
+  def testPrintBenchmarkListWithOneDisabledBenchmark(self):
+    expected_printed_stream = (
+        'Available benchmarks for TestBrowser are:\n'
+        '  FooBenchmark      Benchmark Foo for testing.\n'
+        '\n'
+        'Disabled benchmarks for TestBrowser are (force run with -d):\n'
+        '  BarBenchmarkkkkk  Benchmark Bar for testing long description line.\n'
+        'Pass --browser to list benchmarks for another browser.\n\n')
+    with mock.patch('telemetry.benchmark_runner.decorators') as mock_module:
+      def FakeIsEnabled(benchmark_class, _):
+        if benchmark_class is BenchmarkFoo:
+          return True
+        else:
+          return False
+
+      mock_module.IsBenchmarkEnabled = FakeIsEnabled
+      benchmark_runner.PrintBenchmarkList(
+        [BenchmarkFoo, BenchmarkBar], self._mock_possible_browser, self._stream)
+      self.assertEquals(expected_printed_stream, self._stream.output_data)
+
+  def testShouldDisable(self):
+    """Ensure that overridden ShouldDisable class methods are respected."""
+    expected_printed_stream = (
+        'Available benchmarks for TestBrowser are:\n'
+        '  BarBenchmarkkkkk  Benchmark Bar for testing long description line.\n'
+        '\n'
+        'Disabled benchmarks for TestBrowser are (force run with -d):\n'
+        '  FooBenchmark      Benchmark Foo for testing.\n'
+        'Pass --browser to list benchmarks for another browser.\n\n')
+    @classmethod
+    def FakeShouldDisable(cls, possible_browser):
+      del possible_browser  # unused
+      return cls is BenchmarkFoo
+    BenchmarkFoo.ShouldDisable = FakeShouldDisable
+    BenchmarkBar.ShouldDisable = FakeShouldDisable
+    benchmark_runner.PrintBenchmarkList(
+      [BenchmarkFoo, BenchmarkBar], self._mock_possible_browser, self._stream)
+    self.assertEquals(expected_printed_stream, self._stream.output_data)
+
+  def testShouldDisableComplex(self):
+    """Ensure that browser-dependent ShouldDisable overrides are respected."""
+    expected_printed_stream = (
+        # Expected output for 'TestBrowser':
+        'Available benchmarks for TestBrowser are:\n'
+        '  FooBenchmark      Benchmark Foo for testing.\n'
+        '\n'
+        'Disabled benchmarks for TestBrowser are (force run with -d):\n'
+        '  BarBenchmarkkkkk  Benchmark Bar for testing long description line.\n'
+        'Pass --browser to list benchmarks for another browser.\n\n'
+        # Expected output for 'MockBrowser':
+        'Available benchmarks for MockBrowser are:\n'
+        '  BarBenchmarkkkkk  Benchmark Bar for testing long description line.\n'
+        '  FooBenchmark      Benchmark Foo for testing.\n'
+        'Pass --browser to list benchmarks for another browser.\n\n')
+    @classmethod
+    def FakeShouldDisable(cls, possible_browser):
+      return cls is BenchmarkBar and not 'Mock' in possible_browser.browser_type
+    BenchmarkFoo.ShouldDisable = FakeShouldDisable
+    BenchmarkBar.ShouldDisable = FakeShouldDisable
+    benchmark_runner.PrintBenchmarkList(
+      [BenchmarkFoo, BenchmarkBar], self._mock_possible_browser, self._stream)
+    self._mock_possible_browser.browser_type = 'MockBrowser'
+    benchmark_runner.PrintBenchmarkList(
+      [BenchmarkFoo, BenchmarkBar], self._mock_possible_browser, self._stream)
+    self.assertEquals(expected_printed_stream, self._stream.output_data)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/benchmark_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/benchmark_unittest.py
new file mode 100644
index 0000000..cb23c6f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/benchmark_unittest.py
@@ -0,0 +1,161 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import optparse
+import unittest
+
+from telemetry import android
+from telemetry import benchmark
+from telemetry.testing import options_for_unittests
+from telemetry.internal import story_runner
+from telemetry import page
+from telemetry.page import legacy_page_test
+from telemetry.page import shared_page_state
+from telemetry import story as story_module
+from telemetry.web_perf import timeline_based_measurement
+
+
+class DummyPageTest(legacy_page_test.LegacyPageTest):
+  def ValidateAndMeasurePage(self, *_):
+    pass
+
+
+class TestBenchmark(benchmark.Benchmark):
+  def __init__(self, story):
+    super(TestBenchmark, self).__init__()
+    self._story_set = story_module.StorySet()
+    self._story_set.AddStory(story)
+
+  def CreatePageTest(self, _):
+    return DummyPageTest()
+
+  def CreateStorySet(self, _):
+    return self._story_set
+
+
+class BenchmarkTest(unittest.TestCase):
+
+  def testPageTestWithIncompatibleStory(self):
+    b = TestBenchmark(story_module.Story(
+        shared_state_class=shared_page_state.SharedPageState))
+    with self.assertRaisesRegexp(
+        Exception, 'containing only telemetry.page.Page stories'):
+      b.Run(options_for_unittests.GetCopy())
+
+    state_class = story_module.SharedState
+    b = TestBenchmark(story_module.Story(
+        shared_state_class=state_class))
+    with self.assertRaisesRegexp(
+        Exception, 'containing only telemetry.page.Page stories'):
+      b.Run(options_for_unittests.GetCopy())
+
+    b = TestBenchmark(android.AndroidStory(start_intent=None))
+    with self.assertRaisesRegexp(
+        Exception, 'containing only telemetry.page.Page stories'):
+      b.Run(options_for_unittests.GetCopy())
+
+  def testPageTestWithCompatibleStory(self):
+    original_run_fn = story_runner.Run
+    was_run = [False]
+    def RunStub(*arg, **kwargs):
+      del arg, kwargs
+      was_run[0] = True
+    story_runner.Run = RunStub
+
+    try:
+      options = options_for_unittests.GetCopy()
+      options.output_formats = ['none']
+      options.suppress_gtest_report = True
+      parser = optparse.OptionParser()
+      benchmark.AddCommandLineArgs(parser)
+      options.MergeDefaultValues(parser.get_default_values())
+
+      b = TestBenchmark(page.Page(url='about:blank'))
+      b.Run(options)
+    finally:
+      story_runner.Run = original_run_fn
+
+    self.assertTrue(was_run[0])
+
+  def testOverriddenTbmOptionsAndPageTestRaises(self):
+    class FakeTimelineBasedMeasurementOptions(object):
+      pass
+
+    class OverrideBothBenchmark(benchmark.Benchmark):
+      def CreatePageTest(self, _):
+        return DummyPageTest()
+      def CreateTimelineBasedMeasurementOptions(self):
+        return FakeTimelineBasedMeasurementOptions()
+
+    assertion_regex = (
+        'Cannot override both CreatePageTest and '
+        'CreateTimelineBasedMeasurementOptions')
+    with self.assertRaisesRegexp(AssertionError, assertion_regex):
+      OverrideBothBenchmark()
+
+  def testBenchmarkMakesTbmTestByDefault(self):
+    class DefaultTbmBenchmark(benchmark.Benchmark):
+      pass
+
+    self.assertIsInstance(
+        DefaultTbmBenchmark().CreatePageTest(options=None),
+        timeline_based_measurement.TimelineBasedMeasurement)
+
+  def testUnknownTestTypeRaises(self):
+    class UnknownTestType(object):
+      pass
+    class UnknownTestTypeBenchmark(benchmark.Benchmark):
+      test = UnknownTestType
+
+    type_error_regex = (
+        '"UnknownTestType" is not a PageTest or a TimelineBasedMeasurement')
+    with self.assertRaisesRegexp(TypeError, type_error_regex):
+      UnknownTestTypeBenchmark().CreatePageTest(options=None)
+
+  def testOverriddenTbmOptionsAndPageTestTestAttributeRaises(self):
+    class FakeTimelineBasedMeasurementOptions(object):
+      pass
+
+    class OverrideOptionsOnPageTestBenchmark(benchmark.Benchmark):
+      test = DummyPageTest
+      def CreateTimelineBasedMeasurementOptions(self):
+        return FakeTimelineBasedMeasurementOptions()
+
+    assertion_regex = (
+        'Cannot override CreateTimelineBasedMeasurementOptions '
+        'with a PageTest')
+    with self.assertRaisesRegexp(AssertionError, assertion_regex):
+      OverrideOptionsOnPageTestBenchmark().CreatePageTest(options=None)
+
+  def testBenchmarkPredicate(self):
+    class PredicateBenchmark(TestBenchmark):
+      @classmethod
+      def ValueCanBeAddedPredicate(cls, value, is_first_result):
+        return False
+
+    original_run_fn = story_runner.Run
+    validPredicate = [False]
+
+    def RunStub(test, story_set_module, finder_options, results,
+                *args, **kwargs): # pylint: disable=unused-argument
+      predicate = results._value_can_be_added_predicate
+      valid = predicate == PredicateBenchmark.ValueCanBeAddedPredicate
+      validPredicate[0] = valid
+
+    story_runner.Run = RunStub
+
+    try:
+      options = options_for_unittests.GetCopy()
+      options.output_formats = ['none']
+      options.suppress_gtest_report = True
+      parser = optparse.OptionParser()
+      benchmark.AddCommandLineArgs(parser)
+      options.MergeDefaultValues(parser.get_default_values())
+
+      b = PredicateBenchmark(page.Page(url='about:blank'))
+      b.Run(options)
+    finally:
+      story_runner.Run = original_run_fn
+
+    self.assertTrue(validPredicate[0])
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/__init__.py
new file mode 100644
index 0000000..efcc9c3
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/__init__.py
@@ -0,0 +1,3 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/android_action_runner.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/android_action_runner.py
new file mode 100644
index 0000000..07fdccb
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/android_action_runner.py
@@ -0,0 +1,168 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+import time
+
+from devil.android.sdk import keyevent
+
+import py_utils
+
+
+class ActionNotSupported(Exception):
+  pass
+
+
+class AndroidActionRunner(object):
+  """Provides an API for interacting with an android device.
+
+  This makes use of functionality provided by the android input command. None
+  of the gestures here are guaranteed to be performant for telemetry tests and
+  there is no official support for this API.
+
+  TODO(ariblue): Replace this API with a better implementation for interacting
+  with native components.
+  """
+
+  def __init__(self, platform_backend):
+    self._platform_backend = platform_backend
+
+  def SmoothScrollBy(self, left_start_coord, top_start_coord, direction,
+                     scroll_distance):
+    """Perform gesture to scroll down on the android device.
+    """
+    if direction not in ['down', 'up', 'left', 'right']:
+      raise ActionNotSupported('Invalid scroll direction: %s' % direction)
+
+    # This velocity is slower so that the exact distance we specify is the
+    # distance the page travels.
+    duration = scroll_distance
+
+    # Note that the default behavior is swiping up for scrolling down.
+    if direction == 'down':
+      left_end_coord = left_start_coord
+      top_end_coord = top_start_coord - scroll_distance
+    elif direction == 'up':
+      left_end_coord = left_start_coord
+      top_end_coord = top_start_coord + scroll_distance
+    elif direction == 'right':
+      left_end_coord = left_start_coord - scroll_distance
+      top_end_coord = top_start_coord
+    elif direction == 'left':
+      left_end_coord = left_start_coord + scroll_distance
+      top_end_coord = top_start_coord
+
+    self.InputSwipe(left_start_coord, top_start_coord, left_end_coord,
+                    top_end_coord, duration)
+
+  def Wait(self, seconds):
+    """Wait for the number of seconds specified.
+
+    Args:
+      seconds: The number of seconds to wait.
+    """
+    time.sleep(seconds)
+
+  def InputText(self, string):
+    """Convert the characters of the string into key events and send to device.
+
+    Args:
+      string: The string to send to the device.
+    """
+    self._platform_backend.device.RunShellCommand(
+        ['input', 'text', string], check_return=True)
+
+  def InputKeyEvent(self, keycode):
+    """Send a single key input to the device.
+
+    See the devil.android.sdk.keyevent module for suitable keycode values.
+
+    Args:
+      keycode: A key code number that will be sent to the device.
+    """
+    self._platform_backend.device.SendKeyEvent(keycode)
+
+  def InputTap(self, x_coord, y_coord):
+    """Perform a tap input at the given coordinates.
+
+    Args:
+      x_coord: The x coordinate of the tap event.
+      y_coord: The y coordinate of the tap event.
+    """
+    self._platform_backend.device.RunShellCommand(
+        ['input', 'tap', str(x_coord), str(y_coord)], check_return=True)
+
+  def InputSwipe(self, left_start_coord, top_start_coord, left_end_coord,
+                 top_end_coord, duration):
+    """Perform a swipe input.
+
+    Args:
+      left_start_coord: The horizontal starting coordinate of the gesture
+      top_start_coord: The vertical starting coordinate of the gesture
+      left_end_coord: The horizontal ending coordinate of the gesture
+      top_end_coord: The vertical ending coordinate of the gesture
+      duration: The length of time of the swipe in milliseconds
+    """
+    cmd = ['input', 'swipe']
+    cmd.expand(str(x) for x in (left_start_coord, top_start_coord,
+                                left_end_coord, top_end_coord, duration))
+    self._platform_backend.device.RunShellCommand(cmd, check_return=True)
+
+  def InputPress(self):
+    """Perform a press input."""
+    self._platform_backend.device.RunShellCommand(
+        ['input', 'press'], check_return=True)
+
+  def InputRoll(self, dx, dy):
+    """Perform a roll input. This sends a simple zero-pressure move event.
+
+    Args:
+      dx: Change in the x coordinate due to move.
+      dy: Change in the y coordinate due to move.
+    """
+    self._platform_backend.device.RunShellCommand(
+        ['input', 'roll', str(dx), str(dy)], check_return=True)
+
+  def TurnScreenOn(self):
+    """If device screen is off, turn screen on.
+    If the screen is already on, log a warning and return immediately.
+
+    Raises:
+      Timeout: If the screen is off and device fails to turn screen on.
+    """
+    self._platform_backend.device.SetScreen(True)
+    py_utils.WaitFor(self._platform_backend.device.IsScreenOn, 5)
+
+  def TurnScreenOff(self):
+    """If device screen is on, turn screen off.
+    If the screen is already off, log a warning and return immediately.
+
+    Raises:
+      Timeout: If the screen is on and device fails to turn screen off.
+    """
+
+    def is_screen_off():
+      return not self._platform_backend.device.IsScreenOn()
+
+    self._platform_backend.device.SetScreen(False)
+    py_utils.WaitFor(is_screen_off, 5)
+
+  def UnlockScreen(self):
+    """If device screen is locked, unlocks it.
+    If the device is not locked, log a warning and return immediately.
+
+    Raises:
+      Timeout: If device fails to unlock screen.
+    """
+
+    def is_screen_unlocked():
+      return not self._platform_backend.IsScreenLocked()
+
+    if self._platform_backend.IsScreenLocked():
+      self.InputKeyEvent(keyevent.KEYCODE_MENU)
+    else:
+      logging.warning('Screen not locked when expected.')
+      return
+
+    py_utils.WaitFor(is_screen_unlocked, 5)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/android_platform.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/android_platform.py
new file mode 100644
index 0000000..b2e7e84
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/android_platform.py
@@ -0,0 +1,60 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.core import android_action_runner
+from telemetry.core import platform
+from telemetry.internal.app import android_app
+from telemetry.internal.backends import android_app_backend
+
+
+class AndroidPlatform(platform.Platform):
+
+  def __init__(self, platform_backend):
+    super(AndroidPlatform, self).__init__(platform_backend)
+    self._android_action_runner = android_action_runner.AndroidActionRunner(
+        platform_backend)
+
+  def Initialize(self):
+    self._platform_backend.Initialize()
+
+  @property
+  def android_action_runner(self):
+    return self._android_action_runner
+
+  @property
+  def system_ui(self):
+    """Returns an AppUi object to interact with Android's system UI.
+
+    See devil.android.app_ui for the documentation of the API provided.
+    """
+    return self._platform_backend.GetSystemUi()
+
+  def IsSvelte(self):
+    return self._platform_backend.IsSvelte()
+
+  def LaunchAndroidApplication(self,
+                               start_intent,
+                               is_app_ready_predicate=None,
+                               app_has_webviews=True):
+    """Launches an Android application given the intent.
+
+    Args:
+      start_intent: The intent to use to start the app.
+      is_app_ready_predicate: A predicate function to determine
+          whether the app is ready. This is a function that takes an
+          AndroidApp instance and return a boolean. When it is not passed in,
+          the app is ready when the intent to launch it is completed.
+      app_has_webviews: A boolean indicating whether the app is expected to
+          contain any WebViews. If True, the app will be launched with
+          appropriate webview flags, and the GetWebViews method of the returned
+          object may be used to access them.
+
+    Returns:
+      A reference to the android_app launched.
+    """
+    self._platform_backend.DismissCrashDialogIfNeeded()
+    app_backend = android_app_backend.AndroidAppBackend(
+        self._platform_backend, start_intent, is_app_ready_predicate,
+        app_has_webviews)
+    return android_app.AndroidApp(app_backend, self._platform_backend)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/cros_interface.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/cros_interface.py
new file mode 100644
index 0000000..bf98d3f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/cros_interface.py
@@ -0,0 +1,570 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""A wrapper around ssh for common operations on a CrOS-based device"""
+import logging
+import os
+import re
+import shutil
+import stat
+import subprocess
+import tempfile
+
+# Some developers' workflow includes running the Chrome process from
+# /usr/local/... instead of the default location. We have to check for both
+# paths in order to support this workflow.
+_CHROME_PROCESS_REGEX = [re.compile(r'^/opt/google/chrome/chrome '),
+                         re.compile(r'^/usr/local/?.*/chrome/chrome ')]
+
+
+def RunCmd(args, cwd=None, quiet=False):
+  """Opens a subprocess to execute a program and returns its return value.
+
+  Args:
+    args: A string or a sequence of program arguments. The program to execute is
+      the string or the first item in the args sequence.
+    cwd: If not None, the subprocess's current directory will be changed to
+      |cwd| before it's executed.
+
+  Returns:
+    Return code from the command execution.
+  """
+  if not quiet:
+    logging.debug(' '.join(args) + ' ' + (cwd or ''))
+  with open(os.devnull, 'w') as devnull:
+    p = subprocess.Popen(args=args,
+                         cwd=cwd,
+                         stdout=devnull,
+                         stderr=devnull,
+                         stdin=devnull,
+                         shell=False)
+    return p.wait()
+
+
+def GetAllCmdOutput(args, cwd=None, quiet=False):
+  """Open a subprocess to execute a program and returns its output.
+
+  Args:
+    args: A string or a sequence of program arguments. The program to execute is
+      the string or the first item in the args sequence.
+    cwd: If not None, the subprocess's current directory will be changed to
+      |cwd| before it's executed.
+
+  Returns:
+    Captures and returns the command's stdout.
+    Prints the command's stderr to logger (which defaults to stdout).
+  """
+  if not quiet:
+    logging.debug(' '.join(args) + ' ' + (cwd or ''))
+  with open(os.devnull, 'w') as devnull:
+    p = subprocess.Popen(args=args,
+                         cwd=cwd,
+                         stdout=subprocess.PIPE,
+                         stderr=subprocess.PIPE,
+                         stdin=devnull)
+    stdout, stderr = p.communicate()
+    if not quiet:
+      logging.debug(' > stdout=[%s], stderr=[%s]', stdout, stderr)
+    return stdout, stderr
+
+
+def HasSSH():
+  try:
+    RunCmd(['ssh'], quiet=True)
+    RunCmd(['scp'], quiet=True)
+    logging.debug("HasSSH()->True")
+    return True
+  except OSError:
+    logging.debug("HasSSH()->False")
+    return False
+
+
+class LoginException(Exception):
+  pass
+
+
+class KeylessLoginRequiredException(LoginException):
+  pass
+
+
+class DNSFailureException(LoginException):
+  pass
+
+
+class CrOSInterface(object):
+
+  def __init__(self, hostname=None, ssh_port=None, ssh_identity=None):
+    self._hostname = hostname
+    self._ssh_port = ssh_port
+
+    # List of ports generated from GetRemotePort() that may not be in use yet.
+    self._reserved_ports = []
+
+    if self.local:
+      return
+
+    self._ssh_identity = None
+    self._ssh_args = ['-o ConnectTimeout=5', '-o StrictHostKeyChecking=no',
+                      '-o KbdInteractiveAuthentication=no',
+                      '-o PreferredAuthentications=publickey',
+                      '-o UserKnownHostsFile=/dev/null', '-o ControlMaster=no']
+
+    if ssh_identity:
+      self._ssh_identity = os.path.abspath(os.path.expanduser(ssh_identity))
+      os.chmod(self._ssh_identity, stat.S_IREAD)
+
+    # Establish master SSH connection using ControlPersist.
+    # Since only one test will be run on a remote host at a time,
+    # the control socket filename can be telemetry@hostname.
+    self._ssh_control_file = '/tmp/' + 'telemetry' + '@' + hostname
+    with open(os.devnull, 'w') as devnull:
+      subprocess.call(
+          self.FormSSHCommandLine(['-M', '-o ControlPersist=yes']),
+          stdin=devnull,
+          stdout=devnull,
+          stderr=devnull)
+
+  def __enter__(self):
+    return self
+
+  def __exit__(self, *args):
+    self.CloseConnection()
+
+  @property
+  def local(self):
+    return not self._hostname
+
+  @property
+  def hostname(self):
+    return self._hostname
+
+  @property
+  def ssh_port(self):
+    return self._ssh_port
+
+  def FormSSHCommandLine(self, args, extra_ssh_args=None):
+    """Constructs a subprocess-suitable command line for `ssh'.
+    """
+    if self.local:
+      # We run the command through the shell locally for consistency with
+      # how commands are run through SSH (crbug.com/239161). This work
+      # around will be unnecessary once we implement a persistent SSH
+      # connection to run remote commands (crbug.com/239607).
+      return ['sh', '-c', " ".join(args)]
+
+    full_args = ['ssh', '-o ForwardX11=no', '-o ForwardX11Trusted=no', '-n',
+                 '-S', self._ssh_control_file] + self._ssh_args
+    if self._ssh_identity is not None:
+      full_args.extend(['-i', self._ssh_identity])
+    if extra_ssh_args:
+      full_args.extend(extra_ssh_args)
+    full_args.append('root@%s' % self._hostname)
+    full_args.append('-p%d' % self._ssh_port)
+    full_args.extend(args)
+    return full_args
+
+  def _FormSCPCommandLine(self, src, dst, extra_scp_args=None):
+    """Constructs a subprocess-suitable command line for `scp'.
+
+    Note: this function is not designed to work with IPv6 addresses, which need
+    to have their addresses enclosed in brackets and a '-6' flag supplied
+    in order to be properly parsed by `scp'.
+    """
+    assert not self.local, "Cannot use SCP on local target."
+
+    args = ['scp', '-P', str(self._ssh_port)] + self._ssh_args
+    if self._ssh_identity:
+      args.extend(['-i', self._ssh_identity])
+    if extra_scp_args:
+      args.extend(extra_scp_args)
+    args += [src, dst]
+    return args
+
+  def _FormSCPToRemote(self,
+                       source,
+                       remote_dest,
+                       extra_scp_args=None,
+                       user='root'):
+    return self._FormSCPCommandLine(source,
+                                    '%s@%s:%s' % (user, self._hostname,
+                                                  remote_dest),
+                                    extra_scp_args=extra_scp_args)
+
+  def _FormSCPFromRemote(self,
+                         remote_source,
+                         dest,
+                         extra_scp_args=None,
+                         user='root'):
+    return self._FormSCPCommandLine('%s@%s:%s' % (user, self._hostname,
+                                                  remote_source),
+                                    dest,
+                                    extra_scp_args=extra_scp_args)
+
+  def _RemoveSSHWarnings(self, toClean):
+    """Removes specific ssh warning lines from a string.
+
+    Args:
+      toClean: A string that may be containing multiple lines.
+
+    Returns:
+      A copy of toClean with all the Warning lines removed.
+    """
+    # Remove the Warning about connecting to a new host for the first time.
+    return re.sub(
+        r'Warning: Permanently added [^\n]* to the list of known hosts.\s\n',
+        '', toClean)
+
+  def RunCmdOnDevice(self, args, cwd=None, quiet=False):
+    stdout, stderr = GetAllCmdOutput(
+        self.FormSSHCommandLine(args),
+        cwd,
+        quiet=quiet)
+    # The initial login will add the host to the hosts file but will also print
+    # a warning to stderr that we need to remove.
+    stderr = self._RemoveSSHWarnings(stderr)
+    return stdout, stderr
+
+  def TryLogin(self):
+    logging.debug('TryLogin()')
+    assert not self.local
+    stdout, stderr = self.RunCmdOnDevice(['echo', '$USER'], quiet=True)
+    if stderr != '':
+      if 'Host key verification failed' in stderr:
+        raise LoginException(('%s host key verification failed. ' +
+                              'SSH to it manually to fix connectivity.') %
+                             self._hostname)
+      if 'Operation timed out' in stderr:
+        raise LoginException('Timed out while logging into %s' % self._hostname)
+      if 'UNPROTECTED PRIVATE KEY FILE!' in stderr:
+        raise LoginException('Permissions for %s are too open. To fix this,\n'
+                             'chmod 600 %s' % (self._ssh_identity,
+                                               self._ssh_identity))
+      if 'Permission denied (publickey,keyboard-interactive)' in stderr:
+        raise KeylessLoginRequiredException('Need to set up ssh auth for %s' %
+                                            self._hostname)
+      if 'Could not resolve hostname' in stderr:
+        raise DNSFailureException('Unable to resolve the hostname for: %s' %
+                                  self._hostname)
+      raise LoginException('While logging into %s, got %s' % (self._hostname,
+                                                              stderr))
+    if stdout != 'root\n':
+      raise LoginException('Logged into %s, expected $USER=root, but got %s.' %
+                           (self._hostname, stdout))
+
+  def FileExistsOnDevice(self, file_name):
+    if self.local:
+      return os.path.exists(file_name)
+
+    stdout, stderr = self.RunCmdOnDevice(
+        [
+            'if', 'test', '-e', file_name, ';', 'then', 'echo', '1', ';', 'fi'
+        ],
+        quiet=True)
+    if stderr != '':
+      if "Connection timed out" in stderr:
+        raise OSError('Machine wasn\'t responding to ssh: %s' % stderr)
+      raise OSError('Unexpected error: %s' % stderr)
+    exists = stdout == '1\n'
+    logging.debug("FileExistsOnDevice(<text>, %s)->%s" % (file_name, exists))
+    return exists
+
+  def PushFile(self, filename, remote_filename):
+    if self.local:
+      args = ['cp', '-r', filename, remote_filename]
+      stdout, stderr = GetAllCmdOutput(args, quiet=True)
+      if stderr != '':
+        raise OSError('No such file or directory %s' % stderr)
+      return
+
+    args = self._FormSCPToRemote(
+        os.path.abspath(filename),
+        remote_filename,
+        extra_scp_args=['-r'])
+
+    stdout, stderr = GetAllCmdOutput(args, quiet=True)
+    stderr = self._RemoveSSHWarnings(stderr)
+    if stderr != '':
+      raise OSError('No such file or directory %s' % stderr)
+
+  def PushContents(self, text, remote_filename):
+    logging.debug("PushContents(<text>, %s)" % remote_filename)
+    with tempfile.NamedTemporaryFile() as f:
+      f.write(text)
+      f.flush()
+      self.PushFile(f.name, remote_filename)
+
+  def GetFile(self, filename, destfile=None):
+    """Copies a local file |filename| to |destfile| on the device.
+
+    Args:
+      filename: The name of the local source file.
+      destfile: The name of the file to copy to, and if it is not specified
+        then it is the basename of the source file.
+
+    """
+    logging.debug("GetFile(%s, %s)" % (filename, destfile))
+    if self.local:
+      if destfile is not None and destfile != filename:
+        shutil.copyfile(filename, destfile)
+        return
+      else:
+        raise OSError('No such file or directory %s' % filename)
+
+    if destfile is None:
+      destfile = os.path.basename(filename)
+    args = self._FormSCPFromRemote(filename, os.path.abspath(destfile))
+
+    stdout, stderr = GetAllCmdOutput(args, quiet=True)
+    stderr = self._RemoveSSHWarnings(stderr)
+    if stderr != '':
+      raise OSError('No such file or directory %s' % stderr)
+
+  def GetFileContents(self, filename):
+    """Get the contents of a file on the device.
+
+    Args:
+      filename: The name of the file on the device.
+
+    Returns:
+      A string containing the contents of the file.
+    """
+    with tempfile.NamedTemporaryFile() as t:
+      self.GetFile(filename, t.name)
+      with open(t.name, 'r') as f2:
+        res = f2.read()
+        logging.debug("GetFileContents(%s)->%s" % (filename, res))
+        return res
+
+  def HasSystemd(self):
+    """Return True or False to indicate if systemd is used.
+
+    Note: This function checks to see if the 'systemctl' utilitary
+    is installed. This is only installed along with the systemd daemon.
+    """
+    _, stderr = self.RunCmdOnDevice(['systemctl'], quiet=True)
+    return stderr == ''
+
+  def ListProcesses(self):
+    """Returns (pid, cmd, ppid, state) of all processes on the device."""
+    stdout, stderr = self.RunCmdOnDevice(
+        [
+            '/bin/ps', '--no-headers', '-A', '-o', 'pid,ppid,args:4096,state'
+        ],
+        quiet=True)
+    assert stderr == '', stderr
+    procs = []
+    for l in stdout.split('\n'):
+      if l == '':
+        continue
+      m = re.match(r'^\s*(\d+)\s+(\d+)\s+(.+)\s+(.+)', l, re.DOTALL)
+      assert m
+      procs.append((int(m.group(1)), m.group(3).rstrip(), int(m.group(2)),
+                    m.group(4)))
+    logging.debug("ListProcesses(<predicate>)->[%i processes]" % len(procs))
+    return procs
+
+  def _GetSessionManagerPid(self, procs):
+    """Returns the pid of the session_manager process, given the list of
+    processes."""
+    for pid, process, _, _ in procs:
+      argv = process.split()
+      if argv and os.path.basename(argv[0]) == 'session_manager':
+        return pid
+    return None
+
+  def GetChromeProcess(self):
+    """Locates the the main chrome browser process.
+
+    Chrome on cros is usually in /opt/google/chrome, but could be in
+    /usr/local/ for developer workflows - debug chrome is too large to fit on
+    rootfs.
+
+    Chrome spawns multiple processes for renderers. pids wrap around after they
+    are exhausted so looking for the smallest pid is not always correct. We
+    locate the session_manager's pid, and look for the chrome process that's an
+    immediate child. This is the main browser process.
+    """
+    procs = self.ListProcesses()
+    session_manager_pid = self._GetSessionManagerPid(procs)
+    if not session_manager_pid:
+      return None
+
+    # Find the chrome process that is the child of the session_manager.
+    for pid, process, ppid, _ in procs:
+      if ppid != session_manager_pid:
+        continue
+      for regex in _CHROME_PROCESS_REGEX:
+        path_match = re.match(regex, process)
+        if path_match is not None:
+          return {'pid': pid, 'path': path_match.group(), 'args': process}
+    return None
+
+  def GetChromePid(self):
+    """Returns pid of main chrome browser process."""
+    result = self.GetChromeProcess()
+    if result and 'pid' in result:
+      return result['pid']
+    return None
+
+  def RmRF(self, filename):
+    logging.debug("rm -rf %s" % filename)
+    self.RunCmdOnDevice(['rm', '-rf', filename], quiet=True)
+
+  def Chown(self, filename):
+    self.RunCmdOnDevice(['chown', '-R', 'chronos:chronos', filename])
+
+  def KillAllMatching(self, predicate):
+    kills = ['kill', '-KILL']
+    for pid, cmd, _, _ in self.ListProcesses():
+      if predicate(cmd):
+        logging.info('Killing %s, pid %d' % cmd, pid)
+        kills.append(pid)
+    logging.debug("KillAllMatching(<predicate>)->%i" % (len(kills) - 2))
+    if len(kills) > 2:
+      self.RunCmdOnDevice(kills, quiet=True)
+    return len(kills) - 2
+
+  def IsServiceRunning(self, service_name):
+    """Check with the init daemon if the given service is running."""
+    if self.HasSystemd():
+      # Querying for the pid of the service will return 'MainPID=0' if
+      # the service is not running.
+      stdout, stderr = self.RunCmdOnDevice(
+          ['systemctl', 'show', '-p', 'MainPID', service_name], quiet=True)
+      running = int(stdout.split('=')[1]) != 0
+    else:
+      stdout, stderr = self.RunCmdOnDevice(['status', service_name], quiet=True)
+      running = 'running, process' in stdout
+    assert stderr == '', stderr
+    logging.debug("IsServiceRunning(%s)->%s" % (service_name, running))
+    return running
+
+  def GetRemotePort(self):
+    netstat = self.RunCmdOnDevice(['netstat', '-ant'])
+    netstat = netstat[0].split('\n')
+    ports_in_use = []
+
+    for line in netstat[2:]:
+      if not line:
+        continue
+      address_in_use = line.split()[3]
+      port_in_use = address_in_use.split(':')[-1]
+      ports_in_use.append(int(port_in_use))
+
+    ports_in_use.extend(self._reserved_ports)
+
+    new_port = sorted(ports_in_use)[-1] + 1
+    self._reserved_ports.append(new_port)
+
+    return new_port
+
+  def IsHTTPServerRunningOnPort(self, port):
+    wget_output = self.RunCmdOnDevice(['wget', 'localhost:%i' % (port), '-T1',
+                                       '-t1'])
+
+    if 'Connection refused' in wget_output[1]:
+      return False
+
+    return True
+
+  def _GetMountSourceAndTarget(self, path):
+    df_out, _ = self.RunCmdOnDevice(['/bin/df', '--output=source,target', path])
+    df_ary = df_out.split('\n')
+    # 3 lines for title, mount info, and empty line.
+    if len(df_ary) == 3:
+      line_ary = df_ary[1].split()
+      return line_ary if len(line_ary) == 2 else None
+    return None
+
+  def FilesystemMountedAt(self, path):
+    """Returns the filesystem mounted at |path|"""
+    mount_info = self._GetMountSourceAndTarget(path)
+    return mount_info[0] if mount_info else None
+
+  def CryptohomePath(self, user):
+    """Returns the cryptohome mount point for |user|."""
+    stdout, stderr = self.RunCmdOnDevice(['cryptohome-path', 'user', "'%s'" %
+                                          user])
+    if stderr != '':
+      raise OSError('cryptohome-path failed: %s' % stderr)
+    return stdout.rstrip()
+
+  def IsCryptohomeMounted(self, username, is_guest):
+    """Returns True iff |user|'s cryptohome is mounted."""
+    profile_path = self.CryptohomePath(username)
+    mount_info = self._GetMountSourceAndTarget(profile_path)
+    if mount_info:
+      # Checks if the filesytem at |profile_path| is mounted on |profile_path|
+      # itself. Before mounting cryptohome, it shows an upper directory (/home).
+      is_guestfs = (mount_info[0] == 'guestfs')
+      return is_guestfs == is_guest and mount_info[1] == profile_path
+    return False
+
+  def TakeScreenshot(self, file_path):
+    stdout, stderr = self.RunCmdOnDevice(
+        ['/usr/local/autotest/bin/screenshot.py', file_path])
+    return stdout == '' and stderr == ''
+
+  def TakeScreenshotWithPrefix(self, screenshot_prefix):
+    """Takes a screenshot, useful for debugging failures."""
+    # TODO(achuith): Find a better location for screenshots. Cros autotests
+    # upload everything in /var/log so use /var/log/screenshots for now.
+    SCREENSHOT_DIR = '/var/log/screenshots/'
+    SCREENSHOT_EXT = '.png'
+
+    self.RunCmdOnDevice(['mkdir', '-p', SCREENSHOT_DIR])
+    # Large number of screenshots can increase hardware lab bandwidth
+    # dramatically, so keep this number low. crbug.com/524814.
+    for i in xrange(2):
+      screenshot_file = ('%s%s-%d%s' %
+                         (SCREENSHOT_DIR, screenshot_prefix, i, SCREENSHOT_EXT))
+      if not self.FileExistsOnDevice(screenshot_file):
+        return self.TakeScreenshot(screenshot_file)
+    logging.warning('screenshot directory full.')
+    return False
+
+  def GetArchName(self):
+    return self.RunCmdOnDevice(['uname', '-m'])[0]
+
+  def IsRunningOnVM(self):
+    return self.RunCmdOnDevice(['crossystem', 'inside_vm'])[0] != '0'
+
+  def LsbReleaseValue(self, key, default):
+    """/etc/lsb-release is a file with key=value pairs."""
+    lines = self.GetFileContents('/etc/lsb-release').split('\n')
+    for l in lines:
+      m = re.match(r'([^=]*)=(.*)', l)
+      if m and m.group(1) == key:
+        return m.group(2)
+    return default
+
+  def GetDeviceTypeName(self):
+    """DEVICETYPE in /etc/lsb-release is CHROMEBOOK, CHROMEBIT, etc."""
+    return self.LsbReleaseValue(key='DEVICETYPE', default='CHROMEBOOK')
+
+  def RestartUI(self, clear_enterprise_policy):
+    logging.info('(Re)starting the ui (logs the user out)')
+    start_cmd = ['start', 'ui']
+    restart_cmd = ['restart', 'ui']
+    stop_cmd = ['stop', 'ui']
+    if self.HasSystemd():
+      start_cmd.insert(0, 'systemctl')
+      restart_cmd.insert(0, 'systemctl')
+      stop_cmd.insert(0, 'systemctl')
+    if clear_enterprise_policy:
+      self.RunCmdOnDevice(stop_cmd)
+      self.RmRF('/var/lib/whitelist/*')
+      self.RmRF(r'/home/chronos/Local\ State')
+
+    if self.IsServiceRunning('ui'):
+      self.RunCmdOnDevice(restart_cmd)
+    else:
+      self.RunCmdOnDevice(start_cmd)
+
+  def CloseConnection(self):
+    if not self.local:
+      with open(os.devnull, 'w') as devnull:
+        subprocess.call(
+            self.FormSSHCommandLine(['-O', 'exit', self._hostname]),
+            stdout=devnull,
+            stderr=devnull)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/cros_interface_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/cros_interface_unittest.py
new file mode 100644
index 0000000..268bd52
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/cros_interface_unittest.py
@@ -0,0 +1,253 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# TODO(nduca): Rewrite what some of these tests to use mocks instead of
+# actually talking to the device. This would improve our coverage quite
+# a bit.
+
+import socket
+import tempfile
+import unittest
+import mock
+
+from telemetry.core import cros_interface
+from telemetry import decorators
+from telemetry.internal import forwarders
+from telemetry.internal.forwarders import cros_forwarder
+from telemetry.testing import options_for_unittests
+
+
+class CrOSInterfaceTest(unittest.TestCase):
+
+  def _GetCRI(self):
+    remote = options_for_unittests.GetCopy().cros_remote
+    remote_ssh_port = options_for_unittests.GetCopy().cros_remote_ssh_port
+    return cros_interface.CrOSInterface(
+        remote, remote_ssh_port,
+        options_for_unittests.GetCopy().cros_ssh_identity)
+
+  @decorators.Enabled('chromeos')
+  def testPushContents(self):
+    with self._GetCRI() as cri:
+      tmp_file = '/tmp/testPushContents'
+      test_contents = 'hello world'
+      cri.RmRF(tmp_file)
+      cri.PushContents(test_contents, tmp_file)
+      contents = cri.GetFileContents(tmp_file)
+      self.assertEquals(contents, test_contents)
+
+  @decorators.Enabled('chromeos')
+  def testExists(self):
+    with self._GetCRI() as cri:
+      self.assertTrue(cri.FileExistsOnDevice('/proc/cpuinfo'))
+      self.assertTrue(cri.FileExistsOnDevice('/etc/passwd'))
+      self.assertFalse(cri.FileExistsOnDevice('/etc/sdlfsdjflskfjsflj'))
+
+  @decorators.Enabled('chromeos')
+  def testGetFileContents(self):
+    with self._GetCRI() as cri:
+      hosts = cri.GetFileContents('/etc/lsb-release')
+      self.assertTrue('CHROMEOS' in hosts)
+
+  @decorators.Enabled('chromeos')
+  def testGetFile(self):  # pylint: disable=no-self-use
+    with self._GetCRI() as cri:
+      f = tempfile.NamedTemporaryFile()
+      cri.GetFile('/etc/lsb-release', f.name)
+      with open(f.name, 'r') as f2:
+        res = f2.read()
+        self.assertTrue('CHROMEOS' in res)
+
+  @decorators.Enabled('chromeos')
+  def testGetFileNonExistent(self):
+    with self._GetCRI() as cri:
+      f = tempfile.NamedTemporaryFile()
+      cri.PushContents('testGetFileNonExistent', f.name)
+      cri.RmRF(f.name)
+      self.assertRaises(OSError, lambda: cri.GetFile(f.name))
+
+  @decorators.Enabled('chromeos')
+  def testIsServiceRunning(self):
+    with self._GetCRI() as cri:
+      self.assertTrue(cri.IsServiceRunning('openssh-server'))
+
+  # TODO(achuith): Fix this test. crbug.com/619767.
+  @decorators.Disabled('all')
+  def testGetRemotePortAndIsHTTPServerRunningOnPort(self):
+    with self._GetCRI() as cri:
+      # Create local server.
+      sock = socket.socket()
+      sock.bind(('', 0))
+      port = sock.getsockname()[1]
+      sock.listen(0)
+
+      # Get remote port and ensure that it was unused.
+      remote_port = cri.GetRemotePort()
+      self.assertFalse(cri.IsHTTPServerRunningOnPort(remote_port))
+
+      # Forward local server's port to remote device's remote_port.
+      forwarder = cros_forwarder.CrOsForwarderFactory(cri).Create(
+          forwarders.PortPairs(http=forwarders.PortPair(port, remote_port),
+                               https=None,
+                               dns=None))
+
+      # At this point, remote device should be able to connect to local server.
+      self.assertTrue(cri.IsHTTPServerRunningOnPort(remote_port))
+
+      # Next remote port shouldn't be the same as remote_port, since remote_port
+      # is now in use.
+      self.assertTrue(cri.GetRemotePort() != remote_port)
+
+      # Close forwarder and local server ports.
+      forwarder.Close()
+      sock.close()
+
+      # Device should no longer be able to connect to remote_port since it is no
+      # longer in use.
+      self.assertFalse(cri.IsHTTPServerRunningOnPort(remote_port))
+
+  @decorators.Enabled('chromeos')
+  def testGetRemotePortReservedPorts(self):
+    with self._GetCRI() as cri:
+      # Should return 2 separate ports even though the first one isn't
+      # technically being used yet.
+      remote_port_1 = cri.GetRemotePort()
+      remote_port_2 = cri.GetRemotePort()
+
+      self.assertTrue(remote_port_1 != remote_port_2)
+
+  # TODO(achuith): Doesn't work in VMs.
+  @decorators.Disabled('all')
+  def testTakeScreenshotWithPrefix(self):
+    with self._GetCRI() as cri:
+      def _Cleanup():
+        cri.RmRF('/var/log/screenshots/test-prefix*')
+
+      _Cleanup()
+      self.assertTrue(cri.TakeScreenshotWithPrefix('test-prefix'))
+      self.assertTrue(cri.FileExistsOnDevice(
+          '/var/log/screenshots/test-prefix-0.png'))
+      _Cleanup()
+
+  @decorators.Enabled('chromeos')
+  def testLsbReleaseValue(self):
+    with self._GetCRI() as cri:
+      build_num = cri.LsbReleaseValue('CHROMEOS_RELEASE_BUILD_NUMBER', None)
+      self.assertTrue(build_num.isdigit())
+      device_type = cri.GetDeviceTypeName()
+      self.assertTrue(device_type.isalpha())
+
+  @decorators.Enabled('chromeos')
+  def testEscapeCmdArguments(self):
+    """Commands and their arguments that are executed through the cros
+    interface should follow bash syntax. This test needs to run on remotely
+    and locally on the device to check for consistency.
+    """
+    options = options_for_unittests.GetCopy()
+    with cros_interface.CrOSInterface(options.cros_remote,
+                                      options.cros_remote_ssh_port,
+                                      options.cros_ssh_identity) as cri:
+
+      # Check arguments with no special characters
+      stdout, _ = cri.RunCmdOnDevice(['echo', '--arg1=value1', '--arg2=value2',
+                                      '--arg3="value3"'])
+      assert stdout.strip() == '--arg1=value1 --arg2=value2 --arg3=value3'
+
+      # Check argument with special characters escaped
+      stdout, _ = cri.RunCmdOnDevice(['echo', '--arg=A\\; echo \\"B\\"'])
+      assert stdout.strip() == '--arg=A; echo "B"'
+
+      # Check argument with special characters in quotes
+      stdout, _ = cri.RunCmdOnDevice(['echo', "--arg='$HOME;;$PATH'"])
+      assert stdout.strip() == "--arg=$HOME;;$PATH"
+
+  @decorators.Enabled('chromeos')
+  @mock.patch.object(cros_interface.CrOSInterface, 'RunCmdOnDevice')
+  def testTryLoginSuccess(self, mock_run_cmd):
+    mock_run_cmd.return_value = ('root\n', '')
+    cri = cros_interface.CrOSInterface(
+        "testhostname", 22, options_for_unittests.GetCopy().cros_ssh_identity)
+    cri.TryLogin()
+    mock_run_cmd.assert_called_once_with(['echo', '$USER'], quiet=True)
+
+  @decorators.Enabled('chromeos')
+  @mock.patch.object(cros_interface.CrOSInterface, 'RunCmdOnDevice')
+  def testTryLoginStderr(self, mock_run_cmd):
+    cri = cros_interface.CrOSInterface(
+        "testhostname", 22, options_for_unittests.GetCopy().cros_ssh_identity)
+
+    mock_run_cmd.return_value = ('', 'Host key verification failed')
+    self.assertRaises(cros_interface.LoginException, cri.TryLogin)
+    self.assertRaisesRegexp(cros_interface.LoginException,
+                            r'.*host key verification failed..*', cri.TryLogin)
+
+    mock_run_cmd.return_value = ('', 'Operation timed out')
+    self.assertRaisesRegexp(cros_interface.LoginException,
+                            r'Timed out while logging into.*', cri.TryLogin)
+
+    mock_run_cmd.return_value = ('', 'UNPROTECTED PRIVATE KEY FILE!')
+    self.assertRaisesRegexp(cros_interface.LoginException,
+                            r'Permissions for .* are too open. To fix this.*',
+                            cri.TryLogin)
+
+    mock_run_cmd.return_value = (
+        '', 'Permission denied (publickey,keyboard-interactive)')
+    self.assertRaisesRegexp(cros_interface.KeylessLoginRequiredException,
+                            r'Need to set up ssh auth for .*', cri.TryLogin)
+
+    mock_run_cmd.return_value = ('', 'Fallback error case')
+    self.assertRaisesRegexp(cros_interface.LoginException,
+                            r'While logging into .*, got .*', cri.TryLogin)
+
+    mock_run_cmd.return_value = ('', 'Could not resolve hostname')
+    self.assertRaisesRegexp(cros_interface.DNSFailureException,
+                            r'Unable to resolve the hostname for:.*',
+                            cri.TryLogin)
+
+  @decorators.Enabled('chromeos')
+  @mock.patch.object(cros_interface.CrOSInterface, 'RunCmdOnDevice')
+  def testTryLoginStdout(self, mock_run_cmd):
+    mock_run_cmd.return_value = ('notrooot', '')
+    cri = cros_interface.CrOSInterface(
+        "testhostname", 22, options_for_unittests.GetCopy().cros_ssh_identity)
+    self.assertRaisesRegexp(cros_interface.LoginException,
+                            r'Logged into .*, expected \$USER=root, but got .*',
+                            cri.TryLogin)
+
+  @decorators.Enabled('chromeos')
+  @mock.patch.object(cros_interface.CrOSInterface, 'RunCmdOnDevice')
+  def testIsCryptohomeMounted(self, mock_run_cmd):
+    # The device's mount state is checked by the command
+    #   /bin/df --someoption `cryptohome-path user $username`.
+    # The following mock replaces RunCmdOnDevice() to return mocked mount states
+    # from the command execution.
+    def mockRunCmdOnDevice(args):
+      if args[0] == 'cryptohome-path':
+        return ('/home/user/%s' % args[2], '')
+      elif args[0] == '/bin/df':
+        if 'unmount' in args[2]:
+          # For the user unmount@gmail.com, returns the unmounted state.
+          source, target = '/dev/sda1', '/home'
+        elif 'mount' in args[2]:
+          # For the user mount@gmail.com, returns the mounted state.
+          source, target = '/dev/sda1', args[2]
+        elif 'guest' in args[2]:
+          # For the user $guest, returns the guest-mounted state.
+          source, target = 'guestfs', args[2]
+        return ('Filesystem Mounted on\n%s %s\n' % (source, target), '')
+    mock_run_cmd.side_effect = mockRunCmdOnDevice
+
+    cri = cros_interface.CrOSInterface(
+        "testhostname", 22, options_for_unittests.GetCopy().cros_ssh_identity)
+    # Returns False if the user's cryptohome is not mounted.
+    self.assertFalse(cri.IsCryptohomeMounted('unmount@gmail.com', False))
+    # Returns True if the user's cryptohome is mounted.
+    self.assertTrue(cri.IsCryptohomeMounted('mount@gmail.com', False))
+    # Returns True if the guest cryptohome is mounted.
+    self.assertTrue(cri.IsCryptohomeMounted('$guest', True))
+    # Sanity check. Returns False if the |is_guest| parameter does not match
+    # with whether or not the user is really a guest.
+    self.assertFalse(cri.IsCryptohomeMounted('unmount@gmail.com', True))
+    self.assertFalse(cri.IsCryptohomeMounted('mount@gmail.com', True))
+    self.assertFalse(cri.IsCryptohomeMounted('$guest', False))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/discover.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/discover.py
new file mode 100644
index 0000000..7eb667c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/discover.py
@@ -0,0 +1,175 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import fnmatch
+import inspect
+import os
+import re
+import sys
+
+from telemetry.internal.util import camel_case
+from telemetry.internal.util import classes as classes_module
+
+
+def DiscoverModules(start_dir, top_level_dir, pattern='*'):
+  """Discover all modules in |start_dir| which match |pattern|.
+
+  Args:
+    start_dir: The directory to recursively search.
+    top_level_dir: The top level of the package, for importing.
+    pattern: Unix shell-style pattern for filtering the filenames to import.
+
+  Returns:
+    list of modules.
+  """
+  # start_dir and top_level_dir must be consistent with each other.
+  start_dir = os.path.realpath(start_dir)
+  top_level_dir = os.path.realpath(top_level_dir)
+
+  modules = []
+  sub_paths = list(os.walk(start_dir))
+  # We sort the directories & file paths to ensure a deterministic ordering when
+  # traversing |top_level_dir|.
+  sub_paths.sort(key=lambda paths_tuple: paths_tuple[0])
+  for dir_path, _, filenames in sub_paths:
+    # Sort the directories to walk recursively by the directory path.
+    filenames.sort()
+    for filename in filenames:
+      # Filter out unwanted filenames.
+      if filename.startswith('.') or filename.startswith('_'):
+        continue
+      if os.path.splitext(filename)[1] != '.py':
+        continue
+      if not fnmatch.fnmatch(filename, pattern):
+        continue
+
+      # Find the module.
+      module_rel_path = os.path.relpath(
+          os.path.join(dir_path, filename), top_level_dir)
+      module_name = re.sub(r'[/\\]', '.', os.path.splitext(module_rel_path)[0])
+
+      # Import the module.
+      try:
+        # Make sure that top_level_dir is the first path in the sys.path in case
+        # there are naming conflict in module parts.
+        original_sys_path = sys.path[:]
+        sys.path.insert(0, top_level_dir)
+        module = __import__(module_name, fromlist=[True])
+        modules.append(module)
+      finally:
+        sys.path = original_sys_path
+  return modules
+
+
+def AssertNoKeyConflicts(classes_by_key_1, classes_by_key_2):
+  for k in classes_by_key_1:
+    if k in classes_by_key_2:
+      assert classes_by_key_1[k] is classes_by_key_2[k], (
+          'Found conflicting classes for the same key: '
+          'key=%s, class_1=%s, class_2=%s' % (
+              k, classes_by_key_1[k], classes_by_key_2[k]))
+
+
+# TODO(dtu): Normalize all discoverable classes to have corresponding module
+# and class names, then always index by class name.
+def DiscoverClasses(start_dir,
+                    top_level_dir,
+                    base_class,
+                    pattern='*',
+                    index_by_class_name=True,
+                    directly_constructable=False):
+  """Discover all classes in |start_dir| which subclass |base_class|.
+
+  Base classes that contain subclasses are ignored by default.
+
+  Args:
+    start_dir: The directory to recursively search.
+    top_level_dir: The top level of the package, for importing.
+    base_class: The base class to search for.
+    pattern: Unix shell-style pattern for filtering the filenames to import.
+    index_by_class_name: If True, use class name converted to
+        lowercase_with_underscores instead of module name in return dict keys.
+    directly_constructable: If True, will only return classes that can be
+        constructed without arguments
+
+  Returns:
+    dict of {module_name: class} or {underscored_class_name: class}
+  """
+  modules = DiscoverModules(start_dir, top_level_dir, pattern)
+  classes = {}
+  for module in modules:
+    new_classes = DiscoverClassesInModule(
+        module, base_class, index_by_class_name, directly_constructable)
+    # TODO(nednguyen): we should remove index_by_class_name once
+    # benchmark_smoke_unittest in chromium/src/tools/perf no longer relied
+    # naming collisions to reduce the number of smoked benchmark tests.
+    # crbug.com/548652
+    if index_by_class_name:
+      AssertNoKeyConflicts(classes, new_classes)
+    classes = dict(classes.items() + new_classes.items())
+  return classes
+
+
+# TODO(nednguyen): we should remove index_by_class_name once
+# benchmark_smoke_unittest in chromium/src/tools/perf no longer relied
+# naming collisions to reduce the number of smoked benchmark tests.
+# crbug.com/548652
+def DiscoverClassesInModule(module,
+                            base_class,
+                            index_by_class_name=False,
+                            directly_constructable=False):
+  """Discover all classes in |module| which subclass |base_class|.
+
+  Base classes that contain subclasses are ignored by default.
+
+  Args:
+    module: The module to search.
+    base_class: The base class to search for.
+    index_by_class_name: If True, use class name converted to
+        lowercase_with_underscores instead of module name in return dict keys.
+
+  Returns:
+    dict of {module_name: class} or {underscored_class_name: class}
+  """
+  classes = {}
+  for _, obj in inspect.getmembers(module):
+    # Ensure object is a class.
+    if not inspect.isclass(obj):
+      continue
+    # Include only subclasses of base_class.
+    if not issubclass(obj, base_class):
+      continue
+    # Exclude the base_class itself.
+    if obj is base_class:
+      continue
+    # Exclude protected or private classes.
+    if obj.__name__.startswith('_'):
+      continue
+    # Include only the module in which the class is defined.
+    # If a class is imported by another module, exclude those duplicates.
+    if obj.__module__ != module.__name__:
+      continue
+
+    if index_by_class_name:
+      key_name = camel_case.ToUnderscore(obj.__name__)
+    else:
+      key_name = module.__name__.split('.')[-1]
+    if (not directly_constructable or
+        classes_module.IsDirectlyConstructable(obj)):
+      if key_name in classes and index_by_class_name:
+        assert classes[key_name] is obj, (
+          'Duplicate key_name with different objs detected: '
+          'key=%s, obj1=%s, obj2=%s' % (key_name, classes[key_name], obj))
+      else:
+        classes[key_name] = obj
+
+  return classes
+
+
+_counter = [0]
+
+
+def _GetUniqueModuleName():
+  _counter[0] += 1
+  return "module_" + str(_counter[0])
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/discover_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/discover_unittest.py
new file mode 100644
index 0000000..bb6785c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/discover_unittest.py
@@ -0,0 +1,109 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import os
+import unittest
+
+from telemetry.core import discover
+from telemetry.core import util
+
+
+class DiscoverTest(unittest.TestCase):
+
+  def setUp(self):
+    self._base_dir = util.GetUnittestDataDir()
+    self._start_dir = os.path.join(self._base_dir, 'discoverable_classes')
+    self._base_class = Exception
+
+  def testDiscoverClassesWithIndexByModuleName(self):
+    classes = discover.DiscoverClasses(self._start_dir,
+                                       self._base_dir,
+                                       self._base_class,
+                                       index_by_class_name=False)
+
+    actual_classes = dict((name, cls.__name__)
+                          for name, cls in classes.iteritems())
+    expected_classes = {
+        'another_discover_dummyclass': 'DummyExceptionWithParameterImpl1',
+        'discover_dummyclass': 'DummyException',
+        'parameter_discover_dummyclass': 'DummyExceptionWithParameterImpl2'
+    }
+    self.assertEqual(actual_classes, expected_classes)
+
+  def testDiscoverDirectlyConstructableClassesWithIndexByClassName(self):
+    classes = discover.DiscoverClasses(self._start_dir,
+                                       self._base_dir,
+                                       self._base_class,
+                                       directly_constructable=True)
+
+    actual_classes = dict((name, cls.__name__)
+                          for name, cls in classes.iteritems())
+    expected_classes = {
+        'dummy_exception': 'DummyException',
+        'dummy_exception_impl1': 'DummyExceptionImpl1',
+        'dummy_exception_impl2': 'DummyExceptionImpl2',
+    }
+    self.assertEqual(actual_classes, expected_classes)
+
+  def testDiscoverClassesWithIndexByClassName(self):
+    classes = discover.DiscoverClasses(self._start_dir, self._base_dir,
+                                       self._base_class)
+
+    actual_classes = dict((name, cls.__name__)
+                          for name, cls in classes.iteritems())
+    expected_classes = {
+        'dummy_exception': 'DummyException',
+        'dummy_exception_impl1': 'DummyExceptionImpl1',
+        'dummy_exception_impl2': 'DummyExceptionImpl2',
+        'dummy_exception_with_parameter_impl1':
+            'DummyExceptionWithParameterImpl1',
+        'dummy_exception_with_parameter_impl2':
+            'DummyExceptionWithParameterImpl2'
+    }
+    self.assertEqual(actual_classes, expected_classes)
+
+  def testDiscoverClassesWithPatternAndIndexByModule(self):
+    classes = discover.DiscoverClasses(self._start_dir,
+                                       self._base_dir,
+                                       self._base_class,
+                                       pattern='another*',
+                                       index_by_class_name=False)
+
+    actual_classes = dict((name, cls.__name__)
+                          for name, cls in classes.iteritems())
+    expected_classes = {
+        'another_discover_dummyclass': 'DummyExceptionWithParameterImpl1'
+    }
+    self.assertEqual(actual_classes, expected_classes)
+
+  def testDiscoverDirectlyConstructableClassesWithPatternAndIndexByClassName(
+      self):
+    classes = discover.DiscoverClasses(self._start_dir,
+                                       self._base_dir,
+                                       self._base_class,
+                                       pattern='another*',
+                                       directly_constructable=True)
+
+    actual_classes = dict((name, cls.__name__)
+                          for name, cls in classes.iteritems())
+    expected_classes = {
+        'dummy_exception_impl1': 'DummyExceptionImpl1',
+        'dummy_exception_impl2': 'DummyExceptionImpl2',
+    }
+    self.assertEqual(actual_classes, expected_classes)
+
+  def testDiscoverClassesWithPatternAndIndexByClassName(self):
+    classes = discover.DiscoverClasses(self._start_dir,
+                                       self._base_dir,
+                                       self._base_class,
+                                       pattern='another*')
+
+    actual_classes = dict((name, cls.__name__)
+                          for name, cls in classes.iteritems())
+    expected_classes = {
+        'dummy_exception_impl1': 'DummyExceptionImpl1',
+        'dummy_exception_impl2': 'DummyExceptionImpl2',
+        'dummy_exception_with_parameter_impl1':
+            'DummyExceptionWithParameterImpl1',
+    }
+    self.assertEqual(actual_classes, expected_classes)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/exceptions.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/exceptions.py
new file mode 100644
index 0000000..1038256
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/exceptions.py
@@ -0,0 +1,183 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import logging
+import sys
+
+
+class Error(Exception):
+  """Base class for Telemetry exceptions."""
+
+  def __init__(self, msg=''):
+    super(Error, self).__init__(msg)
+    self._debugging_messages = []
+
+  def AddDebuggingMessage(self, msg):
+    """Adds a message to the description of the exception.
+
+    Many Telemetry exceptions arise from failures in another application. These
+    failures are difficult to pinpoint. This method allows Telemetry classes to
+    append useful debugging information to the exception. This method also logs
+    information about the location from where it was called.
+    """
+    frame = sys._getframe(1)
+    line_number = frame.f_lineno
+    file_name = frame.f_code.co_filename
+    function_name = frame.f_code.co_name
+    call_site = '%s:%s %s' % (file_name, line_number, function_name)
+    annotated_message = '(%s) %s' % (call_site, msg)
+
+    self._debugging_messages.append(annotated_message)
+
+  def __str__(self):
+    divider = '\n' + '*' * 80 + '\n'
+    output = super(Error, self).__str__()
+    for message in self._debugging_messages:
+      output += divider
+      output += message
+    return output
+
+
+class PlatformError(Error):
+  """ Represents an exception thrown when constructing platform. """
+
+
+class TimeoutException(Error):
+  """The operation failed to complete because of a timeout.
+
+  It is possible that waiting for a longer period of time would result in a
+  successful operation.
+  """
+  pass
+
+
+class AppCrashException(Error):
+
+  def __init__(self, app=None, msg=''):
+    super(AppCrashException, self).__init__(msg)
+    self._msg = msg
+    self._is_valid_dump = False
+    self._stack_trace = []
+    self._app_stdout = []
+    self._minidump_path = ''
+    self._system_log = '(Not implemented)'
+    if app:
+      try:
+        system_log = app.platform.GetSystemLog()
+        if system_log:
+          self._system_log = system_log
+        self._is_valid_dump, trace_output = app.GetStackTrace()
+        self._stack_trace = trace_output.splitlines()
+        self._minidump_path = app.GetMostRecentMinidumpPath()
+      except Exception as err:
+        logging.error('Problem when trying to gather stack trace: %s' % err)
+      try:
+        self._app_stdout = app.GetStandardOutput().splitlines()
+      except Exception as err:
+        logging.error('Problem when trying to gather standard output: %s' % err)
+
+  @property
+  def stack_trace(self):
+    return self._stack_trace
+
+  @property
+  def minidump_path(self):
+    return self._minidump_path
+
+  @property
+  def is_valid_dump(self):
+    return self._is_valid_dump
+
+  def __str__(self):
+    divider = '*' * 80
+    debug_messages = []
+    debug_messages.append(super(AppCrashException, self).__str__())
+    debug_messages.append('Found Minidump: %s' % self._is_valid_dump)
+    debug_messages.append('Stack Trace:')
+    debug_messages.append(divider)
+    debug_messages.extend(('\t%s' % l) for l in self._stack_trace)
+    debug_messages.append(divider)
+    debug_messages.append('Standard output:')
+    debug_messages.append(divider)
+    debug_messages.extend(('\t%s' % l) for l in self._app_stdout)
+    debug_messages.append(divider)
+    debug_messages.append('System log:')
+    debug_messages.append(self._system_log)
+    return '\n'.join(debug_messages)
+
+class DevtoolsTargetCrashException(AppCrashException):
+  """Represents a crash of the current devtools target but not the overall app.
+
+  This can be a tab or a WebView. In this state, the tab/WebView is
+  gone, but the underlying browser is still alive.
+  """
+
+  def __init__(self, app, msg='Devtools target crashed'):
+    super(DevtoolsTargetCrashException, self).__init__(app, msg)
+
+
+class BrowserGoneException(AppCrashException):
+  """Represents a crash of the entire browser.
+
+  In this state, all bets are pretty much off."""
+
+  def __init__(self, app, msg='Browser crashed'):
+    super(BrowserGoneException, self).__init__(app, msg)
+
+
+class BrowserConnectionGoneException(BrowserGoneException):
+  """Represents a browser that still exists but cannot be reached."""
+
+  def __init__(self, app, msg='Browser exists but the connection is gone'):
+    super(BrowserConnectionGoneException, self).__init__(app, msg)
+
+
+class ProcessGoneException(Error):
+  """Represents a process that no longer exists for an unknown reason."""
+
+
+class IntentionalException(Error):
+  """Represent an exception raised by a unittest which is not printed."""
+
+
+class InitializationError(Error):
+
+  def __init__(self, string):
+    super(InitializationError, self).__init__(string)
+
+
+class LoginException(Error):
+  pass
+
+
+class EvaluateException(Error):
+  def __init__(self, text='', class_name='', description=None):
+    super(EvaluateException, self).__init__(text)
+    self._class_name = class_name
+    self._description = description
+
+  def __str__(self):
+    output = super(EvaluateException, self).__str__()
+    if self._class_name and self._description:
+      output += '%s:\n%s' % (self._class_name, self._description)
+    return output
+
+
+class ProfilingException(Error):
+  pass
+
+
+class PathMissingError(Error):
+  """ Represents an exception thrown when an expected path doesn't exist. """
+
+
+class UnknownPackageError(Error):
+  """ Represents an exception when encountering an unsupported Android APK. """
+
+
+class PackageDetectionError(Error):
+  """ Represents an error when parsing an Android APK's package. """
+
+
+class AndroidDeviceParsingError(Error):
+  """Represents an error when parsing output from an android device"""
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/local_server.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/local_server.py
new file mode 100644
index 0000000..a734174
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/local_server.py
@@ -0,0 +1,218 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# TODO(aiolos): this should be moved to catapult/base after the repo move.
+# It is used by tracing in tvcm/browser_controller.
+import collections
+import json
+import os
+import re
+import subprocess
+import sys
+
+from telemetry.core import util
+
+NamedPort = collections.namedtuple('NamedPort', ['name', 'port'])
+
+
+class LocalServerBackend(object):
+
+  def __init__(self):
+    pass
+
+  def StartAndGetNamedPorts(self, args):
+    """Starts the actual server and obtains any sockets on which it
+    should listen.
+
+    Returns a list of NamedPort on which this backend is listening.
+    """
+    raise NotImplementedError()
+
+  def ServeForever(self):
+    raise NotImplementedError()
+
+
+class LocalServer(object):
+
+  def __init__(self, server_backend_class):
+    assert LocalServerBackend in server_backend_class.__bases__
+    server_module_name = server_backend_class.__module__
+    assert server_module_name in sys.modules, \
+        'The server class\' module must be findable via sys.modules'
+    assert getattr(sys.modules[server_module_name],
+                   server_backend_class.__name__), \
+        'The server class must getattrable from its __module__ by its __name__'
+
+    self._server_backend_class = server_backend_class
+    self._subprocess = None
+    self._devnull = None
+    self._local_server_controller = None
+    self.host_ip = None
+    self.port = None
+
+  def Start(self, local_server_controller):
+    assert self._subprocess == None
+    self._local_server_controller = local_server_controller
+
+    self.host_ip = local_server_controller.host_ip
+
+    server_args = self.GetBackendStartupArgs()
+    server_args_as_json = json.dumps(server_args)
+    server_module_name = self._server_backend_class.__module__
+
+    self._devnull = open(os.devnull, 'w')
+    cmd = [
+        sys.executable,
+        '-m',
+        __name__,
+        'run_backend',
+        server_module_name,
+        self._server_backend_class.__name__,
+        server_args_as_json,
+    ]
+
+    env = os.environ.copy()
+    env['PYTHONPATH'] = os.pathsep.join(sys.path)
+
+    self._subprocess = subprocess.Popen(cmd,
+                                        cwd=util.GetTelemetryDir(),
+                                        env=env,
+                                        stdout=subprocess.PIPE)
+
+    named_ports = self._GetNamedPortsFromBackend()
+    http_port = None
+    for p in named_ports:
+      if p.name == 'http':
+        http_port = p.port
+    assert http_port and len(named_ports) == 1, (
+        'Only http port is supported: %s' % named_ports)
+    self.port = http_port
+
+  def _GetNamedPortsFromBackend(self):
+    named_ports_json = None
+    named_ports_re = re.compile('LocalServerBackend started: (?P<port>.+)')
+    # TODO: This will hang if the subprocess doesn't print the correct output.
+    while self._subprocess.poll() == None:
+      m = named_ports_re.match(self._subprocess.stdout.readline())
+      if m:
+        named_ports_json = m.group('port')
+        break
+
+    if not named_ports_json:
+      raise Exception('Server process died prematurely ' +
+                      'without giving us port pairs.')
+    return [NamedPort(**pair) for pair in json.loads(named_ports_json.lower())]
+
+  @property
+  def is_running(self):
+    return self._subprocess != None
+
+  def __enter__(self):
+    return self
+
+  def __exit__(self, *args):
+    self.Close()
+
+  def __del__(self):
+    self.Close()
+
+  def Close(self):
+    if self._subprocess:
+      # TODO(tonyg): Should this block until it goes away?
+      self._subprocess.kill()
+      self._subprocess = None
+    if self._devnull:
+      self._devnull.close()
+      self._devnull = None
+    if self._local_server_controller:
+      self._local_server_controller.ServerDidClose(self)
+      self._local_server_controller = None
+
+  def GetBackendStartupArgs(self):
+    """Returns whatever arguments are required to start up the backend"""
+    raise NotImplementedError()
+
+
+class LocalServerController(object):
+  """Manages the list of running servers
+
+  This class manages the running servers, but also provides an isolation layer
+  to prevent LocalServer subclasses from accessing the browser backend directly.
+
+  """
+
+  def __init__(self, platform_backend):
+    self._platform_backend = platform_backend
+    self._local_servers_by_class = {}
+    self.host_ip = self._platform_backend.forwarder_factory.host_ip
+
+  def StartServer(self, server):
+    assert not server.is_running, 'Server already started'
+    assert self._platform_backend.network_controller_backend.is_initialized
+    assert isinstance(server, LocalServer)
+    if server.__class__ in self._local_servers_by_class:
+      raise Exception(
+          'Cannot have two servers of the same class running at once. ' +
+          'Locate the existing one and use it, or call Close() on it.')
+
+    server.Start(self)
+    self._local_servers_by_class[server.__class__] = server
+
+  def GetRunningServer(self, server_class, default_value):
+    return self._local_servers_by_class.get(server_class, default_value)
+
+  @property
+  def local_servers(self):
+    return self._local_servers_by_class.values()
+
+  def Close(self):
+    while len(self._local_servers_by_class):
+      server = self._local_servers_by_class.itervalues().next()
+      try:
+        server.Close()
+      except Exception:
+        import traceback
+        traceback.print_exc()
+
+  def GetRemotePort(self, port):
+    return self._platform_backend.GetRemotePort(port)
+
+  def ServerDidClose(self, server):
+    del self._local_servers_by_class[server.__class__]
+
+
+def _LocalServerBackendMain(args):
+  assert len(args) == 4
+  (cmd, server_module_name, server_backend_class_name,
+   server_args_as_json) = args[:4]
+  assert cmd == 'run_backend'
+  server_module = __import__(server_module_name, fromlist=[True])
+  server_backend_class = getattr(server_module, server_backend_class_name)
+  server = server_backend_class()
+
+  server_args = json.loads(server_args_as_json)
+
+  named_ports = server.StartAndGetNamedPorts(server_args)
+  assert isinstance(named_ports, list)
+  for named_port in named_ports:
+    assert isinstance(named_port, NamedPort)
+
+  # Note: This message is scraped by the parent process'
+  # _GetNamedPortsFromBackend(). Do **not** change it.
+  # pylint: disable=protected-access
+  print 'LocalServerBackend started: %s' % json.dumps([pair._asdict()
+                                                       for pair in named_ports])
+  sys.stdout.flush()
+
+  return server.ServeForever()
+
+
+if __name__ == '__main__':
+  # This trick is needed because local_server.NamedPort is not the
+  # same as sys.modules['__main__'].NamedPort. The module itself is loaded
+  # twice, basically.
+  from telemetry.core import local_server  # pylint: disable=import-self
+  sys.exit(
+      local_server._LocalServerBackendMain(  # pylint: disable=protected-access
+          sys.argv[1:]))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/local_server_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/local_server_unittest.py
new file mode 100644
index 0000000..46ff85c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/local_server_unittest.py
@@ -0,0 +1,89 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import BaseHTTPServer
+import SimpleHTTPServer
+
+from telemetry import decorators
+from telemetry.core import local_server
+from telemetry.testing import tab_test_case
+
+
+class SimpleLocalServerBackendRequestHandler(
+    SimpleHTTPServer.SimpleHTTPRequestHandler):
+
+  def do_GET(self):
+    msg = """<!DOCTYPE html>
+<html>
+<body>
+hello world
+</body>
+"""
+
+    self.send_response(200)
+    self.send_header('Content-Type', 'text/html')
+    self.send_header('Content-Length', len(msg))
+    self.end_headers()
+    self.wfile.write(msg)
+
+  def log_request(self, code='-', size='-'):
+    pass
+
+
+class SimpleLocalServerBackend(BaseHTTPServer.HTTPServer,
+                               local_server.LocalServerBackend):
+
+  def __init__(self):
+    BaseHTTPServer.HTTPServer.__init__(self, ('127.0.0.1', 0),
+                                       SimpleLocalServerBackendRequestHandler)
+    local_server.LocalServerBackend.__init__(self)
+
+  def StartAndGetNamedPorts(self, args):
+    assert 'hello' in args
+    assert args['hello'] == 'world'
+    return [local_server.NamedPort('http', self.server_address[1])]
+
+  def ServeForever(self):
+    self.serve_forever()
+
+
+class SimpleLocalServer(local_server.LocalServer):
+
+  def __init__(self):
+    super(SimpleLocalServer, self).__init__(SimpleLocalServerBackend)
+
+  def GetBackendStartupArgs(self):
+    return {'hello': 'world'}
+
+  @property
+  def url(self):
+    return self.forwarder.url + '/'
+
+
+class LocalServerUnittest(tab_test_case.TabTestCase):
+
+  @classmethod
+  def setUpClass(cls):
+    super(LocalServerUnittest, cls).setUpClass()
+    cls._server = SimpleLocalServer()
+    cls._platform.StartLocalServer(cls._server)
+
+  @decorators.Disabled('all') # https://crbug.com/570955
+  def testLocalServer(self):
+    self.assertTrue(self._server in self._platform.local_servers)
+    self._tab.Navigate(self._server.url)
+    self._tab.WaitForDocumentReadyStateToBeComplete()
+    body_text = self._tab.EvaluateJavaScript('document.body.textContent')
+    body_text = body_text.strip()
+    self.assertEquals('hello world', body_text)
+
+  @decorators.Disabled('all') # https://crbug.com/570955
+  def testStartingAndRestarting(self):
+    server2 = SimpleLocalServer()
+    self.assertRaises(Exception,
+                      lambda: self._platform.StartLocalServer(server2))
+
+    self._server.Close()
+    self.assertTrue(self._server not in self._platform.local_servers)
+
+    self._platform.StartLocalServer(server2)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/memory_cache_http_server.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/memory_cache_http_server.py
new file mode 100644
index 0000000..336e6ec
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/memory_cache_http_server.py
@@ -0,0 +1,279 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import BaseHTTPServer
+from collections import namedtuple
+import errno
+import gzip
+import mimetypes
+import os
+import SimpleHTTPServer
+import socket
+import SocketServer
+import StringIO
+import sys
+import urlparse
+
+from telemetry.core import local_server
+
+ByteRange = namedtuple('ByteRange', ['from_byte', 'to_byte'])
+ResourceAndRange = namedtuple('ResourceAndRange', ['resource', 'byte_range'])
+
+
+class MemoryCacheHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
+
+  protocol_version = 'HTTP/1.1'  # override BaseHTTPServer setting
+  wbufsize = -1  # override StreamRequestHandler (a base class) setting
+
+  def handle(self):
+    try:
+      BaseHTTPServer.BaseHTTPRequestHandler.handle(self)
+    except socket.error as e:
+      # Connection reset errors happen all the time due to the browser closing
+      # without terminating the connection properly.  They can be safely
+      # ignored.
+      if e[0] != errno.ECONNRESET:
+        raise
+
+  def do_GET(self):
+    """Serve a GET request."""
+    resource_range = self.SendHead()
+
+    if not resource_range or not resource_range.resource:
+      return
+    response = resource_range.resource['response']
+
+    if not resource_range.byte_range:
+      self.wfile.write(response)
+      return
+
+    start_index = resource_range.byte_range.from_byte
+    end_index = resource_range.byte_range.to_byte
+    self.wfile.write(response[start_index:end_index + 1])
+
+  def do_HEAD(self):
+    """Serve a HEAD request."""
+    self.SendHead()
+
+  def log_error(self, fmt, *args):
+    pass
+
+  def log_request(self, code='-', size='-'):
+    # Don't spam the console unless it is important.
+    pass
+
+  def SendHead(self):
+    path = os.path.realpath(self.translate_path(self.path))
+    if path not in self.server.resource_map:
+      self.send_error(404, 'File not found')
+      return None
+
+    resource = self.server.resource_map[path]
+    total_num_of_bytes = resource['content-length']
+    byte_range = self.GetByteRange(total_num_of_bytes)
+    if byte_range:
+      # request specified a range, so set response code to 206.
+      self.send_response(206)
+      self.send_header('Content-Range', 'bytes %d-%d/%d' %
+                       (byte_range.from_byte, byte_range.to_byte,
+                        total_num_of_bytes))
+      total_num_of_bytes = byte_range.to_byte - byte_range.from_byte + 1
+    else:
+      self.send_response(200)
+
+    self.send_header('Content-Length', str(total_num_of_bytes))
+    self.send_header('Content-Type', resource['content-type'])
+    self.send_header('Last-Modified',
+                     self.date_time_string(resource['last-modified']))
+    if resource['zipped']:
+      self.send_header('Content-Encoding', 'gzip')
+    self.end_headers()
+    return ResourceAndRange(resource, byte_range)
+
+  def GetByteRange(self, total_num_of_bytes):
+    """Parse the header and get the range values specified.
+
+    Args:
+      total_num_of_bytes: Total # of bytes in requested resource,
+      used to calculate upper range limit.
+    Returns:
+      A ByteRange namedtuple object with the requested byte-range values.
+      If no Range is explicitly requested or there is a failure parsing,
+      return None.
+      If range specified is in the format "N-", return N-END. Refer to
+      http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for details.
+      If upper range limit is greater than total # of bytes, return upper index.
+    """
+
+    range_header = self.headers.getheader('Range')
+    if range_header is None:
+      return None
+    if not range_header.startswith('bytes='):
+      return None
+
+    # The range header is expected to be a string in this format:
+    # bytes=0-1
+    # Get the upper and lower limits of the specified byte-range.
+    # We've already confirmed that range_header starts with 'bytes='.
+    byte_range_values = range_header[len('bytes='):].split('-')
+    from_byte = 0
+    to_byte = 0
+
+    if len(byte_range_values) == 2:
+      # If to_range is not defined return all bytes starting from from_byte.
+      to_byte = (int(byte_range_values[1]) if byte_range_values[1] else
+                 total_num_of_bytes - 1)
+      # If from_range is not defined return last 'to_byte' bytes.
+      from_byte = (int(byte_range_values[0]) if byte_range_values[0] else
+                   total_num_of_bytes - to_byte)
+    else:
+      return None
+
+    # Do some validation.
+    if from_byte < 0:
+      return None
+
+    # Make to_byte the end byte by default in edge cases.
+    if to_byte < from_byte or to_byte >= total_num_of_bytes:
+      to_byte = total_num_of_bytes - 1
+
+    return ByteRange(from_byte, to_byte)
+
+
+class _MemoryCacheHTTPServerImpl(SocketServer.ThreadingMixIn,
+                                 BaseHTTPServer.HTTPServer):
+  # Increase the request queue size. The default value, 5, is set in
+  # SocketServer.TCPServer (the parent of BaseHTTPServer.HTTPServer).
+  # Since we're intercepting many domains through this single server,
+  # it is quite possible to get more than 5 concurrent requests.
+  request_queue_size = 128
+
+  # Don't prevent python from exiting when there is thread activity.
+  daemon_threads = True
+
+  def __init__(self, host_port, handler, paths):
+    BaseHTTPServer.HTTPServer.__init__(self, host_port, handler)
+    self.resource_map = {}
+    for path in paths:
+      if os.path.isdir(path):
+        self.AddDirectoryToResourceMap(path)
+      else:
+        self.AddFileToResourceMap(path)
+
+  def AddDirectoryToResourceMap(self, directory_path):
+    """Loads all files in directory_path into the in-memory resource map."""
+    for root, dirs, files in os.walk(directory_path):
+      # Skip hidden files and folders (like .svn and .git).
+      files = [f for f in files if f[0] != '.']
+      dirs[:] = [d for d in dirs if d[0] != '.']
+
+      for f in files:
+        file_path = os.path.join(root, f)
+        if not os.path.exists(file_path):  # Allow for '.#' files
+          continue
+        self.AddFileToResourceMap(file_path)
+
+  def AddFileToResourceMap(self, file_path):
+    """Loads file_path into the in-memory resource map."""
+    file_path = os.path.realpath(file_path)
+    if file_path in self.resource_map:
+      return
+
+    with open(file_path, 'rb') as fd:
+      response = fd.read()
+      fs = os.fstat(fd.fileno())
+    content_type = mimetypes.guess_type(file_path)[0]
+    zipped = False
+    if content_type in ['text/html', 'text/css', 'application/javascript']:
+      zipped = True
+      sio = StringIO.StringIO()
+      gzf = gzip.GzipFile(fileobj=sio, compresslevel=9, mode='wb')
+      gzf.write(response)
+      gzf.close()
+      response = sio.getvalue()
+      sio.close()
+    self.resource_map[file_path] = {
+        'content-type': content_type,
+        'content-length': len(response),
+        'last-modified': fs.st_mtime,
+        'response': response,
+        'zipped': zipped
+    }
+
+    index = 'index.html'
+    if os.path.basename(file_path) == index:
+      dir_path = os.path.dirname(file_path)
+      self.resource_map[dir_path] = self.resource_map[file_path]
+
+
+class MemoryCacheHTTPServerBackend(local_server.LocalServerBackend):
+
+  def __init__(self):
+    super(MemoryCacheHTTPServerBackend, self).__init__()
+    self._httpd = None
+
+  def StartAndGetNamedPorts(self, args):
+    base_dir = args['base_dir']
+    os.chdir(base_dir)
+
+    paths = args['paths']
+    for path in paths:
+      if not os.path.realpath(path).startswith(os.path.realpath(os.getcwd())):
+        print >> sys.stderr, '"%s" is not under the cwd.' % path
+        sys.exit(1)
+
+    server_address = (args['host'], args['port'])
+    MemoryCacheHTTPRequestHandler.protocol_version = 'HTTP/1.1'
+    self._httpd = _MemoryCacheHTTPServerImpl(
+        server_address, MemoryCacheHTTPRequestHandler, paths)
+    return [local_server.NamedPort('http', self._httpd.server_address[1])]
+
+  def ServeForever(self):
+    return self._httpd.serve_forever()
+
+
+class MemoryCacheHTTPServer(local_server.LocalServer):
+
+  def __init__(self, paths):
+    super(MemoryCacheHTTPServer, self).__init__(MemoryCacheHTTPServerBackend)
+    self._base_dir = None
+
+    for path in paths:
+      assert os.path.exists(path), '%s does not exist.' % path
+
+    paths = list(paths)
+    self._paths = paths
+
+    self._paths_as_set = set(map(os.path.realpath, paths))
+
+    common_prefix = os.path.commonprefix(paths)
+    if os.path.isdir(common_prefix):
+      self._base_dir = common_prefix
+    else:
+      self._base_dir = os.path.dirname(common_prefix)
+
+  def GetBackendStartupArgs(self):
+    return {'base_dir': self._base_dir,
+            'paths': self._paths,
+            'host': self.host_ip,
+            'port': 0}
+
+  @property
+  def paths(self):
+    return self._paths_as_set
+
+  @property
+  def url(self):
+    return 'http://127.0.0.1:%s' % self.port
+
+  def UrlOf(self, path):
+    if os.path.isabs(path):
+      relative_path = os.path.relpath(path, self._base_dir)
+    else:
+      relative_path = path
+    # Preserve trailing slash or backslash.
+    # It doesn't matter in a file path, but it does matter in a URL.
+    if path.endswith(os.sep) or (os.altsep and path.endswith(os.altsep)):
+      relative_path += '/'
+    return urlparse.urljoin(self.url, relative_path.replace(os.sep, '/'))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/memory_cache_http_server_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/memory_cache_http_server_unittest.py
new file mode 100644
index 0000000..9a74a1b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/memory_cache_http_server_unittest.py
@@ -0,0 +1,80 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+
+from telemetry.core import util
+from telemetry.testing import tab_test_case
+
+
+class MemoryCacheHTTPServerTest(tab_test_case.TabTestCase):
+
+  def setUp(self):
+    super(MemoryCacheHTTPServerTest, self).setUp()
+    self._test_filename = 'bear.webm'
+    _test_file = os.path.join(util.GetUnittestDataDir(), 'bear.webm')
+    self._test_file_size = os.stat(_test_file).st_size
+
+  def testBasicHostingAndRangeRequests(self):
+    self.Navigate('blank.html')
+    x = self._tab.EvaluateJavaScript('document.body.innerHTML')
+    x = x.strip()
+
+    # Test basic html hosting.
+    self.assertEquals(x, 'Hello world')
+
+    file_size = self._test_file_size
+    last_byte = file_size - 1
+    # Test byte range request: no end byte.
+    self.CheckContentHeaders('0-', '0-%d' % last_byte, file_size)
+
+    # Test byte range request: greater than zero start byte.
+    self.CheckContentHeaders('100-', '100-%d' % last_byte, file_size - 100)
+
+    # Test byte range request: explicit byte range.
+    self.CheckContentHeaders('2-500', '2-500', '499')
+
+    # Test byte range request: no start byte.
+    self.CheckContentHeaders('-228', '%d-%d' % (file_size - 228, last_byte),
+                             '228')
+
+    # Test byte range request: end byte less than start byte.
+    self.CheckContentHeaders('100-5', '100-%d' % last_byte, file_size - 100)
+
+  def CheckContentHeaders(self, content_range_request, content_range_response,
+                          content_length_response):
+    self._tab.ExecuteJavaScript("""
+        var loaded = false;
+        var xmlhttp = new XMLHttpRequest();
+        xmlhttp.onload = function(e) {
+          loaded = true;
+        };
+        // Avoid cached content by appending unique URL param.
+        xmlhttp.open('GET', {{ url }} + "?t=" + Date.now(), true);
+        xmlhttp.setRequestHeader('Range', {{ range }});
+        xmlhttp.send();
+        """,
+        url=self.UrlOfUnittestFile(self._test_filename),
+        range='bytes=%s' % content_range_request)
+    self._tab.WaitForJavaScriptCondition('loaded', timeout=5)
+    content_range = self._tab.EvaluateJavaScript(
+        'xmlhttp.getResponseHeader("Content-Range");')
+    content_range_response = 'bytes %s/%d' % (content_range_response,
+                                              self._test_file_size)
+    self.assertEquals(content_range, content_range_response)
+    content_length = self._tab.EvaluateJavaScript(
+        'xmlhttp.getResponseHeader("Content-Length");')
+    self.assertEquals(content_length, str(content_length_response))
+
+  def testAbsoluteAndRelativePathsYieldSameURL(self):
+    test_file_rel_path = 'green_rect.html'
+    test_file_abs_path = os.path.abspath(os.path.join(util.GetUnittestDataDir(),
+                                                      test_file_rel_path))
+    # It's necessary to bypass self.UrlOfUnittestFile since that
+    # concatenates the unittest directory on to the incoming path,
+    # causing the same code path to be taken in both cases.
+    self._platform.SetHTTPServerDirectories(util.GetUnittestDataDir())
+    self.assertEquals(
+      self._platform.http_server.UrlOf(test_file_rel_path),
+      self._platform.http_server.UrlOf(test_file_abs_path))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/network_controller.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/network_controller.py
new file mode 100644
index 0000000..da2e55a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/network_controller.py
@@ -0,0 +1,43 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from py_trace_event import trace_event
+
+
+class NetworkController(object):
+  """Control network settings and servers to simulate the Web.
+
+  Network changes include forwarding device ports to host platform ports.
+  Web Page Replay is used to record and replay HTTP/HTTPS responses.
+  """
+
+  __metaclass__ = trace_event.TracedMetaClass
+
+  def __init__(self, network_controller_backend):
+    self._network_controller_backend = network_controller_backend
+
+  def InitializeIfNeeded(self, use_live_traffic=False):
+    self._network_controller_backend.InitializeIfNeeded(use_live_traffic)
+
+  def Open(self, wpr_mode, extra_wpr_args):
+    self._network_controller_backend.Open(wpr_mode, extra_wpr_args)
+
+  def UpdateTrafficSettings(self, round_trip_latency_ms=None,
+      download_bandwidth_kbps=None, upload_bandwidth_kbps=None):
+    self._network_controller_backend.ts_proxy_server.UpdateTrafficSettings(
+      round_trip_latency_ms, download_bandwidth_kbps, upload_bandwidth_kbps)
+
+  @property
+  def is_open(self):
+    return self._network_controller_backend.is_open
+
+  def Close(self):
+    self._network_controller_backend.Close()
+
+  def StartReplay(self, archive_path, make_javascript_deterministic=False):
+    self._network_controller_backend.StartReplay(
+        archive_path, make_javascript_deterministic)
+
+  def StopReplay(self):
+    self._network_controller_backend.StopReplay()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/os_version.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/os_version.py
new file mode 100644
index 0000000..57285c1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/os_version.py
@@ -0,0 +1,41 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# pylint: disable=protected-access
+
+
+class OSVersion(str):
+  def __new__(cls, friendly_name, sortable_name):
+    version = str.__new__(cls, friendly_name)
+    version._sortable_name = sortable_name
+    return version
+
+  def __lt__(self, other):
+    return self._sortable_name < other._sortable_name
+
+  def __gt__(self, other):
+    return self._sortable_name > other._sortable_name
+
+  def __le__(self, other):
+    return self._sortable_name <= other._sortable_name
+
+  def __ge__(self, other):
+    return self._sortable_name >= other._sortable_name
+
+
+XP = OSVersion('xp', 5.1)
+VISTA = OSVersion('vista', 6.0)
+WIN7 = OSVersion('win7', 6.1)
+WIN8 = OSVersion('win8', 6.2)
+WIN81 = OSVersion('win8.1', 6.3)
+WIN10 = OSVersion('win10', 10)
+
+LEOPARD = OSVersion('leopard', 105)
+SNOWLEOPARD = OSVersion('snowleopard', 106)
+LION = OSVersion('lion', 107)
+MOUNTAINLION = OSVersion('mountainlion', 108)
+MAVERICKS = OSVersion('mavericks', 109)
+YOSEMITE = OSVersion('yosemite', 1010)
+ELCAPITAN = OSVersion('elcapitan', 1011)
+SIERRA = OSVersion('sierra', 1012)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/platform.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/platform.py
new file mode 100644
index 0000000..7435c4e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/platform.py
@@ -0,0 +1,425 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import logging as real_logging
+import os
+import sys
+
+from telemetry.core import discover
+from telemetry.core import local_server
+from telemetry.core import memory_cache_http_server
+from telemetry.core import network_controller
+from telemetry.core import tracing_controller
+from telemetry.core import util
+from telemetry.internal.platform import (platform_backend as
+                                         platform_backend_module)
+
+_host_platform = None
+# Remote platform is a dictionary from device ids to remote platform instances.
+_remote_platforms = {}
+
+
+def _InitHostPlatformIfNeeded():
+  global _host_platform
+  if _host_platform:
+    return
+  backend = None
+  backends = _IterAllPlatformBackendClasses()
+  for platform_backend_class in backends:
+    if platform_backend_class.IsPlatformBackendForHost():
+      backend = platform_backend_class()
+      break
+  if not backend:
+    raise NotImplementedError()
+  _host_platform = Platform(backend)
+
+
+def GetHostPlatform():
+  _InitHostPlatformIfNeeded()
+  return _host_platform
+
+
+def _IterAllPlatformBackendClasses():
+  platform_dir = os.path.dirname(os.path.realpath(
+      platform_backend_module.__file__))
+  return discover.DiscoverClasses(
+      platform_dir, util.GetTelemetryDir(),
+      platform_backend_module.PlatformBackend).itervalues()
+
+
+def GetPlatformForDevice(device, finder_options, logging=real_logging):
+  """ Returns a platform instance for the device.
+    Args:
+      device: a device.Device instance.
+  """
+  if device.guid in _remote_platforms:
+    return _remote_platforms[device.guid]
+  try:
+    for platform_backend_class in _IterAllPlatformBackendClasses():
+      if platform_backend_class.SupportsDevice(device):
+        _remote_platforms[device.guid] = (
+            platform_backend_class.CreatePlatformForDevice(device,
+                                                           finder_options))
+        return _remote_platforms[device.guid]
+    return None
+  except Exception:
+    current_exception = sys.exc_info()
+    logging.error('Fail to create platform instance for %s.', device.name)
+    raise current_exception[0], current_exception[1], current_exception[2]
+
+
+class Platform(object):
+  """The platform that the target browser is running on.
+
+  Provides a limited interface to interact with the platform itself, where
+  possible. It's important to note that platforms may not provide a specific
+  API, so check with IsFooBar() for availability.
+  """
+
+  def __init__(self, platform_backend):
+    self._platform_backend = platform_backend
+    self._platform_backend.InitPlatformBackend()
+    self._platform_backend.SetPlatform(self)
+    self._network_controller = network_controller.NetworkController(
+        self._platform_backend.network_controller_backend)
+    self._tracing_controller = tracing_controller.TracingController(
+        self._platform_backend.tracing_controller_backend)
+    self._local_server_controller = local_server.LocalServerController(
+        self._platform_backend)
+    self._is_monitoring_power = False
+
+  @property
+  def is_host_platform(self):
+    return self == GetHostPlatform()
+
+  @property
+  def network_controller(self):
+    """Control network settings and servers to simulate the Web."""
+    return self._network_controller
+
+  @property
+  def tracing_controller(self):
+    return self._tracing_controller
+
+  def Initialize(self):
+    pass
+
+  def CanMonitorThermalThrottling(self):
+    """Platforms may be able to detect thermal throttling.
+
+    Some fan-less computers go into a reduced performance mode when their heat
+    exceeds a certain threshold. Performance tests in particular should use this
+    API to detect if this has happened and interpret results accordingly.
+    """
+    return self._platform_backend.CanMonitorThermalThrottling()
+
+  def GetSystemLog(self):
+    return self._platform_backend.GetSystemLog()
+
+  def IsThermallyThrottled(self):
+    """Returns True if the device is currently thermally throttled."""
+    return self._platform_backend.IsThermallyThrottled()
+
+  def HasBeenThermallyThrottled(self):
+    """Returns True if the device has been thermally throttled."""
+    return self._platform_backend.HasBeenThermallyThrottled()
+
+  def GetDeviceTypeName(self):
+    """Returns a string description of the Platform device, or None.
+
+    Examples: Nexus 7, Nexus 6, Desktop"""
+    return self._platform_backend.GetDeviceTypeName()
+
+  def GetArchName(self):
+    """Returns a string description of the Platform architecture.
+
+    Examples: x86_64 (posix), AMD64 (win), armeabi-v7a, x86"""
+    return self._platform_backend.GetArchName()
+
+  def GetOSName(self):
+    """Returns a string description of the Platform OS.
+
+    Examples: WIN, MAC, LINUX, CHROMEOS"""
+    return self._platform_backend.GetOSName()
+
+  def GetOSVersionName(self):
+    """Returns a logically sortable, string-like description of the Platform OS
+    version.
+
+    Examples: VISTA, WIN7, LION, MOUNTAINLION"""
+    return self._platform_backend.GetOSVersionName()
+
+  def GetOSVersionNumber(self):
+    """Returns an integer description of the Platform OS major version.
+
+    Examples: On Mac, 13 for Mavericks, 14 for Yosemite."""
+    return self._platform_backend.GetOSVersionNumber()
+
+  def GetSystemTotalPhysicalMemory(self):
+    """Returns an integer with the total physical memory in bytes."""
+    return self._platform_backend.GetSystemTotalPhysicalMemory()
+
+  def CanFlushIndividualFilesFromSystemCache(self):
+    """Returns true if the disk cache can be flushed for specific files."""
+    return self._platform_backend.CanFlushIndividualFilesFromSystemCache()
+
+  def SupportFlushEntireSystemCache(self):
+    """Returns true if entire system cache can be flushed.
+
+    Also checks that platform has required privilegues to flush system caches.
+    """
+    return self._platform_backend.SupportFlushEntireSystemCache()
+
+  def FlushEntireSystemCache(self):
+    """Flushes the OS's file cache completely.
+
+    This function may require root or administrator access. Clients should
+    call SupportFlushEntireSystemCache to check first.
+    """
+    return self._platform_backend.FlushEntireSystemCache()
+
+  def FlushSystemCacheForDirectory(self, directory):
+    """Flushes the OS's file cache for the specified directory.
+
+    This function does not require root or administrator access."""
+    return self._platform_backend.FlushSystemCacheForDirectory(directory)
+
+  def FlushDnsCache(self):
+    """Flushes the OS's DNS cache completely.
+
+    This function may require root or administrator access."""
+    return self._platform_backend.FlushDnsCache()
+
+  def LaunchApplication(self,
+                        application,
+                        parameters=None,
+                        elevate_privilege=False):
+    """"Launches the given |application| with a list of |parameters| on the OS.
+
+    Set |elevate_privilege| to launch the application with root or admin rights.
+
+    Returns:
+      A popen style process handle for host platforms.
+    """
+    return self._platform_backend.LaunchApplication(
+        application,
+        parameters,
+        elevate_privilege=elevate_privilege)
+
+  def IsApplicationRunning(self, application):
+    """Returns whether an application is currently running."""
+    return self._platform_backend.IsApplicationRunning(application)
+
+  def CanLaunchApplication(self, application):
+    """Returns whether the platform can launch the given application."""
+    return self._platform_backend.CanLaunchApplication(application)
+
+  def InstallApplication(self, application):
+    """Installs the given application."""
+    return self._platform_backend.InstallApplication(application)
+
+  def CanCaptureVideo(self):
+    """Returns a bool indicating whether the platform supports video capture."""
+    return self._platform_backend.CanCaptureVideo()
+
+  def StartVideoCapture(self, min_bitrate_mbps):
+    """Starts capturing video.
+
+    Outer framing may be included (from the OS, browser window, and webcam).
+
+    Args:
+      min_bitrate_mbps: The minimum capture bitrate in MegaBits Per Second.
+          The platform is free to deliver a higher bitrate if it can do so
+          without increasing overhead.
+
+    Raises:
+      ValueError if the required |min_bitrate_mbps| can't be achieved.
+    """
+    return self._platform_backend.StartVideoCapture(min_bitrate_mbps)
+
+  def StopVideoCapture(self):
+    """Stops capturing video.
+
+    Returns:
+      A telemetry.core.video.Video object.
+    """
+    return self._platform_backend.StopVideoCapture()
+
+  def CanMonitorPower(self):
+    """Returns True iff power can be monitored asynchronously via
+    StartMonitoringPower() and StopMonitoringPower().
+    """
+    return self._platform_backend.CanMonitorPower()
+
+  def CanMeasurePerApplicationPower(self):
+    """Returns True if the power monitor can measure power for the target
+    application in isolation. False if power measurement is for full system
+    energy consumption."""
+    return self._platform_backend.CanMeasurePerApplicationPower()
+
+  def StartMonitoringPower(self, browser):
+    """Starts monitoring power utilization statistics.
+
+    Args:
+      browser: The browser to monitor.
+    """
+    assert self._platform_backend.CanMonitorPower()
+    self._platform_backend.StartMonitoringPower(browser)
+    self._is_monitoring_power = True
+
+  def StopMonitoringPower(self):
+    """Stops monitoring power utilization and returns stats
+
+    Returns:
+      None if power measurement failed for some reason, otherwise a dict of
+      power utilization statistics containing: {
+        # An identifier for the data provider. Allows to evaluate the precision
+        # of the data. Example values: monsoon, powermetrics, ds2784
+        'identifier': identifier,
+
+        # The instantaneous power (voltage * current) reading in milliwatts at
+        # each sample.
+        'power_samples_mw':  [mw0, mw1, ..., mwN],
+
+        # The full system energy consumption during the sampling period in
+        # milliwatt hours. May be estimated by integrating power samples or may
+        # be exact on supported hardware.
+        'energy_consumption_mwh': mwh,
+
+        # The target application's energy consumption during the sampling period
+        # in milliwatt hours. Should be returned iff
+        # CanMeasurePerApplicationPower() return true.
+        'application_energy_consumption_mwh': mwh,
+
+        # A platform-specific dictionary of additional details about the
+        # utilization of individual hardware components.
+        component_utilization: {
+          ...
+        }
+        # Platform-specific data not attributed to any particular hardware
+        # component.
+        platform_info: {
+
+          # Device-specific onboard temperature sensor.
+          'average_temperature_c': c,
+
+           ...
+        }
+
+      }
+    """
+    ret_val = self._platform_backend.StopMonitoringPower()
+    self._is_monitoring_power = False
+    return ret_val
+
+  def IsMonitoringPower(self):
+    """Returns true if power is currently being monitored, false otherwise."""
+    # TODO(rnephew): Remove when crbug.com/553601 is solved.
+    real_logging.info('IsMonitoringPower: %s', self._is_monitoring_power)
+    return self._is_monitoring_power
+
+  def CanMonitorNetworkData(self):
+    """Returns true if network data can be retrieved, false otherwise."""
+    return self._platform_backend.CanMonitorNetworkData()
+
+  def GetNetworkData(self, browser):
+    """Get current network data.
+    Returns:
+      Tuple of (sent_data, received_data) in kb if data can be found,
+      None otherwise.
+    """
+    assert browser.platform == self
+    return self._platform_backend.GetNetworkData(browser)
+
+  def IsCooperativeShutdownSupported(self):
+    """Indicates whether CooperativelyShutdown, below, is supported.
+    It is not necessary to implement it on all platforms."""
+    return self._platform_backend.IsCooperativeShutdownSupported()
+
+  def CooperativelyShutdown(self, proc, app_name):
+    """Cooperatively shut down the given process from subprocess.Popen.
+
+    Currently this is only implemented on Windows. See
+    crbug.com/424024 for background on why it was added.
+
+    Args:
+      proc: a process object returned from subprocess.Popen.
+      app_name: on Windows, is the prefix of the application's window
+          class name that should be searched for. This helps ensure
+          that only the application's windows are closed.
+
+    Returns True if it is believed the attempt succeeded.
+    """
+    return self._platform_backend.CooperativelyShutdown(proc, app_name)
+
+  def CanTakeScreenshot(self):
+    return self._platform_backend.CanTakeScreenshot()
+
+  # TODO(nednguyen): Implement this on Mac, Linux & Win. (crbug.com/369490)
+  def TakeScreenshot(self, file_path):
+    """ Takes a screenshot of the platform and save to |file_path|.
+
+    Note that this method may not be supported on all platform, so check with
+    CanTakeScreenshot before calling this.
+
+    Args:
+      file_path: Where to save the screenshot to. If the platform is remote,
+        |file_path| is the path on the host platform.
+
+    Returns True if it is believed the attempt succeeded.
+    """
+    return self._platform_backend.TakeScreenshot(file_path)
+
+  def StartLocalServer(self, server):
+    """Starts a LocalServer and associates it with this platform.
+    |server.Close()| should be called manually to close the started server.
+    """
+    self._local_server_controller.StartServer(server)
+
+  @property
+  def http_server(self):
+    return self._local_server_controller.GetRunningServer(
+        memory_cache_http_server.MemoryCacheHTTPServer, None)
+
+  def SetHTTPServerDirectories(self, paths):
+    """Returns True if the HTTP server was started, False otherwise."""
+    if isinstance(paths, basestring):
+      paths = set([paths])
+    paths = set(os.path.realpath(p) for p in paths)
+
+    # If any path is in a subdirectory of another, remove the subdirectory.
+    duplicates = set()
+    for parent_path in paths:
+      for sub_path in paths:
+        if parent_path == sub_path:
+          continue
+        if os.path.commonprefix((parent_path, sub_path)) == parent_path:
+          duplicates.add(sub_path)
+    paths -= duplicates
+
+    if self.http_server:
+      if paths and self.http_server.paths == paths:
+        return False
+
+      self.http_server.Close()
+
+    if not paths:
+      return False
+
+    server = memory_cache_http_server.MemoryCacheHTTPServer(paths)
+    self.StartLocalServer(server)
+    return True
+
+  def StopAllLocalServers(self):
+    self._local_server_controller.Close()
+
+  @property
+  def local_servers(self):
+    """Returns the currently running local servers."""
+    return self._local_server_controller.local_servers
+
+  def HasBattOrConnected(self):
+    return  self._platform_backend.HasBattOrConnected()
+
+  def WaitForTemperature(self, temp):
+    return self._platform_backend.WaitForTemperature(temp)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/platform_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/platform_unittest.py
new file mode 100644
index 0000000..5a838d2
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/platform_unittest.py
@@ -0,0 +1,45 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import tempfile
+
+from telemetry import decorators
+from telemetry.core import os_version
+from telemetry.util import image_util
+from telemetry.testing import tab_test_case
+
+
+class PlatformScreenshotTest(tab_test_case.TabTestCase):
+
+  def testScreenshotSupported(self):
+    if self._platform.GetOSName() == 'android':
+      self.assertTrue(self._platform.CanTakeScreenshot())
+
+  # Run this test in serial to avoid multiple browsers pop up on the screen.
+  @decorators.Isolated
+  @decorators.Disabled('linux')  # crbug.com/563656
+  @decorators.Disabled('mac')  # crbug.com/660587
+  def testScreenshot(self):
+    if not self._platform.CanTakeScreenshot():
+      self.skipTest('Platform does not support screenshots, skipping test.')
+    # Skip the test on Mac 10.5, 10.6, 10.7 because png format isn't
+    # recognizable on Mac < 10.8 (crbug.com/369490#c13)
+    if (self._platform.GetOSName() == 'mac' and
+        self._platform.GetOSVersionName() in
+        (os_version.LEOPARD, os_version.SNOWLEOPARD, os_version.LION)):
+      self.skipTest('OS X version %s too old' % self._platform.GetOSName())
+    tf = tempfile.NamedTemporaryFile(delete=False, suffix='.png')
+    tf.close()
+    try:
+      self.Navigate('screenshot_test.html')
+      self._platform.TakeScreenshot(tf.name)
+      # Assert that screenshot image contains the color of the triangle defined
+      # in screenshot_test.html.
+      img = image_util.FromPngFile(tf.name)
+      screenshot_pixels = image_util.Pixels(img)
+      special_colored_pixel = bytearray([217, 115, 43])
+      self.assertTrue(special_colored_pixel in screenshot_pixels)
+    finally:
+      os.remove(tf.name)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/profiling_controller.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/profiling_controller.py
new file mode 100644
index 0000000..0e008a7
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/profiling_controller.py
@@ -0,0 +1,15 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+class ProfilingController(object):
+
+  def __init__(self, profiling_controller_backend):
+    self._profiling_controller_backend = profiling_controller_backend
+
+  def Start(self, profiler_name, base_output_file):
+    self._profiling_controller_backend.Start(profiler_name, base_output_file)
+
+  def Stop(self):
+    return self._profiling_controller_backend.Stop()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/tracing_controller.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/tracing_controller.py
new file mode 100644
index 0000000..1e26f7e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/tracing_controller.py
@@ -0,0 +1,89 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.internal.platform import tracing_agent
+
+
+class TracingController(tracing_agent.TracingAgent):
+
+  def __init__(self, tracing_controller_backend):
+    """Provides control of the tracing systems supported by telemetry."""
+    super(TracingController, self).__init__(
+        tracing_controller_backend._platform_backend)
+    self._tracing_controller_backend = tracing_controller_backend
+
+  @property
+  def telemetry_info(self):
+    return self._tracing_controller_backend.telemetry_info
+
+  @telemetry_info.setter
+  def telemetry_info(self, ii):
+    self._tracing_controller_backend.telemetry_info = ii
+
+  def StartTracing(self, tracing_config, timeout=10):
+    """Starts tracing.
+
+    tracing config contains both tracing options and category filters.
+
+    trace_options specifies which tracing systems to activate. Category filter
+    allows fine-tuning of the data that are collected by the selected tracing
+    systems.
+
+    Some tracers are process-specific, e.g. chrome tracing, but are not
+    guaranteed to be supported. In order to support tracing of these kinds of
+    tracers, Start will succeed *always*, even if the tracing systems you have
+    requested are not supported.
+
+    If you absolutely require a particular tracer to exist, then check
+    for its support after you have started the process in question. Or, have
+    your code fail gracefully when the data you require is not present in the
+    resulting trace.
+    """
+    self._tracing_controller_backend.StartTracing(tracing_config, timeout)
+
+  def StopTracing(self):
+    """Stops tracing and returns a TraceValue."""
+    return self._tracing_controller_backend.StopTracing()
+
+  def FlushTracing(self):
+    """Flush tracing buffer and continue tracing.
+
+    Warning: This method is a temporary hack to enable multi-tab benchmarks
+    (see https://goo.gl/8Gjstr). Please contact Telemetry owners before using
+    it.
+    """
+    self._tracing_controller_backend.FlushTracing()
+
+  @property
+  def is_tracing_running(self):
+    return self._tracing_controller_backend.is_tracing_running
+
+  def IsChromeTracingSupported(self):
+    """Returns whether chrome tracing is supported."""
+    return self._tracing_controller_backend.IsChromeTracingSupported()
+
+  def StartAgentTracing(self, config, timeout=10):
+    """ Starts agent tracing for tracing controller"""
+    return self._tracing_controller_backend.StartAgentTracing(config, timeout)
+
+  def StopAgentTracing(self):
+    """ Stops agent tracing for tracing controller. """
+    return self._tracing_controller_backend.StopAgentTracing()
+
+  def CollectAgentTraceData(self, trace_data_builder, timeout=None):
+    """ Collect tracing data. """
+    return self._tracing_controller_backend.CollectTraceData(trace_data_builder,
+                                                             timeout=timeout)
+
+  def SupportsExplicitClockSync(self):
+    return self._tracing_controller_backend.SupportsExplicitClockSync()
+
+  def RecordClockSyncMarker(self, sync_id,
+                            record_controller_clocksync_marker_callback):
+    return self._tracing_controller_backend.RecordClockSyncMarker(
+        sync_id, record_controller_clocksync_marker_callback)
+
+  def ClearStateIfNeeded(self):
+    """Clear tracing state if needed."""
+    self._tracing_controller_backend.ClearStateIfNeeded()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/tracing_controller_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/tracing_controller_unittest.py
new file mode 100644
index 0000000..aaf6de6
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/tracing_controller_unittest.py
@@ -0,0 +1,187 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import time
+
+from battor import battor_wrapper
+from telemetry import decorators
+from telemetry.core import platform as platform_module
+from telemetry.testing import browser_test_case
+from telemetry.testing import tab_test_case
+from telemetry.timeline import model as model_module
+from telemetry.timeline import tracing_config
+from tracing.trace_data import trace_data as trace_data_module
+
+
+class TracingControllerTest(tab_test_case.TabTestCase):
+
+  @decorators.Isolated
+  def testExceptionRaisedInStopTracing(self):
+    tracing_controller = self._tab.browser.platform.tracing_controller
+    config = tracing_config.TracingConfig()
+    config.enable_chrome_trace = True
+    tracing_controller.StartTracing(config)
+
+    self.Navigate('blank.html')
+
+    def _FakeStopChromeTracing(*args):
+      del args  # Unused
+      raise Exception('Intentional Tracing Exception')
+
+    self._tab._inspector_backend._devtools_client.StopChromeTracing = (
+      _FakeStopChromeTracing)
+    with self.assertRaisesRegexp(Exception, 'Intentional Tracing Exception'):
+      tracing_controller.StopTracing()
+
+    # Tracing is stopped even if there is exception.
+    self.assertFalse(tracing_controller.is_tracing_running)
+
+
+  @decorators.Isolated
+  def testGotTrace(self):
+    tracing_controller = self._browser.platform.tracing_controller
+    config = tracing_config.TracingConfig()
+    config.enable_chrome_trace = True
+    tracing_controller.StartTracing(config)
+
+    trace_data = tracing_controller.StopTracing()
+    # Test that trace data is parsable
+    model = model_module.TimelineModel(trace_data)
+    assert len(model.processes) > 0
+
+  @decorators.Isolated
+  def testStartAndStopTraceMultipleTimes(self):
+    tracing_controller = self._browser.platform.tracing_controller
+    config = tracing_config.TracingConfig()
+    config.enable_chrome_trace = True
+    tracing_controller.StartTracing(config)
+    self.assertFalse(tracing_controller.StartTracing(config))
+
+    trace_data = tracing_controller.StopTracing()
+    # Test that trace data is parsable
+    model_module.TimelineModel(trace_data)
+    self.assertFalse(tracing_controller.is_tracing_running)
+    # Calling stop again will raise exception
+    self.assertRaises(Exception, tracing_controller.StopTracing)
+
+  @decorators.Isolated
+  def testFlushTracing(self):
+    SUBTRACE_COUNT = 5
+
+    tab = self._browser.tabs[0]
+    def InjectMarker(index):
+      marker = 'test-marker-%d' % index
+      tab.EvaluateJavaScript('console.time({{ marker }});', marker=marker)
+      tab.EvaluateJavaScript('console.timeEnd({{ marker }});', marker=marker)
+
+    # Set up the tracing config.
+    tracing_controller = self._browser.platform.tracing_controller
+    config = tracing_config.TracingConfig()
+    config.enable_chrome_trace = True
+
+    # Start tracing and inject a unique marker into the sub-trace.
+    tracing_controller.StartTracing(config)
+    self.assertTrue(tracing_controller.is_tracing_running)
+    InjectMarker(0)
+
+    # Flush tracing |SUBTRACE_COUNT - 1| times and inject a unique marker into
+    # the sub-trace each time.
+    for i in xrange(1, SUBTRACE_COUNT):
+      tracing_controller.FlushTracing()
+      self.assertTrue(tracing_controller.is_tracing_running)
+      InjectMarker(i)
+
+    # Stop tracing.
+    trace_data = tracing_controller.StopTracing()
+    self.assertFalse(tracing_controller.is_tracing_running)
+
+    # Test that trace data is parsable
+    model = model_module.TimelineModel(trace_data)
+
+    # Check that the markers 'test-marker-0', 'flush-tracing', 'test-marker-1',
+    # ..., 'flush-tracing', 'test-marker-|SUBTRACE_COUNT - 1|' are monotonic.
+    custom_markers = [marker for i in xrange(SUBTRACE_COUNT)
+                      for marker in model.FindTimelineMarkers(
+                          'test-marker-%d' % i)]
+    flush_markers = model.FindTimelineMarkers(
+        ['flush-tracing'] * (SUBTRACE_COUNT - 1))
+    markers = [marker for group in zip(custom_markers, flush_markers)
+               for marker in group] + custom_markers[-1:]
+
+    self.assertEquals(len(custom_markers), SUBTRACE_COUNT)
+    self.assertEquals(len(flush_markers), SUBTRACE_COUNT - 1)
+    self.assertEquals(len(markers), 2 * SUBTRACE_COUNT - 1)
+
+    for i in xrange(1, len(markers)):
+      self.assertLess(markers[i - 1].end, markers[i].start)
+
+  def _StartupTracing(self, platform):
+    # Stop browser
+    browser_test_case.teardown_browser()
+
+    # Start tracing
+    self.assertFalse(platform.tracing_controller.is_tracing_running)
+    config = tracing_config.TracingConfig()
+    config.enable_chrome_trace = True
+    platform.tracing_controller.StartTracing(config)
+    self.assertTrue(platform.tracing_controller.is_tracing_running)
+
+    try:
+      # Start browser
+      self.setUpClass()
+      self._browser.tabs[0].Navigate('about:blank')
+      self._browser.tabs[0].WaitForDocumentReadyStateToBeInteractiveOrBetter()
+      self.assertEquals(platform, self._browser.platform)
+
+      # Calling start tracing again will return False
+      self.assertFalse(platform.tracing_controller.StartTracing(config))
+
+      trace_data = platform.tracing_controller.StopTracing()
+      # Test that trace data is parsable
+      model_module.TimelineModel(trace_data)
+      self.assertFalse(platform.tracing_controller.is_tracing_running)
+      # Calling stop tracing again will raise exception
+      self.assertRaises(Exception, platform.tracing_controller.StopTracing)
+    finally:
+      if platform.tracing_controller.is_tracing_running:
+        platform.tracing_controller.StopTracing()
+      if self._browser:
+        self._browser.Close()
+        self._browser = None
+
+  # https://github.com/catapult-project/catapult/issues/3099 (Android)
+  @decorators.Disabled('all')
+  @decorators.Isolated
+  def testStartupTracingOnAndroid(self):
+    self._StartupTracing(self._browser.platform)
+
+  @decorators.Enabled('chromeos')
+  @decorators.Isolated
+  def testStartupTracingOnCrOS(self):
+    self._StartupTracing(self._browser.platform)
+
+  @decorators.Enabled('linux', 'mac', 'win')
+  @decorators.Isolated
+  def testStartupTracingOnDesktop(self):
+    self._StartupTracing(platform_module.GetHostPlatform())
+
+  @decorators.Disabled('linux')  # crbug.com/673761
+  def testBattOrTracing(self):
+    test_platform = self._browser.platform.GetOSName()
+    device = (self._browser.platform._platform_backend.device
+              if test_platform == 'android' else None)
+    if (not battor_wrapper.IsBattOrConnected(test_platform,
+                                             android_device=device)):
+      return # Do not run the test if no BattOr is connected.
+
+    tracing_controller = self._browser.platform.tracing_controller
+    config = tracing_config.TracingConfig()
+    config.enable_battor_trace = True
+    tracing_controller.StartTracing(config)
+    # We wait 1s before starting and stopping tracing to avoid crbug.com/602266,
+    # which would cause a crash otherwise.
+    time.sleep(1)
+    trace_data = tracing_controller.StopTracing()
+    self.assertTrue(
+        trace_data.HasTracesFor(trace_data_module.BATTOR_TRACE_PART))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/util.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/util.py
new file mode 100644
index 0000000..cf44603
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/util.py
@@ -0,0 +1,147 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import glob
+import imp
+import os
+import socket
+import sys
+
+from telemetry import decorators
+
+import py_utils as catapult_util  # pylint: disable=import-error
+
+
+IsRunningOnCrosDevice = catapult_util.IsRunningOnCrosDevice
+GetCatapultDir = catapult_util.GetCatapultDir
+
+
+def GetBaseDir():
+  main_module = sys.modules['__main__']
+  if hasattr(main_module, '__file__'):
+    return os.path.dirname(os.path.abspath(main_module.__file__))
+  else:
+    return os.getcwd()
+
+
+def GetCatapultThirdPartyDir():
+  return os.path.normpath(os.path.join(GetCatapultDir(), 'third_party'))
+
+
+def GetTelemetryDir():
+  return os.path.normpath(os.path.join(
+      os.path.abspath(__file__), '..', '..', '..'))
+
+
+def GetTelemetryThirdPartyDir():
+  return os.path.join(GetTelemetryDir(), 'third_party')
+
+
+def GetUnittestDataDir():
+  return os.path.join(GetTelemetryDir(), 'telemetry', 'internal', 'testing')
+
+
+def GetChromiumSrcDir():
+  return os.path.normpath(os.path.join(GetTelemetryDir(), '..', '..', '..'))
+
+
+_counter = [0]
+
+
+def _GetUniqueModuleName():
+  _counter[0] += 1
+  return "page_set_module_" + str(_counter[0])
+
+
+def GetPythonPageSetModule(file_path):
+  return imp.load_source(_GetUniqueModuleName(), file_path)
+
+
+@decorators.Deprecated(
+    2017, 6, 1,
+    'telemetry.core.utils.WaitFor() is being deprecated. Please use '
+    'catapult.common.py_utils.WaitFor() instead.')
+def WaitFor(condition, timeout):
+  return catapult_util.WaitFor(condition, timeout)
+
+
+class PortKeeper(object):
+  """Port keeper hold an available port on the system.
+
+  Before actually use the port, you must call Release().
+  """
+
+  def __init__(self):
+    self._temp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    self._temp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+    self._temp_socket.bind(('', 0))
+    self._port = self._temp_socket.getsockname()[1]
+
+  @property
+  def port(self):
+    return self._port
+
+  def Release(self):
+    assert self._temp_socket, 'Already released'
+    self._temp_socket.close()
+    self._temp_socket = None
+
+
+def GetUnreservedAvailableLocalPort():
+  """Returns an available port on the system.
+
+  WARNING: This method does not reserve the port it returns, so it may be used
+  by something else before you get to use it. This can lead to flake.
+  """
+  tmp = socket.socket()
+  tmp.bind(('', 0))
+  port = tmp.getsockname()[1]
+  tmp.close()
+
+  return port
+
+
+def GetBuildDirectories(chrome_root=None):
+  """Yields all combination of Chromium build output directories."""
+  # chrome_root can be set to something else via --chrome-root.
+  if not chrome_root:
+    chrome_root = GetChromiumSrcDir()
+
+  # CHROMIUM_OUTPUT_DIR can be set by --chromium-output-directory.
+  output_dir = os.environ.get('CHROMIUM_OUTPUT_DIR')
+  if output_dir:
+    yield os.path.join(chrome_root, output_dir)
+  elif os.path.exists('build.ninja'):
+    yield os.getcwd()
+  else:
+    out_dir = os.environ.get('CHROMIUM_OUT_DIR')
+    if out_dir:
+      build_dirs = [out_dir]
+    else:
+      build_dirs = ['build',
+                    'out',
+                    'xcodebuild']
+
+    build_types = ['Debug', 'Debug_x64', 'Release', 'Release_x64', 'Default']
+
+    for build_dir in build_dirs:
+      for build_type in build_types:
+        yield os.path.join(chrome_root, build_dir, build_type)
+
+
+def GetSequentialFileName(base_name):
+  """Returns the next sequential file name based on |base_name| and the
+  existing files. base_name should not contain extension.
+  e.g: if base_name is /tmp/test, and /tmp/test_000.json,
+  /tmp/test_001.mp3 exist, this returns /tmp/test_002. In case no
+  other sequential file name exist, this will return /tmp/test_000
+  """
+  name, ext = os.path.splitext(base_name)
+  assert ext == '', 'base_name cannot contain file extension.'
+  index = 0
+  while True:
+    output_name = '%s_%03d' % (name, index)
+    if not glob.glob(output_name + '.*'):
+      break
+    index = index + 1
+  return output_name
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/util_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/util_unittest.py
new file mode 100644
index 0000000..75110ad
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/core/util_unittest.py
@@ -0,0 +1,39 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import os
+import shutil
+import tempfile
+import unittest
+
+from telemetry.core import util
+
+
+class TestGetSequentialFileName(unittest.TestCase):
+
+  def __init__(self, *args, **kwargs):
+    super(TestGetSequentialFileName, self).__init__(*args, **kwargs)
+    self.test_directory = None
+
+  def setUp(self):
+    self.test_directory = tempfile.mkdtemp()
+
+  def testGetSequentialFileNameNoOtherSequentialFile(self):
+    next_json_test_file_path = util.GetSequentialFileName(os.path.join(
+        self.test_directory, 'test'))
+    self.assertEquals(
+        os.path.join(self.test_directory, 'test_000'), next_json_test_file_path)
+
+  def testGetSequentialFileNameWithOtherSequentialFiles(self):
+    # Create test_000.json, test_001.json, test_002.json in test directory.
+    for i in xrange(3):
+      with open(
+          os.path.join(self.test_directory, 'test_%03d.json' % i), 'w') as _:
+        pass
+    next_json_test_file_path = util.GetSequentialFileName(os.path.join(
+        self.test_directory, 'test'))
+    self.assertEquals(
+        os.path.join(self.test_directory, 'test_003'), next_json_test_file_path)
+
+  def tearDown(self):
+    shutil.rmtree(self.test_directory)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/decorators.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/decorators.py
new file mode 100644
index 0000000..1192782
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/decorators.py
@@ -0,0 +1,343 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+# pylint: disable=protected-access
+
+import datetime
+import functools
+import os
+import inspect
+import types
+import warnings
+
+
+def Cache(obj):
+  """Decorator for caching read-only properties.
+
+  Example usage (always returns the same Foo instance):
+    @Cache
+    def CreateFoo():
+      return Foo()
+
+  If CreateFoo() accepts parameters, a separate cached value is maintained
+  for each unique parameter combination.
+
+  Cached methods maintain their cache for the lifetime of the /instance/, while
+  cached functions maintain their cache for the lifetime of the /module/.
+  """
+  @functools.wraps(obj)
+  def Cacher(*args, **kwargs):
+    cacher = args[0] if inspect.getargspec(obj).args[:1] == ['self'] else obj
+    cacher.__cache = cacher.__cache if hasattr(cacher, '__cache') else {}
+    key = str(obj) + str(args) + str(kwargs)
+    if key not in cacher.__cache:
+      cacher.__cache[key] = obj(*args, **kwargs)
+    return cacher.__cache[key]
+  return Cacher
+
+
+class Deprecated(object):
+
+  def __init__(self, year, month, day, extra_guidance=''):
+    self._date_of_support_removal = datetime.date(year, month, day)
+    self._extra_guidance = extra_guidance
+
+  def _DisplayWarningMessage(self, target):
+    target_str = ''
+    if isinstance(target, types.FunctionType):
+      target_str = 'Function %s' % target.__name__
+    else:
+      target_str = 'Class %s' % target.__name__
+    warnings.warn('%s is deprecated. It will no longer be supported on %s. '
+                  'Please remove it or switch to an alternative before '
+                  'that time. %s\n'
+                  % (target_str,
+                     self._date_of_support_removal.strftime('%B %d, %Y'),
+                     self._extra_guidance),
+                  stacklevel=self._ComputeStackLevel())
+
+  def _ComputeStackLevel(self):
+    this_file, _ = os.path.splitext(__file__)
+    frame = inspect.currentframe()
+    i = 0
+    while True:
+      filename = frame.f_code.co_filename
+      if not filename.startswith(this_file):
+        return i
+      frame = frame.f_back
+      i += 1
+
+  def __call__(self, target):
+    if isinstance(target, types.FunctionType):
+      @functools.wraps(target)
+      def wrapper(*args, **kwargs):
+        self._DisplayWarningMessage(target)
+        return target(*args, **kwargs)
+      return wrapper
+    elif inspect.isclass(target):
+      original_ctor = target.__init__
+
+      # We have to handle case original_ctor is object.__init__ separately
+      # since object.__init__ does not have __module__ defined, which
+      # cause functools.wraps() to raise exception.
+      if original_ctor == object.__init__:
+        def new_ctor(*args, **kwargs):
+          self._DisplayWarningMessage(target)
+          return original_ctor(*args, **kwargs)
+      else:
+        @functools.wraps(original_ctor)
+        def new_ctor(*args, **kwargs):
+          self._DisplayWarningMessage(target)
+          return original_ctor(*args, **kwargs)
+
+      target.__init__ = new_ctor
+      return target
+    else:
+      raise TypeError('@Deprecated is only applicable to functions or classes')
+
+
+def Disabled(*args):
+  """Decorator for disabling tests/benchmarks.
+
+
+  If args are given, the test will be disabled if ANY of the args match the
+  browser type, OS name or OS version:
+    @Disabled('canary')        # Disabled for canary browsers
+    @Disabled('win')           # Disabled on Windows.
+    @Disabled('win', 'linux')  # Disabled on both Windows and Linux.
+    @Disabled('mavericks')     # Disabled on Mac Mavericks (10.9) only.
+    @Disabled('all')  # Unconditionally disabled.
+  """
+  def _Disabled(func):
+    disabled_attr_name = DisabledAttributeName(func)
+    if not hasattr(func, disabled_attr_name):
+      setattr(func, disabled_attr_name, set())
+    disabled_set = getattr(func, disabled_attr_name)
+    disabled_set.update(disabled_strings)
+    setattr(func, disabled_attr_name, disabled_set)
+    return func
+  assert args, (
+      "@Disabled(...) requires arguments. Use @Disabled('all') if you want to "
+      'unconditionally disable the test.')
+  assert not callable(args[0]), 'Please use @Disabled(..).'
+  disabled_strings = list(args)
+  for disabled_string in disabled_strings:
+    # TODO(tonyg): Validate that these strings are recognized.
+    assert isinstance(disabled_string, str), '@Disabled accepts a list of strs'
+  return _Disabled
+
+
+def Enabled(*args):
+  """Decorator for enabling tests/benchmarks.
+
+  The test will be enabled if ANY of the args match the browser type, OS name
+  or OS version:
+    @Enabled('canary')        # Enabled only for canary browsers
+    @Enabled('win')           # Enabled only on Windows.
+    @Enabled('win', 'linux')  # Enabled only on Windows or Linux.
+    @Enabled('mavericks')     # Enabled only on Mac Mavericks (10.9).
+  """
+  def _Enabled(func):
+    enabled_attr_name = EnabledAttributeName(func)
+    if not hasattr(func, enabled_attr_name):
+      setattr(func, enabled_attr_name, set())
+    enabled_set = getattr(func, enabled_attr_name)
+    enabled_set.update(enabled_strings)
+    setattr(func, enabled_attr_name, enabled_set)
+    return func
+  assert args, '@Enabled(..) requires arguments'
+  assert not callable(args[0]), 'Please use @Enabled(..).'
+  enabled_strings = list(args)
+  for enabled_string in enabled_strings:
+    # TODO(tonyg): Validate that these strings are recognized.
+    assert isinstance(enabled_string, str), '@Enabled accepts a list of strs'
+  return _Enabled
+
+
+def Owner(emails=None, component=None):
+  """Decorator for specifying the owner of a benchmark."""
+  def _Owner(func):
+    owner_attr_name = OwnerAttributeName(func)
+    assert inspect.isclass(func), '@Owner(...) can only be used on classes'
+    if not hasattr(func, owner_attr_name):
+      setattr(func, owner_attr_name, {})
+    owner_dict = getattr(func, owner_attr_name)
+    if emails:
+      assert 'emails' not in owner_dict, 'emails can only be set once'
+      owner_dict['emails'] = emails
+    if component:
+      assert 'component' not in owner_dict, 'component can only be set once'
+      owner_dict['component'] = component
+    setattr(func, owner_attr_name, owner_dict)
+    return func
+  help_text = '@Owner(...) requires emails and/or a component'
+  assert emails or component, help_text
+  if emails:
+    assert isinstance(emails, list), 'emails must be a list of strs'
+    for e in emails:
+      assert isinstance(e, str), 'emails must be a list of strs'
+  return _Owner
+
+
+# TODO(dpranke): Remove if we don't need this.
+def Isolated(*args):
+  """Decorator for noting that tests must be run in isolation.
+
+  The test will be run by itself (not concurrently with any other tests)
+  if ANY of the args match the browser type, OS name, or OS version."""
+  def _Isolated(func):
+    if not isinstance(func, types.FunctionType):
+      func._isolated_strings = isolated_strings
+      return func
+    @functools.wraps(func)
+    def wrapper(*args, **kwargs):
+      func(*args, **kwargs)
+    wrapper._isolated_strings = isolated_strings
+    return wrapper
+  if len(args) == 1 and callable(args[0]):
+    isolated_strings = []
+    return _Isolated(args[0])
+  isolated_strings = list(args)
+  for isolated_string in isolated_strings:
+    # TODO(tonyg): Validate that these strings are recognized.
+    assert isinstance(isolated_string, str), 'Isolated accepts a list of strs'
+  return _Isolated
+
+
+# TODO(nednguyen): Remove this and have call site just use ShouldSkip directly.
+def IsEnabled(test, possible_browser):
+  """Returns True iff |test| is enabled given the |possible_browser|.
+
+  Use to respect the @Enabled / @Disabled decorators.
+
+  Args:
+    test: A function or class that may contain _disabled_strings and/or
+          _enabled_strings attributes.
+    possible_browser: A PossibleBrowser to check whether |test| may run against.
+  """
+  should_skip, msg = ShouldSkip(test, possible_browser)
+  return (not should_skip, msg)
+
+
+def IsBenchmarkEnabled(benchmark, possible_browser):
+  return (not benchmark.ShouldDisable(possible_browser) and
+          IsEnabled(benchmark, possible_browser)[0])
+
+
+def _TestName(test):
+  if inspect.ismethod(test):
+    # On methods, __name__ is "instancemethod", use __func__.__name__ instead.
+    test = test.__func__
+  if hasattr(test, '__name__'):
+    return test.__name__
+  elif hasattr(test, '__class__'):
+    return test.__class__.__name__
+  return str(test)
+
+
+def DisabledAttributeName(test):
+  name = _TestName(test)
+  return '_%s_%s_disabled_strings' % (test.__module__, name)
+
+
+def GetDisabledAttributes(test):
+  disabled_attr_name = DisabledAttributeName(test)
+  if not hasattr(test, disabled_attr_name):
+    return set()
+  return set(getattr(test, disabled_attr_name))
+
+
+def GetEnabledAttributes(test):
+  enabled_attr_name = EnabledAttributeName(test)
+  if not hasattr(test, enabled_attr_name):
+    return set()
+  enabled_strings = set(getattr(test, enabled_attr_name))
+  return enabled_strings
+
+
+def EnabledAttributeName(test):
+  name = _TestName(test)
+  return '_%s_%s_enabled_strings' % (test.__module__, name)
+
+
+def OwnerAttributeName(test):
+  name = _TestName(test)
+  return '_%s_%s_owner' % (test.__module__, name)
+
+
+def GetEmails(test):
+  owner_attr_name = OwnerAttributeName(test)
+  owner = getattr(test, owner_attr_name, {})
+  if 'emails' in owner:
+    return owner['emails']
+  return None
+
+
+def GetComponent(test):
+  owner_attr_name = OwnerAttributeName(test)
+  owner = getattr(test, owner_attr_name, {})
+  if 'component' in owner:
+    return owner['component']
+  return None
+
+
+def ShouldSkip(test, possible_browser):
+  """Returns whether the test should be skipped and the reason for it."""
+  platform_attributes = _PlatformAttributes(possible_browser)
+
+  name = _TestName(test)
+  skip = 'Skipping %s (%s) because' % (name, str(test))
+  running = 'You are running %r.' % platform_attributes
+
+  disabled_attr_name = DisabledAttributeName(test)
+  if hasattr(test, disabled_attr_name):
+    disabled_strings = getattr(test, disabled_attr_name)
+    if 'all' in disabled_strings:
+      return (True, '%s it is unconditionally disabled.' % skip)
+    if set(disabled_strings) & set(platform_attributes):
+      return (True, '%s it is disabled for %s. %s' %
+                      (skip, ' and '.join(disabled_strings), running))
+
+  enabled_attr_name = EnabledAttributeName(test)
+  if hasattr(test, enabled_attr_name):
+    enabled_strings = getattr(test, enabled_attr_name)
+    if 'all' in enabled_strings:
+      return False, None  # No arguments to @Enabled means always enable.
+    if not set(enabled_strings) & set(platform_attributes):
+      return (True, '%s it is only enabled for %s. %s' %
+                      (skip, ' or '.join(enabled_strings), running))
+
+  return False, None
+
+
+def ShouldBeIsolated(test, possible_browser):
+  platform_attributes = _PlatformAttributes(possible_browser)
+  if hasattr(test, '_isolated_strings'):
+    isolated_strings = test._isolated_strings
+    if not isolated_strings:
+      return True # No arguments to @Isolated means always isolate.
+    for isolated_string in isolated_strings:
+      if isolated_string in platform_attributes:
+        return True
+    return False
+  return False
+
+
+def _PlatformAttributes(possible_browser):
+  """Returns a list of platform attribute strings."""
+  attributes = [a.lower() for a in [
+      possible_browser.browser_type,
+      possible_browser.platform.GetOSName(),
+      possible_browser.platform.GetOSVersionName(),
+  ]]
+  if possible_browser.supports_tab_control:
+    attributes.append('has tabs')
+  if 'content-shell' in possible_browser.browser_type:
+    attributes.append('content-shell')
+  if possible_browser.browser_type == 'reference':
+    ref_attributes = []
+    for attribute in attributes:
+      if attribute != 'reference':
+        ref_attributes.append('%s-reference' % attribute)
+    attributes.extend(ref_attributes)
+  return attributes
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/decorators_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/decorators_unittest.py
new file mode 100644
index 0000000..1c02648
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/decorators_unittest.py
@@ -0,0 +1,528 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+import mock
+
+from telemetry.core import platform
+from telemetry import decorators
+from telemetry.internal.browser import possible_browser
+
+
+class FakeTest(object):
+
+  def SetEnabledStrings(self, enabled_strings):
+    enabled_attr_name = decorators.EnabledAttributeName(self)
+    setattr(self, enabled_attr_name, enabled_strings)
+
+  def SetDisabledStrings(self, disabled_strings):
+    # pylint: disable=attribute-defined-outside-init
+    disabled_attr_name = decorators.DisabledAttributeName(self)
+    setattr(self, disabled_attr_name, disabled_strings)
+
+
+class TestDisableDecorators(unittest.TestCase):
+
+  def testDisabledStringOnFunction(self):
+
+    @decorators.Disabled('bar')
+    def Sum():
+      return 1 + 1
+
+    self.assertEquals({'bar'}, decorators.GetDisabledAttributes(Sum))
+
+    @decorators.Disabled('bar')
+    @decorators.Disabled('baz')
+    @decorators.Disabled('bart', 'baz')
+    def Product():
+      return 1 * 1
+
+    self.assertEquals({'bar', 'bart', 'baz'},
+                      decorators.GetDisabledAttributes(Product))
+
+  def testDisabledStringOnClass(self):
+
+    @decorators.Disabled('windshield')
+    class Ford(object):
+      pass
+
+    self.assertEquals({'windshield'}, decorators.GetDisabledAttributes(Ford))
+
+    @decorators.Disabled('windows', 'Drive')
+    @decorators.Disabled('wheel')
+    @decorators.Disabled('windows')
+    class Honda(object):
+      pass
+
+    self.assertEquals({'windshield'}, decorators.GetDisabledAttributes(Ford))
+    self.assertEquals({'wheel', 'Drive', 'windows'},
+                      decorators.GetDisabledAttributes(Honda))
+
+  def testDisabledStringOnSubClass(self):
+
+    @decorators.Disabled('windshield')
+    class Car(object):
+      pass
+
+    class Ford(Car):
+      pass
+
+    self.assertEquals({'windshield'}, decorators.GetDisabledAttributes(Car))
+    self.assertFalse(decorators.GetDisabledAttributes(Ford))
+
+    @decorators.Disabled('windows', 'Drive')
+    @decorators.Disabled('wheel')
+    @decorators.Disabled('windows')
+    class Honda(Car):
+      pass
+
+    self.assertFalse(decorators.GetDisabledAttributes(Ford))
+    self.assertEquals({'windshield'}, decorators.GetDisabledAttributes(Car))
+    self.assertEquals({'wheel', 'Drive', 'windows'},
+                      decorators.GetDisabledAttributes(Honda))
+
+  def testDisabledStringOnMethod(self):
+
+    class Ford(object):
+
+      @decorators.Disabled('windshield')
+      def Drive(self):
+        pass
+
+    self.assertEquals({'windshield'},
+                      decorators.GetDisabledAttributes(Ford().Drive))
+
+    class Honda(object):
+
+      @decorators.Disabled('windows', 'Drive')
+      @decorators.Disabled('wheel')
+      @decorators.Disabled('windows')
+      def Drive(self):
+        pass
+
+    self.assertEquals({'wheel', 'Drive', 'windows'},
+                      decorators.GetDisabledAttributes(Honda().Drive))
+    self.assertEquals({'windshield'},
+                      decorators.GetDisabledAttributes(Ford().Drive))
+
+    class Accord(Honda):
+
+      def Drive(self):
+        pass
+
+    class Explorer(Ford):
+      pass
+
+    self.assertEquals({'wheel', 'Drive', 'windows'},
+                      decorators.GetDisabledAttributes(Honda().Drive))
+    self.assertEquals({'windshield'},
+                      decorators.GetDisabledAttributes(Ford().Drive))
+    self.assertEquals({'windshield'},
+                      decorators.GetDisabledAttributes(Explorer().Drive))
+    self.assertFalse(decorators.GetDisabledAttributes(Accord().Drive))
+
+
+class TestEnableDecorators(unittest.TestCase):
+
+  def testEnabledStringOnFunction(self):
+
+    @decorators.Enabled('minus', 'power')
+    def Sum():
+      return 1 + 1
+
+    self.assertEquals({'minus', 'power'}, decorators.GetEnabledAttributes(Sum))
+
+    @decorators.Enabled('dot')
+    @decorators.Enabled('product')
+    @decorators.Enabled('product', 'dot')
+    def Product():
+      return 1 * 1
+
+    self.assertEquals({'dot', 'product'},
+                      decorators.GetEnabledAttributes(Product))
+
+  def testEnabledStringOnClass(self):
+
+    @decorators.Enabled('windshield', 'light')
+    class Ford(object):
+      pass
+
+    self.assertEquals({'windshield', 'light'},
+                      decorators.GetEnabledAttributes(Ford))
+
+    @decorators.Enabled('wheel', 'Drive')
+    @decorators.Enabled('wheel')
+    @decorators.Enabled('windows')
+    class Honda(object):
+      pass
+
+    self.assertEquals({'wheel', 'Drive', 'windows'},
+                      decorators.GetEnabledAttributes(Honda))
+    self.assertEquals({'windshield', 'light'},
+                      decorators.GetEnabledAttributes(Ford))
+
+  def testEnabledStringOnSubClass(self):
+
+    @decorators.Enabled('windshield')
+    class Car(object):
+      pass
+
+    class Ford(Car):
+      pass
+
+    self.assertEquals({'windshield'}, decorators.GetEnabledAttributes(Car))
+    self.assertFalse(decorators.GetEnabledAttributes(Ford))
+
+    @decorators.Enabled('windows', 'Drive')
+    @decorators.Enabled('wheel')
+    @decorators.Enabled('windows')
+    class Honda(Car):
+      pass
+
+    self.assertFalse(decorators.GetEnabledAttributes(Ford))
+    self.assertEquals({'windshield'}, decorators.GetEnabledAttributes(Car))
+    self.assertEquals({'wheel', 'Drive', 'windows'},
+                      decorators.GetEnabledAttributes(Honda))
+
+  def testEnabledStringOnMethod(self):
+
+    class Ford(object):
+
+      @decorators.Enabled('windshield')
+      def Drive(self):
+        pass
+
+    self.assertEquals({'windshield'},
+                      decorators.GetEnabledAttributes(Ford().Drive))
+
+    class Honda(object):
+
+      @decorators.Enabled('windows', 'Drive')
+      @decorators.Enabled('wheel', 'Drive')
+      @decorators.Enabled('windows')
+      def Drive(self):
+        pass
+
+    self.assertEquals({'wheel', 'Drive', 'windows'},
+                      decorators.GetEnabledAttributes(Honda().Drive))
+
+    class Accord(Honda):
+
+      def Drive(self):
+        pass
+
+    class Explorer(Ford):
+      pass
+
+    self.assertEquals({'wheel', 'Drive', 'windows'},
+                      decorators.GetEnabledAttributes(Honda().Drive))
+    self.assertEquals({'windshield'},
+                      decorators.GetEnabledAttributes(Ford().Drive))
+    self.assertEquals({'windshield'},
+                      decorators.GetEnabledAttributes(Explorer().Drive))
+    self.assertFalse(decorators.GetEnabledAttributes(Accord().Drive))
+
+
+class TestOwnerDecorators(unittest.TestCase):
+
+  def testOwnerStringOnClass(self):
+
+    @decorators.Owner(emails=['owner@chromium.org'])
+    class Ford(object):
+      pass
+
+    self.assertEquals(['owner@chromium.org'], decorators.GetEmails(Ford))
+
+    @decorators.Owner(emails=['owner2@chromium.org'])
+    @decorators.Owner(component='component')
+    class Honda(object):
+      pass
+
+    self.assertEquals(['owner2@chromium.org'], decorators.GetEmails(Honda))
+    self.assertEquals('component', decorators.GetComponent(Honda))
+    self.assertEquals(['owner@chromium.org'], decorators.GetEmails(Ford))
+
+
+  def testOwnerStringOnSubClass(self):
+
+    @decorators.Owner(emails=['owner@chromium.org'], component='comp')
+    class Car(object):
+      pass
+
+    class Ford(Car):
+      pass
+
+    self.assertEquals(['owner@chromium.org'], decorators.GetEmails(Car))
+    self.assertEquals('comp', decorators.GetComponent(Car))
+    self.assertFalse(decorators.GetEmails(Ford))
+    self.assertFalse(decorators.GetComponent(Ford))
+
+
+  def testOwnerWithDuplicateAttributeSetting(self):
+
+    with self.assertRaises(AssertionError):
+      @decorators.Owner(emails=['owner2@chromium.org'])
+      @decorators.Owner(emails=['owner@chromium.org'], component='comp')
+      class Car(object):
+        pass
+
+      self.assertEquals(['owner@chromium.org'], decorators.GetEmails(Car))
+
+
+class TestShouldSkip(unittest.TestCase):
+
+  def setUp(self):
+    fake_platform = mock.Mock(spec_set=platform.Platform)
+    fake_platform.GetOSName.return_value = 'os_name'
+    fake_platform.GetOSVersionName.return_value = 'os_version_name'
+
+    self.possible_browser = mock.Mock(spec_set=possible_browser.PossibleBrowser)
+    self.possible_browser.browser_type = 'browser_type'
+    self.possible_browser.platform = fake_platform
+    self.possible_browser.supports_tab_control = False
+
+  def testEnabledStrings(self):
+    test = FakeTest()
+
+    # When no enabled_strings is given, everything should be enabled.
+    self.assertFalse(decorators.ShouldSkip(test, self.possible_browser)[0])
+
+    test.SetEnabledStrings(['os_name'])
+    self.assertFalse(decorators.ShouldSkip(test, self.possible_browser)[0])
+
+    test.SetEnabledStrings(['another_os_name'])
+    self.assertTrue(decorators.ShouldSkip(test, self.possible_browser)[0])
+
+    test.SetEnabledStrings(['os_version_name'])
+    self.assertFalse(decorators.ShouldSkip(test, self.possible_browser)[0])
+
+    test.SetEnabledStrings(['os_name', 'another_os_name'])
+    self.assertFalse(decorators.ShouldSkip(test, self.possible_browser)[0])
+
+    test.SetEnabledStrings(['another_os_name', 'os_name'])
+    self.assertFalse(decorators.ShouldSkip(test, self.possible_browser)[0])
+
+    test.SetEnabledStrings(['another_os_name', 'another_os_version_name'])
+    self.assertTrue(decorators.ShouldSkip(test, self.possible_browser)[0])
+
+    test.SetEnabledStrings(['os_name-reference'])
+    self.assertTrue(decorators.ShouldSkip(test, self.possible_browser)[0])
+
+    test.SetEnabledStrings(['another_os_name-reference'])
+    self.assertTrue(decorators.ShouldSkip(test, self.possible_browser)[0])
+
+    test.SetEnabledStrings(['os_version_name-reference'])
+    self.assertTrue(decorators.ShouldSkip(test, self.possible_browser)[0])
+
+    test.SetEnabledStrings(['os_name-reference', 'another_os_name-reference'])
+    self.assertTrue(decorators.ShouldSkip(test, self.possible_browser)[0])
+
+    test.SetEnabledStrings(['another_os_name-reference', 'os_name-reference'])
+    self.assertTrue(decorators.ShouldSkip(test, self.possible_browser)[0])
+
+    test.SetEnabledStrings(['another_os_name-reference',
+                            'another_os_version_name-reference'])
+    self.assertTrue(decorators.ShouldSkip(test, self.possible_browser)[0])
+
+  def testDisabledStrings(self):
+    test = FakeTest()
+
+    # When no disabled_strings is given, nothing should be disabled.
+    self.assertFalse(decorators.ShouldSkip(test, self.possible_browser)[0])
+
+    test.SetDisabledStrings(['os_name'])
+    self.assertTrue(decorators.ShouldSkip(test, self.possible_browser)[0])
+
+    test.SetDisabledStrings(['another_os_name'])
+    self.assertFalse(decorators.ShouldSkip(test, self.possible_browser)[0])
+
+    test.SetDisabledStrings(['os_version_name'])
+    self.assertTrue(decorators.ShouldSkip(test, self.possible_browser)[0])
+
+    test.SetDisabledStrings(['os_name', 'another_os_name'])
+    self.assertTrue(decorators.ShouldSkip(test, self.possible_browser)[0])
+
+    test.SetDisabledStrings(['another_os_name', 'os_name'])
+    self.assertTrue(decorators.ShouldSkip(test, self.possible_browser)[0])
+
+    test.SetDisabledStrings(['another_os_name', 'another_os_version_name'])
+    self.assertFalse(decorators.ShouldSkip(test, self.possible_browser)[0])
+
+    test.SetDisabledStrings(['reference'])
+    self.assertFalse(decorators.ShouldSkip(test, self.possible_browser)[0])
+
+    test.SetDisabledStrings(['os_name-reference'])
+    self.assertFalse(decorators.ShouldSkip(test, self.possible_browser)[0])
+
+    test.SetDisabledStrings(['another_os_name-reference'])
+    self.assertFalse(decorators.ShouldSkip(test, self.possible_browser)[0])
+
+    test.SetDisabledStrings(['os_version_name-reference'])
+    self.assertFalse(decorators.ShouldSkip(test, self.possible_browser)[0])
+
+    test.SetDisabledStrings(['os_name-reference', 'another_os_name-reference'])
+    self.assertFalse(decorators.ShouldSkip(test, self.possible_browser)[0])
+
+    test.SetDisabledStrings(['another_os_name-reference', 'os_name-reference'])
+    self.assertFalse(decorators.ShouldSkip(test, self.possible_browser)[0])
+
+    test.SetDisabledStrings(['another_os_name-reference',
+                             'another_os_version_name-reference'])
+    self.assertFalse(decorators.ShouldSkip(test, self.possible_browser)[0])
+
+  def testReferenceEnabledStrings(self):
+    self.possible_browser.browser_type = 'reference'
+    test = FakeTest()
+
+    # When no enabled_strings is given, everything should be enabled.
+    self.assertFalse(decorators.ShouldSkip(test, self.possible_browser)[0])
+
+    test.SetEnabledStrings(['os_name-reference'])
+    self.assertFalse(decorators.ShouldSkip(test, self.possible_browser)[0])
+
+    test.SetEnabledStrings(['another_os_name-reference'])
+    self.assertTrue(decorators.ShouldSkip(test, self.possible_browser)[0])
+
+    test.SetEnabledStrings(['os_version_name-reference'])
+    self.assertFalse(decorators.ShouldSkip(test, self.possible_browser)[0])
+
+    test.SetEnabledStrings(['os_name-reference', 'another_os_name-reference'])
+    self.assertFalse(decorators.ShouldSkip(test, self.possible_browser)[0])
+
+    test.SetEnabledStrings(['another_os_name-reference', 'os_name-reference'])
+    self.assertFalse(decorators.ShouldSkip(test, self.possible_browser)[0])
+
+    test.SetEnabledStrings(['another_os_name-reference',
+                            'another_os_version_name-reference'])
+    self.assertTrue(decorators.ShouldSkip(test, self.possible_browser)[0])
+
+  def testReferenceDisabledStrings(self):
+    self.possible_browser.browser_type = 'reference'
+    test = FakeTest()
+
+    # When no disabled_strings is given, nothing should be disabled.
+    self.assertFalse(decorators.ShouldSkip(test, self.possible_browser)[0])
+
+    test.SetDisabledStrings(['reference'])
+    self.assertTrue(decorators.ShouldSkip(test, self.possible_browser)[0])
+
+    test.SetDisabledStrings(['os_name-reference'])
+    self.assertTrue(decorators.ShouldSkip(test, self.possible_browser)[0])
+
+    test.SetDisabledStrings(['another_os_name-reference'])
+    self.assertFalse(decorators.ShouldSkip(test, self.possible_browser)[0])
+
+    test.SetDisabledStrings(['os_version_name-reference'])
+    self.assertTrue(decorators.ShouldSkip(test, self.possible_browser)[0])
+
+    test.SetDisabledStrings(['os_name-reference', 'another_os_name-reference'])
+    self.assertTrue(decorators.ShouldSkip(test, self.possible_browser)[0])
+
+    test.SetDisabledStrings(['another_os_name-reference', 'os_name-reference'])
+    self.assertTrue(decorators.ShouldSkip(test, self.possible_browser)[0])
+
+    test.SetDisabledStrings(['another_os_name-reference',
+                             'another_os_version_name-reference'])
+    self.assertFalse(decorators.ShouldSkip(test, self.possible_browser)[0])
+
+
+class TestDeprecation(unittest.TestCase):
+
+  @mock.patch('warnings.warn')
+  def testFunctionDeprecation(self, warn_mock):
+
+    @decorators.Deprecated(2015, 12, 1)
+    def Foo(x):
+      return x
+
+    Foo(1)
+    warn_mock.assert_called_with(
+        'Function Foo is deprecated. It will no longer be supported on '
+        'December 01, 2015. Please remove it or switch to an alternative '
+        'before that time. \n',
+        stacklevel=4)
+
+  @mock.patch('warnings.warn')
+  def testMethodDeprecated(self, warn_mock):
+
+    class Bar(object):
+
+      @decorators.Deprecated(2015, 12, 1, 'Testing only.')
+      def Foo(self, x):
+        return x
+
+    Bar().Foo(1)
+    warn_mock.assert_called_with(
+        'Function Foo is deprecated. It will no longer be supported on '
+        'December 01, 2015. Please remove it or switch to an alternative '
+        'before that time. Testing only.\n',
+        stacklevel=4)
+
+  @mock.patch('warnings.warn')
+  def testClassWithoutInitDefinedDeprecated(self, warn_mock):
+
+    @decorators.Deprecated(2015, 12, 1)
+    class Bar(object):
+
+      def Foo(self, x):
+        return x
+
+    Bar().Foo(1)
+    warn_mock.assert_called_with(
+        'Class Bar is deprecated. It will no longer be supported on '
+        'December 01, 2015. Please remove it or switch to an alternative '
+        'before that time. \n',
+        stacklevel=4)
+
+  @mock.patch('warnings.warn')
+  def testClassWithInitDefinedDeprecated(self, warn_mock):
+
+    @decorators.Deprecated(2015, 12, 1)
+    class Bar(object):
+
+      def __init__(self):
+        pass
+
+      def Foo(self, x):
+        return x
+
+    Bar().Foo(1)
+    warn_mock.assert_called_with(
+        'Class Bar is deprecated. It will no longer be supported on '
+        'December 01, 2015. Please remove it or switch to an alternative '
+        'before that time. \n',
+        stacklevel=4)
+
+  @mock.patch('warnings.warn')
+  def testInheritedClassDeprecated(self, warn_mock):
+
+    class Ba(object):
+      pass
+
+    @decorators.Deprecated(2015, 12, 1)
+    class Bar(Ba):
+
+      def Foo(self, x):
+        return x
+
+    class Baz(Bar):
+      pass
+
+    Baz().Foo(1)
+    warn_mock.assert_called_with(
+        'Class Bar is deprecated. It will no longer be supported on '
+        'December 01, 2015. Please remove it or switch to an alternative '
+        'before that time. \n',
+        stacklevel=4)
+
+  def testReturnValue(self):
+
+    class Bar(object):
+
+      @decorators.Deprecated(2015, 12, 1, 'Testing only.')
+      def Foo(self, x):
+        return x
+
+    self.assertEquals(5, Bar().Foo(5))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/__init__.py
new file mode 100644
index 0000000..50b23df
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/__init__.py
@@ -0,0 +1,3 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/__init__.py
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/action_runner.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/action_runner.py
new file mode 100644
index 0000000..ec2a5ea
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/action_runner.py
@@ -0,0 +1,861 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+import time
+import urlparse
+
+from telemetry.core import exceptions
+from telemetry.internal.actions.drag import DragAction
+from telemetry.internal.actions.javascript_click import ClickElementAction
+from telemetry.internal.actions.key_event import KeyPressAction
+from telemetry.internal.actions.load_media import LoadMediaAction
+from telemetry.internal.actions.loop import LoopAction
+from telemetry.internal.actions.mouse_click import MouseClickAction
+from telemetry.internal.actions.navigate import NavigateAction
+from telemetry.internal.actions.page_action import GESTURE_SOURCE_DEFAULT
+from telemetry.internal.actions.page_action import SUPPORTED_GESTURE_SOURCES
+from telemetry.internal.actions.pinch import PinchAction
+from telemetry.internal.actions.play import PlayAction
+from telemetry.internal.actions.repaint_continuously import (
+    RepaintContinuouslyAction)
+from telemetry.internal.actions.repeatable_scroll import RepeatableScrollAction
+from telemetry.internal.actions.scroll import ScrollAction
+from telemetry.internal.actions.scroll_bounce import ScrollBounceAction
+from telemetry.internal.actions.scroll_to_element import ScrollToElementAction
+from telemetry.internal.actions.seek import SeekAction
+from telemetry.internal.actions.swipe import SwipeAction
+from telemetry.internal.actions.tap import TapAction
+from telemetry.internal.actions.wait import WaitForElementAction
+from telemetry.web_perf import timeline_interaction_record
+
+from py_trace_event import trace_event
+
+import py_utils
+
+
+_DUMP_WAIT_TIME = 3
+
+
+class ActionRunner(object):
+
+  __metaclass__ = trace_event.TracedMetaClass
+
+  def __init__(self, tab, skip_waits=False):
+    self._tab = tab
+    self._skip_waits = skip_waits
+
+  @property
+  def tab(self):
+    """Returns the tab on which actions are performed."""
+    return self._tab
+
+  def _RunAction(self, action):
+    action.WillRunAction(self._tab)
+    action.RunAction(self._tab)
+
+  def CreateInteraction(self, label, repeatable=False):
+    """ Create an action.Interaction object that issues interaction record.
+
+    An interaction record is a labeled time period containing
+    interaction that developers care about. Each set of metrics
+    specified in flags will be calculated for this time period.
+
+    To mark the start of interaction record, call Begin() method on the returned
+    object. To mark the finish of interaction record, call End() method on
+    it. Or better yet, use the with statement to create an
+    interaction record that covers the actions in the with block.
+
+    e.g:
+      with action_runner.CreateInteraction('Animation-1'):
+        action_runner.TapElement(...)
+        action_runner.WaitForJavaScriptCondition(...)
+
+    Args:
+      label: A label for this particular interaction. This can be any
+          user-defined string, but must not contain '/'.
+      repeatable: Whether other interactions may use the same logical name
+          as this interaction. All interactions with the same logical name must
+          have the same flags.
+
+    Returns:
+      An instance of action_runner.Interaction
+    """
+    flags = []
+    if repeatable:
+      flags.append(timeline_interaction_record.REPEATABLE)
+
+    return Interaction(self, label, flags)
+
+  def CreateGestureInteraction(self, label, repeatable=False):
+    """ Create an action.Interaction object that issues gesture-based
+    interaction record.
+
+    This is similar to normal interaction record, but it will
+    auto-narrow the interaction time period to only include the
+    synthetic gesture event output by Chrome. This is typically use to
+    reduce noise in gesture-based analysis (e.g., analysis for a
+    swipe/scroll).
+
+    The interaction record label will be prepended with 'Gesture_'.
+
+    e.g:
+      with action_runner.CreateGestureInteraction('Scroll-1'):
+        action_runner.ScrollPage()
+
+    Args:
+      label: A label for this particular interaction. This can be any
+          user-defined string, but must not contain '/'.
+      repeatable: Whether other interactions may use the same logical name
+          as this interaction. All interactions with the same logical name must
+          have the same flags.
+
+    Returns:
+      An instance of action_runner.Interaction
+    """
+    return self.CreateInteraction('Gesture_' + label, repeatable)
+
+  def WaitForNetworkQuiescence(self, timeout_in_seconds=10):
+    """ Wait for network quiesence on the page.
+    Args:
+      timeout_in_seconds: maximum amount of time (seconds) to wait for network
+        quiesence unil raising exception.
+
+    Raises:
+      py_utils.TimeoutException when the timeout is reached but the page's
+        network is not quiet.
+    """
+
+    py_utils.WaitFor(self.tab.HasReachedQuiescence, timeout_in_seconds)
+
+  def MeasureMemory(self, deterministic_mode=False):
+    """Add a memory measurement to the trace being recorded.
+
+    Behaves as a no-op if tracing is not enabled.
+
+    TODO(perezju): Also behave as a no-op if tracing is enabled but
+    memory-infra is not.
+
+    Args:
+      deterministic_mode: A boolean indicating whether to attempt or not to
+          control the environment (force GCs, clear caches) before making the
+          measurement in an attempt to obtain more deterministic results.
+
+    Returns:
+      GUID of the generated dump if one was triggered, None otherwise.
+    """
+    platform = self.tab.browser.platform
+    if not platform.tracing_controller.is_tracing_running:
+      logging.warning('Tracing is off. No memory dumps are being recorded.')
+      return None
+    if deterministic_mode:
+      self.Wait(_DUMP_WAIT_TIME)
+      self.ForceGarbageCollection()
+      if platform.SupportFlushEntireSystemCache():
+        platform.FlushEntireSystemCache()
+      self.Wait(_DUMP_WAIT_TIME)
+    dump_id = self.tab.browser.DumpMemory()
+    if not dump_id:
+      raise exceptions.Error('Unable to obtain memory dump')
+    return dump_id
+
+  def Navigate(self, url, script_to_evaluate_on_commit=None,
+               timeout_in_seconds=60):
+    """Navigates to |url|.
+
+    If |script_to_evaluate_on_commit| is given, the script source string will be
+    evaluated when the navigation is committed. This is after the context of
+    the page exists, but before any script on the page itself has executed.
+    """
+    if urlparse.urlparse(url).scheme == 'file':
+      url = self._tab.browser.platform.http_server.UrlOf(url[7:])
+
+    self._RunAction(NavigateAction(
+        url=url,
+        script_to_evaluate_on_commit=script_to_evaluate_on_commit,
+        timeout_in_seconds=timeout_in_seconds))
+
+  def NavigateBack(self):
+    """ Navigate back to the previous page."""
+    self.ExecuteJavaScript('window.history.back()')
+
+  def WaitForNavigate(self, timeout_in_seconds_seconds=60):
+    start_time = time.time()
+    self._tab.WaitForNavigate(timeout_in_seconds_seconds)
+
+    time_left_in_seconds = (start_time + timeout_in_seconds_seconds
+                            - time.time())
+    time_left_in_seconds = max(0, time_left_in_seconds)
+    self._tab.WaitForDocumentReadyStateToBeInteractiveOrBetter(
+        time_left_in_seconds)
+
+  def ReloadPage(self):
+    """Reloads the page."""
+    self._tab.ExecuteJavaScript('window.location.reload()')
+    self._tab.WaitForDocumentReadyStateToBeInteractiveOrBetter()
+
+  def ExecuteJavaScript(self, *args, **kwargs):
+    """Executes a given JavaScript statement. Does not return the result.
+
+    Example: runner.ExecuteJavaScript('var foo = {{ value }};', value='hi');
+
+    Args:
+      statement: The statement to execute (provided as a string).
+
+    Optional keyword args:
+      timeout: The number of seconds to wait for the statement to execute.
+      Additional keyword arguments provide values to be interpolated within
+          the statement. See telemetry.util.js_template for details.
+
+    Raises:
+      EvaluationException: The statement failed to execute.
+    """
+    return self._tab.ExecuteJavaScript(*args, **kwargs)
+
+  def EvaluateJavaScript(self, *args, **kwargs):
+    """Returns the result of evaluating a given JavaScript expression.
+
+    The evaluation results must be convertible to JSON. If the result
+    is not needed, use ExecuteJavaScript instead.
+
+    Example: runner.ExecuteJavaScript('document.location.href');
+
+    Args:
+      expression: The expression to execute (provided as a string).
+
+    Optional keyword args:
+      timeout: The number of seconds to wait for the expression to evaluate.
+      Additional keyword arguments provide values to be interpolated within
+          the expression. See telemetry.util.js_template for details.
+
+    Raises:
+      EvaluationException: The statement expression failed to execute
+          or the evaluation result can not be JSON-ized.
+    """
+    return self._tab.EvaluateJavaScript(*args, **kwargs)
+
+  def WaitForJavaScriptCondition(self, *args, **kwargs):
+    """Wait for a JavaScript condition to become true.
+
+    Example: runner.WaitForJavaScriptCondition('window.foo == 10');
+
+    Args:
+      condition: The JavaScript condition (provided as string).
+
+    Optional keyword args:
+      timeout: The number in seconds to wait for the condition to become
+          True (default to 60).
+      Additional keyword arguments provide values to be interpolated within
+          the expression. See telemetry.util.js_template for details.
+    """
+    return self._tab.WaitForJavaScriptCondition(*args, **kwargs)
+
+  def Wait(self, seconds):
+    """Wait for the number of seconds specified.
+
+    Args:
+      seconds: The number of seconds to wait.
+    """
+    if not self._skip_waits:
+      time.sleep(seconds)
+
+  def WaitForElement(self, selector=None, text=None, element_function=None,
+                     timeout_in_seconds=60):
+    """Wait for an element to appear in the document.
+
+    The element may be selected via selector, text, or element_function.
+    Only one of these arguments must be specified.
+
+    Args:
+      selector: A CSS selector describing the element.
+      text: The element must contains this exact text.
+      element_function: A JavaScript function (as string) that is used
+          to retrieve the element. For example:
+          '(function() { return foo.element; })()'.
+      timeout_in_seconds: The timeout in seconds (default to 60).
+    """
+    self._RunAction(WaitForElementAction(
+        selector=selector, text=text, element_function=element_function,
+        timeout_in_seconds=timeout_in_seconds))
+
+  def TapElement(self, selector=None, text=None, element_function=None):
+    """Tap an element.
+
+    The element may be selected via selector, text, or element_function.
+    Only one of these arguments must be specified.
+
+    Args:
+      selector: A CSS selector describing the element.
+      text: The element must contains this exact text.
+      element_function: A JavaScript function (as string) that is used
+          to retrieve the element. For example:
+          '(function() { return foo.element; })()'.
+    """
+    self._RunAction(TapAction(
+        selector=selector, text=text, element_function=element_function))
+
+  def ClickElement(self, selector=None, text=None, element_function=None):
+    """Click an element.
+
+    The element may be selected via selector, text, or element_function.
+    Only one of these arguments must be specified.
+
+    Args:
+      selector: A CSS selector describing the element.
+      text: The element must contains this exact text.
+      element_function: A JavaScript function (as string) that is used
+          to retrieve the element. For example:
+          '(function() { return foo.element; })()'.
+    """
+    self._RunAction(ClickElementAction(
+        selector=selector, text=text, element_function=element_function))
+
+  def DragPage(self, left_start_ratio, top_start_ratio, left_end_ratio,
+               top_end_ratio, speed_in_pixels_per_second=800, use_touch=False,
+               selector=None, text=None, element_function=None):
+    """Perform a drag gesture on the page.
+
+    You should specify a start and an end point in ratios of page width and
+    height (see drag.js for full implementation).
+
+    Args:
+      left_start_ratio: The horizontal starting coordinate of the
+          gesture, as a ratio of the visible bounding rectangle for
+          document.body.
+      top_start_ratio: The vertical starting coordinate of the
+          gesture, as a ratio of the visible bounding rectangle for
+          document.body.
+      left_end_ratio: The horizontal ending coordinate of the
+          gesture, as a ratio of the visible bounding rectangle for
+          document.body.
+      top_end_ratio: The vertical ending coordinate of the
+          gesture, as a ratio of the visible bounding rectangle for
+          document.body.
+      speed_in_pixels_per_second: The speed of the gesture (in pixels/s).
+      use_touch: Whether dragging should be done with touch input.
+    """
+    self._RunAction(DragAction(
+        left_start_ratio=left_start_ratio, top_start_ratio=top_start_ratio,
+        left_end_ratio=left_end_ratio, top_end_ratio=top_end_ratio,
+        speed_in_pixels_per_second=speed_in_pixels_per_second,
+        use_touch=use_touch, selector=selector, text=text,
+        element_function=element_function))
+
+  def PinchPage(self, left_anchor_ratio=0.5, top_anchor_ratio=0.5,
+                scale_factor=None, speed_in_pixels_per_second=800):
+    """Perform the pinch gesture on the page.
+
+    It computes the pinch gesture automatically based on the anchor
+    coordinate and the scale factor. The scale factor is the ratio of
+    of the final span and the initial span of the gesture.
+
+    Args:
+      left_anchor_ratio: The horizontal pinch anchor coordinate of the
+          gesture, as a ratio of the visible bounding rectangle for
+          document.body.
+      top_anchor_ratio: The vertical pinch anchor coordinate of the
+          gesture, as a ratio of the visible bounding rectangle for
+          document.body.
+      scale_factor: The ratio of the final span to the initial span.
+          The default scale factor is
+          3.0 / (window.outerWidth/window.innerWidth).
+      speed_in_pixels_per_second: The speed of the gesture (in pixels/s).
+    """
+    self._RunAction(PinchAction(
+        left_anchor_ratio=left_anchor_ratio, top_anchor_ratio=top_anchor_ratio,
+        scale_factor=scale_factor,
+        speed_in_pixels_per_second=speed_in_pixels_per_second))
+
+  def PinchElement(self, selector=None, text=None, element_function=None,
+                   left_anchor_ratio=0.5, top_anchor_ratio=0.5,
+                   scale_factor=None, speed_in_pixels_per_second=800):
+    """Perform the pinch gesture on an element.
+
+    It computes the pinch gesture automatically based on the anchor
+    coordinate and the scale factor. The scale factor is the ratio of
+    of the final span and the initial span of the gesture.
+
+    Args:
+      selector: A CSS selector describing the element.
+      text: The element must contains this exact text.
+      element_function: A JavaScript function (as string) that is used
+          to retrieve the element. For example:
+          'function() { return foo.element; }'.
+      left_anchor_ratio: The horizontal pinch anchor coordinate of the
+          gesture, as a ratio of the visible bounding rectangle for
+          the element.
+      top_anchor_ratio: The vertical pinch anchor coordinate of the
+          gesture, as a ratio of the visible bounding rectangle for
+          the element.
+      scale_factor: The ratio of the final span to the initial span.
+          The default scale factor is
+          3.0 / (window.outerWidth/window.innerWidth).
+      speed_in_pixels_per_second: The speed of the gesture (in pixels/s).
+    """
+    self._RunAction(PinchAction(
+        selector=selector, text=text, element_function=element_function,
+        left_anchor_ratio=left_anchor_ratio, top_anchor_ratio=top_anchor_ratio,
+        scale_factor=scale_factor,
+        speed_in_pixels_per_second=speed_in_pixels_per_second))
+
+  def ScrollPage(self, left_start_ratio=0.5, top_start_ratio=0.5,
+                 direction='down', distance=None, distance_expr=None,
+                 speed_in_pixels_per_second=800, use_touch=False,
+                 synthetic_gesture_source=GESTURE_SOURCE_DEFAULT):
+    """Perform scroll gesture on the page.
+
+    You may specify distance or distance_expr, but not both. If
+    neither is specified, the default scroll distance is variable
+    depending on direction (see scroll.js for full implementation).
+
+    Args:
+      left_start_ratio: The horizontal starting coordinate of the
+          gesture, as a ratio of the visible bounding rectangle for
+          document.body.
+      top_start_ratio: The vertical starting coordinate of the
+          gesture, as a ratio of the visible bounding rectangle for
+          document.body.
+      direction: The direction of scroll, either 'left', 'right',
+          'up', 'down', 'upleft', 'upright', 'downleft', or 'downright'
+      distance: The distance to scroll (in pixel).
+      distance_expr: A JavaScript expression (as string) that can be
+          evaluated to compute scroll distance. Example:
+          'window.scrollTop' or '(function() { return crazyMath(); })()'.
+      speed_in_pixels_per_second: The speed of the gesture (in pixels/s).
+      use_touch: Whether scrolling should be done with touch input.
+      synthetic_gesture_source: the source input device type for the
+          synthetic gesture: 'DEFAULT', 'TOUCH' or 'MOUSE'.
+    """
+    assert synthetic_gesture_source in SUPPORTED_GESTURE_SOURCES
+    self._RunAction(ScrollAction(
+        left_start_ratio=left_start_ratio, top_start_ratio=top_start_ratio,
+        direction=direction, distance=distance, distance_expr=distance_expr,
+        speed_in_pixels_per_second=speed_in_pixels_per_second,
+        use_touch=use_touch, synthetic_gesture_source=synthetic_gesture_source))
+
+  def ScrollPageToElement(self, selector=None, element_function=None,
+                          container_selector=None,
+                          container_element_function=None,
+                          speed_in_pixels_per_second=800):
+    """Perform scroll gesture on container until an element is in view.
+
+    Both the element and the container can be specified by a CSS selector
+    xor a JavaScript function, provided as a string, which returns an element.
+    The element is required so exactly one of selector and element_function
+    must be provided. The container is optional so at most one of
+    container_selector and container_element_function can be provided.
+    The container defaults to document.scrollingElement or document.body if
+    scrollingElement is not set.
+
+    Args:
+      selector: A CSS selector describing the element.
+      element_function: A JavaScript function (as string) that is used
+          to retrieve the element. For example:
+          'function() { return foo.element; }'.
+      container_selector: A CSS selector describing the container element.
+      container_element_function: A JavaScript function (as a string) that is
+          used to retrieve the container element.
+      speed_in_pixels_per_second: Speed to scroll.
+    """
+    self._RunAction(ScrollToElementAction(
+        selector=selector, element_function=element_function,
+        container_selector=container_selector,
+        container_element_function=container_element_function,
+        speed_in_pixels_per_second=speed_in_pixels_per_second))
+
+  def RepeatableBrowserDrivenScroll(self, x_scroll_distance_ratio=0.0,
+                                    y_scroll_distance_ratio=0.5,
+                                    repeat_count=0,
+                                    repeat_delay_ms=250,
+                                    timeout=60,
+                                    prevent_fling=None,
+                                    speed=None):
+    """Perform a browser driven repeatable scroll gesture.
+
+    The scroll gesture is driven from the browser, this is useful because the
+    main thread often isn't resposive but the browser process usually is, so the
+    delay between the scroll gestures should be consistent.
+
+    Args:
+      x_scroll_distance_ratio: The horizontal length of the scroll as a fraction
+          of the screen width.
+      y_scroll_distance_ratio: The vertical length of the scroll as a fraction
+          of the screen height.
+      repeat_count: The number of additional times to repeat the gesture.
+      repeat_delay_ms: The delay in milliseconds between each scroll gesture.
+      prevent_fling: Prevents a fling gesture.
+      speed: Swipe speed in pixels per second.
+    """
+    self._RunAction(RepeatableScrollAction(
+        x_scroll_distance_ratio=x_scroll_distance_ratio,
+        y_scroll_distance_ratio=y_scroll_distance_ratio,
+        repeat_count=repeat_count,
+        repeat_delay_ms=repeat_delay_ms, timeout=timeout,
+        prevent_fling=prevent_fling, speed=speed))
+
+  def ScrollElement(self, selector=None, text=None, element_function=None,
+                    left_start_ratio=0.5, top_start_ratio=0.5,
+                    direction='down', distance=None, distance_expr=None,
+                    speed_in_pixels_per_second=800, use_touch=False,
+                    synthetic_gesture_source=GESTURE_SOURCE_DEFAULT):
+    """Perform scroll gesture on the element.
+
+    The element may be selected via selector, text, or element_function.
+    Only one of these arguments must be specified.
+
+    You may specify distance or distance_expr, but not both. If
+    neither is specified, the default scroll distance is variable
+    depending on direction (see scroll.js for full implementation).
+
+    Args:
+      selector: A CSS selector describing the element.
+      text: The element must contains this exact text.
+      element_function: A JavaScript function (as string) that is used
+          to retrieve the element. For example:
+          'function() { return foo.element; }'.
+      left_start_ratio: The horizontal starting coordinate of the
+          gesture, as a ratio of the visible bounding rectangle for
+          the element.
+      top_start_ratio: The vertical starting coordinate of the
+          gesture, as a ratio of the visible bounding rectangle for
+          the element.
+      direction: The direction of scroll, either 'left', 'right',
+          'up', 'down', 'upleft', 'upright', 'downleft', or 'downright'
+      distance: The distance to scroll (in pixel).
+      distance_expr: A JavaScript expression (as string) that can be
+          evaluated to compute scroll distance. Example:
+          'window.scrollTop' or '(function() { return crazyMath(); })()'.
+      speed_in_pixels_per_second: The speed of the gesture (in pixels/s).
+      use_touch: Whether scrolling should be done with touch input.
+      synthetic_gesture_source: the source input device type for the
+          synthetic gesture: 'DEFAULT', 'TOUCH' or 'MOUSE'.
+    """
+    assert synthetic_gesture_source in SUPPORTED_GESTURE_SOURCES
+    self._RunAction(ScrollAction(
+        selector=selector, text=text, element_function=element_function,
+        left_start_ratio=left_start_ratio, top_start_ratio=top_start_ratio,
+        direction=direction, distance=distance, distance_expr=distance_expr,
+        speed_in_pixels_per_second=speed_in_pixels_per_second,
+        use_touch=use_touch, synthetic_gesture_source=synthetic_gesture_source))
+
+  def ScrollBouncePage(self, left_start_ratio=0.5, top_start_ratio=0.5,
+                       direction='down', distance=100,
+                       overscroll=10, repeat_count=10,
+                       speed_in_pixels_per_second=400):
+    """Perform scroll bounce gesture on the page.
+
+    This gesture scrolls the page by the number of pixels specified in
+    distance, in the given direction, followed by a scroll by
+    (distance + overscroll) pixels in the opposite direction.
+    The above gesture is repeated repeat_count times.
+
+    Args:
+      left_start_ratio: The horizontal starting coordinate of the
+          gesture, as a ratio of the visible bounding rectangle for
+          document.body.
+      top_start_ratio: The vertical starting coordinate of the
+          gesture, as a ratio of the visible bounding rectangle for
+          document.body.
+      direction: The direction of scroll, either 'left', 'right',
+          'up', 'down', 'upleft', 'upright', 'downleft', or 'downright'
+      distance: The distance to scroll (in pixel).
+      overscroll: The number of additional pixels to scroll back, in
+          addition to the givendistance.
+      repeat_count: How often we want to repeat the full gesture.
+      speed_in_pixels_per_second: The speed of the gesture (in pixels/s).
+    """
+    self._RunAction(ScrollBounceAction(
+        left_start_ratio=left_start_ratio, top_start_ratio=top_start_ratio,
+        direction=direction, distance=distance,
+        overscroll=overscroll, repeat_count=repeat_count,
+        speed_in_pixels_per_second=speed_in_pixels_per_second))
+
+  def ScrollBounceElement(
+      self, selector=None, text=None, element_function=None,
+      left_start_ratio=0.5, top_start_ratio=0.5,
+      direction='down', distance=100,
+      overscroll=10, repeat_count=10,
+      speed_in_pixels_per_second=400):
+    """Perform scroll bounce gesture on the element.
+
+    This gesture scrolls on the element by the number of pixels specified in
+    distance, in the given direction, followed by a scroll by
+    (distance + overscroll) pixels in the opposite direction.
+    The above gesture is repeated repeat_count times.
+
+    Args:
+      selector: A CSS selector describing the element.
+      text: The element must contains this exact text.
+      element_function: A JavaScript function (as string) that is used
+          to retrieve the element. For example:
+          'function() { return foo.element; }'.
+      left_start_ratio: The horizontal starting coordinate of the
+          gesture, as a ratio of the visible bounding rectangle for
+          document.body.
+      top_start_ratio: The vertical starting coordinate of the
+          gesture, as a ratio of the visible bounding rectangle for
+          document.body.
+      direction: The direction of scroll, either 'left', 'right',
+          'up', 'down', 'upleft', 'upright', 'downleft', or 'downright'
+      distance: The distance to scroll (in pixel).
+      overscroll: The number of additional pixels to scroll back, in
+          addition to the given distance.
+      repeat_count: How often we want to repeat the full gesture.
+      speed_in_pixels_per_second: The speed of the gesture (in pixels/s).
+    """
+    self._RunAction(ScrollBounceAction(
+        selector=selector, text=text, element_function=element_function,
+        left_start_ratio=left_start_ratio, top_start_ratio=top_start_ratio,
+        direction=direction, distance=distance,
+        overscroll=overscroll, repeat_count=repeat_count,
+        speed_in_pixels_per_second=speed_in_pixels_per_second))
+
+  def MouseClick(self, selector=None):
+    """Mouse click the given element.
+
+    Args:
+      selector: A CSS selector describing the element.
+    """
+    self._RunAction(MouseClickAction(selector=selector))
+
+  def SwipePage(self, left_start_ratio=0.5, top_start_ratio=0.5,
+                direction='left', distance=100, speed_in_pixels_per_second=800):
+    """Perform swipe gesture on the page.
+
+    Args:
+      left_start_ratio: The horizontal starting coordinate of the
+          gesture, as a ratio of the visible bounding rectangle for
+          document.body.
+      top_start_ratio: The vertical starting coordinate of the
+          gesture, as a ratio of the visible bounding rectangle for
+          document.body.
+      direction: The direction of swipe, either 'left', 'right',
+          'up', or 'down'
+      distance: The distance to swipe (in pixel).
+      speed_in_pixels_per_second: The speed of the gesture (in pixels/s).
+    """
+    self._RunAction(SwipeAction(
+        left_start_ratio=left_start_ratio, top_start_ratio=top_start_ratio,
+        direction=direction, distance=distance,
+        speed_in_pixels_per_second=speed_in_pixels_per_second))
+
+  def SwipeElement(self, selector=None, text=None, element_function=None,
+                   left_start_ratio=0.5, top_start_ratio=0.5,
+                   direction='left', distance=100,
+                   speed_in_pixels_per_second=800):
+    """Perform swipe gesture on the element.
+
+    The element may be selected via selector, text, or element_function.
+    Only one of these arguments must be specified.
+
+    Args:
+      selector: A CSS selector describing the element.
+      text: The element must contains this exact text.
+      element_function: A JavaScript function (as string) that is used
+          to retrieve the element. For example:
+          'function() { return foo.element; }'.
+      left_start_ratio: The horizontal starting coordinate of the
+          gesture, as a ratio of the visible bounding rectangle for
+          the element.
+      top_start_ratio: The vertical starting coordinate of the
+          gesture, as a ratio of the visible bounding rectangle for
+          the element.
+      direction: The direction of swipe, either 'left', 'right',
+          'up', or 'down'
+      distance: The distance to swipe (in pixel).
+      speed_in_pixels_per_second: The speed of the gesture (in pixels/s).
+    """
+    self._RunAction(SwipeAction(
+        selector=selector, text=text, element_function=element_function,
+        left_start_ratio=left_start_ratio, top_start_ratio=top_start_ratio,
+        direction=direction, distance=distance,
+        speed_in_pixels_per_second=speed_in_pixels_per_second))
+
+  def PressKey(self, key, repeat_count=1, repeat_delay_ms=100, timeout=60):
+    """Perform a key press.
+
+    Args:
+      key: DOM value of the pressed key (e.g. 'PageDown', see
+          https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key).
+      repeat_count: How many times the key should be pressed.
+      repeat_delay_ms: Delay after each keypress (including the last one) in
+          milliseconds.
+    """
+    for _ in xrange(repeat_count):
+      self._RunAction(KeyPressAction(key, timeout=timeout))
+      self.Wait(repeat_delay_ms / 1000.0)
+
+  def EnterText(self, text, character_delay_ms=100, timeout=60):
+    """Enter text by performing key presses.
+
+    Args:
+      text: The text to enter.
+      character_delay_ms: Delay after each keypress (including the last one) in
+          milliseconds.
+    """
+    for c in text:
+      self.PressKey(c, repeat_delay_ms=character_delay_ms, timeout=timeout)
+
+  def LoadMedia(self, selector=None, event_timeout_in_seconds=0,
+                event_to_await='canplaythrough'):
+    """Invokes load() on media elements and awaits an event.
+
+    Args:
+      selector: A CSS selector describing the element. If none is
+          specified, play the first media element on the page. If the
+          selector matches more than 1 media element, all of them will
+          be played.
+      event_timeout_in_seconds: Maximum waiting time for the event to be fired.
+          0 means do not wait.
+      event_to_await: Which event to await. For example: 'canplaythrough' or
+          'loadedmetadata'.
+
+    Raises:
+      TimeoutException: If the maximum waiting time is exceeded.
+    """
+    self._RunAction(LoadMediaAction(
+        selector=selector, timeout_in_seconds=event_timeout_in_seconds,
+        event_to_await=event_to_await))
+
+  def PlayMedia(self, selector=None,
+                playing_event_timeout_in_seconds=0,
+                ended_event_timeout_in_seconds=0):
+    """Invokes the "play" action on media elements (such as video).
+
+    Args:
+      selector: A CSS selector describing the element. If none is
+          specified, play the first media element on the page. If the
+          selector matches more than 1 media element, all of them will
+          be played.
+      playing_event_timeout_in_seconds: Maximum waiting time for the "playing"
+          event (dispatched when the media begins to play) to be fired.
+          0 means do not wait.
+      ended_event_timeout_in_seconds: Maximum waiting time for the "ended"
+          event (dispatched when playback completes) to be fired.
+          0 means do not wait.
+
+    Raises:
+      TimeoutException: If the maximum waiting time is exceeded.
+    """
+    self._RunAction(PlayAction(
+        selector=selector,
+        playing_event_timeout_in_seconds=playing_event_timeout_in_seconds,
+        ended_event_timeout_in_seconds=ended_event_timeout_in_seconds))
+
+  def SeekMedia(self, seconds, selector=None, timeout_in_seconds=0,
+                log_time=True, label=''):
+    """Performs a seek action on media elements (such as video).
+
+    Args:
+      seconds: The media time to seek to.
+      selector: A CSS selector describing the element. If none is
+          specified, seek the first media element on the page. If the
+          selector matches more than 1 media element, all of them will
+          be seeked.
+      timeout_in_seconds: Maximum waiting time for the "seeked" event
+          (dispatched when the seeked operation completes) to be
+          fired.  0 means do not wait.
+      log_time: Whether to log the seek time for the perf
+          measurement. Useful when performing multiple seek.
+      label: A suffix string to name the seek perf measurement.
+
+    Raises:
+      TimeoutException: If the maximum waiting time is exceeded.
+    """
+    self._RunAction(SeekAction(
+        seconds=seconds, selector=selector,
+        timeout_in_seconds=timeout_in_seconds,
+        log_time=log_time, label=label))
+
+  def LoopMedia(self, loop_count, selector=None, timeout_in_seconds=None):
+    """Loops a media playback.
+
+    Args:
+      loop_count: The number of times to loop the playback.
+      selector: A CSS selector describing the element. If none is
+          specified, loop the first media element on the page. If the
+          selector matches more than 1 media element, all of them will
+          be looped.
+      timeout_in_seconds: Maximum waiting time for the looped playback to
+          complete. 0 means do not wait. None (the default) means to
+          wait loop_count * 60 seconds.
+
+    Raises:
+      TimeoutException: If the maximum waiting time is exceeded.
+    """
+    self._RunAction(LoopAction(
+        loop_count=loop_count, selector=selector,
+        timeout_in_seconds=timeout_in_seconds))
+
+  def ForceGarbageCollection(self):
+    """Forces JavaScript garbage collection on the page."""
+    self._tab.CollectGarbage()
+
+  def SimulateMemoryPressureNotification(self, pressure_level):
+    """Simulate memory pressure notification.
+
+    Args:
+      pressure_level: 'moderate' or 'critical'.
+    """
+    self._tab.browser.SimulateMemoryPressureNotification(pressure_level)
+
+  def PauseInteractive(self):
+    """Pause the page execution and wait for terminal interaction.
+
+    This is typically used for debugging. You can use this to pause
+    the page execution and inspect the browser state before
+    continuing.
+    """
+    raw_input("Interacting... Press Enter to continue.")
+
+  def RepaintContinuously(self, seconds):
+    """Continuously repaints the visible content.
+
+    It does this by requesting animation frames until the given number
+    of seconds have elapsed AND at least three RAFs have been
+    fired. Times out after max(60, self.seconds), if less than three
+    RAFs were fired."""
+    self._RunAction(RepaintContinuouslyAction(
+        seconds=0 if self._skip_waits else seconds))
+
+
+class Interaction(object):
+
+  def __init__(self, action_runner, label, flags):
+    assert action_runner
+    assert label
+    assert isinstance(flags, list)
+
+    self._action_runner = action_runner
+    self._label = label
+    self._flags = flags
+    self._started = False
+
+  def __enter__(self):
+    self.Begin()
+    return self
+
+  def __exit__(self, exc_type, exc_value, traceback):
+    if exc_value is None:
+      self.End()
+    else:
+      logging.warning(
+          'Exception was raised in the with statement block, the end of '
+          'interaction record is not marked.')
+
+  def Begin(self):
+    assert not self._started
+    self._started = True
+    self._action_runner.ExecuteJavaScript(
+        'console.time({{ marker }});',
+        marker=timeline_interaction_record.GetJavaScriptMarker(
+            self._label, self._flags))
+
+  def End(self):
+    assert self._started
+    self._started = False
+    self._action_runner.ExecuteJavaScript(
+        'console.timeEnd({{ marker }});',
+        marker=timeline_interaction_record.GetJavaScriptMarker(
+            self._label, self._flags))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/action_runner_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/action_runner_unittest.py
new file mode 100644
index 0000000..1c4c259
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/action_runner_unittest.py
@@ -0,0 +1,470 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import mock
+import unittest
+
+from telemetry.core import exceptions
+from telemetry import decorators
+from telemetry.internal.actions import action_runner as action_runner_module
+from telemetry.internal.actions import page_action
+from telemetry.testing import tab_test_case
+from telemetry.timeline import chrome_trace_category_filter
+from telemetry.timeline import model
+from telemetry.timeline import tracing_config
+from telemetry.web_perf import timeline_interaction_record as tir_module
+
+import py_utils
+
+
+class ActionRunnerInteractionTest(tab_test_case.TabTestCase):
+
+  def GetInteractionRecords(self, trace_data):
+    timeline_model = model.TimelineModel(trace_data)
+    renderer_thread = timeline_model.GetRendererThreadFromTabId(self._tab.id)
+    return [
+        tir_module.TimelineInteractionRecord.FromAsyncEvent(e)
+        for e in renderer_thread.async_slices
+        if tir_module.IsTimelineInteractionRecord(e.name)
+        ]
+
+  def VerifyIssuingInteractionRecords(self, **interaction_kwargs):
+    action_runner = action_runner_module.ActionRunner(self._tab,
+                                                      skip_waits=True)
+    self.Navigate('interaction_enabled_page.html')
+    action_runner.Wait(1)
+    config = tracing_config.TracingConfig()
+    config.chrome_trace_config.SetLowOverheadFilter()
+    config.enable_chrome_trace = True
+    self._browser.platform.tracing_controller.StartTracing(config)
+    with action_runner.CreateInteraction('InteractionName',
+                                                 **interaction_kwargs):
+      pass
+    trace_data = self._browser.platform.tracing_controller.StopTracing()
+
+    records = self.GetInteractionRecords(trace_data)
+    self.assertEqual(
+        1, len(records),
+        'Failed to issue the interaction record on the tracing timeline.'
+        ' Trace data:\n%s' % repr(trace_data._raw_data))
+    self.assertEqual('InteractionName', records[0].label)
+    for attribute_name in interaction_kwargs:
+      self.assertTrue(getattr(records[0], attribute_name))
+
+  # Test disabled for android: crbug.com/437057
+  # Test disabled for linux: crbug.com/513874
+  @decorators.Disabled('android', 'chromeos', 'linux')
+  def testIssuingMultipleMeasurementInteractionRecords(self):
+    self.VerifyIssuingInteractionRecords(repeatable=True)
+
+
+class ActionRunnerMeasureMemoryTest(tab_test_case.TabTestCase):
+  def setUp(self):
+    super(ActionRunnerMeasureMemoryTest, self).setUp()
+    self.action_runner = action_runner_module.ActionRunner(self._tab,
+                                                           skip_waits=True)
+    self.Navigate('blank.html')
+
+  def testWithoutTracing(self):
+    with mock.patch.object(self._tab.browser, 'DumpMemory') as mock_method:
+      self.assertIsNone(self.action_runner.MeasureMemory())
+      self.assertFalse(mock_method.called)  # No-op with no tracing.
+
+  def _testWithTracing(self, deterministic_mode=False):
+    trace_memory = chrome_trace_category_filter.ChromeTraceCategoryFilter(
+        filter_string='-*,blink.console,disabled-by-default-memory-infra')
+    config = tracing_config.TracingConfig()
+    config.enable_chrome_trace = True
+    config.chrome_trace_config.SetCategoryFilter(trace_memory)
+    self._browser.platform.tracing_controller.StartTracing(config)
+    try:
+      dump_id = self.action_runner.MeasureMemory(deterministic_mode)
+    finally:
+      trace_data = self._browser.platform.tracing_controller.StopTracing()
+
+    # If successful, i.e. we haven't balied out due to an exception, check
+    # that we can find our dump in the trace.
+    self.assertIsNotNone(dump_id)
+    timeline_model = model.TimelineModel(trace_data)
+    dump_ids = (d.dump_id for d in timeline_model.IterGlobalMemoryDumps())
+    self.assertIn(dump_id, dump_ids)
+
+  # TODO(perezju): Enable when reference browser is >= M53
+  # https://github.com/catapult-project/catapult/issues/2610
+  @decorators.Disabled('reference')
+  def testDeterministicMode(self):
+    self._testWithTracing(deterministic_mode=True)
+
+  # TODO(perezju): Enable when reference browser is >= M53
+  # https://github.com/catapult-project/catapult/issues/2610
+  @decorators.Disabled('reference')
+  def testRealisticMode(self):
+    with mock.patch.object(
+        self.action_runner, 'ForceGarbageCollection') as mock_method:
+      self._testWithTracing(deterministic_mode=False)
+      self.assertFalse(mock_method.called)  # No forced GC in "realistic" mode.
+
+  def testWithFailedDump(self):
+    with mock.patch.object(self._tab.browser, 'DumpMemory') as mock_method:
+      mock_method.return_value = False  # Dump fails!
+      with self.assertRaises(exceptions.Error):
+        self._testWithTracing()
+
+
+class ActionRunnerTest(tab_test_case.TabTestCase):
+  def testExecuteJavaScript(self):
+    action_runner = action_runner_module.ActionRunner(self._tab,
+                                                      skip_waits=True)
+    self.Navigate('blank.html')
+    action_runner.ExecuteJavaScript('var testing = 42;')
+    self.assertEqual(42, self._tab.EvaluateJavaScript('testing'))
+
+  def testWaitForNavigate(self):
+    self.Navigate('page_with_link.html')
+    action_runner = action_runner_module.ActionRunner(self._tab,
+                                                      skip_waits=True)
+    action_runner.ClickElement('#clickme')
+    action_runner.WaitForNavigate()
+
+    self.assertTrue(self._tab.EvaluateJavaScript(
+        'document.readyState == "interactive" || '
+        'document.readyState == "complete"'))
+    self.assertEqual(
+        self._tab.EvaluateJavaScript('document.location.pathname;'),
+        '/blank.html')
+
+  def testNavigateBack(self):
+    action_runner = action_runner_module.ActionRunner(self._tab,
+                                                      skip_waits=True)
+    self.Navigate('page_with_link.html')
+    action_runner.WaitForJavaScriptCondition(
+        'document.location.pathname === "/page_with_link.html"')
+
+    # Test that after 3 navigations & 3 back navs, we have to be back at the
+    # initial page
+    self.Navigate('page_with_swipeables.html')
+    action_runner.WaitForJavaScriptCondition(
+        'document.location.pathname === "/page_with_swipeables.html"')
+
+    self.Navigate('blank.html')
+    action_runner.WaitForJavaScriptCondition(
+        'document.location.pathname === "/blank.html"')
+
+    self.Navigate('page_with_swipeables.html')
+    action_runner.WaitForJavaScriptCondition(
+        'document.location.pathname === "/page_with_swipeables.html"')
+
+    action_runner.NavigateBack()
+    action_runner.WaitForJavaScriptCondition(
+        'document.location.pathname === "/blank.html"')
+
+    action_runner.NavigateBack()
+    action_runner.WaitForJavaScriptCondition(
+        'document.location.pathname === "/page_with_swipeables.html"')
+
+    action_runner.NavigateBack()
+    action_runner.WaitForJavaScriptCondition(
+        'document.location.pathname === "/page_with_link.html"')
+
+  def testWait(self):
+    action_runner = action_runner_module.ActionRunner(self._tab)
+    self.Navigate('blank.html')
+
+    action_runner.ExecuteJavaScript(
+        'window.setTimeout(function() { window.testing = 101; }, 50);')
+    action_runner.Wait(0.1)
+    self.assertEqual(101, self._tab.EvaluateJavaScript('window.testing'))
+
+    action_runner.ExecuteJavaScript(
+        'window.setTimeout(function() { window.testing = 102; }, 100);')
+    action_runner.Wait(0.2)
+    self.assertEqual(102, self._tab.EvaluateJavaScript('window.testing'))
+
+  def testWaitForJavaScriptCondition(self):
+    action_runner = action_runner_module.ActionRunner(self._tab,
+                                                      skip_waits=True)
+    self.Navigate('blank.html')
+
+    action_runner.ExecuteJavaScript('window.testing = 219;')
+    action_runner.WaitForJavaScriptCondition(
+        'window.testing == 219', timeout=0.1)
+    action_runner.ExecuteJavaScript(
+        'window.setTimeout(function() { window.testing = 220; }, 50);')
+    action_runner.WaitForJavaScriptCondition(
+        'window.testing == 220', timeout=0.1)
+    self.assertEqual(220, self._tab.EvaluateJavaScript('window.testing'))
+
+  def testWaitForJavaScriptCondition_returnsValue(self):
+    action_runner = action_runner_module.ActionRunner(self._tab,
+                                                      skip_waits=True)
+    self.Navigate('blank.html')
+
+    action_runner.ExecuteJavaScript('window.testing = 0;')
+    action_runner.WaitForJavaScriptCondition(
+        'window.testing == 0', timeout=0.1)
+    action_runner.ExecuteJavaScript(
+        'window.setTimeout(function() { window.testing = 42; }, 50);')
+    self.assertEqual(
+        42,
+        action_runner.WaitForJavaScriptCondition('window.testing', timeout=10))
+
+  def testWaitForElement(self):
+    action_runner = action_runner_module.ActionRunner(self._tab,
+                                                      skip_waits=True)
+    self.Navigate('blank.html')
+
+    action_runner.ExecuteJavaScript(
+        '(function() {'
+        '  var el = document.createElement("div");'
+        '  el.id = "test1";'
+        '  el.textContent = "foo";'
+        '  document.body.appendChild(el);'
+        '})()')
+    action_runner.WaitForElement('#test1', timeout_in_seconds=0.1)
+    action_runner.WaitForElement(text='foo', timeout_in_seconds=0.1)
+    action_runner.WaitForElement(
+        element_function='document.getElementById("test1")')
+    action_runner.ExecuteJavaScript(
+        'window.setTimeout(function() {'
+        '  var el = document.createElement("div");'
+        '  el.id = "test2";'
+        '  document.body.appendChild(el);'
+        '}, 50)')
+    action_runner.WaitForElement('#test2', timeout_in_seconds=0.1)
+    action_runner.ExecuteJavaScript(
+        'window.setTimeout(function() {'
+        '  document.getElementById("test2").textContent = "bar";'
+        '}, 50)')
+    action_runner.WaitForElement(text='bar', timeout_in_seconds=0.1)
+    action_runner.ExecuteJavaScript(
+        'window.setTimeout(function() {'
+        '  var el = document.createElement("div");'
+        '  el.id = "test3";'
+        '  document.body.appendChild(el);'
+        '}, 50)')
+    action_runner.WaitForElement(
+        element_function='document.getElementById("test3")')
+
+  def testWaitForElementWithWrongText(self):
+    action_runner = action_runner_module.ActionRunner(self._tab,
+                                                      skip_waits=True)
+    self.Navigate('blank.html')
+
+    action_runner.ExecuteJavaScript(
+        '(function() {'
+        '  var el = document.createElement("div");'
+        '  el.id = "test1";'
+        '  el.textContent = "foo";'
+        '  document.body.appendChild(el);'
+        '})()')
+    action_runner.WaitForElement('#test1', timeout_in_seconds=0.2)
+    def WaitForElement():
+      action_runner.WaitForElement(text='oo', timeout_in_seconds=0.2)
+    self.assertRaises(py_utils.TimeoutException, WaitForElement)
+
+  def testClickElement(self):
+    self.Navigate('page_with_clickables.html')
+    action_runner = action_runner_module.ActionRunner(self._tab,
+                                                      skip_waits=True)
+
+    action_runner.ExecuteJavaScript('valueSettableByTest = 1;')
+    action_runner.ClickElement('#test')
+    self.assertEqual(1, action_runner.EvaluateJavaScript('valueToTest'))
+
+    action_runner.ExecuteJavaScript('valueSettableByTest = 2;')
+    action_runner.ClickElement(text='Click/tap me')
+    self.assertEqual(2, action_runner.EvaluateJavaScript('valueToTest'))
+
+    action_runner.ExecuteJavaScript('valueSettableByTest = 3;')
+    action_runner.ClickElement(
+        element_function='document.body.firstElementChild;')
+    self.assertEqual(3, action_runner.EvaluateJavaScript('valueToTest'))
+
+    def WillFail():
+      action_runner.ClickElement('#notfound')
+    self.assertRaises(exceptions.EvaluateException, WillFail)
+
+  @decorators.Disabled('android', 'debug',  # crbug.com/437068
+                       'chromeos',          # crbug.com/483212
+                       'win')               # catapult/issues/2282
+  def testTapElement(self):
+    self.Navigate('page_with_clickables.html')
+    action_runner = action_runner_module.ActionRunner(self._tab,
+                                                      skip_waits=True)
+
+    action_runner.ExecuteJavaScript('valueSettableByTest = 1;')
+    action_runner.TapElement('#test')
+    self.assertEqual(1, action_runner.EvaluateJavaScript('valueToTest'))
+
+    action_runner.ExecuteJavaScript('valueSettableByTest = 2;')
+    action_runner.TapElement(text='Click/tap me')
+    self.assertEqual(2, action_runner.EvaluateJavaScript('valueToTest'))
+
+    action_runner.ExecuteJavaScript('valueSettableByTest = 3;')
+    action_runner.TapElement(
+        element_function='document.body.firstElementChild')
+    self.assertEqual(3, action_runner.EvaluateJavaScript('valueToTest'))
+
+    def WillFail():
+      action_runner.TapElement('#notfound')
+    self.assertRaises(exceptions.EvaluateException, WillFail)
+
+  # https://github.com/catapult-project/catapult/issues/3099
+  @decorators.Disabled('android')
+  def testScrollToElement(self):
+    self.Navigate('page_with_swipeables.html')
+    action_runner = action_runner_module.ActionRunner(self._tab,
+                                                      skip_waits=True)
+
+    off_screen_element = 'document.querySelectorAll("#off-screen")[0]'
+    top_bottom_element = 'document.querySelector("#top-bottom")'
+
+    def viewport_comparator(element):
+      return action_runner.EvaluateJavaScript('''
+          (function(elem) {
+            var rect = elem.getBoundingClientRect();
+
+            if (rect.bottom < 0) {
+              // The bottom of the element is above the viewport.
+              return -1;
+            }
+            if (rect.top - window.innerHeight > 0) {
+              // rect.top provides the pixel offset of the element from the
+              // top of the page. Because that exceeds the viewport's height,
+              // we know that the element is below the viewport.
+              return 1;
+            }
+            return 0;
+          })({{ @element }});
+          ''', element=element)
+
+
+    self.assertEqual(viewport_comparator(off_screen_element), 1)
+    action_runner.ScrollPageToElement(selector='#off-screen',
+                                      speed_in_pixels_per_second=5000)
+    self.assertEqual(viewport_comparator(off_screen_element), 0)
+
+    self.assertEqual(viewport_comparator(top_bottom_element), -1)
+    action_runner.ScrollPageToElement(selector='#top-bottom',
+                                      container_selector='body',
+                                      speed_in_pixels_per_second=5000)
+    self.assertEqual(viewport_comparator(top_bottom_element), 0)
+
+  @decorators.Disabled('android',   # crbug.com/437065.
+                       'chromeos')  # crbug.com/483212.
+  def testScroll(self):
+    if not page_action.IsGestureSourceTypeSupported(
+        self._tab, 'touch'):
+      return
+
+    self.Navigate('page_with_swipeables.html')
+    action_runner = action_runner_module.ActionRunner(self._tab,
+                                                      skip_waits=True)
+
+    action_runner.ScrollElement(
+        selector='#left-right', direction='right', left_start_ratio=0.9)
+    self.assertTrue(action_runner.EvaluateJavaScript(
+        'document.querySelector("#left-right").scrollLeft') > 75)
+    action_runner.ScrollElement(
+        selector='#top-bottom', direction='down', top_start_ratio=0.9)
+    self.assertTrue(action_runner.EvaluateJavaScript(
+        'document.querySelector("#top-bottom").scrollTop') > 75)
+
+    action_runner.ScrollPage(direction='right', left_start_ratio=0.9,
+                             distance=100)
+    self.assertTrue(action_runner.EvaluateJavaScript(
+        '(document.scrollingElement || document.body).scrollLeft') > 75)
+
+  @decorators.Disabled('android',   # crbug.com/437065.
+                       'chromeos')  # crbug.com/483212.
+  def testSwipe(self):
+    if not page_action.IsGestureSourceTypeSupported(
+        self._tab, 'touch'):
+      return
+
+    self.Navigate('page_with_swipeables.html')
+    action_runner = action_runner_module.ActionRunner(self._tab,
+                                                      skip_waits=True)
+
+    action_runner.SwipeElement(
+        selector='#left-right', direction='left', left_start_ratio=0.9)
+    self.assertTrue(action_runner.EvaluateJavaScript(
+        'document.querySelector("#left-right").scrollLeft') > 75)
+    action_runner.SwipeElement(
+        selector='#top-bottom', direction='up', top_start_ratio=0.9)
+    self.assertTrue(action_runner.EvaluateJavaScript(
+        'document.querySelector("#top-bottom").scrollTop') > 75)
+
+    action_runner.SwipePage(direction='left', left_start_ratio=0.9)
+    self.assertTrue(action_runner.EvaluateJavaScript(
+        '(document.scrollingElement || document.body).scrollLeft') > 75)
+
+  def testWaitForNetworkQuiescenceSmoke(self):
+    self.Navigate('blank.html')
+    action_runner = action_runner_module.ActionRunner(self._tab)
+    action_runner.WaitForNetworkQuiescence()
+    self.assertEqual(
+        self._tab.EvaluateJavaScript('document.location.pathname;'),
+        '/blank.html')
+
+  def testEnterText(self):
+    self.Navigate('blank.html')
+    self._tab.ExecuteJavaScript(
+        '(function() {'
+        '  var elem = document.createElement("textarea");'
+        '  document.body.appendChild(elem);'
+        '  elem.focus();'
+        '})();')
+
+    action_runner = action_runner_module.ActionRunner(self._tab,
+                                                      skip_waits=True)
+    action_runner.EnterText('That is boring')  # That is boring|.
+    action_runner.PressKey('Home')  # |That is boring.
+    action_runner.PressKey('ArrowRight', repeat_count=2)  # Th|at is boring.
+    action_runner.PressKey('Delete', repeat_count=2)  # Th| is boring.
+    action_runner.EnterText('is')  # This| is boring.
+    action_runner.PressKey('End')  # This is boring|.
+    action_runner.PressKey('ArrowLeft', repeat_count=3)  # This is bor|ing.
+    action_runner.PressKey('Backspace', repeat_count=3)  # This is |ing.
+    action_runner.EnterText('interest')  # This is interest|ing.
+
+    # Check that the contents of the textarea is correct. It might take a second
+    # until all keystrokes have been handled by the browser (crbug.com/630017).
+    self._tab.WaitForJavaScriptCondition(
+        'document.querySelector("textarea").value === "This is interesting"',
+        timeout=1)
+
+
+class InteractionTest(unittest.TestCase):
+
+  def setUp(self):
+    self.mock_action_runner = mock.Mock(action_runner_module.ActionRunner)
+
+    def expected_js_call(method):
+      return mock.call.ExecuteJavaScript(
+          '%s({{ marker }});' % method, marker='Interaction.ABC')
+
+    self.expected_calls = [
+        expected_js_call('console.time'),
+        expected_js_call('console.timeEnd')]
+
+  def testIssuingInteractionRecordCommand(self):
+    with action_runner_module.Interaction(
+        self.mock_action_runner, label='ABC', flags=[]):
+      pass
+    self.assertEqual(self.expected_calls, self.mock_action_runner.mock_calls)
+
+  def testExceptionRaisedInWithInteraction(self):
+    class FooException(Exception):
+      pass
+    # Test that the Foo exception raised in the with block is propagated to the
+    # caller.
+    with self.assertRaises(FooException):
+      with action_runner_module.Interaction(
+          self.mock_action_runner, label='ABC', flags=[]):
+        raise FooException()
+
+    # Test that the end console.timeEnd(...) isn't called because exception was
+    # raised.
+    self.assertEqual(
+        self.expected_calls[:1], self.mock_action_runner.mock_calls)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/drag.js b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/drag.js
new file mode 100644
index 0000000..8c5613c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/drag.js
@@ -0,0 +1,72 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file provides the DragAction object, which performs drag on a page
+// using given start and end positions:
+//   1. var action = new __DragAction(callback)
+//   2. action.start(drag_options)
+'use strict';
+
+(function() {
+  function DragGestureOptions(opt_options) {
+    this.element_ = opt_options.element;
+    this.left_start_ratio_ = opt_options.left_start_ratio;
+    this.top_start_ratio_ = opt_options.top_start_ratio;
+    this.left_end_ratio_ = opt_options.left_end_ratio;
+    this.top_end_ratio_ = opt_options.top_end_ratio;
+    this.speed_ = opt_options.speed;
+    this.gesture_source_type_ = opt_options.gesture_source_type;
+  }
+
+  function supportedByBrowser() {
+    return !!(window.chrome &&
+              chrome.gpuBenchmarking &&
+              chrome.gpuBenchmarking.smoothDrag &&
+              chrome.gpuBenchmarking.visualViewportHeight &&
+              chrome.gpuBenchmarking.visualViewportWidth);
+  }
+
+  // This class performs drag action using given start and end positions,
+  // by a single drag gesture.
+  function DragAction(opt_callback) {
+    this.beginMeasuringHook = function() {};
+    this.endMeasuringHook = function() {};
+
+    this.callback_ = opt_callback;
+  }
+
+  DragAction.prototype.start = function(opt_options) {
+    this.options_ = new DragGestureOptions(opt_options);
+    requestAnimationFrame(this.startGesture_.bind(this));
+  };
+
+  DragAction.prototype.startGesture_ = function() {
+    this.beginMeasuringHook();
+
+    var rect = __GestureCommon_GetBoundingVisibleRect(this.options_.element_);
+    var start_left =
+        rect.left + (rect.width * this.options_.left_start_ratio_);
+    var start_top =
+        rect.top + (rect.height * this.options_.top_start_ratio_);
+    var end_left =
+        rect.left + (rect.width * this.options_.left_end_ratio_);
+    var end_top =
+        rect.top + (rect.height * this.options_.top_end_ratio_);
+    chrome.gpuBenchmarking.smoothDrag(
+        start_left, start_top, end_left, end_top,
+        this.onGestureComplete_.bind(this), this.options_.gesture_source_type_,
+        this.options_.speed_);
+  };
+
+  DragAction.prototype.onGestureComplete_ = function() {
+    this.endMeasuringHook();
+
+    // We're done.
+    if (this.callback_)
+      this.callback_();
+  };
+
+  window.__DragAction = DragAction;
+  window.__DragAction_SupportedByBrowser = supportedByBrowser;
+})();
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/drag.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/drag.py
new file mode 100644
index 0000000..2a0e6a6
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/drag.py
@@ -0,0 +1,106 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""A Telemetry page_action that performs the "drag" action on pages.
+
+Action parameters are:
+- selector: If no selector is defined then the action attempts to drag the
+            document element on the page.
+- element_function: CSS selector used to evaluate callback when test completes
+- text: The element with exact text is selected.
+- left_start_ratio: ratio of start point's left coordinate to the element
+                    width.
+- top_start_ratio: ratio of start point's top coordinate to the element height.
+- left_end_ratio: ratio of end point's left coordinate to the element width.
+- left_end_ratio: ratio of end point's top coordinate to the element height.
+- speed_in_pixels_per_second: speed of the drag gesture in pixels per second.
+- use_touch: boolean value to specify if gesture should use touch input or not.
+"""
+
+from telemetry.internal.actions import page_action
+from telemetry.internal.actions import utils
+from telemetry.util import js_template
+
+
+class DragAction(page_action.PageAction):
+
+  def __init__(self, selector=None, text=None, element_function=None,
+               left_start_ratio=None, top_start_ratio=None, left_end_ratio=None,
+               top_end_ratio=None, speed_in_pixels_per_second=800,
+               use_touch=False,
+               synthetic_gesture_source=page_action.GESTURE_SOURCE_DEFAULT):
+    super(DragAction, self).__init__()
+    self._selector = selector
+    self._text = text
+    self._element_function = element_function
+    self._left_start_ratio = left_start_ratio
+    self._top_start_ratio = top_start_ratio
+    self._left_end_ratio = left_end_ratio
+    self._top_end_ratio = top_end_ratio
+    self._speed = speed_in_pixels_per_second
+    self._use_touch = use_touch
+    self._synthetic_gesture_source = ('chrome.gpuBenchmarking.%s_INPUT' %
+                                      synthetic_gesture_source)
+
+  def WillRunAction(self, tab):
+    utils.InjectJavaScript(tab, 'gesture_common.js')
+    utils.InjectJavaScript(tab, 'drag.js')
+
+    # Fail if browser doesn't support synthetic drag gestures.
+    if not tab.EvaluateJavaScript('window.__DragAction_SupportedByBrowser()'):
+      raise page_action.PageActionNotSupported(
+          'Synthetic drag not supported for this browser')
+
+    # Fail if this action requires touch and we can't send touch events.
+    if self._use_touch:
+      if not page_action.IsGestureSourceTypeSupported(tab, 'touch'):
+        raise page_action.PageActionNotSupported(
+            'Touch drag not supported for this browser')
+
+      if (self._synthetic_gesture_source ==
+          'chrome.gpuBenchmarking.MOUSE_INPUT'):
+        raise page_action.PageActionNotSupported(
+            'Drag requires touch on this page but mouse input was requested')
+
+    tab.ExecuteJavaScript('''
+        window.__dragActionDone = false;
+        window.__dragAction = new __DragAction(function() {
+          window.__dragActionDone = true;
+        });''')
+
+  def RunAction(self, tab):
+    if (self._selector is None and self._text is None and
+        self._element_function is None):
+      self._element_function = 'document.body'
+
+    gesture_source_type = 'chrome.gpuBenchmarking.TOUCH_INPUT'
+    if (page_action.IsGestureSourceTypeSupported(tab, 'mouse') and
+        not self._use_touch):
+      gesture_source_type = 'chrome.gpuBenchmarking.MOUSE_INPUT'
+
+    code = js_template.Render('''
+        function(element, info) {
+          if (!element) {
+            throw Error('Cannot find element: ' + info);
+          }
+          window.__dragAction.start({
+            element: element,
+            left_start_ratio: {{ left_start_ratio }},
+            top_start_ratio: {{ top_start_ratio }},
+            left_end_ratio: {{ left_end_ratio }},
+            top_end_ratio: {{ top_end_ratio }},
+            speed: {{ speed }},
+            gesture_source_type: {{ @gesture_source_type }}
+          });
+        }''',
+        left_start_ratio=self._left_start_ratio,
+        top_start_ratio=self._top_start_ratio,
+        left_end_ratio=self._left_end_ratio,
+        top_end_ratio=self._top_end_ratio,
+        speed=self._speed,
+        gesture_source_type=gesture_source_type)
+    page_action.EvaluateCallbackWithElement(
+        tab, code, selector=self._selector, text=self._text,
+        element_function=self._element_function)
+    tab.WaitForJavaScriptCondition('window.__dragActionDone', timeout=60)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/drag_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/drag_unittest.py
new file mode 100644
index 0000000..a126c5b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/drag_unittest.py
@@ -0,0 +1,71 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+import math
+
+from telemetry import decorators
+from telemetry.internal.actions import drag
+from telemetry.internal.actions import page_action
+from telemetry.internal.actions import utils
+from telemetry.testing import tab_test_case
+
+
+class DragActionTest(tab_test_case.TabTestCase):
+  def CheckWithinRange(self, value, expected, error_ratio):
+    error_range = abs(expected * error_ratio)
+    return abs(value - expected) <= error_range
+
+  # https://github.com/catapult-project/catapult/issues/3099 (Android)
+  # crbug.com/483212 (CrOS)
+  @decorators.Disabled('android', 'chromeos')
+  def testDragAction(self):
+    self.Navigate('draggable.html')
+
+    utils.InjectJavaScript(self._tab, 'gesture_common.js')
+
+    div_width = self._tab.EvaluateJavaScript(
+        '__GestureCommon_GetBoundingVisibleRect(document.body).width')
+    div_height = self._tab.EvaluateJavaScript(
+        '__GestureCommon_GetBoundingVisibleRect(document.body).height')
+
+    i = drag.DragAction(left_start_ratio=0.5, top_start_ratio=0.5,
+            left_end_ratio=0.25, top_end_ratio=0.25)
+    try:
+      i.WillRunAction(self._tab)
+    except page_action.PageActionNotSupported:
+      logging.warning('This browser does not support drag gesture. Please try'
+                      ' updating chrome.')
+      return
+
+    self._tab.ExecuteJavaScript('''
+        window.__dragAction.beginMeasuringHook = function() {
+            window.__didBeginMeasuring = true;
+        };
+        window.__dragAction.endMeasuringHook = function() {
+            window.__didEndMeasuring = true;
+        };''')
+    i.RunAction(self._tab)
+
+    self.assertTrue(self._tab.EvaluateJavaScript('window.__didBeginMeasuring'))
+    self.assertTrue(self._tab.EvaluateJavaScript('window.__didEndMeasuring'))
+
+    div_position_x = self._tab.EvaluateJavaScript(
+        'document.getElementById("drag_div").offsetLeft')
+    div_position_y = self._tab.EvaluateJavaScript(
+        'document.getElementById("drag_div").offsetTop')
+
+    # 0.25 is the ratio of displacement to the initial size.
+    expected_x = math.floor(div_width * -0.25)
+    expected_y = math.floor(div_height * -0.25)
+    error_ratio = 0.1
+
+    self.assertTrue(
+        self.CheckWithinRange(div_position_x, expected_x, error_ratio),
+        msg="Moved element's left coordinate: %d, expected: %d" %
+        (div_position_x, expected_x))
+    self.assertTrue(
+        self.CheckWithinRange(div_position_y, expected_y, error_ratio),
+        msg="Moved element's top coordinate: %d, expected: %d" %
+        (div_position_y, expected_y))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/gesture_common.js b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/gesture_common.js
new file mode 100644
index 0000000..ec76eba
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/gesture_common.js
@@ -0,0 +1,77 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file provides common functionality for synthetic gesture actions.
+'use strict';
+
+(function() {
+
+  // Make sure functions are injected only once.
+  if (window.__GestureCommon_GetBoundingVisibleRect)
+    return;
+
+  // Returns the bounding rectangle wrt to the top-most document.
+  function getBoundingRect(el) {
+    var clientRect = el.getBoundingClientRect();
+    var bound = { left: clientRect.left,
+                  top: clientRect.top,
+                  width: clientRect.width,
+                  height: clientRect.height };
+
+    var frame = el.ownerDocument.defaultView.frameElement;
+    while (frame) {
+      var frameBound = frame.getBoundingClientRect();
+      // This computation doesn't account for more complex CSS transforms on the
+      // frame (e.g. scaling or rotations).
+      bound.left += frameBound.left;
+      bound.top += frameBound.top;
+
+      frame = frame.ownerDocument.frameElement;
+    }
+    return bound;
+  }
+
+  // TODO(ulan): Remove this function once
+  // chrome.gpuBenchmarking.pageScaleFactor is available in reference builds.
+  function getPageScaleFactor() {
+    if (chrome.gpuBenchmarking.pageScaleFactor)
+      return chrome.gpuBenchmarking.pageScaleFactor();
+    return 1;
+  }
+
+  // Zoom-independent window height. See crbug.com/627123 for more details.
+  function getWindowHeight() {
+    return getPageScaleFactor() * chrome.gpuBenchmarking.visualViewportHeight();
+  }
+
+  // Zoom-independent window width. See crbug.com/627123 for more details.
+  function getWindowWidth() {
+    return getPageScaleFactor() * chrome.gpuBenchmarking.visualViewportWidth();
+  }
+
+  function clamp(min, value, max) {
+    return Math.min(Math.max(min, value), max);
+  }
+
+  function getBoundingVisibleRect(el) {
+    // Get the element bounding rect.
+    var rect = getBoundingRect(el);
+
+    // Get the window dimensions.
+    var windowHeight = getWindowHeight();
+    var windowWidth = getWindowWidth();
+
+    // Then clip the rect to the screen size.
+    rect.top = clamp(0, rect.top, windowHeight);
+    rect.left = clamp(0, rect.left, windowWidth);
+    rect.height = clamp(0, rect.height, windowHeight - rect.top);
+    rect.width = clamp(0, rect.width, windowWidth - rect.left);
+
+    return rect;
+  }
+
+  window.__GestureCommon_GetBoundingVisibleRect = getBoundingVisibleRect;
+  window.__GestureCommon_GetWindowHeight = getWindowHeight;
+  window.__GestureCommon_GetWindowWidth = getWindowWidth;
+})();
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/javascript_click.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/javascript_click.py
new file mode 100644
index 0000000..3a70e00
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/javascript_click.py
@@ -0,0 +1,25 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.internal.actions import page_action
+
+
+class ClickElementAction(page_action.PageAction):
+  def __init__(self, selector=None, text=None, element_function=None):
+    super(ClickElementAction, self).__init__()
+    self.selector = selector
+    self.text = text
+    self.element_function = element_function
+
+  def RunAction(self, tab):
+    code = '''
+        function(element, errorMsg) {
+          if (!element) {
+            throw Error('Cannot find element: ' + errorMsg);
+          }
+          element.click();
+        }'''
+    page_action.EvaluateCallbackWithElement(
+        tab, code, selector=self.selector, text=self.text,
+        element_function=self.element_function)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/key_event.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/key_event.py
new file mode 100644
index 0000000..1558607
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/key_event.py
@@ -0,0 +1,96 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import string
+
+from telemetry.internal.actions import page_action
+
+
+# Map from DOM key values
+# (https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key) to
+# Windows virtual key codes
+# (https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/WindowsKeyboardCodes.h)
+# and their printed representations (if available).
+_KEY_MAP = {}
+
+def _AddSpecialKey(key, windows_virtual_key_code, text=None):
+  assert key not in _KEY_MAP, 'Duplicate key: %s' % key
+  _KEY_MAP[key] = (windows_virtual_key_code, text)
+
+def _AddRegularKey(keys, windows_virtual_key_code):
+  for k in keys:
+    assert k not in _KEY_MAP, 'Duplicate key: %s' % k
+    _KEY_MAP[k] = (windows_virtual_key_code, k)
+
+_AddSpecialKey('PageUp', 0x21)
+_AddSpecialKey('PageDown', 0x22)
+_AddSpecialKey('End', 0x23)
+_AddSpecialKey('Home', 0x24)
+_AddSpecialKey('ArrowLeft', 0x25)
+_AddSpecialKey('ArrowUp', 0x26)
+_AddSpecialKey('ArrowRight', 0x27)
+_AddSpecialKey('ArrowDown', 0x28)
+
+_AddSpecialKey('Return', 0x0D, text='\x0D')
+_AddSpecialKey('Delete', 0x2E, text='\x7F')
+_AddSpecialKey('Backspace', 0x08, text='\x08')
+
+# Letter keys.
+for c in string.ascii_uppercase:
+  _AddRegularKey([c, c.lower()], ord(c))
+
+# Symbol keys.
+_AddRegularKey(';:', 0xBA)
+_AddRegularKey('=+', 0xBB)
+_AddRegularKey(',<', 0xBC)
+_AddRegularKey('-_', 0xBD)
+_AddRegularKey('.>', 0xBE)
+_AddRegularKey('/?', 0xBF)
+_AddRegularKey('`~', 0xC0)
+_AddRegularKey('[{', 0xDB)
+_AddRegularKey('\\|', 0xDC)
+_AddRegularKey(']}', 0xDD)
+_AddRegularKey('\'"', 0xDE)
+
+# Numeric keys.
+_AddRegularKey('0)', 0x30)
+_AddRegularKey('1!', 0x31)
+_AddRegularKey('2@', 0x32)
+_AddRegularKey('3#', 0x33)
+_AddRegularKey('4$', 0x34)
+_AddRegularKey('5%', 0x35)
+_AddRegularKey('6^', 0x36)
+_AddRegularKey('7&', 0x37)
+_AddRegularKey('8*', 0x38)
+_AddRegularKey('9(', 0x39)
+
+# Space.
+_AddRegularKey(' ', 0x20)
+
+
+class KeyPressAction(page_action.PageAction):
+
+  def __init__(self, dom_key, timeout=60):
+    super(KeyPressAction, self).__init__()
+    self._dom_key = dom_key
+    if dom_key not in _KEY_MAP:
+      raise ValueError('No mapping for key: %s' % dom_key)
+    self._windows_virtual_key_code, self._text = _KEY_MAP[dom_key]
+    self._timeout = timeout
+
+  def RunAction(self, tab):
+    tab.DispatchKeyEvent(keyEventType='rawKeyDown',
+                         domKey=self._dom_key,
+                         windowsVirtualKeyCode=self._windows_virtual_key_code,
+                         timeout=self._timeout)
+    if self._text:
+      tab.DispatchKeyEvent(keyEventType='char',
+                           text=self._text,
+                           domKey=self._dom_key,
+                           windowsVirtualKeyCode=ord(self._text),
+                           timeout=self._timeout)
+    tab.DispatchKeyEvent(keyEventType='keyUp',
+                         domKey=self._dom_key,
+                         windowsVirtualKeyCode=self._windows_virtual_key_code,
+                         timeout=self._timeout)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/key_event_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/key_event_unittest.py
new file mode 100644
index 0000000..ca35ac1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/key_event_unittest.py
@@ -0,0 +1,89 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import time
+
+from telemetry import decorators
+from telemetry.internal.actions import key_event
+from telemetry.internal.actions import utils
+from telemetry.testing import tab_test_case
+
+
+class KeyPressActionTest(tab_test_case.TabTestCase):
+
+  @property
+  def _scroll_position(self):
+    return self._tab.EvaluateJavaScript(
+        'document.documentElement.scrollTop || document.body.scrollTop')
+
+  @property
+  def _window_height(self):
+    return self._tab.EvaluateJavaScript('__GestureCommon_GetWindowHeight()')
+
+  def _PressKey(self, key):
+    action = key_event.KeyPressAction(key)
+    action.WillRunAction(self._tab)
+    action.RunAction(self._tab)
+
+  def setUp(self):
+    tab_test_case.TabTestCase.setUp(self)
+    self.Navigate('blank.html')
+    utils.InjectJavaScript(self._tab, 'gesture_common.js')
+
+  # https://github.com/catapult-project/catapult/issues/3099
+  @decorators.Disabled('android')
+  def testPressEndAndHome(self):
+    # Make page taller than the window so it's scrollable.
+    self._tab.ExecuteJavaScript('document.body.style.height ='
+        '(3 * __GestureCommon_GetWindowHeight() + 1) + "px";')
+
+    # Check that the browser is currently showing the top of the page and that
+    # the page has non-trivial height.
+    self.assertEquals(0, self._scroll_position)
+    self.assertLess(50, self._window_height)
+
+    self._PressKey('End')
+
+    # Scroll happens *after* key press returns, so we need to wait a little.
+    time.sleep(1)
+
+    # We can only expect the bottom scroll position to be approximatly equal.
+    self.assertAlmostEqual(2 * self._window_height, self._scroll_position,
+                           delta=20)
+
+    self._PressKey('Home')
+
+    # Scroll happens *after* key press returns, so we need to wait a little.
+    time.sleep(1)
+
+    self.assertEquals(self._scroll_position, 0)
+
+  def testTextEntry(self):
+    # Add an input box to the page.
+    self._tab.ExecuteJavaScript(
+        '(function() {'
+        '  var elem = document.createElement("textarea");'
+        '  document.body.appendChild(elem);'
+        '  elem.focus();'
+        '})();')
+
+    # Simulate typing a sentence.
+    for char in 'Hello, World!':
+      self._PressKey(char)
+
+    # Make changes to the sentence using special keys.
+    for _ in xrange(6):
+      self._PressKey('ArrowLeft')
+    self._PressKey('Backspace')
+    self._PressKey('Return')
+
+    # Check that the contents of the textarea is correct. It might take a second
+    # until all keystrokes have been handled by the browser (crbug.com/630017).
+    self._tab.WaitForJavaScriptCondition(
+        'document.querySelector("textarea").value === "Hello,\\nWorld!"',
+        timeout=1)
+
+  def testPressUnknownKey(self):
+    with self.assertRaises(ValueError):
+      self._PressKey('UnknownKeyName')
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/load_media.js b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/load_media.js
new file mode 100644
index 0000000..06479f9
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/load_media.js
@@ -0,0 +1,30 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function() {
+  function loadMediaAndAwait(selector, event_to_await) {
+    var mediaElements = window.__findMediaElements(selector);
+    for (var i = 0; i < mediaElements.length; i++) {
+      console.log('Listening for ' + event_to_await + ' on element: ' +
+                  mediaElements[i].src);
+      registerListeners(mediaElements[i], event_to_await);
+      loadMediaElement(mediaElements[i]);
+    }
+  }
+
+  function loadMediaElement(element) {
+    if (element instanceof HTMLMediaElement) {
+      element.load();
+    } else {
+      throw new Error('Can not load non media elements.');
+    }
+  }
+
+  function registerListeners(element, event_to_await) {
+    window.__registerHTML5ErrorEvents(element);
+    window.__registerHTML5EventCompleted(element, event_to_await);
+  }
+
+  window.__loadMediaAndAwait = loadMediaAndAwait;
+})();
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/load_media.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/load_media.py
new file mode 100644
index 0000000..1e69dc1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/load_media.py
@@ -0,0 +1,38 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.core import exceptions
+from telemetry.internal.actions import media_action
+from telemetry.internal.actions import page_action
+from telemetry.internal.actions import utils
+
+
+class LoadMediaAction(media_action.MediaAction):
+  """ For calling load() on media elements and waiting for an event to fire.
+  """
+
+  def __init__(self, selector=None, timeout_in_seconds=0,
+               event_to_await='canplaythrough'):
+    super(LoadMediaAction, self).__init__()
+    self._selector = selector or ''
+    self._timeout_in_seconds = timeout_in_seconds
+    self._event_to_await = event_to_await
+
+  def WillRunAction(self, tab):
+    """Load the JS code prior to running the action."""
+    super(LoadMediaAction, self).WillRunAction(tab)
+    utils.InjectJavaScript(tab, 'load_media.js')
+
+  def RunAction(self, tab):
+    try:
+      tab.ExecuteJavaScript(
+          'window.__loadMediaAndAwait({{ selector }}, {{ event }});',
+          selector=self._selector, event=self._event_to_await)
+      if self._timeout_in_seconds > 0:
+        self.WaitForEvent(tab, self._selector, self._event_to_await,
+                          self._timeout_in_seconds)
+    except exceptions.EvaluateException:
+      raise page_action.PageActionFailed('Failed waiting for event "%s" on '
+                                         'elements with selector = %s.' %
+                                         (self._event_to_await, self._selector))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/load_media_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/load_media_unittest.py
new file mode 100644
index 0000000..95347af
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/load_media_unittest.py
@@ -0,0 +1,66 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry import decorators
+from telemetry.internal.actions.load_media import LoadMediaAction
+from telemetry.testing import tab_test_case
+
+import py_utils
+
+
+class LoadMediaActionTest(tab_test_case.TabTestCase):
+
+  def setUp(self):
+    tab_test_case.TabTestCase.setUp(self)
+    self.Navigate('video_test.html')
+
+  def eventFired(self, selector, event):
+    return self._tab.EvaluateJavaScript(
+        'window.__hasEventCompleted({{ selector }}, {{ event }});',
+        selector=selector, event=event)
+
+  @decorators.Disabled('linux',     # crbug.com/418577
+                       'chromeos')  # crbug.com/632802
+  def testAwaitedEventIsConfigurable(self):
+    """It's possible to wait for different events."""
+    action = LoadMediaAction(selector='#video_1', timeout_in_seconds=0.1,
+                             event_to_await='loadedmetadata')
+    action.WillRunAction(self._tab)
+    action.RunAction(self._tab)
+    self.assertTrue(self.eventFired('#video_1', 'loadedmetadata'))
+
+  @decorators.Disabled('linux')  # crbug.com/418577
+  def testLoadWithNoSelector(self):
+    """With no selector the first media element is loaded."""
+    action = LoadMediaAction(timeout_in_seconds=5)
+    action.WillRunAction(self._tab)
+    action.RunAction(self._tab)
+    self.assertTrue(self.eventFired('#video_1', 'canplaythrough'))
+    self.assertFalse(self.eventFired('#audio_1', 'canplaythrough'))
+
+  @decorators.Disabled('linux')  # crbug.com/418577
+  def testLoadWithSelector(self):
+    """Only the element matching the selector is loaded."""
+    action = LoadMediaAction(selector='#audio_1', timeout_in_seconds=5)
+    action.WillRunAction(self._tab)
+    action.RunAction(self._tab)
+    self.assertFalse(self.eventFired('#video_1', 'canplaythrough'))
+    self.assertTrue(self.eventFired('#audio_1', 'canplaythrough'))
+
+  @decorators.Disabled('linux')  # crbug.com/418577
+  def testLoadWithAllSelector(self):
+    """Both elements are loaded with selector='all'."""
+    action = LoadMediaAction(selector='all', timeout_in_seconds=5)
+    action.WillRunAction(self._tab)
+    action.RunAction(self._tab)
+    self.assertTrue(self.eventFired('#video_1', 'canplaythrough'))
+    self.assertTrue(self.eventFired('#audio_1', 'canplaythrough'))
+
+  @decorators.Disabled('linux')  # crbug.com/418577
+  def testLoadRaisesAnExceptionOnTimeout(self):
+    """The load action times out if the event does not fire."""
+    action = LoadMediaAction(selector='#video_1', timeout_in_seconds=0.1,
+                             event_to_await='a_nonexistent_event')
+    action.WillRunAction(self._tab)
+    self.assertRaises(py_utils.TimeoutException, action.RunAction, self._tab)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/loop.js b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/loop.js
new file mode 100644
index 0000000..f862784
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/loop.js
@@ -0,0 +1,53 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file performs actions on media elements.
+(function() {
+  function loopMedia(selector, loopCount) {
+    // Loops media playback `loopCount` times.
+    var mediaElements = window.__findMediaElements(selector);
+    for (var i = 0; i < mediaElements.length; i++) {
+      loop(mediaElements[i], loopCount);
+    }
+  }
+
+  function loop(element, loopCount) {
+    if (element instanceof HTMLMediaElement)
+      loopHTML5Element(element, loopCount);
+    else
+      throw new Error('Can not play non HTML5 media elements.');
+  }
+
+  function loopHTML5Element(element, loopCount) {
+    window.__registerHTML5ErrorEvents(element);
+    element['loop_completed'] = false;
+    var currentLoop = 0;
+    var onLoop = function(e) {
+      ++currentLoop;
+      if (currentLoop == loopCount) {
+        element.pause();
+        element.removeEventListener('seeked', onLoop);
+        element['loop_completed'] = true;
+        // Dispatch endLoopEvent to mark end of looping.
+        var endLoopEvent = document.createEvent('Event');
+        endLoopEvent.initEvent('endLoop', false, false);
+        element.dispatchEvent(endLoopEvent);
+      }
+    };
+
+    element.addEventListener('seeked', onLoop);
+    element.loop = true;
+
+    // Dispatch willLoopEvent to measure loop time.
+    var willLoopEvent = document.createEvent('Event');
+    willLoopEvent.initEvent('willLoop', false, false);
+    willLoopEvent.loopCount = loopCount;
+    element.dispatchEvent(willLoopEvent);
+    // Reset HTML5 player to start playback from beginning.
+    element.load();
+    element.play();
+  }
+
+  window.__loopMedia = loopMedia;
+})();
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/loop.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/loop.py
new file mode 100644
index 0000000..3ee7fdc
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/loop.py
@@ -0,0 +1,43 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""A Telemetry page_action that loops media playback.
+
+Action parameters are:
+- loop_count: The number of times to loop media.
+- selector: If no selector is defined then the action attempts to loop the first
+            media element on the page. If 'all' then loop all media elements.
+- timeout_in_seconds: Timeout to wait for media to loop. Default is
+                      60 sec x loop_count. 0 means do not wait.
+"""
+
+from telemetry.core import exceptions
+from telemetry.internal.actions import media_action
+from telemetry.internal.actions import page_action
+from telemetry.internal.actions import utils
+
+
+class LoopAction(media_action.MediaAction):
+  def __init__(self, loop_count, selector=None, timeout_in_seconds=None):
+    super(LoopAction, self).__init__()
+    self._loop_count = loop_count
+    self._selector = selector if selector else ''
+    self._timeout_in_seconds = (
+        timeout_in_seconds if timeout_in_seconds else 60 * loop_count)
+
+  def WillRunAction(self, tab):
+    """Load the media metrics JS code prior to running the action."""
+    super(LoopAction, self).WillRunAction(tab)
+    utils.InjectJavaScript(tab, 'loop.js')
+
+  def RunAction(self, tab):
+    try:
+      tab.ExecuteJavaScript(
+          'window.__loopMedia({{ selector }}, {{ loop_count }});',
+          selector=self._selector, loop_count=self._loop_count)
+      if self._timeout_in_seconds > 0:
+        self.WaitForEvent(tab, self._selector, 'loop', self._timeout_in_seconds)
+    except exceptions.EvaluateException:
+      raise page_action.PageActionFailed('Cannot loop media element(s) with '
+                                         'selector = %s.' % self._selector)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/loop_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/loop_unittest.py
new file mode 100644
index 0000000..6438cac
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/loop_unittest.py
@@ -0,0 +1,54 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry import decorators
+from telemetry.internal.actions import loop
+from telemetry.testing import tab_test_case
+
+import py_utils
+
+
+AUDIO_1_LOOP_CHECK = 'window.__hasEventCompleted("#audio_1", "loop");'
+VIDEO_1_LOOP_CHECK = 'window.__hasEventCompleted("#video_1", "loop");'
+
+
+class LoopActionTest(tab_test_case.TabTestCase):
+
+  def setUp(self):
+    tab_test_case.TabTestCase.setUp(self)
+    self.Navigate('video_test.html')
+
+  @decorators.Disabled('android', 'linux')  # crbug.com/418577
+  def testLoopWithNoSelector(self):
+    """Tests that with no selector Loop action loops first media element."""
+    action = loop.LoopAction(loop_count=2, selector='#video_1',
+                             timeout_in_seconds=10)
+    action.WillRunAction(self._tab)
+    action.RunAction(self._tab)
+    # Assert only first video has played.
+    self.assertTrue(self._tab.EvaluateJavaScript(VIDEO_1_LOOP_CHECK))
+    self.assertFalse(self._tab.EvaluateJavaScript(AUDIO_1_LOOP_CHECK))
+
+  @decorators.Disabled('android', 'linux')  # crbug.com/418577
+  def testLoopWithAllSelector(self):
+    """Tests that Loop action loops all video elements with selector='all'."""
+    action = loop.LoopAction(loop_count=2, selector='all',
+                             timeout_in_seconds=10)
+    action.WillRunAction(self._tab)
+    # Both videos not playing before running action.
+    self.assertFalse(self._tab.EvaluateJavaScript(VIDEO_1_LOOP_CHECK))
+    self.assertFalse(self._tab.EvaluateJavaScript(AUDIO_1_LOOP_CHECK))
+    action.RunAction(self._tab)
+    # Assert all media elements played.
+    self.assertTrue(self._tab.EvaluateJavaScript(VIDEO_1_LOOP_CHECK))
+    self.assertTrue(self._tab.EvaluateJavaScript(AUDIO_1_LOOP_CHECK))
+
+  @decorators.Disabled('android', 'linux')  # crbug.com/418577
+  def testLoopWaitForLoopTimeout(self):
+    """Tests that wait_for_loop timeout_in_secondss if video does not loop."""
+    action = loop.LoopAction(loop_count=2, selector='#video_1',
+                             timeout_in_seconds=1)
+    action.WillRunAction(self._tab)
+    self.assertFalse(self._tab.EvaluateJavaScript(VIDEO_1_LOOP_CHECK))
+    self.assertRaises(py_utils.TimeoutException, action.RunAction, self._tab)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/media_action.js b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/media_action.js
new file mode 100644
index 0000000..e9df62d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/media_action.js
@@ -0,0 +1,59 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+// This file provides common functions for media actions.
+window.__findMediaElements = function(selector) {
+  // Returns elements matching the selector, otherwise returns the first video
+  // or audio tag element that can be found.
+  // If selector == 'all', returns all media elements.
+  if (selector == 'all') {
+    return document.querySelectorAll('video, audio');
+  } else if (selector) {
+    return document.querySelectorAll(selector);
+  } else {
+    var media = document.getElementsByTagName('video');
+    if (media.length > 0) {
+      return [media[0]];
+    } else {
+      media = document.getElementsByTagName('audio');
+      if (media.length > 0) {
+        return [media[0]];
+      }
+    }
+  }
+  console.error('Could not find any media elements matching: ' + selector);
+  return [];
+};
+
+window.__hasEventCompleted = function(selector, event_name) {
+  // Return true if the event_name fired for media satisfying the selector.
+  var mediaElements = window.__findMediaElements(selector);
+  for (var i = 0; i < mediaElements.length; i++) {
+    if (!mediaElements[i][event_name + '_completed'])
+      return false;
+  }
+  return true;
+};
+
+window.__registerHTML5ErrorEvents = function(element) {
+  // Listens to HTML5 media errors.
+  function onError(e) {
+    window.__error = 'Media error: ' + e.type + ', code:' + e.target.error.code;
+    throw new Error(window.__error);
+  }
+  element.addEventListener('error', onError);
+  element.addEventListener('abort', onError);
+};
+
+window.__registerHTML5EventCompleted = function(element, event_name) {
+  // Logs |even_name| on element when completed.
+  var logEventHappened = function(e) {
+    element[e.type + '_completed'] = true;
+    element.removeEventListener(event_name, logEventHappened);
+  }
+  element.addEventListener(event_name, logEventHappened);
+};
+
+window.__error = null;
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/media_action.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/media_action.py
new file mode 100644
index 0000000..6c97588
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/media_action.py
@@ -0,0 +1,47 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Common media action functions."""
+
+import logging
+
+from telemetry.internal.actions import page_action
+from telemetry.internal.actions import utils
+
+import py_utils
+
+
+class MediaAction(page_action.PageAction):
+  def WillRunAction(self, tab):
+    """Loads the common media action JS code prior to running the action."""
+    utils.InjectJavaScript(tab, 'media_action.js')
+
+  def RunAction(self, tab):
+    super(MediaAction, self).RunAction(tab)
+
+  def WaitForEvent(self, tab, selector, event_name, timeout_in_seconds):
+    """Halts media action until the selector's event is fired.
+
+    Args:
+      tab: The tab to check for event on.
+      selector: Media element selector.
+      event_name: Name of the event to check if fired or not.
+      timeout_in_seconds: Timeout to check for event, throws an exception if
+          not fired.
+    """
+    py_utils.WaitFor(
+        lambda: self.HasEventCompletedOrError(tab, selector, event_name),
+        timeout=timeout_in_seconds)
+
+  def HasEventCompletedOrError(self, tab, selector, event_name):
+    if tab.EvaluateJavaScript(
+        'window.__hasEventCompleted({{ selector }}, {{ event_name }});',
+        selector=selector, event_name=event_name):
+      return True
+    error = tab.EvaluateJavaScript('window.__error')
+    if error:
+      logging.error('Detected media error while waiting for %s: %s', event_name,
+                    error)
+      return True
+    return False
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/mouse_click.js b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/mouse_click.js
new file mode 100644
index 0000000..e85239a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/mouse_click.js
@@ -0,0 +1,40 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+'use strict';
+
+(function() {
+  function MouseClickAction(opt_callback) {
+    this.callback_ = opt_callback;
+  }
+
+  MouseClickAction.prototype.start = function(options) {
+    this.click_(options.element);
+  };
+
+  MouseClickAction.prototype.click_ = function(element) {
+    var triggerMouseEvent = this.triggerMouseEvent_;
+    var callback = this.callback_;
+    triggerMouseEvent(element, 'mouseover');
+    triggerMouseEvent(element, 'mousedown');
+    // ~100ms is typical for a mouse click's elapsed time.
+    window.setTimeout(
+      function() {
+        triggerMouseEvent(element, 'mouseup');
+        triggerMouseEvent(element, 'click', callback);
+      }, 100);
+  };
+
+  MouseClickAction.prototype.triggerMouseEvent_ = function(
+      node, eventType, callback) {
+    var clickEvent = document.createEvent('MouseEvents');
+    clickEvent.initEvent(eventType, true, true);
+    node.dispatchEvent(clickEvent);
+    if (callback) {
+      window.setTimeout(callback, 0);
+    }
+  };
+
+  window.__MouseClickAction = MouseClickAction;
+})();
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/mouse_click.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/mouse_click.py
new file mode 100644
index 0000000..300bc4b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/mouse_click.py
@@ -0,0 +1,36 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.internal.actions import page_action
+from telemetry.internal.actions import utils
+
+
+class MouseClickAction(page_action.PageAction):
+  def __init__(self, selector=None):
+    super(MouseClickAction, self).__init__()
+    self._selector = selector
+
+  def WillRunAction(self, tab):
+    """Load the mouse click JS code prior to running the action."""
+    super(MouseClickAction, self).WillRunAction(tab)
+    utils.InjectJavaScript(tab, 'mouse_click.js')
+    tab.ExecuteJavaScript("""
+        window.__mouseClickActionDone = false;
+        window.__mouseClickAction = new __MouseClickAction(function() {
+          window.__mouseClickActionDone = true;
+        });""")
+
+  def RunAction(self, tab):
+    code = '''
+        function(element, info) {
+          if (!element) {
+            throw Error('Cannot find element: ' + info);
+          }
+          window.__mouseClickAction.start({
+            element: element
+          });
+        }'''
+    page_action.EvaluateCallbackWithElement(
+        tab, code, selector=self._selector)
+    tab.WaitForJavaScriptCondition('window.__mouseClickActionDone', timeout=60)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/mouse_click_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/mouse_click_unittest.py
new file mode 100644
index 0000000..6ad06c7
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/mouse_click_unittest.py
@@ -0,0 +1,50 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.core import exceptions
+from telemetry.internal.actions import mouse_click
+from telemetry.testing import tab_test_case
+
+
+class MouseClickActionTest(tab_test_case.TabTestCase):
+
+  def testMouseClickAction(self):
+    self.Navigate('blank.html')
+
+    self._tab.ExecuteJavaScript("""
+        (function() {
+           function createElement(id, textContent) {
+             var el = document.createElement("div");
+             el.id = id;
+             el.textContent = textContent;
+             document.body.appendChild(el);
+           }
+
+           createElement('test-1', 'foo');
+        })();""")
+    i = mouse_click.MouseClickAction(selector='#test-1')
+    i.WillRunAction(self._tab)
+    i.RunAction(self._tab)
+    self.assertTrue(self._tab.EvaluateJavaScript(
+        'window.__mouseClickActionDone'))
+
+  def testMouseClickActionOnNonExistingElement(self):
+    self.Navigate('blank.html')
+
+    self._tab.ExecuteJavaScript("""
+        (function() {
+           function createElement(id, textContent) {
+             var el = document.createElement("div");
+             el.id = id;
+             el.textContent = textContent;
+             document.body.appendChild(el);
+           }
+
+           createElement('test-1', 'foo');
+        })();""")
+    i = mouse_click.MouseClickAction(selector='#test-2')
+    i.WillRunAction(self._tab)
+    def WillFail():
+      i.RunAction(self._tab)
+    self.assertRaises(exceptions.EvaluateException, WillFail)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/navigate.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/navigate.py
new file mode 100644
index 0000000..c2a81fd
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/navigate.py
@@ -0,0 +1,30 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import time
+
+from telemetry.internal.actions import page_action
+
+
+class NavigateAction(page_action.PageAction):
+  def __init__(self, url, script_to_evaluate_on_commit=None,
+               timeout_in_seconds=60):
+    super(NavigateAction, self).__init__()
+    assert url, 'Must specify url for navigate action'
+    self._url = url
+    self._script_to_evaluate_on_commit = script_to_evaluate_on_commit
+    self._timeout_in_seconds = timeout_in_seconds
+
+  def RunAction(self, tab):
+    start_time = time.time()
+    tab.Navigate(self._url,
+                 self._script_to_evaluate_on_commit,
+                 self._timeout_in_seconds)
+
+    time_left_in_seconds = (start_time + self._timeout_in_seconds
+        - time.time())
+    time_left_in_seconds = max(0, time_left_in_seconds)
+    tab.WaitForDocumentReadyStateToBeInteractiveOrBetter(
+        time_left_in_seconds)
+    tab.WaitForFrameToBeDisplayed()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/navigate_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/navigate_unittest.py
new file mode 100644
index 0000000..a327d84
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/navigate_unittest.py
@@ -0,0 +1,15 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.internal.actions import navigate
+from telemetry.testing import tab_test_case
+
+
+class NavigateActionTest(tab_test_case.TabTestCase):
+  def testNavigateAction(self):
+    i = navigate.NavigateAction(url=self.UrlOfUnittestFile('blank.html'))
+    i.RunAction(self._tab)
+    self.assertEquals(
+        self._tab.EvaluateJavaScript('document.location.pathname;'),
+        '/blank.html')
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/page_action.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/page_action.py
new file mode 100644
index 0000000..d85d47a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/page_action.py
@@ -0,0 +1,144 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from py_trace_event import trace_event
+
+from telemetry import decorators
+from telemetry.util import js_template
+
+
+GESTURE_SOURCE_DEFAULT = 'DEFAULT'
+GESTURE_SOURCE_MOUSE = 'MOUSE'
+GESTURE_SOURCE_TOUCH = 'TOUCH'
+SUPPORTED_GESTURE_SOURCES = (GESTURE_SOURCE_DEFAULT,
+                             GESTURE_SOURCE_MOUSE,
+                             GESTURE_SOURCE_TOUCH)
+
+class PageActionNotSupported(Exception):
+  pass
+
+class PageActionFailed(Exception):
+  pass
+
+
+class PageAction(object):
+  """Represents an action that a user might try to perform to a page."""
+
+  __metaclass__ = trace_event.TracedMetaClass
+
+  def WillRunAction(self, tab):
+    """Override to do action-specific setup before
+    Test.WillRunAction is called."""
+    pass
+
+  def RunAction(self, tab):
+    raise NotImplementedError()
+
+  def CleanUp(self, tab):
+    pass
+
+def EvaluateCallbackWithElement(
+    tab, callback_js, selector=None, text=None, element_function=None,
+    wait=False, timeout_in_seconds=60):
+  """Evaluates the JavaScript callback with the given element.
+
+  The element may be selected via selector, text, or element_function.
+  Only one of these arguments must be specified.
+
+  Returns:
+    The callback's return value, if any. The return value must be
+    convertible to JSON.
+
+  Args:
+    tab: A telemetry.core.Tab object.
+    callback_js: The JavaScript callback to call (as string).
+        The callback receive 2 parameters: the element, and information
+        string about what method was used to retrieve the element.
+        Example: '''
+          function(element, info) {
+            if (!element) {
+              throw Error('Can not find element: ' + info);
+            }
+            element.click()
+          }'''
+    selector: A CSS selector describing the element.
+    text: The element must contains this exact text.
+    element_function: A JavaScript function (as string) that is used
+        to retrieve the element. For example:
+        '(function() { return foo.element; })()'.
+    wait: Whether to wait for the return value to be true.
+    timeout_in_seconds: The timeout for wait (if waiting).
+  """
+  count = 0
+  info_msg = ''
+  if element_function is not None:
+    count = count + 1
+    info_msg = js_template.Render(
+        'using element_function: {{ @code }}', code=element_function)
+  if selector is not None:
+    count = count + 1
+    info_msg = js_template.Render(
+        'using selector {{ selector }}', selector=selector)
+    element_function = js_template.Render(
+        'document.querySelector({{ selector }})', selector=selector)
+  if text is not None:
+    count = count + 1
+    info_msg = js_template.Render(
+        'using exact text match {{ text }}', text=text)
+    element_function = js_template.Render('''
+        (function() {
+          function _findElement(element, text) {
+            if (element.innerHTML == text) {
+              return element;
+            }
+
+            var childNodes = element.childNodes;
+            for (var i = 0, len = childNodes.length; i < len; ++i) {
+              var found = _findElement(childNodes[i], text);
+              if (found) {
+                return found;
+              }
+            }
+            return null;
+          }
+          return _findElement(document, {{ text }});
+        })()''',
+        text=text)
+
+  if count != 1:
+    raise PageActionFailed(
+        'Must specify 1 way to retrieve element, but %s was specified.' % count)
+
+  code = js_template.Render('''
+      (function() {
+        var element = {{ @element_function }};
+        var callback = {{ @callback_js }};
+        return callback(element, {{ info_msg }});
+      })()''',
+      element_function=element_function,
+      callback_js=callback_js,
+      info_msg=info_msg)
+
+  if wait:
+    tab.WaitForJavaScriptCondition(code, timeout=timeout_in_seconds)
+    return True
+  else:
+    return tab.EvaluateJavaScript(code)
+
+
+@decorators.Cache
+def IsGestureSourceTypeSupported(tab, gesture_source_type):
+  # TODO(dominikg): remove once support for
+  #                 'chrome.gpuBenchmarking.gestureSourceTypeSupported' has
+  #                 been rolled into reference build.
+  if tab.EvaluateJavaScript("""
+      typeof chrome.gpuBenchmarking.gestureSourceTypeSupported ===
+          'undefined'"""):
+    return (tab.browser.platform.GetOSName() != 'mac' or
+            gesture_source_type.lower() != 'touch')
+
+  return tab.EvaluateJavaScript("""
+      chrome.gpuBenchmarking.gestureSourceTypeSupported(
+          chrome.gpuBenchmarking.{{ @gesture_source_type }}_INPUT)""",
+      gesture_source_type=gesture_source_type.upper())
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/page_action_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/page_action_unittest.py
new file mode 100644
index 0000000..5f37135
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/page_action_unittest.py
@@ -0,0 +1,80 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Tests for page_action."""
+
+from telemetry.internal.actions import page_action
+from telemetry.testing import tab_test_case
+
+
+class PageActionTest(tab_test_case.TabTestCase):
+
+  def testEvaluateCallbackWithElement(self):
+    self.Navigate('blank.html')
+    self._tab.action_runner.ExecuteJavaScript('''
+        (function() {
+           function createElement(id, textContent) {
+             var el = document.createElement("div");
+             el.id = id;
+             el.textContent = textContent;
+             document.body.appendChild(el);
+           }
+
+           createElement('test-1', 'foo');
+           createElement('test-2', 'bar');
+           createElement('test-3', 'baz');
+        })();''')
+    self.assertEqual(
+        'foo',
+        page_action.EvaluateCallbackWithElement(
+            self._tab, 'function(el) { return el.textContent; }',
+            selector='#test-1'))
+    self.assertEqual(
+        'bar',
+        page_action.EvaluateCallbackWithElement(
+            self._tab, 'function(el) { return el.textContent; }',
+            text='bar'))
+    self.assertEqual(
+        'baz',
+        page_action.EvaluateCallbackWithElement(
+            self._tab, 'function(el) { return el.textContent; }',
+            element_function='document.getElementById("test-3")'))
+    self.assertEqual(
+        'baz',
+        page_action.EvaluateCallbackWithElement(
+            self._tab, 'function(el) { return el.textContent; }',
+            element_function='''
+                (function() {
+                  return document.getElementById("test-3");
+                })()'''))
+
+    # Test for when the element is not found.
+    self.assertEqual(
+        None,
+        page_action.EvaluateCallbackWithElement(
+            self._tab, 'function(el) { return el; }',
+            element_function='document.getElementById("test-4")'))
+
+    # Test the info message.
+    self.assertEqual(
+        'using selector "#test-1"',
+        page_action.EvaluateCallbackWithElement(
+            self._tab, 'function(el, info) { return info; }',
+            selector='#test-1'))
+
+  def testEvaluateCallbackWithElementWithConflictingParams(self):
+    def Evaluate1():
+      page_action.EvaluateCallbackWithElement(
+          self._tab, 'function() {}', selector='div', text='foo')
+    self.assertRaises(page_action.PageActionFailed, Evaluate1)
+
+    def Evaluate2():
+      page_action.EvaluateCallbackWithElement(
+          self._tab, 'function() {}', selector='div', element_function='foo')
+    self.assertRaises(page_action.PageActionFailed, Evaluate2)
+
+    def Evaluate3():
+      page_action.EvaluateCallbackWithElement(
+          self._tab, 'function() {}', text='foo', element_function='')
+    self.assertRaises(page_action.PageActionFailed, Evaluate3)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/pinch.js b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/pinch.js
new file mode 100644
index 0000000..4abb66e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/pinch.js
@@ -0,0 +1,77 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file provides the PinchAction object, which zooms into or out of a
+// page by a given scale factor:
+//   1. var action = new __PinchAction(callback)
+//   2. action.start(pinch_options)
+'use strict';
+
+(function() {
+
+  function PinchGestureOptions(opt_options) {
+    if (opt_options) {
+      this.element_ = opt_options.element;
+      this.left_anchor_ratio_ = opt_options.left_anchor_ratio;
+      this.top_anchor_ratio_ = opt_options.top_anchor_ratio;
+      this.scale_factor_ = opt_options.scale_factor;
+      this.speed_ = opt_options.speed;
+    } else {
+      this.element_ = document.body;
+      this.left_anchor_ratio_ = 0.5;
+      this.top_anchor_ratio_ = 0.5;
+      this.scale_factor_ = 2.0;
+      this.speed_ = 800;
+    }
+  }
+
+  function supportedByBrowser() {
+    return !!(window.chrome &&
+              chrome.gpuBenchmarking &&
+              chrome.gpuBenchmarking.pinchBy &&
+              chrome.gpuBenchmarking.visualViewportHeight &&
+              chrome.gpuBenchmarking.visualViewportWidth);
+  }
+
+  // This class zooms into or out of a page, given a number of pixels for
+  // the synthetic pinch gesture to cover.
+  function PinchAction(opt_callback) {
+    var self = this;
+
+    this.beginMeasuringHook = function() {};
+    this.endMeasuringHook = function() {};
+
+    this.callback_ = opt_callback;
+  };
+
+  PinchAction.prototype.start = function(opt_options) {
+    this.options_ = new PinchGestureOptions(opt_options);
+
+    requestAnimationFrame(this.startPass_.bind(this));
+  };
+
+  PinchAction.prototype.startPass_ = function() {
+    this.beginMeasuringHook();
+
+    var rect = __GestureCommon_GetBoundingVisibleRect(this.options_.element_);
+    var anchor_left =
+        rect.left + rect.width * this.options_.left_anchor_ratio_;
+    var anchor_top =
+        rect.top + rect.height * this.options_.top_anchor_ratio_;
+    chrome.gpuBenchmarking.pinchBy(this.options_.scale_factor_,
+                                   anchor_left, anchor_top,
+                                   this.onGestureComplete_.bind(this),
+                                   this.options_.speed_);
+  };
+
+  PinchAction.prototype.onGestureComplete_ = function() {
+    this.endMeasuringHook();
+
+    if (this.callback_)
+      this.callback_();
+  };
+
+  window.__PinchAction = PinchAction;
+  window.__PinchAction_SupportedByBrowser = supportedByBrowser;
+})();
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/pinch.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/pinch.py
new file mode 100644
index 0000000..9d81bfb
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/pinch.py
@@ -0,0 +1,74 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.internal.actions import page_action
+from telemetry.internal.actions import utils
+from telemetry.util import js_template
+
+
+class PinchAction(page_action.PageAction):
+  def __init__(self, selector=None, text=None, element_function=None,
+               left_anchor_ratio=0.5, top_anchor_ratio=0.5,
+               scale_factor=None, speed_in_pixels_per_second=800,
+               synthetic_gesture_source=page_action.GESTURE_SOURCE_DEFAULT):
+    super(PinchAction, self).__init__()
+    self._selector = selector
+    self._text = text
+    self._element_function = element_function
+    self._left_anchor_ratio = left_anchor_ratio
+    self._top_anchor_ratio = top_anchor_ratio
+    self._scale_factor = scale_factor
+    self._speed = speed_in_pixels_per_second
+    self._synthetic_gesture_source = ('chrome.gpuBenchmarking.%s_INPUT' %
+                                      synthetic_gesture_source)
+
+    if (self._selector is None and self._text is None and
+        self._element_function is None):
+      self._element_function = 'document.body'
+
+  def WillRunAction(self, tab):
+    utils.InjectJavaScript(tab, 'gesture_common.js')
+    utils.InjectJavaScript(tab, 'pinch.js')
+
+    # Fail if browser doesn't support synthetic pinch gestures.
+    if not tab.EvaluateJavaScript('window.__PinchAction_SupportedByBrowser()'):
+      raise page_action.PageActionNotSupported(
+          'Synthetic pinch not supported for this browser')
+
+    tab.ExecuteJavaScript("""
+        window.__pinchActionDone = false;
+        window.__pinchAction = new __PinchAction(function() {
+          window.__pinchActionDone = true;
+        });""")
+
+  @staticmethod
+  def _GetDefaultScaleFactorForPage(tab):
+    current_scale_factor = tab.EvaluateJavaScript(
+        'window.outerWidth / window.innerWidth')
+    return 3.0 / current_scale_factor
+
+  def RunAction(self, tab):
+    scale_factor = (self._scale_factor if self._scale_factor else
+                    PinchAction._GetDefaultScaleFactorForPage(tab))
+    code = js_template.Render('''
+        function(element, info) {
+          if (!element) {
+            throw Error('Cannot find element: ' + info);
+          }
+          window.__pinchAction.start({
+            element: element,
+            left_anchor_ratio: {{ left_anchor_ratio }},
+            top_anchor_ratio: {{ top_anchor_ratio }},
+            scale_factor: {{ scale_factor }},
+            speed: {{ speed }}
+          });
+        }''',
+        left_anchor_ratio=self._left_anchor_ratio,
+        top_anchor_ratio=self._top_anchor_ratio,
+        scale_factor=scale_factor,
+        speed=self._speed)
+    page_action.EvaluateCallbackWithElement(
+        tab, code, selector=self._selector, text=self._text,
+        element_function=self._element_function)
+    tab.WaitForJavaScriptCondition('window.__pinchActionDone', timeout=60)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/pinch_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/pinch_unittest.py
new file mode 100644
index 0000000..6d2aa50
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/pinch_unittest.py
@@ -0,0 +1,39 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.internal.actions import page_action
+from telemetry.testing import tab_test_case
+
+
+class PinchActionTest(tab_test_case.TabTestCase):
+  def setUp(self):
+    super(PinchActionTest, self).setUp()
+
+  def testPinchByApiCalledWithCorrectArguments(self):
+    self.Navigate('blank.html')
+    if not page_action.IsGestureSourceTypeSupported(self._tab, 'touch'):
+      return
+
+    action_runner = self._tab.action_runner
+    action_runner.ExecuteJavaScript('''
+        chrome.gpuBenchmarking.pinchBy = function(
+            scaleFactor, anchorLeft, anchorTop, callback, speed) {
+          window.__test_scaleFactor = scaleFactor;
+          window.__test_anchorLeft = anchorLeft;
+          window.__test_anchorTop = anchorTop;
+          window.__test_callback = callback;
+          window.__test_speed = speed;
+          window.__pinchActionDone = true;
+        };''')
+    action_runner.PinchPage(scale_factor=2)
+    self.assertEqual(
+        2, action_runner.EvaluateJavaScript('window.__test_scaleFactor'))
+    self.assertTrue(
+        action_runner.EvaluateJavaScript('!isNaN(window.__test_anchorLeft)'))
+    self.assertTrue(
+        action_runner.EvaluateJavaScript('!isNaN(window.__test_anchorTop)'))
+    self.assertTrue(
+        action_runner.EvaluateJavaScript('!!window.__test_callback'))
+    self.assertEqual(
+        800, action_runner.EvaluateJavaScript('window.__test_speed'))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/play.js b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/play.js
new file mode 100644
index 0000000..c646d78
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/play.js
@@ -0,0 +1,35 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file performs actions on media elements.
+(function() {
+  function playMedia(selector) {
+    // Performs the "Play" action on media satisfying selector.
+    var mediaElements = window.__findMediaElements(selector);
+    for (var i = 0; i < mediaElements.length; i++) {
+      console.log('Playing element: ' + mediaElements[i].src);
+      play(mediaElements[i]);
+    }
+  }
+
+  function play(element) {
+    if (element instanceof HTMLMediaElement)
+      playHTML5Element(element);
+    else
+      throw new Error('Can not play non HTML5 media elements.');
+  }
+
+  function playHTML5Element(element) {
+    window.__registerHTML5ErrorEvents(element);
+    window.__registerHTML5EventCompleted(element, 'playing');
+    window.__registerHTML5EventCompleted(element, 'ended');
+
+    var willPlayEvent = document.createEvent('Event');
+    willPlayEvent.initEvent('willPlay', false, false);
+    element.dispatchEvent(willPlayEvent);
+    element.play();
+  }
+
+  window.__playMedia = playMedia;
+})();
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/play.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/play.py
new file mode 100644
index 0000000..f740195
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/play.py
@@ -0,0 +1,50 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""A Telemetry page_action that performs the "play" action on media elements.
+
+Media elements can be specified by a selector argument. If no selector is
+defined then then the action attempts to play the first video element or audio
+element on the page. A selector can also be 'all' to play all media elements.
+
+Other arguments to use are: playing_event_timeout_in_seconds and
+ended_event_timeout_in_seconds, which forces the action to wait until
+playing and ended events get fired respectively.
+"""
+
+from telemetry.core import exceptions
+from telemetry.internal.actions import media_action
+from telemetry.internal.actions import page_action
+from telemetry.internal.actions import utils
+
+
+class PlayAction(media_action.MediaAction):
+  def __init__(self, selector=None,
+               playing_event_timeout_in_seconds=0,
+               ended_event_timeout_in_seconds=0):
+    super(PlayAction, self).__init__()
+    self._selector = selector if selector else ''
+    self._playing_event_timeout_in_seconds = playing_event_timeout_in_seconds
+    self._ended_event_timeout_in_seconds = ended_event_timeout_in_seconds
+
+  def WillRunAction(self, tab):
+    """Load the media metrics JS code prior to running the action."""
+    super(PlayAction, self).WillRunAction(tab)
+    utils.InjectJavaScript(tab, 'play.js')
+
+  def RunAction(self, tab):
+    try:
+      tab.ExecuteJavaScript(
+          'window.__playMedia({{ selector }});', selector=self._selector)
+      # Check if we need to wait for 'playing' event to fire.
+      if self._playing_event_timeout_in_seconds > 0:
+        self.WaitForEvent(tab, self._selector, 'playing',
+                          self._playing_event_timeout_in_seconds)
+      # Check if we need to wait for 'ended' event to fire.
+      if self._ended_event_timeout_in_seconds > 0:
+        self.WaitForEvent(tab, self._selector, 'ended',
+                          self._ended_event_timeout_in_seconds)
+    except exceptions.EvaluateException:
+      raise page_action.PageActionFailed('Cannot play media element(s) with '
+                                         'selector = %s.' % self._selector)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/play_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/play_unittest.py
new file mode 100644
index 0000000..c497cf9
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/play_unittest.py
@@ -0,0 +1,112 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry import decorators
+from telemetry.internal.actions import play
+from telemetry.testing import tab_test_case
+
+import py_utils
+
+
+AUDIO_1_PLAYING_CHECK = 'window.__hasEventCompleted("#audio_1", "playing");'
+VIDEO_1_PLAYING_CHECK = 'window.__hasEventCompleted("#video_1", "playing");'
+VIDEO_1_ENDED_CHECK = 'window.__hasEventCompleted("#video_1", "ended");'
+
+
+class PlayActionTest(tab_test_case.TabTestCase):
+
+  def setUp(self):
+    tab_test_case.TabTestCase.setUp(self)
+    self.Navigate('video_test.html')
+
+  @decorators.Disabled('android', 'linux')  # crbug.com/418577
+  def testPlayWithNoSelector(self):
+    """Tests that with no selector Play action plays first video element."""
+    action = play.PlayAction(playing_event_timeout_in_seconds=5)
+    action.WillRunAction(self._tab)
+    # Both videos not playing before running action.
+    self.assertFalse(self._tab.EvaluateJavaScript(VIDEO_1_PLAYING_CHECK))
+    self.assertFalse(self._tab.EvaluateJavaScript(AUDIO_1_PLAYING_CHECK))
+    action.RunAction(self._tab)
+    # Assert only first video has played.
+    self.assertTrue(self._tab.EvaluateJavaScript(VIDEO_1_PLAYING_CHECK))
+    self.assertFalse(self._tab.EvaluateJavaScript(AUDIO_1_PLAYING_CHECK))
+
+  @decorators.Disabled('android', 'linux')  # crbug.com/418577
+  def testPlayWithVideoSelector(self):
+    """Tests that Play action plays video element matching selector."""
+    action = play.PlayAction(selector='#video_1',
+                             playing_event_timeout_in_seconds=5)
+    action.WillRunAction(self._tab)
+    # Both videos not playing before running action.
+    self.assertFalse(self._tab.EvaluateJavaScript(VIDEO_1_PLAYING_CHECK))
+    self.assertFalse(self._tab.EvaluateJavaScript(AUDIO_1_PLAYING_CHECK))
+    action.RunAction(self._tab)
+    # Assert only video matching selector has played.
+    self.assertTrue(self._tab.EvaluateJavaScript(VIDEO_1_PLAYING_CHECK))
+    self.assertFalse(self._tab.EvaluateJavaScript(AUDIO_1_PLAYING_CHECK))
+
+  @decorators.Disabled('android', 'linux')  # crbug.com/418577
+  def testPlayWithAllSelector(self):
+    """Tests that Play action plays all video elements with selector='all'."""
+    action = play.PlayAction(selector='all',
+                             playing_event_timeout_in_seconds=5)
+    action.WillRunAction(self._tab)
+    # Both videos not playing before running action.
+    self.assertFalse(self._tab.EvaluateJavaScript(VIDEO_1_PLAYING_CHECK))
+    self.assertFalse(self._tab.EvaluateJavaScript(AUDIO_1_PLAYING_CHECK))
+    action.RunAction(self._tab)
+    # Assert all media elements played.
+    self.assertTrue(self._tab.EvaluateJavaScript(VIDEO_1_PLAYING_CHECK))
+    self.assertTrue(self._tab.EvaluateJavaScript(AUDIO_1_PLAYING_CHECK))
+
+  # http://crbug.com/273887
+  @decorators.Disabled('linux')  # crbug.com/418577
+  def testPlayWaitForPlayTimeout(self):
+    """Tests that wait_for_playing timeouts if video does not play."""
+    action = play.PlayAction(selector='#video_1',
+                             playing_event_timeout_in_seconds=5)
+    action.WillRunAction(self._tab)
+    self._tab.EvaluateJavaScript('document.getElementById("video_1").src = ""')
+    self.assertFalse(self._tab.EvaluateJavaScript(VIDEO_1_PLAYING_CHECK))
+    self.assertRaises(py_utils.TimeoutException, action.RunAction, self._tab)
+
+  @decorators.Disabled('android', 'linux')  # crbug.com/418577
+  def testPlayWaitForEnded(self):
+    """Tests that wait_for_ended waits for video to end."""
+    action = play.PlayAction(selector='#video_1',
+                             ended_event_timeout_in_seconds=5)
+    action.WillRunAction(self._tab)
+    # Assert video not playing before running action.
+    self.assertFalse(self._tab.EvaluateJavaScript(VIDEO_1_PLAYING_CHECK))
+    self.assertFalse(self._tab.EvaluateJavaScript(VIDEO_1_ENDED_CHECK))
+    action.RunAction(self._tab)
+    # Assert video ended.
+    self.assertTrue(self._tab.EvaluateJavaScript(VIDEO_1_ENDED_CHECK))
+
+  @decorators.Disabled('linux')  # crbug.com/418577
+  def testPlayWithoutWaitForEnded(self):
+    """Tests that wait_for_ended waits for video to end."""
+    action = play.PlayAction(selector='#video_1',
+                             ended_event_timeout_in_seconds=0)
+    action.WillRunAction(self._tab)
+    # Assert video not playing before running action.
+    self.assertFalse(self._tab.EvaluateJavaScript(VIDEO_1_PLAYING_CHECK))
+    self.assertFalse(self._tab.EvaluateJavaScript(VIDEO_1_ENDED_CHECK))
+    action.RunAction(self._tab)
+    # Assert video did not end.
+    self.assertFalse(self._tab.EvaluateJavaScript(VIDEO_1_ENDED_CHECK))
+
+  @decorators.Disabled('linux')  # crbug.com/418577
+  def testPlayWaitForEndedTimeout(self):
+    """Tests that action raises exception if timeout is reached."""
+    action = play.PlayAction(selector='#video_1',
+                             ended_event_timeout_in_seconds=0.1)
+    action.WillRunAction(self._tab)
+    # Assert video not playing before running action.
+    self.assertFalse(self._tab.EvaluateJavaScript(VIDEO_1_PLAYING_CHECK))
+    self.assertFalse(self._tab.EvaluateJavaScript(VIDEO_1_ENDED_CHECK))
+    self.assertRaises(py_utils.TimeoutException, action.RunAction, self._tab)
+    # Assert video did not end.
+    self.assertFalse(self._tab.EvaluateJavaScript(VIDEO_1_ENDED_CHECK))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/repaint_continuously.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/repaint_continuously.py
new file mode 100644
index 0000000..e1ad4f3
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/repaint_continuously.py
@@ -0,0 +1,36 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import time
+
+from telemetry.internal.actions import page_action
+
+import py_utils
+
+
+class RepaintContinuouslyAction(page_action.PageAction):
+  """Continuously repaints the visible content by requesting animation frames
+  until self.seconds have elapsed AND at least three RAFs have been fired. Times
+  out after max(60, self.seconds), if less than three RAFs were fired.
+  """
+
+  def __init__(self, seconds):
+    super(RepaintContinuouslyAction, self).__init__()
+    self._seconds = seconds
+
+  def RunAction(self, tab):
+    tab.ExecuteJavaScript(
+        'window.__rafCount = 0;'
+        'window.__rafFunction = function() {'
+          'window.__rafCount += 1;'
+          'window.webkitRequestAnimationFrame(window.__rafFunction);'
+        '};'
+        'window.webkitRequestAnimationFrame(window.__rafFunction);')
+
+    # Wait until at least self.seconds have elapsed AND min_rafs have been
+    # fired. Use a hard time-out after 60 seconds (or self.seconds).
+    time.sleep(self._seconds)
+    def HasMinRafs():
+      return tab.EvaluateJavaScript('window.__rafCount;') >= 3
+    py_utils.WaitFor(HasMinRafs, max(60 - self._seconds, 0))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/repeatable_scroll.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/repeatable_scroll.py
new file mode 100644
index 0000000..9ed09e0
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/repeatable_scroll.py
@@ -0,0 +1,50 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import numbers
+
+from telemetry.internal.actions import page_action
+from telemetry.internal.actions import utils
+from telemetry.web_perf import timeline_interaction_record
+
+
+class RepeatableScrollAction(page_action.PageAction):
+
+  def __init__(self, x_scroll_distance_ratio=0.0, y_scroll_distance_ratio=0.5,
+               repeat_count=0, repeat_delay_ms=250, timeout=60,
+               prevent_fling=None, speed=None):
+    super(RepeatableScrollAction, self).__init__()
+    self._x_scroll_distance_ratio = x_scroll_distance_ratio
+    self._y_scroll_distance_ratio = y_scroll_distance_ratio
+    self._repeat_count = repeat_count
+    self._repeat_delay_ms = repeat_delay_ms
+    self._windowsize = []
+    self._timeout = timeout
+    self._prevent_fling = prevent_fling
+    self._speed = speed
+
+  def WillRunAction(self, tab):
+    utils.InjectJavaScript(tab, 'gesture_common.js')
+    # Get the dimensions of the screen.
+    self._windowsize = tab.EvaluateJavaScript(
+        '[__GestureCommon_GetWindowWidth(),'
+        ' __GestureCommon_GetWindowHeight()]')
+    assert len(self._windowsize) == 2
+    assert all(isinstance(d, numbers.Number) for d in self._windowsize)
+
+  def RunAction(self, tab):
+    # Set up a browser driven repeating scroll. The delay between the scrolls
+    # should be unaffected by render thread responsivness (or lack there of).
+    tab.SynthesizeScrollGesture(
+        x=int(self._windowsize[0] / 2),
+        y=int(self._windowsize[1] / 2),
+        xDistance=int(self._x_scroll_distance_ratio * self._windowsize[0]),
+        yDistance=int(-self._y_scroll_distance_ratio * self._windowsize[1]),
+        preventFling=self._prevent_fling,
+        speed=self._speed,
+        repeatCount=self._repeat_count,
+        repeatDelayMs=self._repeat_delay_ms,
+        interactionMarkerName=timeline_interaction_record.GetJavaScriptMarker(
+            'Gesture_ScrollAction', [timeline_interaction_record.REPEATABLE]),
+        timeout=self._timeout)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/repeatable_scroll_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/repeatable_scroll_unittest.py
new file mode 100644
index 0000000..675f58b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/repeatable_scroll_unittest.py
@@ -0,0 +1,97 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry import decorators
+
+from telemetry.internal.actions import page_action
+from telemetry.internal.actions import repeatable_scroll
+from telemetry.internal.actions import utils
+from telemetry.internal.browser import browser_info as browser_info_module
+from telemetry.testing import tab_test_case
+
+
+class RepeatableScrollActionTest(tab_test_case.TabTestCase):
+
+  def setUp(self):
+    tab_test_case.TabTestCase.setUp(self)
+    self.Navigate('blank.html')
+    utils.InjectJavaScript(self._tab, 'gesture_common.js')
+
+    # Make page taller than window so it's scrollable.
+    self._tab.ExecuteJavaScript('document.body.style.height ='
+        '(3 * __GestureCommon_GetWindowHeight() + 1) + "px";')
+
+    self.assertEquals(
+        self._tab.EvaluateJavaScript('document.scrollingElement.scrollTop'), 0)
+
+    self._browser_info = browser_info_module.BrowserInfo(self._tab.browser)
+    self._window_height = int(self._tab.EvaluateJavaScript(
+        '__GestureCommon_GetWindowHeight()'))
+
+  # https://github.com/catapult-project/catapult/issues/3099
+  @decorators.Disabled('android')
+  def testRepeatableScrollActionNoRepeats(self):
+    if not self._browser_info.HasRepeatableSynthesizeScrollGesture():
+      return
+
+    expected_scroll = (self._window_height / 2) - 1
+
+    i = repeatable_scroll.RepeatableScrollAction(y_scroll_distance_ratio=0.5)
+    i.WillRunAction(self._tab)
+
+    i.RunAction(self._tab)
+
+    scroll_position = self._tab.EvaluateJavaScript(
+        'document.scrollingElement.scrollTop')
+    # We can only expect the final scroll position to be approximatly equal.
+    self.assertTrue(abs(scroll_position - expected_scroll) < 20,
+                    msg='scroll_position=%d;expected %d' % (scroll_position,
+                                                            expected_scroll))
+
+  # https://github.com/catapult-project/catapult/issues/3099
+  @decorators.Disabled('android')
+  def testRepeatableScrollActionTwoRepeats(self):
+    if not self._browser_info.HasRepeatableSynthesizeScrollGesture():
+      return
+
+    expected_scroll = ((self._window_height / 2) - 1) * 3
+
+    i = repeatable_scroll.RepeatableScrollAction(y_scroll_distance_ratio=0.5,
+                                                 repeat_count=2,
+                                                 repeat_delay_ms=1)
+    i.WillRunAction(self._tab)
+
+    i.RunAction(self._tab)
+
+    scroll_position = self._tab.EvaluateJavaScript(
+        'document.scrollingElement.scrollTop')
+    # We can only expect the final scroll position to be approximatly equal.
+    self.assertTrue(abs(scroll_position - expected_scroll) < 20,
+                    msg='scroll_position=%d;expected %d' % (scroll_position,
+                                                            expected_scroll))
+
+  # Regression test for crbug.com/627166
+  # TODO(ulan): enable for Android after catapult:#2475 is fixed.
+  @decorators.Disabled('all')
+  def testRepeatableScrollActionNoRepeatsZoomed(self):
+    if (not self._browser_info.HasRepeatableSynthesizeScrollGesture() or
+        not page_action.IsGestureSourceTypeSupported(self._tab, 'touch')):
+      return
+
+    self._tab.action_runner.PinchPage(scale_factor=0.1)
+
+    inner_height = self._tab.EvaluateJavaScript('window.innerHeight')
+    outer_height = self._tab.EvaluateJavaScript('window.outerHeight')
+
+    self.assertGreater(inner_height, outer_height)
+
+    i = repeatable_scroll.RepeatableScrollAction(y_scroll_distance_ratio=0.5)
+    i.WillRunAction(self._tab)
+    i.RunAction(self._tab)
+    # If scroll coordinates are computed incorrectly Chrome will crash with
+    # [FATAL:synthetic_gesture_target_base.cc(62)] Check failed:
+    # web_touch.touches[i].state != WebTouchPoint::StatePressed ||
+    # PointIsWithinContents(web_touch.touches[i].position.x,
+    # web_touch.touches[i].position.y). Touch coordinates are not within content
+    # bounds on TouchStart.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/scroll.js b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/scroll.js
new file mode 100644
index 0000000..2cc5650
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/scroll.js
@@ -0,0 +1,149 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file provides the ScrollAction object, which scrolls a page
+// to the bottom or for a specified distance:
+//   1. var action = new __ScrollAction(callback, opt_distance_func)
+//   2. action.start(scroll_options)
+'use strict';
+
+(function() {
+  var MAX_SCROLL_LENGTH_TIME_MS = 6250;
+
+  function ScrollGestureOptions(opt_options) {
+    if (opt_options) {
+      this.element_ = opt_options.element;
+      this.left_start_ratio_ = opt_options.left_start_ratio;
+      this.top_start_ratio_ = opt_options.top_start_ratio;
+      this.direction_ = opt_options.direction;
+      this.speed_ = opt_options.speed;
+      this.gesture_source_type_ = opt_options.gesture_source_type;
+    } else {
+      this.element_ = document.scrollingElement || document.body;
+      this.left_start_ratio_ = 0.5;
+      this.top_start_ratio_ = 0.5;
+      this.direction_ = 'down';
+      this.speed_ = 800;
+      this.gesture_source_type_ = chrome.gpuBenchmarking.DEFAULT_INPUT;
+    }
+  }
+
+  function supportedByBrowser() {
+    return !!(window.chrome &&
+              chrome.gpuBenchmarking &&
+              chrome.gpuBenchmarking.smoothScrollBy &&
+              chrome.gpuBenchmarking.visualViewportHeight &&
+              chrome.gpuBenchmarking.visualViewportWidth);
+  }
+
+  // This class scrolls a page from the top to the bottom once.
+  //
+  // The page is scrolled down by a single scroll gesture.
+  function ScrollAction(opt_callback, opt_distance_func) {
+    var self = this;
+
+    this.beginMeasuringHook = function() {};
+    this.endMeasuringHook = function() {};
+
+    this.callback_ = opt_callback;
+    this.distance_func_ = opt_distance_func;
+  }
+
+  ScrollAction.prototype.getScrollDistanceDown_ = function() {
+    var clientHeight;
+    // clientHeight is "special" for the body element.
+    if (this.element_ == document.body)
+      clientHeight = __GestureCommon_GetWindowHeight();
+    else
+      clientHeight = this.element_.clientHeight;
+
+    return this.element_.scrollHeight -
+           this.element_.scrollTop -
+           clientHeight;
+  };
+
+  ScrollAction.prototype.getScrollDistanceUp_ = function() {
+    return this.element_.scrollTop;
+  };
+
+  ScrollAction.prototype.getScrollDistanceRight_ = function() {
+    var clientWidth;
+    // clientWidth is "special" for the body element.
+    if (this.element_ == document.body)
+      clientWidth = __GestureCommon_GetWindowWidth();
+    else
+      clientWidth = this.element_.clientWidth;
+
+    return this.element_.scrollWidth - this.element_.scrollLeft - clientWidth;
+  };
+
+  ScrollAction.prototype.getScrollDistanceLeft_ = function() {
+    return this.element_.scrollLeft;
+  };
+
+  ScrollAction.prototype.getScrollDistance_ = function() {
+    if (this.distance_func_)
+      return this.distance_func_();
+
+    if (this.options_.direction_ == 'down') {
+      return this.getScrollDistanceDown_();
+    } else if (this.options_.direction_ == 'up') {
+      return this.getScrollDistanceUp_();
+    } else if (this.options_.direction_ == 'right') {
+      return this.getScrollDistanceRight_();
+    } else if (this.options_.direction_ == 'left') {
+      return this.getScrollDistanceLeft_();
+    } else if (this.options_.direction_ == 'upleft') {
+      return Math.min(this.getScrollDistanceUp_(),
+                      this.getScrollDistanceLeft_());
+    } else if (this.options_.direction_ == 'upright') {
+      return Math.min(this.getScrollDistanceUp_(),
+                      this.getScrollDistanceRight_());
+    } else if (this.options_.direction_ == 'downleft') {
+      return Math.min(this.getScrollDistanceDown_(),
+                      this.getScrollDistanceLeft_());
+    } else if (this.options_.direction_ == 'downright') {
+      return Math.min(this.getScrollDistanceDown_(),
+                      this.getScrollDistanceRight_());
+    }
+  };
+
+  ScrollAction.prototype.start = function(opt_options) {
+    this.options_ = new ScrollGestureOptions(opt_options);
+    // Assign this.element_ here instead of constructor, because the constructor
+    // ensures this method will be called after the document is loaded.
+    this.element_ = this.options_.element_;
+    requestAnimationFrame(this.startGesture_.bind(this));
+  };
+
+  ScrollAction.prototype.startGesture_ = function() {
+    this.beginMeasuringHook();
+
+    var max_scroll_length_pixels = (MAX_SCROLL_LENGTH_TIME_MS / 1000) *
+        this.options_.speed_;
+    var distance = Math.min(max_scroll_length_pixels,
+                            this.getScrollDistance_());
+
+    var rect = __GestureCommon_GetBoundingVisibleRect(this.options_.element_);
+    var start_left =
+        rect.left + rect.width * this.options_.left_start_ratio_;
+    var start_top =
+        rect.top + rect.height * this.options_.top_start_ratio_;
+    chrome.gpuBenchmarking.smoothScrollBy(
+        distance, this.onGestureComplete_.bind(this), start_left, start_top,
+        this.options_.gesture_source_type_, this.options_.direction_,
+        this.options_.speed_);
+  };
+
+  ScrollAction.prototype.onGestureComplete_ = function() {
+    this.endMeasuringHook();
+
+    // We're done.
+    if (this.callback_)
+      this.callback_();
+  };
+
+  window.__ScrollAction = ScrollAction;
+  window.__ScrollAction_SupportedByBrowser = supportedByBrowser;
+})();
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/scroll.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/scroll.py
new file mode 100644
index 0000000..dc4f165
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/scroll.py
@@ -0,0 +1,110 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.internal.actions import page_action
+from telemetry.internal.actions import utils
+from telemetry.util import js_template
+
+
+class ScrollAction(page_action.PageAction):
+  # TODO(chrishenry): Ignore attributes, to be deleted when usage in
+  # other repo is cleaned up.
+  def __init__(self, selector=None, text=None, element_function=None,
+               left_start_ratio=0.5, top_start_ratio=0.5, direction='down',
+               distance=None, distance_expr=None,
+               speed_in_pixels_per_second=800, use_touch=False,
+               synthetic_gesture_source=page_action.GESTURE_SOURCE_DEFAULT):
+    super(ScrollAction, self).__init__()
+    if direction not in ('down', 'up', 'left', 'right',
+                         'downleft', 'downright',
+                         'upleft', 'upright'):
+      raise page_action.PageActionNotSupported(
+          'Invalid scroll direction: %s' % self.direction)
+    self._selector = selector
+    self._text = text
+    self._element_function = element_function
+    self._left_start_ratio = left_start_ratio
+    self._top_start_ratio = top_start_ratio
+    self._direction = direction
+    self._speed = speed_in_pixels_per_second
+    self._use_touch = use_touch
+    self._synthetic_gesture_source = ('chrome.gpuBenchmarking.%s_INPUT' %
+                                      synthetic_gesture_source)
+
+    self._distance_func = js_template.RenderValue(None)
+    if distance:
+      assert not distance_expr
+      distance_expr = str(distance)
+    if distance_expr:
+      self._distance_func = js_template.Render(
+          'function() { return 0 + {{ @expr }}; }', expr=distance_expr)
+
+  def WillRunAction(self, tab):
+    if self._direction in ('downleft', 'downright', 'upleft', 'upright'):
+      # Diagonal scrolling support was added in Chrome branch number 2332.
+      branch_num = (
+          tab.browser._browser_backend.devtools_client.GetChromeBranchNumber())
+      if branch_num < 2332:
+        raise ValueError('Diagonal scrolling requires Chrome branch number'
+                         ' 2332 or later. Found branch number %d' %
+                         branch_num)
+    utils.InjectJavaScript(tab, 'gesture_common.js')
+    utils.InjectJavaScript(tab, 'scroll.js')
+
+    # Fail if browser doesn't support synthetic scroll gestures.
+    if not tab.EvaluateJavaScript(
+        'window.__ScrollAction_SupportedByBrowser()'):
+      raise page_action.PageActionNotSupported(
+          'Synthetic scroll not supported for this browser')
+
+    # Fail if this action requires touch and we can't send touch events.
+    if self._use_touch:
+      if not page_action.IsGestureSourceTypeSupported(tab, 'touch'):
+        raise page_action.PageActionNotSupported(
+            'Touch scroll not supported for this browser')
+
+      if (self._synthetic_gesture_source ==
+          'chrome.gpuBenchmarking.MOUSE_INPUT'):
+        raise page_action.PageActionNotSupported(
+            'Scroll requires touch on this page but mouse input was requested')
+
+    tab.ExecuteJavaScript("""
+        window.__scrollActionDone = false;
+        window.__scrollAction = new __ScrollAction(
+            {{ @callback }}, {{ @distance }});""",
+        callback='function() { window.__scrollActionDone = true; }',
+        distance=self._distance_func)
+
+  def RunAction(self, tab):
+    if (self._selector is None and self._text is None and
+        self._element_function is None):
+      self._element_function = '(document.scrollingElement || document.body)'
+
+    gesture_source_type = self._synthetic_gesture_source
+    if self._use_touch:
+      gesture_source_type = 'chrome.gpuBenchmarking.TOUCH_INPUT'
+
+    code = js_template.Render('''
+        function(element, info) {
+          if (!element) {
+            throw Error('Cannot find element: ' + info);
+          }
+          window.__scrollAction.start({
+            element: element,
+            left_start_ratio: {{ left_start_ratio }},
+            top_start_ratio: {{ top_start_ratio }},
+            direction: {{ direction }},
+            speed: {{ speed }},
+            gesture_source_type: {{ @gesture_source_type }}
+          });
+        }''',
+        left_start_ratio=self._left_start_ratio,
+        top_start_ratio=self._top_start_ratio,
+        direction=self._direction,
+        speed=self._speed,
+        gesture_source_type=gesture_source_type)
+    page_action.EvaluateCallbackWithElement(
+        tab, code, selector=self._selector, text=self._text,
+        element_function=self._element_function)
+    tab.WaitForJavaScriptCondition('window.__scrollActionDone', timeout=60)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/scroll_bounce.js b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/scroll_bounce.js
new file mode 100644
index 0000000..ec0fc7f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/scroll_bounce.js
@@ -0,0 +1,60 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+'use strict';
+
+(function() {
+  function supportedByBrowser() {
+    return !!(window.chrome &&
+              chrome.gpuBenchmarking &&
+              chrome.gpuBenchmarking.scrollBounce &&
+              chrome.gpuBenchmarking.visualViewportHeight &&
+              chrome.gpuBenchmarking.visualViewportWidth);
+  }
+
+  function ScrollBounceAction(opt_callback) {
+    var self = this;
+
+    this.beginMeasuringHook = function() {};
+    this.endMeasuringHook = function() {};
+
+    this.callback_ = opt_callback;
+  }
+
+  ScrollBounceAction.prototype.start = function(options) {
+    this.options_ = options;
+    // Assign this.element_ here instead of constructor, because the constructor
+    // ensures this method will be called after the document is loaded.
+    this.element_ = this.options_.element;
+    requestAnimationFrame(this.startGesture_.bind(this));
+  };
+
+  ScrollBounceAction.prototype.startGesture_ = function() {
+    this.beginMeasuringHook();
+
+    var rect = __GestureCommon_GetBoundingVisibleRect(this.options_.element);
+    var start_left =
+        rect.left + rect.width * this.options_.left_start_ratio;
+    var start_top =
+        rect.top + rect.height * this.options_.top_start_ratio;
+    chrome.gpuBenchmarking.scrollBounce(this.options_.direction,
+                                        this.options_.distance,
+                                        this.options_.overscroll,
+                                        this.options_.repeat_count,
+                                        this.onGestureComplete_.bind(this),
+                                        start_left, start_top,
+                                        this.options_.speed);
+  };
+
+  ScrollBounceAction.prototype.onGestureComplete_ = function() {
+    this.endMeasuringHook();
+
+    // We're done.
+    if (this.callback_)
+      this.callback_();
+  };
+
+  window.__ScrollBounceAction = ScrollBounceAction;
+  window.__ScrollBounceAction_SupportedByBrowser = supportedByBrowser;
+})();
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/scroll_bounce.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/scroll_bounce.py
new file mode 100644
index 0000000..f293a4f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/scroll_bounce.py
@@ -0,0 +1,98 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.internal.actions import page_action
+from telemetry.internal.actions import utils
+from telemetry.util import js_template
+
+
+class ScrollBounceAction(page_action.PageAction):
+  def __init__(self, selector=None, text=None, element_function=None,
+               left_start_ratio=0.5, top_start_ratio=0.5,
+               direction='down', distance=100,
+               overscroll=10, repeat_count=10,
+               speed_in_pixels_per_second=400,
+               synthetic_gesture_source=page_action.GESTURE_SOURCE_DEFAULT):
+    super(ScrollBounceAction, self).__init__()
+    if direction not in ['down', 'up', 'left', 'right']:
+      raise page_action.PageActionNotSupported(
+          'Invalid scroll direction: %s' % self.direction)
+    self._selector = selector
+    self._text = text
+    self._element_function = element_function
+    self._left_start_ratio = left_start_ratio
+    self._top_start_ratio = top_start_ratio
+    # Should be big enough to do more than just hide the URL bar.
+    self._distance = distance
+    self._direction = direction
+    # This needs to be < height / repeat_count so we don't walk off the screen.
+    # We also probably don't want to spend more than a couple frames in
+    # overscroll since it may mask any synthetic delays.
+    self._overscroll = overscroll
+    # It's the transitions we really want to stress, make this big.
+    self._repeat_count = repeat_count
+    # 7 pixels per frame should be plenty of frames.
+    self._speed = speed_in_pixels_per_second
+    self._synthetic_gesture_source = ('chrome.gpuBenchmarking.%s_INPUT' %
+                                      synthetic_gesture_source)
+
+    if (self._selector is None and self._text is None and
+        self._element_function is None):
+      self._element_function = '(document.scrollingElement || document.body)'
+
+  def WillRunAction(self, tab):
+    utils.InjectJavaScript(tab, 'gesture_common.js')
+    utils.InjectJavaScript(tab, 'scroll_bounce.js')
+
+    # Fail if browser doesn't support synthetic scroll bounce gestures.
+    if not tab.EvaluateJavaScript(
+        'window.__ScrollBounceAction_SupportedByBrowser()'):
+      raise page_action.PageActionNotSupported(
+          'Synthetic scroll bounce not supported for this browser')
+
+    # Fail if we can't send touch events (bouncing is really only
+    # interesting for touch)
+    if not page_action.IsGestureSourceTypeSupported(tab, 'touch'):
+      raise page_action.PageActionNotSupported(
+          'Touch scroll not supported for this browser')
+
+    if (self._synthetic_gesture_source ==
+        'chrome.gpuBenchmarking.MOUSE_INPUT'):
+      raise page_action.PageActionNotSupported(
+          'ScrollBounce page action does not support mouse input')
+
+    tab.ExecuteJavaScript("""
+        window.__scrollBounceActionDone = false;
+        window.__scrollBounceAction = new __ScrollBounceAction(
+            function() { window.__scrollBounceActionDone = true; });""")
+
+  def RunAction(self, tab):
+    code = js_template.Render('''
+        function(element, info) {
+          if (!element) {
+            throw Error('Cannot find element: ' + info);
+          }
+          window.__scrollBounceAction.start({
+            element: element,
+            left_start_ratio: {{ left_start_ratio }},
+            top_start_ratio: {{ top_start_ratio }},
+            direction: {{ direction }},
+            distance: {{ distance }},
+            overscroll: {{ overscroll }},
+            repeat_count: {{ repeat_count }},
+            speed: {{ speed }}
+          });
+        }''',
+        left_start_ratio=self._left_start_ratio,
+        top_start_ratio=self._top_start_ratio,
+        direction=self._direction,
+        distance=self._distance,
+        overscroll=self._overscroll,
+        repeat_count=self._repeat_count,
+        speed=self._speed)
+    page_action.EvaluateCallbackWithElement(
+        tab, code, selector=self._selector, text=self._text,
+        element_function=self._element_function)
+    tab.WaitForJavaScriptCondition(
+        'window.__scrollBounceActionDone', timeout=60)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/scroll_to_element.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/scroll_to_element.py
new file mode 100644
index 0000000..e6c8cd1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/scroll_to_element.py
@@ -0,0 +1,85 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.internal.actions import page_action
+from telemetry.internal.actions.scroll import ScrollAction
+from telemetry.util import js_template
+
+
+class ScrollToElementAction(page_action.PageAction):
+
+
+  def __init__(self, selector=None, element_function=None,
+               container_selector=None, container_element_function=None,
+               speed_in_pixels_per_second=800):
+    """Perform scroll gesture on container until an element is in view.
+
+    Both the element and the container can be specified by a CSS selector
+    xor a JavaScript function, provided as a string, which returns an element.
+    The element is required so exactly one of selector and element_function
+    must be provided. The container is optional so at most one of
+    container_selector and container_element_function can be provided.
+    The container defaults to document.scrollingElement or document.body if
+    scrollingElement is not set.
+
+    Args:
+      selector: A CSS selector describing the element.
+      element_function: A JavaScript function (as string) that is used
+          to retrieve the element. For example:
+          'function() { return foo.element; }'.
+      container_selector: A CSS selector describing the container element.
+      container_element_function: A JavaScript function (as a string) that is
+          used to retrieve the container element.
+      speed_in_pixels_per_second: Speed to scroll.
+    """
+    super(ScrollToElementAction, self).__init__()
+    self._selector = selector
+    self._element_function = element_function
+    self._container_selector = container_selector
+    self._container_element_function = container_element_function
+    self._speed = speed_in_pixels_per_second
+    self._distance = None
+    self._direction = None
+    self._scroller = None
+    assert (self._selector or self._element_function), (
+        'Must have either selector or element function')
+
+  def WillRunAction(self, tab):
+    if self._selector:
+      element = js_template.Render(
+          'document.querySelector({{ selector }})', selector=self._selector)
+    else:
+      element = self._element_function
+
+    self._distance = tab.EvaluateJavaScript('''
+        (function(elem){
+          var rect = elem.getBoundingClientRect();
+          if (rect.bottom < 0) {
+            // The bottom of the element is above the viewport.
+            // Scroll up until the top of the element is on screen.
+            return rect.top - (window.innerHeight / 2);
+          }
+          if (rect.top - window.innerHeight >= 0) {
+            // rect.top provides the pixel offset of the element from the
+            // top of the page. Because that exceeds the viewport's height,
+            // we know that the element is below the viewport.
+            return rect.top - (window.innerHeight / 2);
+          }
+          return 0;
+        })({{ @element }});
+        ''', element=element)
+    self._direction = 'down' if self._distance > 0 else 'up'
+    self._distance = abs(self._distance)
+    self._scroller = ScrollAction(
+        direction=self._direction,
+        selector=self._container_selector,
+        element_function=self._container_element_function,
+        distance=self._distance,
+        speed_in_pixels_per_second=self._speed)
+
+  def RunAction(self, tab):
+    if self._distance == 0:  # Element is already in view.
+      return
+    self._scroller.WillRunAction(tab)
+    self._scroller.RunAction(tab)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/scroll_to_element_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/scroll_to_element_unittest.py
new file mode 100644
index 0000000..10469c5
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/scroll_to_element_unittest.py
@@ -0,0 +1,88 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry import decorators
+from telemetry.internal.actions import scroll_to_element
+from telemetry.internal.actions import utils
+from telemetry.testing import tab_test_case
+
+
+class ScrollToElementActionTest(tab_test_case.TabTestCase):
+
+  def _MakePageVerticallyScrollable(self):
+    # Make page taller than window so it's scrollable vertically.
+    self._tab.ExecuteJavaScript('document.body.style.height ='
+        '(6 * __GestureCommon_GetWindowHeight() + 1) + "px";')
+
+  def _VisibleAreaOfElement(self, selector='#element'):
+    return self._tab.EvaluateJavaScript("""
+      (function() {
+        var element = document.querySelector({{ selector }});
+        var rect = __GestureCommon_GetBoundingVisibleRect(element);
+        return rect.width * rect.height;
+      })()
+    """, selector=selector)
+
+  def _InsertContainer(self, theid='container'):
+    self._tab.ExecuteJavaScript("""
+      var container = document.createElement("div")
+      container.id = {{ theid }};
+      container.style.position = 'relative';
+      container.style.height = '100%';
+      document.body.appendChild(container);
+    """, theid=theid)
+
+  def _InsertElement(self, theid='element', container_selector='body'):
+    self._tab.ExecuteJavaScript("""
+      var container = document.querySelector({{ container_selector }});
+      var element = document.createElement("div");
+      element.id = {{ theid }};
+      element.textContent = 'My element';
+      element.style.position = 'absolute';
+      element.style.top = (__GestureCommon_GetWindowHeight() * 3) + "px";
+      container.appendChild(element);
+    """, theid=theid, container_selector=container_selector)
+
+  def setUp(self):
+    tab_test_case.TabTestCase.setUp(self)
+    self.Navigate('blank.html')
+    utils.InjectJavaScript(self._tab, 'gesture_common.js')
+
+  # https://github.com/catapult-project/catapult/issues/3099
+  @decorators.Disabled('android')
+  def testScrollToElement(self):
+    self._MakePageVerticallyScrollable()
+    self._InsertElement()
+    self.assertEquals(
+        self._tab.EvaluateJavaScript('document.scrollingElement.scrollTop'), 0)
+
+    # Before we scroll down the element should not be visible at all.
+    self.assertEquals(self._VisibleAreaOfElement(), 0)
+
+    i = scroll_to_element.ScrollToElementAction(selector='#element')
+    i.WillRunAction(self._tab)
+    i.RunAction(self._tab)
+
+    # After we scroll down at least some of the element should be visible.
+    self.assertGreater(self._VisibleAreaOfElement(selector='#element'), 0)
+
+  # https://github.com/catapult-project/catapult/issues/3099
+  @decorators.Disabled('android')
+  def testScrollContainerToElement(self):
+    self._MakePageVerticallyScrollable()
+    self._InsertContainer()
+    self._InsertElement(container_selector='#container')
+    self.assertEquals(
+        self._tab.EvaluateJavaScript('document.scrollingElement.scrollTop'), 0)
+
+    # Before we scroll down the element should not be visible at all.
+    self.assertEquals(self._VisibleAreaOfElement(), 0)
+
+    i = scroll_to_element.ScrollToElementAction(
+        selector='#element', container_selector='#container')
+    i.WillRunAction(self._tab)
+    i.RunAction(self._tab)
+
+    # After we scroll down at least some of the element should be visible.
+    self.assertGreater(self._VisibleAreaOfElement(), 0)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/scroll_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/scroll_unittest.py
new file mode 100644
index 0000000..b51dd6a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/scroll_unittest.py
@@ -0,0 +1,130 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry import decorators
+from telemetry.internal.actions import scroll
+from telemetry.internal.actions import utils
+from telemetry.testing import tab_test_case
+
+class ScrollActionTest(tab_test_case.TabTestCase):
+  def _MakePageVerticallyScrollable(self):
+    # Make page taller than window so it's scrollable vertically.
+    self._tab.ExecuteJavaScript('document.body.style.height ='
+        '(3 * __GestureCommon_GetWindowHeight() + 1) + "px";')
+
+  def _MakePageHorizontallyScrollable(self):
+    # Make page wider than window so it's scrollable horizontally.
+    self._tab.ExecuteJavaScript('document.body.style.width ='
+        '(3 * __GestureCommon_GetWindowWidth() + 1) + "px";')
+
+  def setUp(self):
+    tab_test_case.TabTestCase.setUp(self)
+    self.Navigate('blank.html')
+    utils.InjectJavaScript(self._tab, 'gesture_common.js')
+
+  def testScrollAction(self):
+
+    self._MakePageVerticallyScrollable()
+    self.assertEquals(
+        self._tab.EvaluateJavaScript('document.scrollingElement.scrollTop'), 0)
+
+    i = scroll.ScrollAction()
+    i.WillRunAction(self._tab)
+
+    self._tab.ExecuteJavaScript("""
+        window.__scrollAction.beginMeasuringHook = function() {
+            window.__didBeginMeasuring = true;
+        };
+        window.__scrollAction.endMeasuringHook = function() {
+            window.__didEndMeasuring = true;
+        };""")
+    i.RunAction(self._tab)
+
+    self.assertTrue(self._tab.EvaluateJavaScript('window.__didBeginMeasuring'))
+    self.assertTrue(self._tab.EvaluateJavaScript('window.__didEndMeasuring'))
+
+    scroll_position = self._tab.EvaluateJavaScript(
+        'document.scrollingElement.scrollTop')
+    self.assertTrue(scroll_position != 0,
+                    msg='scroll_position=%d;' % (scroll_position))
+
+  # https://github.com/catapult-project/catapult/issues/3099
+  @decorators.Disabled('android')
+  def testDiagonalScrollAction(self):
+    # Diagonal scrolling was not supported in the ScrollAction until Chrome
+    # branch number 2332
+    branch_num = self._tab.browser._browser_backend.devtools_client \
+        .GetChromeBranchNumber()
+    if branch_num < 2332:
+      return
+
+    self._MakePageVerticallyScrollable()
+    self.assertEquals(
+        self._tab.EvaluateJavaScript('document.scrollingElement.scrollTop'), 0)
+
+    self._MakePageHorizontallyScrollable()
+    self.assertEquals(
+        self._tab.EvaluateJavaScript('document.scrollingElement.scrollLeft'),
+        0)
+
+    i = scroll.ScrollAction(direction='downright')
+    i.WillRunAction(self._tab)
+
+    i.RunAction(self._tab)
+
+    viewport_top = self._tab.EvaluateJavaScript(
+        'document.scrollingElement.scrollTop')
+    self.assertTrue(viewport_top != 0, msg='viewport_top=%d;' % viewport_top)
+
+    viewport_left = self._tab.EvaluateJavaScript(
+        'document.scrollingElement.scrollLeft')
+    self.assertTrue(viewport_left != 0, msg='viewport_left=%d;' % viewport_left)
+
+  def testBoundingClientRect(self):
+    # Verify that the rect returned by getBoundingVisibleRect() in scroll.js is
+    # completely contained within the viewport. Scroll events dispatched by the
+    # scrolling API use the center of this rect as their location, and this
+    # location needs to be within the viewport bounds to correctly decide
+    # between main-thread and impl-thread scroll. If the scrollable area were
+    # not clipped to the viewport bounds, then the instance used here (the
+    # scrollable area being more than twice as tall as the viewport) would
+    # result in a scroll location outside of the viewport bounds.
+    self._MakePageVerticallyScrollable()
+    self.assertEquals(
+        self._tab.EvaluateJavaScript('document.scrollingElement.scrollTop'), 0)
+
+    self._MakePageHorizontallyScrollable()
+    self.assertEquals(
+        self._tab.EvaluateJavaScript('document.scrollingElement.scrollLeft'),
+        0)
+
+    self._tab.ExecuteJavaScript("""
+        window.scrollTo(__GestureCommon_GetWindowWidth(),
+                        __GestureCommon_GetWindowHeight());""")
+
+    rect_top = int(self._tab.EvaluateJavaScript(
+        '__GestureCommon_GetBoundingVisibleRect(document.body).top'))
+    rect_height = int(self._tab.EvaluateJavaScript(
+        '__GestureCommon_GetBoundingVisibleRect(document.body).height'))
+    rect_bottom = rect_top + rect_height
+
+    rect_left = int(self._tab.EvaluateJavaScript(
+        '__GestureCommon_GetBoundingVisibleRect(document.body).left'))
+    rect_width = int(self._tab.EvaluateJavaScript(
+        '__GestureCommon_GetBoundingVisibleRect(document.body).width'))
+    rect_right = rect_left + rect_width
+
+    viewport_height = int(self._tab.EvaluateJavaScript(
+        '__GestureCommon_GetWindowHeight()'))
+    viewport_width = int(self._tab.EvaluateJavaScript(
+        '__GestureCommon_GetWindowWidth()'))
+
+    self.assertTrue(rect_top >= 0,
+        msg='%s >= %s' % (rect_top, 0))
+    self.assertTrue(rect_left >= 0,
+        msg='%s >= %s' % (rect_left, 0))
+    self.assertTrue(rect_bottom <= viewport_height,
+        msg='%s + %s <= %s' % (rect_top, rect_height, viewport_height))
+    self.assertTrue(rect_right <= viewport_width,
+        msg='%s + %s <= %s' % (rect_left, rect_width, viewport_width))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/seek.js b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/seek.js
new file mode 100644
index 0000000..baef30c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/seek.js
@@ -0,0 +1,57 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file performs actions on media elements.
+(function() {
+  function seekMedia(selector, seekTime, logSeekTime, seekLabel) {
+    // Performs the "Seek" action on media satisfying selector.
+    var mediaElements = window.__findMediaElements(selector);
+    for (var i = 0; i < mediaElements.length; i++) {
+      if (mediaElements[i] instanceof HTMLMediaElement)
+        seekHTML5Element(mediaElements[i], seekTime, logSeekTime, seekLabel);
+      else
+        throw new Error('Can not seek non HTML5 media elements.');
+    }
+  }
+
+  function seekHTML5Element(element, seekTime, logSeekTime, seekLabel) {
+    function readyForSeek() {
+      seekHTML5ElementPostLoad(element, seekTime, logSeekTime, seekLabel);
+    }
+    if (element.readyState == element.HAVE_NOTHING) {
+      var onLoadedMetaData = function(e) {
+        element.removeEventListener('loadedmetadata', onLoadedMetaData);
+        readyForSeek();
+      };
+      element.addEventListener('loadedmetadata', onLoadedMetaData);
+      element.load();
+    } else {
+      readyForSeek();
+    }
+  }
+
+  function seekHTML5ElementPostLoad(element, seekTime, logSeekTime, seekLabel) {
+    // Reset seek completion since multiple seeks can run on same media element.
+    element['seeked_completed'] = false;
+    window.__registerHTML5ErrorEvents(element);
+    window.__registerHTML5EventCompleted(element, 'seeked');
+
+    if (logSeekTime) {
+      var willSeekEvent = document.createEvent('Event');
+      willSeekEvent.initEvent('willSeek', false, false);
+      if (seekLabel)
+        willSeekEvent.seekLabel = seekLabel;
+      else
+        willSeekEvent.seekLabel = seekTime;
+      element.dispatchEvent(willSeekEvent);
+    }
+    try {
+      element.currentTime = seekTime;
+    } catch (err) {
+      throw new Error('Cannot seek in network state: ' + element.networkState);
+    }
+  }
+
+  window.__seekMedia = seekMedia;
+})();
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/seek.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/seek.py
new file mode 100644
index 0000000..6e82b1f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/seek.py
@@ -0,0 +1,55 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""A Telemetry page_action that performs the "seek" action on media elements.
+
+Action parameters are:
+- seconds: The media time to seek to. Test fails if not provided.
+- selector: If no selector is defined then the action attempts to seek the first
+            media element on the page. If 'all' then seek all media elements.
+- timeout_in_seconds: Maximum waiting time for the "seeked" event
+                      (dispatched when the seeked operation completes)
+                      to be fired.  0 means do not wait.
+- log_time: If true the seek time is recorded, otherwise media
+            measurement will not be aware of the seek action. Used to
+            perform multiple seeks. Default true.
+- label: A suffix string to name the seek perf measurement.
+"""
+
+from telemetry.core import exceptions
+from telemetry.internal.actions import media_action
+from telemetry.internal.actions import page_action
+from telemetry.internal.actions import utils
+
+
+class SeekAction(media_action.MediaAction):
+  def __init__(self, seconds, selector=None, timeout_in_seconds=0,
+               log_time=True, label=''):
+    super(SeekAction, self).__init__()
+    self._seconds = seconds
+    self._selector = selector if selector else ''
+    self._timeout_in_seconds = timeout_in_seconds
+    self._log_time = log_time
+    self._label = label
+
+  def WillRunAction(self, tab):
+    """Load the media metrics JS code prior to running the action."""
+    super(SeekAction, self).WillRunAction(tab)
+    utils.InjectJavaScript(tab, 'seek.js')
+
+  def RunAction(self, tab):
+    try:
+      tab.ExecuteJavaScript(
+          'window.__seekMedia('
+              '{{ selector }}, {{ seconds }}, {{ log_time }}, {{ label}});',
+          selector=self._selector,
+          seconds=str(self._seconds),
+          log_time=self._log_time,
+          label=self._label)
+      if self._timeout_in_seconds > 0:
+        self.WaitForEvent(tab, self._selector, 'seeked',
+                          self._timeout_in_seconds)
+    except exceptions.EvaluateException:
+      raise page_action.PageActionFailed('Cannot seek media element(s) with '
+                                         'selector = %s.' % self._selector)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/seek_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/seek_unittest.py
new file mode 100644
index 0000000..80b9689
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/seek_unittest.py
@@ -0,0 +1,68 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry import decorators
+from telemetry.internal.actions import seek
+from telemetry.testing import tab_test_case
+
+import py_utils
+
+
+AUDIO_1_SEEKED_CHECK = 'window.__hasEventCompleted("#audio_1", "seeked");'
+VIDEO_1_SEEKED_CHECK = 'window.__hasEventCompleted("#video_1", "seeked");'
+
+
+class SeekActionTest(tab_test_case.TabTestCase):
+
+  def setUp(self):
+    tab_test_case.TabTestCase.setUp(self)
+    self.Navigate('video_test.html')
+
+  @decorators.Disabled('linux')  # crbug.com/418577
+  def testSeekWithNoSelector(self):
+    """Tests that with no selector Seek  action seeks first media element."""
+    action = seek.SeekAction(seconds=1, timeout_in_seconds=5)
+    action.WillRunAction(self._tab)
+    action.RunAction(self._tab)
+    # Assert only first video has played.
+    self.assertTrue(self._tab.EvaluateJavaScript(VIDEO_1_SEEKED_CHECK))
+    self.assertFalse(self._tab.EvaluateJavaScript(AUDIO_1_SEEKED_CHECK))
+
+  @decorators.Disabled('linux')  # crbug.com/418577
+  def testSeekWithVideoSelector(self):
+    """Tests that Seek action seeks video element matching selector."""
+    action = seek.SeekAction(seconds=1, selector='#video_1',
+                             timeout_in_seconds=5)
+    action.WillRunAction(self._tab)
+    # Both videos not playing before running action.
+    self.assertFalse(self._tab.EvaluateJavaScript(VIDEO_1_SEEKED_CHECK))
+    self.assertFalse(self._tab.EvaluateJavaScript(AUDIO_1_SEEKED_CHECK))
+    action.RunAction(self._tab)
+    # Assert only video matching selector has played.
+    self.assertTrue(self._tab.EvaluateJavaScript(VIDEO_1_SEEKED_CHECK))
+    self.assertFalse(self._tab.EvaluateJavaScript(AUDIO_1_SEEKED_CHECK))
+
+  @decorators.Disabled('linux')  # crbug.com/418577
+  def testSeekWithAllSelector(self):
+    """Tests that Seek action seeks all video elements with selector='all'."""
+    action = seek.SeekAction(seconds=1, selector='all',
+                             timeout_in_seconds=5)
+    action.WillRunAction(self._tab)
+    # Both videos not playing before running action.
+    self.assertFalse(self._tab.EvaluateJavaScript(VIDEO_1_SEEKED_CHECK))
+    self.assertFalse(self._tab.EvaluateJavaScript(AUDIO_1_SEEKED_CHECK))
+    action.RunAction(self._tab)
+    # Assert all media elements played.
+    self.assertTrue(self._tab.EvaluateJavaScript(VIDEO_1_SEEKED_CHECK))
+    self.assertTrue(self._tab.EvaluateJavaScript(AUDIO_1_SEEKED_CHECK))
+
+  @decorators.Disabled('linux')  # crbug.com/418577
+  def testSeekWaitForSeekTimeout(self):
+    """Tests that wait_for_seeked timeouts if video does not seek."""
+    action = seek.SeekAction(seconds=1, selector='#video_1',
+                             timeout_in_seconds=0.1)
+    action.WillRunAction(self._tab)
+    self._tab.EvaluateJavaScript('document.getElementById("video_1").src = ""')
+    self.assertFalse(self._tab.EvaluateJavaScript(VIDEO_1_SEEKED_CHECK))
+    self.assertRaises(py_utils.TimeoutException, action.RunAction, self._tab)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/swipe.js b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/swipe.js
new file mode 100644
index 0000000..517485c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/swipe.js
@@ -0,0 +1,77 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+'use strict';
+
+(function() {
+  function SwipeGestureOptions(opt_options) {
+    if (opt_options) {
+      this.element_ = opt_options.element;
+      this.left_start_ratio_ = opt_options.left_start_ratio;
+      this.top_start_ratio_ = opt_options.top_start_ratio;
+      this.direction_ = opt_options.direction;
+      this.distance_ = opt_options.distance;
+      this.speed_ = opt_options.speed;
+    } else {
+      this.element_ = document.body;
+      this.left_start_ratio_ = 0.5;
+      this.top_start_ratio_ = 0.5;
+      this.direction_ = 'left';
+      this.distance_ = 0;
+      this.speed_ = 800;
+    }
+  }
+
+  function supportedByBrowser() {
+    return !!(window.chrome &&
+              chrome.gpuBenchmarking &&
+              chrome.gpuBenchmarking.swipe &&
+              chrome.gpuBenchmarking.visualViewportHeight &&
+              chrome.gpuBenchmarking.visualViewportWidth);
+  }
+
+  // This class swipes a page for a specified distance.
+  function SwipeAction(opt_callback) {
+    var self = this;
+
+    this.beginMeasuringHook = function() {};
+    this.endMeasuringHook = function() {};
+
+    this.callback_ = opt_callback;
+  }
+
+  SwipeAction.prototype.start = function(opt_options) {
+    this.options_ = new SwipeGestureOptions(opt_options);
+    // Assign this.element_ here instead of constructor, because the constructor
+    // ensures this method will be called after the document is loaded.
+    this.element_ = this.options_.element_;
+    requestAnimationFrame(this.startGesture_.bind(this));
+  };
+
+  SwipeAction.prototype.startGesture_ = function() {
+    this.beginMeasuringHook();
+
+    var rect = __GestureCommon_GetBoundingVisibleRect(this.options_.element_);
+    var start_left =
+        rect.left + rect.width * this.options_.left_start_ratio_;
+    var start_top =
+        rect.top + rect.height * this.options_.top_start_ratio_;
+    chrome.gpuBenchmarking.swipe(this.options_.direction_,
+                                 this.options_.distance_,
+                                 this.onGestureComplete_.bind(this),
+                                 start_left, start_top,
+                                 this.options_.speed_);
+  };
+
+  SwipeAction.prototype.onGestureComplete_ = function() {
+    this.endMeasuringHook();
+
+    // We're done.
+    if (this.callback_)
+      this.callback_();
+  };
+
+  window.__SwipeAction = SwipeAction;
+  window.__SwipeAction_SupportedByBrowser = supportedByBrowser;
+})();
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/swipe.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/swipe.py
new file mode 100644
index 0000000..1232e3b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/swipe.py
@@ -0,0 +1,80 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.internal.actions import page_action
+from telemetry.internal.actions import utils
+from telemetry.util import js_template
+
+
+class SwipeAction(page_action.PageAction):
+  def __init__(self, selector=None, text=None, element_function=None,
+               left_start_ratio=0.5, top_start_ratio=0.5,
+               direction='left', distance=100, speed_in_pixels_per_second=800,
+               synthetic_gesture_source=page_action.GESTURE_SOURCE_DEFAULT):
+    super(SwipeAction, self).__init__()
+    if direction not in ['down', 'up', 'left', 'right']:
+      raise page_action.PageActionNotSupported(
+          'Invalid swipe direction: %s' % self.direction)
+    self._selector = selector
+    self._text = text
+    self._element_function = element_function
+    self._left_start_ratio = left_start_ratio
+    self._top_start_ratio = top_start_ratio
+    self._direction = direction
+    self._distance = distance
+    self._speed = speed_in_pixels_per_second
+    self._synthetic_gesture_source = ('chrome.gpuBenchmarking.%s_INPUT' %
+                                      synthetic_gesture_source)
+
+  def WillRunAction(self, tab):
+    utils.InjectJavaScript(tab, 'gesture_common.js')
+    utils.InjectJavaScript(tab, 'swipe.js')
+
+    # Fail if browser doesn't support synthetic swipe gestures.
+    if not tab.EvaluateJavaScript('window.__SwipeAction_SupportedByBrowser()'):
+      raise page_action.PageActionNotSupported(
+          'Synthetic swipe not supported for this browser')
+
+    if (self._synthetic_gesture_source ==
+        'chrome.gpuBenchmarking.MOUSE_INPUT'):
+      raise page_action.PageActionNotSupported(
+          'Swipe page action does not support mouse input')
+
+    if not page_action.IsGestureSourceTypeSupported(tab, 'touch'):
+      raise page_action.PageActionNotSupported(
+          'Touch input not supported for this browser')
+
+    tab.ExecuteJavaScript("""
+        window.__swipeActionDone = false;
+        window.__swipeAction = new __SwipeAction(function() {
+          window.__swipeActionDone = true;
+        });""")
+
+  def RunAction(self, tab):
+    if (self._selector is None and self._text is None and
+        self._element_function is None):
+      self._element_function = '(document.scrollingElement || document.body)'
+    code = js_template.Render('''
+        function(element, info) {
+          if (!element) {
+            throw Error('Cannot find element: ' + info);
+          }
+          window.__swipeAction.start({
+            element: element,
+            left_start_ratio: {{ left_start_ratio }},
+            top_start_ratio: {{ top_start_ratio }},
+            direction: {{ direction }},
+            distance: {{ distance }},
+            speed: {{ speed }}
+          });
+        }''',
+        left_start_ratio=self._left_start_ratio,
+        top_start_ratio=self._top_start_ratio,
+        direction=self._direction,
+        distance=self._distance,
+        speed=self._speed)
+    page_action.EvaluateCallbackWithElement(
+        tab, code, selector=self._selector, text=self._text,
+        element_function=self._element_function)
+    tab.WaitForJavaScriptCondition('window.__swipeActionDone', timeout=60)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/tap.js b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/tap.js
new file mode 100644
index 0000000..e8c8b30
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/tap.js
@@ -0,0 +1,77 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+'use strict';
+
+(function() {
+
+  function TapGestureOptions(opt_options) {
+    if (opt_options) {
+      this.element_ = opt_options.element;
+      this.left_position_percentage_ = opt_options.left_position_percentage;
+      this.top_position_percentage_ = opt_options.top_position_percentage;
+      this.duration_ms_ = opt_options.duration_ms;
+      this.gesture_source_type_ = opt_options.gesture_source_type;
+    } else {
+      this.element_ = document.body;
+      this.left_position_percentage_ = 0.5;
+      this.top_position_percentage_ = 0.5;
+      this.duration_ms_ = 50;
+      this.gesture_source_type_ = chrome.gpuBenchmarking.DEFAULT_INPUT;
+    }
+  }
+
+  function supportedByBrowser() {
+    return !!(window.chrome &&
+              chrome.gpuBenchmarking &&
+              chrome.gpuBenchmarking.tap &&
+              chrome.gpuBenchmarking.visualViewportHeight &&
+              chrome.gpuBenchmarking.visualViewportWidth);
+  }
+
+  function TapAction(opt_callback) {
+    var self = this;
+
+    this.beginMeasuringHook = function() {};
+    this.endMeasuringHook = function() {};
+
+    this.callback_ = opt_callback;
+  }
+
+  TapAction.prototype.start = function(opt_options) {
+    this.options_ = new TapGestureOptions(opt_options);
+    // Assign this.element_ here instead of constructor, because the constructor
+    // ensures this method will be called after the document is loaded.
+    this.element_ = this.options_.element_;
+
+    this.beginMeasuringHook();
+
+    var rect = __GestureCommon_GetBoundingVisibleRect(this.options_.element_);
+    var position_left =
+        rect.left + rect.width * this.options_.left_position_percentage_;
+    var position_top =
+        rect.top + rect.height * this.options_.top_position_percentage_;
+    if (position_left < 0 ||
+        position_left >= __GestureCommon_GetWindowWidth() ||
+        position_top < 0 ||
+        position_top >= __GestureCommon_GetWindowHeight()) {
+      throw new Error('Tap position is off-screen');
+    }
+    chrome.gpuBenchmarking.tap(position_left, position_top,
+                               this.onGestureComplete_.bind(this),
+                               this.options_.duration_ms_,
+                               this.options_.gesture_source_type_);
+  };
+
+  TapAction.prototype.onGestureComplete_ = function() {
+    this.endMeasuringHook();
+
+    // We're done.
+    if (this.callback_)
+      this.callback_();
+  };
+
+  window.__TapAction = TapAction;
+  window.__TapAction_SupportedByBrowser = supportedByBrowser;
+})();
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/tap.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/tap.py
new file mode 100644
index 0000000..3ad8b7d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/tap.py
@@ -0,0 +1,74 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.internal.actions import page_action
+from telemetry.internal.actions import utils
+from telemetry.util import js_template
+
+
+class TapAction(page_action.PageAction):
+  def __init__(self, selector=None, text=None, element_function=None,
+               left_position_percentage=0.5, top_position_percentage=0.5,
+               duration_ms=50,
+               synthetic_gesture_source=page_action.GESTURE_SOURCE_DEFAULT):
+    super(TapAction, self).__init__()
+    self.selector = selector
+    self.text = text
+    self.element_function = element_function
+    self.left_position_percentage = left_position_percentage
+    self.top_position_percentage = top_position_percentage
+    self.duration_ms = duration_ms
+    self._synthetic_gesture_source = ('chrome.gpuBenchmarking.%s_INPUT' %
+                                      synthetic_gesture_source)
+
+  def WillRunAction(self, tab):
+    utils.InjectJavaScript(tab, 'gesture_common.js')
+    utils.InjectJavaScript(tab, 'tap.js')
+
+    # Fail if browser doesn't support synthetic tap gestures.
+    if not tab.EvaluateJavaScript('window.__TapAction_SupportedByBrowser()'):
+      raise page_action.PageActionNotSupported(
+          'Synthetic tap not supported for this browser')
+
+    tab.ExecuteJavaScript("""
+        window.__tapActionDone = false;
+        window.__tapAction = new __TapAction(function() {
+          window.__tapActionDone = true;
+        });""")
+
+  def HasElementSelector(self):
+    return (self.element_function is not None or self.selector is not None or
+            self.text is not None)
+
+  def RunAction(self, tab):
+    if not self.HasElementSelector():
+      self.element_function = 'document.body'
+
+    code = js_template.Render('''
+        function(element, errorMsg) {
+          if (!element) {
+            throw Error('Cannot find element: ' + errorMsg);
+          }
+          window.__tapAction.start({
+            element: element,
+            left_position_percentage: {{ left_position_percentage }},
+            top_position_percentage: {{ top_position_percentage }},
+            duration_ms: {{ duration_ms }},
+            gesture_source_type: {{ @gesture_source_type }}
+          });
+        }''',
+        left_position_percentage=self.left_position_percentage,
+        top_position_percentage=self.top_position_percentage,
+        duration_ms=self.duration_ms,
+        gesture_source_type=self._synthetic_gesture_source)
+
+    page_action.EvaluateCallbackWithElement(
+        tab, code, selector=self.selector, text=self.text,
+        element_function=self.element_function)
+    # The second disjunct handles the case where the tap action leads to an
+    # immediate navigation (in which case the expression below might already be
+    # evaluated on the new page).
+    tab.WaitForJavaScriptCondition(
+        'window.__tapActionDone || window.__tapAction === undefined',
+        timeout=60)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/tap_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/tap_unittest.py
new file mode 100644
index 0000000..f6708ed
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/tap_unittest.py
@@ -0,0 +1,41 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry import decorators
+from telemetry.internal.actions import tap
+from telemetry.testing import tab_test_case
+
+class TapActionTest(tab_test_case.TabTestCase):
+
+  def _PerformTapAction(self, *args, **kwargs):
+    action = tap.TapAction(*args, **kwargs)
+    action.WillRunAction(self._tab)
+    action.RunAction(self._tab)
+
+  # https://github.com/catapult-project/catapult/issues/3099 (Android)
+  # http://crbug.com/634343 (Windows)
+  @decorators.Disabled('android', 'win')
+  def testTapSinglePage(self):
+    self.Navigate('page_with_clickables.html')
+
+    self._tab.ExecuteJavaScript('valueSettableByTest = 1;')
+    self._PerformTapAction('#test')
+    self.assertEqual(1, self._tab.EvaluateJavaScript('valueToTest'))
+
+    self._tab.ExecuteJavaScript('valueSettableByTest = 2;')
+    self._PerformTapAction(text='Click/tap me')
+    self.assertEqual(2, self._tab.EvaluateJavaScript('valueToTest'))
+
+    self._tab.ExecuteJavaScript('valueSettableByTest = 3;')
+    self._PerformTapAction(element_function='document.body.firstElementChild')
+    self.assertEqual(3, self._tab.EvaluateJavaScript('valueToTest'))
+
+  @decorators.Disabled('win')  # http://crbug.com/634343
+  def testTapNavigate(self):
+    self.Navigate('page_with_link.html')
+    self._PerformTapAction(selector='#clickme')
+    self._tab.WaitForJavaScriptCondition(
+        'document.location.pathname === "/blank.html"', timeout=5)
+    self._tab.WaitForJavaScriptCondition(
+        'document.readyState === "complete"', timeout=5)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/utils.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/utils.py
new file mode 100644
index 0000000..8ef5906
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/utils.py
@@ -0,0 +1,11 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+
+
+def InjectJavaScript(tab, jsFileName):
+  with open(os.path.join(os.path.dirname(__file__), jsFileName)) as f:
+    js = f.read()
+    tab.ExecuteJavaScript(js)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/wait.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/wait.py
new file mode 100644
index 0000000..3413f3c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/actions/wait.py
@@ -0,0 +1,22 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.internal.actions import page_action
+
+
+class WaitForElementAction(page_action.PageAction):
+  def __init__(self, selector=None, text=None, element_function=None,
+               timeout_in_seconds=60):
+    super(WaitForElementAction, self).__init__()
+    self.selector = selector
+    self.text = text
+    self.element_function = element_function
+    self.timeout_in_seconds = timeout_in_seconds
+
+  def RunAction(self, tab):
+    code = 'function(element) { return element != null; }'
+    page_action.EvaluateCallbackWithElement(
+        tab, code, selector=self.selector, text=self.text,
+        element_function=self.element_function,
+        wait=True, timeout_in_seconds=self.timeout_in_seconds)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/app/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/app/__init__.py
new file mode 100644
index 0000000..0a1d389
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/app/__init__.py
@@ -0,0 +1,44 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+class App(object):
+  """ A running application instance that can be controlled in a limited way.
+
+  Be sure to clean up after yourself by calling Close() when you are done with
+  the app. Or better yet:
+    with possible_app.Create(options) as app:
+      ... do all your operations on app here
+  """
+  def __init__(self, app_backend, platform_backend):
+    assert platform_backend.platform != None
+    self._app_backend = app_backend
+    self._platform_backend = platform_backend
+    self._app_backend.SetApp(self)
+
+  @property
+  def app_type(self):
+    return self._app_backend.app_type
+
+  @property
+  def platform(self):
+    return self._platform_backend.platform
+
+  def __enter__(self):
+    return self
+
+  def __exit__(self, *args):
+    self.Close()
+
+  def Close(self):
+    raise NotImplementedError()
+
+  def GetStandardOutput(self):
+    return self._app_backend.GetStandardOutput()
+
+  def GetStackTrace(self):
+    return self._app_backend.GetStackTrace()
+
+  def GetMostRecentMinidumpPath(self):
+    return self._app_backend.GetMostRecentMinidumpPath()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/app/android_app.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/app/android_app.py
new file mode 100644
index 0000000..0cee256
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/app/android_app.py
@@ -0,0 +1,42 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.internal import app
+
+
+class AndroidApp(app.App):
+  """A running android app instance that can be controlled in a limited way.
+
+  Be sure to clean up after yourself by calling Close() when you are done with
+  the app. Or better yet:
+    with possible_android_app.Create(options) as android_app:
+      ... do all your operations on android_app here
+  """
+  def __init__(self, app_backend, platform_backend):
+    super(AndroidApp, self).__init__(app_backend=app_backend,
+                                     platform_backend=platform_backend)
+    self._app_backend.Start()
+
+  @property
+  def ui(self):
+    """Returns an AppUi object to interact with the app's UI.
+
+    See devil.android.app_ui for the documentation of the API provided.
+    """
+    return self._app_backend.GetAppUi()
+
+  def Close(self):
+    self._app_backend.Close()
+
+  def GetProcesses(self):
+    """Returns the current set of processes belonging to this app."""
+    return self._app_backend.GetProcesses()
+
+  def GetProcess(self, subprocess_name):
+    """Returns the process with the specified subprocess name."""
+    return self._app_backend.GetProcess(subprocess_name)
+
+  def GetWebViews(self):
+    """Returns the set of all WebViews belonging to all processes of the app."""
+    return self._app_backend.GetWebViews()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/app/android_app_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/app/android_app_unittest.py
new file mode 100644
index 0000000..f63b2e0
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/app/android_app_unittest.py
@@ -0,0 +1,55 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+import time
+import unittest
+
+from telemetry.core import platform as platform_module
+from telemetry.internal.app import android_app
+from telemetry.internal.backends import android_app_backend
+from telemetry.internal.platform import android_device
+from telemetry.testing import options_for_unittests
+
+from devil.android.sdk import intent
+
+
+class AndroidAppTest(unittest.TestCase):
+  def setUp(self):
+    self._options = options_for_unittests.GetCopy()
+    self._device = android_device.GetDevice(self._options)
+
+  def CreateAndroidApp(self, start_intent):
+    platform = platform_module.GetPlatformForDevice(self._device, self._options)
+    platform_backend = platform._platform_backend
+    app_backend = android_app_backend.AndroidAppBackend(
+        platform_backend, start_intent)
+    return android_app.AndroidApp(app_backend, platform_backend)
+
+  def testWebView(self):
+    if self._device is None:
+      logging.warning('No device found, skipping test.')
+      return
+
+    start_intent = intent.Intent(
+        package='com.google.android.googlequicksearchbox',
+        activity='.SearchActivity',
+        action='com.google.android.googlequicksearchbox.GOOGLE_SEARCH',
+        data=None,
+        extras={'query': 'google'},
+        category=None)
+    search_app = self.CreateAndroidApp(start_intent)
+    search_process = search_app.GetProcess(':search')
+    search_process._UpdateDevToolsClient()
+
+    # TODO(ariblue): Replace the app used in this test with one in which the
+    # setWebContentsDebuggingEnabled method is called on the WebView class.
+    # This will configure webviews for debugging with chrome devtools inspector
+    # and allow us to remove this check.
+    if search_process._devtools_client is None:
+      return
+
+    webview = search_app.GetProcess(':search').GetWebViews().pop()
+    webview.Navigate('https://www.google.com/search?q=flowers')
+    time.sleep(5)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/app/android_process.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/app/android_process.py
new file mode 100644
index 0000000..3c9a294
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/app/android_process.py
@@ -0,0 +1,56 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.internal.backends.chrome_inspector import devtools_client_backend
+from telemetry.internal.browser import web_contents
+
+try:
+  from devil.android import ports
+except ImportError:
+  ports = None
+
+class WebViewNotFoundException(Exception):
+  pass
+
+class AndroidProcess(object):
+  """Represents a single android process."""
+
+  def __init__(self, app_backend, pid, name):
+    self._app_backend = app_backend
+    self._pid = pid
+    self._name = name
+    self._local_port = ports.AllocateTestServerPort()
+    self._devtools_client = None
+
+  @property
+  def pid(self):
+    return self._pid
+
+  @property
+  def name(self):
+    return self._name
+
+  @property
+  def _remote_devtools_port(self):
+    return 'localabstract:webview_devtools_remote_%s' % str(self.pid)
+
+  def _UpdateDevToolsClient(self):
+    if self._devtools_client is None:
+      self._app_backend.platform_backend.ForwardHostToDevice(
+          self._local_port, self._remote_devtools_port)
+      if devtools_client_backend.IsDevToolsAgentAvailable(
+          self._local_port, self._app_backend):
+        self._devtools_client = devtools_client_backend.DevToolsClientBackend(
+            self._local_port, self._remote_devtools_port, self._app_backend)
+
+  def GetWebViews(self):
+    webviews = []
+    self._UpdateDevToolsClient()
+    if self._devtools_client is not None:
+      devtools_context_map = (
+          self._devtools_client.GetUpdatedInspectableContexts())
+      for context in devtools_context_map.contexts:
+        webviews.append(web_contents.WebContents(
+            devtools_context_map.GetInspectorBackend(context['id'])))
+    return webviews
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/app/possible_app.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/app/possible_app.py
new file mode 100644
index 0000000..ee53f5b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/app/possible_app.py
@@ -0,0 +1,43 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+class PossibleApp(object):
+  """A factory class that can be used to create a running instance of app.
+
+  Call Create() to launch the app and begin manipulating it.
+  """
+
+  def __init__(self, app_type, target_os):
+    self._app_type = app_type
+    self._target_os = target_os
+    self._platform = None
+    self._platform_backend = None
+
+  def __repr__(self):
+    return 'PossibleApp(app_type=%s)' % self.app_type
+
+  @property
+  def app_type(self):
+    return self._app_type
+
+  @property
+  def target_os(self):
+    """Target OS, the app will run on."""
+    return self._target_os
+
+  @property
+  def platform(self):
+    self._InitPlatformIfNeeded()
+    return self._platform
+
+  def _InitPlatformIfNeeded(self):
+    raise NotImplementedError()
+
+  def Create(self, finder_options):
+    raise NotImplementedError()
+
+  def SupportsOptions(self, browser_options):
+    """Tests for extension support."""
+    raise NotImplementedError()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/__init__.py
new file mode 100644
index 0000000..9228df8
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/__init__.py
@@ -0,0 +1,3 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/android_app_backend.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/android_app_backend.py
new file mode 100644
index 0000000..1d308cc
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/android_app_backend.py
@@ -0,0 +1,153 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import re
+
+from telemetry.internal.app import android_process
+from telemetry.internal.backends import android_browser_backend_settings
+from telemetry.internal.backends import app_backend
+
+from devil.android import app_ui
+from devil.android import flag_changer
+from devil.android.sdk import intent
+
+import py_utils
+
+
+class AndroidAppBackend(app_backend.AppBackend):
+
+  def __init__(self, android_platform_backend, start_intent,
+               is_app_ready_predicate=None, app_has_webviews=True):
+    super(AndroidAppBackend, self).__init__(
+        start_intent.package, android_platform_backend)
+    self._default_process_name = start_intent.package
+    self._start_intent = start_intent
+    self._is_app_ready_predicate = is_app_ready_predicate
+    self._is_running = False
+    self._app_has_webviews = app_has_webviews
+    self._existing_processes_by_pid = {}
+    self._app_ui = None
+
+  @property
+  def device(self):
+    return self.platform_backend.device
+
+  def GetAppUi(self):
+    if self._app_ui is None:
+      self._app_ui = app_ui.AppUi(self.device, self._start_intent.package)
+    return self._app_ui
+
+  def _LaunchAndWaitForApplication(self):
+    """Launch the app and wait for it to be ready."""
+    def is_app_ready():
+      return self._is_app_ready_predicate(self.app)
+
+    # When "is_app_ready_predicate" is provided, we use it to wait for the
+    # app to become ready, otherwise "blocking=True" is used as a fall back.
+    # TODO(slamm): check if the wait for "ps" check is really needed, or
+    # whether the "blocking=True" fall back is sufficient.
+    has_ready_predicate = self._is_app_ready_predicate is not None
+    self.device.StartActivity(
+        self._start_intent,
+        blocking=not has_ready_predicate,
+        force_stop=True,  # Ensure app was not running.
+    )
+    if has_ready_predicate:
+      py_utils.WaitFor(is_app_ready, timeout=60)
+
+  def Start(self):
+    """Start an Android app and wait for it to finish launching.
+
+    If the app has webviews, the app is launched with the suitable
+    command line arguments.
+
+    AppStory derivations can customize the wait-for-ready-state to wait
+    for a more specific event if needed.
+    """
+    if self._app_has_webviews:
+      webview_startup_args = self.GetWebviewStartupArgs()
+      command_line_name = (
+          android_browser_backend_settings.WebviewBackendSettings(
+              'android-webview')).command_line_name
+      with flag_changer.CustomCommandLineFlags(
+          self.device, command_line_name, webview_startup_args):
+        self._LaunchAndWaitForApplication()
+    else:
+      self._LaunchAndWaitForApplication()
+    self._is_running = True
+
+  def Foreground(self):
+    self.device.StartActivity(
+        intent.Intent(package=self._start_intent.package,
+                      activity=self._start_intent.activity,
+                      action=None,
+                      flags=[intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED]),
+        blocking=True)
+
+  def Background(self):
+    package = 'org.chromium.push_apps_to_background'
+    activity = package + '.PushAppsToBackgroundActivity'
+    self.device.StartActivity(
+        intent.Intent(
+            package=package,
+            activity=activity,
+            action=None,
+            flags=[intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED]),
+        blocking=True)
+
+  def Close(self):
+    self._is_running = False
+    self.platform_backend.KillApplication(self._start_intent.package)
+
+  def IsAppRunning(self):
+    return self._is_running
+
+  def GetStandardOutput(self):
+    raise NotImplementedError
+
+  def GetStackTrace(self):
+    raise NotImplementedError
+
+  def GetProcesses(self, process_filter=None):
+    if process_filter is None:
+      # Match process names of the form: 'process_name[:subprocess]'.
+      process_filter = re.compile(
+          '^%s(:|$)' % re.escape(self._default_process_name)).match
+
+    processes = set()
+    ps_output = self.platform_backend.GetPsOutput(['pid', 'name'])
+    for pid, name in ps_output:
+      if not process_filter(name):
+        continue
+
+      if pid not in self._existing_processes_by_pid:
+        self._existing_processes_by_pid[pid] = android_process.AndroidProcess(
+            self, pid, name)
+      processes.add(self._existing_processes_by_pid[pid])
+    return processes
+
+  def GetProcess(self, subprocess_name):
+    assert subprocess_name.startswith(':')
+    process_name = self._default_process_name + subprocess_name
+    return self.GetProcesses(lambda n: n == process_name).pop()
+
+  def GetWebViews(self):
+    assert self._app_has_webviews
+    webviews = set()
+    for process in self.GetProcesses():
+      webviews.update(process.GetWebViews())
+    return webviews
+
+  def GetWebviewStartupArgs(self):
+    assert self._app_has_webviews
+    args = []
+
+    # Turn on GPU benchmarking extension for all runs. The only side effect of
+    # the extension being on is that render stats are tracked. This is believed
+    # to be effectively free. And, by doing so here, it avoids us having to
+    # programmatically inspect a pageset's actions in order to determine if it
+    # might eventually scroll.
+    args.append('--enable-gpu-benchmarking')
+
+    return args
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/android_app_backend_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/android_app_backend_unittest.py
new file mode 100644
index 0000000..8140e89
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/android_app_backend_unittest.py
@@ -0,0 +1,38 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import collections
+import mock
+import unittest
+
+from telemetry.internal.backends import android_app_backend
+from devil.android.sdk import intent as intent_module
+
+
+_FakeAndroidProcess = collections.namedtuple(
+    'AndroidProcess', ['app_backend', 'pid', 'name'])
+
+
+class AndroidAppBackendUnittest(unittest.TestCase):
+
+  def setUp(self):
+    self.platform_backend = mock.Mock()
+    self.start_intent = intent_module.Intent(
+        package='com.example.my_app',
+        activity='com.example.my_app.LaunchMyApp')
+    self.app_backend = android_app_backend.AndroidAppBackend(
+        self.platform_backend, self.start_intent)
+
+  @mock.patch('telemetry.internal.backends.android_app_backend'
+              '.android_process.AndroidProcess', _FakeAndroidProcess)
+  def testGetProcesses(self):
+    # Only processes belonging to 'com.example.my_app' should match.
+    self.platform_backend.GetPsOutput.return_value = [
+      ['1111', 'com.example.my_app'],
+      ['2222', 'com.example.my_appointments_helper'],
+      ['3333', 'com.example.my_app:service'],
+      ['4444', 'com.example.some_other_app'],
+      ['5555', 'com_example_my_app'],
+    ]
+    process_pids = set(p.pid for p in self.app_backend.GetProcesses())
+    self.assertEquals(process_pids, set(['1111', '3333']))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/android_browser_backend_settings.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/android_browser_backend_settings.py
new file mode 100644
index 0000000..1151b9a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/android_browser_backend_settings.py
@@ -0,0 +1,121 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+import time
+
+
+class AndroidBrowserBackendSettings(object):
+
+  def __init__(self, activity, cmdline_file, package, pseudo_exec_name,
+               supports_tab_control):
+    self._activity = activity
+    self._cmdline_file = cmdline_file
+    self._package = package
+    self._pseudo_exec_name = pseudo_exec_name
+    self._supports_tab_control = supports_tab_control
+
+  @property
+  def activity(self):
+    return self._activity
+
+  @property
+  def package(self):
+    return self._package
+
+  @property
+  def pseudo_exec_name(self):
+    return self._pseudo_exec_name
+
+  @property
+  def supports_tab_control(self):
+    return self._supports_tab_control
+
+  @property
+  def command_line_name(self):
+    return self._cmdline_file
+
+  def GetDevtoolsRemotePort(self, device):
+    raise NotImplementedError()
+
+  @property
+  def profile_ignore_list(self):
+    # Don't delete lib, since it is created by the installer.
+    return ['lib']
+
+
+class ChromeBackendSettings(AndroidBrowserBackendSettings):
+  # Stores a default Preferences file, re-used to speed up "--page-repeat".
+  _default_preferences_file = None
+
+  def __init__(self, package):
+    super(ChromeBackendSettings, self).__init__(
+        activity='com.google.android.apps.chrome.Main',
+        cmdline_file='chrome-command-line',
+        package=package,
+        pseudo_exec_name='chrome',
+        supports_tab_control=True)
+
+  def GetDevtoolsRemotePort(self, device):
+    return 'localabstract:chrome_devtools_remote'
+
+
+class ContentShellBackendSettings(AndroidBrowserBackendSettings):
+  def __init__(self, package):
+    super(ContentShellBackendSettings, self).__init__(
+        activity='org.chromium.content_shell_apk.ContentShellActivity',
+        cmdline_file='content-shell-command-line',
+        package=package,
+        pseudo_exec_name='content_shell',
+        supports_tab_control=False)
+
+  def GetDevtoolsRemotePort(self, device):
+    return 'localabstract:content_shell_devtools_remote'
+
+
+class WebviewBackendSettings(AndroidBrowserBackendSettings):
+  def __init__(self,
+               package,
+               activity='org.chromium.webview_shell.TelemetryActivity',
+               cmdline_file='webview-command-line'):
+    super(WebviewBackendSettings, self).__init__(
+        activity=activity,
+        cmdline_file=cmdline_file,
+        package=package,
+        pseudo_exec_name='webview',
+        supports_tab_control=False)
+
+  def GetDevtoolsRemotePort(self, device):
+    # The DevTools socket name for WebView depends on the activity PID's.
+    retries = 0
+    timeout = 1
+    pid = None
+    while True:
+      pids = device.GetPids(self.package)
+      if not pids or self.package not in pids:
+        time.sleep(timeout)
+        retries += 1
+        timeout *= 2
+        if retries == 4:
+          logging.critical('android_browser_backend: Timeout while waiting for '
+                           'activity %s:%s to come up',
+                           self.package,
+                           self.activity)
+          raise Exception('Timeout waiting for PID.')
+      if len(pids.get(self.package, [])) > 1:
+        raise Exception(
+            'At most one instance of process %s expected but found pids: '
+            '%s' % (self.package, pids))
+      if len(pids.get(self.package, [])) == 1:
+        pid = pids[self.package][0]
+        break
+    return 'localabstract:webview_devtools_remote_%s' % str(pid)
+
+
+class WebviewShellBackendSettings(WebviewBackendSettings):
+  def __init__(self, package):
+    super(WebviewShellBackendSettings, self).__init__(
+        activity='org.chromium.android_webview.shell.AwShellActivity',
+        cmdline_file='android-webview-command-line',
+        package=package)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/android_browser_backend_settings_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/android_browser_backend_settings_unittest.py
new file mode 100644
index 0000000..5b6c6c2
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/android_browser_backend_settings_unittest.py
@@ -0,0 +1,55 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import mock
+import unittest
+
+from telemetry.internal.backends import android_browser_backend_settings
+
+
+class AndroidBrowserBackendSettingsUnittest(unittest.TestCase):
+
+  def testWebViewGetDevtoolsRemotePortRetrySuccess(self):
+    mock_device = mock.Mock()
+    mock_device.GetPids.side_effect = [
+        {},
+        {},
+        {'webview.package': ['1111']},
+    ]
+
+    settings = android_browser_backend_settings.WebviewBackendSettings(
+        package='webview.package')
+    with mock.patch('time.sleep', return_value=None):
+      self.assertEquals(
+          settings.GetDevtoolsRemotePort(mock_device),
+          'localabstract:webview_devtools_remote_1111')
+
+  def testWebViewGetDevtoolsRemotePortMultipleProcessesFailure(self):
+    mock_device = mock.Mock()
+    mock_device.GetPids.side_effect = [
+        {'webview.package': ['1111', '2222']}
+    ]
+
+    settings = android_browser_backend_settings.WebviewBackendSettings(
+        package='webview.package')
+    with mock.patch('time.sleep', return_value=None):
+      with self.assertRaises(Exception):
+        settings.GetDevtoolsRemotePort(mock_device)
+
+  def testWebViewGetDevtoolsRemotePortTimeoutFailure(self):
+    mock_device = mock.Mock()
+    mock_device.GetPids.side_effect = [
+        {},
+        {},
+        {},
+        {},
+    ]
+
+    settings = android_browser_backend_settings.WebviewBackendSettings(
+        package='webview.package')
+    with mock.patch('time.sleep', return_value=None) as time_mock:
+      with self.assertRaises(Exception):
+        settings.GetDevtoolsRemotePort(mock_device)
+      time_mock.assert_has_calls(
+          [mock.call(1), mock.call(2), mock.call(4), mock.call(8)])
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/app_backend.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/app_backend.py
new file mode 100644
index 0000000..dadb1c1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/app_backend.py
@@ -0,0 +1,65 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from py_trace_event import trace_event
+
+
+class AppBackend(object):
+
+  __metaclass__ = trace_event.TracedMetaClass
+
+  def __init__(self, app_type, platform_backend):
+    super(AppBackend, self).__init__()
+    self._app = None
+    self._app_type = app_type
+    self._platform_backend = platform_backend
+
+  def __del__(self):
+    self.Close()
+
+  def SetApp(self, app):
+    self._app = app
+
+  @property
+  def app(self):
+    return self._app
+
+  @property
+  def app_type(self):
+    return self._app_type
+
+  @property
+  def pid(self):
+    raise NotImplementedError
+
+  @property
+  def platform_backend(self):
+    return self._platform_backend
+
+  def Start(self):
+    raise NotImplementedError
+
+  def Foreground(self):
+    # TODO(catapult:#2194): Remove the unnecessary pass below when the method
+    # has been implemented on all concrete subclasses.
+    pass  # pylint: disable=unnecessary-pass
+    raise NotImplementedError
+
+  def Background(self):
+    raise NotImplementedError
+
+  def Close(self):
+    raise NotImplementedError
+
+  def IsAppRunning(self):
+    raise NotImplementedError
+
+  def GetStandardOutput(self):
+    raise NotImplementedError
+
+  def GetStackTrace(self):
+    raise NotImplementedError
+
+  def GetMostRecentMinidumpPath(self):
+    raise NotImplementedError
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/browser_backend.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/browser_backend.py
new file mode 100644
index 0000000..43e7773
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/browser_backend.py
@@ -0,0 +1,170 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import uuid
+import sys
+
+from py_utils import cloud_storage  # pylint: disable=import-error
+
+from telemetry import decorators
+from telemetry.internal.backends import app_backend
+from telemetry.internal.browser import web_contents
+from telemetry.internal.platform import profiling_controller_backend
+
+
+class ExtensionsNotSupportedException(Exception):
+  pass
+
+
+class BrowserBackend(app_backend.AppBackend):
+  """A base class for browser backends."""
+
+  def __init__(self, platform_backend, supports_extensions, browser_options,
+               tab_list_backend):
+    assert browser_options.browser_type
+    super(BrowserBackend, self).__init__(
+        browser_options.browser_type, platform_backend)
+    self._supports_extensions = supports_extensions
+    self.browser_options = browser_options
+    self._tab_list_backend_class = tab_list_backend
+    self._profiling_controller_backend = (
+        profiling_controller_backend.ProfilingControllerBackend(
+          platform_backend, self))
+
+  def SetBrowser(self, browser):
+    super(BrowserBackend, self).SetApp(app=browser)
+
+  @property
+  def log_file_path(self):
+    # Specific browser backend is responsible for overriding this properly.
+    raise NotImplementedError
+
+  def GetLogFileContents(self):
+    if not self.log_file_path:
+      return 'No log file'
+    with file(self.log_file_path) as f:
+      return f.read()
+
+  def UploadLogsToCloudStorage(self):
+    """ Uploading log files produce by this browser instance to cloud storage.
+
+    Check supports_uploading_logs before calling this method.
+    """
+    assert self.supports_uploading_logs
+    remote_path = (self.browser_options.logs_cloud_remote_path or
+                   'log_%s' % uuid.uuid4())
+    cloud_url = cloud_storage.Insert(
+        bucket=self.browser_options.logs_cloud_bucket,
+        remote_path=remote_path,
+        local_path=self.log_file_path)
+    sys.stderr.write('Uploading browser log to %s\n' % cloud_url)
+
+  @property
+  def browser(self):
+    return self.app
+
+  @property
+  def profiling_controller_backend(self):
+    return self._profiling_controller_backend
+
+  @property
+  def browser_type(self):
+    return self.app_type
+
+  @property
+  def supports_uploading_logs(self):
+    # Specific browser backend is responsible for overriding this properly.
+    return False
+
+  @property
+  def supports_extensions(self):
+    """True if this browser backend supports extensions."""
+    return self._supports_extensions
+
+  @property
+  def supports_tab_control(self):
+    raise NotImplementedError()
+
+  @property
+  @decorators.Cache
+  def tab_list_backend(self):
+    return self._tab_list_backend_class(self)
+
+  @property
+  def supports_tracing(self):
+    raise NotImplementedError()
+
+  @property
+  def supports_system_info(self):
+    return False
+
+  def StartTracing(self, trace_options,
+                   timeout=web_contents.DEFAULT_WEB_CONTENTS_TIMEOUT):
+    raise NotImplementedError()
+
+  def StopTracing(self):
+    raise NotImplementedError()
+
+  def CollectTracingData(self, trace_data_builder):
+    raise NotImplementedError()
+
+  def Start(self):
+    raise NotImplementedError()
+
+  def IsBrowserRunning(self):
+    raise NotImplementedError()
+
+  def IsAppRunning(self):
+    return self.IsBrowserRunning()
+
+  def GetStandardOutput(self):
+    raise NotImplementedError()
+
+  def GetStackTrace(self):
+    raise NotImplementedError()
+
+  def GetMostRecentMinidumpPath(self):
+    raise NotImplementedError()
+
+  def GetAllMinidumpPaths(self):
+    raise NotImplementedError()
+
+  def GetAllUnsymbolizedMinidumpPaths(self):
+    raise NotImplementedError()
+
+  def SymbolizeMinidump(self, minidump_path):
+    raise NotImplementedError()
+
+  def GetSystemInfo(self):
+    raise NotImplementedError()
+
+  @property
+  def supports_memory_dumping(self):
+    return False
+
+  def DumpMemory(self, timeout=web_contents.DEFAULT_WEB_CONTENTS_TIMEOUT):
+    raise NotImplementedError()
+
+  @property
+  def supports_overriding_memory_pressure_notifications(self):
+    return False
+
+  def SetMemoryPressureNotificationsSuppressed(
+      self, suppressed, timeout=web_contents.DEFAULT_WEB_CONTENTS_TIMEOUT):
+    raise NotImplementedError()
+
+  def SimulateMemoryPressureNotification(
+      self, pressure_level, timeout=web_contents.DEFAULT_WEB_CONTENTS_TIMEOUT):
+    raise NotImplementedError()
+
+  @property
+  def supports_cpu_metrics(self):
+    raise NotImplementedError()
+
+  @property
+  def supports_memory_metrics(self):
+    raise NotImplementedError()
+
+  @property
+  def supports_power_metrics(self):
+    raise NotImplementedError()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/browser_backend_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/browser_backend_unittest.py
new file mode 100644
index 0000000..702c967
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/browser_backend_unittest.py
@@ -0,0 +1,45 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import tempfile
+import unittest
+
+from telemetry.internal.backends import browser_backend
+from telemetry.testing import options_for_unittests
+import mock
+
+
+class BrowserBackendLogsUploadingUnittest(unittest.TestCase):
+  def testUploadingToCLoudStorage(self):
+    temp_file = tempfile.NamedTemporaryFile(delete=False)
+    temp_file_name = temp_file.name
+    try:
+      temp_file.write('This is a\ntest log file.\n')
+      temp_file.close()
+
+      # pylint: disable=abstract-method
+      class FakeBrowserBackend(browser_backend.BrowserBackend):
+        @property
+        def supports_uploading_logs(self):
+          return True
+
+        @property
+        def log_file_path(self):
+          return temp_file_name
+
+      options = options_for_unittests.GetCopy()
+      options.browser_options.logging_verbosity = (
+          options.browser_options.VERBOSE_LOGGING)
+      options.browser_options.logs_cloud_bucket = 'ABC'
+      options.browser_options.logs_cloud_remote_path = 'def'
+
+      b = FakeBrowserBackend(None, False, options.browser_options, None)
+      self.assertEquals(b.GetLogFileContents(), 'This is a\ntest log file.\n')
+      with mock.patch('py_utils.cloud_storage.Insert') as mock_insert:
+        b.UploadLogsToCloudStorage()
+        mock_insert.assert_called_with(
+            bucket='ABC', remote_path='def', local_path=temp_file_name)
+    finally:
+      os.remove(temp_file_name)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/__init__.py
new file mode 100644
index 0000000..9228df8
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/__init__.py
@@ -0,0 +1,3 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/android_browser_backend.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/android_browser_backend.py
new file mode 100644
index 0000000..763f965
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/android_browser_backend.py
@@ -0,0 +1,253 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+import subprocess
+
+from telemetry.core import exceptions
+from telemetry.internal.platform import android_platform_backend as \
+  android_platform_backend_module
+from telemetry.core import util
+from telemetry.internal.backends import browser_backend
+from telemetry.internal.backends.chrome import chrome_browser_backend
+from telemetry.internal.browser import user_agent
+
+from devil.android import app_ui
+from devil.android import flag_changer
+from devil.android.sdk import intent
+
+
+class AndroidBrowserBackend(chrome_browser_backend.ChromeBrowserBackend):
+  """The backend for controlling a browser instance running on Android."""
+  def __init__(self, android_platform_backend, browser_options,
+               backend_settings):
+    assert isinstance(android_platform_backend,
+                      android_platform_backend_module.AndroidPlatformBackend)
+    super(AndroidBrowserBackend, self).__init__(
+        android_platform_backend,
+        supports_tab_control=backend_settings.supports_tab_control,
+        supports_extensions=False, browser_options=browser_options)
+
+    self._port_keeper = util.PortKeeper()
+    # Use the port hold by _port_keeper by default.
+    self._port = self._port_keeper.port
+
+    extensions_to_load = browser_options.extensions_to_load
+
+    if len(extensions_to_load) > 0:
+      raise browser_backend.ExtensionsNotSupportedException(
+          'Android browser does not support extensions.')
+
+    # Initialize fields so that an explosion during init doesn't break in Close.
+    self._backend_settings = backend_settings
+    self._saved_sslflag = ''
+
+    # Stop old browser, if any.
+    self._StopBrowser()
+
+    if self.device.HasRoot() or self.device.NeedsSU():
+      if self.browser_options.profile_dir:
+        self.platform_backend.PushProfile(
+            self._backend_settings.package,
+            self.browser_options.profile_dir)
+      elif not self.browser_options.dont_override_profile:
+        self.platform_backend.RemoveProfile(
+            self._backend_settings.package,
+            self._backend_settings.profile_ignore_list)
+
+    # Set the debug app if needed.
+    self.platform_backend.SetDebugApp(self._backend_settings.package)
+
+  @property
+  def log_file_path(self):
+    return None
+
+  @property
+  def device(self):
+    return self.platform_backend.device
+
+  def _StopBrowser(self):
+    # Note: it's important to stop and _not_ kill the browser app, since
+    # stopping also clears the app state in Android's activity manager.
+    self.platform_backend.StopApplication(self._backend_settings.package)
+
+  def Start(self):
+    self.device.adb.Logcat(clear=True)
+    if self.browser_options.startup_url:
+      url = self.browser_options.startup_url
+    elif self.browser_options.profile_dir:
+      url = None
+    else:
+      # If we have no existing tabs start with a blank page since default
+      # startup with the NTP can lead to race conditions with Telemetry
+      url = 'about:blank'
+
+    self.platform_backend.DismissCrashDialogIfNeeded()
+
+    user_agent_dict = user_agent.GetChromeUserAgentDictFromType(
+        self.browser_options.browser_user_agent_type)
+
+    browser_startup_args = self.GetBrowserStartupArgs()
+    command_line_name = self._backend_settings.command_line_name
+    with flag_changer.CustomCommandLineFlags(
+        self.device, command_line_name, browser_startup_args):
+      self.device.StartActivity(
+          intent.Intent(package=self._backend_settings.package,
+                        activity=self._backend_settings.activity,
+                        action=None, data=url, category=None,
+                        extras=user_agent_dict),
+          blocking=True)
+
+      # TODO(crbug.com/404771): Move port forwarding to network_controller.
+      remote_devtools_port = self._backend_settings.GetDevtoolsRemotePort(
+          self.device)
+      try:
+        # Release reserved port right before forwarding host to device.
+        self._port_keeper.Release()
+        assert self._port == self._port_keeper.port, (
+          'Android browser backend must use reserved port by _port_keeper')
+        self.platform_backend.ForwardHostToDevice(
+            self._port, remote_devtools_port)
+      except Exception:
+        logging.exception('Failed to forward %s to %s.',
+            str(self._port), str(remote_devtools_port))
+        logging.warning('Currently forwarding:')
+        try:
+          for line in self.device.adb.ForwardList().splitlines():
+            logging.warning('  %s', line)
+        except Exception:
+          logging.warning('Exception raised while listing forwarded '
+                          'connections.')
+
+        logging.warning('Host tcp ports in use:')
+        try:
+          for line in subprocess.check_output(['netstat', '-t']).splitlines():
+            logging.warning('  %s', line)
+        except Exception:
+          logging.warning('Exception raised while listing tcp ports.')
+
+        logging.warning('Device unix domain sockets in use:')
+        try:
+          for line in self.device.ReadFile('/proc/net/unix', as_root=True,
+                                           force_pull=True).splitlines():
+            logging.warning('  %s', line)
+        except Exception:
+          logging.warning('Exception raised while listing unix domain sockets.')
+
+        raise
+
+      try:
+        self._WaitForBrowserToComeUp()
+        self._InitDevtoolsClientBackend(remote_devtools_port)
+      except exceptions.BrowserGoneException:
+        logging.critical('Failed to connect to browser.')
+        if not (self.device.HasRoot() or self.device.NeedsSU()):
+          logging.critical(
+            'Resolve this by either: '
+            '(1) Flashing to a userdebug build OR '
+            '(2) Manually enabling web debugging in Chrome at '
+            'Settings > Developer tools > Enable USB Web debugging.')
+        self.Close()
+        raise
+      except:
+        self.Close()
+        raise
+
+  def Foreground(self):
+    package = self._backend_settings.package
+    activity = self._backend_settings.activity
+    self.device.StartActivity(
+        intent.Intent(package=package,
+                      activity=activity,
+                      action=None,
+                      flags=[intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED]),
+        blocking=False)
+    # TODO(crbug.com/601052): The following waits for any UI node for the
+    # package launched to appear on the screen. When the referenced bug is
+    # fixed, remove this workaround and just switch blocking above to True.
+    try:
+      app_ui.AppUi(self.device).WaitForUiNode(package=package)
+    except Exception:
+      raise exceptions.BrowserGoneException(self.browser,
+          'Timed out waiting for browser to come back foreground.')
+
+  def Background(self):
+    package = 'org.chromium.push_apps_to_background'
+    activity = package + '.PushAppsToBackgroundActivity'
+    self.device.StartActivity(
+        intent.Intent(
+            package=package,
+            activity=activity,
+            action=None,
+            flags=[intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED]),
+        blocking=True)
+
+  def GetBrowserStartupArgs(self):
+    args = super(AndroidBrowserBackend, self).GetBrowserStartupArgs()
+    args.append('--enable-remote-debugging')
+    args.append('--disable-fre')
+    args.append('--disable-external-intent-requests')
+    return args
+
+  @property
+  def pid(self):
+    pids = self.device.GetPids(self._backend_settings.package)
+    if not pids or self._backend_settings.package not in pids:
+      raise exceptions.BrowserGoneException(self.browser)
+    if len(pids[self._backend_settings.package]) > 1:
+      raise Exception(
+          'At most one instance of process %s expected but found pids: '
+          '%s' % (self._backend_settings.package, pids))
+    return int(pids[self._backend_settings.package][0])
+
+  @property
+  def browser_directory(self):
+    return None
+
+  @property
+  def profile_directory(self):
+    return self._backend_settings.profile_dir
+
+  @property
+  def package(self):
+    return self._backend_settings.package
+
+  @property
+  def activity(self):
+    return self._backend_settings.activity
+
+  def __del__(self):
+    self.Close()
+
+  def Close(self):
+    super(AndroidBrowserBackend, self).Close()
+
+    self._StopBrowser()
+
+    self.platform_backend.StopForwardingHost(self._port)
+
+    if self._output_profile_path:
+      self.platform_backend.PullProfile(
+          self._backend_settings.package, self._output_profile_path)
+
+  def IsBrowserRunning(self):
+    return self.platform_backend.IsAppRunning(self._backend_settings.package)
+
+  def GetStandardOutput(self):
+    return self.platform_backend.GetStandardOutput()
+
+  def GetStackTrace(self):
+    return self.platform_backend.GetStackTrace()
+
+  def GetMostRecentMinidumpPath(self):
+    return None
+
+  def GetAllMinidumpPaths(self):
+    return None
+
+  def GetAllUnsymbolizedMinidumpPaths(self):
+    return None
+
+  def SymbolizeMinidump(self, minidump_path):
+    return None
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/android_browser_finder.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/android_browser_finder.py
new file mode 100644
index 0000000..188a3e3
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/android_browser_finder.py
@@ -0,0 +1,268 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Finds android browsers that can be controlled by telemetry."""
+
+import logging
+import os
+import subprocess
+import sys
+
+from py_utils import dependency_util
+from devil import base_error
+from devil.android import apk_helper
+
+from telemetry.core import exceptions
+from telemetry.core import platform
+from telemetry.core import util
+from telemetry import decorators
+from telemetry.internal.backends import android_browser_backend_settings
+from telemetry.internal.backends.chrome import android_browser_backend
+from telemetry.internal.browser import browser
+from telemetry.internal.browser import possible_browser
+from telemetry.internal.platform import android_device
+from telemetry.internal.util import binary_manager
+
+
+CHROME_PACKAGE_NAMES = {
+  'android-content-shell':
+      ['org.chromium.content_shell_apk',
+       android_browser_backend_settings.ContentShellBackendSettings,
+       'ContentShell.apk'],
+  'android-webview':
+      ['org.chromium.webview_shell',
+       android_browser_backend_settings.WebviewBackendSettings,
+       None],
+  'android-webview-shell':
+      ['org.chromium.android_webview.shell',
+       android_browser_backend_settings.WebviewShellBackendSettings,
+       'AndroidWebView.apk'],
+  'android-chromium':
+      ['org.chromium.chrome',
+       android_browser_backend_settings.ChromeBackendSettings,
+       'ChromePublic.apk'],
+  'android-chrome':
+      ['com.google.android.apps.chrome',
+       android_browser_backend_settings.ChromeBackendSettings,
+       'Chrome.apk'],
+  'android-chrome-work':
+      ['com.chrome.work',
+       android_browser_backend_settings.ChromeBackendSettings,
+       None],
+  'android-chrome-beta':
+      ['com.chrome.beta',
+       android_browser_backend_settings.ChromeBackendSettings,
+       None],
+  'android-chrome-dev':
+      ['com.chrome.dev',
+       android_browser_backend_settings.ChromeBackendSettings,
+       None],
+  'android-chrome-canary':
+      ['com.chrome.canary',
+       android_browser_backend_settings.ChromeBackendSettings,
+       None],
+  'android-system-chrome':
+      ['com.android.chrome',
+       android_browser_backend_settings.ChromeBackendSettings,
+       None],
+}
+
+
+class PossibleAndroidBrowser(possible_browser.PossibleBrowser):
+  """A launchable android browser instance."""
+  def __init__(self, browser_type, finder_options, android_platform,
+               backend_settings, apk_name):
+    super(PossibleAndroidBrowser, self).__init__(
+        browser_type, 'android', backend_settings.supports_tab_control)
+    assert browser_type in FindAllBrowserTypes(finder_options), (
+        'Please add %s to android_browser_finder.FindAllBrowserTypes' %
+         browser_type)
+    self._platform = android_platform
+    self._platform_backend = (
+        android_platform._platform_backend)  # pylint: disable=protected-access
+    self._backend_settings = backend_settings
+    self._local_apk = None
+
+    if browser_type == 'exact':
+      if not os.path.exists(apk_name):
+        raise exceptions.PathMissingError(
+            'Unable to find exact apk %s specified by --browser-executable' %
+            apk_name)
+      self._local_apk = apk_name
+    elif browser_type == 'reference':
+      if not os.path.exists(apk_name):
+        raise exceptions.PathMissingError(
+            'Unable to find reference apk at expected location %s.' % apk_name)
+      self._local_apk = apk_name
+    elif apk_name:
+      assert finder_options.chrome_root, (
+          'Must specify Chromium source to use apk_name')
+      chrome_root = finder_options.chrome_root
+      candidate_apks = []
+      for build_path in util.GetBuildDirectories(chrome_root):
+        apk_full_name = os.path.join(build_path, 'apks', apk_name)
+        if os.path.exists(apk_full_name):
+          last_changed = os.path.getmtime(apk_full_name)
+          candidate_apks.append((last_changed, apk_full_name))
+
+      if candidate_apks:
+        # Find the candidate .apk with the latest modification time.
+        newest_apk_path = sorted(candidate_apks)[-1][1]
+        self._local_apk = newest_apk_path
+
+  def __repr__(self):
+    return 'PossibleAndroidBrowser(browser_type=%s)' % self.browser_type
+
+  def _InitPlatformIfNeeded(self):
+    pass
+
+  def Create(self, finder_options):
+    self._InitPlatformIfNeeded()
+    browser_backend = android_browser_backend.AndroidBrowserBackend(
+        self._platform_backend,
+        finder_options.browser_options, self._backend_settings)
+    try:
+      return browser.Browser(
+          browser_backend, self._platform_backend, self._credentials_path)
+    except Exception:
+      logging.exception('Failure while creating Android browser.')
+      original_exception = sys.exc_info()
+      try:
+        browser_backend.Close()
+      except Exception:
+        logging.exception('Secondary failure while closing browser backend.')
+
+      raise original_exception[0], original_exception[1], original_exception[2]
+
+  def SupportsOptions(self, browser_options):
+    if len(browser_options.extensions_to_load) != 0:
+      return False
+    return True
+
+  def HaveLocalAPK(self):
+    return self._local_apk and os.path.exists(self._local_apk)
+
+  @decorators.Cache
+  def UpdateExecutableIfNeeded(self):
+    if self.HaveLocalAPK():
+      logging.warn('Installing %s on device if needed.' % self._local_apk)
+      self.platform.InstallApplication(self._local_apk)
+
+  def last_modification_time(self):
+    if self.HaveLocalAPK():
+      return os.path.getmtime(self._local_apk)
+    return -1
+
+
+def SelectDefaultBrowser(possible_browsers):
+  """Return the newest possible browser."""
+  if not possible_browsers:
+    return None
+  return max(possible_browsers, key=lambda b: b.last_modification_time())
+
+
+def CanFindAvailableBrowsers():
+  return android_device.CanDiscoverDevices()
+
+
+def CanPossiblyHandlePath(target_path):
+  return os.path.splitext(target_path.lower())[1] == '.apk'
+
+
+def FindAllBrowserTypes(options):
+  del options  # unused
+  return CHROME_PACKAGE_NAMES.keys() + ['exact', 'reference']
+
+
+def _FindAllPossibleBrowsers(finder_options, android_platform):
+  """Testable version of FindAllAvailableBrowsers."""
+  if not android_platform:
+    return []
+  possible_browsers = []
+
+  # Add the exact APK if given.
+  if (finder_options.browser_executable and
+      CanPossiblyHandlePath(finder_options.browser_executable)):
+    apk_name = os.path.basename(finder_options.browser_executable)
+    normalized_path = os.path.expanduser(finder_options.browser_executable)
+    exact_package = apk_helper.GetPackageName(normalized_path)
+    package_info = next(
+        (info for info in CHROME_PACKAGE_NAMES.itervalues()
+         if info[0] == exact_package or info[2] == apk_name), None)
+
+    # It is okay if the APK name or package doesn't match any of known chrome
+    # browser APKs, since it may be of a different browser.
+    if package_info:
+      if not exact_package:
+        raise exceptions.PackageDetectionError(
+            'Unable to find package for %s specified by --browser-executable' %
+            normalized_path)
+
+      [package, backend_settings, _] = package_info
+      if package == exact_package:
+        possible_browsers.append(PossibleAndroidBrowser(
+            'exact',
+            finder_options,
+            android_platform,
+            backend_settings(package),
+            normalized_path))
+      else:
+        raise exceptions.UnknownPackageError(
+            '%s specified by --browser-executable has an unknown package: %s' %
+            (normalized_path, exact_package))
+
+  # Add the reference build if found.
+  os_version = dependency_util.GetChromeApkOsVersion(
+      android_platform.GetOSVersionName())
+  arch = android_platform.GetArchName()
+  try:
+    reference_build = binary_manager.FetchPath(
+        'chrome_stable', arch, 'android', os_version)
+  except (binary_manager.NoPathFoundError,
+          binary_manager.CloudStorageError):
+    reference_build = None
+
+  if reference_build and os.path.exists(reference_build):
+    # TODO(aiolos): how do we stably map the android chrome_stable apk to the
+    # correct package name?
+    package, backend_settings, _ = CHROME_PACKAGE_NAMES['android-chrome']
+    possible_browsers.append(PossibleAndroidBrowser(
+        'reference',
+        finder_options,
+        android_platform,
+        backend_settings(package),
+        reference_build))
+
+  # Add any known local versions.
+  for name, package_info in CHROME_PACKAGE_NAMES.iteritems():
+    package, backend_settings, apk_name = package_info
+    if apk_name and not finder_options.chrome_root:
+      continue
+    b = PossibleAndroidBrowser(name,
+                               finder_options,
+                               android_platform,
+                               backend_settings(package),
+                               apk_name)
+    if b.platform.CanLaunchApplication(package) or b.HaveLocalAPK():
+      possible_browsers.append(b)
+  return possible_browsers
+
+
+def FindAllAvailableBrowsers(finder_options, device):
+  """Finds all the possible browsers on one device.
+
+  The device is either the only device on the host platform,
+  or |finder_options| specifies a particular device.
+  """
+  if not isinstance(device, android_device.AndroidDevice):
+    return []
+
+  try:
+    android_platform = platform.GetPlatformForDevice(device, finder_options)
+    return _FindAllPossibleBrowsers(finder_options, android_platform)
+  except base_error.BaseError as e:
+    logging.error('Unable to find browsers on %s: %s', device.device_id, str(e))
+    ps_output = subprocess.check_output(['ps', '-ef'])
+    logging.error('Ongoing processes:\n%s', ps_output)
+  return []
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/android_browser_finder_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/android_browser_finder_unittest.py
new file mode 100644
index 0000000..7425e57
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/android_browser_finder_unittest.py
@@ -0,0 +1,192 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import unittest
+
+import mock
+from pyfakefs import fake_filesystem_unittest
+
+from telemetry.core import android_platform
+from telemetry.internal.backends.chrome import android_browser_finder
+from telemetry.internal.platform import android_platform_backend
+from telemetry.internal.util import binary_manager
+from telemetry.testing import options_for_unittests
+
+
+def FakeFetchPath(dependency, arch, os_name, os_version=None):
+  return os.path.join(
+      'dependency_dir', dependency, '%s_%s_%s.apk' % (
+        os_name, os_version, arch))
+
+
+class AndroidBrowserFinderTest(fake_filesystem_unittest.TestCase):
+  def setUp(self):
+    self.finder_options = options_for_unittests.GetCopy()
+    # Mock out what's needed for testing with exact APKs
+    self.setUpPyfakefs()
+    self._fetch_path_patcher = mock.patch(
+        'telemetry.internal.backends.chrome.android_browser_finder.binary_manager.FetchPath',  # pylint: disable=line-too-long
+        FakeFetchPath)
+    self._fetch_path_mock = self._fetch_path_patcher.start()
+    self._get_package_name_patcher = mock.patch(
+        'devil.android.apk_helper.GetPackageName')
+    self._get_package_name_mock = self._get_package_name_patcher.start()
+    self.fake_platform = mock.Mock(spec=android_platform.AndroidPlatform)
+    self.fake_platform.CanLaunchApplication.return_value = True
+    self.fake_platform._platform_backend = mock.create_autospec(
+        android_platform_backend, spec_set=True)
+    self.fake_platform.GetOSVersionName.return_value = 'L23ds5'
+    self.fake_platform.GetArchName.return_value = 'armeabi-v7a'
+    # The android_browser_finder converts the os version name to 'k' or 'l'
+    self.expected_reference_build = FakeFetchPath(
+        'chrome_stable', 'armeabi-v7a', 'android', 'l')
+
+  def tearDown(self):
+    self.tearDownPyfakefs()
+    self._get_package_name_patcher.stop()
+    self._fetch_path_patcher.stop()
+
+  def testNoPlatformReturnsEmptyList(self):
+    fake_platform = None
+    possible_browsers = android_browser_finder._FindAllPossibleBrowsers(
+        self.finder_options, fake_platform)
+    self.assertEqual([], possible_browsers)
+
+  def testCanLaunchAlwaysTrueReturnsAllExceptExactAndReference(self):
+    if not self.finder_options.chrome_root:
+      self.skipTest('--chrome-root is not specified, skip the test')
+    all_types = set(
+        android_browser_finder.FindAllBrowserTypes(self.finder_options))
+    expected_types = all_types - set(('exact', 'reference'))
+    possible_browsers = android_browser_finder._FindAllPossibleBrowsers(
+        self.finder_options, self.fake_platform)
+    self.assertEqual(
+        expected_types,
+        set([b.browser_type for b in possible_browsers]))
+
+  def testCanLaunchAlwaysTrueReturnsAllExceptExact(self):
+    if not self.finder_options.chrome_root:
+      self.skipTest('--chrome-root is not specified, skip the test')
+    self.fs.CreateFile(self.expected_reference_build)
+    all_types = set(
+        android_browser_finder.FindAllBrowserTypes(self.finder_options))
+    expected_types = all_types - set(('exact',))
+    possible_browsers = android_browser_finder._FindAllPossibleBrowsers(
+        self.finder_options, self.fake_platform)
+    self.assertEqual(
+        expected_types,
+        set([b.browser_type for b in possible_browsers]))
+
+  def testCanLaunchAlwaysTrueWithExactApkReturnsAll(self):
+    if not self.finder_options.chrome_root:
+      self.skipTest('--chrome-root is not specified, skip the test')
+    self.fs.CreateFile(
+        '/foo/ContentShell.apk')
+    self.fs.CreateFile(self.expected_reference_build)
+    self.finder_options.browser_executable = '/foo/ContentShell.apk'
+    self._get_package_name_mock.return_value = 'org.chromium.content_shell_apk'
+
+    expected_types = set(
+        android_browser_finder.FindAllBrowserTypes(self.finder_options))
+    possible_browsers = android_browser_finder._FindAllPossibleBrowsers(
+        self.finder_options, self.fake_platform)
+    self.assertEqual(
+        expected_types,
+        set([b.browser_type for b in possible_browsers]))
+
+  def testErrorWithUnknownExactApk(self):
+    self.fs.CreateFile(
+        '/foo/ContentShell.apk')
+    self.finder_options.browser_executable = '/foo/ContentShell.apk'
+    self._get_package_name_mock.return_value = 'org.unknown.app'
+
+    self.assertRaises(Exception,
+        android_browser_finder._FindAllPossibleBrowsers,
+        self.finder_options, self.fake_platform)
+
+  def testErrorWithNonExistantExactApk(self):
+    self.finder_options.browser_executable = '/foo/ContentShell.apk'
+    self._get_package_name_mock.return_value = 'org.chromium.content_shell_apk'
+
+    self.assertRaises(Exception,
+        android_browser_finder._FindAllPossibleBrowsers,
+        self.finder_options, self.fake_platform)
+
+  def testNoErrorWithUnrecognizedApkName(self):
+    if not self.finder_options.chrome_root:
+      self.skipTest('--chrome-root is not specified, skip the test')
+    self.fs.CreateFile(
+        '/foo/unknown.apk')
+    self.finder_options.browser_executable = '/foo/unknown.apk'
+
+    possible_browsers = android_browser_finder._FindAllPossibleBrowsers(
+        self.finder_options, self.fake_platform)
+    self.assertNotIn('exact', [b.browser_type for b in possible_browsers])
+
+  def testCanLaunchExactWithUnrecognizedApkNameButKnownPackageName(self):
+    if not self.finder_options.chrome_root:
+      self.skipTest('--chrome-root is not specified, skip the test')
+    self.fs.CreateFile(
+        '/foo/MyFooBrowser.apk')
+    self._get_package_name_mock.return_value = 'org.chromium.chrome'
+    self.finder_options.browser_executable = '/foo/MyFooBrowser.apk'
+
+    possible_browsers = android_browser_finder._FindAllPossibleBrowsers(
+        self.finder_options, self.fake_platform)
+    self.assertIn('exact', [b.browser_type for b in possible_browsers])
+
+  def testNoErrorWithMissingReferenceBuild(self):
+    if not self.finder_options.chrome_root:
+      self.skipTest('--chrome-root is not specified, skip the test')
+    possible_browsers = android_browser_finder._FindAllPossibleBrowsers(
+      self.finder_options, self.fake_platform)
+    self.assertNotIn('reference', [b.browser_type for b in possible_browsers])
+
+  def testNoErrorWithReferenceBuildCloudStorageError(self):
+    if not self.finder_options.chrome_root:
+      self.skipTest('--chrome-root is not specified, skip the test')
+    with mock.patch(
+        'telemetry.internal.backends.chrome.android_browser_finder.binary_manager.FetchPath',  # pylint: disable=line-too-long
+        side_effect=binary_manager.CloudStorageError):
+      possible_browsers = android_browser_finder._FindAllPossibleBrowsers(
+        self.finder_options, self.fake_platform)
+    self.assertNotIn('reference', [b.browser_type for b in possible_browsers])
+
+  def testNoErrorWithReferenceBuildNoPathFoundError(self):
+    if not self.finder_options.chrome_root:
+      self.skipTest('--chrome-root is not specified, skip the test')
+    self._fetch_path_mock.side_effect = binary_manager.NoPathFoundError
+    possible_browsers = android_browser_finder._FindAllPossibleBrowsers(
+      self.finder_options, self.fake_platform)
+    self.assertNotIn('reference', [b.browser_type for b in possible_browsers])
+
+
+class FakePossibleBrowser(object):
+  def __init__(self, last_modification_time):
+    self._last_modification_time = last_modification_time
+
+  def last_modification_time(self):
+    return self._last_modification_time
+
+
+class SelectDefaultBrowserTest(unittest.TestCase):
+  def testEmptyListGivesNone(self):
+    self.assertIsNone(android_browser_finder.SelectDefaultBrowser([]))
+
+  def testSinglePossibleReturnsSame(self):
+    possible_browsers = [FakePossibleBrowser(last_modification_time=1)]
+    self.assertIs(
+      possible_browsers[0],
+      android_browser_finder.SelectDefaultBrowser(possible_browsers))
+
+  def testListGivesNewest(self):
+    possible_browsers = [
+        FakePossibleBrowser(last_modification_time=2),
+        FakePossibleBrowser(last_modification_time=3),  # newest
+        FakePossibleBrowser(last_modification_time=1),
+        ]
+    self.assertIs(
+      possible_browsers[1],
+      android_browser_finder.SelectDefaultBrowser(possible_browsers))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/chrome_browser_backend.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/chrome_browser_backend.py
new file mode 100644
index 0000000..80d2cf3
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/chrome_browser_backend.py
@@ -0,0 +1,310 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+import pprint
+import shlex
+import sys
+
+from telemetry.core import exceptions
+from telemetry.core import util
+from telemetry import decorators
+from telemetry.internal.backends import browser_backend
+from telemetry.internal.backends.chrome import extension_backend
+from telemetry.internal.backends.chrome import system_info_backend
+from telemetry.internal.backends.chrome import tab_list_backend
+from telemetry.internal.backends.chrome_inspector import devtools_client_backend
+from telemetry.internal.browser import user_agent
+from telemetry.internal.browser import web_contents
+from telemetry.testing import options_for_unittests
+
+import py_utils
+
+
+class ChromeBrowserBackend(browser_backend.BrowserBackend):
+  """An abstract class for chrome browser backends. Provides basic functionality
+  once a remote-debugger port has been established."""
+  # It is OK to have abstract methods. pylint: disable=abstract-method
+
+  def __init__(self, platform_backend, supports_tab_control,
+               supports_extensions, browser_options):
+    super(ChromeBrowserBackend, self).__init__(
+        platform_backend=platform_backend,
+        supports_extensions=supports_extensions,
+        browser_options=browser_options,
+        tab_list_backend=tab_list_backend.TabListBackend)
+    self._port = None
+
+    self._supports_tab_control = supports_tab_control
+    self._devtools_client = None
+    self._system_info_backend = None
+
+    self._output_profile_path = browser_options.output_profile_path
+    self._extensions_to_load = browser_options.extensions_to_load
+
+    if (self.browser_options.dont_override_profile and
+        not options_for_unittests.AreSet()):
+      sys.stderr.write('Warning: Not overriding profile. This can cause '
+                       'unexpected effects due to profile-specific settings, '
+                       'such as about:flags settings, cookies, and '
+                       'extensions.\n')
+
+  @property
+  def devtools_client(self):
+    return self._devtools_client
+
+  @property
+  @decorators.Cache
+  def extension_backend(self):
+    if not self.supports_extensions:
+      return None
+    return extension_backend.ExtensionBackendDict(self)
+
+  def _ArgsNeedProxyServer(self, args):
+    """Returns True if args for Chrome indicate the need for proxy server."""
+    if '--enable-spdy-proxy-auth' in args:
+      return True
+    return [arg for arg in args if arg.startswith('--proxy-server=')]
+
+  def GetBrowserStartupArgs(self):
+    assert not '--no-proxy-server' in self.browser_options.extra_browser_args, (
+        '--no-proxy-server flag is disallowed as Chrome needs to be route to '
+        'ts_proxy_server')
+    args = []
+    args.extend(self.browser_options.extra_browser_args)
+    args.append('--enable-net-benchmarking')
+    args.append('--metrics-recording-only')
+    args.append('--no-default-browser-check')
+    args.append('--no-first-run')
+
+    # Turn on GPU benchmarking extension for all runs. The only side effect of
+    # the extension being on is that render stats are tracked. This is believed
+    # to be effectively free. And, by doing so here, it avoids us having to
+    # programmatically inspect a pageset's actions in order to determine if it
+    # might eventually scroll.
+    args.append('--enable-gpu-benchmarking')
+
+    if self.browser_options.disable_background_networking:
+      args.append('--disable-background-networking')
+    args.extend(self.GetReplayBrowserStartupArgs())
+    args.extend(user_agent.GetChromeUserAgentArgumentFromType(
+        self.browser_options.browser_user_agent_type))
+
+    extensions = [extension.local_path
+                  for extension in self._extensions_to_load]
+    extension_str = ','.join(extensions)
+    if len(extensions) > 0:
+      args.append('--load-extension=%s' % extension_str)
+
+    if self.browser_options.disable_component_extensions_with_background_pages:
+      args.append('--disable-component-extensions-with-background-pages')
+
+    # Disables the start page, as well as other external apps that can
+    # steal focus or make measurements inconsistent.
+    if self.browser_options.disable_default_apps:
+      args.append('--disable-default-apps')
+
+    # Disable the search geolocation disclosure infobar, as it is only shown a
+    # small number of times to users and should not be part of perf comparisons.
+    args.append('--disable-search-geolocation-disclosure')
+
+    if (self.browser_options.logging_verbosity ==
+        self.browser_options.NON_VERBOSE_LOGGING):
+      args.extend(['--enable-logging', '--v=0'])
+    elif (self.browser_options.logging_verbosity ==
+          self.browser_options.VERBOSE_LOGGING):
+      args.extend(['--enable-logging', '--v=1'])
+
+    return args
+
+  def GetReplayBrowserStartupArgs(self):
+    replay_args = []
+    network_backend = self.platform_backend.network_controller_backend
+    if not network_backend.is_initialized:
+      return []
+    proxy_port = network_backend.forwarder.port_pair.remote_port
+    replay_args.append('--proxy-server=socks://localhost:%s' % proxy_port)
+    if not network_backend.is_test_ca_installed:
+      # Ignore certificate errors if the platform backend has not created
+      # and installed a root certificate.
+      replay_args.append('--ignore-certificate-errors')
+    return replay_args
+
+  def HasBrowserFinishedLaunching(self):
+    assert self._port, 'No DevTools port info available.'
+    return devtools_client_backend.IsDevToolsAgentAvailable(self._port, self)
+
+  def _InitDevtoolsClientBackend(self, remote_devtools_port=None):
+    """ Initiate the devtool client backend which allow browser connection
+    through browser' devtool.
+
+    Args:
+      remote_devtools_port: The remote devtools port, if
+          any. Otherwise assumed to be the same as self._port.
+    """
+    assert not self._devtools_client, (
+        'Devtool client backend cannot be init twice')
+    self._devtools_client = devtools_client_backend.DevToolsClientBackend(
+        self._port, remote_devtools_port or self._port, self)
+
+  def _WaitForBrowserToComeUp(self):
+    """ Wait for browser to come up. """
+    try:
+      timeout = self.browser_options.browser_startup_timeout
+      py_utils.WaitFor(self.HasBrowserFinishedLaunching, timeout=timeout)
+    except (py_utils.TimeoutException, exceptions.ProcessGoneException) as e:
+      if not self.IsBrowserRunning():
+        raise exceptions.BrowserGoneException(self.browser, e)
+      raise exceptions.BrowserConnectionGoneException(self.browser, e)
+
+  def _WaitForExtensionsToLoad(self):
+    """ Wait for all extensions to load.
+    Be sure to check whether the browser_backend supports_extensions before
+    calling this method.
+    """
+    assert self._supports_extensions
+    assert self._devtools_client, (
+        'Waiting for extensions required devtool client to be initiated first')
+    try:
+      py_utils.WaitFor(self._AllExtensionsLoaded, timeout=60)
+    except py_utils.TimeoutException:
+      logging.error('ExtensionsToLoad: ' +
+          repr([e.extension_id for e in self._extensions_to_load]))
+      logging.error('Extension list: ' +
+          pprint.pformat(self.extension_backend, indent=4))
+      raise
+
+  def _AllExtensionsLoaded(self):
+    # Extension pages are loaded from an about:blank page,
+    # so we need to check that the document URL is the extension
+    # page in addition to the ready state.
+    for e in self._extensions_to_load:
+      try:
+        extension_objects = self.extension_backend[e.extension_id]
+      except KeyError:
+        return False
+      for extension_object in extension_objects:
+        try:
+          res = extension_object.EvaluateJavaScript("""
+              document.URL.lastIndexOf({{ url }}, 0) == 0 &&
+              (document.readyState == 'complete' ||
+               document.readyState == 'interactive')
+              """,
+              url='chrome-extension://%s/' % e.extension_id)
+        except exceptions.EvaluateException:
+          # If the inspected page is not ready, we will get an error
+          # when we evaluate a JS expression, but we can just keep polling
+          # until the page is ready (crbug.com/251913).
+          res = None
+
+        # TODO(tengs): We don't have full support for getting the Chrome
+        # version before launch, so for now we use a generic workaround to
+        # check for an extension binding bug in old versions of Chrome.
+        # See crbug.com/263162 for details.
+        if res and extension_object.EvaluateJavaScript(
+            'chrome.runtime == null'):
+          extension_object.Reload()
+        if not res:
+          return False
+    return True
+
+  @property
+  def browser_directory(self):
+    raise NotImplementedError()
+
+  @property
+  def profile_directory(self):
+    raise NotImplementedError()
+
+  @property
+  def supports_tab_control(self):
+    return self._supports_tab_control
+
+  @property
+  def supports_tracing(self):
+    return True
+
+  def StartTracing(self, trace_options,
+                   timeout=web_contents.DEFAULT_WEB_CONTENTS_TIMEOUT):
+    """
+    Args:
+        trace_options: An tracing_options.TracingOptions instance.
+    """
+    return self.devtools_client.StartChromeTracing(trace_options, timeout)
+
+  def StopTracing(self):
+    self.devtools_client.StopChromeTracing()
+
+  def CollectTracingData(self, trace_data_builder):
+    self.devtools_client.CollectChromeTracingData(trace_data_builder)
+
+  def GetProcessName(self, cmd_line):
+    """Returns a user-friendly name for the process of the given |cmd_line|."""
+    if not cmd_line:
+      # TODO(tonyg): Eventually we should make all of these known and add an
+      # assertion.
+      return 'unknown'
+    if 'nacl_helper_bootstrap' in cmd_line:
+      return 'nacl_helper_bootstrap'
+    if ':sandboxed_process' in cmd_line:
+      return 'renderer'
+    if ':privileged_process' in cmd_line:
+      return 'gpu-process'
+    args = shlex.split(cmd_line)
+    types = [arg.split('=')[1] for arg in args if arg.startswith('--type=')]
+    if not types:
+      return 'browser'
+    return types[0]
+
+  def Close(self):
+    if self._devtools_client:
+      self._devtools_client.Close()
+      self._devtools_client = None
+
+  @property
+  def supports_system_info(self):
+    return self.GetSystemInfo() != None
+
+  def GetSystemInfo(self):
+    if self._system_info_backend is None:
+      self._system_info_backend = system_info_backend.SystemInfoBackend(
+          self._port)
+    # TODO(crbug.com/706336): Remove this condional branch once crbug.com/704024
+    # is fixed.
+    if util.IsRunningOnCrosDevice():
+      return self._system_info_backend.GetSystemInfo(timeout=30)
+    return self._system_info_backend.GetSystemInfo()
+
+  @property
+  def supports_memory_dumping(self):
+    return True
+
+  def DumpMemory(self, timeout=web_contents.DEFAULT_WEB_CONTENTS_TIMEOUT):
+    return self.devtools_client.DumpMemory(timeout)
+
+  @property
+  def supports_overriding_memory_pressure_notifications(self):
+    return True
+
+  def SetMemoryPressureNotificationsSuppressed(
+      self, suppressed, timeout=web_contents.DEFAULT_WEB_CONTENTS_TIMEOUT):
+    self.devtools_client.SetMemoryPressureNotificationsSuppressed(
+        suppressed, timeout)
+
+  def SimulateMemoryPressureNotification(
+      self, pressure_level, timeout=web_contents.DEFAULT_WEB_CONTENTS_TIMEOUT):
+    self.devtools_client.SimulateMemoryPressureNotification(
+        pressure_level, timeout)
+
+  @property
+  def supports_cpu_metrics(self):
+    return True
+
+  @property
+  def supports_memory_metrics(self):
+    return True
+
+  @property
+  def supports_power_metrics(self):
+    return True
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/chrome_browser_backend_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/chrome_browser_backend_unittest.py
new file mode 100644
index 0000000..a1691aa
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/chrome_browser_backend_unittest.py
@@ -0,0 +1,100 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+import mock
+
+from telemetry.internal import forwarders
+from telemetry.internal.backends.chrome import chrome_browser_backend
+from telemetry.internal.browser import browser_options as browser_options_module
+from telemetry.util import wpr_modes
+
+
+class FakePlatformBackend(object):
+  def __init__(self, is_initialized, local_ts_proxy_port, remote_port,
+               is_host_platform):
+    self.is_host_platform = is_host_platform
+
+    self.forwarder_factory = mock.Mock()
+
+    self.network_controller_backend = mock.Mock()
+    self.network_controller_backend.is_initialized = is_initialized
+    if is_initialized:
+      self.network_controller_backend.forwarder.port_pair = forwarders.PortPair(
+          local_port=local_ts_proxy_port, remote_port=remote_port)
+    else:
+      self.network_controller_backend.forwarder = None
+    self.network_controller_backend.host_ip = '127.0.0.1'
+    self.network_controller_backend.is_test_ca_installed = False
+
+
+class FakeBrowserOptions(browser_options_module.BrowserOptions):
+  def __init__(self, wpr_mode=wpr_modes.WPR_OFF):
+    super(FakeBrowserOptions, self).__init__()
+    self.wpr_mode = wpr_mode
+    self.browser_type = 'chrome'
+    self.browser_user_agent_type = 'desktop'
+    self.disable_background_networking = False
+    self.disable_component_extensions_with_background_pages = False
+    self.disable_default_apps = False
+
+
+class TestChromeBrowserBackend(chrome_browser_backend.ChromeBrowserBackend):
+  # The test does not need to define the abstract methods.
+  # pylint: disable=abstract-method
+
+  def __init__(self, browser_options,
+               local_ts_proxy_port=None,
+               remote_port=None,
+               is_running_locally=False):
+    browser_options.extensions_to_load = []
+    browser_options.output_profile_path = None
+    super(TestChromeBrowserBackend, self).__init__(
+        platform_backend=FakePlatformBackend(
+            browser_options.wpr_mode != wpr_modes.WPR_OFF,
+            local_ts_proxy_port, remote_port, is_running_locally),
+        supports_tab_control=False,
+        supports_extensions=False,
+        browser_options=browser_options)
+
+
+class ReplayStartupArgsTest(unittest.TestCase):
+  """Test expected inputs for GetReplayBrowserStartupArgs."""
+
+  def testReplayOffGivesEmptyArgs(self):
+    browser_options = FakeBrowserOptions()
+    browser_backend = TestChromeBrowserBackend(browser_options)
+    self.assertEqual([], browser_backend.GetReplayBrowserStartupArgs())
+
+  def BasicArgsHelper(self, is_running_locally):
+    browser_options = FakeBrowserOptions(wpr_mode=wpr_modes.WPR_REPLAY)
+    browser_backend = TestChromeBrowserBackend(
+        browser_options,
+        local_ts_proxy_port=567,
+        remote_port=789,
+        is_running_locally=is_running_locally)
+    expected_args = [
+        '--ignore-certificate-errors',
+        '--proxy-server=socks://localhost:789',
+        ]
+    self.assertEqual(
+        expected_args,
+        sorted(browser_backend.GetReplayBrowserStartupArgs()))
+
+  def testBasicArgs(self):
+    # The result is the same regardless of whether running locally.
+    self.BasicArgsHelper(is_running_locally=True)
+    self.BasicArgsHelper(is_running_locally=False)
+
+  def testReplayNotActive(self):
+    browser_options = FakeBrowserOptions(wpr_mode=wpr_modes.WPR_OFF)
+    browser_backend = TestChromeBrowserBackend(
+        browser_options,
+        local_ts_proxy_port=567,
+        remote_port=789,
+        is_running_locally=True)
+    expected_args = []
+    self.assertEqual(
+        expected_args,
+        sorted(browser_backend.GetReplayBrowserStartupArgs()))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/chromeos_login_ext/main.html b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/chromeos_login_ext/main.html
new file mode 100644
index 0000000..07a643c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/chromeos_login_ext/main.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <script src="main.js"></script>
+  </head>
+  <body>
+  </body>
+</html>
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/chromeos_login_ext/main.js b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/chromeos_login_ext/main.js
new file mode 100644
index 0000000..7d21eb3
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/chromeos_login_ext/main.js
@@ -0,0 +1,18 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+var PARENT_PAGE = 'chrome://oobe/';
+
+var msg = {
+  'method': 'loginUILoaded'
+};
+window.parent.postMessage(msg, PARENT_PAGE);
+
+var msg = {
+  'method': 'completeLogin',
+  'email': 'test@test.test',
+  'gaiaId': '12345',
+  'password': ''
+};
+window.parent.postMessage(msg, PARENT_PAGE);
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/chromeos_login_ext/manifest.json b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/chromeos_login_ext/manifest.json
new file mode 100644
index 0000000..ec809c6
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/chromeos_login_ext/manifest.json
@@ -0,0 +1,16 @@
+{
+  // chrome-extension://mfffpogegjflfpflabcdkioaeobkgjik/
+  "key": "MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQC4L17nAfeTd6Xhtx96WhQ6DSr8KdHeQmfzgCkieKLCgUkWdwB9G1DCuh0EPMDn1MdtSwUAT7xE36APEzi0X/UpKjOVyX8tCC3aQcLoRAE0aJAvCcGwK7qIaQaczHmHKvPC2lrRdzSoMMTC5esvHX+ZqIBMi123FOL0dGW6OPKzIwIBIw==",
+  "name": "GaiaDummyAuthExtension",
+  "version": "0.0.1",
+  "manifest_version": 2,
+  "content_security_policy": "default-src 'self' blob: filesystem:; script-src 'self' blob: filesystem:; style-src 'self' blob: filesystem: 'unsafe-inline'",
+  "description": "GAIA Dummy Component Extension",
+  "web_accessible_resources": [
+    "main.html",
+    "main.js"
+  ],
+  "permissions": [
+      "chrome://oobe/"
+  ]
+}
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/cros_browser_backend.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/cros_browser_backend.py
new file mode 100644
index 0000000..4ff1b19
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/cros_browser_backend.py
@@ -0,0 +1,277 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+import os
+
+from telemetry.core import exceptions
+from telemetry.core import util
+from telemetry import decorators
+from telemetry.internal.backends.chrome import chrome_browser_backend
+from telemetry.internal.backends.chrome import misc_web_contents_backend
+from telemetry.internal import forwarders
+
+import py_utils
+
+
+class CrOSBrowserBackend(chrome_browser_backend.ChromeBrowserBackend):
+  def __init__(self, cros_platform_backend, browser_options, cri, is_guest):
+    super(CrOSBrowserBackend, self).__init__(
+        cros_platform_backend, supports_tab_control=True,
+        supports_extensions=not is_guest,
+        browser_options=browser_options)
+    assert browser_options.IsCrosBrowserOptions()
+    # Initialize fields so that an explosion during init doesn't break in Close.
+    self._cri = cri
+    self._is_guest = is_guest
+    self._forwarder = None
+    self._remote_debugging_port = self._cri.GetRemotePort()
+    self._port = self._remote_debugging_port
+
+    extensions_to_load = browser_options.extensions_to_load
+
+    # Copy extensions to temp directories on the device.
+    # Note that we also perform this copy locally to ensure that
+    # the owner of the extensions is set to chronos.
+    for e in extensions_to_load:
+      extension_dir = cri.RunCmdOnDevice(
+          ['mktemp', '-d', '/tmp/extension_XXXXX'])[0].rstrip()
+      e.local_path = os.path.join(extension_dir, os.path.basename(e.path))
+      cri.PushFile(e.path, extension_dir)
+      cri.Chown(extension_dir)
+
+    self._cri.RestartUI(self.browser_options.clear_enterprise_policy)
+    py_utils.WaitFor(self.IsBrowserRunning, 20)
+
+    # Delete test user's cryptohome vault (user data directory).
+    if not self.browser_options.dont_override_profile:
+      self._cri.RunCmdOnDevice(['cryptohome', '--action=remove', '--force',
+                                '--user=%s' % self._username])
+
+  @property
+  def log_file_path(self):
+    return None
+
+  def GetBrowserStartupArgs(self):
+    args = super(CrOSBrowserBackend, self).GetBrowserStartupArgs()
+
+    logging_patterns = ['*/chromeos/net/*',
+                        '*/chromeos/login/*',
+                        'chrome_browser_main_posix']
+    vmodule = '--vmodule='
+    for pattern in logging_patterns:
+      vmodule += '%s=2,' % pattern
+    vmodule = vmodule.rstrip(',')
+
+    args.extend([
+            '--enable-smooth-scrolling',
+            '--enable-threaded-compositing',
+            # Allow devtools to connect to chrome.
+            '--remote-debugging-port=%i' % self._remote_debugging_port,
+            # Open a maximized window.
+            '--start-maximized',
+            # Disable system startup sound.
+            '--ash-disable-system-sounds',
+            # Ignore DMServer errors for policy fetches.
+            '--allow-failed-policy-fetch-for-test',
+            # Skip user image selection screen, and post login screens.
+            '--oobe-skip-postlogin',
+            # Debug logging.
+            vmodule])
+
+    # Disable GAIA services unless we're using GAIA login, or if there's an
+    # explicit request for it.
+    if (self.browser_options.disable_gaia_services and
+        not self.browser_options.gaia_login):
+      args.append('--disable-gaia-services')
+
+    trace_config_file = (self.platform_backend.tracing_controller_backend
+                         .GetChromeTraceConfigFile())
+    if trace_config_file:
+      args.append('--trace-config-file=%s' % trace_config_file)
+
+    return args
+
+  @property
+  def pid(self):
+    return self._cri.GetChromePid()
+
+  @property
+  def browser_directory(self):
+    result = self._cri.GetChromeProcess()
+    if result and 'path' in result:
+      return os.path.dirname(result['path'])
+    return None
+
+  @property
+  def profile_directory(self):
+    return '/home/chronos/Default'
+
+  def __del__(self):
+    self.Close()
+
+  def Start(self):
+    # Escape all commas in the startup arguments we pass to Chrome
+    # because dbus-send delimits array elements by commas
+    startup_args = [a.replace(',', '\\,') for a in self.GetBrowserStartupArgs()]
+
+    # Restart Chrome with the login extension and remote debugging.
+    pid = self.pid
+    logging.info('Restarting Chrome (pid=%d) with remote port', pid)
+    args = ['dbus-send', '--system', '--type=method_call',
+            '--dest=org.chromium.SessionManager',
+            '/org/chromium/SessionManager',
+            'org.chromium.SessionManagerInterface.EnableChromeTesting',
+            'boolean:true',
+            'array:string:"%s"' % ','.join(startup_args)]
+    logging.info(' '.join(args))
+    self._cri.RunCmdOnDevice(args)
+
+    if not self._cri.local:
+      # TODO(crbug.com/404771): Move port forwarding to network_controller.
+      self._port = util.GetUnreservedAvailableLocalPort()
+      self._forwarder = self._platform_backend.forwarder_factory.Create(
+          forwarders.PortPair(self._port, self._remote_debugging_port),
+          use_remote_port_forwarding=False)
+
+    # Wait for new chrome and oobe.
+    py_utils.WaitFor(lambda: pid != self.pid, 15)
+    self._WaitForBrowserToComeUp()
+    self._InitDevtoolsClientBackend(
+        remote_devtools_port=self._remote_debugging_port)
+    py_utils.WaitFor(lambda: self.oobe_exists, 30)
+
+    if self.browser_options.auto_login:
+      if self._is_guest:
+        pid = self.pid
+        self.oobe.NavigateGuestLogin()
+        # Guest browsing shuts down the current browser and launches an
+        # incognito browser in a separate process, which we need to wait for.
+        try:
+          # TODO(achuith): Reduce this timeout to 15 sec after crbug.com/631640
+          # is resolved.
+          py_utils.WaitFor(lambda: pid != self.pid, 60)
+        except py_utils.TimeoutException:
+          self._RaiseOnLoginFailure(
+              'Failed to restart browser in guest mode (pid %d).' % pid)
+
+      elif self.browser_options.gaia_login:
+        self.oobe.NavigateGaiaLogin(self._username, self._password)
+      else:
+        self.oobe.NavigateFakeLogin(self._username, self._password,
+            self._gaia_id, not self.browser_options.disable_gaia_services)
+
+      try:
+        self._WaitForLogin()
+      except py_utils.TimeoutException:
+        self._RaiseOnLoginFailure('Timed out going through login screen. '
+                                  + self._GetLoginStatus())
+
+    logging.info('Browser is up!')
+
+  def Background(self):
+    raise NotImplementedError
+
+  def Close(self):
+    super(CrOSBrowserBackend, self).Close()
+
+    if self._cri:
+      self._cri.RestartUI(False) # Logs out.
+      self._cri.CloseConnection()
+
+    py_utils.WaitFor(lambda: not self._IsCryptohomeMounted(), 180)
+
+    if self._forwarder:
+      self._forwarder.Close()
+      self._forwarder = None
+
+    if self._cri:
+      for e in self._extensions_to_load:
+        self._cri.RmRF(os.path.dirname(e.local_path))
+
+    self._cri = None
+
+  def IsBrowserRunning(self):
+    return bool(self.pid)
+
+  def GetStandardOutput(self):
+    return 'Cannot get standard output on CrOS'
+
+  def GetStackTrace(self):
+    return (False, 'Cannot get stack trace on CrOS')
+
+  def GetMostRecentMinidumpPath(self):
+    return None
+
+  def GetAllMinidumpPaths(self):
+    return None
+
+  def GetAllUnsymbolizedMinidumpPaths(self):
+    return None
+
+  def SymbolizeMinidump(self, minidump_path):
+    return None
+
+  @property
+  @decorators.Cache
+  def misc_web_contents_backend(self):
+    """Access to chrome://oobe/login page."""
+    return misc_web_contents_backend.MiscWebContentsBackend(self)
+
+  @property
+  def oobe(self):
+    return self.misc_web_contents_backend.GetOobe()
+
+  @property
+  def oobe_exists(self):
+    return self.misc_web_contents_backend.oobe_exists
+
+  @property
+  def _username(self):
+    return self.browser_options.username
+
+  @property
+  def _password(self):
+    return self.browser_options.password
+
+  @property
+  def _gaia_id(self):
+    return self.browser_options.gaia_id
+
+  def _IsCryptohomeMounted(self):
+    username = '$guest' if self._is_guest else self._username
+    return self._cri.IsCryptohomeMounted(username, self._is_guest)
+
+  def _GetLoginStatus(self):
+    """Returns login status. If logged in, empty string is returned."""
+    status = ''
+    if not self._IsCryptohomeMounted():
+      status += 'Cryptohome not mounted. '
+    if not self.HasBrowserFinishedLaunching():
+      status += 'Browser didn\'t launch. '
+    if self.oobe_exists:
+      status += 'OOBE not dismissed.'
+    return status
+
+  def _IsLoggedIn(self):
+    """Returns True if cryptohome has mounted, the browser is
+    responsive to devtools requests, and the oobe has been dismissed."""
+    return not self._GetLoginStatus()
+
+  def _WaitForLogin(self):
+    # Wait for cryptohome to mount.
+    py_utils.WaitFor(self._IsLoggedIn, 900)
+
+    # For incognito mode, the session manager actually relaunches chrome with
+    # new arguments, so we have to wait for the browser to come up.
+    self._WaitForBrowserToComeUp()
+
+    # Wait for extensions to load.
+    if self._supports_extensions:
+      self._WaitForExtensionsToLoad()
+
+  def _RaiseOnLoginFailure(self, error):
+    if self._platform_backend.CanTakeScreenshot():
+      self._cri.TakeScreenshotWithPrefix('login-screen')
+    raise exceptions.LoginException(error)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/cros_browser_finder.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/cros_browser_finder.py
new file mode 100644
index 0000000..2c72ca7
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/cros_browser_finder.py
@@ -0,0 +1,122 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Finds CrOS browsers that can be controlled by telemetry."""
+
+import logging
+
+from telemetry.core import cros_interface
+from telemetry.core import platform as platform_module
+from telemetry.internal.backends.chrome import cros_browser_backend
+from telemetry.internal.backends.chrome import cros_browser_with_oobe
+from telemetry.internal.browser import browser
+from telemetry.internal.browser import browser_finder_exceptions
+from telemetry.internal.browser import possible_browser
+from telemetry.internal.platform import cros_device
+
+
+class PossibleCrOSBrowser(possible_browser.PossibleBrowser):
+  """A launchable CrOS browser instance."""
+  def __init__(self, browser_type, finder_options, cros_platform, is_guest):
+    super(PossibleCrOSBrowser, self).__init__(browser_type, 'cros', True)
+    assert browser_type in FindAllBrowserTypes(finder_options), (
+        'Please add %s to cros_browser_finder.FindAllBrowserTypes()' %
+         browser_type)
+    self._platform = cros_platform
+    self._platform_backend = (
+        cros_platform._platform_backend)  # pylint: disable=protected-access
+    self._is_guest = is_guest
+
+  def __repr__(self):
+    return 'PossibleCrOSBrowser(browser_type=%s)' % self.browser_type
+
+  def _InitPlatformIfNeeded(self):
+    pass
+
+  def Create(self, finder_options):
+    if finder_options.browser_options.output_profile_path:
+      raise NotImplementedError(
+          'Profile generation is not yet supported on CrOS.')
+
+    browser_options = finder_options.browser_options
+    browser_backend = cros_browser_backend.CrOSBrowserBackend(
+        self._platform_backend, browser_options, self._platform_backend.cri,
+        self._is_guest)
+    if browser_options.create_browser_with_oobe:
+      return cros_browser_with_oobe.CrOSBrowserWithOOBE(
+          browser_backend,
+          self._platform_backend,
+          self._credentials_path)
+    return browser.Browser(
+        browser_backend, self._platform_backend, self._credentials_path)
+
+  def SupportsOptions(self, browser_options):
+    return (len(browser_options.extensions_to_load) == 0) or not self._is_guest
+
+  def UpdateExecutableIfNeeded(self):
+    pass
+
+def SelectDefaultBrowser(possible_browsers):
+  if cros_device.IsRunningOnCrOS():
+    for b in possible_browsers:
+      if b.browser_type == 'system':
+        return b
+  return None
+
+def CanFindAvailableBrowsers(finder_options):
+  return (cros_device.IsRunningOnCrOS() or
+          finder_options.cros_remote or
+          cros_interface.HasSSH())
+
+def FindAllBrowserTypes(_):
+  return [
+      'cros-chrome',
+      'cros-chrome-guest',
+      'system',
+      'system-guest',
+  ]
+
+def FindAllAvailableBrowsers(finder_options, device):
+  """Finds all available CrOS browsers, locally and remotely."""
+  browsers = []
+  if not isinstance(device, cros_device.CrOSDevice):
+    return browsers
+
+  if cros_device.IsRunningOnCrOS():
+    browsers = [PossibleCrOSBrowser('system', finder_options,
+                                    platform_module.GetHostPlatform(),
+                                    is_guest=False),
+                PossibleCrOSBrowser('system-guest', finder_options,
+                                    platform_module.GetHostPlatform(),
+                                    is_guest=True)]
+
+  # Check ssh
+  try:
+    platform = platform_module.GetPlatformForDevice(device, finder_options)
+  except cros_interface.LoginException, ex:
+    if isinstance(ex, cros_interface.KeylessLoginRequiredException):
+      logging.warn('Could not ssh into %s. Your device must be configured',
+                   finder_options.cros_remote)
+      logging.warn('to allow passwordless login as root.')
+      logging.warn('For a test-build device, pass this to your script:')
+      logging.warn('   --identity $(CHROMITE)/ssh_keys/testing_rsa')
+      logging.warn('')
+      logging.warn('For a developer-mode device, the steps are:')
+      logging.warn(' - Ensure you have an id_rsa.pub (etc) on this computer')
+      logging.warn(' - On the chromebook:')
+      logging.warn('   -  Control-Alt-T; shell; sudo -s')
+      logging.warn('   -  openssh-server start')
+      logging.warn('   -  scp <this machine>:.ssh/id_rsa.pub /tmp/')
+      logging.warn('   -  mkdir /root/.ssh')
+      logging.warn('   -  chown go-rx /root/.ssh')
+      logging.warn('   -  cat /tmp/id_rsa.pub >> /root/.ssh/authorized_keys')
+      logging.warn('   -  chown 0600 /root/.ssh/authorized_keys')
+    raise browser_finder_exceptions.BrowserFinderException(str(ex))
+
+  browsers.extend([PossibleCrOSBrowser('cros-chrome', finder_options,
+                                       platform, is_guest=False),
+                   PossibleCrOSBrowser('cros-chrome-guest',
+                                       finder_options, platform,
+                                       is_guest=True)])
+  return browsers
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/cros_browser_finder_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/cros_browser_finder_unittest.py
new file mode 100644
index 0000000..d467819
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/cros_browser_finder_unittest.py
@@ -0,0 +1,10 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# TODO(nduca): Add basic unit test for cros_browser_finder.
+#
+# Here, we should mock the cros_interface module (assuming its working) and
+# verify that the finder does the right thing. Because the finder delegates most
+# of its work to the CRI, the test code here is going to be comparatively
+# simple.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/cros_browser_with_oobe.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/cros_browser_with_oobe.py
new file mode 100644
index 0000000..127a180
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/cros_browser_with_oobe.py
@@ -0,0 +1,28 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.internal.backends.chrome import cros_browser_backend
+from telemetry.internal.browser import browser
+
+
+class CrOSBrowserWithOOBE(browser.Browser):
+  """Cros-specific browser."""
+  def __init__(self, backend, platform_backend, credentials_path):
+    assert isinstance(backend, cros_browser_backend.CrOSBrowserBackend)
+    super(CrOSBrowserWithOOBE, self).__init__(
+        backend, platform_backend, credentials_path)
+
+  @property
+  def oobe(self):
+    """The login webui (also serves as ui for screenlock and
+    out-of-box-experience).
+    """
+    return self._browser_backend.oobe
+
+  @property
+  def oobe_exists(self):
+    """True if the login/oobe/screenlock webui exists. This is more lightweight
+    than accessing the oobe property.
+    """
+    return self._browser_backend.oobe_exists
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/cros_test_case.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/cros_test_case.py
new file mode 100644
index 0000000..259157b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/cros_test_case.py
@@ -0,0 +1,81 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import unittest
+
+from telemetry.core import cros_interface
+from telemetry.core import util
+from telemetry.internal.browser import browser_finder
+from telemetry.internal.browser import extension_to_load
+from telemetry.testing import options_for_unittests
+
+
+class CrOSTestCase(unittest.TestCase):
+  def setUp(self):
+    options = options_for_unittests.GetCopy()
+    self._cri = cros_interface.CrOSInterface(options.cros_remote,
+                                             options.cros_remote_ssh_port,
+                                             options.cros_ssh_identity)
+    self._is_guest = options.browser_type == 'cros-chrome-guest'
+    self._username = options.browser_options.username
+    self._password = options.browser_options.password
+    self._gaia_id = options.browser_options.gaia_id
+    self._load_extension = None
+
+  def _CreateBrowser(self, autotest_ext=False, auto_login=True,
+                     gaia_login=False, username=None, password=None,
+                     gaia_id=None, dont_override_profile=False):
+    """Finds and creates a browser for tests. if autotest_ext is True,
+    also loads the autotest extension"""
+    options = options_for_unittests.GetCopy()
+
+    if autotest_ext:
+      extension_path = os.path.join(util.GetUnittestDataDir(), 'autotest_ext')
+      assert os.path.isdir(extension_path)
+      self._load_extension = extension_to_load.ExtensionToLoad(
+          path=extension_path,
+          browser_type=options.browser_type)
+      options.browser_options.extensions_to_load = [self._load_extension]
+
+    browser_to_create = browser_finder.FindBrowser(options)
+    self.assertTrue(browser_to_create)
+    browser_options = options.browser_options
+    browser_options.create_browser_with_oobe = True
+    browser_options.auto_login = auto_login
+    browser_options.gaia_login = gaia_login
+    browser_options.dont_override_profile = dont_override_profile
+    if username is not None:
+      browser_options.username = username
+    if password is not None:
+      browser_options.password = password
+    if gaia_id is not None:
+      browser_options.gaia_id = gaia_id
+
+    return browser_to_create.Create(options)
+
+  def _GetAutotestExtension(self, browser):
+    """Returns the autotest extension instance"""
+    extension = browser.extensions[self._load_extension]
+    self.assertTrue(extension)
+    return extension
+
+  def _IsCryptohomeMounted(self):
+    """Returns True if cryptohome is mounted. as determined by the cmd
+    cryptohome --action=is_mounted"""
+    return self._cri.RunCmdOnDevice(
+        ['/usr/sbin/cryptohome', '--action=is_mounted'])[0].strip() == 'true'
+
+  def _GetLoginStatus(self, browser):
+    extension = self._GetAutotestExtension(browser)
+    self.assertTrue(extension.EvaluateJavaScript(
+        "typeof('chrome.autotestPrivate') != 'undefined'"))
+    extension.ExecuteJavaScript('''
+        window.__login_status = null;
+        chrome.autotestPrivate.loginStatus(function(s) {
+          window.__login_status = s;
+        });
+    ''')
+    return extension.WaitForJavaScriptCondition(
+        'window.__login_status', timeout=10)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/cros_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/cros_unittest.py
new file mode 100644
index 0000000..6581046
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/cros_unittest.py
@@ -0,0 +1,199 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+import urllib2
+import os
+
+from telemetry.core import exceptions
+from telemetry import decorators
+from telemetry.internal.backends.chrome import cros_test_case
+
+import py_utils
+
+
+class CrOSCryptohomeTest(cros_test_case.CrOSTestCase):
+  @decorators.Enabled('chromeos')
+  def testCryptohome(self):
+    """Verifies cryptohome mount status for regular and guest user and when
+    logged out"""
+    with self._CreateBrowser() as b:
+      self.assertEquals(1, len(b.tabs))
+      self.assertTrue(b.tabs[0].url)
+      self.assertTrue(self._IsCryptohomeMounted())
+      self.assertTrue(
+          self._cri.IsCryptohomeMounted(self._username, self._is_guest))
+
+      # TODO(achuith): Remove dependency on /home/chronos/user.
+      chronos_fs = self._cri.FilesystemMountedAt('/home/chronos/user')
+      self.assertTrue(chronos_fs)
+      if self._is_guest:
+        self.assertEquals(chronos_fs, 'guestfs')
+      else:
+        crypto_fs = self._cri.FilesystemMountedAt(
+            self._cri.CryptohomePath(self._username))
+        self.assertEquals(crypto_fs, chronos_fs)
+
+    self.assertFalse(self._IsCryptohomeMounted())
+    self.assertFalse(
+        self._cri.IsCryptohomeMounted(self._username, self._is_guest))
+    self.assertEquals(self._cri.FilesystemMountedAt('/home/chronos/user'),
+                      '/dev/mapper/encstateful')
+
+
+class CrOSLoginTest(cros_test_case.CrOSTestCase):
+  def _GetCredentials(self, credentials=None):
+    """Read username and password from credentials.txt. The file is a single
+    line of the format username:password. Alternatively, |credentials| is used,
+    also of the same format."""
+    credentials_file = os.path.join(os.path.dirname(__file__),
+                                    'credentials.txt')
+    if not credentials and os.path.exists(credentials_file):
+      with open(credentials_file) as f:
+        credentials = f.read().strip()
+
+    if not credentials:
+      return (None, None)
+
+    user, password = credentials.split(':')
+    # Canonicalize.
+    if user.find('@') == -1:
+      username = user
+      domain = 'gmail.com'
+    else:
+      username, domain = user.split('@')
+
+    # Remove dots.
+    if domain == 'gmail.com':
+      username = username.replace('.', '')
+    return ('%s@%s' % (username, domain), password)
+
+  @decorators.Enabled('chromeos')
+  def testGetCredentials(self):
+    (username, password) = self._GetCredentials('user.1:foo.1')
+    self.assertEquals(username, 'user1@gmail.com')
+    self.assertEquals(password, 'foo.1')
+
+    (username, password) = self._GetCredentials('user.1@chromium.org:bar.1')
+    self.assertEquals(username, 'user.1@chromium.org')
+    self.assertEquals(password, 'bar.1')
+
+  @decorators.Enabled('chromeos')
+  def testLoginStatus(self):
+    """Tests autotestPrivate.loginStatus"""
+    if self._is_guest:
+      return
+    with self._CreateBrowser(autotest_ext=True) as b:
+      login_status = self._GetLoginStatus(b)
+      self.assertEquals(type(login_status), dict)
+
+      self.assertEquals(not self._is_guest, login_status['isRegularUser'])
+      self.assertEquals(self._is_guest, login_status['isGuest'])
+      self.assertEquals(login_status['email'], self._username)
+      self.assertFalse(login_status['isScreenLocked'])
+
+  @decorators.Enabled('chromeos')
+  def testLogout(self):
+    """Tests autotestPrivate.logout"""
+    if self._is_guest:
+      return
+    with self._CreateBrowser(autotest_ext=True) as b:
+      extension = self._GetAutotestExtension(b)
+      try:
+        extension.ExecuteJavaScript('chrome.autotestPrivate.logout();')
+      except exceptions.Error:
+        pass
+      py_utils.WaitFor(lambda: not self._IsCryptohomeMounted(), 20)
+
+  @decorators.Disabled('all')
+  def testGaiaLogin(self):
+    """Tests gaia login. Use credentials in credentials.txt if it exists,
+    otherwise use powerloadtest."""
+    if self._is_guest:
+      return
+    username, password = self._GetCredentials()
+    if not username or not password:
+      username = 'powerloadtest@gmail.com'
+      password = urllib2.urlopen(
+          'https://sites.google.com/a/chromium.org/dev/chromium-os/testing/'
+          'power-testing/pltp/pltp').read().rstrip()
+    with self._CreateBrowser(gaia_login=True,
+                             username=username,
+                             password=password):
+      self.assertTrue(py_utils.WaitFor(self._IsCryptohomeMounted, 10))
+
+  @decorators.Enabled('chromeos')
+  def testEnterpriseEnroll(self):
+    """Tests enterprise enrollment. Credentials are expected to be found in a
+    credentials.txt file. The account must be from an enterprise domain and
+    have device enrollment permission. The device must be unowned."""
+    if self._is_guest:
+      return
+
+    username, password = self._GetCredentials()
+    if not username or not password:
+      return
+    # Enroll the device.
+    with self._CreateBrowser(auto_login=False) as browser:
+      browser.oobe.NavigateGaiaLogin(username, password,
+                                     enterprise_enroll=True,
+                                     for_user_triggered_enrollment=True)
+
+    # Check for the existence of the device policy file.
+    self.assertTrue(py_utils.WaitFor(lambda: self._cri.FileExistsOnDevice(
+        '/home/.shadow/install_attributes.pb'), 15))
+
+
+class CrOSScreenLockerTest(cros_test_case.CrOSTestCase):
+  def _IsScreenLocked(self, browser):
+    return self._GetLoginStatus(browser)['isScreenLocked']
+
+  def _LockScreen(self, browser):
+    self.assertFalse(self._IsScreenLocked(browser))
+
+    extension = self._GetAutotestExtension(browser)
+    self.assertTrue(extension.EvaluateJavaScript(
+        "typeof chrome.autotestPrivate.lockScreen == 'function'"))
+    logging.info('Locking screen')
+    extension.ExecuteJavaScript('chrome.autotestPrivate.lockScreen();')
+
+    logging.info('Waiting for the lock screen')
+    def ScreenLocked():
+      return (browser.oobe_exists and
+          browser.oobe.EvaluateJavaScript("typeof Oobe == 'function'") and
+          browser.oobe.EvaluateJavaScript(
+              "typeof Oobe.authenticateForTesting == 'function'"))
+    py_utils.WaitFor(ScreenLocked, 10)
+    self.assertTrue(self._IsScreenLocked(browser))
+
+  def _AttemptUnlockBadPassword(self, browser):
+    logging.info('Trying a bad password')
+    def ErrorBubbleVisible():
+      return not browser.oobe.EvaluateJavaScript(
+          "document.getElementById('bubble').hidden")
+
+    self.assertFalse(ErrorBubbleVisible())
+    browser.oobe.ExecuteJavaScript(
+        "Oobe.authenticateForTesting({{ username }}, 'bad');",
+        username=self._username)
+    py_utils.WaitFor(ErrorBubbleVisible, 10)
+    self.assertTrue(self._IsScreenLocked(browser))
+
+  def _UnlockScreen(self, browser):
+    logging.info('Unlocking')
+    browser.oobe.ExecuteJavaScript(
+        'Oobe.authenticateForTesting({{ username }}, {{ password }});',
+        username=self._username, password=self._password)
+    py_utils.WaitFor(lambda: not browser.oobe_exists, 10)
+    self.assertFalse(self._IsScreenLocked(browser))
+
+  @decorators.Disabled('all')
+  def testScreenLock(self):
+    """Tests autotestPrivate.screenLock"""
+    if self._is_guest:
+      return
+    with self._CreateBrowser(autotest_ext=True) as browser:
+      self._LockScreen(browser)
+      self._AttemptUnlockBadPassword(browser)
+      self._UnlockScreen(browser)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/crx_id.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/crx_id.py
new file mode 100644
index 0000000..21d17a2
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/crx_id.py
@@ -0,0 +1,130 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+""" Read a CRX file and write out the App ID and the Full Hash of the ID.
+See: http://code.google.com/chrome/extensions/crx.html
+and 'http://stackoverflow.com/questions/'
+  + '1882981/google-chrome-alphanumeric-hashes-to-identify-extensions'
+for docs on the format.
+"""
+
+import base64
+import os
+import hashlib
+import json
+
+EXPECTED_CRX_MAGIC_NUM = 'Cr24'
+EXPECTED_CRX_VERSION = 2
+
+def HexToInt(hex_chars):
+  """ Convert bytes like \xab -> 171 """
+  val = 0
+  for i in xrange(len(hex_chars)):
+    val += pow(256, i) * ord(hex_chars[i])
+  return val
+
+def HexToMPDecimal(hex_chars):
+  """ Convert bytes to an MPDecimal string. Example \x00 -> "aa"
+      This gives us the AppID for a chrome extension.
+  """
+  result = ''
+  base = ord('a')
+  for i in xrange(len(hex_chars)):
+    value = ord(hex_chars[i])
+    dig1 = value / 16
+    dig2 = value % 16
+    result += chr(dig1 + base)
+    result += chr(dig2 + base)
+  return result
+
+def HexTo256(hex_chars):
+  """ Convert bytes to pairs of hex digits. E.g., \x00\x11 -> "{0x00, 0x11}"
+      The format is taylored for copy and paste into C code:
+      const uint8 sha256_hash[] = { ... }; """
+  result = []
+  for i in xrange(len(hex_chars)):
+    value = ord(hex_chars[i])
+    dig1 = value / 16
+    dig2 = value % 16
+    result.append('0x' + hex(dig1)[2:] + hex(dig2)[2:])
+  return '{%s}' % ', '.join(result)
+
+def GetPublicKeyPacked(f):
+  magic_num = f.read(4)
+  if magic_num != EXPECTED_CRX_MAGIC_NUM:
+    raise Exception('Invalid magic number: %s (expecting %s)' %
+                    (magic_num,
+                     EXPECTED_CRX_MAGIC_NUM))
+  version = f.read(4)
+  if not version[0] != EXPECTED_CRX_VERSION:
+    raise Exception('Invalid version number: %s (expecting %s)' %
+                    (version,
+                     EXPECTED_CRX_VERSION))
+  pub_key_len_bytes = HexToInt(f.read(4))
+  f.read(4)
+  return f.read(pub_key_len_bytes)
+
+def GetPublicKeyFromPath(filepath, is_win_path=False):
+  # Normalize the path for windows to have capital drive letters.
+  # We intentionally don't check if sys.platform == 'win32' and just
+  # check if this looks like drive letter so that we can test this
+  # even on posix systems.
+  if (len(filepath) >= 2 and
+      filepath[0].islower() and
+      filepath[1] == ':'):
+    filepath = filepath[0].upper() + filepath[1:]
+
+  # On Windows, filepaths are encoded using UTF-16, little endian byte order,
+  # using "wide characters" that are 16 bits in size. On POSIX systems, the
+  # encoding is generally UTF-8, which has the property of being equivalent to
+  # ASCII when only ASCII characters are in the path.
+  if is_win_path:
+    filepath = filepath.encode('utf-16le')
+
+  return filepath
+
+def GetPublicKeyUnpacked(f, filepath):
+  manifest = json.load(f)
+  if 'key' not in manifest:
+    # Use the path as the public key.
+    # See Extension::GenerateIdForPath in extension.cc
+    return GetPublicKeyFromPath(filepath)
+  else:
+    return base64.standard_b64decode(manifest['key'])
+
+def HasPublicKey(filename):
+  if os.path.isdir(filename):
+    with open(os.path.join(filename, 'manifest.json'), 'rb') as f:
+      manifest = json.load(f)
+      return 'key' in manifest
+  return False
+
+def GetPublicKey(filename, from_file_path, is_win_path=False):
+  if from_file_path:
+    return GetPublicKeyFromPath(
+        filename, is_win_path=is_win_path)
+
+  pub_key = ''
+  if os.path.isdir(filename):
+    # Assume it's an unpacked extension
+    f = open(os.path.join(filename, 'manifest.json'), 'rb')
+    pub_key = GetPublicKeyUnpacked(f, filename)
+    f.close()
+  else:
+    # Assume it's a packed extension.
+    f = open(filename, 'rb')
+    pub_key = GetPublicKeyPacked(f)
+    f.close()
+  return pub_key
+
+def GetCRXHash(filename, from_file_path=False, is_win_path=False):
+  pub_key = GetPublicKey(filename, from_file_path, is_win_path=is_win_path)
+  pub_key_hash = hashlib.sha256(pub_key).digest()
+  return HexTo256(pub_key_hash)
+
+def GetCRXAppID(filename, from_file_path=False, is_win_path=False):
+  pub_key = GetPublicKey(filename, from_file_path, is_win_path=is_win_path)
+  pub_key_hash = hashlib.sha256(pub_key).digest()
+  # AppID is the MPDecimal of only the first 128 bits of the hash.
+  return HexToMPDecimal(pub_key_hash[:128/8])
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/crx_id_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/crx_id_unittest.py
new file mode 100644
index 0000000..9cea905
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/crx_id_unittest.py
@@ -0,0 +1,78 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import os
+import shutil
+import unittest
+import tempfile
+
+from telemetry.core import util
+from telemetry.internal.backends.chrome import crx_id
+
+
+class CrxIdUnittest(unittest.TestCase):
+  CRX_ID_DIR = util.GetUnittestDataDir()
+  PACKED_CRX = os.path.join(CRX_ID_DIR,
+                            'jebgalgnebhfojomionfpkfelancnnkf.crx')
+
+  PACKED_APP_ID = 'jebgalgnebhfojomionfpkfelancnnkf'
+  PACKED_HASH_BYTES = \
+      '{0x94, 0x16, 0x0b, 0x6d, 0x41, 0x75, 0xe9, 0xec,' \
+      ' 0x8e, 0xd5, 0xfa, 0x54, 0xb0, 0xd2, 0xdd, 0xa5,' \
+      ' 0x6e, 0x05, 0x6b, 0xe8, 0x73, 0x47, 0xf6, 0xc4,' \
+      ' 0x11, 0x9f, 0xbc, 0xb3, 0x09, 0xb3, 0x5b, 0x40}'
+
+  UNPACKED_APP_ID = 'cbcdidchbppangcjoddlpdjlenngjldk'
+  UNPACKED_HASH_BYTES = \
+      '{0x21, 0x23, 0x83, 0x27, 0x1f, 0xf0, 0xd6, 0x29,' \
+      ' 0xe3, 0x3b, 0xf3, 0x9b, 0x4d, 0xd6, 0x9b, 0x3a,' \
+      ' 0xff, 0x7d, 0x6b, 0xc4, 0x78, 0x30, 0x47, 0xa6,' \
+      ' 0x23, 0x12, 0x72, 0x84, 0x9b, 0x9a, 0xf6, 0x3c}'
+
+
+  def testPackedHashAppId(self):
+    """ Test the output generated for a canned, packed CRX. """
+    self.assertEqual(crx_id.GetCRXAppID(self.PACKED_CRX),
+                     self.PACKED_APP_ID)
+    self.assertEqual(crx_id.GetCRXHash(self.PACKED_CRX),
+                     self.PACKED_HASH_BYTES)
+
+
+  def testUnpackedHashAppId(self):
+    """ Test the output generated for a canned, unpacked extension. """
+    unpacked_test_manifest_path = os.path.join(
+        self.CRX_ID_DIR, 'manifest_with_key.json')
+    temp_unpacked_crx = tempfile.mkdtemp()
+    shutil.copy2(unpacked_test_manifest_path,
+                 os.path.join(temp_unpacked_crx, 'manifest.json'))
+    self.assertEqual(crx_id.GetCRXAppID(temp_unpacked_crx),
+                     self.UNPACKED_APP_ID)
+    self.assertEqual(crx_id.GetCRXHash(temp_unpacked_crx),
+                     self.UNPACKED_HASH_BYTES)
+    self.assertTrue(crx_id.HasPublicKey(temp_unpacked_crx))
+    shutil.rmtree(temp_unpacked_crx)
+
+
+  def testFromFilePath(self):
+    """ Test calculation of extension id from file paths. """
+    self.assertEqual(crx_id.GetCRXAppID('/tmp/temp_extension',
+                                        from_file_path=True),
+                     'ajbbicncdkdlchpjplgjaglppbcbmaji')
+
+
+  def testFromWindowsPath(self):
+    self.assertEqual(crx_id.GetCRXAppID(r'D:\Documents\chrome\test_extension',
+                                        from_file_path=True,
+                                        is_win_path=True),
+                     'fegemedmbnhglnecjgbdhekaghkccplm')
+
+    # Test drive letter normalization.
+    kWinPathId = 'aiinlcdagjihibappcdnnhcccdokjlaf'
+    self.assertEqual(crx_id.GetCRXAppID(r'c:\temp_extension',
+                                        from_file_path=True,
+                                        is_win_path=True),
+                     kWinPathId)
+    self.assertEqual(crx_id.GetCRXAppID(r'C:\temp_extension',
+                                        from_file_path=True,
+                                        is_win_path=True),
+                     kWinPathId)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/desktop_browser_backend.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/desktop_browser_backend.py
new file mode 100644
index 0000000..034b483
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/desktop_browser_backend.py
@@ -0,0 +1,648 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import datetime
+import glob
+import heapq
+import logging
+import os
+import os.path
+import random
+import re
+import shutil
+import subprocess as subprocess
+import sys
+import tempfile
+import time
+
+import py_utils
+from py_utils import cloud_storage  # pylint: disable=import-error
+import dependency_manager  # pylint: disable=import-error
+
+from telemetry.internal.util import binary_manager
+from telemetry.core import exceptions
+from telemetry.internal.backends import browser_backend
+from telemetry.internal.backends.chrome import chrome_browser_backend
+from telemetry.internal.util import path
+
+
+def ParseCrashpadDateTime(date_time_str):
+  # Python strptime does not support time zone parsing, strip it.
+  date_time_parts = date_time_str.split()
+  if len(date_time_parts) >= 3:
+    date_time_str = ' '.join(date_time_parts[:2])
+  return datetime.datetime.strptime(date_time_str, '%Y-%m-%d %H:%M:%S')
+
+
+def GetSymbolBinaries(minidump, arch_name, os_name):
+  # Returns binary file where symbols are located.
+  minidump_dump = binary_manager.FetchPath('minidump_dump', arch_name, os_name)
+  assert minidump_dump
+
+  symbol_binaries = []
+
+  minidump_cmd = [minidump_dump, minidump]
+  try:
+    with open(os.devnull, 'wb') as DEVNULL:
+      minidump_output = subprocess.check_output(minidump_cmd, stderr=DEVNULL)
+  except subprocess.CalledProcessError as e:
+    # For some reason minidump_dump always fails despite successful dumping.
+    minidump_output = e.output
+
+  minidump_binary_re = re.compile(r'\W+\(code_file\)\W+=\W\"(.*)\"')
+  for minidump_line in minidump_output.splitlines():
+    line_match = minidump_binary_re.match(minidump_line)
+    if line_match:
+      binary_path = line_match.group(1)
+      if not os.path.isfile(binary_path):
+        continue
+
+      # Filter out system binaries.
+      if (binary_path.startswith('/usr/lib/') or
+          binary_path.startswith('/System/Library/') or
+          binary_path.startswith('/lib/')):
+        continue
+
+      # Filter out other binary file types which have no symbols.
+      if (binary_path.endswith('.pak') or
+          binary_path.endswith('.bin') or
+          binary_path.endswith('.dat') or
+          binary_path.endswith('.ttf')):
+        continue
+
+      symbol_binaries.append(binary_path)
+  return symbol_binaries
+
+
+def GenerateBreakpadSymbols(minidump, arch, os_name, symbols_dir, browser_dir):
+  logging.info('Dumping breakpad symbols.')
+  generate_breakpad_symbols_command = binary_manager.FetchPath(
+      'generate_breakpad_symbols', arch, os_name)
+  if not generate_breakpad_symbols_command:
+    return
+
+  for binary_path in GetSymbolBinaries(minidump, arch, os_name):
+    cmd = [
+        sys.executable,
+        generate_breakpad_symbols_command,
+        '--binary=%s' % binary_path,
+        '--symbols-dir=%s' % symbols_dir,
+        '--build-dir=%s' % browser_dir,
+        ]
+
+    try:
+      subprocess.check_call(cmd, stderr=open(os.devnull, 'w'))
+    except subprocess.CalledProcessError:
+      logging.warning('Failed to execute "%s"' % ' '.join(cmd))
+      return
+
+
+class DesktopBrowserBackend(chrome_browser_backend.ChromeBrowserBackend):
+  """The backend for controlling a locally-executed browser instance, on Linux,
+  Mac or Windows.
+  """
+  def __init__(self, desktop_platform_backend, browser_options, executable,
+               flash_path, is_content_shell, browser_directory):
+    super(DesktopBrowserBackend, self).__init__(
+        desktop_platform_backend,
+        supports_tab_control=not is_content_shell,
+        supports_extensions=not is_content_shell,
+        browser_options=browser_options)
+
+    # Initialize fields so that an explosion during init doesn't break in Close.
+    self._proc = None
+    self._tmp_profile_dir = None
+    self._tmp_output_file = None
+    self._most_recent_symbolized_minidump_paths = set([])
+    self._minidump_path_crashpad_retrieval = {}
+
+    self._executable = executable
+    if not self._executable:
+      raise Exception('Cannot create browser, no executable found!')
+
+    assert not flash_path or os.path.exists(flash_path)
+    self._flash_path = flash_path
+
+    self._is_content_shell = is_content_shell
+
+    extensions_to_load = browser_options.extensions_to_load
+
+    if len(extensions_to_load) > 0 and is_content_shell:
+      raise browser_backend.ExtensionsNotSupportedException(
+          'Content shell does not support extensions.')
+
+    self._browser_directory = browser_directory
+    self._port = None
+    self._tmp_minidump_dir = tempfile.mkdtemp()
+    if self.is_logging_enabled:
+      self._log_file_path = os.path.join(tempfile.mkdtemp(), 'chrome.log')
+    else:
+      self._log_file_path = None
+
+    self._SetupProfile()
+
+  @property
+  def is_logging_enabled(self):
+    return self.browser_options.logging_verbosity in [
+        self.browser_options.NON_VERBOSE_LOGGING,
+        self.browser_options.VERBOSE_LOGGING]
+
+  @property
+  def log_file_path(self):
+    return self._log_file_path
+
+  @property
+  def supports_uploading_logs(self):
+    return (self.browser_options.logs_cloud_bucket and self.log_file_path and
+            os.path.isfile(self.log_file_path))
+
+  def _SetupProfile(self):
+    if not self.browser_options.dont_override_profile:
+      if self._output_profile_path:
+        self._tmp_profile_dir = self._output_profile_path
+      else:
+        self._tmp_profile_dir = tempfile.mkdtemp()
+
+      profile_dir = self.browser_options.profile_dir
+      if profile_dir:
+        assert self._tmp_profile_dir != profile_dir
+        if self._is_content_shell:
+          logging.critical('Profiles cannot be used with content shell')
+          sys.exit(1)
+        logging.info("Using profile directory:'%s'." % profile_dir)
+        shutil.rmtree(self._tmp_profile_dir)
+        shutil.copytree(profile_dir, self._tmp_profile_dir)
+    # No matter whether we're using an existing profile directory or
+    # creating a new one, always delete the well-known file containing
+    # the active DevTools port number.
+    port_file = self._GetDevToolsActivePortPath()
+    if os.path.isfile(port_file):
+      try:
+        os.remove(port_file)
+      except Exception as e:
+        logging.critical('Unable to remove DevToolsActivePort file: %s' % e)
+        sys.exit(1)
+
+  def _GetDevToolsActivePortPath(self):
+    return os.path.join(self.profile_directory, 'DevToolsActivePort')
+
+  def _GetCdbPath(self):
+    # cdb.exe might have been co-located with the browser's executable
+    # during the build, but that's not a certainty. (This is only done
+    # in Chromium builds on the bots, which is why it's not a hard
+    # requirement.) See if it's available.
+    colocated_cdb = os.path.join(self._browser_directory, 'cdb', 'cdb.exe')
+    if path.IsExecutable(colocated_cdb):
+      return colocated_cdb
+    possible_paths = (
+        # Installed copies of the Windows SDK.
+        os.path.join('Windows Kits', '*', 'Debuggers', 'x86'),
+        os.path.join('Windows Kits', '*', 'Debuggers', 'x64'),
+        # Old copies of the Debugging Tools for Windows.
+        'Debugging Tools For Windows',
+        'Debugging Tools For Windows (x86)',
+        'Debugging Tools For Windows (x64)',
+        # The hermetic copy of the Windows toolchain in depot_tools.
+        os.path.join('win_toolchain', 'vs_files', '*', 'win_sdk',
+                     'Debuggers', 'x86'),
+        os.path.join('win_toolchain', 'vs_files', '*', 'win_sdk',
+                     'Debuggers', 'x64'),
+    )
+    for possible_path in possible_paths:
+      app_path = os.path.join(possible_path, 'cdb.exe')
+      app_path = path.FindInstalledWindowsApplication(app_path)
+      if app_path:
+        return app_path
+    return None
+
+  def HasBrowserFinishedLaunching(self):
+    # In addition to the functional check performed by the base class, quickly
+    # check if the browser process is still alive.
+    if not self.IsBrowserRunning():
+      raise exceptions.ProcessGoneException(
+          "Return code: %d" % self._proc.returncode)
+    # Start DevTools on an ephemeral port and wait for the well-known file
+    # containing the port number to exist.
+    port_file = self._GetDevToolsActivePortPath()
+    if not os.path.isfile(port_file):
+      # File isn't ready yet. Return false. Will retry.
+      return False
+    # Attempt to avoid reading the file until it's populated.
+    got_port = False
+    try:
+      if os.stat(port_file).st_size > 0:
+        with open(port_file) as f:
+          port_string = f.read()
+          self._port = int(port_string)
+          logging.info('Discovered ephemeral port %s' % self._port)
+          got_port = True
+    except Exception:
+      # Both stat and open can throw exceptions.
+      pass
+    if not got_port:
+      # File isn't ready yet. Return false. Will retry.
+      return False
+    return super(DesktopBrowserBackend, self).HasBrowserFinishedLaunching()
+
+  def GetBrowserStartupArgs(self):
+    args = super(DesktopBrowserBackend, self).GetBrowserStartupArgs()
+    self._port = 0
+    logging.info('Requested remote debugging port: %d' % self._port)
+    args.append('--remote-debugging-port=%i' % self._port)
+    args.append('--enable-crash-reporter-for-testing')
+    args.append('--disable-component-update')
+    if not self._is_content_shell:
+      args.append('--window-size=1280,1024')
+      if self._flash_path:
+        args.append('--ppapi-flash-path=%s' % self._flash_path)
+        # Also specify the version of Flash as a large version, so that it is
+        # not overridden by the bundled or component-updated version of Flash.
+        args.append('--ppapi-flash-version=99.9.999.999')
+      if not self.browser_options.dont_override_profile:
+        args.append('--user-data-dir=%s' % self._tmp_profile_dir)
+    else:
+      args.append('--data-path=%s' % self._tmp_profile_dir)
+
+    trace_config_file = (self.platform_backend.tracing_controller_backend
+                         .GetChromeTraceConfigFile())
+    if trace_config_file:
+      args.append('--trace-config-file=%s' % trace_config_file)
+    return args
+
+  def Start(self):
+    assert not self._proc, 'Must call Close() before Start()'
+
+    # macOS displays a blocking crash resume dialog that we need to suppress.
+    if self.browser.platform.GetOSName() == 'mac':
+      subprocess.call(['defaults', 'write', '-app', self._executable,
+                       'NSQuitAlwaysKeepsWindows', '-bool', 'false'])
+
+
+    args = [self._executable]
+    args.extend(self.GetBrowserStartupArgs())
+    if self.browser_options.startup_url:
+      args.append(self.browser_options.startup_url)
+    env = os.environ.copy()
+    env['CHROME_HEADLESS'] = '1'  # Don't upload minidumps.
+    env['BREAKPAD_DUMP_LOCATION'] = self._tmp_minidump_dir
+    if self.is_logging_enabled:
+      sys.stderr.write(
+        'Chrome log file will be saved in %s\n' % self.log_file_path)
+      env['CHROME_LOG_FILE'] = self.log_file_path
+    logging.info('Starting Chrome %s', args)
+    if not self.browser_options.show_stdout:
+      self._tmp_output_file = tempfile.NamedTemporaryFile('w', 0)
+      self._proc = subprocess.Popen(
+          args, stdout=self._tmp_output_file, stderr=subprocess.STDOUT, env=env)
+    else:
+      self._proc = subprocess.Popen(args, env=env)
+
+    try:
+      self._WaitForBrowserToComeUp()
+      # browser is foregrounded by default on Windows and Linux, but not Mac.
+      if self.browser.platform.GetOSName() == 'mac':
+        subprocess.Popen([
+          'osascript', '-e', ('tell application "%s" to activate' %
+                              self._executable)])
+      self._InitDevtoolsClientBackend()
+      if self._supports_extensions:
+        self._WaitForExtensionsToLoad()
+    except:
+      self.Close()
+      raise
+
+  @property
+  def pid(self):
+    if self._proc:
+      return self._proc.pid
+    return None
+
+  @property
+  def browser_directory(self):
+    return self._browser_directory
+
+  @property
+  def profile_directory(self):
+    return self._tmp_profile_dir
+
+  def IsBrowserRunning(self):
+    return self._proc and self._proc.poll() == None
+
+  def GetStandardOutput(self):
+    if not self._tmp_output_file:
+      if self.browser_options.show_stdout:
+        # This can happen in the case that loading the Chrome binary fails.
+        # We print rather than using logging here, because that makes a
+        # recursive call to this function.
+        print >> sys.stderr, "Can't get standard output with --show-stdout"
+      return ''
+    self._tmp_output_file.flush()
+    try:
+      with open(self._tmp_output_file.name) as f:
+        return f.read()
+    except IOError:
+      return ''
+
+  def _MinidumpObtainedFromCrashpad(self, minidump):
+    if minidump in self._minidump_path_crashpad_retrieval:
+      return self._minidump_path_crashpad_retrieval[minidump]
+    # Default to crashpad where we hope to be eventually
+    return True
+
+  def _GetAllCrashpadMinidumps(self):
+    if not self._tmp_minidump_dir:
+      logging.warning('No _tmp_minidump_dir; browser already closed?')
+      return None
+    os_name = self.browser.platform.GetOSName()
+    arch_name = self.browser.platform.GetArchName()
+    try:
+      crashpad_database_util = binary_manager.FetchPath(
+          'crashpad_database_util', arch_name, os_name)
+      if not crashpad_database_util:
+        logging.warning('No crashpad_database_util found')
+        return None
+    except dependency_manager.NoPathFoundError:
+      logging.warning('No path to crashpad_database_util found')
+      return None
+
+    logging.info('Found crashpad_database_util')
+
+    report_output = subprocess.check_output([
+        crashpad_database_util, '--database=' + self._tmp_minidump_dir,
+        '--show-pending-reports', '--show-completed-reports',
+        '--show-all-report-info'])
+
+    last_indentation = -1
+    reports_list = []
+    report_dict = {}
+    for report_line in report_output.splitlines():
+      # Report values are grouped together by the same indentation level.
+      current_indentation = 0
+      for report_char in report_line:
+        if not report_char.isspace():
+          break
+        current_indentation += 1
+
+      # Decrease in indentation level indicates a new report is being printed.
+      if current_indentation >= last_indentation:
+        report_key, report_value = report_line.split(':', 1)
+        if report_value:
+          report_dict[report_key.strip()] = report_value.strip()
+      elif report_dict:
+        try:
+          report_time = ParseCrashpadDateTime(report_dict['Creation time'])
+          report_path = report_dict['Path'].strip()
+          reports_list.append((report_time, report_path))
+        except (ValueError, KeyError) as e:
+          logging.warning('Crashpad report expected valid keys'
+                          ' "Path" and "Creation time": %s', e)
+        finally:
+          report_dict = {}
+
+      last_indentation = current_indentation
+
+    # Include the last report.
+    if report_dict:
+      try:
+        report_time = ParseCrashpadDateTime(report_dict['Creation time'])
+        report_path = report_dict['Path'].strip()
+        reports_list.append((report_time, report_path))
+      except (ValueError, KeyError) as e:
+        logging.warning('Crashpad report expected valid keys'
+                          ' "Path" and "Creation time": %s', e)
+
+    return reports_list
+
+  def _GetMostRecentCrashpadMinidump(self):
+    reports_list = self._GetAllCrashpadMinidumps()
+    if reports_list:
+      _, most_recent_report_path = max(reports_list)
+      return most_recent_report_path
+
+    return None
+
+  def _GetBreakPadMinidumpPaths(self):
+    if not self._tmp_minidump_dir:
+      logging.warning('No _tmp_minidump_dir; browser already closed?')
+      return None
+    return glob.glob(os.path.join(self._tmp_minidump_dir, '*.dmp'))
+
+  def _GetMostRecentMinidump(self):
+    # Crashpad dump layout will be the standard eventually, check it first.
+    crashpad_dump = True
+    most_recent_dump = self._GetMostRecentCrashpadMinidump()
+
+    # Typical breakpad format is simply dump files in a folder.
+    if not most_recent_dump:
+      crashpad_dump = False
+      logging.info('No minidump found via crashpad_database_util')
+      dumps = self._GetBreakPadMinidumpPaths()
+      if dumps:
+        most_recent_dump = heapq.nlargest(1, dumps, os.path.getmtime)[0]
+        if most_recent_dump:
+          logging.info('Found minidump via globbing in minidump dir')
+
+    # As a sanity check, make sure the crash dump is recent.
+    if (most_recent_dump and
+        os.path.getmtime(most_recent_dump) < (time.time() - (5 * 60))):
+      logging.warning('Crash dump is older than 5 minutes. May not be correct.')
+
+    self._minidump_path_crashpad_retrieval[most_recent_dump] = crashpad_dump
+    return most_recent_dump
+
+  def _IsExecutableStripped(self):
+    if self.browser.platform.GetOSName() == 'mac':
+      try:
+        symbols = subprocess.check_output(['/usr/bin/nm', self._executable])
+      except subprocess.CalledProcessError as err:
+        logging.warning('Error when checking whether executable is stripped: %s'
+                        % err.output)
+        # Just assume that binary is stripped to skip breakpad symbol generation
+        # if this check failed.
+        return True
+      num_symbols = len(symbols.splitlines())
+      # We assume that if there are more than 10 symbols the executable is not
+      # stripped.
+      return num_symbols < 10
+    else:
+      return False
+
+  def _GetStackFromMinidump(self, minidump):
+    os_name = self.browser.platform.GetOSName()
+    if os_name == 'win':
+      cdb = self._GetCdbPath()
+      if not cdb:
+        logging.warning('cdb.exe not found.')
+        return None
+      # Move to the thread which triggered the exception (".ecxr"). Then include
+      # a description of the exception (".lastevent"). Also include all the
+      # threads' stacks ("~*kb30") as well as the ostensibly crashed stack
+      # associated with the exception context record ("kb30"). Note that stack
+      # dumps, including that for the crashed thread, may not be as precise as
+      # the one starting from the exception context record.
+      # Specify kb instead of k in order to get four arguments listed, for
+      # easier diagnosis from stacks.
+      output = subprocess.check_output([cdb, '-y', self._browser_directory,
+                                        '-c', '.ecxr;.lastevent;kb30;~*kb30;q',
+                                        '-z', minidump])
+      # The output we care about starts with "Last event:" or possibly
+      # other things we haven't seen yet. If we can't find the start of the
+      # last event entry, include output from the beginning.
+      info_start = 0
+      info_start_match = re.search("Last event:", output, re.MULTILINE)
+      if info_start_match:
+        info_start = info_start_match.start()
+      info_end = output.find('quit:')
+      return output[info_start:info_end]
+
+    arch_name = self.browser.platform.GetArchName()
+    stackwalk = binary_manager.FetchPath(
+        'minidump_stackwalk', arch_name, os_name)
+    if not stackwalk:
+      logging.warning('minidump_stackwalk binary not found.')
+      return None
+    # We only want this logic on linux platforms that are still using breakpad.
+    # See crbug.com/667475
+    if not self._MinidumpObtainedFromCrashpad(minidump):
+      with open(minidump, 'rb') as infile:
+        minidump += '.stripped'
+        with open(minidump, 'wb') as outfile:
+          outfile.write(''.join(infile.read().partition('MDMP')[1:]))
+
+    symbols_path = os.path.join(self._tmp_minidump_dir, 'symbols')
+    GenerateBreakpadSymbols(minidump, arch_name, os_name,
+                            symbols_path, self._browser_directory)
+
+    return subprocess.check_output([stackwalk, minidump, symbols_path],
+                                   stderr=open(os.devnull, 'w'))
+
+  def _UploadMinidumpToCloudStorage(self, minidump_path):
+    """ Upload minidump_path to cloud storage and return the cloud storage url.
+    """
+    remote_path = ('minidump-%s-%i.dmp' %
+                   (datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S'),
+                    random.randint(0, 1000000)))
+    try:
+      return cloud_storage.Insert(cloud_storage.TELEMETRY_OUTPUT, remote_path,
+                                  minidump_path)
+    except cloud_storage.CloudStorageError as err:
+      logging.error('Cloud storage error while trying to upload dump: %s' %
+                    repr(err))
+      return '<Missing link>'
+
+  def GetStackTrace(self):
+    """Returns a stack trace if a valid minidump is found, will return a tuple
+       (valid, output) where valid will be True if a valid minidump was found
+       and output will contain either an error message or the attempt to
+       symbolize the minidump if one was found.
+    """
+    most_recent_dump = self._GetMostRecentMinidump()
+    if not most_recent_dump:
+      return (False, 'No crash dump found.')
+    logging.info('Minidump found: %s' % most_recent_dump)
+    return self._InternalSymbolizeMinidump(most_recent_dump)
+
+  def GetMostRecentMinidumpPath(self):
+    return self._GetMostRecentMinidump()
+
+  def GetAllMinidumpPaths(self):
+    reports_list = self._GetAllCrashpadMinidumps()
+    if reports_list:
+      for report in reports_list:
+        self._minidump_path_crashpad_retrieval[report[1]] = True
+      return [report[1] for report in reports_list]
+    else:
+      logging.info('No minidump found via crashpad_database_util')
+      dumps = self._GetBreakPadMinidumpPaths()
+      if dumps:
+        logging.info('Found minidump via globbing in minidump dir')
+        for dump in dumps:
+          self._minidump_path_crashpad_retrieval[dump] = False
+        return dumps
+      return []
+
+  def GetAllUnsymbolizedMinidumpPaths(self):
+    minidump_paths = set(self.GetAllMinidumpPaths())
+    # If we have already symbolized paths remove them from the list
+    unsymbolized_paths = (minidump_paths
+      - self._most_recent_symbolized_minidump_paths)
+    return list(unsymbolized_paths)
+
+  def SymbolizeMinidump(self, minidump_path):
+    return self._InternalSymbolizeMinidump(minidump_path)
+
+  def _InternalSymbolizeMinidump(self, minidump_path):
+    cloud_storage_link = self._UploadMinidumpToCloudStorage(minidump_path)
+
+    stack = self._GetStackFromMinidump(minidump_path)
+    if not stack:
+      error_message = ('Failed to symbolize minidump. Raw stack is uploaded to'
+                       ' cloud storage: %s.' % cloud_storage_link)
+      return (False, error_message)
+
+    self._most_recent_symbolized_minidump_paths.add(minidump_path)
+    return (True, stack)
+
+  def __del__(self):
+    self.Close()
+
+  def _TryCooperativeShutdown(self):
+    if self.browser.platform.IsCooperativeShutdownSupported():
+      # Ideally there would be a portable, cooperative shutdown
+      # mechanism for the browser. This seems difficult to do
+      # correctly for all embedders of the content API. The only known
+      # problem with unclean shutdown of the browser process is on
+      # Windows, where suspended child processes frequently leak. For
+      # now, just solve this particular problem. See Issue 424024.
+      if self.browser.platform.CooperativelyShutdown(self._proc, "chrome"):
+        try:
+          py_utils.WaitFor(lambda: not self.IsBrowserRunning(), timeout=5)
+          logging.info('Successfully shut down browser cooperatively')
+        except py_utils.TimeoutException as e:
+          logging.warning('Failed to cooperatively shutdown. ' +
+                          'Proceeding to terminate: ' + str(e))
+
+  def Background(self):
+    raise NotImplementedError
+
+  def Close(self):
+    super(DesktopBrowserBackend, self).Close()
+
+    # First, try to cooperatively shutdown.
+    if self.IsBrowserRunning():
+      self._TryCooperativeShutdown()
+
+    # Second, try to politely shutdown with SIGTERM.
+    if self.IsBrowserRunning():
+      self._proc.terminate()
+      try:
+        py_utils.WaitFor(lambda: not self.IsBrowserRunning(), timeout=5)
+        self._proc = None
+      except py_utils.TimeoutException:
+        logging.warning('Failed to gracefully shutdown.')
+
+    # Shutdown aggressively if all above failed.
+    if self.IsBrowserRunning():
+      logging.warning('Proceed to kill the browser.')
+      self._proc.kill()
+    self._proc = None
+
+    if self._output_profile_path:
+      # If we need the output then double check that it exists.
+      if not (self._tmp_profile_dir and os.path.exists(self._tmp_profile_dir)):
+        raise Exception("No profile directory generated by Chrome: '%s'." %
+            self._tmp_profile_dir)
+    else:
+      # If we don't need the profile after the run then cleanup.
+      if self._tmp_profile_dir and os.path.exists(self._tmp_profile_dir):
+        shutil.rmtree(self._tmp_profile_dir, ignore_errors=True)
+        self._tmp_profile_dir = None
+
+    if self._tmp_output_file:
+      self._tmp_output_file.close()
+      self._tmp_output_file = None
+
+    if self._tmp_minidump_dir:
+      shutil.rmtree(self._tmp_minidump_dir, ignore_errors=True)
+      self._tmp_minidump_dir = None
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/desktop_browser_finder.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/desktop_browser_finder.py
new file mode 100644
index 0000000..e2cedb7
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/desktop_browser_finder.py
@@ -0,0 +1,288 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Finds desktop browsers that can be controlled by telemetry."""
+
+import logging
+import os
+import sys
+
+import dependency_manager  # pylint: disable=import-error
+
+from telemetry.core import exceptions
+from telemetry.core import platform as platform_module
+from telemetry.internal.backends.chrome import desktop_browser_backend
+from telemetry.internal.browser import browser
+from telemetry.internal.browser import possible_browser
+from telemetry.internal.platform import desktop_device
+from telemetry.internal.util import binary_manager
+# This is a workaround for https://goo.gl/1tGNgd
+from telemetry.internal.util import path as path_module
+
+
+class PossibleDesktopBrowser(possible_browser.PossibleBrowser):
+  """A desktop browser that can be controlled."""
+
+  def __init__(self, browser_type, finder_options, executable, flash_path,
+               is_content_shell, browser_directory, is_local_build=False):
+    target_os = sys.platform.lower()
+    super(PossibleDesktopBrowser, self).__init__(
+        browser_type, target_os, not is_content_shell)
+    assert browser_type in FindAllBrowserTypes(finder_options), (
+        'Please add %s to desktop_browser_finder.FindAllBrowserTypes' %
+        browser_type)
+    self._local_executable = executable
+    self._flash_path = flash_path
+    self._is_content_shell = is_content_shell
+    self._browser_directory = browser_directory
+    self.is_local_build = is_local_build
+
+  def __repr__(self):
+    return 'PossibleDesktopBrowser(type=%s, executable=%s, flash=%s)' % (
+        self.browser_type, self._local_executable, self._flash_path)
+
+  def _InitPlatformIfNeeded(self):
+    if self._platform:
+      return
+
+    self._platform = platform_module.GetHostPlatform()
+
+    # pylint: disable=protected-access
+    self._platform_backend = self._platform._platform_backend
+
+  def Create(self, finder_options):
+    if self._flash_path and not os.path.exists(self._flash_path):
+      logging.warning(
+          'Could not find Flash at %s. Continuing without Flash.\n'
+          'To run with Flash, check it out via http://go/read-src-internal',
+          self._flash_path)
+      self._flash_path = None
+
+    self._InitPlatformIfNeeded()
+
+    browser_backend = desktop_browser_backend.DesktopBrowserBackend(
+        self._platform_backend,
+        finder_options.browser_options, self._local_executable,
+        self._flash_path, self._is_content_shell, self._browser_directory)
+    return browser.Browser(
+        browser_backend, self._platform_backend, self._credentials_path)
+
+  def SupportsOptions(self, browser_options):
+    if ((len(browser_options.extensions_to_load) != 0)
+        and self._is_content_shell):
+      return False
+    return True
+
+  def UpdateExecutableIfNeeded(self):
+    pass
+
+  def last_modification_time(self):
+    if os.path.exists(self._local_executable):
+      return os.path.getmtime(self._local_executable)
+    return -1
+
+def SelectDefaultBrowser(possible_browsers):
+  local_builds_by_date = [
+      b for b in sorted(possible_browsers,
+                        key=lambda b: b.last_modification_time())
+      if b.is_local_build]
+  if local_builds_by_date:
+    return local_builds_by_date[-1]
+  return None
+
+def CanFindAvailableBrowsers():
+  return not platform_module.GetHostPlatform().GetOSName() == 'chromeos'
+
+def CanPossiblyHandlePath(target_path):
+  _, extension = os.path.splitext(target_path.lower())
+  if sys.platform == 'darwin' or sys.platform.startswith('linux'):
+    return not extension
+  elif sys.platform.startswith('win'):
+    return extension == '.exe'
+  return False
+
+def FindAllBrowserTypes(_):
+  return [
+      'exact',
+      'reference',
+      'release',
+      'release_x64',
+      'debug',
+      'debug_x64',
+      'default',
+      'stable',
+      'beta',
+      'dev',
+      'canary',
+      'content-shell-debug',
+      'content-shell-debug_x64',
+      'content-shell-release',
+      'content-shell-release_x64',
+      'content-shell-default',
+      'system']
+
+def FindAllAvailableBrowsers(finder_options, device):
+  """Finds all the desktop browsers available on this machine."""
+  if not isinstance(device, desktop_device.DesktopDevice):
+    return []
+
+  browsers = []
+
+  if not CanFindAvailableBrowsers():
+    return []
+
+  has_x11_display = True
+  if (sys.platform.startswith('linux') and
+      os.getenv('DISPLAY') == None):
+    has_x11_display = False
+
+  os_name = platform_module.GetHostPlatform().GetOSName()
+  arch_name = platform_module.GetHostPlatform().GetArchName()
+  try:
+    flash_path = binary_manager.LocalPath('flash', arch_name, os_name)
+  except dependency_manager.NoPathFoundError:
+    flash_path = None
+    logging.warning(
+        'Chrome build location for %s_%s not found. Browser will be run '
+        'without Flash.', os_name, arch_name)
+
+  chromium_app_names = []
+  if sys.platform == 'darwin':
+    chromium_app_names.append('Chromium.app/Contents/MacOS/Chromium')
+    chromium_app_names.append('Google Chrome.app/Contents/MacOS/Google Chrome')
+    content_shell_app_name = 'Content Shell.app/Contents/MacOS/Content Shell'
+  elif sys.platform.startswith('linux'):
+    chromium_app_names.append('chrome')
+    content_shell_app_name = 'content_shell'
+  elif sys.platform.startswith('win'):
+    chromium_app_names.append('chrome.exe')
+    content_shell_app_name = 'content_shell.exe'
+  else:
+    raise Exception('Platform not recognized')
+
+  # Add the explicit browser executable if given and we can handle it.
+  if (finder_options.browser_executable and
+      CanPossiblyHandlePath(finder_options.browser_executable)):
+    is_content_shell = finder_options.browser_executable.endswith(
+        content_shell_app_name)
+    is_chrome_or_chromium = len([x for x in chromium_app_names if
+        finder_options.browser_executable.endswith(x)]) != 0
+
+    # It is okay if the executable name doesn't match any of known chrome
+    # browser executables, since it may be of a different browser.
+    if is_chrome_or_chromium or is_content_shell:
+      normalized_executable = os.path.expanduser(
+          finder_options.browser_executable)
+      if path_module.IsExecutable(normalized_executable):
+        browser_directory = os.path.dirname(finder_options.browser_executable)
+        browsers.append(PossibleDesktopBrowser(
+            'exact', finder_options, normalized_executable, flash_path,
+            is_content_shell,
+            browser_directory))
+      else:
+        raise exceptions.PathMissingError(
+            '%s specified by --browser-executable does not exist or is not '
+            'executable' %
+            normalized_executable)
+
+  def AddIfFound(browser_type, build_path, app_name, content_shell):
+    app = os.path.join(build_path, app_name)
+    if path_module.IsExecutable(app):
+      browsers.append(PossibleDesktopBrowser(
+          browser_type, finder_options, app, flash_path,
+          content_shell, build_path, is_local_build=True))
+      return True
+    return False
+
+  # Add local builds
+  for build_path in path_module.GetBuildDirectories(finder_options.chrome_root):
+    # TODO(agrieve): Extract browser_type from args.gn's is_debug.
+    browser_type = os.path.basename(build_path).lower()
+    for chromium_app_name in chromium_app_names:
+      AddIfFound(browser_type, build_path, chromium_app_name, False)
+    AddIfFound('content-shell-' + browser_type, build_path,
+               content_shell_app_name, True)
+
+  reference_build = None
+  if finder_options.browser_type == 'reference':
+    # Reference builds are only available in a Chromium checkout. We should not
+    # raise an error just because they don't exist.
+    os_name = platform_module.GetHostPlatform().GetOSName()
+    arch_name = platform_module.GetHostPlatform().GetArchName()
+    reference_build = binary_manager.FetchPath(
+        'chrome_stable', arch_name, os_name)
+
+  # Mac-specific options.
+  if sys.platform == 'darwin':
+    mac_canary_root = '/Applications/Google Chrome Canary.app/'
+    mac_canary = mac_canary_root + 'Contents/MacOS/Google Chrome Canary'
+    mac_system_root = '/Applications/Google Chrome.app'
+    mac_system = mac_system_root + '/Contents/MacOS/Google Chrome'
+    if path_module.IsExecutable(mac_canary):
+      browsers.append(PossibleDesktopBrowser('canary', finder_options,
+                                             mac_canary, None, False,
+                                             mac_canary_root))
+
+    if path_module.IsExecutable(mac_system):
+      browsers.append(PossibleDesktopBrowser('system', finder_options,
+                                             mac_system, None, False,
+                                             mac_system_root))
+
+    if reference_build and path_module.IsExecutable(reference_build):
+      reference_root = os.path.dirname(os.path.dirname(os.path.dirname(
+          reference_build)))
+      browsers.append(PossibleDesktopBrowser('reference', finder_options,
+                                             reference_build, None, False,
+                                             reference_root))
+
+  # Linux specific options.
+  if sys.platform.startswith('linux'):
+    versions = {
+        'system': os.path.split(os.path.realpath('/usr/bin/google-chrome'))[0],
+        'stable': '/opt/google/chrome',
+        'beta': '/opt/google/chrome-beta',
+        'dev': '/opt/google/chrome-unstable'
+    }
+
+    for version, root in versions.iteritems():
+      browser_path = os.path.join(root, 'chrome')
+      if path_module.IsExecutable(browser_path):
+        browsers.append(PossibleDesktopBrowser(version, finder_options,
+                                               browser_path, None, False, root))
+    if reference_build and path_module.IsExecutable(reference_build):
+      reference_root = os.path.dirname(reference_build)
+      browsers.append(PossibleDesktopBrowser('reference', finder_options,
+                                             reference_build, None, False,
+                                             reference_root))
+
+  # Win32-specific options.
+  if sys.platform.startswith('win'):
+    app_paths = [
+        ('system', os.path.join('Google', 'Chrome', 'Application')),
+        ('canary', os.path.join('Google', 'Chrome SxS', 'Application')),
+    ]
+    if reference_build:
+      app_paths.append(
+          ('reference', os.path.dirname(reference_build)))
+
+    for browser_name, app_path in app_paths:
+      for chromium_app_name in chromium_app_names:
+        full_path = path_module.FindInstalledWindowsApplication(
+            os.path.join(app_path, chromium_app_name))
+        if full_path:
+          browsers.append(PossibleDesktopBrowser(
+              browser_name, finder_options, full_path,
+              None, False, os.path.dirname(full_path)))
+
+  has_ozone_platform = False
+  for arg in finder_options.browser_options.extra_browser_args:
+    if "--ozone-platform" in arg:
+      has_ozone_platform = True
+
+  if len(browsers) and not has_x11_display and not has_ozone_platform:
+    logging.warning(
+      'Found (%s), but you do not have a DISPLAY environment set.' %
+      ','.join([b.browser_type for b in browsers]))
+    return []
+
+  return browsers
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/desktop_browser_finder_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/desktop_browser_finder_unittest.py
new file mode 100644
index 0000000..c6c630e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/desktop_browser_finder_unittest.py
@@ -0,0 +1,298 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import os
+import stat
+import unittest
+
+from pyfakefs import fake_filesystem_unittest
+
+from telemetry.core import platform
+from telemetry.core import util
+from telemetry.internal.backends.chrome import desktop_browser_finder
+from telemetry.internal.browser import browser_options
+from telemetry.internal.platform import desktop_device
+from telemetry.testing import system_stub
+
+
+# This file verifies the logic for finding a browser instance on all platforms
+# at once. It does so by providing stubs for the OS/sys/subprocess primitives
+# that the underlying finding logic usually uses to locate a suitable browser.
+# We prefer this approach to having to run the same test on every platform on
+# which we want this code to work.
+
+class FindTestBase(unittest.TestCase):
+  def setUp(self):
+    self._finder_options = browser_options.BrowserFinderOptions()
+    self._finder_options.chrome_root = '../../../'
+    self._finder_stubs = system_stub.Override(desktop_browser_finder,
+                                              ['os', 'subprocess', 'sys'])
+    self._path_stubs = system_stub.Override(
+        desktop_browser_finder.path_module, ['os', 'sys'])
+    self._catapult_path_stubs = system_stub.Override(
+        desktop_browser_finder.path_module.catapult_util, ['os', 'sys'])
+    self._util_stubs = system_stub.Override(util, ['os', 'sys'])
+
+  def tearDown(self):
+    self._finder_stubs.Restore()
+    self._path_stubs.Restore()
+    self._catapult_path_stubs.Restore()
+    self._util_stubs.Restore()
+
+  @property
+  def _files(self):
+    return self._catapult_path_stubs.os.path.files
+
+  def DoFindAll(self):
+    return desktop_browser_finder.FindAllAvailableBrowsers(
+      self._finder_options, desktop_device.DesktopDevice())
+
+  def DoFindAllTypes(self):
+    browsers = self.DoFindAll()
+    return [b.browser_type for b in browsers]
+
+  def CanFindAvailableBrowsers(self):
+    return desktop_browser_finder.CanFindAvailableBrowsers()
+
+
+def has_type(array, browser_type):
+  return len([x for x in array if x.browser_type == browser_type]) != 0
+
+
+class FindSystemTest(FindTestBase):
+  def setUp(self):
+    super(FindSystemTest, self).setUp()
+    self._finder_stubs.sys.platform = 'win32'
+    self._path_stubs.sys.platform = 'win32'
+    self._util_stubs.sys.platform = 'win32'
+
+  def testFindProgramFiles(self):
+    if not self.CanFindAvailableBrowsers():
+      return
+
+    self._files.append(
+        'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe')
+    self._path_stubs.os.program_files = 'C:\\Program Files'
+    self.assertIn('system', self.DoFindAllTypes())
+
+  def testFindProgramFilesX86(self):
+    if not self.CanFindAvailableBrowsers():
+      return
+
+    self._files.append(
+        'C:\\Program Files(x86)\\Google\\Chrome\\Application\\chrome.exe')
+    self._path_stubs.os.program_files_x86 = 'C:\\Program Files(x86)'
+    self.assertIn('system', self.DoFindAllTypes())
+
+  def testFindLocalAppData(self):
+    if not self.CanFindAvailableBrowsers():
+      return
+
+    self._files.append(
+        'C:\\Local App Data\\Google\\Chrome\\Application\\chrome.exe')
+    self._path_stubs.os.local_app_data = 'C:\\Local App Data'
+    self.assertIn('system', self.DoFindAllTypes())
+
+
+class FindLocalBuildsTest(FindTestBase):
+  def setUp(self):
+    super(FindLocalBuildsTest, self).setUp()
+    self._finder_stubs.sys.platform = 'win32'
+    self._path_stubs.sys.platform = 'win32'
+    self._util_stubs.sys.platform = 'win32'
+
+  def testFindBuild(self):
+    if not self.CanFindAvailableBrowsers():
+      return
+
+    self._files.append('..\\..\\..\\build\\Release\\chrome.exe')
+    self.assertIn('release', self.DoFindAllTypes())
+
+  def testFindOut(self):
+    if not self.CanFindAvailableBrowsers():
+      return
+
+    self._files.append('..\\..\\..\\out\\Release\\chrome.exe')
+    self.assertIn('release', self.DoFindAllTypes())
+
+  def testFindXcodebuild(self):
+    if not self.CanFindAvailableBrowsers():
+      return
+
+    self._files.append('..\\..\\..\\xcodebuild\\Release\\chrome.exe')
+    self.assertIn('release', self.DoFindAllTypes())
+
+
+class OSXFindTest(FindTestBase):
+  def setUp(self):
+    super(OSXFindTest, self).setUp()
+    self._finder_stubs.sys.platform = 'darwin'
+    self._path_stubs.sys.platform = 'darwin'
+    self._util_stubs.sys.platform = 'darwin'
+    self._files.append('/Applications/Google Chrome Canary.app/'
+                       'Contents/MacOS/Google Chrome Canary')
+    self._files.append('/Applications/Google Chrome.app/' +
+                       'Contents/MacOS/Google Chrome')
+    self._files.append(
+      '../../../out/Release/Chromium.app/Contents/MacOS/Chromium')
+    self._files.append(
+      '../../../out/Debug/Chromium.app/Contents/MacOS/Chromium')
+    self._files.append(
+      '../../../out/Release/Content Shell.app/Contents/MacOS/Content Shell')
+    self._files.append(
+      '../../../out/Debug/Content Shell.app/Contents/MacOS/Content Shell')
+
+  def testFindAll(self):
+    if not self.CanFindAvailableBrowsers():
+      return
+
+    types = self.DoFindAllTypes()
+    self.assertEquals(
+      set(types),
+      set(['debug', 'release',
+           'content-shell-debug', 'content-shell-release',
+           'canary', 'system']))
+
+  def testFindExact(self):
+    if not self.CanFindAvailableBrowsers():
+      return
+
+    self._files.append(
+      '../../../foo1/Chromium.app/Contents/MacOS/Chromium')
+    self._finder_options.browser_executable = (
+        '../../../foo1/Chromium.app/Contents/MacOS/Chromium')
+    types = self.DoFindAllTypes()
+    self.assertTrue('exact' in types)
+
+  def testCannotFindExact(self):
+    if not self.CanFindAvailableBrowsers():
+      return
+
+    self._files.append(
+      '../../../foo1/Chromium.app/Contents/MacOS/Chromium')
+    self._finder_options.browser_executable = (
+        '../../../foo2/Chromium.app/Contents/MacOS/Chromium')
+    self.assertRaises(Exception, self.DoFindAllTypes)
+
+class LinuxFindTest(fake_filesystem_unittest.TestCase):
+
+  def setUp(self):
+    if not platform.GetHostPlatform().GetOSName() == 'linux':
+      self.skipTest('Not running on Linux')
+    self.setUpPyfakefs()
+
+    self._finder_options = browser_options.BrowserFinderOptions()
+    self._finder_options.chrome_root = '/src/'
+
+  def CreateBrowser(self, path):
+    self.fs.CreateFile(path)
+    os.chmod(path, stat.S_IXUSR)
+
+  def DoFindAll(self):
+    return desktop_browser_finder.FindAllAvailableBrowsers(
+        self._finder_options, desktop_device.DesktopDevice())
+
+  def DoFindAllTypes(self):
+    return [b.browser_type for b in self.DoFindAll()]
+
+  def testFindAllWithCheckout(self):
+    for target in ['Release', 'Debug']:
+      for browser in ['chrome', 'content_shell']:
+        self.CreateBrowser('/src/out/%s/%s' % (target, browser))
+
+    self.assertEquals(
+        set(self.DoFindAllTypes()),
+        {'debug', 'release', 'content-shell-debug', 'content-shell-release'})
+
+  def testFindAllFailsIfNotExecutable(self):
+    self.fs.CreateFile('/src/out/Release/chrome')
+
+    self.assertFalse(self.DoFindAllTypes())
+
+  def testFindWithProvidedExecutable(self):
+    self.CreateBrowser('/foo/chrome')
+    self._finder_options.browser_executable = '/foo/chrome'
+    self.assertIn('exact', self.DoFindAllTypes())
+
+  def testFindWithProvidedApk(self):
+    self._finder_options.browser_executable = '/foo/chrome.apk'
+    self.assertNotIn('exact', self.DoFindAllTypes())
+
+  def testNoErrorWithNonChromeExecutableName(self):
+    self.fs.CreateFile('/foo/another_browser')
+    self._finder_options.browser_executable = '/foo/another_browser'
+    self.assertNotIn('exact', self.DoFindAllTypes())
+
+  def testFindAllWithInstalled(self):
+    official_names = ['chrome', 'chrome-beta', 'chrome-unstable']
+
+    for name in official_names:
+      self.CreateBrowser('/opt/google/%s/chrome' % name)
+
+    self.assertEquals(set(self.DoFindAllTypes()), {'stable', 'beta', 'dev'})
+
+  def testFindAllSystem(self):
+    self.CreateBrowser('/opt/google/chrome/chrome')
+    os.symlink('/opt/google/chrome/chrome', '/usr/bin/google-chrome')
+
+    self.assertEquals(set(self.DoFindAllTypes()), {'system', 'stable'})
+
+  def testFindAllSystemIsBeta(self):
+    self.CreateBrowser('/opt/google/chrome/chrome')
+    self.CreateBrowser('/opt/google/chrome-beta/chrome')
+    os.symlink('/opt/google/chrome-beta/chrome', '/usr/bin/google-chrome')
+
+    google_chrome = [browser for browser in self.DoFindAll()
+                     if browser.browser_type == 'system'][0]
+    self.assertEquals('/opt/google/chrome-beta',
+                      google_chrome._browser_directory)
+
+
+class WinFindTest(FindTestBase):
+  def setUp(self):
+    super(WinFindTest, self).setUp()
+
+    self._finder_stubs.sys.platform = 'win32'
+    self._path_stubs.sys.platform = 'win32'
+    self._util_stubs.sys.platform = 'win32'
+    self._path_stubs.os.local_app_data = 'c:\\Users\\Someone\\AppData\\Local'
+    self._files.append('c:\\tmp\\chrome.exe')
+    self._files.append('..\\..\\..\\build\\Release\\chrome.exe')
+    self._files.append('..\\..\\..\\build\\Debug\\chrome.exe')
+    self._files.append('..\\..\\..\\build\\Release\\content_shell.exe')
+    self._files.append('..\\..\\..\\build\\Debug\\content_shell.exe')
+    self._files.append(self._path_stubs.os.local_app_data + '\\' +
+                       'Google\\Chrome\\Application\\chrome.exe')
+    self._files.append(self._path_stubs.os.local_app_data + '\\' +
+                       'Google\\Chrome SxS\\Application\\chrome.exe')
+
+  def testFindAllGivenDefaults(self):
+    if not self.CanFindAvailableBrowsers():
+      return
+
+    types = self.DoFindAllTypes()
+    self.assertEquals(set(types),
+                      set(['debug', 'release',
+                           'content-shell-debug', 'content-shell-release',
+                           'system', 'canary']))
+
+  def testFindAllWithExact(self):
+    if not self.CanFindAvailableBrowsers():
+      return
+
+    self._finder_options.browser_executable = 'c:\\tmp\\chrome.exe'
+    types = self.DoFindAllTypes()
+    self.assertEquals(
+        set(types),
+        set(['exact',
+             'debug', 'release',
+             'content-shell-debug', 'content-shell-release',
+             'system', 'canary']))
+
+  def testNoErrorWithUnrecognizedExecutableName(self):
+    if not self.CanFindAvailableBrowsers():
+      return
+
+    self._files.append('c:\\foo\\another_browser.exe')
+    self._finder_options.browser_dir = 'c:\\foo\\another_browser.exe'
+    self.assertNotIn('exact', self.DoFindAllTypes())
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/extension_backend.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/extension_backend.py
new file mode 100644
index 0000000..b6abd97
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/extension_backend.py
@@ -0,0 +1,48 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import collections
+
+from telemetry.internal.backends.chrome_inspector import inspector_backend_list
+from telemetry.internal.browser import extension_page
+
+
+class ExtensionBackendList(inspector_backend_list.InspectorBackendList):
+  """A dynamic sequence of extension_page.ExtensionPages."""
+
+  def __init__(self, browser_backend):
+    super(ExtensionBackendList, self).__init__(browser_backend)
+
+  def ShouldIncludeContext(self, context):
+    return context['url'].startswith('chrome-extension://')
+
+  def CreateWrapper(self, inspector_backend):
+    return extension_page.ExtensionPage(inspector_backend)
+
+class ExtensionBackendDict(collections.Mapping):
+  """A dynamic mapping of extension_id to extension_page.ExtensionPages."""
+
+  def __init__(self, browser_backend):
+    self._extension_backend_list = ExtensionBackendList(browser_backend)
+
+  def __getitem__(self, extension_id):
+    extensions = []
+    for context_id in self._extension_backend_list.IterContextIds():
+      if self.ContextIdToExtensionId(context_id) == extension_id:
+        extensions.append(
+            self._extension_backend_list.GetBackendFromContextId(context_id))
+    if not extensions:
+      raise KeyError('Cannot find an extension with id=%s' % extension_id)
+    return extensions
+
+  def __iter__(self):
+    for context_id in self._extension_backend_list.IterContextIds():
+      yield self._extension_backend_list.GetBackendFromContextId(context_id)
+
+  def __len__(self):
+    return len(self._extension_backend_list)
+
+  def ContextIdToExtensionId(self, context_id):
+    context = self._extension_backend_list.GetContextInfo(context_id)
+    return extension_page.UrlToExtensionId(context['url'])
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/ios_browser_backend.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/ios_browser_backend.py
new file mode 100644
index 0000000..fac37cb
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/ios_browser_backend.py
@@ -0,0 +1,157 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import contextlib
+import json
+import logging
+import re
+import urllib2
+
+from telemetry.internal.backends.chrome import chrome_browser_backend
+from telemetry.internal.backends.chrome import system_info_backend
+
+import py_utils
+
+
+class IosBrowserBackend(chrome_browser_backend.ChromeBrowserBackend):
+  _DEBUGGER_URL_BUILDER = 'ws://localhost:%i/devtools/page/%i'
+  _DEBUGGER_URL_REGEX = r'ws://localhost:(\d+)/devtools/page/(\d+)'
+  _DEVICE_LIST_URL = 'http://localhost:9221/json'
+
+  def __init__(self, ios_platform_backend, browser_options):
+    browser_options.output_profile_path = '.'
+    super(IosBrowserBackend, self).__init__(
+        ios_platform_backend,
+        supports_tab_control=False,
+        supports_extensions=False,
+        browser_options=browser_options)
+    self._webviews = []
+    self._port = None
+    self._page = None
+    self._system_info_backend = None
+    self.UpdateRunningBrowsersInfo()
+
+  def UpdateRunningBrowsersInfo(self):
+    """ Refresh to match current state of the running browser.
+    """
+    device_urls = self.GetDeviceUrls()
+    urls = self.GetWebSocketDebuggerUrls(device_urls)
+    for url in urls:
+      m = re.match(self._DEBUGGER_URL_REGEX, url)
+      if m:
+        self._webviews.append([int(m.group(1)), int(m.group(2))])
+      else:
+        logging.error('Unexpected url format: %s' % url)
+
+    # TODO(baxley): For now, grab first item from |_webviews|. Ideally, we'd
+    # prefer to have the currently displayed tab, or something similar.
+    if self._webviews:
+      self._port = self._webviews[0][0]
+      self._page = self._webviews[0][1]
+
+  def GetDeviceUrls(self):
+    device_urls = []
+    try:
+      with contextlib.closing(
+          urllib2.urlopen(self._DEVICE_LIST_URL)) as device_list:
+        json_urls = device_list.read()
+        device_urls = json.loads(json_urls)
+        if not device_urls:
+          logging.debug('No iOS devices found. Will not try searching for iOS '
+                        'browsers.')
+          return []
+    except urllib2.URLError as e:
+      logging.debug('Error communicating with iOS device.')
+      logging.debug(str(e))
+      return []
+    return device_urls
+
+  def GetWebSocketDebuggerUrls(self, device_urls):
+    """ Get a list of the websocket debugger URLs to communicate with
+        all running UIWebViews.
+    """
+    data = []
+    # Loop through all devices.
+    for d in device_urls:
+      def GetData():
+        try:
+          with contextlib.closing(
+              # pylint: disable=cell-var-from-loop
+              urllib2.urlopen('http://%s/json' % d['url'])) as f:
+            json_result = f.read()
+            data = json.loads(json_result)
+            return data
+        except urllib2.URLError as e:
+          logging.debug('Error communicating with iOS device.')
+          logging.debug(e)
+          return False
+      try:
+        # Retry a few times since it can take a few seconds for this API to be
+        # ready, if ios_webkit_debug_proxy is just launched.
+        data = py_utils.WaitFor(GetData, 5)
+      except py_utils.TimeoutException as e:
+        logging.debug('Timeout retrieving data from iOS device')
+        logging.debug(e)
+        return []
+
+    # Find all running UIWebViews.
+    debug_urls = []
+    for j in data:
+      debug_urls.append(j['webSocketDebuggerUrl'])
+
+    return debug_urls
+
+  def GetSystemInfo(self):
+    if self._system_info_backend is None:
+      self._system_info_backend = system_info_backend.SystemInfoBackend(
+          self._port, self._page)
+    return self._system_info_backend.GetSystemInfo()
+
+  def IsBrowserRunning(self):
+    return bool(self._webviews)
+
+  #TODO(baxley): The following were stubbed out to get the sunspider benchmark
+  # running. These should be implemented.
+  @property
+  def browser_directory(self):
+    logging.warn('Not implemented')
+    return None
+
+  @property
+  def profile_directory(self):
+    logging.warn('Not implemented')
+    return None
+
+  def Start(self):
+    logging.warn('Not implemented')
+
+  def extension_backend(self):
+    logging.warn('Not implemented')
+    return None
+
+  def GetBrowserStartupArgs(self):
+    logging.warn('Not implemented')
+    return None
+
+  def HasBrowserFinishedLaunching(self):
+    logging.warn('Not implemented')
+    return False
+
+  def GetStandardOutput(self):
+    raise NotImplementedError()
+
+  def GetStackTrace(self):
+    raise NotImplementedError()
+
+  def GetMostRecentMinidumpPath(self):
+    return None
+
+  def GetAllMinidumpPaths(self):
+    return None
+
+  def GetAllUnsymbolizedMinidumpPaths(self):
+    return None
+
+  def SymbolizeMinidump(self, minidump_path):
+    return None
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/ios_browser_finder.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/ios_browser_finder.py
new file mode 100644
index 0000000..c4e98f3
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/ios_browser_finder.py
@@ -0,0 +1,115 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Finds iOS browsers that can be controlled by telemetry."""
+
+import logging
+import re
+
+from telemetry.core import platform
+from telemetry.internal.backends.chrome import ios_browser_backend
+from telemetry.internal.backends.chrome_inspector import inspector_backend
+from telemetry.internal.browser import browser
+from telemetry.internal.browser import possible_browser
+from telemetry.internal.platform import ios_device
+from telemetry.internal.platform import ios_platform_backend
+
+
+# Key matches output from ios-webkit-debug-proxy and the value is a readable
+# description of the browser.
+IOS_BROWSERS = {'CriOS': 'ios-chrome', 'Version': 'ios-safari'}
+DEVICE_LIST_URL = 'http://127.0.0.1:9221/json'
+IOS_WEBKIT_DEBUG_PROXY = 'ios_webkit_debug_proxy'
+
+
+class PossibleIOSBrowser(possible_browser.PossibleBrowser):
+
+  """A running iOS browser instance."""
+  def __init__(self, browser_type, _):
+    super(PossibleIOSBrowser, self).__init__(browser_type, 'ios', True)
+
+  # TODO(baxley): Implement the following methods for iOS.
+  def Create(self, finder_options):
+    browser_backend = ios_browser_backend.IosBrowserBackend(
+        self._platform_backend, finder_options.browser_options)
+    return browser.Browser(
+        browser_backend, self._platform_backend, self._credentials_path)
+
+  def SupportsOptions(self, browser_options):
+    #TODO(baxley): Implement me.
+    return True
+
+  def UpdateExecutableIfNeeded(self):
+    #TODO(baxley): Implement me.
+    pass
+
+  def _InitPlatformIfNeeded(self):
+    if self._platform:
+      return
+
+    self._platform_backend = ios_platform_backend.IosPlatformBackend()
+    self._platform = platform.Platform(self._platform_backend)
+
+def SelectDefaultBrowser(_):
+  return None  # TODO(baxley): Implement me.
+
+
+def CanFindAvailableBrowsers():
+  # TODO(baxley): Add support for all platforms possible. Probably Linux,
+  # probably not Windows.
+  return platform.GetHostPlatform().GetOSName() == 'mac'
+
+
+def FindAllBrowserTypes(_):
+  return IOS_BROWSERS.values()
+
+
+def FindAllAvailableBrowsers(finder_options, device):
+  """Find all running iOS browsers on connected devices."""
+  if not isinstance(device, ios_device.IOSDevice):
+    return []
+
+  if not CanFindAvailableBrowsers():
+    return []
+
+  options = finder_options.browser_options
+
+  options.browser_type = 'ios-chrome'
+  host = platform.GetHostPlatform()
+  backend = ios_browser_backend.IosBrowserBackend(host, options)
+  # TODO(baxley): Use idevice to wake up device or log debug statement.
+  if not host.IsApplicationRunning(IOS_WEBKIT_DEBUG_PROXY):
+    host.LaunchApplication(IOS_WEBKIT_DEBUG_PROXY)
+    if not host.IsApplicationRunning(IOS_WEBKIT_DEBUG_PROXY):
+      return []
+
+  device_urls = backend.GetDeviceUrls()
+  if not device_urls:
+    logging.debug('Could not find any devices over %s.'
+                  % IOS_WEBKIT_DEBUG_PROXY)
+    return []
+
+  debug_urls = backend.GetWebSocketDebuggerUrls(device_urls)
+
+  # Get the userAgent for each UIWebView to find the browsers.
+  browser_pattern = (r'\)\s(%s)\/(\d+[\.\d]*)\sMobile'
+                     % '|'.join(IOS_BROWSERS.keys()))
+  browser_types = set()
+  for url in debug_urls:
+    context = {'webSocketDebuggerUrl': url, 'id': 1}
+    try:
+      inspector = inspector_backend.InspectorBackend(
+          backend.app, backend.devtools_client, context)
+      res = inspector.EvaluateJavaScript("navigator.userAgent")
+    finally:
+      inspector.Disconnect()
+    match_browsers = re.search(browser_pattern, res)
+    if match_browsers:
+      browser_types.add(match_browsers.group(1))
+
+  browsers = []
+  for browser_type in browser_types:
+    browsers.append(PossibleIOSBrowser(IOS_BROWSERS[browser_type],
+                                       finder_options))
+  return list(browsers)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/ios_browser_finder_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/ios_browser_finder_unittest.py
new file mode 100644
index 0000000..b5b6756
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/ios_browser_finder_unittest.py
@@ -0,0 +1,26 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import unittest
+
+from telemetry import decorators
+from telemetry.internal.backends.chrome import ios_browser_finder
+from telemetry.internal.browser import browser_options
+from telemetry.internal.platform import ios_device
+
+
+class IosBrowserFinderUnitTest(unittest.TestCase):
+  # TODO(baxley): Currently the tests require a device with Chrome running.
+  # This should be stubbed out so it runs on any system, with no device
+  # dependencies.
+  @decorators.Enabled('ios')
+  def testFindIosChrome(self):
+    finder_options = browser_options.BrowserFinderOptions()
+    browsers = ios_browser_finder.FindAllAvailableBrowsers(
+      finder_options, ios_device.IOSDevice())
+    self.assertTrue(browsers)
+    for browser in browsers:
+      self.assertEqual('ios-chrome', browser.browser_type)
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/misc_web_contents_backend.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/misc_web_contents_backend.py
new file mode 100644
index 0000000..c2babbd
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/misc_web_contents_backend.py
@@ -0,0 +1,36 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.core import exceptions
+from telemetry.internal.backends.chrome import oobe
+from telemetry.internal.backends.chrome_inspector import inspector_backend_list
+
+
+class MiscWebContentsBackend(inspector_backend_list.InspectorBackendList):
+  """A dynamic sequence of web contents not related to tabs and extensions.
+
+  Provides acccess to chrome://oobe/login page.
+  """
+
+  def __init__(self, browser_backend):
+    super(MiscWebContentsBackend, self).__init__(browser_backend)
+
+  @property
+  def oobe_exists(self):
+    """Lightweight property to determine if the oobe webui is visible."""
+    try:
+      return bool(len(self))
+    except exceptions.Error:
+      return False
+
+  def GetOobe(self):
+    if not len(self):
+      return None
+    return self[0]
+
+  def ShouldIncludeContext(self, context):
+    return context.get('url').startswith('chrome://oobe')
+
+  def CreateWrapper(self, inspector_backend):
+    return oobe.Oobe(inspector_backend)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/oobe.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/oobe.py
new file mode 100644
index 0000000..fff4278
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/oobe.py
@@ -0,0 +1,125 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from functools import partial
+import logging
+
+from telemetry.core import exceptions
+from telemetry.internal.browser import web_contents
+
+import py_utils
+
+
+class Oobe(web_contents.WebContents):
+  def __init__(self, inspector_backend):
+    super(Oobe, self).__init__(inspector_backend)
+
+  def _GaiaIFrameContext(self):
+    max_context_id = self.EnableAllContexts()
+    logging.debug('%d contexts in Gaia page' % max_context_id)
+    for gaia_iframe_context in range(max_context_id + 1):
+      try:
+        if self.EvaluateJavaScript(
+            "document.readyState == 'complete' && "
+            "document.getElementById('Email') != null",
+            context_id=gaia_iframe_context):
+          return gaia_iframe_context
+      except exceptions.EvaluateException:
+        pass
+    return None
+
+  def _GaiaWebviewContext(self):
+    webview_contexts = self.GetWebviewContexts()
+    if webview_contexts:
+      return webview_contexts[0]
+    return None
+
+  def _ExecuteOobeApi(self, api, *args):
+    logging.info('Invoking %s' % api)
+    self.WaitForJavaScriptCondition("typeof Oobe == 'function'", timeout=120)
+
+    if self.EvaluateJavaScript(
+        "typeof {{ @api }} == 'undefined'", api=api):
+      raise exceptions.LoginException('%s js api missing' % api)
+
+    # Example values:
+    #   |api|:    'doLogin'
+    #   |args|:   ['username', 'pass', True]
+    #   Executes: 'doLogin("username", "pass", true)'
+    self.ExecuteJavaScript('{{ @f }}({{ *args }})', f=api, args=args)
+
+  def NavigateGuestLogin(self):
+    """Logs in as guest."""
+    self._ExecuteOobeApi('Oobe.guestLoginForTesting')
+
+  def NavigateFakeLogin(self, username, password, gaia_id,
+                        enterprise_enroll=False):
+    """Fake user login."""
+    self._ExecuteOobeApi('Oobe.loginForTesting', username, password, gaia_id,
+                         enterprise_enroll)
+
+  def NavigateGaiaLogin(self, username, password,
+                        enterprise_enroll=False,
+                        for_user_triggered_enrollment=False):
+    """Logs in using the GAIA webview or IFrame, whichever is
+    present. |enterprise_enroll| allows for enterprise enrollment.
+    |for_user_triggered_enrollment| should be False for remora enrollment."""
+    self._ExecuteOobeApi('Oobe.skipToLoginForTesting')
+    if for_user_triggered_enrollment:
+      self._ExecuteOobeApi('Oobe.switchToEnterpriseEnrollmentForTesting')
+
+    self._NavigateGaiaLogin(username, password, enterprise_enroll)
+
+    if enterprise_enroll:
+      self.WaitForJavaScriptCondition(
+          'Oobe.isEnrollmentSuccessfulForTest()', timeout=30)
+      self._ExecuteOobeApi('Oobe.enterpriseEnrollmentDone')
+
+  def _NavigateGaiaLogin(self, username, password, enterprise_enroll):
+    """Invokes NavigateIFrameLogin or NavigateWebViewLogin as appropriate."""
+    def _GetGaiaFunction():
+      if self._GaiaWebviewContext() is not None:
+        return partial(Oobe._NavigateWebViewLogin,
+                       wait_for_close=not enterprise_enroll)
+      elif self._GaiaIFrameContext() is not None:
+        return partial(Oobe._NavigateIFrameLogin,
+                       add_user_for_testing=not enterprise_enroll)
+      return None
+    py_utils.WaitFor(_GetGaiaFunction, 20)(self, username, password)
+
+  def _NavigateIFrameLogin(self, username, password, add_user_for_testing):
+    """Logs into the IFrame-based GAIA screen"""
+    gaia_iframe_context = py_utils.WaitFor(self._GaiaIFrameContext, timeout=30)
+
+    if add_user_for_testing:
+      self._ExecuteOobeApi('Oobe.showAddUserForTesting')
+    self.ExecuteJavaScript("""
+        document.getElementById('Email').value= {{ username }};
+        document.getElementById('Passwd').value= {{ password }};
+        document.getElementById('signIn').click();""",
+        username=username, password=password,
+        context_id=gaia_iframe_context)
+
+  def _NavigateWebViewLogin(self, username, password, wait_for_close):
+    """Logs into the webview-based GAIA screen"""
+    self._NavigateWebViewEntry('identifierId', username, 'identifierNext')
+    self._NavigateWebViewEntry('password', password, 'passwordNext')
+    if wait_for_close:
+      py_utils.WaitFor(lambda: not self._GaiaWebviewContext(), 60)
+
+  def _NavigateWebViewEntry(self, field, value, next_field):
+    self._WaitForField(field)
+    self._WaitForField(next_field)
+    gaia_webview_context = self._GaiaWebviewContext()
+    gaia_webview_context.EvaluateJavaScript("""
+       document.getElementById({{ field }}).value= {{ value }};
+       document.getElementById({{ next_field }}).click()""",
+       field=field, value=value, next_field=next_field)
+
+  def _WaitForField(self, field):
+    gaia_webview_context = py_utils.WaitFor(self._GaiaWebviewContext, 5)
+    py_utils.WaitFor(gaia_webview_context.HasReachedQuiescence, 20)
+    gaia_webview_context.WaitForJavaScriptCondition(
+        "document.getElementById({{ field }}) != null",
+        field=field, timeout=20)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/system_info_backend.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/system_info_backend.py
new file mode 100644
index 0000000..4f71633
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/system_info_backend.py
@@ -0,0 +1,31 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.internal.backends.chrome_inspector import inspector_websocket
+from telemetry.internal.platform import system_info
+from telemetry.internal.util import camel_case
+
+
+class SystemInfoBackend(object):
+  def __init__(self, devtools_port, devtools_page=None):
+    self._port = devtools_port
+    self._page = devtools_page
+
+  def GetSystemInfo(self, timeout=10):
+    req = {'method': 'SystemInfo.getInfo'}
+    websocket = inspector_websocket.InspectorWebsocket()
+    try:
+      if self._page:
+        websocket.Connect('ws://127.0.0.1:%i/devtools/page/%i' %
+                          (self._port, self._page), timeout)
+      else:
+        websocket.Connect('ws://127.0.0.1:%i/devtools/browser' % self._port,
+                          timeout)
+      res = websocket.SyncRequest(req, timeout)
+    finally:
+      websocket.Disconnect()
+    if 'error' in res:
+      return None
+    return system_info.SystemInfo.FromDict(
+        camel_case.ToUnderscore(res['result']))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/tab_list_backend.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/tab_list_backend.py
new file mode 100644
index 0000000..36e38e3
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/tab_list_backend.py
@@ -0,0 +1,112 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import json
+
+from telemetry.core import exceptions
+from telemetry.internal.backends.chrome_inspector import inspector_backend_list
+from telemetry.internal.browser import tab
+
+import py_utils
+
+
+class TabUnexpectedResponseException(exceptions.DevtoolsTargetCrashException):
+  pass
+
+
+class TabListBackend(inspector_backend_list.InspectorBackendList):
+  """A dynamic sequence of tab.Tabs in UI order."""
+
+  def __init__(self, browser_backend):
+    super(TabListBackend, self).__init__(browser_backend)
+
+  def New(self, timeout):
+    """Makes a new tab.
+
+    Returns:
+      A Tab object.
+
+    Raises:
+      devtools_http.DevToolsClientConnectionError
+    """
+    if not self._browser_backend.supports_tab_control:
+      raise NotImplementedError("Browser doesn't support tab control.")
+    response = self._browser_backend.devtools_client.RequestNewTab(timeout)
+    try:
+      response = json.loads(response)
+      context_id = response['id']
+    except (KeyError, ValueError):
+      raise TabUnexpectedResponseException(
+          app=self._browser_backend.browser,
+          msg='Received response: %s' % response)
+    return self.GetBackendFromContextId(context_id)
+
+  def CloseTab(self, tab_id, timeout=300):
+    """Closes the tab with the given debugger_url.
+
+    Raises:
+      devtools_http.DevToolsClientConnectionError
+      devtools_client_backend.TabNotFoundError
+      TabUnexpectedResponseException
+      py_utils.TimeoutException
+    """
+    assert self._browser_backend.supports_tab_control
+    # TODO(dtu): crbug.com/160946, allow closing the last tab on some platforms.
+    # For now, just create a new tab before closing the last tab.
+    if len(self) <= 1:
+      self.New(timeout)
+
+    response = self._browser_backend.devtools_client.CloseTab(tab_id, timeout)
+
+    if response != 'Target is closing':
+      raise TabUnexpectedResponseException(
+          app=self._browser_backend.browser,
+          msg='Received response: %s' % response)
+
+    py_utils.WaitFor(lambda: tab_id not in self.IterContextIds(), timeout=5)
+
+  def ActivateTab(self, tab_id, timeout=30):
+    """Activates the tab with the given debugger_url.
+
+    Raises:
+      devtools_http.DevToolsClientConnectionError
+      devtools_client_backend.TabNotFoundError
+      TabUnexpectedResponseException
+    """
+    assert self._browser_backend.supports_tab_control
+
+    response = self._browser_backend.devtools_client.ActivateTab(tab_id,
+                                                                 timeout)
+
+    if response != 'Target activated':
+      raise TabUnexpectedResponseException(
+          app=self._browser_backend.browser,
+          msg='Received response: %s' % response)
+
+  def Get(self, index, ret):
+    """Returns self[index] if it exists, or ret if index is out of bounds."""
+    if len(self) <= index:
+      return ret
+    return self[index]
+
+  def ShouldIncludeContext(self, context):
+    if 'type' in context:
+      return (context['type'] == 'page' or
+              context['url'] == 'chrome://media-router/')
+    # TODO: For compatibility with Chrome before r177683.
+    # This check is not completely correct, see crbug.com/190592.
+    return not context['url'].startswith('chrome-extension://')
+
+  def CreateWrapper(self, inspector_backend):
+    return tab.Tab(inspector_backend, self, self._browser_backend.browser)
+
+  def _HandleDevToolsConnectionError(self, error):
+    if not self._browser_backend.IsAppRunning():
+      error.AddDebuggingMessage('The browser is not running. It probably '
+                                'crashed.')
+    elif not self._browser_backend.HasBrowserFinishedLaunching():
+      error.AddDebuggingMessage('The browser exists but cannot be reached.')
+    else:
+      error.AddDebuggingMessage('The browser exists and can be reached. '
+                                'The devtools target probably crashed.')
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/tab_list_backend_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/tab_list_backend_unittest.py
new file mode 100644
index 0000000..47e3451
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome/tab_list_backend_unittest.py
@@ -0,0 +1,51 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.core import exceptions
+from telemetry import decorators
+from telemetry.testing import tab_test_case
+
+
+class TabListBackendTest(tab_test_case.TabTestCase):
+  @decorators.Enabled('has tabs')
+  def testNewTab(self):
+    tabs = set(tab.id for tab in self.tabs)
+    for _ in xrange(10):
+      new_tab_id = self.tabs.New().id
+      self.assertNotIn(new_tab_id, tabs)
+      tabs.add(new_tab_id)
+      new_tabs = set(tab.id for tab in self.tabs)
+      self.assertEqual(tabs, new_tabs)
+
+  @decorators.Enabled('has tabs')
+  def testTabIdMatchesContextId(self):
+    # Ensure that there are two tabs.
+    while len(self.tabs) < 2:
+      self.tabs.New()
+
+    # Check that the tab.id matches context_id.
+    tabs = []
+    for context_id in self.tabs._tab_list_backend.IterContextIds():
+      tab = self.tabs.GetTabById(context_id)
+      self.assertEquals(tab.id, context_id)
+      tabs.append(self.tabs.GetTabById(context_id))
+
+  # https://github.com/catapult-project/catapult/issues/3099 (Android)
+  @decorators.Enabled('has tabs')
+  @decorators.Disabled('android')
+  def testTabIdStableAfterTabCrash(self):
+    # Ensure that there are two tabs.
+    while len(self.tabs) < 2:
+      self.tabs.New()
+
+    tabs = [t for t in self.tabs]
+
+    # Crash the first tab.
+    self.assertRaises(exceptions.DevtoolsTargetCrashException,
+        lambda: tabs[0].Navigate('chrome://crash'))
+
+    # Fetching the second tab by id should still work. Fetching the first tab
+    # should raise an exception.
+    self.assertEquals(tabs[1], self.tabs.GetTabById(tabs[1].id))
+    self.assertRaises(KeyError, lambda: self.tabs.GetTabById(tabs[0].id))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/__init__.py
new file mode 100644
index 0000000..83c375c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/__init__.py
@@ -0,0 +1,6 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""This package contains classes and methods for controlling the chrome
+devtool.
+"""
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/devtools_client_backend.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/devtools_client_backend.py
new file mode 100644
index 0000000..a10ceb5
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/devtools_client_backend.py
@@ -0,0 +1,491 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+import re
+import socket
+import sys
+
+from telemetry.core import exceptions
+from telemetry import decorators
+from telemetry.internal.backends import browser_backend
+from telemetry.internal.backends.chrome_inspector import devtools_http
+from telemetry.internal.backends.chrome_inspector import inspector_backend
+from telemetry.internal.backends.chrome_inspector import inspector_websocket
+from telemetry.internal.backends.chrome_inspector import memory_backend
+from telemetry.internal.backends.chrome_inspector import tracing_backend
+from telemetry.internal.backends.chrome_inspector import websocket
+from telemetry.internal.platform.tracing_agent import chrome_tracing_agent
+from telemetry.internal.platform.tracing_agent import (
+    chrome_tracing_devtools_manager)
+from tracing.trace_data import trace_data as trace_data_module
+
+
+BROWSER_INSPECTOR_WEBSOCKET_URL = 'ws://127.0.0.1:%i/devtools/browser'
+
+
+class TabNotFoundError(exceptions.Error):
+  pass
+
+
+def IsDevToolsAgentAvailable(port, app_backend):
+  """Returns True if a DevTools agent is available on the given port."""
+  if (isinstance(app_backend, browser_backend.BrowserBackend) and
+      app_backend.supports_tracing):
+    inspector_websocket_instance = inspector_websocket.InspectorWebsocket()
+    try:
+      if not _IsInspectorWebsocketAvailable(inspector_websocket_instance, port):
+        return False
+    finally:
+      inspector_websocket_instance.Disconnect()
+
+  devtools_http_instance = devtools_http.DevToolsHttp(port)
+  try:
+    return _IsDevToolsAgentAvailable(devtools_http_instance)
+  finally:
+    devtools_http_instance.Disconnect()
+
+
+def _IsInspectorWebsocketAvailable(inspector_websocket_instance, port):
+  try:
+    inspector_websocket_instance.Connect(
+        BROWSER_INSPECTOR_WEBSOCKET_URL % port, timeout=10)
+  except websocket.WebSocketException:
+    return False
+  except socket.error:
+    return False
+  except Exception as e:
+    sys.stderr.write('Unidentified exception while checking if wesocket is'
+                     'available on port %i. Exception message: %s\n' %
+                     (port, e.message))
+    return False
+  else:
+    return True
+
+
+# TODO(nednguyen): Find a more reliable way to check whether the devtool agent
+# is still alive.
+def _IsDevToolsAgentAvailable(devtools_http_instance):
+  try:
+    devtools_http_instance.Request('')
+  except devtools_http.DevToolsClientConnectionError:
+    return False
+  else:
+    return True
+
+
+class DevToolsClientBackend(object):
+  """An object that communicates with Chrome's devtools.
+
+  This class owns a map of InspectorBackends. It is responsible for creating
+  them and destroying them.
+  """
+  def __init__(self, devtools_port, remote_devtools_port, app_backend):
+    """Creates a new DevToolsClientBackend.
+
+    A DevTools agent must exist on the given devtools_port.
+
+    Args:
+      devtools_port: The port to use to connect to DevTools agent.
+      remote_devtools_port: In some cases (e.g., app running on
+          Android device, devtools_port is the forwarded port on the
+          host platform. We also need to know the remote_devtools_port
+          so that we can uniquely identify the DevTools agent.
+      app_backend: For the app that contains the DevTools agent.
+    """
+    self._devtools_port = devtools_port
+    self._remote_devtools_port = remote_devtools_port
+    self._devtools_http = devtools_http.DevToolsHttp(devtools_port)
+    self._browser_inspector_websocket = None
+    self._tracing_backend = None
+    self._memory_backend = None
+    self._app_backend = app_backend
+    self._devtools_context_map_backend = _DevToolsContextMapBackend(
+        self._app_backend, self)
+
+    self._tab_ids = None
+
+    if not self.supports_tracing:
+      return
+    chrome_tracing_devtools_manager.RegisterDevToolsClient(
+        self, self._app_backend.platform_backend)
+
+    # Telemetry has started Chrome tracing if there is trace config, so start
+    # tracing on this newly created devtools client if needed.
+    trace_config = (self._app_backend.platform_backend
+                    .tracing_controller_backend.GetChromeTraceConfig())
+    if not trace_config:
+      self._CreateTracingBackendIfNeeded(is_tracing_running=False)
+      return
+
+    if self.support_startup_tracing:
+      self._CreateTracingBackendIfNeeded(is_tracing_running=True)
+      return
+
+    self._CreateTracingBackendIfNeeded(is_tracing_running=False)
+    self.StartChromeTracing(trace_config)
+
+  @property
+  def remote_port(self):
+    return self._remote_devtools_port
+
+  @property
+  def supports_tracing(self):
+    if not isinstance(self._app_backend, browser_backend.BrowserBackend):
+      return False
+    return self._app_backend.supports_tracing
+
+  @property
+  def supports_overriding_memory_pressure_notifications(self):
+    if not isinstance(self._app_backend, browser_backend.BrowserBackend):
+      return False
+    return self._app_backend.supports_overriding_memory_pressure_notifications
+
+
+  @property
+  def is_tracing_running(self):
+    if not self.supports_tracing:
+      return False
+    if not self._tracing_backend:
+      return False
+    return self._tracing_backend.is_tracing_running
+
+  @property
+  def support_startup_tracing(self):
+    # Startup tracing with --trace-config-file flag was not supported until
+    # Chromium branch number 2512 (see crrev.com/1309243004 and
+    # crrev.com/1353583002).
+    if not chrome_tracing_agent.ChromeTracingAgent.IsStartupTracingSupported(
+        self._app_backend.platform_backend):
+      return False
+    # TODO(zhenw): Remove this once stable Chrome and reference browser have
+    # passed 2512.
+    return self.GetChromeBranchNumber() >= 2512
+
+  @property
+  def support_modern_devtools_tracing_start_api(self):
+    # Modern DevTools Tracing.start API (via 'traceConfig' parameter) was not
+    # supported until Chromium branch number 2683 (see crrev.com/1808353002).
+    # TODO(petrcermak): Remove this once stable Chrome and reference browser
+    # have passed 2683.
+    return self.GetChromeBranchNumber() >= 2683
+
+  def IsAlive(self):
+    """Whether the DevTools server is available and connectable."""
+    return (self._devtools_http and
+        _IsDevToolsAgentAvailable(self._devtools_http))
+
+  def Close(self):
+    if self._tracing_backend:
+      self._tracing_backend.Close()
+      self._tracing_backend = None
+    if self._memory_backend:
+      self._memory_backend.Close()
+      self._memory_backend = None
+
+    if self._devtools_context_map_backend:
+      self._devtools_context_map_backend.Clear()
+
+    # Close the browser inspector socket last (in case the backend needs to
+    # interact with it before closing).
+    if self._browser_inspector_websocket:
+      self._browser_inspector_websocket.Disconnect()
+      self._browser_inspector_websocket = None
+
+    assert self._devtools_http
+    self._devtools_http.Disconnect()
+    self._devtools_http = None
+
+
+  @decorators.Cache
+  def GetChromeBranchNumber(self):
+    # Detect version information.
+    resp = self._devtools_http.RequestJson('version')
+    if 'Protocol-Version' in resp:
+      if 'Browser' in resp:
+        branch_number_match = re.search(r'Chrome/\d+\.\d+\.(\d+)\.\d+',
+                                        resp['Browser'])
+      else:
+        branch_number_match = re.search(
+            r'Chrome/\d+\.\d+\.(\d+)\.\d+ (Mobile )?Safari',
+            resp['User-Agent'])
+
+      if branch_number_match:
+        branch_number = int(branch_number_match.group(1))
+        if branch_number:
+          return branch_number
+
+    # Branch number can't be determined, so fail any branch number checks.
+    return 0
+
+  def _ListInspectableContexts(self):
+    return self._devtools_http.RequestJson('')
+
+  def RequestNewTab(self, timeout):
+    """Creates a new tab.
+
+    Returns:
+      A JSON string as returned by DevTools. Example:
+      {
+        "description": "",
+        "devtoolsFrontendUrl":
+            "/devtools/inspector.html?ws=host:port/devtools/page/id-string",
+        "id": "id-string",
+        "title": "Page Title",
+        "type": "page",
+        "url": "url",
+        "webSocketDebuggerUrl": "ws://host:port/devtools/page/id-string"
+      }
+
+    Raises:
+      devtools_http.DevToolsClientConnectionError
+    """
+    return self._devtools_http.Request('new', timeout=timeout)
+
+  def CloseTab(self, tab_id, timeout):
+    """Closes the tab with the given id.
+
+    Raises:
+      devtools_http.DevToolsClientConnectionError
+      TabNotFoundError
+    """
+    try:
+      return self._devtools_http.Request('close/%s' % tab_id,
+                                         timeout=timeout)
+    except devtools_http.DevToolsClientUrlError:
+      error = TabNotFoundError(
+          'Unable to close tab, tab id not found: %s' % tab_id)
+      raise error, None, sys.exc_info()[2]
+
+  def ActivateTab(self, tab_id, timeout):
+    """Activates the tab with the given id.
+
+    Raises:
+      devtools_http.DevToolsClientConnectionError
+      TabNotFoundError
+    """
+    try:
+      return self._devtools_http.Request('activate/%s' % tab_id,
+                                         timeout=timeout)
+    except devtools_http.DevToolsClientUrlError:
+      error = TabNotFoundError(
+          'Unable to activate tab, tab id not found: %s' % tab_id)
+      raise error, None, sys.exc_info()[2]
+
+  def GetUrl(self, tab_id):
+    """Returns the URL of the tab with |tab_id|, as reported by devtools.
+
+    Raises:
+      devtools_http.DevToolsClientConnectionError
+    """
+    for c in self._ListInspectableContexts():
+      if c['id'] == tab_id:
+        return c['url']
+    return None
+
+  def IsInspectable(self, tab_id):
+    """Whether the tab with |tab_id| is inspectable, as reported by devtools.
+
+    Raises:
+      devtools_http.DevToolsClientConnectionError
+    """
+    contexts = self._ListInspectableContexts()
+    return tab_id in [c['id'] for c in contexts]
+
+  def GetUpdatedInspectableContexts(self):
+    """Returns an updated instance of _DevToolsContextMapBackend."""
+    contexts = self._ListInspectableContexts()
+    self._devtools_context_map_backend._Update(contexts)
+    return self._devtools_context_map_backend
+
+  def _CreateTracingBackendIfNeeded(self, is_tracing_running=False):
+    assert self.supports_tracing
+    if not self._tracing_backend:
+      self._CreateAndConnectBrowserInspectorWebsocketIfNeeded()
+      self._tracing_backend = tracing_backend.TracingBackend(
+          self._browser_inspector_websocket, is_tracing_running,
+          self.support_modern_devtools_tracing_start_api)
+
+  def _CreateMemoryBackendIfNeeded(self):
+    assert self.supports_overriding_memory_pressure_notifications
+    if not self._memory_backend:
+      self._CreateAndConnectBrowserInspectorWebsocketIfNeeded()
+      self._memory_backend = memory_backend.MemoryBackend(
+          self._browser_inspector_websocket)
+
+  def _CreateAndConnectBrowserInspectorWebsocketIfNeeded(self):
+    if not self._browser_inspector_websocket:
+      self._browser_inspector_websocket = (
+          inspector_websocket.InspectorWebsocket())
+      self._browser_inspector_websocket.Connect(
+          BROWSER_INSPECTOR_WEBSOCKET_URL % self._devtools_port, timeout=10)
+
+  def IsChromeTracingSupported(self):
+    if not self.supports_tracing:
+      return False
+    self._CreateTracingBackendIfNeeded()
+    return self._tracing_backend.IsTracingSupported()
+
+  def StartChromeTracing(self, trace_config, timeout=10):
+    """
+    Args:
+        trace_config: An tracing_config.TracingConfig instance.
+    """
+    assert trace_config and trace_config.enable_chrome_trace
+    self._CreateTracingBackendIfNeeded()
+    return self._tracing_backend.StartTracing(
+        trace_config.chrome_trace_config, timeout)
+
+  def RecordChromeClockSyncMarker(self, sync_id):
+    assert self.is_tracing_running, 'Tracing must be running to clock sync.'
+    self._tracing_backend.RecordClockSyncMarker(sync_id)
+
+  def StopChromeTracing(self):
+    assert self.is_tracing_running
+    self._tab_ids = []
+    try:
+      context_map = self.GetUpdatedInspectableContexts()
+      for context in context_map.contexts:
+        if context['type'] not in ['iframe', 'page', 'webview']:
+          continue
+        context_id = context['id']
+        backend = context_map.GetInspectorBackend(context_id)
+        backend.EvaluateJavaScript("""
+            console.time({{ backend_id }});
+            console.timeEnd({{ backend_id }});
+            console.time.toString().indexOf('[native code]') != -1;
+            """,
+            backend_id=backend.id)
+        self._tab_ids.append(backend.id)
+    finally:
+      self._tracing_backend.StopTracing()
+
+  def CollectChromeTracingData(self, trace_data_builder, timeout=60):
+    try:
+      trace_data_builder.AddTraceFor(
+          trace_data_module.TAB_ID_PART, self._tab_ids[:])
+      self._tab_ids = None
+    finally:
+      self._tracing_backend.CollectTraceData(trace_data_builder, timeout)
+
+  def DumpMemory(self, timeout=30):
+    """Dumps memory.
+
+    Returns:
+      GUID of the generated dump if successful, None otherwise.
+
+    Raises:
+      TracingTimeoutException: If more than |timeout| seconds has passed
+      since the last time any data is received.
+      TracingUnrecoverableException: If there is a websocket error.
+      TracingUnexpectedResponseException: If the response contains an error
+      or does not contain the expected result.
+    """
+    self._CreateTracingBackendIfNeeded()
+    return self._tracing_backend.DumpMemory(timeout)
+
+  def SetMemoryPressureNotificationsSuppressed(self, suppressed, timeout=30):
+    """Enable/disable suppressing memory pressure notifications.
+
+    Args:
+      suppressed: If true, memory pressure notifications will be suppressed.
+      timeout: The timeout in seconds.
+
+    Raises:
+      MemoryTimeoutException: If more than |timeout| seconds has passed
+      since the last time any data is received.
+      MemoryUnrecoverableException: If there is a websocket error.
+      MemoryUnexpectedResponseException: If the response contains an error
+      or does not contain the expected result.
+    """
+    self._CreateMemoryBackendIfNeeded()
+    return self._memory_backend.SetMemoryPressureNotificationsSuppressed(
+        suppressed, timeout)
+
+  def SimulateMemoryPressureNotification(self, pressure_level, timeout=30):
+    """Simulate a memory pressure notification.
+
+    Args:
+      pressure level: The memory pressure level of the notification ('moderate'
+      or 'critical').
+      timeout: The timeout in seconds.
+
+    Raises:
+      MemoryTimeoutException: If more than |timeout| seconds has passed
+      since the last time any data is received.
+      MemoryUnrecoverableException: If there is a websocket error.
+      MemoryUnexpectedResponseException: If the response contains an error
+      or does not contain the expected result.
+    """
+    self._CreateMemoryBackendIfNeeded()
+    return self._memory_backend.SimulateMemoryPressureNotification(
+        pressure_level, timeout)
+
+
+class _DevToolsContextMapBackend(object):
+  def __init__(self, app_backend, devtools_client):
+    self._app_backend = app_backend
+    self._devtools_client = devtools_client
+    self._contexts = None
+    self._inspector_backends_dict = {}
+
+  @property
+  def contexts(self):
+    """The most up to date contexts data.
+
+    Returned in the order returned by devtools agent."""
+    return self._contexts
+
+  def GetContextInfo(self, context_id):
+    for context in self._contexts:
+      if context['id'] == context_id:
+        return context
+    raise KeyError('Cannot find a context with id=%s' % context_id)
+
+  def GetInspectorBackend(self, context_id):
+    """Gets an InspectorBackend instance for the given context_id.
+
+    This lazily creates InspectorBackend for the context_id if it does
+    not exist yet. Otherwise, it will return the cached instance."""
+    if context_id in self._inspector_backends_dict:
+      return self._inspector_backends_dict[context_id]
+
+    for context in self._contexts:
+      if context['id'] == context_id:
+        new_backend = inspector_backend.InspectorBackend(
+            self._app_backend.app, self._devtools_client, context)
+        self._inspector_backends_dict[context_id] = new_backend
+        return new_backend
+
+    raise KeyError('Cannot find a context with id=%s' % context_id)
+
+  def _Update(self, contexts):
+    # Remove InspectorBackend that is not in the current inspectable
+    # contexts list.
+    context_ids = [context['id'] for context in contexts]
+    for context_id in self._inspector_backends_dict.keys():
+      if context_id not in context_ids:
+        backend = self._inspector_backends_dict[context_id]
+        backend.Disconnect()
+        del self._inspector_backends_dict[context_id]
+
+    valid_contexts = []
+    for context in contexts:
+      # If the context does not have webSocketDebuggerUrl, skip it.
+      # If an InspectorBackend is already created for the tab,
+      # webSocketDebuggerUrl will be missing, and this is expected.
+      context_id = context['id']
+      if context_id not in self._inspector_backends_dict:
+        if 'webSocketDebuggerUrl' not in context:
+          logging.debug('webSocketDebuggerUrl missing, removing %s'
+                        % context_id)
+          continue
+      valid_contexts.append(context)
+    self._contexts = valid_contexts
+
+  def Clear(self):
+    for backend in self._inspector_backends_dict.values():
+      backend.Disconnect()
+    self._inspector_backends_dict = {}
+    self._contexts = None
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/devtools_client_backend_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/devtools_client_backend_unittest.py
new file mode 100644
index 0000000..085956a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/devtools_client_backend_unittest.py
@@ -0,0 +1,97 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry import decorators
+from telemetry.testing import browser_test_case
+from telemetry.timeline import model
+from telemetry.timeline import tracing_config
+from tracing.trace_data import trace_data
+
+
+class DevToolsClientBackendTest(browser_test_case.BrowserTestCase):
+  @property
+  def _browser_backend(self):
+    return self._browser._browser_backend
+
+  @property
+  def _devtools_client(self):
+    return self._browser_backend.devtools_client
+
+  def testGetChromeBranchNumber(self):
+    branch_num = self._devtools_client.GetChromeBranchNumber()
+    self.assertIsInstance(branch_num, int)
+    self.assertGreater(branch_num, 0)
+
+  def testIsAlive(self):
+    self.assertTrue(self._devtools_client.IsAlive())
+
+  # https://github.com/catapult-project/catapult/issues/3099 (Android)
+  # crbug.com/483212 (CrOS)
+  @decorators.Enabled('has tabs')
+  @decorators.Disabled('android', 'chromeos')
+  def testGetUpdatedInspectableContexts(self):
+    self._browser.tabs.New()
+    c1 = self._devtools_client.GetUpdatedInspectableContexts()
+    self.assertEqual(len(c1.contexts), 2)
+    backends1 = [c1.GetInspectorBackend(c['id']) for c in c1.contexts]
+    tabs1 = list(self._browser.tabs)
+
+    c2 = self._devtools_client.GetUpdatedInspectableContexts()
+    self.assertEqual(len(c2.contexts), 2)
+    backends2 = [c2.GetInspectorBackend(c['id']) for c in c2.contexts]
+    tabs2 = list(self._browser.tabs)
+    self.assertEqual(backends2, backends1)
+    self.assertEqual(tabs2, tabs1)
+
+    self._browser.tabs.New()
+    c3 = self._devtools_client.GetUpdatedInspectableContexts()
+    self.assertEqual(len(c3.contexts), 3)
+    backends3 = [c3.GetInspectorBackend(c['id']) for c in c3.contexts]
+    tabs3 = list(self._browser.tabs)
+    self.assertEqual(backends3[1], backends1[0])
+    self.assertEqual(backends3[2], backends1[1])
+    self.assertEqual(tabs3[0], tabs1[0])
+    self.assertEqual(tabs3[1], tabs1[1])
+
+    self._browser.tabs[1].Close()
+    c4 = self._devtools_client.GetUpdatedInspectableContexts()
+    self.assertEqual(len(c4.contexts), 2)
+    backends4 = [c4.GetInspectorBackend(c['id']) for c in c4.contexts]
+    tabs4 = list(self._browser.tabs)
+    self.assertEqual(backends4[0], backends3[0])
+    self.assertEqual(backends4[1], backends3[1])
+    self.assertEqual(tabs4[0], tabs3[0])
+    self.assertEqual(tabs4[1], tabs3[2])
+
+  # https://github.com/catapult-project/catapult/issues/3099 (Android)
+  # crbug.com/483212 (CrOS)
+  @decorators.Disabled('android', 'chromeos')
+  def testGetUpdatedInspectableContextsUpdateContextsData(self):
+    c1 = self._devtools_client.GetUpdatedInspectableContexts()
+    self.assertEqual(len(c1.contexts), 1)
+    self.assertEqual(c1.contexts[0]['url'], 'about:blank')
+
+    context_id = c1.contexts[0]['id']
+    backend = c1.GetInspectorBackend(context_id)
+    backend.Navigate(self.UrlOfUnittestFile('blank.html'), None, 10)
+    c2 = self._devtools_client.GetUpdatedInspectableContexts()
+    self.assertEqual(len(c2.contexts), 1)
+    self.assertTrue('blank.html' in c2.contexts[0]['url'])
+    self.assertEqual(c2.GetInspectorBackend(context_id), backend)
+
+  def testTracing(self):
+    devtools_client = self._devtools_client
+    if not devtools_client.IsChromeTracingSupported():
+      self.skipTest('Browser does not support tracing, skipping test.')
+
+    # Start Chrome tracing.
+    config = tracing_config.TracingConfig()
+    config.enable_chrome_trace = True
+    devtools_client.StartChromeTracing(config)
+
+    # Stop Chrome tracing and check that the resulting data is valid.
+    builder = trace_data.TraceDataBuilder()
+    devtools_client.StopChromeTracing()
+    devtools_client.CollectChromeTracingData(builder)
+    model.TimelineModel(builder.AsData())
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/devtools_http.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/devtools_http.py
new file mode 100644
index 0000000..fecd768
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/devtools_http.py
@@ -0,0 +1,107 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import errno
+import httplib
+import json
+import socket
+import sys
+
+from telemetry.core import exceptions
+
+
+class DevToolsClientConnectionError(exceptions.Error):
+  pass
+
+
+class DevToolsClientUrlError(DevToolsClientConnectionError):
+  pass
+
+
+class DevToolsHttp(object):
+  """A helper class to send and parse DevTools HTTP requests.
+
+  This class maintains a persistent http connection to Chrome devtools.
+  Ideally, owners of instances of this class should call Disconnect() before
+  disposing of the instance. Otherwise, the connection will not be closed until
+  the instance is garbage collected.
+  """
+
+  def __init__(self, devtools_port):
+    self._devtools_port = devtools_port
+    self._conn = None
+
+  def __del__(self):
+    self.Disconnect()
+
+  def _Connect(self, timeout):
+    """Attempts to establish a connection to Chrome devtools."""
+    assert not self._conn
+    try:
+      host_port = '127.0.0.1:%i' % self._devtools_port
+      self._conn = httplib.HTTPConnection(host_port, timeout=timeout)
+    except (socket.error, httplib.HTTPException) as e:
+      raise DevToolsClientConnectionError, (e,), sys.exc_info()[2]
+
+  def Disconnect(self):
+    """Closes the HTTP connection."""
+    if not self._conn:
+      return
+
+    try:
+      self._conn.close()
+    except (socket.error, httplib.HTTPException) as e:
+      raise DevToolsClientConnectionError, (e,), sys.exc_info()[2]
+    finally:
+      self._conn = None
+
+  def Request(self, path, timeout=30):
+    """Sends a request to Chrome devtools.
+
+    This method lazily creates an HTTP connection, if one does not already
+    exist.
+
+    Args:
+      path: The DevTools URL path, without the /json/ prefix.
+      timeout: Timeout defaults to 30 seconds.
+
+    Raises:
+      DevToolsClientConnectionError: If the connection fails.
+    """
+    assert timeout
+
+    if not self._conn:
+      self._Connect(timeout)
+
+    endpoint = '/json'
+    if path:
+      endpoint += '/' + path
+    if self._conn.sock:
+      self._conn.sock.settimeout(timeout)
+    else:
+      self._conn.timeout = timeout
+
+    try:
+      # By default, httplib avoids going through the default system proxy.
+      self._conn.request('GET', endpoint)
+      response = self._conn.getresponse()
+      return response.read()
+    except (socket.error, httplib.HTTPException) as e:
+      self.Disconnect()
+      if isinstance(e, socket.error) and e.errno == errno.ECONNREFUSED:
+        raise DevToolsClientUrlError, (e,), sys.exc_info()[2]
+      raise DevToolsClientConnectionError, (e,), sys.exc_info()[2]
+
+  def RequestJson(self, path, timeout=30):
+    """Sends a request and parse the response as JSON.
+
+    Args:
+      path: The DevTools URL path, without the /json/ prefix.
+      timeout: Timeout defaults to 30 seconds.
+
+    Raises:
+      DevToolsClientConnectionError: If the connection fails.
+      ValueError: If the response is not a valid JSON.
+    """
+    return json.loads(self.Request(path, timeout))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/devtools_http_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/devtools_http_unittest.py
new file mode 100644
index 0000000..ff397ff
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/devtools_http_unittest.py
@@ -0,0 +1,19 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from telemetry.internal.backends.chrome_inspector import devtools_http
+
+
+class DevToolsHttpTest(unittest.TestCase):
+
+  def testUrlError(self):
+    with self.assertRaises(devtools_http.DevToolsClientUrlError):
+      devtools_http.DevToolsHttp(1000).Request('')
+
+  def testSocketError(self):
+    with self.assertRaises(devtools_http.DevToolsClientConnectionError) as e:
+      devtools_http.DevToolsHttp(1000).Request('')
+      self.assertnotisinstance(e, devtools_http.devtoolsclienturlerror)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/inspector_backend.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/inspector_backend.py
new file mode 100644
index 0000000..821163d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/inspector_backend.py
@@ -0,0 +1,508 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import functools
+import logging
+import os
+import socket
+import sys
+
+from py_trace_event import trace_event
+
+from telemetry.core import exceptions
+from telemetry import decorators
+from telemetry.internal.backends.chrome_inspector import devtools_http
+from telemetry.internal.backends.chrome_inspector import inspector_console
+from telemetry.internal.backends.chrome_inspector import inspector_memory
+from telemetry.internal.backends.chrome_inspector import inspector_page
+from telemetry.internal.backends.chrome_inspector import inspector_runtime
+from telemetry.internal.backends.chrome_inspector import inspector_websocket
+from telemetry.internal.backends.chrome_inspector import websocket
+from telemetry.util import js_template
+
+import py_utils
+
+
+def _HandleInspectorWebSocketExceptions(func):
+  """Decorator for converting inspector_websocket exceptions.
+
+  When an inspector_websocket exception is thrown in the original function,
+  this decorator converts it into a telemetry exception and adds debugging
+  information.
+  """
+  @functools.wraps(func)
+  def inner(inspector_backend, *args, **kwargs):
+    try:
+      return func(inspector_backend, *args, **kwargs)
+    except (socket.error, websocket.WebSocketException,
+            inspector_websocket.WebSocketDisconnected) as e:
+      inspector_backend._ConvertExceptionFromInspectorWebsocket(e)
+
+  return inner
+
+
+class InspectorBackend(object):
+  """Class for communicating with a devtools client.
+
+  The owner of an instance of this class is responsible for calling
+  Disconnect() before disposing of the instance.
+  """
+
+  __metaclass__ = trace_event.TracedMetaClass
+
+  def __init__(self, app, devtools_client, context, timeout=120):
+    self._websocket = inspector_websocket.InspectorWebsocket()
+    self._websocket.RegisterDomain(
+        'Inspector', self._HandleInspectorDomainNotification)
+
+    self._app = app
+    self._devtools_client = devtools_client
+    # Be careful when using the context object, since the data may be
+    # outdated since this is never updated once InspectorBackend is
+    # created. Consider an updating strategy for this. (For an example
+    # of the subtlety, see the logic for self.url property.)
+    self._context = context
+
+    logging.debug('InspectorBackend._Connect() to %s', self.debugger_url)
+    try:
+      self._websocket.Connect(self.debugger_url, timeout)
+      self._console = inspector_console.InspectorConsole(self._websocket)
+      self._memory = inspector_memory.InspectorMemory(self._websocket)
+      self._page = inspector_page.InspectorPage(
+          self._websocket, timeout=timeout)
+      self._runtime = inspector_runtime.InspectorRuntime(self._websocket)
+    except (websocket.WebSocketException, exceptions.TimeoutException,
+            py_utils.TimeoutException) as e:
+      self._ConvertExceptionFromInspectorWebsocket(e)
+
+  def Disconnect(self):
+    """Disconnects the inspector websocket.
+
+    This method intentionally leaves the self._websocket object around, so that
+    future calls it to it will fail with a relevant error.
+    """
+    if self._websocket:
+      self._websocket.Disconnect()
+
+  def __del__(self):
+    self.Disconnect()
+
+  @property
+  def app(self):
+    return self._app
+
+  @property
+  def url(self):
+    """Returns the URL of the tab, as reported by devtools.
+
+    Raises:
+      devtools_http.DevToolsClientConnectionError
+    """
+    return self._devtools_client.GetUrl(self.id)
+
+  @property
+  def id(self):
+    return self._context['id']
+
+  @property
+  def debugger_url(self):
+    return self._context['webSocketDebuggerUrl']
+
+  def GetWebviewInspectorBackends(self):
+    """Returns a list of InspectorBackend instances associated with webviews.
+
+    Raises:
+      devtools_http.DevToolsClientConnectionError
+    """
+    inspector_backends = []
+    devtools_context_map = self._devtools_client.GetUpdatedInspectableContexts()
+    for context in devtools_context_map.contexts:
+      if context['type'] == 'webview':
+        inspector_backends.append(
+            devtools_context_map.GetInspectorBackend(context['id']))
+    return inspector_backends
+
+  def IsInspectable(self):
+    """Whether the tab is inspectable, as reported by devtools."""
+    try:
+      return self._devtools_client.IsInspectable(self.id)
+    except devtools_http.DevToolsClientConnectionError:
+      return False
+
+  # Public methods implemented in JavaScript.
+
+  @property
+  @decorators.Cache
+  def screenshot_supported(self):
+    if (self.app.platform.GetOSName() == 'linux' and (
+        os.getenv('DISPLAY') not in [':0', ':0.0'])):
+      # Displays other than 0 mean we are likely running in something like
+      # xvfb where screenshotting doesn't work.
+      return False
+    return True
+
+  @_HandleInspectorWebSocketExceptions
+  def Screenshot(self, timeout):
+    assert self.screenshot_supported, 'Browser does not support screenshotting'
+    return self._page.CaptureScreenshot(timeout)
+
+  # Memory public methods.
+
+  @_HandleInspectorWebSocketExceptions
+  def GetDOMStats(self, timeout):
+    """Gets memory stats from the DOM.
+
+    Raises:
+      inspector_memory.InspectorMemoryException
+      exceptions.TimeoutException
+      exceptions.DevtoolsTargetCrashException
+    """
+    dom_counters = self._memory.GetDOMCounters(timeout)
+    return {
+      'document_count': dom_counters['documents'],
+      'node_count': dom_counters['nodes'],
+      'event_listener_count': dom_counters['jsEventListeners']
+    }
+
+  # Page public methods.
+
+  @_HandleInspectorWebSocketExceptions
+  def WaitForNavigate(self, timeout):
+    self._page.WaitForNavigate(timeout)
+
+  @_HandleInspectorWebSocketExceptions
+  def Navigate(self, url, script_to_evaluate_on_commit, timeout):
+    self._page.Navigate(url, script_to_evaluate_on_commit, timeout)
+
+  @_HandleInspectorWebSocketExceptions
+  def GetCookieByName(self, name, timeout):
+    return self._page.GetCookieByName(name, timeout)
+
+  # Console public methods.
+
+  @_HandleInspectorWebSocketExceptions
+  def GetCurrentConsoleOutputBuffer(self, timeout=10):
+    return self._console.GetCurrentConsoleOutputBuffer(timeout)
+
+  # Runtime public methods.
+
+  @_HandleInspectorWebSocketExceptions
+  def ExecuteJavaScript(self, statement, **kwargs):
+    """Executes a given JavaScript statement. Does not return the result.
+
+    Example: runner.ExecuteJavaScript('var foo = {{ value }};', value='hi');
+
+    Args:
+      statement: The statement to execute (provided as a string).
+
+    Optional keyword args:
+      timeout: The number of seconds to wait for the statement to execute.
+      context_id: The id of an iframe where to execute the code; the main page
+          has context_id=1, the first iframe context_id=2, etc.
+      Additional keyword arguments provide values to be interpolated within
+          the statement. See telemetry.util.js_template for details.
+
+    Raises:
+      py_utils.TimeoutException
+      exceptions.EvaluationException
+      exceptions.WebSocketException
+      exceptions.DevtoolsTargetCrashException
+    """
+    # Use the default both when timeout=None or the option is ommited.
+    timeout = kwargs.pop('timeout', None) or 60
+    context_id = kwargs.pop('context_id', None)
+    statement = js_template.Render(statement, **kwargs)
+    self._runtime.Execute(statement, context_id, timeout)
+
+  @_HandleInspectorWebSocketExceptions
+  def EvaluateJavaScript(self, expression, **kwargs):
+    """Returns the result of evaluating a given JavaScript expression.
+
+    Example: runner.ExecuteJavaScript('document.location.href');
+
+    Args:
+      expression: The expression to execute (provided as a string).
+
+    Optional keyword args:
+      timeout: The number of seconds to wait for the expression to evaluate.
+      context_id: The id of an iframe where to execute the code; the main page
+          has context_id=1, the first iframe context_id=2, etc.
+      Additional keyword arguments provide values to be interpolated within
+          the expression. See telemetry.util.js_template for details.
+
+    Raises:
+      py_utils.TimeoutException
+      exceptions.EvaluationException
+      exceptions.WebSocketException
+      exceptions.DevtoolsTargetCrashException
+    """
+    # Use the default both when timeout=None or the option is ommited.
+    timeout = kwargs.pop('timeout', None) or 60
+    context_id = kwargs.pop('context_id', None)
+    expression = js_template.Render(expression, **kwargs)
+    return self._runtime.Evaluate(expression, context_id, timeout)
+
+  def WaitForJavaScriptCondition(self, condition, **kwargs):
+    """Wait for a JavaScript condition to become truthy.
+
+    Example: runner.WaitForJavaScriptCondition('window.foo == 10');
+
+    Args:
+      condition: The JavaScript condition (provided as string).
+
+    Optional keyword args:
+      timeout: The number in seconds to wait for the condition to become
+          True (default to 60).
+      context_id: The id of an iframe where to execute the code; the main page
+          has context_id=1, the first iframe context_id=2, etc.
+      Additional keyword arguments provide values to be interpolated within
+          the expression. See telemetry.util.js_template for details.
+
+    Returns:
+      The value returned by the JavaScript condition that got interpreted as
+      true.
+
+    Raises:
+      py_utils.TimeoutException
+      exceptions.EvaluationException
+      exceptions.WebSocketException
+      exceptions.DevtoolsTargetCrashException
+    """
+    # Use the default both when timeout=None or the option is ommited.
+    timeout = kwargs.pop('timeout', None) or 60
+    context_id = kwargs.pop('context_id', None)
+    condition = js_template.Render(condition, **kwargs)
+
+    def IsJavaScriptExpressionTrue():
+      return self._runtime.Evaluate(condition, context_id, timeout)
+
+    try:
+      return py_utils.WaitFor(IsJavaScriptExpressionTrue, timeout)
+    except py_utils.TimeoutException as e:
+      # Try to make timeouts a little more actionable by dumping console output.
+      debug_message = None
+      try:
+        debug_message = (
+            'Console output:\n%s' %
+            self.GetCurrentConsoleOutputBuffer())
+      except Exception as e:
+        debug_message = (
+            'Exception thrown when trying to capture console output: %s' %
+            repr(e))
+      raise py_utils.TimeoutException(
+          e.message + '\n' + debug_message)
+
+  @_HandleInspectorWebSocketExceptions
+  def EnableAllContexts(self):
+    """Allows access to iframes.
+
+    Raises:
+      exceptions.WebSocketDisconnected
+      exceptions.TimeoutException
+      exceptions.DevtoolsTargetCrashException
+    """
+    return self._runtime.EnableAllContexts()
+
+  @_HandleInspectorWebSocketExceptions
+  def SynthesizeScrollGesture(self, x=100, y=800, xDistance=0, yDistance=-500,
+                              xOverscroll=None, yOverscroll=None,
+                              preventFling=None, speed=None,
+                              gestureSourceType=None, repeatCount=None,
+                              repeatDelayMs=None, interactionMarkerName=None,
+                              timeout=60):
+    """Runs an inspector command that causes a repeatable browser driven scroll.
+
+    Args:
+      x: X coordinate of the start of the gesture in CSS pixels.
+      y: Y coordinate of the start of the gesture in CSS pixels.
+      xDistance: Distance to scroll along the X axis (positive to scroll left).
+      yDistance: Distance to scroll along the Y axis (positive to scroll up).
+      xOverscroll: Number of additional pixels to scroll back along the X axis.
+      xOverscroll: Number of additional pixels to scroll back along the Y axis.
+      preventFling: Prevents a fling gesture.
+      speed: Swipe speed in pixels per second.
+      gestureSourceType: Which type of input events to be generated.
+      repeatCount: Number of additional repeats beyond the first scroll.
+      repeatDelayMs: Number of milliseconds delay between each repeat.
+      interactionMarkerName: The name of the interaction markers to generate.
+
+    Raises:
+      exceptions.TimeoutException
+      exceptions.DevtoolsTargetCrashException
+    """
+    params = {
+        'x': x,
+        'y': y,
+        'xDistance': xDistance,
+        'yDistance': yDistance
+    }
+
+    if preventFling is not None:
+      params['preventFling'] = preventFling
+
+    if xOverscroll is not None:
+      params['xOverscroll'] = xOverscroll
+
+    if yOverscroll is not None:
+      params['yOverscroll'] = yOverscroll
+
+    if speed is not None:
+      params['speed'] = speed
+
+    if repeatCount is not None:
+      params['repeatCount'] = repeatCount
+
+    if gestureSourceType is not None:
+      params['gestureSourceType'] = gestureSourceType
+
+    if repeatDelayMs is not None:
+      params['repeatDelayMs'] = repeatDelayMs
+
+    if interactionMarkerName is not None:
+      params['interactionMarkerName'] = interactionMarkerName
+
+    scroll_command = {
+      'method': 'Input.synthesizeScrollGesture',
+      'params': params
+    }
+    return self._runtime.RunInspectorCommand(scroll_command, timeout)
+
+  @_HandleInspectorWebSocketExceptions
+  def DispatchKeyEvent(self, keyEventType='char', modifiers=None,
+                       timestamp=None, text=None, unmodifiedText=None,
+                       keyIdentifier=None, domCode=None, domKey=None,
+                       windowsVirtualKeyCode=None, nativeVirtualKeyCode=None,
+                       autoRepeat=None, isKeypad=None, isSystemKey=None,
+                       timeout=60):
+    """Dispatches a key event to the page.
+
+    Args:
+      type: Type of the key event. Allowed values: 'keyDown', 'keyUp',
+          'rawKeyDown', 'char'.
+      modifiers: Bit field representing pressed modifier keys. Alt=1, Ctrl=2,
+          Meta/Command=4, Shift=8 (default: 0).
+      timestamp: Time at which the event occurred. Measured in UTC time in
+          seconds since January 1, 1970 (default: current time).
+      text: Text as generated by processing a virtual key code with a keyboard
+          layout. Not needed for for keyUp and rawKeyDown events (default: '').
+      unmodifiedText: Text that would have been generated by the keyboard if no
+          modifiers were pressed (except for shift). Useful for shortcut
+          (accelerator) key handling (default: "").
+      keyIdentifier: Unique key identifier (e.g., 'U+0041') (default: '').
+      windowsVirtualKeyCode: Windows virtual key code (default: 0).
+      nativeVirtualKeyCode: Native virtual key code (default: 0).
+      autoRepeat: Whether the event was generated from auto repeat (default:
+          False).
+      isKeypad: Whether the event was generated from the keypad (default:
+          False).
+      isSystemKey: Whether the event was a system key event (default: False).
+
+    Raises:
+      exceptions.TimeoutException
+      exceptions.DevtoolsTargetCrashException
+    """
+    params = {
+      'type': keyEventType,
+    }
+
+    if modifiers is not None:
+      params['modifiers'] = modifiers
+    if timestamp is not None:
+      params['timestamp'] = timestamp
+    if text is not None:
+      params['text'] = text
+    if unmodifiedText is not None:
+      params['unmodifiedText'] = unmodifiedText
+    if keyIdentifier is not None:
+      params['keyIdentifier'] = keyIdentifier
+    if domCode is not None:
+      params['code'] = domCode
+    if domKey is not None:
+      params['key'] = domKey
+    if windowsVirtualKeyCode is not None:
+      params['windowsVirtualKeyCode'] = windowsVirtualKeyCode
+    if nativeVirtualKeyCode is not None:
+      params['nativeVirtualKeyCode'] = nativeVirtualKeyCode
+    if autoRepeat is not None:
+      params['autoRepeat'] = autoRepeat
+    if isKeypad is not None:
+      params['isKeypad'] = isKeypad
+    if isSystemKey is not None:
+      params['isSystemKey'] = isSystemKey
+
+    key_command = {
+      'method': 'Input.dispatchKeyEvent',
+      'params': params
+    }
+    return self._runtime.RunInspectorCommand(key_command, timeout)
+
+  # Methods used internally by other backends.
+
+  def _HandleInspectorDomainNotification(self, res):
+    if (res['method'] == 'Inspector.detached' and
+        res.get('params', {}).get('reason', '') == 'replaced_with_devtools'):
+      self._WaitForInspectorToGoAway()
+      return
+    if res['method'] == 'Inspector.targetCrashed':
+      exception = exceptions.DevtoolsTargetCrashException(self.app)
+      self._AddDebuggingInformation(exception)
+      raise exception
+
+  def _WaitForInspectorToGoAway(self):
+    self._websocket.Disconnect()
+    raw_input('The connection to Chrome was lost to the inspector ui.\n'
+              'Please close the inspector and press enter to resume '
+              'Telemetry run...')
+    raise exceptions.DevtoolsTargetCrashException(
+        self.app, 'Devtool connection with the browser was interrupted due to '
+        'the opening of an inspector.')
+
+  def _ConvertExceptionFromInspectorWebsocket(self, error):
+    """Converts an Exception from inspector_websocket.
+
+    This method always raises a Telemetry exception. It appends debugging
+    information. The exact exception raised depends on |error|.
+
+    Args:
+      error: An instance of socket.error or websocket.WebSocketException.
+    Raises:
+      exceptions.TimeoutException: A timeout occurred.
+      exceptions.DevtoolsTargetCrashException: On any other error, the most
+        likely explanation is that the devtool's target crashed.
+    """
+    if isinstance(error, websocket.WebSocketTimeoutException):
+      new_error = exceptions.TimeoutException()
+      new_error.AddDebuggingMessage(exceptions.AppCrashException(
+          self.app, 'The app is probably crashed:\n'))
+    else:
+      new_error = exceptions.DevtoolsTargetCrashException(self.app)
+
+    original_error_msg = 'Original exception:\n' + str(error)
+    new_error.AddDebuggingMessage(original_error_msg)
+    self._AddDebuggingInformation(new_error)
+
+    raise new_error, None, sys.exc_info()[2]
+
+  def _AddDebuggingInformation(self, error):
+    """Adds debugging information to error.
+
+    Args:
+      error: An instance of exceptions.Error.
+    """
+    if self.IsInspectable():
+      msg = (
+          'Received a socket error in the browser connection and the tab '
+          'still exists. The operation probably timed out.'
+      )
+    else:
+      msg = (
+          'Received a socket error in the browser connection and the tab no '
+          'longer exists. The tab probably crashed.'
+      )
+    error.AddDebuggingMessage(msg)
+    error.AddDebuggingMessage('Debugger url: %s' % self.debugger_url)
+
+  @_HandleInspectorWebSocketExceptions
+  def CollectGarbage(self):
+    self._page.CollectGarbage()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/inspector_backend_list.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/inspector_backend_list.py
new file mode 100644
index 0000000..7e1f8fb
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/inspector_backend_list.py
@@ -0,0 +1,124 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import collections
+from telemetry.core import exceptions
+
+
+def DebuggerUrlToId(debugger_url):
+  return debugger_url.split('/')[-1]
+
+
+class InspectorBackendList(collections.Sequence):
+  """A dynamic sequence of active InspectorBackends."""
+
+  def __init__(self, browser_backend):
+    """Constructor.
+
+    Args:
+      browser_backend: The BrowserBackend instance to query for
+          InspectorBackends.
+    """
+    self._browser_backend = browser_backend
+    self._devtools_context_map_backend = None
+    # A list of filtered contexts.
+    self._filtered_context_ids = []
+    # A cache of inspector backends, by context ID.
+    self._wrapper_dict = {}
+
+  @property
+  def _devtools_client(self):
+    return self._browser_backend.devtools_client
+
+  @property
+  def app(self):
+    return self._browser_backend.app
+
+  def GetContextInfo(self, context_id):
+    return self._devtools_context_map_backend.GetContextInfo(context_id)
+
+  def ShouldIncludeContext(self, _):
+    """Override this method to control which contexts are included."""
+    return True
+
+  def CreateWrapper(self, inspector_backend_instance):
+    """Override to return the wrapper API over InspectorBackend.
+
+    The wrapper API is the public interface for InspectorBackend. It
+    may expose whatever methods are desired on top of that backend.
+    """
+    raise NotImplementedError
+
+  # TODO(nednguyen): Remove this method and turn inspector_backend_list API to
+  # dictionary-like API (crbug.com/398467)
+  def __getitem__(self, index):
+    self._Update()
+    if index >= len(self._filtered_context_ids):
+      raise exceptions.DevtoolsTargetCrashException(
+          self.app,
+          'Web content with index %s may have crashed. '
+          'filtered_context_ids = %s' % (
+              index, repr(self._filtered_context_ids)))
+    context_id = self._filtered_context_ids[index]
+    return self.GetBackendFromContextId(context_id)
+
+  def GetTabById(self, identifier):
+    self._Update()
+    return self.GetBackendFromContextId(identifier)
+
+  def GetBackendFromContextId(self, context_id):
+    self._Update()
+    if context_id not in self._wrapper_dict:
+      try:
+        backend = self._devtools_context_map_backend.GetInspectorBackend(
+            context_id)
+      except exceptions.Error as e:
+        self._HandleDevToolsConnectionError(e)
+        raise e
+      # Propagate KeyError from GetInspectorBackend call.
+
+      wrapper = self.CreateWrapper(backend)
+      self._wrapper_dict[context_id] = wrapper
+    return self._wrapper_dict[context_id]
+
+  def IterContextIds(self):
+    self._Update()
+    return iter(self._filtered_context_ids)
+
+  def __iter__(self):
+    self._Update()
+    for context_id in self._filtered_context_ids:
+      yield self.GetTabById(context_id)
+
+  def __len__(self):
+    self._Update()
+    return len(self._filtered_context_ids)
+
+  def _Update(self):
+    backends_map = self._devtools_client.GetUpdatedInspectableContexts()
+    self._devtools_context_map_backend = backends_map
+
+    # Clear context ids that do not appear in the inspectable contexts.
+    context_ids = [context['id'] for context in backends_map.contexts]
+    self._filtered_context_ids = [context_id
+                                  for context_id in self._filtered_context_ids
+                                  if context_id in context_ids]
+    # Add new context ids.
+    for context in backends_map.contexts:
+      if (context['id'] not in self._filtered_context_ids and
+          self.ShouldIncludeContext(context)):
+        self._filtered_context_ids.append(context['id'])
+
+    # Clean up any backends for contexts that have gone away.
+    for context_id in self._wrapper_dict.keys():
+      if context_id not in self._filtered_context_ids:
+        del self._wrapper_dict[context_id]
+
+  def _HandleDevToolsConnectionError(self, error):
+    """Called when handling errors in connecting to the DevTools websocket.
+
+    This can be overwritten by sub-classes to add more debugging information to
+    errors.
+    """
+    pass
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/inspector_console.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/inspector_console.py
new file mode 100644
index 0000000..9657cd1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/inspector_console.py
@@ -0,0 +1,54 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import StringIO
+
+from telemetry.internal.backends.chrome_inspector import websocket
+
+
+class InspectorConsole(object):
+  def __init__(self, inspector_websocket):
+    self._inspector_websocket = inspector_websocket
+    self._inspector_websocket.RegisterDomain('Console', self._OnNotification)
+    self._message_output_stream = None
+    self._last_message = None
+    self._console_enabled = False
+
+  def _OnNotification(self, msg):
+    if msg['method'] == 'Console.messageAdded':
+      assert self._message_output_stream
+      if msg['params']['message']['url'] == 'chrome://newtab/':
+        return
+      self._last_message = '(%s) %s:%i: %s' % (
+        msg['params']['message']['level'],
+        msg['params']['message']['url'],
+        msg['params']['message']['line'],
+        msg['params']['message']['text'])
+      self._message_output_stream.write(
+        '%s\n' % self._last_message)
+
+    elif msg['method'] == 'Console.messageRepeatCountUpdated':
+      if self._message_output_stream:
+        self._message_output_stream.write(
+          '%s\n' % self._last_message)
+
+  def GetCurrentConsoleOutputBuffer(self, timeout=10):
+    self._message_output_stream = StringIO.StringIO()
+    self._EnableConsoleOutputStream(timeout)
+    try:
+      self._inspector_websocket.DispatchNotifications(timeout)
+      return self._message_output_stream.getvalue()
+    except websocket.WebSocketTimeoutException:
+      return self._message_output_stream.getvalue()
+    finally:
+      self._DisableConsoleOutputStream(timeout)
+      self._message_output_stream.close()
+      self._message_output_stream = None
+
+
+  def _EnableConsoleOutputStream(self, timeout):
+    self._inspector_websocket.SyncRequest({'method': 'Console.enable'}, timeout)
+
+  def _DisableConsoleOutputStream(self, timeout):
+    self._inspector_websocket.SyncRequest(
+        {'method': 'Console.disable'}, timeout)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/inspector_console_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/inspector_console_unittest.py
new file mode 100644
index 0000000..858568e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/inspector_console_unittest.py
@@ -0,0 +1,30 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import re
+
+from telemetry.testing import tab_test_case
+
+import py_utils
+
+
+class TabConsoleTest(tab_test_case.TabTestCase):
+  def testConsoleOutputStream(self):
+    self.Navigate('page_that_logs_to_console.html')
+
+    initial = self._tab.EvaluateJavaScript('window.__logCount')
+    def GotLog():
+      current = self._tab.EvaluateJavaScript('window.__logCount')
+      return current > initial
+    py_utils.WaitFor(GotLog, 5)
+
+    console_output = (
+        self._tab._inspector_backend.GetCurrentConsoleOutputBuffer())
+    lines = [l for l in console_output.split('\n') if len(l)]
+
+    self.assertTrue(len(lines) >= 1)
+    for line in lines:
+      prefix = 'http://(.+)/page_that_logs_to_console.html:9'
+      expected_line = r'\(log\) %s: Hello, world' % prefix
+      self.assertTrue(re.match(expected_line, line))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/inspector_memory.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/inspector_memory.py
new file mode 100644
index 0000000..cd4ddf0
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/inspector_memory.py
@@ -0,0 +1,53 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import json
+
+from telemetry.core import exceptions
+
+
+class InspectorMemoryException(exceptions.Error):
+  pass
+
+
+class InspectorMemory(object):
+  """Communicates with the remote inspector's Memory domain."""
+
+  def __init__(self, inspector_websocket):
+    self._inspector_websocket = inspector_websocket
+    self._inspector_websocket.RegisterDomain('Memory', self._OnNotification)
+
+  def _OnNotification(self, msg):
+    pass
+
+  def GetDOMCounters(self, timeout):
+    """Retrieves DOM element counts.
+
+    Args:
+      timeout: The number of seconds to wait for the inspector backend to
+          service the request before timing out.
+
+    Returns:
+      A dictionary containing the counts associated with "nodes", "documents",
+      and "jsEventListeners".
+    Raises:
+      InspectorMemoryException
+      websocket.WebSocketException
+      socket.error
+      exceptions.WebSocketDisconnected
+    """
+    res = self._inspector_websocket.SyncRequest({
+      'method': 'Memory.getDOMCounters'
+    }, timeout)
+    if ('result' not in res or
+        'nodes' not in res['result'] or
+        'documents' not in res['result'] or
+        'jsEventListeners' not in res['result']):
+      raise InspectorMemoryException(
+          'Inspector returned unexpected result for Memory.getDOMCounters:\n' +
+          json.dumps(res, indent=2))
+    return {
+        'nodes': res['result']['nodes'],
+        'documents': res['result']['documents'],
+        'jsEventListeners': res['result']['jsEventListeners']
+    }
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/inspector_memory_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/inspector_memory_unittest.py
new file mode 100644
index 0000000..9b7f5e0
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/inspector_memory_unittest.py
@@ -0,0 +1,36 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry import decorators
+from telemetry.testing import tab_test_case
+
+
+class InspectorMemoryTest(tab_test_case.TabTestCase):
+
+  @decorators.Enabled('has tabs')
+  def testGetDOMStats(self):
+    # Due to an issue with CrOS, we create a new tab here rather than
+    # using the existing tab to get a consistent starting page on all platforms.
+    self._tab = self._browser.tabs.New()
+
+    self.Navigate('dom_counter_sample.html')
+
+    self._tab.ExecuteJavaScript('gc();')
+
+    # Document_count > 1 indicates that WebCore::Document loaded in Chrome
+    # is leaking! The baseline should exactly match the numbers on:
+    # internal/testing/dom_counter_sample.html
+    # Please contact kouhei@, hajimehoshi@ when rebaselining.
+    counts = self._tab.dom_stats
+    self.assertEqual(counts['document_count'], 1,
+        'Document leak is detected! '+
+        'The previous document is likely retained unexpectedly.')
+    self.assertEqual(counts['node_count'], 14,
+        'Node leak is detected!')
+    self.assertEqual(counts['event_listener_count'], 2,
+        'EventListener leak is detected!')
+
+  @classmethod
+  def CustomizeBrowserOptions(cls, options):
+    options.AppendExtraBrowserArgs('--js-flags=--expose-gc')
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/inspector_page.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/inspector_page.py
new file mode 100644
index 0000000..0c37347
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/inspector_page.py
@@ -0,0 +1,156 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import time
+
+from telemetry.util import image_util
+
+
+class InspectorPage(object):
+  """Class that controls a page connected by an inspector_websocket.
+
+  This class provides utility methods for controlling a page connected by an
+  inspector_websocket. It does not perform any exception handling. All
+  inspector_websocket exceptions must be handled by the caller.
+  """
+  def __init__(self, inspector_websocket, timeout):
+    self._inspector_websocket = inspector_websocket
+    self._inspector_websocket.RegisterDomain('Page', self._OnNotification)
+
+    self._navigation_pending = False
+    self._navigation_url = ''  # Support for legacy backends.
+    self._navigation_frame_id = ''
+    self._navigated_frame_ids = None  # Holds frame ids while navigating.
+    self._script_to_evaluate_on_commit = None
+    # Turn on notifications. We need them to get the Page.frameNavigated event.
+    self._EnablePageNotifications(timeout=timeout)
+
+  def _OnNotification(self, msg):
+    if msg['method'] == 'Page.frameNavigated':
+      url = msg['params']['frame']['url']
+      if not self._navigated_frame_ids == None:
+        frame_id = msg['params']['frame']['id']
+        if self._navigation_frame_id == frame_id:
+          self._navigation_frame_id = ''
+          self._navigated_frame_ids = None
+          self._navigation_pending = False
+        else:
+          self._navigated_frame_ids.add(frame_id)
+      elif self._navigation_url == url:
+        # TODO(tonyg): Remove this when Chrome 38 goes stable.
+        self._navigation_url = ''
+        self._navigation_pending = False
+      elif (not url == 'chrome://newtab/' and not url == 'about:blank'
+        and not 'parentId' in msg['params']['frame']):
+        # Marks the navigation as complete and unblocks the
+        # WaitForNavigate call.
+        self._navigation_pending = False
+
+  def _SetScriptToEvaluateOnCommit(self, source, timeout):
+    existing_source = (self._script_to_evaluate_on_commit and
+                       self._script_to_evaluate_on_commit['source'])
+    if source == existing_source:
+      return
+    if existing_source:
+      request = {
+          'method': 'Page.removeScriptToEvaluateOnLoad',
+          'params': {
+              'identifier': self._script_to_evaluate_on_commit['id'],
+              }
+          }
+      self._inspector_websocket.SyncRequest(request, timeout)
+      self._script_to_evaluate_on_commit = None
+    if source:
+      request = {
+          'method': 'Page.addScriptToEvaluateOnLoad',
+          'params': {
+              'scriptSource': source,
+              }
+          }
+      res = self._inspector_websocket.SyncRequest(request, timeout)
+      self._script_to_evaluate_on_commit = {
+          'id': res['result']['identifier'],
+          'source': source
+          }
+
+  def _EnablePageNotifications(self, timeout=60):
+    request = {
+        'method': 'Page.enable'
+        }
+    res = self._inspector_websocket.SyncRequest(request, timeout)
+    assert len(res['result'].keys()) == 0
+
+  def WaitForNavigate(self, timeout=60):
+    """Waits for the navigation to complete.
+
+    The current page is expect to be in a navigation. This function returns
+    when the navigation is complete or when the timeout has been exceeded.
+    """
+    start_time = time.time()
+    remaining_time = timeout
+    self._navigation_pending = True
+    while self._navigation_pending and remaining_time > 0:
+      remaining_time = max(timeout - (time.time() - start_time), 0.0)
+      self._inspector_websocket.DispatchNotifications(remaining_time)
+
+  def Navigate(self, url, script_to_evaluate_on_commit=None, timeout=60):
+    """Navigates to |url|.
+
+    If |script_to_evaluate_on_commit| is given, the script source string will be
+    evaluated when the navigation is committed. This is after the context of
+    the page exists, but before any script on the page itself has executed.
+    """
+
+    self._SetScriptToEvaluateOnCommit(script_to_evaluate_on_commit, timeout)
+    request = {
+        'method': 'Page.navigate',
+        'params': {
+            'url': url,
+            }
+        }
+    self._navigated_frame_ids = set()
+    res = self._inspector_websocket.SyncRequest(request, timeout)
+    if 'frameId' in res['result']:
+      # Modern backends are returning frameId from Page.navigate.
+      # Use it here to unblock upon precise navigation.
+      frame_id = res['result']['frameId']
+      if self._navigated_frame_ids and frame_id in self._navigated_frame_ids:
+        self._navigated_frame_ids = None
+        return
+      self._navigation_frame_id = frame_id
+    else:
+      # TODO(tonyg): Remove this when Chrome 38 goes stable.
+      self._navigated_frame_ids = None
+      self._navigation_url = url
+    self.WaitForNavigate(timeout)
+
+  def GetCookieByName(self, name, timeout=60):
+    """Returns the value of the cookie by the given |name|."""
+    request = {
+        'method': 'Page.getCookies'
+        }
+    res = self._inspector_websocket.SyncRequest(request, timeout)
+    cookies = res['result']['cookies']
+    for cookie in cookies:
+      if cookie['name'] == name:
+        return cookie['value']
+    return None
+
+  def CaptureScreenshot(self, timeout=60):
+    request = {
+        'method': 'Page.captureScreenshot'
+        }
+    # "Google API are missing..." infobar might cause a viewport resize
+    # which invalidates screenshot request. See crbug.com/459820.
+    for _ in range(2):
+      res = self._inspector_websocket.SyncRequest(request, timeout)
+      if res and ('result' in res) and ('data' in res['result']):
+        return image_util.FromBase64Png(res['result']['data'])
+    return None
+
+  def CollectGarbage(self, timeout=60):
+    request = {
+        'method': 'HeapProfiler.collectGarbage'
+        }
+    res = self._inspector_websocket.SyncRequest(request, timeout)
+    assert 'result' in res
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/inspector_page_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/inspector_page_unittest.py
new file mode 100644
index 0000000..935b8df
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/inspector_page_unittest.py
@@ -0,0 +1,46 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry import decorators
+from telemetry.testing import tab_test_case
+from telemetry.util import image_util
+
+
+class InspectorPageTest(tab_test_case.TabTestCase):
+  def testPageNavigateToNormalUrl(self):
+    self.Navigate('blank.html')
+
+  def testCustomActionToNavigate(self):
+    self.Navigate('page_with_link.html')
+    self.assertEquals(
+        self._tab.EvaluateJavaScript('document.location.pathname;'),
+        '/page_with_link.html')
+
+    self._tab.ExecuteJavaScript('document.getElementById("clickme").click();')
+    self._tab.WaitForNavigate()
+
+    self.assertEquals(
+        self._tab.EvaluateJavaScript('document.location.pathname;'),
+        '/blank.html')
+
+  def testGetCookieByName(self):
+    self.Navigate('blank.html')
+    self._tab.ExecuteJavaScript('document.cookie="foo=bar"')
+    self.assertEquals(self._tab.GetCookieByName('foo'), 'bar')
+
+  def testScriptToEvaluateOnCommit(self):
+    self.Navigate('blank.html',
+                  script_to_evaluate_on_commit='var foo = "bar";')
+    self._tab.WaitForDocumentReadyStateToBeComplete()
+    self.assertEquals(self._tab.EvaluateJavaScript('foo'), 'bar')
+
+  @decorators.Disabled('chromeos')  # crbug.com/483212
+  def testCaptureScreenshot(self):
+    if not self._tab.screenshot_supported:
+      return
+    self.Navigate('green_rect.html')
+    res = image_util.Pixels(self._tab.Screenshot())
+    self.assertEquals(0x00, res[0])
+    self.assertEquals(0xFF, res[1])
+    self.assertEquals(0x00, res[2])
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/inspector_runtime.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/inspector_runtime.py
new file mode 100644
index 0000000..7b619c1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/inspector_runtime.py
@@ -0,0 +1,83 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+from telemetry.core import exceptions
+
+
+class InspectorRuntime(object):
+  def __init__(self, inspector_websocket):
+    self._inspector_websocket = inspector_websocket
+    self._inspector_websocket.RegisterDomain('Runtime', self._OnNotification)
+    self._contexts_enabled = False
+    self._max_context_id = None
+
+  def _OnNotification(self, msg):
+    if (self._contexts_enabled and
+        msg['method'] == 'Runtime.executionContextCreated'):
+      self._max_context_id = max(self._max_context_id,
+                                 msg['params']['context']['id'])
+
+  def Execute(self, expr, context_id, timeout):
+    self.Evaluate(expr + '; 0;', context_id, timeout)
+
+  def Evaluate(self, expr, context_id, timeout):
+    """Evaluates a javascript expression and returns the result.
+
+    |context_id| can refer to an iframe. The main page has context_id=1, the
+    first iframe context_id=2, etc.
+
+    Raises:
+      exceptions.EvaluateException
+      exceptions.WebSocketDisconnected
+      websocket.WebSocketException
+      socket.error
+    """
+    request = {
+      'method': 'Runtime.evaluate',
+      'params': {
+        'expression': expr,
+        'returnByValue': True
+        }
+      }
+    if context_id is not None:
+      self.EnableAllContexts()
+      request['params']['contextId'] = context_id
+    res = self._inspector_websocket.SyncRequest(request, timeout)
+    if 'error' in res:
+      raise exceptions.EvaluateException(res['error']['message'])
+
+    if 'exceptionDetails' in res['result']:
+      details = res['result']['exceptionDetails']
+      raise exceptions.EvaluateException(
+          text=details['text'],
+          class_name=details.get('exception', {}).get('className'),
+          description=details.get('exception', {}).get('description'))
+
+    if res['result']['result']['type'] == 'undefined':
+      return None
+    return res['result']['result']['value']
+
+  def EnableAllContexts(self):
+    """Allow access to iframes.
+
+    Raises:
+      exceptions.WebSocketDisconnected
+      websocket.WebSocketException
+      socket.error
+    """
+    if not self._contexts_enabled:
+      self._contexts_enabled = True
+      self._inspector_websocket.SyncRequest({'method': 'Runtime.enable'},
+                                            timeout=30)
+    return self._max_context_id
+
+  def RunInspectorCommand(self, command, timeout):
+    """Runs an inspector command.
+
+    Raises:
+      exceptions.WebSocketDisconnected
+      websocket.WebSocketException
+      socket.error
+    """
+    res = self._inspector_websocket.SyncRequest(command, timeout)
+    return res
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/inspector_runtime_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/inspector_runtime_unittest.py
new file mode 100644
index 0000000..1bb8e40
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/inspector_runtime_unittest.py
@@ -0,0 +1,82 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.core import exceptions
+from telemetry.testing import tab_test_case
+
+import py_utils
+
+
+class InspectorRuntimeTest(tab_test_case.TabTestCase):
+  def testRuntimeEvaluateSimple(self):
+    res = self._tab.EvaluateJavaScript('1+1')
+    assert res == 2
+
+  def testRuntimeEvaluateThatFails(self):
+    with self.assertRaises(exceptions.EvaluateException) as ex_context:
+      self._tab.EvaluateJavaScript('var x = 1;\nfsdfsdfsf')
+    exception_message = str(ex_context.exception)
+    self.assertIn('ReferenceError: fsdfsdfsf is not defined', exception_message)
+
+  def testRuntimeEvaluateOfSomethingThatCantJSONize(self):
+
+    def test():
+      self._tab.EvaluateJavaScript("""
+        var cur = {};
+        var root = {next: cur};
+        for (var i = 0; i < 1000; i++) {
+          next = {};
+          cur.next = next;
+          cur = next;
+        }
+        root;""")
+    self.assertRaises(exceptions.EvaluateException, test)
+
+  def testRuntimeExecuteOfSomethingThatCantJSONize(self):
+    self._tab.ExecuteJavaScript('window')
+
+  def testIFrame(self):
+    starting_contexts = self._tab.EnableAllContexts()
+
+    self.Navigate('host.html')
+
+    # Access host page.
+    test_defined_js = "typeof(testVar) != 'undefined'"
+    self._tab.WaitForJavaScriptCondition(test_defined_js, timeout=10)
+
+    py_utils.WaitFor(lambda: self._tab.EnableAllContexts() != starting_contexts,
+                 timeout=10)
+
+    self.assertEquals(self._tab.EvaluateJavaScript('testVar'), 'host')
+
+    def TestVarReady(context_id):
+      """Returns True if the context and testVar are both ready."""
+      try:
+        return self._tab.EvaluateJavaScript(
+            test_defined_js, context_id=context_id)
+      except exceptions.EvaluateException:
+        # This happens when the context is not ready.
+        return False
+
+    def TestVar(context_id):
+      """Waits for testVar and the context to be ready, then returns the value
+      of testVar."""
+      py_utils.WaitFor(lambda: TestVarReady(context_id), timeout=10)
+      return self._tab.EvaluateJavaScript('testVar', context_id=context_id)
+
+    all_contexts = self._tab.EnableAllContexts()
+    # Access parent page using EvaluateJavaScriptInContext.
+    self.assertEquals(TestVar(context_id=all_contexts-3), 'host')
+
+    # Access the iframes without guarantees on which order they loaded.
+    iframe1 = TestVar(context_id=all_contexts-2)
+    iframe2 = TestVar(context_id=all_contexts-1)
+    iframe3 = TestVar(context_id=all_contexts)
+    self.assertEqual(set([iframe1, iframe2, iframe3]),
+                     set(['iframe1', 'iframe2', 'iframe3']))
+
+    # Accessing a non-existent iframe throws an exception.
+    self.assertRaises(exceptions.EvaluateException,
+        lambda: self._tab.EvaluateJavaScript(
+            '1+1', context_id=all_contexts + 1))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/inspector_websocket.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/inspector_websocket.py
new file mode 100644
index 0000000..1fe5c2a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/inspector_websocket.py
@@ -0,0 +1,184 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import errno
+import json
+import logging
+import socket
+import time
+
+from telemetry.core import exceptions
+from telemetry.internal.backends.chrome_inspector import websocket
+
+class WebSocketDisconnected(exceptions.Error):
+  """An attempt was made to use a web socket after it had been disconnected."""
+  pass
+
+
+class InspectorWebsocket(object):
+
+  # See http://www.jsonrpc.org/specification#error_object.
+  METHOD_NOT_FOUND_CODE = -32601
+
+  def __init__(self):
+    """Create a websocket handler for communicating with Inspectors."""
+    self._socket = None
+    self._cur_socket_timeout = 0
+    self._next_request_id = 0
+    self._domain_handlers = {}
+    self._pending_callbacks = dict()
+
+  def RegisterDomain(self, domain_name, notification_handler):
+    """Registers a given domain for handling notification methods.
+
+    For example, given inspector_backend:
+       def OnConsoleNotification(msg):
+          if msg['method'] == 'Console.messageAdded':
+             print msg['params']['message']
+       inspector_backend.RegisterDomain('Console', OnConsoleNotification)
+
+    Args:
+      domain_name: The devtools domain name. E.g., 'Tracing', 'Memory', 'Page'.
+      notification_handler: Handler for devtools notification. Will be
+          called if a devtools notification with matching domain is received
+          via DispatchNotifications. The handler accepts a single paramater:
+          the JSON object representing the notification.
+    """
+    assert domain_name not in self._domain_handlers
+    self._domain_handlers[domain_name] = notification_handler
+
+  def UnregisterDomain(self, domain_name):
+    """Unregisters a previously registered domain."""
+    assert domain_name in self._domain_handlers
+    del self._domain_handlers[domain_name]
+
+  def Connect(self, url, timeout):
+    """Connects the websocket.
+
+    Raises:
+      websocket.WebSocketException
+      socket.error
+    """
+    assert not self._socket
+    self._socket = websocket.create_connection(url, timeout=timeout)
+    self._cur_socket_timeout = 0
+    self._next_request_id = 0
+
+  def Disconnect(self):
+    """Disconnects the inspector websocket.
+
+    Raises:
+      websocket.WebSocketException
+      socket.error
+    """
+    if self._socket:
+      self._socket.close()
+      self._socket = None
+
+  def SendAndIgnoreResponse(self, req):
+    """Sends a request without waiting for a response.
+
+    Raises:
+      websocket.WebSocketException: Error from websocket library.
+      socket.error: Error from websocket library.
+      exceptions.WebSocketDisconnected: The socket was disconnected.
+    """
+    self._SendRequest(req)
+
+  def _SendRequest(self, req):
+    if not self._socket:
+      raise WebSocketDisconnected()
+    req['id'] = self._next_request_id
+    self._next_request_id += 1
+    data = json.dumps(req)
+    self._socket.send(data)
+    if logging.getLogger().isEnabledFor(logging.DEBUG):
+      logging.debug('sent [%s]', json.dumps(req, indent=2, sort_keys=True))
+
+  def SyncRequest(self, req, timeout):
+    """Sends a request and waits for a response.
+
+    Raises:
+      websocket.WebSocketException: Error from websocket library.
+      socket.error: Error from websocket library.
+      exceptions.WebSocketDisconnected: The socket was disconnected.
+    """
+    self._SendRequest(req)
+
+    while True:
+      res = self._Receive(timeout)
+      if 'id' in res and res['id'] == req['id']:
+        return res
+
+  def AsyncRequest(self, req, callback):
+    """Sends an async request and returns immediately.
+
+    Response will be handled in the |callback| later when DispatchNotifications
+    is invoked.
+
+    Args:
+      callback: a function that takes inspector's response as the argument.
+    """
+    self._SendRequest(req)
+    self._pending_callbacks[req['id']] = callback
+
+  def DispatchNotifications(self, timeout):
+    """Waits for responses from the websocket, dispatching them as necessary.
+
+    Raises:
+      websocket.WebSocketException: Error from websocket library.
+      socket.error: Error from websocket library.
+      exceptions.WebSocketDisconnected: The socket was disconnected.
+    """
+    self._Receive(timeout)
+
+  def _SetTimeout(self, timeout):
+    if self._cur_socket_timeout != timeout:
+      self._socket.settimeout(timeout)
+      self._cur_socket_timeout = timeout
+
+  def _Receive(self, timeout):
+    if not self._socket:
+      raise WebSocketDisconnected()
+
+    self._SetTimeout(timeout)
+
+    while True:
+      try:
+        data = self._socket.recv()
+      except socket.error, e:
+        if e.errno == errno.EAGAIN:
+          # Resource is temporarily unavailable. Try again.
+          # See https://code.google.com/p/chromium/issues/detail?id=545853#c3
+          # for more details.
+          time.sleep(0.1)
+        else:
+          raise
+      else:
+        break
+
+    result = json.loads(data)
+    if logging.getLogger().isEnabledFor(logging.DEBUG):
+      logging.debug(
+          'got [%s]', json.dumps(result, indent=2, sort_keys=True))
+    if 'method' in result:
+      self._HandleNotification(result)
+    elif 'id' in result:
+      self._HandleAsyncResponse(result)
+    return result
+
+  def _HandleNotification(self, result):
+    mname = result['method']
+    dot_pos = mname.find('.')
+    domain_name = mname[:dot_pos]
+    if not domain_name in self._domain_handlers:
+      logging.warn('Unhandled inspector message: %s', result)
+      return
+
+    self._domain_handlers[domain_name](result)
+
+  def _HandleAsyncResponse(self, result):
+    callback = self._pending_callbacks.pop(result['id'], None)
+    if callback:
+      callback(result)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/inspector_websocket_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/inspector_websocket_unittest.py
new file mode 100644
index 0000000..f528589
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/inspector_websocket_unittest.py
@@ -0,0 +1,186 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import errno
+import socket
+import unittest
+
+from telemetry import decorators
+from telemetry.internal.backends.chrome_inspector import inspector_websocket
+from telemetry.internal.backends.chrome_inspector import websocket
+from telemetry.testing import fakes
+
+
+class FakeSocket(object):
+  """A fake websocket that allows test to send random data."""
+  def __init__(self, fake_timer):
+    self._fake_timer = fake_timer
+    self._responses = []
+    self._timeout = None
+
+  def AddResponse(self, response, time):
+    if self._responses:
+      assert self._responses[-1][1] < time, (
+          'Current response is scheduled earlier than previous response.')
+    self._responses.append((response, time))
+
+  def send(self, data):
+    pass
+
+  def recv(self):
+    if not self._responses:
+      raise Exception('No more recorded responses.')
+
+    response, time = self._responses.pop(0)
+    current_time = self._fake_timer.time()
+    if self._timeout is not None and time - current_time > self._timeout:
+      self._fake_timer.SetTime(current_time + self._timeout + 1)
+      raise websocket.WebSocketTimeoutException()
+
+    self._fake_timer.SetTime(time)
+    if isinstance(response, Exception):
+      raise response
+    return response
+
+  def settimeout(self, timeout):
+    self._timeout = timeout
+
+
+def _DoNothingHandler(elapsed_time):
+  del elapsed_time  # unused
+
+
+class InspectorWebsocketUnittest(unittest.TestCase):
+
+  def setUp(self):
+    self._fake_timer = fakes.FakeTimer()
+
+  def tearDown(self):
+    self._fake_timer.Restore()
+
+  @decorators.Disabled('chromeos', 'mac')  # crbug.com/483212, crbug.com/498950
+  def testDispatchNotification(self):
+    inspector = inspector_websocket.InspectorWebsocket()
+    fake_socket = FakeSocket(self._fake_timer)
+    # pylint: disable=protected-access
+    inspector._socket = fake_socket
+
+    results = []
+    def OnTestEvent(result):
+      results.append(result)
+
+    inspector.RegisterDomain('Test', OnTestEvent)
+    fake_socket.AddResponse('{"method": "Test.foo"}', 5)
+    inspector.DispatchNotifications(10)
+    self.assertEqual(1, len(results))
+    self.assertEqual('Test.foo', results[0]['method'])
+
+  @decorators.Disabled('chromeos')  # crbug.com/483212
+  def testDispatchNotificationTimedOut(self):
+    inspector = inspector_websocket.InspectorWebsocket()
+    fake_socket = FakeSocket(self._fake_timer)
+    # pylint: disable=protected-access
+    inspector._socket = fake_socket
+
+    results = []
+    def OnTestEvent(result):
+      results.append(result)
+
+    inspector.RegisterDomain('Test', OnTestEvent)
+    fake_socket.AddResponse('{"method": "Test.foo"}', 11)
+    with self.assertRaises(
+        websocket.WebSocketTimeoutException):
+      inspector.DispatchNotifications(timeout=10)
+    self.assertEqual(0, len(results))
+
+  @decorators.Disabled('chromeos')  # crbug.com/483212
+  def testUnregisterDomain(self):
+    inspector = inspector_websocket.InspectorWebsocket()
+    fake_socket = FakeSocket(self._fake_timer)
+    # pylint: disable=protected-access
+    inspector._socket = fake_socket
+
+    results = []
+    def OnTestEvent(result):
+      results.append(result)
+
+    inspector.RegisterDomain('Test', OnTestEvent)
+    inspector.RegisterDomain('Test2', OnTestEvent)
+    inspector.UnregisterDomain('Test')
+
+    fake_socket.AddResponse('{"method": "Test.foo"}', 5)
+    fake_socket.AddResponse('{"method": "Test2.foo"}', 10)
+
+    inspector.DispatchNotifications(10)
+    self.assertEqual(0, len(results))
+
+    inspector.DispatchNotifications(10)
+    self.assertEqual(1, len(results))
+    self.assertEqual('Test2.foo', results[0]['method'])
+
+  @decorators.Disabled('chromeos')  # crbug.com/483212
+  def testUnregisterDomainWithUnregisteredDomain(self):
+    inspector = inspector_websocket.InspectorWebsocket()
+    with self.assertRaises(AssertionError):
+      inspector.UnregisterDomain('Test')
+
+  def testAsyncRequest(self):
+    inspector = inspector_websocket.InspectorWebsocket()
+    fake_socket = FakeSocket(self._fake_timer)
+    # pylint: disable=protected-access
+    inspector._socket = fake_socket
+    response_count = [0]
+
+    def callback0(response):
+      response_count[0] += 1
+      self.assertEqual(2, response_count[0])
+      self.assertEqual('response1', response['result']['data'])
+
+    def callback1(response):
+      response_count[0] += 1
+      self.assertEqual(1, response_count[0])
+      self.assertEqual('response2', response['result']['data'])
+
+    request1 = {'method': 'Test.foo'}
+    inspector.AsyncRequest(request1, callback0)
+    request2 = {'method': 'Test.foo'}
+    inspector.AsyncRequest(request2, callback1)
+    fake_socket.AddResponse('{"id": 5555555, "result": {}}', 1)
+    inspector.DispatchNotifications(10)
+    self.assertEqual(0, response_count[0])
+    fake_socket.AddResponse(
+        '{"id": %d, "result": {"data": "response2"}}' % request2['id'], 1)
+    fake_socket.AddResponse(
+        '{"id": %d, "result": {"data": "response1"}}' % request1['id'], 2)
+    inspector.DispatchNotifications(10)
+    inspector.DispatchNotifications(10)
+    self.assertEqual(2, response_count[0])
+    fake_socket.AddResponse('{"id": 6666666, "result": {}}', 1)
+    inspector.DispatchNotifications(10)
+    self.assertEqual(2, response_count[0])
+
+  def testEAGAIN(self):
+    inspector = inspector_websocket.InspectorWebsocket()
+    fake_socket = FakeSocket(self._fake_timer)
+    # pylint: disable=protected-access
+    inspector._socket = fake_socket
+
+    error = socket.error(errno.EAGAIN, "error string")
+    fake_socket.AddResponse(error, 4)
+    fake_socket.AddResponse('{"asdf": "qwer"}', 5)
+
+    result = inspector._Receive(10)
+    self.assertEqual(result, {"asdf" : "qwer"})
+
+  def testSocketErrorOtherThanEAGAIN(self):
+    inspector = inspector_websocket.InspectorWebsocket()
+    fake_socket = FakeSocket(self._fake_timer)
+    # pylint: disable=protected-access
+    inspector._socket = fake_socket
+
+    error = socket.error(errno.EPIPE, "error string")
+    fake_socket.AddResponse(error, 4)
+
+    with self.assertRaises(socket.error):
+      inspector._Receive(10)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/memory_backend.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/memory_backend.py
new file mode 100644
index 0000000..430fe73
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/memory_backend.py
@@ -0,0 +1,95 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import json
+import logging
+import socket
+import traceback
+
+from telemetry.internal.backends.chrome_inspector import inspector_websocket
+from telemetry.internal.backends.chrome_inspector import websocket
+
+
+class MemoryTimeoutException(Exception):
+  pass
+
+
+class MemoryUnrecoverableException(Exception):
+  pass
+
+
+class MemoryUnexpectedResponseException(Exception):
+  pass
+
+
+class MemoryBackend(object):
+
+  def __init__(self, inspector_socket):
+    self._inspector_websocket = inspector_socket
+
+  def SetMemoryPressureNotificationsSuppressed(self, suppressed, timeout=30):
+    """Enable/disable suppressing memory pressure notifications.
+
+    Args:
+      suppressed: If true, memory pressure notifications will be suppressed.
+      timeout: The timeout in seconds.
+
+    Raises:
+      MemoryTimeoutException: If more than |timeout| seconds has passed
+      since the last time any data is received.
+      MemoryUnrecoverableException: If there is a websocket error.
+      MemoryUnexpectedResponseException: If the response contains an error
+      or does not contain the expected result.
+    """
+    self._SendMemoryRequest('setPressureNotificationsSuppressed',
+                            {'suppressed': suppressed}, timeout)
+
+  def SimulateMemoryPressureNotification(self, pressure_level, timeout=30):
+    """Simulate a memory pressure notification.
+
+    Args:
+      pressure level: The memory pressure level of the notification ('moderate'
+          or 'critical').
+      timeout: The timeout in seconds.
+
+    Raises:
+      MemoryTimeoutException: If more than |timeout| seconds has passed
+      since the last time any data is received.
+      MemoryUnrecoverableException: If there is a websocket error.
+      MemoryUnexpectedResponseException: If the response contains an error
+      or does not contain the expected result.
+    """
+    self._SendMemoryRequest('simulatePressureNotification',
+                            {'level': pressure_level}, timeout)
+
+  def _SendMemoryRequest(self, command, params, timeout):
+    method = 'Memory.%s' % command
+    request = {
+      'method': method,
+      'params': params
+    }
+    try:
+      response = self._inspector_websocket.SyncRequest(request, timeout)
+    except websocket.WebSocketTimeoutException:
+      raise MemoryTimeoutException(
+          'Exception raised while sending a %s request:\n%s' %
+              (method, traceback.format_exc()))
+    except (socket.error, websocket.WebSocketException,
+            inspector_websocket.WebSocketDisconnected):
+      raise MemoryUnrecoverableException(
+          'Exception raised while sending a %s request:\n%s' %
+              (method, traceback.format_exc()))
+
+    if 'error' in response:
+      code = response['error']['code']
+      if code == inspector_websocket.InspectorWebsocket.METHOD_NOT_FOUND_CODE:
+        logging.warning(
+            '%s DevTools method not supported by the browser' % method)
+      else:
+        raise MemoryUnexpectedResponseException(
+            'Inspector returned unexpected response for %s:\n%s' %
+                (method, json.dumps(response, indent=2)))
+
+  def Close(self):
+    self._inspector_websocket = None
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/memory_backend_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/memory_backend_unittest.py
new file mode 100644
index 0000000..e07818f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/memory_backend_unittest.py
@@ -0,0 +1,151 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import mock
+import unittest
+
+
+from telemetry.internal.backends.chrome_inspector import inspector_websocket
+from telemetry.internal.backends.chrome_inspector import memory_backend
+from telemetry.testing import fakes
+from telemetry.testing import tab_test_case
+
+
+class MemoryBackendTest(tab_test_case.TabTestCase):
+
+  def setUp(self):
+    super(MemoryBackendTest, self).setUp()
+    if not self._browser.supports_overriding_memory_pressure_notifications:
+      self.skipTest('Browser does not support overriding memory pressure '
+                    'notification signals, skipping test.')
+
+  def testSetMemoryPressureNotificationsSuppressed(self):
+    def PerformCheck(suppressed):
+      # Check that the method sends the correct DevTools request.
+      with mock.patch.object(inspector_websocket.InspectorWebsocket,
+                             'SyncRequest') as mock_method:
+        self._browser.SetMemoryPressureNotificationsSuppressed(suppressed)
+        self.assertEqual(1, mock_method.call_count)
+        request = mock_method.call_args[0][0]
+        self.assertEqual('Memory.setPressureNotificationsSuppressed',
+                         request['method'])
+        self.assertEqual(suppressed, request['params']['suppressed'])
+
+      # Check that the request and the response from the browser are handled
+      # properly.
+      self._browser.SetMemoryPressureNotificationsSuppressed(suppressed)
+
+    PerformCheck(True)
+    PerformCheck(False)
+
+  def testSimulateMemoryPressureNotification(self):
+    def PerformCheck(pressure_level):
+      # Check that the method sends the correct DevTools request.
+      with mock.patch.object(inspector_websocket.InspectorWebsocket,
+                             'SyncRequest') as mock_method:
+        self._browser.SimulateMemoryPressureNotification(pressure_level)
+        self.assertEqual(1, mock_method.call_count)
+        request = mock_method.call_args[0][0]
+        self.assertEqual('Memory.simulatePressureNotification',
+                         request['method'])
+        self.assertEqual(pressure_level, request['params']['level'])
+
+      # Check that the request and the response from the browser are handled
+      # properly.
+      self._browser.SimulateMemoryPressureNotification(pressure_level)
+
+    PerformCheck('moderate')
+    PerformCheck('critical')
+
+
+class MemoryBackendUnitTest(unittest.TestCase):
+
+  def setUp(self):
+    self._fake_timer = fakes.FakeTimer()
+    self._inspector_socket = fakes.FakeInspectorWebsocket(self._fake_timer)
+
+  def tearDown(self):
+    self._fake_timer.Restore()
+
+  def testSetMemoryPressureNotificationsSuppressedSuccess(self):
+    response_handler = mock.Mock(return_value={'result': {}})
+    self._inspector_socket.AddResponseHandler(
+        'Memory.setPressureNotificationsSuppressed', response_handler)
+    backend = memory_backend.MemoryBackend(self._inspector_socket)
+
+    backend.SetMemoryPressureNotificationsSuppressed(True)
+    self.assertEqual(1, response_handler.call_count)
+    self.assertTrue(response_handler.call_args[0][0]['params']['suppressed'])
+
+    backend.SetMemoryPressureNotificationsSuppressed(False)
+    self.assertEqual(2, response_handler.call_count)
+    self.assertFalse(response_handler.call_args[0][0]['params']['suppressed'])
+
+  def testSetMemoryPressureNotificationsSuppressedFailure(self):
+    response_handler = mock.Mock()
+    backend = memory_backend.MemoryBackend(self._inspector_socket)
+    self._inspector_socket.AddResponseHandler(
+        'Memory.setPressureNotificationsSuppressed', response_handler)
+
+    # If the DevTools method is missing, the backend should fail silently.
+    response_handler.return_value = {
+      'result': {},
+      'error': {
+        'code': -32601  # Method does not exist.
+      }
+    }
+    backend.SetMemoryPressureNotificationsSuppressed(True)
+    self.assertEqual(1, response_handler.call_count)
+
+    # All other errors should raise an exception.
+    response_handler.return_value = {
+      'result': {},
+      'error': {
+        'code': -32602  # Invalid method params.
+      }
+    }
+    self.assertRaises(memory_backend.MemoryUnexpectedResponseException,
+                      backend.SetMemoryPressureNotificationsSuppressed, True)
+
+  def testSimulateMemoryPressureNotificationSuccess(self):
+    response_handler = mock.Mock(return_value={'result': {}})
+    self._inspector_socket.AddResponseHandler(
+        'Memory.simulatePressureNotification', response_handler)
+    backend = memory_backend.MemoryBackend(self._inspector_socket)
+
+    backend.SimulateMemoryPressureNotification('critical')
+    self.assertEqual(1, response_handler.call_count)
+    self.assertEqual('critical',
+                     response_handler.call_args[0][0]['params']['level'])
+
+    backend.SimulateMemoryPressureNotification('moderate')
+    self.assertEqual(2, response_handler.call_count)
+    self.assertEqual('moderate',
+                     response_handler.call_args[0][0]['params']['level'])
+
+  def testSimulateMemoryPressureNotificationFailure(self):
+    response_handler = mock.Mock()
+    backend = memory_backend.MemoryBackend(self._inspector_socket)
+    self._inspector_socket.AddResponseHandler(
+        'Memory.simulatePressureNotification', response_handler)
+
+    # If the DevTools method is missing, the backend should fail silently.
+    response_handler.return_value = {
+      'result': {},
+      'error': {
+        'code': -32601  # Method does not exist.
+      }
+    }
+    backend.SimulateMemoryPressureNotification('critical')
+    self.assertEqual(1, response_handler.call_count)
+
+    # All other errors should raise an exception.
+    response_handler.return_value = {
+      'result': {},
+      'error': {
+        'code': -32602  # Invalid method params.
+      }
+    }
+    self.assertRaises(memory_backend.MemoryUnexpectedResponseException,
+                      backend.SimulateMemoryPressureNotification, 'critical')
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/tracing_backend.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/tracing_backend.py
new file mode 100644
index 0000000..1a32b87
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/tracing_backend.py
@@ -0,0 +1,298 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import json
+import logging
+import socket
+import time
+import traceback
+
+from telemetry import decorators
+from telemetry.internal.backends.chrome_inspector import inspector_websocket
+from telemetry.internal.backends.chrome_inspector import websocket
+from tracing.trace_data import trace_data as trace_data_module
+
+
+class TracingUnsupportedException(Exception):
+  pass
+
+
+class TracingTimeoutException(Exception):
+  pass
+
+
+class TracingUnrecoverableException(Exception):
+  pass
+
+
+class TracingHasNotRunException(Exception):
+  pass
+
+
+class TracingUnexpectedResponseException(Exception):
+  pass
+
+
+class ClockSyncResponseException(Exception):
+  pass
+
+
+class _DevToolsStreamReader(object):
+  def __init__(self, inspector_socket, stream_handle):
+    self._inspector_websocket = inspector_socket
+    self._handle = stream_handle
+    self._trace_file_handle = None
+    self._callback = None
+
+  def Read(self, callback):
+    # Do not allow the instance of this class to be reused, as
+    # we only read data sequentially at the moment, so a stream
+    # can only be read once.
+    assert not self._callback
+    self._trace_file_handle = trace_data_module.TraceFileHandle()
+    self._trace_file_handle.Open()
+    self._callback = callback
+    self._ReadChunkFromStream()
+    # The below is not a typo -- queue one extra read ahead to avoid latency.
+    self._ReadChunkFromStream()
+
+  def _ReadChunkFromStream(self):
+    # Limit max block size to avoid fragmenting memory in sock.recv(),
+    # (see https://github.com/liris/websocket-client/issues/163 for details)
+    req = {'method': 'IO.read', 'params': {
+        'handle': self._handle, 'size': 32768}}
+    self._inspector_websocket.AsyncRequest(req, self._GotChunkFromStream)
+
+  def _GotChunkFromStream(self, response):
+    # Quietly discard responses from reads queued ahead after EOF.
+    if self._trace_file_handle is None:
+      return
+    if 'error' in response:
+      raise TracingUnrecoverableException(
+          'Reading trace failed: %s' % response['error']['message'])
+    result = response['result']
+    # Convert the trace data that's receive as UTF32 to its native encoding of
+    # UTF8 in order to reduce its size.
+    self._trace_file_handle.AppendTraceData(result['data'].encode('utf8'))
+    if not result.get('eof', False):
+      self._ReadChunkFromStream()
+      return
+    req = {'method': 'IO.close', 'params': {'handle': self._handle}}
+    self._inspector_websocket.SendAndIgnoreResponse(req)
+    self._trace_file_handle.Close()
+    self._callback(self._trace_file_handle)
+    self._trace_file_handle = None
+
+
+class TracingBackend(object):
+
+  _TRACING_DOMAIN = 'Tracing'
+
+  def __init__(self, inspector_socket, is_tracing_running=False,
+               support_modern_devtools_tracing_start_api=False):
+    self._inspector_websocket = inspector_socket
+    self._inspector_websocket.RegisterDomain(
+        self._TRACING_DOMAIN, self._NotificationHandler)
+    self._is_tracing_running = is_tracing_running
+    self._start_issued = False
+    self._can_collect_data = False
+    self._has_received_all_tracing_data = False
+    self._support_modern_devtools_tracing_start_api = (
+        support_modern_devtools_tracing_start_api)
+    self._trace_data_builder = None
+
+  @property
+  def is_tracing_running(self):
+    return self._is_tracing_running
+
+  def StartTracing(self, chrome_trace_config, timeout=10):
+    """When first called, starts tracing, and returns True.
+
+    If called during tracing, tracing is unchanged, and it returns False.
+    """
+    if self.is_tracing_running:
+      return False
+    assert not self._can_collect_data, 'Data not collected from last trace.'
+    # Reset collected tracing data from previous tracing calls.
+
+    if not self.IsTracingSupported():
+      raise TracingUnsupportedException(
+          'Chrome tracing not supported for this app.')
+
+    params = {'transferMode': 'ReturnAsStream'}
+    if self._support_modern_devtools_tracing_start_api:
+      params['traceConfig'] = (
+          chrome_trace_config.GetChromeTraceConfigForDevTools())
+    else:
+      if chrome_trace_config.requires_modern_devtools_tracing_start_api:
+        raise TracingUnsupportedException(
+            'Trace options require modern Tracing.start DevTools API, '
+            'which is NOT supported by the browser')
+      params['categories'], params['options'] = (
+          chrome_trace_config.GetChromeTraceCategoriesAndOptionsForDevTools())
+
+    req = {'method': 'Tracing.start', 'params': params}
+    logging.info('Start Tracing Request: %r', req)
+    response = self._inspector_websocket.SyncRequest(req, timeout)
+
+    if 'error' in response:
+      raise TracingUnexpectedResponseException(
+          'Inspector returned unexpected response for '
+          'Tracing.start:\n' + json.dumps(response, indent=2))
+
+    self._is_tracing_running = True
+    self._start_issued = True
+    return True
+
+  def RecordClockSyncMarker(self, sync_id):
+    assert self.is_tracing_running, 'Tracing must be running to clock sync.'
+    req = {
+      'method': 'Tracing.recordClockSyncMarker',
+      'params': {
+        'syncId': sync_id
+      }
+    }
+    rc = self._inspector_websocket.SyncRequest(req, timeout=2)
+    if 'error' in rc:
+      raise ClockSyncResponseException(rc['error']['message'])
+
+  def StopTracing(self):
+    """Stops tracing and pushes results to the supplied TraceDataBuilder.
+
+    If this is called after tracing has been stopped, trace data from the last
+    tracing run is pushed.
+    """
+    if not self.is_tracing_running:
+      raise TracingHasNotRunException()
+    else:
+      if not self._start_issued:
+        # Tracing is running but start was not issued so, startup tracing must
+        # be in effect. Issue another Tracing.start to update the transfer mode.
+        # TODO(caseq): get rid of it when streaming is the default.
+        params = {
+          'transferMode': 'ReturnAsStream',
+          'traceConfig': {}
+        }
+        req = {'method': 'Tracing.start', 'params': params}
+        self._inspector_websocket.SendAndIgnoreResponse(req)
+
+      req = {'method': 'Tracing.end'}
+      self._inspector_websocket.SendAndIgnoreResponse(req)
+
+    self._is_tracing_running = False
+    self._start_issued = False
+    self._can_collect_data = True
+
+  def DumpMemory(self, timeout=30):
+    """Dumps memory.
+
+    Returns:
+      GUID of the generated dump if successful, None otherwise.
+
+    Raises:
+      TracingTimeoutException: If more than |timeout| seconds has passed
+      since the last time any data is received.
+      TracingUnrecoverableException: If there is a websocket error.
+      TracingUnexpectedResponseException: If the response contains an error
+      or does not contain the expected result.
+    """
+    request = {
+      'method': 'Tracing.requestMemoryDump'
+    }
+    try:
+      response = self._inspector_websocket.SyncRequest(request, timeout)
+    except websocket.WebSocketTimeoutException:
+      raise TracingTimeoutException(
+          'Exception raised while sending a Tracing.requestMemoryDump '
+          'request:\n' + traceback.format_exc())
+    except (socket.error, websocket.WebSocketException,
+            inspector_websocket.WebSocketDisconnected):
+      raise TracingUnrecoverableException(
+          'Exception raised while sending a Tracing.requestMemoryDump '
+          'request:\n' + traceback.format_exc())
+
+
+    if ('error' in response or
+        'result' not in response or
+        'success' not in response['result'] or
+        'dumpGuid' not in response['result']):
+      raise TracingUnexpectedResponseException(
+          'Inspector returned unexpected response for '
+          'Tracing.requestMemoryDump:\n' + json.dumps(response, indent=2))
+
+    result = response['result']
+    return result['dumpGuid'] if result['success'] else None
+
+  def CollectTraceData(self, trace_data_builder, timeout=60):
+    if not self._can_collect_data:
+      raise Exception('Cannot collect before tracing is finished.')
+    self._CollectTracingData(trace_data_builder, timeout)
+    self._can_collect_data = False
+
+  def _CollectTracingData(self, trace_data_builder, timeout):
+    """Collects tracing data. Assumes that Tracing.end has already been sent.
+
+    Args:
+      trace_data_builder: An instance of TraceDataBuilder to put results into.
+      timeout: The timeout in seconds.
+
+    Raises:
+      TracingTimeoutException: If more than |timeout| seconds has passed
+      since the last time any data is received.
+      TracingUnrecoverableException: If there is a websocket error.
+    """
+    self._has_received_all_tracing_data = False
+    start_time = time.time()
+    self._trace_data_builder = trace_data_builder
+    try:
+      while True:
+        try:
+          self._inspector_websocket.DispatchNotifications(timeout)
+          start_time = time.time()
+        except websocket.WebSocketTimeoutException:
+          pass
+        except (socket.error, websocket.WebSocketException):
+          raise TracingUnrecoverableException(
+              'Exception raised while collecting tracing data:\n' +
+                  traceback.format_exc())
+
+        if self._has_received_all_tracing_data:
+          break
+
+        elapsed_time = time.time() - start_time
+        if elapsed_time > timeout:
+          raise TracingTimeoutException(
+              'Only received partial trace data due to timeout after %s '
+              'seconds. If the trace data is big, you may want to increase '
+              'the timeout amount.' % elapsed_time)
+    finally:
+      self._trace_data_builder = None
+
+  def _NotificationHandler(self, res):
+    if 'Tracing.dataCollected' == res.get('method'):
+      value = res.get('params', {}).get('value')
+      self._trace_data_builder.AddTraceFor(
+        trace_data_module.CHROME_TRACE_PART, value)
+    elif 'Tracing.tracingComplete' == res.get('method'):
+      stream_handle = res.get('params', {}).get('stream')
+      if not stream_handle:
+        self._has_received_all_tracing_data = True
+        return
+      reader = _DevToolsStreamReader(self._inspector_websocket, stream_handle)
+      reader.Read(self._ReceivedAllTraceDataFromStream)
+
+  def _ReceivedAllTraceDataFromStream(self, trace_handle):
+    self._trace_data_builder.AddTraceFor(
+        trace_data_module.CHROME_TRACE_PART, trace_handle)
+    self._has_received_all_tracing_data = True
+
+  def Close(self):
+    self._inspector_websocket.UnregisterDomain(self._TRACING_DOMAIN)
+    self._inspector_websocket = None
+
+  @decorators.Cache
+  def IsTracingSupported(self):
+    req = {'method': 'Tracing.hasCompleted'}
+    res = self._inspector_websocket.SyncRequest(req, timeout=10)
+    return not res.get('response')
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/tracing_backend_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/tracing_backend_unittest.py
new file mode 100644
index 0000000..3f793ee
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/tracing_backend_unittest.py
@@ -0,0 +1,285 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import timeit
+import unittest
+
+from telemetry import decorators
+from telemetry.internal.backends.chrome_inspector import tracing_backend
+from telemetry.internal.backends.chrome_inspector.tracing_backend import _DevToolsStreamReader
+from telemetry.testing import fakes
+from telemetry.testing import tab_test_case
+from telemetry.timeline import chrome_trace_config
+from telemetry.timeline import model as model_module
+from telemetry.timeline import tracing_config
+from tracing.trace_data import trace_data
+
+
+class TracingBackendTest(tab_test_case.TabTestCase):
+
+  # Number of consecutively requested memory dumps.
+  _REQUESTED_DUMP_COUNT = 3
+
+  @classmethod
+  def CustomizeBrowserOptions(cls, options):
+    options.logging_verbosity = options.VERBOSE_LOGGING
+    options.AppendExtraBrowserArgs([
+        # Memory maps currently cannot be retrieved on sandboxed processes.
+        # See crbug.com/461788.
+        '--no-sandbox',
+    ])
+
+  def setUp(self):
+    super(TracingBackendTest, self).setUp()
+    self._tracing_controller = self._browser.platform.tracing_controller
+    if not self._tracing_controller.IsChromeTracingSupported():
+      self.skipTest('Browser does not support tracing, skipping test.')
+    if not self._browser.supports_memory_dumping:
+      self.skipTest('Browser does not support memory dumping, skipping test.')
+
+  # win: https://github.com/catapult-project/catapult/issues/3131.
+  # chromeos: http://crbug.com/622836.
+  @decorators.Disabled('win', 'chromeos')
+  def testDumpMemorySuccess(self):
+    # Check that dumping memory before tracing starts raises an exception.
+    self.assertRaises(Exception, self._browser.DumpMemory)
+
+    # Start tracing with memory dumps enabled.
+    config = tracing_config.TracingConfig()
+    config.chrome_trace_config.category_filter.AddDisabledByDefault(
+        'disabled-by-default-memory-infra')
+    config.chrome_trace_config.SetMemoryDumpConfig(
+        chrome_trace_config.MemoryDumpConfig())
+    config.enable_chrome_trace = True
+    self._tracing_controller.StartTracing(config)
+
+    # Request several memory dumps in a row and test that they were all
+    # successfully created with unique IDs.
+    expected_dump_ids = []
+    for _ in xrange(self._REQUESTED_DUMP_COUNT):
+      dump_id = self._browser.DumpMemory()
+      self.assertIsNotNone(dump_id)
+      self.assertNotIn(dump_id, expected_dump_ids)
+      expected_dump_ids.append(dump_id)
+
+    tracing_data = self._tracing_controller.StopTracing()
+
+    # Check that clock sync data is in tracing data.
+    clock_sync_found = False
+    trace = tracing_data.GetTraceFor(trace_data.CHROME_TRACE_PART)
+    for event in trace['traceEvents']:
+      if event['name'] == 'clock_sync' or 'ClockSyncEvent' in event['name']:
+        clock_sync_found = True
+        break
+    self.assertTrue(clock_sync_found)
+
+    # Check that dumping memory after tracing stopped raises an exception.
+    self.assertRaises(Exception, self._browser.DumpMemory)
+
+    # Test that trace data is parsable.
+    model = model_module.TimelineModel(tracing_data)
+    self.assertGreater(len(model.processes), 0)
+
+    # Test that the resulting model contains the requested memory dumps in the
+    # correct order (and nothing more).
+    actual_dump_ids = [d.dump_id for d in model.IterGlobalMemoryDumps()]
+    self.assertEqual(actual_dump_ids, expected_dump_ids)
+
+  def testDumpMemoryFailure(self):
+    # Check that dumping memory before tracing starts raises an exception.
+    self.assertRaises(Exception, self._browser.DumpMemory)
+
+    # Start tracing with memory dumps disabled.
+    config = tracing_config.TracingConfig()
+    config.enable_chrome_trace = True
+    self._tracing_controller.StartTracing(config)
+
+    # Check that the method returns None if the dump was not successful.
+    self.assertIsNone(self._browser.DumpMemory())
+
+    tracing_data = self._tracing_controller.StopTracing()
+
+    # Check that dumping memory after tracing stopped raises an exception.
+    self.assertRaises(Exception, self._browser.DumpMemory)
+
+    # Test that trace data is parsable.
+    model = model_module.TimelineModel(tracing_data)
+    self.assertGreater(len(model.processes), 0)
+
+    # Test that the resulting model contains no memory dumps.
+    self.assertEqual(len(list(model.IterGlobalMemoryDumps())), 0)
+
+
+class TracingBackendUnittest(unittest.TestCase):
+  def setUp(self):
+    self._fake_timer = fakes.FakeTimer(tracing_backend)
+    self._inspector_socket = fakes.FakeInspectorWebsocket(self._fake_timer)
+
+  def tearDown(self):
+    self._fake_timer.Restore()
+
+  def _GetRawChromeTracesFor(self, trace_data_builder):
+    data = trace_data_builder.AsData().GetTracesFor(
+        trace_data.CHROME_TRACE_PART)
+    traces = []
+    for d in data:
+      traces.append(d)
+    return traces
+
+  def testCollectTracingDataTimeout(self):
+    self._inspector_socket.AddEvent(
+        'Tracing.dataCollected', {'value': {'traceEvents': [{'ph': 'B'}]}}, 9)
+    self._inspector_socket.AddEvent(
+        'Tracing.dataCollected', {'value': {'traceEvents': [{'ph': 'E'}]}}, 19)
+    self._inspector_socket.AddEvent('Tracing.tracingComplete', {}, 35)
+    backend = tracing_backend.TracingBackend(self._inspector_socket)
+
+    trace_data_builder = trace_data.TraceDataBuilder()
+    # The third response is 16 seconds after the second response, so we expect
+    # a TracingTimeoutException.
+    with self.assertRaises(tracing_backend.TracingTimeoutException):
+      backend._CollectTracingData(trace_data_builder, 10)
+    traces = self._GetRawChromeTracesFor(trace_data_builder)
+    self.assertEqual(2, len(traces))
+    self.assertEqual(1, len(traces[0].get('traceEvents', [])))
+    self.assertEqual(1, len(traces[1].get('traceEvents', [])))
+    self.assertFalse(backend._has_received_all_tracing_data)
+
+  def testCollectTracingDataNoTimeout(self):
+    self._inspector_socket.AddEvent(
+        'Tracing.dataCollected', {'value': {'traceEvents': [{'ph': 'B'}]}}, 9)
+    self._inspector_socket.AddEvent(
+        'Tracing.dataCollected', {'value': {'traceEvents': [{'ph': 'E'}]}}, 14)
+    self._inspector_socket.AddEvent('Tracing.tracingComplete', {}, 19)
+    backend = tracing_backend.TracingBackend(self._inspector_socket)
+    trace_data_builder = trace_data.TraceDataBuilder()
+    backend._CollectTracingData(trace_data_builder, 10)
+    traces = self._GetRawChromeTracesFor(trace_data_builder)
+    self.assertEqual(2, len(traces))
+    self.assertEqual(1, len(traces[0].get('traceEvents', [])))
+    self.assertEqual(1, len(traces[1].get('traceEvents', [])))
+    self.assertTrue(backend._has_received_all_tracing_data)
+
+  def testCollectTracingDataFromStreamNoContainer(self):
+    self._inspector_socket.AddEvent(
+        'Tracing.tracingComplete', {'stream': '42'}, 1)
+    self._inspector_socket.AddAsyncResponse(
+        'IO.read', {'data': '{"traceEvents": [{},{},{'}, 2)
+    self._inspector_socket.AddAsyncResponse(
+        'IO.read', {'data': '},{},{}]}', 'eof': True}, 3)
+    backend = tracing_backend.TracingBackend(self._inspector_socket)
+    trace_data_builder = trace_data.TraceDataBuilder()
+    backend._CollectTracingData(trace_data_builder, 10)
+    trace_events = self._GetRawChromeTracesFor(trace_data_builder)[0].get(
+        'traceEvents', [])
+    self.assertEqual(5, len(trace_events))
+    self.assertTrue(backend._has_received_all_tracing_data)
+
+  def testCollectTracingDataFromStreamJSONContainer(self):
+    self._inspector_socket.AddEvent(
+        'Tracing.tracingComplete', {'stream': '42'}, 1)
+    self._inspector_socket.AddAsyncResponse(
+        'IO.read', {'data': '{"traceEvents": [{},{},{}],'}, 2)
+    self._inspector_socket.AddAsyncResponse(
+        'IO.read', {'data': '"metadata": {"a": "b"}'}, 3)
+    self._inspector_socket.AddAsyncResponse(
+        'IO.read', {'data': '}', 'eof': True}, 4)
+    backend = tracing_backend.TracingBackend(self._inspector_socket)
+    trace_data_builder = trace_data.TraceDataBuilder()
+    backend._CollectTracingData(trace_data_builder, 10)
+    chrome_trace = self._GetRawChromeTracesFor(trace_data_builder)[0]
+
+    self.assertEqual(3, len(chrome_trace.get('traceEvents', [])))
+    self.assertEqual(dict, type(chrome_trace.get('metadata')))
+    self.assertTrue(backend._has_received_all_tracing_data)
+
+  def testDumpMemorySuccess(self):
+    self._inspector_socket.AddResponseHandler(
+        'Tracing.requestMemoryDump',
+        lambda req: {'result': {'success': True, 'dumpGuid': '42abc'}})
+    backend = tracing_backend.TracingBackend(self._inspector_socket)
+
+    self.assertEqual(backend.DumpMemory(), '42abc')
+
+  def testDumpMemoryFailure(self):
+    self._inspector_socket.AddResponseHandler(
+        'Tracing.requestMemoryDump',
+        lambda req: {'result': {'success': False, 'dumpGuid': '42abc'}})
+    backend = tracing_backend.TracingBackend(self._inspector_socket)
+
+    self.assertIsNone(backend.DumpMemory())
+
+  def testStartTracingFailure(self):
+    self._inspector_socket.AddResponseHandler(
+        'Tracing.start',
+        lambda req: {'error': {'message': 'Tracing is already started'}})
+    self._inspector_socket.AddResponseHandler(
+        'Tracing.hasCompleted', lambda req: {})
+    backend = tracing_backend.TracingBackend(self._inspector_socket)
+    config = tracing_config.TracingConfig()
+    self.assertRaisesRegexp(
+        tracing_backend.TracingUnexpectedResponseException,
+        'Tracing is already started',
+        backend.StartTracing, config.chrome_trace_config)
+
+  def testStartTracingWithoutCollection(self):
+    self._inspector_socket.AddResponseHandler('Tracing.start', lambda req: {})
+    self._inspector_socket.AddEvent(
+        'Tracing.dataCollected', {'value': [{'ph': 'B'}]}, 1)
+    self._inspector_socket.AddEvent(
+        'Tracing.dataCollected', {'value': [{'ph': 'E'}]}, 2)
+    self._inspector_socket.AddEvent('Tracing.tracingComplete', {}, 3)
+    self._inspector_socket.AddResponseHandler(
+        'Tracing.hasCompleted', lambda req: {})
+
+    backend = tracing_backend.TracingBackend(self._inspector_socket)
+    config = tracing_config.TracingConfig()
+    backend.StartTracing(config._chrome_trace_config)
+    backend.StopTracing()
+    with self.assertRaisesRegexp(AssertionError, 'Data not collected from .*'):
+      backend.StartTracing(config._chrome_trace_config)
+
+
+class DevToolsStreamPerformanceTest(unittest.TestCase):
+  def setUp(self):
+    self._fake_timer = fakes.FakeTimer(tracing_backend)
+    self._inspector_socket = fakes.FakeInspectorWebsocket(self._fake_timer)
+
+  def _MeasureReadTime(self, count):
+    fake_time = self._fake_timer.time() + 1
+    payload = ','.join(['{}'] * 5000)
+    self._inspector_socket.AddAsyncResponse('IO.read', {'data': '[' + payload},
+                                            fake_time)
+    startClock = timeit.default_timer()
+
+    done = {'done': False}
+    def mark_done(data):
+      del data  # unused
+      done['done'] = True
+
+    reader = _DevToolsStreamReader(self._inspector_socket, 'dummy')
+    reader.Read(mark_done)
+    while not done['done']:
+      fake_time += 1
+      if count > 0:
+        self._inspector_socket.AddAsyncResponse('IO.read', {'data': payload},
+            fake_time)
+      elif count == 0:
+        self._inspector_socket.AddAsyncResponse('IO.read',
+            {'data': payload + ']', 'eof': True}, fake_time)
+      count -= 1
+      self._inspector_socket.DispatchNotifications(10)
+    return timeit.default_timer() - startClock
+
+  def testReadTime(self):
+    n1 = 1000
+    while True:
+      t1 = self._MeasureReadTime(n1)
+      if t1 > 0.01:
+        break
+      n1 *= 5
+    t2 = self._MeasureReadTime(n1 * 10)
+    # Time is an illusion, CPU time is doubly so, allow great deal of tolerance.
+    toleranceFactor = 5
+    self.assertLess(t2, t1 * 10 * toleranceFactor)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/websocket.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/websocket.py
new file mode 100644
index 0000000..2195cb6
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/websocket.py
@@ -0,0 +1,25 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+from __future__ import absolute_import
+
+import socket
+
+# pylint: disable=unused-import
+from websocket import create_connection as _create_connection
+from websocket import WebSocketConnectionClosedException
+from websocket import WebSocketException
+from websocket import WebSocketTimeoutException
+
+
+def create_connection(*args, **kwargs):
+  sockopt = kwargs.get('sockopt', [])
+
+  # By default, we set SO_REUSEADDR on all websockets used by Telemetry.
+  # This prevents spurious address in use errors on Windows.
+  #
+  # TODO(tonyg): We may want to set SO_NODELAY here as well.
+  sockopt.append((socket.SOL_SOCKET, socket.SO_REUSEADDR, 1))
+
+  kwargs['sockopt'] = sockopt
+  return _create_connection(*args, **kwargs)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/websocket_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/websocket_unittest.py
new file mode 100644
index 0000000..426043e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/chrome_inspector/websocket_unittest.py
@@ -0,0 +1,61 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import base64
+import BaseHTTPServer
+import hashlib
+import socket
+import threading
+import unittest
+
+from telemetry.internal.backends.chrome_inspector import websocket
+
+
+# Minimal handler for a local websocket server.
+class _FakeWebSocketHandler(BaseHTTPServer.BaseHTTPRequestHandler):
+  def do_GET(self):
+    key = self.headers.getheader('Sec-WebSocket-Key')
+
+    value = key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
+    hashed = base64.encodestring(hashlib.sha1(value).digest()).strip().lower()
+
+    self.send_response(101)
+
+    self.send_header('Sec-Websocket-Accept', hashed)
+    self.send_header('upgrade', 'websocket')
+    self.send_header('connection', 'upgrade')
+    self.end_headers()
+
+    self.wfile.flush()
+
+
+class TestWebSocket(unittest.TestCase):
+  def testExports(self):
+    self.assertNotEqual(websocket.create_connection, None)
+    self.assertNotEqual(websocket.WebSocketException, None)
+    self.assertNotEqual(websocket.WebSocketTimeoutException, None)
+
+  def testSockOpts(self):
+    httpd = BaseHTTPServer.HTTPServer(('127.0.0.1', 0), _FakeWebSocketHandler)
+    ws_url = 'ws://127.0.0.1:%d' % httpd.server_port
+
+    threading.Thread(target=httpd.handle_request).start()
+    ws = websocket.create_connection(ws_url)
+    try:
+      self.assertNotEquals(
+          ws.sock.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR), 0)
+    finally:
+      ws.close()
+
+    threading.Thread(target=httpd.handle_request).start()
+    ws = websocket.create_connection(
+        ws_url,
+        sockopt=[(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)])
+    try:
+      self.assertNotEquals(
+          ws.sock.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR), 0)
+      self.assertNotEquals(
+          ws.sock.getsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY), 0)
+    finally:
+      ws.close()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/codepen_credentials_backend.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/codepen_credentials_backend.py
new file mode 100644
index 0000000..6cd1a57
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/codepen_credentials_backend.py
@@ -0,0 +1,41 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.internal.backends import form_based_credentials_backend
+
+
+class CodePenCredentialsBackend(
+    form_based_credentials_backend.FormBasedCredentialsBackend):
+
+  @property
+  def logged_in_javascript(self):
+    """Evaluates to true iff already logged in."""
+    return 'document.querySelector(".login-area") === null'
+
+  @property
+  def credentials_type(self):
+    return 'codepen'
+
+  @property
+  def url(self):
+    return 'https://codepen.io/login'
+
+  @property
+  def login_form_id(self):
+    return 'login-login-form'
+
+  @property
+  def login_button_javascript(self):
+    return """
+        LoginSettings.timeOnPageStartTime = 0;
+        document.getElementById("log-in-button").click();
+        """
+
+  @property
+  def login_input_id(self):
+    return 'login-email-field'
+
+  @property
+  def password_input_id(self):
+    return 'login-password-field_'
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/codepen_credentials_backend_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/codepen_credentials_backend_unittest.py
new file mode 100644
index 0000000..2dc8ce7
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/codepen_credentials_backend_unittest.py
@@ -0,0 +1,19 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+from telemetry.internal.backends import codepen_credentials_backend
+from telemetry.internal.backends \
+      import form_based_credentials_backend_unittest_base
+
+
+class TestCodePenCredentialsBackend(
+    form_based_credentials_backend_unittest_base.
+    FormBasedCredentialsBackendUnitTestBase):
+  def setUp(self):
+    self._credentials_type = 'codepen'
+
+  def testLoginUsingMock(self):
+    backend = codepen_credentials_backend.CodePenCredentialsBackend()
+    self._LoginUsingMock(backend, backend.url, backend.login_input_id,
+                         backend.password_input_id, backend.login_form_id,
+                         backend.logged_in_javascript)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/facebook_credentials_backend.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/facebook_credentials_backend.py
new file mode 100644
index 0000000..b1a25dc
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/facebook_credentials_backend.py
@@ -0,0 +1,46 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.internal.backends import form_based_credentials_backend
+
+
+class FacebookCredentialsBackend(
+    form_based_credentials_backend.FormBasedCredentialsBackend):
+
+  @property
+  def logged_in_javascript(self):
+    """Evaluates to true iff already logged in."""
+    return ('document.getElementById("fbNotificationsList")!== null || '
+            'document.getElementById("m_home_notice")!== null')
+
+  @property
+  def credentials_type(self):
+    return 'facebook'
+
+  @property
+  def url(self):
+    return 'http://www.facebook.com/'
+
+  @property
+  def login_form_id(self):
+    return 'login_form'
+
+  @property
+  def login_input_id(self):
+    return 'email'
+
+  @property
+  def password_input_id(self):
+    return 'pass'
+
+class FacebookCredentialsBackend2(FacebookCredentialsBackend):
+  """ Facebook credential backend for https client. """
+
+  @property
+  def credentials_type(self):
+    return 'facebook2'
+
+  @property
+  def url(self):
+    return 'https://www.facebook.com/'
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/facebook_credentials_backend_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/facebook_credentials_backend_unittest.py
new file mode 100644
index 0000000..bbcd34a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/facebook_credentials_backend_unittest.py
@@ -0,0 +1,19 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+from telemetry.internal.backends import facebook_credentials_backend
+from telemetry.internal.backends \
+       import form_based_credentials_backend_unittest_base
+
+
+class TestFacebookCredentialsBackend(
+    form_based_credentials_backend_unittest_base.
+    FormBasedCredentialsBackendUnitTestBase):
+  def setUp(self):
+    self._credentials_type = 'facebook'
+
+  def testLoginUsingMock(self):
+    backend = facebook_credentials_backend.FacebookCredentialsBackend()
+    self._LoginUsingMock(backend, backend.url, backend.login_input_id,
+                         backend.password_input_id, backend.login_form_id,
+                         backend.logged_in_javascript)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/form_based_credentials_backend.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/form_based_credentials_backend.py
new file mode 100644
index 0000000..5fc29e4
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/form_based_credentials_backend.py
@@ -0,0 +1,131 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import logging
+
+from telemetry import decorators
+
+import py_utils
+
+
+class FormBasedCredentialsBackend(object):
+  def __init__(self):
+    self._logged_in = False
+
+  def IsAlreadyLoggedIn(self, tab):
+    return tab.EvaluateJavaScript(self.logged_in_javascript)
+
+  @property
+  def credentials_type(self):
+    raise NotImplementedError()
+
+  @property
+  def url(self):
+    raise NotImplementedError()
+
+  @property
+  def login_form_id(self):
+    raise NotImplementedError()
+
+  @property
+  def login_button_javascript(self):
+    """Some sites have custom JS to log in."""
+    return None
+
+  @property
+  def login_input_id(self):
+    raise NotImplementedError()
+
+  @property
+  def password_input_id(self):
+    raise NotImplementedError()
+
+  @property
+  def logged_in_javascript(self):
+    """Evaluates to true iff already logged in."""
+    raise NotImplementedError()
+
+  def IsLoggedIn(self):
+    return self._logged_in
+
+  def _ResetLoggedInState(self):
+    """Makes the backend think we're not logged in even though we are.
+    Should only be used in unit tests to simulate --dont-override-profile.
+    """
+    self._logged_in = False
+
+  def _WaitForLoginState(self, action_runner):
+    """Waits until it can detect either the login form, or already logged in."""
+    action_runner.WaitForJavaScriptCondition(
+        '(document.querySelector({{ form_id }}) !== null) || ({{ @code }})',
+        form_id='#' + self.login_form_id, code=self.logged_in_javascript,
+        timeout=60)
+
+  def _SubmitLoginFormAndWait(self, action_runner, tab, username, password):
+    """Submits the login form and waits for the navigation."""
+    tab.WaitForDocumentReadyStateToBeInteractiveOrBetter()
+    tab.ExecuteJavaScript(
+        'document.querySelector({{ selector }}).value = {{ username }};',
+        selector='#%s #%s' % (self.login_form_id, self.login_input_id),
+        username=username)
+    tab.ExecuteJavaScript(
+        'document.querySelector({{ selector }}).value = {{ password }};',
+        selector='#%s #%s' % (self.login_form_id, self.password_input_id),
+        password=password)
+    if self.login_button_javascript:
+      tab.ExecuteJavaScript(self.login_button_javascript)
+    else:
+      tab.ExecuteJavaScript(
+          'document.getElementById({{ form_id }}).submit();',
+          form_id=self.login_form_id)
+    # Wait for the form element to disappear as confirmation of the navigation.
+    action_runner.WaitForNavigate()
+
+  # pylint: disable=line-too-long
+  @decorators.Deprecated(2017, 5, 5,
+                         'FormBasedCredentialsBackend is deprecated. Use the '
+                         'login helper modules in '
+                         'https://code.google.com/p/chromium/codesearch#chromium/src/tools/perf/page_sets/login_helpers/'
+                         ' instead.')
+  # pylint: enable=line-too-long
+  def LoginNeeded(self, tab, action_runner, config):
+    """Logs in to a test account.
+
+    Raises:
+      RuntimeError: if could not get credential information.
+    """
+    if self._logged_in:
+      return True
+
+    if 'username' not in config or 'password' not in config:
+      message = ('Credentials for "%s" must include username and password.' %
+                 self.credentials_type)
+      raise RuntimeError(message)
+
+    logging.debug('Logging into %s account...' % self.credentials_type)
+
+    if 'url' in config:
+      url = config['url']
+    else:
+      url = self.url
+
+    try:
+      logging.info('Loading %s...', url)
+      tab.Navigate(url)
+      self._WaitForLoginState(action_runner)
+
+      if self.IsAlreadyLoggedIn(tab):
+        self._logged_in = True
+        return True
+
+      self._SubmitLoginFormAndWait(
+          action_runner, tab, config['username'], config['password'])
+
+      self._logged_in = True
+      return True
+    except py_utils.TimeoutException:
+      logging.warning('Timed out while loading: %s', url)
+      return False
+
+  def LoginNoLongerNeeded(self, tab): # pylint: disable=unused-argument
+    assert self._logged_in
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/form_based_credentials_backend_unittest_base.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/form_based_credentials_backend_unittest_base.py
new file mode 100644
index 0000000..59b4e77
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/form_based_credentials_backend_unittest_base.py
@@ -0,0 +1,58 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from telemetry.testing import simple_mock
+
+_ = simple_mock.DONT_CARE
+
+
+class FormBasedCredentialsBackendUnitTestBase(unittest.TestCase):
+  def setUp(self):
+    self._credentials_type = None
+
+  def testLoginUsingMock(self):
+    raise NotImplementedError()
+
+  def _LoginUsingMock(self, backend, login_page_url, email_element_id,
+                      password_element_id, form_element_id,
+                      already_logged_in_js): # pylint: disable=no-self-use
+    del form_element_id  # Unused.
+    del email_element_id  # Unused.
+    del password_element_id  # Unused.
+    tab = simple_mock.MockObject()
+    ar = simple_mock.MockObject()
+
+    config = {'username': 'blah',
+              'password': 'blargh'}
+
+    tab.ExpectCall('Navigate', login_page_url)
+    tab.ExpectCall(
+        'EvaluateJavaScript', already_logged_in_js).WillReturn(False)
+    tab.ExpectCall('WaitForDocumentReadyStateToBeInteractiveOrBetter')
+
+    ar.ExpectCall(
+        'WaitForJavaScriptCondition',
+        '(document.querySelector({{ form_id }}) !== null) || ({{ @code }})')
+    ar.ExpectCall('WaitForNavigate')
+
+    def VerifyEmail(js):
+      assert '{{ selector }}' in js
+      assert '{{ username }}' in js
+    tab.ExpectCall('ExecuteJavaScript', _).WhenCalled(VerifyEmail)
+
+    def VerifyPw(js):
+      assert '{{ selector }}' in js
+      assert '{{ password }}' in js
+    tab.ExpectCall('ExecuteJavaScript', _).WhenCalled(VerifyPw)
+
+    def VerifySubmit(js):
+      assert '.submit' in js or '.click' in js
+    tab.ExpectCall('ExecuteJavaScript', _).WhenCalled(VerifySubmit)
+
+    # Checking for form still up.
+    tab.ExpectCall('EvaluateJavaScript', _).WillReturn(False)
+
+    backend.LoginNeeded(tab, ar, config)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/google_credentials_backend.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/google_credentials_backend.py
new file mode 100644
index 0000000..0c43182
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/google_credentials_backend.py
@@ -0,0 +1,46 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.internal.backends import form_based_credentials_backend
+
+
+class GoogleCredentialsBackend(
+    form_based_credentials_backend.FormBasedCredentialsBackend):
+
+  @property
+  def logged_in_javascript(self):
+    """Evaluates to true iff already logged in."""
+    return 'document.getElementById("gb")!== null'
+
+  @property
+  def credentials_type(self):
+    return 'google'
+
+  @property
+  def url(self):
+    # pylint: disable=line-too-long
+    # WPR doesn't support having 2 responses for the same URL (with/without
+    # session cookie), so after login behaviour differs with/without wpr.
+    # Sign-in URL is specified directly to overcome this.
+    return 'https://accounts.google.com/ServiceLogin?continue=https%3A%2F%2Faccounts.google.com%2FManageAccount'
+
+  @property
+  def login_form_id(self):
+    return 'gaia_loginform'
+
+  @property
+  def login_input_id(self):
+    return 'Email'
+
+  @property
+  def password_input_id(self):
+    return 'Passwd'
+
+
+class GoogleCredentialsBackend2(GoogleCredentialsBackend):
+  """ Google credential backend for google2 credential. """
+
+  @property
+  def credentials_type(self):
+    return 'google2'
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/google_credentials_backend_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/google_credentials_backend_unittest.py
new file mode 100644
index 0000000..b30c051
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/backends/google_credentials_backend_unittest.py
@@ -0,0 +1,19 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+from telemetry.internal.backends import\
+       form_based_credentials_backend_unittest_base
+from telemetry.internal.backends import google_credentials_backend
+
+
+class TestGoogleCredentialsBackend(
+    form_based_credentials_backend_unittest_base.
+    FormBasedCredentialsBackendUnitTestBase):
+  def setUp(self):
+    self._credentials_type = 'google'
+
+  def testLoginUsingMock(self):
+    backend = google_credentials_backend.GoogleCredentialsBackend()
+    self._LoginUsingMock(backend, backend.url, backend.login_input_id,
+                         backend.password_input_id, backend.login_form_id,
+                         backend.logged_in_javascript)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/bin/README.md b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/bin/README.md
new file mode 100644
index 0000000..eb42425
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/bin/README.md
@@ -0,0 +1,8 @@
+<!-- Copyright 2015 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file.
+-->
+Do not check files into this folder.
+____________________________________
+Files are downloaded to this folder from cloud storage by the BinaryManager,
+and any local files may be overwritten without warning.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/binary_dependencies.json b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/binary_dependencies.json
new file mode 100644
index 0000000..c841c40
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/binary_dependencies.json
@@ -0,0 +1,323 @@
+{
+  "config_type": "BaseConfig",
+  "dependencies": {
+    "avconv": {
+      "cloud_storage_base_folder": "binary_dependencies",
+      "cloud_storage_bucket": "chromium-telemetry",
+      "file_info": {
+        "linux_x86_64": {
+          "cloud_storage_hash": "03896ec9bdc9f1d6fa388f893d8f62f454e7e707",
+          "download_path": "bin/linux/x86_64/avconv"
+        }
+      }
+    },
+    "bitmaptools": {
+      "cloud_storage_base_folder": "binary_dependencies",
+      "cloud_storage_bucket": "chromium-telemetry",
+      "file_info": {
+        "linux_x86_64": {
+          "cloud_storage_hash": "661ce936b3276f7ec3d687ab62be05b96d796f21",
+          "download_path": "bin/linux/x86_64/bitmaptools"
+        },
+        "mac_x86_64": {
+          "cloud_storage_hash": "c7b1bfc6399dc683058e88dac1ef0f877edea74b",
+          "download_path": "bin/mac/x86_64/bitmaptools"
+        },
+        "win_AMD64": {
+          "cloud_storage_hash": "ac4fee89a51662b9d920bce443c19b9b2929b198",
+          "download_path": "bin/win/AMD64/bitmaptools.exe"
+        },
+        "win_x86": {
+          "cloud_storage_hash": "ac4fee89a51662b9d920bce443c19b9b2929b198",
+          "download_path": "bin/win/x86/bitmaptools.exe"
+        }
+      }
+    },
+    "clear_system_cache": {
+      "cloud_storage_base_folder": "binary_dependencies",
+      "cloud_storage_bucket": "chromium-telemetry",
+      "file_info": {
+        "linux_x86_64": {
+          "cloud_storage_hash": "20739db88864685f6a0be66879e34a83813352cc",
+          "download_path": "bin/linux/x86_64/clear_system_cache"
+        },
+        "mac_x86_64": {
+          "cloud_storage_hash": "0f0ef42a9223592fa234ee5d248f6216fee2b677",
+          "download_path": "bin/mac/x86_64/clear_system_cache"
+        },
+        "win_AMD64": {
+          "cloud_storage_hash": "afe4fc71151f3aa176bc7137f0f6c9396bc18f3b",
+          "download_path": "bin/win/AMD64/clear_system_cache.exe"
+        }
+      }
+    },
+    "determine_if_keychain_entry_is_decryptable": {
+      "cloud_storage_base_folder": "binary_dependencies",
+      "cloud_storage_bucket": "chromium-telemetry",
+      "file_info": {
+        "mac_x86_64": {
+          "cloud_storage_hash": "5daabb8e5d485a99efc9139634a8242fa60a25e7",
+          "download_path": "bin/mac/x86_64/determine_if_keychain_entry_is_decryptable"
+        }
+      }
+    },
+    "determine_if_keychain_is_locked": {
+      "cloud_storage_base_folder": "binary_dependencies",
+      "cloud_storage_bucket": "chromium-telemetry",
+      "file_info": {
+        "mac_x86_64": {
+          "cloud_storage_hash": "13a57efae9a680ac0f160b3567e02e81f4ac493c",
+          "download_path": "bin/mac/x86_64/determine_if_keychain_is_locked"
+        }
+      }
+    },
+    "device_forwarder": {
+      "cloud_storage_base_folder": "binary_dependencies",
+      "cloud_storage_bucket": "chromium-telemetry",
+      "file_info": {
+        "android_arm64-v8a": {
+          "cloud_storage_hash": "90cc60cefb497512d95d774045146754c477b433",
+          "download_path": "bin/android/arm64-v8a/device_forwarder"
+        },
+        "android_armeabi-v7a": {
+          "cloud_storage_hash": "3a5ade8fbaffea3b0670bffaa845288db5e4d567",
+          "download_path": "bin/android/armeabi-v7a/device_forwarder"
+        }
+      }
+    },
+    "file_poller": {
+      "cloud_storage_base_folder": "binary_dependencies",
+      "cloud_storage_bucket": "chromium-telemetry",
+      "file_info": {
+        "android_arm64-v8a": {
+          "cloud_storage_hash": "fd5b417f78c7f7d9192a98967058709ded1d399d",
+          "download_path": "bin/android/arm64-v8a/file_poller"
+        },
+        "android_armeabi-v7a": {
+          "cloud_storage_hash": "cf5c8fe920378ce30d057e76591d57f63fd31c1a",
+          "download_path": "bin/android/armeabi-v7a/file_poller"
+        }
+      }
+    },
+    "gdb": {
+      "cloud_storage_base_folder": "binary_dependencies",
+      "cloud_storage_bucket": "chromium-telemetry",
+      "file_info": {
+        "android_arm": {
+          "cloud_storage_hash": "9231584d135fb76bb7075d96ce387679de7ada7b",
+          "download_path": "bin/android/arm/arm-linux-androideabi-gdb"
+        },
+        "android_x64": {
+          "cloud_storage_hash": "09177be2fed00b44df0e777932828425440b23b3",
+          "download_path": "bin/android/x64/x86_64-linux-androideabi-gdb"
+        },
+        "android_x86": {
+          "cloud_storage_hash": "bcf02af039713a48b69b89bd7f0f9c81ed8183a4",
+          "download_path": "bin/android/x86/i686-linux-androideabi-gdb"
+        }
+      }
+    },
+    "host_forwarder": {
+      "cloud_storage_base_folder": "binary_dependencies",
+      "cloud_storage_bucket": "chromium-telemetry",
+      "file_info": {
+        "linux_x86_64": {
+          "cloud_storage_hash": "63653293098d7fe17aa115b147c8d9aa253b0912",
+          "download_path": "bin/linux/x86_64/host_forwarder"
+        }
+      }
+    },
+    "hprof-conv": {
+      "cloud_storage_base_folder": "binary_dependencies",
+      "cloud_storage_bucket": "chromium-telemetry",
+      "file_info": {
+        "linux_x86_64": {
+          "cloud_storage_hash": "0b14eeee2e2a51cc94e361295379f69ee6f7cf8f",
+          "download_path": "../bin/linux/x86_64/hprof-conv"
+        }
+      }
+    },
+    "ipfw": {
+      "cloud_storage_base_folder": "binary_dependencies",
+      "cloud_storage_bucket": "chromium-telemetry",
+      "file_info": {
+        "linux_x86_64": {
+          "cloud_storage_hash": "0c96ed5a618083100cb2269e9c0400ef73708351",
+          "download_path": "bin/linux/x86_64/ipfw"
+        }
+      }
+    },
+    "ipfw_mod": {
+      "cloud_storage_base_folder": "binary_dependencies",
+      "cloud_storage_bucket": "chromium-telemetry",
+      "file_info": {
+        "linux_x86_64": {
+          "cloud_storage_hash": "91d3336cec855d4ed4aafadf7f9d496f3c09b07c",
+          "download_path": "bin/linux/x86_64/ipfw_mod.ko"
+        }
+      }
+    },
+    "md5sum_bin": {
+      "cloud_storage_base_folder": "binary_dependencies",
+      "cloud_storage_bucket": "chromium-telemetry",
+      "file_info": {
+        "android_arm64-v8a": {
+          "cloud_storage_hash": "fca070d85c5c732bc6fb098fc061f53474148dc0",
+          "download_path": "bin/android/arm64-v8a/md5sum_bin"
+        },
+        "android_armeabi-v7a": {
+          "cloud_storage_hash": "293c093c4487b2f1cdff6ed8c93b0214e45c4e7c",
+          "download_path": "bin/android/armeabi-v7a/md5sum_bin"
+        }
+      }
+    },
+    "md5sum_bin_host": {
+      "cloud_storage_base_folder": "binary_dependencies",
+      "cloud_storage_bucket": "chromium-telemetry",
+      "file_info": {
+        "linux_x86_64": {
+          "cloud_storage_hash": "1f5060df87c9332d423bf25a31ffcd4c6c0ef1bf",
+          "download_path": "bin/linux/x86_64/md5sum_bin_host"
+        },
+        "mac_x86_64": {
+          "cloud_storage_hash": "b068e3ba81a1fb7dbfe121edd2dac0c3942eb7e6",
+          "download_path": "bin/mac/x86_64/md5sum_bin_host"
+        }
+      }
+    },
+    "memtrack_helper": {
+      "cloud_storage_base_folder": "binary_dependencies",
+      "cloud_storage_bucket": "chromium-telemetry",
+      "file_info": {
+        "android_arm64-v8a": {
+          "cloud_storage_hash": "c80618de34416bb095562df1c4d2fcf725576ba6",
+          "download_path": "bin/android/arm64-v8a/memtrack_helper"
+        },
+        "android_armeabi-v7a": {
+          "cloud_storage_hash": "cb4437cd23e4e34fbf7930c02da496975c281004",
+          "download_path": "bin/android/armeabi-v7a/memtrack_helper"
+        }
+      }
+    },
+    "minidump_dump": {
+      "cloud_storage_base_folder": "binary_dependencies",
+      "cloud_storage_bucket": "chromium-telemetry",
+      "file_info": {
+        "linux_x86_64": {
+          "cloud_storage_hash": "7e3711b77837f7851f0be5022ecf086f82225809",
+          "download_path": "bin/linux/x86_64/minidump_dump"
+        },
+        "mac_x86_64": {
+          "cloud_storage_hash": "c39bd7a3b9fa6279893b2d759045699d79ce4dcb",
+          "download_path": "bin/mac/x86_64/minidump_dump"
+        }
+      }
+    },
+    "minidump_stackwalk": {
+      "cloud_storage_base_folder": "binary_dependencies",
+      "cloud_storage_bucket": "chromium-telemetry",
+      "file_info": {
+        "linux_x86_64": {
+          "cloud_storage_hash": "9eac751ac8618d7cc3514792719c05e7722e9bdc",
+          "download_path": "bin/linux/x86_64/minidump_stackwalk"
+        },
+        "mac_x86_64": {
+          "cloud_storage_hash": "76c5983fc9e9316a9d4251ba3e68b955c4fc9bf3",
+          "download_path": "bin/mac/x86_64/minidump_stackwalk"
+        }
+      }
+    },
+    "perf": {
+      "cloud_storage_base_folder": "binary_dependencies",
+      "cloud_storage_bucket": "chromium-telemetry",
+      "file_info": {
+        "android_arm64-v8a": {
+          "cloud_storage_hash": "a43abae56791cf7de2b6f0fcbb0f06035b9e4b07",
+          "download_path": "bin/android/arm64-v8a/perf"
+        },
+        "android_armeabi-v7a": {
+          "cloud_storage_hash": "c0173517d500728a8755eefcc1ed26ba31fce13b",
+          "download_path": "bin/android/armeabi-v7a/perf"
+        }
+      }
+    },
+    "perfhost": {
+      "cloud_storage_base_folder": "binary_dependencies",
+      "cloud_storage_bucket": "chromium-telemetry",
+      "file_info": {
+        "linux_x86_64": {
+          "cloud_storage_hash": "ddb9b3508fe3e8b056071fc7c3d069d13635d371",
+          "download_path": "bin/linux/x86_64/perfhost"
+        }
+      }
+    },
+    "perfhost_precise": {
+      "cloud_storage_base_folder": "binary_dependencies",
+      "cloud_storage_bucket": "chromium-telemetry",
+      "file_info": {
+        "linux_x86_64": {
+          "cloud_storage_hash": "ddb9b3508fe3e8b056071fc7c3d069d13635d371",
+          "download_path": "bin/linux/x86_64/perfhost_precise"
+        }
+      }
+    },
+    "perfhost_trusty": {
+      "cloud_storage_base_folder": "binary_dependencies",
+      "cloud_storage_bucket": "chromium-telemetry",
+      "file_info": {
+        "linux_x86_64": {
+          "cloud_storage_hash": "cd0980a3a0b119bc4764bbbac53429e1d9013e70",
+          "download_path": "bin/linux/x86_64/perfhost_trusty"
+        }
+      }
+    },
+    "purge_ashmem": {
+      "cloud_storage_base_folder": "binary_dependencies",
+      "cloud_storage_bucket": "chromium-telemetry",
+      "file_info": {
+        "android_arm64-v8a": {
+          "cloud_storage_hash": "fb1ef95e54208564418494b016ac525594370db8",
+          "download_path": "bin/android/arm64-v8a/purge_ashmem"
+        },
+        "android_armeabi-v7a": {
+          "cloud_storage_hash": "9c1a2aad6e92b1f407428a972c8acf036c039287",
+          "download_path": "bin/android/armeabi-v7a/purge_ashmem"
+        }
+      }
+    },
+    "push_apps_to_background_apk": {
+      "cloud_storage_base_folder": "binary_dependencies",
+      "cloud_storage_bucket": "chromium-telemetry",
+      "file_info": {
+        "android_arm64-v8a": {
+          "cloud_storage_hash": "5b724ddcb63405d1e23de80da9152bf84e72524d",
+          "download_path": "bin/android/arm64-v8a/PushAppsToBackground.apk"
+        },
+        "android_armeabi-v7a": {
+          "cloud_storage_hash": "5b724ddcb63405d1e23de80da9152bf84e72524d",
+          "download_path": "bin/android/armeabi-v7a/PushAppsToBackground.apk"
+        }
+      }
+    },
+    "tcpdump": {
+      "cloud_storage_base_folder": "binary_dependencies",
+      "cloud_storage_bucket": "chromium-telemetry",
+      "file_info": {
+        "android_armeabi-v7a": {
+          "cloud_storage_hash": "6cf2d56024208a08ceaf3a04ea98d5a362396fca",
+          "download_path": "bin/android/armeabi-v7a/tcpdump"
+        }
+      }
+    },
+    "winring0": {
+      "cloud_storage_base_folder": "binary_dependencies",
+      "cloud_storage_bucket": "chromium-telemetry",
+      "file_info": {
+        "win_AMD64": {
+          "cloud_storage_hash": "978305805fb52d838b3b4b09e9327da81f6973f7",
+          "download_path": "bin/win/AMD64/winring0.zip"
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/__init__.py
new file mode 100644
index 0000000..50b23df
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/__init__.py
@@ -0,0 +1,3 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/browser.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/browser.py
new file mode 100644
index 0000000..2d40e64
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/browser.py
@@ -0,0 +1,365 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+import sys
+
+from py_utils import cloud_storage  # pylint: disable=import-error
+
+from telemetry.core import exceptions
+from telemetry.core import profiling_controller
+from telemetry import decorators
+from telemetry.internal import app
+from telemetry.internal.backends import browser_backend
+from telemetry.internal.browser import browser_credentials
+from telemetry.internal.browser import extension_dict
+from telemetry.internal.browser import tab_list
+from telemetry.internal.browser import web_contents
+from telemetry.internal.util import exception_formatter
+
+
+class Browser(app.App):
+  """A running browser instance that can be controlled in a limited way.
+
+  To create a browser instance, use browser_finder.FindBrowser.
+
+  Be sure to clean up after yourself by calling Close() when you are done with
+  the browser. Or better yet:
+    browser_to_create = FindBrowser(options)
+    with browser_to_create.Create(options) as browser:
+      ... do all your operations on browser here
+  """
+  def __init__(self, backend, platform_backend, credentials_path):
+    super(Browser, self).__init__(app_backend=backend,
+                                  platform_backend=platform_backend)
+    try:
+      self._browser_backend = backend
+      self._platform_backend = platform_backend
+      self._tabs = tab_list.TabList(backend.tab_list_backend)
+      self.credentials = browser_credentials.BrowserCredentials()
+      self.credentials.credentials_path = credentials_path
+      self._platform_backend.DidCreateBrowser(self, self._browser_backend)
+      browser_options = self._browser_backend.browser_options
+      self.platform.FlushDnsCache()
+      if browser_options.clear_sytem_cache_for_browser_and_profile_on_start:
+        if self.platform.CanFlushIndividualFilesFromSystemCache():
+          self.platform.FlushSystemCacheForDirectory(
+              self._browser_backend.profile_directory)
+          self.platform.FlushSystemCacheForDirectory(
+              self._browser_backend.browser_directory)
+        elif self.platform.SupportFlushEntireSystemCache():
+          self.platform.FlushEntireSystemCache()
+        else:
+          logging.warning('Flush system cache is not supported. ' +
+              'Did not flush system cache.')
+
+      self._browser_backend.SetBrowser(self)
+      self._browser_backend.Start()
+      self._LogBrowserInfo()
+      self._platform_backend.DidStartBrowser(self, self._browser_backend)
+      self._profiling_controller = profiling_controller.ProfilingController(
+          self._browser_backend.profiling_controller_backend)
+    except Exception:
+      exc_info = sys.exc_info()
+      logging.exception('Failure while starting browser backend.')
+      try:
+        self._platform_backend.WillCloseBrowser(self, self._browser_backend)
+      except Exception:
+        exception_formatter.PrintFormattedException(
+            msg='Exception raised while closing platform backend')
+      raise exc_info[0], exc_info[1], exc_info[2]
+
+  @property
+  def profiling_controller(self):
+    return self._profiling_controller
+
+  @property
+  def browser_type(self):
+    return self.app_type
+
+  @property
+  def supports_extensions(self):
+    return self._browser_backend.supports_extensions
+
+  @property
+  def supports_tab_control(self):
+    return self._browser_backend.supports_tab_control
+
+  @property
+  def tabs(self):
+    return self._tabs
+
+  @property
+  def foreground_tab(self):
+    for i in xrange(len(self._tabs)):
+      # The foreground tab is the first (only) one that isn't hidden.
+      # This only works through luck on Android, due to crbug.com/322544
+      # which means that tabs that have never been in the foreground return
+      # document.hidden as false; however in current code the Android foreground
+      # tab is always tab 0, which will be the first one that isn't hidden
+      if self._tabs[i].EvaluateJavaScript('!document.hidden'):
+        return self._tabs[i]
+    raise Exception("No foreground tab found")
+
+  @property
+  @decorators.Cache
+  def extensions(self):
+    if not self.supports_extensions:
+      raise browser_backend.ExtensionsNotSupportedException(
+          'Extensions not supported')
+    return extension_dict.ExtensionDict(self._browser_backend.extension_backend)
+
+  def _LogBrowserInfo(self):
+    logging.info('OS: %s %s',
+                 self._platform_backend.platform.GetOSName(),
+                 self._platform_backend.platform.GetOSVersionName())
+    if self.supports_system_info:
+      system_info = self.GetSystemInfo()
+      if system_info.model_name:
+        logging.info('Model: %s', system_info.model_name)
+      if system_info.gpu:
+        for i, device in enumerate(system_info.gpu.devices):
+          logging.info('GPU device %d: %s', i, device)
+        if system_info.gpu.aux_attributes:
+          logging.info('GPU Attributes:')
+          for k, v in sorted(system_info.gpu.aux_attributes.iteritems()):
+            logging.info('  %-20s: %s', k, v)
+        if system_info.gpu.feature_status:
+          logging.info('Feature Status:')
+          for k, v in sorted(system_info.gpu.feature_status.iteritems()):
+            logging.info('  %-20s: %s', k, v)
+        if system_info.gpu.driver_bug_workarounds:
+          logging.info('Driver Bug Workarounds:')
+          for workaround in system_info.gpu.driver_bug_workarounds:
+            logging.info('  %s', workaround)
+      else:
+        logging.info('No GPU devices')
+    else:
+      logging.warning('System info not supported')
+
+  def _GetStatsCommon(self, pid_stats_function):
+    browser_pid = self._browser_backend.pid
+    result = {
+        'Browser': dict(pid_stats_function(browser_pid), **{'ProcessCount': 1}),
+        'Renderer': {'ProcessCount': 0},
+        'Gpu': {'ProcessCount': 0},
+        'Other': {'ProcessCount': 0}
+    }
+    process_count = 1
+    for child_pid in self._platform_backend.GetChildPids(browser_pid):
+      try:
+        child_cmd_line = self._platform_backend.GetCommandLine(child_pid)
+        child_stats = pid_stats_function(child_pid)
+      except exceptions.ProcessGoneException:
+        # It is perfectly fine for a process to have gone away between calling
+        # GetChildPids() and then further examining it.
+        continue
+      child_process_name = self._browser_backend.GetProcessName(child_cmd_line)
+      process_name_type_key_map = {'gpu-process': 'Gpu', 'renderer': 'Renderer'}
+      if child_process_name in process_name_type_key_map:
+        child_process_type_key = process_name_type_key_map[child_process_name]
+      else:
+        # TODO: identify other process types (zygote, plugin, etc), instead of
+        # lumping them in a single category.
+        child_process_type_key = 'Other'
+      result[child_process_type_key]['ProcessCount'] += 1
+      for k, v in child_stats.iteritems():
+        if k in result[child_process_type_key]:
+          result[child_process_type_key][k] += v
+        else:
+          result[child_process_type_key][k] = v
+      process_count += 1
+    for v in result.itervalues():
+      if v['ProcessCount'] > 1:
+        for k in v.keys():
+          if k.endswith('Peak'):
+            del v[k]
+      del v['ProcessCount']
+    result['ProcessCount'] = process_count
+    return result
+
+  @property
+  def memory_stats(self):
+    """Returns a dict of memory statistics for the browser:
+    { 'Browser': {
+        'VM': R,
+        'VMPeak': S,
+        'WorkingSetSize': T,
+        'WorkingSetSizePeak': U,
+        'ProportionalSetSize': V,
+        'PrivateDirty': W
+      },
+      'Gpu': {
+        'VM': R,
+        'VMPeak': S,
+        'WorkingSetSize': T,
+        'WorkingSetSizePeak': U,
+        'ProportionalSetSize': V,
+        'PrivateDirty': W
+      },
+      'Renderer': {
+        'VM': R,
+        'VMPeak': S,
+        'WorkingSetSize': T,
+        'WorkingSetSizePeak': U,
+        'ProportionalSetSize': V,
+        'PrivateDirty': W
+      },
+      'SystemCommitCharge': X,
+      'SystemTotalPhysicalMemory': Y,
+      'ProcessCount': Z,
+    }
+    Any of the above keys may be missing on a per-platform basis.
+    """
+    self._platform_backend.PurgeUnpinnedMemory()
+    result = self._GetStatsCommon(self._platform_backend.GetMemoryStats)
+    commit_charge = self._platform_backend.GetSystemCommitCharge()
+    if commit_charge:
+      result['SystemCommitCharge'] = commit_charge
+    total = self._platform_backend.GetSystemTotalPhysicalMemory()
+    if total:
+      result['SystemTotalPhysicalMemory'] = total
+    return result
+
+  @property
+  def cpu_stats(self):
+    """Returns a dict of cpu statistics for the system.
+    { 'Browser': {
+        'CpuProcessTime': S,
+        'TotalTime': T
+      },
+      'Gpu': {
+        'CpuProcessTime': S,
+        'TotalTime': T
+      },
+      'Renderer': {
+        'CpuProcessTime': S,
+        'TotalTime': T
+      }
+    }
+    Any of the above keys may be missing on a per-platform basis.
+    """
+    result = self._GetStatsCommon(self._platform_backend.GetCpuStats)
+    del result['ProcessCount']
+
+    # We want a single time value, not the sum for all processes.
+    cpu_timestamp = self._platform_backend.GetCpuTimestamp()
+    for process_type in result:
+      # Skip any process_types that are empty
+      if not len(result[process_type]):
+        continue
+      result[process_type].update(cpu_timestamp)
+    return result
+
+  def Close(self):
+    """Closes this browser."""
+    try:
+      if self._browser_backend.IsBrowserRunning():
+        self._platform_backend.WillCloseBrowser(self, self._browser_backend)
+
+      self._browser_backend.profiling_controller_backend.WillCloseBrowser()
+      if self._browser_backend.supports_uploading_logs:
+        try:
+          self._browser_backend.UploadLogsToCloudStorage()
+        except cloud_storage.CloudStorageError as e:
+          logging.error('Cannot upload browser log: %s' % str(e))
+    finally:
+      self._browser_backend.Close()
+      self.credentials = None
+
+  def Foreground(self):
+    """Ensure the browser application is moved to the foreground."""
+    return self._browser_backend.Foreground()
+
+  def Background(self):
+    """Ensure the browser application is moved to the background."""
+    return self._browser_backend.Background()
+
+  def GetStandardOutput(self):
+    return self._browser_backend.GetStandardOutput()
+
+  def GetLogFileContents(self):
+    return self._browser_backend.GetLogFileContents()
+
+  def GetStackTrace(self):
+    return self._browser_backend.GetStackTrace()
+
+  def GetMostRecentMinidumpPath(self):
+    """Returns the path to the most recent minidump."""
+    return self._browser_backend.GetMostRecentMinidumpPath()
+
+  def GetAllMinidumpPaths(self):
+    """Returns all minidump paths available in the backend."""
+    return self._browser_backend.GetAllMinidumpPaths()
+
+  def GetAllUnsymbolizedMinidumpPaths(self):
+    """Returns paths to all minidumps that have not already been
+    symbolized."""
+    return self._browser_backend.GetAllUnsymbolizedMinidumpPaths()
+
+  def SymbolizeMinidump(self, minidump_path):
+    """Given a minidump path, this method returns a tuple with the
+    first value being whether or not the minidump was able to be
+    symbolized and the second being that symbolized dump when true
+    and error message when false."""
+    return self._browser_backend.SymbolizeMinidump(minidump_path)
+
+  @property
+  def supports_system_info(self):
+    return self._browser_backend.supports_system_info
+
+  def GetSystemInfo(self):
+    """Returns low-level information about the system, if available.
+
+       See the documentation of the SystemInfo class for more details."""
+    return self._browser_backend.GetSystemInfo()
+
+  @property
+  def supports_memory_dumping(self):
+    return self._browser_backend.supports_memory_dumping
+
+  def DumpMemory(self, timeout=web_contents.DEFAULT_WEB_CONTENTS_TIMEOUT):
+    return self._browser_backend.DumpMemory(timeout)
+
+  @property
+  def supports_overriding_memory_pressure_notifications(self):
+    return (
+        self._browser_backend.supports_overriding_memory_pressure_notifications)
+
+  def SetMemoryPressureNotificationsSuppressed(
+      self, suppressed, timeout=web_contents.DEFAULT_WEB_CONTENTS_TIMEOUT):
+    self._browser_backend.SetMemoryPressureNotificationsSuppressed(
+        suppressed, timeout)
+
+  def SimulateMemoryPressureNotification(
+      self, pressure_level, timeout=web_contents.DEFAULT_WEB_CONTENTS_TIMEOUT):
+    self._browser_backend.SimulateMemoryPressureNotification(
+        pressure_level, timeout)
+
+  @property
+  def supports_cpu_metrics(self):
+    return self._browser_backend.supports_cpu_metrics
+
+  @property
+  def supports_memory_metrics(self):
+    return self._browser_backend.supports_memory_metrics
+
+  @property
+  def supports_power_metrics(self):
+    return self._browser_backend.supports_power_metrics
+
+  def DumpStateUponFailure(self):
+    logging.info('*************** BROWSER STANDARD OUTPUT ***************')
+    try:  # pylint: disable=broad-except
+      logging.info(self.GetStandardOutput())
+    except Exception:
+      logging.exception('Failed to get browser standard output:')
+    logging.info('*********** END OF BROWSER STANDARD OUTPUT ************')
+
+    logging.info('********************* BROWSER LOG *********************')
+    try:  # pylint: disable=broad-except
+      logging.info(self.GetLogFileContents())
+    except Exception:
+      logging.exception('Failed to get browser log:')
+    logging.info('***************** END OF BROWSER LOG ******************')
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/browser_credentials.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/browser_credentials.py
new file mode 100644
index 0000000..38f99c5
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/browser_credentials.py
@@ -0,0 +1,149 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import json
+import logging
+import os
+
+from telemetry.core import util
+from telemetry.internal.backends import codepen_credentials_backend
+from telemetry.internal.backends import facebook_credentials_backend
+from telemetry.internal.backends import google_credentials_backend
+from telemetry.testing import options_for_unittests
+
+
+class CredentialsError(Exception):
+  """Error that can be thrown when logging in."""
+
+
+class BrowserCredentials(object):
+  def __init__(self, backends=None):
+    self._credentials = {}
+    self._credentials_path = None
+    self._extra_credentials = {}
+
+    if backends is None:
+      backends = [
+        codepen_credentials_backend.CodePenCredentialsBackend(),
+        facebook_credentials_backend.FacebookCredentialsBackend(),
+        facebook_credentials_backend.FacebookCredentialsBackend2(),
+        google_credentials_backend.GoogleCredentialsBackend(),
+        google_credentials_backend.GoogleCredentialsBackend2()]
+
+    self._backends = {}
+    for backend in backends:
+      self._backends[backend.credentials_type] = backend
+
+  def AddBackend(self, backend):
+    assert backend.credentials_type not in self._backends
+    self._backends[backend.credentials_type] = backend
+
+  def IsLoggedIn(self, credentials_type):
+    if credentials_type not in self._backends:
+      raise CredentialsError(
+          'Unrecognized credentials type: %s', credentials_type)
+    if credentials_type not in self._credentials:
+      return False
+    return self._backends[credentials_type].IsLoggedIn()
+
+  def CanLogin(self, credentials_type):
+    if credentials_type not in self._backends:
+      raise CredentialsError(
+          'Unrecognized credentials type: %s', credentials_type)
+    return credentials_type in self._credentials
+
+  def LoginNeeded(self, tab, credentials_type):
+    if credentials_type not in self._backends:
+      raise CredentialsError(
+          'Unrecognized credentials type: %s', credentials_type)
+    if credentials_type not in self._credentials:
+      return False
+    runner = tab.action_runner
+    return self._backends[credentials_type].LoginNeeded(
+      tab, runner, self._credentials[credentials_type])
+
+  def LoginNoLongerNeeded(self, tab, credentials_type):
+    assert credentials_type in self._backends
+    self._backends[credentials_type].LoginNoLongerNeeded(tab)
+
+  @property
+  def credentials_path(self):
+    return self._credentials_path
+
+  @credentials_path.setter
+  def credentials_path(self, credentials_path):
+    self._credentials_path = credentials_path
+    self._RebuildCredentials()
+
+  def Add(self, credentials_type, data):
+    if credentials_type not in self._extra_credentials:
+      self._extra_credentials[credentials_type] = {}
+    for k, v in data.items():
+      assert k not in self._extra_credentials[credentials_type]
+      self._extra_credentials[credentials_type][k] = v
+    self._RebuildCredentials()
+
+  def _ResetLoggedInState(self):
+    """Makes the backends think we're not logged in even though we are.
+    Should only be used in unit tests to simulate --dont-override-profile.
+    """
+    for backend in self._backends.keys():
+      # pylint: disable=protected-access
+      self._backends[backend]._ResetLoggedInState()
+
+  def _RebuildCredentials(self):
+    credentials = {}
+    if self._credentials_path == None:
+      pass
+    elif os.path.exists(self._credentials_path):
+      with open(self._credentials_path, 'r') as f:
+        credentials = json.loads(f.read())
+
+    # TODO(nduca): use system keychain, if possible.
+    homedir_credentials_path = os.path.expanduser('~/.telemetry-credentials')
+    homedir_credentials = {}
+
+    if (not options_for_unittests.GetCopy() and
+        os.path.exists(homedir_credentials_path)):
+      logging.info("Found ~/.telemetry-credentials. Its contents will be used "
+                   "when no other credentials can be found.")
+      with open(homedir_credentials_path, 'r') as f:
+        homedir_credentials = json.loads(f.read())
+
+    self._credentials = {}
+    all_keys = set(credentials.keys()).union(
+      homedir_credentials.keys()).union(
+      self._extra_credentials.keys())
+
+    for k in all_keys:
+      if k in credentials:
+        self._credentials[k] = credentials[k]
+      if k in homedir_credentials:
+        logging.info("Will use ~/.telemetry-credentials for %s logins." % k)
+        self._credentials[k] = homedir_credentials[k]
+      if k in self._extra_credentials:
+        self._credentials[k] = self._extra_credentials[k]
+
+  def WarnIfMissingCredentials(self, page):
+    if page.credentials and not self.CanLogin(page.credentials):
+      files_to_tweak = []
+      if page.credentials_path:
+        files_to_tweak.append(page.credentials_path)
+      files_to_tweak.append('~/.telemetry-credentials')
+
+      example_credentials_file = os.path.join(
+          util.GetTelemetryDir(), 'examples', 'credentials_example.json')
+
+      logging.warning("""
+        Credentials for %s were not found. page %s will not be tested.
+
+        To fix this, either follow the instructions to authenticate to gsutil
+        here:
+        http://www.chromium.org/developers/telemetry/upload_to_cloud_storage,
+
+        or add your own credentials to:
+            %s
+        An example credentials file you can copy from is here:
+            %s\n""" % (page.credentials, page, ' or '.join(files_to_tweak),
+                       example_credentials_file))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/browser_credentials_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/browser_credentials_unittest.py
new file mode 100644
index 0000000..79ebcc0
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/browser_credentials_unittest.py
@@ -0,0 +1,76 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import os
+import tempfile
+import unittest
+
+from telemetry.internal.browser import browser_credentials
+
+
+SIMPLE_CREDENTIALS_STRING = """
+{
+  "google": {
+    "username": "example",
+    "password": "asdf"
+  }
+}
+"""
+
+class BackendStub(object):
+  def __init__(self, credentials_type):
+    self.login_needed_called = None
+    self.login_no_longer_needed_called = None
+    self.credentials_type = credentials_type
+
+  def LoginNeeded(self, config, _, tab):
+    self.login_needed_called = (config, tab)
+    return True
+
+  def LoginNoLongerNeeded(self, tab):
+    self.login_no_longer_needed_called = (tab, )
+
+
+class TestBrowserCredentials(unittest.TestCase):
+  def testCredentialsInfrastructure(self):
+    google_backend = BackendStub("google")
+    othersite_backend = BackendStub("othersite")
+    browser_cred = browser_credentials.BrowserCredentials(
+      [google_backend,
+       othersite_backend])
+    try:
+      with tempfile.NamedTemporaryFile(delete=False) as f:
+        f.write(SIMPLE_CREDENTIALS_STRING)
+
+      browser_cred.credentials_path = f.name
+
+      # Should true because it has a password and a backend.
+      self.assertTrue(browser_cred.CanLogin('google'))
+
+      # Should be false succeed because it has no password.
+      self.assertFalse(browser_cred.CanLogin('othersite'))
+
+      # Should fail because it has no backend.
+      self.assertRaises(
+        Exception,
+        lambda: browser_cred.CanLogin('foobar'))
+
+      class FakeTab(object):
+        def __init__(self):
+          self.action_runner = None
+
+      tab = FakeTab()
+      ret = browser_cred.LoginNeeded(tab, 'google')
+      self.assertTrue(ret)
+      self.assertTrue(google_backend.login_needed_called is not None)
+      self.assertEqual(tab, google_backend.login_needed_called[0])
+      self.assertEqual("example",
+                       google_backend.login_needed_called[1]["username"])
+      self.assertEqual("asdf",
+                       google_backend.login_needed_called[1]["password"])
+
+      browser_cred.LoginNoLongerNeeded(tab, 'google')
+      self.assertTrue(google_backend.login_no_longer_needed_called is not None)
+      self.assertEqual(tab, google_backend.login_no_longer_needed_called[0])
+    finally:
+      os.remove(f.name)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/browser_finder.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/browser_finder.py
new file mode 100644
index 0000000..242f491
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/browser_finder.py
@@ -0,0 +1,179 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Finds browsers that can be controlled by telemetry."""
+
+import logging
+import operator
+
+from telemetry import decorators
+from telemetry.internal.backends.chrome import android_browser_finder
+from telemetry.internal.backends.chrome import cros_browser_finder
+from telemetry.internal.backends.chrome import desktop_browser_finder
+from telemetry.internal.backends.chrome import ios_browser_finder
+from telemetry.internal.browser import browser_finder_exceptions
+from telemetry.internal.platform import device_finder
+
+BROWSER_FINDERS = [
+  desktop_browser_finder,
+  android_browser_finder,
+  cros_browser_finder,
+  ios_browser_finder,
+  ]
+
+
+def FindAllBrowserTypes(options):
+  return reduce(operator.add,
+                [bf.FindAllBrowserTypes(options) for bf in BROWSER_FINDERS])
+
+
+@decorators.Cache
+def FindBrowser(options):
+  """Finds the best PossibleBrowser object given a BrowserOptions object.
+
+  Args:
+    A BrowserOptions object.
+
+  Returns:
+    A PossibleBrowser object.
+
+  Raises:
+    BrowserFinderException: Options improperly set, or an error occurred.
+  """
+  if options.__class__.__name__ == '_FakeBrowserFinderOptions':
+    return options.fake_possible_browser
+  if options.browser_type == 'exact' and options.browser_executable == None:
+    raise browser_finder_exceptions.BrowserFinderException(
+        '--browser=exact requires --browser-executable to be set.')
+  if options.browser_type != 'exact' and options.browser_executable != None:
+    raise browser_finder_exceptions.BrowserFinderException(
+        '--browser-executable requires --browser=exact.')
+
+  if options.browser_type == 'cros-chrome' and options.cros_remote == None:
+    raise browser_finder_exceptions.BrowserFinderException(
+        'browser_type=cros-chrome requires cros_remote be set.')
+  if (options.browser_type != 'cros-chrome' and
+      options.browser_type != 'cros-chrome-guest' and
+      options.cros_remote != None):
+    raise browser_finder_exceptions.BrowserFinderException(
+        '--remote requires --browser=cros-chrome or cros-chrome-guest.')
+
+  devices = device_finder.GetDevicesMatchingOptions(options)
+  browsers = []
+  default_browsers = []
+  for device in devices:
+    for finder in BROWSER_FINDERS:
+      if(options.browser_type and options.browser_type != 'any' and
+         options.browser_type not in finder.FindAllBrowserTypes(options)):
+        continue
+      curr_browsers = finder.FindAllAvailableBrowsers(options, device)
+      new_default_browser = finder.SelectDefaultBrowser(curr_browsers)
+      if new_default_browser:
+        default_browsers.append(new_default_browser)
+      browsers.extend(curr_browsers)
+
+  if options.browser_type == None:
+    if default_browsers:
+      default_browser = sorted(default_browsers,
+                               key=lambda b: b.last_modification_time())[-1]
+
+      logging.warning('--browser omitted. Using most recent local build: %s' %
+                      default_browser.browser_type)
+      default_browser.UpdateExecutableIfNeeded()
+      return default_browser
+
+    if len(browsers) == 1:
+      logging.warning('--browser omitted. Using only available browser: %s' %
+                      browsers[0].browser_type)
+      browsers[0].UpdateExecutableIfNeeded()
+      return browsers[0]
+
+    raise browser_finder_exceptions.BrowserTypeRequiredException(
+        '--browser must be specified. Available browsers:\n%s' %
+        '\n'.join(sorted(set([b.browser_type for b in browsers]))))
+
+  if options.browser_type == 'any':
+    types = FindAllBrowserTypes(options)
+    def CompareBrowsersOnTypePriority(x, y):
+      x_idx = types.index(x.browser_type)
+      y_idx = types.index(y.browser_type)
+      return x_idx - y_idx
+    browsers.sort(CompareBrowsersOnTypePriority)
+    if len(browsers) >= 1:
+      browsers[0].UpdateExecutableIfNeeded()
+      return browsers[0]
+    else:
+      return None
+
+  matching_browsers = [b for b in browsers
+      if b.browser_type == options.browser_type and
+      b.SupportsOptions(options.browser_options)]
+
+  chosen_browser = None
+  if len(matching_browsers) == 1:
+    chosen_browser = matching_browsers[0]
+  elif len(matching_browsers) > 1:
+    logging.warning('Multiple browsers of the same type found: %s' % (
+                    repr(matching_browsers)))
+    chosen_browser = sorted(matching_browsers,
+                            key=lambda b: b.last_modification_time())[-1]
+
+  if chosen_browser:
+    logging.info('Chose browser: %s' % (repr(chosen_browser)))
+    chosen_browser.UpdateExecutableIfNeeded()
+
+  return chosen_browser
+
+
+@decorators.Cache
+def GetAllAvailableBrowsers(options, device):
+  """Returns a list of available browsers on the device.
+
+  Args:
+    options: A BrowserOptions object.
+    device: The target device, which can be None.
+
+  Returns:
+    A list of browser instances.
+
+  Raises:
+    BrowserFinderException: Options are improperly set, or an error occurred.
+  """
+  if not device:
+    return []
+  possible_browsers = []
+  for browser_finder in BROWSER_FINDERS:
+    possible_browsers.extend(
+      browser_finder.FindAllAvailableBrowsers(options, device))
+  return possible_browsers
+
+
+@decorators.Cache
+def GetAllAvailableBrowserTypes(options):
+  """Returns a list of available browser types.
+
+  Args:
+    options: A BrowserOptions object.
+
+  Returns:
+    A list of browser type strings.
+
+  Raises:
+    BrowserFinderException: Options are improperly set, or an error occurred.
+  """
+  devices = device_finder.GetDevicesMatchingOptions(options)
+  possible_browsers = []
+  for device in devices:
+    possible_browsers.extend(GetAllAvailableBrowsers(options, device))
+  type_list = set([browser.browser_type for browser in possible_browsers])
+  # The reference build should be available for mac, linux and win, but the
+  # desktop browser finder won't return it in the list of browsers.
+  for browser in possible_browsers:
+    if (browser.target_os == 'darwin' or browser.target_os.startswith('linux')
+        or browser.target_os.startswith('win')):
+      type_list.add('reference')
+      break
+  type_list = list(type_list)
+  type_list.sort()
+  return type_list
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/browser_finder_exceptions.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/browser_finder_exceptions.py
new file mode 100644
index 0000000..76fbdcf
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/browser_finder_exceptions.py
@@ -0,0 +1,11 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+class BrowserTypeRequiredException(Exception):
+  pass
+
+
+class BrowserFinderException(Exception):
+  pass
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/browser_info.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/browser_info.py
new file mode 100644
index 0000000..5db7150
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/browser_info.py
@@ -0,0 +1,70 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+_check_webgl_supported_script = """
+(function () {
+  var c = document.createElement('canvas');
+  var gl = c.getContext('webgl', { failIfMajorPerformanceCaveat: true });
+  if (gl == null) {
+    gl = c.getContext('experimental-webgl',
+        { failIfMajorPerformanceCaveat: true });
+    if (gl == null) {
+      return false;
+    }
+  }
+  return true;
+})();
+"""
+
+class BrowserInfo(object):
+  """A wrapper around browser object that allows looking up infos of the
+  browser.
+  """
+  def __init__(self, browser):
+    self._browser = browser
+
+  def HasWebGLSupport(self):
+    result = False
+    # If no tab is opened, open one and close it after evaluate
+    # _check_webgl_supported_script
+    if len(self._browser.tabs) == 0 and self._browser.supports_tab_control:
+      self._browser.tabs.New()
+      tab = self._browser.tabs[0]
+      result = tab.EvaluateJavaScript(_check_webgl_supported_script)
+      tab.Close()
+    elif len(self._browser.tabs) > 0:
+      tab = self._browser.tabs[0]
+      result = tab.EvaluateJavaScript(_check_webgl_supported_script)
+    return result
+
+  def HasFlingGestureSupport(self):
+    # Synthetic fling gestures weren't properly tracked by telemetry until
+    # Chromium branch number 2339 (see crrev.com/1003023002).
+    # TODO(jdduke): Resolve lack of branch number support for content_shell
+    # targets, see crbug.com/470273.
+    branch_num = (
+        self._browser._browser_backend.devtools_client.GetChromeBranchNumber())
+    return branch_num >= 2339
+
+  def HasDiagonalScrollingSupport(self):
+    # Diagonal scrolling was not supported in the ScrollAction until
+    # Chromium branch number 2332
+    branch_num = (
+        self._browser._browser_backend.devtools_client.GetChromeBranchNumber())
+    return branch_num >= 2332
+
+  def HasRepeatableSynthesizeScrollGesture(self):
+    # Repeatable SynthesizeScrollGesture scrolling was not supported until
+    # Chromium branch number 2480
+    branch_num = (
+        self._browser._browser_backend.devtools_client.GetChromeBranchNumber())
+    return branch_num >= 2480
+
+  @property
+  def browser_type(self):
+    return self._browser.browser_type
+
+  @property
+  def browser(self):
+    return self._browser
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/browser_options.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/browser_options.py
new file mode 100644
index 0000000..67b6d16
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/browser_options.py
@@ -0,0 +1,470 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import copy
+import logging
+import optparse
+import os
+import shlex
+import socket
+import sys
+
+from py_utils import cloud_storage  # pylint: disable=import-error
+
+from telemetry.core import platform
+from telemetry.core import util
+from telemetry.internal.browser import browser_finder
+from telemetry.internal.browser import browser_finder_exceptions
+from telemetry.internal.browser import profile_types
+from telemetry.internal.platform import device_finder
+from telemetry.internal.platform import remote_platform_options
+from telemetry.internal.platform.profiler import profiler_finder
+from telemetry.internal.util import binary_manager
+from telemetry.util import wpr_modes
+
+
+class BrowserFinderOptions(optparse.Values):
+  """Options to be used for discovering a browser."""
+
+  def __init__(self, browser_type=None):
+    optparse.Values.__init__(self)
+
+    self.browser_type = browser_type
+    self.browser_executable = None
+    self.chrome_root = None  # Path to src/
+    self.chromium_output_dir = None  # E.g.: out/Debug
+    self.device = None
+    self.cros_ssh_identity = None
+
+    self.cros_remote = None
+
+    self.profiler = None
+    self.verbosity = 0
+
+    self.browser_options = BrowserOptions()
+    self.output_file = None
+
+    self.remote_platform_options = None
+
+    self.no_performance_mode = False
+
+  def __repr__(self):
+    return str(sorted(self.__dict__.items()))
+
+  def Copy(self):
+    return copy.deepcopy(self)
+
+  def CreateParser(self, *args, **kwargs):
+    parser = optparse.OptionParser(*args, **kwargs)
+
+    # Selection group
+    group = optparse.OptionGroup(parser, 'Which browser to use')
+    group.add_option('--browser',
+        dest='browser_type',
+        default=None,
+        help='Browser type to run, '
+             'in order of priority. Supported values: list,%s' %
+             ','.join(browser_finder.FindAllBrowserTypes(self)))
+    group.add_option('--browser-executable',
+        dest='browser_executable',
+        help='The exact browser to run.')
+    group.add_option('--chrome-root',
+        dest='chrome_root',
+        help='Where to look for chrome builds. '
+             'Defaults to searching parent dirs by default.')
+    group.add_option('--chromium-output-directory',
+        dest='chromium_output_dir',
+        help='Where to look for build artifacts. '
+             'Can also be specified by setting environment variable '
+             'CHROMIUM_OUTPUT_DIR.')
+    group.add_option(
+        '--remote',
+        dest='cros_remote',
+        help='The hostname of a remote ChromeOS device to use.')
+    group.add_option(
+        '--remote-ssh-port',
+        type=int,
+        default=socket.getservbyname('ssh'),
+        dest='cros_remote_ssh_port',
+        help='The SSH port of the remote ChromeOS device (requires --remote).')
+    identity = None
+    testing_rsa = os.path.join(
+        util.GetTelemetryThirdPartyDir(), 'chromite', 'ssh_keys', 'testing_rsa')
+    if os.path.exists(testing_rsa):
+      identity = testing_rsa
+    group.add_option('--identity',
+        dest='cros_ssh_identity',
+        default=identity,
+        help='The identity file to use when ssh\'ing into the ChromeOS device')
+    parser.add_option_group(group)
+
+    # Debugging options
+    group = optparse.OptionGroup(parser, 'When things go wrong')
+    profiler_choices = profiler_finder.GetAllAvailableProfilers()
+    group.add_option(
+        '--profiler', default=None, type='choice',
+        choices=profiler_choices,
+        help='Record profiling data using this tool. Supported values: %s. '
+             '(Notice: this flag cannot be used for Timeline Based Measurement '
+             'benchmarks.)' % ', '.join(profiler_choices))
+    group.add_option(
+        '-v', '--verbose', action='count', dest='verbosity',
+        help='Increase verbosity level (repeat as needed)')
+    group.add_option('--print-bootstrap-deps',
+                     action='store_true',
+                     help='Output bootstrap deps list.')
+    parser.add_option_group(group)
+
+    # Platform options
+    group = optparse.OptionGroup(parser, 'Platform options')
+    group.add_option('--no-performance-mode', action='store_true',
+        help='Some platforms run on "full performance mode" where the '
+        'test is executed at maximum CPU speed in order to minimize noise '
+        '(specially important for dashboards / continuous builds). '
+        'This option prevents Telemetry from tweaking such platform settings.')
+    parser.add_option_group(group)
+
+    # Remote platform options
+    group = optparse.OptionGroup(parser, 'Remote platform options')
+    group.add_option('--android-blacklist-file',
+                     help='Device blacklist JSON file.')
+    group.add_option('--device',
+    help='The device ID to use. '
+         'If not specified, only 0 or 1 connected devices are supported. '
+         'If specified as "android", all available Android devices are '
+         'used.')
+    parser.add_option_group(group)
+
+    # Browser options.
+    self.browser_options.AddCommandLineArgs(parser)
+
+    real_parse = parser.parse_args
+    def ParseArgs(args=None):
+      defaults = parser.get_default_values()
+      for k, v in defaults.__dict__.items():
+        if k in self.__dict__ and self.__dict__[k] != None:
+          continue
+        self.__dict__[k] = v
+      ret = real_parse(args, self) # pylint: disable=E1121
+
+      if self.verbosity >= 2:
+        logging.getLogger().setLevel(logging.DEBUG)
+      elif self.verbosity:
+        logging.getLogger().setLevel(logging.INFO)
+      else:
+        logging.getLogger().setLevel(logging.WARNING)
+
+      if self.chromium_output_dir:
+        os.environ['CHROMIUM_OUTPUT_DIR'] = self.chromium_output_dir
+
+      # Parse remote platform options.
+      self.BuildRemotePlatformOptions()
+
+      if self.remote_platform_options.device == 'list':
+        if binary_manager.NeedsInit():
+          binary_manager.InitDependencyManager([])
+        devices = device_finder.GetDevicesMatchingOptions(self)
+        print 'Available devices:'
+        for device in devices:
+          print ' ', device.name
+        sys.exit(0)
+
+      if self.browser_executable and not self.browser_type:
+        self.browser_type = 'exact'
+      if self.browser_type == 'list':
+        if binary_manager.NeedsInit():
+          binary_manager.InitDependencyManager([])
+        devices = device_finder.GetDevicesMatchingOptions(self)
+        if not devices:
+          sys.exit(0)
+        browser_types = {}
+        for device in devices:
+          try:
+            possible_browsers = browser_finder.GetAllAvailableBrowsers(self,
+                                                                       device)
+            browser_types[device.name] = sorted(
+              [browser.browser_type for browser in possible_browsers])
+          except browser_finder_exceptions.BrowserFinderException as ex:
+            print >> sys.stderr, 'ERROR: ', ex
+            sys.exit(1)
+        print 'Available browsers:'
+        if len(browser_types) == 0:
+          print '  No devices were found.'
+        for device_name in sorted(browser_types.keys()):
+          print '  ', device_name
+          for browser_type in browser_types[device_name]:
+            print '    ', browser_type
+        sys.exit(0)
+
+      # Parse browser options.
+      self.browser_options.UpdateFromParseResults(self)
+
+      return ret
+    parser.parse_args = ParseArgs
+    return parser
+
+  # TODO(eakuefner): Factor this out into OptionBuilder pattern
+  def BuildRemotePlatformOptions(self):
+    if self.device or self.android_blacklist_file:
+      self.remote_platform_options = (
+          remote_platform_options.AndroidPlatformOptions(
+              self.device, self.android_blacklist_file))
+
+      # We delete these options because they should live solely in the
+      # AndroidPlatformOptions instance belonging to this class.
+      if self.device:
+        del self.device
+      if self.android_blacklist_file:
+        del self.android_blacklist_file
+    else:
+      self.remote_platform_options = (
+          remote_platform_options.AndroidPlatformOptions())
+
+  def AppendExtraBrowserArgs(self, args):
+    self.browser_options.AppendExtraBrowserArgs(args)
+
+  def MergeDefaultValues(self, defaults):
+    for k, v in defaults.__dict__.items():
+      self.ensure_value(k, v)
+
+class BrowserOptions(object):
+  """Options to be used for launching a browser."""
+
+  # Levels of browser logging.
+  NO_LOGGING = 'none'
+  NON_VERBOSE_LOGGING = 'non-verbose'
+  VERBOSE_LOGGING = 'verbose'
+
+  _LOGGING_LEVELS = (NO_LOGGING, NON_VERBOSE_LOGGING, VERBOSE_LOGGING)
+  _DEFAULT_LOGGING_LEVEL = NO_LOGGING
+
+  def __init__(self):
+    self.browser_type = None
+    self.show_stdout = False
+
+    self.extensions_to_load = []
+
+    # If set, copy the generated profile to this path on exit.
+    self.output_profile_path = None
+
+    # When set to True, the browser will use the default profile.  Telemetry
+    # will not provide an alternate profile directory.
+    self.dont_override_profile = False
+    self.profile_dir = None
+    self.profile_type = None
+    self._extra_browser_args = set()
+    self.extra_wpr_args = []
+    self.wpr_mode = wpr_modes.WPR_OFF
+    self.full_performance_mode = True
+
+    # The amount of time Telemetry should wait for the browser to start.
+    # This property is not exposed as a command line option.
+    self._browser_startup_timeout = 60
+
+    self.disable_background_networking = True
+    self.browser_user_agent_type = None
+
+    self.clear_sytem_cache_for_browser_and_profile_on_start = False
+    self.startup_url = 'about:blank'
+
+    # Background pages of built-in component extensions can interfere with
+    # performance measurements.
+    self.disable_component_extensions_with_background_pages = True
+    # Disable default apps.
+    self.disable_default_apps = True
+
+    self.logging_verbosity = self._DEFAULT_LOGGING_LEVEL
+
+    # The cloud storage bucket & path for uploading logs data produced by the
+    # browser to.
+    # If logs_cloud_remote_path is None, a random remote path is generated every
+    # time the logs data is uploaded.
+    self.logs_cloud_bucket = cloud_storage.TELEMETRY_OUTPUT
+    self.logs_cloud_remote_path = None
+
+    # TODO(danduong): Find a way to store target_os here instead of
+    # finder_options.
+    self._finder_options = None
+
+    # Whether to take screen shot for failed page & put them in telemetry's
+    # profiling results.
+    self.take_screenshot_for_failed_page = False
+
+  def __repr__(self):
+    # This works around the infinite loop caused by the introduction of a
+    # circular reference with _finder_options.
+    obj = self.__dict__.copy()
+    del obj['_finder_options']
+    return str(sorted(obj.items()))
+
+  def IsCrosBrowserOptions(self):
+    return False
+
+  @classmethod
+  def AddCommandLineArgs(cls, parser):
+
+    ############################################################################
+    # Please do not add any more options here without first discussing with    #
+    # a telemetry owner. This is not the right place for platform-specific     #
+    # options.                                                                 #
+    ############################################################################
+
+    group = optparse.OptionGroup(parser, 'Browser options')
+    profile_choices = profile_types.GetProfileTypes()
+    group.add_option('--profile-type',
+        dest='profile_type',
+        type='choice',
+        default='clean',
+        choices=profile_choices,
+        help=('The user profile to use. A clean profile is used by default. '
+              'Supported values: ' + ', '.join(profile_choices)))
+    group.add_option('--profile-dir',
+        dest='profile_dir',
+        help='Profile directory to launch the browser with. '
+             'A clean profile is used by default')
+    group.add_option('--extra-browser-args',
+        dest='extra_browser_args_as_string',
+        help='Additional arguments to pass to the browser when it starts')
+    group.add_option('--extra-wpr-args',
+        dest='extra_wpr_args_as_string',
+        help=('Additional arguments to pass to Web Page Replay. '
+              'See third_party/web-page-replay/replay.py for usage.'))
+    group.add_option('--show-stdout',
+        action='store_true',
+        help='When possible, will display the stdout of the process')
+
+    group.add_option('--browser-logging-verbosity',
+        dest='logging_verbosity',
+        type='choice',
+        choices=cls._LOGGING_LEVELS,
+        help=('Browser logging verbosity. The log file is saved in temp '
+              "directory. Note that logging affects the browser's "
+              'performance. Supported values: %s. Defaults to %s.' % (
+                  ', '.join(cls._LOGGING_LEVELS), cls._DEFAULT_LOGGING_LEVEL)))
+    parser.add_option_group(group)
+
+    group = optparse.OptionGroup(parser, 'Compatibility options')
+    group.add_option('--gtest_output',
+        help='Ignored argument for compatibility with runtest.py harness')
+    parser.add_option_group(group)
+
+  def UpdateFromParseResults(self, finder_options):
+    """Copies our options from finder_options"""
+    browser_options_list = [
+        'extra_browser_args_as_string',
+        'extra_wpr_args_as_string',
+        'profile_dir',
+        'profile_type',
+        'show_stdout',
+        ]
+    for o in browser_options_list:
+      a = getattr(finder_options, o, None)
+      if a is not None:
+        setattr(self, o, a)
+        delattr(finder_options, o)
+
+    self.browser_type = finder_options.browser_type
+    self._finder_options = finder_options
+
+    if hasattr(self, 'extra_browser_args_as_string'):
+      tmp = shlex.split(
+        self.extra_browser_args_as_string)
+      self.AppendExtraBrowserArgs(tmp)
+      delattr(self, 'extra_browser_args_as_string')
+    if hasattr(self, 'extra_wpr_args_as_string'):
+      tmp = shlex.split(
+        self.extra_wpr_args_as_string)
+      self.extra_wpr_args.extend(tmp)
+      delattr(self, 'extra_wpr_args_as_string')
+    if self.profile_type == 'default':
+      self.dont_override_profile = True
+
+    if self.profile_dir and self.profile_type != 'clean':
+      logging.critical(
+          "It's illegal to specify both --profile-type and --profile-dir.\n"
+          "For more information see: http://goo.gl/ngdGD5")
+      sys.exit(1)
+
+    if self.profile_dir and not os.path.isdir(self.profile_dir):
+      logging.critical(
+          "Directory specified by --profile-dir (%s) doesn't exist "
+          "or isn't a directory.\n"
+          "For more information see: http://goo.gl/ngdGD5" % self.profile_dir)
+      sys.exit(1)
+
+    if not self.profile_dir:
+      self.profile_dir = profile_types.GetProfileDir(self.profile_type)
+
+    if getattr(finder_options, 'logging_verbosity'):
+      self.logging_verbosity = finder_options.logging_verbosity
+      delattr(finder_options, 'logging_verbosity')
+
+    # This deferred import is necessary because browser_options is imported in
+    # telemetry/telemetry/__init__.py.
+    finder_options.browser_options = CreateChromeBrowserOptions(self)
+
+  @property
+  def finder_options(self):
+    return self._finder_options
+
+  @property
+  def extra_browser_args(self):
+    return self._extra_browser_args
+
+  @property
+  def browser_startup_timeout(self):
+    return self._browser_startup_timeout
+
+  @browser_startup_timeout.setter
+  def browser_startup_timeout(self, value):
+    self._browser_startup_timeout = value
+
+  def AppendExtraBrowserArgs(self, args):
+    if isinstance(args, list):
+      self._extra_browser_args.update(args)
+    else:
+      self._extra_browser_args.add(args)
+
+
+def CreateChromeBrowserOptions(br_options):
+  browser_type = br_options.browser_type
+
+  if (platform.GetHostPlatform().GetOSName() == 'chromeos' or
+      (browser_type and browser_type.startswith('cros'))):
+    return CrosBrowserOptions(br_options)
+
+  return br_options
+
+
+class ChromeBrowserOptions(BrowserOptions):
+  """Chrome-specific browser options."""
+
+  def __init__(self, br_options):
+    super(ChromeBrowserOptions, self).__init__()
+    # Copy to self.
+    self.__dict__.update(br_options.__dict__)
+
+
+class CrosBrowserOptions(ChromeBrowserOptions):
+  """ChromeOS-specific browser options."""
+
+  def __init__(self, br_options):
+    super(CrosBrowserOptions, self).__init__(br_options)
+    # Create a browser with oobe property.
+    self.create_browser_with_oobe = False
+    # Clear enterprise policy before logging in.
+    self.clear_enterprise_policy = True
+    # Disable GAIA/enterprise services.
+    self.disable_gaia_services = True
+
+    self.auto_login = True
+    self.gaia_login = False
+    self.username = 'test@test.test'
+    self.password = ''
+    self.gaia_id = '12345'
+    # For non-accelerated QEMU VMs.
+    self.browser_startup_timeout = 240
+
+  def IsCrosBrowserOptions(self):
+    return True
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/browser_options_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/browser_options_unittest.py
new file mode 100644
index 0000000..08791a5
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/browser_options_unittest.py
@@ -0,0 +1,111 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import optparse
+import os
+import unittest
+
+from telemetry.internal.browser import browser_options
+
+
+class BrowserOptionsTest(unittest.TestCase):
+  def testDefaults(self):
+    options = browser_options.BrowserFinderOptions()
+    parser = options.CreateParser()
+    parser.add_option('-x', action='store', default=3)
+    parser.parse_args(['--browser', 'any'])
+    self.assertEquals(options.x, 3) # pylint: disable=no-member
+
+  def testDefaultsPlusOverride(self):
+    options = browser_options.BrowserFinderOptions()
+    parser = options.CreateParser()
+    parser.add_option('-x', action='store', default=3)
+    parser.parse_args(['--browser', 'any', '-x', 10])
+    self.assertEquals(options.x, 10) # pylint: disable=no-member
+
+  def testDefaultsDontClobberPresetValue(self):
+    options = browser_options.BrowserFinderOptions()
+    setattr(options, 'x', 7)
+    parser = options.CreateParser()
+    parser.add_option('-x', action='store', default=3)
+    parser.parse_args(['--browser', 'any'])
+    self.assertEquals(options.x, 7) # pylint: disable=no-member
+
+  def testCount0(self):
+    options = browser_options.BrowserFinderOptions()
+    parser = options.CreateParser()
+    parser.add_option('-x', action='count', dest='v')
+    parser.parse_args(['--browser', 'any'])
+    self.assertEquals(options.v, None) # pylint: disable=no-member
+
+  def testCount2(self):
+    options = browser_options.BrowserFinderOptions()
+    parser = options.CreateParser()
+    parser.add_option('-x', action='count', dest='v')
+    parser.parse_args(['--browser', 'any', '-xx'])
+    self.assertEquals(options.v, 2) # pylint: disable=no-member
+
+  def testOptparseMutabilityWhenSpecified(self):
+    options = browser_options.BrowserFinderOptions()
+    parser = options.CreateParser()
+    parser.add_option('-x', dest='verbosity', action='store_true')
+    options_ret, _ = parser.parse_args(['--browser', 'any', '-x'])
+    self.assertEquals(options_ret, options)
+    self.assertTrue(options.verbosity)
+
+  def testOptparseMutabilityWhenNotSpecified(self):
+    options = browser_options.BrowserFinderOptions()
+
+    parser = options.CreateParser()
+    parser.add_option('-x', dest='verbosity', action='store_true')
+    options_ret, _ = parser.parse_args(['--browser', 'any'])
+    self.assertEquals(options_ret, options)
+    self.assertFalse(options.verbosity)
+
+  def testProfileDirDefault(self):
+    options = browser_options.BrowserFinderOptions()
+    parser = options.CreateParser()
+    parser.parse_args(['--browser', 'any'])
+    self.assertEquals(options.browser_options.profile_dir, None)
+
+  def testProfileDir(self):
+    options = browser_options.BrowserFinderOptions()
+    parser = options.CreateParser()
+    # Need to use a directory that exists.
+    current_dir = os.path.dirname(__file__)
+    parser.parse_args(['--browser', 'any', '--profile-dir', current_dir])
+    self.assertEquals(options.browser_options.profile_dir, current_dir)
+
+  def testExtraBrowserArgs(self):
+    options = browser_options.BrowserFinderOptions()
+    parser = options.CreateParser()
+    parser.parse_args(['--extra-browser-args=--foo --bar'])
+
+    self.assertEquals(options.browser_options.extra_browser_args,
+                      set(['--foo', '--bar']))
+
+  def testMergeDefaultValues(self):
+    options = browser_options.BrowserFinderOptions()
+    options.already_true = True
+    options.already_false = False
+    options.override_to_true = False
+    options.override_to_false = True
+
+    parser = optparse.OptionParser()
+    parser.add_option('--already_true', action='store_true')
+    parser.add_option('--already_false', action='store_true')
+    parser.add_option('--unset', action='store_true')
+    parser.add_option('--default_true', action='store_true', default=True)
+    parser.add_option('--default_false', action='store_true', default=False)
+    parser.add_option('--override_to_true', action='store_true', default=False)
+    parser.add_option('--override_to_false', action='store_true', default=True)
+
+    options.MergeDefaultValues(parser.get_default_values())
+
+    self.assertTrue(options.already_true)
+    self.assertFalse(options.already_false)
+    self.assertTrue(options.unset is None)
+    self.assertTrue(options.default_true)
+    self.assertFalse(options.default_false)
+    self.assertFalse(options.override_to_true)
+    self.assertTrue(options.override_to_false)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/browser_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/browser_unittest.py
new file mode 100644
index 0000000..02026cc
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/browser_unittest.py
@@ -0,0 +1,284 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+import os
+import shutil
+import tempfile
+import unittest
+
+from telemetry.core import exceptions
+from telemetry import decorators
+from telemetry.internal.browser import browser as browser_module
+from telemetry.internal.browser import browser_finder
+from telemetry.internal.platform import gpu_device
+from telemetry.internal.platform import gpu_info
+from telemetry.internal.platform import system_info
+from telemetry.internal.util import path
+from telemetry.testing import browser_test_case
+from telemetry.testing import options_for_unittests
+from telemetry.timeline import tracing_config
+
+import mock
+import py_utils
+
+class IntentionalException(Exception):
+  pass
+
+
+class BrowserTest(browser_test_case.BrowserTestCase):
+  def testBrowserCreation(self):
+    self.assertEquals(1, len(self._browser.tabs))
+
+    # Different browsers boot up to different things.
+    assert self._browser.tabs[0].url
+
+  @decorators.Enabled('has tabs')
+  def testNewCloseTab(self):
+    existing_tab = self._browser.tabs[0]
+    self.assertEquals(1, len(self._browser.tabs))
+    existing_tab_url = existing_tab.url
+
+    new_tab = self._browser.tabs.New()
+    self.assertEquals(2, len(self._browser.tabs))
+    self.assertEquals(existing_tab.url, existing_tab_url)
+    self.assertEquals(new_tab.url, 'about:blank')
+
+    new_tab.Close()
+    self.assertEquals(1, len(self._browser.tabs))
+    self.assertEquals(existing_tab.url, existing_tab_url)
+
+  def testMultipleTabCalls(self):
+    self._browser.tabs[0].Navigate(self.UrlOfUnittestFile('blank.html'))
+    self._browser.tabs[0].WaitForDocumentReadyStateToBeInteractiveOrBetter()
+
+  def testTabCallByReference(self):
+    tab = self._browser.tabs[0]
+    tab.Navigate(self.UrlOfUnittestFile('blank.html'))
+    self._browser.tabs[0].WaitForDocumentReadyStateToBeInteractiveOrBetter()
+
+  @decorators.Enabled('has tabs')
+  def testCloseReferencedTab(self):
+    self._browser.tabs.New()
+    tab = self._browser.tabs[0]
+    tab.Navigate(self.UrlOfUnittestFile('blank.html'))
+    tab.Close()
+    self.assertEquals(1, len(self._browser.tabs))
+
+  @decorators.Enabled('has tabs')
+  def testForegroundTab(self):
+    # Should be only one tab at this stage, so that must be the foreground tab
+    original_tab = self._browser.tabs[0]
+    self.assertEqual(self._browser.foreground_tab, original_tab)
+    new_tab = self._browser.tabs.New()
+    # New tab shouls be foreground tab
+    self.assertEqual(self._browser.foreground_tab, new_tab)
+    # Make sure that activating the background tab makes it the foreground tab
+    original_tab.Activate()
+    self.assertEqual(self._browser.foreground_tab, original_tab)
+    # Closing the current foreground tab should switch the foreground tab to the
+    # other tab
+    original_tab.Close()
+    self.assertEqual(self._browser.foreground_tab, new_tab)
+
+  # This test uses the reference browser and doesn't have access to
+  # helper binaries like crashpad_database_util.
+  @decorators.Enabled('linux')
+  def testGetMinidumpPathOnCrash(self):
+    tab = self._browser.tabs[0]
+    with self.assertRaises(exceptions.AppCrashException):
+      tab.Navigate('chrome://crash', timeout=5)
+    crash_minidump_path = self._browser.GetMostRecentMinidumpPath()
+    self.assertIsNotNone(crash_minidump_path)
+
+  def testGetSystemInfo(self):
+    if not self._browser.supports_system_info:
+      logging.warning(
+          'Browser does not support getting system info, skipping test.')
+      return
+
+    info = self._browser.GetSystemInfo()
+
+    self.assertTrue(isinstance(info, system_info.SystemInfo))
+    self.assertTrue(hasattr(info, 'model_name'))
+    self.assertTrue(hasattr(info, 'gpu'))
+    self.assertTrue(isinstance(info.gpu, gpu_info.GPUInfo))
+    self.assertTrue(hasattr(info.gpu, 'devices'))
+    self.assertTrue(len(info.gpu.devices) > 0)
+    for g in info.gpu.devices:
+      self.assertTrue(isinstance(g, gpu_device.GPUDevice))
+
+  def testGetSystemInfoNotCachedObject(self):
+    if not self._browser.supports_system_info:
+      logging.warning(
+          'Browser does not support getting system info, skipping test.')
+      return
+
+    info_a = self._browser.GetSystemInfo()
+    info_b = self._browser.GetSystemInfo()
+    self.assertFalse(info_a is info_b)
+
+  def testGetSystemTotalMemory(self):
+    self.assertTrue(self._browser.memory_stats['SystemTotalPhysicalMemory'] > 0)
+
+
+  # crbug.com/628836 (CrOS, where system-guest indicates ChromeOS guest)
+  # github.com/catapult-project/catapult/issues/3130 (Windows)
+  @decorators.Disabled('cros-chrome-guest', 'system-guest', 'chromeos', 'win')
+  def testIsTracingRunning(self):
+    tracing_controller = self._browser.platform.tracing_controller
+    if not tracing_controller.IsChromeTracingSupported():
+      return
+    self.assertFalse(tracing_controller.is_tracing_running)
+    config = tracing_config.TracingConfig()
+    config.enable_chrome_trace = True
+    tracing_controller.StartTracing(config)
+    self.assertTrue(tracing_controller.is_tracing_running)
+    tracing_controller.StopTracing()
+    self.assertFalse(tracing_controller.is_tracing_running)
+
+
+class CommandLineBrowserTest(browser_test_case.BrowserTestCase):
+  @classmethod
+  def CustomizeBrowserOptions(cls, options):
+    options.AppendExtraBrowserArgs('--user-agent=telemetry')
+
+  def testCommandLineOverriding(self):
+    # This test starts the browser with --user-agent=telemetry. This tests
+    # whether the user agent is then set.
+    t = self._browser.tabs[0]
+    t.Navigate(self.UrlOfUnittestFile('blank.html'))
+    t.WaitForDocumentReadyStateToBeInteractiveOrBetter()
+    self.assertEquals(t.EvaluateJavaScript('navigator.userAgent'),
+                      'telemetry')
+
+class DirtyProfileBrowserTest(browser_test_case.BrowserTestCase):
+  @classmethod
+  def CustomizeBrowserOptions(cls, options):
+    options.profile_type = 'small_profile'
+
+  @decorators.Disabled('chromeos')  # crbug.com/243912
+  def testDirtyProfileCreation(self):
+    self.assertEquals(1, len(self._browser.tabs))
+
+
+class BrowserLoggingTest(browser_test_case.BrowserTestCase):
+  @classmethod
+  def CustomizeBrowserOptions(cls, options):
+    options.logging_verbosity = options.VERBOSE_LOGGING
+
+  @decorators.Disabled('chromeos', 'android')
+  def testLogFileExist(self):
+    self.assertTrue(
+       os.path.isfile(self._browser._browser_backend.log_file_path))
+
+
+def _GenerateBrowserProfile(number_of_tabs):
+  """ Generate a browser profile which browser had |number_of_tabs| number of
+  tabs opened before it was closed.
+      Returns:
+        profile_dir: the directory of profile.
+  """
+  profile_dir = tempfile.mkdtemp()
+  options = options_for_unittests.GetCopy()
+  options.browser_options.output_profile_path = profile_dir
+  browser_to_create = browser_finder.FindBrowser(options)
+  browser_to_create.platform.network_controller.InitializeIfNeeded()
+  try:
+    with browser_to_create.Create(options) as browser:
+      browser.platform.SetHTTPServerDirectories(path.GetUnittestDataDir())
+      blank_file_path = os.path.join(path.GetUnittestDataDir(), 'blank.html')
+      blank_url = browser.platform.http_server.UrlOf(blank_file_path)
+      browser.foreground_tab.Navigate(blank_url)
+      browser.foreground_tab.WaitForDocumentReadyStateToBeComplete()
+      for _ in xrange(number_of_tabs - 1):
+        tab = browser.tabs.New()
+        tab.Navigate(blank_url)
+        tab.WaitForDocumentReadyStateToBeComplete()
+    return profile_dir
+  finally:
+    browser_to_create.platform.network_controller.Close()
+
+
+class BrowserCreationTest(unittest.TestCase):
+  def setUp(self):
+    self.mock_browser_backend = mock.MagicMock()
+    self.mock_platform_backend = mock.MagicMock()
+
+  def testCleanedUpCalledWhenExceptionRaisedInBrowserCreation(self):
+    self.mock_platform_backend.platform.FlushDnsCache.side_effect = (
+        IntentionalException('Boom!'))
+    with self.assertRaises(IntentionalException):
+      browser_module.Browser(
+         self.mock_browser_backend, self.mock_platform_backend,
+         credentials_path=None)
+    self.assertTrue(self.mock_platform_backend.WillCloseBrowser.called)
+
+  def testOriginalExceptionNotSwallow(self):
+    self.mock_platform_backend.platform.FlushDnsCache.side_effect = (
+        IntentionalException('Boom!'))
+    self.mock_platform_backend.WillCloseBrowser.side_effect = (
+        IntentionalException('Cannot close browser!'))
+    with self.assertRaises(IntentionalException) as context:
+      browser_module.Browser(
+         self.mock_browser_backend, self.mock_platform_backend,
+         credentials_path=None)
+    self.assertIn('Boom!', context.exception.message)
+
+
+class BrowserRestoreSessionTest(unittest.TestCase):
+
+  @classmethod
+  def setUpClass(cls):
+    cls._number_of_tabs = 4
+    cls._profile_dir = _GenerateBrowserProfile(cls._number_of_tabs)
+    cls._options = options_for_unittests.GetCopy()
+    cls._options.browser_options.AppendExtraBrowserArgs(
+        ['--restore-last-session'])
+    cls._options.browser_options.profile_dir = cls._profile_dir
+    cls._browser_to_create = browser_finder.FindBrowser(cls._options)
+    cls._browser_to_create.platform.network_controller.InitializeIfNeeded()
+
+  @decorators.Enabled('has tabs')
+  @decorators.Disabled('chromeos', 'win', 'mac')
+  # TODO(nednguyen): Enable this test on windowsn platform
+  def testRestoreBrowserWithMultipleTabs(self):
+    with self._browser_to_create.Create(self._options) as browser:
+      # The number of tabs will be self._number_of_tabs + 1 as it includes the
+      # old tabs and a new blank tab.
+      expected_number_of_tabs = self._number_of_tabs + 1
+      try:
+        py_utils.WaitFor(
+            lambda: len(browser.tabs) == expected_number_of_tabs, 10)
+      except:
+        logging.error('Number of tabs is %s' % len(browser.tabs))
+        raise
+      self.assertEquals(expected_number_of_tabs, len(browser.tabs))
+
+  @classmethod
+  def tearDownClass(cls):
+    cls._browser_to_create.platform.network_controller.Close()
+    shutil.rmtree(cls._profile_dir)
+
+
+class TestBrowserOperationDoNotLeakTempFiles(unittest.TestCase):
+
+  @decorators.Enabled('win', 'mac', 'linux')
+  @decorators.Isolated
+  def testBrowserNotLeakingTempFiles(self):
+    options = options_for_unittests.GetCopy()
+    browser_to_create = browser_finder.FindBrowser(options)
+    self.assertIsNotNone(browser_to_create)
+    before_browser_run_temp_dir_content = os.listdir(tempfile.tempdir)
+    browser_to_create.platform.network_controller.InitializeIfNeeded()
+    try:
+      with browser_to_create.Create(options) as browser:
+        tab = browser.tabs.New()
+        tab.Navigate('about:blank')
+        self.assertEquals(2, tab.EvaluateJavaScript('1 + 1'))
+      after_browser_run_temp_dir_content = os.listdir(tempfile.tempdir)
+      self.assertEqual(before_browser_run_temp_dir_content,
+                       after_browser_run_temp_dir_content)
+    finally:
+      browser_to_create.platform.network_controller.Close()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/extension_dict.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/extension_dict.py
new file mode 100644
index 0000000..95742b5
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/extension_dict.py
@@ -0,0 +1,33 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.internal.browser import extension_to_load
+
+
+class ExtensionDict(object):
+  """Dictionary of ExtensionPage instances, with extension_id as key."""
+
+  def __init__(self, extension_backend):
+    self._extension_backend = extension_backend
+
+  def __getitem__(self, load_extension):
+    """Given an ExtensionToLoad instance, returns the corresponding
+    ExtensionPage instance."""
+    if not isinstance(load_extension, extension_to_load.ExtensionToLoad):
+      raise TypeError("Input param must be of type ExtensionToLoad")
+    return self.GetByExtensionId(load_extension.extension_id)[0]
+
+  def __contains__(self, load_extension):
+    """Checks if this ExtensionToLoad instance has been loaded"""
+    if not isinstance(load_extension, extension_to_load.ExtensionToLoad):
+      raise TypeError("Input param must be of type ExtensionToLoad")
+    return load_extension.extension_id in self._extension_backend
+
+  def keys(self):
+    return self._extension_backend.keys()
+
+  def GetByExtensionId(self, extension_id):
+    """Returns a list of extensions given an extension id. This is useful for
+    connecting to built-in apps and component extensions."""
+    return self._extension_backend[extension_id]
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/extension_page.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/extension_page.py
new file mode 100644
index 0000000..350de6c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/extension_page.py
@@ -0,0 +1,27 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import re
+
+from telemetry.internal.browser import web_contents
+
+
+def UrlToExtensionId(url):
+  return re.match(r"(chrome-extension://)([^/]+)", url).group(2)
+
+
+class ExtensionPage(web_contents.WebContents):
+  """Represents an extension page in the browser"""
+
+  def __init__(self, inspector_backend):
+    super(ExtensionPage, self).__init__(inspector_backend)
+    self.url = inspector_backend.url
+    self.extension_id = UrlToExtensionId(self.url)
+
+  def Reload(self):
+    """Reloading an extension page is used as a workaround for an extension
+    binding bug for old versions of Chrome (crbug.com/263162). After Navigate
+    returns, we are guaranteed that the inspected page is in the correct state.
+    """
+    self._inspector_backend.Navigate(self.url, None, 10)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/extension_to_load.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/extension_to_load.py
new file mode 100644
index 0000000..bec211c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/extension_to_load.py
@@ -0,0 +1,57 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import os
+
+from telemetry.internal.backends.chrome import crx_id
+
+
+class ExtensionPathNonExistentException(Exception):
+  pass
+
+class MissingPublicKeyException(Exception):
+  pass
+
+class ExtensionToLoad(object):
+  def __init__(self, path, browser_type):
+    if not os.path.isdir(path):
+      raise ExtensionPathNonExistentException(
+          'Extension path not a directory %s' % path)
+    self._path = path
+    self._local_path = path
+    # It is possible that we are running telemetry on Windows targeting
+    # a remote CrOS or Android device. In this case, we need the
+    # browser_type argument to determine how we should encode
+    # the extension path.
+    self._is_win = (os.name == 'nt'
+        and not (browser_type.startswith('android')
+                 or browser_type.startswith('cros')))
+
+  @property
+  def extension_id(self):
+    """Unique extension id of this extension."""
+    if crx_id.HasPublicKey(self._path):
+      # Calculate extension id from the public key.
+      return crx_id.GetCRXAppID(os.path.realpath(self._path))
+    else:
+      # Calculate extension id based on the path on the device.
+      return crx_id.GetCRXAppID(
+          os.path.realpath(self._local_path),
+          from_file_path=True,
+          is_win_path=self._is_win)
+
+  @property
+  def path(self):
+    """Path to extension source directory."""
+    return self._path
+
+  @property
+  def local_path(self):
+    """Path to extension destination directory, for remote instances of
+    chrome"""
+    return self._local_path
+
+  @local_path.setter
+  def local_path(self, local_path):
+    self._local_path = local_path
+
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/extension_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/extension_unittest.py
new file mode 100644
index 0000000..514f2a5
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/extension_unittest.py
@@ -0,0 +1,191 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+import os
+import shutil
+import tempfile
+import unittest
+
+from telemetry.core import util
+from telemetry import decorators
+from telemetry.internal.browser import browser_finder
+from telemetry.internal.browser import extension_to_load
+from telemetry.testing import options_for_unittests
+
+
+class ExtensionTest(unittest.TestCase):
+  def setUp(self):
+    self._browser = None
+    self._platform = None
+    self._extension = None
+    self._extension_id = None
+
+  def CreateBrowserWithExtension(self, ext_path):
+    extension_path = os.path.join(util.GetUnittestDataDir(), ext_path)
+    options = options_for_unittests.GetCopy()
+    load_extension = extension_to_load.ExtensionToLoad(
+        extension_path, options.browser_type)
+    options.browser_options.extensions_to_load = [load_extension]
+    browser_to_create = browser_finder.FindBrowser(options)
+
+    if not browser_to_create:
+      # May not find a browser that supports extensions.
+      return False
+    self._platform = browser_to_create.platform
+    self._platform.network_controller.InitializeIfNeeded()
+    self._browser = browser_to_create.Create(options)
+    self._extension = self._browser.extensions[load_extension]
+    self._extension_id = load_extension.extension_id
+    self.assertTrue(self._extension)
+    return True
+
+  def tearDown(self):
+    if self._browser:
+      self._browser.Close()
+      self._platform.network_controller.Close()
+
+  def testExtensionBasic(self):
+    """Test ExtensionPage's ExecuteJavaScript and EvaluateJavaScript."""
+    if not self.CreateBrowserWithExtension('simple_extension'):
+      logging.warning('Did not find a browser that supports extensions, '
+                      'skipping test.')
+      return
+    self.assertTrue(
+        self._extension.EvaluateJavaScript('chrome.runtime != null'))
+    self._extension.ExecuteJavaScript('setTestVar("abcdef")')
+    self.assertEquals('abcdef',
+                      self._extension.EvaluateJavaScript('_testVar'))
+
+  def testExtensionGetByExtensionId(self):
+    """Test GetByExtensionId for a simple extension with a background page."""
+    if not self.CreateBrowserWithExtension('simple_extension'):
+      logging.warning('Did not find a browser that supports extensions, '
+                      'skipping test.')
+      return
+    ext = self._browser.extensions.GetByExtensionId(self._extension_id)
+    self.assertEqual(1, len(ext))
+    self.assertEqual(ext[0], self._extension)
+    self.assertTrue(
+        ext[0].EvaluateJavaScript('chrome.runtime != null'))
+
+  @decorators.Disabled('mac')
+  def testWebApp(self):
+    """Tests GetByExtensionId for a web app with multiple pages."""
+    if not self.CreateBrowserWithExtension('simple_app'):
+      logging.warning('Did not find a browser that supports extensions, '
+                      'skipping test.')
+      return
+    extensions = self._browser.extensions.GetByExtensionId(self._extension_id)
+    extension_urls = set([ext.EvaluateJavaScript('location.href;')
+                          for ext in extensions])
+    expected_urls = set(['chrome-extension://' + self._extension_id + '/' + path
+                         for path in ['main.html', 'second.html',
+                                      '_generated_background_page.html']])
+
+    self.assertEqual(expected_urls, extension_urls)
+
+class NonExistentExtensionTest(unittest.TestCase):
+  def testNonExistentExtensionPath(self):
+    """Test that a non-existent extension path will raise an exception."""
+    extension_path = os.path.join(util.GetUnittestDataDir(), 'foo')
+    options = options_for_unittests.GetCopy()
+    self.assertRaises(extension_to_load.ExtensionPathNonExistentException,
+                      lambda: extension_to_load.ExtensionToLoad(
+                          extension_path, options.browser_type))
+
+  def testExtensionNotLoaded(self):
+    """Querying an extension that was not loaded will return None"""
+    extension_path = os.path.join(util.GetUnittestDataDir(), 'simple_extension')
+    options = options_for_unittests.GetCopy()
+    load_extension = extension_to_load.ExtensionToLoad(
+        extension_path, options.browser_type)
+    browser_to_create = browser_finder.FindBrowser(options)
+    try:
+      browser_to_create.platform.network_controller.InitializeIfNeeded()
+      with browser_to_create.Create(options) as b:
+        if b.supports_extensions:
+          self.assertRaises(KeyError, lambda: b.extensions[load_extension])
+    finally:
+      browser_to_create.platform.network_controller.Close()
+
+class MultipleExtensionTest(unittest.TestCase):
+  def setUp(self):
+    """ Copy the manifest and background.js files of simple_extension to a
+    number of temporary directories to load as extensions"""
+    self._extension_dirs = [tempfile.mkdtemp() for _ in range(3)]
+    src_extension_dir = os.path.join(
+        util.GetUnittestDataDir(), 'simple_extension')
+    manifest_path = os.path.join(src_extension_dir, 'manifest.json')
+    script_path = os.path.join(src_extension_dir, 'background.js')
+    for d in self._extension_dirs:
+      shutil.copy(manifest_path, d)
+      shutil.copy(script_path, d)
+    options = options_for_unittests.GetCopy()
+    self._extensions_to_load = [extension_to_load.ExtensionToLoad(
+                                    d, options.browser_type)
+                                for d in self._extension_dirs]
+    options.browser_options.extensions_to_load = self._extensions_to_load
+    browser_to_create = browser_finder.FindBrowser(options)
+    self._platform = None
+    self._browser = None
+    # May not find a browser that supports extensions.
+    if browser_to_create:
+      self._platform = browser_to_create.platform
+      self._platform.network_controller.InitializeIfNeeded()
+      self._browser = browser_to_create.Create(options)
+
+  def tearDown(self):
+    if self._platform:
+      self._platform.network_controller.Close()
+    if self._browser:
+      self._browser.Close()
+    for d in self._extension_dirs:
+      shutil.rmtree(d)
+
+  def testMultipleExtensions(self):
+    if not self._browser:
+      logging.warning('Did not find a browser that supports extensions, '
+                      'skipping test.')
+      return
+
+    # Test contains.
+    loaded_extensions = [e for e in self._extensions_to_load
+                         if e in self._browser.extensions]
+    self.assertEqual(len(loaded_extensions), len(self._extensions_to_load))
+    for load_extension in self._extensions_to_load:
+      extension = self._browser.extensions[load_extension]
+      assert extension
+      self.assertTrue(
+          extension.EvaluateJavaScript('chrome.runtime != null'))
+      extension.ExecuteJavaScript('setTestVar("abcdef")')
+      self.assertEquals('abcdef', extension.EvaluateJavaScript('_testVar'))
+
+
+class WebviewInExtensionTest(ExtensionTest):
+
+  # Flaky on windows, hits an exception: http://crbug.com/508325
+  # Flaky on macOS too: http://crbug.com/661434
+  # ChromeOS: http://crbug.com/674220
+  @decorators.Disabled('win', 'linux', 'mac', 'chromeos')
+  def testWebviewInExtension(self):
+    """Tests GetWebviewContext() for a web app containing <webview> element."""
+    if not self.CreateBrowserWithExtension('webview_app'):
+      logging.warning('Did not find a browser that supports extensions, '
+                      'skipping test.')
+      return
+
+    self._browser.extensions.GetByExtensionId(self._extension_id)
+    webview_contexts = self._extension.GetWebviewContexts()
+    self.assertEquals(1, len(webview_contexts))
+    webview_context = webview_contexts[0]
+    webview_context.WaitForDocumentReadyStateToBeComplete()
+    # Check that the context has the right url from the <webview> element.
+    self.assertTrue(webview_context.GetUrl().startswith('data:'))
+    # Check |test_input_id| element is accessible from the webview context.
+    self.assertTrue(webview_context.EvaluateJavaScript(
+                    'document.getElementById("test_input_id") != null'))
+    # Check that |test_input_id| is not accessible from outside webview context
+    self.assertFalse(self._extension.EvaluateJavaScript(
+                    'document.getElementById("test_input_id") != null'))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/network_quiescence.js b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/network_quiescence.js
new file mode 100644
index 0000000..f317d6c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/network_quiescence.js
@@ -0,0 +1,107 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+'use strict';
+
+/**
+ * @fileoverview This file provides a JavaScript helper function that
+ * determines when network quiescence has been reached based on the time since
+ * the last resource was received.
+ */
+(function() {
+
+  // Make executing this code idempotent.
+  if (window.__telemetry_testHasReachedNetworkQuiescence) {
+    return;
+  }
+
+  // Some benchmarks patch window.performance to make it deterministic.
+  // Save the original performance object before it is patched.
+  var real_performance = window.performance;
+
+  // Set the Resource Timing interface functions that will be used below
+  // to use whatever version is available currently regardless of vendor
+  // prefix.
+  real_performance.clearResourceTimings =
+      (real_performance.clearResourceTimings     ||
+       real_performance.mozClearResourceTimings  ||
+       real_performance.msClearResourceTimings   ||
+       real_performance.oClearResourceTimings    ||
+       real_performance.webkitClearResourceTimings);
+
+  real_performance.getEntriesByType =
+      (real_performance.getEntriesByType     ||
+       real_performance.mozGetEntriesByType  ||
+       real_performance.msGetEntriesByType   ||
+       real_performance.oGetEntriesByType    ||
+       real_performance.webkitGetEntriesByType);
+
+  // This variable will available to the function below and it will be
+  // persistent across different function calls. It stores the last
+  // entry in the list of PerformanceResourceTiming objects returned by
+  // real_performance.getEntriesByType('resource').
+  //
+  // The reason for doing it this way is because the buffer for
+  // PerformanceResourceTiming objects has a limit, and once it's full,
+  // new entries are not added. We're only interested in the last entry,
+  // so we can clear new entries when they're added.
+  var lastEntry = null;
+
+  // True when no resource has been loaded from the network for
+  //|QUIESCENCE_TIMEOUT_MS| milliseconds. This value is sticky.
+  var hasReachedQuiesence = false;
+
+  // Time to wait before declaring network quiescence in milliseconds.
+  var QUIESCENCE_TIMEOUT_MS = 2000;
+
+  /**
+   * This method uses the Resource Timing interface, which is described at
+   * http://www.w3.org/TR/resource-timing/. It determines whether the time
+   * since lodading any resources such as images and script files (including
+   * resources requested via XMLHttpRequest) has exceeded a threshold defined
+   # by |QUIESCENCE_TIMEOUT_MS|.
+   *
+   * @return {boolean} True if the time since either the load event, or the last
+   *   resource was received after the load event exceeds the aforementioned
+   *   threshold. This state is sticky, so once this function returns true for a
+   *   given page, it will always return true.
+   */
+  window.__telemetry_testHasReachedNetworkQuiescence = function() {
+    if (hasReachedQuiesence) {
+      return true;
+    }
+
+    if (window.document.readyState !== 'complete') {
+      return false;
+    }
+
+    var resourceTimings = real_performance.getEntriesByType('resource');
+    if (resourceTimings.length > 0) {
+      lastEntry = resourceTimings.pop();
+      real_performance.clearResourceTimings();
+    }
+
+    // The times for performance.now() and in the PerformanceResourceTiming
+    // objects are all in milliseconds since performance.timing.navigationStart,
+    // so we must also get load time in the same terms.
+    var timing = real_performance.timing;
+    var loadTime = timing.loadEventEnd - timing.navigationStart;
+    var lastResponseTimeMs = 0;
+
+    // If there have been no resource timing entries, or the last entry was
+    // before the load event, then use the time since the load event.
+    if (!lastEntry || lastEntry.responseEnd < loadTime) {
+      lastResponseTimeMs = real_performance.now() - loadTime;
+    } else {
+      lastResponseTimeMs = real_performance.now() - lastEntry.responseEnd;
+    }
+
+    if (lastResponseTimeMs >= QUIESCENCE_TIMEOUT_MS) {
+      hasReachedQuiesence = true;
+    }
+
+    return hasReachedQuiesence;
+  }
+
+})();
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/possible_browser.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/possible_browser.py
new file mode 100644
index 0000000..499bf51
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/possible_browser.py
@@ -0,0 +1,54 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.internal.app import possible_app
+
+
+class PossibleBrowser(possible_app.PossibleApp):
+  """A browser that can be controlled.
+
+  Call Create() to launch the browser and begin manipulating it..
+  """
+
+  def __init__(self, browser_type, target_os, supports_tab_control):
+    super(PossibleBrowser, self).__init__(app_type=browser_type,
+                                          target_os=target_os)
+    self._supports_tab_control = supports_tab_control
+    self._credentials_path = None
+
+  def __repr__(self):
+    return 'PossibleBrowser(app_type=%s)' % self.app_type
+
+  @property
+  def browser_type(self):
+    return self.app_type
+
+  @property
+  def supports_tab_control(self):
+    return self._supports_tab_control
+
+  def _InitPlatformIfNeeded(self):
+    raise NotImplementedError()
+
+  def Create(self, finder_options):
+    raise NotImplementedError()
+
+  def SupportsOptions(self, browser_options):
+    """Tests for extension support."""
+    raise NotImplementedError()
+
+  def IsRemote(self):
+    return False
+
+  def RunRemote(self):
+    pass
+
+  def UpdateExecutableIfNeeded(self):
+    pass
+
+  def last_modification_time(self):
+    return -1
+
+  def SetCredentialsPath(self, credentials_path):
+    self._credentials_path = credentials_path
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/profile_types.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/profile_types.py
new file mode 100644
index 0000000..73a0408
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/profile_types.py
@@ -0,0 +1,33 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+
+from telemetry.core import util
+
+BASE_PROFILE_TYPES = ['clean', 'default']
+
+PROFILE_TYPE_MAPPING = {
+  'typical_user': 'content_scripts1',
+  'power_user': 'extension_webrequest',
+}
+
+def GetProfileTypes():
+  """Returns a list of all command line options that can be specified for
+  profile type."""
+  return BASE_PROFILE_TYPES + PROFILE_TYPE_MAPPING.keys()
+
+def GetProfileDir(profile_type):
+  """Given a |profile_type| (as returned by GetProfileTypes()), return the
+  directory to use for that profile or None if the profile doesn't need a
+  profile directory (e.g. using the browser default profile).
+  """
+  if profile_type in BASE_PROFILE_TYPES:
+    return None
+
+  path = os.path.join(
+      util.GetTelemetryDir(), 'telemetry', 'internal', 'browser_profiles')
+
+  assert os.path.exists(path)
+  return path
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/profile_types_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/profile_types_unittest.py
new file mode 100644
index 0000000..fd99c1a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/profile_types_unittest.py
@@ -0,0 +1,17 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import unittest
+
+from telemetry.internal.browser import profile_types
+
+
+class ProfileTypesTest(unittest.TestCase):
+  def testGetProfileTypes(self):
+    types = profile_types.GetProfileTypes()
+
+    self.assertTrue('clean' in types)
+    self.assertTrue(len(types) > 0)
+
+  def testGetProfileDir(self):
+    self.assertFalse(profile_types.GetProfileDir('typical_user') is None)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/tab.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/tab.py
new file mode 100644
index 0000000..c847be6
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/tab.py
@@ -0,0 +1,269 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.internal.actions import action_runner
+from telemetry.internal.browser import web_contents
+from telemetry.internal.image_processing import video
+
+DEFAULT_TAB_TIMEOUT = 60
+
+
+class Tab(web_contents.WebContents):
+  """Represents a tab in the browser
+
+  The important parts of the Tab object are in the runtime and page objects.
+  E.g.:
+      # Navigates the tab to a given url.
+      tab.Navigate('http://www.google.com/')
+
+      # Evaluates 1+1 in the tab's JavaScript context.
+      tab.Evaluate('1+1')
+  """
+  def __init__(self, inspector_backend, tab_list_backend, browser):
+    super(Tab, self).__init__(inspector_backend)
+    self._tab_list_backend = tab_list_backend
+    self._browser = browser
+    self._action_runner = action_runner.ActionRunner(self)
+
+  @property
+  def browser(self):
+    """The browser in which this tab resides."""
+    return self._browser
+
+  @property
+  def action_runner(self):
+    return self._action_runner
+
+  @property
+  def url(self):
+    """Returns the URL of the tab, as reported by devtools.
+
+    Raises:
+      devtools_http.DevToolsClientConnectionError
+    """
+    return self._inspector_backend.url
+
+  @property
+  def dom_stats(self):
+    """A dictionary populated with measured DOM statistics.
+
+    Currently this dictionary contains:
+    {
+      'document_count': integer,
+      'node_count': integer,
+      'event_listener_count': integer
+    }
+
+    Raises:
+      inspector_memory.InspectorMemoryException
+      exceptions.TimeoutException
+      exceptions.DevtoolsTargetCrashException
+    """
+    dom_counters = self._inspector_backend.GetDOMStats(
+        timeout=DEFAULT_TAB_TIMEOUT)
+    assert (len(dom_counters) == 3 and
+            all([x in dom_counters for x in ['document_count', 'node_count',
+                                             'event_listener_count']]))
+    return dom_counters
+
+  def Activate(self):
+    """Brings this tab to the foreground asynchronously.
+
+    Not all browsers or browser versions support this method.
+    Be sure to check browser.supports_tab_control.
+
+    Please note: this is asynchronous. There is a delay between this call
+    and the page's documentVisibilityState becoming 'visible', and yet more
+    delay until the actual tab is visible to the user. None of these delays
+    are included in this call.
+
+    Raises:
+      devtools_http.DevToolsClientConnectionError
+      devtools_client_backend.TabNotFoundError
+      tab_list_backend.TabUnexpectedResponseException
+    """
+    self._tab_list_backend.ActivateTab(self.id)
+
+  def Close(self):
+    """Closes this tab.
+
+    Not all browsers or browser versions support this method.
+    Be sure to check browser.supports_tab_control.
+
+    Raises:
+      devtools_http.DevToolsClientConnectionError
+      devtools_client_backend.TabNotFoundError
+      tab_list_backend.TabUnexpectedResponseException
+      exceptions.TimeoutException
+    """
+    self._tab_list_backend.CloseTab(self.id)
+
+  @property
+  def screenshot_supported(self):
+    """True if the browser instance is capable of capturing screenshots."""
+    return self._inspector_backend.screenshot_supported
+
+  def Screenshot(self, timeout=DEFAULT_TAB_TIMEOUT):
+    """Capture a screenshot of the tab's contents.
+
+    Returns:
+      A telemetry.core.Bitmap.
+    Raises:
+      exceptions.WebSocketDisconnected
+      exceptions.TimeoutException
+      exceptions.DevtoolsTargetCrashException
+    """
+    return self._inspector_backend.Screenshot(timeout)
+
+  @property
+  def video_capture_supported(self):
+    """True if the browser instance is capable of capturing video."""
+    return self.browser.platform.CanCaptureVideo()
+
+  def Highlight(self, color):
+    """Synchronously highlights entire tab contents with the given RgbaColor.
+
+    TODO(tonyg): It is possible that the z-index hack here might not work for
+    all pages. If this happens, DevTools also provides a method for this.
+
+    Raises:
+      exceptions.EvaluateException
+      exceptions.WebSocketDisconnected
+      exceptions.TimeoutException
+      exceptions.DevtoolsTargetCrashException
+    """
+    screen_save = 'window.__telemetry_screen_%d' % int(color)
+    self.ExecuteJavaScript("""
+        (function() {
+          var screen = document.createElement('div');
+          screen.style.background = {{ color }};
+          screen.style.position = 'fixed';
+          screen.style.top = '0';
+          screen.style.left = '0';
+          screen.style.width = '100%';
+          screen.style.height = '100%';
+          screen.style.zIndex = '2147483638';
+          document.body.appendChild(screen);
+          requestAnimationFrame(function() {
+            requestAnimationFrame(function() {
+              {{ @screen_save }} = screen;
+            });
+          });
+        })();
+        """,
+        color='rgba(%d, %d, %d, %d)' % (color.r, color.g, color.b, color.a),
+        screen_save=screen_save)
+    self.WaitForJavaScriptCondition(
+        '!!{{ @screen_save }}', screen_save=screen_save, timeout=5)
+
+  def ClearHighlight(self, color):
+    """Clears a highlight of the given bitmap.RgbaColor.
+
+    Raises:
+      exceptions.EvaluateException
+      exceptions.WebSocketDisconnected
+      exceptions.TimeoutException
+      exceptions.DevtoolsTargetCrashException
+    """
+    screen_save = 'window.__telemetry_screen_%d' % int(color)
+    self.ExecuteJavaScript("""
+        (function() {
+          document.body.removeChild({{ @screen_save }});
+          requestAnimationFrame(function() {
+            requestAnimationFrame(function() {
+              {{ @screen_save }} = null;
+              console.time('__ClearHighlight.video_capture_start');
+              console.timeEnd('__ClearHighlight.video_capture_start');
+            });
+          });
+        })();
+        """, screen_save=screen_save)
+    self.WaitForJavaScriptCondition(
+        '!{{ @screen_save }}', screen_save=screen_save, timeout=5)
+
+  def StartVideoCapture(self, min_bitrate_mbps,
+                        highlight_bitmap=video.HIGHLIGHT_ORANGE_FRAME):
+    """Starts capturing video of the tab's contents.
+
+    This works by flashing the entire tab contents to a arbitrary color and then
+    starting video recording. When the frames are processed, we can look for
+    that flash as the content bounds.
+
+    Args:
+      min_bitrate_mbps: The minimum caputre bitrate in MegaBits Per Second.
+          The platform is free to deliver a higher bitrate if it can do so
+          without increasing overhead.
+
+    Raises:
+      exceptions.EvaluateException
+      exceptions.WebSocketDisconnected
+      exceptions.TimeoutException
+      exceptions.DevtoolsTargetCrashException
+      ValueError: If the required |min_bitrate_mbps| can't be achieved.
+    """
+    self.Highlight(highlight_bitmap)
+    self.browser.platform.StartVideoCapture(min_bitrate_mbps)
+    self.ClearHighlight(highlight_bitmap)
+
+  @property
+  def is_video_capture_running(self):
+    return self.browser.platform.is_video_capture_running
+
+  def StopVideoCapture(self):
+    """Stops recording video of the tab's contents.
+
+    This looks for the initial color flash in the first frame to establish the
+    tab content boundaries and then omits all frames displaying the flash.
+
+    Returns:
+      video: A video object which is a telemetry.core.Video
+    """
+    return self.browser.platform.StopVideoCapture()
+
+  def GetCookieByName(self, name, timeout=DEFAULT_TAB_TIMEOUT):
+    """Returns the value of the cookie by the given |name|.
+
+    Raises:
+      exceptions.WebSocketDisconnected
+      exceptions.TimeoutException
+      exceptions.DevtoolsTargetCrashException
+    """
+    return self._inspector_backend.GetCookieByName(name, timeout)
+
+  def CollectGarbage(self):
+    """Forces a garbage collection.
+
+    Raises:
+      exceptions.WebSocketDisconnected
+      exceptions.TimeoutException
+      exceptions.DevtoolsTargetCrashException
+    """
+    self._inspector_backend.CollectGarbage()
+
+  def ClearCache(self, force):
+    """Clears the browser's networking related disk, memory and other caches.
+
+    Args:
+      force: Iff true, navigates to about:blank which destroys the previous
+          renderer, ensuring that even "live" resources in the memory cache are
+          cleared.
+
+    Raises:
+      exceptions.EvaluateException
+      exceptions.WebSocketDisconnected
+      exceptions.TimeoutException
+      exceptions.DevtoolsTargetCrashException
+      errors.DeviceUnresponsiveError
+    """
+    self.browser.platform.FlushDnsCache()
+    self.ExecuteJavaScript("""
+        if (window.chrome && chrome.benchmarking &&
+            chrome.benchmarking.clearCache) {
+          chrome.benchmarking.clearCache();
+          chrome.benchmarking.clearPredictorCache();
+          chrome.benchmarking.clearHostResolverCache();
+        }
+    """)
+    if force:
+      self.Navigate('about:blank')
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/tab_list.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/tab_list.py
new file mode 100644
index 0000000..99bbaae
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/tab_list.py
@@ -0,0 +1,23 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+class TabList(object):
+  def __init__(self, tab_list_backend):
+    self._tab_list_backend = tab_list_backend
+
+  def New(self, timeout=300):
+    return self._tab_list_backend.New(timeout)
+
+  def __iter__(self):
+    return self._tab_list_backend.__iter__()
+
+  def __len__(self):
+    return self._tab_list_backend.__len__()
+
+  def __getitem__(self, index):
+    return self._tab_list_backend.__getitem__(index)
+
+  def GetTabById(self, identifier):
+    """The identifier of a tab can be accessed with tab.id."""
+    return self._tab_list_backend.GetTabById(identifier)
+
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/tab_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/tab_unittest.py
new file mode 100644
index 0000000..9170f8b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/tab_unittest.py
@@ -0,0 +1,248 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+import tempfile
+import time
+
+from telemetry.core import exceptions
+from telemetry import decorators
+from telemetry.internal.image_processing import video
+from telemetry.testing import tab_test_case
+from telemetry.timeline import model
+from telemetry.timeline import tracing_config
+from telemetry.util import image_util
+from telemetry.util import rgba_color
+
+import py_utils
+
+
+def _IsDocumentVisible(tab):
+  return not tab.EvaluateJavaScript('document.hidden || document.webkitHidden')
+
+
+class FakePlatformBackend(object):
+  def __init__(self):
+    self.platform = FakePlatform()
+
+  def DidStartBrowser(self, _, _2):
+    pass
+
+  def WillCloseBrowser(self, _, _2):
+    pass
+
+
+class FakePlatform(object):
+  def __init__(self):
+    self._is_video_capture_running = False
+
+  #pylint: disable=unused-argument
+  def StartVideoCapture(self, min_bitrate_mbps):
+    self._is_video_capture_running = True
+
+  def StopVideoCapture(self):
+    self._is_video_capture_running = False
+    return video.Video(tempfile.NamedTemporaryFile())
+
+  @property
+  def is_video_capture_running(self):
+    return self._is_video_capture_running
+
+
+class TabTest(tab_test_case.TabTestCase):
+  def testNavigateAndWaitForCompleteState(self):
+    self._tab.Navigate(self.UrlOfUnittestFile('blank.html'))
+    self._tab.WaitForDocumentReadyStateToBeComplete()
+
+  def testNavigateAndWaitForInteractiveState(self):
+    self._tab.Navigate(self.UrlOfUnittestFile('blank.html'))
+    self._tab.WaitForDocumentReadyStateToBeInteractiveOrBetter()
+
+  def testTabBrowserIsRightBrowser(self):
+    self.assertEquals(self._tab.browser, self._browser)
+
+  def testRendererCrash(self):
+    self.assertRaises(exceptions.DevtoolsTargetCrashException,
+                      lambda: self._tab.Navigate('chrome://crash',
+                                                 timeout=30))
+
+  def testTimeoutExceptionIncludeConsoleMessage(self):
+    self._tab.EvaluateJavaScript("""
+        window.__set_timeout_called = false;
+        function buggyReference() {
+          window.__set_timeout_called = true;
+          if (window.__one.not_defined === undefined)
+             window.__one = 1;
+        }
+        setTimeout(buggyReference, 200);""")
+    self._tab.WaitForJavaScriptCondition(
+        'window.__set_timeout_called === true', timeout=5)
+    with self.assertRaises(py_utils.TimeoutException) as context:
+      self._tab.WaitForJavaScriptCondition(
+          'window.__one === 1', timeout=1)
+      self.assertIn(
+        ("(error) :5: Uncaught TypeError: Cannot read property 'not_defined' "
+        'of undefined\n'),
+        context.exception.message)
+
+  @decorators.Enabled('has tabs')
+  def testActivateTab(self):
+    py_utils.WaitFor(lambda: _IsDocumentVisible(self._tab), timeout=5)
+    new_tab = self._browser.tabs.New()
+    new_tab.Navigate('about:blank')
+    py_utils.WaitFor(lambda: _IsDocumentVisible(new_tab), timeout=5)
+    self.assertFalse(_IsDocumentVisible(self._tab))
+    self._tab.Activate()
+    py_utils.WaitFor(lambda: _IsDocumentVisible(self._tab), timeout=5)
+    self.assertFalse(_IsDocumentVisible(new_tab))
+
+  def testTabUrl(self):
+    self.assertEquals(self._tab.url, 'about:blank')
+    url = self.UrlOfUnittestFile('blank.html')
+    self._tab.Navigate(url)
+    self.assertEquals(self._tab.url, url)
+
+  #pylint: disable=protected-access
+  def testIsVideoCaptureRunning(self):
+    original_platform_backend = self._tab.browser._platform_backend
+    try:
+      self._tab.browser._platform_backend = FakePlatformBackend()
+      self.assertFalse(self._tab.is_video_capture_running)
+      self._tab.StartVideoCapture(min_bitrate_mbps=2)
+      self.assertTrue(self._tab.is_video_capture_running)
+      self.assertIsNotNone(self._tab.StopVideoCapture())
+      self.assertFalse(self._tab.is_video_capture_running)
+    finally:
+      self._tab.browser._platform_backend = original_platform_backend
+
+  # Test failing on android: http://crbug.com/437057
+  # and mac: http://crbug.com/468675
+  @decorators.Disabled('android', 'chromeos', 'mac')
+  def testHighlight(self):
+    self.assertEquals(self._tab.url, 'about:blank')
+    config = tracing_config.TracingConfig()
+    config.chrome_trace_config.SetLowOverheadFilter()
+    config.enable_chrome_trace = True
+    self._browser.platform.tracing_controller.StartTracing(config)
+    self._tab.Highlight(rgba_color.WEB_PAGE_TEST_ORANGE)
+    self._tab.ClearHighlight(rgba_color.WEB_PAGE_TEST_ORANGE)
+    trace_data = self._browser.platform.tracing_controller.StopTracing()
+    timeline_model = model.TimelineModel(trace_data)
+    renderer_thread = timeline_model.GetRendererThreadFromTabId(
+        self._tab.id)
+    found_video_start_event = False
+    for event in renderer_thread.async_slices:
+      if event.name == '__ClearHighlight.video_capture_start':
+        found_video_start_event = True
+        break
+    self.assertTrue(found_video_start_event)
+
+  @decorators.Enabled('has tabs')
+  @decorators.Disabled('mac', 'linux')  # crbug.com/499207.
+  def testGetRendererThreadFromTabId(self):
+    self.assertEquals(self._tab.url, 'about:blank')
+    # Create 3 tabs. The third tab is closed before we call
+    # tracing_controller.StartTracing.
+    first_tab = self._tab
+    second_tab = self._browser.tabs.New()
+    second_tab.Navigate('about:blank')
+    second_tab.WaitForDocumentReadyStateToBeInteractiveOrBetter()
+    third_tab = self._browser.tabs.New()
+    third_tab.Navigate('about:blank')
+    third_tab.WaitForDocumentReadyStateToBeInteractiveOrBetter()
+    third_tab.Close()
+    config = tracing_config.TracingConfig()
+    config.chrome_trace_config.SetLowOverheadFilter()
+    config.enable_chrome_trace = True
+    self._browser.platform.tracing_controller.StartTracing(config)
+    first_tab.ExecuteJavaScript('console.time("first-tab-marker");')
+    first_tab.ExecuteJavaScript('console.timeEnd("first-tab-marker");')
+    second_tab.ExecuteJavaScript('console.time("second-tab-marker");')
+    second_tab.ExecuteJavaScript('console.timeEnd("second-tab-marker");')
+    trace_data = self._browser.platform.tracing_controller.StopTracing()
+    timeline_model = model.TimelineModel(trace_data)
+
+    # Assert that the renderer_thread of the first tab contains
+    # 'first-tab-marker'.
+    renderer_thread = timeline_model.GetRendererThreadFromTabId(
+        first_tab.id)
+    first_tab_markers = [
+        renderer_thread.IterAllSlicesOfName('first-tab-marker')]
+    self.assertEquals(1, len(first_tab_markers))
+
+    # Close second tab and assert that the renderer_thread of the second tab
+    # contains 'second-tab-marker'.
+    second_tab.Close()
+    renderer_thread = timeline_model.GetRendererThreadFromTabId(
+        second_tab.id)
+    second_tab_markers = [
+        renderer_thread.IterAllSlicesOfName('second-tab-marker')]
+    self.assertEquals(1, len(second_tab_markers))
+
+    # Third tab wasn't available when we start tracing, so there is no
+    # renderer_thread corresponding to it in the the trace.
+    self.assertIs(None, timeline_model.GetRendererThreadFromTabId(third_tab.id))
+
+  @decorators.Disabled('android') # https://crbug.com/463933
+  def testTabIsAlive(self):
+    self.assertEquals(self._tab.url, 'about:blank')
+    self.assertTrue(self._tab.IsAlive())
+
+    self._tab.Navigate(self.UrlOfUnittestFile('blank.html'))
+    self.assertTrue(self._tab.IsAlive())
+
+    self.assertRaises(exceptions.DevtoolsTargetCrashException,
+        lambda: self._tab.Navigate(self.UrlOfUnittestFile('chrome://crash')))
+    self.assertFalse(self._tab.IsAlive())
+
+
+class GpuTabTest(tab_test_case.TabTestCase):
+  @classmethod
+  def CustomizeBrowserOptions(cls, options):
+    options.AppendExtraBrowserArgs('--enable-gpu-benchmarking')
+
+  # Test flaky on mac: crbug.com/358664, chromeos: crbug.com/483212.
+  @decorators.Disabled('android', 'mac', 'chromeos')
+  @decorators.Disabled('win')  # catapult/issues/2282
+  def testScreenshot(self):
+    if not self._tab.screenshot_supported:
+      logging.warning('Browser does not support screenshots, skipping test.')
+      return
+
+    self.Navigate('green_rect.html')
+    pixel_ratio = self._tab.EvaluateJavaScript('window.devicePixelRatio || 1')
+
+    screenshot = self._tab.Screenshot(5)
+    assert screenshot is not None
+    image_util.GetPixelColor(
+        screenshot, 0 * pixel_ratio, 0 * pixel_ratio).AssertIsRGB(
+            0, 255, 0, tolerance=2)
+    image_util.GetPixelColor(
+        screenshot, 31 * pixel_ratio, 31 * pixel_ratio).AssertIsRGB(
+            0, 255, 0, tolerance=2)
+    image_util.GetPixelColor(
+        screenshot, 32 * pixel_ratio, 32 * pixel_ratio).AssertIsRGB(
+            255, 255, 255, tolerance=2)
+
+
+class MediaRouterDialogTabTest(tab_test_case.TabTestCase):
+  @classmethod
+  def CustomizeBrowserOptions(cls, options):
+    options.AppendExtraBrowserArgs('--media-router=1')
+
+  # There is no media router dialog on android/chromeos, it is a desktop-only
+  # feature.
+  @decorators.Disabled('android', 'chromeos')
+  @decorators.Disabled('win')  # catapult/issues/2282
+  def testMediaRouterDialog(self):
+    self._tab.Navigate(self.UrlOfUnittestFile('cast.html'))
+    self._tab.WaitForDocumentReadyStateToBeComplete()
+    self._tab.action_runner.TapElement(selector='#start_session_button')
+    # Wait for media router dialog
+    start_time = time.time()
+    while (time.time() - start_time < 5 and
+           len(self.tabs) != 2):
+      time.sleep(1)
+    self.assertEquals(len(self.tabs), 2)
+    self.assertEquals(self.tabs[1].url, 'chrome://media-router/')
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/user_agent.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/user_agent.py
new file mode 100644
index 0000000..8e968c9
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/user_agent.py
@@ -0,0 +1,37 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+UA_TYPE_MAPPING = {
+  'desktop':
+      'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) '
+      'AppleWebKit/537.36 (KHTML, like Gecko) '
+      'Chrome/40.0.2194.2 Safari/537.36',
+  'mobile':
+      'Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) '
+      'AppleWebKit/535.36 (KHTML, like Gecko) Chrome/40.0.2194.2 Mobile '
+      'Safari/535.36',
+  'tablet':
+      'Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus 7 Build/IMM76B) '
+      'AppleWebKit/535.36 (KHTML, like Gecko) Chrome/40.0.2194.2 '
+      'Safari/535.36',
+  'tablet_10_inch':
+      'Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus 10 Build/IMM76B) '
+      'AppleWebKit/535.36 (KHTML, like Gecko) Chrome/40.0.2194.2 '
+      'Safari/535.36',
+}
+
+
+def GetChromeUserAgentArgumentFromType(user_agent_type):
+  """Returns a chrome user agent based on a user agent type.
+  This is derived from:
+  https://developers.google.com/chrome/mobile/docs/user-agent
+  """
+  if user_agent_type:
+    return ['--user-agent=%s' % UA_TYPE_MAPPING[user_agent_type]]
+  return []
+
+def GetChromeUserAgentDictFromType(user_agent_type):
+  if user_agent_type:
+    return {'userAgent': UA_TYPE_MAPPING[user_agent_type]}
+  return {}
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/user_agent_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/user_agent_unittest.py
new file mode 100644
index 0000000..19c5d68
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/user_agent_unittest.py
@@ -0,0 +1,38 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry import decorators
+from telemetry.internal.browser import user_agent
+from telemetry.testing import tab_test_case
+
+
+class MobileUserAgentTest(tab_test_case.TabTestCase):
+  @classmethod
+  def CustomizeBrowserOptions(cls, options):
+    options.browser_user_agent_type = 'mobile'
+
+  @decorators.Disabled('chromeos')  # crbug.com/483212
+  def testUserAgent(self):
+    ua = self._tab.EvaluateJavaScript('window.navigator.userAgent')
+    self.assertEquals(ua, user_agent.UA_TYPE_MAPPING['mobile'])
+
+class TabletUserAgentTest(tab_test_case.TabTestCase):
+  @classmethod
+  def CustomizeBrowserOptions(cls, options):
+    options.browser_user_agent_type = 'tablet'
+
+  @decorators.Disabled('chromeos')  # crbug.com/483212
+  def testUserAgent(self):
+    ua = self._tab.EvaluateJavaScript('window.navigator.userAgent')
+    self.assertEquals(ua, user_agent.UA_TYPE_MAPPING['tablet'])
+
+class DesktopUserAgentTest(tab_test_case.TabTestCase):
+  @classmethod
+  def CustomizeBrowserOptions(cls, options):
+    options.browser_user_agent_type = 'desktop'
+
+  @decorators.Disabled('chromeos')  # crbug.com/483212
+  def testUserAgent(self):
+    ua = self._tab.EvaluateJavaScript('window.navigator.userAgent')
+    self.assertEquals(ua, user_agent.UA_TYPE_MAPPING['desktop'])
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/wait_for_frame.js b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/wait_for_frame.js
new file mode 100644
index 0000000..761b9ba
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/wait_for_frame.js
@@ -0,0 +1,46 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+'use strict';
+
+/**
+ * @fileoverview This file provides a JavaScript helper function that
+ * determines whether at least one frame has passed since the first
+ * call of this function with the given request id.
+ */
+(function() {
+
+  // Make executing this code idempotent.
+  if (window.__telemetry_testHasFramePassed) {
+    return;
+  }
+
+  // A dictionary which tracks the number of requestAnimationFrame calls that
+  // we are waiting on for the given request id.
+  var pending_frames = {};
+
+  window.__telemetry_testHasFramePassed = function(request_id) {
+    // If we already have pending frames for the given request id, there is
+    // no work to do, just return whether the pending frames have reached 0.
+    if (pending_frames.hasOwnProperty(request_id)) {
+      return pending_frames[request_id] == 0;
+    }
+
+    // We wait for two calls to requestAnimationFrame. When the first
+    // requestAnimationFrame is called, we know that a frame is in the
+    // pipeline. When the second requestAnimationFrame is called, we know that
+    // the first frame has reached the screen.
+    pending_frames[request_id] = 2;
+
+    var wait_for_raf = function() {
+      pending_frames[request_id]--;
+      if (pending_frames[request_id] > 0) {
+        window.requestAnimationFrame(wait_for_raf);
+      }
+    };
+
+    window.requestAnimationFrame(wait_for_raf);
+    return false;
+  };
+})();
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/web_contents.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/web_contents.py
new file mode 100644
index 0000000..6cef07f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser/web_contents.py
@@ -0,0 +1,326 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+
+from telemetry.core import exceptions
+
+from py_trace_event import trace_event
+
+DEFAULT_WEB_CONTENTS_TIMEOUT = 90
+
+# TODO(achuith, dtu, nduca): Add unit tests specifically for WebContents,
+# independent of Tab.
+class WebContents(object):
+
+  __metaclass__ = trace_event.TracedMetaClass
+
+  """Represents web contents in the browser"""
+  def __init__(self, inspector_backend):
+    self._inspector_backend = inspector_backend
+
+    with open(os.path.join(os.path.dirname(__file__),
+        'network_quiescence.js')) as f:
+      self._quiescence_js = f.read()
+
+    with open(os.path.join(os.path.dirname(__file__),
+        'wait_for_frame.js')) as f:
+      self._wait_for_frame_js = f.read()
+
+    # An incrementing ID used to query frame timing javascript. Using a new id
+    # with each request ensures that previously timed-out wait for frame
+    # requests don't impact new requests.
+    self._wait_for_frame_id = 0
+
+  @property
+  def id(self):
+    """Return the unique id string for this tab object."""
+    return self._inspector_backend.id
+
+  def GetUrl(self):
+    """Returns the URL to which the WebContents is connected.
+
+    Raises:
+      exceptions.Error: If there is an error in inspector backend connection.
+    """
+    return self._inspector_backend.url
+
+  def GetWebviewContexts(self):
+    """Returns a list of webview contexts within the current inspector backend.
+
+    Returns:
+      A list of WebContents objects representing the webview contexts.
+
+    Raises:
+      exceptions.Error: If there is an error in inspector backend connection.
+    """
+    webviews = []
+    inspector_backends = self._inspector_backend.GetWebviewInspectorBackends()
+    for inspector_backend in inspector_backends:
+      webviews.append(WebContents(inspector_backend))
+    return webviews
+
+  def WaitForDocumentReadyStateToBeComplete(self,
+      timeout=DEFAULT_WEB_CONTENTS_TIMEOUT):
+    """Waits for the document to finish loading.
+
+    Raises:
+      exceptions.Error: See WaitForJavaScriptCondition() for a detailed list
+      of possible exceptions.
+    """
+
+    self.WaitForJavaScriptCondition(
+        'document.readyState == "complete"', timeout=timeout)
+
+  def WaitForDocumentReadyStateToBeInteractiveOrBetter(self,
+      timeout=DEFAULT_WEB_CONTENTS_TIMEOUT):
+    """Waits for the document to be interactive.
+
+    Raises:
+      exceptions.Error: See WaitForJavaScriptCondition() for a detailed list
+      of possible exceptions.
+    """
+    self.WaitForJavaScriptCondition(
+        'document.readyState == "interactive" || '
+        'document.readyState == "complete"', timeout=timeout)
+
+  def WaitForFrameToBeDisplayed(self,
+          timeout=DEFAULT_WEB_CONTENTS_TIMEOUT):
+    """Waits for a frame to be displayed before returning.
+
+    Raises:
+      exceptions.Error: See WaitForJavaScriptCondition() for a detailed list
+      of possible exceptions.
+    """
+    # Generate a new id for each call of this function to ensure that we track
+    # each request to wait seperately.
+    self._wait_for_frame_id += 1
+    self.WaitForJavaScriptCondition(
+        '{{ @script }}; window.__telemetry_testHasFramePassed({{ frame_id }})',
+        script=self._wait_for_frame_js,
+        frame_id=str(self._wait_for_frame_id),  # Place id as a str.
+        timeout=timeout)
+
+  def HasReachedQuiescence(self):
+    """Determine whether the page has reached quiescence after loading.
+
+    Returns:
+      True if 2 seconds have passed since last resource received, false
+      otherwise.
+    Raises:
+      exceptions.Error: See EvaluateJavaScript() for a detailed list of
+      possible exceptions.
+    """
+    # Inclusion of the script that provides
+    # window.__telemetry_testHasReachedNetworkQuiescence()
+    # is idempotent, it's run on every call because WebContents doesn't track
+    # page loads and we need to execute anew for every newly loaded page.
+    return self.EvaluateJavaScript(
+        '{{ @script }}; window.__telemetry_testHasReachedNetworkQuiescence()',
+        script=self._quiescence_js)
+
+  def ExecuteJavaScript(self, *args, **kwargs):
+    """Executes a given JavaScript statement. Does not return the result.
+
+    Example: runner.ExecuteJavaScript('var foo = {{ value }};', value='hi');
+
+    Args:
+      statement: The statement to execute (provided as a string).
+
+    Optional keyword args:
+      timeout: The number of seconds to wait for the statement to execute.
+      context_id: The id of an iframe where to execute the code; the main page
+          has context_id=1, the first iframe context_id=2, etc.
+      Additional keyword arguments provide values to be interpolated within
+          the statement. See telemetry.util.js_template for details.
+
+    Raises:
+      py_utils.TimeoutException
+      exceptions.EvaluationException
+      exceptions.WebSocketException
+      exceptions.DevtoolsTargetCrashException
+    """
+    return self._inspector_backend.ExecuteJavaScript(*args, **kwargs)
+
+  def EvaluateJavaScript(self, *args, **kwargs):
+    """Returns the result of evaluating a given JavaScript expression.
+
+    Example: runner.ExecuteJavaScript('document.location.href');
+
+    Args:
+      expression: The expression to execute (provided as a string).
+
+    Optional keyword args:
+      timeout: The number of seconds to wait for the expression to evaluate.
+      context_id: The id of an iframe where to execute the code; the main page
+          has context_id=1, the first iframe context_id=2, etc.
+      Additional keyword arguments provide values to be interpolated within
+          the expression. See telemetry.util.js_template for details.
+
+    Raises:
+      py_utils.TimeoutException
+      exceptions.EvaluationException
+      exceptions.WebSocketException
+      exceptions.DevtoolsTargetCrashException
+    """
+    return self._inspector_backend.EvaluateJavaScript(*args, **kwargs)
+
+  def WaitForJavaScriptCondition(self, *args, **kwargs):
+    """Wait for a JavaScript condition to become true.
+
+    Example: runner.WaitForJavaScriptCondition('window.foo == 10');
+
+    Args:
+      condition: The JavaScript condition (provided as string).
+
+    Optional keyword args:
+      timeout: The number in seconds to wait for the condition to become
+          True (default to 60).
+      context_id: The id of an iframe where to execute the code; the main page
+          has context_id=1, the first iframe context_id=2, etc.
+      Additional keyword arguments provide values to be interpolated within
+          the expression. See telemetry.util.js_template for details.
+
+    Raises:
+      py_utils.TimeoutException
+      exceptions.EvaluationException
+      exceptions.WebSocketException
+      exceptions.DevtoolsTargetCrashException
+    """
+    return self._inspector_backend.WaitForJavaScriptCondition(*args, **kwargs)
+
+  def EnableAllContexts(self):
+    """Enable all contexts in a page. Returns the number of available contexts.
+
+    Raises:
+      exceptions.WebSocketDisconnected
+      py_utils.TimeoutException
+      exceptions.DevtoolsTargetCrashException
+    """
+    return self._inspector_backend.EnableAllContexts()
+
+  def WaitForNavigate(self, timeout=DEFAULT_WEB_CONTENTS_TIMEOUT):
+    """Waits for the navigation to complete.
+
+    The current page is expect to be in a navigation.
+    This function returns when the navigation is complete or when
+    the timeout has been exceeded.
+
+    Raises:
+      py_utils.TimeoutException
+      exceptions.DevtoolsTargetCrashException
+    """
+    self._inspector_backend.WaitForNavigate(timeout)
+
+  def Navigate(self, url, script_to_evaluate_on_commit=None,
+               timeout=DEFAULT_WEB_CONTENTS_TIMEOUT):
+    """Navigates to url.
+
+    If |script_to_evaluate_on_commit| is given, the script source string will be
+    evaluated when the navigation is committed. This is after the context of
+    the page exists, but before any script on the page itself has executed.
+
+    Raises:
+      py_utils.TimeoutException
+      exceptions.DevtoolsTargetCrashException
+    """
+    self._inspector_backend.Navigate(url, script_to_evaluate_on_commit, timeout)
+
+  def IsAlive(self):
+    """Whether the WebContents is still operating normally.
+
+    Since WebContents function asynchronously, this method does not guarantee
+    that the WebContents will still be alive at any point in the future.
+
+    Returns:
+      A boolean indicating whether the WebContents is opearting normally.
+    """
+    return self._inspector_backend.IsInspectable()
+
+  def CloseConnections(self):
+    """Closes all TCP sockets held open by the browser.
+
+    Raises:
+      exceptions.DevtoolsTargetCrashException if the tab is not alive.
+    """
+    if not self.IsAlive():
+      raise exceptions.DevtoolsTargetCrashException
+    self.ExecuteJavaScript('window.chrome && chrome.benchmarking &&'
+                           'chrome.benchmarking.closeConnections()')
+
+  def SynthesizeScrollGesture(self, x=100, y=800, xDistance=0, yDistance=-500,
+                              xOverscroll=None, yOverscroll=None,
+                              preventFling=None, speed=None,
+                              gestureSourceType=None, repeatCount=None,
+                              repeatDelayMs=None, interactionMarkerName=None,
+                              timeout=60):
+    """Runs an inspector command that causes a repeatable browser driven scroll.
+
+    Args:
+      x: X coordinate of the start of the gesture in CSS pixels.
+      y: Y coordinate of the start of the gesture in CSS pixels.
+      xDistance: Distance to scroll along the X axis (positive to scroll left).
+      yDistance: Ddistance to scroll along the Y axis (positive to scroll up).
+      xOverscroll: Number of additional pixels to scroll back along the X axis.
+      xOverscroll: Number of additional pixels to scroll back along the Y axis.
+      preventFling: Prevents a fling gesture.
+      speed: Swipe speed in pixels per second.
+      gestureSourceType: Which type of input events to be generated.
+      repeatCount: Number of additional repeats beyond the first scroll.
+      repeatDelayMs: Number of milliseconds delay between each repeat.
+      interactionMarkerName: The name of the interaction markers to generate.
+
+    Raises:
+      py_utils.TimeoutException
+      exceptions.DevtoolsTargetCrashException
+    """
+    return self._inspector_backend.SynthesizeScrollGesture(
+        x=x, y=y, xDistance=xDistance, yDistance=yDistance,
+        xOverscroll=xOverscroll, yOverscroll=yOverscroll,
+        preventFling=preventFling, speed=speed,
+        gestureSourceType=gestureSourceType, repeatCount=repeatCount,
+        repeatDelayMs=repeatDelayMs,
+        interactionMarkerName=interactionMarkerName,
+        timeout=timeout)
+
+  def DispatchKeyEvent(self, keyEventType='char', modifiers=None,
+                       timestamp=None, text=None, unmodifiedText=None,
+                       keyIdentifier=None, domCode=None, domKey=None,
+                       windowsVirtualKeyCode=None, nativeVirtualKeyCode=None,
+                       autoRepeat=None, isKeypad=None, isSystemKey=None,
+                       timeout=60):
+    """Dispatches a key event to the page.
+
+    Args:
+      type: Type of the key event. Allowed values: 'keyDown', 'keyUp',
+          'rawKeyDown', 'char'.
+      modifiers: Bit field representing pressed modifier keys. Alt=1, Ctrl=2,
+          Meta/Command=4, Shift=8 (default: 0).
+      timestamp: Time at which the event occurred. Measured in UTC time in
+          seconds since January 1, 1970 (default: current time).
+      text: Text as generated by processing a virtual key code with a keyboard
+          layout. Not needed for for keyUp and rawKeyDown events (default: '').
+      unmodifiedText: Text that would have been generated by the keyboard if no
+          modifiers were pressed (except for shift). Useful for shortcut
+          (accelerator) key handling (default: "").
+      keyIdentifier: Unique key identifier (e.g., 'U+0041') (default: '').
+      windowsVirtualKeyCode: Windows virtual key code (default: 0).
+      nativeVirtualKeyCode: Native virtual key code (default: 0).
+      autoRepeat: Whether the event was generated from auto repeat (default:
+          False).
+      isKeypad: Whether the event was generated from the keypad (default:
+          False).
+      isSystemKey: Whether the event was a system key event (default: False).
+
+    Raises:
+      py_utils.TimeoutException
+      exceptions.DevtoolsTargetCrashException
+    """
+    return self._inspector_backend.DispatchKeyEvent(
+        keyEventType=keyEventType, modifiers=modifiers, timestamp=timestamp,
+        text=text, unmodifiedText=unmodifiedText, keyIdentifier=keyIdentifier,
+        domCode=domCode, domKey=domKey,
+        windowsVirtualKeyCode=windowsVirtualKeyCode,
+        nativeVirtualKeyCode=nativeVirtualKeyCode, autoRepeat=autoRepeat,
+        isKeypad=isKeypad, isSystemKey=isSystemKey, timeout=timeout)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser_profiles/content_scripts1/Default/Extensions/behllobkkfkfnphdnhnkndlbkcpglgmj/1.0.0.0/manifest.json b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser_profiles/content_scripts1/Default/Extensions/behllobkkfkfnphdnhnkndlbkcpglgmj/1.0.0.0/manifest.json
new file mode 100644
index 0000000..56fdb6c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser_profiles/content_scripts1/Default/Extensions/behllobkkfkfnphdnhnkndlbkcpglgmj/1.0.0.0/manifest.json
@@ -0,0 +1,8 @@
+{
+  "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDuUZGKCDbff6IRaxa4Pue7PPkxwPaNhGT3JEqppEsNWFjM80imEdqMbf3lrWqEfaHgaNku7nlpwPO1mu3/4Hr+XdNa5MhfnOnuPee4hyTLwOs3Vzz81wpbdzUxZSi2OmqMyI5oTaBYICfNHLwcuc65N5dbt6WKGeKgTpp4v7j7zwIDAQAB",
+  "version": "1.0.0.0",
+  "name": "1 content script",
+  "content_scripts": [
+    { "matches": ["file://*"], "js": ["script.js"] }
+  ]
+}
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser_profiles/content_scripts1/Default/Extensions/behllobkkfkfnphdnhnkndlbkcpglgmj/1.0.0.0/script.js b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser_profiles/content_scripts1/Default/Extensions/behllobkkfkfnphdnhnkndlbkcpglgmj/1.0.0.0/script.js
new file mode 100644
index 0000000..afc1cf8
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser_profiles/content_scripts1/Default/Extensions/behllobkkfkfnphdnhnkndlbkcpglgmj/1.0.0.0/script.js
@@ -0,0 +1,8 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Useless script just to test injection.
+var x = 1;
+var y = 2;
+var z = 3;
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser_profiles/content_scripts1/Default/Preferences b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser_profiles/content_scripts1/Default/Preferences
new file mode 100644
index 0000000..142e7ff
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser_profiles/content_scripts1/Default/Preferences
@@ -0,0 +1,42 @@
+{
+   "download": {
+      "directory_upgrade": true,
+      "extensions_to_open": ""
+   },
+   "extensions": {
+      "autoupdate": {
+         "next_check": "12897640036342487"
+      },
+      "settings": {
+         "behllobkkfkfnphdnhnkndlbkcpglgmj": {
+            "active_permissions": {
+               "scriptable_host": [ "file:///*" ]
+            },
+            "granted_permissions": {
+               "scriptable_host": [ "file:///*" ]
+            },
+            "location": 1,
+            "manifest":
+            {
+              "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDuUZGKCDbff6IRaxa4Pue7PPkxwPaNhGT3JEqppEsNWFjM80imEdqMbf3lrWqEfaHgaNku7nlpwPO1mu3/4Hr+XdNa5MhfnOnuPee4hyTLwOs3Vzz81wpbdzUxZSi2OmqMyI5oTaBYICfNHLwcuc65N5dbt6WKGeKgTpp4v7j7zwIDAQAB",
+              "version": "1.0.0.0",
+              "name": "1 content script",
+              "content_scripts": [
+                { "matches": ["file://*"], "js": ["script.js"] }
+              ]
+            },
+            "path": "behllobkkfkfnphdnhnkndlbkcpglgmj/1.0.0.0",
+            "state": 1
+         }
+      }
+   },
+   "profile": {
+      "exited_cleanly": true,
+      "id": "not-signed-in",
+      "name": "",
+      "nickname": ""
+   },
+   "session": {
+      "startup_urls": [  ]
+   }
+}
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser_profiles/extension_webrequest/Default/Extensions/behllobkkfkfnphdnhnkndlbkcpglgmj/1.0.0.0/background.html b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser_profiles/extension_webrequest/Default/Extensions/behllobkkfkfnphdnhnkndlbkcpglgmj/1.0.0.0/background.html
new file mode 100644
index 0000000..d3f9988
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser_profiles/extension_webrequest/Default/Extensions/behllobkkfkfnphdnhnkndlbkcpglgmj/1.0.0.0/background.html
@@ -0,0 +1,10 @@
+<script>
+var onBefore = chrome.webRequest.onBeforeRequest;
+onBefore.addListener(function(info) {
+  return {"cancel": false};
+}, {urls: ["http://*/*"]}, ['blocking']);
+var onBefore = chrome.webRequest.onBeforeRequest;
+onBefore.addListener(function(info) {
+  return {"cancel": false};
+}, {urls: ["file://*/*"]}, ['blocking']);
+</script>
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser_profiles/extension_webrequest/Default/Extensions/behllobkkfkfnphdnhnkndlbkcpglgmj/1.0.0.0/manifest.json b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser_profiles/extension_webrequest/Default/Extensions/behllobkkfkfnphdnhnkndlbkcpglgmj/1.0.0.0/manifest.json
new file mode 100644
index 0000000..835bc44
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser_profiles/extension_webrequest/Default/Extensions/behllobkkfkfnphdnhnkndlbkcpglgmj/1.0.0.0/manifest.json
@@ -0,0 +1,9 @@
+{
+  "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDuUZGKCDbff6IRaxa4Pue7PPkxwPaNhGT3JEqppEsNWFjM80imEdqMbf3lrWqEfaHgaNku7nlpwPO1mu3/4Hr+XdNa5MhfnOnuPee4hyTLwOs3Vzz81wpbdzUxZSi2OmqMyI5oTaBYICfNHLwcuc65N5dbt6WKGeKgTpp4v7j7zwIDAQAB",
+  "version": "1.0.0.0",
+  "name": "Webrequest",
+  "permissions": ["experimental", "webRequest"],
+  "background": {
+    "page": "background.html"
+  }
+}
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser_profiles/extension_webrequest/Default/Preferences b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser_profiles/extension_webrequest/Default/Preferences
new file mode 100644
index 0000000..e93588e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/browser_profiles/extension_webrequest/Default/Preferences
@@ -0,0 +1,77 @@
+{
+   "browser": {
+      "window_placement": {
+         "bottom": 1150,
+         "left": 10,
+         "maximized": false,
+         "right": 955,
+         "top": 10,
+         "work_area_bottom": 1160,
+         "work_area_left": 0,
+         "work_area_right": 1920,
+         "work_area_top": 0
+      }
+   },
+   "countryid_at_install": 21843,
+   "dns_prefetching": {
+      "host_referral_list": [ 2 ],
+      "startup_list": [ 1 ]
+   },
+   "download": {
+      "directory_upgrade": true,
+      "extensions_to_open": ""
+   },
+   "extensions": {
+      "autoupdate": {
+         "next_check": "12943417068538765"
+      },
+      "chrome_url_overrides": {
+         "bookmarks": [ "chrome-extension://eemcgdkfndhakfknompkggombfjjjeno/main.html" ]
+      },
+      "settings": {
+         "behllobkkfkfnphdnhnkndlbkcpglgmj": {
+            "granted_permissions": {
+               "api": [ "experimental" ],
+               "full": false
+            },
+            "install_time": "12943416715915765",
+            "location": 1,
+            "manifest": {
+               "background": {
+                 "page": "background.html"
+               },
+               "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDuUZGKCDbff6IRaxa4Pue7PPkxwPaNhGT3JEqppEsNWFjM80imEdqMbf3lrWqEfaHgaNku7nlpwPO1mu3/4Hr+XdNa5MhfnOnuPee4hyTLwOs3Vzz81wpbdzUxZSi2OmqMyI5oTaBYICfNHLwcuc65N5dbt6WKGeKgTpp4v7j7zwIDAQAB",
+               "name": "Webrequest",
+               "permissions": [ "experimental", "webRequest" ],
+               "version": "1.0.0.0"
+            },
+            "path": "behllobkkfkfnphdnhnkndlbkcpglgmj/1.0.0.0",
+            "state": 1
+         }
+      }
+   },
+   "google": {
+      "services": {
+         "username": ""
+      }
+   },
+   "ntp": {
+      "pref_version": 3,
+      "promo_resource_cache_update": "1298943121.229765"
+   },
+   "plugins": {
+      "enabled_internal_pdf3": true
+   },
+   "profile": {
+      "content_settings": {
+         "pref_version": 1
+      },
+      "exited_cleanly": true,
+      "id": "not-signed-in",
+      "name": "",
+      "nickname": ""
+   },
+   "tabs": {
+      "use_vertical_tabs": false
+   }
+}
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/forwarders/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/forwarders/__init__.py
new file mode 100644
index 0000000..b11fbfe
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/forwarders/__init__.py
@@ -0,0 +1,55 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import collections
+
+
+PortPair = collections.namedtuple('PortPair', ['local_port', 'remote_port'])
+PortSet = collections.namedtuple('PortSet', ['http', 'https', 'dns'])
+
+
+
+class ForwarderFactory(object):
+
+  def Create(self, port_pair):
+    """Creates a forwarder that maps remote (device) <-> local (host) ports.
+
+    Args:
+      port_pair: A PortPairs instance that consists of a PortPair mapping
+          for each protocol. http is required. https and dns may be None.
+    """
+    raise NotImplementedError()
+
+  @property
+  def host_ip(self):
+    return '127.0.0.1'
+
+
+class Forwarder(object):
+
+  def __init__(self, port_pair):
+    assert port_pair, 'Port mapping is required.'
+    self._port_pair = port_pair
+    self._forwarding = True
+
+  @property
+  def host_port(self):
+    return self._port_pair.remote_port
+
+  @property
+  def host_ip(self):
+    return '127.0.0.1'
+
+  @property
+  def port_pair(self):
+    return self._port_pair
+
+  @property
+  def url(self):
+    assert self.host_ip and self.host_port
+    return 'http://%s:%i' % (self.host_ip, self.host_port)
+
+  def Close(self):
+    self._port_pair = None
+    self._forwarding = False
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/forwarders/android_forwarder.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/forwarders/android_forwarder.py
new file mode 100644
index 0000000..eb20845
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/forwarders/android_forwarder.py
@@ -0,0 +1,85 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.internal.util import atexit_with_log
+import logging
+import subprocess
+
+from telemetry.internal import forwarders
+
+try:
+  from devil.android import forwarder
+except ImportError:
+  forwarder = None
+
+
+class AndroidForwarderFactory(forwarders.ForwarderFactory):
+
+  def __init__(self, device):
+    super(AndroidForwarderFactory, self).__init__()
+    self._device = device
+
+  def Create(self, port_pair):
+    try:
+      return AndroidForwarder(self._device, port_pair)
+    except Exception:
+      try:
+        logging.warning('Failed to create forwarder. '
+                        'Currently forwarded connections:')
+        for line in self._device.adb.ForwardList().splitlines():
+          logging.warning('  %s', line)
+      except Exception:
+        logging.warning('Exception raised while listing forwarded connections.')
+
+      logging.warning('Relevant device tcp sockets in use:')
+      try:
+        proc_net_tcp_target = ':%s ' % hex(port_pair.remote_port)[2:]
+        for line in self._device.ReadFile('/proc/net/tcp', as_root=True,
+                                          force_pull=True).splitlines():
+          if proc_net_tcp_target in line:
+            logging.warning('  %s', line)
+      except Exception:
+        logging.warning('Exception raised while listing tcp sockets.')
+
+      logging.warning('Possibly relevant lsof entries:')
+      try:
+        lsof_output = self._device.RunShellCommand(
+            ['lsof'], as_root=True, check_return=True)
+        lsof_target = str(port_pair.remote_port)
+        for line in lsof_output:
+          if lsof_target in line:
+            logging.warning('  %s', line)
+      except Exception:
+        logging.warning('Exception raised running lsof.')
+
+      logging.warning('Alive webpagereplay instances:')
+      try:
+        for line in subprocess.check_output(['ps', '-ef']).splitlines():
+          if 'webpagereplay' in line:
+            logging.warning('  %s', line)
+      except Exception:
+        logging.warning('Exception raised while listing WPR intances.')
+
+      raise
+
+
+class AndroidForwarder(forwarders.Forwarder):
+
+  def __init__(self, device, port_pair):
+    super(AndroidForwarder, self).__init__(port_pair)
+    self._device = device
+    forwarder.Forwarder.Map(
+        [(port_pair.remote_port, port_pair.local_port)], self._device)
+    self._port_pair = (
+        forwarders.PortPair(
+            port_pair.local_port,
+            forwarder.Forwarder.DevicePortForHostPort(port_pair.local_port)))
+    atexit_with_log.Register(self.Close)
+    # TODO(tonyg): Verify that each port can connect to host.
+
+  def Close(self):
+    if self._forwarding:
+      forwarder.Forwarder.UnmapDevicePort(
+          self._port_pair.remote_port, self._device)
+    super(AndroidForwarder, self).Close()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/forwarders/cros_forwarder.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/forwarders/cros_forwarder.py
new file mode 100644
index 0000000..4316954
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/forwarders/cros_forwarder.py
@@ -0,0 +1,64 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+import subprocess
+
+from telemetry.internal import forwarders
+from telemetry.internal.forwarders import do_nothing_forwarder
+
+import py_utils
+
+
+class CrOsForwarderFactory(forwarders.ForwarderFactory):
+
+  def __init__(self, cri):
+    super(CrOsForwarderFactory, self).__init__()
+    self._cri = cri
+
+  # pylint: disable=arguments-differ
+  def Create(self, port_pair, use_remote_port_forwarding=True):
+    if self._cri.local:
+      return do_nothing_forwarder.DoNothingForwarder(port_pair)
+    return CrOsSshForwarder(self._cri, use_remote_port_forwarding, port_pair)
+
+
+class CrOsSshForwarder(forwarders.Forwarder):
+
+  def __init__(self, cri, use_remote_port_forwarding, port_pair):
+    super(CrOsSshForwarder, self).__init__(port_pair)
+    self._cri = cri
+    self._proc = None
+    forwarding_args = self._ForwardingArgs(
+        use_remote_port_forwarding, self.host_ip, port_pair)
+    self._proc = subprocess.Popen(
+        self._cri.FormSSHCommandLine(['sleep', '999999999'], forwarding_args),
+        stdout=subprocess.PIPE,
+        stderr=subprocess.PIPE,
+        stdin=subprocess.PIPE,
+        shell=False)
+    py_utils.WaitFor(
+        lambda: self._cri.IsHTTPServerRunningOnPort(self.host_port), 60)
+    logging.debug('Server started on %s:%d', self.host_ip, self.host_port)
+
+  # pylint: disable=unused-argument
+  @staticmethod
+  def _ForwardingArgs(use_remote_port_forwarding, host_ip, port_pair):
+    if use_remote_port_forwarding:
+      arg_format = '-R{remote_port}:{host_ip}:{local_port}'
+    else:
+      arg_format = '-L{local_port}:{host_ip}:{remote_port}'
+    return [arg_format.format(host_ip=host_ip,
+                              local_port=port_pair.local_port,
+                              remote_port=port_pair.remote_port)]
+
+  @property
+  def host_port(self):
+    return self._port_pair.remote_port
+
+  def Close(self):
+    if self._proc:
+      self._proc.kill()
+      self._proc = None
+    super(CrOsSshForwarder, self).Close()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/forwarders/cros_forwarder_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/forwarders/cros_forwarder_unittest.py
new file mode 100644
index 0000000..809b63e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/forwarders/cros_forwarder_unittest.py
@@ -0,0 +1,24 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from telemetry.internal import forwarders
+from telemetry.internal.forwarders import cros_forwarder
+
+# pylint: disable=protected-access
+class ForwardingArgsTest(unittest.TestCase):
+  port_pair = forwarders.PortPair(111, 222)
+
+  def testForwardingArgsReverse(self):
+    forwarding_args = cros_forwarder.CrOsSshForwarder._ForwardingArgs(
+        use_remote_port_forwarding=True, host_ip='5.5.5.5',
+        port_pair=self.port_pair)
+    self.assertEqual(['-R222:5.5.5.5:111'], forwarding_args)
+
+  def testForwardingArgs(self):
+    forwarding_args = cros_forwarder.CrOsSshForwarder._ForwardingArgs(
+        use_remote_port_forwarding=False, host_ip='2.2.2.2',
+        port_pair=self.port_pair)
+    self.assertEqual(['-L111:2.2.2.2:222'], forwarding_args)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/forwarders/do_nothing_forwarder.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/forwarders/do_nothing_forwarder.py
new file mode 100644
index 0000000..40cfba3
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/forwarders/do_nothing_forwarder.py
@@ -0,0 +1,66 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import contextlib
+import logging
+import socket
+
+from telemetry.internal import forwarders
+
+import py_utils
+
+
+class Error(Exception):
+  """Base class for exceptions in this module."""
+  pass
+
+
+class PortsMismatchError(Error):
+  """Raised when local and remote ports are not equal."""
+  pass
+
+
+class ConnectionError(Error):
+  """Raised when unable to connect to local TCP ports."""
+  pass
+
+
+class DoNothingForwarderFactory(forwarders.ForwarderFactory):
+
+  def Create(self, port_pair):
+    return DoNothingForwarder(port_pair)
+
+
+class DoNothingForwarder(forwarders.Forwarder):
+  """Check that no forwarding is needed for the given port pairs.
+
+  The local and remote ports must be equal. Otherwise, the "do nothing"
+  forwarder does not make sense. (Raises PortsMismatchError.)
+
+  Also, check that all TCP ports support connections.  (Raises ConnectionError.)
+  """
+
+  def __init__(self, port_pair):
+    super(DoNothingForwarder, self).__init__(port_pair)
+    self._CheckPortPair()
+
+  def _CheckPortPair(self):
+    if self._port_pair.local_port != self._port_pair.remote_port:
+      raise PortsMismatchError('Local port forwarding is not supported')
+    try:
+      self._WaitForConnectionEstablished(
+          (self.host_ip, self._port_pair.local_port), timeout=10)
+      logging.debug(
+          'Connection test succeeded for %s:%d',
+          self.host_ip, self._port_pair.local_port)
+    except py_utils.TimeoutException:
+      raise ConnectionError(
+          'Unable to connect to address: %s:%d',
+          self.host_ip, self._port_pair.local_port)
+
+  def _WaitForConnectionEstablished(self, address, timeout):
+    def CanConnect():
+      with contextlib.closing(socket.socket()) as s:
+        return s.connect_ex(address) == 0
+    py_utils.WaitFor(CanConnect, timeout)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/forwarders/do_nothing_forwarder_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/forwarders/do_nothing_forwarder_unittest.py
new file mode 100644
index 0000000..7a17c87
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/forwarders/do_nothing_forwarder_unittest.py
@@ -0,0 +1,49 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from telemetry.internal import forwarders
+from telemetry.internal.forwarders import do_nothing_forwarder
+
+import py_utils
+
+
+class TestDoNothingForwarder(do_nothing_forwarder.DoNothingForwarder):
+  """Override _WaitForConnect to avoid actual socket connection."""
+
+  def __init__(self, port_pairs):
+    self.connected_addresses = []
+    super(TestDoNothingForwarder, self).__init__(port_pairs)
+
+  def _WaitForConnectionEstablished(self, address, timeout):
+    self.connected_addresses.append(address)
+
+
+class TestErrorDoNothingForwarder(do_nothing_forwarder.DoNothingForwarder):
+  """Simulate a connection error."""
+
+  def _WaitForConnectionEstablished(self, address, timeout):
+    raise py_utils.TimeoutException
+
+
+class CheckPortPairsTest(unittest.TestCase):
+  def testBasicCheck(self):
+    port_pair = forwarders.PortPair(80, 80)
+    f = TestDoNothingForwarder(port_pair)
+    expected_connected_addresses = [
+        ('127.0.0.1', 80),
+        ]
+    self.assertEqual(expected_connected_addresses, f.connected_addresses)
+
+  def testPortMismatchRaisesPortsMismatchError(self):
+    # The do_nothing_forward cannot forward from one port to another.
+    port_pair = forwarders.PortPair(80, 81)
+    with self.assertRaises(do_nothing_forwarder.PortsMismatchError):
+      TestDoNothingForwarder(port_pair)
+
+  def testConnectionTimeoutRaisesConnectionError(self):
+    port_pair = forwarders.PortPair(80, 80)
+    with self.assertRaises(do_nothing_forwarder.ConnectionError):
+      TestErrorDoNothingForwarder(port_pair)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/image_processing/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/image_processing/__init__.py
new file mode 100644
index 0000000..4d6aabb
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/image_processing/__init__.py
@@ -0,0 +1,3 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/image_processing/bitmaptools.cc b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/image_processing/bitmaptools.cc
new file mode 100644
index 0000000..fd54f28
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/image_processing/bitmaptools.cc
@@ -0,0 +1,264 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if defined(WIN32)
+#include <fcntl.h>
+#include <io.h>
+#endif
+
+enum Commands {
+  CROP_PIXELS = 0,
+  HISTOGRAM = 1,
+  BOUNDING_BOX = 2
+};
+
+bool ReadInt(int* out) {
+  return fread(out, sizeof(*out), 1, stdin) == 1;
+}
+
+void WriteResponse(void* data, int size) {
+  fwrite(&size, sizeof(size), 1, stdout);
+  fwrite(data, size, 1, stdout);
+  fflush(stdout);
+}
+
+struct Box {
+  Box() : left(), top(), right(), bottom() {}
+
+  // Expected input is:
+  // left, top, width, height
+  bool Read() {
+    int width;
+    int height;
+    if (!(ReadInt(&left) && ReadInt(&top) &&
+          ReadInt(&width) && ReadInt(&height))) {
+      fprintf(stderr, "Could not parse Box.\n");
+      return false;
+    }
+    if (left < 0 || top < 0 || width < 0 || height < 0) {
+      fprintf(stderr, "Box dimensions must be non-negative.\n");
+      return false;
+    }
+    right = left + width;
+    bottom = top + height;
+    return true;
+  }
+
+  void Union(int x, int y) {
+    if (left > x) left = x;
+    if (right <= x) right = x + 1;
+    if (top > y) top = y;
+    if (bottom <= y) bottom = y + 1;
+  }
+
+  int width() const { return right - left; }
+  int height() const { return bottom - top; }
+
+  int left;
+  int top;
+  int right;
+  int bottom;
+};
+
+
+// Represents a bitmap buffer with a crop box.
+struct Bitmap {
+  Bitmap() : pixels(NULL) {}
+
+  ~Bitmap() {
+    if (pixels)
+      delete[] pixels;
+  }
+
+  // Expected input is:
+  // bpp, width, height, box, pixels
+  bool Read() {
+    int bpp;
+    int width;
+    int height;
+    if (!(ReadInt(&bpp) && ReadInt(&width) && ReadInt(&height))) {
+      fprintf(stderr, "Could not parse Bitmap initializer.\n");
+      return false;
+    }
+    if (bpp <= 0 || width <= 0 || height <= 0) {
+      fprintf(stderr, "Dimensions must be positive.\n");
+      return false;
+    }
+
+    int size = width * height * bpp;
+
+    row_stride = width * bpp;
+    pixel_stride = bpp;
+    total_size = size;
+    row_size = row_stride;
+
+    if (!box.Read()) {
+      fprintf(stderr, "Expected crop box argument not found.\n");
+      return false;
+    }
+
+    if (box.bottom * row_stride > total_size ||
+        box.right * pixel_stride > row_size) {
+      fprintf(stderr, "Crop box overflows the bitmap.\n");
+      return false;
+    }
+
+    pixels = new unsigned char[size];
+    if (fread(pixels, sizeof(pixels[0]), size, stdin) <
+        static_cast<size_t>(size)) {
+      fprintf(stderr, "Not enough pixels found,\n");
+      return false;
+    }
+
+    total_size = (box.bottom - box.top) * row_stride;
+    row_size = (box.right - box.left) * pixel_stride;
+    data = pixels + box.top * row_stride + box.left * pixel_stride;
+    return true;
+  }
+
+  void WriteCroppedPixels() const {
+    int out_size = row_size * box.height();
+    unsigned char* out = new unsigned char[out_size];
+    unsigned char* dst = out;
+    for (const unsigned char* row = data;
+        row < data + total_size;
+        row += row_stride, dst += row_size) {
+      // No change in pixel_stride, so we can copy whole rows.
+      memcpy(dst, row, row_size);
+    }
+
+    WriteResponse(out, out_size);
+    delete[] out;
+  }
+
+  unsigned char* pixels;
+  Box box;
+  // Points at the top-left pixel in |pixels|.
+  const unsigned char* data;
+  // These counts are in bytes.
+  int row_stride;
+  int pixel_stride;
+  int total_size;
+  int row_size;
+};
+
+
+static inline
+bool PixelsEqual(const unsigned char* pixel1, const unsigned char* pixel2,
+                 int tolerance) {
+  // Note: this works for both RGB and RGBA. Alpha channel is ignored.
+  return (abs(pixel1[0] - pixel2[0]) <= tolerance) &&
+         (abs(pixel1[1] - pixel2[1]) <= tolerance) &&
+         (abs(pixel1[2] - pixel2[2]) <= tolerance);
+}
+
+
+static inline
+bool PixelsEqual(const unsigned char* pixel, int color, int tolerance) {
+  unsigned char pixel2[3] = { color >> 16, color >> 8, color };
+  return PixelsEqual(pixel, pixel2, tolerance);
+}
+
+
+static
+bool Histogram(const Bitmap& bmp) {
+  int ignore_color;
+  int tolerance;
+  if (!(ReadInt(&ignore_color) && ReadInt(&tolerance))) {
+    fprintf(stderr, "Could not parse HISTOGRAM command.\n");
+    return false;
+  }
+
+  const int kLength = 3 * 256;
+  int counts[kLength] = {};
+
+  for (const unsigned char* row = bmp.data; row < bmp.data + bmp.total_size;
+       row += bmp.row_stride) {
+    for (const unsigned char* pixel = row; pixel < row + bmp.row_size;
+       pixel += bmp.pixel_stride) {
+      if (ignore_color >= 0 && PixelsEqual(pixel, ignore_color, tolerance))
+        continue;
+      ++(counts[256 * 0 + pixel[0]]);
+      ++(counts[256 * 1 + pixel[1]]);
+      ++(counts[256 * 2 + pixel[2]]);
+    }
+  }
+
+  WriteResponse(counts, sizeof(counts));
+  return true;
+}
+
+
+static
+bool BoundingBox(const Bitmap& bmp) {
+  int color;
+  int tolerance;
+  if (!(ReadInt(&color) && ReadInt(&tolerance))) {
+    fprintf(stderr, "Could not parse BOUNDING_BOX command.\n");
+    return false;
+  }
+
+  Box box;
+  box.left = bmp.total_size;
+  box.top = bmp.total_size;
+  box.right = 0;
+  box.bottom = 0;
+
+  int count = 0;
+  int y = 0;
+  for (const unsigned char* row = bmp.data; row < bmp.data + bmp.total_size;
+       row += bmp.row_stride, ++y) {
+    int x = 0;
+    for (const unsigned char* pixel = row; pixel < row + bmp.row_size;
+         pixel += bmp.pixel_stride, ++x) {
+      if (!PixelsEqual(pixel, color, tolerance))
+        continue;
+      box.Union(x, y);
+      ++count;
+    }
+  }
+
+  int response[] = { box.left, box.top, box.width(), box.height(), count };
+  WriteResponse(response, sizeof(response));
+  return true;
+}
+
+
+int main() {
+  Bitmap bmp;
+  int command;
+
+#if defined(WIN32)
+  _setmode(_fileno(stdin), _O_BINARY);
+  _setmode(_fileno(stdout), _O_BINARY);
+#else
+  stdin = freopen(NULL, "rb", stdin);
+  stdout = freopen(NULL, "wb", stdout);
+#endif
+
+  if (!bmp.Read()) return -1;
+  if (!ReadInt(&command)) {
+    fprintf(stderr, "Expected command.\n");
+    return -1;
+  }
+  switch (command) {
+    case CROP_PIXELS:
+      bmp.WriteCroppedPixels();
+      break;
+    case BOUNDING_BOX:
+      if (!BoundingBox(bmp)) return -1;
+      break;
+    case HISTOGRAM:
+      if (!Histogram(bmp)) return -1;
+      break;
+    default:
+      fprintf(stderr, "Unrecognized command\n");
+      return -1;
+  }
+  return 0;
+}
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/image_processing/cv_util.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/image_processing/cv_util.py
new file mode 100644
index 0000000..356c0d6
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/image_processing/cv_util.py
@@ -0,0 +1,90 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""This module provides implementations of common computer Vision operations."""
+
+from __future__ import division
+from telemetry.internal.util import external_modules
+
+np = external_modules.ImportRequiredModule('numpy')
+
+
+def AreLinesOrthogonal(line1, line2, tolerance):
+  """Returns true if lines are within tolerance radians of being orthogonal."""
+  # Map each line onto an angle between 0 and 180.
+  theta1 = np.arctan2(np.float(line1[1] - line1[3]),
+                      np.float(line1[0] - line1[2]))
+  theta2 = np.arctan2(np.float(line2[1] - line2[3]),
+                      np.float(line2[0] - line2[2]))
+  angle2 = abs(theta2 - theta1)
+  if angle2 >= np.pi:
+    angle2 -= np.pi
+  # If the difference between the angles is more than pi/2 - tolerance, the
+  # lines are not orthogonal.
+  return not abs(angle2 - (np.pi / 2.0)) > tolerance
+
+
+def FindLineIntersection(line1, line2):
+  """If the line segments intersect, returns True and their intersection.
+  Otherwise, returns False and the intersection of the line segments if they
+  were to be extended."""
+  # Compute g, and h, the factor by which each line must be extended to
+  # exactly touch the other line. If both are between 0 and 1, then the lines
+  # currently intersect. We use h to compute their intersection.
+  line1p1 = line1[:2]
+  line1p0 = line1[2:]
+  line2p1 = line2[:2]
+  line2p0 = line2[2:]
+  E = np.subtract(line1p1, line1p0)
+  F = np.subtract(line2p1, line2p0)
+  Pe = np.asfarray((-E[1], E[0]))
+  Pf = np.asfarray((-F[1], F[0]))
+  h = np.dot(np.subtract(line1p0, line2p0), Pe)
+  h = np.divide(h, np.dot(F, Pe))
+  g = np.dot(np.subtract(line2p0, line1p0), Pf)
+  g = np.divide(g, np.dot(E, Pf))
+  intersection = np.add(line2p0, np.dot(F, h))
+  intersect = (h >= -0.000001 and h <= 1.000001 and
+               g >= -0.000001 and g <= 1.000001)
+  return intersect, intersection
+
+
+def ExtendLines(lines, length):
+  """Extends lines in an array to a given length, maintaining the center
+  point. Does not necessarily maintain point order."""
+  half_length = length / 2.0
+  angles = np.arctan2(lines[:, 1] - lines[:, 3], lines[:, 0] - lines[:, 2])
+  xoffsets = half_length * np.cos(angles)
+  yoffsets = half_length * np.sin(angles)
+  centerx = (lines[:, 0] + lines[:, 2]) / 2.0
+  centery = (lines[:, 1] + lines[:, 3]) / 2.0
+  lines[:, 0] = centerx - xoffsets
+  lines[:, 2] = centerx + xoffsets
+  lines[:, 1] = centery - yoffsets
+  lines[:, 3] = centery + yoffsets
+  return lines
+
+
+def IsPointApproxOnLine(point, line, tolerance=1):
+  """Approximates distance between point and line for small distances using
+  the determinant and checks whether it's within the tolerance. Tolerance is
+  an approximate distance in pixels, precision decreases with distance."""
+  xd = line[0] - line[2]
+  yd = line[1] - line[3]
+  det = ((xd) * (point[1] - line[3])) - ((yd) * (point[0] - line[2]))
+  tolerance = float(tolerance) * (abs(xd) + abs(yd))
+  return abs(det) * 2.0 <= tolerance
+
+
+def SqDistances(points1, points2):
+  """Computes the square of the distance between two sets of points, or a
+  set of points and a point."""
+  d = np.square(points1 - points2)
+  return d[:, 0] + d[:, 1]
+
+
+def SqDistance(point1, point2):
+  """Computes the square of the distance between two points."""
+  d = np.square(point1 - point2)
+  return d[0] + d[1]
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/image_processing/cv_util_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/image_processing/cv_util_unittest.py
new file mode 100644
index 0000000..d5d5cd7
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/image_processing/cv_util_unittest.py
@@ -0,0 +1,114 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from telemetry.internal.util import external_modules
+
+try:
+  np = external_modules.ImportRequiredModule('numpy')
+except ImportError:
+  pass
+else:
+  class CVUtilTest(unittest.TestCase):
+    def __init__(self, *args, **kwargs):
+      super(CVUtilTest, self).__init__(*args, **kwargs)
+      # Import modules with dependencies that may not be preset in test setup so
+      # that importing this unit test doesn't cause the test runner to raise an
+      # exception.
+      from telemetry.internal.image_processing import cv_util
+      self.cv_util = cv_util
+
+    def testAreLinesOrthogonalish(self):
+      l1 = np.asfarray((0, 0, 1, 0))
+      l2 = np.asfarray((0, 0, 0, 1))
+      self.assertTrue(self.cv_util.AreLinesOrthogonal(l1, l2, 0))
+      self.assertTrue(self.cv_util.AreLinesOrthogonal(l2, l1, 0))
+      self.assertFalse(self.cv_util.AreLinesOrthogonal(l1, l1,
+                                                       np.pi / 2 - 1e-10))
+      self.assertFalse(self.cv_util.AreLinesOrthogonal(l2, l2,
+                                                       np.pi / 2 - 1e-10))
+      self.assertTrue(self.cv_util.AreLinesOrthogonal(l1, l1, np.pi / 2))
+      self.assertTrue(self.cv_util.AreLinesOrthogonal(l2, l2, np.pi / 2))
+
+      l3 = np.asfarray((0, 0, 1, 1))
+      l4 = np.asfarray((1, 1, 0, 0))
+      self.assertFalse(self.cv_util.AreLinesOrthogonal(l3, l4,
+                                                       np.pi / 2 - 1e-10))
+      self.assertTrue(self.cv_util.AreLinesOrthogonal(l3, l1, np.pi / 4))
+
+      l5 = np.asfarray((0, 1, 1, 0))
+      self.assertTrue(self.cv_util.AreLinesOrthogonal(l3, l5, 0))
+
+    def testFindLineIntersection(self):
+      l1 = np.asfarray((1, 1, 2, 1))
+      l2 = np.asfarray((1, 1, 1, 2))
+      ret, p = self.cv_util.FindLineIntersection(l1, l2)
+      self.assertTrue(ret)
+      self.assertTrue(np.array_equal(p, np.array([1, 1])))
+      l3 = np.asfarray((1.1, 1, 2, 1))
+      ret, p = self.cv_util.FindLineIntersection(l2, l3)
+      self.assertFalse(ret)
+      self.assertTrue(np.array_equal(p, np.array([1, 1])))
+      l4 = np.asfarray((2, 1, 1, 1))
+      l5 = np.asfarray((1, 2, 1, 1))
+      ret, p = self.cv_util.FindLineIntersection(l4, l5)
+      self.assertTrue(ret)
+      self.assertTrue(np.array_equal(p, np.array([1, 1])))
+      l6 = np.asfarray((1, 1, 0, 0))
+      l7 = np.asfarray((0, 1, 1, 0))
+      ret, p = self.cv_util.FindLineIntersection(l7, l6)
+      self.assertTrue(ret)
+      self.assertTrue(np.array_equal(p, np.array([0.5, 0.5])))
+      l8 = np.asfarray((0, 0, 0, 1))
+      l9 = np.asfarray((1, 0, 1, 1))
+      ret, p = self.cv_util.FindLineIntersection(l8, l9)
+      self.assertFalse(ret)
+      self.assertTrue(np.isnan(p[0]))
+
+    def testExtendLines(self):
+      l1 = (-1, 0, 1, 0)
+      l2 = (0, -1, 0, 1)
+      l3 = (4, 4, 6, 6)
+      l4 = (1, 1, 1, 1)
+      lines = self.cv_util.ExtendLines(np.asfarray([l1, l2, l3, l4],
+                                                   dtype=np.float64), 10)
+      lines = np.around(lines, 10)
+      expected0 = ((5.0, 0.0, -5.0, 0.0))
+      self.assertAlmostEqual(np.sum(np.abs(np.subtract(lines[0], expected0))),
+                             0.0, 7)
+      expected1 = ((0.0, 5.0, 0.0, -5.0))
+      self.assertAlmostEqual(np.sum(np.abs(np.subtract(lines[1], expected1))),
+                             0.0, 7)
+
+      off = np.divide(np.sqrt(50), 2, dtype=np.float64)
+      expected2 = ((5 + off, 5 + off, 5 - off, 5 - off))
+      self.assertAlmostEqual(np.sum(np.abs(np.subtract(lines[2], expected2))),
+                             0.0, 7)
+      expected3 = ((-4, 1, 6, 1))
+      self.assertAlmostEqual(np.sum(np.abs(np.subtract(lines[3], expected3))),
+                             0.0, 7)
+
+    def testIsPointApproxOnLine(self):
+      p1 = np.asfarray((-1, -1))
+      l1 = np.asfarray((0, 0, 100, 100))
+      p2 = np.asfarray((1, 2))
+      p3 = np.asfarray((2, 1))
+      p4 = np.asfarray((3, 1))
+      self.assertTrue(self.cv_util.IsPointApproxOnLine(p1, l1, 1 + 1e-7))
+      self.assertTrue(self.cv_util.IsPointApproxOnLine(p2, l1, 1 + 1e-7))
+      self.assertTrue(self.cv_util.IsPointApproxOnLine(p3, l1, 1 + 1e-7))
+      self.assertFalse(self.cv_util.IsPointApproxOnLine(p4, l1, 1 + 1e-7))
+
+    def testSqDistances(self):
+      p1 = np.array([[0, 2], [0, 3]])
+      p2 = np.array([2, 0])
+      dists = self.cv_util.SqDistance(p1, p2)
+      self.assertEqual(dists[0], 8)
+      self.assertEqual(dists[1], 13)
+
+    def testSqDistance(self):
+      p1 = np.array([0, 2])
+      p2 = np.array([2, 0])
+      self.assertEqual(self.cv_util.SqDistance(p1, p2), 8)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/image_processing/fake_frame_generator.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/image_processing/fake_frame_generator.py
new file mode 100644
index 0000000..cd77541
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/image_processing/fake_frame_generator.py
@@ -0,0 +1,62 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.internal.image_processing import frame_generator
+from telemetry.internal.util import external_modules
+
+np = external_modules.ImportRequiredModule('numpy')
+
+
+class FakeFrameGenerator(frame_generator.FrameGenerator):
+  """ Fakes a Frame Generator, for testing.
+
+  Attributes:
+    _frame_index: A frame read counter.
+    _timestamps: A generator of timestamps to return, or None.
+    _timestamp: The current timestamp.
+    _dimensions: The dimensions to return.
+    _channels: The number of color channels to return in the generated frames.
+    _frames: The number of frames to return before fake EOF."""
+  def __init__(self, frames=1e16, dimensions=(320, 240), channels=3,
+               timestamps=(x for x in iter(int, 1))):
+    """ Initializes the FakeFrameGenerator object.
+
+    Args:
+      frames: int, The number of frames to return before fake EOF.
+      dimensions: (int, int), The dimensions to return.
+      timestamps: generator, A generator of timestamps to return. The default
+          value is an infinite 0 generator.
+      channels: int, The number of color channels to return in the generated
+          frames, 1 for greyscale, 3 for RGB."""
+    self._dimensions = dimensions
+    self._timestamps = timestamps
+    self._timestamp = 0
+    self._frame_index = -1
+    self._channels = channels
+    self._frames = frames
+
+    super(FakeFrameGenerator, self).__init__()
+
+  # OVERRIDE
+  def _CreateGenerator(self):
+    while self._frame_index < self._frames - 1:
+      self._frame_index += 1
+      self._timestamp = next(self._timestamps)
+      yield np.zeros((self._dimensions[0], self._dimensions[1],
+                      self._channels), np.uint8)
+
+  # OVERRIDE
+  @property
+  def CurrentTimestamp(self):
+    return self._timestamp
+
+  # OVERRIDE
+  @property
+  def CurrentFrameNumber(self):
+    return self._frame_index
+
+  # OVERRIDE
+  @property
+  def Dimensions(self):
+    return self._dimensions
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/image_processing/frame_generator.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/image_processing/frame_generator.py
new file mode 100644
index 0000000..b701418
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/image_processing/frame_generator.py
@@ -0,0 +1,62 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import abc
+
+
+class FrameReadError(Exception):
+  pass
+
+
+class FrameGenerator(object):
+  """ Defines an interface for reading input frames.
+
+  Attributes:
+    _generator: A reference to the created generator.
+  """
+  __metaclass__ = abc.ABCMeta
+
+  def __init__(self):
+    """ Initializes the FrameGenerator object. """
+    self._generator = self._CreateGenerator()
+
+  @abc.abstractmethod
+  def _CreateGenerator(self):
+    """ Creates a new generator.
+
+    Implemented in derived classes.
+
+    Raises:
+      FrameReadError: A error occurred in reading the frame.
+    """
+    raise NotImplementedError
+
+  @property
+  def Generator(self):
+    """ Returns:
+          A reference to the created generator.
+    """
+    return self._generator
+
+  @abc.abstractproperty
+  def CurrentTimestamp(self):
+    """ Returns:
+          float, The timestamp of the current frame in milliseconds.
+    """
+    raise NotImplementedError
+
+  @abc.abstractproperty
+  def CurrentFrameNumber(self):
+    """ Returns:
+          int, The frame index of the current frame.
+    """
+    raise NotImplementedError
+
+  @abc.abstractproperty
+  def Dimensions(self):
+    """ Returns:
+          The dimensions of the frame sequence as a tuple int (width, height).
+          This value should be constant across frames.
+    """
+    raise NotImplementedError
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/image_processing/image_util_bitmap_impl.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/image_processing/image_util_bitmap_impl.py
new file mode 100644
index 0000000..18e4554
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/image_processing/image_util_bitmap_impl.py
@@ -0,0 +1,50 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from __future__ import division
+
+from telemetry.internal.image_processing import _bitmap
+
+
+def Channels(bitmap):
+  return bitmap.bpp
+
+def Width(bitmap):
+  return bitmap.width
+
+def Height(bitmap):
+  return bitmap.height
+
+def Pixels(bitmap):
+  return bitmap.pixels
+
+def GetPixelColor(bitmap, x, y):
+  return bitmap.GetPixelColor(x, y)
+
+def WritePngFile(bitmap, path):
+  bitmap.WritePngFile(path)
+
+def FromRGBPixels(width, height, pixels, bpp):
+  return _bitmap.Bitmap(bpp, width, height, pixels)
+
+def FromPng(png_data):
+  return _bitmap.Bitmap.FromPng(png_data)
+
+def FromPngFile(path):
+  return _bitmap.Bitmap.FromPngFile(path)
+
+def AreEqual(bitmap1, bitmap2, tolerance, _):
+  return bitmap1.IsEqual(bitmap2, tolerance)
+
+def Diff(bitmap1, bitmap2):
+  return bitmap1.Diff(bitmap2)
+
+def GetBoundingBox(bitmap, color, tolerance):
+  return bitmap.GetBoundingBox(color, tolerance)
+
+def Crop(bitmap, left, top, width, height):
+  return bitmap.Crop(left, top, width, height)
+
+def GetColorHistogram(bitmap, ignore_color, tolerance):
+  return bitmap.ColorHistogram(ignore_color, tolerance)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/image_processing/image_util_numpy_impl.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/image_processing/image_util_numpy_impl.py
new file mode 100644
index 0000000..5b71429
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/image_processing/image_util_numpy_impl.py
@@ -0,0 +1,187 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from __future__ import division
+
+import warnings
+
+from telemetry.internal.util import external_modules
+from telemetry.util import color_histogram
+from telemetry.util import rgba_color
+import png
+
+cv2 = external_modules.ImportOptionalModule('cv2')
+np = external_modules.ImportRequiredModule('numpy')
+
+
+def Channels(image):
+  return image.shape[2]
+
+def Width(image):
+  return image.shape[1]
+
+def Height(image):
+  return image.shape[0]
+
+def Pixels(image):
+  return bytearray(np.uint8(image[:, :, ::-1]).flat)  # Convert from bgr to rgb.
+
+def GetPixelColor(image, x, y):
+  bgr = image[y][x]
+  return rgba_color.RgbaColor(bgr[2], bgr[1], bgr[0])
+
+def WritePngFile(image, path):
+  if cv2 is not None:
+    cv2.imwrite(path, image)
+  else:
+    with open(path, "wb") as f:
+      metadata = {}
+      metadata['size'] = (Width(image), Height(image))
+      metadata['alpha'] = False
+      metadata['bitdepth'] = 8
+      img = image[:, :, ::-1]
+      pixels = img.reshape(-1).tolist()
+      png.Writer(**metadata).write_array(f, pixels)
+
+def FromRGBPixels(width, height, pixels, bpp):
+  img = np.array(pixels, order='F', dtype=np.uint8)
+  img.resize((height, width, bpp))
+  if bpp == 4:
+    img = img[:, :, :3]  # Drop alpha.
+  return img[:, :, ::-1]  # Convert from rgb to bgr.
+
+def FromPngFile(path):
+  if cv2 is not None:
+    img = cv2.imread(path, cv2.CV_LOAD_IMAGE_COLOR)
+    if img is None:
+      raise ValueError('Image at path {0} could not be read'.format(path))
+    return img
+  else:
+    with open(path, "rb") as f:
+      return FromPng(f.read())
+
+def FromPng(png_data):
+  if cv2 is not None:
+    file_bytes = np.asarray(bytearray(png_data), dtype=np.uint8)
+    return cv2.imdecode(file_bytes, cv2.CV_LOAD_IMAGE_COLOR)
+  else:
+    warnings.warn(
+        'Using pure python png decoder, which could be very slow. To speed up, '
+        'consider installing numpy & cv2 (OpenCV).')
+    width, height, pixels, meta = png.Reader(bytes=png_data).read_flat()
+    return FromRGBPixels(width, height, pixels, 4 if meta['alpha'] else 3)
+
+def _SimpleDiff(image1, image2):
+  if cv2 is not None:
+    return cv2.absdiff(image1, image2)
+  else:
+    amax = np.maximum(image1, image2)
+    amin = np.minimum(image1, image2)
+    return amax - amin
+
+def AreEqual(image1, image2, tolerance, likely_equal):
+  if image1.shape != image2.shape:
+    return False
+  self_image = image1
+  other_image = image2
+  if tolerance:
+    if likely_equal:
+      return np.amax(_SimpleDiff(image1, image2)) <= tolerance
+    else:
+      for row in xrange(Height(image1)):
+        if np.amax(_SimpleDiff(image1[row], image2[row])) > tolerance:
+          return False
+      return True
+  else:
+    if likely_equal:
+      return (self_image == other_image).all()
+    else:
+      for row in xrange(Height(image1)):
+        if not (self_image[row] == other_image[row]).all():
+          return False
+      return True
+
+def Diff(image1, image2):
+  self_image = image1
+  other_image = image2
+  if image1.shape[2] != image2.shape[2]:
+    raise ValueError('Cannot diff images of differing bit depth')
+  if image1.shape[:2] != image2.shape[:2]:
+    width = max(Width(image1), Width(image2))
+    height = max(Height(image1), Height(image2))
+    self_image = np.zeros((width, height, image1.shape[2]), np.uint8)
+    other_image = np.zeros((width, height, image1.shape[2]), np.uint8)
+    self_image[0:Height(image1), 0:Width(image1)] = image1
+    other_image[0:Height(image2), 0:Width(image2)] = image2
+  return _SimpleDiff(self_image, other_image)
+
+def GetBoundingBox(image, color, tolerance):
+  if cv2 is not None:
+    color = np.array([color.b, color.g, color.r])
+    img = cv2.inRange(image, np.subtract(color[0:3], tolerance),
+                      np.add(color[0:3], tolerance))
+    count = cv2.countNonZero(img)
+    if count == 0:
+      return None, 0
+    contours, _ = cv2.findContours(img, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
+    contour = np.concatenate(contours)
+    return cv2.boundingRect(contour), count
+  else:
+    if tolerance:
+      color = np.array([color.b, color.g, color.r])
+      colorm = color - tolerance
+      colorp = color + tolerance
+      b = image[:, :, 0]
+      g = image[:, :, 1]
+      r = image[:, :, 2]
+      w = np.where(((b >= colorm[0]) & (b <= colorp[0]) &
+                    (g >= colorm[1]) & (g <= colorp[1]) &
+                    (r >= colorm[2]) & (r <= colorp[2])))
+    else:
+      w = np.where((image[:, :, 0] == color.b) &
+                   (image[:, :, 1] == color.g) &
+                   (image[:, :, 2] == color.r))
+    if len(w[0]) == 0:
+      return None, 0
+    return (w[1][0], w[0][0], w[1][-1] - w[1][0] + 1, w[0][-1] - w[0][0] + 1), \
+        len(w[0])
+
+def Crop(image, left, top, width, height):
+  img_height, img_width = image.shape[:2]
+  if (left < 0 or top < 0 or
+      (left + width) > img_width or
+      (top + height) > img_height):
+    raise ValueError('Invalid dimensions')
+  return image[top:top + height, left:left + width]
+
+def GetColorHistogram(image, ignore_color, tolerance):
+  if cv2 is not None:
+    mask = None
+    if ignore_color is not None:
+      color = np.array([ignore_color.b, ignore_color.g, ignore_color.r])
+      mask = ~cv2.inRange(image, np.subtract(color, tolerance),
+                          np.add(color, tolerance))
+
+    flatten = np.ndarray.flatten
+    hist_b = flatten(cv2.calcHist([image], [0], mask, [256], [0, 256]))
+    hist_g = flatten(cv2.calcHist([image], [1], mask, [256], [0, 256]))
+    hist_r = flatten(cv2.calcHist([image], [2], mask, [256], [0, 256]))
+  else:
+    filtered = image.reshape(-1, 3)
+    if ignore_color is not None:
+      color = np.array([ignore_color.b, ignore_color.g, ignore_color.r])
+      colorm = np.array(color) - tolerance
+      colorp = np.array(color) + tolerance
+      in_range = ((filtered[:, 0] < colorm[0]) | (filtered[:, 0] > colorp[0]) |
+                  (filtered[:, 1] < colorm[1]) | (filtered[:, 1] > colorp[1]) |
+                  (filtered[:, 2] < colorm[2]) | (filtered[:, 2] > colorp[2]))
+      filtered = np.compress(in_range, filtered, axis=0)
+    if len(filtered[:, 0]) == 0:
+      return color_histogram.ColorHistogram(np.zeros((256)), np.zeros((256)),
+                                      np.zeros((256)), ignore_color)
+    hist_b = np.bincount(filtered[:, 0], minlength=256)
+    hist_g = np.bincount(filtered[:, 1], minlength=256)
+    hist_r = np.bincount(filtered[:, 2], minlength=256)
+
+  return color_histogram.ColorHistogram(hist_r, hist_g, hist_b, ignore_color)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/image_processing/screen_finder.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/image_processing/screen_finder.py
new file mode 100755
index 0000000..932d6df
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/image_processing/screen_finder.py
@@ -0,0 +1,857 @@
+#!/usr/bin/env python
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+#
+# This script attempts to detect the region of a camera's field of view that
+# contains the screen of the device we are testing.
+#
+# Usage: ./screen_finder.py path_to_video 0 0 --verbose
+
+from __future__ import division
+
+import copy
+import logging
+import os
+import sys
+
+if __name__ == '__main__':
+  sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..'))
+
+from telemetry.internal.image_processing import cv_util
+from telemetry.internal.image_processing import frame_generator as \
+    frame_generator_module
+from telemetry.internal.image_processing import video_file_frame_generator
+from telemetry.internal.util import external_modules
+
+np = external_modules.ImportRequiredModule('numpy')
+cv2 = external_modules.ImportRequiredModule('cv2')
+
+
+class ScreenFinder(object):
+  """Finds and extracts device screens from video.
+
+  Sample Usage:
+    sf = ScreenFinder(sys.argv[1])
+    while sf.HasNext():
+      ret, screen = sf.GetNext()
+
+  Attributes:
+    _lost_corners: Each index represents whether or not we lost track of that
+        corner on the previous frame. Ordered by [top-right, top-left,
+        bottom-left, bottom-right]
+    _frame: An unmodified copy of the frame we're currently processing.
+    _frame_debug: A copy of the frame we're currently processing, may be
+        modified at any time, used for debugging.
+    _frame_grey: A greyscale copy of the frame we're currently processing.
+    _frame_edges: A Canny Edge detected copy of the frame we're currently
+        processing.
+    _screen_size: The size of device screen in the video when first detected.
+    _avg_corners: Exponentially weighted average of the previous corner
+        locations.
+    _prev_corners: The location of the corners in the previous frame.
+    _lost_corner_frames: A counter of the number of successive frames in which
+        we've lost a corner location.
+    _border: See |border| above.
+    _min_line_length: The minimum length a line must be before we consider it
+        a possible screen edge.
+    _frame_generator: See |frame_generator| above.
+    _width, _height: The width and height of the frame.
+    _anglesp5, _anglesm5: The angles for each point we look at in the grid
+        when computing brightness, constant across frames."""
+
+  class ScreenNotFoundError(Exception):
+    pass
+
+  # Square of the distance a corner can travel in pixels between frames
+  MAX_INTERFRAME_MOTION = 25
+  # The minimum width line that may be considered a screen edge.
+  MIN_SCREEN_WIDTH = 40
+  # Number of frames with lost corners before we ignore MAX_INTERFRAME_MOTION
+  RESET_AFTER_N_BAD_FRAMES = 2
+  # The weight applied to the new screen location when exponentially averaging
+  # screen location.
+  # TODO(mthiesse): This should be framerate dependent, for lower framerates
+  # this value should approach 1. For higher framerates, this value should
+  # approach 0. The current 0.5 value works well in testing with 240 FPS.
+  CORNER_AVERAGE_WEIGHT = 0.5
+
+  # TODO(mthiesse): Investigate how to select the constants used here. In very
+  # bright videos, twice as bright may be too high, and the minimum of 60 may
+  # be too low.
+  # The factor by which a quadrant at an intersection must be brighter than
+  # the other quadrants to be considered a screen corner.
+  MIN_RELATIVE_BRIGHTNESS_FACTOR = 1.5
+  # The minimum average brightness required of an intersection quadrant to
+  # be considered a screen corner (on a scale of 0-255).
+  MIN_CORNER_ABSOLUTE_BRIGHTNESS = 60
+
+  # Low and high hysteresis parameters to be passed to the Canny edge
+  # detection algorithm.
+  CANNY_HYSTERESIS_THRESH_LOW = 300
+  CANNY_HYSTERESIS_THRESH_HIGH = 500
+
+  SMALL_ANGLE = 5 / 180 * np.pi  # 5 degrees in radians
+
+  DEBUG = False
+
+  def __init__(self, frame_generator, border=5):
+    """Initializes the ScreenFinder object.
+
+    Args:
+      frame_generator: FrameGenerator, An initialized Video Frame Generator.
+      border: int, number of pixels of border to be kept when cropping the
+          detected screen.
+
+    Raises:
+      FrameReadError: The frame generator may output a read error during
+          initialization."""
+    assert isinstance(frame_generator, frame_generator_module.FrameGenerator)
+    self._lost_corners = [False, False, False, False]
+    self._frame_debug = None
+    self._frame = None
+    self._frame_grey = None
+    self._frame_edges = None
+    self._screen_size = None
+    self._avg_corners = None
+    self._prev_corners = None
+    self._lost_corner_frames = 0
+    self._border = border
+    self._min_line_length = self.MIN_SCREEN_WIDTH
+    self._frame_generator = frame_generator
+    self._anglesp5 = None
+    self._anglesm5 = None
+
+    if not self._InitNextFrame():
+      logging.warn('Not enough frames in video feed!')
+      return
+
+    self._height, self._width = self._frame.shape[:2]
+
+  def _InitNextFrame(self):
+    """Called after processing each frame, reads in the next frame to ensure
+    HasNext() is accurate."""
+    self._frame_debug = None
+    self._frame = None
+    self._frame_grey = None
+    self._frame_edges = None
+    try:
+      frame = next(self._frame_generator.Generator)
+    except StopIteration:
+      return False
+    self._frame = frame
+    self._frame_debug = copy.copy(frame)
+    self._frame_grey = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
+    self._frame_edges = cv2.Canny(self._frame_grey,
+                                  self.CANNY_HYSTERESIS_THRESH_LOW,
+                                  self.CANNY_HYSTERESIS_THRESH_HIGH)
+    return True
+
+  def HasNext(self):
+    """True if there are more frames available to process. """
+    return self._frame is not None
+
+  def GetNext(self):
+    """Gets the next screen image.
+
+    Returns:
+      A numpy matrix containing the screen surrounded by the number of border
+      pixels specified in initialization, and the location of the detected
+      screen corners in the current frame, if a screen is found. The returned
+      screen is guaranteed to be the same size at each frame.
+      'None' and 'None' if no screen was found on the current frame.
+
+    Raises:
+      FrameReadError: An error occurred in the FrameGenerator.
+      RuntimeError: This method was called when no frames were available."""
+    if self._frame is None:
+      raise RuntimeError('No more frames available.')
+
+    logging.info('Processing frame: %d',
+                 self._frame_generator.CurrentFrameNumber)
+
+    # Finds straight lines in the image.
+    hlines = cv2.HoughLinesP(self._frame_edges, 1, np.pi / 180, 60,
+                             minLineLength=self._min_line_length,
+                             maxLineGap=100)
+
+    # Extends these straight lines to be long enough to ensure the screen edge
+    # lines intersect.
+    lines = cv_util.ExtendLines(np.float32(hlines[0]), 10000) \
+        if hlines is not None else []
+
+    # Find intersections in the lines; these are likely to be screen corners.
+    intersections = self._FindIntersections(lines)
+    if len(intersections[:, 0]) > 0:
+      points = np.vstack(intersections[:, 0].flat)
+      if (self._prev_corners is not None and len(points) >= 4 and
+          not self._HasMovedFast(points, self._prev_corners)):
+        corners = self._prev_corners
+        missing_corners = 0
+      else:
+        # Extract the corners from all intersections.
+        corners, missing_corners = self._FindCorners(
+            intersections, self._frame_grey)
+    else:
+      corners = np.empty((4, 2), np.float32)
+      corners[:] = np.nan
+      missing_corners = 4
+
+    screen = None
+    found_screen = True
+    final_corners = None
+    try:
+      # Handle the cases where we have missing corners.
+      screen_corners = self._NewScreenLocation(
+          corners, missing_corners, intersections)
+
+      final_corners = self._SmoothCorners(screen_corners)
+
+      # Create a perspective transform from our corners.
+      transform, w, h = self._GetTransform(final_corners, self._border)
+
+      # Apply the perspective transform to get our output.
+      screen = cv2.warpPerspective(
+          self._frame, transform, (int(w + 0.5), int(h + 0.5)))
+
+      self._prev_corners = final_corners
+
+    except self.ScreenNotFoundError as e:
+      found_screen = False
+      logging.info(e)
+
+    if self.DEBUG:
+      self._Debug(lines, corners, final_corners, screen)
+
+    self._InitNextFrame()
+    if found_screen:
+      return screen, self._prev_corners
+    return None, None
+
+  def _FindIntersections(self, lines):
+    """Finds intersections in a set of lines.
+
+    Filters pairs of lines that are less than 45 degrees apart. Filtering
+    these pairs helps dramatically reduce the number of points we have to
+    process, as these points could not represent screen corners anyways.
+
+    Returns:
+      The intersections, represented as a tuple of (point, line, line) of the
+      points and the lines that intersect there of all lines in the array that
+      are more than 45 degrees apart."""
+    intersections = np.empty((0, 3), np.float32)
+    for i in xrange(0, len(lines)):
+      for j in xrange(i + 1, len(lines)):
+        # Filter lines that are less than 45 (or greater than 135) degrees
+        # apart.
+        if not cv_util.AreLinesOrthogonal(lines[i], lines[j], (np.pi / 4.0)):
+          continue
+        ret, point = cv_util.FindLineIntersection(lines[i], lines[j])
+        point = np.float32(point)
+        if not ret:
+          continue
+        # If we know where the previous corners are, we can also filter
+        # intersections that are too far away from the previous corners to be
+        # where the screen has moved.
+        if self._prev_corners is not None and \
+           self._lost_corner_frames <= self.RESET_AFTER_N_BAD_FRAMES and \
+           not self._PointIsCloseToPreviousCorners(point):
+          continue
+        intersections = np.vstack((intersections,
+                                   np.array((point, lines[i], lines[j]))))
+    return intersections
+
+  def _PointIsCloseToPreviousCorners(self, point):
+    """True if the point is close to the previous corners."""
+    max_dist = self.MAX_INTERFRAME_MOTION
+    if cv_util.SqDistance(self._prev_corners[0], point) <= max_dist or \
+       cv_util.SqDistance(self._prev_corners[1], point) <= max_dist or \
+       cv_util.SqDistance(self._prev_corners[2], point) <= max_dist or \
+       cv_util.SqDistance(self._prev_corners[3], point) <= max_dist:
+      return True
+    return False
+
+  def _HasMovedFast(self, corners, prev_corners):
+    min_dist = np.zeros(4, np.float32)
+    for i in xrange(4):
+      dist = np.min(cv_util.SqDistances(corners, prev_corners[i]))
+      min_dist[i] = dist
+    # 3 corners can move up to one pixel before we consider the screen to have
+    # moved. TODO(mthiesse): Should this be relaxed? Resolution dependent?
+    if np.sum(min_dist) < 3:
+      return False
+    return True
+
+  class CornerData(object):
+
+    def __init__(self, corner_index, corner_location, brightness_score, line1,
+                 line2):
+      self.corner_index = corner_index
+      self.corner_location = corner_location
+      self.brightness_score = brightness_score
+      self.line1 = line1
+      self.line2 = line2
+
+    def __gt__(self, corner_data2):
+      return self.corner_index > corner_data2.corner_index
+
+    def __repr__(self):
+      return ('\nCorner index: ' + str(self.corner_index) +
+              ',\nCorner location: ' + str(self.corner_location) +
+              ',\nBrightness score: ' + str(self.brightness_score) +
+              ',\nline1: ' + str(self.line1) + ',\nline2: ' + str(self.line2))
+
+  def _FindCorners(self, intersections, grey_frame):
+    """Finds the screen corners in the image.
+
+    Given the set of intersections in the image, finds the intersections most
+    likely to be corners.
+
+    Args:
+      intersections: The array of intersections in the image.
+      grey_frame: The greyscale frame we're processing.
+
+    Returns:
+      An array of length 4 containing the positions of the corners, or nan for
+      each index where a corner could not be found, and a count of the number
+      of missing corners.
+      The corners are ordered as follows:
+        1 | 0
+        -----
+        2 | 3
+      Ex. 3 corners are found from a square of width 2 centered at the origin,
+      the output would look like:
+          '[[1, 1], [np.nan, np.nan], [-1, -1], [1, -1]], 1'"""
+    filtered = []
+    corners = np.empty((0, 2), np.float32)
+    for corner_pos, score, point, line1, line2 in \
+        self._LooksLikeCorner(intersections, grey_frame):
+      if self.DEBUG:
+        center = (int(point[0] + 0.5), int(point[1] + 0.5))
+        cv2.circle(self._frame_debug, center, 5, (0, 255, 0), 1)
+      point.resize(1, 2)
+      corners = np.append(corners, point, axis=0)
+      point.resize(2,)
+      corner_data = self.CornerData(corner_pos, point, score, line1, line2)
+      filtered.append(corner_data)
+
+    # De-duplicate corners because we may have found many false positives, or
+    # near-misses.
+    self._DeDupCorners(filtered, corners)
+
+    # Strip everything but the corner location.
+    filtered_corners = np.array(
+        [corner_data.corner_location for corner_data in filtered])
+    corner_indices = [corner_data.corner_index for corner_data in filtered]
+
+    # If we have found a corner to replace a lost corner, we want to check
+    # that the corner is not erroneous by ensuring it makes a rectangle with
+    # the 3 known good corners.
+    if len(filtered) == 4:
+      for i in xrange(4):
+        point_info = (filtered[i].corner_location,
+                      filtered[i].line1,
+                      filtered[i].line2)
+        if (self._lost_corners[i] and
+            not self._PointConnectsToCorners(filtered_corners, point_info)):
+          filtered_corners = np.delete(filtered_corners, i, 0)
+          corner_indices = np.delete(corner_indices, i, 0)
+          break
+
+    # Ensure corners are sorted properly, inserting nans for missing corners.
+    sorted_corners = np.empty((4, 2), np.float32)
+    sorted_corners[:] = np.nan
+    for i in xrange(len(filtered_corners)):
+      sorted_corners[corner_indices[i]] = filtered_corners[i]
+
+    # From this point on, our corners arrays are guaranteed to have 4
+    # elements, though some may be nan.
+
+    # Filter corners that have moved too far from the previous corner if we
+    # are not resetting known corner information.
+    reset_corners = (
+        (self._lost_corner_frames > self.RESET_AFTER_N_BAD_FRAMES)
+        and len(filtered_corners) == 4)
+    if self._prev_corners is not None and not reset_corners:
+      sqdists = cv_util.SqDistances(self._prev_corners, sorted_corners)
+      for i in xrange(4):
+        if np.isnan(sorted_corners[i][0]):
+          continue
+        if sqdists[i] > self.MAX_INTERFRAME_MOTION:
+          sorted_corners[i] = np.nan
+
+    real_corners = self._FindExactCorners(sorted_corners)
+    missing_corners = np.count_nonzero(np.isnan(real_corners)) / 2
+    return real_corners, missing_corners
+
+  def _LooksLikeCorner(self, intersections, grey_frame):
+    """Finds any intersections of lines that look like a screen corner.
+
+    Args:
+      intersections: The numpy array of points, and the lines that intersect
+          at the given point.
+      grey_frame: The greyscale frame we're processing.
+
+    Returns:
+      An array of: The corner location (0-3), the relative brightness score
+      (to be used to de-duplicate corners later), the point, and the lines
+      that make up the intersection, for all intersections that look like a
+      corner."""
+    points = np.vstack(intersections[:, 0].flat)
+    lines1 = np.vstack(intersections[:, 1].flat)
+    lines2 = np.vstack(intersections[:, 2].flat)
+    # Map the image to four quadrants defined as the regions between each of
+    # the lines that make up the intersection.
+    line1a1 = np.pi - np.arctan2(lines1[:, 1] - points[:, 1],
+                                 lines1[:, 0] - points[:, 0])
+    line1a2 = np.pi - np.arctan2(lines1[:, 3] - points[:, 1],
+                                 lines1[:, 2] - points[:, 0])
+    line2a1 = np.pi - np.arctan2(lines2[:, 1] - points[:, 1],
+                                 lines2[:, 0] - points[:, 0])
+    line2a2 = np.pi - np.arctan2(lines2[:, 3] - points[:, 1],
+                                 lines2[:, 2] - points[:, 0])
+    line1a1 = line1a1.reshape(-1, 1)
+    line1a2 = line1a2.reshape(-1, 1)
+    line2a1 = line2a1.reshape(-1, 1)
+    line2a2 = line2a2.reshape(-1, 1)
+
+    line_angles = np.concatenate((line1a1, line1a2, line2a1, line2a2), axis=1)
+    np.ndarray.sort(line_angles)
+
+    # TODO(mthiesse): Investigate whether these should scale with image or
+    # screen size. My intuition is that these don't scale with image size,
+    # though they may be affected by image quality and how blurry the corners
+    # are. See stackoverflow.com/q/7765810/ for inspiration.
+    avg_range = 8.0
+    num_points = 7
+
+    points_m_avg = points - avg_range
+    points_p_avg = points + avg_range
+    # Exclude points near frame boundaries.
+    include = np.where((points_m_avg[:, 0] > 0) & (points_m_avg[:, 1] > 0) &
+                       (points_p_avg[:, 0] < self._width) &
+                       (points_p_avg[:, 1] < self._height))
+    line_angles = line_angles[include]
+    points = points[include]
+    lines1 = lines1[include]
+    lines2 = lines2[include]
+    points_m_avg = points_m_avg[include]
+    points_p_avg = points_p_avg[include]
+    # Perform a 2-d linspace to generate the x, y ranges for each
+    # intersection.
+    arr1 = points_m_avg[:, 0].reshape(-1, 1)
+    arr2 = points_p_avg[:, 0].reshape(-1, 1)
+    lin = np.linspace(0, 1, num_points)
+    x_range = arr1 + (arr2 - arr1) * lin
+    arr1 = points_m_avg[:, 1].reshape(-1, 1)
+    arr2 = points_p_avg[:, 1].reshape(-1, 1)
+    y_range = arr1 + (arr2 - arr1) * lin
+
+    # The angles for each point we look at in the grid when computing
+    # brightness are constant across frames, so we can generate them once.
+    if self._anglesp5 is None:
+      ind = np.transpose([np.tile(x_range[0], num_points),
+                          np.repeat(y_range[0], num_points)])
+      vectors = ind - points[0]
+      angles = np.arctan2(vectors[:, 1], vectors[:, 0]) + np.pi
+      self._anglesp5 = angles + self.SMALL_ANGLE
+      self._anglesm5 = angles - self.SMALL_ANGLE
+    results = []
+    for i in xrange(len(y_range)):
+      # Generate our filters for which points belong to which quadrant.
+      one = np.where((self._anglesp5 <= line_angles[i, 1]) &
+                     (self._anglesm5 >= line_angles[i, 0]))
+      two = np.where((self._anglesp5 <= line_angles[i, 2]) &
+                     (self._anglesm5 >= line_angles[i, 1]))
+      thr = np.where((self._anglesp5 <= line_angles[i, 3]) &
+                     (self._anglesm5 >= line_angles[i, 2]))
+      fou = np.where((self._anglesp5 <= line_angles[i, 0]) |
+                     (self._anglesm5 >= line_angles[i, 3]))
+      # Take the cartesian product of our x and y ranges to get the full list
+      # of pixels to look at.
+      ind = np.transpose([np.tile(x_range[i], num_points),
+                          np.repeat(y_range[i], num_points)])
+
+      # Filter the full list by which indices belong to which quadrant, and
+      # convert to integers so we can index with them.
+      one_i = np.int32(np.rint(ind[one[0]]))
+      two_i = np.int32(np.rint(ind[two[0]]))
+      thr_i = np.int32(np.rint(ind[thr[0]]))
+      fou_i = np.int32(np.rint(ind[fou[0]]))
+
+      # Average the brightness of the pixels that belong to each quadrant.
+      q_1 = np.average(grey_frame[one_i[:, 1], one_i[:, 0]])
+      q_2 = np.average(grey_frame[two_i[:, 1], two_i[:, 0]])
+      q_3 = np.average(grey_frame[thr_i[:, 1], thr_i[:, 0]])
+      q_4 = np.average(grey_frame[fou_i[:, 1], fou_i[:, 0]])
+
+      avg_intensity = [(q_4, 0), (q_1, 1), (q_2, 2), (q_3, 3)]
+      # Sort by intensity.
+      avg_intensity.sort(reverse=True)
+
+      # Treat the point as a corner if one quadrant is at least twice as
+      # bright as the next brightest quadrant, with a minimum brightness
+      # requirement.
+      tau = (2.0 * np.pi)
+      min_factor = self.MIN_RELATIVE_BRIGHTNESS_FACTOR
+      min_brightness = self.MIN_RELATIVE_BRIGHTNESS_FACTOR
+      if avg_intensity[0][0] > avg_intensity[1][0] * min_factor and \
+         avg_intensity[0][0] > min_brightness:
+        bright_corner = avg_intensity[0][1]
+        if bright_corner == 0:
+          angle = np.pi - (line_angles[i, 0] + line_angles[i, 3]) / 2.0
+          if angle < 0:
+            angle = angle + tau
+        else:
+          angle = tau - (line_angles[i, bright_corner] +
+                         line_angles[i, bright_corner - 1]) / 2.0
+        score = avg_intensity[0][0] - avg_intensity[1][0]
+        # TODO(mthiesse): int(angle / (pi / 2.0)) will break if the screen is
+        # rotated through 45 degrees. Probably many other things will break as
+        # well, movement of corners from one quadrant to another hasn't been
+        # tested. We should support this eventually, but this is unlikely to
+        # cause issues for any test setups.
+        results.append((int(angle / (np.pi / 2.0)), score, points[i],
+                        lines1[i], lines2[i]))
+    return results
+
+  def _DeDupCorners(self, corner_data, corners):
+    """De-duplicate corners based on corner_index.
+
+    For each set of points representing a corner: If one point is part of the
+    rectangle and the other is not, filter the other one. If both or none are
+    part of the rectangle, filter based on score (highest relative brightness
+    of a quadrant). The reason we allow for neither to be part of the
+    rectangle is because we may not have found all four corners of the
+    rectangle, and in degenerate cases like this it's better to find 3 likely
+    corners than none.
+
+    Modifies corner_data directly.
+
+    Args:
+      corner_data: CornerData for each potential corner in the frame.
+      corners: List of all potential corners in the frame."""
+    # TODO(mthiesse): Ensure that the corners form a sensible rectangle. For
+    # example, it is currently possible (but unlikely) to detect a 'screen'
+    # where the bottom-left corner is above the top-left corner, while the
+    # bottom-right corner is below the top-right corner.
+
+    # Sort by corner_index to make de-duping easier.
+    corner_data.sort()
+
+    # De-dup corners.
+    c_old = None
+    for i in xrange(len(corner_data) - 1, 0, -1):
+      if corner_data[i].corner_index != corner_data[i - 1].corner_index:
+        c_old = None
+        continue
+      if c_old is None:
+        point_info = (corner_data[i].corner_location,
+                      corner_data[i].line1,
+                      corner_data[i].line2)
+        c_old = self._PointConnectsToCorners(corners, point_info, 2)
+      point_info_new = (corner_data[i - 1].corner_location,
+                        corner_data[i - 1].line1,
+                        corner_data[i - 1].line2)
+      c_new = self._PointConnectsToCorners(corners, point_info_new, 2)
+      if (not (c_old or c_new)) or (c_old and c_new):
+        if (corner_data[i].brightness_score <
+            corner_data[i - 1].brightness_score):
+          del corner_data[i]
+          c_old = c_new
+        else:
+          del corner_data[i - 1]
+      elif c_old:
+        del corner_data[i - 1]
+      else:
+        del corner_data[i]
+        c_old = c_new
+
+  def _PointConnectsToCorners(self, corners, point_info, tolerance=1):
+    """Checks if the lines of an intersection intersect with corners.
+
+    This is useful to check if the point is part of a rectangle specified by
+    |corners|.
+
+    Args:
+      point_info: A tuple of (point, line, line) representing an intersection
+          of two lines.
+      corners: corners that (hopefully) make up a rectangle.
+      tolerance: The tolerance (approximately in pixels) of the distance
+          between the corners and the lines for detecting if the point is on
+          the line.
+
+    Returns:
+      True if each of the two lines that make up the intersection where the
+      point is located connect the point to other corners."""
+    line1_connected = False
+    line2_connected = False
+    point, line1, line2 = point_info
+    for corner in corners:
+      if corner is None:
+        continue
+
+      # Filter out points that are too close to one another to be different
+      # corners.
+      sqdist = cv_util.SqDistance(corner, point)
+      if sqdist < self.MIN_SCREEN_WIDTH * self.MIN_SCREEN_WIDTH:
+        continue
+
+      line1_connected = line1_connected or \
+          cv_util.IsPointApproxOnLine(corner, line1, tolerance)
+      line2_connected = line2_connected or \
+          cv_util.IsPointApproxOnLine(corner, line2, tolerance)
+    if line1_connected and line2_connected:
+      return True
+    return False
+
+  def _FindExactCorners(self, sorted_corners):
+    """Attempts to find more accurate corner locations.
+
+    Args:
+      sorted_corners: The four screen corners, sorted by corner_index.
+
+    Returns:
+      A list of 4 probably more accurate corners, still sorted."""
+    real_corners = np.empty((4, 2), np.float32)
+    # Count missing corners, and search in a small area around our
+    # intersections representing corners to see if we can find a more exact
+    # corner, as the position of the intersections is noisy and not always
+    # perfectly accurate.
+    for i in xrange(4):
+      corner = sorted_corners[i]
+      if np.isnan(corner[0]):
+        real_corners[i] = np.nan
+        continue
+
+      # Almost unbelievably, in edge cases with floating point error, the
+      # width/height of the cropped corner image may be 2 or 4. This is fine
+      # though, as long as the width and height of the cropped corner are not
+      # hard-coded anywhere.
+      corner_image = self._frame_edges[corner[1] - 1:corner[1] + 2,
+                                       corner[0] - 1:corner[0] + 2]
+      ret, p = self._FindExactCorner(i <= 1, i == 1 or i == 2, corner_image)
+      if ret:
+        if self.DEBUG:
+          self._frame_edges[corner[1] - 1 + p[1]][corner[0] - 1 + p[0]] = 128
+        real_corners[i] = corner - 1 + p
+      else:
+        real_corners[i] = corner
+    return real_corners
+
+  def _FindExactCorner(self, top, left, img):
+    """Tries to finds the exact corner location for a given corner.
+
+    Searches for the top or bottom, left or right most lit
+    pixel in an edge-detected image, which should represent, with pixel
+    precision, as accurate a corner location as possible. (Though perhaps
+    up-sampling using cubic spline interpolation could get sub-pixel
+    precision)
+
+    TODO(mthiesse): This algorithm could be improved by including a larger
+    region to search in, but would have to be made smarter about which lit
+    pixels are on the detected screen edge and which are a not as it's
+    currently extremely easy to fool by things like notification icons in
+    screen corners.
+
+    Args:
+      top: boolean, whether or not we're looking for a top corner.
+      left: boolean, whether or not we're looking for a left corner.
+      img: A small cropping of the edge detected image in which to search.
+
+    Returns:
+      True and the location if a better corner location is found,
+      False otherwise."""
+    h, w = img.shape[:2]
+    cy = 0
+    starting_x = w - 1 if left else 0
+    cx = starting_x
+    if top:
+      y_range = xrange(h - 1, -1, -1)
+    else:
+      y_range = xrange(0, h, 1)
+    if left:
+      x_range = xrange(w - 1, -1, -1)
+    else:
+      x_range = xrange(0, w, 1)
+    for y in y_range:
+      for x in x_range:
+        if img[y][x] == 255:
+          cy = y
+          if (left and x <= cx) or (not left and x >= cx):
+            cx = x
+    if cx == starting_x and cy == 0 and img[0][starting_x] != 255:
+      return False, (0, 0)
+    return True, (cx, cy)
+
+  def _NewScreenLocation(self, new_corners, missing_corners, intersections):
+    """Computes the new screen location with best effort.
+
+    Creates the final list of corners that represents the best effort attempt
+    to find the new screen location. Handles degenerate cases where 3 or fewer
+    new corners are present, using previous corner and intersection data.
+
+    Args:
+      new_corners: The corners found by our search for corners.
+      missing_corners: The count of how many corners we're missing.
+      intersections: The intersections of straight lines found in the current
+          frame.
+
+    Returns:
+      An array of 4 new_corners hopefully representing the screen, or throws
+      an error if this is not possible.
+
+    Raises:
+      ValueError: Finding the screen location was not possible."""
+    screen_corners = copy.copy(new_corners)
+    if missing_corners == 0:
+      self._lost_corner_frames = 0
+      self._lost_corners = [False, False, False, False]
+      return screen_corners
+    if self._prev_corners is None:
+      raise self.ScreenNotFoundError(
+          'Could not locate screen on frame %d' %
+          self._frame_generator.CurrentFrameNumber)
+
+    self._lost_corner_frames += 1
+    if missing_corners > 1:
+      logging.info('Unable to properly detect screen corners, making '
+                   'potentially false assumptions on frame %d',
+                   self._frame_generator.CurrentFrameNumber)
+    # Replace missing new_corners with either nearest intersection to previous
+    # corner, or previous corner if no intersections are found.
+    for i in xrange(0, 4):
+      if not np.isnan(new_corners[i][0]):
+        self._lost_corners[i] = False
+        continue
+      self._lost_corners[i] = True
+      min_dist = self.MAX_INTERFRAME_MOTION
+      min_corner = None
+
+      for isection in intersections:
+        dist = cv_util.SqDistance(isection[0], self._prev_corners[i])
+        if dist >= min_dist:
+          continue
+        if missing_corners == 1:
+          # We know in this case that we have 3 corners present, meaning
+          # all 4 screen lines, and therefore intersections near screen
+          # corners present, so our new corner must connect to these
+          # other corners.
+          if not self._PointConnectsToCorners(new_corners, isection, 3):
+            continue
+        min_corner = isection[0]
+        min_dist = dist
+      screen_corners[i] = min_corner if min_corner is not None else \
+          self._prev_corners[i]
+
+    return screen_corners
+
+  def _SmoothCorners(self, corners):
+    """Smoothes the motion of corners, reduces noise.
+
+    Smoothes the motion of corners by computing an exponentially weighted
+    moving average of corner positions over time.
+
+    Args:
+      corners: The corners of the detected screen.
+
+    Returns:
+      The final corner positions."""
+    if self._avg_corners is None:
+      self._avg_corners = np.asfarray(corners, np.float32)
+    for i in xrange(0, 4):
+      # Keep an exponential moving average of the corner location to reduce
+      # noise.
+      new_contrib = np.multiply(self.CORNER_AVERAGE_WEIGHT, corners[i])
+      old_contrib = np.multiply(1 - self.CORNER_AVERAGE_WEIGHT,
+                                self._avg_corners[i])
+      self._avg_corners[i] = np.add(new_contrib, old_contrib)
+
+    return self._avg_corners
+
+  def _GetTransform(self, corners, border):
+    """Gets the perspective transform of the screen.
+
+    Args:
+      corners: The corners of the detected screen.
+      border: The number of pixels of border to crop along with the screen.
+
+    Returns:
+      A perspective transform and the width and height of the target
+      transform.
+
+    Raises:
+      ScreenNotFoundError: Something went wrong in detecting the screen."""
+    if self._screen_size is None:
+      w = np.sqrt(cv_util.SqDistance(corners[1], corners[0]))
+      h = np.sqrt(cv_util.SqDistance(corners[1], corners[2]))
+      if w < 1 or h < 1:
+        raise self.ScreenNotFoundError(
+            'Screen detected improperly (bad corners)')
+      if min(w, h) < self.MIN_SCREEN_WIDTH:
+        raise self.ScreenNotFoundError('Detected screen was too small.')
+
+      self._screen_size = (w, h)
+      # Extend min line length, if we can, to reduce the number of extraneous
+      # lines the line finder finds.
+      self._min_line_length = max(self._min_line_length, min(w, h) / 1.75)
+    w = self._screen_size[0]
+    h = self._screen_size[1]
+
+    target = np.zeros((4, 2), np.float32)
+    width = w + border
+    height = h + border
+    target[0] = np.asfarray((width, border))
+    target[1] = np.asfarray((border, border))
+    target[2] = np.asfarray((border, height))
+    target[3] = np.asfarray((width, height))
+    transform_w = width + border
+    transform_h = height + border
+    transform = cv2.getPerspectiveTransform(corners, target)
+    return transform, transform_w, transform_h
+
+  def _Debug(self, lines, corners, final_corners, screen):
+    for line in lines:
+      intline = ((int(line[0]), int(line[1])),
+                 (int(line[2]), int(line[3])))
+      cv2.line(self._frame_debug, intline[0], intline[1], (0, 0, 255), 1)
+    i = 0
+    for corner in corners:
+      if not np.isnan(corner[0]):
+        cv2.putText(
+            self._frame_debug, str(i), (int(corner[0]), int(corner[1])),
+            cv2.FONT_HERSHEY_COMPLEX_SMALL, 1, (255, 255, 0), 1, cv2.CV_AA)
+        i += 1
+    if final_corners is not None:
+      for corner in final_corners:
+        cv2.circle(self._frame_debug,
+                   (int(corner[0]), int(corner[1])), 5, (255, 0, 255), 1)
+    cv2.imshow('original', self._frame)
+    cv2.imshow('debug', self._frame_debug)
+    if screen is not None:
+      cv2.imshow('screen', screen)
+    cv2.waitKey()
+
+# For being run as a script.
+# TODO(mthiesse): To be replaced with a better standalone script.
+# Ex: ./screen_finder.py path_to_video 0 5 --verbose
+
+
+def main():
+  start_frame = int(sys.argv[2]) if len(sys.argv) >= 3 else 0
+  vf = video_file_frame_generator.VideoFileFrameGenerator(sys.argv[1],
+                                                          start_frame)
+  if len(sys.argv) >= 4:
+    sf = ScreenFinder(vf, int(sys.argv[3]))
+  else:
+    sf = ScreenFinder(vf)
+  # TODO(mthiesse): Use argument parser to improve command line parsing.
+  if len(sys.argv) > 4 and sys.argv[4] == '--verbose':
+    logging.basicConfig(format='%(message)s', level=logging.INFO)
+  else:
+    logging.basicConfig(format='%(message)s', level=logging.WARN)
+  while sf.HasNext():
+    sf.GetNext()
+
+if __name__ == '__main__':
+  main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/image_processing/screen_finder_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/image_processing/screen_finder_unittest.py
new file mode 100644
index 0000000..313b496
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/image_processing/screen_finder_unittest.py
@@ -0,0 +1,368 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import copy
+import math
+import os
+import unittest
+
+from telemetry.core import util
+from telemetry.internal.util import external_modules
+
+try:
+  np = external_modules.ImportRequiredModule('numpy')
+  cv2 = external_modules.ImportRequiredModule('cv2')
+except (ImportError, NotImplementedError) as err:
+  pass
+else:
+  # pylint: disable=protected-access
+  class ScreenFinderTest(unittest.TestCase):
+    def __init__(self, *args, **kwargs):
+      super(ScreenFinderTest, self).__init__(*args, **kwargs)
+      # Import modules with dependencies that may not be preset in test setup so
+      # that importing this unit test doesn't cause the test runner to raise an
+      # exception.
+      from telemetry.internal.image_processing import fake_frame_generator
+      from telemetry.internal.image_processing import screen_finder
+      from telemetry.internal.image_processing import video_file_frame_generator
+      self.FakeFrameGenerator = fake_frame_generator.FakeFrameGenerator
+      self.VideoFileFrameGenerator = \
+          video_file_frame_generator.VideoFileFrameGenerator
+      self.ScreenFinder = screen_finder.ScreenFinder
+
+    def _GetScreenFinder(self, video_filename):
+      if not video_filename:
+        fg = self.FakeFrameGenerator()
+      else:
+        vid = os.path.join(util.GetUnittestDataDir(), video_filename)
+        fg = self.VideoFileFrameGenerator(vid)
+      return self.ScreenFinder(fg)
+
+    def testBasicFunctionality(self):
+      def CheckCorners(corners, expected):
+        for i in xrange(len(corners)):
+          for j in xrange(len(corners[i])):
+            self.assertAlmostEqual(corners[i][j], expected[i][j], delta=1.1)
+      expected = [[314, 60], [168, 58], [162, 274], [311, 276]]
+      sf = self._GetScreenFinder('screen_3_frames.mov')
+      self.assertTrue(sf.HasNext())
+      screen, corners = sf.GetNext()
+      CheckCorners(corners, expected)
+      self.assertIsNotNone(screen)
+      height, width = screen.shape[:2]
+      self.assertAlmostEqual(height, 226, delta=2)
+      self.assertAlmostEqual(width, 156, delta=2)
+      self.assertTrue(sf.HasNext())
+      screen, corners = sf.GetNext()
+      CheckCorners(corners, expected)
+      self.assertIsNotNone(screen)
+      height1, width1 = screen.shape[:2]
+      self.assertEqual(width, width1)
+      self.assertEqual(height, height1)
+      self.assertTrue(sf.HasNext())
+      screen, corners = sf.GetNext()
+      CheckCorners(corners, expected)
+      self.assertIsNotNone(screen)
+      height2, width2 = screen.shape[:2]
+      self.assertEqual(width, width2)
+      self.assertEqual(height, height2)
+      self.assertFalse(sf.HasNext())
+      error = ''
+      try:
+        sf.GetNext()
+      except RuntimeError as e:
+        error = str(e)
+      self.assertEqual(error, 'No more frames available.')
+
+    def testHasMovedFast(self):
+      sf = self._GetScreenFinder(None)
+      prev_corners = np.asfarray(([1000, 1000], [0, 1000], [0, 0], [1000, 0]))
+      self.assertFalse(sf._HasMovedFast(prev_corners, prev_corners))
+      not_moved = copy.deepcopy(prev_corners)
+      not_moved[0][1] += 1
+      not_moved[1][1] += 1
+      not_moved[3][0] += 0.9
+      self.assertFalse(sf._HasMovedFast(not_moved, prev_corners))
+      moved = copy.deepcopy(prev_corners)
+      moved[0][1] += math.sqrt(0.5)
+      moved[0][0] += math.sqrt(0.5)
+      moved[1][1] += 2.1
+      self.assertTrue(sf._HasMovedFast(moved, prev_corners))
+
+    def testPointConnectsToCorners(self):
+      sf = self._GetScreenFinder(None)
+      line1 = np.asfarray(((0, 0, 1, 0)))
+      line2 = np.asfarray(((0, 0, 0, 1)))
+      point = np.asfarray((0, 0))
+      point_info = (point, line1, line2)
+      corners = np.asfarray(((1, 0), (0, 1)))
+      self.assertFalse(sf._PointConnectsToCorners(corners, point_info, 1))
+      corners = np.append(corners, (100, 1))
+      corners = np.append(corners, (1, 100))
+      corners = corners.reshape(-1, 2)
+      self.assertTrue(sf._PointConnectsToCorners(corners, point_info, 2))
+      self.assertFalse(sf._PointConnectsToCorners(corners, point_info, 0.5))
+      corners = np.append(corners, (100, 0))
+      corners = np.append(corners, (0, 100))
+      corners = corners.reshape(-1, 2)
+      self.assertTrue(sf._PointConnectsToCorners(corners, point_info, 0))
+
+    def testFindIntersections(self):
+      def _BuildResult(point, line1, line2):
+        return [point, np.asfarray(line1).tolist(), np.asfarray(line2).tolist()]
+
+      def _IntersectionResultsToList(results):
+        result_list = []
+        for result in results:
+          point, line1, line2 = result
+          p = np.round(point).tolist()
+          l1 = np.round(line1).tolist()
+          l2 = np.round(line2).tolist()
+          result_list.append([p, l1, l2])
+        return result_list
+
+      sf = self._GetScreenFinder(None)
+      expected = []
+      lines = []
+      # Box with corners at (0, 0), (1000, 0), (0, 1000), (1000, 1000)
+      lines.append(np.asfarray(((0, 1001, 0, -1))))
+      lines.append(np.asfarray(((-1, 0, 1001, 0))))
+      lines.append(np.asfarray(((1000, 1001, 1000, -1))))
+      lines.append(np.asfarray(((-1, 1000, 1001, 1000))))
+      expected.append(_BuildResult([0, 0], lines[0], lines[1]))
+      expected.append(_BuildResult([0, 1000], lines[0], lines[3]))
+      expected.append(_BuildResult([1000, 0], lines[1], lines[2]))
+      expected.append(_BuildResult([1000, 1000], lines[2], lines[3]))
+
+      # crosses 2 lines at 45 degrees.
+      lines.append(np.asfarray(((0, 500, 500, 0))))
+      expected.append(_BuildResult([0, 500], lines[0], lines[4]))
+      expected.append(_BuildResult([500, 0], lines[1], lines[4]))
+
+      # crosses 1 line at > 45 degrees, 1 line at < 45 degrees.
+      lines.append(np.asfarray(((0, 400, 600, 0))))
+      expected.append(_BuildResult([0, 400], lines[0], lines[5]))
+
+      # Test without previous corner data, all intersections should be found.
+      results = sf._FindIntersections(lines)
+      result_list = _IntersectionResultsToList(results)
+
+      for e in expected:
+        self.assertIn(e, result_list)
+      self.assertEqual(len(expected), len(result_list))
+
+      # Now introduce previous corners, but also reset conditions. No
+      # intersections should be lost.
+      corners = ((1000, 1000), (0, 1000), (0, 0), (1000, 0))
+      sf._prev_corners = np.asfarray(corners, np.float32)
+      sf._lost_corner_frames = sf.RESET_AFTER_N_BAD_FRAMES + 1
+      results = sf._FindIntersections(lines)
+      result_list = _IntersectionResultsToList(results)
+
+      for e in expected:
+        self.assertIn(e, result_list)
+      self.assertEqual(len(expected), len(result_list))
+
+      # Remove reset conditions, so intersections not near corners will be lost.
+      sf._lost_corner_frames = sf.RESET_AFTER_N_BAD_FRAMES
+      # First 4 intersections are the ones at the old corner locations.
+      expected = expected[0:4]
+      results = sf._FindIntersections(lines)
+      result_list = _IntersectionResultsToList(results)
+
+      for e in expected:
+        self.assertIn(e, result_list)
+      self.assertEqual(len(expected), len(result_list))
+
+    def testPointIsCloseToPreviousCorners(self):
+      sf = self._GetScreenFinder(None)
+      corners = ((1000, 1000), (0, 1000), (0, 0), (1000, 0))
+      sf._prev_corners = np.asfarray(corners, np.float32)
+      dist = math.sqrt(sf.MAX_INTERFRAME_MOTION)
+      sidedist1 = math.sqrt(sf.MAX_INTERFRAME_MOTION) / math.sqrt(2) - (1e-13)
+      sidedist2 = math.sqrt(sf.MAX_INTERFRAME_MOTION) / math.sqrt(2) + (1e-13)
+      point1 = (corners[3][0] + dist, corners[3][1])
+      self.assertTrue(sf._PointIsCloseToPreviousCorners(point1))
+      point2 = (corners[3][0] + sidedist1, corners[3][1] + sidedist1)
+      self.assertTrue(sf._PointIsCloseToPreviousCorners(point2))
+      point3 = (corners[1][0] + sidedist2, corners[1][1] + sidedist2)
+      self.assertFalse(sf._PointIsCloseToPreviousCorners(point3))
+
+    def testLooksLikeCorner(self):
+      # TODO: Probably easier to just do end to end tests.
+      pass
+
+    def testCornerData(self):
+      cd = self.ScreenFinder.CornerData('a', 'b', 'c', 'd', 'e')
+      self.assertEqual(cd.corner_index, 'a')
+      self.assertEqual(cd.corner_location, 'b')
+      self.assertEqual(cd.brightness_score, 'c')
+      self.assertEqual(cd.line1, 'd')
+      self.assertEqual(cd.line2, 'e')
+      cd_list = []
+      cd_list.append(self.ScreenFinder.CornerData(0, None, None, None, None))
+      cd_list.append(self.ScreenFinder.CornerData(3, None, None, None, None))
+      cd_list.append(self.ScreenFinder.CornerData(1, None, None, None, None))
+      cd_list.append(self.ScreenFinder.CornerData(2, None, None, None, None))
+      cd_list.sort()
+      for i in range(len(cd_list)):
+        self.assertEqual(i, cd_list[i].corner_index)
+
+    def testFindCorners(self):
+      # TODO: Probably easier to just do end to end tests.
+      pass
+
+    def testDeDupCorners(self):
+      sf = self._GetScreenFinder(None)
+      data = []
+      lines = []
+      lines.append(np.asfarray((0, 1001, 0, -1)))
+      lines.append(np.asfarray((-1, 0, 1001, 0)))
+      lines.append(np.asfarray((1000, 1001, 1000, -1)))
+      lines.append(np.asfarray((-1, 1000, 1001, 1000)))
+      lines.append(np.asfarray((0, 10, 10, 0)))
+      lines.append(np.asfarray((-1, 1001, 1001, 1001)))
+      corners = np.asfarray(((1000, 1000), (0, 1000), (0, 0),
+                             (1000, 0), (0, 10), (10, 0), (1000, 1001)))
+      data.append(self.ScreenFinder.CornerData(2, corners[2], 100,
+                                               lines[0], lines[1]))
+      data.append(self.ScreenFinder.CornerData(1, corners[1], 100,
+                                               lines[0], lines[3]))
+      data.append(self.ScreenFinder.CornerData(3, corners[3], 100,
+                                               lines[1], lines[2]))
+      data.append(self.ScreenFinder.CornerData(0, corners[0], 100,
+                                               lines[2], lines[3]))
+      data.append(self.ScreenFinder.CornerData(2, corners[4], 120,
+                                               lines[0], lines[4]))
+      data.append(self.ScreenFinder.CornerData(2, corners[5], 110,
+                                               lines[1], lines[4]))
+      data.append(self.ScreenFinder.CornerData(0, corners[6], 110,
+                                               lines[2], lines[5]))
+      dedup = copy.copy(data)
+      # Tests 2 non-duplicate corners, 1 corner with connected and unconnected
+      # corners, and 1 corner with two connected corners.
+      sf._DeDupCorners(dedup, corners)
+      self.assertEqual(len(dedup), 4)
+      self.assertIn(data[0], dedup)
+      self.assertIn(data[1], dedup)
+      self.assertIn(data[2], dedup)
+      self.assertIn(data[6], dedup)
+
+      # Same test, but this time the corner with connected and unconnected
+      # corners now only contains unconnected corners.
+      del data[0]
+      corners = np.delete(corners, 2, axis=0)
+      dedup2 = copy.copy(data)
+      sf._DeDupCorners(dedup2, corners)
+      self.assertEqual(len(dedup2), 4)
+      self.assertIn(data[3], dedup2)
+      self.assertIn(data[0], dedup2)
+      self.assertIn(data[1], dedup2)
+      self.assertIn(data[5], dedup2)
+
+    def testFindExactCorners(self):
+      sf = self._GetScreenFinder(None)
+      img = np.zeros((3, 3), np.uint8)
+      img[1][0] = 255
+      img[0][1] = 255
+      img[1][2] = 255
+      img[2][1] = 255
+      sf._frame_edges = img
+      corners = np.asfarray([(1, 1), (1, 1), (1, 1), (1, 1)])
+      expected = np.asfarray([(2, 0), (0, 0), (0, 2), (2, 2)])
+      ret = sf._FindExactCorners(corners)
+      np.testing.assert_equal(ret, expected)
+      img2 = np.zeros((3, 3), np.uint8)
+      img2[1][0] = 255
+      img2[1][1] = 255
+      img2[2][2] = 255
+      img2[2][1] = 255
+      sf._frame_edges = img2
+      expected2 = [(2, 1), (0, 1), (0, 2), (2, 2)]
+      ret2 = sf._FindExactCorners(corners)
+      np.testing.assert_equal(ret2, expected2)
+
+    def testSmoothCorners(self):
+      sf = self._GetScreenFinder(None)
+      corners = [[10, 10], [10, 10], [10, 10], [10, 10]]
+      ret = sf._SmoothCorners(corners).tolist()
+      self.assertListEqual(ret, corners)
+      corners = [[0, 0], [0, 0], [0, 0], [0, 0]]
+      expected = [[5, 5], [5, 5], [5, 5], [5, 5]]
+      ret = sf._SmoothCorners(corners).tolist()
+      self.assertListEqual(ret, expected)
+      expected = [[2.5, 2.5], [2.5, 2.5], [2.5, 2.5], [2.5, 2.5]]
+      ret = sf._SmoothCorners(corners).tolist()
+      self.assertListEqual(ret, expected)
+
+    def testGetTransform(self):
+      sf = self._GetScreenFinder(None)
+      corners = np.array([[100, 1000], [0, 1000], [0, 0], [100, 0]], np.float32)
+      transform, w, h = sf._GetTransform(corners, 1)
+      transform = np.round(transform, 2)
+      expected = [[1., 0., 1.], [-0., -1., 1001.], [0., -0., 1.]]
+      self.assertListEqual(transform.tolist(), expected)
+      self.assertEqual(w, 102)
+      self.assertEqual(h, 1002)
+
+      corners = np.array([(200, 2000), (0, 2000), (0, 0), (200, 0)], np.float32)
+      transform, w, h = sf._GetTransform(corners, 5)
+      transform = np.round(transform, 2)
+      expected = [[0.5, 0.0, 5.0], [-0.0, -0.5, 1005.0], [-0.0, 0.0, 1.0]]
+      self.assertListEqual(transform.tolist(), expected)
+      self.assertEqual(w, 110)
+      self.assertEqual(h, 1010)
+
+    def testNewScreenLocation(self):
+      sf = self._GetScreenFinder(None)
+      corners_2 = np.asfarray([[np.nan, np.nan], [0, 1000], [np.nan, np.nan],
+                               [1000, 0]])
+      corners_3 = np.asfarray([[1000, 1000], [0, 1000], [np.nan, np.nan],
+                               [1000, 0]])
+      corners_4 = np.asfarray([[1000, 1000], [0, 1000], [0, 0], [1000, 0]])
+      lines = []
+      # Box with corners at (0, 0), (1000, 0), (0, 1000), (1000, 1000)
+      lines.append(np.asfarray(((0, 1001, 0, -1))))
+      lines.append(np.asfarray(((-1, 0, 1001, 0))))
+      lines.append(np.asfarray(((1000, 1001, 1000, -1))))
+      lines.append(np.asfarray(((-1, 1000, 1001, 1000))))
+      # Additional intersections near a corner.
+      lines.append(np.asfarray(((0, 3, 7, 0))))
+      lines.append(np.asfarray(((0, 4, 6, 0))))
+      intersections = sf._FindIntersections(lines)
+      failed = False
+      try:
+        sf._NewScreenLocation(corners_3, 1, intersections)
+      except self.ScreenFinder.ScreenNotFoundError:
+        failed = True
+      self.assertTrue(failed)
+
+      sf._lost_corner_frames = 10
+      sf._lost_corners = [True, True, True, True]
+      ret = sf._NewScreenLocation(corners_4, 0, intersections)
+      np.testing.assert_equal(ret, corners_4)
+      self.assertListEqual(sf._lost_corners, [False, False, False, False])
+      self.assertEqual(sf._lost_corner_frames, 0)
+
+      sf._prev_corners = corners_4
+      ret = sf._NewScreenLocation(corners_3, 1, intersections)
+      ret = np.round(ret)
+      np.testing.assert_equal(ret, corners_4)
+      self.assertListEqual(sf._lost_corners, [False, False, True, False])
+      self.assertEqual(sf._lost_corner_frames, 1)
+
+      sf._prev_corners = np.asfarray([(1000, 1000), (0, 1000),
+                                      (0, 3), (1000, 0)])
+      ret = sf._NewScreenLocation(corners_3, 1, intersections)
+      ret = np.round(ret)
+      np.testing.assert_equal(ret, corners_4)
+      self.assertListEqual(sf._lost_corners, [False, False, True, False])
+      self.assertEqual(sf._lost_corner_frames, 2)
+
+      ret = sf._NewScreenLocation(corners_2, 2, intersections)
+      ret = np.round(ret)
+      expected = [[1000, 1000], [0, 1000], [0, 3], [1000, 0]]
+      np.testing.assert_equal(ret, expected)
+      self.assertListEqual(sf._lost_corners, [True, False, True, False])
+      self.assertEqual(sf._lost_corner_frames, 3)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/image_processing/video.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/image_processing/video.py
new file mode 100644
index 0000000..06fc9ca
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/image_processing/video.py
@@ -0,0 +1,172 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import subprocess
+
+from py_utils import cloud_storage  # pylint: disable=import-error
+
+from telemetry.core import platform
+from telemetry.util import image_util
+from telemetry.util import rgba_color
+
+HIGHLIGHT_ORANGE_FRAME = rgba_color.WEB_PAGE_TEST_ORANGE
+
+class BoundingBoxNotFoundException(Exception):
+  pass
+
+
+class Video(object):
+  """Utilities for storing and interacting with the video capture."""
+
+  def __init__(self, video_file_obj):
+    assert video_file_obj.delete
+    assert not video_file_obj.close_called
+    self._video_file_obj = video_file_obj
+    self._tab_contents_bounding_box = None
+
+  def UploadToCloudStorage(self, bucket, target_path):
+    """Uploads video file to cloud storage.
+
+    Args:
+      target_path: Path indicating where to store the file in cloud storage.
+    """
+    cloud_storage.Insert(bucket, target_path, self._video_file_obj.name)
+
+  def GetVideoFrameIter(self):
+    """Returns the iteration for processing the video capture.
+
+    This looks for the initial color flash in the first frame to establish the
+    tab content boundaries and then omits all frames displaying the flash.
+
+    Yields:
+      (time_ms, image) tuples representing each video keyframe. Only the first
+      frame is a run of sequential duplicate bitmaps is typically included.
+        time_ms is milliseconds since navigationStart.
+        image may be a telemetry.core.Bitmap, or a numpy array depending on
+        whether numpy is installed.
+    """
+    frame_generator = self._FramesFromMp4(self._video_file_obj.name)
+
+    # Flip through frames until we find the initial tab contents flash.
+    content_box = None
+    for _, bmp in frame_generator:
+      content_box = self._FindHighlightBoundingBox(
+          bmp, HIGHLIGHT_ORANGE_FRAME)
+      if content_box:
+        break
+
+    if not content_box:
+      raise BoundingBoxNotFoundException(
+          'Failed to identify tab contents in video capture.')
+
+    # Flip through frames until the flash goes away and emit that as frame 0.
+    timestamp = 0
+    for timestamp, bmp in frame_generator:
+      if not self._FindHighlightBoundingBox(bmp, HIGHLIGHT_ORANGE_FRAME):
+        yield 0, image_util.Crop(bmp, *content_box)
+        break
+
+    start_time = timestamp
+    for timestamp, bmp in frame_generator:
+      yield timestamp - start_time, image_util.Crop(bmp, *content_box)
+
+  def _FindHighlightBoundingBox(self, bmp, color, bounds_tolerance=8,
+                                color_tolerance=8):
+    """Returns the bounding box of the content highlight of the given color.
+
+    Raises:
+      BoundingBoxNotFoundException if the hightlight could not be found.
+    """
+    content_box, pixel_count = image_util.GetBoundingBox(bmp, color,
+        tolerance=color_tolerance)
+
+    if not content_box:
+      return None
+
+    # We assume arbitrarily that tabs are all larger than 200x200. If this
+    # fails it either means that assumption has changed or something is
+    # awry with our bounding box calculation.
+    if content_box[2] < 200 or content_box[3] < 200:
+      raise BoundingBoxNotFoundException('Unexpectedly small tab contents.')
+
+    # TODO(tonyg): Can this threshold be increased?
+    if pixel_count < 0.9 * content_box[2] * content_box[3]:
+      raise BoundingBoxNotFoundException(
+          'Low count of pixels in tab contents matching expected color.')
+
+    # Since we allow some fuzziness in bounding box finding, we want to make
+    # sure that the bounds are always stable across a run. So we cache the
+    # first box, whatever it may be.
+    #
+    # This relies on the assumption that since Telemetry doesn't know how to
+    # resize the window, we should always get the same content box for a tab.
+    # If this assumption changes, this caching needs to be reworked.
+    if not self._tab_contents_bounding_box:
+      self._tab_contents_bounding_box = content_box
+
+    # Verify that there is only minor variation in the bounding box. If it's
+    # just a few pixels, we can assume it's due to compression artifacts.
+    for x, y in zip(self._tab_contents_bounding_box, content_box):
+      if abs(x - y) > bounds_tolerance:
+        # If this fails, it means either that either the above assumption has
+        # changed or something is awry with our bounding box calculation.
+        raise BoundingBoxNotFoundException(
+            'Unexpected change in tab contents box.')
+
+    return self._tab_contents_bounding_box
+
+  def _FramesFromMp4(self, mp4_file):
+    host_platform = platform.GetHostPlatform()
+    if not host_platform.CanLaunchApplication('avconv'):
+      host_platform.InstallApplication('avconv')
+
+    def GetDimensions(video):
+      proc = subprocess.Popen(['avconv', '-i', video], stderr=subprocess.PIPE)
+      dimensions = None
+      output = ''
+      for line in proc.stderr.readlines():
+        output += line
+        if 'Video:' in line:
+          dimensions = line.split(',')[2]
+          dimensions = map(int, dimensions.split()[0].split('x'))
+          break
+      proc.communicate()
+      assert dimensions, ('Failed to determine video dimensions. output=%s' %
+                          output)
+      return dimensions
+
+    def GetFrameTimestampMs(stderr):
+      """Returns the frame timestamp in integer milliseconds from the dump log.
+
+      The expected line format is:
+      '  dts=1.715  pts=1.715\n'
+
+      We have to be careful to only read a single timestamp per call to avoid
+      deadlock because avconv interleaves its writes to stdout and stderr.
+      """
+      while True:
+        line = ''
+        next_char = ''
+        while next_char != '\n':
+          next_char = stderr.read(1)
+          line += next_char
+        if 'pts=' in line:
+          return int(1000 * float(line.split('=')[-1]))
+
+    dimensions = GetDimensions(mp4_file)
+    frame_length = dimensions[0] * dimensions[1] * 3
+    frame_data = bytearray(frame_length)
+
+    # Use rawvideo so that we don't need any external library to parse frames.
+    proc = subprocess.Popen(['avconv', '-i', mp4_file, '-vcodec',
+                             'rawvideo', '-pix_fmt', 'rgb24', '-dump',
+                             '-loglevel', 'debug', '-f', 'rawvideo', '-'],
+                            stderr=subprocess.PIPE, stdout=subprocess.PIPE)
+    while True:
+      num_read = proc.stdout.readinto(frame_data)
+      if not num_read:
+        raise StopIteration
+      assert num_read == len(frame_data), 'Unexpected frame size: %d' % num_read
+      yield (GetFrameTimestampMs(proc.stderr),
+             image_util.FromRGBPixels(dimensions[0], dimensions[1], frame_data))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/image_processing/video_file_frame_generator.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/image_processing/video_file_frame_generator.py
new file mode 100644
index 0000000..dc209b4
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/image_processing/video_file_frame_generator.py
@@ -0,0 +1,95 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.internal.image_processing import frame_generator
+from telemetry.internal.util import external_modules
+
+cv2 = external_modules.ImportRequiredModule('cv2')
+
+
+class VideoFileFrameGenerator(frame_generator.FrameGenerator):
+  """Provides a Frame Generator for a video file.
+
+  Sample Usage:
+    generator = VideoFileFrameGenerator(sys.argv[1]).GetGenerator()
+    for frame in generator:
+      # Do something
+
+  Attributes:
+    _capture: The openCV video capture.
+    _frame_count: The number of frames in the video capture.
+    _frame_index: The frame number of the current frame.
+    _timestamp: The timestamp of the current frame.
+    _dimensions: The dimensions of the video capture."""
+  def __init__(self, video_filename, start_frame_index=0):
+    """Initializes the VideoFileFrameGenerator object.
+
+    Args:
+      video_filename: str, The path to the video file.
+      start_frame_index: int, The number of frames to skip at the start of the
+          file.
+
+    Raises:
+      FrameReadError: A read error occurred during initialization."""
+    self._capture = cv2.VideoCapture(video_filename)
+    self._frame_count = int(self._capture.get(cv2.cv.CV_CAP_PROP_FRAME_COUNT))
+    self._frame_index = -1
+    self._timestamp = 0
+    width = self._capture.get(cv2.cv.CV_CAP_PROP_FRAME_WIDTH)
+    height = self._capture.get(cv2.cv.CV_CAP_PROP_FRAME_HEIGHT)
+    self._dimensions = (int(width), int(height))
+    if self._frame_count <= start_frame_index:
+      raise frame_generator.FrameReadError('Not enough frames in capture.')
+    while self._frame_index < start_frame_index - 1:
+      self._ReadFrame(True)
+
+    super(self.__class__, self).__init__()
+
+  def _ReadFrame(self, skip_decode=False):
+    """Reads the next frame, updates attributes.
+
+    Args:
+      skip_decode: Whether or not to skip decoding. Useful for seeking.
+
+    Returns:
+      The frame if not EOF, 'None' if EOF.
+
+    Raises:
+      FrameReadError: Unexpectedly failed to read a frame from the capture."""
+    if self._frame_index >= self._frame_count - 1:
+      return None
+    self._timestamp = self._capture.get(cv2.cv.CV_CAP_PROP_POS_MSEC)
+    if skip_decode:
+      ret = self._capture.grab()
+      frame = None
+    else:
+      ret, frame = self._capture.read()
+    if not ret:
+      raise frame_generator.FrameReadError(
+          'Failed to read frame from capture.')
+    self._frame_index += 1
+    return frame
+
+  # OVERRIDE
+  def _CreateGenerator(self):
+    while True:
+      frame = self._ReadFrame()
+      if frame is None:
+        break
+      yield frame
+
+  # OVERRIDE
+  @property
+  def CurrentTimestamp(self):
+    return self._timestamp
+
+  # OVERRIDE
+  @property
+  def CurrentFrameNumber(self):
+    return self._frame_index
+
+  # OVERRIDE
+  @property
+  def Dimensions(self):
+    return self._dimensions
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/image_processing/video_file_frame_generator_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/image_processing/video_file_frame_generator_unittest.py
new file mode 100644
index 0000000..72632a2
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/image_processing/video_file_frame_generator_unittest.py
@@ -0,0 +1,86 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import unittest
+
+from telemetry.core import util
+from telemetry.internal.image_processing import frame_generator
+from telemetry.internal.util import external_modules
+
+try:
+  cv2 = external_modules.ImportRequiredModule('cv2')
+except (ImportError, NotImplementedError):
+  pass
+else:
+  class VideoFileFrameGeneratorTest(unittest.TestCase):
+    def __init__(self, *args, **kwargs):
+      super(VideoFileFrameGeneratorTest, self).__init__(*args, **kwargs)
+      # Import modules with dependencies that may not be preset in test setup so
+      # that importing this unit test doesn't cause the test runner to raise an
+      # exception.
+      from telemetry.internal.image_processing import video_file_frame_generator
+      self.VideoFileFrameGenerator = \
+          video_file_frame_generator.VideoFileFrameGenerator
+
+    def testVideoFileFrameGeneratorSuccess(self):
+      vid = os.path.join(util.GetUnittestDataDir(), 'screen_3_frames.mov')
+      fg = self.VideoFileFrameGenerator(vid)
+      timestamps = [0, 33.367, 66.733]
+      self.assertTrue(isinstance(fg, frame_generator.FrameGenerator))
+
+      self.assertEqual(fg.CurrentFrameNumber, -1)
+      self.assertAlmostEqual(fg.CurrentTimestamp, 0, 3)
+      self.assertEqual(fg.Dimensions, (432, 320))
+      generator = fg.Generator
+      i = 0
+      for frame in generator:
+        self.assertEqual(fg.CurrentFrameNumber, i)
+        self.assertAlmostEqual(fg.CurrentTimestamp, timestamps[i], 3)
+        self.assertEqual(fg.Dimensions, (432, 320))
+        self.assertEqual(frame.shape[:2], (320, 432))
+        i += 1
+      self.assertEqual(i, 3)
+      try:
+        next(generator)
+        stopped = False
+      except StopIteration:
+        stopped = True
+      self.assertTrue(stopped)
+      try:
+        next(fg.Generator)
+        stopped = False
+      except StopIteration:
+        stopped = True
+      self.assertTrue(stopped)
+
+    def testVideoFileFrameGeneratorSkipFrames(self):
+      vid = os.path.join(util.GetUnittestDataDir(), 'screen_3_frames.mov')
+      fg = self.VideoFileFrameGenerator(vid, 2)
+      self.assertEqual(fg.CurrentFrameNumber, 1)
+      self.assertAlmostEqual(fg.CurrentTimestamp, 33.367, 3)
+      self.assertEqual(fg.Dimensions, (432, 320))
+      next(fg.Generator)
+      try:
+        next(fg.Generator)
+        stopped = False
+      except StopIteration:
+        stopped = True
+      self.assertTrue(stopped)
+
+    def testVideoFileFrameGeneratorFailure(self):
+      vid = os.path.join(util.GetUnittestDataDir(), 'screen_3_frames.mov')
+      try:
+        self.VideoFileFrameGenerator(vid, 4)
+        fail = False
+      except frame_generator.FrameReadError:
+        fail = True
+      self.assertTrue(fail)
+
+      try:
+        self.VideoFileFrameGenerator('not_a_file', 0)
+        fail = False
+      except frame_generator.FrameReadError:
+        fail = True
+      self.assertTrue(fail)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/image_processing/video_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/image_processing/video_unittest.py
new file mode 100644
index 0000000..0f83ff5
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/image_processing/video_unittest.py
@@ -0,0 +1,50 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+import os
+import unittest
+
+from telemetry.core import platform
+from telemetry.core import util
+from telemetry import decorators
+from telemetry.internal.image_processing import video
+from telemetry.util import image_util
+
+
+class VideoTest(unittest.TestCase):
+
+  @decorators.Disabled('all')
+  def testFramesFromMp4(self):
+    host_platform = platform.GetHostPlatform()
+
+    try:
+      host_platform.InstallApplication('avconv')
+    finally:
+      if not host_platform.CanLaunchApplication('avconv'):
+        logging.warning('Test not supported on this platform')
+        return  # pylint: disable=lost-exception
+
+    vid = os.path.join(util.GetUnittestDataDir(), 'vid.mp4')
+    expected_timestamps = [
+      0,
+      763,
+      783,
+      940,
+      1715,
+      1732,
+      1842,
+      1926,
+      ]
+
+    video_obj = video.Video(vid)
+
+    # Calling _FramesFromMp4 should return all frames.
+    # pylint: disable=protected-access
+    for i, timestamp_bitmap in enumerate(video_obj._FramesFromMp4(vid)):
+      timestamp, bmp = timestamp_bitmap
+      self.assertEquals(timestamp, expected_timestamps[i])
+      expected_bitmap = image_util.FromPngFile(os.path.join(
+          util.GetUnittestDataDir(), 'frame%d.png' % i))
+      self.assertTrue(image_util.AreEqual(expected_bitmap, bmp))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/__init__.py
new file mode 100644
index 0000000..50b23df
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/__init__.py
@@ -0,0 +1,3 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/android_device.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/android_device.py
new file mode 100644
index 0000000..3afdbd2
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/android_device.py
@@ -0,0 +1,187 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+import os
+
+from telemetry.internal.platform import cros_device
+from telemetry.internal.platform import device
+from telemetry.internal.platform.profiler import monsoon
+
+from devil.android import device_blacklist
+from devil.android import device_errors
+from devil.android import device_utils
+from devil.android.sdk import adb_wrapper
+
+import py_utils
+
+class AndroidDevice(device.Device):
+  """ Class represents information for connecting to an android device.
+
+  Attributes:
+    device_id: the device's serial string created by adb to uniquely
+      identify an emulator/device instance. This string can be found by running
+      'adb devices' command
+    enable_performance_mode: when this is set to True, android platform will be
+    set to high performance mode after browser is started.
+  """
+  def __init__(self, device_id, enable_performance_mode=True):
+    super(AndroidDevice, self).__init__(
+        name='Android device %s' % device_id, guid=device_id)
+    self._device_id = device_id
+    self._enable_performance_mode = enable_performance_mode
+
+  @classmethod
+  def GetAllConnectedDevices(cls, blacklist):
+    device_serials = GetDeviceSerials(blacklist)
+    return [cls(s) for s in device_serials]
+
+  @property
+  def device_id(self):
+    return self._device_id
+
+  @property
+  def enable_performance_mode(self):
+    return self._enable_performance_mode
+
+
+def _ListSerialsOfHealthyOnlineDevices(blacklist):
+  return [d.adb.GetDeviceSerial()
+          for d in device_utils.DeviceUtils.HealthyDevices(blacklist)]
+
+
+def GetDeviceSerials(blacklist):
+  """Return the list of device serials of healthy devices.
+
+  If a preferred device has been set with ANDROID_SERIAL, it will be first in
+  the returned list. The arguments specify what devices to include in the list.
+  """
+
+  device_serials = _ListSerialsOfHealthyOnlineDevices(blacklist)
+
+  # The monsoon provides power for the device, so for devices with no
+  # real battery, we need to turn them on after the monsoon enables voltage
+  # output to the device.
+  if not device_serials:
+    try:
+      m = monsoon.Monsoon(wait=False)
+      m.SetUsbPassthrough(1)
+      m.SetVoltage(3.8)
+      m.SetMaxCurrent(8)
+      logging.warn("""
+Monsoon power monitor detected, but no Android devices.
+
+The Monsoon's power output has been enabled. Please now ensure that:
+
+  1. The Monsoon's front and back USB are connected to the host.
+  2. The device is connected to the Monsoon's main and USB channels.
+  3. The device is turned on.
+
+Waiting for device...
+""")
+      py_utils.WaitFor(_ListSerialsOfHealthyOnlineDevices(blacklist), 600)
+      device_serials = _ListSerialsOfHealthyOnlineDevices(blacklist)
+    except IOError:
+      return []
+
+  preferred_device = os.environ.get('ANDROID_SERIAL')
+  if preferred_device in device_serials:
+    logging.warn(
+        'ANDROID_SERIAL is defined. Put %s in the first of the'
+        'discovered devices list.' % preferred_device)
+    device_serials.remove(preferred_device)
+    device_serials.insert(0, preferred_device)
+  return device_serials
+
+
+def GetDevice(finder_options):
+  """Return a Platform instance for the device specified by |finder_options|."""
+  android_platform_options = finder_options.remote_platform_options
+  if not CanDiscoverDevices():
+    logging.info(
+        'No adb command found. Will not try searching for Android browsers.')
+    return None
+
+  if android_platform_options.android_blacklist_file:
+    blacklist = device_blacklist.Blacklist(
+        android_platform_options.android_blacklist_file)
+  else:
+    blacklist = None
+
+  if (android_platform_options.device
+      and android_platform_options.device in GetDeviceSerials(blacklist)):
+    return AndroidDevice(
+        android_platform_options.device,
+        enable_performance_mode=not finder_options.no_performance_mode)
+
+  devices = AndroidDevice.GetAllConnectedDevices(blacklist)
+  if len(devices) == 0:
+    logging.warn('No android devices found.')
+    return None
+  if len(devices) > 1:
+    logging.warn(
+        'Multiple devices attached. Please specify one of the following:\n' +
+        '\n'.join(['  --device=%s' % d.device_id for d in devices]))
+    return None
+  return devices[0]
+
+
+def _HasValidAdb():
+  """Returns true if adb is present.
+
+  Note that this currently will return True even if the adb that's present
+  cannot run on this system.
+  """
+  if os.name != 'posix' or cros_device.IsRunningOnCrOS():
+    return False
+
+  try:
+    adb_path = adb_wrapper.AdbWrapper.GetAdbPath()
+  except device_errors.NoAdbError:
+    return False
+
+  if os.path.isabs(adb_path) and not os.path.exists(adb_path):
+    return False
+
+  return True
+
+
+def CanDiscoverDevices():
+  """Returns true if devices are discoverable via adb."""
+  if not _HasValidAdb():
+    return False
+
+  try:
+    device_utils.DeviceUtils.HealthyDevices(None)
+    return True
+  except (device_errors.CommandFailedError, device_errors.CommandTimeoutError,
+          device_errors.NoAdbError, OSError):
+    return False
+
+
+def FindAllAvailableDevices(options):
+  """Returns a list of available devices.
+  """
+  # Disable Android device discovery when remote testing a CrOS device
+  if options.cros_remote:
+    return []
+
+  android_platform_options = options.remote_platform_options
+  devices = []
+  try:
+    if CanDiscoverDevices():
+      blacklist = None
+      if android_platform_options.android_blacklist_file:
+        blacklist = device_blacklist.Blacklist(
+            android_platform_options.android_blacklist_file)
+      devices = AndroidDevice.GetAllConnectedDevices(blacklist)
+  finally:
+    if not devices and _HasValidAdb():
+      try:
+        adb_wrapper.AdbWrapper.KillServer()
+      except device_errors.NoAdbError as e:
+        logging.warning(
+            'adb reported as present, but NoAdbError thrown: %s', str(e))
+
+  return devices
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/android_device_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/android_device_unittest.py
new file mode 100644
index 0000000..ac09935
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/android_device_unittest.py
@@ -0,0 +1,152 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from telemetry import decorators
+from telemetry.internal.browser import browser_options
+from telemetry.internal.platform import android_device
+from telemetry.internal.platform import remote_platform_options
+from telemetry.testing import system_stub
+import mock
+
+from devil.android import device_utils
+from devil.android import device_blacklist
+
+
+class _BaseAndroidDeviceTest(unittest.TestCase):
+  def setUp(self):
+    def check_blacklist_arg(blacklist):
+      self.assertTrue(blacklist is None
+                      or isinstance(blacklist, device_blacklist.Blacklist))
+      return mock.DEFAULT
+
+    self._healthy_device_patcher = mock.patch(
+        'devil.android.device_utils.DeviceUtils.HealthyDevices')
+    self._healthy_device_mock = self._healthy_device_patcher.start()
+    self._healthy_device_mock.side_effect = check_blacklist_arg
+    self._android_device_stub = system_stub.Override(
+        android_device, ['subprocess', 'logging'])
+
+  def _GetMockDeviceUtils(self, device_serial):
+    device = device_utils.DeviceUtils(device_serial)
+    return device
+
+  def tearDown(self):
+    self._healthy_device_patcher.stop()
+    self._android_device_stub.Restore()
+
+
+class AndroidDeviceTest(_BaseAndroidDeviceTest):
+  @decorators.Enabled('android')
+  def testGetAllAttachedAndroidDevices(self):
+    self._healthy_device_mock.return_value = [
+        self._GetMockDeviceUtils('01'),
+        self._GetMockDeviceUtils('02')]
+    self.assertEquals(
+        set(['01', '02']),
+        set(device.device_id for device in
+            android_device.AndroidDevice.GetAllConnectedDevices(None)))
+
+  @decorators.Enabled('android')
+  def testNoAdbReturnsNone(self):
+    finder_options = browser_options.BrowserFinderOptions()
+    with (
+        mock.patch('os.path.isabs', return_value=True)), (
+        mock.patch('os.path.exists', return_value=False)):
+      self.assertEquals([], self._android_device_stub.logging.warnings)
+      self.assertIsNone(android_device.GetDevice(finder_options))
+
+  # https://github.com/catapult-project/catapult/issues/3099 (Android)
+  @decorators.Disabled('all')
+  def testAdbNoDevicesReturnsNone(self):
+    finder_options = browser_options.BrowserFinderOptions()
+    with mock.patch('os.path.isabs', return_value=False):
+      self._healthy_device_mock.return_value = []
+      self.assertEquals([], self._android_device_stub.logging.warnings)
+      self.assertIsNone(android_device.GetDevice(finder_options))
+
+  # https://github.com/catapult-project/catapult/issues/3099 (Android)
+  @decorators.Disabled('all')
+  def testAdbTwoDevicesReturnsNone(self):
+    finder_options = browser_options.BrowserFinderOptions()
+    with mock.patch('os.path.isabs', return_value=False):
+      self._healthy_device_mock.return_value = [
+          self._GetMockDeviceUtils('015d14fec128220c'),
+          self._GetMockDeviceUtils('015d14fec128220d')]
+      device = android_device.GetDevice(finder_options)
+      self.assertEquals([
+          'Multiple devices attached. Please specify one of the following:\n'
+          '  --device=015d14fec128220c\n'
+          '  --device=015d14fec128220d'],
+          self._android_device_stub.logging.warnings)
+      self.assertIsNone(device)
+
+  @decorators.Enabled('android')
+  def testAdbPickOneDeviceReturnsDeviceInstance(self):
+    finder_options = browser_options.BrowserFinderOptions()
+    platform_options = remote_platform_options.AndroidPlatformOptions(
+        device='555d14fecddddddd')  # pick one
+    finder_options.remote_platform_options = platform_options
+    with mock.patch('os.path.isabs', return_value=False):
+      self._healthy_device_mock.return_value = [
+          self._GetMockDeviceUtils('015d14fec128220c'),
+          self._GetMockDeviceUtils('555d14fecddddddd')]
+      device = android_device.GetDevice(finder_options)
+      self.assertEquals([], self._android_device_stub.logging.warnings)
+      self.assertEquals('555d14fecddddddd', device.device_id)
+
+  # https://github.com/catapult-project/catapult/issues/3099 (Android)
+  @decorators.Disabled('all')
+  def testAdbOneDeviceReturnsDeviceInstance(self):
+    finder_options = browser_options.BrowserFinderOptions()
+    with mock.patch('os.path.isabs', return_value=False):
+      self._healthy_device_mock.return_value = [
+          self._GetMockDeviceUtils('015d14fec128220c')]
+      device = android_device.GetDevice(finder_options)
+      self.assertEquals([], self._android_device_stub.logging.warnings)
+      self.assertEquals('015d14fec128220c', device.device_id)
+
+
+class FindAllAvailableDevicesTest(_BaseAndroidDeviceTest):
+  # https://github.com/catapult-project/catapult/issues/3099 (Android)
+  @decorators.Disabled('all')
+  def testAdbNoDeviceReturnsEmptyList(self):
+    finder_options = browser_options.BrowserFinderOptions()
+    with mock.patch('os.path.isabs', return_value=False):
+      self._healthy_device_mock.return_value = []
+      devices = android_device.FindAllAvailableDevices(finder_options)
+      self.assertEquals([], self._android_device_stub.logging.warnings)
+      self.assertIsNotNone(devices)
+      self.assertEquals(len(devices), 0)
+
+  # https://github.com/catapult-project/catapult/issues/3099 (Android)
+  @decorators.Disabled('all')
+  def testAdbOneDeviceReturnsListWithOneDeviceInstance(self):
+    finder_options = browser_options.BrowserFinderOptions()
+    with mock.patch('os.path.isabs', return_value=False):
+      self._healthy_device_mock.return_value = [
+          self._GetMockDeviceUtils('015d14fec128220c')]
+      devices = android_device.FindAllAvailableDevices(finder_options)
+      self.assertEquals([], self._android_device_stub.logging.warnings)
+      self.assertIsNotNone(devices)
+      self.assertEquals(len(devices), 1)
+      self.assertEquals('015d14fec128220c', devices[0].device_id)
+
+  # https://github.com/catapult-project/catapult/issues/3099 (Android)
+  @decorators.Disabled('all')
+  def testAdbMultipleDevicesReturnsListWithAllDeviceInstances(self):
+    finder_options = browser_options.BrowserFinderOptions()
+    with mock.patch('os.path.isabs', return_value=False):
+      self._healthy_device_mock.return_value = [
+          self._GetMockDeviceUtils('015d14fec128220c'),
+          self._GetMockDeviceUtils('015d14fec128220d'),
+          self._GetMockDeviceUtils('015d14fec128220e')]
+      devices = android_device.FindAllAvailableDevices(finder_options)
+      self.assertEquals([], self._android_device_stub.logging.warnings)
+      self.assertIsNotNone(devices)
+      self.assertEquals(len(devices), 3)
+      self.assertEquals(devices[0].guid, '015d14fec128220c')
+      self.assertEquals(devices[1].guid, '015d14fec128220d')
+      self.assertEquals(devices[2].guid, '015d14fec128220e')
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/android_platform_backend.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/android_platform_backend.py
new file mode 100644
index 0000000..37cded3
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/android_platform_backend.py
@@ -0,0 +1,834 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+import os
+import posixpath
+import re
+import subprocess
+import tempfile
+
+from battor import battor_wrapper
+from telemetry.core import android_platform
+from telemetry.core import exceptions
+from telemetry.core import util
+from telemetry import decorators
+from telemetry.internal import forwarders
+from telemetry.internal.forwarders import android_forwarder
+from telemetry.internal.image_processing import video
+from telemetry.internal.platform import android_device
+from telemetry.internal.platform import linux_based_platform_backend
+from telemetry.internal.platform.power_monitor import android_dumpsys_power_monitor
+from telemetry.internal.platform.power_monitor import android_fuelgauge_power_monitor
+from telemetry.internal.platform.power_monitor import android_temperature_monitor
+from telemetry.internal.platform.power_monitor import monsoon_power_monitor
+from telemetry.internal.platform.power_monitor import (
+  android_power_monitor_controller)
+from telemetry.internal.platform.power_monitor import sysfs_power_monitor
+from telemetry.internal.platform.profiler import android_prebuilt_profiler_helper
+from telemetry.internal.util import binary_manager
+from telemetry.internal.util import external_modules
+
+psutil = external_modules.ImportOptionalModule('psutil')
+import adb_install_cert
+
+from devil.android import app_ui
+from devil.android import battery_utils
+from devil.android import device_errors
+from devil.android import device_utils
+from devil.android.perf import cache_control
+from devil.android.perf import perf_control
+from devil.android.perf import thermal_throttle
+from devil.android.sdk import version_codes
+from devil.android.tools import video_recorder
+
+try:
+  # devil.android.forwarder uses fcntl, which doesn't exist on Windows.
+  from devil.android import forwarder
+except ImportError:
+  forwarder = None
+
+try:
+  from devil.android.perf import surface_stats_collector
+except Exception:
+  surface_stats_collector = None
+
+
+_ARCH_TO_STACK_TOOL_ARCH = {
+  'armeabi-v7a': 'arm',
+  'arm64-v8a': 'arm64',
+}
+_DEVICE_COPY_SCRIPT_FILE = os.path.abspath(os.path.join(
+    os.path.dirname(__file__), 'efficient_android_directory_copy.sh'))
+_DEVICE_COPY_SCRIPT_LOCATION = (
+    '/data/local/tmp/efficient_android_directory_copy.sh')
+
+# TODO(nednguyen): Remove this method and update the client config to point to
+# the correct binary instead.
+def _FindLocallyBuiltPath(binary_name):
+  """Finds the most recently built |binary_name|."""
+  command = None
+  command_mtime = 0
+  required_mode = os.X_OK
+  if binary_name.endswith('.apk'):
+    required_mode = os.R_OK
+  for build_path in util.GetBuildDirectories():
+    candidate = os.path.join(build_path, binary_name)
+    if os.path.isfile(candidate) and os.access(candidate, required_mode):
+      candidate_mtime = os.stat(candidate).st_mtime
+      if candidate_mtime > command_mtime:
+        command = candidate
+        command_mtime = candidate_mtime
+  return command
+
+
+class AndroidPlatformBackend(
+    linux_based_platform_backend.LinuxBasedPlatformBackend):
+  def __init__(self, device):
+    assert device, (
+        'AndroidPlatformBackend can only be initialized from remote device')
+    super(AndroidPlatformBackend, self).__init__(device)
+    self._device = device_utils.DeviceUtils(device.device_id)
+    # Trying to root the device, if possible.
+    if not self._device.HasRoot():
+      try:
+        self._device.EnableRoot()
+      except device_errors.CommandFailedError:
+        logging.warning('Unable to root %s', str(self._device))
+    self._battery = battery_utils.BatteryUtils(self._device)
+    self._enable_performance_mode = device.enable_performance_mode
+    self._surface_stats_collector = None
+    self._perf_tests_setup = perf_control.PerfControl(self._device)
+    self._thermal_throttle = thermal_throttle.ThermalThrottle(self._device)
+    self._raw_display_frame_rate_measurements = []
+    self._can_elevate_privilege = (
+        self._device.HasRoot() or self._device.NeedsSU())
+    self._device_copy_script = None
+    self._power_monitor = (
+      android_power_monitor_controller.AndroidPowerMonitorController([
+        android_temperature_monitor.AndroidTemperatureMonitor(self._device),
+        monsoon_power_monitor.MonsoonPowerMonitor(self._device, self),
+        android_dumpsys_power_monitor.DumpsysPowerMonitor(
+          self._battery, self),
+        sysfs_power_monitor.SysfsPowerMonitor(self, standalone=True),
+        android_fuelgauge_power_monitor.FuelGaugePowerMonitor(
+            self._battery),
+    ], self._battery))
+    self._video_recorder = None
+    self._installed_applications = None
+
+    self._device_cert_util = None
+    self._system_ui = None
+
+    _FixPossibleAdbInstability()
+
+  @property
+  def log_file_path(self):
+    return None
+
+  @classmethod
+  def SupportsDevice(cls, device):
+    return isinstance(device, android_device.AndroidDevice)
+
+  @classmethod
+  def CreatePlatformForDevice(cls, device, finder_options):
+    assert cls.SupportsDevice(device)
+    platform_backend = AndroidPlatformBackend(device)
+    return android_platform.AndroidPlatform(platform_backend)
+
+  @property
+  def forwarder_factory(self):
+    if not self._forwarder_factory:
+      self._forwarder_factory = android_forwarder.AndroidForwarderFactory(
+          self._device)
+
+    return self._forwarder_factory
+
+  @property
+  def device(self):
+    return self._device
+
+  def Initialize(self):
+    self.EnsureBackgroundApkInstalled()
+
+  def GetSystemUi(self):
+    if self._system_ui is None:
+      self._system_ui = app_ui.AppUi(self.device, 'com.android.systemui')
+    return self._system_ui
+
+  def IsSvelte(self):
+    description = self._device.GetProp('ro.build.description', cache=True)
+    if description is not None:
+      return 'svelte' in description
+    else:
+      return False
+
+  def GetRemotePort(self, port):
+    return forwarder.Forwarder.DevicePortForHostPort(port) or 0
+
+  def IsDisplayTracingSupported(self):
+    return bool(self.GetOSVersionName() >= 'J')
+
+  def StartDisplayTracing(self):
+    assert not self._surface_stats_collector
+    # Clear any leftover data from previous timed out tests
+    self._raw_display_frame_rate_measurements = []
+    self._surface_stats_collector = \
+        surface_stats_collector.SurfaceStatsCollector(self._device)
+    self._surface_stats_collector.Start()
+
+  def StopDisplayTracing(self):
+    if not self._surface_stats_collector:
+      return
+
+    try:
+      refresh_period, timestamps = self._surface_stats_collector.Stop()
+      pid = self._surface_stats_collector.GetSurfaceFlingerPid()
+    finally:
+      self._surface_stats_collector = None
+    # TODO(sullivan): should this code be inline, or live elsewhere?
+    events = []
+    for ts in timestamps:
+      events.append({
+        'cat': 'SurfaceFlinger',
+        'name': 'vsync_before',
+        'ts': ts,
+        'pid': pid,
+        'tid': pid,
+        'args': {'data': {
+          'frame_count': 1,
+          'refresh_period': refresh_period,
+        }}
+      })
+    return events
+
+  def CanTakeScreenshot(self):
+    return True
+
+  def TakeScreenshot(self, file_path):
+    return bool(self._device.TakeScreenshot(host_path=file_path))
+
+  def SetFullPerformanceModeEnabled(self, enabled):
+    if not self._enable_performance_mode:
+      logging.warning('CPU governor will not be set!')
+      return
+    if enabled:
+      self._perf_tests_setup.SetHighPerfMode()
+    else:
+      self._perf_tests_setup.SetDefaultPerfMode()
+
+  def CanMonitorThermalThrottling(self):
+    return True
+
+  def IsThermallyThrottled(self):
+    return self._thermal_throttle.IsThrottled()
+
+  def HasBeenThermallyThrottled(self):
+    return self._thermal_throttle.HasBeenThrottled()
+
+  def GetCpuStats(self, pid):
+    if not self._can_elevate_privilege:
+      logging.warning('CPU stats cannot be retrieved on non-rooted device.')
+      return {}
+    return super(AndroidPlatformBackend, self).GetCpuStats(pid)
+
+  def GetCpuTimestamp(self):
+    if not self._can_elevate_privilege:
+      logging.warning('CPU timestamp cannot be retrieved on non-rooted device.')
+      return {}
+    return super(AndroidPlatformBackend, self).GetCpuTimestamp()
+
+  def SetGraphicsMemoryTrackingEnabled(self, enabled):
+    if not enabled:
+      self.KillApplication('memtrack_helper')
+      return
+
+    if not android_prebuilt_profiler_helper.InstallOnDevice(
+        self._device, 'memtrack_helper'):
+      raise Exception('Error installing memtrack_helper.')
+    self._device.RunShellCommand([
+      android_prebuilt_profiler_helper.GetDevicePath('memtrack_helper'),
+      '-d'], as_root=True, check_return=True)
+
+  def EnsureBackgroundApkInstalled(self):
+    app = 'push_apps_to_background_apk'
+    arch_name = self._device.GetABI()
+    host_path = binary_manager.FetchPath(app, arch_name, 'android')
+    if not host_path:
+      raise Exception('Error installing PushAppsToBackground.apk.')
+    self.InstallApplication(host_path)
+
+  def PurgeUnpinnedMemory(self):
+    """Purges the unpinned ashmem memory for the whole system.
+
+    This can be used to make memory measurements more stable. Requires root.
+    """
+    if not self._can_elevate_privilege:
+      logging.warning('Cannot run purge_ashmem. Requires a rooted device.')
+      return
+
+    if not android_prebuilt_profiler_helper.InstallOnDevice(
+        self._device, 'purge_ashmem'):
+      raise Exception('Error installing purge_ashmem.')
+    output = self._device.RunShellCommand([
+      android_prebuilt_profiler_helper.GetDevicePath('purge_ashmem')],
+      check_return=True)
+    for l in output:
+      logging.info(l)
+
+  @decorators.Deprecated(
+      2017, 11, 4,
+      'Clients should use tracing and memory-infra in new Telemetry '
+      'benchmarks. See for context: https://crbug.com/632021')
+  def GetMemoryStats(self, pid):
+    memory_usage = self._device.GetMemoryUsageForPid(pid)
+    if not memory_usage:
+      return {}
+    return {'ProportionalSetSize': memory_usage['Pss'] * 1024,
+            'SharedDirty': memory_usage['Shared_Dirty'] * 1024,
+            'PrivateDirty': memory_usage['Private_Dirty'] * 1024,
+            'VMPeak': memory_usage['VmHWM'] * 1024}
+
+  def GetChildPids(self, pid):
+    child_pids = []
+    ps = self.GetPsOutput(['pid', 'name'])
+    for curr_pid, curr_name in ps:
+      if int(curr_pid) == pid:
+        name = curr_name
+        for curr_pid, curr_name in ps:
+          if curr_name.startswith(name) and curr_name != name:
+            child_pids.append(int(curr_pid))
+        break
+    return child_pids
+
+  @decorators.Cache
+  def GetCommandLine(self, pid):
+    ps = self.GetPsOutput(['pid', 'name'], pid)
+    if not ps:
+      raise exceptions.ProcessGoneException()
+    return ps[0][1]
+
+  @decorators.Cache
+  def GetArchName(self):
+    return self._device.GetABI()
+
+  def GetOSName(self):
+    return 'android'
+
+  def GetDeviceTypeName(self):
+    return self._device.product_model
+
+  @decorators.Cache
+  def GetOSVersionName(self):
+    return self._device.GetProp('ro.build.id')[0]
+
+  def CanFlushIndividualFilesFromSystemCache(self):
+    return False
+
+  def SupportFlushEntireSystemCache(self):
+    return self._can_elevate_privilege
+
+  def FlushEntireSystemCache(self):
+    cache = cache_control.CacheControl(self._device)
+    cache.DropRamCaches()
+
+  def FlushSystemCacheForDirectory(self, directory):
+    raise NotImplementedError()
+
+  def FlushDnsCache(self):
+    self._device.RunShellCommand(
+        ['ndc', 'resolver', 'flushdefaultif'], as_root=True, check_return=True)
+
+  def StopApplication(self, application):
+    """Stop the given |application|.
+
+    Args:
+       application: The full package name string of the application to stop.
+    """
+    self._device.ForceStop(application)
+
+  def KillApplication(self, application):
+    """Kill the given |application|.
+
+    Might be used instead of ForceStop for efficiency reasons.
+
+    Args:
+      application: The full package name string of the application to kill.
+    """
+    assert isinstance(application, basestring)
+    self._device.KillAll(application, blocking=True, quiet=True)
+
+  def LaunchApplication(
+      self, application, parameters=None, elevate_privilege=False):
+    """Launches the given |application| with a list of |parameters| on the OS.
+
+    Args:
+      application: The full package name string of the application to launch.
+      parameters: A list of parameters to be passed to the ActivityManager.
+      elevate_privilege: Currently unimplemented on Android.
+    """
+    if elevate_privilege:
+      raise NotImplementedError("elevate_privilege isn't supported on android.")
+    # TODO(catapult:#3215): Migrate to StartActivity.
+    cmd = ['am', 'start']
+    if parameters:
+      cmd.extend(parameters)
+    cmd.append(application)
+    result_lines = self._device.RunShellCommand(cmd, check_return=True)
+    for line in result_lines:
+      if line.startswith('Error: '):
+        raise ValueError('Failed to start "%s" with error\n  %s' %
+                         (application, line))
+
+  def IsApplicationRunning(self, application):
+    return len(self._device.GetPids(application)) > 0
+
+  def CanLaunchApplication(self, application):
+    if not self._installed_applications:
+      self._installed_applications = self._device.RunShellCommand(
+          ['pm', 'list', 'packages'], check_return=True)
+    return 'package:' + application in self._installed_applications
+
+  def InstallApplication(self, application):
+    self._installed_applications = None
+    self._device.Install(application)
+
+  @decorators.Cache
+  def CanCaptureVideo(self):
+    return self.GetOSVersionName() >= 'K'
+
+  def StartVideoCapture(self, min_bitrate_mbps):
+    """Starts the video capture at specified bitrate."""
+    min_bitrate_mbps = max(min_bitrate_mbps, 0.1)
+    if min_bitrate_mbps > 100:
+      raise ValueError('Android video capture cannot capture at %dmbps. '
+                       'Max capture rate is 100mbps.' % min_bitrate_mbps)
+    if self.is_video_capture_running:
+      self._video_recorder.Stop()
+    self._video_recorder = video_recorder.VideoRecorder(
+        self._device, megabits_per_second=min_bitrate_mbps)
+    self._video_recorder.Start(timeout=5)
+
+  @property
+  def is_video_capture_running(self):
+    return self._video_recorder is not None
+
+  def StopVideoCapture(self):
+    assert self.is_video_capture_running, 'Must start video capture first'
+    self._video_recorder.Stop()
+    video_file_obj = tempfile.NamedTemporaryFile()
+    self._video_recorder.Pull(video_file_obj.name)
+    self._video_recorder = None
+
+    return video.Video(video_file_obj)
+
+  def CanMonitorPower(self):
+    return self._power_monitor.CanMonitorPower()
+
+  def StartMonitoringPower(self, browser):
+    self._power_monitor.StartMonitoringPower(browser)
+
+  def StopMonitoringPower(self):
+    return self._power_monitor.StopMonitoringPower()
+
+  def CanMonitorNetworkData(self):
+    return self._device.build_version_sdk >= version_codes.LOLLIPOP
+
+  def GetNetworkData(self, browser):
+    return self._battery.GetNetworkData(browser._browser_backend.package)
+
+  def PathExists(self, device_path, timeout=None, retries=None):
+    """ Return whether the given path exists on the device.
+    This method is the same as
+    devil.android.device_utils.DeviceUtils.PathExists.
+    """
+    return self._device.PathExists(
+        device_path, timeout=timeout, retries=retries)
+
+  def GetFileContents(self, fname):
+    if not self._can_elevate_privilege:
+      logging.warning('%s cannot be retrieved on non-rooted device.', fname)
+      return ''
+    return self._device.ReadFile(fname, as_root=True)
+
+  def GetPsOutput(self, columns, pid=None):
+    assert columns == ['pid', 'name'] or columns == ['pid'], \
+        'Only know how to return pid and name. Requested: ' + columns
+    if pid is not None:
+      pid = str(pid)
+    procs_pids = self._device.GetPids()
+    output = []
+    for curr_name, pids_list in procs_pids.iteritems():
+      for curr_pid in pids_list:
+        if columns == ['pid', 'name']:
+          row = [curr_pid, curr_name]
+        else:
+          row = [curr_pid]
+        if pid is not None:
+          if curr_pid == pid:
+            return [row]
+        else:
+          output.append(row)
+    return output
+
+  def RunCommand(self, command):
+    return '\n'.join(self._device.RunShellCommand(command, check_return=True))
+
+  @staticmethod
+  def ParseCStateSample(sample):
+    sample_stats = {}
+    for cpu in sample:
+      values = sample[cpu].splitlines()
+      # Each state has three values after excluding the time value.
+      num_states = (len(values) - 1) / 3
+      names = values[:num_states]
+      times = values[num_states:2 * num_states]
+      cstates = {'C0': int(values[-1]) * 10 ** 6}
+      for i, state in enumerate(names):
+        if state == 'C0':
+          # The Exynos cpuidle driver for the Nexus 10 uses the name 'C0' for
+          # its WFI state.
+          # TODO(tmandel): We should verify that no other Android device
+          # actually reports time in C0 causing this to report active time as
+          # idle time.
+          state = 'WFI'
+        cstates[state] = int(times[i])
+        cstates['C0'] -= int(times[i])
+      sample_stats[cpu] = cstates
+    return sample_stats
+
+  def SetRelaxSslCheck(self, value):
+    old_flag = self._device.GetProp('socket.relaxsslcheck')
+    self._device.SetProp('socket.relaxsslcheck', value)
+    return old_flag
+
+  def ForwardHostToDevice(self, host_port, device_port):
+    self._device.adb.Forward('tcp:%d' % host_port, device_port)
+
+  def StopForwardingHost(self, host_port):
+    # This used to run `adb forward --list` to check that the requested
+    # port was actually being forwarded to self._device. Unfortunately,
+    # starting in adb 1.0.36, a bug (b/31811775) keeps this from working.
+    # For now, try to remove the port forwarding and ignore failures.
+    try:
+      self._device.adb.ForwardRemove('tcp:%d' % host_port)
+    except device_errors.AdbCommandFailedError:
+      logging.critical(
+          'Attempted to unforward port tcp:%d but failed.', host_port)
+
+  def DismissCrashDialogIfNeeded(self):
+    """Dismiss any error dialogs.
+
+    Limit the number in case we have an error loop or we are failing to dismiss.
+    """
+    for _ in xrange(10):
+      if not self._device.DismissCrashDialogIfNeeded():
+        break
+
+  def IsAppRunning(self, process_name):
+    """Determine if the given process is running.
+
+    Args:
+      process_name: The full package name string of the process.
+    """
+    return bool(self._device.GetPids(process_name))
+
+  @property
+  def supports_test_ca(self):
+    # TODO(nednguyen): figure out how to install certificate on Android M
+    # crbug.com/593152
+    return self._device.build_version_sdk <= version_codes.LOLLIPOP_MR1
+
+  def InstallTestCa(self, ca_cert_path):
+    """Install a randomly generated root CA on the android device.
+
+    This allows transparent HTTPS testing with WPR server without need
+    to tweak application network stack.
+
+    Note: If this method fails with any exception, then RemoveTestCa will be
+    automatically called by the network_controller_backend.
+    """
+    if self._device_cert_util is not None:
+      logging.warning('Test certificate authority is already installed.')
+      return
+    self._device_cert_util = adb_install_cert.AndroidCertInstaller(
+        self._device.adb.GetDeviceSerial(), None, ca_cert_path,
+        adb_path=self._device.adb.GetAdbPath())
+    self._device_cert_util.install_cert(overwrite_cert=True)
+
+  def RemoveTestCa(self):
+    """Remove root CA from device installed by InstallTestCa.
+
+    Note: Any exceptions raised by this method will be logged but dismissed by
+    the network_controller_backend.
+    """
+    if self._device_cert_util is not None:
+      try:
+        self._device_cert_util.remove_cert()
+      finally:
+        self._device_cert_util = None
+
+  def PushProfile(self, package, new_profile_dir):
+    """Replace application profile with files found on host machine.
+
+    Pushing the profile is slow, so we don't want to do it every time.
+    Avoid this by pushing to a safe location using PushChangedFiles, and
+    then copying into the correct location on each test run.
+
+    Args:
+      package: The full package name string of the application for which the
+        profile is to be updated.
+      new_profile_dir: Location where profile to be pushed is stored on the
+        host machine.
+    """
+    (profile_parent, profile_base) = os.path.split(new_profile_dir)
+    # If the path ends with a '/' python split will return an empty string for
+    # the base name; so we now need to get the base name from the directory.
+    if not profile_base:
+      profile_base = os.path.basename(profile_parent)
+
+    saved_profile_location = '/sdcard/profile/%s' % profile_base
+    self._device.PushChangedFiles([(new_profile_dir, saved_profile_location)])
+
+    profile_dir = self._GetProfileDir(package)
+    self._EfficientDeviceDirectoryCopy(
+        saved_profile_location, profile_dir)
+    dumpsys = self._device.RunShellCommand(
+        ['dumpsys', 'package', package], check_return=True)
+    id_line = next(line for line in dumpsys if 'userId=' in line)
+    uid = re.search(r'\d+', id_line).group()
+    files = self._device.ListDirectory(profile_dir, as_root=True)
+    paths = [posixpath.join(profile_dir, f) for f in files if f != 'lib']
+    for path in paths:
+      # TODO(crbug.com/628617): Implement without ignoring shell errors.
+      # Note: need to pass command as a string for the shell to expand the *'s.
+      extended_path = '%s %s/* %s/*/* %s/*/*/*' % (path, path, path, path)
+      self._device.RunShellCommand(
+          'chown %s.%s %s' % (uid, uid, extended_path),
+          check_return=False, shell=True)
+
+  def _EfficientDeviceDirectoryCopy(self, source, dest):
+    if not self._device_copy_script:
+      self._device.adb.Push(
+          _DEVICE_COPY_SCRIPT_FILE,
+          _DEVICE_COPY_SCRIPT_LOCATION)
+      self._device_copy_script = _DEVICE_COPY_SCRIPT_LOCATION
+    self._device.RunShellCommand(
+        ['sh', self._device_copy_script, source, dest], check_return=True)
+
+  def GetPortPairForForwarding(self, local_port):
+    return forwarders.PortPair(local_port=local_port, remote_port=0)
+
+  def RemoveProfile(self, package, ignore_list):
+    """Delete application profile on device.
+
+    Args:
+      package: The full package name string of the application for which the
+        profile is to be deleted.
+      ignore_list: List of files to keep.
+    """
+    profile_dir = self._GetProfileDir(package)
+    if not self._device.PathExists(profile_dir):
+      return
+    files = [
+      posixpath.join(profile_dir, f)
+      for f in self._device.ListDirectory(profile_dir, as_root=True)
+      if f not in ignore_list]
+    if not files:
+      return
+    self._device.RemovePath(files, recursive=True, as_root=True)
+
+  def PullProfile(self, package, output_profile_path):
+    """Copy application profile from device to host machine.
+
+    Args:
+      package: The full package name string of the application for which the
+        profile is to be copied.
+      output_profile_dir: Location where profile to be stored on host machine.
+    """
+    profile_dir = self._GetProfileDir(package)
+    logging.info("Pulling profile directory from device: '%s'->'%s'.",
+                 profile_dir, output_profile_path)
+    # To minimize bandwidth it might be good to look at whether all the data
+    # pulled down is really needed e.g. .pak files.
+    if not os.path.exists(output_profile_path):
+      os.makedirs(output_profile_path)
+    problem_files = []
+    for filename in self._device.ListDirectory(profile_dir, as_root=True):
+      # Don't pull lib, since it is created by the installer.
+      if filename == 'lib':
+        continue
+      source = posixpath.join(profile_dir, filename)
+      dest = os.path.join(output_profile_path, filename)
+      try:
+        self._device.PullFile(source, dest, timeout=240)
+      except device_errors.CommandFailedError:
+        problem_files.append(source)
+    if problem_files:
+      # Some paths (e.g. 'files', 'app_textures') consistently fail to be
+      # pulled from the device.
+      logging.warning(
+          'There were errors retrieving the following paths from the profile:')
+      for filepath in problem_files:
+        logging.warning('- %s', filepath)
+
+  def _GetProfileDir(self, package):
+    """Returns the on-device location where the application profile is stored
+    based on Android convention.
+
+    Args:
+      package: The full package name string of the application.
+    """
+    return '/data/data/%s/' % package
+
+  def SetDebugApp(self, package):
+    """Set application to debugging.
+
+    Args:
+      package: The full package name string of the application.
+    """
+    if self._device.IsUserBuild():
+      logging.debug('User build device, setting debug app')
+      self._device.RunShellCommand(
+          ['am', 'set-debug-app', '--persistent', package],
+          check_return=True)
+
+  def GetLogCat(self, number_of_lines=500):
+    """Returns most recent lines of logcat dump.
+
+    Args:
+      number_of_lines: Number of lines of log to return.
+    """
+    def decode_line(line):
+      try:
+        uline = unicode(line, encoding='utf-8')
+        return uline.encode('ascii', 'backslashreplace')
+      except Exception:
+        logging.error('Error encoding UTF-8 logcat line as ASCII.')
+        return '<MISSING LOGCAT LINE: FAILED TO ENCODE>'
+
+    logcat_output = self._device.RunShellCommand(
+        ['logcat', '-d', '-t', str(number_of_lines)],
+        check_return=True, large_output=True)
+    return '\n'.join(decode_line(l) for l in logcat_output)
+
+  def GetStandardOutput(self):
+    return 'Cannot get standard output on Android'
+
+  def GetStackTrace(self):
+    """Returns stack trace.
+
+    The stack trace consists of raw logcat dump, logcat dump with symbols,
+    and stack info from tomstone files.
+    """
+    def Decorate(title, content):
+      return "%s\n%s\n%s\n" % (title, content, '*' * 80)
+
+    # Get the UI nodes that can be found on the screen
+    ret = Decorate('UI dump', '\n'.join(self.GetSystemUi().ScreenDump()))
+
+    # Get the last lines of logcat (large enough to contain stacktrace)
+    logcat = self.GetLogCat()
+    ret += Decorate('Logcat', logcat)
+    stack = os.path.join(util.GetChromiumSrcDir(), 'third_party',
+                         'android_platform', 'development', 'scripts', 'stack')
+    # Try to symbolize logcat.
+    if os.path.exists(stack):
+      cmd = [stack]
+      arch = self.GetArchName()
+      arch = _ARCH_TO_STACK_TOOL_ARCH.get(arch, arch)
+      cmd.append('--arch=%s' % arch)
+      p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
+      ret += Decorate('Stack from Logcat', p.communicate(input=logcat)[0])
+
+    # Try to get tombstones.
+    tombstones = os.path.join(util.GetChromiumSrcDir(), 'build', 'android',
+                              'tombstones.py')
+    if os.path.exists(tombstones):
+      tombstones_cmd = [
+          tombstones, '-w',
+          '--device', self._device.adb.GetDeviceSerial(),
+          '--adb-path', self._device.adb.GetAdbPath(),
+      ]
+      ret += Decorate('Tombstones',
+                      subprocess.Popen(tombstones_cmd,
+                                       stdout=subprocess.PIPE).communicate()[0])
+    return (True, ret)
+
+  def GetMinidumpPath(self):
+    return None
+
+  def IsScreenOn(self):
+    """Determines if device screen is on."""
+    return self._device.IsScreenOn()
+
+  @staticmethod
+  def _IsScreenLocked(input_methods):
+    """Parser method for IsScreenLocked()
+
+    Args:
+      input_methods: Output from dumpsys input_methods
+
+    Returns:
+      boolean: True if screen is locked, false if screen is not locked.
+
+    Raises:
+      ValueError: An unknown value is found for the screen lock state.
+      AndroidDeviceParsingError: Error in detecting screen state.
+
+    """
+    for line in input_methods:
+      if 'mHasBeenInactive' in line:
+        for pair in line.strip().split(' '):
+          key, value = pair.split('=', 1)
+          if key == 'mHasBeenInactive':
+            if value == 'true':
+              return True
+            elif value == 'false':
+              return False
+            else:
+              raise ValueError('Unknown value for %s: %s' % (key, value))
+    raise exceptions.AndroidDeviceParsingError(str(input_methods))
+
+  def IsScreenLocked(self):
+    """Determines if device screen is locked."""
+    input_methods = self._device.RunShellCommand(['dumpsys', 'input_method'],
+                                                 check_return=True)
+    return self._IsScreenLocked(input_methods)
+
+  def HasBattOrConnected(self):
+    # Use linux instead of Android because when determining what tests to run on
+    # a bot the individual device could be down, which would make BattOr tests
+    # not run on any device. BattOrs communicate with the host and not android
+    # devices.
+    return battor_wrapper.IsBattOrConnected('linux')
+
+  def Log(self, message):
+    """Prints line to logcat."""
+    TELEMETRY_LOGCAT_TAG = 'Telemetry'
+    self._device.RunShellCommand(
+        ['log', '-p', 'i', '-t', TELEMETRY_LOGCAT_TAG, message],
+        check_return=True)
+
+  def WaitForTemperature(self, temp):
+    # Temperature is in tenths of a degree C, so we convert to that scale.
+    self._battery.LetBatteryCoolToTemperature(temp * 10)
+
+def _FixPossibleAdbInstability():
+  """Host side workaround for crbug.com/268450 (adb instability).
+
+  The adb server has a race which is mitigated by binding to a single core.
+  """
+  if not psutil:
+    return
+  for process in psutil.process_iter():
+    try:
+      if psutil.version_info >= (2, 0):
+        if 'adb' in process.name():
+          process.cpu_affinity([0])
+      else:
+        if 'adb' in process.name:
+          process.set_cpu_affinity([0])
+    except (psutil.NoSuchProcess, psutil.AccessDenied):
+      logging.warn('Failed to set adb process CPU affinity')
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/android_platform_backend_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/android_platform_backend_unittest.py
new file mode 100644
index 0000000..554df5c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/android_platform_backend_unittest.py
@@ -0,0 +1,216 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from telemetry import decorators
+from telemetry.internal.platform import android_device
+from telemetry.internal.platform import android_platform_backend
+from telemetry.testing import system_stub
+import mock
+
+from devil.android import battery_utils
+from devil.android import device_utils
+
+class AndroidPlatformBackendTest(unittest.TestCase):
+  def setUp(self):
+    self._stubs = system_stub.Override(
+        android_platform_backend,
+        ['perf_control', 'thermal_throttle'])
+
+    # Skip _FixPossibleAdbInstability by setting psutil to None.
+    self._actual_ps_util = android_platform_backend.psutil
+    android_platform_backend.psutil = None
+    self.battery_patcher = mock.patch.object(battery_utils, 'BatteryUtils')
+    self.battery_patcher.start()
+
+    def get_prop(name, cache=None):
+      del cache  # unused
+      return {'ro.product.cpu.abi': 'armeabi-v7a'}.get(name)
+
+    self.device_patcher = mock.patch.multiple(
+        device_utils.DeviceUtils,
+        HasRoot=mock.MagicMock(return_value=True),
+        GetProp=mock.MagicMock(side_effect=get_prop))
+    self.device_patcher.start()
+
+  def tearDown(self):
+    self._stubs.Restore()
+    android_platform_backend.psutil = self._actual_ps_util
+    self.battery_patcher.stop()
+    self.device_patcher.stop()
+
+  @decorators.Disabled('chromeos', 'mac', 'win')
+  def testIsSvelte(self):
+    with mock.patch('devil.android.device_utils.DeviceUtils.GetProp',
+                    return_value='svelte'):
+      backend = android_platform_backend.AndroidPlatformBackend(
+          android_device.AndroidDevice('12345'))
+      self.assertTrue(backend.IsSvelte())
+
+  @decorators.Disabled('chromeos', 'mac', 'win')
+  def testIsNotSvelte(self):
+    with mock.patch('devil.android.device_utils.DeviceUtils.GetProp',
+                    return_value='foo'):
+      backend = android_platform_backend.AndroidPlatformBackend(
+          android_device.AndroidDevice('12345'))
+      self.assertFalse(backend.IsSvelte())
+
+  @decorators.Disabled('chromeos', 'mac', 'win')
+  def testGetCpuStats(self):
+    proc_stat_content = (
+        '7702 (.android.chrome) S 167 167 0 0 -1 1077936448 '
+        '3247 0 0 0 4 1 0 0 20 0 9 0 5603962 337379328 5867 '
+        '4294967295 1074458624 1074463824 3197495984 3197494152 '
+        '1074767676 0 4612 0 38136 4294967295 0 0 17 0 0 0 0 0 0 '
+        '1074470376 1074470912 1102155776\n')
+    with mock.patch('devil.android.device_utils.DeviceUtils.ReadFile',
+                    return_value=proc_stat_content):
+      backend = android_platform_backend.AndroidPlatformBackend(
+          android_device.AndroidDevice('12345'))
+      cpu_stats = backend.GetCpuStats('7702')
+      self.assertEquals(cpu_stats, {'CpuProcessTime': 0.05})
+
+  @decorators.Disabled('chromeos', 'mac', 'win')
+  def testGetCpuStatsInvalidPID(self):
+    # Mock an empty /proc/pid/stat.
+    with mock.patch('devil.android.device_utils.DeviceUtils.ReadFile',
+                    return_value=''):
+      backend = android_platform_backend.AndroidPlatformBackend(
+          android_device.AndroidDevice('1234'))
+      cpu_stats = backend.GetCpuStats('7702')
+      self.assertEquals(cpu_stats, {})
+
+  @decorators.Disabled('chromeos', 'mac', 'win')
+  def testAndroidParseCpuStates(self):
+    cstate = {
+      'cpu0': 'C0\nC1\n103203424\n5342040\n300\n500\n1403232500',
+      'cpu1': 'C0\n124361858\n300\n1403232500'
+    }
+    expected_cstate = {
+      'cpu0': {
+        'WFI': 103203424,
+        'C0': 1403232391454536,
+        'C1': 5342040
+      },
+      'cpu1': {
+        'WFI': 124361858,
+        'C0': 1403232375638142
+      }
+    }
+    # Use mock start and end times to allow for the test to calculate C0.
+    result = android_platform_backend.AndroidPlatformBackend.ParseCStateSample(
+        cstate)
+    for cpu in result:
+      for state in result[cpu]:
+        self.assertAlmostEqual(result[cpu][state], expected_cstate[cpu][state])
+
+  @decorators.Disabled('chromeos', 'mac', 'win')
+  def testInstallTestCaSuccess(self):
+    backend = android_platform_backend.AndroidPlatformBackend(
+        android_device.AndroidDevice('success'))
+    with mock.patch('adb_install_cert.AndroidCertInstaller'):
+      backend.InstallTestCa('testca.pem')
+      self.assertIsNotNone(backend._device_cert_util)
+
+      backend.RemoveTestCa()
+      self.assertIsNone(backend._device_cert_util)
+
+  @decorators.Disabled('chromeos', 'mac', 'win')
+  def testIsScreenLockedTrue(self):
+    test_input = ['a=b', 'mHasBeenInactive=true']
+    backend = android_platform_backend.AndroidPlatformBackend(
+        android_device.AndroidDevice('success'))
+    self.assertTrue(backend._IsScreenLocked(test_input))
+
+  @decorators.Disabled('chromeos', 'mac', 'win')
+  def testIsScreenLockedFalse(self):
+    test_input = ['a=b', 'mHasBeenInactive=false']
+    backend = android_platform_backend.AndroidPlatformBackend(
+        android_device.AndroidDevice('success'))
+    self.assertFalse(backend._IsScreenLocked(test_input))
+
+
+class AndroidPlatformBackendPsutilTest(unittest.TestCase):
+
+  class psutil_1_0(object):
+    version_info = (1, 0)
+    def __init__(self):
+      self.set_cpu_affinity_args = []
+    class Process(object):
+      def __init__(self, parent):
+        self._parent = parent
+        self.name = 'adb'
+      def set_cpu_affinity(self, cpus):
+        self._parent.set_cpu_affinity_args.append(cpus)
+    def process_iter(self):
+      return [self.Process(self)]
+
+  class psutil_2_0(object):
+    version_info = (2, 0)
+    def __init__(self):
+      self.set_cpu_affinity_args = []
+    class Process(object):
+      def __init__(self, parent):
+        self._parent = parent
+        self.set_cpu_affinity_args = []
+      def name(self):
+        return 'adb'
+      def cpu_affinity(self, cpus=None):
+        self._parent.set_cpu_affinity_args.append(cpus)
+    def process_iter(self):
+      return [self.Process(self)]
+
+  def setUp(self):
+    self._stubs = system_stub.Override(
+        android_platform_backend,
+        ['perf_control'])
+    self.battery_patcher = mock.patch.object(battery_utils, 'BatteryUtils')
+    self.battery_patcher.start()
+    self._actual_ps_util = android_platform_backend.psutil
+
+    def get_prop(name, cache=None):
+      del cache  # unused
+      return {'ro.product.cpu.abi': 'armeabi-v7a'}.get(name)
+
+    self.device_patcher = mock.patch.multiple(
+        device_utils.DeviceUtils,
+        FileExists=mock.MagicMock(return_value=False),
+        GetProp=mock.MagicMock(side_effect=get_prop),
+        HasRoot=mock.MagicMock(return_value=True))
+    self.device_patcher.start()
+
+  def tearDown(self):
+    self._stubs.Restore()
+    android_platform_backend.psutil = self._actual_ps_util
+    self.battery_patcher.stop()
+    self.device_patcher.stop()
+
+  @decorators.Disabled('chromeos', 'mac', 'win')
+  def testPsutil1(self):
+    psutil = self.psutil_1_0()
+    android_platform_backend.psutil = psutil
+
+    # Mock an empty /proc/pid/stat.
+    with mock.patch('devil.android.device_utils.DeviceUtils.ReadFile',
+                    return_value=''):
+      backend = android_platform_backend.AndroidPlatformBackend(
+          android_device.AndroidDevice('1234'))
+      cpu_stats = backend.GetCpuStats('7702')
+      self.assertEquals({}, cpu_stats)
+      self.assertEquals([[0]], psutil.set_cpu_affinity_args)
+
+  @decorators.Disabled('chromeos', 'mac', 'win')
+  def testPsutil2(self):
+    psutil = self.psutil_2_0()
+    android_platform_backend.psutil = psutil
+
+    # Mock an empty /proc/pid/stat.
+    with mock.patch('devil.android.device_utils.DeviceUtils.ReadFile',
+                    return_value=''):
+      backend = android_platform_backend.AndroidPlatformBackend(
+          android_device.AndroidDevice('1234'))
+      cpu_stats = backend.GetCpuStats('7702')
+      self.assertEquals({}, cpu_stats)
+      self.assertEquals([[0]], psutil.set_cpu_affinity_args)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/cros_device.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/cros_device.py
new file mode 100644
index 0000000..f27ebd1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/cros_device.py
@@ -0,0 +1,54 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import logging
+
+from telemetry.core import cros_interface
+from telemetry.core import platform
+from telemetry.internal.platform import device
+
+
+class CrOSDevice(device.Device):
+  def __init__(self, host_name, ssh_port, ssh_identity, is_local):
+    super(CrOSDevice, self).__init__(
+        name='ChromeOs with host %s' % host_name or 'localhost',
+        guid='cros:%s' % host_name or 'localhost')
+    self._host_name = host_name
+    self._ssh_port = ssh_port
+    self._ssh_identity = ssh_identity
+    self._is_local = is_local
+
+  @classmethod
+  def GetAllConnectedDevices(cls, blacklist):
+    return []
+
+  @property
+  def host_name(self):
+    return self._host_name
+
+  @property
+  def ssh_port(self):
+    return self._ssh_port
+
+  @property
+  def ssh_identity(self):
+    return self._ssh_identity
+
+  @property
+  def is_local(self):
+    return self._is_local
+
+
+def IsRunningOnCrOS():
+  return platform.GetHostPlatform().GetOSName() == 'chromeos'
+
+
+def FindAllAvailableDevices(options):
+  """Returns a list of available device types."""
+  use_ssh = options.cros_remote and cros_interface.HasSSH()
+  if not use_ssh and not IsRunningOnCrOS():
+    logging.debug('No --remote specified, and not running on ChromeOs.')
+    return []
+
+  return [CrOSDevice(options.cros_remote, options.cros_remote_ssh_port,
+                     options.cros_ssh_identity, not use_ssh)]
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/cros_platform_backend.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/cros_platform_backend.py
new file mode 100644
index 0000000..89466fd
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/cros_platform_backend.py
@@ -0,0 +1,167 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+
+from telemetry import decorators
+from telemetry.core import cros_interface
+from telemetry.core import platform
+from telemetry.core import util
+from telemetry.internal.forwarders import cros_forwarder
+from telemetry.internal.platform import cros_device
+from telemetry.internal.platform import linux_based_platform_backend
+from telemetry.internal.platform.power_monitor import cros_power_monitor
+from telemetry.internal.util import ps_util
+
+
+class CrosPlatformBackend(
+    linux_based_platform_backend.LinuxBasedPlatformBackend):
+  def __init__(self, device=None):
+    super(CrosPlatformBackend, self).__init__(device)
+    if device and not device.is_local:
+      self._cri = cros_interface.CrOSInterface(
+          device.host_name, device.ssh_port, device.ssh_identity)
+      self._cri.TryLogin()
+    else:
+      self._cri = cros_interface.CrOSInterface()
+    self._powermonitor = cros_power_monitor.CrosPowerMonitor(self)
+
+  @classmethod
+  def IsPlatformBackendForHost(cls):
+    return util.IsRunningOnCrosDevice()
+
+  @classmethod
+  def SupportsDevice(cls, device):
+    return isinstance(device, cros_device.CrOSDevice)
+
+  @classmethod
+  def CreatePlatformForDevice(cls, device, finder_options):
+    assert cls.SupportsDevice(device)
+    return platform.Platform(CrosPlatformBackend(device))
+
+  @property
+  def cri(self):
+    return self._cri
+
+  @property
+  def forwarder_factory(self):
+    if not self._forwarder_factory:
+      self._forwarder_factory = cros_forwarder.CrOsForwarderFactory(self._cri)
+    return self._forwarder_factory
+
+  def GetRemotePort(self, port):
+    if self._cri.local:
+      return port
+    return self._cri.GetRemotePort()
+
+  def IsThermallyThrottled(self):
+    raise NotImplementedError()
+
+  def HasBeenThermallyThrottled(self):
+    raise NotImplementedError()
+
+  def RunCommand(self, args):
+    if not isinstance(args, list):
+      args = [args]
+    stdout, stderr = self._cri.RunCmdOnDevice(args)
+    if stderr:
+      raise IOError('Failed to run: cmd = %s, stderr = %s' %
+                    (str(args), stderr))
+    return stdout
+
+  def GetFileContents(self, filename):
+    try:
+      return self.RunCommand(['cat', filename])
+    except AssertionError:
+      return ''
+
+  def GetPsOutput(self, columns, pid=None):
+    return ps_util.GetPsOutputWithPlatformBackend(self, columns, pid)
+
+  @staticmethod
+  def ParseCStateSample(sample):
+    sample_stats = {}
+    for cpu in sample:
+      values = sample[cpu].splitlines()
+      # There are three values per state after excluding the single time value.
+      num_states = (len(values) - 1) / 3
+      names = values[:num_states]
+      times = values[num_states:2 * num_states]
+      latencies = values[2 * num_states:]
+      # The last line in the sample contains the time.
+      cstates = {'C0': int(values[-1]) * 10 ** 6}
+      for i, state in enumerate(names):
+        if names[i] == 'POLL' and not int(latencies[i]):
+          # C0 state. Kernel stats aren't right, so calculate by
+          # subtracting all other states from total time (using epoch
+          # timer since we calculate differences in the end anyway).
+          # NOTE: Only x86 lists C0 under cpuidle, ARM does not.
+          continue
+        cstates['C0'] -= int(times[i])
+        if names[i] == '<null>':
+          # Kernel race condition that can happen while a new C-state gets
+          # added (e.g. AC->battery). Don't know the 'name' of the state
+          # yet, but its 'time' would be 0 anyway.
+          continue
+        cstates[state] = int(times[i])
+      sample_stats[cpu] = cstates
+    return sample_stats
+
+  def GetDeviceTypeName(self):
+    return self._cri.GetDeviceTypeName()
+
+  @decorators.Cache
+  def GetArchName(self):
+    return self._cri.GetArchName()
+
+  def GetOSName(self):
+    return 'chromeos'
+
+  def GetOSVersionName(self):
+    return ''  # TODO: Implement this.
+
+  def GetChildPids(self, pid):
+    """Returns a list of child pids of |pid|."""
+    all_process_info = self._cri.ListProcesses()
+    processes = [(curr_pid, curr_ppid, curr_state)
+                 for curr_pid, _, curr_ppid, curr_state in all_process_info]
+    return ps_util.GetChildPids(processes, pid)
+
+  def GetCommandLine(self, pid):
+    procs = self._cri.ListProcesses()
+    return next((proc[1] for proc in procs if proc[0] == pid), None)
+
+  def CanFlushIndividualFilesFromSystemCache(self):
+    return True
+
+  def FlushEntireSystemCache(self):
+    raise NotImplementedError()
+
+  def FlushSystemCacheForDirectory(self, directory):
+    flush_command = (
+        '/usr/local/telemetry/src/src/out/Release/clear_system_cache')
+    self.RunCommand(['chmod', '+x', flush_command])
+    self.RunCommand([flush_command, '--recurse', directory])
+
+  def CanMonitorPower(self):
+    return self._powermonitor.CanMonitorPower()
+
+  def StartMonitoringPower(self, browser):
+    self._powermonitor.StartMonitoringPower(browser)
+
+  def StopMonitoringPower(self):
+    return self._powermonitor.StopMonitoringPower()
+
+  def PathExists(self, path, timeout=None, retries=None):
+    if timeout or retries:
+      logging.warning(
+          'PathExists: params timeout and retries are not support on CrOS.')
+    return self._cri.FileExistsOnDevice(path)
+
+  def CanTakeScreenshot(self):
+    # crbug.com/609001: screenshots don't work on VMs.
+    return not self.cri.IsRunningOnVM()
+
+  def TakeScreenshot(self, file_path):
+    return self._cri.TakeScreenshot(file_path)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/cros_platform_backend_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/cros_platform_backend_unittest.py
new file mode 100644
index 0000000..d6c8ac7
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/cros_platform_backend_unittest.py
@@ -0,0 +1,38 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from telemetry.internal.platform import cros_platform_backend
+
+
+class CrosPlatformBackendTest(unittest.TestCase):
+  initial_cstate = {
+    'cpu0': 'POLL\nC1\nC2\nC3\n0\n138356189\n102416540\n'
+            '17158209182\n0\n1\n500\n1000\n1403211341',
+    'cpu1': 'POLL\nC1\nC2\nC3\n0\n107318149\n81786238\n'
+            '17348563431\n0\n1\n500\n1000\n1403211341'
+  }
+  expected_cstate = {
+    'cpu0': {
+      'C0': 1403193942018089,
+      'C1': 138356189,
+      'C2': 102416540,
+      'C3': 17158209182
+    },
+    'cpu1': {
+      'C0': 1403193803332182,
+      'C1': 107318149,
+      'C2': 81786238,
+      'C3': 17348563431
+    }
+  }
+  def testCrosParseCpuStates(self):
+    # Use mock start and end times to allow for the test to calculate C0.
+    results = cros_platform_backend.CrosPlatformBackend.ParseCStateSample(
+        self.initial_cstate)
+    for cpu in results:
+      for state in results[cpu]:
+        self.assertAlmostEqual(results[cpu][state],
+                               self.expected_cstate[cpu][state])
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/desktop_device.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/desktop_device.py
new file mode 100644
index 0000000..0a9f0fa
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/desktop_device.py
@@ -0,0 +1,24 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.core import platform
+from telemetry.internal.platform import device
+
+
+class DesktopDevice(device.Device):
+  def __init__(self):
+    super(DesktopDevice, self).__init__(name='desktop', guid='desktop')
+
+  @classmethod
+  def GetAllConnectedDevices(cls, blacklist):
+    return []
+
+
+def FindAllAvailableDevices(_):
+  """Returns a list of available devices.
+  """
+  # If the host platform is Chrome OS, the device is also considered as cros.
+  if platform.GetHostPlatform().GetOSName() == 'chromeos':
+    return []
+  return [DesktopDevice()]
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/desktop_platform_backend.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/desktop_platform_backend.py
new file mode 100644
index 0000000..510b534
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/desktop_platform_backend.py
@@ -0,0 +1,27 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import subprocess
+
+from telemetry.internal.util import binary_manager
+from telemetry.internal.platform import platform_backend
+
+
+class DesktopPlatformBackend(platform_backend.PlatformBackend):
+
+  # This is an abstract class. It is OK to have abstract methods.
+  # pylint: disable=abstract-method
+
+  def FlushSystemCacheForDirectory(self, directory):
+    assert directory and os.path.exists(directory), \
+        'Target directory %s must exist' % directory
+    flush_command = binary_manager.FetchPath(
+        'clear_system_cache', self.GetArchName(), self.GetOSName())
+    assert flush_command, 'You must build clear_system_cache first'
+
+    subprocess.check_call([flush_command, '--recurse', directory])
+
+  def GetDeviceTypeName(self):
+    return 'Desktop'
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/device.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/device.py
new file mode 100644
index 0000000..34ca721
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/device.py
@@ -0,0 +1,32 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+class Device(object):
+  """ A base class of devices.
+  A device instance contains all the necessary information for constructing
+  a platform backend object for remote platforms.
+
+  Attributes:
+    name: A device name string in human-understandable term.
+    guid: A unique id of the device. Subclass of device must specify this
+      id properly so that device objects to a same actual device must have same
+      guid.
+    """
+
+  def __init__(self, name, guid):
+    self._name = name
+    self._guid = guid
+
+  @property
+  def name(self):
+    return self._name
+
+  @property
+  def guid(self):
+    return self._guid
+
+  @classmethod
+  def GetAllConnectedDevices(cls, blacklist):
+    raise NotImplementedError()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/device_finder.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/device_finder.py
new file mode 100644
index 0000000..fdcd5ae
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/device_finder.py
@@ -0,0 +1,43 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Finds devices that can be controlled by telemetry."""
+
+from telemetry.internal.platform import android_device
+from telemetry.internal.platform import cros_device
+from telemetry.internal.platform import desktop_device
+from telemetry.internal.platform import ios_device
+
+DEVICES = [
+  android_device,
+  cros_device,
+  desktop_device,
+  ios_device,
+]
+
+
+def _GetAllAvailableDevices(options):
+  """Returns a list of all available devices."""
+  devices = []
+  for device in DEVICES:
+    devices.extend(device.FindAllAvailableDevices(options))
+  return devices
+
+
+def GetDevicesMatchingOptions(options):
+  """Returns a list of devices matching the options."""
+  devices = []
+  remote_platform_options = options.remote_platform_options
+  if (not remote_platform_options.device or
+      remote_platform_options.device == 'list'):
+    devices = _GetAllAvailableDevices(options)
+  elif remote_platform_options.device == 'android':
+    devices = android_device.FindAllAvailableDevices(options)
+  else:
+    devices = _GetAllAvailableDevices(options)
+    devices = [d for d in devices if d.guid ==
+               options.remote_platform_options.device]
+
+  devices.sort(key=lambda device: device.name)
+  return devices
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/efficient_android_directory_copy.sh b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/efficient_android_directory_copy.sh
new file mode 100755
index 0000000..7021109
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/efficient_android_directory_copy.sh
@@ -0,0 +1,78 @@
+#!/system/bin/sh
+
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Android shell script to make the destination directory identical with the
+# source directory, without doing unnecessary copies. This assumes that the
+# the destination directory was originally a copy of the source directory, and
+# has since been modified.
+
+source=$1
+dest=$2
+echo copying $source to $dest
+
+delete_extra() {
+  # Don't delete symbolic links, since doing so deletes the vital lib link.
+  if [ ! -L "$1" ]
+  then
+    if [ ! -e "$source/$1" ]
+    then
+      echo rm -rf "$dest/$1"
+      rm -rf "$dest/$1"
+    elif [ -d "$1" ]
+    then
+      for f in "$1"/*
+      do
+       delete_extra "$f"
+      done
+    fi
+  fi
+}
+
+copy_if_older() {
+  if [ -d "$1" ] && [ -e "$dest/$1" ]
+  then
+    if [ ! -e "$dest/$1" ]
+    then
+      echo cp -a "$1" "$dest/$1"
+      cp -a "$1" "$dest/$1"
+    else
+      for f in "$1"/*
+      do
+        copy_if_older "$f"
+      done
+    fi
+  elif [ ! -e "$dest/$1" ] || [ "$1" -ot "$dest/$1" ] || [ "$1" -nt "$dest/$1" ]
+  then
+    # dates are different, so either the destination of the source has changed.
+    echo cp -a "$1" "$dest/$1"
+    cp -a "$1" "$dest/$1"
+  fi
+}
+
+if [ -e "$dest" ]
+then
+  echo cd "$dest"
+  cd "$dest"
+  for f in ./*
+  do
+    if [ -e "$f" ]
+    then
+      delete_extra "$f"
+    fi
+  done
+else
+  echo mkdir "$dest"
+  mkdir "$dest"
+fi
+echo cd "$source"
+cd "$source"
+for f in ./*
+do
+  if [ -e "$f" ]
+  then
+    copy_if_older "$f"
+  fi
+done
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/gpu_device.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/gpu_device.py
new file mode 100644
index 0000000..926f927
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/gpu_device.py
@@ -0,0 +1,84 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+class GPUDevice(object):
+  """Provides information about an individual GPU device.
+
+     On platforms which support them, the vendor_id and device_id are
+     PCI IDs. On other platforms, the vendor_string and device_string
+     are platform-dependent strings.
+  """
+
+  _VENDOR_ID_MAP = {
+    0x1002: 'ATI',
+    0x8086: 'Intel',
+    0x10de: 'Nvidia',
+    }
+
+  def __init__(self, vendor_id, device_id, vendor_string, device_string):
+    self._vendor_id = vendor_id
+    self._device_id = device_id
+    self._vendor_string = vendor_string
+    self._device_string = device_string
+
+  def __str__(self):
+    vendor = 'VENDOR = 0x%x' % self._vendor_id
+    vendor_string = self._vendor_string
+    if not vendor_string and self._vendor_id in self._VENDOR_ID_MAP:
+      vendor_string = self._VENDOR_ID_MAP[self._vendor_id]
+    if vendor_string:
+      vendor += ' (%s)' % vendor_string
+    device = 'DEVICE = 0x%x' % self._device_id
+    if self._device_string:
+      device += ' (%s)' % self._device_string
+    return '%s, %s' % (vendor, device)
+
+  @classmethod
+  def FromDict(cls, attrs):
+    """Constructs a GPUDevice from a dictionary. Requires the
+       following attributes to be present in the dictionary:
+
+         vendor_id
+         device_id
+         vendor_string
+         device_string
+
+       Raises an exception if any attributes are missing.
+    """
+    return cls(attrs['vendor_id'], attrs['device_id'],
+               attrs['vendor_string'], attrs['device_string'])
+
+  @property
+  def vendor_id(self):
+    """The GPU vendor's PCI ID as a number, or 0 if not available.
+
+       Most desktop machines supply this information rather than the
+       vendor and device strings."""
+    return self._vendor_id
+
+  @property
+  def device_id(self):
+    """The GPU device's PCI ID as a number, or 0 if not available.
+
+       Most desktop machines supply this information rather than the
+       vendor and device strings."""
+    return self._device_id
+
+  @property
+  def vendor_string(self):
+    """The GPU vendor's name as a string, or the empty string if not
+       available.
+
+       Most mobile devices supply this information rather than the PCI
+       IDs."""
+    return self._vendor_string
+
+  @property
+  def device_string(self):
+    """The GPU device's name as a string, or the empty string if not
+       available.
+
+       Most mobile devices supply this information rather than the PCI
+       IDs."""
+    return self._device_string
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/gpu_device_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/gpu_device_unittest.py
new file mode 100644
index 0000000..d4ba384
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/gpu_device_unittest.py
@@ -0,0 +1,46 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import unittest
+
+from telemetry.internal.platform import gpu_device
+
+
+class TestGPUDevice(unittest.TestCase):
+
+  def testConstruction(self):
+    device = gpu_device.GPUDevice(1000, 2000, 'test_vendor', 'test_device')
+    self.assertEquals(device.vendor_id, 1000)
+    self.assertEquals(device.device_id, 2000)
+    self.assertEquals(device.vendor_string, 'test_vendor')
+    self.assertEquals(device.device_string, 'test_device')
+
+  def testFromDict(self):
+    dictionary = {'vendor_id': 3000,
+                  'device_id': 4000,
+                  'vendor_string': 'test_vendor_2',
+                  'device_string': 'test_device_2'}
+    device = gpu_device.GPUDevice.FromDict(dictionary)
+    self.assertEquals(device.vendor_id, 3000)
+    self.assertEquals(device.device_id, 4000)
+    self.assertEquals(device.vendor_string, 'test_vendor_2')
+    self.assertEquals(device.device_string, 'test_device_2')
+
+  def testMissingAttrsFromDict(self):
+    data = {
+        'vendor_id': 1,
+        'device_id': 2,
+        'vendor_string': 'a',
+        'device_string': 'b'
+    }
+
+    for k in data:
+      data_copy = data.copy()
+      del data_copy[k]
+      try:
+        gpu_device.GPUDevice.FromDict(data_copy)
+        self.fail('Should raise exception if attribute "%s" is missing' % k)
+      except AssertionError:
+        raise
+      except KeyError:
+        pass
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/gpu_info.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/gpu_info.py
new file mode 100644
index 0000000..b565bb1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/gpu_info.py
@@ -0,0 +1,65 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.internal.platform import gpu_device
+
+
+class GPUInfo(object):
+  """Provides information about the GPUs on the system."""
+
+  def __init__(self, device_array, aux_attributes,
+               feature_status, driver_bug_workarounds):
+    if device_array == None:
+      raise Exception('Missing required "devices" property')
+    if len(device_array) == 0:
+      raise Exception('Missing at least one GPU in device_array')
+
+    self._devices = [gpu_device.GPUDevice.FromDict(d) for d in device_array]
+    self._aux_attributes = aux_attributes
+    self._feature_status = feature_status
+    self._driver_bug_workarounds = driver_bug_workarounds
+
+  @classmethod
+  def FromDict(cls, attrs):
+    """Constructs a GPUInfo from a dictionary of attributes.
+
+    Attributes currently required to be present in the dictionary:
+      devices (array of dictionaries, each of which contains
+          GPUDevice's required attributes)
+    """
+    return cls(attrs['devices'], attrs.get('aux_attributes'),
+               attrs.get('feature_status'),
+               attrs.get('driver_bug_workarounds'))
+
+  @property
+  def devices(self):
+    """An array of GPUDevices. Element 0 is the primary GPU on the system."""
+    return self._devices
+
+  @property
+  def aux_attributes(self):
+    """Returns a dictionary of auxiliary, optional, attributes.
+
+    On the Chrome browser, for example, this dictionary contains:
+      optimus (boolean)
+      amd_switchable (boolean)
+      driver_vendor (string)
+      driver_version (string)
+      driver_date (string)
+      gl_version_string (string)
+      gl_vendor (string)
+      gl_renderer (string)
+      gl_extensions (string)
+    """
+    return self._aux_attributes
+
+  @property
+  def feature_status(self):
+    """Returns an optional dictionary of graphics features and their status."""
+    return self._feature_status
+
+  @property
+  def driver_bug_workarounds(self):
+    """Returns an optional array of driver bug workarounds."""
+    return self._driver_bug_workarounds
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/gpu_info_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/gpu_info_unittest.py
new file mode 100644
index 0000000..a395ddd
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/gpu_info_unittest.py
@@ -0,0 +1,82 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import unittest
+
+from telemetry.internal.platform import gpu_device
+from telemetry.internal.platform import gpu_info
+
+
+class TestGPUInfo(unittest.TestCase):
+
+  def testConstruction(self):
+    data = {
+        'devices': [
+            {'vendor_id': 1000, 'device_id': 2000,
+             'vendor_string': 'a', 'device_string': 'b'},
+            {'vendor_id': 3000, 'device_id': 4000,
+             'vendor_string': 'k', 'device_string': 'l'}
+        ],
+        'aux_attributes': {
+            'optimus': False,
+            'amd_switchable': False,
+            'driver_vendor': 'c',
+            'driver_version': 'd',
+            'driver_date': 'e',
+            'gl_version_string': 'g',
+            'gl_vendor': 'h',
+            'gl_renderer': 'i',
+            'gl_extensions': 'j',
+        }
+    }
+    info = gpu_info.GPUInfo.FromDict(data)
+    self.assertTrue(len(info.devices) == 2)
+    self.assertTrue(isinstance(info.devices[0], gpu_device.GPUDevice))
+    self.assertEquals(info.devices[0].vendor_id, 1000)
+    self.assertEquals(info.devices[0].device_id, 2000)
+    self.assertEquals(info.devices[0].vendor_string, 'a')
+    self.assertEquals(info.devices[0].device_string, 'b')
+    self.assertTrue(isinstance(info.devices[1], gpu_device.GPUDevice))
+    self.assertEquals(info.devices[1].vendor_id, 3000)
+    self.assertEquals(info.devices[1].device_id, 4000)
+    self.assertEquals(info.devices[1].vendor_string, 'k')
+    self.assertEquals(info.devices[1].device_string, 'l')
+    self.assertEquals(info.aux_attributes['optimus'], False)
+    self.assertEquals(info.aux_attributes['amd_switchable'], False)
+    self.assertEquals(info.aux_attributes['driver_vendor'], 'c')
+    self.assertEquals(info.aux_attributes['driver_version'], 'd')
+    self.assertEquals(info.aux_attributes['driver_date'], 'e')
+    self.assertEquals(info.aux_attributes['gl_version_string'], 'g')
+    self.assertEquals(info.aux_attributes['gl_vendor'], 'h')
+    self.assertEquals(info.aux_attributes['gl_renderer'], 'i')
+    self.assertEquals(info.aux_attributes['gl_extensions'], 'j')
+
+  def testMissingAttrsFromDict(self):
+    data = {
+        'devices': [{'vendor_id': 1000, 'device_id': 2000,
+                     'vendor_string': 'a', 'device_string': 'b'}]
+    }
+
+    for k in data:
+      data_copy = data.copy()
+      del data_copy[k]
+      try:
+        gpu_info.GPUInfo.FromDict(data_copy)
+        self.fail('Should raise exception if attribute "%s" is missing' % k)
+      except AssertionError:
+        raise
+      except KeyError:
+        pass
+
+  def testMissingDevices(self):
+    data = {
+        'devices': []
+    }
+
+    try:
+      gpu_info.GPUInfo.FromDict(data)
+      self.fail('Should raise exception if devices array is empty')
+    except AssertionError:
+      raise
+    except Exception:
+      pass
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/ios_device.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/ios_device.py
new file mode 100644
index 0000000..fac0280
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/ios_device.py
@@ -0,0 +1,72 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import logging
+import re
+import subprocess
+
+from telemetry.core import platform
+from telemetry.internal.platform import device
+
+
+IOSSIM_BUILD_DIRECTORIES = [
+    'Debug-iphonesimulator',
+    'Profile-iphonesimulator',
+    'Release-iphonesimulator'
+]
+
+class IOSDevice(device.Device):
+  def __init__(self):
+    super(IOSDevice, self).__init__(name='ios', guid='ios')
+
+  @classmethod
+  def GetAllConnectedDevices(cls, blacklist):
+    return []
+
+
+def _IsIosDeviceAttached():
+  devices = subprocess.check_output('system_profiler SPUSBDataType', shell=True)
+  for line in devices.split('\n'):
+    if line and re.match(r'\s*(iPod|iPhone|iPad):', line):
+      return True
+  return False
+
+def _IsIosSimulatorAvailable(chrome_root):
+  """Determines whether an iOS simulator is present in the local checkout.
+
+  Assumes the iOS simulator (iossim) and Chromium have already been built.
+
+  Returns:
+    True if at least one simulator is found, otherwise False.
+  """
+  for build_dir in IOSSIM_BUILD_DIRECTORIES:
+    iossim_path = os.path.join(
+        chrome_root, 'out', build_dir, 'iossim')
+    chromium_path = os.path.join(
+        chrome_root, 'out', build_dir, 'Chromium.app')
+
+    # If the iOS simulator and Chromium app are present, return True
+    if os.path.exists(iossim_path) and os.path.exists(chromium_path):
+      return True
+
+  return False
+
+def FindAllAvailableDevices(options):
+  """Returns a list of available devices.
+  """
+  # TODO(baxley): Add support for all platforms possible. Probably Linux,
+  # probably not Windows.
+  if platform.GetHostPlatform().GetOSName() != 'mac':
+    return []
+
+  if options.chrome_root is None:
+    logging.warning('--chrome-root is not specified, skip iOS simulator tests.')
+    return []
+
+  if (not _IsIosDeviceAttached() and not
+      _IsIosSimulatorAvailable(options.chrome_root)):
+    return []
+
+  return [IOSDevice()]
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/ios_platform_backend.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/ios_platform_backend.py
new file mode 100644
index 0000000..1221422
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/ios_platform_backend.py
@@ -0,0 +1,62 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+
+from telemetry.internal.platform import posix_platform_backend
+
+#TODO(baxley): Put in real values.
+class IosPlatformBackend(posix_platform_backend.PosixPlatformBackend):
+  def __init__(self):
+    super(IosPlatformBackend, self).__init__()
+
+  def GetOSName(self):
+    # TODO(baxley): Get value from ideviceinfo.
+    logging.warn('Not implemented')
+    return 'ios'
+
+  def GetOSVersionName(self):
+    # TODO(baxley): Get value from ideviceinfo.
+    logging.warn('Not implemented')
+    return '7.1'
+
+  def SetFullPerformanceModeEnabled(self, enabled):
+    logging.warn('Not implemented')
+    return
+
+  def FlushDnsCache(self):
+    logging.warn('Not implemented')
+    return
+
+  def CanMonitorThermalThrottling(self):
+    logging.warn('Not implemented')
+    return False
+
+  def CanMonitorPower(self):
+    logging.warn('Not implemented')
+    return False
+
+  def StartMonitoringPower(self, browser):
+    raise NotImplementedError()
+
+  def StopMonitoringPower(self):
+    raise NotImplementedError()
+
+  def FlushEntireSystemCache(self):
+    raise NotImplementedError()
+
+  def HasBeenThermallyThrottled(self):
+    raise NotImplementedError()
+
+  def StopVideoCapture(self):
+    raise NotImplementedError()
+
+  def IsThermallyThrottled(self):
+    raise NotImplementedError()
+
+  def GetSystemTotalPhysicalMemory(self):
+    raise NotImplementedError()
+
+  def InstallApplication(self, application):
+    raise NotImplementedError()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/linux_based_platform_backend.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/linux_based_platform_backend.py
new file mode 100644
index 0000000..d06b85c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/linux_based_platform_backend.py
@@ -0,0 +1,175 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+try:
+  import resource  # pylint: disable=import-error
+except ImportError:
+  resource = None  # Not available on all platforms
+
+import re
+
+from telemetry.core import exceptions
+from telemetry import decorators
+from telemetry.internal.platform import platform_backend
+
+
+class LinuxBasedPlatformBackend(platform_backend.PlatformBackend):
+
+  """Abstract platform containing functionality shared by all Linux based OSes.
+
+  This includes Android and ChromeOS.
+
+  Subclasses must implement RunCommand, GetFileContents, GetPsOutput, and
+  ParseCStateSample."""
+
+  # Get the commit charge in kB.
+  def GetSystemCommitCharge(self):
+    meminfo_contents = self.GetFileContents('/proc/meminfo')
+    meminfo = self._GetProcFileDict(meminfo_contents)
+    if not meminfo:
+      return None
+    return (self._ConvertToKb(meminfo['MemTotal'])
+            - self._ConvertToKb(meminfo['MemFree'])
+            - self._ConvertToKb(meminfo['Buffers'])
+            - self._ConvertToKb(meminfo['Cached']))
+
+  @decorators.Cache
+  def GetSystemTotalPhysicalMemory(self):
+    meminfo_contents = self.GetFileContents('/proc/meminfo')
+    meminfo = self._GetProcFileDict(meminfo_contents)
+    if not meminfo:
+      return None
+    return self._ConvertToBytes(meminfo['MemTotal'])
+
+  def GetCpuStats(self, pid):
+    results = {}
+    stats = self._GetProcFileForPid(pid, 'stat')
+    if not stats:
+      return results
+    stats = stats.split()
+    utime = float(stats[13])
+    stime = float(stats[14])
+    cpu_process_jiffies = utime + stime
+    clock_ticks = self.GetClockTicks()
+    results.update({'CpuProcessTime': cpu_process_jiffies / clock_ticks})
+    return results
+
+  def GetCpuTimestamp(self):
+    total_jiffies = self._GetProcJiffies()
+    clock_ticks = self.GetClockTicks()
+    return {'TotalTime': total_jiffies / clock_ticks}
+
+  @decorators.Deprecated(
+      2017, 11, 4,
+      'Clients should use tracing and memory-infra in new Telemetry '
+      'benchmarks. See for context: https://crbug.com/632021')
+  def GetMemoryStats(self, pid):
+    status_contents = self._GetProcFileForPid(pid, 'status')
+    stats = self._GetProcFileForPid(pid, 'stat').split()
+    status = self._GetProcFileDict(status_contents)
+    if not status or not stats or 'Z' in status['State']:
+      return {}
+    vm = int(stats[22])
+    vm_peak = (self._ConvertToBytes(status['VmPeak'])
+               if 'VmPeak' in status else vm)
+    wss = int(stats[23]) * resource.getpagesize()
+    wss_peak = (self._ConvertToBytes(status['VmHWM'])
+                if 'VmHWM' in status else wss)
+
+    private_dirty_bytes = 0
+    for line in self._GetProcFileForPid(pid, 'smaps').splitlines():
+      if line.startswith('Private_Dirty:'):
+        private_dirty_bytes += self._ConvertToBytes(line.split(':')[1].strip())
+
+    return {'VM': vm,
+            'VMPeak': vm_peak,
+            'PrivateDirty': private_dirty_bytes,
+            'WorkingSetSize': wss,
+            'WorkingSetSizePeak': wss_peak}
+
+  @decorators.Cache
+  def GetClockTicks(self):
+    """Returns the number of clock ticks per second.
+
+    The proper way is to call os.sysconf('SC_CLK_TCK') but that is not easy to
+    do on Android/CrOS. In practice, nearly all Linux machines have a USER_HZ
+    of 100, so just return that.
+    """
+    return 100
+
+  def GetFileContents(self, filename):
+    raise NotImplementedError()
+
+  def GetPsOutput(self, columns, pid=None):
+    raise NotImplementedError()
+
+  def RunCommand(self, cmd):
+    """Runs the specified command.
+
+    Args:
+        cmd: A list of program arguments or the path string of the program.
+    Returns:
+        A string whose content is the output of the command.
+    """
+    raise NotImplementedError()
+
+  @staticmethod
+  def ParseCStateSample(sample):
+    """Parse a single c-state residency sample.
+
+    Args:
+        sample: A sample of c-state residency times to be parsed. Organized as
+            a dictionary mapping CPU name to a string containing all c-state
+            names, the times in each state, the latency of each state, and the
+            time at which the sample was taken all separated by newlines.
+            Ex: {'cpu0': 'C0\nC1\n5000\n2000\n20\n30\n1406673171'}
+
+    Returns:
+        Dictionary associating a c-state with a time.
+    """
+    raise NotImplementedError()
+
+  def _IsPidAlive(self, pid):
+    assert pid, 'pid is required'
+    return bool(self.GetPsOutput(['pid'], pid) == str(pid))
+
+  def _GetProcFileForPid(self, pid, filename):
+    try:
+      return self.GetFileContents('/proc/%s/%s' % (pid, filename))
+    except IOError:
+      if not self._IsPidAlive(pid):
+        raise exceptions.ProcessGoneException()
+      raise
+
+  def _ConvertToKb(self, value):
+    return int(value.replace('kB', ''))
+
+  def _ConvertToBytes(self, value):
+    return self._ConvertToKb(value) * 1024
+
+  def _GetProcFileDict(self, contents):
+    retval = {}
+    for line in contents.splitlines():
+      key, value = line.split(':')
+      retval[key.strip()] = value.strip()
+    return retval
+
+  def _GetProcJiffies(self):
+    """Parse '/proc/timer_list' output and returns the first jiffies attribute.
+
+    Multi-CPU machines will have multiple 'jiffies:' lines, all of which will be
+    essentially the same.  Return the first one."""
+    jiffies_timer_lines = self.RunCommand(
+        ['grep', 'jiffies', '/proc/timer_list'])
+    if not jiffies_timer_lines:
+      raise Exception('Unable to find jiffies from /proc/timer_list')
+    jiffies_timer_list = jiffies_timer_lines.splitlines()
+    # Each line should look something like 'jiffies: 4315883489'.
+    for line in jiffies_timer_list:
+      match = re.match(r'\s*jiffies\s*:\s*(\d+)', line)
+      if match:
+        value = match.group(1)
+        return float(value)
+    raise Exception('Unable to parse jiffies attribute: %s' %
+                    repr(jiffies_timer_lines))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/linux_based_platform_backend_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/linux_based_platform_backend_unittest.py
new file mode 100644
index 0000000..69ddb48
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/linux_based_platform_backend_unittest.py
@@ -0,0 +1,121 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+import os
+import unittest
+
+from telemetry.core import util
+from telemetry.internal.platform import linux_based_platform_backend
+import mock
+
+
+class TestLinuxBackend(linux_based_platform_backend.LinuxBasedPlatformBackend):
+
+  # pylint: disable=abstract-method
+
+  def __init__(self):
+    super(TestLinuxBackend, self).__init__()
+    self._mock_files = {}
+
+  def SetMockFile(self, filename, output):
+    self._mock_files[filename] = output
+
+  def GetFileContents(self, filename):
+    return self._mock_files[filename]
+
+  def GetClockTicks(self):
+    return 41
+
+
+class LinuxBasedPlatformBackendTest(unittest.TestCase):
+
+  def SetMockFileInBackend(self, backend, real_file, mock_file):
+    with open(os.path.join(util.GetUnittestDataDir(), real_file)) as f:
+      backend.SetMockFile(mock_file, f.read())
+
+  def testGetSystemCommitCharge(self):
+    if not linux_based_platform_backend.resource:
+      logging.warning('Test not supported')
+      return
+
+    backend = TestLinuxBackend()
+    self.SetMockFileInBackend(backend, 'proc_meminfo', '/proc/meminfo')
+    result = backend.GetSystemCommitCharge()
+    # 25252140 == MemTotal - MemFree - Buffers - Cached (in kB)
+    self.assertEquals(result, 25252140)
+
+  def testGetSystemTotalPhysicalMemory(self):
+    if not linux_based_platform_backend.resource:
+      logging.warning('Test not supported')
+      return
+
+    backend = TestLinuxBackend()
+    self.SetMockFileInBackend(backend, 'proc_meminfo', '/proc/meminfo')
+    result = backend.GetSystemTotalPhysicalMemory()
+    # 67479191552 == MemTotal * 1024
+    self.assertEquals(result, 67479191552)
+
+  def testGetCpuStatsBasic(self):
+    if not linux_based_platform_backend.resource:
+      logging.warning('Test not supported')
+      return
+
+    backend = TestLinuxBackend()
+    self.SetMockFileInBackend(backend, 'stat', '/proc/1/stat')
+    result = backend.GetCpuStats(1)
+    self.assertEquals(result, {'CpuProcessTime': 22.0})
+
+  def testGetCpuTimestampBasic(self):
+    if not linux_based_platform_backend.resource:
+      logging.warning('Test not supported')
+      return
+    jiffies_grep_string = """
+    jiffies
+jiffies  a1111
+    .last_jiffies   : 4307239958
+    .next_jiffies   : 4307239968
+    jiffies: 10505463300
+    jiffies: 10505463333
+    """
+    with mock.patch.object(
+        linux_based_platform_backend.LinuxBasedPlatformBackend,
+        'RunCommand', return_value=jiffies_grep_string) as mock_method:
+      backend = linux_based_platform_backend.LinuxBasedPlatformBackend()
+      result = backend.GetCpuTimestamp()
+      self.assertEquals(result, {'TotalTime': 105054633.0})
+    mock_method.assert_call_once_with(
+        ['grep', '-m', '1', 'jiffies:', '/proc/timer_list'])
+
+  def testGetMemoryStatsBasic(self):
+    if not linux_based_platform_backend.resource:
+      logging.warning('Test not supported')
+      return
+
+    backend = TestLinuxBackend()
+    self.SetMockFileInBackend(backend, 'stat', '/proc/1/stat')
+    self.SetMockFileInBackend(backend, 'status', '/proc/1/status')
+    self.SetMockFileInBackend(backend, 'smaps', '/proc/1/smaps')
+    result = backend.GetMemoryStats(1)
+    self.assertEquals(result, {'PrivateDirty': 5324800,
+                               'VM': 1025978368,
+                               'VMPeak': 1050099712,
+                               'WorkingSetSize': 84000768,
+                               'WorkingSetSizePeak': 144547840})
+
+  def testGetMemoryStatsNoHWM(self):
+    if not linux_based_platform_backend.resource:
+      logging.warning('Test not supported')
+      return
+
+    backend = TestLinuxBackend()
+    self.SetMockFileInBackend(backend, 'stat', '/proc/1/stat')
+    self.SetMockFileInBackend(backend, 'status_nohwm', '/proc/1/status')
+    self.SetMockFileInBackend(backend, 'smaps', '/proc/1/smaps')
+    result = backend.GetMemoryStats(1)
+    self.assertEquals(result, {'PrivateDirty': 5324800,
+                               'VM': 1025978368,
+                               'VMPeak': 1025978368,
+                               'WorkingSetSize': 84000768,
+                               'WorkingSetSizePeak': 84000768})
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/linux_platform_backend.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/linux_platform_backend.py
new file mode 100644
index 0000000..5532d59
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/linux_platform_backend.py
@@ -0,0 +1,162 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+import os
+import platform
+import subprocess
+import sys
+
+from py_utils import cloud_storage  # pylint: disable=import-error
+
+from telemetry.internal.util import binary_manager
+from telemetry.core import os_version
+from telemetry.core import util
+from telemetry import decorators
+from telemetry.internal.platform import linux_based_platform_backend
+from telemetry.internal.platform import posix_platform_backend
+from telemetry.internal.platform.power_monitor import msr_power_monitor
+
+
+_POSSIBLE_PERFHOST_APPLICATIONS = [
+  'perfhost_precise',
+  'perfhost_trusty',
+]
+
+
+class LinuxPlatformBackend(
+    posix_platform_backend.PosixPlatformBackend,
+    linux_based_platform_backend.LinuxBasedPlatformBackend):
+  def __init__(self):
+    super(LinuxPlatformBackend, self).__init__()
+    self._power_monitor = msr_power_monitor.MsrPowerMonitorLinux(self)
+
+  @classmethod
+  def IsPlatformBackendForHost(cls):
+    return sys.platform.startswith('linux') and not util.IsRunningOnCrosDevice()
+
+  def IsThermallyThrottled(self):
+    raise NotImplementedError()
+
+  def HasBeenThermallyThrottled(self):
+    raise NotImplementedError()
+
+  @decorators.Cache
+  def GetArchName(self):
+    return platform.machine()
+
+  def GetOSName(self):
+    return 'linux'
+
+  @decorators.Cache
+  def GetOSVersionName(self):
+    if not os.path.exists('/etc/lsb-release'):
+      raise NotImplementedError('Unknown Linux OS version')
+
+    codename = None
+    version = None
+    for line in self.GetFileContents('/etc/lsb-release').splitlines():
+      key, _, value = line.partition('=')
+      if key == 'DISTRIB_CODENAME':
+        codename = value.strip()
+      elif key == 'DISTRIB_RELEASE':
+        try:
+          version = float(value)
+        except ValueError:
+          version = 0
+      if codename and version:
+        break
+    return os_version.OSVersion(codename, version)
+
+  def CanFlushIndividualFilesFromSystemCache(self):
+    return True
+
+  def SupportFlushEntireSystemCache(self):
+    return self.HasRootAccess()
+
+  def FlushEntireSystemCache(self):
+    p = subprocess.Popen(['/sbin/sysctl', '-w', 'vm.drop_caches=3'])
+    p.wait()
+    assert p.returncode == 0, 'Failed to flush system cache'
+
+  def CanLaunchApplication(self, application):
+    if application == 'ipfw' and not self._IsIpfwKernelModuleInstalled():
+      return False
+    return super(LinuxPlatformBackend, self).CanLaunchApplication(application)
+
+  def InstallApplication(self, application):
+    if application == 'ipfw':
+      self._InstallIpfw()
+    elif application == 'avconv':
+      self._InstallBinary(application)
+    elif application in _POSSIBLE_PERFHOST_APPLICATIONS:
+      self._InstallBinary(application)
+    else:
+      raise NotImplementedError(
+          'Please teach Telemetry how to install ' + application)
+
+  def CanMonitorPower(self):
+    return self._power_monitor.CanMonitorPower()
+
+  def CanMeasurePerApplicationPower(self):
+    return self._power_monitor.CanMeasurePerApplicationPower()
+
+  def StartMonitoringPower(self, browser):
+    self._power_monitor.StartMonitoringPower(browser)
+
+  def StopMonitoringPower(self):
+    return self._power_monitor.StopMonitoringPower()
+
+  def ReadMsr(self, msr_number, start=0, length=64):
+    cmd = ['rdmsr', '-d', str(msr_number)]
+    (out, err) = subprocess.Popen(cmd,
+                                  stdout=subprocess.PIPE,
+                                  stderr=subprocess.PIPE).communicate()
+    if err:
+      raise OSError(err)
+    try:
+      result = int(out)
+    except ValueError:
+      raise OSError('Cannot interpret rdmsr output: %s' % out)
+    return result >> start & ((1 << length) - 1)
+
+  def _IsIpfwKernelModuleInstalled(self):
+    return 'ipfw_mod' in subprocess.Popen(
+        ['lsmod'], stdout=subprocess.PIPE).communicate()[0]
+
+  def _InstallIpfw(self):
+    ipfw_bin = binary_manager.FindPath(
+        'ipfw', self.GetArchName(), self.GetOSName())
+    ipfw_mod = binary_manager.FindPath(
+        'ipfw_mod.ko', self.GetArchName(), self.GetOSName())
+
+    try:
+      changed = cloud_storage.GetIfChanged(
+          ipfw_bin, cloud_storage.INTERNAL_BUCKET)
+      changed |= cloud_storage.GetIfChanged(
+          ipfw_mod, cloud_storage.INTERNAL_BUCKET)
+    except cloud_storage.CloudStorageError, e:
+      logging.error(str(e))
+      logging.error('You may proceed by manually building and installing'
+                    'dummynet for your kernel. See: '
+                    'http://info.iet.unipi.it/~luigi/dummynet/')
+      sys.exit(1)
+
+    if changed or not self.CanLaunchApplication('ipfw'):
+      if not self._IsIpfwKernelModuleInstalled():
+        subprocess.check_call(['/usr/bin/sudo', 'insmod', ipfw_mod])
+      os.chmod(ipfw_bin, 0755)
+      subprocess.check_call(
+          ['/usr/bin/sudo', 'cp', ipfw_bin, '/usr/local/sbin'])
+
+    assert self.CanLaunchApplication('ipfw'), 'Failed to install ipfw. ' \
+        'ipfw provided binaries are not supported for linux kernel < 3.13. ' \
+        'You may proceed by manually building and installing dummynet for ' \
+        'your kernel. See: http://info.iet.unipi.it/~luigi/dummynet/'
+
+  def _InstallBinary(self, bin_name):
+    bin_path = binary_manager.FetchPath(
+        bin_name, self.GetArchName(), self.GetOSName())
+    os.environ['PATH'] += os.pathsep + os.path.dirname(bin_path)
+    assert self.CanLaunchApplication(bin_name), 'Failed to install ' + bin_name
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/linux_platform_backend_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/linux_platform_backend_unittest.py
new file mode 100644
index 0000000..052d59f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/linux_platform_backend_unittest.py
@@ -0,0 +1,41 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import unittest
+
+from telemetry import decorators
+from telemetry.core import util
+from telemetry.internal.platform import linux_platform_backend
+import mock
+
+
+class LinuxPlatformBackendTest(unittest.TestCase):
+  @decorators.Enabled('linux')
+  def testGetOSVersionNameSaucy(self):
+    path = os.path.join(util.GetUnittestDataDir(), 'ubuntu-saucy-lsb-release')
+    with open(path) as f:
+      unbuntu_saucy_lsb_release_content = f.read()
+
+    with mock.patch.object(
+        linux_platform_backend.LinuxPlatformBackend, 'GetFileContents',
+        return_value=unbuntu_saucy_lsb_release_content) as mock_method:
+      backend = linux_platform_backend.LinuxPlatformBackend()
+      self.assertEqual(backend.GetOSVersionName(), 'saucy')
+      mock_method.assert_called_once_with('/etc/lsb-release')
+
+  @decorators.Enabled('linux')
+  def testGetOSVersionNameArch(self):
+    path = os.path.join(util.GetUnittestDataDir(), 'arch-lsb-release')
+    with open(path) as f:
+      arch_lsb_release_content = f.read()
+
+    with mock.patch.object(
+        linux_platform_backend.LinuxPlatformBackend, 'GetFileContents',
+        return_value=arch_lsb_release_content) as mock_method:
+      backend = linux_platform_backend.LinuxPlatformBackend()
+      # a distribution may not have a codename or a release number. We just
+      # check that GetOSVersionName doesn't raise an exception
+      backend.GetOSVersionName()
+      mock_method.assert_called_once_with('/etc/lsb-release')
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/mac_platform_backend.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/mac_platform_backend.py
new file mode 100644
index 0000000..168986a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/mac_platform_backend.py
@@ -0,0 +1,194 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import ctypes
+import os
+import platform
+import subprocess
+import sys
+import time
+
+from telemetry.core import os_version as os_version_module
+from telemetry import decorators
+from telemetry.internal.platform import posix_platform_backend
+from telemetry.internal.platform.power_monitor import powermetrics_power_monitor
+from telemetry.util import process_statistic_timeline_data
+
+try:
+  import resource  # pylint: disable=import-error
+except ImportError:
+  resource = None  # Not available on all platforms
+
+
+
+class MacPlatformBackend(posix_platform_backend.PosixPlatformBackend):
+  def __init__(self):
+    super(MacPlatformBackend, self).__init__()
+    self.libproc = None
+    self._power_monitor = powermetrics_power_monitor.PowerMetricsPowerMonitor(
+        self)
+
+  def GetSystemLog(self):
+    # Since the log file can be very large, only show the last 200 lines.
+    return subprocess.check_output(
+        ['tail', '-n', '200', '/var/log/system.log'])
+
+  @classmethod
+  def IsPlatformBackendForHost(cls):
+    return sys.platform == 'darwin'
+
+  def IsThermallyThrottled(self):
+    raise NotImplementedError()
+
+  def HasBeenThermallyThrottled(self):
+    raise NotImplementedError()
+
+  def _GetIdleWakeupCount(self, pid):
+    top_output = self._GetTopOutput(pid, ['idlew'])
+
+    # Sometimes top won't return anything here, just ignore such cases -
+    # crbug.com/354812 .
+    if top_output[-2] != 'IDLEW':
+      return process_statistic_timeline_data.IdleWakeupTimelineData(pid, 0)
+    # Numbers reported by top may have a '+' appended.
+    wakeup_count = int(top_output[-1].strip('+ '))
+    return process_statistic_timeline_data.IdleWakeupTimelineData(pid,
+        wakeup_count)
+
+  def GetCpuStats(self, pid):
+    """Returns a dict of cpu statistics for the process represented by |pid|."""
+    class ProcTaskInfo(ctypes.Structure):
+      """Struct for proc_pidinfo() call."""
+      _fields_ = [("pti_virtual_size", ctypes.c_uint64),
+                  ("pti_resident_size", ctypes.c_uint64),
+                  ("pti_total_user", ctypes.c_uint64),
+                  ("pti_total_system", ctypes.c_uint64),
+                  ("pti_threads_user", ctypes.c_uint64),
+                  ("pti_threads_system", ctypes.c_uint64),
+                  ("pti_policy", ctypes.c_int32),
+                  ("pti_faults", ctypes.c_int32),
+                  ("pti_pageins", ctypes.c_int32),
+                  ("pti_cow_faults", ctypes.c_int32),
+                  ("pti_messages_sent", ctypes.c_int32),
+                  ("pti_messages_received", ctypes.c_int32),
+                  ("pti_syscalls_mach", ctypes.c_int32),
+                  ("pti_syscalls_unix", ctypes.c_int32),
+                  ("pti_csw", ctypes.c_int32),
+                  ("pti_threadnum", ctypes.c_int32),
+                  ("pti_numrunning", ctypes.c_int32),
+                  ("pti_priority", ctypes.c_int32)]
+      PROC_PIDTASKINFO = 4
+      def __init__(self):
+        self.size = ctypes.sizeof(self)
+        super(ProcTaskInfo, self).__init__()  # pylint: disable=bad-super-call
+
+    proc_info = ProcTaskInfo()
+    if not self.libproc:
+      self.libproc = ctypes.CDLL(ctypes.util.find_library('libproc'))
+    self.libproc.proc_pidinfo(pid, proc_info.PROC_PIDTASKINFO, 0,
+                              ctypes.byref(proc_info), proc_info.size)
+
+    # Convert nanoseconds to seconds.
+    cpu_time = (proc_info.pti_total_user / 1000000000.0 +
+                proc_info.pti_total_system / 1000000000.0)
+    results = {'CpuProcessTime': cpu_time,
+               'ContextSwitches': proc_info.pti_csw}
+
+    # top only reports idle wakeup count starting from OS X 10.9.
+    if self.GetOSVersionName() >= os_version_module.MAVERICKS:
+      results.update({'IdleWakeupCount': self._GetIdleWakeupCount(pid)})
+    return results
+
+  def GetCpuTimestamp(self):
+    """Return current timestamp in seconds."""
+    return {'TotalTime': time.time()}
+
+  def GetSystemCommitCharge(self):
+    vm_stat = self.RunCommand(['vm_stat'])
+    for stat in vm_stat.splitlines():
+      key, value = stat.split(':')
+      if key == 'Pages active':
+        pages_active = int(value.strip()[:-1])  # Strip trailing '.'
+        return pages_active * resource.getpagesize() / 1024
+    return 0
+
+  @decorators.Cache
+  def GetSystemTotalPhysicalMemory(self):
+    return int(self.RunCommand(['sysctl', '-n', 'hw.memsize']))
+
+  def PurgeUnpinnedMemory(self):
+    # TODO(pliard): Implement this.
+    pass
+
+  @decorators.Deprecated(
+      2017, 11, 4,
+      'Clients should use tracing and memory-infra in new Telemetry '
+      'benchmarks. See for context: https://crbug.com/632021')
+  def GetMemoryStats(self, pid):
+    rss_vsz = self.GetPsOutput(['rss', 'vsz'], pid)
+    if rss_vsz:
+      rss, vsz = rss_vsz[0].split()
+      return {'VM': 1024 * int(vsz),
+              'WorkingSetSize': 1024 * int(rss)}
+    return {}
+
+  @decorators.Cache
+  def GetArchName(self):
+    return platform.machine()
+
+  def GetOSName(self):
+    return 'mac'
+
+  @decorators.Cache
+  def GetOSVersionName(self):
+    os_version = os.uname()[2]
+
+    if os_version.startswith('9.'):
+      return os_version_module.LEOPARD
+    if os_version.startswith('10.'):
+      return os_version_module.SNOWLEOPARD
+    if os_version.startswith('11.'):
+      return os_version_module.LION
+    if os_version.startswith('12.'):
+      return os_version_module.MOUNTAINLION
+    if os_version.startswith('13.'):
+      return os_version_module.MAVERICKS
+    if os_version.startswith('14.'):
+      return os_version_module.YOSEMITE
+    if os_version.startswith('15.'):
+      return os_version_module.ELCAPITAN
+    if os_version.startswith('16.'):
+      return os_version_module.SIERRA
+
+    raise NotImplementedError('Unknown mac version %s.' % os_version)
+
+  def CanTakeScreenshot(self):
+    return True
+
+  def TakeScreenshot(self, file_path):
+    return subprocess.call(['screencapture', file_path])
+
+  def CanFlushIndividualFilesFromSystemCache(self):
+    return False
+
+  def SupportFlushEntireSystemCache(self):
+    return self.HasRootAccess()
+
+  def FlushEntireSystemCache(self):
+    mavericks_or_later = self.GetOSVersionName() >= os_version_module.MAVERICKS
+    p = self.LaunchApplication('purge', elevate_privilege=mavericks_or_later)
+    p.communicate()
+    assert p.returncode == 0, 'Failed to flush system cache'
+
+  def CanMonitorPower(self):
+    return self._power_monitor.CanMonitorPower()
+
+  def CanMeasurePerApplicationPower(self):
+    return self._power_monitor.CanMeasurePerApplicationPower()
+
+  def StartMonitoringPower(self, browser):
+    self._power_monitor.StartMonitoringPower(browser)
+
+  def StopMonitoringPower(self):
+    return self._power_monitor.StopMonitoringPower()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/mac_platform_backend_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/mac_platform_backend_unittest.py
new file mode 100644
index 0000000..c3b67bc
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/mac_platform_backend_unittest.py
@@ -0,0 +1,40 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import unittest
+
+from telemetry.core import platform as platform_module
+from telemetry.core import os_version
+from telemetry import decorators
+
+
+class MacPlatformBackendTest(unittest.TestCase):
+  def testVersionCamparison(self):
+    self.assertGreater(os_version.YOSEMITE, os_version.MAVERICKS)
+    self.assertGreater(os_version.MAVERICKS, os_version.SNOWLEOPARD)
+    self.assertGreater(os_version.LION, os_version.LEOPARD)
+    self.assertEqual(os_version.YOSEMITE, 'yosemite')
+    self.assertEqual(os_version.MAVERICKS, 'mavericks')
+    self.assertEqual('%s2' % os_version.MAVERICKS, 'mavericks2')
+    self.assertEqual(''.join([os_version.MAVERICKS, '2']),
+                     'mavericks2')
+    self.assertEqual(os_version.LION.upper(), 'LION')
+
+  @decorators.Enabled('mac')
+  def testGetCPUStats(self):
+    platform = platform_module.GetHostPlatform()
+
+    backend = platform._platform_backend # pylint: disable=protected-access
+
+    cpu_stats = backend.GetCpuStats(os.getpid())
+    self.assertGreater(cpu_stats['CpuProcessTime'], 0)
+    self.assertTrue(cpu_stats.has_key('ContextSwitches'))
+    if backend.GetOSVersionName() >= os_version.MAVERICKS:
+      self.assertTrue(cpu_stats.has_key('IdleWakeupCount'))
+
+  @decorators.Enabled('mac')
+  def testGetSystemLogSmoke(self):
+    platform = platform_module.GetHostPlatform()
+    self.assertTrue(platform.GetSystemLog())
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/msr_server_win.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/msr_server_win.py
new file mode 100644
index 0000000..087407b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/msr_server_win.py
@@ -0,0 +1,114 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""A server that serves MSR values over TCP. Takes a port as its sole parameter.
+
+The reference client for this server is msr_power_monitor.MsrPowerMonitor.
+
+Must be run as Administrator. We use TCP instead of named pipes or another IPC
+to avoid dealing with the pipe security mechanisms. We take the port as a
+parameter instead of choosing one, because it's hard to communicate the port
+number across integrity levels.
+
+Requires WinRing0 to be installed in the Python directory.
+msr_power_monitor.MsrPowerMonitor does this if needed.
+"""
+
+import argparse
+import ctypes
+import os
+import SocketServer
+import struct
+import sys
+try:
+  import win32api  # pylint: disable=import-error
+  import win32file  # pylint: disable=import-error
+except ImportError:
+  win32api = None
+  win32file = None
+
+
+WINRING0_STATUS_MESSAGES = (
+    'No error',
+    'Unsupported platform',
+    'Driver not loaded. You may need to run as Administrator',
+    'Driver not found',
+    'Driver unloaded by other process',
+    'Driver not loaded because of executing on Network Drive',
+    'Unknown error',
+)
+
+
+# The DLL initialization is global, so put it in a global variable.
+_winring0 = None
+
+
+class WinRing0Error(OSError):
+  pass
+
+
+def _WinRing0Path():
+  python_is_64_bit = sys.maxsize > 2 ** 32
+  dll_file_name = 'WinRing0x64.dll' if python_is_64_bit else 'WinRing0.dll'
+  return os.path.join(os.path.dirname(sys.executable), dll_file_name)
+
+
+def _Initialize():
+  global _winring0
+  if not _winring0:
+    winring0 = ctypes.WinDLL(_WinRing0Path())
+    if not winring0.InitializeOls():
+      winring0_status = winring0.GetDllStatus()
+      raise WinRing0Error(winring0_status,
+                          'Unable to initialize WinRing0: %s' %
+                          WINRING0_STATUS_MESSAGES[winring0_status])
+    _winring0 = winring0
+
+
+def _Deinitialize():
+  global _winring0
+  if _winring0:
+    _winring0.DeinitializeOls()
+    _winring0 = None
+
+
+def _ReadMsr(msr_number):
+  low = ctypes.c_uint()
+  high = ctypes.c_uint()
+  _winring0.Rdmsr(ctypes.c_uint(msr_number),
+                  ctypes.byref(low), ctypes.byref(high))
+  return high.value << 32 | low.value
+
+
+class MsrRequestHandler(SocketServer.StreamRequestHandler):
+  def handle(self):
+    msr_number = struct.unpack('I', self.rfile.read(4))[0]
+    self.wfile.write(struct.pack('Q', _ReadMsr(msr_number)))
+
+
+def main():
+  parser = argparse.ArgumentParser()
+  parser.add_argument('pipe_name', type=str)
+  args = parser.parse_args()
+
+  _Initialize()
+  try:
+    SocketServer.TCPServer.allow_reuse_address = True
+    server_address = ('127.0.0.1', 0)
+    server = SocketServer.TCPServer(server_address, MsrRequestHandler)
+    handle = win32file.CreateFile(args.pipe_name,
+                                  win32file.GENERIC_WRITE,
+                                  0, None,
+                                  win32file.OPEN_EXISTING,
+                                  0, None)
+    _, port = server.server_address
+    win32file.WriteFile(handle, str(port))
+    win32api.CloseHandle(handle)
+    server.serve_forever()
+  finally:
+    _Deinitialize()
+
+
+if __name__ == '__main__':
+  main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/network_controller_backend.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/network_controller_backend.py
new file mode 100644
index 0000000..052be07
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/network_controller_backend.py
@@ -0,0 +1,280 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+import os
+import shutil
+import tempfile
+
+from telemetry.internal.util import wpr_server
+from telemetry.internal.util import ts_proxy_server
+from telemetry.util import wpr_modes
+
+import certutils
+import platformsettings
+
+
+class ArchiveDoesNotExistError(Exception):
+  """Raised when the archive path does not exist for replay mode."""
+  pass
+
+
+class ReplayAndBrowserPortsError(Exception):
+  """Raised an existing browser would get different remote replay ports."""
+  pass
+
+
+class NetworkControllerBackend(object):
+  """Control network settings and servers to simulate the Web.
+
+  Network changes include forwarding device ports to host platform ports.
+  Web Page Replay is used to record and replay HTTP/HTTPS responses.
+  """
+
+  def __init__(self, platform_backend):
+    self._platform_backend = platform_backend
+    self._wpr_mode = None
+    self._extra_wpr_args = None
+    self._archive_path = None
+    self._make_javascript_deterministic = None
+    self._forwarder = None
+    self._wpr_ca_cert_path = None
+    self._wpr_server = None
+    self._ts_proxy_server = None
+    self._port_pair = None
+    self._use_live_traffic = None
+
+  def InitializeIfNeeded(self, use_live_traffic):
+    """
+    This may, e.g., install test certificates and perform any needed setup
+    on the target platform.
+
+    After network interactions are over, clients should call the Close method.
+    """
+    if self._use_live_traffic is None:
+      self._use_live_traffic = use_live_traffic
+    assert self._use_live_traffic == use_live_traffic, (
+        'inconsistent state of use_live_traffic')
+    assert bool(self._ts_proxy_server) == bool(self._forwarder)
+    if self._ts_proxy_server:
+      return
+    local_port = self._StartTsProxyServer(self._use_live_traffic)
+    self._forwarder = self._platform_backend.forwarder_factory.Create(
+        self._platform_backend.GetPortPairForForwarding(local_port))
+
+  @property
+  def is_open(self):
+    return self._wpr_mode is not None
+
+  @property
+  def is_initialized(self):
+    return self._forwarder is not None
+
+  @property
+  def host_ip(self):
+    return self._platform_backend.forwarder_factory.host_ip
+
+  @property
+  def wpr_device_ports(self):
+    try:
+      return self._forwarder.port_pairs.remote_ports
+    except AttributeError:
+      return None
+
+  @property
+  def is_test_ca_installed(self):
+    return self._wpr_ca_cert_path is not None
+
+  def Open(self, wpr_mode, extra_wpr_args):
+    """Configure and prepare target platform for network control.
+
+    This may, e.g., install test certificates and perform any needed setup
+    on the target platform.
+
+    After network interactions are over, clients should call the Close method.
+
+    Args:
+      wpr_mode: a mode for web page replay; available modes are
+          wpr_modes.WPR_OFF, wpr_modes.APPEND, wpr_modes.WPR_REPLAY, or
+          wpr_modes.WPR_RECORD.
+      extra_wpr_args: an list of extra arguments for web page replay.
+    """
+    assert not self.is_open, 'Network controller is already open'
+    self._wpr_mode = wpr_mode
+    self._extra_wpr_args = extra_wpr_args
+    self._InstallTestCa()
+
+  def Close(self):
+    """Undo changes in the target platform used for network control.
+
+    Implicitly stops replay if currently active.
+    """
+    self.StopReplay()
+    self._StopForwarder()
+    self._StopTsProxyServer()
+    self._RemoveTestCa()
+    self._make_javascript_deterministic = None
+    self._archive_path = None
+    self._extra_wpr_args = None
+    self._wpr_mode = None
+
+  def _InstallTestCa(self):
+    if not self._platform_backend.supports_test_ca:
+      return
+    assert not self.is_test_ca_installed, 'Test CA is already installed'
+    if certutils.openssl_import_error:
+      logging.warning(
+          'The OpenSSL module is unavailable. '
+          'Browsers may fall back to ignoring certificate errors.')
+      return
+    if not platformsettings.HasSniSupport():
+      logging.warning(
+          'Web Page Replay requires SNI support (pyOpenSSL 0.13 or greater) '
+          'to generate certificates from a test CA. '
+          'Browsers may fall back to ignoring certificate errors.')
+      return
+    self._wpr_ca_cert_path = os.path.join(tempfile.mkdtemp(), 'testca.pem')
+    try:
+      certutils.write_dummy_ca_cert(*certutils.generate_dummy_ca_cert(),
+                                    cert_path=self._wpr_ca_cert_path)
+      self._platform_backend.InstallTestCa(self._wpr_ca_cert_path)
+      logging.info('Test certificate authority installed on target platform.')
+    except Exception:
+      logging.exception(
+          'Failed to install test certificate authority on target platform. '
+          'Browsers may fall back to ignoring certificate errors.')
+      self._RemoveTestCa()
+
+  def _RemoveTestCa(self):
+    if not self.is_test_ca_installed:
+      return
+    try:
+      self._platform_backend.RemoveTestCa()
+    except Exception:
+      # Best effort cleanup - show the error and continue.
+      logging.exception(
+          'Error trying to remove certificate authority from target platform.')
+    try:
+      shutil.rmtree(os.path.dirname(self._wpr_ca_cert_path), ignore_errors=True)
+    finally:
+      self._wpr_ca_cert_path = None
+
+  def StartReplay(self, archive_path, make_javascript_deterministic=False):
+    """Start web page replay from a given replay archive.
+
+    Starts as needed, and reuses if possible, the replay server on the host and
+    a forwarder from the host to the target platform.
+
+    Implementation details
+    ----------------------
+
+    The local host is where Telemetry is run. The remote is host where
+    the target application is run. The local and remote hosts may be
+    the same (e.g., testing a desktop browser) or different (e.g., testing
+    an android browser).
+
+    A replay server is started on the local host using the local ports, while
+    a forwarder ties the local to the remote ports.
+
+    Both local and remote ports may be zero. In that case they are determined
+    by the replay server and the forwarder respectively. Setting dns to None
+    disables DNS traffic.
+
+    Args:
+      archive_path: a path to a specific WPR archive.
+      make_javascript_deterministic: True if replay should inject a script
+          to make JavaScript behave deterministically (e.g., override Date()).
+    """
+    assert self.is_open, 'Network controller is not open'
+    if self._wpr_mode == wpr_modes.WPR_OFF:
+      return
+    if not archive_path:
+      # TODO(slamm, tonyg): Ideally, replay mode should be stopped when there is
+      # no archive path. However, if the replay server already started, and
+      # a file URL is tested with the
+      # telemetry.core.local_server.LocalServerController, then the
+      # replay server forwards requests to it. (Chrome is configured to use
+      # fixed ports fo all HTTP/HTTPS requests.)
+      return
+    if (self._wpr_mode == wpr_modes.WPR_REPLAY and
+        not os.path.exists(archive_path)):
+      raise ArchiveDoesNotExistError(
+          'Archive path does not exist: %s' % archive_path)
+    if (self._wpr_server is not None and
+        self._archive_path == archive_path and
+        self._make_javascript_deterministic == make_javascript_deterministic):
+      return  # We may reuse the existing replay server.
+
+    self._archive_path = archive_path
+    self._make_javascript_deterministic = make_javascript_deterministic
+    local_ports = self._StartReplayServer()
+    self._ts_proxy_server.UpdateOutboundPorts(
+        http_port=local_ports.http, https_port=local_ports.https)
+
+  def _StopForwarder(self):
+    if self._forwarder:
+      self._forwarder.Close()
+      self._forwarder = None
+
+  def StopReplay(self):
+    """Stop web page replay.
+
+    Stops both the replay server and the forwarder if currently active.
+    """
+    self._StopReplayServer()
+
+  def _StartReplayServer(self):
+    """Start the replay server and return the started local_ports."""
+    self._StopReplayServer()  # In case it was already running.
+    self._wpr_server = wpr_server.ReplayServer(
+        self._archive_path,
+        self.host_ip,
+        http_port=0,
+        https_port=0,
+        dns_port=None,
+        replay_options=self._ReplayCommandLineArgs())
+    return self._wpr_server.StartServer()
+
+  def _StopReplayServer(self):
+    """Stop the replay server only."""
+    if self._wpr_server:
+      self._wpr_server.StopServer()
+      self._wpr_server = None
+
+  def _StopTsProxyServer(self):
+    """Stop the replay server only."""
+    if self._ts_proxy_server:
+      self._ts_proxy_server.StopServer()
+      self._ts_proxy_server = None
+
+  def _ReplayCommandLineArgs(self):
+    wpr_args = list(self._extra_wpr_args)
+    if self._wpr_mode == wpr_modes.WPR_APPEND:
+      wpr_args.append('--append')
+    elif self._wpr_mode == wpr_modes.WPR_RECORD:
+      wpr_args.append('--record')
+    if not self._make_javascript_deterministic:
+      wpr_args.append('--inject_scripts=')
+    if self._wpr_ca_cert_path:
+      wpr_args.extend([
+          '--should_generate_certs',
+          '--https_root_ca_cert_path=%s' % self._wpr_ca_cert_path])
+    return wpr_args
+
+  def _StartTsProxyServer(self, use_live_traffic):
+    assert not self._ts_proxy_server, 'ts_proxy_server is already started'
+    host_ip = None
+    if not use_live_traffic:
+      host_ip = self.host_ip
+    self._ts_proxy_server = ts_proxy_server.TsProxyServer(host_ip=host_ip)
+    self._ts_proxy_server.StartServer()
+    return self._ts_proxy_server.port
+
+  @property
+  def forwarder(self):
+    return self._forwarder
+
+  @property
+  def ts_proxy_server(self):
+    return self._ts_proxy_server
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/platform_backend.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/platform_backend.py
new file mode 100644
index 0000000..9ca19a4
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/platform_backend.py
@@ -0,0 +1,308 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import weakref
+
+from battor import battor_wrapper
+from telemetry.internal import forwarders
+from telemetry.internal.forwarders import do_nothing_forwarder
+from telemetry.internal.platform import network_controller_backend
+from telemetry.internal.platform import tracing_controller_backend
+
+
+# pylint: disable=unused-argument
+
+class PlatformBackend(object):
+
+  def __init__(self, device=None):
+    """ Initalize an instance of PlatformBackend from a device optionally.
+      Call sites need to use SupportsDevice before intialization to check
+      whether this platform backend supports the device.
+      If device is None, this constructor returns the host platform backend
+      which telemetry is running on.
+
+      Args:
+        device: an instance of telemetry.core.platform.device.Device.
+    """
+    if device and not self.SupportsDevice(device):
+      raise ValueError('Unsupported device: %s' % device.name)
+    self._platform = None
+    self._running_browser_backends = weakref.WeakSet()
+    self._network_controller_backend = None
+    self._tracing_controller_backend = None
+    self._forwarder_factory = None
+
+  def InitPlatformBackend(self):
+    self._network_controller_backend = (
+        network_controller_backend.NetworkControllerBackend(self))
+    self._tracing_controller_backend = (
+        tracing_controller_backend.TracingControllerBackend(self))
+
+  @classmethod
+  def IsPlatformBackendForHost(cls):
+    """ Returns whether this platform backend is the platform backend to be used
+    for the host device which telemetry is running on. """
+    return False
+
+  @classmethod
+  def SupportsDevice(cls, device):
+    """ Returns whether this platform backend supports intialization from the
+    device. """
+    return False
+
+  @classmethod
+  def CreatePlatformForDevice(cls, device, finder_options):
+    raise NotImplementedError
+
+  def SetPlatform(self, platform):
+    assert self._platform == None
+    self._platform = platform
+
+  @property
+  def platform(self):
+    return self._platform
+
+  @property
+  def is_host_platform(self):
+    return self._platform.is_host_platform
+
+  @property
+  def running_browser_backends(self):
+    return list(self._running_browser_backends)
+
+  @property
+  def network_controller_backend(self):
+    return self._network_controller_backend
+
+  @property
+  def tracing_controller_backend(self):
+    return self._tracing_controller_backend
+
+  @property
+  def forwarder_factory(self):
+    if not self._forwarder_factory:
+      self._forwarder_factory = do_nothing_forwarder.DoNothingForwarderFactory()
+    return self._forwarder_factory
+
+  def GetPortPairForForwarding(self, local_port):
+    return forwarders.PortPair(local_port=local_port, remote_port=local_port)
+
+  def GetRemotePort(self, port):
+    return port
+
+  def GetSystemLog(self):
+    return None
+
+  def DidCreateBrowser(self, browser, browser_backend):
+    browser_options = browser_backend.browser_options
+    self.SetFullPerformanceModeEnabled(browser_options.full_performance_mode)
+
+  def DidStartBrowser(self, browser, browser_backend):
+    assert browser not in self._running_browser_backends
+    self._running_browser_backends.add(browser_backend)
+
+  def WillCloseBrowser(self, browser, browser_backend):
+    is_last_browser = len(self._running_browser_backends) <= 1
+    if is_last_browser:
+      self.SetFullPerformanceModeEnabled(False)
+
+    self._running_browser_backends.discard(browser_backend)
+
+  def IsDisplayTracingSupported(self):
+    return False
+
+  def StartDisplayTracing(self):
+    """Start gathering a trace with frame timestamps close to physical
+    display."""
+    raise NotImplementedError()
+
+  def StopDisplayTracing(self):
+    """Stop gathering a trace with frame timestamps close to physical display.
+
+    Returns a raw tracing events that contains the timestamps of physical
+    display.
+    """
+    raise NotImplementedError()
+
+  def SetFullPerformanceModeEnabled(self, enabled):
+    pass
+
+  def CanMonitorThermalThrottling(self):
+    return False
+
+  def IsThermallyThrottled(self):
+    raise NotImplementedError()
+
+  def HasBeenThermallyThrottled(self):
+    raise NotImplementedError()
+
+  def GetSystemCommitCharge(self):
+    raise NotImplementedError()
+
+  def GetSystemTotalPhysicalMemory(self):
+    raise NotImplementedError()
+
+  def GetCpuStats(self, pid):
+    return {}
+
+  def GetCpuTimestamp(self):
+    return {}
+
+  def PurgeUnpinnedMemory(self):
+    pass
+
+  def GetMemoryStats(self, pid):
+    return {}
+
+  def GetChildPids(self, pid):
+    raise NotImplementedError()
+
+  def GetCommandLine(self, pid):
+    raise NotImplementedError()
+
+  def GetDeviceTypeName(self):
+    raise NotImplementedError()
+
+  def GetArchName(self):
+    raise NotImplementedError()
+
+  def GetOSName(self):
+    raise NotImplementedError()
+
+  def GetOSVersionName(self):
+    raise NotImplementedError()
+
+  def CanFlushIndividualFilesFromSystemCache(self):
+    raise NotImplementedError()
+
+  def SupportFlushEntireSystemCache(self):
+    return False
+
+  def FlushEntireSystemCache(self):
+    raise NotImplementedError()
+
+  def FlushSystemCacheForDirectory(self, directory):
+    raise NotImplementedError()
+
+  def FlushDnsCache(self):
+    pass
+
+  def LaunchApplication(
+      self, application, parameters=None, elevate_privilege=False):
+    raise NotImplementedError()
+
+  def IsApplicationRunning(self, application):
+    raise NotImplementedError()
+
+  def CanLaunchApplication(self, application):
+    return False
+
+  def InstallApplication(self, application):
+    raise NotImplementedError()
+
+  def CanCaptureVideo(self):
+    return False
+
+  def StartVideoCapture(self, min_bitrate_mbps):
+    raise NotImplementedError()
+
+  @property
+  def is_video_capture_running(self):
+    return False
+
+  def StopVideoCapture(self):
+    raise NotImplementedError()
+
+  def CanMonitorPower(self):
+    return False
+
+  def CanMeasurePerApplicationPower(self):
+    return False
+
+  def StartMonitoringPower(self, browser):
+    raise NotImplementedError()
+
+  def StopMonitoringPower(self):
+    raise NotImplementedError()
+
+  def CanMonitorNetworkData(self):
+    return False
+
+  def GetNetworkData(self, browser):
+    raise NotImplementedError()
+
+  def ReadMsr(self, msr_number, start=0, length=64):
+    """Read a CPU model-specific register (MSR).
+
+    Which MSRs are available depends on the CPU model.
+    On systems with multiple CPUs, this function may run on any CPU.
+
+    Args:
+      msr_number: The number of the register to read.
+      start: The least significant bit to read, zero-indexed.
+          (Said another way, the number of bits to right-shift the MSR value.)
+      length: The number of bits to read. MSRs are 64 bits, even on 32-bit CPUs.
+    """
+    raise NotImplementedError()
+
+  @property
+  def supports_test_ca(self):
+    """Indicates whether the platform supports installing test CA."""
+    return False
+
+  def InstallTestCa(self, ca_cert_path):
+    """Install a test CA on the platform."""
+    raise NotImplementedError()
+
+  def RemoveTestCa(self):
+    """Remove a previously installed test CA from the platform."""
+    raise NotImplementedError()
+
+  def CanTakeScreenshot(self):
+    return False
+
+  def TakeScreenshot(self, file_path):
+    raise NotImplementedError
+
+  def IsCooperativeShutdownSupported(self):
+    """Indicates whether CooperativelyShutdown, below, is supported.
+    It is not necessary to implement it on all platforms."""
+    return False
+
+  def CooperativelyShutdown(self, proc, app_name):
+    """Cooperatively shut down the given process from subprocess.Popen.
+
+    Currently this is only implemented on Windows. See
+    crbug.com/424024 for background on why it was added.
+
+    Args:
+      proc: a process object returned from subprocess.Popen.
+      app_name: on Windows, is the prefix of the application's window
+          class name that should be searched for. This helps ensure
+          that only the application's windows are closed.
+
+    Returns True if it is believed the attempt succeeded.
+    """
+    raise NotImplementedError()
+
+  def PathExists(self, path, timeout=None, retries=None):
+    """Tests whether the given path exists on the target platform.
+    Args:
+      path: path in request.
+      timeout: timeout.
+      retries: num of retries.
+    Return:
+      Whether the path exists on the target platform.
+    """
+    raise NotImplementedError()
+
+  def HasBattOrConnected(self):
+    return battor_wrapper.IsBattOrConnected(self.GetOSName())
+
+  def WaitForTemperature(self, temp):
+    """Waits for device under test to cool down to temperature given.
+    Args:
+      temp: temperature target in degrees C.
+    """
+    pass
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/platform_backend_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/platform_backend_unittest.py
new file mode 100644
index 0000000..b2d3b83
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/platform_backend_unittest.py
@@ -0,0 +1,39 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+import time
+import unittest
+
+from telemetry.core import platform as platform_module
+from telemetry import decorators
+
+
+class PlatformBackendTest(unittest.TestCase):
+  @decorators.Disabled('mac',       # crbug.com/440666
+                       'vista',     # crbug.com/479337
+                       'chromeos',  # crbug.com/483212
+                       'win')       # catapult/issues/2282
+  def testPowerMonitoringSync(self):
+    # Tests that the act of monitoring power doesn't blow up.
+    platform = platform_module.GetHostPlatform()
+    can_monitor_power = platform.CanMonitorPower()
+    self.assertIsInstance(can_monitor_power, bool)
+    if not can_monitor_power:
+      logging.warning('Test not supported on this platform.')
+      return
+
+    browser_mock = lambda: None
+    # Android needs to access the package of the monitored app.
+    if platform.GetOSName() == 'android':
+      # pylint: disable=protected-access
+      browser_mock._browser_backend = lambda: None
+      # Monitor the launcher, which is always present.
+      browser_mock._browser_backend.package = 'com.android.launcher'
+
+    platform.StartMonitoringPower(browser_mock)
+    time.sleep(0.001)
+    output = platform.StopMonitoringPower()
+    self.assertTrue(output.has_key('energy_consumption_mwh'))
+    self.assertTrue(output.has_key('identifier'))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/posix_platform_backend.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/posix_platform_backend.py
new file mode 100644
index 0000000..aeb4c19
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/posix_platform_backend.py
@@ -0,0 +1,156 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import distutils.spawn
+import logging
+import os
+import re
+import stat
+import subprocess
+import sys
+
+from telemetry.internal.platform import desktop_platform_backend
+from telemetry.internal.util import ps_util
+
+
+def _BinaryExistsInSudoersFiles(path, sudoers_file_contents):
+  """Returns True if the binary in |path| features in the sudoers file.
+  """
+  for line in sudoers_file_contents.splitlines():
+    if re.match(r'\s*\(.+\) NOPASSWD: %s(\s\S+)*$' % re.escape(path), line):
+      return True
+  return False
+
+
+def _CanRunElevatedWithSudo(path):
+  """Returns True if the binary at |path| appears in the sudoers file.
+  If this function returns true then the binary at |path| can be run via sudo
+  without prompting for a password.
+  """
+  sudoers = subprocess.check_output(['/usr/bin/sudo', '-l'])
+  return _BinaryExistsInSudoersFiles(path, sudoers)
+
+
+class PosixPlatformBackend(desktop_platform_backend.DesktopPlatformBackend):
+
+  # This is an abstract class. It is OK to have abstract methods.
+  # pylint: disable=abstract-method
+
+  def HasRootAccess(self):
+    return os.getuid() == 0
+
+  def RunCommand(self, args):
+    return subprocess.Popen(args, stdout=subprocess.PIPE).communicate()[0]
+
+  def GetFileContents(self, path):
+    with open(path, 'r') as f:
+      return f.read()
+
+  def GetPsOutput(self, columns, pid=None):
+    """Returns output of the 'ps' command as a list of lines.
+    Subclass should override this function.
+
+    Args:
+      columns: A list of require columns, e.g., ['pid', 'pss'].
+      pid: If not None, returns only the information of the process
+         with the pid.
+    """
+    return ps_util.GetPsOutputWithPlatformBackend(self, columns, pid)
+
+  def _GetTopOutput(self, pid, columns):
+    """Returns output of the 'top' command as a list of lines.
+
+    Args:
+      pid: pid of process to examine.
+      columns: A list of require columns, e.g., ['idlew', 'vsize'].
+    """
+    args = ['top']
+    args.extend(['-pid', str(pid), '-l', '1', '-s', '0', '-stats',
+        ','.join(columns)])
+    return self.RunCommand(args).splitlines()
+
+  def GetChildPids(self, pid):
+    """Returns a list of child pids of |pid|."""
+    ps_output = self.GetPsOutput(['pid', 'ppid', 'state'])
+    ps_line_re = re.compile(
+        r'\s*(?P<pid>\d+)\s*(?P<ppid>\d+)\s*(?P<state>\S*)\s*')
+    processes = []
+    for pid_ppid_state in ps_output:
+      m = ps_line_re.match(pid_ppid_state)
+      assert m, 'Did not understand ps output: %s' % pid_ppid_state
+      processes.append((m.group('pid'), m.group('ppid'), m.group('state')))
+    return ps_util.GetChildPids(processes, pid)
+
+  def GetCommandLine(self, pid):
+    command = self.GetPsOutput(['command'], pid)
+    return command[0] if command else None
+
+  def CanLaunchApplication(self, application):
+    return bool(distutils.spawn.find_executable(application))
+
+  def IsApplicationRunning(self, application):
+    ps_output = self.GetPsOutput(['command'])
+    application_re = re.compile(
+        r'(.*%s|^)%s(\s|$)' % (os.path.sep, application))
+    return any(application_re.match(cmd) for cmd in ps_output)
+
+  def LaunchApplication(
+      self, application, parameters=None, elevate_privilege=False):
+    assert application, 'Must specify application to launch'
+
+    if os.path.sep not in application:
+      application = distutils.spawn.find_executable(application)
+      assert application, 'Failed to find application in path'
+
+    args = [application]
+
+    if parameters:
+      assert isinstance(parameters, list), 'parameters must be a list'
+      args += parameters
+
+    def IsElevated():
+      """ Returns True if the current process is elevated via sudo i.e. running
+      sudo will not prompt for a password. Returns False if not authenticated
+      via sudo or if telemetry is run on a non-interactive TTY."""
+      # `sudo -v` will always fail if run from a non-interactive TTY.
+      p = subprocess.Popen(
+          ['/usr/bin/sudo', '-nv'], stdin=subprocess.PIPE,
+          stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+      stdout = p.communicate()[0]
+      # Some versions of sudo set the returncode based on whether sudo requires
+      # a password currently. Other versions return output when password is
+      # required and no output when the user is already authenticated.
+      return not p.returncode and not stdout
+
+    def IsSetUID(path):
+      """Returns True if the binary at |path| has the setuid bit set."""
+      return (os.stat(path).st_mode & stat.S_ISUID) == stat.S_ISUID
+
+    if elevate_privilege and not IsSetUID(application):
+      args = ['/usr/bin/sudo'] + args
+      if not _CanRunElevatedWithSudo(application) and not IsElevated():
+        if not sys.stdout.isatty():
+          # Without an interactive terminal (or a configured 'askpass', but
+          # that is rarely relevant), there's no way to prompt the user for
+          # sudo. Fail with a helpful error message. For more information, see:
+          #   https://code.google.com/p/chromium/issues/detail?id=426720
+          text = ('Telemetry needs to run %s with elevated privileges, but the '
+                 'setuid bit is not set and there is no interactive terminal '
+                 'for a prompt. Please ask an administrator to set the setuid '
+                 'bit on this executable and ensure that it is owned by a user '
+                 'with the necessary privileges. Aborting.' % application)
+          print text
+          raise Exception(text)
+        # Else, there is a tty that can be used for a useful interactive prompt.
+        print ('Telemetry needs to run %s under sudo. Please authenticate.' %
+               application)
+        # Synchronously authenticate.
+        subprocess.check_call(['/usr/bin/sudo', '-v'])
+
+    stderror_destination = subprocess.PIPE
+    if logging.getLogger().isEnabledFor(logging.DEBUG):
+      stderror_destination = None
+
+    return subprocess.Popen(
+        args, stdout=subprocess.PIPE, stderr=stderror_destination)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/posix_platform_backend_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/posix_platform_backend_unittest.py
new file mode 100644
index 0000000..e139980
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/posix_platform_backend_unittest.py
@@ -0,0 +1,87 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import os
+import sys
+import unittest
+
+from telemetry.core import platform as platform_module
+from telemetry import decorators
+from telemetry.internal.platform import posix_platform_backend
+
+
+class TestBackend(posix_platform_backend.PosixPlatformBackend):
+
+  # pylint: disable=abstract-method
+
+  def __init__(self):
+    super(TestBackend, self).__init__()
+    self._mock_ps_output = None
+
+  def SetMockPsOutput(self, output):
+    self._mock_ps_output = output
+
+  def GetPsOutput(self, columns, pid=None):
+    return self._mock_ps_output
+
+
+class PosixPlatformBackendTest(unittest.TestCase):
+
+  def testGetChildPidsWithGrandChildren(self):
+    backend = TestBackend()
+    backend.SetMockPsOutput(['1 0 S', '2 1 R', '3 2 S', '4 1 R', '5 4 R'])
+    result = backend.GetChildPids(1)
+    self.assertEquals(set(result), set([2, 3, 4, 5]))
+
+  def testGetChildPidsWithNoSuchPid(self):
+    backend = TestBackend()
+    backend.SetMockPsOutput(['1 0 S', '2 1 R', '3 2 S', '4 1 R', '5 4 R'])
+    result = backend.GetChildPids(6)
+    self.assertEquals(set(result), set())
+
+  def testGetChildPidsWithZombieChildren(self):
+    backend = TestBackend()
+    backend.SetMockPsOutput(['1 0 S', '2 1 R', '3 2 S', '4 1 R', '5 4 Z'])
+    result = backend.GetChildPids(1)
+    self.assertEquals(set(result), set([2, 3, 4]))
+
+  def testGetChildPidsWithMissingState(self):
+    backend = TestBackend()
+    backend.SetMockPsOutput(['  1 0 S  ', '  2 1', '3 2 '])
+    result = backend.GetChildPids(1)
+    self.assertEquals(set(result), set([2, 3]))
+
+  def testSudoersFileParsing(self):
+    binary_path = '/usr/bin/pkill'
+    self.assertFalse(
+        posix_platform_backend._BinaryExistsInSudoersFiles(binary_path, ''))
+    self.assertFalse(
+        posix_platform_backend._BinaryExistsInSudoersFiles(
+            binary_path, '    (ALL) ALL'))
+    self.assertFalse(
+        posix_platform_backend._BinaryExistsInSudoersFiles(
+            binary_path, '     (root) NOPASSWD: /usr/bin/pkill_DUMMY'))
+    self.assertFalse(
+        posix_platform_backend._BinaryExistsInSudoersFiles(
+            binary_path, '     (root) NOPASSWD: pkill'))
+
+
+    self.assertTrue(
+        posix_platform_backend._BinaryExistsInSudoersFiles(
+            binary_path, '(root) NOPASSWD: /usr/bin/pkill'))
+    self.assertTrue(
+        posix_platform_backend._BinaryExistsInSudoersFiles(
+            binary_path, '     (root) NOPASSWD: /usr/bin/pkill'))
+    self.assertTrue(
+        posix_platform_backend._BinaryExistsInSudoersFiles(
+            binary_path, '     (root) NOPASSWD: /usr/bin/pkill arg1 arg2'))
+
+  @decorators.Enabled('linux', 'mac')
+  def testIsApplicationRunning(self):
+    platform = platform_module.GetHostPlatform()
+
+    self.assertFalse(platform.IsApplicationRunning('This_Is_A_Bad___App__Name'))
+    sys_exe = os.path.basename(sys.executable)
+    self.assertTrue(platform.IsApplicationRunning(sys_exe))
+    self.assertFalse(
+        platform.IsApplicationRunning('%s append_bad_after_space' % sys_exe))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/__init__.py
new file mode 100644
index 0000000..4c3cf45
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/__init__.py
@@ -0,0 +1,50 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+
+from telemetry.core import exceptions
+
+
+class PowerMonitor(object):
+  """A power profiler.
+
+  Provides an interface to register power consumption during a test.
+  """
+  def __init__(self):
+    self._monitoring = False
+
+  def CanMonitorPower(self):
+    """Returns True iff power can be monitored asynchronously via
+    StartMonitoringPower() and StopMonitoringPower().
+    """
+    return False
+
+  def CanMeasurePerApplicationPower(self):
+    """Returns True if the power monitor can measure power for the target
+    application in isolation. False if power measurement is for full system
+    energy consumption."""
+    return False
+
+  def _CheckStart(self):
+    assert not self._monitoring, "Already monitoring power."
+    self._monitoring = True
+
+  def _CheckStop(self):
+    assert self._monitoring, "Not monitoring power."
+    self._monitoring = False
+
+  def StartMonitoringPower(self, browser):
+    """Starts monitoring power utilization statistics.
+
+    See Platform#StartMonitoringPower for the arguments format.
+    """
+    raise NotImplementedError()
+
+  def StopMonitoringPower(self):
+    """Stops monitoring power utilization and returns collects stats
+
+    See Platform#StopMonitoringPower for the return format.
+    """
+    raise NotImplementedError()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/android_dumpsys_power_monitor.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/android_dumpsys_power_monitor.py
new file mode 100644
index 0000000..2983777
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/android_dumpsys_power_monitor.py
@@ -0,0 +1,68 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import csv
+import logging
+
+from telemetry.internal.platform.power_monitor import android_power_monitor_base
+
+class DumpsysPowerMonitor(android_power_monitor_base.AndroidPowerMonitorBase):
+  """PowerMonitor that relies on the dumpsys batterystats to monitor the power
+  consumption of a single android application. This measure uses a heuristic
+  and is the same information end-users see with the battery application.
+  Available on Android L and higher releases.
+  """
+  def __init__(self, battery, platform_backend):
+    """Constructor.
+
+    Args:
+        battery: A BatteryUtil instance.
+        platform_backend: A LinuxBasedPlatformBackend instance.
+    """
+    super(DumpsysPowerMonitor, self).__init__()
+    self._battery = battery
+    self._browser = None
+    self._platform = platform_backend
+
+  def CanMonitorPower(self):
+    result = self._platform.device.RunShellCommand(
+        ['dumpsys', 'batterystats', '-c'], check_return=True)
+    DUMP_VERSION_INDEX = 0
+    # Dumpsys power data is present in dumpsys versions 8 and 9
+    # which is found on L+ devices.
+    return (csv.reader(result).next()[DUMP_VERSION_INDEX] in ['8', '9'])
+
+  def StartMonitoringPower(self, browser):
+    self._CheckStart()
+    assert browser
+    self._browser = browser
+    # Disable the charging of the device over USB. This is necessary because the
+    # device only collects information about power usage when the device is not
+    # charging.
+
+  def StopMonitoringPower(self):
+    self._CheckStop()
+    assert self._browser
+    package = self._browser._browser_backend.package
+    self._browser = None
+
+    voltage = self._ParseVoltage(self._battery.GetBatteryInfo().get('voltage'))
+    power_data = self._battery.GetPowerData()
+    power_results = self.ProcessPowerData(power_data, voltage, package)
+    self._LogPowerAnomalies(power_results, package)
+    return power_results
+
+  @staticmethod
+  def ProcessPowerData(power_data, voltage, package):
+    package_power_data = power_data['per_package'].get(package)
+    if not package_power_data:
+      logging.warning('No power data for %s in dumpsys output.' % package)
+      package_power = 0
+    else:
+      package_power = sum(package_power_data['data'])
+
+    return {'identifier': 'dumpsys',
+            'power_samples_mw': [],
+            'energy_consumption_mwh': power_data['system_total'] * voltage,
+            'application_energy_consumption_mwh': package_power * voltage}
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/android_dumpsys_power_monitor_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/android_dumpsys_power_monitor_unittest.py
new file mode 100644
index 0000000..e38ebc5
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/android_dumpsys_power_monitor_unittest.py
@@ -0,0 +1,92 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from telemetry.internal.platform.power_monitor import android_dumpsys_power_monitor
+from telemetry.internal.platform.power_monitor import pm_mock
+
+
+_PACKAGE = 'com.google.android.apps.chrome'
+
+_TYPICAL_POWER_DATA = {
+      'system_total': 2000.0,
+      'per_package': {
+        _PACKAGE: {'data': [23.9], 'uid': '12345'}
+      }
+    }
+
+_TYPICAL_POWER_DATA_MULTISAMPLE = {
+      'system_total': 2000.0,
+      'per_package': {
+        _PACKAGE: {'data': [23.9, 26.1], 'uid': '12345'}
+      }
+    }
+
+
+class DumpsysPowerMonitorMonitorTest(unittest.TestCase):
+
+  def testApplicationEnergyConsumption(self):
+    results = (
+        android_dumpsys_power_monitor.DumpsysPowerMonitor.ProcessPowerData(
+            _TYPICAL_POWER_DATA, 4.0, _PACKAGE))
+    self.assertEqual(results['identifier'], 'dumpsys')
+    self.assertAlmostEqual(results['application_energy_consumption_mwh'], 95.6)
+
+  def testSystemEnergyConsumption(self):
+    power_data = {
+      'system_total': 2000.0,
+      'per_package': {}
+    }
+    results = (
+        android_dumpsys_power_monitor.DumpsysPowerMonitor.ProcessPowerData(
+            power_data, 4.0, 'some.package'))
+    self.assertEqual(results['identifier'], 'dumpsys')
+    self.assertEqual(results['application_energy_consumption_mwh'], 0)
+    self.assertEqual(results['energy_consumption_mwh'], 8000.0)
+
+  def testMonitorCycle(self):
+    browser = pm_mock.MockBrowser(_PACKAGE)
+    battery = pm_mock.MockBattery(_TYPICAL_POWER_DATA_MULTISAMPLE, voltage=5.0)
+    backend = pm_mock.MockPlatformBackend()
+    pm = android_dumpsys_power_monitor.DumpsysPowerMonitor(battery, backend)
+    pm.StartMonitoringPower(browser)
+    result = pm.StopMonitoringPower()
+    self.assertEqual(result['identifier'], 'dumpsys')
+    self.assertEqual(result['power_samples_mw'], [])
+    self.assertAlmostEqual(result['application_energy_consumption_mwh'], 250.0)
+    self.assertAlmostEqual(result['energy_consumption_mwh'], 10000.0)
+
+  def testDoubleStop(self):
+    browser = pm_mock.MockBrowser(_PACKAGE)
+    battery = pm_mock.MockBattery(_TYPICAL_POWER_DATA_MULTISAMPLE, voltage=5.0)
+    backend = pm_mock.MockPlatformBackend()
+    pm = android_dumpsys_power_monitor.DumpsysPowerMonitor(battery, backend)
+    pm.StartMonitoringPower(browser)
+    pm.StopMonitoringPower()
+    with self.assertRaises(AssertionError):
+      pm.StopMonitoringPower()
+
+  def testDoubleStart(self):
+    browser = pm_mock.MockBrowser(_PACKAGE)
+    battery = pm_mock.MockBattery(_TYPICAL_POWER_DATA_MULTISAMPLE, voltage=5.0)
+    backend = pm_mock.MockPlatformBackend()
+    pm = android_dumpsys_power_monitor.DumpsysPowerMonitor(battery, backend)
+    pm.StartMonitoringPower(browser)
+    with self.assertRaises(AssertionError):
+      pm.StartMonitoringPower(browser)
+
+  def testBatteryChargingState(self):
+    browser = pm_mock.MockBrowser(_PACKAGE)
+    battery = pm_mock.MockBattery(_TYPICAL_POWER_DATA_MULTISAMPLE, voltage=5.0)
+    backend = pm_mock.MockPlatformBackend()
+    pm = android_dumpsys_power_monitor.DumpsysPowerMonitor(battery, backend)
+    self.assertEqual(battery.GetCharging(), True)
+    pm.StartMonitoringPower(browser)
+    self.assertEqual(battery.GetCharging(), True)
+    pm.StopMonitoringPower()
+    self.assertEqual(battery.GetCharging(), True)
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/android_fuelgauge_power_monitor.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/android_fuelgauge_power_monitor.py
new file mode 100644
index 0000000..7bd421b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/android_fuelgauge_power_monitor.py
@@ -0,0 +1,42 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.internal.platform.power_monitor import android_power_monitor_base
+
+
+class FuelGaugePowerMonitor(android_power_monitor_base.AndroidPowerMonitorBase):
+  """PowerMonitor that relies on the fuel gauge chips to monitor the power
+  consumption of a android device.
+  """
+  def __init__(self, battery):
+    """Constructor.
+
+    Args:
+        battery: A BatteryUtil instance.
+        platform_backend: A LinuxBasedPlatformBackend instance.
+    """
+    super(FuelGaugePowerMonitor, self).__init__()
+    self._battery = battery
+    self._starting_fuel_gauge = None
+
+  def CanMonitorPower(self):
+    return self._battery.SupportsFuelGauge()
+
+  def StartMonitoringPower(self, browser):
+    self._CheckStart()
+    self._starting_fuel_gauge = self._battery.GetFuelGaugeChargeCounter()
+
+  def StopMonitoringPower(self):
+    self._CheckStop()
+    # Convert from nAh to mAh.
+    fuel_gauge_delta = (
+        float((self._starting_fuel_gauge) -
+        self._battery.GetFuelGaugeChargeCounter()) / 1000000)
+    voltage = self._ParseVoltage(self._battery.GetBatteryInfo().get('voltage'))
+    return self.ProcessPowerData(voltage, fuel_gauge_delta)
+
+  @staticmethod
+  def ProcessPowerData(voltage, fuel_gauge_delta):
+    return {'identifier': 'fuel_gauge',
+            'fuel_gauge_energy_consumption_mwh': fuel_gauge_delta * voltage}
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/android_fuelgauge_power_monitor_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/android_fuelgauge_power_monitor_unittest.py
new file mode 100644
index 0000000..6bce9c3
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/android_fuelgauge_power_monitor_unittest.py
@@ -0,0 +1,48 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from telemetry.internal.platform.power_monitor import (
+    android_fuelgauge_power_monitor)
+from telemetry.internal.platform.power_monitor import pm_mock
+
+
+class FuelGaugePowerMonitorMonitorTest(unittest.TestCase):
+
+  def testEnergyConsumption(self):
+    fuel_gauge_delta = 100
+    results = (
+        android_fuelgauge_power_monitor.FuelGaugePowerMonitor.ProcessPowerData(
+            4.0, fuel_gauge_delta))
+    self.assertEqual(results['identifier'], 'fuel_gauge')
+    self.assertEqual(
+        results.get('fuel_gauge_energy_consumption_mwh'), 400)
+
+  def testMonitorCycle(self):
+    battery = pm_mock.MockBattery(None, voltage=5.0, fuelgauge=[5.e6, 3.e6])
+    pm = android_fuelgauge_power_monitor.FuelGaugePowerMonitor(battery)
+    pm.StartMonitoringPower(None)
+    results = pm.StopMonitoringPower()
+    self.assertEqual(results['identifier'], 'fuel_gauge')
+    self.assertAlmostEqual(results['fuel_gauge_energy_consumption_mwh'], 10)
+
+  def testDoubleStop(self):
+    battery = pm_mock.MockBattery(None, voltage=5.0, fuelgauge=[5.e6, 3.e6])
+    pm = android_fuelgauge_power_monitor.FuelGaugePowerMonitor(battery)
+    pm.StartMonitoringPower(None)
+    pm.StopMonitoringPower()
+    with self.assertRaises(AssertionError):
+      pm.StopMonitoringPower()
+
+  def testDoubleStart(self):
+    battery = pm_mock.MockBattery(None, voltage=5.0, fuelgauge=[5.e6, 3.e6])
+    pm = android_fuelgauge_power_monitor.FuelGaugePowerMonitor(battery)
+    pm.StartMonitoringPower(None)
+    with self.assertRaises(AssertionError):
+      pm.StartMonitoringPower(None)
+
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/android_power_monitor_base.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/android_power_monitor_base.py
new file mode 100644
index 0000000..afcc87d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/android_power_monitor_base.py
@@ -0,0 +1,36 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+
+from telemetry.internal.platform import power_monitor
+
+
+class AndroidPowerMonitorBase(power_monitor.PowerMonitor):
+
+  # Abstract class.
+  # pylint: disable=abstract-method
+
+  def _ParseVoltage(self, millivolts):
+    # Parse voltage information.
+    # If voltage is None, use 4.0 as default.
+    # Otherwise, convert millivolts to volts.
+    if millivolts is None:
+      # Converting at a nominal voltage of 4.0V, as those values are obtained by
+      # a heuristic, and 4.0V is the voltage we set when using a monsoon device.
+      voltage = 4.0
+      logging.warning('Unable to get device voltage. Using %s.', voltage)
+    else:
+      voltage = float(millivolts) / 1000
+      logging.info('Device voltage at %s', voltage)
+      return voltage
+
+  def _LogPowerAnomalies(self, power_data, package):
+    # Log anomalies in power data.
+    if power_data['energy_consumption_mwh'] == 0:
+      logging.warning('Power data is returning 0 for system total usage. %s'
+                      % (power_data))
+      if power_data['application_energy_consumption_mwh'] == 0:
+        logging.warning('Power data is returning 0 usage for %s. %s'
+                        % (package, power_data))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/android_power_monitor_controller.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/android_power_monitor_controller.py
new file mode 100644
index 0000000..5648163
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/android_power_monitor_controller.py
@@ -0,0 +1,91 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.internal.util import atexit_with_log
+import logging
+
+from telemetry.internal.platform.power_monitor import android_power_monitor_base
+
+def _ReenableChargingIfNeeded(battery):
+  if not battery.GetCharging():
+    battery.SetCharging(True)
+  logging.info('Charging status checked at exit.')
+
+class AndroidPowerMonitorController(
+    android_power_monitor_base.AndroidPowerMonitorBase):
+  """
+  PowerMonitor that acts as facade for a list of PowerMonitor objects and uses
+  the first available one.
+  """
+  def __init__(self, power_monitors, battery):
+    super(AndroidPowerMonitorController, self).__init__()
+    self._candidate_power_monitors = power_monitors
+    self._active_monitors = []
+    self._battery = battery
+    atexit_with_log.Register(_ReenableChargingIfNeeded, self._battery)
+
+  def CanMonitorPower(self):
+    return any(m.CanMonitorPower() for m in self._candidate_power_monitors)
+
+  def StartMonitoringPower(self, browser):
+    # TODO(rnephew): re-add assert when crbug.com/553601 is solved and
+    # StopMonitoringPower is called in the correct place.
+    if self._active_monitors:
+      logging.warning('StopMonitoringPower() not called when expected. Last '
+                      'results are likely not reported.')
+      self.StopMonitoringPower()
+    self._CheckStart()
+    self._ChargingOff(self._battery)
+    self._active_monitors = (
+        [m for m in self._candidate_power_monitors if m.CanMonitorPower()])
+    assert self._active_monitors, 'No available monitor.'
+    for monitor in self._active_monitors:
+      monitor.StartMonitoringPower(browser)
+
+  @staticmethod
+  def _MergePowerResults(combined_results, monitor_results):
+    """
+    Merges monitor_results into combined_results and leaves monitor_results
+    values if there are merge conflicts.
+    """
+    def _CheckDuplicateKeys(dict_one, dict_two, ignore_list=None):
+      for key in dict_one:
+        if key in dict_two and key not in ignore_list:
+          logging.warning('Found multiple instances of %s in power monitor '
+                          'entries. Using newest one.', key)
+    # Sub level power entries.
+    for part in ['platform_info', 'component_utilization']:
+      if part in monitor_results:
+        _CheckDuplicateKeys(combined_results[part], monitor_results[part])
+        combined_results[part].update(monitor_results[part])
+
+    # Top level power entries.
+    platform_info = combined_results['platform_info'].copy()
+    comp_utilization = combined_results['component_utilization'].copy()
+    _CheckDuplicateKeys(
+        combined_results, monitor_results,
+        ['identifier', 'platform_info', 'component_utilization'])
+    combined_results.update(monitor_results)
+    combined_results['platform_info'] = platform_info
+    combined_results['component_utilization'] = comp_utilization
+
+  def StopMonitoringPower(self):
+    self._CheckStop()
+    self._ChargingOn(self._battery)
+    try:
+      results = {'platform_info': {}, 'component_utilization': {}}
+      for monitor in self._active_monitors:
+        self._MergePowerResults(results, monitor.StopMonitoringPower())
+      return results
+    finally:
+      self._active_monitors = []
+
+  def _ChargingOff(self, battery):
+    battery.SetCharging(False)
+
+  def _ChargingOn(self, battery):
+    if battery.GetCharging():
+      logging.warning('Charging re-enabled during test.'
+                      'Results may be inaccurate.')
+    battery.SetCharging(True)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/android_power_monitor_controller_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/android_power_monitor_controller_unittest.py
new file mode 100644
index 0000000..a77c3cc
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/android_power_monitor_controller_unittest.py
@@ -0,0 +1,89 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from telemetry.internal.platform import power_monitor as power_monitor
+from telemetry.internal.platform.power_monitor import (
+  android_power_monitor_controller)
+import mock
+from devil.android import battery_utils
+
+# pylint: disable=import-error, unused-argument
+
+
+class AndroidPowerMonitorControllerTest(unittest.TestCase):
+  @mock.patch.object(battery_utils, 'BatteryUtils')
+  def testComposition(self, _):
+
+    class P1(power_monitor.PowerMonitor):
+      def StartMonitoringPower(self, browser):
+        raise NotImplementedError()
+      def StopMonitoringPower(self):
+        raise NotImplementedError()
+
+    class P2(power_monitor.PowerMonitor):
+      def __init__(self, value):
+        super(P2, self).__init__()
+        self._value = {'P2': value}
+      def CanMonitorPower(self):
+        return True
+      def StartMonitoringPower(self, browser):
+        pass
+      def StopMonitoringPower(self):
+        return self._value
+
+    class P3(power_monitor.PowerMonitor):
+      def __init__(self, value):
+        super(P3, self).__init__()
+        self._value = {'P3': value}
+      def CanMonitorPower(self):
+        return True
+      def StartMonitoringPower(self, browser):
+        pass
+      def StopMonitoringPower(self):
+        return self._value
+
+    battery = battery_utils.BatteryUtils(None)
+    controller = android_power_monitor_controller.AndroidPowerMonitorController(
+        [P1(), P2(1), P3(2)], battery)
+    self.assertEqual(controller.CanMonitorPower(), True)
+    controller.StartMonitoringPower(None)
+    controller_returns = controller.StopMonitoringPower()
+    self.assertEqual(controller_returns['P2'], 1)
+    self.assertEqual(controller_returns['P3'], 2)
+
+  @mock.patch.object(battery_utils, 'BatteryUtils')
+  def testReenableChargingIfNeeded(self, mock_battery):
+    battery = battery_utils.BatteryUtils(None)
+    battery.GetCharging.return_value = False
+    android_power_monitor_controller._ReenableChargingIfNeeded(battery)
+
+  def testMergePowerResultsOneEmpty(self):
+    dict_one = {'platform_info': {}, 'component_utilization': {}}
+    dict_two = {'test': 1, 'component_utilization': {'test': 2}}
+    results = {
+        'platform_info': {},
+        'component_utilization': {'test': 2},
+        'test': 1
+    }
+    (android_power_monitor_controller.AndroidPowerMonitorController.
+     _MergePowerResults(dict_one, dict_two))
+    self.assertDictEqual(dict_one, results)
+
+  def testMergePowerResultsSameEntry(self):
+    dict_one = {
+        'test': 1,
+        'component_utilization': {'test': 2},
+        'platform_info': {'test2': 'a'}
+    }
+    dict_two = {'test': 3, 'platform_info': {'test': 4}}
+    results = {
+        'test' : 3,
+        'component_utilization': {'test': 2},
+        'platform_info': {'test': 4, 'test2': 'a'}
+    }
+    (android_power_monitor_controller.AndroidPowerMonitorController.
+     _MergePowerResults(dict_one, dict_two))
+    self.assertDictEqual(dict_one, results)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/android_temperature_monitor.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/android_temperature_monitor.py
new file mode 100644
index 0000000..87d7c37
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/android_temperature_monitor.py
@@ -0,0 +1,52 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+
+from telemetry.internal.platform import power_monitor
+
+try:
+  from devil.android import device_errors  # pylint: disable=import-error
+except ImportError:
+  device_errors = None
+
+
+_TEMPERATURE_FILE = '/sys/class/thermal/thermal_zone0/temp'
+
+
+class AndroidTemperatureMonitor(power_monitor.PowerMonitor):
+  """
+  Returns temperature results in power monitor dictionary format.
+  """
+  def __init__(self, device):
+    super(AndroidTemperatureMonitor, self).__init__()
+    self._device = device
+
+  def CanMonitorPower(self):
+    return self._GetBoardTemperatureCelsius() is not None
+
+  def StartMonitoringPower(self, browser):
+    # don't call _CheckStart() because this is temperature, not power
+    # therefore, StartMonitoringPower and StopMonitoringPower
+    # do not need to be paired
+    pass
+
+  def StopMonitoringPower(self):
+    avg_temp = self._GetBoardTemperatureCelsius()
+    if avg_temp is None:
+      return {'identifier': 'android_temperature_monitor'}
+    else:
+      return {'identifier': 'android_temperature_monitor',
+              'platform_info': {'average_temperature_c': avg_temp}}
+
+  def _GetBoardTemperatureCelsius(self):
+    try:
+      contents = self._device.ReadFile(_TEMPERATURE_FILE)
+      return float(contents) if contents else None
+    except ValueError:
+      logging.warning('String returned from device.ReadFile(_TEMPERATURE_FILE) '
+                      'in invalid format.')
+      return None
+    except device_errors.CommandFailedError:
+      return None
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/android_temperature_monitor_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/android_temperature_monitor_unittest.py
new file mode 100644
index 0000000..830ba0f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/android_temperature_monitor_unittest.py
@@ -0,0 +1,50 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+import mock
+from telemetry.internal.platform.power_monitor import android_temperature_monitor
+
+class AndroidTemperatureMonitorTest(unittest.TestCase):
+
+  def testPowerMonitoringResultsWereUpdated(self):
+    mock_device_utils = mock.Mock()
+    mock_device_utils.ReadFile.side_effect = ['0', '24']
+
+    monitor = android_temperature_monitor.AndroidTemperatureMonitor(
+        mock_device_utils)
+    self.assertTrue(monitor.CanMonitorPower())
+    monitor.StartMonitoringPower(None)
+    measurements = monitor.StopMonitoringPower()
+    mock_device_utils.ReadFile.assert_has_calls(
+        [mock.call(mock.ANY), mock.call(mock.ANY)])
+    expected_return = {
+        'identifier': 'android_temperature_monitor',
+        'platform_info': {'average_temperature_c': 24.0}
+    }
+    self.assertDictEqual(expected_return, measurements)
+
+  def testSysfsReadFailed(self):
+    mock_device_utils = mock.Mock()
+    mock_device_utils.ReadFile.side_effect = ['24', None]
+
+    monitor = android_temperature_monitor.AndroidTemperatureMonitor(
+        mock_device_utils)
+    self.assertTrue(monitor.CanMonitorPower())
+    monitor.StartMonitoringPower(None)
+    measurements = monitor.StopMonitoringPower()
+    mock_device_utils.ReadFile.assert_has_calls(
+        [mock.call(mock.ANY), mock.call(mock.ANY)])
+    self.assertTrue('identifier' in measurements)
+    self.assertTrue('platform_info' not in measurements)
+
+  def testSysfsReadFailedCanMonitor(self):
+    mock_device_utils = mock.Mock()
+    mock_device_utils.ReadFile.side_effect = [None]
+
+    monitor = android_temperature_monitor.AndroidTemperatureMonitor(
+        mock_device_utils)
+    self.assertFalse(monitor.CanMonitorPower())
+    mock_device_utils.ReadFile.assert_called_once_with(mock.ANY)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/cros_power_monitor.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/cros_power_monitor.py
new file mode 100644
index 0000000..7f922bd
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/cros_power_monitor.py
@@ -0,0 +1,164 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import collections
+import logging
+import re
+
+from telemetry import decorators
+from telemetry.internal.platform.power_monitor import sysfs_power_monitor
+
+
+class CrosPowerMonitor(sysfs_power_monitor.SysfsPowerMonitor):
+  """PowerMonitor that relies on 'dump_power_status' to monitor power
+  consumption of a single ChromeOS application.
+  """
+  def __init__(self, platform_backend):
+    """Constructor.
+
+    Args:
+        platform_backend: A LinuxBasedPlatformBackend object.
+
+    Attributes:
+        _initial_power: The result of 'dump_power_status' before the test.
+        _start_time: The epoch time at which the test starts executing.
+    """
+    super(CrosPowerMonitor, self).__init__(platform_backend)
+    self._initial_power = None
+    self._start_time = None
+
+  @decorators.Cache
+  def CanMonitorPower(self):
+    return super(CrosPowerMonitor, self).CanMonitorPower()
+
+  def StartMonitoringPower(self, browser):
+    super(CrosPowerMonitor, self).StartMonitoringPower(browser)
+    if self._IsOnBatteryPower():
+      sample = self._platform.RunCommand(['dump_power_status;', 'date', '+%s'])
+      self._initial_power, self._start_time = CrosPowerMonitor.SplitSample(
+          sample)
+    else:
+      logging.warning('Device not on battery power during power monitoring. '
+                      'Results may be incorrect.')
+
+  def StopMonitoringPower(self):
+    # Don't need to call self._CheckStop here; it's called by the superclass
+    cpu_stats = super(CrosPowerMonitor, self).StopMonitoringPower()
+    power_stats = {}
+    if self._IsOnBatteryPower():
+      sample = self._platform.RunCommand(['dump_power_status;', 'date', '+%s'])
+      final_power, end_time = CrosPowerMonitor.SplitSample(sample)
+      # The length of the test is used to measure energy consumption.
+      length_h = (end_time - self._start_time) / 3600.0
+      power_stats = CrosPowerMonitor.ParsePower(self._initial_power,
+                                                final_power, length_h)
+    else:
+      logging.warning('Device not on battery power during power monitoring. '
+                      'Results may be incorrect.')
+    return CrosPowerMonitor.CombineResults(cpu_stats, power_stats)
+
+  @staticmethod
+  def SplitSample(sample):
+    """Splits a power and time sample into the two separate values.
+
+    Args:
+        sample: The result of calling 'dump_power_status; date +%s' on the
+            device.
+
+    Returns:
+        A tuple of power sample and epoch time of the sample.
+    """
+    sample = sample.strip()
+    index = sample.rfind('\n')
+    power = sample[:index]
+    time = sample[index + 1:]
+    return power, int(time)
+
+  @staticmethod
+  def IsOnBatteryPower(status, board):
+    """Determines if the devices is being charged.
+
+    Args:
+        status: The parsed result of 'dump_power_status'
+        board: The name of the board running the test.
+
+    Returns:
+        True if the device is on battery power; False otherwise.
+    """
+    on_battery = status['line_power_connected'] == '0'
+    # Butterfly can incorrectly report AC online for some time after unplug.
+    # Check battery discharge state to confirm.
+    if board == 'butterfly':
+      on_battery |= status['battery_discharging'] == '1'
+    return on_battery
+
+  def _IsOnBatteryPower(self):
+    """Determines if the device is being charged.
+
+    Returns:
+        True if the device is on battery power; False otherwise.
+    """
+    status = CrosPowerMonitor.ParsePowerStatus(
+        self._platform.RunCommand(['dump_power_status']))
+    board_data = self._platform.RunCommand(['cat', '/etc/lsb-release'])
+    board = re.search('BOARD=(.*)', board_data).group(1)
+    return CrosPowerMonitor.IsOnBatteryPower(status, board)
+
+  @staticmethod
+  def ParsePowerStatus(sample):
+    """Parses 'dump_power_status' command output.
+
+    Args:
+        sample: The output of 'dump_power_status'
+
+    Returns:
+        Dictionary containing all fields from 'dump_power_status'
+    """
+    rv = collections.defaultdict(dict)
+    for ln in sample.splitlines():
+      words = ln.split()
+      assert len(words) == 2
+      rv[words[0]] = words[1]
+    return dict(rv)
+
+  @staticmethod
+  def ParsePower(initial_stats, final_stats, length_h):
+    """Parse output of 'dump_power_status'
+
+    Args:
+        initial_stats: The output of 'dump_power_status' before the test.
+        final_stats: The output of 'dump_power_status' after the test.
+        length_h: The length of the test in hours.
+
+    Returns:
+        Dictionary in the format returned by StopMonitoringPower().
+    """
+    initial = CrosPowerMonitor.ParsePowerStatus(initial_stats)
+    final = CrosPowerMonitor.ParsePowerStatus(final_stats)
+    # The charge value reported by 'dump_power_status' is not precise enough to
+    # give meaningful results across shorter tests, so average energy rate and
+    # the length of the test are used.
+    initial_power_mw = float(initial['battery_energy_rate']) * 10 ** 3
+    final_power_mw = float(final['battery_energy_rate']) * 10 ** 3
+    average_power_mw = (initial_power_mw + final_power_mw) / 2.0
+
+    # Duplicating CrOS battery fields where applicable.
+    def CopyFinalState(field, key):
+      """Copy fields from battery final state."""
+      if field in final:
+        battery[key] = float(final[field])
+
+    battery = {}
+    CopyFinalState('battery_charge_full', 'charge_full')
+    CopyFinalState('battery_charge_full_design', 'charge_full_design')
+    CopyFinalState('battery_charge', 'charge_now')
+    CopyFinalState('battery_current', 'current_now')
+    CopyFinalState('battery_energy', 'energy')
+    CopyFinalState('battery_energy_rate', 'energy_rate')
+    CopyFinalState('battery_voltage', 'voltage_now')
+
+    return {'identifier': 'dump_power_status',
+            'power_samples_mw': [initial_power_mw, final_power_mw],
+            'energy_consumption_mwh': average_power_mw * length_h,
+            'component_utilization': {'battery': battery}}
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/cros_power_monitor_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/cros_power_monitor_unittest.py
new file mode 100644
index 0000000..2d4945e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/cros_power_monitor_unittest.py
@@ -0,0 +1,227 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from telemetry.internal.platform.power_monitor import cros_power_monitor
+
+
+class CrosPowerMonitorMonitorTest(unittest.TestCase):
+  initial_power = ('''line_power_connected 0
+battery_present 1
+battery_percent 70.20
+battery_charge 2.83
+battery_charge_full 4.03
+battery_charge_full_design 4.03
+battery_current 1.08
+battery_energy 31.83
+battery_energy_rate 12.78
+battery_voltage 11.82
+battery_discharging 1''')
+  final_power = ('''line_power_connected 0
+battery_present 1
+battery_percent 70.20
+battery_charge 2.83
+battery_charge_full 4.03
+battery_charge_full_design 4.03
+battery_current 1.08
+battery_energy 31.83
+battery_energy_rate 12.80
+battery_voltage 12.24
+battery_discharging 1''')
+  incomplete_final_power = ('''line_power_connected 0
+battery_present 1
+battery_percent 70.20
+battery_charge 2.83
+battery_charge_full 4.03
+battery_charge_full_design 4.03
+battery_energy_rate 12.80
+battery_discharging 1''')
+  expected_power = {
+    'energy_consumption_mwh': 2558.0,
+    'power_samples_mw': [12780.0, 12800.0],
+    'component_utilization': {
+      'battery': {
+        'charge_full': 4.03,
+        'charge_full_design': 4.03,
+        'charge_now': 2.83,
+        'current_now': 1.08,
+        'energy': 31.83,
+        'energy_rate': 12.80,
+        'voltage_now': 12.24
+      }
+    }
+  }
+  expected_incomplete_power = {
+    'energy_consumption_mwh': 2558.0,
+    'power_samples_mw': [12780.0, 12800.0],
+    'component_utilization': {
+      'battery': {
+        'charge_full': 4.03,
+        'charge_full_design': 4.03,
+        'charge_now': 2.83,
+        'energy_rate': 12.80,
+      }
+    }
+  }
+  expected_cpu = {
+    'whole_package': {
+      'frequency_percent': {
+        1700000000: 3.29254111574526,
+        1600000000: 0.0,
+        1500000000: 0.0,
+        1400000000: 0.15926805099535601,
+        1300000000: 0.47124116307273645,
+        1200000000: 0.818756100807525,
+        1100000000: 1.099381692400982,
+        1000000000: 2.5942528544384302,
+        900000000: 5.68661122326737,
+        800000000: 3.850545467654628,
+        700000000: 2.409691872245393,
+        600000000: 1.4693702487650486,
+        500000000: 2.4623575553879373,
+        400000000: 2.672038150383057,
+        300000000: 3.415770495015825,
+        200000000: 69.59817400982045
+      },
+      'cstate_residency_percent': {
+        'C0': 83.67623835616438535,
+        'C1': 0.2698609589041096,
+        'C2': 0.2780191780821918,
+        'C3': 15.77588150684931505
+      }
+    },
+    'cpu0': {
+      'frequency_percent': {
+        1700000000: 4.113700564971752,
+        1600000000: 0.0,
+        1500000000: 0.0,
+        1400000000: 0.1765536723163842,
+        1300000000: 0.4943502824858757,
+        1200000000: 0.7944915254237288,
+        1100000000: 1.2226341807909604,
+        1000000000: 3.0632062146892656,
+        900000000: 5.680614406779661,
+        800000000: 3.6679025423728815,
+        700000000: 2.379060734463277,
+        600000000: 1.4124293785310735,
+        500000000: 2.599752824858757,
+        400000000: 3.0102401129943503,
+        300000000: 3.650247175141243,
+        200000000: 67.73481638418079
+      },
+      'cstate_residency_percent': {
+        'C0': 76.76226164383562,
+        'C1': 0.3189164383561644,
+        'C2': 0.4544301369863014,
+        'C3': 22.4643917808219178
+      }
+    },
+    'cpu1': {
+      'frequency_percent': {
+        1700000000: 2.4713816665187682,
+        1600000000: 0.0,
+        1500000000: 0.0,
+        1400000000: 0.1419824296743278,
+        1300000000: 0.44813204365959713,
+        1200000000: 0.8430206761913214,
+        1100000000: 0.9761292040110037,
+        1000000000: 2.1252994941875945,
+        900000000: 5.69260803975508,
+        800000000: 4.033188392936374,
+        700000000: 2.4403230100275093,
+        600000000: 1.526311118999024,
+        500000000: 2.3249622859171177,
+        400000000: 2.3338361877717633,
+        300000000: 3.1812938148904073,
+        200000000: 71.46153163546012
+      },
+      'cstate_residency_percent': {
+        'C0': 90.5902150684931507,
+        'C1': 0.2208054794520548,
+        'C2': 0.1016082191780822,
+        'C3': 9.0873712328767123
+      }
+    }
+  }
+
+  def _assertPowerEqual(self, results, expected):
+    battery = results['component_utilization']['battery']
+    expected_battery = expected['component_utilization']['battery']
+    self.assertItemsEqual(battery.keys(), expected_battery.keys())
+    for value in battery:
+      self.assertAlmostEqual(battery[value], expected_battery[value])
+
+    self.assertAlmostEqual(results['energy_consumption_mwh'],
+                           expected['energy_consumption_mwh'])
+    self.assertAlmostEqual(results['power_samples_mw'][0],
+                           expected['power_samples_mw'][0])
+    self.assertAlmostEqual(results['power_samples_mw'][1],
+                           expected['power_samples_mw'][1])
+
+  def testParsePower(self):
+    results = cros_power_monitor.CrosPowerMonitor.ParsePower(
+        self.initial_power, self.final_power, 0.2)
+    self._assertPowerEqual(results, self.expected_power)
+
+  def testParseIncompletePowerState(self):
+    """Test the case where dump_power_status only outputs partial fields.
+
+    CrosPowerMonitor hard-coded expected fields from dump_power_status,
+    this test ensures it parses successfully when expected fields does not
+    exist. It's mainly for backward compatibility.
+    """
+    results = cros_power_monitor.CrosPowerMonitor.ParsePower(
+        self.initial_power, self.incomplete_final_power, 0.2)
+    self._assertPowerEqual(results, self.expected_incomplete_power)
+
+
+  def testSplitSample(self):
+    sample = self.initial_power + '\n1408739546\n'
+    power, time = cros_power_monitor.CrosPowerMonitor.SplitSample(sample)
+    self.assertEqual(power, self.initial_power)
+    self.assertEqual(time, 1408739546)
+
+  def testCombineResults(self):
+    result = cros_power_monitor.CrosPowerMonitor.CombineResults(
+        self.expected_cpu, self.expected_power)
+    comp_util = result['component_utilization']
+    # Test power values.
+    self.assertEqual(result['energy_consumption_mwh'],
+                     self.expected_power['energy_consumption_mwh'])
+    self.assertEqual(result['power_samples_mw'],
+                     self.expected_power['power_samples_mw'])
+    self.assertEqual(comp_util['battery'],
+                     self.expected_power['component_utilization']['battery'])
+    # Test frequency values.
+    self.assertDictEqual(
+        comp_util['whole_package']['frequency_percent'],
+        self.expected_cpu['whole_package']['frequency_percent'])
+    self.assertDictEqual(
+        comp_util['cpu0']['frequency_percent'],
+        self.expected_cpu['cpu0']['frequency_percent'])
+    self.assertDictEqual(
+        comp_util['cpu1']['frequency_percent'],
+        self.expected_cpu['cpu1']['frequency_percent'])
+    # Test c-state residency values.
+    self.assertDictEqual(
+        comp_util['whole_package']['cstate_residency_percent'],
+        self.expected_cpu['whole_package']['cstate_residency_percent'])
+    self.assertDictEqual(
+        comp_util['cpu0']['cstate_residency_percent'],
+        self.expected_cpu['cpu0']['cstate_residency_percent'])
+    self.assertDictEqual(
+        comp_util['cpu1']['cstate_residency_percent'],
+        self.expected_cpu['cpu1']['cstate_residency_percent'])
+
+  def testCanMonitorPower(self):
+    # TODO(tmandel): Add a test here where the device cannot monitor power.
+    initial_status = cros_power_monitor.CrosPowerMonitor.ParsePowerStatus(
+        self.initial_power)
+    final_status = cros_power_monitor.CrosPowerMonitor.ParsePowerStatus(
+        self.final_power)
+    self.assertTrue(cros_power_monitor.CrosPowerMonitor.IsOnBatteryPower(
+        initial_status, 'peppy'))
+    self.assertTrue(cros_power_monitor.CrosPowerMonitor.IsOnBatteryPower(
+        final_status, 'butterfly'))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/monsoon_power_monitor.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/monsoon_power_monitor.py
new file mode 100644
index 0000000..abbd9f4
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/monsoon_power_monitor.py
@@ -0,0 +1,120 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import json
+import logging
+import multiprocessing
+import tempfile
+import time
+
+from telemetry.core import exceptions
+from telemetry.internal.platform.power_monitor import android_power_monitor_base
+from telemetry.internal.platform.profiler import monsoon
+
+
+def _MonitorPower(device, is_collecting, output):
+  """Monitoring process
+     Args:
+       device: A profiler.monsoon object to collect samples from.
+       is_collecting: the event to synchronize on.
+       output: opened file to write the samples.
+  """
+  with output:
+    samples = []
+    start_time = None
+    end_time = None
+    try:
+      device.StartDataCollection()
+      is_collecting.set()
+      # First sample also calibrate the computation.
+      device.CollectData()
+      start_time = time.time()
+      while is_collecting.is_set():
+        new_data = device.CollectData()
+        assert new_data, 'Unable to collect data from device'
+        samples += new_data
+      end_time = time.time()
+    finally:
+      device.StopDataCollection()
+    result = {
+      'duration_s': end_time - start_time,
+      'samples': samples
+    }
+    json.dump(result, output)
+
+
+class MonsoonPowerMonitor(android_power_monitor_base.AndroidPowerMonitorBase):
+  def __init__(self, _, platform_backend):
+    super(MonsoonPowerMonitor, self).__init__()
+    self._powermonitor_process = None
+    self._powermonitor_output_file = None
+    self._is_collecting = None
+    self._monsoon = None
+    self._platform = platform_backend
+    try:
+      self._monsoon = monsoon.Monsoon(wait=False)
+      # Nominal Li-ion voltage is 3.7V, but it puts out 4.2V at max capacity.
+      # Use 4.0V to simulate a "~80%" charged battery. Google "li-ion voltage
+      # curve". This is true only for a single cell. (Most smartphones, some
+      # tablets.)
+      self._monsoon.SetVoltage(4.0)
+    except EnvironmentError:
+      self._monsoon = None
+
+  def CanMonitorPower(self):
+    return self._monsoon is not None
+
+  def StartMonitoringPower(self, browser):
+    self._CheckStart()
+    self._powermonitor_output_file = tempfile.TemporaryFile()
+    self._is_collecting = multiprocessing.Event()
+    self._powermonitor_process = multiprocessing.Process(
+        target=_MonitorPower,
+        args=(self._monsoon,
+              self._is_collecting,
+              self._powermonitor_output_file))
+    # Ensure child is not left behind: parent kills daemonic children on exit.
+    self._powermonitor_process.daemon = True
+    self._powermonitor_process.start()
+    if not self._is_collecting.wait(timeout=0.5):
+      self._powermonitor_process.terminate()
+      raise exceptions.ProfilingException('Failed to start data collection.')
+
+  def StopMonitoringPower(self):
+    self._CheckStop()
+    try:
+      # Tell powermonitor to take an immediate sample and join.
+      self._is_collecting.clear()
+      self._powermonitor_process.join()
+      with self._powermonitor_output_file:
+        self._powermonitor_output_file.seek(0)
+        powermonitor_output = self._powermonitor_output_file.read()
+      assert powermonitor_output, 'PowerMonitor produced no output'
+      return MonsoonPowerMonitor.ParseSamplingOutput(powermonitor_output)
+    finally:
+      self._powermonitor_output_file = None
+      self._powermonitor_process = None
+      self._is_collecting = None
+
+  @staticmethod
+  def ParseSamplingOutput(powermonitor_output):
+    """Parse the output of of the samples collector process.
+
+    Returns:
+        Dictionary in the format returned by StopMonitoringPower().
+    """
+    result = json.loads(powermonitor_output)
+    if result['samples']:
+      timedelta_h = (result['duration_s'] / len(result['samples'])) / 3600.0
+      power_samples = [current_a * voltage_v * 10**3
+                       for (current_a, voltage_v) in result['samples']]
+      total_energy_consumption_mwh = sum(power_samples) * timedelta_h
+    else:
+      logging.warning('Sample information not available.')
+      power_samples = []
+      total_energy_consumption_mwh = 0
+
+    return {'identifier':'monsoon',
+            'power_samples_mw':power_samples,
+            'monsoon_energy_consumption_mwh':total_energy_consumption_mwh}
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/monsoon_power_monitor_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/monsoon_power_monitor_unittest.py
new file mode 100644
index 0000000..f2f35c3
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/monsoon_power_monitor_unittest.py
@@ -0,0 +1,21 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import json
+import unittest
+
+from telemetry.internal.platform.power_monitor import monsoon_power_monitor
+
+
+class MonsoonPowerMonitorTest(unittest.TestCase):
+
+  def testEnergyComsumption(self):
+    data = {
+        'duration_s': 3600.0,
+        'samples': [(1.0, 1.0), (2.0, 2.0), (3.0, 3.0), (4.0, 4.0)]
+    }
+    results = monsoon_power_monitor.MonsoonPowerMonitor.ParseSamplingOutput(
+        json.dumps(data))
+    self.assertEqual(results['power_samples_mw'], [1000, 4000, 9000, 16000])
+    self.assertEqual(results['monsoon_energy_consumption_mwh'], 7500)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/msr_power_monitor.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/msr_power_monitor.py
new file mode 100644
index 0000000..5cf3d16
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/msr_power_monitor.py
@@ -0,0 +1,143 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+import platform
+import re
+
+from telemetry import decorators
+from telemetry.internal.platform import power_monitor
+
+
+MSR_RAPL_POWER_UNIT = 0x606
+MSR_PKG_ENERGY_STATUS = 0x611  # Whole package
+MSR_PP0_ENERGY_STATUS = 0x639  # Core
+MSR_PP1_ENERGY_STATUS = 0x641  # Uncore
+MSR_DRAM_ENERGY_STATUS = 0x619
+IA32_PACKAGE_THERM_STATUS = 0x1b1
+IA32_TEMPERATURE_TARGET = 0x1a2
+
+
+def _JoulesToMilliwattHours(value_joules):
+  return value_joules * 1000 / 3600.
+
+
+def _IsSandyBridgeOrLater(vendor, family, model):
+  # Model numbers from:
+  # https://software.intel.com/en-us/articles/intel-architecture-and-processor-identification-with-cpuid-model-and-family-numbers
+  # http://www.speedtraq.com
+  return ('Intel' in vendor and family == 6 and
+          (model in (0x2A, 0x2D) or model >= 0x30))
+
+
+class MsrPowerMonitor(power_monitor.PowerMonitor):
+  def __init__(self, backend):
+    super(MsrPowerMonitor, self).__init__()
+    self._backend = backend
+    self._start_energy_j = None
+    self._start_temp_c = None
+
+  def CanMonitorPower(self):
+    raise NotImplementedError()
+
+  def StartMonitoringPower(self, browser):
+    self._CheckStart()
+    self._start_energy_j = self._PackageEnergyJoules()
+    self._start_temp_c = self._TemperatureCelsius()
+
+  def StopMonitoringPower(self):
+    self._CheckStop()
+    energy_consumption_j = self._PackageEnergyJoules() - self._start_energy_j
+    average_temp_c = (self._TemperatureCelsius() + self._start_temp_c) / 2.
+    if energy_consumption_j < 0:  # Correct overflow.
+      # The energy portion of the MSR is 4 bytes.
+      energy_consumption_j += 2 ** 32 * self._EnergyMultiplier()
+
+    self._start_energy_j = None
+    self._start_temp_c = None
+
+    return {
+        'identifier': 'msr',
+        'energy_consumption_mwh': _JoulesToMilliwattHours(energy_consumption_j),
+        'platform_info': {
+            'average_temperature_c': average_temp_c,
+        },
+    }
+
+  @decorators.Cache
+  def _EnergyMultiplier(self):
+    return 0.5 ** self._backend.ReadMsr(MSR_RAPL_POWER_UNIT, 8, 5)
+
+  def _PackageEnergyJoules(self):
+    return (self._backend.ReadMsr(MSR_PKG_ENERGY_STATUS, 0, 32) *
+            self._EnergyMultiplier())
+
+  def _TemperatureCelsius(self):
+    tcc_activation_temp = self._backend.ReadMsr(IA32_TEMPERATURE_TARGET, 16, 7)
+    if tcc_activation_temp <= 0:
+      tcc_activation_temp = 105
+    package_temp_headroom = self._backend.ReadMsr(
+        IA32_PACKAGE_THERM_STATUS, 16, 7)
+    return tcc_activation_temp - package_temp_headroom
+
+  def _CheckMSRs(self):
+    try:
+      if self._PackageEnergyJoules() <= 0:
+        logging.info('Cannot monitor power: no energy readings.')
+        return False
+
+      if self._TemperatureCelsius() <= 0:
+        logging.info('Cannot monitor power: no temperature readings.')
+        return False
+    except OSError as e:
+      logging.info('Cannot monitor power: %s' % e)
+      return False
+    return True
+
+
+class MsrPowerMonitorLinux(MsrPowerMonitor):
+  def CanMonitorPower(self):
+    vendor = None
+    family = None
+    model = None
+    cpuinfo = open('/proc/cpuinfo').read().splitlines()
+    for line in cpuinfo:
+      if vendor and family and model:
+        break
+      if line.startswith('vendor_id'):
+        vendor = line.split('\t')[1]
+      elif line.startswith('cpu family'):
+        family = int(line.split(' ')[2])
+      elif line.startswith('model\t\t'):
+        model = int(line.split(' ')[1])
+    if not _IsSandyBridgeOrLater(vendor, family, model):
+      logging.info('Cannot monitor power: pre-Sandy Bridge CPU.')
+      return False
+
+    if not self._CheckMSRs():
+      logging.info('Try running tools/telemetry/build/linux_setup_msr.py.')
+      return False
+
+    return True
+
+
+class MsrPowerMonitorWin(MsrPowerMonitor):
+  def CanMonitorPower(self):
+    family, model = map(int, re.match('.+ Family ([0-9]+) Model ([0-9]+)',
+                        platform.processor()).groups())
+    if not _IsSandyBridgeOrLater(platform.processor(), family, model):
+      logging.info('Cannot monitor power: pre-Sandy Bridge CPU.')
+      return False
+
+    try:
+      return self._CheckMSRs()
+    finally:
+      # Since _CheckMSRs() starts the MSR server on win platform, we must close
+      # it after checking to avoid leaking msr server process.
+      self._backend.CloseMsrServer()
+
+  def StopMonitoringPower(self):
+    power_statistics = super(MsrPowerMonitorWin, self).StopMonitoringPower()
+    self._backend.CloseMsrServer()
+    return power_statistics
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/msr_power_monitor_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/msr_power_monitor_unittest.py
new file mode 100644
index 0000000..5734308
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/msr_power_monitor_unittest.py
@@ -0,0 +1,29 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+import time
+import unittest
+
+from telemetry import decorators
+from telemetry.internal.platform.power_monitor import msr_power_monitor
+from telemetry.internal.platform import win_platform_backend
+
+
+class MsrPowerMonitorTest(unittest.TestCase):
+  @decorators.Enabled('xp', 'win7', 'win8')  # http://crbug.com/479337
+  def testMsrRuns(self):
+    platform_backend = win_platform_backend.WinPlatformBackend()
+    power_monitor = msr_power_monitor.MsrPowerMonitorWin(platform_backend)
+    if not power_monitor.CanMonitorPower():
+      logging.warning('Test not supported on this platform.')
+      return
+
+    power_monitor.StartMonitoringPower(None)
+    time.sleep(0.01)
+    statistics = power_monitor.StopMonitoringPower()
+
+    self.assertEqual(statistics['identifier'], 'msr')
+    self.assertIn('energy_consumption_mwh', statistics)
+    self.assertGreater(statistics['energy_consumption_mwh'], 0)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/pm_mock.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/pm_mock.py
new file mode 100644
index 0000000..d9bbab5
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/pm_mock.py
@@ -0,0 +1,61 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+class MockBrowserBackend(object):
+  def __init__(self, package):
+    self.package = package
+
+class MockBrowser(object):
+  def __init__(self, package):
+    self._browser_backend = MockBrowserBackend(package)
+
+class MockBattery(object):
+  def __init__(self,
+               power_results,
+               starts_charging=True,
+               voltage=4.0,
+               fuelgauge=None):
+    # voltage in millivolts
+    self._power_results = power_results
+    self._charging = starts_charging
+    self._voltage = voltage
+    self._fuelgauge = fuelgauge if fuelgauge else []
+    self._fuel_idx = 0
+
+  def SupportsFuelGauge(self):
+    return len(self._fuelgauge) >= 0
+
+  def GetFuelGaugeChargeCounter(self):
+    try:
+      x = self._fuelgauge[self._fuel_idx]
+      self._fuel_idx += 1
+      return x
+    except IndexError:
+      assert False, "Too many GetFuelGaugeChargeCounter() calls."
+
+  def GetCharging(self):
+    return self._charging
+
+  def SetCharging(self, charging):
+    if charging:
+      assert not self._charging, "Mock battery already charging."
+      self._charging = True
+    else:
+      assert self._charging, "Mock battery already not charging."
+      self._charging = False
+
+  def GetPowerData(self):
+    return self._power_results
+
+  def GetBatteryInfo(self):
+    # the voltage returned by GetBatteryInfo() is in millivolts
+    return {'voltage': int(self._voltage*1000)}
+
+class MockPlatformBackend(object):
+  def __init__(self, command_dict=None):
+    self._cdict = (command_dict if command_dict else {})
+
+  def RunCommand(self, command):
+    assert command in self._cdict, "Mock platform error: Unexpected command."
+    return self._cdict[command]
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/powermetrics_power_monitor.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/powermetrics_power_monitor.py
new file mode 100644
index 0000000..89d141a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/powermetrics_power_monitor.py
@@ -0,0 +1,316 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import collections
+import logging
+import os
+import plistlib
+import shutil
+import tempfile
+import xml.parsers.expat
+
+from telemetry.core import os_version
+from telemetry import decorators
+from telemetry.internal.platform import power_monitor
+
+import py_utils
+
+
+# TODO: rename this class (seems like this is used by mac)
+class PowerMetricsPowerMonitor(power_monitor.PowerMonitor):
+
+  def __init__(self, backend):
+    super(PowerMetricsPowerMonitor, self).__init__()
+    self._powermetrics_process = None
+    self._backend = backend
+    self._output_filename = None
+    self._output_directory = None
+
+  @property
+  def binary_path(self):
+    return '/usr/bin/powermetrics'
+
+  def StartMonitoringPower(self, browser):
+    self._CheckStart()
+    # Empirically powermetrics creates an empty output file immediately upon
+    # starting.  We detect file creation as a signal that measurement has
+    # started.  In order to avoid various race conditions in tempfile creation
+    # we create a temp directory and have powermetrics create it's output
+    # there rather than say, creating a tempfile, deleting it and reusing its
+    # name.
+    self._output_directory = tempfile.mkdtemp()
+    self._output_filename = os.path.join(self._output_directory,
+                                         'powermetrics.output')
+    args = ['-f', 'plist',
+            '-u', self._output_filename,
+            '-i0',
+            '--show-usage-summary']
+    self._powermetrics_process = self._backend.LaunchApplication(
+        self.binary_path, args, elevate_privilege=True)
+
+    # Block until output file is written to ensure this function call is
+    # synchronous in respect to powermetrics starting.
+    def _OutputFileExists():
+      return os.path.isfile(self._output_filename)
+    py_utils.WaitFor(_OutputFileExists, 1)
+
+  @decorators.Cache
+  def CanMonitorPower(self):
+    mavericks_or_later = (
+        self._backend.GetOSVersionName() >= os_version.MAVERICKS)
+    binary_path = self.binary_path
+    return mavericks_or_later and self._backend.CanLaunchApplication(
+        binary_path)
+
+  @staticmethod
+  def _ParsePlistString(plist_string):
+    """Wrapper to parse a plist from a string and catch any errors.
+
+    Sometimes powermetrics will exit in the middle of writing it's output,
+    empirically it seems that it always writes at least one sample in it's
+    entirety so we can safely ignore any errors in it's output.
+
+    Returns:
+        Parser output on successful parse, None on parse error.
+    """
+    try:
+      return plistlib.readPlistFromString(plist_string)
+    except xml.parsers.expat.ExpatError:
+      return None
+
+  @staticmethod
+  def ParsePowerMetricsOutput(powermetrics_output):
+    """Parse output of powermetrics command line utility.
+
+    Returns:
+        Dictionary in the format returned by StopMonitoringPower() or None
+        if |powermetrics_output| is empty - crbug.com/353250 .
+    """
+    if len(powermetrics_output) == 0:
+      logging.warning('powermetrics produced zero length output')
+      return {}
+
+    # Container to collect samples for running averages.
+    # out_path - list containing the key path in the output dictionary.
+    # src_path - list containing the key path to get the data from in
+    #    powermetrics' output.
+    def ConstructMetric(out_path, src_path):
+      RunningAverage = collections.namedtuple('RunningAverage', [
+          'out_path', 'src_path', 'samples'])
+      return RunningAverage(out_path, src_path, [])
+
+    # List of RunningAverage objects specifying metrics we want to aggregate.
+    metrics = [
+        ConstructMetric(
+            ['platform_info', 'average_frequency_hz'],
+            ['processor', 'freq_hz']),
+        ConstructMetric(
+            ['platform_info', 'idle_percent'],
+            ['processor', 'packages', 0, 'c_state_ratio'])]
+
+    def DataWithMetricKeyPath(metric, powermetrics_output):
+      """Retrieve the sample from powermetrics' output for a given metric.
+
+      Args:
+          metric: The RunningAverage object we want to collect a new sample for.
+          powermetrics_output: Dictionary containing powermetrics output.
+
+      Returns:
+          The sample corresponding to |metric|'s keypath."""
+      # Get actual data corresponding to key path.
+      out_data = powermetrics_output
+      for k in metric.src_path:
+        out_data = out_data[k]
+
+      assert type(out_data) in [int, float], (
+          'Was expecting a number: %s (%s)' % (type(out_data), out_data))
+      return float(out_data)
+
+    sample_durations = []
+    total_energy_consumption_mwh = 0
+    # powermetrics outputs multiple plists separated by null terminators.
+    raw_plists = powermetrics_output.split('\0')
+    raw_plists = [x for x in raw_plists if len(x) > 0]
+    assert len(raw_plists) == 1
+
+    # -------- Examine contents of first plist for systems specs. --------
+    plist = PowerMetricsPowerMonitor._ParsePlistString(raw_plists[0])
+    if not plist:
+      logging.warning('powermetrics produced invalid output, output length: '
+                      '%d', len(powermetrics_output))
+      return {}
+
+    # Powermetrics doesn't record power usage when running on a VM.
+    hw_model = plist.get('hw_model')
+    if hw_model and hw_model.startswith('VMware'):
+      return {}
+
+    if 'GPU' in plist:
+      metrics.extend([
+          ConstructMetric(
+              ['component_utilization', 'gpu', 'average_frequency_hz'],
+              ['GPU', 0, 'freq_hz']),
+          ConstructMetric(
+              ['component_utilization', 'gpu', 'idle_percent'],
+              ['GPU', 0, 'c_state_ratio'])])
+
+    # There's no way of knowing ahead of time how many cpus and packages the
+    # current system has. Iterate over cores and cpus - construct metrics for
+    # each one.
+    if 'processor' in plist:
+      core_dict = plist['processor']['packages'][0]['cores']
+      num_cores = len(core_dict)
+      cpu_num = 0
+      for core_idx in xrange(num_cores):
+        num_cpus = len(core_dict[core_idx]['cpus'])
+        base_src_path = ['processor', 'packages', 0, 'cores', core_idx]
+        for cpu_idx in xrange(num_cpus):
+          base_out_path = ['component_utilization', 'cpu%d' % cpu_num]
+          # C State ratio is per-package, component CPUs of that package may
+          # have different frequencies.
+          metrics.append(ConstructMetric(
+              base_out_path + ['average_frequency_hz'],
+              base_src_path + ['cpus', cpu_idx, 'freq_hz']))
+          metrics.append(ConstructMetric(
+              base_out_path + ['idle_percent'],
+              base_src_path + ['c_state_ratio']))
+          cpu_num += 1
+
+    # -------- Parse Data Out of Plists --------
+    plist = PowerMetricsPowerMonitor._ParsePlistString(raw_plists[0])
+    if not plist:
+      logging.error('Error parsing plist.')
+      return {}
+
+    # Duration of this sample.
+    sample_duration_ms = int(plist['elapsed_ns']) / 10 ** 6
+    sample_durations.append(sample_duration_ms)
+
+    if 'processor' not in plist:
+      logging.error("'processor' field not found in plist.")
+      return {}
+    processor = plist['processor']
+
+    total_energy_consumption_mwh = (
+        (float(processor.get('package_joules', 0)) / 3600.) * 10 ** 3)
+
+    for m in metrics:
+      try:
+        m.samples.append(DataWithMetricKeyPath(m, plist))
+      except KeyError:
+        # Old CPUs don't have c-states, so if data is missing, just ignore it.
+        logging.info('Field missing from powermetrics output: %s', m.src_path)
+        continue
+
+    # -------- Collect and Process Data --------
+    out_dict = {}
+    out_dict['identifier'] = 'powermetrics'
+    out_dict['energy_consumption_mwh'] = total_energy_consumption_mwh
+
+    def StoreMetricAverage(metric, sample_durations, out):
+      """Calculate average value of samples in a metric and store in output
+         path as specified by metric.
+
+      Args:
+          metric: A RunningAverage object containing samples to average.
+          sample_durations: A list which parallels the samples list containing
+              the time slice for each sample.
+          out: The output dicat, average is stored in the location specified by
+              metric.out_path.
+      """
+      if len(metric.samples) == 0:
+        return
+
+      assert len(metric.samples) == len(sample_durations)
+      avg = 0
+      for i in xrange(len(metric.samples)):
+        avg += metric.samples[i] * sample_durations[i]
+      avg /= sum(sample_durations)
+
+      # Store data in output, creating empty dictionaries as we go.
+      for k in metric.out_path[:-1]:
+        if not out.has_key(k):
+          out[k] = {}
+        out = out[k]
+      out[metric.out_path[-1]] = avg
+
+    for m in metrics:
+      StoreMetricAverage(m, sample_durations, out_dict)
+
+    if 'tasks' not in plist:
+      logging.error("'tasks' field not found in plist.")
+      return {}
+
+    # The following CPU metrics are already time-normalized, and segmented by
+    # process. Sum the metrics across all Chrome processes.
+    cputime = 0
+    energy_impact = 0
+    browser_process_count = 0
+    idle_wakeups = 0
+    for task in plist['tasks']:
+      if 'Chrome' in task['name'] or 'Chromium' in task['name']:
+        if 'Helper' not in task['name']:
+          browser_process_count += 1
+        cputime += float(task['cputime_ms_per_s'])
+        energy_impact += float(task.get('energy_impact', 0))
+        idle_wakeups += float(task['idle_wakeups_per_s'])
+    if browser_process_count == 0:
+      logging.warning('No Chrome or Chromium browser process found with '
+                      'powermetrics. Chrome CPU metrics will not be emitted.')
+      return {}
+    elif browser_process_count >= 2:
+      logging.warning('powermetrics found more than one Chrome or Chromium '
+                      'browser. Chrome CPU metrics will not be emitted.')
+      # During Telemetry unit tests, there may be multiple Chrome browsers
+      # present. Don't add cpu metrics, but don't return {} either.
+    else:  # browser_process_count == 1:
+      chrome_dict = {}
+      chrome_dict['cputime_ms_per_s'] = cputime
+      chrome_dict['energy_impact'] = energy_impact
+      chrome_dict['idle_wakeups_per_s'] = idle_wakeups
+      out_dict['component_utilization']['chrome'] = chrome_dict
+
+    return out_dict
+
+  def _KillPowerMetricsProcess(self):
+    """Kill a running powermetrics process."""
+    try:
+      if self._powermetrics_process.poll() is None:
+        self._powermetrics_process.terminate()
+    except OSError as e:
+      logging.warning(
+          'Error when trying to terminate powermetric process: %s', repr(e))
+      if self._powermetrics_process.poll() is None:
+        # terminate() can fail when Powermetrics does not have the SetUID set.
+        self._backend.LaunchApplication(
+          '/usr/bin/pkill',
+          ['-SIGTERM', os.path.basename(self.binary_path)],
+          elevate_privilege=True)
+
+  def StopMonitoringPower(self):
+    self._CheckStop()
+    # Tell powermetrics to take an immediate sample.
+    try:
+      self._KillPowerMetricsProcess()
+      (power_stdout, power_stderr) = self._powermetrics_process.communicate()
+      returncode = self._powermetrics_process.returncode
+      assert returncode in [0, -15], (
+          """powermetrics error
+          return code=%d
+          stdout=(%s)
+          stderr=(%s)""" % (returncode, power_stdout, power_stderr))
+
+      with open(self._output_filename, 'rb') as output_file:
+        powermetrics_output = output_file.read()
+      return PowerMetricsPowerMonitor.ParsePowerMetricsOutput(
+          powermetrics_output)
+    except Exception as e:
+      logging.warning(
+          'Error when trying to collect power monitoring data: %s', repr(e))
+      return PowerMetricsPowerMonitor.ParsePowerMetricsOutput('')
+    finally:
+      shutil.rmtree(self._output_directory)
+      self._output_directory = None
+      self._output_filename = None
+      self._powermetrics_process = None
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/powermetrics_power_monitor_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/powermetrics_power_monitor_unittest.py
new file mode 100644
index 0000000..1c5a65c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/powermetrics_power_monitor_unittest.py
@@ -0,0 +1,76 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+import os
+import unittest
+
+from telemetry.core import os_version
+from telemetry.core import util
+from telemetry import decorators
+from telemetry.internal.platform import mac_platform_backend
+from telemetry.internal.platform.power_monitor import powermetrics_power_monitor
+
+
+def _parsePowerMetricsDataFromTestFile(output_file):
+  test_data_path = os.path.join(util.GetUnittestDataDir(), output_file)
+  with open(test_data_path, 'r') as f:
+    process_output = f.read()
+  return (powermetrics_power_monitor.PowerMetricsPowerMonitor.
+      ParsePowerMetricsOutput(process_output))
+
+
+class PowerMetricsPowerMonitorTest(unittest.TestCase):
+  @decorators.Enabled('mac')
+  def testCanMonitorPowerUsage(self):
+    backend = mac_platform_backend.MacPlatformBackend()
+    power_monitor = powermetrics_power_monitor.PowerMetricsPowerMonitor(backend)
+    mavericks_or_later = (
+        backend.GetOSVersionName() >= os_version.MAVERICKS)
+    # Should always be able to monitor power usage on OS Version >= 10.9 .
+    self.assertEqual(power_monitor.CanMonitorPower(), mavericks_or_later,
+        "Error checking powermetrics availability: '%s'" % '|'.join(os.uname()))
+
+  @decorators.Enabled('mac')
+  def testParseEmptyPowerMetricsOutput(self):
+    # Important to handle zero length powermetrics outout - crbug.com/353250 .
+    self.assertFalse(powermetrics_power_monitor.PowerMetricsPowerMonitor.
+        ParsePowerMetricsOutput(''))
+
+  @decorators.Enabled('mac')
+  def testParsePowerMetricsOutputFromVM(self):
+    # Don't fail when running on VM - crbug.com/423688.
+    self.assertEquals({},
+        _parsePowerMetricsDataFromTestFile('powermetrics_vmware.output'))
+
+  @decorators.Enabled('mac')
+  def testParsePowerMetricsOutput(self):
+    power_monitor = powermetrics_power_monitor.PowerMetricsPowerMonitor(
+        mac_platform_backend.MacPlatformBackend())
+    if not power_monitor.CanMonitorPower():
+      logging.warning('Test not supported on this platform.')
+      return
+
+    # Not supported on Mac at this time.
+    self.assertFalse(power_monitor.CanMeasurePerApplicationPower())
+
+    # Supported hardware reports power samples and energy consumption.
+    result = _parsePowerMetricsDataFromTestFile('powermetrics_output.output')
+
+    self.assertTrue(result['energy_consumption_mwh'] > 0)
+
+    # Verify platform info exists in output.
+    self.assertTrue(result['platform_info']['average_frequency_hz'] > 0)
+    self.assertTrue(result['platform_info']['idle_percent'] > 0)
+
+    # Verify that all component entries exist in output.
+    component_utilization = result['component_utilization']
+    for k in ['gpu'] + ['cpu%d' % x for x in range(8)]:
+      self.assertTrue(component_utilization[k]['average_frequency_hz'] > 0)
+      self.assertTrue(component_utilization[k]['idle_percent'] > 0)
+
+    # Unsupported hardware doesn't.
+    result = _parsePowerMetricsDataFromTestFile(
+        'powermetrics_output_unsupported_hardware.output')
+    self.assertNotIn('energy_consumption_mwh', result)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/sysfs_power_monitor.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/sysfs_power_monitor.py
new file mode 100644
index 0000000..85e5d30
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/sysfs_power_monitor.py
@@ -0,0 +1,230 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import collections
+import logging
+import os
+import re
+
+from telemetry.internal.platform import power_monitor
+from telemetry import decorators
+
+
+CPU_PATH = '/sys/devices/system/cpu/'
+
+
+class SysfsPowerMonitor(power_monitor.PowerMonitor):
+  """PowerMonitor that relies on sysfs to monitor CPU statistics on several
+  different platforms.
+  """
+  # TODO(rnephew): crbug.com/513453
+  # Convert all platforms to use standalone power monitors.
+  def __init__(self, linux_based_platform_backend, standalone=False):
+    """Constructor.
+
+    Args:
+        linux_based_platform_backend: A LinuxBasedPlatformBackend object.
+        standalone: If it is not wrapping another monitor, set to True.
+
+    Attributes:
+        _cpus: A list of the CPUs on the target device.
+        _end_time: The time the test stopped monitoring power.
+        _final_cstate: The c-state residency times after the test.
+        _final_freq: The CPU frequency times after the test.
+        _initial_cstate: The c-state residency times before the test.
+        _initial_freq: The CPU frequency times before the test.
+        _platform: A LinuxBasedPlatformBackend object associated with the
+            target platform.
+        _start_time: The time the test started monitoring power.
+    """
+    super(SysfsPowerMonitor, self).__init__()
+    self._cpus = None
+    self._final_cstate = None
+    self._final_freq = None
+    self._initial_cstate = None
+    self._initial_freq = None
+    self._platform = linux_based_platform_backend
+    self._standalone = standalone
+
+  @decorators.Cache
+  def CanMonitorPower(self):
+    return bool(self._platform.RunCommand(
+        'if [ -e %s ]; then echo true; fi' % CPU_PATH))
+
+  def StartMonitoringPower(self, browser):
+    del browser  # unused
+    self._CheckStart()
+    if self.CanMonitorPower():
+      self._cpus = filter(  # pylint: disable=deprecated-lambda
+          lambda x: re.match(r'^cpu[0-9]+', x),
+          self._platform.RunCommand('ls %s' % CPU_PATH).split())
+      self._initial_freq = self.GetCpuFreq()
+      self._initial_cstate = self.GetCpuState()
+
+  def StopMonitoringPower(self):
+    self._CheckStop()
+    try:
+      out = {}
+      if SysfsPowerMonitor.CanMonitorPower(self):
+        self._final_freq = self.GetCpuFreq()
+        self._final_cstate = self.GetCpuState()
+        frequencies = SysfsPowerMonitor.ComputeCpuStats(
+            SysfsPowerMonitor.ParseFreqSample(self._initial_freq),
+            SysfsPowerMonitor.ParseFreqSample(self._final_freq))
+        cstates = SysfsPowerMonitor.ComputeCpuStats(
+            self._platform.ParseCStateSample(self._initial_cstate),
+            self._platform.ParseCStateSample(self._final_cstate))
+        for cpu in frequencies:
+          out[cpu] = {'frequency_percent': frequencies.get(cpu)}
+          out[cpu] = {'cstate_residency_percent': cstates.get(cpu)}
+      if self._standalone:
+        return self.CombineResults(out, {})
+      return out
+    finally:
+      self._initial_cstate = None
+      self._initial_freq = None
+
+  def GetCpuState(self):
+    """Retrieve CPU c-state residency times from the device.
+
+    Returns:
+        Dictionary containing c-state residency times for each CPU.
+    """
+    stats = {}
+    for cpu in self._cpus:
+      cpu_idle_path = os.path.join(CPU_PATH, cpu, 'cpuidle')
+      if not self._platform.PathExists(cpu_idle_path):
+        logging.warning(
+            'Cannot read cpu c-state residency times for %s due to %s not exist'
+            % (cpu, cpu_idle_path))
+        continue
+      cpu_state_path = os.path.join(cpu_idle_path, 'state*')
+      output = self._platform.RunCommand(
+          'cat %s %s %s; date +%%s' % (
+              os.path.join(cpu_state_path, 'name'),
+              os.path.join(cpu_state_path, 'time'),
+              os.path.join(cpu_state_path, 'latency')))
+      stats[cpu] = re.sub('\n\n+', '\n', output)
+    return stats
+
+  def GetCpuFreq(self):
+    """Retrieve CPU frequency times from the device.
+
+    Returns:
+        Dictionary containing frequency times for each CPU.
+    """
+    stats = {}
+    for cpu in self._cpus:
+      cpu_freq_path = os.path.join(
+          CPU_PATH, cpu, 'cpufreq/stats/time_in_state')
+      if not self._platform.PathExists(cpu_freq_path):
+        logging.warning(
+            'Cannot read cpu frequency times for %s due to %s not existing'
+            % (cpu, cpu_freq_path))
+        stats[cpu] = None
+        continue
+      try:
+        stats[cpu] = self._platform.GetFileContents(cpu_freq_path)
+      except Exception as e:
+        logging.warning(
+            'Cannot read cpu frequency times in %s due to error: %s' %
+            (cpu_freq_path, e.message))
+        stats[cpu] = None
+    return stats
+
+  @staticmethod
+  def ParseFreqSample(sample):
+    """Parse a single frequency sample.
+
+    Args:
+        sample: The single sample of frequency data to be parsed.
+
+    Returns:
+        A dictionary associating a frequency with a time.
+    """
+    sample_stats = {}
+    for cpu in sample:
+      frequencies = {}
+      if sample[cpu] is None:
+        sample_stats[cpu] = None
+        continue
+      for line in sample[cpu].splitlines():
+        pair = line.split()
+        freq = int(pair[0]) * 10 ** 3
+        timeunits = int(pair[1])
+        if freq in frequencies:
+          frequencies[freq] += timeunits
+        else:
+          frequencies[freq] = timeunits
+      sample_stats[cpu] = frequencies
+    return sample_stats
+
+  @staticmethod
+  def ComputeCpuStats(initial, final):
+    """Parse the CPU c-state and frequency values saved during monitoring.
+
+    Args:
+        initial: The parsed dictionary of initial statistics to be converted
+        into percentages.
+        final: The parsed dictionary of final statistics to be converted
+        into percentages.
+
+    Returns:
+        Dictionary containing percentages for each CPU as well as an average
+        across all CPUs.
+    """
+    cpu_stats = {}
+    # Each core might have different states or frequencies, so keep track of
+    # the total time in a state or frequency and how many cores report a time.
+    cumulative_times = collections.defaultdict(lambda: (0, 0))
+    for cpu in initial:
+      current_cpu = {}
+      total = 0
+      if not initial[cpu] or not final[cpu]:
+        cpu_stats[cpu] = collections.defaultdict(int)
+        continue
+      for state in initial[cpu]:
+        current_cpu[state] = final[cpu][state] - initial[cpu][state]
+        total += current_cpu[state]
+      if total == 0:
+        # Somehow it's possible for initial and final to have the same sum,
+        # but a different distribution, making total == 0. crbug.com/426430
+        cpu_stats[cpu] = collections.defaultdict(int)
+        continue
+      for state in current_cpu:
+        current_cpu[state] /= (float(total) / 100.0)
+        # Calculate the average c-state residency across all CPUs.
+        time, count = cumulative_times[state]
+        cumulative_times[state] = (time + current_cpu[state], count + 1)
+      cpu_stats[cpu] = current_cpu
+    average = {}
+    for state in cumulative_times:
+      time, count = cumulative_times[state]
+      average[state] = time / float(count)
+    cpu_stats['platform_info'] = average
+    return cpu_stats
+
+  @staticmethod
+  def CombineResults(cpu_stats, power_stats):
+    """Add frequency and c-state residency data to the power data.
+
+    Args:
+        cpu_stats: Dictionary containing CPU statistics.
+        power_stats: Dictionary containing power statistics.
+
+    Returns:
+        Dictionary in the format returned by StopMonitoringPower.
+    """
+    if not cpu_stats:
+      return power_stats
+    if 'component_utilization' not in power_stats:
+      power_stats['component_utilization'] = {}
+    if 'platform_info' in cpu_stats:
+      if 'platform_info' not in power_stats:
+        power_stats['platform_info'] = {}
+      power_stats['platform_info'].update(cpu_stats['platform_info'])
+      del cpu_stats['platform_info']
+    for cpu in cpu_stats:
+      power_stats['component_utilization'][cpu] = cpu_stats[cpu]
+    return power_stats
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/sysfs_power_monitor_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/sysfs_power_monitor_unittest.py
new file mode 100644
index 0000000..4389590
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/power_monitor/sysfs_power_monitor_unittest.py
@@ -0,0 +1,285 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from telemetry.internal.platform import android_platform_backend
+from telemetry.internal.platform.power_monitor import sysfs_power_monitor
+
+
+class SysfsPowerMonitorMonitorTest(unittest.TestCase):
+  initial_freq = {
+    'cpu0': '1700000 6227\n1600000 0\n1500000 0\n1400000 28\n1300000 22\n'
+            '1200000 14\n1100000 19\n1000000 22\n900000 14\n800000 20\n'
+            '700000 15\n600000 23\n500000 23\n400000 9\n300000 28\n200000 179',
+    'cpu1': '1700000 11491\n1600000 0\n1500000 0\n1400000 248\n1300000 1166\n'
+            '1200000 2082\n1100000 2943\n1000000 6560\n900000 12517\n'
+            '800000 8690\n700000 5105\n600000 3800\n500000 5131\n400000 5479\n'
+            '300000 7571\n200000 133618',
+    'cpu2': '1700000 1131',
+    'cpu3': '1700000 1131'
+  }
+  final_freq = {
+    'cpu0': '1700000 7159\n1600000 0\n1500000 0\n1400000 68\n1300000 134\n'
+            '1200000 194\n1100000 296\n1000000 716\n900000 1301\n800000 851\n'
+            '700000 554\n600000 343\n500000 612\n400000 691\n300000 855\n'
+            '200000 15525',
+    'cpu1': '1700000 12048\n1600000 0\n1500000 0\n1400000 280\n1300000 1267\n'
+            '1200000 2272\n1100000 3163\n1000000 7039\n900000 13800\n'
+            '800000 9599\n700000 5655\n600000 4144\n500000 5655\n400000 6005\n'
+            '300000 8288\n200000 149724',
+    'cpu2': None,
+    'cpu3': ''
+  }
+  expected_initial_freq = {
+    'cpu0': {
+      1700000000: 6227,
+      1600000000: 0,
+      1500000000: 0,
+      1400000000: 28,
+      1300000000: 22,
+      1200000000: 14,
+      1100000000: 19,
+      1000000000: 22,
+      900000000: 14,
+      800000000: 20,
+      700000000: 15,
+      600000000: 23,
+      500000000: 23,
+      400000000: 9,
+      300000000: 28,
+      200000000: 179
+    },
+    'cpu1': {
+      1700000000: 11491,
+      1600000000: 0,
+      1500000000: 0,
+      1400000000: 248,
+      1300000000: 1166,
+      1200000000: 2082,
+      1100000000: 2943,
+      1000000000: 6560,
+      900000000: 12517,
+      800000000: 8690,
+      700000000: 5105,
+      600000000: 3800,
+      500000000: 5131,
+      400000000: 5479,
+      300000000: 7571,
+      200000000: 133618
+    },
+    'cpu2': {
+      1700000000: 1131
+    },
+    'cpu3': {
+      1700000000: 1131
+    }
+  }
+  expected_final_freq = {
+    'cpu0': {
+      1700000000: 7159,
+      1600000000: 0,
+      1500000000: 0,
+      1400000000: 68,
+      1300000000: 134,
+      1200000000: 194,
+      1100000000: 296,
+      1000000000: 716,
+      900000000: 1301,
+      800000000: 851,
+      700000000: 554,
+      600000000: 343,
+      500000000: 612,
+      400000000: 691,
+      300000000: 855,
+      200000000: 15525
+    },
+    'cpu1': {
+      1700000000: 12048,
+      1600000000: 0,
+      1500000000: 0,
+      1400000000: 280,
+      1300000000: 1267,
+      1200000000: 2272,
+      1100000000: 3163,
+      1000000000: 7039,
+      900000000: 13800,
+      800000000: 9599,
+      700000000: 5655,
+      600000000: 4144,
+      500000000: 5655,
+      400000000: 6005,
+      300000000: 8288,
+      200000000: 149724
+    },
+    'cpu2': None,
+    'cpu3': {}
+  }
+  expected_freq_percents = {
+    'platform_info': {
+      1700000000: 3.29254111574526,
+      1600000000: 0.0,
+      1500000000: 0.0,
+      1400000000: 0.15926805099535601,
+      1300000000: 0.47124116307273645,
+      1200000000: 0.818756100807525,
+      1100000000: 1.099381692400982,
+      1000000000: 2.5942528544384302,
+      900000000: 5.68661122326737,
+      800000000: 3.850545467654628,
+      700000000: 2.409691872245393,
+      600000000: 1.4693702487650486,
+      500000000: 2.4623575553879373,
+      400000000: 2.672038150383057,
+      300000000: 3.415770495015825,
+      200000000: 69.59817400982045
+    },
+    'cpu0': {
+      1700000000: 4.113700564971752,
+      1600000000: 0.0,
+      1500000000: 0.0,
+      1400000000: 0.1765536723163842,
+      1300000000: 0.4943502824858757,
+      1200000000: 0.7944915254237288,
+      1100000000: 1.2226341807909604,
+      1000000000: 3.0632062146892656,
+      900000000: 5.680614406779661,
+      800000000: 3.6679025423728815,
+      700000000: 2.379060734463277,
+      600000000: 1.4124293785310735,
+      500000000: 2.599752824858757,
+      400000000: 3.0102401129943503,
+      300000000: 3.650247175141243,
+      200000000: 67.73481638418079
+    },
+    'cpu1': {
+      1700000000: 2.4713816665187682,
+      1600000000: 0.0,
+      1500000000: 0.0,
+      1400000000: 0.1419824296743278,
+      1300000000: 0.44813204365959713,
+      1200000000: 0.8430206761913214,
+      1100000000: 0.9761292040110037,
+      1000000000: 2.1252994941875945,
+      900000000: 5.69260803975508,
+      800000000: 4.033188392936374,
+      700000000: 2.4403230100275093,
+      600000000: 1.526311118999024,
+      500000000: 2.3249622859171177,
+      400000000: 2.3338361877717633,
+      300000000: 3.1812938148904073,
+      200000000: 71.46153163546012
+    },
+    'cpu2': {
+      1700000000: 0.0,
+    },
+    'cpu3': {
+      1700000000: 0.0,
+   }
+  }
+
+  def testParseCpuFreq(self):
+    initial = sysfs_power_monitor.SysfsPowerMonitor.ParseFreqSample(
+        self.initial_freq)
+    final = sysfs_power_monitor.SysfsPowerMonitor.ParseFreqSample(
+        self.final_freq)
+    self.assertDictEqual(initial, self.expected_initial_freq)
+    self.assertDictEqual(final, self.expected_final_freq)
+
+  def testComputeCpuStats(self):
+    results = sysfs_power_monitor.SysfsPowerMonitor.ComputeCpuStats(
+        self.expected_initial_freq, self.expected_final_freq)
+    for cpu in self.expected_freq_percents:
+      for freq in results[cpu]:
+        self.assertAlmostEqual(results[cpu][freq],
+                               self.expected_freq_percents[cpu][freq])
+
+  def testComputeCpuStatsWithMissingData(self):
+    results = sysfs_power_monitor.SysfsPowerMonitor.ComputeCpuStats(
+        {'cpu1': {}}, {'cpu1': {}})
+    self.assertEqual(results['cpu1'][12345], 0)
+
+    results = sysfs_power_monitor.SysfsPowerMonitor.ComputeCpuStats(
+        {'cpu1': {123: 0}}, {'cpu1': {123: 0}})
+    self.assertEqual(results['cpu1'][123], 0)
+
+    results = sysfs_power_monitor.SysfsPowerMonitor.ComputeCpuStats(
+        {'cpu1': {123: 456}}, {'cpu1': {123: 456}})
+    self.assertEqual(results['cpu1'][123], 0)
+
+  def testComputeCpuStatsWithNumberChange(self):
+    results = sysfs_power_monitor.SysfsPowerMonitor.ComputeCpuStats(
+        {'cpu1': {'C0': 10, 'WFI': 20}},
+        {'cpu1': {'C0': 20, 'WFI': 10}})
+    self.assertEqual(results['cpu1']['C0'], 0)
+    self.assertEqual(results['cpu1']['WFI'], 0)
+
+  def testGetCpuStateForAndroidDevices(self):
+    class PlatformStub(object):
+      def __init__(self, run_command_return_value):
+        self._run_command_return_value = run_command_return_value
+      def RunCommand(self, cmd):
+        del cmd  # unused
+        return self._run_command_return_value
+      def PathExists(self, path):
+        return 'cpu0' in path or 'cpu1' in path
+
+    cpu_state_from_samsung_note3 = (
+        "C0\n\nC1\n\nC2\n\nC3\n\n"
+        "53658520886\n1809072\n7073\n1722554\n"
+        "1\n35\n300\n500\n"
+        "1412949256\n")
+    expected_cstate_dict = {
+      'C0': 1412895593940415,
+      'C1': 1809072,
+      'C2': 7073,
+      'C3': 1722554,
+      'WFI': 53658520886
+    }
+    cpus = ["cpu%d" % cpu for cpu in range(4)]
+    expected_result = dict(zip(cpus, [expected_cstate_dict]*2))
+
+    sysfsmon = sysfs_power_monitor.SysfsPowerMonitor(
+      PlatformStub(cpu_state_from_samsung_note3))
+    # pylint: disable=protected-access
+    sysfsmon._cpus = cpus
+    cstate = sysfsmon.GetCpuState()
+    result = android_platform_backend.AndroidPlatformBackend.ParseCStateSample(
+        cstate)
+    self.assertDictEqual(expected_result, result)
+
+  def testStandAlone(self):
+    class PlatformStub(object):
+      def __init__(self, run_command_return_value):
+        self._run_command_return_value = run_command_return_value
+      def RunCommand(self, cmd):
+        del cmd  # unused
+        return self._run_command_return_value
+      def PathExists(self, path):
+        del path  # unused
+        return True
+
+    cpu_state_from_samsung_note3 = (
+        "C0\n\nC1\n\nC2\n\nC3\n\n"
+        "53658520886\n1809072\n7073\n1722554\n"
+        "1\n35\n300\n500\n"
+        "1412949256\n")
+    expected_cstate_dict = {
+        'C0': 1412895593940415,
+        'C1': 1809072,
+        'C2': 7073,
+        'C3': 1722554,
+        'WFI': 53658520886
+    }
+    cpus = ["cpu%d" % cpu for cpu in range(2)]
+    expected_result = dict(zip(cpus, [expected_cstate_dict]*len(cpus)))
+
+    sysfsmon = sysfs_power_monitor.SysfsPowerMonitor(
+        PlatformStub(cpu_state_from_samsung_note3), standalone=True)
+    # pylint: disable=protected-access
+    sysfsmon._cpus = cpus
+    cstate = sysfsmon.GetCpuState()
+    result = android_platform_backend.AndroidPlatformBackend.ParseCStateSample(
+        cstate)
+    self.assertDictEqual(expected_result, result)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/__init__.py
new file mode 100644
index 0000000..4f035a3
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/__init__.py
@@ -0,0 +1,68 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import collections
+
+from telemetry.core import exceptions
+
+class Profiler(object):
+  """A sampling profiler provided by the platform.
+
+  A profiler is started on its constructor, and should
+  gather data until CollectProfile().
+  The life cycle is normally tied to a single page,
+  i.e., multiple profilers will be created for a page set.
+  WillCloseBrowser() is called right before the browser
+  is closed to allow any further cleanup.
+  """
+
+  def __init__(self, browser_backend, platform_backend, output_path, state):
+    self._browser_backend = browser_backend
+    self._platform_backend = platform_backend
+    self._output_path = output_path
+    self._state = state
+
+  @classmethod
+  def name(cls):
+    """User-friendly name of this profiler."""
+    raise NotImplementedError()
+
+  @classmethod
+  def is_supported(cls, browser_type):
+    """True iff this profiler is currently supported by the platform."""
+    raise NotImplementedError()
+
+  @classmethod
+  def CustomizeBrowserOptions(cls, browser_type, options):
+    """Override to customize the Browser's options before it is created."""
+    pass
+
+  @classmethod
+  def WillCloseBrowser(cls, browser_backend, platform_backend):
+    """Called before the browser is stopped."""
+    pass
+
+  def _GetProcessOutputFileMap(self):
+    """Returns a dict with pid: output_file."""
+    all_pids = ([self._browser_backend.pid] +
+                self._platform_backend.GetChildPids(self._browser_backend.pid))
+
+    process_name_counts = collections.defaultdict(int)
+    process_output_file_map = {}
+    for pid in all_pids:
+      try:
+        cmd_line = self._platform_backend.GetCommandLine(pid)
+        process_name = self._browser_backend.GetProcessName(cmd_line)
+        output_file = '%s.%s%s' % (self._output_path, process_name,
+                                   process_name_counts[process_name])
+        process_name_counts[process_name] += 1
+        process_output_file_map[pid] = output_file
+      except exceptions.ProcessGoneException:
+        # Ignore processes that disappeared since calling GetChildPids().
+        continue
+    return process_output_file_map
+
+  def CollectProfile(self):
+    """Collect the profile from the profiler."""
+    raise NotImplementedError()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/android_prebuilt_profiler_helper.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/android_prebuilt_profiler_helper.py
new file mode 100644
index 0000000..77e2314
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/android_prebuilt_profiler_helper.py
@@ -0,0 +1,33 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Android-specific, installs pre-built profilers."""
+
+import logging
+import os
+
+from telemetry.internal.util import binary_manager
+from telemetry import decorators
+
+_DEVICE_PROFILER_DIR = '/data/local/tmp/profilers/'
+
+
+def GetDevicePath(profiler_binary):
+  return os.path.join(_DEVICE_PROFILER_DIR, os.path.basename(profiler_binary))
+
+
+@decorators.Cache
+def InstallOnDevice(device, profiler_binary):
+  arch_name = device.GetABI()
+  host_path = binary_manager.FetchPath(profiler_binary, arch_name, 'android')
+  if not host_path:
+    logging.error('Profiler binary "%s" not found. Could not be installed',
+                  host_path)
+    return False
+
+  device_binary_path = GetDevicePath(profiler_binary)
+  device.PushChangedFiles([(host_path, device_binary_path)])
+  device.RunShellCommand(
+      ['chmod', '777', device_binary_path], check_return=True)
+  return True
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/android_profiling_helper.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/android_profiling_helper.py
new file mode 100644
index 0000000..e1dc786
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/android_profiling_helper.py
@@ -0,0 +1,325 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import glob
+import hashlib
+import logging
+import os
+import platform
+import re
+import shutil
+import subprocess
+
+from telemetry.internal.util import binary_manager
+from telemetry.core import platform as telemetry_platform
+from telemetry.core import util
+from telemetry import decorators
+from telemetry.internal.platform.profiler import android_prebuilt_profiler_helper
+
+from devil.android import md5sum  # pylint: disable=import-error
+
+
+try:
+  import sqlite3
+except ImportError:
+  sqlite3 = None
+
+
+
+_TEXT_SECTION = '.text'
+
+
+def _ElfMachineId(elf_file):
+  headers = subprocess.check_output(['readelf', '-h', elf_file])
+  return re.match(r'.*Machine:\s+(\w+)', headers, re.DOTALL).group(1)
+
+
+def _ElfSectionAsString(elf_file, section):
+  return subprocess.check_output(['readelf', '-p', section, elf_file])
+
+
+def _ElfSectionMd5Sum(elf_file, section):
+  result = subprocess.check_output(
+      'readelf -p%s "%s" | md5sum' % (section, elf_file), shell=True)
+  return result.split(' ', 1)[0]
+
+
+def _FindMatchingUnstrippedLibraryOnHost(device, lib):
+  lib_base = os.path.basename(lib)
+
+  device_md5sums = md5sum.CalculateDeviceMd5Sums([lib], device)
+  if lib not in device_md5sums:
+    return None
+
+  device_md5 = device_md5sums[lib]
+
+  def FindMatchingStrippedLibrary(out_path):
+    # First find a matching stripped library on the host. This avoids the need
+    # to pull the stripped library from the device, which can take tens of
+    # seconds.
+    # Check the GN stripped lib path first, and the GYP ones afterwards.
+    host_lib_path = os.path.join(out_path, lib_base)
+    host_lib_pattern = os.path.join(out_path, '*_apk', 'libs', '*', lib_base)
+    for stripped_host_lib in [host_lib_path] + glob.glob(host_lib_pattern):
+      if os.path.exists(stripped_host_lib):
+        with open(stripped_host_lib) as f:
+          host_md5 = hashlib.md5(f.read()).hexdigest()
+          if host_md5 == device_md5:
+            return stripped_host_lib
+    return None
+
+  out_path = None
+  stripped_host_lib = None
+  for out_path in util.GetBuildDirectories():
+    stripped_host_lib = FindMatchingStrippedLibrary(out_path)
+    if stripped_host_lib:
+      break
+
+  if not stripped_host_lib:
+    return None
+
+  # The corresponding unstripped library will be under lib.unstripped for GN, or
+  # lib for GYP.
+  unstripped_host_lib_paths = [
+      os.path.join(out_path, 'lib.unstripped', lib_base),
+      os.path.join(out_path, 'lib', lib_base)
+  ]
+  unstripped_host_lib = next(
+      (lib for lib in unstripped_host_lib_paths if os.path.exists(lib)), None)
+  if unstripped_host_lib is None:
+    return None
+
+  # Make sure the unstripped library matches the stripped one. We do this
+  # by comparing the hashes of text sections in both libraries. This isn't an
+  # exact guarantee, but should still give reasonable confidence that the
+  # libraries are compatible.
+  # TODO(skyostil): Check .note.gnu.build-id instead once we're using
+  # --build-id=sha1.
+  # pylint: disable=undefined-loop-variable
+  if (_ElfSectionMd5Sum(unstripped_host_lib, _TEXT_SECTION) !=
+      _ElfSectionMd5Sum(stripped_host_lib, _TEXT_SECTION)):
+    return None
+  return unstripped_host_lib
+
+
+@decorators.Cache
+def GetPerfhostName():
+  return 'perfhost_' + telemetry_platform.GetHostPlatform().GetOSVersionName()
+
+
+# Ignored directories for libraries that aren't useful for symbolization.
+_IGNORED_LIB_PATHS = [
+  '/data/dalvik-cache',
+  '/tmp'
+]
+
+
+def GetRequiredLibrariesForPerfProfile(profile_file):
+  """Returns the set of libraries necessary to symbolize a given perf profile.
+
+  Args:
+    profile_file: Path to perf profile to analyse.
+
+  Returns:
+    A set of required library file names.
+  """
+  with open(os.devnull, 'w') as dev_null:
+    perfhost_path = binary_manager.FetchPath(
+        GetPerfhostName(), 'x86_64', 'linux')
+    perf = subprocess.Popen([perfhost_path, 'script', '-i', profile_file],
+                             stdout=dev_null, stderr=subprocess.PIPE)
+    _, output = perf.communicate()
+  missing_lib_re = re.compile(
+      ('^Failed to open (.*), continuing without symbols|'
+       '^(.*[.]so).*not found, continuing without symbols'))
+  libs = set()
+  for line in output.split('\n'):
+    lib = missing_lib_re.match(line)
+    if lib:
+      lib = lib.group(1) or lib.group(2)
+      path = os.path.dirname(lib)
+      if (any(path.startswith(ignored_path)
+              for ignored_path in _IGNORED_LIB_PATHS)
+          or path == '/' or not path):
+        continue
+      libs.add(lib)
+  return libs
+
+
+def GetRequiredLibrariesForVTuneProfile(profile_file):
+  """Returns the set of libraries necessary to symbolize a given VTune profile.
+
+  Args:
+    profile_file: Path to VTune profile to analyse.
+
+  Returns:
+    A set of required library file names.
+  """
+  db_file = os.path.join(profile_file, 'sqlite-db', 'dicer.db')
+  conn = sqlite3.connect(db_file)
+
+  try:
+    # The 'dd_module_file' table lists all libraries on the device. Only the
+    # ones with 'bin_located_path' are needed for the profile.
+    query = 'SELECT bin_path, bin_located_path FROM dd_module_file'
+    return set(row[0] for row in conn.cursor().execute(query) if row[1])
+  finally:
+    conn.close()
+
+
+def _FileMetadataMatches(filea, fileb):
+  """Check if the metadata of two files matches."""
+  assert os.path.exists(filea)
+  if not os.path.exists(fileb):
+    return False
+
+  fields_to_compare = [
+      'st_ctime', 'st_gid', 'st_mode', 'st_mtime', 'st_size', 'st_uid']
+
+  filea_stat = os.stat(filea)
+  fileb_stat = os.stat(fileb)
+  for field in fields_to_compare:
+    # shutil.copy2 doesn't get ctime/mtime identical when the file system
+    # provides sub-second accuracy.
+    if int(getattr(filea_stat, field)) != int(getattr(fileb_stat, field)):
+      return False
+  return True
+
+
+def CreateSymFs(device, symfs_dir, libraries, use_symlinks=True):
+  """Creates a symfs directory to be used for symbolizing profiles.
+
+  Prepares a set of files ("symfs") to be used with profilers such as perf for
+  converting binary addresses into human readable function names.
+
+  Args:
+    device: DeviceUtils instance identifying the target device.
+    symfs_dir: Path where the symfs should be created.
+    libraries: Set of library file names that should be included in the symfs.
+    use_symlinks: If True, link instead of copy unstripped libraries into the
+      symfs. This will speed up the operation, but the resulting symfs will no
+      longer be valid if the linked files are modified, e.g., by rebuilding.
+
+  Returns:
+    The absolute path to the kernel symbols within the created symfs.
+  """
+  logging.info('Building symfs into %s.' % symfs_dir)
+
+  for lib in libraries:
+    device_dir = os.path.dirname(lib)
+    output_dir = os.path.join(symfs_dir, device_dir[1:])
+    if not os.path.exists(output_dir):
+      os.makedirs(output_dir)
+    output_lib = os.path.join(output_dir, os.path.basename(lib))
+
+    if lib.startswith('/data/app'):
+      # If this is our own library instead of a system one, look for a matching
+      # unstripped library under the out directory.
+      unstripped_host_lib = _FindMatchingUnstrippedLibraryOnHost(device, lib)
+      if not unstripped_host_lib:
+        logging.warning('Could not find symbols for %s.' % lib)
+        logging.warning('Is the correct output directory selected '
+                        '(CHROMIUM_OUTPUT_DIR)? Did you install the APK after '
+                        'building?')
+        continue
+      if use_symlinks:
+        if os.path.lexists(output_lib):
+          os.remove(output_lib)
+        os.symlink(os.path.abspath(unstripped_host_lib), output_lib)
+      # Copy the unstripped library only if it has been changed to avoid the
+      # delay.
+      elif not _FileMetadataMatches(unstripped_host_lib, output_lib):
+        logging.info('Copying %s to %s' % (unstripped_host_lib, output_lib))
+        shutil.copy2(unstripped_host_lib, output_lib)
+    else:
+      # Otherwise save a copy of the stripped system library under the symfs so
+      # the profiler can at least use the public symbols of that library. To
+      # speed things up, only pull files that don't match copies we already
+      # have in the symfs.
+      if not os.path.exists(output_lib):
+        pull = True
+      else:
+        host_md5sums = md5sum.CalculateHostMd5Sums([output_lib])
+        device_md5sums = md5sum.CalculateDeviceMd5Sums([lib], device)
+
+        pull = True
+        if host_md5sums and device_md5sums and output_lib in host_md5sums \
+          and lib in device_md5sums:
+          pull = host_md5sums[output_lib] != device_md5sums[lib]
+
+      if pull:
+        logging.info('Pulling %s to %s', lib, output_lib)
+        device.PullFile(lib, output_lib)
+
+  # Also pull a copy of the kernel symbols.
+  output_kallsyms = os.path.join(symfs_dir, 'kallsyms')
+  if not os.path.exists(output_kallsyms):
+    device.PullFile('/proc/kallsyms', output_kallsyms)
+  return output_kallsyms
+
+
+def PrepareDeviceForPerf(device):
+  """Set up a device for running perf.
+
+  Args:
+    device: DeviceUtils instance identifying the target device.
+
+  Returns:
+    The path to the installed perf binary on the device.
+  """
+  android_prebuilt_profiler_helper.InstallOnDevice(device, 'perf')
+  # Make sure kernel pointers are not hidden.
+  device.WriteFile('/proc/sys/kernel/kptr_restrict', '0', as_root=True)
+  return android_prebuilt_profiler_helper.GetDevicePath('perf')
+
+
+def GetToolchainBinaryPath(library_file, binary_name):
+  """Return the path to an Android toolchain binary on the host.
+
+  Args:
+    library_file: ELF library which is used to identify the used ABI,
+        architecture and toolchain.
+    binary_name: Binary to search for, e.g., 'objdump'
+  Returns:
+    Full path to binary or None if the binary was not found.
+  """
+  # Mapping from ELF machine identifiers to GNU toolchain names.
+  toolchain_configs = {
+    'x86': 'i686-linux-android',
+    'MIPS': 'mipsel-linux-android',
+    'ARM': 'arm-linux-androideabi',
+    'x86-64': 'x86_64-linux-android',
+    'AArch64': 'aarch64-linux-android',
+  }
+  toolchain_config = toolchain_configs[_ElfMachineId(library_file)]
+  host_os = platform.uname()[0].lower()
+  host_machine = platform.uname()[4]
+
+  elf_comment = _ElfSectionAsString(library_file, '.comment')
+  toolchain_version = re.match(r'.*GCC: \(GNU\) ([\w.]+)',
+                               elf_comment, re.DOTALL)
+  if not toolchain_version:
+    return None
+  toolchain_version = toolchain_version.group(1)
+  toolchain_version = toolchain_version.replace('.x', '')
+
+  toolchain_path = os.path.abspath(os.path.join(
+      util.GetChromiumSrcDir(), 'third_party', 'android_tools', 'ndk',
+      'toolchains', '%s-%s' % (toolchain_config, toolchain_version)))
+  if not os.path.exists(toolchain_path):
+    logging.warning(
+        'Unable to find toolchain binary %s: toolchain not found at %s',
+        binary_name, toolchain_path)
+    return None
+
+  path = os.path.join(
+      toolchain_path, 'prebuilt', '%s-%s' % (host_os, host_machine), 'bin',
+      '%s-%s' % (toolchain_config, binary_name))
+  if not os.path.exists(path):
+    logging.warning(
+        'Unable to find toolchain binary %s: binary not found at %s',
+        binary_name, path)
+    return None
+
+  return path
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/android_profiling_helper_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/android_profiling_helper_unittest.py
new file mode 100644
index 0000000..70aa2f6
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/android_profiling_helper_unittest.py
@@ -0,0 +1,212 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import pickle
+import re
+import shutil
+import tempfile
+import time
+import unittest
+
+from telemetry.core import util
+from telemetry import decorators
+from telemetry.internal.platform.profiler import android_profiling_helper
+from telemetry.testing import simple_mock
+from telemetry.testing import tab_test_case
+
+
+def _GetLibrariesMappedIntoProcesses(device, pids):
+  libs = set()
+  for pid in pids:
+    maps_file = '/proc/%d/maps' % pid
+    maps = device.ReadFile(maps_file, as_root=True).splitlines()
+    for map_line in maps:
+      lib = re.match(r'.*\s(/.*[.]so)$', map_line)
+      if lib:
+        libs.add(lib.group(1))
+  return libs
+
+
+class TestFileMetadataMatches(unittest.TestCase):
+  def setUp(self):
+    self.tempdir = tempfile.mkdtemp()
+    self.filename_a = os.path.join(self.tempdir, 'filea')
+    self.filename_b = os.path.join(self.tempdir, 'fileb')
+
+    with open(self.filename_a, 'w') as f:
+      f.write('testing')
+
+  def tearDown(self):
+    shutil.rmtree(self.tempdir)
+
+  def testDoesntMatchNonExistant(self):
+    self.assertFalse(
+        android_profiling_helper._FileMetadataMatches(
+            self.filename_a, self.filename_b))
+
+  def testDoesntMatchJustExistence(self):
+    with open(self.filename_b, 'w') as f:
+      f.write('blah')
+
+    self.assertFalse(
+        android_profiling_helper._FileMetadataMatches(
+            self.filename_a, self.filename_b))
+
+  def testDoesntMatchCopy(self):
+    # This test can run so fast that the file system doesn't have enough
+    # accuracy to differentiate between the copy and initial file times.
+    # Hence we need to guarantee a delay here.
+    time.sleep(3)
+    shutil.copy(self.filename_a, self.filename_b)
+    self.assertFalse(
+        android_profiling_helper._FileMetadataMatches(
+            self.filename_a, self.filename_b))
+
+  def testMatchesAfterCopy2(self):
+    shutil.copy2(self.filename_a, self.filename_b)
+    self.assertTrue(
+        android_profiling_helper._FileMetadataMatches(
+            self.filename_a, self.filename_b))
+
+  def testDoesntMatchAfterCopy2ThenModify(self):
+    shutil.copy2(self.filename_a, self.filename_b)
+
+    filea = open(self.filename_a, 'w')
+    filea.write('moar testing!')
+    filea.close()
+
+    self.assertFalse(
+        android_profiling_helper._FileMetadataMatches(
+            self.filename_a, self.filename_b))
+
+  def testDoesntMatchAfterCopy2ThenModifyStats(self):
+    shutil.copy2(self.filename_a, self.filename_b)
+    os.utime(self.filename_a, (20, 20))
+    self.assertFalse(
+        android_profiling_helper._FileMetadataMatches(
+            self.filename_a, self.filename_b))
+
+  def testMatchesAfterCopyStatWithDifferentContent(self):
+    fileb = open(self.filename_b, 'w')
+    fileb.write('blahing')
+    fileb.close()
+
+    shutil.copystat(self.filename_a, self.filename_b)
+
+    self.assertTrue(
+        android_profiling_helper._FileMetadataMatches(
+            self.filename_a, self.filename_b))
+
+
+class TestAndroidProfilingHelper(unittest.TestCase):
+
+  @decorators.Enabled('linux')
+  def testGetRequiredLibrariesForPerfProfile(self):
+    perf_output = os.path.join(
+        util.GetUnittestDataDir(), 'sample_perf_report_output.txt')
+    with open(perf_output) as f:
+      perf_output = f.read()
+
+    mock_popen = simple_mock.MockObject()
+    mock_popen.ExpectCall('communicate').WillReturn([None, perf_output])
+
+    mock_subprocess = simple_mock.MockObject()
+    mock_subprocess.ExpectCall(
+        'Popen').WithArgs(simple_mock.DONT_CARE).WillReturn(mock_popen)
+    mock_subprocess.SetAttribute('PIPE', simple_mock.MockObject())
+
+    real_subprocess = android_profiling_helper.subprocess
+    android_profiling_helper.subprocess = mock_subprocess
+    try:
+      libs = android_profiling_helper.GetRequiredLibrariesForPerfProfile('foo')
+      self.assertEqual(libs, set([
+          '/data/app-lib/com.google.android.apps.chrome-2/libchrome.2016.0.so',
+          '/system/lib/libart.so',
+          '/system/lib/libc.so',
+          '/system/lib/libm.so']))
+    finally:
+      android_profiling_helper.subprocess = real_subprocess
+
+  @decorators.Enabled('android')
+  def testGetRequiredLibrariesForVTuneProfile(self):
+    vtune_db_output = os.path.join(
+        util.GetUnittestDataDir(), 'sample_vtune_db_output')
+    with open(vtune_db_output, 'rb') as f:
+      vtune_db_output = pickle.load(f)
+
+    mock_cursor = simple_mock.MockObject()
+    mock_cursor.ExpectCall(
+        'execute').WithArgs(simple_mock.DONT_CARE).WillReturn(vtune_db_output)
+
+    mock_conn = simple_mock.MockObject()
+    mock_conn.ExpectCall('cursor').WillReturn(mock_cursor)
+    mock_conn.ExpectCall('close')
+
+    mock_sqlite3 = simple_mock.MockObject()
+    mock_sqlite3.ExpectCall(
+        'connect').WithArgs(simple_mock.DONT_CARE).WillReturn(mock_conn)
+
+    real_sqlite3 = android_profiling_helper.sqlite3
+    android_profiling_helper.sqlite3 = mock_sqlite3
+    try:
+      libs = android_profiling_helper.GetRequiredLibrariesForVTuneProfile('foo')
+      self.assertEqual(libs, set([
+          '/data/app-lib/com.google.android.apps.chrome-1/libchrome.2019.0.so',
+          '/system/lib/libdvm.so',
+          '/system/lib/libc.so',
+          '/system/lib/libm.so']))
+    finally:
+      android_profiling_helper.sqlite3 = real_sqlite3
+
+
+class TestAndroidProfilingHelperTabTestCase(tab_test_case.TabTestCase):
+
+  def setUp(self):
+    super(TestAndroidProfilingHelperTabTestCase, self).setUp()
+    # pylint: disable=protected-access
+    browser_backend = self._browser._browser_backend
+    self._device = browser_backend.device()
+
+  # https://github.com/catapult-project/catapult/issues/3099 (Android)
+  @decorators.Disabled('all')
+  def testCreateSymFs(self):
+    # pylint: disable=protected-access
+    browser_pid = self._browser._browser_backend.pid
+    pids = ([browser_pid] +
+        self._browser._platform_backend.GetChildPids(browser_pid))
+    libs = _GetLibrariesMappedIntoProcesses(self._device, pids)
+    assert libs
+
+    symfs_dir = tempfile.mkdtemp()
+    try:
+      kallsyms = android_profiling_helper.CreateSymFs(self._device, symfs_dir,
+                                                      libs)
+
+      # Check that we have kernel symbols.
+      assert os.path.exists(kallsyms)
+
+      is_unstripped = re.compile(r'^/data/app(-lib)?/.*\.so$')
+      has_unstripped = False
+
+      # Check that all requested libraries are present.
+      for lib in libs:
+        has_unstripped = has_unstripped or is_unstripped.match(lib)
+        assert os.path.exists(os.path.join(symfs_dir, lib[1:])), \
+            '%s not found in symfs' % lib
+
+      # Make sure we found at least one unstripped library.
+      assert has_unstripped
+    finally:
+      shutil.rmtree(symfs_dir)
+
+  # Test fails: crbug.com/437081
+  # @decorators.Enabled('android')
+  @decorators.Disabled('all')
+  def testGetToolchainBinaryPath(self):
+    with tempfile.NamedTemporaryFile() as libc:
+      self._device.PullFile('/system/lib/libc.so', libc.name)
+      path = android_profiling_helper.GetToolchainBinaryPath(libc.name,
+                                                             'objdump')
+      assert path and os.path.exists(path)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/android_screen_recorder_profiler.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/android_screen_recorder_profiler.py
new file mode 100644
index 0000000..c1d4031
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/android_screen_recorder_profiler.py
@@ -0,0 +1,46 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# devil may not be available if we're not in an Android checkout.
+try:
+  from devil.android.tools import video_recorder
+except ImportError:
+  video_recorder = None
+
+from telemetry.internal.platform import profiler
+from telemetry.internal.backends.chrome import android_browser_finder
+
+
+_VIDEO_MEGABITS_PER_SECOND = 4
+
+
+class AndroidScreenRecordingProfiler(profiler.Profiler):
+  """Captures a screen recording on Android."""
+
+  def __init__(self, browser_backend, platform_backend, output_path, state):
+    super(AndroidScreenRecordingProfiler, self).__init__(
+        browser_backend, platform_backend, output_path, state)
+    self._output_path = output_path + '.mp4'
+    self._recorder = video_recorder.VideoRecorder(
+        browser_backend.device,
+        megabits_per_second=_VIDEO_MEGABITS_PER_SECOND)
+    self._recorder.Start()
+
+  @classmethod
+  def name(cls):
+    return 'android-screen-recorder'
+
+  @classmethod
+  def is_supported(cls, browser_type):
+    if browser_type == 'any':
+      return android_browser_finder.CanFindAvailableBrowsers()
+    return browser_type.startswith('android')
+
+  def CollectProfile(self):
+    self._recorder.Stop()
+    self._recorder.Pull(self._output_path)
+
+    print 'Screen recording saved as %s' % self._output_path
+    print 'To view, open in Chrome or a video player'
+    return [self._output_path]
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/android_screen_recorder_profiler_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/android_screen_recorder_profiler_unittest.py
new file mode 100644
index 0000000..8e6632b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/android_screen_recorder_profiler_unittest.py
@@ -0,0 +1,30 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import shutil
+import tempfile
+
+from telemetry import decorators
+from telemetry.internal.platform.profiler import (
+    android_screen_recorder_profiler)
+from telemetry.testing import tab_test_case
+
+
+class AndroidScreenRecorderProfilerTest(tab_test_case.TabTestCase):
+  @decorators.Enabled('android')
+  def testRecording(self):
+    out_dir = tempfile.mkdtemp()
+    try:
+      # pylint: disable=protected-access
+      profiler = (
+          android_screen_recorder_profiler.AndroidScreenRecordingProfiler(
+              self._browser._browser_backend,
+              self._browser._platform_backend,
+              os.path.join(out_dir, 'android_screen_recording'),
+              {}))
+      result = profiler.CollectProfile()[0]
+      self.assertTrue(os.path.exists(result))
+    finally:
+      shutil.rmtree(out_dir)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/android_systrace_profiler.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/android_systrace_profiler.py
new file mode 100644
index 0000000..f117244
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/android_systrace_profiler.py
@@ -0,0 +1,78 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import StringIO
+import subprocess
+import zipfile
+
+from telemetry.core import util
+from telemetry.internal.backends.chrome import android_browser_finder
+from telemetry.internal.platform import profiler
+from telemetry.timeline import tracing_config
+from tracing.trace_data import trace_data as trace_data_module
+
+_SYSTRACE_CATEGORIES = [
+    'gfx',
+    'input',
+    'view',
+    'sched',
+    'freq',
+]
+
+class AndroidSystraceProfiler(profiler.Profiler):
+  """Collects a Systrace on Android."""
+
+  def __init__(self, browser_backend, platform_backend, output_path, state,
+               device=None):
+    super(AndroidSystraceProfiler, self).__init__(
+        browser_backend, platform_backend, output_path, state)
+    assert self._browser_backend.supports_tracing
+    self._output_path = output_path + '-trace.zip'
+    self._systrace_output_path = output_path + '.systrace'
+
+    # Use telemetry's own tracing backend instead the combined mode in
+    # adb_profile_chrome because some benchmarks also do tracing of their own
+    # and the two methods conflict.
+    config = tracing_config.TracingConfig()
+    config.enable_chrome_trace = True
+    self._browser_backend.StartTracing(config, timeout=10)
+    command = ['python', os.path.join(util.GetCatapultDir(), 'systrace', 'bin',
+                                      'adb_profile_chrome'),
+               '--categories', '', '--continuous', '--output',
+               self._systrace_output_path, '--json', '--systrace',
+               ','.join(_SYSTRACE_CATEGORIES)]
+    if device:
+      command.extend(['--device', device])
+    self._profiler = subprocess.Popen(command, stdin=subprocess.PIPE,
+                                      stdout=subprocess.PIPE)
+
+  @classmethod
+  def name(cls):
+    return 'android-systrace'
+
+  @classmethod
+  def is_supported(cls, browser_type):
+    if browser_type == 'any':
+      return android_browser_finder.CanFindAvailableBrowsers()
+    return browser_type.startswith('android')
+
+  def CollectProfile(self):
+    self._profiler.communicate(input='\n')
+    trace_result_builder = trace_data_module.TraceDataBuilder()
+    self._browser_backend.StopTracing(trace_result_builder)
+    trace_result = trace_result_builder.AsData()
+
+    trace_file = StringIO.StringIO()
+    trace_result.Serialize(trace_file)
+
+    # Merge the chrome and systraces into a zip file.
+    with zipfile.ZipFile(self._output_path, 'w', zipfile.ZIP_DEFLATED) as z:
+      z.writestr('trace.json', trace_file.getvalue())
+      z.write(self._systrace_output_path, 'systrace')
+      os.unlink(self._systrace_output_path)
+
+    print 'Systrace saved as %s' % self._output_path
+    print 'To view, open in chrome://tracing'
+    return [self._output_path]
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/android_systrace_profiler_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/android_systrace_profiler_unittest.py
new file mode 100644
index 0000000..db4f9df
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/android_systrace_profiler_unittest.py
@@ -0,0 +1,33 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import os
+import shutil
+import tempfile
+import zipfile
+
+from telemetry import decorators
+from telemetry.internal.platform.profiler import android_systrace_profiler
+from telemetry.testing import tab_test_case
+
+
+class TestAndroidSystraceProfiler(tab_test_case.TabTestCase):
+  # https://github.com/catapult-project/catapult/issues/3099 (Android)
+  @decorators.Disabled('all')
+  def testSystraceProfiler(self):
+    try:
+      out_dir = tempfile.mkdtemp()
+      # pylint: disable=protected-access
+      profiler = android_systrace_profiler.AndroidSystraceProfiler(
+          self._browser._browser_backend,
+          self._browser._platform_backend,
+          os.path.join(out_dir, 'systrace'),
+          {},
+          self._device)
+      result = profiler.CollectProfile()[0]
+      self.assertTrue(zipfile.is_zipfile(result))
+      with zipfile.ZipFile(result) as z:
+        self.assertEquals(len(z.namelist()), 2)
+        self.assertIn('sched_wakeup', z.open('systrace').read())
+    finally:
+      shutil.rmtree(out_dir)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/android_traceview_profiler.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/android_traceview_profiler.py
new file mode 100644
index 0000000..5cb6b49
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/android_traceview_profiler.py
@@ -0,0 +1,79 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+
+from telemetry.internal.backends.chrome import android_browser_finder
+from telemetry.internal.platform import profiler
+
+import py_utils
+
+try:
+  from devil.android import device_errors  # pylint: disable=import-error
+except ImportError:
+  device_errors = None
+
+
+class AndroidTraceviewProfiler(profiler.Profiler):
+  """Collects a Traceview on Android."""
+
+  _DEFAULT_DEVICE_DIR = '/data/local/tmp/traceview'
+
+  def __init__(self, browser_backend, platform_backend, output_path, state):
+    super(AndroidTraceviewProfiler, self).__init__(
+        browser_backend, platform_backend, output_path, state)
+
+    if self._browser_backend.device.FileExists(self._DEFAULT_DEVICE_DIR):
+      # Note: command must be passed as a string to expand wildcards.
+      self._browser_backend.device.RunShellCommand(
+          'rm ' + os.path.join(self._DEFAULT_DEVICE_DIR, '*'),
+          check_return=True, shell=True)
+    else:
+      self._browser_backend.device.RunShellCommand(
+          ['mkdir', '-p', self._DEFAULT_DEVICE_DIR], check_return=True)
+      self._browser_backend.device.RunShellCommand(
+          ['chmod', '777', self._DEFAULT_DEVICE_DIR], check_return=True)
+
+    self._trace_files = []
+    for pid in self._GetProcessOutputFileMap().iterkeys():
+      device_dump_file = '%s/%s.trace' % (self._DEFAULT_DEVICE_DIR, pid)
+      self._trace_files.append((pid, device_dump_file))
+      self._browser_backend.device.RunShellCommand(
+          ['am', 'profile', str(pid), 'start', device_dump_file],
+          check_return=True)
+
+  @classmethod
+  def name(cls):
+    return 'android-traceview'
+
+  @classmethod
+  def is_supported(cls, browser_type):
+    if browser_type == 'any':
+      return android_browser_finder.CanFindAvailableBrowsers()
+    return browser_type.startswith('android')
+
+  def CollectProfile(self):
+    output_files = []
+    for pid, trace_file in self._trace_files:
+      self._browser_backend.device.RunShellCommand(
+          ['am', 'profile', str(pid), 'stop'], check_return=True)
+      # pylint: disable=cell-var-from-loop
+      py_utils.WaitFor(lambda: self._FileSize(trace_file) > 0, timeout=10)
+      output_files.append(trace_file)
+    self._browser_backend.device.PullFile(
+        self._DEFAULT_DEVICE_DIR, self._output_path)
+    # Note: command must be passed as a string to expand wildcards.
+    self._browser_backend.device.RunShellCommand(
+        'rm ' + os.path.join(self._DEFAULT_DEVICE_DIR, '*'),
+        check_return=True, shell=True)
+    print 'Traceview profiles available in ', self._output_path
+    print 'Use third_party/android_tools/sdk/tools/monitor '
+    print 'then use "File->Open File" to visualize them.'
+    return output_files
+
+  def _FileSize(self, file_name):
+    try:
+      return self._browser_backend.device.FileSize(file_name)
+    except device_errors.CommandFailedError:
+      return 0
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/iprofiler_profiler.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/iprofiler_profiler.py
new file mode 100644
index 0000000..bf57427
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/iprofiler_profiler.py
@@ -0,0 +1,98 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import signal
+import sys
+
+from telemetry.core import exceptions
+from telemetry.internal.platform import profiler
+
+import py_utils
+
+try:
+  import pexpect  # pylint: disable=import-error
+except ImportError:
+  pass
+
+
+class _SingleProcessIprofilerProfiler(object):
+  """An internal class for using iprofiler for a given process."""
+  def __init__(self, pid, output_path):
+    self._output_path = output_path
+    output_dir = os.path.dirname(self._output_path)
+    output_file = os.path.basename(self._output_path)
+    self._proc = pexpect.spawn(
+        'iprofiler', ['-timeprofiler', '-T', '300', '-a', str(pid),
+                      '-d', output_dir, '-o', output_file],
+        timeout=300)
+    while True:
+      if self._proc.getecho():
+        output = self._proc.readline().strip()
+        if not output:
+          continue
+        if 'iprofiler: Profiling process' in output:
+          break
+        print output
+      self._proc.interact(escape_character='\x0d')
+      if 'Failed to authorize rights' in output:
+        raise exceptions.ProfilingException(
+            'Failed to authorize rights for iprofiler\n')
+      if 'iprofiler error' in output:
+        raise exceptions.ProfilingException(
+            'Failed to start iprofiler for process %s\n' %
+            self._output_path.split('.')[1])
+      self._proc.write('\x0d')
+      print
+      def Echo():
+        return self._proc.getecho()
+      py_utils.WaitFor(Echo, timeout=5)
+
+  def CollectProfile(self):
+    self._proc.kill(signal.SIGINT)
+    try:
+      self._proc.wait()
+    except pexpect.ExceptionPexpect:
+      pass
+    finally:
+      self._proc = None
+
+    print 'To view the profile, run:'
+    print '  open -a Instruments %s.dtps' % self._output_path
+    return self._output_path
+
+
+class IprofilerProfiler(profiler.Profiler):
+
+  def __init__(self, browser_backend, platform_backend, output_path, state):
+    super(IprofilerProfiler, self).__init__(
+        browser_backend, platform_backend, output_path, state)
+    process_output_file_map = self._GetProcessOutputFileMap()
+    self._process_profilers = []
+    for pid, output_file in process_output_file_map.iteritems():
+      if '.utility' in output_file:
+        # The utility process may not have been started by Telemetry.
+        # So we won't have permissing to profile it
+        continue
+      self._process_profilers.append(
+          _SingleProcessIprofilerProfiler(pid, output_file))
+
+  @classmethod
+  def name(cls):
+    return 'iprofiler'
+
+  @classmethod
+  def is_supported(cls, browser_type):
+    if sys.platform != 'darwin':
+      return False
+    if browser_type == 'any':
+      return True
+    return (not browser_type.startswith('android') and
+            not browser_type.startswith('cros'))
+
+  def CollectProfile(self):
+    output_files = []
+    for single_process in self._process_profilers:
+      output_files.append(single_process.CollectProfile())
+    return output_files
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/java_heap_profiler.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/java_heap_profiler.py
new file mode 100644
index 0000000..c127a19
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/java_heap_profiler.py
@@ -0,0 +1,95 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import subprocess
+import threading
+
+from telemetry.core import platform
+from telemetry.internal.backends.chrome import android_browser_finder
+from telemetry.internal.platform import profiler
+from telemetry.internal.util import binary_manager
+
+import py_utils
+
+try:
+  from devil.android import device_errors  # pylint: disable=import-error
+except ImportError:
+  device_errors = None
+
+
+class JavaHeapProfiler(profiler.Profiler):
+  """Android-specific, trigger and fetch java heap dumps."""
+
+  _DEFAULT_DEVICE_DIR = '/data/local/tmp/javaheap'
+  # TODO(bulach): expose this as a command line option somehow.
+  _DEFAULT_INTERVAL = 20
+  def __init__(self, browser_backend, platform_backend, output_path, state):
+    super(JavaHeapProfiler, self).__init__(
+        browser_backend, platform_backend, output_path, state)
+    self._run_count = 1
+
+    self._DumpJavaHeap(False)
+
+    self._timer = threading.Timer(self._DEFAULT_INTERVAL, self._OnTimer)
+    self._timer.start()
+
+  @classmethod
+  def name(cls):
+    return 'java-heap'
+
+  @classmethod
+  def is_supported(cls, browser_type):
+    if browser_type == 'any':
+      return android_browser_finder.CanFindAvailableBrowsers()
+    return browser_type.startswith('android')
+
+  def CollectProfile(self):
+    self._timer.cancel()
+    self._DumpJavaHeap(True)
+    self._browser_backend.device.PullFile(
+        self._DEFAULT_DEVICE_DIR, self._output_path)
+    # Note: command must be passed as a string to expand wildcards.
+    self._browser_backend.device.RunShellCommand(
+        'rm -f ' + os.path.join(self._DEFAULT_DEVICE_DIR, '*'),
+        check_return=True, shell=True)
+    output_files = []
+    for f in os.listdir(self._output_path):
+      if os.path.splitext(f)[1] == '.aprof':
+        input_file = os.path.join(self._output_path, f)
+        output_file = input_file.replace('.aprof', '.hprof')
+        hprof_conv = binary_manager.FetchPath(
+            'hprof-conv',
+            platform.GetHostPlatform().GetArchName(),
+            platform.GetHostPlatform().GetOSName())
+        subprocess.call([hprof_conv, input_file, output_file])
+        output_files.append(output_file)
+    return output_files
+
+  def _OnTimer(self):
+    self._DumpJavaHeap(False)
+
+  def _DumpJavaHeap(self, wait_for_completion):
+    if not self._browser_backend.device.FileExists(
+        self._DEFAULT_DEVICE_DIR):
+      self._browser_backend.device.RunShellCommand(
+          ['mkdir', '-p', self._DEFAULT_DEVICE_DIR], check_return=True)
+      self._browser_backend.device.RunShellCommand(
+          ['chmod', '777', self._DEFAULT_DEVICE_DIR], check_return=True)
+
+    device_dump_file = None
+    for pid in self._GetProcessOutputFileMap().iterkeys():
+      device_dump_file = '%s/%s.%s.aprof' % (self._DEFAULT_DEVICE_DIR, pid,
+                                             self._run_count)
+      self._browser_backend.device.RunShellCommand(
+          ['am', 'dumpheap', str(pid), device_dump_file], check_return=True)
+    if device_dump_file and wait_for_completion:
+      py_utils.WaitFor(lambda: self._FileSize(device_dump_file) > 0, timeout=2)
+    self._run_count += 1
+
+  def _FileSize(self, file_name):
+    try:
+      return self._browser_backend.device.FileSize(file_name)
+    except device_errors.CommandFailedError:
+      return 0
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/monsoon.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/monsoon.py
new file mode 100644
index 0000000..72425f7
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/monsoon.py
@@ -0,0 +1,292 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Interface for a USB-connected Monsoon power meter.
+
+http://msoon.com/LabEquipment/PowerMonitor/
+Currently Unix-only. Relies on fcntl, /dev, and /tmp.
+"""
+
+import collections
+import logging
+import os
+import select
+import struct
+import time
+
+import serial  # pylint: disable=import-error,no-name-in-module
+import serial.tools.list_ports  # pylint: disable=import-error,no-name-in-module
+
+
+Power = collections.namedtuple('Power', ['amps', 'volts'])
+
+
+class Monsoon(object):
+  """Provides a simple class to use the power meter.
+
+  mon = monsoon.Monsoon()
+  mon.SetVoltage(3.7)
+  mon.StartDataCollection()
+  mydata = []
+  while len(mydata) < 1000:
+    mydata.extend(mon.CollectData())
+  mon.StopDataCollection()
+  """
+
+  def __init__(self, device=None, serialno=None, wait=True):
+    """Establish a connection to a Monsoon.
+
+    By default, opens the first available port, waiting if none are ready.
+    A particular port can be specified with 'device', or a particular Monsoon
+    can be specified with 'serialno' (using the number printed on its back).
+    With wait=False, IOError is thrown if a device is not immediately available.
+    """
+    assert float(serial.VERSION) >= 2.7, \
+     'Monsoon requires pyserial v2.7 or later. You have %s' % serial.VERSION
+
+    self._coarse_ref = self._fine_ref = self._coarse_zero = self._fine_zero = 0
+    self._coarse_scale = self._fine_scale = 0
+    self._last_seq = 0
+    self._voltage_multiplier = None
+
+    if device:
+      self.ser = serial.Serial(device, timeout=1)
+      return
+
+    while 1:
+      for (port, desc, _) in serial.tools.list_ports.comports():
+        if not desc.lower().startswith('mobile device power monitor'):
+          continue
+        tmpname = '/tmp/monsoon.%s.%s' % (os.uname()[0], os.path.basename(port))
+        self._tempfile = open(tmpname, 'w')
+        try:  # Use a lockfile to ensure exclusive access.
+          # Put the import in here to avoid doing it on unsupported platforms.
+          import fcntl  # pylint: disable=import-error
+          fcntl.lockf(self._tempfile, fcntl.LOCK_EX | fcntl.LOCK_NB)
+        except IOError:
+          logging.error('device %s is in use', port)
+          continue
+
+        try:  # Try to open the device.
+          self.ser = serial.Serial(port, timeout=1)
+          self.StopDataCollection()  # Just in case.
+          self._FlushInput()  # Discard stale input.
+          status = self.GetStatus()
+        except IOError, e:
+          logging.error('error opening device %s: %s', port, e)
+          continue
+
+        if not status:
+          logging.error('no response from device %s', port)
+        elif serialno and status['serialNumber'] != serialno:
+          logging.error('device %s is #%d', port, status['serialNumber'])
+        else:
+          if status['hardwareRevision'] == 1:
+            self._voltage_multiplier = 62.5 / 10**6
+          else:
+            self._voltage_multiplier = 125.0 / 10**6
+          return
+
+      self._tempfile = None
+      if not wait:
+        raise IOError('No device found')
+      logging.info('waiting for device...')
+      time.sleep(1)
+
+  def GetStatus(self):
+    """Requests and waits for status.  Returns status dictionary."""
+
+    # status packet format
+    STATUS_FORMAT = '>BBBhhhHhhhHBBBxBbHBHHHHBbbHHBBBbbbbbbbbbBH'
+    STATUS_FIELDS = [
+        'packetType', 'firmwareVersion', 'protocolVersion',
+        'mainFineCurrent', 'usbFineCurrent', 'auxFineCurrent', 'voltage1',
+        'mainCoarseCurrent', 'usbCoarseCurrent', 'auxCoarseCurrent', 'voltage2',
+        'outputVoltageSetting', 'temperature', 'status', 'leds',
+        'mainFineResistor', 'serialNumber', 'sampleRate',
+        'dacCalLow', 'dacCalHigh',
+        'powerUpCurrentLimit', 'runTimeCurrentLimit', 'powerUpTime',
+        'usbFineResistor', 'auxFineResistor',
+        'initialUsbVoltage', 'initialAuxVoltage',
+        'hardwareRevision', 'temperatureLimit', 'usbPassthroughMode',
+        'mainCoarseResistor', 'usbCoarseResistor', 'auxCoarseResistor',
+        'defMainFineResistor', 'defUsbFineResistor', 'defAuxFineResistor',
+        'defMainCoarseResistor', 'defUsbCoarseResistor', 'defAuxCoarseResistor',
+        'eventCode', 'eventData',
+    ]
+
+    self._SendStruct('BBB', 0x01, 0x00, 0x00)
+    while 1:  # Keep reading, discarding non-status packets.
+      data = self._ReadPacket()
+      if not data:
+        return None
+      if len(data) != struct.calcsize(STATUS_FORMAT) or data[0] != '\x10':
+        logging.debug('wanted status, dropped type=0x%02x, len=%d',
+                      ord(data[0]), len(data))
+        continue
+
+      status = dict(zip(STATUS_FIELDS, struct.unpack(STATUS_FORMAT, data)))
+      assert status['packetType'] == 0x10
+      for k in status.keys():
+        if k.endswith('VoltageSetting'):
+          status[k] = 2.0 + status[k] * 0.01
+        elif k.endswith('FineCurrent'):
+          pass  # Needs calibration data.
+        elif k.endswith('CoarseCurrent'):
+          pass  # Needs calibration data.
+        elif k.startswith('voltage') or k.endswith('Voltage'):
+          status[k] = status[k] * 0.000125
+        elif k.endswith('Resistor'):
+          status[k] = 0.05 + status[k] * 0.0001
+          if k.startswith('aux') or k.startswith('defAux'):
+            status[k] += 0.05
+        elif k.endswith('CurrentLimit'):
+          status[k] = 8 * (1023 - status[k]) / 1023.0
+      return status
+
+
+  def SetVoltage(self, v):
+    """Set the output voltage, 0 to disable."""
+    if v == 0:
+      self._SendStruct('BBB', 0x01, 0x01, 0x00)
+    else:
+      self._SendStruct('BBB', 0x01, 0x01, int((v - 2.0) * 100))
+
+  def SetStartupCurrent(self, a):
+    """Set the max startup output current. the unit of |a| : Amperes """
+    assert a >= 0 and a <= 8
+
+    val = 1023 - int((a/8.0)*1023)
+    self._SendStruct('BBB', 0x01, 0x08, val & 0xff)
+    self._SendStruct('BBB', 0x01, 0x09, val >> 8)
+
+  def SetMaxCurrent(self, a):
+    """Set the max output current. the unit of |a| : Amperes """
+    assert a >= 0 and a <= 8
+
+    val = 1023 - int((a/8.0)*1023)
+    self._SendStruct('BBB', 0x01, 0x0a, val & 0xff)
+    self._SendStruct('BBB', 0x01, 0x0b, val >> 8)
+
+  def SetUsbPassthrough(self, val):
+    """Set the USB passthrough mode: 0 = off, 1 = on,  2 = auto."""
+    self._SendStruct('BBB', 0x01, 0x10, val)
+
+
+  def StartDataCollection(self):
+    """Tell the device to start collecting and sending measurement data."""
+    self._SendStruct('BBB', 0x01, 0x1b, 0x01)  # Mystery command.
+    self._SendStruct('BBBBBBB', 0x02, 0xff, 0xff, 0xff, 0xff, 0x03, 0xe8)
+
+
+  def StopDataCollection(self):
+    """Tell the device to stop collecting measurement data."""
+    self._SendStruct('BB', 0x03, 0x00)  # Stop.
+
+
+  def CollectData(self):
+    """Return some current samples.  Call StartDataCollection() first."""
+    while 1:  # Loop until we get data or a timeout.
+      data = self._ReadPacket()
+      if not data:
+        return None
+      if len(data) < 4 + 8 + 1 or data[0] < '\x20' or data[0] > '\x2F':
+        logging.debug('wanted data, dropped type=0x%02x, len=%d',
+            ord(data[0]), len(data))
+        continue
+
+      seq, packet_type, x, _ = struct.unpack('BBBB', data[:4])
+      data = [struct.unpack(">hhhh", data[x:x+8])
+              for x in range(4, len(data) - 8, 8)]
+
+      if self._last_seq and seq & 0xF != (self._last_seq + 1) & 0xF:
+        logging.info('data sequence skipped, lost packet?')
+      self._last_seq = seq
+
+      if packet_type == 0:
+        if not self._coarse_scale or not self._fine_scale:
+          logging.info('waiting for calibration, dropped data packet')
+          continue
+
+        out = []
+        for main, usb, _, voltage in data:
+          main_voltage_v = self._voltage_multiplier * (voltage & ~3)
+          sample = 0.0
+          if main & 1:
+            sample += ((main & ~1) - self._coarse_zero) * self._coarse_scale
+          else:
+            sample += (main - self._fine_zero) * self._fine_scale
+          if usb & 1:
+            sample += ((usb & ~1) - self._coarse_zero) * self._coarse_scale
+          else:
+            sample += (usb - self._fine_zero) * self._fine_scale
+          out.append(Power(sample, main_voltage_v))
+        return out
+
+      elif packet_type == 1:
+        self._fine_zero = data[0][0]
+        self._coarse_zero = data[1][0]
+
+      elif packet_type == 2:
+        self._fine_ref = data[0][0]
+        self._coarse_ref = data[1][0]
+
+      else:
+        logging.debug('discarding data packet type=0x%02x', packet_type)
+        continue
+
+      if self._coarse_ref != self._coarse_zero:
+        self._coarse_scale = 2.88 / (self._coarse_ref - self._coarse_zero)
+      if self._fine_ref != self._fine_zero:
+        self._fine_scale = 0.0332 / (self._fine_ref - self._fine_zero)
+
+
+  def _SendStruct(self, fmt, *args):
+    """Pack a struct (without length or checksum) and send it."""
+    data = struct.pack(fmt, *args)
+    data_len = len(data) + 1
+    checksum = (data_len + sum(struct.unpack('B' * len(data), data))) % 256
+    out = struct.pack('B', data_len) + data + struct.pack('B', checksum)
+    self.ser.write(out)
+
+
+  def _ReadPacket(self):
+    """Read a single data record as a string (without length or checksum)."""
+    len_char = self.ser.read(1)
+    if not len_char:
+      logging.error('timeout reading from serial port')
+      return None
+
+    data_len = struct.unpack('B', len_char)
+    data_len = ord(len_char)
+    if not data_len:
+      return ''
+
+    result = self.ser.read(data_len)
+    if len(result) != data_len:
+      return None
+    body = result[:-1]
+    checksum = (data_len + sum(struct.unpack('B' * len(body), body))) % 256
+    if result[-1] != struct.pack('B', checksum):
+      logging.error('invalid checksum from serial port')
+      return None
+    return result[:-1]
+
+  def _FlushInput(self):
+    """Flush all read data until no more available."""
+    self.ser.flush()
+    flushed = 0
+    while True:
+      ready_r, _, ready_x = select.select([self.ser], [], [self.ser], 0)
+      if len(ready_x) > 0:
+        logging.error('exception from serial port')
+        return None
+      elif len(ready_r) > 0:
+        flushed += 1
+        self.ser.read(1)  # This may cause underlying buffering.
+        self.ser.flush()  # Flush the underlying buffer too.
+      else:
+        break
+    if flushed > 0:
+      logging.debug('dropped >%d bytes', flushed)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/monsoon_profiler.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/monsoon_profiler.py
new file mode 100644
index 0000000..e8712e6
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/monsoon_profiler.py
@@ -0,0 +1,97 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Profiler using data collected from a Monsoon power meter.
+
+http://msoon.com/LabEquipment/PowerMonitor/
+Data collected is a namedtuple of (amps, volts), at 5000 samples/second.
+Output graph plots power in watts over time in seconds.
+"""
+
+import csv
+import multiprocessing
+
+from telemetry.core import exceptions
+from telemetry.internal.platform import profiler
+from telemetry.internal.platform.profiler import monsoon
+from telemetry.util import statistics
+
+
+def _CollectData(output_path, is_collecting):
+  mon = monsoon.Monsoon(wait=False)
+  # Note: Telemetry requires the device to be connected by USB, but that
+  # puts it in charging mode. This increases the power consumption.
+  mon.SetUsbPassthrough(1)
+  # Nominal Li-ion voltage is 3.7V, but it puts out 4.2V at max capacity. Use
+  # 4.0V to simulate a "~80%" charged battery. Google "li-ion voltage curve".
+  # This is true only for a single cell. (Most smartphones, some tablets.)
+  mon.SetVoltage(4.0)
+
+  samples = []
+  try:
+    mon.StartDataCollection()
+    # Do one CollectData() to make the Monsoon set up, which takes about
+    # 0.3 seconds, and only signal that we've started after that.
+    mon.CollectData()
+    is_collecting.set()
+    while is_collecting.is_set():
+      samples += mon.CollectData()
+  finally:
+    mon.StopDataCollection()
+
+  # Add x-axis labels.
+  plot_data = [(i / 5000., sample.amps * sample.volts)
+               for i, sample in enumerate(samples)]
+
+  # Print data in csv.
+  with open(output_path, 'w') as output_file:
+    output_writer = csv.writer(output_file)
+    output_writer.writerows(plot_data)
+    output_file.flush()
+
+  power_samples = [s.amps * s.volts for s in samples]
+
+  print 'Monsoon profile power readings in watts:'
+  print '  Total    = %f' % statistics.TrapezoidalRule(power_samples, 1/5000.)
+  print ('  Average  = %f' % statistics.ArithmeticMean(power_samples) +
+         '+-%f' % statistics.StandardDeviation(power_samples))
+  print '  Peak     = %f' % max(power_samples)
+  print '  Duration = %f' % (len(power_samples) / 5000.)
+
+  print 'To view the Monsoon profile, run:'
+  print ('  echo "set datafile separator \',\'; plot \'%s\' with lines" | '
+      'gnuplot --persist' % output_path)
+
+
+class MonsoonProfiler(profiler.Profiler):
+  def __init__(self, browser_backend, platform_backend, output_path, state):
+    super(MonsoonProfiler, self).__init__(
+        browser_backend, platform_backend, output_path, state)
+    # We collect the data in a separate process, so we can continuously
+    # read the samples from the USB port while running the test.
+    self._is_collecting = multiprocessing.Event()
+    self._collector = multiprocessing.Process(
+        target=_CollectData, args=(output_path, self._is_collecting))
+    self._collector.start()
+    if not self._is_collecting.wait(timeout=0.5):
+      self._collector.terminate()
+      raise exceptions.ProfilingException('Failed to start data collection.')
+
+  @classmethod
+  def name(cls):
+    return 'monsoon'
+
+  @classmethod
+  def is_supported(cls, browser_type):
+    try:
+      monsoon.Monsoon(wait=False)
+    except EnvironmentError:
+      return False
+    else:
+      return True
+
+  def CollectProfile(self):
+    self._is_collecting.clear()
+    self._collector.join()
+    return [self._output_path]
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/netlog_profiler.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/netlog_profiler.py
new file mode 100644
index 0000000..4d3673d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/netlog_profiler.py
@@ -0,0 +1,45 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import tempfile
+
+from telemetry.internal.platform import profiler
+
+
+class NetLogProfiler(profiler.Profiler):
+
+  _NET_LOG_ARG = '--log-net-log='
+
+  @classmethod
+  def name(cls):
+    return 'netlog'
+
+  @classmethod
+  def is_supported(cls, browser_type):
+    return not browser_type.startswith('cros')
+
+  @classmethod
+  def CustomizeBrowserOptions(cls, browser_type, options):
+    if browser_type.startswith('android'):
+      dump_file = '/sdcard/net-internals-profile.json'
+    else:
+      dump_file = tempfile.mkstemp()[1]
+    options.AppendExtraBrowserArgs([cls._NET_LOG_ARG + dump_file])
+
+  def CollectProfile(self):
+    # Find output filename from browser argument.
+    for i in self._browser_backend.browser_options.extra_browser_args:
+      if i.startswith(self._NET_LOG_ARG):
+        output_file = i[len(self._NET_LOG_ARG):]
+    assert output_file
+    # On Android pull the output file to the host.
+    if self._platform_backend.GetOSName() == 'android':
+      host_output_file = '%s.json' % self._output_path
+      self._browser_backend.device.PullFile(output_file, host_output_file)
+      # Clean the device
+      self._browser_backend.device.RemovePath(output_file)
+      output_file = host_output_file
+    print 'Net-internals log saved as %s' % output_file
+    print 'To view, open in chrome://net-internals'
+    return [output_file]
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/perf_profiler.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/perf_profiler.py
new file mode 100644
index 0000000..aac177e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/perf_profiler.py
@@ -0,0 +1,257 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+import os
+import re
+import signal
+import subprocess
+import sys
+import tempfile
+
+from devil.android import device_errors  # pylint: disable=import-error
+
+from telemetry.internal.util import binary_manager
+from telemetry.core import platform
+from telemetry.internal.platform import profiler
+from telemetry.internal.platform.profiler import android_profiling_helper
+
+from devil.android.perf import perf_control  # pylint: disable=import-error
+
+
+_PERF_OPTIONS = [
+    # Sample across all processes and CPUs to so that the current CPU gets
+    # recorded to each sample.
+    '--all-cpus',
+    # In perf 3.13 --call-graph requires an argument, so use the -g short-hand
+    # which does not.
+    '-g',
+    # Record raw samples to get CPU information.
+    '--raw-samples',
+    # Increase sampling frequency for better coverage.
+    '--freq', '2000',
+]
+
+_PERF_OPTIONS_ANDROID = [
+    # Increase priority to avoid dropping samples. Requires root.
+    '--realtime', '80',
+]
+
+
+def _NicePath(path):
+  rel_path = os.path.relpath(path, os.curdir)
+  return rel_path if len(rel_path) < len(path) else path
+
+
+def _PrepareHostForPerf():
+  kptr_file = '/proc/sys/kernel/kptr_restrict'
+  with open(kptr_file) as f:
+    if f.read().strip() != '0':
+      logging.warning('Making kernel symbols unrestricted. You might have to '
+          'enter your password for "sudo".')
+      with tempfile.NamedTemporaryFile() as zero:
+        zero.write('0')
+        zero.flush()
+        subprocess.call(['/usr/bin/sudo', 'cp', zero.name, kptr_file])
+
+
+def _InstallPerfHost():
+  perfhost_name = android_profiling_helper.GetPerfhostName()
+  host = platform.GetHostPlatform()
+  if not host.CanLaunchApplication(perfhost_name):
+    host.InstallApplication(perfhost_name)
+  return binary_manager.FetchPath(perfhost_name, 'x86_64', 'linux')
+
+
+class _SingleProcessPerfProfiler(object):
+  """An internal class for using perf for a given process.
+
+  On android, this profiler uses pre-built binaries from AOSP.
+  See more details in prebuilt/android/README.txt.
+  """
+  def __init__(self, pid, output_file, browser_backend, platform_backend,
+               perf_binary, perfhost_binary):
+    self._pid = pid
+    self._browser_backend = browser_backend
+    self._platform_backend = platform_backend
+    self._output_file = output_file
+    self._tmp_output_file = tempfile.NamedTemporaryFile('w', 0)
+    self._is_android = platform_backend.GetOSName() == 'android'
+    self._perf_binary = perf_binary
+    self._perfhost_binary = perfhost_binary
+    cmd_prefix = []
+    perf_args = ['record', '--pid', str(pid)]
+    if self._is_android:
+      cmd_prefix = [
+          browser_backend.device.adb.GetAdbPath(),
+          '-s', browser_backend.device.adb.GetDeviceSerial(),
+          'shell', perf_binary]
+      perf_args += _PERF_OPTIONS_ANDROID
+      output_file = os.path.join('/sdcard', 'perf_profiles',
+                                 os.path.basename(output_file))
+      self._device_output_file = output_file
+      browser_backend.device.RunShellCommand(
+          ['mkdir', '-p', os.path.dirname(self._device_output_file)],
+          check_return=True)
+      browser_backend.device.RemovePath(self._device_output_file, force=True)
+    else:
+      cmd_prefix = [perf_binary]
+    perf_args += ['--output', output_file] + _PERF_OPTIONS
+    self._proc = subprocess.Popen(cmd_prefix + perf_args,
+        stdout=self._tmp_output_file, stderr=subprocess.STDOUT)
+
+  def CollectProfile(self):
+    if ('renderer' in self._output_file and
+        not self._is_android and
+        not self._platform_backend.GetCommandLine(self._pid)):
+      logging.warning('Renderer was swapped out during profiling. '
+                      'To collect a full profile rerun with '
+                      '"--extra-browser-args=--single-process"')
+    if self._is_android:
+      device = self._browser_backend.device
+      try:
+        binary_name = os.path.basename(self._perf_binary)
+        device.KillAll(binary_name, signum=signal.SIGINT, blocking=True,
+                       quiet=True)
+      except device_errors.CommandFailedError:
+        logging.warning('The perf process could not be killed on the device.')
+    self._proc.send_signal(signal.SIGINT)
+    exit_code = self._proc.wait()
+    try:
+      if exit_code == 128:
+        raise Exception(
+            """perf failed with exit code 128.
+Try rerunning this script under sudo or setting
+/proc/sys/kernel/perf_event_paranoid to "-1".\nOutput:\n%s""" %
+            self._GetStdOut())
+      elif exit_code not in (0, -2):
+        raise Exception(
+            'perf failed with exit code %d. Output:\n%s' % (exit_code,
+                                                            self._GetStdOut()))
+    finally:
+      self._tmp_output_file.close()
+    cmd = '%s report -n -i %s' % (_NicePath(self._perfhost_binary),
+                                  self._output_file)
+    if self._is_android:
+      device = self._browser_backend.device
+      device.PullFile(self._device_output_file, self._output_file)
+      required_libs = \
+          android_profiling_helper.GetRequiredLibrariesForPerfProfile(
+              self._output_file)
+      symfs_root = os.path.join(os.path.dirname(self._output_file), 'symfs')
+      if not os.path.exists(symfs_root):
+        os.makedirs(symfs_root)
+      kallsyms = android_profiling_helper.CreateSymFs(device,
+                                                      symfs_root,
+                                                      required_libs,
+                                                      use_symlinks=True)
+      cmd += ' --symfs %s --kallsyms %s' % (symfs_root, kallsyms)
+      for lib in required_libs:
+        lib = os.path.join(symfs_root, lib[1:])
+        if not os.path.exists(lib):
+          continue
+        objdump_path = android_profiling_helper.GetToolchainBinaryPath(
+            lib, 'objdump')
+        if objdump_path:
+          cmd += ' --objdump %s' % _NicePath(objdump_path)
+          break
+
+    print 'To view the profile, run:'
+    print ' ', cmd
+    return self._output_file
+
+  def _GetStdOut(self):
+    self._tmp_output_file.flush()
+    try:
+      with open(self._tmp_output_file.name) as f:
+        return f.read()
+    except IOError:
+      return ''
+
+
+class PerfProfiler(profiler.Profiler):
+
+  def __init__(self, browser_backend, platform_backend, output_path, state):
+    super(PerfProfiler, self).__init__(
+        browser_backend, platform_backend, output_path, state)
+    process_output_file_map = self._GetProcessOutputFileMap()
+    self._process_profilers = []
+    self._perf_control = None
+
+    perf_binary = perfhost_binary = _InstallPerfHost()
+    try:
+      if platform_backend.GetOSName() == 'android':
+        device = browser_backend.device
+        perf_binary = android_profiling_helper.PrepareDeviceForPerf(device)
+        self._perf_control = perf_control.PerfControl(device)
+        self._perf_control.SetPerfProfilingMode()
+      else:
+        _PrepareHostForPerf()
+
+      for pid, output_file in process_output_file_map.iteritems():
+        if 'zygote' in output_file:
+          continue
+        self._process_profilers.append(
+            _SingleProcessPerfProfiler(
+                pid, output_file, browser_backend, platform_backend,
+                perf_binary, perfhost_binary))
+    except:
+      if self._perf_control:
+        self._perf_control.SetDefaultPerfMode()
+      raise
+
+  @classmethod
+  def name(cls):
+    return 'perf'
+
+  @classmethod
+  def is_supported(cls, browser_type):
+    if sys.platform != 'linux2':
+      return False
+    if platform.GetHostPlatform().GetOSName() == 'chromeos':
+      return False
+    return True
+
+  @classmethod
+  def CustomizeBrowserOptions(cls, browser_type, options):
+    options.AppendExtraBrowserArgs([
+        '--no-sandbox',
+        '--allow-sandbox-debugging',
+    ])
+
+  def CollectProfile(self):
+    if self._perf_control:
+      self._perf_control.SetDefaultPerfMode()
+    output_files = []
+    for single_process in self._process_profilers:
+      output_files.append(single_process.CollectProfile())
+    return output_files
+
+  @classmethod
+  def GetTopSamples(cls, file_name, number):
+    """Parses the perf generated profile in |file_name| and returns a
+    {function: period} dict of the |number| hottests functions.
+    """
+    assert os.path.exists(file_name)
+    with open(os.devnull, 'w') as devnull:
+      _InstallPerfHost()
+      report = subprocess.Popen(
+          [android_profiling_helper.GetPerfhostName(),
+           'report', '--show-total-period', '-U', '-t', '^', '-i', file_name],
+          stdout=subprocess.PIPE, stderr=devnull).communicate()[0]
+    period_by_function = {}
+    for line in report.split('\n'):
+      if not line or line.startswith('#'):
+        continue
+      fields = line.split('^')
+      if len(fields) != 5:
+        continue
+      period = int(fields[1])
+      function = fields[4].partition(' ')[2]
+      function = re.sub('<.*>', '', function)  # Strip template params.
+      function = re.sub('[(].*[)]', '', function)  # Strip function params.
+      period_by_function[function] = period
+      if len(period_by_function) == number:
+        break
+    return period_by_function
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/perf_profiler_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/perf_profiler_unittest.py
new file mode 100644
index 0000000..7026946
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/perf_profiler_unittest.py
@@ -0,0 +1,48 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import logging
+import os
+import unittest
+
+from telemetry.core import util
+from telemetry.internal.platform.profiler import perf_profiler
+from telemetry.testing import options_for_unittests
+import mock
+
+class TestPerfProfiler(unittest.TestCase):
+  @mock.patch('telemetry.internal.platform.profiler.perf_profiler.subprocess')
+  def testPerfProfiler(self, mock_subprocess):
+    options = options_for_unittests.GetCopy()
+    if not perf_profiler.PerfProfiler.is_supported(options.browser_type):
+      logging.warning('PerfProfiler is not supported. Skipping test')
+      return
+
+    profile_file = os.path.join(
+        util.GetUnittestDataDir(), 'perf_report_output.txt')
+    with open(profile_file) as f:
+      perf_report_output = f.read()
+
+    mock_popen = mock.Mock()
+    mock_popen.communicate.side_effect = [[perf_report_output]]
+    mock_subprocess.Popen.side_effect = [mock_popen]
+    mock_subprocess.PIPE = mock.Mock()
+
+    perf_profiler.subprocess = mock_subprocess
+
+    self.assertEqual(
+        perf_profiler.PerfProfiler.GetTopSamples(profile_file, 10),
+        {'v8::internal::StaticMarkingVisitor::MarkMapContents': 63615201,
+         'v8::internal::RelocIterator::next': 38271931,
+         'v8::internal::LAllocator::MeetConstraintsBetween': 42913933,
+         'v8::internal::FlexibleBodyVisitor::Visit': 31909537,
+         'v8::internal::LiveRange::CreateAssignedOperand': 42913933,
+         'void v8::internal::RelocInfo::Visit': 96878864,
+         'WebCore::HTMLTokenizer::nextToken': 48240439,
+         'v8::internal::Scanner::ScanIdentifierOrKeyword': 46054550,
+         'sk_memset32_SSE2': 45121317,
+         'v8::internal::HeapObject::Size': 39786862
+         })
+    mock_popen.communicate.assert_called_once_with()
+    mock_subprocess.Popen.assert_called_once_with(
+        mock.ANY, stdout=mock.ANY, stderr=mock.ANY)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/profiler_finder.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/profiler_finder.py
new file mode 100644
index 0000000..33c31e5
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/profiler_finder.py
@@ -0,0 +1,28 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+
+from telemetry.core import discover
+from telemetry.internal.platform import profiler
+from telemetry.core import util
+
+
+def _DiscoverProfilers():
+  profiler_dir = os.path.dirname(__file__)
+  return discover.DiscoverClasses(profiler_dir, util.GetTelemetryDir(),
+                                  profiler.Profiler,
+                                  index_by_class_name=True).values()
+
+
+def FindProfiler(name):
+  for p in _DiscoverProfilers():
+    if p.name() == name:
+      return p
+  return None
+
+
+def GetAllAvailableProfilers():
+  return sorted([p.name() for p in _DiscoverProfilers()
+                 if p.is_supported(browser_type='any')])
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/sample_profiler.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/sample_profiler.py
new file mode 100644
index 0000000..1955901
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/sample_profiler.py
@@ -0,0 +1,91 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import signal
+import subprocess
+import sys
+import tempfile
+
+from telemetry.core import exceptions
+from telemetry.internal.platform import profiler
+
+import py_utils
+
+
+class _SingleProcessSampleProfiler(object):
+  """An internal class for using iprofiler for a given process."""
+  def __init__(self, pid, output_path):
+    self._output_path = output_path
+    self._tmp_output_file = tempfile.NamedTemporaryFile('w', 0)
+    self._proc = subprocess.Popen(
+        ['sample', str(pid), '-mayDie', '-file', self._output_path],
+        stdout=self._tmp_output_file, stderr=subprocess.STDOUT)
+    def IsStarted():
+      stdout = self._GetStdOut()
+      if 'sample cannot examine process' in stdout:
+        raise exceptions.ProfilingException(
+            'Failed to start sample for process %s\n' %
+            self._output_path.split('.')[1])
+      return 'Sampling process' in stdout
+    py_utils.WaitFor(IsStarted, 120)
+
+  def CollectProfile(self):
+    self._proc.send_signal(signal.SIGINT)
+    exit_code = self._proc.wait()
+    try:
+      if exit_code:
+        raise Exception(
+            'sample failed with exit code %d. Output:\n%s' % (
+            exit_code, self._GetStdOut()))
+    finally:
+      self._proc = None
+      self._tmp_output_file.close()
+
+    print 'To view the profile, run:'
+    print '  open -a TextEdit %s' % self._output_path
+
+    return self._output_path
+
+  def _GetStdOut(self):
+    self._tmp_output_file.flush()
+    try:
+      with open(self._tmp_output_file.name) as f:
+        return f.read()
+    except IOError:
+      return ''
+
+
+class SampleProfiler(profiler.Profiler):
+
+  def __init__(self, browser_backend, platform_backend, output_path, state):
+    super(SampleProfiler, self).__init__(
+        browser_backend, platform_backend, output_path, state)
+    process_output_file_map = self._GetProcessOutputFileMap()
+    self._process_profilers = []
+    for pid, output_file in process_output_file_map.iteritems():
+      if '.utility' in output_file:
+        # The utility process may not have been started by Telemetry.
+        # So we won't have permissing to profile it
+        continue
+      self._process_profilers.append(
+          _SingleProcessSampleProfiler(pid, output_file))
+
+  @classmethod
+  def name(cls):
+    return 'sample'
+
+  @classmethod
+  def is_supported(cls, browser_type):
+    if sys.platform != 'darwin':
+      return False
+    if browser_type == 'any':
+      return True
+    return (not browser_type.startswith('android') and
+            not browser_type.startswith('cros'))
+
+  def CollectProfile(self):
+    output_paths = []
+    for single_process in self._process_profilers:
+      output_paths.append(single_process.CollectProfile())
+    return output_paths
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/strace_profiler.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/strace_profiler.py
new file mode 100644
index 0000000..57f4c08
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/strace_profiler.py
@@ -0,0 +1,256 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import json
+import logging
+import re
+import signal
+import subprocess
+import sys
+import tempfile
+
+from telemetry.internal.platform import profiler
+from telemetry.timeline import model
+from tracing.trace_data import trace_data as trace_data_module
+
+
+# Parses one line of strace output, for example:
+# 6052  1311456063.159722 read(8, "\1\0\0\0\0\0\0\0", 8) = 8 <0.000022>
+_STRACE_LINE_RE = re.compile(
+    r'^(?P<tid>\d+)\s+'
+    r'(?P<ts>\d+)'
+    r'(?P<micro>.\d+)\s+'
+    r'(?P<func>.*?)'
+    r'[(](?P<args>.*?)[)]\s+=\s+'
+    r'(?P<ret>.*?)\s+'
+    r'<(?P<dur>[\d.]+)>$')
+
+_UNFINISHED_LINE_RE = re.compile(
+    r'^(?P<tid>\d+)\s+'
+    r'(?P<line>.*?)'
+    r'<unfinished ...>$')
+
+_RESUMED_LINE_RE = re.compile(
+    r'^(?P<tid>\d+)\s+'
+    r'(?P<ts>\d+)'
+    r'(?P<micro>.\d+)\s+'
+    r'<[.][.][.]\s(?P<func>.*?)\sresumed>'
+    r'(?P<line>.*?)$')
+
+_KILLED_LINE_RE = re.compile(
+    r'^(?P<tid>\d+)\s+'
+    r'(?P<ts>\d+)'
+    r'(?P<micro>.\d+)\s+'
+    r'[+][+][+] killed by SIGKILL [+][+][+]$')
+
+
+def _StraceToChromeTrace(pid, infile):
+  """Returns chrometrace json format for |infile| strace output."""
+  # Map of fd:file_name for open file descriptors. Useful for displaying
+  # file name instead of the descriptor number.
+  fd_map = {}
+
+  # Map of tid:interrupted_call for the interrupted call on each thread. It is
+  # possible to context switch during a system call. In this case we must
+  # match up the lines.
+  interrupted_call_map = {}
+
+  out = []
+  with open(infile, 'r') as f:
+    for line in f.readlines():
+      # Ignore kill lines for now.
+      m = _KILLED_LINE_RE.match(line)
+      if m:
+        continue
+
+      # If this line is interrupted, then remember it and continue.
+      m = _UNFINISHED_LINE_RE.match(line)
+      if m:
+        assert m.group('tid') not in interrupted_call_map
+        interrupted_call_map[m.group('tid')] = line
+        continue
+
+      # If this is a resume of a previous line, stitch it together.
+      interrupted = False
+      m = _RESUMED_LINE_RE.match(line)
+      if m:
+        interrupted = True
+        assert m.group('tid') in interrupted_call_map
+        line = interrupted_call_map[m.group('tid')].replace(
+            '<unfinished ...>', m.group('line'))
+        del interrupted_call_map[m.group('tid')]
+
+      # At this point we can do a normal match.
+      m = _STRACE_LINE_RE.match(line)
+      if not m:
+        if ('exit' not in line and
+            'Profiling timer expired' not in line and
+            '<unavailable>' not in line):
+          logging.warn('Failed to parse line: %s' % line)
+        continue
+
+      ts_begin = int(1000000 * (int(m.group('ts')) + float(m.group('micro'))))
+      ts_end = ts_begin + int(1000000 * float(m.group('dur')))
+      tid = int(m.group('tid'))
+      function_name = unicode(m.group('func'), errors='ignore')
+      function_args = unicode(m.group('args'), errors='ignore')
+      ret = unicode(m.group('ret'), errors='ignore')
+      cat = 'strace'
+
+      possible_fd_arg = None
+      first_arg = function_args.split(',')[0]
+      if first_arg and first_arg.strip().isdigit():
+        possible_fd_arg = first_arg.strip()
+
+      if function_name == 'open' and ret.isdigit():
+        # 1918  1311606151.649379 open("/foo/bar.so", O_RDONLY) = 7 <0.000088>
+        fd_map[ret] = first_arg
+
+      args = {
+          'args': function_args,
+          'ret': ret,
+          }
+      if interrupted:
+        args['interrupted'] = True
+      if possible_fd_arg and possible_fd_arg in fd_map:
+        args['fd%s' % first_arg] = fd_map[possible_fd_arg]
+
+      out.append({
+          'cat': cat,
+          'pid': pid,
+          'tid': tid,
+          'ts': ts_begin,
+          'ph': 'B',  # Begin
+          'name': function_name,
+          })
+      out.append({
+          'cat': cat,
+          'pid': pid,
+          'tid': tid,
+          'ts': ts_end,
+          'ph': 'E',  # End
+          'name': function_name,
+          'args': args,
+          })
+
+  return out
+
+
+def _GenerateTraceMetadata(timeline_model):
+  out = []
+  for process in timeline_model.processes:
+    out.append({
+        'name': 'process_name',
+        'ph': 'M',  # Metadata
+        'pid': process,
+        'args': {
+          'name': timeline_model.processes[process].name
+          }
+        })
+    for thread in timeline_model.processes[process].threads:
+      out.append({
+          'name': 'thread_name',
+          'ph': 'M',  # Metadata
+          'pid': process,
+          'tid': thread,
+          'args': {
+            'name': timeline_model.processes[process].threads[thread].name
+            }
+          })
+  return out
+
+
+class _SingleProcessStraceProfiler(object):
+  """An internal class for using perf for a given process."""
+  def __init__(self, pid, output_file, platform_backend):
+    self._pid = pid
+    self._platform_backend = platform_backend
+    self._output_file = output_file
+    self._tmp_output_file = tempfile.NamedTemporaryFile('w', 0)
+    self._proc = subprocess.Popen(
+        ['strace', '-ttt', '-f', '-T', '-p', str(pid), '-o', output_file],
+        stdout=self._tmp_output_file, stderr=subprocess.STDOUT)
+
+  def CollectProfile(self):
+    if ('renderer' in self._output_file and
+        not self._platform_backend.GetCommandLine(self._pid)):
+      logging.warning('Renderer was swapped out during profiling. '
+                      'To collect a full profile rerun with '
+                      '"--extra-browser-args=--single-process"')
+    self._proc.send_signal(signal.SIGINT)
+    exit_code = self._proc.wait()
+    try:
+      if exit_code:
+        raise Exception('strace failed with exit code %d. Output:\n%s' % (
+                        exit_code, self._GetStdOut()))
+    finally:
+      self._tmp_output_file.close()
+
+    return _StraceToChromeTrace(self._pid, self._output_file)
+
+  def _GetStdOut(self):
+    self._tmp_output_file.flush()
+    try:
+      with open(self._tmp_output_file.name) as f:
+        return f.read()
+    except IOError:
+      return ''
+
+
+class StraceProfiler(profiler.Profiler):
+
+  def __init__(self, browser_backend, platform_backend, output_path, state):
+    super(StraceProfiler, self).__init__(
+        browser_backend, platform_backend, output_path, state)
+    assert self._browser_backend.supports_tracing
+    self._browser_backend.browser.StartTracing(None, timeout=10)
+    process_output_file_map = self._GetProcessOutputFileMap()
+    self._process_profilers = []
+    self._output_file = output_path + '.json'
+    for pid, output_file in process_output_file_map.iteritems():
+      if 'zygote' in output_file:
+        continue
+      self._process_profilers.append(
+          _SingleProcessStraceProfiler(pid, output_file, platform_backend))
+
+  @classmethod
+  def name(cls):
+    return 'strace'
+
+  @classmethod
+  def is_supported(cls, browser_type):
+    if sys.platform != 'linux2':
+      return False
+    # TODO(tonyg): This should be supported on android and cros.
+    if (browser_type.startswith('android') or
+       browser_type.startswith('cros')):
+      return False
+    return True
+
+  @classmethod
+  def CustomizeBrowserOptions(cls, browser_type, options):
+    options.AppendExtraBrowserArgs([
+        '--no-sandbox',
+        '--allow-sandbox-debugging'
+    ])
+
+  def CollectProfile(self):
+    print 'Processing trace...'
+
+    out_json = []
+
+    for single_process in self._process_profilers:
+      out_json.extend(single_process.CollectProfile())
+
+    trace_data_builder = trace_data_module.TraceDataBuilder()
+    self._browser_backend.browser.StopTracing(trace_data_builder)
+    timeline_model = model.TimelineModel(trace_data_builder.AsData())
+    out_json.extend(_GenerateTraceMetadata(timeline_model))
+
+    with open(self._output_file, 'w') as f:
+      f.write(json.dumps(out_json, separators=(',', ':')))
+
+    print 'Trace saved as %s' % self._output_file
+    print 'To view, open in chrome://tracing'
+    return [self._output_file]
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/tcmalloc_heap_profiler.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/tcmalloc_heap_profiler.py
new file mode 100644
index 0000000..382bfb6
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/tcmalloc_heap_profiler.py
@@ -0,0 +1,146 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+import os
+import sys
+
+from telemetry.internal.backends.chrome import android_browser_finder
+from telemetry.internal.platform import profiler
+
+# Environment variables to (android properties, default value) mapping.
+_ENV_VARIABLES = {
+  'HEAP_PROFILE_TIME_INTERVAL': ('heapprof.time_interval', 20),
+  'HEAP_PROFILE_MMAP': ('heapprof.mmap', 1),
+  'DEEP_HEAP_PROFILE': ('heapprof.deep_heap_profile', 1),
+}
+
+
+class _TCMallocHeapProfilerAndroid(object):
+  """An internal class to set android properties and fetch dumps from device."""
+
+  _DEFAULT_DEVICE_DIR = '/data/local/tmp/heap'
+
+  def __init__(self, browser_backend, output_path):
+    self._browser_backend = browser_backend
+    self._output_path = output_path
+
+    _ENV_VARIABLES['HEAPPROFILE'] = ('heapprof',
+        os.path.join(self._DEFAULT_DEVICE_DIR, 'dmprof'))
+
+    self._SetDeviceProperties(_ENV_VARIABLES)
+
+  def _SetDeviceProperties(self, properties):
+    device_configured = False
+    # This profiler requires adb root to set properties.
+    self._browser_backend.device.EnableRoot()
+    for values in properties.itervalues():
+      device_property = self._browser_backend.device.GetProp(values[0])
+      if not device_property or not device_property.strip():
+        self._browser_backend.device.SetProp(values[0], values[1])
+        device_configured = True
+    if not self._browser_backend.device.FileExists(
+        self._DEFAULT_DEVICE_DIR):
+      self._browser_backend.device.RunShellCommand(
+          ['mkdir', '-p', self._DEFAULT_DEVICE_DIR], check_return=True)
+      self._browser_backend.device.RunShellCommand(
+          ['chmod', '777', self._DEFAULT_DEVICE_DIR], check_return=True)
+      device_configured = True
+    if device_configured:
+      raise Exception('Device required special config, run again.')
+
+  def CollectProfile(self):
+    try:
+      self._browser_backend.device.PullFile(
+          self._DEFAULT_DEVICE_DIR, self._output_path)
+    except:
+      logging.exception('New exception caused by DeviceUtils conversion')
+      raise
+    # Note: command must be passed as a string to expand wildcards.
+    self._browser_backend.device.RunShellCommand(
+        'rm -f ' + os.path.join(self._DEFAULT_DEVICE_DIR, '*'),
+        check_return=True, shell=True)
+    if os.path.exists(self._output_path):
+      logging.info('TCMalloc dumps pulled to %s', self._output_path)
+      with file(os.path.join(self._output_path,
+                             'browser.pid'), 'w') as pid_file:
+        pid_file.write(str(self._browser_backend.pid).rjust(5, '0'))
+    return [self._output_path]
+
+
+class _TCMallocHeapProfilerLinux(object):
+  """An internal class to set environment variables and fetch dumps."""
+
+  _DEFAULT_DIR = '/tmp/tcmalloc/'
+
+  def __init__(self, browser_backend):
+    self._browser_backend = browser_backend
+    _ENV_VARIABLES['HEAPPROFILE'] = ('heapprof', self._DEFAULT_DIR + 'dmprof')
+    self._CheckEnvironmentVariables(_ENV_VARIABLES)
+
+  def _CheckEnvironmentVariables(self, env_vars):
+    msg = ''
+    for key, values in env_vars.iteritems():
+      if key not in os.environ:
+        msg += '%s=%s ' % (key, str(values[1]))
+    if msg:
+      raise Exception('Need environment variables, try again with:\n %s' % msg)
+    if not os.path.exists(os.environ['HEAPPROFILE']):
+      os.makedirs(os.environ['HEAPPROFILE'])
+    assert os.path.isdir(os.environ['HEAPPROFILE']), 'HEAPPROFILE is not a dir'
+
+  def CollectProfile(self):
+    with file(os.path.join(os.path.dirname(os.environ['HEAPPROFILE']),
+                           'browser.pid'), 'w') as pid_file:
+      pid_file.write(str(self._browser_backend.pid))
+    print 'TCMalloc dumps available ', os.environ['HEAPPROFILE']
+    return [os.environ['HEAPPROFILE']]
+
+
+class TCMallocHeapProfiler(profiler.Profiler):
+  """A Factory to instantiate the platform-specific profiler."""
+  def __init__(self, browser_backend, platform_backend, output_path, state):
+    super(TCMallocHeapProfiler, self).__init__(
+        browser_backend, platform_backend, output_path, state)
+    if platform_backend.GetOSName() == 'android':
+      self._platform_profiler = _TCMallocHeapProfilerAndroid(
+          browser_backend, output_path)
+    else:
+      self._platform_profiler = _TCMallocHeapProfilerLinux(browser_backend)
+
+  @classmethod
+  def name(cls):
+    return 'tcmalloc-heap'
+
+  @classmethod
+  def is_supported(cls, browser_type):
+    if browser_type.startswith('cros'):
+      return False
+    if sys.platform.startswith('linux'):
+      return True
+    if browser_type == 'any':
+      return android_browser_finder.CanFindAvailableBrowsers()
+    return browser_type.startswith('android')
+
+  @classmethod
+  def CustomizeBrowserOptions(cls, browser_type, options):
+    options.AppendExtraBrowserArgs('--no-sandbox')
+    options.AppendExtraBrowserArgs('--enable-memory-benchmarking')
+
+  @classmethod
+  def WillCloseBrowser(cls, browser_backend, platform_backend):
+    # The tcmalloc_heap_profiler dumps files at regular
+    # intervals (~20 secs).
+    # This is a minor optimization to ensure it'll dump the last file when
+    # the test completes.
+    for i in xrange(len(browser_backend.browser.tabs)):
+      browser_backend.browser.tabs[i].ExecuteJavaScript("""
+        if (chrome && chrome.memoryBenchmarking) {
+          chrome.memoryBenchmarking.heapProfilerDump('renderer', 'final');
+          chrome.memoryBenchmarking.heapProfilerDump('browser', 'final');
+        }
+      """)
+
+  def CollectProfile(self):
+    return self._platform_profiler.CollectProfile()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/tcpdump_profiler.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/tcpdump_profiler.py
new file mode 100644
index 0000000..a8527cb
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/tcpdump_profiler.py
@@ -0,0 +1,128 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import signal
+import subprocess
+import sys
+import tempfile
+
+from telemetry.internal.platform import profiler
+from telemetry.internal.platform.profiler import android_prebuilt_profiler_helper
+
+_TCP_DUMP_BASE_OPTS = ['-i', 'any', '-p', '-s', '0', '-w']
+
+
+class _TCPDumpProfilerAndroid(object):
+  """An internal class to collect TCP dumps on android.
+
+  This profiler uses pre-built binaries from AOSP.
+  See more details in prebuilt/android/README.txt.
+  """
+
+  _DEVICE_DUMP_FILE = '/sdcard/tcpdump_profiles/capture.pcap'
+
+  def __init__(self, device, output_path):
+    self._device = device
+    self._output_path = output_path
+    self._device.RunShellCommand(
+        ['mkdir', '-p', os.path.dirname(self._DEVICE_DUMP_FILE)],
+        check_return=True)
+    self._proc = subprocess.Popen(
+        [self._device.adb.GetAdbPath(),
+         '-s', self._device.adb.GetDeviceSerial(),
+         'shell', android_prebuilt_profiler_helper.GetDevicePath('tcpdump')] +
+         _TCP_DUMP_BASE_OPTS +
+         [self._DEVICE_DUMP_FILE])
+
+  def CollectProfile(self):
+    tcpdump_pid = self._device.GetPids('tcpdump')
+    if not tcpdump_pid or not 'tcpdump' in tcpdump_pid:
+      raise Exception('Unable to find TCPDump. Check your device is rooted '
+          'and tcpdump is installed at ' +
+          android_prebuilt_profiler_helper.GetDevicePath('tcpdump'))
+    if len(tcpdump_pid['tcpdump']) > 1:
+      raise Exception(
+          'At most one instance of process tcpdump expected but found pids: '
+          '%s' % tcpdump_pid)
+    tcpdump_pid = int(tcpdump_pid['tcpdump'][0])
+    self._device.RunShellCommand(
+        ['kill', '-term', str(tcpdump_pid)], check_return=True)
+    self._proc.terminate()
+    host_dump = os.path.join(self._output_path,
+                             os.path.basename(self._DEVICE_DUMP_FILE))
+    self._device.PullFile(self._DEVICE_DUMP_FILE, host_dump)
+    print 'TCP dump available at: %s ' % host_dump
+    print 'Use Wireshark to open it.'
+    return host_dump
+
+
+class _TCPDumpProfilerLinux(object):
+  """An internal class to collect TCP dumps on linux desktop."""
+
+  _DUMP_FILE = 'capture.pcap'
+
+  def __init__(self, output_path):
+    if not os.path.exists(output_path):
+      os.makedirs(output_path)
+    self._dump_file = os.path.join(output_path, self._DUMP_FILE)
+    self._tmp_output_file = tempfile.NamedTemporaryFile('w', 0)
+    try:
+      self._proc = subprocess.Popen(
+          ['tcpdump'] + _TCP_DUMP_BASE_OPTS + [self._dump_file],
+          stdout=self._tmp_output_file, stderr=subprocess.STDOUT)
+    except OSError as e:
+      raise Exception('Unable to execute TCPDump, please check your '
+          'installation. ' + str(e))
+
+  def CollectProfile(self):
+    self._proc.send_signal(signal.SIGINT)
+    exit_code = self._proc.wait()
+    try:
+      if exit_code:
+        raise Exception(
+            'tcpdump failed with exit code %d. Output:\n%s' %
+            (exit_code, self._GetStdOut()))
+    finally:
+      self._tmp_output_file.close()
+    print 'TCP dump available at: ', self._dump_file
+    print 'Use Wireshark to open it.'
+    return self._dump_file
+
+  def _GetStdOut(self):
+    self._tmp_output_file.flush()
+    try:
+      with open(self._tmp_output_file.name) as f:
+        return f.read()
+    except IOError:
+      return ''
+
+
+class TCPDumpProfiler(profiler.Profiler):
+  """A Factory to instantiate the platform-specific profiler."""
+  def __init__(self, browser_backend, platform_backend, output_path, state):
+    super(TCPDumpProfiler, self).__init__(
+        browser_backend, platform_backend, output_path, state)
+    if platform_backend.GetOSName() == 'android':
+      android_prebuilt_profiler_helper.InstallOnDevice(
+          browser_backend.device, 'tcpdump')
+      self._platform_profiler = _TCPDumpProfilerAndroid(
+          browser_backend.device, output_path)
+    else:
+      self._platform_profiler = _TCPDumpProfilerLinux(output_path)
+
+  @classmethod
+  def name(cls):
+    return 'tcpdump'
+
+  @classmethod
+  def is_supported(cls, browser_type):
+    if browser_type.startswith('cros'):
+      return False
+    if sys.platform.startswith('linux'):
+      return True
+    return browser_type.startswith('android')
+
+  def CollectProfile(self):
+    return self._platform_profiler.CollectProfile()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/trace_profiler.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/trace_profiler.py
new file mode 100644
index 0000000..a9a662a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/trace_profiler.py
@@ -0,0 +1,79 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+
+from telemetry.internal.platform import profiler
+from telemetry.timeline import chrome_trace_category_filter
+from telemetry.timeline import tracing_config
+from tracing.trace_data import trace_data as trace_data_module
+
+
+class TraceProfiler(profiler.Profiler):
+
+  def __init__(self, browser_backend, platform_backend, output_path, state,
+               categories=None):
+    super(TraceProfiler, self).__init__(
+        browser_backend, platform_backend, output_path, state)
+    assert self._browser_backend.supports_tracing
+    # We always want flow events when tracing via telemetry.
+    categories_with_flow = 'disabled-by-default-toplevel.flow'
+    if categories:
+      categories_with_flow += ',%s' % categories
+    config = tracing_config.TracingConfig()
+    config.enable_chrome_trace = True
+    config.chrome_trace_config.SetCategoryFilter(
+        chrome_trace_category_filter.ChromeTraceCategoryFilter(
+            categories_with_flow))
+    self._browser_backend.StartTracing(config, timeout=10)
+
+  @classmethod
+  def name(cls):
+    return 'trace'
+
+  @classmethod
+  def is_supported(cls, browser_type):
+    return True
+
+  def CollectProfile(self):
+    print 'Processing trace...'
+
+    trace_result_builder = trace_data_module.TraceDataBuilder()
+    self._browser_backend.StopTracing()
+    self._browser_backend.CollectTracingData(trace_result_builder)
+    trace_result = trace_result_builder.AsData()
+    try:
+      trace_file = '%s.html' % self._output_path
+      title = os.path.basename(self._output_path)
+      trace_result.Serialize(trace_file, trace_title=title)
+    finally:
+      trace_result.CleanUpAllTraces()
+
+    print 'Trace saved as file:///%s' % os.path.abspath(trace_file)
+
+    return [trace_file]
+
+
+class TraceDetailedProfiler(TraceProfiler):
+
+  def __init__(self, browser_backend, platform_backend, output_path, state):
+    super(TraceDetailedProfiler, self).__init__(
+        browser_backend, platform_backend, output_path, state,
+        categories='disabled-by-default-cc.debug*')
+
+  @classmethod
+  def name(cls):
+    return 'trace-detailed'
+
+
+class TraceAllProfiler(TraceProfiler):
+
+  def __init__(self, browser_backend, platform_backend, output_path, state):
+    super(TraceAllProfiler, self).__init__(
+        browser_backend, platform_backend, output_path, state,
+        categories='disabled-by-default-*')
+
+  @classmethod
+  def name(cls):
+    return 'trace-all'
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/trace_profiler_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/trace_profiler_unittest.py
new file mode 100644
index 0000000..3e5f542
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/trace_profiler_unittest.py
@@ -0,0 +1,26 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import os
+import shutil
+import tempfile
+
+from telemetry.internal.platform.profiler import trace_profiler
+from telemetry.testing import tab_test_case
+
+
+class TestTraceProfiler(tab_test_case.TabTestCase):
+
+  def testTraceProfiler(self):
+    try:
+      out_dir = tempfile.mkdtemp()
+      profiler = trace_profiler.TraceProfiler(
+          self._browser._browser_backend,
+          self._browser._platform_backend,
+          os.path.join(out_dir, 'trace'),
+          {})
+      result = profiler.CollectProfile()[0]
+      with open(result) as f:
+        self.assertTrue(f.read())
+    finally:
+      shutil.rmtree(out_dir)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/v8_profiler.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/v8_profiler.py
new file mode 100644
index 0000000..b2d930f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/v8_profiler.py
@@ -0,0 +1,48 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import re
+import tempfile
+
+from telemetry.internal.platform import profiler
+
+
+class V8Profiler(profiler.Profiler):
+
+  _V8_ARG = '--js-flags=--logfile=%s --prof --log-timer-events'
+
+  @classmethod
+  def name(cls):
+    return 'v8'
+
+  @classmethod
+  def is_supported(cls, browser_type):
+    return not browser_type.startswith('cros')
+
+  @classmethod
+  def CustomizeBrowserOptions(cls, browser_type, options):
+    if browser_type.startswith('android'):
+      dump_file = '/data/local/tmp/v8-profile.log'
+    else:
+      dump_file = tempfile.mkstemp()[1]
+    options.AppendExtraBrowserArgs([cls._V8_ARG % dump_file, '--no-sandbox'])
+
+  def CollectProfile(self):
+    # Find output filename from browser argument.
+    for i in self._browser_backend.browser_options.extra_browser_args:
+      match = re.match(self._V8_ARG % r'(\S+)', i)
+      if match:
+        output_file = match.groups(0)[0]
+    assert output_file
+    # On Android pull the output file to the host.
+    if self._platform_backend.GetOSName() == 'android':
+      host_output_file = '%s.log' % self._output_path
+      self._browser_backend.device.PullFile(output_file, host_output_file)
+      # Clean the device
+      self._browser_backend.device.RemovePath(output_file)
+      output_file = host_output_file
+    print 'V8 profile saved as %s' % output_file
+    print 'To view, open in ' \
+          'http://v8.googlecode.com/svn/trunk/tools/tick-processor.html'
+    return [output_file]
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/vtune_profiler.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/vtune_profiler.py
new file mode 100644
index 0000000..edbf6ef
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/vtune_profiler.py
@@ -0,0 +1,161 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+import os
+import subprocess
+import sys
+import tempfile
+
+from telemetry.core import exceptions
+from telemetry.internal.platform import profiler
+from telemetry.internal.platform.profiler import android_profiling_helper
+
+from devil.android.sdk import adb_wrapper
+
+
+class _SingleProcessVTuneProfiler(object):
+  """An internal class for using vtune for a given process."""
+  def __init__(self, pid, output_file, browser_backend, platform_backend):
+    self._pid = pid
+    self._browser_backend = browser_backend
+    self._platform_backend = platform_backend
+    self._output_file = output_file
+    self._tmp_output_file = tempfile.NamedTemporaryFile('w', 0)
+    cmd = ['amplxe-cl', '-collect', 'hotspots',
+           '-target-pid', str(pid), '-r', self._output_file]
+    self._is_android = platform_backend.GetOSName() == 'android'
+    if self._is_android:
+      cmd += ['-target-system', 'android']
+
+    self._proc = subprocess.Popen(
+        cmd, stdout=self._tmp_output_file, stderr=subprocess.STDOUT)
+
+  def CollectProfile(self):
+    if 'renderer' in self._output_file:
+      try:
+        self._platform_backend.GetCommandLine(self._pid)
+      except exceptions.ProcessGoneException:
+        logging.warning('Renderer was swapped out during profiling. '
+                        'To collect a full profile rerun with '
+                        '"--extra-browser-args=--single-process"')
+    subprocess.call(['amplxe-cl', '-command', 'stop', '-r', self._output_file])
+
+    exit_code = self._proc.wait()
+    try:
+      # 1: amplxe: Error: Cannot find a running process with the specified ID.
+      #    Provide a valid PID.
+      if exit_code not in (0, 1):
+        raise Exception(
+            'amplxe-cl failed with exit code %d. Output:\n%s' % (exit_code,
+            self._GetStdOut()))
+    finally:
+      self._tmp_output_file.close()
+
+    if exit_code:
+      # The renderer process was swapped out. Now that we made sure VTune has
+      # stopped, return without further processing the invalid profile.
+      return self._output_file
+
+    if self._is_android:
+      required_libs = \
+          android_profiling_helper.GetRequiredLibrariesForVTuneProfile(
+              self._output_file)
+
+      device = self._browser_backend.device
+      symfs_root = os.path.join(os.path.dirname(self._output_file), 'symfs')
+      if not os.path.exists(symfs_root):
+        os.makedirs(symfs_root)
+      android_profiling_helper.CreateSymFs(device,
+                                           symfs_root,
+                                           required_libs,
+                                           use_symlinks=True)
+      logging.info('Resolving symbols in profile.')
+      subprocess.call(['amplxe-cl', '-finalize', '-r', self._output_file,
+                       '-search-dir', symfs_root])
+
+    print 'To view the profile, run:'
+    print '  amplxe-gui %s' % self._output_file
+
+    return self._output_file
+
+  def _GetStdOut(self):
+    self._tmp_output_file.flush()
+    try:
+      with open(self._tmp_output_file.name) as f:
+        return f.read()
+    except IOError:
+      return ''
+
+
+class VTuneProfiler(profiler.Profiler):
+
+  def __init__(self, browser_backend, platform_backend, output_path, state):
+    super(VTuneProfiler, self).__init__(
+        browser_backend, platform_backend, output_path, state)
+    process_output_file_map = self._GetProcessOutputFileMap()
+    self._process_profilers = []
+
+    has_renderer = False
+    for pid, output_file in process_output_file_map.iteritems():
+      if 'renderer' in output_file:
+        has_renderer = True
+        break
+
+    for pid, output_file in process_output_file_map.iteritems():
+      if has_renderer:
+        if not 'renderer' in output_file:
+          continue
+      elif not 'browser0' in output_file:
+        continue
+
+      self._process_profilers.append(
+          _SingleProcessVTuneProfiler(pid, output_file, browser_backend,
+                                      platform_backend))
+
+  @classmethod
+  def name(cls):
+    return 'vtune'
+
+  @classmethod
+  def is_supported(cls, browser_type):
+    if sys.platform != 'linux2':
+      return False
+    if browser_type.startswith('cros'):
+      return False
+    try:
+      proc = subprocess.Popen(['amplxe-cl', '-version'],
+                              stderr=subprocess.STDOUT,
+                              stdout=subprocess.PIPE)
+      proc.communicate()
+      if proc.returncode != 0:
+        return False
+
+      if browser_type.startswith('android'):
+        # VTune checks if 'su' is available on the device.
+        proc = subprocess.Popen(
+            [adb_wrapper.AdbWrapper.GetAdbPath(),
+             'shell', 'su', '-c', 'id'],
+            stderr=subprocess.STDOUT,
+            stdout=subprocess.PIPE)
+        return 'not found' not in proc.communicate()[0]
+
+      return True
+    except OSError:
+      return False
+
+  @classmethod
+  def CustomizeBrowserOptions(cls, browser_type, options):
+    options.AppendExtraBrowserArgs([
+        '--no-sandbox',
+        '--allow-sandbox-debugging',
+    ])
+
+  def CollectProfile(self):
+    print 'Processing profile, this will take a few minutes...'
+
+    output_files = []
+    for single_process in self._process_profilers:
+      output_files.append(single_process.CollectProfile())
+    return output_files
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/vtune_profiler_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/vtune_profiler_unittest.py
new file mode 100644
index 0000000..d763d77
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/vtune_profiler_unittest.py
@@ -0,0 +1,118 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import sys
+import unittest
+
+from telemetry import decorators
+from telemetry.internal.platform.profiler import vtune_profiler
+from telemetry.testing import options_for_unittests
+from telemetry.testing import simple_mock
+from telemetry.testing import tab_test_case
+
+
+class MockPopen(object):
+  def __init__(self, returncode, stdout=None, stderr=None):
+    self.returncode = returncode
+    self.stdout = stdout
+    self.stderr = stderr
+
+  def communicate(self):
+    return (self.stdout, self.stderr)
+
+  def wait(self):
+    return self.returncode
+
+
+class MockSubprocess(object):
+  def __init__(self):
+    self.PIPE = simple_mock.MockObject()
+    self.STDOUT = simple_mock.MockObject()
+    self._num_collect_calls = 0
+    self._num_stop_calls = 0
+
+  @property
+  def num_collect_calls(self):
+    return self._num_collect_calls
+
+  @property
+  def num_stop_calls(self):
+    return self._num_stop_calls
+
+  def Popen(self, cmd, **_):
+    self._AnalyzeCommand(cmd)
+    return MockPopen(0)
+
+  def call(self, cmd):
+    self._AnalyzeCommand(cmd)
+
+  def _AnalyzeCommand(self, cmd):
+    if MockSubprocess._IsCollectCommand(cmd):
+      self._num_collect_calls += 1
+    elif MockSubprocess._IsStopCommand(cmd):
+      self._num_stop_calls += 1
+
+  @staticmethod
+  def _IsCollectCommand(cmd):
+    return '-collect' in cmd
+
+  @staticmethod
+  def _IsStopCommand(cmd):
+    try:
+      cmd_idx = cmd.index('-command') + 1
+      return cmd_idx < len(cmd) and cmd[cmd_idx] == 'stop'
+    except ValueError:
+      return False
+
+
+class TestVTuneProfiler(unittest.TestCase):
+
+  def testVTuneProfilerIsSupported(self):
+    options = options_for_unittests.GetCopy()
+
+    mock_subprocess = simple_mock.MockObject()
+    mock_subprocess.ExpectCall(
+        'Popen').WithArgs(simple_mock.DONT_CARE).WillReturn(MockPopen(0))
+    mock_subprocess.SetAttribute('PIPE', simple_mock.MockObject())
+    mock_subprocess.SetAttribute('STDOUT', simple_mock.MockObject())
+
+    real_subprocess = vtune_profiler.subprocess
+    vtune_profiler.subprocess = mock_subprocess
+
+    if options.browser_type.startswith('android'):
+      # On Android we're querying if 'su' is available.
+      mock_subprocess.ExpectCall('Popen').WithArgs(
+          simple_mock.DONT_CARE).WillReturn(MockPopen(0, 'su', None))
+
+    try:
+      self.assertTrue(
+          vtune_profiler.VTuneProfiler.is_supported(options.browser_type) or
+          sys.platform != 'linux2' or
+          options.browser_type.startswith('cros'))
+    finally:
+      vtune_profiler.subprocess = real_subprocess
+
+
+class TestVTuneProfilerTabTestCase(tab_test_case.TabTestCase):
+
+  # This test is only meant to be run if VTune is installed locally. Please
+  # run it locally if you are modifying related code, but it's disabled on the
+  # bots because they don't have VTune. See crbug.com/437085
+  @decorators.Disabled('all')
+  def testVTuneProfiler(self):
+    mock_subprocess = MockSubprocess()
+    real_subprocess = vtune_profiler.subprocess
+    vtune_profiler.subprocess = mock_subprocess
+
+    try:
+      # pylint: disable=protected-access
+      profiler = vtune_profiler.VTuneProfiler(self._browser._browser_backend,
+                                              self._browser._platform_backend,
+                                              'tmp',
+                                              {})
+      profiler.CollectProfile()
+      self.assertEqual(mock_subprocess.num_collect_calls,
+                       mock_subprocess.num_stop_calls)
+    finally:
+      vtune_profiler.subprocess = real_subprocess
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/win_pgo_profiler.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/win_pgo_profiler.py
new file mode 100644
index 0000000..d11e43b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiler/win_pgo_profiler.py
@@ -0,0 +1,102 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import glob
+import os
+import subprocess
+import sys
+
+from telemetry.internal.platform import profiler
+
+_PGOSWEEP_EXECUTABLE = 'pgosweep.exe'
+
+
+class WinPGOProfiler(profiler.Profiler):
+  """A profiler that run the Visual Studio PGO utility 'pgosweep.exe' before
+  terminating a browser or a renderer process.
+  """
+
+  def __init__(self, browser_backend, platform_backend, output_path, state):
+    super(WinPGOProfiler, self).__init__(
+        browser_backend, platform_backend, output_path, state)
+
+    self._pgosweep_path = None
+
+    if os.path.exists(os.path.join(browser_backend.browser_directory,
+                                   _PGOSWEEP_EXECUTABLE)):
+      self._pgosweep_path = os.path.join(browser_backend.browser_directory,
+                                         _PGOSWEEP_EXECUTABLE)
+
+    if not self._pgosweep_path:
+      for entry in os.environ['PATH'].split(os.pathsep):
+        if os.path.exists(os.path.join(entry, _PGOSWEEP_EXECUTABLE)):
+          self._pgosweep_path = os.path.join(entry, _PGOSWEEP_EXECUTABLE)
+          break
+    if not self._pgosweep_path:
+      raise IOError(2, 'Can\'t find %s, run vcvarsall.bat to fix this.' %
+                    _PGOSWEEP_EXECUTABLE)
+
+    self._browser_dir = browser_backend.browser_directory
+    self._chrome_child_pgc_counter = self._GetNextProfileIndex('chrome_child')
+
+  def _GetNextProfileIndex(self, dll_name):
+    """Scan the directory containing the DLL |dll_name| to find the next index
+    to use for the profile data files.
+
+    Args:
+      dll_name: The name of the DLL for which we want to get the next index to
+          to use.
+    """
+    max_index = 0
+    pgc_files = glob.glob(os.path.join(self._browser_dir,
+                                       '%s!*.pgc' % dll_name))
+    for pgc_file in pgc_files:
+      max_index = max(max_index,
+          int(os.path.splitext(os.path.split(pgc_file)[1])[0].split('!')[1]))
+    return max_index + 1
+
+  def _RunPGOSweep(self, pid, dll_name, index):
+    """Run the pgosweep utility to gather the profile data of a given process.
+
+    Args:
+      pid: The PID of the process we're interested in.
+      dll_name: The name of the DLL for which we want the profile data.
+      index: The index to use for the profile data file.
+
+    Returns the name of the profile data file.
+    """
+    pgc_filename = '%s\\%s!%d.pgc' % (self._browser_dir, dll_name, index)
+    subprocess.Popen([self._pgosweep_path,
+                      '/pid:%d' % pid,
+                      '%s.dll' % dll_name,
+                      pgc_filename]
+                    ).wait()
+    return pgc_filename
+
+  @classmethod
+  def name(cls):
+    return 'win_pgo_profiler'
+
+  @classmethod
+  def is_supported(cls, browser_type):
+    # This profiler only make sense when doing a Windows build with Visual
+    # Studio (minimal supported version is 2013 Update 2).
+    return sys.platform.startswith('win')
+
+  @classmethod
+  def CustomizeBrowserOptions(cls, browser_type, options):
+    # The sandbox need to be disabled if we want to be able to gather the
+    # profile data.
+    options.AppendExtraBrowserArgs('--no-sandbox')
+
+  def CollectProfile(self):
+    """Collect the profile data for the current processes."""
+    output_files = []
+    for pid, output_file in self._GetProcessOutputFileMap().iteritems():
+      if 'renderer' in output_file:
+        output_files.append(self._RunPGOSweep(pid,
+                                              'chrome_child',
+                                              self._chrome_child_pgc_counter))
+        self._chrome_child_pgc_counter += 1
+    return output_files
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiling_controller_backend.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiling_controller_backend.py
new file mode 100644
index 0000000..744bc28
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/profiling_controller_backend.py
@@ -0,0 +1,47 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+from telemetry.internal.platform.profiler import profiler_finder
+
+
+class ProfilingControllerBackend(object):
+  def __init__(self, platform_backend, browser_backend):
+    self._platform_backend = platform_backend
+    self._browser_backend = browser_backend
+    self._active_profilers = []
+    self._profilers_states = {}
+
+  def Start(self, profiler_name, base_output_file):
+    """Starts profiling using |profiler_name|. Results are saved to
+    |base_output_file|.<process_name>."""
+    assert not self._active_profilers, 'Already profiling. Must stop first.'
+
+    profiler_class = profiler_finder.FindProfiler(profiler_name)
+
+    if not profiler_class.is_supported(self._browser_backend.browser_type):
+      raise Exception('The %s profiler is not '
+                      'supported on this platform.' % profiler_name)
+
+    if not profiler_class in self._profilers_states:
+      self._profilers_states[profiler_class] = {}
+
+    self._active_profilers.append(
+        profiler_class(self._browser_backend, self._platform_backend,
+            base_output_file, self._profilers_states[profiler_class]))
+
+  def Stop(self):
+    """Stops all active profilers and saves their results.
+
+    Returns:
+      A list of filenames produced by the profiler.
+    """
+    output_files = []
+    for profiler in self._active_profilers:
+      output_files.extend(profiler.CollectProfile())
+    self._active_profilers = []
+    return output_files
+
+  def WillCloseBrowser(self):
+    for profiler_class in self._profilers_states:
+      profiler_class.WillCloseBrowser(
+        self._browser_backend, self._platform_backend)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/remote_platform_options.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/remote_platform_options.py
new file mode 100644
index 0000000..05c9df5
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/remote_platform_options.py
@@ -0,0 +1,17 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+class RemotePlatformOptions(object):
+  """Options to be used for creating a remote platform instance."""
+
+
+class AndroidPlatformOptions(RemotePlatformOptions):
+  """Android-specific remote platform options."""
+
+  def __init__(self, device=None, android_blacklist_file=None):
+    super(AndroidPlatformOptions, self).__init__()
+
+    self.device = device
+    self.android_blacklist_file = android_blacklist_file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/system_info.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/system_info.py
new file mode 100644
index 0000000..8cf6d17
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/system_info.py
@@ -0,0 +1,41 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+from telemetry.internal.platform import gpu_info
+
+
+class SystemInfo(object):
+  """Provides low-level system information."""
+
+  def __init__(self, model_name, gpu_dict):
+    if (model_name == None) or (gpu_dict == None):
+      raise Exception("Missing model_name or gpu_dict argument")
+    self._model_name = model_name
+    self._gpu = gpu_info.GPUInfo.FromDict(gpu_dict)
+
+  @classmethod
+  def FromDict(cls, attrs):
+    """Constructs a SystemInfo from a dictionary of attributes.
+       Attributes currently required to be present in the dictionary:
+
+         model_name (string): a platform-dependent string
+           describing the model of machine, or the empty string if not
+           supported.
+         gpu (object containing GPUInfo's required attributes)
+    """
+    return cls(attrs["model_name"], attrs["gpu"])
+
+  @property
+  def model_name(self):
+    """A string describing the machine model.
+
+       This is a highly platform-dependent value and not currently
+       specified for any machine type aside from Macs. On Mac OS, this
+       is the model identifier, reformatted slightly; for example,
+       'MacBookPro 10.1'."""
+    return self._model_name
+
+  @property
+  def gpu(self):
+    """A GPUInfo object describing the graphics processor(s) on the system."""
+    return self._gpu
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/system_info_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/system_info_unittest.py
new file mode 100644
index 0000000..d4c341e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/system_info_unittest.py
@@ -0,0 +1,68 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import unittest
+
+from telemetry.internal.platform import gpu_device
+from telemetry.internal.platform import gpu_info
+from telemetry.internal.platform import system_info
+
+
+class TestSystemInfo(unittest.TestCase):
+
+  def testConstruction(self):
+    data = {
+        'model_name': 'MacBookPro 10.1',
+        'gpu': {
+            'devices': [
+                {'vendor_id': 1000, 'device_id': 2000,
+                 'vendor_string': 'a', 'device_string': 'b'},
+            ]
+        }
+    }
+    info = system_info.SystemInfo.FromDict(data)
+    self.assertTrue(isinstance(info, system_info.SystemInfo))
+    self.assertTrue(isinstance(info.gpu, gpu_info.GPUInfo))
+    self.assertEquals(info.model_name, 'MacBookPro 10.1')
+    self.assertTrue(len(info.gpu.devices) == 1)
+    self.assertTrue(isinstance(info.gpu.devices[0], gpu_device.GPUDevice))
+    self.assertEquals(info.gpu.devices[0].vendor_id, 1000)
+    self.assertEquals(info.gpu.devices[0].device_id, 2000)
+    self.assertEquals(info.gpu.devices[0].vendor_string, 'a')
+    self.assertEquals(info.gpu.devices[0].device_string, 'b')
+
+  def testEmptyModelName(self):
+    data = {
+        'model_name': '',
+        'gpu': {
+            'devices': [
+                {'vendor_id': 1000, 'device_id': 2000,
+                 'vendor_string': 'a', 'device_string': 'b'},
+            ]
+        }
+    }
+    try:
+      info = system_info.SystemInfo.FromDict(data)
+      self.assertEquals(info.model_name, '')
+    except AssertionError:
+      raise
+    except Exception:
+      self.fail('Should not raise exception for empty model_name string')
+
+  def testMissingAttrsFromDict(self):
+    data = {
+        'model_name': 'MacBookPro 10.1',
+        'devices': [{'vendor_id': 1000, 'device_id': 2000,
+                     'vendor_string': 'a', 'device_string': 'b'}]
+    }
+
+    for k in data:
+      data_copy = data.copy()
+      del data_copy[k]
+      try:
+        system_info.SystemInfo.FromDict(data_copy)
+        self.fail('Should raise exception if attribute "%s" is missing' % k)
+      except AssertionError:
+        raise
+      except KeyError:
+        pass
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/tracing_agent/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/tracing_agent/__init__.py
new file mode 100644
index 0000000..f57b0ec
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/tracing_agent/__init__.py
@@ -0,0 +1,98 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+class TracingAgent(object):
+  """A tracing agent provided by the platform.
+
+  A tracing agent can gather data with Start() until Stop().
+  Before constructing an TracingAgent, check whether it's supported on the
+  platform with IsSupported method first.
+
+  NOTE: All subclasses of TracingAgent must not change the constructor's
+  parameters so the agents can be dynamically constructed in
+  tracing_controller_backend.
+
+  """
+
+  def __init__(self, platform_backend):
+    self._platform_backend = platform_backend
+
+  @classmethod
+  def IsSupported(cls, platform_backend):
+    del platform_backend  # unused
+    return False
+
+  def StartAgentTracing(self, config, timeout):
+    """ Override to add tracing agent's custom logic to start tracing.
+
+    Depending on trace_options and category_filter, the tracing agent may choose
+    to start or not start tracing.
+
+    Args:
+      config: tracing_config instance that contains trace_option and
+        category_filter
+        trace_options: an instance of tracing_options.TracingOptions that
+          control which core tracing systems should be enabled.
+        category_filter: an instance of
+          chrome_trace_category_filter.ChromeTraceCategoryFilter
+      timeout: number of seconds that this tracing agent should try to start
+        tracing until time out.
+
+    Returns:
+      True if tracing agent started successfully.
+    """
+    raise NotImplementedError
+
+  def StopAgentTracing(self):
+    """ Override to add tracing agent's custom logic to stop tracing.
+
+    StopAgentTracing() should guarantee tracing is stopped, even if there may
+    be exception.
+    """
+    raise NotImplementedError
+
+  def SupportsFlushingAgentTracing(self):
+    """ Override to indicate support of flushing tracing. """
+    return False
+
+  def FlushAgentTracing(self, config, timeout, trace_data_builder):
+    """ Override to add tracing agent's custom logic to flush tracing. """
+    del config, timeout, trace_data_builder  # unused
+    raise NotImplementedError
+
+  def SupportsExplicitClockSync(self):
+    """ Override to indicate support of explicit clock syncing. """
+    return False
+
+  def RecordClockSyncMarker(self, sync_id,
+                            record_controller_clocksync_marker_callback):
+    """ Override to record clock sync marker.
+
+    Only override if supports explicit clock syncing.
+    Args:
+      sync_id: Unqiue id for sync event.
+      record_controller_clocksync_marker_callback: Function that accepts two
+        arguments: a sync ID and a timestamp taken immediately before the
+        controller requested that the agent write a clock sync marker into its
+        trace. Any tracing agent that implements this method must invoke this
+        callback immediately after receiving confirmation from the agent that
+        the clock sync marker was recorded.
+
+        We use a callback here rather than just calling this function after
+        RecordClockSyncMarker because it's important for clock sync accuracy
+        reasons that the "issued" timestamp and "received confirmation"
+        timestamp be as accurate as possible, and some agents are forced to do
+        additional time-consuming cleanup work in RecordClockSyncMarker after
+        receiving this confirmation.
+    """
+    del sync_id # unused
+    del record_controller_clocksync_marker_callback # unused
+    raise NotImplementedError
+
+  def CollectAgentTraceData(self, trace_data_builder, timeout=None):
+    """ Override to add agent's custom logic to collect tracing data. """
+    del trace_data_builder
+    del timeout
+    raise NotImplementedError
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/tracing_agent/atrace_tracing_agent.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/tracing_agent/atrace_tracing_agent.py
new file mode 100644
index 0000000..02ca3c7
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/tracing_agent/atrace_tracing_agent.py
@@ -0,0 +1,55 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from systrace.tracing_agents import atrace_agent
+from telemetry.internal.platform import tracing_agent
+from tracing.trace_data import trace_data
+
+from devil.android.sdk import version_codes
+
+
+class AtraceTracingAgent(tracing_agent.TracingAgent):
+  def __init__(self, platform_backend):
+    super(AtraceTracingAgent, self).__init__(platform_backend)
+    self._device = platform_backend.device
+    self._categories = None
+    self._atrace_agent = atrace_agent.AtraceAgent(
+        platform_backend.device.build_version_sdk)
+    self._config = None
+
+  @classmethod
+  def IsSupported(cls, platform_backend):
+    return (platform_backend.GetOSName() == 'android' and
+        platform_backend.device.build_version_sdk >
+            version_codes.JELLY_BEAN_MR1)
+
+  def StartAgentTracing(self, config, timeout):
+    if not config.enable_atrace_trace:
+      return False
+
+    app_name = (','.join(config.atrace_config.app_name) if
+        isinstance(config.atrace_config.app_name, list) else
+        config.atrace_config.app_name)
+    self._config = atrace_agent.AtraceConfig(
+        config.atrace_config.categories,
+        trace_buf_size=None, kfuncs=None, app_name=app_name,
+        compress_trace_data=True, from_file=True,
+        device_serial_number=str(self._device), trace_time=None,
+        target='android')
+    return self._atrace_agent.StartAgentTracing(self._config, timeout)
+
+  def StopAgentTracing(self):
+    self._atrace_agent.StopAgentTracing()
+
+  def SupportsExplicitClockSync(self):
+    return self._atrace_agent.SupportsExplicitClockSync()
+
+  def RecordClockSyncMarker(self, sync_id,
+                            record_controller_clock_sync_marker_callback):
+    return self._atrace_agent.RecordClockSyncMarker(sync_id,
+        lambda t, sid: record_controller_clock_sync_marker_callback(sid, t))
+
+  def CollectAgentTraceData(self, trace_data_builder, timeout=None):
+    raw_data = self._atrace_agent.GetResults(timeout).raw_data
+    trace_data_builder.AddTraceFor(trace_data.ATRACE_PART, raw_data)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/tracing_agent/battor_tracing_agent.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/tracing_agent/battor_tracing_agent.py
new file mode 100644
index 0000000..42dac1c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/tracing_agent/battor_tracing_agent.py
@@ -0,0 +1,113 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+
+from battor import battor_error
+from battor import battor_wrapper
+from py_utils import cloud_storage
+from devil.android import battery_utils
+from py_trace_event import trace_time
+from telemetry.internal.platform import tracing_agent
+from telemetry.internal.util import atexit_with_log
+from tracing.trace_data import trace_data
+
+
+def _ReenableChargingIfNeeded(battery):
+  if not battery.GetCharging():
+    battery.SetCharging(True)
+  logging.info('Charging status checked at exit.')
+
+
+class BattOrTracingAgent(tracing_agent.TracingAgent):
+  """A tracing agent for getting power data from a BattOr device.
+
+  BattOrTracingAgent allows Telemetry to issue high-level tracing commands
+  (StartTracing, StopTracing, RecordClockSyncMarker) to BattOrs, which are
+  high-frequency power monitors used for battery testing.
+  """
+
+  def __init__(self, platform_backend):
+    super(BattOrTracingAgent, self).__init__(platform_backend)
+    self._platform_backend = platform_backend
+    android_device = (
+        platform_backend.device if platform_backend.GetOSName() == 'android'
+        else None)
+    self._battery = (
+        battery_utils.BatteryUtils(platform_backend.device)
+        if platform_backend.GetOSName() == 'android' else None)
+    self._battor = battor_wrapper.BattOrWrapper(
+        platform_backend.GetOSName(), android_device=android_device,
+        serial_log_bucket=cloud_storage.TELEMETRY_OUTPUT)
+
+  @classmethod
+  def IsSupported(cls, platform_backend):
+    """Returns True if BattOr tracing is available."""
+    if platform_backend.GetOSName() == 'android':
+      # TODO(rnephew): When we pass BattOr device map into Telemetry, change
+      # this to reflect that.
+      return battor_wrapper.IsBattOrConnected(
+          'android', android_device=platform_backend.device)
+    return battor_wrapper.IsBattOrConnected(platform_backend.GetOSName())
+
+  def StartAgentTracing(self, config, timeout):
+    """Start tracing on the BattOr.
+
+    Args:
+      config: A TracingConfig instance.
+      timeout: number of seconds that this tracing agent should try to start
+        tracing until timing out.
+
+    Returns:
+      True if the tracing agent started successfully.
+    """
+    if not config.enable_battor_trace:
+      return False
+    try:
+      if self._battery:
+        self._battery.SetCharging(False)
+        atexit_with_log.Register(_ReenableChargingIfNeeded, self._battery)
+
+      self._battor.StartShell()
+      self._battor.StartTracing()
+      return True
+    except battor_error.BattOrError:
+      if self._battery:
+        self._battery.SetCharging(True)
+      raise
+
+  def StopAgentTracing(self):
+    """Stops tracing on the BattOr."""
+    try:
+      self._battor.StopTracing()
+    finally:
+      if self._battery:
+        self._battery.SetCharging(True)
+
+  def SupportsExplicitClockSync(self):
+    return self._battor.SupportsExplicitClockSync()
+
+  def RecordClockSyncMarker(self, sync_id,
+                            record_controller_clock_sync_marker_callback):
+    """Records a clock sync marker in the BattOr trace.
+
+    Args:
+      sync_id: Unique id for sync event.
+      record_controller_clock_sync_marker_callback: Function that takes a sync
+        ID and a timestamp as arguments. This function typically will record the
+        tracing controller clock sync marker.
+    """
+    timestamp = trace_time.Now()
+    try:
+      self._battor.RecordClockSyncMarker(sync_id)
+    except battor_error.BattOrError:
+      logging.critical(
+          'Error while clock syncing with BattOr. Killing BattOr shell.')
+      self._battor.KillBattOrShell()
+      raise
+    record_controller_clock_sync_marker_callback(sync_id, timestamp)
+
+  def CollectAgentTraceData(self, trace_data_builder, timeout=None):
+    data = self._battor.CollectTraceData(timeout=timeout)
+    trace_data_builder.AddTraceFor(trace_data.BATTOR_TRACE_PART, data)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/tracing_agent/battor_tracing_agent_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/tracing_agent/battor_tracing_agent_unittest.py
new file mode 100644
index 0000000..1c6dc06
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/tracing_agent/battor_tracing_agent_unittest.py
@@ -0,0 +1,196 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from battor import battor_error
+from battor import battor_wrapper
+from devil.android import battery_utils
+from telemetry.internal.platform.tracing_agent import battor_tracing_agent
+from telemetry.timeline import trace_data
+from telemetry.timeline import tracing_config
+from tracing.trace_data import trace_data
+
+
+_BATTOR_RETURN = 'fake\nbattor\ndata'
+
+
+class FakeBatteryUtils(object):
+  def __init__(self, device):
+    self._device = device
+    self._charging_state = True
+
+  def SetCharging(self, state):
+    self._charging_state = state
+
+  def GetCharging(self):
+    return self._charging_state
+
+
+class FakePlatformBackend(object):
+  def GetOSName(self):
+    return ''
+
+
+class FakeAndroidPlatformBackend(FakePlatformBackend):
+  def __init__(self):
+    super(FakeAndroidPlatformBackend, self).__init__()
+    self.device = 'fake_device'
+
+  def GetOSName(self):
+    return 'android'
+
+
+class FakeDesktopPlatformBackend(FakePlatformBackend):
+  def __init__(self):
+    self.platform = 'win'
+
+  def GetOSName(self):
+    return self.platform
+
+
+class FakeBattOr(object):
+  def __init__(self, test_platform, android_device=None, battor_path=None,
+               battor_map=None, serial_log_bucket=None):
+    self._is_shell_running = False
+    self._android_device = android_device
+    self._battor_path = battor_path
+    self._battor_map = battor_map
+    self._test_platform = test_platform
+    self._serial_log_bucket = serial_log_bucket
+    self._stop_tracing_called = False
+    self._start_shell_called = False
+    self._start_tracing_called = False
+    self._collect_trace_data_called = False
+    self._record_clock_sync_marker_called = False
+
+  def IsShellRunning(self):
+    return self._is_shell_running
+
+  def StartShell(self):
+    self._is_shell_running = True
+    self._start_shell_called = True
+
+  def StartTracing(self):
+    self.StartShell()
+    self._start_tracing_called = True
+
+  def StopTracing(self):
+    self._is_shell_running = False
+    self._stop_tracing_called = True
+
+  def CollectTraceData(self, timeout=None):
+    del timeout # unused
+    self._collect_trace_data_called = True
+    return _BATTOR_RETURN
+
+  def RecordClockSyncMarker(self, _):
+    self._record_clock_sync_marker_called = True
+
+
+class BattOrTracingAgentTest(unittest.TestCase):
+  def setUp(self):
+    self._config = tracing_config.TracingConfig()
+    self._config.enable_battor_trace = True
+
+    # Monkey patch BattOrWrapper.
+    self._battor_wrapper = battor_wrapper.BattOrWrapper
+    battor_wrapper.BattOrWrapper = FakeBattOr
+    battor_wrapper.IsBattOrConnected = lambda x, android_device=None: True
+
+    self._battery_utils = battery_utils.BatteryUtils
+    battery_utils.BatteryUtils = FakeBatteryUtils
+
+    # Agents and backends.
+    self.android_backend = FakeAndroidPlatformBackend()
+    self.desktop_backend = FakeDesktopPlatformBackend()
+    self.android_agent = (
+        battor_tracing_agent.BattOrTracingAgent(self.android_backend))
+    self.desktop_agent = (
+        battor_tracing_agent.BattOrTracingAgent(self.desktop_backend))
+
+  def tearDown(self):
+    battor_wrapper.BattOrWrapper = self._battor_wrapper
+    battery_utils.BatteryUtils = self._battery_utils
+
+  def testInit(self):
+    self.assertTrue(isinstance(self.android_agent._platform_backend,
+                               FakeAndroidPlatformBackend))
+    self.assertTrue(isinstance(self.desktop_agent._platform_backend,
+                               FakeDesktopPlatformBackend))
+
+  def testIsSupportedAndroid(self):
+    self.assertTrue(battor_tracing_agent.BattOrTracingAgent.IsSupported(
+        self.android_backend))
+    battor_wrapper.IsBattOrConnected = lambda x, android_device=None: False
+    self.assertFalse(battor_tracing_agent.BattOrTracingAgent.IsSupported(
+        self.android_backend))
+
+  def testIsSupportedNonAndroid(self):
+    self.desktop_backend.platform = 'mac'
+    battor_wrapper.IsBattOrConnected = lambda *unused: True
+    self.assertTrue(battor_tracing_agent.BattOrTracingAgent.IsSupported(
+        self.desktop_backend))
+    battor_wrapper.IsBattOrConnected = lambda *unused: False
+    self.assertFalse(battor_tracing_agent.BattOrTracingAgent.IsSupported(
+        self.desktop_backend))
+
+  def testStartAgentTracingPass(self):
+    self.assertTrue(self.android_agent.StartAgentTracing(self._config, 0))
+    self.assertTrue(self.android_agent._battor._is_shell_running)
+    self.assertTrue(self.android_agent._battor._start_shell_called)
+    self.assertTrue(self.android_agent._battor._start_tracing_called)
+    self.assertFalse(self.android_agent._battor._stop_tracing_called)
+    self.assertFalse(
+        self.android_agent._battor._record_clock_sync_marker_called)
+
+  def testStartAgentTracingConfigSetToFalse(self):
+    self._config.enable_battor_trace = False
+    self.assertFalse(self.android_agent.StartAgentTracing(self._config, 0))
+    self.assertFalse(self.android_agent._battor._is_shell_running)
+    self.assertFalse(self.android_agent._battor._start_shell_called)
+    self.assertFalse(self.android_agent._battor._start_tracing_called)
+    self.assertFalse(self.android_agent._battor._stop_tracing_called)
+    self.assertFalse(
+        self.android_agent._battor._record_clock_sync_marker_called)
+
+  def testStartAgentTracingFail(self):
+    def throw_battor_error():
+      raise battor_error.BattOrError('Forced Exception')
+    self.android_agent._battor.StartTracing = throw_battor_error
+    with self.assertRaises(battor_error.BattOrError):
+      self.android_agent.StartAgentTracing(self._config, 0)
+
+  def testStopAgentTracing(self):
+    self.android_agent.StopAgentTracing()
+    self.assertTrue(self.android_agent._battor._stop_tracing_called)
+
+  def testCollectAgentTraceData(self):
+    builder = trace_data.TraceDataBuilder()
+    self.android_agent.CollectAgentTraceData(builder)
+    self.assertTrue(self.android_agent._battor._collect_trace_data_called)
+    builder = builder.AsData()
+    self.assertTrue(builder.HasTracesFor(trace_data.BATTOR_TRACE_PART))
+    data_from_builder = builder.GetTracesFor(trace_data.BATTOR_TRACE_PART)
+    self.assertEqual([_BATTOR_RETURN], data_from_builder)
+
+  def testAndroidCharging(self):
+    self.assertTrue(self.android_agent._battery.GetCharging())
+    self.assertTrue(self.android_agent.StartAgentTracing(self._config, 0))
+    self.assertFalse(self.android_agent._battery.GetCharging())
+    self.android_agent.StopAgentTracing()
+    self.assertTrue(self.android_agent._battery.GetCharging())
+
+  def testRecordClockSyncMarker(self):
+    def callback_with_exception(a, b):
+      del a # unused
+      del b # unused
+      raise Exception
+    def callback_without_exception(a, b):
+      del a # unused
+      del b # unused
+
+    self.android_agent.RecordClockSyncMarker('123', callback_without_exception)
+    with self.assertRaises(Exception):
+      self.android_agent.RecordClockSyncMarker('abc', callback_with_exception)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/tracing_agent/chrome_tracing_agent.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/tracing_agent/chrome_tracing_agent.py
new file mode 100644
index 0000000..0572989
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/tracing_agent/chrome_tracing_agent.py
@@ -0,0 +1,331 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.internal.util import atexit_with_log
+import json
+import logging
+import os
+import shutil
+import stat
+import sys
+import tempfile
+import traceback
+
+from py_trace_event import trace_time
+from telemetry.internal.platform import tracing_agent
+from telemetry.internal.platform.tracing_agent import (
+    chrome_tracing_devtools_manager)
+
+_DESKTOP_OS_NAMES = ['linux', 'mac', 'win']
+_STARTUP_TRACING_OS_NAMES = _DESKTOP_OS_NAMES + ['android', 'chromeos']
+
+# The trace config file path should be the same as specified in
+# src/components/tracing/trace_config_file.[h|cc]
+_CHROME_TRACE_CONFIG_DIR_ANDROID = '/data/local/'
+_CHROME_TRACE_CONFIG_DIR_CROS = '/tmp/'
+_CHROME_TRACE_CONFIG_FILE_NAME = 'chrome-trace-config.json'
+
+
+def ClearStarupTracingStateIfNeeded(platform_backend):
+  # Trace config file has fixed path on Android and temporary path on desktop.
+  if platform_backend.GetOSName() == 'android':
+    trace_config_file = os.path.join(_CHROME_TRACE_CONFIG_DIR_ANDROID,
+                                     _CHROME_TRACE_CONFIG_FILE_NAME)
+    platform_backend.device.RunShellCommand(
+        ['rm', '-f', trace_config_file], check_return=True, as_root=True)
+
+
+class ChromeTracingStartedError(Exception):
+  pass
+
+
+class ChromeTracingStoppedError(Exception):
+  pass
+
+
+class ChromeClockSyncError(Exception):
+  pass
+
+
+class ChromeTracingAgent(tracing_agent.TracingAgent):
+  def __init__(self, platform_backend):
+    super(ChromeTracingAgent, self).__init__(platform_backend)
+    self._trace_config = None
+    self._trace_config_file = None
+    self._previously_responsive_devtools = []
+
+  @property
+  def trace_config(self):
+    # Trace config is also used to check if Chrome tracing is running or not.
+    return self._trace_config
+
+  @property
+  def trace_config_file(self):
+    return self._trace_config_file
+
+  @classmethod
+  def IsStartupTracingSupported(cls, platform_backend):
+    if platform_backend.GetOSName() in _STARTUP_TRACING_OS_NAMES:
+      return True
+    else:
+      return False
+
+  @classmethod
+  def IsSupported(cls, platform_backend):
+    if cls.IsStartupTracingSupported(platform_backend):
+      return True
+    else:
+      return chrome_tracing_devtools_manager.IsSupported(platform_backend)
+
+  def _StartStartupTracing(self, config):
+    if not self.IsStartupTracingSupported(self._platform_backend):
+      return False
+    self._CreateTraceConfigFile(config)
+    return True
+
+  def _StartDevToolsTracing(self, config, timeout):
+    if not chrome_tracing_devtools_manager.IsSupported(self._platform_backend):
+      return False
+    devtools_clients = (chrome_tracing_devtools_manager
+        .GetActiveDevToolsClients(self._platform_backend))
+    if not devtools_clients:
+      return False
+    for client in devtools_clients:
+      if client.is_tracing_running:
+        raise ChromeTracingStartedError(
+            'Tracing is already running on devtools at port %s on platform'
+            'backend %s.' % (client.remote_port, self._platform_backend))
+      client.StartChromeTracing(config, timeout)
+    return True
+
+  def StartAgentTracing(self, config, timeout):
+    if not config.enable_chrome_trace:
+      return False
+
+    if self._trace_config:
+      raise ChromeTracingStartedError(
+          'Tracing is already running on platform backend %s.'
+          % self._platform_backend)
+
+    if (config.enable_android_graphics_memtrack and
+        self._platform_backend.GetOSName() == 'android'):
+      self._platform_backend.SetGraphicsMemoryTrackingEnabled(True)
+
+    # Chrome tracing Agent needs to start tracing for chrome browsers that are
+    # not yet started, and for the ones that already are. For the former, we
+    # first setup the trace_config_file, which allows browsers that starts after
+    # this point to use it for enabling tracing upon browser startup. For the
+    # latter, we invoke start tracing command through devtools for browsers that
+    # are already started and tracked by chrome_tracing_devtools_manager.
+    started_startup_tracing = self._StartStartupTracing(config)
+    started_devtools_tracing = self._StartDevToolsTracing(config, timeout)
+    if started_startup_tracing or started_devtools_tracing:
+      self._trace_config = config
+      return True
+    return False
+
+  def SupportsExplicitClockSync(self):
+    return True
+
+  def _RecordClockSyncMarkerDevTools(
+      self, sync_id, record_controller_clock_sync_marker_callback,
+      devtools_clients):
+    has_clock_synced = False
+    for client in devtools_clients:
+      try:
+        timestamp = trace_time.Now()
+        client.RecordChromeClockSyncMarker(sync_id)
+        # We only need one successful clock sync.
+        has_clock_synced = True
+        break
+      except Exception:
+        logging.exception('Failed to record clock sync marker with sync_id=%r '
+                          'via DevTools client %r:' % (sync_id, client))
+    if not has_clock_synced:
+      raise ChromeClockSyncError(
+          'Failed to issue clock sync to devtools client')
+    record_controller_clock_sync_marker_callback(sync_id, timestamp)
+
+  def _RecordClockSyncMarkerAsyncEvent(
+      self, sync_id, record_controller_clock_sync_marker_callback):
+    has_clock_synced = False
+    for backend in self._IterInspectorBackends():
+      try:
+        timestamp = trace_time.Now()
+        event = 'ClockSyncEvent.%s' % sync_id
+        backend.EvaluateJavaScript(
+            "console.time({{ event }});", event=event)
+        backend.EvaluateJavaScript(
+            "console.timeEnd({{ event }});", event=event)
+        has_clock_synced = True
+        break
+      except Exception:
+        logging.exception('Failed to record clock sync marker with sync_id=%r '
+                          'via inspector backend %r:' % (sync_id, backend))
+    if not has_clock_synced:
+      raise ChromeClockSyncError(
+          'Failed to issue clock sync to devtools client')
+    record_controller_clock_sync_marker_callback(sync_id, timestamp)
+
+  def RecordClockSyncMarker(self, sync_id,
+                            record_controller_clock_sync_marker_callback):
+    devtools_clients = (chrome_tracing_devtools_manager
+        .GetActiveDevToolsClients(self._platform_backend))
+    if not devtools_clients:
+      raise ChromeClockSyncError('Cannot issue clock sync. No devtools clients')
+    version = None
+    for client in devtools_clients:
+      version = client.GetChromeBranchNumber()
+      break
+    logging.info('Chrome version: %s', version)
+    # Note, we aren't sure whether 2744 is the correct cut-off point which
+    # Chrome will support clock sync marker, however we verified that 2743 does
+    # not support clock sync (catapult/issues/2804) hence we use it here.
+    # On the next update of Chrome ref build, if testTBM2ForSmoke still fails,
+    # the cut-off branch number will need to be bumped up again.
+    if version and int(version) > 2743:
+      self._RecordClockSyncMarkerDevTools(
+          sync_id, record_controller_clock_sync_marker_callback,
+          devtools_clients)
+    else:  # TODO(rnephew): Remove once chrome stable is past branch 2743.
+      self._RecordClockSyncMarkerAsyncEvent(
+          sync_id, record_controller_clock_sync_marker_callback)
+
+  def StopAgentTracing(self):
+    if not self._trace_config:
+      raise ChromeTracingStoppedError(
+          'Tracing is not running on platform backend %s.'
+          % self._platform_backend)
+
+    if self.IsStartupTracingSupported(self._platform_backend):
+      self._RemoveTraceConfigFile()
+
+    # We get all DevTools clients including the stale ones, so that we get an
+    # exception if there is a stale client. This is because we will potentially
+    # lose data if there is a stale client.
+    devtools_clients = (chrome_tracing_devtools_manager
+        .GetDevToolsClients(self._platform_backend))
+    raised_exception_messages = []
+    assert len(self._previously_responsive_devtools) == 0
+    for client in devtools_clients:
+      try:
+        client.StopChromeTracing()
+        self._previously_responsive_devtools.append(client)
+
+      except Exception:
+        raised_exception_messages.append(
+          'Error when trying to stop Chrome tracing on devtools at port %s:\n%s'
+          % (client.remote_port,
+             ''.join(traceback.format_exception(*sys.exc_info()))))
+
+    if (self._trace_config.enable_android_graphics_memtrack and
+        self._platform_backend.GetOSName() == 'android'):
+      self._platform_backend.SetGraphicsMemoryTrackingEnabled(False)
+
+    self._trace_config = None
+    if raised_exception_messages:
+      raise ChromeTracingStoppedError(
+          'Exceptions raised when trying to stop Chrome devtool tracing:\n' +
+          '\n'.join(raised_exception_messages))
+
+  def CollectAgentTraceData(self, trace_data_builder, timeout=None):
+    raised_exception_messages = []
+    for client in self._previously_responsive_devtools:
+      try:
+        client.CollectChromeTracingData(trace_data_builder)
+      except Exception:
+        raised_exception_messages.append(
+          'Error when collecting Chrome tracing on devtools at port %s:\n%s'
+          % (client.remote_port,
+             ''.join(traceback.format_exception(*sys.exc_info()))))
+    self._previously_responsive_devtools = []
+
+    if raised_exception_messages:
+      raise ChromeTracingStoppedError(
+          'Exceptions raised when trying to collect Chrome devtool tracing:\n' +
+          '\n'.join(raised_exception_messages))
+
+  def _CreateTraceConfigFileString(self, config):
+    # See src/components/tracing/trace_config_file.h for the format
+    result = {
+      'trace_config':
+        config.chrome_trace_config.GetChromeTraceConfigForStartupTracing()
+    }
+    return json.dumps(result, sort_keys=True)
+
+  def _CreateTraceConfigFile(self, config):
+    assert not self._trace_config_file
+    if self._platform_backend.GetOSName() == 'android':
+      self._trace_config_file = os.path.join(_CHROME_TRACE_CONFIG_DIR_ANDROID,
+                                             _CHROME_TRACE_CONFIG_FILE_NAME)
+      self._platform_backend.device.WriteFile(self._trace_config_file,
+          self._CreateTraceConfigFileString(config), as_root=True)
+      # The config file has fixed path on Android. We need to ensure it is
+      # always cleaned up.
+      atexit_with_log.Register(self._RemoveTraceConfigFile)
+    elif self._platform_backend.GetOSName() == 'chromeos':
+      self._trace_config_file = os.path.join(_CHROME_TRACE_CONFIG_DIR_CROS,
+                                             _CHROME_TRACE_CONFIG_FILE_NAME)
+      cri = self._platform_backend.cri
+      cri.PushContents(self._CreateTraceConfigFileString(config),
+                       self._trace_config_file)
+      cri.Chown(self._trace_config_file)
+      # The config file has fixed path on CrOS. We need to ensure it is
+      # always cleaned up.
+      atexit_with_log.Register(self._RemoveTraceConfigFile)
+    elif self._platform_backend.GetOSName() in _DESKTOP_OS_NAMES:
+      self._trace_config_file = os.path.join(tempfile.mkdtemp(),
+                                             _CHROME_TRACE_CONFIG_FILE_NAME)
+      with open(self._trace_config_file, 'w') as f:
+        trace_config_string = self._CreateTraceConfigFileString(config)
+        logging.info('Trace config file string: %s', trace_config_string)
+        f.write(trace_config_string)
+      os.chmod(self._trace_config_file,
+               os.stat(self._trace_config_file).st_mode | stat.S_IROTH)
+    else:
+      raise NotImplementedError
+
+  def _RemoveTraceConfigFile(self):
+    if not self._trace_config_file:
+      return
+    if self._platform_backend.GetOSName() == 'android':
+      self._platform_backend.device.RunShellCommand(
+          ['rm', '-f', self._trace_config_file], check_return=True,
+          as_root=True)
+    elif self._platform_backend.GetOSName() == 'chromeos':
+      self._platform_backend.cri.RmRF(self._trace_config_file)
+    elif self._platform_backend.GetOSName() in _DESKTOP_OS_NAMES:
+      if os.path.exists(self._trace_config_file):
+        os.remove(self._trace_config_file)
+      shutil.rmtree(os.path.dirname(self._trace_config_file))
+    else:
+      raise NotImplementedError
+    self._trace_config_file = None
+
+  def SupportsFlushingAgentTracing(self):
+    return True
+
+  def FlushAgentTracing(self, config, timeout, trace_data_builder):
+    if not self._trace_config:
+      raise ChromeTracingStoppedError(
+          'Tracing is not running on platform backend %s.'
+          % self._platform_backend)
+
+    for backend in self._IterInspectorBackends():
+      backend.EvaluateJavaScript("console.time('flush-tracing');")
+
+    self.StopAgentTracing()
+    self.CollectAgentTraceData(trace_data_builder)
+    self.StartAgentTracing(config, timeout)
+
+    for backend in self._IterInspectorBackends():
+      backend.EvaluateJavaScript("console.timeEnd('flush-tracing');")
+
+  def _IterInspectorBackends(self):
+    for client in chrome_tracing_devtools_manager.GetDevToolsClients(
+        self._platform_backend):
+      context_map = client.GetUpdatedInspectableContexts()
+      for context in context_map.contexts:
+        if context['type'] in ['iframe', 'page', 'webview']:
+          yield context_map.GetInspectorBackend(context['id'])
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/tracing_agent/chrome_tracing_agent_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/tracing_agent/chrome_tracing_agent_unittest.py
new file mode 100644
index 0000000..b4d3c51
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/tracing_agent/chrome_tracing_agent_unittest.py
@@ -0,0 +1,387 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import os
+import platform
+import stat
+import unittest
+
+from telemetry import decorators
+from telemetry.internal.platform.tracing_agent import chrome_tracing_agent
+from telemetry.internal.platform.tracing_agent import (
+    chrome_tracing_devtools_manager)
+from telemetry.timeline import tracing_config
+from telemetry.core import cros_interface
+from telemetry.testing import options_for_unittests
+
+
+from devil.android import device_utils
+
+
+class FakeTracingControllerBackend(object):
+  def __init__(self):
+    self.is_tracing_running = False
+
+
+class FakePlatformBackend(object):
+  def __init__(self):
+    self.tracing_controller_backend = FakeTracingControllerBackend()
+
+  def GetOSName(self):
+    return ''
+
+class FakeAndroidPlatformBackend(FakePlatformBackend):
+  def __init__(self):
+    super(FakeAndroidPlatformBackend, self).__init__()
+    devices = device_utils.DeviceUtils.HealthyDevices(None)
+    self.device = devices[0]
+
+  def GetOSName(self):
+    return 'android'
+
+class FakeCrOSPlatformBackend(FakePlatformBackend):
+  def __init__(self):
+    super(FakeCrOSPlatformBackend, self).__init__()
+    remote = options_for_unittests.GetCopy().cros_remote
+    remote_ssh_port = options_for_unittests.GetCopy().cros_remote_ssh_port
+    self.cri = cros_interface.CrOSInterface(
+        remote, remote_ssh_port,
+        options_for_unittests.GetCopy().cros_ssh_identity)
+
+  def GetOSName(self):
+    return 'chromeos'
+
+class FakeDesktopPlatformBackend(FakePlatformBackend):
+  def GetOSName(self):
+    system = platform.system()
+    if system == 'Linux':
+      return 'linux'
+    if system == 'Darwin':
+      return 'mac'
+    if system == 'Windows':
+      return 'win'
+
+
+class FakeContextMap(object):
+  def __init__(self, contexts):
+    self.contexts = contexts
+
+
+class FakeDevtoolsClient(object):
+  def __init__(self, remote_port):
+    self.is_alive = True
+    self.is_tracing_running = False
+    self.remote_port = remote_port
+    self.will_raise_exception_in_stop_tracing = False
+    self.collected = False
+
+  def IsAlive(self):
+    return self.is_alive
+
+  def StartChromeTracing(self, trace_options, timeout=10):
+    del trace_options, timeout  # unused
+    self.is_tracing_running = True
+
+  def StopChromeTracing(self):
+    self.is_tracing_running = False
+    if self.will_raise_exception_in_stop_tracing:
+      raise Exception
+
+  def CollectChromeTracingData(self, trace_data_builder, timeout=30):
+    del trace_data_builder  # unused
+    del timeout # unused
+    self.collected = True
+
+  def IsChromeTracingSupported(self):
+    return True
+
+  def GetUpdatedInspectableContexts(self):
+    return FakeContextMap([])
+
+
+class ChromeTracingAgentTest(unittest.TestCase):
+  def setUp(self):
+    self.platform1 = FakePlatformBackend()
+    self.platform2 = FakePlatformBackend()
+    self.platform3 = FakePlatformBackend()
+
+  def StartTracing(self, platform_backend, enable_chrome_trace=True):
+    assert chrome_tracing_agent.ChromeTracingAgent.IsSupported(platform_backend)
+    agent = chrome_tracing_agent.ChromeTracingAgent(platform_backend)
+    config = tracing_config.TracingConfig()
+    config.enable_chrome_trace = enable_chrome_trace
+    config.chrome_trace_config.category_filter.AddIncludedCategory('foo')
+    agent._platform_backend.tracing_controller_backend.is_tracing_running = True
+    agent._test_config = config
+    agent.StartAgentTracing(config, 10)
+    return agent
+
+  def FlushTracing(self, agent):
+    agent.FlushAgentTracing(agent._test_config, 10, None)
+
+  def StopTracing(self, agent):
+    agent._platform_backend.tracing_controller_backend.is_tracing_running = (
+        False)
+    agent.StopAgentTracing()
+    agent.CollectAgentTraceData(None)
+
+  def testRegisterDevtoolsClient(self):
+    chrome_tracing_devtools_manager.RegisterDevToolsClient(
+        FakeDevtoolsClient(1), self.platform1)
+    chrome_tracing_devtools_manager.RegisterDevToolsClient(
+        FakeDevtoolsClient(2), self.platform1)
+    chrome_tracing_devtools_manager.RegisterDevToolsClient(
+        FakeDevtoolsClient(3), self.platform1)
+
+    tracing_agent_of_platform1 = self.StartTracing(self.platform1)
+
+    chrome_tracing_devtools_manager.RegisterDevToolsClient(
+        FakeDevtoolsClient(4), self.platform1)
+    chrome_tracing_devtools_manager.RegisterDevToolsClient(
+        FakeDevtoolsClient(5), self.platform2)
+
+    self.StopTracing(tracing_agent_of_platform1)
+    chrome_tracing_devtools_manager.RegisterDevToolsClient(
+        FakeDevtoolsClient(6), self.platform1)
+
+  def testIsSupportWithoutStartupTracingSupport(self):
+    self.assertFalse(
+        chrome_tracing_agent.ChromeTracingAgent.IsSupported(self.platform1))
+    self.assertFalse(
+        chrome_tracing_agent.ChromeTracingAgent.IsSupported(self.platform2))
+    self.assertFalse(
+        chrome_tracing_agent.ChromeTracingAgent.IsSupported(self.platform3))
+
+    devtool1 = FakeDevtoolsClient(1)
+    devtool2 = FakeDevtoolsClient(2)
+    chrome_tracing_devtools_manager.RegisterDevToolsClient(
+        devtool1, self.platform1)
+    chrome_tracing_devtools_manager.RegisterDevToolsClient(
+        devtool2, self.platform2)
+    devtool2.is_alive = False
+
+    # Chrome tracing is only supported on platform 1 since only platform 1 has
+    # an alive devtool.
+    self.assertTrue(
+        chrome_tracing_agent.ChromeTracingAgent.IsSupported(self.platform1))
+    self.assertFalse(
+        chrome_tracing_agent.ChromeTracingAgent.IsSupported(self.platform2))
+    self.assertFalse(
+        chrome_tracing_agent.ChromeTracingAgent.IsSupported(self.platform3))
+
+  @decorators.Enabled('linux', 'mac', 'win')
+  def testIsSupportOnDesktopPlatform(self):
+    # Chrome tracing is always supported on desktop platforms because of startup
+    # tracing.
+    desktop_platform = FakeDesktopPlatformBackend()
+    self.assertTrue(
+        chrome_tracing_agent.ChromeTracingAgent.IsSupported(desktop_platform))
+
+    devtool = FakeDevtoolsClient(1)
+    chrome_tracing_devtools_manager.RegisterDevToolsClient(
+        devtool, desktop_platform)
+    self.assertTrue(
+        chrome_tracing_agent.ChromeTracingAgent.IsSupported(desktop_platform))
+
+  def testStartAndStopTracing(self):
+    devtool1 = FakeDevtoolsClient(1)
+    devtool2 = FakeDevtoolsClient(2)
+    devtool3 = FakeDevtoolsClient(3)
+    devtool4 = FakeDevtoolsClient(2)
+    # Register devtools 1, 2, 3 on platform1 and devtool 4 on platform 2
+    chrome_tracing_devtools_manager.RegisterDevToolsClient(
+        devtool1, self.platform1)
+    chrome_tracing_devtools_manager.RegisterDevToolsClient(
+        devtool2, self.platform1)
+    chrome_tracing_devtools_manager.RegisterDevToolsClient(
+        devtool3, self.platform1)
+    chrome_tracing_devtools_manager.RegisterDevToolsClient(
+        devtool4, self.platform2)
+    devtool2.is_alive = False
+
+    tracing_agent1 = self.StartTracing(self.platform1)
+    with self.assertRaises(chrome_tracing_agent.ChromeTracingStartedError):
+      self.StartTracing(self.platform1)
+
+    self.assertTrue(devtool1.is_tracing_running)
+    self.assertFalse(devtool2.is_tracing_running)
+    self.assertTrue(devtool3.is_tracing_running)
+    # Devtool 4 shouldn't have tracing started although it has the same remote
+    # port as devtool 2
+    self.assertFalse(devtool4.is_tracing_running)
+
+
+    self.assertFalse(devtool1.collected)
+    self.StopTracing(tracing_agent1)
+    self.assertTrue(devtool1.collected)
+    self.assertFalse(devtool1.is_tracing_running)
+    self.assertFalse(devtool2.is_tracing_running)
+    self.assertFalse(devtool3.is_tracing_running)
+    self.assertFalse(devtool4.is_tracing_running)
+
+    # Test that it should be ok to start & stop tracing on platform1 again.
+    tracing_agent1 = self.StartTracing(self.platform1)
+    self.StopTracing(tracing_agent1)
+
+    tracing_agent2 = self.StartTracing(self.platform2)
+    self.assertTrue(devtool4.is_tracing_running)
+    self.assertFalse(devtool4.collected)
+    self.StopTracing(tracing_agent2)
+    self.assertFalse(devtool4.is_tracing_running)
+    self.assertTrue(devtool4.collected)
+
+  def testFlushTracing(self):
+    devtool1 = FakeDevtoolsClient(1)
+    devtool2 = FakeDevtoolsClient(2)
+    devtool3 = FakeDevtoolsClient(3)
+    devtool4 = FakeDevtoolsClient(2)
+
+    # Register devtools 1, 2, 3 on platform1 and devtool 4 on platform 2.
+    chrome_tracing_devtools_manager.RegisterDevToolsClient(
+        devtool1, self.platform1)
+    chrome_tracing_devtools_manager.RegisterDevToolsClient(
+        devtool2, self.platform1)
+    chrome_tracing_devtools_manager.RegisterDevToolsClient(
+        devtool3, self.platform1)
+    chrome_tracing_devtools_manager.RegisterDevToolsClient(
+        devtool4, self.platform2)
+    devtool2.is_alive = False
+
+    tracing_agent1 = self.StartTracing(self.platform1)
+
+    self.assertTrue(devtool1.is_tracing_running)
+    self.assertFalse(devtool2.is_tracing_running)
+    self.assertTrue(devtool3.is_tracing_running)
+    # Devtool 4 shouldn't have tracing started although it has the same remote
+    # port as devtool 2.
+    self.assertFalse(devtool4.is_tracing_running)
+
+    for _ in xrange(5):
+      self.FlushTracing(tracing_agent1)
+      self.assertTrue(devtool1.is_tracing_running)
+      self.assertFalse(devtool2.is_tracing_running)
+      self.assertTrue(devtool3.is_tracing_running)
+      self.assertFalse(devtool4.is_tracing_running)
+
+    self.StopTracing(tracing_agent1)
+    self.assertFalse(devtool1.is_tracing_running)
+    self.assertFalse(devtool2.is_tracing_running)
+    self.assertFalse(devtool3.is_tracing_running)
+    self.assertFalse(devtool4.is_tracing_running)
+
+    # Test that it is ok to start, flush & stop tracing on platform1 again.
+    tracing_agent1 = self.StartTracing(self.platform1)
+    self.FlushTracing(tracing_agent1)
+    self.StopTracing(tracing_agent1)
+
+    tracing_agent2 = self.StartTracing(self.platform2)
+    self.assertTrue(devtool4.is_tracing_running)
+    self.FlushTracing(tracing_agent2)
+    self.assertTrue(devtool4.is_tracing_running)
+    self.StopTracing(tracing_agent2)
+    self.assertFalse(devtool4.is_tracing_running)
+
+  def testExceptionRaisedInStopTracing(self):
+    devtool1 = FakeDevtoolsClient(1)
+    devtool2 = FakeDevtoolsClient(2)
+    # Register devtools 1, 2 on platform 1
+    chrome_tracing_devtools_manager.RegisterDevToolsClient(
+        devtool1, self.platform1)
+    chrome_tracing_devtools_manager.RegisterDevToolsClient(
+        devtool2, self.platform1)
+    tracing_agent1 = self.StartTracing(self.platform1)
+
+    self.assertTrue(devtool1.is_tracing_running)
+    self.assertTrue(devtool2.is_tracing_running)
+
+    devtool1.will_raise_exception_in_stop_tracing = True
+    with self.assertRaises(chrome_tracing_agent.ChromeTracingStoppedError):
+      self.StopTracing(tracing_agent1)
+    # Tracing is stopped on both devtools clients even if there is exception.
+    self.assertIsNone(tracing_agent1.trace_config)
+    self.assertFalse(devtool1.is_tracing_running)
+    self.assertFalse(devtool2.is_tracing_running)
+
+    devtool1.is_alive = False
+    devtool2.is_alive = False
+    # Register devtools 3 on platform 1 should not raise any exception.
+    devtool3 = FakeDevtoolsClient(3)
+    chrome_tracing_devtools_manager.RegisterDevToolsClient(
+        devtool3, self.platform1)
+
+    # Start & Stop tracing on platform 1 should work just fine.
+    tracing_agent2 = self.StartTracing(self.platform1)
+    self.StopTracing(tracing_agent2)
+
+  @decorators.Enabled('android')
+  def testCreateAndRemoveTraceConfigFileOnAndroid(self):
+    platform_backend = FakeAndroidPlatformBackend()
+    agent = chrome_tracing_agent.ChromeTracingAgent(platform_backend)
+    self.assertIsNone(agent.trace_config_file)
+
+    config = tracing_config.TracingConfig()
+    agent._CreateTraceConfigFile(config)
+    self.assertIsNotNone(agent.trace_config_file)
+    self.assertTrue(platform_backend.device.PathExists(agent.trace_config_file))
+    config_file_str = platform_backend.device.ReadFile(agent.trace_config_file,
+                                                       as_root=True)
+    self.assertEqual(agent._CreateTraceConfigFileString(config),
+                     config_file_str.strip())
+
+    config_file_path = agent.trace_config_file
+    agent._RemoveTraceConfigFile()
+    self.assertFalse(platform_backend.device.PathExists(config_file_path))
+    self.assertIsNone(agent.trace_config_file)
+    # robust to multiple file removal
+    agent._RemoveTraceConfigFile()
+    self.assertFalse(platform_backend.device.PathExists(config_file_path))
+    self.assertIsNone(agent.trace_config_file)
+
+  @decorators.Enabled('chromeos')
+  def testCreateAndRemoveTraceConfigFileOnCrOS(self):
+    platform_backend = FakeCrOSPlatformBackend()
+    cri = platform_backend.cri
+    agent = chrome_tracing_agent.ChromeTracingAgent(platform_backend)
+    self.assertIsNone(agent.trace_config_file)
+
+    config = tracing_config.TracingConfig()
+    agent._CreateTraceConfigFile(config)
+    self.assertIsNotNone(agent.trace_config_file)
+    self.assertTrue(cri.FileExistsOnDevice(agent.trace_config_file))
+    config_file_str = cri.GetFileContents(agent.trace_config_file)
+    self.assertEqual(agent._CreateTraceConfigFileString(config),
+                     config_file_str.strip())
+
+    config_file_path = agent.trace_config_file
+    agent._RemoveTraceConfigFile()
+    self.assertFalse(cri.FileExistsOnDevice(config_file_path))
+    self.assertIsNone(agent.trace_config_file)
+    # robust to multiple file removal
+    agent._RemoveTraceConfigFile()
+    self.assertFalse(cri.FileExistsOnDevice(config_file_path))
+    self.assertIsNone(agent.trace_config_file)
+
+  @decorators.Enabled('linux', 'mac', 'win')
+  def testCreateAndRemoveTraceConfigFileOnDesktop(self):
+    platform_backend = FakeDesktopPlatformBackend()
+    agent = chrome_tracing_agent.ChromeTracingAgent(platform_backend)
+    self.assertIsNone(agent.trace_config_file)
+
+    config = tracing_config.TracingConfig()
+    agent._CreateTraceConfigFile(config)
+    self.assertIsNotNone(agent.trace_config_file)
+    self.assertTrue(os.path.exists(agent.trace_config_file))
+    self.assertTrue(os.stat(agent.trace_config_file).st_mode & stat.S_IROTH)
+    with open(agent.trace_config_file, 'r') as f:
+      config_file_str = f.read()
+      self.assertEqual(agent._CreateTraceConfigFileString(config),
+                       config_file_str.strip())
+
+    config_file_path = agent.trace_config_file
+    agent._RemoveTraceConfigFile()
+    self.assertFalse(os.path.exists(config_file_path))
+    self.assertIsNone(agent.trace_config_file)
+    # robust to multiple file removal
+    agent._RemoveTraceConfigFile()
+    self.assertFalse(os.path.exists(config_file_path))
+    self.assertIsNone(agent.trace_config_file)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/tracing_agent/chrome_tracing_devtools_manager.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/tracing_agent/chrome_tracing_devtools_manager.py
new file mode 100644
index 0000000..ea2bde2
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/tracing_agent/chrome_tracing_devtools_manager.py
@@ -0,0 +1,58 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# A singleton map from platform backends to maps of uniquely-identifying
+# remote port (which may be the same as local port) to DevToolsClientBackend.
+# There is no guarantee that the devtools agent is still alive.
+_platform_backends_to_devtools_clients_maps = {}
+
+
+def _RemoveStaleDevToolsClient(platform_backend):
+  """Removes DevTools clients that are no longer connectable."""
+  devtools_clients_map = _platform_backends_to_devtools_clients_maps.get(
+      platform_backend, {})
+  devtools_clients_map = {
+      port: client
+      for port, client in devtools_clients_map.iteritems()
+      if client.IsAlive()
+      }
+  _platform_backends_to_devtools_clients_maps[platform_backend] = (
+      devtools_clients_map)
+
+
+def RegisterDevToolsClient(devtools_client_backend, platform_backend):
+  """Register DevTools client
+
+  This should only be called from DevToolsClientBackend when it is initialized.
+  """
+  remote_port = str(devtools_client_backend.remote_port)
+  if platform_backend not in _platform_backends_to_devtools_clients_maps:
+    _platform_backends_to_devtools_clients_maps[platform_backend] = {}
+  devtools_clients_map = (
+    _platform_backends_to_devtools_clients_maps[platform_backend])
+  devtools_clients_map[remote_port] = devtools_client_backend
+
+
+def IsSupported(platform_backend):
+  _RemoveStaleDevToolsClient(platform_backend)
+  devtools_clients_map = _platform_backends_to_devtools_clients_maps.get(
+      platform_backend, {})
+  for _, devtools_client in devtools_clients_map.iteritems():
+    if devtools_client.IsChromeTracingSupported():
+      return True
+  return False
+
+
+def GetDevToolsClients(platform_backend):
+  """Get DevTools clients including the ones that are no longer connectable."""
+  devtools_clients_map = _platform_backends_to_devtools_clients_maps.get(
+      platform_backend, {})
+  if not devtools_clients_map:
+    return []
+  return devtools_clients_map.values()
+
+def GetActiveDevToolsClients(platform_backend):
+  """Get DevTools clients that are still connectable."""
+  _RemoveStaleDevToolsClient(platform_backend)
+  return GetDevToolsClients(platform_backend)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/tracing_agent/cpu_tracing_agent.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/tracing_agent/cpu_tracing_agent.py
new file mode 100644
index 0000000..92427d5
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/tracing_agent/cpu_tracing_agent.py
@@ -0,0 +1,326 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import json
+import os
+import re
+import subprocess
+from threading import Timer
+
+from py_trace_event import trace_time
+from telemetry.internal.platform import tracing_agent
+from tracing.trace_data import trace_data
+
+
+def _ParsePsProcessString(line):
+  """Parses a process line from the output of `ps`.
+
+  Example of `ps` command output:
+  '3.4 8.0 31887 31447 com.app.Webkit'
+  """
+  token_list = line.strip().split()
+  if len(token_list) < 5:
+    raise ValueError('Line has too few tokens: %s.' % token_list)
+
+  return {
+    'pCpu': float(token_list[0]),
+    'pMem': float(token_list[1]),
+    'pid': int(token_list[2]),
+    'ppid': int(token_list[3]),
+    'name': ' '.join(token_list[4:])
+  }
+
+
+class ProcessCollector(object):
+  def _GetProcessesAsStrings(self):
+    """Returns a list of strings, each of which contains info about a
+    process.
+    """
+    raise NotImplementedError
+
+  # pylint: disable=unused-argument
+  def _ParseProcessString(self, proc_string):
+    """Parses an individual process string returned by _GetProcessesAsStrings().
+
+    Returns:
+      A dictionary containing keys of 'pid' (an integer process ID), 'ppid' (an
+      integer parent process ID), 'name' (a string for the process name), 'pCpu'
+      (a float for the percent CPU load incurred by the process), and 'pMem' (a
+      float for the percent memory load caused by the process).
+    """
+    raise NotImplementedError
+
+  def Init(self):
+    """Performs any required initialization before starting tracing."""
+    pass
+
+  def GetProcesses(self):
+    """Fetches the top processes returned by top command.
+
+    Returns:
+      A list of dictionaries, each containing 'pid' (an integer process ID),
+      'ppid' (an integer parent process ID), 'name (a string for the process
+      name), pCpu' (a float for the percent CPU load incurred by the process),
+      and 'pMem' (a float for the percent memory load caused by the process).
+    """
+    proc_strings = self._GetProcessesAsStrings()
+    return [
+        self._ParseProcessString(proc_string) for proc_string in proc_strings
+    ]
+
+
+class WindowsProcessCollector(ProcessCollector):
+  """Class for collecting information about processes on Windows.
+
+  Example of Windows command output:
+  '3644      1724   chrome#1                 8           84497'
+  '3644      832    chrome#2                 4           34872'
+  """
+  _GET_PERF_DATA_SHELL_COMMAND = [
+    'wmic',
+    'path', # Retrieve a WMI object from the following path.
+    'Win32_PerfFormattedData_PerfProc_Process', # Contains process perf data.
+    'get',
+    'CreatingProcessID,IDProcess,Name,PercentProcessorTime,WorkingSet'
+  ]
+
+  _GET_COMMANDS_SHELL_COMMAND = [
+    'wmic',
+    'Process',
+    'get',
+    'CommandLine,ProcessID',
+    # Formatting the result as a CSV means that if no CommandLine is available,
+    # we can at least tell by the lack of data between commas.
+    '/format:csv'
+  ]
+
+  _GET_PHYSICAL_MEMORY_BYTES_SHELL_COMMAND = [
+    'wmic',
+    'ComputerSystem',
+    'get',
+    'TotalPhysicalMemory'
+  ]
+
+  def __init__(self):
+    self._physicalMemoryBytes = None
+
+  def Init(self):
+    if not self._physicalMemoryBytes:
+      self._physicalMemoryBytes = self._GetPhysicalMemoryBytes()
+
+    # The command to get the per-process perf data takes significantly longer
+    # the first time that it's run (~10s, compared to ~60ms for subsequent
+    # runs). In order to avoid having this affect tracing, we run it once ahead
+    # of time.
+    self._GetProcessesAsStrings()
+
+  def GetProcesses(self):
+    processes = super(WindowsProcessCollector, self).GetProcesses()
+
+    # On Windows, the absolute minimal name of the process is given
+    # (e.g. "python" for Telemetry). In order to make this more useful, we check
+    # if a more descriptive command is available for each PID and use that
+    # command if it is.
+    pid_to_command_dict = self._GetPidToCommandDict()
+    for process in processes:
+      if process['pid'] in pid_to_command_dict:
+        process['name'] = pid_to_command_dict[process['pid']]
+
+    return processes
+
+  def _GetPhysicalMemoryBytes(self):
+    """Returns the number of bytes of physical memory on the computer."""
+    raw_output = subprocess.check_output(
+        self._GET_PHYSICAL_MEMORY_BYTES_SHELL_COMMAND)
+    # The bytes of physical memory is on the second row (after the header row).
+    return int(raw_output.strip().split('\n')[1])
+
+  def _GetProcessesAsStrings(self):
+    # Skip the header and total rows and strip the trailing newline.
+    return subprocess.check_output(
+        self._GET_PERF_DATA_SHELL_COMMAND).strip().split('\n')[2:]
+
+  def _ParseProcessString(self, proc_string):
+    assert self._physicalMemoryBytes, 'Must call Init() before using collector'
+
+    token_list = proc_string.strip().split()
+    if len(token_list) < 5:
+      raise ValueError('Line has too few tokens: %s.' % token_list)
+
+    # Process names are given in the form:
+    #
+    #   windowsUpdate
+    #   Windows Explorer
+    #   chrome#1
+    #   chrome#2
+    #
+    # In order to match other platforms, where multiple processes can have the
+    # same name and can be easily grouped based on that name, we strip any
+    # pound sign and number.
+    name = ' '.join(token_list[2:-2])
+    name = re.sub(r'#[0-9]+$', '', name)
+    # The working set size (roughly equivalent to the resident set size on Unix)
+    # is given in bytes. In order to convert this to percent of physical memory
+    # occupied by the process, we divide by the amount of total physical memory
+    # on the machine.
+    percent_memory = float(token_list[-1]) / self._physicalMemoryBytes * 100
+
+    return {
+      'ppid': int(token_list[0]),
+      'pid': int(token_list[1]),
+      'name': name,
+      'pCpu': float(token_list[-2]),
+      'pMem': percent_memory
+    }
+
+  def _GetPidToCommandDict(self):
+    """Returns a dictionary from the PID of a process to the full command used
+    to launch that process. If no full command is available for a given process,
+    that process is omitted from the returned dictionary.
+    """
+    # Skip the header row and strip the trailing newline.
+    process_strings = subprocess.check_output(
+        self._GET_COMMANDS_SHELL_COMMAND).strip().split('\n')[1:]
+    command_by_pid = {}
+    for process_string in process_strings:
+      process_string = process_string.strip()
+      command = self._ParseCommandString(process_string)
+
+      # Only return additional information about the command if it's available.
+      if command['command']:
+        command_by_pid[command['pid']] = command['command']
+
+    return command_by_pid
+
+  def _ParseCommandString(self, command_string):
+    groups = re.match(r'^([^,]+),(.*),([0-9]+)$', command_string).groups()
+    return {
+      # Ignore groups[0]: it's the hostname.
+      'pid': int(groups[2]),
+      'command': groups[1]
+    }
+
+
+class LinuxProcessCollector(ProcessCollector):
+  """Class for collecting information about processes on Linux.
+
+  Example of Linux command output:
+  '3.4 8.0 31887 31447 com.app.Webkit'
+  """
+  _SHELL_COMMAND = [
+    'ps',
+    '-a', # Include processes that aren't session leaders.
+    '-x', # List all processes, even those not owned by the user.
+    '-o', # Show the output in the specified format.
+    'pcpu,pmem,pid,ppid,cmd'
+  ]
+
+  def _GetProcessesAsStrings(self):
+    # Skip the header row and strip the trailing newline.
+    return subprocess.check_output(self._SHELL_COMMAND).strip().split('\n')[1:]
+
+  def _ParseProcessString(self, proc_string):
+    return _ParsePsProcessString(proc_string)
+
+
+class MacProcessCollector(ProcessCollector):
+  """Class for collecting information about processes on Mac.
+
+  Example of Mac command output:
+  '3.4 8.0 31887 31447 com.app.Webkit'
+  """
+
+  _SHELL_COMMAND = [
+    'ps',
+    '-a', # Include all users' processes.
+    '-ww', # Don't limit the length of each line.
+    '-x', # Include processes that aren't associated with a terminal.
+    '-o', # Show the output in the specified format.
+    '%cpu %mem pid ppid command' # Put the command last to avoid truncation.
+  ]
+
+  def _GetProcessesAsStrings(self):
+    # Skip the header row and strip the trailing newline.
+    return subprocess.check_output(self._SHELL_COMMAND).strip().split('\n')[1:]
+
+  def _ParseProcessString(self, proc_string):
+    return _ParsePsProcessString(proc_string)
+
+
+class CpuTracingAgent(tracing_agent.TracingAgent):
+  _SNAPSHOT_INTERVAL_BY_OS = {
+    # Sampling via wmic on Windows is about twice as expensive as sampling via
+    # ps on Linux and Mac, so we halve the sampling frequency.
+    'win': 2.0,
+    'mac': 1.0,
+    'linux': 1.0
+  }
+
+  def __init__(self, platform_backend):
+    super(CpuTracingAgent, self).__init__(platform_backend)
+    self._snapshot_ongoing = False
+    self._snapshots = []
+    self._os_name = platform_backend.GetOSName()
+    if  self._os_name == 'win':
+      self._collector = WindowsProcessCollector()
+    elif self._os_name == 'mac':
+      self._collector = MacProcessCollector()
+    else:
+      self._collector = LinuxProcessCollector()
+
+  @classmethod
+  def IsSupported(cls, platform_backend):
+    os_name = platform_backend.GetOSName()
+    return (os_name in ['mac', 'linux', 'win'])
+
+  def StartAgentTracing(self, config, timeout):
+    assert not self._snapshot_ongoing, (
+           'Agent is already taking snapshots when tracing is started.')
+    if not config.enable_cpu_trace:
+      return False
+
+    self._collector.Init()
+    self._snapshot_ongoing = True
+    self._KeepTakingSnapshots()
+    return True
+
+  def _KeepTakingSnapshots(self):
+    """Take CPU snapshots every SNAPSHOT_FREQUENCY seconds."""
+    if not self._snapshot_ongoing:
+      return
+    # Assume CpuTracingAgent shares the same clock domain as telemetry
+    self._snapshots.append(
+        (self._collector.GetProcesses(), trace_time.Now()))
+    interval = self._SNAPSHOT_INTERVAL_BY_OS[self._os_name]
+    Timer(interval, self._KeepTakingSnapshots).start()
+
+  def StopAgentTracing(self):
+    assert self._snapshot_ongoing, (
+           'Agent is not taking snapshots when tracing is stopped.')
+    self._snapshot_ongoing = False
+
+  def CollectAgentTraceData(self, trace_data_builder, timeout=None):
+    assert not self._snapshot_ongoing, (
+           'Agent is still taking snapshots when data is collected.')
+    self._snapshot_ongoing = False
+    data = json.dumps(self._FormatSnapshotsData())
+    trace_data_builder.AddTraceFor(trace_data.CPU_TRACE_DATA, data)
+
+  def _FormatSnapshotsData(self):
+    """Format raw data into Object Event specified in Trace Format document."""
+    pid = os.getpid()
+    return [{
+      'name': 'CPUSnapshots',
+      'ph': 'O',
+      'id': '0x1000',
+      'local': True,
+      'ts': timestamp,
+      'pid': pid,
+      'tid':None,
+      'args': {
+        'snapshot':{
+          'processes': snapshot
+        }
+      }
+    } for snapshot, timestamp in self._snapshots]
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/tracing_agent/cpu_tracing_agent_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/tracing_agent/cpu_tracing_agent_unittest.py
new file mode 100644
index 0000000..f87f009
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/tracing_agent/cpu_tracing_agent_unittest.py
@@ -0,0 +1,150 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import json
+import sys
+import time
+import unittest
+
+from telemetry import decorators
+from telemetry.internal.platform.tracing_agent import cpu_tracing_agent
+from telemetry.internal.platform import tracing_agent
+from telemetry.internal.platform import linux_platform_backend
+from telemetry.internal.platform import mac_platform_backend
+from telemetry.internal.platform import win_platform_backend
+from telemetry.timeline import tracing_config
+from tracing.trace_data import trace_data
+
+
+SNAPSHOT_KEYS = ['pid', 'ppid', 'name', 'pCpu', 'pMem']
+TRACE_EVENT_KEYS = ['name', 'tid', 'pid', 'ph', 'args', 'local', 'id', 'ts']
+
+
+class FakeAndroidPlatformBackend(object):
+  def __init__(self):
+    self.device = 'fake_device'
+
+  def GetOSName(self):
+    return 'android'
+
+
+class CpuTracingAgentTest(unittest.TestCase):
+
+  def setUp(self):
+    self._config = tracing_config.TracingConfig()
+    self._config.enable_cpu_trace = True
+    if sys.platform.startswith('win'):
+      self._desktop_backend = win_platform_backend.WinPlatformBackend()
+    elif sys.platform.startswith('darwin'):
+      self._desktop_backend = mac_platform_backend.MacPlatformBackend()
+    else:
+      self._desktop_backend = linux_platform_backend.LinuxPlatformBackend()
+    self._agent = cpu_tracing_agent.CpuTracingAgent(self._desktop_backend)
+
+  @decorators.Enabled('linux', 'mac', 'win')
+  def testInit(self):
+    self.assertTrue(isinstance(self._agent,
+                               tracing_agent.TracingAgent))
+    self.assertFalse(self._agent._snapshots)
+    self.assertFalse(self._agent._snapshot_ongoing)
+
+  @decorators.Enabled('linux', 'mac', 'win')
+  def testIsSupported(self):
+    self.assertTrue(cpu_tracing_agent.CpuTracingAgent.IsSupported(
+      self._desktop_backend))
+    self.assertFalse(cpu_tracing_agent.CpuTracingAgent.IsSupported(
+      FakeAndroidPlatformBackend()))
+
+  @decorators.Enabled('linux', 'mac', 'win')
+  def testStartAgentTracing(self):
+    self.assertFalse(self._agent._snapshot_ongoing)
+    self.assertFalse(self._agent._snapshots)
+    self.assertTrue(self._agent.StartAgentTracing(self._config, 0))
+    self.assertTrue(self._agent._snapshot_ongoing)
+    time.sleep(2)
+    self.assertTrue(self._agent._snapshots)
+    self._agent.StopAgentTracing()
+
+  @decorators.Enabled('linux', 'mac', 'win')
+  def testStartAgentTracingNotEnabled(self):
+    self._config.enable_cpu_trace = False
+    self.assertFalse(self._agent._snapshot_ongoing)
+    self.assertFalse(self._agent.StartAgentTracing(self._config, 0))
+    self.assertFalse(self._agent._snapshot_ongoing)
+    self.assertFalse(self._agent._snapshots)
+    time.sleep(2)
+    self.assertFalse(self._agent._snapshots)
+
+  @decorators.Enabled('linux', 'mac', 'win')
+  def testStopAgentTracingBeforeStart(self):
+    self.assertRaises(AssertionError, self._agent.StopAgentTracing)
+
+  @decorators.Enabled('linux', 'mac', 'win')
+  def testStopAgentTracing(self):
+    self._agent.StartAgentTracing(self._config, 0)
+    self._agent.StopAgentTracing()
+    self.assertFalse(self._agent._snapshot_ongoing)
+
+  @decorators.Enabled('linux', 'mac', 'win')
+  def testCollectAgentTraceDataBeforeStop(self):
+    self._agent.StartAgentTracing(self._config, 0)
+    self.assertRaises(AssertionError, self._agent.CollectAgentTraceData,
+        trace_data.TraceDataBuilder())
+    self._agent.StopAgentTracing()
+
+  @decorators.Enabled('linux', 'mac', 'win')
+  def testCollectAgentTraceData(self):
+    builder = trace_data.TraceDataBuilder()
+    self._agent.StartAgentTracing(self._config, 0)
+    self._agent.StopAgentTracing()
+    self._agent.CollectAgentTraceData(builder)
+    self.assertFalse(self._agent._snapshot_ongoing)
+    builder = builder.AsData()
+    self.assertTrue(builder.HasTracesFor(trace_data.CPU_TRACE_DATA))
+
+  @decorators.Enabled('linux', 'mac', 'win')
+  def testCollectAgentTraceDataFormat(self):
+    builder = trace_data.TraceDataBuilder()
+    self._agent.StartAgentTracing(self._config, 0)
+    time.sleep(2)
+    self._agent.StopAgentTracing()
+    self._agent.CollectAgentTraceData(builder)
+    builder = builder.AsData()
+    data = json.loads(builder.GetTracesFor(trace_data.CPU_TRACE_DATA)[0])
+    self.assertTrue(data)
+    self.assertEquals(set(data[0].keys()), set(TRACE_EVENT_KEYS))
+    self.assertEquals(set(data[0]['args']['snapshot'].keys()),
+                      set(['processes']))
+    self.assertTrue(data[0]['args']['snapshot']['processes'])
+    self.assertEquals(set(data[0]['args']['snapshot']['processes'][0].keys()),
+                      set(SNAPSHOT_KEYS))
+
+  @decorators.Enabled('linux', 'mac', 'win')
+  def testContainsRealProcesses(self):
+    builder = trace_data.TraceDataBuilder()
+    self._agent.StartAgentTracing(self._config, 0)
+    time.sleep(2)
+    self._agent.StopAgentTracing()
+    self._agent.CollectAgentTraceData(builder)
+    builder = builder.AsData()
+    data = json.loads(builder.GetTracesFor(trace_data.CPU_TRACE_DATA)[0])
+    self.assertTrue(data)
+    for snapshot in data:
+      found_unittest_process = False
+      processes = snapshot['args']['snapshot']['processes']
+      for process in processes:
+        if 'run_tests' in process['name']:
+          found_unittest_process = True
+
+      self.assertTrue(found_unittest_process)
+
+  @decorators.Enabled('win')
+  def testWindowsCanHandleProcessesWithSpaces(self):
+    proc_collector = cpu_tracing_agent.WindowsProcessCollector()
+    proc_collector.Init()
+    proc = proc_collector._ParseProcessString(
+      '0 1 Multi Word Process 50 75')
+    self.assertEquals(proc['ppid'], 0)
+    self.assertEquals(proc['pid'], 1)
+    self.assertEquals(proc['name'], 'Multi Word Process')
+    self.assertEquals(proc['pCpu'], 50)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/tracing_agent/display_tracing_agent.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/tracing_agent/display_tracing_agent.py
new file mode 100644
index 0000000..c7c67a3
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/tracing_agent/display_tracing_agent.py
@@ -0,0 +1,32 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.internal.platform import tracing_agent
+from tracing.trace_data import trace_data
+
+
+class DisplayTracingAgent(tracing_agent.TracingAgent):
+  def __init__(self, platform_backend):
+    super(DisplayTracingAgent, self).__init__(platform_backend)
+
+  @classmethod
+  def IsSupported(cls, platform_backend):
+    return platform_backend.IsDisplayTracingSupported()
+
+  def StartAgentTracing(self, config, timeout):
+    del timeout  # unused
+    if config.enable_platform_display_trace:
+      self._platform_backend.StartDisplayTracing()
+      return True
+
+  def StopAgentTracing(self):
+    # TODO: Split collection and stopping.
+    pass
+
+  def CollectAgentTraceData(self, trace_data_builder, timeout=None):
+    # TODO: Move stopping to StopAgentTracing.
+    del timeout
+    surface_flinger_trace_data = self._platform_backend.StopDisplayTracing()
+    trace_data_builder.AddTraceFor(
+          trace_data.SURFACE_FLINGER_PART, surface_flinger_trace_data)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/tracing_agent/display_tracing_agent_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/tracing_agent/display_tracing_agent_unittest.py
new file mode 100644
index 0000000..62b5b08
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/tracing_agent/display_tracing_agent_unittest.py
@@ -0,0 +1,67 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import mock
+import unittest
+
+from telemetry.internal.platform import android_platform_backend
+from telemetry.internal.platform.tracing_agent import display_tracing_agent
+from telemetry.timeline import tracing_config
+
+# pylint: disable=super-init-not-called, abstract-method, unused-argument
+class FakeAndroidPlatformBackend(
+    android_platform_backend.AndroidPlatformBackend):
+  def __init__(self):
+    self._device = 0
+    self._raw_display_frame_rate_measurements = []
+    self._surface_stats_collector = None
+
+  @property
+  def surface_stats_collector(self):
+    return self._surface_stats_collector
+
+  def IsDisplayTracingSupported(self):
+    return True
+
+
+class DisplayTracingAgentTest(unittest.TestCase):
+  def setUp(self):
+    self._config = tracing_config.TracingConfig()
+    self._config.enable_platform_display_trace = True
+    self._platform_backend = FakeAndroidPlatformBackend()
+    self._agent = display_tracing_agent.DisplayTracingAgent(
+        self._platform_backend)
+
+  def stopAndCollect(self):
+    self._agent.StopAgentTracing()
+    self._agent.CollectAgentTraceData(mock.MagicMock())
+
+  @mock.patch(
+      'devil.android.perf.surface_stats_collector.SurfaceStatsCollector')
+  def testStartAndStopTracing(self, MockSurfaceStatsCollector):
+    self._agent.StartAgentTracing(self._config, 10)
+    # Second start tracing will raise error.
+    with self.assertRaises(AssertionError):
+      self._agent.StartAgentTracing(self._config, 10)
+    self._platform_backend.surface_stats_collector.Stop.return_value = (0, [])
+    self.stopAndCollect()
+
+    # Can start and stop tracing multiple times.
+    self._agent.StartAgentTracing(self._config, 10)
+    self._platform_backend.surface_stats_collector.Stop.return_value = (0, [])
+    self.stopAndCollect()
+
+  @mock.patch(
+      'devil.android.perf.surface_stats_collector.SurfaceStatsCollector')
+  def testExceptionRaisedInStopTracing(self, MockSurfaceStatsCollector):
+    self._agent.StartAgentTracing(self._config, 10)
+    self._platform_backend.surface_stats_collector.Stop.side_effect = Exception(
+        'Raise error when stopping tracing.')
+    with self.assertRaises(Exception):
+      self.stopAndCollect()
+    # Tracing is stopped even if there is exception. And the agent can start
+    # tracing again.
+    self._agent.StartAgentTracing(self._config, 10)
+    self._platform_backend.surface_stats_collector.Stop.side_effect = None
+    self._platform_backend.surface_stats_collector.Stop.return_value = (0, [])
+    self.stopAndCollect()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/tracing_controller_backend.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/tracing_controller_backend.py
new file mode 100644
index 0000000..31365b0
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/tracing_controller_backend.py
@@ -0,0 +1,293 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import ast
+from telemetry.internal.util import atexit_with_log
+import contextlib
+import gc
+import logging
+import os
+import sys
+import tempfile
+import traceback
+import uuid
+
+from py_trace_event import trace_event
+from telemetry.core import discover
+from telemetry.core import exceptions
+from telemetry.core import util
+from telemetry.internal.platform import tracing_agent
+from telemetry.internal.platform.tracing_agent import chrome_tracing_agent
+from telemetry.timeline import tracing_config
+from tracing.trace_data import trace_data as trace_data_module
+
+
+def _IterAllTracingAgentClasses():
+  tracing_agent_dir = os.path.join(
+      os.path.dirname(os.path.realpath(__file__)), 'tracing_agent')
+  return discover.DiscoverClasses(
+      tracing_agent_dir, util.GetTelemetryDir(),
+      tracing_agent.TracingAgent).itervalues()
+
+
+class _TracingState(object):
+
+  def __init__(self, config, timeout):
+    self._builder = trace_data_module.TraceDataBuilder()
+    self._config = config
+    self._timeout = timeout
+
+  @property
+  def builder(self):
+    return self._builder
+
+  @property
+  def config(self):
+    return self._config
+
+  @property
+  def timeout(self):
+    return self._timeout
+
+
+class TracingControllerBackend(object):
+  def __init__(self, platform_backend):
+    self._platform_backend = platform_backend
+    self._current_state = None
+    self._supported_agents_classes = [
+        agent_classes for agent_classes in _IterAllTracingAgentClasses() if
+        agent_classes.IsSupported(platform_backend)]
+    self._active_agents_instances = []
+    self._trace_log = None
+    self._is_tracing_controllable = True
+    self._telemetry_info = None
+
+  def StartTracing(self, config, timeout):
+    if self.is_tracing_running:
+      return False
+
+    assert isinstance(config, tracing_config.TracingConfig)
+    assert len(self._active_agents_instances) == 0
+
+    self._current_state = _TracingState(config, timeout)
+    # Hack: chrome tracing agent may only depend on the number of alive chrome
+    # devtools processes, rather platform (when startup tracing is not
+    # supported), hence we add it to the list of supported agents here if it was
+    # not added.
+    if (chrome_tracing_agent.ChromeTracingAgent.IsSupported(
+        self._platform_backend) and
+        not chrome_tracing_agent.ChromeTracingAgent in
+        self._supported_agents_classes):
+      self._supported_agents_classes.append(
+          chrome_tracing_agent.ChromeTracingAgent)
+
+    self.StartAgentTracing(config, timeout)
+    for agent_class in self._supported_agents_classes:
+      agent = agent_class(self._platform_backend)
+      with trace_event.trace(
+          'StartAgentTracing',
+          agent=str(agent.__class__.__name__)):
+        started = agent.StartAgentTracing(config, timeout)
+      if started:
+        self._active_agents_instances.append(agent)
+    return True
+
+  def _GenerateClockSyncId(self):
+    return str(uuid.uuid4())
+
+  @contextlib.contextmanager
+  def _DisableGarbageCollection(self):
+    try:
+      gc.disable()
+      yield
+    finally:
+      gc.enable()
+
+  def StopTracing(self):
+    assert self.is_tracing_running, 'Can only stop tracing when tracing is on.'
+    self._IssueClockSyncMarker()
+    builder = self._current_state.builder
+
+    raised_exception_messages = []
+    for agent in self._active_agents_instances + [self]:
+      try:
+        with trace_event.trace(
+            'StopAgentTracing',
+            agent=str(agent.__class__.__name__)):
+          agent.StopAgentTracing()
+      except Exception: # pylint: disable=broad-except
+        raised_exception_messages.append(
+            ''.join(traceback.format_exception(*sys.exc_info())))
+
+    for agent in self._active_agents_instances + [self]:
+      try:
+        with trace_event.trace(
+            'CollectAgentTraceData',
+            agent=str(agent.__class__.__name__)):
+          agent.CollectAgentTraceData(builder)
+      except Exception: # pylint: disable=broad-except
+        raised_exception_messages.append(
+            ''.join(traceback.format_exception(*sys.exc_info())))
+
+    self._telemetry_info = None
+    self._active_agents_instances = []
+    self._current_state = None
+
+    if raised_exception_messages:
+      raise exceptions.Error(
+          'Exceptions raised when trying to stop tracing:\n' +
+          '\n'.join(raised_exception_messages))
+
+    return builder.AsData()
+
+  def FlushTracing(self):
+    assert self.is_tracing_running, 'Can only flush tracing when tracing is on.'
+    self._IssueClockSyncMarker()
+
+    raised_exception_messages = []
+    # Flushing the controller's pytrace is not supported.
+    for agent in self._active_agents_instances:
+      try:
+        if agent.SupportsFlushingAgentTracing():
+          with trace_event.trace(
+              'FlushAgentTracing',
+              agent=str(agent.__class__.__name__)):
+            agent.FlushAgentTracing(self._current_state.config,
+                                    self._current_state.timeout,
+                                    self._current_state.builder)
+      except Exception: # pylint: disable=broad-except
+        raised_exception_messages.append(
+            ''.join(traceback.format_exception(*sys.exc_info())))
+
+    if raised_exception_messages:
+      raise exceptions.Error(
+          'Exceptions raised when trying to flush tracing:\n' +
+          '\n'.join(raised_exception_messages))
+
+  def StartAgentTracing(self, config, timeout):
+    self._is_tracing_controllable = self._IsTracingControllable()
+    if not self._is_tracing_controllable:
+      return False
+
+    tf = tempfile.NamedTemporaryFile(delete=False)
+    self._trace_log = tf.name
+    tf.close()
+    del config # unused
+    del timeout # unused
+    assert not trace_event.trace_is_enabled(), 'Tracing already running.'
+    trace_event.trace_enable(self._trace_log)
+    assert trace_event.trace_is_enabled(), 'Tracing didn\'t enable properly.'
+    return True
+
+  def StopAgentTracing(self):
+    if not self._is_tracing_controllable:
+      return
+    assert trace_event.trace_is_enabled(), 'Tracing not running'
+    trace_event.trace_disable()
+    assert not trace_event.trace_is_enabled(), 'Tracing didnt disable properly.'
+
+  def SupportsExplicitClockSync(self):
+    return True
+
+  def _RecordIssuerClockSyncMarker(self, sync_id, issue_ts):
+    """ Record clock sync event.
+
+    Args:
+      sync_id: Unqiue id for sync event.
+      issue_ts: timestamp before issuing clocksync to agent.
+    """
+    if self._is_tracing_controllable:
+      trace_event.clock_sync(sync_id, issue_ts=issue_ts)
+
+  def _IssueClockSyncMarker(self):
+    with self._DisableGarbageCollection():
+      for agent in self._active_agents_instances:
+        if agent.SupportsExplicitClockSync():
+          sync_id = self._GenerateClockSyncId()
+          with trace_event.trace(
+              'RecordClockSyncMarker',
+              agent=str(agent.__class__.__name__),
+              sync_id=sync_id):
+            agent.RecordClockSyncMarker(sync_id,
+                                        self._RecordIssuerClockSyncMarker)
+
+  def IsChromeTracingSupported(self):
+    return chrome_tracing_agent.ChromeTracingAgent.IsSupported(
+        self._platform_backend)
+
+  @property
+  def is_tracing_running(self):
+    return self._current_state is not None
+
+  def _GetActiveChromeTracingAgent(self):
+    if not self.is_tracing_running:
+      return None
+    if not self._current_state.config.enable_chrome_trace:
+      return None
+    for agent in self._active_agents_instances:
+      if isinstance(agent, chrome_tracing_agent.ChromeTracingAgent):
+        return agent
+    return None
+
+  def GetChromeTraceConfig(self):
+    agent = self._GetActiveChromeTracingAgent()
+    if agent:
+      return agent.trace_config
+    return None
+
+  def GetChromeTraceConfigFile(self):
+    agent = self._GetActiveChromeTracingAgent()
+    if agent:
+      return agent.trace_config_file
+    return None
+
+  def _IsTracingControllable(self):
+    return trace_event.is_tracing_controllable()
+
+  def ClearStateIfNeeded(self):
+    chrome_tracing_agent.ClearStarupTracingStateIfNeeded(self._platform_backend)
+
+  @property
+  def telemetry_info(self):
+    return self._telemetry_info
+
+  @telemetry_info.setter
+  def telemetry_info(self, ii):
+    self._telemetry_info = ii
+
+  def CollectAgentTraceData(self, trace_data_builder):
+    if not self._is_tracing_controllable:
+      return
+    assert not trace_event.trace_is_enabled(), 'Stop tracing before collection.'
+    with open(self._trace_log, 'r') as fp:
+      data = ast.literal_eval(fp.read() + ']')
+    trace_data_builder.AddTraceFor(trace_data_module.TELEMETRY_PART, {
+        "traceEvents": data,
+        "metadata": {
+            # TODO(charliea): For right now, we use "TELEMETRY" as the clock
+            # domain to guarantee that Telemetry is given its own clock
+            # domain. Telemetry isn't really a clock domain, though: it's a
+            # system that USES a clock domain like LINUX_CLOCK_MONOTONIC or
+            # WIN_QPC. However, there's a chance that a Telemetry controller
+            # running on Linux (using LINUX_CLOCK_MONOTONIC) is interacting with
+            # an Android phone (also using LINUX_CLOCK_MONOTONIC, but on a
+            # different machine). The current logic collapses clock domains
+            # based solely on the clock domain string, but we really should to
+            # collapse based on some (device ID, clock domain ID) tuple. Giving
+            # Telemetry its own clock domain is a work-around for this.
+            "clock-domain": "TELEMETRY",
+            "telemetry": (self._telemetry_info.AsDict()
+                if self._telemetry_info else {}),
+        }
+    })
+    try:
+      os.remove(self._trace_log)
+      self._trace_log = None
+    except OSError:
+      logging.exception('Error when deleting %s, will try again at exit.',
+                        self._trace_log)
+      def DeleteAtExit(path):
+        os.remove(path)
+      atexit_with_log.Register(DeleteAtExit, self._trace_log)
+    self._trace_log = None
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/tracing_controller_backend_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/tracing_controller_backend_unittest.py
new file mode 100644
index 0000000..4431985
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/tracing_controller_backend_unittest.py
@@ -0,0 +1,373 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import gc
+import platform as _platform
+import unittest
+
+from telemetry import decorators
+from telemetry.internal.platform import linux_based_platform_backend
+from telemetry.internal.platform import tracing_agent
+from telemetry.internal.platform import tracing_controller_backend
+from telemetry.timeline import tracing_config
+from tracing.trace_data import trace_data
+
+
+class PlatformDevice(object):
+  def __init__(self):
+    self.build_version_sdk = 99
+
+class PlatformBackend(linux_based_platform_backend.LinuxBasedPlatformBackend):
+  # pylint: disable=abstract-method
+  def __init__(self):
+    super(PlatformBackend, self).__init__()
+    self._mock_files = {}
+
+  def GetOSName(self):
+    if 'Win' in _platform.system():
+      return 'win'
+    elif 'Linux' in _platform.system():
+      return 'android'
+    elif 'Darwin' in _platform.system():
+      return 'mac'
+
+  @property
+  def device(self):
+    return PlatformDevice()
+
+
+class FakeTracingAgentBase(tracing_agent.TracingAgent):
+  def __init__(
+      self, platform, start=True, clock_sync=True, split_collection=True):
+    super(FakeTracingAgentBase, self).__init__(platform)
+    self._start = start
+    self._clock_sync = clock_sync
+    self._sync_seen = False
+    self._split_collection = split_collection
+
+  def StartAgentTracing(self, config, timeout):
+    return self._start
+
+  def StopAgentTracing(self):
+    pass
+
+  def SupportsExplicitClockSync(self):
+    return self._clock_sync
+
+  def RecordClockSyncMarker(self, sync_id, callback):
+    if not self._clock_sync:
+      raise NotImplementedError
+    self._sync_seen = True
+    callback(sync_id, 1)
+
+  def CollectAgentTraceData(self, trace_data_builder, timeout=None):
+    pass
+
+
+class FakeTracingAgentStartAndClockSync(FakeTracingAgentBase):
+  def __init__(self, platform):
+    super(FakeTracingAgentStartAndClockSync, self).__init__(
+        platform, start=True, clock_sync=True, split_collection=False)
+
+
+class FakeTracingAgentStartAndNoClockSync(FakeTracingAgentBase):
+  def __init__(self, platform):
+    super(FakeTracingAgentStartAndNoClockSync, self).__init__(platform,
+                                                            start=True,
+                                                            clock_sync=False)
+
+
+class FakeTracingAgentNoStartAndNoClockSync(FakeTracingAgentBase):
+  def __init__(self, platform):
+    super(FakeTracingAgentNoStartAndNoClockSync, self).__init__(platform,
+                                                            start=False,
+                                                            clock_sync=False)
+
+
+class FakeTracingAgentNoStartAndClockSync(FakeTracingAgentBase):
+  def __init__(self, platform):
+    super(FakeTracingAgentNoStartAndClockSync, self).__init__(platform,
+                                                              start=False,
+                                                              clock_sync=True)
+
+
+class TracingControllerBackendTest(unittest.TestCase):
+  def _getControllerEventsAslist(self, data):
+    traces = data.GetTracesFor(trace_data.TELEMETRY_PART)
+    if not traces:
+      return []
+    assert len(traces) == 1
+    telemetry_trace = traces[0]
+    return telemetry_trace["traceEvents"]
+
+  def _getControllerClockDomain(self, data):
+    traces = data.GetTracesFor(trace_data.TELEMETRY_PART)
+    if not traces:
+      return []
+    assert len(traces) == 1
+    telemetry_trace = traces[0]
+    telemetry_trace = data.GetTracesFor(trace_data.TELEMETRY_PART)[0]
+    if not telemetry_trace or not telemetry_trace["metadata"]:
+      return ""
+    return telemetry_trace["metadata"]["clock-domain"]
+
+  def _getSyncCount(self, data):
+    return len([entry for entry in self._getControllerEventsAslist(data)
+                if entry.get('name') == 'clock_sync'])
+
+  def setUp(self):
+    self.platform = PlatformBackend()
+    self.controller = (
+        tracing_controller_backend.TracingControllerBackend(self.platform))
+    self.controller._supported_agents_classes = [FakeTracingAgentBase]
+    self.config = tracing_config.TracingConfig()
+    self.controller_log = self.controller._trace_log
+
+  def tearDown(self):
+    if self.controller.is_tracing_running:
+      self.controller.StopTracing()
+
+  @decorators.Isolated
+  def testStartTracing(self):
+    self.assertFalse(self.controller.is_tracing_running)
+    self.assertTrue(self.controller.StartTracing(self.config, 30))
+    self.assertTrue(self.controller.is_tracing_running)
+
+  @decorators.Isolated
+  def testDoubleStartTracing(self):
+    self.assertFalse(self.controller.is_tracing_running)
+    self.assertTrue(self.controller.StartTracing(self.config, 30))
+    self.assertTrue(self.controller.is_tracing_running)
+    self.assertFalse(self.controller.StartTracing(self.config, 30))
+
+  @decorators.Isolated
+  def testStopTracingNotStarted(self):
+    with self.assertRaises(AssertionError):
+      self.controller.StopTracing()
+
+  @decorators.Isolated
+  def testStopTracing(self):
+    self.assertFalse(self.controller.is_tracing_running)
+    self.assertTrue(self.controller.StartTracing(self.config, 30))
+    self.assertTrue(self.controller.is_tracing_running)
+    data = self.controller.StopTracing()
+    self.assertEqual(self._getSyncCount(data), 1)
+    self.assertFalse(self.controller.is_tracing_running)
+    self.assertEqual(self.controller._trace_log, None)
+
+  @decorators.Isolated
+  def testDoubleStopTracing(self):
+    self.assertFalse(self.controller.is_tracing_running)
+    self.assertTrue(self.controller.StartTracing(self.config, 30))
+    self.assertTrue(self.controller.is_tracing_running)
+    self.controller.StopTracing()
+    self.assertFalse(self.controller.is_tracing_running)
+    with self.assertRaises(AssertionError):
+      self.controller.StopTracing()
+
+  @decorators.Isolated
+  def testMultipleStartStop(self):
+    self.assertFalse(self.controller.is_tracing_running)
+    self.assertTrue(self.controller.StartTracing(self.config, 30))
+    self.assertTrue(self.controller.is_tracing_running)
+    data = self.controller.StopTracing()
+    self.assertEqual(self._getSyncCount(data), 1)
+    sync_event_one = [x for x in self._getControllerEventsAslist(data)
+                      if x.get('name') == 'clock_sync'][0]
+    self.assertFalse(self.controller.is_tracing_running)
+    self.assertEqual(self.controller._trace_log, None)
+    # Run 2
+    self.assertTrue(self.controller.StartTracing(self.config, 30))
+    self.assertTrue(self.controller.is_tracing_running)
+    data = self.controller.StopTracing()
+    self.assertEqual(self._getSyncCount(data), 1)
+    sync_event_two = [x for x in self._getControllerEventsAslist(data)
+                      if x.get('name') == 'clock_sync'][0]
+    self.assertFalse(self.controller.is_tracing_running)
+    self.assertFalse(self.controller._trace_log, None)
+    # Test difference between events
+    self.assertNotEqual(sync_event_one, sync_event_two)
+
+  @decorators.Isolated
+  def testCollectAgentDataBeforeStoppingTracing(self):
+    self.assertFalse(self.controller.is_tracing_running)
+    self.assertTrue(self.controller.StartTracing(self.config, 30))
+    self.assertTrue(self.controller.is_tracing_running)
+    with self.assertRaises(AssertionError):
+      self.controller.CollectAgentTraceData(None)
+
+  @decorators.Isolated
+  def testFlush(self):
+    self.assertFalse(self.controller.is_tracing_running)
+    self.assertIsNone(self.controller._current_state)
+
+    # Start tracing.
+    self.assertTrue(self.controller.StartTracing(self.config, 30))
+    self.assertTrue(self.controller.is_tracing_running)
+    self.assertIs(self.controller._current_state.config, self.config)
+    self.assertEqual(self.controller._current_state.timeout, 30)
+    self.assertIsNotNone(self.controller._current_state.builder)
+
+    # Flush tracing several times.
+    for _ in xrange(5):
+      self.controller.FlushTracing()
+      self.assertTrue(self.controller.is_tracing_running)
+      self.assertIs(self.controller._current_state.config, self.config)
+      self.assertEqual(self.controller._current_state.timeout, 30)
+      self.assertIsNotNone(self.controller._current_state.builder)
+
+    # Stop tracing.
+    data = self.controller.StopTracing()
+    self.assertFalse(self.controller.is_tracing_running)
+    self.assertIsNone(self.controller._current_state)
+
+    self.assertEqual(self._getSyncCount(data), 6)
+
+  @decorators.Isolated
+  def testNoWorkingAgents(self):
+    self.controller._supported_agents_classes = [
+        FakeTracingAgentNoStartAndNoClockSync
+    ]
+    self.assertFalse(self.controller.is_tracing_running)
+    self.assertTrue(self.controller.StartTracing(self.config, 30))
+    self.assertTrue(self.controller.is_tracing_running)
+    self.assertEquals(self.controller._active_agents_instances, [])
+    data = self.controller.StopTracing()
+    self.assertEqual(self._getSyncCount(data), 0)
+    self.assertFalse(self.controller.is_tracing_running)
+
+  @decorators.Isolated
+  def testNoClockSyncSupport(self):
+    self.controller._supported_agents_classes = [
+        FakeTracingAgentStartAndNoClockSync,
+        FakeTracingAgentNoStartAndNoClockSync,
+    ]
+    self.assertFalse(self.controller.is_tracing_running)
+    self.assertTrue(self.controller.StartTracing(self.config, 30))
+    self.assertTrue(self.controller.is_tracing_running)
+    data = self.controller.StopTracing()
+    self.assertFalse(self.controller.is_tracing_running)
+    self.assertEquals(self._getSyncCount(data), 0)
+
+  @decorators.Isolated
+  def testClockSyncSupport(self):
+    self.controller._supported_agents_classes = [
+        FakeTracingAgentStartAndClockSync,
+        FakeTracingAgentStartAndClockSync,
+        FakeTracingAgentStartAndNoClockSync,
+        FakeTracingAgentNoStartAndClockSync,
+        FakeTracingAgentNoStartAndNoClockSync
+    ]
+    self.assertFalse(self.controller.is_tracing_running)
+    self.assertTrue(self.controller.StartTracing(self.config, 30))
+    self.assertTrue(self.controller.is_tracing_running)
+    self.assertEquals(len(self.controller._active_agents_instances), 3)
+    # No sync event before running StopTracing().
+    data = self.controller.StopTracing()
+    self.assertFalse(self.controller.is_tracing_running)
+    self.assertEquals(self._getSyncCount(data), 2)
+    self.assertEquals(self._getControllerClockDomain(data), "TELEMETRY")
+
+  @decorators.Isolated
+  def testMultipleAgents(self):
+    self.controller._supported_agents_classes = [
+        FakeTracingAgentStartAndClockSync,
+        FakeTracingAgentStartAndClockSync,
+        FakeTracingAgentNoStartAndClockSync,
+        FakeTracingAgentNoStartAndClockSync,
+        FakeTracingAgentNoStartAndNoClockSync,
+        FakeTracingAgentNoStartAndNoClockSync,
+        FakeTracingAgentStartAndNoClockSync,
+        FakeTracingAgentStartAndNoClockSync
+    ]
+    self.assertFalse(self.controller.is_tracing_running)
+    self.assertTrue(self.controller.StartTracing(self.config, 30))
+    self.assertTrue(self.controller.is_tracing_running)
+    self.assertEquals(len(self.controller._active_agents_instances), 4)
+    data = self.controller.StopTracing()
+    self.assertFalse(self.controller.is_tracing_running)
+    self.assertEquals(self._getSyncCount(data), 2)
+
+  @decorators.Isolated
+  def testGenerateRandomSyncId(self):
+    ids = []
+    for _ in xrange(1000):
+      i = self.controller._GenerateClockSyncId()
+      self.assertFalse(i in ids)
+      ids.append(i)
+
+  @decorators.Isolated
+  def testRecordIssuerClockSyncMarker(self):
+    sync_id = 'test_id'
+    ts = 1
+    self.controller._supported_agents_classes = [
+        FakeTracingAgentNoStartAndNoClockSync,
+        FakeTracingAgentStartAndNoClockSync
+    ]
+    self.assertTrue(self.controller.StartTracing(self.config, 30))
+    self.controller._RecordIssuerClockSyncMarker(sync_id, ts)
+    data = self.controller.StopTracing()
+    self.assertFalse(self.controller.is_tracing_running)
+    self.assertEquals(self._getSyncCount(data), 1)
+    self.assertEquals(self._getControllerClockDomain(data), "TELEMETRY")
+    log = self._getControllerEventsAslist(data)
+    for entry in log:
+      if entry.get('name') == 'clock_sync':
+        self.assertEqual(entry['args']['sync_id'], sync_id)
+        self.assertEqual(entry['args']['issue_ts'], 1)
+
+  @decorators.Isolated
+  def testIssueClockSyncMarker_normalUse(self):
+    self.controller._supported_agents_classes = [
+        FakeTracingAgentStartAndClockSync,
+        FakeTracingAgentStartAndClockSync,
+        FakeTracingAgentNoStartAndClockSync,
+        FakeTracingAgentNoStartAndClockSync,
+        FakeTracingAgentNoStartAndNoClockSync,
+        FakeTracingAgentNoStartAndNoClockSync,
+        FakeTracingAgentStartAndNoClockSync,
+        FakeTracingAgentStartAndNoClockSync
+    ]
+    self.assertFalse(self.controller.is_tracing_running)
+    self.assertTrue(self.controller.StartTracing(self.config, 30))
+    self.assertTrue(self.controller.is_tracing_running)
+    self.assertEquals(len(self.controller._active_agents_instances), 4)
+    self.controller._IssueClockSyncMarker()
+    data = self.controller.StopTracing()
+    self.assertFalse(self.controller.is_tracing_running)
+    self.assertEquals(self._getSyncCount(data), 4)
+    self.assertEquals(self._getControllerClockDomain(data), "TELEMETRY")
+
+  @decorators.Isolated
+  def testIssueClockSyncMarker_tracingNotControllable(self):
+    self.controller._supported_agents_classes = [
+        FakeTracingAgentStartAndClockSync,
+        FakeTracingAgentStartAndClockSync,
+        FakeTracingAgentNoStartAndClockSync,
+        FakeTracingAgentNoStartAndClockSync,
+        FakeTracingAgentNoStartAndNoClockSync,
+        FakeTracingAgentNoStartAndNoClockSync,
+        FakeTracingAgentStartAndNoClockSync,
+        FakeTracingAgentStartAndNoClockSync
+    ]
+    original_controllable = self.controller._IsTracingControllable
+    self.controller._IsTracingControllable = lambda: False
+    try:
+      self.assertFalse(self.controller.is_tracing_running)
+      self.assertTrue(self.controller.StartTracing(self.config, 30))
+      self.assertTrue(self.controller.is_tracing_running)
+      self.assertEquals(len(self.controller._active_agents_instances), 4)
+      self.controller._IssueClockSyncMarker()
+      data = self.controller.StopTracing()
+      self.assertFalse(self.controller.is_tracing_running)
+      self.assertEquals(self._getSyncCount(data), 0)
+    finally:
+      self.controller._IsTracingControllable = original_controllable
+
+  @decorators.Isolated
+  def testDisableGarbageCollection(self):
+    self.assertTrue(gc.isenabled())
+    with self.controller._DisableGarbageCollection():
+      self.assertFalse(gc.isenabled())
+    self.assertTrue(gc.isenabled())
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/win_platform_backend.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/win_platform_backend.py
new file mode 100644
index 0000000..f1fbf05
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/platform/win_platform_backend.py
@@ -0,0 +1,443 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.internal.util import atexit_with_log
+import collections
+import contextlib
+import ctypes
+import logging
+import os
+import platform
+import re
+import socket
+import struct
+import subprocess
+import sys
+import time
+import zipfile
+
+from py_utils import cloud_storage  # pylint: disable=import-error
+
+from telemetry.core import exceptions
+from telemetry.core import os_version as os_version_module
+from telemetry import decorators
+from telemetry.internal.platform import desktop_platform_backend
+from telemetry.internal.platform.power_monitor import msr_power_monitor
+from telemetry.internal.util import path
+
+try:
+  import pywintypes  # pylint: disable=import-error
+  import win32api  # pylint: disable=import-error
+  from win32com.shell import shell  # pylint: disable=no-name-in-module
+  from win32com.shell import shellcon  # pylint: disable=no-name-in-module
+  import win32con  # pylint: disable=import-error
+  import win32file  # pylint: disable=import-error
+  import win32gui  # pylint: disable=import-error
+  import win32pipe  # pylint: disable=import-error
+  import win32process  # pylint: disable=import-error
+  try:
+    import winreg  # pylint: disable=import-error
+  except ImportError:
+    import _winreg as winreg  # pylint: disable=import-error
+  import win32security  # pylint: disable=import-error
+except ImportError:
+  pywintypes = None
+  shell = None
+  shellcon = None
+  win32api = None
+  win32con = None
+  win32file = None
+  win32gui = None
+  win32pipe = None
+  win32process = None
+  win32security = None
+  winreg = None
+
+
+def _InstallWinRing0():
+  """WinRing0 is used for reading MSRs."""
+  executable_dir = os.path.dirname(sys.executable)
+
+  python_is_64_bit = sys.maxsize > 2 ** 32
+  dll_file_name = 'WinRing0x64.dll' if python_is_64_bit else 'WinRing0.dll'
+  dll_path = os.path.join(executable_dir, dll_file_name)
+
+  os_is_64_bit = platform.machine().endswith('64')
+  driver_file_name = 'WinRing0x64.sys' if os_is_64_bit else 'WinRing0.sys'
+  driver_path = os.path.join(executable_dir, driver_file_name)
+
+  # Check for WinRing0 and download if needed.
+  if not (os.path.exists(dll_path) and os.path.exists(driver_path)):
+    win_binary_dir = os.path.join(
+        path.GetTelemetryDir(), 'bin', 'win', 'AMD64')
+    zip_path = os.path.join(win_binary_dir, 'winring0.zip')
+    cloud_storage.GetIfChanged(zip_path, bucket=cloud_storage.PUBLIC_BUCKET)
+    try:
+      with zipfile.ZipFile(zip_path, 'r') as zip_file:
+        error_message = (
+            'Failed to extract %s into %s. If python claims that '
+            'the zip file is locked, this may be a lie. The problem may be '
+            'that python does not have write permissions to the destination '
+            'directory.'
+        )
+        # Install DLL.
+        if not os.path.exists(dll_path):
+          try:
+            zip_file.extract(dll_file_name, executable_dir)
+          except:
+            logging.error(error_message % (dll_file_name, executable_dir))
+            raise
+
+        # Install kernel driver.
+        if not os.path.exists(driver_path):
+          try:
+            zip_file.extract(driver_file_name, executable_dir)
+          except:
+            logging.error(error_message % (driver_file_name, executable_dir))
+            raise
+    finally:
+      os.remove(zip_path)
+
+
+def TerminateProcess(process_handle):
+  if not process_handle:
+    return
+  if win32process.GetExitCodeProcess(process_handle) == win32con.STILL_ACTIVE:
+    win32process.TerminateProcess(process_handle, 0)
+  process_handle.close()
+
+
+class WinPlatformBackend(desktop_platform_backend.DesktopPlatformBackend):
+  def __init__(self):
+    super(WinPlatformBackend, self).__init__()
+    self._msr_server_handle = None
+    self._msr_server_port = None
+    self._power_monitor = msr_power_monitor.MsrPowerMonitorWin(self)
+
+  @classmethod
+  def IsPlatformBackendForHost(cls):
+    return sys.platform == 'win32'
+
+  def __del__(self):
+    self.close()
+
+  def close(self):
+    self.CloseMsrServer()
+
+  def CloseMsrServer(self):
+    if not self._msr_server_handle:
+      return
+
+    TerminateProcess(self._msr_server_handle)
+    self._msr_server_handle = None
+    self._msr_server_port = None
+
+  def IsThermallyThrottled(self):
+    raise NotImplementedError()
+
+  def HasBeenThermallyThrottled(self):
+    raise NotImplementedError()
+
+  def GetSystemCommitCharge(self):
+    performance_info = self._GetPerformanceInfo()
+    return performance_info.CommitTotal * performance_info.PageSize / 1024
+
+  @decorators.Cache
+  def GetSystemTotalPhysicalMemory(self):
+    performance_info = self._GetPerformanceInfo()
+    return performance_info.PhysicalTotal * performance_info.PageSize / 1024
+
+  def GetCpuStats(self, pid):
+    cpu_info = self._GetWin32ProcessInfo(win32process.GetProcessTimes, pid)
+    # Convert 100 nanosecond units to seconds
+    cpu_time = (cpu_info['UserTime'] / 1e7 +
+                cpu_info['KernelTime'] / 1e7)
+    return {'CpuProcessTime': cpu_time}
+
+  def GetCpuTimestamp(self):
+    """Return current timestamp in seconds."""
+    return {'TotalTime': time.time()}
+
+  @decorators.Deprecated(
+      2017, 11, 4,
+      'Clients should use tracing and memory-infra in new Telemetry '
+      'benchmarks. See for context: https://crbug.com/632021')
+  def GetMemoryStats(self, pid):
+    memory_info = self._GetWin32ProcessInfo(
+        win32process.GetProcessMemoryInfo, pid)
+    return {'VM': memory_info['PagefileUsage'],
+            'VMPeak': memory_info['PeakPagefileUsage'],
+            'WorkingSetSize': memory_info['WorkingSetSize'],
+            'WorkingSetSizePeak': memory_info['PeakWorkingSetSize']}
+
+  def KillProcess(self, pid, kill_process_tree=False):
+    # os.kill for Windows is Python 2.7.
+    cmd = ['taskkill', '/F', '/PID', str(pid)]
+    if kill_process_tree:
+      cmd.append('/T')
+    subprocess.Popen(cmd, stdout=subprocess.PIPE,
+                     stderr=subprocess.STDOUT).communicate()
+
+  def GetSystemProcessInfo(self):
+    # [3:] To skip 2 blank lines and header.
+    lines = subprocess.Popen(
+        ['wmic', 'process', 'get',
+         'CommandLine,CreationDate,Name,ParentProcessId,ProcessId',
+         '/format:csv'],
+        stdout=subprocess.PIPE).communicate()[0].splitlines()[3:]
+    process_info = []
+    for line in lines:
+      if not line:
+        continue
+      parts = line.split(',')
+      pi = {}
+      pi['ProcessId'] = int(parts[-1])
+      pi['ParentProcessId'] = int(parts[-2])
+      pi['Name'] = parts[-3]
+      creation_date = None
+      if parts[-4]:
+        creation_date = float(re.split('[+-]', parts[-4])[0])
+      pi['CreationDate'] = creation_date
+      pi['CommandLine'] = ','.join(parts[1:-4])
+      process_info.append(pi)
+    return process_info
+
+  def GetChildPids(self, pid):
+    """Retunds a list of child pids of |pid|."""
+    ppid_map = collections.defaultdict(list)
+    creation_map = {}
+    for pi in self.GetSystemProcessInfo():
+      ppid_map[pi['ParentProcessId']].append(pi['ProcessId'])
+      if pi['CreationDate']:
+        creation_map[pi['ProcessId']] = pi['CreationDate']
+
+    def _InnerGetChildPids(pid):
+      if not pid or pid not in ppid_map:
+        return []
+      ret = [p for p in ppid_map[pid] if creation_map[p] >= creation_map[pid]]
+      for child in ret:
+        if child == pid:
+          continue
+        ret.extend(_InnerGetChildPids(child))
+      return ret
+
+    return _InnerGetChildPids(pid)
+
+  def GetCommandLine(self, pid):
+    for pi in self.GetSystemProcessInfo():
+      if pid == pi['ProcessId']:
+        return pi['CommandLine']
+    raise exceptions.ProcessGoneException()
+
+  @decorators.Cache
+  def GetArchName(self):
+    return platform.machine()
+
+  def GetOSName(self):
+    return 'win'
+
+  @decorators.Cache
+  def GetOSVersionName(self):
+    os_version = platform.uname()[3]
+
+    if os_version.startswith('5.1.'):
+      return os_version_module.XP
+    if os_version.startswith('6.0.'):
+      return os_version_module.VISTA
+    if os_version.startswith('6.1.'):
+      return os_version_module.WIN7
+    # The version of python.exe we commonly use (2.7) is only manifested as
+    # being compatible with Windows versions up to 8. Therefore Windows *lies*
+    # to python about the version number to keep it runnable on Windows 10.
+    key_name = r'Software\Microsoft\Windows NT\CurrentVersion'
+    key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, key_name)
+    try:
+      value, _ = winreg.QueryValueEx(key, 'CurrentMajorVersionNumber')
+    except OSError:
+      value = None
+    finally:
+      key.Close()
+    if value == 10:
+      return os_version_module.WIN10
+    elif os_version.startswith('6.2.'):
+      return os_version_module.WIN8
+    elif os_version.startswith('6.3.'):
+      return os_version_module.WIN81
+    raise NotImplementedError(
+        'Unknown win version: %s, CurrentMajorVersionNumber: %s' %
+        (os_version, value))
+
+  def CanFlushIndividualFilesFromSystemCache(self):
+    return True
+
+  def _GetWin32ProcessInfo(self, func, pid):
+    mask = (win32con.PROCESS_QUERY_INFORMATION |
+            win32con.PROCESS_VM_READ)
+    handle = None
+    try:
+      handle = win32api.OpenProcess(mask, False, pid)
+      return func(handle)
+    except pywintypes.error, e:
+      errcode = e[0]
+      if errcode == 87:
+        raise exceptions.ProcessGoneException()
+      raise
+    finally:
+      if handle:
+        win32api.CloseHandle(handle)
+
+  def _GetPerformanceInfo(self):
+    class PerformanceInfo(ctypes.Structure):
+      """Struct for GetPerformanceInfo() call
+      http://msdn.microsoft.com/en-us/library/ms683210
+      """
+      _fields_ = [('size', ctypes.c_ulong),
+                  ('CommitTotal', ctypes.c_size_t),
+                  ('CommitLimit', ctypes.c_size_t),
+                  ('CommitPeak', ctypes.c_size_t),
+                  ('PhysicalTotal', ctypes.c_size_t),
+                  ('PhysicalAvailable', ctypes.c_size_t),
+                  ('SystemCache', ctypes.c_size_t),
+                  ('KernelTotal', ctypes.c_size_t),
+                  ('KernelPaged', ctypes.c_size_t),
+                  ('KernelNonpaged', ctypes.c_size_t),
+                  ('PageSize', ctypes.c_size_t),
+                  ('HandleCount', ctypes.c_ulong),
+                  ('ProcessCount', ctypes.c_ulong),
+                  ('ThreadCount', ctypes.c_ulong)]
+
+      def __init__(self):
+        self.size = ctypes.sizeof(self)
+        # pylint: disable=bad-super-call
+        super(PerformanceInfo, self).__init__()
+
+    performance_info = PerformanceInfo()
+    ctypes.windll.psapi.GetPerformanceInfo(
+        ctypes.byref(performance_info), performance_info.size)
+    return performance_info
+
+  def IsCurrentProcessElevated(self):
+    if self.GetOSVersionName() < os_version_module.VISTA:
+      # TOKEN_QUERY is not defined before Vista. All processes are elevated.
+      return True
+
+    handle = win32process.GetCurrentProcess()
+    with contextlib.closing(
+        win32security.OpenProcessToken(handle, win32con.TOKEN_QUERY)) as token:
+      return bool(win32security.GetTokenInformation(
+          token, win32security.TokenElevation))
+
+  def LaunchApplication(
+      self, application, parameters=None, elevate_privilege=False):
+    """Launch an application. Returns a PyHANDLE object."""
+
+    parameters = ' '.join(parameters) if parameters else ''
+    if elevate_privilege and not self.IsCurrentProcessElevated():
+      # Use ShellExecuteEx() instead of subprocess.Popen()/CreateProcess() to
+      # elevate privileges. A new console will be created if the new process has
+      # different permissions than this process.
+      proc_info = shell.ShellExecuteEx(
+          fMask=shellcon.SEE_MASK_NOCLOSEPROCESS | shellcon.SEE_MASK_NO_CONSOLE,
+          lpVerb='runas' if elevate_privilege else '',
+          lpFile=application,
+          lpParameters=parameters,
+          nShow=win32con.SW_HIDE)
+      if proc_info['hInstApp'] <= 32:
+        raise Exception('Unable to launch %s' % application)
+      return proc_info['hProcess']
+    else:
+      handle, _, _, _ = win32process.CreateProcess(
+          None, application + ' ' + parameters, None, None, False,
+          win32process.CREATE_NO_WINDOW, None, None, win32process.STARTUPINFO())
+      return handle
+
+  def CanMonitorPower(self):
+    return self._power_monitor.CanMonitorPower()
+
+  def CanMeasurePerApplicationPower(self):
+    return self._power_monitor.CanMeasurePerApplicationPower()
+
+  def StartMonitoringPower(self, browser):
+    self._power_monitor.StartMonitoringPower(browser)
+
+  def StopMonitoringPower(self):
+    return self._power_monitor.StopMonitoringPower()
+
+  def _StartMsrServerIfNeeded(self):
+    if self._msr_server_handle:
+      return
+
+    _InstallWinRing0()
+
+    pipe_name = r"\\.\pipe\msr_server_pipe_{}".format(os.getpid())
+    # Try to open a named pipe to receive a msr port number from server process.
+    pipe = win32pipe.CreateNamedPipe(
+        pipe_name,
+        win32pipe.PIPE_ACCESS_INBOUND,
+        win32pipe.PIPE_TYPE_MESSAGE | win32pipe.PIPE_WAIT,
+        1, 32, 32, 300, None)
+    parameters = (
+        os.path.join(os.path.dirname(__file__), 'msr_server_win.py'),
+        pipe_name,
+    )
+    self._msr_server_handle = self.LaunchApplication(
+        sys.executable, parameters, elevate_privilege=True)
+    if pipe != win32file.INVALID_HANDLE_VALUE:
+      if win32pipe.ConnectNamedPipe(pipe, None) == 0:
+        self._msr_server_port = int(win32file.ReadFile(pipe, 32)[1])
+      win32api.CloseHandle(pipe)
+    # Wait for server to start.
+    try:
+      socket.create_connection(('127.0.0.1', self._msr_server_port), 5).close()
+    except socket.error:
+      self.CloseMsrServer()
+    atexit_with_log.Register(TerminateProcess, self._msr_server_handle)
+
+  def ReadMsr(self, msr_number, start=0, length=64):
+    self._StartMsrServerIfNeeded()
+    if not self._msr_server_handle:
+      raise OSError('Unable to start MSR server.')
+
+    sock = socket.create_connection(('127.0.0.1', self._msr_server_port), 5)
+    try:
+      sock.sendall(struct.pack('I', msr_number))
+      response = sock.recv(8)
+    finally:
+      sock.close()
+    return struct.unpack('Q', response)[0] >> start & ((1 << length) - 1)
+
+  def IsCooperativeShutdownSupported(self):
+    return True
+
+  def CooperativelyShutdown(self, proc, app_name):
+    pid = proc.pid
+
+    # http://timgolden.me.uk/python/win32_how_do_i/
+    #   find-the-window-for-my-subprocess.html
+    #
+    # It seems that intermittently this code manages to find windows
+    # that don't belong to Chrome -- for example, the cmd.exe window
+    # running slave.bat on the tryservers. Try to be careful about
+    # finding only Chrome's windows. This works for both the browser
+    # and content_shell.
+    #
+    # It seems safest to send the WM_CLOSE messages after discovering
+    # all of the sub-process's windows.
+    def find_chrome_windows(hwnd, hwnds):
+      _, win_pid = win32process.GetWindowThreadProcessId(hwnd)
+      if (pid == win_pid and
+          win32gui.IsWindowVisible(hwnd) and
+          win32gui.IsWindowEnabled(hwnd) and
+          win32gui.GetClassName(hwnd).lower().startswith(app_name)):
+        hwnds.append(hwnd)
+      return True
+    hwnds = []
+    win32gui.EnumWindows(find_chrome_windows, hwnds)
+    if hwnds:
+      for hwnd in hwnds:
+        win32gui.SendMessage(hwnd, win32con.WM_CLOSE, 0, 0)
+      return True
+    else:
+      logging.info('Did not find any windows owned by target process')
+    return False
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/__init__.py
new file mode 100644
index 0000000..172bd9e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/__init__.py
@@ -0,0 +1,8 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""
+The PageTestResults hierarchy provides a way of representing the results of
+running the test or measurement on pages.
+"""
+
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/base_test_results_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/base_test_results_unittest.py
new file mode 100644
index 0000000..84e0438
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/base_test_results_unittest.py
@@ -0,0 +1,53 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import sys
+import unittest
+
+from telemetry.core import exceptions
+
+
+class BaseTestResultsUnittest(unittest.TestCase):
+
+  def CreateException(self):
+    try:
+      raise exceptions.IntentionalException
+    except Exception:
+      return sys.exc_info()
+
+  def assertEquals(self, ex, res):
+    # This helps diagnose result mismatches.
+    if ex != res and isinstance(ex, list):
+      def CleanList(l):
+        res = []
+        for x in l:
+          x = x.split('\n')
+          res.extend(x)
+        return res
+      ex = CleanList(ex)
+      res = CleanList(res)
+      max_len = max(len(ex), len(res))
+      max_width = max([len(x) for x in ex + res])
+      max_width = max(10, max_width)
+      print 'Lists differ!'
+      print '%*s | %*s' % (max_width, 'expected', max_width, 'result')
+      for i in range(max_len):
+        if i < len(ex):
+          e = ex[i]
+        else:
+          e = ''
+        if i < len(res):
+          r = res[i]
+        else:
+          r = ''
+        if e != r:
+          sep = '*'
+        else:
+          sep = '|'
+        print '%*s %s %*s' % (max_width, e, sep, max_width, r)
+      print ''
+    if ex != res and isinstance(ex, str) and isinstance(res, str):
+      print 'Strings differ!'
+      print 'exepected:\n%s' % repr(ex)
+      print 'result:\n%s\n' % repr(res)
+    super(BaseTestResultsUnittest, self).assertEquals(ex, res)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/chart_json_output_formatter.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/chart_json_output_formatter.py
new file mode 100644
index 0000000..6a84be1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/chart_json_output_formatter.py
@@ -0,0 +1,114 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import collections
+import itertools
+import json
+
+from telemetry.internal.results import output_formatter
+from telemetry.value import trace
+
+def ResultsAsChartDict(benchmark_metadata, page_specific_values,
+                       summary_values):
+  """Produces a dict for serialization to Chart JSON format from raw values.
+
+  Chart JSON is a transformation of the basic Telemetry JSON format that
+  removes the page map, summarizes the raw values, and organizes the results
+  by chart and trace name. This function takes the key pieces of data needed to
+  perform this transformation (namely, lists of values and a benchmark metadata
+  object) and processes them into a dict which can be serialized using the json
+  module.
+
+  Design doc for schema: http://goo.gl/kOtf1Y
+
+  Args:
+    page_specific_values: list of page-specific values
+    summary_values: list of summary values
+    benchmark_metadata: a benchmark.BenchmarkMetadata object
+
+  Returns:
+    A Chart JSON dict corresponding to the given data.
+  """
+  values = itertools.chain(
+      output_formatter.SummarizePageSpecificValues(page_specific_values),
+      summary_values)
+  charts = collections.defaultdict(dict)
+
+  for value in values:
+    if value.page:
+      chart_name, trace_name = (value.GetChartAndTraceNameForPerPageResult())
+    else:
+      chart_name, trace_name = (
+          value.GetChartAndTraceNameForComputedSummaryResult(None))
+      if chart_name == trace_name:
+        trace_name = 'summary'
+
+    # Dashboard handles the chart_name of trace values specially: it
+    # strips out the field with chart_name 'trace'. Hence in case trace
+    # value has tir_label, we preserve the chart_name.
+    # For relevant section code of dashboard code that handles this, see:
+    # https://github.com/catapult-project/catapult/blob/25e660b/dashboard/dashboard/add_point.py#L199#L216
+    if value.tir_label and not isinstance(value, trace.TraceValue):
+      chart_name = value.tir_label + '@@' + chart_name
+
+    # This intentionally overwrites the trace if it already exists because this
+    # is expected of output from the buildbots currently.
+    # See: crbug.com/413393
+    charts[chart_name][trace_name] = value.AsDict()
+
+  result_dict = {
+    'format_version': '0.1',
+    'next_version': '0.2',
+    # TODO(sullivan): benchmark_name, benchmark_description, and
+    # trace_rerun_options should be removed when incrementing format_version
+    # to 0.1.
+    'benchmark_name': benchmark_metadata.name,
+    'benchmark_description': benchmark_metadata.description,
+    'trace_rerun_options': benchmark_metadata.rerun_options,
+    'benchmark_metadata': benchmark_metadata.AsDict(),
+    'charts': charts,
+    # Need to add this in for compatibility with disabled chartjson results.
+    'enabled': True
+  }
+
+  return result_dict
+
+
+def DisabledResultsDict(benchmark_name):
+  """Produces a dict for serialization to Chart JSON when a benchmark is
+    disabled.
+
+  Args:
+    benchmark_name: name of the disabled benchmark
+
+  Returns:
+    A Chart JSON dict corresponding to a disabled benchmark.
+  """
+  result_dict = {
+    'benchmark_name': benchmark_name,
+    'enabled': False
+  }
+
+  return result_dict
+
+
+# TODO(eakuefner): Transition this to translate Telemetry JSON.
+class ChartJsonOutputFormatter(output_formatter.OutputFormatter):
+  def __init__(self, output_stream, benchmark_metadata):
+    super(ChartJsonOutputFormatter, self).__init__(output_stream)
+    self._benchmark_metadata = benchmark_metadata
+
+  def FormatDisabled(self):
+    self._Dump(DisabledResultsDict(self._benchmark_metadata.name))
+
+  def Format(self, page_test_results):
+    self._Dump(ResultsAsChartDict(
+      self._benchmark_metadata,
+      page_test_results.all_page_specific_values,
+      page_test_results.all_summary_values))
+
+  def _Dump(self, results):
+    json.dump(results, self.output_stream, indent=2,
+      separators=(',', ': '))
+    self.output_stream.write('\n')
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/chart_json_output_formatter_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/chart_json_output_formatter_unittest.py
new file mode 100644
index 0000000..df56b0c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/chart_json_output_formatter_unittest.py
@@ -0,0 +1,229 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import json
+import os
+import StringIO
+import unittest
+
+from telemetry import benchmark
+from telemetry import story
+from telemetry.internal.results import chart_json_output_formatter
+from telemetry.internal.results import page_test_results
+from telemetry import page as page_module
+from telemetry.value import improvement_direction
+from telemetry.value import list_of_scalar_values
+from telemetry.value import scalar
+from telemetry.value import trace
+from tracing.trace_data import trace_data
+
+
+def _MakeStorySet():
+  ps = story.StorySet(base_dir=os.path.dirname(__file__))
+  ps.AddStory(page_module.Page('http://www.foo.com/', ps, ps.base_dir))
+  ps.AddStory(page_module.Page('http://www.bar.com/', ps, ps.base_dir))
+  return ps
+
+class ChartJsonTest(unittest.TestCase):
+  def setUp(self):
+    self._output = StringIO.StringIO()
+    self._story_set = _MakeStorySet()
+    self._benchmark_metadata = benchmark.BenchmarkMetadata(
+        'benchmark_name', 'benchmark_description')
+    self._formatter = chart_json_output_formatter.ChartJsonOutputFormatter(
+        self._output, self._benchmark_metadata)
+
+  def testOutputAndParse(self):
+    results = page_test_results.PageTestResults()
+
+    self._output.truncate(0)
+
+    results.WillRunPage(self._story_set[0])
+    v0 = scalar.ScalarValue(results.current_page, 'foo', 'seconds', 3,
+                            improvement_direction=improvement_direction.DOWN)
+    results.AddValue(v0)
+    results.DidRunPage(self._story_set[0])
+
+    self._formatter.Format(results)
+    d = json.loads(self._output.getvalue())
+    self.assertIn('foo', d['charts'])
+
+  def testOutputAndParseDisabled(self):
+    self._formatter.FormatDisabled()
+    d = json.loads(self._output.getvalue())
+    self.assertEquals(d['benchmark_name'], 'benchmark_name')
+    self.assertFalse(d['enabled'])
+
+  def testAsChartDictSerializable(self):
+    v0 = scalar.ScalarValue(self._story_set[0], 'foo', 'seconds', 3,
+                            improvement_direction=improvement_direction.DOWN)
+    page_specific_values = [v0]
+    summary_values = []
+
+    d = chart_json_output_formatter.ResultsAsChartDict(
+        self._benchmark_metadata,
+        page_specific_values,
+        summary_values)
+    json.dumps(d)
+
+  def testAsChartDictBaseKeys(self):
+    page_specific_values = []
+    summary_values = []
+
+    d = chart_json_output_formatter.ResultsAsChartDict(
+        self._benchmark_metadata,
+        page_specific_values,
+        summary_values)
+
+    self.assertEquals(d['format_version'], '0.1')
+    self.assertEquals(d['next_version'], '0.2')
+    self.assertEquals(d['benchmark_metadata']['name'], 'benchmark_name')
+    self.assertEquals(d['benchmark_metadata']['description'],
+                      'benchmark_description')
+    self.assertEquals(d['benchmark_metadata']['type'], 'telemetry_benchmark')
+    self.assertTrue(d['enabled'])
+
+  def testAsChartDictNoDescription(self):
+    page_specific_values = []
+    summary_values = []
+
+    d = chart_json_output_formatter.ResultsAsChartDict(
+        benchmark.BenchmarkMetadata('benchmark_name', ''),
+        page_specific_values,
+        summary_values)
+
+    self.assertEquals('', d['benchmark_metadata']['description'])
+
+  def testAsChartDictPageSpecificValuesSamePageWithInteractionRecord(self):
+    v0 = scalar.ScalarValue(self._story_set[0], 'foo', 'seconds', 3,
+                            improvement_direction=improvement_direction.DOWN,
+                            tir_label='MyIR')
+    v1 = scalar.ScalarValue(self._story_set[0], 'foo', 'seconds', 4,
+                            improvement_direction=improvement_direction.DOWN,
+                            tir_label='MyIR')
+    page_specific_values = [v0, v1]
+    summary_values = []
+
+    d = chart_json_output_formatter.ResultsAsChartDict(
+        self._benchmark_metadata,
+        page_specific_values,
+        summary_values)
+
+    self.assertTrue('MyIR@@foo' in d['charts'])
+    self.assertTrue('http://www.foo.com/' in d['charts']['MyIR@@foo'])
+    self.assertTrue(d['enabled'])
+
+  def testAsChartDictPageSpecificValuesSamePageWithoutInteractionRecord(self):
+    v0 = scalar.ScalarValue(self._story_set[0], 'foo', 'seconds', 3,
+                            improvement_direction=improvement_direction.DOWN)
+    v1 = scalar.ScalarValue(self._story_set[0], 'foo', 'seconds', 4,
+                            improvement_direction=improvement_direction.DOWN)
+    page_specific_values = [v0, v1]
+    summary_values = []
+
+    d = chart_json_output_formatter.ResultsAsChartDict(
+        self._benchmark_metadata,
+        page_specific_values,
+        summary_values)
+
+    self.assertTrue('foo' in d['charts'])
+    self.assertTrue('http://www.foo.com/' in d['charts']['foo'])
+    self.assertTrue(d['enabled'])
+
+  def testAsChartDictPageSpecificValuesAndComputedSummaryWithTraceName(self):
+    v0 = scalar.ScalarValue(self._story_set[0], 'foo.bar', 'seconds', 3,
+                            improvement_direction=improvement_direction.DOWN)
+    v1 = scalar.ScalarValue(self._story_set[1], 'foo.bar', 'seconds', 4,
+                            improvement_direction=improvement_direction.DOWN)
+    page_specific_values = [v0, v1]
+    summary_values = []
+
+    d = chart_json_output_formatter.ResultsAsChartDict(
+        self._benchmark_metadata,
+        page_specific_values,
+        summary_values)
+
+    self.assertTrue('foo' in d['charts'])
+    self.assertTrue('http://www.foo.com/' in d['charts']['foo'])
+    self.assertTrue('http://www.bar.com/' in d['charts']['foo'])
+    self.assertTrue('bar' in d['charts']['foo'])
+    self.assertTrue(d['enabled'])
+
+  def testAsChartDictPageSpecificValuesAndComputedSummaryWithoutTraceName(self):
+    v0 = scalar.ScalarValue(self._story_set[0], 'foo', 'seconds', 3,
+                            improvement_direction=improvement_direction.DOWN)
+    v1 = scalar.ScalarValue(self._story_set[1], 'foo', 'seconds', 4,
+                            improvement_direction=improvement_direction.DOWN)
+    page_specific_values = [v0, v1]
+    summary_values = []
+
+    d = chart_json_output_formatter.ResultsAsChartDict(
+        self._benchmark_metadata,
+        page_specific_values,
+        summary_values)
+
+    self.assertTrue('foo' in d['charts'])
+    self.assertTrue('http://www.foo.com/' in d['charts']['foo'])
+    self.assertTrue('http://www.bar.com/' in d['charts']['foo'])
+    self.assertTrue('summary' in d['charts']['foo'])
+    self.assertTrue(d['enabled'])
+
+  def testAsChartDictSummaryValueWithTraceName(self):
+    v0 = list_of_scalar_values.ListOfScalarValues(
+        None, 'foo.bar', 'seconds', [3, 4],
+        improvement_direction=improvement_direction.DOWN)
+    page_specific_values = []
+    summary_values = [v0]
+
+    d = chart_json_output_formatter.ResultsAsChartDict(
+        self._benchmark_metadata,
+        page_specific_values,
+        summary_values)
+
+    self.assertTrue('bar' in d['charts']['foo'])
+    self.assertTrue(d['enabled'])
+
+  def testAsChartDictSummaryValueWithoutTraceName(self):
+    v0 = list_of_scalar_values.ListOfScalarValues(
+        None, 'foo', 'seconds', [3, 4],
+        improvement_direction=improvement_direction.DOWN)
+    page_specific_values = []
+    summary_values = [v0]
+
+    d = chart_json_output_formatter.ResultsAsChartDict(
+        self._benchmark_metadata,
+        page_specific_values,
+        summary_values)
+
+    self.assertTrue('summary' in d['charts']['foo'])
+    self.assertTrue(d['enabled'])
+
+  def testAsChartDictWithTraceValuesThatHasTirLabel(self):
+    v = trace.TraceValue(self._story_set[0],
+                         trace_data.CreateTraceDataFromRawData([{'test': 1}]))
+    v.tir_label = 'background'
+
+    d = chart_json_output_formatter.ResultsAsChartDict(
+        self._benchmark_metadata,
+        page_specific_values=[v],
+        summary_values=[v])
+
+    self.assertTrue('trace' in d['charts'])
+    self.assertTrue('http://www.foo.com/' in d['charts']['trace'],
+                    msg=d['charts']['trace'])
+    self.assertTrue(d['enabled'])
+
+  def testAsChartDictValueSmokeTest(self):
+    v0 = list_of_scalar_values.ListOfScalarValues(
+        None, 'foo.bar', 'seconds', [3, 4],
+        improvement_direction=improvement_direction.DOWN)
+    page_specific_values = []
+    summary_values = [v0]
+
+    d = chart_json_output_formatter.ResultsAsChartDict(
+        self._benchmark_metadata,
+        page_specific_values,
+        summary_values)
+
+    self.assertEquals(d['charts']['foo']['bar']['values'], [3, 4])
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/csv_pivot_table_output_formatter.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/csv_pivot_table_output_formatter.py
new file mode 100644
index 0000000..43fb973
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/csv_pivot_table_output_formatter.py
@@ -0,0 +1,63 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import csv
+
+from telemetry.internal.results import output_formatter
+from telemetry.value import scalar
+from telemetry.value import trace
+
+
+class CsvPivotTableOutputFormatter(output_formatter.OutputFormatter):
+  """Output the results as CSV suitable for reading into a spreadsheet.
+
+  This will write a header row, and one row for each value. Each value row
+  contains the value and unit, identifies the value (story_set, page, name), and
+  (optionally) data from --output-trace-tag. This format matches what
+  spreadsheet programs expect as input for a "pivot table".
+
+  A trace tag (--output-trace-tag) can be used to tag each value, to allow
+  easy combination of the resulting CSVs from several runs.
+  If the trace_tag contains a comma, it will be written as several
+  comma-separated values.
+
+  This class only processes scalar values.
+  """
+
+  FIELDS = ['story_set', 'page', 'name', 'value', 'units', 'run_index']
+
+  def __init__(self, output_stream, trace_tag=''):
+    super(CsvPivotTableOutputFormatter, self).__init__(output_stream)
+    self._trace_tag = trace_tag
+
+  def Format(self, page_test_results):
+    csv_writer = csv.writer(self.output_stream)
+
+    # Observe trace_tag. Use comma to split up the trace tag.
+    tag_values = self._trace_tag.split(',')
+    tag_values = [x for x in tag_values if x] # filter empty list entries
+    tag_headers = ['trace_tag_%d' % i for i in range(len(tag_values))]
+
+    # Write header.
+    csv_writer.writerow(self.FIELDS + tag_headers)
+
+    # Write all values. Each row contains a value + page-level metadata.
+    for run in page_test_results.all_page_runs:
+      run_index = page_test_results.all_page_runs.index(run)
+      page_dict = {
+          'page': run.story.display_name,
+          'story_set': run.story.page_set.Name(),
+          'run_index': run_index,
+      }
+      for value in run.values:
+        if (isinstance(value, scalar.ScalarValue) or
+            isinstance(value, trace.TraceValue)):
+          value_dict = {
+            'name': value.name,
+            'value': value.value,
+            'units': value.units,
+          }
+          value_dict.update(page_dict.items())
+          csv_writer.writerow(
+              [value_dict[field] for field in self.FIELDS] + tag_values)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/csv_pivot_table_output_formatter_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/csv_pivot_table_output_formatter_unittest.py
new file mode 100644
index 0000000..1ada569
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/csv_pivot_table_output_formatter_unittest.py
@@ -0,0 +1,125 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import os
+import StringIO
+import unittest
+
+import mock
+
+from telemetry import story
+from telemetry.internal.results import csv_pivot_table_output_formatter
+from telemetry.internal.results import page_test_results
+from telemetry import page as page_module
+from telemetry.value import improvement_direction
+from telemetry.value import scalar
+from telemetry.value import trace
+from tracing.trace_data import trace_data
+
+
+def _MakeStorySet():
+  story_set = story.StorySet(base_dir=os.path.dirname(__file__))
+  story_set.AddStory(
+      page_module.Page('http://www.foo.com/', story_set, story_set.base_dir))
+  story_set.AddStory(
+      page_module.Page('http://www.bar.com/', story_set, story_set.base_dir))
+  return story_set
+
+
+class CsvPivotTableOutputFormatterTest(unittest.TestCase):
+
+  # The line separator used by CSV formatter.
+  _LINE_SEPARATOR = '\r\n'
+
+  def setUp(self):
+    self._output = StringIO.StringIO()
+    self._story_set = _MakeStorySet()
+    self._results = page_test_results.PageTestResults()
+    self._formatter = None
+    self.MakeFormatter()
+
+  def MakeFormatter(self, trace_tag=''):
+    self._formatter = (
+        csv_pivot_table_output_formatter.CsvPivotTableOutputFormatter(
+            self._output, trace_tag))
+
+  def SimulateBenchmarkRun(self, list_of_page_and_values):
+    """Simulate one run of a benchmark, using the supplied values.
+
+    Args:
+      list_of_pages_and_values: a list of tuple (page, list of values)
+    """
+    for page, values in list_of_page_and_values:
+      self._results.WillRunPage(page)
+      for v in values:
+        v.page = page
+        self._results.AddValue(v)
+      self._results.DidRunPage(page)
+
+  def Format(self):
+    self._formatter.Format(self._results)
+    return self._output.getvalue()
+
+  def testSimple(self):
+    # Test a simple benchmark with only one value:
+    self.SimulateBenchmarkRun([
+        (self._story_set[0], [scalar.ScalarValue(
+            None, 'foo', 'seconds', 3,
+            improvement_direction=improvement_direction.DOWN)])])
+    expected = self._LINE_SEPARATOR.join([
+        'story_set,page,name,value,units,run_index',
+        'story_set,http://www.foo.com/,foo,3,seconds,0',
+        ''])
+
+    self.assertEqual(expected, self.Format())
+
+  @mock.patch('py_utils.cloud_storage.Insert')
+  def testMultiplePagesAndValues(self, cs_insert_mock):
+    cs_insert_mock.return_value = 'https://cloud_storage_url/foo'
+    trace_value = trace.TraceValue(
+        None, trace_data.CreateTraceDataFromRawData('{"traceEvents": []}'))
+    trace_value.UploadToCloud(bucket='foo')
+    self.SimulateBenchmarkRun([
+        (self._story_set[0], [
+            scalar.ScalarValue(
+              None, 'foo', 'seconds', 4,
+              improvement_direction=improvement_direction.DOWN)]),
+        (self._story_set[1], [
+            scalar.ScalarValue(
+                None, 'foo', 'seconds', 3.4,
+                improvement_direction=improvement_direction.DOWN),
+            trace_value,
+            scalar.ScalarValue(
+                None, 'bar', 'km', 10,
+                improvement_direction=improvement_direction.DOWN),
+            scalar.ScalarValue(
+                None, 'baz', 'count', 5,
+                improvement_direction=improvement_direction.DOWN)])])
+
+    # Parse CSV output into list of lists.
+    csv_string = self.Format()
+    lines = csv_string.split(self._LINE_SEPARATOR)
+    values = [s.split(',') for s in lines[1:-1]]
+
+    self.assertEquals(len(values), 5)  # We expect 5 value in total.
+    self.assertEquals(len(set((v[1] for v in values))), 2)  # 2 pages.
+    self.assertEquals(len(set((v[2] for v in values))), 4)  # 4 value names.
+    self.assertEquals(values[2],
+        ['story_set', 'http://www.bar.com/', 'trace',
+         'https://cloud_storage_url/foo', '', '1'])
+
+  def testTraceTag(self):
+    self.MakeFormatter(trace_tag='date,option')
+    self.SimulateBenchmarkRun([
+        (self._story_set[0], [
+            scalar.ScalarValue(
+                None, 'foo', 'seconds', 3,
+                improvement_direction=improvement_direction.DOWN),
+            scalar.ScalarValue(
+                None, 'bar', 'tons', 5,
+                improvement_direction=improvement_direction.DOWN)])])
+    output = self.Format().split(self._LINE_SEPARATOR)
+
+    self.assertTrue(output[0].endswith(',trace_tag_0,trace_tag_1'))
+    for line in output[1:-1]:
+      self.assertTrue(line.endswith(',date,option'))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/gtest_progress_reporter.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/gtest_progress_reporter.py
new file mode 100644
index 0000000..75e6864
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/gtest_progress_reporter.py
@@ -0,0 +1,106 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import time
+
+from telemetry.internal.results import progress_reporter
+from telemetry.value import failure
+from telemetry.value import skip
+
+
+class GTestProgressReporter(progress_reporter.ProgressReporter):
+  """A progress reporter that outputs the progress report in gtest style.
+
+  Be careful each print should only handle one string. Otherwise, the output
+  might be interrupted by Chrome logging, and the output interpretation might
+  be incorrect. For example:
+      print >> self._output_stream, "[ OK ]", testname
+  should be written as
+      print >> self._output_stream, "[ OK ] %s" % testname
+  """
+
+  def __init__(self, output_stream, output_skipped_tests_summary=False):
+    super(GTestProgressReporter, self).__init__()
+    self._output_stream = output_stream
+    self._timestamp = None
+    self._output_skipped_tests_summary = output_skipped_tests_summary
+
+  def _GetMs(self):
+    assert self._timestamp is not None, 'Did not call WillRunPage.'
+    return (time.time() - self._timestamp) * 1000
+
+  def _GenerateGroupingKeyString(self, page):
+    return '' if not page.grouping_keys else '@%s' % str(page.grouping_keys)
+
+  def DidAddValue(self, value):
+    super(GTestProgressReporter, self).DidAddValue(value)
+    if isinstance(value, failure.FailureValue):
+      print >> self._output_stream, failure.GetStringFromExcInfo(
+          value.exc_info)
+      self._output_stream.flush()
+    elif isinstance(value, skip.SkipValue):
+      print >> self._output_stream, '===== SKIPPING TEST %s: %s =====' % (
+          value.page.display_name, value.reason)
+    # TODO(chrishenry): Consider outputting metric values as well. For
+    # e.g., it can replace BuildbotOutputFormatter in
+    # --output-format=html, which we used only so that users can grep
+    # the results without opening results.html.
+
+  def WillRunPage(self, page_test_results):
+    super(GTestProgressReporter, self).WillRunPage(page_test_results)
+    print >> self._output_stream, '[ RUN      ] %s%s' % (
+        page_test_results.current_page.display_name,
+        self._GenerateGroupingKeyString(page_test_results.current_page))
+
+    self._output_stream.flush()
+    self._timestamp = time.time()
+
+  def DidRunPage(self, page_test_results):
+    super(GTestProgressReporter, self).DidRunPage(page_test_results)
+    page = page_test_results.current_page
+    if page_test_results.current_page_run.failed:
+      print >> self._output_stream, '[  FAILED  ] %s%s (%0.f ms)' % (
+          page.display_name,
+          self._GenerateGroupingKeyString(page_test_results.current_page),
+          self._GetMs())
+    else:
+      print >> self._output_stream, '[       OK ] %s%s (%0.f ms)' % (
+          page.display_name,
+          self._GenerateGroupingKeyString(page_test_results.current_page),
+          self._GetMs())
+    self._output_stream.flush()
+
+  def DidFinishAllTests(self, page_test_results):
+    super(GTestProgressReporter, self).DidFinishAllTests(page_test_results)
+    successful_runs = []
+    failed_runs = []
+    for run in page_test_results.all_page_runs:
+      if run.failed:
+        failed_runs.append(run)
+      else:
+        successful_runs.append(run)
+
+    unit = 'test' if len(successful_runs) == 1 else 'tests'
+    print >> self._output_stream, '[  PASSED  ] %d %s.' % (
+        (len(successful_runs), unit))
+    if len(failed_runs) > 0:
+      unit = 'test' if len(failed_runs) == 1 else 'tests'
+      print >> self._output_stream, '[  FAILED  ] %d %s, listed below:' % (
+          (len(page_test_results.failures), unit))
+      for failed_run in failed_runs:
+        print >> self._output_stream, '[  FAILED  ]  %s%s' % (
+            failed_run.story.display_name,
+            self._GenerateGroupingKeyString(failed_run.story))
+      print >> self._output_stream
+      count = len(failed_runs)
+      unit = 'TEST' if count == 1 else 'TESTS'
+      print >> self._output_stream, '%d FAILED %s' % (count, unit)
+    print >> self._output_stream
+
+    if self._output_skipped_tests_summary:
+      if len(page_test_results.skipped_values) > 0:
+        print >> self._output_stream, 'Skipped pages:\n%s\n' % ('\n'.join(
+            v.page.display_name for v in page_test_results.skipped_values))
+
+    self._output_stream.flush()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/gtest_progress_reporter_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/gtest_progress_reporter_unittest.py
new file mode 100644
index 0000000..a6d0d5d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/gtest_progress_reporter_unittest.py
@@ -0,0 +1,250 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import traceback
+
+from telemetry import story
+from telemetry.internal.results import base_test_results_unittest
+from telemetry.internal.results import gtest_progress_reporter
+from telemetry.internal.results import page_test_results
+from telemetry import page as page_module
+from telemetry.testing import fakes
+from telemetry.testing import stream
+from telemetry.value import failure
+from telemetry.value import skip
+
+
+_GROUPING_KEY_DEFAULT = {'1': '2'}
+
+
+def _MakeStorySet():
+  story_set = story.StorySet(base_dir=os.path.dirname(__file__))
+  story_set.AddStory(
+      page_module.Page('http://www.foo.com/', story_set, story_set.base_dir))
+  story_set.AddStory(
+      page_module.Page('http://www.bar.com/', story_set, story_set.base_dir))
+  story_set.AddStory(
+      page_module.Page('http://www.baz.com/', story_set, story_set.base_dir))
+  story_set.AddStory(
+      page_module.Page('http://www.roz.com/', story_set, story_set.base_dir))
+  story_set.AddStory(
+      page_module.Page('http://www.fus.com/', story_set, story_set.base_dir,
+                       grouping_keys=_GROUPING_KEY_DEFAULT))
+  story_set.AddStory(
+      page_module.Page('http://www.ro.com/', story_set, story_set.base_dir,
+                       grouping_keys=_GROUPING_KEY_DEFAULT))
+  return story_set
+
+
+class GTestProgressReporterTest(
+    base_test_results_unittest.BaseTestResultsUnittest):
+
+  def setUp(self):
+    super(GTestProgressReporterTest, self).setUp()
+    self._fake_timer = fakes.FakeTimer(gtest_progress_reporter)
+
+    self._output_stream = stream.TestOutputStream()
+    self._reporter = gtest_progress_reporter.GTestProgressReporter(
+        self._output_stream)
+
+  def tearDown(self):
+    self._fake_timer.Restore()
+
+  def testSingleSuccessPage(self):
+    test_story_set = _MakeStorySet()
+
+    results = page_test_results.PageTestResults(
+        progress_reporter=self._reporter)
+    results.WillRunPage(test_story_set.stories[0])
+    self._fake_timer.SetTime(0.007)
+    results.DidRunPage(test_story_set.stories[0])
+
+    results.PrintSummary()
+    expected = ('[ RUN      ] http://www.foo.com/\n'
+                '[       OK ] http://www.foo.com/ (7 ms)\n'
+                '[  PASSED  ] 1 test.\n\n')
+    self.assertEquals(expected, ''.join(self._output_stream.output_data))
+
+  def testSingleSuccessPageWithGroupingKeys(self):
+    test_story_set = _MakeStorySet()
+
+    results = page_test_results.PageTestResults(
+        progress_reporter=self._reporter)
+    results.WillRunPage(test_story_set.stories[4])
+    self._fake_timer.SetTime(0.007)
+    results.DidRunPage(test_story_set.stories[4])
+
+    results.PrintSummary()
+    expected = ("[ RUN      ] http://www.fus.com/@{'1': '2'}\n"
+                "[       OK ] http://www.fus.com/@{'1': '2'} (7 ms)\n"
+                "[  PASSED  ] 1 test.\n\n")
+    self.assertEquals(expected, ''.join(self._output_stream.output_data))
+
+  def testSingleFailedPage(self):
+    test_story_set = _MakeStorySet()
+
+    results = page_test_results.PageTestResults(
+        progress_reporter=self._reporter)
+    results.WillRunPage(test_story_set.stories[0])
+    exc_info = self.CreateException()
+    results.AddValue(failure.FailureValue(test_story_set.stories[0], exc_info))
+    results.DidRunPage(test_story_set.stories[0])
+
+    results.PrintSummary()
+    exception_trace = ''.join(traceback.format_exception(*exc_info))
+    expected = ('[ RUN      ] http://www.foo.com/\n'
+                '%s\n'
+                '[  FAILED  ] http://www.foo.com/ (0 ms)\n'
+                '[  PASSED  ] 0 tests.\n'
+                '[  FAILED  ] 1 test, listed below:\n'
+                '[  FAILED  ]  http://www.foo.com/\n\n'
+                '1 FAILED TEST\n\n' % exception_trace)
+    self.assertEquals(expected, ''.join(self._output_stream.output_data))
+
+  def testSingleFailedPageWithGroupingKeys(self):
+    test_story_set = _MakeStorySet()
+
+    results = page_test_results.PageTestResults(
+        progress_reporter=self._reporter)
+    results.WillRunPage(test_story_set.stories[4])
+    exc_info = self.CreateException()
+    results.AddValue(failure.FailureValue(test_story_set.stories[4], exc_info))
+    results.DidRunPage(test_story_set.stories[4])
+
+    results.PrintSummary()
+    exception_trace = ''.join(traceback.format_exception(*exc_info))
+    expected = ("[ RUN      ] http://www.fus.com/@{'1': '2'}\n"
+                "%s\n"
+                "[  FAILED  ] http://www.fus.com/@{'1': '2'} (0 ms)\n"
+                "[  PASSED  ] 0 tests.\n"
+                "[  FAILED  ] 1 test, listed below:\n"
+                "[  FAILED  ]  http://www.fus.com/@{'1': '2'}\n\n"
+                "1 FAILED TEST\n\n" % exception_trace)
+    self.assertEquals(expected, ''.join(self._output_stream.output_data))
+
+  def testSingleSkippedPage(self):
+    test_story_set = _MakeStorySet()
+    results = page_test_results.PageTestResults(
+        progress_reporter=self._reporter)
+    results.WillRunPage(test_story_set.stories[0])
+    self._fake_timer.SetTime(0.007)
+    results.AddValue(skip.SkipValue(test_story_set.stories[0],
+        'Page skipped for testing reason'))
+    results.DidRunPage(test_story_set.stories[0])
+
+    results.PrintSummary()
+    expected = ('[ RUN      ] http://www.foo.com/\n'
+                '===== SKIPPING TEST http://www.foo.com/:'
+                ' Page skipped for testing reason =====\n'
+                '[       OK ] http://www.foo.com/ (7 ms)\n'
+                '[  PASSED  ] 1 test.\n\n')
+    self.assertEquals(expected, ''.join(self._output_stream.output_data))
+
+  def testPassAndFailedPages(self):
+    test_story_set = _MakeStorySet()
+    results = page_test_results.PageTestResults(
+        progress_reporter=self._reporter)
+    exc_info = self.CreateException()
+
+    results.WillRunPage(test_story_set.stories[0])
+    self._fake_timer.SetTime(0.007)
+    results.DidRunPage(test_story_set.stories[0])
+
+    results.WillRunPage(test_story_set.stories[1])
+    self._fake_timer.SetTime(0.009)
+    results.AddValue(failure.FailureValue(test_story_set.stories[1], exc_info))
+    results.DidRunPage(test_story_set.stories[1])
+
+    results.WillRunPage(test_story_set.stories[2])
+    self._fake_timer.SetTime(0.015)
+    results.AddValue(failure.FailureValue(test_story_set.stories[2], exc_info))
+    results.DidRunPage(test_story_set.stories[2])
+
+    results.WillRunPage(test_story_set.stories[3])
+    self._fake_timer.SetTime(0.020)
+    results.DidRunPage(test_story_set.stories[3])
+
+    results.WillRunPage(test_story_set.stories[4])
+    self._fake_timer.SetTime(0.025)
+    results.DidRunPage(test_story_set.stories[4])
+
+    results.WillRunPage(test_story_set.stories[5])
+    self._fake_timer.SetTime(0.030)
+    results.AddValue(failure.FailureValue(test_story_set.stories[5], exc_info))
+    results.DidRunPage(test_story_set.stories[5])
+
+    results.PrintSummary()
+    exception_trace = ''.join(traceback.format_exception(*exc_info))
+    expected = ("[ RUN      ] http://www.foo.com/\n"
+                "[       OK ] http://www.foo.com/ (7 ms)\n"
+                "[ RUN      ] http://www.bar.com/\n"
+                "%s\n"
+                "[  FAILED  ] http://www.bar.com/ (2 ms)\n"
+                "[ RUN      ] http://www.baz.com/\n"
+                "%s\n"
+                "[  FAILED  ] http://www.baz.com/ (6 ms)\n"
+                "[ RUN      ] http://www.roz.com/\n"
+                "[       OK ] http://www.roz.com/ (5 ms)\n"
+                "[ RUN      ] http://www.fus.com/@{'1': '2'}\n"
+                "[       OK ] http://www.fus.com/@{'1': '2'} (5 ms)\n"
+                "[ RUN      ] http://www.ro.com/@{'1': '2'}\n"
+                "%s\n"
+                "[  FAILED  ] http://www.ro.com/@{'1': '2'} (5 ms)\n"
+                "[  PASSED  ] 3 tests.\n"
+                "[  FAILED  ] 3 tests, listed below:\n"
+                "[  FAILED  ]  http://www.bar.com/\n"
+                "[  FAILED  ]  http://www.baz.com/\n"
+                "[  FAILED  ]  http://www.ro.com/@{'1': '2'}\n\n"
+                "3 FAILED TESTS\n\n"
+                % (exception_trace, exception_trace, exception_trace))
+    self.assertEquals(expected, ''.join(self._output_stream.output_data))
+
+  def testStreamingResults(self):
+    test_story_set = _MakeStorySet()
+    results = page_test_results.PageTestResults(
+        progress_reporter=self._reporter)
+    exc_info = self.CreateException()
+
+    results.WillRunPage(test_story_set.stories[0])
+    self._fake_timer.SetTime(0.007)
+    results.DidRunPage(test_story_set.stories[0])
+    expected = ('[ RUN      ] http://www.foo.com/\n'
+                '[       OK ] http://www.foo.com/ (7 ms)\n')
+    self.assertEquals(expected, ''.join(self._output_stream.output_data))
+
+    results.WillRunPage(test_story_set.stories[1])
+    self._fake_timer.SetTime(0.009)
+    exception_trace = ''.join(traceback.format_exception(*exc_info))
+    results.AddValue(failure.FailureValue(test_story_set.stories[1], exc_info))
+    results.DidRunPage(test_story_set.stories[1])
+    expected = ('[ RUN      ] http://www.foo.com/\n'
+                '[       OK ] http://www.foo.com/ (7 ms)\n'
+                '[ RUN      ] http://www.bar.com/\n'
+                '%s\n'
+                '[  FAILED  ] http://www.bar.com/ (2 ms)\n' % exception_trace)
+
+  def testOutputSkipInformation(self):
+    test_story_set = _MakeStorySet()
+    self._reporter = gtest_progress_reporter.GTestProgressReporter(
+        self._output_stream, output_skipped_tests_summary=True)
+    results = page_test_results.PageTestResults(
+        progress_reporter=self._reporter)
+    results.WillRunPage(test_story_set.stories[0])
+    self._fake_timer.SetTime(0.007)
+    results.AddValue(skip.SkipValue(test_story_set.stories[0],
+        'Page skipped for testing reason'))
+    results.DidRunPage(test_story_set.stories[0])
+
+    results.PrintSummary()
+    expected = ('[ RUN      ] http://www.foo.com/\n'
+                '===== SKIPPING TEST http://www.foo.com/:'
+                ' Page skipped for testing reason =====\n'
+                '[       OK ] http://www.foo.com/ (7 ms)\n'
+                '[  PASSED  ] 1 test.\n'
+                '\n'
+                'Skipped pages:\n'
+                'http://www.foo.com/\n'
+                '\n')
+    self.assertEquals(expected, ''.join(self._output_stream.output_data))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/histogram_set_json_output_formatter.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/histogram_set_json_output_formatter.py
new file mode 100644
index 0000000..6c6f397
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/histogram_set_json_output_formatter.py
@@ -0,0 +1,28 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import json
+import logging
+
+from telemetry.internal.results import output_formatter
+
+
+class HistogramSetJsonOutputFormatter(output_formatter.OutputFormatter):
+  def __init__(self, output_stream, metadata, reset_results):
+    super(HistogramSetJsonOutputFormatter, self).__init__(output_stream)
+    self._metadata = metadata
+    self._reset_results = reset_results
+
+  def Format(self, page_test_results):
+    histograms = page_test_results.AsHistogramDicts(self._metadata)
+    self._output_stream.seek(0)
+    if not self._reset_results:
+      existing = self._output_stream.read()
+      self._output_stream.seek(0)
+      if existing:
+        try:
+          histograms += json.loads(existing)
+        except ValueError:
+          logging.warn('Found existing histograms json but failed to parse it.')
+    json.dump(histograms, self._output_stream)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/html_output_formatter.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/html_output_formatter.py
new file mode 100644
index 0000000..1e12965
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/html_output_formatter.py
@@ -0,0 +1,66 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import datetime
+import json
+import logging
+import os
+import tempfile
+
+from py_utils import cloud_storage
+
+from telemetry.internal.results import chart_json_output_formatter
+from telemetry.internal.results import output_formatter
+
+from tracing import results_renderer
+from tracing.value import convert_chart_json
+
+
+class HtmlOutputFormatter(output_formatter.OutputFormatter):
+  def __init__(self, output_stream, metadata, reset_results,
+               upload_bucket=None):
+    super(HtmlOutputFormatter, self).__init__(output_stream)
+    self._metadata = metadata
+    self._upload_bucket = upload_bucket
+    self._reset_results = reset_results
+
+  def _ConvertChartJson(self, page_test_results):
+    chart_json = chart_json_output_formatter.ResultsAsChartDict(
+        self._metadata, page_test_results.all_page_specific_values,
+        page_test_results.all_summary_values)
+    info = page_test_results.telemetry_info
+    chart_json['label'] = info.label
+    chart_json['benchmarkStartMs'] = info.benchmark_start_ms
+
+    file_descriptor, chart_json_path = tempfile.mkstemp()
+    os.close(file_descriptor)
+    json.dump(chart_json, file(chart_json_path, 'w'))
+
+    vinn_result = convert_chart_json.ConvertChartJson(chart_json_path)
+
+    os.remove(chart_json_path)
+
+    if vinn_result.returncode != 0:
+      logging.error('Error converting chart json to Histograms:\n' +
+          vinn_result.stdout)
+      return []
+    return json.loads(vinn_result.stdout)
+
+  def Format(self, page_test_results):
+    histograms = page_test_results.value_set
+    if not histograms:
+      histograms = self._ConvertChartJson(page_test_results)
+
+    results_renderer.RenderHTMLView(histograms,
+        self._output_stream, self._reset_results)
+    file_path = os.path.abspath(self._output_stream.name)
+    if self._upload_bucket:
+      remote_path = ('html-results/results-%s' %
+                     datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S'))
+      try:
+        url = cloud_storage.Insert(self._upload_bucket, remote_path, file_path)
+        print 'View HTML results online at %s' % url
+      except cloud_storage.PermissionError as e:
+        logging.error('Cannot upload profiling files to cloud storage due to '
+                      ' permission error: %s' % e.message)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/json_output_formatter.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/json_output_formatter.py
new file mode 100644
index 0000000..81dd35f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/json_output_formatter.py
@@ -0,0 +1,59 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import json
+
+from telemetry.internal.results import output_formatter
+
+
+def ResultsAsDict(page_test_results, benchmark_metadata):
+  """Takes PageTestResults to a dict serializable to JSON.
+
+  To serialize results as JSON we first convert them to a dict that can be
+  serialized by the json module. It also requires a benchmark_metadat object
+  for metadata to be integrated into the results (currently the benchmark
+  name). This function will also output trace files if they exist.
+
+  Args:
+    page_test_results: a PageTestResults object
+    benchmark_metadata: a benchmark.BenchmarkMetadata object
+  """
+  result_dict = {
+    'format_version': '0.2',
+    'next_version': '0.3',
+    # TODO(sullivan): benchmark_name should be removed when updating
+    # format_version to 0.3.
+    'benchmark_name': benchmark_metadata.name,
+    'benchmark_metadata': benchmark_metadata.AsDict(),
+    'summary_values': [v.AsDict() for v in
+                       page_test_results.all_summary_values],
+    'per_page_values': [v.AsDict() for v in
+                        page_test_results.all_page_specific_values],
+    'pages': {p.id: p.AsDict() for p in _GetAllPages(page_test_results)}
+  }
+  if page_test_results.serialized_trace_file_ids_to_paths:
+    result_dict['files'] = page_test_results.serialized_trace_file_ids_to_paths
+  return result_dict
+
+
+def _GetAllPages(page_test_results):
+  pages = set(page_run.story for page_run in
+              page_test_results.all_page_runs)
+  return pages
+
+
+class JsonOutputFormatter(output_formatter.OutputFormatter):
+  def __init__(self, output_stream, benchmark_metadata):
+    super(JsonOutputFormatter, self).__init__(output_stream)
+    self._benchmark_metadata = benchmark_metadata
+
+  @property
+  def benchmark_metadata(self):
+    return self._benchmark_metadata
+
+  def Format(self, page_test_results):
+    json.dump(
+        ResultsAsDict(page_test_results, self.benchmark_metadata),
+        self.output_stream, indent=2)
+    self.output_stream.write('\n')
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/json_output_formatter_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/json_output_formatter_unittest.py
new file mode 100644
index 0000000..d2cd40e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/json_output_formatter_unittest.py
@@ -0,0 +1,136 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import json
+import os
+import shutil
+import StringIO
+import tempfile
+import unittest
+
+from telemetry import story
+from telemetry import benchmark
+from telemetry.internal.results import json_output_formatter
+from telemetry.internal.results import page_test_results
+from telemetry import page as page_module
+from telemetry.value import improvement_direction
+from telemetry.value import scalar
+from telemetry.value import trace
+from tracing.trace_data import trace_data
+
+
+def _MakeStorySet():
+  story_set = story.StorySet(base_dir=os.path.dirname(__file__))
+  story_set.AddStory(
+      page_module.Page('http://www.foo.com/', story_set, story_set.base_dir))
+  story_set.AddStory(
+      page_module.Page('http://www.bar.com/', story_set, story_set.base_dir))
+  return story_set
+
+def _HasPage(pages, page):
+  return pages.get(page.id, None) != None
+
+def _HasValueNamed(values, name):
+  return len([x for x in values if x['name'] == name]) == 1
+
+class JsonOutputFormatterTest(unittest.TestCase):
+  def setUp(self):
+    self._output = StringIO.StringIO()
+    self._story_set = _MakeStorySet()
+    self._formatter = json_output_formatter.JsonOutputFormatter(
+        self._output,
+        benchmark.BenchmarkMetadata('benchmark_name'))
+
+  def testOutputAndParse(self):
+    results = page_test_results.PageTestResults()
+
+    self._output.truncate(0)
+
+    results.WillRunPage(self._story_set[0])
+    v0 = scalar.ScalarValue(results.current_page, 'foo', 'seconds', 3,
+                            improvement_direction=improvement_direction.DOWN)
+    results.AddValue(v0)
+    results.DidRunPage(self._story_set[0])
+
+    self._formatter.Format(results)
+    json.loads(self._output.getvalue())
+
+  def testAsDictBaseKeys(self):
+    results = page_test_results.PageTestResults()
+    d = json_output_formatter.ResultsAsDict(results,
+        self._formatter.benchmark_metadata)
+
+    self.assertEquals(d['format_version'], '0.2')
+    self.assertEquals(d['next_version'], '0.3')
+    self.assertEquals(d['benchmark_metadata']['name'], 'benchmark_name')
+
+  def testAsDictWithOnePage(self):
+    results = page_test_results.PageTestResults()
+    results.WillRunPage(self._story_set[0])
+    v0 = scalar.ScalarValue(results.current_page, 'foo', 'seconds', 3,
+                            improvement_direction=improvement_direction.DOWN)
+    results.AddValue(v0)
+    results.DidRunPage(self._story_set[0])
+
+    d = json_output_formatter.ResultsAsDict(results,
+        self._formatter.benchmark_metadata)
+
+    self.assertTrue(_HasPage(d['pages'], self._story_set[0]))
+    self.assertTrue(_HasValueNamed(d['per_page_values'], 'foo'))
+
+  def testAsDictWithTraceValue(self):
+    tempdir = tempfile.mkdtemp()
+    try:
+      results = page_test_results.PageTestResults()
+      results.WillRunPage(self._story_set[0])
+      v0 = trace.TraceValue(
+          results.current_page,
+          trace_data.CreateTraceDataFromRawData([{'event': 'test'}]))
+      results.AddValue(v0)
+      results.DidRunPage(self._story_set[0])
+      results._SerializeTracesToDirPath(tempdir)
+      d = json_output_formatter.ResultsAsDict(results,
+          self._formatter.benchmark_metadata)
+
+      self.assertTrue(_HasPage(d['pages'], self._story_set[0]))
+      self.assertTrue(_HasValueNamed(d['per_page_values'], 'trace'))
+      self.assertEquals(len(d['files']), 1)
+      output_trace_path = d['files'].values()[0]
+      self.assertTrue(output_trace_path.startswith(tempdir))
+      self.assertTrue(os.path.exists(output_trace_path))
+    finally:
+      shutil.rmtree(tempdir)
+
+  def testAsDictWithTwoPages(self):
+    results = page_test_results.PageTestResults()
+    results.WillRunPage(self._story_set[0])
+    v0 = scalar.ScalarValue(results.current_page, 'foo', 'seconds', 3,
+                            improvement_direction=improvement_direction.DOWN)
+    results.AddValue(v0)
+    results.DidRunPage(self._story_set[0])
+
+    results.WillRunPage(self._story_set[1])
+    v1 = scalar.ScalarValue(results.current_page, 'bar', 'seconds', 4,
+                            improvement_direction=improvement_direction.DOWN)
+    results.AddValue(v1)
+    results.DidRunPage(self._story_set[1])
+
+    d = json_output_formatter.ResultsAsDict(results,
+        self._formatter.benchmark_metadata)
+
+    self.assertTrue(_HasPage(d['pages'], self._story_set[0]))
+    self.assertTrue(_HasPage(d['pages'], self._story_set[1]))
+    self.assertTrue(_HasValueNamed(d['per_page_values'], 'foo'))
+    self.assertTrue(_HasValueNamed(d['per_page_values'], 'bar'))
+
+  def testAsDictWithSummaryValueOnly(self):
+    results = page_test_results.PageTestResults()
+    v = scalar.ScalarValue(None, 'baz', 'seconds', 5,
+                           improvement_direction=improvement_direction.DOWN)
+    results.AddSummaryValue(v)
+
+    d = json_output_formatter.ResultsAsDict(results,
+        self._formatter.benchmark_metadata)
+
+    self.assertFalse(d['pages'])
+    self.assertTrue(_HasValueNamed(d['summary_values'], 'baz'))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/legacy_html_output_formatter.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/legacy_html_output_formatter.py
new file mode 100644
index 0000000..a3782c0
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/legacy_html_output_formatter.py
@@ -0,0 +1,174 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import datetime
+import json
+import logging
+import os
+import re
+
+from telemetry.core import util
+from telemetry import decorators
+from telemetry.internal.results import chart_json_output_formatter
+from telemetry.internal.results import output_formatter
+from telemetry import value as value_module
+from telemetry.value import list_of_scalar_values
+
+
+_TEMPLATE_HTML_PATH = os.path.join(
+    util.GetTelemetryDir(), 'support', 'html_output', 'results-template.html')
+_JS_PLUGINS = [os.path.join('flot', 'jquery.flot.min.js'),
+               os.path.join('WebKit', 'PerformanceTests', 'resources',
+                            'jquery.tablesorter.min.js'),
+               os.path.join('WebKit', 'PerformanceTests', 'resources',
+                            'statistics.js')]
+_UNIT_JSON = os.path.join(
+    util.GetTelemetryDir(), 'telemetry', 'value', 'unit-info.json')
+
+
+def _DatetimeInEs5CompatibleFormat(dt):
+  return dt.strftime('%Y-%m-%dT%H:%M:%S.%f')
+
+
+def _ShortDatetimeInEs5CompatibleFormat(dt):
+  return dt.strftime('%Y-%m-%d %H:%M:%S')
+
+
+class LegacyHtmlOutputFormatter(output_formatter.OutputFormatter):
+  def __init__(self, output_stream, metadata, reset_results, browser_type,
+               results_label=None):
+    super(LegacyHtmlOutputFormatter, self).__init__(output_stream)
+    self._metadata = metadata
+    self._reset_results = reset_results
+    self._build_time = self._GetBuildTime()
+    self._combined_results = []
+    if results_label:
+      self._results_label = results_label
+    else:
+      self._results_label = '%s (%s)' % (
+          metadata.name, _ShortDatetimeInEs5CompatibleFormat(self._build_time))
+    self._result = {
+        'buildTime': _DatetimeInEs5CompatibleFormat(self._build_time),
+        'label': self._results_label,
+        'platform': browser_type,
+        'tests': {}
+        }
+
+  def _GetBuildTime(self):
+    return datetime.datetime.utcnow()
+
+  def _GetHtmlTemplate(self):
+    with open(_TEMPLATE_HTML_PATH) as f:
+      return f.read()
+
+  def _GetPlugins(self):
+    plugins = ''
+    for p in _JS_PLUGINS:
+      with open(os.path.join(util.GetTelemetryThirdPartyDir(), p)) as f:
+        plugins += f.read()
+    return plugins
+
+  def _GetUnitJson(self):
+    with open(_UNIT_JSON) as f:
+      return f.read()
+
+  def _ReadExistingResults(self, output_stream):
+    results_html = output_stream.read()
+    if self._reset_results or not results_html:
+      return []
+    m = re.search(
+        '^<script id="results-json" type="application/json">(.*?)</script>$',
+        results_html, re.MULTILINE | re.DOTALL)
+    if not m:
+      logging.warn('Failed to extract previous results from HTML output')
+      return []
+    return json.loads(m.group(1))[:512]
+
+  def _SaveResults(self, results):
+    self._output_stream.seek(0)
+    self._output_stream.write(unicode(results, 'utf-8'))
+    self._output_stream.truncate()
+
+  def _PrintPerfResult(self, measurement, trace, values, units,
+                       result_type='default', std=None):
+    metric_name = measurement
+    if trace != measurement:
+      metric_name += '.' + trace
+    self._result['tests'].setdefault(self._test_name, {})
+    self._result['tests'][self._test_name].setdefault('metrics', {})
+    metric_data = {
+        'current': values,
+        'units': units,
+        'important': result_type == 'default'
+        }
+    if std is not None:
+      metric_data['std'] = std
+    self._result['tests'][self._test_name]['metrics'][metric_name] = metric_data
+
+  def _TranslateChartJson(self, chart_json_dict):
+    dummy_dict = dict()
+
+    for chart_name, traces in chart_json_dict['charts'].iteritems():
+      for trace_name, value_dict in traces.iteritems():
+        # TODO(eakuefner): refactor summarization so we don't have to jump
+        # through hoops like this.
+        if 'page_id' in value_dict:
+          del value_dict['page_id']
+          result_type = 'nondefault'
+        else:
+          result_type = 'default'
+
+        # Note: we explicitly ignore TraceValues because Buildbot did.
+        if value_dict['type'] == 'trace':
+          continue
+        value = value_module.Value.FromDict(value_dict, dummy_dict)
+
+        perf_value = value.GetBuildbotValue()
+
+        if '@@' in chart_name:
+          chart_name_to_print = '%s-%s' % tuple(chart_name.split('@@'))
+        else:
+          chart_name_to_print = str(chart_name)
+
+        if trace_name == 'summary':
+          trace_name = chart_name_to_print
+
+        std = None
+        if isinstance(value, list_of_scalar_values.ListOfScalarValues):
+          std = value.std
+
+        self._PrintPerfResult(chart_name_to_print, trace_name, perf_value,
+                              value.units, result_type, std)
+
+  @property
+  def _test_name(self):
+    return self._metadata.name
+
+  def GetResults(self):
+    return self._result
+
+  def GetCombinedResults(self):
+    return self._combined_results
+
+  @decorators.Deprecated(
+      2017, 1, 7, 'Use HtmlOutputFormatter and file any issues on GitHub at '
+      'https://github.com/catapult-project/catapult/issues/new?labels=Results2&title=[Results2]')
+  def Format(self, page_test_results):
+    chart_json_dict = chart_json_output_formatter.ResultsAsChartDict(
+        self._metadata, page_test_results.all_page_specific_values,
+        page_test_results.all_summary_values)
+
+    self._TranslateChartJson(chart_json_dict)
+    self._PrintPerfResult('telemetry_page_measurement_results', 'num_failed',
+                          [len(page_test_results.failures)], 'count',
+                          'unimportant')
+
+    self._combined_results = self._ReadExistingResults(self._output_stream)
+    self._combined_results.append(self._result)
+
+    html = self._GetHtmlTemplate()
+    html = html.replace('%json_results%', json.dumps(self.GetCombinedResults()))
+    html = html.replace('%json_units%', self._GetUnitJson())
+    html = html.replace('%plugins%', self._GetPlugins())
+    self._SaveResults(html)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/legacy_html_output_formatter_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/legacy_html_output_formatter_unittest.py
new file mode 100644
index 0000000..84baebd
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/legacy_html_output_formatter_unittest.py
@@ -0,0 +1,265 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import datetime
+import os
+import StringIO
+import unittest
+
+from telemetry import benchmark
+from telemetry import story
+from telemetry.internal.results import legacy_html_output_formatter
+from telemetry.internal.results import page_test_results
+from telemetry import page as page_module
+from telemetry.value import improvement_direction
+from telemetry.value import scalar
+
+
+def _MakeStorySet():
+  story_set = story.StorySet(base_dir=os.path.dirname(__file__))
+  story_set.AddStory(
+      page_module.Page('http://www.foo.com/', story_set, story_set.base_dir))
+  story_set.AddStory(
+      page_module.Page('http://www.bar.com/', story_set, story_set.base_dir))
+  story_set.AddStory(
+      page_module.Page('http://www.baz.com/', story_set, story_set.base_dir))
+  return story_set
+
+
+class DeterministicHtmlOutputFormatter(
+    legacy_html_output_formatter.LegacyHtmlOutputFormatter):
+  def _GetBuildTime(self):
+    return datetime.datetime(1998, 9, 4, 13, 0, 0, 7777)
+
+  def _GetRevision(self):
+    return 'revision'
+
+class FakeMetadataForTest(benchmark.BenchmarkMetadata):
+  def __init__(self):
+    super(FakeMetadataForTest, self).__init__('test_name')
+
+# Wrap string IO with a .name property so that it behaves more like a file.
+class StringIOFile(StringIO.StringIO):
+  name = 'fake_output_file'
+
+
+class LegacyHtmlOutputFormatterTest(unittest.TestCase):
+
+  def setUp(self):
+    self.maxDiff = 100000
+
+  def test_basic_summary(self):
+    test_story_set = _MakeStorySet()
+    output_file = StringIOFile()
+
+    # Run the first time and verify the results are written to the HTML file.
+    results = page_test_results.PageTestResults()
+    results.WillRunPage(test_story_set.stories[0])
+    results.AddValue(scalar.ScalarValue(
+        test_story_set.stories[0], 'a', 'seconds', 3,
+        improvement_direction=improvement_direction.DOWN))
+    results.DidRunPage(test_story_set.stories[0])
+
+    results.WillRunPage(test_story_set.stories[1])
+    results.AddValue(scalar.ScalarValue(
+        test_story_set.stories[1], 'a', 'seconds', 7,
+        improvement_direction=improvement_direction.DOWN))
+    results.DidRunPage(test_story_set.stories[1])
+
+    formatter = DeterministicHtmlOutputFormatter(
+        output_file, FakeMetadataForTest(), False, 'browser_type')
+    formatter.Format(results)
+    expected = {
+      "platform": "browser_type",
+      "buildTime": "1998-09-04T13:00:00.007777",
+      "label": 'test_name (1998-09-04 13:00:00)',
+      "tests": {
+        "test_name": {
+          "metrics": {
+            "a": {
+              "current": [3, 7],
+              "std": 0.0,  # Only one sample per page.
+              "units": "seconds",
+              "important": True
+            },
+            "telemetry_page_measurement_results.num_failed": {
+              "current": [0],
+              "units": "count",
+              "important": False
+            },
+            "a.http://www.bar.com/": {
+              "current": [7],
+              "std": 0.0,
+              "units": "seconds",
+              "important": False
+            },
+            "a.http://www.foo.com/": {
+              "current": [3],
+              "std": 0.0,
+              "units": "seconds",
+              "important": False
+            }
+          }
+        }
+      },
+    }
+    self.assertEquals(expected, formatter.GetResults())
+
+    # Run the second time and verify the results are appended to the HTML file.
+    output_file.seek(0)
+    results = page_test_results.PageTestResults()
+    results.WillRunPage(test_story_set.stories[0])
+    results.AddValue(scalar.ScalarValue(
+        test_story_set.stories[0], 'a', 'seconds', 4,
+        improvement_direction=improvement_direction.DOWN))
+    results.DidRunPage(test_story_set.stories[0])
+
+    results.WillRunPage(test_story_set.stories[1])
+    results.AddValue(scalar.ScalarValue(
+        test_story_set.stories[1], 'a', 'seconds', 8,
+        improvement_direction=improvement_direction.DOWN))
+    results.DidRunPage(test_story_set.stories[1])
+
+    formatter = DeterministicHtmlOutputFormatter(
+        output_file, FakeMetadataForTest(), False, 'browser_type')
+    formatter.Format(results)
+    expected = [
+      {
+        "platform": "browser_type",
+        "buildTime": "1998-09-04T13:00:00.007777",
+        "label": 'test_name (1998-09-04 13:00:00)',
+        "tests": {
+          "test_name": {
+            "metrics": {
+              "a": {
+                "current": [3, 7],
+                "units": "seconds",
+                "std": 0.0,  # Only one sample per page.
+                "important": True
+              },
+              "telemetry_page_measurement_results.num_failed": {
+                "current": [0],
+                "units": "count",
+                "important": False
+              },
+              "a.http://www.bar.com/": {
+                "current": [7],
+                "std": 0.0,
+                "units": "seconds",
+                "important": False
+              },
+              "a.http://www.foo.com/": {
+                "current": [3],
+                "std": 0.0,
+                "units": "seconds",
+                "important": False
+              }
+            }
+          }
+        },
+      },
+      {
+        "platform": "browser_type",
+        "buildTime": "1998-09-04T13:00:00.007777",
+        "label": 'test_name (1998-09-04 13:00:00)',
+        "tests": {
+          "test_name": {
+            "metrics": {
+              "a": {
+                "current": [4, 8],
+                'std': 0.0,  # Only one sample per page.
+                "units": "seconds",
+                "important": True
+              },
+              "telemetry_page_measurement_results.num_failed": {
+                "current": [0],
+                "units": "count",
+                "important": False,
+              },
+              "a.http://www.bar.com/": {
+                "current": [8],
+                "std": 0.0,
+                "units": "seconds",
+                "important": False
+              },
+              "a.http://www.foo.com/": {
+                "current": [4],
+                "std": 0.0,
+                "units": "seconds",
+                "important": False
+              }
+            }
+          }
+        },
+      }]
+    self.assertEquals(expected, formatter.GetCombinedResults())
+    last_output_len = len(output_file.getvalue())
+
+    # Now reset the results and verify the old ones are gone.
+    output_file.seek(0)
+    results = page_test_results.PageTestResults()
+    results.WillRunPage(test_story_set.stories[0])
+    results.AddValue(scalar.ScalarValue(
+        test_story_set.stories[0], 'a', 'seconds', 5,
+        improvement_direction=improvement_direction.DOWN))
+    results.DidRunPage(test_story_set.stories[0])
+
+    results.WillRunPage(test_story_set.stories[1])
+    results.AddValue(scalar.ScalarValue(
+        test_story_set.stories[1], 'a', 'seconds', 9,
+        improvement_direction=improvement_direction.DOWN))
+    results.AddValue(scalar.ScalarValue(
+        test_story_set.stories[1], 'b', 'seconds', 20, tir_label='foo'))
+    results.DidRunPage(test_story_set.stories[1])
+
+    formatter = DeterministicHtmlOutputFormatter(
+       output_file, FakeMetadataForTest(), True, 'browser_type')
+    formatter.Format(results)
+    expected = [{
+      "platform": "browser_type",
+      "buildTime": "1998-09-04T13:00:00.007777",
+      "label": 'test_name (1998-09-04 13:00:00)',
+      "tests": {
+        "test_name": {
+          "metrics": {
+            "a": {
+              "current": [5, 9],
+              'std': 0.0,  # Only one sample per page.
+              "units": "seconds",
+              "important": True
+            },
+            "telemetry_page_measurement_results.num_failed": {
+              "current": [0],
+              "units": "count",
+              "important": False
+            },
+            "a.http://www.bar.com/": {
+              "current": [9],
+              "std": 0.0,
+              "units": "seconds",
+              "important": False
+            },
+            "a.http://www.foo.com/": {
+              "current": [5],
+              "std": 0.0,
+              "units": "seconds",
+              "important": False
+            },
+            "foo-b.http://www.bar.com/": {
+              "current": [20],
+              "std": 0.0,
+              "units": "seconds",
+              "important": False
+            },
+            "foo-b": {
+              "current": [20],
+              "std": 0.0,
+              "units": "seconds",
+              "important": True
+            }
+          }
+        }
+      },
+    }]
+    self.assertEquals(expected, formatter.GetCombinedResults())
+    self.assertTrue(len(output_file.getvalue()) < last_output_len)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/output_formatter.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/output_formatter.py
new file mode 100644
index 0000000..ccd49d2
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/output_formatter.py
@@ -0,0 +1,76 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+
+from telemetry.value import summary as summary_module
+
+class OutputFormatter(object):
+  """A formatter for PageTestResults.
+
+  An OutputFormatter takes PageTestResults, formats the results
+  (telemetry.value.Value instances), and output the formatted results
+  in the given output stream.
+
+  Examples of output formatter: CsvOutputFormatter produces results in
+  CSV format."""
+
+  def __init__(self, output_stream):
+    """Constructs a new formatter that writes to the output_stream.
+
+    Args:
+      output_stream: The stream to write the formatted output to.
+    """
+    self._output_stream = output_stream
+
+  def Format(self, page_test_results):
+    """Formats the given PageTestResults into the output stream.
+
+    This will be called once at the end of a benchmark.
+
+    Args:
+      page_test_results: A PageTestResults object containing all results
+         from the current benchmark run.
+    """
+    raise NotImplementedError()
+
+  def PrintViewResults(self):
+    print 'View result at file://' + os.path.abspath(self.output_stream.name)
+
+  def FormatDisabled(self):
+    """Formats disabled results into the output stream.
+
+    This will be called once when a benchmark is run but disabled.
+    """
+    pass
+
+  @property
+  def output_stream(self):
+    return self._output_stream
+
+
+def SummarizePageSpecificValues(page_specific_values):
+  """Summarize results appropriately for TBM and legacy benchmarks.
+
+  For benchmarks that are timeline-based, we need to summarize not once, but
+  twice, once by name and tir_label (default) and again by name only. But for
+  benchmarks that are not timeline-based, since no tir_labels are set, we will
+  end up duplicating values.
+
+  Thus, we only want to summarize once if the benchmark is not timeline-based,
+  but twice, using the two different key functions, otherwise.
+  """
+  # Default summary uses merge_values.DefaultKeyFunc to summarize both by name
+  # and tir_label.
+  summary = summary_module.Summary(page_specific_values)
+  values = summary.interleaved_computed_per_page_values_and_summaries
+
+  if any(v.tir_label for v in page_specific_values):
+    summary_by_name_only = summary_module.Summary(
+        page_specific_values,
+        key_func=lambda v: v.name)
+    values.extend(
+        summary_by_name_only.interleaved_computed_per_page_values_and_summaries
+    )
+  return values
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/page_test_results.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/page_test_results.py
new file mode 100644
index 0000000..05c27e6
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/page_test_results.py
@@ -0,0 +1,416 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import collections
+import copy
+import datetime
+import json
+import logging
+import os
+import random
+import sys
+import tempfile
+import traceback
+
+from py_utils import cloud_storage  # pylint: disable=import-error
+
+from telemetry import value as value_module
+from telemetry.internal.results import chart_json_output_formatter
+from telemetry.internal.results import json_output_formatter
+from telemetry.internal.results import progress_reporter as reporter_module
+from telemetry.internal.results import story_run
+from telemetry.value import failure
+from telemetry.value import skip
+from telemetry.value import trace
+
+from tracing.value import convert_chart_json
+
+class TelemetryInfo(object):
+  def __init__(self):
+    self._benchmark_name = None
+    self._benchmark_start_ms = None
+    self._label = None
+    self._story_display_name = ''
+    self._story_grouping_keys = {}
+    self._storyset_repeat_counter = 0
+
+  @property
+  def benchmark_name(self):
+    return self._benchmark_name
+
+  @benchmark_name.setter
+  def benchmark_name(self, benchmark_name):
+    assert self.benchmark_name is None, (
+      'benchmark_name must be set exactly once')
+    self._benchmark_name = benchmark_name
+
+  @property
+  def benchmark_start_ms(self):
+    return self._benchmark_start_ms
+
+  @benchmark_start_ms.setter
+  def benchmark_start_ms(self, benchmark_start_ms):
+    assert self.benchmark_start_ms is None, (
+      'benchmark_start_ms must be set exactly once')
+    self._benchmark_start_ms = benchmark_start_ms
+
+  @property
+  def label(self):
+    return self._label
+
+  @label.setter
+  def label(self, label):
+    assert self.label is None, 'label cannot be set more than once'
+    self._label = label
+
+  @property
+  def story_display_name(self):
+    return self._story_display_name
+
+  @property
+  def story_grouping_keys(self):
+    return self._story_grouping_keys
+
+  @property
+  def storyset_repeat_counter(self):
+    return self._storyset_repeat_counter
+
+  def WillRunStory(self, story, storyset_repeat_counter):
+    self._story_display_name = story.display_name
+    if story.grouping_keys:
+      self._story_grouping_keys = story.grouping_keys
+    self._storyset_repeat_counter = storyset_repeat_counter
+
+  def AsDict(self):
+    assert self.benchmark_name is not None, (
+        'benchmark_name must be set exactly once')
+    assert self.benchmark_start_ms is not None, (
+        'benchmark_start_ms must be set exactly once')
+    d = {}
+    d['benchmarkName'] = self.benchmark_name
+    d['benchmarkStartMs'] = self.benchmark_start_ms
+    if self.label:
+      d['label'] = self.label
+    d['storyDisplayName'] = self.story_display_name
+    d['storyGroupingKeys'] = self.story_grouping_keys
+    d['storysetRepeatCounter'] = self.storyset_repeat_counter
+    return d
+
+
+class PageTestResults(object):
+  def __init__(self, output_formatters=None,
+               progress_reporter=None, trace_tag='', output_dir=None,
+               value_can_be_added_predicate=lambda v, is_first: True,
+               benchmark_enabled=True):
+    """
+    Args:
+      output_formatters: A list of output formatters. The output
+          formatters are typically used to format the test results, such
+          as CsvPivotTableOutputFormatter, which output the test results as CSV.
+      progress_reporter: An instance of progress_reporter.ProgressReporter,
+          to be used to output test status/results progressively.
+      trace_tag: A string to append to the buildbot trace name. Currently only
+          used for buildbot.
+      output_dir: A string specified the directory where to store the test
+          artifacts, e.g: trace, videos,...
+      value_can_be_added_predicate: A function that takes two arguments:
+          a value.Value instance (except failure.FailureValue, skip.SkipValue
+          or trace.TraceValue) and a boolean (True when the value is part of
+          the first result for the story). It returns True if the value
+          can be added to the test results and False otherwise.
+    """
+    # TODO(chrishenry): Figure out if trace_tag is still necessary.
+
+    super(PageTestResults, self).__init__()
+    self._progress_reporter = (
+        progress_reporter if progress_reporter is not None
+        else reporter_module.ProgressReporter())
+    self._output_formatters = (
+        output_formatters if output_formatters is not None else [])
+    self._trace_tag = trace_tag
+    self._output_dir = output_dir
+    self._value_can_be_added_predicate = value_can_be_added_predicate
+
+    self._current_page_run = None
+    self._all_page_runs = []
+    self._all_stories = set()
+    self._representative_value_for_each_value_name = {}
+    self._all_summary_values = []
+    self._serialized_trace_file_ids_to_paths = {}
+    self._pages_to_profiling_files = collections.defaultdict(list)
+    self._pages_to_profiling_files_cloud_url = collections.defaultdict(list)
+
+    # You'd expect this to be a set(), but Values are dictionaries, which are
+    # unhashable. We could wrap Values with custom __eq/hash__, but we don't
+    # actually need set-ness in python.
+    self._value_set = []
+
+    self._telemetry_info = TelemetryInfo()
+
+    # State of the benchmark this set of results represents.
+    self._benchmark_enabled = benchmark_enabled
+
+  @property
+  def telemetry_info(self):
+    return self._telemetry_info
+
+  @property
+  def value_set(self):
+    return self._value_set
+
+  def AsHistogramDicts(self, benchmark_metadata):
+    if self.value_set:
+      return self.value_set
+    chart_json = chart_json_output_formatter.ResultsAsChartDict(
+        benchmark_metadata, self.all_page_specific_values,
+        self.all_summary_values)
+    info = self.telemetry_info
+    chart_json['label'] = info.label
+    chart_json['benchmarkStartMs'] = info.benchmark_start_ms
+
+    file_descriptor, chart_json_path = tempfile.mkstemp()
+    os.close(file_descriptor)
+    json.dump(chart_json, file(chart_json_path, 'w'))
+
+    vinn_result = convert_chart_json.ConvertChartJson(chart_json_path)
+
+    os.remove(chart_json_path)
+
+    if vinn_result.returncode != 0:
+      logging.error('Error converting chart json to Histograms:\n' +
+          vinn_result.stdout)
+      return []
+    return json.loads(vinn_result.stdout)
+
+  def __copy__(self):
+    cls = self.__class__
+    result = cls.__new__(cls)
+    for k, v in self.__dict__.items():
+      if isinstance(v, collections.Container):
+        v = copy.copy(v)
+      setattr(result, k, v)
+    return result
+
+  @property
+  def pages_to_profiling_files(self):
+    return self._pages_to_profiling_files
+
+  @property
+  def serialized_trace_file_ids_to_paths(self):
+    return self._serialized_trace_file_ids_to_paths
+
+  @property
+  def pages_to_profiling_files_cloud_url(self):
+    return self._pages_to_profiling_files_cloud_url
+
+  @property
+  def all_page_specific_values(self):
+    values = []
+    for run in self._all_page_runs:
+      values += run.values
+    if self._current_page_run:
+      values += self._current_page_run.values
+    return values
+
+  @property
+  def all_summary_values(self):
+    return self._all_summary_values
+
+  @property
+  def current_page(self):
+    assert self._current_page_run, 'Not currently running test.'
+    return self._current_page_run.story
+
+  @property
+  def current_page_run(self):
+    assert self._current_page_run, 'Not currently running test.'
+    return self._current_page_run
+
+  @property
+  def all_page_runs(self):
+    return self._all_page_runs
+
+  @property
+  def pages_that_succeeded(self):
+    """Returns the set of pages that succeeded."""
+    pages = set(run.story for run in self.all_page_runs)
+    pages.difference_update(self.pages_that_failed)
+    return pages
+
+  @property
+  def pages_that_failed(self):
+    """Returns the set of failed pages."""
+    failed_pages = set()
+    for run in self.all_page_runs:
+      if run.failed:
+        failed_pages.add(run.story)
+    return failed_pages
+
+  @property
+  def failures(self):
+    values = self.all_page_specific_values
+    return [v for v in values if isinstance(v, failure.FailureValue)]
+
+  @property
+  def skipped_values(self):
+    values = self.all_page_specific_values
+    return [v for v in values if isinstance(v, skip.SkipValue)]
+
+  def _GetStringFromExcInfo(self, err):
+    return ''.join(traceback.format_exception(*err))
+
+  def CleanUp(self):
+    """Clean up any TraceValues contained within this results object."""
+    for run in self._all_page_runs:
+      for v in run.values:
+        if isinstance(v, trace.TraceValue):
+          v.CleanUp()
+          run.values.remove(v)
+
+  def __enter__(self):
+    return self
+
+  def __exit__(self, _, __, ___):
+    self.CleanUp()
+
+  def WillRunPage(self, page, storyset_repeat_counter=0):
+    assert not self._current_page_run, 'Did not call DidRunPage.'
+    self._current_page_run = story_run.StoryRun(page)
+    self._progress_reporter.WillRunPage(self)
+    self.telemetry_info.WillRunStory(
+        page, storyset_repeat_counter)
+
+  def DidRunPage(self, page):  # pylint: disable=unused-argument
+    """
+    Args:
+      page: The current page under test.
+    """
+    assert self._current_page_run, 'Did not call WillRunPage.'
+    self._progress_reporter.DidRunPage(self)
+    self._all_page_runs.append(self._current_page_run)
+    self._all_stories.add(self._current_page_run.story)
+    self._current_page_run = None
+
+  def AddValue(self, value):
+    assert self._current_page_run, 'Not currently running test.'
+    assert self._benchmark_enabled, 'Cannot add value to disabled results'
+    self._ValidateValue(value)
+    is_first_result = (
+      self._current_page_run.story not in self._all_stories)
+
+    story_keys = self._current_page_run.story.grouping_keys
+
+    if story_keys:
+      for k, v in story_keys.iteritems():
+        assert k not in value.grouping_keys, (
+            'Tried to add story grouping key ' + k + ' already defined by ' +
+            'value')
+        value.grouping_keys[k] = v
+
+      # We sort by key name to make building the tir_label deterministic.
+      story_keys_label = '_'.join(v for _, v in sorted(story_keys.iteritems()))
+      if value.tir_label:
+        assert value.tir_label == story_keys_label, (
+            'Value has an explicit tir_label (%s) that does not match the '
+            'one computed from story_keys (%s)' % (value.tir_label, story_keys))
+      else:
+        value.tir_label = story_keys_label
+
+    if not (isinstance(value, skip.SkipValue) or
+            isinstance(value, failure.FailureValue) or
+            isinstance(value, trace.TraceValue) or
+            self._value_can_be_added_predicate(value, is_first_result)):
+      return
+    # TODO(eakuefner/chrishenry): Add only one skip per pagerun assert here
+    self._current_page_run.AddValue(value)
+    self._progress_reporter.DidAddValue(value)
+
+  def AddProfilingFile(self, page, file_handle):
+    self._pages_to_profiling_files[page].append(file_handle)
+
+  def AddSummaryValue(self, value):
+    assert value.page is None
+    self._ValidateValue(value)
+    self._all_summary_values.append(value)
+
+  def _ValidateValue(self, value):
+    assert isinstance(value, value_module.Value)
+    if value.name not in self._representative_value_for_each_value_name:
+      self._representative_value_for_each_value_name[value.name] = value
+    representative_value = self._representative_value_for_each_value_name[
+        value.name]
+    assert value.IsMergableWith(representative_value)
+
+  def PrintSummary(self):
+    if self._benchmark_enabled:
+      self._progress_reporter.DidFinishAllTests(self)
+
+      # Only serialize the trace if output_format is json.
+      if (self._output_dir and
+          any(isinstance(o, json_output_formatter.JsonOutputFormatter)
+              for o in self._output_formatters)):
+        self._SerializeTracesToDirPath(self._output_dir)
+      for output_formatter in self._output_formatters:
+        output_formatter.Format(self)
+        output_formatter.PrintViewResults()
+    else:
+      for output_formatter in self._output_formatters:
+        output_formatter.FormatDisabled()
+
+  def FindValues(self, predicate):
+    """Finds all values matching the specified predicate.
+
+    Args:
+      predicate: A function that takes a Value and returns a bool.
+    Returns:
+      A list of values matching |predicate|.
+    """
+    values = []
+    for value in self.all_page_specific_values:
+      if predicate(value):
+        values.append(value)
+    return values
+
+  def FindPageSpecificValuesForPage(self, page, value_name):
+    return self.FindValues(lambda v: v.page == page and v.name == value_name)
+
+  def FindAllPageSpecificValuesNamed(self, value_name):
+    return self.FindValues(lambda v: v.name == value_name)
+
+  def FindAllPageSpecificValuesFromIRNamed(self, tir_label, value_name):
+    return self.FindValues(lambda v: v.name == value_name
+                           and v.tir_label == tir_label)
+
+  def FindAllTraceValues(self):
+    return self.FindValues(lambda v: isinstance(v, trace.TraceValue))
+
+  def _SerializeTracesToDirPath(self, dir_path):
+    """ Serialize all trace values to files in dir_path and return a list of
+    file handles to those files. """
+    for value in self.FindAllTraceValues():
+      fh = value.Serialize(dir_path)
+      self._serialized_trace_file_ids_to_paths[fh.id] = fh.GetAbsPath()
+
+  def UploadTraceFilesToCloud(self, bucket):
+    for value in self.FindAllTraceValues():
+      value.UploadToCloud(bucket)
+
+  def UploadProfilingFilesToCloud(self, bucket):
+    for page, file_handle_list in self._pages_to_profiling_files.iteritems():
+      for file_handle in file_handle_list:
+        remote_path = ('profiler-file-id_%s-%s%-d%s' % (
+            file_handle.id,
+            datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S'),
+            random.randint(1, 100000),
+            file_handle.extension))
+        try:
+          cloud_url = cloud_storage.Insert(
+              bucket, remote_path, file_handle.GetAbsPath())
+          sys.stderr.write(
+              'View generated profiler files online at %s for page %s\n' %
+              (cloud_url, page.display_name))
+          self._pages_to_profiling_files_cloud_url[page].append(cloud_url)
+        except cloud_storage.PermissionError as e:
+          logging.error('Cannot upload profiling files to cloud storage due to '
+                        ' permission error: %s' % e.message)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/page_test_results_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/page_test_results_unittest.py
new file mode 100644
index 0000000..c4eb8ba
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/page_test_results_unittest.py
@@ -0,0 +1,512 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import unittest
+
+from telemetry import benchmark
+from telemetry import story
+from telemetry.internal.results import base_test_results_unittest
+from telemetry.internal.results import chart_json_output_formatter
+from telemetry.internal.results import json_output_formatter
+from telemetry.internal.results import page_test_results
+from telemetry import page as page_module
+from telemetry.testing import stream
+from telemetry.value import failure
+from telemetry.value import histogram
+from telemetry.value import improvement_direction
+from telemetry.value import scalar
+from telemetry.value import skip
+from telemetry.value import trace
+from tracing.trace_data import trace_data
+
+
+class PageTestResultsTest(base_test_results_unittest.BaseTestResultsUnittest):
+  def setUp(self):
+    story_set = story.StorySet(base_dir=os.path.dirname(__file__))
+    story_set.AddStory(page_module.Page("http://www.bar.com/", story_set, story_set.base_dir))
+    story_set.AddStory(page_module.Page("http://www.baz.com/", story_set, story_set.base_dir))
+    story_set.AddStory(page_module.Page("http://www.foo.com/", story_set, story_set.base_dir))
+    self.story_set = story_set
+
+  @property
+  def pages(self):
+    return self.story_set.stories
+
+  def testFailures(self):
+    results = page_test_results.PageTestResults()
+    results.WillRunPage(self.pages[0])
+    results.AddValue(
+        failure.FailureValue(self.pages[0], self.CreateException()))
+    results.DidRunPage(self.pages[0])
+
+    results.WillRunPage(self.pages[1])
+    results.DidRunPage(self.pages[1])
+
+    self.assertEqual(set([self.pages[0]]), results.pages_that_failed)
+    self.assertEqual(set([self.pages[1]]), results.pages_that_succeeded)
+
+    self.assertEqual(2, len(results.all_page_runs))
+    self.assertTrue(results.all_page_runs[0].failed)
+    self.assertTrue(results.all_page_runs[1].ok)
+
+  def testSkips(self):
+    results = page_test_results.PageTestResults()
+    results.WillRunPage(self.pages[0])
+    results.AddValue(skip.SkipValue(self.pages[0], 'testing reason'))
+    results.DidRunPage(self.pages[0])
+
+    results.WillRunPage(self.pages[1])
+    results.DidRunPage(self.pages[1])
+
+    self.assertTrue(results.all_page_runs[0].skipped)
+    self.assertEqual(self.pages[0], results.all_page_runs[0].story)
+    self.assertEqual(set([self.pages[0], self.pages[1]]),
+                     results.pages_that_succeeded)
+
+    self.assertEqual(2, len(results.all_page_runs))
+    self.assertTrue(results.all_page_runs[0].skipped)
+    self.assertTrue(results.all_page_runs[1].ok)
+
+  def testBasic(self):
+    results = page_test_results.PageTestResults()
+    results.WillRunPage(self.pages[0])
+    results.AddValue(scalar.ScalarValue(
+        self.pages[0], 'a', 'seconds', 3,
+        improvement_direction=improvement_direction.UP))
+    results.DidRunPage(self.pages[0])
+
+    results.WillRunPage(self.pages[1])
+    results.AddValue(scalar.ScalarValue(
+        self.pages[1], 'a', 'seconds', 3,
+        improvement_direction=improvement_direction.UP))
+    results.DidRunPage(self.pages[1])
+
+    results.PrintSummary()
+
+    values = results.FindPageSpecificValuesForPage(self.pages[0], 'a')
+    self.assertEquals(1, len(values))
+    v = values[0]
+    self.assertEquals(v.name, 'a')
+    self.assertEquals(v.page, self.pages[0])
+
+    values = results.FindAllPageSpecificValuesNamed('a')
+    assert len(values) == 2
+
+  def testAddValueWithStoryGroupingKeys(self):
+    results = page_test_results.PageTestResults()
+    self.pages[0].grouping_keys['foo'] = 'bar'
+    self.pages[0].grouping_keys['answer'] = '42'
+    results.WillRunPage(self.pages[0])
+    results.AddValue(scalar.ScalarValue(
+        self.pages[0], 'a', 'seconds', 3,
+        improvement_direction=improvement_direction.UP))
+    results.DidRunPage(self.pages[0])
+
+    results.PrintSummary()
+
+    values = results.FindPageSpecificValuesForPage(self.pages[0], 'a')
+    v = values[0]
+    self.assertEquals(v.grouping_keys['foo'], 'bar')
+    self.assertEquals(v.grouping_keys['answer'], '42')
+    self.assertEquals(v.tir_label, '42_bar')
+
+  def testAddValueWithStoryGroupingKeysAndMatchingTirLabel(self):
+    results = page_test_results.PageTestResults()
+    self.pages[0].grouping_keys['foo'] = 'bar'
+    self.pages[0].grouping_keys['answer'] = '42'
+    results.WillRunPage(self.pages[0])
+    results.AddValue(scalar.ScalarValue(
+        self.pages[0], 'a', 'seconds', 3,
+        improvement_direction=improvement_direction.UP,
+        tir_label='42_bar'))
+    results.DidRunPage(self.pages[0])
+
+    results.PrintSummary()
+
+    values = results.FindPageSpecificValuesForPage(self.pages[0], 'a')
+    v = values[0]
+    self.assertEquals(v.grouping_keys['foo'], 'bar')
+    self.assertEquals(v.grouping_keys['answer'], '42')
+    self.assertEquals(v.tir_label, '42_bar')
+
+  def testAddValueWithStoryGroupingKeysAndMismatchingTirLabel(self):
+    results = page_test_results.PageTestResults()
+    self.pages[0].grouping_keys['foo'] = 'bar'
+    self.pages[0].grouping_keys['answer'] = '42'
+    results.WillRunPage(self.pages[0])
+    with self.assertRaises(AssertionError):
+      results.AddValue(scalar.ScalarValue(
+          self.pages[0], 'a', 'seconds', 3,
+          improvement_direction=improvement_direction.UP,
+          tir_label='another_label'))
+
+  def testAddValueWithDuplicateStoryGroupingKeyFails(self):
+    results = page_test_results.PageTestResults()
+    self.pages[0].grouping_keys['foo'] = 'bar'
+    results.WillRunPage(self.pages[0])
+    with self.assertRaises(AssertionError):
+      results.AddValue(scalar.ScalarValue(
+          self.pages[0], 'a', 'seconds', 3,
+          improvement_direction=improvement_direction.UP,
+          grouping_keys={'foo': 'bar'}))
+
+  def testUrlIsInvalidValue(self):
+    results = page_test_results.PageTestResults()
+    results.WillRunPage(self.pages[0])
+    self.assertRaises(
+      AssertionError,
+      lambda: results.AddValue(scalar.ScalarValue(
+          self.pages[0], 'url', 'string', 'foo',
+          improvement_direction=improvement_direction.UP)))
+
+  def testAddSummaryValueWithPageSpecified(self):
+    results = page_test_results.PageTestResults()
+    results.WillRunPage(self.pages[0])
+    self.assertRaises(
+      AssertionError,
+      lambda: results.AddSummaryValue(scalar.ScalarValue(
+          self.pages[0], 'a', 'units', 3,
+          improvement_direction=improvement_direction.UP)))
+
+  def testUnitChange(self):
+    results = page_test_results.PageTestResults()
+    results.WillRunPage(self.pages[0])
+    results.AddValue(scalar.ScalarValue(
+        self.pages[0], 'a', 'seconds', 3,
+        improvement_direction=improvement_direction.UP))
+    results.DidRunPage(self.pages[0])
+
+    results.WillRunPage(self.pages[1])
+    self.assertRaises(
+      AssertionError,
+      lambda: results.AddValue(scalar.ScalarValue(
+          self.pages[1], 'a', 'foobgrobbers', 3,
+          improvement_direction=improvement_direction.UP)))
+
+  def testTypeChange(self):
+    results = page_test_results.PageTestResults()
+    results.WillRunPage(self.pages[0])
+    results.AddValue(scalar.ScalarValue(
+        self.pages[0], 'a', 'seconds', 3,
+        improvement_direction=improvement_direction.UP))
+    results.DidRunPage(self.pages[0])
+
+    results.WillRunPage(self.pages[1])
+    self.assertRaises(
+      AssertionError,
+      lambda: results.AddValue(histogram.HistogramValue(
+          self.pages[1], 'a', 'seconds',
+          raw_value_json='{"buckets": [{"low": 1, "high": 2, "count": 1}]}',
+          improvement_direction=improvement_direction.UP)))
+
+  def testGetPagesThatSucceededAllPagesFail(self):
+    results = page_test_results.PageTestResults()
+    results.WillRunPage(self.pages[0])
+    results.AddValue(scalar.ScalarValue(
+        self.pages[0], 'a', 'seconds', 3,
+        improvement_direction=improvement_direction.UP))
+    results.AddValue(failure.FailureValue.FromMessage(self.pages[0], 'message'))
+    results.DidRunPage(self.pages[0])
+
+    results.WillRunPage(self.pages[1])
+    results.AddValue(scalar.ScalarValue(
+        self.pages[1], 'a', 'seconds', 7,
+        improvement_direction=improvement_direction.UP))
+    results.AddValue(failure.FailureValue.FromMessage(self.pages[1], 'message'))
+    results.DidRunPage(self.pages[1])
+
+    results.PrintSummary()
+    self.assertEquals(0, len(results.pages_that_succeeded))
+
+  def testGetSuccessfulPageValuesMergedNoFailures(self):
+    results = page_test_results.PageTestResults()
+    results.WillRunPage(self.pages[0])
+    results.AddValue(scalar.ScalarValue(
+        self.pages[0], 'a', 'seconds', 3,
+        improvement_direction=improvement_direction.UP))
+    self.assertEquals(1, len(results.all_page_specific_values))
+    results.DidRunPage(self.pages[0])
+
+  def testGetAllValuesForSuccessfulPages(self):
+    results = page_test_results.PageTestResults()
+    results.WillRunPage(self.pages[0])
+    value1 = scalar.ScalarValue(
+        self.pages[0], 'a', 'seconds', 3,
+        improvement_direction=improvement_direction.UP)
+    results.AddValue(value1)
+    results.DidRunPage(self.pages[0])
+
+    results.WillRunPage(self.pages[1])
+    value2 = scalar.ScalarValue(
+        self.pages[1], 'a', 'seconds', 3,
+        improvement_direction=improvement_direction.UP)
+    results.AddValue(value2)
+    results.DidRunPage(self.pages[1])
+
+    results.WillRunPage(self.pages[2])
+    value3 = scalar.ScalarValue(
+        self.pages[2], 'a', 'seconds', 3,
+        improvement_direction=improvement_direction.UP)
+    results.AddValue(value3)
+    results.DidRunPage(self.pages[2])
+
+    self.assertEquals(
+        [value1, value2, value3], results.all_page_specific_values)
+
+  def testGetAllValuesForSuccessfulPagesOnePageFails(self):
+    results = page_test_results.PageTestResults()
+    results.WillRunPage(self.pages[0])
+    value1 = scalar.ScalarValue(
+        self.pages[0], 'a', 'seconds', 3,
+        improvement_direction=improvement_direction.UP)
+    results.AddValue(value1)
+    results.DidRunPage(self.pages[0])
+
+    results.WillRunPage(self.pages[1])
+    value2 = failure.FailureValue.FromMessage(self.pages[1], 'Failure')
+    results.AddValue(value2)
+    results.DidRunPage(self.pages[1])
+
+    results.WillRunPage(self.pages[2])
+    value3 = scalar.ScalarValue(
+        self.pages[2], 'a', 'seconds', 3,
+        improvement_direction=improvement_direction.UP)
+    results.AddValue(value3)
+    results.DidRunPage(self.pages[2])
+
+    self.assertEquals(
+        [value1, value2, value3], results.all_page_specific_values)
+
+  def testFindValues(self):
+    results = page_test_results.PageTestResults()
+    results.WillRunPage(self.pages[0])
+    v0 = scalar.ScalarValue(
+        self.pages[0], 'a', 'seconds', 3,
+        improvement_direction=improvement_direction.UP)
+    results.AddValue(v0)
+    v1 = scalar.ScalarValue(
+        self.pages[0], 'a', 'seconds', 4,
+        improvement_direction=improvement_direction.UP)
+    results.AddValue(v1)
+    results.DidRunPage(self.pages[1])
+
+    values = results.FindValues(lambda v: v.value == 3)
+    self.assertEquals([v0], values)
+
+  def testValueWithTIRLabel(self):
+    results = page_test_results.PageTestResults()
+    results.WillRunPage(self.pages[0])
+    v0 = scalar.ScalarValue(
+        self.pages[0], 'a', 'seconds', 3, tir_label='foo',
+        improvement_direction=improvement_direction.UP)
+    results.AddValue(v0)
+    v1 = scalar.ScalarValue(
+        self.pages[0], 'a', 'seconds', 3, tir_label='bar',
+        improvement_direction=improvement_direction.UP)
+    results.AddValue(v1)
+    results.DidRunPage(self.pages[0])
+
+    values = results.FindAllPageSpecificValuesFromIRNamed('foo', 'a')
+    self.assertEquals([v0], values)
+
+  def testTraceValue(self):
+    results = page_test_results.PageTestResults()
+    results.WillRunPage(self.pages[0])
+    results.AddValue(trace.TraceValue(
+        None, trace_data.CreateTraceDataFromRawData([[{'test': 1}]])))
+    results.DidRunPage(self.pages[0])
+
+    results.WillRunPage(self.pages[1])
+    results.AddValue(trace.TraceValue(
+        None, trace_data.CreateTraceDataFromRawData([[{'test': 2}]])))
+    results.DidRunPage(self.pages[1])
+
+    results.PrintSummary()
+
+    values = results.FindAllTraceValues()
+    self.assertEquals(2, len(values))
+
+  def testCleanUpCleansUpTraceValues(self):
+    results = page_test_results.PageTestResults()
+    v0 = trace.TraceValue(
+        None, trace_data.CreateTraceDataFromRawData([{'test': 1}]))
+    v1 = trace.TraceValue(
+        None, trace_data.CreateTraceDataFromRawData([{'test': 2}]))
+
+    results.WillRunPage(self.pages[0])
+    results.AddValue(v0)
+    results.DidRunPage(self.pages[0])
+
+    results.WillRunPage(self.pages[1])
+    results.AddValue(v1)
+    results.DidRunPage(self.pages[1])
+
+    results.CleanUp()
+    self.assertTrue(v0.cleaned_up)
+    self.assertTrue(v1.cleaned_up)
+
+  def testNoTracesLeftAfterCleanUp(self):
+    results = page_test_results.PageTestResults()
+    v0 = trace.TraceValue(None,
+                          trace_data.CreateTraceDataFromRawData([{'test': 1}]))
+    v1 = trace.TraceValue(None,
+                          trace_data.CreateTraceDataFromRawData([{'test': 2}]))
+
+    results.WillRunPage(self.pages[0])
+    results.AddValue(v0)
+    results.DidRunPage(self.pages[0])
+
+    results.WillRunPage(self.pages[1])
+    results.AddValue(v1)
+    results.DidRunPage(self.pages[1])
+
+    results.CleanUp()
+    self.assertFalse(results.FindAllTraceValues())
+
+  def testPrintSummaryDisabledResults(self):
+    output_stream = stream.TestOutputStream()
+    output_formatters = []
+    benchmark_metadata = benchmark.BenchmarkMetadata(
+      'benchmark_name', 'benchmark_description')
+    output_formatters.append(
+        chart_json_output_formatter.ChartJsonOutputFormatter(
+            output_stream, benchmark_metadata))
+    output_formatters.append(json_output_formatter.JsonOutputFormatter(
+        output_stream, benchmark_metadata))
+    results = page_test_results.PageTestResults(
+        output_formatters=output_formatters, benchmark_enabled=False)
+    results.PrintSummary()
+    self.assertEquals(output_stream.output_data,
+      "{\n  \"enabled\": false,\n  \"benchmark_name\": \"benchmark_name\"\n}\n")
+
+
+class PageTestResultsFilterTest(unittest.TestCase):
+  def setUp(self):
+    story_set = story.StorySet(base_dir=os.path.dirname(__file__))
+    story_set.AddStory(
+        page_module.Page('http://www.foo.com/', story_set, story_set.base_dir))
+    story_set.AddStory(
+        page_module.Page('http://www.bar.com/', story_set, story_set.base_dir))
+    self.story_set = story_set
+
+  @property
+  def pages(self):
+    return self.story_set.stories
+
+  def testFilterValue(self):
+    def AcceptValueNamed_a(value, _):
+      return value.name == 'a'
+    results = page_test_results.PageTestResults(
+        value_can_be_added_predicate=AcceptValueNamed_a)
+    results.WillRunPage(self.pages[0])
+    results.AddValue(scalar.ScalarValue(
+        self.pages[0], 'a', 'seconds', 3,
+        improvement_direction=improvement_direction.UP))
+    results.AddValue(scalar.ScalarValue(
+        self.pages[0], 'b', 'seconds', 3,
+        improvement_direction=improvement_direction.UP))
+    results.DidRunPage(self.pages[0])
+
+    results.WillRunPage(self.pages[1])
+    results.AddValue(scalar.ScalarValue(
+        self.pages[1], 'a', 'seconds', 3,
+        improvement_direction=improvement_direction.UP))
+    results.AddValue(scalar.ScalarValue(
+        self.pages[1], 'd', 'seconds', 3,
+        improvement_direction=improvement_direction.UP))
+    results.DidRunPage(self.pages[1])
+    results.PrintSummary()
+    self.assertEquals(
+        [('a', 'http://www.foo.com/'), ('a', 'http://www.bar.com/')],
+        [(v.name, v.page.url) for v in results.all_page_specific_values])
+
+  def testFilterIsFirstResult(self):
+    def AcceptSecondValues(_, is_first_result):
+      return not is_first_result
+    results = page_test_results.PageTestResults(
+        value_can_be_added_predicate=AcceptSecondValues)
+
+    # First results (filtered out)
+    results.WillRunPage(self.pages[0])
+    results.AddValue(scalar.ScalarValue(
+        self.pages[0], 'a', 'seconds', 7,
+        improvement_direction=improvement_direction.UP))
+    results.AddValue(scalar.ScalarValue(
+        self.pages[0], 'b', 'seconds', 8,
+        improvement_direction=improvement_direction.UP))
+    results.DidRunPage(self.pages[0])
+    results.WillRunPage(self.pages[1])
+    results.AddValue(scalar.ScalarValue(
+        self.pages[1], 'a', 'seconds', 5,
+        improvement_direction=improvement_direction.UP))
+    results.AddValue(scalar.ScalarValue(
+        self.pages[1], 'd', 'seconds', 6,
+        improvement_direction=improvement_direction.UP))
+    results.DidRunPage(self.pages[1])
+
+    # Second results
+    results.WillRunPage(self.pages[0])
+    results.AddValue(scalar.ScalarValue(
+        self.pages[0], 'a', 'seconds', 3,
+        improvement_direction=improvement_direction.UP))
+    results.AddValue(scalar.ScalarValue(
+        self.pages[0], 'b', 'seconds', 4,
+        improvement_direction=improvement_direction.UP))
+    results.DidRunPage(self.pages[0])
+    results.WillRunPage(self.pages[1])
+    results.AddValue(scalar.ScalarValue(
+        self.pages[1], 'a', 'seconds', 1,
+        improvement_direction=improvement_direction.UP))
+    results.AddValue(scalar.ScalarValue(
+        self.pages[1], 'd', 'seconds', 2,
+        improvement_direction=improvement_direction.UP))
+    results.DidRunPage(self.pages[1])
+    results.PrintSummary()
+    expected_values = [
+        ('a', 'http://www.foo.com/', 3),
+        ('b', 'http://www.foo.com/', 4),
+        ('a', 'http://www.bar.com/', 1),
+        ('d', 'http://www.bar.com/', 2)]
+    actual_values = [(v.name, v.page.url, v.value)
+                     for v in results.all_page_specific_values]
+    self.assertEquals(expected_values, actual_values)
+
+  def testFailureValueCannotBeFiltered(self):
+    def AcceptValueNamed_a(value, _):
+      return value.name == 'a'
+    results = page_test_results.PageTestResults(
+        value_can_be_added_predicate=AcceptValueNamed_a)
+    results.WillRunPage(self.pages[0])
+    results.AddValue(scalar.ScalarValue(
+        self.pages[0], 'b', 'seconds', 8,
+        improvement_direction=improvement_direction.UP))
+    failure_value = failure.FailureValue.FromMessage(self.pages[0], 'failure')
+    results.AddValue(failure_value)
+    results.DidRunPage(self.pages[0])
+    results.PrintSummary()
+
+    # Although predicate says only accept values named 'a', the failure value is
+    # added anyway.
+    self.assertEquals(len(results.all_page_specific_values), 1)
+    self.assertIn(failure_value, results.all_page_specific_values)
+
+  def testSkipValueCannotBeFiltered(self):
+    def AcceptValueNamed_a(value, _):
+      return value.name == 'a'
+    results = page_test_results.PageTestResults(
+        value_can_be_added_predicate=AcceptValueNamed_a)
+    results.WillRunPage(self.pages[0])
+    skip_value = skip.SkipValue(self.pages[0], 'skip for testing')
+    results.AddValue(scalar.ScalarValue(
+        self.pages[0], 'b', 'seconds', 8,
+        improvement_direction=improvement_direction.UP))
+    results.AddValue(skip_value)
+    results.DidRunPage(self.pages[0])
+    results.PrintSummary()
+
+    # Although predicate says only accept value with named 'a', skip value is
+    # added anyway.
+    self.assertEquals(len(results.all_page_specific_values), 1)
+    self.assertIn(skip_value, results.all_page_specific_values)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/progress_reporter.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/progress_reporter.py
new file mode 100644
index 0000000..3eea999
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/progress_reporter.py
@@ -0,0 +1,27 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+class ProgressReporter(object):
+  """A class that reports progress of a benchmark.
+
+  The reporter produces output whenever a significant event happens
+  during the progress of a benchmark, including (but not limited to):
+  when a page run is started, when a page run finished, any failures
+  during a page run.
+
+  The default implementation outputs nothing.
+  """
+
+  def DidAddValue(self, value):
+    pass
+
+  def WillRunPage(self, page_test_results):
+    pass
+
+  def DidRunPage(self, page_test_results):
+    pass
+
+  def DidFinishAllTests(self, page_test_results):
+    pass
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/results_options.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/results_options.py
new file mode 100644
index 0000000..357ec30
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/results_options.py
@@ -0,0 +1,186 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import codecs
+import optparse
+import os
+import sys
+import time
+
+from py_utils import cloud_storage  # pylint: disable=import-error
+
+from telemetry.core import util
+from telemetry.internal.results import chart_json_output_formatter
+from telemetry.internal.results import csv_pivot_table_output_formatter
+from telemetry.internal.results import gtest_progress_reporter
+from telemetry.internal.results import histogram_set_json_output_formatter
+from telemetry.internal.results import html_output_formatter
+from telemetry.internal.results import json_output_formatter
+from telemetry.internal.results import legacy_html_output_formatter
+from telemetry.internal.results import page_test_results
+from telemetry.internal.results import progress_reporter
+
+# Allowed output formats. The default is the first item in the list.
+
+_OUTPUT_FORMAT_CHOICES = ('html', 'gtest', 'json', 'chartjson',
+    'csv-pivot-table', 'histograms', 'legacy-html', 'none')
+
+
+# Filenames to use for given output formats.
+_OUTPUT_FILENAME_LOOKUP = {
+    'html': 'results.html',
+    'json': 'results.json',
+    'chartjson': 'results-chart.json',
+    'csv-pivot-table': 'results-pivot-table.csv',
+    'histograms': 'histograms.json',
+    'legacy-html': 'legacy-results.html'
+}
+
+
+def AddResultsOptions(parser):
+  group = optparse.OptionGroup(parser, 'Results options')
+  group.add_option('--output-format', action='append', dest='output_formats',
+                    choices=_OUTPUT_FORMAT_CHOICES, default=[],
+                    help='Output format. Defaults to "%%default". '
+                    'Can be %s.' % ', '.join(_OUTPUT_FORMAT_CHOICES))
+  group.add_option('-o', '--output',
+                    dest='output_file',
+                    default=None,
+                    help='Redirects output to a file. Defaults to stdout.')
+  group.add_option('--output-dir', default=util.GetBaseDir(),
+                    help='Where to save output data after the run.')
+  group.add_option('--output-trace-tag',
+                    default='',
+                    help='Append a tag to the key of each result trace. Use '
+                    'with html, csv-pivot-table output formats.')
+  group.add_option('--reset-results', action='store_true',
+                    help='Delete all stored results.')
+  group.add_option('--upload-results', action='store_true',
+                    help='Upload the results to cloud storage.')
+  group.add_option('--upload-bucket', default='output',
+                    help='Storage bucket to use for the uploaded results. ' +
+                    'Defaults to output bucket. Supported values are: ' +
+                    ', '.join(cloud_storage.BUCKET_ALIAS_NAMES) +
+                    '; or a valid cloud storage bucket name.')
+  group.add_option('--results-label',
+                    default=None,
+                    help='Optional label to use for the results of a run .')
+  group.add_option('--suppress_gtest_report',
+                   default=False,
+                   help='Whether to suppress GTest progress report.')
+  parser.add_option_group(group)
+
+
+def ProcessCommandLineArgs(parser, args):
+  # TODO(ariblue): Delete this flag entirely at some future data, when the
+  # existence of such a flag has been long forgotten.
+  if args.output_file:
+    parser.error('This flag is deprecated. Please use --output-dir instead.')
+
+  try:
+    os.makedirs(args.output_dir)
+  except OSError:
+    # Do nothing if the output directory already exists. Existing files will
+    # get overwritten.
+    pass
+
+  args.output_dir = os.path.expanduser(args.output_dir)
+
+
+def _GetOutputStream(output_format, output_dir):
+  assert output_format in _OUTPUT_FORMAT_CHOICES, 'Must specify a valid format.'
+  assert output_format not in ('gtest', 'none'), (
+      'Cannot set stream for \'gtest\' or \'none\' output formats.')
+
+  assert output_format in _OUTPUT_FILENAME_LOOKUP, (
+      'No known filename for the \'%s\' output format' % output_format)
+  output_file = os.path.join(output_dir, _OUTPUT_FILENAME_LOOKUP[output_format])
+
+  # TODO(eakuefner): Factor this hack out after we rewrite HTMLOutputFormatter.
+  if output_format == 'html' or output_format == 'legacy-html':
+    open(output_file, 'a').close() # Create file if it doesn't exist.
+    return codecs.open(output_file, mode='r+', encoding='utf-8')
+  else:
+    return open(output_file, mode='w+')
+
+
+def _GetProgressReporter(output_skipped_tests_summary, suppress_gtest_report):
+  if suppress_gtest_report:
+    return progress_reporter.ProgressReporter()
+
+  return gtest_progress_reporter.GTestProgressReporter(
+      sys.stdout, output_skipped_tests_summary=output_skipped_tests_summary)
+
+
+def CreateResults(benchmark_metadata, options,
+                  value_can_be_added_predicate=lambda v, is_first: True,
+                  benchmark_enabled=True):
+  """
+  Args:
+    options: Contains the options specified in AddResultsOptions.
+  """
+  if not options.output_formats:
+    options.output_formats = [_OUTPUT_FORMAT_CHOICES[0]]
+
+  upload_bucket = None
+  if options.upload_results:
+    upload_bucket = options.upload_bucket
+    if upload_bucket in cloud_storage.BUCKET_ALIASES:
+      upload_bucket = cloud_storage.BUCKET_ALIASES[upload_bucket]
+
+  output_formatters = []
+  for output_format in options.output_formats:
+    if output_format == 'none' or output_format == "gtest":
+      continue
+
+    output_stream = _GetOutputStream(output_format, options.output_dir)
+    if output_format == 'csv-pivot-table':
+      output_formatters.append(
+          csv_pivot_table_output_formatter.CsvPivotTableOutputFormatter(
+              output_stream, trace_tag=options.output_trace_tag))
+    elif output_format == 'html':
+      output_formatters.append(html_output_formatter.HtmlOutputFormatter(
+          output_stream, benchmark_metadata, options.reset_results,
+          upload_bucket))
+    elif output_format == 'json':
+      output_formatters.append(json_output_formatter.JsonOutputFormatter(
+          output_stream, benchmark_metadata))
+    elif output_format == 'chartjson':
+      output_formatters.append(
+          chart_json_output_formatter.ChartJsonOutputFormatter(
+              output_stream, benchmark_metadata))
+    elif output_format == 'histograms':
+      output_formatters.append(
+          histogram_set_json_output_formatter.HistogramSetJsonOutputFormatter(
+              output_stream, benchmark_metadata, options.reset_results))
+    elif output_format == 'legacy-html':
+      output_formatters.append(
+          legacy_html_output_formatter.LegacyHtmlOutputFormatter(
+              output_stream, benchmark_metadata, options.reset_results,
+              options.browser_type, options.results_label))
+    else:
+      # Should never be reached. The parser enforces the choices.
+      raise Exception('Invalid --output-format "%s". Valid choices are: %s'
+                      % (output_format, ', '.join(_OUTPUT_FORMAT_CHOICES)))
+
+  # TODO(chrishenry): This is here to not change the output of
+  # gtest. Let's try enabling skipped tests summary for gtest test
+  # results too (in a separate patch), and see if we break anything.
+  output_skipped_tests_summary = 'gtest' in options.output_formats
+
+  reporter = _GetProgressReporter(output_skipped_tests_summary,
+                                  options.suppress_gtest_report)
+
+  results = page_test_results.PageTestResults(
+      output_formatters=output_formatters, progress_reporter=reporter,
+      output_dir=options.output_dir,
+      value_can_be_added_predicate=value_can_be_added_predicate,
+      benchmark_enabled=benchmark_enabled)
+
+  results.telemetry_info.benchmark_name = benchmark_metadata.name
+  results.telemetry_info.benchmark_start_ms = time.time() * 1000.0
+  if options.results_label:
+    results.telemetry_info.label = options.results_label
+
+  return results
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/story_run.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/story_run.py
new file mode 100644
index 0000000..078d0fc
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/story_run.py
@@ -0,0 +1,51 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.value import failure
+from telemetry.value import skip
+
+
+class StoryRun(object):
+  def __init__(self, story):
+    self._story = story
+    self._values = []
+
+  def AddValue(self, value):
+    self._values.append(value)
+
+  @property
+  def story(self):
+    return self._story
+
+  @property
+  def values(self):
+    """The values that correspond to this story run."""
+    return self._values
+
+  @property
+  def ok(self):
+    """Whether the current run is still ok.
+
+    To be precise: returns true if there is neither FailureValue nor
+    SkipValue in self.values.
+    """
+    return not self.skipped and not self.failed
+
+  @property
+  def skipped(self):
+    """Whether the current run is being skipped.
+
+    To be precise: returns true if there is any SkipValue in self.values.
+    """
+    return any(isinstance(v, skip.SkipValue) for v in self.values)
+
+  @property
+  def failed(self):
+    """Whether the current run failed.
+
+    To be precise: returns true if there is a FailureValue but not
+    SkipValue in self.values.
+    """
+    return not self.skipped and any(
+        isinstance(v, failure.FailureValue) for v in self.values)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/story_run_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/story_run_unittest.py
new file mode 100644
index 0000000..d084a75
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/results/story_run_unittest.py
@@ -0,0 +1,80 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from telemetry.internal.results import story_run
+from telemetry.story import shared_state
+from telemetry.story import story_set
+from telemetry import story as story_module
+from telemetry.value import failure
+from telemetry.value import improvement_direction
+from telemetry.value import scalar
+from telemetry.value import skip
+
+
+# pylint: disable=abstract-method
+class SharedStateBar(shared_state.SharedState):
+  pass
+
+class StoryFoo(story_module.Story):
+  def __init__(self, name='', tags=None):
+    super(StoryFoo, self).__init__(
+        SharedStateBar, name, tags)
+
+class StoryRunTest(unittest.TestCase):
+  def setUp(self):
+    self.story_set = story_set.StorySet()
+    self.story_set.AddStory(StoryFoo())
+
+  @property
+  def stories(self):
+    return self.story_set.stories
+
+  def testStoryRunFailed(self):
+    run = story_run.StoryRun(self.stories[0])
+    run.AddValue(failure.FailureValue.FromMessage(self.stories[0], 'test'))
+    self.assertFalse(run.ok)
+    self.assertTrue(run.failed)
+    self.assertFalse(run.skipped)
+
+    run = story_run.StoryRun(self.stories[0])
+    run.AddValue(scalar.ScalarValue(
+        self.stories[0], 'a', 's', 1,
+        improvement_direction=improvement_direction.UP))
+    run.AddValue(failure.FailureValue.FromMessage(self.stories[0], 'test'))
+    self.assertFalse(run.ok)
+    self.assertTrue(run.failed)
+    self.assertFalse(run.skipped)
+
+  def testStoryRunSkipped(self):
+    run = story_run.StoryRun(self.stories[0])
+    run.AddValue(failure.FailureValue.FromMessage(self.stories[0], 'test'))
+    run.AddValue(skip.SkipValue(self.stories[0], 'test'))
+    self.assertFalse(run.ok)
+    self.assertFalse(run.failed)
+    self.assertTrue(run.skipped)
+
+    run = story_run.StoryRun(self.stories[0])
+    run.AddValue(scalar.ScalarValue(
+        self.stories[0], 'a', 's', 1,
+        improvement_direction=improvement_direction.UP))
+    run.AddValue(skip.SkipValue(self.stories[0], 'test'))
+    self.assertFalse(run.ok)
+    self.assertFalse(run.failed)
+    self.assertTrue(run.skipped)
+
+  def testStoryRunSucceeded(self):
+    run = story_run.StoryRun(self.stories[0])
+    self.assertTrue(run.ok)
+    self.assertFalse(run.failed)
+    self.assertFalse(run.skipped)
+
+    run = story_run.StoryRun(self.stories[0])
+    run.AddValue(scalar.ScalarValue(
+        self.stories[0], 'a', 's', 1,
+        improvement_direction=improvement_direction.UP))
+    self.assertTrue(run.ok)
+    self.assertFalse(run.failed)
+    self.assertFalse(run.skipped)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/story_runner.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/story_runner.py
new file mode 100644
index 0000000..63e31b1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/story_runner.py
@@ -0,0 +1,470 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+import optparse
+import os
+import subprocess
+import sys
+import time
+
+import py_utils
+from py_utils import cloud_storage  # pylint: disable=import-error
+
+from telemetry.core import exceptions
+from telemetry import decorators
+from telemetry.internal.actions import page_action
+from telemetry.internal.browser import browser_finder
+from telemetry.internal.results import results_options
+from telemetry.internal.util import exception_formatter
+from telemetry import page
+from telemetry.page import legacy_page_test
+from telemetry import story as story_module
+from telemetry.util import wpr_modes
+from telemetry.value import failure
+from telemetry.value import skip
+from telemetry.value import scalar
+from telemetry.web_perf import story_test
+
+
+class ArchiveError(Exception):
+  pass
+
+
+def AddCommandLineArgs(parser):
+  story_module.StoryFilter.AddCommandLineArgs(parser)
+  results_options.AddResultsOptions(parser)
+
+  # Page set options
+  group = optparse.OptionGroup(parser, 'Page set repeat options')
+  group.add_option('--pageset-repeat', default=1, type='int',
+                   help='Number of times to repeat the entire pageset.')
+  group.add_option('--max-failures', default=None, type='int',
+                   help='Maximum number of test failures before aborting '
+                   'the run. Defaults to the number specified by the '
+                   'PageTest.')
+  parser.add_option_group(group)
+
+  # WPR options
+  group = optparse.OptionGroup(parser, 'Web Page Replay options')
+  group.add_option('--use-live-sites',
+      dest='use_live_sites', action='store_true',
+      help='Run against live sites and ignore the Web Page Replay archives.')
+  parser.add_option_group(group)
+
+  parser.add_option('-d', '--also-run-disabled-tests',
+                    dest='run_disabled_tests',
+                    action='store_true', default=False,
+                    help='Ignore @Disabled and @Enabled restrictions.')
+
+
+def ProcessCommandLineArgs(parser, args):
+  story_module.StoryFilter.ProcessCommandLineArgs(parser, args)
+  results_options.ProcessCommandLineArgs(parser, args)
+
+  if args.pageset_repeat < 1:
+    parser.error('--pageset-repeat must be a positive integer.')
+
+
+def _RunStoryAndProcessErrorIfNeeded(story, results, state, test):
+  def ProcessError(description=None):
+    state.DumpStateUponFailure(story, results)
+    results.AddValue(failure.FailureValue(story, sys.exc_info(), description))
+  try:
+    # TODO(mikecase): Remove this logging once Android perf bots are swarmed.
+    # crbug.com/678282
+    if state.platform.GetOSName() == 'android':
+      state.platform._platform_backend.Log(
+          'START %s' % (story.name if story.name else str(story)))
+    if isinstance(test, story_test.StoryTest):
+      test.WillRunStory(state.platform)
+    state.WillRunStory(story)
+    if not state.CanRunStory(story):
+      results.AddValue(skip.SkipValue(
+          story,
+          'Skipped because story is not supported '
+          '(SharedState.CanRunStory() returns False).'))
+      return
+    state.RunStory(results)
+    if isinstance(test, story_test.StoryTest):
+      test.Measure(state.platform, results)
+  except (legacy_page_test.Failure, exceptions.TimeoutException,
+          exceptions.LoginException, exceptions.ProfilingException,
+          py_utils.TimeoutException):
+    ProcessError()
+  except exceptions.Error:
+    ProcessError()
+    raise
+  except page_action.PageActionNotSupported as e:
+    results.AddValue(
+        skip.SkipValue(story, 'Unsupported page action: %s' % e))
+  except Exception:
+    ProcessError(description='Unhandlable exception raised.')
+    raise
+  finally:
+    has_existing_exception = (sys.exc_info() != (None, None, None))
+    try:
+      state.DidRunStory(results)
+      # if state.DidRunStory raises exception, things are messed up badly and we
+      # do not need to run test.DidRunStory at that point.
+      if isinstance(test, story_test.StoryTest):
+        test.DidRunStory(state.platform)
+      else:
+        test.DidRunPage(state.platform)
+      # TODO(mikecase): Remove this logging once Android perf bots are swarmed.
+      # crbug.com/678282
+      if state.platform.GetOSName() == 'android':
+        state.platform._platform_backend.Log(
+            'END %s' % (story.name if story.name else str(story)))
+    except Exception:
+      if not has_existing_exception:
+        state.DumpStateUponFailure(story, results)
+        raise
+      # Print current exception and propagate existing exception.
+      exception_formatter.PrintFormattedException(
+          msg='Exception raised when cleaning story run: ')
+
+
+class StoryGroup(object):
+  def __init__(self, shared_state_class):
+    self._shared_state_class = shared_state_class
+    self._stories = []
+
+  @property
+  def shared_state_class(self):
+    return self._shared_state_class
+
+  @property
+  def stories(self):
+    return self._stories
+
+  def AddStory(self, story):
+    assert (story.shared_state_class is
+            self._shared_state_class)
+    self._stories.append(story)
+
+
+def StoriesGroupedByStateClass(story_set, allow_multiple_groups):
+  """ Returns a list of story groups which each contains stories with
+  the same shared_state_class.
+
+  Example:
+    Assume A1, A2, A3 are stories with same shared story class, and
+    similar for B1, B2.
+    If their orders in story set is A1 A2 B1 B2 A3, then the grouping will
+    be [A1 A2] [B1 B2] [A3].
+
+  It's purposefully done this way to make sure that order of
+  stories are the same of that defined in story_set. It's recommended that
+  stories with the same states should be arranged next to each others in
+  story sets to reduce the overhead of setting up & tearing down the
+  shared story state.
+  """
+  story_groups = []
+  story_groups.append(
+      StoryGroup(story_set[0].shared_state_class))
+  for story in story_set:
+    if (story.shared_state_class is not
+        story_groups[-1].shared_state_class):
+      if not allow_multiple_groups:
+        raise ValueError('This StorySet is only allowed to have one '
+                         'SharedState but contains the following '
+                         'SharedState classes: %s, %s.\n Either '
+                         'remove the extra SharedStates or override '
+                         'allow_mixed_story_states.' % (
+                         story_groups[-1].shared_state_class,
+                         story.shared_state_class))
+      story_groups.append(
+          StoryGroup(story.shared_state_class))
+    story_groups[-1].AddStory(story)
+  return story_groups
+
+
+def Run(test, story_set, finder_options, results, max_failures=None,
+        tear_down_after_story=False, tear_down_after_story_set=False):
+  """Runs a given test against a given page_set with the given options.
+
+  Stop execution for unexpected exceptions such as KeyboardInterrupt.
+  We "white list" certain exceptions for which the story runner
+  can continue running the remaining stories.
+  """
+  for s in story_set:
+    ValidateStory(s)
+
+  # Filter page set based on options.
+  stories = filter(story_module.StoryFilter.IsSelected, story_set)
+
+  if (not finder_options.use_live_sites and
+      finder_options.browser_options.wpr_mode != wpr_modes.WPR_RECORD):
+    serving_dirs = story_set.serving_dirs
+    if story_set.bucket:
+      for directory in serving_dirs:
+        cloud_storage.GetFilesInDirectoryIfChanged(directory,
+                                                   story_set.bucket)
+    if story_set.archive_data_file and not _UpdateAndCheckArchives(
+        story_set.archive_data_file, story_set.wpr_archive_info, stories):
+      return
+
+  if not stories:
+    return
+
+  # Effective max failures gives priority to command-line flag value.
+  effective_max_failures = finder_options.max_failures
+  if effective_max_failures is None:
+    effective_max_failures = max_failures
+
+  story_groups = StoriesGroupedByStateClass(
+      stories,
+      story_set.allow_mixed_story_states)
+
+  for group in story_groups:
+    state = None
+    try:
+      for storyset_repeat_counter in xrange(finder_options.pageset_repeat):
+        for story in group.stories:
+          if not state:
+            # Construct shared state by using a copy of finder_options. Shared
+            # state may update the finder_options. If we tear down the shared
+            # state after this story run, we want to construct the shared
+            # state for the next story from the original finder_options.
+            state = group.shared_state_class(
+                test, finder_options.Copy(), story_set)
+
+          results.WillRunPage(story, storyset_repeat_counter)
+          try:
+            # Log ps on n7s to determine if adb changed processes.
+            # crbug.com/667470
+            if 'Nexus 7' in state.platform.GetDeviceTypeName():
+              ps_output = subprocess.check_output(['ps', '-ef'])
+              logging.info('Ongoing processes:\n%s', ps_output)
+
+            state.platform.WaitForTemperature(35)
+            _WaitForThermalThrottlingIfNeeded(state.platform)
+            _RunStoryAndProcessErrorIfNeeded(story, results, state, test)
+          except exceptions.Error:
+            # Catch all Telemetry errors to give the story a chance to retry.
+            # The retry is enabled by tearing down the state and creating
+            # a new state instance in the next iteration.
+            try:
+              # If TearDownState raises, do not catch the exception.
+              # (The Error was saved as a failure value.)
+              state.TearDownState()
+            finally:
+              # Later finally-blocks use state, so ensure it is cleared.
+              state = None
+          finally:
+            has_existing_exception = sys.exc_info() != (None, None, None)
+            try:
+              if state:
+                _CheckThermalThrottling(state.platform)
+              results.DidRunPage(story)
+            except Exception:
+              if not has_existing_exception:
+                raise
+              # Print current exception and propagate existing exception.
+              exception_formatter.PrintFormattedException(
+                  msg='Exception from result processing:')
+            if state and tear_down_after_story:
+              state.TearDownState()
+              state = None
+          if (effective_max_failures is not None and
+              len(results.failures) > effective_max_failures):
+            logging.error('Too many failures. Aborting.')
+            return
+        if state and tear_down_after_story_set:
+          state.TearDownState()
+          state = None
+    finally:
+      if state:
+        has_existing_exception = sys.exc_info() != (None, None, None)
+        try:
+          state.TearDownState()
+        except Exception:
+          if not has_existing_exception:
+            raise
+          # Print current exception and propagate existing exception.
+          exception_formatter.PrintFormattedException(
+              msg='Exception from TearDownState:')
+
+
+def ValidateStory(story):
+  if len(story.display_name) > 180:
+    raise ValueError(
+        'User story has display name exceeding 180 characters: %s' %
+        story.display_name)
+
+
+def RunBenchmark(benchmark, finder_options):
+  """Run this test with the given options.
+
+  Returns:
+    The number of failure values (up to 254) or 255 if there is an uncaught
+    exception.
+  """
+  start = time.time()
+  benchmark.CustomizeBrowserOptions(finder_options.browser_options)
+
+  benchmark_metadata = benchmark.GetMetadata()
+  possible_browser = browser_finder.FindBrowser(finder_options)
+  if not possible_browser:
+    print ('Cannot find browser of type %s. To list out all '
+           'available browsers, rerun your command with '
+           '--browser=list' %  finder_options.browser_options.browser_type)
+    return 1
+  if (possible_browser and
+    not decorators.IsBenchmarkEnabled(benchmark, possible_browser)):
+    print '%s is disabled on the selected browser' % benchmark.Name()
+    if finder_options.run_disabled_tests:
+      print 'Running benchmark anyway due to: --also-run-disabled-tests'
+    else:
+      print 'Try --also-run-disabled-tests to force the benchmark to run.'
+      # If chartjson is specified, this will print a dict indicating the
+      # benchmark name and disabled state.
+      with results_options.CreateResults(
+          benchmark_metadata, finder_options,
+          benchmark.ValueCanBeAddedPredicate, benchmark_enabled=False
+          ) as results:
+        results.PrintSummary()
+      # When a disabled benchmark is run we now want to return success since
+      # we are no longer filtering these out in the buildbot recipes.
+      return 0
+
+  pt = benchmark.CreatePageTest(finder_options)
+  pt.__name__ = benchmark.__class__.__name__
+
+  disabled_attr_name = decorators.DisabledAttributeName(benchmark)
+  # pylint: disable=protected-access
+  pt._disabled_strings = getattr(benchmark, disabled_attr_name, set())
+  if hasattr(benchmark, '_enabled_strings'):
+    # pylint: disable=protected-access
+    pt._enabled_strings = benchmark._enabled_strings
+
+  stories = benchmark.CreateStorySet(finder_options)
+
+  if isinstance(pt, legacy_page_test.LegacyPageTest):
+    if any(not isinstance(p, page.Page) for p in stories.stories):
+      raise Exception(
+          'PageTest must be used with StorySet containing only '
+          'telemetry.page.Page stories.')
+
+  should_tear_down_state_after_each_story_run = (
+      benchmark.ShouldTearDownStateAfterEachStoryRun())
+  # HACK: restarting shared state has huge overhead on cros (crbug.com/645329),
+  # hence we default this to False when test is run against CrOS.
+  # TODO(cros-team): figure out ways to remove this hack.
+  if (possible_browser.platform.GetOSName() == 'chromeos' and
+      not benchmark.IsShouldTearDownStateAfterEachStoryRunOverriden()):
+    should_tear_down_state_after_each_story_run = False
+
+  with results_options.CreateResults(
+      benchmark_metadata, finder_options,
+      benchmark.ValueCanBeAddedPredicate, benchmark_enabled=True) as results:
+    try:
+      Run(pt, stories, finder_options, results, benchmark.max_failures,
+          should_tear_down_state_after_each_story_run,
+          benchmark.ShouldTearDownStateAfterEachStorySetRun())
+      return_code = min(254, len(results.failures))
+    except Exception:
+      exception_formatter.PrintFormattedException()
+      return_code = 255
+
+    try:
+      if finder_options.upload_results:
+        bucket = finder_options.upload_bucket
+        if bucket in cloud_storage.BUCKET_ALIASES:
+          bucket = cloud_storage.BUCKET_ALIASES[bucket]
+        results.UploadTraceFilesToCloud(bucket)
+        results.UploadProfilingFilesToCloud(bucket)
+    finally:
+      duration = time.time() - start
+      results.AddSummaryValue(scalar.ScalarValue(
+          None, 'BenchmarkDuration', 'minutes', duration / 60.0))
+      results.PrintSummary()
+  return return_code
+
+
+def _UpdateAndCheckArchives(archive_data_file, wpr_archive_info,
+                            filtered_stories):
+  """Verifies that all stories are local or have WPR archives.
+
+  Logs warnings and returns False if any are missing.
+  """
+  # Report any problems with the entire story set.
+  if any(not story.is_local for story in filtered_stories):
+    if not archive_data_file:
+      logging.error('The story set is missing an "archive_data_file" '
+                    'property.\nTo run from live sites pass the flag '
+                    '--use-live-sites.\nTo create an archive file add an '
+                    'archive_data_file property to the story set and then '
+                    'run record_wpr.')
+      raise ArchiveError('No archive data file.')
+    if not wpr_archive_info:
+      logging.error('The archive info file is missing.\n'
+                    'To fix this, either add svn-internal to your '
+                    '.gclient using http://goto/read-src-internal, '
+                    'or create a new archive using record_wpr.')
+      raise ArchiveError('No archive info file.')
+    wpr_archive_info.DownloadArchivesIfNeeded()
+
+  # Report any problems with individual story.
+  stories_missing_archive_path = []
+  stories_missing_archive_data = []
+  for story in filtered_stories:
+    if not story.is_local:
+      archive_path = wpr_archive_info.WprFilePathForStory(story)
+      if not archive_path:
+        stories_missing_archive_path.append(story)
+      elif not os.path.isfile(archive_path):
+        stories_missing_archive_data.append(story)
+  if stories_missing_archive_path:
+    logging.error(
+        'The story set archives for some stories do not exist.\n'
+        'To fix this, record those stories using record_wpr.\n'
+        'To ignore this warning and run against live sites, '
+        'pass the flag --use-live-sites.')
+    logging.error(
+        'stories without archives: %s',
+        ', '.join(story.display_name
+                  for story in stories_missing_archive_path))
+  if stories_missing_archive_data:
+    logging.error(
+        'The story set archives for some stories are missing.\n'
+        'Someone forgot to check them in, uploaded them to the '
+        'wrong cloud storage bucket, or they were deleted.\n'
+        'To fix this, record those stories using record_wpr.\n'
+        'To ignore this warning and run against live sites, '
+        'pass the flag --use-live-sites.')
+    logging.error(
+        'stories missing archives: %s',
+        ', '.join(story.display_name
+                  for story in stories_missing_archive_data))
+  if stories_missing_archive_path or stories_missing_archive_data:
+    raise ArchiveError('Archive file is missing stories.')
+  # Only run valid stories if no problems with the story set or
+  # individual stories.
+  return True
+
+
+def _WaitForThermalThrottlingIfNeeded(platform):
+  if not platform.CanMonitorThermalThrottling():
+    return
+  thermal_throttling_retry = 0
+  while (platform.IsThermallyThrottled() and
+         thermal_throttling_retry < 3):
+    logging.warning('Thermally throttled, waiting (%d)...',
+                    thermal_throttling_retry)
+    thermal_throttling_retry += 1
+    time.sleep(thermal_throttling_retry * 2)
+
+  if thermal_throttling_retry and platform.IsThermallyThrottled():
+    logging.warning('Device is thermally throttled before running '
+                    'performance tests, results will vary.')
+
+
+def _CheckThermalThrottling(platform):
+  if not platform.CanMonitorThermalThrottling():
+    return
+  if platform.HasBeenThermallyThrottled():
+    logging.warning('Device has been thermally throttled during '
+                    'performance tests, results will vary.')
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/story_runner_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/story_runner_unittest.py
new file mode 100644
index 0000000..822092f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/story_runner_unittest.py
@@ -0,0 +1,1083 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import json
+import math
+import os
+import shutil
+import StringIO
+import sys
+import tempfile
+import unittest
+
+from py_utils import cloud_storage  # pylint: disable=import-error
+
+from telemetry import benchmark
+from telemetry.core import exceptions
+from telemetry.core import util
+from telemetry import decorators
+from telemetry.internal.actions import page_action
+from telemetry.internal.results import page_test_results
+from telemetry.internal.results import results_options
+from telemetry.internal import story_runner
+from telemetry.internal.util import exception_formatter as ex_formatter_module
+from telemetry.page import page as page_module
+from telemetry.page import legacy_page_test
+from telemetry import story as story_module
+from telemetry.testing import fakes
+from telemetry.testing import options_for_unittests
+from telemetry.testing import system_stub
+import mock
+from telemetry.value import failure
+from telemetry.value import improvement_direction
+from telemetry.value import list_of_scalar_values
+from telemetry.value import scalar
+from telemetry.value import skip
+from telemetry.value import summary as summary_module
+from telemetry.web_perf import story_test
+from telemetry.web_perf import timeline_based_measurement
+from telemetry.wpr import archive_info
+
+# This linter complains if we define classes nested inside functions.
+# pylint: disable=bad-super-call
+
+# pylint: disable=too-many-lines
+
+class FakePlatform(object):
+  def CanMonitorThermalThrottling(self):
+    return False
+
+  def GetOSName(self):
+    pass
+
+  def WaitForTemperature(self, _):
+    pass
+
+  def GetDeviceTypeName(self):
+    return "GetDeviceTypeName"
+
+class TestSharedState(story_module.SharedState):
+
+  _platform = FakePlatform()
+
+  @classmethod
+  def SetTestPlatform(cls, platform):
+    cls._platform = platform
+
+  def __init__(self, test, options, story_set):
+    super(TestSharedState, self).__init__(
+        test, options, story_set)
+    self._test = test
+    self._current_story = None
+
+  @property
+  def platform(self):
+    return self._platform
+
+  def WillRunStory(self, story):
+    self._current_story = story
+
+  def CanRunStory(self, story):
+    return True
+
+  def RunStory(self, results):
+    raise NotImplementedError
+
+  def DidRunStory(self, results):
+    pass
+
+  def TearDownState(self):
+    pass
+
+  def DumpStateUponFailure(self, story, results):
+    pass
+
+
+class TestSharedPageState(TestSharedState):
+  def RunStory(self, results):
+    self._test.RunPage(self._current_story, None, results)
+
+
+class FooStoryState(TestSharedPageState):
+  pass
+
+
+class BarStoryState(TestSharedPageState):
+  pass
+
+
+class DummyTest(legacy_page_test.LegacyPageTest):
+  def RunPage(self, *_):
+    pass
+
+  def ValidateAndMeasurePage(self, page, tab, results):
+    pass
+
+
+class EmptyMetadataForTest(benchmark.BenchmarkMetadata):
+  def __init__(self):
+    super(EmptyMetadataForTest, self).__init__('')
+
+
+class DummyLocalStory(story_module.Story):
+  def __init__(self, shared_state_class, name=''):
+    super(DummyLocalStory, self).__init__(
+        shared_state_class, name=name)
+
+  def Run(self, shared_state):
+    pass
+
+  @property
+  def is_local(self):
+    return True
+
+  @property
+  def url(self):
+    return 'data:,'
+
+
+class MixedStateStorySet(story_module.StorySet):
+  @property
+  def allow_mixed_story_states(self):
+    return True
+
+
+def SetupStorySet(allow_multiple_story_states, story_state_list):
+  if allow_multiple_story_states:
+    story_set = MixedStateStorySet()
+  else:
+    story_set = story_module.StorySet()
+  for i, story_state in enumerate(story_state_list):
+    story_set.AddStory(DummyLocalStory(story_state,
+                                       name='story%d' % i))
+  return story_set
+
+class FakeBenchmark(benchmark.Benchmark):
+  @classmethod
+  def Name(cls):
+    return 'fake'
+
+  test = DummyTest
+
+  def page_set(self):
+    return story_module.StorySet()
+
+
+def _GetOptionForUnittest():
+  options = options_for_unittests.GetCopy()
+  options.output_formats = ['none']
+  options.suppress_gtest_report = False
+  parser = options.CreateParser()
+  story_runner.AddCommandLineArgs(parser)
+  options.MergeDefaultValues(parser.get_default_values())
+  story_runner.ProcessCommandLineArgs(parser, options)
+  return options
+
+
+class FakeExceptionFormatterModule(object):
+  @staticmethod
+  def PrintFormattedException(
+      exception_class=None, exception=None, tb=None, msg=None):
+    pass
+
+
+def GetNumberOfSuccessfulPageRuns(results):
+  return len([run for run in results.all_page_runs if run.ok or run.skipped])
+
+
+class TestOnlyException(Exception):
+  pass
+
+
+class FailureValueMatcher(object):
+  def __init__(self, expected_exception_message):
+    self._expected_exception_message = expected_exception_message
+
+  def __eq__(self, other):
+    return (isinstance(other, failure.FailureValue) and
+            other.exc_info[1].message == self._expected_exception_message)
+
+
+class SkipValueMatcher(object):
+  def __eq__(self, other):
+    return isinstance(other, skip.SkipValue)
+
+
+class StoryRunnerTest(unittest.TestCase):
+  def setUp(self):
+    self.fake_stdout = StringIO.StringIO()
+    self.actual_stdout = sys.stdout
+    sys.stdout = self.fake_stdout
+    self.options = _GetOptionForUnittest()
+    self.results = results_options.CreateResults(
+        EmptyMetadataForTest(), self.options)
+    self._story_runner_logging_stub = None
+
+  def SuppressExceptionFormatting(self):
+    """Fake out exception formatter to avoid spamming the unittest stdout."""
+    story_runner.exception_formatter = FakeExceptionFormatterModule
+    self._story_runner_logging_stub = system_stub.Override(
+      story_runner, ['logging'])
+
+  def RestoreExceptionFormatter(self):
+    story_runner.exception_formatter = ex_formatter_module
+    if self._story_runner_logging_stub:
+      self._story_runner_logging_stub.Restore()
+      self._story_runner_logging_stub = None
+
+  def tearDown(self):
+    sys.stdout = self.actual_stdout
+    self.RestoreExceptionFormatter()
+
+  def testStoriesGroupedByStateClass(self):
+    foo_states = [FooStoryState, FooStoryState, FooStoryState,
+                  FooStoryState, FooStoryState]
+    mixed_states = [FooStoryState, FooStoryState, FooStoryState,
+                    BarStoryState, FooStoryState]
+    # StorySet's are only allowed to have one SharedState.
+    story_set = SetupStorySet(False, foo_states)
+    story_groups = (
+        story_runner.StoriesGroupedByStateClass(
+            story_set, False))
+    self.assertEqual(len(story_groups), 1)
+    story_set = SetupStorySet(False, mixed_states)
+    self.assertRaises(
+        ValueError,
+        story_runner.StoriesGroupedByStateClass,
+        story_set, False)
+    # BaseStorySets are allowed to have multiple SharedStates.
+    mixed_story_set = SetupStorySet(True, mixed_states)
+    story_groups = (
+        story_runner.StoriesGroupedByStateClass(
+            mixed_story_set, True))
+    self.assertEqual(len(story_groups), 3)
+    self.assertEqual(story_groups[0].shared_state_class,
+                     FooStoryState)
+    self.assertEqual(story_groups[1].shared_state_class,
+                     BarStoryState)
+    self.assertEqual(story_groups[2].shared_state_class,
+                     FooStoryState)
+
+  def RunStoryTest(self, s, expected_successes):
+    test = DummyTest()
+    story_runner.Run(
+        test, s, self.options, self.results)
+    self.assertEquals(0, len(self.results.failures))
+    self.assertEquals(expected_successes,
+                      GetNumberOfSuccessfulPageRuns(self.results))
+
+  def testRunStoryWithMissingArchiveFile(self):
+    story_set = story_module.StorySet(archive_data_file='data/hi.json')
+    story_set.AddStory(page_module.Page(
+        'http://www.testurl.com', story_set, story_set.base_dir))
+    test = DummyTest()
+    self.assertRaises(story_runner.ArchiveError, story_runner.Run, test,
+                      story_set, self.options, self.results)
+
+  def testStoryTest(self):
+    all_foo = [FooStoryState, FooStoryState, FooStoryState]
+    one_bar = [FooStoryState, FooStoryState, BarStoryState]
+    story_set = SetupStorySet(True, one_bar)
+    self.RunStoryTest(story_set, 3)
+    story_set = SetupStorySet(True, all_foo)
+    self.RunStoryTest(story_set, 6)
+    story_set = SetupStorySet(False, all_foo)
+    self.RunStoryTest(story_set, 9)
+    story_set = SetupStorySet(False, one_bar)
+    test = DummyTest()
+    self.assertRaises(ValueError, story_runner.Run, test, story_set,
+                      self.options, self.results)
+
+  def testRunStoryWithLongName(self):
+    story_set = story_module.StorySet()
+    story_set.AddStory(DummyLocalStory(FooStoryState, name='l' * 182))
+    test = DummyTest()
+    self.assertRaises(ValueError, story_runner.Run, test, story_set,
+                      self.options, self.results)
+
+  def testRunStoryWithLongURLPage(self):
+    story_set = story_module.StorySet()
+    story_set.AddStory(page_module.Page('file://long' + 'g' * 180, story_set))
+    test = DummyTest()
+    self.assertRaises(ValueError, story_runner.Run, test, story_set,
+                      self.options, self.results)
+
+  def testSuccessfulTimelineBasedMeasurementTest(self):
+    """Check that PageTest is not required for story_runner.Run.
+
+    Any PageTest related calls or attributes need to only be called
+    for PageTest tests.
+    """
+    class TestSharedTbmState(TestSharedState):
+      def RunStory(self, results):
+        pass
+
+    TEST_WILL_RUN_STORY = 'test.WillRunStory'
+    TEST_MEASURE = 'test.Measure'
+    TEST_DID_RUN_STORY = 'test.DidRunStory'
+
+    EXPECTED_CALLS_IN_ORDER = [TEST_WILL_RUN_STORY,
+                               TEST_MEASURE,
+                               TEST_DID_RUN_STORY]
+
+    test = timeline_based_measurement.TimelineBasedMeasurement(
+        timeline_based_measurement.Options())
+
+    manager = mock.MagicMock()
+    test.WillRunStory = mock.MagicMock()
+    test.Measure = mock.MagicMock()
+    test.DidRunStory = mock.MagicMock()
+    manager.attach_mock(test.WillRunStory, TEST_WILL_RUN_STORY)
+    manager.attach_mock(test.Measure, TEST_MEASURE)
+    manager.attach_mock(test.DidRunStory, TEST_DID_RUN_STORY)
+
+    story_set = story_module.StorySet()
+    story_set.AddStory(DummyLocalStory(TestSharedTbmState, name='foo'))
+    story_set.AddStory(DummyLocalStory(TestSharedTbmState, name='bar'))
+    story_set.AddStory(DummyLocalStory(TestSharedTbmState, name='baz'))
+    story_runner.Run(
+        test, story_set, self.options, self.results)
+    self.assertEquals(0, len(self.results.failures))
+    self.assertEquals(3, GetNumberOfSuccessfulPageRuns(self.results))
+
+    self.assertEquals(3*EXPECTED_CALLS_IN_ORDER,
+                      [call[0] for call in manager.mock_calls])
+
+  def testCallOrderBetweenStoryTestAndSharedState(self):
+    """Check that the call order between StoryTest and SharedState is correct.
+    """
+    TEST_WILL_RUN_STORY = 'test.WillRunStory'
+    TEST_MEASURE = 'test.Measure'
+    TEST_DID_RUN_STORY = 'test.DidRunStory'
+    STATE_WILL_RUN_STORY = 'state.WillRunStory'
+    STATE_RUN_STORY = 'state.RunStory'
+    STATE_DID_RUN_STORY = 'state.DidRunStory'
+
+    EXPECTED_CALLS_IN_ORDER = [TEST_WILL_RUN_STORY,
+                               STATE_WILL_RUN_STORY,
+                               STATE_RUN_STORY,
+                               TEST_MEASURE,
+                               STATE_DID_RUN_STORY,
+                               TEST_DID_RUN_STORY]
+
+    class TestStoryTest(story_test.StoryTest):
+      def WillRunStory(self, platform):
+        pass
+
+      def Measure(self, platform, results):
+        pass
+
+      def DidRunStory(self, platform):
+        pass
+
+    class TestSharedStateForStoryTest(TestSharedState):
+      def RunStory(self, results):
+        pass
+
+    @mock.patch.object(TestStoryTest, 'WillRunStory')
+    @mock.patch.object(TestStoryTest, 'Measure')
+    @mock.patch.object(TestStoryTest, 'DidRunStory')
+    @mock.patch.object(TestSharedStateForStoryTest, 'WillRunStory')
+    @mock.patch.object(TestSharedStateForStoryTest, 'RunStory')
+    @mock.patch.object(TestSharedStateForStoryTest, 'DidRunStory')
+    def GetCallsInOrder(state_DidRunStory, state_RunStory, state_WillRunStory,
+                        test_DidRunStory, test_Measure, test_WillRunStory):
+      manager = mock.MagicMock()
+      manager.attach_mock(test_WillRunStory, TEST_WILL_RUN_STORY)
+      manager.attach_mock(test_Measure, TEST_MEASURE)
+      manager.attach_mock(test_DidRunStory, TEST_DID_RUN_STORY)
+      manager.attach_mock(state_WillRunStory, STATE_WILL_RUN_STORY)
+      manager.attach_mock(state_RunStory, STATE_RUN_STORY)
+      manager.attach_mock(state_DidRunStory, STATE_DID_RUN_STORY)
+
+      test = TestStoryTest()
+      story_set = story_module.StorySet()
+      story_set.AddStory(DummyLocalStory(TestSharedStateForStoryTest))
+      story_runner.Run(test, story_set, self.options, self.results)
+      return [call[0] for call in manager.mock_calls]
+
+    calls_in_order = GetCallsInOrder() # pylint: disable=no-value-for-parameter
+    self.assertEquals(EXPECTED_CALLS_IN_ORDER, calls_in_order)
+
+  def testTearDownStateAfterEachStoryOrStorySetRun(self):
+    class TestSharedStateForTearDown(TestSharedState):
+      num_of_tear_downs = 0
+
+      def RunStory(self, results):
+        pass
+
+      def TearDownState(self):
+        TestSharedStateForTearDown.num_of_tear_downs += 1
+
+    story_set = story_module.StorySet()
+    story_set.AddStory(DummyLocalStory(TestSharedStateForTearDown, name='foo'))
+    story_set.AddStory(DummyLocalStory(TestSharedStateForTearDown, name='bar'))
+    story_set.AddStory(DummyLocalStory(TestSharedStateForTearDown, name='baz'))
+
+    TestSharedStateForTearDown.num_of_tear_downs = 0
+    story_runner.Run(mock.MagicMock(), story_set, self.options, self.results)
+    self.assertEquals(TestSharedStateForTearDown.num_of_tear_downs, 1)
+
+    TestSharedStateForTearDown.num_of_tear_downs = 0
+    story_runner.Run(mock.MagicMock(), story_set, self.options, self.results,
+                     tear_down_after_story=True)
+    self.assertEquals(TestSharedStateForTearDown.num_of_tear_downs, 3)
+
+    self.options.pageset_repeat = 5
+    TestSharedStateForTearDown.num_of_tear_downs = 0
+    story_runner.Run(mock.MagicMock(), story_set, self.options, self.results,
+                     tear_down_after_story_set=True)
+    self.assertEquals(TestSharedStateForTearDown.num_of_tear_downs, 5)
+
+  def testTearDownIsCalledOnceForEachStoryGroupWithPageSetRepeat(self):
+    self.options.pageset_repeat = 3
+    fooz_init_call_counter = [0]
+    fooz_tear_down_call_counter = [0]
+    barz_init_call_counter = [0]
+    barz_tear_down_call_counter = [0]
+    class FoozStoryState(FooStoryState):
+      def __init__(self, test, options, storyz):
+        super(FoozStoryState, self).__init__(
+          test, options, storyz)
+        fooz_init_call_counter[0] += 1
+      def TearDownState(self):
+        fooz_tear_down_call_counter[0] += 1
+
+    class BarzStoryState(BarStoryState):
+      def __init__(self, test, options, storyz):
+        super(BarzStoryState, self).__init__(
+          test, options, storyz)
+        barz_init_call_counter[0] += 1
+      def TearDownState(self):
+        barz_tear_down_call_counter[0] += 1
+    def AssertAndCleanUpFoo():
+      self.assertEquals(1, fooz_init_call_counter[0])
+      self.assertEquals(1, fooz_tear_down_call_counter[0])
+      fooz_init_call_counter[0] = 0
+      fooz_tear_down_call_counter[0] = 0
+
+    story_set1_list = [FoozStoryState, FoozStoryState, FoozStoryState,
+                       BarzStoryState, BarzStoryState]
+    story_set1 = SetupStorySet(True, story_set1_list)
+    self.RunStoryTest(story_set1, 15)
+    AssertAndCleanUpFoo()
+    self.assertEquals(1, barz_init_call_counter[0])
+    self.assertEquals(1, barz_tear_down_call_counter[0])
+    barz_init_call_counter[0] = 0
+    barz_tear_down_call_counter[0] = 0
+
+    story_set2_list = [FoozStoryState, FoozStoryState, FoozStoryState,
+                       FoozStoryState]
+    story_set2 = SetupStorySet(False, story_set2_list)
+    self.RunStoryTest(story_set2, 27)
+    AssertAndCleanUpFoo()
+    self.assertEquals(0, barz_init_call_counter[0])
+    self.assertEquals(0, barz_tear_down_call_counter[0])
+
+  def testAppCrashExceptionCausesFailureValue(self):
+    self.SuppressExceptionFormatting()
+    story_set = story_module.StorySet()
+    class SharedStoryThatCausesAppCrash(TestSharedPageState):
+      def WillRunStory(self, story):
+        raise exceptions.AppCrashException(msg='App Foo crashes')
+
+    story_set.AddStory(DummyLocalStory(
+          SharedStoryThatCausesAppCrash))
+    story_runner.Run(
+        DummyTest(), story_set, self.options, self.results)
+    self.assertEquals(1, len(self.results.failures))
+    self.assertEquals(0, GetNumberOfSuccessfulPageRuns(self.results))
+    self.assertIn('App Foo crashes', self.fake_stdout.getvalue())
+
+  def testExceptionRaisedInSharedStateTearDown(self):
+    self.SuppressExceptionFormatting()
+    story_set = story_module.StorySet()
+    class SharedStoryThatCausesAppCrash(TestSharedPageState):
+      def TearDownState(self):
+        raise TestOnlyException()
+
+    story_set.AddStory(DummyLocalStory(
+          SharedStoryThatCausesAppCrash))
+    with self.assertRaises(TestOnlyException):
+      story_runner.Run(
+          DummyTest(), story_set, self.options, self.results)
+
+  def testUnknownExceptionIsFatal(self):
+    self.SuppressExceptionFormatting()
+    story_set = story_module.StorySet()
+
+    class UnknownException(Exception):
+      pass
+
+    # This erroneous test is set up to raise exception for the 2nd story
+    # run.
+    class Test(legacy_page_test.LegacyPageTest):
+      def __init__(self, *args):
+        super(Test, self).__init__(*args)
+        self.run_count = 0
+
+      def RunPage(self, *_):
+        old_run_count = self.run_count
+        self.run_count += 1
+        if old_run_count == 1:
+          raise UnknownException('FooBarzException')
+
+      def ValidateAndMeasurePage(self, page, tab, results):
+        pass
+
+    s1 = DummyLocalStory(TestSharedPageState, name='foo')
+    s2 = DummyLocalStory(TestSharedPageState, name='bar')
+    story_set.AddStory(s1)
+    story_set.AddStory(s2)
+    test = Test()
+    with self.assertRaises(UnknownException):
+      story_runner.Run(
+          test, story_set, self.options, self.results)
+    self.assertEqual(set([s2]), self.results.pages_that_failed)
+    self.assertEqual(set([s1]), self.results.pages_that_succeeded)
+    self.assertIn('FooBarzException', self.fake_stdout.getvalue())
+
+  def testRaiseBrowserGoneExceptionFromRunPage(self):
+    self.SuppressExceptionFormatting()
+    story_set = story_module.StorySet()
+
+    class Test(legacy_page_test.LegacyPageTest):
+      def __init__(self, *args):
+        super(Test, self).__init__(*args)
+        self.run_count = 0
+
+      def RunPage(self, *_):
+        old_run_count = self.run_count
+        self.run_count += 1
+        if old_run_count == 0:
+          raise exceptions.BrowserGoneException(
+              None, 'i am a browser crash message')
+
+      def ValidateAndMeasurePage(self, page, tab, results):
+        pass
+
+    story_set.AddStory(DummyLocalStory(TestSharedPageState, name='foo'))
+    story_set.AddStory(DummyLocalStory(TestSharedPageState, name='bar'))
+    test = Test()
+    story_runner.Run(
+        test, story_set, self.options, self.results)
+    self.assertEquals(2, test.run_count)
+    self.assertEquals(1, len(self.results.failures))
+    self.assertEquals(1, GetNumberOfSuccessfulPageRuns(self.results))
+
+  def testAppCrashThenRaiseInTearDownFatal(self):
+    self.SuppressExceptionFormatting()
+    story_set = story_module.StorySet()
+
+    unit_test_events = []  # track what was called when
+    class DidRunTestError(Exception):
+      pass
+
+    class TestTearDownSharedState(TestSharedPageState):
+      def TearDownState(self):
+        unit_test_events.append('tear-down-state')
+        raise DidRunTestError
+
+      def DumpStateUponFailure(self, story, results):
+        unit_test_events.append('dump-state')
+
+
+    class Test(legacy_page_test.LegacyPageTest):
+      def __init__(self, *args):
+        super(Test, self).__init__(*args)
+        self.run_count = 0
+
+      def RunPage(self, *_):
+        old_run_count = self.run_count
+        self.run_count += 1
+        if old_run_count == 0:
+          unit_test_events.append('app-crash')
+          raise exceptions.AppCrashException
+
+      def ValidateAndMeasurePage(self, page, tab, results):
+        pass
+
+    story_set.AddStory(DummyLocalStory(TestTearDownSharedState, name='foo'))
+    story_set.AddStory(DummyLocalStory(TestTearDownSharedState, name='bar'))
+    test = Test()
+
+    with self.assertRaises(DidRunTestError):
+      story_runner.Run(
+          test, story_set, self.options, self.results)
+    self.assertEqual(['app-crash', 'dump-state', 'tear-down-state'],
+                     unit_test_events)
+    # The AppCrashException gets added as a failure.
+    self.assertEquals(1, len(self.results.failures))
+
+  def testPagesetRepeat(self):
+    story_set = story_module.StorySet()
+
+    # TODO(eakuefner): Factor this out after flattening page ref in Value
+    blank_story = DummyLocalStory(TestSharedPageState, name='blank')
+    green_story = DummyLocalStory(TestSharedPageState, name='green')
+    story_set.AddStory(blank_story)
+    story_set.AddStory(green_story)
+
+    class Measurement(legacy_page_test.LegacyPageTest):
+      i = 0
+      def RunPage(self, page, _, results):
+        self.i += 1
+        results.AddValue(scalar.ScalarValue(
+            page, 'metric', 'unit', self.i,
+            improvement_direction=improvement_direction.UP))
+
+      def ValidateAndMeasurePage(self, page, tab, results):
+        pass
+
+    self.options.pageset_repeat = 2
+    self.options.output_formats = []
+    results = results_options.CreateResults(
+      EmptyMetadataForTest(), self.options)
+    story_runner.Run(
+        Measurement(), story_set, self.options, results)
+    summary = summary_module.Summary(results.all_page_specific_values)
+    values = summary.interleaved_computed_per_page_values_and_summaries
+
+    blank_value = list_of_scalar_values.ListOfScalarValues(
+        blank_story, 'metric', 'unit', [1, 3],
+        improvement_direction=improvement_direction.UP)
+    green_value = list_of_scalar_values.ListOfScalarValues(
+        green_story, 'metric', 'unit', [2, 4],
+        improvement_direction=improvement_direction.UP)
+    merged_value = list_of_scalar_values.ListOfScalarValues(
+        None, 'metric', 'unit',
+        [1, 3, 2, 4], std=math.sqrt(2),  # Pooled standard deviation.
+        improvement_direction=improvement_direction.UP)
+
+    self.assertEquals(4, GetNumberOfSuccessfulPageRuns(results))
+    self.assertEquals(0, len(results.failures))
+    self.assertEquals(3, len(values))
+    self.assertIn(blank_value, values)
+    self.assertIn(green_value, values)
+    self.assertIn(merged_value, values)
+
+  @decorators.Disabled('chromeos')  # crbug.com/483212
+  def testUpdateAndCheckArchives(self):
+    usr_stub = system_stub.Override(story_runner, ['cloud_storage'])
+    wpr_stub = system_stub.Override(archive_info, ['cloud_storage'])
+    archive_data_dir = os.path.join(
+        util.GetTelemetryDir(),
+        'telemetry', 'internal', 'testing', 'archive_files')
+    try:
+      story_set = story_module.StorySet()
+      story_set.AddStory(page_module.Page(
+          'http://www.testurl.com', story_set, story_set.base_dir))
+      # Page set missing archive_data_file.
+      self.assertRaises(
+          story_runner.ArchiveError,
+          story_runner._UpdateAndCheckArchives,
+          story_set.archive_data_file,
+          story_set.wpr_archive_info,
+          story_set.stories)
+
+      story_set = story_module.StorySet(
+          archive_data_file='missing_archive_data_file.json')
+      story_set.AddStory(page_module.Page(
+          'http://www.testurl.com', story_set, story_set.base_dir))
+      # Page set missing json file specified in archive_data_file.
+      self.assertRaises(
+          story_runner.ArchiveError,
+          story_runner._UpdateAndCheckArchives,
+          story_set.archive_data_file,
+          story_set.wpr_archive_info,
+          story_set.stories)
+
+      story_set = story_module.StorySet(
+          archive_data_file=os.path.join(archive_data_dir, 'test.json'),
+          cloud_storage_bucket=cloud_storage.PUBLIC_BUCKET)
+      story_set.AddStory(page_module.Page(
+          'http://www.testurl.com', story_set, story_set.base_dir))
+      # Page set with valid archive_data_file.
+      self.assertTrue(story_runner._UpdateAndCheckArchives(
+            story_set.archive_data_file, story_set.wpr_archive_info,
+            story_set.stories))
+      story_set.AddStory(page_module.Page(
+          'http://www.google.com', story_set, story_set.base_dir))
+      # Page set with an archive_data_file which exists but is missing a page.
+      self.assertRaises(
+          story_runner.ArchiveError,
+          story_runner._UpdateAndCheckArchives,
+          story_set.archive_data_file,
+          story_set.wpr_archive_info,
+          story_set.stories)
+
+      story_set = story_module.StorySet(
+          archive_data_file=
+              os.path.join(archive_data_dir, 'test_missing_wpr_file.json'),
+          cloud_storage_bucket=cloud_storage.PUBLIC_BUCKET)
+      story_set.AddStory(page_module.Page(
+          'http://www.testurl.com', story_set, story_set.base_dir))
+      story_set.AddStory(page_module.Page(
+          'http://www.google.com', story_set, story_set.base_dir))
+      # Page set with an archive_data_file which exists and contains all pages
+      # but fails to find a wpr file.
+      self.assertRaises(
+          story_runner.ArchiveError,
+          story_runner._UpdateAndCheckArchives,
+          story_set.archive_data_file,
+          story_set.wpr_archive_info,
+          story_set.stories)
+    finally:
+      usr_stub.Restore()
+      wpr_stub.Restore()
+
+
+  def _testMaxFailuresOptionIsRespectedAndOverridable(
+      self, num_failing_stories, runner_max_failures, options_max_failures,
+      expected_num_failures):
+    class SimpleSharedState(story_module.SharedState):
+      _fake_platform = FakePlatform()
+      _current_story = None
+
+      @property
+      def platform(self):
+        return self._fake_platform
+
+      def WillRunStory(self, story):
+        self._current_story = story
+
+      def RunStory(self, results):
+        self._current_story.Run(self)
+
+      def DidRunStory(self, results):
+        pass
+
+      def CanRunStory(self, story):
+        return True
+
+      def TearDownState(self):
+        pass
+
+      def DumpStateUponFailure(self, story, results):
+        pass
+
+    class FailingStory(story_module.Story):
+      def __init__(self, name):
+        super(FailingStory, self).__init__(
+            shared_state_class=SimpleSharedState,
+            is_local=True, name=name)
+        self.was_run = False
+
+      def Run(self, shared_state):
+        self.was_run = True
+        raise legacy_page_test.Failure
+
+      @property
+      def url(self):
+        return 'data:,'
+
+    self.SuppressExceptionFormatting()
+
+    story_set = story_module.StorySet()
+    for i in range(num_failing_stories):
+      story_set.AddStory(FailingStory(name='failing%d' % i))
+
+    options = _GetOptionForUnittest()
+    options.output_formats = ['none']
+    options.suppress_gtest_report = True
+    if options_max_failures:
+      options.max_failures = options_max_failures
+
+    results = results_options.CreateResults(EmptyMetadataForTest(), options)
+    story_runner.Run(
+        DummyTest(), story_set, options,
+        results, max_failures=runner_max_failures)
+    self.assertEquals(0, GetNumberOfSuccessfulPageRuns(results))
+    self.assertEquals(expected_num_failures, len(results.failures))
+    for ii, story in enumerate(story_set.stories):
+      self.assertEqual(story.was_run, ii < expected_num_failures)
+
+  def testMaxFailuresNotSpecified(self):
+    self._testMaxFailuresOptionIsRespectedAndOverridable(
+        num_failing_stories=5, runner_max_failures=None,
+        options_max_failures=None, expected_num_failures=5)
+
+  def testMaxFailuresSpecifiedToRun(self):
+    # Runs up to max_failures+1 failing tests before stopping, since
+    # every tests after max_failures failures have been encountered
+    # may all be passing.
+    self._testMaxFailuresOptionIsRespectedAndOverridable(
+        num_failing_stories=5, runner_max_failures=3,
+        options_max_failures=None, expected_num_failures=4)
+
+  def testMaxFailuresOption(self):
+    # Runs up to max_failures+1 failing tests before stopping, since
+    # every tests after max_failures failures have been encountered
+    # may all be passing.
+    self._testMaxFailuresOptionIsRespectedAndOverridable(
+        num_failing_stories=5, runner_max_failures=3,
+        options_max_failures=1, expected_num_failures=2)
+
+  def _CreateErrorProcessingMock(self, method_exceptions=None,
+                                 legacy_test=False):
+    if legacy_test:
+      test_class = legacy_page_test.LegacyPageTest
+    else:
+      test_class = story_test.StoryTest
+
+    root_mock = mock.NonCallableMock(
+        story=mock.NonCallableMagicMock(story_module.Story),
+        results=mock.NonCallableMagicMock(page_test_results.PageTestResults),
+        test=mock.NonCallableMagicMock(test_class),
+        state=mock.NonCallableMagicMock(
+            story_module.SharedState,
+            CanRunStory=mock.Mock(return_value=True)))
+
+    if method_exceptions:
+      root_mock.configure_mock(**{
+          path + '.side_effect': exception
+          for path, exception in method_exceptions.iteritems()})
+
+    return root_mock
+
+  def testRunStoryAndProcessErrorIfNeeded_success(self):
+    root_mock = self._CreateErrorProcessingMock()
+
+    story_runner._RunStoryAndProcessErrorIfNeeded(
+        root_mock.story, root_mock.results, root_mock.state, root_mock.test)
+
+    self.assertEquals(root_mock.method_calls, [
+      mock.call.state.platform.GetOSName(),
+      mock.call.test.WillRunStory(root_mock.state.platform),
+      mock.call.state.WillRunStory(root_mock.story),
+      mock.call.state.CanRunStory(root_mock.story),
+      mock.call.state.RunStory(root_mock.results),
+      mock.call.test.Measure(root_mock.state.platform, root_mock.results),
+      mock.call.state.DidRunStory(root_mock.results),
+      mock.call.test.DidRunStory(root_mock.state.platform),
+      mock.call.state.platform.GetOSName(),
+    ])
+
+  def testRunStoryAndProcessErrorIfNeeded_successLegacy(self):
+    root_mock = self._CreateErrorProcessingMock(legacy_test=True)
+
+    story_runner._RunStoryAndProcessErrorIfNeeded(
+        root_mock.story, root_mock.results, root_mock.state, root_mock.test)
+
+    self.assertEquals(root_mock.method_calls, [
+      mock.call.state.platform.GetOSName(),
+      mock.call.state.WillRunStory(root_mock.story),
+      mock.call.state.CanRunStory(root_mock.story),
+      mock.call.state.RunStory(root_mock.results),
+      mock.call.state.DidRunStory(root_mock.results),
+      mock.call.test.DidRunPage(root_mock.state.platform),
+      mock.call.state.platform.GetOSName(),
+    ])
+
+  def testRunStoryAndProcessErrorIfNeeded_tryTimeout(self):
+    root_mock = self._CreateErrorProcessingMock(method_exceptions={
+      'state.WillRunStory': exceptions.TimeoutException('foo')
+    })
+
+    story_runner._RunStoryAndProcessErrorIfNeeded(
+        root_mock.story, root_mock.results, root_mock.state, root_mock.test)
+
+    self.assertEquals(root_mock.method_calls, [
+      mock.call.state.platform.GetOSName(),
+      mock.call.test.WillRunStory(root_mock.state.platform),
+      mock.call.state.WillRunStory(root_mock.story),
+      mock.call.state.DumpStateUponFailure(root_mock.story, root_mock.results),
+      mock.call.results.AddValue(FailureValueMatcher('foo')),
+      mock.call.state.DidRunStory(root_mock.results),
+      mock.call.test.DidRunStory(root_mock.state.platform),
+      mock.call.state.platform.GetOSName(),
+    ])
+
+  def testRunStoryAndProcessErrorIfNeeded_tryError(self):
+    root_mock = self._CreateErrorProcessingMock(method_exceptions={
+      'state.CanRunStory': exceptions.Error('foo')
+    })
+
+    with self.assertRaisesRegexp(exceptions.Error, 'foo'):
+      story_runner._RunStoryAndProcessErrorIfNeeded(
+          root_mock.story, root_mock.results, root_mock.state, root_mock.test)
+
+    self.assertEquals(root_mock.method_calls, [
+      mock.call.state.platform.GetOSName(),
+      mock.call.test.WillRunStory(root_mock.state.platform),
+      mock.call.state.WillRunStory(root_mock.story),
+      mock.call.state.CanRunStory(root_mock.story),
+      mock.call.state.DumpStateUponFailure(root_mock.story, root_mock.results),
+      mock.call.results.AddValue(FailureValueMatcher('foo')),
+      mock.call.state.DidRunStory(root_mock.results),
+      mock.call.test.DidRunStory(root_mock.state.platform),
+      mock.call.state.platform.GetOSName(),
+    ])
+
+  def testRunStoryAndProcessErrorIfNeeded_tryUnsupportedAction(self):
+    root_mock = self._CreateErrorProcessingMock(method_exceptions={
+      'state.RunStory': page_action.PageActionNotSupported('foo')
+    })
+
+    story_runner._RunStoryAndProcessErrorIfNeeded(
+        root_mock.story, root_mock.results, root_mock.state, root_mock.test)
+
+    self.assertEquals(root_mock.method_calls, [
+      mock.call.state.platform.GetOSName(),
+      mock.call.test.WillRunStory(root_mock.state.platform),
+      mock.call.state.WillRunStory(root_mock.story),
+      mock.call.state.CanRunStory(root_mock.story),
+      mock.call.state.RunStory(root_mock.results),
+      mock.call.results.AddValue(SkipValueMatcher()),
+      mock.call.state.DidRunStory(root_mock.results),
+      mock.call.test.DidRunStory(root_mock.state.platform),
+      mock.call.state.platform.GetOSName(),
+    ])
+
+  def testRunStoryAndProcessErrorIfNeeded_tryUnhandlable(self):
+    root_mock = self._CreateErrorProcessingMock(method_exceptions={
+      'test.WillRunStory': Exception('foo')
+    })
+
+    with self.assertRaisesRegexp(Exception, 'foo'):
+      story_runner._RunStoryAndProcessErrorIfNeeded(
+          root_mock.story, root_mock.results, root_mock.state, root_mock.test)
+
+    self.assertEquals(root_mock.method_calls, [
+      mock.call.state.platform.GetOSName(),
+      mock.call.test.WillRunStory(root_mock.state.platform),
+      mock.call.state.DumpStateUponFailure(root_mock.story, root_mock.results),
+      mock.call.results.AddValue(FailureValueMatcher('foo')),
+      mock.call.state.DidRunStory(root_mock.results),
+      mock.call.test.DidRunStory(root_mock.state.platform),
+      mock.call.state.platform.GetOSName(),
+    ])
+
+  def testRunStoryAndProcessErrorIfNeeded_finallyException(self):
+    root_mock = self._CreateErrorProcessingMock(method_exceptions={
+      'state.DidRunStory': Exception('bar')
+    })
+
+    with self.assertRaisesRegexp(Exception, 'bar'):
+      story_runner._RunStoryAndProcessErrorIfNeeded(
+          root_mock.story, root_mock.results, root_mock.state, root_mock.test)
+
+    self.assertEquals(root_mock.method_calls, [
+      mock.call.state.platform.GetOSName(),
+      mock.call.test.WillRunStory(root_mock.state.platform),
+      mock.call.state.WillRunStory(root_mock.story),
+      mock.call.state.CanRunStory(root_mock.story),
+      mock.call.state.RunStory(root_mock.results),
+      mock.call.test.Measure(root_mock.state.platform, root_mock.results),
+      mock.call.state.DidRunStory(root_mock.results),
+      mock.call.state.DumpStateUponFailure(root_mock.story, root_mock.results)
+    ])
+
+  def testRunStoryAndProcessErrorIfNeeded_tryTimeout_finallyException(self):
+    root_mock = self._CreateErrorProcessingMock(method_exceptions={
+      'state.RunStory': exceptions.TimeoutException('foo'),
+      'state.DidRunStory': Exception('bar')
+    })
+
+    story_runner._RunStoryAndProcessErrorIfNeeded(
+        root_mock.story, root_mock.results, root_mock.state, root_mock.test)
+
+    self.assertEquals(root_mock.method_calls, [
+      mock.call.state.platform.GetOSName(),
+      mock.call.test.WillRunStory(root_mock.state.platform),
+      mock.call.state.WillRunStory(root_mock.story),
+      mock.call.state.CanRunStory(root_mock.story),
+      mock.call.state.RunStory(root_mock.results),
+      mock.call.state.DumpStateUponFailure(root_mock.story, root_mock.results),
+      mock.call.results.AddValue(FailureValueMatcher('foo')),
+      mock.call.state.DidRunStory(root_mock.results)
+    ])
+
+  def testRunStoryAndProcessErrorIfNeeded_tryError_finallyException(self):
+    root_mock = self._CreateErrorProcessingMock(method_exceptions={
+      'state.WillRunStory': exceptions.Error('foo'),
+      'test.DidRunStory': Exception('bar')
+    })
+
+    with self.assertRaisesRegexp(exceptions.Error, 'foo'):
+      story_runner._RunStoryAndProcessErrorIfNeeded(
+          root_mock.story, root_mock.results, root_mock.state, root_mock.test)
+
+    self.assertEquals(root_mock.method_calls, [
+      mock.call.state.platform.GetOSName(),
+      mock.call.test.WillRunStory(root_mock.state.platform),
+      mock.call.state.WillRunStory(root_mock.story),
+      mock.call.state.DumpStateUponFailure(root_mock.story, root_mock.results),
+      mock.call.results.AddValue(FailureValueMatcher('foo')),
+      mock.call.state.DidRunStory(root_mock.results),
+      mock.call.test.DidRunStory(root_mock.state.platform)
+    ])
+
+  def testRunStoryAndProcessErrorIfNeeded_tryUnsupportedAction_finallyException(
+      self):
+    root_mock = self._CreateErrorProcessingMock(method_exceptions={
+      'test.WillRunStory': page_action.PageActionNotSupported('foo'),
+      'state.DidRunStory': Exception('bar')
+    })
+
+    story_runner._RunStoryAndProcessErrorIfNeeded(
+        root_mock.story, root_mock.results, root_mock.state, root_mock.test)
+
+    self.assertEquals(root_mock.method_calls, [
+      mock.call.state.platform.GetOSName(),
+      mock.call.test.WillRunStory(root_mock.state.platform),
+      mock.call.results.AddValue(SkipValueMatcher()),
+      mock.call.state.DidRunStory(root_mock.results)
+    ])
+
+  def testRunStoryAndProcessErrorIfNeeded_tryUnhandlable_finallyException(self):
+    root_mock = self._CreateErrorProcessingMock(method_exceptions={
+      'test.Measure': Exception('foo'),
+      'test.DidRunStory': Exception('bar')
+    })
+
+    with self.assertRaisesRegexp(Exception, 'foo'):
+      story_runner._RunStoryAndProcessErrorIfNeeded(
+          root_mock.story, root_mock.results, root_mock.state, root_mock.test)
+
+    self.assertEquals(root_mock.method_calls, [
+      mock.call.state.platform.GetOSName(),
+      mock.call.test.WillRunStory(root_mock.state.platform),
+      mock.call.state.WillRunStory(root_mock.story),
+      mock.call.state.CanRunStory(root_mock.story),
+      mock.call.state.RunStory(root_mock.results),
+      mock.call.test.Measure(root_mock.state.platform, root_mock.results),
+      mock.call.state.DumpStateUponFailure(root_mock.story, root_mock.results),
+      mock.call.results.AddValue(FailureValueMatcher('foo')),
+      mock.call.state.DidRunStory(root_mock.results),
+      mock.call.test.DidRunStory(root_mock.state.platform)
+    ])
+
+  def testRunBenchmarkTimeDuration(self):
+    fake_benchmark = FakeBenchmark()
+    options = fakes.CreateBrowserFinderOptions()
+    options.upload_results = None
+    options.suppress_gtest_report = False
+    options.results_label = None
+    options.use_live_sites = False
+    options.max_failures = 100
+    options.pageset_repeat = 1
+    options.output_formats = ['chartjson']
+
+    with mock.patch('telemetry.internal.story_runner.time.time') as time_patch:
+      # 3, because telemetry code asks for the time at some point
+      time_patch.side_effect = [1, 0, 61]
+      tmp_path = tempfile.mkdtemp()
+
+      try:
+        options.output_dir = tmp_path
+        story_runner.RunBenchmark(fake_benchmark, options)
+        with open(os.path.join(tmp_path, 'results-chart.json')) as f:
+          data = json.load(f)
+
+        self.assertEqual(len(data['charts']), 1)
+        charts = data['charts']
+        self.assertIn('BenchmarkDuration', charts)
+        duration = charts['BenchmarkDuration']
+        self.assertIn("summary", duration)
+        summary = duration['summary']
+        duration = summary['value']
+        self.assertAlmostEqual(duration, 1)
+      finally:
+        shutil.rmtree(tmp_path)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/__init__.py
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/animated_page.html b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/animated_page.html
new file mode 100644
index 0000000..3eb9502
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/animated_page.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<html>
+  <head>
+    <style type="text/css">
+    @-webkit-keyframes rotating {
+      from{
+        -webkit-transform: rotate(0deg);
+      }
+      to{
+        -webkit-transform: rotate(360deg);
+      }
+    }
+    .rotating {
+      -webkit-animation: rotating 2s linear infinite;
+    }
+    </style>
+  </head>
+  <body>
+    <img src="image.png" class="rotating">
+  </body>
+</html>
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/arch-lsb-release b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/arch-lsb-release
new file mode 100644
index 0000000..1e02092
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/arch-lsb-release
@@ -0,0 +1,4 @@
+LSB_VERSION=1.4
+DISTRIB_ID=Arch
+DISTRIB_RELEASE=rolling
+DISTRIB_DESCRIPTION="Arch Linux"
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/archive_files/test.json b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/archive_files/test.json
new file mode 100644
index 0000000..1b7e6b8
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/archive_files/test.json
@@ -0,0 +1,9 @@
+{
+    "archives": {
+        "http://www.testurl.com": {
+            "DEFAULT": "test_000.wpr"
+        }
+    },
+    "description": "Describes the Web Page Replay archives for a story set. Don't edit by hand! Use record_wpr for updating.",
+    "platform_specific": true
+}
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/archive_files/test_000.wpr b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/archive_files/test_000.wpr
new file mode 100644
index 0000000..32d459f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/archive_files/test_000.wpr
@@ -0,0 +1 @@
+File needed for story_runner_unittest testCheckArchives.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/archive_files/test_000.wpr.sha1 b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/archive_files/test_000.wpr.sha1
new file mode 100644
index 0000000..e27e86d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/archive_files/test_000.wpr.sha1
@@ -0,0 +1 @@
+6bd6f4b08b44abc206f68471c0444df156d0fd1a
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/archive_files/test_missing_wpr_file.json b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/archive_files/test_missing_wpr_file.json
new file mode 100644
index 0000000..3a64401
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/archive_files/test_missing_wpr_file.json
@@ -0,0 +1,12 @@
+{
+    "archives": {
+        "http://www.google.com": {
+            "DEFAULT": "test_missing_wpr_file.wpr"
+        },
+        "http://www.testurl.com": {
+            "DEFAULT": "test_000.wpr"
+        }
+    },
+    "description": "Describes the Web Page Replay archives for a story set. Don't edit by hand! Use record_wpr for updating.",
+    "platform_specific": true
+}
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/archive_files/test_page_set.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/archive_files/test_page_set.py
new file mode 100644
index 0000000..ec53709
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/archive_files/test_page_set.py
@@ -0,0 +1,36 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry import story
+from telemetry.page import page
+from telemetry.internal.testing.pages.external_page import ExternalPage
+
+
+class InternalPage(page.Page):
+  def __init__(self, story_set):
+    super(InternalPage, self).__init__('file://bar.html', story=story_set)
+
+class TestPageSet(story.StorySet):
+  """A pageset for testing purpose"""
+
+  def __init__(self):
+    super(TestPageSet, self).__init__(
+      archive_data_file='data/archive_files/test.json',
+      credentials_path='data/credential',
+      user_agent_type='desktop',
+      bucket=story.PUBLIC_BUCKET)
+
+    #top google property; a google tab is often open
+    class Google(page.Page):
+      def __init__(self, story_set):
+        # pylint: disable=bad-super-call
+        super(Google, self).__init__('https://www.google.com',
+                                     page_set=story_set)
+
+      def RunGetActionRunner(self, action_runner):
+        return action_runner
+
+    self.AddStory(Google(self))
+    self.AddStory(InternalPage(self))
+    self.AddStory(ExternalPage(self))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/archive_files/test_simple_one_page_set.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/archive_files/test_simple_one_page_set.py
new file mode 100644
index 0000000..4fb1198
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/archive_files/test_simple_one_page_set.py
@@ -0,0 +1,12 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry import story
+
+
+class TestSimpleOnePageSet(story.StorySet):
+  def __init__(self):
+    super(TestSimpleOnePageSet, self).__init__(
+      archive_data_file='data/archive_files/test.json',
+      credentials_path='data/credential')
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/archive_files/test_simple_two_page_set.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/archive_files/test_simple_two_page_set.py
new file mode 100644
index 0000000..c17c5f3
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/archive_files/test_simple_two_page_set.py
@@ -0,0 +1,12 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry import story
+
+
+class TestSimpleTwoPageSet(story.StorySet):
+  def __init__(self):
+    super(TestSimpleTwoPageSet, self).__init__(
+      archive_data_file='data/archive_files/test.json',
+      credentials_path='data/credential')
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/autotest_ext/background.js b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/autotest_ext/background.js
new file mode 100644
index 0000000..1513ac0
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/autotest_ext/background.js
@@ -0,0 +1,4 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/autotest_ext/manifest.json b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/autotest_ext/manifest.json
new file mode 100644
index 0000000..1eb454e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/autotest_ext/manifest.json
@@ -0,0 +1,13 @@
+{
+  "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDuUZGKCDbff6IRaxa4Pue7PPkxwPaNhGT3JEqppEsNWFjM80imEdqMbf3lrWqEfaHgaNku7nlpwPO1mu3/4Hr+XdNa5MhfnOnuPee4hyTLwOs3Vzz81wpbdzUxZSi2OmqMyI5oTaBYICfNHLwcuc65N5dbt6WKGeKgTpp4v7j7zwIDAQAB",
+  "description": "Telemetry ChromeOS Autotest component extension",
+  "name": "Telemetry ChromeOS AutoTest Component Extension",
+  "background": {
+    "scripts": ["background.js"]
+  },
+  "manifest_version": 2,
+  "version": "0.1",
+  "permissions" : [
+    "autotestPrivate"
+  ]
+}
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/bear.webm b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/bear.webm
new file mode 100644
index 0000000..a1b4150
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/bear.webm
Binary files differ
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/blank.html b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/blank.html
new file mode 100644
index 0000000..8d0ce09
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/blank.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+</head>
+<body>
+Hello world
+</body>
+</html>
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/blink_style.html b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/blink_style.html
new file mode 100644
index 0000000..213020c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/blink_style.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<body>
+Test Page for Blink Style measurement.
+<script>
+var css = '';
+for (var i = 0; i < 1000; i++) {
+  css += 'div { background: green }\n';
+}
+var style = document.createElement('style');
+style.textContent = css;
+document.head.appendChild(style);
+for (var i = 0; i < 1000; i++) {
+  document.body.appendChild(document.createElement('div'));
+}
+</script>
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/cast.html b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/cast.html
new file mode 100644
index 0000000..80314a3
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/cast.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <title>Media Router Dialog Test</title>
+    <script type="text/javascript">
+      'use strict';
+      var startSessionPromise = null;
+      var presentationUrl = 'http://www.google.com/#__testprovider__=true';
+      var startSessionRequest = new PresentationRequest(presentationUrl);
+      window.navigator.presentation.defaultRequest = startSessionRequest;
+
+      function startSession() {
+        startSessionPromise = startSessionRequest.start();
+        console.log('start session');
+      }
+    </script>
+  </head>
+  <body>
+    <button id="start_session_button" onclick="startSession()">
+      Start session
+    </button>
+  </body>
+</html>
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/create_many_objects.html b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/create_many_objects.html
new file mode 100644
index 0000000..4893563
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/create_many_objects.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<script>
+var maxObjects = 100000;
+
+var TestRunner = function() {
+  this.isDone = false;
+};
+
+var testRunner = null;
+window.onload = function () {
+  testRunner = new TestRunner();
+
+  // Create a lot of objects can trigger a Blink GC.
+  for (var i = 0; i < maxObjects; i++) {
+    new TextDecoder();
+  }
+
+  testRunner.isDone = true;
+}
+</script>
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/dependency_test_dir/dog/dog/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/dependency_test_dir/dog/dog/__init__.py
new file mode 100644
index 0000000..50b23df
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/dependency_test_dir/dog/dog/__init__.py
@@ -0,0 +1,3 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/dependency_test_dir/dog/dog/dog_object.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/dependency_test_dir/dog/dog/dog_object.py
new file mode 100644
index 0000000..dfba650
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/dependency_test_dir/dog/dog/dog_object.py
@@ -0,0 +1,15 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import sys
+
+sys.path.append(os.path.join(
+  os.path.dirname(__file__), '..', '..', 'other_animals', 'cat'))
+
+from cat import cat_object  # pylint: disable=import-error
+
+class Dog(object):
+  def CreateEnemy(self):
+    return cat_object.Cat()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/dependency_test_dir/other_animals/cat/cat/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/dependency_test_dir/other_animals/cat/cat/__init__.py
new file mode 100644
index 0000000..50b23df
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/dependency_test_dir/other_animals/cat/cat/__init__.py
@@ -0,0 +1,3 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/dependency_test_dir/other_animals/cat/cat/cat_object.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/dependency_test_dir/other_animals/cat/cat/cat_object.py
new file mode 100644
index 0000000..41be7ba
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/dependency_test_dir/other_animals/cat/cat/cat_object.py
@@ -0,0 +1,7 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+class Cat(object):
+  def Run(self):
+    print 'Meow'
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/dependency_test_dir/other_animals/moose/moose/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/dependency_test_dir/other_animals/moose/moose/__init__.py
new file mode 100644
index 0000000..50b23df
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/dependency_test_dir/other_animals/moose/moose/__init__.py
@@ -0,0 +1,3 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/dependency_test_dir/other_animals/moose/moose/horn/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/dependency_test_dir/other_animals/moose/moose/horn/__init__.py
new file mode 100644
index 0000000..50b23df
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/dependency_test_dir/other_animals/moose/moose/horn/__init__.py
@@ -0,0 +1,3 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/dependency_test_dir/other_animals/moose/moose/horn/horn_object.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/dependency_test_dir/other_animals/moose/moose/horn/horn_object.py
new file mode 100644
index 0000000..1a883ef
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/dependency_test_dir/other_animals/moose/moose/horn/horn_object.py
@@ -0,0 +1,7 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+class Horn(object):
+  def IsBig(self):
+    return True
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/dependency_test_dir/other_animals/moose/moose/moose_object.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/dependency_test_dir/other_animals/moose/moose/moose_object.py
new file mode 100644
index 0000000..1e87ea4
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/dependency_test_dir/other_animals/moose/moose/moose_object.py
@@ -0,0 +1,13 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from horn import horn_object  # pylint: disable=relative-import
+
+class Moose(object):
+  def __init__(self):
+    self._horn = horn_object.Horn()
+
+  def Run(self):
+    if self._horn.IsBig():
+      print 'I need to drop my horn! It is big!'
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/discoverable_classes/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/discoverable_classes/__init__.py
new file mode 100644
index 0000000..9228df8
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/discoverable_classes/__init__.py
@@ -0,0 +1,3 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/discoverable_classes/another_discover_dummyclass.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/discoverable_classes/another_discover_dummyclass.py
new file mode 100644
index 0000000..88581be
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/discoverable_classes/another_discover_dummyclass.py
@@ -0,0 +1,33 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""More dummy exception subclasses used by core/discover.py's unit tests."""
+
+# Import class instead of module explicitly so that inspect.getmembers() returns
+# two Exception subclasses in this current file.
+# Suppress complaints about unable to import class.  The directory path is
+# added at runtime by telemetry test runner.
+#pylint: disable=import-error
+from telemetry.internal.testing.discoverable_classes import discover_dummyclass
+
+
+class _PrivateDummyException(discover_dummyclass.DummyException):
+  def __init__(self):
+    super(_PrivateDummyException, self).__init__()
+
+
+class DummyExceptionImpl1(_PrivateDummyException):
+  def __init__(self):
+    super(DummyExceptionImpl1, self).__init__()
+
+
+class DummyExceptionImpl2(_PrivateDummyException):
+  def __init__(self):
+    super(DummyExceptionImpl2, self).__init__()
+
+
+class DummyExceptionWithParameterImpl1(_PrivateDummyException):
+  def __init__(self, parameter):
+    super(DummyExceptionWithParameterImpl1, self).__init__()
+    del parameter
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/discoverable_classes/discover_dummyclass.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/discoverable_classes/discover_dummyclass.py
new file mode 100644
index 0000000..15dcb35
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/discoverable_classes/discover_dummyclass.py
@@ -0,0 +1,9 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""A dummy exception subclass used by core/discover.py's unit tests."""
+
+class DummyException(Exception):
+  def __init__(self):
+    super(DummyException, self).__init__()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/discoverable_classes/parameter_discover_dummyclass.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/discoverable_classes/parameter_discover_dummyclass.py
new file mode 100644
index 0000000..3360fbd
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/discoverable_classes/parameter_discover_dummyclass.py
@@ -0,0 +1,11 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""A dummy exception subclass used by core/discover.py's unit tests."""
+from telemetry.internal.testing.discoverable_classes import discover_dummyclass
+
+class DummyExceptionWithParameterImpl2(discover_dummyclass.DummyException):
+  def __init__(self, parameter1, parameter2):
+    super(DummyExceptionWithParameterImpl2, self).__init__()
+    del parameter1, parameter2
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/dom_counter_sample.html b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/dom_counter_sample.html
new file mode 100644
index 0000000..483d1e0
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/dom_counter_sample.html
@@ -0,0 +1,12 @@
+<html>
+  <title>DOM counter unit test page</title>
+  <body>
+    <h1 onclick="">Webpage to be used for DOM counter unit testing.</h1>
+    <p onclick="">
+      This webpage is meant to be used by a unit test that checks DOM element
+      counters.  The test expects there to be exactly 1 document, 14 nodes,
+      and 2 event listeners.  Beware, modifying the HTML of this page may
+      change the element count, causing the unit test to fail!
+    </p>
+  </body>
+</html>
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/draggable.html b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/draggable.html
new file mode 100644
index 0000000..8af37ba
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/draggable.html
@@ -0,0 +1,52 @@
+<html>
+  <head>
+    <style>
+      body { margin: 0; }
+      #drag_div {
+        width: 100%;
+        height: 100%;
+        background: blue;
+        position: relative;
+      }
+    </style>
+    <script>
+    offsetX = 0;
+    offsetY = 0;
+
+    function drag(event) {
+      d = document.getElementById('drag_div');
+      offsetX = event.clientX - d.offsetLeft;
+      offsetY = event.clientY - d.offsetTop;
+    }
+
+    function drop(event) {
+      d = document.getElementById('drag_div');
+      d.style.left = event.clientX - offsetX;
+      d.style.top = event.clientY - offsetY;
+    }
+
+    function touchStart(event) {
+      d = document.getElementById('drag_div');
+      offsetX = event.touches[0].clientX - d.offsetLeft;
+      offsetY = event.touches[0].clientY - d.offsetTop;
+    }
+
+    function touchEnd(event) {
+      d = document.getElementById('drag_div');
+      d.style.left = event.changedTouches[0].clientX - offsetX;
+      d.style.top = event.changedTouches[0].clientY - offsetY;
+    }
+
+    </script>
+  </head>
+
+  <body id ="body">
+    <div id="drag_div"> </div>
+  </body>
+  <script>
+  document.getElementById('body').addEventListener('mouseup', drop);
+  document.getElementById('body').addEventListener('touchend', touchEnd);
+  document.getElementById('drag_div').addEventListener('mousedown', drag);
+  document.getElementById('drag_div').addEventListener('touchstart', touchStart);
+  </script>
+</html>
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/favicon.ico b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/favicon.ico
new file mode 100644
index 0000000..0069d5d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/favicon.ico
Binary files differ
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/frame0.png b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/frame0.png
new file mode 100644
index 0000000..956b62c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/frame0.png
Binary files differ
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/frame1.png b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/frame1.png
new file mode 100644
index 0000000..8f59394
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/frame1.png
Binary files differ
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/frame2.png b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/frame2.png
new file mode 100644
index 0000000..f0bf178
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/frame2.png
Binary files differ
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/frame3.png b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/frame3.png
new file mode 100644
index 0000000..f36b453
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/frame3.png
Binary files differ
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/frame4.png b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/frame4.png
new file mode 100644
index 0000000..ef9d38a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/frame4.png
Binary files differ
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/frame5.png b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/frame5.png
new file mode 100644
index 0000000..ef1926d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/frame5.png
Binary files differ
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/frame6.png b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/frame6.png
new file mode 100644
index 0000000..0a3028f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/frame6.png
Binary files differ
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/frame7.png b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/frame7.png
new file mode 100644
index 0000000..9b6b6e5
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/frame7.png
Binary files differ
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/green_rect.html b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/green_rect.html
new file mode 100644
index 0000000..478c755
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/green_rect.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <style>
+  html, body {
+    margin: 0;
+    padding: 0;
+  }
+  #green {
+    width: 32px;
+    height: 32px;
+    background-color: rgb(0, 255, 0);
+  }
+  </style>
+</head>
+<body>
+  <div id="green"></div>
+</body>
+</html>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/host.html b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/host.html
new file mode 100644
index 0000000..46cfb06
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/host.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<script type="text/javascript">var testVar = "host";</script>
+</head>
+<body>
+This is the host page.
+<br>
+<iframe src="iframe1.html"></iframe>
+<iframe src="iframe3.html"></iframe>
+</body>
+</html>
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/iframe1.html b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/iframe1.html
new file mode 100644
index 0000000..972b8fb
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/iframe1.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<script type="text/javascript">var testVar="iframe1";</script>
+</head>
+<body>
+This is IFrame 1.
+<br>
+<iframe src="iframe2.html"></iframe>
+</body>
+</html>
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/iframe2.html b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/iframe2.html
new file mode 100644
index 0000000..778f8a0
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/iframe2.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<script type="text/javascript">var testVar="iframe2";</script>
+</head>
+<body>
+This is IFrame 2.
+</body>
+</html>
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/iframe3.html b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/iframe3.html
new file mode 100644
index 0000000..f992152
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/iframe3.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<script type="text/javascript">var testVar="iframe3";</script>
+</head>
+<body>
+This is IFrame 3.
+</body>
+</html>
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/image.png b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/image.png
new file mode 100644
index 0000000..82c2870
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/image.png
Binary files differ
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/image_decoding.html b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/image_decoding.html
new file mode 100644
index 0000000..59c1f0d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/image_decoding.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+</head>
+<body>
+  <img src="image.png">
+</body>
+</html>
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/interaction_enabled_page.html b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/interaction_enabled_page.html
new file mode 100644
index 0000000..761f912
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/interaction_enabled_page.html
@@ -0,0 +1,96 @@
+<!doctype html>
+<html>
+  <head>
+    <meta name="viewport" content="user-scalable:no">
+    <style type="text/css">
+      body { height: 1500px; }
+      #center {
+         position: fixed;
+         left: 40%;;
+         width: 50%;
+         height: 250px;
+         top: 25%;
+         background-color: grey;
+         -webkit-transform: scale(0.25, 0.25);
+         -webkit-transition: -webkit-transform 1s;
+      }
+
+      #drawer {
+         position: fixed;
+         top: 0;
+         left: 0;
+         height: 100%;
+         width: 120px;
+         background-color: red;
+         -webkit-transform: translate3d(-1000px, 0, 0);
+         -webkit-transition: -webkit-transform 1s;
+      }
+
+    </style>
+    <script>
+    'use strict';
+    window.animationDone = false;
+    function makeAnimation() {
+      var centerEl = document.querySelector('#center');
+      centerEl.style.webkitTransform = 'scale(1.0, 1.0)';
+      console.time('Interaction.CenterAnimation');
+      centerEl.addEventListener('transitionend', function() {
+        console.timeEnd('Interaction.CenterAnimation');
+        var drawerEl = document.querySelector('#drawer');
+        drawerEl.style.webkitTransform = 'translate3D(0, 0, 0)';
+        console.time('Interaction.DrawerAnimation');
+        drawerEl.addEventListener('transitionend', function() {
+          console.timeEnd('Interaction.DrawerAnimation');
+          window.animationDone = true;
+        });
+      });
+    }
+    </script>
+
+    <script>
+    'use strict';
+    var jankMs = 100;
+    var slowMs = 200;
+    window.jankScriptDone = false;
+    window.slowScriptDone = false;
+    function waitMs(ms) {
+      var startTime = window.performance.now();
+      var currTime = startTime;
+      while (currTime - startTime < ms) {
+        var currTime = window.performance.now();
+      }
+    }
+    function makeJank() {
+      console.time('Interaction.JankThreadJSRun');
+      waitMs(jankMs);
+      console.timeEnd('Interaction.JankThreadJSRun');
+      window.jankScriptDone = true;
+    }
+    function makeSlow() {
+      console.time('Interaction.SlowThreadJsRun');
+      waitMs(slowMs);
+      console.timeEnd('Interaction.SlowThreadJsRun');
+      window.slowScriptDone = true;
+    }
+    </script>
+
+  </head>
+  <body>
+    <div id="center">
+      This is something in the middle.
+    </div>
+    <div id="drawer">
+      This is a drawer.
+    </div>
+    <button type="button" id="animating-button" onclick="makeAnimation()">
+      Click or tap this to trigger an animation.
+    </div>
+    <button type="button" id="jank-button" onclick="makeJank()">
+      Click or tap this to make jank of 100ms (approximately).
+    </div>
+    <button type="button" id="slow-button" onclick="makeSlow()">
+      Click or tap this to make wait 200ms (approximately).
+    </div>
+
+  </body>
+</html>
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/jebgalgnebhfojomionfpkfelancnnkf.crx b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/jebgalgnebhfojomionfpkfelancnnkf.crx
new file mode 100644
index 0000000..4fb2a3b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/jebgalgnebhfojomionfpkfelancnnkf.crx
Binary files differ
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/manifest_with_key.json b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/manifest_with_key.json
new file mode 100644
index 0000000..4b41dd4
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/manifest_with_key.json
@@ -0,0 +1,7 @@
+{
+  "name": "unpacked extension",
+  "description": "this is an unpacked extension with a key in it",
+  "version": "1",
+  "manifest_version": 2,
+  "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCoq+cJMuoIaoL2hx//QoIeHnNkXLAEu3IJGcLpM95qbmw9VnAplFI0tpSv4IpuJ1DPPsdsEMhONu1mPhK9xd3BHCtzqXRfRsnx/uOap4NTcUimxiUH3uuX9xkCNWO8EihdV0atnrKROhhnyIxmhgKmKfAYLheOrSGSXP0A4SqaBQIDAQAB"
+}
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/non_scrollable_page.html b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/non_scrollable_page.html
new file mode 100644
index 0000000..5770408
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/non_scrollable_page.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<html>
+  <head>
+    <style type="text/css">
+    </style>
+  </head>
+  <body>
+    Hello, world.
+  </body>
+</html>
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/page_that_logs_to_console.html b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/page_that_logs_to_console.html
new file mode 100644
index 0000000..373eebc
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/page_that_logs_to_console.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+</head>
+<body>
+<script>
+  window.__logCount = 0;
+  setInterval(function() {
+    console.log("Hello, world")
+    window.__logCount += 1
+  }, 100);
+</script>
+</body>
+</html>
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/page_with_clickables.html b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/page_with_clickables.html
new file mode 100644
index 0000000..a1bfc07
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/page_with_clickables.html
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+</head>
+<body>
+<div id="test">Click/tap me</div>
+<script>
+var el = document.getElementById('test');
+var valueSettableByTest = 0;
+var valueToTest = 0;
+el.addEventListener('click', function() {
+  valueToTest = valueSettableByTest;
+});
+</script>
+</body>
+</html>
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/page_with_link.html b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/page_with_link.html
new file mode 100644
index 0000000..dc3c008
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/page_with_link.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+</head>
+<body>
+<a id="clickme" href="blank.html">Click me</a>
+</body>
+</html>
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/page_with_swipeables.html b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/page_with_swipeables.html
new file mode 100644
index 0000000..8844bb9
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/page_with_swipeables.html
@@ -0,0 +1,53 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<style>
+.swipeable {
+  overflow: scroll;
+  height: 200px;
+  width: 200px;
+}
+.left-right-item,
+.top-bottom-item {
+  height: 100px;
+  width: 100px;
+}
+.left-right-item {
+  display: inline-block;
+}
+.red-bg {
+  background-color: red;
+}
+.blue-bg {
+  background-color: blue;
+}
+.tall-and-wide {
+  background-color: grey;
+  height: 5000px;
+  width: 5000px;
+}
+</style>
+</head>
+<body>
+
+<div id="left-right" class="swipeable">
+  <div style="width: 1000px;">
+    <div class="left-right-item red-bg">Test</div>
+    <div class="left-right-item blue-bg">Test</div>
+    <div class="left-right-item red-bg">Test</div>
+    <div class="left-right-item blue-bg">Test</div>
+  </div>
+</div>
+
+<div id="top-bottom" class="swipeable">
+  <div class="top-bottom-item red-bg">Test</div>
+  <div class="top-bottom-item blue-bg">Test</div>
+  <div class="top-bottom-item red-bg">Test</div>
+  <div class="top-bottom-item blue-bg">Test</div>
+</div>
+
+<div class="tall-and-wide"></div>
+
+<div id="off-screen"></div>
+</body>
+</html>
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/pages/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/pages/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/pages/__init__.py
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/pages/external_page.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/pages/external_page.py
new file mode 100755
index 0000000..c0a1cf3
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/pages/external_page.py
@@ -0,0 +1,11 @@
+#!/usr/bin/env python
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.page.page import Page
+
+
+class ExternalPage(Page):
+  def __init__(self, ps):
+    super(ExternalPage, self).__init__('file://foo.html', page_set=ps)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/perf_report_output.txt b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/perf_report_output.txt
new file mode 100644
index 0000000..d9e9c44
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/perf_report_output.txt
@@ -0,0 +1,2497 @@
+# ========
+# captured on: Thu Aug 15 09:40:36 2013
+# hostname : tonyg-linux.mtv.corp.google.com
+# os release : 3.2.5-gg1236
+# perf version : 3.2.5
+# arch : x86_64
+# nrcpus online : 32
+# nrcpus avail : 32
+# cpudesc : Intel(R) Xeon(R) CPU E5-2690 0 @ 2.90GHz
+# cpuid : GenuineIntel,6,45,7
+# total memory : 65904780 kB
+# cmdline : /usr/bin/perf_3.2.5-gg1236 record --call-graph --pid 20916 --output /tmp/tmprt1Qz7/http___www_techcrunch_com__001.renderer0 
+# event : name = cycles, type = 0, config = 0x0, config1 = 0x0, config2 = 0x0, excl_usr = 0, excl_kern = 0, id = { 40116, 40117, 40118, 40119, 40120, 40121, 40122, 40123, 40124 }
+# HEADER_CPU_TOPOLOGY info available, use -I to display
+# HEADER_NUMA_TOPOLOGY info available, use -I to display
+# ========
+#
+# Events: 825  cycles
+#
+# Overhead^Period^Command^Shared Object^Symbol
+3.22^96878864^HTMLParserThrea^chrome               ^[.] void v8::internal::RelocInfo::Visit<v8::internal::MarkCompactMarkingVisitor>(v8::internal::Heap*)
+            |
+            --- void v8::internal::RelocInfo::Visit<v8::internal::MarkCompactMarkingVisitor>(v8::internal::Heap*)
+
+2.11^63615201^HTMLParserThrea^chrome               ^[.] v8::internal::StaticMarkingVisitor<v8::internal::MarkCompactMarkingVisitor>::MarkMapContents(v8::internal::Heap*, v8::internal::Map*)
+            |
+            --- v8::internal::StaticMarkingVisitor<v8::internal::MarkCompactMarkingVisitor>::MarkMapContents(v8::internal::Heap*, v8::internal::Map*)
+
+1.60^48240439^HTMLParserThrea^chrome               ^[.] WebCore::HTMLTokenizer::nextToken(WebCore::SegmentedString&, WebCore::HTMLToken&)
+            |
+            --- WebCore::HTMLTokenizer::nextToken(WebCore::SegmentedString&, WebCore::HTMLToken&)
+               |          
+                --100.00%-- 0x3b2abdf74074
+
+1.53^46054550^HTMLParserThrea^chrome               ^[.] v8::internal::Scanner::ScanIdentifierOrKeyword()
+            |
+            --- v8::internal::Scanner::ScanIdentifierOrKeyword()
+               |          
+               |--33.87%-- 0x3b2abe3e3700
+               |          
+               |--33.85%-- 0x3b2abdf52240
+               |          
+                --32.28%-- 0x3b2abd766000
+
+1.50^45121317^HTMLParserThrea^chrome               ^[.] sk_memset32_SSE2(unsigned int*, unsigned int, int)
+            |
+            --- sk_memset32_SSE2(unsigned int*, unsigned int, int)
+
+1.43^42913933^HTMLParserThrea^chrome               ^[.] v8::internal::LiveRange::CreateAssignedOperand(v8::internal::Zone*)
+            |
+            --- v8::internal::LiveRange::CreateAssignedOperand(v8::internal::Zone*)
+
+1.43^42913933^HTMLParserThrea^chrome               ^[.] v8::internal::LAllocator::MeetConstraintsBetween(v8::internal::LInstruction*, v8::internal::LInstruction*, int)
+            |
+            --- v8::internal::LAllocator::MeetConstraintsBetween(v8::internal::LInstruction*, v8::internal::LInstruction*, int)
+
+1.32^39786862^HTMLParserThrea^chrome               ^[.] v8::internal::HeapObject::Size()
+            |
+            --- v8::internal::HeapObject::Size()
+               |          
+                --100.00%-- 0x1d00000015
+
+1.27^38271931^HTMLParserThrea^chrome               ^[.] v8::internal::RelocIterator::next()
+            |
+            --- v8::internal::RelocIterator::next()
+               |          
+               |--85.80%-- 0x228100000001
+               |          
+                --14.20%-- 0x7fff30e90200
+
+1.06^31909537^HTMLParserThrea^chrome               ^[.] v8::internal::FlexibleBodyVisitor<v8::internal::MarkCompactMarkingVisitor, v8::internal::FixedArray::BodyDescriptor, void>::Visit(v8::internal::Map*, v8::internal::HeapObject*)
+            |
+            --- v8::internal::FlexibleBodyVisitor<v8::internal::MarkCompactMarkingVisitor, v8::internal::FixedArray::BodyDescriptor, void>::Visit(v8::internal::Map*, v8::internal::HeapObject*)
+
+0.98^29461075^HTMLParserThrea^chrome               ^[.] operator new(unsigned long)
+            |
+            --- operator new(unsigned long)
+
+0.88^26612604^HTMLParserThrea^chrome               ^[.] v8::internal::InnerPointerToCodeCache::GcSafeFindCodeForInnerPointer(unsigned char*)
+            |
+            --- v8::internal::InnerPointerToCodeCache::GcSafeFindCodeForInnerPointer(unsigned char*)
+
+0.83^25019326^HTMLParserThrea^chrome               ^[.] v8::internal::Scanner::Scan()
+            |
+            --- v8::internal::Scanner::Scan()
+               |          
+               |--31.35%-- 0x7fff30e9065000
+               |          
+               |--25.38%-- 0x7fff30e915a000
+               |          
+               |--22.29%-- 0x7fff30e9081000
+               |          
+                --20.99%-- 0x7fff30e91900
+                          0x900000001
+
+0.80^24225440^HTMLParserThrea^chrome               ^[.] WebCore::TokenPreloadScanner::scan(WebCore::CompactHTMLToken const&, WebCore::SegmentedString const&, WTF::Vector<WTF::OwnPtr<WebCore::PreloadRequest>, 0ul>&)
+            |
+            --- WebCore::TokenPreloadScanner::scan(WebCore::CompactHTMLToken const&, WebCore::SegmentedString const&, WTF::Vector<WTF::OwnPtr<WebCore::PreloadRequest>, 0ul>&)
+
+0.73^22115352^HTMLParserThrea^chrome               ^[.] v8::internal::DescriptorArray::Append(v8::internal::Descriptor*)
+            |
+            --- v8::internal::DescriptorArray::Append(v8::internal::Descriptor*)
+
+0.73^21947849^HTMLParserThrea^chrome               ^[.] v8::internal::HRepresentationChangesPhase::InsertRepresentationChangesForValue(v8::internal::HValue*)
+            |
+            --- v8::internal::HRepresentationChangesPhase::InsertRepresentationChangesForValue(v8::internal::HValue*)
+
+0.72^21805774^HTMLParserThrea^chrome               ^[.] SkBlitLCD16OpaqueRow_SSE2(unsigned int*, unsigned short const*, unsigned int, int, unsigned int)
+            |
+            --- SkBlitLCD16OpaqueRow_SSE2(unsigned int*, unsigned short const*, unsigned int, int, unsigned int)
+
+0.71^21252690^HTMLParserThrea^chrome               ^[.] int v8::internal::Search<(v8::internal::SearchMode)1, v8::internal::DescriptorArray>(v8::internal::DescriptorArray*, v8::internal::Name*, int)
+            |
+            --- int v8::internal::Search<(v8::internal::SearchMode)1, v8::internal::DescriptorArray>(v8::internal::DescriptorArray*, v8::internal::Name*, int)
+
+0.63^18864914^HTMLParserThrea^chrome               ^[.] v8::internal::HashTable<v8::internal::StringTableShape, v8::internal::HashTableKey*>::FindEntry(v8::internal::Isolate*, v8::internal::HashTableKey*)
+            |
+            --- v8::internal::HashTable<v8::internal::StringTableShape, v8::internal::HashTableKey*>::FindEntry(v8::internal::Isolate*, v8::internal::HashTableKey*)
+               |          
+                --100.00%-- 0x3b2abdff2b80
+
+0.56^16917055^HTMLParserThrea^chrome               ^[.] WebCore::LayoutUnit::LayoutUnit(int)
+            |
+            --- WebCore::LayoutUnit::LayoutUnit(int)
+               |          
+                --100.00%-- (nil)
+
+0.54^16308133^HTMLParserThrea^chrome               ^[.] event_base_loop
+            |
+            --- event_base_loop
+                0x7f7a0b959fc0
+                epoll_init
+                0x7265727265666572
+
+0.51^15412114^HTMLParserThrea^chrome               ^[.] tc_malloc
+            |
+            --- tc_malloc
+
+0.51^15239999^HTMLParserThrea^chrome               ^[.] v8::internal::Scanner::Next()
+            |
+            --- v8::internal::Scanner::Next()
+               |          
+               |--23.51%-- 0x7fff30e9054a
+               |          
+               |--23.51%-- 0x7fff30e906ca
+               |          
+               |--23.38%-- (nil)
+               |          
+               |--15.20%-- 0x7fff30e9081000
+               |          
+                --14.39%-- 0x7fff30e9048c
+
+0.47^14231099^HTMLParserThrea^chrome               ^[.] v8::internal::Heap::DoScavenge(v8::internal::ObjectVisitor*, unsigned char*)
+            |
+            --- v8::internal::Heap::DoScavenge(v8::internal::ObjectVisitor*, unsigned char*)
+
+0.47^14229607^HTMLParserThrea^chrome               ^[.] bool WebCore::SelectorChecker::checkOne<WebCore::DOMSiblingTraversalStrategy>(WebCore::SelectorChecker::SelectorCheckingContext const&, WebCore::DOMSiblingTraversalStrategy const&) const
+            |
+            --- bool WebCore::SelectorChecker::checkOne<WebCore::DOMSiblingTraversalStrategy>(WebCore::SelectorChecker::SelectorCheckingContext const&, WebCore::DOMSiblingTraversalStrategy const&) const
+               |          
+               |--25.11%-- 0x1e83176cd048
+               |          
+               |--25.08%-- 0x7f7a0b9980e000
+               |          
+               |--24.93%-- 0x7f7a00000000
+               |          
+                --24.88%-- 0x7f7a0b997c9000
+
+0.46^13955450^HTMLParserThrea^chrome               ^[.] tc_realloc
+            |
+            --- tc_realloc
+
+0.46^13941187^HTMLParserThrea^chrome               ^[.] WebCore::minimumValueForLength(WebCore::Length const&, WebCore::LayoutUnit, WebCore::RenderView*, bool)
+            |
+            --- WebCore::minimumValueForLength(WebCore::Length const&, WebCore::LayoutUnit, WebCore::RenderView*, bool)
+
+0.46^13834654^HTMLParserThrea^chrome               ^[.] v8::internal::JSReceiver::LocalLookup(v8::internal::Name*, v8::internal::LookupResult*, bool)
+            |
+            --- v8::internal::JSReceiver::LocalLookup(v8::internal::Name*, v8::internal::LookupResult*, bool)
+               |          
+               |--33.05%-- 0x7fff30e91860
+               |          
+               |--26.80%-- (nil)
+               |          
+               |--20.72%-- 0x7fff30e91a10
+               |          
+                --19.43%-- 0x7fff30e90860
+
+0.44^13202625^HTMLParserThrea^chrome               ^[.] v8::internal::Expression::Expression(v8::internal::Isolate*)
+            |
+            --- v8::internal::Expression::Expression(v8::internal::Isolate*)
+               |          
+               |--35.68%-- 0x7fff30e9198000
+               |          
+               |--32.51%-- 0x7f7a00000000
+               |          
+                --31.81%-- (nil)
+
+0.44^13149418^HTMLParserThrea^chrome               ^[.] v8::internal::String::IsOneByteEqualTo(v8::internal::Vector<unsigned char const>)
+            |
+            --- v8::internal::String::IsOneByteEqualTo(v8::internal::Vector<unsigned char const>)
+
+0.43^13061272^HTMLParserThrea^chrome               ^[.] event_active
+            |
+            --- event_active
+                0x40
+
+0.43^12899765^HTMLParserThrea^chrome               ^[.] v8::internal::JSObject::LocalLookupRealNamedProperty(v8::internal::Name*, v8::internal::LookupResult*)
+            |
+            --- v8::internal::JSObject::LocalLookupRealNamedProperty(v8::internal::Name*, v8::internal::LookupResult*)
+               |          
+               |--79.39%-- (nil)
+               |          
+                --20.61%-- 0x7fff30e90ab0
+
+0.42^12696531^HTMLParserThrea^chrome               ^[.] v8::internal::IsIdentifier(v8::internal::UnicodeCache*, v8::internal::Name*)
+            |
+            --- v8::internal::IsIdentifier(v8::internal::UnicodeCache*, v8::internal::Name*)
+
+0.41^12442842^HTMLParserThrea^chrome               ^[.] v8::internal::PointersUpdatingVisitor::VisitPointers(v8::internal::Object**, v8::internal::Object**)
+            |
+            --- v8::internal::PointersUpdatingVisitor::VisitPointers(v8::internal::Object**, v8::internal::Object**)
+
+0.41^12212739^HTMLParserThrea^chrome               ^[.] v8::internal::StubCompiler::CheckPrototypes(v8::internal::Handle<v8::internal::JSObject>, v8::internal::Register, v8::internal::Handle<v8::internal::JSObject>, v8::internal::Register, v8::internal::Register, v8::internal::Register, v8::internal::Handle<v8::internal::Name>, int, v8::internal::Label*, v8::internal::PrototypeCheckType)
+            |
+            --- v8::internal::StubCompiler::CheckPrototypes(v8::internal::Handle<v8::internal::JSObject>, v8::internal::Register, v8::internal::Handle<v8::internal::JSObject>, v8::internal::Register, v8::internal::Register, v8::internal::Register, v8::internal::Handle<v8::internal::Name>, int, v8::internal::Label*, v8::internal::PrototypeCheckType)
+               |          
+                --100.00%-- 0x29f988404121
+
+0.40^11929902^HTMLParserThrea^chrome               ^[.] _ZN2v88internal12BinarySearchILNS0_10SearchModeE1ENS0_15DescriptorArrayEEEiPT0_PNS0_4NameEiii.constprop.530
+            |
+            --- _ZN2v88internal12BinarySearchILNS0_10SearchModeE1ENS0_15DescriptorArrayEEEiPT0_PNS0_4NameEiii.constprop.530
+
+0.36^10844486^HTMLParserThrea^chrome               ^[.] content::VideoCaptureMessageFilter::OnMessageReceived(IPC::Message const&)
+            |
+            --- content::VideoCaptureMessageFilter::OnMessageReceived(IPC::Message const&)
+                0xfffffffc0000000e
+                IPC::ChannelProxy::Context::OnChannelConnected(int)
+                0x6c894cfb8948d824
+
+0.36^10745270^HTMLParserThrea^chrome               ^[.] v8::internal::StaticMarkingVisitor<v8::internal::MarkCompactMarkingVisitor>::VisitJSFunctionStrongCode(v8::internal::Heap*, v8::internal::HeapObject*)
+            |
+            --- v8::internal::StaticMarkingVisitor<v8::internal::MarkCompactMarkingVisitor>::VisitJSFunctionStrongCode(v8::internal::Heap*, v8::internal::HeapObject*)
+
+0.36^10717680^HTMLParserThrea^chrome               ^[.] v8::internal::TemplateHashMapImpl<v8::internal::ZoneAllocationPolicy>::Lookup(void*, unsigned int, bool, v8::internal::ZoneAllocationPolicy)
+            |
+            --- v8::internal::TemplateHashMapImpl<v8::internal::ZoneAllocationPolicy>::Lookup(void*, unsigned int, bool, v8::internal::ZoneAllocationPolicy)
+
+0.36^10710349^HTMLParserThrea^chrome               ^[.] v8::internal::Code::CopyFrom(v8::internal::CodeDesc const&)
+            |
+            --- v8::internal::Code::CopyFrom(v8::internal::CodeDesc const&)
+
+0.35^10674297^HTMLParserThrea^chrome               ^[.] v8::internal::Parser::ParseBinaryExpression(int, bool, bool*)
+            |
+            --- v8::internal::Parser::ParseBinaryExpression(int, bool, bool*)
+               |          
+               |--66.59%-- (nil)
+               |          
+                --33.41%-- 0x7f7a0b98e010
+
+0.35^10650066^HTMLParserThrea^chrome               ^[.] WebCore::SelectorFilter::pushParentStackFrame(WebCore::Element*)
+            |
+            --- WebCore::SelectorFilter::pushParentStackFrame(WebCore::Element*)
+
+0.35^10614251^HTMLParserThrea^libc-2.15.so         ^[.] __memcpy_ssse3_back
+            |
+            --- __memcpy_ssse3_back
+
+0.35^10611142^HTMLParserThrea^chrome               ^[.] v8::internal::GlobalHandles::ComputeObjectGroupsAndImplicitReferences()
+            |
+            --- v8::internal::GlobalHandles::ComputeObjectGroupsAndImplicitReferences()
+
+0.35^10527946^HTMLParserThrea^chrome               ^[.] tcmalloc::FL_PopRange(void**, int, void**, void**)
+            |
+            --- tcmalloc::FL_PopRange(void**, int, void**, void**)
+
+0.35^10514181^HTMLParserThrea^libpthread-2.15.so   ^[.] pthread_getspecific
+            |
+            --- pthread_getspecific
+
+0.35^10497704^HTMLParserThrea^chrome               ^[.] v8::internal::IC::PostPatching(unsigned char*, v8::internal::Code*, v8::internal::Code*)
+            |
+            --- v8::internal::IC::PostPatching(unsigned char*, v8::internal::Code*, v8::internal::Code*)
+
+0.35^10452932^HTMLParserThrea^chrome               ^[.] _ZN2v88internal12StringHasher13AddCharactersIhEEvPKT_i.constprop.518
+            |
+            --- _ZN2v88internal12StringHasher13AddCharactersIhEEvPKT_i.constprop.518
+
+0.35^10426463^HTMLParserThrea^chrome               ^[.] _ZN2v88internalL13LookupForReadENS0_6HandleINS0_6ObjectEEENS1_INS0_6StringEEEPNS0_12LookupResultE.constprop.171
+            |
+            --- _ZN2v88internalL13LookupForReadENS0_6HandleINS0_6ObjectEEENS1_INS0_6StringEEEPNS0_12LookupResultE.constprop.171
+               |          
+               |--65.70%-- (nil)
+               |          
+                --34.30%-- 0x7fff30e91e38
+
+0.34^10228287^HTMLParserThrea^chrome               ^[.] WTF::Unicode::convertLatin1ToUTF8(unsigned char const**, unsigned char const*, char**, char*)
+            |
+            --- WTF::Unicode::convertLatin1ToUTF8(unsigned char const**, unsigned char const*, char**, char*)
+               |          
+               |--34.63%-- 0x3b2abdeedb48
+               |          
+               |--32.97%-- webkit_glue::(anonymous namespace)::HeaderFlattener::visitHeader(WebKit::WebString const&, WebKit::WebString const&)
+               |          0x3b2abdfe9230
+               |          
+                --32.40%-- void WTF::StringBuilder::reallocateBuffer<unsigned char>(unsigned int)
+
+0.34^10139628^HTMLParserThrea^chrome               ^[.] base::subtle::RefCountedThreadSafeBase::Release() const
+            |
+            --- base::subtle::RefCountedThreadSafeBase::Release() const
+               |          
+               |--63.45%-- base::internal::Invoker<1, base::internal::BindState<base::internal::RunnableAdapter<void (base::BaseTimerTaskInternal::*)()>, void ()(base::BaseTimerTaskInternal*), void ()(base::internal::OwnedWrapper<base::BaseTimerTaskInternal>)>, void ()(base::BaseTimerTaskInternal*)>::Run(base::internal::BindStateBase*)
+               |          
+                --36.55%-- 0x7f7a0b8ff200
+
+0.33^9932889^HTMLParserThrea^chrome               ^[.] void WebCore::StyleResolver::applyProperties<(WebCore::StyleResolver::StyleApplicationPass)1>(WebCore::StyleResolverState&, WebCore::StylePropertySet const*, WebCore::StyleRule*, bool, bool, WebCore::PropertyWhitelistType)
+            |
+            --- void WebCore::StyleResolver::applyProperties<(WebCore::StyleResolver::StyleApplicationPass)1>(WebCore::StyleResolverState&, WebCore::StylePropertySet const*, WebCore::StyleRule*, bool, bool, WebCore::PropertyWhitelistType)
+
+0.33^9928749^HTMLParserThrea^chrome               ^[.] v8::internal::CodeStub::GetCode(v8::internal::Isolate*)
+            |
+            --- v8::internal::CodeStub::GetCode(v8::internal::Isolate*)
+
+0.33^9868335^HTMLParserThrea^chrome               ^[.] v8::internal::Heap::AllocateSharedFunctionInfo(v8::internal::Object*)
+            |
+            --- v8::internal::Heap::AllocateSharedFunctionInfo(v8::internal::Object*)
+
+0.33^9807337^HTMLParserThrea^chrome               ^[.] v8::internal::Zone::New(int)
+            |
+            --- v8::internal::Zone::New(int)
+               |          
+                --100.00%-- 0x7fff30e91d48
+
+0.32^9579492^HTMLParserThrea^chrome               ^[.] v8::internal::Object::GetProperty(v8::internal::Object*, v8::internal::LookupResult*, v8::internal::Name*, PropertyAttributes*)
+            |
+            --- v8::internal::Object::GetProperty(v8::internal::Object*, v8::internal::LookupResult*, v8::internal::Name*, PropertyAttributes*)
+               |          
+               |--58.83%-- 0x2281c1418f41
+               |          
+                --41.17%-- (nil)
+
+0.32^9578890^HTMLParserThrea^chrome               ^[.] _ZN2v88internal15DescriptorArray3SetEiPNS0_10DescriptorERKNS1_16WhitenessWitnessE.isra.252
+            |
+            --- _ZN2v88internal15DescriptorArray3SetEiPNS0_10DescriptorERKNS1_16WhitenessWitnessE.isra.252
+
+0.31^9412432^HTMLParserThrea^chrome               ^[.] WebCore::ScrollView::unscaledVisibleContentSize(WebCore::ScrollableArea::VisibleContentRectIncludesScrollbars) const
+            |
+            --- WebCore::ScrollView::unscaledVisibleContentSize(WebCore::ScrollableArea::VisibleContentRectIncludesScrollbars) const
+
+0.31^9384389^HTMLParserThrea^chrome               ^[.] v8::internal::CallICBase::ReceiverToObjectIfRequired(v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::Object>)
+            |
+            --- v8::internal::CallICBase::ReceiverToObjectIfRequired(v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::Object>)
+               |          
+               |--37.40%-- 0x2281c141ab41
+               |          
+               |--35.85%-- 0x2281c1419541
+               |          
+                --26.75%-- 0x2281c141a941
+
+0.31^9309826^HTMLParserThrea^chrome               ^[.] v8::internal::Factory::NewFunctionFromSharedFunctionInfo(v8::internal::Handle<v8::internal::SharedFunctionInfo>, v8::internal::Handle<v8::internal::Context>, v8::internal::PretenureFlag)
+            |
+            --- v8::internal::Factory::NewFunctionFromSharedFunctionInfo(v8::internal::Handle<v8::internal::SharedFunctionInfo>, v8::internal::Handle<v8::internal::Context>, v8::internal::PretenureFlag)
+
+0.31^9238181^HTMLParserThrea^chrome               ^[.] v8::internal::Parser::ParseAssignmentExpression(bool, bool*)
+            |
+            --- v8::internal::Parser::ParseAssignmentExpression(bool, bool*)
+               |          
+               |--73.43%-- (nil)
+               |          
+                --26.57%-- 0x7fff30e9136000
+
+0.30^9076438^HTMLParserThrea^chrome               ^[.] WebCore::RenderObject* WebCore::bidiNextShared<WebCore::BidiResolver<WebCore::InlineIterator, WebCore::BidiRun> >(WebCore::RenderObject*, WebCore::RenderObject*, WebCore::BidiResolver<WebCore::InlineIterator, WebCore::BidiRun>*, WebCore::EmptyInlineBehavior, bool*)
+            |
+            --- WebCore::RenderObject* WebCore::bidiNextShared<WebCore::BidiResolver<WebCore::InlineIterator, WebCore::BidiRun> >(WebCore::RenderObject*, WebCore::RenderObject*, WebCore::BidiResolver<WebCore::InlineIterator, WebCore::BidiRun>*, WebCore::EmptyInlineBehavior, bool*)
+               |          
+                --100.00%-- 0x3b2abd841928
+
+0.30^8941888^HTMLParserThrea^chrome               ^[.] v8::internal::Heap::AllocateStruct(v8::internal::InstanceType)
+            |
+            --- v8::internal::Heap::AllocateStruct(v8::internal::InstanceType)
+
+0.29^8602723^HTMLParserThrea^chrome               ^[.] v8::internal::LoadIC::ComputeLoadHandler(v8::internal::LookupResult*, v8::internal::Handle<v8::internal::JSObject>, v8::internal::Handle<v8::internal::String>)
+            |
+            --- v8::internal::LoadIC::ComputeLoadHandler(v8::internal::LookupResult*, v8::internal::Handle<v8::internal::JSObject>, v8::internal::Handle<v8::internal::String>)
+               |          
+                --100.00%-- 0x7fff30e90648
+
+0.28^8470525^HTMLParserThrea^chrome               ^[.] void v8::internal::String::WriteToFlat<unsigned char>(v8::internal::String*, unsigned char*, int, int)
+            |
+            --- void v8::internal::String::WriteToFlat<unsigned char>(v8::internal::String*, unsigned char*, int, int)
+
+0.28^8366209^HTMLParserThrea^chrome               ^[.] WTF::StringImpl::hashSlowCase() const
+            |
+            --- WTF::StringImpl::hashSlowCase() const
+               |          
+               |--36.45%-- 0x7f7a11ebab98
+               |          0x7f7a0b9bd000
+               |          
+               |--33.90%-- 0xfffe02a2
+               |          0x1e83176903e8
+               |          
+                --29.65%-- 0x3b2abd1fa100
+
+0.27^8156738^HTMLParserThrea^chrome               ^[.] v8::internal::Runtime_ParallelRecompile(int, v8::internal::Object**, v8::internal::Isolate*)
+            |
+            --- v8::internal::Runtime_ParallelRecompile(int, v8::internal::Object**, v8::internal::Isolate*)
+                0x2281c142b871
+                0x2281c1f35545
+                0x2281c1f31dba
+                0x2281c140e854
+                0x2281c142b65e
+                0x2281c1417d97
+                v8::internal::Invoke(bool, v8::internal::Handle<v8::internal::JSFunction>, v8::internal::Handle<v8::internal::Object>, int, v8::internal::Handle<v8::internal::Object>*, bool*)
+
+0.27^8131676^HTMLParserThrea^chrome               ^[.] v8::internal::FixedArray::set(int, v8::internal::Object*)
+            |
+            --- v8::internal::FixedArray::set(int, v8::internal::Object*)
+
+0.27^8075110^HTMLParserThrea^chrome               ^[.] v8::internal::Heap::AllocateRaw(int, v8::internal::AllocationSpace, v8::internal::AllocationSpace)
+            |
+            --- v8::internal::Heap::AllocateRaw(int, v8::internal::AllocationSpace, v8::internal::AllocationSpace)
+
+0.26^7972021^HTMLParserThrea^chrome               ^[.] WebCore::MarkupAccumulator::appendCharactersReplacingEntities(WTF::StringBuilder&, WTF::String const&, unsigned int, unsigned int, WebCore::EntityMask)
+            |
+            --- WebCore::MarkupAccumulator::appendCharactersReplacingEntities(WTF::StringBuilder&, WTF::String const&, unsigned int, unsigned int, WebCore::EntityMask)
+
+0.25^7610316^HTMLParserThrea^chrome               ^[.] _ZN12_GLOBAL__N_121do_free_with_callbackEPvPFvS0_E.constprop.42
+            |
+            --- _ZN12_GLOBAL__N_121do_free_with_callbackEPvPFvS0_E.constprop.42
+
+0.25^7488722^HTMLParserThrea^chrome               ^[.] WebCore::NodeListV8Internal::indexedPropertyGetterCallback(unsigned int, v8::PropertyCallbackInfo<v8::Value> const&)
+            |
+            --- WebCore::NodeListV8Internal::indexedPropertyGetterCallback(unsigned int, v8::PropertyCallbackInfo<v8::Value> const&)
+                0x7f7a0b900000
+               |          
+                --100.00%-- 0x7f7a0b900000
+
+0.25^7420533^HTMLParserThrea^chrome               ^[.] webkit_glue::WebThreadBase::TaskObserverAdapter::DidProcessTask(base::PendingTask const&)
+            |
+            --- webkit_glue::WebThreadBase::TaskObserverAdapter::DidProcessTask(base::PendingTask const&)
+                0x7f7a0b8ff200
+
+0.24^7201464^HTMLParserThrea^chrome               ^[.] WebCore::RenderBox::computeRectForRepaint(WebCore::RenderLayerModelObject const*, WebCore::LayoutRect&, bool) const
+            |
+            --- WebCore::RenderBox::computeRectForRepaint(WebCore::RenderLayerModelObject const*, WebCore::LayoutRect&, bool) const
+               |          
+               |--50.28%-- 0x124000002bc0
+               |          
+                --49.72%-- 0x194000003e80
+
+0.24^7186236^HTMLParserThrea^chrome               ^[.] v8::internal::StoreBuffer::IteratePointersInStoreBuffer(void (*)(v8::internal::HeapObject**, v8::internal::HeapObject*), bool)
+            |
+            --- v8::internal::StoreBuffer::IteratePointersInStoreBuffer(void (*)(v8::internal::HeapObject**, v8::internal::HeapObject*), bool)
+
+0.24^7180913^HTMLParserThrea^chrome               ^[.] v8::internal::StaticMarkingVisitor<v8::internal::MarkCompactMarkingVisitor>::VisitJSFunction(v8::internal::Map*, v8::internal::HeapObject*)
+            |
+            --- v8::internal::StaticMarkingVisitor<v8::internal::MarkCompactMarkingVisitor>::VisitJSFunction(v8::internal::Map*, v8::internal::HeapObject*)
+
+0.24^7180572^HTMLParserThrea^chrome               ^[.] v8::internal::MarkCompactCollector::ClearNonLivePrototypeTransitions(v8::internal::Map*)
+            |
+            --- v8::internal::MarkCompactCollector::ClearNonLivePrototypeTransitions(v8::internal::Map*)
+
+0.24^7178618^HTMLParserThrea^chrome               ^[.] v8::internal::MarkCompactCollector::SweepSpace(v8::internal::PagedSpace*, v8::internal::MarkCompactCollector::SweeperType)
+            |
+            --- v8::internal::MarkCompactCollector::SweepSpace(v8::internal::PagedSpace*, v8::internal::MarkCompactCollector::SweeperType)
+
+0.24^7154254^HTMLParserThrea^chrome               ^[.] WTF::HashTableConstIterator<WebCore::RenderObject const*, WebCore::RenderObject const*, WTF::IdentityExtractor, WTF::PtrHash<WebCore::RenderObject const*>, WTF::HashTraits<WebCore::RenderObject const*>, WTF::HashTraits<WebCore::RenderObject const*> > WTF::HashTable<WebCore::RenderObject const*, WebCore::RenderObject const*, WTF::IdentityExtractor, WTF::PtrHash<WebCore::RenderObject const*>, WTF::HashTraits<WebCore::RenderObject const*>, WTF::HashTraits
+            |
+            --- WTF::HashTableConstIterator<WebCore::RenderObject const*, WebCore::RenderObject const*, WTF::IdentityExtractor, WTF::PtrHash<WebCore::RenderObject const*>, WTF::HashTraits<WebCore::RenderObject const*>, WTF::HashTraits<WebCore::RenderObject const*> > WTF::HashTable<WebCore::RenderObject const*, WebCore::RenderObject const*, WTF::IdentityExtractor, WTF::PtrHash<WebCore::RenderObject const*>, WTF::HashTraits<WebCore::RenderObject const*>, WTF::HashTraits<WebCore::RenderObject const*> >::find<WTF::IdentityHashTranslator<WTF::PtrHash<WebCore::RenderObject const*> >, WebCore::RenderObject const*>(WebCore::RenderObject const* const&) const
+
+0.24^7150261^HTMLParserThrea^chrome               ^[.] void url_parse::(anonymous namespace)::DoParseAuthority<char>(char const*, url_parse::Component const&, url_parse::Component*, url_parse::Component*, url_parse::Component*, url_parse::Component*)
+            |
+            --- void url_parse::(anonymous namespace)::DoParseAuthority<char>(char const*, url_parse::Component const&, url_parse::Component*, url_parse::Component*, url_parse::Component*, url_parse::Component*)
+               |          
+                --100.00%-- 0xffffffff00000000
+
+0.24^7147589^HTMLParserThrea^chrome               ^[.] v8::internal::Heap::AllocateFixedArray(int)
+            |
+            --- v8::internal::Heap::AllocateFixedArray(int)
+
+0.24^7145279^HTMLParserThrea^chrome               ^[.] v8::internal::Scope::MustAllocate(v8::internal::Variable*)
+            |
+            --- v8::internal::Scope::MustAllocate(v8::internal::Variable*)
+
+0.24^7140300^HTMLParserThrea^chrome               ^[.] WebCore::RenderBox::fillAvailableMeasure(WebCore::LayoutUnit, WebCore::LayoutUnit&, WebCore::LayoutUnit&) const
+            |
+            --- WebCore::RenderBox::fillAvailableMeasure(WebCore::LayoutUnit, WebCore::LayoutUnit&, WebCore::LayoutUnit&) const
+
+0.24^7139300^HTMLParserThrea^chrome               ^[.] WebCore::RenderBlock::computeInlinePreferredLogicalWidths(WebCore::LayoutUnit&, WebCore::LayoutUnit&)
+            |
+            --- WebCore::RenderBlock::computeInlinePreferredLogicalWidths(WebCore::LayoutUnit&, WebCore::LayoutUnit&)
+
+0.24^7134579^HTMLParserThrea^chrome               ^[.] v8::internal::Scanner::ScanString()
+            |
+            --- v8::internal::Scanner::ScanString()
+
+0.24^7132161^HTMLParserThrea^chrome               ^[.] WebCore::ElementRuleCollector::collectRuleIfMatches(WebCore::RuleData const&, WebCore::MatchRequest const&, WebCore::RuleRange&)
+            |
+            --- WebCore::ElementRuleCollector::collectRuleIfMatches(WebCore::RuleData const&, WebCore::MatchRequest const&, WebCore::RuleRange&)
+
+0.24^7127556^HTMLParserThrea^chrome               ^[.] v8::internal::TemplateHashMapImpl<v8::internal::FreeStoreAllocationPolicy>::Lookup(void*, unsigned int, bool, v8::internal::FreeStoreAllocationPolicy)
+            |
+            --- v8::internal::TemplateHashMapImpl<v8::internal::FreeStoreAllocationPolicy>::Lookup(void*, unsigned int, bool, v8::internal::FreeStoreAllocationPolicy)
+
+0.24^7124874^HTMLParserThrea^chrome               ^[.] url_canon::RemoveURLWhitespace(char const*, int, url_canon::CanonOutputT<char>*, int*)
+            |
+            --- url_canon::RemoveURLWhitespace(char const*, int, url_canon::CanonOutputT<char>*, int*)
+
+0.24^7120126^HTMLParserThrea^chrome               ^[.] v8::internal::RelocInfoWriter::Write(v8::internal::RelocInfo const*)
+            |
+            --- v8::internal::RelocInfoWriter::Write(v8::internal::RelocInfo const*)
+
+0.24^7115866^HTMLParserThrea^chrome               ^[.] v8::internal::Parser::ParseMemberWithNewPrefixesExpression(v8::internal::PositionStack*, bool*)
+            |
+            --- v8::internal::Parser::ParseMemberWithNewPrefixesExpression(v8::internal::PositionStack*, bool*)
+               |          
+               |--50.15%-- 0x7fff30e9065000
+               |          
+                --49.85%-- 0x7fff30e914d000
+
+0.24^7113138^HTMLParserThrea^chrome               ^[.] v8::internal::JSObject::AddFastPropertyUsingMap(v8::internal::Map*, v8::internal::Name*, v8::internal::Object*, int, v8::internal::Representation)
+            |
+            --- v8::internal::JSObject::AddFastPropertyUsingMap(v8::internal::Map*, v8::internal::Name*, v8::internal::Object*, int, v8::internal::Representation)
+
+0.24^7106943^HTMLParserThrea^chrome               ^[.] v8::internal::StoreBuffer::Compact()
+            |
+            --- v8::internal::StoreBuffer::Compact()
+
+0.24^7104154^HTMLParserThrea^chrome               ^[.] WebCore::VisitedLinkState::determineLinkStateSlowCase(WebCore::Element*)
+            |
+            --- WebCore::VisitedLinkState::determineLinkStateSlowCase(WebCore::Element*)
+
+0.24^7089266^HTMLParserThrea^chrome               ^[.] WebCore::selectorIdentifierHash(WebCore::CSSSelector const*)
+            |
+            --- WebCore::selectorIdentifierHash(WebCore::CSSSelector const*)
+
+0.24^7086923^HTMLParserThrea^chrome               ^[.] v8::internal::InnerPointerToCodeCache::GetCacheEntry(unsigned char*)
+            |
+            --- v8::internal::InnerPointerToCodeCache::GetCacheEntry(unsigned char*)
+               |          
+                --100.00%-- 0x7fff30e912c0
+                          0xc6a3430a8a9
+
+0.24^7081815^HTMLParserThrea^chrome               ^[.] v8::internal::Parser::ParseUnaryExpression(bool*)
+            |
+            --- v8::internal::Parser::ParseUnaryExpression(bool*)
+                (nil)
+
+0.24^7079188^HTMLParserThrea^chrome               ^[.] WebCore::SelectorFilter::fastRejectSelector(WebCore::CSSSelector const*) const
+            |
+            --- WebCore::SelectorFilter::fastRejectSelector(WebCore::CSSSelector const*) const
+
+0.24^7077714^HTMLParserThrea^chrome               ^[.] v8::internal::CallICBase::LoadFunction(v8::internal::InlineCacheState, int, v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::String>)
+            |
+            --- v8::internal::CallICBase::LoadFunction(v8::internal::InlineCacheState, int, v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::String>)
+               |          
+               |--50.54%-- 0x7fff30e90d30
+               |          
+                --49.46%-- 0x7fff30e90270
+
+0.23^7065769^HTMLParserThrea^chrome               ^[.] WebCore::RenderBoxModelObject::computedCSSPadding(WebCore::Length) const
+            |
+            --- WebCore::RenderBoxModelObject::computedCSSPadding(WebCore::Length) const
+
+0.23^7059576^HTMLParserThrea^chrome               ^[.] v8::internal::Deserializer::ReadChunk(v8::internal::Object**, v8::internal::Object**, int, unsigned char*)
+            |
+            --- v8::internal::Deserializer::ReadChunk(v8::internal::Object**, v8::internal::Object**, int, unsigned char*)
+                0x7f7a0b900000
+
+0.23^7058229^HTMLParserThrea^chrome               ^[.] visitedlink::VisitedLinkCommon::IsVisited(unsigned long) const
+            |
+            --- visitedlink::VisitedLinkCommon::IsVisited(unsigned long) const
+
+0.23^7051436^HTMLParserThrea^chrome               ^[.] v8::internal::AllocateFixedArrayWithFiller(v8::internal::Heap*, int, v8::internal::PretenureFlag, v8::internal::Object*)
+            |
+            --- v8::internal::AllocateFixedArrayWithFiller(v8::internal::Heap*, int, v8::internal::PretenureFlag, v8::internal::Object*)
+
+0.23^7043481^HTMLParserThrea^chrome               ^[.] v8::internal::FuncNameInferrer::PushLiteralName(v8::internal::Handle<v8::internal::String>)
+            |
+            --- v8::internal::FuncNameInferrer::PushLiteralName(v8::internal::Handle<v8::internal::String>)
+
+0.23^7023232^HTMLParserThrea^chrome               ^[.] v8::internal::VariableProxy::VariableProxy(v8::internal::Isolate*, v8::internal::Handle<v8::internal::String>, bool, v8::internal::Interface*, int)
+            |
+            --- v8::internal::VariableProxy::VariableProxy(v8::internal::Isolate*, v8::internal::Handle<v8::internal::String>, bool, v8::internal::Interface*, int)
+               |          
+                --100.00%-- 0x7fff30e90ed8
+
+0.23^7022335^HTMLParserThrea^chrome               ^[.] WebCore::V8CSSStyleDeclaration::createWrapper(WTF::PassRefPtr<WebCore::CSSStyleDeclaration>, v8::Handle<v8::Object>, v8::Isolate*)
+            |
+            --- WebCore::V8CSSStyleDeclaration::createWrapper(WTF::PassRefPtr<WebCore::CSSStyleDeclaration>, v8::Handle<v8::Object>, v8::Isolate*)
+                0x7f7a0b93a3c0
+
+0.23^7015773^HTMLParserThrea^chrome               ^[.] WebCore::RenderBlock::LineBreaker::nextSegmentBreak(WebCore::BidiResolver<WebCore::InlineIterator, WebCore::BidiRun>&, WebCore::LineInfo&, WebCore::RenderBlock::RenderTextInfo&, WebCore::RenderBlock::FloatingObject*, unsigned int, WTF::Vector<WebCore::WordMeasurement, 64ul>&)
+            |
+            --- WebCore::RenderBlock::LineBreaker::nextSegmentBreak(WebCore::BidiResolver<WebCore::InlineIterator, WebCore::BidiRun>&, WebCore::LineInfo&, WebCore::RenderBlock::RenderTextInfo&, WebCore::RenderBlock::FloatingObject*, unsigned int, WTF::Vector<WebCore::WordMeasurement, 64ul>&)
+
+0.23^7009841^HTMLParserThrea^chrome               ^[.] v8::internal::CodeCache::LookupDefaultCache(v8::internal::Name*, unsigned int)
+            |
+            --- v8::internal::CodeCache::LookupDefaultCache(v8::internal::Name*, unsigned int)
+
+0.23^6940000^HTMLParserThrea^chrome               ^[.] v8::internal::PositionsRecorder::RecordPosition(int)
+            |
+            --- v8::internal::PositionsRecorder::RecordPosition(int)
+
+0.23^6866416^HTMLParserThrea^chrome               ^[.] _ZN2v88internal6Parser25ParseVariableDeclarationsENS1_26VariableDeclarationContextEPNS1_29VariableDeclarationPropertiesEPNS0_8ZoneListINS0_6HandleINS0_6StringEEEEEPS8_Pb.constprop.454
+            |
+            --- _ZN2v88internal6Parser25ParseVariableDeclarationsENS1_26VariableDeclarationContextEPNS1_29VariableDeclarationPropertiesEPNS0_8ZoneListINS0_6HandleINS0_6StringEEEEEPS8_Pb.constprop.454
+
+0.23^6858851^HTMLParserThrea^chrome               ^[.] S32A_Opaque_BlitRow32_SSE2(unsigned int*, unsigned int const*, int, unsigned int)
+            |
+            --- S32A_Opaque_BlitRow32_SSE2(unsigned int*, unsigned int const*, int, unsigned int)
+
+0.23^6846415^HTMLParserThrea^chrome               ^[.] v8::internal::String::SlowEquals(v8::internal::String*)
+            |
+            --- v8::internal::String::SlowEquals(v8::internal::String*)
+
+0.22^6771375^HTMLParserThrea^chrome               ^[.] v8::internal::Parser::Expect(v8::internal::Token::Value, bool*)
+            |
+            --- v8::internal::Parser::Expect(v8::internal::Token::Value, bool*)
+               |          
+               |--52.15%-- 0x7fff30e905d002
+               |          
+                --47.85%-- (nil)
+
+0.22^6645010^HTMLParserThrea^chrome               ^[.] v8::internal::DeoptimizerData::FindDeoptimizingCode(unsigned char*)
+            |
+            --- v8::internal::DeoptimizerData::FindDeoptimizingCode(unsigned char*)
+
+0.22^6627879^HTMLParserThrea^chrome               ^[.] v8::internal::StoreIC::Store(v8::internal::InlineCacheState, v8::internal::StrictModeFlag, v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::String>, v8::internal::Handle<v8::internal::Object>, v8::internal::JSReceiver::StoreFromKeyed)
+            |
+            --- v8::internal::StoreIC::Store(v8::internal::InlineCacheState, v8::internal::StrictModeFlag, v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::String>, v8::internal::Handle<v8::internal::Object>, v8::internal::JSReceiver::StoreFromKeyed)
+               |          
+               |--53.63%-- 0xc6a34268609
+               |          
+                --46.37%-- 0xc6a34ea2a81
+
+0.22^6600293^HTMLParserThrea^chrome               ^[.] WTF::HashTableAddResult<WTF::HashTableIterator<WTF::AtomicString, WTF::AtomicString, WTF::IdentityExtractor, WTF::AtomicStringHash, WTF::HashTraits<WTF::AtomicString>, WTF::HashTraits<WTF::AtomicString> > > WTF::HashTable<WTF::AtomicString, WTF::AtomicString, WTF::IdentityExtractor, WTF::AtomicStringHash, WTF::HashTraits<WTF::AtomicString>, WTF::HashTraits<WTF::AtomicString> >::add<WTF::IdentityHashTranslator<WTF::AtomicStringHash>, WTF::AtomicString, 
+            |
+            --- WTF::HashTableAddResult<WTF::HashTableIterator<WTF::AtomicString, WTF::AtomicString, WTF::IdentityExtractor, WTF::AtomicStringHash, WTF::HashTraits<WTF::AtomicString>, WTF::HashTraits<WTF::AtomicString> > > WTF::HashTable<WTF::AtomicString, WTF::AtomicString, WTF::IdentityExtractor, WTF::AtomicStringHash, WTF::HashTraits<WTF::AtomicString>, WTF::HashTraits<WTF::AtomicString> >::add<WTF::IdentityHashTranslator<WTF::AtomicStringHash>, WTF::AtomicString, WTF::AtomicString>(WTF::AtomicString const&, WTF::AtomicString const&)
+
+0.22^6596875^HTMLParserThrea^chrome               ^[.] v8::internal::Assembler::bind(v8::internal::Label*)
+            |
+            --- v8::internal::Assembler::bind(v8::internal::Label*)
+
+0.22^6594480^HTMLParserThrea^chrome               ^[.] WebCore::InspectorInstrumentation::instrumentingAgentsForPage(WebCore::Page*)
+            |
+            --- WebCore::InspectorInstrumentation::instrumentingAgentsForPage(WebCore::Page*)
+
+0.22^6579973^HTMLParserThrea^chrome               ^[.] v8::internal::IC::PatchCache(v8::internal::InlineCacheState, v8::internal::StrictModeFlag, v8::internal::Handle<v8::internal::JSObject>, v8::internal::Handle<v8::internal::String>, v8::internal::Handle<v8::internal::Code>)
+            |
+            --- v8::internal::IC::PatchCache(v8::internal::InlineCacheState, v8::internal::StrictModeFlag, v8::internal::Handle<v8::internal::JSObject>, v8::internal::Handle<v8::internal::String>, v8::internal::Handle<v8::internal::Code>)
+               |          
+               |--54.27%-- 0x29f988404121
+               |          
+                --45.73%-- 0xc6a3487f2f1
+
+0.22^6576754^HTMLParserThrea^chrome               ^[.] v8::internal::Assembler::movq(v8::internal::Register, v8::internal::Handle<v8::internal::Object>, v8::internal::RelocInfo::Mode)
+            |
+            --- v8::internal::Assembler::movq(v8::internal::Register, v8::internal::Handle<v8::internal::Object>, v8::internal::RelocInfo::Mode)
+
+0.22^6564875^HTMLParserThrea^libpthread-2.15.so   ^[.] pthread_mutex_lock
+            |
+            --- pthread_mutex_lock
+
+0.22^6559671^HTMLParserThrea^chrome               ^[.] v8::internal::VariableProxy::node_type() const
+            |
+            --- v8::internal::VariableProxy::node_type() const
+
+0.22^6543901^HTMLParserThrea^chrome               ^[.] v8::internal::Runtime_NewClosure(int, v8::internal::Object**, v8::internal::Isolate*)
+            |
+            --- v8::internal::Runtime_NewClosure(int, v8::internal::Object**, v8::internal::Isolate*)
+               |          
+                --100.00%-- 0x2281c1ab1279
+                          0x2281c142b664
+                          0x2281c1417d97
+                          v8::internal::Invoke(bool, v8::internal::Handle<v8::internal::JSFunction>, v8::internal::Handle<v8::internal::Object>, int, v8::internal::Handle<v8::internal::Object>*, bool*)
+
+0.22^6487316^HTMLParserThrea^chrome               ^[.] v8::internal::Heap::AllocateConsString(v8::internal::String*, v8::internal::String*)
+            |
+            --- v8::internal::Heap::AllocateConsString(v8::internal::String*, v8::internal::String*)
+
+0.21^6470256^HTMLParserThrea^chrome               ^[.] v8::internal::StringTable::LookupOneByteString(v8::internal::Vector<unsigned char const>, v8::internal::Object**)
+            |
+            --- v8::internal::StringTable::LookupOneByteString(v8::internal::Vector<unsigned char const>, v8::internal::Object**)
+
+0.21^6461476^HTMLParserThrea^chrome               ^[.] v8::internal::JSObject::SetLocalPropertyIgnoreAttributes(v8::internal::Name*, v8::internal::Object*, PropertyAttributes, v8::internal::Object::ValueType)
+            |
+            --- v8::internal::JSObject::SetLocalPropertyIgnoreAttributes(v8::internal::Name*, v8::internal::Object*, PropertyAttributes, v8::internal::Object::ValueType)
+
+0.21^6410633^HTMLParserThrea^chrome               ^[.] v8::internal::BaseLoadStoreStubCompiler::CompilePolymorphicIC(v8::internal::List<v8::internal::Handle<v8::internal::Map>, v8::internal::FreeStoreAllocationPolicy>*, v8::internal::List<v8::internal::Handle<v8::internal::Code>, v8::internal::FreeStoreAllocationPolicy>*, v8::internal::Handle<v8::internal::Name>, v8::internal::Code::StubType, v8::internal::IcCheckType)
+            |
+            --- v8::internal::BaseLoadStoreStubCompiler::CompilePolymorphicIC(v8::internal::List<v8::internal::Handle<v8::internal::Map>, v8::internal::FreeStoreAllocationPolicy>*, v8::internal::List<v8::internal::Handle<v8::internal::Code>, v8::internal::FreeStoreAllocationPolicy>*, v8::internal::Handle<v8::internal::Name>, v8::internal::Code::StubType, v8::internal::IcCheckType)
+                0x7f7a0b900000
+
+0.21^6354296^HTMLParserThrea^chrome               ^[.] v8::internal::Parser::ParseExpressionOrLabelledStatement(v8::internal::ZoneList<v8::internal::Handle<v8::internal::String> >*, bool*)
+            |
+            --- v8::internal::Parser::ParseExpressionOrLabelledStatement(v8::internal::ZoneList<v8::internal::Handle<v8::internal::String> >*, bool*)
+               |          
+               |--55.30%-- 0x7fff30e9145000
+               |          
+                --44.70%-- 0x7fff30e906e000
+
+0.21^6319435^HTMLParserThrea^chrome               ^[.] _ZN2v88internal14NameDictionary9FindEntryEPNS0_4NameE.part.385
+            |
+            --- _ZN2v88internal14NameDictionary9FindEntryEPNS0_4NameE.part.385
+               |          
+                --100.00%-- (nil)
+
+0.21^6243685^HTMLParserThrea^chrome               ^[.] WebCore::Font::glyphDataAndPageForCharacter(int, bool, WebCore::FontDataVariant) const
+            |
+            --- WebCore::Font::glyphDataAndPageForCharacter(int, bool, WebCore::FontDataVariant) const
+                0x7fff30e8f0e0
+
+0.20^6145083^HTMLParserThrea^chrome               ^[.] v8::internal::FullCodeGenerator::AccumulatorValueContext::Plug(v8::internal::Register) const
+            |
+            --- v8::internal::FullCodeGenerator::AccumulatorValueContext::Plug(v8::internal::Register) const
+
+0.20^6137312^HTMLParserThrea^chrome               ^[.] v8::internal::JSObject::SetPropertyForResult(v8::internal::LookupResult*, v8::internal::Name*, v8::internal::Object*, PropertyAttributes, v8::internal::StrictModeFlag, v8::internal::JSReceiver::StoreFromKeyed)
+            |
+            --- v8::internal::JSObject::SetPropertyForResult(v8::internal::LookupResult*, v8::internal::Name*, v8::internal::Object*, PropertyAttributes, v8::internal::StrictModeFlag, v8::internal::JSReceiver::StoreFromKeyed)
+
+0.20^6135468^HTMLParserThrea^chrome               ^[.] v8::internal::Factory::NewNumber(double, v8::internal::PretenureFlag)
+            |
+            --- v8::internal::Factory::NewNumber(double, v8::internal::PretenureFlag)
+               |          
+                --100.00%-- 0x7fff30e9160000
+
+0.20^6051127^HTMLParserThrea^chrome               ^[.] WebCore::WidthIterator::glyphDataForCharacter(int, bool, int, unsigned int&)
+            |
+            --- WebCore::WidthIterator::glyphDataForCharacter(int, bool, int, unsigned int&)
+               |          
+               |--58.44%-- 0x7fff30e8cfa0
+               |          
+                --41.56%-- 0x7fff30e8f550
+
+0.20^6000454^HTMLParserThrea^chrome               ^[.] v8::internal::CodeCache::UpdateDefaultCache(v8::internal::Name*, v8::internal::Code*)
+            |
+            --- v8::internal::CodeCache::UpdateDefaultCache(v8::internal::Name*, v8::internal::Code*)
+               |          
+                --100.00%-- 0xc6a355788f1
+
+0.20^5976974^HTMLParserThrea^chrome               ^[.] WebCore::RenderObject::containingBlock() const
+            |
+            --- WebCore::RenderObject::containingBlock() const
+
+0.20^5926952^HTMLParserThrea^chrome               ^[.] v8::internal::GlobalHandles::Create(v8::internal::Object*)
+            |
+            --- v8::internal::GlobalHandles::Create(v8::internal::Object*)
+
+0.20^5880116^HTMLParserThrea^chrome               ^[.] v8::internal::Parser::ParseLeftHandSideExpression(bool*)
+            |
+            --- v8::internal::Parser::ParseLeftHandSideExpression(bool*)
+               |          
+                --100.00%-- 0x7fff30e9151000
+
+0.19^5834557^HTMLParserThrea^chrome               ^[.] WebCore::CachedRawResource::willSendRequest(WebCore::ResourceRequest&, WebCore::ResourceResponse const&)
+            |
+            --- WebCore::CachedRawResource::willSendRequest(WebCore::ResourceRequest&, WebCore::ResourceResponse const&)
+
+0.18^5527800^HTMLParserThrea^chrome               ^[.] WebCore::RenderBlock::paint(WebCore::PaintInfo&, WebCore::LayoutPoint const&)
+            |
+            --- WebCore::RenderBlock::paint(WebCore::PaintInfo&, WebCore::LayoutPoint const&)
+
+0.18^5461500^HTMLParserThrea^chrome               ^[.] WebCore::Document::updateStyleIfNeeded()
+            |
+            --- WebCore::Document::updateStyleIfNeeded()
+
+0.18^5428693^HTMLParserThrea^libc-2.15.so         ^[.] __memset_sse2
+            |
+            --- __memset_sse2
+
+0.18^5423805^HTMLParserThrea^chrome               ^[.] WebCore::FrameView::paintContents(WebCore::GraphicsContext*, WebCore::IntRect const&)
+            |
+            --- WebCore::FrameView::paintContents(WebCore::GraphicsContext*, WebCore::IntRect const&)
+               |          
+               |--61.15%-- 0x5a000002d8
+               |          
+                --38.85%-- 0x2e00000092
+
+0.18^5363793^HTMLParserThrea^chrome               ^[.] WebCore::HTTPHeaderMap::get(WTF::AtomicString const&) const
+            |
+            --- WebCore::HTTPHeaderMap::get(WTF::AtomicString const&) const
+
+0.18^5306454^HTMLParserThrea^chrome               ^[.] v8::internal::Scope::Scope(v8::internal::Scope*, v8::internal::ScopeType, v8::internal::Zone*)
+            |
+            --- v8::internal::Scope::Scope(v8::internal::Scope*, v8::internal::ScopeType, v8::internal::Zone*)
+
+0.18^5274362^HTMLParserThrea^chrome               ^[.] v8::internal::Assembler::bind_to(v8::internal::Label*, int)
+            |
+            --- v8::internal::Assembler::bind_to(v8::internal::Label*, int)
+               |          
+                --100.00%-- 0x3b2abd7c3000
+
+0.18^5270432^HTMLParserThrea^chrome               ^[.] SkDraw::drawPosText(char const*, unsigned long, float const*, float, int, SkPaint const&) const
+            |
+            --- SkDraw::drawPosText(char const*, unsigned long, float const*, float, int, SkPaint const&) const
+                (nil)
+               |          
+               |--55.20%-- 0x4f005200260003
+               |          
+                --44.80%-- 0x52004a00440003
+
+0.17^5196475^HTMLParserThrea^chrome               ^[.] WTF::equalNonNull(WTF::StringImpl const*, WTF::StringImpl const*)
+            |
+            --- WTF::equalNonNull(WTF::StringImpl const*, WTF::StringImpl const*)
+                0x7f7a0b975100
+
+0.17^5103842^HTMLParserThrea^chrome               ^[.] WebCore::RenderLayer::convertToLayerCoords(WebCore::RenderLayer const*, WebCore::LayoutPoint&) const
+            |
+            --- WebCore::RenderLayer::convertToLayerCoords(WebCore::RenderLayer const*, WebCore::LayoutPoint&) const
+
+0.17^4967343^HTMLParserThrea^chrome               ^[.] _ZN2v88internal4ListINS0_16FuncNameInferrer4NameENS0_20ZoneAllocationPolicyEE3AddERKS3_S4_.constprop.51
+            |
+            --- _ZN2v88internal4ListINS0_16FuncNameInferrer4NameENS0_20ZoneAllocationPolicyEE3AddERKS3_S4_.constprop.51
+
+0.16^4900762^HTMLParserThrea^libc-2.15.so         ^[.] __memcmp_sse4_1
+            |
+            --- __memcmp_sse4_1
+               |          
+                --100.00%-- 0x7f7a0b975100
+
+0.15^4610114^HTMLParserThrea^chrome               ^[.] v8::internal::JSObject::GetPropertyWithCallback(v8::internal::Object*, v8::internal::Object*, v8::internal::Name*)
+            |
+            --- v8::internal::JSObject::GetPropertyWithCallback(v8::internal::Object*, v8::internal::Object*, v8::internal::Name*)
+
+0.15^4535632^HTMLParserThrea^chrome               ^[.] v8::HandleScope::HandleScope()
+            |
+            --- v8::HandleScope::HandleScope()
+
+0.15^4419717^HTMLParserThrea^chrome               ^[.] WebCore::MarkupAccumulator::appendStartTag(WebCore::Node*, WTF::HashMap<WTF::AtomicStringImpl*, WTF::AtomicStringImpl*, WTF::PtrHash<WTF::AtomicStringImpl*>, WTF::HashTraits<WTF::AtomicStringImpl*>, WTF::HashTraits<WTF::AtomicStringImpl*> >*)
+            |
+            --- WebCore::MarkupAccumulator::appendStartTag(WebCore::Node*, WTF::HashMap<WTF::AtomicStringImpl*, WTF::AtomicStringImpl*, WTF::PtrHash<WTF::AtomicStringImpl*>, WTF::HashTraits<WTF::AtomicStringImpl*>, WTF::HashTraits<WTF::AtomicStringImpl*> >*)
+
+0.14^4269939^HTMLParserThrea^chrome               ^[.] cssyyparse(WebCore::CSSParser*)
+            |
+            --- cssyyparse(WebCore::CSSParser*)
+               |          
+               |--56.10%-- content::PepperGraphics2DHost::Paint(SkCanvas*, gfx::Rect const&, gfx::Rect const&)
+               |          
+                --43.90%-- 0x1b800000b600
+
+0.14^4189741^HTMLParserThrea^chrome               ^[.] v8::internal::HandleScope::Extend(v8::internal::Isolate*)
+            |
+            --- v8::internal::HandleScope::Extend(v8::internal::Isolate*)
+
+0.13^3989323^HTMLParserThrea^chrome               ^[.] WebCore::RenderBox::overflowRectForPaintRejection() const
+            |
+            --- WebCore::RenderBox::overflowRectForPaintRejection() const
+
+0.12^3691476^HTMLParserThrea^libstdc++.so.6.0.16  ^[.] std::_Rb_tree_increment(std::_Rb_tree_node_base*)
+            |
+            --- std::_Rb_tree_increment(std::_Rb_tree_node_base*)
+
+0.12^3619871^HTMLParserThrea^chrome               ^[.] WebCore::ChildNodeInsertionNotifier::notifyDescendantInsertedIntoTree(WebCore::ContainerNode*)
+            |
+            --- WebCore::ChildNodeInsertionNotifier::notifyDescendantInsertedIntoTree(WebCore::ContainerNode*)
+                (nil)
+
+0.12^3610233^HTMLParserThrea^chrome               ^[.] WebCore::RenderBox::addLayoutOverflow(WebCore::LayoutRect const&)
+            |
+            --- WebCore::RenderBox::addLayoutOverflow(WebCore::LayoutRect const&)
+
+0.12^3602214^HTMLParserThrea^chrome               ^[.] v8::internal::RelocIterator::RelocIterator(v8::internal::Code*, int)
+            |
+            --- v8::internal::RelocIterator::RelocIterator(v8::internal::Code*, int)
+
+0.12^3592533^HTMLParserThrea^chrome               ^[.] v8::internal::MarkCompactCollector::MigrateObject(unsigned char*, unsigned char*, int, v8::internal::AllocationSpace)
+            |
+            --- v8::internal::MarkCompactCollector::MigrateObject(unsigned char*, unsigned char*, int, v8::internal::AllocationSpace)
+
+0.12^3591487^HTMLParserThrea^chrome               ^[.] v8::internal::StringTableCleaner::VisitPointers(v8::internal::Object**, v8::internal::Object**)
+            |
+            --- v8::internal::StringTableCleaner::VisitPointers(v8::internal::Object**, v8::internal::Object**)
+
+0.12^3590966^HTMLParserThrea^chrome               ^[.] v8::internal::MarkCompactCollector::DiscoverAndPromoteBlackObjectsOnPage(v8::internal::NewSpace*, v8::internal::NewSpacePage*)
+            |
+            --- v8::internal::MarkCompactCollector::DiscoverAndPromoteBlackObjectsOnPage(v8::internal::NewSpace*, v8::internal::NewSpacePage*)
+
+0.12^3590884^HTMLParserThrea^chrome               ^[.] v8::internal::MarkCompactCollector::ClearNonLiveReferences()
+            |
+            --- v8::internal::MarkCompactCollector::ClearNonLiveReferences()
+
+0.12^3590478^HTMLParserThrea^chrome               ^[.] void std::__introsort_loop<v8::internal::ObjectGroupConnection*, long>(v8::internal::ObjectGroupConnection*, v8::internal::ObjectGroupConnection*, long)
+            |
+            --- void std::__introsort_loop<v8::internal::ObjectGroupConnection*, long>(v8::internal::ObjectGroupConnection*, v8::internal::ObjectGroupConnection*, long)
+
+0.12^3590200^HTMLParserThrea^chrome               ^[.] v8::internal::StaticMarkingVisitor<v8::internal::MarkCompactMarkingVisitor>::VisitPropertyCell(v8::internal::Map*, v8::internal::HeapObject*)
+            |
+            --- v8::internal::StaticMarkingVisitor<v8::internal::MarkCompactMarkingVisitor>::VisitPropertyCell(v8::internal::Map*, v8::internal::HeapObject*)
+
+0.12^3589094^HTMLParserThrea^chrome               ^[.] long v8::internal::MarkCompactCollector::SweepConservatively<(v8::internal::MarkCompactCollector::SweepingParallelism)0>(v8::internal::PagedSpace*, v8::internal::FreeList*, v8::internal::Page*)
+            |
+            --- long v8::internal::MarkCompactCollector::SweepConservatively<(v8::internal::MarkCompactCollector::SweepingParallelism)0>(v8::internal::PagedSpace*, v8::internal::FreeList*, v8::internal::Page*)
+
+0.12^3589023^HTMLParserThrea^chrome               ^[.] WebCore::RenderStyle::~RenderStyle()
+            |
+            --- WebCore::RenderStyle::~RenderStyle()
+
+0.12^3588259^HTMLParserThrea^chrome               ^[.] v8::internal::FlexibleBodyVisitor<v8::internal::MarkCompactMarkingVisitor, v8::internal::JSObject::BodyDescriptor, void>::Visit(v8::internal::Map*, v8::internal::HeapObject*)
+            |
+            --- v8::internal::FlexibleBodyVisitor<v8::internal::MarkCompactMarkingVisitor, v8::internal::JSObject::BodyDescriptor, void>::Visit(v8::internal::Map*, v8::internal::HeapObject*)
+
+0.12^3588121^HTMLParserThrea^chrome               ^[.] v8::internal::GlobalHandles::IterateWeakRoots(v8::internal::ObjectVisitor*)
+            |
+            --- v8::internal::GlobalHandles::IterateWeakRoots(v8::internal::ObjectVisitor*)
+
+0.12^3587651^HTMLParserThrea^chrome               ^[.] v8::internal::StaticMarkingVisitor<v8::internal::MarkCompactMarkingVisitor>::MarkTransitionArray(v8::internal::Heap*, v8::internal::TransitionArray*)
+            |
+            --- v8::internal::StaticMarkingVisitor<v8::internal::MarkCompactMarkingVisitor>::MarkTransitionArray(v8::internal::Heap*, v8::internal::TransitionArray*)
+
+0.12^3585248^HTMLParserThrea^chrome               ^[.] v8::internal::SequentialStringKey<unsigned char>::Hash()
+            |
+            --- v8::internal::SequentialStringKey<unsigned char>::Hash()
+
+0.12^3584278^HTMLParserThrea^chrome               ^[.] v8::internal::ChoiceNode::Emit(v8::internal::RegExpCompiler*, v8::internal::Trace*)
+            |
+            --- v8::internal::ChoiceNode::Emit(v8::internal::RegExpCompiler*, v8::internal::Trace*)
+                0x3b2abd5271d8
+
+0.12^3583348^HTMLParserThrea^chrome               ^[.] v8::internal::LoadIC::UpdateMonomorphicIC(v8::internal::Handle<v8::internal::JSObject>, v8::internal::Handle<v8::internal::Code>, v8::internal::Handle<v8::internal::String>, v8::internal::StrictModeFlag)
+            |
+            --- v8::internal::LoadIC::UpdateMonomorphicIC(v8::internal::Handle<v8::internal::JSObject>, v8::internal::Handle<v8::internal::Code>, v8::internal::Handle<v8::internal::String>, v8::internal::StrictModeFlag)
+                0xc6a34dd2791
+
+0.12^3582808^HTMLParserThrea^chrome               ^[.] v8::internal::Parser::ParseIfStatement(v8::internal::ZoneList<v8::internal::Handle<v8::internal::String> >*, bool*)
+            |
+            --- v8::internal::Parser::ParseIfStatement(v8::internal::ZoneList<v8::internal::Handle<v8::internal::String> >*, bool*)
+                0x3b2abdf3f010
+
+0.12^3582142^HTMLParserThrea^chrome               ^[.] WebCore::RenderBlock::addOverflowFromInlineChildren()
+            |
+            --- WebCore::RenderBlock::addOverflowFromInlineChildren()
+
+0.12^3581759^HTMLParserThrea^chrome               ^[.] _ZN7WebCore10RenderText29computePreferredLogicalWidthsEfRN3WTF7HashSetIPKNS_14SimpleFontDataENS1_7PtrHashIS5_EENS1_10HashTraitsIS5_EEEERNS_13GlyphOverflowE.part.184
+            |
+            --- _ZN7WebCore10RenderText29computePreferredLogicalWidthsEfRN3WTF7HashSetIPKNS_14SimpleFontDataENS1_7PtrHashIS5_EENS1_10HashTraitsIS5_EEEERNS_13GlyphOverflowE.part.184
+
+0.12^3581038^HTMLParserThrea^chrome               ^[.] WTF::String WebCore::v8StringToWebCoreString<WTF::String>(v8::Handle<v8::String>, WebCore::ExternalMode)
+            |
+            --- WTF::String WebCore::v8StringToWebCoreString<WTF::String>(v8::Handle<v8::String>, WebCore::ExternalMode)
+                0xc6a34d43fe9
+
+0.12^3581023^HTMLParserThrea^chrome               ^[.] WebCore::RenderInline::marginLeft() const
+            |
+            --- WebCore::RenderInline::marginLeft() const
+
+0.12^3580522^HTMLParserThrea^chrome               ^[.] WTF::Vector<WebCore::GraphicsContext::DeferredSaveState, 0ul>::expandCapacity(unsigned long)
+            |
+            --- WTF::Vector<WebCore::GraphicsContext::DeferredSaveState, 0ul>::expandCapacity(unsigned long)
+                0x100017718028
+
+0.12^3580338^HTMLParserThrea^chrome               ^[.] v8::internal::Runtime_MaterializeRegExpLiteral(int, v8::internal::Object**, v8::internal::Isolate*)
+            |
+            --- v8::internal::Runtime_MaterializeRegExpLiteral(int, v8::internal::Object**, v8::internal::Isolate*)
+
+0.12^3579940^HTMLParserThrea^chrome               ^[.] v8::internal::FunctionLiteral::AllowsLazyCompilation()
+            |
+            --- v8::internal::FunctionLiteral::AllowsLazyCompilation()
+
+0.12^3579555^HTMLParserThrea^chrome               ^[.] WebCore::XMLHttpRequest::isAllowedHTTPMethod(WTF::String const&)
+            |
+            --- WebCore::XMLHttpRequest::isAllowedHTTPMethod(WTF::String const&)
+                0x7f7a0b900000
+
+0.12^3579477^HTMLParserThrea^chrome               ^[.] ppapi::PpapiGlobals::Get()
+            |
+            --- ppapi::PpapiGlobals::Get()
+
+0.12^3579477^HTMLParserThrea^chrome               ^[.] v8::internal::StaticVisitorBase::GetVisitorId(int, int)
+            |
+            --- v8::internal::StaticVisitorBase::GetVisitorId(int, int)
+
+0.12^3578703^HTMLParserThrea^chrome               ^[.] v8::internal::LoadIC::ComputePolymorphicIC(v8::internal::List<v8::internal::Handle<v8::internal::Map>, v8::internal::FreeStoreAllocationPolicy>*, v8::internal::List<v8::internal::Handle<v8::internal::Code>, v8::internal::FreeStoreAllocationPolicy>*, int, v8::internal::Handle<v8::internal::Name>, v8::internal::StrictModeFlag)
+            |
+            --- v8::internal::LoadIC::ComputePolymorphicIC(v8::internal::List<v8::internal::Handle<v8::internal::Map>, v8::internal::FreeStoreAllocationPolicy>*, v8::internal::List<v8::internal::Handle<v8::internal::Code>, v8::internal::FreeStoreAllocationPolicy>*, int, v8::internal::Handle<v8::internal::Name>, v8::internal::StrictModeFlag)
+                0xc6a354cfa21
+
+0.12^3578592^HTMLParserThrea^chrome               ^[.] v8::internal::Parser::BuildObjectLiteralConstantProperties(v8::internal::ZoneList<v8::internal::ObjectLiteralProperty*>*, v8::internal::Handle<v8::internal::FixedArray>, bool*, bool*, int*, bool*)
+            |
+            --- v8::internal::Parser::BuildObjectLiteralConstantProperties(v8::internal::ZoneList<v8::internal::ObjectLiteralProperty*>*, v8::internal::Handle<v8::internal::FixedArray>, bool*, bool*, int*, bool*)
+
+0.12^3578004^HTMLParserThrea^chrome               ^[.] v8::internal::StubCache::ComputePolymorphicLoadIC(v8::internal::List<v8::internal::Handle<v8::internal::Map>, v8::internal::FreeStoreAllocationPolicy>*, v8::internal::List<v8::internal::Handle<v8::internal::Code>, v8::internal::FreeStoreAllocationPolicy>*, int, v8::internal::Handle<v8::internal::Name>)
+            |
+            --- v8::internal::StubCache::ComputePolymorphicLoadIC(v8::internal::List<v8::internal::Handle<v8::internal::Map>, v8::internal::FreeStoreAllocationPolicy>*, v8::internal::List<v8::internal::Handle<v8::internal::Code>, v8::internal::FreeStoreAllocationPolicy>*, int, v8::internal::Handle<v8::internal::Name>)
+                0xc6a3444c2a9
+
+0.12^3577719^HTMLParserThrea^chrome               ^[.] v8::internal::BoyerMooreLookahead::FindBestInterval(int, int, int*, int*)
+            |
+            --- v8::internal::BoyerMooreLookahead::FindBestInterval(int, int, int*, int*)
+
+0.12^3577405^HTMLParserThrea^chrome               ^[.] WebCore::RenderBoxModelObject::continuation() const
+            |
+            --- WebCore::RenderBoxModelObject::continuation() const
+
+0.12^3576942^HTMLParserThrea^chrome               ^[.] WebCore::RenderBlock::hasPercentHeightDescendant(WebCore::RenderBox*)
+            |
+            --- WebCore::RenderBlock::hasPercentHeightDescendant(WebCore::RenderBox*)
+
+0.12^3575468^HTMLParserThrea^chrome               ^[.] v8::internal::VariableProxy* v8::internal::Scope::NewUnresolved<v8::internal::AstConstructionVisitor>(v8::internal::AstNodeFactory<v8::internal::AstConstructionVisitor>*, v8::internal::Handle<v8::internal::String>, v8::internal::Interface*, int)
+            |
+            --- v8::internal::VariableProxy* v8::internal::Scope::NewUnresolved<v8::internal::AstConstructionVisitor>(v8::internal::AstNodeFactory<v8::internal::AstConstructionVisitor>*, v8::internal::Handle<v8::internal::String>, v8::internal::Interface*, int)
+                0x7fff30e91928
+
+0.12^3575428^HTMLParserThrea^chrome               ^[.] WebCore::BidiResolver<WebCore::InlineIterator, WebCore::BidiRun>::createBidiRunsForLine(WebCore::InlineIterator const&, WebCore::VisualDirectionOverride, bool)
+            |
+            --- WebCore::BidiResolver<WebCore::InlineIterator, WebCore::BidiRun>::createBidiRunsForLine(WebCore::InlineIterator const&, WebCore::VisualDirectionOverride, bool)
+                0x3b2abd843120
+
+0.12^3574896^HTMLParserThrea^chrome               ^[.] v8::internal::FixedArray::set(int, v8::internal::Object*, v8::internal::WriteBarrierMode)
+            |
+            --- v8::internal::FixedArray::set(int, v8::internal::Object*, v8::internal::WriteBarrierMode)
+
+0.12^3574498^HTMLParserThrea^chrome               ^[.] v8::internal::String::Equals(v8::internal::String*)
+            |
+            --- v8::internal::String::Equals(v8::internal::String*)
+
+0.12^3574405^HTMLParserThrea^chrome               ^[.] v8::internal::FullCodeGenerator::ExpressionContext::IsTest() const
+            |
+            --- v8::internal::FullCodeGenerator::ExpressionContext::IsTest() const
+
+0.12^3574327^HTMLParserThrea^chrome               ^[.] v8::internal::Scope::ResolveVariable(v8::internal::CompilationInfo*, v8::internal::VariableProxy*, v8::internal::AstNodeFactory<v8::internal::AstNullVisitor>*)
+            |
+            --- v8::internal::Scope::ResolveVariable(v8::internal::CompilationInfo*, v8::internal::VariableProxy*, v8::internal::AstNodeFactory<v8::internal::AstNullVisitor>*)
+
+0.12^3573789^HTMLParserThrea^chrome               ^[.] v8::internal::Heap::AllocateMap(v8::internal::InstanceType, int, v8::internal::ElementsKind)
+            |
+            --- v8::internal::Heap::AllocateMap(v8::internal::InstanceType, int, v8::internal::ElementsKind)
+
+0.12^3573631^HTMLParserThrea^chrome               ^[.] v8::internal::Runtime::CreateArrayLiteralBoilerplate(v8::internal::Isolate*, v8::internal::Handle<v8::internal::FixedArray>, v8::internal::Handle<v8::internal::FixedArray>)
+            |
+            --- v8::internal::Runtime::CreateArrayLiteralBoilerplate(v8::internal::Isolate*, v8::internal::Handle<v8::internal::FixedArray>, v8::internal::Handle<v8::internal::FixedArray>)
+
+0.12^3573214^HTMLParserThrea^chrome               ^[.] WTF::HashTableIterator<WebCore::CachedResource*, WTF::KeyValuePair<WebCore::CachedResource*, WTF::RefPtr<WebCore::ResourceTimingInfo> >, WTF::KeyValuePairKeyExtractor<WTF::KeyValuePair<WebCore::CachedResource*, WTF::RefPtr<WebCore::ResourceTimingInfo> > >, WTF::PtrHash<WebCore::CachedResource*>, WTF::HashMapValueTraits<WTF::HashTraits<WebCore::CachedResource*>, WTF::HashTraits<WTF::RefPtr<WebCore::ResourceTimingInfo> > >, WTF::HashTraits<WebCore::Cache
+            |
+            --- WTF::HashTableIterator<WebCore::CachedResource*, WTF::KeyValuePair<WebCore::CachedResource*, WTF::RefPtr<WebCore::ResourceTimingInfo> >, WTF::KeyValuePairKeyExtractor<WTF::KeyValuePair<WebCore::CachedResource*, WTF::RefPtr<WebCore::ResourceTimingInfo> > >, WTF::PtrHash<WebCore::CachedResource*>, WTF::HashMapValueTraits<WTF::HashTraits<WebCore::CachedResource*>, WTF::HashTraits<WTF::RefPtr<WebCore::ResourceTimingInfo> > >, WTF::HashTraits<WebCore::CachedResource*> > WTF::HashTable<WebCore::CachedResource*, WTF::KeyValuePair<WebCore::CachedResource*, WTF::RefPtr<WebCore::ResourceTimingInfo> >, WTF::KeyValuePairKeyExtractor<WTF::KeyValuePair<WebCore::CachedResource*, WTF::RefPtr<WebCore::ResourceTimingInfo> > >, WTF::PtrHash<WebCore::CachedResource*>, WTF::HashMapValueTraits<WTF::HashTraits<WebCore::CachedResource*>, WTF::HashTraits<WTF::RefPtr<WebCore::ResourceTimingInfo> > >, WTF::HashTraits<WebCore::CachedResource*> >::find<WTF::IdentityHashTranslator<WTF::PtrHash<WebCore::CachedResource*> >, WebCore::CachedResource*>(WebCore::CachedResource* const&)
+
+0.12^3573010^HTMLParserThrea^chrome               ^[.] Pickle::Resize(unsigned long)
+            |
+            --- Pickle::Resize(unsigned long)
+
+0.12^3572646^HTMLParserThrea^chrome               ^[.] WebCore::RenderBlock::floatingObjects() const
+            |
+            --- WebCore::RenderBlock::floatingObjects() const
+
+0.12^3572547^HTMLParserThrea^chrome               ^[.] WebCore::LiveNodeListBase::item(unsigned int) const
+            |
+            --- WebCore::LiveNodeListBase::item(unsigned int) const
+
+0.12^3572456^HTMLParserThrea^chrome               ^[.] WebCore::RenderBox::computeLogicalHeight(WebCore::LayoutUnit, WebCore::LayoutUnit, WebCore::RenderBox::LogicalExtentComputedValues&) const
+            |
+            --- WebCore::RenderBox::computeLogicalHeight(WebCore::LayoutUnit, WebCore::LayoutUnit, WebCore::RenderBox::LogicalExtentComputedValues&) const
+                (nil)
+
+0.12^3572406^HTMLParserThrea^chrome               ^[.] WebCore::ScriptController::callFunction(v8::Handle<v8::Function>, v8::Handle<v8::Object>, int, v8::Handle<v8::Value>*)
+            |
+            --- WebCore::ScriptController::callFunction(v8::Handle<v8::Function>, v8::Handle<v8::Object>, int, v8::Handle<v8::Value>*)
+
+0.12^3571839^HTMLParserThrea^chrome               ^[.] WTF::HashTableAddResult<WTF::HashTableIterator<WTF::AtomicStringImpl*, WTF::KeyValuePair<WTF::AtomicStringImpl*, WTF::OwnPtr<WTF::LinkedStack<WebCore::RuleData> > >, WTF::KeyValuePairKeyExtractor<WTF::KeyValuePair<WTF::AtomicStringImpl*, WTF::OwnPtr<WTF::LinkedStack<WebCore::RuleData> > > >, WTF::PtrHash<WTF::AtomicStringImpl*>, WTF::HashMapValueTraits<WTF::HashTraits<WTF::AtomicStringImpl*>, WTF::HashTraits<WTF::OwnPtr<WTF::LinkedStack<WebCore::RuleDa
+            |
+            --- WTF::HashTableAddResult<WTF::HashTableIterator<WTF::AtomicStringImpl*, WTF::KeyValuePair<WTF::AtomicStringImpl*, WTF::OwnPtr<WTF::LinkedStack<WebCore::RuleData> > >, WTF::KeyValuePairKeyExtractor<WTF::KeyValuePair<WTF::AtomicStringImpl*, WTF::OwnPtr<WTF::LinkedStack<WebCore::RuleData> > > >, WTF::PtrHash<WTF::AtomicStringImpl*>, WTF::HashMapValueTraits<WTF::HashTraits<WTF::AtomicStringImpl*>, WTF::HashTraits<WTF::OwnPtr<WTF::LinkedStack<WebCore::RuleData> > > >, WTF::HashTraits<WTF::AtomicStringImpl*> > > WTF::HashTable<WTF::AtomicStringImpl*, WTF::KeyValuePair<WTF::AtomicStringImpl*, WTF::OwnPtr<WTF::LinkedStack<WebCore::RuleData> > >, WTF::KeyValuePairKeyExtractor<WTF::KeyValuePair<WTF::AtomicStringImpl*, WTF::OwnPtr<WTF::LinkedStack<WebCore::RuleData> > > >, WTF::PtrHash<WTF::AtomicStringImpl*>, WTF::HashMapValueTraits<WTF::HashTraits<WTF::AtomicStringImpl*>, WTF::HashTraits<WTF::OwnPtr<WTF::LinkedStack<WebCore::RuleData> > > >, WTF::HashTraits<WTF::AtomicStringImpl*> >::add<WTF::HashMapTranslator<WTF::HashMapValueTraits<WTF::HashTraits<WTF::AtomicStringImpl*>, WTF::HashTraits<WTF::OwnPtr<WTF::LinkedStack<WebCore::RuleData> > > >, WTF::PtrHash<WTF::AtomicStringImpl*> >, WTF::AtomicStringImpl*, WTF::PassOwnPtr<WTF::LinkedStack<WebCore::RuleData> > >(WTF::AtomicStringImpl* const&, WTF::PassOwnPtr<WTF::LinkedStack<WebCore::RuleData> > const&)
+
+0.12^3571626^HTMLParserThrea^chrome               ^[.] v8::internal::Factory::ObjectLiteralMapFromCache(v8::internal::Handle<v8::internal::Context>, v8::internal::Handle<v8::internal::FixedArray>)
+            |
+            --- v8::internal::Factory::ObjectLiteralMapFromCache(v8::internal::Handle<v8::internal::Context>, v8::internal::Handle<v8::internal::FixedArray>)
+
+0.12^3571455^HTMLParserThrea^chrome               ^[.] WebCore::RenderView::intervalArena()
+            |
+            --- WebCore::RenderView::intervalArena()
+
+0.12^3571267^HTMLParserThrea^chrome               ^[.] v8::internal::VarAndOrder::Compare(v8::internal::VarAndOrder const*, v8::internal::VarAndOrder const*)
+            |
+            --- v8::internal::VarAndOrder::Compare(v8::internal::VarAndOrder const*, v8::internal::VarAndOrder const*)
+
+0.12^3570914^HTMLParserThrea^chrome               ^[.] bool WebCore::shouldInvalidateNodeListCachesForAttr<2u>(unsigned int const*, WebCore::QualifiedName const&)
+            |
+            --- bool WebCore::shouldInvalidateNodeListCachesForAttr<2u>(unsigned int const*, WebCore::QualifiedName const&)
+
+0.12^3570523^HTMLParserThrea^chrome               ^[.] v8::internal::CodeCacheHashTableKey::IsMatch(v8::internal::Object*)
+            |
+            --- v8::internal::CodeCacheHashTableKey::IsMatch(v8::internal::Object*)
+
+0.12^3570308^HTMLParserThrea^chrome               ^[.] WebCore::RenderBox::hasRelativeDimensions() const
+            |
+            --- WebCore::RenderBox::hasRelativeDimensions() const
+
+0.12^3570109^HTMLParserThrea^chrome               ^[.] WTF::HashMap<WTF::AtomicStringImpl*, WTF::OwnPtr<WebCore::RuleData>, WTF::PtrHash<WTF::AtomicStringImpl*>, WTF::HashTraits<WTF::AtomicStringImpl*>, WTF::HashTraits<WTF::OwnPtr<WebCore::RuleData> > >::get(WTF::AtomicStringImpl* const&) const
+            |
+            --- WTF::HashMap<WTF::AtomicStringImpl*, WTF::OwnPtr<WebCore::RuleData>, WTF::PtrHash<WTF::AtomicStringImpl*>, WTF::HashTraits<WTF::AtomicStringImpl*>, WTF::HashTraits<WTF::OwnPtr<WebCore::RuleData> > >::get(WTF::AtomicStringImpl* const&) const
+                void WTF::Vector<WebCore::StyleRule*, 64ul>::appendSlowCase<WebCore::StyleRule*>(WebCore::StyleRule* const&)
+
+0.12^3569976^HTMLParserThrea^chrome               ^[.] v8::internal::IC::address() const
+            |
+            --- v8::internal::IC::address() const
+
+0.12^3569735^HTMLParserThrea^chrome               ^[.] v8::internal::StringTable::LookupUtf8String(v8::internal::Vector<char const>, v8::internal::Object**)
+            |
+            --- v8::internal::StringTable::LookupUtf8String(v8::internal::Vector<char const>, v8::internal::Object**)
+
+0.12^3569690^HTMLParserThrea^chrome               ^[.] WebCore::RenderBlock::updateLogicalWidthForAlignment(WebCore::ETextAlign const&, WebCore::BidiRun*, float&, float&, float&, int)
+            |
+            --- WebCore::RenderBlock::updateLogicalWidthForAlignment(WebCore::ETextAlign const&, WebCore::BidiRun*, float&, float&, float&, int)
+
+0.12^3569632^HTMLParserThrea^chrome               ^[.] content::GetContentClient()
+            |
+            --- content::GetContentClient()
+                WebCore::Node::invalidateNodeListCachesInAncestors(WebCore::QualifiedName const*, WebCore::Element*)
+
+0.12^3569389^HTMLParserThrea^chrome               ^[.] v8::internal::Scanner::SkipSingleLineComment()
+            |
+            --- v8::internal::Scanner::SkipSingleLineComment()
+
+0.12^3568302^HTMLParserThrea^chrome               ^[.] v8::internal::PagedSpace::AllocateRaw(int)
+            |
+            --- v8::internal::PagedSpace::AllocateRaw(int)
+                0x5d00001000
+
+0.12^3567675^HTMLParserThrea^chrome               ^[.] v8::internal::HashTable<v8::internal::NameDictionaryShape, v8::internal::Name*>::FindEntry(v8::internal::Isolate*, v8::internal::Name*)
+            |
+            --- v8::internal::HashTable<v8::internal::NameDictionaryShape, v8::internal::Name*>::FindEntry(v8::internal::Isolate*, v8::internal::Name*)
+
+0.12^3567487^HTMLParserThrea^chrome               ^[.] WebCore::ImageLoader::updateFromElement()
+            |
+            --- WebCore::ImageLoader::updateFromElement()
+
+0.12^3567473^HTMLParserThrea^chrome               ^[.] WebCore::RenderObject::localToContainerQuad(WebCore::FloatQuad const&, WebCore::RenderLayerModelObject const*, unsigned int, bool*) const
+            |
+            --- WebCore::RenderObject::localToContainerQuad(WebCore::FloatQuad const&, WebCore::RenderLayerModelObject const*, unsigned int, bool*) const
+
+0.12^3567274^HTMLParserThrea^chrome               ^[.] webCoreInitializeScriptWrappableForInterface(WebCore::DocumentFragment*)
+            |
+            --- webCoreInitializeScriptWrappableForInterface(WebCore::DocumentFragment*)
+
+0.12^3567005^HTMLParserThrea^chrome               ^[.] WebCore::Frame::settings() const
+            |
+            --- WebCore::Frame::settings() const
+
+0.12^3566636^HTMLParserThrea^chrome               ^[.] v8::internal::Parser::ParseIdentifier(bool*)
+            |
+            --- v8::internal::Parser::ParseIdentifier(bool*)
+                0x7fff30e90bf000
+
+0.12^3566359^HTMLParserThrea^chrome               ^[.] WebCore::CachedImage::didAddClient(WebCore::CachedResourceClient*)
+            |
+            --- WebCore::CachedImage::didAddClient(WebCore::CachedResourceClient*)
+
+0.12^3566119^HTMLParserThrea^chrome               ^[.] WebCore::RenderStyle::diff(WebCore::RenderStyle const*, unsigned int&) const
+            |
+            --- WebCore::RenderStyle::diff(WebCore::RenderStyle const*, unsigned int&) const
+
+0.12^3565713^HTMLParserThrea^chrome               ^[.] WebCore::LiveNodeListBase::invalidateCache() const
+            |
+            --- WebCore::LiveNodeListBase::invalidateCache() const
+
+0.12^3565698^HTMLParserThrea^chrome               ^[.] WebCore::SelectorChecker::SelectorChecker(WebCore::Document*, WebCore::SelectorChecker::Mode)
+            |
+            --- WebCore::SelectorChecker::SelectorChecker(WebCore::Document*, WebCore::SelectorChecker::Mode)
+
+0.12^3565455^HTMLParserThrea^chrome               ^[.] WebCore::HTMLTreeBuilder::processEndTag(WebCore::AtomicHTMLToken*)
+            |
+            --- WebCore::HTMLTreeBuilder::processEndTag(WebCore::AtomicHTMLToken*)
+                0x7f7a11e52eb0
+
+0.12^3565182^HTMLParserThrea^chrome               ^[.] v8::internal::Assembler::arithmetic_op_32(unsigned char, v8::internal::Register, v8::internal::Register)
+            |
+            --- v8::internal::Assembler::arithmetic_op_32(unsigned char, v8::internal::Register, v8::internal::Register)
+
+0.12^3565074^HTMLParserThrea^chrome               ^[.] v8::internal::TranslationBuffer::Add(int, v8::internal::Zone*)
+            |
+            --- v8::internal::TranslationBuffer::Add(int, v8::internal::Zone*)
+
+0.12^3565037^HTMLParserThrea^chrome               ^[.] WebCore::Node::parentOrShadowHostElement() const
+            |
+            --- WebCore::Node::parentOrShadowHostElement() const
+
+0.12^3564789^HTMLParserThrea^chrome               ^[.] void v8::internal::FlexibleBodyVisitor<v8::internal::MarkCompactMarkingVisitor, v8::internal::StructBodyDescriptor, void>::VisitSpecialized<56>(v8::internal::Map*, v8::internal::HeapObject*)
+            |
+            --- void v8::internal::FlexibleBodyVisitor<v8::internal::MarkCompactMarkingVisitor, v8::internal::StructBodyDescriptor, void>::VisitSpecialized<56>(v8::internal::Map*, v8::internal::HeapObject*)
+
+0.12^3564677^HTMLParserThrea^chrome               ^[.] v8::internal::Map::RawCopy(int)
+            |
+            --- v8::internal::Map::RawCopy(int)
+
+0.12^3563819^HTMLParserThrea^chrome               ^[.] _ZN2v88internal8LCodeGen16WriteTranslationEPNS0_12LEnvironmentEPNS0_11TranslationE.constprop.384
+            |
+            --- _ZN2v88internal8LCodeGen16WriteTranslationEPNS0_12LEnvironmentEPNS0_11TranslationE.constprop.384
+
+0.12^3563122^HTMLParserThrea^chrome               ^[.] WebCore::MatchedProperties::~MatchedProperties()
+            |
+            --- WebCore::MatchedProperties::~MatchedProperties()
+                0x3b2abdb056b8
+                0x1e83176feba8
+
+0.12^3562575^HTMLParserThrea^chrome               ^[.] v8::internal::FullCodeGenerator::MakeCode(v8::internal::CompilationInfo*)
+            |
+            --- v8::internal::FullCodeGenerator::MakeCode(v8::internal::CompilationInfo*)
+
+0.12^3561995^HTMLParserThrea^chrome               ^[.] v8::internal::BaseLoadStoreStubCompiler::GetICCode(v8::internal::Code::Kind, v8::internal::Code::StubType, v8::internal::Handle<v8::internal::Name>, v8::internal::InlineCacheState)
+            |
+            --- v8::internal::BaseLoadStoreStubCompiler::GetICCode(v8::internal::Code::Kind, v8::internal::Code::StubType, v8::internal::Handle<v8::internal::Name>, v8::internal::InlineCacheState)
+                0x9d2ae798179
+
+0.12^3561893^HTMLParserThrea^chrome               ^[.] int v8::internal::FlexibleBodyVisitor<v8::internal::NewSpaceScavenger, v8::internal::JSObject::BodyDescriptor, int>::VisitSpecialized<24>(v8::internal::Map*, v8::internal::HeapObject*)
+            |
+            --- int v8::internal::FlexibleBodyVisitor<v8::internal::NewSpaceScavenger, v8::internal::JSObject::BodyDescriptor, int>::VisitSpecialized<24>(v8::internal::Map*, v8::internal::HeapObject*)
+
+0.12^3561621^HTMLParserThrea^chrome               ^[.] WTF::AtomicString::addSlowCase(WTF::StringImpl*)
+            |
+            --- WTF::AtomicString::addSlowCase(WTF::StringImpl*)
+
+0.12^3561375^HTMLParserThrea^chrome               ^[.] WebCore::StyleResolver::styleForElement(WebCore::Element*, WebCore::RenderStyle*, WebCore::StyleSharingBehavior, WebCore::RuleMatchingBehavior, WebCore::RenderRegion*)
+            |
+            --- WebCore::StyleResolver::styleForElement(WebCore::Element*, WebCore::RenderStyle*, WebCore::StyleSharingBehavior, WebCore::RuleMatchingBehavior, WebCore::RenderRegion*)
+
+0.12^3561200^HTMLParserThrea^chrome               ^[.] v8::internal::Builtins::LoadIC_PreMonomorphic()
+            |
+            --- v8::internal::Builtins::LoadIC_PreMonomorphic()
+                0x29f9884600a9
+
+0.12^3561181^HTMLParserThrea^chrome               ^[.] WebCore::FloatPoint::FloatPoint(WebCore::LayoutPoint const&)
+            |
+            --- WebCore::FloatPoint::FloatPoint(WebCore::LayoutPoint const&)
+                WebCore::RenderBox::clippedOverflowRectForRepaint(WebCore::RenderLayerModelObject const*) const
+                WebCore::RenderBox::contentWidth() const
+
+0.12^3561117^HTMLParserThrea^chrome               ^[.] v8::internal::LoadStubCompiler::registers()
+            |
+            --- v8::internal::LoadStubCompiler::registers()
+                0xc6a34d0ef91
+
+0.12^3561066^HTMLParserThrea^chrome               ^[.] WebCore::BorderData::BorderData()
+            |
+            --- WebCore::BorderData::BorderData()
+
+0.12^3560598^HTMLParserThrea^chrome               ^[.] WebCore::HTMLElement::parseAttribute(WebCore::QualifiedName const&, WTF::AtomicString const&)
+            |
+            --- WebCore::HTMLElement::parseAttribute(WebCore::QualifiedName const&, WTF::AtomicString const&)
+
+0.12^3560313^HTMLParserThrea^chrome               ^[.] WebCore::RenderBlock::createLineBoxesFromBidiRuns(WebCore::BidiRunList<WebCore::BidiRun>&, WebCore::InlineIterator const&, WebCore::LineInfo&, WebCore::VerticalPositionCache&, WebCore::BidiRun*, WTF::Vector<WebCore::WordMeasurement, 64ul>&)
+            |
+            --- WebCore::RenderBlock::createLineBoxesFromBidiRuns(WebCore::BidiRunList<WebCore::BidiRun>&, WebCore::InlineIterator const&, WebCore::LineInfo&, WebCore::VerticalPositionCache&, WebCore::BidiRun*, WTF::Vector<WebCore::WordMeasurement, 64ul>&)
+                0x3b2abd54deb0
+
+0.12^3560066^HTMLParserThrea^chrome               ^[.] void WebCore::StringCache::setReturnValueFromString<v8::PropertyCallbackInfo<v8::Value> >(v8::PropertyCallbackInfo<v8::Value> const&, WTF::StringImpl*, v8::Isolate*)
+            |
+            --- void WebCore::StringCache::setReturnValueFromString<v8::PropertyCallbackInfo<v8::Value> >(v8::PropertyCallbackInfo<v8::Value> const&, WTF::StringImpl*, v8::Isolate*)
+                0xc6a35a2a0c9
+                v8::internal::JSReceiver::LocalLookup(v8::internal::Name*, v8::internal::LookupResult*, bool)
+
+0.12^3560037^HTMLParserThrea^chrome               ^[.] v8::internal::StaticMarkingVisitor<v8::internal::MarkCompactMarkingVisitor>::VisitSharedFunctionInfo(v8::internal::Map*, v8::internal::HeapObject*)
+            |
+            --- v8::internal::StaticMarkingVisitor<v8::internal::MarkCompactMarkingVisitor>::VisitSharedFunctionInfo(v8::internal::Map*, v8::internal::HeapObject*)
+
+0.12^3559792^HTMLParserThrea^chrome               ^[.] WebCore::ComposedShadowTreeWalker::traverseParent(WebCore::Node const*, WebCore::NodeRenderingTraversal::ParentDetails*) const
+            |
+            --- WebCore::ComposedShadowTreeWalker::traverseParent(WebCore::Node const*, WebCore::NodeRenderingTraversal::ParentDetails*) const
+
+0.12^3559628^HTMLParserThrea^chrome               ^[.] WebCore::Element::rebuildPresentationAttributeStyle()
+            |
+            --- WebCore::Element::rebuildPresentationAttributeStyle()
+                0x1e83176beb48
+
+0.12^3558753^HTMLParserThrea^chrome               ^[.] v8::internal::LoadStubCompiler::JitEvent(v8::internal::Handle<v8::internal::Name>, v8::internal::Handle<v8::internal::Code>)
+            |
+            --- v8::internal::LoadStubCompiler::JitEvent(v8::internal::Handle<v8::internal::Name>, v8::internal::Handle<v8::internal::Code>)
+                0xc6a354902f1
+
+0.12^3557965^HTMLParserThrea^chrome               ^[.] v8::internal::Assembler::lea(v8::internal::Register, v8::internal::Operand const&)
+            |
+            --- v8::internal::Assembler::lea(v8::internal::Register, v8::internal::Operand const&)
+
+0.12^3557486^HTMLParserThrea^chrome               ^[.] WebCore::RenderBlock::updateShapeInsideInfoAfterStyleChange(WebCore::ShapeValue const*, WebCore::ShapeValue const*)
+            |
+            --- WebCore::RenderBlock::updateShapeInsideInfoAfterStyleChange(WebCore::ShapeValue const*, WebCore::ShapeValue const*)
+
+0.12^3556781^HTMLParserThrea^libc-2.15.so         ^[.] __strlen_sse2_pminub
+            |
+            --- __strlen_sse2_pminub
+
+0.12^3556662^HTMLParserThrea^chrome               ^[.] WebCore::ElementRuleCollector::collectMatchingRulesForList(WebCore::RuleData const*, WebCore::MatchRequest const&, WebCore::RuleRange&)
+            |
+            --- WebCore::ElementRuleCollector::collectMatchingRulesForList(WebCore::RuleData const*, WebCore::MatchRequest const&, WebCore::RuleRange&)
+
+0.12^3556305^HTMLParserThrea^chrome               ^[.] v8::internal::Heap::Scavenge()
+            |
+            --- v8::internal::Heap::Scavenge()
+
+0.12^3556183^HTMLParserThrea^chrome               ^[.] WebCore::RenderBlock::linkToEndLineIfNeeded(WebCore::LineLayoutState&)
+            |
+            --- WebCore::RenderBlock::linkToEndLineIfNeeded(WebCore::LineLayoutState&)
+
+0.12^3555975^HTMLParserThrea^chrome               ^[.] WebCore::SharedStyleFinder::locateCousinList(WebCore::Element*, unsigned int&) const
+            |
+            --- WebCore::SharedStyleFinder::locateCousinList(WebCore::Element*, unsigned int&) const
+                0x176da48800000000
+
+0.12^3555563^HTMLParserThrea^chrome               ^[.] v8::internal::FullCodeGenerator::PrepareForBailoutForId(v8::internal::BailoutId, v8::internal::FullCodeGenerator::State)
+            |
+            --- v8::internal::FullCodeGenerator::PrepareForBailoutForId(v8::internal::BailoutId, v8::internal::FullCodeGenerator::State)
+
+0.12^3555363^HTMLParserThrea^chrome               ^[.] v8::internal::PropertyCallbackArguments::Call(v8::Handle<v8::Value> (*)(v8::Local<v8::String>, v8::AccessorInfo const&), v8::Local<v8::String>)
+            |
+            --- v8::internal::PropertyCallbackArguments::Call(v8::Handle<v8::Value> (*)(v8::Local<v8::String>, v8::AccessorInfo const&), v8::Local<v8::String>)
+                0x7f7a0b900000
+
+0.12^3554978^HTMLParserThrea^chrome               ^[.] v8::internal::Object::Lookup(v8::internal::Name*, v8::internal::LookupResult*)
+            |
+            --- v8::internal::Object::Lookup(v8::internal::Name*, v8::internal::LookupResult*)
+
+0.12^3554874^HTMLParserThrea^chrome               ^[.] v8::internal::Heap::AllocateRawOneByteString(int, v8::internal::PretenureFlag)
+            |
+            --- v8::internal::Heap::AllocateRawOneByteString(int, v8::internal::PretenureFlag)
+
+0.12^3554475^HTMLParserThrea^chrome               ^[.] WebCore::RenderBlock::styleWillChange(WebCore::StyleDifference, WebCore::RenderStyle const*)
+            |
+            --- WebCore::RenderBlock::styleWillChange(WebCore::StyleDifference, WebCore::RenderStyle const*)
+
+0.12^3553383^HTMLParserThrea^chrome               ^[.] base::Time::Now()
+            |
+            --- base::Time::Now()
+
+0.12^3553107^HTMLParserThrea^chrome               ^[.] WebCore::ElementRuleCollector::~ElementRuleCollector()
+            |
+            --- WebCore::ElementRuleCollector::~ElementRuleCollector()
+
+0.12^3550744^HTMLParserThrea^chrome               ^[.] net::HttpResponseHeaders::FindHeader(unsigned long, base::BasicStringPiece<std::string> const&) const
+            |
+            --- net::HttpResponseHeaders::FindHeader(unsigned long, base::BasicStringPiece<std::string> const&) const
+
+0.12^3550703^HTMLParserThrea^chrome               ^[.] WebCore::NodeRenderingContext::createRendererForElementIfNeeded()
+            |
+            --- WebCore::NodeRenderingContext::createRendererForElementIfNeeded()
+
+0.12^3550144^HTMLParserThrea^chrome               ^[.] v8::internal::Heap::ScavengeObject(v8::internal::HeapObject**, v8::internal::HeapObject*)
+            |
+            --- v8::internal::Heap::ScavengeObject(v8::internal::HeapObject**, v8::internal::HeapObject*)
+
+0.12^3550090^HTMLParserThrea^chrome               ^[.] WebCore::RenderLayerModelObject::styleDidChange(WebCore::StyleDifference, WebCore::RenderStyle const*)
+            |
+            --- WebCore::RenderLayerModelObject::styleDidChange(WebCore::StyleDifference, WebCore::RenderStyle const*)
+
+0.12^3549513^HTMLParserThrea^chrome               ^[.] std::_Rb_tree<extensions::ChromeV8Context*, extensions::ChromeV8Context*, std::_Identity<extensions::ChromeV8Context*>, std::less<extensions::ChromeV8Context*>, std::allocator<extensions::ChromeV8Context*> >::_M_insert_unique(extensions::ChromeV8Context* const&)
+            |
+            --- std::_Rb_tree<extensions::ChromeV8Context*, extensions::ChromeV8Context*, std::_Identity<extensions::ChromeV8Context*>, std::less<extensions::ChromeV8Context*>, std::allocator<extensions::ChromeV8Context*> >::_M_insert_unique(extensions::ChromeV8Context* const&)
+
+0.12^3549422^HTMLParserThrea^chrome               ^[.] WebCore::RenderBlock::clearFloats()
+            |
+            --- WebCore::RenderBlock::clearFloats()
+
+0.12^3548487^HTMLParserThrea^chrome               ^[.] WebCore::RenderReplaced::computeAspectRatioInformationForRenderBox(WebCore::RenderBox*, WebCore::FloatSize&, double&, bool&) const
+            |
+            --- WebCore::RenderReplaced::computeAspectRatioInformationForRenderBox(WebCore::RenderBox*, WebCore::FloatSize&, double&, bool&) const
+                0x3b2abdce1ed8
+
+0.12^3545724^HTMLParserThrea^chrome               ^[.] WebCore::SVGRenderSupport::styleChanged(WebCore::RenderObject*)
+            |
+            --- WebCore::SVGRenderSupport::styleChanged(WebCore::RenderObject*)
+
+0.12^3545370^HTMLParserThrea^chrome               ^[.] v8::internal::Scope::AllocateNonParameterLocals()
+            |
+            --- v8::internal::Scope::AllocateNonParameterLocals()
+                0x700000007
+
+0.12^3545160^HTMLParserThrea^chrome               ^[.] WebCore::Element::hasFlagsSetDuringStylingOfChildren() const
+            |
+            --- WebCore::Element::hasFlagsSetDuringStylingOfChildren() const
+
+0.12^3543621^HTMLParserThrea^chrome               ^[.] WebCore::RenderBox::visualOverflowRectForPropagation(WebCore::RenderStyle*) const
+            |
+            --- WebCore::RenderBox::visualOverflowRectForPropagation(WebCore::RenderStyle*) const
+
+0.12^3542672^HTMLParserThrea^libm-2.15.so         ^[.] __ceilf_sse41
+            |
+            --- __ceilf_sse41
+
+0.12^3542332^HTMLParserThrea^chrome               ^[.] v8::internal::CallStubCompiler::CompileHandlerFrontend(v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::JSObject>, v8::internal::Handle<v8::internal::Name>, v8::internal::CheckType, v8::internal::Label*)
+            |
+            --- v8::internal::CallStubCompiler::CompileHandlerFrontend(v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::JSObject>, v8::internal::Handle<v8::internal::Name>, v8::internal::CheckType, v8::internal::Label*)
+                0x7f7a11d17f90
+
+0.12^3542050^HTMLParserThrea^chrome               ^[.] v8::internal::MarkCompactCollector::RecordRelocSlot(v8::internal::RelocInfo*, v8::internal::Object*)
+            |
+            --- v8::internal::MarkCompactCollector::RecordRelocSlot(v8::internal::RelocInfo*, v8::internal::Object*)
+
+0.12^3540984^HTMLParserThrea^chrome               ^[.] v8::internal::RegExpMacroAssemblerX64::CheckSpecialCharacterClass(unsigned short, v8::internal::Label*)
+            |
+            --- v8::internal::RegExpMacroAssemblerX64::CheckSpecialCharacterClass(unsigned short, v8::internal::Label*)
+
+0.12^3537796^HTMLParserThrea^chrome               ^[.] SkBitmapProcState::chooseProcs(SkMatrix const&, SkPaint const&)
+            |
+            --- SkBitmapProcState::chooseProcs(SkMatrix const&, SkPaint const&)
+                0x3f80000041400000
+
+0.12^3537246^HTMLParserThrea^chrome               ^[.] v8::internal::Heap::CreateCode(v8::internal::CodeDesc const&, unsigned int, v8::internal::Handle<v8::internal::Object>, bool, bool)
+            |
+            --- v8::internal::Heap::CreateCode(v8::internal::CodeDesc const&, unsigned int, v8::internal::Handle<v8::internal::Object>, bool, bool)
+                0x8f00001000
+
+0.12^3535475^HTMLParserThrea^chrome               ^[.] WebCore::Element::getAttribute(WebCore::QualifiedName const&) const
+            |
+            --- WebCore::Element::getAttribute(WebCore::QualifiedName const&) const
+
+0.12^3534788^HTMLParserThrea^chrome               ^[.] v8::internal::ToBooleanIC_Miss(int, v8::internal::Object**, v8::internal::Isolate*)
+            |
+            --- v8::internal::ToBooleanIC_Miss(int, v8::internal::Object**, v8::internal::Isolate*)
+
+0.12^3534268^HTMLParserThrea^chrome               ^[.] WebCore::FormElementKey::deref() const
+            |
+            --- WebCore::FormElementKey::deref() const
+
+0.12^3533584^HTMLParserThrea^chrome               ^[.] std::basic_string<unsigned short, base::string16_char_traits, std::allocator<unsigned short> >::assign(std::basic_string<unsigned short, base::string16_char_traits, std::allocator<unsigned short> > const&)
+            |
+            --- std::basic_string<unsigned short, base::string16_char_traits, std::allocator<unsigned short> >::assign(std::basic_string<unsigned short, base::string16_char_traits, std::allocator<unsigned short> > const&)
+                0x33d2a411c441
+
+0.12^3533503^HTMLParserThrea^chrome               ^[.] void v8::internal::ScavengingVisitor<(v8::internal::MarksHandling)1, (v8::internal::LoggingAndProfiling)1>::EvacuateObject<(v8::internal::ScavengingVisitor<(v8::internal::MarksHandling)1, (v8::internal::LoggingAndProfiling)1>::ObjectContents)1, (v8::internal::ScavengingVisitor<(v8::internal::MarksHandling)1, (v8::internal::LoggingAndProfiling)1>::SizeRestriction)0, 8>(v8::internal::Map*, v8::internal::HeapObject**, v8::internal::HeapObject*, int)
+            |
+            --- void v8::internal::ScavengingVisitor<(v8::internal::MarksHandling)1, (v8::internal::LoggingAndProfiling)1>::EvacuateObject<(v8::internal::ScavengingVisitor<(v8::internal::MarksHandling)1, (v8::internal::LoggingAndProfiling)1>::ObjectContents)1, (v8::internal::ScavengingVisitor<(v8::internal::MarksHandling)1, (v8::internal::LoggingAndProfiling)1>::SizeRestriction)0, 8>(v8::internal::Map*, v8::internal::HeapObject**, v8::internal::HeapObject*, int)
+
+0.12^3532801^HTMLParserThrea^chrome               ^[.] WebCore::HTMLConstructionSite::indexOfFirstUnopenFormattingElement(unsigned int&) const
+            |
+            --- WebCore::HTMLConstructionSite::indexOfFirstUnopenFormattingElement(unsigned int&) const
+
+0.12^3531175^HTMLParserThrea^chrome               ^[.] v8::internal::StubCache::ComputeLoadNonexistent(v8::internal::Handle<v8::internal::Name>, v8::internal::Handle<v8::internal::JSObject>)
+            |
+            --- v8::internal::StubCache::ComputeLoadNonexistent(v8::internal::Handle<v8::internal::Name>, v8::internal::Handle<v8::internal::JSObject>)
+
+0.12^3529077^HTMLParserThrea^chrome               ^[.] WTF::currentTime()
+            |
+            --- WTF::currentTime()
+
+0.12^3527952^HTMLParserThrea^chrome               ^[.] void WebCore::SelectorDataList::execute<false>(WebCore::Node*, WTF::Vector<WTF::RefPtr<WebCore::Node>, 0ul>&) const
+            |
+            --- void WebCore::SelectorDataList::execute<false>(WebCore::Node*, WTF::Vector<WTF::RefPtr<WebCore::Node>, 0ul>&) const
+
+0.12^3527820^HTMLParserThrea^chrome               ^[.] v8::preparser::PreParser::ParseStatement(bool*)
+            |
+            --- v8::preparser::PreParser::ParseStatement(bool*)
+                0x3b2abd78300000
+
+0.12^3526909^HTMLParserThrea^chrome               ^[.] v8::internal::MacroAssembler::LoadSmiConstant(v8::internal::Register, v8::internal::Smi*)
+            |
+            --- v8::internal::MacroAssembler::LoadSmiConstant(v8::internal::Register, v8::internal::Smi*)
+
+0.12^3526173^HTMLParserThrea^chrome               ^[.] v8::internal::MacroAssembler::CheckMap(v8::internal::Register, v8::internal::Handle<v8::internal::Map>, v8::internal::Label*, v8::internal::SmiCheckType)
+            |
+            --- v8::internal::MacroAssembler::CheckMap(v8::internal::Register, v8::internal::Handle<v8::internal::Map>, v8::internal::Label*, v8::internal::SmiCheckType)
+
+0.12^3525548^HTMLParserThrea^chrome               ^[.] v8::internal::String::SlowTryFlatten(v8::internal::PretenureFlag)
+            |
+            --- v8::internal::String::SlowTryFlatten(v8::internal::PretenureFlag)
+
+0.12^3525251^HTMLParserThrea^chrome               ^[.] WebCore::RuleSet::addChildRules(WTF::Vector<WTF::RefPtr<WebCore::StyleRuleBase>, 0ul> const&, WebCore::MediaQueryEvaluator const&, WebCore::StyleResolver*, WebCore::ContainerNode const*, bool, WebCore::AddRuleFlags)
+            |
+            --- WebCore::RuleSet::addChildRules(WTF::Vector<WTF::RefPtr<WebCore::StyleRuleBase>, 0ul> const&, WebCore::MediaQueryEvaluator const&, WebCore::StyleResolver*, WebCore::ContainerNode const*, bool, WebCore::AddRuleFlags)
+
+0.12^3524914^HTMLParserThrea^chrome               ^[.] v8::internal::PositionsRecorder::WriteRecordedPositions()
+            |
+            --- v8::internal::PositionsRecorder::WriteRecordedPositions()
+
+0.12^3524273^HTMLParserThrea^chrome               ^[.] WebCore::FrameLoader::checkLoadComplete()
+            |
+            --- WebCore::FrameLoader::checkLoadComplete()
+
+0.12^3523358^HTMLParserThrea^chrome               ^[.] v8::internal::ScavengingVisitor<(v8::internal::MarksHandling)1, (v8::internal::LoggingAndProfiling)1>::EvacuateFixedArray(v8::internal::Map*, v8::internal::HeapObject**, v8::internal::HeapObject*)
+            |
+            --- v8::internal::ScavengingVisitor<(v8::internal::MarksHandling)1, (v8::internal::LoggingAndProfiling)1>::EvacuateFixedArray(v8::internal::Map*, v8::internal::HeapObject**, v8::internal::HeapObject*)
+
+0.12^3522817^HTMLParserThrea^chrome               ^[.] WebCore::HTMLFrameElementBase::attach(WebCore::Node::AttachContext const&)
+            |
+            --- WebCore::HTMLFrameElementBase::attach(WebCore::Node::AttachContext const&)
+
+0.12^3522399^HTMLParserThrea^chrome               ^[.] v8::internal::Runtime::SetObjectProperty(v8::internal::Isolate*, v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::Object>, PropertyAttributes, v8::internal::StrictModeFlag)
+            |
+            --- v8::internal::Runtime::SetObjectProperty(v8::internal::Isolate*, v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::Object>, PropertyAttributes, v8::internal::StrictModeFlag)
+                0x2281c143442a
+                0x2281c143180c
+                0x2281c14310e9
+                0x2281c140e854
+                0x2281c14317b0
+                0x2281c14310e9
+                0x2281c140e854
+                0x2281c14317b0
+                0x2281c14310e9
+                0x2281c140e854
+                0x2281c142b65e
+                0x2281c1417d97
+                v8::internal::Invoke(bool, v8::internal::Handle<v8::internal::JSFunction>, v8::internal::Handle<v8::internal::Object>, int, v8::internal::Handle<v8::internal::Object>*, bool*)
+
+0.12^3521835^HTMLParserThrea^chrome               ^[.] v8::Object::SetAlignedPointerInInternalField(int, void*)
+            |
+            --- v8::Object::SetAlignedPointerInInternalField(int, void*)
+
+0.12^3519670^HTMLParserThrea^chrome               ^[.] v8::internal::HDeadCodeEliminationPhase::RemoveDeadInstructions()
+            |
+            --- v8::internal::HDeadCodeEliminationPhase::RemoveDeadInstructions()
+                0x7fff30e8edb0
+
+0.12^3518719^HTMLParserThrea^chrome               ^[.] v8::internal::LCodeGen::Comment(char const*, ...)
+            |
+            --- v8::internal::LCodeGen::Comment(char const*, ...)
+
+0.12^3518131^HTMLParserThrea^chrome               ^[.] v8::internal::JSObject::HasRealNamedProperty(v8::internal::Isolate*, v8::internal::Name*)
+            |
+            --- v8::internal::JSObject::HasRealNamedProperty(v8::internal::Isolate*, v8::internal::Name*)
+
+0.12^3516705^HTMLParserThrea^chrome               ^[.] v8::internal::Runtime_GetTemplateField(int, v8::internal::Object**, v8::internal::Isolate*)
+            |
+            --- v8::internal::Runtime_GetTemplateField(int, v8::internal::Object**, v8::internal::Isolate*)
+                0x2281c1431084
+                0x2281c140e854
+                0x2281c142b65e
+                0x2281c1417d97
+                v8::internal::Invoke(bool, v8::internal::Handle<v8::internal::JSFunction>, v8::internal::Handle<v8::internal::Object>, int, v8::internal::Handle<v8::internal::Object>*, bool*)
+
+0.12^3515242^HTMLParserThrea^chrome               ^[.] void WebCore::StyleResolver::applyProperties<(WebCore::StyleResolver::StyleApplicationPass)0>(WebCore::StyleResolverState&, WebCore::StylePropertySet const*, WebCore::StyleRule*, bool, bool, WebCore::PropertyWhitelistType)
+            |
+            --- void WebCore::StyleResolver::applyProperties<(WebCore::StyleResolver::StyleApplicationPass)0>(WebCore::StyleResolverState&, WebCore::StylePropertySet const*, WebCore::StyleRule*, bool, bool, WebCore::PropertyWhitelistType)
+
+0.12^3514809^HTMLParserThrea^chrome               ^[.] url_util::LowerCaseEqualsASCII(char const*, char const*, char const*)
+            |
+            --- url_util::LowerCaseEqualsASCII(char const*, char const*, char const*)
+
+0.12^3514002^HTMLParserThrea^chrome               ^[.] v8::internal::ScavengeVisitor::VisitPointer(v8::internal::Object**)
+            |
+            --- v8::internal::ScavengeVisitor::VisitPointer(v8::internal::Object**)
+
+0.12^3513688^HTMLParserThrea^chrome               ^[.] _ZN2v88internal15DescriptorArray6AppendEPNS0_10DescriptorERKNS1_16WhitenessWitnessE.constprop.537
+            |
+            --- _ZN2v88internal15DescriptorArray6AppendEPNS0_10DescriptorERKNS1_16WhitenessWitnessE.constprop.537
+
+0.12^3512883^HTMLParserThrea^chrome               ^[.] v8::internal::LoadIC_Miss(int, v8::internal::Object**, v8::internal::Isolate*)
+            |
+            --- v8::internal::LoadIC_Miss(int, v8::internal::Object**, v8::internal::Isolate*)
+                0x7fff30e92040
+
+0.12^3511238^HTMLParserThrea^chrome               ^[.] WebCore::V8ScriptRunner::precompileScript(v8::Handle<v8::String>, WebCore::CachedScript*)
+            |
+            --- WebCore::V8ScriptRunner::precompileScript(v8::Handle<v8::String>, WebCore::CachedScript*)
+
+0.12^3506824^HTMLParserThrea^chrome               ^[.] v8::internal::Factory::NewAllocationSite()
+            |
+            --- v8::internal::Factory::NewAllocationSite()
+
+0.12^3504680^HTMLParserThrea^chrome               ^[.] WebCore::InputTypeNames::text()
+            |
+            --- WebCore::InputTypeNames::text()
+
+0.12^3503829^HTMLParserThrea^chrome               ^[.] v8::internal::FastElementsAccessor<v8::internal::FastPackedObjectElementsAccessor, v8::internal::ElementsKindTraits<(v8::internal::ElementsKind)2>, 8>::SetLengthWithoutNormalize(v8::internal::FixedArrayBase*, v8::internal::JSArray*, v8::internal::Object*, unsigned int)
+            |
+            --- v8::internal::FastElementsAccessor<v8::internal::FastPackedObjectElementsAccessor, v8::internal::ElementsKindTraits<(v8::internal::ElementsKind)2>, 8>::SetLengthWithoutNormalize(v8::internal::FixedArrayBase*, v8::internal::JSArray*, v8::internal::Object*, unsigned int)
+
+0.12^3500368^HTMLParserThrea^chrome               ^[.] WTF::Vector<WTF::String, 0ul>::expandCapacity(unsigned long)
+            |
+            --- WTF::Vector<WTF::String, 0ul>::expandCapacity(unsigned long)
+                0x1000
+
+0.12^3497657^HTMLParserThrea^chrome               ^[.] WebCore::RenderObject::adjustStyleDifference(WebCore::StyleDifference, unsigned int) const
+            |
+            --- WebCore::RenderObject::adjustStyleDifference(WebCore::StyleDifference, unsigned int) const
+
+0.12^3494287^HTMLParserThrea^chrome               ^[.] v8::internal::IC::Clear(unsigned char*)
+            |
+            --- v8::internal::IC::Clear(unsigned char*)
+
+0.12^3492300^HTMLParserThrea^chrome               ^[.] ppapi::proxy::InterfaceProxy::Send(IPC::Message*)
+            |
+            --- ppapi::proxy::InterfaceProxy::Send(IPC::Message*)
+
+0.12^3492214^HTMLParserThrea^chrome               ^[.] v8::internal::Assembler::Assembler(v8::internal::Isolate*, void*, int)
+            |
+            --- v8::internal::Assembler::Assembler(v8::internal::Isolate*, void*, int)
+                0xc6a346f4711
+
+0.12^3489812^HTMLParserThrea^chrome               ^[.] WebCore::SelectorCheckerFastPath::matchesRightmostSelector(WebCore::SelectorChecker::VisitedMatchType) const
+            |
+            --- WebCore::SelectorCheckerFastPath::matchesRightmostSelector(WebCore::SelectorChecker::VisitedMatchType) const
+
+0.12^3488486^HTMLParserThrea^chrome               ^[.] tc_free
+            |
+            --- tc_free
+                0x100000000
+                0x29f988404121
+
+0.12^3488229^HTMLParserThrea^chrome               ^[.] WebCore::RenderObject::updateFillImages(WebCore::FillLayer const*, WebCore::FillLayer const*)
+            |
+            --- WebCore::RenderObject::updateFillImages(WebCore::FillLayer const*, WebCore::FillLayer const*)
+
+0.12^3486534^HTMLParserThrea^chrome               ^[.] v8::internal::Parser::ParsePostfixExpression(bool*)
+            |
+            --- v8::internal::Parser::ParsePostfixExpression(bool*)
+                0x7fff30e905d002
+
+0.12^3484804^HTMLParserThrea^chrome               ^[.] v8::internal::SharedStoreIC_ExtendStorage(int, v8::internal::Object**, v8::internal::Isolate*)
+            |
+            --- v8::internal::SharedStoreIC_ExtendStorage(int, v8::internal::Object**, v8::internal::Isolate*)
+
+0.12^3484146^HTMLParserThrea^chrome               ^[.] WebCore::RenderCounter::rendererSubtreeAttached(WebCore::RenderObject*)
+            |
+            --- WebCore::RenderCounter::rendererSubtreeAttached(WebCore::RenderObject*)
+
+0.12^3483547^HTMLParserThrea^chrome               ^[.] WebCore::LiveNodeListBase::itemBeforeOrAfterCachedItem(unsigned int, WebCore::ContainerNode*) const
+            |
+            --- WebCore::LiveNodeListBase::itemBeforeOrAfterCachedItem(unsigned int, WebCore::ContainerNode*) const
+
+0.12^3482072^HTMLParserThrea^chrome               ^[.] v8::internal::CPU::FlushICache(void*, unsigned long)
+            |
+            --- v8::internal::CPU::FlushICache(void*, unsigned long)
+
+0.12^3481935^HTMLParserThrea^chrome               ^[.] WebCore::StyleResolver::matchAuthorRules(WebCore::Element*, WebCore::ElementRuleCollector&, bool)
+            |
+            --- WebCore::StyleResolver::matchAuthorRules(WebCore::Element*, WebCore::ElementRuleCollector&, bool)
+                0x1e83176abb08
+
+0.12^3481470^HTMLParserThrea^chrome               ^[.] v8::internal::StackFrame::GetCallerState(v8::internal::StackFrame::State*) const
+            |
+            --- v8::internal::StackFrame::GetCallerState(v8::internal::StackFrame::State*) const
+                0x7fff30e8f698
+                0x9d2ae7aa1e1
+
+0.12^3479937^HTMLParserThrea^chrome               ^[.] WTF::AtomicString::add(unsigned char const*)
+            |
+            --- WTF::AtomicString::add(unsigned char const*)
+
+0.12^3476115^HTMLParserThrea^chrome               ^[.] WebCore::StyleAdjuster::adjustRenderStyle(WebCore::RenderStyle*, WebCore::RenderStyle*, WebCore::Element*)
+            |
+            --- WebCore::StyleAdjuster::adjustRenderStyle(WebCore::RenderStyle*, WebCore::RenderStyle*, WebCore::Element*)
+
+0.12^3474363^HTMLParserThrea^chrome               ^[.] WebCore::CSSValue::deref()
+            |
+            --- WebCore::CSSValue::deref()
+
+0.12^3473710^HTMLParserThrea^chrome               ^[.] v8::internal::Assembler::movq(v8::internal::Register, long, v8::internal::RelocInfo::Mode)
+            |
+            --- v8::internal::Assembler::movq(v8::internal::Register, long, v8::internal::RelocInfo::Mode)
+
+0.12^3471996^HTMLParserThrea^chrome               ^[.] v8::internal::RegExpEngine::Compile(v8::internal::RegExpCompileData*, bool, bool, bool, v8::internal::Handle<v8::internal::String>, v8::internal::Handle<v8::internal::String>, bool, v8::internal::Zone*)
+            |
+            --- v8::internal::RegExpEngine::Compile(v8::internal::RegExpCompileData*, bool, bool, bool, v8::internal::Handle<v8::internal::String>, v8::internal::Handle<v8::internal::String>, bool, v8::internal::Zone*)
+                0xc6a3439fca1
+
+0.12^3468177^HTMLParserThrea^chrome               ^[.] v8::internal::Context::global_proxy()
+            |
+            --- v8::internal::Context::global_proxy()
+
+0.12^3467384^HTMLParserThrea^chrome               ^[.] WTF::Vector<std::pair<void (*)(WebCore::Node*), WTF::RefPtr<WebCore::Node> >, 0ul>::shrinkCapacity(unsigned long)
+            |
+            --- WTF::Vector<std::pair<void (*)(WebCore::Node*), WTF::RefPtr<WebCore::Node> >, 0ul>::shrinkCapacity(unsigned long)
+
+0.12^3466196^HTMLParserThrea^chrome               ^[.] v8::internal::Parser::GetSymbol()
+            |
+            --- v8::internal::Parser::GetSymbol()
+                0x7fff30e9182000
+
+0.12^3462208^HTMLParserThrea^chrome               ^[.] v8::internal::JSReceiver::SetProperty(v8::internal::Name*, v8::internal::Object*, PropertyAttributes, v8::internal::StrictModeFlag, v8::internal::JSReceiver::StoreFromKeyed)
+            |
+            --- v8::internal::JSReceiver::SetProperty(v8::internal::Name*, v8::internal::Object*, PropertyAttributes, v8::internal::StrictModeFlag, v8::internal::JSReceiver::StoreFromKeyed)
+                0x7fff30e91950
+
+0.12^3461655^HTMLParserThrea^chrome               ^[.] WebCore::RenderLayer::shouldBeNormalFlowOnlyIgnoringCompositedScrolling() const
+            |
+            --- WebCore::RenderLayer::shouldBeNormalFlowOnlyIgnoringCompositedScrolling() const
+
+0.11^3453572^HTMLParserThrea^chrome               ^[.] v8::internal::HBasicBlock::HBasicBlock(v8::internal::HGraph*)
+            |
+            --- v8::internal::HBasicBlock::HBasicBlock(v8::internal::HGraph*)
+
+0.11^3450775^HTMLParserThrea^chrome               ^[.] WebCore::RenderObject::invalidateContainerPreferredLogicalWidths()
+            |
+            --- WebCore::RenderObject::invalidateContainerPreferredLogicalWidths()
+
+0.11^3446446^HTMLParserThrea^chrome               ^[.] WebCore::LayoutUnit::round() const
+            |
+            --- WebCore::LayoutUnit::round() const
+
+0.11^3445539^HTMLParserThrea^chrome               ^[.] v8::internal::Runtime_RegExpExec(int, v8::internal::Object**, v8::internal::Isolate*)
+            |
+            --- v8::internal::Runtime_RegExpExec(int, v8::internal::Object**, v8::internal::Isolate*)
+                0xc6a3540bd09
+
+0.11^3445347^HTMLParserThrea^chrome               ^[.] v8::internal::BaseLoadStubCompiler::GenerateLoadCallback(v8::internal::Register, v8::internal::Handle<v8::internal::ExecutableAccessorInfo>)
+            |
+            --- v8::internal::BaseLoadStubCompiler::GenerateLoadCallback(v8::internal::Register, v8::internal::Handle<v8::internal::ExecutableAccessorInfo>)
+
+0.11^3444801^HTMLParserThrea^chrome               ^[.] v8::internal::BufferedUtf16CharacterStream::ReadBlock()
+            |
+            --- v8::internal::BufferedUtf16CharacterStream::ReadBlock()
+                0x7fff30e91798
+
+0.11^3436515^HTMLParserThrea^chrome               ^[.] WTF::Vector<unsigned short, 256ul>::shrinkCapacity(unsigned long)
+            |
+            --- WTF::Vector<unsigned short, 256ul>::shrinkCapacity(unsigned long)
+
+0.11^3435352^HTMLParserThrea^chrome               ^[.] v8::internal::Parser::ParseFunctionLiteral(v8::internal::Handle<v8::internal::String>, bool, bool, int, v8::internal::FunctionLiteral::FunctionType, bool*)
+            |
+            --- v8::internal::Parser::ParseFunctionLiteral(v8::internal::Handle<v8::internal::String>, bool, bool, int, v8::internal::FunctionLiteral::FunctionType, bool*)
+                0x7fff30e903a000
+
+0.11^3434720^HTMLParserThrea^chrome               ^[.] WebCore::ImageSource::setData(WebCore::SharedBuffer*, bool)
+            |
+            --- WebCore::ImageSource::setData(WebCore::SharedBuffer*, bool)
+
+0.11^3432682^HTMLParserThrea^chrome               ^[.] v8::internal::StandardFrame::ComputeCallerState(v8::internal::StackFrame::State*) const
+            |
+            --- v8::internal::StandardFrame::ComputeCallerState(v8::internal::StackFrame::State*) const
+                0x7fff30e90df8
+                0xc6a346b4fe9
+
+0.11^3432008^HTMLParserThrea^chrome               ^[.] v8::internal::VariableProxy::Accept(v8::internal::AstVisitor*)
+            |
+            --- v8::internal::VariableProxy::Accept(v8::internal::AstVisitor*)
+
+0.11^3420656^HTMLParserThrea^chrome               ^[.] SkBlitter::Choose(SkBitmap const&, SkMatrix const&, SkPaint const&, void*, unsigned long)
+            |
+            --- SkBlitter::Choose(SkBitmap const&, SkMatrix const&, SkPaint const&, void*, unsigned long)
+
+0.11^3420637^HTMLParserThrea^chrome               ^[.] v8::internal::Map::UpdateCodeCache(v8::internal::Name*, v8::internal::Code*)
+            |
+            --- v8::internal::Map::UpdateCodeCache(v8::internal::Name*, v8::internal::Code*)
+
+0.11^3419913^HTMLParserThrea^chrome               ^[.] WebCore::Element::childrenChanged(bool, WebCore::Node*, WebCore::Node*, int)
+            |
+            --- WebCore::Element::childrenChanged(bool, WebCore::Node*, WebCore::Node*, int)
+
+0.11^3413310^HTMLParserThrea^chrome               ^[.] v8::internal::Scope::ResolveVariablesRecursively(v8::internal::CompilationInfo*, v8::internal::AstNodeFactory<v8::internal::AstNullVisitor>*)
+            |
+            --- v8::internal::Scope::ResolveVariablesRecursively(v8::internal::CompilationInfo*, v8::internal::AstNodeFactory<v8::internal::AstNullVisitor>*)
+
+0.11^3413062^HTMLParserThrea^chrome               ^[.] void WTF::Vector<WebCore::MatchedProperties, 0ul>::append<WebCore::MatchedProperties>(WebCore::MatchedProperties const*, unsigned long)
+            |
+            --- void WTF::Vector<WebCore::MatchedProperties, 0ul>::append<WebCore::MatchedProperties>(WebCore::MatchedProperties const*, unsigned long)
+
+0.11^3409803^HTMLParserThrea^chrome               ^[.] v8::internal::Assembler::movq(v8::internal::Register, v8::internal::Operand const&)
+            |
+            --- v8::internal::Assembler::movq(v8::internal::Register, v8::internal::Operand const&)
+
+0.11^3406785^HTMLParserThrea^chrome               ^[.] v8::internal::HashTable<v8::internal::MapCacheShape, v8::internal::HashTableKey*>::FindEntry(v8::internal::Isolate*, v8::internal::HashTableKey*)
+            |
+            --- v8::internal::HashTable<v8::internal::MapCacheShape, v8::internal::HashTableKey*>::FindEntry(v8::internal::Isolate*, v8::internal::HashTableKey*)
+
+0.11^3404799^HTMLParserThrea^chrome               ^[.] WebCore::RenderLineBoxList::removeLineBox(WebCore::InlineFlowBox*)
+            |
+            --- WebCore::RenderLineBoxList::removeLineBox(WebCore::InlineFlowBox*)
+
+0.11^3398149^HTMLParserThrea^chrome               ^[.] WebCore::RenderLayer::clippingRootForPainting() const
+            |
+            --- WebCore::RenderLayer::clippingRootForPainting() const
+                0x2bc00000a000
+
+0.11^3390095^HTMLParserThrea^chrome               ^[.] v8::internal::FullCodeGenerator::VarOperand(v8::internal::Variable*, v8::internal::Register)
+            |
+            --- v8::internal::FullCodeGenerator::VarOperand(v8::internal::Variable*, v8::internal::Register)
+                0x7fff30e91980
+
+0.11^3388424^HTMLParserThrea^chrome               ^[.] WebCore::DOMTimer::fired()
+            |
+            --- WebCore::DOMTimer::fired()
+
+0.11^3378612^HTMLParserThrea^chrome               ^[.] v8::internal::JSObject::SetFastElementsCapacityAndLength(int, int, v8::internal::JSObject::SetFastElementsCapacitySmiMode)
+            |
+            --- v8::internal::JSObject::SetFastElementsCapacityAndLength(int, int, v8::internal::JSObject::SetFastElementsCapacitySmiMode)
+
+0.11^3374619^HTMLParserThrea^chrome               ^[.] v8::internal::Code::MakeCodeAgeSequenceYoung(unsigned char*)
+            |
+            --- v8::internal::Code::MakeCodeAgeSequenceYoung(unsigned char*)
+                0x2281c140e854
+                0x2281c1e636e1
+                0x2281c1e52c8f
+                0x2281c142b664
+                0x2281c1417d97
+                v8::internal::Invoke(bool, v8::internal::Handle<v8::internal::JSFunction>, v8::internal::Handle<v8::internal::Object>, int, v8::internal::Handle<v8::internal::Object>*, bool*)
+
+0.11^3373628^HTMLParserThrea^chrome               ^[.] ucase_fold_46
+            |
+            --- ucase_fold_46
+
+0.11^3372159^HTMLParserThrea^chrome               ^[.] WebCore::LayoutState::LayoutState(WebCore::LayoutState*, WebCore::RenderBox*, WebCore::LayoutSize const&, WebCore::LayoutUnit, bool, WebCore::ColumnInfo*)
+            |
+            --- WebCore::LayoutState::LayoutState(WebCore::LayoutState*, WebCore::RenderBox*, WebCore::LayoutSize const&, WebCore::LayoutUnit, bool, WebCore::ColumnInfo*)
+
+0.11^3365891^HTMLParserThrea^chrome               ^[.] v8::internal::HOptimizedGraphBuilder::BuildGraph()
+            |
+            --- v8::internal::HOptimizedGraphBuilder::BuildGraph()
+
+0.11^3359190^HTMLParserThrea^chrome               ^[.] v8::internal::Scope::Initialize()
+            |
+            --- v8::internal::Scope::Initialize()
+
+0.11^3356725^HTMLParserThrea^chrome               ^[.] WebCore::RenderLayer::updateScrollbarsAfterLayout()
+            |
+            --- WebCore::RenderLayer::updateScrollbarsAfterLayout()
+
+0.11^3356459^HTMLParserThrea^chrome               ^[.] _ZN7WebCoreplEiRKNS_10LayoutUnitE.constprop.860
+            |
+            --- _ZN7WebCoreplEiRKNS_10LayoutUnitE.constprop.860
+
+0.11^3355399^HTMLParserThrea^chrome               ^[.] v8::internal::ExitFrame::GetCallerStackPointer() const
+            |
+            --- v8::internal::ExitFrame::GetCallerStackPointer() const
+                (nil)
+
+0.11^3351779^HTMLParserThrea^chrome               ^[.] WebCore::RenderBlock::layoutRunsAndFloatsInRange(WebCore::LineLayoutState&, WebCore::BidiResolver<WebCore::InlineIterator, WebCore::BidiRun>&, WebCore::InlineIterator const&, WebCore::BidiStatus const&, unsigned int)
+            |
+            --- WebCore::RenderBlock::layoutRunsAndFloatsInRange(WebCore::LineLayoutState&, WebCore::BidiResolver<WebCore::InlineIterator, WebCore::BidiRun>&, WebCore::InlineIterator const&, WebCore::BidiStatus const&, unsigned int)
+                0x3b2abd866648
+
+0.11^3344999^HTMLParserThrea^chrome               ^[.] v8::internal::CompilationCacheRegExp::Lookup(v8::internal::Handle<v8::internal::String>, v8::internal::JSRegExp::Flags)
+            |
+            --- v8::internal::CompilationCacheRegExp::Lookup(v8::internal::Handle<v8::internal::String>, v8::internal::JSRegExp::Flags)
+
+0.11^3344476^HTMLParserThrea^chrome               ^[.] tracked_objects::DeathData::RecordDeath(int, int, int)
+            |
+            --- tracked_objects::DeathData::RecordDeath(int, int, int)
+
+0.11^3343357^HTMLParserThrea^chrome               ^[.] void WTF::Vector<WebCore::SelectorFilter::ParentStackFrame, 0ul>::appendSlowCase<WebCore::SelectorFilter::ParentStackFrame>(WebCore::SelectorFilter::ParentStackFrame const&)
+            |
+            --- void WTF::Vector<WebCore::SelectorFilter::ParentStackFrame, 0ul>::appendSlowCase<WebCore::SelectorFilter::ParentStackFrame>(WebCore::SelectorFilter::ParentStackFrame const&)
+
+0.11^3341336^HTMLParserThrea^chrome               ^[.] v8::internal::FullCodeGenerator::VisitObjectLiteral(v8::internal::ObjectLiteral*)
+            |
+            --- v8::internal::FullCodeGenerator::VisitObjectLiteral(v8::internal::ObjectLiteral*)
+
+0.11^3340676^HTMLParserThrea^chrome               ^[.] v8::internal::Assignment::Accept(v8::internal::AstVisitor*)
+            |
+            --- v8::internal::Assignment::Accept(v8::internal::AstVisitor*)
+
+0.11^3336841^HTMLParserThrea^chrome               ^[.] v8::internal::MacroAssembler::Set(v8::internal::Register, long)
+            |
+            --- v8::internal::MacroAssembler::Set(v8::internal::Register, long)
+                0x3b2abd624000
+
+0.11^3335871^HTMLParserThrea^chrome               ^[.] v8::internal::Scanner::ScanRegExpPattern(bool)
+            |
+            --- v8::internal::Scanner::ScanRegExpPattern(bool)
+                0x10e8a1901
+
+0.11^3313926^HTMLParserThrea^chrome               ^[.] v8::internal::ToBooleanStub::Types::UpdateStatus(v8::internal::Handle<v8::internal::Object>)
+            |
+            --- v8::internal::ToBooleanStub::Types::UpdateStatus(v8::internal::Handle<v8::internal::Object>)
+                0x2281c14140c1
+
+0.11^3300775^HTMLParserThrea^chrome               ^[.] WebCore::SVGRenderSupport::setRendererHasSVGShadow(WebCore::RenderObject*, bool)
+            |
+            --- WebCore::SVGRenderSupport::setRendererHasSVGShadow(WebCore::RenderObject*, bool)
+
+0.11^3292871^HTMLParserThrea^chrome               ^[.] base::debug::TraceLog::GetInstance()
+            |
+            --- base::debug::TraceLog::GetInstance()
+
+0.11^3289620^HTMLParserThrea^chrome               ^[.] WebCore::FrameView::pagination() const
+            |
+            --- WebCore::FrameView::pagination() const
+                (nil)
+                0x3b2abdc16c30
+
+0.11^3274771^HTMLParserThrea^chrome               ^[.] v8::internal::Factory::NewMap(v8::internal::InstanceType, int, v8::internal::ElementsKind)
+            |
+            --- v8::internal::Factory::NewMap(v8::internal::InstanceType, int, v8::internal::ElementsKind)
+
+0.11^3268228^HTMLParserThrea^chrome               ^[.] WebCore::RenderBlock::updateBlockChildDirtyBitsBeforeLayout(bool, WebCore::RenderBox*)
+            |
+            --- WebCore::RenderBlock::updateBlockChildDirtyBitsBeforeLayout(bool, WebCore::RenderBox*)
+
+0.11^3264333^HTMLParserThrea^chrome               ^[.] v8::internal::CreateObjectLiteralBoilerplate(v8::internal::Isolate*, v8::internal::Handle<v8::internal::FixedArray>, v8::internal::Handle<v8::internal::FixedArray>, bool, bool)
+            |
+            --- v8::internal::CreateObjectLiteralBoilerplate(v8::internal::Isolate*, v8::internal::Handle<v8::internal::FixedArray>, v8::internal::Handle<v8::internal::FixedArray>, bool, bool)
+
+0.11^3263293^HTMLParserThrea^chrome               ^[.] content::RenderViewObserver::DidClearWindowObject(WebKit::WebFrame*)
+            |
+            --- content::RenderViewObserver::DidClearWindowObject(WebKit::WebFrame*)
+
+0.11^3263262^HTMLParserThrea^chrome               ^[.] WebCore::V8HiddenPropertyName::sleepFunction()
+            |
+            --- WebCore::V8HiddenPropertyName::sleepFunction()
+                0x5bf5d67bd1
+
+0.11^3253742^HTMLParserThrea^chrome               ^[.] WebCore::RenderLayer::boundingBox(WebCore::RenderLayer const*, unsigned int, WebCore::LayoutPoint const*) const
+            |
+            --- WebCore::RenderLayer::boundingBox(WebCore::RenderLayer const*, unsigned int, WebCore::LayoutPoint const*) const
+                (nil)
+
+0.11^3250656^HTMLParserThrea^chrome               ^[.] v8::internal::Runtime_StringCharCodeAt(int, v8::internal::Object**, v8::internal::Isolate*)
+            |
+            --- v8::internal::Runtime_StringCharCodeAt(int, v8::internal::Object**, v8::internal::Isolate*)
+                0x2281c1449ea3
+                0x2281c14497a7
+                0x2281c1425b83
+                0x2281c141b848
+                0x2281c1443ab6
+                0x2281c14480ae
+                0x2281c144938f
+                0x2281c142b664
+                0x2281c1417d97
+                v8::internal::Invoke(bool, v8::internal::Handle<v8::internal::JSFunction>, v8::internal::Handle<v8::internal::Object>, int, v8::internal::Handle<v8::internal::Object>*, bool*)
+
+0.11^3240988^HTMLParserThrea^chrome               ^[.] WTF::equalIgnoringCaseNonNull(WTF::StringImpl const*, WTF::StringImpl const*)
+            |
+            --- WTF::equalIgnoringCaseNonNull(WTF::StringImpl const*, WTF::StringImpl const*)
+
+0.11^3237736^HTMLParserThrea^chrome               ^[.] v8::internal::Assembler::call(v8::internal::Handle<v8::internal::Code>, v8::internal::RelocInfo::Mode, v8::internal::TypeFeedbackId)
+            |
+            --- v8::internal::Assembler::call(v8::internal::Handle<v8::internal::Code>, v8::internal::RelocInfo::Mode, v8::internal::TypeFeedbackId)
+
+0.11^3228754^HTMLParserThrea^libpthread-2.15.so   ^[.] pthread_mutex_unlock
+            |
+            --- pthread_mutex_unlock
+
+0.11^3225835^HTMLParserThrea^chrome               ^[.] WebCore::RenderObject::repaintUsingContainer(WebCore::RenderLayerModelObject const*, WebCore::IntRect const&) const
+            |
+            --- WebCore::RenderObject::repaintUsingContainer(WebCore::RenderLayerModelObject const*, WebCore::IntRect const&) const
+
+0.11^3219559^HTMLParserThrea^chrome               ^[.] icu_46::RuleBasedBreakIterator::handleNext(icu_46::RBBIStateTable const*)
+            |
+            --- icu_46::RuleBasedBreakIterator::handleNext(icu_46::RBBIStateTable const*)
+
+0.11^3215902^HTMLParserThrea^chrome               ^[.] v8::internal::Scanner::Scanner(v8::internal::UnicodeCache*)
+            |
+            --- v8::internal::Scanner::Scanner(v8::internal::UnicodeCache*)
+                0x7f7a0b900000
+
+0.11^3202188^HTMLParserThrea^chrome               ^[.] v8::Object::FindInstanceInPrototypeChain(v8::Handle<v8::FunctionTemplate>)
+            |
+            --- v8::Object::FindInstanceInPrototypeChain(v8::Handle<v8::FunctionTemplate>)
+
+0.11^3202083^HTMLParserThrea^chrome               ^[.] WebCore::Element::attach(WebCore::Node::AttachContext const&)
+            |
+            --- WebCore::Element::attach(WebCore::Node::AttachContext const&)
+
+0.11^3200673^HTMLParserThrea^chrome               ^[.] v8::internal::CompareIC::NewInputState(v8::internal::CompareIC::State, v8::internal::Handle<v8::internal::Object>)
+            |
+            --- v8::internal::CompareIC::NewInputState(v8::internal::CompareIC::State, v8::internal::Handle<v8::internal::Object>)
+
+0.11^3176018^HTMLParserThrea^chrome               ^[.] WebCore::Element::recalcStyle(WebCore::Node::StyleChange)
+            |
+            --- WebCore::Element::recalcStyle(WebCore::Node::StyleChange)
+
+0.11^3170916^HTMLParserThrea^chrome               ^[.] WebCore::MajorGCWrapperVisitor::VisitPersistentHandle(v8::Persistent<v8::Value>*, unsigned short)
+            |
+            --- WebCore::MajorGCWrapperVisitor::VisitPersistentHandle(v8::Persistent<v8::Value>*, unsigned short)
+                0x80e894c5308
+
+0.10^3137671^HTMLParserThrea^chrome               ^[.] v8::internal::DependentCode::DeoptimizeDependentCodeGroup(v8::internal::Isolate*, v8::internal::DependentCode::DependencyGroup)
+            |
+            --- v8::internal::DependentCode::DeoptimizeDependentCodeGroup(v8::internal::Isolate*, v8::internal::DependentCode::DependencyGroup)
+
+0.10^3136480^HTMLParserThrea^chrome               ^[.] WebCore::DOMWindow::document() const
+            |
+            --- WebCore::DOMWindow::document() const
+
+0.10^3136403^HTMLParserThrea^chrome               ^[.] _ZNK3WTF7HashMapIPN7WebCore12RenderObjectENS_6RefPtrINS1_18CompositeAnimationEEENS_7PtrHashIS3_EENS_10HashTraitsIS3_EENS9_IS6_EEE3getERKS3_.isra.185
+            |
+            --- _ZNK3WTF7HashMapIPN7WebCore12RenderObjectENS_6RefPtrINS1_18CompositeAnimationEEENS_7PtrHashIS3_EENS_10HashTraitsIS3_EENS9_IS6_EEE3getERKS3_.isra.185
+
+0.10^3126165^HTMLParserThrea^chrome               ^[.] v8::internal::VariableProxy::BindTo(v8::internal::Variable*)
+            |
+            --- v8::internal::VariableProxy::BindTo(v8::internal::Variable*)
+                0x3b2abdf3f010
+
+0.10^3124159^HTMLParserThrea^chrome               ^[.] WebCore::InlineTextBox::logicalOverflowRect() const
+            |
+            --- WebCore::InlineTextBox::logicalOverflowRect() const
+                0xaf00000000
+                0x3b2abdda8680
+
+0.10^3121836^HTMLParserThrea^chrome               ^[.] v8::internal::JSObject::SetPropertyViaPrototypes(v8::internal::Name*, v8::internal::Object*, PropertyAttributes, v8::internal::StrictModeFlag, bool*)
+            |
+            --- v8::internal::JSObject::SetPropertyViaPrototypes(v8::internal::Name*, v8::internal::Object*, PropertyAttributes, v8::internal::StrictModeFlag, bool*)
+
+0.10^3107295^HTMLParserThrea^chrome               ^[.] WebCore::ElementStyleResources::ElementStyleResources()
+            |
+            --- WebCore::ElementStyleResources::ElementStyleResources()
+
+0.10^3103474^HTMLParserThrea^chrome               ^[.] void WTF::deleteAllValues<true, WebCore::RenderBlock::FloatingObject*, WTF::HashTable<WTF::ListHashSetNode<WebCore::RenderBlock::FloatingObject*, 4ul>*, WTF::ListHashSetNode<WebCore::RenderBlock::FloatingObject*, 4ul>*, WTF::IdentityExtractor, WTF::ListHashSetNodeHashFunctions<WebCore::RenderBlock::FloatingObjectHashFunctions>, WTF::HashTraits<WTF::ListHashSetNode<WebCore::RenderBlock::FloatingObject*, 4ul>*>, WTF::HashTraits<WTF::ListHashSetNode<WebCo
+            |
+            --- void WTF::deleteAllValues<true, WebCore::RenderBlock::FloatingObject*, WTF::HashTable<WTF::ListHashSetNode<WebCore::RenderBlock::FloatingObject*, 4ul>*, WTF::ListHashSetNode<WebCore::RenderBlock::FloatingObject*, 4ul>*, WTF::IdentityExtractor, WTF::ListHashSetNodeHashFunctions<WebCore::RenderBlock::FloatingObjectHashFunctions>, WTF::HashTraits<WTF::ListHashSetNode<WebCore::RenderBlock::FloatingObject*, 4ul>*>, WTF::HashTraits<WTF::ListHashSetNode<WebCore::RenderBlock::FloatingObject*, 4ul>*> > const>(WTF::HashTable<WTF::ListHashSetNode<WebCore::RenderBlock::FloatingObject*, 4ul>*, WTF::ListHashSetNode<WebCore::RenderBlock::FloatingObject*, 4ul>*, WTF::IdentityExtractor, WTF::ListHashSetNodeHashFunctions<WebCore::RenderBlock::FloatingObjectHashFunctions>, WTF::HashTraits<WTF::ListHashSetNode<WebCore::RenderBlock::FloatingObject*, 4ul>*>, WTF::HashTraits<WTF::ListHashSetNode<WebCore::RenderBlock::FloatingObject*, 4ul>*> > const&)
+
+0.10^3103474^HTMLParserThrea^chrome               ^[.] base::subtle::RefCountedBase::Release() const
+            |
+            --- base::subtle::RefCountedBase::Release() const
+
+0.10^3092884^HTMLParserThrea^chrome               ^[.] WebCore::RenderView::usesCompositing() const
+            |
+            --- WebCore::RenderView::usesCompositing() const
+
+0.10^3090079^HTMLParserThrea^chrome               ^[.] webCoreInitializeScriptWrappableForInterface(WebCore::Element*)
+            |
+            --- webCoreInitializeScriptWrappableForInterface(WebCore::Element*)
+
+0.10^3084985^HTMLParserThrea^chrome               ^[.] WebCore::ScopedStyleTree::pushStyleCache(WebCore::ContainerNode const*, WebCore::ContainerNode const*)
+            |
+            --- WebCore::ScopedStyleTree::pushStyleCache(WebCore::ContainerNode const*, WebCore::ContainerNode const*)
+
+0.10^3075629^HTMLParserThrea^chrome               ^[.] v8::internal::HydrogenCodeStub::MinorKey()
+            |
+            --- v8::internal::HydrogenCodeStub::MinorKey()
+
+0.10^3075535^HTMLParserThrea^librt-2.15.so        ^[.] clock_gettime
+            |
+            --- clock_gettime
+
+0.10^3066941^HTMLParserThrea^chrome               ^[.] v8::internal::Assembler::jmp(v8::internal::Handle<v8::internal::Code>, v8::internal::RelocInfo::Mode)
+            |
+            --- v8::internal::Assembler::jmp(v8::internal::Handle<v8::internal::Code>, v8::internal::RelocInfo::Mode)
+
+0.10^3064555^HTMLParserThrea^chrome               ^[.] WebCore::RenderStyle::getRoundedBorderFor(WebCore::LayoutRect const&, WebCore::RenderView*, bool, bool) const
+            |
+            --- WebCore::RenderStyle::getRoundedBorderFor(WebCore::LayoutRect const&, WebCore::RenderView*, bool, bool) const
+
+0.10^3056872^HTMLParserThrea^chrome               ^[.] WebCore::LayoutRect::intersect(WebCore::LayoutRect const&)
+            |
+            --- WebCore::LayoutRect::intersect(WebCore::LayoutRect const&)
+                (nil)
+
+0.10^3056623^HTMLParserThrea^chrome               ^[.] v8::internal::CodeCacheHashTable::Put(v8::internal::Name*, v8::internal::Code*)
+            |
+            --- v8::internal::CodeCacheHashTable::Put(v8::internal::Name*, v8::internal::Code*)
+
+0.10^3054623^HTMLParserThrea^chrome               ^[.] v8::internal::ActionNode::GetQuickCheckDetails(v8::internal::QuickCheckDetails*, v8::internal::RegExpCompiler*, int, bool)
+            |
+            --- v8::internal::ActionNode::GetQuickCheckDetails(v8::internal::QuickCheckDetails*, v8::internal::RegExpCompiler*, int, bool)
+                0x3b2a00000002
+
+0.10^3051571^HTMLParserThrea^chrome               ^[.] _ZN2v88internal12PropertyCell17SetValueInferTypeEPNS0_6ObjectENS0_16WriteBarrierModeE.part.326
+            |
+            --- _ZN2v88internal12PropertyCell17SetValueInferTypeEPNS0_6ObjectENS0_16WriteBarrierModeE.part.326
+
+0.10^3047011^HTMLParserThrea^chrome               ^[.] WebCore::RenderBoxModelObject::paintFillLayerExtended(WebCore::PaintInfo const&, WebCore::Color const&, WebCore::FillLayer const*, WebCore::LayoutRect const&, WebCore::BackgroundBleedAvoidance, WebCore::InlineFlowBox*, WebCore::LayoutSize const&, WebCore::CompositeOperator, WebCore::RenderObject*)
+            |
+            --- WebCore::RenderBoxModelObject::paintFillLayerExtended(WebCore::PaintInfo const&, WebCore::Color const&, WebCore::FillLayer const*, WebCore::LayoutRect const&, WebCore::BackgroundBleedAvoidance, WebCore::InlineFlowBox*, WebCore::LayoutSize const&, WebCore::CompositeOperator, WebCore::RenderObject*)
+
+0.10^3047011^HTMLParserThrea^chrome               ^[.] WebCore::Timer<WebCore::EventSender<WebCore::ImageLoader> >::fired()
+            |
+            --- WebCore::Timer<WebCore::EventSender<WebCore::ImageLoader> >::fired()
+
+0.10^3042405^HTMLParserThrea^chrome               ^[.] SkGlyphCache::getGlyphIDMetrics(unsigned short)
+            |
+            --- SkGlyphCache::getGlyphIDMetrics(unsigned short)
+
+0.10^3034275^HTMLParserThrea^chrome               ^[.] WebCore::StyleBuilder::applyProperty(WebCore::CSSPropertyID, WebCore::StyleResolver*, WebCore::StyleResolverState&, WebCore::CSSValue*, bool, bool)
+            |
+            --- WebCore::StyleBuilder::applyProperty(WebCore::CSSPropertyID, WebCore::StyleResolver*, WebCore::StyleResolverState&, WebCore::CSSValue*, bool, bool)
+                0x1e83176a8568
+
+0.10^3027837^HTMLParserThrea^chrome               ^[.] v8::preparser::PreParser::ParseBinaryExpression(int, bool, bool*)
+            |
+            --- v8::preparser::PreParser::ParseBinaryExpression(int, bool, bool*)
+                0x7f7a0b96214000
+
+0.10^3027190^HTMLParserThrea^chrome               ^[.] v8::internal::CallOptimization::GetPrototypeDepthOfExpectedType(v8::internal::Handle<v8::internal::JSObject>, v8::internal::Handle<v8::internal::JSObject>) const
+            |
+            --- v8::internal::CallOptimization::GetPrototypeDepthOfExpectedType(v8::internal::Handle<v8::internal::JSObject>, v8::internal::Handle<v8::internal::JSObject>) const
+
+0.10^3025376^HTMLParserThrea^chrome               ^[.] v8::Function::Call(v8::Handle<v8::Object>, int, v8::Handle<v8::Value>*)
+            |
+            --- v8::Function::Call(v8::Handle<v8::Object>, int, v8::Handle<v8::Value>*)
+
+0.10^3019937^HTMLParserThrea^chrome               ^[.] net::HttpResponseHeaders::EnumerateHeader(void**, base::BasicStringPiece<std::string> const&, std::string*) const
+            |
+            --- net::HttpResponseHeaders::EnumerateHeader(void**, base::BasicStringPiece<std::string> const&, std::string*) const
+                0x7fff30e91800
+
+0.10^3017488^HTMLParserThrea^chrome               ^[.] v8::internal::Parser::ParseLazy()
+            |
+            --- v8::internal::Parser::ParseLazy()
+
+0.10^3014944^HTMLParserThrea^chrome               ^[.] WebCore::MarkupAccumulator::appendAttribute(WTF::StringBuilder&, WebCore::Element*, WebCore::Attribute const&, WTF::HashMap<WTF::AtomicStringImpl*, WTF::AtomicStringImpl*, WTF::PtrHash<WTF::AtomicStringImpl*>, WTF::HashTraits<WTF::AtomicStringImpl*>, WTF::HashTraits<WTF::AtomicStringImpl*> >*)
+            |
+            --- WebCore::MarkupAccumulator::appendAttribute(WTF::StringBuilder&, WebCore::Element*, WebCore::Attribute const&, WTF::HashMap<WTF::AtomicStringImpl*, WTF::AtomicStringImpl*, WTF::PtrHash<WTF::AtomicStringImpl*>, WTF::HashTraits<WTF::AtomicStringImpl*>, WTF::HashTraits<WTF::AtomicStringImpl*> >*)
+
+0.10^3005563^HTMLParserThrea^chrome               ^[.] tcmalloc::PageHeap::Carve(tcmalloc::Span*, unsigned long)
+            |
+            --- tcmalloc::PageHeap::Carve(tcmalloc::Span*, unsigned long)
+
+0.10^3002471^HTMLParserThrea^chrome               ^[.] WebCore::RenderLayer::paintOutlineForFragments(WTF::Vector<WebCore::LayerFragment, 1ul> const&, WebCore::GraphicsContext*, WebCore::RenderLayer::LayerPaintingInfo const&, unsigned int, WebCore::RenderObject*)
+            |
+            --- WebCore::RenderLayer::paintOutlineForFragments(WTF::Vector<WebCore::LayerFragment, 1ul> const&, WebCore::GraphicsContext*, WebCore::RenderLayer::LayerPaintingInfo const&, unsigned int, WebCore::RenderObject*)
+
+0.10^2988466^HTMLParserThrea^chrome               ^[.] v8::internal::CompareIC::TargetState(v8::internal::CompareIC::State, v8::internal::CompareIC::State, v8::internal::CompareIC::State, bool, v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::Object>)
+            |
+            --- v8::internal::CompareIC::TargetState(v8::internal::CompareIC::State, v8::internal::CompareIC::State, v8::internal::CompareIC::State, bool, v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::Object>)
+
+0.10^2985776^HTMLParserThrea^chrome               ^[.] v8::internal::CodeStub::UseSpecialCache()
+            |
+            --- v8::internal::CodeStub::UseSpecialCache()
+
+0.10^2983793^HTMLParserThrea^chrome               ^[.] v8::internal::Genesis::TransferNamedProperties(v8::internal::Handle<v8::internal::JSObject>, v8::internal::Handle<v8::internal::JSObject>)
+            |
+            --- v8::internal::Genesis::TransferNamedProperties(v8::internal::Handle<v8::internal::JSObject>, v8::internal::Handle<v8::internal::JSObject>)
+
+0.10^2978233^HTMLParserThrea^chrome               ^[.] v8::internal::ReturnStatement::Accept(v8::internal::AstVisitor*)
+            |
+            --- v8::internal::ReturnStatement::Accept(v8::internal::AstVisitor*)
+
+0.10^2961361^HTMLParserThrea^chrome               ^[.] WebCore::HTMLInputElement::parseAttribute(WebCore::QualifiedName const&, WTF::AtomicString const&)
+            |
+            --- WebCore::HTMLInputElement::parseAttribute(WebCore::QualifiedName const&, WTF::AtomicString const&)
+                WebCore::V8PerIsolateData::hasInstance(WebCore::WrapperTypeInfo*, v8::Handle<v8::Value>, WebCore::WrapperWorldType)
+
+0.10^2950771^HTMLParserThrea^chrome               ^[.] webkit::ppapi::PluginInstance::Paint(SkCanvas*, gfx::Rect const&, gfx::Rect const&)
+            |
+            --- webkit::ppapi::PluginInstance::Paint(SkCanvas*, gfx::Rect const&, gfx::Rect const&)
+
+0.10^2941407^HTMLParserThrea^chrome               ^[.] WebCore::DocumentStyleSheetCollection::combineCSSFeatureFlags(WebCore::RuleFeatureSet const&)
+            |
+            --- WebCore::DocumentStyleSheetCollection::combineCSSFeatureFlags(WebCore::RuleFeatureSet const&)
+
+0.10^2927492^HTMLParserThrea^chrome               ^[.] SkCanvas::~SkCanvas()
+            |
+            --- SkCanvas::~SkCanvas()
+
+0.10^2924222^HTMLParserThrea^chrome               ^[.] WTF::HashTableAddResult<WTF::HashTableIterator<unsigned long, WTF::KeyValuePair<unsigned long, WTF::OwnPtr<WebCore::ProgressItem> >, WTF::KeyValuePairKeyExtractor<WTF::KeyValuePair<unsigned long, WTF::OwnPtr<WebCore::ProgressItem> > >, WTF::IntHash<unsigned long>, WTF::HashMapValueTraits<WTF::HashTraits<unsigned long>, WTF::HashTraits<WTF::OwnPtr<WebCore::ProgressItem> > >, WTF::HashTraits<unsigned long> > > WTF::HashTable<unsigned long, WTF::KeyValueP
+            |
+            --- WTF::HashTableAddResult<WTF::HashTableIterator<unsigned long, WTF::KeyValuePair<unsigned long, WTF::OwnPtr<WebCore::ProgressItem> >, WTF::KeyValuePairKeyExtractor<WTF::KeyValuePair<unsigned long, WTF::OwnPtr<WebCore::ProgressItem> > >, WTF::IntHash<unsigned long>, WTF::HashMapValueTraits<WTF::HashTraits<unsigned long>, WTF::HashTraits<WTF::OwnPtr<WebCore::ProgressItem> > >, WTF::HashTraits<unsigned long> > > WTF::HashTable<unsigned long, WTF::KeyValuePair<unsigned long, WTF::OwnPtr<WebCore::ProgressItem> >, WTF::KeyValuePairKeyExtractor<WTF::KeyValuePair<unsigned long, WTF::OwnPtr<WebCore::ProgressItem> > >, WTF::IntHash<unsigned long>, WTF::HashMapValueTraits<WTF::HashTraits<unsigned long>, WTF::HashTraits<WTF::OwnPtr<WebCore::ProgressItem> > >, WTF::HashTraits<unsigned long> >::add<WTF::HashMapTranslator<WTF::HashMapValueTraits<WTF::HashTraits<unsigned long>, WTF::HashTraits<WTF::OwnPtr<WebCore::ProgressItem> > >, WTF::IntHash<unsigned long> >, unsigned long, WTF::PassOwnPtr<WebCore::ProgressItem> >(unsigned long const&, WTF::PassOwnPtr<WebCore::ProgressItem> const&)
+
+0.10^2912173^HTMLParserThrea^chrome               ^[.] v8::internal::Genesis::CreateNewGlobals(v8::Handle<v8::ObjectTemplate>, v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::GlobalObject>*)
+            |
+            --- v8::internal::Genesis::CreateNewGlobals(v8::Handle<v8::ObjectTemplate>, v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::GlobalObject>*)
+
+0.10^2898436^HTMLParserThrea^chrome               ^[.] v8::internal::Runtime_FunctionSetName(int, v8::internal::Object**, v8::internal::Isolate*)
+            |
+            --- v8::internal::Runtime_FunctionSetName(int, v8::internal::Object**, v8::internal::Isolate*)
+                0x2281c14315f1
+                0x2281c14310e9
+                0x2281c1434402
+                0x2281c142b664
+                0x2281c1417d97
+                v8::internal::Invoke(bool, v8::internal::Handle<v8::internal::JSFunction>, v8::internal::Handle<v8::internal::Object>, int, v8::internal::Handle<v8::internal::Object>*, bool*)
+
+0.10^2892794^HTMLParserThrea^chrome               ^[.] v8::internal::Heap::AllocateUninitializedFixedArray(int)
+            |
+            --- v8::internal::Heap::AllocateUninitializedFixedArray(int)
+
+0.10^2891663^HTMLParserThrea^chrome               ^[.] content::RenderViewImpl::didCommitProvisionalLoad(WebKit::WebFrame*, bool)
+            |
+            --- content::RenderViewImpl::didCommitProvisionalLoad(WebKit::WebFrame*, bool)
+
+0.10^2867709^HTMLParserThrea^chrome               ^[.] v8::internal::Object::GetPropertyWithReceiver(v8::internal::Object*, v8::internal::Name*, PropertyAttributes*)
+            |
+            --- v8::internal::Object::GetPropertyWithReceiver(v8::internal::Object*, v8::internal::Name*, PropertyAttributes*)
+
+0.09^2852873^HTMLParserThrea^chrome               ^[.] WebCore::RenderLineBoxList::deleteLineBoxes(WebCore::RenderArena*)
+            |
+            --- WebCore::RenderLineBoxList::deleteLineBoxes(WebCore::RenderArena*)
+
+0.09^2848135^HTMLParserThrea^chrome               ^[.] base::internal::IncomingTaskQueue::PostPendingTask(base::PendingTask*)
+            |
+            --- base::internal::IncomingTaskQueue::PostPendingTask(base::PendingTask*)
+
+0.09^2845960^HTMLParserThrea^chrome               ^[.] WebCore::V8HTMLEmbedElement::namedPropertyGetterCustom(v8::Local<v8::String>, v8::PropertyCallbackInfo<v8::Value> const&)
+            |
+            --- WebCore::V8HTMLEmbedElement::namedPropertyGetterCustom(v8::Local<v8::String>, v8::PropertyCallbackInfo<v8::Value> const&)
+                0x7f7a0b900000
+
+0.09^2831855^HTMLParserThrea^chrome               ^[.] WebCore::RenderLayer::paintLayerContents(WebCore::GraphicsContext*, WebCore::RenderLayer::LayerPaintingInfo const&, unsigned int)
+            |
+            --- WebCore::RenderLayer::paintLayerContents(WebCore::GraphicsContext*, WebCore::RenderLayer::LayerPaintingInfo const&, unsigned int)
+                (nil)
+
+0.09^2830116^HTMLParserThrea^chrome               ^[.] v8::internal::RegExpParser::Advance()
+            |
+            --- v8::internal::RegExpParser::Advance()
+                0x3b2abdce1010
+
+0.09^2802116^HTMLParserThrea^chrome               ^[.] WebCore::RenderBlock::logicalLeftFloatOffsetForLine(WebCore::LayoutUnit, WebCore::LayoutUnit, WebCore::LayoutUnit*, WebCore::LayoutUnit, WebCore::RenderBlock::ShapeOutsideFloatOffsetMode) const
+            |
+            --- WebCore::RenderBlock::logicalLeftFloatOffsetForLine(WebCore::LayoutUnit, WebCore::LayoutUnit, WebCore::LayoutUnit*, WebCore::LayoutUnit, WebCore::RenderBlock::ShapeOutsideFloatOffsetMode) const
+
+0.09^2792893^HTMLParserThrea^chrome               ^[.] v8::internal::HashTable<v8::internal::UnseededNumberDictionaryShape, unsigned int>::FindEntry(v8::internal::Isolate*, unsigned int)
+            |
+            --- v8::internal::HashTable<v8::internal::UnseededNumberDictionaryShape, unsigned int>::FindEntry(v8::internal::Isolate*, unsigned int)
+
+0.09^2758347^HTMLParserThrea^chrome               ^[.] v8::internal::ElementsAccessorBase<v8::internal::DictionaryElementsAccessor, v8::internal::ElementsKindTraits<(v8::internal::ElementsKind)6> >::Get(v8::internal::Object*, v8::internal::JSObject*, unsigned int, v8::internal::FixedArrayBase*)
+            |
+            --- v8::internal::ElementsAccessorBase<v8::internal::DictionaryElementsAccessor, v8::internal::ElementsKindTraits<(v8::internal::ElementsKind)6> >::Get(v8::internal::Object*, v8::internal::JSObject*, unsigned int, v8::internal::FixedArrayBase*)
+
+0.09^2738185^HTMLParserThrea^chrome               ^[.] v8::internal::LoadFieldStub::MajorKey()
+            |
+            --- v8::internal::LoadFieldStub::MajorKey()
+
+0.09^2719813^HTMLParserThrea^chrome               ^[.] _ZN2v88internal9Execution4CallENS0_6HandleINS0_6ObjectEEES4_iPS4_Pbb.constprop.84
+            |
+            --- _ZN2v88internal9Execution4CallENS0_6HandleINS0_6ObjectEEES4_iPS4_Pbb.constprop.84
+
+0.09^2709340^HTMLParserThrea^chrome               ^[.] operator delete(void*)
+            |
+            --- operator delete(void*)
+
+0.09^2697911^HTMLParserThrea^chrome               ^[.] v8::internal::Scope::AllocateVariablesRecursively()
+            |
+            --- v8::internal::Scope::AllocateVariablesRecursively()
+
+0.09^2685980^HTMLParserThrea^chrome               ^[.] IPC::SyncChannel::SendWithTimeout(IPC::Message*, int)
+            |
+            --- IPC::SyncChannel::SendWithTimeout(IPC::Message*, int)
+
+0.09^2673092^HTMLParserThrea^chrome               ^[.] v8::internal::StubCache::ComputeCallPreMonomorphic(int, v8::internal::Code::Kind, int)
+            |
+            --- v8::internal::StubCache::ComputeCallPreMonomorphic(int, v8::internal::Code::Kind, int)
+
+0.09^2660139^HTMLParserThrea^chrome               ^[.] v8::internal::HashTable<v8::internal::CodeCacheHashTableShape, v8::internal::HashTableKey*>::FindEntry(v8::internal::HashTableKey*)
+            |
+            --- v8::internal::HashTable<v8::internal::CodeCacheHashTableShape, v8::internal::HashTableKey*>::FindEntry(v8::internal::HashTableKey*)
+
+0.09^2639411^HTMLParserThrea^chrome               ^[.] base::subtle::RefCountedThreadSafeBase::HasOneRef() const
+            |
+            --- base::subtle::RefCountedThreadSafeBase::HasOneRef() const
+                0x3b2abd1f8b20
+
+0.09^2639411^HTMLParserThrea^chrome               ^[.] PickleIterator::ReadLong(long*)
+            |
+            --- PickleIterator::ReadLong(long*)
+                0x2e40c81e951be0
+
+0.09^2632999^HTMLParserThrea^chrome               ^[.] IPC::SyncChannel::SyncContext::DispatchMessages()
+            |
+            --- IPC::SyncChannel::SyncContext::DispatchMessages()
+
+0.09^2600329^HTMLParserThrea^chrome               ^[.] v8::internal::Operand::Operand(v8::internal::Register, int)
+            |
+            --- v8::internal::Operand::Operand(v8::internal::Register, int)
+                0x3b2abd628000
+
+0.09^2577224^HTMLParserThrea^chrome               ^[.] v8::internal::CompareIC::ComputeCondition(v8::internal::Token::Value)
+            |
+            --- v8::internal::CompareIC::ComputeCondition(v8::internal::Token::Value)
+
+0.09^2566312^HTMLParserThrea^chrome               ^[.] v8::internal::Rewriter::Rewrite(v8::internal::CompilationInfo*)
+            |
+            --- v8::internal::Rewriter::Rewrite(v8::internal::CompilationInfo*)
+                0x7f7a0b900000
+
+0.08^2546113^HTMLParserThrea^chrome               ^[.] WebCore::RenderBoxModelObject::borderLeft() const
+            |
+            --- WebCore::RenderBoxModelObject::borderLeft() const
+
+0.08^2538991^HTMLParserThrea^chrome               ^[.] v8::internal::Handle<v8::internal::String> v8::internal::URIEscape::Escape<unsigned char>(v8::internal::Isolate*, v8::internal::Handle<v8::internal::String>)
+            |
+            --- v8::internal::Handle<v8::internal::String> v8::internal::URIEscape::Escape<unsigned char>(v8::internal::Isolate*, v8::internal::Handle<v8::internal::String>)
+
+0.08^2527291^HTMLParserThrea^chrome               ^[.] WTF::HashTableAddResult<WTF::HashTableIterator<WTF::StringImpl*, WTF::StringImpl*, WTF::IdentityExtractor, WTF::StringHash, WTF::HashTraits<WTF::StringImpl*>, WTF::HashTraits<WTF::StringImpl*> > > WTF::HashTable<WTF::StringImpl*, WTF::StringImpl*, WTF::IdentityExtractor, WTF::StringHash, WTF::HashTraits<WTF::StringImpl*>, WTF::HashTraits<WTF::StringImpl*> >::add<WTF::IdentityHashTranslator<WTF::StringHash>, WTF::StringImpl*, WTF::StringImpl*>(WTF::Stri
+            |
+            --- WTF::HashTableAddResult<WTF::HashTableIterator<WTF::StringImpl*, WTF::StringImpl*, WTF::IdentityExtractor, WTF::StringHash, WTF::HashTraits<WTF::StringImpl*>, WTF::HashTraits<WTF::StringImpl*> > > WTF::HashTable<WTF::StringImpl*, WTF::StringImpl*, WTF::IdentityExtractor, WTF::StringHash, WTF::HashTraits<WTF::StringImpl*>, WTF::HashTraits<WTF::StringImpl*> >::add<WTF::IdentityHashTranslator<WTF::StringHash>, WTF::StringImpl*, WTF::StringImpl*>(WTF::StringImpl* const&, WTF::StringImpl* const&)
+                0x7fff30e91098
+
+0.08^2505118^HTMLParserThrea^chrome               ^[.] v8::internal::AssertionNode::Emit(v8::internal::RegExpCompiler*, v8::internal::Trace*)
+            |
+            --- v8::internal::AssertionNode::Emit(v8::internal::RegExpCompiler*, v8::internal::Trace*)
+                0xa
+
+0.08^2499686^HTMLParserThrea^chrome               ^[.] base::SampleVector::GetBucketIndex(int) const
+            |
+            --- base::SampleVector::GetBucketIndex(int) const
+
+0.08^2493442^HTMLParserThrea^chrome               ^[.] v8::internal::RegExpImpl::SetLastMatchInfo(v8::internal::Handle<v8::internal::JSArray>, v8::internal::Handle<v8::internal::String>, int, int*)
+            |
+            --- v8::internal::RegExpImpl::SetLastMatchInfo(v8::internal::Handle<v8::internal::JSArray>, v8::internal::Handle<v8::internal::String>, int, int*)
+
+0.08^2449547^HTMLParserThrea^chrome               ^[.] content::(anonymous namespace)::RendererMessageLoopObserver::DidProcessTask(base::PendingTask const&)
+            |
+            --- content::(anonymous namespace)::RendererMessageLoopObserver::DidProcessTask(base::PendingTask const&)
+                0x7f7a0b8ff200
+
+0.08^2382741^HTMLParserThrea^chrome               ^[.] WTF::HashTableAddResult<WTF::HashTableIterator<WebCore::CachedResource*, WTF::KeyValuePair<WebCore::CachedResource*, WTF::RefPtr<WebCore::ResourceTimingInfo> >, WTF::KeyValuePairKeyExtractor<WTF::KeyValuePair<WebCore::CachedResource*, WTF::RefPtr<WebCore::ResourceTimingInfo> > >, WTF::PtrHash<WebCore::CachedResource*>, WTF::HashMapValueTraits<WTF::HashTraits<WebCore::CachedResource*>, WTF::HashTraits<WTF::RefPtr<WebCore::ResourceTimingInfo> > >, WTF::H
+            |
+            --- WTF::HashTableAddResult<WTF::HashTableIterator<WebCore::CachedResource*, WTF::KeyValuePair<WebCore::CachedResource*, WTF::RefPtr<WebCore::ResourceTimingInfo> >, WTF::KeyValuePairKeyExtractor<WTF::KeyValuePair<WebCore::CachedResource*, WTF::RefPtr<WebCore::ResourceTimingInfo> > >, WTF::PtrHash<WebCore::CachedResource*>, WTF::HashMapValueTraits<WTF::HashTraits<WebCore::CachedResource*>, WTF::HashTraits<WTF::RefPtr<WebCore::ResourceTimingInfo> > >, WTF::HashTraits<WebCore::CachedResource*> > > WTF::HashTable<WebCore::CachedResource*, WTF::KeyValuePair<WebCore::CachedResource*, WTF::RefPtr<WebCore::ResourceTimingInfo> >, WTF::KeyValuePairKeyExtractor<WTF::KeyValuePair<WebCore::CachedResource*, WTF::RefPtr<WebCore::ResourceTimingInfo> > >, WTF::PtrHash<WebCore::CachedResource*>, WTF::HashMapValueTraits<WTF::HashTraits<WebCore::CachedResource*>, WTF::HashTraits<WTF::RefPtr<WebCore::ResourceTimingInfo> > >, WTF::HashTraits<WebCore::CachedResource*> >::add<WTF::HashMapTranslator<WTF::HashMapValueTraits<WTF::HashTraits<WebCore::CachedResource*>, WTF::HashTraits<WTF::RefPtr<WebCore::ResourceTimingInfo> > >, WTF::PtrHash<WebCore::CachedResource*> >, WebCore::CachedResource*, WTF::PassRefPtr<WebCore::ResourceTimingInfo> >(WebCore::CachedResource* const&, WTF::PassRefPtr<WebCore::ResourceTimingInfo> const&)
+
+0.08^2347799^HTMLParserThrea^chrome               ^[.] v8::internal::Isolate::MayNamedAccess(v8::internal::JSObject*, v8::internal::Object*, v8::AccessType)
+            |
+            --- v8::internal::Isolate::MayNamedAccess(v8::internal::JSObject*, v8::internal::Object*, v8::AccessType)
+
+0.08^2337746^HTMLParserThrea^chrome               ^[.] v8::internal::Heap::NumberFromDouble(double, v8::internal::PretenureFlag)
+            |
+            --- v8::internal::Heap::NumberFromDouble(double, v8::internal::PretenureFlag)
+
+0.08^2334937^HTMLParserThrea^chrome               ^[.] WebCore::LayoutRect::intersects(WebCore::LayoutRect const&) const
+            |
+            --- WebCore::LayoutRect::intersects(WebCore::LayoutRect const&) const
+
+0.08^2315633^HTMLParserThrea^chrome               ^[.] _ZN7content14RenderViewImpl15willSendRequestEPN6WebKit8WebFrameEjRNS1_13WebURLRequestERKNS1_14WebURLResponseE.part.953
+            |
+            --- _ZN7content14RenderViewImpl15willSendRequestEPN6WebKit8WebFrameEjRNS1_13WebURLRequestERKNS1_14WebURLResponseE.part.953
+                0x7f7a11c1f320
+                0x7fff30e90fd0
+
+0.08^2309876^HTMLParserThrea^libstdc++.so.6.0.16  ^[.] std::string::find_first_not_of(char const*, unsigned long, unsigned long) const
+            |
+            --- std::string::find_first_not_of(char const*, unsigned long, unsigned long) const
+                (nil)
+
+0.08^2271635^HTMLParserThrea^chrome               ^[.] base::TaskQueue::Swap(base::TaskQueue*)
+            |
+            --- base::TaskQueue::Swap(base::TaskQueue*)
+
+0.08^2270859^HTMLParserThrea^chrome               ^[.] v8::internal::Map::ShareDescriptor(v8::internal::DescriptorArray*, v8::internal::Descriptor*)
+            |
+            --- v8::internal::Map::ShareDescriptor(v8::internal::DescriptorArray*, v8::internal::Descriptor*)
+
+0.08^2267153^HTMLParserThrea^chrome               ^[.] v8::internal::Map::EnsureDescriptorSlack(v8::internal::Handle<v8::internal::Map>, int)
+            |
+            --- v8::internal::Map::EnsureDescriptorSlack(v8::internal::Handle<v8::internal::Map>, int)
+
+0.07^2254719^HTMLParserThrea^chrome               ^[.] v8::internal::Map::IndexInCodeCache(v8::internal::Object*, v8::internal::Code*)
+            |
+            --- v8::internal::Map::IndexInCodeCache(v8::internal::Object*, v8::internal::Code*)
+
+0.07^2218397^HTMLParserThrea^chrome               ^[.] WebCore::RenderBlock::paintContents(WebCore::PaintInfo&, WebCore::LayoutPoint const&)
+            |
+            --- WebCore::RenderBlock::paintContents(WebCore::PaintInfo&, WebCore::LayoutPoint const&)
+
+0.07^2215989^HTMLParserThrea^chrome               ^[.] SkRegion::freeRuns()
+            |
+            --- SkRegion::freeRuns()
+
+0.07^2213591^HTMLParserThrea^libpthread-2.15.so   ^[.] pthread_cond_destroy@@GLIBC_2.3.2
+            |
+            --- pthread_cond_destroy@@GLIBC_2.3.2
+
+0.07^2185498^HTMLParserThrea^chrome               ^[.] WebCore::RenderLayer::paintForegroundForFragmentsWithPhase(WebCore::PaintPhase, WTF::Vector<WebCore::LayerFragment, 1ul> const&, WebCore::GraphicsContext*, WebCore::RenderLayer::LayerPaintingInfo const&, unsigned int, WebCore::RenderObject*)
+            |
+            --- WebCore::RenderLayer::paintForegroundForFragmentsWithPhase(WebCore::PaintPhase, WTF::Vector<WebCore::LayerFragment, 1ul> const&, WebCore::GraphicsContext*, WebCore::RenderLayer::LayerPaintingInfo const&, unsigned int, WebCore::RenderObject*)
+                (nil)
+
+0.07^2181719^HTMLParserThrea^chrome               ^[.] v8::internal::JSFunction::CompileLazy(v8::internal::Handle<v8::internal::JSFunction>, v8::internal::ClearExceptionFlag)
+            |
+            --- v8::internal::JSFunction::CompileLazy(v8::internal::Handle<v8::internal::JSFunction>, v8::internal::ClearExceptionFlag)
+
+0.07^2181709^HTMLParserThrea^chrome               ^[.] base::subtle::RefCountedThreadSafeBase::AddRef() const
+            |
+            --- base::subtle::RefCountedThreadSafeBase::AddRef() const
+
+0.07^2162161^HTMLParserThrea^chrome               ^[.] v8::internal::LChunk::MarkEmptyBlocks()
+            |
+            --- v8::internal::LChunk::MarkEmptyBlocks()
+
+0.07^2154798^HTMLParserThrea^chrome               ^[.] v8::preparser::PreParser::ParsePostfixExpression(bool*)
+            |
+            --- v8::preparser::PreParser::ParsePostfixExpression(bool*)
+
+0.07^2153747^HTMLParserThrea^chrome               ^[.] WebCore::deviceScaleFactor(WebCore::Frame*)
+            |
+            --- WebCore::deviceScaleFactor(WebCore::Frame*)
+                (nil)
+
+0.07^2111797^HTMLParserThrea^chrome               ^[.] v8::preparser::PreParser::ParseConditionalExpression(bool, bool*)
+            |
+            --- v8::preparser::PreParser::ParseConditionalExpression(bool, bool*)
+                0x3b2abe08368000
+
+0.07^2107366^HTMLParserThrea^chrome               ^[.] ppapi::thunk::subtle::EnterBase::EnterBase(int)
+            |
+            --- ppapi::thunk::subtle::EnterBase::EnterBase(int)
+
+0.07^2061079^HTMLParserThrea^chrome               ^[.] v8::internal::FullCodeGenerator::EmitBackEdgeTable()
+            |
+            --- v8::internal::FullCodeGenerator::EmitBackEdgeTable()
+
+0.07^2055678^HTMLParserThrea^chrome               ^[.] WebCore::MarkupAccumulator::appendStartMarkup(WTF::StringBuilder&, WebCore::Node const*, WTF::HashMap<WTF::AtomicStringImpl*, WTF::AtomicStringImpl*, WTF::PtrHash<WTF::AtomicStringImpl*>, WTF::HashTraits<WTF::AtomicStringImpl*>, WTF::HashTraits<WTF::AtomicStringImpl*> >*)
+            |
+            --- WebCore::MarkupAccumulator::appendStartMarkup(WTF::StringBuilder&, WebCore::Node const*, WTF::HashMap<WTF::AtomicStringImpl*, WTF::AtomicStringImpl*, WTF::PtrHash<WTF::AtomicStringImpl*>, WTF::HashTraits<WTF::AtomicStringImpl*>, WTF::HashTraits<WTF::AtomicStringImpl*> >*)
+                (nil)
+
+0.07^2053845^HTMLParserThrea^chrome               ^[.] WebCore::RenderBox::flipForWritingMode(WebCore::LayoutRect&) const
+            |
+            --- WebCore::RenderBox::flipForWritingMode(WebCore::LayoutRect&) const
+
+0.07^2043240^HTMLParserThrea^chrome               ^[.] net::HttpUtil::HeadersIterator::GetNext()
+            |
+            --- net::HttpUtil::HeadersIterator::GetNext()
+                0x7f7a0689c4d8
+
+0.07^2038325^HTMLParserThrea^chrome               ^[.] WTF::HashTable<WTF::AtomicString, WTF::KeyValuePair<WTF::AtomicString, WTF::AtomicString>, WTF::KeyValuePairKeyExtractor<WTF::KeyValuePair<WTF::AtomicString, WTF::AtomicString> >, WTF::CaseFoldingHash, WTF::HashMapValueTraits<WTF::HashTraits<WTF::AtomicString>, WTF::HashTraits<WTF::AtomicString> >, WTF::HashTraits<WTF::AtomicString> >::rehash(int)
+            |
+            --- WTF::HashTable<WTF::AtomicString, WTF::KeyValuePair<WTF::AtomicString, WTF::AtomicString>, WTF::KeyValuePairKeyExtractor<WTF::KeyValuePair<WTF::AtomicString, WTF::AtomicString> >, WTF::CaseFoldingHash, WTF::HashMapValueTraits<WTF::HashTraits<WTF::AtomicString>, WTF::HashTraits<WTF::AtomicString> >, WTF::HashTraits<WTF::AtomicString> >::rehash(int)
+
+0.07^2038325^HTMLParserThrea^chrome               ^[.] WebCore::KURL::isValid() const
+            |
+            --- WebCore::KURL::isValid() const
+
+0.07^2030555^HTMLParserThrea^chrome               ^[.] v8::internal::Scope::LocalLookup(v8::internal::Handle<v8::internal::String>)
+            |
+            --- v8::internal::Scope::LocalLookup(v8::internal::Handle<v8::internal::String>)
+
+0.07^2017287^HTMLParserThrea^chrome               ^[.] WebKit::WebFrameImpl::document() const
+            |
+            --- WebKit::WebFrameImpl::document() const
+
+0.07^1991309^HTMLParserThrea^chrome               ^[.] v8::internal::Assembler::int3()
+            |
+            --- v8::internal::Assembler::int3()
+
+0.07^1989976^HTMLParserThrea^chrome               ^[.] __gnu_cxx::hashtable<std::pair<int const, std::pair<ppapi::Resource*, int> >, int, __gnu_cxx::hash<int>, std::_Select1st<std::pair<int const, std::pair<ppapi::Resource*, int> > >, std::equal_to<int>, std::allocator<std::pair<ppapi::Resource*, int> > >::find_or_insert(std::pair<int const, std::pair<ppapi::Resource*, int> > const&)
+            |
+            --- __gnu_cxx::hashtable<std::pair<int const, std::pair<ppapi::Resource*, int> >, int, __gnu_cxx::hash<int>, std::_Select1st<std::pair<int const, std::pair<ppapi::Resource*, int> > >, std::equal_to<int>, std::allocator<std::pair<ppapi::Resource*, int> > >::find_or_insert(std::pair<int const, std::pair<ppapi::Resource*, int> > const&)
+                (nil)
+
+0.07^1989976^HTMLParserThrea^chrome               ^[.] content::PaintAggregator::InvalidateRect(gfx::Rect const&)
+            |
+            --- content::PaintAggregator::InvalidateRect(gfx::Rect const&)
+
+0.07^1983969^HTMLParserThrea^chrome               ^[.] tracked_objects::ThreadData::TallyADeath(tracked_objects::Births const&, int, int)
+            |
+            --- tracked_objects::ThreadData::TallyADeath(tracked_objects::Births const&, int, int)
+
+0.07^1983969^HTMLParserThrea^chrome               ^[.] WebCore::HTMLDocumentParser::pumpTokenizer(WebCore::HTMLDocumentParser::SynchronousMode)
+            |
+            --- WebCore::HTMLDocumentParser::pumpTokenizer(WebCore::HTMLDocumentParser::SynchronousMode)
+
+0.07^1964943^HTMLParserThrea^chrome               ^[.] v8::internal::NormalizedMapCache::Get(v8::internal::JSObject*, v8::internal::PropertyNormalizationMode)
+            |
+            --- v8::internal::NormalizedMapCache::Get(v8::internal::JSObject*, v8::internal::PropertyNormalizationMode)
+
+0.07^1960139^HTMLParserThrea^chrome               ^[.] WebCore::RenderBox::visualOverflowRect() const
+            |
+            --- WebCore::RenderBox::visualOverflowRect() const
+
+0.06^1953892^HTMLParserThrea^chrome               ^[.] base::MessagePumpDefault::ScheduleWork()
+            |
+            --- base::MessagePumpDefault::ScheduleWork()
+
+0.06^1922033^HTMLParserThrea^chrome               ^[.] v8::preparser::PreParser::ParseIdentifier(bool*)
+            |
+            --- v8::preparser::PreParser::ParseIdentifier(bool*)
+                0x3b2abe08368000
+
+0.06^1902957^HTMLParserThrea^chrome               ^[.] base::MessageLoop::ScheduleWork(bool)
+            |
+            --- base::MessageLoop::ScheduleWork(bool)
+
+0.06^1857157^HTMLParserThrea^chrome               ^[.] v8::internal::FullCodeGenerator::DoTest(v8::internal::Expression*, v8::internal::Label*, v8::internal::Label*, v8::internal::Label*)
+            |
+            --- v8::internal::FullCodeGenerator::DoTest(v8::internal::Expression*, v8::internal::Label*, v8::internal::Label*, v8::internal::Label*)
+                0x7fff30e91bc0
+
+0.06^1758928^HTMLParserThrea^chrome               ^[.] WebCore::RenderLayer::ancestorStackingContainer() const
+            |
+            --- WebCore::RenderLayer::ancestorStackingContainer() const
+
+0.06^1708834^HTMLParserThrea^libcairo.so.2.11000.2^[.] _cairo_matrix_is_identity
+            |
+            --- _cairo_matrix_is_identity
+
+0.06^1707254^HTMLParserThrea^chrome               ^[.] WebCore::RenderLineBoxList::anyLineIntersectsRect(WebCore::RenderBoxModelObject*, WebCore::LayoutRect const&, WebCore::LayoutPoint const&, WebCore::LayoutUnit) const
+            |
+            --- WebCore::RenderLineBoxList::anyLineIntersectsRect(WebCore::RenderBoxModelObject*, WebCore::LayoutRect const&, WebCore::LayoutPoint const&, WebCore::LayoutUnit) const
+
+0.06^1695178^HTMLParserThrea^chrome               ^[.] base::internal::CallbackBase::CallbackBase(base::internal::BindStateBase*)
+            |
+            --- base::internal::CallbackBase::CallbackBase(base::internal::BindStateBase*)
+
+0.06^1682330^HTMLParserThrea^chrome               ^[.] WebCore::RenderBox::clippedOverflowRectForRepaint(WebCore::RenderLayerModelObject const*) const
+            |
+            --- WebCore::RenderBox::clippedOverflowRectForRepaint(WebCore::RenderLayerModelObject const*) const
+
+0.06^1665533^HTMLParserThrea^chrome               ^[.] v8::internal::Zone::DeleteAll()
+            |
+            --- v8::internal::Zone::DeleteAll()
+
+0.05^1641632^HTMLParserThrea^chrome               ^[.] WebCore::Color::blend(WebCore::Color const&) const
+            |
+            --- WebCore::Color::blend(WebCore::Color const&) const
+
+0.05^1611521^HTMLParserThrea^chrome               ^[.] WebKit::currentTimeFunction()
+            |
+            --- WebKit::currentTimeFunction()
+
+0.05^1539536^HTMLParserThrea^chrome               ^[.] WebCore::RenderLayer::collectFragments(WTF::Vector<WebCore::LayerFragment, 1ul>&, WebCore::RenderLayer const*, WebCore::RenderRegion*, WebCore::LayoutRect const&, WebCore::ClipRectsType, WebCore::OverlayScrollbarSizeRelevancy, WebCore::ShouldRespectOverflowClip, WebCore::LayoutPoint const*, WebCore::LayoutRect const*)
+            |
+            --- WebCore::RenderLayer::collectFragments(WTF::Vector<WebCore::LayerFragment, 1ul>&, WebCore::RenderLayer const*, WebCore::RenderRegion*, WebCore::LayoutRect const&, WebCore::ClipRectsType, WebCore::OverlayScrollbarSizeRelevancy, WebCore::ShouldRespectOverflowClip, WebCore::LayoutPoint const*, WebCore::LayoutRect const*)
+
+0.05^1523423^HTMLParserThrea^chrome               ^[.] v8::internal::StubCache::ComputeLoadNormal(v8::internal::Handle<v8::internal::Name>, v8::internal::Handle<v8::internal::JSObject>)
+            |
+            --- v8::internal::StubCache::ComputeLoadNormal(v8::internal::Handle<v8::internal::Name>, v8::internal::Handle<v8::internal::JSObject>)
+                (nil)
+
+0.05^1521966^HTMLParserThrea^chrome               ^[.] void url_canon::(anonymous namespace)::DoHost<char, unsigned char>(char const*, url_parse::Component const&, url_canon::CanonOutputT<char>*, url_canon::CanonHostInfo*)
+            |
+            --- void url_canon::(anonymous namespace)::DoHost<char, unsigned char>(char const*, url_parse::Component const&, url_canon::CanonOutputT<char>*, url_canon::CanonHostInfo*)
+                0xffffffff00000000
+
+0.05^1443413^HTMLParserThrea^chrome               ^[.] WTF::HashTableAddResult<WTF::HashTableIterator<WTF::ListHashSetNode<WebCore::CachedResource*, 256ul>*, WTF::ListHashSetNode<WebCore::CachedResource*, 256ul>*, WTF::IdentityExtractor, WTF::ListHashSetNodeHashFunctions<WTF::PtrHash<WebCore::CachedResource*> >, WTF::HashTraits<WTF::ListHashSetNode<WebCore::CachedResource*, 256ul>*>, WTF::HashTraits<WTF::ListHashSetNode<WebCore::CachedResource*, 256ul>*> > > WTF::HashTable<WTF::ListHashSetNode<WebCore::Cac
+            |
+            --- WTF::HashTableAddResult<WTF::HashTableIterator<WTF::ListHashSetNode<WebCore::CachedResource*, 256ul>*, WTF::ListHashSetNode<WebCore::CachedResource*, 256ul>*, WTF::IdentityExtractor, WTF::ListHashSetNodeHashFunctions<WTF::PtrHash<WebCore::CachedResource*> >, WTF::HashTraits<WTF::ListHashSetNode<WebCore::CachedResource*, 256ul>*>, WTF::HashTraits<WTF::ListHashSetNode<WebCore::CachedResource*, 256ul>*> > > WTF::HashTable<WTF::ListHashSetNode<WebCore::CachedResource*, 256ul>*, WTF::ListHashSetNode<WebCore::CachedResource*, 256ul>*, WTF::IdentityExtractor, WTF::ListHashSetNodeHashFunctions<WTF::PtrHash<WebCore::CachedResource*> >, WTF::HashTraits<WTF::ListHashSetNode<WebCore::CachedResource*, 256ul>*>, WTF::HashTraits<WTF::ListHashSetNode<WebCore::CachedResource*, 256ul>*> >::add<WTF::ListHashSetTranslator<WTF::PtrHash<WebCore::CachedResource*> >, WebCore::CachedResource*, WTF::ListHashSetNodeAllocator<WebCore::CachedResource*, 256ul>*>(WebCore::CachedResource* const&, WTF::ListHashSetNodeAllocator<WebCore::CachedResource*, 256ul>* const&)
+                (nil)
+
+0.05^1380694^HTMLParserThrea^chrome               ^[.] v8::internal::HBasicBlock::Finish(v8::internal::HControlInstruction*)
+            |
+            --- v8::internal::HBasicBlock::Finish(v8::internal::HControlInstruction*)
+
+0.04^1244009^HTMLParserThrea^chrome               ^[.] WebCore::Node::isWebVTTElement() const
+            |
+            --- WebCore::Node::isWebVTTElement() const
+                0x7fff30e91700
+
+
+
+#
+# (For a higher level overview, try: perf report --sort comm,dso)
+#
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/powermetrics_output.output b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/powermetrics_output.output
new file mode 100644
index 0000000..d952096
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/powermetrics_output.output
@@ -0,0 +1,3375 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>GPU</key>
+	<array>
+		<dict>
+			<key>freq_hz</key>
+			<real>1</real>
+			<key>c_state_ns</key>
+			<integer>4295987562</integer>
+			<key>c_state_ratio</key>
+			<real>1</real>
+			<key>GPU number</key>
+			<integer>0</integer>
+			<key>misc counters</key>
+			<array>
+				<dict>
+					<key>GPU Busy</key>
+					<real>0</real>
+				</dict>
+				<dict>
+					<key>FB Test Case</key>
+					<real>0</real>
+				</dict>
+			</array>
+			<key>c_states</key>
+			<array>
+				<dict>
+					<key>used_ns</key>
+					<integer>8737642</integer>
+					<key>used_ratio</key>
+					<real>0.00203391</real>
+				</dict>
+				<dict>
+					<key>used_ns</key>
+					<integer>4287249920</integer>
+					<key>used_ratio</key>
+					<real>0.997969</real>
+				</dict>
+			</array>
+			<key>p_states</key>
+			<array>
+				<dict>
+					<key>frequency</key>
+					<string>1300000000</string>
+					<key>used_ns</key>
+					<integer>0</integer>
+					<key>used_ratio</key>
+					<real>0</real>
+				</dict>
+				<dict>
+					<key>frequency</key>
+					<string>1250000000</string>
+					<key>used_ns</key>
+					<integer>0</integer>
+					<key>used_ratio</key>
+					<real>0</real>
+				</dict>
+				<dict>
+					<key>frequency</key>
+					<string>1200000000</string>
+					<key>used_ns</key>
+					<integer>0</integer>
+					<key>used_ratio</key>
+					<real>0</real>
+				</dict>
+				<dict>
+					<key>frequency</key>
+					<string>1150000000</string>
+					<key>used_ns</key>
+					<integer>0</integer>
+					<key>used_ratio</key>
+					<real>0</real>
+				</dict>
+				<dict>
+					<key>frequency</key>
+					<string>1100000000</string>
+					<key>used_ns</key>
+					<integer>0</integer>
+					<key>used_ratio</key>
+					<real>0</real>
+				</dict>
+				<dict>
+					<key>frequency</key>
+					<string>1050000000</string>
+					<key>used_ns</key>
+					<integer>0</integer>
+					<key>used_ratio</key>
+					<real>0</real>
+				</dict>
+				<dict>
+					<key>frequency</key>
+					<string>1000000000</string>
+					<key>used_ns</key>
+					<integer>0</integer>
+					<key>used_ratio</key>
+					<real>0</real>
+				</dict>
+				<dict>
+					<key>frequency</key>
+					<string>950000000</string>
+					<key>used_ns</key>
+					<integer>0</integer>
+					<key>used_ratio</key>
+					<real>0</real>
+				</dict>
+				<dict>
+					<key>frequency</key>
+					<string>900000000</string>
+					<key>used_ns</key>
+					<integer>0</integer>
+					<key>used_ratio</key>
+					<real>0</real>
+				</dict>
+				<dict>
+					<key>frequency</key>
+					<string>850000000</string>
+					<key>used_ns</key>
+					<integer>0</integer>
+					<key>used_ratio</key>
+					<real>0</real>
+				</dict>
+				<dict>
+					<key>frequency</key>
+					<string>800000000</string>
+					<key>used_ns</key>
+					<integer>0</integer>
+					<key>used_ratio</key>
+					<real>0</real>
+				</dict>
+				<dict>
+					<key>frequency</key>
+					<string>750000000</string>
+					<key>used_ns</key>
+					<integer>0</integer>
+					<key>used_ratio</key>
+					<real>0</real>
+				</dict>
+				<dict>
+					<key>frequency</key>
+					<string>700000000</string>
+					<key>used_ns</key>
+					<integer>0</integer>
+					<key>used_ratio</key>
+					<real>0</real>
+				</dict>
+				<dict>
+					<key>frequency</key>
+					<string>650000000</string>
+					<key>used_ns</key>
+					<integer>0</integer>
+					<key>used_ratio</key>
+					<real>0</real>
+				</dict>
+				<dict>
+					<key>frequency</key>
+					<string>600000000</string>
+					<key>used_ns</key>
+					<integer>0</integer>
+					<key>used_ratio</key>
+					<real>0</real>
+				</dict>
+				<dict>
+					<key>frequency</key>
+					<string>550000000</string>
+					<key>used_ns</key>
+					<integer>0</integer>
+					<key>used_ratio</key>
+					<real>0</real>
+				</dict>
+				<dict>
+					<key>frequency</key>
+					<string>500000000</string>
+					<key>used_ns</key>
+					<integer>0</integer>
+					<key>used_ratio</key>
+					<real>0</real>
+				</dict>
+				<dict>
+					<key>frequency</key>
+					<string>450000000</string>
+					<key>used_ns</key>
+					<integer>0</integer>
+					<key>used_ratio</key>
+					<real>0</real>
+				</dict>
+				<dict>
+					<key>frequency</key>
+					<string>400000000</string>
+					<key>used_ns</key>
+					<integer>0</integer>
+					<key>used_ratio</key>
+					<real>0</real>
+				</dict>
+				<dict>
+					<key>frequency</key>
+					<string>350000000</string>
+					<key>used_ns</key>
+					<integer>0</integer>
+					<key>used_ratio</key>
+					<real>0</real>
+				</dict>
+				<dict>
+					<key>frequency</key>
+					<string>300000000</string>
+					<key>used_ns</key>
+					<integer>0</integer>
+					<key>used_ratio</key>
+					<real>0</real>
+				</dict>
+				<dict>
+					<key>frequency</key>
+					<string>250000000</string>
+					<key>used_ns</key>
+					<integer>0</integer>
+					<key>used_ratio</key>
+					<real>0</real>
+				</dict>
+				<dict>
+					<key>frequency</key>
+					<string>200000000</string>
+					<key>used_ns</key>
+					<integer>0</integer>
+					<key>used_ratio</key>
+					<real>0</real>
+				</dict>
+			</array>
+			<key>freq_ratio</key>
+			<real>0</real>
+			<key>name</key>
+			<string>IntelIG</string>
+		</dict>
+	</array>
+	<key>timestamp</key>
+	<date>2014-06-13T17:07:20Z</date>
+	<key>tasks</key>
+	<array>
+		<dict>
+			<key>idle_wakeups</key>
+			<integer>592</integer>
+			<key>cputime_ms_per_s</key>
+			<real>23.4812</real>
+			<key>cputime_userland_ratio</key>
+			<real>0</real>
+			<key>intr_wakeups</key>
+			<integer>687</integer>
+			<key>cputime_ms_per_s_reliable</key>
+			<true/>
+			<key>idle_wakeups_per_s</key>
+			<real>137.889</real>
+			<key>started_ns</key>
+			<integer>0</integer>
+			<key>cputime_ns</key>
+			<integer>100812002</integer>
+			<key>timer_wakeups</key>
+			<array>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>3.72673</real>
+					<key>interval_ns</key>
+					<integer>2000000</integer>
+					<key>wakeups</key>
+					<integer>16</integer>
+				</dict>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>5000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+			</array>
+			<key>intr_wakeups_per_s</key>
+			<real>160.016</real>
+			<key>pid</key>
+			<integer>0</integer>
+			<key>name</key>
+			<string>kernel_task</string>
+			<key>started_ns_reliable</key>
+			<false/>
+		</dict>
+		<dict>
+			<key>idle_wakeups</key>
+			<integer>141</integer>
+			<key>cputime_ms_per_s</key>
+			<real>24.2995</real>
+			<key>cputime_userland_ratio</key>
+			<real>0.6389860000000001</real>
+			<key>intr_wakeups</key>
+			<integer>145</integer>
+			<key>cputime_ms_per_s_reliable</key>
+			<true/>
+			<key>idle_wakeups_per_s</key>
+			<real>32.8418</real>
+			<key>started_ns</key>
+			<integer>1610501000</integer>
+			<key>cputime_ns</key>
+			<integer>104325487</integer>
+			<key>timer_wakeups</key>
+			<array>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>17.469</real>
+					<key>interval_ns</key>
+					<integer>2000000</integer>
+					<key>wakeups</key>
+					<integer>75</integer>
+				</dict>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>12.1119</real>
+					<key>interval_ns</key>
+					<integer>5000000</integer>
+					<key>wakeups</key>
+					<integer>52</integer>
+				</dict>
+			</array>
+			<key>intr_wakeups_per_s</key>
+			<real>33.7735</real>
+			<key>pid</key>
+			<integer>113</integer>
+			<key>name</key>
+			<string>WindowServer</string>
+			<key>started_ns_reliable</key>
+			<false/>
+		</dict>
+		<dict>
+			<key>idle_wakeups</key>
+			<integer>40</integer>
+			<key>cputime_ms_per_s</key>
+			<real>21.3695</real>
+			<key>cputime_userland_ratio</key>
+			<real>0.970651</real>
+			<key>intr_wakeups</key>
+			<integer>44</integer>
+			<key>cputime_ms_per_s_reliable</key>
+			<true/>
+			<key>idle_wakeups_per_s</key>
+			<real>9.31682</real>
+			<key>started_ns</key>
+			<integer>7367983585000</integer>
+			<key>cputime_ns</key>
+			<integer>91746051</integer>
+			<key>timer_wakeups</key>
+			<array>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>2000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>10.2485</real>
+					<key>interval_ns</key>
+					<integer>5000000</integer>
+					<key>wakeups</key>
+					<integer>44</integer>
+				</dict>
+			</array>
+			<key>intr_wakeups_per_s</key>
+			<real>10.2485</real>
+			<key>pid</key>
+			<integer>12745</integer>
+			<key>name</key>
+			<string>Python</string>
+			<key>started_ns_reliable</key>
+			<false/>
+		</dict>
+		<dict>
+			<key>idle_wakeups</key>
+			<integer>20</integer>
+			<key>cputime_ms_per_s</key>
+			<real>18.167</real>
+			<key>cputime_userland_ratio</key>
+			<real>0.914095</real>
+			<key>intr_wakeups</key>
+			<integer>22</integer>
+			<key>cputime_ms_per_s_reliable</key>
+			<true/>
+			<key>idle_wakeups_per_s</key>
+			<real>4.65841</real>
+			<key>started_ns</key>
+			<integer>43264491000</integer>
+			<key>cputime_ns</key>
+			<integer>77996425</integer>
+			<key>timer_wakeups</key>
+			<array>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>2000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0.23292</real>
+					<key>interval_ns</key>
+					<integer>5000000</integer>
+					<key>wakeups</key>
+					<integer>1</integer>
+				</dict>
+			</array>
+			<key>intr_wakeups_per_s</key>
+			<real>5.12425</real>
+			<key>pid</key>
+			<integer>1318</integer>
+			<key>name</key>
+			<string>Google Chrome He</string>
+			<key>started_ns_reliable</key>
+			<false/>
+		</dict>
+		<dict>
+			<key>idle_wakeups</key>
+			<integer>117</integer>
+			<key>cputime_ms_per_s</key>
+			<real>10.6337</real>
+			<key>cputime_userland_ratio</key>
+			<real>0.8333199999999999</real>
+			<key>intr_wakeups</key>
+			<integer>125</integer>
+			<key>cputime_ms_per_s_reliable</key>
+			<true/>
+			<key>idle_wakeups_per_s</key>
+			<real>27.2517</real>
+			<key>started_ns</key>
+			<integer>10444809000</integer>
+			<key>cputime_ns</key>
+			<integer>45653817</integer>
+			<key>timer_wakeups</key>
+			<array>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>2000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>5000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+			</array>
+			<key>intr_wakeups_per_s</key>
+			<real>29.1151</real>
+			<key>pid</key>
+			<integer>583</integer>
+			<key>name</key>
+			<string>Terminal</string>
+			<key>started_ns_reliable</key>
+			<false/>
+		</dict>
+		<dict>
+			<key>idle_wakeups</key>
+			<integer>65</integer>
+			<key>cputime_ms_per_s</key>
+			<real>11.7345</real>
+			<key>cputime_userland_ratio</key>
+			<real>0.88018</real>
+			<key>intr_wakeups</key>
+			<integer>75</integer>
+			<key>cputime_ms_per_s_reliable</key>
+			<true/>
+			<key>idle_wakeups_per_s</key>
+			<real>15.1398</real>
+			<key>started_ns</key>
+			<integer>7911274078000</integer>
+			<key>cputime_ns</key>
+			<integer>50379825</integer>
+			<key>timer_wakeups</key>
+			<array>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>17.469</real>
+					<key>interval_ns</key>
+					<integer>2000000</integer>
+					<key>wakeups</key>
+					<integer>75</integer>
+				</dict>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>5000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+			</array>
+			<key>intr_wakeups_per_s</key>
+			<real>17.469</real>
+			<key>pid</key>
+			<integer>13560</integer>
+			<key>name</key>
+			<string>Python</string>
+			<key>started_ns_reliable</key>
+			<false/>
+		</dict>
+		<dict>
+			<key>idle_wakeups</key>
+			<integer>47</integer>
+			<key>cputime_ms_per_s</key>
+			<real>8.64067</real>
+			<key>cputime_userland_ratio</key>
+			<real>0.584309</real>
+			<key>intr_wakeups</key>
+			<integer>50</integer>
+			<key>cputime_ms_per_s_reliable</key>
+			<true/>
+			<key>idle_wakeups_per_s</key>
+			<real>10.9473</real>
+			<key>started_ns</key>
+			<integer>10437471000</integer>
+			<key>cputime_ns</key>
+			<integer>37097106</integer>
+			<key>timer_wakeups</key>
+			<array>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0.931682</real>
+					<key>interval_ns</key>
+					<integer>2000000</integer>
+					<key>wakeups</key>
+					<integer>4</integer>
+				</dict>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>5000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+			</array>
+			<key>intr_wakeups_per_s</key>
+			<real>11.646</real>
+			<key>pid</key>
+			<integer>582</integer>
+			<key>name</key>
+			<string>Google Chrome</string>
+			<key>started_ns_reliable</key>
+			<false/>
+		</dict>
+		<dict>
+			<key>idle_wakeups</key>
+			<integer>14</integer>
+			<key>cputime_ms_per_s</key>
+			<real>7.9918</real>
+			<key>cputime_userland_ratio</key>
+			<real>0.919737</real>
+			<key>intr_wakeups</key>
+			<integer>17</integer>
+			<key>cputime_ms_per_s_reliable</key>
+			<true/>
+			<key>idle_wakeups_per_s</key>
+			<real>3.26089</real>
+			<key>started_ns</key>
+			<integer>10465929000</integer>
+			<key>cputime_ns</key>
+			<integer>34311297</integer>
+			<key>timer_wakeups</key>
+			<array>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>2000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>5000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+			</array>
+			<key>intr_wakeups_per_s</key>
+			<real>3.95965</real>
+			<key>pid</key>
+			<integer>587</integer>
+			<key>name</key>
+			<string>SystemUIServer</string>
+			<key>started_ns_reliable</key>
+			<false/>
+		</dict>
+		<dict>
+			<key>idle_wakeups</key>
+			<integer>6</integer>
+			<key>cputime_ms_per_s</key>
+			<real>6.42884</real>
+			<key>cputime_userland_ratio</key>
+			<real>0.582192</real>
+			<key>intr_wakeups</key>
+			<integer>7</integer>
+			<key>cputime_ms_per_s_reliable</key>
+			<true/>
+			<key>idle_wakeups_per_s</key>
+			<real>1.39752</real>
+			<key>started_ns</key>
+			<integer>639336000</integer>
+			<key>cputime_ns</key>
+			<integer>27601023</integer>
+			<key>timer_wakeups</key>
+			<array>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>2000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>5000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+			</array>
+			<key>intr_wakeups_per_s</key>
+			<real>1.63044</real>
+			<key>pid</key>
+			<integer>25</integer>
+			<key>name</key>
+			<string>syslogd</string>
+			<key>started_ns_reliable</key>
+			<false/>
+		</dict>
+		<dict>
+			<key>idle_wakeups</key>
+			<integer>45</integer>
+			<key>cputime_ms_per_s</key>
+			<real>0.681478</real>
+			<key>cputime_userland_ratio</key>
+			<real>0.365057</real>
+			<key>intr_wakeups</key>
+			<integer>49</integer>
+			<key>cputime_ms_per_s_reliable</key>
+			<true/>
+			<key>idle_wakeups_per_s</key>
+			<real>10.4814</real>
+			<key>started_ns</key>
+			<integer>49189572000</integer>
+			<key>cputime_ns</key>
+			<integer>2925797</integer>
+			<key>timer_wakeups</key>
+			<array>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>2000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>5000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+			</array>
+			<key>intr_wakeups_per_s</key>
+			<real>11.4131</real>
+			<key>pid</key>
+			<integer>1551</integer>
+			<key>name</key>
+			<string>GoogleTalkPlugin</string>
+			<key>started_ns_reliable</key>
+			<false/>
+		</dict>
+		<dict>
+			<key>idle_wakeups</key>
+			<integer>12</integer>
+			<key>cputime_ms_per_s</key>
+			<real>0.399903</real>
+			<key>cputime_userland_ratio</key>
+			<real>0.489519</real>
+			<key>intr_wakeups</key>
+			<integer>15</integer>
+			<key>cputime_ms_per_s_reliable</key>
+			<true/>
+			<key>idle_wakeups_per_s</key>
+			<real>2.79504</real>
+			<key>started_ns</key>
+			<integer>1881153000</integer>
+			<key>cputime_ns</key>
+			<integer>1716908</integer>
+			<key>timer_wakeups</key>
+			<array>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>2000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>5000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+			</array>
+			<key>intr_wakeups_per_s</key>
+			<real>3.49381</real>
+			<key>pid</key>
+			<integer>145</integer>
+			<key>name</key>
+			<string>gagent</string>
+			<key>started_ns_reliable</key>
+			<false/>
+		</dict>
+		<dict>
+			<key>idle_wakeups</key>
+			<integer>5</integer>
+			<key>cputime_ms_per_s</key>
+			<real>0.475033</real>
+			<key>cputime_userland_ratio</key>
+			<real>0.725976</real>
+			<key>intr_wakeups</key>
+			<integer>6</integer>
+			<key>cputime_ms_per_s_reliable</key>
+			<true/>
+			<key>idle_wakeups_per_s</key>
+			<real>1.1646</real>
+			<key>started_ns</key>
+			<integer>8483361342000</integer>
+			<key>cputime_ns</key>
+			<integer>2039463</integer>
+			<key>timer_wakeups</key>
+			<array>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>2000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>5000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+			</array>
+			<key>intr_wakeups_per_s</key>
+			<real>1.39752</real>
+			<key>pid</key>
+			<integer>14091</integer>
+			<key>name</key>
+			<string>Google Chrome He</string>
+			<key>started_ns_reliable</key>
+			<false/>
+		</dict>
+		<dict>
+			<key>idle_wakeups</key>
+			<integer>0</integer>
+			<key>cputime_ms_per_s</key>
+			<real>0.679799</real>
+			<key>cputime_userland_ratio</key>
+			<real>0.256151</real>
+			<key>intr_wakeups</key>
+			<integer>0</integer>
+			<key>cputime_ms_per_s_reliable</key>
+			<true/>
+			<key>idle_wakeups_per_s</key>
+			<real>0</real>
+			<key>started_ns</key>
+			<integer>8917493171000</integer>
+			<key>cputime_ns</key>
+			<integer>2918588</integer>
+			<key>timer_wakeups</key>
+			<array>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>2000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>5000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+			</array>
+			<key>intr_wakeups_per_s</key>
+			<real>0</real>
+			<key>pid</key>
+			<integer>14209</integer>
+			<key>name</key>
+			<string>powermetrics</string>
+			<key>started_ns_reliable</key>
+			<false/>
+		</dict>
+		<dict>
+			<key>idle_wakeups</key>
+			<integer>2</integer>
+			<key>cputime_ms_per_s</key>
+			<real>0.211558</real>
+			<key>cputime_userland_ratio</key>
+			<real>0.6101799999999999</real>
+			<key>intr_wakeups</key>
+			<integer>2</integer>
+			<key>cputime_ms_per_s_reliable</key>
+			<true/>
+			<key>idle_wakeups_per_s</key>
+			<real>0.465841</real>
+			<key>started_ns</key>
+			<integer>1493715000</integer>
+			<key>cputime_ns</key>
+			<integer>908286</integer>
+			<key>timer_wakeups</key>
+			<array>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>2000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>5000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+			</array>
+			<key>intr_wakeups_per_s</key>
+			<real>0.465841</real>
+			<key>pid</key>
+			<integer>95</integer>
+			<key>name</key>
+			<string>Python</string>
+			<key>started_ns_reliable</key>
+			<false/>
+		</dict>
+		<dict>
+			<key>idle_wakeups</key>
+			<integer>2</integer>
+			<key>cputime_ms_per_s</key>
+			<real>0.181734</real>
+			<key>cputime_userland_ratio</key>
+			<real>0.677046</real>
+			<key>intr_wakeups</key>
+			<integer>3</integer>
+			<key>cputime_ms_per_s_reliable</key>
+			<true/>
+			<key>idle_wakeups_per_s</key>
+			<real>0.465841</real>
+			<key>started_ns</key>
+			<integer>12546437000</integer>
+			<key>cputime_ns</key>
+			<integer>780239</integer>
+			<key>timer_wakeups</key>
+			<array>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>2000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>5000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+			</array>
+			<key>intr_wakeups_per_s</key>
+			<real>0.698761</real>
+			<key>pid</key>
+			<integer>695</integer>
+			<key>name</key>
+			<string>Google Chrome He</string>
+			<key>started_ns_reliable</key>
+			<false/>
+		</dict>
+		<dict>
+			<key>idle_wakeups</key>
+			<integer>3</integer>
+			<key>cputime_ms_per_s</key>
+			<real>0.117981</real>
+			<key>cputime_userland_ratio</key>
+			<real>0.252391</real>
+			<key>intr_wakeups</key>
+			<integer>4</integer>
+			<key>cputime_ms_per_s_reliable</key>
+			<true/>
+			<key>idle_wakeups_per_s</key>
+			<real>0.698761</real>
+			<key>started_ns</key>
+			<integer>1676947000</integer>
+			<key>cputime_ns</key>
+			<integer>506528</integer>
+			<key>timer_wakeups</key>
+			<array>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>2000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>5000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+			</array>
+			<key>intr_wakeups_per_s</key>
+			<real>0.931682</real>
+			<key>pid</key>
+			<integer>132</integer>
+			<key>name</key>
+			<string>pacemaker</string>
+			<key>started_ns_reliable</key>
+			<false/>
+		</dict>
+		<dict>
+			<key>idle_wakeups</key>
+			<integer>2</integer>
+			<key>cputime_ms_per_s</key>
+			<real>0.125953</real>
+			<key>cputime_userland_ratio</key>
+			<real>0.55472</real>
+			<key>intr_wakeups</key>
+			<integer>2</integer>
+			<key>cputime_ms_per_s_reliable</key>
+			<true/>
+			<key>idle_wakeups_per_s</key>
+			<real>0.465841</real>
+			<key>started_ns</key>
+			<integer>11624896000</integer>
+			<key>cputime_ns</key>
+			<integer>540756</integer>
+			<key>timer_wakeups</key>
+			<array>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>2000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>5000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+			</array>
+			<key>intr_wakeups_per_s</key>
+			<real>0.465841</real>
+			<key>pid</key>
+			<integer>654</integer>
+			<key>name</key>
+			<string>Google Chrome He</string>
+			<key>started_ns_reliable</key>
+			<false/>
+		</dict>
+		<dict>
+			<key>idle_wakeups</key>
+			<integer>2</integer>
+			<key>cputime_ms_per_s</key>
+			<real>0.108103</real>
+			<key>cputime_userland_ratio</key>
+			<real>0.648833</real>
+			<key>intr_wakeups</key>
+			<integer>2</integer>
+			<key>cputime_ms_per_s_reliable</key>
+			<true/>
+			<key>idle_wakeups_per_s</key>
+			<real>0.465841</real>
+			<key>started_ns</key>
+			<integer>6838183543000</integer>
+			<key>cputime_ns</key>
+			<integer>464121</integer>
+			<key>timer_wakeups</key>
+			<array>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>2000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>5000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+			</array>
+			<key>intr_wakeups_per_s</key>
+			<real>0.465841</real>
+			<key>pid</key>
+			<integer>12260</integer>
+			<key>name</key>
+			<string>Google Chrome He</string>
+			<key>started_ns_reliable</key>
+			<false/>
+		</dict>
+		<dict>
+			<key>idle_wakeups</key>
+			<integer>1</integer>
+			<key>cputime_ms_per_s</key>
+			<real>0.120981</real>
+			<key>cputime_userland_ratio</key>
+			<real>0.682463</real>
+			<key>intr_wakeups</key>
+			<integer>3</integer>
+			<key>cputime_ms_per_s_reliable</key>
+			<true/>
+			<key>idle_wakeups_per_s</key>
+			<real>0.23292</real>
+			<key>started_ns</key>
+			<integer>1491205000</integer>
+			<key>cputime_ns</key>
+			<integer>519408</integer>
+			<key>timer_wakeups</key>
+			<array>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>2000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>5000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+			</array>
+			<key>intr_wakeups_per_s</key>
+			<real>0.698761</real>
+			<key>pid</key>
+			<integer>79</integer>
+			<key>name</key>
+			<string>fseventsd</string>
+			<key>started_ns_reliable</key>
+			<false/>
+		</dict>
+		<dict>
+			<key>idle_wakeups</key>
+			<integer>2</integer>
+			<key>cputime_ms_per_s</key>
+			<real>0.0486729</real>
+			<key>cputime_userland_ratio</key>
+			<real>0.254202</real>
+			<key>intr_wakeups</key>
+			<integer>2</integer>
+			<key>cputime_ms_per_s_reliable</key>
+			<true/>
+			<key>idle_wakeups_per_s</key>
+			<real>0.465841</real>
+			<key>started_ns</key>
+			<integer>1622879000</integer>
+			<key>cputime_ns</key>
+			<integer>208968</integer>
+			<key>timer_wakeups</key>
+			<array>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>2000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>5000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+			</array>
+			<key>intr_wakeups_per_s</key>
+			<real>0.465841</real>
+			<key>pid</key>
+			<integer>114</integer>
+			<key>name</key>
+			<string>ocspd</string>
+			<key>started_ns_reliable</key>
+			<false/>
+		</dict>
+		<dict>
+			<key>idle_wakeups</key>
+			<integer>1</integer>
+			<key>cputime_ms_per_s</key>
+			<real>0.08862200000000001</real>
+			<key>cputime_userland_ratio</key>
+			<real>0.635134</real>
+			<key>intr_wakeups</key>
+			<integer>2</integer>
+			<key>cputime_ms_per_s_reliable</key>
+			<true/>
+			<key>idle_wakeups_per_s</key>
+			<real>0.23292</real>
+			<key>started_ns</key>
+			<integer>12714103000</integer>
+			<key>cputime_ns</key>
+			<integer>380482</integer>
+			<key>timer_wakeups</key>
+			<array>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>2000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>5000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+			</array>
+			<key>intr_wakeups_per_s</key>
+			<real>0.465841</real>
+			<key>pid</key>
+			<integer>698</integer>
+			<key>name</key>
+			<string>Google Chrome He</string>
+			<key>started_ns_reliable</key>
+			<false/>
+		</dict>
+		<dict>
+			<key>idle_wakeups</key>
+			<integer>2</integer>
+			<key>cputime_ms_per_s</key>
+			<real>0.0410855</real>
+			<key>cputime_userland_ratio</key>
+			<real>0.189299</real>
+			<key>intr_wakeups</key>
+			<integer>2</integer>
+			<key>cputime_ms_per_s_reliable</key>
+			<true/>
+			<key>idle_wakeups_per_s</key>
+			<real>0.465841</real>
+			<key>started_ns</key>
+			<integer>1492092000</integer>
+			<key>cputime_ns</key>
+			<integer>176393</integer>
+			<key>timer_wakeups</key>
+			<array>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>2000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>5000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+			</array>
+			<key>intr_wakeups_per_s</key>
+			<real>0.465841</real>
+			<key>pid</key>
+			<integer>85</integer>
+			<key>name</key>
+			<string>launchservicesd</string>
+			<key>started_ns_reliable</key>
+			<false/>
+		</dict>
+		<dict>
+			<key>idle_wakeups</key>
+			<integer>0</integer>
+			<key>cputime_ms_per_s</key>
+			<real>0.110853</real>
+			<key>cputime_userland_ratio</key>
+			<real>0.279103</real>
+			<key>intr_wakeups</key>
+			<integer>0</integer>
+			<key>cputime_ms_per_s_reliable</key>
+			<true/>
+			<key>idle_wakeups_per_s</key>
+			<real>0</real>
+			<key>started_ns</key>
+			<integer>624120000</integer>
+			<key>cputime_ns</key>
+			<integer>475925</integer>
+			<key>timer_wakeups</key>
+			<array>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>2000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>5000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+			</array>
+			<key>intr_wakeups_per_s</key>
+			<real>0</real>
+			<key>pid</key>
+			<integer>20</integer>
+			<key>name</key>
+			<string>notifyd</string>
+			<key>started_ns_reliable</key>
+			<false/>
+		</dict>
+		<dict>
+			<key>idle_wakeups</key>
+			<integer>1</integer>
+			<key>cputime_ms_per_s</key>
+			<real>0.0619692</real>
+			<key>cputime_userland_ratio</key>
+			<real>0.401646</real>
+			<key>intr_wakeups</key>
+			<integer>2</integer>
+			<key>cputime_ms_per_s_reliable</key>
+			<true/>
+			<key>idle_wakeups_per_s</key>
+			<real>0.23292</real>
+			<key>started_ns</key>
+			<integer>1489680000</integer>
+			<key>cputime_ns</key>
+			<integer>266053</integer>
+			<key>timer_wakeups</key>
+			<array>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>2000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>5000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+			</array>
+			<key>intr_wakeups_per_s</key>
+			<real>0.465841</real>
+			<key>pid</key>
+			<integer>69</integer>
+			<key>name</key>
+			<string>mds</string>
+			<key>started_ns_reliable</key>
+			<false/>
+		</dict>
+		<dict>
+			<key>idle_wakeups</key>
+			<integer>1</integer>
+			<key>cputime_ms_per_s</key>
+			<real>0.0550766</real>
+			<key>cputime_userland_ratio</key>
+			<real>0.362267</real>
+			<key>intr_wakeups</key>
+			<integer>1</integer>
+			<key>cputime_ms_per_s_reliable</key>
+			<true/>
+			<key>idle_wakeups_per_s</key>
+			<real>0.23292</real>
+			<key>started_ns</key>
+			<integer>2992466000</integer>
+			<key>cputime_ns</key>
+			<integer>236461</integer>
+			<key>timer_wakeups</key>
+			<array>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>2000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>5000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+			</array>
+			<key>intr_wakeups_per_s</key>
+			<real>0.23292</real>
+			<key>pid</key>
+			<integer>202</integer>
+			<key>name</key>
+			<string>CVMServer</string>
+			<key>started_ns_reliable</key>
+			<false/>
+		</dict>
+		<dict>
+			<key>idle_wakeups</key>
+			<integer>0</integer>
+			<key>cputime_ms_per_s</key>
+			<real>0.0873813</real>
+			<key>cputime_userland_ratio</key>
+			<real>0.842692</real>
+			<key>intr_wakeups</key>
+			<integer>0</integer>
+			<key>cputime_ms_per_s_reliable</key>
+			<true/>
+			<key>idle_wakeups_per_s</key>
+			<real>0</real>
+			<key>started_ns</key>
+			<integer>7393764015000</integer>
+			<key>cputime_ns</key>
+			<integer>375155</integer>
+			<key>timer_wakeups</key>
+			<array>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>2000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>5000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+			</array>
+			<key>intr_wakeups_per_s</key>
+			<real>0</real>
+			<key>pid</key>
+			<integer>13253</integer>
+			<key>name</key>
+			<string>syslog</string>
+			<key>started_ns_reliable</key>
+			<false/>
+		</dict>
+		<dict>
+			<key>idle_wakeups</key>
+			<integer>1</integer>
+			<key>cputime_ms_per_s</key>
+			<real>0.010856</real>
+			<key>cputime_userland_ratio</key>
+			<real>0.187307</real>
+			<key>intr_wakeups</key>
+			<integer>1</integer>
+			<key>cputime_ms_per_s_reliable</key>
+			<true/>
+			<key>idle_wakeups_per_s</key>
+			<real>0.23292</real>
+			<key>started_ns</key>
+			<integer>10349417000</integer>
+			<key>cputime_ns</key>
+			<integer>46608</integer>
+			<key>timer_wakeups</key>
+			<array>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>2000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>5000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+			</array>
+			<key>intr_wakeups_per_s</key>
+			<real>0.23292</real>
+			<key>pid</key>
+			<integer>573</integer>
+			<key>name</key>
+			<string>cfprefsd</string>
+			<key>started_ns_reliable</key>
+			<false/>
+		</dict>
+		<dict>
+			<key>idle_wakeups</key>
+			<integer>1</integer>
+			<key>cputime_ms_per_s</key>
+			<real>0.00967575</real>
+			<key>cputime_userland_ratio</key>
+			<real>0.216677</real>
+			<key>intr_wakeups</key>
+			<integer>1</integer>
+			<key>cputime_ms_per_s_reliable</key>
+			<true/>
+			<key>idle_wakeups_per_s</key>
+			<real>0.23292</real>
+			<key>started_ns</key>
+			<integer>865104000</integer>
+			<key>cputime_ns</key>
+			<integer>41541</integer>
+			<key>timer_wakeups</key>
+			<array>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>2000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>5000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+			</array>
+			<key>intr_wakeups_per_s</key>
+			<real>0.23292</real>
+			<key>pid</key>
+			<integer>30</integer>
+			<key>name</key>
+			<string>cfprefsd</string>
+			<key>started_ns_reliable</key>
+			<false/>
+		</dict>
+		<dict>
+			<key>idle_wakeups</key>
+			<integer>0</integer>
+			<key>cputime_ms_per_s</key>
+			<real>0.039699</real>
+			<key>cputime_userland_ratio</key>
+			<real>0.240431</real>
+			<key>intr_wakeups</key>
+			<integer>0</integer>
+			<key>cputime_ms_per_s_reliable</key>
+			<true/>
+			<key>idle_wakeups_per_s</key>
+			<real>0</real>
+			<key>started_ns</key>
+			<integer>603081000</integer>
+			<key>cputime_ns</key>
+			<integer>170440</integer>
+			<key>timer_wakeups</key>
+			<array>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>2000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>5000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+			</array>
+			<key>intr_wakeups_per_s</key>
+			<real>0</real>
+			<key>pid</key>
+			<integer>17</integer>
+			<key>name</key>
+			<string>UserEventAgent</string>
+			<key>started_ns_reliable</key>
+			<false/>
+		</dict>
+		<dict>
+			<key>idle_wakeups</key>
+			<integer>0</integer>
+			<key>cputime_ms_per_s</key>
+			<real>0.0260172</real>
+			<key>cputime_userland_ratio</key>
+			<real>0.260627</real>
+			<key>intr_wakeups</key>
+			<integer>0</integer>
+			<key>cputime_ms_per_s_reliable</key>
+			<true/>
+			<key>idle_wakeups_per_s</key>
+			<real>0</real>
+			<key>started_ns</key>
+			<integer>10467715000</integer>
+			<key>cputime_ns</key>
+			<integer>111700</integer>
+			<key>timer_wakeups</key>
+			<array>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>2000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+				<dict>
+					<key>wakeups_per_s</key>
+					<real>0</real>
+					<key>interval_ns</key>
+					<integer>5000000</integer>
+					<key>wakeups</key>
+					<integer>0</integer>
+				</dict>
+			</array>
+			<key>intr_wakeups_per_s</key>
+			<real>0</real>
+			<key>pid</key>
+			<integer>588</integer>
+			<key>name</key>
+			<string>Finder</string>
+			<key>started_ns_reliable</key>
+			<false/>
+		</dict>
+	</array>
+	<key>hw_model</key>
+	<string>MacBookPro11,3</string>
+	<key>interrupts</key>
+	<array>
+		<dict>
+			<key>cpu</key>
+			<integer>0</integer>
+			<key>vectors</key>
+			<array>
+				<dict>
+					<key>vector</key>
+					<integer>116</integer>
+					<key>events</key>
+					<integer>167</integer>
+					<key>events_per_s</key>
+					<real>38.8753</real>
+					<key>name</key>
+					<string>XHC1</string>
+				</dict>
+				<dict>
+					<key>vector</key>
+					<integer>119</integer>
+					<key>events</key>
+					<integer>463</integer>
+					<key>events_per_s</key>
+					<real>107.78</real>
+					<key>name</key>
+					<string>GFX0</string>
+				</dict>
+				<dict>
+					<key>vector</key>
+					<integer>122</integer>
+					<key>events</key>
+					<integer>65</integer>
+					<key>events_per_s</key>
+					<real>15.1311</real>
+					<key>name</key>
+					<string>ARPT</string>
+				</dict>
+				<dict>
+					<key>vector</key>
+					<integer>221</integer>
+					<key>events</key>
+					<integer>635</integer>
+					<key>events_per_s</key>
+					<real>147.819</real>
+					<key>name</key>
+					<string>TMR</string>
+				</dict>
+				<dict>
+					<key>vector</key>
+					<integer>222</integer>
+					<key>events</key>
+					<integer>12</integer>
+					<key>events_per_s</key>
+					<real>2.79343</real>
+					<key>name</key>
+					<string>IPI</string>
+				</dict>
+			</array>
+		</dict>
+		<dict>
+			<key>cpu</key>
+			<integer>1</integer>
+			<key>vectors</key>
+			<array>
+				<dict>
+					<key>vector</key>
+					<integer>221</integer>
+					<key>events</key>
+					<integer>3</integer>
+					<key>events_per_s</key>
+					<real>0.698358</real>
+					<key>name</key>
+					<string>TMR</string>
+				</dict>
+				<dict>
+					<key>vector</key>
+					<integer>222</integer>
+					<key>events</key>
+					<integer>3</integer>
+					<key>events_per_s</key>
+					<real>0.698358</real>
+					<key>name</key>
+					<string>IPI</string>
+				</dict>
+			</array>
+		</dict>
+		<dict>
+			<key>cpu</key>
+			<integer>2</integer>
+			<key>vectors</key>
+			<array>
+				<dict>
+					<key>vector</key>
+					<integer>221</integer>
+					<key>events</key>
+					<integer>92</integer>
+					<key>events_per_s</key>
+					<real>21.4163</real>
+					<key>name</key>
+					<string>TMR</string>
+				</dict>
+				<dict>
+					<key>vector</key>
+					<integer>222</integer>
+					<key>events</key>
+					<integer>16</integer>
+					<key>events_per_s</key>
+					<real>3.72458</real>
+					<key>name</key>
+					<string>IPI</string>
+				</dict>
+			</array>
+		</dict>
+		<dict>
+			<key>cpu</key>
+			<integer>3</integer>
+			<key>vectors</key>
+			<array>
+				<dict>
+					<key>vector</key>
+					<integer>222</integer>
+					<key>events</key>
+					<integer>3</integer>
+					<key>events_per_s</key>
+					<real>0.698358</real>
+					<key>name</key>
+					<string>IPI</string>
+				</dict>
+			</array>
+		</dict>
+		<dict>
+			<key>cpu</key>
+			<integer>4</integer>
+			<key>vectors</key>
+			<array>
+				<dict>
+					<key>vector</key>
+					<integer>221</integer>
+					<key>events</key>
+					<integer>131</integer>
+					<key>events_per_s</key>
+					<real>30.495</real>
+					<key>name</key>
+					<string>TMR</string>
+				</dict>
+				<dict>
+					<key>vector</key>
+					<integer>222</integer>
+					<key>events</key>
+					<integer>20</integer>
+					<key>events_per_s</key>
+					<real>4.65572</real>
+					<key>name</key>
+					<string>IPI</string>
+				</dict>
+			</array>
+		</dict>
+		<dict>
+			<key>cpu</key>
+			<integer>5</integer>
+			<key>vectors</key>
+			<array>
+				<dict>
+					<key>vector</key>
+					<integer>221</integer>
+					<key>events</key>
+					<integer>2</integer>
+					<key>events_per_s</key>
+					<real>0.465572</real>
+					<key>name</key>
+					<string>TMR</string>
+				</dict>
+				<dict>
+					<key>vector</key>
+					<integer>222</integer>
+					<key>events</key>
+					<integer>3</integer>
+					<key>events_per_s</key>
+					<real>0.698358</real>
+					<key>name</key>
+					<string>IPI</string>
+				</dict>
+			</array>
+		</dict>
+		<dict>
+			<key>cpu</key>
+			<integer>6</integer>
+			<key>vectors</key>
+			<array>
+				<dict>
+					<key>vector</key>
+					<integer>221</integer>
+					<key>events</key>
+					<integer>111</integer>
+					<key>events_per_s</key>
+					<real>25.8393</real>
+					<key>name</key>
+					<string>TMR</string>
+				</dict>
+				<dict>
+					<key>vector</key>
+					<integer>222</integer>
+					<key>events</key>
+					<integer>15</integer>
+					<key>events_per_s</key>
+					<real>3.49179</real>
+					<key>name</key>
+					<string>IPI</string>
+				</dict>
+			</array>
+		</dict>
+		<dict>
+			<key>cpu</key>
+			<integer>7</integer>
+			<key>vectors</key>
+			<array>
+				<dict>
+					<key>vector</key>
+					<integer>222</integer>
+					<key>events</key>
+					<integer>2</integer>
+					<key>events_per_s</key>
+					<real>0.465572</real>
+					<key>name</key>
+					<string>IPI</string>
+				</dict>
+			</array>
+		</dict>
+	</array>
+	<key>backlight</key>
+	<dict>
+		<key>min</key>
+		<integer>0</integer>
+		<key>value</key>
+		<integer>687</integer>
+		<key>max</key>
+		<integer>1024</integer>
+	</dict>
+	<key>kern_osversion</key>
+	<string>13D65</string>
+	<key>all_tasks</key>
+	<dict>
+		<key>idle_wakeups</key>
+		<integer>1125</integer>
+		<key>cputime_ms_per_s</key>
+		<real>136.35</real>
+		<key>cputime_userland_ratio</key>
+		<real>0.65911</real>
+		<key>intr_wakeups</key>
+		<integer>1269</integer>
+		<key>cputime_ms_per_s_reliable</key>
+		<true/>
+		<key>idle_wakeups_per_s</key>
+		<real>261.884</real>
+		<key>started_ns</key>
+		<integer>0</integer>
+		<key>cputime_ns</key>
+		<integer>585732853</integer>
+		<key>timer_wakeups</key>
+		<array>
+			<dict>
+				<key>wakeups_per_s</key>
+				<real>39.5736</real>
+				<key>interval_ns</key>
+				<integer>2000000</integer>
+				<key>wakeups</key>
+				<integer>170</integer>
+			</dict>
+			<dict>
+				<key>wakeups_per_s</key>
+				<real>22.5803</real>
+				<key>interval_ns</key>
+				<integer>5000000</integer>
+				<key>wakeups</key>
+				<integer>97</integer>
+			</dict>
+		</array>
+		<key>intr_wakeups_per_s</key>
+		<real>295.406</real>
+		<key>pid</key>
+		<integer>-2</integer>
+		<key>name</key>
+		<string>ALL_TASKS</string>
+		<key>started_ns_reliable</key>
+		<false/>
+	</dict>
+	<key>disk</key>
+	<dict>
+		<key>rbytes_diff</key>
+		<integer>0</integer>
+		<key>rops_per_s</key>
+		<real>0</real>
+		<key>rops_diff</key>
+		<integer>0</integer>
+		<key>wops_diff</key>
+		<integer>0</integer>
+		<key>wops_per_s</key>
+		<real>0</real>
+		<key>wbytes_per_s</key>
+		<real>0</real>
+		<key>rbytes_per_s</key>
+		<real>0</real>
+		<key>wbytes_diff</key>
+		<integer>0</integer>
+	</dict>
+	<key>processor</key>
+	<dict>
+		<key>freq_hz</key>
+		<real>1668120000</real>
+		<key>package_joules</key>
+		<real>13.0469</real>
+		<key>packages</key>
+		<array>
+			<dict>
+				<key>c_state_ns</key>
+				<integer>3723350796</integer>
+				<key>c_state_ratio</key>
+				<real>0.866745</real>
+				<key>package</key>
+				<integer>0</integer>
+				<key>c_states</key>
+				<array>
+					<dict>
+						<key>used_ns</key>
+						<integer>191313723</integer>
+						<key>used_ratio</key>
+						<real>0.0445352</real>
+						<key>name</key>
+						<string>C2</string>
+					</dict>
+					<dict>
+						<key>used_ns</key>
+						<integer>743617321</integer>
+						<key>used_ratio</key>
+						<real>0.173104</real>
+						<key>name</key>
+						<string>C3</string>
+					</dict>
+					<dict>
+						<key>used_ns</key>
+						<integer>2788419751</integer>
+						<key>used_ratio</key>
+						<real>0.649106</real>
+						<key>name</key>
+						<string>C6</string>
+					</dict>
+					<dict>
+						<key>used_ns</key>
+						<integer>0</integer>
+						<key>used_ratio</key>
+						<real>0</real>
+						<key>name</key>
+						<string>C7</string>
+					</dict>
+				</array>
+				<key>cores</key>
+				<array>
+					<dict>
+						<key>c_state_ns</key>
+						<integer>3942072317</integer>
+						<key>c_state_ratio</key>
+						<real>0.91766</real>
+						<key>core</key>
+						<integer>0</integer>
+						<key>c_states</key>
+						<array>
+							<dict>
+								<key>used_ns</key>
+								<integer>9035527</integer>
+								<key>used_ratio</key>
+								<real>0.00210335</real>
+								<key>name</key>
+								<string>C3</string>
+							</dict>
+							<dict>
+								<key>used_ns</key>
+								<integer>0</integer>
+								<key>used_ratio</key>
+								<real>0</real>
+								<key>name</key>
+								<string>C6</string>
+							</dict>
+							<dict>
+								<key>used_ns</key>
+								<integer>3933036790</integer>
+								<key>used_ratio</key>
+								<real>0.915557</real>
+								<key>name</key>
+								<string>C7</string>
+							</dict>
+						</array>
+						<key>cpus</key>
+						<array>
+							<dict>
+								<key>duty_cycles</key>
+								<array>
+									<dict>
+										<key>active_count</key>
+										<integer>139</integer>
+										<key>interval_ns</key>
+										<integer>16000</integer>
+										<key>active_per_s</key>
+										<real>32.3573</real>
+										<key>idle_count</key>
+										<integer>25</integer>
+										<key>idle_per_s</key>
+										<real>32.3573</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>16</integer>
+										<key>interval_ns</key>
+										<integer>32000</integer>
+										<key>active_per_s</key>
+										<real>3.72458</real>
+										<key>idle_count</key>
+										<integer>47</integer>
+										<key>idle_per_s</key>
+										<real>3.72458</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>59</integer>
+										<key>interval_ns</key>
+										<integer>64000</integer>
+										<key>active_per_s</key>
+										<real>13.7344</real>
+										<key>idle_count</key>
+										<integer>75</integer>
+										<key>idle_per_s</key>
+										<real>13.7344</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>263</integer>
+										<key>interval_ns</key>
+										<integer>128000</integer>
+										<key>active_per_s</key>
+										<real>61.2228</real>
+										<key>idle_count</key>
+										<integer>171</integer>
+										<key>idle_per_s</key>
+										<real>61.2228</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>356</integer>
+										<key>interval_ns</key>
+										<integer>256000</integer>
+										<key>active_per_s</key>
+										<real>82.8719</real>
+										<key>idle_count</key>
+										<integer>63</integer>
+										<key>idle_per_s</key>
+										<real>82.8719</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>266</integer>
+										<key>interval_ns</key>
+										<integer>512000</integer>
+										<key>active_per_s</key>
+										<real>61.9211</real>
+										<key>idle_count</key>
+										<integer>89</integer>
+										<key>idle_per_s</key>
+										<real>61.9211</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>78</integer>
+										<key>interval_ns</key>
+										<integer>1024000</integer>
+										<key>active_per_s</key>
+										<real>18.1573</real>
+										<key>idle_count</key>
+										<integer>154</integer>
+										<key>idle_per_s</key>
+										<real>18.1573</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>10</integer>
+										<key>interval_ns</key>
+										<integer>2048000</integer>
+										<key>active_per_s</key>
+										<real>2.32786</real>
+										<key>idle_count</key>
+										<integer>130</integer>
+										<key>idle_per_s</key>
+										<real>2.32786</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>4</integer>
+										<key>interval_ns</key>
+										<integer>4096000</integer>
+										<key>active_per_s</key>
+										<real>0.931145</real>
+										<key>idle_count</key>
+										<integer>140</integer>
+										<key>idle_per_s</key>
+										<real>0.931145</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>7</integer>
+										<key>interval_ns</key>
+										<integer>8192000</integer>
+										<key>active_per_s</key>
+										<real>1.6295</real>
+										<key>idle_count</key>
+										<integer>134</integer>
+										<key>idle_per_s</key>
+										<real>1.6295</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>0</integer>
+										<key>interval_ns</key>
+										<integer>16384000</integer>
+										<key>active_per_s</key>
+										<real>0</real>
+										<key>idle_count</key>
+										<integer>119</integer>
+										<key>idle_per_s</key>
+										<real>0</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>0</integer>
+										<key>interval_ns</key>
+										<integer>32768000</integer>
+										<key>active_per_s</key>
+										<real>0</real>
+										<key>idle_count</key>
+										<integer>50</integer>
+										<key>idle_per_s</key>
+										<real>0</real>
+									</dict>
+								</array>
+								<key>cpu</key>
+								<integer>0</integer>
+								<key>freq_hz</key>
+								<real>1478510000</real>
+								<key>freq_ratio</key>
+								<real>0.569975</real>
+							</dict>
+							<dict>
+								<key>duty_cycles</key>
+								<array>
+									<dict>
+										<key>active_count</key>
+										<integer>11</integer>
+										<key>interval_ns</key>
+										<integer>16000</integer>
+										<key>active_per_s</key>
+										<real>2.56065</real>
+										<key>idle_count</key>
+										<integer>1</integer>
+										<key>idle_per_s</key>
+										<real>2.56065</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>5</integer>
+										<key>interval_ns</key>
+										<integer>32000</integer>
+										<key>active_per_s</key>
+										<real>1.16393</real>
+										<key>idle_count</key>
+										<integer>0</integer>
+										<key>idle_per_s</key>
+										<real>1.16393</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>4</integer>
+										<key>interval_ns</key>
+										<integer>64000</integer>
+										<key>active_per_s</key>
+										<real>0.931145</real>
+										<key>idle_count</key>
+										<integer>2</integer>
+										<key>idle_per_s</key>
+										<real>0.931145</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>3</integer>
+										<key>interval_ns</key>
+										<integer>128000</integer>
+										<key>active_per_s</key>
+										<real>0.698358</real>
+										<key>idle_count</key>
+										<integer>1</integer>
+										<key>idle_per_s</key>
+										<real>0.698358</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>4</integer>
+										<key>interval_ns</key>
+										<integer>256000</integer>
+										<key>active_per_s</key>
+										<real>0.931145</real>
+										<key>idle_count</key>
+										<integer>1</integer>
+										<key>idle_per_s</key>
+										<real>0.931145</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>0</integer>
+										<key>interval_ns</key>
+										<integer>512000</integer>
+										<key>active_per_s</key>
+										<real>0</real>
+										<key>idle_count</key>
+										<integer>0</integer>
+										<key>idle_per_s</key>
+										<real>0</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>0</integer>
+										<key>interval_ns</key>
+										<integer>1024000</integer>
+										<key>active_per_s</key>
+										<real>0</real>
+										<key>idle_count</key>
+										<integer>1</integer>
+										<key>idle_per_s</key>
+										<real>0</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>1</integer>
+										<key>interval_ns</key>
+										<integer>2048000</integer>
+										<key>active_per_s</key>
+										<real>0.232786</real>
+										<key>idle_count</key>
+										<integer>1</integer>
+										<key>idle_per_s</key>
+										<real>0.232786</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>0</integer>
+										<key>interval_ns</key>
+										<integer>4096000</integer>
+										<key>active_per_s</key>
+										<real>0</real>
+										<key>idle_count</key>
+										<integer>1</integer>
+										<key>idle_per_s</key>
+										<real>0</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>0</integer>
+										<key>interval_ns</key>
+										<integer>8192000</integer>
+										<key>active_per_s</key>
+										<real>0</real>
+										<key>idle_count</key>
+										<integer>2</integer>
+										<key>idle_per_s</key>
+										<real>0</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>0</integer>
+										<key>interval_ns</key>
+										<integer>16384000</integer>
+										<key>active_per_s</key>
+										<real>0</real>
+										<key>idle_count</key>
+										<integer>1</integer>
+										<key>idle_per_s</key>
+										<real>0</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>0</integer>
+										<key>interval_ns</key>
+										<integer>32768000</integer>
+										<key>active_per_s</key>
+										<real>0</real>
+										<key>idle_count</key>
+										<integer>2</integer>
+										<key>idle_per_s</key>
+										<real>0</real>
+									</dict>
+								</array>
+								<key>cpu</key>
+								<integer>1</integer>
+								<key>freq_hz</key>
+								<real>1604120000</real>
+								<key>freq_ratio</key>
+								<real>0.6183959999999999</real>
+							</dict>
+						</array>
+					</dict>
+					<dict>
+						<key>c_state_ns</key>
+						<integer>4165689565</integer>
+						<key>c_state_ratio</key>
+						<real>0.969715</real>
+						<key>core</key>
+						<integer>1</integer>
+						<key>c_states</key>
+						<array>
+							<dict>
+								<key>used_ns</key>
+								<integer>3710669</integer>
+								<key>used_ratio</key>
+								<real>0.000863793</real>
+								<key>name</key>
+								<string>C3</string>
+							</dict>
+							<dict>
+								<key>used_ns</key>
+								<integer>0</integer>
+								<key>used_ratio</key>
+								<real>0</real>
+								<key>name</key>
+								<string>C6</string>
+							</dict>
+							<dict>
+								<key>used_ns</key>
+								<integer>4161978895</integer>
+								<key>used_ratio</key>
+								<real>0.968851</real>
+								<key>name</key>
+								<string>C7</string>
+							</dict>
+						</array>
+						<key>cpus</key>
+						<array>
+							<dict>
+								<key>duty_cycles</key>
+								<array>
+									<dict>
+										<key>active_count</key>
+										<integer>325</integer>
+										<key>interval_ns</key>
+										<integer>16000</integer>
+										<key>active_per_s</key>
+										<real>75.6555</real>
+										<key>idle_count</key>
+										<integer>28</integer>
+										<key>idle_per_s</key>
+										<real>75.6555</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>19</integer>
+										<key>interval_ns</key>
+										<integer>32000</integer>
+										<key>active_per_s</key>
+										<real>4.42294</real>
+										<key>idle_count</key>
+										<integer>61</integer>
+										<key>idle_per_s</key>
+										<real>4.42294</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>58</integer>
+										<key>interval_ns</key>
+										<integer>64000</integer>
+										<key>active_per_s</key>
+										<real>13.5016</real>
+										<key>idle_count</key>
+										<integer>104</integer>
+										<key>idle_per_s</key>
+										<real>13.5016</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>84</integer>
+										<key>interval_ns</key>
+										<integer>128000</integer>
+										<key>active_per_s</key>
+										<real>19.554</real>
+										<key>idle_count</key>
+										<integer>87</integer>
+										<key>idle_per_s</key>
+										<real>19.554</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>29</integer>
+										<key>interval_ns</key>
+										<integer>256000</integer>
+										<key>active_per_s</key>
+										<real>6.7508</real>
+										<key>idle_count</key>
+										<integer>36</integer>
+										<key>idle_per_s</key>
+										<real>6.7508</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>18</integer>
+										<key>interval_ns</key>
+										<integer>512000</integer>
+										<key>active_per_s</key>
+										<real>4.19015</real>
+										<key>idle_count</key>
+										<integer>24</integer>
+										<key>idle_per_s</key>
+										<real>4.19015</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>39</integer>
+										<key>interval_ns</key>
+										<integer>1024000</integer>
+										<key>active_per_s</key>
+										<real>9.078659999999999</real>
+										<key>idle_count</key>
+										<integer>19</integer>
+										<key>idle_per_s</key>
+										<real>9.078659999999999</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>1</integer>
+										<key>interval_ns</key>
+										<integer>2048000</integer>
+										<key>active_per_s</key>
+										<real>0.232786</real>
+										<key>idle_count</key>
+										<integer>20</integer>
+										<key>idle_per_s</key>
+										<real>0.232786</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>9</integer>
+										<key>interval_ns</key>
+										<integer>4096000</integer>
+										<key>active_per_s</key>
+										<real>2.09508</real>
+										<key>idle_count</key>
+										<integer>31</integer>
+										<key>idle_per_s</key>
+										<real>2.09508</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>1</integer>
+										<key>interval_ns</key>
+										<integer>8192000</integer>
+										<key>active_per_s</key>
+										<real>0.232786</real>
+										<key>idle_count</key>
+										<integer>37</integer>
+										<key>idle_per_s</key>
+										<real>0.232786</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>0</integer>
+										<key>interval_ns</key>
+										<integer>16384000</integer>
+										<key>active_per_s</key>
+										<real>0</real>
+										<key>idle_count</key>
+										<integer>44</integer>
+										<key>idle_per_s</key>
+										<real>0</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>1</integer>
+										<key>interval_ns</key>
+										<integer>32768000</integer>
+										<key>active_per_s</key>
+										<real>0.232786</real>
+										<key>idle_count</key>
+										<integer>62</integer>
+										<key>idle_per_s</key>
+										<real>0.232786</real>
+									</dict>
+								</array>
+								<key>cpu</key>
+								<integer>2</integer>
+								<key>freq_hz</key>
+								<real>2073780000</real>
+								<key>freq_ratio</key>
+								<real>0.799453</real>
+							</dict>
+							<dict>
+								<key>duty_cycles</key>
+								<array>
+									<dict>
+										<key>active_count</key>
+										<integer>9</integer>
+										<key>interval_ns</key>
+										<integer>16000</integer>
+										<key>active_per_s</key>
+										<real>2.09508</real>
+										<key>idle_count</key>
+										<integer>0</integer>
+										<key>idle_per_s</key>
+										<real>2.09508</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>3</integer>
+										<key>interval_ns</key>
+										<integer>32000</integer>
+										<key>active_per_s</key>
+										<real>0.698358</real>
+										<key>idle_count</key>
+										<integer>2</integer>
+										<key>idle_per_s</key>
+										<real>0.698358</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>1</integer>
+										<key>interval_ns</key>
+										<integer>64000</integer>
+										<key>active_per_s</key>
+										<real>0.232786</real>
+										<key>idle_count</key>
+										<integer>0</integer>
+										<key>idle_per_s</key>
+										<real>0.232786</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>1</integer>
+										<key>interval_ns</key>
+										<integer>128000</integer>
+										<key>active_per_s</key>
+										<real>0.232786</real>
+										<key>idle_count</key>
+										<integer>1</integer>
+										<key>idle_per_s</key>
+										<real>0.232786</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>3</integer>
+										<key>interval_ns</key>
+										<integer>256000</integer>
+										<key>active_per_s</key>
+										<real>0.698358</real>
+										<key>idle_count</key>
+										<integer>1</integer>
+										<key>idle_per_s</key>
+										<real>0.698358</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>1</integer>
+										<key>interval_ns</key>
+										<integer>512000</integer>
+										<key>active_per_s</key>
+										<real>0.232786</real>
+										<key>idle_count</key>
+										<integer>1</integer>
+										<key>idle_per_s</key>
+										<real>0.232786</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>0</integer>
+										<key>interval_ns</key>
+										<integer>1024000</integer>
+										<key>active_per_s</key>
+										<real>0</real>
+										<key>idle_count</key>
+										<integer>2</integer>
+										<key>idle_per_s</key>
+										<real>0</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>0</integer>
+										<key>interval_ns</key>
+										<integer>2048000</integer>
+										<key>active_per_s</key>
+										<real>0</real>
+										<key>idle_count</key>
+										<integer>0</integer>
+										<key>idle_per_s</key>
+										<real>0</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>0</integer>
+										<key>interval_ns</key>
+										<integer>4096000</integer>
+										<key>active_per_s</key>
+										<real>0</real>
+										<key>idle_count</key>
+										<integer>2</integer>
+										<key>idle_per_s</key>
+										<real>0</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>0</integer>
+										<key>interval_ns</key>
+										<integer>8192000</integer>
+										<key>active_per_s</key>
+										<real>0</real>
+										<key>idle_count</key>
+										<integer>0</integer>
+										<key>idle_per_s</key>
+										<real>0</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>0</integer>
+										<key>interval_ns</key>
+										<integer>16384000</integer>
+										<key>active_per_s</key>
+										<real>0</real>
+										<key>idle_count</key>
+										<integer>0</integer>
+										<key>idle_per_s</key>
+										<real>0</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>0</integer>
+										<key>interval_ns</key>
+										<integer>32768000</integer>
+										<key>active_per_s</key>
+										<real>0</real>
+										<key>idle_count</key>
+										<integer>1</integer>
+										<key>idle_per_s</key>
+										<real>0</real>
+									</dict>
+								</array>
+								<key>cpu</key>
+								<integer>3</integer>
+								<key>freq_hz</key>
+								<real>1989580000</real>
+								<key>freq_ratio</key>
+								<real>0.766994</real>
+							</dict>
+						</array>
+					</dict>
+					<dict>
+						<key>c_state_ns</key>
+						<integer>4141777109</integer>
+						<key>c_state_ratio</key>
+						<real>0.964148</real>
+						<key>core</key>
+						<integer>2</integer>
+						<key>c_states</key>
+						<array>
+							<dict>
+								<key>used_ns</key>
+								<integer>884933</integer>
+								<key>used_ratio</key>
+								<real>0.000206</real>
+								<key>name</key>
+								<string>C3</string>
+							</dict>
+							<dict>
+								<key>used_ns</key>
+								<integer>0</integer>
+								<key>used_ratio</key>
+								<real>0</real>
+								<key>name</key>
+								<string>C6</string>
+							</dict>
+							<dict>
+								<key>used_ns</key>
+								<integer>4140892176</integer>
+								<key>used_ratio</key>
+								<real>0.963942</real>
+								<key>name</key>
+								<string>C7</string>
+							</dict>
+						</array>
+						<key>cpus</key>
+						<array>
+							<dict>
+								<key>duty_cycles</key>
+								<array>
+									<dict>
+										<key>active_count</key>
+										<integer>396</integer>
+										<key>interval_ns</key>
+										<integer>16000</integer>
+										<key>active_per_s</key>
+										<real>92.1833</real>
+										<key>idle_count</key>
+										<integer>26</integer>
+										<key>idle_per_s</key>
+										<real>92.1833</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>28</integer>
+										<key>interval_ns</key>
+										<integer>32000</integer>
+										<key>active_per_s</key>
+										<real>6.51801</real>
+										<key>idle_count</key>
+										<integer>58</integer>
+										<key>idle_per_s</key>
+										<real>6.51801</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>69</integer>
+										<key>interval_ns</key>
+										<integer>64000</integer>
+										<key>active_per_s</key>
+										<real>16.0622</real>
+										<key>idle_count</key>
+										<integer>124</integer>
+										<key>idle_per_s</key>
+										<real>16.0622</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>97</integer>
+										<key>interval_ns</key>
+										<integer>128000</integer>
+										<key>active_per_s</key>
+										<real>22.5803</real>
+										<key>idle_count</key>
+										<integer>115</integer>
+										<key>idle_per_s</key>
+										<real>22.5803</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>37</integer>
+										<key>interval_ns</key>
+										<integer>256000</integer>
+										<key>active_per_s</key>
+										<real>8.61309</real>
+										<key>idle_count</key>
+										<integer>36</integer>
+										<key>idle_per_s</key>
+										<real>8.61309</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>34</integer>
+										<key>interval_ns</key>
+										<integer>512000</integer>
+										<key>active_per_s</key>
+										<real>7.91473</real>
+										<key>idle_count</key>
+										<integer>34</integer>
+										<key>idle_per_s</key>
+										<real>7.91473</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>32</integer>
+										<key>interval_ns</key>
+										<integer>1024000</integer>
+										<key>active_per_s</key>
+										<real>7.44916</real>
+										<key>idle_count</key>
+										<integer>18</integer>
+										<key>idle_per_s</key>
+										<real>7.44916</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>7</integer>
+										<key>interval_ns</key>
+										<integer>2048000</integer>
+										<key>active_per_s</key>
+										<real>1.6295</real>
+										<key>idle_count</key>
+										<integer>40</integer>
+										<key>idle_per_s</key>
+										<real>1.6295</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>17</integer>
+										<key>interval_ns</key>
+										<integer>4096000</integer>
+										<key>active_per_s</key>
+										<real>3.95736</real>
+										<key>idle_count</key>
+										<integer>40</integer>
+										<key>idle_per_s</key>
+										<real>3.95736</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>3</integer>
+										<key>interval_ns</key>
+										<integer>8192000</integer>
+										<key>active_per_s</key>
+										<real>0.698358</real>
+										<key>idle_count</key>
+										<integer>64</integer>
+										<key>idle_per_s</key>
+										<real>0.698358</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>0</integer>
+										<key>interval_ns</key>
+										<integer>16384000</integer>
+										<key>active_per_s</key>
+										<real>0</real>
+										<key>idle_count</key>
+										<integer>72</integer>
+										<key>idle_per_s</key>
+										<real>0</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>0</integer>
+										<key>interval_ns</key>
+										<integer>32768000</integer>
+										<key>active_per_s</key>
+										<real>0</real>
+										<key>idle_count</key>
+										<integer>75</integer>
+										<key>idle_per_s</key>
+										<real>0</real>
+									</dict>
+								</array>
+								<key>cpu</key>
+								<integer>4</integer>
+								<key>freq_hz</key>
+								<real>1768190000</real>
+								<key>freq_ratio</key>
+								<real>0.6816449999999999</real>
+							</dict>
+							<dict>
+								<key>duty_cycles</key>
+								<array>
+									<dict>
+										<key>active_count</key>
+										<integer>16</integer>
+										<key>interval_ns</key>
+										<integer>16000</integer>
+										<key>active_per_s</key>
+										<real>3.72458</real>
+										<key>idle_count</key>
+										<integer>0</integer>
+										<key>idle_per_s</key>
+										<real>3.72458</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>3</integer>
+										<key>interval_ns</key>
+										<integer>32000</integer>
+										<key>active_per_s</key>
+										<real>0.698358</real>
+										<key>idle_count</key>
+										<integer>0</integer>
+										<key>idle_per_s</key>
+										<real>0.698358</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>1</integer>
+										<key>interval_ns</key>
+										<integer>64000</integer>
+										<key>active_per_s</key>
+										<real>0.232786</real>
+										<key>idle_count</key>
+										<integer>0</integer>
+										<key>idle_per_s</key>
+										<real>0.232786</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>1</integer>
+										<key>interval_ns</key>
+										<integer>128000</integer>
+										<key>active_per_s</key>
+										<real>0.232786</real>
+										<key>idle_count</key>
+										<integer>2</integer>
+										<key>idle_per_s</key>
+										<real>0.232786</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>3</integer>
+										<key>interval_ns</key>
+										<integer>256000</integer>
+										<key>active_per_s</key>
+										<real>0.698358</real>
+										<key>idle_count</key>
+										<integer>2</integer>
+										<key>idle_per_s</key>
+										<real>0.698358</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>0</integer>
+										<key>interval_ns</key>
+										<integer>512000</integer>
+										<key>active_per_s</key>
+										<real>0</real>
+										<key>idle_count</key>
+										<integer>1</integer>
+										<key>idle_per_s</key>
+										<real>0</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>0</integer>
+										<key>interval_ns</key>
+										<integer>1024000</integer>
+										<key>active_per_s</key>
+										<real>0</real>
+										<key>idle_count</key>
+										<integer>1</integer>
+										<key>idle_per_s</key>
+										<real>0</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>1</integer>
+										<key>interval_ns</key>
+										<integer>2048000</integer>
+										<key>active_per_s</key>
+										<real>0.232786</real>
+										<key>idle_count</key>
+										<integer>0</integer>
+										<key>idle_per_s</key>
+										<real>0.232786</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>0</integer>
+										<key>interval_ns</key>
+										<integer>4096000</integer>
+										<key>active_per_s</key>
+										<real>0</real>
+										<key>idle_count</key>
+										<integer>2</integer>
+										<key>idle_per_s</key>
+										<real>0</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>0</integer>
+										<key>interval_ns</key>
+										<integer>8192000</integer>
+										<key>active_per_s</key>
+										<real>0</real>
+										<key>idle_count</key>
+										<integer>1</integer>
+										<key>idle_per_s</key>
+										<real>0</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>0</integer>
+										<key>interval_ns</key>
+										<integer>16384000</integer>
+										<key>active_per_s</key>
+										<real>0</real>
+										<key>idle_count</key>
+										<integer>0</integer>
+										<key>idle_per_s</key>
+										<real>0</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>0</integer>
+										<key>interval_ns</key>
+										<integer>32768000</integer>
+										<key>active_per_s</key>
+										<real>0</real>
+										<key>idle_count</key>
+										<integer>0</integer>
+										<key>idle_per_s</key>
+										<real>0</real>
+									</dict>
+								</array>
+								<key>cpu</key>
+								<integer>5</integer>
+								<key>freq_hz</key>
+								<real>2250530000</real>
+								<key>freq_ratio</key>
+								<real>0.867594</real>
+							</dict>
+						</array>
+					</dict>
+					<dict>
+						<key>c_state_ns</key>
+						<integer>4177380735</integer>
+						<key>c_state_ratio</key>
+						<real>0.972436</real>
+						<key>core</key>
+						<integer>3</integer>
+						<key>c_states</key>
+						<array>
+							<dict>
+								<key>used_ns</key>
+								<integer>9674041</integer>
+								<key>used_ratio</key>
+								<real>0.00225198</real>
+								<key>name</key>
+								<string>C3</string>
+							</dict>
+							<dict>
+								<key>used_ns</key>
+								<integer>0</integer>
+								<key>used_ratio</key>
+								<real>0</real>
+								<key>name</key>
+								<string>C6</string>
+							</dict>
+							<dict>
+								<key>used_ns</key>
+								<integer>4167706693</integer>
+								<key>used_ratio</key>
+								<real>0.970184</real>
+								<key>name</key>
+								<string>C7</string>
+							</dict>
+						</array>
+						<key>cpus</key>
+						<array>
+							<dict>
+								<key>duty_cycles</key>
+								<array>
+									<dict>
+										<key>active_count</key>
+										<integer>378</integer>
+										<key>interval_ns</key>
+										<integer>16000</integer>
+										<key>active_per_s</key>
+										<real>87.9932</real>
+										<key>idle_count</key>
+										<integer>29</integer>
+										<key>idle_per_s</key>
+										<real>87.9932</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>28</integer>
+										<key>interval_ns</key>
+										<integer>32000</integer>
+										<key>active_per_s</key>
+										<real>6.51801</real>
+										<key>idle_count</key>
+										<integer>51</integer>
+										<key>idle_per_s</key>
+										<real>6.51801</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>62</integer>
+										<key>interval_ns</key>
+										<integer>64000</integer>
+										<key>active_per_s</key>
+										<real>14.4327</real>
+										<key>idle_count</key>
+										<integer>119</integer>
+										<key>idle_per_s</key>
+										<real>14.4327</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>84</integer>
+										<key>interval_ns</key>
+										<integer>128000</integer>
+										<key>active_per_s</key>
+										<real>19.554</real>
+										<key>idle_count</key>
+										<integer>103</integer>
+										<key>idle_per_s</key>
+										<real>19.554</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>32</integer>
+										<key>interval_ns</key>
+										<integer>256000</integer>
+										<key>active_per_s</key>
+										<real>7.44916</real>
+										<key>idle_count</key>
+										<integer>41</integer>
+										<key>idle_per_s</key>
+										<real>7.44916</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>27</integer>
+										<key>interval_ns</key>
+										<integer>512000</integer>
+										<key>active_per_s</key>
+										<real>6.28523</real>
+										<key>idle_count</key>
+										<integer>23</integer>
+										<key>idle_per_s</key>
+										<real>6.28523</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>29</integer>
+										<key>interval_ns</key>
+										<integer>1024000</integer>
+										<key>active_per_s</key>
+										<real>6.7508</real>
+										<key>idle_count</key>
+										<integer>17</integer>
+										<key>idle_per_s</key>
+										<real>6.7508</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>10</integer>
+										<key>interval_ns</key>
+										<integer>2048000</integer>
+										<key>active_per_s</key>
+										<real>2.32786</real>
+										<key>idle_count</key>
+										<integer>30</integer>
+										<key>idle_per_s</key>
+										<real>2.32786</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>12</integer>
+										<key>interval_ns</key>
+										<integer>4096000</integer>
+										<key>active_per_s</key>
+										<real>2.79343</real>
+										<key>idle_count</key>
+										<integer>32</integer>
+										<key>idle_per_s</key>
+										<real>2.79343</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>1</integer>
+										<key>interval_ns</key>
+										<integer>8192000</integer>
+										<key>active_per_s</key>
+										<real>0.232786</real>
+										<key>idle_count</key>
+										<integer>73</integer>
+										<key>idle_per_s</key>
+										<real>0.232786</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>0</integer>
+										<key>interval_ns</key>
+										<integer>16384000</integer>
+										<key>active_per_s</key>
+										<real>0</real>
+										<key>idle_count</key>
+										<integer>51</integer>
+										<key>idle_per_s</key>
+										<real>0</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>0</integer>
+										<key>interval_ns</key>
+										<integer>32768000</integer>
+										<key>active_per_s</key>
+										<real>0</real>
+										<key>idle_count</key>
+										<integer>72</integer>
+										<key>idle_per_s</key>
+										<real>0</real>
+									</dict>
+								</array>
+								<key>cpu</key>
+								<integer>6</integer>
+								<key>freq_hz</key>
+								<real>1635700000</real>
+								<key>freq_ratio</key>
+								<real>0.6305730000000001</real>
+							</dict>
+							<dict>
+								<key>duty_cycles</key>
+								<array>
+									<dict>
+										<key>active_count</key>
+										<integer>15</integer>
+										<key>interval_ns</key>
+										<integer>16000</integer>
+										<key>active_per_s</key>
+										<real>3.49179</real>
+										<key>idle_count</key>
+										<integer>1</integer>
+										<key>idle_per_s</key>
+										<real>3.49179</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>3</integer>
+										<key>interval_ns</key>
+										<integer>32000</integer>
+										<key>active_per_s</key>
+										<real>0.698358</real>
+										<key>idle_count</key>
+										<integer>1</integer>
+										<key>idle_per_s</key>
+										<real>0.698358</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>2</integer>
+										<key>interval_ns</key>
+										<integer>64000</integer>
+										<key>active_per_s</key>
+										<real>0.465572</real>
+										<key>idle_count</key>
+										<integer>0</integer>
+										<key>idle_per_s</key>
+										<real>0.465572</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>1</integer>
+										<key>interval_ns</key>
+										<integer>128000</integer>
+										<key>active_per_s</key>
+										<real>0.232786</real>
+										<key>idle_count</key>
+										<integer>0</integer>
+										<key>idle_per_s</key>
+										<real>0.232786</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>1</integer>
+										<key>interval_ns</key>
+										<integer>256000</integer>
+										<key>active_per_s</key>
+										<real>0.232786</real>
+										<key>idle_count</key>
+										<integer>1</integer>
+										<key>idle_per_s</key>
+										<real>0.232786</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>0</integer>
+										<key>interval_ns</key>
+										<integer>512000</integer>
+										<key>active_per_s</key>
+										<real>0</real>
+										<key>idle_count</key>
+										<integer>1</integer>
+										<key>idle_per_s</key>
+										<real>0</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>0</integer>
+										<key>interval_ns</key>
+										<integer>1024000</integer>
+										<key>active_per_s</key>
+										<real>0</real>
+										<key>idle_count</key>
+										<integer>0</integer>
+										<key>idle_per_s</key>
+										<real>0</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>0</integer>
+										<key>interval_ns</key>
+										<integer>2048000</integer>
+										<key>active_per_s</key>
+										<real>0</real>
+										<key>idle_count</key>
+										<integer>0</integer>
+										<key>idle_per_s</key>
+										<real>0</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>0</integer>
+										<key>interval_ns</key>
+										<integer>4096000</integer>
+										<key>active_per_s</key>
+										<real>0</real>
+										<key>idle_count</key>
+										<integer>2</integer>
+										<key>idle_per_s</key>
+										<real>0</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>0</integer>
+										<key>interval_ns</key>
+										<integer>8192000</integer>
+										<key>active_per_s</key>
+										<real>0</real>
+										<key>idle_count</key>
+										<integer>1</integer>
+										<key>idle_per_s</key>
+										<real>0</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>0</integer>
+										<key>interval_ns</key>
+										<integer>16384000</integer>
+										<key>active_per_s</key>
+										<real>0</real>
+										<key>idle_count</key>
+										<integer>0</integer>
+										<key>idle_per_s</key>
+										<real>0</real>
+									</dict>
+									<dict>
+										<key>active_count</key>
+										<integer>0</integer>
+										<key>interval_ns</key>
+										<integer>32768000</integer>
+										<key>active_per_s</key>
+										<real>0</real>
+										<key>idle_count</key>
+										<integer>0</integer>
+										<key>idle_per_s</key>
+										<real>0</real>
+									</dict>
+								</array>
+								<key>cpu</key>
+								<integer>7</integer>
+								<key>freq_hz</key>
+								<real>1818650000</real>
+								<key>freq_ratio</key>
+								<real>0.701099</real>
+							</dict>
+						</array>
+					</dict>
+				</array>
+			</dict>
+		</array>
+		<key>package_watts</key>
+		<real>3.03713</real>
+		<key>llc_flushed_ratio</key>
+		<real>0.850534</real>
+		<key>freq_ratio</key>
+		<real>0.643069</real>
+	</dict>
+	<key>network</key>
+	<dict>
+		<key>obytes</key>
+		<integer>258</integer>
+		<key>obyte_rate</key>
+		<real>60.0588</real>
+		<key>opacket_rate</key>
+		<real>0.698358</real>
+		<key>ipackets</key>
+		<integer>8</integer>
+		<key>ibytes</key>
+		<integer>2449</integer>
+		<key>ibyte_rate</key>
+		<real>570.093</real>
+		<key>ipacket_rate</key>
+		<real>1.86229</real>
+		<key>opackets</key>
+		<integer>3</integer>
+	</dict>
+	<key>elapsed_ns</key>
+	<integer>4295788088</integer>
+	<key>is_delta</key>
+	<false/>
+</dict>
+</plist>
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/powermetrics_output_unsupported_hardware.output b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/powermetrics_output_unsupported_hardware.output
new file mode 100644
index 0000000..ce03f93
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/powermetrics_output_unsupported_hardware.output
@@ -0,0 +1,505 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+<key>is_delta</key><true/>
+<key>elapsed_ns</key><integer>57758028</integer>
+<key>hw_model</key><string>MacBookPro5,4</string>
+<key>kern_osversion</key><string>13B42</string>
+<key>timestamp</key><date>2014-01-26T03:58:59Z</date>
+<key>tasks</key>
+<array>
+<dict>
+<key>pid</key><integer>6555</integer>
+<key>name</key><string>Google Chrome He</string>
+<key>started_ns</key><integer>284136698238000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>35244306</integer>
+<key>cputime_ms_per_s</key><real>610.206</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.889658</real>
+<key>intr_wakeups</key><integer>3</integer>
+<key>intr_wakeups_per_s</key><real>51.9408</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>6546</integer>
+<key>name</key><string>Google Chrome He</string>
+<key>started_ns</key><integer>284134682369000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>32548397</integer>
+<key>cputime_ms_per_s</key><real>563.53</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.966536</real>
+<key>intr_wakeups</key><integer>0</integer>
+<key>intr_wakeups_per_s</key><real>0</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>6544</integer>
+<key>name</key><string>Google Chrome Ca</string>
+<key>started_ns</key><integer>284134369378000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>8773988</integer>
+<key>cputime_ms_per_s</key><real>151.909</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.78863</real>
+<key>intr_wakeups</key><integer>3</integer>
+<key>intr_wakeups_per_s</key><real>51.9408</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>6543</integer>
+<key>name</key><string>Python</string>
+<key>started_ns</key><integer>284133951261000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>5664752</integer>
+<key>cputime_ms_per_s</key><real>98.0773</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.813448</real>
+<key>intr_wakeups</key><integer>0</integer>
+<key>intr_wakeups_per_s</key><real>0</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>6556</integer>
+<key>name</key><string>powermetrics</string>
+<key>started_ns</key><integer>284137559665000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>5653312</integer>
+<key>cputime_ms_per_s</key><real>97.8792</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.275642</real>
+<key>intr_wakeups</key><integer>1</integer>
+<key>intr_wakeups_per_s</key><real>17.3136</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>4658</integer>
+<key>name</key><string>powermetrics</string>
+<key>started_ns</key><integer>283169551318000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>4295690</integer>
+<key>cputime_ms_per_s</key><real>74.3739</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.255386</real>
+<key>intr_wakeups</key><integer>1</integer>
+<key>intr_wakeups_per_s</key><real>17.3136</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>923</integer>
+<key>name</key><string>Google Chrome</string>
+<key>started_ns</key><integer>159793249000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>3993669</integer>
+<key>cputime_ms_per_s</key><real>69.1448</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.885849</real>
+<key>intr_wakeups</key><integer>2</integer>
+<key>intr_wakeups_per_s</key><real>34.6272</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>939</integer>
+<key>name</key><string>coreaudiod</string>
+<key>started_ns</key><integer>160547555000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>3831594</integer>
+<key>cputime_ms_per_s</key><real>66.3387</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.111444</real>
+<key>intr_wakeups</key><integer>20</integer>
+<key>intr_wakeups_per_s</key><real>346.272</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>20</integer>
+  <key>wakeups_per_s</key><real>346.272</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>171</integer>
+<key>name</key><string>WindowServer</string>
+<key>started_ns</key><integer>80236634000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>2596609</integer>
+<key>cputime_ms_per_s</key><real>44.9567</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.7211</real>
+<key>intr_wakeups</key><integer>1</integer>
+<key>intr_wakeups_per_s</key><real>17.3136</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>92729</integer>
+<key>name</key><string>Google Chrome He</string>
+<key>started_ns</key><integer>280934551284000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>2189391</integer>
+<key>cputime_ms_per_s</key><real>37.9063</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.859925</real>
+<key>intr_wakeups</key><integer>9</integer>
+<key>intr_wakeups_per_s</key><real>155.822</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>0</integer>
+<key>name</key><string>kernel_task</string>
+<key>started_ns</key><integer>5162903000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>1223535</integer>
+<key>cputime_ms_per_s</key><real>21.1838</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0</real>
+<key>intr_wakeups</key><integer>19</integer>
+<key>intr_wakeups_per_s</key><real>328.959</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>60</integer>
+<key>name</key><string>mds</string>
+<key>started_ns</key><integer>71568427000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>33490</integer>
+<key>cputime_ms_per_s</key><real>0.579833</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.718901</real>
+<key>intr_wakeups</key><integer>0</integer>
+<key>intr_wakeups_per_s</key><real>0</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>14230</integer>
+<key>name</key><string>CIJScannerRegist</string>
+<key>started_ns</key><integer>17913578888000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>28198</integer>
+<key>cputime_ms_per_s</key><real>0.488209</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.271189</real>
+<key>intr_wakeups</key><integer>2</integer>
+<key>intr_wakeups_per_s</key><real>34.6272</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>92766</integer>
+<key>name</key><string>GoogleTalkPlugin</string>
+<key>started_ns</key><integer>281459670930000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>22858</integer>
+<key>cputime_ms_per_s</key><real>0.395755</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.442558</real>
+<key>intr_wakeups</key><integer>1</integer>
+<key>intr_wakeups_per_s</key><real>17.3136</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+</array>
+<key>all_tasks</key>
+<dict>
+<key>pid</key><integer>-2</integer>
+<key>name</key><string>ALL_TASKS</string>
+<key>started_ns</key><integer>0</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>106099789</integer>
+<key>cputime_ms_per_s</key><real>1836.97</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.798862</real>
+<key>intr_wakeups</key><integer>62</integer>
+<key>intr_wakeups_per_s</key><real>1073.44</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>20</integer>
+  <key>wakeups_per_s</key><real>346.272</real>
+</dict>
+</array>
+</dict>
+<key>backlight</key>
+<dict>
+<key>value</key><integer>412</integer>
+<key>min</key><integer>0</integer>
+<key>max</key><integer>1024</integer>
+</dict>
+<key>network</key>
+<dict>
+<key>opackets</key><integer>24</integer>
+<key>opacket_rate</key><real>415.527</real>
+<key>ipackets</key><integer>24</integer>
+<key>ipacket_rate</key><real>415.527</real>
+<key>obytes</key><integer>7244</integer>
+<key>obyte_rate</key><real>125420</real>
+<key>ibytes</key><integer>7244</integer>
+<key>ibyte_rate</key><real>125420</real>
+</dict>
+<key>disk</key>
+<dict>
+<key>rops_diff</key><integer>0</integer>
+<key>rops_per_s</key><real>0</real>
+<key>wops_diff</key><integer>0</integer>
+<key>wops_per_s</key><real>0</real>
+<key>rbytes_diff</key><integer>0</integer>
+<key>rbytes_per_s</key><real>0</real>
+<key>wbytes_diff</key><integer>0</integer>
+<key>wbytes_per_s</key><real>0</real>
+</dict>
+<key>interrupts</key>
+<array>
+<dict>
+<key>cpu</key><integer>0</integer>
+<key>vectors</key>
+<array>
+<dict>
+<key>name</key><string>HPET</string>
+<key>vector</key><integer>66</integer>
+<key>events</key><integer>2</integer>
+<key>events_per_s</key><real>34.6272</real>
+</dict>
+<dict>
+<key>name</key><string>IGPU</string>
+<key>vector</key><integer>146</integer>
+<key>events</key><integer>2</integer>
+<key>events_per_s</key><real>34.6272</real>
+</dict>
+<dict>
+<key>name</key><string>ARPT</string>
+<key>vector</key><integer>148</integer>
+<key>events</key><integer>2</integer>
+<key>events_per_s</key><real>34.6272</real>
+</dict>
+<dict>
+<key>name</key><string>TMR</string>
+<key>vector</key><integer>221</integer>
+<key>events</key><integer>43</integer>
+<key>events_per_s</key><real>744.485</real>
+</dict>
+<dict>
+<key>name</key><string>IPI</string>
+<key>vector</key><integer>222</integer>
+<key>events</key><integer>47</integer>
+<key>events_per_s</key><real>813.74</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>cpu</key><integer>1</integer>
+<key>vectors</key>
+<array>
+<dict>
+<key>name</key><string>TMR</string>
+<key>vector</key><integer>221</integer>
+<key>events</key><integer>15</integer>
+<key>events_per_s</key><real>259.704</real>
+</dict>
+<dict>
+<key>name</key><string>IPI</string>
+<key>vector</key><integer>222</integer>
+<key>events</key><integer>56</integer>
+<key>events_per_s</key><real>969.562</real>
+</dict>
+</array>
+</dict>
+</array>
+</dict>
+</plist>
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/powermetrics_vmware.output b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/powermetrics_vmware.output
new file mode 100644
index 0000000..8cf3b8d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/powermetrics_vmware.output
@@ -0,0 +1,2889 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+<key>is_delta</key><false/>
+<key>elapsed_ns</key><integer>2791294949</integer>
+<key>hw_model</key><string>VMware7,1</string>
+<key>kern_osversion</key><string>13F34</string>
+<key>timestamp</key><date>2014-12-09T13:48:10Z</date>
+<key>tasks</key>
+<array>
+<dict>
+<key>pid</key><integer>8283</integer>
+<key>name</key><string>Python</string>
+<key>started_ns</key><integer>258047690000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>2785827455</integer>
+<key>cputime_ms_per_s</key><real>998.698</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.907007</real>
+<key>intr_wakeups</key><integer>0</integer>
+<key>intr_wakeups_per_s</key><real>0</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>0</integer>
+<key>name</key><string>kernel_task</string>
+<key>started_ns</key><integer>252926000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>22377453</integer>
+<key>cputime_ms_per_s</key><real>8.02215</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0</real>
+<key>intr_wakeups</key><integer>393</integer>
+<key>intr_wakeups_per_s</key><real>140.888</real>
+<key>idle_wakeups</key><integer>142</integer>
+<key>idle_wakeups_per_s</key><real>50.9059</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>124</integer>
+<key>name</key><string>grr</string>
+<key>started_ns</key><integer>8507377000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>2568839</integer>
+<key>cputime_ms_per_s</key><real>0.92091</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.718355</real>
+<key>intr_wakeups</key><integer>67</integer>
+<key>intr_wakeups_per_s</key><real>24.019</real>
+<key>idle_wakeups</key><integer>52</integer>
+<key>idle_wakeups_per_s</key><real>18.6416</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>2</integer>
+  <key>wakeups_per_s</key><real>0.716985</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>1</integer>
+  <key>wakeups_per_s</key><real>0.358493</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>116</integer>
+<key>name</key><string>vmware-tools-dae</string>
+<key>started_ns</key><integer>8411762000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>3273185</integer>
+<key>cputime_ms_per_s</key><real>1.17341</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.449496</real>
+<key>intr_wakeups</key><integer>31</integer>
+<key>intr_wakeups_per_s</key><real>11.1133</real>
+<key>idle_wakeups</key><integer>22</integer>
+<key>idle_wakeups_per_s</key><real>7.88684</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>267</integer>
+<key>name</key><string>vmware-tools-dae</string>
+<key>started_ns</key><integer>73620941000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>3680193</integer>
+<key>cputime_ms_per_s</key><real>1.31932</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.437701</real>
+<key>intr_wakeups</key><integer>32</integer>
+<key>intr_wakeups_per_s</key><real>11.4718</real>
+<key>idle_wakeups</key><integer>9</integer>
+<key>idle_wakeups_per_s</key><real>3.22643</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>90</integer>
+<key>name</key><string>mds</string>
+<key>started_ns</key><integer>8404018000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>4231481</integer>
+<key>cputime_ms_per_s</key><real>1.51695</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.422196</real>
+<key>intr_wakeups</key><integer>12</integer>
+<key>intr_wakeups_per_s</key><real>4.30191</real>
+<key>idle_wakeups</key><integer>1</integer>
+<key>idle_wakeups_per_s</key><real>0.358493</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>47</integer>
+<key>name</key><string>fseventsd</string>
+<key>started_ns</key><integer>5848136000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>3609311</integer>
+<key>cputime_ms_per_s</key><real>1.29391</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.425773</real>
+<key>intr_wakeups</key><integer>16</integer>
+<key>intr_wakeups_per_s</key><real>5.73588</real>
+<key>idle_wakeups</key><integer>1</integer>
+<key>idle_wakeups_per_s</key><real>0.358493</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>69</integer>
+<key>name</key><string>netbiosd</string>
+<key>started_ns</key><integer>8370786000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>2217146</integer>
+<key>cputime_ms_per_s</key><real>0.79483</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.319186</real>
+<key>intr_wakeups</key><integer>0</integer>
+<key>intr_wakeups_per_s</key><real>0</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>8289</integer>
+<key>name</key><string>powermetrics</string>
+<key>started_ns</key><integer>266634178000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>1857249</integer>
+<key>cputime_ms_per_s</key><real>0.66581</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.244225</real>
+<key>intr_wakeups</key><integer>0</integer>
+<key>intr_wakeups_per_s</key><real>0</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>230</integer>
+<key>name</key><string>Finder</string>
+<key>started_ns</key><integer>72342540000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>1460335</integer>
+<key>cputime_ms_per_s</key><real>0.523519</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.403851</real>
+<key>intr_wakeups</key><integer>1</integer>
+<key>intr_wakeups_per_s</key><real>0.358493</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>50</integer>
+<key>name</key><string>mDNSResponder</string>
+<key>started_ns</key><integer>5850938000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>1156791</integer>
+<key>cputime_ms_per_s</key><real>0.414701</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.499696</real>
+<key>intr_wakeups</key><integer>8</integer>
+<key>intr_wakeups_per_s</key><real>2.86794</real>
+<key>idle_wakeups</key><integer>1</integer>
+<key>idle_wakeups_per_s</key><real>0.358493</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>128</integer>
+<key>name</key><string>nrpe</string>
+<key>started_ns</key><integer>8596070000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>158225</integer>
+<key>cputime_ms_per_s</key><real>0.0567225</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.148194</real>
+<key>intr_wakeups</key><integer>5</integer>
+<key>intr_wakeups_per_s</key><real>1.79246</real>
+<key>idle_wakeups</key><integer>4</integer>
+<key>idle_wakeups_per_s</key><real>1.43397</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>26</integer>
+<key>name</key><string>cfprefsd</string>
+<key>started_ns</key><integer>5283770000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>357267</integer>
+<key>cputime_ms_per_s</key><real>0.128078</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.16081</real>
+<key>intr_wakeups</key><integer>3</integer>
+<key>intr_wakeups_per_s</key><real>1.07548</real>
+<key>idle_wakeups</key><integer>3</integer>
+<key>idle_wakeups_per_s</key><real>1.07548</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>72</integer>
+<key>name</key><string>snmpd</string>
+<key>started_ns</key><integer>8399046000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>254431</integer>
+<key>cputime_ms_per_s</key><real>0.0912116</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.289497</real>
+<key>intr_wakeups</key><integer>2</integer>
+<key>intr_wakeups_per_s</key><real>0.716985</real>
+<key>idle_wakeups</key><integer>1</integer>
+<key>idle_wakeups_per_s</key><real>0.358493</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>87</integer>
+<key>name</key><string>pacemaker</string>
+<key>started_ns</key><integer>8403252000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>234206</integer>
+<key>cputime_ms_per_s</key><real>0.0839611</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.310556</real>
+<key>intr_wakeups</key><integer>3</integer>
+<key>intr_wakeups_per_s</key><real>1.07548</real>
+<key>idle_wakeups</key><integer>1</integer>
+<key>idle_wakeups_per_s</key><real>0.358493</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>95</integer>
+<key>name</key><string>locationd</string>
+<key>started_ns</key><integer>8405499000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>161023</integer>
+<key>cputime_ms_per_s</key><real>0.0577255</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.196748</real>
+<key>intr_wakeups</key><integer>2</integer>
+<key>intr_wakeups_per_s</key><real>0.716985</real>
+<key>idle_wakeups</key><integer>1</integer>
+<key>idle_wakeups_per_s</key><real>0.358493</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>73</integer>
+<key>name</key><string>httpd</string>
+<key>started_ns</key><integer>8399290000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>65001</integer>
+<key>cputime_ms_per_s</key><real>0.0233024</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.298288</real>
+<key>intr_wakeups</key><integer>3</integer>
+<key>intr_wakeups_per_s</key><real>1.07548</real>
+<key>idle_wakeups</key><integer>1</integer>
+<key>idle_wakeups_per_s</key><real>0.358493</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>117</integer>
+<key>name</key><string>ruby</string>
+<key>started_ns</key><integer>8412025000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>250926</integer>
+<key>cputime_ms_per_s</key><real>0.0899551</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.789161</real>
+<key>intr_wakeups</key><integer>1</integer>
+<key>intr_wakeups_per_s</key><real>0.358493</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>14</integer>
+<key>name</key><string>kextd</string>
+<key>started_ns</key><integer>3391443000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>239039</integer>
+<key>cputime_ms_per_s</key><real>0.0856937</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.502717</real>
+<key>intr_wakeups</key><integer>1</integer>
+<key>intr_wakeups_per_s</key><real>0.358493</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>20</integer>
+<key>name</key><string>configd</string>
+<key>started_ns</key><integer>3427308000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>209575</integer>
+<key>cputime_ms_per_s</key><real>0.0751311</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.266702</real>
+<key>intr_wakeups</key><integer>1</integer>
+<key>intr_wakeups_per_s</key><real>0.358493</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>93</integer>
+<key>name</key><string>loginwindow</string>
+<key>started_ns</key><integer>8404920000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>209443</integer>
+<key>cputime_ms_per_s</key><real>0.0750838</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.213891</real>
+<key>intr_wakeups</key><integer>1</integer>
+<key>intr_wakeups_per_s</key><real>0.358493</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>272</integer>
+<key>name</key><string>com.apple.ShareK</string>
+<key>started_ns</key><integer>73682045000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>190765</integer>
+<key>cputime_ms_per_s</key><real>0.0683878</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.183666</real>
+<key>intr_wakeups</key><integer>1</integer>
+<key>intr_wakeups_per_s</key><real>0.358493</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>8248</integer>
+<key>name</key><string>sshd</string>
+<key>started_ns</key><integer>203501449000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>186285</integer>
+<key>cputime_ms_per_s</key><real>0.0667818</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.258147</real>
+<key>intr_wakeups</key><integer>0</integer>
+<key>intr_wakeups_per_s</key><real>0</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>205</integer>
+<key>name</key><string>UserEventAgent</string>
+<key>started_ns</key><integer>12002515000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>166112</integer>
+<key>cputime_ms_per_s</key><real>0.0595499</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.258067</real>
+<key>intr_wakeups</key><integer>1</integer>
+<key>intr_wakeups_per_s</key><real>0.358493</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>260</integer>
+<key>name</key><string>identityservices</string>
+<key>started_ns</key><integer>73617898000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>162945</integer>
+<key>cputime_ms_per_s</key><real>0.0584146</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.153935</real>
+<key>intr_wakeups</key><integer>1</integer>
+<key>intr_wakeups_per_s</key><real>0.358493</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>17</integer>
+<key>name</key><string>notifyd</string>
+<key>started_ns</key><integer>3409006000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>155892</integer>
+<key>cputime_ms_per_s</key><real>0.0558861</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.244599</real>
+<key>intr_wakeups</key><integer>2</integer>
+<key>intr_wakeups_per_s</key><real>0.716985</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>245</integer>
+<key>name</key><string>AirPlayUIAgent</string>
+<key>started_ns</key><integer>72643963000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>143964</integer>
+<key>cputime_ms_per_s</key><real>0.05161</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.296227</real>
+<key>intr_wakeups</key><integer>1</integer>
+<key>intr_wakeups_per_s</key><real>0.358493</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>249</integer>
+<key>name</key><string>NotificationCent</string>
+<key>started_ns</key><integer>73038119000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>138477</integer>
+<key>cputime_ms_per_s</key><real>0.049643</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.175401</real>
+<key>intr_wakeups</key><integer>2</integer>
+<key>intr_wakeups_per_s</key><real>0.716985</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>19</integer>
+<key>name</key><string>diskarbitrationd</string>
+<key>started_ns</key><integer>3425697000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>133407</integer>
+<key>cputime_ms_per_s</key><real>0.0478254</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.293141</real>
+<key>intr_wakeups</key><integer>1</integer>
+<key>intr_wakeups_per_s</key><real>0.358493</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>112</integer>
+<key>name</key><string>apsd</string>
+<key>started_ns</key><integer>8410568000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>133265</integer>
+<key>cputime_ms_per_s</key><real>0.0477745</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.188587</real>
+<key>intr_wakeups</key><integer>2</integer>
+<key>intr_wakeups_per_s</key><real>0.716985</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>254</integer>
+<key>name</key><string>ReportPanic</string>
+<key>started_ns</key><integer>73615217000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>128367</integer>
+<key>cputime_ms_per_s</key><real>0.0460186</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.208917</real>
+<key>intr_wakeups</key><integer>1</integer>
+<key>intr_wakeups_per_s</key><real>0.358493</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>227</integer>
+<key>name</key><string>Dock</string>
+<key>started_ns</key><integer>72337119000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>126905</integer>
+<key>cputime_ms_per_s</key><real>0.0454945</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.271258</real>
+<key>intr_wakeups</key><integer>1</integer>
+<key>intr_wakeups_per_s</key><real>0.358493</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>18</integer>
+<key>name</key><string>powerd</string>
+<key>started_ns</key><integer>3420877000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>125879</integer>
+<key>cputime_ms_per_s</key><real>0.0451267</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.211783</real>
+<key>intr_wakeups</key><integer>1</integer>
+<key>intr_wakeups_per_s</key><real>0.358493</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>250</integer>
+<key>name</key><string>com.apple.dock.e</string>
+<key>started_ns</key><integer>73506698000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>122322</integer>
+<key>cputime_ms_per_s</key><real>0.0438515</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.187816</real>
+<key>intr_wakeups</key><integer>1</integer>
+<key>intr_wakeups_per_s</key><real>0.358493</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>22</integer>
+<key>name</key><string>syslogd</string>
+<key>started_ns</key><integer>3438979000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>121278</integer>
+<key>cputime_ms_per_s</key><real>0.0434773</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.240629</real>
+<key>intr_wakeups</key><integer>1</integer>
+<key>intr_wakeups_per_s</key><real>0.358493</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>111</integer>
+<key>name</key><string>autofsd</string>
+<key>started_ns</key><integer>8410315000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>117402</integer>
+<key>cputime_ms_per_s</key><real>0.0420877</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.186803</real>
+<key>intr_wakeups</key><integer>1</integer>
+<key>intr_wakeups_per_s</key><real>0.358493</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>99</integer>
+<key>name</key><string>hidd</string>
+<key>started_ns</key><integer>8406630000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>116557</integer>
+<key>cputime_ms_per_s</key><real>0.0417848</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.206037</real>
+<key>intr_wakeups</key><integer>1</integer>
+<key>intr_wakeups_per_s</key><real>0.358493</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>248</integer>
+<key>name</key><string>ARDAgent</string>
+<key>started_ns</key><integer>72729660000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>116430</integer>
+<key>cputime_ms_per_s</key><real>0.0417393</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.216465</real>
+<key>intr_wakeups</key><integer>1</integer>
+<key>intr_wakeups_per_s</key><real>0.358493</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>80</integer>
+<key>name</key><string>stackshot</string>
+<key>started_ns</key><integer>8401291000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>115387</integer>
+<key>cputime_ms_per_s</key><real>0.0413654</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.276002</real>
+<key>intr_wakeups</key><integer>1</integer>
+<key>intr_wakeups_per_s</key><real>0.358493</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>139</integer>
+<key>name</key><string>rpcsvchost</string>
+<key>started_ns</key><integer>8709470000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>112551</integer>
+<key>cputime_ms_per_s</key><real>0.0403487</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.273076</real>
+<key>intr_wakeups</key><integer>1</integer>
+<key>intr_wakeups_per_s</key><real>0.358493</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>130</integer>
+<key>name</key><string>WindowServer</string>
+<key>started_ns</key><integer>8647186000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>110836</integer>
+<key>cputime_ms_per_s</key><real>0.0397339</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.239417</real>
+<key>intr_wakeups</key><integer>1</integer>
+<key>intr_wakeups_per_s</key><real>0.358493</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>522</integer>
+<key>name</key><string>suhelperd</string>
+<key>started_ns</key><integer>83826131000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>106108</integer>
+<key>cputime_ms_per_s</key><real>0.0380389</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.180627</real>
+<key>intr_wakeups</key><integer>1</integer>
+<key>intr_wakeups_per_s</key><real>0.358493</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>114</integer>
+<key>name</key><string>airportd</string>
+<key>started_ns</key><integer>8411228000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>105857</integer>
+<key>cputime_ms_per_s</key><real>0.0379489</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.192524</real>
+<key>intr_wakeups</key><integer>1</integer>
+<key>intr_wakeups_per_s</key><real>0.358493</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>261</integer>
+<key>name</key><string>helpd</string>
+<key>started_ns</key><integer>73618414000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>105836</integer>
+<key>cputime_ms_per_s</key><real>0.0379414</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.278535</real>
+<key>intr_wakeups</key><integer>1</integer>
+<key>intr_wakeups_per_s</key><real>0.358493</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>520</integer>
+<key>name</key><string>softwareupdated</string>
+<key>started_ns</key><integer>83764815000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>105122</integer>
+<key>cputime_ms_per_s</key><real>0.0376855</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.205019</real>
+<key>intr_wakeups</key><integer>1</integer>
+<key>intr_wakeups_per_s</key><real>0.358493</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>259</integer>
+<key>name</key><string>imagent</string>
+<key>started_ns</key><integer>73617455000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>103551</integer>
+<key>cputime_ms_per_s</key><real>0.0371223</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.18247</real>
+<key>intr_wakeups</key><integer>2</integer>
+<key>intr_wakeups_per_s</key><real>0.716985</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>211</integer>
+<key>name</key><string>fontd</string>
+<key>started_ns</key><integer>12043121000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>101409</integer>
+<key>cputime_ms_per_s</key><real>0.0363544</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.240639</real>
+<key>intr_wakeups</key><integer>1</integer>
+<key>intr_wakeups_per_s</key><real>0.358493</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>77</integer>
+<key>name</key><string>usbmuxd</string>
+<key>started_ns</key><integer>8400477000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>99736</integer>
+<key>cputime_ms_per_s</key><real>0.0357546</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.236936</real>
+<key>intr_wakeups</key><integer>1</integer>
+<key>intr_wakeups_per_s</key><real>0.358493</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>115</integer>
+<key>name</key><string>daemondo</string>
+<key>started_ns</key><integer>8411524000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>92755</integer>
+<key>cputime_ms_per_s</key><real>0.033252</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.290475</real>
+<key>intr_wakeups</key><integer>1</integer>
+<key>intr_wakeups_per_s</key><real>0.358493</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>231</integer>
+<key>name</key><string>coreaudiod</string>
+<key>started_ns</key><integer>72346216000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>89762</integer>
+<key>cputime_ms_per_s</key><real>0.032179</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.28248</real>
+<key>intr_wakeups</key><integer>1</integer>
+<key>intr_wakeups_per_s</key><real>0.358493</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>49</integer>
+<key>name</key><string>networkd</string>
+<key>started_ns</key><integer>5850133000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>89467</integer>
+<key>cputime_ms_per_s</key><real>0.0320733</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.232935</real>
+<key>intr_wakeups</key><integer>1</integer>
+<key>intr_wakeups_per_s</key><real>0.358493</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>160</integer>
+<key>name</key><string>ocspd</string>
+<key>started_ns</key><integer>10495124000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>87848</integer>
+<key>cputime_ms_per_s</key><real>0.0314929</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.239163</real>
+<key>intr_wakeups</key><integer>1</integer>
+<key>intr_wakeups_per_s</key><real>0.358493</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>23</integer>
+<key>name</key><string>opendirectoryd</string>
+<key>started_ns</key><integer>3453740000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>87217</integer>
+<key>cputime_ms_per_s</key><real>0.0312666</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.202231</real>
+<key>intr_wakeups</key><integer>1</integer>
+<key>intr_wakeups_per_s</key><real>0.358493</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>113</integer>
+<key>name</key><string>aosnotifyd</string>
+<key>started_ns</key><integer>8410931000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>86484</integer>
+<key>cputime_ms_per_s</key><real>0.0310039</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.280919</real>
+<key>intr_wakeups</key><integer>1</integer>
+<key>intr_wakeups_per_s</key><real>0.358493</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>273</integer>
+<key>name</key><string>secd</string>
+<key>started_ns</key><integer>73714612000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>86365</integer>
+<key>cputime_ms_per_s</key><real>0.0309612</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.303746</real>
+<key>intr_wakeups</key><integer>1</integer>
+<key>intr_wakeups_per_s</key><real>0.358493</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>218</integer>
+<key>name</key><string>CalendarAgent</string>
+<key>started_ns</key><integer>12364928000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>86062</integer>
+<key>cputime_ms_per_s</key><real>0.0308526</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.260138</real>
+<key>intr_wakeups</key><integer>1</integer>
+<key>intr_wakeups_per_s</key><real>0.358493</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>251</integer>
+<key>name</key><string>WiFiKeychainProx</string>
+<key>started_ns</key><integer>73614011000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>82732</integer>
+<key>cputime_ms_per_s</key><real>0.0296588</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.207731</real>
+<key>intr_wakeups</key><integer>1</integer>
+<key>intr_wakeups_per_s</key><real>0.358493</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>8288</integer>
+<key>name</key><string>sudo</string>
+<key>started_ns</key><integer>266624234000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>78962</integer>
+<key>cputime_ms_per_s</key><real>0.0283073</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.223145</real>
+<key>intr_wakeups</key><integer>0</integer>
+<key>intr_wakeups_per_s</key><real>0</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>237</integer>
+<key>name</key><string>ubd</string>
+<key>started_ns</key><integer>72509873000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>76577</integer>
+<key>cputime_ms_per_s</key><real>0.0274523</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.324732</real>
+<key>intr_wakeups</key><integer>1</integer>
+<key>intr_wakeups_per_s</key><real>0.358493</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>97</integer>
+<key>name</key><string>kdc</string>
+<key>started_ns</key><integer>8406021000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>70459</integer>
+<key>cputime_ms_per_s</key><real>0.025259</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.264281</real>
+<key>intr_wakeups</key><integer>1</integer>
+<key>intr_wakeups_per_s</key><real>0.358493</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>pid</key><integer>638</integer>
+<key>name</key><string>Python</string>
+<key>started_ns</key><integer>104013927000</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>67435</integer>
+<key>cputime_ms_per_s</key><real>0.0241749</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.235634</real>
+<key>intr_wakeups</key><integer>0</integer>
+<key>intr_wakeups_per_s</key><real>0</real>
+<key>idle_wakeups</key><integer>0</integer>
+<key>idle_wakeups_per_s</key><real>0</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>0</integer>
+  <key>wakeups_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+</array>
+<key>all_tasks</key>
+warning: time went backwards by 1418132621000000000 ns
+<dict>
+<key>pid</key><integer>-2</integer>
+<key>name</key><string>ALL_TASKS</string>
+<key>started_ns</key><integer>0</integer>
+<key>started_ns_reliable</key><false/>
+<key>cputime_ns</key><integer>2838933344</integer>
+<key>cputime_ms_per_s</key><real>1017.07</real>
+<key>cputime_ms_per_s_reliable</key><true/>
+<key>cputime_userland_ratio</key><real>0.894385</real>
+<key>intr_wakeups</key><integer>623</integer>
+<key>intr_wakeups_per_s</key><real>223.194</real>
+<key>idle_wakeups</key><integer>239</integer>
+<key>idle_wakeups_per_s</key><real>85.6233</real>
+<key>timer_wakeups</key>
+<array>
+<dict>
+  <key>interval_ns</key><integer>2000000</integer>
+  <key>wakeups</key><integer>2</integer>
+  <key>wakeups_per_s</key><real>0.716513</real>
+</dict>
+<dict>
+  <key>interval_ns</key><integer>5000000</integer>
+  <key>wakeups</key><integer>1</integer>
+  <key>wakeups_per_s</key><real>0.358257</real>
+</dict>
+</array>
+</dict>
+<key>network</key>
+<dict>
+<key>opackets</key><integer>2</integer>
+<key>opacket_rate</key><real>0.716513</real>
+<key>ipackets</key><integer>41</integer>
+<key>ipacket_rate</key><real>14.6885</real>
+<key>obytes</key><integer>168</integer>
+<key>obyte_rate</key><real>60.1871</real>
+<key>ibytes</key><integer>6304</integer>
+<key>ibyte_rate</key><real>2258.45</real>
+</dict>
+<key>disk</key>
+<dict>
+<key>rops_diff</key><integer>1</integer>
+<key>rops_per_s</key><real>0.358257</real>
+<key>wops_diff</key><integer>251</integer>
+<key>wops_per_s</key><real>89.9224</real>
+<key>rbytes_diff</key><integer>4096</integer>
+<key>rbytes_per_s</key><real>1467.42</real>
+<key>wbytes_diff</key><integer>65906688</integer>
+<key>wbytes_per_s</key><real>2.36115e+07</real>
+</dict>
+<key>interrupts</key>
+<array>
+<dict>
+<key>cpu</key><integer>0</integer>
+<key>vectors</key>
+<array>
+<dict>
+<key>name</key><string>CHN1</string>
+<key>vector</key><integer>79</integer>
+<key>events</key><integer>9</integer>
+<key>events_per_s</key><real>3.22431</real>
+</dict>
+<dict>
+<key>name</key><string>scsi</string>
+<key>vector</key><integer>81</integer>
+<key>events</key><integer>239</integer>
+<key>events_per_s</key><real>85.6233</real>
+</dict>
+<dict>
+<key>name</key><string>S2F0</string>
+<key>vector</key><integer>83</integer>
+<key>events</key><integer>34</integer>
+<key>events_per_s</key><real>12.1807</real>
+</dict>
+<dict>
+<key>name</key><string>TMR</string>
+<key>vector</key><integer>221</integer>
+<key>events</key><integer>108</integer>
+<key>events_per_s</key><real>38.6917</real>
+</dict>
+<dict>
+<key>name</key><string>IPI</string>
+<key>vector</key><integer>222</integer>
+<key>events</key><integer>92</integer>
+<key>events_per_s</key><real>32.9596</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>cpu</key><integer>1</integer>
+<key>vectors</key>
+<array>
+<dict>
+<key>name</key><string>TMR</string>
+<key>vector</key><integer>221</integer>
+<key>events</key><integer>126</integer>
+<key>events_per_s</key><real>45.1403</real>
+</dict>
+<dict>
+<key>name</key><string>IPI</string>
+<key>vector</key><integer>222</integer>
+<key>events</key><integer>183</integer>
+<key>events_per_s</key><real>65.561</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>cpu</key><integer>2</integer>
+<key>vectors</key>
+<array>
+<dict>
+<key>name</key><string>TMR</string>
+<key>vector</key><integer>221</integer>
+<key>events</key><integer>41</integer>
+<key>events_per_s</key><real>14.6885</real>
+</dict>
+<dict>
+<key>name</key><string>IPI</string>
+<key>vector</key><integer>222</integer>
+<key>events</key><integer>152</integer>
+<key>events_per_s</key><real>54.455</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>cpu</key><integer>3</integer>
+<key>vectors</key>
+<array>
+<dict>
+<key>name</key><string>TMR</string>
+<key>vector</key><integer>221</integer>
+<key>events</key><integer>97</integer>
+<key>events_per_s</key><real>34.7509</real>
+</dict>
+<dict>
+<key>name</key><string>IPI</string>
+<key>vector</key><integer>222</integer>
+<key>events</key><integer>72</integer>
+<key>events_per_s</key><real>25.7945</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>cpu</key><integer>4</integer>
+<key>vectors</key>
+<array>
+<dict>
+<key>name</key><string>TMR</string>
+<key>vector</key><integer>221</integer>
+<key>events</key><integer>10</integer>
+<key>events_per_s</key><real>3.58257</real>
+</dict>
+<dict>
+<key>name</key><string>IPI</string>
+<key>vector</key><integer>222</integer>
+<key>events</key><integer>104</integer>
+<key>events_per_s</key><real>37.2587</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>cpu</key><integer>5</integer>
+<key>vectors</key>
+<array>
+<dict>
+<key>name</key><string>TMR</string>
+<key>vector</key><integer>221</integer>
+<key>events</key><integer>94</integer>
+<key>events_per_s</key><real>33.6761</real>
+</dict>
+<dict>
+<key>name</key><string>IPI</string>
+<key>vector</key><integer>222</integer>
+<key>events</key><integer>140</integer>
+<key>events_per_s</key><real>50.1559</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>cpu</key><integer>6</integer>
+<key>vectors</key>
+<array>
+<dict>
+<key>name</key><string>TMR</string>
+<key>vector</key><integer>221</integer>
+<key>events</key><integer>53</integer>
+<key>events_per_s</key><real>18.9876</real>
+</dict>
+<dict>
+<key>name</key><string>IPI</string>
+<key>vector</key><integer>222</integer>
+<key>events</key><integer>150</integer>
+<key>events_per_s</key><real>53.7385</real>
+</dict>
+</array>
+</dict>
+<dict>
+<key>cpu</key><integer>7</integer>
+<key>vectors</key>
+<array>
+<dict>
+<key>name</key><string>TMR</string>
+<key>vector</key><integer>221</integer>
+<key>events</key><integer>1</integer>
+<key>events_per_s</key><real>0.358257</real>
+</dict>
+<dict>
+<key>name</key><string>IPI</string>
+<key>vector</key><integer>222</integer>
+<key>events</key><integer>28</integer>
+<key>events_per_s</key><real>10.0312</real>
+</dict>
+</array>
+</dict>
+</array>
+<key>processor</key>
+<dict>
+<key>llc_flushed_ratio</key><real>0</real>
+<key>packages</key>
+<array>
+<dict>
+<key>package</key><integer>0</integer>
+<key>c_state_ns</key><integer>0</integer>
+<key>c_state_ratio</key><real>0</real>
+<key>c_states</key>
+<array>
+<dict>
+<key>name</key><string>C2</string>
+<key>used_ns</key><integer>0</integer>
+<key>used_ratio</key><real>0</real>
+</dict>
+<dict>
+<key>name</key><string>C3</string>
+<key>used_ns</key><integer>0</integer>
+<key>used_ratio</key><real>0</real>
+</dict>
+<dict>
+<key>name</key><string>C6</string>
+<key>used_ns</key><integer>0</integer>
+<key>used_ratio</key><real>0</real>
+</dict>
+<dict>
+<key>name</key><string>C7</string>
+<key>used_ns</key><integer>0</integer>
+<key>used_ratio</key><real>0</real>
+</dict>
+</array>
+<key>cores</key>
+<array>
+<dict>
+<key>core</key><integer>0</integer>
+<key>c_state_ns</key><integer>0</integer>
+<key>c_state_ratio</key><real>0</real>
+<key>c_states</key>
+<array>
+<dict>
+<key>name</key><string>C3</string>
+<key>used_ns</key><integer>0</integer>
+<key>used_ratio</key><real>0</real>
+</dict>
+<dict>
+<key>name</key><string>C6</string>
+<key>used_ns</key><integer>0</integer>
+<key>used_ratio</key><real>0</real>
+</dict>
+<dict>
+<key>name</key><string>C7</string>
+<key>used_ns</key><integer>0</integer>
+<key>used_ratio</key><real>0</real>
+</dict>
+</array>
+<key>cpus</key>
+<array>
+<dict>
+<key>cpu</key><integer>0</integer>
+<key>duty_cycles</key>
+<array>
+<dict>
+<key>interval_ns</key><integer>16000</integer>
+<key>active_count</key><integer>162</integer>
+<key>active_per_s</key><real>58.0376</real>
+<key>idle_count</key><integer>4</integer>
+<key>idle_per_s</key><real>58.0376</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>32000</integer>
+<key>active_count</key><integer>6</integer>
+<key>active_per_s</key><real>2.14954</real>
+<key>idle_count</key><integer>15</integer>
+<key>idle_per_s</key><real>2.14954</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>64000</integer>
+<key>active_count</key><integer>28</integer>
+<key>active_per_s</key><real>10.0312</real>
+<key>idle_count</key><integer>104</integer>
+<key>idle_per_s</key><real>10.0312</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>128000</integer>
+<key>active_count</key><integer>62</integer>
+<key>active_per_s</key><real>22.2119</real>
+<key>idle_count</key><integer>8</integer>
+<key>idle_per_s</key><real>22.2119</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>256000</integer>
+<key>active_count</key><integer>23</integer>
+<key>active_per_s</key><real>8.2399</real>
+<key>idle_count</key><integer>5</integer>
+<key>idle_per_s</key><real>8.2399</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>512000</integer>
+<key>active_count</key><integer>2</integer>
+<key>active_per_s</key><real>0.716513</real>
+<key>idle_count</key><integer>4</integer>
+<key>idle_per_s</key><real>0.716513</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>1024000</integer>
+<key>active_count</key><integer>1</integer>
+<key>active_per_s</key><real>0.358257</real>
+<key>idle_count</key><integer>3</integer>
+<key>idle_per_s</key><real>0.358257</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>2048000</integer>
+<key>active_count</key><integer>0</integer>
+<key>active_per_s</key><real>0</real>
+<key>idle_count</key><integer>19</integer>
+<key>idle_per_s</key><real>0</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>4096000</integer>
+<key>active_count</key><integer>0</integer>
+<key>active_per_s</key><real>0</real>
+<key>idle_count</key><integer>20</integer>
+<key>idle_per_s</key><real>0</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>8192000</integer>
+<key>active_count</key><integer>0</integer>
+<key>active_per_s</key><real>0</real>
+<key>idle_count</key><integer>31</integer>
+<key>idle_per_s</key><real>0</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>16384000</integer>
+<key>active_count</key><integer>0</integer>
+<key>active_per_s</key><real>0</real>
+<key>idle_count</key><integer>29</integer>
+<key>idle_per_s</key><real>0</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>32768000</integer>
+<key>active_count</key><integer>0</integer>
+<key>active_per_s</key><real>0</real>
+<key>idle_count</key><integer>26</integer>
+<key>idle_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+</array>
+</dict>
+<dict>
+<key>core</key><integer>1</integer>
+<key>c_state_ns</key><integer>0</integer>
+<key>c_state_ratio</key><real>0</real>
+<key>c_states</key>
+<array>
+<dict>
+<key>name</key><string>C3</string>
+<key>used_ns</key><integer>0</integer>
+<key>used_ratio</key><real>0</real>
+</dict>
+<dict>
+<key>name</key><string>C6</string>
+<key>used_ns</key><integer>0</integer>
+<key>used_ratio</key><real>0</real>
+</dict>
+<dict>
+<key>name</key><string>C7</string>
+<key>used_ns</key><integer>0</integer>
+<key>used_ratio</key><real>0</real>
+</dict>
+</array>
+<key>cpus</key>
+<array>
+<dict>
+<key>cpu</key><integer>1</integer>
+<key>duty_cycles</key>
+<array>
+<dict>
+<key>interval_ns</key><integer>16000</integer>
+<key>active_count</key><integer>98</integer>
+<key>active_per_s</key><real>35.1092</real>
+<key>idle_count</key><integer>3</integer>
+<key>idle_per_s</key><real>35.1092</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>32000</integer>
+<key>active_count</key><integer>24</integer>
+<key>active_per_s</key><real>8.59816</real>
+<key>idle_count</key><integer>21</integer>
+<key>idle_per_s</key><real>8.59816</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>64000</integer>
+<key>active_count</key><integer>51</integer>
+<key>active_per_s</key><real>18.2711</real>
+<key>idle_count</key><integer>43</integer>
+<key>idle_per_s</key><real>18.2711</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>128000</integer>
+<key>active_count</key><integer>34</integer>
+<key>active_per_s</key><real>12.1807</real>
+<key>idle_count</key><integer>12</integer>
+<key>idle_per_s</key><real>12.1807</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>256000</integer>
+<key>active_count</key><integer>13</integer>
+<key>active_per_s</key><real>4.65734</real>
+<key>idle_count</key><integer>6</integer>
+<key>idle_per_s</key><real>4.65734</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>512000</integer>
+<key>active_count</key><integer>3</integer>
+<key>active_per_s</key><real>1.07477</real>
+<key>idle_count</key><integer>5</integer>
+<key>idle_per_s</key><real>1.07477</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>1024000</integer>
+<key>active_count</key><integer>2</integer>
+<key>active_per_s</key><real>0.716513</real>
+<key>idle_count</key><integer>4</integer>
+<key>idle_per_s</key><real>0.716513</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>2048000</integer>
+<key>active_count</key><integer>0</integer>
+<key>active_per_s</key><real>0</real>
+<key>idle_count</key><integer>19</integer>
+<key>idle_per_s</key><real>0</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>4096000</integer>
+<key>active_count</key><integer>0</integer>
+<key>active_per_s</key><real>0</real>
+<key>idle_count</key><integer>21</integer>
+<key>idle_per_s</key><real>0</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>8192000</integer>
+<key>active_count</key><integer>0</integer>
+<key>active_per_s</key><real>0</real>
+<key>idle_count</key><integer>25</integer>
+<key>idle_per_s</key><real>0</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>16384000</integer>
+<key>active_count</key><integer>0</integer>
+<key>active_per_s</key><real>0</real>
+<key>idle_count</key><integer>25</integer>
+<key>idle_per_s</key><real>0</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>32768000</integer>
+<key>active_count</key><integer>0</integer>
+<key>active_per_s</key><real>0</real>
+<key>idle_count</key><integer>29</integer>
+<key>idle_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+</array>
+</dict>
+<dict>
+<key>core</key><integer>2</integer>
+<key>c_state_ns</key><integer>0</integer>
+<key>c_state_ratio</key><real>0</real>
+<key>c_states</key>
+<array>
+<dict>
+<key>name</key><string>C3</string>
+<key>used_ns</key><integer>0</integer>
+<key>used_ratio</key><real>0</real>
+</dict>
+<dict>
+<key>name</key><string>C6</string>
+<key>used_ns</key><integer>0</integer>
+<key>used_ratio</key><real>0</real>
+</dict>
+<dict>
+<key>name</key><string>C7</string>
+<key>used_ns</key><integer>0</integer>
+<key>used_ratio</key><real>0</real>
+</dict>
+</array>
+<key>cpus</key>
+<array>
+<dict>
+<key>cpu</key><integer>2</integer>
+<key>duty_cycles</key>
+<array>
+<dict>
+<key>interval_ns</key><integer>16000</integer>
+<key>active_count</key><integer>79</integer>
+<key>active_per_s</key><real>28.3023</real>
+<key>idle_count</key><integer>5</integer>
+<key>idle_per_s</key><real>28.3023</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>32000</integer>
+<key>active_count</key><integer>25</integer>
+<key>active_per_s</key><real>8.95642</real>
+<key>idle_count</key><integer>29</integer>
+<key>idle_per_s</key><real>8.95642</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>64000</integer>
+<key>active_count</key><integer>39</integer>
+<key>active_per_s</key><real>13.972</real>
+<key>idle_count</key><integer>17</integer>
+<key>idle_per_s</key><real>13.972</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>128000</integer>
+<key>active_count</key><integer>26</integer>
+<key>active_per_s</key><real>9.31467</real>
+<key>idle_count</key><integer>6</integer>
+<key>idle_per_s</key><real>9.31467</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>256000</integer>
+<key>active_count</key><integer>11</integer>
+<key>active_per_s</key><real>3.94082</real>
+<key>idle_count</key><integer>7</integer>
+<key>idle_per_s</key><real>3.94082</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>512000</integer>
+<key>active_count</key><integer>1</integer>
+<key>active_per_s</key><real>0.358257</real>
+<key>idle_count</key><integer>2</integer>
+<key>idle_per_s</key><real>0.358257</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>1024000</integer>
+<key>active_count</key><integer>1</integer>
+<key>active_per_s</key><real>0.358257</real>
+<key>idle_count</key><integer>1</integer>
+<key>idle_per_s</key><real>0.358257</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>2048000</integer>
+<key>active_count</key><integer>0</integer>
+<key>active_per_s</key><real>0</real>
+<key>idle_count</key><integer>9</integer>
+<key>idle_per_s</key><real>0</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>4096000</integer>
+<key>active_count</key><integer>0</integer>
+<key>active_per_s</key><real>0</real>
+<key>idle_count</key><integer>12</integer>
+<key>idle_per_s</key><real>0</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>8192000</integer>
+<key>active_count</key><integer>0</integer>
+<key>active_per_s</key><real>0</real>
+<key>idle_count</key><integer>14</integer>
+<key>idle_per_s</key><real>0</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>16384000</integer>
+<key>active_count</key><integer>0</integer>
+<key>active_per_s</key><real>0</real>
+<key>idle_count</key><integer>26</integer>
+<key>idle_per_s</key><real>0</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>32768000</integer>
+<key>active_count</key><integer>0</integer>
+<key>active_per_s</key><real>0</real>
+<key>idle_count</key><integer>26</integer>
+<key>idle_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+</array>
+</dict>
+<dict>
+<key>core</key><integer>3</integer>
+<key>c_state_ns</key><integer>0</integer>
+<key>c_state_ratio</key><real>0</real>
+<key>c_states</key>
+<array>
+<dict>
+<key>name</key><string>C3</string>
+<key>used_ns</key><integer>0</integer>
+<key>used_ratio</key><real>0</real>
+</dict>
+<dict>
+<key>name</key><string>C6</string>
+<key>used_ns</key><integer>0</integer>
+<key>used_ratio</key><real>0</real>
+</dict>
+<dict>
+<key>name</key><string>C7</string>
+<key>used_ns</key><integer>0</integer>
+<key>used_ratio</key><real>0</real>
+</dict>
+</array>
+<key>cpus</key>
+<array>
+<dict>
+<key>cpu</key><integer>3</integer>
+<key>duty_cycles</key>
+<array>
+<dict>
+<key>interval_ns</key><integer>16000</integer>
+<key>active_count</key><integer>37</integer>
+<key>active_per_s</key><real>13.2555</real>
+<key>idle_count</key><integer>3</integer>
+<key>idle_per_s</key><real>13.2555</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>32000</integer>
+<key>active_count</key><integer>9</integer>
+<key>active_per_s</key><real>3.22431</real>
+<key>idle_count</key><integer>12</integer>
+<key>idle_per_s</key><real>3.22431</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>64000</integer>
+<key>active_count</key><integer>15</integer>
+<key>active_per_s</key><real>5.37385</real>
+<key>idle_count</key><integer>14</integer>
+<key>idle_per_s</key><real>5.37385</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>128000</integer>
+<key>active_count</key><integer>11</integer>
+<key>active_per_s</key><real>3.94082</real>
+<key>idle_count</key><integer>2</integer>
+<key>idle_per_s</key><real>3.94082</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>256000</integer>
+<key>active_count</key><integer>5</integer>
+<key>active_per_s</key><real>1.79128</real>
+<key>idle_count</key><integer>2</integer>
+<key>idle_per_s</key><real>1.79128</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>512000</integer>
+<key>active_count</key><integer>0</integer>
+<key>active_per_s</key><real>0</real>
+<key>idle_count</key><integer>1</integer>
+<key>idle_per_s</key><real>0</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>1024000</integer>
+<key>active_count</key><integer>0</integer>
+<key>active_per_s</key><real>0</real>
+<key>idle_count</key><integer>0</integer>
+<key>idle_per_s</key><real>0</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>2048000</integer>
+<key>active_count</key><integer>0</integer>
+<key>active_per_s</key><real>0</real>
+<key>idle_count</key><integer>1</integer>
+<key>idle_per_s</key><real>0</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>4096000</integer>
+<key>active_count</key><integer>0</integer>
+<key>active_per_s</key><real>0</real>
+<key>idle_count</key><integer>1</integer>
+<key>idle_per_s</key><real>0</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>8192000</integer>
+<key>active_count</key><integer>0</integer>
+<key>active_per_s</key><real>0</real>
+<key>idle_count</key><integer>4</integer>
+<key>idle_per_s</key><real>0</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>16384000</integer>
+<key>active_count</key><integer>0</integer>
+<key>active_per_s</key><real>0</real>
+<key>idle_count</key><integer>10</integer>
+<key>idle_per_s</key><real>0</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>32768000</integer>
+<key>active_count</key><integer>0</integer>
+<key>active_per_s</key><real>0</real>
+<key>idle_count</key><integer>11</integer>
+<key>idle_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+</array>
+</dict>
+</array>
+</dict>
+<dict>
+<key>package</key><integer>1</integer>
+<key>c_state_ns</key><integer>0</integer>
+<key>c_state_ratio</key><real>0</real>
+<key>c_states</key>
+<array>
+<dict>
+<key>name</key><string>C2</string>
+<key>used_ns</key><integer>0</integer>
+<key>used_ratio</key><real>0</real>
+</dict>
+<dict>
+<key>name</key><string>C3</string>
+<key>used_ns</key><integer>0</integer>
+<key>used_ratio</key><real>0</real>
+</dict>
+<dict>
+<key>name</key><string>C6</string>
+<key>used_ns</key><integer>0</integer>
+<key>used_ratio</key><real>0</real>
+</dict>
+<dict>
+<key>name</key><string>C7</string>
+<key>used_ns</key><integer>0</integer>
+<key>used_ratio</key><real>0</real>
+</dict>
+<dict>
+<key>name</key><string>C8</string>
+<key>used_ns</key><integer>0</integer>
+<key>used_ratio</key><real>0</real>
+</dict>
+<dict>
+<key>name</key><string>C9</string>
+<key>used_ns</key><integer>0</integer>
+<key>used_ratio</key><real>0</real>
+</dict>
+<dict>
+<key>name</key><string>C10</string>
+<key>used_ns</key><integer>0</integer>
+<key>used_ratio</key><real>0</real>
+</dict>
+</array>
+<key>cores</key>
+<array>
+<dict>
+<key>core</key><integer>4</integer>
+<key>c_state_ns</key><integer>0</integer>
+<key>c_state_ratio</key><real>0</real>
+<key>c_states</key>
+<array>
+<dict>
+<key>name</key><string>C3</string>
+<key>used_ns</key><integer>0</integer>
+<key>used_ratio</key><real>0</real>
+</dict>
+<dict>
+<key>name</key><string>C6</string>
+<key>used_ns</key><integer>0</integer>
+<key>used_ratio</key><real>0</real>
+</dict>
+<dict>
+<key>name</key><string>C7</string>
+<key>used_ns</key><integer>0</integer>
+<key>used_ratio</key><real>0</real>
+</dict>
+</array>
+<key>cpus</key>
+<array>
+<dict>
+<key>cpu</key><integer>4</integer>
+<key>duty_cycles</key>
+<array>
+<dict>
+<key>interval_ns</key><integer>16000</integer>
+<key>active_count</key><integer>23</integer>
+<key>active_per_s</key><real>8.2399</real>
+<key>idle_count</key><integer>4</integer>
+<key>idle_per_s</key><real>8.2399</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>32000</integer>
+<key>active_count</key><integer>10</integer>
+<key>active_per_s</key><real>3.58257</real>
+<key>idle_count</key><integer>21</integer>
+<key>idle_per_s</key><real>3.58257</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>64000</integer>
+<key>active_count</key><integer>30</integer>
+<key>active_per_s</key><real>10.7477</real>
+<key>idle_count</key><integer>13</integer>
+<key>idle_per_s</key><real>10.7477</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>128000</integer>
+<key>active_count</key><integer>22</integer>
+<key>active_per_s</key><real>7.88165</real>
+<key>idle_count</key><integer>5</integer>
+<key>idle_per_s</key><real>7.88165</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>256000</integer>
+<key>active_count</key><integer>9</integer>
+<key>active_per_s</key><real>3.22431</real>
+<key>idle_count</key><integer>3</integer>
+<key>idle_per_s</key><real>3.22431</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>512000</integer>
+<key>active_count</key><integer>1</integer>
+<key>active_per_s</key><real>0.358257</real>
+<key>idle_count</key><integer>0</integer>
+<key>idle_per_s</key><real>0.358257</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>1024000</integer>
+<key>active_count</key><integer>1</integer>
+<key>active_per_s</key><real>0.358257</real>
+<key>idle_count</key><integer>2</integer>
+<key>idle_per_s</key><real>0.358257</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>2048000</integer>
+<key>active_count</key><integer>0</integer>
+<key>active_per_s</key><real>0</real>
+<key>idle_count</key><integer>8</integer>
+<key>idle_per_s</key><real>0</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>4096000</integer>
+<key>active_count</key><integer>0</integer>
+<key>active_per_s</key><real>0</real>
+<key>idle_count</key><integer>6</integer>
+<key>idle_per_s</key><real>0</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>8192000</integer>
+<key>active_count</key><integer>1</integer>
+<key>active_per_s</key><real>0.358257</real>
+<key>idle_count</key><integer>11</integer>
+<key>idle_per_s</key><real>0.358257</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>16384000</integer>
+<key>active_count</key><integer>0</integer>
+<key>active_per_s</key><real>0</real>
+<key>idle_count</key><integer>3</integer>
+<key>idle_per_s</key><real>0</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>32768000</integer>
+<key>active_count</key><integer>0</integer>
+<key>active_per_s</key><real>0</real>
+<key>idle_count</key><integer>5</integer>
+<key>idle_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+</array>
+</dict>
+<dict>
+<key>core</key><integer>5</integer>
+<key>c_state_ns</key><integer>0</integer>
+<key>c_state_ratio</key><real>0</real>
+<key>c_states</key>
+<array>
+<dict>
+<key>name</key><string>C3</string>
+<key>used_ns</key><integer>0</integer>
+<key>used_ratio</key><real>0</real>
+</dict>
+<dict>
+<key>name</key><string>C6</string>
+<key>used_ns</key><integer>0</integer>
+<key>used_ratio</key><real>0</real>
+</dict>
+<dict>
+<key>name</key><string>C7</string>
+<key>used_ns</key><integer>0</integer>
+<key>used_ratio</key><real>0</real>
+</dict>
+</array>
+<key>cpus</key>
+<array>
+<dict>
+<key>cpu</key><integer>5</integer>
+<key>duty_cycles</key>
+<array>
+<dict>
+<key>interval_ns</key><integer>16000</integer>
+<key>active_count</key><integer>97</integer>
+<key>active_per_s</key><real>34.7509</real>
+<key>idle_count</key><integer>3</integer>
+<key>idle_per_s</key><real>34.7509</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>32000</integer>
+<key>active_count</key><integer>20</integer>
+<key>active_per_s</key><real>7.16513</real>
+<key>idle_count</key><integer>60</integer>
+<key>idle_per_s</key><real>7.16513</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>64000</integer>
+<key>active_count</key><integer>18</integer>
+<key>active_per_s</key><real>6.44862</real>
+<key>idle_count</key><integer>38</integer>
+<key>idle_per_s</key><real>6.44862</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>128000</integer>
+<key>active_count</key><integer>9</integer>
+<key>active_per_s</key><real>3.22431</real>
+<key>idle_count</key><integer>1</integer>
+<key>idle_per_s</key><real>3.22431</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>256000</integer>
+<key>active_count</key><integer>2</integer>
+<key>active_per_s</key><real>0.716513</real>
+<key>idle_count</key><integer>1</integer>
+<key>idle_per_s</key><real>0.716513</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>512000</integer>
+<key>active_count</key><integer>1</integer>
+<key>active_per_s</key><real>0.358257</real>
+<key>idle_count</key><integer>1</integer>
+<key>idle_per_s</key><real>0.358257</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>1024000</integer>
+<key>active_count</key><integer>0</integer>
+<key>active_per_s</key><real>0</real>
+<key>idle_count</key><integer>0</integer>
+<key>idle_per_s</key><real>0</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>2048000</integer>
+<key>active_count</key><integer>0</integer>
+<key>active_per_s</key><real>0</real>
+<key>idle_count</key><integer>4</integer>
+<key>idle_per_s</key><real>0</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>4096000</integer>
+<key>active_count</key><integer>0</integer>
+<key>active_per_s</key><real>0</real>
+<key>idle_count</key><integer>4</integer>
+<key>idle_per_s</key><real>0</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>8192000</integer>
+<key>active_count</key><integer>0</integer>
+<key>active_per_s</key><real>0</real>
+<key>idle_count</key><integer>6</integer>
+<key>idle_per_s</key><real>0</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>16384000</integer>
+<key>active_count</key><integer>0</integer>
+<key>active_per_s</key><real>0</real>
+<key>idle_count</key><integer>2</integer>
+<key>idle_per_s</key><real>0</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>32768000</integer>
+<key>active_count</key><integer>0</integer>
+<key>active_per_s</key><real>0</real>
+<key>idle_count</key><integer>4</integer>
+<key>idle_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+</array>
+</dict>
+<dict>
+<key>core</key><integer>6</integer>
+<key>c_state_ns</key><integer>0</integer>
+<key>c_state_ratio</key><real>0</real>
+<key>c_states</key>
+<array>
+<dict>
+<key>name</key><string>C3</string>
+<key>used_ns</key><integer>0</integer>
+<key>used_ratio</key><real>0</real>
+</dict>
+<dict>
+<key>name</key><string>C6</string>
+<key>used_ns</key><integer>0</integer>
+<key>used_ratio</key><real>0</real>
+</dict>
+<dict>
+<key>name</key><string>C7</string>
+<key>used_ns</key><integer>0</integer>
+<key>used_ratio</key><real>0</real>
+</dict>
+</array>
+<key>cpus</key>
+<array>
+<dict>
+<key>cpu</key><integer>6</integer>
+<key>duty_cycles</key>
+<array>
+<dict>
+<key>interval_ns</key><integer>16000</integer>
+<key>active_count</key><integer>78</integer>
+<key>active_per_s</key><real>27.944</real>
+<key>idle_count</key><integer>6</integer>
+<key>idle_per_s</key><real>27.944</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>32000</integer>
+<key>active_count</key><integer>40</integer>
+<key>active_per_s</key><real>14.3303</real>
+<key>idle_count</key><integer>29</integer>
+<key>idle_per_s</key><real>14.3303</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>64000</integer>
+<key>active_count</key><integer>31</integer>
+<key>active_per_s</key><real>11.106</real>
+<key>idle_count</key><integer>36</integer>
+<key>idle_per_s</key><real>11.106</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>128000</integer>
+<key>active_count</key><integer>31</integer>
+<key>active_per_s</key><real>11.106</real>
+<key>idle_count</key><integer>7</integer>
+<key>idle_per_s</key><real>11.106</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>256000</integer>
+<key>active_count</key><integer>2</integer>
+<key>active_per_s</key><real>0.716513</real>
+<key>idle_count</key><integer>2</integer>
+<key>idle_per_s</key><real>0.716513</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>512000</integer>
+<key>active_count</key><integer>1</integer>
+<key>active_per_s</key><real>0.358257</real>
+<key>idle_count</key><integer>1</integer>
+<key>idle_per_s</key><real>0.358257</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>1024000</integer>
+<key>active_count</key><integer>1</integer>
+<key>active_per_s</key><real>0.358257</real>
+<key>idle_count</key><integer>0</integer>
+<key>idle_per_s</key><real>0.358257</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>2048000</integer>
+<key>active_count</key><integer>1</integer>
+<key>active_per_s</key><real>0.358257</real>
+<key>idle_count</key><integer>6</integer>
+<key>idle_per_s</key><real>0.358257</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>4096000</integer>
+<key>active_count</key><integer>0</integer>
+<key>active_per_s</key><real>0</real>
+<key>idle_count</key><integer>10</integer>
+<key>idle_per_s</key><real>0</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>8192000</integer>
+<key>active_count</key><integer>0</integer>
+<key>active_per_s</key><real>0</real>
+<key>idle_count</key><integer>9</integer>
+<key>idle_per_s</key><real>0</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>16384000</integer>
+<key>active_count</key><integer>0</integer>
+<key>active_per_s</key><real>0</real>
+<key>idle_count</key><integer>21</integer>
+<key>idle_per_s</key><real>0</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>32768000</integer>
+<key>active_count</key><integer>0</integer>
+<key>active_per_s</key><real>0</real>
+<key>idle_count</key><integer>18</integer>
+<key>idle_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+</array>
+</dict>
+<dict>
+<key>core</key><integer>7</integer>
+<key>c_state_ns</key><integer>0</integer>
+<key>c_state_ratio</key><real>0</real>
+<key>c_states</key>
+<array>
+<dict>
+<key>name</key><string>C3</string>
+<key>used_ns</key><integer>0</integer>
+<key>used_ratio</key><real>0</real>
+</dict>
+<dict>
+<key>name</key><string>C6</string>
+<key>used_ns</key><integer>0</integer>
+<key>used_ratio</key><real>0</real>
+</dict>
+<dict>
+<key>name</key><string>C7</string>
+<key>used_ns</key><integer>0</integer>
+<key>used_ratio</key><real>0</real>
+</dict>
+</array>
+<key>cpus</key>
+<array>
+<dict>
+<key>cpu</key><integer>7</integer>
+<key>duty_cycles</key>
+<array>
+<dict>
+<key>interval_ns</key><integer>16000</integer>
+<key>active_count</key><integer>11</integer>
+<key>active_per_s</key><real>3.94082</real>
+<key>idle_count</key><integer>0</integer>
+<key>idle_per_s</key><real>3.94082</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>32000</integer>
+<key>active_count</key><integer>0</integer>
+<key>active_per_s</key><real>0</real>
+<key>idle_count</key><integer>2</integer>
+<key>idle_per_s</key><real>0</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>64000</integer>
+<key>active_count</key><integer>0</integer>
+<key>active_per_s</key><real>0</real>
+<key>idle_count</key><integer>2</integer>
+<key>idle_per_s</key><real>0</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>128000</integer>
+<key>active_count</key><integer>2</integer>
+<key>active_per_s</key><real>0.716513</real>
+<key>idle_count</key><integer>0</integer>
+<key>idle_per_s</key><real>0.716513</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>256000</integer>
+<key>active_count</key><integer>0</integer>
+<key>active_per_s</key><real>0</real>
+<key>idle_count</key><integer>0</integer>
+<key>idle_per_s</key><real>0</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>512000</integer>
+<key>active_count</key><integer>0</integer>
+<key>active_per_s</key><real>0</real>
+<key>idle_count</key><integer>0</integer>
+<key>idle_per_s</key><real>0</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>1024000</integer>
+<key>active_count</key><integer>1</integer>
+<key>active_per_s</key><real>0.358257</real>
+<key>idle_count</key><integer>1</integer>
+<key>idle_per_s</key><real>0.358257</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>2048000</integer>
+<key>active_count</key><integer>0</integer>
+<key>active_per_s</key><real>0</real>
+<key>idle_count</key><integer>1</integer>
+<key>idle_per_s</key><real>0</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>4096000</integer>
+<key>active_count</key><integer>0</integer>
+<key>active_per_s</key><real>0</real>
+<key>idle_count</key><integer>1</integer>
+<key>idle_per_s</key><real>0</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>8192000</integer>
+<key>active_count</key><integer>0</integer>
+<key>active_per_s</key><real>0</real>
+<key>idle_count</key><integer>0</integer>
+<key>idle_per_s</key><real>0</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>16384000</integer>
+<key>active_count</key><integer>0</integer>
+<key>active_per_s</key><real>0</real>
+<key>idle_count</key><integer>0</integer>
+<key>idle_per_s</key><real>0</real>
+</dict>
+<dict>
+<key>interval_ns</key><integer>32768000</integer>
+<key>active_count</key><integer>0</integer>
+<key>active_per_s</key><real>0</real>
+<key>idle_count</key><integer>1</integer>
+<key>idle_per_s</key><real>0</real>
+</dict>
+</array>
+</dict>
+</array>
+</dict>
+</array>
+</dict>
+</array>
+</dict>
+</dict>
+</plist>
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/proc_meminfo b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/proc_meminfo
new file mode 100644
index 0000000..600101a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/proc_meminfo
@@ -0,0 +1,43 @@
+MemTotal:       65897648 kB
+MemFree:         1084568 kB
+Buffers:         1221540 kB
+Cached:         38339400 kB
+SwapCached:       119564 kB
+Active:         38582600 kB
+Inactive:       20341624 kB
+Active(anon):   17113656 kB
+Inactive(anon):  2566996 kB
+Active(file):   21468944 kB
+Inactive(file): 17774628 kB
+Unevictable:       27280 kB
+Mlocked:           27280 kB
+SwapTotal:       1048572 kB
+SwapFree:              8 kB
+Dirty:               452 kB
+Writeback:             0 kB
+AnonPages:      19271176 kB
+Mapped:           724456 kB
+Shmem:            290412 kB
+Slab:            5047568 kB
+SReclaimable:    4773516 kB
+SUnreclaim:       274052 kB
+KernelStack:       21824 kB
+PageTables:       133852 kB
+NFS_Unstable:          0 kB
+Bounce:                0 kB
+WritebackTmp:          0 kB
+CommitLimit:    33997396 kB
+Committed_AS:   36218868 kB
+VmallocTotal:   34359738367 kB
+VmallocUsed:      338372 kB
+VmallocChunk:   34325659540 kB
+HardwareCorrupted:     0 kB
+AnonHugePages:   9410560 kB
+HugePages_Total:       0
+HugePages_Free:        0
+HugePages_Rsvd:        0
+HugePages_Surp:        0
+Hugepagesize:       2048 kB
+DirectMap4k:     6369212 kB
+DirectMap2M:    58564608 kB
+DirectMap1G:     2097152 kB
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/sample_perf_report_output.txt b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/sample_perf_report_output.txt
new file mode 100644
index 0000000..2f7dd93
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/sample_perf_report_output.txt
@@ -0,0 +1,10 @@
+Failed to open /data/app-lib/com.google.android.apps.chrome-2/libchrome.2016.0.so, continuing without symbols
+Failed to open /data/dalvik-cache/arm/data@app@com.google.android.apps.chrome-2.apk@classes.dex, continuing without symbols
+No kallsyms or vmlinux with build-id 3b63ca692cbb756f837744e061d02a790bf637d5 was found
+[kernel.kallsyms] with build id 3b63ca692cbb756f837744e061d02a790bf637d5 not found, continuing without symbols
+Failed to open /system/lib/libc.so, continuing without symbols
+Failed to open /system/lib/libm.so, continuing without symbols
+Failed to open /tmp/perf-18246.map, continuing without symbols
+Failed to open /system/lib/libart.so, continuing without symbols
+Failed to open /init, continuing without symbols
+Failed to open [vectors], continuing without symbols
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/sample_vtune_db_output b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/sample_vtune_db_output
new file mode 100644
index 0000000..71c6eaa
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/sample_vtune_db_output
@@ -0,0 +1,316 @@
+(lp0
+(V/data/app-lib/com.google.android.apps.chrome-1/libchrome.2019.0.so
+p1
+V/tmp/amplxe-tmp-dominikg/modules.android/libchrome.2019.0.so/00a9b3533743e080ab94e760bc891ab8/libchrome.2019.0.so
+p2
+tp3
+a(V/system/bin/app_process
+p4
+Ntp5
+a(V/system/bin/linker
+p6
+Ntp7
+a(V/system/lib/libcutils.so
+p8
+Ntp9
+a(V/system/lib/liblog.so
+p10
+Ntp11
+a(V/system/lib/libc.so
+p12
+V/tmp/amplxe-tmp-dominikg/modules.android/libc.so/abc82d050be774f342adeb7e91fe5a0a/libc.so
+p13
+tp14
+a(V/system/lib/libstdc++.so
+p15
+Ntp16
+a(V/system/lib/libm.so
+p17
+V/tmp/amplxe-tmp-dominikg/modules.android/libm.so/55c87bc955f6c7bf84cce8522fb1e88b/libm.so
+p18
+tp19
+a(V/system/lib/libutils.so
+p20
+Ntp21
+a(V/system/lib/libcorkscrew.so
+p22
+Ntp23
+a(V/system/lib/libgccdemangle.so
+p24
+Ntp25
+a(V/system/lib/libz.so
+p26
+Ntp27
+a(V/system/lib/libbinder.so
+p28
+Ntp29
+a(V/system/lib/libandroid_runtime.so
+p30
+Ntp31
+a(V/system/lib/libandroidfw.so
+p32
+Ntp33
+a(V/system/lib/libskia.so
+p34
+Ntp35
+a(V/system/lib/libemoji.so
+p36
+Ntp37
+a(V/system/lib/libjpeg.so
+p38
+Ntp39
+a(V/system/lib/libexpat.so
+p40
+Ntp41
+a(V/system/lib/libEGL.so
+p42
+Ntp43
+a(V/system/lib/libGLES_trace.so
+p44
+Ntp45
+a(V/system/lib/libstlport.so
+p46
+Ntp47
+a(V/system/lib/libGLESv2.so
+p48
+Ntp49
+a(V/system/lib/libnativehelper.so
+p50
+Ntp51
+a(V/system/lib/libnetutils.so
+p52
+Ntp53
+a(V/system/lib/libui.so
+p54
+Ntp55
+a(V/system/lib/libhardware.so
+p56
+Ntp57
+a(V/system/lib/libsync.so
+p58
+Ntp59
+a(V/system/lib/libgui.so
+p60
+Ntp61
+a(V/system/lib/libcamera_client.so
+p62
+Ntp63
+a(V/system/lib/libcamera_metadata.so
+p64
+Ntp65
+a(V/system/lib/libsqlite.so
+p66
+Ntp67
+a(V/system/lib/libicuuc.so
+p68
+Ntp69
+a(V/system/lib/libgabi++.so
+p70
+Ntp71
+a(V/system/lib/libicui18n.so
+p72
+Ntp73
+a(V/system/lib/libdvm.so
+p74
+V/tmp/amplxe-tmp-dominikg/modules.android/libdvm.so/8f6c9167e20b377dc90646a1bac0e201/libdvm.so
+p75
+tp76
+a(V/system/lib/libselinux.so
+p77
+Ntp78
+a(V/system/lib/libGLESv1_CM.so
+p79
+Ntp80
+a(V/system/lib/libETC1.so
+p81
+Ntp82
+a(V/system/lib/libhardware_legacy.so
+p83
+Ntp84
+a(V/system/lib/libwpa_client.so
+p85
+Ntp86
+a(V/system/lib/libsonivox.so
+p87
+Ntp88
+a(V/system/lib/libcrypto.so
+p89
+Ntp90
+a(V/system/lib/libssl.so
+p91
+Ntp92
+a(V/system/lib/libmedia.so
+p93
+Ntp94
+a(V/system/lib/libstagefright_foundation.so
+p95
+Ntp96
+a(V/system/lib/libaudioutils.so
+p97
+Ntp98
+a(V/system/lib/libspeexresampler.so
+p99
+Ntp100
+a(V/system/lib/libaudioresample.so
+p101
+Ntp102
+a(V/system/lib/libusbhost.so
+p103
+Ntp104
+a(V/system/lib/libharfbuzz_ng.so
+p105
+Ntp106
+a(V/system/lib/libhwui.so
+p107
+Ntp108
+a(V/system/lib/libRS.so
+p109
+Ntp110
+a(V/system/lib/libbcc.so
+p111
+Ntp112
+a(V/system/lib/libbcinfo.so
+p113
+Ntp114
+a(V/system/lib/libLLVM.so
+p115
+Ntp116
+a(V/system/lib/libRScpp.so
+p117
+Ntp118
+a(V/system/lib/libjavacore.so
+p119
+Ntp120
+a(V/system/lib/libdrmframework_jni.so
+p121
+Ntp122
+a(V/system/lib/libdrmframework.so
+p123
+Ntp124
+a(V/system/lib/libstagefright_yuv.so
+p125
+Ntp126
+a(V/system/lib/libdrm.so
+p127
+Ntp128
+a(V/system/lib/libmedia_jni.so
+p129
+Ntp130
+a(V/system/lib/libstagefright_omx.so
+p131
+Ntp132
+a(V/system/lib/libvorbisidec.so
+p133
+Ntp134
+a(V/system/lib/libva.so
+p135
+Ntp136
+a(V/system/lib/libva-android.so
+p137
+Ntp138
+a(V/system/lib/libva-tpi.so
+p139
+Ntp140
+a(V/system/lib/libva_videodecoder.so
+p141
+Ntp142
+a(V/system/lib/libmixvbp.so
+p143
+Ntp144
+a(V/system/lib/libasfparser.so
+p145
+Ntp146
+a(V/system/lib/libstagefright_enc_common.so
+p147
+Ntp148
+a(V/system/lib/libstagefright_avc_common.so
+p149
+Ntp150
+a(V/system/lib/libmultidisplay.so
+p151
+Ntp152
+a(V/system/lib/libsepdrm.so
+p153
+Ntp154
+a(V/system/lib/libmtp.so
+p155
+Ntp156
+a(V/system/lib/libexif.so
+p157
+Ntp158
+a(V/system/lib/libstagefright_amrnb_common.so
+p159
+Ntp160
+a(V/system/lib/libexif_jni.so
+p161
+Ntp162
+a(V/system/lib/libsoundpool.so
+p163
+Ntp164
+a(V/system/lib/libvideoeditor_jni.so
+p165
+Ntp166
+a(V/system/lib/libaudioflinger.so
+p167
+Ntp168
+a(V/system/lib/libcommon_time_client.so
+p169
+Ntp170
+a(V/system/lib/libnbaio.so
+p171
+Ntp172
+a(V/system/lib/libeffects.so
+p173
+Ntp174
+a(V/system/lib/libpowermanager.so
+p175
+Ntp176
+a(V/system/lib/libvideoeditor_core.so
+p177
+Ntp178
+a(V/system/lib/libvideoeditor_osal.so
+p179
+Ntp180
+a(V/system/lib/libvideoeditor_videofilters.so
+p181
+Ntp182
+a(V/system/lib/libvideoeditorplayer.so
+p183
+Ntp184
+a(V/system/lib/libsharedbuffer.so
+p185
+Ntp186
+a(V/system/lib/libva_videoencoder.so
+p187
+Ntp188
+a(V/system/lib/libintelmetadatabuffer.so
+p189
+Ntp190
+a(V/system/lib/librs_jni.so
+p191
+Ntp192
+a(V/system/lib/libandroid.so
+p193
+Ntp194
+a(V/system/lib/libharfbuzz.so
+p195
+Ntp196
+a(V/system/lib/libstagefright.so
+p197
+Ntp198
+a(V/system/lib/libwebcore.so
+p199
+Ntp200
+a(V/system/lib/libchromium_net.so
+p201
+Ntp202
+a(V/data/app-lib/com.google.android.apps.chrome-1/libchromium_android_linker.so
+p203
+Ntp204
+a(V/system/lib/libjnigraphics.so
+p205
+Ntp206
+a(V/dev/ashmem/dalvik-jit-code-cache (deleted)
+p207
+Ntp208
+a.
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/screen_3_frames.mov b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/screen_3_frames.mov
new file mode 100644
index 0000000..7a90f07
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/screen_3_frames.mov
Binary files differ
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/screenshot_test.html b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/screenshot_test.html
new file mode 100644
index 0000000..239e9a7
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/screenshot_test.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <style>
+  html, body {
+    margin: 0;
+    padding: 0;
+  }
+  #colorful {
+    width: 32px;
+    height: 32px;
+    background-color: rgb(217, 115, 43);
+  }
+  </style>
+</head>
+<body>
+  <div id="colorful"></div>
+</body>
+</html>
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/scrollable_page.html b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/scrollable_page.html
new file mode 100644
index 0000000..c50175b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/scrollable_page.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<html>
+  <head>
+    <style type="text/css">
+      body { height: 200vh; }
+    </style>
+  </head>
+  <body>
+    <!--
+    Below info are used in smoothness unittest only. use URL below to get the 
+    diagnostic info which is used when this page failed in test due to the 
+    scroll bar didn't show up.
+    scrollable_page.html?show_scroll_diagnosis_info
+    -->
+    <div id="info"></div>
+    <script>
+      var txt = "<h3>Screen info (Used for diagnosis):</h3>"
+                + "<p>Total width/height: " + screen.width + "*" + screen.height
+                + "</p><p>Available width/height: " + screen.availWidth + "*" 
+                + screen.availHeight + "</p><p>Color depth: "
+                + screen.colorDepth + "</p><p>Color resolution: "
+                + screen.pixelDepth + "</p><p>Body scrollable height: "
+                + document.body.scrollHeight + "</p><p>Body offset height: "
+                + document.body.offsetHeight + "</p><p>Body client height: "
+                + document.body.clientHeight + "</p><p>Window inner height: "
+                + window.innerHeight + "</p><p>Window device Pixel Ratio: "
+                + window.devicePixelRatio + "</p>";
+      if (window.location.search.substr(1) == "show_scroll_diagnosis_info") {
+          document.getElementById("info").innerHTML = txt;
+      }
+    </script>
+  </body>
+</html>
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/simple_app/background.js b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/simple_app/background.js
new file mode 100644
index 0000000..01412a3
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/simple_app/background.js
@@ -0,0 +1,6 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+chrome.app.window.create('main.html');
+chrome.app.window.create('second.html');
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/simple_app/main.html b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/simple_app/main.html
new file mode 100644
index 0000000..bb67633
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/simple_app/main.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+This is the simple telemetry webapp main page.
+</body>
+</html>
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/simple_app/manifest.json b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/simple_app/manifest.json
new file mode 100644
index 0000000..5275205
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/simple_app/manifest.json
@@ -0,0 +1,11 @@
+{
+  "description": "Simple Telemetry Test WebApp",
+  "name": "Simple Telemetry Test WebApp",
+  "app": {
+    "background": {
+      "scripts": ["background.js"]
+    }
+  },
+  "manifest_version": 2,
+  "version": "1.0"
+}
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/simple_app/second.html b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/simple_app/second.html
new file mode 100644
index 0000000..debc8fc
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/simple_app/second.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+This is the simple telemetry webapp secondary page.
+</body>
+</html>
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/simple_extension/background.js b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/simple_extension/background.js
new file mode 100644
index 0000000..303ef9f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/simple_extension/background.js
@@ -0,0 +1,8 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+var _testVar;
+function setTestVar(x) {
+  _testVar = x;
+}
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/simple_extension/manifest.json b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/simple_extension/manifest.json
new file mode 100644
index 0000000..d4bb6a3
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/simple_extension/manifest.json
@@ -0,0 +1,9 @@
+{
+  "description": "Simple test extension which has just a background script",
+  "name": "Simple Telemetry Test Extension",
+  "background": {
+    "scripts": ["background.js"]
+  },
+  "manifest_version": 2,
+  "version": "0.1"
+}
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/smaps b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/smaps
new file mode 100644
index 0000000..7902e52
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/smaps
@@ -0,0 +1,1065 @@
+00400000-004a2000 r-xp 00000000 08:01 2883606                            /bin/zsh4
+Size:                648 kB
+Rss:                 584 kB
+Pss:                  30 kB
+Shared_Clean:        584 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          584 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+006a1000-006a2000 r--p 000a1000 08:01 2883606                            /bin/zsh4
+Size:                  4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+006a2000-006a8000 rw-p 000a2000 08:01 2883606                            /bin/zsh4
+Size:                 24 kB
+Rss:                  24 kB
+Pss:                  24 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        24 kB
+Referenced:           24 kB
+Anonymous:            24 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+006a8000-006bc000 rw-p 00000000 00:00 0 
+Size:                 80 kB
+Rss:                  44 kB
+Pss:                  44 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        44 kB
+Referenced:           44 kB
+Anonymous:            44 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+0137c000-0168d000 rw-p 00000000 00:00 0                                  [heap]
+Size:               3140 kB
+Rss:                3116 kB
+Pss:                3116 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:      3116 kB
+Referenced:         3116 kB
+Anonymous:          3116 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff557c58000-7ff557c68000 r-xp 00000000 08:01 7082269                    /usr/lib/zsh/4.3.17/zsh/computil.so
+Size:                 64 kB
+Rss:                  52 kB
+Pss:                   4 kB
+Shared_Clean:         52 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           52 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff557c68000-7ff557e67000 ---p 00010000 08:01 7082269                    /usr/lib/zsh/4.3.17/zsh/computil.so
+Size:               2044 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff557e67000-7ff557e68000 r--p 0000f000 08:01 7082269                    /usr/lib/zsh/4.3.17/zsh/computil.so
+Size:                  4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff557e68000-7ff557e69000 rw-p 00010000 08:01 7082269                    /usr/lib/zsh/4.3.17/zsh/computil.so
+Size:                  4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff557e69000-7ff557e72000 r-xp 00000000 08:01 7082280                    /usr/lib/zsh/4.3.17/zsh/parameter.so
+Size:                 36 kB
+Rss:                  28 kB
+Pss:                   2 kB
+Shared_Clean:         28 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           28 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff557e72000-7ff558071000 ---p 00009000 08:01 7082280                    /usr/lib/zsh/4.3.17/zsh/parameter.so
+Size:               2044 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff558071000-7ff558072000 r--p 00008000 08:01 7082280                    /usr/lib/zsh/4.3.17/zsh/parameter.so
+Size:                  4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff558072000-7ff558073000 rw-p 00009000 08:01 7082280                    /usr/lib/zsh/4.3.17/zsh/parameter.so
+Size:                  4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff558073000-7ff55807a000 r-xp 00000000 08:01 7082272                    /usr/lib/zsh/4.3.17/zsh/zutil.so
+Size:                 28 kB
+Rss:                  24 kB
+Pss:                   1 kB
+Shared_Clean:         24 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           24 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff55807a000-7ff558279000 ---p 00007000 08:01 7082272                    /usr/lib/zsh/4.3.17/zsh/zutil.so
+Size:               2044 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff558279000-7ff55827a000 r--p 00006000 08:01 7082272                    /usr/lib/zsh/4.3.17/zsh/zutil.so
+Size:                  4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff55827a000-7ff55827b000 rw-p 00007000 08:01 7082272                    /usr/lib/zsh/4.3.17/zsh/zutil.so
+Size:                  4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff55827b000-7ff55829d000 r-xp 00000000 08:01 7082291                    /usr/lib/zsh/4.3.17/zsh/complete.so
+Size:                136 kB
+Rss:                 128 kB
+Pss:                   8 kB
+Shared_Clean:        128 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          128 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff55829d000-7ff55849c000 ---p 00022000 08:01 7082291                    /usr/lib/zsh/4.3.17/zsh/complete.so
+Size:               2044 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff55849c000-7ff55849d000 r--p 00021000 08:01 7082291                    /usr/lib/zsh/4.3.17/zsh/complete.so
+Size:                  4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff55849d000-7ff55849e000 rw-p 00022000 08:01 7082291                    /usr/lib/zsh/4.3.17/zsh/complete.so
+Size:                  4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff55849e000-7ff5584dd000 r-xp 00000000 08:01 7082277                    /usr/lib/zsh/4.3.17/zsh/zle.so
+Size:                252 kB
+Rss:                 224 kB
+Pss:                  13 kB
+Shared_Clean:        224 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          224 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff5584dd000-7ff5586dd000 ---p 0003f000 08:01 7082277                    /usr/lib/zsh/4.3.17/zsh/zle.so
+Size:               2048 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff5586dd000-7ff5586de000 r--p 0003f000 08:01 7082277                    /usr/lib/zsh/4.3.17/zsh/zle.so
+Size:                  4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff5586de000-7ff5586e5000 rw-p 00040000 08:01 7082277                    /usr/lib/zsh/4.3.17/zsh/zle.so
+Size:                 28 kB
+Rss:                  28 kB
+Pss:                  28 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        28 kB
+Referenced:           28 kB
+Anonymous:            28 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff5586e5000-7ff5586e7000 r-xp 00000000 08:01 7082293                    /usr/lib/zsh/4.3.17/zsh/terminfo.so
+Size:                  8 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          8 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff5586e7000-7ff5588e6000 ---p 00002000 08:01 7082293                    /usr/lib/zsh/4.3.17/zsh/terminfo.so
+Size:               2044 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff5588e6000-7ff5588e7000 r--p 00001000 08:01 7082293                    /usr/lib/zsh/4.3.17/zsh/terminfo.so
+Size:                  4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff5588e7000-7ff5588e8000 rw-p 00002000 08:01 7082293                    /usr/lib/zsh/4.3.17/zsh/terminfo.so
+Size:                  4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff5588e8000-7ff5588ec000 r-xp 00000000 08:01 6688948                    /usr/lib/libnss_cache.so.2.0 (deleted)
+Size:                 16 kB
+Rss:                  12 kB
+Pss:                   0 kB
+Shared_Clean:         12 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           12 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff5588ec000-7ff558aeb000 ---p 00004000 08:01 6688948                    /usr/lib/libnss_cache.so.2.0 (deleted)
+Size:               2044 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff558aeb000-7ff558aec000 r--p 00003000 08:01 6688948                    /usr/lib/libnss_cache.so.2.0 (deleted)
+Size:                  4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff558aec000-7ff558aed000 rw-p 00004000 08:01 6688948                    /usr/lib/libnss_cache.so.2.0 (deleted)
+Size:                  4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff558aed000-7ff558af9000 r-xp 00000000 08:01 41681577                   /lib/x86_64-linux-gnu/libnss_files-2.15.so
+Size:                 48 kB
+Rss:                  16 kB
+Pss:                   0 kB
+Shared_Clean:         16 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           16 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff558af9000-7ff558cf8000 ---p 0000c000 08:01 41681577                   /lib/x86_64-linux-gnu/libnss_files-2.15.so
+Size:               2044 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff558cf8000-7ff558cf9000 r--p 0000b000 08:01 41681577                   /lib/x86_64-linux-gnu/libnss_files-2.15.so
+Size:                  4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff558cf9000-7ff558cfa000 rw-p 0000c000 08:01 41681577                   /lib/x86_64-linux-gnu/libnss_files-2.15.so
+Size:                  4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff558d1e000-7ff559be1000 r--p 00000000 08:01 7209368                    /usr/lib/locale/locale-archive
+Size:              15116 kB
+Rss:                  76 kB
+Pss:                  10 kB
+Shared_Clean:         68 kB
+Shared_Dirty:          0 kB
+Private_Clean:         8 kB
+Private_Dirty:         0 kB
+Referenced:           76 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff559be1000-7ff559d96000 r-xp 00000000 08:01 41681428                   /lib/x86_64-linux-gnu/libc-2.15.so
+Size:               1748 kB
+Rss:                 696 kB
+Pss:                   7 kB
+Shared_Clean:        696 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          696 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff559d96000-7ff559f96000 ---p 001b5000 08:01 41681428                   /lib/x86_64-linux-gnu/libc-2.15.so
+Size:               2048 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff559f96000-7ff559f9a000 r--p 001b5000 08:01 41681428                   /lib/x86_64-linux-gnu/libc-2.15.so
+Size:                 16 kB
+Rss:                  16 kB
+Pss:                  16 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        16 kB
+Referenced:           16 kB
+Anonymous:            16 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff559f9a000-7ff559f9c000 rw-p 001b9000 08:01 41681428                   /lib/x86_64-linux-gnu/libc-2.15.so
+Size:                  8 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff559f9c000-7ff559fa1000 rw-p 00000000 00:00 0 
+Size:                 20 kB
+Rss:                  20 kB
+Pss:                  20 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        20 kB
+Referenced:           20 kB
+Anonymous:            20 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff559fa1000-7ff55a09c000 r-xp 00000000 08:01 41681674                   /lib/x86_64-linux-gnu/libm-2.15.so
+Size:               1004 kB
+Rss:                  64 kB
+Pss:                   1 kB
+Shared_Clean:         64 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           64 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff55a09c000-7ff55a29b000 ---p 000fb000 08:01 41681674                   /lib/x86_64-linux-gnu/libm-2.15.so
+Size:               2044 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff55a29b000-7ff55a29c000 r--p 000fa000 08:01 41681674                   /lib/x86_64-linux-gnu/libm-2.15.so
+Size:                  4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff55a29c000-7ff55a29d000 rw-p 000fb000 08:01 41681674                   /lib/x86_64-linux-gnu/libm-2.15.so
+Size:                  4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff55a29d000-7ff55a2bf000 r-xp 00000000 08:01 41681144                   /lib/x86_64-linux-gnu/libtinfo.so.5.9
+Size:                136 kB
+Rss:                 124 kB
+Pss:                   3 kB
+Shared_Clean:        124 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          124 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff55a2bf000-7ff55a4bf000 ---p 00022000 08:01 41681144                   /lib/x86_64-linux-gnu/libtinfo.so.5.9
+Size:               2048 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff55a4bf000-7ff55a4c3000 r--p 00022000 08:01 41681144                   /lib/x86_64-linux-gnu/libtinfo.so.5.9
+Size:                 16 kB
+Rss:                  16 kB
+Pss:                  16 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        16 kB
+Referenced:           16 kB
+Anonymous:            16 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff55a4c3000-7ff55a4c4000 rw-p 00026000 08:01 41681144                   /lib/x86_64-linux-gnu/libtinfo.so.5.9
+Size:                  4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff55a4c4000-7ff55a4c6000 r-xp 00000000 08:01 41681743                   /lib/x86_64-linux-gnu/libdl-2.15.so
+Size:                  8 kB
+Rss:                   8 kB
+Pss:                   0 kB
+Shared_Clean:          8 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            8 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff55a4c6000-7ff55a6c6000 ---p 00002000 08:01 41681743                   /lib/x86_64-linux-gnu/libdl-2.15.so
+Size:               2048 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff55a6c6000-7ff55a6c7000 r--p 00002000 08:01 41681743                   /lib/x86_64-linux-gnu/libdl-2.15.so
+Size:                  4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff55a6c7000-7ff55a6c8000 rw-p 00003000 08:01 41681743                   /lib/x86_64-linux-gnu/libdl-2.15.so
+Size:                  4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff55a6c8000-7ff55a6cc000 r-xp 00000000 08:01 41681169                   /lib/x86_64-linux-gnu/libcap.so.2.22
+Size:                 16 kB
+Rss:                  12 kB
+Pss:                   0 kB
+Shared_Clean:         12 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           12 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff55a6cc000-7ff55a8cb000 ---p 00004000 08:01 41681169                   /lib/x86_64-linux-gnu/libcap.so.2.22
+Size:               2044 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff55a8cb000-7ff55a8cc000 r--p 00003000 08:01 41681169                   /lib/x86_64-linux-gnu/libcap.so.2.22
+Size:                  4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff55a8cc000-7ff55a8cd000 rw-p 00004000 08:01 41681169                   /lib/x86_64-linux-gnu/libcap.so.2.22
+Size:                  4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff55a8cd000-7ff55a8ef000 r-xp 00000000 08:01 41681675                   /lib/x86_64-linux-gnu/ld-2.15.so
+Size:                136 kB
+Rss:                 120 kB
+Pss:                   1 kB
+Shared_Clean:        120 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:          120 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff55a930000-7ff55aac9000 rw-p 00000000 00:00 0 
+Size:               1636 kB
+Rss:                1636 kB
+Pss:                1636 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:      1636 kB
+Referenced:         1636 kB
+Anonymous:          1636 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff55aacc000-7ff55aad8000 rw-p 00000000 00:00 0 
+Size:                 48 kB
+Rss:                  48 kB
+Pss:                  48 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        48 kB
+Referenced:           48 kB
+Anonymous:            48 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff55aad9000-7ff55aae1000 rw-p 00000000 00:00 0 
+Size:                 32 kB
+Rss:                  32 kB
+Pss:                  32 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        32 kB
+Referenced:           32 kB
+Anonymous:            32 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff55aae2000-7ff55aae6000 rw-p 00000000 00:00 0 
+Size:                 16 kB
+Rss:                  16 kB
+Pss:                  16 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        16 kB
+Referenced:           16 kB
+Anonymous:            16 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff55aae6000-7ff55aaed000 r--s 00000000 08:01 6826621                    /usr/lib/x86_64-linux-gnu/gconv/gconv-modules.cache
+Size:                 28 kB
+Rss:                  24 kB
+Pss:                   0 kB
+Shared_Clean:         24 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:           24 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff55aaed000-7ff55aaef000 rw-p 00000000 00:00 0 
+Size:                  8 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff55aaef000-7ff55aaf0000 r--p 00022000 08:01 41681675                   /lib/x86_64-linux-gnu/ld-2.15.so
+Size:                  4 kB
+Rss:                   4 kB
+Pss:                   4 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         4 kB
+Referenced:            4 kB
+Anonymous:             4 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7ff55aaf0000-7ff55aaf2000 rw-p 00023000 08:01 41681675                   /lib/x86_64-linux-gnu/ld-2.15.so
+Size:                  8 kB
+Rss:                   8 kB
+Pss:                   8 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         8 kB
+Referenced:            8 kB
+Anonymous:             8 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7fff76a3d000-7fff76a5e000 rw-p 00000000 00:00 0                          [stack]
+Size:                136 kB
+Rss:                  84 kB
+Pss:                  84 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:        84 kB
+Referenced:           84 kB
+Anonymous:            84 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+7fff76bbb000-7fff76bbc000 r-xp 00000000 00:00 0                          [vdso]
+Size:                  4 kB
+Rss:                   4 kB
+Pss:                   0 kB
+Shared_Clean:          4 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            4 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
+ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
+Size:                  4 kB
+Rss:                   0 kB
+Pss:                   0 kB
+Shared_Clean:          0 kB
+Shared_Dirty:          0 kB
+Private_Clean:         0 kB
+Private_Dirty:         0 kB
+Referenced:            0 kB
+Anonymous:             0 kB
+AnonHugePages:         0 kB
+Swap:                  0 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Locked:                0 kB
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/stat b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/stat
new file mode 100644
index 0000000..a2c20d1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/stat
@@ -0,0 +1 @@
+12911 (chrome) S 16468 4631 4631 0 -1 4202560 145133 0 0 0 831 71 0 0 20 0 4 0 62513410 1025978368 20508 18446744073709551615 1 1 0 0 0 0 0 67112962 1073808616 18446744073709551615 0 0 17 23 0 0 0 0 0
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/status b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/status
new file mode 100644
index 0000000..cb3d4f6
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/status
@@ -0,0 +1,39 @@
+Name:	chrome
+State:	S (sleeping)
+Tgid:	12911
+Pid:	12911
+PPid:	16468
+TracerPid:	0
+Uid:	20989	20989	20989	20989
+Gid:	5000	5000	5000	5000
+FDSize:	64
+Groups:	4 20 24 25 44 46 104 128 499 5000 5001 5762 5825 66187 66609 66975 68604 74787 74990 75209 75279 76551 76613 76701 76830 77056 77281 78255 79910 79982 80665 80824 
+VmPeak:	 1025488 kB
+VmSize:	 1001932 kB
+VmLck:	       0 kB
+VmPin:	       0 kB
+VmHWM:	  141160 kB
+VmRSS:	   82032 kB
+VmData:	  660664 kB
+VmStk:	     136 kB
+VmExe:	   91844 kB
+VmLib:	   52088 kB
+VmPTE:	     852 kB
+VmSwap:	       0 kB
+Threads:	4
+SigQ:	0/514714
+SigPnd:	0000000000000000
+ShdPnd:	0000000000000000
+SigBlk:	0000000000000000
+SigIgn:	0000000004001002
+SigCgt:	00000001c00104e8
+CapInh:	0000000000000000
+CapPrm:	0000000000000000
+CapEff:	0000000000000000
+CapBnd:	ffffffffffffffff
+Cpus_allowed:	ffffffff
+Cpus_allowed_list:	0-31
+Mems_allowed:	00000000,00000003
+Mems_allowed_list:	0-1
+voluntary_ctxt_switches:	3061
+nonvoluntary_ctxt_switches:	730
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/status_nohwm b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/status_nohwm
new file mode 100644
index 0000000..5d94aa4
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/status_nohwm
@@ -0,0 +1,37 @@
+Name:	chrome
+State:	S (sleeping)
+Tgid:	12911
+Pid:	12911
+PPid:	16468
+TracerPid:	0
+Uid:	20989	20989	20989	20989
+Gid:	5000	5000	5000	5000
+FDSize:	64
+Groups:	4 20 24 25 44 46 104 128 499 5000 5001 5762 5825 66187 66609 66975 68604 74787 74990 75209 75279 76551 76613 76701 76830 77056 77281 78255 79910 79982 80665 80824 
+VmSize:	 1001932 kB
+VmLck:	       0 kB
+VmPin:	       0 kB
+VmRSS:	   82032 kB
+VmData:	  660664 kB
+VmStk:	     136 kB
+VmExe:	   91844 kB
+VmLib:	   52088 kB
+VmPTE:	     852 kB
+VmSwap:	       0 kB
+Threads:	4
+SigQ:	0/514714
+SigPnd:	0000000000000000
+ShdPnd:	0000000000000000
+SigBlk:	0000000000000000
+SigIgn:	0000000004001002
+SigCgt:	00000001c00104e8
+CapInh:	0000000000000000
+CapPrm:	0000000000000000
+CapEff:	0000000000000000
+CapBnd:	ffffffffffffffff
+Cpus_allowed:	ffffffff
+Cpus_allowed_list:	0-31
+Mems_allowed:	00000000,00000003
+Mems_allowed_list:	0-1
+voluntary_ctxt_switches:	3063
+nonvoluntary_ctxt_switches:	730
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/system_stub_test_module.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/system_stub_test_module.py
new file mode 100644
index 0000000..97e3ef3
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/system_stub_test_module.py
@@ -0,0 +1,7 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+class SystemStubTest(object):
+  @staticmethod
+  def TestOpen(file_path):
+    return open(file_path)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/test_page_sets/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/test_page_sets/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/test_page_sets/__init__.py
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/test_page_sets/data/example_domain.json b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/test_page_sets/data/example_domain.json
new file mode 100644
index 0000000..9628451
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/test_page_sets/data/example_domain.json
@@ -0,0 +1,12 @@
+{
+    "description": "Describes the Web Page Replay archives for a user story set. Don't edit by hand! Use record_wpr for updating.", 
+    "platform_specific": true,
+    "archives": {
+        "http://www.example.com": {
+          "DEFAULT": "example_domain_001.wpr"
+        },
+        "https://www.example.com":{
+          "DEFAULT": "example_domain_001.wpr"
+        }
+    }
+}
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/test_page_sets/data/example_domain_001.wpr.sha1 b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/test_page_sets/data/example_domain_001.wpr.sha1
new file mode 100644
index 0000000..fdfac39
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/test_page_sets/data/example_domain_001.wpr.sha1
@@ -0,0 +1 @@
+5e49b8152e40b5df427a8e73062045ddde2edcb8
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/test_page_sets/example_domain.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/test_page_sets/example_domain.py
new file mode 100644
index 0000000..c56e0ae
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/test_page_sets/example_domain.py
@@ -0,0 +1,16 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry import story
+from telemetry.page import page
+
+
+class ExampleDomainPageSet(story.StorySet):
+  def __init__(self):
+    super(ExampleDomainPageSet, self).__init__(
+      archive_data_file='data/example_domain.json',
+      cloud_storage_bucket=story.PUBLIC_BUCKET)
+
+    self.AddStory(page.Page('http://www.example.com', self))
+    self.AddStory(page.Page('https://www.example.com', self))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/test_png.png b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/test_png.png
new file mode 100644
index 0000000..3aaf03b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/test_png.png
Binary files differ
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/test_png_2.png b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/test_png_2.png
new file mode 100644
index 0000000..f44a4a6
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/test_png_2.png
Binary files differ
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/ubuntu-saucy-lsb-release b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/ubuntu-saucy-lsb-release
new file mode 100644
index 0000000..382258e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/ubuntu-saucy-lsb-release
@@ -0,0 +1,4 @@
+DISTRIB_ID=Ubuntu
+DISTRIB_RELEASE=13.10
+DISTRIB_CODENAME=saucy
+DISTRIB_DESCRIPTION="Ubuntu 13.10"
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/vid.mp4 b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/vid.mp4
new file mode 100644
index 0000000..117ff0c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/vid.mp4
Binary files differ
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/video_test.html b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/video_test.html
new file mode 100644
index 0000000..ce8529c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/video_test.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    <video id="video_1" src="bear.webm" controls></video>
+    <audio id="audio_1" src="bear.webm" controls></audio>
+  </body>
+</html>
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/webview_app/background.js b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/webview_app/background.js
new file mode 100644
index 0000000..5f284a8
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/webview_app/background.js
@@ -0,0 +1,5 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+chrome.app.window.create('main.html');
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/webview_app/main.html b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/webview_app/main.html
new file mode 100644
index 0000000..0294246
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/webview_app/main.html
@@ -0,0 +1,7 @@
+<html>
+<body>
+This is the simple telemetry webapp main page with a &lt;webview&gt; element.
+<webview id="foo" src="data:text/html;charset=utf-8,<html><body><input id='test_input_id' type='text'></body></html>" style="width:640px; height:480px" autosize="on" minwidth="576" minheight="432">
+</webview>
+</body>
+</html>
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/webview_app/manifest.json b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/webview_app/manifest.json
new file mode 100644
index 0000000..b24438e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/testing/webview_app/manifest.json
@@ -0,0 +1,14 @@
+{
+  "description": "Simple Telemetry Test WebApp Containing <webview> Element",
+  "name": "Simple <webview> Telemetry Test WebApp",
+  "app": {
+    "background": {
+      "scripts": ["background.js"]
+    }
+  },
+  "permissions": [
+    "webview"
+  ],
+  "manifest_version": 2,
+  "version": "1.0"
+}
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/__init__.py
new file mode 100644
index 0000000..50b23df
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/__init__.py
@@ -0,0 +1,3 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/atexit_with_log.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/atexit_with_log.py
new file mode 100644
index 0000000..6dd7967
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/atexit_with_log.py
@@ -0,0 +1,16 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import atexit
+import logging
+
+def _WrapFunction(function):
+  def _wrapped_function(*args, **kwargs):
+    logging.debug('Try running %s', repr(function))
+    function(*args, **kwargs)
+    logging.debug('Did run %s', repr(function))
+  return _wrapped_function
+
+def Register(function, *args, **kwargs):
+  atexit.register(_WrapFunction(function), *args, **kwargs)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/binary_manager.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/binary_manager.py
new file mode 100644
index 0000000..27f1b1d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/binary_manager.py
@@ -0,0 +1,153 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+import os
+
+from py_utils import binary_manager
+from py_utils import dependency_util
+import dependency_manager
+from devil import devil_env
+
+from telemetry.core import exceptions
+from telemetry.core import util
+from telemetry.core import platform as platform_module
+
+
+TELEMETRY_PROJECT_CONFIG = os.path.join(
+    util.GetTelemetryDir(), 'telemetry', 'internal', 'binary_dependencies.json')
+
+
+CHROME_BINARY_CONFIG = os.path.join(util.GetCatapultDir(), 'common', 'py_utils',
+                                    'py_utils', 'chrome_binaries.json')
+
+
+BATTOR_BINARY_CONFIG = os.path.join(util.GetCatapultDir(), 'common', 'battor',
+                                    'battor', 'battor_binary_dependencies.json')
+
+
+NoPathFoundError = dependency_manager.NoPathFoundError
+CloudStorageError = dependency_manager.CloudStorageError
+
+
+_binary_manager = None
+
+
+def NeedsInit():
+  return not _binary_manager
+
+
+def InitDependencyManager(client_configs):
+  global _binary_manager
+  if _binary_manager:
+    raise exceptions.InitializationError(
+        'Trying to re-initialize the binary manager with config %s'
+        % client_configs)
+  configs = []
+  if client_configs:
+    configs += client_configs
+  configs += [TELEMETRY_PROJECT_CONFIG, CHROME_BINARY_CONFIG]
+  _binary_manager = binary_manager.BinaryManager(configs)
+
+  devil_env.config.Initialize()
+
+
+def FetchPath(binary_name, arch, os_name, os_version=None):
+  """ Return a path to the appropriate executable for <binary_name>, downloading
+      from cloud storage if needed, or None if it cannot be found.
+  """
+  if _binary_manager is None:
+    raise exceptions.InitializationError(
+        'Called FetchPath with uninitialized binary manager.')
+  return _binary_manager.FetchPath(binary_name, os_name, arch, os_version)
+
+
+def LocalPath(binary_name, arch, os_name, os_version=None):
+  """ Return a local path to the given binary name, or None if an executable
+      cannot be found. Will not download the executable.
+      """
+  if _binary_manager is None:
+    raise exceptions.InitializationError(
+        'Called LocalPath with uninitialized binary manager.')
+  return _binary_manager.LocalPath(binary_name, os_name, arch, os_version)
+
+
+def FetchBinaryDependencies(platform, client_configs,
+                          fetch_reference_chrome_binary):
+  """ Fetch all binary dependenencies for the given |platform|.
+
+  Note: we don't fetch browser binaries by default because the size of the
+  binary is about 2Gb, and it requires cloud storage permission to
+  chrome-telemetry bucket.
+
+  Args:
+    platform: an instance of telemetry.core.platform
+    client_configs: A list of paths (string) to dependencies json files.
+    fetch_reference_chrome_binary: whether to fetch reference chrome binary for
+      the given platform.
+  """
+  configs = [
+      dependency_manager.BaseConfig(TELEMETRY_PROJECT_CONFIG),
+      dependency_manager.BaseConfig(BATTOR_BINARY_CONFIG)
+  ]
+  dep_manager = dependency_manager.DependencyManager(configs)
+  target_platform = '%s_%s' % (platform.GetOSName(), platform.GetArchName())
+  dep_manager.PrefetchPaths(target_platform)
+
+  host_platform = None
+  fetch_devil_deps = False
+  if platform.GetOSName() == 'android':
+    host_platform = '%s_%s' % (
+        platform_module.GetHostPlatform().GetOSName(),
+        platform_module.GetHostPlatform().GetArchName())
+    dep_manager.PrefetchPaths(host_platform)
+    # TODO(aiolos): this is a hack to prefetch the devil deps.
+    if host_platform == 'linux_x86_64':
+      fetch_devil_deps = True
+    else:
+      logging.error('Devil only supports 64 bit linux as a host platform. '
+                    'Android tests may fail.')
+
+  if fetch_reference_chrome_binary:
+    _FetchReferenceBrowserBinary(platform)
+
+  # For now, handle client config separately because the BUILD.gn & .isolate of
+  # telemetry tests in chromium src failed to include the files specified in its
+  # client config.
+  # (https://github.com/catapult-project/catapult/issues/2192)
+  # For now this is ok because the client configs usually don't include cloud
+  # storage infos.
+  # TODO(nednguyen): remove the logic of swallowing exception once the issue is
+  # fixed on Chromium side.
+  if client_configs:
+    manager = dependency_manager.DependencyManager(
+        list(dependency_manager.BaseConfig(c) for c in client_configs))
+    try:
+      manager.PrefetchPaths(target_platform)
+      if host_platform is not None:
+        manager.PrefetchPaths(host_platform)
+
+    except dependency_manager.NoPathFoundError as e:
+      logging.error('Error when trying to prefetch paths for %s: %s',
+                    target_platform, e.message)
+
+  if fetch_devil_deps:
+    devil_env.config.Initialize()
+    devil_env.config.PrefetchPaths(arch=platform.GetArchName())
+    devil_env.config.PrefetchPaths()
+
+
+def _FetchReferenceBrowserBinary(platform):
+  os_name = platform.GetOSName()
+  arch_name = platform.GetArchName()
+  manager = binary_manager.BinaryManager(
+             [CHROME_BINARY_CONFIG])
+  if os_name == 'android':
+    os_version = dependency_util.GetChromeApkOsVersion(
+        platform.GetOSVersionName())
+    manager.FetchPath(
+        'chrome_stable', os_name, arch_name, os_version)
+  else:
+    manager.FetchPath(
+        'chrome_stable', os_name, arch_name)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/binary_manager_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/binary_manager_unittest.py
new file mode 100644
index 0000000..acf63b4
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/binary_manager_unittest.py
@@ -0,0 +1,52 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from telemetry.core import exceptions
+from telemetry.internal.util import binary_manager
+import mock
+
+
+class BinaryManagerTest(unittest.TestCase):
+  def setUp(self):
+    # We need to preserve the real initialized dependecny_manager.
+    self.actual_binary_manager = binary_manager._binary_manager
+    binary_manager._binary_manager = None
+
+  def tearDown(self):
+    binary_manager._binary_manager = self.actual_binary_manager
+
+  def testReinitialization(self):
+    binary_manager.InitDependencyManager(None)
+    self.assertRaises(exceptions.InitializationError,
+                      binary_manager.InitDependencyManager, None)
+
+  @mock.patch('py_utils.binary_manager.BinaryManager')
+  def testFetchPathInitialized(self, binary_manager_mock):
+    expected = [mock.call.binary_manager.BinaryManager(
+                   ['base_config_object']),
+                mock.call.binary_manager.BinaryManager().FetchPath(
+                    'dep', 'plat_arch')]
+    binary_manager.InitDependencyManager(None)
+    binary_manager.FetchPath('dep', 'plat', 'arch')
+    binary_manager_mock.assert_call_args(expected)
+
+  def testFetchPathUninitialized(self):
+    self.assertRaises(exceptions.InitializationError,
+                      binary_manager.FetchPath, 'dep', 'plat', 'arch')
+
+  @mock.patch('py_utils.binary_manager.BinaryManager')
+  def testLocalPathInitialized(self, binary_manager_mock):
+    expected = [mock.call.binary_manager.BinaryManager(
+                   ['base_config_object']),
+                mock.call.binary_manager.BinaryManager().LocalPath(
+                    'dep', 'plat_arch')]
+    binary_manager.InitDependencyManager(None)
+    binary_manager.LocalPath('dep', 'plat', 'arch')
+    binary_manager_mock.assert_call_args(expected)
+
+  def testLocalPathUninitialized(self):
+    self.assertRaises(exceptions.InitializationError,
+                      binary_manager.LocalPath, 'dep', 'plat', 'arch')
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/camel_case.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/camel_case.py
new file mode 100644
index 0000000..9a76890
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/camel_case.py
@@ -0,0 +1,30 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import re
+
+
+def ToUnderscore(obj):
+  """Converts a string, list, or dict from camelCase to lower_with_underscores.
+
+  Descends recursively into lists and dicts, converting all dict keys.
+  Returns a newly allocated object of the same structure as the input.
+  """
+  if isinstance(obj, basestring):
+    return re.sub('(?!^)([A-Z]+)', r'_\1', obj).lower()
+
+  elif isinstance(obj, list):
+    return [ToUnderscore(item) for item in obj]
+
+  elif isinstance(obj, dict):
+    output = {}
+    for k, v in obj.iteritems():
+      if isinstance(v, list) or isinstance(v, dict):
+        output[ToUnderscore(k)] = ToUnderscore(v)
+      else:
+        output[ToUnderscore(k)] = v
+    return output
+
+  else:
+    return obj
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/camel_case_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/camel_case_unittest.py
new file mode 100644
index 0000000..9ba4d3a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/camel_case_unittest.py
@@ -0,0 +1,50 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from telemetry.internal.util import camel_case
+
+
+class CamelCaseTest(unittest.TestCase):
+
+  def testString(self):
+    self.assertEqual(camel_case.ToUnderscore('camelCase'), 'camel_case')
+    self.assertEqual(camel_case.ToUnderscore('CamelCase'), 'camel_case')
+    self.assertEqual(camel_case.ToUnderscore('Camel2Case'), 'camel2_case')
+    self.assertEqual(camel_case.ToUnderscore('Camel2Case2'), 'camel2_case2')
+    self.assertEqual(camel_case.ToUnderscore('2012Q3'), '2012_q3')
+
+  def testList(self):
+    camel_case_list = ['CamelCase', ['NestedList']]
+    underscore_list = ['camel_case', ['nested_list']]
+    self.assertEqual(camel_case.ToUnderscore(camel_case_list), underscore_list)
+
+  def testDict(self):
+    camel_case_dict = {
+        'gpu': {
+            'vendorId': 1000,
+            'deviceId': 2000,
+            'vendorString': 'aString',
+            'deviceString': 'bString'},
+        'secondaryGpus': [
+            {'vendorId': 3000, 'deviceId': 4000,
+             'vendorString': 'k', 'deviceString': 'l'}
+        ]
+    }
+    underscore_dict = {
+        'gpu': {
+            'vendor_id': 1000,
+            'device_id': 2000,
+            'vendor_string': 'aString',
+            'device_string': 'bString'},
+        'secondary_gpus': [
+            {'vendor_id': 3000, 'device_id': 4000,
+             'vendor_string': 'k', 'device_string': 'l'}
+        ]
+    }
+    self.assertEqual(camel_case.ToUnderscore(camel_case_dict), underscore_dict)
+
+  def testOther(self):
+    self.assertEqual(camel_case.ToUnderscore(self), self)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/classes.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/classes.py
new file mode 100644
index 0000000..0f90a06
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/classes.py
@@ -0,0 +1,22 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import inspect
+
+
+def IsDirectlyConstructable(cls):
+  """Returns True if instance of |cls| can be construct without arguments."""
+  assert inspect.isclass(cls)
+  if not hasattr(cls, '__init__'):
+    # Case |class A: pass|.
+    return True
+  if cls.__init__ is object.__init__:
+    # Case |class A(object): pass|.
+    return True
+  # Case |class (object):| with |__init__| other than |object.__init__|.
+  args, _, _, defaults = inspect.getargspec(cls.__init__)
+  if defaults is None:
+    defaults = ()
+  # Return true if |self| is only arg without a default.
+  return len(args) == len(defaults) + 1
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/classes_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/classes_unittest.py
new file mode 100644
index 0000000..dc7ac51
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/classes_unittest.py
@@ -0,0 +1,45 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from telemetry.internal.util import classes
+
+
+class ClassWithoutInitDefOne: # pylint: disable=old-style-class, no-init
+  pass
+
+
+class ClassWithoutInitDefTwo(object):
+  pass
+
+
+class ClassWhoseInitOnlyHasSelf(object):
+  def __init__(self):
+    pass
+
+
+class ClassWhoseInitWithDefaultArguments(object):
+  def __init__(self, dog=1, cat=None, cow=None, fud='a'):
+    pass
+
+
+class ClassWhoseInitWithDefaultArgumentsAndNonDefaultArguments(object):
+  def __init__(self, x, dog=1, cat=None, fish=None, fud='a'):
+    pass
+
+
+class ClassesUnitTest(unittest.TestCase):
+
+  def testIsDirectlyConstructableReturnsTrue(self):
+    self.assertTrue(classes.IsDirectlyConstructable(ClassWithoutInitDefOne))
+    self.assertTrue(classes.IsDirectlyConstructable(ClassWithoutInitDefTwo))
+    self.assertTrue(classes.IsDirectlyConstructable(ClassWhoseInitOnlyHasSelf))
+    self.assertTrue(
+        classes.IsDirectlyConstructable(ClassWhoseInitWithDefaultArguments))
+
+  def testIsDirectlyConstructableReturnsFalse(self):
+    self.assertFalse(
+        classes.IsDirectlyConstructable(
+            ClassWhoseInitWithDefaultArgumentsAndNonDefaultArguments))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/command_line.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/command_line.py
new file mode 100644
index 0000000..2a0d603
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/command_line.py
@@ -0,0 +1,121 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import argparse
+import optparse
+
+from telemetry.internal.util import camel_case
+
+
+class ArgumentHandlerMixIn(object):
+  """A structured way to handle command-line arguments.
+
+  In AddCommandLineArgs, add command-line arguments.
+  In ProcessCommandLineArgs, validate them and store them in a private class
+  variable. This way, each class encapsulates its own arguments, without needing
+  to pass an arguments object around everywhere.
+  """
+
+  @classmethod
+  def AddCommandLineArgs(cls, parser):
+    """Override to accept custom command-line arguments."""
+
+  @classmethod
+  def ProcessCommandLineArgs(cls, parser, args):
+    """Override to process command-line arguments.
+
+    We pass in parser so we can call parser.error()."""
+
+
+class Command(ArgumentHandlerMixIn):
+  """An abstraction for things that run from the command-line."""
+
+  @classmethod
+  def Name(cls):
+    return camel_case.ToUnderscore(cls.__name__)
+
+  @classmethod
+  def Description(cls):
+    if cls.__doc__:
+      return cls.__doc__.splitlines()[0]
+    else:
+      return ''
+
+  def Run(self, args):
+    raise NotImplementedError()
+
+  @classmethod
+  def main(cls, args=None):
+    """Main method to run this command as a standalone script."""
+    parser = argparse.ArgumentParser()
+    cls.AddCommandLineArgs(parser)
+    args = parser.parse_args(args=args)
+    cls.ProcessCommandLineArgs(parser, args)
+    return min(cls().Run(args), 255)
+
+
+# TODO: Convert everything to argparse.
+class OptparseCommand(Command):
+  usage = ''
+
+  @classmethod
+  def CreateParser(cls):
+    return optparse.OptionParser('%%prog %s %s' % (cls.Name(), cls.usage),
+                                 description=cls.Description())
+
+  @classmethod
+  def AddCommandLineArgs(cls, parser, environment):
+    # pylint: disable=arguments-differ
+    pass
+
+  @classmethod
+  def ProcessCommandLineArgs(cls, parser, args, environment):
+    # pylint: disable=arguments-differ
+    pass
+
+  def Run(self, args):
+    raise NotImplementedError()
+
+  @classmethod
+  def main(cls, args=None):
+    """Main method to run this command as a standalone script."""
+    parser = cls.CreateParser()
+    cls.AddCommandLineArgs(parser, None)
+    options, args = parser.parse_args(args=args)
+    options.positional_args = args
+    cls.ProcessCommandLineArgs(parser, options, None)
+    return min(cls().Run(options), 255)
+
+
+class SubcommandCommand(Command):
+  """Combines Commands into one big command with sub-commands.
+
+  E.g. "svn checkout", "svn update", and "svn commit" are separate sub-commands.
+
+  Example usage:
+    class MyCommand(command_line.SubcommandCommand):
+      commands = (Help, List, Run)
+
+    if __name__ == '__main__':
+      sys.exit(MyCommand.main())
+  """
+
+  commands = ()
+
+  @classmethod
+  def AddCommandLineArgs(cls, parser):
+    subparsers = parser.add_subparsers()
+
+    for command in cls.commands:
+      subparser = subparsers.add_parser(
+          command.Name(), help=command.Description())
+      subparser.set_defaults(command=command)
+      command.AddCommandLineArgs(subparser)
+
+  @classmethod
+  def ProcessCommandLineArgs(cls, parser, args):
+    args.command.ProcessCommandLineArgs(parser, args)
+
+  def Run(self, args):
+    return args.command().Run(args)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/exception_formatter.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/exception_formatter.py
new file mode 100644
index 0000000..fe036c8
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/exception_formatter.py
@@ -0,0 +1,103 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Print prettier and more detailed exceptions."""
+
+import logging
+import math
+import os
+import sys
+import traceback
+
+from telemetry.core import exceptions
+
+
+def PrintFormattedException(exception_class=None, exception=None, tb=None,
+                            msg=None):
+  logging.info('Try printing formatted exception: %s %s %s' %
+               (exception_class, exception, tb))
+  assert bool(exception_class) == bool(exception) == bool(tb), (
+      'Must specify all or none of exception_class, exception, and tb')
+
+  if not exception_class:
+    exception_class, exception, tb = sys.exc_info()
+
+  if exception_class == exceptions.IntentionalException:
+    return
+
+  def _GetFinalFrame(tb_level):
+    while tb_level.tb_next:
+      tb_level = tb_level.tb_next
+    return tb_level.tb_frame
+
+  processed_tb = traceback.extract_tb(tb)
+  frame = _GetFinalFrame(tb)
+  exception_list = traceback.format_exception_only(exception_class, exception)
+  exception_string = '\n'.join(l.strip() for l in exception_list)
+
+  if msg:
+    print >> sys.stderr
+    print >> sys.stderr, msg
+
+  _PrintFormattedTrace(processed_tb, frame, exception_string)
+
+
+def PrintFormattedFrame(frame, exception_string=None):
+  _PrintFormattedTrace(traceback.extract_stack(frame), frame, exception_string)
+
+
+def _PrintFormattedTrace(processed_tb, frame, exception_string=None):
+  """Prints an Exception in a more useful format than the default.
+
+  TODO(tonyg): Consider further enhancements. For instance:
+    - Report stacks to maintainers like depot_tools does.
+    - Add a debug flag to automatically start pdb upon exception.
+  """
+  print >> sys.stderr
+
+  # Format the traceback.
+  print >> sys.stderr, 'Traceback (most recent call last):'
+  for filename, line, function, text in processed_tb:
+    filename = os.path.abspath(filename)
+    print >> sys.stderr, '  %s at %s:%d' % (function, filename, line)
+    print >> sys.stderr, '    %s' % text
+
+  # Format the exception.
+  if exception_string:
+    print >> sys.stderr, exception_string
+
+  # Format the locals.
+  local_variables = [(variable, value) for variable, value in
+                     frame.f_locals.iteritems() if variable != 'self']
+  print >> sys.stderr
+  print >> sys.stderr, 'Locals:'
+  if local_variables:
+    longest_variable = max(len(v) for v, _ in local_variables)
+    for variable, value in sorted(local_variables):
+      value = repr(value)
+      possibly_truncated_value = _AbbreviateMiddleOfString(value, ' ... ', 1024)
+      truncation_indication = ''
+      if len(possibly_truncated_value) != len(value):
+        truncation_indication = ' (truncated)'
+      print >> sys.stderr, '  %s: %s%s' % (variable.ljust(longest_variable + 1),
+                                           possibly_truncated_value,
+                                           truncation_indication)
+  else:
+    print >> sys.stderr, '  No locals!'
+
+  print >> sys.stderr
+  sys.stderr.flush()
+
+
+def _AbbreviateMiddleOfString(target, middle, max_length):
+  if max_length < 0:
+    raise ValueError('Must provide positive max_length')
+  if len(middle) > max_length:
+    raise ValueError('middle must not be greater than max_length')
+
+  if len(target) <= max_length:
+    return target
+  half_length = (max_length - len(middle)) / 2.
+  return (target[:int(math.floor(half_length))] + middle +
+          target[-int(math.ceil(half_length)):])
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/external_modules.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/external_modules.py
new file mode 100644
index 0000000..92faf59
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/external_modules.py
@@ -0,0 +1,56 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import importlib
+
+from distutils import version
+
+MODULES = {
+  'cv2': (version.StrictVersion('2.4.8'), version.StrictVersion('3.0.0')),
+  'numpy': (version.StrictVersion('1.6.1'), None),
+  'psutil': (version.StrictVersion('0.5.0'), None),
+}
+
+def ImportRequiredModule(module):
+  """Tries to import the desired module.
+
+  Returns:
+    The module on success, raises error on failure.
+  Raises:
+    ImportError: The import failed."""
+  versions = MODULES.get(module)
+  if versions is None:
+    raise NotImplementedError('Please teach telemetry about module %s.' %
+                              module)
+  min_version, max_version = versions
+
+  module = importlib.import_module(module)
+  try:
+    if ((min_version is not None and
+            version.StrictVersion(module.__version__) < min_version) or
+        (max_version is not None and
+            version.StrictVersion(module.__version__) >= max_version)):
+      raise ImportError(('Incorrect {0} version found, expected {1} <= version '
+                         '< {2}, found version {3}').format(
+          module, min_version, max_version, module.__version__))
+  except ValueError as e:
+    # This error is raised when a module returns and incorrectly formatted
+    # version string. ex. '$build 1456a'
+    if 'invalid version number' in str(e):
+      raise ImportError(('Incorrectly formatted {0} version found, expected '
+                         '{1} <= version < {2}, found version {3}').format(
+          module, min_version, max_version, module.__version__))
+    else:
+      raise
+  return module
+
+def ImportOptionalModule(module):
+  """Tries to import the desired module.
+
+  Returns:
+    The module if successful, None if not."""
+  try:
+    return ImportRequiredModule(module)
+  except ImportError:
+    return None
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/file_handle.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/file_handle.py
new file mode 100644
index 0000000..8133b0d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/file_handle.py
@@ -0,0 +1,73 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+
+
+_next_file_id = 0
+
+
+class FileHandle(object):
+  def __init__(self, temp_file=None, absolute_path=None):
+    """Constructs a FileHandle object.
+
+    This constructor should not be used by the user; rather it is preferred to
+    use the module-level GetAbsPath and FromTempFile functions.
+
+    Args:
+      temp_file: An instance of a temporary file object.
+      absolute_path: A path; should not be passed if tempfile is and vice-versa.
+      extension: A string that specifies the file extension. It must starts with
+        ".".
+    """
+    # Exactly one of absolute_path or temp_file must be specified.
+    assert (absolute_path is None) != (temp_file is None)
+    self._temp_file = temp_file
+    self._absolute_path = absolute_path
+
+    global _next_file_id
+    self._id = _next_file_id
+    _next_file_id += 1
+
+  @property
+  def id(self):
+    return self._id
+
+  @property
+  def extension(self):
+    return os.path.splitext(self.GetAbsPath())[1]
+
+  def GetAbsPath(self):
+    """Returns the path to the pointed-to file relative to the given start path.
+
+    Args:
+      start: A string representing a starting path.
+    Returns:
+      A string giving the relative path from path to this file.
+    """
+    if self._temp_file:
+      self._temp_file.close()
+      return self._temp_file.name
+    else:
+      return self._absolute_path
+
+
+def FromTempFile(temp_file):
+  """Constructs a FileHandle pointing to a temporary file.
+
+  Returns:
+    A FileHandle referring to a named temporary file.
+  """
+  return FileHandle(temp_file)
+
+
+def FromFilePath(path):
+  """Constructs a FileHandle from an absolute file path.
+
+  Args:
+    path: A string giving the absolute path to a file.
+  Returns:
+    A FileHandle referring to the file at the specified path.
+  """
+  return FileHandle(None, os.path.abspath(path))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/file_handle_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/file_handle_unittest.py
new file mode 100644
index 0000000..28c4265
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/file_handle_unittest.py
@@ -0,0 +1,29 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import tempfile
+import unittest
+
+from telemetry.internal.util import file_handle
+
+
+class FileHandleUnittest(unittest.TestCase):
+
+  def setUp(self):
+    self.temp_file_txt = tempfile.NamedTemporaryFile(
+        suffix='.txt', delete=False)
+    self.abs_path_html = tempfile.NamedTemporaryFile(
+        suffix='.html', delete=False).name
+
+  def tearDown(self):
+    os.remove(self.abs_path_html)
+
+  def testCreatingFileHandle(self):
+    fh1 = file_handle.FromTempFile(self.temp_file_txt)
+    self.assertEquals(fh1.extension, '.txt')
+
+    fh2 = file_handle.FromFilePath(self.abs_path_html)
+    self.assertEquals(fh2.extension, '.html')
+    self.assertNotEquals(fh1.id, fh2.id)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/global_hooks.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/global_hooks.py
new file mode 100644
index 0000000..d343575
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/global_hooks.py
@@ -0,0 +1,40 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Hooks that apply globally to all scripts that import or use Telemetry."""
+import signal
+import sys
+
+from telemetry.internal.util import exception_formatter
+
+
+def InstallHooks():
+  InstallUnhandledExceptionFormatter()
+  InstallStackDumpOnSigusr1()
+  InstallTerminationHook()
+
+def InstallUnhandledExceptionFormatter():
+  """Print prettier exceptions that also contain the stack frame's locals."""
+  sys.excepthook = exception_formatter.PrintFormattedException
+
+
+def InstallStackDumpOnSigusr1():
+  """Catch SIGUSR1 and print a stack trace."""
+  # Windows doesn't define SIGUSR1.
+  if not hasattr(signal, 'SIGUSR1'):
+    return
+
+  def PrintDiagnostics(_, stack_frame):
+    exception_string = 'SIGUSR1 received, printed stack trace'
+    exception_formatter.PrintFormattedFrame(stack_frame, exception_string)
+  signal.signal(signal.SIGUSR1, PrintDiagnostics)
+
+
+def InstallTerminationHook():
+  """Catch SIGTERM, print a stack trace, and exit."""
+  def PrintStackAndExit(sig, stack_frame):
+    exception_string = 'Received signal %s, exiting' % sig
+    exception_formatter.PrintFormattedFrame(stack_frame, exception_string)
+    sys.exit(-1)
+  signal.signal(signal.SIGTERM, PrintStackAndExit)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/path.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/path.py
new file mode 100644
index 0000000..80379df
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/path.py
@@ -0,0 +1,71 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import glob
+import os
+
+from telemetry.core import util
+import py_utils as catapult_util
+
+# TODO(aiolos): Move these functions to catapult_util or here.
+GetBaseDir = util.GetBaseDir
+GetTelemetryDir = util.GetTelemetryDir
+GetUnittestDataDir = util.GetUnittestDataDir
+GetChromiumSrcDir = util.GetChromiumSrcDir
+GetBuildDirectories = util.GetBuildDirectories
+
+IsExecutable = catapult_util.IsExecutable
+
+
+def _HasWildcardCharacters(input_string):
+  # Could make this more precise.
+  return '*' in input_string or '+' in input_string
+
+
+def FindInstalledWindowsApplication(application_path):
+  """Search common Windows installation directories for an application.
+
+  Args:
+    application_path: Path to application relative from installation location.
+  Returns:
+    A string representing the full path, or None if not found.
+  """
+  search_paths = [os.getenv('PROGRAMFILES(X86)'),
+                  os.getenv('PROGRAMFILES'),
+                  os.getenv('LOCALAPPDATA')]
+  search_paths += os.getenv('PATH', '').split(os.pathsep)
+  for search_path in search_paths:
+    if not search_path:
+      continue
+    path = os.path.join(search_path, application_path)
+    if _HasWildcardCharacters(path):
+      paths = glob.glob(path)
+    else:
+      paths = [path]
+    for p in paths:
+      if IsExecutable(p):
+        return p
+  return None
+
+
+def IsSubpath(subpath, superpath):
+  """Returns True iff subpath is or is in superpath."""
+  subpath = os.path.realpath(subpath)
+  superpath = os.path.realpath(superpath)
+
+  while len(subpath) >= len(superpath):
+    if subpath == superpath:
+      return True
+    subpath = os.path.split(subpath)[0]
+  return False
+
+
+def ListFiles(base_directory, should_include_dir=lambda _: True,
+              should_include_file=lambda _: True):
+  matching_files = []
+  for root, dirs, files in os.walk(base_directory):
+    dirs[:] = [dir_name for dir_name in dirs if should_include_dir(dir_name)]
+    matching_files += [os.path.join(root, file_name)
+                       for file_name in files if should_include_file(file_name)]
+  return sorted(matching_files)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/path_set.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/path_set.py
new file mode 100644
index 0000000..0b46783
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/path_set.py
@@ -0,0 +1,44 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import collections
+import os
+
+
+class PathSet(collections.MutableSet):
+  """A set of paths.
+
+  All mutation methods can take both directories or individual files, but the
+  iterator yields the individual files. All paths are automatically normalized.
+  """
+  def __init__(self, iterable=None):
+    self._paths = set()
+    if iterable:
+      self |= iterable
+
+  def __contains__(self, path):
+    return os.path.realpath(path) in self._paths
+
+  def __iter__(self):
+    return iter(self._paths)
+
+  def __len__(self):
+    return len(self._paths)
+
+  def add(self, path):
+    path = os.path.realpath(path)
+    if os.path.isfile(path):
+      self._paths.add(path)
+    for root, _, files in os.walk(path):
+      for basename in files:
+        file_path = os.path.join(root, basename)
+        if os.path.isfile(file_path):
+          self._paths.add(file_path)
+
+  def discard(self, path):
+    path = os.path.realpath(path)
+    self._paths.discard(path)
+    for root, _, files in os.walk(path):
+      for basename in files:
+        self._paths.discard(os.path.join(root, basename))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/path_set_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/path_set_unittest.py
new file mode 100755
index 0000000..6cc0ec1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/path_set_unittest.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import unittest
+
+from telemetry.internal.util import path_set
+
+
+class PathSetTest(unittest.TestCase):
+  def testCreate(self):
+    ps = path_set.PathSet()
+    self.assertEqual(len(ps), 0)  # Check __len__.
+    self.assertFalse(__file__ in ps)
+    for _ in ps:  # Check __iter__.
+      self.fail('New set is not empty.')
+
+    ps = path_set.PathSet([__file__])
+    self.assertEqual(len(ps), 1)
+    self.assertTrue(__file__ in ps)
+    self.assertEqual(ps.pop(), os.path.realpath(__file__))
+
+  def testAdd(self):
+    ps = path_set.PathSet()
+    ps.add(__file__)
+    self.assertEqual(len(ps), 1)
+    self.assertTrue(__file__ in ps)
+    self.assertEqual(ps.pop(), os.path.realpath(__file__))
+
+  def testDiscard(self):
+    ps = path_set.PathSet([__file__])
+    ps.discard(__file__)
+    self.assertEqual(len(ps), 0)
+    self.assertFalse(__file__ in ps)
+
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/path_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/path_unittest.py
new file mode 100644
index 0000000..e9a0ee0
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/path_unittest.py
@@ -0,0 +1,26 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import sys
+import unittest
+
+from telemetry import decorators
+from telemetry.internal.util import path
+
+
+class PathTest(unittest.TestCase):
+  def testIsExecutable(self):
+    self.assertFalse(path.IsExecutable('nonexistent_file'))
+    self.assertTrue(path.IsExecutable(sys.executable))
+
+  @decorators.Enabled('win')
+  def testFindInstalledWindowsApplication(self):
+    self.assertTrue(path.FindInstalledWindowsApplication(os.path.join(
+        'Internet Explorer', 'iexplore.exe')))
+
+  @decorators.Enabled('win')
+  def testFindInstalledWindowsApplicationWithWildcards(self):
+    self.assertTrue(path.FindInstalledWindowsApplication(os.path.join(
+        '*', 'iexplore.exe')))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/ps_util.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/ps_util.py
new file mode 100644
index 0000000..6cae814
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/ps_util.py
@@ -0,0 +1,91 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.internal.util import atexit_with_log
+import inspect
+import logging
+import os
+
+from collections import defaultdict
+
+
+def GetChildPids(processes, pid):
+  """Returns all child processes of |pid| from the given |processes| list.
+
+  Args:
+    processes: A tuple of (pid, ppid, state) as generated by ps.
+    pid: The pid for which to get children.
+
+  Returns:
+    A list of child pids.
+  """
+  child_dict = defaultdict(list)
+  for curr_pid, curr_ppid, state in processes:
+    if 'Z' in state:
+      continue  # Ignore zombie processes
+    child_dict[int(curr_ppid)].append(int(curr_pid))
+  queue = [pid]
+  child_ids = []
+  while queue:
+    parent = queue.pop()
+    if parent in child_dict:
+      children = child_dict[parent]
+      queue.extend(children)
+      child_ids.extend(children)
+  return child_ids
+
+
+def GetPsOutputWithPlatformBackend(platform_backend, columns, pid):
+  """Returns output of the 'ps' command as a list of lines.
+
+  Args:
+    platform_backend: The platform backend (LinuxBasedPlatformBackend or
+        PosixPlatformBackend).
+    columns: A list of require columns, e.g., ['pid', 'pss'].
+    pid: If not None, returns only the information of the process with the pid.
+  """
+  args = ['ps']
+  args.extend(['-p', str(pid)] if pid != None else ['-e'])
+  for c in columns:
+    args.extend(['-o', c + '='])
+  return platform_backend.RunCommand(args).splitlines()
+
+
+def EnableListingStrayProcessesUponExitHook():
+  def _ListAllSubprocesses():
+    try:
+      import psutil
+    except ImportError:
+      logging.warning(
+          'psutil is not installed on the system. Not listing possible '
+          'leaked processes. To install psutil, see: '
+          'https://pypi.python.org/pypi/psutil')
+      return
+    telemetry_pid = os.getpid()
+    parent = psutil.Process(telemetry_pid)
+    if hasattr(parent, 'children'):
+      children = parent.children(recursive=True)
+    else:  # Some old version of psutil use get_children instead children.
+      children = parent.get_children()
+    if children:
+      leak_processes_info = []
+      for p in children:
+        if inspect.ismethod(p.name):
+          name = p.name()
+        else:  # Process.name is a property in old versions of psutil.
+          name = p.name
+        process_info = '%s (%s)' % (name, p.pid)
+        try:
+          if inspect.ismethod(p.cmdline):
+            cmdline = p.cmdline()
+          else:
+            cmdline = p.cmdline
+          process_info += ' - %s' % cmdline
+        except Exception as e:
+          logging.warning(str(e))
+        leak_processes_info.append(process_info)
+      logging.warning('Telemetry leaks these processes: %s',
+                      ', '.join(leak_processes_info))
+
+  atexit_with_log.Register(_ListAllSubprocesses)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/ts_proxy_server.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/ts_proxy_server.py
new file mode 100644
index 0000000..c4c5cd3
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/ts_proxy_server.py
@@ -0,0 +1,144 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Start and stop tsproxy."""
+
+import logging
+import os
+import re
+import subprocess
+import sys
+
+from telemetry.core import util
+from telemetry.internal.util import atexit_with_log
+
+import py_utils
+
+
+_TSPROXY_PATH = os.path.join(
+    util.GetTelemetryThirdPartyDir(), 'tsproxy', 'tsproxy.py')
+
+
+def ParseTsProxyPortFromOutput(output_line):
+  port_re = re.compile(
+      r'Started Socks5 proxy server on '
+      r'(?P<host>[^:]*):'
+      r'(?P<port>\d+)')
+  m = port_re.match(output_line.strip())
+  if m:
+    return int(m.group('port'))
+
+
+class TsProxyServer(object):
+  """Start and Stop Tsproxy.
+
+  TsProxy provides basic latency, download and upload traffic shaping. This
+  class provides a programming API to the tsproxy script in
+  telemetry/third_party/tsproxy/tsproxy.py
+  """
+
+  def __init__(self, host_ip=None, http_port=None, https_port=None):
+    """Initialize TsProxyServer.
+    """
+    self._proc = None
+    self._port = None
+    self._is_running = False
+    self._host_ip = host_ip
+    assert bool(http_port) == bool(https_port)
+    self._http_port = http_port
+    self._https_port = https_port
+
+  @property
+  def port(self):
+    return self._port
+
+  def StartServer(self, timeout=10):
+    """Start TsProxy server and verify that it started.
+    """
+    cmd_line = [sys.executable, _TSPROXY_PATH]
+    cmd_line.extend([
+        '--port=0'])  # Use port 0 so tsproxy picks a random available port.
+    if self._host_ip:
+      cmd_line.append('--desthost=%s' % self._host_ip)
+    if self._http_port:
+      cmd_line.append(
+        '--mapports=443:%s,*:%s' % (self._https_port, self._http_port))
+    logging.info('Tsproxy commandline: %r' % cmd_line)
+    self._proc = subprocess.Popen(
+        cmd_line, stdout=subprocess.PIPE, stdin=subprocess.PIPE,
+        stderr=subprocess.PIPE, bufsize=1)
+    atexit_with_log.Register(self.StopServer)
+    try:
+      py_utils.WaitFor(self._IsStarted, timeout)
+      logging.info('TsProxy port: %s', self._port)
+      self._is_running = True
+    except py_utils.TimeoutException:
+      err = self.StopServer()
+      raise RuntimeError(
+          'Error starting tsproxy: %s' % err)
+
+  def _IsStarted(self):
+    assert not self._is_running
+    assert self._proc
+    if self._proc.poll() is not None:
+      return False
+    self._proc.stdout.flush()
+    self._port = ParseTsProxyPortFromOutput(
+          output_line=self._proc.stdout.readline())
+    return self._port != None
+
+
+  def _IssueCommand(self, command_string, timeout):
+    logging.info('Issuing command to ts_proxy_server: %s', command_string)
+    command_output = []
+    self._proc.stdin.write('%s\n' % command_string)
+    self._proc.stdin.flush()
+    self._proc.stdout.flush()
+    def CommandStatusIsRead():
+      command_output.append(self._proc.stdout.readline().strip())
+      return (
+          command_output[-1] == 'OK' or command_output[-1] == 'ERROR')
+    py_utils.WaitFor(CommandStatusIsRead, timeout)
+    if not 'OK' in command_output:
+      raise RuntimeError('Failed to execute command %s:\n%s' %
+                         (repr(command_string), '\n'.join(command_output)))
+
+
+  def UpdateOutboundPorts(self, http_port, https_port, timeout=5):
+    assert http_port and https_port
+    assert http_port != https_port
+    assert isinstance(http_port, int) and isinstance(https_port, int)
+    assert 1 <= http_port <= 65535
+    assert 1 <= https_port <= 65535
+    self._IssueCommand('set mapports 443:%i,*:%i' % (https_port, http_port),
+                       timeout)
+
+  def UpdateTrafficSettings(self, round_trip_latency_ms=0,
+      download_bandwidth_kbps=0, upload_bandwidth_kbps=0, timeout=5):
+    self._IssueCommand('set rtt %s' % round_trip_latency_ms, timeout)
+    self._IssueCommand('set inkbps %s' % download_bandwidth_kbps, timeout)
+    self._IssueCommand('set outkbps %s' % upload_bandwidth_kbps, timeout)
+
+  def StopServer(self):
+    """Stop TsProxy Server."""
+    if not self._is_running:
+      logging.debug('Attempting to stop TsProxy server that is not running.')
+      return
+    if self._proc:
+      self._proc.terminate()
+      self._proc.wait()
+    err = self._proc.stderr.read()
+    self._proc = None
+    self._port = None
+    self._is_running = False
+    return err
+
+  def __enter__(self):
+    """Add support for with-statement."""
+    self.StartServer()
+    return self
+
+  def __exit__(self, unused_exc_type, unused_exc_val, unused_exc_tb):
+    """Add support for with-statement."""
+    self.StopServer()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/ts_proxy_server_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/ts_proxy_server_unittest.py
new file mode 100644
index 0000000..feb1824
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/ts_proxy_server_unittest.py
@@ -0,0 +1,48 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from telemetry.internal.util import ts_proxy_server
+
+class TsProxyServerTest(unittest.TestCase):
+  def testParseTsProxyPort(self):
+    self.assertEquals(
+      ts_proxy_server.ParseTsProxyPortFromOutput(
+          'Started Socks5 proxy server on 127.0.0.1:54430 \n'),
+      54430)
+    self.assertEquals(
+      ts_proxy_server.ParseTsProxyPortFromOutput(
+          'Started Socks5 proxy server on foo.bar.com:430 \n'),
+      430)
+    self.assertEquals(
+      ts_proxy_server.ParseTsProxyPortFromOutput(
+          'Failed to start sock5 proxy.'),
+      None)
+
+  def testSmokeStartingTsProxyServer(self):
+    with ts_proxy_server.TsProxyServer() as server:
+      self.assertIsNotNone(server.port)
+    with ts_proxy_server.TsProxyServer(None, 37124, 37125) as server:
+      self.assertIsNotNone(server.port)
+
+  def testSmokeUpdatingOutboundPorts(self):
+    with ts_proxy_server.TsProxyServer() as server:
+      self.assertIsNotNone(server.port)
+      server.UpdateOutboundPorts(31242, 14220)
+
+  def testSmokeUpdateOutboundPortsInvalid(self):
+    with ts_proxy_server.TsProxyServer() as server:
+      self.assertIsNotNone(server.port)
+      with self.assertRaises(AssertionError):
+        server.UpdateOutboundPorts(31242, 'abcde')
+
+  def testSmokeUpdateTrafficSettings(self):
+    with ts_proxy_server.TsProxyServer() as server:
+      server.UpdateTrafficSettings(round_trip_latency_ms=100)
+      server.UpdateTrafficSettings(download_bandwidth_kbps=5000)
+      server.UpdateTrafficSettings(upload_bandwidth_kbps=2000)
+      server.UpdateTrafficSettings(
+          round_trip_latency_ms=200, download_bandwidth_kbps=500,
+          upload_bandwidth_kbps=2000)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/wp_server_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/wp_server_unittest.py
new file mode 100644
index 0000000..054760d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/wp_server_unittest.py
@@ -0,0 +1,71 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+import sys
+import unittest
+
+from telemetry.internal.util import wpr_server
+
+
+# pylint: disable=protected-access
+class CreateCommandTest(unittest.TestCase):
+  def testHasDnsGivesDnsPort(self):
+    expected_cmd_line = [
+        sys.executable, 'replay.py', '--host=127.0.0.1',
+        '--port=2', '--ssl_port=1', '--dns_port=0',
+        '--use_closest_match', '--log_level=warning', '--extra_arg', 'foo.wpr']
+    cmd_line = wpr_server.ReplayServer._GetCommandLine(
+        'replay.py', '127.0.0.1', 2, 1, 0, ['--extra_arg'], 'foo.wpr',
+        log_level=logging.WARNING)
+    self.assertEqual(expected_cmd_line, cmd_line)
+
+  def testNoDnsGivesNoDnsForwarding(self):
+    expected_cmd_line = [
+        sys.executable, 'replay.py', '--host=127.0.0.1',
+        '--port=8080', '--ssl_port=8443', '--no-dns_forwarding',
+        '--use_closest_match', '--log_level=warning', 'bar.wpr']
+    cmd_line = wpr_server.ReplayServer._GetCommandLine(
+        'replay.py', '127.0.0.1', 8080, 8443, None, [], 'bar.wpr',
+        log_level=logging.WARNING)
+    self.assertEqual(expected_cmd_line, cmd_line)
+
+
+# pylint: disable=protected-access
+class ParseLogFilePortsTest(unittest.TestCase):
+  def testEmptyLinesGivesEmptyDict(self):
+    log_lines = iter([])
+    self.assertEqual(
+      {},
+      wpr_server.ReplayServer._ParseLogFilePorts(log_lines))
+
+  def testSingleMatchGivesSingleElementDict(self):
+    log_lines = iter([
+        'extra stuff',
+        '2014-09-27 17:04:27,11 WARNING HTTP server started on 127.0.0.1:5167',
+        'extra stuff',
+        ])
+    self.assertEqual(
+        {'http': 5167},
+        wpr_server.ReplayServer._ParseLogFilePorts(log_lines))
+
+  def testUnknownProtocolSkipped(self):
+    log_lines = iter([
+        '2014-09-27 17:04:27,11 WARNING FOO server started on 127.0.0.1:1111',
+        '2014-09-27 17:04:27,12 WARNING HTTP server started on 127.0.0.1:5167',
+        ])
+    self.assertEqual(
+        {'http': 5167},
+        wpr_server.ReplayServer._ParseLogFilePorts(log_lines))
+
+  def testTypicalLogLinesGiveFullDict(self):
+    log_lines = iter([
+        'extra',
+        '2014-09-27 17:04:27,11 WARNING DNS server started on 127.0.0.1:2345',
+        '2014-09-27 17:04:27,12 WARNING HTTP server started on 127.0.0.1:3456',
+        '2014-09-27 17:04:27,13 WARNING HTTPS server started on 127.0.0.1:4567',
+        ])
+    self.assertEqual(
+        {'dns': 2345, 'http': 3456, 'https': 4567},
+        wpr_server.ReplayServer._ParseLogFilePorts(log_lines))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/wpr_server.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/wpr_server.py
new file mode 100644
index 0000000..d7f78db
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/internal/util/wpr_server.py
@@ -0,0 +1,339 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Start and stop Web Page Replay."""
+
+from telemetry.internal.util import atexit_with_log
+import logging
+import os
+import re
+import signal
+import subprocess
+import sys
+import tempfile
+import urllib
+
+from telemetry.core import util
+from telemetry.internal import forwarders
+
+import py_utils
+
+_REPLAY_DIR = os.path.join(
+    util.GetTelemetryThirdPartyDir(), 'web-page-replay')
+
+
+class ReplayError(Exception):
+  """Catch-all exception for the module."""
+  pass
+
+
+class ReplayNotFoundError(ReplayError):
+  def __init__(self, label, path):
+    super(ReplayNotFoundError, self).__init__()
+    self.args = (label, path)
+
+  def __str__(self):
+    label, path = self.args
+    return 'Path does not exist for %s: %s' % (label, path)
+
+
+class ReplayNotStartedError(ReplayError):
+  pass
+
+
+class ReplayServer(object):
+  """Start and Stop Web Page Replay.
+
+  Web Page Replay is a proxy that can record and "replay" web pages with
+  simulated network characteristics -- without having to edit the pages
+  by hand. With WPR, tests can use "real" web content, and catch
+  performance issues that may result from introducing network delays and
+  bandwidth throttling.
+
+  Example:
+     with ReplayServer(archive_path):
+       self.NavigateToURL(start_url)
+       self.WaitUntil(...)
+  """
+
+  def __init__(self, archive_path, replay_host, http_port, https_port, dns_port,
+               replay_options):
+    """Initialize ReplayServer.
+
+    Args:
+      archive_path: a path to a specific WPR archive (required).
+      replay_host: the hostname to serve traffic.
+      http_port: an integer port on which to serve HTTP traffic. May be zero
+          to let the OS choose an available port.
+      https_port: an integer port on which to serve HTTPS traffic. May be zero
+          to let the OS choose an available port.
+      dns_port: an integer port on which to serve DNS traffic. May be zero
+          to let the OS choose an available port. If None DNS forwarding is
+          disabled.
+      replay_options: an iterable of options strings to forward to replay.py.
+    """
+    self.archive_path = archive_path
+    self._replay_host = replay_host
+    self._use_dns_server = dns_port is not None
+    self._started_ports = {}  # a dict such as {'http': 80, 'https': 443}
+
+    # A temporary path for storing stdout & stderr of the webpagereplay
+    # subprocess.
+    self._temp_log_file_path = None
+
+    replay_py = os.path.join(_REPLAY_DIR, 'replay.py')
+    self._cmd_line = self._GetCommandLine(
+        replay_py, self._replay_host, http_port, https_port, dns_port,
+        replay_options, archive_path)
+
+    if '--record' in replay_options:
+      self._CheckPath('archive directory', os.path.dirname(self.archive_path))
+    elif not os.path.exists(self.archive_path):
+      self._CheckPath('archive file', self.archive_path)
+    self._CheckPath('replay script', replay_py)
+
+    self.replay_process = None
+
+  @staticmethod
+  def _GetLoggingLevel(log_level=None):
+    return {
+      logging.DEBUG: 'debug',
+      logging.INFO: 'info',
+      logging.WARNING: 'warning',
+      logging.ERROR: 'error',
+      logging.CRITICAL: 'critical',
+    }[log_level or logging.getLogger().level]
+
+  @staticmethod
+  def _GetCommandLine(replay_py, host_ip, http_port, https_port, dns_port,
+                      replay_options, archive_path, log_level=None):
+    """Set WPR command-line options. Can be overridden if needed."""
+    cmd_line = [sys.executable, replay_py]
+    cmd_line.extend([
+        '--host=%s' % host_ip,
+        '--port=%s' % http_port,
+        '--ssl_port=%s' % https_port
+        ])
+    if dns_port is not None:
+      # Note that if --host is not '127.0.0.1', Replay will override the local
+      # DNS nameserver settings to point to the replay-started DNS server.
+      cmd_line.append('--dns_port=%s' % dns_port)
+    else:
+      cmd_line.append('--no-dns_forwarding')
+    cmd_line.extend([
+        '--use_closest_match',
+        '--log_level=%s' % ReplayServer._GetLoggingLevel(log_level)
+        ])
+    cmd_line.extend(replay_options)
+    cmd_line.append(archive_path)
+    return cmd_line
+
+  def _CheckPath(self, label, path):
+    if not os.path.exists(path):
+      raise ReplayNotFoundError(label, path)
+
+  def _OpenLogFile(self):
+    """Opens the log file for writing."""
+    log_dir = os.path.dirname(self._temp_log_file_path)
+    if not os.path.exists(log_dir):
+      os.makedirs(log_dir)
+    return open(self._temp_log_file_path, 'w')
+
+  def _LogLines(self):
+    """Yields the log lines."""
+    if not os.path.isfile(self._temp_log_file_path):
+      return
+    with open(self._temp_log_file_path) as f:
+      for line in f:
+        yield line
+
+  def _IsStarted(self):
+    """Returns true if the server is up and running."""
+    if self.replay_process.poll() is not None:
+      # The process terminated.
+      return False
+
+    def HasIncompleteStartedPorts():
+      return ('http' not in self._started_ports or
+              'https' not in self._started_ports or
+              (self._use_dns_server and 'dns' not in self._started_ports))
+
+    if HasIncompleteStartedPorts():
+      self._started_ports = self._ParseLogFilePorts(self._LogLines())
+    if HasIncompleteStartedPorts():
+      return False
+    try:
+      # HTTPS may require SNI (which urllib does not speak), so only check
+      # that HTTP responds.
+      return 200 == self._UrlOpen('web-page-replay-generate-200').getcode()
+    except IOError:
+      return False
+
+  @staticmethod
+  def _ParseLogFilePorts(log_lines):
+    """Returns the ports on which replay listens as reported in its log file.
+
+    Only matches HTTP, HTTPS, and DNS. One call may return only some
+    of the ports depending on what has been written to the log file.
+
+    Example log lines:
+        2014-09-03 17:04:27,978 WARNING HTTP server started on 127.0.0.1:51673
+        2014-09-03 17:04:27,978 WARNING HTTPS server started on 127.0.0.1:35270
+
+    Returns:
+      a dict with ports available in log_lines. For example,
+         {}  # no ports found
+         {'http': 1234, 'https': 2345, 'dns': 3456}
+    """
+    ports = {}
+    port_re = re.compile(
+        r'.*?(?P<protocol>HTTP|HTTPS|DNS)'
+        r' server started on '
+        r'(?P<host>[^:]*):'
+        r'(?P<port>\d+)')
+    for line in log_lines:
+      m = port_re.match(line.strip())
+      if m:
+        protocol = m.group('protocol').lower()
+        ports[protocol] = int(m.group('port'))
+    return ports
+
+  def StartServer(self):
+    """Start Web Page Replay and verify that it started.
+
+    Returns:
+      A forwarders.PortSet(http, https, dns) tuple; with dns None if unused.
+    Raises:
+      ReplayNotStartedError: if Replay start-up fails.
+    """
+    is_posix = sys.platform.startswith('linux') or sys.platform == 'darwin'
+    logging.info('Starting Web-Page-Replay: %s', self._cmd_line)
+    self._CreateTempLogFilePath()
+    with open(self._temp_log_file_path, 'w') as log_fh:
+      self.replay_process = subprocess.Popen(
+          self._cmd_line, stdout=log_fh, stderr=subprocess.STDOUT,
+          preexec_fn=(_ResetInterruptHandler if is_posix else None))
+    try:
+      py_utils.WaitFor(self._IsStarted, 30)
+      logging.info('WPR ports: %s' % self._started_ports)
+      atexit_with_log.Register(self.StopServer)
+      return forwarders.PortSet(
+          self._started_ports['http'],
+          self._started_ports['https'],
+          self._started_ports.get('dns'),  # None if unused
+          )
+    except py_utils.TimeoutException:
+      raise ReplayNotStartedError(
+          'Web Page Replay failed to start. Log output:\n%s' %
+          ''.join(self._LogLines()))
+
+  def StopServer(self):
+    """Stop Web Page Replay."""
+    if self._IsStarted():
+      try:
+        self._StopReplayProcess()
+      finally:
+        # TODO(rnephew): Upload logs to google storage. crbug.com/525787
+        self._CleanUpTempLogFilePath()
+
+  def _StopReplayProcess(self):
+    if not self.replay_process:
+      return
+
+    logging.debug('Trying to stop Web-Page-Replay gracefully')
+    try:
+      if self._started_ports:
+        self._UrlOpen('web-page-replay-command-exit').close()
+    except IOError:
+      # IOError is possible because the server might exit without response.
+      pass
+
+    try:
+      py_utils.WaitFor(lambda: self.replay_process.poll() is not None, 10)
+    except py_utils.TimeoutException:
+      try:
+        # Use a SIGINT so that it can do graceful cleanup.
+        self.replay_process.send_signal(signal.SIGINT)
+      except:  # pylint: disable=bare-except
+        # On Windows, we are left with no other option than terminate().
+        is_primary_nameserver_changed_by_replay = (
+            self._use_dns_server and self._replay_host == '127.0.0.1')
+        if is_primary_nameserver_changed_by_replay:
+          # Replay changes the DNS nameserver configuration so that DNS
+          # requests are resolved by replay's own DNS server. It resolves
+          # all DNS requests to it own IP address to it can server the
+          # HTTP and HTTPS requests.
+          # If the replay host is not '127.0.0.1', then replay skips the
+          # nameserver change because it assumes a different mechanism
+          # will be used to route DNS requests to replay's DNS server.
+          logging.warning(
+              'Unable to stop Web-Page-Replay gracefully.\n'
+              'Replay changed the DNS nameserver configuration to make replay '
+              'the primary nameserver. That might not be restored!')
+        try:
+          self.replay_process.terminate()
+        except:  # pylint: disable=bare-except
+          pass
+      self.replay_process.wait()
+
+  def _CreateTempLogFilePath(self):
+    assert self._temp_log_file_path is None
+    handle, self._temp_log_file_path = tempfile.mkstemp()
+    os.close(handle)
+
+  def _CleanUpTempLogFilePath(self):
+    assert self._temp_log_file_path
+    if logging.getLogger('').isEnabledFor(logging.DEBUG):
+      with open(self._temp_log_file_path, 'r') as f:
+        wpr_log_content = '\n'.join([
+            '************************** WPR LOG *****************************',
+            f.read(),
+            '************************** END OF WPR LOG **********************'])
+      logging.debug(wpr_log_content)
+    os.remove(self._temp_log_file_path)
+    self._temp_log_file_path = None
+
+  def __enter__(self):
+    """Add support for with-statement."""
+    self.StartServer()
+    return self
+
+  def __exit__(self, unused_exc_type, unused_exc_val, unused_exc_tb):
+    """Add support for with-statement."""
+    self.StopServer()
+
+  def _UrlOpen(self, url_path, protocol='http'):
+    """Open a Replay URL.
+
+    For matching requests in the archive, Replay relies on the "Host:" header.
+    For Replay command URLs, the "Host:" header is not needed.
+
+    Args:
+      url_path: WPR server request path.
+      protocol: 'http' or 'https'
+    Returns:
+      a file-like object from urllib.urlopen
+    """
+    url = '%s://%s:%s/%s' % (
+        protocol, self._replay_host, self._started_ports[protocol], url_path)
+    return urllib.urlopen(url, proxies={})
+
+def _ResetInterruptHandler():
+  """Reset the interrupt handler back to the default.
+
+  The replay process is stopped gracefully by making an HTTP request
+  ('web-page-replay-command-exit'). The graceful exit is important for
+  restoring the DNS configuration. If the HTTP request fails, the fallback
+  is to send SIGINT to the process.
+
+  On posix system, running this function before starting replay fixes a
+  bug that shows up when Telemetry is run as a background command from a
+  script. https://crbug.com/254572.
+
+  Background: Signal masks on Linux are inherited from parent
+  processes. If anything invoking us accidentally masks SIGINT
+  (e.g. by putting a process in the background from a shell script),
+  sending a SIGINT to the child will fail to terminate it.
+  """
+  signal.signal(signal.SIGINT, signal.SIG_DFL)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/page/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/page/__init__.py
new file mode 100644
index 0000000..f11e2e1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/page/__init__.py
@@ -0,0 +1,228 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import inspect
+import logging
+import os
+import urlparse
+
+from py_utils import cloud_storage  # pylint: disable=import-error
+
+from telemetry import story
+from telemetry.page import cache_temperature as cache_temperature_module
+from telemetry.page import shared_page_state
+from telemetry.page import traffic_setting as traffic_setting_module
+from telemetry.internal.actions import action_runner as action_runner_module
+
+
+class Page(story.Story):
+
+  def __init__(self, url, page_set=None, base_dir=None, name='',
+               credentials_path=None,
+               credentials_bucket=cloud_storage.PUBLIC_BUCKET, tags=None,
+               startup_url='', make_javascript_deterministic=True,
+               shared_page_state_class=shared_page_state.SharedPageState,
+               grouping_keys=None,
+               cache_temperature=cache_temperature_module.ANY,
+               traffic_setting=traffic_setting_module.NONE,
+               platform_specific=False):
+    self._url = url
+
+    super(Page, self).__init__(
+        shared_page_state_class, name=name, tags=tags,
+        is_local=self._scheme in ['file', 'chrome', 'about'],
+        make_javascript_deterministic=make_javascript_deterministic,
+        grouping_keys=grouping_keys, platform_specific=platform_specific)
+
+    self._page_set = page_set
+    # Default value of base_dir is the directory of the file that defines the
+    # class of this page instance.
+    if base_dir is None:
+      base_dir = os.path.dirname(inspect.getfile(self.__class__))
+    self._base_dir = base_dir
+    self._name = name
+    if credentials_path:
+      credentials_path = os.path.join(self._base_dir, credentials_path)
+      cloud_storage.GetIfChanged(credentials_path, credentials_bucket)
+      if not os.path.exists(credentials_path):
+        logging.error('Invalid credentials path: %s' % credentials_path)
+        credentials_path = None
+    self._credentials_path = credentials_path
+    self._cache_temperature = cache_temperature
+    if cache_temperature != cache_temperature_module.ANY:
+      self.grouping_keys['cache_temperature'] = cache_temperature
+    if traffic_setting != traffic_setting_module.NONE:
+      self.grouping_keys['traffic_setting'] = traffic_setting
+
+    assert traffic_setting in traffic_setting_module.NETWORK_CONFIGS, (
+        'Invalid traffic setting: %s' % traffic_setting)
+    self._traffic_setting = traffic_setting
+
+    # Whether to collect garbage on the page before navigating & performing
+    # page actions.
+    self._collect_garbage_before_run = True
+
+    # These attributes can be set dynamically by the page.
+    self.synthetic_delays = dict()
+    self._startup_url = startup_url
+    self.credentials = None
+    self.skip_waits = False
+    self.script_to_evaluate_on_commit = None
+    self._SchemeErrorCheck()
+
+  @property
+  def credentials_path(self):
+    return self._credentials_path
+
+  @property
+  def cache_temperature(self):
+    return self._cache_temperature
+
+  @property
+  def traffic_setting(self):
+    return self._traffic_setting
+
+  @property
+  def startup_url(self):
+    return self._startup_url
+
+  def _SchemeErrorCheck(self):
+    if not self._scheme:
+      raise ValueError('Must prepend the URL with scheme (e.g. file://)')
+
+    if self.startup_url:
+      startup_url_scheme = urlparse.urlparse(self.startup_url).scheme
+      if not startup_url_scheme:
+        raise ValueError('Must prepend the URL with scheme (e.g. http://)')
+      if startup_url_scheme == 'file':
+        raise ValueError('startup_url with local file scheme is not supported')
+
+  def Run(self, shared_state):
+    current_tab = shared_state.current_tab
+    # Collect garbage from previous run several times to make the results more
+    # stable if needed.
+    if self._collect_garbage_before_run:
+      for _ in xrange(0, 5):
+        current_tab.CollectGarbage()
+    shared_state.page_test.WillNavigateToPage(self, current_tab)
+    shared_state.page_test.RunNavigateSteps(self, current_tab)
+    shared_state.page_test.DidNavigateToPage(self, current_tab)
+    action_runner = action_runner_module.ActionRunner(
+        current_tab, skip_waits=self.skip_waits)
+    self.RunPageInteractions(action_runner)
+
+  def RunNavigateSteps(self, action_runner):
+    url = self.file_path_url_with_scheme if self.is_file else self.url
+    action_runner.Navigate(
+        url, script_to_evaluate_on_commit=self.script_to_evaluate_on_commit)
+
+  def RunPageInteractions(self, action_runner):
+    """Override this to define custom interactions with the page.
+    e.g:
+      def RunPageInteractions(self, action_runner):
+        action_runner.ScrollPage()
+        action_runner.TapElement(text='Next')
+    """
+    pass
+
+  def AsDict(self):
+    """Converts a page object to a dict suitable for JSON output."""
+    d = {
+        'id': self._id,
+        'url': self._url,
+    }
+    if self._name:
+      d['name'] = self._name
+    return d
+
+  @property
+  def story_set(self):
+    return self._page_set
+
+  # TODO(nednguyen, aiolos): deprecate this property.
+  @property
+  def page_set(self):
+    return self._page_set
+
+  @property
+  def url(self):
+    return self._url
+
+  def GetSyntheticDelayCategories(self):
+    result = []
+    for delay, options in self.synthetic_delays.items():
+      options = '%f;%s' % (options.get('target_duration', 0),
+                           options.get('mode', 'static'))
+      result.append('DELAY(%s;%s)' % (delay, options))
+    return result
+
+  def __lt__(self, other):
+    return self.url < other.url
+
+  def __cmp__(self, other):
+    x = cmp(self.name, other.name)
+    if x != 0:
+      return x
+    return cmp(self.url, other.url)
+
+  def __str__(self):
+    return self.url
+
+  @property
+  def _scheme(self):
+    return urlparse.urlparse(self.url).scheme
+
+  @property
+  def is_file(self):
+    """Returns True iff this URL points to a file."""
+    return self._scheme == 'file'
+
+  @property
+  def file_path(self):
+    """Returns the path of the file, stripping the scheme and query string."""
+    assert self.is_file
+    # Because ? is a valid character in a filename,
+    # we have to treat the URL as a non-file by removing the scheme.
+    parsed_url = urlparse.urlparse(self.url[7:])
+    return os.path.normpath(os.path.join(
+        self._base_dir, parsed_url.netloc + parsed_url.path))
+
+  @property
+  def base_dir(self):
+    return self._base_dir
+
+  @property
+  def file_path_url(self):
+    """Returns the file path, including the params, query, and fragment."""
+    assert self.is_file
+    file_path_url = os.path.normpath(
+        os.path.join(self._base_dir, self.url[7:]))
+    # Preserve trailing slash or backslash.
+    # It doesn't matter in a file path, but it does matter in a URL.
+    if self.url.endswith('/'):
+      file_path_url += os.sep
+    return file_path_url
+
+  @property
+  def file_path_url_with_scheme(self):
+    return 'file://' + self.file_path_url
+
+  @property
+  def serving_dir(self):
+    if not self.is_file:
+      return None
+    file_path = os.path.realpath(self.file_path)
+    if os.path.isdir(file_path):
+      return file_path
+    else:
+      return os.path.dirname(file_path)
+
+  @property
+  def display_name(self):
+    if self.name:
+      return self.name
+    if self.page_set is None or not self.is_file:
+      return self.url
+    all_urls = [p.url.rstrip('/') for p in self.page_set if p.is_file]
+    common_prefix = os.path.dirname(os.path.commonprefix(all_urls))
+    return self.url[len(common_prefix):].strip('/')
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/page/action_runner.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/page/action_runner.py
new file mode 100644
index 0000000..d250a57
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/page/action_runner.py
@@ -0,0 +1,7 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.internal.actions import action_runner
+
+ActionRunner = action_runner.ActionRunner
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/page/cache_temperature.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/page/cache_temperature.py
new file mode 100644
index 0000000..868bba5
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/page/cache_temperature.py
@@ -0,0 +1,90 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Cache temperature specifies how the browser cache should be configured before
+the page run.
+
+See design doc for details:
+https://docs.google.com/document/u/1/d/12D7tkhZi887g9d0U2askU9JypU_wYiEI7Lw0bfwxUgA
+"""
+
+import logging
+
+import py_utils
+
+# Default Cache Temperature. The page doesn't care which browser cache state
+# it is run on.
+ANY = 'any'
+# Emulates PageCycler V1 cold runs. Clears system DNS cache, browser DiskCache,
+# net/ predictor cache, and net/ host resolver cache.
+PCV1_COLD = 'pcv1-cold'
+# Emulates PageCycler V1 warm runs. Ensures that the page was visited at least
+# once just before the run.
+PCV1_WARM = 'pcv1-warm'
+
+class MarkTelemetryInternal(object):
+
+  def __init__(self, browser, identifier):
+    self.browser = browser
+    self.identifier = identifier
+
+  def __enter__(self):
+    marker = 'telemetry.internal.%s.start' % self.identifier
+    self.browser.tabs[0].ExecuteJavaScript(
+        "console.time({{ marker }});", marker=marker)
+    self.browser.tabs[0].ExecuteJavaScript(
+        "console.timeEnd({{ marker }});", marker=marker)
+    return self
+
+  def __exit__(self, exception_type, exception_value, traceback):
+    if exception_type:
+      return True
+
+    marker = 'telemetry.internal.%s.end' % self.identifier
+    self.browser.tabs[0].ExecuteJavaScript(
+        "console.time({{ marker }});", marker=marker)
+    self.browser.tabs[0].ExecuteJavaScript(
+        "console.timeEnd({{ marker }});", marker=marker)
+    return True
+
+def EnsurePageCacheTemperature(page, browser, previous_page=None):
+  temperature = page.cache_temperature
+  logging.info('PageCacheTemperature: %s', temperature)
+
+  if temperature == ANY:
+    return
+
+  if temperature == PCV1_COLD:
+    if previous_page is None:
+      with MarkTelemetryInternal(browser, 'ensure_diskcache'):
+        tab = browser.tabs[0]
+        tab.Navigate("http://does.not.exist")
+        tab.WaitForDocumentReadyStateToBeComplete()
+
+    any_tab = browser.tabs[0]
+    any_tab.ClearCache(force=True)
+  elif temperature == PCV1_WARM:
+    if (previous_page is not None and
+        previous_page.url == page.url and
+        (previous_page.cache_temperature == PCV1_COLD or
+            previous_page.cache_temperature == PCV1_WARM)):
+      if '#' in page.url:
+        # Navigate to inexistent URL to avoid in-page hash navigation.
+        # Note: Unlike PCv1, PCv2 iterates the same URL for different cache
+        #       configurations. This may issue blink in-page hash navigations,
+        #       which isn't intended here.
+        with MarkTelemetryInternal(browser, 'avoid_double_hash_navigation'):
+          tab = browser.tabs[0]
+          tab.Navigate("http://does.not.exist")
+          tab.WaitForDocumentReadyStateToBeComplete()
+      return
+
+    with MarkTelemetryInternal(browser, 'warmCache'):
+      tab = browser.tabs[0]
+      tab.Navigate(page.url)
+      py_utils.WaitFor(tab.HasReachedQuiescence, 60)
+      tab.WaitForDocumentReadyStateToBeComplete()
+      tab.Navigate("about:blank")
+      tab.WaitForDocumentReadyStateToBeComplete()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/page/cache_temperature_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/page/cache_temperature_unittest.py
new file mode 100644
index 0000000..37cd02e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/page/cache_temperature_unittest.py
@@ -0,0 +1,95 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import contextlib
+
+from telemetry import decorators
+from telemetry import page as page_module
+from telemetry import story
+from telemetry.page import cache_temperature
+from telemetry.testing import browser_test_case
+from telemetry.timeline import tracing_config
+from tracing.trace_data import trace_data
+
+
+class CacheTempeartureTests(browser_test_case.BrowserTestCase):
+  def __init__(self, *args, **kwargs):
+    super(CacheTempeartureTests, self).__init__(*args, **kwargs)
+    self._full_trace = None
+
+  @contextlib.contextmanager
+  def captureTrace(self):
+    tracing_controller = self._browser.platform.tracing_controller
+    options = tracing_config.TracingConfig()
+    options.enable_chrome_trace = True
+    tracing_controller.StartTracing(options)
+    try:
+      yield
+    finally:
+      self._full_trace = tracing_controller.StopTracing()
+
+  def traceMarkers(self):
+    if not self._full_trace:
+      return set()
+
+    chrome_trace = self._full_trace.GetTraceFor(trace_data.CHROME_TRACE_PART)
+    return set(
+        event['name']
+        for event in chrome_trace['traceEvents']
+        if event['cat'] == 'blink.console')
+
+  @decorators.Enabled('has tabs')
+  def testEnsureAny(self):
+    with self.captureTrace():
+      story_set = story.StorySet()
+      page = page_module.Page('http://google.com', page_set=story_set,
+          cache_temperature=cache_temperature.ANY)
+      cache_temperature.EnsurePageCacheTemperature(page, self._browser)
+
+    markers = self.traceMarkers()
+    self.assertNotIn('telemetry.internal.ensure_diskcache.start', markers)
+    self.assertNotIn('telemetry.internal.warmCache.start', markers)
+
+  @decorators.Enabled('has tabs')
+  @decorators.Disabled('chromeos')
+  def testEnsurePCv1Cold(self):
+    with self.captureTrace():
+      story_set = story.StorySet()
+      page = page_module.Page('http://google.com', page_set=story_set,
+          cache_temperature=cache_temperature.PCV1_COLD)
+      cache_temperature.EnsurePageCacheTemperature(page, self._browser)
+
+    markers = self.traceMarkers()
+    self.assertIn('telemetry.internal.ensure_diskcache.start', markers)
+    self.assertIn('telemetry.internal.ensure_diskcache.end', markers)
+
+  @decorators.Enabled('has tabs')
+  def testEnsurePCv1WarmAfterPCv1ColdRun(self):
+    with self.captureTrace():
+      story_set = story.StorySet()
+      page = page_module.Page('http://google.com', page_set=story_set,
+          cache_temperature=cache_temperature.PCV1_COLD)
+      cache_temperature.EnsurePageCacheTemperature(page, self._browser)
+
+      previous_page = page
+      page = page_module.Page('http://google.com', page_set=story_set,
+          cache_temperature=cache_temperature.PCV1_WARM)
+      cache_temperature.EnsurePageCacheTemperature(page, self._browser,
+          previous_page)
+
+    markers = self.traceMarkers()
+    self.assertNotIn('telemetry.internal.warmCache.start', markers)
+
+  @decorators.Enabled('has tabs')
+  @decorators.Disabled('chromeos')
+  def testEnsurePCv1WarmFromScratch(self):
+    with self.captureTrace():
+      story_set = story.StorySet()
+      page = page_module.Page('http://google.com', page_set=story_set,
+          cache_temperature=cache_temperature.PCV1_WARM)
+      cache_temperature.EnsurePageCacheTemperature(page, self._browser)
+
+    markers = self.traceMarkers()
+    self.assertIn('telemetry.internal.warmCache.start', markers)
+    self.assertIn('telemetry.internal.warmCache.end', markers)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/page/legacy_page_test.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/page/legacy_page_test.py
new file mode 100644
index 0000000..47b6f80
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/page/legacy_page_test.py
@@ -0,0 +1,195 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+
+from py_trace_event import trace_event
+
+from telemetry.core import exceptions
+from telemetry.internal.actions import action_runner as action_runner_module
+
+# Export story_test.Failure to this page_test module
+from telemetry.web_perf.story_test import Failure
+
+
+class TestNotSupportedOnPlatformError(Exception):
+  """LegacyPageTest Exception raised when a required feature is unavailable.
+
+  The feature required to run the test could be part of the platform,
+  hardware configuration, or browser.
+  """
+
+
+class MultiTabTestAppCrashError(Exception):
+  """Exception raised after browser or tab crash for multi-tab tests.
+
+  Used to abort the test rather than try to recover from an unknown state.
+  """
+
+
+class MeasurementFailure(Failure):
+  """Exception raised when an undesired but designed-for problem."""
+
+
+class LegacyPageTest(object):
+  """A class styled on unittest.TestCase for creating page-specific tests.
+
+  Note that this method of measuring browser's performance is obsolete and only
+  here for "historical" reason. For your performance measurement need, please
+  use TimelineBasedMeasurement instead: https://goo.gl/eMvikK
+
+  For correctness testing, please use
+  serially_executed_browser_test_case.SeriallyExecutedBrowserTestCase
+  instead. See examples in:
+  https://github.com/catapult-project/catapult/tree/master/telemetry/examples/browser_tests
+
+  Test should override ValidateAndMeasurePage to perform test
+  validation and page measurement as necessary.
+
+     class BodyChildElementMeasurement(LegacyPageTest):
+       def ValidateAndMeasurePage(self, page, tab, results):
+         body_child_count = tab.EvaluateJavaScript(
+             'document.body.children.length')
+         results.AddValue(scalar.ScalarValue(
+             page, 'body_children', 'count', body_child_count))
+  """
+
+  __metaclass__ = trace_event.TracedMetaClass
+
+  def __init__(self,
+               needs_browser_restart_after_each_page=False,
+               clear_cache_before_each_run=False):
+    super(LegacyPageTest, self).__init__()
+
+    self.options = None
+    self._needs_browser_restart_after_each_page = (
+        needs_browser_restart_after_each_page)
+    self._clear_cache_before_each_run = clear_cache_before_each_run
+    self._close_tabs_before_run = True
+
+  @property
+  def is_multi_tab_test(self):
+    """Returns True if the test opens multiple tabs.
+
+    If the test overrides TabForPage, it is deemed a multi-tab test.
+    Multi-tab tests do not retry after tab or browser crashes, whereas,
+    single-tab tests too. That is because the state of multi-tab tests
+    (e.g., how many tabs are open, etc.) is unknown after crashes.
+    """
+    return self.TabForPage.__func__ is not LegacyPageTest.TabForPage.__func__
+
+  @property
+  def clear_cache_before_each_run(self):
+    """When set to True, the browser's disk and memory cache will be cleared
+    before each run."""
+    return self._clear_cache_before_each_run
+
+  @property
+  def close_tabs_before_run(self):
+    """When set to True, all tabs are closed before running the test for the
+    first time."""
+    return self._close_tabs_before_run
+
+  @close_tabs_before_run.setter
+  def close_tabs_before_run(self, close_tabs):
+    self._close_tabs_before_run = close_tabs
+
+  def RestartBrowserBeforeEachPage(self):
+    """ Should the browser be restarted for the page?
+
+    This returns true if the test needs to unconditionally restart the
+    browser for each page. It may be called before the browser is started.
+    """
+    return self._needs_browser_restart_after_each_page
+
+  def StopBrowserAfterPage(self, browser, page):
+    """Should the browser be stopped after the page is run?
+
+    This is called after a page is run to decide whether the browser needs to
+    be stopped to clean up its state. If it is stopped, then it will be
+    restarted to run the next page.
+
+    A test that overrides this can look at both the page and the browser to
+    decide whether it needs to stop the browser.
+    """
+    del browser, page  # unused
+    return False
+
+  def CustomizeBrowserOptions(self, options):
+    """Override to add test-specific options to the BrowserOptions object"""
+
+  def WillStartBrowser(self, platform):
+    """Override to manipulate the browser environment before it launches."""
+
+  def DidStartBrowser(self, browser):
+    """Override to customize the browser right after it has launched."""
+
+  def SetOptions(self, options):
+    """Sets the BrowserFinderOptions instance to use."""
+    self.options = options
+
+  def WillNavigateToPage(self, page, tab):
+    """Override to do operations before the page is navigated, notably Telemetry
+    will already have performed the following operations on the browser before
+    calling this function:
+    * Ensure only one tab is open.
+    * Call WaitForDocumentReadyStateToComplete on the tab."""
+
+  def DidNavigateToPage(self, page, tab):
+    """Override to do operations right after the page is navigated and after
+    all waiting for completion has occurred."""
+
+  def DidRunPage(self, platform):
+    """Called after the test run method was run, even if it failed."""
+
+  def TabForPage(self, page, browser):   # pylint: disable=unused-argument
+    """Override to select a different tab for the page.  For instance, to
+    create a new tab for every page, return browser.tabs.New()."""
+    try:
+      return browser.tabs[0]
+    # The tab may have gone away in some case, so we create a new tab and retry
+    # (See crbug.com/496280)
+    except exceptions.DevtoolsTargetCrashException as e:
+      logging.error('Tab may have crashed: %s' % str(e))
+      browser.tabs.New()
+      # See comment in shared_page_state.WillRunStory for why this waiting
+      # is needed.
+      browser.tabs[0].WaitForDocumentReadyStateToBeComplete()
+      return browser.tabs[0]
+
+  def ValidateAndMeasurePage(self, page, tab, results):
+    """Override to check test assertions and perform measurement.
+
+    When adding measurement results, call results.AddValue(...) for
+    each result. Raise an exception or add a failure.FailureValue on
+    failure. legacy_page_test.py also provides several base exception classes
+    to use.
+
+    Prefer metric value names that are in accordance with python
+    variable style. e.g., metric_name. The name 'url' must not be used.
+
+    Put together:
+      def ValidateAndMeasurePage(self, page, tab, results):
+        res = tab.EvaluateJavaScript('2+2')
+        if res != 4:
+          raise Exception('Oh, wow.')
+        results.AddValue(scalar.ScalarValue(
+            page, 'two_plus_two', 'count', res))
+
+    Args:
+      page: A telemetry.page.Page instance.
+      tab: A telemetry.core.Tab instance.
+      results: A telemetry.results.PageTestResults instance.
+    """
+    raise NotImplementedError
+
+  # Deprecated: do not use this hook. (crbug.com/470147)
+  def RunNavigateSteps(self, page, tab):
+    """Navigates the tab to the page URL attribute.
+
+    Runs the 'navigate_steps' page attribute as a compound action.
+    """
+    action_runner = action_runner_module.ActionRunner(
+        tab, skip_waits=page.skip_waits)
+    page.RunNavigateSteps(action_runner)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/page/page.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/page/page.py
new file mode 100644
index 0000000..b838894
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/page/page.py
@@ -0,0 +1,8 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# TODO(eakuefner): Refactor references to Page and kill this hack.
+from telemetry import page
+
+Page = page.Page
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/page/page_run_end_to_end_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/page/page_run_end_to_end_unittest.py
new file mode 100644
index 0000000..b18eb31
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/page/page_run_end_to_end_unittest.py
@@ -0,0 +1,788 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import re
+import shutil
+import sys
+import StringIO
+import tempfile
+import time
+import unittest
+
+from telemetry import benchmark
+from telemetry import story
+from telemetry.core import exceptions
+from telemetry.core import util
+from telemetry import decorators
+from telemetry.internal.browser import browser_finder
+from telemetry.internal.browser import user_agent
+from telemetry.internal.results import results_options
+from telemetry.internal import story_runner
+from telemetry.internal.testing.test_page_sets import example_domain
+from telemetry.internal.util import exception_formatter
+from telemetry.page import page as page_module
+from telemetry.page import legacy_page_test
+from telemetry.page import shared_page_state
+from telemetry.page import traffic_setting as traffic_setting_module
+from telemetry.util import image_util
+from telemetry.testing import fakes
+from telemetry.testing import options_for_unittests
+from telemetry.testing import system_stub
+
+
+SIMPLE_CREDENTIALS_STRING = """
+{
+  "test": {
+    "username": "example",
+    "password": "asdf"
+  }
+}
+"""
+
+
+class DummyTest(legacy_page_test.LegacyPageTest):
+
+  def ValidateAndMeasurePage(self, *_):
+    pass
+
+
+def SetUpStoryRunnerArguments(options):
+  parser = options.CreateParser()
+  story_runner.AddCommandLineArgs(parser)
+  options.MergeDefaultValues(parser.get_default_values())
+  story_runner.ProcessCommandLineArgs(parser, options)
+
+
+class EmptyMetadataForTest(benchmark.BenchmarkMetadata):
+
+  def __init__(self):
+    super(EmptyMetadataForTest, self).__init__('')
+
+
+class StubCredentialsBackend(object):
+
+  def __init__(self, login_return_value):
+    self.did_get_login = False
+    self.did_get_login_no_longer_needed = False
+    self.login_return_value = login_return_value
+
+  @property
+  def credentials_type(self):
+    return 'test'
+
+  def LoginNeeded(self, *_):
+    self.did_get_login = True
+    return self.login_return_value
+
+  def LoginNoLongerNeeded(self, _):
+    self.did_get_login_no_longer_needed = True
+
+
+def GetSuccessfulPageRuns(results):
+  return [run for run in results.all_page_runs if run.ok or run.skipped]
+
+
+def CaptureStderr(func, output_buffer):
+  def wrapper(*args, **kwargs):
+    original_stderr, sys.stderr = sys.stderr, output_buffer
+    try:
+      return func(*args, **kwargs)
+    finally:
+      sys.stderr = original_stderr
+  return wrapper
+
+
+# TODO: remove test cases that use real browsers and replace with a
+# story_runner or shared_page_state unittest that tests the same logic.
+class ActualPageRunEndToEndTests(unittest.TestCase):
+  # TODO(nduca): Move the basic "test failed, test succeeded" tests from
+  # page_test_unittest to here.
+
+  def setUp(self):
+    self._story_runner_logging_stub = None
+    self._formatted_exception_buffer = StringIO.StringIO()
+    self._original_formatter = exception_formatter.PrintFormattedException
+
+  def tearDown(self):
+    self.RestoreExceptionFormatter()
+
+  def CaptureFormattedException(self):
+    exception_formatter.PrintFormattedException = CaptureStderr(
+        exception_formatter.PrintFormattedException,
+        self._formatted_exception_buffer)
+    self._story_runner_logging_stub = system_stub.Override(
+        story_runner, ['logging'])
+
+  @property
+  def formatted_exception(self):
+    return self._formatted_exception_buffer.getvalue()
+
+  def RestoreExceptionFormatter(self):
+    exception_formatter.PrintFormattedException = self._original_formatter
+    if self._story_runner_logging_stub:
+      self._story_runner_logging_stub.Restore()
+      self._story_runner_logging_stub = None
+
+  def assertFormattedExceptionIsEmpty(self):
+    self.longMessage = False
+    self.assertEquals(
+        '', self.formatted_exception,
+        msg='Expected empty formatted exception: actual=%s' % '\n   > '.join(
+            self.formatted_exception.split('\n')))
+
+  def assertFormattedExceptionOnlyHas(self, expected_exception_name):
+    self.longMessage = True
+    actual_exception_names = re.findall(r'^Traceback.*?^(\w+)',
+                                        self.formatted_exception,
+                                        re.DOTALL | re.MULTILINE)
+    self.assertEquals([expected_exception_name], actual_exception_names,
+                      msg='Full formatted exception: %s' % '\n   > '.join(
+                          self.formatted_exception.split('\n')))
+
+  def testRaiseBrowserGoneExceptionFromRestartBrowserBeforeEachPage(self):
+    self.CaptureFormattedException()
+    story_set = story.StorySet()
+    story_set.AddStory(page_module.Page(
+        'file://blank.html', story_set, base_dir=util.GetUnittestDataDir(),
+        name='foo'))
+    story_set.AddStory(page_module.Page(
+        'file://blank.html', story_set, base_dir=util.GetUnittestDataDir(),
+        name='bar'))
+    story_set.AddStory(page_module.Page(
+        'file://blank.html', story_set, base_dir=util.GetUnittestDataDir(),
+        name='baz'))
+
+    class Test(legacy_page_test.LegacyPageTest):
+
+      def __init__(self, *args):
+        super(Test, self).__init__(
+            *args, needs_browser_restart_after_each_page=True)
+        self.run_count = 0
+
+      def RestartBrowserBeforeEachPage(self):
+        # This will only be called twice with 3 pages.
+        old_run_count = self.run_count
+        self.run_count += 1
+        if old_run_count == 1:
+          raise exceptions.BrowserGoneException(None)
+        return self._needs_browser_restart_after_each_page
+
+      def ValidateAndMeasurePage(self, page, tab, results):
+        pass
+
+    options = options_for_unittests.GetCopy()
+    options.output_formats = ['none']
+    options.suppress_gtest_report = True
+    test = Test()
+    SetUpStoryRunnerArguments(options)
+    results = results_options.CreateResults(EmptyMetadataForTest(), options)
+    story_runner.Run(test, story_set, options, results)
+    self.assertEquals(2, test.run_count)
+    self.assertEquals(2, len(GetSuccessfulPageRuns(results)))
+    self.assertEquals(1, len(results.failures))
+    self.assertFormattedExceptionIsEmpty()
+
+  def testNeedsBrowserRestartAfterEachPage(self):
+    self.CaptureFormattedException()
+    story_set = story.StorySet()
+    story_set.AddStory(page_module.Page(
+        'file://blank.html', story_set, base_dir=util.GetUnittestDataDir(),
+        name='foo'))
+    story_set.AddStory(page_module.Page(
+        'file://blank.html', story_set, base_dir=util.GetUnittestDataDir(),
+        name='bar'))
+
+    class Test(legacy_page_test.LegacyPageTest):
+
+      def __init__(self, *args, **kwargs):
+        super(Test, self).__init__(*args, **kwargs)
+        self.browser_starts = 0
+
+      def DidStartBrowser(self, *args):
+        super(Test, self).DidStartBrowser(*args)
+        self.browser_starts += 1
+
+      def ValidateAndMeasurePage(self, page, tab, results):
+        pass
+
+    options = options_for_unittests.GetCopy()
+    options.output_formats = ['none']
+    options.suppress_gtest_report = True
+    test = Test(needs_browser_restart_after_each_page=True)
+    SetUpStoryRunnerArguments(options)
+    results = results_options.CreateResults(EmptyMetadataForTest(), options)
+    story_runner.Run(test, story_set, options, results)
+    self.assertEquals(2, len(GetSuccessfulPageRuns(results)))
+    self.assertEquals(2, test.browser_starts)
+    self.assertFormattedExceptionIsEmpty()
+
+  def testCredentialsWhenLoginFails(self):
+    self.CaptureFormattedException()
+    credentials_backend = StubCredentialsBackend(login_return_value=False)
+    did_run = self.runCredentialsTest(credentials_backend)
+    assert credentials_backend.did_get_login
+    assert not credentials_backend.did_get_login_no_longer_needed
+    assert not did_run
+    self.assertFormattedExceptionIsEmpty()
+
+  def testCredentialsWhenLoginSucceeds(self):
+    credentials_backend = StubCredentialsBackend(login_return_value=True)
+    did_run = self.runCredentialsTest(credentials_backend)
+    assert credentials_backend.did_get_login
+    assert credentials_backend.did_get_login_no_longer_needed
+    assert did_run
+
+  def runCredentialsTest(self, credentials_backend):
+    story_set = story.StorySet()
+    did_run = [False]
+
+    try:
+      with tempfile.NamedTemporaryFile(delete=False) as f:
+        page = page_module.Page(
+            'file://blank.html', story_set, base_dir=util.GetUnittestDataDir(),
+            credentials_path=f.name)
+        page.credentials = "test"
+        story_set.AddStory(page)
+
+        f.write(SIMPLE_CREDENTIALS_STRING)
+
+      class TestThatInstallsCredentialsBackend(legacy_page_test.LegacyPageTest):
+
+        def __init__(self, credentials_backend):
+          super(TestThatInstallsCredentialsBackend, self).__init__()
+          self._credentials_backend = credentials_backend
+
+        def DidStartBrowser(self, browser):
+          browser.credentials.AddBackend(self._credentials_backend)
+
+        def ValidateAndMeasurePage(self, *_):
+          did_run[0] = True
+
+      test = TestThatInstallsCredentialsBackend(credentials_backend)
+      options = options_for_unittests.GetCopy()
+      options.output_formats = ['none']
+      options.suppress_gtest_report = True
+      SetUpStoryRunnerArguments(options)
+      results = results_options.CreateResults(EmptyMetadataForTest(), options)
+      story_runner.Run(test, story_set, options, results)
+    finally:
+      os.remove(f.name)
+
+    return did_run[0]
+
+  @decorators.Disabled('chromeos')  # crbug.com/483212
+  def testUserAgent(self):
+    story_set = story.StorySet()
+    page = page_module.Page(
+        'file://blank.html', story_set, base_dir=util.GetUnittestDataDir(),
+        shared_page_state_class=shared_page_state.SharedTabletPageState)
+    story_set.AddStory(page)
+
+    class TestUserAgent(legacy_page_test.LegacyPageTest):
+      def ValidateAndMeasurePage(self, page, tab, results):
+        del page, results  # unused
+        actual_user_agent = tab.EvaluateJavaScript(
+            'window.navigator.userAgent')
+        expected_user_agent = user_agent.UA_TYPE_MAPPING['tablet']
+        assert actual_user_agent.strip() == expected_user_agent
+
+        # This is so we can check later that the test actually made it into this
+        # function. Previously it was timing out before even getting here, which
+        # should fail, but since it skipped all the asserts, it slipped by.
+        self.hasRun = True  # pylint: disable=attribute-defined-outside-init
+
+    test = TestUserAgent()
+    options = options_for_unittests.GetCopy()
+    options.output_formats = ['none']
+    options.suppress_gtest_report = True
+    SetUpStoryRunnerArguments(options)
+    results = results_options.CreateResults(EmptyMetadataForTest(), options)
+    story_runner.Run(test, story_set, options, results)
+
+    self.assertTrue(hasattr(test, 'hasRun') and test.hasRun)
+
+  # Ensure that story_runner forces exactly 1 tab before running a page.
+  @decorators.Enabled('has tabs')
+  def testOneTab(self):
+    story_set = story.StorySet()
+    page = page_module.Page(
+        'file://blank.html', story_set, base_dir=util.GetUnittestDataDir())
+    story_set.AddStory(page)
+
+    class TestOneTab(legacy_page_test.LegacyPageTest):
+
+      def DidStartBrowser(self, browser):
+        browser.tabs.New()
+
+      def ValidateAndMeasurePage(self, page, tab, results):
+        del page, results  # unused
+        assert len(tab.browser.tabs) == 1
+
+    test = TestOneTab()
+    options = options_for_unittests.GetCopy()
+    options.output_formats = ['none']
+    options.suppress_gtest_report = True
+    SetUpStoryRunnerArguments(options)
+    results = results_options.CreateResults(EmptyMetadataForTest(), options)
+    story_runner.Run(test, story_set, options, results)
+
+  @decorators.Disabled('chromeos')  # crbug.com/652385
+  def testTrafficSettings(self):
+    story_set = story.StorySet()
+    slow_page = page_module.Page(
+        'file://green_rect.html', story_set, base_dir=util.GetUnittestDataDir(),
+        name='slow',
+        traffic_setting=traffic_setting_module.REGULAR_2G)
+    fast_page = page_module.Page(
+        'file://green_rect.html', story_set, base_dir=util.GetUnittestDataDir(),
+        name='fast',
+        traffic_setting=traffic_setting_module.WIFI)
+    story_set.AddStory(slow_page)
+    story_set.AddStory(fast_page)
+
+    latencies_by_page_in_ms = {}
+
+    class MeasureLatency(legacy_page_test.LegacyPageTest):
+      def __init__(self):
+        super(MeasureLatency, self).__init__()
+        self._will_navigate_time = None
+
+      def WillNavigateToPage(self, page, tab):
+        del page, tab # unused
+        self._will_navigate_time = time.time() * 1000
+
+      def ValidateAndMeasurePage(self, page, tab, results):
+        del results  # unused
+        latencies_by_page_in_ms[page.name] = (
+            time.time() * 1000 - self._will_navigate_time)
+
+    test = MeasureLatency()
+    options = options_for_unittests.GetCopy()
+    options.output_formats = ['none']
+    options.suppress_gtest_report = True
+    SetUpStoryRunnerArguments(options)
+    results = results_options.CreateResults(EmptyMetadataForTest(), options)
+    story_runner.Run(test, story_set, options, results)
+    # Slow page should be slower than fast page by at least 300 ms (roundtrip
+    # time of 2G) - 2 ms (roundtrip time of Wifi)
+    self.assertGreater(latencies_by_page_in_ms['slow'],
+                       latencies_by_page_in_ms['fast'] + 300 - 2)
+
+  # Ensure that story_runner allows >1 tab for multi-tab test.
+  @decorators.Enabled('has tabs')
+  def testMultipleTabsOkayForMultiTabTest(self):
+    story_set = story.StorySet()
+    page = page_module.Page(
+        'file://blank.html', story_set, base_dir=util.GetUnittestDataDir())
+    story_set.AddStory(page)
+
+    class TestMultiTabs(legacy_page_test.LegacyPageTest):
+      def TabForPage(self, page, browser):
+        del page  # unused
+        return browser.tabs.New()
+
+      def ValidateAndMeasurePage(self, page, tab, results):
+        del page, results  # unused
+        assert len(tab.browser.tabs) == 2
+
+    test = TestMultiTabs()
+    options = options_for_unittests.GetCopy()
+    options.output_formats = ['none']
+    options.suppress_gtest_report = True
+    SetUpStoryRunnerArguments(options)
+    results = results_options.CreateResults(EmptyMetadataForTest(), options)
+    story_runner.Run(test, story_set, options, results)
+
+  # Ensure that story_runner allows the test to customize the browser
+  # before it launches.
+  def testBrowserBeforeLaunch(self):
+    story_set = story.StorySet()
+    page = page_module.Page(
+        'file://blank.html', story_set, base_dir=util.GetUnittestDataDir())
+    story_set.AddStory(page)
+
+    class TestBeforeLaunch(legacy_page_test.LegacyPageTest):
+
+      def __init__(self):
+        super(TestBeforeLaunch, self).__init__()
+        self._did_call_will_start = False
+        self._did_call_did_start = False
+
+      def WillStartBrowser(self, platform):
+        self._did_call_will_start = True
+        # TODO(simonjam): Test that the profile is available.
+
+      def DidStartBrowser(self, browser):
+        assert self._did_call_will_start
+        self._did_call_did_start = True
+
+      def ValidateAndMeasurePage(self, *_):
+        assert self._did_call_did_start
+
+    test = TestBeforeLaunch()
+    options = options_for_unittests.GetCopy()
+    options.output_formats = ['none']
+    options.suppress_gtest_report = True
+    SetUpStoryRunnerArguments(options)
+    results = results_options.CreateResults(EmptyMetadataForTest(), options)
+    story_runner.Run(test, story_set, options, results)
+
+  def testRunPageWithStartupUrl(self):
+    num_times_browser_closed = [0]
+
+    class TestSharedState(shared_page_state.SharedPageState):
+
+      def _StopBrowser(self):
+        super(TestSharedState, self)._StopBrowser()
+        num_times_browser_closed[0] += 1
+
+    story_set = story.StorySet()
+    page = page_module.Page(
+        'file://blank.html', story_set, base_dir=util.GetUnittestDataDir(),
+        startup_url='about:blank', shared_page_state_class=TestSharedState)
+    story_set.AddStory(page)
+
+    class Measurement(legacy_page_test.LegacyPageTest):
+
+      def __init__(self):
+        super(Measurement, self).__init__()
+
+      def ValidateAndMeasurePage(self, page, tab, results):
+        del page, tab, results  # not used
+
+    options = options_for_unittests.GetCopy()
+    options.pageset_repeat = 2
+    options.output_formats = ['none']
+    options.suppress_gtest_report = True
+    if not browser_finder.FindBrowser(options):
+      return
+    test = Measurement()
+    SetUpStoryRunnerArguments(options)
+    results = results_options.CreateResults(EmptyMetadataForTest(), options)
+    story_runner.Run(test, story_set, options, results)
+    self.assertEquals('about:blank', options.browser_options.startup_url)
+    # _StopBrowser should be called 2 times:
+    # 1. browser restarts after page 1 run
+    # 2. in the TearDownState after all the pages have run.
+    self.assertEquals(num_times_browser_closed[0], 2)
+
+  # Ensure that story_runner calls cleanUp when a page run fails.
+  def testCleanUpPage(self):
+    story_set = story.StorySet()
+    page = page_module.Page(
+        'file://blank.html', story_set, base_dir=util.GetUnittestDataDir())
+    story_set.AddStory(page)
+
+    class Test(legacy_page_test.LegacyPageTest):
+
+      def __init__(self):
+        super(Test, self).__init__()
+        self.did_call_clean_up = False
+
+      def ValidateAndMeasurePage(self, *_):
+        raise legacy_page_test.Failure
+
+      def DidRunPage(self, platform):
+        del platform  # unused
+        self.did_call_clean_up = True
+
+    test = Test()
+    options = options_for_unittests.GetCopy()
+    options.output_formats = ['none']
+    options.suppress_gtest_report = True
+    SetUpStoryRunnerArguments(options)
+    results = results_options.CreateResults(EmptyMetadataForTest(), options)
+    story_runner.Run(test, story_set, options, results)
+    assert test.did_call_clean_up
+
+  # Ensure skipping the test if shared state cannot be run on the browser.
+  def testSharedPageStateCannotRunOnBrowser(self):
+    story_set = story.StorySet()
+
+    class UnrunnableSharedState(shared_page_state.SharedPageState):
+      def CanRunOnBrowser(self, browser_info, page):
+        del browser_info, page  # unused
+        return False
+
+      def ValidateAndMeasurePage(self, _):
+        pass
+
+    story_set.AddStory(page_module.Page(
+        url='file://blank.html', page_set=story_set,
+        base_dir=util.GetUnittestDataDir(),
+        shared_page_state_class=UnrunnableSharedState))
+
+    class Test(legacy_page_test.LegacyPageTest):
+
+      def __init__(self, *args, **kwargs):
+        super(Test, self).__init__(*args, **kwargs)
+        self.will_navigate_to_page_called = False
+
+      def ValidateAndMeasurePage(self, *args):
+        del args  # unused
+        raise Exception('Exception should not be thrown')
+
+      def WillNavigateToPage(self, page, tab):
+        del page, tab  # unused
+        self.will_navigate_to_page_called = True
+
+    test = Test()
+    options = options_for_unittests.GetCopy()
+    options.output_formats = ['none']
+    options.suppress_gtest_report = True
+    SetUpStoryRunnerArguments(options)
+    results = results_options.CreateResults(EmptyMetadataForTest(), options)
+    story_runner.Run(test, story_set, options, results)
+    self.assertFalse(test.will_navigate_to_page_called)
+    self.assertEquals(1, len(GetSuccessfulPageRuns(results)))
+    self.assertEquals(1, len(results.skipped_values))
+    self.assertEquals(0, len(results.failures))
+
+  def testRunPageWithProfilingFlag(self):
+    story_set = story.StorySet()
+    story_set.AddStory(page_module.Page(
+        'file://blank.html', story_set, base_dir=util.GetUnittestDataDir()))
+
+    class Measurement(legacy_page_test.LegacyPageTest):
+
+      def ValidateAndMeasurePage(self, page, tab, results):
+        pass
+
+    options = options_for_unittests.GetCopy()
+    options.output_formats = ['none']
+    options.suppress_gtest_report = True
+    options.reset_results = None
+    options.upload_results = None
+    options.results_label = None
+    options.output_dir = tempfile.mkdtemp()
+    options.profiler = 'trace'
+    try:
+      SetUpStoryRunnerArguments(options)
+      results = results_options.CreateResults(EmptyMetadataForTest(), options)
+      story_runner.Run(Measurement(), story_set, options, results)
+      self.assertEquals(1, len(GetSuccessfulPageRuns(results)))
+      self.assertEquals(0, len(results.failures))
+      self.assertEquals(0, len(results.all_page_specific_values))
+      self.assertTrue(os.path.isfile(
+          os.path.join(options.output_dir, 'blank_html.html')))
+    finally:
+      shutil.rmtree(options.output_dir)
+
+  def _RunPageTestThatRaisesAppCrashException(self, test, max_failures):
+    class TestPage(page_module.Page):
+
+      def RunNavigateSteps(self, _):
+        raise exceptions.AppCrashException
+
+    story_set = story.StorySet()
+    for i in range(5):
+      story_set.AddStory(
+          TestPage('file://blank.html', story_set,
+                   base_dir=util.GetUnittestDataDir(), name='foo%d' % i))
+    options = options_for_unittests.GetCopy()
+    options.output_formats = ['none']
+    options.suppress_gtest_report = True
+    SetUpStoryRunnerArguments(options)
+    results = results_options.CreateResults(EmptyMetadataForTest(), options)
+    story_runner.Run(test, story_set, options, results,
+                     max_failures=max_failures)
+    return results
+
+  def testSingleTabMeansCrashWillCauseFailureValue(self):
+    self.CaptureFormattedException()
+
+    class SingleTabTest(legacy_page_test.LegacyPageTest):
+      # Test is not multi-tab because it does not override TabForPage.
+
+      def ValidateAndMeasurePage(self, *_):
+        pass
+
+    test = SingleTabTest()
+    results = self._RunPageTestThatRaisesAppCrashException(
+        test, max_failures=1)
+    self.assertEquals([], GetSuccessfulPageRuns(results))
+    self.assertEquals(2, len(results.failures))  # max_failures + 1
+    self.assertFormattedExceptionIsEmpty()
+
+  @decorators.Enabled('has tabs')
+  def testMultipleTabsMeansCrashRaises(self):
+    self.CaptureFormattedException()
+
+    class MultipleTabsTest(legacy_page_test.LegacyPageTest):
+      # Test *is* multi-tab because it overrides TabForPage.
+
+      def TabForPage(self, page, browser):
+        return browser.tabs.New()
+
+      def ValidateAndMeasurePage(self, *_):
+        pass
+
+    test = MultipleTabsTest()
+    with self.assertRaises(legacy_page_test.MultiTabTestAppCrashError):
+      self._RunPageTestThatRaisesAppCrashException(test, max_failures=1)
+    self.assertFormattedExceptionOnlyHas('AppCrashException')
+
+  def testWebPageReplay(self):
+    story_set = example_domain.ExampleDomainPageSet()
+    body = []
+
+    class TestWpr(legacy_page_test.LegacyPageTest):
+      def ValidateAndMeasurePage(self, page, tab, results):
+        del page, results  # unused
+        body.append(tab.EvaluateJavaScript('document.body.innerText'))
+
+      def DidRunPage(self, platform):
+        # Force the replay server to restart between pages; this verifies that
+        # the restart mechanism works.
+        platform.network_controller.StopReplay()
+
+    test = TestWpr()
+    options = options_for_unittests.GetCopy()
+    options.output_formats = ['none']
+    options.suppress_gtest_report = True
+    SetUpStoryRunnerArguments(options)
+    results = results_options.CreateResults(EmptyMetadataForTest(), options)
+
+    story_runner.Run(test, story_set, options, results)
+
+    self.longMessage = True
+    self.assertIn('Example Domain', body[0],
+                  msg='URL: %s' % story_set.stories[0].url)
+    self.assertIn('Example Domain', body[1],
+                  msg='URL: %s' % story_set.stories[1].url)
+
+    self.assertEquals(2, len(GetSuccessfulPageRuns(results)))
+    self.assertEquals(0, len(results.failures))
+
+  def testScreenShotTakenForFailedPage(self):
+    self.CaptureFormattedException()
+    platform_screenshot_supported = [False]
+    tab_screenshot_supported = [False]
+    chrome_version_screen_shot = [None]
+
+    class FailingTestPage(page_module.Page):
+
+      def RunNavigateSteps(self, action_runner):
+        action_runner.Navigate(self._url)
+        platform_screenshot_supported[0] = (
+            action_runner.tab.browser.platform.CanTakeScreenshot)
+        tab_screenshot_supported[0] = action_runner.tab.screenshot_supported
+        if not platform_screenshot_supported[0] and tab_screenshot_supported[0]:
+          chrome_version_screen_shot[0] = action_runner.tab.Screenshot()
+        raise exceptions.AppCrashException
+
+    story_set = story.StorySet()
+    story_set.AddStory(page_module.Page('file://blank.html', story_set))
+    failing_page = FailingTestPage('chrome://version', story_set)
+    story_set.AddStory(failing_page)
+    options = options_for_unittests.GetCopy()
+    options.output_formats = ['none']
+    options.browser_options.take_screenshot_for_failed_page = True
+    options.suppress_gtest_report = True
+    SetUpStoryRunnerArguments(options)
+    results = results_options.CreateResults(EmptyMetadataForTest(), options)
+    story_runner.Run(DummyTest(), story_set, options, results,
+                     max_failures=2)
+    self.assertEquals(1, len(results.failures))
+    if not platform_screenshot_supported[0] and tab_screenshot_supported[0]:
+      self.assertEquals(1, len(results.pages_to_profiling_files))
+      self.assertIn(failing_page,
+                    results.pages_to_profiling_files)
+      screenshot_file_path = (
+          results.pages_to_profiling_files[failing_page][0].GetAbsPath())
+      try:
+        actual_screenshot = image_util.FromPngFile(screenshot_file_path)
+        self.assertEquals(image_util.Pixels(chrome_version_screen_shot[0]),
+                          image_util.Pixels(actual_screenshot))
+      finally:  # Must clean up screenshot file if exists.
+        os.remove(screenshot_file_path)
+
+  def testNoProfilingFilesCreatedForPageByDefault(self):
+    self.CaptureFormattedException()
+
+    class FailingTestPage(page_module.Page):
+
+      def RunNavigateSteps(self, action_runner):
+        action_runner.Navigate(self._url)
+        raise exceptions.AppCrashException
+
+    story_set = story.StorySet()
+    story_set.AddStory(page_module.Page('file://blank.html', story_set))
+    failing_page = FailingTestPage('chrome://version', story_set)
+    story_set.AddStory(failing_page)
+    options = options_for_unittests.GetCopy()
+    options.output_formats = ['none']
+    options.suppress_gtest_report = True
+    SetUpStoryRunnerArguments(options)
+    results = results_options.CreateResults(EmptyMetadataForTest(), options)
+    story_runner.Run(DummyTest(), story_set, options, results,
+                     max_failures=2)
+    self.assertEquals(1, len(results.failures))
+    self.assertEquals(0, len(results.pages_to_profiling_files))
+
+
+class FakePageRunEndToEndTests(unittest.TestCase):
+
+  def setUp(self):
+    self.options = fakes.CreateBrowserFinderOptions()
+    self.options.output_formats = ['none']
+    self.options.suppress_gtest_report = True
+    SetUpStoryRunnerArguments(self.options)
+
+  def testNoScreenShotTakenForFailedPageDueToNoSupport(self):
+    self.options.browser_options.take_screenshot_for_failed_page = True
+
+    class FailingTestPage(page_module.Page):
+
+      def RunNavigateSteps(self, action_runner):
+        raise exceptions.AppCrashException
+
+    story_set = story.StorySet()
+    story_set.AddStory(page_module.Page('file://blank.html', story_set))
+    failing_page = FailingTestPage('chrome://version', story_set)
+    story_set.AddStory(failing_page)
+    results = results_options.CreateResults(
+        EmptyMetadataForTest(), self.options)
+    story_runner.Run(DummyTest(), story_set, self.options, results,
+                     max_failures=2)
+    self.assertEquals(1, len(results.failures))
+    self.assertEquals(0, len(results.pages_to_profiling_files))
+
+  def testScreenShotTakenForFailedPageOnSupportedPlatform(self):
+    fake_platform = self.options.fake_possible_browser.returned_browser.platform
+    expected_png_base64 = """
+ iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91
+ JpzAAAAFklEQVR4Xg3EAQ0AAABAMP1LY3YI7l8l6A
+ T8tgwbJAAAAABJRU5ErkJggg==
+"""
+    fake_platform.screenshot_png_data = expected_png_base64
+    self.options.browser_options.take_screenshot_for_failed_page = True
+
+    class FailingTestPage(page_module.Page):
+
+      def RunNavigateSteps(self, action_runner):
+        raise exceptions.AppCrashException
+    story_set = story.StorySet()
+    story_set.AddStory(page_module.Page('file://blank.html', story_set))
+    failing_page = FailingTestPage('chrome://version', story_set)
+    story_set.AddStory(failing_page)
+
+    results = results_options.CreateResults(
+        EmptyMetadataForTest(), self.options)
+    story_runner.Run(DummyTest(), story_set, self.options, results,
+                     max_failures=2)
+    self.assertEquals(1, len(results.failures))
+    self.assertEquals(1, len(results.pages_to_profiling_files))
+    self.assertIn(failing_page,
+                  results.pages_to_profiling_files)
+    screenshot_file_path = (
+        results.pages_to_profiling_files[failing_page][0].GetAbsPath())
+    try:
+      actual_screenshot_img = image_util.FromPngFile(screenshot_file_path)
+      self.assertTrue(image_util.AreEqual(
+                      image_util.FromBase64Png(expected_png_base64),
+                      actual_screenshot_img))
+    finally:  # Must clean up screenshot file if exists.
+      os.remove(screenshot_file_path)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/page/page_test_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/page/page_test_unittest.py
new file mode 100644
index 0000000..f4cc3bf
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/page/page_test_unittest.py
@@ -0,0 +1,196 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import json
+import os
+import unittest
+
+from telemetry import decorators
+from telemetry import story
+from telemetry.page import page as page_module
+from telemetry.page import legacy_page_test
+from telemetry.testing import options_for_unittests
+from telemetry.testing import page_test_test_case
+from telemetry.util import wpr_modes
+from telemetry.wpr import archive_info
+
+
+class PageTestThatFails(legacy_page_test.LegacyPageTest):
+
+  def ValidateAndMeasurePage(self, page, tab, results):
+    raise legacy_page_test.Failure
+
+
+class PageTestForBlank(legacy_page_test.LegacyPageTest):
+
+  def ValidateAndMeasurePage(self, page, tab, results):
+    contents = tab.EvaluateJavaScript('document.body.textContent')
+    if contents.strip() != 'Hello world':
+      raise legacy_page_test.MeasurementFailure(
+          'Page contents were: ' + contents)
+
+
+class PageTestForReplay(legacy_page_test.LegacyPageTest):
+
+  def ValidateAndMeasurePage(self, page, tab, results):
+    # Web Page Replay returns '404 Not found' if a page is not in the archive.
+    contents = tab.EvaluateJavaScript('document.body.textContent')
+    if '404 Not Found' in contents.strip():
+      raise legacy_page_test.MeasurementFailure('Page not in archive.')
+
+
+class PageTestQueryParams(legacy_page_test.LegacyPageTest):
+
+  def ValidateAndMeasurePage(self, page, tab, results):
+    query = tab.EvaluateJavaScript('window.location.search')
+    expected = '?foo=1'
+    if query.strip() != expected:
+      raise legacy_page_test.MeasurementFailure(
+          'query was %s, not %s.' % (query, expected))
+
+
+class PageTestWithAction(legacy_page_test.LegacyPageTest):
+
+  def __init__(self):
+    super(PageTestWithAction, self).__init__()
+
+  def ValidateAndMeasurePage(self, page, tab, results):
+    pass
+
+
+class PageWithAction(page_module.Page):
+
+  def __init__(self, url, story_set):
+    super(PageWithAction, self).__init__(url, story_set, story_set.base_dir)
+    self.run_test_action_called = False
+
+  def RunPageInteractions(self, _):
+    self.run_test_action_called = True
+
+
+class PageTestUnitTest(page_test_test_case.PageTestTestCase):
+
+  def setUp(self):
+    self._options = options_for_unittests.GetCopy()
+    self._options.browser_options.wpr_mode = wpr_modes.WPR_OFF
+
+  def testGotToBlank(self):
+    story_set = self.CreateStorySetFromFileInUnittestDataDir('blank.html')
+    measurement = PageTestForBlank()
+    all_results = self.RunMeasurement(
+        measurement, story_set, options=self._options)
+    self.assertEquals(0, len(all_results.failures))
+
+  def testGotQueryParams(self):
+    story_set = self.CreateStorySetFromFileInUnittestDataDir(
+        'blank.html?foo=1')
+    measurement = PageTestQueryParams()
+    all_results = self.RunMeasurement(
+        measurement, story_set, options=self._options)
+    self.assertEquals(0, len(all_results.failures))
+
+  def testFailure(self):
+    story_set = self.CreateStorySetFromFileInUnittestDataDir('blank.html')
+    measurement = PageTestThatFails()
+    all_results = self.RunMeasurement(
+        measurement, story_set, options=self._options)
+    self.assertEquals(1, len(all_results.failures))
+
+  # This test is disabled because it runs against live sites, and needs to be
+  # fixed. crbug.com/179038
+  @decorators.Disabled('all')
+  def testRecordAndReplay(self):
+    test_archive = '/tmp/google.wpr'
+    google_url = 'http://www.google.com/'
+    foo_url = 'http://www.foo.com/'
+    archive_info_template = ("""
+{
+"archives": {
+  "%s": ["%s"]
+}
+}
+""")
+    try:
+      story_set = story.StorySet.PageSet()
+      measurement = PageTestForReplay()
+
+      # First record an archive with only www.google.com.
+      self._options.browser_options.wpr_mode = wpr_modes.WPR_RECORD
+
+      story_set._wpr_archive_info = archive_info.WprArchiveInfo(
+          '', json.loads(archive_info_template % (test_archive, google_url)),
+          story_set.bucket)
+      story_set.pages = [page_module.Page(google_url, story_set)]
+      all_results = self.RunMeasurement(
+          measurement, story_set, options=self._options)
+      self.assertEquals(0, len(all_results.failures))
+
+      # Now replay it and verify that google.com is found but foo.com is not.
+      self._options.browser_options.wpr_mode = wpr_modes.WPR_REPLAY
+
+      story_set._wpr_archive_info = archive_info.WprArchiveInfo(
+          '', json.loads(archive_info_template % (test_archive, foo_url)),
+          story_set.bucket)
+      story_set.pages = [page_module.Page(foo_url, story_set)]
+      all_results = self.RunMeasurement(
+          measurement, story_set, options=self._options)
+      self.assertEquals(1, len(all_results.failures))
+
+      story_set._wpr_archive_info = archive_info.WprArchiveInfo(
+          '', json.loads(archive_info_template % (test_archive, google_url)),
+          story_set.bucket)
+      story_set.pages = [page_module.Page(google_url, story_set)]
+      all_results = self.RunMeasurement(
+          measurement, story_set, options=self._options)
+      self.assertEquals(0, len(all_results.failures))
+
+      self.assertTrue(os.path.isfile(test_archive))
+
+    finally:
+      if os.path.isfile(test_archive):
+        os.remove(test_archive)
+
+  def testRunActions(self):
+    story_set = self.CreateEmptyPageSet()
+    page = PageWithAction('file://blank.html', story_set)
+    story_set.AddStory(page)
+    measurement = PageTestWithAction()
+    self.RunMeasurement(measurement, story_set, options=self._options)
+    self.assertTrue(page.run_test_action_called)
+
+
+class MultiTabPageTestUnitTest(unittest.TestCase):
+
+  def testNoTabForPageReturnsFalse(self):
+    class PageTestWithoutTabForPage(legacy_page_test.LegacyPageTest):
+
+      def ValidateAndMeasurePage(self, *_):
+        pass
+    test = PageTestWithoutTabForPage()
+    self.assertFalse(test.is_multi_tab_test)
+
+  def testHasTabForPageReturnsTrue(self):
+    class PageTestWithTabForPage(legacy_page_test.LegacyPageTest):
+
+      def ValidateAndMeasurePage(self, *_):
+        pass
+
+      def TabForPage(self, *_):
+        pass
+    test = PageTestWithTabForPage()
+    self.assertTrue(test.is_multi_tab_test)
+
+  def testHasTabForPageInAncestor(self):
+    class PageTestWithTabForPage(legacy_page_test.LegacyPageTest):
+
+      def ValidateAndMeasurePage(self, *_):
+        pass
+
+      def TabForPage(self, *_):
+        pass
+
+    class PageTestWithTabForPageInParent(PageTestWithTabForPage):
+      pass
+    test = PageTestWithTabForPageInParent()
+    self.assertTrue(test.is_multi_tab_test)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/page/page_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/page/page_unittest.py
new file mode 100644
index 0000000..7620465
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/page/page_unittest.py
@@ -0,0 +1,216 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import unittest
+
+from telemetry import story
+from telemetry.page import page
+
+import mock
+
+
+class TestPage(unittest.TestCase):
+
+  def assertPathEqual(self, path1, path2):
+    self.assertEqual(os.path.normpath(path1), os.path.normpath(path2))
+
+  def testFilePathRelative(self):
+    apage = page.Page('file://somedir/otherdir/file.html',
+                      None, base_dir='basedir')
+    self.assertPathEqual(apage.file_path, 'basedir/somedir/otherdir/file.html')
+
+  def testFilePathAbsolute(self):
+    apage = page.Page('file:///somedir/otherdir/file.html',
+                      None, base_dir='basedir')
+    self.assertPathEqual(apage.file_path, '/somedir/otherdir/file.html')
+
+  def testFilePathQueryString(self):
+    apage = page.Page('file://somedir/otherdir/file.html?key=val',
+                      None, base_dir='basedir')
+    self.assertPathEqual(apage.file_path, 'basedir/somedir/otherdir/file.html')
+
+  def testFilePathUrlQueryString(self):
+    apage = page.Page('file://somedir/file.html?key=val',
+                      None, base_dir='basedir')
+    self.assertPathEqual(apage.file_path_url,
+                         'basedir/somedir/file.html?key=val')
+
+  def testFilePathUrlTrailingSeparator(self):
+    apage = page.Page('file://somedir/otherdir/',
+                      None, base_dir='basedir')
+    self.assertPathEqual(apage.file_path_url, 'basedir/somedir/otherdir/')
+    self.assertTrue(apage.file_path_url.endswith(os.sep) or
+                    (os.altsep and apage.file_path_url.endswith(os.altsep)))
+
+  def testSort(self):
+    story_set = story.StorySet(base_dir=os.path.dirname(__file__))
+    story_set.AddStory(
+        page.Page('http://www.foo.com/', story_set, story_set.base_dir))
+    story_set.AddStory(
+        page.Page('http://www.bar.com/', story_set, story_set.base_dir))
+
+    pages = sorted([story_set.stories[0], story_set.stories[1]])
+    self.assertEquals([story_set.stories[1], story_set.stories[0]],
+                      pages)
+
+  def testGetUrlBaseDirAndFileForUrlBaseDir(self):
+    base_dir = os.path.dirname(__file__)
+    file_path = os.path.join(
+        os.path.dirname(base_dir), 'otherdir', 'file.html')
+    story_set = story.StorySet(base_dir=base_dir,
+                               serving_dirs=[os.path.join('..', 'somedir', '')])
+    story_set.AddStory(
+        page.Page('file://../otherdir/file.html', story_set,
+                  story_set.base_dir))
+    self.assertPathEqual(story_set[0].file_path, file_path)
+
+  def testDisplayUrlForHttp(self):
+    story_set = story.StorySet(base_dir=os.path.dirname(__file__))
+    story_set.AddStory(
+        page.Page('http://www.foo.com/', story_set, story_set.base_dir))
+    story_set.AddStory(
+        page.Page('http://www.bar.com/', story_set, story_set.base_dir))
+
+    self.assertEquals(story_set[0].display_name, 'http://www.foo.com/')
+    self.assertEquals(story_set[1].display_name, 'http://www.bar.com/')
+
+  def testDisplayUrlForHttps(self):
+    story_set = story.StorySet(base_dir=os.path.dirname(__file__))
+    story_set.AddStory(
+        page.Page('http://www.foo.com/', story_set, story_set.base_dir))
+    story_set.AddStory(
+        page.Page('https://www.bar.com/', story_set, story_set.base_dir))
+
+    self.assertEquals(story_set[0].display_name, 'http://www.foo.com/')
+    self.assertEquals(story_set[1].display_name, 'https://www.bar.com/')
+
+  def testDisplayUrlForFile(self):
+    story_set = story.StorySet(base_dir=os.path.dirname(__file__))
+    story_set.AddStory(page.Page(
+        'file://../../otherdir/foo.html', story_set, story_set.base_dir))
+    story_set.AddStory(page.Page(
+        'file://../../otherdir/bar.html', story_set, story_set.base_dir))
+
+    self.assertEquals(story_set[0].display_name, 'foo.html')
+    self.assertEquals(story_set[1].display_name, 'bar.html')
+
+  def testDisplayUrlForFilesDifferingBySuffix(self):
+    story_set = story.StorySet(base_dir=os.path.dirname(__file__))
+    story_set.AddStory(page.Page(
+        'file://../../otherdir/foo.html', story_set, story_set.base_dir))
+    story_set.AddStory(page.Page(
+        'file://../../otherdir/foo1.html', story_set, story_set.base_dir))
+
+    self.assertEquals(story_set[0].display_name, 'foo.html')
+    self.assertEquals(story_set[1].display_name, 'foo1.html')
+
+  def testDisplayUrlForFileOfDifferentPaths(self):
+    story_set = story.StorySet(base_dir=os.path.dirname(__file__))
+    story_set.AddStory(
+        page.Page(
+            'file://../../somedir/foo.html', story_set, story_set.base_dir))
+    story_set.AddStory(page.Page(
+        'file://../../otherdir/bar.html', story_set, story_set.base_dir))
+
+    self.assertEquals(story_set[0].display_name, 'somedir/foo.html')
+    self.assertEquals(story_set[1].display_name, 'otherdir/bar.html')
+
+  def testDisplayUrlForFileDirectories(self):
+    story_set = story.StorySet(base_dir=os.path.dirname(__file__))
+    story_set.AddStory(
+        page.Page('file://../../otherdir/foo', story_set, story_set.base_dir))
+    story_set.AddStory(
+        page.Page('file://../../otherdir/bar', story_set, story_set.base_dir))
+
+    self.assertEquals(story_set[0].display_name, 'foo')
+    self.assertEquals(story_set[1].display_name, 'bar')
+
+  def testDisplayUrlForSingleFile(self):
+    story_set = story.StorySet(base_dir=os.path.dirname(__file__))
+    story_set.AddStory(page.Page(
+        'file://../../otherdir/foo.html', story_set, story_set.base_dir))
+
+    self.assertEquals(story_set[0].display_name, 'foo.html')
+
+  def testDisplayUrlForSingleDirectory(self):
+    story_set = story.StorySet(base_dir=os.path.dirname(__file__))
+    story_set.AddStory(
+        page.Page('file://../../otherdir/foo', story_set, story_set.base_dir))
+
+    self.assertEquals(story_set[0].display_name, 'foo')
+
+  def testPagesHaveDifferentIds(self):
+    p0 = page.Page("http://example.com")
+    p1 = page.Page("http://example.com")
+    self.assertNotEqual(p0.id, p1.id)
+
+  def testNamelessPageAsDict(self):
+    nameless_dict = page.Page('http://example.com/').AsDict()
+    self.assertIn('id', nameless_dict)
+    del nameless_dict['id']
+    self.assertEquals({
+                      'url': 'http://example.com/',
+                      }, nameless_dict)
+
+  def testNamedPageAsDict(self):
+    named_dict = page.Page('http://example.com/', name='Example').AsDict()
+    self.assertIn('id', named_dict)
+    del named_dict['id']
+    self.assertEquals({
+                      'url': 'http://example.com/',
+                      'name': 'Example'
+                      }, named_dict)
+
+  def testIsLocal(self):
+    p = page.Page('file://foo.html')
+    self.assertTrue(p.is_local)
+
+    p = page.Page('chrome://extensions')
+    self.assertTrue(p.is_local)
+
+    p = page.Page('about:blank')
+    self.assertTrue(p.is_local)
+
+    p = page.Page('http://foo.com')
+    self.assertFalse(p.is_local)
+
+
+class TestPageRun(unittest.TestCase):
+
+  def testFiveGarbageCollectionCallsByDefault(self):
+    mock_shared_state = mock.Mock()
+    p = page.Page('file://foo.html')
+    p.Run(mock_shared_state)
+    expected = [mock.call.current_tab.CollectGarbage(),
+                mock.call.current_tab.CollectGarbage(),
+                mock.call.current_tab.CollectGarbage(),
+                mock.call.current_tab.CollectGarbage(),
+                mock.call.current_tab.CollectGarbage(),
+                mock.call.page_test.WillNavigateToPage(
+                p, mock_shared_state.current_tab),
+                mock.call.page_test.RunNavigateSteps(
+                p, mock_shared_state.current_tab),
+                mock.call.page_test.DidNavigateToPage(
+                p, mock_shared_state.current_tab)]
+    self.assertEquals(mock_shared_state.mock_calls, expected)
+
+  def testNoGarbageCollectionCalls(self):
+    mock_shared_state = mock.Mock()
+
+    class NonGarbageCollectPage(page.Page):
+
+      def __init__(self, url):
+        super(NonGarbageCollectPage, self).__init__(url)
+        self._collect_garbage_before_run = False
+
+    p = NonGarbageCollectPage('file://foo.html')
+    p.Run(mock_shared_state)
+    expected = [mock.call.page_test.WillNavigateToPage(
+                p, mock_shared_state.current_tab),
+                mock.call.page_test.RunNavigateSteps(
+                p, mock_shared_state.current_tab),
+                mock.call.page_test.DidNavigateToPage(
+                p, mock_shared_state.current_tab)]
+    self.assertEquals(mock_shared_state.mock_calls, expected)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/page/shared_page_state.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/page/shared_page_state.py
new file mode 100644
index 0000000..a42d7e2
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/page/shared_page_state.py
@@ -0,0 +1,350 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+import os
+import sys
+
+from telemetry.core import exceptions
+from telemetry.core import util
+from telemetry import decorators
+from telemetry.internal.browser import browser_finder
+from telemetry.internal.browser import browser_finder_exceptions
+from telemetry.internal.browser import browser_info as browser_info_module
+from telemetry.internal.platform.profiler import profiler_finder
+from telemetry.internal.util import exception_formatter
+from telemetry.internal.util import file_handle
+from telemetry.page import cache_temperature
+from telemetry.page import traffic_setting
+from telemetry.page import legacy_page_test
+from telemetry import story
+from telemetry.util import screenshot
+from telemetry.util import wpr_modes
+from telemetry.web_perf import timeline_based_measurement
+
+
+def _PrepareFinderOptions(finder_options, test, device_type):
+  browser_options = finder_options.browser_options
+  # Set up user agent.
+  browser_options.browser_user_agent_type = device_type
+
+  test.CustomizeBrowserOptions(finder_options.browser_options)
+  if finder_options.profiler:
+    profiler_class = profiler_finder.FindProfiler(finder_options.profiler)
+    profiler_class.CustomizeBrowserOptions(browser_options.browser_type,
+                                           finder_options)
+
+
+class SharedPageState(story.SharedState):
+  """
+  This class contains all specific logic necessary to run a Chrome browser
+  benchmark.
+  """
+
+  _device_type = None
+
+  def __init__(self, test, finder_options, story_set):
+    super(SharedPageState, self).__init__(test, finder_options, story_set)
+    if isinstance(test, timeline_based_measurement.TimelineBasedMeasurement):
+      if finder_options.profiler:
+        assert not 'trace' in finder_options.profiler, (
+            'This is a Timeline Based Measurement benchmark. You cannot run it '
+            'with trace profiler enabled. If you need trace data, tracing is '
+            'always enabled in Timeline Based Measurement benchmarks and you '
+            'can get the trace data by adding --output-format=json.')
+      # This is to avoid the cyclic-import caused by timeline_based_page_test.
+      from telemetry.web_perf import timeline_based_page_test
+      self._test = timeline_based_page_test.TimelineBasedPageTest(test)
+    else:
+      self._test = test
+    _PrepareFinderOptions(finder_options, self._test, self._device_type)
+    self._browser = None
+    self._finder_options = finder_options
+    self._possible_browser = self._GetPossibleBrowser(
+        self._test, finder_options)
+
+    self._first_browser = True
+    self._did_login_for_current_page = False
+    self._previous_page = None
+    self._current_page = None
+    self._current_tab = None
+
+    self._test.SetOptions(self._finder_options)
+
+    # TODO(crbug/404771): Move network controller options out of
+    # browser_options and into finder_options.
+    browser_options = self._finder_options.browser_options
+    if self._finder_options.use_live_sites:
+      wpr_mode = wpr_modes.WPR_OFF
+    elif browser_options.wpr_mode == wpr_modes.WPR_RECORD:
+      wpr_mode = wpr_modes.WPR_RECORD
+    else:
+      wpr_mode = wpr_modes.WPR_REPLAY
+
+    use_live_traffic = wpr_mode == wpr_modes.WPR_OFF
+
+    if self.platform.network_controller.is_open:
+      self.platform.network_controller.Close()
+    self.platform.network_controller.InitializeIfNeeded(
+        use_live_traffic=use_live_traffic)
+    self.platform.network_controller.Open(wpr_mode,
+                                          browser_options.extra_wpr_args)
+    self.platform.Initialize()
+
+  @property
+  def possible_browser(self):
+    return self._possible_browser
+
+  @property
+  def browser(self):
+    return self._browser
+
+  def _FindBrowser(self, finder_options):
+    possible_browser = browser_finder.FindBrowser(finder_options)
+    if not possible_browser:
+      raise browser_finder_exceptions.BrowserFinderException(
+          'No browser found.\n\nAvailable browsers:\n%s\n' %
+          '\n'.join(browser_finder.GetAllAvailableBrowserTypes(finder_options)))
+    return possible_browser
+
+  def _GetPossibleBrowser(self, test, finder_options):
+    """Return a possible_browser with the given options for |test|. """
+    possible_browser = self._FindBrowser(finder_options)
+    finder_options.browser_options.browser_type = (
+        possible_browser.browser_type)
+
+    enabled, msg = decorators.IsEnabled(test, possible_browser)
+    if not enabled and not finder_options.run_disabled_tests:
+      logging.warning(msg)
+      logging.warning('You are trying to run a disabled test.')
+
+    if possible_browser.IsRemote():
+      possible_browser.RunRemote()
+      sys.exit(0)
+    return possible_browser
+
+  def DumpStateUponFailure(self, page, results):
+    # Dump browser standard output and log.
+    if self._browser:
+      self._browser.DumpStateUponFailure()
+    else:
+      logging.warning('Cannot dump browser state: No browser.')
+
+    # Capture a screenshot
+    if self._finder_options.browser_options.take_screenshot_for_failed_page:
+      fh = screenshot.TryCaptureScreenShot(self.platform, self._current_tab)
+      if fh is not None:
+        results.AddProfilingFile(page, fh)
+    else:
+      logging.warning('Taking screenshots upon failures disabled.')
+
+  def DidRunStory(self, results):
+    if self._finder_options.profiler:
+      self._StopProfiling(results)
+    # We might hang while trying to close the connection, and need to guarantee
+    # the page will get cleaned up to avoid future tests failing in weird ways.
+    try:
+      if self._current_tab and self._current_tab.IsAlive():
+        self._current_tab.CloseConnections()
+      self._previous_page = self._current_page
+    except Exception:
+      if self._current_tab:
+        self._current_tab.Close()
+    finally:
+      if self._current_page.credentials and self._did_login_for_current_page:
+        self.browser.credentials.LoginNoLongerNeeded(
+            self._current_tab, self._current_page.credentials)
+      if self._test.StopBrowserAfterPage(self.browser, self._current_page):
+        self._StopBrowser()
+      self._current_page = None
+      self._current_tab = None
+
+  @property
+  def platform(self):
+    return self._possible_browser.platform
+
+  def _StartBrowser(self, page):
+    assert self._browser is None
+    self._possible_browser.SetCredentialsPath(page.credentials_path)
+
+    self._test.WillStartBrowser(self.platform)
+    if page.startup_url:
+      self._finder_options.browser_options.startup_url = page.startup_url
+    self._browser = self._possible_browser.Create(self._finder_options)
+    self._test.DidStartBrowser(self.browser)
+
+    if self._first_browser:
+      self._first_browser = False
+      self.browser.credentials.WarnIfMissingCredentials(page)
+
+  def WillRunStory(self, page):
+    if not self.platform.tracing_controller.is_tracing_running:
+      # For TimelineBasedMeasurement benchmarks, tracing has already started.
+      # For PageTest benchmarks, tracing has not yet started. We need to make
+      # sure no tracing state is left before starting the browser for PageTest
+      # benchmarks.
+      self.platform.tracing_controller.ClearStateIfNeeded()
+
+    page_set = page.page_set
+    self._current_page = page
+    if self._browser and (self._test.RestartBrowserBeforeEachPage()
+                          or page.startup_url):
+      assert not self.platform.tracing_controller.is_tracing_running, (
+          'Should not restart browser when tracing is already running. For '
+          'TimelineBasedMeasurement (TBM) benchmarks, you should not use '
+          'startup_url. Use benchmark.ShouldTearDownStateAfterEachStoryRun '
+          'instead.')
+      self._StopBrowser()
+    started_browser = not self.browser
+
+    archive_path = page_set.WprFilePathForStory(page, self.platform.GetOSName())
+    # TODO(nednguyen, perezju): Ideally we should just let the network
+    # controller raise an exception when the archive_path is not found.
+    if archive_path is not None and not os.path.isfile(archive_path):
+      logging.warning('WPR archive missing: %s', archive_path)
+      archive_path = None
+    self.platform.network_controller.StartReplay(
+        archive_path, page.make_javascript_deterministic)
+
+    if self.browser:
+      # Set new credential path for browser.
+      self.browser.credentials.credentials_path = page.credentials_path
+    else:
+      self._StartBrowser(page)
+    if self.browser.supports_tab_control and self._test.close_tabs_before_run:
+      # Create a tab if there's none.
+      if len(self.browser.tabs) == 0:
+        self.browser.tabs.New()
+
+      # Ensure only one tab is open, unless the test is a multi-tab test.
+      if not self._test.is_multi_tab_test:
+        while len(self.browser.tabs) > 1:
+          self.browser.tabs[-1].Close()
+
+      # Must wait for tab to commit otherwise it can commit after the next
+      # navigation has begun and RenderFrameHostManager::DidNavigateMainFrame()
+      # will cancel the next navigation because it's pending. This manifests as
+      # the first navigation in a PageSet freezing indefinitely because the
+      # navigation was silently canceled when |self.browser.tabs[0]| was
+      # committed. Only do this when we just started the browser, otherwise
+      # there are cases where previous pages in a PageSet never complete
+      # loading so we'll wait forever.
+      if started_browser:
+        self.browser.tabs[0].WaitForDocumentReadyStateToBeComplete()
+
+    # Reset traffic shaping to speed up cache temperature setup.
+    self.platform.network_controller.UpdateTrafficSettings(0, 0, 0)
+    cache_temperature.EnsurePageCacheTemperature(
+        self._current_page, self.browser, self._previous_page)
+    if self._current_page.traffic_setting != traffic_setting.NONE:
+      s = traffic_setting.NETWORK_CONFIGS[self._current_page.traffic_setting]
+      self.platform.network_controller.UpdateTrafficSettings(
+          round_trip_latency_ms=s.round_trip_latency_ms,
+          download_bandwidth_kbps=s.download_bandwidth_kbps,
+          upload_bandwidth_kbps=s.upload_bandwidth_kbps)
+
+    # Start profiling if needed.
+    if self._finder_options.profiler:
+      self._StartProfiling(self._current_page)
+
+  def CanRunStory(self, page):
+    return self.CanRunOnBrowser(browser_info_module.BrowserInfo(self.browser),
+                                page)
+
+  def CanRunOnBrowser(self, browser_info,
+                      page):  # pylint: disable=unused-argument
+    """Override this to return whether the browser brought up by this state
+    instance is suitable for running the given page.
+
+    Args:
+      browser_info: an instance of telemetry.core.browser_info.BrowserInfo
+      page: an instance of telemetry.page.Page
+    """
+    del browser_info, page  # unused
+    return True
+
+  def _PreparePage(self):
+    self._current_tab = self._test.TabForPage(self._current_page, self.browser)
+    if self._current_page.is_file:
+      self.platform.SetHTTPServerDirectories(
+          self._current_page.page_set.serving_dirs |
+          set([self._current_page.serving_dir]))
+
+    if self._current_page.credentials:
+      if not self.browser.credentials.LoginNeeded(
+          self._current_tab, self._current_page.credentials):
+        raise legacy_page_test.Failure(
+            'Login as ' + self._current_page.credentials + ' failed')
+      self._did_login_for_current_page = True
+
+    if self._test.clear_cache_before_each_run:
+      self._current_tab.ClearCache(force=True)
+
+  @property
+  def current_page(self):
+    return self._current_page
+
+  @property
+  def current_tab(self):
+    return self._current_tab
+
+  @property
+  def page_test(self):
+    return self._test
+
+  def RunStory(self, results):
+    try:
+      self._PreparePage()
+      self._current_page.Run(self)
+      self._test.ValidateAndMeasurePage(
+          self._current_page, self._current_tab, results)
+    except exceptions.Error:
+      if self._test.is_multi_tab_test:
+        # Avoid trying to recover from an unknown multi-tab state.
+        exception_formatter.PrintFormattedException(
+            msg='Telemetry Error during multi tab test:')
+        raise legacy_page_test.MultiTabTestAppCrashError
+      raise
+
+  def TearDownState(self):
+    self._StopBrowser()
+    self.platform.StopAllLocalServers()
+    self.platform.network_controller.Close()
+
+  def _StopBrowser(self):
+    if self._browser:
+      self._browser.Close()
+      self._browser = None
+
+  def _StartProfiling(self, page):
+    output_file = os.path.join(self._finder_options.output_dir,
+                               page.file_safe_name)
+    if self._finder_options.pageset_repeat != 1:
+      output_file = util.GetSequentialFileName(output_file)
+    self.browser.profiling_controller.Start(
+        self._finder_options.profiler, output_file)
+
+  def _StopProfiling(self, results):
+    if self.browser:
+      profiler_files = self.browser.profiling_controller.Stop()
+      for f in profiler_files:
+        if os.path.isfile(f):
+          results.AddProfilingFile(self._current_page,
+                                   file_handle.FromFilePath(f))
+
+
+class SharedMobilePageState(SharedPageState):
+  _device_type = 'mobile'
+
+
+class SharedDesktopPageState(SharedPageState):
+  _device_type = 'desktop'
+
+
+class SharedTabletPageState(SharedPageState):
+  _device_type = 'tablet'
+
+
+class Shared10InchTabletPageState(SharedPageState):
+  _device_type = 'tablet_10_inch'
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/page/shared_page_state_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/page/shared_page_state_unittest.py
new file mode 100644
index 0000000..7909f8f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/page/shared_page_state_unittest.py
@@ -0,0 +1,115 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from telemetry.internal import story_runner
+from telemetry.page import page
+from telemetry.page import legacy_page_test
+from telemetry.page import shared_page_state
+from telemetry import story as story_module
+from telemetry.testing import fakes
+from telemetry.util import wpr_modes
+
+
+def SetUpPageRunnerArguments(options):
+  parser = options.CreateParser()
+  story_runner.AddCommandLineArgs(parser)
+  options.MergeDefaultValues(parser.get_default_values())
+  story_runner.ProcessCommandLineArgs(parser, options)
+
+
+class DummyTest(legacy_page_test.LegacyPageTest):
+
+  def ValidateAndMeasurePage(self, *_):
+    pass
+
+
+class SharedPageStateTests(unittest.TestCase):
+
+  def setUp(self):
+    self.options = fakes.CreateBrowserFinderOptions()
+    self.options.use_live_sites = False
+    self.options.output_formats = ['none']
+    self.options.suppress_gtest_report = True
+
+  def testUseLiveSitesFlagSet(self):
+    self.options.use_live_sites = True
+    run_state = shared_page_state.SharedPageState(
+        DummyTest(), self.options, story_module.StorySet())
+    self.assertTrue(run_state.platform.network_controller.is_open)
+    self.assertEquals(run_state.platform.network_controller.wpr_mode,
+                      wpr_modes.WPR_OFF)
+    self.assertTrue(run_state.platform.network_controller.use_live_traffic)
+
+  def testUseLiveSitesFlagUnset(self):
+    run_state = shared_page_state.SharedPageState(
+        DummyTest(), self.options, story_module.StorySet())
+    self.assertTrue(run_state.platform.network_controller.is_open)
+    self.assertEquals(run_state.platform.network_controller.wpr_mode,
+                      wpr_modes.WPR_REPLAY)
+    self.assertFalse(run_state.platform.network_controller.use_live_traffic)
+
+  def testWPRRecordEnable(self):
+    self.options.browser_options.wpr_mode = wpr_modes.WPR_RECORD
+    run_state = shared_page_state.SharedPageState(
+        DummyTest(), self.options, story_module.StorySet())
+    self.assertTrue(run_state.platform.network_controller.is_open)
+    self.assertEquals(run_state.platform.network_controller.wpr_mode,
+                      wpr_modes.WPR_RECORD)
+    self.assertFalse(run_state.platform.network_controller.use_live_traffic)
+
+  def testConstructorCallsSetOptions(self):
+    test = DummyTest()
+    shared_page_state.SharedPageState(
+        test, self.options, story_module.StorySet())
+    self.assertEqual(test.options, self.options)
+
+  def assertUserAgentSetCorrectly(
+      self, shared_page_state_class, expected_user_agent):
+    story = page.Page(
+        'http://www.google.com',
+        shared_page_state_class=shared_page_state_class)
+    test = DummyTest()
+    story_set = story_module.StorySet()
+    story_set.AddStory(story)
+    story.shared_state_class(test, self.options, story_set)
+    browser_options = self.options.browser_options
+    actual_user_agent = browser_options.browser_user_agent_type
+    self.assertEqual(expected_user_agent, actual_user_agent)
+
+  def testPageStatesUserAgentType(self):
+    self.assertUserAgentSetCorrectly(
+        shared_page_state.SharedMobilePageState, 'mobile')
+    self.assertUserAgentSetCorrectly(
+        shared_page_state.SharedDesktopPageState, 'desktop')
+    self.assertUserAgentSetCorrectly(
+        shared_page_state.SharedTabletPageState, 'tablet')
+    self.assertUserAgentSetCorrectly(
+        shared_page_state.Shared10InchTabletPageState, 'tablet_10_inch')
+    self.assertUserAgentSetCorrectly(
+        shared_page_state.SharedPageState, None)
+
+  def testBrowserStartupURLSetCorrectly(self):
+    story_set = story_module.StorySet()
+    google_page = page.Page(
+        'http://www.google.com',
+        startup_url='http://www.google.com', page_set=story_set)
+    example_page = page.Page(
+        'https://www.example.com',
+        startup_url='https://www.example.com', page_set=story_set)
+    gmail_page = page.Page(
+        'https://www.gmail.com',
+        startup_url='https://www.gmail.com', page_set=story_set)
+
+    for p in (google_page, example_page, gmail_page):
+      story_set.AddStory(p)
+
+    shared_state = shared_page_state.SharedPageState(
+        DummyTest(), self.options, story_set)
+
+    for p in (google_page, example_page, gmail_page):
+      shared_state.WillRunStory(p)
+      self.assertEquals(
+          p.startup_url, self.options.browser_options.startup_url)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/page/traffic_setting.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/page/traffic_setting.py
new file mode 100644
index 0000000..caffa09
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/page/traffic_setting.py
@@ -0,0 +1,37 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import collections
+
+_Configs = collections.namedtuple(
+  '_Configs', ('download_bandwidth_kbps,'
+               'upload_bandwidth_kbps,'
+               'round_trip_latency_ms'))
+
+# These presets are copied from devtool's:
+# https://cs.chromium.org/chromium/src/third_party/WebKit/Source/devtools/front_end/components/NetworkConditionsSelector.js?l=43
+NONE = 'none'
+GPRS = 'GPRS'
+REGULAR_2G = 'Regular-2G'
+GOOD_2G = 'Good-2G'
+REGULAR_3G = 'Regular-3G'
+GOOD_3G = 'Good-3G'
+REGULAR_4G = 'Regular-4G'
+DSL = 'DSL'
+WIFI = 'WiFi'
+
+NETWORK_CONFIGS = {
+  NONE: _Configs(0, 0, 0),
+  GPRS: _Configs(50 * 1024 / 8, 20 * 1024 / 8, 500),
+  REGULAR_2G: _Configs(250 * 1024 / 8, 50 * 1024 / 8, 300),
+  GOOD_2G: _Configs(450 * 1024 / 8, 150 * 1024 / 8, 150),
+  REGULAR_3G: _Configs(750 * 1024 / 8, 250 * 1024 / 8, 100),
+  GOOD_3G: _Configs(1.5 * 1024 * 1024 / 8, 750 * 1024 / 8, 40),
+  REGULAR_4G: _Configs(4 * 1024 * 1024 / 8, 3 * 1024 * 1024 / 8, 20),
+  DSL: _Configs(2 * 1024 * 1024 / 8, 1 * 1024 * 1024 / 8, 5),
+  WIFI: _Configs(30 * 1024 * 1024 / 8, 15 * 1024 * 1024 / 8, 2),
+}
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/project_config.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/project_config.py
new file mode 100644
index 0000000..5763dce
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/project_config.py
@@ -0,0 +1,51 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+class ProjectConfig(object):
+  """Contains information about the benchmark runtime environment.
+
+  Attributes:
+    top_level_dir: A dir that contains benchmark, page test, and/or story
+        set dirs and associated artifacts.
+    benchmark_dirs: A list of dirs containing benchmarks.
+    benchmark_aliases: A dict of name:alias string pairs to be matched against
+        exactly during benchmark selection.
+    client_configs: A list of paths to a ProjectDependencies json files.
+    default_chrome_root: A path to chromium source directory. Many telemetry
+      features depend on chromium source tree's presence and those won't work
+      in case this is not specified.
+  """
+  def __init__(self, top_level_dir, benchmark_dirs=None,
+               benchmark_aliases=None, client_configs=None,
+               default_chrome_root=None):
+    self._top_level_dir = top_level_dir
+    self._benchmark_dirs = benchmark_dirs or []
+    self._benchmark_aliases = benchmark_aliases or dict()
+    self._client_configs = client_configs or []
+    self._default_chrome_root = default_chrome_root
+
+  @property
+  def top_level_dir(self):
+    return self._top_level_dir
+
+  @property
+  def start_dirs(self):
+    return self._benchmark_dirs
+
+  @property
+  def benchmark_dirs(self):
+    return self._benchmark_dirs
+
+  @property
+  def benchmark_aliases(self):
+    return self._benchmark_aliases
+
+  @property
+  def client_configs(self):
+    return self._client_configs
+
+  @property
+  def default_chrome_root(self):
+    return self._default_chrome_root
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/record_wpr.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/record_wpr.py
new file mode 100644
index 0000000..0cab637
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/record_wpr.py
@@ -0,0 +1,295 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import argparse
+import logging
+import sys
+
+from telemetry import benchmark
+from telemetry import story
+from telemetry.core import discover
+from telemetry.internal.browser import browser_options
+from telemetry.internal.results import results_options
+from telemetry.internal import story_runner
+from telemetry.internal.util import binary_manager
+from telemetry.page import legacy_page_test
+from telemetry.util import matching
+from telemetry.util import wpr_modes
+from telemetry.web_perf import timeline_based_measurement
+from telemetry.web_perf import timeline_based_page_test
+
+import py_utils
+
+DEFAULT_LOG_FORMAT = (
+  '(%(levelname)s) %(asctime)s %(module)s.%(funcName)s:%(lineno)d  '
+  '%(message)s')
+
+
+class RecorderPageTest(legacy_page_test.LegacyPageTest):
+  def __init__(self):
+    super(RecorderPageTest, self).__init__()
+    self.page_test = None
+    self.platform = None
+
+  def CustomizeBrowserOptions(self, options):
+    if self.page_test:
+      self.page_test.CustomizeBrowserOptions(options)
+
+  def WillStartBrowser(self, browser):
+    if self.platform is not None:
+      assert browser.GetOSName() == self.platform
+    self.platform = browser.GetOSName()
+    if self.page_test:
+      self.page_test.WillStartBrowser(browser)
+
+  def DidStartBrowser(self, browser):
+    if self.page_test:
+      self.page_test.DidStartBrowser(browser)
+
+  def WillNavigateToPage(self, page, tab):
+    """Override to ensure all resources are fetched from network."""
+    tab.ClearCache(force=False)
+    if self.page_test:
+      self.page_test.WillNavigateToPage(page, tab)
+
+  def DidNavigateToPage(self, page, tab):
+    if self.page_test:
+      self.page_test.DidNavigateToPage(page, tab)
+    tab.WaitForDocumentReadyStateToBeComplete()
+    py_utils.WaitFor(tab.HasReachedQuiescence, 30)
+
+  def CleanUpAfterPage(self, page, tab):
+    if self.page_test:
+      self.page_test.CleanUpAfterPage(page, tab)
+
+  def ValidateAndMeasurePage(self, page, tab, results):
+    if self.page_test:
+      self.page_test.ValidateAndMeasurePage(page, tab, results)
+
+  def RunNavigateSteps(self, page, tab):
+    if self.page_test:
+      self.page_test.RunNavigateSteps(page, tab)
+    else:
+      super(RecorderPageTest, self).RunNavigateSteps(page, tab)
+
+
+def _GetSubclasses(base_dir, cls):
+  """Returns all subclasses of |cls| in |base_dir|.
+
+  Args:
+    cls: a class
+
+  Returns:
+    dict of {underscored_class_name: benchmark class}
+  """
+  return discover.DiscoverClasses(base_dir, base_dir, cls,
+                                  index_by_class_name=True)
+
+
+def _MaybeGetInstanceOfClass(target, base_dir, cls):
+  if isinstance(target, cls):
+    return target
+  classes = _GetSubclasses(base_dir, cls)
+  return classes[target]() if target in classes else None
+
+
+def _PrintAllImpl(all_items, item_name, output_stream):
+  output_stream.write('Available %s\' names with descriptions:\n' % item_name)
+  keys = sorted(all_items.keys())
+  key_description = [(k, all_items[k].Description()) for k in keys]
+  _PrintPairs(key_description, output_stream)
+  output_stream.write('\n')
+
+
+def _PrintAllBenchmarks(base_dir, output_stream):
+  # TODO: reuse the logic of finding supported benchmarks in benchmark_runner.py
+  # so this only prints out benchmarks that are supported by the recording
+  # platform.
+  _PrintAllImpl(_GetSubclasses(base_dir, benchmark.Benchmark), 'benchmarks',
+                output_stream)
+
+
+def _PrintAllStories(base_dir, output_stream):
+  # TODO: actually print all stories once record_wpr support general
+  # stories recording.
+  _PrintAllImpl(_GetSubclasses(base_dir, story.StorySet), 'story sets',
+                output_stream)
+
+
+def _PrintPairs(pairs, output_stream, prefix=''):
+  """Prints a list of string pairs with alignment."""
+  first_column_length = max(len(a) for a, _ in pairs)
+  format_string = '%s%%-%ds  %%s\n' % (prefix, first_column_length)
+  for a, b in pairs:
+    output_stream.write(format_string % (a, b.strip()))
+
+
+class WprRecorder(object):
+
+  def __init__(self, base_dir, target, args=None):
+    self._base_dir = base_dir
+    self._record_page_test = RecorderPageTest()
+    self._options = self._CreateOptions()
+
+    self._benchmark = _MaybeGetInstanceOfClass(target, base_dir,
+                                               benchmark.Benchmark)
+    self._parser = self._options.CreateParser(usage='See %prog --help')
+    self._AddCommandLineArgs()
+    self._ParseArgs(args)
+    self._ProcessCommandLineArgs()
+    if self._benchmark is not None:
+      test = self._benchmark.CreatePageTest(self.options)
+      if isinstance(test, timeline_based_measurement.TimelineBasedMeasurement):
+        test = timeline_based_page_test.TimelineBasedPageTest(test)
+      # This must be called after the command line args are added.
+      self._record_page_test.page_test = test
+
+    self._page_set_base_dir = (
+        self._options.page_set_base_dir if self._options.page_set_base_dir
+        else self._base_dir)
+    self._story_set = self._GetStorySet(target)
+
+  @property
+  def options(self):
+    return self._options
+
+  def _CreateOptions(self):
+    options = browser_options.BrowserFinderOptions()
+    options.browser_options.wpr_mode = wpr_modes.WPR_RECORD
+    return options
+
+  def CreateResults(self):
+    if self._benchmark is not None:
+      benchmark_metadata = self._benchmark.GetMetadata()
+    else:
+      benchmark_metadata = benchmark.BenchmarkMetadata('record_wpr')
+
+    return results_options.CreateResults(benchmark_metadata, self._options)
+
+  def _AddCommandLineArgs(self):
+    self._parser.add_option('--page-set-base-dir', action='store',
+                            type='string')
+    story_runner.AddCommandLineArgs(self._parser)
+    if self._benchmark is not None:
+      self._benchmark.AddCommandLineArgs(self._parser)
+      self._benchmark.SetArgumentDefaults(self._parser)
+    self._parser.add_option('--upload', action='store_true')
+    self._SetArgumentDefaults()
+
+  def _SetArgumentDefaults(self):
+    self._parser.set_defaults(**{'output_formats': ['none']})
+
+  def _ParseArgs(self, args=None):
+    args_to_parse = sys.argv[1:] if args is None else args
+    self._parser.parse_args(args_to_parse)
+
+  def _ProcessCommandLineArgs(self):
+    story_runner.ProcessCommandLineArgs(self._parser, self._options)
+
+    if self._options.use_live_sites:
+      self._parser.error("Can't --use-live-sites while recording")
+
+    if self._benchmark is not None:
+      self._benchmark.ProcessCommandLineArgs(self._parser, self._options)
+
+  def _GetStorySet(self, target):
+    if self._benchmark is not None:
+      return self._benchmark.CreateStorySet(self._options)
+    story_set = _MaybeGetInstanceOfClass(target, self._page_set_base_dir,
+                                         story.StorySet)
+    if story_set is None:
+      sys.stderr.write('Target %s is neither benchmark nor story set.\n'
+                       % target)
+      if not self._HintMostLikelyBenchmarksStories(target):
+        sys.stderr.write(
+            'Found no similar benchmark or story. Please use '
+            '--list-benchmarks or --list-stories to list candidates.\n')
+        self._parser.print_usage()
+      sys.exit(1)
+    return story_set
+
+  def _HintMostLikelyBenchmarksStories(self, target):
+    def _Impl(all_items, category_name):
+      candidates = matching.GetMostLikelyMatchedObject(
+          all_items.iteritems(), target, name_func=lambda kv: kv[1].Name())
+      if candidates:
+        sys.stderr.write('\nDo you mean any of those %s below?\n' %
+                         category_name)
+        _PrintPairs([(k, v.Description()) for k, v in candidates], sys.stderr)
+        return True
+      return False
+
+    has_benchmark_hint = _Impl(
+        _GetSubclasses(self._base_dir, benchmark.Benchmark), 'benchmarks')
+    has_story_hint = _Impl(
+        _GetSubclasses(self._base_dir, story.StorySet), 'stories')
+    return has_benchmark_hint or has_story_hint
+
+  def Record(self, results):
+    assert self._story_set.wpr_archive_info, (
+      'Pageset archive_data_file path must be specified.')
+    self._story_set.wpr_archive_info.AddNewTemporaryRecording()
+    self._record_page_test.CustomizeBrowserOptions(self._options)
+    story_runner.Run(self._record_page_test, self._story_set,
+        self._options, results)
+
+  def HandleResults(self, results, upload_to_cloud_storage):
+    if results.failures or results.skipped_values:
+      logging.warning('Some pages failed and/or were skipped. The recording '
+                      'has not been updated for these pages.')
+    results.PrintSummary()
+    self._story_set.wpr_archive_info.AddRecordedStories(
+        results.pages_that_succeeded,
+        upload_to_cloud_storage,
+        target_platform=self._record_page_test.platform)
+
+
+def Main(environment, **log_config_kwargs):
+  # the log level is set in browser_options
+  log_config_kwargs.pop('level', None)
+  log_config_kwargs.setdefault('format', DEFAULT_LOG_FORMAT)
+  logging.basicConfig(**log_config_kwargs)
+
+  parser = argparse.ArgumentParser(
+      usage='Record a benchmark or a story (page set).')
+  parser.add_argument(
+      'benchmark',
+      help=('benchmark name. This argument is optional. If both benchmark name '
+            'and story name are specified, this takes precedence as the '
+            'target of the recording.'),
+      nargs='?')
+  parser.add_argument('--story', help='story (page set) name')
+  parser.add_argument('--list-stories', dest='list_stories',
+                      action='store_true', help='list all story names.')
+  parser.add_argument('--list-benchmarks', dest='list_benchmarks',
+                      action='store_true', help='list all benchmark names.')
+  parser.add_argument('--upload', action='store_true',
+                      help='upload to cloud storage.')
+  args, extra_args = parser.parse_known_args()
+
+  if args.list_benchmarks or args.list_stories:
+    if args.list_benchmarks:
+      _PrintAllBenchmarks(environment.top_level_dir, sys.stderr)
+    if args.list_stories:
+      _PrintAllStories(environment.top_level_dir, sys.stderr)
+    return 0
+
+  target = args.benchmark or args.story
+
+  if not target:
+    sys.stderr.write('Please specify target (benchmark or story). Please refer '
+                     'usage below\n\n')
+    parser.print_help()
+    return 0
+
+  binary_manager.InitDependencyManager(environment.client_configs)
+
+  # TODO(nednguyen): update WprRecorder so that it handles the difference
+  # between recording a benchmark vs recording a story better based on
+  # the distinction between args.benchmark & args.story
+  wpr_recorder = WprRecorder(environment.top_level_dir, target, extra_args)
+  results = wpr_recorder.CreateResults()
+  wpr_recorder.Record(results)
+  wpr_recorder.HandleResults(results, args.upload)
+  return min(255, len(results.failures))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/record_wpr_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/record_wpr_unittest.py
new file mode 100644
index 0000000..926dc8f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/record_wpr_unittest.py
@@ -0,0 +1,238 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import sys
+
+from telemetry import benchmark
+from telemetry import story
+from telemetry.core import util
+from telemetry import decorators
+from telemetry.page import page as page_module
+from telemetry.page import legacy_page_test
+from telemetry import record_wpr
+from telemetry.testing import options_for_unittests
+from telemetry.testing import tab_test_case
+from telemetry.util import wpr_modes
+
+
+class MockPage(page_module.Page):
+  def __init__(self, story_set, url):
+    super(MockPage, self).__init__(url=url,
+                                   page_set=story_set,
+                                   base_dir=util.GetUnittestDataDir())
+    self.func_calls = []
+
+  def RunNavigateSteps(self, action_runner):
+    self.func_calls.append('RunNavigateSteps')
+    super(MockPage, self).RunNavigateSteps(action_runner)
+
+  def RunPageInteractions(self, _):
+    self.func_calls.append('RunPageInteractions')
+
+  def RunSmoothness(self, _):
+    self.func_calls.append('RunSmoothness')
+
+class MockStorySet(story.StorySet):
+  def __init__(self, url=''):
+    super(MockStorySet, self).__init__(
+        archive_data_file='data/archive_files/test.json')
+    self.AddStory(MockPage(self, url))
+
+
+class MockPageTest(legacy_page_test.LegacyPageTest):
+  def __init__(self):
+    super(MockPageTest, self).__init__()
+    self._action_name_to_run = "RunPageInteractions"
+    self.func_calls = []
+
+  def CustomizeBrowserOptions(self, options):
+    self.func_calls.append('CustomizeBrowserOptions')
+
+  def WillNavigateToPage(self, page, tab):
+    self.func_calls.append('WillNavigateToPage')
+
+  def DidNavigateToPage(self, page, tab):
+    self.func_calls.append('DidNavigateToPage')
+
+  def ValidateAndMeasurePage(self, page, tab, results):
+    self.func_calls.append('ValidateAndMeasurePage')
+
+  def WillStartBrowser(self, platform):
+    self.func_calls.append('WillStartBrowser')
+
+  def DidStartBrowser(self, browser):
+    self.func_calls.append('DidStartBrowser')
+
+class MockBenchmark(benchmark.Benchmark):
+  test = MockPageTest
+
+  def __init__(self):
+    super(MockBenchmark, self).__init__()
+    self.mock_story_set = None
+
+  @classmethod
+  def AddBenchmarkCommandLineArgs(cls, group):
+    group.add_option('', '--mock-benchmark-url', action='store', type='string')
+
+  def CreateStorySet(self, options):
+    kwargs = {}
+    if options.mock_benchmark_url:
+      kwargs['url'] = options.mock_benchmark_url
+    self.mock_story_set = MockStorySet(**kwargs)
+    return self.mock_story_set
+
+
+class MockTimelineBasedMeasurementBenchmark(benchmark.Benchmark):
+
+  def __init__(self):
+    super(MockTimelineBasedMeasurementBenchmark, self).__init__()
+    self.mock_story_set = None
+
+  @classmethod
+  def AddBenchmarkCommandLineArgs(cls, group):
+    group.add_option('', '--mock-benchmark-url', action='store', type='string')
+
+  def CreateStorySet(self, options):
+    kwargs = {}
+    if options.mock_benchmark_url:
+      kwargs['url'] = options.mock_benchmark_url
+    self.mock_story_set = MockStorySet(**kwargs)
+    return self.mock_story_set
+
+
+class RecordWprUnitTests(tab_test_case.TabTestCase):
+
+  _base_dir = util.GetUnittestDataDir()
+  _test_data_dir = os.path.join(util.GetUnittestDataDir(), 'page_tests')
+
+  @classmethod
+  def setUpClass(cls):
+    sys.path.extend([cls._base_dir, cls._test_data_dir])
+    super(RecordWprUnitTests, cls).setUpClass()
+    cls._url = cls.UrlOfUnittestFile('blank.html')
+    cls._test_options = options_for_unittests.GetCopy()
+
+  # When the RecorderPageTest is created from a PageSet, we do not have a
+  # PageTest to use. In this case, we will record every available action.
+  def testRunPage_AllActions(self):
+    record_page_test = record_wpr.RecorderPageTest()
+    page = MockPage(story_set=MockStorySet(url=self._url), url=self._url)
+
+    record_page_test.RunNavigateSteps(page, self._tab)
+    self.assertTrue('RunNavigateSteps' in page.func_calls)
+
+  # When the RecorderPageTest is created from a Benchmark, the benchmark will
+  # have a PageTest, specified by its test attribute.
+  def testRunPage_OnlyRunBenchmarkAction(self):
+    record_page_test = record_wpr.RecorderPageTest()
+    record_page_test.page_test = MockBenchmark().test()
+    page = MockPage(story_set=MockStorySet(url=self._url), url=self._url)
+    record_page_test.ValidateAndMeasurePage(page, self._tab, results=None)
+
+  def testRunPage_CallBenchmarksPageTestsFunctions(self):
+    record_page_test = record_wpr.RecorderPageTest()
+    record_page_test.page_test = MockBenchmark().test()
+    page = MockPage(story_set=MockStorySet(url=self._url), url=self._url)
+    record_page_test.ValidateAndMeasurePage(page, self._tab, results=None)
+    self.assertEqual(1, len(record_page_test.page_test.func_calls))
+    self.assertEqual('ValidateAndMeasurePage',
+                     record_page_test.page_test.func_calls[0])
+
+  def GetBrowserDeviceFlags(self):
+    flags = ['--browser', self._browser.browser_type,
+             '--remote', self._test_options.cros_remote,
+             '--device', self._device]
+    if self._test_options.chrome_root:
+      flags += ['--chrome-root', self._test_options.chrome_root]
+    return flags
+
+  @decorators.Disabled('chromeos') # crbug.com/404868.
+  def testWprRecorderWithPageSet(self):
+    flags = self.GetBrowserDeviceFlags()
+    mock_story_set = MockStorySet(url=self._url)
+    wpr_recorder = record_wpr.WprRecorder(self._test_data_dir,
+                                          mock_story_set, flags)
+    results = wpr_recorder.CreateResults()
+    wpr_recorder.Record(results)
+    self.assertEqual(set(mock_story_set.stories), results.pages_that_succeeded)
+
+  def testWprRecorderWithBenchmark(self):
+    flags = self.GetBrowserDeviceFlags()
+    flags.extend(['--mock-benchmark-url', self._url])
+    mock_benchmark = MockBenchmark()
+    wpr_recorder = record_wpr.WprRecorder(self._test_data_dir, mock_benchmark,
+                                          flags)
+    results = wpr_recorder.CreateResults()
+    wpr_recorder.Record(results)
+    self.assertEqual(set(mock_benchmark.mock_story_set.stories),
+                     results.pages_that_succeeded)
+
+  def testWprRecorderWithTimelineBasedMeasurementBenchmark(self):
+    flags = self.GetBrowserDeviceFlags()
+    flags.extend(['--mock-benchmark-url', self._url])
+    mock_benchmark = MockTimelineBasedMeasurementBenchmark()
+    wpr_recorder = record_wpr.WprRecorder(self._test_data_dir, mock_benchmark,
+                                          flags)
+    results = wpr_recorder.CreateResults()
+    wpr_recorder.Record(results)
+    self.assertEqual(set(mock_benchmark.mock_story_set.stories),
+                     results.pages_that_succeeded)
+
+  def testPageSetBaseDirFlag(self):
+    flags = self.GetBrowserDeviceFlags()
+    flags.extend(['--page-set-base-dir', self._test_data_dir,
+                  '--mock-benchmark-url', self._url])
+    mock_benchmark = MockBenchmark()
+    wpr_recorder = record_wpr.WprRecorder(
+        'non-existent-dummy-dir', mock_benchmark, flags)
+    results = wpr_recorder.CreateResults()
+    wpr_recorder.Record(results)
+    self.assertEqual(set(mock_benchmark.mock_story_set.stories),
+                     results.pages_that_succeeded)
+
+  def testCommandLineFlags(self):
+    flags = [
+        '--pageset-repeat', '2',
+        '--mock-benchmark-url', self._url,
+        '--upload',
+    ]
+    wpr_recorder = record_wpr.WprRecorder(self._test_data_dir, MockBenchmark(),
+                                          flags)
+    # page_runner command-line args
+    self.assertEquals(2, wpr_recorder.options.pageset_repeat)
+    # benchmark command-line args
+    self.assertEquals(self._url, wpr_recorder.options.mock_benchmark_url)
+    # record_wpr command-line arg to upload to cloud-storage.
+    self.assertTrue(wpr_recorder.options.upload)
+    # invalid command-line args
+    self.assertFalse(hasattr(wpr_recorder.options, 'not_a_real_option'))
+
+  def testRecordingEnabled(self):
+    flags = ['--mock-benchmark-url', self._url]
+    wpr_recorder = record_wpr.WprRecorder(self._test_data_dir, MockBenchmark(),
+                                          flags)
+    self.assertEqual(wpr_modes.WPR_RECORD,
+                     wpr_recorder.options.browser_options.wpr_mode)
+
+  # When the RecorderPageTest CustomizeBrowserOptions/WillStartBrowser/
+  # DidStartBrowser function is called, it forwards the call to the PageTest
+  def testRecorderPageTest_BrowserMethods(self):
+    flags = ['--mock-benchmark-url', self._url]
+    record_page_test = record_wpr.RecorderPageTest()
+    record_page_test.page_test = MockBenchmark().test()
+    wpr_recorder = record_wpr.WprRecorder(self._test_data_dir, MockBenchmark(),
+                                          flags)
+    record_page_test.CustomizeBrowserOptions(wpr_recorder.options)
+    record_page_test.WillStartBrowser(self._tab.browser.platform)
+    record_page_test.DidStartBrowser(self._tab.browser)
+    self.assertTrue(
+        'CustomizeBrowserOptions' in record_page_test.page_test.func_calls)
+    self.assertTrue('WillStartBrowser' in record_page_test.page_test.func_calls)
+    self.assertTrue('DidStartBrowser' in record_page_test.page_test.func_calls)
+
+  def testUseLiveSitesUnsupported(self):
+    flags = ['--use-live-sites']
+    with self.assertRaises(SystemExit):
+      record_wpr.WprRecorder(self._test_data_dir, MockBenchmark(), flags)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/story/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/story/__init__.py
new file mode 100644
index 0000000..a17b72d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/story/__init__.py
@@ -0,0 +1,15 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from py_utils import cloud_storage  # pylint: disable=import-error
+
+from telemetry.story.shared_state import SharedState
+from telemetry.story.story import Story
+from telemetry.story.story_filter import StoryFilter
+from telemetry.story.story_set import StorySet
+
+
+PUBLIC_BUCKET = cloud_storage.PUBLIC_BUCKET
+PARTNER_BUCKET = cloud_storage.PARTNER_BUCKET
+INTERNAL_BUCKET = cloud_storage.INTERNAL_BUCKET
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/story/shared_state.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/story/shared_state.py
new file mode 100644
index 0000000..9de4945
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/story/shared_state.py
@@ -0,0 +1,80 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from py_trace_event import trace_event
+
+
+class SharedState(object):
+  """A class that manages the test state across multiple stories.
+  It's styled on unittest.TestCase for handling test setup & teardown logic.
+
+  """
+
+  __metaclass__ = trace_event.TracedMetaClass
+
+  def __init__(self, test, options, story_set):
+    """ This method is styled on unittest.TestCase.setUpClass.
+    Override to do any action before running stories that
+    share this same state.
+    Args:
+      test: a legacy_page_test.LegacyPageTest or story_test.StoryTest instance.
+      options: a BrowserFinderOptions instance that contains command line
+        options.
+      story_set: a story.StorySet instance.
+    """
+    pass
+
+  @property
+  def platform(self):
+    """ Override to return the platform which stories that share this same
+    state will be run on.
+    """
+    raise NotImplementedError()
+
+  def WillRunStory(self, story):
+    """ Override to do any action before running each one of all stories
+    that share this same state.
+    This method is styled on unittest.TestCase.setUp.
+    """
+    raise NotImplementedError()
+
+  def DidRunStory(self, results):
+    """ Override to do any action after running each of all stories that
+    share this same state.
+    This method is styled on unittest.TestCase.tearDown.
+    """
+    raise NotImplementedError()
+
+  def CanRunStory(self, story):
+    """Indicate whether the story can be run in the current configuration.
+    This is called after WillRunStory and before RunStory. Return True
+    if the story should be run, and False if it should be skipped.
+    Most subclasses will probably want to override this to always
+    return True.
+    Args:
+      story: a story.Story instance.
+    """
+    raise NotImplementedError()
+
+  def RunStory(self, results):
+    """ Override to do any action before running each one of all stories
+    that share this same state.
+    This method is styled on unittest.TestCase.run.
+    """
+    raise NotImplementedError()
+
+  def TearDownState(self):
+    """ Override to do any action after running multiple stories that
+    share this same state.
+    This method is styled on unittest.TestCase.tearDownClass.
+    """
+    raise NotImplementedError()
+
+  def DumpStateUponFailure(self, story, results):
+    """ Dump the state upon failure.
+    This method tries to dump as much information about the application under
+    test as possible (output, log, screenshot, etc.) to simplify triaging the
+    failure.
+    """
+    raise NotImplementedError()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/story/story.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/story/story.py
new file mode 100644
index 0000000..99a2ed9
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/story/story.py
@@ -0,0 +1,142 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import re
+
+from telemetry.story import shared_state as shared_state_module
+
+_next_story_id = 0
+
+
+class Story(object):
+  """A class styled on unittest.TestCase for creating story tests.
+
+  Tests should override Run to maybe start the application and perform actions
+  on it. To share state between different tests, one can define a
+  shared_state which contains hooks that will be called before and
+  after mutiple stories run and in between runs.
+
+  Args:
+    shared_state_class: subclass of telemetry.story.shared_state.SharedState.
+    name: string name of this story that can be used for identifying this story
+        in results output.
+    tags: A list or set of string labels that are used for filtering. See
+        story.story_filter for more information.
+    is_local: If True, the story does not require network.
+    grouping_keys: A dict of grouping keys that will be added to values computed
+        on this story.
+  """
+
+  def __init__(self, shared_state_class, name='', tags=None,
+               is_local=False, make_javascript_deterministic=True,
+               grouping_keys=None, platform_specific=False):
+    """
+    Args:
+      make_javascript_deterministic: Whether JavaScript performed on
+          the page is made deterministic across multiple runs. This
+          requires that the web content is served via Web Page Replay
+          to take effect. This setting does not affect stories containing no web
+          content or where the HTTP MIME type is not text/html.See also:
+          _InjectScripts method in third_party/web-page-replay/httpclient.py.
+      platform_specific: Boolean indicating if a separate web page replay
+          recording is required on each platform.
+    """
+    assert issubclass(shared_state_class,
+                      shared_state_module.SharedState)
+    self._shared_state_class = shared_state_class
+    self._name = name
+    self._platform_specific = platform_specific
+    global _next_story_id
+    self._id = _next_story_id
+    _next_story_id += 1
+    if tags is None:
+      tags = set()
+    elif isinstance(tags, list):
+      tags = set(tags)
+    else:
+      assert isinstance(tags, set)
+    self._tags = tags
+    self._is_local = is_local
+    self._make_javascript_deterministic = make_javascript_deterministic
+    if grouping_keys is None:
+      grouping_keys = {}
+    else:
+      assert isinstance(grouping_keys, dict)
+    self._grouping_keys = grouping_keys
+
+  def Run(self, shared_state):
+    """Execute the interactions with the applications and/or platforms."""
+    raise NotImplementedError
+
+  @property
+  def tags(self):
+    return self._tags
+
+  @property
+  def shared_state_class(self):
+    return self._shared_state_class
+
+  @property
+  def id(self):
+    return self._id
+
+  @property
+  def name(self):
+    return self._name
+
+  @property
+  def grouping_keys(self):
+    return self._grouping_keys
+
+  @property
+  def display_name_and_grouping_key_tuple(self):
+    return self.display_name, tuple(self.grouping_keys.iteritems())
+
+  def AsDict(self):
+    """Converts a story object to a dict suitable for JSON output."""
+    d = {
+      'id': self._id,
+    }
+    if self._name:
+      d['name'] = self._name
+    return d
+
+  @property
+  def file_safe_name(self):
+    """A version of display_name that's safe to use as a filename.
+
+    The default implementation sanitizes special characters with underscores,
+    but it's okay to override it with a more specific implementation in
+    subclasses.
+    """
+    # This fail-safe implementation is safe for subclasses to override.
+    return re.sub('[^a-zA-Z0-9]', '_', self.display_name)
+
+  @property
+  def display_name(self):
+    if self.name:
+      return self.name
+    else:
+      return self.__class__.__name__
+
+  @property
+  def is_local(self):
+    """Returns True iff this story does not require network."""
+    return self._is_local
+
+  @property
+  def serving_dir(self):
+    """Returns the absolute path to a directory with hash files to data that
+       should be updated from cloud storage, or None if no files need to be
+       updated.
+    """
+    return None
+
+  @property
+  def make_javascript_deterministic(self):
+    return self._make_javascript_deterministic
+
+  @property
+  def platform_specific(self):
+    return self._platform_specific
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/story/story_filter.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/story/story_filter.py
new file mode 100644
index 0000000..1bbb528
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/story/story_filter.py
@@ -0,0 +1,82 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import optparse
+import re
+
+from telemetry.internal.util import command_line
+
+
+class _StoryMatcher(object):
+  def __init__(self, pattern):
+    self._regex = None
+    self.has_compile_error = False
+    if pattern:
+      try:
+        self._regex = re.compile(pattern)
+      except re.error:
+        self.has_compile_error = True
+
+  def __nonzero__(self):
+    return self._regex is not None
+
+  def HasMatch(self, story):
+    return self and bool(
+        self._regex.search(story.display_name) or
+        (story.name and self._regex.search(story.name)))
+
+
+class _StoryTagMatcher(object):
+  def __init__(self, tags_str):
+    self._tags = tags_str.split(',') if tags_str else None
+
+  def __nonzero__(self):
+    return self._tags is not None
+
+  def HasLabelIn(self, story):
+    return self and bool(story.tags.intersection(self._tags))
+
+
+class StoryFilter(command_line.ArgumentHandlerMixIn):
+  """Filters stories in the story set based on command-line flags."""
+
+  @classmethod
+  def AddCommandLineArgs(cls, parser):
+    group = optparse.OptionGroup(parser, 'User story filtering options')
+    group.add_option('--story-filter',
+        help='Use only stories whose names match the given filter regexp.')
+    group.add_option('--story-filter-exclude',
+        help='Exclude stories whose names match the given filter regexp.')
+    group.add_option('--story-tag-filter',
+        help='Use only stories that have any of these tags')
+    group.add_option('--story-tag-filter-exclude',
+        help='Exclude stories that have any of these tags')
+    parser.add_option_group(group)
+
+  @classmethod
+  def ProcessCommandLineArgs(cls, parser, args):
+    cls._include_regex = _StoryMatcher(args.story_filter)
+    cls._exclude_regex = _StoryMatcher(args.story_filter_exclude)
+
+    cls._include_tags = _StoryTagMatcher(args.story_tag_filter)
+    cls._exclude_tags = _StoryTagMatcher(args.story_tag_filter_exclude)
+
+    if cls._include_regex.has_compile_error:
+      raise parser.error('--story-filter: Invalid regex.')
+    if cls._exclude_regex.has_compile_error:
+      raise parser.error('--story-filter-exclude: Invalid regex.')
+
+  @classmethod
+  def IsSelected(cls, story):
+    # Exclude filters take priority.
+    if cls._exclude_tags.HasLabelIn(story):
+      return False
+    if cls._exclude_regex.HasMatch(story):
+      return False
+
+    if cls._include_tags and not cls._include_tags.HasLabelIn(story):
+      return False
+    if cls._include_regex and not cls._include_regex.HasMatch(story):
+      return False
+    return True
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/story/story_filter_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/story/story_filter_unittest.py
new file mode 100644
index 0000000..2df8bc3
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/story/story_filter_unittest.py
@@ -0,0 +1,94 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from telemetry import story
+from telemetry.page import page
+from telemetry.story import story_filter as story_filter_module
+
+
+class FilterTest(unittest.TestCase):
+
+  def setUp(self):
+    story_set = story.StorySet()
+    self.p1 = page.Page(
+      url='file://your/smile/widen.html', page_set=story_set,
+      name='MayYour.smile_widen', tags=['tag1', 'tag2'])
+    self.p2 = page.Page(
+      url='file://share_a/smile/too.html', page_set=story_set,
+      name='ShareA.smiles_too', tags=['tag1'])
+    self.p3 = page.Page(
+      url='file://share_a/smile/too.html', page_set=story_set,
+      tags=['tag2'])
+    self.pages = [self.p1, self.p2, self.p3]
+
+  @staticmethod
+  def ProcessCommandLineArgs(parser=None, **kwargs):
+    class Options(object):
+      def __init__(
+          self, story_filter=None, story_filter_exclude=None,
+          story_tag_filter=None, story_tag_filter_exclude=None):
+        self.story_filter = story_filter
+        self.story_filter_exclude = story_filter_exclude
+        self.story_tag_filter = story_tag_filter
+        self.story_tag_filter_exclude = story_tag_filter_exclude
+    story_filter_module.StoryFilter.ProcessCommandLineArgs(
+        parser, Options(**kwargs))
+
+  def PageSelections(self):
+    return [story_filter_module.StoryFilter.IsSelected(p) for p in self.pages]
+
+  def testNoFilterMatchesAll(self):
+    self.ProcessCommandLineArgs()
+    self.assertEquals([True, True, True], self.PageSelections())
+
+  def testBadRegexCallsParserError(self):
+    class MockParserException(Exception):
+      pass
+    class MockParser(object):
+      def error(self, _):
+        raise MockParserException
+    with self.assertRaises(MockParserException):
+      self.ProcessCommandLineArgs(parser=MockParser(), story_filter='+')
+
+  def testUniqueSubstring(self):
+    self.ProcessCommandLineArgs(story_filter='smile_widen')
+    self.assertEquals([True, False, False], self.PageSelections())
+
+  def testSharedSubstring(self):
+    self.ProcessCommandLineArgs(story_filter='smile')
+    self.assertEquals([True, True, True], self.PageSelections())
+
+  def testNoMatch(self):
+    self.ProcessCommandLineArgs(story_filter='frown')
+    self.assertEquals([False, False, False], self.PageSelections())
+
+  def testExclude(self):
+    self.ProcessCommandLineArgs(story_filter_exclude='ShareA')
+    self.assertEquals([True, False, True], self.PageSelections())
+
+  def testExcludeTakesPriority(self):
+    self.ProcessCommandLineArgs(
+        story_filter='smile',
+        story_filter_exclude='wide')
+    self.assertEquals([False, True, True], self.PageSelections())
+
+  def testNoNameMatchesDisplayName(self):
+    self.ProcessCommandLineArgs(story_filter='share_a/smile')
+    self.assertEquals([False, False, True], self.PageSelections())
+
+  def testNotagMatch(self):
+    self.ProcessCommandLineArgs(story_tag_filter='tagX')
+    self.assertEquals([False, False, False], self.PageSelections())
+
+  def testtagsAllMatch(self):
+    self.ProcessCommandLineArgs(story_tag_filter='tag1,tag2')
+    self.assertEquals([True, True, True], self.PageSelections())
+
+  def testExcludetagTakesPriority(self):
+    self.ProcessCommandLineArgs(
+        story_tag_filter='tag1',
+        story_tag_filter_exclude='tag2')
+    self.assertEquals([False, True, False], self.PageSelections())
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/story/story_set.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/story/story_set.py
new file mode 100644
index 0000000..bae72ab
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/story/story_set.py
@@ -0,0 +1,172 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import inspect
+import os
+
+from telemetry.story import story as story_module
+from telemetry.wpr import archive_info
+
+
+class StorySet(object):
+  """A collection of stories.
+
+  A typical usage of StorySet would be to subclass it and then call
+  AddStory for each Story.
+  """
+
+  def __init__(self, archive_data_file='', cloud_storage_bucket=None,
+               base_dir=None, serving_dirs=None):
+    """Creates a new StorySet.
+
+    Args:
+      archive_data_file: The path to Web Page Replay's archive data, relative
+          to self.base_dir.
+      cloud_storage_bucket: The cloud storage bucket used to download
+          Web Page Replay's archive data. Valid values are: None,
+          story.PUBLIC_BUCKET, story.PARTNER_BUCKET, or story.INTERNAL_BUCKET
+          (defined in telemetry.util.cloud_storage).
+      serving_dirs: A set of paths, relative to self.base_dir, to directories
+          containing hash files for non-wpr archive data stored in cloud
+          storage.
+    """
+    self._stories = []
+    self._story_names_and_grouping_keys = set()
+    self._archive_data_file = archive_data_file
+    self._wpr_archive_info = None
+    archive_info.AssertValidCloudStorageBucket(cloud_storage_bucket)
+    self._cloud_storage_bucket = cloud_storage_bucket
+    if base_dir:
+      if not os.path.isdir(base_dir):
+        raise ValueError('Invalid directory path of base_dir: %s' % base_dir)
+      self._base_dir = base_dir
+    else:
+      self._base_dir = os.path.dirname(inspect.getfile(self.__class__))
+    # Convert any relative serving_dirs to absolute paths.
+    self._serving_dirs = set(os.path.realpath(os.path.join(self.base_dir, d))
+                             for d in serving_dirs or [])
+
+  @property
+  def allow_mixed_story_states(self):
+    """True iff Stories are allowed to have different StoryState classes.
+
+    There are no checks in place for determining if SharedStates are
+    being assigned correctly to all Stories in a given StorySet. The
+    majority of test cases should not need the ability to have multiple
+    SharedStates, which usually implies you should be writing multiple
+    benchmarks instead. We provide errors to avoid accidentally assigning
+    or defaulting to the wrong SharedState.
+    Override at your own risk. Here be dragons.
+    """
+    return False
+
+  @property
+  def file_path(self):
+    return inspect.getfile(self.__class__).replace('.pyc', '.py')
+
+  @property
+  def base_dir(self):
+    """The base directory to resolve archive_data_file.
+
+    This defaults to the directory containing the StorySet instance's class.
+    """
+    return self._base_dir
+
+  @property
+  def serving_dirs(self):
+    all_serving_dirs = self._serving_dirs.copy()
+    for story in self.stories:
+      if story.serving_dir:
+        all_serving_dirs.add(story.serving_dir)
+    return all_serving_dirs
+
+  @property
+  def archive_data_file(self):
+    return self._archive_data_file
+
+  @property
+  def bucket(self):
+    return self._cloud_storage_bucket
+
+  @property
+  def wpr_archive_info(self):
+    """Lazily constructs wpr_archive_info if it's not set and returns it."""
+    if self.archive_data_file and not self._wpr_archive_info:
+      self._wpr_archive_info = archive_info.WprArchiveInfo.FromFile(
+          os.path.join(self.base_dir, self.archive_data_file), self.bucket)
+    return self._wpr_archive_info
+
+  @property
+  def stories(self):
+    return self._stories
+
+  def AddStory(self, story):
+    assert isinstance(story, story_module.Story)
+    assert self._IsUnique(story), ('Tried to add story with duplicate display '
+                                   'name %s. Story display names should be '
+                                   'unique.' % story.display_name)
+    self._stories.append(story)
+    self._story_names_and_grouping_keys.add(
+        story.display_name_and_grouping_key_tuple)
+
+  def _IsUnique(self, story):
+    return (story.display_name_and_grouping_key_tuple not in
+            self._story_names_and_grouping_keys)
+
+  def RemoveStory(self, story):
+    """Removes a Story.
+
+    Allows the stories to be filtered.
+    """
+    self._stories.remove(story)
+    self._story_names_and_grouping_keys.remove(
+        story.display_name_and_grouping_key_tuple)
+
+  @classmethod
+  def Name(cls):
+    """Returns the string name of this StorySet.
+    Note that this should be a classmethod so the benchmark_runner script can
+    match the story class with its name specified in the run command:
+    'Run <User story test name> <User story class name>'
+    """
+    return cls.__module__.split('.')[-1]
+
+  @classmethod
+  def Description(cls):
+    """Return a string explaining in human-understandable terms what this
+    story represents.
+    Note that this should be a classmethod so the benchmark_runner script can
+    display stories' names along with their descriptions in the list command.
+    """
+    if cls.__doc__:
+      return cls.__doc__.splitlines()[0]
+    else:
+      return ''
+
+  def WprFilePathForStory(self, story, target_platform=None):
+    """Convenient function to retrieve WPR archive file path.
+
+    Args:
+      story: The Story to look up.
+
+    Returns:
+      The WPR archive file path for the given Story, if found.
+      Otherwise, None.
+    """
+    if not self.wpr_archive_info:
+      return None
+    return self.wpr_archive_info.WprFilePathForStory(
+        story, target_platform=target_platform)
+
+  def __iter__(self):
+    return self.stories.__iter__()
+
+  def __len__(self):
+    return len(self.stories)
+
+  def __getitem__(self, key):
+    return self.stories[key]
+
+  def __setitem__(self, key, value):
+    self._stories[key] = value
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/story/story_set_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/story/story_set_unittest.py
new file mode 100644
index 0000000..b6710c6
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/story/story_set_unittest.py
@@ -0,0 +1,120 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import unittest
+
+from telemetry import story
+
+
+# pylint: disable=abstract-method
+class SharedStateBar(story.SharedState):
+  pass
+
+
+class StoryFoo(story.Story):
+  def __init__(self, name='', labels=None, grouping_keys=None):
+    super(StoryFoo, self).__init__(
+        SharedStateBar, name, labels, grouping_keys=grouping_keys)
+
+
+class StorySetFoo(story.StorySet):
+  """ StorySetFoo is a story set created for testing purpose. """
+  pass
+
+
+class StorySetTest(unittest.TestCase):
+
+  def testStorySetTestName(self):
+    self.assertEquals('story_set_unittest', StorySetFoo.Name())
+
+  def testStorySetTestDescription(self):
+    self.assertEquals(
+        ' StorySetFoo is a story set created for testing purpose. ',
+        StorySetFoo.Description())
+
+  def testBaseDir(self):
+    story_set = StorySetFoo()
+    base_dir = story_set.base_dir
+    self.assertTrue(os.path.isdir(base_dir))
+    self.assertEqual(base_dir, os.path.dirname(__file__))
+
+  def testFilePath(self):
+    story_set = StorySetFoo()
+    self.assertEqual(os.path.abspath(__file__).replace('.pyc', '.py'),
+                     story_set.file_path)
+
+  def testCloudBucket(self):
+    blank_story_set = story.StorySet()
+    self.assertEqual(blank_story_set.bucket, None)
+
+    public_story_set = story.StorySet(
+        cloud_storage_bucket=story.PUBLIC_BUCKET)
+    self.assertEqual(public_story_set.bucket, story.PUBLIC_BUCKET)
+
+    partner_story_set = story.StorySet(
+        cloud_storage_bucket=story.PARTNER_BUCKET)
+    self.assertEqual(partner_story_set.bucket, story.PARTNER_BUCKET)
+
+    internal_story_set = story.StorySet(
+        cloud_storage_bucket=story.INTERNAL_BUCKET)
+    self.assertEqual(internal_story_set.bucket, story.INTERNAL_BUCKET)
+
+    with self.assertRaises(ValueError):
+      story.StorySet(cloud_storage_bucket='garbage_bucket')
+
+  def testRemoveWithEmptySetRaises(self):
+    story_set = story.StorySet()
+    foo_story = StoryFoo()
+    with self.assertRaises(ValueError):
+      story_set.RemoveStory(foo_story)
+
+  def testBasicAddRemove(self):
+    story_set = story.StorySet()
+    foo_story = StoryFoo()
+    story_set.AddStory(foo_story)
+    self.assertEqual([foo_story], story_set.stories)
+
+    story_set.RemoveStory(foo_story)
+    self.assertEqual([], story_set.stories)
+
+  def testAddDuplicateDisplayNameWithoutGroupingKeysRaises(self):
+    story_set = story.StorySet()
+    foo_story = StoryFoo(name='foo')
+
+    story_set.AddStory(foo_story)
+
+    with self.assertRaises(AssertionError):
+      story_set.AddStory(foo_story)
+
+  def testAddDuplicateDisplayNameWithDifferentGroupingKeys(self):
+    story_set = story.StorySet()
+    foo_story0 = StoryFoo(name='foo', grouping_keys={
+        'bar': 3, 'baz': 4})
+    foo_story1 = StoryFoo(name='foo', grouping_keys={
+        'bar': 7, 'baz': 8})
+
+    story_set.AddStory(foo_story0)
+    story_set.AddStory(foo_story1)
+
+  def testAddDuplicateDisplayNameWithSameGroupingKeysRaises(self):
+    story_set = story.StorySet()
+    foo_story0 = StoryFoo(name='foo', grouping_keys={
+        'bar': 3, 'baz': 4})
+    foo_story1 = StoryFoo(name='foo', grouping_keys={
+        'bar': 3, 'baz': 4})
+
+    story_set.AddStory(foo_story0)
+
+    with self.assertRaises(AssertionError):
+      story_set.AddStory(foo_story1)
+
+  def testAddRemoveAddStoryIsStillUnique(self):
+    story_set = story.StorySet()
+    foo_story = StoryFoo(name='foo', grouping_keys={
+        'bar': 3, 'baz': 4})
+
+    story_set.AddStory(foo_story)
+    story_set.RemoveStory(foo_story)
+    story_set.AddStory(foo_story)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/story/story_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/story/story_unittest.py
new file mode 100644
index 0000000..9bc18b5
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/story/story_unittest.py
@@ -0,0 +1,60 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from telemetry import story
+from telemetry.story import shared_state
+
+
+# pylint: disable=abstract-method
+class SharedStateBar(shared_state.SharedState):
+  pass
+
+
+class StoryFoo(story.Story):
+  def __init__(self, name='', tags=None):
+    super(StoryFoo, self).__init__(
+        SharedStateBar, name, tags)
+
+
+class StoryTest(unittest.TestCase):
+  def testStoriesHaveDifferentIds(self):
+    s0 = story.Story(SharedStateBar, 'foo')
+    s1 = story.Story(SharedStateBar, 'bar')
+    self.assertNotEqual(s0.id, s1.id)
+
+  def testNamelessStoryDisplayName(self):
+    s = StoryFoo()
+    self.assertEquals('StoryFoo', s.display_name)
+
+  def testNamedStoryDisplayName(self):
+    s = StoryFoo('Bar')
+    self.assertEquals('Bar', s.display_name)
+
+  def testStoryFileSafeName(self):
+    s = StoryFoo('Foo Bar:Baz~0')
+    self.assertEquals('Foo_Bar_Baz_0', s.file_safe_name)
+
+  def testNamelessStoryAsDict(self):
+    s = story.Story(SharedStateBar)
+    s_dict = s.AsDict()
+    self.assertEquals(s_dict['id'], s.id)
+    self.assertNotIn('name', s_dict)
+
+  def testNamedStoryAsDict(self):
+    s = story.Story(SharedStateBar, 'Foo')
+    s_dict = s.AsDict()
+    self.assertEquals(s_dict['id'], s.id)
+    self.assertEquals('Foo', s_dict['name'])
+
+  def testMakeJavaScriptDeterministic(self):
+    s = story.Story(SharedStateBar)
+    self.assertTrue(s.make_javascript_deterministic)
+
+    s = story.Story(SharedStateBar, make_javascript_deterministic=False)
+    self.assertFalse(s.make_javascript_deterministic)
+
+    s = story.Story(SharedStateBar, make_javascript_deterministic=True)
+    self.assertTrue(s.make_javascript_deterministic)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/telemetry_unittest_deps.json b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/telemetry_unittest_deps.json
new file mode 100644
index 0000000..429493b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/telemetry_unittest_deps.json
@@ -0,0 +1,27 @@
+{
+  "config_type": "BaseConfig",
+  "dependencies": {
+    "example_domain_archive": {
+      "cloud_storage_base_folder": "wpr-archives",
+      "cloud_storage_bucket": "chromium-telemetry",
+      "file_info": {
+        "linux_x86_64": {
+          "cloud_storage_hash": "5e49b8152e40b5df427a8e73062045ddde2edcb8",
+          "download_path": "internal/testing/test_page_sets/data/example_domain_001.wpr"
+        },
+        "mac_x86_64": {
+          "cloud_storage_hash": "5e49b8152e40b5df427a8e73062045ddde2edcb8",
+          "download_path": "internal/testing/test_page_sets/data/example_domain_001.wpr"
+        },
+        "win_AMD64": {
+          "cloud_storage_hash": "5e49b8152e40b5df427a8e73062045ddde2edcb8",
+          "download_path": "internal/testing/test_page_sets/data/example_domain_001.wpr"
+        },
+        "win_x86": {
+          "cloud_storage_hash": "5e49b8152e40b5df427a8e73062045ddde2edcb8",
+          "download_path": "internal/testing/test_page_sets/data/example_domain_001.wpr"
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/__init__.py
new file mode 100644
index 0000000..4d6aabb
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/__init__.py
@@ -0,0 +1,3 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/browser_test_case.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/browser_test_case.py
new file mode 100644
index 0000000..956e5d8
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/browser_test_case.py
@@ -0,0 +1,115 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from functools import wraps
+import logging
+import os
+import sys
+import types
+import unittest
+
+from telemetry.internal.browser import browser_finder
+from telemetry.internal.util import path
+from telemetry.testing import options_for_unittests
+
+current_browser_options = None
+current_browser = None
+
+
+class _MetaBrowserTestCase(type):
+  """Metaclass for BrowserTestCase.
+
+  The metaclass wraps all test* methods of all subclasses of BrowserTestCase to
+  print browser standard output and log upon failure.
+  """
+
+  def __new__(mcs, name, bases, dct):
+    new_dct = {}
+    for attributeName, attribute in dct.iteritems():
+      if (isinstance(attribute, types.FunctionType) and
+          attributeName.startswith('test')):
+        attribute = mcs._PrintBrowserStandardOutputAndLogOnFailure(attribute)
+      new_dct[attributeName] = attribute
+    return type.__new__(mcs, name, bases, new_dct)
+
+  @staticmethod
+  def _PrintBrowserStandardOutputAndLogOnFailure(method):
+    @wraps(method)
+    def WrappedMethod(self):
+      try:  # pylint: disable=broad-except
+        method(self)
+      except Exception:
+        exc_info = sys.exc_info()
+
+        if self._browser:
+          self._browser.DumpStateUponFailure()
+        else:
+          logging.warning('Cannot dump browser state: No browser.')
+
+        # Re-raise the original exception. Note that we can't just use 'raise'
+        # without any arguments because an exception might have been thrown when
+        # dumping the state of the browser.
+        raise exc_info[0], exc_info[1], exc_info[2]
+    return WrappedMethod
+
+
+def teardown_browser():
+  global current_browser
+  global current_browser_options
+
+  if current_browser:
+    current_browser.Close()
+    current_browser.platform.network_controller.Close()
+  current_browser = None
+  current_browser_options = None
+
+
+class BrowserTestCase(unittest.TestCase):
+  __metaclass__ = _MetaBrowserTestCase
+
+  @classmethod
+  def setUpClass(cls):
+    cls._platform = None
+    global current_browser
+    global current_browser_options
+
+    options = options_for_unittests.GetCopy()
+
+    cls.CustomizeBrowserOptions(options.browser_options)
+    if not current_browser or (current_browser_options !=
+                               options.browser_options):
+      if current_browser:
+        teardown_browser()
+
+      browser_to_create = browser_finder.FindBrowser(options)
+      if not browser_to_create:
+        raise Exception('No browser found, cannot continue test.')
+      cls._platform = browser_to_create.platform
+      cls._platform.network_controller.InitializeIfNeeded()
+
+      try:
+        current_browser = browser_to_create.Create(options)
+        current_browser_options = options.browser_options
+      except:
+        cls.tearDownClass()
+        raise
+    cls._browser = current_browser
+    cls._device = options.remote_platform_options.device
+
+  @classmethod
+  def tearDownClass(cls):
+    if cls._platform:
+      cls._platform.StopAllLocalServers()
+      cls._platform.network_controller.Close()
+
+  @classmethod
+  def CustomizeBrowserOptions(cls, options):
+    """Override to add test-specific options to the BrowserOptions object"""
+    pass
+
+  @classmethod
+  def UrlOfUnittestFile(cls, filename):
+    cls._platform.SetHTTPServerDirectories(path.GetUnittestDataDir())
+    file_path = os.path.join(path.GetUnittestDataDir(), filename)
+    return cls._platform.http_server.UrlOf(file_path)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/browser_test_context.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/browser_test_context.py
new file mode 100644
index 0000000..f119372
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/browser_test_context.py
@@ -0,0 +1,74 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import copy
+import sets
+
+
+_global_test_context = None
+
+
+def GetCopy():
+  return copy.deepcopy(_global_test_context)
+
+
+class TypTestContext(object):
+  """ The TestContext that is used for passing data from the main test process
+  to typ's subprocesses. Those includes:
+     _ client_configs: list of client configs that contain infos about binaries
+        to use.
+     _ finder_options: the commandline options object. This is an instance of
+        telemetry.internal.browser.browser_options.BrowserFinderOptions.
+     _ test_class: the name of the test class to be run.
+     _ test_case_ids_to_run: the ids of the test cases to be run. e.g:
+        foo.bar.Test1, foo.bar.Test2,..
+
+
+  This object is designed to be pickle-able so that it can be easily pass from
+  the main process to test subprocesses. It also supports immutable mode to
+  ensure its data won't be changed by the subprocesses.
+  """
+  def __init__(self):
+    self._client_configs = []
+    self._finder_options = None
+    self._test_class = None
+    self._test_cases_ids_to_run = set()
+    self._frozen = False
+
+  def Freeze(self):
+    """ Makes the |self| object immutable.
+
+    Calling setter on |self|'s property will throw exception.
+    """
+    assert self._finder_options
+    assert self._test_class
+    self._frozen = True
+    self._test_cases_ids_to_run = sets.ImmutableSet(self._test_cases_ids_to_run)
+    self._client_configs = tuple(self._client_configs)
+
+  @property
+  def finder_options(self):
+    return self._finder_options
+
+  @property
+  def client_configs(self):
+    return self._client_configs
+
+  @property
+  def test_class(self):
+    return self._test_class
+
+  @property
+  def test_case_ids_to_run(self):
+    return self._test_cases_ids_to_run
+
+  @finder_options.setter
+  def finder_options(self, value):
+    assert not self._frozen
+    self._finder_options = value
+
+  @test_class.setter
+  def test_class(self, value):
+    assert not self._test_class
+    self._test_class = value
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/browser_test_runner.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/browser_test_runner.py
new file mode 100644
index 0000000..5d883c8
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/browser_test_runner.py
@@ -0,0 +1,37 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import os
+import subprocess
+import sys
+
+from telemetry.core import util
+
+
+def Run(project_config, args):
+  assert '--top-level-dir' not in args, (
+      'Top level directory for running tests should be specified through '
+      'the instance of telemetry.project_config.ProjectConfig.')
+  assert '--client-config' not in args, (
+      'Client config file to be used for telemetry should be specified through '
+      'the instance of telemetry.project_config.ProjectConfig.')
+  assert project_config.top_level_dir, 'Must specify top level dir for project'
+  args.extend(['--top-level-dir', project_config.top_level_dir])
+  for c in project_config.client_configs:
+    args.extend(['--client-config', c])
+  for s in project_config.start_dirs:
+    args.extend(['--start-dir', s])
+
+  if project_config.default_chrome_root and not '--chrome-root' in args:
+    args.extend(['--chrome-root', project_config.default_chrome_root])
+
+  env = os.environ.copy()
+  telemetry_dir = util.GetTelemetryDir()
+  if 'PYTHONPATH' in env:
+    env['PYTHONPATH'] = os.pathsep.join([env['PYTHONPATH'], telemetry_dir])
+  else:
+    env['PYTHONPATH'] = telemetry_dir
+
+  path_to_run_tests = os.path.join(os.path.abspath(os.path.dirname(__file__)),
+                                   'run_browser_tests.py')
+  return subprocess.call([sys.executable, path_to_run_tests] + args, env=env)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/browser_test_runner_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/browser_test_runner_unittest.py
new file mode 100644
index 0000000..d3439bb
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/browser_test_runner_unittest.py
@@ -0,0 +1,399 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import string
+import sys
+import tempfile
+import unittest
+import json
+
+from telemetry import decorators
+from telemetry import project_config
+from telemetry.core import util
+from telemetry.testing import browser_test_context
+from telemetry.testing import browser_test_runner
+from telemetry.testing import options_for_unittests
+from telemetry.testing import run_browser_tests
+from telemetry.testing import serially_executed_browser_test_case
+
+
+class BrowserTestRunnerTest(unittest.TestCase):
+
+  def _ExtractTestResults(self, test_result):
+    delimiter = test_result['path_delimiter']
+    failures = []
+    successes = []
+    def _IsLeafNode(node):
+      test_dict = node[1]
+      return ('expected' in test_dict and
+              isinstance(test_dict['expected'], basestring))
+    node_queues = []
+    for t in test_result['tests']:
+      node_queues.append((t, test_result['tests'][t]))
+    while node_queues:
+      node = node_queues.pop()
+      full_test_name, test_dict = node
+      if _IsLeafNode(node):
+        if all(res not in test_dict['expected'].split() for res in
+               test_dict['actual'].split()):
+          failures.append(full_test_name)
+        else:
+          successes.append(full_test_name)
+      else:
+        for k in test_dict:
+          node_queues.append(
+            ('%s%s%s' % (full_test_name, delimiter, k),
+             test_dict[k]))
+    return successes, failures
+
+  def baseTest(self, test_filter,
+               failures, successes, test_name='SimpleTest'):
+    config = project_config.ProjectConfig(
+        top_level_dir=os.path.join(util.GetTelemetryDir(), 'examples'),
+        client_configs=[],
+        benchmark_dirs=[
+            os.path.join(util.GetTelemetryDir(), 'examples', 'browser_tests')]
+    )
+    temp_file = tempfile.NamedTemporaryFile(delete=False)
+    temp_file.close()
+    temp_file_name = temp_file.name
+    try:
+      browser_test_runner.Run(
+          config,
+          [test_name,
+           '--write-full-results-to=%s' % temp_file_name,
+           '--test-filter=%s' % test_filter])
+      with open(temp_file_name) as f:
+        test_result = json.load(f)
+
+      actual_successes, actual_failures = self._ExtractTestResults(test_result)
+      self.assertEquals(set(actual_failures), set(failures))
+      self.assertEquals(set(actual_successes), set(successes))
+    finally:
+      os.remove(temp_file_name)
+
+  @decorators.Disabled('chromeos')  # crbug.com/696553
+  def testJsonOutputFormatNegativeFilter(self):
+    self.baseTest(
+      '^(add|multiplier).*',
+      ['browser_tests.simple_numeric_test.SimpleTest.add_1_and_2',
+       'browser_tests.simple_numeric_test.SimpleTest.add_7_and_3',
+       'browser_tests.simple_numeric_test.SimpleTest.multiplier_simple_2'],
+      ['browser_tests.simple_numeric_test.SimpleTest.add_2_and_3',
+       'browser_tests.simple_numeric_test.SimpleTest.multiplier_simple',
+       'browser_tests.simple_numeric_test.SimpleTest.multiplier_simple_3'])
+
+  @decorators.Disabled('chromeos')  # crbug.com/696553
+  def testJsonOutputWhenSetupClassFailed(self):
+    self.baseTest(
+      '.*',
+      ['browser_tests.failed_tests.SetUpClassFailedTest.dummy_test_0',
+       'browser_tests.failed_tests.SetUpClassFailedTest.dummy_test_1',
+       'browser_tests.failed_tests.SetUpClassFailedTest.dummy_test_2'],
+      [], test_name='SetUpClassFailedTest')
+
+  @decorators.Disabled('chromeos')  # crbug.com/696553
+  def testJsonOutputWhenTearDownClassFailed(self):
+    self.baseTest(
+      '.*',
+      ['browser_tests.failed_tests.TearDownClassFailedTest.dummy_test_0',
+       'browser_tests.failed_tests.TearDownClassFailedTest.dummy_test_1',
+       'browser_tests.failed_tests.TearDownClassFailedTest.dummy_test_2'],
+      [], test_name='TearDownClassFailedTest')
+
+  @decorators.Disabled('chromeos')  # crbug.com/696553
+  def testSetUpProcessCalledOnce(self):
+    self.baseTest(
+      '.*',
+      [],
+      ['browser_tests.process_tests.FailIfSetUpProcessCalledTwice.Dummy_0',
+       'browser_tests.process_tests.FailIfSetUpProcessCalledTwice.Dummy_1',
+       'browser_tests.process_tests.FailIfSetUpProcessCalledTwice.Dummy_2'],
+      test_name='FailIfSetUpProcessCalledTwice')
+
+  @decorators.Disabled('chromeos')  # crbug.com/696553
+  def testTearDownProcessCalledOnce(self):
+    self.baseTest(
+      '.*',
+      [],
+      ['browser_tests.process_tests.FailIfTearDownProcessCalledTwice.Dummy_0',
+       'browser_tests.process_tests.FailIfTearDownProcessCalledTwice.Dummy_1',
+       'browser_tests.process_tests.FailIfTearDownProcessCalledTwice.Dummy_2'],
+      test_name='FailIfTearDownProcessCalledTwice')
+
+  @decorators.Disabled('chromeos')  # crbug.com/696553
+  def testJsonOutputFormatPositiveFilter(self):
+    self.baseTest(
+      '(TestSimple|TestException).*',
+      ['browser_tests.simple_numeric_test.SimpleTest.TestException',
+       'browser_tests.simple_numeric_test.SimpleTest.TestSimple'], [])
+
+  @decorators.Disabled('chromeos')  # crbug.com/696553
+  def testExecutingTestsInSortedOrder(self):
+    alphabetical_tests = []
+    prefix = 'browser_tests.simple_numeric_test.SimpleTest.Alphabetical_'
+    for i in xrange(20):
+      alphabetical_tests.append(prefix + str(i))
+    for c in string.uppercase[:26]:
+      alphabetical_tests.append(prefix + c)
+    for c in string.lowercase[:26]:
+      alphabetical_tests.append(prefix + c)
+    alphabetical_tests.sort()
+    self.baseTest(
+        'Alphabetical', [], alphabetical_tests)
+
+  def shardingRangeTestHelper(self, total_shards, num_tests):
+    shard_ranges = []
+    for shard_index in xrange(0, total_shards):
+      shard_ranges.append(run_browser_tests._TestRangeForShard(
+        total_shards, shard_index, num_tests))
+    # Make assertions about ranges
+    num_tests_run = 0
+    for i in xrange(0, len(shard_ranges)):
+      cur_range = shard_ranges[i]
+      if i < num_tests:
+        self.assertGreater(cur_range[1], cur_range[0])
+        num_tests_run += (cur_range[1] - cur_range[0])
+      else:
+        # Not enough tests to go around all of the shards.
+        self.assertEquals(cur_range[0], cur_range[1])
+    # Make assertions about non-overlapping ranges
+    for i in xrange(1, len(shard_ranges)):
+      prev_range = shard_ranges[i - 1]
+      cur_range = shard_ranges[i]
+      self.assertEquals(prev_range[1], cur_range[0])
+    # Assert that we run all of the tests (very important)
+    self.assertEquals(num_tests_run, num_tests)
+
+  def testShardsWithPrimeNumTests(self):
+    for total_shards in xrange(1, 20):
+      # Nice non-prime number
+      self.shardingRangeTestHelper(total_shards, 101)
+
+  def testShardsWithDivisibleNumTests(self):
+    for total_shards in xrange(1, 6):
+      self.shardingRangeTestHelper(total_shards, 8)
+
+  def testShardBoundaryConditions(self):
+    self.shardingRangeTestHelper(1, 0)
+    self.shardingRangeTestHelper(1, 1)
+    self.shardingRangeTestHelper(2, 1)
+
+  def baseShardingTest(self, total_shards, shard_index, failures, successes,
+                       opt_abbr_input_json_file=None,
+                       opt_test_filter='',
+                       opt_filter_tests_after_sharding=False):
+    config = project_config.ProjectConfig(
+        top_level_dir=os.path.join(util.GetTelemetryDir(), 'examples'),
+        client_configs=[],
+        benchmark_dirs=[
+            os.path.join(util.GetTelemetryDir(), 'examples', 'browser_tests')]
+    )
+    temp_file = tempfile.NamedTemporaryFile(delete=False)
+    temp_file.close()
+    temp_file_name = temp_file.name
+    opt_args = []
+    if opt_abbr_input_json_file:
+      opt_args += [
+        '--read-abbreviated-json-results-from=%s' % opt_abbr_input_json_file]
+    if opt_test_filter:
+      opt_args += [
+        '--test-filter=%s' % opt_test_filter]
+    if opt_filter_tests_after_sharding:
+      opt_args += ['--filter-tests-after-sharding']
+    try:
+      browser_test_runner.Run(
+          config,
+          ['SimpleShardingTest',
+           '--write-full-results-to=%s' % temp_file_name,
+           '--total-shards=%d' % total_shards,
+           '--shard-index=%d' % shard_index] + opt_args)
+      with open(temp_file_name) as f:
+        test_result = json.load(f)
+      actual_successes, actual_failures = self._ExtractTestResults(test_result)
+      self.assertEquals(set(actual_failures), set(failures))
+      self.assertEquals(set(actual_successes), set(successes))
+    finally:
+      os.remove(temp_file_name)
+
+  @decorators.Disabled('chromeos')  # crbug.com/696553
+  def testShardedTestRun(self):
+    self.baseShardingTest(3, 0, [], [
+      'browser_tests.simple_sharding_test.SimpleShardingTest.Test1',
+      'browser_tests.simple_sharding_test.SimpleShardingTest.Test2',
+      'browser_tests.simple_sharding_test.SimpleShardingTest.Test3',
+      'browser_tests.simple_sharding_test.SimpleShardingTest.passing_test_0',
+      'browser_tests.simple_sharding_test.SimpleShardingTest.passing_test_1',
+    ])
+    self.baseShardingTest(3, 1, [], [
+      'browser_tests.simple_sharding_test.SimpleShardingTest.passing_test_2',
+      'browser_tests.simple_sharding_test.SimpleShardingTest.passing_test_3',
+      'browser_tests.simple_sharding_test.SimpleShardingTest.passing_test_4',
+      'browser_tests.simple_sharding_test.SimpleShardingTest.passing_test_5',
+    ])
+    self.baseShardingTest(3, 2, [], [
+      'browser_tests.simple_sharding_test.SimpleShardingTest.passing_test_6',
+      'browser_tests.simple_sharding_test.SimpleShardingTest.passing_test_7',
+      'browser_tests.simple_sharding_test.SimpleShardingTest.passing_test_8',
+      'browser_tests.simple_sharding_test.SimpleShardingTest.passing_test_9',
+    ])
+
+  def writeMockTestResultsFile(self):
+    mock_test_results = {
+      'passes': [
+        'Test1',
+        'Test2',
+        'Test3',
+        'passing_test_0',
+        'passing_test_1',
+        'passing_test_2',
+        'passing_test_3',
+        'passing_test_4',
+        'passing_test_5',
+        'passing_test_6',
+        'passing_test_7',
+        'passing_test_8',
+        'passing_test_9',
+      ],
+      'failures': [],
+      'valid': True,
+      'times': {
+        'Test1': 3.0,
+        'Test2': 3.0,
+        'Test3': 3.0,
+        'passing_test_0': 3.0,
+        'passing_test_1': 2.0,
+        'passing_test_2': 2.0,
+        'passing_test_3': 2.0,
+        'passing_test_4': 2.0,
+        'passing_test_5': 1.0,
+        'passing_test_6': 1.0,
+        'passing_test_7': 1.0,
+        'passing_test_8': 1.0,
+        'passing_test_9': 0.5,
+      }
+    }
+    temp_file = tempfile.NamedTemporaryFile(delete=False)
+    temp_file.close()
+    temp_file_name = temp_file.name
+    with open(temp_file_name, 'w') as f:
+      json.dump(mock_test_results, f)
+    return temp_file_name
+
+  @decorators.Disabled('chromeos')  # crbug.com/696553
+  def testSplittingShardsByTimes(self):
+    temp_file_name = self.writeMockTestResultsFile()
+    # It seems that the sorting order of the first four tests above is:
+    #   passing_test_0, Test1, Test2, Test3
+    # This is probably because the relative order of the "fixed" tests
+    # (starting with "Test") and the generated ones ("passing_") is
+    # not well defined, and the sorting is stable afterward.  The
+    # expectations have been adjusted for this fact.
+    try:
+      self.baseShardingTest(
+        4, 0, [],
+        ['browser_tests.simple_sharding_test.SimpleShardingTest.passing_test_0',
+         'browser_tests.simple_sharding_test.SimpleShardingTest.passing_test_1',
+         'browser_tests.simple_sharding_test.SimpleShardingTest.passing_test_5',
+         'browser_tests.simple_sharding_test.SimpleShardingTest.passing_test_9'
+        ], temp_file_name)
+      self.baseShardingTest(
+        4, 1, [],
+        ['browser_tests.simple_sharding_test.SimpleShardingTest.Test1',
+         'browser_tests.simple_sharding_test.SimpleShardingTest.passing_test_2',
+         'browser_tests.simple_sharding_test.SimpleShardingTest.passing_test_6'
+        ], temp_file_name)
+      self.baseShardingTest(
+        4, 2, [],
+        ['browser_tests.simple_sharding_test.SimpleShardingTest.Test2',
+         'browser_tests.simple_sharding_test.SimpleShardingTest.passing_test_3',
+         'browser_tests.simple_sharding_test.SimpleShardingTest.passing_test_7'
+        ], temp_file_name)
+      self.baseShardingTest(
+        4, 3, [],
+        ['browser_tests.simple_sharding_test.SimpleShardingTest.Test3',
+         'browser_tests.simple_sharding_test.SimpleShardingTest.passing_test_4',
+         'browser_tests.simple_sharding_test.SimpleShardingTest.passing_test_8'
+        ], temp_file_name)
+    finally:
+      os.remove(temp_file_name)
+
+  @decorators.Disabled('chromeos')  # crbug.com/696553
+  def testFilteringAfterSharding(self):
+    temp_file_name = self.writeMockTestResultsFile()
+    try:
+      self.baseShardingTest(
+        4, 1, [],
+        ['browser_tests.simple_sharding_test.SimpleShardingTest.Test1',
+         'browser_tests.simple_sharding_test.SimpleShardingTest.passing_test_2',
+         'browser_tests.simple_sharding_test.SimpleShardingTest.passing_test_6'
+        ], temp_file_name,
+        opt_test_filter='(Test1|passing_test_2|passing_test_6)',
+        opt_filter_tests_after_sharding=True)
+    finally:
+      os.remove(temp_file_name)
+
+  def testMedianComputation(self):
+    self.assertEquals(2.0, run_browser_tests._MedianTestTime(
+      {'test1': 2.0, 'test2': 7.0, 'test3': 1.0}))
+    self.assertEquals(2.0, run_browser_tests._MedianTestTime(
+      {'test1': 2.0}))
+    self.assertEquals(0.0, run_browser_tests._MedianTestTime({}))
+    self.assertEqual(4.0, run_browser_tests._MedianTestTime(
+      {'test1': 2.0, 'test2': 6.0, 'test3': 1.0, 'test4': 8.0}))
+
+
+class Algebra(
+    serially_executed_browser_test_case.SeriallyExecutedBrowserTestCase):
+
+  @classmethod
+  def GenerateTestCases_Simple(cls, options):
+    del options  # Unused.
+    yield 'testOne', (1, 2)
+    yield 'testTwo', (3, 3)
+
+  def Simple(self, x, y):
+    self.assertEquals(x, y)
+
+  def TestNumber(self):
+    self.assertEquals(0, 1)
+
+
+class ErrorneousGeometric(
+    serially_executed_browser_test_case.SeriallyExecutedBrowserTestCase):
+
+  @classmethod
+  def GenerateTestCases_Compare(cls, options):
+    del options  # Unused.
+    assert False, 'I am a problematic generator'
+    yield 'testBasic', ('square', 'circle')
+
+  def Compare(self, x, y):
+    self.assertEquals(x, y)
+
+  def TestAngle(self):
+    self.assertEquals(90, 450)
+
+class TestLoadAllTestModules(unittest.TestCase):
+  def testLoadAllTestsInModule(self):
+    context = browser_test_context.TypTestContext()
+    context.finder_options = options_for_unittests.GetCopy()
+    context.test_class = Algebra
+    context.test_case_ids_to_run.add(
+      'telemetry.testing.browser_test_runner_unittest.Algebra.TestNumber')
+    context.test_case_ids_to_run.add(
+      'telemetry.testing.browser_test_runner_unittest.Algebra.testOne')
+    context.Freeze()
+    browser_test_context._global_test_context = context
+    try:
+      # This should not invoke GenerateTestCases of ErrorneousGeometric class,
+      # otherwise that would throw Exception.
+      tests = serially_executed_browser_test_case.LoadAllTestsInModule(
+          sys.modules[__name__])
+      self.assertEquals(sorted([t.id() for t in tests]),
+          ['telemetry.testing.browser_test_runner_unittest.Algebra.TestNumber',
+           'telemetry.testing.browser_test_runner_unittest.Algebra.testOne'])
+    finally:
+      browser_test_context._global_test_context = None
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/decorators_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/decorators_unittest.py
new file mode 100644
index 0000000..f27d36c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/decorators_unittest.py
@@ -0,0 +1,58 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from telemetry import decorators
+
+_counter = 0
+
+
+class Foo(object):
+  @decorators.Cache
+  def GetCountCached(self, _):
+    global _counter
+    _counter = _counter + 1
+    return _counter
+
+
+def CreateFooUncached(_):
+  return Foo()
+
+
+@decorators.Cache
+def CreateFooCached(_):
+  return Foo()
+
+
+class DecoratorsUnitTest(unittest.TestCase):
+  # pylint: disable=blacklisted-name
+
+  def testCacheDecorator(self):
+    self.assertNotEquals(CreateFooUncached(1), CreateFooUncached(2))
+    self.assertNotEquals(CreateFooCached(1), CreateFooCached(2))
+
+    self.assertNotEquals(CreateFooUncached(1), CreateFooUncached(1))
+    self.assertEquals(CreateFooCached(1), CreateFooCached(1))
+
+  def testCacheableMemberCachesOnlyForSameArgs(self):
+    foo = Foo()
+    value_of_one = foo.GetCountCached(1)
+
+    self.assertEquals(value_of_one, foo.GetCountCached(1))
+    self.assertNotEquals(value_of_one, foo.GetCountCached(2))
+
+  def testCacheableMemberHasSeparateCachesForSiblingInstances(self):
+    foo = Foo()
+    sibling_foo = Foo()
+
+    self.assertNotEquals(foo.GetCountCached(1), sibling_foo.GetCountCached(1))
+
+  def testCacheableMemberHasSeparateCachesForNextGenerationInstances(self):
+    foo = Foo()
+    last_generation_count = foo.GetCountCached(1)
+    foo = None
+    foo = Foo()
+
+    self.assertNotEquals(last_generation_count, foo.GetCountCached(1))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/disabled_cases.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/disabled_cases.py
new file mode 100644
index 0000000..bb4641a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/disabled_cases.py
@@ -0,0 +1,63 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from telemetry import decorators
+
+
+# These are not real unittests.
+# They are merely to test our Enable/Disable annotations.
+class DisabledCases(unittest.TestCase):
+
+  def testAllEnabled(self):
+    pass
+
+  @decorators.Disabled('all')
+  def testAllDisabled(self):
+    pass
+
+  @decorators.Enabled('mavericks')
+  def testMavericksOnly(self):
+    pass
+
+  @decorators.Disabled('mavericks')
+  def testNoMavericks(self):
+    pass
+
+  @decorators.Enabled('mac')
+  def testMacOnly(self):
+    pass
+
+  @decorators.Disabled('mac')
+  def testNoMac(self):
+    pass
+
+  @decorators.Enabled('chromeos')
+  def testChromeOSOnly(self):
+    pass
+
+  @decorators.Disabled('chromeos')
+  def testNoChromeOS(self):
+    pass
+
+  @decorators.Enabled('win', 'linux')
+  def testWinOrLinuxOnly(self):
+    pass
+
+  @decorators.Disabled('win', 'linux')
+  def testNoWinLinux(self):
+    pass
+
+  @decorators.Enabled('system')
+  def testSystemOnly(self):
+    pass
+
+  @decorators.Disabled('system')
+  def testNoSystem(self):
+    pass
+
+  @decorators.Enabled('has tabs')
+  def testHasTabs(self):
+    pass
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/fakes/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/fakes/__init__.py
new file mode 100644
index 0000000..97c6360
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/fakes/__init__.py
@@ -0,0 +1,539 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Provides fakes for several of Telemetry's internal objects.
+
+These allow code like story_runner and Benchmark to be run and tested
+without compiling or starting a browser. Class names prepended with an
+underscore are intended to be implementation details, and should not
+be subclassed; however, some, like _FakeBrowser, have public APIs that
+may need to be called in tests.
+"""
+from telemetry.internal.backends.chrome_inspector import websocket
+from telemetry.internal.browser import browser_options
+from telemetry.internal.platform import system_info
+from telemetry.page import shared_page_state
+from telemetry.util import image_util
+from telemetry.testing.internal import fake_gpu_info
+from types import ModuleType
+
+
+# Classes and functions which are intended to be part of the public
+# fakes API.
+
+class FakePlatform(object):
+  def __init__(self):
+    self._network_controller = None
+    self._tracing_controller = None
+
+  @property
+  def is_host_platform(self):
+    raise NotImplementedError
+
+  @property
+  def network_controller(self):
+    if self._network_controller is None:
+      self._network_controller = _FakeNetworkController()
+    return  self._network_controller
+
+  @property
+  def tracing_controller(self):
+    if self._tracing_controller is None:
+      self._tracing_controller = _FakeTracingController()
+    return  self._tracing_controller
+
+  def Initialize(self):
+    pass
+
+  def CanMonitorThermalThrottling(self):
+    return False
+
+  def IsThermallyThrottled(self):
+    return False
+
+  def HasBeenThermallyThrottled(self):
+    return False
+
+  def GetDeviceTypeName(self):
+    return 'FakeDevice'
+
+  def GetArchName(self):
+    raise NotImplementedError
+
+  def GetOSName(self):
+    return 'FakeOS'
+
+  def GetOSVersionName(self):
+    raise NotImplementedError
+
+  def StopAllLocalServers(self):
+    pass
+
+  def WaitForTemperature(self, _):
+    pass
+
+
+class FakeLinuxPlatform(FakePlatform):
+  def __init__(self):
+    super(FakeLinuxPlatform, self).__init__()
+    self.screenshot_png_data = None
+    self.http_server_directories = []
+    self.http_server = FakeHTTPServer()
+
+  @property
+  def is_host_platform(self):
+    return True
+
+  def GetDeviceTypeName(self):
+    return 'Desktop'
+
+  def GetArchName(self):
+    return 'x86_64'
+
+  def GetOSName(self):
+    return 'linux'
+
+  def GetOSVersionName(self):
+    return 'trusty'
+
+  def CanTakeScreenshot(self):
+    return bool(self.screenshot_png_data)
+
+  def TakeScreenshot(self, file_path):
+    if not self.CanTakeScreenshot():
+      raise NotImplementedError
+    img = image_util.FromBase64Png(self.screenshot_png_data)
+    image_util.WritePngFile(img, file_path)
+    return True
+
+  def SetHTTPServerDirectories(self, paths):
+    self.http_server_directories.append(paths)
+
+
+class FakeHTTPServer(object):
+  def UrlOf(self, url):
+    del url  # unused
+    return 'file:///foo'
+
+
+class FakePossibleBrowser(object):
+  def __init__(self, execute_on_startup=None,
+               execute_after_browser_creation=None):
+    self._returned_browser = _FakeBrowser(FakeLinuxPlatform())
+    self.browser_type = 'linux'
+    self.supports_tab_control = False
+    self.is_remote = False
+    self.execute_on_startup = execute_on_startup
+    self.execute_after_browser_creation = execute_after_browser_creation
+
+  @property
+  def returned_browser(self):
+    """The browser object that will be returned through later API calls."""
+    return self._returned_browser
+
+  def Create(self, finder_options):
+    if self.execute_on_startup is not None:
+      self.execute_on_startup()
+    del finder_options  # unused
+    if self.execute_after_browser_creation is not None:
+      self.execute_after_browser_creation(self._returned_browser)
+    return self.returned_browser
+
+  @property
+  def platform(self):
+    """The platform object from the returned browser.
+
+    To change this or set it up, change the returned browser's
+    platform.
+    """
+    return self.returned_browser.platform
+
+  def IsRemote(self):
+    return self.is_remote
+
+  def SetCredentialsPath(self, _):
+    pass
+
+
+class FakeSharedPageState(shared_page_state.SharedPageState):
+  def __init__(self, test, finder_options, story_set):
+    super(FakeSharedPageState, self).__init__(test, finder_options, story_set)
+
+  def _GetPossibleBrowser(self, test, finder_options):
+    p = FakePossibleBrowser()
+    self.ConfigurePossibleBrowser(p)
+    return p
+
+  def ConfigurePossibleBrowser(self, possible_browser):
+    """Override this to configure the PossibleBrowser.
+
+    Can make changes to the browser's configuration here via e.g.:
+       possible_browser.returned_browser.returned_system_info = ...
+    """
+    pass
+
+
+  def DidRunStory(self, results):
+    # TODO(kbr): add a test which throws an exception from DidRunStory
+    # to verify the fix from https://crrev.com/86984d5fc56ce00e7b37ebe .
+    super(FakeSharedPageState, self).DidRunStory(results)
+
+
+class FakeSystemInfo(system_info.SystemInfo):
+  def __init__(self, model_name='', gpu_dict=None):
+    if gpu_dict == None:
+      gpu_dict = fake_gpu_info.FAKE_GPU_INFO
+    super(FakeSystemInfo, self).__init__(model_name, gpu_dict)
+
+
+class _FakeBrowserFinderOptions(browser_options.BrowserFinderOptions):
+  def __init__(self, execute_on_startup=None,
+               execute_after_browser_creation=None, *args, **kwargs):
+    browser_options.BrowserFinderOptions.__init__(self, *args, **kwargs)
+    self.fake_possible_browser = \
+      FakePossibleBrowser(
+        execute_on_startup=execute_on_startup,
+        execute_after_browser_creation=execute_after_browser_creation)
+
+def CreateBrowserFinderOptions(browser_type=None, execute_on_startup=None,
+                               execute_after_browser_creation=None):
+  """Creates fake browser finder options for discovering a browser."""
+  return _FakeBrowserFinderOptions(
+    browser_type=browser_type,
+    execute_on_startup=execute_on_startup,
+    execute_after_browser_creation=execute_after_browser_creation)
+
+
+# Internal classes. Note that end users may still need to both call
+# and mock out methods of these classes, but they should not be
+# subclassed.
+
+class _FakeBrowser(object):
+  def __init__(self, platform):
+    self._tabs = _FakeTabList(self)
+    # Fake the creation of the first tab.
+    self._tabs.New()
+    self._returned_system_info = FakeSystemInfo()
+    self._platform = platform
+    self._browser_type = 'release'
+    self._is_crashed = False
+
+  @property
+  def platform(self):
+    return self._platform
+
+  @platform.setter
+  def platform(self, incoming):
+    """Allows overriding of the fake browser's platform object."""
+    assert isinstance(incoming, FakePlatform)
+    self._platform = incoming
+
+  @property
+  def returned_system_info(self):
+    """The object which will be returned from calls to GetSystemInfo."""
+    return self._returned_system_info
+
+  @returned_system_info.setter
+  def returned_system_info(self, incoming):
+    """Allows overriding of the returned SystemInfo object.
+
+    Incoming argument must be an instance of FakeSystemInfo."""
+    assert isinstance(incoming, FakeSystemInfo)
+    self._returned_system_info = incoming
+
+  @property
+  def browser_type(self):
+    """The browser_type this browser claims to be ('debug', 'release', etc.)"""
+    return self._browser_type
+
+  @browser_type.setter
+  def browser_type(self, incoming):
+    """Allows setting of the browser_type."""
+    self._browser_type = incoming
+
+  @property
+  def credentials(self):
+    return _FakeCredentials()
+
+  def Close(self):
+    self._is_crashed = False
+
+  @property
+  def supports_system_info(self):
+    return True
+
+  def GetSystemInfo(self):
+    return self.returned_system_info
+
+  @property
+  def supports_tab_control(self):
+    return True
+
+  @property
+  def tabs(self):
+    return self._tabs
+
+  def DumpStateUponFailure(self):
+    pass
+
+
+class _FakeCredentials(object):
+  def WarnIfMissingCredentials(self, _):
+    pass
+
+
+class _FakeTracingController(object):
+  def __init__(self):
+    self._is_tracing = False
+
+  def StartTracing(self, tracing_config, timeout=10):
+    self._is_tracing = True
+    del tracing_config
+    del timeout
+
+  def StopTracing(self):
+    self._is_tracing = False
+
+  @property
+  def is_tracing_running(self):
+    return self._is_tracing
+
+  def ClearStateIfNeeded(self):
+    pass
+
+  def IsChromeTracingSupported(self):
+    return True
+
+
+class _FakeNetworkController(object):
+  def __init__(self):
+    self.wpr_mode = None
+    self.extra_wpr_args = None
+    self.is_initialized = False
+    self.is_open = False
+    self.use_live_traffic = None
+
+  def InitializeIfNeeded(self, use_live_traffic=False):
+    self.use_live_traffic = use_live_traffic
+
+  def UpdateTrafficSettings(self, round_trip_latency_ms=None,
+      download_bandwidth_kbps=None, upload_bandwidth_kbps=None):
+    pass
+
+  def Open(self, wpr_mode, extra_wpr_args):
+    self.wpr_mode = wpr_mode
+    self.extra_wpr_args = extra_wpr_args
+    self.is_open = True
+
+  def Close(self):
+    self.wpr_mode = None
+    self.extra_wpr_args = None
+    self.is_initialized = False
+    self.is_open = False
+
+  def StartReplay(self, archive_path, make_javascript_deterministic=False):
+    del make_javascript_deterministic  # Unused.
+    assert self.is_open
+    self.is_initialized = archive_path is not None
+
+  def StopReplay(self):
+    self.is_initialized = False
+
+
+class _FakeTab(object):
+  def __init__(self, browser, tab_id):
+    self._browser = browser
+    self._tab_id = str(tab_id)
+    self._collect_garbage_count = 0
+    self.test_png = None
+
+  @property
+  def collect_garbage_count(self):
+    return self._collect_garbage_count
+
+  @property
+  def id(self):
+    return self._tab_id
+
+  @property
+  def browser(self):
+    return self._browser
+
+  def WaitForDocumentReadyStateToBeComplete(self, timeout=0):
+    pass
+
+  def Navigate(self, url, script_to_evaluate_on_commit=None,
+               timeout=0):
+    del script_to_evaluate_on_commit, timeout # unused
+    if url == 'chrome://crash':
+      self.browser._is_crashed = True
+      raise Exception
+
+  def WaitForDocumentReadyStateToBeInteractiveOrBetter(self, timeout=0):
+    pass
+
+  def WaitForFrameToBeDisplayed(self, timeout=0):
+    pass
+
+  def IsAlive(self):
+    return True
+
+  def CloseConnections(self):
+    pass
+
+  def CollectGarbage(self):
+    self._collect_garbage_count += 1
+
+  def Close(self):
+    pass
+
+  @property
+  def screenshot_supported(self):
+    return self.test_png is not None
+
+  def Screenshot(self):
+    assert self.screenshot_supported, 'Screenshot is not supported'
+    return image_util.FromBase64Png(self.test_png)
+
+
+class _FakeTabList(object):
+  _current_tab_id = 0
+
+  def __init__(self, browser):
+    self._tabs = []
+    self._browser = browser
+
+  def New(self, timeout=300):
+    del timeout  # unused
+    type(self)._current_tab_id += 1
+    t = _FakeTab(self._browser, type(self)._current_tab_id)
+    self._tabs.append(t)
+    return t
+
+  def __iter__(self):
+    return self._tabs.__iter__()
+
+  def __len__(self):
+    return len(self._tabs)
+
+  def __getitem__(self, index):
+    if self._tabs[index].browser._is_crashed:
+      raise Exception
+    else:
+      return self._tabs[index]
+
+  def GetTabById(self, identifier):
+    """The identifier of a tab can be accessed with tab.id."""
+    for tab in self._tabs:
+      if tab.id == identifier:
+        return tab
+    return None
+
+
+class FakeInspectorWebsocket(object):
+  _NOTIFICATION_EVENT = 1
+  _NOTIFICATION_CALLBACK = 2
+
+  """A fake InspectorWebsocket.
+
+  A fake that allows tests to send pregenerated data. Normal
+  InspectorWebsockets allow for any number of domain handlers. This fake only
+  allows up to 1 domain handler, and assumes that the domain of the response
+  always matches that of the handler.
+  """
+  def __init__(self, mock_timer):
+    self._mock_timer = mock_timer
+    self._notifications = []
+    self._response_handlers = {}
+    self._pending_callbacks = {}
+    self._handler = None
+
+  def RegisterDomain(self, _, handler):
+    self._handler = handler
+
+  def AddEvent(self, method, params, time):
+    if self._notifications:
+      assert self._notifications[-1][1] < time, (
+          'Current response is scheduled earlier than previous response.')
+    response = {'method': method, 'params': params}
+    self._notifications.append((response, time, self._NOTIFICATION_EVENT))
+
+  def AddAsyncResponse(self, method, result, time):
+    if self._notifications:
+      assert self._notifications[-1][1] < time, (
+          'Current response is scheduled earlier than previous response.')
+    response = {'method': method, 'result': result}
+    self._notifications.append((response, time, self._NOTIFICATION_CALLBACK))
+
+  def AddResponseHandler(self, method, handler):
+    self._response_handlers[method] = handler
+
+  def SyncRequest(self, request, *args, **kwargs):
+    del args, kwargs  # unused
+    handler = self._response_handlers[request['method']]
+    return handler(request) if handler else None
+
+  def AsyncRequest(self, request, callback):
+    self._pending_callbacks.setdefault(request['method'], []).append(callback)
+
+  def SendAndIgnoreResponse(self, request):
+    pass
+
+  def Connect(self, _):
+    pass
+
+  def DispatchNotifications(self, timeout):
+    current_time = self._mock_timer.time()
+    if not self._notifications:
+      self._mock_timer.SetTime(current_time + timeout + 1)
+      raise websocket.WebSocketTimeoutException()
+
+    response, time, kind = self._notifications[0]
+    if time - current_time > timeout:
+      self._mock_timer.SetTime(current_time + timeout + 1)
+      raise websocket.WebSocketTimeoutException()
+
+    self._notifications.pop(0)
+    self._mock_timer.SetTime(time + 1)
+    if kind == self._NOTIFICATION_EVENT:
+      self._handler(response)
+    elif kind == self._NOTIFICATION_CALLBACK:
+      callback = self._pending_callbacks.get(response['method']).pop(0)
+      callback(response)
+    else:
+      raise Exception('Unexpected response type')
+
+
+class FakeTimer(object):
+  """ A fake timer to fake out the timing for a module.
+    Args:
+      module: module to fake out the time
+  """
+  def __init__(self, module=None):
+    self._elapsed_time = 0
+    self._module = module
+    self._actual_time = None
+    if module:
+      assert isinstance(module, ModuleType)
+      self._actual_time = module.time
+      self._module.time = self
+
+  def sleep(self, time):
+    self._elapsed_time += time
+
+  def time(self):
+    return self._elapsed_time
+
+  def SetTime(self, time):
+    self._elapsed_time = time
+
+  def __del__(self):
+    self.Restore()
+
+  def Restore(self):
+    if self._module:
+      self._module.time = self._actual_time
+      self._module = None
+      self._actual_time = None
+
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/internal/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/internal/__init__.py
new file mode 100644
index 0000000..50b23df
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/internal/__init__.py
@@ -0,0 +1,3 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/internal/fake_gpu_info.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/internal/fake_gpu_info.py
new file mode 100644
index 0000000..fe74b07
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/internal/fake_gpu_info.py
@@ -0,0 +1,241 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# This dictionary of GPU information was captured from a run of
+# Telemetry on a Linux workstation with NVIDIA GPU. It helps test
+# telemetry.internal.platform's GPUInfo class, and specifically the
+# attributes it expects to find in the dictionary; if the code changes
+# in an incompatible way, tests using this fake GPU info will begin
+# failing, indicating this fake data must be updated.
+#
+# To regenerate it, import pdb in
+# telemetry/internal/platform/gpu_info.py and add a call to
+# pdb.set_trace() in GPUInfo.FromDict before the return statement.
+# Print the attrs dictionary in the debugger and copy/paste the result
+# on the right-hand side of this assignment. Then run:
+#
+#   pyformat [this file name] | sed -e "s/'/'/g"
+#
+# and put the output into this file.
+
+FAKE_GPU_INFO = {
+    'feature_status':
+        {
+            'flash_stage3d': 'enabled',
+            'gpu_compositing': 'enabled',
+            'video_decode': 'unavailable_software',
+            'flash_3d': 'enabled',
+            'webgl': 'enabled',
+            'video_encode': 'enabled',
+            'multiple_raster_threads': 'enabled_on',
+            '2d_canvas': 'unavailable_software',
+            'rasterization': 'disabled_software',
+            'flash_stage3d_baseline': 'enabled'
+        },
+    'aux_attributes':
+        {
+            'optimus': False,
+            'sandboxed': True,
+            'basic_info_state': 1,
+            'adapter_luid': 0.0,
+            'driver_version': '331.79',
+            'direct_rendering': True,
+            'amd_switchable': False,
+            'context_info_state': 1,
+            'process_crash_count': 0,
+            'pixel_shader_version': '4.40',
+            'gl_ws_version': '1.4',
+            'can_lose_context': False,
+            'driver_vendor': 'NVIDIA',
+            'max_msaa_samples': '64',
+            'software_rendering': False,
+            'gl_version': '4.4.0 NVIDIA 331.79',
+            'gl_ws_vendor': 'NVIDIA Corporation',
+            'vertex_shader_version': '4.40',
+            'initialization_time': 1.284043,
+            'gl_reset_notification_strategy': 33362,
+            'gl_ws_extensions':
+                'GLX_EXT_visual_info GLX_EXT_visual_rating GLX_SGIX_fbconfig '
+                'GLX_SGIX_pbuffer GLX_SGI_video_sync GLX_SGI_swap_control '
+                'GLX_EXT_swap_control GLX_EXT_swap_control_tear '
+                'GLX_EXT_texture_from_pixmap GLX_EXT_buffer_age '
+                'GLX_ARB_create_context GLX_ARB_create_context_profile '
+                'GLX_EXT_create_context_es_profile '
+                'GLX_EXT_create_context_es2_profile '
+                'GLX_ARB_create_context_robustness GLX_ARB_multisample '
+                'GLX_NV_float_buffer GLX_ARB_fbconfig_float GLX_NV_swap_group'
+                ' GLX_EXT_framebuffer_sRGB GLX_NV_multisample_coverage '
+                'GLX_NV_copy_image GLX_NV_video_capture ',
+            'gl_renderer': 'Quadro 600/PCIe/SSE2',
+            'driver_date': '',
+            'gl_vendor': 'NVIDIA Corporation',
+            'gl_extensions':
+                'GL_AMD_multi_draw_indirect GL_ARB_arrays_of_arrays '
+                'GL_ARB_base_instance GL_ARB_blend_func_extended '
+                'GL_ARB_buffer_storage GL_ARB_clear_buffer_object '
+                'GL_ARB_clear_texture GL_ARB_color_buffer_float '
+                'GL_ARB_compatibility GL_ARB_compressed_texture_pixel_storage'
+                ' GL_ARB_conservative_depth GL_ARB_compute_shader '
+                'GL_ARB_compute_variable_group_size GL_ARB_copy_buffer '
+                'GL_ARB_copy_image GL_ARB_debug_output '
+                'GL_ARB_depth_buffer_float GL_ARB_depth_clamp '
+                'GL_ARB_depth_texture GL_ARB_draw_buffers '
+                'GL_ARB_draw_buffers_blend GL_ARB_draw_indirect '
+                'GL_ARB_draw_elements_base_vertex GL_ARB_draw_instanced '
+                'GL_ARB_enhanced_layouts GL_ARB_ES2_compatibility '
+                'GL_ARB_ES3_compatibility GL_ARB_explicit_attrib_location '
+                'GL_ARB_explicit_uniform_location '
+                'GL_ARB_fragment_coord_conventions '
+                'GL_ARB_fragment_layer_viewport GL_ARB_fragment_program '
+                'GL_ARB_fragment_program_shadow GL_ARB_fragment_shader '
+                'GL_ARB_framebuffer_no_attachments GL_ARB_framebuffer_object '
+                'GL_ARB_framebuffer_sRGB GL_ARB_geometry_shader4 '
+                'GL_ARB_get_program_binary GL_ARB_gpu_shader5 '
+                'GL_ARB_gpu_shader_fp64 GL_ARB_half_float_pixel '
+                'GL_ARB_half_float_vertex GL_ARB_imaging '
+                'GL_ARB_indirect_parameters GL_ARB_instanced_arrays '
+                'GL_ARB_internalformat_query GL_ARB_internalformat_query2 '
+                'GL_ARB_invalidate_subdata GL_ARB_map_buffer_alignment '
+                'GL_ARB_map_buffer_range GL_ARB_multi_bind '
+                'GL_ARB_multi_draw_indirect GL_ARB_multisample '
+                'GL_ARB_multitexture GL_ARB_occlusion_query '
+                'GL_ARB_occlusion_query2 GL_ARB_pixel_buffer_object '
+                'GL_ARB_point_parameters GL_ARB_point_sprite '
+                'GL_ARB_program_interface_query GL_ARB_provoking_vertex '
+                'GL_ARB_robust_buffer_access_behavior GL_ARB_robustness '
+                'GL_ARB_sample_shading GL_ARB_sampler_objects '
+                'GL_ARB_seamless_cube_map GL_ARB_separate_shader_objects '
+                'GL_ARB_shader_atomic_counters GL_ARB_shader_bit_encoding '
+                'GL_ARB_shader_draw_parameters GL_ARB_shader_group_vote '
+                'GL_ARB_shader_image_load_store GL_ARB_shader_image_size '
+                'GL_ARB_shader_objects GL_ARB_shader_precision '
+                'GL_ARB_query_buffer_object '
+                'GL_ARB_shader_storage_buffer_object GL_ARB_shader_subroutine'
+                ' GL_ARB_shader_texture_lod GL_ARB_shading_language_100 '
+                'GL_ARB_shading_language_420pack '
+                'GL_ARB_shading_language_include '
+                'GL_ARB_shading_language_packing GL_ARB_shadow '
+                'GL_ARB_stencil_texturing GL_ARB_sync '
+                'GL_ARB_tessellation_shader GL_ARB_texture_border_clamp '
+                'GL_ARB_texture_buffer_object '
+                'GL_ARB_texture_buffer_object_rgb32 '
+                'GL_ARB_texture_buffer_range GL_ARB_texture_compression '
+                'GL_ARB_texture_compression_bptc '
+                'GL_ARB_texture_compression_rgtc GL_ARB_texture_cube_map '
+                'GL_ARB_texture_cube_map_array GL_ARB_texture_env_add '
+                'GL_ARB_texture_env_combine GL_ARB_texture_env_crossbar '
+                'GL_ARB_texture_env_dot3 GL_ARB_texture_float '
+                'GL_ARB_texture_gather GL_ARB_texture_mirror_clamp_to_edge '
+                'GL_ARB_texture_mirrored_repeat GL_ARB_texture_multisample '
+                'GL_ARB_texture_non_power_of_two GL_ARB_texture_query_levels '
+                'GL_ARB_texture_query_lod GL_ARB_texture_rectangle '
+                'GL_ARB_texture_rg GL_ARB_texture_rgb10_a2ui '
+                'GL_ARB_texture_stencil8 GL_ARB_texture_storage '
+                'GL_ARB_texture_storage_multisample GL_ARB_texture_swizzle '
+                'GL_ARB_texture_view GL_ARB_timer_query '
+                'GL_ARB_transform_feedback2 GL_ARB_transform_feedback3 '
+                'GL_ARB_transform_feedback_instanced GL_ARB_transpose_matrix '
+                'GL_ARB_uniform_buffer_object GL_ARB_vertex_array_bgra '
+                'GL_ARB_vertex_array_object GL_ARB_vertex_attrib_64bit '
+                'GL_ARB_vertex_attrib_binding GL_ARB_vertex_buffer_object '
+                'GL_ARB_vertex_program GL_ARB_vertex_shader '
+                'GL_ARB_vertex_type_10f_11f_11f_rev '
+                'GL_ARB_vertex_type_2_10_10_10_rev GL_ARB_viewport_array '
+                'GL_ARB_window_pos GL_ATI_draw_buffers GL_ATI_texture_float '
+                'GL_ATI_texture_mirror_once GL_S3_s3tc GL_EXT_texture_env_add'
+                ' GL_EXT_abgr GL_EXT_bgra GL_EXT_bindable_uniform '
+                'GL_EXT_blend_color GL_EXT_blend_equation_separate '
+                'GL_EXT_blend_func_separate GL_EXT_blend_minmax '
+                'GL_EXT_blend_subtract GL_EXT_compiled_vertex_array '
+                'GL_EXT_Cg_shader GL_EXT_depth_bounds_test '
+                'GL_EXT_direct_state_access GL_EXT_draw_buffers2 '
+                'GL_EXT_draw_instanced GL_EXT_draw_range_elements '
+                'GL_EXT_fog_coord GL_EXT_framebuffer_blit '
+                'GL_EXT_framebuffer_multisample '
+                'GL_EXTX_framebuffer_mixed_formats '
+                'GL_EXT_framebuffer_multisample_blit_scaled '
+                'GL_EXT_framebuffer_object GL_EXT_framebuffer_sRGB '
+                'GL_EXT_geometry_shader4 GL_EXT_gpu_program_parameters '
+                'GL_EXT_gpu_shader4 GL_EXT_multi_draw_arrays '
+                'GL_EXT_packed_depth_stencil GL_EXT_packed_float '
+                'GL_EXT_packed_pixels GL_EXT_pixel_buffer_object '
+                'GL_EXT_point_parameters GL_EXT_provoking_vertex '
+                'GL_EXT_rescale_normal GL_EXT_secondary_color '
+                'GL_EXT_separate_shader_objects '
+                'GL_EXT_separate_specular_color '
+                'GL_EXT_shader_image_load_store GL_EXT_shadow_funcs '
+                'GL_EXT_stencil_two_side GL_EXT_stencil_wrap GL_EXT_texture3D'
+                ' GL_EXT_texture_array GL_EXT_texture_buffer_object '
+                'GL_EXT_texture_compression_dxt1 '
+                'GL_EXT_texture_compression_latc '
+                'GL_EXT_texture_compression_rgtc '
+                'GL_EXT_texture_compression_s3tc GL_EXT_texture_cube_map '
+                'GL_EXT_texture_edge_clamp GL_EXT_texture_env_combine '
+                'GL_EXT_texture_env_dot3 GL_EXT_texture_filter_anisotropic '
+                'GL_EXT_texture_integer GL_EXT_texture_lod '
+                'GL_EXT_texture_lod_bias GL_EXT_texture_mirror_clamp '
+                'GL_EXT_texture_object GL_EXT_texture_shared_exponent '
+                'GL_EXT_texture_sRGB GL_EXT_texture_sRGB_decode '
+                'GL_EXT_texture_storage GL_EXT_texture_swizzle '
+                'GL_EXT_timer_query GL_EXT_transform_feedback2 '
+                'GL_EXT_vertex_array GL_EXT_vertex_array_bgra '
+                'GL_EXT_vertex_attrib_64bit GL_EXT_x11_sync_object '
+                'GL_EXT_import_sync_object GL_IBM_rasterpos_clip '
+                'GL_IBM_texture_mirrored_repeat GL_KHR_debug '
+                'GL_KTX_buffer_region GL_NV_bindless_multi_draw_indirect '
+                'GL_NV_blend_equation_advanced GL_NV_blend_square '
+                'GL_NV_compute_program5 GL_NV_conditional_render '
+                'GL_NV_copy_depth_to_color GL_NV_copy_image '
+                'GL_NV_depth_buffer_float GL_NV_depth_clamp '
+                'GL_NV_draw_texture GL_NV_ES1_1_compatibility '
+                'GL_NV_explicit_multisample GL_NV_fence GL_NV_float_buffer '
+                'GL_NV_fog_distance GL_NV_fragment_program '
+                'GL_NV_fragment_program_option GL_NV_fragment_program2 '
+                'GL_NV_framebuffer_multisample_coverage '
+                'GL_NV_geometry_shader4 GL_NV_gpu_program4 '
+                'GL_NV_gpu_program4_1 GL_NV_gpu_program5 '
+                'GL_NV_gpu_program5_mem_extended GL_NV_gpu_program_fp64 '
+                'GL_NV_gpu_shader5 GL_NV_half_float GL_NV_light_max_exponent '
+                'GL_NV_multisample_coverage GL_NV_multisample_filter_hint '
+                'GL_NV_occlusion_query GL_NV_packed_depth_stencil '
+                'GL_NV_parameter_buffer_object GL_NV_parameter_buffer_object2'
+                ' GL_NV_path_rendering GL_NV_pixel_data_range '
+                'GL_NV_point_sprite GL_NV_primitive_restart '
+                'GL_NV_register_combiners GL_NV_register_combiners2 '
+                'GL_NV_shader_atomic_counters GL_NV_shader_atomic_float '
+                'GL_NV_shader_buffer_load GL_NV_shader_storage_buffer_object '
+                'GL_ARB_sparse_texture GL_NV_texgen_reflection '
+                'GL_NV_texture_barrier GL_NV_texture_compression_vtc '
+                'GL_NV_texture_env_combine4 GL_NV_texture_expand_normal '
+                'GL_NV_texture_multisample GL_NV_texture_rectangle '
+                'GL_NV_texture_shader GL_NV_texture_shader2 '
+                'GL_NV_texture_shader3 GL_NV_transform_feedback '
+                'GL_NV_transform_feedback2 GL_NV_vdpau_interop '
+                'GL_NV_vertex_array_range GL_NV_vertex_array_range2 '
+                'GL_NV_vertex_attrib_integer_64bit '
+                'GL_NV_vertex_buffer_unified_memory GL_NV_vertex_program '
+                'GL_NV_vertex_program1_1 GL_NV_vertex_program2 '
+                'GL_NV_vertex_program2_option GL_NV_vertex_program3 '
+                'GL_NVX_conditional_render GL_NVX_gpu_memory_info '
+                'GL_SGIS_generate_mipmap GL_SGIS_texture_lod '
+                'GL_SGIX_depth_texture GL_SGIX_shadow GL_SUN_slice_accum '
+        },
+    'devices':
+        [
+            {
+                'device_string': '',
+                'vendor_id': 4318.0,
+                'device_id': 3576.0,
+                'vendor_string': ''
+            }],
+    'driver_bug_workarounds':
+        ['clear_uniforms_before_first_program_use',
+         'disable_gl_path_rendering',
+         'init_gl_position_in_vertex_shader',
+         'init_vertex_attributes',
+         'remove_pow_with_constant_exponent',
+         'scalarize_vec_and_mat_constructor_args',
+         'use_current_program_after_successful_link',
+         'use_virtualized_gl_contexts']
+}
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/options_for_unittests.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/options_for_unittests.py
new file mode 100644
index 0000000..1c47fbf
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/options_for_unittests.py
@@ -0,0 +1,32 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""This module provides the global variable options_for_unittests.
+
+This is set to a BrowserOptions object by the test harness, or None
+if unit tests are not running.
+
+This allows multiple unit tests to use a specific
+browser, in face of multiple options."""
+
+
+_options = []
+
+
+def Push(options):
+  _options.append(options)
+
+
+def Pop():
+  return _options.pop()
+
+
+def GetCopy():
+  if not AreSet():
+    return None
+  return _options[-1].Copy()
+
+
+def AreSet():
+  return bool(_options)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/page_test_test_case.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/page_test_test_case.py
new file mode 100644
index 0000000..d7e7296
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/page_test_test_case.py
@@ -0,0 +1,106 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Provide a TestCase base class for PageTest subclasses' unittests."""
+
+import unittest
+
+from telemetry import benchmark
+from telemetry import story
+from telemetry.core import exceptions
+from telemetry.core import util
+from telemetry.internal.results import results_options
+from telemetry.internal import story_runner
+from telemetry.page import page as page_module
+from telemetry.page import legacy_page_test
+from telemetry.testing import options_for_unittests
+
+
+class BasicTestPage(page_module.Page):
+  def __init__(self, url, story_set, base_dir):
+    super(BasicTestPage, self).__init__(url, story_set, base_dir)
+
+  def RunPageInteractions(self, action_runner):
+    with action_runner.CreateGestureInteraction('ScrollAction'):
+      action_runner.ScrollPage()
+
+
+class EmptyMetadataForTest(benchmark.BenchmarkMetadata):
+  def __init__(self):
+    super(EmptyMetadataForTest, self).__init__('')
+
+
+class PageTestTestCase(unittest.TestCase):
+  """A base class to simplify writing unit tests for PageTest subclasses."""
+
+  def CreateStorySetFromFileInUnittestDataDir(self, test_filename):
+    ps = self.CreateEmptyPageSet()
+    page = BasicTestPage('file://' + test_filename, ps, base_dir=ps.base_dir)
+    ps.AddStory(page)
+    return ps
+
+  def CreateEmptyPageSet(self):
+    base_dir = util.GetUnittestDataDir()
+    ps = story.StorySet(base_dir=base_dir)
+    return ps
+
+  def RunMeasurement(self, measurement, ps,
+      options=None):
+    """Runs a measurement against a pageset, returning the rows its outputs."""
+    if options is None:
+      options = options_for_unittests.GetCopy()
+    assert options
+    temp_parser = options.CreateParser()
+    story_runner.AddCommandLineArgs(temp_parser)
+    defaults = temp_parser.get_default_values()
+    for k, v in defaults.__dict__.items():
+      if hasattr(options, k):
+        continue
+      setattr(options, k, v)
+
+    if isinstance(measurement, legacy_page_test.LegacyPageTest):
+      measurement.CustomizeBrowserOptions(options.browser_options)
+    options.output_file = None
+    options.output_formats = ['none']
+    options.suppress_gtest_report = True
+    options.output_trace_tag = None
+    story_runner.ProcessCommandLineArgs(temp_parser, options)
+    results = results_options.CreateResults(EmptyMetadataForTest(), options)
+    story_runner.Run(measurement, ps, options, results)
+    return results
+
+  def TestTracingCleanedUp(self, measurement_class, options=None):
+    ps = self.CreateStorySetFromFileInUnittestDataDir('blank.html')
+    start_tracing_called = [False]
+    stop_tracing_called = [False]
+
+    class BuggyMeasurement(measurement_class):
+      def __init__(self, *args, **kwargs):
+        measurement_class.__init__(self, *args, **kwargs)
+
+      # Inject fake tracing methods to tracing_controller
+      def TabForPage(self, page, browser):
+        ActualStartTracing = browser.platform.tracing_controller.StartTracing
+        def FakeStartTracing(*args, **kwargs):
+          ActualStartTracing(*args, **kwargs)
+          start_tracing_called[0] = True
+          raise exceptions.IntentionalException
+        browser.StartTracing = FakeStartTracing
+
+        ActualStopTracing = browser.platform.tracing_controller.StopTracing
+        def FakeStopTracing(*args, **kwargs):
+          result = ActualStopTracing(*args, **kwargs)
+          stop_tracing_called[0] = True
+          return result
+        browser.platform.tracing_controller.StopTracing = FakeStopTracing
+
+        return measurement_class.TabForPage(self, page, browser)
+
+    measurement = BuggyMeasurement()
+    try:
+      self.RunMeasurement(measurement, ps, options=options)
+    except legacy_page_test.TestNotSupportedOnPlatformError:
+      pass
+    if start_tracing_called[0]:
+      self.assertTrue(stop_tracing_called[0])
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/progress_reporter.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/progress_reporter.py
new file mode 100644
index 0000000..036192b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/progress_reporter.py
@@ -0,0 +1,131 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+import sys
+
+from telemetry.internal.util import path
+from telemetry.testing import options_for_unittests
+
+
+class ProgressReporter(object):
+  def __init__(self, output_stream):
+    self._output_stream = output_stream
+
+  def StartTest(self, test):
+    pass
+
+  def StartTestSuite(self, suite):
+    pass
+
+  def StartTestRun(self):
+    pass
+
+  def StopTest(self, test):
+    pass
+
+  def StopTestSuite(self, suite):
+    pass
+
+  def StopTestRun(self, result):
+    pass
+
+  def Error(self, test, err):
+    pass
+
+  def Failure(self, test, err):
+    pass
+
+  def Success(self, test):
+    pass
+
+  def Skip(self, test, reason):
+    pass
+
+
+class TestSuite(unittest.TestSuite):
+  """TestSuite that can delegate start and stop calls to a TestResult object."""
+  def run(self, result):  # pylint: disable=arguments-differ
+    if hasattr(result, 'startTestSuite'):
+      result.startTestSuite(self)
+    result = super(TestSuite, self).run(result)
+    if hasattr(result, 'stopTestSuite'):
+      result.stopTestSuite(self)
+    return result
+
+
+class TestRunner(object):
+  def run(self, test, progress_reporters, repeat_count, args):
+    sys.path.append(path.GetUnittestDataDir())
+    result = TestResult(progress_reporters)
+    result.startTestRun()
+    try:
+      options_for_unittests.Push(args)
+      for _ in xrange(repeat_count):
+        test(result)
+    finally:
+      options_for_unittests.Pop()
+      result.stopTestRun()
+
+    return result
+
+
+class TestResult(unittest.TestResult):
+  def __init__(self, progress_reporters):
+    super(TestResult, self).__init__()
+    self.successes = []
+    self._progress_reporters = progress_reporters
+
+  @property
+  def failures_and_errors(self):
+    return self.failures + self.errors
+
+  def startTest(self, test):
+    super(TestResult, self).startTest(test)
+    for progress_reporter in self._progress_reporters:
+      progress_reporter.StartTest(test)
+
+  def startTestSuite(self, suite):
+    for progress_reporter in self._progress_reporters:
+      progress_reporter.StartTestSuite(suite)
+
+  def startTestRun(self):
+    super(TestResult, self).startTestRun()
+    for progress_reporter in self._progress_reporters:
+      progress_reporter.StartTestRun()
+
+  def stopTest(self, test):
+    super(TestResult, self).stopTest(test)
+    for progress_reporter in self._progress_reporters:
+      progress_reporter.StopTest(test)
+
+  def stopTestSuite(self, suite):
+    for progress_reporter in self._progress_reporters:
+      progress_reporter.StopTestSuite(suite)
+
+  def stopTestRun(self):
+    super(TestResult, self).stopTestRun()
+    for progress_reporter in self._progress_reporters:
+      progress_reporter.StopTestRun(self)
+
+  def addError(self, test, err):
+    super(TestResult, self).addError(test, err)
+    for progress_reporter in self._progress_reporters:
+      progress_reporter.Error(test, err)
+
+  def addFailure(self, test, err):
+    super(TestResult, self).addFailure(test, err)
+    for progress_reporter in self._progress_reporters:
+      progress_reporter.Failure(test, err)
+
+  def addSuccess(self, test):
+    super(TestResult, self).addSuccess(test)
+    self.successes.append(test)
+    for progress_reporter in self._progress_reporters:
+      progress_reporter.Success(test)
+
+  def addSkip(self, test, reason):
+    super(TestResult, self).addSkip(test, reason)
+    for progress_reporter in self._progress_reporters:
+      progress_reporter.Skip(test, reason)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/progress_reporter_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/progress_reporter_unittest.py
new file mode 100644
index 0000000..2a60882
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/progress_reporter_unittest.py
@@ -0,0 +1,54 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from telemetry.testing import progress_reporter
+
+
+class TestFoo(unittest.TestCase):
+  # Test method doesn't have test- prefix intentionally. This is so that
+  # run_test script won't run this test.
+  def RunPassingTest(self):
+    pass
+
+  def RunFailingTest(self):
+    self.fail('expected failure')
+
+
+class LoggingProgressReporter(object):
+  def __init__(self):
+    self._call_log = []
+
+  @property
+  def call_log(self):
+    return tuple(self._call_log)
+
+  def __getattr__(self, name):
+    def wrapper(*_):
+      self._call_log.append(name)
+    return wrapper
+
+
+class ProgressReporterTest(unittest.TestCase):
+  def testTestRunner(self):
+    suite = progress_reporter.TestSuite()
+    suite.addTest(TestFoo(methodName='RunPassingTest'))
+    suite.addTest(TestFoo(methodName='RunFailingTest'))
+
+    reporter = LoggingProgressReporter()
+    runner = progress_reporter.TestRunner()
+    progress_reporters = (reporter,)
+    result = runner.run(suite, progress_reporters, 1, None)
+
+    self.assertEqual(len(result.successes), 1)
+    self.assertEqual(len(result.failures), 1)
+    self.assertEqual(len(result.failures_and_errors), 1)
+    expected = (
+        'StartTestRun', 'StartTestSuite',
+        'StartTest', 'Success', 'StopTest',
+        'StartTest', 'Failure', 'StopTest',
+        'StopTestSuite', 'StopTestRun',
+    )
+    self.assertEqual(reporter.call_log, expected)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/run_browser_tests.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/run_browser_tests.py
new file mode 100644
index 0000000..d637116
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/run_browser_tests.py
@@ -0,0 +1,355 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import fnmatch
+import re
+import sys
+import json
+
+from telemetry.core import discover
+from telemetry.internal.browser import browser_options
+from telemetry.internal.platform import android_device
+from telemetry.internal.util import binary_manager
+from telemetry.testing import browser_test_context
+from telemetry.testing import serially_executed_browser_test_case
+
+import typ
+from typ import arg_parser
+
+TEST_SUFFIXES = ['*_test.py', '*_tests.py', '*_unittest.py', '*_unittests.py']
+
+
+def ProcessCommandLineOptions(test_class, typ_options, args):
+  options = browser_options.BrowserFinderOptions()
+  options.browser_type = 'any'
+  parser = options.CreateParser(test_class.__doc__)
+  test_class.AddCommandlineArgs(parser)
+  # Set the default chrome root variable. This is required for the
+  # Android browser finder to function properly.
+  if typ_options.default_chrome_root:
+    parser.set_defaults(chrome_root=typ_options.default_chrome_root)
+  finder_options, positional_args = parser.parse_args(args)
+  finder_options.positional_args = positional_args
+  # Typ parses the "verbose", or "-v", command line arguments which
+  # are supposed to control logging verbosity. Carry them over.
+  finder_options.verbosity = typ_options.verbose
+  return finder_options
+
+
+def _ValidateDistinctNames(browser_test_classes):
+  names_to_test_classes = {}
+  for cl in browser_test_classes:
+    name = cl.Name()
+    if name in names_to_test_classes:
+      raise Exception('Test name %s is duplicated between %s and %s' % (
+          name, repr(cl), repr(names_to_test_classes[name])))
+    names_to_test_classes[name] = cl
+
+
+def _TestRangeForShard(total_shards, shard_index, num_tests):
+  """Returns a 2-tuple containing the start (inclusive) and ending
+  (exclusive) indices of the tests that should be run, given that
+  |num_tests| tests are split across |total_shards| shards, and that
+  |shard_index| is currently being run.
+  """
+  assert num_tests >= 0
+  assert total_shards >= 1
+  assert shard_index >= 0 and shard_index < total_shards, (
+    'shard_index (%d) must be >= 0 and < total_shards (%d)' %
+    (shard_index, total_shards))
+  if num_tests == 0:
+    return (0, 0)
+  floored_tests_per_shard = num_tests // total_shards
+  remaining_tests = num_tests % total_shards
+  if remaining_tests == 0:
+    return (floored_tests_per_shard * shard_index,
+            floored_tests_per_shard * (1 + shard_index))
+  # More complicated. Some shards will run floored_tests_per_shard
+  # tests, and some will run 1 + floored_tests_per_shard.
+  num_earlier_shards_with_one_extra_test = min(remaining_tests, shard_index)
+  num_earlier_shards_with_no_extra_tests = max(
+    0, shard_index - num_earlier_shards_with_one_extra_test)
+  num_earlier_tests = (
+    num_earlier_shards_with_one_extra_test * (floored_tests_per_shard + 1) +
+    num_earlier_shards_with_no_extra_tests * floored_tests_per_shard)
+  tests_for_this_shard = floored_tests_per_shard
+  if shard_index < remaining_tests:
+    tests_for_this_shard += 1
+  return (num_earlier_tests, num_earlier_tests + tests_for_this_shard)
+
+
+def _MedianTestTime(test_times):
+  times = test_times.values()
+  times.sort()
+  if len(times) == 0:
+    return 0
+  halfLen = len(times) / 2
+  if len(times) % 2:
+    return times[halfLen]
+  else:
+    return 0.5 * (times[halfLen - 1] + times[halfLen])
+
+
+def _TestTime(test, test_times, default_test_time):
+  return test_times.get(test.shortName()) or default_test_time
+
+
+def _DebugShardDistributions(shards, test_times):
+  for i, s in enumerate(shards):
+    num_tests = len(s)
+    if test_times:
+      median = _MedianTestTime(test_times)
+      shard_time = 0.0
+      for t in s:
+        shard_time += _TestTime(t, test_times, median)
+      print 'shard %d: %d seconds (%d tests)' % (i, shard_time, num_tests)
+    else:
+      print 'shard %d: %d tests (unknown duration)' % (i, num_tests)
+
+
+def _SplitShardsByTime(test_cases, total_shards, test_times,
+                       debug_shard_distributions):
+  median = _MedianTestTime(test_times)
+  shards = []
+  for i in xrange(total_shards):
+    shards.append({'total_time': 0.0, 'tests': []})
+  test_cases.sort(key=lambda t: _TestTime(t, test_times, median),
+                  reverse=True)
+
+  # The greedy algorithm has been empirically tested on the WebGL 2.0
+  # conformance tests' times, and results in an essentially perfect
+  # shard distribution of 530 seconds per shard. In the same scenario,
+  # round-robin scheduling resulted in shard times spread between 502
+  # and 592 seconds, and the current alphabetical sharding resulted in
+  # shard times spread between 44 and 1591 seconds.
+
+  # Greedy scheduling. O(m*n), where m is the number of shards and n
+  # is the number of test cases.
+  for t in test_cases:
+    min_shard_index = 0
+    min_shard_time = None
+    for i in xrange(total_shards):
+      if min_shard_time is None or shards[i]['total_time'] < min_shard_time:
+        min_shard_index = i
+        min_shard_time = shards[i]['total_time']
+    shards[min_shard_index]['tests'].append(t)
+    shards[min_shard_index]['total_time'] += _TestTime(t, test_times, median)
+
+  res = [s['tests'] for s in shards]
+  if debug_shard_distributions:
+    _DebugShardDistributions(res, test_times)
+
+  return res
+
+
+def LoadTestCasesToBeRun(
+    test_class, finder_options, filter_regex_str, filter_tests_after_sharding,
+    total_shards, shard_index, test_times, debug_shard_distributions):
+  test_cases = []
+  real_regex = re.compile(filter_regex_str)
+  noop_regex = re.compile('')
+  if filter_tests_after_sharding:
+    filter_regex = noop_regex
+    post_filter_regex = real_regex
+  else:
+    filter_regex = real_regex
+    post_filter_regex = noop_regex
+
+  for t in serially_executed_browser_test_case.GenerateTestCases(
+      test_class, finder_options):
+    if filter_regex.search(t.shortName()):
+      test_cases.append(t)
+
+  if test_times:
+    # Assign tests to shards.
+    shards = _SplitShardsByTime(test_cases, total_shards, test_times,
+                                debug_shard_distributions)
+    return [t for t in shards[shard_index]
+            if post_filter_regex.search(t.shortName())]
+  else:
+    test_cases.sort(key=lambda t: t.shortName())
+    test_range = _TestRangeForShard(total_shards, shard_index, len(test_cases))
+    if debug_shard_distributions:
+      tmp_shards = []
+      for i in xrange(total_shards):
+        tmp_range = _TestRangeForShard(total_shards, i, len(test_cases))
+        tmp_shards.append(test_cases[tmp_range[0]:tmp_range[1]])
+      # Can edit the code to get 'test_times' passed in here for
+      # debugging and comparison purposes.
+      _DebugShardDistributions(tmp_shards, None)
+    return [t for t in test_cases[test_range[0]:test_range[1]]
+            if post_filter_regex.search(t.shortName())]
+
+
+def _CreateTestArgParsers():
+  parser = typ.ArgumentParser(discovery=False, reporting=True, running=True)
+  parser.add_argument('test', type=str, help='Name of the test suite to run')
+  parser.add_argument('--test-filter', type=str, default='', action='store',
+      help='Run only tests whose names match the given filter regexp.')
+  parser.add_argument(
+    '--filter-tests-after-sharding', default=False, action='store_true',
+    help=('Apply the test filter after tests are split for sharding. Useful '
+          'for reproducing bugs related to the order in which tests run.'))
+  parser.add_argument(
+      '--read-abbreviated-json-results-from', metavar='FILENAME',
+      action='store', help=(
+        'If specified, reads abbreviated results from that path in json form. '
+        'This information is used to more evenly distribute tests among '
+        'shards.'))
+  parser.add_argument('--debug-shard-distributions',
+      action='store_true', default=False,
+      help='Print debugging information about the shards\' test distributions')
+
+  parser.add_argument('--default-chrome-root', type=str, default=None)
+  parser.add_argument('--client-config', dest='client_configs',
+                      action='append', default=[])
+  parser.add_argument('--start-dir', dest='start_dirs',
+                      action='append', default=[])
+  parser.add_argument('--skip', metavar='glob', default=[],
+      action='append',
+      help=('Globs of test names to skip (defaults to %(default)s).'))
+  return parser
+
+
+def _SkipMatch(name, skipGlobs):
+  return any(fnmatch.fnmatch(name, glob) for glob in skipGlobs)
+
+
+def _GetClassifier(args):
+  def _SeriallyExecutedBrowserTestCaseClassifer(test_set, test):
+    # Do not pick up tests that do not inherit from
+    # serially_executed_browser_test_case.SeriallyExecutedBrowserTestCase
+    # class.
+    if not isinstance(test,
+        serially_executed_browser_test_case.SeriallyExecutedBrowserTestCase):
+      return
+    name = test.id()
+    if _SkipMatch(name, args.skip):
+      test_set.tests_to_skip.append(
+          typ.TestInput(name, 'skipped because matched --skip'))
+      return
+    # For now, only support running these tests serially.
+    test_set.isolated_tests.append(typ.TestInput(name))
+  return _SeriallyExecutedBrowserTestCaseClassifer
+
+
+def RunTests(args):
+  parser = _CreateTestArgParsers()
+  try:
+    options, extra_args = parser.parse_known_args(args)
+  except arg_parser._Bailout:
+    return parser.exit_status
+  binary_manager.InitDependencyManager(options.client_configs)
+
+  for start_dir in options.start_dirs:
+    modules_to_classes = discover.DiscoverClasses(
+        start_dir, options.top_level_dir,
+        base_class=serially_executed_browser_test_case.
+            SeriallyExecutedBrowserTestCase)
+    browser_test_classes = modules_to_classes.values()
+
+  _ValidateDistinctNames(browser_test_classes)
+
+  test_class = None
+  for cl in browser_test_classes:
+    if cl.Name() == options.test:
+      test_class = cl
+      break
+
+  if not test_class:
+    print 'Cannot find test class with name matching %s' % options.test
+    print 'Available tests: %s' % '\n'.join(
+        cl.Name() for cl in browser_test_classes)
+    return 1
+
+  # Create test context.
+  context = browser_test_context.TypTestContext()
+  for c in options.client_configs:
+    context.client_configs.append(c)
+  context.finder_options = ProcessCommandLineOptions(
+      test_class, options, extra_args)
+  context.test_class = test_class
+  test_times = None
+  if options.read_abbreviated_json_results_from:
+    with open(options.read_abbreviated_json_results_from, 'r') as f:
+      abbr_results = json.load(f)
+      test_times = abbr_results.get('times')
+  tests_to_run = LoadTestCasesToBeRun(
+      test_class=test_class, finder_options=context.finder_options,
+      filter_regex_str=options.test_filter,
+      filter_tests_after_sharding=options.filter_tests_after_sharding,
+      total_shards=options.total_shards, shard_index=options.shard_index,
+      test_times=test_times,
+      debug_shard_distributions=options.debug_shard_distributions)
+  for t in tests_to_run:
+    context.test_case_ids_to_run.add(t.id())
+  context.Freeze()
+  browser_test_context._global_test_context = context
+
+  # Setup typ runner.
+  runner = typ.Runner()
+
+  runner.context = context
+  runner.setup_fn = _SetUpProcess
+  runner.teardown_fn = _TearDownProcess
+
+  runner.args.jobs = options.jobs
+  runner.args.metadata = options.metadata
+  runner.args.passthrough = options.passthrough
+  runner.args.path = options.path
+  runner.args.retry_limit = options.retry_limit
+  runner.args.test_results_server = options.test_results_server
+  runner.args.test_type = options.test_type
+  runner.args.top_level_dir = options.top_level_dir
+  runner.args.write_full_results_to = options.write_full_results_to
+  runner.args.write_trace_to = options.write_trace_to
+  runner.args.list_only = options.list_only
+  runner.classifier = _GetClassifier(options)
+
+  runner.args.suffixes = TEST_SUFFIXES
+
+  # Since sharding logic is handled by browser_test_runner harness by passing
+  # browser_test_context.test_case_ids_to_run to subprocess to indicate test
+  # cases to be run, we explicitly disable sharding logic in typ.
+  runner.args.total_shards = 1
+  runner.args.shard_index = 0
+
+  runner.args.timing = True
+  runner.args.verbose = options.verbose
+  runner.win_multiprocessing = typ.WinMultiprocessing.importable
+  try:
+    ret, _, _ = runner.run()
+  except KeyboardInterrupt:
+    print >> sys.stderr, "interrupted, exiting"
+    ret = 130
+  return ret
+
+
+def _SetUpProcess(child, context):
+  del child  # Unused.
+  args = context.finder_options
+  if binary_manager.NeedsInit():
+    # On windows, typ doesn't keep the DependencyManager initialization in the
+    # child processes.
+    binary_manager.InitDependencyManager(context.client_configs)
+  if args.remote_platform_options.device == 'android':
+    android_devices = android_device.FindAllAvailableDevices(args)
+    if not android_devices:
+      raise RuntimeError("No Android device found")
+    android_devices.sort(key=lambda device: device.name)
+    args.remote_platform_options.device = (
+        android_devices[child.worker_num-1].guid)
+  browser_test_context._global_test_context = context
+  context.test_class.SetUpProcess()
+
+
+def _TearDownProcess(child, context):
+  del child, context  # Unused.
+  browser_test_context._global_test_context.test_class.TearDownProcess()
+  browser_test_context._global_test_context = None
+
+
+if __name__ == '__main__':
+  ret_code = RunTests(sys.argv[1:])
+  sys.exit(ret_code)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/run_chromeos_tests.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/run_chromeos_tests.py
new file mode 100644
index 0000000..2313321
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/run_chromeos_tests.py
@@ -0,0 +1,60 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import logging
+
+from telemetry.testing import run_tests
+
+
+def RunChromeOSTests(browser_type, tests_to_run):
+  """ Run ChromeOS tests.
+  Args:
+    |browser_type|: string specifies which browser type to use.
+    |tests_to_run|: a list of tuples (top_level_dir, unit_tests), whereas
+      |top_level_dir| specifies the top level directory for running tests, and
+      |unit_tests| is a list of string test names to run.
+  """
+  stream = _LoggingOutputStream()
+  error_string = ''
+
+  for (top_level_dir, unit_tests) in tests_to_run:
+    logging.info('Running unit tests in %s with browser_type "%s".' %
+                 (top_level_dir, browser_type))
+
+    ret = _RunOneSetOfTests(browser_type, top_level_dir, unit_tests, stream)
+    if ret:
+      error_string += 'The unit tests of %s failed.\n' % top_level_dir
+  return error_string
+
+
+def _RunOneSetOfTests(browser_type, top_level_dir, tests, stream):
+  args = ['--browser', browser_type,
+          '--top-level-dir', top_level_dir,
+          '--jobs', '1',
+          '--disable-logging-config'] + tests
+  return run_tests.RunTestsCommand.main(args, stream=stream)
+
+
+class _LoggingOutputStream(object):
+
+  def __init__(self):
+    self._buffer = []
+
+  def write(self, s):
+    """Buffer a string write. Log it when we encounter a newline."""
+    if '\n' in s:
+      segments = s.split('\n')
+      segments[0] = ''.join(self._buffer + [segments[0]])
+      log_level = logging.getLogger().getEffectiveLevel()
+      try:  # TODO(dtu): We need this because of crbug.com/394571
+        logging.getLogger().setLevel(logging.INFO)
+        for line in segments[:-1]:
+          logging.info(line)
+      finally:
+        logging.getLogger().setLevel(log_level)
+      self._buffer = [segments[-1]]
+    else:
+      self._buffer.append(s)
+
+  def flush(self):
+    pass
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/run_tests.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/run_tests.py
new file mode 100644
index 0000000..17c9ff5
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/run_tests.py
@@ -0,0 +1,303 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import fnmatch
+import logging
+import os
+import sys
+
+from telemetry.core import util
+from telemetry.core import platform as platform_module
+from telemetry import decorators
+from telemetry.internal.browser import browser_finder
+from telemetry.internal.browser import browser_finder_exceptions
+from telemetry.internal.browser import browser_options
+from telemetry.internal.platform import android_device
+from telemetry.internal.util import binary_manager
+from telemetry.internal.util import command_line
+from telemetry.internal.util import ps_util
+from telemetry.testing import browser_test_case
+from telemetry.testing import options_for_unittests
+
+from py_utils import cloud_storage
+from py_utils import xvfb
+
+import typ
+
+
+class RunTestsCommand(command_line.OptparseCommand):
+  """Run unit tests"""
+
+  usage = '[test_name ...] [<options>]'
+  xvfb_process = None
+
+  def __init__(self):
+    super(RunTestsCommand, self).__init__()
+    self.stream = sys.stdout
+
+  @classmethod
+  def CreateParser(cls):
+    options = browser_options.BrowserFinderOptions()
+    options.browser_type = 'any'
+    parser = options.CreateParser('%%prog %s' % cls.usage)
+    return parser
+
+  @classmethod
+  def AddCommandLineArgs(cls, parser, _):
+    parser.add_option('--start-xvfb', action='store_true',
+                      default=False, help='Start Xvfb display if needed.')
+    parser.add_option('--disable-cloud-storage-io', action='store_true',
+                      default=False, help=('Disable cloud storage IO when '
+                                           'tests are run in parallel.'))
+    parser.add_option('--repeat-count', type='int', default=1,
+                      help='Repeats each a provided number of times.')
+    parser.add_option('--no-browser', action='store_true', default=False,
+                      help='Don\'t require an actual browser to run the tests.')
+    parser.add_option('-d', '--also-run-disabled-tests',
+                      dest='run_disabled_tests',
+                      action='store_true', default=False,
+                      help='Ignore @Disabled and @Enabled restrictions.')
+    parser.add_option('--exact-test-filter', action='store_true', default=False,
+                      help='Treat test filter as exact matches (default is '
+                           'substring matches).')
+    parser.add_option('--client-config', dest='client_configs',
+                      action='append', default=[])
+    parser.add_option('--disable-logging-config', action='store_true',
+                      default=False, help='Configure logging (default on)')
+    parser.add_option('--skip', metavar='glob', default=[],
+                      action='append', help=(
+                          'Globs of test names to skip (defaults to '
+                          '%(default)s).'))
+    typ.ArgumentParser.add_option_group(parser,
+                                        "Options for running the tests",
+                                        running=True,
+                                        skip=['-d', '-v', '--verbose'])
+    typ.ArgumentParser.add_option_group(parser,
+                                        "Options for reporting the results",
+                                        reporting=True)
+
+  @classmethod
+  def ProcessCommandLineArgs(cls, parser, args, _):
+    # We retry failures by default unless we're running a list of tests
+    # explicitly.
+    if not args.retry_limit and not args.positional_args:
+      args.retry_limit = 3
+
+    if args.no_browser:
+      return
+
+    if args.start_xvfb and xvfb.ShouldStartXvfb():
+      cls.xvfb_process = xvfb.StartXvfb()
+      # Work around Mesa issues on Linux. See
+      # https://github.com/catapult-project/catapult/issues/3074
+      args.browser_options.AppendExtraBrowserArgs('--disable-gpu')
+
+    try:
+      possible_browser = browser_finder.FindBrowser(args)
+    except browser_finder_exceptions.BrowserFinderException, ex:
+      parser.error(ex)
+
+    if not possible_browser:
+      parser.error('No browser found of type %s. Cannot run tests.\n'
+                   'Re-run with --browser=list to see '
+                   'available browser types.' % args.browser_type)
+
+  @classmethod
+  def main(cls, args=None, stream=None):  # pylint: disable=arguments-differ
+    # We override the superclass so that we can hook in the 'stream' arg.
+    parser = cls.CreateParser()
+    cls.AddCommandLineArgs(parser, None)
+    options, positional_args = parser.parse_args(args)
+    options.positional_args = positional_args
+
+    try:
+      # Must initialize the DependencyManager before calling
+      # browser_finder.FindBrowser(args)
+      binary_manager.InitDependencyManager(options.client_configs)
+      cls.ProcessCommandLineArgs(parser, options, None)
+
+      obj = cls()
+      if stream is not None:
+        obj.stream = stream
+      return obj.Run(options)
+    finally:
+      if cls.xvfb_process:
+        cls.xvfb_process.kill()
+
+  def Run(self, args):
+    runner = typ.Runner()
+    if self.stream:
+      runner.host.stdout = self.stream
+
+    if args.no_browser:
+      possible_browser = None
+      platform = platform_module.GetHostPlatform()
+    else:
+      possible_browser = browser_finder.FindBrowser(args)
+      platform = possible_browser.platform
+
+    fetch_reference_chrome_binary = False
+    # Fetch all binaries needed by telemetry before we run the benchmark.
+    if possible_browser and possible_browser.browser_type == 'reference':
+      fetch_reference_chrome_binary = True
+    binary_manager.FetchBinaryDependencies(
+        platform, args.client_configs, fetch_reference_chrome_binary)
+
+    # Telemetry seems to overload the system if we run one test per core,
+    # so we scale things back a fair amount. Many of the telemetry tests
+    # are long-running, so there's a limit to how much parallelism we
+    # can effectively use for now anyway.
+    #
+    # It should be possible to handle multiple devices if we adjust the
+    # browser_finder code properly, but for now we only handle one on ChromeOS.
+    if platform.GetOSName() == 'chromeos':
+      runner.args.jobs = 1
+    elif platform.GetOSName() == 'android':
+      android_devs = android_device.FindAllAvailableDevices(args)
+      runner.args.jobs = len(android_devs)
+      if runner.args.jobs == 0:
+        raise RuntimeError("No Android device found")
+      print 'Running tests with %d Android device(s).' % runner.args.jobs
+    elif platform.GetOSVersionName() == 'xp':
+      # For an undiagnosed reason, XP falls over with more parallelism.
+      # See crbug.com/388256
+      runner.args.jobs = max(int(args.jobs) // 4, 1)
+    else:
+      runner.args.jobs = max(int(args.jobs) // 2, 1)
+
+    runner.args.skip = args.skip
+    runner.args.metadata = args.metadata
+    runner.args.passthrough = args.passthrough
+    runner.args.path = args.path
+    runner.args.retry_limit = args.retry_limit
+    runner.args.test_results_server = args.test_results_server
+    runner.args.test_type = args.test_type
+    runner.args.top_level_dir = args.top_level_dir
+    runner.args.write_full_results_to = args.write_full_results_to
+    runner.args.write_trace_to = args.write_trace_to
+    runner.args.list_only = args.list_only
+    runner.args.shard_index = args.shard_index
+    runner.args.total_shards = args.total_shards
+
+    runner.args.path.append(util.GetUnittestDataDir())
+
+    # Always print out these info for the ease of debugging.
+    runner.args.timing = True
+    runner.args.verbose = 3
+
+    runner.classifier = GetClassifier(args, possible_browser)
+    runner.context = args
+    runner.setup_fn = _SetUpProcess
+    runner.teardown_fn = _TearDownProcess
+    runner.win_multiprocessing = typ.WinMultiprocessing.importable
+    try:
+      ret, _, _ = runner.run()
+    except KeyboardInterrupt:
+      print >> sys.stderr, "interrupted, exiting"
+      ret = 130
+    return ret
+
+
+def _SkipMatch(name, skipGlobs):
+  return any(fnmatch.fnmatch(name, glob) for glob in skipGlobs)
+
+
+def GetClassifier(args, possible_browser):
+
+  def ClassifyTestWithoutBrowser(test_set, test):
+    name = test.id()
+    if _SkipMatch(name, args.skip):
+      test_set.tests_to_skip.append(
+          typ.TestInput(name, 'skipped because matched --skip'))
+      return
+    if (not args.positional_args
+        or _MatchesSelectedTest(name, args.positional_args,
+                                  args.exact_test_filter)):
+      # TODO(telemetry-team): Make sure that all telemetry unittest that invokes
+      # actual browser are subclasses of browser_test_case.BrowserTestCase
+      # (crbug.com/537428)
+      if issubclass(test.__class__, browser_test_case.BrowserTestCase):
+        test_set.tests_to_skip.append(typ.TestInput(
+            name, msg='Skip the test because it requires a browser.'))
+      else:
+        test_set.parallel_tests.append(typ.TestInput(name))
+
+  def ClassifyTestWithBrowser(test_set, test):
+    name = test.id()
+    if _SkipMatch(name, args.skip):
+      test_set.tests_to_skip.append(
+          typ.TestInput(name, 'skipped because matched --skip'))
+      return
+    if (not args.positional_args
+        or _MatchesSelectedTest(name, args.positional_args,
+                                args.exact_test_filter)):
+      assert hasattr(test, '_testMethodName')
+      method = getattr(
+          test, test._testMethodName)  # pylint: disable=protected-access
+      should_skip, reason = decorators.ShouldSkip(method, possible_browser)
+      if should_skip and not args.run_disabled_tests:
+        test_set.tests_to_skip.append(typ.TestInput(name, msg=reason))
+      elif decorators.ShouldBeIsolated(method, possible_browser):
+        test_set.isolated_tests.append(typ.TestInput(name))
+      else:
+        test_set.parallel_tests.append(typ.TestInput(name))
+
+  if possible_browser:
+    return ClassifyTestWithBrowser
+  else:
+    return ClassifyTestWithoutBrowser
+
+
+def _MatchesSelectedTest(name, selected_tests, selected_tests_are_exact):
+  if not selected_tests:
+    return False
+  if selected_tests_are_exact:
+    return any(name in selected_tests)
+  else:
+    return any(test in name for test in selected_tests)
+
+
+def _SetUpProcess(child, context): # pylint: disable=unused-argument
+  ps_util.EnableListingStrayProcessesUponExitHook()
+  # Make sure that we don't invokes cloud storage I/Os when we run the tests in
+  # parallel.
+  # TODO(nednguyen): always do this once telemetry tests in Chromium is updated
+  # to prefetch files.
+  # (https://github.com/catapult-project/catapult/issues/2192)
+  args = context
+  if args.disable_cloud_storage_io:
+    os.environ[cloud_storage.DISABLE_CLOUD_STORAGE_IO] = '1'
+  if binary_manager.NeedsInit():
+    # Typ doesn't keep the DependencyManager initialization in the child
+    # processes.
+    binary_manager.InitDependencyManager(context.client_configs)
+  # We need to reset the handlers in case some other parts of telemetry already
+  # set it to make this work.
+  if not args.disable_logging_config:
+    logging.getLogger().handlers = []
+    logging.basicConfig(
+        level=logging.INFO,
+        format='(%(levelname)s) %(asctime)s pid=%(process)d'
+               '  %(module)s.%(funcName)s:%(lineno)d'
+               '  %(message)s')
+  if args.remote_platform_options.device == 'android':
+    android_devices = android_device.FindAllAvailableDevices(args)
+    if not android_devices:
+      raise RuntimeError("No Android device found")
+    android_devices.sort(key=lambda device: device.name)
+    args.remote_platform_options.device = (
+        android_devices[child.worker_num-1].guid)
+  options_for_unittests.Push(args)
+
+
+def _TearDownProcess(child, context): # pylint: disable=unused-argument
+  # It's safe to call teardown_browser even if we did not start any browser
+  # in any of the tests.
+  browser_test_case.teardown_browser()
+  options_for_unittests.Pop()
+
+
+if __name__ == '__main__':
+  ret_code = RunTestsCommand.main()
+  sys.exit(ret_code)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/run_tests_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/run_tests_unittest.py
new file mode 100644
index 0000000..8728813
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/run_tests_unittest.py
@@ -0,0 +1,119 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from telemetry.core import util
+from telemetry.testing import run_tests
+
+
+class MockArgs(object):
+  def __init__(self):
+    self.positional_args = []
+    self.exact_test_filter = True
+    self.run_disabled_tests = False
+    self.skip = []
+
+
+class MockPossibleBrowser(object):
+  def __init__(self, browser_type, os_name, os_version_name,
+               supports_tab_control):
+    self.browser_type = browser_type
+    self.platform = MockPlatform(os_name, os_version_name)
+    self.supports_tab_control = supports_tab_control
+
+
+class MockPlatform(object):
+  def __init__(self, os_name, os_version_name):
+    self.os_name = os_name
+    self.os_version_name = os_version_name
+
+  def GetOSName(self):
+    return self.os_name
+
+  def GetOSVersionName(self):
+    return self.os_version_name
+
+
+class RunTestsUnitTest(unittest.TestCase):
+
+  def _GetEnabledTests(self, browser_type, os_name, os_version_name,
+                       supports_tab_control, args=None):
+    if not args:
+      args = MockArgs()
+    runner = run_tests.typ.Runner()
+    host = runner.host
+    runner.top_level_dir = util.GetTelemetryDir()
+    runner.args.tests = [host.join(util.GetTelemetryDir(),
+        'telemetry', 'testing', 'disabled_cases.py')]
+    possible_browser = MockPossibleBrowser(
+        browser_type, os_name, os_version_name, supports_tab_control)
+    runner.classifier = run_tests.GetClassifier(args, possible_browser)
+    _, test_set = runner.find_tests(runner.args)
+    return set(test.name.split('.')[-1] for test in test_set.parallel_tests)
+
+  def testSystemMacMavericks(self):
+    self.assertEquals(
+        set(['testAllEnabled',
+             'testMacOnly',
+             'testMavericksOnly',
+             'testNoChromeOS',
+             'testNoWinLinux',
+             'testSystemOnly',
+             'testHasTabs']),
+        self._GetEnabledTests('system', 'mac', 'mavericks', True))
+
+  def testSystemMacLion(self):
+    self.assertEquals(
+        set(['testAllEnabled',
+             'testMacOnly',
+             'testNoChromeOS',
+             'testNoMavericks',
+             'testNoWinLinux',
+             'testSystemOnly',
+             'testHasTabs']),
+        self._GetEnabledTests('system', 'mac', 'lion', True))
+
+  def testCrosGuestChromeOS(self):
+    self.assertEquals(
+        set(['testAllEnabled',
+             'testChromeOSOnly',
+             'testNoMac',
+             'testNoMavericks',
+             'testNoSystem',
+             'testNoWinLinux',
+             'testHasTabs']),
+        self._GetEnabledTests('cros-guest', 'chromeos', '', True))
+
+  def testCanaryWindowsWin7(self):
+    self.assertEquals(
+        set(['testAllEnabled',
+             'testNoChromeOS',
+             'testNoMac',
+             'testNoMavericks',
+             'testNoSystem',
+             'testWinOrLinuxOnly',
+             'testHasTabs']),
+        self._GetEnabledTests('canary', 'win', 'win7', True))
+
+  def testDoesntHaveTabs(self):
+    self.assertEquals(
+        set(['testAllEnabled',
+             'testNoChromeOS',
+             'testNoMac',
+             'testNoMavericks',
+             'testNoSystem',
+             'testWinOrLinuxOnly']),
+        self._GetEnabledTests('canary', 'win', 'win7', False))
+
+  def testSkip(self):
+    args = MockArgs()
+    args.skip = ['telemetry.*testNoMac', '*NoMavericks',
+                 'telemetry.testing.disabled_cases.DisabledCases.testNoSystem']
+    self.assertEquals(
+        set(['testAllEnabled',
+             'testNoChromeOS',
+             'testWinOrLinuxOnly',
+             'testHasTabs']),
+        self._GetEnabledTests('canary', 'win', 'win7', True, args))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/serially_executed_browser_test_case.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/serially_executed_browser_test_case.py
new file mode 100644
index 0000000..d0a4049
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/serially_executed_browser_test_case.py
@@ -0,0 +1,228 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import inspect
+import logging
+import re
+import unittest
+
+from py_utils import cloud_storage
+from telemetry.internal.browser import browser_finder
+from telemetry.testing import browser_test_context
+from telemetry.util import wpr_modes
+
+
+DEFAULT_LOG_FORMAT = (
+  '(%(levelname)s) %(asctime)s %(module)s.%(funcName)s:%(lineno)d  '
+  '%(message)s')
+
+
+class SeriallyExecutedBrowserTestCase(unittest.TestCase):
+  def __init__(self, methodName):
+    super(SeriallyExecutedBrowserTestCase, self).__init__(methodName)
+    self._private_methodname = methodName
+
+  def shortName(self):
+    """Returns the method name this test runs, without the package prefix."""
+    return self._private_methodname
+
+  @classmethod
+  def Name(cls):
+    return cls.__name__
+
+  @classmethod
+  def AddCommandlineArgs(cls, parser):
+    pass
+
+  @classmethod
+  def SetUpProcess(cls):
+    """ Set up testing logic before running the test case.
+    This is guaranteed to be called only once for all the tests before the test
+    suite runs.
+    """
+    finder_options = browser_test_context.GetCopy().finder_options
+    cls._finder_options = finder_options
+
+    # Set up logging based on the verbosity passed from the parent to
+    # the child process.
+    if finder_options.verbosity >= 2:
+      logging.getLogger().setLevel(logging.DEBUG)
+    elif finder_options.verbosity:
+      logging.getLogger().setLevel(logging.INFO)
+    else:
+      logging.getLogger().setLevel(logging.WARNING)
+    logging.basicConfig(format=DEFAULT_LOG_FORMAT)
+
+    cls.platform = None
+    cls.browser = None
+    cls._browser_to_create = None
+    cls._browser_options = None
+
+  @classmethod
+  def SetBrowserOptions(cls, browser_options):
+    """Sets the browser option for the browser to create.
+
+    Args:
+      browser_options: Browser options object for the browser we want to test.
+    """
+    cls._browser_options = browser_options
+    cls._browser_to_create = browser_finder.FindBrowser(browser_options)
+    if not cls.platform:
+      cls.platform = cls._browser_to_create.platform
+      cls.platform.network_controller.InitializeIfNeeded()
+    else:
+      assert cls.platform == cls._browser_to_create.platform, (
+          'All browser launches within same test suite must use browsers on '
+          'the same platform')
+
+  @classmethod
+  def StartWPRServer(cls, archive_path=None, archive_bucket=None):
+    """Start a webpage replay server.
+
+    Args:
+      archive_path: Path to the WPR file. If there is a corresponding sha1 file,
+          this archive will be automatically downloaded from Google Storage.
+      archive_bucket: The bucket to look for the WPR archive.
+    """
+    assert cls._browser_options, (
+        'Browser options must be set with |SetBrowserOptions| prior to '
+        'starting WPR')
+    assert not cls.browser, 'WPR must be started prior to browser being started'
+
+    cloud_storage.GetIfChanged(archive_path, archive_bucket)
+    cls.platform.network_controller.Open(wpr_modes.WPR_REPLAY, [])
+    cls.platform.network_controller.StartReplay(archive_path=archive_path)
+
+  @classmethod
+  def StopWPRServer(cls):
+    cls.platform.network_controller.StopReplay()
+
+  @classmethod
+  def StartBrowser(cls):
+    assert cls._browser_options, (
+        'Browser options must be set with |SetBrowserOptions| prior to '
+        'starting WPR')
+    assert not cls.browser, 'Browser is started. Must close it first'
+
+    cls.browser = cls._browser_to_create.Create(cls._browser_options)
+
+  @classmethod
+  def StopBrowser(cls):
+    assert cls.browser, 'Browser is not started'
+    cls.browser.Close()
+    cls.browser = None
+
+  @classmethod
+  def TearDownProcess(cls):
+    """ Tear down the testing logic after running the test cases.
+    This is guaranteed to be called only once for all the tests after the test
+    suite finishes running.
+    """
+
+    if cls.platform:
+      cls.platform.StopAllLocalServers()
+      cls.platform.network_controller.Close()
+    if cls.browser:
+      cls.StopBrowser()
+
+  @classmethod
+  def SetStaticServerDirs(cls, dirs_path):
+    assert cls.platform
+    assert isinstance(dirs_path, list)
+    cls.platform.SetHTTPServerDirectories(dirs_path)
+
+  @classmethod
+  def UrlOfStaticFilePath(cls, file_path):
+    return cls.platform.http_server.UrlOf(file_path)
+
+
+def LoadAllTestsInModule(module):
+  """ Load all tests & generated browser tests in a given module.
+
+  This is supposed to be invoke in load_tests() method of your test modules that
+  use browser_test_runner framework to discover & generate the tests to be
+  picked up by the test runner. Here is the example of how your test module
+  should looks like:
+
+  ################## my_awesome_browser_tests.py  ################
+  import sys
+
+  from telemetry.testing import serially_executed_browser_test_case
+  ...
+
+  class TestSimpleBrowser(
+      serially_executed_browser_test_case.SeriallyExecutedBrowserTestCase):
+  ...
+  ...
+
+  def load_tests(loader, tests, pattern):
+    return serially_executed_browser_test_case.LoadAllTestsInModule(
+        sys.modules[__name__])
+  #################################################################
+
+  Args:
+    module: the module which contains test cases classes.
+
+  Returns:
+    an instance of unittest.TestSuite, which contains all the tests & generated
+    test cases to be run.
+  """
+  suite = unittest.TestSuite()
+  test_context = browser_test_context.GetCopy()
+  if not test_context:
+    return suite
+  for _, obj in inspect.getmembers(module):
+    if (inspect.isclass(obj) and
+        issubclass(obj, SeriallyExecutedBrowserTestCase)):
+      # We bail out early if this class doesn't match the targeted
+      # test_class in test_context to avoid calling GenerateTestCases
+      # for tests that we don't intend to run. This is to avoid possible errors
+      # in GenerateTestCases as the test class may define custom options in
+      # the finder_options object, and hence would raise error if they can't
+      # find their custom options in finder_options object.
+      if test_context.test_class != obj:
+        continue
+      for test in GenerateTestCases(
+          test_class=obj, finder_options=test_context.finder_options):
+        if test.id() in test_context.test_case_ids_to_run:
+          suite.addTest(test)
+  return suite
+
+
+def _GenerateTestMethod(based_method, args):
+  return lambda self: based_method(self, *args)
+
+
+_TEST_GENERATOR_PREFIX = 'GenerateTestCases_'
+_INVALID_TEST_NAME_RE = re.compile(r'[^a-zA-Z0-9_]')
+
+def _ValidateTestMethodname(test_name):
+  assert not bool(_INVALID_TEST_NAME_RE.search(test_name))
+
+
+def GenerateTestCases(test_class, finder_options):
+  test_cases = []
+  for name, method in inspect.getmembers(
+      test_class, predicate=inspect.ismethod):
+    if name.startswith('test'):
+      # Do not allow method names starting with "test" in these
+      # subclasses, to avoid collisions with Python's unit test runner.
+      raise Exception('Name collision with Python\'s unittest runner: %s' %
+                      name)
+    elif name.startswith('Test'):
+      # Pass these through for the time being. We may want to rethink
+      # how they are handled in the future.
+      test_cases.append(test_class(name))
+    elif name.startswith(_TEST_GENERATOR_PREFIX):
+      based_method_name = name[len(_TEST_GENERATOR_PREFIX):]
+      assert hasattr(test_class, based_method_name), (
+          '%s is specified but based method %s does not exist' %
+          (name, based_method_name))
+      based_method = getattr(test_class, based_method_name)
+      for generated_test_name, args in method(finder_options):
+        _ValidateTestMethodname(generated_test_name)
+        setattr(test_class, generated_test_name, _GenerateTestMethod(
+            based_method, args))
+        test_cases.append(test_class(generated_test_name))
+  return test_cases
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/simple_mock.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/simple_mock.py
new file mode 100644
index 0000000..dbd02b6
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/simple_mock.py
@@ -0,0 +1,98 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""A very very simple mock object harness."""
+
+DONT_CARE = ''
+
+class MockFunctionCall(object):
+  def __init__(self, name):
+    self.name = name
+    self.args = tuple()
+    self.return_value = None
+    self.when_called_handlers = []
+
+  def WithArgs(self, *args):
+    self.args = args
+    return self
+
+  def WillReturn(self, value):
+    self.return_value = value
+    return self
+
+  def WhenCalled(self, handler):
+    self.when_called_handlers.append(handler)
+
+  def VerifyEquals(self, got):
+    if self.name != got.name:
+      raise Exception('Self %s, got %s' % (repr(self), repr(got)))
+    if len(self.args) != len(got.args):
+      raise Exception('Self %s, got %s' % (repr(self), repr(got)))
+    for i in range(len(self.args)):
+      self_a = self.args[i]
+      got_a = got.args[i]
+      if self_a == DONT_CARE:
+        continue
+      if self_a != got_a:
+        raise Exception('Self %s, got %s' % (repr(self), repr(got)))
+
+  def __repr__(self):
+    def arg_to_text(a):
+      if a == DONT_CARE:
+        return '_'
+      return repr(a)
+    args_text = ', '.join([arg_to_text(a) for a in self.args])
+    if self.return_value in (None, DONT_CARE):
+      return '%s(%s)' % (self.name, args_text)
+    return '%s(%s)->%s' % (self.name, args_text, repr(self.return_value))
+
+class MockTrace(object):
+  def __init__(self):
+    self.expected_calls = []
+    self.next_call_index = 0
+
+class MockObject(object):
+  def __init__(self, parent_mock=None):
+    if parent_mock:
+      self._trace = parent_mock._trace # pylint: disable=protected-access
+    else:
+      self._trace = MockTrace()
+
+  def __setattr__(self, name, value):
+    if (not hasattr(self, '_trace') or
+        hasattr(value, 'is_hook')):
+      object.__setattr__(self, name, value)
+      return
+    assert isinstance(value, MockObject)
+    object.__setattr__(self, name, value)
+
+  def SetAttribute(self, name, value):
+    setattr(self, name, value)
+
+  def ExpectCall(self, func_name, *args):
+    assert self._trace.next_call_index == 0
+    if not hasattr(self, func_name):
+      self._install_hook(func_name)
+
+    call = MockFunctionCall(func_name)
+    self._trace.expected_calls.append(call)
+    call.WithArgs(*args)
+    return call
+
+  def _install_hook(self, func_name):
+    def handler(*args, **_):
+      got_call = MockFunctionCall(
+        func_name).WithArgs(*args).WillReturn(DONT_CARE)
+      if self._trace.next_call_index >= len(self._trace.expected_calls):
+        raise Exception(
+          'Call to %s was not expected, at end of programmed trace.' %
+          repr(got_call))
+      expected_call = self._trace.expected_calls[
+        self._trace.next_call_index]
+      expected_call.VerifyEquals(got_call)
+      self._trace.next_call_index += 1
+      for h in expected_call.when_called_handlers:
+        h(*args)
+      return expected_call.return_value
+    handler.is_hook = True
+    setattr(self, func_name, handler)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/simple_mock_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/simple_mock_unittest.py
new file mode 100644
index 0000000..67cee58
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/simple_mock_unittest.py
@@ -0,0 +1,83 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import unittest
+
+from telemetry.testing import simple_mock
+
+_ = simple_mock.DONT_CARE
+
+# pylint: disable=no-member
+class SimpleMockUnitTest(unittest.TestCase):
+  def testBasic(self):
+    mock = simple_mock.MockObject()
+    mock.ExpectCall('foo')
+
+    mock.foo()
+
+  def testReturn(self):
+    mock = simple_mock.MockObject()
+    mock.ExpectCall('foo').WillReturn(7)
+
+    ret = mock.foo()
+    self.assertEquals(ret, 7)
+
+  def testArgs(self):
+    mock = simple_mock.MockObject()
+    mock.ExpectCall('foo').WithArgs(3, 4)
+
+    mock.foo(3, 4)
+
+  def testArgs2(self):
+    mock = simple_mock.MockObject()
+    mock.ExpectCall('foo', 3, 4)
+
+    mock.foo(3, 4)
+
+  def testArgsMismatch(self):
+    mock = simple_mock.MockObject()
+    mock.ExpectCall('foo').WithArgs(3, 4)
+
+    self.assertRaises(Exception,
+                      lambda: mock.foo(4, 4))
+
+
+  def testArgsDontCare(self):
+    mock = simple_mock.MockObject()
+    mock.ExpectCall('foo').WithArgs(_, 4)
+
+    mock.foo(4, 4)
+
+  def testOnCall(self):
+    mock = simple_mock.MockObject()
+
+    handler_called = []
+    def Handler(arg0):
+      assert arg0 == 7
+      handler_called.append(True)
+    mock.ExpectCall('baz', 7).WhenCalled(Handler)
+
+    mock.baz(7)
+    self.assertTrue(len(handler_called) > 0)
+
+
+  def testSubObject(self):
+    mock = simple_mock.MockObject()
+    mock.bar = simple_mock.MockObject(mock)
+
+    mock.ExpectCall('foo').WithArgs(_, 4)
+    mock.bar.ExpectCall('baz')
+
+    mock.foo(0, 4)
+    mock.bar.baz()
+
+  def testSubObjectMismatch(self):
+    mock = simple_mock.MockObject()
+    mock.bar = simple_mock.MockObject(mock)
+
+    mock.ExpectCall('foo').WithArgs(_, 4)
+    mock.bar.ExpectCall('baz')
+
+    self.assertRaises(
+      Exception,
+      lambda: mock.bar.baz()) # pylint: disable=unnecessary-lambda
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/story_set_smoke_test.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/story_set_smoke_test.py
new file mode 100644
index 0000000..8697741
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/story_set_smoke_test.py
@@ -0,0 +1,155 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+import os
+import unittest
+
+from telemetry.core import discover
+from telemetry.internal.browser import browser_credentials
+from telemetry.internal import story_runner
+from telemetry import page
+from telemetry import story as story_module
+from telemetry.wpr import archive_info
+
+
+class StorySetSmokeTest(unittest.TestCase):
+
+  def setUp(self):
+    # Make sure the added failure message is appended to the default failure
+    # message.
+    self.longMessage = True
+
+  def GetAllStorySetClasses(self, story_sets_dir, top_level_dir):
+    # We can't test page sets that aren't directly constructible since we
+    # don't know what arguments to put for the constructor.
+    return discover.DiscoverClasses(story_sets_dir, top_level_dir,
+                                    story_module.StorySet,
+                                    directly_constructable=True).values()
+
+  def CheckArchive(self, story_set):
+    """Verify that all URLs of pages in story_set have an associated archive."""
+    # TODO: Eventually these should be fatal.
+    if not story_set.archive_data_file:
+      logging.warning('Skipping %s: no archive data file', story_set.file_path)
+      return
+
+    logging.info('Testing %s', story_set.file_path)
+
+    archive_data_file_path = os.path.join(story_set.base_dir,
+                                          story_set.archive_data_file)
+    self.assertTrue(os.path.exists(archive_data_file_path),
+                    msg='Archive data file not found for %s' %
+                    story_set.file_path)
+
+    wpr_archive_info = archive_info.WprArchiveInfo.FromFile(
+        archive_data_file_path, story_set.bucket)
+    for story in story_set.stories:
+      if isinstance(story, page.Page) and story.url.startswith('http'):
+        self.assertTrue(wpr_archive_info.WprFilePathForStory(story),
+                        msg='No archive found for %s in %s' % (
+                            story.url, story_set.archive_data_file))
+
+  def CheckCredentials(self, story_set):
+    """Verify that all pages in story_set use proper credentials"""
+    for story in story_set.stories:
+      if not isinstance(story, page.Page):
+        continue
+      credentials = browser_credentials.BrowserCredentials()
+      if story.credentials_path:
+        credentials.credentials_path = (
+            os.path.join(story.base_dir, story.credentials_path))
+      fail_message = ('page %s of %s has invalid credentials %s' %
+                      (story.url, story_set.file_path, story.credentials))
+      if story.credentials:
+        try:
+          self.assertTrue(credentials.CanLogin(story.credentials), fail_message)
+        except browser_credentials.CredentialsError:
+          self.fail(fail_message)
+
+  def CheckAttributes(self, story_set):
+    """Verify that story_set and its stories base attributes have the right
+       types.
+    """
+    self.CheckAttributesOfStorySetBasicAttributes(story_set)
+    for story in story_set.stories:
+      self.CheckAttributesOfStoryBasicAttributes(story)
+
+  def CheckAttributesOfStorySetBasicAttributes(self, story_set):
+    if story_set.base_dir is not None:
+      self.assertTrue(
+          isinstance(story_set.base_dir, str),
+          msg='story_set %\'s base_dir must have type string')
+
+    self.assertTrue(
+        isinstance(story_set.archive_data_file, str),
+        msg='story_set\'s archive_data_file path must have type string')
+
+  def CheckAttributesOfStoryBasicAttributes(self, story):
+    self.assertTrue(not hasattr(story, 'disabled'))
+    self.assertTrue(
+       isinstance(story.name, str),
+       msg='story %s \'s name field must have type string' % story.display_name)
+    self.assertTrue(
+       isinstance(story.tags, set),
+       msg='story %s \'s tags field must have type set' % story.display_name)
+    for t in story.tags:
+      self.assertTrue(
+         isinstance(t, str),
+         msg='tag %s in story %s \'s tags must have type string'
+         % (str(t), story.display_name))
+    if not isinstance(story, page.Page):
+      return
+    self.assertTrue(
+       # We use basestring instead of str because story's URL can be string of
+       # unicode.
+       isinstance(story.url, basestring),
+       msg='page %s \'s url must have type string' % story.display_name)
+    self.assertTrue(
+        isinstance(story.startup_url, str),
+        msg=('page %s \'s startup_url field must have type string'
+            % story.display_name))
+    self.assertIsInstance(
+        story.make_javascript_deterministic, bool,
+        msg='page %s \'s make_javascript_deterministic must have type bool'
+            % story.display_name)
+
+  def CheckSharedStates(self, story_set):
+    if not story_set.allow_mixed_story_states:
+      shared_state_class = (
+          story_set.stories[0].shared_state_class)
+      for story in story_set:
+        self.assertIs(
+            shared_state_class,
+            story.shared_state_class,
+            msg='story %s\'s shared_state_class field is different '
+            'from other story\'s shared_state_class whereas '
+            'story set %s disallows having mixed states' %
+            (story, story_set))
+
+  def CheckPassingStoryRunnerValidation(self, story_set):
+    errors = []
+    for s in story_set:
+      try:
+        story_runner.ValidateStory(s)
+      except ValueError as e:
+        errors.append(e)
+    self.assertFalse(
+        errors, 'Errors validating user stories in %s:\n %s' % (
+            story_set, '\n'.join(e.message for e in errors)))
+
+  def RunSmokeTest(self, story_sets_dir, top_level_dir):
+    """Run smoke test on all story sets in story_sets_dir.
+
+    Subclass of StorySetSmokeTest is supposed to call this in some test
+    method to run smoke test.
+    """
+    story_sets = self.GetAllStorySetClasses(story_sets_dir, top_level_dir)
+    for story_set_class in story_sets:
+      story_set = story_set_class()
+      self.CheckArchive(story_set)
+      self.CheckCredentials(story_set)
+      self.CheckAttributes(story_set)
+      self.CheckSharedStates(story_set)
+      self.CheckPassingStoryRunnerValidation(story_set)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/stream.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/stream.py
new file mode 100644
index 0000000..4d97ab4
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/stream.py
@@ -0,0 +1,18 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+class TestOutputStream(object):
+  def __init__(self):
+    self._output_data = []
+
+  @property
+  def output_data(self):
+    return ''.join(self._output_data)
+
+  def write(self, data):
+    self._output_data.append(data)
+
+  def flush(self):
+    pass
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/system_stub.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/system_stub.py
new file mode 100644
index 0000000..78e152f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/system_stub.py
@@ -0,0 +1,491 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Provides stubs for os, sys and subprocess for testing
+
+This test allows one to test code that itself uses os, sys, and subprocess.
+"""
+
+import ntpath
+import os
+import posixpath
+import re
+import shlex
+import sys
+
+
+class Override(object):
+
+  _overidden_modules = set()
+
+  def __init__(self, base_module, module_list):
+    self.cloud_storage = None
+    self.open = None
+    self.os = None
+    self.perf_control = None
+    self.raw_input = None
+    self.subprocess = None
+    self.sys = None
+    self.thermal_throttle = None
+    self.logging = None
+    stubs = {'cloud_storage': CloudStorageModuleStub,
+             'open': OpenFunctionStub,
+             'os': OsModuleStub,
+             'perf_control': PerfControlModuleStub,
+             'raw_input': RawInputFunctionStub,
+             'subprocess': SubprocessModuleStub,
+             'sys': SysModuleStub,
+             'thermal_throttle': ThermalThrottleModuleStub,
+             'logging': LoggingStub,
+    }
+    self.adb_commands = None
+    self.os = None
+    self.subprocess = None
+    self.sys = None
+
+    assert base_module not in self._overidden_modules, (
+        '%s is already overridden' % base_module.__name__)
+    self._overidden_modules.add(base_module)
+    self._base_module = base_module
+    self._overrides = {}
+
+    for module_name in module_list:
+      self._overrides[module_name] = getattr(base_module, module_name, None)
+      setattr(self, module_name, stubs[module_name]())
+      setattr(base_module, module_name, getattr(self, module_name))
+
+    if self.os and self.sys:
+      self.os.path.sys = self.sys
+
+  def __del__(self):
+    assert not len(self._overrides)
+
+  def Restore(self):
+    for module_name, original_module in self._overrides.iteritems():
+      if original_module is None:
+        # This will happen when we override built-in functions, like open.
+        # If we don't delete the attribute, we will shadow the built-in
+        # function with an attribute set to None.
+        delattr(self._base_module, module_name)
+      else:
+        setattr(self._base_module, module_name, original_module)
+    self._overrides = {}
+    self._overidden_modules.remove(self._base_module)
+    self._base_module = None
+
+
+class AdbDevice(object):
+
+  def __init__(self):
+    self.has_root = False
+    self.needs_su = False
+    self.shell_command_handlers = {}
+    self.mock_content = []
+    self.system_properties = {}
+    if self.system_properties.get('ro.product.cpu.abi') == None:
+      self.system_properties['ro.product.cpu.abi'] = 'armeabi-v7a'
+
+  def HasRoot(self):
+    return self.has_root
+
+  def NeedsSU(self):
+    return self.needs_su
+
+  def RunShellCommand(self, args, **kwargs):
+    del kwargs  # unused
+    if isinstance(args, basestring):
+      args = shlex.split(args)
+    handler = self.shell_command_handlers[args[0]]
+    return handler(args)
+
+  def FileExists(self, _):
+    return False
+
+  def ReadFile(self, device_path, as_root=False):
+    del device_path, as_root  # unused
+    return self.mock_content
+
+  def GetProp(self, property_name):
+    return self.system_properties[property_name]
+
+  def SetProp(self, property_name, property_value):
+    self.system_properties[property_name] = property_value
+
+
+class CloudStorageModuleStub(object):
+  PUBLIC_BUCKET = 'chromium-telemetry'
+  PARTNER_BUCKET = 'chrome-partner-telemetry'
+  INTERNAL_BUCKET = 'chrome-telemetry'
+  BUCKET_ALIASES = {
+    'public': PUBLIC_BUCKET,
+    'partner': PARTNER_BUCKET,
+    'internal': INTERNAL_BUCKET,
+  }
+
+  # These are used to test for CloudStorage errors.
+  INTERNAL_PERMISSION = 2
+  PARTNER_PERMISSION = 1
+  PUBLIC_PERMISSION = 0
+  # Not logged in.
+  CREDENTIALS_ERROR_PERMISSION = -1
+
+  class NotFoundError(Exception):
+    pass
+
+  class CloudStorageError(Exception):
+    pass
+
+  class PermissionError(CloudStorageError):
+    pass
+
+  class CredentialsError(CloudStorageError):
+    pass
+
+  def __init__(self):
+    self.default_remote_paths = {CloudStorageModuleStub.INTERNAL_BUCKET:{},
+                                 CloudStorageModuleStub.PARTNER_BUCKET:{},
+                                 CloudStorageModuleStub.PUBLIC_BUCKET:{}}
+    self.remote_paths = self.default_remote_paths
+    self.local_file_hashes = {}
+    self.local_hash_files = {}
+    self.permission_level = CloudStorageModuleStub.INTERNAL_PERMISSION
+    self.downloaded_files = []
+
+  def SetPermissionLevelForTesting(self, permission_level):
+    self.permission_level = permission_level
+
+  def CheckPermissionLevelForBucket(self, bucket):
+    if bucket == CloudStorageModuleStub.PUBLIC_BUCKET:
+      return
+    elif (self.permission_level ==
+          CloudStorageModuleStub.CREDENTIALS_ERROR_PERMISSION):
+      raise CloudStorageModuleStub.CredentialsError()
+    elif bucket == CloudStorageModuleStub.PARTNER_BUCKET:
+      if self.permission_level < CloudStorageModuleStub.PARTNER_PERMISSION:
+        raise CloudStorageModuleStub.PermissionError()
+    elif bucket == CloudStorageModuleStub.INTERNAL_BUCKET:
+      if self.permission_level < CloudStorageModuleStub.INTERNAL_PERMISSION:
+        raise CloudStorageModuleStub.PermissionError()
+    elif bucket not in self.remote_paths:
+      raise CloudStorageModuleStub.NotFoundError()
+
+  def SetRemotePathsForTesting(self, remote_path_dict=None):
+    if not remote_path_dict:
+      self.remote_paths = self.default_remote_paths
+      return
+    self.remote_paths = remote_path_dict
+
+  def GetRemotePathsForTesting(self):
+    if not self.remote_paths:
+      self.remote_paths = self.default_remote_paths
+    return self.remote_paths
+
+  # Set a dictionary of data files and their "calculated" hashes.
+  def SetCalculatedHashesForTesting(self, calculated_hash_dictionary):
+    self.local_file_hashes = calculated_hash_dictionary
+
+  def GetLocalDataFiles(self):
+    return self.local_file_hashes.keys()
+
+  # Set a dictionary of hash files and the hashes they should contain.
+  def SetHashFileContentsForTesting(self, hash_file_dictionary):
+    self.local_hash_files = hash_file_dictionary
+
+  def GetLocalHashFiles(self):
+    return self.local_hash_files.keys()
+
+  def ChangeRemoteHashForTesting(self, bucket, remote_path, new_hash):
+    self.remote_paths[bucket][remote_path] = new_hash
+
+  def List(self, bucket):
+    if not bucket or not bucket in self.remote_paths:
+      bucket_error = ('Incorrect bucket specified, correct buckets:' +
+                      str(self.remote_paths))
+      raise CloudStorageModuleStub.CloudStorageError(bucket_error)
+    CloudStorageModuleStub.CheckPermissionLevelForBucket(self, bucket)
+    return list(self.remote_paths[bucket].keys())
+
+  def Exists(self, bucket, remote_path):
+    CloudStorageModuleStub.CheckPermissionLevelForBucket(self, bucket)
+    return remote_path in self.remote_paths[bucket]
+
+  def Insert(self, bucket, remote_path, local_path):
+    CloudStorageModuleStub.CheckPermissionLevelForBucket(self, bucket)
+    if not local_path in self.GetLocalDataFiles():
+      file_path_error = 'Local file path does not exist'
+      raise CloudStorageModuleStub.CloudStorageError(file_path_error)
+    self.remote_paths[bucket][remote_path] = (
+      CloudStorageModuleStub.CalculateHash(self, local_path))
+    return remote_path
+
+  def GetHelper(self, bucket, remote_path, local_path, only_if_changed):
+    CloudStorageModuleStub.CheckPermissionLevelForBucket(self, bucket)
+    if not remote_path in self.remote_paths[bucket]:
+      if only_if_changed:
+        return False
+      raise CloudStorageModuleStub.NotFoundError('Remote file does not exist.')
+    remote_hash = self.remote_paths[bucket][remote_path]
+    local_hash = self.local_file_hashes[local_path]
+    if only_if_changed and remote_hash == local_hash:
+      return False
+    self.downloaded_files.append(remote_path)
+    self.local_file_hashes[local_path] = remote_hash
+    self.local_hash_files[local_path + '.sha1'] = remote_hash
+    return remote_hash
+
+  def Get(self, bucket, remote_path, local_path):
+    return CloudStorageModuleStub.GetHelper(self, bucket, remote_path,
+                                            local_path, False)
+
+  def GetIfChanged(self, local_path, bucket=None):
+    remote_path = os.path.basename(local_path)
+    if bucket:
+      return CloudStorageModuleStub.GetHelper(self, bucket, remote_path,
+                                              local_path, True)
+    result = CloudStorageModuleStub.GetHelper(
+        self, self.PUBLIC_BUCKET, remote_path, local_path, True)
+    if not result:
+      result = CloudStorageModuleStub.GetHelper(
+          self, self.PARTNER_BUCKET, remote_path, local_path, True)
+    if not result:
+      result = CloudStorageModuleStub.GetHelper(
+          self, self.INTERNAL_BUCKET, remote_path, local_path, True)
+    return result
+
+  def GetFilesInDirectoryIfChanged(self, directory, bucket):
+    if os.path.dirname(directory) == directory: # If in the root dir.
+      raise ValueError('Trying to serve root directory from HTTP server.')
+    for dirpath, _, filenames in os.walk(directory):
+      for filename in filenames:
+        path, extension = os.path.splitext(
+            os.path.join(dirpath, filename))
+        if extension != '.sha1':
+          continue
+        self.GetIfChanged(path, bucket)
+
+  def CalculateHash(self, file_path):
+    return self.local_file_hashes[file_path]
+
+  def ReadHash(self, hash_path):
+    return self.local_hash_files[hash_path]
+
+
+class LoggingStub(object):
+  def __init__(self):
+    self.warnings = []
+    self.errors = []
+
+  def info(self, msg, *args):
+    pass
+
+  def error(self, msg, *args):
+    self.errors.append(msg % args)
+
+  def warning(self, msg, *args):
+    self.warnings.append(msg % args)
+
+  def warn(self, msg, *args):
+    self.warning(msg, *args)
+
+
+class OpenFunctionStub(object):
+  class FileStub(object):
+    def __init__(self, data):
+      self._data = data
+
+    def __enter__(self):
+      return self
+
+    def __exit__(self, *args):
+      pass
+
+    def read(self, size=None):
+      if size:
+        return self._data[:size]
+      else:
+        return self._data
+
+    def write(self, data):
+      self._data.write(data)
+
+    def close(self):
+      pass
+
+  def __init__(self):
+    self.files = {}
+
+  def __call__(self, name, *args, **kwargs):
+    return OpenFunctionStub.FileStub(self.files[name])
+
+
+class OsModuleStub(object):
+  class OsEnvironModuleStub(object):
+    def get(self, _):
+      return None
+
+  class OsPathModuleStub(object):
+    def __init__(self, sys_module):
+      self.sys = sys_module
+      self.files = []
+      self.dirs = []
+
+    def exists(self, path):
+      return path in self.files
+
+    def isfile(self, path):
+      return path in self.files
+
+    def isdir(self, path):
+      return path in self.dirs
+
+    def join(self, *paths):
+      def IsAbsolutePath(path):
+        if self.sys.platform.startswith('win'):
+          return re.match('[a-zA-Z]:\\\\', path)
+        else:
+          return path.startswith('/')
+
+      # Per Python specification, if any component is an absolute path,
+      # discard previous components.
+      for index, path in reversed(list(enumerate(paths))):
+        if IsAbsolutePath(path):
+          paths = paths[index:]
+          break
+
+      if self.sys.platform.startswith('win'):
+        tmp = os.path.join(*paths)
+        return tmp.replace('/', '\\')
+      else:
+        tmp = os.path.join(*paths)
+        return tmp.replace('\\', '/')
+
+    def basename(self, path):
+      if self.sys.platform.startswith('win'):
+        return ntpath.basename(path)
+      else:
+        return posixpath.basename(path)
+
+    @staticmethod
+    def abspath(path):
+      return os.path.abspath(path)
+
+    @staticmethod
+    def expanduser(path):
+      return os.path.expanduser(path)
+
+    @staticmethod
+    def dirname(path):
+      return os.path.dirname(path)
+
+    @staticmethod
+    def realpath(path):
+      return os.path.realpath(path)
+
+    @staticmethod
+    def split(path):
+      return os.path.split(path)
+
+    @staticmethod
+    def splitext(path):
+      return os.path.splitext(path)
+
+    @staticmethod
+    def splitdrive(path):
+      return os.path.splitdrive(path)
+
+  X_OK = os.X_OK
+
+  sep = os.sep
+  pathsep = os.pathsep
+
+  def __init__(self, sys_module=sys):
+    self.path = OsModuleStub.OsPathModuleStub(sys_module)
+    self.environ = OsModuleStub.OsEnvironModuleStub()
+    self.display = ':0'
+    self.local_app_data = None
+    self.sys_path = None
+    self.program_files = None
+    self.program_files_x86 = None
+    self.devnull = os.devnull
+    self._directory = {}
+
+  def access(self, path, _):
+    return path in self.path.files
+
+  def getenv(self, name, value=None):
+    if name == 'DISPLAY':
+      env = self.display
+    elif name == 'LOCALAPPDATA':
+      env = self.local_app_data
+    elif name == 'PATH':
+      env = self.sys_path
+    elif name == 'PROGRAMFILES':
+      env = self.program_files
+    elif name == 'PROGRAMFILES(X86)':
+      env = self.program_files_x86
+    else:
+      raise NotImplementedError('Unsupported getenv')
+    return env if env else value
+
+  def chdir(self, path):
+    pass
+
+  def walk(self, top):
+    for dir_name in self._directory:
+      yield top, dir_name, self._directory[dir_name]
+
+
+class PerfControlModuleStub(object):
+  class PerfControlStub(object):
+    def __init__(self, adb):
+      pass
+
+  def __init__(self):
+    self.PerfControl = PerfControlModuleStub.PerfControlStub
+
+
+class RawInputFunctionStub(object):
+  def __init__(self):
+    self.input = ''
+
+  def __call__(self, name, *args, **kwargs):
+    return self.input
+
+
+class SubprocessModuleStub(object):
+  class PopenStub(object):
+    def __init__(self):
+      self.communicate_result = ('', '')
+      self.returncode_result = 0
+
+    def __call__(self, args, **kwargs):
+      return self
+
+    def communicate(self):
+      return self.communicate_result
+
+    @property
+    def returncode(self):
+      return self.returncode_result
+
+  def __init__(self):
+    self.Popen = SubprocessModuleStub.PopenStub()
+    self.PIPE = None
+
+  def call(self, *args, **kwargs):
+    pass
+
+
+class SysModuleStub(object):
+  def __init__(self):
+    self.platform = ''
+
+
+class ThermalThrottleModuleStub(object):
+  class ThermalThrottleStub(object):
+    def __init__(self, adb):
+      pass
+
+  def __init__(self):
+    self.ThermalThrottle = ThermalThrottleModuleStub.ThermalThrottleStub
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/system_stub_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/system_stub_unittest.py
new file mode 100644
index 0000000..5a23ed4
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/system_stub_unittest.py
@@ -0,0 +1,251 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import unittest
+
+PERF_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+from telemetry.testing import system_stub
+from telemetry.internal.testing import system_stub_test_module
+
+class CloudStorageTest(unittest.TestCase):
+  SUCCESS_FILE_HASH = 'success'.zfill(40)
+  PUBLIC_FILE_HASH = 'public'.zfill(40)
+  PARTNER_FILE_HASH = 'partner'.zfill(40)
+  INTERNAL_FILE_HASH = 'internal'.zfill(40)
+  UPDATED_HASH = 'updated'.zfill(40)
+
+  def setUp(self):
+    self.cloud_storage = system_stub.CloudStorageModuleStub()
+
+    # Files in Cloud Storage.
+    self.remote_files = ['preset_public_file.wpr',
+                         'preset_partner_file.wpr',
+                         'preset_internal_file.wpr']
+    self.remote_paths = {
+      self.cloud_storage.PUBLIC_BUCKET:
+        {'preset_public_file.wpr':CloudStorageTest.PUBLIC_FILE_HASH},
+      self.cloud_storage.PARTNER_BUCKET:
+        {'preset_partner_file.wpr':CloudStorageTest.PARTNER_FILE_HASH},
+      self.cloud_storage.INTERNAL_BUCKET:
+        {'preset_internal_file.wpr':CloudStorageTest.INTERNAL_FILE_HASH}}
+
+    # Local data files and hashes.
+    self.data_files = [
+        os.path.join(os.path.sep, 'path', 'to', 'success.wpr'),
+        os.path.join(os.path.sep, 'path', 'to', 'wrong_hash.wpr'),
+        os.path.join(os.path.sep, 'path', 'to', 'preset_public_file.wpr'),
+        os.path.join(os.path.sep, 'path', 'to', 'preset_partner_file.wpr'),
+        os.path.join(os.path.sep, 'path', 'to', 'preset_internal_file.wpr')]
+    self.local_file_hashes = {
+        os.path.join(os.path.sep, 'path', 'to', 'success.wpr'):
+            CloudStorageTest.SUCCESS_FILE_HASH,
+        os.path.join(os.path.sep, 'path', 'to', 'wrong_hash.wpr'):
+            CloudStorageTest.SUCCESS_FILE_HASH,
+        os.path.join(os.path.sep, 'path', 'to', 'preset_public_file.wpr'):
+            CloudStorageTest.PUBLIC_FILE_HASH,
+        os.path.join(os.path.sep, 'path', 'to', 'preset_partner_file.wpr'):
+            CloudStorageTest.PARTNER_FILE_HASH,
+        os.path.join(os.path.sep, 'path', 'to', 'preset_internal_file.wpr'):
+            CloudStorageTest.INTERNAL_FILE_HASH,
+    }
+    self.cloud_storage.SetCalculatedHashesForTesting(self.local_file_hashes)
+    # Local hash files and their contents.
+    local_hash_files = {
+        os.path.join(os.path.sep, 'path', 'to', 'success.wpr.sha1'):
+            CloudStorageTest.SUCCESS_FILE_HASH,
+        os.path.join(os.path.sep, 'path', 'to', 'wrong_hash.wpr.sha1'):
+            'wronghash'.zfill(40),
+        os.path.join(os.path.sep, 'path', 'to', 'preset_public_file.wpr.sha1'):
+            CloudStorageTest.PUBLIC_FILE_HASH,
+        os.path.join(os.path.sep, 'path', 'to', 'preset_partner_file.wpr.sha1'):
+            CloudStorageTest.PARTNER_FILE_HASH,
+        os.path.join(os.path.sep, 'path', 'to',
+                     'preset_internal_file.wpr.sha1'):
+            CloudStorageTest.INTERNAL_FILE_HASH,
+    }
+    self.cloud_storage.SetHashFileContentsForTesting(local_hash_files)
+
+  def testSetup(self):
+    self.assertEqual(self.local_file_hashes,
+                     self.cloud_storage.local_file_hashes)
+    self.assertEqual(set(self.data_files),
+                     set(self.cloud_storage.GetLocalDataFiles()))
+    self.assertEqual(self.cloud_storage.default_remote_paths,
+                     self.cloud_storage.GetRemotePathsForTesting())
+    self.cloud_storage.SetRemotePathsForTesting(self.remote_paths)
+    self.assertEqual(self.remote_paths,
+                     self.cloud_storage.GetRemotePathsForTesting())
+
+  def testExistsEmptyCloudStorage(self):
+    # Test empty remote files dictionary.
+    self.assertFalse(self.cloud_storage.Exists(self.cloud_storage.PUBLIC_BUCKET,
+                                               'preset_public_file.wpr'))
+    self.assertFalse(self.cloud_storage.Exists(
+        self.cloud_storage.PARTNER_BUCKET, 'preset_partner_file.wpr'))
+    self.assertFalse(self.cloud_storage.Exists(
+        self.cloud_storage.INTERNAL_BUCKET, 'preset_internal_file.wpr'))
+
+  def testExistsNonEmptyCloudStorage(self):
+    # Test non-empty remote files dictionary.
+    self.cloud_storage.SetRemotePathsForTesting(self.remote_paths)
+    self.assertTrue(self.cloud_storage.Exists(
+        self.cloud_storage.PUBLIC_BUCKET, 'preset_public_file.wpr'))
+    self.assertTrue(self.cloud_storage.Exists(
+        self.cloud_storage.PARTNER_BUCKET, 'preset_partner_file.wpr'))
+    self.assertTrue(self.cloud_storage.Exists(
+        self.cloud_storage.INTERNAL_BUCKET, 'preset_internal_file.wpr'))
+    self.assertFalse(self.cloud_storage.Exists(
+        self.cloud_storage.PUBLIC_BUCKET, 'fake_file'))
+    self.assertFalse(self.cloud_storage.Exists(
+        self.cloud_storage.PARTNER_BUCKET, 'fake_file'))
+    self.assertFalse(self.cloud_storage.Exists(
+        self.cloud_storage.INTERNAL_BUCKET, 'fake_file'))
+    # Reset state.
+    self.cloud_storage.SetRemotePathsForTesting()
+
+  def testNonEmptyInsertAndExistsPublic(self):
+    # Test non-empty remote files dictionary.
+    self.cloud_storage.SetRemotePathsForTesting(self.remote_paths)
+    self.assertFalse(self.cloud_storage.Exists(self.cloud_storage.PUBLIC_BUCKET,
+                                               'success.wpr'))
+    self.cloud_storage.Insert(
+        self.cloud_storage.PUBLIC_BUCKET, 'success.wpr',
+        os.path.join(os.path.sep, 'path', 'to', 'success.wpr'))
+    self.assertTrue(self.cloud_storage.Exists(
+        self.cloud_storage.PUBLIC_BUCKET, 'success.wpr'))
+    # Reset state.
+    self.cloud_storage.SetRemotePathsForTesting()
+
+  def testEmptyInsertAndExistsPublic(self):
+    # Test empty remote files dictionary.
+    self.assertFalse(self.cloud_storage.Exists(
+        self.cloud_storage.PUBLIC_BUCKET, 'success.wpr'))
+    self.cloud_storage.Insert(
+        self.cloud_storage.PUBLIC_BUCKET, 'success.wpr',
+        os.path.join(os.path.sep, 'path', 'to', 'success.wpr'))
+    self.assertTrue(self.cloud_storage.Exists(
+        self.cloud_storage.PUBLIC_BUCKET, 'success.wpr'))
+
+  def testEmptyInsertAndGet(self):
+    self.assertRaises(self.cloud_storage.NotFoundError, self.cloud_storage.Get,
+                      self.cloud_storage.PUBLIC_BUCKET, 'success.wpr',
+                      os.path.join(os.path.sep, 'path', 'to', 'success.wpr'))
+    self.cloud_storage.Insert(self.cloud_storage.PUBLIC_BUCKET, 'success.wpr',
+                              os.path.join(os.path.sep, 'path', 'to',
+                                           'success.wpr'))
+    self.assertTrue(self.cloud_storage.Exists(
+        self.cloud_storage.PUBLIC_BUCKET, 'success.wpr'))
+    self.assertEqual(CloudStorageTest.SUCCESS_FILE_HASH, self.cloud_storage.Get(
+        self.cloud_storage.PUBLIC_BUCKET, 'success.wpr',
+        os.path.join(os.path.sep, 'path', 'to', 'success.wpr')))
+
+  def testNonEmptyInsertAndGet(self):
+    self.cloud_storage.SetRemotePathsForTesting(self.remote_paths)
+    self.assertRaises(self.cloud_storage.NotFoundError, self.cloud_storage.Get,
+                      self.cloud_storage.PUBLIC_BUCKET, 'success.wpr',
+                      os.path.join(os.path.sep, 'path', 'to', 'success.wpr'))
+    self.cloud_storage.Insert(self.cloud_storage.PUBLIC_BUCKET, 'success.wpr',
+                              os.path.join(os.path.sep, 'path', 'to',
+                                           'success.wpr'))
+    self.assertTrue(self.cloud_storage.Exists(self.cloud_storage.PUBLIC_BUCKET,
+                                              'success.wpr'))
+    self.assertEqual(
+        CloudStorageTest.SUCCESS_FILE_HASH, self.cloud_storage.Get(
+            self.cloud_storage.PUBLIC_BUCKET, 'success.wpr',
+            os.path.join(os.path.sep, 'path', 'to', 'success.wpr')))
+    # Reset state.
+    self.cloud_storage.SetRemotePathsForTesting()
+
+  def testGetIfChanged(self):
+    self.cloud_storage.SetRemotePathsForTesting(self.remote_paths)
+    self.assertRaises(
+        self.cloud_storage.NotFoundError, self.cloud_storage.Get,
+        self.cloud_storage.PUBLIC_BUCKET, 'success.wpr',
+        os.path.join(os.path.sep, 'path', 'to', 'success.wpr'))
+    self.assertFalse(self.cloud_storage.GetIfChanged(
+        os.path.join(os.path.sep, 'path', 'to', 'preset_public_file.wpr'),
+        self.cloud_storage.PUBLIC_BUCKET))
+    self.cloud_storage.ChangeRemoteHashForTesting(
+        self.cloud_storage.PUBLIC_BUCKET, 'preset_public_file.wpr',
+        CloudStorageTest.UPDATED_HASH)
+    self.assertTrue(self.cloud_storage.GetIfChanged(
+        os.path.join(os.path.sep, 'path', 'to', 'preset_public_file.wpr'),
+        self.cloud_storage.PUBLIC_BUCKET))
+    self.assertFalse(self.cloud_storage.GetIfChanged(
+        os.path.join(os.path.sep, 'path', 'to', 'preset_public_file.wpr'),
+        self.cloud_storage.PUBLIC_BUCKET))
+    # Reset state.
+    self.cloud_storage.SetRemotePathsForTesting()
+
+  def testList(self):
+    self.assertEqual([],
+                     self.cloud_storage.List(self.cloud_storage.PUBLIC_BUCKET))
+    self.cloud_storage.SetRemotePathsForTesting(self.remote_paths)
+    self.assertEqual(['preset_public_file.wpr'],
+                     self.cloud_storage.List(self.cloud_storage.PUBLIC_BUCKET))
+    # Reset state.
+    self.cloud_storage.SetRemotePathsForTesting()
+
+  def testPermissionError(self):
+    self.cloud_storage.SetRemotePathsForTesting(self.remote_paths)
+    self.cloud_storage.SetPermissionLevelForTesting(
+        self.cloud_storage.PUBLIC_PERMISSION)
+    self.assertRaises(
+        self.cloud_storage.PermissionError, self.cloud_storage.Get,
+        self.cloud_storage.INTERNAL_BUCKET, 'preset_internal_file.wpr',
+        os.path.join(os.path.sep, 'path', 'to', 'preset_internal_file.wpr'))
+    self.assertRaises(
+        self.cloud_storage.PermissionError, self.cloud_storage.GetIfChanged,
+        os.path.join(os.path.sep, 'path', 'to', 'preset_internal_file.wpr'),
+        self.cloud_storage.INTERNAL_BUCKET)
+    self.assertRaises(
+        self.cloud_storage.PermissionError, self.cloud_storage.List,
+        self.cloud_storage.INTERNAL_BUCKET)
+    self.assertRaises(
+        self.cloud_storage.PermissionError, self.cloud_storage.Exists,
+        self.cloud_storage.INTERNAL_BUCKET, 'preset_internal_file.wpr')
+    self.assertRaises(
+        self.cloud_storage.PermissionError, self.cloud_storage.Insert,
+        self.cloud_storage.INTERNAL_BUCKET, 'success.wpr',
+        os.path.join(os.path.sep, 'path', 'to', 'success.wpr'))
+    # Reset state.
+    self.cloud_storage.SetRemotePathsForTesting()
+
+  def testCredentialsError(self):
+    self.cloud_storage.SetRemotePathsForTesting(self.remote_paths)
+    self.cloud_storage.SetPermissionLevelForTesting(
+        self.cloud_storage.CREDENTIALS_ERROR_PERMISSION)
+    self.assertRaises(
+        self.cloud_storage.CredentialsError, self.cloud_storage.Get,
+        self.cloud_storage.INTERNAL_BUCKET, 'preset_internal_file.wpr',
+        os.path.join(os.path.sep, 'path', 'to', 'preset_internal_file.wpr'))
+    self.assertRaises(
+        self.cloud_storage.CredentialsError, self.cloud_storage.GetIfChanged,
+        self.cloud_storage.INTERNAL_BUCKET,
+        os.path.join(os.path.sep, 'path', 'to', 'preset_internal_file.wpr'))
+    self.assertRaises(
+        self.cloud_storage.CredentialsError, self.cloud_storage.List,
+        self.cloud_storage.INTERNAL_BUCKET)
+    self.assertRaises(
+        self.cloud_storage.CredentialsError, self.cloud_storage.Exists,
+        self.cloud_storage.INTERNAL_BUCKET, 'preset_internal_file.wpr')
+    self.assertRaises(
+        self.cloud_storage.CredentialsError, self.cloud_storage.Insert,
+        self.cloud_storage.INTERNAL_BUCKET, 'success.wpr',
+        os.path.join(os.path.sep, 'path', 'to', 'success.wpr'))
+    # Reset state.
+    self.cloud_storage.SetRemotePathsForTesting()
+
+  def testOpenRestoresCorrectly(self):
+    file_path = os.path.realpath(__file__)
+    stubs = system_stub.Override(system_stub_test_module, ['open'])
+    stubs.open.files = {file_path:'contents'}
+    f = system_stub_test_module.SystemStubTest.TestOpen(file_path)
+    self.assertEqual(type(f), system_stub.OpenFunctionStub.FileStub)
+    stubs.open.files = {}
+    stubs.Restore()
+    # This will throw an error if the open stub wasn't restored correctly.
+    f = system_stub_test_module.SystemStubTest.TestOpen(file_path)
+    self.assertEqual(type(f), file)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/tab_test_case.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/tab_test_case.py
new file mode 100644
index 0000000..2fff0ca
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/tab_test_case.py
@@ -0,0 +1,49 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.core import exceptions
+from telemetry.testing import browser_test_case
+
+
+class TabTestCase(browser_test_case.BrowserTestCase):
+  def __init__(self, *args):
+    super(TabTestCase, self).__init__(*args)
+    self._tab = None
+
+  def setUp(self):
+    super(TabTestCase, self).setUp()
+
+    if self._browser.supports_tab_control:
+      try:
+        while len(self._browser.tabs) < 1:
+          self._browser.tabs.New()
+        while len(self._browser.tabs) > 1:
+          self._browser.tabs[0].Close()
+        self._tab = self._browser.tabs[0]
+      except exceptions.TimeoutException:
+        self._RestartBrowser()
+    else:
+      self._RestartBrowser()
+    self._tab.Navigate('about:blank')
+    self._tab.WaitForDocumentReadyStateToBeInteractiveOrBetter()
+
+  def Navigate(self, filename, script_to_evaluate_on_commit=None):
+    """Navigates |tab| to |filename| in the unittest data directory.
+
+    Also sets up http server to point to the unittest data directory.
+    """
+    url = self.UrlOfUnittestFile(filename)
+    self._tab.Navigate(url, script_to_evaluate_on_commit)
+    self._tab.WaitForDocumentReadyStateToBeComplete()
+    self._tab.WaitForFrameToBeDisplayed()
+
+  def _RestartBrowser(self):
+    if not self._browser.tabs:
+      self.tearDownClass()
+      self.setUpClass()
+    self._tab = self._browser.tabs[0]
+
+  @property
+  def tabs(self):
+    return self._browser.tabs
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/test_page_test_results.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/test_page_test_results.py
new file mode 100644
index 0000000..3770c72
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/test_page_test_results.py
@@ -0,0 +1,40 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.internal.results import page_test_results
+from telemetry.page import page as page_module
+from telemetry.value import list_of_scalar_values
+from telemetry.value import scalar
+
+
+class TestPageTestResults(
+    page_test_results.PageTestResults):
+  def __init__(self, test):
+    super(TestPageTestResults, self).__init__()
+    self.test = test
+    page = page_module.Page("http://www.google.com", {})
+    self.WillRunPage(page)
+
+  def GetPageSpecificValueNamed(self, name):
+    values = [value for value in self.all_page_specific_values
+         if value.name == name]
+    assert len(values) == 1, 'Could not find value named %s' % name
+    return values[0]
+
+  def AssertHasPageSpecificScalarValue(self, name, units, expected_value):
+    value = self.GetPageSpecificValueNamed(name)
+    self.test.assertEquals(units, value.units)
+    self.test.assertTrue(isinstance(value, scalar.ScalarValue))
+    self.test.assertEquals(expected_value, value.value)
+
+  def AssertHasPageSpecificListOfScalarValues(self, name, units,
+                                              expected_values):
+    value = self.GetPageSpecificValueNamed(name)
+    self.test.assertEquals(units, value.units)
+    self.test.assertTrue(
+        isinstance(value, list_of_scalar_values.ListOfScalarValues))
+    self.test.assertItemsEqual(expected_values, value.values)
+
+  def __str__(self):
+    return '\n'.join([repr(x) for x in self.all_page_specific_values])
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/unittest_runner.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/unittest_runner.py
new file mode 100644
index 0000000..1a4c21b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/testing/unittest_runner.py
@@ -0,0 +1,43 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import subprocess
+import sys
+
+from telemetry.core import util
+
+
+def Run(project_config, no_browser=False,
+        disable_cloud_storage_io_during_test=False):
+  args = sys.argv[1:]
+  assert '--top-level-dir' not in args, (
+      'Top level directory for running tests should be specified through '
+      'the instance of telemetry.project_config.ProjectConfig.')
+  assert '--client-config' not in args, (
+      'Client config file to be used for telemetry should be specified through '
+      'the instance of telemetry.project_config.ProjectConfig.')
+  assert project_config.top_level_dir, 'Must specify top level dir for project'
+  args.extend(['--top-level-dir', project_config.top_level_dir])
+  for c in project_config.client_configs:
+    args.extend(['--client-config', c])
+  if no_browser and not '--no-browser' in args:
+    args.extend(['--no-browser'])
+
+  if project_config.default_chrome_root and not '--chrome-root' in args:
+    args.extend(['--chrome-root', project_config.default_chrome_root])
+
+  if disable_cloud_storage_io_during_test:
+    args.extend(['--disable-cloud-storage-io'])
+
+  env = os.environ.copy()
+  telemetry_dir = util.GetTelemetryDir()
+  if 'PYTHONPATH' in env:
+    env['PYTHONPATH'] = os.pathsep.join([env['PYTHONPATH'], telemetry_dir])
+  else:
+    env['PYTHONPATH'] = telemetry_dir
+
+  path_to_run_tests = os.path.join(os.path.abspath(os.path.dirname(__file__)),
+                                   'run_tests.py')
+  return subprocess.call([sys.executable, path_to_run_tests] + args, env=env)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/__init__.py
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/async_slice.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/async_slice.py
new file mode 100644
index 0000000..2a5068f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/async_slice.py
@@ -0,0 +1,31 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import telemetry.timeline.event as event
+
+
+class AsyncSlice(event.TimelineEvent):
+  """An AsyncSlice represents an interval of time during which an
+  asynchronous operation is in progress. An AsyncSlice consumes no CPU time
+  itself and so is only associated with Threads at its start and end point.
+  """
+  def __init__(self, category, name, timestamp, args=None,
+               duration=0, start_thread=None, end_thread=None,
+               thread_start=None, thread_duration=None):
+    super(AsyncSlice, self).__init__(
+        category, name, timestamp, duration, thread_start, thread_duration,
+        args)
+    self.parent_slice = None
+    self.start_thread = start_thread
+    self.end_thread = end_thread
+    self.sub_slices = []
+    self.id = None
+
+  def AddSubSlice(self, sub_slice):
+    assert sub_slice.parent_slice == self
+    self.sub_slices.append(sub_slice)
+
+  def IterEventsInThisContainerRecrusively(self):
+    for sub_slice in self.sub_slices:
+      yield sub_slice
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/atrace_config.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/atrace_config.py
new file mode 100644
index 0000000..4956009
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/atrace_config.py
@@ -0,0 +1,18 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from systrace.tracing_agents import atrace_agent
+
+class AtraceConfig(object):
+  """Stores configuration options specific to Atrace.
+
+    categories: List that specifies the Atrace categories to trace.
+        Example: ['sched', 'webview']
+    app_name: String or list that specifies the application name (or names)
+        on which to run application level tracing.
+        Example: 'org.chromium.webview_shell'
+  """
+  def __init__(self):
+    self.categories = atrace_agent.DEFAULT_CATEGORIES
+    self.app_name = None
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/bounds.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/bounds.py
new file mode 100644
index 0000000..dd7a4ef
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/bounds.py
@@ -0,0 +1,114 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+class Bounds(object):
+  """Represents a min-max bounds."""
+  def __init__(self):
+    self.is_empty_ = True
+    self.min_ = None
+    self.max_ = None
+
+  @staticmethod
+  def CreateFromEvent(event):
+    bounds = Bounds()
+    bounds.AddEvent(event)
+    return bounds
+
+  def __repr__(self):
+    if self.is_empty_:
+      return "Bounds()"
+    else:
+      return "Bounds(min=%s,max=%s)" % (self.min_, self.max_)
+
+  @property
+  def is_empty(self):
+    return self.is_empty_
+
+  @property
+  def min(self):
+    if self.is_empty_:
+      return None
+    return self.min_
+
+  @property
+  def max(self):
+    if self.is_empty_:
+      return None
+    return self.max_
+
+  @property
+  def bounds(self):
+    if self.is_empty_:
+      return None
+    return self.max_ - self.min_
+
+  @property
+  def center(self):
+    return (self.min_ + self.max_) * 0.5
+
+  def Contains(self, other):
+    if self.is_empty or other.is_empty:
+      return False
+    return self.min <= other.min and self.max >= other.max
+
+  def ContainsInterval(self, start, end):
+    return self.min <= start and self.max >= end
+
+  def Intersects(self, other):
+    if self.is_empty or other.is_empty:
+      return False
+    return not (other.max < self.min or other.min > self.max)
+
+  def Reset(self):
+    self.is_empty_ = True
+    self.min_ = None
+    self.max_ = None
+
+  def AddBounds(self, bounds):
+    if bounds.is_empty:
+      return
+    self.AddValue(bounds.min_)
+    self.AddValue(bounds.max_)
+
+  def AddValue(self, value):
+    if self.is_empty_:
+      self.max_ = value
+      self.min_ = value
+      self.is_empty_ = False
+      return
+
+    self.max_ = max(self.max_, value)
+    self.min_ = min(self.min_, value)
+
+  def AddEvent(self, event):
+    self.AddValue(event.start)
+    self.AddValue(event.start + event.duration)
+
+  @staticmethod
+  def CompareByMinTimes(a, b):
+    if not a.is_empty and not b.is_empty:
+      return a.min_ - b.min_
+
+    if a.is_empty and not b.is_empty:
+      return -1
+
+    if not a.is_empty and b.is_empty:
+      return 1
+
+    return 0
+
+  @staticmethod
+  def GetOverlapBetweenBounds(first_bounds, second_bounds):
+    """Compute the overlap duration between first_bounds and second_bounds."""
+    return Bounds.GetOverlap(first_bounds.min_, first_bounds.max_,
+                             second_bounds.min_, second_bounds.max_)
+
+  @staticmethod
+  def GetOverlap(first_bounds_min, first_bounds_max,
+                 second_bounds_min, second_bounds_max):
+    assert first_bounds_min <= first_bounds_max
+    assert second_bounds_min <= second_bounds_max
+    overlapped_range_start = max(first_bounds_min, second_bounds_min)
+    overlapped_range_end = min(first_bounds_max, second_bounds_max)
+    return max(overlapped_range_end - overlapped_range_start, 0)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/bounds_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/bounds_unittest.py
new file mode 100644
index 0000000..0b06124
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/bounds_unittest.py
@@ -0,0 +1,20 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import unittest
+
+from telemetry.timeline import bounds
+
+
+class BoundsTests(unittest.TestCase):
+
+  def testGetOverlap(self):
+    # Non overlap cases.
+    self.assertEquals(0, bounds.Bounds.GetOverlap(10, 20, 30, 40))
+    self.assertEquals(0, bounds.Bounds.GetOverlap(30, 40, 10, 20))
+    # Overlap cases.
+    self.assertEquals(10, bounds.Bounds.GetOverlap(10, 30, 20, 40))
+    self.assertEquals(10, bounds.Bounds.GetOverlap(20, 40, 10, 30))
+    # Inclusive cases.
+    self.assertEquals(10, bounds.Bounds.GetOverlap(10, 40, 20, 30))
+    self.assertEquals(10, bounds.Bounds.GetOverlap(20, 30, 10, 40))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/chrome_trace_category_filter.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/chrome_trace_category_filter.py
new file mode 100644
index 0000000..203c5e9
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/chrome_trace_category_filter.py
@@ -0,0 +1,220 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import re
+
+
+def CreateLowOverheadFilter():
+  """Returns a filter with the least overhead possible.
+
+  This contains no sub-traces of thread tasks, so it's only useful for
+  capturing the cpu-time spent on threads (as well as needed benchmark
+  traces).
+
+  FIXME: Remove webkit.console when blink.console lands in chromium and
+  the ref builds are updated. crbug.com/386847
+  """
+  categories = [
+    "toplevel",
+    "benchmark",
+    "webkit.console",
+    "blink.console",
+    "trace_event_overhead"
+  ]
+  return ChromeTraceCategoryFilter(filter_string=','.join(categories))
+
+
+def CreateDefaultOverheadFilter():
+  """Returns a filter with the best-effort amount of overhead.
+
+  This matches Chrome tracing's default category filter setting, i.e., enable
+  all categories except the disabled-by-default-* ones.
+
+  We should use '*' instead of '' (empty string) here. On the Chrome side, both
+  '*' and '' mean default category filter setting. However, if someone adds
+  additional category filters, the behavior becomes different.
+
+  For example:
+  '*': enable all categories except the disabled-by-default-* ones.
+  '':  enable all categories except the disabled-by-default-* ones.
+
+  Now add an additional category filter 'abc' to '*' and '':
+  '*,abc': enable all categories (including 'abc') except the
+           disabled-by-default-* ones.
+  'abc':   enable only 'abc', and disable all other ones.
+  """
+  return ChromeTraceCategoryFilter(filter_string='*')
+
+
+def CreateDebugOverheadFilter():
+  """Returns a filter with as many traces enabled as is useful."""
+  return ChromeTraceCategoryFilter(
+      filter_string='*,disabled-by-default-cc.debug')
+
+
+_delay_re = re.compile(r'DELAY[(][A-Za-z0-9._;]+[)]')
+
+
+class ChromeTraceCategoryFilter(object):
+  """A set of included and excluded categories that should be traced.
+
+  The ChromeTraceCategoryFilter allows fine tuning of what data is traced for
+  Chrome. Basic choice of which tracers to use is done by TracingConfig.
+
+  Providing filter_string=None gives the default category filter, which leaves
+  what to trace up to the individual trace systems.
+  """
+  def __init__(self, filter_string=None):
+    self._included_categories = set()
+    self._excluded_categories = set()
+    self._disabled_by_default_categories = set()
+    self._synthetic_delays = set()
+    self.contains_wildcards = False
+    self.AddFilterString(filter_string)
+
+  def AddFilterString(self, filter_string):
+    if filter_string == None:
+      return
+
+    if '*' in filter_string or '?' in filter_string:
+      self.contains_wildcards = True
+
+    filter_set = set([cf.strip() for cf in filter_string.split(',')])
+    for category in filter_set:
+      if category == '':
+        continue
+
+      if _delay_re.match(category):
+        self._synthetic_delays.add(category)
+        continue
+
+      if category[0] == '-':
+        assert not category[1:] in self._included_categories
+        self._excluded_categories.add(category[1:])
+        continue
+
+      if category.startswith('disabled-by-default-'):
+        self._disabled_by_default_categories.add(category)
+        continue
+
+      assert not category in self._excluded_categories
+      self._included_categories.add(category)
+
+  @property
+  def included_categories(self):
+    return self._included_categories
+
+  @property
+  def excluded_categories(self):
+    return self._excluded_categories
+
+  @property
+  def disabled_by_default_categories(self):
+    return self._disabled_by_default_categories
+
+  @property
+  def synthetic_delays(self):
+    return self._synthetic_delays
+
+  @property
+  def filter_string(self):
+    return self._GetFilterString(stable_output=False)
+
+  @property
+  def stable_filter_string(self):
+    return self._GetFilterString(stable_output=True)
+
+  def _GetFilterString(self, stable_output):
+    # Note: This outputs fields in an order that intentionally matches
+    # trace_event_impl's CategoryFilter string order.
+    lists = []
+    lists.append(self._included_categories)
+    lists.append(self._disabled_by_default_categories)
+    lists.append(['-%s' % x for x in self._excluded_categories])
+    lists.append(self._synthetic_delays)
+    categories = []
+    for l in lists:
+      if stable_output:
+        l = list(l)
+        l.sort()
+      categories.extend(l)
+    return ','.join(categories)
+
+  def GetDictForChromeTracing(self):
+    INCLUDED_CATEGORIES_PARAM = 'included_categories'
+    EXCLUDED_CATEGORIES_PARAM = 'excluded_categories'
+    SYNTHETIC_DELAYS_PARAM = 'synthetic_delays'
+
+    result = {}
+    if self._included_categories or self._disabled_by_default_categories:
+      result[INCLUDED_CATEGORIES_PARAM] = list(
+        self._included_categories | self._disabled_by_default_categories)
+    if self._excluded_categories:
+      result[EXCLUDED_CATEGORIES_PARAM] = list(self._excluded_categories)
+    if self._synthetic_delays:
+      result[SYNTHETIC_DELAYS_PARAM] = list(self._synthetic_delays)
+    return result
+
+  def AddDisabledByDefault(self, category):
+    assert category.startswith('disabled-by-default-')
+    self._disabled_by_default_categories.add(category)
+
+  def AddIncludedCategory(self, category_glob):
+    """Explicitly enables anything matching category_glob."""
+    assert not category_glob.startswith('disabled-by-default-')
+    assert not category_glob in self._excluded_categories
+    self._included_categories.add(category_glob)
+
+  def AddExcludedCategory(self, category_glob):
+    """Explicitly disables anything matching category_glob."""
+    assert not category_glob.startswith('disabled-by-default-')
+    assert not category_glob in self._included_categories
+    self._excluded_categories.add(category_glob)
+
+  def AddSyntheticDelay(self, delay):
+    assert _delay_re.match(delay)
+    self._synthetic_delays.add(delay)
+
+  def IsSubset(self, other):
+    """ Determine if filter A (self) is a subset of filter B (other).
+        Returns True if A is a subset of B, False if A is not a subset of B,
+        and None if we can't tell for sure.
+    """
+    # We don't handle filters with wildcards in this test.
+    if self.contains_wildcards or other.contains_wildcards:
+      return None
+
+    # Disabled categories get into a trace if and only if they are contained in
+    # the 'disabled' set. Return False if A's disabled set is not a subset of
+    # B's disabled set.
+    if not self.disabled_by_default_categories <= \
+       other.disabled_by_default_categories:
+      return False
+
+    # If A defines more or different synthetic delays than B, then A is not a
+    # subset.
+    if not self.synthetic_delays <= other.synthetic_delays:
+      return False
+
+    if self.included_categories and other.included_categories:
+      # A and B have explicit include lists. If A includes something that B
+      # doesn't, return False.
+      if not self.included_categories <= other.included_categories:
+        return False
+    elif self.included_categories:
+      # Only A has an explicit include list. If A includes something that B
+      # excludes, return False.
+      if self.included_categories.intersection(other.excluded_categories):
+        return False
+    elif other.included_categories:
+      # Only B has an explicit include list. We don't know which categories are
+      # contained in the default list, so return None.
+      return None
+    else:
+      # None of the filter have explicit include list. If B excludes categories
+      # that A doesn't exclude, return False.
+      if not other.excluded_categories <= self.excluded_categories:
+        return False
+
+    return True
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/chrome_trace_category_filter_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/chrome_trace_category_filter_unittest.py
new file mode 100644
index 0000000..9bcb377
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/chrome_trace_category_filter_unittest.py
@@ -0,0 +1,176 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from telemetry.timeline import chrome_trace_category_filter
+
+
+class ChromeTraceCategoryFilterTest(unittest.TestCase):
+  def CheckBasicCategoryFilters(self, cf):
+    self.assertEquals(set(['x']), set(cf.included_categories))
+    self.assertEquals(set(['y']), set(cf.excluded_categories))
+    self.assertEquals(set(['disabled-by-default-z']),
+        set(cf.disabled_by_default_categories))
+    self.assertEquals(set(['DELAY(7;foo)']), set(cf.synthetic_delays))
+
+    self.assertTrue('x' in cf.filter_string)
+    self.assertEquals(
+        'x,disabled-by-default-z,-y,DELAY(7;foo)',
+        cf.stable_filter_string)
+
+  def testBasic(self):
+    cf = chrome_trace_category_filter.ChromeTraceCategoryFilter(
+        'x,-y,disabled-by-default-z,DELAY(7;foo)')
+    self.CheckBasicCategoryFilters(cf)
+
+  def testBasicWithSpace(self):
+    cf = chrome_trace_category_filter.ChromeTraceCategoryFilter(
+        ' x ,\n-y\t,disabled-by-default-z ,DELAY(7;foo)')
+    self.CheckBasicCategoryFilters(cf)
+
+  def testNoneAndEmptyCategory(self):
+    a = chrome_trace_category_filter.ChromeTraceCategoryFilter()
+    self.assertEquals(a.stable_filter_string, '')
+    self.assertEquals(a.filter_string, '')
+    self.assertEquals(str(a.GetDictForChromeTracing()), '{}')
+
+    # Initializing chrome trace category filter with empty string is the same
+    # as initialization with None.
+    b = chrome_trace_category_filter.ChromeTraceCategoryFilter(filter_string='')
+    self.assertEquals(b.stable_filter_string, '')
+    self.assertEquals(b.filter_string, '')
+    self.assertEquals(str(b.GetDictForChromeTracing()), '{}')
+
+  def testAddIncludedCategory(self):
+    a = chrome_trace_category_filter.ChromeTraceCategoryFilter()
+    a.AddIncludedCategory('foo')
+    a.AddIncludedCategory('bar')
+    a.AddIncludedCategory('foo')
+    self.assertEquals(a.stable_filter_string, 'bar,foo')
+
+  def testAddExcludedCategory(self):
+    a = chrome_trace_category_filter.ChromeTraceCategoryFilter()
+    a.AddExcludedCategory('foo')
+    a.AddExcludedCategory('bar')
+    a.AddExcludedCategory('foo')
+    self.assertEquals(a.stable_filter_string, '-bar,-foo')
+
+  def testIncludeAndExcludeCategoryRaisesAssertion(self):
+    a = chrome_trace_category_filter.ChromeTraceCategoryFilter()
+    a.AddIncludedCategory('foo')
+    self.assertRaises(AssertionError, a.AddExcludedCategory, 'foo')
+
+    a = chrome_trace_category_filter.ChromeTraceCategoryFilter()
+    a.AddExcludedCategory('foo')
+    self.assertRaises(AssertionError, a.AddIncludedCategory, 'foo')
+
+    self.assertRaises(AssertionError,
+                      chrome_trace_category_filter.ChromeTraceCategoryFilter,
+                      'foo,-foo')
+
+    self.assertRaises(AssertionError,
+                      chrome_trace_category_filter.ChromeTraceCategoryFilter,
+                      '-foo,foo')
+
+
+  def testIsSubset(self):
+    b = chrome_trace_category_filter.ChromeTraceCategoryFilter()
+    a = chrome_trace_category_filter.ChromeTraceCategoryFilter()
+    self.assertEquals(a.IsSubset(b), True)
+
+    b = chrome_trace_category_filter.ChromeTraceCategoryFilter()
+    a = chrome_trace_category_filter.ChromeTraceCategoryFilter("test1,test2")
+    self.assertEquals(a.IsSubset(b), True)
+
+    b = chrome_trace_category_filter.ChromeTraceCategoryFilter()
+    a = chrome_trace_category_filter.ChromeTraceCategoryFilter("-test1,-test2")
+    self.assertEquals(a.IsSubset(b), True)
+
+    b = chrome_trace_category_filter.ChromeTraceCategoryFilter("test1,test2")
+    a = chrome_trace_category_filter.ChromeTraceCategoryFilter()
+    self.assertEquals(a.IsSubset(b), None)
+
+    b = chrome_trace_category_filter.ChromeTraceCategoryFilter()
+    a = chrome_trace_category_filter.ChromeTraceCategoryFilter("test*")
+    self.assertEquals(a.IsSubset(b), None)
+
+    b = chrome_trace_category_filter.ChromeTraceCategoryFilter("test?")
+    a = chrome_trace_category_filter.ChromeTraceCategoryFilter()
+    self.assertEquals(a.IsSubset(b), None)
+
+    b = chrome_trace_category_filter.ChromeTraceCategoryFilter("test1")
+    a = chrome_trace_category_filter.ChromeTraceCategoryFilter("test1,test2")
+    self.assertEquals(a.IsSubset(b), False)
+
+    b = chrome_trace_category_filter.ChromeTraceCategoryFilter("-test1")
+    a = chrome_trace_category_filter.ChromeTraceCategoryFilter("test1")
+    self.assertEquals(a.IsSubset(b), False)
+
+    b = chrome_trace_category_filter.ChromeTraceCategoryFilter("test1,test2")
+    a = chrome_trace_category_filter.ChromeTraceCategoryFilter("test2,test1")
+    self.assertEquals(a.IsSubset(b), True)
+
+    b = chrome_trace_category_filter.ChromeTraceCategoryFilter("-test1,-test2")
+    a = chrome_trace_category_filter.ChromeTraceCategoryFilter("-test2")
+    self.assertEquals(a.IsSubset(b), False)
+
+    b = chrome_trace_category_filter.ChromeTraceCategoryFilter(
+        "disabled-by-default-test1")
+    a = chrome_trace_category_filter.ChromeTraceCategoryFilter(
+        "disabled-by-default-test1,disabled-by-default-test2")
+    self.assertEquals(a.IsSubset(b), False)
+
+    b = chrome_trace_category_filter.ChromeTraceCategoryFilter(
+        "disabled-by-default-test1")
+    a = chrome_trace_category_filter.ChromeTraceCategoryFilter(
+        "disabled-by-default-test2")
+    self.assertEquals(a.IsSubset(b), False)
+
+  def testIsSubsetWithSyntheticDelays(self):
+    b = chrome_trace_category_filter.ChromeTraceCategoryFilter(
+        "DELAY(foo;0.016)")
+    a = chrome_trace_category_filter.ChromeTraceCategoryFilter(
+        "DELAY(foo;0.016)")
+    self.assertEquals(a.IsSubset(b), True)
+
+    b = chrome_trace_category_filter.ChromeTraceCategoryFilter(
+        "DELAY(foo;0.016)")
+    a = chrome_trace_category_filter.ChromeTraceCategoryFilter()
+    self.assertEquals(a.IsSubset(b), True)
+
+    b = chrome_trace_category_filter.ChromeTraceCategoryFilter()
+    a = chrome_trace_category_filter.ChromeTraceCategoryFilter(
+        "DELAY(foo;0.016)")
+    self.assertEquals(a.IsSubset(b), False)
+
+    b = chrome_trace_category_filter.ChromeTraceCategoryFilter(
+        "DELAY(foo;0.016)")
+    a = chrome_trace_category_filter.ChromeTraceCategoryFilter(
+        "DELAY(foo;0.032)")
+    self.assertEquals(a.IsSubset(b), False)
+
+    b = chrome_trace_category_filter.ChromeTraceCategoryFilter(
+        "DELAY(foo;0.016;static)")
+    a = chrome_trace_category_filter.ChromeTraceCategoryFilter(
+        "DELAY(foo;0.016;oneshot)")
+    self.assertEquals(a.IsSubset(b), False)
+
+    b = chrome_trace_category_filter.ChromeTraceCategoryFilter(
+        "DELAY(foo;0.016),DELAY(bar;0.1)")
+    a = chrome_trace_category_filter.ChromeTraceCategoryFilter(
+        "DELAY(bar;0.1),DELAY(foo;0.016)")
+    self.assertEquals(a.IsSubset(b), True)
+
+    b = chrome_trace_category_filter.ChromeTraceCategoryFilter(
+        "DELAY(foo;0.016),DELAY(bar;0.1)")
+    a = chrome_trace_category_filter.ChromeTraceCategoryFilter(
+        "DELAY(bar;0.1)")
+    self.assertEquals(a.IsSubset(b), True)
+
+    b = chrome_trace_category_filter.ChromeTraceCategoryFilter(
+        "DELAY(foo;0.016),DELAY(bar;0.1)")
+    a = chrome_trace_category_filter.ChromeTraceCategoryFilter(
+        "DELAY(foo;0.032),DELAY(bar;0.1)")
+    self.assertEquals(a.IsSubset(b), False)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/chrome_trace_config.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/chrome_trace_config.py
new file mode 100644
index 0000000..850b84e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/chrome_trace_config.py
@@ -0,0 +1,189 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import re
+
+from telemetry.timeline import chrome_trace_category_filter
+
+
+RECORD_MODE_PARAM = 'record_mode'
+
+ECHO_TO_CONSOLE = 'trace-to-console'
+RECORD_AS_MUCH_AS_POSSIBLE = 'record-as-much-as-possible'
+RECORD_CONTINUOUSLY = 'record-continuously'
+RECORD_UNTIL_FULL = 'record-until-full'
+
+# Map telemetry's tracing record_mode to the DevTools API string.
+# (The keys happen to be the same as the values.)
+RECORD_MODE_MAP = {
+  RECORD_UNTIL_FULL: 'record-until-full',
+  RECORD_CONTINUOUSLY: 'record-continuously',
+  RECORD_AS_MUCH_AS_POSSIBLE: 'record-as-much-as-possible',
+  ECHO_TO_CONSOLE: 'trace-to-console'
+}
+
+
+def ConvertStringToCamelCase(string):
+  """Convert an underscore/hyphen-case string to its camel-case counterpart.
+
+  This function is the inverse of Chromium's ConvertFromCamelCase function
+  in src/content/browser/devtools/protocol/tracing_handler.cc.
+  """
+  parts = re.split(r'[-_]', string)
+  return parts[0] + ''.join([p.title() for p in parts[1:]])
+
+
+def ConvertDictKeysToCamelCaseRecursively(data):
+  """Recursively convert dictionary keys from underscore/hyphen- to camel-case.
+
+  This function is the inverse of Chromium's ConvertDictKeyStyle function
+  in src/content/browser/devtools/protocol/tracing_handler.cc.
+  """
+  if isinstance(data, dict):
+    return {ConvertStringToCamelCase(k):
+            ConvertDictKeysToCamelCaseRecursively(v)
+            for k, v in data.iteritems()}
+  elif isinstance(data, list):
+    return map(ConvertDictKeysToCamelCaseRecursively, data)
+  else:
+    return data
+
+
+class ChromeTraceConfig(object):
+  """Stores configuration options specific to the Chrome tracing agent.
+
+    This produces the trace config JSON string for tracing in Chrome.
+
+    record_mode: can be any mode in RECORD_MODE_MAP. This corresponds to
+        record modes in chrome.
+    category_filter: Object that specifies which tracing categories to trace.
+    memory_dump_config: Stores the triggers for memory dumps.
+  """
+
+  def __init__(self):
+    self._record_mode = RECORD_AS_MUCH_AS_POSSIBLE
+    self._category_filter = (
+        chrome_trace_category_filter.ChromeTraceCategoryFilter())
+    self._memory_dump_config = None
+
+  def SetLowOverheadFilter(self):
+    self._category_filter = (
+        chrome_trace_category_filter.CreateLowOverheadFilter())
+
+  def SetDefaultOverheadFilter(self):
+    self._category_filter = (
+        chrome_trace_category_filter.CreateDefaultOverheadFilter())
+
+  def SetDebugOverheadFilter(self):
+    self._category_filter = (
+        chrome_trace_category_filter.CreateDebugOverheadFilter())
+
+  @property
+  def category_filter(self):
+    return self._category_filter
+
+  def SetCategoryFilter(self, cf):
+    if isinstance(cf, chrome_trace_category_filter.ChromeTraceCategoryFilter):
+      self._category_filter = cf
+    else:
+      raise TypeError(
+          'Must pass SetCategoryFilter a ChromeTraceCategoryFilter instance')
+
+  def SetMemoryDumpConfig(self, dump_config):
+    if isinstance(dump_config, MemoryDumpConfig):
+      self._memory_dump_config = dump_config
+    else:
+      raise TypeError(
+          'Must pass SetMemoryDumpConfig a MemoryDumpConfig instance')
+
+  @property
+  def record_mode(self):
+    return self._record_mode
+
+  @record_mode.setter
+  def record_mode(self, value):
+    assert value in RECORD_MODE_MAP
+    self._record_mode = value
+
+  def GetChromeTraceConfigForStartupTracing(self):
+    """Map the config to a JSON string for startup tracing.
+
+    All keys in the returned dictionary use underscore-case (e.g.
+    'record_mode'). In addition, the 'record_mode' value uses hyphen-case
+    (e.g. 'record-until-full').
+    """
+    result = {
+        RECORD_MODE_PARAM: RECORD_MODE_MAP[self._record_mode]
+    }
+    result.update(self._category_filter.GetDictForChromeTracing())
+    if self._memory_dump_config:
+      result.update(self._memory_dump_config.GetDictForChromeTracing())
+    return result
+
+  @property
+  def requires_modern_devtools_tracing_start_api(self):
+    """Returns True iff the config CANNOT be passed via the legacy DevTools API.
+
+    Legacy DevTools Tracing.start API:
+      Available since:    the introduction of the Tracing.start request.
+      Parameters:         categories (string), options (string),
+                          bufferUsageReportingInterval (number),
+                          transferMode (enum).
+      TraceConfig method: GetChromeTraceCategoriesAndOptionsStringsForDevTools()
+
+    Modern DevTools Tracing.start API:
+      Available since:    Chrome 51.0.2683.0.
+      Parameters:         traceConfig (dict),
+                          bufferUsageReportingInterval (number),
+                          transferMode (enum).
+      TraceConfig method: GetChromeTraceConfigDictForDevTools()
+    """
+    # Memory dump config cannot be passed via the 'options' string (legacy API)
+    # in the DevTools Tracing.start request.
+    return bool(self._memory_dump_config)
+
+  def GetChromeTraceConfigForDevTools(self):
+    """Map the config to a DevTools API config dictionary.
+
+    All keys in the returned dictionary use camel-case (e.g. 'recordMode').
+    In addition, the 'recordMode' value also uses camel-case (e.g.
+    'recordUntilFull'). This is to invert the camel-case ->
+    underscore/hyphen-delimited mapping performed in Chromium devtools.
+    """
+    result = self.GetChromeTraceConfigForStartupTracing()
+    if result[RECORD_MODE_PARAM]:
+      result[RECORD_MODE_PARAM] = ConvertStringToCamelCase(
+          result[RECORD_MODE_PARAM])
+    return ConvertDictKeysToCamelCaseRecursively(result)
+
+  def GetChromeTraceCategoriesAndOptionsForDevTools(self):
+    """Map the categories and options to their DevTools API counterparts."""
+    assert not self.requires_modern_devtools_tracing_start_api
+    options_parts = [RECORD_MODE_MAP[self._record_mode]]
+    return (self._category_filter.stable_filter_string,
+            ','.join(options_parts))
+
+
+class MemoryDumpConfig(object):
+  """Stores the triggers for memory dumps in ChromeTraceConfig."""
+  def __init__(self):
+    self._triggers = []
+
+  def AddTrigger(self, mode, periodic_interval_ms):
+    """Adds a new trigger to config.
+
+    Args:
+      periodic_interval_ms: Dump time period in milliseconds.
+      level_of_detail: Memory dump level of detail string.
+          Valid arguments are "background", "light" and "detailed".
+    """
+    assert mode in ['background', 'light', 'detailed']
+    assert periodic_interval_ms > 0
+    self._triggers.append({'mode': mode,
+                           'periodic_interval_ms': periodic_interval_ms})
+
+  def GetDictForChromeTracing(self):
+    """Returns the dump config as dictionary for chrome tracing."""
+    # An empty trigger list would mean no periodic memory dumps.
+    return {'memory_dump_config': {'triggers': self._triggers}}
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/chrome_trace_config_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/chrome_trace_config_unittest.py
new file mode 100644
index 0000000..e65e354
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/chrome_trace_config_unittest.py
@@ -0,0 +1,112 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from telemetry.timeline import chrome_trace_category_filter
+from telemetry.timeline import chrome_trace_config
+
+
+class ChromeTraceConfigTests(unittest.TestCase):
+  def testDefault(self):
+    config = chrome_trace_config.ChromeTraceConfig()
+
+    # Trace config for startup tracing.
+    self.assertEquals({
+        'record_mode': 'record-as-much-as-possible'
+    }, config.GetChromeTraceConfigForStartupTracing())
+
+    # Trace config for DevTools (modern API).
+    self.assertEquals({
+        'recordMode': 'recordAsMuchAsPossible'
+    }, config.GetChromeTraceConfigForDevTools())
+
+    # Trace categories and options for DevTools (legacy API).
+    self.assertFalse(config.requires_modern_devtools_tracing_start_api)
+    self.assertEquals(
+        ('', 'record-as-much-as-possible'),
+        config.GetChromeTraceCategoriesAndOptionsForDevTools())
+
+  def testBasic(self):
+    category_filter = chrome_trace_category_filter.ChromeTraceCategoryFilter(
+        'x,-y,disabled-by-default-z,DELAY(7;foo)')
+    config = chrome_trace_config.ChromeTraceConfig()
+    config.SetCategoryFilter(category_filter)
+    config.record_mode = chrome_trace_config.RECORD_UNTIL_FULL
+
+    # Trace config for startup tracing.
+    self.assertEquals({
+        'excluded_categories': ['y'],
+        'included_categories': ['x', 'disabled-by-default-z'],
+        'record_mode': 'record-until-full',
+        'synthetic_delays': ['DELAY(7;foo)']
+    }, config.GetChromeTraceConfigForStartupTracing())
+
+    # Trace config for DevTools (modern API).
+    self.assertEquals({
+        'excludedCategories': ['y'],
+        'includedCategories': ['x', 'disabled-by-default-z'],
+        'recordMode': 'recordUntilFull',
+        'syntheticDelays': ['DELAY(7;foo)']
+    }, config.GetChromeTraceConfigForDevTools())
+
+    # Trace categories and options for DevTools (legacy API).
+    self.assertFalse(config.requires_modern_devtools_tracing_start_api)
+    self.assertEquals(
+        ('x,disabled-by-default-z,-y,DELAY(7;foo)',
+         'record-until-full'),
+        config.GetChromeTraceCategoriesAndOptionsForDevTools())
+
+  def testMemoryDumpConfigFormat(self):
+    config = chrome_trace_config.ChromeTraceConfig()
+    config.record_mode = chrome_trace_config.ECHO_TO_CONSOLE
+    dump_config = chrome_trace_config.MemoryDumpConfig()
+    config.SetMemoryDumpConfig(dump_config)
+
+    # Trace config for startup tracing.
+    self.assertEquals({
+        'memory_dump_config': {'triggers': []},
+        'record_mode': 'trace-to-console'
+    }, config.GetChromeTraceConfigForStartupTracing())
+
+    # Trace config for DevTools (modern API).
+    self.assertEquals({
+        'memoryDumpConfig': {'triggers': []},
+        'recordMode': 'traceToConsole'
+    }, config.GetChromeTraceConfigForDevTools())
+
+    # Trace categories and options for DevTools (legacy API).
+    self.assertTrue(config.requires_modern_devtools_tracing_start_api)
+    with self.assertRaises(AssertionError):
+      config.GetChromeTraceCategoriesAndOptionsForDevTools()
+
+    dump_config.AddTrigger('light', 250)
+    dump_config.AddTrigger('detailed', 2000)
+
+    # Trace config for startup tracing.
+    self.assertEquals({
+        'memory_dump_config': {
+            'triggers': [
+                {'mode': 'light', 'periodic_interval_ms': 250},
+                {'mode': 'detailed', 'periodic_interval_ms': 2000}
+            ]
+        },
+        'record_mode': 'trace-to-console'
+    }, config.GetChromeTraceConfigForStartupTracing())
+
+    # Trace config for DevTools (modern API).
+    self.assertEquals({
+        'memoryDumpConfig': {
+            'triggers': [
+                {'mode': 'light', 'periodicIntervalMs': 250},
+                {'mode': 'detailed', 'periodicIntervalMs': 2000}
+            ]
+        },
+        'recordMode': 'traceToConsole'
+    }, config.GetChromeTraceConfigForDevTools())
+
+    # Trace categories and options for DevTools (legacy API).
+    self.assertTrue(config.requires_modern_devtools_tracing_start_api)
+    with self.assertRaises(AssertionError):
+      config.GetChromeTraceCategoriesAndOptionsForDevTools()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/counter.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/counter.py
new file mode 100644
index 0000000..63aacc6
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/counter.py
@@ -0,0 +1,112 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import telemetry.timeline.event_container as event_container
+
+
+# Doesn't inherit from TimelineEvent because its only a temporary wrapper of a
+# counter sample into an event. During stable operation, the samples are stored
+# a dense array of values rather than in the long-form done by an Event.
+class CounterSample(object):
+  def __init__(self, counter, sample_index):
+    self._counter = counter
+    self._sample_index = sample_index
+
+  @property
+  def category(self):
+    return self._counter.category
+
+  @property
+  def name(self):
+    return self._counter.full_name
+
+  @property
+  def value(self):
+    return self._counter.samples[self._sample_index]
+
+  @property
+  def start(self):
+    return self._counter.timestamps[self._sample_index]
+
+  @start.setter
+  def start(self, start):
+    self._counter.timestamps[self._sample_index] = start
+
+  @property
+  def duration(self):
+    return 0
+
+  @property
+  def end(self):
+    return self.start
+
+  @property
+  def thread_start(self):
+    return None
+
+  @property
+  def thread_duration(self):
+    return None
+
+  @property
+  def thread_end(self):
+    return None
+
+
+class Counter(event_container.TimelineEventContainer):
+  """ Stores all the samples for a given counter.
+  """
+  def __init__(self, parent, category, name):
+    super(Counter, self).__init__(name, parent)
+    self.category = category
+    self.full_name = category + '.' + name
+    self.samples = []
+    self.timestamps = []
+    self.series_names = []
+    self.totals = []
+    self.max_total = 0
+
+  def IterChildContainers(self):
+    return
+    yield # pylint: disable=unreachable
+
+  def IterEventsInThisContainer(self, event_type_predicate, event_predicate):
+    if not event_type_predicate(CounterSample) or not self.timestamps:
+      return
+
+    # Pass event_predicate a reused CounterSample instance to avoid
+    # creating a ton of garbage for rejected samples.
+    test_sample = CounterSample(self, 0)
+    for i in xrange(len(self.timestamps)):
+      test_sample._sample_index = i  # pylint: disable=protected-access
+      if event_predicate(test_sample):
+        yield CounterSample(self, i)
+
+  @property
+  def num_series(self):
+    return len(self.series_names)
+
+  @property
+  def num_samples(self):
+    return len(self.timestamps)
+
+  def FinalizeImport(self):
+    if self.num_series * self.num_samples != len(self.samples):
+      raise ValueError(
+          'Length of samples must be a multiple of length of timestamps.')
+
+    self.totals = []
+    self.max_total = 0
+    if not len(self.samples):
+      return
+
+    max_total = None
+    for i in xrange(self.num_samples):
+      total = 0
+      for j in xrange(self.num_series):
+        total += self.samples[i * self.num_series + j]
+        self.totals.append(total)
+      if max_total is None or total > max_total:
+        max_total = total
+    self.max_total = max_total
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/counter_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/counter_unittest.py
new file mode 100644
index 0000000..a97d4ac
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/counter_unittest.py
@@ -0,0 +1,64 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import types
+import unittest
+
+from telemetry.timeline import counter as counter_module
+
+
+class FakeProcess(object):
+  pass
+
+
+class CounterIterEventsInThisContainerTest(unittest.TestCase):
+
+  def setUp(self):
+    parent = FakeProcess()
+    self.counter = counter_module.Counter(parent, 'cat', 'name')
+
+  def assertIsEmptyIterator(self, itr):
+    self.assertIsInstance(itr, types.GeneratorType)
+    self.assertRaises(StopIteration, itr.next)
+
+  def testEmptyTimestamps(self):
+    self.assertIsEmptyIterator(self.counter.IterEventsInThisContainer(
+        event_type_predicate=lambda x: True,
+        event_predicate=lambda x: True))
+
+  def testEventTypeMismatch(self):
+    self.counter.timestamps = [111, 222]
+    self.assertIsEmptyIterator(self.counter.IterEventsInThisContainer(
+        event_type_predicate=lambda x: False,
+        event_predicate=lambda x: True))
+
+  def testNoEventMatch(self):
+    self.counter.timestamps = [111, 222]
+    self.assertIsEmptyIterator(self.counter.IterEventsInThisContainer(
+        event_type_predicate=lambda x: True,
+        event_predicate=lambda x: False))
+
+  def testAllMatch(self):
+    self.counter.timestamps = [111, 222]
+    self.counter.samples = [100, 200]
+    events = self.counter.IterEventsInThisContainer(
+        event_type_predicate=lambda x: True,
+        event_predicate=lambda x: True)
+    self.assertIsInstance(events, types.GeneratorType)
+    eventlist = list(events)
+    self.assertEqual([111, 222], [s.start for s in eventlist])
+    self.assertEqual(['cat.name', 'cat.name'], [s.name for s in eventlist])
+    self.assertEqual([100, 200], [s.value for s in eventlist])
+
+  def testPartialMatch(self):
+    self.counter.timestamps = [111, 222]
+    self.counter.samples = [100, 200]
+    events = self.counter.IterEventsInThisContainer(
+        event_type_predicate=lambda x: True,
+        event_predicate=lambda x: x.start > 200)
+    self.assertIsInstance(events, types.GeneratorType)
+    eventlist = list(events)
+    self.assertEqual([222], [s.start for s in eventlist])
+    self.assertEqual(['cat.name'], [s.name for s in eventlist])
+    self.assertEqual([200], [s.value for s in eventlist])
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/event.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/event.py
new file mode 100644
index 0000000..996cba9
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/event.py
@@ -0,0 +1,56 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+class TimelineEvent(object):
+  """Represents a timeline event.
+
+  thread_start, thread_duration and thread_end are the start time, duration
+  and end time of this event as measured by the thread-specific CPU clock
+  (ticking when the thread is actually scheduled). Thread time is optional
+  on trace events and the corresponding attributes in TimelineEvent will be
+  set to None (not 0) if not present. Users of this class need to properly
+  handle this case.
+  """
+  def __init__(self, category, name, start, duration, thread_start=None,
+               thread_duration=None, args=None):
+    self.category = category
+    self.name = name
+    self.start = start
+    self.duration = duration
+    self.thread_start = thread_start
+    self.thread_duration = thread_duration
+    self.args = args
+
+  @property
+  def end(self):
+    return self.start + self.duration
+
+  @property
+  def has_thread_timestamps(self):
+    return self.thread_start is not None and self.thread_duration is not None
+
+  @property
+  def thread_end(self):
+    """Thread-specific CPU time when this event ended.
+
+    May be None if the trace event didn't have thread time data.
+    """
+    if self.thread_start == None or self.thread_duration == None:
+      return None
+    return self.thread_start + self.thread_duration
+
+  def __repr__(self):
+    if self.args:
+      args_str = ', ' + repr(self.args)
+    else:
+      args_str = ''
+
+    return ("TimelineEvent(name='%s', start=%f, duration=%s, " +
+            "thread_start=%s, thread_duration=%s%s)") % (
+                self.name,
+                self.start,
+                self.duration,
+                self.thread_start,
+                self.thread_duration,
+                args_str)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/event_container.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/event_container.py
new file mode 100644
index 0000000..4d41e68
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/event_container.py
@@ -0,0 +1,144 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.timeline import async_slice as async_slice_module
+from telemetry.timeline import flow_event as flow_event_module
+from telemetry.timeline import slice as slice_module
+
+
+class TimelineEventContainer(object):
+  """Represents a container for events.
+
+  """
+  def __init__(self, name, parent):
+    self.parent = parent
+    self.name = name
+
+  @staticmethod
+  def IsAsyncSlice(t):
+    return t == async_slice_module.AsyncSlice
+
+  # Basic functions that subclasses of TimelineEventContainer should implement
+  # in order to expose their events. New methods should be added to this part of
+  # the code only when absolutely certain they're needed.
+
+  def IterChildContainers(self):
+    raise NotImplementedError()
+
+  def IterEventsInThisContainer(self, event_type_predicate, event_predicate):
+    """Iterates all the TimelineEvents in this container.
+
+    Only events with a type matching event_type_predicate AND matching event
+    event_predicate will be yielded.
+
+    event_type_predicate is given an actual type object, e.g.:
+        event_type_predicate(slice_module.Slice)
+
+    event_predicate is given actual events:
+        event_predicate(thread.slices[7])
+
+    DO NOT ASSUME that the event_type_predicate will be called for every event
+    found. The relative calling order of the two is left up to the implementer
+    of the method.
+
+    """
+    del event_type_predicate, event_predicate  # unused
+    return
+    yield # pylint: disable=unreachable
+
+
+  def IterAllEvents(self,
+                    recursive=True,
+                    event_type_predicate=lambda t: True,
+                    event_predicate=lambda e: True):
+    """Iterates all events in this container, pre-filtered by two predicates.
+
+    Only events with a type matching event_type_predicate AND matching event
+    event_predicate will be yielded.
+
+    event_type_predicate is given an actual type object, e.g.:
+        event_type_predicate(slice_module.Slice)
+
+    event_predicate is given actual events:
+        event_predicate(thread.slices[7])
+    """
+    if not recursive:
+      for e in self.IterEventsInThisContainer(
+          event_type_predicate, event_predicate):
+        yield e
+      return
+
+    # TODO(nduca): Write this as a proper iterator instead of one that creates a
+    # list and then iterates it.
+    containers = []
+    def GetContainersRecursive(container):
+      containers.append(container)
+      for container in container.IterChildContainers():
+        GetContainersRecursive(container)
+    GetContainersRecursive(self)
+
+    # Actually create the iterator.
+    for c in containers:
+      for e in c.IterEventsInThisContainer(event_type_predicate,
+                                           event_predicate):
+        yield e
+
+  # Helper functions for finding common kinds of events. Must always take an
+  # optinal recurisve parameter and be implemented in terms fo IterAllEvents.
+  def IterAllEventsOfName(self, name, recursive=True):
+    return self.IterAllEvents(
+      recursive=recursive,
+      event_type_predicate=lambda t: True,
+      event_predicate=lambda e: e.name == name)
+
+  def IterAllSlices(self, recursive=True):
+    return self.IterAllEvents(
+      recursive=recursive,
+      event_type_predicate=lambda t: t == slice_module.Slice)
+
+  def IterAllSlicesInRange(self, start, end, recursive=True):
+    return self.IterAllEvents(
+      recursive=recursive,
+      event_type_predicate=lambda t: t == slice_module.Slice,
+      event_predicate=lambda s: s.start >= start and s.end <= end)
+
+  def IterAllSlicesOfName(self, name, recursive=True):
+    return self.IterAllEvents(
+      recursive=recursive,
+      event_type_predicate=lambda t: t == slice_module.Slice,
+      event_predicate=lambda e: e.name == name)
+
+  def IterAllToplevelSlicesOfName(self, name, recursive=True):
+    return self.IterAllEvents(
+      recursive=recursive,
+      event_type_predicate=lambda t: t == slice_module.Slice,
+      event_predicate=lambda e: e.name == name and e.parent_slice == None)
+
+  def IterAllAsyncSlicesOfName(self, name, recursive=True):
+    return self.IterAllEvents(
+      recursive=recursive,
+      event_type_predicate=self.IsAsyncSlice,
+      event_predicate=lambda e: e.name == name)
+
+  def IterAllAsyncSlicesStartsWithName(self, name, recursive=True):
+    return self.IterAllEvents(
+      recursive=recursive,
+      event_type_predicate=self.IsAsyncSlice,
+      event_predicate=lambda e: e.name.startswith(name))
+
+  def IterAllFlowEvents(self, recursive=True):
+    return self.IterAllEvents(
+      recursive=recursive,
+      event_type_predicate=lambda t: t == flow_event_module.FlowEvent)
+
+  # List versions. These should always be simple expressions that list() on
+  # an underlying iter method.
+  def GetAllEvents(self, recursive=True):
+    return list(self.IterAllEvents(recursive=recursive))
+
+  def GetAllEventsOfName(self, name, recursive=True):
+    return list(self.IterAllEventsOfName(name, recursive))
+
+  def GetAllToplevelSlicesOfName(self, name, recursive=True):
+    return list(self.IterAllToplevelSlicesOfName(name, recursive))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/event_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/event_unittest.py
new file mode 100644
index 0000000..380fea4
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/event_unittest.py
@@ -0,0 +1,24 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from telemetry.timeline import event
+
+
+class TimelineEventTest(unittest.TestCase):
+  def testHasThreadTimestamps(self):
+    # No thread_start and no thread_duration
+    event_1 = event.TimelineEvent('test', 'foo', 0, 10)
+    # Has thread_start but no thread_duration
+    event_2 = event.TimelineEvent('test', 'foo', 0, 10, 2)
+    # Has thread_duration but no thread_start
+    event_3 = event.TimelineEvent('test', 'foo', 0, 10, None, 4)
+    # Has thread_start and thread_duration
+    event_4 = event.TimelineEvent('test', 'foo', 0, 10, 2, 4)
+
+    self.assertFalse(event_1.has_thread_timestamps)
+    self.assertFalse(event_2.has_thread_timestamps)
+    self.assertFalse(event_3.has_thread_timestamps)
+    self.assertTrue(event_4.has_thread_timestamps)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/flow_event.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/flow_event.py
new file mode 100644
index 0000000..8a39215
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/flow_event.py
@@ -0,0 +1,15 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import telemetry.timeline.event as event
+
+
+class FlowEvent(event.TimelineEvent):
+  """A FlowEvent represents an interval of time plus parameters associated
+  with that interval.
+  """
+  def __init__(self, category, event_id, name, start, args=None):
+    super(FlowEvent, self).__init__(
+        category, name, start, duration=0, args=args)
+    self.event_id = event_id
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/importer.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/importer.py
new file mode 100644
index 0000000..5c355a9
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/importer.py
@@ -0,0 +1,24 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+class TimelineImporter(object):
+  """Reads TraceData and populates timeline model with what it finds."""
+  def __init__(self, model, trace_data, import_order):
+    self._model = model
+    self._trace_data = trace_data
+    self.import_order = import_order
+
+  @staticmethod
+  def GetSupportedPart():
+    raise NotImplementedError
+
+  def ImportEvents(self):
+    """Processes the event data in the wrapper and creates and adds
+    new timeline events to the model"""
+    raise NotImplementedError
+
+  def FinalizeImport(self):
+    """Called after all other importers for the model are run."""
+    raise NotImplementedError
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/inspector_importer.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/inspector_importer.py
new file mode 100644
index 0000000..21aa4ed
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/inspector_importer.py
@@ -0,0 +1,75 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Imports event data obtained from the inspector's timeline."""
+
+from telemetry.timeline import importer
+import telemetry.timeline.slice as tracing_slice
+import telemetry.timeline.thread as timeline_thread
+from tracing.trace_data import trace_data as trace_data_module
+
+
+class InspectorTimelineImporter(importer.TimelineImporter):
+  def __init__(self, model, trace_data):
+    super(InspectorTimelineImporter, self).__init__(model,
+                                                    trace_data,
+                                                    import_order=1)
+    traces = trace_data.GetTracesFor(
+      trace_data_module.INSPECTOR_TRACE_PART)
+    assert len(traces) == 1
+    self._events = traces[0]
+
+  @staticmethod
+  def GetSupportedPart():
+    return trace_data_module.INSPECTOR_TRACE_PART
+
+  def ImportEvents(self):
+    render_process = self._model.GetOrCreateProcess(0)
+    for raw_event in self._events:
+      thread = render_process.GetOrCreateThread(raw_event.get('thread', 0))
+      InspectorTimelineImporter.AddRawEventToThreadRecursive(thread, raw_event)
+
+  def FinalizeImport(self):
+    pass
+
+  @staticmethod
+  def AddRawEventToThreadRecursive(thread, raw_inspector_event):
+    pending_slice = None
+    if ('startTime' in raw_inspector_event and
+        'type' in raw_inspector_event):
+      args = {}
+      for x in raw_inspector_event:
+        if x in ('startTime', 'endTime', 'children'):
+          continue
+        args[x] = raw_inspector_event[x]
+      if len(args) == 0:
+        args = None
+      start_time = raw_inspector_event['startTime']
+      end_time = raw_inspector_event.get('endTime', start_time)
+
+      pending_slice = tracing_slice.Slice(
+        thread, 'inspector',
+        raw_inspector_event['type'],
+        start_time,
+        thread_timestamp=None,
+        args=args)
+
+    for child in raw_inspector_event.get('children', []):
+      InspectorTimelineImporter.AddRawEventToThreadRecursive(
+          thread, child)
+
+    if pending_slice:
+      pending_slice.duration = end_time - pending_slice.start
+      thread.PushSlice(pending_slice)
+
+  @staticmethod
+  def RawEventToTimelineEvent(raw_inspector_event):
+    """Converts raw_inspector_event to TimelineEvent."""
+    thread = timeline_thread.Thread(None, 0)
+    InspectorTimelineImporter.AddRawEventToThreadRecursive(
+        thread, raw_inspector_event)
+    thread.FinalizeImport()
+    assert len(thread.toplevel_slices) <= 1
+    if len(thread.toplevel_slices) == 0:
+      return None
+    return thread.toplevel_slices[0]
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/inspector_importer_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/inspector_importer_unittest.py
new file mode 100644
index 0000000..d38e4e0
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/inspector_importer_unittest.py
@@ -0,0 +1,150 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import unittest
+
+from telemetry.timeline import inspector_importer
+from telemetry.timeline import model
+from tracing.trace_data import trace_data
+
+
+_BACKGROUND_MESSAGE = {
+  'data': {},
+  'type': 'BeginFrame',
+  'thread': '2',
+  'startTime': 1352783525921.824}
+
+_SAMPLE_MESSAGE = {
+  'children': [
+    {'data': {},
+     'startTime': 1352783525921.823,
+     'type': 'BeginFrame',
+     'usedHeapSize': 1870736},
+    {'children': [],
+     'data': {'height': 723,
+              'width': 1272,
+              'x': 0,
+              'y': 0},
+     'endTime': 1352783525921.8992,
+     'frameId': '10.2',
+     'startTime': 1352783525921.8281,
+     'type': 'Layout',
+     'usedHeapSize': 1870736},
+    {'children': [
+        {'children': [],
+         'data': {'imageType': 'PNG'},
+         'endTime': 1352783525927.7939,
+         'startTime': 1352783525922.4241,
+         'type': 'DecodeImage',
+         'usedHeapSize': 1870736}
+        ],
+     'data': {'height': 432,
+              'width': 1272,
+              'x': 0,
+              'y': 8},
+     'endTime': 1352783525927.9822,
+     'frameId': '10.2',
+     'startTime': 1352783525921.9292,
+     'type': 'Paint',
+     'usedHeapSize': 1870736}
+    ],
+  'data': {},
+  'endTime': 1352783525928.041,
+  'startTime': 1352783525921.8049,
+  'type': 'Program'}
+
+class InspectorEventParsingTest(unittest.TestCase):
+  def testParsingWithSampleData(self):
+    root_event = (inspector_importer.InspectorTimelineImporter
+        .RawEventToTimelineEvent(_SAMPLE_MESSAGE))
+    self.assertTrue(root_event)
+    decode_image_event = [
+      child for child in root_event.IterEventsInThisContainerRecrusively()
+      if child.name == 'DecodeImage'][0]
+    self.assertEquals(decode_image_event.args['data']['imageType'], 'PNG')
+    self.assertTrue(decode_image_event.duration > 0)
+
+  def testParsingWithSimpleData(self):
+    raw_event = {'type': 'Foo',
+                 'startTime': 1,
+                 'endTime': 3,
+                 'children': []}
+    event = (inspector_importer.InspectorTimelineImporter
+        .RawEventToTimelineEvent(raw_event))
+    self.assertEquals('Foo', event.name)
+    self.assertEquals(1, event.start)
+    self.assertEquals(3, event.end)
+    self.assertEquals(2, event.duration)
+    self.assertEquals([], event.sub_slices)
+
+  def testParsingWithArgs(self):
+    raw_event = {'type': 'Foo',
+                 'startTime': 1,
+                 'endTime': 3,
+                 'foo': 7,
+                 'bar': {'x': 1}}
+    event = (inspector_importer.InspectorTimelineImporter
+        .RawEventToTimelineEvent(raw_event))
+    self.assertEquals('Foo', event.name)
+    self.assertEquals(1, event.start)
+    self.assertEquals(3, event.end)
+    self.assertEquals(2, event.duration)
+    self.assertEquals([], event.sub_slices)
+    self.assertEquals(7, event.args['foo'])
+    self.assertEquals(1, event.args['bar']['x'])
+
+  def testEventsWithNoStartTimeAreDropped(self):
+    raw_event = {'type': 'Foo',
+                 'endTime': 1,
+                 'children': []}
+    event = (inspector_importer.InspectorTimelineImporter.
+        RawEventToTimelineEvent(raw_event))
+    self.assertEquals(None, event)
+
+  def testEventsWithNoEndTimeAreOk(self):
+    raw_event = {'type': 'Foo',
+                 'startTime': 1,
+                 'children': []}
+    event = (inspector_importer.InspectorTimelineImporter.
+        RawEventToTimelineEvent(raw_event))
+    self.assertEquals(1, event.start)
+    self.assertEquals(1, event.end)
+
+  def testOutOfOrderData(self):
+    builder = trace_data.TraceDataBuilder()
+    builder.AddTraceFor(
+      trace_data.INSPECTOR_TRACE_PART, [{
+      'startTime': 5295.004, 'endTime': 5305.004,
+      'data': {}, 'type': 'Program',
+      'children': [
+        {'startTime': 5295.004, 'data': {'id': 0}, 'type': 'BeginFrame', },
+        {'startTime': 4492.973, 'endTime': 4493.086, 'data': {'rootNode': -3},
+         'type': 'PaintSetup'},
+        {'startTime': 5298.004, 'endTime': 5301.004, 'type': 'Paint',
+         'frameId': '53228.1',
+         'data': {'rootNode': -3, 'clip': [0, 0, 1018, 0, 1018, 764, 0, 764],
+                  'layerId': 10}, 'children': []},
+        {'startTime': 5301.004, 'endTime': 5305.004, 'data': {},
+         'type': 'CompositeLayers', 'children': []},
+        {'startTime': 5305.004, 'data': {}, 'type': 'MarkFirstPaint'}
+    ]}])
+    model.TimelineModel(builder.AsData(), shift_world_to_zero=False)
+
+class InspectorImporterTest(unittest.TestCase):
+  def testImport(self):
+    builder = trace_data.TraceDataBuilder()
+    builder.AddTraceFor(trace_data.INSPECTOR_TRACE_PART,
+                        [_BACKGROUND_MESSAGE, _SAMPLE_MESSAGE])
+    m = model.TimelineModel(builder.AsData(), shift_world_to_zero=False)
+    self.assertEquals(1, len(m.processes))
+    process = m.processes.values()[0]
+    threads = process.threads
+    self.assertEquals(2, len(threads))
+    renderer_thread = threads[0]
+    self.assertEquals(1, len(renderer_thread.toplevel_slices))
+    self.assertEquals('Program',
+                      renderer_thread.toplevel_slices[0].name)
+    second_thread = threads['2']
+    self.assertEquals(1, len(second_thread.toplevel_slices))
+    self.assertEquals('BeginFrame',
+                      second_thread.toplevel_slices[0].name)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/memory_dump_event.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/memory_dump_event.py
new file mode 100644
index 0000000..e8c0af2
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/memory_dump_event.py
@@ -0,0 +1,343 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import posixpath
+import re
+
+from telemetry.timeline import event as timeline_event
+
+
+class MmapCategory(object):
+  _DEFAULT_CATEGORY = None
+
+  def __init__(self, name, file_pattern, children=None):
+    """A (sub)category for classifying memory maps.
+
+    Args:
+      name: A string to identify the category.
+      file_pattern: A regex pattern, the category will aggregate memory usage
+          for all mapped files matching this pattern.
+      children: A list of MmapCategory objects, used to sub-categorize memory
+          usage.
+    """
+    self.name = name
+    self._file_pattern = re.compile(file_pattern) if file_pattern else None
+    self._children = list(children) if children else None
+
+  @classmethod
+  def DefaultCategory(cls):
+    """An implicit 'Others' match-all category with no children."""
+    if cls._DEFAULT_CATEGORY is None:
+      cls._DEFAULT_CATEGORY = cls('Others', None)
+    return cls._DEFAULT_CATEGORY
+
+  def Match(self, mapped_file):
+    """Test whether a mapped file matches this category."""
+    return (self._file_pattern is None
+            or bool(self._file_pattern.search(mapped_file)))
+
+  def GetMatchingChild(self, mapped_file):
+    """Get the first matching sub-category for a given mapped file.
+
+    Returns None if the category has no children, or the DefaultCategory if
+    it does have children but none of them match.
+    """
+    if not self._children:
+      return None
+    for child in self._children:
+      if child.Match(mapped_file):
+        return child
+    return type(self).DefaultCategory()
+
+
+ROOT_CATEGORY = MmapCategory('/', None, [
+  MmapCategory('Android', r'^\/dev\/ashmem(?!\/libc malloc)', [
+    MmapCategory('Java runtime', r'^\/dev\/ashmem\/dalvik-', [
+      MmapCategory('Spaces', r'\/dalvik-(alloc|main|large'
+                             r' object|non moving|zygote) space', [
+        MmapCategory('Normal', r'\/dalvik-(alloc|main)'),
+        MmapCategory('Large', r'\/dalvik-large object'),
+        MmapCategory('Zygote', r'\/dalvik-zygote'),
+        MmapCategory('Non-moving', r'\/dalvik-non moving')
+      ]),
+      MmapCategory('Linear Alloc', r'\/dalvik-LinearAlloc'),
+      MmapCategory('Indirect Reference Table', r'\/dalvik-indirect.ref'),
+      MmapCategory('Cache', r'\/dalvik-jit-code-cache'),
+      MmapCategory('Accounting', None)
+    ]),
+    MmapCategory('Cursor', r'\/CursorWindow'),
+    MmapCategory('Ashmem', None)
+  ]),
+  MmapCategory('Native heap',
+               r'^((\[heap\])|(\[anon:)|(\/dev\/ashmem\/libc malloc)|$)'),
+  MmapCategory('Stack', r'^\[stack'),
+  MmapCategory('Files',
+               r'\.((((so)|(jar)|(apk)|(ttf)|(odex)|(oat)|(art))$)|(dex))', [
+    MmapCategory('so', r'\.so$'),
+    MmapCategory('jar', r'\.jar$'),
+    MmapCategory('apk', r'\.apk$'),
+    MmapCategory('ttf', r'\.ttf$'),
+    MmapCategory('dex', r'\.((dex)|(odex$))'),
+    MmapCategory('oat', r'\.oat$'),
+    MmapCategory('art', r'\.art$'),
+  ]),
+  MmapCategory('Devices', r'(^\/dev\/)|(anon_inode:dmabuf)', [
+    MmapCategory('GPU', r'\/((nv)|(mali)|(kgsl))'),
+    MmapCategory('DMA', r'anon_inode:dmabuf'),
+  ]),
+  MmapCategory('Discounted tracing overhead',
+               r'\[discounted tracing overhead\]')
+])
+
+
+# Map long descriptive attribute names, as understood by MemoryBucket.GetValue,
+# to the short keys used by events in raw json traces.
+BUCKET_ATTRS = {
+  'proportional_resident': 'pss',
+  'private_dirty_resident': 'pd',
+  'private_clean_resident': 'pc',
+  'shared_dirty_resident': 'sd',
+  'shared_clean_resident': 'sc',
+  'swapped': 'sw'}
+
+
+# Map of {memory_key: (category_path, discount_tracing), ...}.
+# When discount_tracing is True, we have to discount the resident_size of the
+# tracing allocator to get the correct value for that key.
+MMAPS_METRICS = {
+  'mmaps_overall_pss': ('/.proportional_resident', True),
+  'mmaps_private_dirty' : ('/.private_dirty_resident', True),
+  'mmaps_java_heap': ('/Android/Java runtime/Spaces.proportional_resident',
+                      False),
+  'mmaps_ashmem': ('/Android/Ashmem.proportional_resident', False),
+  'mmaps_native_heap': ('/Native heap.proportional_resident', True)}
+
+
+class MemoryBucket(object):
+  """Simple object to hold and aggregate memory values."""
+  def __init__(self):
+    self._bucket = dict.fromkeys(BUCKET_ATTRS.iterkeys(), 0)
+
+  def __repr__(self):
+    values = ', '.join('%s=%d' % (src_key, self._bucket[dst_key])
+                       for dst_key, src_key
+                       in sorted(BUCKET_ATTRS.iteritems()))
+    return '%s[%s]' % (type(self).__name__, values)
+
+  def AddRegion(self, byte_stats):
+    for dst_key, src_key in BUCKET_ATTRS.iteritems():
+      self._bucket[dst_key] += int(byte_stats.get(src_key, '0'), 16)
+
+  def GetValue(self, name):
+    return self._bucket[name]
+
+
+class ProcessMemoryDumpEvent(timeline_event.TimelineEvent):
+  """A memory dump event belonging to a single timeline.Process object.
+
+  It's a subclass of telemetry's TimelineEvent so it can be included in
+  the stream of events contained in timeline.model objects, and have its
+  timing correlated with that of other events in the model.
+
+  Args:
+    process: The Process object associated with the memory dump.
+    dump_events: A list of dump events of the process with the same dump id.
+
+  Properties:
+    dump_id: A string to identify events belonging to the same global dump.
+    process: The timeline.Process object that owns this memory dump event.
+    has_mmaps: True if the memory dump has mmaps information. If False then
+        GetMemoryUsage will report all zeros.
+  """
+  def __init__(self, process, dump_events):
+    assert dump_events
+
+    start_time = min(event['ts'] for event in dump_events) / 1000.0
+    duration = max(event['ts'] for event in dump_events) / 1000.0 - start_time
+    super(ProcessMemoryDumpEvent, self).__init__('memory', 'memory_dump',
+                                                 start_time, duration)
+
+    self.process = process
+    self.dump_id = dump_events[0]['id']
+
+    allocator_dumps = {}
+    vm_regions = []
+    for event in dump_events:
+      assert (event['ph'] == 'v' and self.process.pid == event['pid'] and
+              self.dump_id == event['id'])
+      try:
+        allocator_dumps.update(event['args']['dumps']['allocators'])
+      except KeyError:
+        pass  # It's ok if any of those keys are not present.
+      try:
+        value = event['args']['dumps']['process_mmaps']['vm_regions']
+        assert not vm_regions
+        vm_regions = value
+      except KeyError:
+        pass  # It's ok if any of those keys are not present.
+
+    self._allocators = {}
+    parent_path = ''
+    parent_has_size = False
+    for allocator_name, size_values in sorted(allocator_dumps.iteritems()):
+      if ((allocator_name.startswith(parent_path) and parent_has_size) or
+          allocator_name.startswith('global/')):
+        continue
+      parent_path = allocator_name + '/'
+      parent_has_size = 'size' in size_values['attrs']
+      name_parts = allocator_name.split('/')
+      allocator_name = name_parts[0]
+      # For 'gpu/android_memtrack/*' we want to keep track of individual
+      # components. E.g. 'gpu/android_memtrack/gl' will be stored as
+      # 'android_memtrack_gl' in the allocators dict.
+      if (len(name_parts) == 3 and allocator_name == 'gpu' and
+          name_parts[1] == 'android_memtrack'):
+        allocator_name = '_'.join(name_parts[1:3])
+      allocator = self._allocators.setdefault(allocator_name, {})
+      for size_key, size_value in size_values['attrs'].iteritems():
+        if size_value['units'] == 'bytes':
+          allocator[size_key] = (allocator.get(size_key, 0)
+                                 + int(size_value['value'], 16))
+    # we need to discount tracing from malloc size.
+    try:
+      self._allocators['malloc']['size'] -= self._allocators['tracing']['size']
+    except KeyError:
+      pass  # It's ok if any of those keys are not present.
+
+    self.has_mmaps = bool(vm_regions)
+    self._buckets = {}
+    for vm_region in vm_regions:
+      self._AddRegion(vm_region)
+
+  @property
+  def process_name(self):
+    return self.process.name
+
+  def _AddRegion(self, vm_region):
+    path = ''
+    category = ROOT_CATEGORY
+    while category:
+      path = posixpath.join(path, category.name)
+      self.GetMemoryBucket(path).AddRegion(vm_region['bs'])
+      mapped_file = vm_region['mf']
+      category = category.GetMatchingChild(mapped_file)
+
+  def __repr__(self):
+    values = ['pid=%d' % self.process.pid]
+    for key, value in sorted(self.GetMemoryUsage().iteritems()):
+      values.append('%s=%d' % (key, value))
+    values = ', '.join(values)
+    return '%s[%s]' % (type(self).__name__, values)
+
+  def GetMemoryBucket(self, path):
+    """Return the MemoryBucket associated with a category path.
+
+    An empty bucket will be created if the path does not already exist.
+
+    path: A string with path in the classification tree, e.g.
+        '/Android/Java runtime/Cache'. Note: no trailing slash, except for
+        the root path '/'.
+    """
+    if not path in self._buckets:
+      self._buckets[path] = MemoryBucket()
+    return self._buckets[path]
+
+  def GetMemoryValue(self, category_path, discount_tracing=False):
+    """Return a specific value from within a MemoryBucket.
+
+    category_path: A string composed of a path in the classification tree,
+        followed by a '.', followed by a specific bucket value, e.g.
+        '/Android/Java runtime/Cache.private_dirty_resident'.
+    discount_tracing: A boolean indicating whether the returned value should
+        be discounted by the resident size of the tracing allocator.
+    """
+    path, name = category_path.rsplit('.', 1)
+    value = self.GetMemoryBucket(path).GetValue(name)
+    if discount_tracing and 'tracing' in self._allocators:
+      value -= self._allocators['tracing'].get('resident_size', 0)
+    return value
+
+  def GetMemoryUsage(self):
+    """Get a dictionary with the memory usage of this process."""
+    usage = {}
+    for name, values in self._allocators.iteritems():
+      # If you wish to track more attributes here, make sure they are correctly
+      # calculated by the ProcessMemoryDumpEvent method. All dumps whose parent
+      # has "size" attribute are ignored to avoid double counting. So, the
+      # other attributes are totals of only top level dumps.
+      if 'size' in values:
+        usage['allocator_%s' % name] = values['size']
+      if 'allocated_objects_size' in values:
+        usage['allocated_objects_%s' % name] = values['allocated_objects_size']
+      if 'memtrack_pss' in values:
+        usage[name] = values['memtrack_pss']
+    if self.has_mmaps:
+      usage.update((key, self.GetMemoryValue(*value))
+                   for key, value in MMAPS_METRICS.iteritems())
+    return usage
+
+
+class GlobalMemoryDump(object):
+  """Object to aggregate individual process dumps with the same dump id.
+
+  Args:
+    process_dumps: A sequence of ProcessMemoryDumpEvent objects, all sharing
+        the same global dump id.
+
+  Attributes:
+    dump_id: A string identifying this dump.
+    has_mmaps: True if the memory dump has mmaps information. If False then
+        GetMemoryUsage will report all zeros.
+  """
+  def __init__(self, process_dumps):
+    assert process_dumps
+    # Keep dumps sorted in chronological order.
+    self._process_dumps = sorted(process_dumps, key=lambda dump: dump.start)
+
+    # All process dump events should have the same dump id.
+    dump_ids = set(dump.dump_id for dump in self._process_dumps)
+    assert len(dump_ids) == 1
+    self.dump_id = dump_ids.pop()
+
+    # Either all processes have mmaps or none of them do.
+    have_mmaps = set(dump.has_mmaps for dump in self._process_dumps)
+    assert len(have_mmaps) == 1
+    self.has_mmaps = have_mmaps.pop()
+
+  @property
+  def start(self):
+    return self._process_dumps[0].start
+
+  @property
+  def end(self):
+    return max(dump.end for dump in self._process_dumps)
+
+  @property
+  def duration(self):
+    return self.end - self.start
+
+  @property
+  def pids(self):
+    return set(d.process.pid for d in self._process_dumps)
+
+  def IterProcessMemoryDumps(self):
+    return iter(self._process_dumps)
+
+  def CountProcessMemoryDumps(self):
+    return len(self._process_dumps)
+
+  def __repr__(self):
+    values = ['id=%s' % self.dump_id]
+    for key, value in sorted(self.GetMemoryUsage().iteritems()):
+      values.append('%s=%d' % (key, value))
+    values = ', '.join(values)
+    return '%s[%s]' % (type(self).__name__, values)
+
+  def GetMemoryUsage(self):
+    """Get the aggregated memory usage over all processes in this dump."""
+    result = {}
+    for dump in self._process_dumps:
+      for key, value in dump.GetMemoryUsage().iteritems():
+        result[key] = result.get(key, 0) + value
+    return result
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/memory_dump_event_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/memory_dump_event_unittest.py
new file mode 100644
index 0000000..966f9ad
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/memory_dump_event_unittest.py
@@ -0,0 +1,278 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from telemetry.timeline import memory_dump_event
+import mock
+
+
+def MakeRawMemoryDumpEvent(dump_id='123456ABCDEF', pid=1234, start=0,
+                           mmaps=None, allocators=None):
+
+  def vm_region(mapped_file, byte_stats):
+    return {
+      'mf': mapped_file,
+      'bs': {k: hex(v) for k, v in byte_stats.iteritems()}}
+
+  def attrs(sizes):
+    return {'attrs': {k: {'value': hex(v), 'units': 'bytes'}
+                      for k, v in sizes.iteritems()}}
+
+  if allocators is None:
+    allocators = {}
+
+  event = {'ph': 'v', 'id': dump_id, 'pid': pid, 'ts': start * 1000,
+           'args': {'dumps': {'allocators': {
+               name: attrs(sizes) for name, sizes in allocators.iteritems()}}}}
+  if mmaps:
+    event['args']['dumps']['process_mmaps'] = {
+      'vm_regions': [vm_region(mapped_file, byte_stats)
+                     for mapped_file, byte_stats in mmaps.iteritems()]}
+
+  return event
+
+
+def TestProcessDumpEvent(dump_id='123456ABCDEF', pid=1234, start=0, mmaps=None,
+                         allocators=None):
+  event = MakeRawMemoryDumpEvent(dump_id, pid, start, mmaps=mmaps,
+                                allocators=allocators)
+  process = mock.Mock()
+  process.pid = event['pid']
+  return memory_dump_event.ProcessMemoryDumpEvent(process, [event])
+
+
+class ProcessMemoryDumpEventUnitTest(unittest.TestCase):
+
+  def testProcessMemoryDump_allocators(self):
+    process = mock.Mock()
+    process.pid = 1234
+    events = [
+      MakeRawMemoryDumpEvent(
+        pid=process.pid, allocators={
+          'v8': {'size': 10, 'allocated_objects_size': 5},
+          'v8/allocated_objects': {'size': 4},
+          'skia': {'not_size': 10,
+          'allocated_objects_size': 5},
+          'skia/cache1': {'size': 24}
+        }
+      ),
+      MakeRawMemoryDumpEvent(
+        pid=process.pid, allocators={
+          'skia/cache2': {'not_size': 20},
+          'skia/cache2/obj1': {'size': 8},
+          'skia/cache2/obj2': {'size': 9},
+          'skia_different/obj': {'size': 30},
+          'skia_different/obj/not_counted': {'size': 26},
+          'global/0xdead': {'size': 26}
+        }
+      )
+    ]
+    memory_dump = memory_dump_event.ProcessMemoryDumpEvent(process, events)
+
+    EXPECTED_ALLOCATORS = {
+      'skia': {
+        'allocated_objects_size': 5,
+        'not_size': 30,
+        'size': 41
+      },
+      'v8': {
+        'allocated_objects_size': 5,
+        'size': 10
+      },
+      'skia_different': {'size': 30}
+    }
+
+    self.assertEquals(memory_dump._allocators, EXPECTED_ALLOCATORS)
+
+  def testProcessMemoryDump_mmaps(self):
+    ALL = [2 ** x for x in range(8)]
+    (JAVA_SPACES, JAVA_CACHE, ASHMEM, NATIVE_1, NATIVE_2, STACK, FILES_APK,
+     DEVICE_GPU) = ALL
+
+    memory_dump = TestProcessDumpEvent(mmaps={
+      '/dev/ashmem/dalvik-space-foo': {'pss': JAVA_SPACES},
+      '/dev/ashmem/dalvik-jit-code-cache': {'pss': JAVA_CACHE},
+      '/dev/ashmem/other-random-stuff': {'pss': ASHMEM},
+      '[heap] bar': {'pss': NATIVE_1},
+      '': {'pss': NATIVE_2},
+      '[stack thingy]': {'pss': STACK},
+      'my_little_app.apk': {'pss': FILES_APK},
+      '/dev/mali': {'pss': DEVICE_GPU}
+    })
+
+    EXPECTED = {
+      '/': sum(ALL),
+      '/Android/Java runtime': JAVA_SPACES + JAVA_CACHE,
+      '/Android/Ashmem': ASHMEM,
+      '/Android': JAVA_SPACES + JAVA_CACHE + ASHMEM,
+      '/Native heap': NATIVE_1 + NATIVE_2,
+      '/Stack': STACK,
+      '/Files/apk': FILES_APK,
+      '/Devices': DEVICE_GPU}
+
+    self.assertTrue(memory_dump.has_mmaps)
+    for path, value in EXPECTED.iteritems():
+      self.assertEquals(
+          value,
+          memory_dump.GetMemoryBucket(path).GetValue('proportional_resident'))
+
+  def testProcessMemoryDump_composability(self):
+    java_spaces = 100
+    process = mock.Mock()
+    process.pid = 1234
+    allocators = {'v8': {'size': 10}}
+    mmaps = {'/dev/ashmem/dalvik-space-foo': {'pss': java_spaces}}
+
+    events = [MakeRawMemoryDumpEvent(pid=process.pid, allocators=allocators),
+              MakeRawMemoryDumpEvent(pid=process.pid, mmaps=mmaps)]
+    memory_dump = memory_dump_event.ProcessMemoryDumpEvent(process, events)
+
+    self.assertEquals(memory_dump._allocators, allocators)
+
+    EXPECTED_MMAPS = {
+      '/': java_spaces,
+      '/Android/Java runtime': java_spaces,
+      '/Android': java_spaces,
+    }
+
+    self.assertTrue(memory_dump.has_mmaps)
+    for path, value in EXPECTED_MMAPS.iteritems():
+      self.assertEquals(value,
+          memory_dump.GetMemoryBucket(path).GetValue('proportional_resident'))
+
+
+class MemoryDumpEventUnitTest(unittest.TestCase):
+  def testRepr(self):
+    process_dump1 = TestProcessDumpEvent(
+        mmaps={'/dev/ashmem/other-ashmem': {'pss': 5}},
+        allocators={'v8': {'size': 10, 'allocated_objects_size' : 5}})
+    process_dump2 = TestProcessDumpEvent(
+        mmaps={'/dev/ashmem/libc malloc': {'pss': 42, 'pd': 27}},
+        allocators={'v8': {'size': 20, 'allocated_objects_size' : 10},
+                    'oilpan': {'size': 40}})
+    global_dump = memory_dump_event.GlobalMemoryDump(
+        [process_dump1, process_dump2])
+
+    self.assertEquals(
+        repr(process_dump1),
+        'ProcessMemoryDumpEvent[pid=1234, allocated_objects_v8=5,'
+        ' allocator_v8=10, mmaps_ashmem=5, mmaps_java_heap=0,'
+        ' mmaps_native_heap=0, mmaps_overall_pss=5, mmaps_private_dirty=0]')
+    self.assertEquals(
+        repr(process_dump2),
+        'ProcessMemoryDumpEvent[pid=1234, allocated_objects_v8=10,'
+        ' allocator_oilpan=40, allocator_v8=20, mmaps_ashmem=0,'
+        ' mmaps_java_heap=0, mmaps_native_heap=42, mmaps_overall_pss=42,'
+        ' mmaps_private_dirty=27]')
+    self.assertEquals(
+        repr(global_dump),
+        'GlobalMemoryDump[id=123456ABCDEF, allocated_objects_v8=15,'
+        ' allocator_oilpan=40, allocator_v8=30, mmaps_ashmem=5,'
+        ' mmaps_java_heap=0, mmaps_native_heap=42, mmaps_overall_pss=47,'
+        ' mmaps_private_dirty=27]')
+
+  def testDumpEventsTiming(self):
+    process = mock.Mock()
+    process.pid = 1
+    composable_dump = memory_dump_event.ProcessMemoryDumpEvent(
+        process,
+        [
+          MakeRawMemoryDumpEvent(pid=process.pid, start=8),
+          MakeRawMemoryDumpEvent(pid=process.pid, start=16),
+          MakeRawMemoryDumpEvent(pid=process.pid, start=10)
+        ])
+    self.assertAlmostEquals(8.0, composable_dump.start)
+    self.assertAlmostEquals(16.0, composable_dump.end)
+
+    memory_dump = memory_dump_event.GlobalMemoryDump([
+        composable_dump,
+        TestProcessDumpEvent(pid=3, start=8),
+        TestProcessDumpEvent(pid=2, start=13),
+        TestProcessDumpEvent(pid=4, start=7)])
+
+    self.assertFalse(memory_dump.has_mmaps)
+    self.assertEquals(4, len(list(memory_dump.IterProcessMemoryDumps())))
+    self.assertItemsEqual([1, 2, 3, 4], memory_dump.pids)
+    self.assertAlmostEquals(7.0, memory_dump.start)
+    self.assertAlmostEquals(16.0, memory_dump.end)
+    self.assertAlmostEquals(9.0, memory_dump.duration)
+
+  def testGetMemoryUsage(self):
+    ALL = [2 ** x for x in range(7)]
+    (JAVA_HEAP_1, JAVA_HEAP_2, ASHMEM_1, ASHMEM_2, NATIVE,
+     DIRTY_1, DIRTY_2) = ALL
+
+    memory_dump = memory_dump_event.GlobalMemoryDump([
+        TestProcessDumpEvent(pid=1, mmaps={
+            '/dev/ashmem/dalvik-alloc space': {'pss': JAVA_HEAP_1}}),
+        TestProcessDumpEvent(pid=2, mmaps={
+            '/dev/ashmem/other-ashmem': {'pss': ASHMEM_1, 'pd': DIRTY_1}}),
+        TestProcessDumpEvent(pid=3, mmaps={
+            '[heap] native': {'pss': NATIVE, 'pd': DIRTY_2},
+            '/dev/ashmem/dalvik-zygote space': {'pss': JAVA_HEAP_2}}),
+        TestProcessDumpEvent(pid=4, mmaps={
+            '/dev/ashmem/other-ashmem': {'pss': ASHMEM_2}})])
+
+    self.assertTrue(memory_dump.has_mmaps)
+    self.assertItemsEqual([1, 2, 3, 4], memory_dump.pids)
+    self.assertEquals({'mmaps_overall_pss': sum(ALL[:5]),
+                       'mmaps_private_dirty': DIRTY_1 + DIRTY_2,
+                       'mmaps_java_heap': JAVA_HEAP_1 + JAVA_HEAP_2,
+                       'mmaps_ashmem': ASHMEM_1 + ASHMEM_2,
+                       'mmaps_native_heap': NATIVE},
+                      memory_dump.GetMemoryUsage())
+
+  def testGetMemoryUsageWithAllocators(self):
+    process_dump1 = TestProcessDumpEvent(
+        mmaps={'/dev/ashmem/other-ashmem': {'pss': 5}},
+        allocators={'v8': {'size': 10, 'allocated_objects_size' : 5}})
+    process_dump2 = TestProcessDumpEvent(
+        mmaps={'/dev/ashmem/other-ashmem': {'pss': 5}},
+        allocators={'v8': {'size': 20, 'allocated_objects_size' : 10}})
+    memory_dump = memory_dump_event.GlobalMemoryDump(
+        [process_dump1, process_dump2])
+    self.assertEquals({'mmaps_overall_pss': 10,
+                       'mmaps_private_dirty': 0,
+                       'mmaps_java_heap': 0,
+                       'mmaps_ashmem': 10,
+                       'mmaps_native_heap': 0,
+                       'allocator_v8': 30,
+                       'allocated_objects_v8': 15},
+                      memory_dump.GetMemoryUsage())
+
+  def testGetMemoryUsageWithAndroidMemtrack(self):
+    GL1, EGL1, GL2, EGL2 = [2 ** x for x in range(4)]
+    process_dump1 = TestProcessDumpEvent(
+        allocators={'gpu/android_memtrack/gl': {'memtrack_pss' : GL1},
+                    'gpu/android_memtrack/graphics': {'memtrack_pss': EGL1}})
+    process_dump2 = TestProcessDumpEvent(
+        allocators={'gpu/android_memtrack/gl': {'memtrack_pss' : GL2},
+                    'gpu/android_memtrack/graphics': {'memtrack_pss': EGL2}})
+    memory_dump = memory_dump_event.GlobalMemoryDump(
+        [process_dump1, process_dump2])
+    self.assertEquals({'android_memtrack_gl': GL1 + GL2,
+                       'android_memtrack_graphics': EGL1 + EGL2},
+                      memory_dump.GetMemoryUsage())
+
+  def testGetMemoryUsageDiscountsTracing(self):
+    ALL = [2 ** x for x in range(5)]
+    (HEAP, DIRTY, MALLOC, TRACING_1, TRACING_2) = ALL
+
+    memory_dump = memory_dump_event.GlobalMemoryDump([
+        TestProcessDumpEvent(
+            mmaps={'/dev/ashmem/libc malloc': {'pss': HEAP + TRACING_2,
+                                               'pd': DIRTY + TRACING_2}},
+            allocators={
+                'tracing': {'size': TRACING_1, 'resident_size': TRACING_2},
+                'malloc': {'size': MALLOC + TRACING_1}})])
+
+    self.assertEquals({'mmaps_overall_pss': HEAP,
+                       'mmaps_private_dirty': DIRTY,
+                       'mmaps_java_heap': 0,
+                       'mmaps_ashmem': 0,
+                       'mmaps_native_heap': HEAP,
+                       'allocator_tracing': TRACING_1,
+                       'allocator_malloc': MALLOC},
+                      memory_dump.GetMemoryUsage())
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/model.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/model.py
new file mode 100644
index 0000000..278b70c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/model.py
@@ -0,0 +1,279 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""A container for timeline-based events and traces and can handle importing
+raw event data from different sources. This model closely resembles that in the
+trace_viewer project:
+https://code.google.com/p/trace-viewer/
+"""
+
+import logging
+from operator import attrgetter
+
+from telemetry.timeline import async_slice as async_slice_module
+from telemetry.timeline import bounds
+from telemetry.timeline import event_container
+from telemetry.timeline import inspector_importer
+from telemetry.timeline import process as process_module
+from telemetry.timeline import slice as slice_module
+from telemetry.timeline import surface_flinger_importer
+from telemetry.timeline import tab_id_importer
+from telemetry.timeline import trace_event_importer
+from tracing.trace_data import trace_data as trace_data_module
+
+
+# Register importers for data
+
+_IMPORTERS = [
+    inspector_importer.InspectorTimelineImporter,
+    tab_id_importer.TabIdImporter,
+    trace_event_importer.TraceEventTimelineImporter,
+    surface_flinger_importer.SurfaceFlingerTimelineImporter
+]
+
+
+class MarkerMismatchError(Exception):
+  def __init__(self):
+    super(MarkerMismatchError, self).__init__(
+        'Number or order of timeline markers does not match provided labels')
+
+
+class MarkerOverlapError(Exception):
+  def __init__(self):
+    super(MarkerOverlapError, self).__init__(
+        'Overlapping timeline markers found')
+
+
+def IsSliceOrAsyncSlice(t):
+  if t == async_slice_module.AsyncSlice:
+    return True
+  return t == slice_module.Slice
+
+
+class TimelineModel(event_container.TimelineEventContainer):
+  def __init__(self, trace_data=None, shift_world_to_zero=True):
+    """ Initializes a TimelineModel.
+
+    Args:
+        trace_data: trace_data.TraceData containing events to import
+        shift_world_to_zero: If true, the events will be shifted such that the
+            first event starts at time 0.
+    """
+    super(TimelineModel, self).__init__(name='TimelineModel', parent=None)
+    self._bounds = bounds.Bounds()
+    self._thread_time_bounds = {}
+    self._processes = {}
+    self._browser_process = None
+    self._gpu_process = None
+    self._surface_flinger_process = None
+    self._frozen = False
+    self._tab_ids_to_renderer_threads_map = {}
+    self.import_errors = []
+    self.metadata = []
+    self.flow_events = []
+    self._global_memory_dumps = None
+    if trace_data is not None:
+      self.ImportTraces(trace_data, shift_world_to_zero=shift_world_to_zero)
+
+  def SetGlobalMemoryDumps(self, global_memory_dumps):
+    """Populates the model with a sequence of GlobalMemoryDump objects."""
+    assert not self._frozen and self._global_memory_dumps is None
+    # Keep dumps sorted in chronological order.
+    self._global_memory_dumps = tuple(sorted(global_memory_dumps,
+                                             key=lambda dump: dump.start))
+
+  def IterGlobalMemoryDumps(self):
+    """Iterate over the memory dump events of this model."""
+    return iter(self._global_memory_dumps or [])
+
+  def IterChildContainers(self):
+    for process in self._processes.itervalues():
+      yield process
+
+  def GetAllProcesses(self):
+    return self._processes.values()
+
+  def GetAllThreads(self):
+    threads = []
+    for process in self._processes.values():
+      threads.extend(process.threads.values())
+    return threads
+
+  @property
+  def bounds(self):
+    return self._bounds
+
+  @property
+  def processes(self):
+    return self._processes
+
+  @property
+  def browser_process(self):
+    return self._browser_process
+
+  @browser_process.setter
+  def browser_process(self, browser_process):
+    self._browser_process = browser_process
+
+  @property
+  def gpu_process(self):
+    return self._gpu_process
+
+  @gpu_process.setter
+  def gpu_process(self, gpu_process):
+    self._gpu_process = gpu_process
+
+  @property
+  def surface_flinger_process(self):
+    return self._surface_flinger_process
+
+  @surface_flinger_process.setter
+  def surface_flinger_process(self, surface_flinger_process):
+    self._surface_flinger_process = surface_flinger_process
+
+  def AddMappingFromTabIdToRendererThread(self, tab_id, renderer_thread):
+    if self._frozen:
+      raise Exception('Cannot add mapping from tab id to renderer thread once '
+                      'trace is imported')
+    self._tab_ids_to_renderer_threads_map[tab_id] = renderer_thread
+
+  def ImportTraces(self, trace_data, shift_world_to_zero=True):
+    """Populates the model with the provided trace data.
+
+    trace_data must be an instance of TraceData.
+
+    Passing shift_world_to_zero=True causes the events to be shifted such that
+    the first event starts at time 0.
+    """
+    if self._frozen:
+      raise Exception("Cannot add events once trace is imported")
+    assert isinstance(trace_data, trace_data_module.TraceData)
+
+    importers = self._CreateImporters(trace_data)
+
+    for importer in importers:
+      # TODO: catch exceptions here and add it to error list
+      importer.ImportEvents()
+    self.FinalizeImport(shift_world_to_zero, importers)
+
+  def FinalizeImport(self, shift_world_to_zero=False, importers=None):
+    if importers == None:
+      importers = []
+    self.UpdateBounds()
+    if not self.bounds.is_empty:
+      for process in self._processes.itervalues():
+        process.AutoCloseOpenSlices(self.bounds.max,
+                                    self._thread_time_bounds)
+
+    for importer in importers:
+      importer.FinalizeImport()
+
+    for process in self.processes.itervalues():
+      process.FinalizeImport()
+
+    if shift_world_to_zero:
+      self.ShiftWorldToZero()
+    self.UpdateBounds()
+
+    # Because of FinalizeImport, it would probably be a good idea
+    # to prevent the timeline from from being modified.
+    self._frozen = True
+
+  def ShiftWorldToZero(self):
+    self.UpdateBounds()
+    if self._bounds.is_empty:
+      return
+    shift_amount = self._bounds.min
+    for event in self.IterAllEvents():
+      event.start -= shift_amount
+
+  def UpdateBounds(self):
+    self._bounds.Reset()
+    for event in self.IterAllEvents():
+      self._bounds.AddValue(event.start)
+      self._bounds.AddValue(event.end)
+
+    self._thread_time_bounds = {}
+    for thread in self.GetAllThreads():
+      self._thread_time_bounds[thread] = bounds.Bounds()
+      for event in thread.IterEventsInThisContainer(
+          event_type_predicate=lambda t: True,
+          event_predicate=lambda e: True):
+        if event.thread_start != None:
+          self._thread_time_bounds[thread].AddValue(event.thread_start)
+        if event.thread_end != None:
+          self._thread_time_bounds[thread].AddValue(event.thread_end)
+
+  def GetOrCreateProcess(self, pid):
+    if pid not in self._processes:
+      assert not self._frozen
+      self._processes[pid] = process_module.Process(self, pid)
+    return self._processes[pid]
+
+  def FindTimelineMarkers(self, timeline_marker_names):
+    """Find the timeline events with the given names.
+
+    If the number and order of events found does not match the names,
+    raise an error.
+    """
+    # Make sure names are in a list and remove all None names
+    if not isinstance(timeline_marker_names, list):
+      timeline_marker_names = [timeline_marker_names]
+    names = [x for x in timeline_marker_names if x is not None]
+
+    # Gather all events that match the names and sort them.
+    events = []
+    name_set = set()
+    for name in names:
+      name_set.add(name)
+
+    def IsEventNeeded(event):
+      if event.parent_slice != None:
+        return
+      return event.name in name_set
+
+    events = list(self.IterAllEvents(
+      recursive=True,
+      event_type_predicate=IsSliceOrAsyncSlice,
+      event_predicate=IsEventNeeded))
+    events.sort(key=attrgetter('start'))
+
+    # Check if the number and order of events matches the provided names,
+    # and that the events don't overlap.
+    if len(events) != len(names):
+      raise MarkerMismatchError()
+    for (i, event) in enumerate(events):
+      if event.name != names[i]:
+        raise MarkerMismatchError()
+    for i in xrange(0, len(events)):
+      for j in xrange(i+1, len(events)):
+        if events[j].start < events[i].start + events[i].duration:
+          raise MarkerOverlapError()
+
+    return events
+
+  def GetRendererProcessFromTabId(self, tab_id):
+    renderer_thread = self.GetRendererThreadFromTabId(tab_id)
+    if renderer_thread:
+      return renderer_thread.parent
+    return None
+
+  def GetRendererThreadFromTabId(self, tab_id):
+    return self._tab_ids_to_renderer_threads_map.get(tab_id, None)
+
+  def _CreateImporters(self, trace_data):
+    def FindImporterClassForPart(part):
+      for importer_class in _IMPORTERS:
+        if importer_class.GetSupportedPart() == part:
+          return importer_class
+
+    importers = []
+    for part in trace_data.active_parts:
+      importer_class = FindImporterClassForPart(part)
+      if not importer_class:
+        logging.warning('No importer found for %s' % repr(part))
+      else:
+        importers.append(importer_class(self, trace_data))
+        importers.sort(key=lambda k: k.import_order)
+
+    return importers
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/model_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/model_unittest.py
new file mode 100644
index 0000000..bb1af1b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/model_unittest.py
@@ -0,0 +1,24 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from telemetry.timeline import model as model_module
+from tracing.trace_data import trace_data
+
+
+class TimelineModelUnittest(unittest.TestCase):
+  def testEmptyImport(self):
+    model_module.TimelineModel(trace_data.CreateTraceDataFromRawData({}))
+
+  def testBrowserProcess(self):
+    builder = trace_data.TraceDataBuilder()
+    builder.AddTraceFor(trace_data.CHROME_TRACE_PART, {
+      "traceEvents": [
+        {"name": "process_name", "args": {"name": "Browser"},
+         "pid": 5, "ph": "M"},
+        {"name": "thread_name", "args": {"name": "CrBrowserMain"},
+         "pid": 5, "tid": 32578, "ph": "M"}]})
+    model = model_module.TimelineModel(builder.AsData())
+    self.assertEquals(5, model.browser_process.pid)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/process.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/process.py
new file mode 100644
index 0000000..516f03f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/process.py
@@ -0,0 +1,101 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import telemetry.timeline.counter as tracing_counter
+import telemetry.timeline.event as event_module
+import telemetry.timeline.event_container as event_container
+import telemetry.timeline.thread as tracing_thread
+from telemetry.timeline import memory_dump_event
+
+
+class Process(event_container.TimelineEventContainer):
+  """The Process represents a single userland process in the trace.
+  """
+  def __init__(self, parent, pid):
+    super(Process, self).__init__('process %s' % pid, parent)
+    self.pid = pid
+    self.labels = None
+    self.uptime_seconds = None
+    self._threads = {}
+    self._counters = {}
+    self._trace_buffer_overflow_event = None
+    self._memory_dump_events = {}
+
+  @property
+  def trace_buffer_did_overflow(self):
+    return self._trace_buffer_overflow_event is not None
+
+  @property
+  def trace_buffer_overflow_event(self):
+    return self._trace_buffer_overflow_event
+
+  @property
+  def threads(self):
+    return self._threads
+
+  @property
+  def counters(self):
+    return self._counters
+
+  def IterChildContainers(self):
+    for thread in self._threads.itervalues():
+      yield thread
+    for counter in self._counters.itervalues():
+      yield counter
+
+  def IterEventsInThisContainer(self, event_type_predicate, event_predicate):
+    if (self.trace_buffer_did_overflow and
+        event_type_predicate(event_module.TimelineEvent) and
+        event_predicate(self._trace_buffer_overflow_event)):
+      yield self._trace_buffer_overflow_event
+    if (self._memory_dump_events and
+        event_type_predicate(memory_dump_event.ProcessMemoryDumpEvent)):
+      for memory_dump in self._memory_dump_events.itervalues():
+        if event_predicate(memory_dump):
+          yield memory_dump
+
+  def GetOrCreateThread(self, tid):
+    thread = self.threads.get(tid, None)
+    if thread:
+      return thread
+    thread = tracing_thread.Thread(self, tid)
+    self._threads[tid] = thread
+    return thread
+
+  def GetCounter(self, category, name):
+    counter_id = category + '.' + name
+    if counter_id in self.counters:
+      return self.counters[counter_id]
+    raise ValueError(
+        'Counter %s not found in process with id %s.' % (counter_id,
+                                                         self.pid))
+  def GetOrCreateCounter(self, category, name):
+    try:
+      return self.GetCounter(category, name)
+    except ValueError:
+      ctr = tracing_counter.Counter(self, category, name)
+      self._counters[ctr.full_name] = ctr
+      return ctr
+
+  def AutoCloseOpenSlices(self, max_timestamp, thread_time_bounds):
+    for thread in self._threads.itervalues():
+      thread.AutoCloseOpenSlices(max_timestamp, thread_time_bounds[thread].max)
+
+  def SetTraceBufferOverflowTimestamp(self, timestamp):
+    # TODO: use instant event for trace_buffer_overflow_event
+    self._trace_buffer_overflow_event = event_module.TimelineEvent(
+        "TraceBufferInfo", "trace_buffer_overflowed", timestamp, 0)
+
+  def AddMemoryDumpEvent(self, memory_dump):
+    """Add a ProcessMemoryDumpEvent to this process."""
+    if memory_dump.dump_id in self._memory_dump_events:
+      raise ValueError('Duplicate memory dump id %s in process with id %s.' % (
+          memory_dump.dump_id, self.pid))
+    self._memory_dump_events[memory_dump.dump_id] = memory_dump
+
+  def FinalizeImport(self):
+    for thread in self._threads.itervalues():
+      thread.FinalizeImport()
+    for counter in self._counters.itervalues():
+      counter.FinalizeImport()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/sample.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/sample.py
new file mode 100644
index 0000000..806f60f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/sample.py
@@ -0,0 +1,20 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import telemetry.timeline.event as timeline_event
+
+
+class Sample(timeline_event.TimelineEvent):
+  """A Sample represents a sample taken at an instant in time
+  plus parameters associated with that sample.
+
+  NOTE: The Sample class implements the same interface as
+  Slice. These must be kept in sync.
+
+  All time units are stored in milliseconds.
+  """
+  def __init__(self, parent_thread, category, name, timestamp, args=None):
+    super(Sample, self).__init__(
+        category, name, timestamp, 0, args=args)
+    self.parent_thread = parent_thread
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/slice.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/slice.py
new file mode 100644
index 0000000..3a39a80
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/slice.py
@@ -0,0 +1,78 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import telemetry.timeline.event as timeline_event
+
+
+class Slice(timeline_event.TimelineEvent):
+  """A Slice represents an interval of time plus parameters associated
+  with that interval.
+
+  NOTE: The Sample class implements the same interface as
+  Slice. These must be kept in sync.
+
+  All time units are stored in milliseconds.
+  """
+  def __init__(self, parent_thread, category, name, timestamp, duration=0,
+               thread_timestamp=None, thread_duration=None, args=None):
+    super(Slice, self).__init__(
+        category, name, timestamp, duration, thread_timestamp, thread_duration,
+        args)
+    self.parent_thread = parent_thread
+    self.parent_slice = None
+    self.sub_slices = []
+    self.did_not_finish = False
+
+  def AddSubSlice(self, sub_slice):
+    assert sub_slice.parent_slice == self
+    self.sub_slices.append(sub_slice)
+
+  def IterEventsInThisContainerRecrusively(self, stack=None):
+    # This looks awkward, but it lets us create only a single iterator instead
+    # of having to create one iterator for every subslice found.
+    if stack == None:
+      stack = []
+    else:
+      assert len(stack) == 0
+    stack.extend(reversed(self.sub_slices))
+    while len(stack):
+      s = stack.pop()
+      yield s
+      stack.extend(reversed(s.sub_slices))
+
+  @property
+  def self_time(self):
+    """Time spent in this function less any time spent in child events."""
+    child_total = sum(
+      [e.duration for e in self.sub_slices])
+    return self.duration - child_total
+
+  @property
+  def self_thread_time(self):
+    """Thread (scheduled) time spent in this function less any thread time spent
+    in child events. Returns None if the slice or any of its children does not
+    have a thread_duration value.
+    """
+    if not self.thread_duration:
+      return None
+
+    child_total = 0
+    for e in self.sub_slices:
+      if e.thread_duration == None:
+        return None
+      child_total += e.thread_duration
+
+    return self.thread_duration - child_total
+
+  def _GetSubSlicesRecursive(self):
+    for sub_slice in self.sub_slices:
+      for s in sub_slice.GetAllSubSlices():
+        yield s
+      yield sub_slice
+
+  def GetAllSubSlices(self):
+    return list(self._GetSubSlicesRecursive())
+
+  def GetAllSubSlicesOfName(self, name):
+    return [e for e in self.GetAllSubSlices() if e.name == name]
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/slice_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/slice_unittest.py
new file mode 100644
index 0000000..67106c3
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/slice_unittest.py
@@ -0,0 +1,35 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from telemetry.timeline.slice import Slice
+
+
+class SliceTest(unittest.TestCase):
+  def testChildrenLogic(self):
+    # [      top          ]
+    #   [ a  ]    [  b  ]
+    #    [x]
+    top = Slice(None, 'cat', 'top', 0, duration=10, thread_timestamp=0,
+                thread_duration=5)
+    a = Slice(None, 'cat', 'a', 1, duration=2, thread_timestamp=0.5,
+              thread_duration=1)
+    x = Slice(None, 'cat', 'x', 1.5, duration=0.25, thread_timestamp=0.75,
+              thread_duration=0.125)
+    b = Slice(None, 'cat', 'b', 5, duration=2, thread_timestamp=None,
+              thread_duration=None)
+    top.sub_slices.extend([a, b])
+    a.sub_slices.append(x)
+
+    all_children = list(top.IterEventsInThisContainerRecrusively())
+    self.assertEquals([a, x, b], all_children)
+
+    self.assertEquals(x.self_time, 0.25)
+    self.assertEquals(a.self_time, 1.75) # 2 - 0.25
+    self.assertEquals(top.self_time, 6) # 10 - 2 - 2
+
+    self.assertEquals(x.self_thread_time, 0.125)
+    self.assertEquals(a.self_thread_time, 0.875) # 1 - 0.125
+    self.assertEquals(top.self_thread_time, None) # b has no thread time
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/surface_flinger_importer.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/surface_flinger_importer.py
new file mode 100644
index 0000000..a02637f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/surface_flinger_importer.py
@@ -0,0 +1,38 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.timeline import importer
+from tracing.trace_data import trace_data as trace_data_module
+
+
+class SurfaceFlingerTimelineImporter(importer.TimelineImporter):
+  def __init__(self, model, trace_data):
+    super(SurfaceFlingerTimelineImporter, self).__init__(
+        model, trace_data, import_order=2)
+    traces = trace_data.GetTracesFor(trace_data_module.SURFACE_FLINGER_PART)
+    assert len(traces) == 1
+    self._events = traces[0]
+    self._surface_flinger_process = None
+
+  @staticmethod
+  def GetSupportedPart():
+    return trace_data_module.SURFACE_FLINGER_PART
+
+  def ImportEvents(self):
+    for event in self._events:
+      self._surface_flinger_process = self._model.GetOrCreateProcess(
+          event['pid'])
+      self._surface_flinger_process.name = 'SurfaceFlinger'
+      thread = self._surface_flinger_process.GetOrCreateThread(event['tid'])
+      thread.BeginSlice(event['cat'],
+                        event['name'],
+                        event['ts'],
+                        args=event.get('args'))
+      thread.EndSlice(event['ts'])
+
+  def FinalizeImport(self):
+    """Called by the Model after all other importers have imported their
+    events."""
+    self._model.UpdateBounds()
+    self._model.surface_flinger_process = self._surface_flinger_process
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/tab_id_importer.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/tab_id_importer.py
new file mode 100644
index 0000000..025d80b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/tab_id_importer.py
@@ -0,0 +1,65 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+
+from telemetry.timeline import importer
+from tracing.trace_data import trace_data as trace_data_module
+
+
+class TraceBufferOverflowException(Exception):
+  pass
+
+
+class TabIdImporter(importer.TimelineImporter):
+  def __init__(self, model, trace_data):
+    # Needs to run after all other importers so overflow events have been
+    # created on the model.
+    super(TabIdImporter, self).__init__(
+        model,
+        trace_data,
+        import_order=999)
+    self._trace_data = trace_data
+
+  @staticmethod
+  def GetSupportedPart():
+    return trace_data_module.TAB_ID_PART
+
+  def ImportEvents(self):
+    pass
+
+  def FinalizeImport(self):
+    self._CheckTraceBufferOverflow()
+    self._CreateTabIdsToThreadsMap()
+
+  def _CheckTraceBufferOverflow(self):
+    # Since _CreateTabIdsToThreadsMap() relies on markers output on timeline
+    # tracing data, it may not work in case we have trace events dropped due to
+    # trace buffer overflow.
+    for process in self._model.GetAllProcesses():
+      if process.trace_buffer_did_overflow:
+        raise TraceBufferOverflowException(
+            'Trace buffer of process with pid=%d overflowed at timestamp %d. '
+            'Raw trace data:\n%s' %
+            (process.pid, process.trace_buffer_overflow_event.start,
+             repr(self._trace_data)))
+
+  def _CreateTabIdsToThreadsMap(self):
+    tab_id_events = []
+    for tab_ids in self._trace_data.GetTracesFor(trace_data_module.TAB_ID_PART):
+      tab_id_events.extend(tab_ids)
+
+    for tab_id in tab_id_events:
+      try:
+        timeline_markers = self._model.FindTimelineMarkers(tab_id)
+      # If timeline_markers with name equals |tab_id| can't be found, it's
+      # non-fatal.
+      except Exception:
+        logging.warning('Cannot find timeline marker for tab with id=%s' %
+                        tab_id)
+        continue
+      assert len(timeline_markers) == 1
+      assert timeline_markers[0].start_thread == timeline_markers[0].end_thread
+      self._model.AddMappingFromTabIdToRendererThread(
+          tab_id, timeline_markers[0].start_thread)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/tab_id_importer_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/tab_id_importer_unittest.py
new file mode 100644
index 0000000..6b706ba
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/tab_id_importer_unittest.py
@@ -0,0 +1,72 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from telemetry.timeline import model as timeline_model
+from telemetry.timeline import tab_id_importer
+from tracing.trace_data import trace_data as trace_data_module
+
+
+class TabIdImporterUnitTest(unittest.TestCase):
+  def testImportOverflowedTrace(self):
+    builder = trace_data_module.TraceDataBuilder()
+    builder.AddTraceFor(trace_data_module.CHROME_TRACE_PART, {'traceEvents': [
+      {'name': 'a', 'args': {}, 'pid': 1, 'ts': 7, 'cat': 'foo',
+       'tid': 1, 'ph': 'B'},
+      {'name': 'a', 'args': {}, 'pid': 1, 'ts': 8, 'cat': 'foo',
+       'tid': 1, 'ph': 'E'},
+      {'name': 'b', 'args': {}, 'pid': 2, 'ts': 9, 'cat': 'foo',
+       'tid': 2, 'ph': 'B'},
+      {'name': 'b', 'args': {}, 'pid': 2, 'ts': 10, 'cat': 'foo',
+       'tid': 2, 'ph': 'E'},
+      {'name': 'trace_buffer_overflowed',
+       'args': {'overflowed_at_ts': 12},
+        'pid': 2, 'ts': 0, 'tid': 2, 'ph': 'M'}
+    ]})
+    builder.AddTraceFor(
+        trace_data_module.TAB_ID_PART, ['tab-id-1', 'tab-id-2'])
+
+    with self.assertRaises(tab_id_importer.TraceBufferOverflowException) \
+        as context:
+      timeline_model.TimelineModel(builder.AsData())
+    self.assertTrue(
+        'Trace buffer of process with pid=2 overflowed at timestamp 12' in
+        context.exception.message)
+
+  def testTraceEventsWithTabIdsMarkers(self):
+    builder = trace_data_module.TraceDataBuilder()
+    builder.AddTraceFor(trace_data_module.CHROME_TRACE_PART, {'traceEvents': [
+      {'name': 'a', 'args': {}, 'pid': 1, 'ts': 20, 'tts': 10, 'cat': 'foo',
+       'tid': 1, 'ph': 'B'},
+      # tab-id-1
+      {'name': 'tab-id-1', 'args': {}, 'pid': 1, 'ts': 25, 'cat': 'foo',
+       'tid': 1,
+         'ph': 'S', 'id': 72},
+      {'name': 'a', 'args': {}, 'pid': 1, 'ts': 30, 'tts': 20, 'cat': 'foo',
+       'tid': 1, 'ph': 'E'},
+      {'name': 'tab-id-1', 'args': {}, 'pid': 1, 'ts': 35, 'cat': 'foo',
+       'tid': 1,
+         'ph': 'F', 'id': 72},
+      # tab-id-2
+      {'name': 'tab-id-2', 'args': {}, 'pid': 1, 'ts': 25, 'cat': 'foo',
+       'tid': 2,
+         'ph': 'S', 'id': 72},
+      {'name': 'tab-id-2', 'args': {}, 'pid': 1, 'ts': 26, 'cat': 'foo',
+       'tid': 2,
+         'ph': 'F', 'id': 72},
+     ]})
+    builder.AddTraceFor(
+        trace_data_module.TAB_ID_PART, ['tab-id-1', 'tab-id-2'])
+
+    m = timeline_model.TimelineModel(builder.AsData())
+    processes = m.GetAllProcesses()
+    self.assertEqual(1, len(processes))
+    self.assertIs(processes[0], m.GetRendererProcessFromTabId('tab-id-1'))
+    self.assertIs(processes[0], m.GetRendererProcessFromTabId('tab-id-2'))
+
+    p = processes[0]
+    self.assertEqual(2, len(p.threads))
+    self.assertIs(p.threads[1], m.GetRendererThreadFromTabId('tab-id-1'))
+    self.assertIs(p.threads[2], m.GetRendererThreadFromTabId('tab-id-2'))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/thread.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/thread.py
new file mode 100644
index 0000000..5810ae3
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/thread.py
@@ -0,0 +1,268 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import telemetry.timeline.async_slice as async_slice_module
+import telemetry.timeline.event_container as event_container
+import telemetry.timeline.flow_event as flow_event_module
+import telemetry.timeline.sample as sample_module
+import telemetry.timeline.slice as slice_module
+
+
+class Thread(event_container.TimelineEventContainer):
+  """A Thread stores all the trace events collected for a particular
+  thread. We organize the synchronous slices on a thread by "subrows," where
+  subrow 0 has all the root slices, subrow 1 those nested 1 deep, and so on.
+  The asynchronous slices are stored in an AsyncSliceGroup object.
+  """
+  def __init__(self, process, tid):
+    super(Thread, self).__init__('thread %s' % tid, parent=process)
+    self.tid = tid
+    self._async_slices = []
+    self._flow_events = []
+    self._samples = []
+    self._toplevel_slices = []
+    self._all_slices = []
+
+    # State only valid during import.
+    self._open_slices = []
+    self._newly_added_slices = []
+
+  @property
+  def toplevel_slices(self):
+    return self._toplevel_slices
+
+  @property
+  def all_slices(self):
+    return self._all_slices
+
+  @property
+  def samples(self):
+    return self._samples
+
+  @property
+  def async_slices(self):
+    return self._async_slices
+
+  @property
+  def open_slice_count(self):
+    return len(self._open_slices)
+
+  def IterChildContainers(self):
+    return
+    yield # pylint: disable=unreachable
+
+  def IterEventsInThisContainer(self, event_type_predicate, event_predicate):
+    if event_type_predicate(slice_module.Slice):
+      for s in self._newly_added_slices:
+        if event_predicate(s):
+          yield s
+      for s in self._all_slices:
+        if event_predicate(s):
+          yield s
+
+    if event_type_predicate(async_slice_module.AsyncSlice):
+      for async_slice in self._async_slices:
+        if event_predicate(async_slice):
+          yield async_slice
+        for sub_slice in async_slice.IterEventsInThisContainerRecrusively():
+          if event_predicate(sub_slice):
+            yield sub_slice
+
+    if event_type_predicate(flow_event_module.FlowEvent):
+      for flow_event in self._flow_events:
+        if event_predicate(flow_event):
+          yield flow_event
+
+    if event_type_predicate(sample_module.Sample):
+      for sample in self._samples:
+        if event_predicate(sample):
+          yield sample
+
+  def AddSample(self, category, name, timestamp, args=None):
+    if len(self._samples) and timestamp < self._samples[-1].start:
+      raise ValueError(
+          'Samples must be added in increasing timestamp order')
+    sample = sample_module.Sample(self,
+        category, name, timestamp, args=args)
+    self._samples.append(sample)
+
+  def AddAsyncSlice(self, async_slice):
+    self._async_slices.append(async_slice)
+
+  def AddFlowEvent(self, flow_event):
+    self._flow_events.append(flow_event)
+
+  def BeginSlice(self, category, name, timestamp, thread_timestamp=None,
+                 args=None):
+    """Opens a new slice for the thread.
+    Calls to beginSlice and endSlice must be made with
+    non-monotonically-decreasing timestamps.
+
+    * category: Category to which the slice belongs.
+    * name: Name of the slice to add.
+    * timestamp: The timetsamp of the slice, in milliseconds.
+    * thread_timestamp: Thread specific clock (scheduled) timestamp of the
+                        slice, in milliseconds.
+    * args: Arguments associated with
+
+    Returns newly opened slice
+    """
+    if len(self._open_slices) > 0 and timestamp < self._open_slices[-1].start:
+      raise ValueError(
+          'Slices must be added in increasing timestamp order')
+    new_slice = slice_module.Slice(self, category, name, timestamp,
+                                    thread_timestamp=thread_timestamp,
+                                    args=args)
+    self._open_slices.append(new_slice)
+    new_slice.did_not_finish = True
+    self.PushSlice(new_slice)
+    return new_slice
+
+  def EndSlice(self, end_timestamp, end_thread_timestamp=None):
+    """ Ends the last begun slice in this group and pushes it onto the slice
+    array.
+
+    * end_timestamp: Timestamp when the slice ended in milliseconds
+    * end_thread_timestamp: Timestamp when the scheduled time of the slice ended
+                            in milliseconds
+
+    returns completed slice.
+    """
+    if not len(self._open_slices):
+      raise ValueError(
+          'EndSlice called without an open slice')
+    curr_slice = self._open_slices.pop()
+    if end_timestamp < curr_slice.start:
+      raise ValueError(
+          'Slice %s end time is before its start.' % curr_slice.name)
+    curr_slice.duration = end_timestamp - curr_slice.start
+    # On Windows, it is possible to have a value for |end_thread_timestamp|
+    # but not for |curr_slice.thread_start|, because it takes some time to
+    # initialize the thread time timer.
+    if curr_slice.thread_start != None and end_thread_timestamp != None:
+      curr_slice.thread_duration = (end_thread_timestamp -
+                                    curr_slice.thread_start)
+    curr_slice.did_not_finish = False
+    return curr_slice
+
+  def PushCompleteSlice(self, category, name, timestamp, duration,
+                        thread_timestamp, thread_duration, args=None):
+    new_slice = slice_module.Slice(self, category, name, timestamp,
+                                   thread_timestamp=thread_timestamp,
+                                   args=args)
+    if duration == None:
+      new_slice.did_not_finish = True
+    else:
+      new_slice.duration = duration
+      new_slice.thread_duration = thread_duration
+    self.PushSlice(new_slice)
+    return new_slice
+
+  def PushMarkSlice(self, category, name, timestamp, thread_timestamp,
+        args=None):
+    new_slice = slice_module.Slice(self, category, name, timestamp,
+                                   thread_timestamp=thread_timestamp,
+                                   args=args)
+    self.PushSlice(new_slice)
+    return new_slice
+
+  def PushSlice(self, new_slice):
+    self._newly_added_slices.append(new_slice)
+    return new_slice
+
+  def AutoCloseOpenSlices(self, max_timestamp, max_thread_timestamp):
+    for s in self._newly_added_slices:
+      if s.did_not_finish:
+        s.duration = max_timestamp - s.start
+        assert s.duration >= 0
+        if s.thread_start != None:
+          s.thread_duration = max_thread_timestamp - s.thread_start
+          assert s.thread_duration >= 0
+    self._open_slices = []
+
+  def IsTimestampValidForBeginOrEnd(self, timestamp):
+    if not len(self._open_slices):
+      return True
+    return timestamp >= self._open_slices[-1].start
+
+  def FinalizeImport(self):
+    self._BuildSliceSubRows()
+
+  def _BuildSliceSubRows(self):
+    """This function works by walking through slices by start time.
+
+     The basic idea here is to insert each slice as deep into the subrow
+     list as it can go such that every subslice is fully contained by its
+     parent slice.
+
+     Visually, if we start with this:
+      0:  [    a       ]
+      1:    [  b  ]
+      2:    [c][d]
+
+     To place this slice:
+                   [e]
+     We first check row 2's last item, [d]. [e] wont fit into [d] (they dont
+     even intersect). So we go to row 1. That gives us [b], and [d] wont fit
+     into that either. So, we go to row 0 and its last slice, [a]. That can
+     completely contain [e], so that means we should add [e] as a subslice
+     of [a]. That puts it on row 1, yielding:
+      0:  [    a       ]
+      1:    [  b  ][e]
+      2:    [c][d]
+
+     If we then get this slice:
+                          [f]
+     We do the same deepest-to-shallowest walk of the subrows trying to fit
+     it. This time, it doesn't fit in any open slice. So, we simply append
+     it to row 0 (a root slice):
+      0:  [    a       ]  [f]
+      1:    [  b  ][e]
+    """
+    def CompareSlices(s1, s2):
+      if s1.start == s2.start:
+        # Break ties by having the slice with the greatest
+        # end timestamp come first.
+        return cmp(s2.end, s1.end)
+      return cmp(s1.start, s2.start)
+
+    assert len(self._toplevel_slices) == 0
+    assert len(self._all_slices) == 0
+    if not len(self._newly_added_slices):
+      return
+
+    self._all_slices.extend(self._newly_added_slices)
+
+    sorted_slices = sorted(self._newly_added_slices, cmp=CompareSlices)
+    root_slice = sorted_slices[0]
+    self._toplevel_slices.append(root_slice)
+    for s in sorted_slices[1:]:
+      if not self._AddSliceIfBounds(root_slice, s):
+        root_slice = s
+        self._toplevel_slices.append(root_slice)
+    self._newly_added_slices = []
+
+
+  def _AddSliceIfBounds(self, root, child):
+    """Adds a child slice to a root slice its proper row.
+    Return False if the child slice is not in the bounds
+    of the root slice.
+
+    Because we know that the start time of child is >= the start time
+    of all other slices seen so far, we can just check the last slice
+    of each row for bounding.
+    """
+    # The source trace data is in microseconds but we store it as milliseconds
+    # in floating-point. Since we can't represent micros as millis perfectly,
+    # two end=start+duration combos that should be the same will be slightly
+    # different. Round back to micros to ensure equality below.
+    child_end_micros = round(child.end * 1000)
+    root_end_micros = round(root.end * 1000)
+    if child.start >= root.start and child_end_micros <= root_end_micros:
+      if len(root.sub_slices) > 0:
+        if self._AddSliceIfBounds(root.sub_slices[-1], child):
+          return True
+      child.parent_slice = root
+      root.AddSubSlice(child)
+      return True
+    return False
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/thread_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/thread_unittest.py
new file mode 100644
index 0000000..081a100
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/thread_unittest.py
@@ -0,0 +1,32 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import unittest
+
+from telemetry.timeline import model as model_module
+
+
+class ThreadUnittest(unittest.TestCase):
+
+  def testIterAllSlicesInRange(self):
+    model = model_module.TimelineModel()
+    renderer_main = model.GetOrCreateProcess(1).GetOrCreateThread(2)
+    #    [       X     ] [   Y    ] [   U   ]
+    #        [   Z   ]     [ T ]
+    #      |                           |
+    #    start                        end
+    renderer_main.BeginSlice('cat1', 'X', 10)
+    renderer_main.BeginSlice('cat1', 'Z', 20)
+    renderer_main.EndSlice(30)
+    renderer_main.EndSlice(40)
+    renderer_main.BeginSlice('cat1', 'Y', 50)
+    renderer_main.BeginSlice('cat1', 'T', 52)
+    renderer_main.EndSlice(55)
+    renderer_main.EndSlice(60)
+    renderer_main.BeginSlice('cat1', 'U', 60)
+    renderer_main.EndSlice(70)
+
+    model.FinalizeImport(shift_world_to_zero=False)
+    slice_names = set(s.name for s in
+                      renderer_main.IterAllSlicesInRange(start=12, end=65))
+    self.assertEqual(slice_names, {'Z', 'Y', 'T'})
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/trace_data.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/trace_data.py
new file mode 100644
index 0000000..e6dcf5e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/trace_data.py
@@ -0,0 +1,335 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import copy
+import json
+import logging
+import os
+import shutil
+import subprocess
+import tempfile
+
+from telemetry.core import util
+
+
+_TRACE2HTML_PATH = os.path.join(
+    util.GetCatapultDir(), 'tracing', 'bin', 'trace2html')
+
+
+class NonSerializableTraceData(Exception):
+  """Raised when raw trace data cannot be serialized to TraceData."""
+  pass
+
+
+class TraceDataPart(object):
+  """TraceData can have a variety of events.
+
+  These are called "parts" and are accessed by the following fixed field names.
+  """
+  def __init__(self, raw_field_name):
+    self._raw_field_name = raw_field_name
+
+  def __repr__(self):
+    return 'TraceDataPart("%s")' % self._raw_field_name
+
+  @property
+  def raw_field_name(self):
+    return self._raw_field_name
+
+  def __eq__(self, other):
+    return self.raw_field_name == other.raw_field_name
+
+  def __hash__(self):
+    return hash(self.raw_field_name)
+
+
+ATRACE_PART = TraceDataPart('systemTraceEvents')
+BATTOR_TRACE_PART = TraceDataPart('powerTraceAsString')
+CHROME_TRACE_PART = TraceDataPart('traceEvents')
+CPU_TRACE_DATA = TraceDataPart('cpuSnapshots')
+INSPECTOR_TRACE_PART = TraceDataPart('inspectorTimelineEvents')
+SURFACE_FLINGER_PART = TraceDataPart('surfaceFlinger')
+TAB_ID_PART = TraceDataPart('tabIds')
+TELEMETRY_PART = TraceDataPart('telemetry')
+
+ALL_TRACE_PARTS = {ATRACE_PART,
+                   BATTOR_TRACE_PART,
+                   CHROME_TRACE_PART,
+                   CPU_TRACE_DATA,
+                   INSPECTOR_TRACE_PART,
+                   SURFACE_FLINGER_PART,
+                   TAB_ID_PART,
+                   TELEMETRY_PART}
+
+ALL_TRACE_PARTS_RAW_NAMES = set(k.raw_field_name for k in ALL_TRACE_PARTS)
+
+def _HasTraceFor(part, raw):
+  assert isinstance(part, TraceDataPart)
+  if part.raw_field_name not in raw:
+    return False
+  return len(raw[part.raw_field_name]) > 0
+
+
+def _GetFilePathForTrace(trace, dir_path):
+  """ Return path to a file that contains |trace|.
+
+  Note: if |trace| is an instance of TraceFileHandle, this reuses the trace path
+  that the trace file handle holds. Otherwise, it creates a new trace file
+  in |dir_path| directory.
+  """
+  if isinstance(trace, TraceFileHandle):
+    return trace.file_path
+  with tempfile.NamedTemporaryFile(mode='w', dir=dir_path, delete=False) as fp:
+    if isinstance(trace, basestring):
+      fp.write(trace)
+    elif isinstance(trace, dict) or isinstance(trace, list):
+      json.dump(trace, fp)
+    else:
+      raise TypeError('Trace is of unknown type.')
+    return fp.name
+
+
+class TraceData(object):
+  """ TraceData holds a collection of traces from multiple sources.
+
+  A TraceData can have multiple active parts. Each part represents traces
+  collected from a different trace agent.
+  """
+  def __init__(self):
+    """Creates TraceData from the given data."""
+    self._raw_data = {}
+    self._events_are_safely_mutable = False
+
+  def _SetFromBuilder(self, d):
+    self._raw_data = d
+    self._events_are_safely_mutable = True
+
+  @property
+  def events_are_safely_mutable(self):
+    """Returns true if the events in this value are completely sealed.
+
+    Some importers want to take complex fields out of the TraceData and add
+    them to the model, changing them subtly as they do so. If the TraceData
+    was constructed with data that is shared with something outside the trace
+    data, for instance a test harness, then this mutation is unexpected. But,
+    if the values are sealed, then mutating the events is a lot faster.
+
+    We know if events are sealed if the value came from a string, or if the
+    value came from a TraceDataBuilder.
+    """
+    return self._events_are_safely_mutable
+
+  @property
+  def active_parts(self):
+    return {p for p in ALL_TRACE_PARTS if p.raw_field_name in self._raw_data}
+
+  def HasTracesFor(self, part):
+    return _HasTraceFor(part, self._raw_data)
+
+  def GetTracesFor(self, part):
+    """ Return the list of traces for |part| in string or dictionary forms.
+
+    Note: since this API return the traces that can be directly accessed in
+    memory, it may require lots of memory usage as some of the trace can be
+    very big.
+    For references, we have cases where Telemetry is OOM'ed because the memory
+    required for processing the trace in Python is too big (crbug.com/672097).
+    """
+    assert isinstance(part, TraceDataPart)
+    if not self.HasTracesFor(part):
+      return []
+    traces_list = self._raw_data[part.raw_field_name]
+    # Since this API return the traces in memory form, and since the memory
+    # bottleneck of Telemetry is for keeping trace in memory, there is no uses
+    # in keeping the on-disk form of tracing beyond this point. Hence we convert
+    # all traces for part of form TraceFileHandle to the JSON form.
+    for i, data in enumerate(traces_list):
+      if isinstance(data, TraceFileHandle):
+        traces_list[i] = data.AsTraceData()
+    return traces_list
+
+  def GetTraceFor(self, part):
+    assert isinstance(part, TraceDataPart)
+    traces = self.GetTracesFor(part)
+    assert len(traces) == 1
+    return traces[0]
+
+  def CleanUpAllTraces(self):
+    """ Remove all the traces that this has handles to.
+
+    Those include traces stored in memory & on disk. After invoking this,
+    one can no longer uses this object for collecting the traces.
+    """
+    for traces_list in self._raw_data.itervalues():
+      for trace in traces_list:
+        if isinstance(trace, TraceFileHandle):
+          trace.Clean()
+    self._raw_data = {}
+
+  def Serialize(self, file_path, trace_title=''):
+    """Serializes the trace result to |file_path|.
+
+    """
+    if not self._raw_data:
+      logging.warning('No traces to convert to html.')
+      return
+    temp_dir = tempfile.mkdtemp()
+    trace_files = []
+    try:
+      trace_size_data = {}
+      for part, traces_list in self._raw_data.iteritems():
+        for trace in traces_list:
+          path = _GetFilePathForTrace(trace, temp_dir)
+          trace_size_data.setdefault(part, 0)
+          trace_size_data[part] += os.path.getsize(path)
+          trace_files.append(path)
+      logging.info('Trace sizes in bytes: %s', trace_size_data)
+
+      cmd = (['python', _TRACE2HTML_PATH] + trace_files +
+          ['--output', file_path] + ['--title', trace_title])
+      subprocess.check_output(cmd)
+    finally:
+      shutil.rmtree(temp_dir)
+
+
+class TraceFileHandle(object):
+  """A trace file handle object allows storing trace data on disk.
+
+  TraceFileHandle API allows one to collect traces from Chrome into disk instead
+  of keeping them in memory. This is important for keeping memory usage of
+  Telemetry low to avoid OOM (see:
+  https://github.com/catapult-project/catapult/issues/3119).
+
+  The fact that this uses a file underneath to store tracing data means the
+  callsite is repsonsible for discarding the file when they no longer need the
+  tracing data. Call TraceFileHandle.Clean when you done using this object.
+  """
+  def __init__(self):
+    self._backing_file = None
+    self._file_path = None
+    self._trace_data = None
+
+  def Open(self):
+    assert not self._backing_file and not self._file_path
+    self._backing_file = tempfile.NamedTemporaryFile(delete=False, mode='a')
+
+  def AppendTraceData(self, partial_trace_data):
+    assert isinstance(partial_trace_data, basestring)
+    self._backing_file.write(partial_trace_data)
+
+  @property
+  def file_path(self):
+    assert self._file_path, (
+        'Either the handle need to be closed first or this handle is cleaned')
+    return self._file_path
+
+  def Close(self):
+    assert self._backing_file
+    self._backing_file.close()
+    self._file_path = self._backing_file.name
+    self._backing_file = None
+
+  def AsTraceData(self):
+    """Get the object form of trace data that this handle manages.
+
+    *Warning: this can have large memory footprint if the trace data is big.
+
+    Since this requires the in-memory form of the trace, it is no longer useful
+    to still keep the backing file underneath, invoking this will also discard
+    the file to avoid the risk of leaking the backing trace file.
+    """
+    if self._trace_data:
+      return self._trace_data
+    assert self._file_path
+    with open(self._file_path) as f:
+      self._trace_data = json.load(f)
+    self.Clean()
+    return self._trace_data
+
+  def Clean(self):
+    """Remove the backing file used for storing trace on disk.
+
+    This should be called when and only when you no longer need to use
+    TraceFileHandle.
+    """
+    assert self._file_path
+    os.remove(self._file_path)
+    self._file_path = None
+
+
+class TraceDataBuilder(object):
+  """TraceDataBuilder helps build up a trace from multiple trace agents.
+
+  TraceData is supposed to be immutable, but it is useful during recording to
+  have a mutable version. That is TraceDataBuilder.
+  """
+  def __init__(self):
+    self._raw_data = {}
+
+  def AsData(self):
+    if self._raw_data == None:
+      raise Exception('Can only AsData once')
+    data = TraceData()
+    data._SetFromBuilder(self._raw_data)
+    self._raw_data = None
+    return data
+
+  def AddTraceFor(self, part, trace):
+    assert isinstance(part, TraceDataPart)
+    if part == CHROME_TRACE_PART:
+      assert (isinstance(trace, dict) or
+              isinstance(trace, TraceFileHandle))
+    else:
+      assert (isinstance(trace, basestring) or
+              isinstance(trace, dict) or
+              isinstance(trace, list))
+
+    if self._raw_data == None:
+      raise Exception('Already called AsData() on this builder.')
+
+    self._raw_data.setdefault(part.raw_field_name, [])
+    self._raw_data[part.raw_field_name].append(trace)
+
+  def HasTracesFor(self, part):
+    return _HasTraceFor(part, self._raw_data)
+
+
+def CreateTraceDataFromRawData(raw_data):
+  """Convenient method for creating a TraceData object from |raw_data|.
+     This is mostly used for testing.
+
+     Args:
+        raw_data can be:
+            + A dictionary that repsents multiple trace parts. Keys of the
+            dictionary must always contain 'traceEvents', as chrome trace
+            must always present.
+            + A list that represents Chrome trace events.
+            + JSON string of either above.
+
+  """
+  raw_data = copy.deepcopy(raw_data)
+  if isinstance(raw_data, basestring):
+    json_data = json.loads(raw_data)
+  else:
+    json_data = raw_data
+
+  b = TraceDataBuilder()
+  if not json_data:
+    return b.AsData()
+  if isinstance(json_data, dict):
+    assert 'traceEvents' in json_data, 'Only raw chrome trace is supported'
+    trace_parts_keys = []
+    for k in json_data:
+      if k != 'traceEvents' and k in ALL_TRACE_PARTS_RAW_NAMES:
+        trace_parts_keys.append(k)
+        b.AddTraceFor(TraceDataPart(k), json_data[k])
+    # Delete the data for extra keys to form trace data for Chrome part only.
+    for k in trace_parts_keys:
+      del json_data[k]
+    b.AddTraceFor(CHROME_TRACE_PART, json_data)
+  elif isinstance(json_data, list):
+    b.AddTraceFor(CHROME_TRACE_PART, {'traceEvents': json_data})
+  else:
+    raise NonSerializableTraceData('Unrecognized data format.')
+  return b.AsData()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/trace_data_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/trace_data_unittest.py
new file mode 100644
index 0000000..9ecf668
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/trace_data_unittest.py
@@ -0,0 +1,100 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import datetime
+import exceptions
+import os
+import shutil
+import tempfile
+import unittest
+
+from tracing_build import html2trace
+from telemetry.timeline import trace_data
+
+
+class TraceDataTest(unittest.TestCase):
+  def testSerialize(self):
+    test_dir = tempfile.mkdtemp()
+    trace_path = os.path.join(test_dir, 'test_trace.json')
+    try:
+      ri = trace_data.CreateTraceDataFromRawData({'traceEvents': [1, 2, 3]})
+      ri.Serialize(trace_path)
+      with open(trace_path) as f:
+        json_traces = html2trace.ReadTracesFromHTMLFilePath(f)
+      self.assertEqual(json_traces, [{'traceEvents': [1, 2, 3]}])
+    finally:
+      shutil.rmtree(test_dir)
+
+  def testEmptyArrayValue(self):
+    # We can import empty lists and empty string.
+    d = trace_data.CreateTraceDataFromRawData([])
+    self.assertFalse(d.HasTracesFor(trace_data.CHROME_TRACE_PART))
+
+  def testInvalidTrace(self):
+    with self.assertRaises(AssertionError):
+      trace_data.CreateTraceDataFromRawData({'hello': 1})
+
+  def testListForm(self):
+    d = trace_data.CreateTraceDataFromRawData([{'ph': 'B'}])
+    self.assertTrue(d.HasTracesFor(trace_data.CHROME_TRACE_PART))
+    events = d.GetTracesFor(trace_data.CHROME_TRACE_PART)[0].get(
+        'traceEvents', [])
+    self.assertEquals(1, len(events))
+
+  def testStringForm(self):
+    d = trace_data.CreateTraceDataFromRawData('[{"ph": "B"}]')
+    self.assertTrue(d.HasTracesFor(trace_data.CHROME_TRACE_PART))
+    events = d.GetTracesFor(trace_data.CHROME_TRACE_PART)[0].get(
+        'traceEvents', [])
+    self.assertEquals(1, len(events))
+
+
+class TraceDataBuilderTest(unittest.TestCase):
+  def testBasicChrome(self):
+    builder = trace_data.TraceDataBuilder()
+    builder.AddTraceFor(trace_data.CHROME_TRACE_PART,
+                        {'traceEvents': [1, 2, 3]})
+    builder.AddTraceFor(trace_data.TAB_ID_PART, ['tab-7'])
+    builder.AddTraceFor(trace_data.BATTOR_TRACE_PART, 'battor data here')
+
+    d = builder.AsData()
+    self.assertTrue(d.HasTracesFor(trace_data.CHROME_TRACE_PART))
+    self.assertTrue(d.HasTracesFor(trace_data.TAB_ID_PART))
+    self.assertTrue(d.HasTracesFor(trace_data.BATTOR_TRACE_PART))
+
+    self.assertRaises(Exception, builder.AsData)
+
+  def testSetTraceFor(self):
+    telemetry_trace = {
+        'traceEvents': [1, 2, 3],
+        'metadata': {
+          'field1': 'value1'
+        }
+    }
+
+    builder = trace_data.TraceDataBuilder()
+    builder.AddTraceFor(trace_data.TELEMETRY_PART, telemetry_trace)
+    d = builder.AsData()
+
+    self.assertEqual(d.GetTracesFor(trace_data.TELEMETRY_PART),
+                     [telemetry_trace])
+
+  def testSetTraceForRaisesWithInvalidPart(self):
+    builder = trace_data.TraceDataBuilder()
+
+    self.assertRaises(exceptions.AssertionError,
+                      lambda: builder.AddTraceFor('not_a_trace_part', {}))
+
+  def testSetTraceForRaisesWithInvalidTrace(self):
+    builder = trace_data.TraceDataBuilder()
+
+    self.assertRaises(exceptions.AssertionError, lambda:
+        builder.AddTraceFor(trace_data.TELEMETRY_PART, datetime.time.min))
+
+  def testSetTraceForRaisesAfterAsData(self):
+    builder = trace_data.TraceDataBuilder()
+    builder.AsData()
+
+    self.assertRaises(exceptions.Exception,
+        lambda: builder.AddTraceFor(trace_data.TELEMETRY_PART, {}))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/trace_event_importer.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/trace_event_importer.py
new file mode 100644
index 0000000..b1f48ad
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/trace_event_importer.py
@@ -0,0 +1,466 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""TraceEventImporter imports TraceEvent-formatted data
+into the provided model.
+This is a port of the trace event importer from
+https://code.google.com/p/trace-viewer/
+"""
+
+import collections
+import copy
+
+import telemetry.timeline.async_slice as tracing_async_slice
+import telemetry.timeline.flow_event as tracing_flow_event
+from telemetry.timeline import importer
+from telemetry.timeline import memory_dump_event
+from tracing.trace_data import trace_data as trace_data_module
+
+
+class TraceEventTimelineImporter(importer.TimelineImporter):
+  def __init__(self, model, trace_data):
+    super(TraceEventTimelineImporter, self).__init__(
+        model, trace_data, import_order=1)
+    assert isinstance(trace_data, trace_data_module.TraceData)
+    self._trace_data = trace_data
+
+    self._all_async_events = []
+    self._all_object_events = []
+    self._all_flow_events = []
+    self._all_memory_dumps_by_dump_id = collections.defaultdict(list)
+
+    self._events = []
+    self._metadata = []
+    for trace in trace_data.GetTracesFor(trace_data_module.CHROME_TRACE_PART):
+      self._events.extend(trace['traceEvents'])
+      self.CollectMetadataRecords(trace)
+
+  def CollectMetadataRecords(self, trace):
+    part_field_names = {p.raw_field_name for p in
+                        trace_data_module.ALL_TRACE_PARTS}
+    for k, v in trace.iteritems():
+      if k in part_field_names:
+        continue
+      self._metadata.append({'name': k, 'value': v})
+
+
+  @staticmethod
+  def GetSupportedPart():
+    return trace_data_module.CHROME_TRACE_PART
+
+  def _GetOrCreateProcess(self, pid):
+    return self._model.GetOrCreateProcess(pid)
+
+  def _DeepCopyIfNeeded(self, obj):
+    if self._trace_data.events_are_safely_mutable:
+      return obj
+    return copy.deepcopy(obj)
+
+  def _ProcessAsyncEvent(self, event):
+    """Helper to process an 'async finish' event, which will close an
+    open slice.
+    """
+    thread = (self._GetOrCreateProcess(event['pid'])
+        .GetOrCreateThread(event['tid']))
+    self._all_async_events.append({
+        'event': event,
+        'thread': thread})
+
+  def _ProcessCounterEvent(self, event):
+    """Helper that creates and adds samples to a Counter object based on
+    'C' phase events.
+    """
+    if 'id' in event:
+      ctr_name = event['name'] + '[' + str(event['id']) + ']'
+    else:
+      ctr_name = event['name']
+
+    ctr = (self._GetOrCreateProcess(event['pid'])
+        .GetOrCreateCounter(event['cat'], ctr_name))
+    # Initialize the counter's series fields if needed.
+    if len(ctr.series_names) == 0:
+      #TODO: implement counter object
+      for series_name in event['args']:
+        ctr.series_names.append(series_name)
+      if len(ctr.series_names) == 0:
+        self._model.import_errors.append('Expected counter ' + event['name'] +
+            ' to have at least one argument to use as a value.')
+        # Drop the counter.
+        del ctr.parent.counters[ctr.full_name]
+        return
+
+    # Add the sample values.
+    ctr.timestamps.append(event['ts'] / 1000.0)
+    for series_name in ctr.series_names:
+      if series_name not in event['args']:
+        ctr.samples.append(0)
+        continue
+      ctr.samples.append(event['args'][series_name])
+
+  def _ProcessObjectEvent(self, event):
+    thread = (self._GetOrCreateProcess(event['pid'])
+      .GetOrCreateThread(event['tid']))
+    self._all_object_events.append({
+        'event': event,
+        'thread': thread})
+
+  def _ProcessDurationEvent(self, event):
+    thread = (self._GetOrCreateProcess(event['pid'])
+      .GetOrCreateThread(event['tid']))
+    if not thread.IsTimestampValidForBeginOrEnd(event['ts'] / 1000.0):
+      self._model.import_errors.append(
+          'Timestamps are moving backward.')
+      return
+
+    if event['ph'] == 'B':
+      thread.BeginSlice(event['cat'],
+                        event['name'],
+                        event['ts'] / 1000.0,
+                        event['tts'] / 1000.0 if 'tts' in event else None,
+                        event['args'])
+    elif event['ph'] == 'E':
+      thread = (self._GetOrCreateProcess(event['pid'])
+        .GetOrCreateThread(event['tid']))
+      if not thread.IsTimestampValidForBeginOrEnd(event['ts'] / 1000.0):
+        self._model.import_errors.append(
+            'Timestamps are moving backward.')
+        return
+      if not thread.open_slice_count:
+        self._model.import_errors.append(
+            'E phase event without a matching B phase event.')
+        return
+
+      new_slice = thread.EndSlice(
+          event['ts'] / 1000.0,
+          event['tts'] / 1000.0 if 'tts' in event else None)
+      for arg_name, arg_value in event.get('args', {}).iteritems():
+        if arg_name in new_slice.args:
+          self._model.import_errors.append(
+              'Both the B and E phases of ' + new_slice.name +
+              ' provided values for argument ' + arg_name + '. ' +
+              'The value of the E phase event will be used.')
+        new_slice.args[arg_name] = arg_value
+
+  def _ProcessCompleteEvent(self, event):
+    thread = (self._GetOrCreateProcess(event['pid'])
+        .GetOrCreateThread(event['tid']))
+    thread.PushCompleteSlice(
+        event['cat'],
+        event['name'],
+        event['ts'] / 1000.0,
+        event['dur'] / 1000.0 if 'dur' in event else None,
+        event['tts'] / 1000.0 if 'tts' in event else None,
+        event['tdur'] / 1000.0 if 'tdur' in event else None,
+        event['args'])
+
+  def _ProcessMarkEvent(self, event):
+    thread = (self._GetOrCreateProcess(event['pid'])
+        .GetOrCreateThread(event['tid']))
+    thread.PushMarkSlice(
+        event['cat'],
+        event['name'],
+        event['ts'] / 1000.0,
+        event['tts'] / 1000.0 if 'tts' in event else None,
+        event['args'] if 'args' in event else None)
+
+  def _ProcessMetadataEvent(self, event):
+    if event['name'] == 'thread_name':
+      thread = (self._GetOrCreateProcess(event['pid'])
+          .GetOrCreateThread(event['tid']))
+      thread.name = event['args']['name']
+    elif event['name'] == 'process_name':
+      process = self._GetOrCreateProcess(event['pid'])
+      process.name = event['args']['name']
+    elif event['name'] == 'process_labels':
+      process = self._GetOrCreateProcess(event['pid'])
+      process.labels = event['args']['labels']
+    elif event['name'] == 'process_uptime_seconds':
+      process = self._GetOrCreateProcess(event['pid'])
+      process.uptime_seconds = event['args']['uptime']
+    elif event['name'] == 'trace_buffer_overflowed':
+      process = self._GetOrCreateProcess(event['pid'])
+      process.SetTraceBufferOverflowTimestamp(event['args']['overflowed_at_ts'])
+    else:
+      self._model.import_errors.append(
+          'Unrecognized metadata name: ' + event['name'])
+
+  def _ProcessInstantEvent(self, event):
+    # Treat an Instant event as a duration 0 slice.
+    # SliceTrack's redraw() knows how to handle this.
+    thread = (self._GetOrCreateProcess(event['pid'])
+      .GetOrCreateThread(event['tid']))
+    thread.BeginSlice(event['cat'],
+                      event['name'],
+                      event['ts'] / 1000.0,
+                      args=event.get('args'))
+    thread.EndSlice(event['ts'] / 1000.0)
+
+  def _ProcessSampleEvent(self, event):
+    thread = (self._GetOrCreateProcess(event['pid'])
+        .GetOrCreateThread(event['tid']))
+    thread.AddSample(event['cat'],
+                     event['name'],
+                     event['ts'] / 1000.0,
+                     event.get('args'))
+
+  def _ProcessFlowEvent(self, event):
+    thread = (self._GetOrCreateProcess(event['pid'])
+        .GetOrCreateThread(event['tid']))
+    self._all_flow_events.append({
+        'event': event,
+        'thread': thread})
+
+  def _ProcessMemoryDumpEvents(self, events):
+    # Dictionary to order dumps by id and process.
+    global_dumps = {}
+    for event in events:
+      global_dump = global_dumps.setdefault(event['id'], {})
+      dump_events = global_dump.setdefault(event['pid'], [])
+      dump_events.append(event)
+    for dump_id, global_dump in global_dumps.iteritems():
+      for pid, dump_events in global_dump.iteritems():
+        process = self._GetOrCreateProcess(pid)
+        memory_dump = memory_dump_event.ProcessMemoryDumpEvent(process,
+                                                               dump_events)
+        process.AddMemoryDumpEvent(memory_dump)
+        self._all_memory_dumps_by_dump_id[dump_id].append(memory_dump)
+
+  def ImportEvents(self):
+    """Walks through the events_ list and outputs the structures discovered to
+    model_.
+    """
+    for r in self._metadata:
+      self._model.metadata.append(r)
+    memory_dump_events = []
+    for event in self._events:
+      phase = event.get('ph', None)
+      if phase == 'B' or phase == 'E':
+        self._ProcessDurationEvent(event)
+      elif phase == 'X':
+        self._ProcessCompleteEvent(event)
+      # Note, S, F, T are deprecated and replaced by 'b' and 'e'. For
+      # backwards compatibility continue to support them here.
+      elif phase == 'S' or phase == 'F' or phase == 'T':
+        self._ProcessAsyncEvent(event)
+      elif phase == 'b' or phase == 'e':
+        self._ProcessAsyncEvent(event)
+      # Note, I is historic. The instant event marker got changed, but we
+      # want to support loading old trace files so we have both I and i.
+      elif phase == 'I' or phase == 'i':
+        self._ProcessInstantEvent(event)
+      elif phase == 'P':
+        self._ProcessSampleEvent(event)
+      elif phase == 'C':
+        self._ProcessCounterEvent(event)
+      elif phase == 'M':
+        self._ProcessMetadataEvent(event)
+      elif phase == 'N' or phase == 'D' or phase == 'O':
+        self._ProcessObjectEvent(event)
+      elif phase == 's' or phase == 't' or phase == 'f':
+        self._ProcessFlowEvent(event)
+      elif phase == 'v':
+        memory_dump_events.append(event)
+      elif phase == 'R':
+        self._ProcessMarkEvent(event)
+      else:
+        self._model.import_errors.append('Unrecognized event phase: ' +
+            phase + '(' + event['name'] + ')')
+
+    # Memory dumps of a process with the same dump id need to be merged before
+    # processing. So, memory dump events are processed all at once.
+    self._ProcessMemoryDumpEvents(memory_dump_events)
+    return self._model
+
+  def FinalizeImport(self):
+    """Called by the Model after all other importers have imported their
+    events."""
+    self._model.UpdateBounds()
+
+    # We need to reupdate the bounds in case the minimum start time changes
+    self._model.UpdateBounds()
+    self._CreateAsyncSlices()
+    self._CreateFlowSlices()
+    self._SetBrowserProcess()
+    self._SetGpuProcess()
+    self._CreateExplicitObjects()
+    self._CreateImplicitObjects()
+    self._CreateMemoryDumps()
+
+  def _CreateAsyncSlices(self):
+    if len(self._all_async_events) == 0:
+      return
+
+    self._all_async_events.sort(key=lambda x: x['event']['ts'])
+
+    async_event_states_by_name_then_id = {}
+
+    all_async_events = self._all_async_events
+    for async_event_state in all_async_events:
+      event = async_event_state['event']
+      name = event.get('name', None)
+      if name is None:
+        self._model.import_errors.append(
+            'Async events (ph: b, e, S, T or F) require an name parameter.')
+        continue
+
+      event_id = event.get('id')
+      if event_id is None:
+        self._model.import_errors.append(
+            'Async events (ph: b, e, S, T or F) require an id parameter.')
+        continue
+
+      # TODO(simonjam): Add a synchronous tick on the appropriate thread.
+
+      if event['ph'] == 'S' or event['ph'] == 'b':
+        if not name in async_event_states_by_name_then_id:
+          async_event_states_by_name_then_id[name] = {}
+        if event_id in async_event_states_by_name_then_id[name]:
+          self._model.import_errors.append(
+              'At %d, a slice of the same id %s was already open.' % (
+                  event['ts'], event_id))
+          continue
+
+        async_event_states_by_name_then_id[name][event_id] = []
+        async_event_states_by_name_then_id[name][event_id].append(
+            async_event_state)
+      else:
+        if name not in async_event_states_by_name_then_id:
+          self._model.import_errors.append(
+              'At %d, no slice named %s was open.' % (event['ts'], name,))
+          continue
+        if event_id not in async_event_states_by_name_then_id[name]:
+          self._model.import_errors.append(
+              'At %d, no slice named %s with id=%s was open.' % (
+                  event['ts'], name, event_id))
+          continue
+        events = async_event_states_by_name_then_id[name][event_id]
+        events.append(async_event_state)
+
+        if event['ph'] == 'F' or event['ph'] == 'e':
+          # Create a slice from start to end.
+          async_slice = tracing_async_slice.AsyncSlice(
+              events[0]['event']['cat'],
+              name,
+              events[0]['event']['ts'] / 1000.0)
+
+          async_slice.duration = ((event['ts'] / 1000.0)
+              - (events[0]['event']['ts'] / 1000.0))
+
+          async_slice.start_thread = events[0]['thread']
+          async_slice.end_thread = async_event_state['thread']
+          if async_slice.start_thread == async_slice.end_thread:
+            if 'tts' in event and 'tts' in events[0]['event']:
+              async_slice.thread_start = events[0]['event']['tts'] / 1000.0
+              async_slice.thread_duration = ((event['tts'] / 1000.0)
+                  - (events[0]['event']['tts'] / 1000.0))
+          async_slice.id = event_id
+          async_slice.args = events[0]['event']['args']
+
+          # Create sub_slices for each step.
+          for j in xrange(1, len(events)):
+            sub_name = name
+            if events[j - 1]['event']['ph'] == 'T':
+              sub_name = name + ':' + events[j - 1]['event']['args']['step']
+            sub_slice = tracing_async_slice.AsyncSlice(
+                events[0]['event']['cat'],
+                sub_name,
+                events[j - 1]['event']['ts'] / 1000.0)
+            sub_slice.parent_slice = async_slice
+
+            sub_slice.duration = ((events[j]['event']['ts'] / 1000.0)
+                - (events[j - 1]['event']['ts'] / 1000.0))
+
+            sub_slice.start_thread = events[j - 1]['thread']
+            sub_slice.end_thread = events[j]['thread']
+            if sub_slice.start_thread == sub_slice.end_thread:
+              if 'tts' in events[j]['event'] and \
+                  'tts' in events[j - 1]['event']:
+                sub_slice.thread_duration = \
+                    ((events[j]['event']['tts'] / 1000.0)
+                        - (events[j - 1]['event']['tts'] / 1000.0))
+
+            sub_slice.id = event_id
+            sub_slice.args = events[j - 1]['event']['args']
+
+            async_slice.AddSubSlice(sub_slice)
+
+          # The args for the finish event go in the last sub_slice.
+          last_slice = async_slice.sub_slices[-1]
+          for arg_name, arg_value in event['args'].iteritems():
+            last_slice.args[arg_name] = arg_value
+
+          # Add |async_slice| to the start-thread's async_slices.
+          async_slice.start_thread.AddAsyncSlice(async_slice)
+          del async_event_states_by_name_then_id[name][event_id]
+
+  def _CreateExplicitObjects(self):
+    # TODO(tengs): Implement object instance parsing
+    pass
+
+  def _CreateImplicitObjects(self):
+    # TODO(tengs): Implement object instance parsing
+    pass
+
+  def _CreateFlowSlices(self):
+    if len(self._all_flow_events) == 0:
+      return
+
+    self._all_flow_events.sort(key=lambda x: x['event']['ts'])
+
+    flow_id_to_event = {}
+    for data in self._all_flow_events:
+      event = data['event']
+      thread = data['thread']
+      if 'name' not in event:
+        self._model.import_errors.append(
+          'Flow events (ph: s, t or f) require a name parameter.')
+        continue
+      if 'id' not in event:
+        self._model.import_errors.append(
+          'Flow events (ph: s, t or f) require an id parameter.')
+        continue
+
+      flow_event = tracing_flow_event.FlowEvent(
+          event['cat'],
+          event['id'],
+          event['name'],
+          event['ts'] / 1000.0,
+          event['args'])
+      thread.AddFlowEvent(flow_event)
+
+      if event['ph'] == 's':
+        if event['id'] in flow_id_to_event:
+          self._model.import_errors.append(
+              'event id %s already seen when encountering start of'
+              'flow event.' % event['id'])
+          continue
+        flow_id_to_event[event['id']] = flow_event
+      elif event['ph'] == 't' or event['ph'] == 'f':
+        if not event['id'] in flow_id_to_event:
+          self._model.import_errors.append(
+            'Found flow phase %s for id: %s but no flow start found.' % (
+                event['ph'], event['id']))
+          continue
+        flow_position = flow_id_to_event[event['id']]
+        self._model.flow_events.append([flow_position, flow_event])
+
+        if event['ph'] == 'f':
+          del flow_id_to_event[event['id']]
+        else:
+          # Make this event the next start event in this flow.
+          flow_id_to_event[event['id']] = flow_event
+
+  def _CreateMemoryDumps(self):
+    self._model.SetGlobalMemoryDumps(
+        memory_dump_event.GlobalMemoryDump(events)
+        for events in self._all_memory_dumps_by_dump_id.itervalues())
+
+  def _SetBrowserProcess(self):
+    for thread in self._model.GetAllThreads():
+      if thread.name == 'CrBrowserMain':
+        self._model.browser_process = thread.parent
+
+  def _SetGpuProcess(self):
+    for thread in self._model.GetAllThreads():
+      if thread.name == 'CrGpuMain':
+        self._model.gpu_process = thread.parent
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/trace_event_importer_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/trace_event_importer_unittest.py
new file mode 100644
index 0000000..e49d336
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/trace_event_importer_unittest.py
@@ -0,0 +1,1134 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# pylint: disable=too-many-lines
+
+import unittest
+
+import telemetry.timeline.counter as tracing_counter
+import telemetry.timeline.model as timeline_model
+from tracing.trace_data import trace_data as trace_data_module
+
+
+def FindEventNamed(events, name):
+  for event in events:
+    if event.name == name:
+      return event
+  raise ValueError('No event found with name %s' % name)
+
+class TraceEventTimelineImporterTest(unittest.TestCase):
+
+  def testBasicSingleThreadNonnestedParsing(self):
+    events = [
+      {'name': 'a', 'args': {}, 'pid': 52, 'ts': 520, 'tts': 280, 'cat': 'foo',
+       'tid': 53, 'ph': 'B'},
+      {'name': 'a', 'args': {}, 'pid': 52, 'ts': 560, 'tts': 310, 'cat': 'foo',
+       'tid': 53, 'ph': 'E'},
+      {'name': 'b', 'args': {}, 'pid': 52, 'ts': 629, 'tts': 356, 'cat': 'bar',
+       'tid': 53, 'ph': 'B'},
+      {'name': 'b', 'args': {}, 'pid': 52, 'ts': 631, 'tts': 357, 'cat': 'bar',
+       'tid': 53, 'ph': 'E'},
+      {'name': 'c', 'args': {}, 'pid': 52, 'ts': 633, 'cat': 'baz',
+       'tid': 53, 'ph': 'B'},
+      {'name': 'c', 'args': {}, 'pid': 52, 'ts': 637, 'cat': 'baz',
+       'tid': 53, 'ph': 'E'}
+    ]
+
+    trace_data = trace_data_module.CreateTraceDataFromRawData(events)
+    m = timeline_model.TimelineModel(trace_data)
+    processes = m.GetAllProcesses()
+    self.assertEqual(1, len(processes))
+    p = processes[0]
+    self.assertEqual(52, p.pid)
+
+    self.assertEqual(1, len(p.threads))
+    t = p.threads[53]
+    self.assertEqual(3, len(t.all_slices))
+    self.assertEqual(53, t.tid)
+    slice_event = t.all_slices[0]
+    self.assertEqual('a', slice_event.name)
+    self.assertEqual('foo', slice_event.category)
+    self.assertAlmostEqual(0, slice_event.start)
+    self.assertAlmostEqual((560 - 520) / 1000.0, slice_event.duration)
+    self.assertAlmostEqual((560 - 520) / 1000.0, slice_event.end)
+    self.assertAlmostEqual(280 / 1000.0, slice_event.thread_start)
+    self.assertAlmostEqual((310 - 280) / 1000.0, slice_event.thread_duration)
+    self.assertAlmostEqual(310 / 1000.0, slice_event.thread_end)
+    self.assertEqual(0, len(slice_event.sub_slices))
+
+    slice_event = t.all_slices[1]
+    self.assertEqual('b', slice_event.name)
+    self.assertEqual('bar', slice_event.category)
+    self.assertAlmostEqual((629 - 520) / 1000.0, slice_event.start)
+    self.assertAlmostEqual((631 - 629) / 1000.0, slice_event.duration)
+    self.assertAlmostEqual((631 - 520) / 1000.0, slice_event.end)
+    self.assertAlmostEqual(356 / 1000.0, slice_event.thread_start)
+    self.assertAlmostEqual((357 - 356) / 1000.0, slice_event.thread_duration)
+    self.assertAlmostEqual(357 / 1000.0, slice_event.thread_end)
+    self.assertEqual(0, len(slice_event.sub_slices))
+
+    slice_event = t.all_slices[2]
+    self.assertEqual('c', slice_event.name)
+    self.assertEqual('baz', slice_event.category)
+    self.assertAlmostEqual((633 - 520) / 1000.0, slice_event.start)
+    self.assertAlmostEqual((637 - 633) / 1000.0, slice_event.duration)
+    self.assertEqual(None, slice_event.thread_start)
+    self.assertEqual(None, slice_event.thread_duration)
+    self.assertEqual(None, slice_event.thread_end)
+    self.assertEqual(0, len(slice_event.sub_slices))
+
+
+  def testArgumentDupeCreatesNonFailingImportError(self):
+    events = [
+      {'name': 'a', 'args': {'x': 1}, 'pid': 1, 'ts': 520, 'cat': 'foo',
+       'tid': 1, 'ph': 'B'},
+      {'name': 'a', 'args': {'x': 2}, 'pid': 1, 'ts': 560, 'cat': 'foo',
+       'tid': 1, 'ph': 'E'}
+    ]
+
+    trace_data = trace_data_module.CreateTraceDataFromRawData(events)
+    m = timeline_model.TimelineModel(trace_data)
+    processes = m.GetAllProcesses()
+    t = processes[0].threads[1]
+    slice_a = FindEventNamed(t.all_slices, 'a')
+
+    self.assertEqual(2, slice_a.args['x'])
+    self.assertEqual(1, len(m.import_errors))
+
+  def testCategoryBeginEndMismatchPreferslice_begin(self):
+    events = [
+      {'name': 'a', 'args': {}, 'pid': 52, 'ts': 520, 'cat': 'foo',
+       'tid': 53, 'ph': 'B'},
+      {'name': 'a', 'args': {}, 'pid': 52, 'ts': 560, 'cat': 'bar',
+       'tid': 53, 'ph': 'E'}
+    ]
+
+    trace_data = trace_data_module.CreateTraceDataFromRawData(events)
+    m = timeline_model.TimelineModel(trace_data)
+    processes = m.GetAllProcesses()
+    self.assertEqual(1, len(processes))
+    p = processes[0]
+    self.assertEqual(52, p.pid)
+
+    self.assertEqual(1, len(p.threads))
+    t = p.threads[53]
+    self.assertEqual(1, len(t.all_slices))
+    self.assertEqual(53, t.tid)
+    slice_event = t.all_slices[0]
+    self.assertEqual('a', slice_event.name)
+    self.assertEqual('foo', slice_event.category)
+
+  def testNestedParsing(self):
+    events = [
+      {'name': 'a', 'args': {}, 'pid': 1, 'ts': 1, 'tts': 2, 'cat': 'foo',
+       'tid': 1, 'ph': 'B'},
+      {'name': 'b', 'args': {}, 'pid': 1, 'ts': 3, 'tts': 3, 'cat': 'bar',
+       'tid': 1, 'ph': 'B'},
+      {'name': 'b', 'args': {}, 'pid': 1, 'ts': 5, 'tts': 4, 'cat': 'bar',
+       'tid': 1, 'ph': 'E'},
+      {'name': 'a', 'args': {}, 'pid': 1, 'ts': 7, 'tts': 5, 'cat': 'foo',
+       'tid': 1, 'ph': 'E'}
+    ]
+    trace_data = trace_data_module.CreateTraceDataFromRawData(events)
+    m = timeline_model.TimelineModel(trace_data, shift_world_to_zero=False)
+    t = m.GetAllProcesses()[0].threads[1]
+
+    slice_a = FindEventNamed(t.all_slices, 'a')
+    slice_b = FindEventNamed(t.all_slices, 'b')
+
+    self.assertEqual('a', slice_a.name)
+    self.assertEqual('foo', slice_a.category)
+    self.assertAlmostEqual(0.001, slice_a.start)
+    self.assertAlmostEqual(0.006, slice_a.duration)
+    self.assertAlmostEqual(0.002, slice_a.thread_start)
+    self.assertAlmostEqual(0.003, slice_a.thread_duration)
+
+    self.assertEqual('b', slice_b.name)
+    self.assertEqual('bar', slice_b.category)
+    self.assertAlmostEqual(0.003, slice_b.start)
+    self.assertAlmostEqual(0.002, slice_b.duration)
+    self.assertAlmostEqual(0.003, slice_b.thread_start)
+    self.assertAlmostEqual(0.001, slice_b.thread_duration)
+
+  def testAutoclosing(self):
+    events = [
+      # Slices that don't finish.
+      {'name': 'a', 'args': {}, 'pid': 1, 'ts': 1, 'tts': 1, 'cat': 'foo',
+       'tid': 1, 'ph': 'B'},
+      {'name': 'b', 'args': {}, 'pid': 1, 'ts': 2, 'tts': 2, 'cat': 'foo',
+       'tid': 2, 'ph': 'B'},
+
+      # Slices on thread 1 and 2 that do finish to give an 'end time' to make
+      # autoclosing work.
+      {'name': 'c', 'args': {}, 'pid': 1, 'ts': 2, 'tts': 1.5, 'cat': 'bar',
+       'tid': 1, 'ph': 'B'},
+      {'name': 'c', 'args': {}, 'pid': 1, 'ts': 4, 'tts': 3, 'cat': 'bar',
+       'tid': 1, 'ph': 'E'},
+      {'name': 'd', 'args': {}, 'pid': 1, 'ts': 3, 'tts': 2.5, 'cat': 'bar',
+       'tid': 2, 'ph': 'B'},
+      {'name': 'd', 'args': {}, 'pid': 1, 'ts': 7, 'tts': 5, 'cat': 'bar',
+       'tid': 2, 'ph': 'E'}
+    ]
+    trace_data = trace_data_module.CreateTraceDataFromRawData(events)
+    m = timeline_model.TimelineModel(trace_data)
+    p = m.GetAllProcesses()[0]
+    t1 = p.threads[1]
+    slice_event = FindEventNamed(t1.all_slices, 'a')
+    self.assertEqual('a', slice_event.name)
+    self.assertEqual('foo', slice_event.category)
+    self.assertTrue(slice_event.did_not_finish)
+    self.assertAlmostEqual(0, slice_event.start)
+    self.assertAlmostEqual((7 - 1) / 1000.0, slice_event.duration)
+    self.assertAlmostEqual(1 / 1000.0, slice_event.thread_start)
+    self.assertAlmostEqual((3 - 1) / 1000.0, slice_event.thread_duration)
+
+    t2 = p.threads[2]
+    slice_event = FindEventNamed(t2.all_slices, 'b')
+    self.assertEqual('b', slice_event.name)
+    self.assertEqual('foo', slice_event.category)
+    self.assertTrue(slice_event.did_not_finish)
+    self.assertAlmostEqual((2 - 1) / 1000.0, slice_event.start)
+    self.assertAlmostEqual((7 - 2) / 1000.0, slice_event.duration)
+    self.assertAlmostEqual(2 / 1000.0, slice_event.thread_start)
+    self.assertAlmostEqual((5 - 2) / 1000.0, slice_event.thread_duration)
+
+  def testAutoclosingLoneBegin(self):
+    events = [
+      # Slice that doesn't finish.
+      {'name': 'a', 'args': {}, 'pid': 1, 'ts': 1, 'tts': 1, 'cat': 'foo',
+       'tid': 1, 'ph': 'B'}
+    ]
+    trace_data = trace_data_module.CreateTraceDataFromRawData(events)
+    m = timeline_model.TimelineModel(trace_data)
+    p = m.GetAllProcesses()[0]
+    t = p.threads[1]
+    slice_event = t.all_slices[0]
+    self.assertEqual('a', slice_event.name)
+    self.assertEqual('foo', slice_event.category)
+    self.assertTrue(slice_event.did_not_finish)
+    self.assertAlmostEqual(0, slice_event.start)
+    self.assertAlmostEqual(0, slice_event.duration)
+    self.assertAlmostEqual(1 / 1000.0, slice_event.thread_start)
+    self.assertAlmostEqual(0, slice_event.thread_duration)
+
+  def testAutoclosingWithSubTasks(self):
+    events = [
+      {'name': 'a', 'args': {}, 'pid': 1, 'ts': 1, 'cat': 'foo',
+       'tid': 1, 'ph': 'B'},
+      {'name': 'b1', 'args': {}, 'pid': 1, 'ts': 2, 'cat': 'foo',
+       'tid': 1, 'ph': 'B'},
+      {'name': 'b1', 'args': {}, 'pid': 1, 'ts': 3, 'cat': 'foo',
+       'tid': 1, 'ph': 'E'},
+      {'name': 'b2', 'args': {}, 'pid': 1, 'ts': 3, 'cat': 'foo',
+       'tid': 1, 'ph': 'B'}
+    ]
+    trace_data = trace_data_module.CreateTraceDataFromRawData(events)
+    m = timeline_model.TimelineModel(trace_data, shift_world_to_zero=False)
+    t = m.GetAllProcesses()[0].threads[1]
+
+    slice_a = FindEventNamed(t.all_slices, 'a')
+    slice_b1 = FindEventNamed(t.all_slices, 'b1')
+    slice_b2 = FindEventNamed(t.all_slices, 'b2')
+
+    self.assertAlmostEqual(0.003, slice_a.end)
+    self.assertAlmostEqual(0.003, slice_b1.end)
+    self.assertAlmostEqual(0.003, slice_b2.end)
+
+  def testAutoclosingWithEventsOutsideBounds(self):
+    events = [
+      # Slice that begins before min and ends after max of the other threads.
+      {'name': 'a', 'args': {}, 'pid': 1, 'ts': 0, 'tts': 0, 'cat': 'foo',
+       'tid': 1, 'ph': 'B'},
+      {'name': 'b', 'args': {}, 'pid': 1, 'ts': 6, 'tts': 3, 'cat': 'foo',
+       'tid': 1, 'ph': 'B'},
+
+      # Slice that does finish to give an 'end time' to establish a basis
+      {'name': 'c', 'args': {}, 'pid': 1, 'ts': 2, 'tts': 1, 'cat': 'bar',
+       'tid': 2, 'ph': 'B'},
+      {'name': 'c', 'args': {}, 'pid': 1, 'ts': 4, 'tts': 2, 'cat': 'bar',
+       'tid': 2, 'ph': 'E'}
+    ]
+    trace_data = trace_data_module.CreateTraceDataFromRawData(events)
+    m = timeline_model.TimelineModel(trace_data, shift_world_to_zero=False)
+    p = m.GetAllProcesses()[0]
+    t1 = p.threads[1]
+    t1_thread_time_bounds = (
+        m._thread_time_bounds[t1]) # pylint: disable=protected-access
+    self.assertAlmostEqual(0.000, t1_thread_time_bounds.min)
+    self.assertAlmostEqual(0.003, t1_thread_time_bounds.max)
+    self.assertEqual(2, len(t1.all_slices))
+
+    slice_event = FindEventNamed(t1.all_slices, 'a')
+    self.assertEqual('a', slice_event.name)
+    self.assertEqual('foo', slice_event.category)
+    self.assertAlmostEqual(0, slice_event.start)
+    self.assertAlmostEqual(0.006, slice_event.duration)
+    self.assertAlmostEqual(0, slice_event.thread_start)
+    self.assertAlmostEqual(0.003, slice_event.thread_duration)
+
+    t2 = p.threads[2]
+    t2_thread_time_bounds = (
+        m._thread_time_bounds[t2]) # pylint: disable=protected-access
+    self.assertAlmostEqual(0.001, t2_thread_time_bounds.min)
+    self.assertAlmostEqual(0.002, t2_thread_time_bounds.max)
+    slice2 = FindEventNamed(t2.all_slices, 'c')
+    self.assertEqual('c', slice2.name)
+    self.assertEqual('bar', slice2.category)
+    self.assertAlmostEqual(0.002, slice2.start)
+    self.assertAlmostEqual(0.002, slice2.duration)
+    self.assertAlmostEqual(0.001, slice2.thread_start)
+    self.assertAlmostEqual(0.001, slice2.thread_duration)
+
+    self.assertAlmostEqual(0.000, m.bounds.min)
+    self.assertAlmostEqual(0.006, m.bounds.max)
+
+  def testNestedAutoclosing(self):
+    events = [
+      # Tasks that don't finish.
+      {'name': 'a1', 'args': {}, 'pid': 1, 'ts': 1, 'cat': 'foo',
+       'tid': 1, 'ph': 'B'},
+      {'name': 'a2', 'args': {}, 'pid': 1, 'ts': 1.5, 'cat': 'foo',
+       'tid': 1, 'ph': 'B'},
+
+      # Slice that does finish to give an 'end time' to make autoclosing work.
+      {'name': 'b', 'args': {}, 'pid': 1, 'ts': 1, 'cat': 'foo',
+       'tid': 2, 'ph': 'B'},
+      {'name': 'b', 'args': {}, 'pid': 1, 'ts': 2, 'cat': 'foo',
+       'tid': 2, 'ph': 'E'}
+    ]
+    trace_data = trace_data_module.CreateTraceDataFromRawData(events)
+    m = timeline_model.TimelineModel(trace_data, shift_world_to_zero=False)
+    t1 = m.GetAllProcesses()[0].threads[1]
+    t2 = m.GetAllProcesses()[0].threads[2]
+
+    slice_a1 = FindEventNamed(t1.all_slices, 'a1')
+    slice_a2 = FindEventNamed(t1.all_slices, 'a2')
+    FindEventNamed(t2.all_slices, 'b')
+
+    self.assertAlmostEqual(0.002, slice_a1.end)
+    self.assertAlmostEqual(0.002, slice_a2.end)
+
+  def testMultipleThreadParsing(self):
+    events = [
+      {'name': 'a', 'args': {}, 'pid': 1, 'ts': 2, 'tts': 1, 'cat': 'foo',
+       'tid': 1, 'ph': 'B'},
+      {'name': 'a', 'args': {}, 'pid': 1, 'ts': 4, 'tts': 2, 'cat': 'foo',
+       'tid': 1, 'ph': 'E'},
+      {'name': 'b', 'args': {}, 'pid': 1, 'ts': 6, 'tts': 3, 'cat': 'bar',
+       'tid': 2, 'ph': 'B'},
+      {'name': 'b', 'args': {}, 'pid': 1, 'ts': 8, 'tts': 4, 'cat': 'bar',
+       'tid': 2, 'ph': 'E'}
+    ]
+    trace_data = trace_data_module.CreateTraceDataFromRawData(events)
+    m = timeline_model.TimelineModel(trace_data)
+    processes = m.GetAllProcesses()
+    self.assertEqual(1, len(processes))
+    p = processes[0]
+
+    self.assertEqual(2, len(p.threads))
+
+    # Check thread 1.
+    t = p.threads[1]
+    self.assertAlmostEqual(1, len(t.all_slices))
+    self.assertAlmostEqual(1, t.tid)
+
+    slice_event = t.all_slices[0]
+    self.assertEqual('a', slice_event.name)
+    self.assertEqual('foo', slice_event.category)
+    self.assertAlmostEqual(0, slice_event.start)
+    self.assertAlmostEqual((4 - 2) / 1000.0, slice_event.duration)
+    self.assertAlmostEqual(1 / 1000.0, slice_event.thread_start)
+    self.assertAlmostEqual((2 - 1) / 1000.0, slice_event.thread_duration)
+
+    # Check thread 2.
+    t = p.threads[2]
+    self.assertAlmostEqual(1, len(t.all_slices))
+    self.assertAlmostEqual(2, t.tid)
+
+    slice_event = t.all_slices[0]
+    self.assertEqual('b', slice_event.name)
+    self.assertEqual('bar', slice_event.category)
+    self.assertAlmostEqual((6 - 2) / 1000.0, slice_event.start)
+    self.assertAlmostEqual((8 - 6) / 1000.0, slice_event.duration)
+    self.assertAlmostEqual(3 / 1000.0, slice_event.thread_start)
+    self.assertAlmostEqual((4 - 3) / 1000.0, slice_event.thread_duration)
+
+  def testMultiplePidParsing(self):
+    events = [
+      {'name': 'a', 'args': {}, 'pid': 1, 'ts': 2, 'tts': 1, 'cat': 'foo',
+       'tid': 1, 'ph': 'B'},
+      {'name': 'a', 'args': {}, 'pid': 1, 'ts': 4, 'tts': 2, 'cat': 'foo',
+       'tid': 1, 'ph': 'E'},
+      {'name': 'b', 'args': {}, 'pid': 2, 'ts': 6, 'tts': 3, 'cat': 'bar',
+       'tid': 2, 'ph': 'B'},
+      {'name': 'b', 'args': {}, 'pid': 2, 'ts': 8, 'tts': 4, 'cat': 'bar',
+       'tid': 2, 'ph': 'E'}
+    ]
+
+    trace_data = trace_data_module.CreateTraceDataFromRawData(events)
+    m = timeline_model.TimelineModel(trace_data)
+    processes = m.GetAllProcesses()
+    self.assertEqual(2, len(processes))
+
+    p = processes[0]
+    self.assertEqual(1, p.pid)
+    self.assertEqual(1, len(p.threads))
+
+    # Check process 1 thread 1.
+    t = p.threads[1]
+    self.assertEqual(1, len(t.all_slices))
+    self.assertEqual(1, t.tid)
+
+    slice_event = t.all_slices[0]
+    self.assertEqual('a', slice_event.name)
+    self.assertEqual('foo', slice_event.category)
+    self.assertAlmostEqual(0, slice_event.start)
+    self.assertAlmostEqual((4 - 2) / 1000.0, slice_event.duration)
+    self.assertAlmostEqual(1 / 1000.0, slice_event.thread_start)
+    self.assertAlmostEqual((2 - 1) / 1000.0, slice_event.thread_duration)
+
+    # Check process 2 thread 2.
+    # TODO: will this be in deterministic order?
+    p = processes[1]
+    self.assertEqual(2, p.pid)
+    self.assertEqual(1, len(p.threads))
+    t = p.threads[2]
+    self.assertEqual(1, len(t.all_slices))
+    self.assertEqual(2, t.tid)
+
+    slice_event = t.all_slices[0]
+    self.assertEqual('b', slice_event.name)
+    self.assertEqual('bar', slice_event.category)
+    self.assertAlmostEqual((6 - 2) / 1000.0, slice_event.start)
+    self.assertAlmostEqual((8 - 6) / 1000.0, slice_event.duration)
+    self.assertAlmostEqual(3 / 1000.0, slice_event.thread_start)
+    self.assertAlmostEqual((4 - 3) / 1000.0, slice_event.thread_duration)
+
+    # Check getAllThreads.
+    self.assertEqual([processes[0].threads[1],
+                      processes[1].threads[2]],
+                      m.GetAllThreads())
+
+  def testThreadNames(self):
+    events = [
+      {'name': 'thread_name', 'args': {'name': 'Thread 1'},
+        'pid': 1, 'ts': 0, 'tid': 1, 'ph': 'M'},
+      {'name': 'a', 'args': {}, 'pid': 1, 'ts': 1, 'cat': 'foo',
+       'tid': 1, 'ph': 'B'},
+      {'name': 'a', 'args': {}, 'pid': 1, 'ts': 2, 'cat': 'foo',
+       'tid': 1, 'ph': 'E'},
+      {'name': 'b', 'args': {}, 'pid': 2, 'ts': 3, 'cat': 'foo',
+       'tid': 2, 'ph': 'B'},
+      {'name': 'b', 'args': {}, 'pid': 2, 'ts': 4, 'cat': 'foo',
+       'tid': 2, 'ph': 'E'},
+      {'name': 'thread_name', 'args': {'name': 'Thread 2'},
+        'pid': 2, 'ts': 0, 'tid': 2, 'ph': 'M'}
+    ]
+    trace_data = trace_data_module.CreateTraceDataFromRawData(events)
+    m = timeline_model.TimelineModel(trace_data)
+    processes = m.GetAllProcesses()
+    self.assertEqual('Thread 1', processes[0].threads[1].name)
+    self.assertEqual('Thread 2', processes[1].threads[2].name)
+
+  def testParsingWhenEndComesFirst(self):
+    events = [
+      {'name': 'a', 'args': {}, 'pid': 1, 'ts': 1, 'tts': 1, 'cat': 'foo',
+       'tid': 1, 'ph': 'E'},
+      {'name': 'a', 'args': {}, 'pid': 1, 'ts': 4, 'tts': 4, 'cat': 'foo',
+       'tid': 1, 'ph': 'B'},
+      {'name': 'a', 'args': {}, 'pid': 1, 'ts': 5, 'tts': 5, 'cat': 'foo',
+       'tid': 1, 'ph': 'E'}
+    ]
+    trace_data = trace_data_module.CreateTraceDataFromRawData(events)
+    m = timeline_model.TimelineModel(trace_data, shift_world_to_zero=False)
+    p = m.GetAllProcesses()[0]
+    t = p.threads[1]
+    self.assertEqual(1, len(t.all_slices))
+    self.assertEqual('a', t.all_slices[0].name)
+    self.assertEqual('foo', t.all_slices[0].category)
+    self.assertEqual(0.004, t.all_slices[0].start)
+    self.assertEqual(0.001, t.all_slices[0].duration)
+    self.assertEqual(0.004, t.all_slices[0].thread_start)
+    self.assertEqual(0.001, t.all_slices[0].thread_duration)
+    self.assertEqual(1, len(m.import_errors))
+
+  def testImmediateParsing(self):
+    events = [
+      # Need to include immediates inside a task so the timeline
+      # recentering/zeroing doesn't clobber their timestamp.
+      {'name': 'a', 'args': {}, 'pid': 1, 'ts': 2, 'tts': 1, 'cat': 'foo',
+       'tid': 1, 'ph': 'B'},
+      {'name': 'immediate', 'args': {}, 'pid': 1, 'ts': 4, 'cat': 'bar',
+       'tid': 1, 'ph': 'I'},
+      {'name': 'slower', 'args': {}, 'pid': 1, 'ts': 8, 'cat': 'baz',
+       'tid': 1, 'ph': 'i'},
+      {'name': 'a', 'args': {}, 'pid': 1, 'ts': 8, 'tts': 4, 'cat': 'foo',
+       'tid': 1, 'ph': 'E'}
+    ]
+    trace_data = trace_data_module.CreateTraceDataFromRawData(events)
+    m = timeline_model.TimelineModel(trace_data, shift_world_to_zero=False)
+    p = m.GetAllProcesses()[0]
+    t = p.threads[1]
+    self.assertEqual(3, len(t.all_slices))
+
+    i = m.GetAllEventsOfName('immediate')[0]
+    self.assertEqual('immediate', i.name)
+    self.assertEqual('bar', i.category)
+    self.assertAlmostEqual(0.004, i.start)
+    self.assertAlmostEqual(0, i.duration)
+
+    slower = m.GetAllEventsOfName('slower')[0]
+    self.assertEqual('slower', slower.name)
+    self.assertEqual('baz', slower.category)
+    self.assertAlmostEqual(0.008, slower.start)
+    self.assertAlmostEqual(0, slower.duration)
+
+    a = m.GetAllEventsOfName('a')[0]
+    self.assertEqual('a', a.name)
+    self.assertEqual('foo', a.category)
+    self.assertAlmostEqual(0.002, a.start)
+    self.assertAlmostEqual(0.006, a.duration)
+    self.assertAlmostEqual(0.001, a.thread_start)
+    self.assertAlmostEqual(0.003, a.thread_duration)
+
+
+  def testSimpleCounter(self):
+    events = [
+      {'name': 'ctr', 'args': {'value': 0}, 'pid': 1, 'ts': 0, 'cat': 'foo',
+       'tid': 1, 'ph': 'C'},
+      {'name': 'ctr', 'args': {'value': 10}, 'pid': 1, 'ts': 10, 'cat': 'foo',
+       'tid': 1, 'ph': 'C'},
+      {'name': 'ctr', 'args': {'value': 0}, 'pid': 1, 'ts': 20, 'cat': 'foo',
+       'tid': 1, 'ph': 'C'}
+    ]
+    trace_data = trace_data_module.CreateTraceDataFromRawData(events)
+    m = timeline_model.TimelineModel(trace_data)
+    p = m.GetAllProcesses()[0]
+    ctr = p.counters['foo.ctr']
+
+    self.assertEqual('ctr', ctr.name)
+    self.assertEqual('foo', ctr.category)
+    self.assertEqual(3, ctr.num_samples)
+    self.assertEqual(1, ctr.num_series)
+
+    self.assertEqual(['value'], ctr.series_names)
+    self.assertEqual([0, 0.01, 0.02], ctr.timestamps)
+    self.assertEqual([0, 10, 0], ctr.samples)
+    self.assertEqual([0, 10, 0], ctr.totals)
+    self.assertEqual(10, ctr.max_total)
+
+  def testInstanceCounter(self):
+    events = [
+      {'name': 'ctr', 'args': {'value': 0}, 'pid': 1, 'ts': 0, 'cat': 'foo',
+       'tid': 1,
+       'ph': 'C', 'id': 0},
+      {'name': 'ctr', 'args': {'value': 10}, 'pid': 1, 'ts': 10, 'cat': 'foo',
+       'tid': 1,
+       'ph': 'C', 'id': 0},
+      {'name': 'ctr', 'args': {'value': 10}, 'pid': 1, 'ts': 10, 'cat': 'foo',
+       'tid': 1,
+       'ph': 'C', 'id': 1},
+      {'name': 'ctr', 'args': {'value': 20}, 'pid': 1, 'ts': 15, 'cat': 'foo',
+       'tid': 1,
+       'ph': 'C', 'id': 1},
+      {'name': 'ctr', 'args': {'value': 30}, 'pid': 1, 'ts': 18, 'cat': 'foo',
+       'tid': 1,
+       'ph': 'C', 'id': 1},
+      {'name': 'ctr', 'args': {'value': 40}, 'pid': 1, 'ts': 20, 'cat': 'bar',
+       'tid': 1,
+       'ph': 'C', 'id': 2}
+    ]
+    trace_data = trace_data_module.CreateTraceDataFromRawData(events)
+    m = timeline_model.TimelineModel(trace_data)
+    p = m.GetAllProcesses()[0]
+    ctr = p.counters['foo.ctr[0]']
+    self.assertEqual('ctr[0]', ctr.name)
+    self.assertEqual('foo', ctr.category)
+    self.assertEqual(2, ctr.num_samples)
+    self.assertEqual(1, ctr.num_series)
+    self.assertEqual([0, 0.01], ctr.timestamps)
+    self.assertEqual([0, 10], ctr.samples)
+
+    ctr = m.GetAllProcesses()[0].counters['foo.ctr[1]']
+    self.assertEqual('ctr[1]', ctr.name)
+    self.assertEqual('foo', ctr.category)
+    self.assertEqual(3, ctr.num_samples)
+    self.assertEqual(1, ctr.num_series)
+    self.assertEqual([0.01, 0.015, 0.018], ctr.timestamps)
+    self.assertEqual([10, 20, 30], ctr.samples)
+
+    ctr = m.GetAllProcesses()[0].counters['bar.ctr[2]']
+    self.assertEqual('ctr[2]', ctr.name)
+    self.assertEqual('bar', ctr.category)
+    self.assertEqual(1, ctr.num_samples)
+    self.assertEqual(1, ctr.num_series)
+    self.assertEqual([0.02], ctr.timestamps)
+    self.assertEqual([40], ctr.samples)
+
+  def testMultiCounterUpdateBounds(self):
+    ctr = tracing_counter.Counter(None, 'testBasicCounter',
+        'testBasicCounter')
+    ctr.series_names = ['value1', 'value2']
+    ctr.timestamps = [0, 1, 2, 3, 4, 5, 6, 7]
+    ctr.samples = [0, 0,
+                   1, 0,
+                   1, 1,
+                   2, 1.1,
+                   3, 0,
+                   1, 7,
+                   3, 0,
+                   3.1, 0.5]
+    ctr.FinalizeImport()
+    self.assertEqual(8, ctr.max_total)
+    self.assertEqual([0, 0,
+                       1, 1,
+                       1, 2,
+                       2, 3.1,
+                       3, 3,
+                       1, 8,
+                       3, 3,
+                       3.1, 3.6], ctr.totals)
+
+  def testMultiCounter(self):
+    events = [
+      {'name': 'ctr', 'args': {'value1': 0, 'value2': 7}, 'pid': 1, 'ts': 0,
+       'cat': 'foo', 'tid': 1, 'ph': 'C'},
+      {'name': 'ctr', 'args': {'value1': 10, 'value2': 4}, 'pid': 1, 'ts': 10,
+       'cat': 'foo', 'tid': 1, 'ph': 'C'},
+      {'name': 'ctr', 'args': {'value1': 0, 'value2': 1}, 'pid': 1, 'ts': 20,
+       'cat': 'foo', 'tid': 1, 'ph': 'C'}
+    ]
+    trace_data = trace_data_module.CreateTraceDataFromRawData(events)
+    m = timeline_model.TimelineModel(trace_data)
+    p = m.GetAllProcesses()[0]
+    ctr = p.counters['foo.ctr']
+    self.assertEqual('ctr', ctr.name)
+
+    self.assertEqual('ctr', ctr.name)
+    self.assertEqual('foo', ctr.category)
+    self.assertEqual(3, ctr.num_samples)
+    self.assertEqual(2, ctr.num_series)
+
+    self.assertEqual(sorted(['value1', 'value2']), sorted(ctr.series_names))
+    self.assertEqual(sorted([0, 0.01, 0.02]), sorted(ctr.timestamps))
+    self.assertEqual(sorted([0, 7, 10, 4, 0, 1]), sorted(ctr.samples))
+    # We can't check ctr.totals here because it can change depending on
+    # the order in which the series names are added.
+    self.assertEqual(14, ctr.max_total)
+
+  def testStartFinishOneSliceOneThread(self):
+    events = [
+      # Time is intentionally out of order.
+      {'name': 'a', 'args': {}, 'pid': 52, 'ts': 560, 'cat': 'cat',
+       'tid': 53,
+         'ph': 'F', 'id': 72},
+      {'name': 'a', 'pid': 52, 'ts': 524, 'cat': 'cat',
+       'tid': 53,
+         'ph': 'S', 'id': 72, 'args': {'foo': 'bar'}}
+    ]
+
+    trace_data = trace_data_module.CreateTraceDataFromRawData(events)
+    m = timeline_model.TimelineModel(trace_data)
+
+    events = list(m.IterAllEvents())
+    self.assertEqual(2, len(events))
+
+    processes = m.GetAllProcesses()
+    t = processes[0].threads[53]
+    slices = t.async_slices
+    self.assertEqual(1, len(slices))
+    self.assertEqual('a', slices[0].name)
+    self.assertEqual('cat', slices[0].category)
+    self.assertEqual(72, slices[0].id)
+    self.assertEqual('bar', slices[0].args['foo'])
+    self.assertEqual(0, slices[0].start)
+    self.assertAlmostEqual((60 - 24) / 1000.0, slices[0].duration)
+    self.assertEqual(t, slices[0].start_thread)
+    self.assertEqual(t, slices[0].end_thread)
+
+  def testEndArgsAddedToSlice(self):
+    events = [
+      {'name': 'a', 'args': {'x': 1}, 'pid': 52, 'ts': 520, 'cat': 'foo',
+       'tid': 53, 'ph': 'B'},
+      {'name': 'a', 'args': {'y': 2}, 'pid': 52, 'ts': 560, 'cat': 'foo',
+       'tid': 53, 'ph': 'E'}
+    ]
+
+    trace_data = trace_data_module.CreateTraceDataFromRawData(events)
+    m = timeline_model.TimelineModel(trace_data)
+    processes = m.GetAllProcesses()
+    self.assertEqual(1, len(processes))
+    p = processes[0]
+
+    self.assertEqual(1, len(p.threads))
+    t = p.threads[53]
+    self.assertEqual(1, len(t.all_slices))
+    self.assertEqual(53, t.tid)
+    slice_event = t.all_slices[0]
+    self.assertEqual('a', slice_event.name)
+    self.assertEqual('foo', slice_event.category)
+    self.assertEqual(0, slice_event.start)
+    self.assertEqual(1, slice_event.args['x'])
+    self.assertEqual(2, slice_event.args['y'])
+
+  def testEndArgOverrwritesOriginalArgValueIfDuplicated(self):
+    events = [
+      {'name': 'b', 'args': {'z': 3}, 'pid': 52, 'ts': 629, 'cat': 'foo',
+       'tid': 53, 'ph': 'B'},
+      {'name': 'b', 'args': {'z': 4}, 'pid': 52, 'ts': 631, 'cat': 'foo',
+       'tid': 53, 'ph': 'E'}
+    ]
+
+    trace_data = trace_data_module.CreateTraceDataFromRawData(events)
+    m = timeline_model.TimelineModel(trace_data)
+    processes = m.GetAllProcesses()
+    self.assertEqual(1, len(processes))
+    p = processes[0]
+
+    self.assertEqual(1, len(p.threads))
+    t = p.threads[53]
+    slice_event = t.all_slices[0]
+    self.assertEqual('b', slice_event.name)
+    self.assertEqual('foo', slice_event.category)
+    self.assertEqual(0, slice_event.start)
+    self.assertEqual(4, slice_event.args['z'])
+
+  def testSliceHierarchy(self):
+    """The slice hierarchy should look something like this:
+           [            a            ]
+              [      b      ]  [ d ]
+              [ c ]     [ e ]
+    """
+    events = [
+      {'name': 'a', 'args': {}, 'pid': 52, 'ts': 100, 'cat': 'foo',
+       'tid': 53, 'ph': 'B'},
+      {'name': 'a', 'args': {}, 'pid': 52, 'ts': 200, 'cat': 'foo',
+       'tid': 53, 'ph': 'E'},
+      {'name': 'b', 'args': {}, 'pid': 52, 'ts': 125, 'cat': 'foo',
+       'tid': 53, 'ph': 'B'},
+      {'name': 'b', 'args': {}, 'pid': 52, 'ts': 165, 'cat': 'foo',
+       'tid': 53, 'ph': 'E'},
+      {'name': 'c', 'args': {}, 'pid': 52, 'ts': 125, 'cat': 'foo',
+       'tid': 53, 'ph': 'B'},
+      {'name': 'c', 'args': {}, 'pid': 52, 'ts': 135, 'cat': 'foo',
+       'tid': 53, 'ph': 'E'},
+      {'name': 'd', 'args': {}, 'pid': 52, 'ts': 175, 'cat': 'foo',
+       'tid': 53, 'ph': 'B'},
+      {'name': 'd', 'args': {}, 'pid': 52, 'ts': 190, 'cat': 'foo',
+       'tid': 53, 'ph': 'E'},
+      {'name': 'e', 'args': {}, 'pid': 52, 'ts': 155, 'cat': 'foo',
+       'tid': 53, 'ph': 'B'},
+      {'name': 'e', 'args': {}, 'pid': 52, 'ts': 165, 'cat': 'foo',
+       'tid': 53, 'ph': 'E'}
+    ]
+    trace_data = trace_data_module.CreateTraceDataFromRawData(events)
+    m = timeline_model.TimelineModel(trace_data, shift_world_to_zero=False)
+    processes = m.GetAllProcesses()
+    self.assertEqual(1, len(processes))
+    p = processes[0]
+
+    self.assertEqual(1, len(p.threads))
+    t = p.threads[53]
+
+    slice_a = t.all_slices[0]
+    self.assertEqual(4, len(slice_a.GetAllSubSlices()))
+    self.assertEqual('a', slice_a.name)
+    self.assertEqual(100 / 1000.0, slice_a.start)
+    self.assertEqual(200 / 1000.0, slice_a.end)
+    self.assertEqual(2, len(slice_a.sub_slices))
+
+    slice_b = slice_a.sub_slices[0]
+    self.assertEqual('b', slice_b.name)
+    self.assertEqual(2, len(slice_b.sub_slices))
+    self.assertEqual('c', slice_b.sub_slices[0].name)
+    self.assertEqual('e', slice_b.sub_slices[1].name)
+
+    slice_d = slice_a.sub_slices[1]
+    self.assertEqual('d', slice_d.name)
+    self.assertEqual(0, len(slice_d.sub_slices))
+
+  def testAsyncEndArgAddedToSlice(self):
+    events = [
+      # Time is intentionally out of order.
+      {'name': 'c', 'args': {'y': 2}, 'pid': 52, 'ts': 560, 'cat': 'foo',
+       'tid': 53,
+         'ph': 'F', 'id': 72},
+      {'name': 'c', 'args': {'x': 1}, 'pid': 52, 'ts': 524, 'cat': 'foo',
+       'tid': 53,
+         'ph': 'S', 'id': 72}
+    ]
+
+    trace_data = trace_data_module.CreateTraceDataFromRawData(events)
+    m = timeline_model.TimelineModel(trace_data)
+    t = m.GetAllProcesses()[0].threads[53]
+    self.assertEqual(1, len(t.async_slices))
+    parent_slice = t.async_slices[0]
+    self.assertEqual('c', parent_slice.name)
+    self.assertEqual('foo', parent_slice.category)
+
+    self.assertEqual(1, len(parent_slice.sub_slices))
+    sub_slice = parent_slice.sub_slices[0]
+    self.assertEqual(1, sub_slice.args['x'])
+    self.assertEqual(2, sub_slice.args['y'])
+
+  def testAsyncEndArgOverrwritesOriginalArgValueIfDuplicated(self):
+    events = [
+      # Time is intentionally out of order.
+      {'name': 'd', 'args': {'z': 4}, 'pid': 52, 'ts': 560, 'cat': 'foo',
+       'tid': 53,
+         'ph': 'F', 'id': 72},
+      {'name': 'd', 'args': {'z': 3}, 'pid': 52, 'ts': 524, 'cat': 'foo',
+       'tid': 53,
+         'ph': 'S', 'id': 72}
+    ]
+
+    trace_data = trace_data_module.CreateTraceDataFromRawData(events)
+    m = timeline_model.TimelineModel(trace_data)
+    t = m.GetAllProcesses()[0].threads[53]
+    self.assertEqual(1, len(t.async_slices))
+    parent_slice = t.async_slices[0]
+    self.assertEqual('d', parent_slice.name)
+    self.assertEqual('foo', parent_slice.category)
+
+    self.assertEqual(1, len(parent_slice.sub_slices))
+    sub_slice = parent_slice.sub_slices[0]
+    self.assertEqual(4, sub_slice.args['z'])
+
+  def testAsyncStepsInOneThread(self):
+    events = [
+      # Time is intentionally out of order.
+      {'name': 'a', 'args': {'z': 3}, 'pid': 52, 'ts': 560, 'cat': 'foo',
+       'tid': 53, 'ph': 'F', 'id': 72, 'tts': 25},
+      {'name': 'a', 'args': {'step': 's1', 'y': 2}, 'pid': 52, 'ts': 548,
+       'cat': 'foo', 'tid': 53, 'ph': 'T', 'id': 72, 'tts': 20},
+      {'name': 'a', 'args': {'x': 1}, 'pid': 52, 'ts': 524, 'cat': 'foo',
+       'tid': 53, 'ph': 'S', 'id': 72, 'tts': 17}
+    ]
+
+    trace_data = trace_data_module.CreateTraceDataFromRawData(events)
+    m = timeline_model.TimelineModel(trace_data)
+    t = m.GetAllProcesses()[0].threads[53]
+    self.assertEqual(1, len(t.async_slices))
+    parent_slice = t.async_slices[0]
+    self.assertEqual('a', parent_slice.name)
+    self.assertEqual('foo', parent_slice.category)
+    self.assertEqual(0, parent_slice.start)
+    self.assertAlmostEqual(17/1000.0, parent_slice.thread_start)
+    self.assertAlmostEqual(25/1000.0, parent_slice.thread_end)
+
+    self.assertEqual(2, len(parent_slice.sub_slices))
+    sub_slice = parent_slice.sub_slices[0]
+    self.assertEqual('a', sub_slice.name)
+    self.assertEqual('foo', sub_slice.category)
+    self.assertAlmostEqual(0, sub_slice.start)
+    self.assertAlmostEqual((548 - 524) / 1000.0, sub_slice.duration)
+    self.assertAlmostEqual((20 - 17) / 1000.0, sub_slice.thread_duration)
+    self.assertEqual(1, sub_slice.args['x'])
+
+    sub_slice = parent_slice.sub_slices[1]
+    self.assertEqual('a:s1', sub_slice.name)
+    self.assertEqual('foo', sub_slice.category)
+    self.assertAlmostEqual((548 - 524) / 1000.0, sub_slice.start)
+    self.assertAlmostEqual((560 - 548) / 1000.0, sub_slice.duration)
+    self.assertAlmostEqual((25 - 20) / 1000.0, sub_slice.thread_duration)
+    self.assertEqual(2, sub_slice.args['y'])
+    self.assertEqual(3, sub_slice.args['z'])
+
+  def testAsyncStepsMissingStart(self):
+    events = [
+      # Time is intentionally out of order.
+      {'name': 'a', 'args': {'z': 3}, 'pid': 52, 'ts': 560, 'cat': 'foo',
+       'tid': 53, 'ph': 'F', 'id': 72},
+      {'name': 'a', 'args': {'step': 's1', 'y': 2}, 'pid': 52, 'ts': 548,
+       'cat': 'foo', 'tid': 53, 'ph': 'T', 'id': 72}
+    ]
+
+    trace_data = trace_data_module.CreateTraceDataFromRawData(events)
+    m = timeline_model.TimelineModel(trace_data)
+    t = m.GetAllProcesses()[0].threads[53]
+    self.assertTrue(t is not None)
+
+  def testAsyncStepsMissingFinish(self):
+    events = [
+      # Time is intentionally out of order.
+      {'name': 'a', 'args': {'step': 's1', 'y': 2}, 'pid': 52, 'ts': 548,
+       'cat': 'foo', 'tid': 53, 'ph': 'T', 'id': 72},
+      {'name': 'a', 'args': {'z': 3}, 'pid': 52, 'ts': 560, 'cat': 'foo',
+       'tid': 53, 'ph': 'S', 'id': 72}
+    ]
+
+    trace_data = trace_data_module.CreateTraceDataFromRawData(events)
+    m = timeline_model.TimelineModel(trace_data)
+    t = m.GetAllProcesses()[0].threads[53]
+    self.assertTrue(t is not None)
+
+  def testImportSamples(self):
+    events = [
+      {'name': 'a', 'args': {}, 'pid': 52, 'ts': 548, 'cat': 'test',
+       'tid': 53, 'ph': 'P'},
+      {'name': 'b', 'args': {}, 'pid': 52, 'ts': 548, 'cat': 'test',
+       'tid': 53, 'ph': 'P'},
+      {'name': 'c', 'args': {}, 'pid': 52, 'ts': 558, 'cat': 'test',
+       'tid': 53, 'ph': 'P'}
+    ]
+    trace_data = trace_data_module.CreateTraceDataFromRawData(events)
+    m = timeline_model.TimelineModel(trace_data)
+    p = m.GetAllProcesses()[0]
+    t = p.threads[53]
+    self.assertEqual(3, len(t.samples))
+    self.assertEqual(0.0, t.samples[0].start)
+    self.assertEqual(0.0, t.samples[1].start)
+    self.assertAlmostEqual(0.01, t.samples[2].start)
+    self.assertEqual('a', t.samples[0].name)
+    self.assertEqual('b', t.samples[1].name)
+    self.assertEqual('c', t.samples[2].name)
+    self.assertEqual(0, len(m.import_errors))
+
+  def testImportSamplesMissingArgs(self):
+    events = [
+      {'name': 'a', 'pid': 52, 'ts': 548, 'cat': 'test',
+       'tid': 53, 'ph': 'P'},
+      {'name': 'b', 'pid': 52, 'ts': 548, 'cat': 'test',
+       'tid': 53, 'ph': 'P'},
+      {'name': 'c', 'pid': 52, 'ts': 549, 'cat': 'test',
+       'tid': 53, 'ph': 'P'}
+    ]
+    trace_data = trace_data_module.CreateTraceDataFromRawData(events)
+    m = timeline_model.TimelineModel(trace_data)
+    p = m.GetAllProcesses()[0]
+    t = p.threads[53]
+    self.assertEqual(3, len(t.samples))
+    self.assertEqual(0, len(m.import_errors))
+
+  def testImportCompleteEvent(self):
+    events = [
+      {'name': 'a', 'args': {}, 'pid': 52, 'ts': 629, 'tts': 538, 'dur': 1,
+       'tdur': 1, 'cat': 'baz', 'tid': 53, 'ph': 'X'},
+      {'name': 'b', 'args': {}, 'pid': 52, 'ts': 730, 'tts': 620, 'dur': 20,
+       'tdur': 14, 'cat': 'foo', 'tid': 53, 'ph': 'X'},
+      {'name': 'c', 'args': {}, 'pid': 52, 'ts': 740, 'tts': 625, 'cat': 'baz',
+       'tid': 53, 'ph': 'X'},
+    ]
+    trace_data = trace_data_module.CreateTraceDataFromRawData(events)
+    m = timeline_model.TimelineModel(trace_data)
+    p = m.GetAllProcesses()[0]
+    t = p.threads[53]
+    self.assertEqual(3, len(t.all_slices))
+
+    slice_event = t.all_slices[0]
+    self.assertEqual('a', slice_event.name)
+    self.assertAlmostEqual(0.0, slice_event.start)
+    self.assertAlmostEqual(1 / 1000.0, slice_event.duration)
+    self.assertAlmostEqual(538 / 1000.0, slice_event.thread_start)
+    self.assertAlmostEqual(1 / 1000.0, slice_event.thread_duration)
+    self.assertFalse(slice_event.did_not_finish)
+    self.assertEqual(0, len(slice_event.sub_slices))
+
+    slice_event = t.all_slices[1]
+    self.assertEqual('b', slice_event.name)
+    self.assertAlmostEqual((730 - 629) / 1000.0, slice_event.start)
+    self.assertAlmostEqual(20 / 1000.0, slice_event.duration)
+    self.assertAlmostEqual(620 / 1000.0, slice_event.thread_start)
+    self.assertAlmostEqual(14 / 1000.0, slice_event.thread_duration)
+    self.assertFalse(slice_event.did_not_finish)
+    self.assertEqual(1, len(slice_event.sub_slices))
+    self.assertEqual(t.all_slices[2], slice_event.sub_slices[0])
+
+    slice_event = t.all_slices[2]
+    self.assertEqual('c', slice_event.name)
+    self.assertAlmostEqual((740 - 629) / 1000.0, slice_event.start)
+    self.assertAlmostEqual(10 / 1000.0, slice_event.duration)
+    self.assertAlmostEqual(625 / 1000.0, slice_event.thread_start)
+    self.assertAlmostEqual(9 / 1000.0, slice_event.thread_duration)
+    self.assertTrue(slice_event.did_not_finish)
+    self.assertEqual(0, len(slice_event.sub_slices))
+
+  def testImportMarkEvent(self):
+    events = [
+      {'name': 'a', 'pid': 52, 'ts': 629, 'cat': 'baz', 'tid': 53, 'ph': 'R'},
+      {'name': 'b', 'pid': 52, 'ts': 730, 'cat': 'foo', 'tid': 53, 'ph': 'R'},
+      {'name': 'c', 'pid': 52, 'ts': 740, 'cat': 'baz', 'tid': 53, 'ph': 'R'},
+    ]
+    trace_data = trace_data_module.CreateTraceDataFromRawData(events)
+    m = timeline_model.TimelineModel(trace_data)
+    p = m.GetAllProcesses()[0]
+    t = p.threads[53]
+    self.assertEqual(3, len(t.all_slices))
+
+    slice_event = t.all_slices[0]
+    self.assertEqual('a', slice_event.name)
+    self.assertEqual('baz', slice_event.category)
+    self.assertAlmostEqual(0.0, slice_event.start)
+    self.assertFalse(slice_event.did_not_finish)
+    self.assertEqual(0, len(slice_event.sub_slices))
+
+    slice_event = t.all_slices[1]
+    self.assertEqual('b', slice_event.name)
+    self.assertEqual('foo', slice_event.category)
+    self.assertAlmostEqual((730 - 629) / 1000.0, slice_event.start)
+    self.assertFalse(slice_event.did_not_finish)
+    self.assertEqual(0, len(slice_event.sub_slices))
+
+    slice_event = t.all_slices[2]
+    self.assertEqual('c', slice_event.name)
+    self.assertEqual('baz', slice_event.category)
+    self.assertAlmostEqual((740 - 629) / 1000.0, slice_event.start)
+    self.assertFalse(slice_event.did_not_finish)
+    self.assertEqual(0, len(slice_event.sub_slices))
+
+  def testImportFlowEvent(self):
+    events = [
+      {'name': 'a', 'cat': 'foo', 'id': 72, 'pid': 52, 'tid': 53, 'ts': 548,
+       'ph': 's', 'args': {}},
+      {'name': 'a', 'cat': 'foo', 'id': 72, 'pid': 52, 'tid': 53, 'ts': 560,
+       'ph': 't', 'args': {}},
+      {'name': 'a', 'cat': 'foo', 'id': 72, 'pid': 52, 'tid': 53, 'ts': 580,
+       'ph': 'f', 'args': {}},
+    ]
+
+    trace_data = trace_data_module.CreateTraceDataFromRawData(events)
+    m = timeline_model.TimelineModel(trace_data)
+    p = m.GetAllProcesses()[0]
+    t = p.threads[53]
+    self.assertTrue(t is not None)
+    self.assertEqual(2, len(m.flow_events))
+
+    start = m.flow_events[0][0]
+    step = m.flow_events[0][1]
+    finish = m.flow_events[1][1]
+
+    self.assertEqual('a', start.name)
+    self.assertEqual('foo', start.category)
+    self.assertEqual(72, start.event_id)
+    self.assertEqual(0, start.start)
+    self.assertEqual(0, start.duration)
+
+    self.assertEqual(start.name, step.name)
+    self.assertEqual(start.category, step.category)
+    self.assertEqual(start.event_id, step.event_id)
+    self.assertAlmostEqual(12 / 1000.0, step.start)
+    self.assertEquals(0, step.duration)
+
+    self.assertEqual(start.name, finish.name)
+    self.assertEqual(start.category, finish.category)
+    self.assertEqual(start.event_id, finish.event_id)
+    self.assertAlmostEqual((20 + 12) / 1000.0, finish.start)
+    self.assertEqual(0, finish.duration)
+
+  def testImportOutOfOrderFlowEvent(self):
+    events = [
+      {'name': 'a', 'cat': 'foo', 'id': 72, 'pid': 52, 'tid': 53, 'ts': 548,
+       'ph': 's', 'args': {}},
+      {'name': 'b', 'cat': 'foo', 'id': 73, 'pid': 52, 'tid': 53, 'ts': 148,
+       'ph': 's', 'args': {}},
+      {'name': 'b', 'cat': 'foo', 'id': 73, 'pid': 52, 'tid': 53, 'ts': 570,
+       'ph': 'f', 'args': {}},
+      {'name': 'a', 'cat': 'foo', 'id': 72, 'pid': 52, 'tid': 53, 'ts': 560,
+       'ph': 't', 'args': {}},
+      {'name': 'a', 'cat': 'foo', 'id': 72, 'pid': 52, 'tid': 53, 'ts': 580,
+       'ph': 'f', 'args': {}},
+    ]
+
+    expected = [[0.4, 0.412], [0.0, 0.422], [0.412, 0.432]]
+    trace_data = trace_data_module.CreateTraceDataFromRawData(events)
+    m = timeline_model.TimelineModel(trace_data)
+    self.assertEqual(3, len(m.flow_events))
+
+    for i in range(len(expected)):
+      self.assertAlmostEqual(expected[i][0], m.flow_events[i][0].start)
+      self.assertAlmostEqual(expected[i][1], m.flow_events[i][1].start)
+
+  def testImportErrornousFlowEvent(self):
+    events = [
+      {'name': 'a', 'cat': 'foo', 'id': 70, 'pid': 52, 'tid': 53, 'ts': 548,
+       'ph': 's', 'args': {}},
+      {'name': 'a2', 'cat': 'foo', 'id': 70, 'pid': 52, 'tid': 53, 'ts': 550,
+       'ph': 's', 'args': {}},
+      {'name': 'b', 'cat': 'foo', 'id': 73, 'pid': 52, 'tid': 53, 'ts': 570,
+       'ph': 'f', 'args': {}},
+      {'name': 'a', 'cat': 'foo', 'id': 72, 'pid': 52, 'tid': 53, 'ts': 560,
+       'ph': 't', 'args': {}},
+    ]
+
+    trace_data = trace_data_module.CreateTraceDataFromRawData(events)
+    m = timeline_model.TimelineModel(trace_data)
+    self.assertEqual(0, len(m.flow_events))
+
+  def testImportMemoryDumpEvents(self):
+    events = [
+      {'name': 'a', 'cat': 'b', 'ph': 'v', 'pid': 52, 'ts': 123,
+       'id': '1234ABCD'},
+      {'name': 'a', 'cat': 'b', 'ph': 'v', 'pid': 54, 'ts': 134,
+       'id': '1234ABCD'},
+      {'name': 'a', 'cat': 'b', 'ph': 'v', 'pid': 52, 'ts': 144,
+       'id': '1234ABCD'},
+      {'name': 'a', 'cat': 'b', 'ph': 'v', 'pid': 52, 'ts': 245,
+       'id': '1234ABDF'},
+      {'name': 'a', 'cat': 'b', 'ph': 'v', 'pid': 54, 'ts': 256,
+       'id': '1234ABDF'},
+      {'name': 'a', 'cat': 'b', 'ph': 'v', 'pid': 52, 'ts': 233,
+       'id': '1234ABDF'},
+    ]
+
+    expected_processes = set([52, 54])
+    expected_results = [['1234ABCD', 0, 21], ['1234ABDF', 110, 23]]
+    trace_data = trace_data_module.CreateTraceDataFromRawData(events)
+    m = timeline_model.TimelineModel(trace_data)
+    assert set(p.pid for p in m.GetAllProcesses()) == expected_processes
+
+    memory_dumps = list(m.IterGlobalMemoryDumps())
+    self.assertEqual(len(expected_results), len(memory_dumps))
+    for memory_dump, test_values in zip(memory_dumps, expected_results):
+      assert len(list(memory_dump.IterProcessMemoryDumps())) == len(
+          expected_processes)
+      dump_id, start, duration = test_values
+      self.assertEquals(dump_id, memory_dump.dump_id)
+      self.assertAlmostEqual(start / 1000.0, memory_dump.start)
+      self.assertAlmostEqual(duration / 1000.0, memory_dump.duration)
+
+  def testImportOutOfOrderMemoryDumpEvents(self):
+    events = [
+      {'name': 'a', 'cat': 'b', 'ph': 'v', 'pid': 52, 'ts': 245,
+       'id': '1234ABDF'},
+      {'name': 'a', 'cat': 'b', 'ph': 'v', 'pid': 54, 'ts': 134,
+       'id': '1234ABCD'},
+      {'name': 'a', 'cat': 'b', 'ph': 'v', 'pid': 54, 'ts': 256,
+       'id': '1234ABDF'},
+      {'name': 'a', 'cat': 'b', 'ph': 'v', 'pid': 52, 'ts': 123,
+       'id': '1234ABCD'},
+    ]
+
+    expected = [['1234ABCD', 0, 11], ['1234ABDF', 122, 11]]
+    trace_data = trace_data_module.CreateTraceDataFromRawData(events)
+    m = timeline_model.TimelineModel(trace_data)
+    memory_dumps = list(m.IterGlobalMemoryDumps())
+    self.assertEqual(len(expected), len(memory_dumps))
+    for memory_dump, test_values in zip(memory_dumps, expected):
+      dump_id, start, duration = test_values
+      self.assertEquals(dump_id, memory_dump.dump_id)
+      self.assertAlmostEqual(start / 1000.0, memory_dump.start)
+      self.assertAlmostEqual(duration / 1000.0, memory_dump.duration)
+
+  def testMetadataImport(self):
+    events = [
+      {'cat': '__metadata', 'pid': 14689, 'tid': 14740, 'ts': 245,
+       'ph': 'M', 'name': 'process_name', 'args': {'name': 'Browser'}},
+      {'cat': '__metadata', 'pid': 23828, 'tid': 23828, 'ts': 0,
+       'ph': 'M', 'name': 'process_labels',
+       'args': {'labels': 'huge image - Google Search'}}
+    ]
+
+    expected = [
+      [None, 'Browser'],
+      ['huge image - Google Search', 'process 23828']
+    ]
+    trace_data = trace_data_module.CreateTraceDataFromRawData(events)
+    m = timeline_model.TimelineModel(trace_data)
+    processes = m.GetAllProcesses()
+
+    self.assertEqual(len(processes), len(expected))
+    for process, test_values in zip(processes, expected):
+      process_labels, process_name = test_values
+      self.assertEquals(process_labels, process.labels)
+      self.assertEquals(process_name, process.name)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/tracing_config.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/tracing_config.py
new file mode 100644
index 0000000..049f545
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/timeline/tracing_config.py
@@ -0,0 +1,103 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.timeline import atrace_config
+from telemetry.timeline import chrome_trace_config
+
+
+class TracingConfig(object):
+  """Tracing config is the configuration for tracing in Telemetry.
+
+  TracingConfig configures tracing in Telemetry. It contains tracing options
+  that control which core tracing system should be enabled. If a tracing
+  system requires additional configuration, e.g., what to trace, then it is
+  typically configured in its own config class. TracingConfig provides
+  interfaces to access the configuration for those tracing systems.
+
+  Options:
+      enable_atrace_trace: a boolean that specifies whether to enable
+          atrace tracing.
+      enable_cpu_trace: a boolean that specifies whether to enable cpu tracing.
+      enable_chrome_trace: a boolean that specifies whether to enable
+          chrome tracing.
+      enable_platform_display_trace: a boolean that specifies whether to
+          platform display tracing.
+      enable_android_graphics_memtrack: a boolean that specifies whether
+          to enable the memtrack_helper daemon to track graphics memory on
+          Android (see goo.gl/4Y30p9). Doesn't have any effects on other OSs.
+      enable_battor_trace: a boolean that specifies whether to enable BattOr
+          tracing.
+
+  Detailed configurations:
+      atrace_config: Stores configuration options specific to Atrace.
+      chrome_trace_config: Stores configuration options specific to
+          Chrome trace.
+  """
+
+  def __init__(self):
+    self._enable_atrace_trace = False
+    self._enable_platform_display_trace = False
+    self._enable_android_graphics_memtrack = False
+    self._enable_battor_trace = False
+    self._enable_cpu_trace = False
+    self._enable_chrome_trace = False
+
+    self._atrace_config = atrace_config.AtraceConfig()
+    self._chrome_trace_config = chrome_trace_config.ChromeTraceConfig()
+
+  @property
+  def enable_atrace_trace(self):
+    return self._enable_atrace_trace
+
+  @enable_atrace_trace.setter
+  def enable_atrace_trace(self, value):
+    self._enable_atrace_trace = value
+
+  @property
+  def enable_cpu_trace(self):
+    return self._enable_cpu_trace
+
+  @enable_cpu_trace.setter
+  def enable_cpu_trace(self, value):
+    self._enable_cpu_trace = value
+
+  @property
+  def enable_platform_display_trace(self):
+    return self._enable_platform_display_trace
+
+  @enable_platform_display_trace.setter
+  def enable_platform_display_trace(self, value):
+    self._enable_platform_display_trace = value
+
+  @property
+  def enable_android_graphics_memtrack(self):
+    return self._enable_android_graphics_memtrack
+
+  @enable_android_graphics_memtrack.setter
+  def enable_android_graphics_memtrack(self, value):
+    self._enable_android_graphics_memtrack = value
+
+  @property
+  def enable_battor_trace(self):
+    return self._enable_battor_trace
+
+  @enable_battor_trace.setter
+  def enable_battor_trace(self, value):
+    self._enable_battor_trace = value
+
+  @property
+  def enable_chrome_trace(self):
+    return self._enable_chrome_trace
+
+  @enable_chrome_trace.setter
+  def enable_chrome_trace(self, value):
+    self._enable_chrome_trace = value
+
+  @property
+  def atrace_config(self):
+    return self._atrace_config
+
+  @property
+  def chrome_trace_config(self):
+    return self._chrome_trace_config
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/__init__.py
new file mode 100644
index 0000000..08483cc
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/__init__.py
@@ -0,0 +1,4 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""A library for bootstrapping Telemetry performance testing."""
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/bot_utils.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/bot_utils.py
new file mode 100644
index 0000000..70955f5
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/bot_utils.py
@@ -0,0 +1,27 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Utility functions used to generate info used by the bots.
+
+TODO(eyaich): Remove once we no longer generate the list of benchmarks to
+run on the perf waterfall in telemetry.
+"""
+
+import hashlib
+
+def GetDeviceAffinity(num_shards, base_name):
+  # Based on the current timings, we shift the result of the hash function to
+  # achieve better load balancing. Those shift values are to be revised when
+  # necessary. The shift value is calculated such that the total cycle time
+  # is minimized.
+  hash_shift = {
+    2 : 47,  # for old desktop configurations with 2 slaves
+    5 : 56,  # for new desktop configurations with 5 slaves
+    21 : 43  # for Android 3 slaves 7 devices configurations
+  }
+  shift = hash_shift.get(num_shards, 0)
+  base_name_hash = hashlib.sha1(base_name).hexdigest()
+  device_affinity = (int(base_name_hash, 16) >> shift) % num_shards
+  return device_affinity
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/color_histogram.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/color_histogram.py
new file mode 100644
index 0000000..76bb4b9
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/color_histogram.py
@@ -0,0 +1,67 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Color Histograms and implementations of functions operating on them."""
+
+from __future__ import division
+
+import collections
+
+from telemetry.internal.util import external_modules
+
+np = external_modules.ImportOptionalModule('numpy')
+
+
+def HistogramDistance(hist1, hist2, default_color=None):
+  """Earth mover's distance.
+  http://en.wikipedia.org/wiki/Earth_mover's_distance"""
+  if len(hist1) != len(hist2):
+    raise ValueError('Trying to compare histograms '
+                     'of different sizes, %s != %s' % (len(hist1), len(hist2)))
+  if len(hist1) == 0:
+    return 0
+
+  sum_func = np.sum if np is not None else sum
+
+  n1 = sum_func(hist1)
+  n2 = sum_func(hist2)
+  if (n1 == 0 or n2 == 0) and default_color is None:
+    raise ValueError('Histogram has no data and no default color.')
+  if n1 == 0:
+    hist1[default_color] = 1
+    n1 = 1
+  if n2 == 0:
+    hist2[default_color] = 1
+    n2 = 1
+
+  if np is not None:
+    remainder = np.multiply(hist1, n2) - np.multiply(hist2, n1)
+    cumsum = np.cumsum(remainder)
+    total = np.sum(np.abs(cumsum))
+  else:
+    total = 0
+    remainder = 0
+    for value1, value2 in zip(hist1, hist2):
+      remainder += value1 * n2 - value2 * n1
+      total += abs(remainder)
+    assert remainder == 0, (
+        '%s pixel(s) left over after computing histogram distance.'
+        % abs(remainder))
+  return abs(float(total) / n1 / n2)
+
+
+class ColorHistogram(
+    collections.namedtuple('ColorHistogram', ['r', 'g', 'b', 'default_color'])):
+  # pylint: disable=no-init
+  # pylint: disable=super-on-old-class
+
+  def __new__(cls, r, g, b, default_color=None):
+    return super(ColorHistogram, cls).__new__(cls, r, g, b, default_color)
+
+  def Distance(self, other):
+    total = 0
+    for i in xrange(3):
+      default_color = self[3][i] if self[3] is not None else None
+      total += HistogramDistance(self[i], other[i], default_color)
+    return total
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/color_histogram_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/color_histogram_unittest.py
new file mode 100644
index 0000000..a853d53
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/color_histogram_unittest.py
@@ -0,0 +1,117 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from telemetry.util import color_histogram
+from telemetry.util import image_util
+from telemetry.util import rgba_color
+
+class HistogramDistanceTest(unittest.TestCase):
+  def testNoData(self):
+    hist1 = []
+    hist2 = []
+    self.assertEqual(color_histogram.HistogramDistance(hist1, hist2), 0)
+
+    hist1 = [0, 0, 0]
+    hist2 = [0, 0, 0]
+    self.assertRaises(
+        ValueError, lambda: color_histogram.HistogramDistance(hist1, hist2))
+
+  def testWrongSizes(self):
+    hist1 = [1]
+    hist2 = [1, 0]
+    self.assertRaises(
+        ValueError, lambda: color_histogram.HistogramDistance(hist1, hist2))
+
+  def testNoDistance(self):
+    hist1 = [2, 4, 1, 8, 0, 0]
+    hist2 = [2, 4, 1, 8, 0, 0]
+    self.assertEqual(color_histogram.HistogramDistance(hist1, hist2), 0)
+
+  def testNormalizeCounts(self):
+    hist1 = [0, 0, 1, 0, 0]
+    hist2 = [0, 0, 0, 0, 7]
+    self.assertEqual(color_histogram.HistogramDistance(hist1, hist2), 2)
+    self.assertEqual(color_histogram.HistogramDistance(hist2, hist1), 2)
+
+  def testDistance(self):
+    hist1 = [2, 0, 1, 3, 4]
+    hist2 = [3, 1, 2, 4, 0]
+    self.assertEqual(color_histogram.HistogramDistance(hist1, hist2), 1)
+    self.assertEqual(color_histogram.HistogramDistance(hist2, hist1), 1)
+
+    hist1 = [0, 1, 3, 1]
+    hist2 = [2, 2, 1, 0]
+    self.assertEqual(color_histogram.HistogramDistance(hist1, hist2), 1.2)
+    self.assertEqual(color_histogram.HistogramDistance(hist2, hist1), 1.2)
+
+
+class HistogramTest(unittest.TestCase):
+  def testHistogram(self):
+    pixels = [1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3,
+              1, 2, 3, 8, 7, 6, 5, 4, 6, 1, 2, 3,
+              1, 2, 3, 8, 7, 6, 5, 4, 6, 1, 2, 3]
+    bmp = image_util.FromRGBPixels(4, 3, pixels)
+    bmp = image_util.Crop(bmp, 1, 1, 2, 2)
+
+    hist = image_util.GetColorHistogram(bmp)
+    for i in xrange(3):
+      self.assertEquals(sum(hist[i]),
+                        image_util.Width(bmp) * image_util.Height(bmp))
+    self.assertEquals(hist.r[1], 0)
+    self.assertEquals(hist.r[5], 2)
+    self.assertEquals(hist.r[8], 2)
+    self.assertEquals(hist.g[2], 0)
+    self.assertEquals(hist.g[4], 2)
+    self.assertEquals(hist.g[7], 2)
+    self.assertEquals(hist.b[3], 0)
+    self.assertEquals(hist.b[6], 4)
+
+  def testHistogramIgnoreColor(self):
+    pixels = [1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3,
+              1, 2, 3, 8, 7, 6, 5, 4, 6, 1, 2, 3,
+              1, 2, 3, 8, 7, 6, 5, 4, 6, 1, 2, 3]
+    bmp = image_util.FromRGBPixels(4, 3, pixels)
+
+    hist = image_util.GetColorHistogram(
+        bmp, ignore_color=rgba_color.RgbaColor(1, 2, 3))
+    self.assertEquals(hist.r[1], 0)
+    self.assertEquals(hist.r[5], 2)
+    self.assertEquals(hist.r[8], 2)
+    self.assertEquals(hist.g[2], 0)
+    self.assertEquals(hist.g[4], 2)
+    self.assertEquals(hist.g[7], 2)
+    self.assertEquals(hist.b[3], 0)
+    self.assertEquals(hist.b[6], 4)
+
+  def testHistogramIgnoreColorTolerance(self):
+    pixels = [1, 2, 3, 4, 5, 6,
+              7, 8, 9, 8, 7, 6]
+    bmp = image_util.FromRGBPixels(2, 2, pixels)
+
+    hist = image_util.GetColorHistogram(
+        bmp, ignore_color=rgba_color.RgbaColor(0, 1, 2), tolerance=1)
+    self.assertEquals(hist.r[1], 0)
+    self.assertEquals(hist.r[4], 1)
+    self.assertEquals(hist.r[7], 1)
+    self.assertEquals(hist.r[8], 1)
+    self.assertEquals(hist.g[2], 0)
+    self.assertEquals(hist.g[5], 1)
+    self.assertEquals(hist.g[7], 1)
+    self.assertEquals(hist.g[8], 1)
+    self.assertEquals(hist.b[3], 0)
+    self.assertEquals(hist.b[6], 2)
+    self.assertEquals(hist.b[9], 1)
+
+  def testHistogramDistanceIgnoreColor(self):
+    pixels = [1, 2, 3, 1, 2, 3,
+              1, 2, 3, 1, 2, 3]
+    bmp = image_util.FromRGBPixels(2, 2, pixels)
+
+    hist1 = image_util.GetColorHistogram(
+        bmp, ignore_color=rgba_color.RgbaColor(1, 2, 3))
+    hist2 = image_util.GetColorHistogram(bmp)
+
+    self.assertEquals(hist1.Distance(hist2), 0)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/command_line.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/command_line.py
new file mode 100644
index 0000000..faf870f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/command_line.py
@@ -0,0 +1,39 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import argparse
+
+from telemetry.internal.util import command_line
+
+
+class ArgParseCommand(command_line.Command):
+  usage = ''
+
+  @classmethod
+  def CreateParser(cls):
+    return argparse.ArgumentParser('%%prog %s %s' % (cls.Name(), cls.usage),
+                                   description=cls.Description())
+
+  @classmethod
+  def AddCommandLineArgs(cls, parser, environment):
+    # pylint: disable=arguments-differ
+    pass
+
+  @classmethod
+  def ProcessCommandLineArgs(cls, parser, options, extra_args, environment):
+    # pylint: disable=arguments-differ
+    pass
+
+  def Run(self, options, extra_args=None):
+    # pylint: disable=arguments-differ
+    raise NotImplementedError()
+
+  @classmethod
+  def main(cls, args=None):
+    """Main method to run this command as a standalone script."""
+    parser = cls.CreateParser()
+    cls.AddCommandLineArgs(parser, None)
+    options, extra_args = parser.parse_known_args(args=args)
+    cls.ProcessCommandLineArgs(parser, options, extra_args, None)
+    return min(cls().Run(options, extra_args), 255)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/dependency.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/dependency.py
new file mode 100644
index 0000000..9ff2ee6
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/dependency.py
@@ -0,0 +1,17 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.core import platform as platform_module
+from telemetry.internal.util import binary_manager
+
+def FetchTelemetryDependencies(
+  platform=None, client_configs=None, chrome_reference_browser=False):
+  if not platform:
+    platform = platform_module.GetHostPlatform()
+  if binary_manager.NeedsInit():
+    binary_manager.InitDependencyManager(client_configs)
+  else:
+    raise Exception('Binary manager already initialized with other configs.')
+  binary_manager.FetchBinaryDependencies(
+    platform, client_configs, chrome_reference_browser)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/image_util.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/image_util.py
new file mode 100644
index 0000000..809dc57
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/image_util.py
@@ -0,0 +1,121 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Provides implementations of basic image processing functions.
+
+Implements basic image processing functions, such as reading/writing images,
+cropping, finding the bounding box of a color and diffing images.
+
+When numpy is present, image_util_numpy_impl is used for the implementation of
+this interface. The old bitmap implementation (image_util_bitmap_impl) is used
+as a fallback when numpy is not present."""
+
+import base64
+
+from telemetry.internal.util import external_modules
+
+np = external_modules.ImportOptionalModule('numpy')
+
+if np is None:
+  from telemetry.internal.image_processing import image_util_bitmap_impl
+  impl = image_util_bitmap_impl
+else:
+  from telemetry.internal.image_processing import image_util_numpy_impl
+  impl = image_util_numpy_impl
+
+
+def Channels(image):
+  """Number of color channels in the image."""
+  return impl.Channels(image)
+
+def Width(image):
+  """Width of the image."""
+  return impl.Width(image)
+
+def Height(image):
+  """Height of the image."""
+  return impl.Height(image)
+
+def Pixels(image):
+  """Flat RGB pixel array of the image."""
+  return impl.Pixels(image)
+
+def GetPixelColor(image, x, y):
+  """Returns a RgbaColor for the pixel at (x, y)."""
+  return impl.GetPixelColor(image, x, y)
+
+def WritePngFile(image, path):
+  """Write an image to a PNG file.
+
+  Args:
+    image: an image object.
+    path: The path to the PNG file. Must end in 'png' or an
+          AssertionError will be raised."""
+  assert path.endswith('png')
+  return impl.WritePngFile(image, path)
+
+def FromRGBPixels(width, height, pixels, bpp=3):
+  """Create an image from an array of rgb pixels.
+
+  Ignores alpha channel if present.
+
+  Args:
+    width, height: int, the width and height of the image.
+    pixels: The flat array of pixels in the form of [r,g,b[,a],r,g,b[,a],...]
+    bpp: 3 for RGB, 4 for RGBA."""
+  return impl.FromRGBPixels(width, height, pixels, bpp)
+
+def FromPng(png_data):
+  """Create an image from raw PNG data."""
+  return impl.FromPng(png_data)
+
+def FromPngFile(path):
+  """Create an image from a PNG file.
+
+  Args:
+    path: The path to the PNG file."""
+  return impl.FromPngFile(path)
+
+def FromBase64Png(base64_png):
+  """Create an image from raw PNG data encoded in base64."""
+  return FromPng(base64.b64decode(base64_png))
+
+def AreEqual(image1, image2, tolerance=0, likely_equal=True):
+  """Determines whether two images are identical within a given tolerance.
+  Setting likely_equal to False enables short-circuit equality testing, which
+  is about 2-3x slower for equal images, but can be image height times faster
+  if the images are not equal."""
+  return impl.AreEqual(image1, image2, tolerance, likely_equal)
+
+def Diff(image1, image2):
+  """Returns a new image that represents the difference between this image
+  and another image."""
+  return impl.Diff(image1, image2)
+
+def GetBoundingBox(image, color, tolerance=0):
+  """Finds the minimum box surrounding all occurrences of bgr |color|.
+
+  Ignores the alpha channel.
+
+  Args:
+    color: RbgaColor, bounding box color.
+    tolerance: int, per-channel tolerance for the bounding box color.
+
+  Returns:
+    (top, left, width, height), match_count"""
+  return impl.GetBoundingBox(image, color, tolerance)
+
+def Crop(image, left, top, width, height):
+  """Crops the current image down to the specified box."""
+  return impl.Crop(image, left, top, width, height)
+
+def GetColorHistogram(image, ignore_color=None, tolerance=0):
+  """Computes a histogram of the pixel colors in this image.
+  Args:
+    ignore_color: An RgbaColor to exclude from the bucket counts.
+    tolerance: A tolerance for the ignore_color.
+
+  Returns:
+    A ColorHistogram namedtuple with 256 integers in each field: r, g, and b."""
+  return impl.GetColorHistogram(image, ignore_color, tolerance)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/image_util_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/image_util_unittest.py
new file mode 100644
index 0000000..4cb10e6
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/image_util_unittest.py
@@ -0,0 +1,128 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import tempfile
+import unittest
+
+from telemetry.core import util
+from telemetry.util import image_util
+from telemetry.util import rgba_color
+
+# This is a simple base64 encoded 2x2 PNG which contains, in order, a single
+# Red, Yellow, Blue, and Green pixel.
+test_png = """
+ iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91
+ JpzAAAAFklEQVR4Xg3EAQ0AAABAMP1LY3YI7l8l6A
+ T8tgwbJAAAAABJRU5ErkJggg==
+"""
+test_png_path = os.path.join(util.GetUnittestDataDir(), 'test_png.png')
+test_png_2_path = os.path.join(util.GetUnittestDataDir(), 'test_png_2.png')
+
+class ImageUtilTest(unittest.TestCase):
+  def testReadFromBase64Png(self):
+    bmp = image_util.FromBase64Png(test_png)
+
+    self.assertEquals(2, image_util.Width(bmp))
+    self.assertEquals(2, image_util.Height(bmp))
+
+    image_util.GetPixelColor(bmp, 0, 0).AssertIsRGB(255, 0, 0)
+    image_util.GetPixelColor(bmp, 1, 1).AssertIsRGB(0, 255, 0)
+    image_util.GetPixelColor(bmp, 0, 1).AssertIsRGB(0, 0, 255)
+    image_util.GetPixelColor(bmp, 1, 0).AssertIsRGB(255, 255, 0)
+
+  def testReadFromPngFile(self):
+    file_bmp = image_util.FromPngFile(test_png_path)
+
+    self.assertEquals(2, image_util.Width(file_bmp))
+    self.assertEquals(2, image_util.Height(file_bmp))
+
+    image_util.GetPixelColor(file_bmp, 0, 0).AssertIsRGB(255, 0, 0)
+    image_util.GetPixelColor(file_bmp, 1, 1).AssertIsRGB(0, 255, 0)
+    image_util.GetPixelColor(file_bmp, 0, 1).AssertIsRGB(0, 0, 255)
+    image_util.GetPixelColor(file_bmp, 1, 0).AssertIsRGB(255, 255, 0)
+
+  def testWritePngToPngFile(self):
+    orig = image_util.FromPngFile(test_png_path)
+    temp_file = tempfile.NamedTemporaryFile(suffix='.png').name
+    image_util.WritePngFile(orig, temp_file)
+    new_file = image_util.FromPngFile(temp_file)
+    self.assertTrue(image_util.AreEqual(orig, new_file, likely_equal=True))
+
+  def testWritePngWithoutPngSuffixThrows(self):
+    orig = image_util.FromPngFile(test_png_path)
+    temp_file = tempfile.NamedTemporaryFile().name
+    self.assertRaises(AssertionError, image_util.WritePngFile,
+                      orig, temp_file)
+
+  def testWriteCroppedBmpToPngFile(self):
+    pixels = [255, 0, 0, 255, 255, 0, 0, 0, 0,
+              255, 255, 0, 0, 255, 0, 0, 0, 0]
+    orig = image_util.FromRGBPixels(3, 2, pixels)
+    orig = image_util.Crop(orig, 0, 0, 2, 2)
+    temp_file = tempfile.NamedTemporaryFile(suffix='.png').name
+    image_util.WritePngFile(orig, temp_file)
+    new_file = image_util.FromPngFile(temp_file)
+    self.assertTrue(image_util.AreEqual(orig, new_file, likely_equal=True))
+
+  def testIsEqual(self):
+    bmp = image_util.FromBase64Png(test_png)
+    file_bmp = image_util.FromPngFile(test_png_path)
+    self.assertTrue(image_util.AreEqual(bmp, file_bmp, likely_equal=True))
+
+  def testDiff(self):
+    file_bmp = image_util.FromPngFile(test_png_path)
+    file_bmp_2 = image_util.FromPngFile(test_png_2_path)
+
+    diff_bmp = image_util.Diff(file_bmp, file_bmp)
+
+    self.assertEquals(2, image_util.Width(diff_bmp))
+    self.assertEquals(2, image_util.Height(diff_bmp))
+
+    image_util.GetPixelColor(diff_bmp, 0, 0).AssertIsRGB(0, 0, 0)
+    image_util.GetPixelColor(diff_bmp, 1, 1).AssertIsRGB(0, 0, 0)
+    image_util.GetPixelColor(diff_bmp, 0, 1).AssertIsRGB(0, 0, 0)
+    image_util.GetPixelColor(diff_bmp, 1, 0).AssertIsRGB(0, 0, 0)
+
+    diff_bmp = image_util.Diff(file_bmp, file_bmp_2)
+
+    self.assertEquals(3, image_util.Width(diff_bmp))
+    self.assertEquals(3, image_util.Height(diff_bmp))
+
+    image_util.GetPixelColor(diff_bmp, 0, 0).AssertIsRGB(0, 255, 255)
+    image_util.GetPixelColor(diff_bmp, 1, 1).AssertIsRGB(255, 0, 255)
+    image_util.GetPixelColor(diff_bmp, 0, 1).AssertIsRGB(255, 255, 0)
+    image_util.GetPixelColor(diff_bmp, 1, 0).AssertIsRGB(0, 0, 255)
+
+    image_util.GetPixelColor(diff_bmp, 0, 2).AssertIsRGB(255, 255, 255)
+    image_util.GetPixelColor(diff_bmp, 1, 2).AssertIsRGB(255, 255, 255)
+    image_util.GetPixelColor(diff_bmp, 2, 0).AssertIsRGB(255, 255, 255)
+    image_util.GetPixelColor(diff_bmp, 2, 1).AssertIsRGB(255, 255, 255)
+    image_util.GetPixelColor(diff_bmp, 2, 2).AssertIsRGB(255, 255, 255)
+
+  def testGetBoundingBox(self):
+    pixels = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+              0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0,
+              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
+    bmp = image_util.FromRGBPixels(4, 3, pixels)
+    box, count = image_util.GetBoundingBox(bmp, rgba_color.RgbaColor(1, 0, 0))
+    self.assertEquals(box, (1, 1, 2, 1))
+    self.assertEquals(count, 2)
+
+    box, count = image_util.GetBoundingBox(bmp, rgba_color.RgbaColor(0, 1, 0))
+    self.assertEquals(box, None)
+    self.assertEquals(count, 0)
+
+  def testCrop(self):
+    pixels = [0, 0, 0, 1, 0, 0, 2, 0, 0, 3, 0, 0,
+              0, 1, 0, 1, 1, 0, 2, 1, 0, 3, 1, 0,
+              0, 2, 0, 1, 2, 0, 2, 2, 0, 3, 2, 0]
+    bmp = image_util.FromRGBPixels(4, 3, pixels)
+    bmp = image_util.Crop(bmp, 1, 2, 2, 1)
+
+    self.assertEquals(2, image_util.Width(bmp))
+    self.assertEquals(1, image_util.Height(bmp))
+    image_util.GetPixelColor(bmp, 0, 0).AssertIsRGB(1, 2, 0)
+    image_util.GetPixelColor(bmp, 1, 0).AssertIsRGB(2, 2, 0)
+    self.assertEquals(image_util.Pixels(bmp), bytearray([1, 2, 0, 2, 2, 0]))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/js_template.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/js_template.py
new file mode 100644
index 0000000..8e23f5c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/js_template.py
@@ -0,0 +1,69 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import json
+import re
+
+
+RE_REPLACEMENT_FIELD = re.compile(r'{{(?P<field_spec>[^}]*)}}')
+RE_FIELD_IDENTIFIER = re.compile(r'(?P<modifier>[@*])?(?P<name>\w+)$')
+
+
+def RenderValue(value):
+  """Convert a Python value to a string with its JavaScript representation."""
+  return json.dumps(value, sort_keys=True)
+
+
+def Render(template, **kwargs):
+  """Helper method to interpolate Python values into JavaScript snippets.
+
+  Placeholders in the template, field names enclosed in double curly braces,
+  are replaced with the value of the corresponding named argument.
+
+  Prefixing a field name with '*' causes the value, expected to be a
+  sequence of individual values, to be all interpolated and separated by
+  commas.
+
+  Prefixing a field name with '@' causes the value to be inserted literally.
+
+
+  For example:
+
+    js_template.Render(
+      'var {{ @var_name }} = f({{ x }}, {{ *args }});',
+      var_name='foo', x=42, args=('hello', 'there'))
+
+  Returns:
+
+    'var foo = f(42, "hello", "there");'
+
+  Args:
+    template: A string with a JavaScript template, tagged with {{ fields }}
+      to interpolate with values.
+    **kwargs: Values to be interpolated in the template.
+  """
+  unused = set(kwargs)
+
+  def interpolate(m):
+    field_spec = m.group('field_spec').strip()
+    field = RE_FIELD_IDENTIFIER.match(field_spec)
+    if not field:
+      raise KeyError(field_spec)
+    key = field.group('name')
+    value = kwargs[key]
+    unused.discard(key)
+    if field.group('modifier') == '@':
+      if not isinstance(value, str):
+        raise ValueError('Literal value for %s must be a string' % field_spec)
+      return value
+    elif field.group('modifier') == '*':
+      return ', '.join(RenderValue(v) for v in value)
+    else:
+      return RenderValue(value)
+
+  result = RE_REPLACEMENT_FIELD.sub(interpolate, template)
+  if unused:
+    raise TypeError('Unexpected arguments not used in template: %s.' % (
+      ', '.join(repr(str(k)) for k in sorted(unused))))
+  return result
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/js_template_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/js_template_unittest.py
new file mode 100644
index 0000000..b233a8a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/js_template_unittest.py
@@ -0,0 +1,52 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from telemetry.util import js_template
+
+
+class JavaScriptTemplateTest(unittest.TestCase):
+  def testRenderSimple(self):
+    self.assertEquals(
+        js_template.Render(
+            'foo({{ a }}, {{ b }}, {{ c }})',
+            a=42, b='hello', c=['x', 'y']),
+        'foo(42, "hello", ["x", "y"])')
+
+  def testRenderWithSpecialCharts(self):
+    self.assertEquals(
+        js_template.Render(
+            'function(elem) { return elem.find({{ selector }}); }',
+            selector='.r > a[href*="wikipedia"]'),
+        r'function(elem) { return elem.find(".r > a[href*=\"wikipedia\"]"); }')
+
+  def testRenderWithLiteralValues(self):
+    self.assertEquals(
+        js_template.Render(
+            'var {{ @var_name }} = {{ x }} + {{ y }};',
+            var_name='foo', x='bar', y=None),
+        'var foo = "bar" + null;')
+
+  def testRenderWithArgumentExpansion(self):
+    self.assertEquals(
+        js_template.Render(
+            '{{ @f }}({{ *args }})', f='foo', args=(1, 'hi!', None)),
+        'foo(1, "hi!", null)')
+
+  def testRenderRaisesWithUnknownIdentifier(self):
+    with self.assertRaises(KeyError):
+      js_template.Render('foo({{ some_name }})', another_name='bar')
+
+  def testRenderRaisesWithBadIdentifier(self):
+    with self.assertRaises(KeyError):
+      js_template.Render('foo({{ bad identifier name }})', name='bar')
+
+  def testRenderRaisesWithBadLiteralValue(self):
+    with self.assertRaises(ValueError):
+      js_template.Render('function() { {{ @code }} }', code=['foo', 'bar'])
+
+  def testRenderRaisesWithUnusedKeywordArgs(self):
+    with self.assertRaises(TypeError):
+      js_template.Render('foo = {{ x }};', x=4, y=5, timemout=6)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/mac/README b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/mac/README
new file mode 100644
index 0000000..0c5538b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/mac/README
@@ -0,0 +1,2 @@
+This directory contains files needed to run Chrome browser Telemetry tests on
+OSX.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/mac/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/mac/__init__.py
new file mode 100644
index 0000000..4d6aabb
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/mac/__init__.py
@@ -0,0 +1,3 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/mac/determine_if_keychain_entry_is_decryptable.c b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/mac/determine_if_keychain_entry_is_decryptable.c
new file mode 100644
index 0000000..5facb61
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/mac/determine_if_keychain_entry_is_decryptable.c
@@ -0,0 +1,94 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// This program determines whether a specific entry in the default OSX Keychain
+// is decryptable by all applications without a user prompt.
+//
+// This program uses APIs only available on OSX 10.7+.
+//
+// Input format:
+//  determine_if_keychain_entry_is_decryptable [service name] [account name]
+//
+// Return values:
+//   0 - The entry doesn't exist, or the ACLs are correct.
+//   1 - The ACLs are incorrect.
+//   >=2 - Unexpected error.
+//
+// To compile, run: "clang -framework Security -framework CoreFoundation
+//                   -o determine_if_keychain_entry_is_decryptable
+//                   determine_if_keychain_entry_is_decryptable.c"
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <Security/Security.h>
+#include <string.h>
+
+int main(int argc, char* argv[]) {
+  // There must be exactly 2 arguments to the program.
+  if (argc != 3)
+    return 2;
+
+  const char* service_name = argv[1];
+  const char* account_name = argv[2];
+  SecKeychainItemRef item;
+  OSStatus status = SecKeychainFindGenericPassword(NULL, strlen(service_name),
+      service_name, strlen(account_name), account_name, NULL, NULL, &item);
+
+  // There is no keychain item.
+  if (status == errSecItemNotFound)
+    return 0;
+
+  // Unexpected error.
+  if (status != errSecSuccess)
+    return 3;
+
+  SecAccessRef access;
+  status = SecKeychainItemCopyAccess(item, &access);
+
+  // Unexpected error.
+  if (status != errSecSuccess) {
+    CFRelease(access);
+    CFRelease(item);
+    return 4;
+  }
+
+  CFArrayRef acl_list =
+      SecAccessCopyMatchingACLList(access, kSecACLAuthorizationDecrypt);
+
+  for (CFIndex i = 0; i < CFArrayGetCount(acl_list); ++i) {
+    SecACLRef acl = (SecACLRef)CFArrayGetValueAtIndex(acl_list, i);
+
+    CFArrayRef application_list;
+    CFStringRef description;
+    SecKeychainPromptSelector prompt_selector;
+    status = SecACLCopyContents(acl, &application_list, &description,
+                                &prompt_selector);
+
+    // Unexpected error.
+    if (status != errSecSuccess) {
+      CFRelease(acl_list);
+      CFRelease(access);
+      CFRelease(item);
+      return 5;
+    }
+
+    // Check whether this acl gives decryption access to all applications.
+    bool found_correct_acl = (application_list == NULL);
+    CFRelease(description);
+    if (application_list)
+      CFRelease(application_list);
+
+    if (found_correct_acl) {
+      CFRelease(acl_list);
+      CFRelease(access);
+      CFRelease(item);
+      return 0;
+    }
+  }
+
+  // No acl was found that gave decryption access to all applications.
+  CFRelease(acl_list);
+  CFRelease(access);
+  CFRelease(item);
+  return 1;
+}
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/mac/determine_if_keychain_is_locked.c b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/mac/determine_if_keychain_is_locked.c
new file mode 100644
index 0000000..ddf0aff
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/mac/determine_if_keychain_is_locked.c
@@ -0,0 +1,25 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// This program determines whether the default OSX Keychain is unlocked without
+// causing a user interaction prompt.
+// Return values:
+//   0 - The default keychain is unlocked.
+//   1 - The default keychain is locked.
+//   2 - Unexpected error.
+//
+// To compile, run: "clang -framework Security
+//                   -o determine_if_keychain_is_locked
+//                   determine_if_keychain_is_locked.c"
+
+#include <Security/Security.h>
+
+int main() {
+  SecKeychainStatus keychain_status;
+  OSStatus os_status = SecKeychainGetStatus(NULL, &keychain_status);
+  if (os_status != errSecSuccess)
+    return 2;
+
+  return (keychain_status & kSecUnlockStateStatus) ? 0 : 1;
+}
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/mac/keychain_helper.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/mac/keychain_helper.py
new file mode 100644
index 0000000..40219ea
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/mac/keychain_helper.py
@@ -0,0 +1,65 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import subprocess
+
+from telemetry.internal.util import binary_manager
+from telemetry.core import platform
+from telemetry.core import os_version
+
+def _PathForExecutable(executable_name):
+  """Fetches the executable from cloud storage, and returns its path."""
+  arch_name = platform.GetHostPlatform().GetArchName()
+  return binary_manager.FetchPath(executable_name, arch_name, 'mac')
+
+def IsKeychainLocked():
+  """
+  Returns True if the keychain is locked, or if there is an error determining
+  the keychain state.
+  """
+  path = _PathForExecutable('determine_if_keychain_is_locked')
+
+  child = subprocess.Popen(path, stdout=subprocess.PIPE)
+  child.communicate()
+  return child.returncode != 0
+
+def DoesKeychainHaveTimeout():
+  """
+  Returns True if the keychain will lock itself have a period of time.
+
+  This method will trigger a blocking, modal dialog if the keychain is
+  locked.
+  """
+  command = ("/usr/bin/security", "show-keychain-info")
+  child = subprocess.Popen(command, stderr=subprocess.PIPE)
+  stderr = child.communicate()[1]
+  return "no-timeout" not in stderr
+
+def _IsKeychainConfiguredForBots(service_name, account_name):
+  """
+  Returns True if the keychain entry associated with |service_name| and
+  |account_name| is correctly configured for running telemetry tests on bots.
+
+  This method will trigger a blocking, modal dialog if the keychain is
+  locked.
+  """
+  # The executable requires OSX 10.7+ APIs.
+  if (platform.GetHostPlatform().GetOSVersionName() <
+      os_version.LION):
+    return False
+
+  path = _PathForExecutable('determine_if_keychain_entry_is_decryptable')
+
+  command = (path, service_name, account_name)
+  child = subprocess.Popen(command)
+  child.communicate()
+  return child.returncode == 0
+
+def IsKeychainConfiguredForBotsWithChrome():
+  return _IsKeychainConfiguredForBots("Chrome Safe Storage",
+      "Chrome")
+
+def IsKeychainConfiguredForBotsWithChromium():
+  return _IsKeychainConfiguredForBots("Chromium Safe Storage",
+      "Chromium")
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/matching.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/matching.py
new file mode 100644
index 0000000..1ecd182
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/matching.py
@@ -0,0 +1,27 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import difflib
+
+
+def GetMostLikelyMatchedObject(objects, target_name,
+                               name_func=lambda x: x,
+                               matched_score_threshold=0.4):
+  """Matches objects whose names are most likely matched with target.
+
+  Args:
+    objects: list of objects to match.
+    target_name: name to match.
+    name_func: function to get object name to match. Default bypass.
+    matched_score_threshold: threshold of likelihood to match.
+
+  Returns:
+    A list of objects whose names are likely target_name.
+  """
+  def MatchScore(obj):
+    return difflib.SequenceMatcher(
+        isjunk=None, a=name_func(obj), b=target_name).ratio()
+  object_score = [(o, MatchScore(o)) for o in objects]
+  result = [x for x in object_score if x[1] > matched_score_threshold]
+  return [x[0] for x in sorted(result, key=lambda r: r[1], reverse=True)]
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/matching_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/matching_unittest.py
new file mode 100644
index 0000000..a43c044
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/matching_unittest.py
@@ -0,0 +1,47 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from telemetry.util import matching
+
+
+class BenchmarkFoo(object):
+  """ Benchmark Foo for testing."""
+  @classmethod
+  def Name(cls):
+    return 'FooBenchmark'
+
+
+class BenchmarkBar(object):
+  """ Benchmark Bar for testing long description line."""
+  @classmethod
+  def Name(cls):
+    return 'BarBenchmarkkkkk'
+
+
+class UnusualBenchmark(object):
+  @classmethod
+  def Name(cls):
+    return 'I have a very unusual name'
+
+
+class CommandLineUnittest(unittest.TestCase):
+  def testGetMostLikelyMatchedObject(self):
+    # Test moved from telemetry/benchmark_runner_unittest.py
+    all_benchmarks = [BenchmarkFoo, BenchmarkBar, UnusualBenchmark]
+    self.assertEquals(
+        [BenchmarkFoo, BenchmarkBar],
+        matching.GetMostLikelyMatchedObject(
+            all_benchmarks, 'BenchmarkFooz', name_func=lambda x: x.Name()))
+
+    self.assertEquals(
+        [BenchmarkBar, BenchmarkFoo],
+        matching.GetMostLikelyMatchedObject(
+            all_benchmarks, 'BarBenchmark', name_func=lambda x: x.Name()))
+
+    self.assertEquals(
+        [UnusualBenchmark],
+        matching.GetMostLikelyMatchedObject(
+            all_benchmarks, 'unusual', name_func=lambda x: x.Name()))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/perf_result_data_type.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/perf_result_data_type.py
new file mode 100644
index 0000000..dbaf794
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/perf_result_data_type.py
@@ -0,0 +1,20 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+DEFAULT = 'default'
+UNIMPORTANT = 'unimportant'
+HISTOGRAM = 'histogram'
+UNIMPORTANT_HISTOGRAM = 'unimportant-histogram'
+INFORMATIONAL = 'informational'
+
+ALL_TYPES = [DEFAULT, UNIMPORTANT, HISTOGRAM, UNIMPORTANT_HISTOGRAM,
+             INFORMATIONAL]
+
+
+def IsValidType(datatype):
+  return datatype in ALL_TYPES
+
+
+def IsHistogram(datatype):
+  return datatype == HISTOGRAM or datatype == UNIMPORTANT_HISTOGRAM
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/perf_tests_helper.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/perf_tests_helper.py
new file mode 100644
index 0000000..aaf51be
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/perf_tests_helper.py
@@ -0,0 +1,14 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+from telemetry.util import perf_tests_results_helper
+
+
+FlattenList = \
+    perf_tests_results_helper.FlattenList
+GeomMeanAndStdDevFromHistogram = \
+    perf_tests_results_helper.GeomMeanAndStdDevFromHistogram
+PrintPerfResult = \
+    perf_tests_results_helper.PrintPerfResult
+PrintPages = \
+    perf_tests_results_helper.PrintPages
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/perf_tests_results_helper.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/perf_tests_results_helper.py
new file mode 100644
index 0000000..72ccfb0
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/perf_tests_results_helper.py
@@ -0,0 +1,165 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import re
+import sys
+
+import json
+import math
+
+from telemetry.util import perf_result_data_type
+
+
+# Mapping from result type to test output
+RESULT_TYPES = {perf_result_data_type.UNIMPORTANT: 'RESULT ',
+                perf_result_data_type.DEFAULT: '*RESULT ',
+                perf_result_data_type.INFORMATIONAL: '',
+                perf_result_data_type.UNIMPORTANT_HISTOGRAM: 'HISTOGRAM ',
+                perf_result_data_type.HISTOGRAM: '*HISTOGRAM '}
+
+
+def _EscapePerfResult(s):
+  """Escapes |s| for use in a perf result."""
+  return re.sub(r'[\:|=/#&,]', '_', s)
+
+
+def FlattenList(values):
+  """Returns a simple list without sub-lists."""
+  ret = []
+  for entry in values:
+    if isinstance(entry, list):
+      ret.extend(FlattenList(entry))
+    else:
+      ret.append(entry)
+  return ret
+
+
+def GeomMeanAndStdDevFromHistogram(histogram_json):
+  histogram = json.loads(histogram_json)
+  # Handle empty histograms gracefully.
+  if not 'buckets' in histogram:
+    return 0.0, 0.0
+  count = 0
+  sum_of_logs = 0
+  for bucket in histogram['buckets']:
+    if 'high' in bucket:
+      bucket['mean'] = (bucket['low'] + bucket['high']) / 2.0
+    else:
+      bucket['mean'] = bucket['low']
+    if bucket['mean'] > 0:
+      sum_of_logs += math.log(bucket['mean']) * bucket['count']
+      count += bucket['count']
+
+  if count == 0:
+    return 0.0, 0.0
+
+  sum_of_squares = 0
+  geom_mean = math.exp(sum_of_logs / count)
+  for bucket in histogram['buckets']:
+    if bucket['mean'] > 0:
+      sum_of_squares += (bucket['mean'] - geom_mean) ** 2 * bucket['count']
+  return geom_mean, math.sqrt(sum_of_squares / count)
+
+
+def _ValueToString(v):
+  # Special case for floats so we don't print using scientific notation.
+  if isinstance(v, float):
+    return '%f' % v
+  else:
+    return str(v)
+
+
+def _MeanAndStdDevFromList(values):
+  avg = None
+  sd = None
+  if len(values) > 1:
+    try:
+      value = '[%s]' % ','.join([_ValueToString(v) for v in values])
+      avg = sum([float(v) for v in values]) / len(values)
+      sqdiffs = [(float(v) - avg) ** 2 for v in values]
+      variance = sum(sqdiffs) / (len(values) - 1)
+      sd = math.sqrt(variance)
+    except ValueError:
+      value = ', '.join(values)
+  else:
+    value = values[0]
+  return value, avg, sd
+
+
+def PrintPages(page_list):
+  """Prints list of pages to stdout in the format required by perf tests."""
+  print 'Pages: [%s]' % ','.join([_EscapePerfResult(p) for p in page_list])
+
+
+def PrintPerfResult(measurement, trace, values, units,
+                    result_type=perf_result_data_type.DEFAULT,
+                    print_to_stdout=True):
+  """Prints numerical data to stdout in the format required by perf tests.
+
+  The string args may be empty but they must not contain any colons (:) or
+  equals signs (=).
+  This is parsed by the buildbot using:
+  http://src.chromium.org/viewvc/chrome/trunk/tools/build/scripts/slave/process_log_utils.py
+
+  Args:
+    measurement: A description of the quantity being measured, e.g. "vm_peak".
+        On the dashboard, this maps to a particular graph. Mandatory.
+    trace: A description of the particular data point, e.g. "reference".
+        On the dashboard, this maps to a particular "line" in the graph.
+        Mandatory.
+    values: A list of numeric measured values. An N-dimensional list will be
+        flattened and treated as a simple list.
+    units: A description of the units of measure, e.g. "bytes".
+    result_type: Accepts values of perf_result_data_type.ALL_TYPES.
+    print_to_stdout: If True, prints the output in stdout instead of returning
+        the output to caller.
+
+    Returns:
+      String of the formated perf result.
+  """
+  assert perf_result_data_type.IsValidType(result_type), \
+         'result type: %s is invalid' % result_type
+
+  trace_name = _EscapePerfResult(trace)
+
+  if (result_type == perf_result_data_type.UNIMPORTANT or
+      result_type == perf_result_data_type.DEFAULT or
+      result_type == perf_result_data_type.INFORMATIONAL):
+    assert isinstance(values, list)
+    assert '/' not in measurement
+    flattened_values = FlattenList(values)
+    assert len(flattened_values)
+    value, avg, sd = _MeanAndStdDevFromList(flattened_values)
+    output = '%s%s: %s%s%s %s' % (
+        RESULT_TYPES[result_type],
+        _EscapePerfResult(measurement),
+        trace_name,
+        # Do not show equal sign if the trace is empty. Usually it happens when
+        # measurement is enough clear to describe the result.
+        '= ' if trace_name else '',
+        value,
+        units)
+  else:
+    assert perf_result_data_type.IsHistogram(result_type)
+    assert isinstance(values, list)
+    # The histograms can only be printed individually, there's no computation
+    # across different histograms.
+    assert len(values) == 1
+    value = values[0]
+    output = '%s%s: %s= %s %s' % (
+        RESULT_TYPES[result_type],
+        _EscapePerfResult(measurement),
+        trace_name,
+        value,
+        units)
+    avg, sd = GeomMeanAndStdDevFromHistogram(value)
+
+  if avg:
+    output += '\nAvg %s: %f%s' % (measurement, avg, units)
+  if sd:
+    output += '\nSd  %s: %f%s' % (measurement, sd, units)
+  if print_to_stdout:
+    print output
+    sys.stdout.flush()
+  return output
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/process_statistic_timeline_data.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/process_statistic_timeline_data.py
new file mode 100644
index 0000000..589e8d3
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/process_statistic_timeline_data.py
@@ -0,0 +1,58 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+class ProcessStatisticTimelineData(object):
+  """Holds value of a stat for one or more processes.
+
+  This object can hold a value for more than one pid by adding another
+  object."""
+
+  def __init__(self, pid, value):
+    super(ProcessStatisticTimelineData, self).__init__()
+    assert value >= 0
+    self._value_by_pid = {pid: value}
+
+  def __sub__(self, other):
+    """The results of subtraction is an object holding only the pids contained
+    in |self|.
+
+    The motivation is that some processes may have died between two consecutive
+    measurements. The desired behavior is to only make calculations based on
+    the processes that are alive at the end of the second measurement."""
+    # pylint: disable=protected-access
+    ret = self.__class__(0, 0)
+    my_dict = self._value_by_pid
+
+    ret._value_by_pid = (
+        {k: my_dict[k] - other._value_by_pid.get(k, 0) for
+            k in my_dict.keys()})
+    return ret
+
+  def __add__(self, other):
+    """The result contains pids from both |self| and |other|, if duplicate
+    pids are found between objects, an error will occur. """
+    # pylint: disable=protected-access
+    intersecting_pids = (set(self._value_by_pid.keys()) &
+        set(other._value_by_pid.keys()))
+    assert len(intersecting_pids) == 0
+
+    ret = self.__class__(0, 0)
+    ret._value_by_pid = {}
+    ret._value_by_pid.update(self._value_by_pid)
+    ret._value_by_pid.update(other._value_by_pid)
+    return ret
+
+  @property
+  def value_by_pid(self):
+    return self._value_by_pid
+
+  def total_sum(self):
+    """Returns the sum of all values contained by this object. """
+    return sum(self._value_by_pid.values())
+
+
+class IdleWakeupTimelineData(ProcessStatisticTimelineData):
+  """A ProcessStatisticTimelineData to hold idle wakeups."""
+  pass
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/process_statistic_timeline_data_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/process_statistic_timeline_data_unittest.py
new file mode 100644
index 0000000..98f0346
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/process_statistic_timeline_data_unittest.py
@@ -0,0 +1,47 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from telemetry.util import process_statistic_timeline_data
+
+
+class ProcessStatisticTimelineDataTest(unittest.TestCase):
+
+  def testProcessStatisticValueMath(self):
+    pid1 = 1
+    pid2 = 2
+
+    a = process_statistic_timeline_data.ProcessStatisticTimelineData(pid1, 5)
+    b = process_statistic_timeline_data.ProcessStatisticTimelineData(pid2, 1)
+    c = process_statistic_timeline_data.ProcessStatisticTimelineData(pid1, 1)
+
+    # Test addition.
+    addition_result = (a + b).value_by_pid
+    self.assertEquals(5, addition_result[pid1])
+    self.assertEquals(1, addition_result[pid2])
+    self.assertEquals(2, len(addition_result.keys()))
+
+    # Test subtraction.
+    subtraction_result = ((a + b) - c).value_by_pid
+    self.assertEquals(4, subtraction_result[pid1])
+    self.assertEquals(1, subtraction_result[pid2])
+    self.assertEquals(2, len(subtraction_result.keys()))
+
+    # Test subtraction with a pid that exists only in rhs.
+    subtraction_results1 = (a - (b + c)).value_by_pid
+    self.assertEquals(4, subtraction_results1[pid1])
+    self.assertEquals(1, len(subtraction_results1.keys()))
+
+    # Test calculation of total sum.
+    self.assertEquals(6, (a + b).total_sum())
+
+  def testProcessStatisticValueSummary(self):
+    pid1 = 1
+    pid2 = 2
+
+    a = process_statistic_timeline_data.ProcessStatisticTimelineData(pid1, 1)
+    b = process_statistic_timeline_data.ProcessStatisticTimelineData(pid2, 99)
+    c = a + b
+    self.assertEquals(100, c.total_sum())
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/rgba_color.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/rgba_color.py
new file mode 100644
index 0000000..84e0235
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/rgba_color.py
@@ -0,0 +1,33 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import collections
+
+class RgbaColor(collections.namedtuple('RgbaColor', ['r', 'g', 'b', 'a'])):
+  """Encapsulates an RGBA color retrieved from an image."""
+  def __new__(cls, r, g, b, a=255):
+    return super(RgbaColor, cls).__new__(cls, r, g, b, a)
+
+  def __int__(self):
+    return (self.r << 16) | (self.g << 8) | self.b
+
+  def IsEqual(self, expected_color, tolerance=0):
+    """Verifies that the color is within a given tolerance of
+    the expected color."""
+    r_diff = abs(self.r - expected_color.r)
+    g_diff = abs(self.g - expected_color.g)
+    b_diff = abs(self.b - expected_color.b)
+    a_diff = abs(self.a - expected_color.a)
+    return (r_diff <= tolerance and g_diff <= tolerance
+        and b_diff <= tolerance and a_diff <= tolerance)
+
+  def AssertIsRGB(self, r, g, b, tolerance=0):
+    assert self.IsEqual(RgbaColor(r, g, b), tolerance)
+
+  def AssertIsRGBA(self, r, g, b, a, tolerance=0):
+    assert self.IsEqual(RgbaColor(r, g, b, a), tolerance)
+
+
+WEB_PAGE_TEST_ORANGE = RgbaColor(222, 100, 13)
+WHITE = RgbaColor(255, 255, 255)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/screenshot.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/screenshot.py
new file mode 100644
index 0000000..bdd3014
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/screenshot.py
@@ -0,0 +1,90 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import datetime
+import logging
+import os
+import random
+import tempfile
+
+from py_utils import cloud_storage  # pylint: disable=import-error
+from telemetry.util import image_util
+from telemetry.internal.util import file_handle
+
+
+def TryCaptureScreenShot(platform, tab=None):
+  """ If the platform or tab supports screenshot, attempt to take a screenshot
+  of the current browser.
+
+  Args:
+    platform: current platform
+    tab: browser tab if available
+
+  Returns:
+    file handle of the tempoerary file path for the screenshot if
+    present, None otherwise.
+  """
+  try:
+    # TODO(nednguyen): once all platforms support taking screenshot,
+    # remove the tab checking logic and consider moving this to story_runner.
+    # (crbug.com/369490)
+    if platform.CanTakeScreenshot():
+      tf = tempfile.NamedTemporaryFile(delete=False, suffix='.png')
+      tf.close()
+      platform.TakeScreenshot(tf.name)
+      return file_handle.FromTempFile(tf)
+    elif tab and tab.IsAlive() and tab.screenshot_supported:
+      tf = tempfile.NamedTemporaryFile(delete=False, suffix='.png')
+      tf.close()
+      image = tab.Screenshot()
+      image_util.WritePngFile(image, tf.name)
+      return file_handle.FromTempFile(tf)
+    else:
+      logging.warning(
+          'Either tab has crashed or browser does not support taking tab '
+          'screenshot. Skip taking screenshot on failure.')
+      return None
+  except Exception as e:
+    logging.warning('Exception when trying to capture screenshot: %s', repr(e))
+    return None
+
+
+def TryCaptureScreenShotAndUploadToCloudStorage(platform, tab=None):
+  """ If the platform or tab supports screenshot, attempt to take a screenshot
+  of the current browser.  If present it uploads this local path to cloud
+  storage and returns the URL of the cloud storage path.
+
+  Args:
+    platform: current platform
+    tab: browser tab if available
+
+  Returns:
+    url of the cloud storage path if screenshot is present, None otherwise
+  """
+  fh = TryCaptureScreenShot(platform, tab)
+  if fh is not None:
+    return _UploadScreenShotToCloudStorage(fh)
+
+  return None
+
+def _GenerateRemotePath(fh):
+  return ('browser-screenshot_%s-%s%-d%s' % (
+          fh.id,
+          datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S'),
+          random.randint(1, 100000),
+          fh.extension))
+
+def _UploadScreenShotToCloudStorage(fh):
+  """ Upload the given screenshot image to cloud storage and return the
+    cloud storage url if successful.
+  """
+  try:
+    return cloud_storage.Insert(cloud_storage.TELEMETRY_OUTPUT,
+                                _GenerateRemotePath(fh), fh.GetAbsPath())
+  except cloud_storage.CloudStorageError as err:
+    logging.error('Cloud storage error while trying to upload screenshot: %s'
+                  % repr(err))
+    return '<Missing link>'
+  finally:  # Must clean up screenshot file if exists.
+    os.remove(fh.GetAbsPath())
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/screenshot_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/screenshot_unittest.py
new file mode 100644
index 0000000..1bc17e8
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/screenshot_unittest.py
@@ -0,0 +1,56 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import mock
+import tempfile
+import unittest
+import os
+
+from py_utils import cloud_storage  # pylint: disable=import-error
+from telemetry.testing import fakes
+from telemetry.internal.util import file_handle
+from telemetry.util import image_util
+from telemetry.util import screenshot
+
+class ScreenshotUtilTests(unittest.TestCase):
+
+  def setUp(self):
+    self.options = fakes.CreateBrowserFinderOptions()
+
+  def testScreenShotTakenSupportedPlatform(self):
+    fake_platform = self.options.fake_possible_browser.returned_browser.platform
+    expected_png_base64 = """
+      iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91
+      JpzAAAAFklEQVR4Xg3EAQ0AAABAMP1LY3YI7l8l6A
+      T8tgwbJAAAAABJRU5ErkJggg==
+      """
+    fake_platform.screenshot_png_data = expected_png_base64
+
+    fh = screenshot.TryCaptureScreenShot(fake_platform, None)
+    screenshot_file_path = fh.GetAbsPath()
+    try:
+      actual_screenshot_img = image_util.FromPngFile(screenshot_file_path)
+      self.assertTrue(image_util.AreEqual(
+                      image_util.FromBase64Png(expected_png_base64),
+                      actual_screenshot_img))
+    finally:  # Must clean up screenshot file if exists.
+      os.remove(screenshot_file_path)
+
+  def testUploadScreenshotToCloudStorage(self):
+    tf = tempfile.NamedTemporaryFile(
+        suffix='.png', delete=False)
+    fh1 = file_handle.FromTempFile(tf)
+
+    local_path = '123456abcdefg.png'
+
+    with mock.patch('py_utils.cloud_storage.Insert') as mock_insert:
+      with mock.patch('telemetry.util.screenshot._GenerateRemotePath',
+        return_value=local_path):
+
+        url = screenshot._UploadScreenShotToCloudStorage(fh1)
+        mock_insert.assert_called_with(
+            cloud_storage.TELEMETRY_OUTPUT,
+            local_path,
+            fh1.GetAbsPath())
+        self.assertTrue(url is not None)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/statistics.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/statistics.py
new file mode 100644
index 0000000..381c6b4
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/statistics.py
@@ -0,0 +1,346 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""A collection of statistical utility functions to be used by metrics."""
+
+import math
+
+
+def Clamp(value, low=0.0, high=1.0):
+  """Clamp a value between some low and high value."""
+  return min(max(value, low), high)
+
+
+def NormalizeSamples(samples):
+  """Sorts the samples, and map them linearly to the range [0,1].
+
+  They're mapped such that for the N samples, the first sample is 0.5/N and the
+  last sample is (N-0.5)/N.
+
+  Background: The discrepancy of the sample set i/(N-1); i=0, ..., N-1 is 2/N,
+  twice the discrepancy of the sample set (i+1/2)/N; i=0, ..., N-1. In our case
+  we don't want to distinguish between these two cases, as our original domain
+  is not bounded (it is for Monte Carlo integration, where discrepancy was
+  first used).
+  """
+  if not samples:
+    return samples, 1.0
+  samples = sorted(samples)
+  low = min(samples)
+  high = max(samples)
+  new_low = 0.5 / len(samples)
+  new_high = (len(samples)-0.5) / len(samples)
+  if high-low == 0.0:
+    return [0.5] * len(samples), 1.0
+  scale = (new_high - new_low) / (high - low)
+  for i in xrange(0, len(samples)):
+    samples[i] = float(samples[i] - low) * scale + new_low
+  return samples, scale
+
+
+def Discrepancy(samples, location_count=None):
+  """Computes the discrepancy of a set of 1D samples from the interval [0,1].
+
+  The samples must be sorted. We define the discrepancy of an empty set
+  of samples to be zero.
+
+  http://en.wikipedia.org/wiki/Low-discrepancy_sequence
+  http://mathworld.wolfram.com/Discrepancy.html
+  """
+  if not samples:
+    return 0.0
+
+  max_local_discrepancy = 0
+  inv_sample_count = 1.0 / len(samples)
+  locations = []
+  # For each location, stores the number of samples less than that location.
+  count_less = []
+  # For each location, stores the number of samples less than or equal to that
+  # location.
+  count_less_equal = []
+
+  if location_count:
+    # Generate list of equally spaced locations.
+    sample_index = 0
+    for i in xrange(0, int(location_count)):
+      location = float(i) / (location_count-1)
+      locations.append(location)
+      while sample_index < len(samples) and samples[sample_index] < location:
+        sample_index += 1
+      count_less.append(sample_index)
+      while  sample_index < len(samples) and samples[sample_index] <= location:
+        sample_index += 1
+      count_less_equal.append(sample_index)
+  else:
+    # Populate locations with sample positions. Append 0 and 1 if necessary.
+    if samples[0] > 0.0:
+      locations.append(0.0)
+      count_less.append(0)
+      count_less_equal.append(0)
+    for i in xrange(0, len(samples)):
+      locations.append(samples[i])
+      count_less.append(i)
+      count_less_equal.append(i+1)
+    if samples[-1] < 1.0:
+      locations.append(1.0)
+      count_less.append(len(samples))
+      count_less_equal.append(len(samples))
+
+  # Compute discrepancy as max(overshoot, -undershoot), where
+  # overshoot = max(count_closed(i, j)/N - length(i, j)) for all i < j,
+  # undershoot = min(count_open(i, j)/N - length(i, j)) for all i < j,
+  # N = len(samples),
+  # count_closed(i, j) is the number of points between i and j including ends,
+  # count_open(i, j) is the number of points between i and j excluding ends,
+  # length(i, j) is locations[i] - locations[j].
+
+  # The following algorithm is modification of Kadane's algorithm,
+  # see https://en.wikipedia.org/wiki/Maximum_subarray_problem.
+
+  # The maximum of (count_closed(k, i-1)/N - length(k, i-1)) for any k < i-1.
+  max_diff = 0
+  # The minimum of (count_open(k, i-1)/N - length(k, i-1)) for any k < i-1.
+  min_diff = 0
+  for i in xrange(1, len(locations)):
+    length = locations[i] - locations[i - 1]
+    count_closed = count_less_equal[i] - count_less[i - 1]
+    count_open = count_less[i] - count_less_equal[i - 1]
+    # Number of points that are added if we extend a closed range that
+    # ends at location (i-1).
+    count_closed_increment = count_less_equal[i] - count_less_equal[i - 1]
+    # Number of points that are added if we extend an open range that
+    # ends at location (i-1).
+    count_open_increment = count_less[i] - count_less[i - 1]
+
+    # Either extend the previous optimal range or start a new one.
+    max_diff = max(
+        float(count_closed_increment) * inv_sample_count - length + max_diff,
+        float(count_closed) * inv_sample_count - length)
+    min_diff = min(
+        float(count_open_increment) * inv_sample_count - length + min_diff,
+        float(count_open) * inv_sample_count - length)
+
+    max_local_discrepancy = max(max_diff, -min_diff, max_local_discrepancy)
+  return max_local_discrepancy
+
+
+def TimestampsDiscrepancy(timestamps, absolute=True,
+                          location_count=None):
+  """A discrepancy based metric for measuring timestamp jank.
+
+  TimestampsDiscrepancy quantifies the largest area of jank observed in a series
+  of timestamps.  Note that this is different from metrics based on the
+  max_time_interval. For example, the time stamp series A = [0,1,2,3,5,6] and
+  B = [0,1,2,3,5,7] have the same max_time_interval = 2, but
+  Discrepancy(B) > Discrepancy(A).
+
+  Two variants of discrepancy can be computed:
+
+  Relative discrepancy is following the original definition of
+  discrepancy. It characterized the largest area of jank, relative to the
+  duration of the entire time stamp series.  We normalize the raw results,
+  because the best case discrepancy for a set of N samples is 1/N (for
+  equally spaced samples), and we want our metric to report 0.0 in that
+  case.
+
+  Absolute discrepancy also characterizes the largest area of jank, but its
+  value wouldn't change (except for imprecisions due to a low
+  |interval_multiplier|) if additional 'good' intervals were added to an
+  exisiting list of time stamps.  Its range is [0,inf] and the unit is
+  milliseconds.
+
+  The time stamp series C = [0,2,3,4] and D = [0,2,3,4,5] have the same
+  absolute discrepancy, but D has lower relative discrepancy than C.
+
+  |timestamps| may be a list of lists S = [S_1, S_2, ..., S_N], where each
+  S_i is a time stamp series. In that case, the discrepancy D(S) is:
+  D(S) = max(D(S_1), D(S_2), ..., D(S_N))
+  """
+  if not timestamps:
+    return 0.0
+
+  if isinstance(timestamps[0], list):
+    range_discrepancies = [TimestampsDiscrepancy(r) for r in timestamps]
+    return max(range_discrepancies)
+
+  samples, sample_scale = NormalizeSamples(timestamps)
+  discrepancy = Discrepancy(samples, location_count)
+  inv_sample_count = 1.0 / len(samples)
+  if absolute:
+    # Compute absolute discrepancy
+    discrepancy /= sample_scale
+  else:
+    # Compute relative discrepancy
+    discrepancy = Clamp((discrepancy-inv_sample_count) / (1.0-inv_sample_count))
+  return discrepancy
+
+
+def DurationsDiscrepancy(durations, absolute=True,
+                         location_count=None):
+  """A discrepancy based metric for measuring duration jank.
+
+  DurationsDiscrepancy computes a jank metric which measures how irregular a
+  given sequence of intervals is. In order to minimize jank, each duration
+  should be equally long. This is similar to how timestamp jank works,
+  and we therefore reuse the timestamp discrepancy function above to compute a
+  similar duration discrepancy number.
+
+  Because timestamp discrepancy is defined in terms of timestamps, we first
+  convert the list of durations to monotonically increasing timestamps.
+
+  Args:
+    durations: List of interval lengths in milliseconds.
+    absolute: See TimestampsDiscrepancy.
+    interval_multiplier: See TimestampsDiscrepancy.
+  """
+  if not durations:
+    return 0.0
+
+  timestamps = reduce(lambda x, y: x + [x[-1] + y], durations, [0])
+  return TimestampsDiscrepancy(timestamps, absolute, location_count)
+
+
+def ArithmeticMean(data):
+  """Calculates arithmetic mean.
+
+  Args:
+    data: A list of samples.
+
+  Returns:
+    The arithmetic mean value, or 0 if the list is empty.
+  """
+  numerator_total = Total(data)
+  denominator_total = Total(len(data))
+  return DivideIfPossibleOrZero(numerator_total, denominator_total)
+
+
+def StandardDeviation(data):
+  """Calculates the standard deviation.
+
+  Args:
+    data: A list of samples.
+
+  Returns:
+    The standard deviation of the samples provided.
+  """
+  if len(data) == 1:
+    return 0.0
+
+  mean = ArithmeticMean(data)
+  variances = [float(x) - mean for x in data]
+  variances = [x * x for x in variances]
+  std_dev = math.sqrt(ArithmeticMean(variances))
+
+  return std_dev
+
+
+def TrapezoidalRule(data, dx):
+  """ Calculate the integral according to the trapezoidal rule
+
+  TrapezoidalRule approximates the definite integral of f from a to b by
+  the composite trapezoidal rule, using n subintervals.
+  http://en.wikipedia.org/wiki/Trapezoidal_rule#Uniform_grid
+
+  Args:
+    data: A list of samples
+    dx: The uniform distance along the x axis between any two samples
+
+  Returns:
+    The area under the curve defined by the samples and the uniform distance
+    according to the trapezoidal rule.
+  """
+
+  n = len(data) - 1
+  s = data[0] + data[n]
+
+  if n == 0:
+    return 0.0
+
+  for i in range(1, n):
+    s += 2 * data[i]
+
+  return s * dx / 2.0
+
+def Total(data):
+  """Returns the float value of a number or the sum of a list."""
+  if type(data) == float:
+    total = data
+  elif type(data) == int:
+    total = float(data)
+  elif type(data) == list:
+    total = float(sum(data))
+  else:
+    raise TypeError
+  return total
+
+
+def DivideIfPossibleOrZero(numerator, denominator):
+  """Returns the quotient, or zero if the denominator is zero."""
+  return (float(numerator) / float(denominator)) if denominator else 0
+
+
+def GeneralizedMean(values, exponent):
+  """See http://en.wikipedia.org/wiki/Generalized_mean"""
+  if not values:
+    return 0.0
+  sum_of_powers = 0.0
+  for v in values:
+    sum_of_powers += v ** exponent
+  return (sum_of_powers / len(values)) ** (1.0/exponent)
+
+
+def Median(values):
+  """Gets the median of a list of values."""
+  return Percentile(values, 50)
+
+
+def Percentile(values, percentile):
+  """Calculates the value below which a given percentage of values fall.
+
+  For example, if 17% of the values are less than 5.0, then 5.0 is the 17th
+  percentile for this set of values. When the percentage doesn't exactly
+  match a rank in the list of values, the percentile is computed using linear
+  interpolation between closest ranks.
+
+  Args:
+    values: A list of numerical values.
+    percentile: A number between 0 and 100.
+
+  Returns:
+    The Nth percentile for the list of values, where N is the given percentage.
+  """
+  if not values:
+    return 0.0
+  sorted_values = sorted(values)
+  n = len(values)
+  percentile /= 100.0
+  if percentile <= 0.5 / n:
+    return sorted_values[0]
+  elif percentile >= (n - 0.5) / n:
+    return sorted_values[-1]
+  else:
+    floor_index = int(math.floor(n * percentile -  0.5))
+    floor_value = sorted_values[floor_index]
+    ceil_value = sorted_values[floor_index+1]
+    alpha = n * percentile - 0.5 - floor_index
+    return floor_value + alpha * (ceil_value - floor_value)
+
+
+def GeometricMean(values):
+  """Compute a rounded geometric mean from an array of values."""
+  if not values:
+    return None
+  # To avoid infinite value errors, make sure no value is less than 0.001.
+  new_values = []
+  for value in values:
+    if value > 0.001:
+      new_values.append(value)
+    else:
+      new_values.append(0.001)
+  # Compute the sum of the log of the values.
+  log_sum = sum(map(math.log, new_values))
+  # Raise e to that sum over the number of values.
+  mean = math.pow(math.e, (log_sum / len(new_values)))
+  # Return the rounded mean.
+  return int(round(mean))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/statistics_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/statistics_unittest.py
new file mode 100644
index 0000000..8468a16
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/statistics_unittest.py
@@ -0,0 +1,219 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import math
+import random
+import unittest
+
+from telemetry.util import statistics
+
+
+def Relax(samples, iterations=10):
+  """Lloyd relaxation in 1D.
+
+  Keeps the position of the first and last sample.
+  """
+  for _ in xrange(0, iterations):
+    voronoi_boundaries = []
+    for i in xrange(1, len(samples)):
+      voronoi_boundaries.append((samples[i] + samples[i-1]) * 0.5)
+
+    relaxed_samples = []
+    relaxed_samples.append(samples[0])
+    for i in xrange(1, len(samples)-1):
+      relaxed_samples.append(
+          (voronoi_boundaries[i-1] + voronoi_boundaries[i]) * 0.5)
+    relaxed_samples.append(samples[-1])
+    samples = relaxed_samples
+  return samples
+
+def CreateRandomSamples(num_samples):
+  samples = []
+  position = 0.0
+  samples.append(position)
+  for _ in xrange(1, num_samples):
+    position += random.random()
+    samples.append(position)
+  return samples
+
+class StatisticsUnitTest(unittest.TestCase):
+
+  def testNormalizeSamples(self):
+    samples = []
+    normalized_samples, scale = statistics.NormalizeSamples(samples)
+    self.assertEquals(normalized_samples, [])
+    self.assertEquals(scale, 1.0)
+
+    samples = [0.0, 0.0]
+    normalized_samples, scale = statistics.NormalizeSamples(samples)
+    self.assertEquals(normalized_samples, [0.5, 0.5])
+    self.assertEquals(scale, 1.0)
+
+    samples = [0.0, 1.0/3.0, 2.0/3.0, 1.0]
+    normalized_samples, scale = statistics.NormalizeSamples(samples)
+    self.assertEquals(normalized_samples, [1.0/8.0, 3.0/8.0, 5.0/8.0, 7.0/8.0])
+    self.assertEquals(scale, 0.75)
+
+    samples = [1.0/8.0, 3.0/8.0, 5.0/8.0, 7.0/8.0]
+    normalized_samples, scale = statistics.NormalizeSamples(samples)
+    self.assertEquals(normalized_samples, samples)
+    self.assertEquals(scale, 1.0)
+
+  def testDiscrepancyRandom(self):
+    """Tests NormalizeSamples and Discrepancy with random samples.
+
+    Generates 10 sets of 10 random samples, computes the discrepancy,
+    relaxes the samples using Llloyd's algorithm in 1D, and computes the
+    discrepancy of the relaxed samples. Discrepancy of the relaxed samples
+    must be less than or equal to the discrepancy of the original samples.
+    """
+    random.seed(1234567)
+    for _ in xrange(0, 10):
+      samples = CreateRandomSamples(10)
+      samples = statistics.NormalizeSamples(samples)[0]
+      d = statistics.Discrepancy(samples)
+      relaxed_samples = Relax(samples)
+      d_relaxed = statistics.Discrepancy(relaxed_samples)
+      self.assertTrue(d_relaxed <= d)
+
+  def testDiscrepancyAnalytic(self):
+    """Computes discrepancy for sample sets with known statistics."""
+    samples = []
+    d = statistics.Discrepancy(samples)
+    self.assertEquals(d, 0.0)
+
+    samples = [0.5]
+    d = statistics.Discrepancy(samples)
+    self.assertEquals(d, 0.5)
+
+    samples = [0.0, 1.0]
+    d = statistics.Discrepancy(samples)
+    self.assertEquals(d, 1.0)
+
+    samples = [0.5, 0.5, 0.5]
+    d = statistics.Discrepancy(samples)
+    self.assertEquals(d, 1.0)
+
+    samples = [1.0/8.0, 3.0/8.0, 5.0/8.0, 7.0/8.0]
+    d = statistics.Discrepancy(samples)
+    self.assertEquals(d, 0.25)
+
+    samples = [1.0/8.0, 5.0/8.0, 5.0/8.0, 7.0/8.0]
+    d = statistics.Discrepancy(samples)
+    self.assertEquals(d, 0.5)
+
+    samples = [1.0/8.0, 3.0/8.0, 5.0/8.0, 5.0/8.0, 7.0/8.0]
+    d = statistics.Discrepancy(samples)
+    self.assertEquals(d, 0.4)
+
+    samples = [0.0, 1.0/3.0, 2.0/3.0, 1.0]
+    d = statistics.Discrepancy(samples)
+    self.assertEquals(d, 0.5)
+
+    samples = statistics.NormalizeSamples(samples)[0]
+    d = statistics.Discrepancy(samples)
+    self.assertEquals(d, 0.25)
+
+  def testTimestampsDiscrepancy(self):
+    time_stamps = []
+    d_abs = statistics.TimestampsDiscrepancy(time_stamps, True)
+    self.assertEquals(d_abs, 0.0)
+
+    time_stamps = [4]
+    d_abs = statistics.TimestampsDiscrepancy(time_stamps, True)
+    self.assertEquals(d_abs, 0.5)
+
+    time_stamps_a = [0, 1, 2, 3, 5, 6]
+    time_stamps_b = [0, 1, 2, 3, 5, 7]
+    time_stamps_c = [0, 2, 3, 4]
+    time_stamps_d = [0, 2, 3, 4, 5]
+
+    d_abs_a = statistics.TimestampsDiscrepancy(time_stamps_a, True)
+    d_abs_b = statistics.TimestampsDiscrepancy(time_stamps_b, True)
+    d_abs_c = statistics.TimestampsDiscrepancy(time_stamps_c, True)
+    d_abs_d = statistics.TimestampsDiscrepancy(time_stamps_d, True)
+    d_rel_a = statistics.TimestampsDiscrepancy(time_stamps_a, False)
+    d_rel_b = statistics.TimestampsDiscrepancy(time_stamps_b, False)
+    d_rel_c = statistics.TimestampsDiscrepancy(time_stamps_c, False)
+    d_rel_d = statistics.TimestampsDiscrepancy(time_stamps_d, False)
+
+    self.assertTrue(d_abs_a < d_abs_b)
+    self.assertTrue(d_rel_a < d_rel_b)
+    self.assertTrue(d_rel_d < d_rel_c)
+    self.assertAlmostEquals(d_abs_d, d_abs_c)
+
+  def testDiscrepancyMultipleRanges(self):
+    samples = [[0.0, 1.2, 2.3, 3.3], [6.3, 7.5, 8.4], [4.2, 5.4, 5.9]]
+    d_0 = statistics.TimestampsDiscrepancy(samples[0])
+    d_1 = statistics.TimestampsDiscrepancy(samples[1])
+    d_2 = statistics.TimestampsDiscrepancy(samples[2])
+    d = statistics.TimestampsDiscrepancy(samples)
+    self.assertEquals(d, max(d_0, d_1, d_2))
+
+  def testApproximateDiscrepancy(self):
+    """Tests approimate discrepancy implementation by comparing to exact
+    solution.
+    """
+    random.seed(1234567)
+    for _ in xrange(0, 5):
+      samples = CreateRandomSamples(10)
+      samples = statistics.NormalizeSamples(samples)[0]
+      d = statistics.Discrepancy(samples)
+      d_approx = statistics.Discrepancy(samples, 500)
+      self.assertEquals(round(d, 2), round(d_approx, 2))
+
+  def testPercentile(self):
+    # The 50th percentile is the median value.
+    self.assertEquals(3, statistics.Percentile([4, 5, 1, 3, 2], 50))
+    self.assertEquals(2.5, statistics.Percentile([5, 1, 3, 2], 50))
+    # When the list of values is empty, 0 is returned.
+    self.assertEquals(0, statistics.Percentile([], 50))
+    # When the given percentage is very low, the lowest value is given.
+    self.assertEquals(1, statistics.Percentile([2, 1, 5, 4, 3], 5))
+    # When the given percentage is very high, the highest value is given.
+    self.assertEquals(5, statistics.Percentile([5, 2, 4, 1, 3], 95))
+    # Linear interpolation between closest ranks is used. Using the example
+    # from <http://en.wikipedia.org/wiki/Percentile>:
+    self.assertEquals(27.5, statistics.Percentile([15, 20, 35, 40, 50], 40))
+
+  def testArithmeticMean(self):
+    # The ArithmeticMean function computes the simple average.
+    self.assertAlmostEquals(40/3.0, statistics.ArithmeticMean([10, 10, 20]))
+    self.assertAlmostEquals(15.0, statistics.ArithmeticMean([10, 20]))
+    # If the 'count' is zero, then zero is returned.
+    self.assertEquals(0, statistics.ArithmeticMean([]))
+
+  def testDurationsDiscrepancy(self):
+    durations = []
+    d = statistics.DurationsDiscrepancy(durations)
+    self.assertEquals(d, 0.0)
+
+    durations = [4]
+    d = statistics.DurationsDiscrepancy(durations)
+    self.assertEquals(d, 4.0)
+
+    durations_a = [1, 1, 1, 1, 1]
+    durations_b = [1, 1, 2, 1, 1]
+    durations_c = [1, 2, 1, 2, 1]
+
+    d_a = statistics.DurationsDiscrepancy(durations_a)
+    d_b = statistics.DurationsDiscrepancy(durations_b)
+    d_c = statistics.DurationsDiscrepancy(durations_c)
+
+    self.assertTrue(d_a < d_b < d_c)
+
+  def testStandardDeviation(self):
+    self.assertAlmostEquals(math.sqrt(2/3.0),
+                            statistics.StandardDeviation([1, 2, 3]))
+    self.assertEquals(0, statistics.StandardDeviation([1]))
+    self.assertEquals(0, statistics.StandardDeviation([]))
+
+  def testTrapezoidalRule(self):
+    self.assertEquals(4, statistics.TrapezoidalRule([1, 2, 3], 1))
+    self.assertEquals(2, statistics.TrapezoidalRule([1, 2, 3], .5))
+    self.assertEquals(0, statistics.TrapezoidalRule([1, 2, 3], 0))
+    self.assertEquals(-4, statistics.TrapezoidalRule([1, 2, 3], -1))
+    self.assertEquals(3, statistics.TrapezoidalRule([-1, 2, 3], 1))
+    self.assertEquals(0, statistics.TrapezoidalRule([1], 1))
+    self.assertEquals(0, statistics.TrapezoidalRule([0], 1))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/wpr_modes.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/wpr_modes.py
new file mode 100644
index 0000000..262a4fb
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/util/wpr_modes.py
@@ -0,0 +1,7 @@
+# Copyright 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+WPR_APPEND = 'wpr-append'
+WPR_OFF = 'wpr-off'
+WPR_RECORD = 'wpr-record'
+WPR_REPLAY = 'wpr-replay'
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/__init__.py
new file mode 100644
index 0000000..5b4ad1d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/__init__.py
@@ -0,0 +1,373 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""
+The Value hierarchy provides a way of representing the values measurements
+produce such that they can be merged across runs, grouped by page, and output
+to different targets.
+
+The core Value concept provides the basic functionality:
+- association with a page, may be none
+- naming and units
+- importance tracking [whether a value will show up on a waterfall or output
+  file by default]
+- other metadata, such as a description of what was measured
+- default conversion to scalar and string
+- merging properties
+
+A page may actually run a few times during a single telemetry session.
+Downstream consumers of test results typically want to group these runs
+together, then compute summary statistics across runs. Value provides the
+Merge* family of methods for this kind of aggregation.
+"""
+import os
+
+from telemetry.core import discover
+from telemetry.core import util
+
+# When converting a Value to its buildbot equivalent, the context in which the
+# value is being interpreted actually affects the conversion. This is insane,
+# but there you have it. There are three contexts in which Values are converted
+# for use by buildbot, represented by these output-intent values.
+PER_PAGE_RESULT_OUTPUT_CONTEXT = 'per-page-result-output-context'
+COMPUTED_PER_PAGE_SUMMARY_OUTPUT_CONTEXT = 'merged-pages-result-output-context'
+SUMMARY_RESULT_OUTPUT_CONTEXT = 'summary-result-output-context'
+
+class Value(object):
+  """An abstract value produced by a telemetry page test.
+  """
+  def __init__(self, page, name, units, important, description,
+               tir_label, grouping_keys):
+    """A generic Value object.
+
+    Args:
+      page: A Page object, may be given as None to indicate that the value
+          represents results for multiple pages.
+      name: A value name string, may contain a dot. Values from the same test
+          with the same prefix before the dot may be considered to belong to
+          the same chart.
+      units: A units string.
+      important: Whether the value is "important". Causes the value to appear
+          by default in downstream UIs.
+      description: A string explaining in human-understandable terms what this
+          value represents.
+      tir_label: The string label of the TimelineInteractionRecord with
+          which this value is associated.
+      grouping_keys: A dict that maps grouping key names to grouping keys.
+    """
+    # TODO(eakuefner): Check story here after migration (crbug.com/442036)
+    if not isinstance(name, basestring):
+      raise ValueError('name field of Value must be string.')
+    if not isinstance(units, basestring):
+      raise ValueError('units field of Value must be string.')
+    if not isinstance(important, bool):
+      raise ValueError('important field of Value must be bool.')
+    if not ((description is None) or isinstance(description, basestring)):
+      raise ValueError('description field of Value must absent or string.')
+    if not ((tir_label is None) or
+            isinstance(tir_label, basestring)):
+      raise ValueError('tir_label field of Value must absent or '
+                       'string.')
+    if not ((grouping_keys is None) or isinstance(grouping_keys, dict)):
+      raise ValueError('grouping_keys field of Value must be absent or dict')
+
+    if grouping_keys is None:
+      grouping_keys = {}
+
+    self.page = page
+    self.name = name
+    self.units = units
+    self.important = important
+    self.description = description
+    self.tir_label = tir_label
+    self.grouping_keys = grouping_keys
+
+  def __eq__(self, other):
+    return hash(self) == hash(other)
+
+  def __hash__(self):
+    return hash(str(self))
+
+  def IsMergableWith(self, that):
+    return (self.units == that.units and
+            type(self) == type(that) and
+            self.important == that.important)
+
+  @classmethod
+  def MergeLikeValuesFromSamePage(cls, values):
+    """Combines the provided list of values into a single compound value.
+
+    When a page runs multiple times, it may produce multiple values. This
+    function is given the same-named values across the multiple runs, and has
+    the responsibility of producing a single result.
+
+    It must return a single Value. If merging does not make sense, the
+    implementation must pick a representative value from one of the runs.
+
+    For instance, it may be given
+        [ScalarValue(page, 'a', 1), ScalarValue(page, 'a', 2)]
+    and it might produce
+        ListOfScalarValues(page, 'a', [1, 2])
+    """
+    raise NotImplementedError()
+
+  @classmethod
+  def MergeLikeValuesFromDifferentPages(cls, values):
+    """Combines the provided values into a single compound value.
+
+    When a full pageset runs, a single value_name will usually end up getting
+    collected for multiple pages. For instance, we may end up with
+       [ScalarValue(page1, 'a',  1),
+        ScalarValue(page2, 'a',  2)]
+
+    This function takes in the values of the same name, but across multiple
+    pages, and produces a single summary result value. In this instance, it
+    could produce a ScalarValue(None, 'a', 1.5) to indicate averaging, or even
+    ListOfScalarValues(None, 'a', [1, 2]) if concatenated output was desired.
+
+    Some results are so specific to a page that they make no sense when
+    aggregated across pages. If merging values of this type across pages is
+    non-sensical, this method may return None.
+    """
+    raise NotImplementedError()
+
+  def _IsImportantGivenOutputIntent(self, output_context):
+    if output_context == PER_PAGE_RESULT_OUTPUT_CONTEXT:
+      return False
+    elif output_context == COMPUTED_PER_PAGE_SUMMARY_OUTPUT_CONTEXT:
+      return self.important
+    elif output_context == SUMMARY_RESULT_OUTPUT_CONTEXT:
+      return self.important
+
+  def GetBuildbotDataType(self, output_context):
+    """Returns the buildbot's equivalent data_type.
+
+    This should be one of the values accepted by perf_tests_results_helper.py.
+    """
+    raise NotImplementedError()
+
+  def GetBuildbotValue(self):
+    """Returns the buildbot's equivalent value."""
+    raise NotImplementedError()
+
+  def GetChartAndTraceNameForPerPageResult(self):
+    chart_name, _ = _ConvertValueNameToChartAndTraceName(self.name)
+    trace_name = self.page.display_name
+    return chart_name, trace_name
+
+  @property
+  def name_suffix(self):
+    """Returns the string after a . in the name, or the full name otherwise."""
+    if '.' in self.name:
+      return self.name.split('.', 1)[1]
+    else:
+      return self.name
+
+  def GetChartAndTraceNameForComputedSummaryResult(
+      self, trace_tag):
+    chart_name, trace_name = (
+        _ConvertValueNameToChartAndTraceName(self.name))
+    if trace_tag:
+      return chart_name, trace_name + trace_tag
+    else:
+      return chart_name, trace_name
+
+  def GetRepresentativeNumber(self):
+    """Gets a single scalar value that best-represents this value.
+
+    Returns None if not possible.
+    """
+    raise NotImplementedError()
+
+  def GetRepresentativeString(self):
+    """Gets a string value that best-represents this value.
+
+    Returns None if not possible.
+    """
+    raise NotImplementedError()
+
+  @staticmethod
+  def GetJSONTypeName():
+    """Gets the typename for serialization to JSON using AsDict."""
+    raise NotImplementedError()
+
+  def AsDict(self):
+    """Pre-serializes a value to a dict for output as JSON."""
+    return self._AsDictImpl()
+
+  def _AsDictImpl(self):
+    d = {
+      'name': self.name,
+      'type': self.GetJSONTypeName(),
+      'units': self.units,
+      'important': self.important
+    }
+
+    if self.description:
+      d['description'] = self.description
+
+    if self.tir_label:
+      d['tir_label'] = self.tir_label
+
+    if self.page:
+      d['page_id'] = self.page.id
+
+    if self.grouping_keys:
+      d['grouping_keys'] = self.grouping_keys
+
+    return d
+
+  def AsDictWithoutBaseClassEntries(self):
+    full_dict = self.AsDict()
+    base_dict_keys = set(self._AsDictImpl().keys())
+
+    # Extracts only entries added by the subclass.
+    return dict([(k, v) for (k, v) in full_dict.iteritems()
+                  if k not in base_dict_keys])
+
+  @staticmethod
+  def FromDict(value_dict, page_dict):
+    """Produces a value from a value dict and a page dict.
+
+    Value dicts are produced by serialization to JSON, and must be accompanied
+    by a dict mapping page IDs to pages, also produced by serialization, in
+    order to be completely deserialized. If deserializing multiple values, use
+    ListOfValuesFromListOfDicts instead.
+
+    value_dict: a dictionary produced by AsDict() on a value subclass.
+    page_dict: a dictionary mapping IDs to page objects.
+    """
+    return Value.ListOfValuesFromListOfDicts([value_dict], page_dict)[0]
+
+  @staticmethod
+  def ListOfValuesFromListOfDicts(value_dicts, page_dict):
+    """Takes a list of value dicts to values.
+
+    Given a list of value dicts produced by AsDict, this method
+    deserializes the dicts given a dict mapping page IDs to pages.
+    This method performs memoization for deserializing a list of values
+    efficiently, where FromDict is meant to handle one-offs.
+
+    values: a list of value dicts produced by AsDict() on a value subclass.
+    page_dict: a dictionary mapping IDs to page objects.
+    """
+    value_dir = os.path.dirname(__file__)
+    value_classes = discover.DiscoverClasses(
+        value_dir, util.GetTelemetryDir(),
+        Value, index_by_class_name=True)
+
+    value_json_types = dict((value_classes[x].GetJSONTypeName(), x) for x in
+        value_classes)
+
+    values = []
+    for value_dict in value_dicts:
+      value_class = value_classes[value_json_types[value_dict['type']]]
+      assert 'FromDict' in value_class.__dict__, \
+             'Subclass doesn\'t override FromDict'
+      values.append(value_class.FromDict(value_dict, page_dict))
+
+    return values
+
+  @staticmethod
+  def GetConstructorKwArgs(value_dict, page_dict):
+    """Produces constructor arguments from a value dict and a page dict.
+
+    Takes a dict parsed from JSON and an index of pages and recovers the
+    keyword arguments to be passed to the constructor for deserializing the
+    dict.
+
+    value_dict: a dictionary produced by AsDict() on a value subclass.
+    page_dict: a dictionary mapping IDs to page objects.
+    """
+    d = {
+      'name': value_dict['name'],
+      'units': value_dict['units']
+    }
+
+    description = value_dict.get('description', None)
+    if description:
+      d['description'] = description
+    else:
+      d['description'] = None
+
+    page_id = value_dict.get('page_id', None)
+    if page_id is not None:
+      d['page'] = page_dict[int(page_id)]
+    else:
+      d['page'] = None
+
+    d['important'] = False
+
+    tir_label = value_dict.get('tir_label', None)
+    if tir_label:
+      d['tir_label'] = tir_label
+    else:
+      d['tir_label'] = None
+
+    grouping_keys = value_dict.get('grouping_keys', None)
+    if grouping_keys:
+      d['grouping_keys'] = grouping_keys
+    else:
+      d['grouping_keys'] = None
+
+    return d
+
+
+def MergedTirLabel(values):
+  """Returns the tir_label that should be applied to a merge of values.
+
+  As of TBMv2, we encounter situations where we need to merge values with
+  different tir_labels because Telemetry's tir_label field is being used to
+  store story keys for system health stories. As such, when merging, we want to
+  take the common tir_label if all values share the same label (legacy
+  behavior), or have no tir_label if not.
+
+  Args:
+    values: a list of Value instances
+
+  Returns:
+    The tir_label that would be set on the merge of |values|.
+  """
+  assert len(values) > 0
+  v0 = values[0]
+
+  first_tir_label = v0.tir_label
+  if all(v.tir_label == first_tir_label for v in values):
+    return first_tir_label
+  else:
+    return None
+
+
+def ValueNameFromTraceAndChartName(trace_name, chart_name=None):
+  """Mangles a trace name plus optional chart name into a standard string.
+
+  A value might just be a bareword name, e.g. numPixels. In that case, its
+  chart may be None.
+
+  But, a value might also be intended for display with other values, in which
+  case the chart name indicates that grouping. So, you might have
+  screen.numPixels, screen.resolution, where chartName='screen'.
+  """
+  assert trace_name != 'url', 'The name url cannot be used'
+  if chart_name:
+    return '%s.%s' % (chart_name, trace_name)
+  else:
+    assert '.' not in trace_name, ('Trace names cannot contain "." with an '
+        'empty chart_name since this is used to delimit chart_name.trace_name.')
+    return trace_name
+
+
+def _ConvertValueNameToChartAndTraceName(value_name):
+  """Converts a value_name into the equivalent chart-trace name pair.
+
+  Buildbot represents values by the measurement name and an optional trace name,
+  whereas telemetry represents values with a chart_name.trace_name convention,
+  where chart_name is optional. This convention is also used by chart_json.
+
+  This converts from the telemetry convention to the buildbot convention,
+  returning a 2-tuple (measurement_name, trace_name).
+  """
+  if '.' in value_name:
+    return value_name.split('.', 1)
+  else:
+    return value_name, value_name
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/common_value_helpers.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/common_value_helpers.py
new file mode 100644
index 0000000..a1d366c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/common_value_helpers.py
@@ -0,0 +1,41 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import copy
+from telemetry.value import failure
+from telemetry.value import improvement_direction
+from telemetry.value import scalar
+
+
+def TranslateMreFailure(mre_failure, page):
+  return failure.FailureValue.FromMessage(page, mre_failure.stack)
+
+
+def TranslateScalarValue(scalar_value, page):
+  # This function should not modify scalar_value because it is also held by
+  # PageTestResults.value_set.
+  scalar_value = copy.deepcopy(scalar_value)
+
+  value = scalar_value['numeric']['value']
+  scalar_value['value'] = value
+  if value is None:
+    scalar_value['none_value_reason'] = 'Common scalar contained None'
+
+  name = scalar_value['name']
+
+  unit_parts = scalar_value['numeric']['unit'].split('_')
+  if len(unit_parts) != 2:
+    raise ValueError('Must specify improvement direction for value ' + name)
+
+  scalar_value['units'] = unit_parts[0]
+
+  if unit_parts[1] == 'biggerIsBetter':
+    scalar_value['improvement_direction'] = improvement_direction.UP
+  else:
+    assert unit_parts[1] == 'smallerIsBetter'
+    scalar_value['improvement_direction'] = improvement_direction.DOWN
+
+  scalar_value['page_id'] = page.id
+  scalar_value['name'] = name
+  return scalar.ScalarValue.FromDict(scalar_value, {page.id: page})
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/common_value_helpers_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/common_value_helpers_unittest.py
new file mode 100644
index 0000000..100aca0
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/common_value_helpers_unittest.py
@@ -0,0 +1,80 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+import os
+
+from tracing.mre import function_handle
+from tracing.mre import failure
+from tracing.mre import job as job_module
+
+from telemetry import page
+from telemetry import story
+from telemetry.value import improvement_direction
+from telemetry.value import scalar
+from telemetry.value import common_value_helpers
+
+
+def _SingleFileFunctionHandle(filename, function_name, guid):
+  return function_handle.FunctionHandle(
+      modules_to_load=[function_handle.ModuleToLoad(filename=filename)],
+      function_name=function_name, guid=guid)
+
+
+class TranslateCommonValuesTest(unittest.TestCase):
+  def testTranslateMreFailure(self):
+    map_function_handle = _SingleFileFunctionHandle('foo.html', 'Foo', '2')
+    job = job_module.Job(map_function_handle, '1')
+
+    story_set = story.StorySet(base_dir=os.path.dirname(__file__))
+    p = page.Page('http://www.foo.com/', story_set, story_set.base_dir)
+
+    f = failure.Failure(job, 'foo', '/a.json', 'MyFailure', 'failure', 'stack')
+    fv = common_value_helpers.TranslateMreFailure(f, p)
+
+    self.assertIn('stack', str(fv))
+
+  def testTranslateScalarValue(self):
+    story_set = story.StorySet(base_dir=os.path.dirname(__file__))
+    p = page.Page('http://www.foo.com/', story_set, story_set.base_dir)
+
+    scalar_value = {
+        'type': 'numeric',
+        'numeric': {
+            'type': 'scalar',
+            'unit': 'timeInMs_smallerIsBetter',
+            'value': 42
+        },
+        'name': 'foo',
+        'description': 'desc'
+    }
+
+    v = common_value_helpers.TranslateScalarValue(scalar_value, p)
+
+    self.assertIsInstance(v, scalar.ScalarValue)
+    self.assertEquals('foo', v.name)
+    self.assertEquals(p, v.page)
+    self.assertEquals('timeInMs', v.units)
+    self.assertEquals(42, v.value)
+    self.assertEquals(improvement_direction.DOWN, v.improvement_direction)
+    self.assertEquals('desc', v.description)
+
+  def testTranslateScalarNoneValue(self):
+    story_set = story.StorySet(base_dir=os.path.dirname(__file__))
+    p = page.Page('http://www.foo.com/', story_set, story_set.base_dir)
+
+    scalar_value = {
+        'type': 'numeric',
+        'numeric': {
+            'type': 'scalar',
+            'unit': 'timeInMs_smallerIsBetter',
+            'value': None
+        },
+        'name': 'foo'
+    }
+
+    v = common_value_helpers.TranslateScalarValue(scalar_value, p)
+
+    self.assertIsNone(v.value)
+    self.assertEquals('Common scalar contained None', v.none_value_reason)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/failure.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/failure.py
new file mode 100644
index 0000000..27086f4
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/failure.py
@@ -0,0 +1,102 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import sys
+import traceback
+
+from telemetry import value as value_module
+
+
+class FailureValue(value_module.Value):
+
+  def __init__(self, page, exc_info, description=None, tir_label=None,
+               grouping_keys=None):
+    """A value representing a failure when running the page.
+
+    Args:
+      page: The page where this failure occurs.
+      exc_info: The exception info (sys.exc_info()) corresponding to
+          this failure.
+    """
+    exc_type = exc_info[0].__name__
+    super(FailureValue, self).__init__(page, exc_type, '', True, description,
+                                       tir_label, grouping_keys)
+    self._exc_info = exc_info
+
+  @classmethod
+  def FromMessage(cls, page, message):
+    """Creates a failure value for a given string message.
+
+    Args:
+      page: The page where this failure occurs.
+      message: A string message describing the failure.
+    """
+    exc_info = cls._GetExcInfoFromMessage(message)
+    return FailureValue(page, exc_info)
+
+  @staticmethod
+  def _GetExcInfoFromMessage(message):
+    try:
+      raise Exception(message)
+    except Exception:
+      return sys.exc_info()
+
+  def __repr__(self):
+    if self.page:
+      page_name = self.page.display_name
+    else:
+      page_name = 'None'
+    return 'FailureValue(%s, %s)' % (
+        page_name, GetStringFromExcInfo(self._exc_info))
+
+  @property
+  def exc_info(self):
+    return self._exc_info
+
+  def GetBuildbotDataType(self, output_context):
+    return None
+
+  def GetBuildbotValue(self):
+    return None
+
+  def GetChartAndTraceNameForPerPageResult(self):
+    return None
+
+  def GetRepresentativeNumber(self):
+    return None
+
+  def GetRepresentativeString(self):
+    return None
+
+  @staticmethod
+  def GetJSONTypeName():
+    return 'failure'
+
+  def AsDict(self):
+    d = super(FailureValue, self).AsDict()
+    d['value'] = GetStringFromExcInfo(self.exc_info)
+    return d
+
+  @staticmethod
+  def FromDict(value_dict, page_dict):
+    kwargs = value_module.Value.GetConstructorKwArgs(value_dict, page_dict)
+    del kwargs['name']
+    del kwargs['units']
+    if 'important' in kwargs:
+      del kwargs['important']
+    kwargs['exc_info'] = FailureValue._GetExcInfoFromMessage(
+        value_dict['value'])
+
+    return FailureValue(**kwargs)
+
+  @classmethod
+  def MergeLikeValuesFromSamePage(cls, values):
+    assert False, 'Should not be called.'
+
+  @classmethod
+  def MergeLikeValuesFromDifferentPages(cls, values):
+    assert False, 'Should not be called.'
+
+def GetStringFromExcInfo(exc_info):
+  return ''.join(traceback.format_exception(*exc_info))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/failure_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/failure_unittest.py
new file mode 100644
index 0000000..9a60d65
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/failure_unittest.py
@@ -0,0 +1,72 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import sys
+import traceback
+import unittest
+
+from telemetry import story
+from telemetry import page as page_module
+from telemetry import value
+from telemetry.value import failure
+
+
+class TestBase(unittest.TestCase):
+  def setUp(self):
+    self.story_set = story.StorySet(base_dir=os.path.dirname(__file__))
+    self.story_set.AddStory(page_module.Page(
+        'http://www.bar.com/', self.story_set, self.story_set.base_dir))
+
+  @property
+  def pages(self):
+    return self.story_set.stories
+
+class ValueTest(TestBase):
+  def testRepr(self):
+    v = failure.FailureValue.FromMessage(self.pages[0], 'Failure')
+
+    exc_info_str = failure.GetStringFromExcInfo(v.exc_info)
+    expected = 'FailureValue(http://www.bar.com/, %s)' % exc_info_str
+
+    self.assertEquals(expected, str(v))
+
+  def testName(self):
+    v0 = failure.FailureValue.FromMessage(self.pages[0], 'Failure')
+    self.assertEquals('Exception', v0.name)
+    try:
+      raise NotImplementedError()
+    except Exception:
+      v1 = failure.FailureValue(self.pages[0], sys.exc_info())
+    self.assertEquals('NotImplementedError', v1.name)
+
+  def testBuildbotAndRepresentativeValue(self):
+    v = failure.FailureValue.FromMessage(self.pages[0], 'Failure')
+    self.assertIsNone(v.GetBuildbotValue())
+    self.assertIsNone(v.GetBuildbotDataType(
+        value.COMPUTED_PER_PAGE_SUMMARY_OUTPUT_CONTEXT))
+    self.assertIsNone(v.GetChartAndTraceNameForPerPageResult())
+    self.assertIsNone(v.GetRepresentativeNumber())
+    self.assertIsNone(v.GetRepresentativeString())
+
+  def testAsDict(self):
+    v = failure.FailureValue.FromMessage(self.pages[0], 'Failure')
+    d = v.AsDictWithoutBaseClassEntries()
+    self.assertTrue(d['value'].find('Exception: Failure') > -1)
+
+  def testFromDict(self):
+    try:
+      raise Exception('test')
+    except Exception:
+      exc_info = sys.exc_info()
+    d = {
+      'type': 'failure',
+      'name': exc_info[0].__name__,
+      'units': '',
+      'value': ''.join(traceback.format_exception(*exc_info))
+    }
+    v = value.Value.FromDict(d, {})
+
+    self.assertTrue(isinstance(v, failure.FailureValue))
+    self.assertEquals(v.name, 'Exception')
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/histogram.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/histogram.py
new file mode 100644
index 0000000..55f8928
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/histogram.py
@@ -0,0 +1,138 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import json
+
+from telemetry.util import perf_tests_helper
+from telemetry import value as value_module
+from telemetry.value import histogram_util
+from telemetry.value import summarizable
+
+
+class HistogramValueBucket(object):
+  def __init__(self, low, high, count=0):
+    self.low = low
+    self.high = high
+    self.count = count
+
+  def AsDict(self):
+    return {
+      'low': self.low,
+      'high': self.high,
+      'count': self.count
+    }
+
+  def ToJSONString(self):
+    return '{%s}' % ', '.join([
+      '"low": %i' % self.low,
+      '"high": %i' % self.high,
+      '"count": %i' % self.count])
+
+class HistogramValue(summarizable.SummarizableValue):
+  def __init__(self, page, name, units,
+               raw_value=None, raw_value_json=None, important=True,
+               description=None, tir_label=None, improvement_direction=None,
+               grouping_keys=None):
+    super(HistogramValue, self).__init__(page, name, units, important,
+                                         description, tir_label,
+                                         improvement_direction, grouping_keys)
+    if raw_value_json:
+      assert raw_value == None, \
+             'Don\'t specify both raw_value and raw_value_json'
+      raw_value = json.loads(raw_value_json)
+    if raw_value:
+      self.buckets = []
+      for bucket in histogram_util.GetHistogramBucketsFromRawValue(raw_value):
+        self.buckets.append(HistogramValueBucket(
+          low=bucket['low'],
+          high=bucket['high'],
+          count=bucket['count']))
+    else:
+      self.buckets = []
+
+  def __repr__(self):
+    if self.page:
+      page_name = self.page.display_name
+    else:
+      page_name = 'None'
+    return ('HistogramValue(%s, %s, %s, raw_json_string=%s, '
+            'important=%s, description=%s, tir_label=%s, '
+            'improvement_direction=%s, grouping_keys=%s)') % (
+                page_name,
+                self.name, self.units,
+                self.ToJSONString(),
+                self.important,
+                self.description,
+                self.tir_label,
+                self.improvement_direction,
+                self.grouping_keys)
+
+  def GetBuildbotDataType(self, output_context):
+    if self._IsImportantGivenOutputIntent(output_context):
+      return 'histogram'
+    return 'unimportant-histogram'
+
+  def GetBuildbotValue(self):
+    # More buildbot insanity: perf_tests_results_helper requires the histogram
+    # to be an array of size one.
+    return [self.ToJSONString()]
+
+  def ToJSONString(self):
+    # This has to hand-JSONify the histogram to ensure the order of keys
+    # produced is stable across different systems.
+    #
+    # This is done because the buildbot unittests are string equality
+    # assertions. Thus, tests that contain histograms require stable
+    # stringification of the histogram.
+    #
+    # Sigh, buildbot, Y U gotta be that way.
+    return '{"buckets": [%s]}' % (
+      ', '.join([b.ToJSONString() for b in self.buckets]))
+
+  def GetRepresentativeNumber(self):
+    (mean, _) = perf_tests_helper.GeomMeanAndStdDevFromHistogram(
+        self.ToJSONString())
+    return mean
+
+  def GetRepresentativeString(self):
+    return self.GetBuildbotValue()
+
+  @staticmethod
+  def GetJSONTypeName():
+    return 'histogram'
+
+  def AsDict(self):
+    d = super(HistogramValue, self).AsDict()
+    d['buckets'] = [b.AsDict() for b in self.buckets]
+    return d
+
+  @staticmethod
+  def FromDict(value_dict, page_dict):
+    kwargs = value_module.Value.GetConstructorKwArgs(value_dict, page_dict)
+    kwargs['raw_value'] = value_dict
+
+    if 'improvement_direction' in value_dict:
+      kwargs['improvement_direction'] = value_dict['improvement_direction']
+
+    return HistogramValue(**kwargs)
+
+  @classmethod
+  def MergeLikeValuesFromSamePage(cls, values):
+    assert len(values) > 0
+    v0 = values[0]
+    return HistogramValue(
+        v0.page, v0.name, v0.units,
+        raw_value_json=histogram_util.AddHistograms(
+            [v.ToJSONString() for v in values]),
+        description=v0.description,
+        important=v0.important, tir_label=value_module.MergedTirLabel(values),
+        improvement_direction=v0.improvement_direction,
+        grouping_keys=v0.grouping_keys)
+
+  @classmethod
+  def MergeLikeValuesFromDifferentPages(cls, values):
+    # Histograms cannot be merged across pages, at least for now. It should be
+    # theoretically possible, just requires more work. Instead, return None.
+    # This signals to the merging code that the data is unmergable and it will
+    # cope accordingly.
+    return None
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/histogram_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/histogram_unittest.py
new file mode 100644
index 0000000..3e8e662
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/histogram_unittest.py
@@ -0,0 +1,137 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import os
+import unittest
+
+from telemetry import story
+from telemetry import page as page_module
+from telemetry import value
+from telemetry.value import histogram as histogram_module
+from telemetry.value import improvement_direction
+
+
+class TestBase(unittest.TestCase):
+  def setUp(self):
+    story_set = story.StorySet(base_dir=os.path.dirname(__file__))
+    story_set.AddStory(
+        page_module.Page("http://www.bar.com/", story_set, story_set.base_dir))
+    story_set.AddStory(
+        page_module.Page("http://www.baz.com/", story_set, story_set.base_dir))
+    story_set.AddStory(
+        page_module.Page("http://www.foo.com/", story_set, story_set.base_dir))
+    self.story_set = story_set
+
+  @property
+  def pages(self):
+    return self.story_set.stories
+
+class ValueTest(TestBase):
+  def testRepr(self):
+    page = self.pages[0]
+    v = histogram_module.HistogramValue(
+            page, 'x', 'counts',
+           raw_value_json='{"buckets": [{"low": 1, "high": 2, "count": 1}]}',
+           important=True, description='desc', tir_label='my_ir',
+           improvement_direction=improvement_direction.UP)
+    expected = ('HistogramValue(http://www.bar.com/, x, counts, '
+                'raw_json_string={"buckets": [{"low": 1, "high": 2, "count": '
+                '1}]}, important=True, description=desc, tir_label=my_ir, '
+                'improvement_direction=up, grouping_keys={})')
+
+    self.assertEquals(expected, str(v))
+
+  def testHistogramBasic(self):
+    page0 = self.pages[0]
+    histogram = histogram_module.HistogramValue(
+        page0, 'x', 'counts',
+        raw_value_json='{"buckets": [{"low": 1, "high": 2, "count": 1}]}',
+        important=False, improvement_direction=improvement_direction.UP)
+    self.assertEquals(
+      ['{"buckets": [{"low": 1, "high": 2, "count": 1}]}'],
+      histogram.GetBuildbotValue())
+    self.assertEquals(1.5,
+                      histogram.GetRepresentativeNumber())
+    self.assertEquals(
+      ['{"buckets": [{"low": 1, "high": 2, "count": 1}]}'],
+      histogram.GetBuildbotValue())
+
+    self.assertEquals(
+        'unimportant-histogram',
+        histogram.GetBuildbotDataType(value.SUMMARY_RESULT_OUTPUT_CONTEXT))
+    histogram.important = True
+    self.assertEquals(
+        'histogram',
+        histogram.GetBuildbotDataType(value.SUMMARY_RESULT_OUTPUT_CONTEXT))
+
+  def testBucketAsDict(self):
+    bucket = histogram_module.HistogramValueBucket(33, 45, 78)
+    d = bucket.AsDict()
+
+    self.assertEquals(d, {
+          'low': 33,
+          'high': 45,
+          'count': 78
+        })
+
+  def testAsDict(self):
+    histogram = histogram_module.HistogramValue(
+        None, 'x', 'counts',
+        raw_value_json='{"buckets": [{"low": 1, "high": 2, "count": 1}]}',
+        important=False, improvement_direction=improvement_direction.DOWN)
+    d = histogram.AsDictWithoutBaseClassEntries()
+
+    self.assertEquals(['buckets'], d.keys())
+    self.assertTrue(isinstance(d['buckets'], list))
+    self.assertEquals(len(d['buckets']), 1)
+
+  def testFromDict(self):
+    d = {
+      'type': 'histogram',
+      'name': 'x',
+      'units': 'counts',
+      'buckets': [{'low': 1, 'high': 2, 'count': 1}],
+      'improvement_direction': 'down',
+    }
+    v = value.Value.FromDict(d, {})
+
+    self.assertTrue(isinstance(v, histogram_module.HistogramValue))
+    self.assertEquals(
+      ['{"buckets": [{"low": 1, "high": 2, "count": 1}]}'],
+      v.GetBuildbotValue())
+    self.assertEquals(improvement_direction.DOWN, v.improvement_direction)
+
+  def testFromDictWithoutImprovementDirection(self):
+    d = {
+      'type': 'histogram',
+      'name': 'x',
+      'units': 'counts',
+      'buckets': [{'low': 1, 'high': 2, 'count': 1}],
+    }
+    v = value.Value.FromDict(d, {})
+
+    self.assertTrue(isinstance(v, histogram_module.HistogramValue))
+    self.assertIsNone(v.improvement_direction)
+
+  def testMergeLikeValuesFromSamePage(self):
+    d1 = {
+      'type': 'histogram',
+      'name': 'x',
+      'units': 'counts',
+      'description': 'histogram-based metric',
+      'buckets': [{'low': 1, 'high': 3, 'count': 1}],
+    }
+
+    d2 = {
+      'type': 'histogram',
+      'name': 'x',
+      'units': 'counts',
+      'description': 'histogram-based metric',
+      'buckets': [{'low': 2, 'high': 4, 'count': 1}],
+    }
+
+    v0, v1 = value.Value.FromDict(d1, {}), value.Value.FromDict(d2, {})
+
+    vM = histogram_module.HistogramValue.MergeLikeValuesFromSamePage([v0, v1])
+    self.assertTrue(isinstance(vM, histogram_module.HistogramValue))
+    self.assertEquals('histogram-based metric', vM.description)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/histogram_util.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/histogram_util.py
new file mode 100644
index 0000000..e12bb41
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/histogram_util.py
@@ -0,0 +1,140 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""This is a helper module to get and manipulate histogram data.
+
+The histogram data is the same data as is visible from "chrome://histograms".
+More information can be found at: chromium/src/base/metrics/histogram.h
+"""
+
+import collections
+import json
+import logging
+
+from telemetry.core import exceptions
+
+BROWSER_HISTOGRAM = 'browser_histogram'
+RENDERER_HISTOGRAM = 'renderer_histogram'
+
+
+def GetHistogramBucketsFromJson(histogram_json):
+  return GetHistogramBucketsFromRawValue(json.loads(histogram_json))
+
+
+def GetHistogramBucketsFromRawValue(raw_value):
+  buckets = raw_value.get('buckets', [])
+  if buckets:
+    # If there are values greater than the maximum allowable for the histogram,
+    # the highest bucket will have a 'low': maxvalue entry in the dict but no
+    # 'high' entry. Code often assumes the 'high' value will always be present,
+    # and uses it to get bucket mean. So default it to the same value as low.
+    buckets[-1].setdefault('high', buckets[-1]['low'])
+  return buckets
+
+
+def CustomizeBrowserOptions(options):
+  """Allows histogram collection."""
+  options.AppendExtraBrowserArgs(['--enable-stats-collection-bindings'])
+
+
+def SubtractHistogram(histogram_json, start_histogram_json):
+  """Subtracts a previous histogram from a histogram.
+
+  Both parameters and the returned result are json serializations.
+  """
+  start_histogram = json.loads(start_histogram_json)
+  start_histogram_buckets = GetHistogramBucketsFromRawValue(start_histogram)
+  # It's ok if the start histogram is empty (we had no data, maybe even no
+  # histogram at all, at the start of the test).
+  if not start_histogram_buckets:
+    return histogram_json
+
+  histogram = json.loads(histogram_json)
+  if ('pid' in start_histogram and 'pid' in histogram
+      and start_histogram['pid'] != histogram['pid']):
+    raise Exception(
+        'Trying to compare histograms from different processes (%d and %d)'
+        % (start_histogram['pid'], histogram['pid']))
+
+  start_histogram_bucket_counts = dict()
+  for b in start_histogram_buckets:
+    start_histogram_bucket_counts[b['low']] = b['count']
+
+  new_buckets = []
+  for b in GetHistogramBucketsFromRawValue(histogram):
+    new_bucket = b
+    low = b['low']
+    if low in start_histogram_bucket_counts:
+      new_bucket['count'] = b['count'] - start_histogram_bucket_counts[low]
+      if new_bucket['count'] < 0:
+        logging.error('Histogram subtraction error, starting histogram most '
+                      'probably invalid.')
+    if new_bucket['count']:
+      new_buckets.append(new_bucket)
+  histogram['buckets'] = new_buckets
+  histogram['count'] -= start_histogram['count']
+
+  return json.dumps(histogram)
+
+
+def AddHistograms(histogram_jsons):
+  """Adds histograms together. Used for aggregating data.
+
+  The parameter is a list of json serializations and the returned result is a
+  json serialization too.
+
+  Note that the histograms to be added together are typically from different
+  processes.
+  """
+
+  buckets = collections.defaultdict(int)
+  for histogram_json in histogram_jsons:
+    for b in GetHistogramBucketsFromJson(histogram_json):
+      key = (b['low'], b['high'])
+      buckets[key] += b['count']
+
+  buckets = [{'low': key[0], 'high': key[1], 'count': value}
+      for key, value in buckets.iteritems()]
+  buckets.sort(key=lambda h: h['low'])
+
+  result_histogram = {}
+  result_histogram['buckets'] = buckets
+  return json.dumps(result_histogram)
+
+
+def GetHistogram(histogram_type, histogram_name, tab):
+  """Get a json serialization of a histogram."""
+  assert histogram_type in [BROWSER_HISTOGRAM, RENDERER_HISTOGRAM]
+  function = 'getHistogram'
+  if histogram_type == BROWSER_HISTOGRAM:
+    function = 'getBrowserHistogram'
+  try:
+    histogram_json = tab.EvaluateJavaScript(
+        'statsCollectionController.{{ @f }}({{ name }})',
+        f=function, name=histogram_name)
+  except exceptions.EvaluateException:
+    # Sometimes JavaScript flakily fails to execute: http://crbug.com/508431
+    histogram_json = None
+  if histogram_json:
+    return histogram_json
+  return None
+
+
+def GetHistogramCount(histogram_type, histogram_name, tab):
+  """Get the count of events for the given histograms."""
+  histogram_json = GetHistogram(histogram_type, histogram_name, tab)
+  histogram = json.loads(histogram_json)
+  if 'count' in histogram:
+    return histogram['count']
+  else:
+    return 0
+
+def GetHistogramSum(histogram_type, histogram_name, tab):
+  """Get the sum of events for the given histograms."""
+  histogram_json = GetHistogram(histogram_type, histogram_name, tab)
+  histogram = json.loads(histogram_json)
+  if 'sum' in histogram:
+    return histogram['sum']
+  else:
+    return 0
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/histogram_util_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/histogram_util_unittest.py
new file mode 100644
index 0000000..d20caca
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/histogram_util_unittest.py
@@ -0,0 +1,80 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import json
+import unittest
+
+from telemetry.value import histogram_util
+
+class TestHistogram(unittest.TestCase):
+  def testSubtractHistogram(self):
+    baseline_histogram = """{"count": 3, "buckets": [
+        {"low": 1, "high": 2, "count": 1},
+        {"low": 2, "high": 3, "count": 2}]}"""
+
+    later_histogram = """{"count": 14, "buckets": [
+        {"low": 1, "high": 2, "count": 1},
+        {"low": 2, "high": 3, "count": 3},
+        {"low": 3, "high": 4, "count": 10}]}"""
+
+    new_histogram = json.loads(
+        histogram_util.SubtractHistogram(later_histogram, baseline_histogram))
+    new_buckets = dict()
+    for b in new_histogram['buckets']:
+      new_buckets[b['low']] = b['count']
+    self.assertFalse(1 in new_buckets)
+    self.assertEquals(1, new_buckets[2])
+    self.assertEquals(10, new_buckets[3])
+
+
+  def testAddHistograms(self):
+    histograms = []
+    histograms.append("""{"count": 3, "buckets": [
+        {"low": 1, "high": 2, "count": 1},
+        {"low": 2, "high": 3, "count": 2}]}""")
+
+    histograms.append("""{"count": 20, "buckets": [
+        {"low": 2, "high": 3, "count": 10},
+        {"low": 3, "high": 4, "count": 10}]}""")
+
+    histograms.append("""{"count": 15, "buckets": [
+        {"low": 1, "high": 2, "count": 4},
+        {"low": 3, "high": 4, "count": 11}]}""")
+
+    new_histogram = json.loads(
+        histogram_util.AddHistograms(histograms))
+    new_buckets = dict()
+    for b in new_histogram['buckets']:
+      new_buckets[b['low']] = b['count']
+    self.assertEquals(5, new_buckets[1])
+    self.assertEquals(12, new_buckets[2])
+    self.assertEquals(21, new_buckets[3])
+
+
+  def testGetHistogramBucketsFromRawValue_Max(self):
+    raw_value = {'buckets': [
+      {'count': 4, 'low': 10, 'high': 15,},
+      {'count': 6, 'low': 16, 'high': 18,},
+      {'count': 8, 'low': 19},
+    ]}
+    buckets = histogram_util.GetHistogramBucketsFromRawValue(raw_value)
+    self.assertEquals([
+      {'count': 4, 'low': 10, 'high': 15,},
+      {'count': 6, 'low': 16, 'high': 18,},
+      {'count': 8, 'low': 19, 'high': 19},],
+      buckets)
+
+
+  def testGetHistogramBucketsFromJson(self):
+    json_value = json.dumps({'buckets': [
+      {'count': 4, 'low': 10, 'high': 15,},
+      {'count': 6, 'low': 16, 'high': 18,},
+      {'count': 8, 'low': 19, 'high': 25},
+    ]})
+    buckets = histogram_util.GetHistogramBucketsFromJson(json_value)
+    self.assertEquals([
+      {'count': 4, 'low': 10, 'high': 15,},
+      {'count': 6, 'low': 16, 'high': 18,},
+      {'count': 8, 'low': 19, 'high': 25},],
+      buckets)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/improvement_direction.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/improvement_direction.py
new file mode 100644
index 0000000..aca75ca
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/improvement_direction.py
@@ -0,0 +1,9 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+UP = 'up'
+DOWN = 'down'
+
+def IsValid(improvement_direction):
+  return improvement_direction in [UP, DOWN]
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/list_of_scalar_values.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/list_of_scalar_values.py
new file mode 100644
index 0000000..b7ceff5
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/list_of_scalar_values.py
@@ -0,0 +1,209 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import numbers
+import math
+
+from telemetry import value as value_module
+from telemetry.value import none_values
+from telemetry.value import summarizable
+
+
+def Variance(sample):
+  """ Compute the population variance.
+
+    Args:
+      sample: a list of numbers.
+  """
+  k = len(sample) - 1  # Bessel correction
+  if k <= 0:
+    return 0.0
+  m = _Mean(sample)
+  return sum((x - m)**2 for x in sample)/k
+
+
+def StandardDeviation(sample):
+  """ Compute standard deviation for a list of numbers.
+
+    Args:
+      sample: a list of numbers.
+  """
+  return math.sqrt(Variance(sample))
+
+
+def PooledStandardDeviation(list_of_samples, list_of_variances=None):
+  """ Compute standard deviation for a list of samples.
+
+  See: https://en.wikipedia.org/wiki/Pooled_variance for the formula.
+
+  Args:
+    list_of_samples: a list of lists, each is a list of numbers.
+    list_of_variances: a list of numbers, the i-th element is the variance of
+      the i-th sample in list_of_samples. If this is None, we use
+      Variance(sample) to get the variance of the i-th sample.
+  """
+  pooled_variance = 0.0
+  total_degrees_of_freedom = 0
+  for i in xrange(len(list_of_samples)):
+    l = list_of_samples[i]
+    k = len(l) - 1  # Bessel correction
+    if k <= 0:
+      continue
+    variance = list_of_variances[i] if list_of_variances else Variance(l)
+    pooled_variance += k * variance
+    total_degrees_of_freedom += k
+  if total_degrees_of_freedom:
+    return (pooled_variance / total_degrees_of_freedom) ** 0.5
+  else:
+    return 0.0
+
+
+def _Mean(values):
+  return float(sum(values)) / len(values) if len(values) > 0 else 0.0
+
+
+class ListOfScalarValues(summarizable.SummarizableValue):
+  """ ListOfScalarValues represents a list of numbers.
+
+  By default, std is the standard deviation of all numbers in the list. Std can
+  also be specified in the constructor if the numbers are not from the same
+  population.
+  """
+  def __init__(self, page, name, units, values,
+               important=True, description=None,
+               tir_label=None, none_value_reason=None,
+               std=None, improvement_direction=None, grouping_keys=None):
+    super(ListOfScalarValues, self).__init__(page, name, units, important,
+                                             description, tir_label,
+                                             improvement_direction,
+                                             grouping_keys)
+    if values is not None:
+      assert isinstance(values, list)
+      assert len(values) > 0
+      assert all(isinstance(v, numbers.Number) for v in values)
+      assert std is None or isinstance(std, numbers.Number)
+    else:
+      assert std is None
+    none_values.ValidateNoneValueReason(values, none_value_reason)
+    self.values = values
+    self.none_value_reason = none_value_reason
+    if values is not None and std is None:
+      std = StandardDeviation(values)
+    assert std is None or std >= 0, (
+        'standard deviation cannot be negative: %s' % std)
+    self._std = std
+
+  @property
+  def std(self):
+    return self._std
+
+  @property
+  def variance(self):
+    return self._std ** 2
+
+  def __repr__(self):
+    if self.page:
+      page_name = self.page.display_name
+    else:
+      page_name = 'None'
+    return ('ListOfScalarValues(%s, %s, %s, %s, '
+            'important=%s, description=%s, tir_label=%s, std=%s, '
+            'improvement_direction=%s, grouping_keys=%s)') % (
+                page_name,
+                self.name,
+                self.units,
+                repr(self.values),
+                self.important,
+                self.description,
+                self.tir_label,
+                self.std,
+                self.improvement_direction,
+                self.grouping_keys)
+
+  def GetBuildbotDataType(self, output_context):
+    if self._IsImportantGivenOutputIntent(output_context):
+      return 'default'
+    return 'unimportant'
+
+  def GetBuildbotValue(self):
+    return self.values
+
+  def GetRepresentativeNumber(self):
+    return _Mean(self.values)
+
+  def GetRepresentativeString(self):
+    return repr(self.values)
+
+  @staticmethod
+  def GetJSONTypeName():
+    return 'list_of_scalar_values'
+
+  def AsDict(self):
+    d = super(ListOfScalarValues, self).AsDict()
+    d['values'] = self.values
+    d['std'] = self.std
+
+    if self.none_value_reason is not None:
+      d['none_value_reason'] = self.none_value_reason
+
+    return d
+
+  @staticmethod
+  def FromDict(value_dict, page_dict):
+    kwargs = value_module.Value.GetConstructorKwArgs(value_dict, page_dict)
+    kwargs['values'] = value_dict['values']
+    kwargs['std'] = value_dict['std']
+
+    if 'improvement_direction' in value_dict:
+      kwargs['improvement_direction'] = value_dict['improvement_direction']
+    if 'none_value_reason' in value_dict:
+      kwargs['none_value_reason'] = value_dict['none_value_reason']
+
+    return ListOfScalarValues(**kwargs)
+
+  @classmethod
+  def MergeLikeValuesFromSamePage(cls, values):
+    assert len(values) > 0
+    v0 = values[0]
+
+    return cls._MergeLikeValues(values, v0.page, v0.name, v0.grouping_keys)
+
+  @classmethod
+  def MergeLikeValuesFromDifferentPages(cls, values):
+    assert len(values) > 0
+    v0 = values[0]
+    return cls._MergeLikeValues(values, None, v0.name, v0.grouping_keys)
+
+  @classmethod
+  def _MergeLikeValues(cls, values, page, name, grouping_keys):
+    v0 = values[0]
+    merged_values = []
+    list_of_samples = []
+    none_value_reason = None
+    pooled_std = None
+    for v in values:
+      if v.values is None:
+        merged_values = None
+        merged_none_values = [v for v in values if v.values is None]
+        none_value_reason = (none_values.MERGE_FAILURE_REASON +
+            ' None values: %s' % repr(merged_none_values))
+        break
+      merged_values.extend(v.values)
+      list_of_samples.append(v.values)
+    if merged_values and page is None:
+      # Pooled standard deviation is only used when merging values comming from
+      # different pages. Otherwise, fall back to the default computation done
+      # in the cosntructor of ListOfScalarValues.
+      pooled_std = PooledStandardDeviation(
+          list_of_samples, list_of_variances=[v.variance for v in values])
+    return ListOfScalarValues(
+        page, name, v0.units,
+        merged_values,
+        important=v0.important,
+        description=v0.description,
+        tir_label=value_module.MergedTirLabel(values),
+        std=pooled_std,
+        none_value_reason=none_value_reason,
+        improvement_direction=v0.improvement_direction,
+        grouping_keys=grouping_keys)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/list_of_scalar_values_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/list_of_scalar_values_unittest.py
new file mode 100644
index 0000000..d2c1e38
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/list_of_scalar_values_unittest.py
@@ -0,0 +1,267 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import os
+import unittest
+
+from telemetry import story
+from telemetry import page as page_module
+from telemetry import value
+from telemetry.value import improvement_direction
+from telemetry.value import list_of_scalar_values
+from telemetry.value import none_values
+
+
+class StatisticComputationTest(unittest.TestCase):
+  def testVariance(self):
+    self.assertAlmostEqual(
+        list_of_scalar_values.Variance([]), 0)
+    self.assertAlmostEqual(
+        list_of_scalar_values.Variance([3]), 0)
+    self.assertAlmostEqual(
+        list_of_scalar_values.Variance([600, 470, 170, 430, 300]), 27130)
+
+  def testStandardDeviation(self):
+    self.assertAlmostEqual(
+        list_of_scalar_values.StandardDeviation([]), 0)
+    self.assertAlmostEqual(
+        list_of_scalar_values.StandardDeviation([1]), 0)
+    self.assertAlmostEqual(
+        list_of_scalar_values.StandardDeviation([600, 470, 170, 430, 300]),
+        164.71186, places=4)
+
+  def testPooledVariance(self):
+    self.assertAlmostEqual(
+        list_of_scalar_values.PooledStandardDeviation([[], [], []]), 0)
+    self.assertAlmostEqual(
+        list_of_scalar_values.PooledStandardDeviation([[1], [], [3], []]), 0)
+    self.assertAlmostEqual(
+        list_of_scalar_values.PooledStandardDeviation([[1], [2], [3], [4]]), 0)
+    self.assertAlmostEqual(list_of_scalar_values.PooledStandardDeviation(
+        [[600, 470, 170, 430, 300],           # variance = 27130, std = 164.7
+        [4000, 4020, 4230],                   # variance = 16233, std = 127.41
+        [260, 700, 800, 900, 0, 120, 150]]),  # variance = 136348, std = 369.2
+        282.7060,  # SQRT((27130 4 + 16233*2 + 136348*6)/12)
+        places=4)
+    self.assertAlmostEqual(list_of_scalar_values.PooledStandardDeviation(
+        [[600, 470, 170, 430, 300],
+         [4000, 4020, 4230],
+         [260, 700, 800, 900, 0, 120, 150]],
+        list_of_variances=[100000, 200000, 300000]),
+        465.47466,  # SQRT((100000*4 + 200000* 2 + 300000*6)/12)
+        places=4)
+
+
+class TestBase(unittest.TestCase):
+  def setUp(self):
+    story_set = story.StorySet(base_dir=os.path.dirname(__file__))
+    story_set.AddStory(
+        page_module.Page('http://www.bar.com/', story_set, story_set.base_dir))
+    story_set.AddStory(
+        page_module.Page('http://www.baz.com/', story_set, story_set.base_dir))
+    story_set.AddStory(
+        page_module.Page('http://www.foo.com/', story_set, story_set.base_dir))
+    self.story_set = story_set
+
+  @property
+  def pages(self):
+    return self.story_set.stories
+
+class ValueTest(TestBase):
+  def testRepr(self):
+    page = self.pages[0]
+    v = list_of_scalar_values.ListOfScalarValues(
+        page, 'x', 'unit', [10, 9, 9, 7], important=True, description='desc',
+        tir_label='my_ir', std=42,
+        improvement_direction=improvement_direction.DOWN)
+
+    expected = ('ListOfScalarValues(http://www.bar.com/, x, unit, '
+                '[10, 9, 9, 7], important=True, description=desc, '
+                'tir_label=my_ir, std=42, '
+                'improvement_direction=down, grouping_keys={})')
+
+    self.assertEquals(expected, str(v))
+
+  def testListSamePageMerging(self):
+    page0 = self.pages[0]
+    v0 = list_of_scalar_values.ListOfScalarValues(
+        page0, 'x', 'unit',
+        [10, 9, 9, 7], description='list-based metric',
+        improvement_direction=improvement_direction.DOWN)
+    v1 = list_of_scalar_values.ListOfScalarValues(
+        page0, 'x', 'unit',
+        [300, 302, 303, 304], description='list-based metric',
+        improvement_direction=improvement_direction.DOWN)
+    self.assertTrue(v1.IsMergableWith(v0))
+
+    vM = (list_of_scalar_values.ListOfScalarValues.
+          MergeLikeValuesFromSamePage([v0, v1]))
+    self.assertEquals(page0, vM.page)
+    self.assertEquals('x', vM.name)
+    self.assertEquals('unit', vM.units)
+    self.assertEquals(True, vM.important)
+    self.assertEquals([10, 9, 9, 7, 300, 302, 303, 304], vM.values)
+    # Values from the same page use regular standard deviation.
+    self.assertAlmostEqual(156.88849, vM.std, places=4)
+    self.assertEquals('list-based metric', vM.description)
+    self.assertEquals(improvement_direction.DOWN, vM.improvement_direction)
+
+  def testListDifferentPageMerging(self):
+    page0 = self.pages[0]
+    page1 = self.pages[1]
+    v0 = list_of_scalar_values.ListOfScalarValues(
+        page0, 'x', 'unit',
+        [10, 9, 9, 7], improvement_direction=improvement_direction.DOWN)
+    v1 = list_of_scalar_values.ListOfScalarValues(
+        page1, 'x', 'unit',
+        [300, 302, 303, 304], improvement_direction=improvement_direction.DOWN)
+    self.assertTrue(v1.IsMergableWith(v0))
+
+    vM = (list_of_scalar_values.ListOfScalarValues.
+          MergeLikeValuesFromDifferentPages([v0, v1]))
+    self.assertEquals(None, vM.page)
+    self.assertEquals('x', vM.name)
+    self.assertEquals('unit', vM.units)
+    self.assertEquals(True, vM.important)
+    self.assertEquals([10, 9, 9, 7, 300, 302, 303, 304], vM.values)
+    # Values from different pages use pooled standard deviation.
+    # SQRT((19/12 * 3 + 35/12 * 3)/6) = 1.5
+    self.assertAlmostEqual(1.5, vM.std)
+    self.assertEquals(improvement_direction.DOWN, vM.improvement_direction)
+
+  def testListWithNoneValueMerging(self):
+    page0 = self.pages[0]
+    v0 = list_of_scalar_values.ListOfScalarValues(
+        page0, 'x', 'unit',
+        [1, 2], improvement_direction=improvement_direction.UP)
+    v1 = list_of_scalar_values.ListOfScalarValues(
+        page0, 'x', 'unit',
+        None, none_value_reason='n',
+        improvement_direction=improvement_direction.UP)
+    self.assertTrue(v1.IsMergableWith(v0))
+
+    vM = (list_of_scalar_values.ListOfScalarValues.
+          MergeLikeValuesFromSamePage([v0, v1]))
+    self.assertEquals(None, vM.values)
+    expected_none_value_reason = (
+        'Merging values containing a None value results in a None value. '
+        'None values: [ListOfScalarValues(http://www.bar.com/, x, unit, None, '
+        'important=True, description=None, tir_label=None, std=None,'
+        ' improvement_direction=up, grouping_keys={})]')
+    self.assertEquals(expected_none_value_reason, vM.none_value_reason)
+    self.assertEquals(improvement_direction.UP, vM.improvement_direction)
+
+  def testListWithNoneValueMustHaveNoneReason(self):
+    page0 = self.pages[0]
+    self.assertRaises(none_values.NoneValueMissingReason,
+                      lambda: list_of_scalar_values.ListOfScalarValues(
+                          page0, 'x', 'unit', None,
+                          improvement_direction=improvement_direction.DOWN))
+
+  def testListWithNoneReasonMustHaveNoneValue(self):
+    page0 = self.pages[0]
+    self.assertRaises(none_values.ValueMustHaveNoneValue,
+                      lambda: list_of_scalar_values.ListOfScalarValues(
+                          page0, 'x', 'unit', [1, 2],
+                          none_value_reason='n',
+                          improvement_direction=improvement_direction.UP))
+
+  def testAsDict(self):
+    v = list_of_scalar_values.ListOfScalarValues(
+        None, 'x', 'unit', [1, 2],
+        important=False, improvement_direction=improvement_direction.DOWN)
+    d = v.AsDictWithoutBaseClassEntries()
+
+    self.assertEquals(d['values'], [1, 2])
+    self.assertAlmostEqual(d['std'], 0.7071, places=4)
+
+  def testMergedValueAsDict(self):
+    page0 = self.pages[0]
+    v0 = list_of_scalar_values.ListOfScalarValues(
+        page0, 'x', 'unit',
+        [10, 9, 9, 7], improvement_direction=improvement_direction.DOWN)
+    v1 = list_of_scalar_values.ListOfScalarValues(
+        page0, 'x', 'unit',
+        [300, 302, 303, 304], improvement_direction=improvement_direction.DOWN)
+    self.assertTrue(v1.IsMergableWith(v0))
+
+    vM = (list_of_scalar_values.ListOfScalarValues.
+          MergeLikeValuesFromSamePage([v0, v1]))
+    d = vM.AsDict()
+    self.assertEquals(d['values'], [10, 9, 9, 7, 300, 302, 303, 304])
+    # Values from the same page use regular standard deviation.
+    self.assertAlmostEqual(d['std'], 156.88849, places=4)
+
+
+  def testNoneValueAsDict(self):
+    v = list_of_scalar_values.ListOfScalarValues(
+        None, 'x', 'unit', None,
+        important=False, none_value_reason='n',
+        improvement_direction=improvement_direction.UP)
+    d = v.AsDictWithoutBaseClassEntries()
+
+    self.assertEquals(d, {
+          'values': None,
+          'none_value_reason': 'n',
+          'std': None
+        })
+
+  def testFromDictInts(self):
+    d = {
+      'type': 'list_of_scalar_values',
+      'name': 'x',
+      'units': 'unit',
+      'values': [1, 2],
+      'std': 0.7071,
+      'improvement_direction': improvement_direction.DOWN
+    }
+    v = value.Value.FromDict(d, {})
+
+    self.assertTrue(isinstance(v, list_of_scalar_values.ListOfScalarValues))
+    self.assertEquals(v.values, [1, 2])
+    self.assertEquals(v.std, 0.7071)
+    self.assertEquals(improvement_direction.DOWN, v.improvement_direction)
+
+  def testFromDictFloats(self):
+    d = {
+      'type': 'list_of_scalar_values',
+      'name': 'x',
+      'units': 'unit',
+      'values': [1.3, 2.7, 4.5, 2.1, 3.4],
+      'std': 0.901,
+      'improvement_direction': improvement_direction.UP
+    }
+    v = value.Value.FromDict(d, {})
+
+    self.assertTrue(isinstance(v, list_of_scalar_values.ListOfScalarValues))
+    self.assertEquals(v.values, [1.3, 2.7, 4.5, 2.1, 3.4])
+    self.assertEquals(v.std, 0.901)
+
+  def testFromDictWithoutImprovementDirection(self):
+    d = {
+      'type': 'list_of_scalar_values',
+      'name': 'x',
+      'units': 'unit',
+      'values': [1, 2],
+      'std': 0.7071,
+    }
+    v = value.Value.FromDict(d, {})
+
+    self.assertTrue(isinstance(v, list_of_scalar_values.ListOfScalarValues))
+    self.assertIsNone(v.improvement_direction)
+
+  def testFromDictNoneValue(self):
+    d = {
+      'type': 'list_of_scalar_values',
+      'name': 'x',
+      'units': 'unit',
+      'values': None,
+      'std': None,
+      'none_value_reason': 'n',
+      'improvement_direction': improvement_direction.DOWN
+    }
+    v = value.Value.FromDict(d, {})
+
+    self.assertTrue(isinstance(v, list_of_scalar_values.ListOfScalarValues))
+    self.assertEquals(v.values, None)
+    self.assertEquals(v.none_value_reason, 'n')
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/merge_values.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/merge_values.py
new file mode 100644
index 0000000..7ac808d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/merge_values.py
@@ -0,0 +1,144 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.value import failure
+from telemetry.value import skip
+
+
+# TODO(eakuefner): Get rid of this as part of crbug.com/525688
+def DefaultKeyFunc(value):
+  """Keys values in a standard way for grouping in merging and summary.
+
+  Merging and summarization can be parameterized by a function that groups
+  values into equivalence classes. Any function that returns a comparable
+  object can be used as a key_func, but merge_values and summary both use this
+  function by default, to allow the default grouping to change as Telemtry does.
+
+  Args:
+    value: A Telemetry Value instance
+
+  Returns:
+    A comparable object used to group values.
+  """
+  # We use the name and tir_label because even in the TBMv2 case, right now
+  # metrics are responsible for mangling grouping keys into the name, and
+  # PageTestResults is responsible for mangling story grouping keys into the
+  # tir_label.
+  return value.name, value.tir_label
+
+
+def MergeLikeValuesFromSamePage(all_values, key_func=DefaultKeyFunc):
+  """Merges values that measure the same thing on the same page.
+
+  A page may end up being measured multiple times, meaning that we may end up
+  with something like this:
+       ScalarValue(page1, 'x', 1, 'foo')
+       ScalarValue(page2, 'x', 4, 'bar')
+       ScalarValue(page1, 'x', 2, 'foo')
+       ScalarValue(page2, 'x', 5, 'baz')
+
+  This function will produce:
+       ListOfScalarValues(page1, 'x', [1, 2], 'foo')
+       ListOfScalarValues(page2, 'x', [4], 'bar')
+       ListOfScalarValues(page2, 'x', [5], 'baz')
+
+  The workhorse of this code is Value.MergeLikeValuesFromSamePage.
+
+  This requires (but assumes) that the values passed in with the same grouping
+  key pass the Value.IsMergableWith test. If this is not obeyed, the
+  results will be undefined.
+  """
+  return _MergeLikeValuesCommon(
+      all_values,
+      lambda x: (x.page, key_func(x)),
+      lambda v0, merge_group: v0.MergeLikeValuesFromSamePage(merge_group))
+
+
+def MergeLikeValuesFromDifferentPages(all_values, key_func=DefaultKeyFunc):
+  """Merges values that measure the same thing on different pages.
+
+  After using MergeLikeValuesFromSamePage, one still ends up with values from
+  different pages:
+       ScalarValue(page1, 'x', 1, 'foo')
+       ScalarValue(page1, 'y', 30, 'bar')
+       ScalarValue(page2, 'x', 2, 'foo')
+       ScalarValue(page2, 'y', 40, 'baz')
+
+  This function will group values with the same name and tir_label together:
+       ListOfScalarValues(None, 'x', [1, 2], 'foo')
+       ListOfScalarValues(None, 'y', [30], 'bar')
+       ListOfScalarValues(None, 'y', [40], 'baz')
+
+  The workhorse of this code is Value.MergeLikeValuesFromDifferentPages.
+
+  Not all values that go into this function will come out: not every value can
+  be merged across pages. Values whose MergeLikeValuesFromDifferentPages returns
+  None will be omitted from the results.
+
+  This requires (but assumes) that the values passed in with the same name pass
+  the Value.IsMergableWith test. If this is not obeyed, the results
+  will be undefined.
+  """
+  return _MergeLikeValuesCommon(
+      all_values,
+      key_func,
+      lambda v0, merge_group: v0.MergeLikeValuesFromDifferentPages(merge_group))
+
+
+def _MergeLikeValuesCommon(all_values, key_func, merge_func):
+  """Groups all_values by key_func then applies merge_func to the groups.
+
+  This takes the all_values list and groups each item in that using the key
+  provided by key_func. This produces groups of values with like keys. Thes are
+  then handed to the merge_func to produce a new key. If merge_func produces a
+  non-None return, it is added to the list of returned values.
+  """
+  # When merging, we want to merge values in a consistent order, e.g. so that
+  # Scalar(1), Scalar(2) predictably produces ListOfScalarValues([1,2]) rather
+  # than 2,1.
+  #
+  # To do this, the values are sorted by key up front. Then, grouping is
+  # performed using a dictionary, but as new groups are found, the order in
+  # which they were found is also noted.
+  #
+  # Merging is then performed on groups in group-creation-order. This ensures
+  # that the returned array is in a stable order, group by group.
+  #
+  # Within a group, the order is stable because of the original sort.
+  all_values = list(all_values)
+  merge_groups = GroupStably(all_values, key_func)
+
+  res = []
+  for merge_group in merge_groups:
+    v0 = merge_group[0]
+    vM = merge_func(v0, merge_group)
+    if vM:
+      res.append(vM)
+  return res
+
+def GroupStably(all_values, key_func):
+  """Groups an array by key_func, with the groups returned in a stable order.
+
+  Returns a list of groups.
+  """
+  all_values = list(all_values)
+
+  merge_groups = {}
+  merge_groups_in_creation_order = []
+  for value in all_values:
+    # TODO(chrishenry): This is temporary. When we figure out the
+    # right summarization strategy for page runs with failures/skips, we
+    # should use that instead.
+    should_skip_value = (isinstance(value, failure.FailureValue) or
+                         isinstance(value, skip.SkipValue))
+
+    if should_skip_value:
+      continue
+
+    key = key_func(value)
+    if key not in merge_groups:
+      merge_groups[key] = []
+      merge_groups_in_creation_order.append(merge_groups[key])
+    merge_groups[key].append(value)
+  return merge_groups_in_creation_order
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/merge_values_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/merge_values_unittest.py
new file mode 100644
index 0000000..0725bdd
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/merge_values_unittest.py
@@ -0,0 +1,230 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import os
+import unittest
+
+from telemetry import story
+from telemetry import page as page_module
+from telemetry.value import improvement_direction
+from telemetry.value import list_of_scalar_values
+from telemetry.value import merge_values
+from telemetry.value import scalar
+
+
+class TestBase(unittest.TestCase):
+  def setUp(self):
+    story_set = story.StorySet(base_dir=os.path.dirname(__file__))
+    story_set.AddStory(
+        page_module.Page('http://www.bar.com/', story_set, story_set.base_dir))
+    story_set.AddStory(
+        page_module.Page('http://www.baz.com/', story_set, story_set.base_dir))
+    story_set.AddStory(
+        page_module.Page('http://www.foo.com/', story_set, story_set.base_dir))
+    self.story_set = story_set
+
+  @property
+  def pages(self):
+    return self.story_set.stories
+
+class MergeValueTest(TestBase):
+  def testDefaultKeyFuncWithTirLabel(self):
+    page0 = self.pages[0]
+
+    value = scalar.ScalarValue(
+        page0, 'x', 'units', 1,
+        improvement_direction=improvement_direction.UP,
+        tir_label='foo')
+
+    self.assertEquals(('x', 'foo'), merge_values.DefaultKeyFunc(value))
+
+  def testSamePageMergeBasic(self):
+    page0 = self.pages[0]
+    page1 = self.pages[1]
+
+    all_values = [scalar.ScalarValue(
+                      page0, 'x', 'units', 1,
+                      improvement_direction=improvement_direction.UP),
+                  scalar.ScalarValue(
+                      page1, 'x', 'units', 4,
+                      improvement_direction=improvement_direction.UP),
+                  scalar.ScalarValue(
+                      page0, 'x', 'units', 2,
+                      improvement_direction=improvement_direction.UP),
+                  scalar.ScalarValue(
+                      page1, 'x', 'units', 5,
+                      improvement_direction=improvement_direction.UP)]
+
+    merged_values = merge_values.MergeLikeValuesFromSamePage(all_values)
+    # Sort the results so that their order is predictable for the subsequent
+    # assertions.
+    merged_values.sort(key=lambda x: x.page.url)
+
+    self.assertEquals(2, len(merged_values))
+
+    self.assertEquals((page0, 'x'),
+                      (merged_values[0].page, merged_values[0].name))
+    self.assertEquals([1, 2], merged_values[0].values)
+
+    self.assertEquals((page1, 'x'),
+                      (merged_values[1].page, merged_values[1].name))
+    self.assertEquals([4, 5], merged_values[1].values)
+
+  def testSamePageMergeNonstandardKeyFunc(self):
+    page0 = self.pages[0]
+    page1 = self.pages[1]
+
+    all_values = [scalar.ScalarValue(
+                      page0, 'x', 'units', 1,
+                      improvement_direction=improvement_direction.UP),
+                  scalar.ScalarValue(
+                      page1, 'x', 'units', 4,
+                      improvement_direction=improvement_direction.UP),
+                  scalar.ScalarValue(
+                      page0, 'y', 'units', 2,
+                      improvement_direction=improvement_direction.UP),
+                  scalar.ScalarValue(
+                      page1, 'y', 'units', 5,
+                      improvement_direction=improvement_direction.UP)]
+
+    merged_values = merge_values.MergeLikeValuesFromSamePage(
+      all_values, key_func=lambda v: v.page.display_name)
+    # Sort the results so that their order is predictable for the subsequent
+    # assertions.
+    merged_values.sort(key=lambda x: x.page.url)
+
+    self.assertEquals(2, len(merged_values))
+    self.assertEquals([1, 2], merged_values[0].values)
+    self.assertEquals([4, 5], merged_values[1].values)
+
+  def testSamePageMergeOneValue(self):
+    page0 = self.pages[0]
+
+    all_values = [scalar.ScalarValue(
+                      page0, 'x', 'units', 1,
+                      improvement_direction=improvement_direction.DOWN)]
+
+    # Sort the results so that their order is predictable for the subsequent
+    # assertions.
+    merged_values = merge_values.MergeLikeValuesFromSamePage(all_values)
+    self.assertEquals(1, len(merged_values))
+    self.assertEquals(all_values[0].name, merged_values[0].name)
+    self.assertEquals(all_values[0].units, merged_values[0].units)
+
+  def testSamePageMergeWithInteractionRecord(self):
+    page0 = self.pages[0]
+
+    all_values = [scalar.ScalarValue(
+                      page0, 'foo-x', 'units', 1, tir_label='foo',
+                      improvement_direction=improvement_direction.UP),
+                  scalar.ScalarValue(
+                      page0, 'foo-x', 'units', 4, tir_label='foo',
+                      improvement_direction=improvement_direction.UP)]
+
+    merged_values = merge_values.MergeLikeValuesFromSamePage(all_values)
+    self.assertEquals(1, len(merged_values))
+    self.assertEquals('foo', merged_values[0].tir_label)
+
+  def testSamePageMergeWithTwoInteractionRecords(self):
+    page0 = self.pages[0]
+
+    all_values = [scalar.ScalarValue(page0, 'x', 'units', 1, tir_label='foo'),
+                  scalar.ScalarValue(page0, 'x', 'units', 4, tir_label='bar')]
+
+    merged_values = merge_values.MergeLikeValuesFromSamePage(all_values)
+    self.assertEquals(2, len(merged_values))
+    self.assertEquals('foo', merged_values[0].tir_label)
+    self.assertEquals('bar', merged_values[1].tir_label)
+
+  def testDifferentPageMergeBasic(self):
+    page0 = self.pages[0]
+    page1 = self.pages[1]
+
+    all_values = [scalar.ScalarValue(
+                      page0, 'x', 'units', 1,
+                      improvement_direction=improvement_direction.UP),
+                  scalar.ScalarValue(
+                      page1, 'x', 'units', 2,
+                      improvement_direction=improvement_direction.UP),
+                  scalar.ScalarValue(
+                      page0, 'y', 'units', 10,
+                      improvement_direction=improvement_direction.UP),
+                  scalar.ScalarValue(
+                      page1, 'y', 'units', 20,
+                      improvement_direction=improvement_direction.UP)]
+
+    merged_values = merge_values.MergeLikeValuesFromDifferentPages(all_values)
+    merged_values.sort(key=lambda x: x.name)
+    self.assertEquals(2, len(merged_values))
+
+    self.assertEquals((None, 'x'),
+                      (merged_values[0].page, merged_values[0].name))
+    self.assertEquals([1, 2], merged_values[0].values)
+
+    self.assertEquals((None, 'y'),
+                      (merged_values[1].page, merged_values[1].name))
+    self.assertEquals([10, 20], merged_values[1].values)
+
+  def testDifferentPageMergeNonstandardKeyFunc(self):
+    page0 = self.pages[0]
+    page1 = self.pages[1]
+
+    all_values = [scalar.ScalarValue(
+                      page0, 'x', 'units', 1,
+                      improvement_direction=improvement_direction.UP),
+                  scalar.ScalarValue(
+                      page1, 'x', 'units', 2,
+                      improvement_direction=improvement_direction.UP),
+                  scalar.ScalarValue(
+                      page0, 'y', 'units', 10,
+                      improvement_direction=improvement_direction.UP),
+                  scalar.ScalarValue(
+                      page1, 'y', 'units', 20,
+                      improvement_direction=improvement_direction.UP)]
+
+    merged_values = merge_values.MergeLikeValuesFromDifferentPages(
+      all_values, key_func=lambda v: True)
+
+    self.assertEquals(1, len(merged_values))
+    self.assertEquals([1, 2, 10, 20], merged_values[0].values)
+
+  def testDifferentPageMergeSingleValueStillMerges(self):
+    page0 = self.pages[0]
+
+    all_values = [scalar.ScalarValue(
+                      page0, 'x', 'units', 1,
+                      improvement_direction=improvement_direction.DOWN)]
+
+    # Sort the results so that their order is predictable for the subsequent
+    # assertions.
+    merged_values = merge_values.MergeLikeValuesFromDifferentPages(all_values)
+    self.assertEquals(1, len(merged_values))
+
+    self.assertEquals((None, 'x'),
+                      (merged_values[0].page, merged_values[0].name))
+    self.assertTrue(
+        isinstance(merged_values[0], list_of_scalar_values.ListOfScalarValues))
+    self.assertEquals([1], merged_values[0].values)
+
+  def testDifferentPageMergeWithInteractionRecord(self):
+    page0 = self.pages[0]
+    page1 = self.pages[1]
+
+    v0 = scalar.ScalarValue(page0, 'x', 'units', 1, tir_label='foo')
+    v1 = scalar.ScalarValue(page0, 'y', 'units', 30, tir_label='bar')
+    v2 = scalar.ScalarValue(page1, 'x', 'units', 2, tir_label='foo')
+    v3 = scalar.ScalarValue(page1, 'y', 'units', 40, tir_label='baz')
+
+    all_values = [v0, v1, v2, v3]
+
+    merged_x = list_of_scalar_values.ListOfScalarValues(
+      None, 'x', 'units', [1, 2], tir_label='foo')
+    merged_y_bar = list_of_scalar_values.ListOfScalarValues(
+      None, 'y', 'units', [30], tir_label='bar')
+    merged_y_baz = list_of_scalar_values.ListOfScalarValues(
+      None, 'y', 'units', [40], tir_label='baz')
+
+    merged_values = merge_values.MergeLikeValuesFromDifferentPages(all_values)
+    merged_values.sort(key=lambda x: x.tir_label)
+
+    self.assertEquals([merged_y_bar, merged_y_baz, merged_x], merged_values)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/none_values.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/none_values.py
new file mode 100644
index 0000000..7e2759a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/none_values.py
@@ -0,0 +1,24 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+MERGE_FAILURE_REASON = (
+    'Merging values containing a None value results in a None value.')
+
+class NoneValueMissingReason(Exception):
+  pass
+
+class ValueMustHaveNoneValue(Exception):
+  pass
+
+def ValidateNoneValueReason(value, none_value_reason):
+  """Ensures that the none_value_reason is appropriate for the given value.
+
+  There is a logical equality between having a value of None and having a
+  reason for being None. That is to say, value is None if and only if
+  none_value_reason is a string.
+  """
+  if value is None and not isinstance(none_value_reason, basestring):
+    raise NoneValueMissingReason()
+  if value is not None and none_value_reason is not None:
+    raise ValueMustHaveNoneValue()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/scalar.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/scalar.py
new file mode 100644
index 0000000..2a0a17a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/scalar.py
@@ -0,0 +1,128 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import numbers
+
+from telemetry import value as value_module
+from telemetry.value import list_of_scalar_values
+from telemetry.value import none_values
+from telemetry.value import summarizable
+
+
+class ScalarValue(summarizable.SummarizableValue):
+  def __init__(self, page, name, units, value, important=True,
+               description=None, tir_label=None,
+               none_value_reason=None, improvement_direction=None,
+               grouping_keys=None):
+    """A single value (float or integer) result from a test.
+
+    A test that counts the number of DOM elements in a page might produce a
+    scalar value:
+       ScalarValue(page, 'num_dom_elements', 'count', num_elements)
+    """
+    super(ScalarValue, self).__init__(page, name, units, important, description,
+                                      tir_label, improvement_direction,
+                                      grouping_keys)
+    assert value is None or isinstance(value, numbers.Number)
+    none_values.ValidateNoneValueReason(value, none_value_reason)
+    self.value = value
+    self.none_value_reason = none_value_reason
+
+  def __repr__(self):
+    if self.page:
+      page_name = self.page.display_name
+    else:
+      page_name = 'None'
+    return ('ScalarValue(%s, %s, %s, %s, important=%s, description=%s, '
+            'tir_label=%s, improvement_direction=%s, grouping_keys=%s') % (
+                page_name,
+                self.name,
+                self.units,
+                self.value,
+                self.important,
+                self.description,
+                self.tir_label,
+                self.improvement_direction,
+                self.grouping_keys)
+
+  def GetBuildbotDataType(self, output_context):
+    if self._IsImportantGivenOutputIntent(output_context):
+      return 'default'
+    return 'unimportant'
+
+  def GetBuildbotValue(self):
+    # Buildbot's print_perf_results method likes to get lists for all values,
+    # even when they are scalar, so list-ize the return value.
+    return [self.value]
+
+  def GetRepresentativeNumber(self):
+    return self.value
+
+  def GetRepresentativeString(self):
+    return str(self.value)
+
+  @staticmethod
+  def GetJSONTypeName():
+    return 'scalar'
+
+  def AsDict(self):
+    d = super(ScalarValue, self).AsDict()
+    d['value'] = self.value
+
+    if self.none_value_reason is not None:
+      d['none_value_reason'] = self.none_value_reason
+
+    return d
+
+  @staticmethod
+  def FromDict(value_dict, page_dict):
+    kwargs = value_module.Value.GetConstructorKwArgs(value_dict, page_dict)
+
+    # Infinity and NaN are left out of JSON for security reasons that do not
+    # apply to our use cases, so TBMv2 serializes them as strings,
+    # but TBMv1 doesn't support them.
+    if value_dict['value'] in ['Infinity', '-Infinity', 'NaN']:
+      kwargs['value'] = None
+      kwargs['none_value_reason'] = 'value was ' + value_dict['value']
+    else:
+      kwargs['value'] = value_dict['value']
+
+    if 'improvement_direction' in value_dict:
+      kwargs['improvement_direction'] = value_dict['improvement_direction']
+    if 'none_value_reason' in value_dict:
+      kwargs['none_value_reason'] = value_dict['none_value_reason']
+
+    return ScalarValue(**kwargs)
+
+  @classmethod
+  def MergeLikeValuesFromSamePage(cls, values):
+    assert len(values) > 0
+    v0 = values[0]
+    return cls._MergeLikeValues(values, v0.page, v0.name, v0.grouping_keys)
+
+  @classmethod
+  def MergeLikeValuesFromDifferentPages(cls, values):
+    assert len(values) > 0
+    v0 = values[0]
+    return cls._MergeLikeValues(values, None, v0.name, v0.grouping_keys)
+
+  @classmethod
+  def _MergeLikeValues(cls, values, page, name, grouping_keys):
+    v0 = values[0]
+
+    merged_value = [v.value for v in values]
+    none_value_reason = None
+    if None in merged_value:
+      merged_value = None
+      merged_none_values = [v for v in values if v.value is None]
+      none_value_reason = (
+          none_values.MERGE_FAILURE_REASON +
+          ' None values: %s' % repr(merged_none_values))
+    return list_of_scalar_values.ListOfScalarValues(
+        page, name, v0.units, merged_value, important=v0.important,
+        description=v0.description,
+        tir_label=value_module.MergedTirLabel(values),
+        none_value_reason=none_value_reason,
+        improvement_direction=v0.improvement_direction,
+        grouping_keys=grouping_keys)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/scalar_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/scalar_unittest.py
new file mode 100644
index 0000000..a37d5d4
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/scalar_unittest.py
@@ -0,0 +1,204 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import os
+import unittest
+
+from telemetry import story
+from telemetry import page as page_module
+from telemetry import value
+from telemetry.value import improvement_direction
+from telemetry.value import none_values
+from telemetry.value import scalar
+
+
+class TestBase(unittest.TestCase):
+  def setUp(self):
+    story_set = story.StorySet(base_dir=os.path.dirname(__file__))
+    story_set.AddStory(
+        page_module.Page('http://www.bar.com/', story_set, story_set.base_dir))
+    story_set.AddStory(
+        page_module.Page('http://www.baz.com/', story_set, story_set.base_dir))
+    story_set.AddStory(
+        page_module.Page('http://www.foo.com/', story_set, story_set.base_dir))
+    self.story_set = story_set
+
+  @property
+  def pages(self):
+    return self.story_set.stories
+
+class ValueTest(TestBase):
+  def testRepr(self):
+    page0 = self.pages[0]
+    v = scalar.ScalarValue(page0, 'x', 'unit', 3, important=True,
+                           description='desc', tir_label='my_ir',
+                           improvement_direction=improvement_direction.DOWN)
+
+    expected = ('ScalarValue(http://www.bar.com/, x, unit, 3, important=True, '
+                'description=desc, tir_label=my_ir, '
+                'improvement_direction=down, grouping_keys={}')
+
+    self.assertEquals(expected, str(v))
+
+  def testBuildbotValueType(self):
+    page0 = self.pages[0]
+    v = scalar.ScalarValue(page0, 'x', 'unit', 3, important=True,
+                           improvement_direction=improvement_direction.DOWN)
+    self.assertEquals('default', v.GetBuildbotDataType(
+        value.COMPUTED_PER_PAGE_SUMMARY_OUTPUT_CONTEXT))
+    self.assertEquals([3], v.GetBuildbotValue())
+    self.assertEquals(('x', page0.display_name),
+                      v.GetChartAndTraceNameForPerPageResult())
+
+    v = scalar.ScalarValue(page0, 'x', 'unit', 3, important=False,
+                           improvement_direction=improvement_direction.DOWN)
+    self.assertEquals(
+        'unimportant',
+        v.GetBuildbotDataType(value.COMPUTED_PER_PAGE_SUMMARY_OUTPUT_CONTEXT))
+
+  def testScalarSamePageMerging(self):
+    page0 = self.pages[0]
+    v0 = scalar.ScalarValue(page0, 'x', 'unit', 1,
+                            description='important metric',
+                            improvement_direction=improvement_direction.UP)
+    v1 = scalar.ScalarValue(page0, 'x', 'unit', 2,
+                            description='important metric',
+                            improvement_direction=improvement_direction.UP)
+    self.assertTrue(v1.IsMergableWith(v0))
+
+    vM = scalar.ScalarValue.MergeLikeValuesFromSamePage([v0, v1])
+    self.assertEquals(page0, vM.page)
+    self.assertEquals('x', vM.name)
+    self.assertEquals('unit', vM.units)
+    self.assertEquals('important metric', vM.description)
+    self.assertEquals(True, vM.important)
+    self.assertEquals([1, 2], vM.values)
+    self.assertEquals(improvement_direction.UP, vM.improvement_direction)
+
+  def testScalarDifferentPageMerging(self):
+    page0 = self.pages[0]
+    page1 = self.pages[1]
+    v0 = scalar.ScalarValue(page0, 'x', 'unit', 1,
+                            description='important metric',
+                            improvement_direction=improvement_direction.UP)
+    v1 = scalar.ScalarValue(page1, 'x', 'unit', 2,
+                            description='important metric',
+                            improvement_direction=improvement_direction.UP)
+
+    vM = scalar.ScalarValue.MergeLikeValuesFromDifferentPages([v0, v1])
+    self.assertEquals(None, vM.page)
+    self.assertEquals('x', vM.name)
+    self.assertEquals('unit', vM.units)
+    self.assertEquals('important metric', vM.description)
+    self.assertEquals(True, vM.important)
+    self.assertEquals([1, 2], vM.values)
+    self.assertEquals(improvement_direction.UP, vM.improvement_direction)
+
+  def testScalarWithNoneValueMerging(self):
+    page0 = self.pages[0]
+    v0 = scalar.ScalarValue(
+        page0, 'x', 'unit', 1, improvement_direction=improvement_direction.DOWN)
+    v1 = scalar.ScalarValue(page0, 'x', 'unit', None, none_value_reason='n',
+                            improvement_direction=improvement_direction.DOWN)
+    self.assertTrue(v1.IsMergableWith(v0))
+
+    vM = scalar.ScalarValue.MergeLikeValuesFromSamePage([v0, v1])
+    self.assertEquals(None, vM.values)
+    expected_none_value_reason = (
+        'Merging values containing a None value results in a None value. '
+        'None values: [ScalarValue(http://www.bar.com/, x, unit, None, '
+        'important=True, description=None, tir_label=None, '
+        'improvement_direction=down, grouping_keys={}]')
+    self.assertEquals(expected_none_value_reason, vM.none_value_reason)
+
+  def testScalarWithNoneValueMustHaveNoneReason(self):
+    page0 = self.pages[0]
+    self.assertRaises(none_values.NoneValueMissingReason,
+                      lambda: scalar.ScalarValue(
+                          page0, 'x', 'unit', None,
+                          improvement_direction=improvement_direction.UP))
+
+  def testScalarWithNoneReasonMustHaveNoneValue(self):
+    page0 = self.pages[0]
+    self.assertRaises(none_values.ValueMustHaveNoneValue,
+                      lambda: scalar.ScalarValue(
+                          page0, 'x', 'unit', 1, none_value_reason='n',
+                          improvement_direction=improvement_direction.UP))
+
+  def testAsDict(self):
+    v = scalar.ScalarValue(None, 'x', 'unit', 42, important=False,
+                           improvement_direction=improvement_direction.DOWN)
+    d = v.AsDictWithoutBaseClassEntries()
+
+    self.assertEquals(d, {
+          'value': 42
+        })
+
+  def testNoneValueAsDict(self):
+    v = scalar.ScalarValue(None, 'x', 'unit', None, important=False,
+                           none_value_reason='n',
+                           improvement_direction=improvement_direction.DOWN)
+    d = v.AsDictWithoutBaseClassEntries()
+
+    self.assertEquals(d, {
+          'value': None,
+          'none_value_reason': 'n'
+        })
+
+  def testFromDictInt(self):
+    d = {
+      'type': 'scalar',
+      'name': 'x',
+      'units': 'unit',
+      'value': 42,
+      'improvement_direction': improvement_direction.DOWN,
+    }
+
+    v = value.Value.FromDict(d, {})
+
+    self.assertTrue(isinstance(v, scalar.ScalarValue))
+    self.assertEquals(v.value, 42)
+    self.assertEquals(v.improvement_direction, improvement_direction.DOWN)
+
+  def testFromDictFloat(self):
+    d = {
+      'type': 'scalar',
+      'name': 'x',
+      'units': 'unit',
+      'value': 42.4,
+      'improvement_direction': improvement_direction.UP,
+    }
+
+    v = value.Value.FromDict(d, {})
+
+    self.assertTrue(isinstance(v, scalar.ScalarValue))
+    self.assertEquals(v.value, 42.4)
+
+  def testFromDictWithoutImprovementDirection(self):
+    d = {
+      'type': 'scalar',
+      'name': 'x',
+      'units': 'unit',
+      'value': 42,
+    }
+
+    v = value.Value.FromDict(d, {})
+
+    self.assertTrue(isinstance(v, scalar.ScalarValue))
+    self.assertIsNone(v.improvement_direction)
+
+  def testFromDictNoneValue(self):
+    d = {
+      'type': 'scalar',
+      'name': 'x',
+      'units': 'unit',
+      'value': None,
+      'none_value_reason': 'n',
+      'improvement_direction': improvement_direction.UP,
+    }
+
+    v = value.Value.FromDict(d, {})
+
+    self.assertTrue(isinstance(v, scalar.ScalarValue))
+    self.assertEquals(v.value, None)
+    self.assertEquals(v.none_value_reason, 'n')
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/skip.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/skip.py
new file mode 100644
index 0000000..1dd0108
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/skip.py
@@ -0,0 +1,75 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry import value as value_module
+
+
+class SkipValue(value_module.Value):
+
+  def __init__(self, page, reason, description=None):
+    """A value representing a skipped page.
+
+    Args:
+      page: The skipped page object.
+      reason: The string reason the page was skipped.
+    """
+    super(SkipValue, self).__init__(page, 'skip', '', True, description, None,
+                                    None)
+    self._reason = reason
+
+  def __repr__(self):
+    page_name = self.page.display_name
+    return 'SkipValue(%s, %s, description=%s)' % (page_name, self._reason,
+                                                  self.description)
+
+  @property
+  def reason(self):
+    return self._reason
+
+  def GetBuildbotDataType(self, output_context):
+    return None
+
+  def GetBuildbotValue(self):
+    return None
+
+  def GetChartAndTraceNameForPerPageResult(self):
+    return None
+
+  def GetRepresentativeNumber(self):
+    return None
+
+  def GetRepresentativeString(self):
+    return None
+
+  @staticmethod
+  def GetJSONTypeName():
+    return 'skip'
+
+  def AsDict(self):
+    d = super(SkipValue, self).AsDict()
+    d['reason'] = self._reason
+    return d
+
+  @staticmethod
+  def FromDict(value_dict, page_dict):
+    kwargs = value_module.Value.GetConstructorKwArgs(value_dict, page_dict)
+    del kwargs['name']
+    del kwargs['units']
+    if 'important' in kwargs:
+      del kwargs['important']
+    kwargs['reason'] = value_dict['reason']
+    if 'tir_label' in kwargs:
+      del kwargs['tir_label']
+    if 'grouping_keys' in kwargs:
+      del kwargs['grouping_keys']
+
+    return SkipValue(**kwargs)
+
+  @classmethod
+  def MergeLikeValuesFromSamePage(cls, values):
+    assert False, 'Should not be called.'
+
+  @classmethod
+  def MergeLikeValuesFromDifferentPages(cls, values):
+    assert False, 'Should not be called.'
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/skip_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/skip_unittest.py
new file mode 100644
index 0000000..9b11579
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/skip_unittest.py
@@ -0,0 +1,59 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import unittest
+
+from telemetry import story
+from telemetry import page as page_module
+from telemetry import value
+from telemetry.value import skip
+
+
+class TestBase(unittest.TestCase):
+  def setUp(self):
+    story_set = story.StorySet(base_dir=os.path.dirname(__file__))
+    story_set.AddStory(
+        page_module.Page('http://www.bar.com/', story_set, story_set.base_dir))
+    self.story_set = story_set
+
+  @property
+  def pages(self):
+    return self.story_set.stories
+
+class ValueTest(TestBase):
+  def testRepr(self):
+    v = skip.SkipValue(self.pages[0], 'page skipped for testing reason',
+                       description='desc')
+
+    expected = ('SkipValue(http://www.bar.com/, '
+                'page skipped for testing reason, '
+                'description=desc)')
+
+    self.assertEquals(expected, str(v))
+
+  def testBuildbotAndRepresentativeValue(self):
+    v = skip.SkipValue(self.pages[0], 'page skipped for testing reason')
+    self.assertIsNone(v.GetBuildbotValue())
+    self.assertIsNone(v.GetBuildbotDataType(
+        value.COMPUTED_PER_PAGE_SUMMARY_OUTPUT_CONTEXT))
+    self.assertIsNone(v.GetChartAndTraceNameForPerPageResult())
+    self.assertIsNone(v.GetRepresentativeNumber())
+    self.assertIsNone(v.GetRepresentativeString())
+
+  def testAsDict(self):
+    v = skip.SkipValue(self.pages[0], 'page skipped for testing reason')
+    d = v.AsDictWithoutBaseClassEntries()
+    self.assertEquals(d['reason'], 'page skipped for testing reason')
+
+  def testFromDict(self):
+    d = {
+      'type': 'skip',
+      'name': 'skip',
+      'units': '',
+      'reason': 'page skipped for testing reason'
+    }
+    v = value.Value.FromDict(d, {})
+    self.assertTrue(isinstance(v, skip.SkipValue))
+    self.assertEquals(v.reason, 'page skipped for testing reason')
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/summarizable.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/summarizable.py
new file mode 100644
index 0000000..9cfcf46
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/summarizable.py
@@ -0,0 +1,72 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry import value as value_module
+from telemetry.value import (improvement_direction
+                             as improvement_direction_module)
+
+
+class SummarizableValue(value_module.Value):
+  def __init__(self, page, name, units, important, description, tir_label,
+               improvement_direction, grouping_keys):
+    """A summarizable value result from a test."""
+    super(SummarizableValue, self).__init__(
+        page, name, units, important, description, tir_label, grouping_keys)
+# TODO(eakuefner): uncomment this assert after Telemetry clients are fixed.
+# Note: Telemetry unittests satisfy this assert.
+#    assert improvement_direction_module.IsValid(improvement_direction)
+    self._improvement_direction = improvement_direction
+
+  @property
+  def improvement_direction(self):
+    return self._improvement_direction
+
+  def AsDict(self):
+    d = super(SummarizableValue, self).AsDict()
+    if improvement_direction_module.IsValid(self.improvement_direction):
+      d['improvement_direction'] = self.improvement_direction
+    return d
+
+  @staticmethod
+  def GetJSONTypeName():
+    return 'summarizable'
+
+  def AsDictWithoutBaseClassEntries(self):
+    d = super(SummarizableValue, self).AsDictWithoutBaseClassEntries()
+    if 'improvement_direction' in d:
+      del d['improvement_direction']
+    return d
+
+  def GetBuildbotDataType(self, output_context):
+    """Returns the buildbot's equivalent data_type.
+
+    This should be one of the values accepted by perf_tests_results_helper.py.
+    """
+    raise NotImplementedError()
+
+  def GetBuildbotValue(self):
+    """Returns the buildbot's equivalent value."""
+    raise NotImplementedError()
+
+  @classmethod
+  def MergeLikeValuesFromSamePage(cls, values):
+    raise NotImplementedError()
+
+  @classmethod
+  def MergeLikeValuesFromDifferentPages(cls, values):
+    raise NotImplementedError()
+
+  def GetRepresentativeNumber(self):
+    """Gets a single scalar value that best-represents this value.
+
+    Returns None if not possible.
+    """
+    raise NotImplementedError()
+
+  def GetRepresentativeString(self):
+    """Gets a string value that best-represents this value.
+
+    Returns None if not possible.
+    """
+    raise NotImplementedError()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/summarizable_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/summarizable_unittest.py
new file mode 100644
index 0000000..54a94b2
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/summarizable_unittest.py
@@ -0,0 +1,30 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import unittest
+
+from telemetry.value import summarizable
+
+class SummarizableTest(unittest.TestCase):
+
+  def testAsDictWithoutImprovementDirection(self):
+    value = summarizable.SummarizableValue(
+        None, 'foo', 'bars', important=False, description='desc',
+        tir_label=None, improvement_direction=None, grouping_keys=None)
+
+    self.assertNotIn('improvement_direction', value.AsDict())
+
+  def testAsDictWithoutBaseClassEntries(self):
+    value = summarizable.SummarizableValue(
+        None, 'foo', 'bars', important=False, description='desc',
+        tir_label=None, improvement_direction=None, grouping_keys=None)
+
+    self.assertFalse(value.AsDictWithoutBaseClassEntries())
+
+  def testAsDictWithInvalidImprovementDirection(self):
+    # TODO(eakuefner): Remove this test when we check I.D. in constructor
+    value = summarizable.SummarizableValue(
+        None, 'foo', 'bars', important=False, description='desc',
+        tir_label=None, improvement_direction='baz', grouping_keys=None)
+
+    self.assertNotIn('improvement_direction', value.AsDict())
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/summary.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/summary.py
new file mode 100644
index 0000000..3043f6d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/summary.py
@@ -0,0 +1,150 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from collections import defaultdict
+
+from telemetry.value import failure
+from telemetry.value import merge_values
+from telemetry.value import skip
+
+
+class Summary(object):
+  """Computes summary values from the per-page-run values produced by a test.
+
+  Some telemetry benchmark repeat a number of times in order to get a reliable
+  measurement. The test does not have to handle merging of these runs:
+  summarizer does it for you.
+
+  For instance, if two pages run, 3 and 1 time respectively:
+      ScalarValue(page1, 'foo', units='ms', 1)
+      ScalarValue(page1, 'foo', units='ms', 1)
+      ScalarValue(page1, 'foo', units='ms', 1)
+      ScalarValue(page2, 'foo', units='ms', 2)
+
+  Then summarizer will produce two sets of values. First,
+  computed_per_page_values:
+      [
+         ListOfScalarValues(page1, 'foo', units='ms', [1,1,1])],
+         ListOfScalarValues(page2, 'foo', units='ms', [2])]
+      ]
+
+  In addition, it will produce a summary value:
+      [
+         ListOfScalarValues(page=None, 'foo', units='ms', [1,1,1,2])]
+      ]
+
+  """
+  def __init__(self, all_page_specific_values,
+               key_func=merge_values.DefaultKeyFunc):
+    had_failures = any(isinstance(v, failure.FailureValue) for v in
+        all_page_specific_values)
+    self.had_failures = had_failures
+    self._computed_per_page_values = []
+    self._computed_summary_values = []
+    self._interleaved_computed_per_page_values_and_summaries = []
+    self._key_func = key_func
+    self._ComputePerPageValues(all_page_specific_values)
+
+  @property
+  def computed_per_page_values(self):
+    return self._computed_per_page_values
+
+  @property
+  def computed_summary_values(self):
+    return self._computed_summary_values
+
+  @property
+  def interleaved_computed_per_page_values_and_summaries(self):
+    """Returns the computed per page values and summary values interleaved.
+
+    All the results for a given name are printed together. First per page
+    values, then summary values.
+
+    """
+    return self._interleaved_computed_per_page_values_and_summaries
+
+  def _ComputePerPageValues(self, all_page_specific_values):
+    all_successful_page_values = [
+        v for v in all_page_specific_values if not (isinstance(
+            v, failure.FailureValue) or isinstance(v, skip.SkipValue))]
+
+    # We will later need to determine how many values were originally created
+    # for each value name, to apply a workaround meant to clean up the printf
+    # output.
+    num_successful_pages_for_key = defaultdict(int)
+    for v in all_successful_page_values:
+      num_successful_pages_for_key[self._key_func(v)] += 1
+
+    # By here, due to page repeat options, all_values_from_successful_pages
+    # contains values of the same name not only from mulitple pages, but also
+    # from the same name. So even if, for instance, only one page ran, it may
+    # have run twice, producing two 'x' values.
+    #
+    # So, get rid of the repeated pages by merging.
+    merged_page_values = merge_values.MergeLikeValuesFromSamePage(
+        all_successful_page_values, self._key_func)
+
+    # Now we have a bunch of values, but there is only one value_name per page.
+    # Suppose page1 and page2 ran, producing values x and y. We want to print
+    #    x for page1
+    #    x for page2
+    #    x for page1, page2 combined
+    #
+    #    y for page1
+    #    y for page2
+    #    y for page1, page2 combined
+    #
+    # We already have the x values in the values array. But, we will need
+    # them indexable by summary key.
+    #
+    # The following dict maps summary_key -> list of pages that have values of
+    # that name.
+    per_page_values_by_key = defaultdict(list)
+    for value in merged_page_values:
+      per_page_values_by_key[self._key_func(value)].append(value)
+
+    # We already have the x values in the values array. But, we also need
+    # the values merged across the pages. And, we will need them indexed by
+    # summary key so that we can find them when printing out value names in
+    # alphabetical order.
+    merged_pages_value_by_key = {}
+    if not self.had_failures:
+      for value in merge_values.MergeLikeValuesFromDifferentPages(
+          merged_page_values, self._key_func):
+        value_key = self._key_func(value)
+        assert value_key not in merged_pages_value_by_key
+        merged_pages_value_by_key[value_key] = value
+
+    keys = sorted(set([self._key_func(v) for v in merged_page_values]))
+
+    # Time to walk through the values by key, printing first the page-specific
+    # values and then the merged_site value.
+    for key in keys:
+      per_page_values = per_page_values_by_key.get(key, [])
+
+      # Sort the values by their URL.
+      sorted_per_page_values = list(per_page_values)
+      sorted_per_page_values.sort(
+          key=lambda per_page_values: per_page_values.page.display_name)
+
+      # Output the page-specific results.
+      num_successful_pages_for_this_key = (
+          num_successful_pages_for_key[key])
+      for per_page_value in sorted_per_page_values:
+        self._ComputePerPageValue(per_page_value,
+                                  num_successful_pages_for_this_key)
+
+      # Output the combined values.
+      merged_pages_value = merged_pages_value_by_key.get(key, None)
+      if merged_pages_value:
+        self._computed_summary_values.append(merged_pages_value)
+        self._interleaved_computed_per_page_values_and_summaries.append(
+            merged_pages_value)
+
+  def _ComputePerPageValue(
+      self, value, num_successful_pages_for_this_value_name):
+    if num_successful_pages_for_this_value_name >= 1:
+      # Save the result.
+      self._computed_per_page_values.append(value)
+      self._interleaved_computed_per_page_values_and_summaries.append(value)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/summary_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/summary_unittest.py
new file mode 100644
index 0000000..f0a29fb
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/summary_unittest.py
@@ -0,0 +1,429 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import math
+import os
+import unittest
+
+from telemetry import story
+from telemetry.internal.results import page_test_results
+from telemetry import page as page_module
+from telemetry.value import failure
+from telemetry.value import histogram
+from telemetry.value import improvement_direction
+from telemetry.value import list_of_scalar_values
+from telemetry.value import scalar
+from telemetry.value import summary as summary_module
+
+
+class TestBase(unittest.TestCase):
+  def setUp(self):
+    story_set = story.StorySet(base_dir=os.path.dirname(__file__))
+    story_set.AddStory(
+        page_module.Page('http://www.bar.com/', story_set, story_set.base_dir))
+    story_set.AddStory(
+        page_module.Page('http://www.baz.com/', story_set, story_set.base_dir))
+    story_set.AddStory(
+        page_module.Page('http://www.foo.com/', story_set, story_set.base_dir))
+    self.story_set = story_set
+
+  @property
+  def pages(self):
+    return self.story_set.stories
+
+
+class SummaryTest(TestBase):
+  def testBasicSummary(self):
+    page0 = self.pages[0]
+    page1 = self.pages[1]
+
+    results = page_test_results.PageTestResults()
+
+    results.WillRunPage(page0)
+    v0 = scalar.ScalarValue(page0, 'a', 'seconds', 3,
+                            improvement_direction=improvement_direction.UP)
+    results.AddValue(v0)
+    results.DidRunPage(page0)
+
+    results.WillRunPage(page1)
+    v1 = scalar.ScalarValue(page1, 'a', 'seconds', 7,
+                            improvement_direction=improvement_direction.UP)
+    results.AddValue(v1)
+    results.DidRunPage(page1)
+
+    summary = summary_module.Summary(results.all_page_specific_values)
+    values = summary.interleaved_computed_per_page_values_and_summaries
+
+    v0_list = list_of_scalar_values.ListOfScalarValues(
+        page0, 'a', 'seconds', [3],
+        improvement_direction=improvement_direction.UP)
+    v1_list = list_of_scalar_values.ListOfScalarValues(
+        page1, 'a', 'seconds', [7],
+        improvement_direction=improvement_direction.UP)
+    # Std is 0 because we only have one measurement per page.
+    merged_value = list_of_scalar_values.ListOfScalarValues(
+        None, 'a', 'seconds', [3, 7], std=0.0,
+        improvement_direction=improvement_direction.UP)
+
+    self.assertEquals(3, len(values))
+    self.assertIn(v0_list, values)
+    self.assertIn(v1_list, values)
+    self.assertIn(merged_value, values)
+
+  def testBasicSummaryWithOnlyOnePage(self):
+    page0 = self.pages[0]
+
+    results = page_test_results.PageTestResults()
+
+    results.WillRunPage(page0)
+    v0 = scalar.ScalarValue(page0, 'a', 'seconds', 3,
+                            improvement_direction=improvement_direction.UP)
+    results.AddValue(v0)
+    results.DidRunPage(page0)
+
+    summary = summary_module.Summary(results.all_page_specific_values)
+    values = summary.interleaved_computed_per_page_values_and_summaries
+
+    v0_list = list_of_scalar_values.ListOfScalarValues(
+        page0, 'a', 'seconds', [3],
+        improvement_direction=improvement_direction.UP)
+    merged_list = list_of_scalar_values.ListOfScalarValues(
+        None, 'a', 'seconds', [3],
+        improvement_direction=improvement_direction.UP)
+
+    self.assertEquals(2, len(values))
+    self.assertIn(v0_list, values)
+    self.assertIn(merged_list, values)
+
+  def testBasicSummaryNonuniformResults(self):
+    page0 = self.pages[0]
+    page1 = self.pages[1]
+    page2 = self.pages[2]
+
+    results = page_test_results.PageTestResults()
+    results.WillRunPage(page0)
+    v0 = scalar.ScalarValue(page0, 'a', 'seconds', 3,
+                            improvement_direction=improvement_direction.UP)
+    results.AddValue(v0)
+    v1 = scalar.ScalarValue(page0, 'b', 'seconds', 10,
+                            improvement_direction=improvement_direction.UP)
+    results.AddValue(v1)
+    results.DidRunPage(page0)
+
+    results.WillRunPage(page1)
+    v2 = scalar.ScalarValue(page1, 'a', 'seconds', 3,
+                            improvement_direction=improvement_direction.UP)
+    results.AddValue(v2)
+    v3 = scalar.ScalarValue(page1, 'b', 'seconds', 10,
+                            improvement_direction=improvement_direction.UP)
+    results.AddValue(v3)
+    results.DidRunPage(page1)
+
+    results.WillRunPage(page2)
+    v4 = scalar.ScalarValue(page2, 'a', 'seconds', 7,
+                            improvement_direction=improvement_direction.UP)
+    results.AddValue(v4)
+    # Note, page[2] does not report a 'b' metric.
+    results.DidRunPage(page2)
+
+    summary = summary_module.Summary(results.all_page_specific_values)
+    values = summary.interleaved_computed_per_page_values_and_summaries
+
+    v0_list = list_of_scalar_values.ListOfScalarValues(
+        page0, 'a', 'seconds', [3],
+        improvement_direction=improvement_direction.UP)
+    v1_list = list_of_scalar_values.ListOfScalarValues(
+        page0, 'b', 'seconds', [10],
+        improvement_direction=improvement_direction.UP)
+    v2_list = list_of_scalar_values.ListOfScalarValues(
+        page1, 'a', 'seconds', [3],
+        improvement_direction=improvement_direction.UP)
+    v3_list = list_of_scalar_values.ListOfScalarValues(
+        page1, 'b', 'seconds', [10],
+        improvement_direction=improvement_direction.UP)
+    v4_list = list_of_scalar_values.ListOfScalarValues(
+        page2, 'a', 'seconds', [7],
+        improvement_direction=improvement_direction.UP)
+    # Std is 0 because we only have one measurement per page.
+    a_summary = list_of_scalar_values.ListOfScalarValues(
+        None, 'a', 'seconds', [3, 3, 7], std=0.0,
+        improvement_direction=improvement_direction.UP)
+    b_summary = list_of_scalar_values.ListOfScalarValues(
+        None, 'b', 'seconds', [10, 10], std=0.0,
+        improvement_direction=improvement_direction.UP)
+
+    self.assertEquals(7, len(values))
+    self.assertIn(v0_list, values)
+    self.assertIn(v1_list, values)
+    self.assertIn(v2_list, values)
+    self.assertIn(v3_list, values)
+    self.assertIn(v4_list, values)
+    self.assertIn(a_summary, values)
+    self.assertIn(b_summary, values)
+
+  def testBasicSummaryPassAndFailPage(self):
+    """If a page failed, only print summary for individual pages."""
+    page0 = self.pages[0]
+    page1 = self.pages[1]
+
+    results = page_test_results.PageTestResults()
+    results.WillRunPage(page0)
+    v0 = scalar.ScalarValue(page0, 'a', 'seconds', 3,
+                            improvement_direction=improvement_direction.UP)
+    results.AddValue(v0)
+    v1 = failure.FailureValue.FromMessage(page0, 'message')
+    results.AddValue(v1)
+    results.DidRunPage(page0)
+
+    results.WillRunPage(page1)
+    v2 = scalar.ScalarValue(page1, 'a', 'seconds', 7,
+                            improvement_direction=improvement_direction.UP)
+    results.AddValue(v2)
+    results.DidRunPage(page1)
+
+    summary = summary_module.Summary(results.all_page_specific_values)
+    values = summary.interleaved_computed_per_page_values_and_summaries
+
+    v0_list = list_of_scalar_values.ListOfScalarValues(
+        page0, 'a', 'seconds', [3],
+        improvement_direction=improvement_direction.UP)
+    v2_list = list_of_scalar_values.ListOfScalarValues(
+        page1, 'a', 'seconds', [7],
+        improvement_direction=improvement_direction.UP)
+
+    self.assertEquals(2, len(values))
+    self.assertIn(v0_list, values)
+    self.assertIn(v2_list, values)
+
+  def testRepeatedPagesetOneIterationOnePageFails(self):
+    """Page fails on one iteration, no averaged results should print."""
+    page0 = self.pages[0]
+    page1 = self.pages[1]
+
+    results = page_test_results.PageTestResults()
+    results.WillRunPage(page0)
+    v0 = scalar.ScalarValue(page0, 'a', 'seconds', 3,
+                            improvement_direction=improvement_direction.UP)
+    results.AddValue(v0)
+    results.DidRunPage(page0)
+
+    results.WillRunPage(page1)
+    v1 = scalar.ScalarValue(page1, 'a', 'seconds', 7,
+                            improvement_direction=improvement_direction.UP)
+    results.AddValue(v1)
+    v2 = failure.FailureValue.FromMessage(page1, 'message')
+    results.AddValue(v2)
+    results.DidRunPage(page1)
+
+    results.WillRunPage(page0)
+    v3 = scalar.ScalarValue(page0, 'a', 'seconds', 4,
+                            improvement_direction=improvement_direction.UP)
+    results.AddValue(v3)
+    results.DidRunPage(page0)
+
+    results.WillRunPage(page1)
+    v4 = scalar.ScalarValue(page1, 'a', 'seconds', 8,
+                            improvement_direction=improvement_direction.UP)
+    results.AddValue(v4)
+    results.DidRunPage(page1)
+
+    summary = summary_module.Summary(results.all_page_specific_values)
+    values = summary.interleaved_computed_per_page_values_and_summaries
+
+    page0_aggregated = list_of_scalar_values.ListOfScalarValues(
+        page0, 'a', 'seconds', [3, 4],
+        improvement_direction=improvement_direction.UP)
+    page1_aggregated = list_of_scalar_values.ListOfScalarValues(
+        page1, 'a', 'seconds', [7, 8],
+        improvement_direction=improvement_direction.UP)
+
+    self.assertEquals(2, len(values))
+    self.assertIn(page0_aggregated, values)
+    self.assertIn(page1_aggregated, values)
+
+  def testRepeatedPages(self):
+    page0 = self.pages[0]
+    page1 = self.pages[1]
+
+    results = page_test_results.PageTestResults()
+    results.WillRunPage(page0)
+    v0 = scalar.ScalarValue(page0, 'a', 'seconds', 3,
+                            improvement_direction=improvement_direction.UP)
+    results.AddValue(v0)
+    results.DidRunPage(page0)
+
+    results.WillRunPage(page0)
+    v2 = scalar.ScalarValue(page0, 'a', 'seconds', 4,
+                            improvement_direction=improvement_direction.UP)
+    results.AddValue(v2)
+    results.DidRunPage(page0)
+
+    results.WillRunPage(page1)
+    v1 = scalar.ScalarValue(page1, 'a', 'seconds', 7,
+                            improvement_direction=improvement_direction.UP)
+    results.AddValue(v1)
+    results.DidRunPage(page1)
+
+    results.WillRunPage(page1)
+    v3 = scalar.ScalarValue(page1, 'a', 'seconds', 8,
+                            improvement_direction=improvement_direction.UP)
+    results.AddValue(v3)
+    results.DidRunPage(page1)
+
+    summary = summary_module.Summary(results.all_page_specific_values)
+    values = summary.interleaved_computed_per_page_values_and_summaries
+
+    page0_aggregated = list_of_scalar_values.ListOfScalarValues(
+        page0, 'a', 'seconds', [3, 4],
+        improvement_direction=improvement_direction.UP)
+    page1_aggregated = list_of_scalar_values.ListOfScalarValues(
+        page1, 'a', 'seconds', [7, 8],
+        improvement_direction=improvement_direction.UP)
+    # Std is computed using pooled standard deviation.
+    a_summary = list_of_scalar_values.ListOfScalarValues(
+        None, 'a', 'seconds', [3, 4, 7, 8], std=math.sqrt(0.5),
+        improvement_direction=improvement_direction.UP)
+
+    self.assertEquals(3, len(values))
+    self.assertIn(page0_aggregated, values)
+    self.assertIn(page1_aggregated, values)
+    self.assertIn(a_summary, values)
+
+  def testPageRunsTwice(self):
+    page0 = self.pages[0]
+
+    results = page_test_results.PageTestResults()
+
+    results.WillRunPage(page0)
+    v0 = scalar.ScalarValue(page0, 'b', 'seconds', 2,
+                            improvement_direction=improvement_direction.UP)
+    results.AddValue(v0)
+    results.DidRunPage(page0)
+
+    results.WillRunPage(page0)
+    v1 = scalar.ScalarValue(page0, 'b', 'seconds', 3,
+                            improvement_direction=improvement_direction.UP)
+    results.AddValue(v1)
+    results.DidRunPage(page0)
+
+    summary = summary_module.Summary(results.all_page_specific_values)
+    values = summary.interleaved_computed_per_page_values_and_summaries
+
+    page0_aggregated = list_of_scalar_values.ListOfScalarValues(
+        page0, 'b', 'seconds', [2, 3],
+        improvement_direction=improvement_direction.UP)
+    b_summary = list_of_scalar_values.ListOfScalarValues(
+        None, 'b', 'seconds', [2, 3],
+        improvement_direction=improvement_direction.UP)
+
+    self.assertEquals(2, len(values))
+    self.assertIn(page0_aggregated, values)
+    self.assertIn(b_summary, values)
+
+  def testListValue(self):
+    page0 = self.pages[0]
+    page1 = self.pages[1]
+
+    results = page_test_results.PageTestResults()
+
+    results.WillRunPage(page0)
+    v0 = list_of_scalar_values.ListOfScalarValues(
+        page0, 'b', 'seconds', [2, 2],
+        improvement_direction=improvement_direction.UP)
+    results.AddValue(v0)
+    results.DidRunPage(page0)
+
+    results.WillRunPage(page1)
+    v1 = list_of_scalar_values.ListOfScalarValues(
+        page1, 'b', 'seconds', [3, 3],
+        improvement_direction=improvement_direction.UP)
+    results.AddValue(v1)
+    results.DidRunPage(page1)
+
+    summary = summary_module.Summary(results.all_page_specific_values)
+    values = summary.interleaved_computed_per_page_values_and_summaries
+
+    b_summary = list_of_scalar_values.ListOfScalarValues(
+        None, 'b', 'seconds', [2, 2, 3, 3], std=0.0,
+        improvement_direction=improvement_direction.UP)
+
+    self.assertEquals(3, len(values))
+    self.assertIn(v0, values)
+    self.assertIn(v1, values)
+    self.assertIn(b_summary, values)
+
+  def testHistogram(self):
+    page0 = self.pages[0]
+    page1 = self.pages[1]
+
+    results = page_test_results.PageTestResults()
+    results.WillRunPage(page0)
+    v0 = histogram.HistogramValue(
+        page0, 'a', 'units',
+        raw_value_json='{"buckets": [{"low": 1, "high": 2, "count": 1}]}',
+        important=False, improvement_direction=improvement_direction.UP)
+    results.AddValue(v0)
+    results.DidRunPage(page0)
+
+    results.WillRunPage(page1)
+    v1 = histogram.HistogramValue(
+        page1, 'a', 'units',
+        raw_value_json='{"buckets": [{"low": 2, "high": 3, "count": 1}]}',
+        important=False, improvement_direction=improvement_direction.UP)
+    results.AddValue(v1)
+    results.DidRunPage(page1)
+
+    summary = summary_module.Summary(results.all_page_specific_values)
+    values = summary.interleaved_computed_per_page_values_and_summaries
+
+    self.assertEquals(2, len(values))
+    self.assertIn(v0, values)
+    self.assertIn(v1, values)
+
+  def testSummaryUsesKeyFunc(self):
+    page0 = self.pages[0]
+    page1 = self.pages[1]
+
+    results = page_test_results.PageTestResults()
+
+    results.WillRunPage(page0)
+    v0 = scalar.ScalarValue(page0, 'a', 'seconds', 20,
+                            improvement_direction=improvement_direction.UP)
+    results.AddValue(v0)
+
+    v1 = scalar.ScalarValue(page0, 'b', 'seconds', 42,
+                            improvement_direction=improvement_direction.UP)
+    results.AddValue(v1)
+    results.DidRunPage(page0)
+
+    results.WillRunPage(page1)
+    v2 = scalar.ScalarValue(page1, 'a', 'seconds', 20,
+                            improvement_direction=improvement_direction.UP)
+    results.AddValue(v2)
+
+    v3 = scalar.ScalarValue(page1, 'b', 'seconds', 42,
+                            improvement_direction=improvement_direction.UP)
+    results.AddValue(v3)
+    results.DidRunPage(page1)
+
+    summary = summary_module.Summary(
+        results.all_page_specific_values,
+        key_func=lambda v: True)
+    values = summary.interleaved_computed_per_page_values_and_summaries
+
+    v0_list = list_of_scalar_values.ListOfScalarValues(
+        page0, 'a', 'seconds', [20, 42],
+        improvement_direction=improvement_direction.UP)
+    v2_list = list_of_scalar_values.ListOfScalarValues(
+        page1, 'a', 'seconds', [20, 42],
+        improvement_direction=improvement_direction.UP)
+    # Std is computed using pooled standard deviation.
+    merged_value = list_of_scalar_values.ListOfScalarValues(
+        None, 'a', 'seconds', [20, 42, 20, 42], std=math.sqrt(242.0),
+        improvement_direction=improvement_direction.UP)
+
+    self.assertEquals(3, len(values))
+    self.assertIn(v0_list, values)
+    self.assertIn(v2_list, values)
+    self.assertIn(merged_value, values)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/trace.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/trace.py
new file mode 100644
index 0000000..b6b756d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/trace.py
@@ -0,0 +1,161 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import datetime
+import logging
+import os
+import random
+import shutil
+import sys
+import tempfile
+
+from py_utils import cloud_storage  # pylint: disable=import-error
+
+from telemetry.internal.util import file_handle
+from telemetry.timeline import trace_data as trace_data_module
+from telemetry import value as value_module
+from tracing.trace_data import trace_data as trace_data_module
+
+
+class TraceValue(value_module.Value):
+  def __init__(self, page, trace_data, important=False, description=None):
+    """A value that contains a TraceData object and knows how to
+    output it.
+
+    Adding TraceValues and outputting as JSON will produce a directory full of
+    HTML files called trace_files. Outputting as chart JSON will also produce
+    an index, files.html, linking to each of these files.
+    """
+    super(TraceValue, self).__init__(
+        page, name='trace', units='', important=important,
+        description=description, tir_label=None, grouping_keys=None)
+    self._temp_file = self._GetTempFileHandle(trace_data)
+    self._cloud_url = None
+    self._serialized_file_handle = None
+
+  @property
+  def value(self):
+    if self._cloud_url:
+      return self._cloud_url
+    elif self._serialized_file_handle:
+      return self._serialized_file_handle.GetAbsPath()
+
+  def _GetTraceParts(self, trace_data):
+    return [(trace_data.GetTracesFor(p), p)
+            for p in trace_data_module.ALL_TRACE_PARTS
+            if trace_data.HasTracesFor(p)]
+
+  def _GetTempFileHandle(self, trace_data):
+    tf = tempfile.NamedTemporaryFile(delete=False, suffix='.html')
+    tf.close()
+    title = ''
+    if self.page:
+      title = self.page.display_name
+    trace_data.Serialize(tf.name, trace_title=title)
+    return file_handle.FromFilePath(tf.name)
+
+  def __repr__(self):
+    if self.page:
+      page_name = self.page.display_name
+    else:
+      page_name = 'None'
+    return 'TraceValue(%s, %s)' % (page_name, self.name)
+
+  def CleanUp(self):
+    """Cleans up tempfile after it is no longer needed.
+
+    A cleaned up TraceValue cannot be used for further operations. CleanUp()
+    may be called more than once without error.
+    """
+    if self._temp_file is None:
+      return
+    os.remove(self._temp_file.GetAbsPath())
+    self._temp_file = None
+
+  def __enter__(self):
+    return self
+
+  def __exit__(self, _, __, ___):
+    self.CleanUp()
+
+  @property
+  def cleaned_up(self):
+    return self._temp_file is None
+
+  @property
+  def filename(self):
+    return self._temp_file.GetAbsPath()
+
+  def GetBuildbotDataType(self, output_context):
+    return None
+
+  def GetBuildbotValue(self):
+    return None
+
+  def GetRepresentativeNumber(self):
+    return None
+
+  def GetRepresentativeString(self):
+    return None
+
+  @staticmethod
+  def GetJSONTypeName():
+    return 'trace'
+
+  @classmethod
+  def MergeLikeValuesFromSamePage(cls, values):
+    assert len(values) > 0
+    return values[0]
+
+  @classmethod
+  def MergeLikeValuesFromDifferentPages(cls, values):
+    return None
+
+  def AsDict(self):
+    if self._temp_file is None:
+      raise ValueError('Tried to serialize TraceValue without tempfile.')
+    d = super(TraceValue, self).AsDict()
+    if self._serialized_file_handle:
+      d['file_id'] = self._serialized_file_handle.id
+    if self._cloud_url:
+      d['cloud_url'] = self._cloud_url
+    return d
+
+  def Serialize(self, dir_path):
+    if self._temp_file is None:
+      raise ValueError('Tried to serialize nonexistent trace.')
+    if self.page:
+      file_name = self.page.file_safe_name
+    else:
+      file_name = ''
+    file_name += str(self._temp_file.id)
+    file_name += datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
+    file_name += self._temp_file.extension
+    file_path = os.path.abspath(os.path.join(dir_path, file_name))
+    shutil.copy(self._temp_file.GetAbsPath(), file_path)
+    self._serialized_file_handle = file_handle.FromFilePath(file_path)
+    return self._serialized_file_handle
+
+  def UploadToCloud(self, bucket):
+    if self._temp_file is None:
+      raise ValueError('Tried to upload nonexistent trace to Cloud Storage.')
+    try:
+      if self._serialized_file_handle:
+        fh = self._serialized_file_handle
+      else:
+        fh = self._temp_file
+      remote_path = ('trace-file-id_%s-%s-%d%s' % (
+          fh.id,
+          datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S'),
+          random.randint(1, 100000),
+          fh.extension))
+      self._cloud_url = cloud_storage.Insert(
+          bucket, remote_path, fh.GetAbsPath())
+      sys.stderr.write(
+          'View generated trace files online at %s for page %s\n' %
+          (self._cloud_url, self.page.url if self.page else 'unknown'))
+      return self._cloud_url
+    except cloud_storage.PermissionError as e:
+      logging.error('Cannot upload trace files to cloud storage due to '
+                    ' permission error: %s' % e.message)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/trace_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/trace_unittest.py
new file mode 100644
index 0000000..a95d000
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/trace_unittest.py
@@ -0,0 +1,187 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import codecs
+import json
+import os
+import shutil
+import tempfile
+import unittest
+
+from telemetry import story
+from telemetry import page as page_module
+from telemetry.testing import system_stub
+from telemetry.value import trace
+from tracing_build import html2trace
+from tracing.trace_data import trace_data
+
+
+class TestBase(unittest.TestCase):
+
+  def setUp(self):
+    story_set = story.StorySet(base_dir=os.path.dirname(__file__))
+    story_set.AddStory(
+        page_module.Page('http://www.bar.com/', story_set, story_set.base_dir))
+    story_set.AddStory(
+        page_module.Page('http://www.baz.com/', story_set, story_set.base_dir))
+    story_set.AddStory(
+        page_module.Page('http://www.foo.com/', story_set, story_set.base_dir))
+    self.story_set = story_set
+
+    self._cloud_storage_stub = system_stub.Override(trace, ['cloud_storage'])
+
+  def tearDown(self):
+    if self._cloud_storage_stub:
+      self._cloud_storage_stub.Restore()
+      self._cloud_storage_stub = None
+
+  @property
+  def pages(self):
+    return self.story_set.stories
+
+
+class TestSet(object):
+  """ A test set that represents a set that contains any key. """
+
+  def __contains__(self, key):
+    return True
+
+
+class TestDefaultDict(object):
+  """ A test default dict that represents a dictionary that contains any key
+  with value |default_value|. """
+
+  def __init__(self, default_value):
+    self._default_value = default_value
+    self._test_set = TestSet()
+
+  def __contains__(self, key):
+    return key in self._test_set
+
+  def __getitem__(self, key):
+    return self._default_value
+
+  def keys(self):
+    return self._test_set
+
+
+class ValueTest(TestBase):
+  def testRepr(self):
+    v = trace.TraceValue(
+        self.pages[0], trace_data.CreateTraceDataFromRawData([{'test': 1}]),
+                         important=True, description='desc')
+
+    self.assertEquals('TraceValue(http://www.bar.com/, trace)', str(v))
+
+  def testTraceSerializationContainStoryName(self):
+    tempdir = tempfile.mkdtemp()
+    try:
+      v = trace.TraceValue(self.pages[0],
+                           trace_data.CreateTraceDataFromRawData([{'test': 1}]))
+      fh = v.Serialize(tempdir)
+      self.assertTrue(os.path.basename(fh.GetAbsPath()).startswith(
+          'http___www_bar_com'))
+    finally:
+      shutil.rmtree(tempdir)
+
+  def testAsDictWhenTraceSerializedAndUploaded(self):
+    tempdir = tempfile.mkdtemp()
+    try:
+      v = trace.TraceValue(None,
+                           trace_data.CreateTraceDataFromRawData([{'test': 1}]))
+      fh = v.Serialize(tempdir)
+      # pylint: disable=no-member
+      trace.cloud_storage.SetCalculatedHashesForTesting(
+          {fh.GetAbsPath(): 123})
+      # pylint: enable=no-member
+      bucket = trace.cloud_storage.PUBLIC_BUCKET
+      cloud_url = v.UploadToCloud(bucket)
+      d = v.AsDict()
+      self.assertEqual(d['file_id'], fh.id)
+      self.assertEqual(d['cloud_url'], cloud_url)
+    finally:
+      shutil.rmtree(tempdir)
+
+  def testAsDictWhenTraceIsNotSerializedAndUploaded(self):
+    test_temp_file = tempfile.NamedTemporaryFile(delete=False)
+    try:
+      v = trace.TraceValue(None,
+                           trace_data.CreateTraceDataFromRawData([{'test': 1}]))
+      # pylint: disable=no-member
+      trace.cloud_storage.SetCalculatedHashesForTesting(
+          TestDefaultDict(123))
+      # pylint: enable=no-member
+      bucket = trace.cloud_storage.PUBLIC_BUCKET
+      cloud_url = v.UploadToCloud(bucket)
+      d = v.AsDict()
+      self.assertEqual(d['cloud_url'], cloud_url)
+    finally:
+      if os.path.exists(test_temp_file.name):
+        test_temp_file.close()
+        os.remove(test_temp_file.name)
+
+  def testFindTraceParts(self):
+    raw_data = {
+      'powerTraceAsString': 'BattOr Data',
+      'traceEvents': [{'trace': 1}],
+      'tabIds': 'Tab Data',
+    }
+    data = trace_data.CreateTraceDataFromRawData(raw_data)
+    v = trace.TraceValue(None, data)
+    tempdir = tempfile.mkdtemp()
+    temp_path = os.path.join(tempdir, 'test.json')
+    battor_seen = False
+    chrome_seen = False
+    tabs_seen = False
+    try:
+      with codecs.open(v.filename, mode='r', encoding='utf-8') as f:
+        trace_files = html2trace.CopyTraceDataFromHTMLFilePath(f, temp_path)
+      for f in trace_files:
+        with open(f, 'r') as trace_file:
+          d = trace_file.read()
+          if d == raw_data['powerTraceAsString']:
+            self.assertFalse(battor_seen)
+            battor_seen = True
+          elif d == json.dumps({'traceEvents': raw_data['traceEvents']}):
+            self.assertFalse(chrome_seen)
+            chrome_seen = True
+          elif d == raw_data['tabIds']:
+            self.assertFalse(tabs_seen)
+            tabs_seen = True
+      self.assertTrue(battor_seen)
+      self.assertTrue(chrome_seen)
+      self.assertTrue(tabs_seen)
+    finally:
+      shutil.rmtree(tempdir)
+      os.remove(v.filename)
+
+
+def _IsEmptyDir(path):
+  return os.path.exists(path) and not os.listdir(path)
+
+
+class NoLeakedTempfilesTests(TestBase):
+
+  def setUp(self):
+    super(NoLeakedTempfilesTests, self).setUp()
+    self.temp_test_dir = tempfile.mkdtemp()
+    self.actual_tempdir = trace.tempfile.tempdir
+    trace.tempfile.tempdir = self.temp_test_dir
+
+  def testNoLeakedTempFileOnImplicitCleanUp(self):
+    with trace.TraceValue(
+        None, trace_data.CreateTraceDataFromRawData([{'test': 1}])):
+      pass
+    self.assertTrue(_IsEmptyDir(self.temp_test_dir))
+
+  def testNoLeakedTempFileWhenUploadingTrace(self):
+    v = trace.TraceValue(
+        None, trace_data.CreateTraceDataFromRawData([{'test': 1}]))
+    v.CleanUp()
+    self.assertTrue(_IsEmptyDir(self.temp_test_dir))
+
+  def tearDown(self):
+    super(NoLeakedTempfilesTests, self).tearDown()
+    shutil.rmtree(self.temp_test_dir)
+    trace.tempfile.tempdir = self.actual_tempdir
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/unit-info.json b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/unit-info.json
new file mode 100644
index 0000000..6a6c931
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/unit-info.json
@@ -0,0 +1,221 @@
+{
+  "_description" : "This file contains info about our performance test units, used by the perf dashboard (http://chromeperf.appspot.com).",
+
+  "%": {
+    "improvement_direction": "down",
+    "why": "Percent CPU usage. Used by cpu metric."
+  },
+  "bit/s": {
+    "improvement_direction": "down",
+    "why": "Bitrate."
+  },
+  "bytes/s": {
+    "improvement_direction": "down",
+    "why": "Bitrate."
+  },
+  "bytes": {
+    "improvement_direction": "down"
+  },
+  "chars/s": {
+    "improvement_direction": "up"
+  },
+  "Celsius": {
+    "improvement_direction": "down",
+    "why": "Colder machines are faster."
+  },
+  "commit_count": {
+    "improvement_direction": "up",
+    "why": "layer_tree_host_perftest"
+  },
+  "count": {
+    "improvement_direction": "down",
+    "why": "Processes"
+  },
+  "coverage%": {
+    "improvement_direction": "up",
+    "why": "Used in alloy-perf-test/cts%/passed."
+  },
+  "dB": {
+    "improvement_direction": "up",
+    "why": "Decibels peak signal-to-noise ratio. Used by WebRTC quality tests."
+  },
+  "files": {
+    "improvement_direction": "down",
+    "why": "Static initializers"
+  },
+  "fps": {
+    "improvement_direction": "up",
+    "why": "The faster the better. Used by scirra benchmark."
+  },
+  "frames": {
+    "improvement_direction": "down",
+    "why": "Dropped frames."
+  },
+  "frames-per-second": {
+    "improvement_direction": "up",
+    "why": "Synonym for fps."
+  },
+  "frame_count": {
+    "improvement_direction": "up",
+    "why": "layer_tree_host_perftest"
+  },
+  "frame_time": {
+    "improvement_direction": "down"
+  },
+  "garbage_collections": {
+    "improvement_direction": "down",
+    "why": "Number of GCs needed to collect an object. Less is better."
+  },
+  "Hz": {
+    "improvement_direction": "up",
+    "why": "Higher frequencies are faster."
+  },
+  "janks": {
+    "improvement_direction": "down",
+    "why": "Fewer janks is better."
+  },
+  "kb": {
+    "improvement_direction": "down",
+    "why": "Synonym for KB, used in memory and io metrics."
+  },
+  "available_kB": {
+    "improvement_direction": "up",
+    "why": "kB of memory available. More memory available is better."
+  },
+  "KB": {
+    "improvement_direction": "down",
+    "why": "KB of memory usage. Less memory usage is better. Used in endure."
+  },
+  "lines": {
+    "improvement_direction": "up",
+    "why": "Coverage. More test coverage is better."
+  },
+  "load": {
+    "improvement_direction": "down"
+  },
+  "MB": {
+    "improvement_direction": "down"
+  },
+  "mips": {
+    "improvement_direction": "up",
+    "why": "More instructions processed per time unit."
+  },
+  "mpixels_sec": {
+    "improvement_direction": "up",
+    "why": "More pixels processed per time unit."
+  },
+  "mtexel_sec": {
+    "improvement_direction": "up",
+    "why": "More texels processed per time unit."
+  },
+  "mtri_sec": {
+    "improvement_direction": "up",
+    "why": "More triangles processed per time unit."
+  },
+  "mvtx_sec": {
+    "improvement_direction": "up",
+    "why": "More vertices processed per time unit."
+  },
+  "ms": {
+    "improvement_direction": "down",
+    "why": "Used in many Telemetry measurements. Fewer ms of time means faster."
+  },
+  "ms/1000 elements": {
+    "improvement_direction": "down"
+  },
+  "milliseconds": {
+    "improvement_direction": "down",
+    "why": "Synonym for ms."
+  },
+  "milliseconds-per-frame": {
+    "improvement_direction": "down"
+  },
+  "minutes": {
+    "improvement_direction": "down",
+    "why": "Used for NaCl build time."
+  },
+  "mWh": {
+    "improvement_direction": "down",
+    "why": "Fewer milliwatt-hours means less energy consumed."
+  },
+  "objects (bigger is better)": {
+    "improvement_direction": "up",
+    "why": "Used in spaceport benchmark."
+  },
+  "ObjectsAt30FPS": {
+    "improvement_direction": "up"
+  },
+  "packets": {
+    "improvement_direction": "down",
+    "why": "Monitors how many packets we use to accomplish something."
+  },
+  "percent": {
+    "improvement_direction": "down",
+    "why": "Synonym for %, used in memory metric for percent fragmentation."
+  },
+  "points": {
+    "improvement_direction": "up",
+    "why": "Synonym for score, used in ChromeOS touchpad tests."
+  },
+  "ports": {
+    "improvement_direction": "down"
+  },
+  "reduction%": {
+    "improvement_direction": "up",
+    "why": "Used in draw_property measurement to indicate relative improvement."
+  },
+  "relocs": {
+    "improvement_direction": "down"
+  },
+  "runs/ms": {
+    "improvement_direction": "up",
+    "why": "Higher runs/ms implies faster execution."
+  },
+  "runs/s": {
+    "improvement_direction": "up",
+    "why": "Used in dromaeo. Higher runs/s implies faster execution."
+  },
+  "runs_per_s": {
+    "improvement_direction": "up",
+    "why": "Synonym for runs/s, used in dromaeo data sent by cros bots."
+  },
+  "runs_per_second": {
+    "improvement_direction": "up",
+    "why": "Synonym for runs/s."
+  },
+  "score": {
+    "improvement_direction": "up",
+    "why": "Used in a variety of benchmarks where a higher score is better."
+  },
+  "score_(bigger_is_better)": {
+    "improvement_direction": "up",
+    "why": "Synonym for score."
+  },
+  "score (bigger is better)": {
+    "improvement_direction": "up",
+    "why": "Synonym for score, used in jsgamebench and dom_perf."
+  },
+  "sec": {
+    "improvement_direction": "down"
+  },
+  "seconds": {
+    "improvement_direction": "down"
+  },
+  "tasks": {
+    "improvement_direction": "down"
+  },
+  "tokens/s": {
+    "improvement_direction": "up"
+  },
+  "us": {
+    "improvement_direction": "down"
+  },
+  "vsyncs": {
+    "improvement_direction": "down",
+    "why": "Used in smoothness benchmarks. Number of vsyncs to generate a frame, never < 1.0"
+  },
+  "idle%": {
+    "improvement_direction": "up",
+    "why": "Percentage of work done in idle time."
+  }
+}
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/value_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/value_unittest.py
new file mode 100644
index 0000000..a06300e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/value/value_unittest.py
@@ -0,0 +1,337 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import os
+import unittest
+
+from telemetry import story
+from telemetry import page as page_module
+from telemetry import value
+
+
+class TestBase(unittest.TestCase):
+  def setUp(self):
+    story_set = story.StorySet(base_dir=os.path.dirname(__file__))
+    story_set.AddStory(
+        page_module.Page("http://www.bar.com/", story_set, story_set.base_dir))
+    story_set.AddStory(
+        page_module.Page("http://www.baz.com/", story_set, story_set.base_dir))
+    story_set.AddStory(
+        page_module.Page("http://www.foo.com/", story_set, story_set.base_dir))
+    self.story_set = story_set
+
+  @property
+  def pages(self):
+    return self.story_set.stories
+
+class ValueForTest(value.Value):
+  @classmethod
+  def MergeLikeValuesFromSamePage(cls, values):
+    pass
+
+  @classmethod
+  def MergeLikeValuesFromDifferentPages(cls, values):
+    pass
+
+  def GetBuildbotDataType(self, output_context):
+    pass
+
+  def GetBuildbotValue(self):
+    pass
+
+  def GetChartAndTraceNameForComputedSummaryResult(
+      self, trace_tag):
+    pass
+
+  def GetRepresentativeNumber(self):
+    pass
+
+  def GetRepresentativeString(self):
+    pass
+
+  @staticmethod
+  def GetJSONTypeName():
+    pass
+
+class ValueForAsDictTest(ValueForTest):
+  @staticmethod
+  def GetJSONTypeName():
+    return 'baz'
+
+class ValueForFromDictTest(ValueForTest):
+  @staticmethod
+  def FromDict(value_dict, page_dict):
+    kwargs = value.Value.GetConstructorKwArgs(value_dict, page_dict)
+    return ValueForFromDictTest(**kwargs)
+
+  @staticmethod
+  def GetJSONTypeName():
+    return 'value_for_from_dict_test'
+
+class ValueTest(TestBase):
+  def testCompat(self):
+    page0 = self.pages[0]
+    page1 = self.pages[0]
+
+    a = value.Value(page0, 'x', 'unit', important=False, description=None,
+                    tir_label='foo', grouping_keys=None)
+    b = value.Value(page1, 'x', 'unit', important=False, description=None,
+                    tir_label='foo', grouping_keys=None)
+    self.assertTrue(b.IsMergableWith(a))
+
+    a = value.Value(page0, 'x', 'unit', important=False, description=None,
+                    tir_label='foo', grouping_keys=None)
+    b = value.Value(page0, 'x', 'unit', important=False, description=None,
+                     tir_label='bar', grouping_keys=None)
+    self.assertTrue(b.IsMergableWith(a))
+
+  def testIncompat(self):
+    page0 = self.pages[0]
+
+    a = value.Value(page0, 'x', 'unit', important=False, description=None,
+                    tir_label=None, grouping_keys=None)
+    b = value.Value(page0, 'x', 'incompatUnit', important=False,
+                    tir_label=None, description=None, grouping_keys=None)
+    self.assertFalse(b.IsMergableWith(a))
+
+    a = value.Value(page0, 'x', 'unit', important=False, description=None,
+                    tir_label=None, grouping_keys=None)
+    b = value.Value(page0, 'x', 'unit', important=True, description=None,
+                    tir_label=None, grouping_keys=None)
+    self.assertFalse(b.IsMergableWith(a))
+
+    a = value.Value(page0, 'x', 'unit', important=False, description=None,
+                    tir_label=None, grouping_keys=None)
+    b = ValueForTest(page0, 'x', 'unit', important=True, description=None,
+                     tir_label=None, grouping_keys=None)
+    self.assertFalse(b.IsMergableWith(a))
+
+  def testNameMustBeString(self):
+    with self.assertRaises(ValueError):
+      value.Value(None, 42, 'unit', important=False, description=None,
+                  tir_label=None, grouping_keys=None)
+
+  def testUnitsMustBeString(self):
+    with self.assertRaises(ValueError):
+      value.Value(None, 'x', 42, important=False, description=None,
+                  tir_label=None, grouping_keys=None)
+
+  def testImportantMustBeBool(self):
+    with self.assertRaises(ValueError):
+      value.Value(None, 'x', 'unit', important='foo', description=None,
+                  tir_label=None, grouping_keys=None)
+
+  def testDescriptionMustBeStringOrNone(self):
+    with self.assertRaises(ValueError):
+      value.Value(None, 'x', 'unit', important=False, description=42,
+                  tir_label=None, grouping_keys=None)
+
+  def testInteractionRecordMustBeStringOrNone(self):
+    with self.assertRaises(ValueError):
+      value.Value(None, 'x', 'unit', important=False, description=None,
+                  tir_label=42, grouping_keys=None)
+
+  def testGroupingKeysMustBeDictOrNone(self):
+    with self.assertRaises(ValueError):
+      value.Value(None, 'x', 'unit', important=False, description=None,
+                  tir_label=42, grouping_keys='foo')
+
+  def testAsDictBaseKeys(self):
+    v = ValueForAsDictTest(None, 'x', 'unit', important=True, description=None,
+                           tir_label='bar', grouping_keys={'foo': 'baz'})
+    d = v.AsDict()
+
+    self.assertEquals(d, {
+          'name': 'x',
+          'type': 'baz',
+          'units': 'unit',
+          'important': True,
+          'tir_label': 'bar',
+          'grouping_keys': {'foo': 'baz'}
+        })
+
+  def testAsDictWithPage(self):
+    page0 = self.pages[0]
+
+    v = ValueForAsDictTest(page0, 'x', 'unit', important=False,
+                           description=None, tir_label=None, grouping_keys=None)
+    d = v.AsDict()
+
+    self.assertIn('page_id', d)
+
+  def testAsDictWithoutPage(self):
+    v = ValueForAsDictTest(None, 'x', 'unit', important=False, description=None,
+                           tir_label=None, grouping_keys=None)
+    d = v.AsDict()
+
+    self.assertNotIn('page_id', d)
+
+  def testAsDictWithDescription(self):
+    v = ValueForAsDictTest(None, 'x', 'unit', important=False,
+                           description='Some description.',
+                           tir_label=None, grouping_keys=None)
+    d = v.AsDict()
+    self.assertEqual('Some description.', d['description'])
+
+  def testAsDictWithoutDescription(self):
+    v = ValueForAsDictTest(None, 'x', 'unit', important=False, description=None,
+                           tir_label=None, grouping_keys=None)
+    self.assertNotIn('description', v.AsDict())
+
+  def testAsDictWithInteractionRecord(self):
+    v = ValueForAsDictTest(None, 'x', 'unit', important=False,
+                           description='Some description.',
+                           tir_label='foo', grouping_keys=None)
+    d = v.AsDict()
+    self.assertEqual('foo', d['tir_label'])
+
+  def testAsDictWithoutInteractionRecord(self):
+    v = ValueForAsDictTest(None, 'x', 'unit', important=False, description=None,
+                           tir_label=None, grouping_keys=None)
+    self.assertNotIn('tir_label', v.AsDict())
+
+  def testFromDictBaseKeys(self):
+    d = {
+      'type': 'value_for_from_dict_test',
+      'name': 'x',
+      'units': 'unit'
+    }
+
+    v = value.Value.FromDict(d, None)
+    self.assertEquals(v.name, 'x')
+    self.assertTrue(isinstance(v, ValueForFromDictTest))
+    self.assertEquals(v.units, 'unit')
+
+  def testFromDictWithPage(self):
+    page0 = self.pages[0]
+    page_dict = {page0.id: page0}
+
+    d = {
+      'type': 'value_for_from_dict_test',
+      'name': 'x',
+      'units': 'unit',
+      'page_id': page0.id
+    }
+
+    v = value.Value.FromDict(d, page_dict)
+
+    self.assertEquals(v.page.id, page0.id)
+
+  def testFromDictWithPageId0(self):
+    page_dict = {0: 'foo'}
+
+    d = {
+      'type': 'value_for_from_dict_test',
+      'name': 'x',
+      'units': 'unit',
+      'page_id': 0
+    }
+
+    v = value.Value.FromDict(d, page_dict)
+
+    self.assertEquals(v.page, 'foo')
+
+  def testFromDictWithoutPage(self):
+    d = {
+      'type': 'value_for_from_dict_test',
+      'name': 'x',
+      'units': 'unit'
+    }
+
+    v = value.Value.FromDict(d, {})
+
+    self.assertEquals(v.page, None)
+
+  def testFromDictWithDescription(self):
+    d = {
+          'type': 'value_for_from_dict_test',
+          'name': 'x',
+          'units': 'unit',
+          'description': 'foo'
+        }
+
+    v = value.Value.FromDict(d, {})
+    self.assertEquals(v.description, 'foo')
+
+  def testFromDictWithoutDescription(self):
+    d = {
+          'type': 'value_for_from_dict_test',
+          'name': 'x',
+          'units': 'unit'
+        }
+
+    v = value.Value.FromDict(d, {})
+    self.assertEquals(v.description, None)
+
+  def testFromDictWithInteractionRecord(self):
+    d = {
+          'type': 'value_for_from_dict_test',
+          'name': 'x',
+          'units': 'unit',
+          'description': 'foo',
+          'tir_label': 'bar'
+        }
+
+    v = value.Value.FromDict(d, {})
+    self.assertEquals(v.tir_label, 'bar')
+
+  def testFromDictWithoutInteractionRecord(self):
+    d = {
+          'type': 'value_for_from_dict_test',
+          'name': 'x',
+          'units': 'unit'
+        }
+
+    v = value.Value.FromDict(d, {})
+    self.assertEquals(v.tir_label, None)
+
+  def testFromDictWithGroupingKeys(self):
+    d = {
+          'type': 'value_for_from_dict_test',
+          'name': 'x',
+          'units': 'unit',
+          'description': 'foo',
+          'tir_label': 'bar',
+          'grouping_keys': {'foo': 'bar'}
+        }
+
+    v = value.Value.FromDict(d, {})
+    self.assertEquals(v.grouping_keys, {'foo': 'bar'})
+
+  def testFromDictWithoutGroupingKeys(self):
+    d = {
+          'type': 'value_for_from_dict_test',
+          'name': 'x',
+          'units': 'unit'
+        }
+
+    v = value.Value.FromDict(d, {})
+    self.assertEquals(v.grouping_keys, {})
+
+  def testListOfValuesFromListOfDicts(self):
+    d0 = {
+          'type': 'value_for_from_dict_test',
+          'name': 'x',
+          'units': 'unit'
+        }
+    d1 = {
+          'type': 'value_for_from_dict_test',
+          'name': 'y',
+          'units': 'unit'
+        }
+    vs = value.Value.ListOfValuesFromListOfDicts([d0, d1], {})
+    self.assertEquals(vs[0].name, 'x')
+    self.assertEquals(vs[1].name, 'y')
+
+  def testMergedTirLabelForSameLabel(self):
+    v = ValueForTest(None, 'foo', 'ms', False, 'd', 'bar', {})
+
+    tir_label = value.MergedTirLabel([v, v])
+    self.assertEquals(tir_label, 'bar')
+
+  def testMergedTirLabelForDifferentLabels(self):
+    v0 = ValueForTest(None, 'foo', 'ms', False, 'd', 'bar', {})
+    v1 = ValueForTest(None, 'foo', 'ms', False, 'd', 'baz', {})
+
+    tir_label = value.MergedTirLabel([v0, v1])
+    self.assertIsNone(tir_label)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/__init__.py
new file mode 100644
index 0000000..648af8e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/__init__.py
@@ -0,0 +1,7 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""
+The web_perf module provides utilities and measurements for benchmarking web
+app's performance.
+"""
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/__init__.py
new file mode 100644
index 0000000..89406a1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/__init__.py
@@ -0,0 +1,6 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""
+The web_perf.metrics module provides metrics for analyzing web performance.
+"""
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/blob_timeline.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/blob_timeline.py
new file mode 100644
index 0000000..db81f69
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/blob_timeline.py
@@ -0,0 +1,117 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.value import improvement_direction
+from telemetry.value import list_of_scalar_values
+from telemetry.web_perf.metrics import timeline_based_metric
+
+
+WRITE_EVENT_NAME = 'Registry::RegisterBlob'
+READ_EVENT_NAME = 'BlobRequest'
+
+
+class BlobTimelineMetric(timeline_based_metric.TimelineBasedMetric):
+  """BlobTimelineMetric reports timing information about blob storage.
+
+  The following metrics are added to the results:
+    * blob write times (blob_writes)
+    * blob read times (blob_reads)
+  """
+
+  def __init__(self):
+    super(BlobTimelineMetric, self).__init__()
+
+  @staticmethod
+  def IsWriteEvent(event):
+    return event.name == WRITE_EVENT_NAME
+
+  @staticmethod
+  def IsReadEvent(event):
+    return event.name == READ_EVENT_NAME
+
+  @staticmethod
+  def IsEventInInteraction(event, interaction):
+    return interaction.start <= event.start <= interaction.end
+
+  @staticmethod
+  def ThreadDurationIfPresent(event):
+    if event.thread_duration:
+      return event.thread_duration
+    else:
+      return event.duration
+
+  def AddResults(self, model, renderer_thread, interactions, results):
+    assert interactions
+
+    write_events = []
+    read_events = []
+    for event in model.IterAllEvents(
+        event_predicate=lambda e: self.IsWriteEvent(e) or self.IsReadEvent(e)):
+      if self.IsReadEvent(event):
+        read_events.append(event)
+      else:
+        write_events.append(event)
+
+    # Only these private methods are tested for mocking simplicity.
+    self._AddWriteResultsInternal(write_events, interactions, results)
+    self._AddReadResultsInternal(read_events, interactions, results)
+
+  def _AddWriteResultsInternal(self, events, interactions, results):
+    writes = []
+    for event in events:
+      if (self.IsWriteEvent(event) and
+          any(self.IsEventInInteraction(event, interaction)
+              for interaction in interactions)):
+        writes.append(self.ThreadDurationIfPresent(event))
+    if writes:
+      results.AddValue(list_of_scalar_values.ListOfScalarValues(
+          page=results.current_page,
+          tir_label=interactions[0].label,
+          name='blob-writes',
+          units='ms',
+          values=writes,
+          description='List of durations of blob writes.',
+          improvement_direction=improvement_direction.DOWN))
+    else:
+      results.AddValue(list_of_scalar_values.ListOfScalarValues(
+          page=results.current_page,
+          tir_label=interactions[0].label,
+          name='blob-writes',
+          units='ms',
+          values=None,
+          none_value_reason='No blob write events found for this interaction.',
+          improvement_direction=improvement_direction.DOWN))
+
+  def _AddReadResultsInternal(self, events, interactions, results):
+    reads = dict()
+    for event in events:
+      if (not self.IsReadEvent(event) or
+          not any(self.IsEventInInteraction(event, interaction)
+                 for interaction in interactions)):
+        continue
+      # Every blob has unique UUID.  To get the total time for reading
+      # a blob, we add up the time of all events with the same blob UUID.
+      uuid = event.args['uuid']
+      if uuid not in reads:
+        reads[uuid] = 0
+      reads[uuid] += self.ThreadDurationIfPresent(event)
+
+    if reads:
+      results.AddValue(list_of_scalar_values.ListOfScalarValues(
+          page=results.current_page,
+          tir_label=interactions[0].label,
+          name='blob-reads',
+          units='ms',
+          values=reads.values(),
+          description='List of read times for blobs.',
+          improvement_direction=improvement_direction.DOWN))
+    else:
+      results.AddValue(list_of_scalar_values.ListOfScalarValues(
+          page=results.current_page,
+          tir_label=interactions[0].label,
+          name='blob-reads',
+          units='ms',
+          values=None,
+          none_value_reason='No blob read events found for this interaction.',
+          improvement_direction=improvement_direction.DOWN))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/blob_timeline_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/blob_timeline_unittest.py
new file mode 100644
index 0000000..7f2efb7
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/blob_timeline_unittest.py
@@ -0,0 +1,124 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from collections import namedtuple
+from telemetry.internal.results import page_test_results
+from telemetry.page import page
+from telemetry.web_perf.metrics import blob_timeline
+from telemetry.web_perf import timeline_interaction_record
+
+
+FakeEvent = namedtuple('Event', 'name, start, end, thread_duration, args')
+Interaction = timeline_interaction_record.TimelineInteractionRecord
+TEST_INTERACTION_LABEL = 'Action_TestInteraction'
+WRITE_EVENT_NAME = 'Registry::RegisterBlob'
+READ_EVENT_NAME = 'BlobRequest'
+
+
+def GetBlobMetrics(events, interactions):
+  results = page_test_results.PageTestResults()
+  test_page = page.Page('file://blank.html')
+  results.WillRunPage(test_page)
+  blob_timeline.BlobTimelineMetric()._AddWriteResultsInternal(
+      events, interactions, results)  # pylint:disable=protected-access
+  blob_timeline.BlobTimelineMetric()._AddReadResultsInternal(
+      events, interactions, results)  # pylint:disable=protected-access
+  return_dict = dict((value.name, value.values) for value in
+                     results.current_page_run.values)
+  results.DidRunPage(test_page)
+  return return_dict
+
+def FakeWriteEvent(start, end, thread_duration=None):
+  if not thread_duration:
+    thread_duration = end - start
+  return FakeEvent(blob_timeline.WRITE_EVENT_NAME,
+                   start, end, thread_duration, {'uuid':'fakeuuid'})
+
+def FakeReadEvent(start, end, uuid, thread_duration=None):
+  if not thread_duration:
+    thread_duration = end - start
+  return FakeEvent(blob_timeline.READ_EVENT_NAME,
+                   start, end, thread_duration, {'uuid': uuid})
+
+def TestInteraction(start, end):
+  return Interaction(TEST_INTERACTION_LABEL, start, end)
+
+
+class BlobTimelineMetricUnitTest(unittest.TestCase):
+  def testWriteMetric(self):
+    events = [FakeWriteEvent(0, 1),
+              FakeWriteEvent(9, 11),
+              FakeWriteEvent(10, 13),
+              FakeWriteEvent(20, 24),
+              FakeWriteEvent(21, 26),
+              FakeWriteEvent(29, 35),
+              FakeWriteEvent(30, 37),
+              FakeWriteEvent(40, 48),
+              FakeWriteEvent(41, 50),
+              FakeEvent('something', 10, 13, 3, {}),
+              FakeEvent('FrameView::something', 20, 24, 4, {}),
+              FakeEvent('SomeThing::performLayout', 30, 37, 7, {}),
+              FakeEvent('something else', 40, 48, 8, {})]
+    interactions = [TestInteraction(10, 20),
+                    TestInteraction(30, 40)]
+
+    # The first event starts before the first interaction, so it is ignored.
+    # The second event starts before the first interaction, so it is ignored.
+    # The third event starts during the first interaction, and its duration is
+    # 13 - 10 = 3.
+    # The fourth event starts during the first interaction, and its duration is
+    # 24 - 20 = 4.
+    # The fifth event starts between the two interactions, so it is ignored.
+    # The sixth event starts between the two interactions, so it is ignored.
+    # The seventh event starts during the second interaction, and its duration
+    # is 37 - 30 = 7.
+    # The eighth event starts during the second interaction and its duration is
+    # 48 - 40 = 8.
+    # The ninth event starts after the last interaction, so it is ignored.
+    # The rest of the events are not layout events, so they are ignored.
+    self.assertEqual({'blob-reads': None, 'blob-writes': [3, 4, 7, 8]},
+        GetBlobMetrics(events, interactions))
+
+  def testReadMetric(self):
+    events = [FakeReadEvent(0, 1, 'a'),
+              FakeReadEvent(9, 11, 'a'),
+              FakeReadEvent(10, 13, 'b', 1), # counts
+              FakeReadEvent(15, 18, 'b'),    # counts
+              FakeReadEvent(21, 26, 'b'),
+              FakeReadEvent(29, 35, 'c'),
+              FakeReadEvent(31, 32, 'e'),    # counts
+              FakeReadEvent(34, 36, 'e', 1), # counts
+              FakeReadEvent(32, 37, 'd'),    # counts
+              FakeEvent('something', 10, 13, 3, {}),
+              FakeEvent('something else', 40, 48, 8, {})]
+    interactions = [TestInteraction(10, 20),
+                    TestInteraction(30, 40)]
+
+    # We ignore events outside of the interaction intervals, and we use the
+    # beginning of the first event of the interval and the end of the last
+    # event.
+    # 18 - 10 = 8
+    # 37 - 32 = 5
+    self.assertEqual({'blob-reads': [4, 2, 5], 'blob-writes': None},
+        GetBlobMetrics(events, interactions))
+
+  def testReadAndWriteMetrics(self):
+    events = [FakeReadEvent(0, 1, 'a'),
+              FakeReadEvent(9, 11, 'a'),
+              FakeReadEvent(10, 13, 'b'),     # counts
+              FakeWriteEvent(15, 18),         # counts
+              FakeReadEvent(21, 26, 'c'),
+              FakeReadEvent(29, 35, 'd'),
+              FakeWriteEvent(31, 34, 1), # counts
+              FakeReadEvent(32, 33, 'e'),     # counts
+              FakeReadEvent(34, 35, 'e'),     # counts
+              FakeEvent('something', 31, 33, 2, {})]
+    interactions = [TestInteraction(10, 20),
+                    TestInteraction(30, 35)]
+
+    # We use the read events in the interactions, so the same as the test above.
+    self.assertEqual({'blob-reads': [3, 2], 'blob-writes': [3, 1]},
+      GetBlobMetrics(events, interactions))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/gpu_timeline.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/gpu_timeline.py
new file mode 100644
index 0000000..ff362be
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/gpu_timeline.py
@@ -0,0 +1,240 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import collections
+import math
+import sys
+
+from telemetry.timeline import model as model_module
+from telemetry.value import improvement_direction
+from telemetry.value import list_of_scalar_values
+from telemetry.value import scalar
+from telemetry.web_perf.metrics import timeline_based_metric
+
+TOPLEVEL_GL_CATEGORY = 'gpu_toplevel'
+TOPLEVEL_SERVICE_CATEGORY = 'disabled-by-default-gpu.service'
+TOPLEVEL_DEVICE_CATEGORY = 'disabled-by-default-gpu.device'
+
+SERVICE_FRAME_END_MARKER = (TOPLEVEL_SERVICE_CATEGORY, 'SwapBuffer')
+DEVICE_FRAME_END_MARKER = (TOPLEVEL_DEVICE_CATEGORY, 'SwapBuffer')
+
+TRACKED_GL_CONTEXT_NAME = {'RenderCompositor': 'render_compositor',
+                           'BrowserCompositor': 'browser_compositor',
+                           'Compositor': 'browser_compositor'}
+
+
+def _CalculateFrameTimes(events_per_frame, event_data_func):
+  """Given a list of events per frame and a function to extract event time data,
+     returns a list of frame times."""
+  times_per_frame = []
+  for event_list in events_per_frame:
+    event_times = [event_data_func(event) for event in event_list]
+    times_per_frame.append(sum(event_times))
+  return times_per_frame
+
+
+def _CPUFrameTimes(events_per_frame):
+  """Given a list of events per frame, returns a list of CPU frame times."""
+  # CPU event frames are calculated using the event thread duration.
+  # Some platforms do not support thread_duration, convert those to 0.
+  return _CalculateFrameTimes(events_per_frame,
+                              lambda event: event.thread_duration or 0)
+
+
+def _GPUFrameTimes(events_per_frame):
+  """Given a list of events per frame, returns a list of GPU frame times."""
+  # GPU event frames are asynchronous slices which use the event duration.
+  return _CalculateFrameTimes(events_per_frame,
+                              lambda event: event.duration)
+
+
+def TimelineName(name, source_type, value_type):
+  """Constructs the standard name given in the timeline.
+
+  Args:
+    name: The name of the timeline, for example "total", or "render_compositor".
+    source_type: One of "cpu", "gpu" or None. None is only used for total times.
+    value_type: the type of value. For example "mean", "stddev"...etc.
+  """
+  if source_type:
+    return '%s_%s_%s_time' % (name, value_type, source_type)
+  else:
+    return '%s_%s_time' % (name, value_type)
+
+
+class GPUTimelineMetric(timeline_based_metric.TimelineBasedMetric):
+  """Computes GPU based metrics."""
+
+  def __init__(self):
+    super(GPUTimelineMetric, self).__init__()
+
+  def AddResults(self, model, _, interaction_records, results):
+    self.VerifyNonOverlappedRecords(interaction_records)
+    service_times = self._CalculateGPUTimelineData(model)
+    for value_item, durations in service_times.iteritems():
+      count = len(durations)
+      avg = 0.0
+      stddev = 0.0
+      maximum = 0.0
+      if count:
+        avg = sum(durations) / count
+        stddev = math.sqrt(sum((d - avg) ** 2 for d in durations) / count)
+        maximum = max(durations)
+
+      name, src = value_item
+
+      if src:
+        frame_times_name = '%s_%s_frame_times' % (name, src)
+      else:
+        frame_times_name = '%s_frame_times' % (name)
+
+      if durations:
+        results.AddValue(list_of_scalar_values.ListOfScalarValues(
+            results.current_page, frame_times_name, 'ms', durations,
+            tir_label=interaction_records[0].label,
+            improvement_direction=improvement_direction.DOWN))
+
+      results.AddValue(scalar.ScalarValue(
+          results.current_page, TimelineName(name, src, 'max'), 'ms', maximum,
+          tir_label=interaction_records[0].label,
+          improvement_direction=improvement_direction.DOWN))
+      results.AddValue(scalar.ScalarValue(
+          results.current_page, TimelineName(name, src, 'mean'), 'ms', avg,
+          tir_label=interaction_records[0].label,
+          improvement_direction=improvement_direction.DOWN))
+      results.AddValue(scalar.ScalarValue(
+          results.current_page, TimelineName(name, src, 'stddev'), 'ms', stddev,
+          tir_label=interaction_records[0].label,
+          improvement_direction=improvement_direction.DOWN))
+
+  def _CalculateGPUTimelineData(self, model):
+    """Uses the model and calculates the times for various values for each
+       frame. The return value will be a dictionary of the following format:
+         {
+           (EVENT_NAME1, SRC1_TYPE): [FRAME0_TIME, FRAME1_TIME...etc.],
+           (EVENT_NAME2, SRC2_TYPE): [FRAME0_TIME, FRAME1_TIME...etc.],
+         }
+
+       Events:
+         swap - The time in milliseconds between each swap marker.
+         total - The amount of time spent in the renderer thread.
+         TRACKED_NAMES: Using the TRACKED_GL_CONTEXT_NAME dict, we
+                        include the traces per frame for the
+                        tracked name.
+       Source Types:
+         None - This will only be valid for the "swap" event.
+         cpu - For an event, the "cpu" source type signifies time spent on the
+               gpu thread using the CPU. This uses the "gpu.service" markers.
+         gpu - For an event, the "gpu" source type signifies time spent on the
+               gpu thread using the GPU. This uses the "gpu.device" markers.
+    """
+    all_service_events = []
+    current_service_frame_end = sys.maxint
+    current_service_events = []
+
+    all_device_events = []
+    current_device_frame_end = sys.maxint
+    current_device_events = []
+
+    tracked_events = {}
+    tracked_events.update(
+        dict([((value, 'cpu'), [])
+              for value in TRACKED_GL_CONTEXT_NAME.itervalues()]))
+    tracked_events.update(
+        dict([((value, 'gpu'), [])
+              for value in TRACKED_GL_CONTEXT_NAME.itervalues()]))
+
+    # These will track traces within the current frame.
+    current_tracked_service_events = collections.defaultdict(list)
+    current_tracked_device_events = collections.defaultdict(list)
+
+    event_iter = model.IterAllEvents(
+        event_type_predicate=model_module.IsSliceOrAsyncSlice)
+    for event in event_iter:
+      # Look for frame end markers
+      if (event.category, event.name) == SERVICE_FRAME_END_MARKER:
+        current_service_frame_end = event.end
+      elif (event.category, event.name) == DEVICE_FRAME_END_MARKER:
+        current_device_frame_end = event.end
+
+      # Track all other toplevel gl category markers
+      elif event.args.get('gl_category', None) == TOPLEVEL_GL_CATEGORY:
+        base_name = event.name
+        dash_index = base_name.rfind('-')
+        if dash_index != -1:
+          base_name = base_name[:dash_index]
+        tracked_name = TRACKED_GL_CONTEXT_NAME.get(base_name, None)
+
+        if event.category == TOPLEVEL_SERVICE_CATEGORY:
+          # Check if frame has ended.
+          if event.start >= current_service_frame_end:
+            if current_service_events:
+              all_service_events.append(current_service_events)
+              for value in TRACKED_GL_CONTEXT_NAME.itervalues():
+                tracked_events[(value, 'cpu')].append(
+                    current_tracked_service_events[value])
+            current_service_events = []
+            current_service_frame_end = sys.maxint
+            current_tracked_service_events.clear()
+
+          current_service_events.append(event)
+          if tracked_name:
+            current_tracked_service_events[tracked_name].append(event)
+
+        elif event.category == TOPLEVEL_DEVICE_CATEGORY:
+          # Check if frame has ended.
+          if event.start >= current_device_frame_end:
+            if current_device_events:
+              all_device_events.append(current_device_events)
+              for value in TRACKED_GL_CONTEXT_NAME.itervalues():
+                tracked_events[(value, 'gpu')].append(
+                    current_tracked_device_events[value])
+            current_device_events = []
+            current_device_frame_end = sys.maxint
+            current_tracked_device_events.clear()
+
+          current_device_events.append(event)
+          if tracked_name:
+            current_tracked_device_events[tracked_name].append(event)
+
+    # Append Data for Last Frame.
+    if current_service_events:
+      all_service_events.append(current_service_events)
+      for value in TRACKED_GL_CONTEXT_NAME.itervalues():
+        tracked_events[(value, 'cpu')].append(
+            current_tracked_service_events[value])
+    if current_device_events:
+      all_device_events.append(current_device_events)
+      for value in TRACKED_GL_CONTEXT_NAME.itervalues():
+        tracked_events[(value, 'gpu')].append(
+            current_tracked_device_events[value])
+
+    # Calculate Mean Frame Time for the CPU side.
+    frame_times = []
+    if all_service_events:
+      prev_frame_end = all_service_events[0][0].start
+      for event_list in all_service_events:
+        last_service_event_in_frame = event_list[-1]
+        frame_times.append(last_service_event_in_frame.end - prev_frame_end)
+        prev_frame_end = last_service_event_in_frame.end
+
+    # Create the timeline data dictionary for service side traces.
+    total_frame_value = ('swap', None)
+    cpu_frame_value = ('total', 'cpu')
+    gpu_frame_value = ('total', 'gpu')
+    timeline_data = {}
+    timeline_data[total_frame_value] = frame_times
+    timeline_data[cpu_frame_value] = _CPUFrameTimes(all_service_events)
+    for value in TRACKED_GL_CONTEXT_NAME.itervalues():
+      cpu_value = (value, 'cpu')
+      timeline_data[cpu_value] = _CPUFrameTimes(tracked_events[cpu_value])
+
+    # Add in GPU side traces if it was supported (IE. device traces exist).
+    if all_device_events:
+      timeline_data[gpu_frame_value] = _GPUFrameTimes(all_device_events)
+      for value in TRACKED_GL_CONTEXT_NAME.itervalues():
+        gpu_value = (value, 'gpu')
+        tracked_gpu_event = tracked_events[gpu_value]
+        timeline_data[gpu_value] = _GPUFrameTimes(tracked_gpu_event)
+
+    return timeline_data
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/gpu_timeline_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/gpu_timeline_unittest.py
new file mode 100644
index 0000000..2af5b10
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/gpu_timeline_unittest.py
@@ -0,0 +1,313 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from telemetry.testing import test_page_test_results
+from telemetry.timeline import async_slice as async_slice_module
+from telemetry.timeline import model as model_module
+from telemetry.timeline import slice as slice_module
+from telemetry.web_perf.metrics import gpu_timeline
+from telemetry.web_perf import timeline_interaction_record as tir_module
+
+SERVICE_FRAME_END_CATEGORY, SERVICE_FRAME_END_NAME = \
+    gpu_timeline.SERVICE_FRAME_END_MARKER
+
+DEVICE_FRAME_END_CATEGORY, DEVICE_FRAME_END_NAME = \
+    gpu_timeline.DEVICE_FRAME_END_MARKER
+
+INTERACTION_RECORDS = [tir_module.TimelineInteractionRecord("test-record",
+                                                            0,
+                                                            float('inf'))]
+
+
+def _CreateGPUSlices(parent_thread, name, start_time, duration, offset=0):
+  args = {'gl_category': gpu_timeline.TOPLEVEL_GL_CATEGORY}
+  return (slice_module.Slice(parent_thread,
+                             gpu_timeline.TOPLEVEL_SERVICE_CATEGORY,
+                             name, start_time,
+                             args=args,
+                             duration=duration,
+                             thread_duration=duration),
+          async_slice_module.AsyncSlice(gpu_timeline.TOPLEVEL_DEVICE_CATEGORY,
+                             name, start_time + offset,
+                             args=args,
+                             duration=duration))
+
+def _CreateFrameEndSlices(parent_thread, start_time, duration, offset=0):
+  args = {'gl_category': gpu_timeline.TOPLEVEL_GL_CATEGORY}
+  return (slice_module.Slice(parent_thread,
+                             SERVICE_FRAME_END_CATEGORY,
+                             SERVICE_FRAME_END_NAME,
+                             start_time,
+                             args=args,
+                             duration=duration,
+                             thread_duration=duration),
+          async_slice_module.AsyncSlice(DEVICE_FRAME_END_CATEGORY,
+                             DEVICE_FRAME_END_NAME,
+                             start_time + offset,
+                             args=args,
+                             duration=duration))
+
+
+def _AddSliceToThread(parent_thread, slice_item):
+  if isinstance(slice_item, slice_module.Slice):
+    parent_thread.PushSlice(slice_item)
+  elif isinstance(slice_item, async_slice_module.AsyncSlice):
+    parent_thread.AddAsyncSlice(slice_item)
+  else:
+    assert False, "Invalid Slice Item Type: %s" % type(slice_item)
+
+
+class GPUTimelineTest(unittest.TestCase):
+  def GetResults(self, metric, model, renderer_thread, interaction_records):
+    results = test_page_test_results.TestPageTestResults(self)
+    metric.AddResults(model, renderer_thread, interaction_records, results)
+    return results
+
+  def testExpectedResults(self):
+    """Test a simply trace will output all expected results."""
+    model = model_module.TimelineModel()
+    test_thread = model.GetOrCreateProcess(1).GetOrCreateThread(2)
+    for slice_item in _CreateGPUSlices(test_thread, 'test_item', 100, 10):
+      _AddSliceToThread(test_thread, slice_item)
+    model.FinalizeImport()
+
+    metric = gpu_timeline.GPUTimelineMetric()
+    results = self.GetResults(metric, model=model, renderer_thread=test_thread,
+                              interaction_records=INTERACTION_RECORDS)
+
+    for name, src_type in (('swap', None), ('total', 'cpu'), ('total', 'gpu')):
+      results.AssertHasPageSpecificScalarValue(
+          gpu_timeline.TimelineName(name, src_type, 'max'), 'ms', 10)
+      results.AssertHasPageSpecificScalarValue(
+          gpu_timeline.TimelineName(name, src_type, 'mean'), 'ms', 10)
+      results.AssertHasPageSpecificScalarValue(
+          gpu_timeline.TimelineName(name, src_type, 'stddev'), 'ms', 0)
+
+    for tracked_name in gpu_timeline.TRACKED_GL_CONTEXT_NAME.values():
+      for source_type in ('cpu', 'gpu'):
+        results.AssertHasPageSpecificScalarValue(
+            gpu_timeline.TimelineName(tracked_name, source_type, 'max'),
+                                      'ms', 0)
+        results.AssertHasPageSpecificScalarValue(
+            gpu_timeline.TimelineName(tracked_name, source_type, 'mean'),
+                                      'ms', 0)
+        results.AssertHasPageSpecificScalarValue(
+            gpu_timeline.TimelineName(tracked_name, source_type, 'stddev'),
+                                      'ms', 0)
+
+  def testNoDeviceTraceResults(self):
+    """Test expected results when missing device traces."""
+    model = model_module.TimelineModel()
+    test_thread = model.GetOrCreateProcess(1).GetOrCreateThread(2)
+    service_slice, _ = _CreateGPUSlices(test_thread, 'test_item', 100, 10)
+    _AddSliceToThread(test_thread, service_slice)
+    model.FinalizeImport()
+
+    metric = gpu_timeline.GPUTimelineMetric()
+    results = self.GetResults(metric, model=model, renderer_thread=test_thread,
+                              interaction_records=INTERACTION_RECORDS)
+
+    for name, source_type in (('swap', None), ('total', 'cpu')):
+      results.AssertHasPageSpecificScalarValue(
+          gpu_timeline.TimelineName(name, source_type, 'max'), 'ms', 10)
+      results.AssertHasPageSpecificScalarValue(
+          gpu_timeline.TimelineName(name, source_type, 'mean'), 'ms', 10)
+      results.AssertHasPageSpecificScalarValue(
+          gpu_timeline.TimelineName(name, source_type, 'stddev'), 'ms', 0)
+
+    self.assertRaises(AssertionError, results.GetPageSpecificValueNamed,
+                      gpu_timeline.TimelineName('total', 'gpu', 'max'))
+    self.assertRaises(AssertionError, results.GetPageSpecificValueNamed,
+                      gpu_timeline.TimelineName('total', 'gpu', 'mean'))
+    self.assertRaises(AssertionError, results.GetPageSpecificValueNamed,
+                      gpu_timeline.TimelineName('total', 'gpu', 'stddev'))
+
+    for name in gpu_timeline.TRACKED_GL_CONTEXT_NAME.values():
+      results.AssertHasPageSpecificScalarValue(
+          gpu_timeline.TimelineName(name, 'cpu', 'max'), 'ms', 0)
+      results.AssertHasPageSpecificScalarValue(
+          gpu_timeline.TimelineName(name, 'cpu', 'mean'), 'ms', 0)
+      results.AssertHasPageSpecificScalarValue(
+          gpu_timeline.TimelineName(name, 'cpu', 'stddev'), 'ms', 0)
+
+      self.assertRaises(AssertionError, results.GetPageSpecificValueNamed,
+                        gpu_timeline.TimelineName(name, 'gpu', 'max'))
+      self.assertRaises(AssertionError, results.GetPageSpecificValueNamed,
+                        gpu_timeline.TimelineName(name, 'gpu', 'mean'))
+      self.assertRaises(AssertionError, results.GetPageSpecificValueNamed,
+                        gpu_timeline.TimelineName(name, 'gpu', 'stddev'))
+
+  def testFrameSeparation(self):
+    """Test frames are correctly calculated using the frame end marker."""
+    model = model_module.TimelineModel()
+    test_thread = model.GetOrCreateProcess(1).GetOrCreateThread(2)
+
+    # First frame is 10 seconds.
+    for slice_item in _CreateGPUSlices(test_thread, 'test_item', 100, 10):
+      _AddSliceToThread(test_thread, slice_item)
+
+    # Mark frame end.
+    for slice_item in _CreateFrameEndSlices(test_thread, 105, 5):
+      _AddSliceToThread(test_thread, slice_item)
+
+    # Second frame is 20 seconds.
+    for slice_item in _CreateGPUSlices(test_thread, 'test_item', 110, 20):
+      _AddSliceToThread(test_thread, slice_item)
+
+    model.FinalizeImport()
+
+    metric = gpu_timeline.GPUTimelineMetric()
+    results = self.GetResults(metric, model=model, renderer_thread=test_thread,
+                              interaction_records=INTERACTION_RECORDS)
+
+    for name, source_type in (('swap', None),
+                              ('total', 'cpu'),
+                              ('total', 'gpu')):
+      results.AssertHasPageSpecificScalarValue(
+          gpu_timeline.TimelineName(name, source_type, 'max'), 'ms', 20)
+      results.AssertHasPageSpecificScalarValue(
+          gpu_timeline.TimelineName(name, source_type, 'mean'), 'ms', 15)
+      results.AssertHasPageSpecificScalarValue(
+          gpu_timeline.TimelineName(name, source_type, 'stddev'), 'ms', 5)
+
+  def testFrameSeparationBeforeMarker(self):
+    """Test frames are correctly calculated using the frame end marker."""
+    model = model_module.TimelineModel()
+    test_thread = model.GetOrCreateProcess(1).GetOrCreateThread(2)
+
+    # Mark frame end.
+    for slice_item in _CreateFrameEndSlices(test_thread, 105, 5):
+      _AddSliceToThread(test_thread, slice_item)
+
+    # First frame is 10 seconds.
+    for slice_item in _CreateGPUSlices(test_thread, 'test_item', 100, 10):
+      _AddSliceToThread(test_thread, slice_item)
+
+    # Second frame is 20 seconds.
+    for slice_item in _CreateGPUSlices(test_thread, 'test_item', 110, 20):
+      _AddSliceToThread(test_thread, slice_item)
+
+    model.FinalizeImport()
+
+    metric = gpu_timeline.GPUTimelineMetric()
+    results = self.GetResults(metric, model=model, renderer_thread=test_thread,
+                              interaction_records=INTERACTION_RECORDS)
+
+    for name, src_type in (('swap', None), ('total', 'cpu'), ('total', 'gpu')):
+      results.AssertHasPageSpecificScalarValue(
+          gpu_timeline.TimelineName(name, src_type, 'max'), 'ms', 20)
+      results.AssertHasPageSpecificScalarValue(
+          gpu_timeline.TimelineName(name, src_type, 'mean'), 'ms', 15)
+      results.AssertHasPageSpecificScalarValue(
+          gpu_timeline.TimelineName(name, src_type, 'stddev'), 'ms', 5)
+
+  def testTrackedNameTraces(self):
+    """Be sure tracked names are being recorded correctly."""
+    self.assertGreater(len(gpu_timeline.TRACKED_GL_CONTEXT_NAME), 0)
+
+    marker, result = gpu_timeline.TRACKED_GL_CONTEXT_NAME.iteritems().next()
+
+    model = model_module.TimelineModel()
+    test_thread = model.GetOrCreateProcess(1).GetOrCreateThread(2)
+    for slice_item in _CreateGPUSlices(test_thread, marker, 100, 10):
+      _AddSliceToThread(test_thread, slice_item)
+    model.FinalizeImport()
+
+    metric = gpu_timeline.GPUTimelineMetric()
+    results = self.GetResults(metric, model=model, renderer_thread=test_thread,
+                              interaction_records=INTERACTION_RECORDS)
+
+    for source_type in ('cpu', 'gpu'):
+      results.AssertHasPageSpecificScalarValue(
+          gpu_timeline.TimelineName(result, source_type, 'max'),
+          'ms', 10)
+      results.AssertHasPageSpecificScalarValue(
+          gpu_timeline.TimelineName(result, source_type, 'mean'),
+          'ms', 10)
+      results.AssertHasPageSpecificScalarValue(
+          gpu_timeline.TimelineName(result, source_type, 'stddev'),
+          'ms', 0)
+
+  def testTrackedNameWithContextIDTraces(self):
+    """Be sure tracked names with context IDs are recorded correctly."""
+    self.assertGreater(len(gpu_timeline.TRACKED_GL_CONTEXT_NAME), 0)
+
+    marker, result = gpu_timeline.TRACKED_GL_CONTEXT_NAME.iteritems().next()
+    context_id = '-0x1234'
+
+    model = model_module.TimelineModel()
+    test_thread = model.GetOrCreateProcess(1).GetOrCreateThread(2)
+    for slice_item in _CreateGPUSlices(test_thread, marker + context_id,
+                                       100, 10):
+      _AddSliceToThread(test_thread, slice_item)
+    model.FinalizeImport()
+
+    metric = gpu_timeline.GPUTimelineMetric()
+    results = self.GetResults(metric, model=model, renderer_thread=test_thread,
+                              interaction_records=INTERACTION_RECORDS)
+
+    for source_type in ('cpu', 'gpu'):
+      results.AssertHasPageSpecificScalarValue(
+          gpu_timeline.TimelineName(result, source_type, 'max'),
+          'ms', 10)
+      results.AssertHasPageSpecificScalarValue(
+          gpu_timeline.TimelineName(result, source_type, 'mean'),
+          'ms', 10)
+      results.AssertHasPageSpecificScalarValue(
+          gpu_timeline.TimelineName(result, source_type, 'stddev'),
+          'ms', 0)
+
+  def testOutOfOrderDeviceTraces(self):
+    """Out of order device traces are still matched up to correct services."""
+    self.assertGreaterEqual(len(gpu_timeline.TRACKED_GL_CONTEXT_NAME), 2)
+
+    tracked_names_iter = gpu_timeline.TRACKED_GL_CONTEXT_NAME.iteritems()
+    marker1_name, result1_name = tracked_names_iter.next()
+    result2_name = result1_name
+    while result2_name == result1_name:
+      marker2_name, result2_name = tracked_names_iter.next()
+
+    model = model_module.TimelineModel()
+    test_thread = model.GetOrCreateProcess(1).GetOrCreateThread(2)
+
+    # marker1 lasts for 10 seconds.
+    service_item1, device_item1 = _CreateGPUSlices(test_thread, marker1_name,
+                                                   100, 10)
+    # marker2 lasts for 20 seconds.
+    service_item2, device_item2 = _CreateGPUSlices(test_thread, marker2_name,
+                                                   200, 20)
+
+    # Append out of order
+    _AddSliceToThread(test_thread, service_item1)
+    _AddSliceToThread(test_thread, service_item2)
+    _AddSliceToThread(test_thread, device_item2)
+    _AddSliceToThread(test_thread, device_item1)
+
+    model.FinalizeImport()
+
+    metric = gpu_timeline.GPUTimelineMetric()
+    results = self.GetResults(metric, model=model, renderer_thread=test_thread,
+                              interaction_records=INTERACTION_RECORDS)
+
+    for source_type in ('cpu', 'gpu'):
+      results.AssertHasPageSpecificScalarValue(
+          gpu_timeline.TimelineName(result1_name, source_type, 'max'),
+          'ms', 10)
+      results.AssertHasPageSpecificScalarValue(
+          gpu_timeline.TimelineName(result1_name, source_type, 'mean'),
+          'ms', 10)
+      results.AssertHasPageSpecificScalarValue(
+          gpu_timeline.TimelineName(result1_name, source_type, 'stddev'),
+          'ms', 0)
+      results.AssertHasPageSpecificScalarValue(
+          gpu_timeline.TimelineName(result2_name, source_type, 'max'),
+          'ms', 20)
+      results.AssertHasPageSpecificScalarValue(
+          gpu_timeline.TimelineName(result2_name, source_type, 'mean'),
+          'ms', 20)
+      results.AssertHasPageSpecificScalarValue(
+          gpu_timeline.TimelineName(result2_name, source_type, 'stddev'),
+          'ms', 0)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/indexeddb_timeline.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/indexeddb_timeline.py
new file mode 100644
index 0000000..1dc8382
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/indexeddb_timeline.py
@@ -0,0 +1,80 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+from telemetry.web_perf.metrics import timeline_based_metric
+from telemetry.web_perf.metrics.trace_event_stats import TraceEventStats
+from telemetry.web_perf.metrics.trace_event_stats import TraceEventStatsInput
+
+
+class IndexedDBTimelineMetric(timeline_based_metric.TimelineBasedMetric):
+  """Metrics for IndexedDB operations.
+  """
+
+  def __init__(self):
+    super(IndexedDBTimelineMetric, self).__init__()
+    self._stats = TraceEventStats()
+
+    self._stats.AddInput(TraceEventStatsInput(
+      event_category='IndexedDB',
+      event_name='IndexedDBDatabase::GetOperation',
+      metric_name='idb-gets',
+      metric_description='The duration of all "get" ops in IndexedDB',
+      units='ms',
+      process_name='Browser'))
+
+    self._stats.AddInput(TraceEventStatsInput(
+      event_category='IndexedDB',
+      event_name='IndexedDBDatabase::PutOperation',
+      metric_name='idb-puts',
+      metric_description='The duration of all "put" ops in IndexedDB',
+      units='ms',
+      process_name='Browser'))
+
+    self._stats.AddInput(TraceEventStatsInput(
+      event_category='IndexedDB',
+      event_name='IndexedDBFactoryImpl::Open',
+      metric_name='idb-opens',
+      metric_description='The duration of all "open" ops in IndexedDB',
+      units='ms',
+      process_name='Browser'))
+
+    self._stats.AddInput(TraceEventStatsInput(
+      event_category='IndexedDB',
+      event_name='IndexedDBTransaction::Commit',
+      metric_name='idb-transaction-commits',
+      metric_description=('The duration of all "commit" ops of ' +
+                               'transactions in IndexedDB.'),
+      units='ms',
+      process_name='Browser'))
+
+    self._stats.AddInput(TraceEventStatsInput(
+      event_category='IndexedDB',
+      event_name='IndexedDBFactoryImpl::DeleteDatabase',
+      metric_name='idb-database-deletes',
+      metric_description=('The duration of all "delete" ops of ' +
+                               'IndexedDB databases.'),
+      units='ms',
+      process_name='Browser'))
+
+    self._stats.AddInput(TraceEventStatsInput(
+      event_category='IndexedDB',
+      event_name='IndexedDBDatabase::OpenCursorOperation',
+      metric_name='idb-cursor-opens',
+      metric_description=('The duration of all "open" ops of ' +
+                               'IndexedDB cursors.'),
+      units='ms',
+      process_name='Browser'))
+
+    self._stats.AddInput(TraceEventStatsInput(
+      event_category='IndexedDB',
+      event_name='IndexedDBCursor::CursorIterationOperation',
+      metric_name='idb-cursor-iterations',
+      metric_description=('The duration of all "iteration" ops of ' +
+                               'IndexedDB cursors.'),
+      units='ms',
+      process_name='Browser'))
+
+  def AddResults(self, model, renderer_process, interactions, results):
+    self._stats.AddResults(model, renderer_process, interactions, results)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/jitter_timeline.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/jitter_timeline.py
new file mode 100644
index 0000000..cc1879d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/jitter_timeline.py
@@ -0,0 +1,51 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.value import improvement_direction
+from telemetry.value import list_of_scalar_values
+from telemetry.web_perf.metrics import timeline_based_metric
+
+
+JITTER_EVENT_NAME = 'jitter'
+
+
+class JitterTimelineMetric(timeline_based_metric.TimelineBasedMetric):
+  """JitterTimelineMetric reports jitter in composited layers.
+
+  This jitter is due to the main thread attempting to fix the position of a
+  scrolling composited layer. 'jitter-amount' is the metric added to the
+  results.
+  """
+
+  def __init__(self):
+    super(JitterTimelineMetric, self).__init__()
+
+  @staticmethod
+  def IsJitterEvent(event):
+    return event.name == JITTER_EVENT_NAME
+
+  def AddResults(self, model, renderer_thread, interactions, results):
+    assert interactions
+
+    jitter_events = []
+    for event in model.IterAllEvents(
+        event_predicate=self.IsJitterEvent):
+      jitter_events.append(event)
+
+    self._AddJitterResultsInternal(jitter_events, interactions, results)
+
+  def _AddJitterResultsInternal(self, events, interactions, results):
+    jitters = []
+    for event in events:
+      if timeline_based_metric.IsEventInInteractions(event, interactions):
+        jitters.append(event.args['value'])
+    if jitters:
+      results.AddValue(list_of_scalar_values.ListOfScalarValues(
+          page=results.current_page,
+          tir_label=interactions[0].label,
+          name='jitter-amount',
+          units='score',
+          values=jitters,
+          description='Jitter each frame',
+          improvement_direction=improvement_direction.DOWN))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/jitter_timeline_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/jitter_timeline_unittest.py
new file mode 100644
index 0000000..d2a93dc
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/jitter_timeline_unittest.py
@@ -0,0 +1,50 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from collections import namedtuple
+from telemetry.internal.results import page_test_results
+from telemetry.page import page
+from telemetry.web_perf.metrics import jitter_timeline
+from telemetry.web_perf import timeline_interaction_record
+
+
+FakeEvent = namedtuple('Event', 'name, start, end, thread_duration, args')
+Interaction = timeline_interaction_record.TimelineInteractionRecord
+TEST_INTERACTION_LABEL = 'Action_TestInteraction'
+JITTER_EVENT_NAME = 'jitter'
+
+def GetJitterMetrics(events, interactions):
+  results = page_test_results.PageTestResults()
+  test_page = page.Page('file://blank.html')
+  results.WillRunPage(test_page)
+  jitter_timeline.JitterTimelineMetric()._AddJitterResultsInternal(
+      events, interactions, results)
+  return_dict = dict((value.name, value.values) for value in
+                     results.current_page_run.values)
+  results.DidRunPage(test_page)
+  return return_dict
+
+def FakeJitterEvent(start, end, value, thread_duration=None):
+  if not thread_duration:
+    thread_duration = end - start
+  return FakeEvent(jitter_timeline.JITTER_EVENT_NAME,
+          start, end, thread_duration, {'value':value})
+
+def TestInteraction(start, end):
+  return Interaction(TEST_INTERACTION_LABEL, start, end)
+
+
+class JitterTimelineMetricUnitTest(unittest.TestCase):
+  def testJitterMetric(self):
+    events = [FakeJitterEvent(0, 1, 10),
+              FakeJitterEvent(5, 10, 5),
+              FakeJitterEvent(15, 34, 45)]
+    interactions = [TestInteraction(4, 14)]
+    # The first and the last event do not start during the interaction, so
+    # they are ignored. The second event starts during the interaction, and its
+    # value is 5.
+    self.assertEqual({'jitter-amount': [5]},
+        GetJitterMetrics(events, interactions))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/layout.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/layout.py
new file mode 100644
index 0000000..fb1c5f2
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/layout.py
@@ -0,0 +1,22 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.web_perf.metrics import single_event
+
+EVENT_NAME = 'FrameView::performLayout'
+METRIC_NAME = 'layout'
+
+class LayoutMetric(single_event._SingleEventMetric):
+  """Reports directly durations of FrameView::performLayout events.
+
+    layout: Durations of FrameView::performLayout events that were caused by and
+            start during user interaction.
+
+  Layout happens no more than once per frame, so per-frame-ness is implied.
+  """
+
+  def __init__(self):
+    super(LayoutMetric, self).__init__(EVENT_NAME, METRIC_NAME,
+        metric_description=('List of durations of layouts that were caused by '
+                            'and start during interactions'))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/mainthread_jank_stats.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/mainthread_jank_stats.py
new file mode 100644
index 0000000..66a86a7
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/mainthread_jank_stats.py
@@ -0,0 +1,91 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+# A top level slice of a main thread can cause the webapp to behave
+# unresponsively if its thread duration is greater than or equals to
+# USER_PERCEIVABLE_DELAY_THRESHOLD_MS. Human eyes can perceive delay at low as
+# 100ms, but since we use thread time instead of wall-time, we reduce the
+# threshold further to 50ms to make room for other OS's activities.
+USER_PERCEIVABLE_DELAY_THRESHOLD_MS = 50
+
+
+class _MainthreadJankStat(object):
+  """A small wrapper class for storing mainthread jank stats computed for
+  single record.
+  """
+
+  def __init__(self):
+    self.sum_big_top_slices_thread_time = 0
+    self.biggest_top_slice_thread_time = 0
+
+
+def _ComputeMainthreadJankStatsForRecord(renderer_thread, record):
+  """Computes the mainthread jank stat on a record range.
+
+  Returns:
+      An instance of _MainthreadJankStat, which has:
+
+      sum_big_top_slices_thread_time is the total thread duration of all top
+      slices whose thread time ranges overlapped with (thread_start, thread_end)
+      and the overlapped thread duration is greater than or equal
+      USER_PERCEIVABLE_DELAY_THRESHOLD_MS.
+
+      biggest_top_slice_thread_time is the biggest thread duration of all
+      top slices whose thread time ranges overlapped with
+      (thread_start, thread_end).
+
+      Note: thread duration of each slices is computed using overlapped range
+      with (thread_start, thread_end).
+  """
+  stat = _MainthreadJankStat()
+  for s in renderer_thread.toplevel_slices:
+    jank_thread_duration = record.GetOverlappedThreadTimeForSlice(s)
+    stat.biggest_top_slice_thread_time = max(
+        stat.biggest_top_slice_thread_time, jank_thread_duration)
+    if jank_thread_duration >= USER_PERCEIVABLE_DELAY_THRESHOLD_MS:
+      stat.sum_big_top_slices_thread_time += jank_thread_duration
+  return stat
+
+
+class MainthreadJankStats(object):
+  """
+    Utility class for extracting main thread jank statistics from the timeline
+    (or other loggin facilities), and providing them in a common format to
+    classes that compute benchmark metrics from this data.
+
+      total_big_jank_thread_time is the total thread duration of all top
+      slices whose thread time ranges overlapped with any thread time ranges of
+      the records and the overlapped thread duration is greater than or equal
+      USER_PERCEIVABLE_DELAY_THRESHOLD_MS.
+
+      biggest_jank_thread_time is the biggest thread duration of all
+      top slices whose thread time ranges overlapped with any of records' thread
+      time ranges.
+  """
+
+  def __init__(self, renderer_thread, interaction_records):
+    self._renderer_thread = renderer_thread
+    self._interaction_records = interaction_records
+    self._total_big_jank_thread_time = 0
+    self._biggest_jank_thread_time = 0
+    self._ComputeMainthreadJankStats()
+
+  @property
+  def total_big_jank_thread_time(self):
+    return self._total_big_jank_thread_time
+
+  @property
+  def biggest_jank_thread_time(self):
+    return self._biggest_jank_thread_time
+
+  def _ComputeMainthreadJankStats(self):
+    for record in self._interaction_records:
+      record_jank_stat = _ComputeMainthreadJankStatsForRecord(
+          self._renderer_thread, record)
+      self._total_big_jank_thread_time += (
+          record_jank_stat.sum_big_top_slices_thread_time)
+      self._biggest_jank_thread_time = (
+          max(self._biggest_jank_thread_time,
+              record_jank_stat.biggest_top_slice_thread_time))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/mainthread_jank_stats_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/mainthread_jank_stats_unittest.py
new file mode 100644
index 0000000..093d686
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/mainthread_jank_stats_unittest.py
@@ -0,0 +1,118 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from telemetry.timeline import async_slice
+from telemetry.timeline import model as model_module
+from telemetry.web_perf.metrics import mainthread_jank_stats
+from telemetry.web_perf import timeline_interaction_record as tir_module
+
+
+class MainthreadJankTests(unittest.TestCase):
+
+  def CreateTestRecord(self, name, start, end, thread_start, thread_end,
+                       parent_thread):
+    s = async_slice.AsyncSlice(
+        'cat', 'Interaction.%s' % name,
+        timestamp=start, duration=end - start, start_thread=parent_thread,
+        end_thread=parent_thread, thread_start=thread_start,
+        thread_duration=thread_end - thread_start)
+    return tir_module.TimelineInteractionRecord.FromAsyncEvent(s)
+
+  def testComputeMainthreadJankStatsForRecord(self):
+    # The slice hierarchy should look something like this:
+    # [  MessageLoop::RunTask   ] [MessageLoop::RunTask][  MessagLoop::RunTask ]
+    #                                 [ foo ]                  [ bar ]
+    #            |                                                |
+    #          200ms                                            800ms
+    #       (thread_start)                                   (thread_end)
+    #
+    # Note: all timings mentioned here and in comments below are thread time.
+
+    model = model_module.TimelineModel()
+    renderer_main = model.GetOrCreateProcess(1).GetOrCreateThread(2)
+    renderer_main.name = 'CrRendererMain'
+
+    #   [     MessageLoop::RunTask             ]
+    # 100ms                                   300ms
+    renderer_main.BeginSlice('toplevel', 'MessageLoop::RunTask', 112, 100)
+    renderer_main.EndSlice(240, 300)
+
+    #   [     MessageLoop::RunTask             ]
+    # 450ms     [   foo  ]                    475 ms
+    #         460ms    470ms
+    renderer_main.BeginSlice('toplevel', 'MessageLoop::RunTask', 462, 450)
+    renderer_main.BeginSlice('otherlevel', 'foo', 468, 460)
+    renderer_main.EndSlice(475, 470)
+    renderer_main.EndSlice(620, 475)
+
+    #   [     MessageLoop::RunTask             ]
+    #  620ms     [   bar  ]                   900ms
+    #         750ms    850ms
+    renderer_main.BeginSlice('toplevel', 'MessageLoop::RunTask', 652, 620)
+    renderer_main.BeginSlice('otherlevel', 'bar', 785, 750)
+    renderer_main.EndSlice(875, 850)
+    renderer_main.EndSlice(1040, 900)
+
+    model.FinalizeImport(shift_world_to_zero=False)
+
+    # Make a record that starts at 200ms and ends at 800ms in thread time
+    record = self.CreateTestRecord('test', 100, 700, 200, 800, renderer_main)
+    # pylint: disable=protected-access
+    stat = mainthread_jank_stats._ComputeMainthreadJankStatsForRecord(
+        renderer_main, record)
+
+    # The overlapped between thread time range(200ms -> 800ms)
+    # with the first top slice (100ms -> 300ms) is 300 - 200 = 100ms,
+    # with the second slice (450ms -> 475ms) is 475 - 450 = 25 ms,
+    # with the third slice (620ms -> 900ms) is 800 - 620 = 180 ms.
+    #
+    # Hence we have 2 big top slices which overlapped duration > 50ms,
+    # the biggest top slice is 180ms, and the total big top slice's thread time
+    # is 100 + 180 = 280ms.
+    self.assertEquals(180, stat.biggest_top_slice_thread_time)
+    self.assertEquals(280, stat.sum_big_top_slices_thread_time)
+
+  def testMainthreadJankStats(self):
+    # [ MessageLoop::RunTask]  [MessageLoop::RunTask]  [MessagLoop::RunTask]
+    # 10                   100 120                 400 450                750
+    #     [  record_1  ]       [  record_2  ]   [            record_3        ]
+    #     40          70      120          200  220                         900
+
+    model = model_module.TimelineModel()
+    renderer_main = model.GetOrCreateProcess(1).GetOrCreateThread(2)
+    renderer_main.name = 'CrRendererMain'
+
+    #   [     MessageLoop::RunTask  ]
+    #   10ms                       100ms
+    renderer_main.BeginSlice('toplevel', 'MessageLoop::RunTask', 12, 10)
+    renderer_main.EndSlice(120, 100)
+
+    #   [     MessageLoop::RunTask  ]
+    #   120ms                      200ms
+    renderer_main.BeginSlice('toplevel', 'MessageLoop::RunTask', 115, 120)
+    renderer_main.EndSlice(410, 400)
+
+    #   [     MessageLoop::RunTask  ]
+    #  220ms                       900ms
+    renderer_main.BeginSlice('toplevel', 'MessageLoop::RunTask', 477, 450)
+    renderer_main.EndSlice(772, 750)
+
+    model.FinalizeImport(shift_world_to_zero=False)
+
+    test_records = [
+        self.CreateTestRecord('record_1', 10, 80, 40, 70, renderer_main),
+        self.CreateTestRecord('record_2', 100, 210, 120, 200, renderer_main),
+        self.CreateTestRecord('record_3', 215, 920, 220, 900, renderer_main)
+    ]
+
+    stats = mainthread_jank_stats.MainthreadJankStats(
+        renderer_main, test_records)
+    # Main thread janks covered by records' ranges are:
+    # Record 1: (40ms -> 70ms)
+    # Record 2: (120ms -> 200ms)
+    # Record 3: (220ms -> 400ms), (450ms -> 750ms)
+    self.assertEquals(560, stats.total_big_jank_thread_time)
+    self.assertEquals(300, stats.biggest_jank_thread_time)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/rendering_frame.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/rendering_frame.py
new file mode 100644
index 0000000..48f8546
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/rendering_frame.py
@@ -0,0 +1,86 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from collections import defaultdict
+
+from telemetry.timeline import bounds
+from telemetry.timeline import slice as slice_module
+
+
+class MissingData(Exception):
+  pass
+
+
+class NoBeginFrameIdException(Exception):
+  pass
+
+
+class RenderingFrame(object):
+  """Object with information about the triggering of a BeginMainFrame event."""
+  send_begin_frame_event = 'ThreadProxy::ScheduledActionSendBeginMainFrame'
+  begin_main_frame_event = 'ThreadProxy::BeginMainFrame'
+
+  def __init__(self, events):
+    all_send_begin_frame_events = [e for e in events
+                                   if e.name == self.send_begin_frame_event]
+    if len(all_send_begin_frame_events) != 1:
+      raise MissingData('There must be at exactly one %s event.' %
+                        self.send_begin_frame_event)
+
+    all_begin_main_frame_events = [e for e in events
+                                   if e.name == self.begin_main_frame_event]
+    if not all_begin_main_frame_events:
+      raise MissingData('There must be at least one %s event.' %
+                        self.begin_main_frame_event)
+    all_begin_main_frame_events.sort(key=lambda e: e.start)
+
+    self._send_begin_frame = all_send_begin_frame_events[0]
+    self._begin_main_frame = all_begin_main_frame_events[-1]
+
+    self._bounds = bounds.Bounds()
+    self._bounds.AddEvent(self._begin_main_frame)
+    self._bounds.AddEvent(self._send_begin_frame)
+
+  @staticmethod
+  def IsEventUseful(event):
+    return event.name in [RenderingFrame.send_begin_frame_event,
+                          RenderingFrame.begin_main_frame_event]
+
+  @property
+  def bounds(self):
+    return self._bounds
+
+  @property
+  def queueing_duration(self):
+    return self._begin_main_frame.start - self._send_begin_frame.start
+
+
+def GetFrameEventsInsideRange(renderer_process, timeline_range):
+  """Returns RenderingFrames for all relevant events in the timeline_range."""
+  # First filter all events from the renderer_process and turn them into a
+  # dictonary of the form:
+  #   {0: [send_begin_frame, begin_main_frame, begin_main_frame],
+  #    1: [begin_main_frame, send_begin_frame],
+  #    2: [send_begin_frame, begin_main_frame]}
+  begin_frame_events_by_id = defaultdict(list)
+  for event in renderer_process.IterAllEvents(
+      event_type_predicate=lambda t: t == slice_module.Slice,
+      event_predicate=RenderingFrame.IsEventUseful):
+    begin_frame_id = event.args.get('begin_frame_id', None)
+    if begin_frame_id is None:
+      raise NoBeginFrameIdException('Event is missing a begin_frame_id.')
+    begin_frame_events_by_id[begin_frame_id].append(event)
+
+  # Now, create RenderingFrames for events wherever possible.
+  frames = []
+  for events in begin_frame_events_by_id.values():
+    try:
+      frame = RenderingFrame(events)
+      if frame.bounds.Intersects(timeline_range):
+        frames.append(frame)
+    except MissingData:
+      continue
+  frames.sort(key=lambda frame: frame.bounds.min)
+
+  return frames
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/rendering_frame_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/rendering_frame_unittest.py
new file mode 100644
index 0000000..95f6d93
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/rendering_frame_unittest.py
@@ -0,0 +1,163 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+import telemetry.timeline.bounds as timeline_bounds
+from telemetry.timeline import model
+import telemetry.timeline.slice as tracing_slice
+from telemetry.web_perf.metrics. \
+    rendering_frame import GetFrameEventsInsideRange
+from telemetry.web_perf.metrics.rendering_frame import MissingData
+from telemetry.web_perf.metrics.rendering_frame import RenderingFrame
+
+
+class RenderingFrameTestData(object):
+
+  def __init__(self):
+    self._begin_frame_id = 0
+    self._events = []
+    self._renderer_process = model.TimelineModel().GetOrCreateProcess(pid=1)
+    self._main_thread = self._renderer_process.GetOrCreateThread(tid=11)
+    self._compositor_thread = self._renderer_process.GetOrCreateThread(tid=12)
+
+  @property
+  def events(self):
+    return self._events
+
+  @property
+  def renderer_process(self):
+    return self._renderer_process
+
+  def AddSendEvent(self, ts=0, duration=1):
+    self._begin_frame_id += 1
+    event = self._CreateEvent(
+        RenderingFrame.send_begin_frame_event, ts, duration)
+    self._compositor_thread.PushSlice(event)
+
+  def AddBeginMainFrameEvent(self, ts=0, duration=1):
+    event = self._CreateEvent(
+        RenderingFrame.begin_main_frame_event, ts, duration)
+    self._main_thread.PushSlice(event)
+
+  def FinalizeImport(self):
+    self._renderer_process.FinalizeImport()
+
+  def _CreateEvent(self, event_name, ts, duration):
+    event = tracing_slice.Slice(None, 'cc,benchmark', event_name, ts,
+        duration=duration, args={'begin_frame_id': self._begin_frame_id})
+    self._events.append(event)
+    return event
+
+
+def GenerateTimelineRange(start=0, end=100):
+  timeline_range = timeline_bounds.Bounds()
+  timeline_range.AddValue(start)
+  timeline_range.AddValue(end)
+  return timeline_range
+
+
+class RenderingFrameUnitTest(unittest.TestCase):
+
+  def testRenderingFrame(self):
+    d = RenderingFrameTestData()
+    d.AddSendEvent(ts=10)
+    d.AddBeginMainFrameEvent(ts=20)
+    d.FinalizeImport()
+
+    frame = RenderingFrame(d.events)
+    self.assertEquals(10, frame.queueing_duration)
+
+  def testRenderingFrameMissingSendBeginFrameEvents(self):
+    d = RenderingFrameTestData()
+    d.AddBeginMainFrameEvent(ts=10)
+    d.FinalizeImport()
+
+    self.assertRaises(MissingData, RenderingFrame, d.events)
+
+  def testRenderingFrameDuplicateSendBeginFrameEvents(self):
+    d = RenderingFrameTestData()
+    d.AddSendEvent(ts=10)
+    d.AddBeginMainFrameEvent(ts=20)
+    d.AddSendEvent(ts=30)
+    d.FinalizeImport()
+
+    self.assertRaises(MissingData, RenderingFrame, d.events)
+
+  def testRenderingFrameMissingBeginMainFrameEvents(self):
+    d = RenderingFrameTestData()
+    d.AddSendEvent(ts=10)
+    d.FinalizeImport()
+
+    self.assertRaises(MissingData, RenderingFrame, d.events)
+
+  def testRenderingFrameDuplicateBeginMainFrameEvents(self):
+    d = RenderingFrameTestData()
+    d.AddSendEvent(ts=10)
+    d.AddBeginMainFrameEvent(ts=20)
+    d.AddBeginMainFrameEvent(ts=30)
+    d.AddBeginMainFrameEvent(ts=40)
+    d.FinalizeImport()
+
+    frame = RenderingFrame(d.events)
+    self.assertEquals(30, frame.queueing_duration)
+
+  def testFrameEventMissingBeginFrameId(self):
+    timeline = model.TimelineModel()
+    process = timeline.GetOrCreateProcess(pid=1)
+    main_thread = process.GetOrCreateThread(tid=11)
+    timeline_range = timeline_bounds.Bounds()
+
+    # Create an event without the begin_frame_id argument
+    event = tracing_slice.Slice(
+        None, 'cc,benchmark', RenderingFrame.begin_main_frame_event, 0)
+    main_thread.PushSlice(event)
+    process.FinalizeImport()
+    self.assertRaises(Exception, GetFrameEventsInsideRange, process,
+                      timeline_range)
+
+  def testGetFrameEventsInsideRange(self):
+    """Test a basic sequenece, with expected frame queueing delays A and B.
+
+                 |----A----|    |--B--|
+         Main:        [1]  [1]        [2]
+
+    Compositor:  [1]            [2]
+    """
+    d = RenderingFrameTestData()
+    d.AddSendEvent(ts=10)
+    d.AddBeginMainFrameEvent(ts=20)
+    d.AddBeginMainFrameEvent(ts=30)
+    d.AddSendEvent(ts=40)
+    d.AddBeginMainFrameEvent(ts=50)
+    d.FinalizeImport()
+
+    timeline_range = GenerateTimelineRange()
+    frame_events = GetFrameEventsInsideRange(d.renderer_process, timeline_range)
+
+    self.assertEquals(2, len(frame_events))
+    self.assertEquals(20, frame_events[0].queueing_duration)
+    self.assertEquals(10, frame_events[1].queueing_duration)
+
+  def testFrameEventsMissingDataNotIncluded(self):
+    """Test a sequenece missing an initial SendBeginFrame.
+
+    Only one frame should be returned, with expected frame queueing delay A.
+                           |--A--|
+          Main:  [0]  [0]        [2]
+
+    Compositor:            [2]
+    """
+    d = RenderingFrameTestData()
+    d.AddBeginMainFrameEvent(ts=20)
+    d.AddBeginMainFrameEvent(ts=30)
+    d.AddSendEvent(ts=40)
+    d.AddBeginMainFrameEvent(ts=50)
+    d.FinalizeImport()
+
+    timeline_range = GenerateTimelineRange()
+    frame_events = GetFrameEventsInsideRange(d.renderer_process, timeline_range)
+
+    self.assertEquals(1, len(frame_events))
+    self.assertEquals(10, frame_events[0].queueing_duration)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/rendering_stats.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/rendering_stats.py
new file mode 100644
index 0000000..9f9b104
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/rendering_stats.py
@@ -0,0 +1,316 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import itertools
+
+from operator import attrgetter
+
+from telemetry.web_perf.metrics import rendering_frame
+
+# These are LatencyInfo component names indicating the various components
+# that the input event has travelled through.
+# This is when the input event first reaches chrome.
+UI_COMP_NAME = 'INPUT_EVENT_LATENCY_UI_COMPONENT'
+# This is when the input event was originally created by OS.
+ORIGINAL_COMP_NAME = 'INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT'
+# This is when the input event was sent from browser to renderer.
+BEGIN_COMP_NAME = 'INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT'
+# This is when an input event is turned into a scroll update.
+BEGIN_SCROLL_UPDATE_COMP_NAME = (
+    'LATENCY_BEGIN_SCROLL_LISTENER_UPDATE_MAIN_COMPONENT')
+# This is when a scroll update is forwarded to the main thread.
+FORWARD_SCROLL_UPDATE_COMP_NAME = (
+    'INPUT_EVENT_LATENCY_FORWARD_SCROLL_UPDATE_TO_MAIN_COMPONENT')
+# This is when the input event has reached swap buffer.
+END_COMP_NAME = 'INPUT_EVENT_GPU_SWAP_BUFFER_COMPONENT'
+
+# Name for a main thread scroll update latency event.
+MAIN_THREAD_SCROLL_UPDATE_EVENT_NAME = 'Latency::ScrollUpdate'
+# Name for a gesture scroll update latency event.
+GESTURE_SCROLL_UPDATE_EVENT_NAME = 'InputLatency::GestureScrollUpdate'
+
+# These are keys used in the 'data' field dictionary located in
+# BenchmarkInstrumentation::ImplThreadRenderingStats.
+VISIBLE_CONTENT_DATA = 'visible_content_area'
+APPROXIMATED_VISIBLE_CONTENT_DATA = 'approximated_visible_content_area'
+CHECKERBOARDED_VISIBLE_CONTENT_DATA = 'checkerboarded_visible_content_area'
+# These are keys used in the 'errors' field  dictionary located in
+# RenderingStats in this file.
+APPROXIMATED_PIXEL_ERROR = 'approximated_pixel_percentages'
+CHECKERBOARDED_PIXEL_ERROR = 'checkerboarded_pixel_percentages'
+
+
+def GetLatencyEvents(process, timeline_range):
+  """Get LatencyInfo trace events from the process's trace buffer that are
+     within the timeline_range.
+
+  Input events dump their LatencyInfo into trace buffer as async trace event
+  of name starting with "InputLatency". Non-input events with name starting
+  with "Latency". The trace event has a member 'data' containing its latency
+  history.
+
+  """
+  latency_events = []
+  if not process:
+    return latency_events
+  for event in itertools.chain(
+      process.IterAllAsyncSlicesStartsWithName('InputLatency'),
+      process.IterAllAsyncSlicesStartsWithName('Latency')):
+    if event.start >= timeline_range.min and event.end <= timeline_range.max:
+      for ss in event.sub_slices:
+        if 'data' in ss.args:
+          latency_events.append(ss)
+  return latency_events
+
+
+def ComputeEventLatencies(input_events):
+  """ Compute input event latencies.
+
+  Input event latency is the time from when the input event is created to
+  when its resulted page is swap buffered.
+  Input event on different platforms uses different LatencyInfo component to
+  record its creation timestamp. We go through the following component list
+  to find the creation timestamp:
+  1. INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT -- when event is created in OS
+  2. INPUT_EVENT_LATENCY_UI_COMPONENT -- when event reaches Chrome
+  3. INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT -- when event reaches RenderWidget
+
+  If the latency starts with a
+  LATENCY_BEGIN_SCROLL_UPDATE_MAIN_COMPONENT component, then it is
+  classified as a scroll update instead of a normal input latency measure.
+
+  Returns:
+    A list sorted by increasing start time of latencies which are tuples of
+    (input_event_name, latency_in_ms).
+  """
+  input_event_latencies = []
+  for event in input_events:
+    data = event.args['data']
+    if END_COMP_NAME in data:
+      end_time = data[END_COMP_NAME]['time']
+      if ORIGINAL_COMP_NAME in data:
+        start_time = data[ORIGINAL_COMP_NAME]['time']
+      elif UI_COMP_NAME in data:
+        start_time = data[UI_COMP_NAME]['time']
+      elif BEGIN_COMP_NAME in data:
+        start_time = data[BEGIN_COMP_NAME]['time']
+      elif BEGIN_SCROLL_UPDATE_COMP_NAME in data:
+        start_time = data[BEGIN_SCROLL_UPDATE_COMP_NAME]['time']
+      else:
+        raise ValueError('LatencyInfo has no begin component')
+      latency = (end_time - start_time) / 1000.0
+      input_event_latencies.append((start_time, event.name, latency))
+
+  input_event_latencies.sort()
+  return [(name, latency) for _, name, latency in input_event_latencies]
+
+
+def HasDrmStats(process):
+  """ Return True if the process contains DrmEventFlipComplete event.
+  """
+  if not process:
+    return False
+  for event in process.IterAllSlicesOfName('DrmEventFlipComplete'):
+    if 'data' in event.args and event.args['data']['frame_count'] == 1:
+      return True
+  return False
+
+def HasRenderingStats(process):
+  """ Returns True if the process contains at least one
+      BenchmarkInstrumentation::*RenderingStats event with a frame.
+  """
+  if not process:
+    return False
+  for event in process.IterAllSlicesOfName(
+      'BenchmarkInstrumentation::DisplayRenderingStats'):
+    if 'data' in event.args and event.args['data']['frame_count'] == 1:
+      return True
+  for event in process.IterAllSlicesOfName(
+      'BenchmarkInstrumentation::ImplThreadRenderingStats'):
+    if 'data' in event.args and event.args['data']['frame_count'] == 1:
+      return True
+  return False
+
+def GetTimestampEventName(process):
+  """ Returns the name of the events used to count frame timestamps. """
+  if process.name == 'SurfaceFlinger':
+    return 'vsync_before'
+
+  if process.name == 'GPU Process':
+    return 'DrmEventFlipComplete'
+
+  event_name = 'BenchmarkInstrumentation::DisplayRenderingStats'
+  for event in process.IterAllSlicesOfName(event_name):
+    if 'data' in event.args and event.args['data']['frame_count'] == 1:
+      return event_name
+
+  return 'BenchmarkInstrumentation::ImplThreadRenderingStats'
+
+class RenderingStats(object):
+  def __init__(self, renderer_process, browser_process, surface_flinger_process,
+               gpu_process, timeline_ranges):
+    """
+    Utility class for extracting rendering statistics from the timeline (or
+    other loggin facilities), and providing them in a common format to classes
+    that compute benchmark metrics from this data.
+
+    Stats are lists of lists of numbers. The outer list stores one list per
+    timeline range.
+
+    All *_time values are measured in milliseconds.
+    """
+    assert len(timeline_ranges) > 0
+    self.refresh_period = None
+
+    # Find the top level process with rendering stats (browser or renderer).
+    if surface_flinger_process:
+      timestamp_process = surface_flinger_process
+      self._GetRefreshPeriodFromSurfaceFlingerProcess(surface_flinger_process)
+    elif HasDrmStats(gpu_process):
+      timestamp_process = gpu_process
+    elif HasRenderingStats(browser_process):
+      timestamp_process = browser_process
+    else:
+      timestamp_process = renderer_process
+
+    timestamp_event_name = GetTimestampEventName(timestamp_process)
+
+    # A lookup from list names below to any errors or exceptions encountered
+    # in attempting to generate that list.
+    self.errors = {}
+
+    self.frame_timestamps = []
+    self.frame_times = []
+    self.approximated_pixel_percentages = []
+    self.checkerboarded_pixel_percentages = []
+    # End-to-end latency for input event - from when input event is
+    # generated to when the its resulted page is swap buffered.
+    self.input_event_latency = []
+    self.frame_queueing_durations = []
+    # Latency from when a scroll update is sent to the main thread until the
+    # resulting frame is swapped.
+    self.main_thread_scroll_latency = []
+    # Latency for a GestureScrollUpdate input event.
+    self.gesture_scroll_update_latency = []
+
+    for timeline_range in timeline_ranges:
+      self.frame_timestamps.append([])
+      self.frame_times.append([])
+      self.approximated_pixel_percentages.append([])
+      self.checkerboarded_pixel_percentages.append([])
+      self.input_event_latency.append([])
+      self.main_thread_scroll_latency.append([])
+      self.gesture_scroll_update_latency.append([])
+
+      if timeline_range.is_empty:
+        continue
+      self._InitFrameTimestampsFromTimeline(
+          timestamp_process, timestamp_event_name, timeline_range)
+      self._InitImplThreadRenderingStatsFromTimeline(
+          renderer_process, timeline_range)
+      self._InitInputLatencyStatsFromTimeline(
+          browser_process, renderer_process, timeline_range)
+      self._InitFrameQueueingDurationsFromTimeline(
+          renderer_process, timeline_range)
+
+  def _GetRefreshPeriodFromSurfaceFlingerProcess(self, surface_flinger_process):
+    for event in surface_flinger_process.IterAllEventsOfName('vsync_before'):
+      self.refresh_period = event.args['data']['refresh_period']
+      return
+
+  def _InitInputLatencyStatsFromTimeline(
+      self, browser_process, renderer_process, timeline_range):
+    latency_events = GetLatencyEvents(browser_process, timeline_range)
+    # Plugin input event's latency slice is generated in renderer process.
+    latency_events.extend(GetLatencyEvents(renderer_process, timeline_range))
+    event_latencies = ComputeEventLatencies(latency_events)
+    # Don't include scroll updates in the overall input latency measurement,
+    # because scroll updates can take much more time to process than other
+    # input events and would therefore add noise to overall latency numbers.
+    self.input_event_latency[-1] = [
+        latency for name, latency in event_latencies
+        if name != MAIN_THREAD_SCROLL_UPDATE_EVENT_NAME]
+    self.main_thread_scroll_latency[-1] = [
+        latency for name, latency in event_latencies
+        if name == MAIN_THREAD_SCROLL_UPDATE_EVENT_NAME]
+    self.gesture_scroll_update_latency[-1] = [
+        latency for name, latency in event_latencies
+        if name == GESTURE_SCROLL_UPDATE_EVENT_NAME]
+
+  def _GatherEvents(self, event_name, process, timeline_range):
+    events = []
+    for event in process.IterAllSlicesOfName(event_name):
+      if event.start >= timeline_range.min and event.end <= timeline_range.max:
+        if 'data' not in event.args:
+          continue
+        events.append(event)
+    events.sort(key=attrgetter('start'))
+    return events
+
+  def _AddFrameTimestamp(self, event):
+    frame_count = event.args['data']['frame_count']
+    if frame_count > 1:
+      raise ValueError('trace contains multi-frame render stats')
+    if frame_count == 1:
+      if event.name == 'DrmEventFlipComplete':
+        self.frame_timestamps[-1].append(
+            event.args['data']['vblank.tv_sec'] * 1000.0 +
+            event.args['data']['vblank.tv_usec'] / 1000.0)
+      else:
+        self.frame_timestamps[-1].append(
+            event.start)
+      if len(self.frame_timestamps[-1]) >= 2:
+        self.frame_times[-1].append(
+            self.frame_timestamps[-1][-1] - self.frame_timestamps[-1][-2])
+
+  def _InitFrameTimestampsFromTimeline(
+      self, process, timestamp_event_name, timeline_range):
+    for event in self._GatherEvents(
+        timestamp_event_name, process, timeline_range):
+      self._AddFrameTimestamp(event)
+
+  def _InitImplThreadRenderingStatsFromTimeline(self, process, timeline_range):
+    event_name = 'BenchmarkInstrumentation::ImplThreadRenderingStats'
+    for event in self._GatherEvents(event_name, process, timeline_range):
+      data = event.args['data']
+      if VISIBLE_CONTENT_DATA not in data:
+        self.errors[APPROXIMATED_PIXEL_ERROR] = (
+          'Calculating approximated_pixel_percentages not possible because '
+          'visible_content_area was missing.')
+        self.errors[CHECKERBOARDED_PIXEL_ERROR] = (
+          'Calculating checkerboarded_pixel_percentages not possible because '
+          'visible_content_area was missing.')
+        return
+      visible_content_area = data[VISIBLE_CONTENT_DATA]
+      if visible_content_area == 0:
+        self.errors[APPROXIMATED_PIXEL_ERROR] = (
+          'Calculating approximated_pixel_percentages would have caused '
+          'a divide-by-zero')
+        self.errors[CHECKERBOARDED_PIXEL_ERROR] = (
+          'Calculating checkerboarded_pixel_percentages would have caused '
+          'a divide-by-zero')
+        return
+      if APPROXIMATED_VISIBLE_CONTENT_DATA in data:
+        self.approximated_pixel_percentages[-1].append(
+          round(float(data[APPROXIMATED_VISIBLE_CONTENT_DATA]) /
+                float(data[VISIBLE_CONTENT_DATA]) * 100.0, 3))
+      else:
+        self.errors[APPROXIMATED_PIXEL_ERROR] = (
+          'approximated_pixel_percentages was not recorded')
+      if CHECKERBOARDED_VISIBLE_CONTENT_DATA in data:
+        self.checkerboarded_pixel_percentages[-1].append(
+          round(float(data[CHECKERBOARDED_VISIBLE_CONTENT_DATA]) /
+                float(data[VISIBLE_CONTENT_DATA]) * 100.0, 3))
+      else:
+        self.errors[CHECKERBOARDED_PIXEL_ERROR] = (
+          'checkerboarded_pixel_percentages was not recorded')
+
+  def _InitFrameQueueingDurationsFromTimeline(self, process, timeline_range):
+    try:
+      events = rendering_frame.GetFrameEventsInsideRange(process,
+                                                         timeline_range)
+      new_frame_queueing_durations = [e.queueing_duration for e in events]
+      self.frame_queueing_durations.append(new_frame_queueing_durations)
+    except rendering_frame.NoBeginFrameIdException:
+      self.errors['frame_queueing_durations'] = (
+          'Current chrome version does not support the queueing delay metric.')
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/rendering_stats_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/rendering_stats_unittest.py
new file mode 100644
index 0000000..972d2f0
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/rendering_stats_unittest.py
@@ -0,0 +1,666 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import random
+import unittest
+
+from telemetry.timeline import async_slice
+from telemetry.timeline import bounds
+from telemetry.timeline import model
+from telemetry.util import perf_tests_helper
+from telemetry.util import statistics
+from telemetry.web_perf.metrics import rendering_stats
+
+
+class MockTimer(object):
+  """A mock timer class which can generate random durations.
+
+  An instance of this class is used as a global timer to generate random
+  durations for stats and consistent timestamps for all mock trace events.
+  The unit of time is milliseconds.
+  """
+
+  def __init__(self):
+    self.milliseconds = 0
+
+  def Advance(self, low=0.1, high=1):
+    delta = random.uniform(low, high)
+    self.milliseconds += delta
+    return delta
+
+  def AdvanceAndGet(self, low=0.1, high=1):
+    self.Advance(low, high)
+    return self.milliseconds
+
+
+class MockVblankTimer(object):
+  """A mock vblank timer class which can generate random durations.
+
+  An instance of this class is used as a vblank timer to generate random
+  durations for drm stats and consistent timeval for mock trace drm events.
+  The unit of time is microseconds.
+  """
+
+  def __init__(self):
+    self.microseconds = 200000000
+
+  def TvAdvance(self, low=100, high=1000):
+    delta = random.randint(low, high)
+    self.microseconds += delta
+    return delta
+
+  def TvAdvanceAndGet(self, low=100, high=1000):
+    self.TvAdvance(low, high)
+    return self.microseconds
+
+
+class ReferenceRenderingStats(object):
+  """ Stores expected data for comparison with actual RenderingStats """
+
+  def __init__(self):
+    self.frame_timestamps = []
+    self.frame_times = []
+    self.approximated_pixel_percentages = []
+    self.checkerboarded_pixel_percentages = []
+
+  def AppendNewRange(self):
+    self.frame_timestamps.append([])
+    self.frame_times.append([])
+    self.approximated_pixel_percentages.append([])
+    self.checkerboarded_pixel_percentages.append([])
+
+
+class ReferenceInputLatencyStats(object):
+  """ Stores expected data for comparison with actual input latency stats """
+
+  def __init__(self):
+    self.input_event_latency = []
+    self.input_event = []
+
+
+def AddSurfaceFlingerStats(mock_timer, thread, first_frame,
+                           ref_stats=None):
+  """ Adds a random surface flinger stats event.
+
+  thread: The timeline model thread to which the event will be added.
+  first_frame: Is this the first frame within the bounds of an action?
+  ref_stats: A ReferenceRenderingStats object to record expected values.
+  """
+  # Create random data and timestamp for impl thread rendering stats.
+  data = {'frame_count': 1,
+          'refresh_period': 16.6666}
+  timestamp = mock_timer.AdvanceAndGet()
+
+  # Add a slice with the event data to the given thread.
+  thread.PushCompleteSlice(
+      'SurfaceFlinger', 'vsync_before',
+      timestamp, duration=0.0, thread_timestamp=None, thread_duration=None,
+      args={'data': data})
+
+  if not ref_stats:
+    return
+
+  # Add timestamp only if a frame was output
+  if data['frame_count'] == 1:
+    if not first_frame:
+      # Add frame_time if this is not the first frame in within the bounds of an
+      # action.
+      prev_timestamp = ref_stats.frame_timestamps[-1][-1]
+      ref_stats.frame_times[-1].append(timestamp - prev_timestamp)
+    ref_stats.frame_timestamps[-1].append(timestamp)
+
+
+def AddDrmEventFlipStats(mock_timer, vblank_timer, thread,
+                         first_frame, ref_stats=None):
+  """ Adds a random drm flip complete event.
+
+  thread: The timeline model thread to which the event will be added.
+  first_frame: Is this the first frame within the bounds of an action?
+  ref_stats: A ReferenceRenderingStats object to record expected values.
+  """
+  # Create random data and timestamp for drm thread flip complete stats.
+  vblank_timeval = vblank_timer.TvAdvanceAndGet()
+  vblank_tv_sec = vblank_timeval / 1000000
+  vblank_tv_usec = vblank_timeval % 1000000
+  data = {'frame_count': 1,
+          'vblank.tv_usec': vblank_tv_usec,
+          'vblank.tv_sec': vblank_tv_sec}
+  timestamp = mock_timer.AdvanceAndGet()
+
+  # Add a slice with the event data to the given thread.
+  thread.PushCompleteSlice(
+      'benchmark,drm', 'DrmEventFlipComplete',
+      timestamp, duration=0.0, thread_timestamp=None, thread_duration=None,
+      args={'data': data})
+
+  if not ref_stats:
+    return
+
+  # Add vblank timeval only if a frame was output.
+  cur_timestamp = vblank_tv_sec * 1000.0 + vblank_tv_usec / 1000.0
+  if not first_frame:
+    # Add frame_time if this is not the first frame in within the bounds of an
+    # action.
+    prev_timestamp = ref_stats.frame_timestamps[-1][-1]
+    ref_stats.frame_times[-1].append(cur_timestamp - prev_timestamp)
+  ref_stats.frame_timestamps[-1].append(cur_timestamp)
+
+
+def AddDisplayRenderingStats(mock_timer, thread, first_frame,
+                             ref_stats=None):
+  """ Adds a random display rendering stats event.
+
+  thread: The timeline model thread to which the event will be added.
+  first_frame: Is this the first frame within the bounds of an action?
+  ref_stats: A ReferenceRenderingStats object to record expected values.
+  """
+  # Create random data and timestamp for main thread rendering stats.
+  data = {'frame_count': 1}
+  timestamp = mock_timer.AdvanceAndGet()
+
+  # Add a slice with the event data to the given thread.
+  thread.PushCompleteSlice(
+      'benchmark', 'BenchmarkInstrumentation::DisplayRenderingStats',
+      timestamp, duration=0.0, thread_timestamp=None, thread_duration=None,
+      args={'data': data})
+
+  if not ref_stats:
+    return
+
+  # Add timestamp only if a frame was output
+  if not first_frame:
+    # Add frame_time if this is not the first frame in within the bounds of an
+    # action.
+    prev_timestamp = ref_stats.frame_timestamps[-1][-1]
+    ref_stats.frame_times[-1].append(timestamp - prev_timestamp)
+  ref_stats.frame_timestamps[-1].append(timestamp)
+
+
+def AddImplThreadRenderingStats(mock_timer, thread, first_frame,
+                                ref_stats=None):
+  """ Adds a random impl thread rendering stats event.
+
+  thread: The timeline model thread to which the event will be added.
+  first_frame: Is this the first frame within the bounds of an action?
+  ref_stats: A ReferenceRenderingStats object to record expected values.
+  """
+  # Create random data and timestamp for impl thread rendering stats.
+  data = {'frame_count': 1,
+          'visible_content_area': random.uniform(0, 100),
+          'approximated_visible_content_area': random.uniform(0, 5),
+          'checkerboarded_visible_content_area': random.uniform(0, 5)}
+  timestamp = mock_timer.AdvanceAndGet()
+
+  # Add a slice with the event data to the given thread.
+  thread.PushCompleteSlice(
+      'benchmark', 'BenchmarkInstrumentation::ImplThreadRenderingStats',
+      timestamp, duration=0.0, thread_timestamp=None, thread_duration=None,
+      args={'data': data})
+
+  if not ref_stats:
+    return
+
+  # Add timestamp only if a frame was output
+  if data['frame_count'] == 1:
+    if not first_frame:
+      # Add frame_time if this is not the first frame in within the bounds of an
+      # action.
+      prev_timestamp = ref_stats.frame_timestamps[-1][-1]
+      ref_stats.frame_times[-1].append(timestamp - prev_timestamp)
+    ref_stats.frame_timestamps[-1].append(timestamp)
+
+  ref_stats.approximated_pixel_percentages[-1].append(
+      round(statistics.DivideIfPossibleOrZero(
+          data['approximated_visible_content_area'],
+          data['visible_content_area']) * 100.0, 3))
+
+  ref_stats.checkerboarded_pixel_percentages[-1].append(
+      round(statistics.DivideIfPossibleOrZero(
+          data['checkerboarded_visible_content_area'],
+          data['visible_content_area']) * 100.0, 3))
+
+def AddInputLatencyStats(mock_timer, start_thread, end_thread,
+                         ref_latency_stats=None):
+  """ Adds a random input latency stats event.
+
+  start_thread: The start thread on which the async slice is added.
+  end_thread: The end thread on which the async slice is ended.
+  ref_latency_stats: A ReferenceInputLatencyStats object for expected values.
+  """
+
+  original_comp_time = mock_timer.AdvanceAndGet(2, 4) * 1000.0
+  ui_comp_time = mock_timer.AdvanceAndGet(2, 4) * 1000.0
+  begin_comp_time = mock_timer.AdvanceAndGet(2, 4) * 1000.0
+  forward_comp_time = mock_timer.AdvanceAndGet(2, 4) * 1000.0
+  end_comp_time = mock_timer.AdvanceAndGet(10, 20) * 1000.0
+
+  data = {rendering_stats.ORIGINAL_COMP_NAME: {'time': original_comp_time},
+          rendering_stats.UI_COMP_NAME: {'time': ui_comp_time},
+          rendering_stats.BEGIN_COMP_NAME: {'time': begin_comp_time},
+          rendering_stats.END_COMP_NAME: {'time': end_comp_time}}
+
+  timestamp = mock_timer.AdvanceAndGet(2, 4)
+
+  tracing_async_slice = async_slice.AsyncSlice(
+      'benchmark', 'InputLatency', timestamp)
+
+  async_sub_slice = async_slice.AsyncSlice(
+      'benchmark', rendering_stats.GESTURE_SCROLL_UPDATE_EVENT_NAME, timestamp)
+  async_sub_slice.args = {'data': data}
+  async_sub_slice.parent_slice = tracing_async_slice
+  async_sub_slice.start_thread = start_thread
+  async_sub_slice.end_thread = end_thread
+
+  tracing_async_slice.sub_slices.append(async_sub_slice)
+  tracing_async_slice.start_thread = start_thread
+  tracing_async_slice.end_thread = end_thread
+  start_thread.AddAsyncSlice(tracing_async_slice)
+
+  # Add scroll update latency info.
+  scroll_update_data = {
+      rendering_stats.BEGIN_SCROLL_UPDATE_COMP_NAME: {'time': begin_comp_time},
+      rendering_stats.FORWARD_SCROLL_UPDATE_COMP_NAME:
+          {'time': forward_comp_time},
+      rendering_stats.END_COMP_NAME: {'time': end_comp_time}
+  }
+
+  scroll_async_slice = async_slice.AsyncSlice(
+      'benchmark', 'InputLatency', timestamp)
+
+  scroll_async_sub_slice = async_slice.AsyncSlice(
+      'benchmark', rendering_stats.MAIN_THREAD_SCROLL_UPDATE_EVENT_NAME,
+      timestamp)
+  scroll_async_sub_slice.args = {'data': scroll_update_data}
+  scroll_async_sub_slice.parent_slice = scroll_async_slice
+  scroll_async_sub_slice.start_thread = start_thread
+  scroll_async_sub_slice.end_thread = end_thread
+
+  scroll_async_slice.sub_slices.append(scroll_async_sub_slice)
+  scroll_async_slice.start_thread = start_thread
+  scroll_async_slice.end_thread = end_thread
+  start_thread.AddAsyncSlice(scroll_async_slice)
+
+  # Also add some dummy frame statistics so we can feed the resulting timeline
+  # to RenderingStats.
+  AddImplThreadRenderingStats(mock_timer, end_thread, False)
+
+  if not ref_latency_stats:
+    return
+
+  ref_latency_stats.input_event.append(async_sub_slice)
+  ref_latency_stats.input_event.append(scroll_async_sub_slice)
+  ref_latency_stats.input_event_latency.append((
+      rendering_stats.GESTURE_SCROLL_UPDATE_EVENT_NAME,
+      (data[rendering_stats.END_COMP_NAME]['time'] -
+       data[rendering_stats.ORIGINAL_COMP_NAME]['time']) / 1000.0))
+  scroll_update_time = (
+      scroll_update_data[rendering_stats.END_COMP_NAME]['time'] -
+      scroll_update_data[rendering_stats.BEGIN_SCROLL_UPDATE_COMP_NAME]['time'])
+  ref_latency_stats.input_event_latency.append((
+      rendering_stats.MAIN_THREAD_SCROLL_UPDATE_EVENT_NAME,
+      scroll_update_time / 1000.0))
+
+
+class RenderingStatsUnitTest(unittest.TestCase):
+
+  def testHasRenderingStats(self):
+    timeline = model.TimelineModel()
+    timer = MockTimer()
+
+    # A process without rendering stats
+    process_without_stats = timeline.GetOrCreateProcess(pid=1)
+    thread_without_stats = process_without_stats.GetOrCreateThread(tid=11)
+    process_without_stats.FinalizeImport()
+    self.assertFalse(rendering_stats.HasRenderingStats(thread_without_stats))
+
+    # A process with rendering stats, but no frames in them
+    process_without_frames = timeline.GetOrCreateProcess(pid=2)
+    thread_without_frames = process_without_frames.GetOrCreateThread(tid=21)
+    process_without_frames.FinalizeImport()
+    self.assertFalse(rendering_stats.HasRenderingStats(thread_without_frames))
+
+    # A process with rendering stats and frames in them
+    process_with_frames = timeline.GetOrCreateProcess(pid=3)
+    thread_with_frames = process_with_frames.GetOrCreateThread(tid=31)
+    AddImplThreadRenderingStats(timer, thread_with_frames, True, None)
+    process_with_frames.FinalizeImport()
+    self.assertTrue(rendering_stats.HasRenderingStats(thread_with_frames))
+
+  def testHasDrmStats(self):
+    timeline = model.TimelineModel()
+    timer = MockTimer()
+    vblank_timer = MockVblankTimer()
+
+    # A process without drm stats
+    process_without_stats = timeline.GetOrCreateProcess(pid=5)
+    thread_without_stats = process_without_stats.GetOrCreateThread(tid=51)
+    process_without_stats.FinalizeImport()
+    self.assertFalse(rendering_stats.HasDrmStats(thread_without_stats))
+
+    # A process with drm stats and frames in them
+    process_with_frames = timeline.GetOrCreateProcess(pid=6)
+    thread_with_frames = process_with_frames.GetOrCreateThread(tid=61)
+    AddDrmEventFlipStats(timer, vblank_timer, thread_with_frames, True, None)
+    process_with_frames.FinalizeImport()
+    self.assertTrue(rendering_stats.HasDrmStats(thread_with_frames))
+
+  def testBothSurfaceFlingerAndDisplayStats(self):
+    timeline = model.TimelineModel()
+    timer = MockTimer()
+
+    ref_stats = ReferenceRenderingStats()
+    ref_stats.AppendNewRange()
+    surface_flinger = timeline.GetOrCreateProcess(pid=4)
+    surface_flinger.name = 'SurfaceFlinger'
+    surface_flinger_thread = surface_flinger.GetOrCreateThread(tid=41)
+    renderer = timeline.GetOrCreateProcess(pid=2)
+    browser = timeline.GetOrCreateProcess(pid=3)
+    browser_main = browser.GetOrCreateThread(tid=31)
+    browser_main.BeginSlice('webkit.console', 'ActionA',
+                            timer.AdvanceAndGet(2, 4), '')
+
+    # Create SurfaceFlinger stats and display rendering stats.
+    for i in xrange(0, 10):
+      first = (i == 0)
+      AddSurfaceFlingerStats(timer, surface_flinger_thread, first, ref_stats)
+      timer.Advance(2, 4)
+
+    for i in xrange(0, 10):
+      first = (i == 0)
+      AddDisplayRenderingStats(timer, browser_main, first, None)
+      timer.Advance(5, 10)
+
+    browser_main.EndSlice(timer.AdvanceAndGet())
+    timer.Advance(2, 4)
+
+    browser.FinalizeImport()
+    renderer.FinalizeImport()
+    timeline_markers = timeline.FindTimelineMarkers(['ActionA'])
+    timeline_ranges = [bounds.Bounds.CreateFromEvent(marker)
+                       for marker in timeline_markers]
+    stats = rendering_stats.RenderingStats(
+        renderer, browser, surface_flinger, None, timeline_ranges)
+
+    # Compare rendering stats to reference - Only SurfaceFlinger stats should
+    # count
+    self.assertEquals(stats.frame_timestamps, ref_stats.frame_timestamps)
+    self.assertEquals(stats.frame_times, ref_stats.frame_times)
+
+  def testBothDrmAndDisplayStats(self):
+    timeline = model.TimelineModel()
+    timer = MockTimer()
+    vblank_timer = MockVblankTimer()
+
+    ref_stats = ReferenceRenderingStats()
+    ref_stats.AppendNewRange()
+    gpu = timeline.GetOrCreateProcess(pid=6)
+    gpu.name = 'GPU Process'
+    gpu_drm_thread = gpu.GetOrCreateThread(tid=61)
+    renderer = timeline.GetOrCreateProcess(pid=2)
+    browser = timeline.GetOrCreateProcess(pid=3)
+    browser_main = browser.GetOrCreateThread(tid=31)
+    browser_main.BeginSlice('webkit.console', 'ActionA',
+                            timer.AdvanceAndGet(2, 4), '')
+    vblank_timer.TvAdvance(2000, 4000)
+
+    # Create drm flip stats and display rendering stats.
+    for i in xrange(0, 10):
+      first = (i == 0)
+      AddDrmEventFlipStats(timer, vblank_timer, gpu_drm_thread,
+                           first, ref_stats)
+      timer.Advance(2, 4)
+      vblank_timer.TvAdvance(2000, 4000)
+
+    for i in xrange(0, 10):
+      first = (i == 0)
+      AddDisplayRenderingStats(timer, browser_main, first, None)
+      timer.Advance(5, 10)
+      vblank_timer.TvAdvance(5000, 10000)
+
+    browser_main.EndSlice(timer.AdvanceAndGet())
+    timer.Advance(2, 4)
+    vblank_timer.TvAdvance(2000, 4000)
+
+    browser.FinalizeImport()
+    renderer.FinalizeImport()
+    timeline_markers = timeline.FindTimelineMarkers(['ActionA'])
+    timeline_ranges = [bounds.Bounds.CreateFromEvent(marker)
+                       for marker in timeline_markers]
+    stats = rendering_stats.RenderingStats(
+        renderer, browser, None, gpu, timeline_ranges)
+
+    # Compare rendering stats to reference - Only drm flip stats should
+    # count
+    self.assertEquals(stats.frame_timestamps, ref_stats.frame_timestamps)
+    self.assertEquals(stats.frame_times, ref_stats.frame_times)
+
+  def testBothDisplayAndImplStats(self):
+    timeline = model.TimelineModel()
+    timer = MockTimer()
+
+    ref_stats = ReferenceRenderingStats()
+    ref_stats.AppendNewRange()
+    renderer = timeline.GetOrCreateProcess(pid=2)
+    browser = timeline.GetOrCreateProcess(pid=3)
+    browser_main = browser.GetOrCreateThread(tid=31)
+    browser_main.BeginSlice('webkit.console', 'ActionA',
+                            timer.AdvanceAndGet(2, 4), '')
+
+    # Create main, impl, and display rendering stats.
+    for i in xrange(0, 10):
+      first = (i == 0)
+      AddImplThreadRenderingStats(timer, browser_main, first, None)
+      timer.Advance(2, 4)
+
+    for i in xrange(0, 10):
+      first = (i == 0)
+      AddDisplayRenderingStats(timer, browser_main, first, ref_stats)
+      timer.Advance(5, 10)
+
+    browser_main.EndSlice(timer.AdvanceAndGet())
+    timer.Advance(2, 4)
+
+    browser.FinalizeImport()
+    renderer.FinalizeImport()
+    timeline_markers = timeline.FindTimelineMarkers(['ActionA'])
+    timeline_ranges = [bounds.Bounds.CreateFromEvent(marker)
+                       for marker in timeline_markers]
+    stats = rendering_stats.RenderingStats(
+        renderer, browser, None, None, timeline_ranges)
+
+    # Compare rendering stats to reference - Only display stats should count
+    self.assertEquals(stats.frame_timestamps, ref_stats.frame_timestamps)
+    self.assertEquals(stats.frame_times, ref_stats.frame_times)
+
+  def testRangeWithoutFrames(self):
+    timer = MockTimer()
+    timeline = model.TimelineModel()
+
+    # Create a renderer process, with a main thread and impl thread.
+    renderer = timeline.GetOrCreateProcess(pid=2)
+    renderer_main = renderer.GetOrCreateThread(tid=21)
+    renderer_compositor = renderer.GetOrCreateThread(tid=22)
+
+    # Create 10 main and impl rendering stats events for Action A.
+    renderer_main.BeginSlice('webkit.console', 'ActionA',
+                             timer.AdvanceAndGet(2, 4), '')
+    for i in xrange(0, 10):
+      first = (i == 0)
+      AddImplThreadRenderingStats(timer, renderer_compositor, first, None)
+    renderer_main.EndSlice(timer.AdvanceAndGet(2, 4))
+    timer.Advance(2, 4)
+
+    # Create 5 main and impl rendering stats events not within any action.
+    for i in xrange(0, 5):
+      first = (i == 0)
+      AddImplThreadRenderingStats(timer, renderer_compositor, first, None)
+
+    # Create Action B without any frames. This should trigger
+    # NotEnoughFramesError when the RenderingStats object is created.
+    renderer_main.BeginSlice('webkit.console', 'ActionB',
+                             timer.AdvanceAndGet(2, 4), '')
+    renderer_main.EndSlice(timer.AdvanceAndGet(2, 4))
+
+    renderer.FinalizeImport()
+
+    timeline_markers = timeline.FindTimelineMarkers(['ActionA', 'ActionB'])
+    timeline_ranges = [bounds.Bounds.CreateFromEvent(marker)
+                       for marker in timeline_markers]
+
+    stats = rendering_stats.RenderingStats(
+        renderer, None, None, None, timeline_ranges)
+    self.assertEquals(0, len(stats.frame_timestamps[1]))
+
+  def testFromTimeline(self):
+    timeline = model.TimelineModel()
+
+    # Create a browser process and a renderer process, and a main thread and
+    # impl thread for each.
+    browser = timeline.GetOrCreateProcess(pid=1)
+    browser_compositor = browser.GetOrCreateThread(tid=12)
+    renderer = timeline.GetOrCreateProcess(pid=2)
+    renderer_main = renderer.GetOrCreateThread(tid=21)
+    renderer_compositor = renderer.GetOrCreateThread(tid=22)
+
+    timer = MockTimer()
+    renderer_ref_stats = ReferenceRenderingStats()
+    browser_ref_stats = ReferenceRenderingStats()
+
+    # Create 10 main and impl rendering stats events for Action A.
+    renderer_main.BeginSlice('webkit.console', 'ActionA',
+                             timer.AdvanceAndGet(2, 4), '')
+    renderer_ref_stats.AppendNewRange()
+    browser_ref_stats.AppendNewRange()
+    for i in xrange(0, 10):
+      first = (i == 0)
+      AddImplThreadRenderingStats(
+          timer, renderer_compositor, first, renderer_ref_stats)
+      AddImplThreadRenderingStats(
+          timer, browser_compositor, first, browser_ref_stats)
+    renderer_main.EndSlice(timer.AdvanceAndGet(2, 4))
+
+    # Create 5 main and impl rendering stats events not within any action.
+    for i in xrange(0, 5):
+      first = (i == 0)
+      AddImplThreadRenderingStats(timer, renderer_compositor, first, None)
+      AddImplThreadRenderingStats(timer, browser_compositor, first, None)
+
+    # Create 10 main and impl rendering stats events for Action B.
+    renderer_main.BeginSlice('webkit.console', 'ActionB',
+                             timer.AdvanceAndGet(2, 4), '')
+    renderer_ref_stats.AppendNewRange()
+    browser_ref_stats.AppendNewRange()
+    for i in xrange(0, 10):
+      first = (i == 0)
+      AddImplThreadRenderingStats(
+          timer, renderer_compositor, first, renderer_ref_stats)
+      AddImplThreadRenderingStats(
+          timer, browser_compositor, first, browser_ref_stats)
+    renderer_main.EndSlice(timer.AdvanceAndGet(2, 4))
+
+    # Create 10 main and impl rendering stats events for Action A.
+    renderer_main.BeginSlice('webkit.console', 'ActionA',
+                             timer.AdvanceAndGet(2, 4), '')
+    renderer_ref_stats.AppendNewRange()
+    browser_ref_stats.AppendNewRange()
+    for i in xrange(0, 10):
+      first = (i == 0)
+      AddImplThreadRenderingStats(
+          timer, renderer_compositor, first, renderer_ref_stats)
+      AddImplThreadRenderingStats(
+          timer, browser_compositor, first, browser_ref_stats)
+    renderer_main.EndSlice(timer.AdvanceAndGet(2, 4))
+    timer.Advance(2, 4)
+
+    browser.FinalizeImport()
+    renderer.FinalizeImport()
+
+    timeline_markers = timeline.FindTimelineMarkers(
+        ['ActionA', 'ActionB', 'ActionA'])
+    timeline_ranges = [bounds.Bounds.CreateFromEvent(marker)
+                       for marker in timeline_markers]
+    stats = rendering_stats.RenderingStats(
+        renderer, browser, None, None, timeline_ranges)
+
+    # Compare rendering stats to reference.
+    self.assertEquals(stats.frame_timestamps,
+                      browser_ref_stats.frame_timestamps)
+    self.assertEquals(stats.frame_times, browser_ref_stats.frame_times)
+    self.assertEquals(stats.approximated_pixel_percentages,
+                      renderer_ref_stats.approximated_pixel_percentages)
+    self.assertEquals(stats.checkerboarded_pixel_percentages,
+                      renderer_ref_stats.checkerboarded_pixel_percentages)
+
+  def testInputLatencyFromTimeline(self):
+    timeline = model.TimelineModel()
+
+    # Create a browser process and a renderer process.
+    browser = timeline.GetOrCreateProcess(pid=1)
+    browser_main = browser.GetOrCreateThread(tid=11)
+    renderer = timeline.GetOrCreateProcess(pid=2)
+    renderer_main = renderer.GetOrCreateThread(tid=21)
+
+    timer = MockTimer()
+    ref_latency = ReferenceInputLatencyStats()
+
+    # Create 10 input latency stats events for Action A.
+    renderer_main.BeginSlice('webkit.console', 'ActionA',
+                             timer.AdvanceAndGet(2, 4), '')
+    for _ in xrange(0, 10):
+      AddInputLatencyStats(timer, browser_main, renderer_main, ref_latency)
+    renderer_main.EndSlice(timer.AdvanceAndGet(2, 4))
+
+    # Create 5 input latency stats events not within any action.
+    timer.Advance(2, 4)
+    for _ in xrange(0, 5):
+      AddInputLatencyStats(timer, browser_main, renderer_main, None)
+
+    # Create 10 input latency stats events for Action B.
+    renderer_main.BeginSlice('webkit.console', 'ActionB',
+                             timer.AdvanceAndGet(2, 4), '')
+    for _ in xrange(0, 10):
+      AddInputLatencyStats(timer, browser_main, renderer_main, ref_latency)
+    renderer_main.EndSlice(timer.AdvanceAndGet(2, 4))
+
+    # Create 10 input latency stats events for Action A.
+    renderer_main.BeginSlice('webkit.console', 'ActionA',
+                             timer.AdvanceAndGet(2, 4), '')
+    for _ in xrange(0, 10):
+      AddInputLatencyStats(timer, browser_main, renderer_main, ref_latency)
+    renderer_main.EndSlice(timer.AdvanceAndGet(2, 4))
+
+    browser.FinalizeImport()
+    renderer.FinalizeImport()
+
+    latency_events = []
+
+    timeline_markers = timeline.FindTimelineMarkers(
+        ['ActionA', 'ActionB', 'ActionA'])
+    timeline_ranges = [bounds.Bounds.CreateFromEvent(marker)
+                       for marker in timeline_markers]
+    for timeline_range in timeline_ranges:
+      if timeline_range.is_empty:
+        continue
+      latency_events.extend(rendering_stats.GetLatencyEvents(
+          browser, timeline_range))
+
+    self.assertEquals(latency_events, ref_latency.input_event)
+    event_latency_result = rendering_stats.ComputeEventLatencies(latency_events)
+    self.assertEquals(event_latency_result,
+                      ref_latency.input_event_latency)
+
+    stats = rendering_stats.RenderingStats(
+        renderer, browser, None, None, timeline_ranges)
+    self.assertEquals(
+        perf_tests_helper.FlattenList(stats.input_event_latency),
+        [latency for name, latency in ref_latency.input_event_latency
+         if name != rendering_stats.MAIN_THREAD_SCROLL_UPDATE_EVENT_NAME])
+    self.assertEquals(
+        perf_tests_helper.FlattenList(stats.main_thread_scroll_latency),
+        [latency for name, latency in ref_latency.input_event_latency
+         if name == rendering_stats.MAIN_THREAD_SCROLL_UPDATE_EVENT_NAME])
+    self.assertEquals(
+        perf_tests_helper.FlattenList(stats.gesture_scroll_update_latency),
+        [latency for name, latency in ref_latency.input_event_latency
+         if name == rendering_stats.GESTURE_SCROLL_UPDATE_EVENT_NAME])
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/single_event.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/single_event.py
new file mode 100644
index 0000000..5d510c3
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/single_event.py
@@ -0,0 +1,46 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.value import improvement_direction
+from telemetry.value import list_of_scalar_values
+from telemetry.web_perf.metrics import timeline_based_metric
+
+
+class _SingleEventMetric(timeline_based_metric.TimelineBasedMetric):
+  """Reports directly durations of specific trace events that start during the
+  user interaction.
+  """
+
+  def __init__(self, trace_event_name, metric_name, metric_description=None):
+    super(_SingleEventMetric, self).__init__()
+    self._TRACE_EVENT_NAME = trace_event_name
+    self._metric_name = metric_name
+    self._metric_description = metric_description
+
+  def AddResults(self, model, renderer_thread, interactions, results):
+    del model  # unused
+    assert interactions
+    self._AddResultsInternal(renderer_thread.parent.IterAllSlices(),
+                             interactions, results)
+
+  def _AddResultsInternal(self, events, interactions, results):
+    events_found = []
+    for event in events:
+      if (event.name == self._TRACE_EVENT_NAME) and any(
+              interaction.start <= event.start <= interaction.end
+              for interaction in interactions):
+        if event.has_thread_timestamps:
+          events_found.append(event.thread_duration)
+        else:
+          events_found.append(event.duration)
+    if not events_found:
+      return
+    results.AddValue(list_of_scalar_values.ListOfScalarValues(
+      page=results.current_page,
+      tir_label=interactions[0].label,
+      name=self._metric_name,
+      units='ms',
+      values=events_found,
+      description=self._metric_description,
+      improvement_direction=improvement_direction.DOWN))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/single_event_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/single_event_unittest.py
new file mode 100644
index 0000000..f96ef9f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/single_event_unittest.py
@@ -0,0 +1,72 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from collections import namedtuple
+from telemetry.internal.results import page_test_results
+from telemetry.page import page
+from telemetry.web_perf.metrics import single_event
+from telemetry.web_perf import timeline_interaction_record
+
+TRACE_EVENT_NAME = 'FrameView::performLayout'
+METRIC_NAME = 'layout'
+FakeEventTuple = namedtuple(
+    'Event',
+    'start, end, name, duration, thread_duration, has_thread_timestamps')
+Interaction = timeline_interaction_record.TimelineInteractionRecord
+
+class SingleEventTestMetric(single_event._SingleEventMetric):
+  def __init__(self):
+    super(SingleEventTestMetric, self).__init__(TRACE_EVENT_NAME, METRIC_NAME)
+
+def GetSingleEventMetrics(events, interactions):
+  results = page_test_results.PageTestResults()
+  results.WillRunPage(page.Page('file://blank.html'))
+  SingleEventTestMetric()._AddResultsInternal(events, interactions, results)
+  return dict((value.name, value.values) for value in
+              results.current_page_run.values)
+
+def FakeEvent(start, end, name=TRACE_EVENT_NAME):
+  dur = end - start
+  return FakeEventTuple(start, end, name, dur, dur, True)
+
+
+class SingleEventMetricUnitTest(unittest.TestCase):
+  def testSingleEventMetric(self):
+    events = [FakeEvent(0, 1),
+              FakeEvent(9, 11),
+              FakeEventTuple(10, 13, TRACE_EVENT_NAME, 3, 0, False),
+              FakeEvent(20, 24),
+              FakeEvent(21, 26),
+              FakeEvent(29, 35),
+              FakeEvent(30, 37),
+              FakeEvent(40, 48),
+              FakeEvent(41, 50),
+              FakeEvent(10, 13, name='something'),
+              FakeEvent(20, 24, name='FrameView::something'),
+              FakeEvent(30, 37, name='SomeThing::performLayout'),
+              FakeEvent(40, 48, name='something else')]
+    interactions = [Interaction('interaction', 10, 20),
+                    Interaction('interaction', 30, 40)]
+
+    self.assertFalse(GetSingleEventMetrics(events, []))
+    self.assertFalse(GetSingleEventMetrics([], interactions))
+
+    # The first event starts before the first interaction, so it is ignored.
+    # The second event starts before the first interaction, so it is ignored.
+    # The third event starts during the first interaction, and its duration is
+    # 13 - 10 = 3.
+    # The fourth event starts during the first interaction, and its duration is
+    # 24 - 20 = 4.
+    # The fifth event starts between the two interactions, so it is ignored.
+    # The sixth event starts between the two interactions, so it is ignored.
+    # The seventh event starts during the second interaction, and its duration
+    # is 37 - 30 = 7.
+    # The eighth event starts during the second interaction, and its duration is
+    # 48 - 40 = 8.
+    # The ninth event starts after the last interaction, so it is ignored.
+    # The rest of the events have the wrong name, so they are ignored.
+    self.assertEqual({METRIC_NAME: [3, 4, 7, 8]}, GetSingleEventMetrics(
+        events, interactions))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/smoothness.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/smoothness.py
new file mode 100644
index 0000000..5caa568
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/smoothness.py
@@ -0,0 +1,349 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+
+from telemetry.util import perf_tests_helper
+from telemetry.util import statistics
+from telemetry.value import improvement_direction
+from telemetry.value import list_of_scalar_values
+from telemetry.value import scalar
+from telemetry.web_perf.metrics import rendering_stats
+from telemetry.web_perf.metrics import timeline_based_metric
+
+
+NOT_ENOUGH_FRAMES_MESSAGE = (
+  'Not enough frames for smoothness metrics (at least two are required).\n'
+  'Issues that have caused this in the past:\n'
+  '- Browser bugs that prevents the page from redrawing\n'
+  '- Bugs in the synthetic gesture code\n'
+  '- Page and benchmark out of sync (e.g. clicked element was renamed)\n'
+  '- Pages that render extremely slow\n'
+  '- Pages that can\'t be scrolled')
+
+
+class SmoothnessMetric(timeline_based_metric.TimelineBasedMetric):
+  """Computes metrics that measure smoothness of animations over given ranges.
+
+  Animations are typically considered smooth if the frame rates are close to
+  60 frames per second (fps) and uniformly distributed over the sequence. To
+  determine if a timeline range contains a smooth animation, we update the
+  results object with several representative metrics:
+
+    frame_times: A list of raw frame times
+    mean_frame_time: The arithmetic mean of frame times
+    percentage_smooth: Percentage of frames that were hitting 60 FPS.
+    frame_time_discrepancy: The absolute discrepancy of frame timestamps
+    mean_pixels_approximated: The mean percentage of pixels approximated
+    queueing_durations: The queueing delay between compositor & main threads
+
+  Note that if any of the interaction records provided to AddResults have less
+  than 2 frames, we will return telemetry values with None values for each of
+  the smoothness metrics. Similarly, older browsers without support for
+  tracking the BeginMainFrame events will report a ListOfScalarValues with a
+  None value for the queueing duration metric.
+  """
+
+  def __init__(self):
+    super(SmoothnessMetric, self).__init__()
+
+  def AddResults(self, model, renderer_thread, interaction_records, results):
+    self.VerifyNonOverlappedRecords(interaction_records)
+    renderer_process = renderer_thread.parent
+    stats = rendering_stats.RenderingStats(
+      renderer_process, model.browser_process, model.surface_flinger_process,
+      model.gpu_process, [r.GetBounds() for r in interaction_records])
+    has_surface_flinger_stats = model.surface_flinger_process is not None
+    self._PopulateResultsFromStats(results, stats, has_surface_flinger_stats)
+
+  def _PopulateResultsFromStats(self, results, stats,
+                                has_surface_flinger_stats):
+    page = results.current_page
+    values = [
+        self._ComputeQueueingDuration(page, stats),
+        self._ComputeFrameTimeDiscrepancy(page, stats),
+        self._ComputeMeanPixelsApproximated(page, stats),
+        self._ComputeMeanPixelsCheckerboarded(page, stats)
+    ]
+    values += self._ComputeLatencyMetric(page, stats, 'input_event_latency',
+                                         stats.input_event_latency)
+    values += self._ComputeLatencyMetric(page, stats,
+                                         'main_thread_scroll_latency',
+                                         stats.main_thread_scroll_latency)
+    values.append(self._ComputeFirstGestureScrollUpdateLatencies(page, stats))
+    values += self._ComputeFrameTimeMetric(page, stats)
+    if has_surface_flinger_stats:
+      values += self._ComputeSurfaceFlingerMetric(page, stats)
+
+    for v in values:
+      results.AddValue(v)
+
+  def _HasEnoughFrames(self, list_of_frame_timestamp_lists):
+    """Whether we have collected at least two frames in every timestamp list."""
+    return all(len(s) >= 2 for s in list_of_frame_timestamp_lists)
+
+  @staticmethod
+  def _GetNormalizedDeltas(data, refresh_period, min_normalized_delta=None):
+    deltas = [t2 - t1 for t1, t2 in zip(data, data[1:])]
+    if min_normalized_delta != None:
+      deltas = [d for d in deltas
+                if d / refresh_period >= min_normalized_delta]
+    return (deltas, [delta / refresh_period for delta in deltas])
+
+  @staticmethod
+  def _JoinTimestampRanges(frame_timestamps):
+    """Joins ranges of timestamps, adjusting timestamps to remove deltas
+    between the start of a range and the end of the prior range.
+    """
+    timestamps = []
+    for timestamp_range in frame_timestamps:
+      if len(timestamps) == 0:
+        timestamps.extend(timestamp_range)
+      else:
+        for i in range(1, len(timestamp_range)):
+          timestamps.append(timestamps[-1] +
+              timestamp_range[i] - timestamp_range[i-1])
+    return timestamps
+
+  def _ComputeSurfaceFlingerMetric(self, page, stats):
+    jank_count = None
+    avg_surface_fps = None
+    max_frame_delay = None
+    frame_lengths = None
+    none_value_reason = None
+    if self._HasEnoughFrames(stats.frame_timestamps):
+      timestamps = self._JoinTimestampRanges(stats.frame_timestamps)
+      frame_count = len(timestamps)
+      milliseconds = timestamps[-1] - timestamps[0]
+      min_normalized_frame_length = 0.5
+
+      frame_lengths, normalized_frame_lengths = \
+          self._GetNormalizedDeltas(timestamps, stats.refresh_period,
+                                    min_normalized_frame_length)
+      if len(frame_lengths) < frame_count - 1:
+        logging.warning('Skipping frame lengths that are too short.')
+        frame_count = len(frame_lengths) + 1
+      if len(frame_lengths) == 0:
+        raise Exception('No valid frames lengths found.')
+      _, normalized_changes = \
+          self._GetNormalizedDeltas(frame_lengths, stats.refresh_period)
+      jankiness = [max(0, round(change)) for change in normalized_changes]
+      pause_threshold = 20
+      jank_count = sum(1 for change in jankiness
+                       if change > 0 and change < pause_threshold)
+      avg_surface_fps = int(round((frame_count - 1) * 1000.0 / milliseconds))
+      max_frame_delay = round(max(normalized_frame_lengths))
+      frame_lengths = normalized_frame_lengths
+    else:
+      none_value_reason = NOT_ENOUGH_FRAMES_MESSAGE
+
+    return (
+        scalar.ScalarValue(
+            page, 'avg_surface_fps', 'fps', avg_surface_fps,
+            description='Average frames per second as measured by the '
+                        'platform\'s SurfaceFlinger.',
+            none_value_reason=none_value_reason,
+            improvement_direction=improvement_direction.UP),
+        scalar.ScalarValue(
+            page, 'jank_count', 'janks', jank_count,
+            description='Number of changes in frame rate as measured by the '
+                        'platform\'s SurfaceFlinger.',
+            none_value_reason=none_value_reason,
+            improvement_direction=improvement_direction.DOWN),
+        scalar.ScalarValue(
+            page, 'max_frame_delay', 'vsyncs', max_frame_delay,
+            description='Largest frame time as measured by the platform\'s '
+                        'SurfaceFlinger.',
+            none_value_reason=none_value_reason,
+            improvement_direction=improvement_direction.DOWN),
+        list_of_scalar_values.ListOfScalarValues(
+            page, 'frame_lengths', 'vsyncs', frame_lengths,
+            description='Frame time in vsyncs as measured by the platform\'s '
+                        'SurfaceFlinger.',
+            none_value_reason=none_value_reason,
+            improvement_direction=improvement_direction.DOWN)
+    )
+
+  def _ComputeLatencyMetric(self, page, stats, name, list_of_latency_lists):
+    """Returns Values for the mean and discrepancy for given latency stats."""
+    mean_latency = None
+    latency_discrepancy = None
+    none_value_reason = None
+    if self._HasEnoughFrames(stats.frame_timestamps):
+      latency_list = perf_tests_helper.FlattenList(list_of_latency_lists)
+      if len(latency_list) == 0:
+        return ()
+      mean_latency = round(statistics.ArithmeticMean(latency_list), 3)
+      latency_discrepancy = (
+          round(statistics.DurationsDiscrepancy(latency_list), 4))
+    else:
+      none_value_reason = NOT_ENOUGH_FRAMES_MESSAGE
+    return (
+      scalar.ScalarValue(
+          page, 'mean_%s' % name, 'ms', mean_latency,
+          description='Arithmetic mean of the raw %s values' % name,
+          none_value_reason=none_value_reason,
+          improvement_direction=improvement_direction.DOWN),
+      scalar.ScalarValue(
+          page, '%s_discrepancy' % name, 'ms', latency_discrepancy,
+          description='Discrepancy of the raw %s values' % name,
+          none_value_reason=none_value_reason,
+          improvement_direction=improvement_direction.DOWN)
+    )
+
+  def _ComputeFirstGestureScrollUpdateLatencies(self, page, stats):
+    """Returns a ListOfScalarValuesValues of gesture scroll update latencies.
+
+    Returns a Value for the first gesture scroll update latency for each
+    interaction record in |stats|.
+    """
+    none_value_reason = None
+    first_gesture_scroll_update_latencies = [round(latencies[0], 4)
+        for latencies in stats.gesture_scroll_update_latency
+        if len(latencies)]
+    if (not self._HasEnoughFrames(stats.frame_timestamps) or
+        not first_gesture_scroll_update_latencies):
+      first_gesture_scroll_update_latencies = None
+      none_value_reason = NOT_ENOUGH_FRAMES_MESSAGE
+    return list_of_scalar_values.ListOfScalarValues(
+        page, 'first_gesture_scroll_update_latency', 'ms',
+        first_gesture_scroll_update_latencies,
+        description='First gesture scroll update latency measures the time it '
+                    'takes to process the very first gesture scroll update '
+                    'input event. The first scroll gesture can often get '
+                    'delayed by work related to page loading.',
+        none_value_reason=none_value_reason,
+        improvement_direction=improvement_direction.DOWN)
+
+  def _ComputeQueueingDuration(self, page, stats):
+    """Returns a Value for the frame queueing durations."""
+    queueing_durations = None
+    none_value_reason = None
+    if 'frame_queueing_durations' in stats.errors:
+      none_value_reason = stats.errors['frame_queueing_durations']
+    elif self._HasEnoughFrames(stats.frame_timestamps):
+      queueing_durations = perf_tests_helper.FlattenList(
+          stats.frame_queueing_durations)
+      if len(queueing_durations) == 0:
+        queueing_durations = None
+        none_value_reason = 'No frame queueing durations recorded.'
+    else:
+      none_value_reason = NOT_ENOUGH_FRAMES_MESSAGE
+    return list_of_scalar_values.ListOfScalarValues(
+        page, 'queueing_durations', 'ms', queueing_durations,
+        description='The frame queueing duration quantifies how out of sync '
+                    'the compositor and renderer threads are. It is the amount '
+                    'of wall time that elapses between a '
+                    'ScheduledActionSendBeginMainFrame event in the compositor '
+                    'thread and the corresponding BeginMainFrame event in the '
+                    'main thread.',
+        none_value_reason=none_value_reason,
+        improvement_direction=improvement_direction.DOWN)
+
+  def _ComputeFrameTimeMetric(self, page, stats):
+    """Returns Values for the frame time metrics.
+
+    This includes the raw and mean frame times, as well as the percentage of
+    frames that were hitting 60 fps.
+    """
+    frame_times = None
+    mean_frame_time = None
+    percentage_smooth = None
+    none_value_reason = None
+    if self._HasEnoughFrames(stats.frame_timestamps):
+      frame_times = perf_tests_helper.FlattenList(stats.frame_times)
+      mean_frame_time = round(statistics.ArithmeticMean(frame_times), 3)
+      # We use 17ms as a somewhat looser threshold, instead of 1000.0/60.0.
+      smooth_threshold = 17.0
+      smooth_count = sum(1 for t in frame_times if t < smooth_threshold)
+      percentage_smooth = float(smooth_count) / len(frame_times) * 100.0
+    else:
+      none_value_reason = NOT_ENOUGH_FRAMES_MESSAGE
+    return (
+        list_of_scalar_values.ListOfScalarValues(
+            page, 'frame_times', 'ms', frame_times,
+            description='List of raw frame times, helpful to understand the '
+                        'other metrics.',
+            none_value_reason=none_value_reason,
+            improvement_direction=improvement_direction.DOWN),
+        scalar.ScalarValue(
+            page, 'mean_frame_time', 'ms', mean_frame_time,
+            description='Arithmetic mean of frame times.',
+            none_value_reason=none_value_reason,
+            improvement_direction=improvement_direction.DOWN),
+        scalar.ScalarValue(
+            page, 'percentage_smooth', 'score', percentage_smooth,
+            description='Percentage of frames that were hitting 60 fps.',
+            none_value_reason=none_value_reason,
+            improvement_direction=improvement_direction.UP)
+    )
+
+  def _ComputeFrameTimeDiscrepancy(self, page, stats):
+    """Returns a Value for the absolute discrepancy of frame time stamps."""
+
+    frame_discrepancy = None
+    none_value_reason = None
+    if self._HasEnoughFrames(stats.frame_timestamps):
+      frame_discrepancy = round(statistics.TimestampsDiscrepancy(
+          stats.frame_timestamps), 4)
+    else:
+      none_value_reason = NOT_ENOUGH_FRAMES_MESSAGE
+    return scalar.ScalarValue(
+        page, 'frame_time_discrepancy', 'ms', frame_discrepancy,
+        description='Absolute discrepancy of frame time stamps, where '
+                    'discrepancy is a measure of irregularity. It quantifies '
+                    'the worst jank. For a single pause, discrepancy '
+                    'corresponds to the length of this pause in milliseconds. '
+                    'Consecutive pauses increase the discrepancy. This metric '
+                    'is important because even if the mean and 95th '
+                    'percentile are good, one long pause in the middle of an '
+                    'interaction is still bad.',
+        none_value_reason=none_value_reason,
+        improvement_direction=improvement_direction.DOWN)
+
+  def _ComputeMeanPixelsApproximated(self, page, stats):
+    """Add the mean percentage of pixels approximated.
+
+    This looks at tiles which are missing or of low or non-ideal resolution.
+    """
+    mean_pixels_approximated = None
+    none_value_reason = None
+    if self._HasEnoughFrames(stats.frame_timestamps):
+      mean_pixels_approximated = round(statistics.ArithmeticMean(
+          perf_tests_helper.FlattenList(
+              stats.approximated_pixel_percentages)), 3)
+    else:
+      none_value_reason = NOT_ENOUGH_FRAMES_MESSAGE
+    return scalar.ScalarValue(
+        page, 'mean_pixels_approximated', 'percent', mean_pixels_approximated,
+        description='Percentage of pixels that were approximated '
+                    '(checkerboarding, low-resolution tiles, etc.).',
+        none_value_reason=none_value_reason,
+        improvement_direction=improvement_direction.DOWN)
+
+  def _ComputeMeanPixelsCheckerboarded(self, page, stats):
+    """Add the mean percentage of pixels checkerboarded.
+
+    This looks at tiles which are only missing.
+    It does not take into consideration tiles which are of low or
+    non-ideal resolution.
+    """
+    mean_pixels_checkerboarded = None
+    none_value_reason = None
+    if self._HasEnoughFrames(stats.frame_timestamps):
+      if rendering_stats.CHECKERBOARDED_PIXEL_ERROR in stats.errors:
+        none_value_reason = stats.errors[
+            rendering_stats.CHECKERBOARDED_PIXEL_ERROR]
+      else:
+        mean_pixels_checkerboarded = round(statistics.ArithmeticMean(
+            perf_tests_helper.FlattenList(
+                stats.checkerboarded_pixel_percentages)), 3)
+    else:
+      none_value_reason = NOT_ENOUGH_FRAMES_MESSAGE
+    return scalar.ScalarValue(
+        page, 'mean_pixels_checkerboarded', 'percent',
+        mean_pixels_checkerboarded,
+        description='Percentage of pixels that were checkerboarded.',
+        none_value_reason=none_value_reason,
+        improvement_direction=improvement_direction.DOWN)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/smoothness_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/smoothness_unittest.py
new file mode 100644
index 0000000..efa5716
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/smoothness_unittest.py
@@ -0,0 +1,286 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from telemetry.internal.results import page_test_results
+from telemetry.page import page as page_module
+from telemetry.web_perf.metrics import rendering_stats
+from telemetry.web_perf.metrics import smoothness
+
+
+class _MockRenderingStats(object):
+
+  stats = ['refresh_period', 'frame_timestamps', 'frame_times', 'paint_times',
+           'painted_pixel_counts', 'record_times',
+           'recorded_pixel_counts', 'approximated_pixel_percentages',
+           'checkerboarded_pixel_percentages', 'input_event_latency',
+           'frame_queueing_durations', 'main_thread_scroll_latency',
+           'gesture_scroll_update_latency']
+
+  def __init__(self, **kwargs):
+    self.input_event_latency = None  # to avoid pylint no-member error
+    self.errors = {}
+    for stat in self.stats:
+      value = kwargs[stat] if stat in kwargs else None
+      setattr(self, stat, value)
+
+
+#pylint: disable=protected-access
+class SmoothnessMetricUnitTest(unittest.TestCase):
+
+  def setUp(self):
+    self.metric = smoothness.SmoothnessMetric()
+    self.page = page_module.Page('file://blank.html')
+    self.good_timestamps = [[10, 20], [30, 40, 50]]
+    self.not_enough_frames_timestamps = [[10], [20, 30, 40]]
+
+  def testPopulateResultsFromStats(self):
+    stats = _MockRenderingStats()
+    for stat in _MockRenderingStats.stats:
+      # Just set fake data for all of the relevant arrays of stats typically
+      # found in a RenderingStats object.
+      setattr(stats, stat, [[10, 20], [30, 40, 50]])
+    results = page_test_results.PageTestResults()
+    results.WillRunPage(self.page)
+    self.metric._PopulateResultsFromStats(results, stats, False)
+    current_page_run = results.current_page_run
+    self.assertTrue(current_page_run.ok)
+    expected_values_count = 12
+    self.assertEquals(expected_values_count, len(current_page_run.values))
+
+  def testHasEnoughFrames(self):
+    # This list will pass since every sub-array has at least 2 frames.
+    has_enough_frames = self.metric._HasEnoughFrames(self.good_timestamps)
+    self.assertTrue(has_enough_frames)
+
+  def testHasEnoughFramesWithNotEnoughFrames(self):
+    # This list will fail since the first sub-array only has a single frame.
+    has_enough_frames = self.metric._HasEnoughFrames(
+        self.not_enough_frames_timestamps)
+    self.assertFalse(has_enough_frames)
+
+  def testComputeSurfaceFlingerMetricNoJank(self):
+    stats = _MockRenderingStats(refresh_period=10,
+                                frame_timestamps=[[10, 20], [130, 140, 150]],
+                                frame_times=[[10], [10, 10]])
+    avg_surface_fps, jank_count, max_frame_delay, frame_lengths = (
+        self.metric._ComputeSurfaceFlingerMetric(self.page, stats))
+    self.assertEquals([1, 1, 1], frame_lengths.values)
+    self.assertEquals(1, max_frame_delay.value)
+    self.assertEquals(0, jank_count.value)
+    self.assertEquals(100, avg_surface_fps.value)
+
+  def testComputeSurfaceFlingerMetricJank(self):
+    stats = _MockRenderingStats(
+        refresh_period=10,
+        frame_timestamps=[[10, 20, 50], [130, 140, 150, 170, 180]],
+        frame_times=[[10, 30], [10, 10, 20, 10]])
+    avg_surface_fps, jank_count, max_frame_delay, frame_lengths = (
+        self.metric._ComputeSurfaceFlingerMetric(self.page, stats))
+    self.assertEquals([1, 3, 1, 1, 2, 1], frame_lengths.values)
+    self.assertEquals(3, max_frame_delay.value)
+    self.assertEquals(2, jank_count.value)
+    self.assertEquals(67, avg_surface_fps.value)
+
+  def testComputeFrameTimeMetricWithNotEnoughFrames(self):
+    stats = _MockRenderingStats(
+        refresh_period=10,
+        frame_timestamps=self.not_enough_frames_timestamps,
+        frame_times=[[10, 20], [30, 40, 50]])
+    avg_surface_fps, jank_count, max_frame_delay, frame_lengths = (
+        self.metric._ComputeSurfaceFlingerMetric(self.page, stats))
+    self.assertEquals(None, avg_surface_fps.value)
+    self.assertEquals(smoothness.NOT_ENOUGH_FRAMES_MESSAGE,
+                      avg_surface_fps.none_value_reason)
+    self.assertEquals(None, jank_count.value)
+    self.assertEquals(smoothness.NOT_ENOUGH_FRAMES_MESSAGE,
+                      jank_count.none_value_reason)
+    self.assertEquals(None, max_frame_delay.value)
+    self.assertEquals(smoothness.NOT_ENOUGH_FRAMES_MESSAGE,
+                      max_frame_delay.none_value_reason)
+    self.assertEquals(None, frame_lengths.values)
+    self.assertEquals(smoothness.NOT_ENOUGH_FRAMES_MESSAGE,
+                      frame_lengths.none_value_reason)
+
+  def testComputeLatencyMetric(self):
+    stats = _MockRenderingStats(frame_timestamps=self.good_timestamps,
+                               input_event_latency=[[10, 20], [30, 40, 50]])
+    # pylint: disable=unbalanced-tuple-unpacking
+    mean_value, discrepancy_value = self.metric._ComputeLatencyMetric(
+        self.page, stats, 'input_event_latency', stats.input_event_latency)
+    self.assertEquals(30, mean_value.value)
+    self.assertEquals(60, discrepancy_value.value)
+
+  def testComputeLatencyMetricWithMissingData(self):
+    stats = _MockRenderingStats(frame_timestamps=self.good_timestamps,
+                               input_event_latency=[[], []])
+    value = self.metric._ComputeLatencyMetric(
+        self.page, stats, 'input_event_latency', stats.input_event_latency)
+    self.assertEquals((), value)
+
+  def testComputeLatencyMetricWithNotEnoughFrames(self):
+    stats = _MockRenderingStats(
+        frame_timestamps=self.not_enough_frames_timestamps,
+        input_event_latency=[[], []])
+    # pylint: disable=unbalanced-tuple-unpacking
+    mean_value, discrepancy_value = self.metric._ComputeLatencyMetric(
+        self.page, stats, 'input_event_latency', stats.input_event_latency)
+    self.assertEquals(None, mean_value.value)
+    self.assertEquals(smoothness.NOT_ENOUGH_FRAMES_MESSAGE,
+                      mean_value.none_value_reason)
+    self.assertEquals(None, discrepancy_value.value)
+    self.assertEquals(smoothness.NOT_ENOUGH_FRAMES_MESSAGE,
+                      discrepancy_value.none_value_reason)
+
+  def testComputeGestureScrollUpdateLatencies(self):
+    stats = _MockRenderingStats(
+        frame_timestamps=self.good_timestamps,
+        gesture_scroll_update_latency=[[10, 20], [30, 40, 50]])
+    gesture_value = self.metric._ComputeFirstGestureScrollUpdateLatencies(
+        self.page, stats)
+    self.assertEquals([10, 30], gesture_value.values)
+
+  def testComputeGestureScrollUpdateLatenciesWithMissingData(self):
+    stats = _MockRenderingStats(
+        frame_timestamps=self.good_timestamps,
+        gesture_scroll_update_latency=[[], []])
+    value = self.metric._ComputeFirstGestureScrollUpdateLatencies(
+        self.page, stats)
+    self.assertEquals(None, value.values)
+
+  def testComputeGestureScrollUpdateLatenciesWithNotEnoughFrames(self):
+    stats = _MockRenderingStats(
+        frame_timestamps=self.not_enough_frames_timestamps,
+        gesture_scroll_update_latency=[[10, 20], [30, 40, 50]])
+    gesture_value = self.metric._ComputeFirstGestureScrollUpdateLatencies(
+        self.page, stats)
+    self.assertEquals(None, gesture_value.values)
+    self.assertEquals(smoothness.NOT_ENOUGH_FRAMES_MESSAGE,
+                      gesture_value.none_value_reason)
+
+  def testComputeQueueingDuration(self):
+    stats = _MockRenderingStats(frame_timestamps=self.good_timestamps,
+                               frame_queueing_durations=[[10, 20], [30, 40]])
+    list_of_scalar_values = self.metric._ComputeQueueingDuration(self.page,
+                                                                stats)
+    self.assertEquals([10, 20, 30, 40], list_of_scalar_values.values)
+
+  def testComputeQueueingDurationWithMissingData(self):
+    stats = _MockRenderingStats(frame_timestamps=self.good_timestamps,
+                               frame_queueing_durations=[[], []])
+    list_of_scalar_values = self.metric._ComputeQueueingDuration(
+        self.page, stats)
+    self.assertEquals(None, list_of_scalar_values.values)
+    self.assertEquals('No frame queueing durations recorded.',
+                      list_of_scalar_values.none_value_reason)
+
+  def testComputeQueueingDurationWithMissingDataAndErrorValue(self):
+    stats = _MockRenderingStats(frame_timestamps=self.good_timestamps,
+                               frame_queueing_durations=[[], []])
+    stats.errors['frame_queueing_durations'] = (
+        'Current chrome version does not support the queueing delay metric.')
+    list_of_scalar_values = self.metric._ComputeQueueingDuration(
+        self.page, stats)
+    self.assertEquals(None, list_of_scalar_values.values)
+    self.assertEquals(
+        'Current chrome version does not support the queueing delay metric.',
+        list_of_scalar_values.none_value_reason)
+
+  def testComputeQueueingDurationWithNotEnoughFrames(self):
+    stats = _MockRenderingStats(
+        frame_timestamps=self.not_enough_frames_timestamps,
+        frame_queueing_durations=[[10, 20], [30, 40, 50]])
+    list_of_scalar_values = self.metric._ComputeQueueingDuration(self.page,
+                                                                stats)
+    self.assertEquals(None, list_of_scalar_values.values)
+    self.assertEquals(smoothness.NOT_ENOUGH_FRAMES_MESSAGE,
+                      list_of_scalar_values.none_value_reason)
+
+  def testComputeFrameTimeMetric(self):
+    stats = _MockRenderingStats(frame_timestamps=self.good_timestamps,
+                               frame_times=[[10, 20], [30, 40, 50]])
+    frame_times_value, mean_frame_time_value, percentage_smooth_value = (
+        self.metric._ComputeFrameTimeMetric(self.page, stats))
+    self.assertEquals([10, 20, 30, 40, 50], frame_times_value.values)
+    self.assertEquals(30, mean_frame_time_value.value)
+    self.assertEquals(20, percentage_smooth_value.value)
+
+  def testComputeFrameTimeMetricWithNotEnoughFrames2(self):
+    stats = _MockRenderingStats(
+        frame_timestamps=self.not_enough_frames_timestamps,
+        frame_times=[[10, 20], [30, 40, 50]])
+    frame_times_value, mean_frame_time_value, percentage_smooth_value = (
+        self.metric._ComputeFrameTimeMetric(self.page, stats))
+    self.assertEquals(None, frame_times_value.values)
+    self.assertEquals(smoothness.NOT_ENOUGH_FRAMES_MESSAGE,
+                      frame_times_value.none_value_reason)
+    self.assertEquals(None, mean_frame_time_value.value)
+    self.assertEquals(smoothness.NOT_ENOUGH_FRAMES_MESSAGE,
+                      mean_frame_time_value.none_value_reason)
+    self.assertEquals(None, percentage_smooth_value.value)
+    self.assertEquals(smoothness.NOT_ENOUGH_FRAMES_MESSAGE,
+                      percentage_smooth_value.none_value_reason)
+
+  def testComputeFrameTimeDiscrepancy(self):
+    stats = _MockRenderingStats(frame_timestamps=self.good_timestamps)
+    frame_time_discrepancy_value = self.metric._ComputeFrameTimeDiscrepancy(
+        self.page, stats)
+    self.assertEquals(10, frame_time_discrepancy_value.value)
+
+  def testComputeFrameTimeDiscrepancyWithNotEnoughFrames(self):
+    stats = _MockRenderingStats(
+        frame_timestamps=self.not_enough_frames_timestamps)
+    frame_time_discrepancy_value = self.metric._ComputeFrameTimeDiscrepancy(
+        self.page, stats)
+    self.assertEquals(None, frame_time_discrepancy_value.value)
+    self.assertEquals(smoothness.NOT_ENOUGH_FRAMES_MESSAGE,
+                      frame_time_discrepancy_value.none_value_reason)
+
+  def testComputeMeanPixelsApproximated(self):
+    stats = _MockRenderingStats(
+        frame_timestamps=self.good_timestamps,
+        approximated_pixel_percentages=[[10, 20], [30, 40, 50]])
+    mean_pixels_value = self.metric._ComputeMeanPixelsApproximated(
+        self.page, stats)
+    self.assertEquals(30, mean_pixels_value.value)
+
+  def testComputeMeanPixelsApproximatedWithNotEnoughFrames(self):
+    stats = _MockRenderingStats(
+        frame_timestamps=self.not_enough_frames_timestamps,
+        approximated_pixel_percentages=[[10, 20], [30, 40, 50]])
+    mean_pixels_value = self.metric._ComputeMeanPixelsApproximated(
+        self.page, stats)
+    self.assertEquals(None, mean_pixels_value.value)
+    self.assertEquals(smoothness.NOT_ENOUGH_FRAMES_MESSAGE,
+                      mean_pixels_value.none_value_reason)
+
+  def testComputeMeanPixelsCheckerboarded(self):
+    stats = _MockRenderingStats(
+        frame_timestamps=self.good_timestamps,
+        checkerboarded_pixel_percentages=[[10, 20], [30, 40, 50]])
+    mean_pixels_value = self.metric._ComputeMeanPixelsCheckerboarded(
+        self.page, stats)
+    self.assertEquals(30, mean_pixels_value.value)
+
+  def testComputeMeanPixelsCheckerboardedWithNotEnoughFrames(self):
+    stats = _MockRenderingStats(
+        frame_timestamps=self.not_enough_frames_timestamps,
+        checkerboarded_pixel_percentages=[[10, 20], [30, 40, 50]])
+    mean_pixels_value = self.metric._ComputeMeanPixelsCheckerboarded(
+        self.page, stats)
+    self.assertEquals(None, mean_pixels_value.value)
+    self.assertEquals(smoothness.NOT_ENOUGH_FRAMES_MESSAGE,
+                      mean_pixels_value.none_value_reason)
+
+  def testComputeMeanPixelsCheckerboardedWithNoData(self):
+    stats = _MockRenderingStats(
+        frame_timestamps=self.good_timestamps,
+        checkerboarded_pixel_percentages=None)
+    stats.errors[rendering_stats.CHECKERBOARDED_PIXEL_ERROR] = 'test error'
+    mean_pixels_value = self.metric._ComputeMeanPixelsCheckerboarded(
+        self.page, stats)
+    self.assertEquals(None, mean_pixels_value.value)
+    self.assertEquals('test error',
+                      mean_pixels_value.none_value_reason)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/startup.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/startup.py
new file mode 100644
index 0000000..bac6b58
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/startup.py
@@ -0,0 +1,95 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry import value
+from telemetry.web_perf.metrics import timeline_based_metric
+
+_PROCESS_CREATION = 'Startup.BrowserProcessCreation'
+_MAIN_ENTRY_POINT = 'Startup.BrowserMainEntryPoint'
+
+# A dictionary that maps metric names to a value, which can be either of
+# the two:
+#  1. A tuple of one event name if the event itself contains reported duration
+#  2. A tuple of two event names if the value to report is the time difference
+#     between starting these events
+_METRICS = {
+  'messageloop_start_time':
+      ('Startup.BrowserMessageLoopStartTimeFromMainEntry2',),
+
+  'window_display_time':
+      ('Startup.BrowserWindowDisplay',),
+
+  'open_tabs_time':
+      ('Startup.BrowserOpenTabs',),
+
+  'first_non_empty_paint_time':
+      ('Startup.FirstWebContents.NonEmptyPaint2',),
+
+  'first_main_frame_load_time':
+      ('Startup.FirstWebContents.MainFrameLoad2',),
+
+  'foreground_tab_load_complete':
+      (_MAIN_ENTRY_POINT, 'loadEventEnd'),
+
+  'foreground_tab_request_start':
+      (_MAIN_ENTRY_POINT, 'requestStart'),
+}
+
+_TRACKED_EVENT_NAMES = set()
+for i in _METRICS.values():
+  _TRACKED_EVENT_NAMES.add(i[0])
+  if len(i) == 2:
+    _TRACKED_EVENT_NAMES.add(i[1])
+
+
+class StartupTimelineMetric(timeline_based_metric.TimelineBasedMetric):
+  """Reports summary stats from important startup events."""
+
+  def __init__(self):
+    super(StartupTimelineMetric, self).__init__()
+
+  def AddResults(self, model, _renderer_thread, interactions, results):
+    pass
+
+  def AddWholeTraceResults(self, model, results):
+    browser = model.browser_process
+
+    if not browser:
+      return
+
+    # Produce a map of events to track.
+    tracked_events = {}
+    for event in browser.parent.IterAllEvents(
+      event_predicate=lambda event: event.name in _TRACKED_EVENT_NAMES):
+      # In case of a begin/end trace event, only track the begin that contain
+      # the duration.
+      if event.name in tracked_events:
+        continue
+
+      tracked_events[event.name] = event
+
+    # Generate the metric values according to the tracked events.
+    for display_name, event_names in _METRICS.iteritems():
+      if event_names[0] not in tracked_events:
+        continue
+
+      duration = None
+      if len(event_names) == 1:
+        # The single event contains the duration to report.
+        duration = tracked_events[event_names[0]].duration
+
+      elif len(event_names) == 2:
+        # The duration is defined as the difference between two event starts.
+        if event_names[1] not in tracked_events:
+          continue
+
+        duration = (tracked_events[event_names[1]].start -
+            tracked_events[event_names[0]].start)
+
+      results.AddValue(value.scalar.ScalarValue(
+        page=results.current_page,
+        name=display_name,
+        units='ms',
+        value=duration,
+        improvement_direction=value.improvement_direction.DOWN))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/startup_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/startup_unittest.py
new file mode 100644
index 0000000..a2aa113
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/startup_unittest.py
@@ -0,0 +1,100 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+import telemetry.timeline.event as timeline_event
+from telemetry.testing import test_page_test_results
+from telemetry.web_perf.metrics import startup
+
+
+class StartupTimelineMetricTest(unittest.TestCase):
+
+  def setUp(self):
+    self.events = []
+
+  def AddEvent(self, event_name, start, duration=None):
+    event = timeline_event.TimelineEvent('my_category', event_name,
+                                         start, duration)
+    self.events.append(event)
+
+  # Attributes defined outside __init__
+  # pylint: disable=attribute-defined-outside-init
+  def ComputeStartupMetrics(self):
+    results = test_page_test_results.TestPageTestResults(self)
+
+    # Create a mock model usable by
+    # StartupTimelineMetric.AddWholeTraceResults().
+    def IterateEvents(event_predicate):
+      for event in self.events:
+        if event_predicate(event):
+          yield event
+    class MockClass(object):
+      pass
+    model = MockClass()
+    model.browser_process = MockClass()
+    model.browser_process.parent = MockClass()
+    model.browser_process.parent.IterAllEvents = IterateEvents
+
+    startup.StartupTimelineMetric().AddWholeTraceResults(model, results)
+    return results
+
+  def testUntrackedvents(self):
+    # Code coverage for untracked events
+    self.AddEvent('uknown_event_0', 0)
+    self.AddEvent('uknown_event_1', 1)
+    self.ComputeStartupMetrics()
+
+  def testInstantEventsBasedValue(self):
+    # Test case with instant events to measure the duration between the first
+    # occurrences of two distinct events.
+    START0 = 7
+    START1 = 8
+    DURATION0 = 17
+    DURATION1 = 18
+
+    # Generate duplicated events to make sure we consider only the first one.
+    self.AddEvent(startup._MAIN_ENTRY_POINT, START0)
+    self.AddEvent(startup._MAIN_ENTRY_POINT, START1)
+    self.AddEvent('loadEventEnd', START0 + DURATION0)
+    self.AddEvent('loadEventEnd', START1 + DURATION1)
+    self.AddEvent('requestStart', START0 + DURATION0 * 2)
+    self.AddEvent('requestStart', START1 + DURATION1 * 2)
+
+    results = self.ComputeStartupMetrics()
+    results.AssertHasPageSpecificScalarValue('foreground_tab_load_complete',
+        'ms', DURATION0)
+    results.AssertHasPageSpecificScalarValue('foreground_tab_request_start',
+        'ms', DURATION0 * 2)
+
+  def testDurationEventsBasedValues(self):
+    DURATION_EVENTS = set([
+        'messageloop_start_time',
+        'window_display_time',
+        'open_tabs_time',
+        'first_non_empty_paint_time',
+        'first_main_frame_load_time'])
+
+    # Test case to get the duration of the first occurrence of a duration event.
+    i = 1
+    for display_name in DURATION_EVENTS:
+      self.assertTrue(len(startup._METRICS[display_name]) == 1)
+      event_name = startup._METRICS[display_name][0]
+
+      duration = 13 * i
+      i += 1
+
+      # Generate duplicated events to make sure only the first event is
+      # considered.
+      self.AddEvent(event_name, 5, duration)
+      self.AddEvent(event_name, 6, duration + 2)
+
+    results = self.ComputeStartupMetrics()
+
+    i = 1
+    for display_name in DURATION_EVENTS:
+      duration = 13 * i
+      i += 1
+
+      results.AssertHasPageSpecificScalarValue(display_name, 'ms', duration)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/text_selection.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/text_selection.py
new file mode 100644
index 0000000..6918579
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/text_selection.py
@@ -0,0 +1,19 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.web_perf.metrics import single_event
+
+EVENT_NAME = 'WebLocalFrameImpl::moveRangeSelectionExtent'
+METRIC_NAME = 'text-selection'
+
+class TextSelectionMetric(single_event._SingleEventMetric):
+  """Reports directly durations of WebLocalFrameImpl::moveRangeSelectionExtent
+  events associated with moving a selection extent.
+  """
+
+  def __init__(self):
+    super(TextSelectionMetric, self).__init__(EVENT_NAME, METRIC_NAME,
+        metric_description=('List of durations of selection extent movements '
+                            'that were caused by and start during '
+                            'interactions'))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/timeline_based_metric.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/timeline_based_metric.py
new file mode 100644
index 0000000..d1307e9
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/timeline_based_metric.py
@@ -0,0 +1,87 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+class TimelineBasedMetricException(Exception):
+  """Exception that can be thrown from metrics that implements
+     TimelineBasedMetric to indicate a problem arose when computing the metric.
+     """
+
+
+def _TimeRangesHasOverlap(iterable_time_ranges):
+  """ Returns True if there is are overlapped ranges in time ranges.
+  iterable_time_ranges: an iterable of time ranges. Each time range is a
+  tuple (start time, end time).
+  """
+  # Sort the ranges by the start time
+  sorted_time_ranges = sorted(iterable_time_ranges)
+  last_range = sorted_time_ranges[0]
+  for current_range in sorted_time_ranges[1:]:
+    start_current_range = current_range[0]
+    end_last_range = last_range[1]
+    if start_current_range < end_last_range:
+      return True
+    last_range = current_range
+  return False
+
+
+def IsEventInInteractions(event, interaction_records):
+  """ Return True if event is in any of the interaction records' time range.
+
+  Args:
+    event: an instance of telemetry.timeline.event.TimelineEvent.
+    interaction_records: a list of interaction records, whereas each record is
+      an instance of
+      telemetry.web_perf.timeline_interaction_record.TimelineInteractionRecord.
+
+  Returns:
+    True if |event|'s start & end time is in any of the |interaction_records|'s
+    time range.
+  """
+  return any(ir.start <= event.start and ir.end >= event.end for ir
+             in interaction_records)
+
+
+class TimelineBasedMetric(object):
+
+  def __init__(self):
+    """Computes metrics from a telemetry.timeline Model and a range
+
+    """
+    super(TimelineBasedMetric, self).__init__()
+
+  def AddResults(self, model, renderer_thread, interaction_records, results):
+    """Computes and adds metrics for the interaction_records' time ranges.
+
+    The override of this method should compute results on the data **only**
+    within the interaction_records' start and end time ranges.
+
+    Args:
+      model: An instance of telemetry.timeline.model.TimelineModel.
+      interaction_records: A list of instances of TimelineInteractionRecord. If
+        the override of this method doesn't support overlapped ranges, use
+        VerifyNonOverlappedRecords to check that no records are overlapped.
+      results: An instance of page.PageTestResults.
+
+    """
+    raise NotImplementedError()
+
+  def AddWholeTraceResults(self, model, results):
+    """Computes and adds metrics corresponding to the entire trace.
+
+    Override this method to compute results that correspond to the whole trace.
+
+    Args:
+      model: An instance of telemetry.timeline.model.TimelineModel.
+      results: An instance of page.PageTestResults.
+    """
+    pass
+
+  def VerifyNonOverlappedRecords(self, interaction_records):
+    """This raises exceptions if interaction_records contain overlapped ranges.
+    """
+    if _TimeRangesHasOverlap(((r.start, r.end) for r in interaction_records)):
+      raise TimelineBasedMetricException(
+          'This metric does not support interaction records with overlapped '
+          'time range.')
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/timeline_based_metric_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/timeline_based_metric_unittest.py
new file mode 100644
index 0000000..31f0725
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/timeline_based_metric_unittest.py
@@ -0,0 +1,58 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+import telemetry.web_perf.metrics.timeline_based_metric as tbm_module
+
+
+class FakeEvent(object):
+  def __init__(self, start, end):
+    self.start = start
+    self.end = end
+
+
+class FakeRecord(object):
+  def __init__(self, start, end):
+    self.start = start
+    self.end = end
+
+
+class TimelineBasedMetricTest(unittest.TestCase):
+
+  # pylint: disable=protected-access
+  def testTimeRangesHasOverlap(self):
+    # Test cases with overlap on one side
+    self.assertTrue(tbm_module._TimeRangesHasOverlap([(10, 20), (5, 15)]))
+    self.assertTrue(tbm_module._TimeRangesHasOverlap([(5, 15), (10, 20)]))
+    self.assertTrue(tbm_module._TimeRangesHasOverlap(
+        [(5, 15), (25, 30), (10, 20)]))
+
+    # Test cases with one range fall in the middle of other
+    self.assertTrue(tbm_module._TimeRangesHasOverlap([(10, 20), (15, 18)]))
+    self.assertTrue(tbm_module._TimeRangesHasOverlap([(15, 18), (10, 20)]))
+    self.assertTrue(tbm_module._TimeRangesHasOverlap(
+        [(15, 18), (40, 50), (10, 20)]))
+
+    self.assertFalse(tbm_module._TimeRangesHasOverlap([(15, 18), (20, 25)]))
+    self.assertFalse(tbm_module._TimeRangesHasOverlap(
+        [(1, 2), (2, 3), (0, 1)]))
+
+  def testIsEventInInteractions(self):
+    self.assertFalse(
+        tbm_module.IsEventInInteractions(
+        FakeEvent(0, 100),
+        [FakeRecord(5, 105), FakeRecord(50, 200), FakeRecord(300, 400)]))
+    self.assertFalse(
+        tbm_module.IsEventInInteractions(
+        FakeEvent(50, 100),
+        [FakeRecord(105, 205), FakeRecord(0, 45), FakeRecord(0, 90)]))
+    self.assertTrue(
+        tbm_module.IsEventInInteractions(
+        FakeEvent(50, 100),
+        [FakeRecord(5, 105), FakeRecord(0, 45), FakeRecord(0, 90)]))
+    self.assertTrue(
+        tbm_module.IsEventInInteractions(
+        FakeEvent(50, 100),
+        [FakeRecord(5, 45), FakeRecord(0, 45), FakeRecord(0, 100)]))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/trace_event_stats.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/trace_event_stats.py
new file mode 100644
index 0000000..097d866
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/trace_event_stats.py
@@ -0,0 +1,131 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import collections
+
+from telemetry.value import list_of_scalar_values
+from telemetry.value import scalar
+
+
+class TraceEventStatsInput(object):
+  """Input for the TraceEventStats.
+  Using this object with TraceEventStats will include two metrics, one with a
+  list of times of the given event, and one for the count of the events, named
+  `metric_name + '-count'`.
+  Args:
+    event_category: The category of the event to track.
+    event_name: The name of the event to track.
+    metric_name: The name of the metric name, which accumulates all of the
+                 times of the events.
+    metric_description: Description of the metric.
+    units: Units for the metric.
+    process_name: (optional) The name of the process to inspect for the trace
+                  events. Defaults to 'Renderer'.
+  """
+  def __init__(self, event_category, event_name, metric_name,
+               metric_description, units, process_name='Renderer'):
+    self.event_category = event_category
+    self.event_name = event_name
+    self.metric_name = metric_name
+    self.metric_description = metric_description
+    self.units = units
+    self.process_name = process_name
+    self.event_id = TraceEventStatsInput.GetEventId(event_category, event_name)
+    assert process_name is not None
+
+  @staticmethod
+  def GetEventId(event_category, event_name):
+    return event_category + '^SERIALIZE-DELIM^' + event_name
+
+class TraceEventStats(object):
+  """Reports durations and counts of given trace events.
+  """
+
+  def __init__(self, trace_event_aggregator_inputs=None):
+    self._inputs_by_process_name = collections.defaultdict(list)
+    self._metrics = set()
+    self._IndexNewInputs(trace_event_aggregator_inputs)
+
+  def AddInput(self, trace_event_aggregator_input):
+    self._IndexNewInputs([trace_event_aggregator_input])
+
+  def _IndexNewInputs(self, input_list):
+    if not input_list:
+      return
+    for input_obj in input_list:
+      name = input_obj.metric_name
+      # We check here to make sure we don't have a duplicate metric
+      assert name not in self._metrics
+      assert (name + '-count') not in self._metrics
+      self._metrics.add(name)
+      self._metrics.add(name + '-count')
+
+      self._inputs_by_process_name[input_obj.process_name].append(input_obj)
+
+  @staticmethod
+  def ThreadDurationIfPresent(event):
+    if event.thread_duration:
+      return event.thread_duration
+    else:
+      return event.duration
+
+  def AddResults(self, model, renderer_process, interactions, results):
+    del renderer_process  # unused
+    assert interactions
+    for p in model.GetAllProcesses():
+      if p.name not in self._inputs_by_process_name:
+        continue
+
+      inputs = self._inputs_by_process_name[p.name]
+      input_ids = {i.event_id for i in inputs}
+
+      def InputIdPredicate(e, ids):
+        return TraceEventStatsInput.GetEventId(e.category, e.name) in ids
+
+      self._AddResultsInternal(
+          p.IterAllEvents(
+              recursive=True,
+              event_type_predicate=lambda t: True,
+              event_predicate=
+                  lambda e, ids=input_ids: InputIdPredicate(e, ids)),
+          interactions,
+          results,
+          inputs)
+
+  # We assume events have been filtered already. 'events' is an iterator.
+  def _AddResultsInternal(self, events, interactions, results, inputs):
+    times_by_event_id = collections.defaultdict(list)
+
+    for event in events:
+      if not any(interaction.start <= event.start <= interaction.end
+                 for interaction in interactions):
+        continue
+      event_id = TraceEventStatsInput.GetEventId(event.category, event.name)
+      times_by_event_id[event_id].append(self.ThreadDurationIfPresent(event))
+
+    if not times_by_event_id:
+      return
+
+    inputs_by_event_id = dict([[input_obj.event_id, input_obj]
+                                for input_obj in inputs])
+
+    for (event_name, times) in times_by_event_id.iteritems():
+      input_for_event = inputs_by_event_id[event_name]
+      name = input_for_event.metric_name
+      results.AddValue(scalar.ScalarValue(
+        page=results.current_page,
+        tir_label=interactions[0].label,
+        name=name + '-count',
+        units='count',
+        value=len(times),
+        description='The number of times ' + name + ' was recorded.'))
+      if len(times) == 0:
+        continue
+      results.AddValue(list_of_scalar_values.ListOfScalarValues(
+        page=results.current_page,
+        tir_label=interactions[0].label,
+        name=name,
+        units=input_for_event.units,
+        values=times,
+        description=input_for_event.metric_description))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/trace_event_stats_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/trace_event_stats_unittest.py
new file mode 100644
index 0000000..242ae59
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/trace_event_stats_unittest.py
@@ -0,0 +1,146 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from collections import namedtuple
+from telemetry.testing import test_page_test_results
+from telemetry.timeline import model as model_module
+from telemetry.timeline import slice as slice_module
+from telemetry.web_perf import timeline_interaction_record
+from telemetry.web_perf.metrics.trace_event_stats import TraceEventStats
+from telemetry.web_perf.metrics.trace_event_stats import TraceEventStatsInput
+
+
+FakeEvent = namedtuple('Event', 'name, start, end, thread_duration, args')
+Interaction = timeline_interaction_record.TimelineInteractionRecord
+TEST_INTERACTION_LABEL = 'Action_TestInteraction'
+
+RENDERER_PROCESS = 'Renderer'
+OTHER_PROCESS = 'Other'
+
+EVENT_CATEGORY1 = 'Category1'
+EVENT_CATEGORY2 = 'Category2'
+
+EVENT_NAME1 = 'Name1'
+EVENT_NAME2 = 'Name2'
+
+
+def TestInteraction(start, end):
+  return Interaction(TEST_INTERACTION_LABEL, start, end)
+
+class TraceEventStatsUnittest(unittest.TestCase):
+
+  def setUp(self):
+    self.model = model_module.TimelineModel()
+    self.renderer_process = self.model.GetOrCreateProcess(1)
+    self.renderer_process.name = RENDERER_PROCESS
+    self.main_thread = self.renderer_process.GetOrCreateThread(tid=11)
+    self.other_process = self.model.GetOrCreateProcess(2)
+    self.other_process.name = OTHER_PROCESS
+    self.other_thread = self.other_process.GetOrCreateThread(tid=12)
+
+  def GetThreadForProcessName(self, process_name):
+    if process_name is RENDERER_PROCESS:
+      return self.main_thread
+    elif process_name is OTHER_PROCESS:
+      return self.other_thread
+    else:
+      raise
+
+  def AddEvent(self, process_name, event_category, event_name,
+               start, duration, thread_start, thread_duration):
+    thread = self.GetThreadForProcessName(process_name)
+    record = slice_module.Slice(thread,
+                             event_category,
+                             event_name,
+                             start, duration, thread_start, thread_duration)
+    thread.PushSlice(record)
+
+  def RunAggregator(self, aggregator, interactions):
+    results = test_page_test_results.TestPageTestResults(self)
+    aggregator.AddResults(self.model, self.renderer_process,
+                          interactions, results)
+    return results
+
+  def testBasicUsage(self):
+    self.AddEvent(RENDERER_PROCESS, EVENT_CATEGORY1, EVENT_NAME1, 10, 8, 10, 5)
+    self.AddEvent(RENDERER_PROCESS, EVENT_CATEGORY1, EVENT_NAME1, 14, 2, 14, 2)
+    interactions = [TestInteraction(9, 14)]
+
+    aggregator = TraceEventStats()
+    aggregator.AddInput(TraceEventStatsInput(
+      EVENT_CATEGORY1,
+      EVENT_NAME1,
+      'metric-name',
+      'metric-description',
+      'units',
+      'Renderer'))
+
+    results = self.RunAggregator(aggregator, interactions)
+    results.AssertHasPageSpecificScalarValue('metric-name-count', 'count', 2)
+    results.AssertHasPageSpecificListOfScalarValues(
+      'metric-name', 'units', [5, 2])
+
+  def testFiltering(self):
+    # These should be recorded.
+    self.AddEvent(RENDERER_PROCESS, EVENT_CATEGORY1, EVENT_NAME1, 10, 8, 10, 5)
+    self.AddEvent(RENDERER_PROCESS, EVENT_CATEGORY1, EVENT_NAME1, 14, 2, 14, 2)
+    self.AddEvent(RENDERER_PROCESS, EVENT_CATEGORY1, EVENT_NAME1, 20, 6, 20, 1)
+
+    # These should be filtered.
+    self.AddEvent(RENDERER_PROCESS, EVENT_CATEGORY1, EVENT_NAME1, 15, 1, 15, 1)
+    self.AddEvent(RENDERER_PROCESS, EVENT_CATEGORY2, EVENT_NAME1, 11, 4, 11, 4)
+    self.AddEvent(RENDERER_PROCESS, EVENT_CATEGORY1, EVENT_NAME2, 11, 3, 11, 3)
+    self.AddEvent(OTHER_PROCESS, EVENT_CATEGORY1, EVENT_NAME1, 11, 2, 11, 2)
+
+    interactions = [TestInteraction(9, 14), TestInteraction(20, 21)]
+
+    aggregator = TraceEventStats()
+    # Test that we default to 'Renderer'
+    aggregator.AddInput(TraceEventStatsInput(
+      EVENT_CATEGORY1,
+      EVENT_NAME1,
+      'metric-name',
+      'metric-description',
+      'units'))
+
+    results = self.RunAggregator(aggregator, interactions)
+    results.AssertHasPageSpecificScalarValue('metric-name-count', 'count', 3)
+    results.AssertHasPageSpecificListOfScalarValues(
+      'metric-name', 'units', [5, 2, 1])
+
+  def testNoInputs(self):
+    # These should be recorded.
+    self.AddEvent(RENDERER_PROCESS, EVENT_CATEGORY1, EVENT_NAME1, 10, 8, 10, 5)
+    self.AddEvent(RENDERER_PROCESS, EVENT_CATEGORY1, EVENT_NAME1, 14, 2, 14, 2)
+    self.AddEvent(RENDERER_PROCESS, EVENT_CATEGORY1, EVENT_NAME1, 20, 6, 20, 1)
+
+    # These should be filtered.
+    self.AddEvent(RENDERER_PROCESS, EVENT_CATEGORY1, EVENT_NAME1, 15, 1, 15, 1)
+    self.AddEvent(RENDERER_PROCESS, EVENT_CATEGORY2, EVENT_NAME1, 11, 4, 11, 4)
+    self.AddEvent(RENDERER_PROCESS, EVENT_CATEGORY1, EVENT_NAME2, 11, 3, 11, 3)
+    self.AddEvent(OTHER_PROCESS, EVENT_CATEGORY1, EVENT_NAME1, 11, 2, 11, 2)
+
+    interactions = [TestInteraction(9, 14), TestInteraction(20, 21)]
+
+    aggregator = TraceEventStats()
+
+    results = self.RunAggregator(aggregator, interactions)
+    self.assertEquals([], results.all_page_specific_values)
+
+
+  def testNoEvents(self):
+    interactions = [TestInteraction(9, 14)]
+
+    aggregator = TraceEventStats()
+    aggregator.AddInput(TraceEventStatsInput(
+      EVENT_CATEGORY1,
+      EVENT_NAME1,
+      'metric-name',
+      'metric-description',
+      'units'))
+
+    results = self.RunAggregator(aggregator, interactions)
+    self.assertEquals([], results.all_page_specific_values)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/v8_gc_latency.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/v8_gc_latency.py
new file mode 100644
index 0000000..9d385f6
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/v8_gc_latency.py
@@ -0,0 +1,199 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.util import statistics
+from telemetry.value import improvement_direction
+from telemetry.value import scalar
+from telemetry.web_perf.metrics import timeline_based_metric
+
+import logging
+
+class V8EventStat(object):
+
+  def __init__(self, src_event_name, result_name, result_description):
+    self.src_event_name = src_event_name
+    self.result_name = result_name
+    self.result_description = result_description
+    self.thread_duration = 0.0
+    self.thread_duration_inside_idle = 0.0
+    self.idle_task_overrun_duration = 0.0
+    self.max_thread_duration = 0.0
+    self.count = 0
+
+  @property
+  def thread_duration_outside_idle(self):
+    return self.thread_duration - self.thread_duration_inside_idle
+
+  @property
+  def percentage_thread_duration_during_idle(self):
+    return statistics.DivideIfPossibleOrZero(
+        100 * self.thread_duration_inside_idle, self.thread_duration)
+
+class V8GCLatency(timeline_based_metric.TimelineBasedMetric):
+  _RENDERER_MAIN_THREAD = 'CrRendererMain'
+  _IDLE_TASK_PARENT = 'SingleThreadIdleTaskRunner::RunTask'
+
+  def __init__(self):
+    super(V8GCLatency, self).__init__()
+
+  def AddResults(self, model, renderer_thread, interaction_records, results):
+    self.VerifyNonOverlappedRecords(interaction_records)
+    self._AddV8MetricsToResults(model, interaction_records, results)
+
+  def _AddV8MetricsToResults(self, model,
+                             interaction_records, results):
+    self._AddV8EventStatsToResults(model, interaction_records, results)
+
+  def _AddV8EventStatsToResults(self, model, interactions, results):
+    v8_event_stats = [
+        V8EventStat('V8.GCIncrementalMarking',
+                    'v8_gc_incremental_marking',
+                    'incremental marking steps'),
+        V8EventStat('V8.GCScavenger',
+                    'v8_gc_scavenger',
+                    'scavenges'),
+        V8EventStat('V8.GCCompactor',
+                    'v8_gc_mark_compactor',
+                    'mark-sweep-compactor'),
+        V8EventStat('V8.GCFinalizeMC',
+                    'v8_gc_finalize_incremental',
+                    'finalization of incremental marking'),
+        V8EventStat('V8.GCFinalizeMCReduceMemory',
+                    'v8_gc_finalize_incremental_reduce_memory',
+                    'finalization of incremental marking with memory reducer')]
+    label = interactions[0].label
+    name_to_v8_stat = {x.src_event_name : x for x in v8_event_stats}
+    thread_time_not_available = False
+    for event in model.IterAllSlices():
+      if (not timeline_based_metric.IsEventInInteractions(event, interactions)
+          or not event.name in name_to_v8_stat):
+        continue
+      event_stat = name_to_v8_stat[event.name]
+      if event.thread_duration is None:
+        thread_time_not_available = True
+        event_duration = event.duration
+      else:
+        event_duration = event.thread_duration
+      event_stat.thread_duration += event_duration
+      event_stat.max_thread_duration = max(event_stat.max_thread_duration,
+                                           event_duration)
+      event_stat.count += 1
+
+      parent_idle_task = self._ParentIdleTask(event)
+      if parent_idle_task:
+        allotted_idle_time = parent_idle_task.args['allotted_time_ms']
+        idle_task_wall_overrun = 0
+        if event.duration > allotted_idle_time:
+          idle_task_wall_overrun = event.duration - allotted_idle_time
+        # Don't count time over the deadline as being inside idle time.
+        # Since the deadline should be relative to wall clock we compare
+        # allotted_time_ms with wall duration instead of thread duration, and
+        # then assume the thread duration was inside idle for the same
+        # percentage of time.
+        inside_idle = event_duration * statistics.DivideIfPossibleOrZero(
+            event.duration - idle_task_wall_overrun, event.duration)
+        event_stat.thread_duration_inside_idle += inside_idle
+        event_stat.idle_task_overrun_duration += idle_task_wall_overrun
+
+    if thread_time_not_available:
+      logging.warning(
+          'thread time is not available in trace data, switch to walltime')
+
+    for v8_event_stat in v8_event_stats:
+      results.AddValue(scalar.ScalarValue(
+          results.current_page, v8_event_stat.result_name, 'ms',
+          v8_event_stat.thread_duration,
+          description=('Total thread duration spent in %s' %
+                       v8_event_stat.result_description),
+          tir_label=label,
+          improvement_direction=improvement_direction.DOWN))
+      results.AddValue(scalar.ScalarValue(
+          results.current_page, '%s_max' % v8_event_stat.result_name, 'ms',
+          v8_event_stat.max_thread_duration,
+          description=('Max thread duration spent in %s' %
+                       v8_event_stat.result_description),
+          tir_label=label))
+      results.AddValue(scalar.ScalarValue(
+          results.current_page, '%s_count' % v8_event_stat.result_name, 'count',
+          v8_event_stat.count,
+          description=('Number of %s' %
+                       v8_event_stat.result_description),
+          tir_label=label,
+          improvement_direction=improvement_direction.DOWN))
+      average_thread_duration = statistics.DivideIfPossibleOrZero(
+          v8_event_stat.thread_duration, v8_event_stat.count)
+      results.AddValue(scalar.ScalarValue(
+          results.current_page, '%s_average' % v8_event_stat.result_name, 'ms',
+          average_thread_duration,
+          description=('Average thread duration spent in %s' %
+                       v8_event_stat.result_description),
+          tir_label=label,
+          improvement_direction=improvement_direction.DOWN))
+      results.AddValue(scalar.ScalarValue(results.current_page,
+          '%s_outside_idle' % v8_event_stat.result_name, 'ms',
+          v8_event_stat.thread_duration_outside_idle,
+          description=(
+              'Total thread duration spent in %s outside of idle tasks' %
+              v8_event_stat.result_description),
+          tir_label=label))
+      results.AddValue(scalar.ScalarValue(results.current_page,
+          '%s_idle_deadline_overrun' % v8_event_stat.result_name, 'ms',
+          v8_event_stat.idle_task_overrun_duration,
+          description=('Total idle task deadline overrun for %s idle tasks'
+                       % v8_event_stat.result_description),
+          tir_label=label,
+          improvement_direction=improvement_direction.DOWN))
+      results.AddValue(scalar.ScalarValue(results.current_page,
+          '%s_percentage_idle' % v8_event_stat.result_name, 'idle%',
+          v8_event_stat.percentage_thread_duration_during_idle,
+          description=('Percentage of %s spent in idle time' %
+                       v8_event_stat.result_description),
+          tir_label=label,
+          improvement_direction=improvement_direction.UP))
+
+    # Add total metrics.
+    gc_total = sum(x.thread_duration for x in v8_event_stats)
+    gc_total_outside_idle = sum(
+        x.thread_duration_outside_idle for x in v8_event_stats)
+    gc_total_idle_deadline_overrun = sum(
+        x.idle_task_overrun_duration for x in v8_event_stats)
+    gc_total_percentage_idle = statistics.DivideIfPossibleOrZero(
+        100 * (gc_total - gc_total_outside_idle), gc_total)
+
+    results.AddValue(scalar.ScalarValue(results.current_page,
+        'v8_gc_total', 'ms', gc_total,
+        description='Total thread duration of all garbage collection events',
+          tir_label=label,
+          improvement_direction=improvement_direction.DOWN))
+    results.AddValue(scalar.ScalarValue(results.current_page,
+        'v8_gc_total_outside_idle', 'ms', gc_total_outside_idle,
+        description=(
+            'Total thread duration of all garbage collection events outside of '
+            'idle tasks'),
+          tir_label=label,
+          improvement_direction=improvement_direction.DOWN))
+    results.AddValue(scalar.ScalarValue(results.current_page,
+        'v8_gc_total_idle_deadline_overrun', 'ms',
+        gc_total_idle_deadline_overrun,
+        description=(
+            'Total idle task deadline overrun for all idle tasks garbage '
+            'collection events'),
+          tir_label=label,
+          improvement_direction=improvement_direction.DOWN))
+    results.AddValue(scalar.ScalarValue(results.current_page,
+        'v8_gc_total_percentage_idle', 'idle%', gc_total_percentage_idle,
+        description=(
+            'Percentage of the thread duration of all garbage collection '
+            'events spent inside of idle tasks'),
+          tir_label=label,
+          improvement_direction=improvement_direction.UP))
+
+  def _ParentIdleTask(self, event):
+    parent = event.parent_slice
+    while parent:
+      if parent.name == self._IDLE_TASK_PARENT:
+        return parent
+      parent = parent.parent_slice
+    return None
+
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/v8_gc_latency_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/v8_gc_latency_unittest.py
new file mode 100644
index 0000000..03c850a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/v8_gc_latency_unittest.py
@@ -0,0 +1,445 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.internal.results import page_test_results
+from telemetry.page import page as page_module
+from telemetry.testing import options_for_unittests
+from telemetry.testing import page_test_test_case
+from telemetry.timeline import model as model_module
+from telemetry.util import wpr_modes
+
+from telemetry.web_perf.metrics import v8_gc_latency
+from telemetry.web_perf import timeline_interaction_record
+
+class V8EventStat(object):
+
+  def __init__(self, src_event_name, result_name, result_description):
+    self.src_event_name = src_event_name
+    self.result_name = result_name
+    self.result_description = result_description
+    self.thread_duration = 0.0
+    self.thread_duration_inside_idle = 0.0
+    self.idle_task_overrun_duration = 0.0
+    self.max_thread_duration = 0.0
+    self.count = 0
+
+class V8GCLatencyTestPageHelper(object):
+
+  def __init__(self, page_set):
+    self._page_set = page_set
+    self._model = model_module.TimelineModel()
+    self._renderer_process = self._model.GetOrCreateProcess(1)
+    self._renderer_thread = self._renderer_process.GetOrCreateThread(2)
+    self._renderer_thread.name = 'CrRendererMain'
+    self._interaction_records = []
+
+  def AddEvent(self, category, name, thread_start, thread_duration,
+               args=None, wall_start=None, wall_duration=None):
+    wall_start = wall_start or thread_start
+    wall_duration = wall_duration or thread_duration
+    self._renderer_thread.BeginSlice(category, name, wall_start, thread_start,
+                                     args=args)
+    self._renderer_thread.EndSlice(wall_start + wall_duration,
+                                   thread_start + thread_duration)
+
+  def AddEventWithoutThreadDuration(self, category, name,
+                                    wall_start, wall_duration):
+    self._renderer_thread.BeginSlice(category, name, wall_start)
+    self._renderer_thread.EndSlice(wall_start + wall_duration)
+
+  def AddInteractionRecord(self, label, start, end):
+    self._interaction_records.append(
+      timeline_interaction_record.TimelineInteractionRecord(label, start, end))
+
+  class MockV8GCLatencyPage(page_module.Page):
+
+    def __init__(self, page_set):
+      super(V8GCLatencyTestPageHelper.MockV8GCLatencyPage, self).__init__(
+          'file://blank.html', page_set, page_set.base_dir)
+
+  def MeasureFakePage(self):
+    # Create a fake page and add it to the page set.
+    results = page_test_results.PageTestResults()
+    page = V8GCLatencyTestPageHelper.MockV8GCLatencyPage(self._page_set)
+    self._page_set.AddStory(page)
+
+    # Pretend we're about to run the tests to silence lower level asserts.
+    results.WillRunPage(page)
+
+    metric = v8_gc_latency.V8GCLatency()
+
+    # Finalize the timeline import.
+    self._model.FinalizeImport()
+
+    for interaction in self._interaction_records:
+      # Measure the V8GCLatency metric and return the results
+      # pylint: disable=protected-access
+      metric._AddV8MetricsToResults(self._model, [interaction], results)
+    results.DidRunPage(page)
+    return results
+
+
+class V8GCLatencyTests(page_test_test_case.PageTestTestCase):
+
+  def setUp(self):
+    self._options = options_for_unittests.GetCopy()
+    self._options.browser_options.wpr_mode = wpr_modes.WPR_OFF
+
+  def testWithNoTraceEvents(self):
+    test_page_helper = V8GCLatencyTestPageHelper(
+        self.CreateEmptyPageSet())
+    test_page_helper.AddInteractionRecord('Action', 0, 32)
+
+    results = test_page_helper.MeasureFakePage()
+    self._AssertResultsEqual(_GetEmptyResults(), _ActualValues(results))
+
+  def testWithNoGarbageCollectionEvents(self):
+    test_page_helper = V8GCLatencyTestPageHelper(
+        self.CreateEmptyPageSet())
+
+    test_page_helper.AddInteractionRecord('Action', 0, 32)
+    test_page_helper.AddEvent('toplevel', 'PostMessage',
+        thread_start=0, thread_duration=14, wall_start=5, wall_duration=35)
+
+    results = test_page_helper.MeasureFakePage()
+    expected = _GetEmptyResults()
+
+    self._AssertResultsEqual(expected, _ActualValues(results))
+
+  def testWithGarbageCollectionEvents(self):
+    test_page_helper = V8GCLatencyTestPageHelper(
+        self.CreateEmptyPageSet())
+
+    test_page_helper.AddInteractionRecord('Action', 0, 88)
+    test_page_helper.AddEvent('toplevel', 'PostMessage',
+        thread_start=0, thread_duration=77, wall_start=5, wall_duration=88)
+    test_page_helper.AddEvent('v8', 'V8.GCScavenger', 5, 4)
+    test_page_helper.AddEvent('v8', 'V8.GCScavenger', 15, 3)
+    test_page_helper.AddEvent('v8', 'V8.GCIncrementalMarking', 23, 4)
+    test_page_helper.AddEvent('v8', 'V8.GCIncrementalMarking', 34, 2)
+    test_page_helper.AddEvent('v8', 'V8.GCFinalizeMC', 38, 2)
+    test_page_helper.AddEvent('v8', 'V8.GCFinalizeMC', 42, 3)
+    test_page_helper.AddEvent('v8', 'V8.GCFinalizeMCReduceMemory', 46, 4)
+    test_page_helper.AddEvent('v8', 'V8.GCFinalizeMCReduceMemory', 51, 5)
+    test_page_helper.AddEvent('v8', 'V8.GCCompactor', 62, 4)
+    test_page_helper.AddEvent('v8', 'V8.GCCompactor', 72, 5)
+
+    results = test_page_helper.MeasureFakePage()
+    expected = _GetEmptyResults()
+    expected['v8_gc_incremental_marking'] = ('ms', 6.0)
+    expected['v8_gc_incremental_marking_average'] = ('ms', 3.0)
+    expected['v8_gc_incremental_marking_count'] = ('count', 2)
+    expected['v8_gc_incremental_marking_max'] = ('ms', 4.0)
+    expected['v8_gc_incremental_marking_outside_idle'] = ('ms', 6.0)
+    expected['v8_gc_finalize_incremental'] = ('ms', 5.0)
+    expected['v8_gc_finalize_incremental_average'] = ('ms', 2.5)
+    expected['v8_gc_finalize_incremental_count'] = ('count', 2)
+    expected['v8_gc_finalize_incremental_max'] = ('ms', 3.0)
+    expected['v8_gc_finalize_incremental_outside_idle'] = ('ms', 5.0)
+    expected['v8_gc_finalize_incremental_reduce_memory'] = ('ms', 9.0)
+    expected['v8_gc_finalize_incremental_reduce_memory_average'] = ('ms', 4.5)
+    expected['v8_gc_finalize_incremental_reduce_memory_count'] = ('count', 2)
+    expected['v8_gc_finalize_incremental_reduce_memory_max'] = ('ms', 5.0)
+    expected['v8_gc_finalize_incremental_reduce_memory_outside_idle'] = (
+        'ms', 9.0)
+    expected['v8_gc_scavenger'] = ('ms', 7.0)
+    expected['v8_gc_scavenger_average'] = ('ms', 3.5)
+    expected['v8_gc_scavenger_count'] = ('count', 2)
+    expected['v8_gc_scavenger_max'] = ('ms', 4.0)
+    expected['v8_gc_scavenger_outside_idle'] = ('ms', 7.0)
+    expected['v8_gc_mark_compactor'] = ('ms', 9.0)
+    expected['v8_gc_mark_compactor_average'] = ('ms', 4.5)
+    expected['v8_gc_mark_compactor_count'] = ('count', 2)
+    expected['v8_gc_mark_compactor_max'] = ('ms', 5.0)
+    expected['v8_gc_mark_compactor_outside_idle'] = ('ms', 9.0)
+    expected['v8_gc_total'] = ('ms', 36.0)
+    expected['v8_gc_total_outside_idle'] = ('ms', 36.0)
+
+    self._AssertResultsEqual(expected, _ActualValues(results))
+
+  def testWithIdleTaskGarbageCollectionEvents(self):
+    test_page_helper = V8GCLatencyTestPageHelper(
+        self.CreateEmptyPageSet())
+
+    test_page_helper.AddInteractionRecord('Action', 0, 68)
+    test_page_helper.AddEvent('toplevel', 'PostMessage',
+        thread_start=0, thread_duration=57, wall_start=5, wall_duration=68)
+
+    test_page_helper.AddEvent('v8', 'V8.GCScavenger', 5, 4)
+    test_page_helper.AddEvent('renderer.scheduler',
+        'SingleThreadIdleTaskRunner::RunTask', 15, 4, {'allotted_time_ms': 12})
+    test_page_helper.AddEvent('v8', 'V8.GCScavenger', 15, 3)
+
+    test_page_helper.AddEvent('v8', 'V8.GCIncrementalMarking', 23, 4)
+    test_page_helper.AddEvent('renderer.scheduler',
+        'SingleThreadIdleTaskRunner::RunTask', 34, 3, {'allotted_time_ms': 12})
+    test_page_helper.AddEvent('v8', 'V8.GCIncrementalMarking', 34, 2)
+
+    test_page_helper.AddEvent('v8', 'V8.GCCompactor', 42, 4)
+    test_page_helper.AddEvent('renderer.scheduler',
+        'SingleThreadIdleTaskRunner::RunTask', 52, 6, {'allotted_time_ms': 12})
+    test_page_helper.AddEvent('v8', 'V8.GCCompactor', 52, 5)
+
+    results = test_page_helper.MeasureFakePage()
+    expected = _GetEmptyResults()
+    expected['v8_gc_incremental_marking'] = ('ms', 6.0)
+    expected['v8_gc_incremental_marking_average'] = ('ms', 3.0)
+    expected['v8_gc_incremental_marking_count'] = ('count', 2)
+    expected['v8_gc_incremental_marking_max'] = ('ms', 4.0)
+    expected['v8_gc_incremental_marking_outside_idle'] = ('ms', 4.0)
+    expected['v8_gc_incremental_marking_percentage_idle'] = \
+        ('idle%', 100 * 2 / 6.0)
+    expected['v8_gc_scavenger'] = ('ms', 7.0)
+    expected['v8_gc_scavenger_average'] = ('ms', 3.5)
+    expected['v8_gc_scavenger_count'] = ('count', 2)
+    expected['v8_gc_scavenger_max'] = ('ms', 4.0)
+    expected['v8_gc_scavenger_outside_idle'] = ('ms', 4.0)
+    expected['v8_gc_scavenger_percentage_idle'] = ('idle%', 100 * 3 / 7.0)
+    expected['v8_gc_mark_compactor'] = ('ms', 9.0)
+    expected['v8_gc_mark_compactor_average'] = ('ms', 4.5)
+    expected['v8_gc_mark_compactor_count'] = ('count', 2)
+    expected['v8_gc_mark_compactor_max'] = ('ms', 5.0)
+    expected['v8_gc_mark_compactor_outside_idle'] = ('ms', 4.0)
+    expected['v8_gc_mark_compactor_percentage_idle'] = ('idle%', 100 * 5 / 9.0)
+    expected['v8_gc_total'] = ('ms', 22.0)
+    expected['v8_gc_total_outside_idle'] = ('ms', 12.0)
+    expected['v8_gc_total_percentage_idle'] = ('idle%', 100 * 10 / 22.0)
+
+    self._AssertResultsEqual(expected, _ActualValues(results))
+
+  def testWithIdleTaskOverruns(self):
+    test_page_helper = V8GCLatencyTestPageHelper(
+        self.CreateEmptyPageSet())
+
+    test_page_helper.AddInteractionRecord('Action', 0, 92)
+    test_page_helper.AddEvent('toplevel', 'PostMessage',
+        thread_start=0, thread_duration=80, wall_start=5, wall_duration=92)
+
+    test_page_helper.AddEvent('renderer.scheduler',
+        'SingleThreadIdleTaskRunner::RunTask', 15, 15, {'allotted_time_ms': 8})
+    test_page_helper.AddEvent('v8', 'V8.GCScavenger', 15, 14)
+
+    test_page_helper.AddEvent('renderer.scheduler',
+        'SingleThreadIdleTaskRunner::RunTask', 34, 15, {'allotted_time_ms': 6})
+    test_page_helper.AddEvent('v8', 'V8.GCIncrementalMarking', 34, 14)
+
+    test_page_helper.AddEvent('renderer.scheduler',
+        'SingleThreadIdleTaskRunner::RunTask', 52, 23, {'allotted_time_ms': 9})
+    test_page_helper.AddEvent('v8', 'V8.GCCompactor', 52, 22)
+
+    results = test_page_helper.MeasureFakePage()
+    expected = _GetEmptyResults()
+    expected['v8_gc_incremental_marking'] = ('ms', 14.0)
+    expected['v8_gc_incremental_marking_average'] = ('ms', 14.0)
+    expected['v8_gc_incremental_marking_count'] = ('count', 1)
+    expected['v8_gc_incremental_marking_max'] = ('ms', 14.0)
+    expected['v8_gc_incremental_marking_outside_idle'] = ('ms', 8.0)
+    expected['v8_gc_incremental_marking_idle_deadline_overrun'] = ('ms', 8.0)
+    expected['v8_gc_incremental_marking_percentage_idle'] = \
+        ('idle%', 100 * 6 / 14.0)
+    expected['v8_gc_scavenger'] = ('ms', 14.0)
+    expected['v8_gc_scavenger_average'] = ('ms', 14.0)
+    expected['v8_gc_scavenger_count'] = ('count', 1)
+    expected['v8_gc_scavenger_max'] = ('ms', 14.0)
+    expected['v8_gc_scavenger_outside_idle'] = ('ms', 6.0)
+    expected['v8_gc_scavenger_idle_deadline_overrun'] = ('ms', 6.0)
+    expected['v8_gc_scavenger_percentage_idle'] = ('idle%', 100 * 8 / 14.0)
+    expected['v8_gc_mark_compactor'] = ('ms', 22.0)
+    expected['v8_gc_mark_compactor_average'] = ('ms', 22.0)
+    expected['v8_gc_mark_compactor_count'] = ('count', 1)
+    expected['v8_gc_mark_compactor_max'] = ('ms', 22.0)
+    expected['v8_gc_mark_compactor_outside_idle'] = ('ms', 13.0)
+    expected['v8_gc_mark_compactor_idle_deadline_overrun'] = ('ms', 13.0)
+    expected['v8_gc_mark_compactor_percentage_idle'] = ('idle%', 100 * 9 / 22.0)
+    expected['v8_gc_total'] = ('ms', 50.0)
+    expected['v8_gc_total_outside_idle'] = ('ms', 27.0)
+    expected['v8_gc_total_idle_deadline_overrun'] = ('ms', 27.0)
+    expected['v8_gc_total_percentage_idle'] = ('idle%', 100 * 23 / 50.0)
+
+    self._AssertResultsEqual(expected, _ActualValues(results))
+
+  def testWithIdleTaskWallDurationOverruns(self):
+    test_page_helper = V8GCLatencyTestPageHelper(
+        self.CreateEmptyPageSet())
+
+    test_page_helper.AddInteractionRecord('Action', 0, 92)
+    test_page_helper.AddEvent('toplevel', 'PostMessage',
+        thread_start=0, thread_duration=80, wall_start=5, wall_duration=92)
+
+    test_page_helper.AddEvent('renderer.scheduler',
+        'SingleThreadIdleTaskRunner::RunTask', 15, 15, {'allotted_time_ms': 8})
+    test_page_helper.AddEvent('v8', 'V8.GCScavenger',
+        thread_start=15, thread_duration=4, wall_start=15, wall_duration=14)
+
+    results = test_page_helper.MeasureFakePage()
+    expected = _GetEmptyResults()
+    expected['v8_gc_scavenger'] = ('ms', 4.0)
+    expected['v8_gc_scavenger_average'] = ('ms', 4.0)
+    expected['v8_gc_scavenger_count'] = ('count', 1)
+    expected['v8_gc_scavenger_max'] = ('ms', 4.0)
+    expected_outside_idle = 4.0 - (4.0 * 8 / 14)
+    expected['v8_gc_scavenger_outside_idle'] = ('ms', expected_outside_idle)
+    expected['v8_gc_scavenger_idle_deadline_overrun'] = ('ms', 6.0)
+    expected['v8_gc_scavenger_percentage_idle'] = \
+        ('idle%', 100 * (4.0 - expected_outside_idle) / 4.0)
+    expected['v8_gc_total'] = expected['v8_gc_scavenger']
+    expected['v8_gc_total_outside_idle'] = \
+        expected['v8_gc_scavenger_outside_idle']
+    expected['v8_gc_total_idle_deadline_overrun'] = \
+        expected['v8_gc_scavenger_idle_deadline_overrun']
+    expected['v8_gc_total_percentage_idle'] = \
+        expected['v8_gc_scavenger_percentage_idle']
+
+    self._AssertResultsEqual(expected, _ActualValues(results))
+
+  def testWithMultipleInteractionRecords(self):
+    test_page_helper = V8GCLatencyTestPageHelper(
+        self.CreateEmptyPageSet())
+
+    test_page_helper.AddInteractionRecord('Action1', 5, 18)
+    test_page_helper.AddInteractionRecord('Action2', 19, 57)
+    test_page_helper.AddInteractionRecord('Action3', 60, 68)
+    test_page_helper.AddEvent('toplevel', 'PostMessage',
+        thread_start=0, thread_duration=57, wall_start=5, wall_duration=68)
+
+    # This event is not in any interaction record.
+    test_page_helper.AddEvent('v8', 'V8.GCScavenger', 0, 1)
+
+    # These events are in Action1.
+    test_page_helper.AddEvent('v8', 'V8.GCScavenger', 5, 4)
+    test_page_helper.AddEvent('renderer.scheduler',
+        'SingleThreadIdleTaskRunner::RunTask', 15, 4, {'allotted_time_ms': 12})
+    test_page_helper.AddEvent('v8', 'V8.GCScavenger', 15, 3)
+
+    # These events are in Action2.
+    test_page_helper.AddEvent('v8', 'V8.GCIncrementalMarking', 23, 4)
+    test_page_helper.AddEvent('renderer.scheduler',
+        'SingleThreadIdleTaskRunner::RunTask', 34, 3, {'allotted_time_ms': 12})
+    test_page_helper.AddEvent('v8', 'V8.GCIncrementalMarking', 34, 2)
+    test_page_helper.AddEvent('v8', 'V8.GCCompactor', 42, 4)
+    test_page_helper.AddEvent('renderer.scheduler',
+        'SingleThreadIdleTaskRunner::RunTask', 52, 6, {'allotted_time_ms': 12})
+    test_page_helper.AddEvent('v8', 'V8.GCCompactor', 52, 5)
+
+    # This event is not in any interaction record.
+    test_page_helper.AddEvent('v8', 'V8.GCScavenger', 58, 1)
+
+    results = test_page_helper.MeasureFakePage()
+    expected = _GetEmptyResults()
+    expected['v8_gc_scavenger'] = ('ms', 7.0)
+    expected['v8_gc_scavenger_average'] = ('ms', 3.5)
+    expected['v8_gc_scavenger_count'] = ('count', 2)
+    expected['v8_gc_scavenger_max'] = ('ms', 4.0)
+    expected['v8_gc_scavenger_outside_idle'] = ('ms', 4.0)
+    expected['v8_gc_scavenger_percentage_idle'] = ('idle%', 100 * 3 / 7.0)
+    expected['v8_gc_total'] = ('ms', 7.0)
+    expected['v8_gc_total_outside_idle'] = ('ms', 4.0)
+    expected['v8_gc_total_percentage_idle'] = ('idle%', 100 * 3.0 / 7.0)
+
+    self._AssertResultsEqual(expected, _ActualValues(results, 'Action1'))
+
+    expected = _GetEmptyResults()
+    expected['v8_gc_incremental_marking'] = ('ms', 6.0)
+    expected['v8_gc_incremental_marking_average'] = ('ms', 3.0)
+    expected['v8_gc_incremental_marking_count'] = ('count', 2)
+    expected['v8_gc_incremental_marking_max'] = ('ms', 4.0)
+    expected['v8_gc_incremental_marking_outside_idle'] = ('ms', 4.0)
+    expected['v8_gc_incremental_marking_percentage_idle'] = \
+        ('idle%', 100 * 2 / 6.0)
+    expected['v8_gc_mark_compactor'] = ('ms', 9.0)
+    expected['v8_gc_mark_compactor_average'] = ('ms', 4.5)
+    expected['v8_gc_mark_compactor_count'] = ('count', 2)
+    expected['v8_gc_mark_compactor_max'] = ('ms', 5.0)
+    expected['v8_gc_mark_compactor_outside_idle'] = ('ms', 4.0)
+    expected['v8_gc_mark_compactor_percentage_idle'] = ('idle%', 100 * 5 / 9.0)
+    expected['v8_gc_total'] = ('ms', 15.0)
+    expected['v8_gc_total_outside_idle'] = ('ms', 8.0)
+    expected['v8_gc_total_percentage_idle'] = ('idle%', 100 * 7.0 / 15.0)
+
+    self._AssertResultsEqual(expected, _ActualValues(results, 'Action2'))
+
+    expected = _GetEmptyResults()
+    self._AssertResultsEqual(expected, _ActualValues(results, 'Action3'))
+
+
+  def testRegress549150(self):
+    test_page_helper = V8GCLatencyTestPageHelper(
+        self.CreateEmptyPageSet())
+    test_page_helper.AddInteractionRecord('Action', 0, 10)
+    test_page_helper.AddEvent('toplevel', 'PostMessage',
+        thread_start=0, thread_duration=10, wall_start=0, wall_duration=10)
+    test_page_helper.AddEventWithoutThreadDuration(
+        'v8', 'V8.GCScavenger', 0, 4)
+    results = test_page_helper.MeasureFakePage()
+    expected = _GetEmptyResults()
+    expected['v8_gc_scavenger'] = ('ms', 4.0)
+    expected['v8_gc_scavenger_average'] = ('ms', 4.0)
+    expected['v8_gc_scavenger_count'] = ('count', 1)
+    expected['v8_gc_scavenger_max'] = ('ms', 4.0)
+    expected['v8_gc_scavenger_outside_idle'] = ('ms', 4.0)
+    expected['v8_gc_scavenger_percentage_idle'] = ('idle%', 0.0)
+    expected['v8_gc_total'] = ('ms', 4.0)
+    expected['v8_gc_total_outside_idle'] = ('ms', 4.0)
+    expected['v8_gc_total_percentage_idle'] = ('idle%', 0.0)
+
+    self._AssertResultsEqual(expected, _ActualValues(results, 'Action'))
+
+
+  def _AssertResultsEqual(self, expected, actual):
+    for key in expected.iterkeys():
+      self.assertIn(key, actual.keys())
+      self.assertEqual(expected[key], actual[key],
+          'Result for [' + key + '] - expected ' + str(expected[key]) +
+          ' but got ' + str(actual[key]))
+
+
+def _ActualValues(results, interaction_record=''):
+  return dict(list(
+      (v.name, (v.units, v.value))
+      for v in results.all_page_specific_values
+      if (interaction_record == '' or v.tir_label == interaction_record)
+      ))
+
+
+def _GetEmptyResults():
+  return {'v8_gc_incremental_marking': ('ms', 0.0),
+          'v8_gc_incremental_marking_average': ('ms', 0.0),
+          'v8_gc_incremental_marking_count': ('count', 0),
+          'v8_gc_incremental_marking_max': ('ms', 0.0),
+          'v8_gc_incremental_marking_idle_deadline_overrun': ('ms', 0.0),
+          'v8_gc_incremental_marking_outside_idle': ('ms', 0.0),
+          'v8_gc_incremental_marking_percentage_idle': ('idle%', 0.0),
+          'v8_gc_finalize_incremental': ('ms', 0.0),
+          'v8_gc_finalize_incremental_average': ('ms', 0.0),
+          'v8_gc_finalize_incremental_count': ('count', 0),
+          'v8_gc_finalize_incremental_max': ('ms', 0.0),
+          'v8_gc_finalize_incremental_idle_deadline_overrun': ('ms', 0.0),
+          'v8_gc_finalize_incremental_outside_idle': ('ms', 0.0),
+          'v8_gc_finalize_incremental_percentage_idle': ('idle%', 0.0),
+          'v8_gc_finalize_incremental_reduce_memory': ('ms', 0.0),
+          'v8_gc_finalize_incremental_reduce_memory_average': ('ms', 0.0),
+          'v8_gc_finalize_incremental_reduce_memory_count': ('count', 0),
+          'v8_gc_finalize_incremental_reduce_memory_max': ('ms', 0.0),
+          'v8_gc_finalize_incremental_reduce_memory_idle_deadline_overrun':
+              ('ms', 0.0),
+          'v8_gc_finalize_incremental_reduce_memory_outside_idle': ('ms', 0.0),
+          'v8_gc_finalize_incremental_reduce_memory_percentage_idle':
+              ('idle%', 0.0),
+          'v8_gc_mark_compactor': ('ms', 0.0),
+          'v8_gc_mark_compactor_average': ('ms', 0.0),
+          'v8_gc_mark_compactor_count': ('count', 0),
+          'v8_gc_mark_compactor_max': ('ms', 0.0),
+          'v8_gc_mark_compactor_idle_deadline_overrun': ('ms', 0.0),
+          'v8_gc_mark_compactor_outside_idle': ('ms', 0.0),
+          'v8_gc_mark_compactor_percentage_idle': ('idle%', 0.0),
+          'v8_gc_scavenger': ('ms', 0.0),
+          'v8_gc_scavenger_average': ('ms', 0.0),
+          'v8_gc_scavenger_count': ('count', 0),
+          'v8_gc_scavenger_max': ('ms', 0.0),
+          'v8_gc_scavenger_idle_deadline_overrun': ('ms', 0.0),
+          'v8_gc_scavenger_outside_idle': ('ms', 0.0),
+          'v8_gc_scavenger_percentage_idle': ('idle%', 0.0),
+          'v8_gc_total': ('ms', 0.0),
+          'v8_gc_total_idle_deadline_overrun': ('ms', 0.0),
+          'v8_gc_total_outside_idle': ('ms', 0.0)}
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/webrtc_rendering_stats.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/webrtc_rendering_stats.py
new file mode 100644
index 0000000..43276b6
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/webrtc_rendering_stats.py
@@ -0,0 +1,366 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+
+from telemetry.util import statistics
+
+DISPLAY_HERTZ = 60.0
+VSYNC_DURATION = 1e6 / DISPLAY_HERTZ
+# When to consider a frame frozen (in VSYNC units): meaning 1 initial
+# frame + 5 repeats of that frame.
+FROZEN_THRESHOLD = 6
+# Severity factor.
+SEVERITY = 3
+
+IDEAL_RENDER_INSTANT = 'Ideal Render Instant'
+ACTUAL_RENDER_BEGIN = 'Actual Render Begin'
+ACTUAL_RENDER_END = 'Actual Render End'
+SERIAL = 'Serial'
+
+
+class TimeStats(object):
+  """Stats container for webrtc rendering metrics."""
+
+  def __init__(self, drift_time=None, mean_drift_time=None,
+    std_dev_drift_time=None, percent_badly_out_of_sync=None,
+    percent_out_of_sync=None, smoothness_score=None, freezing_score=None,
+    rendering_length_error=None, fps=None, frame_distribution=None):
+    self.drift_time = drift_time
+    self.mean_drift_time = mean_drift_time
+    self.std_dev_drift_time = std_dev_drift_time
+    self.percent_badly_out_of_sync = percent_badly_out_of_sync
+    self.percent_out_of_sync = percent_out_of_sync
+    self.smoothness_score = smoothness_score
+    self.freezing_score = freezing_score
+    self.rendering_length_error = rendering_length_error
+    self.fps = fps
+    self.frame_distribution = frame_distribution
+    self.invalid_data = False
+
+
+
+class WebMediaPlayerMsRenderingStats(object):
+  """Analyzes events of WebMediaPlayerMs type."""
+
+  def __init__(self, events):
+    """Save relevant events according to their stream."""
+    self.stream_to_events = self._MapEventsToStream(events)
+
+  def _IsEventValid(self, event):
+    """Check that the needed arguments are present in event.
+
+    Args:
+      event: event to check.
+
+    Returns:
+      True is event is valid, false otherwise."""
+    if not event.args:
+      return False
+    mandatory = [ACTUAL_RENDER_BEGIN, ACTUAL_RENDER_END,
+        IDEAL_RENDER_INSTANT, SERIAL]
+    for parameter in mandatory:
+      if not parameter in event.args:
+        return False
+    return True
+
+  def _MapEventsToStream(self, events):
+    """Build a dictionary of events indexed by stream.
+
+    The events of interest have a 'Serial' argument which represents the
+    stream ID. The 'Serial' argument identifies the local or remote nature of
+    the stream with a least significant bit  of 0 or 1 as well as the hash
+    value of the video track's URL. So stream::=hash(0|1} . The method will
+    then list the events of the same stream in a frame_distribution on stream
+    id. Practically speaking remote streams have an odd stream id and local
+    streams have a even stream id.
+    Args:
+      events: Telemetry WebMediaPlayerMs events.
+
+    Returns:
+      A dict of stream IDs mapped to events on that stream.
+    """
+    stream_to_events = {}
+    for event in events:
+      if not self._IsEventValid(event):
+        # This is not a render event, skip it.
+        continue
+      stream = event.args[SERIAL]
+      events_for_stream = stream_to_events.setdefault(stream, [])
+      events_for_stream.append(event)
+
+    return stream_to_events
+
+  def _GetCadence(self, relevant_events):
+    """Calculate the apparent cadence of the rendering.
+
+    In this paragraph I will be using regex notation. What is intended by the
+    word cadence is a sort of extended instantaneous 'Cadence' (thus not
+    necessarily periodic). Just as an example, a normal 'Cadence' could be
+    something like [2 3] which means possibly an observed frame persistence
+    progression of [{2 3}+] for an ideal 20FPS video source. So what we are
+    calculating here is the list of frame persistence, kind of a
+    'Proto-Cadence', but cadence is shorter so we abuse the word.
+
+    Args:
+      relevant_events: list of Telemetry events.
+
+    Returns:
+      a list of frame persistence values.
+    """
+    cadence = []
+    frame_persistence = 0
+    old_ideal_render = 0
+    for event in relevant_events:
+      if not self._IsEventValid(event):
+        # This event is not a render event so skip it.
+        continue
+      if event.args[IDEAL_RENDER_INSTANT] == old_ideal_render:
+        frame_persistence += 1
+      else:
+        cadence.append(frame_persistence)
+        frame_persistence = 1
+        old_ideal_render = event.args[IDEAL_RENDER_INSTANT]
+    cadence.append(frame_persistence)
+    cadence.pop(0)
+    return cadence
+
+  def _GetSourceToOutputDistribution(self, cadence):
+    """Create distribution for the cadence frame display values.
+
+    If the overall display distribution is A1:A2:..:An, this will tell us how
+    many times a frame stays displayed during Ak*VSYNC_DURATION, also known as
+    'source to output' distribution. Or in other terms:
+    a distribution B::= let C be the cadence, B[k]=p with k in Unique(C)
+    and p=Card(k in C).
+
+    Args:
+      cadence: list of frame persistence values.
+
+    Returns:
+      a dictionary containing the distribution
+    """
+    frame_distribution = {}
+    for ticks in cadence:
+      ticks_so_far = frame_distribution.setdefault(ticks, 0)
+      frame_distribution[ticks] = ticks_so_far + 1
+    return frame_distribution
+
+  def _GetFpsFromCadence(self, frame_distribution):
+    """Calculate the apparent FPS from frame distribution.
+
+    Knowing the display frequency and the frame distribution, it is possible to
+    calculate the video apparent frame rate as played by WebMediaPlayerMs
+    module.
+
+    Args:
+      frame_distribution: the source to output distribution.
+
+    Returns:
+      the video apparent frame rate.
+    """
+    number_frames = sum(frame_distribution.values())
+    number_vsyncs = sum([ticks * frame_distribution[ticks]
+       for ticks in frame_distribution])
+    mean_ratio = float(number_vsyncs) / number_frames
+    return DISPLAY_HERTZ / mean_ratio
+
+  def _GetFrozenFramesReports(self, frame_distribution):
+    """Find evidence of frozen frames in distribution.
+
+    For simplicity we count as freezing the frames that appear at least five
+    times in a row counted from 'Ideal Render Instant' perspective. So let's
+    say for 1 source frame, we rendered 6 frames, then we consider 5 of these
+    rendered frames as frozen. But we mitigate this by saying anything under
+    5 frozen frames will not be counted as frozen.
+
+    Args:
+      frame_distribution: the source to output distribution.
+
+    Returns:
+      a list of dicts whose keys are ('frozen_frames', 'occurrences').
+    """
+    frozen_frames = []
+    frozen_frame_vsyncs = [ticks for ticks in frame_distribution if ticks >=
+        FROZEN_THRESHOLD]
+    for frozen_frames_vsync in frozen_frame_vsyncs:
+      logging.debug('%s frames not updated after %s vsyncs',
+          frame_distribution[frozen_frames_vsync], frozen_frames_vsync)
+      frozen_frames.append(
+          {'frozen_frames': frozen_frames_vsync - 1,
+           'occurrences': frame_distribution[frozen_frames_vsync]})
+    return frozen_frames
+
+  def _FrozenPenaltyWeight(self, number_frozen_frames):
+    """Returns the weighted penalty for a number of frozen frames.
+
+    As mentioned earlier, we count for frozen anything above 6 vsync display
+    duration for the same 'Initial Render Instant', which is five frozen
+    frames.
+
+    Args:
+      number_frozen_frames: number of frozen frames.
+
+    Returns:
+      the penalty weight (int) for that number of frozen frames.
+    """
+
+    penalty = {
+      0: 0,
+      1: 0,
+      2: 0,
+      3: 0,
+      4: 0,
+      5: 1,
+      6: 5,
+      7: 15,
+      8: 25
+    }
+    weight = penalty.get(number_frozen_frames, 8 * (number_frozen_frames - 4))
+    return weight
+
+  def _IsRemoteStream(self, stream):
+    """Check if stream is remote."""
+    return stream % 2
+
+  def _GetDrifTimeStats(self, relevant_events, cadence):
+    """Get the drift time statistics.
+
+    This method will calculate drift_time stats, that is to say :
+    drift_time::= list(actual render begin - ideal render).
+    rendering_length error::= the rendering length error.
+
+    Args:
+      relevant_events: events to get drift times stats from.
+      cadence: list of frame persistence values.
+
+    Returns:
+      a tuple of (drift_time, rendering_length_error).
+    """
+    drift_time = []
+    old_ideal_render = 0
+    discrepancy = []
+    index = 0
+    for event in relevant_events:
+      current_ideal_render = event.args[IDEAL_RENDER_INSTANT]
+      if current_ideal_render == old_ideal_render:
+        # Skip to next event because we're looking for a source frame.
+        continue
+      actual_render_begin = event.args[ACTUAL_RENDER_BEGIN]
+      drift_time.append(actual_render_begin - current_ideal_render)
+      discrepancy.append(abs(current_ideal_render - old_ideal_render
+          - VSYNC_DURATION * cadence[index]))
+      old_ideal_render = current_ideal_render
+      index += 1
+    discrepancy.pop(0)
+    last_ideal_render = relevant_events[-1].args[IDEAL_RENDER_INSTANT]
+    first_ideal_render = relevant_events[0].args[IDEAL_RENDER_INSTANT]
+    rendering_length_error = 100.0 * (sum([x for x in discrepancy]) /
+        (last_ideal_render - first_ideal_render))
+
+    return drift_time, rendering_length_error
+
+  def _GetSmoothnessStats(self, norm_drift_time):
+    """Get the smoothness stats from the normalized drift time.
+
+    This method will calculate the smoothness score, along with the percentage
+    of frames badly out of sync and the percentage of frames out of sync. To be
+    considered badly out of sync, a frame has to have missed rendering by at
+    least 2*VSYNC_DURATION. To be considered out of sync, a frame has to have
+    missed rendering by at least one VSYNC_DURATION.
+    The smoothness score is a measure of how out of sync the frames are.
+
+    Args:
+      norm_drift_time: normalized drift time.
+
+    Returns:
+      a tuple of (percent_badly_oos, percent_out_of_sync, smoothness_score)
+    """
+    # How many times is a frame later/earlier than T=2*VSYNC_DURATION. Time is
+    # in microseconds.
+    frames_severely_out_of_sync = len(
+        [x for x in norm_drift_time if abs(x) > 2 * VSYNC_DURATION])
+    percent_badly_oos = (
+        100.0 * frames_severely_out_of_sync / len(norm_drift_time))
+
+    # How many times is a frame later/earlier than VSYNC_DURATION.
+    frames_out_of_sync = len(
+        [x for x in norm_drift_time if abs(x) > VSYNC_DURATION])
+    percent_out_of_sync = (
+        100.0 * frames_out_of_sync / len(norm_drift_time))
+
+    frames_oos_only_once = frames_out_of_sync - frames_severely_out_of_sync
+
+    # Calculate smoothness metric. From the formula, we can see that smoothness
+    # score can be negative.
+    smoothness_score = 100.0 - 100.0 * (frames_oos_only_once +
+        SEVERITY * frames_severely_out_of_sync) / len(norm_drift_time)
+
+    # Minimum smoothness_score value allowed is zero.
+    if smoothness_score < 0:
+      smoothness_score = 0
+
+    return (percent_badly_oos, percent_out_of_sync, smoothness_score)
+
+  def _GetFreezingScore(self, frame_distribution):
+    """Get the freezing score."""
+
+    # The freezing score is based on the source to output distribution.
+    number_vsyncs = sum([n * frame_distribution[n]
+        for n in frame_distribution])
+    frozen_frames = self._GetFrozenFramesReports(frame_distribution)
+
+    # Calculate freezing metric.
+    # Freezing metric can be negative if things are really bad. In that case we
+    # change it to zero as minimum valud.
+    freezing_score = 100.0
+    for frozen_report in frozen_frames:
+      weight = self._FrozenPenaltyWeight(frozen_report['frozen_frames'])
+      freezing_score -= (
+          100.0 * frozen_report['occurrences'] / number_vsyncs * weight)
+    if freezing_score < 0:
+      freezing_score = 0
+
+    return freezing_score
+
+  def GetTimeStats(self):
+    """Calculate time stamp stats for all remote stream events."""
+    stats = {}
+    for stream, relevant_events in self.stream_to_events.iteritems():
+      if len(relevant_events) == 1:
+        logging.debug('Found a stream=%s with just one event', stream)
+        continue
+      if not self._IsRemoteStream(stream):
+        logging.info('Skipping processing of local stream: %s', stream)
+        continue
+
+      cadence = self._GetCadence(relevant_events)
+      if not cadence:
+        stats = TimeStats()
+        stats.invalid_data = True
+        return stats
+
+      frame_distribution = self._GetSourceToOutputDistribution(cadence)
+      fps = self._GetFpsFromCadence(frame_distribution)
+
+      drift_time_stats = self._GetDrifTimeStats(relevant_events, cadence)
+      (drift_time, rendering_length_error) = drift_time_stats
+
+      # Drift time normalization.
+      mean_drift_time = statistics.ArithmeticMean(drift_time)
+      norm_drift_time = [abs(x - mean_drift_time) for x in drift_time]
+
+      smoothness_stats = self._GetSmoothnessStats(norm_drift_time)
+      (percent_badly_oos, percent_out_of_sync,
+          smoothness_score) = smoothness_stats
+
+      freezing_score = self._GetFreezingScore(frame_distribution)
+
+      stats = TimeStats(drift_time=drift_time,
+          percent_badly_out_of_sync=percent_badly_oos,
+          percent_out_of_sync=percent_out_of_sync,
+          smoothness_score=smoothness_score, freezing_score=freezing_score,
+          rendering_length_error=rendering_length_error, fps=fps,
+          frame_distribution=frame_distribution)
+    return stats
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/webrtc_rendering_stats_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/webrtc_rendering_stats_unittest.py
new file mode 100644
index 0000000..15e1035
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/webrtc_rendering_stats_unittest.py
@@ -0,0 +1,271 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import unittest
+
+from telemetry.web_perf.metrics import webrtc_rendering_stats as stats_helper
+
+
+class FakeEvent(object):
+  """Fake event class to mock rendering events."""
+
+  def __init__(self, **kwargs):
+    """Initializer for the fake WebMediaPlayerMS::UpdateCurrentFrame events.
+
+    The WebMediaPlayerMsRenderingStats only cares about actual render begin,
+    actual render end, ideal render instant and serial fields of the events.
+    So we only define these four fields here in this fake event class.
+    This method is written so as to take whatever valid parameters from the
+    event definition. It can also be used to craft incomplete events.
+
+    Args:
+      kwargs::= dict('actual_begin', 'actual_end', 'ideal_instant', 'serial').
+    """
+    self.args = {}
+    name_map = {
+        'Actual Render Begin': 'actual_begin',
+        'Actual Render End': 'actual_end',
+        'Ideal Render Instant': 'ideal_instant',
+        'Serial': 'serial'}
+    for internal_name, external_name in name_map.iteritems():
+      if external_name in kwargs:
+        self.args[internal_name] = kwargs[external_name]
+
+
+class WebMediaPlayerMsRenderingStatsTest(unittest.TestCase):
+
+  def setUp(self):
+    # A local stream id always has an even number.
+    # A remote stream id always has an odd number.
+    self.local_stream = 136390988
+    self.remote_stream = 118626165
+
+  def testInitialization(self):
+    event_local_stream = FakeEvent(actual_begin=1655987203306,
+        actual_end=1655987219972, ideal_instant=1655987154324,
+        serial=self.local_stream)
+
+    event_remote_stream = FakeEvent(actual_begin=1655987203306,
+        actual_end=1655987219972, ideal_instant=1655987167999,
+        serial=self.remote_stream)
+
+    stats_parser = stats_helper.WebMediaPlayerMsRenderingStats(
+        [event_local_stream, event_remote_stream])
+
+    self.assertEqual(2, len(stats_parser.stream_to_events))
+
+    self.assertEqual(event_local_stream.args,
+        stats_parser.stream_to_events[self.local_stream][0].args)
+
+    self.assertEqual(event_remote_stream.args,
+        stats_parser.stream_to_events[self.remote_stream][0].args)
+
+  def testInvalidEvents(self):
+    event_missing_serial = FakeEvent(actual_begin=1655987244074,
+        actual_end=1655987260740, ideal_instant=1655987204839)
+
+    event_missing_actual_begin = FakeEvent(actual_end=1655987260740,
+        ideal_instant=1655987217999, serial=self.local_stream)
+
+    event_missing_actual_end = FakeEvent(actual_end=1655987260740,
+        ideal_instant=1655987217999, serial=self.remote_stream)
+
+    event_missing_ideal_instant = FakeEvent(actual_begin=1655987260740,
+        actual_end=1655987277406, serial=self.remote_stream)
+
+    stats_parser = stats_helper.WebMediaPlayerMsRenderingStats(
+        [event_missing_serial, event_missing_actual_begin,
+         event_missing_actual_end, event_missing_ideal_instant])
+
+    self.assertEqual(0, len(stats_parser.stream_to_events))
+
+  def _GetFakeEvents(self):
+    fake_events = [
+        FakeEvent(actual_begin=1663780195583, actual_end=1663780212249,
+            ideal_instant=1663780179998, serial=self.remote_stream),
+        FakeEvent(actual_begin=1663780212249, actual_end=1663780228915,
+            ideal_instant=1663780179998, serial=self.remote_stream),
+        FakeEvent(actual_begin=1663780228915, actual_end=1663780245581,
+            ideal_instant=1663780197998, serial=self.remote_stream),
+        FakeEvent(actual_begin=1663780245581, actual_end=1663780262247,
+            ideal_instant=1663780215998, serial=self.remote_stream),
+        FakeEvent(actual_begin=1663780262247, actual_end=1663780278913,
+            ideal_instant=1663780215998, serial=self.remote_stream),
+        FakeEvent(actual_begin=1663780278913, actual_end=1663780295579,
+            ideal_instant=1663780254998, serial=self.remote_stream),
+        FakeEvent(actual_begin=1663780295579, actual_end=1663780312245,
+            ideal_instant=1663780254998, serial=self.remote_stream),
+        FakeEvent(actual_begin=1663780312245, actual_end=1663780328911,
+           ideal_instant=1663780254998, serial=self.remote_stream),
+        FakeEvent(actual_begin=1663780328911, actual_end=1663780345577,
+           ideal_instant=1663780310998, serial=self.remote_stream),
+        FakeEvent(actual_begin=1663780345577, actual_end=1663780362243,
+            ideal_instant=1663780310998, serial=self.remote_stream),
+        FakeEvent(actual_begin=1663780362243, actual_end=1663780378909,
+            ideal_instant=1663780310998, serial=self.remote_stream),
+        FakeEvent(actual_begin=1663780378909, actual_end=1663780395575,
+            ideal_instant=1663780361998, serial=self.remote_stream),
+        FakeEvent(actual_begin=1663780395575, actual_end=1663780412241,
+            ideal_instant=1663780361998, serial=self.remote_stream),
+        FakeEvent(actual_begin=1663780412241, actual_end=1663780428907,
+            ideal_instant=1663780361998, serial=self.remote_stream),
+        FakeEvent(actual_begin=1663780428907, actual_end=1663780445573,
+            ideal_instant=1663780412998, serial=self.remote_stream)]
+
+    return fake_events
+
+  def _GetCorruptEvents(self):
+    # The events below are corrupt data because the |ideal_instant|
+    # parameter is zero, which makes all computation meaningless.
+    # Indeed, the ideal_instant (aka Ideal Render Instant) indicates
+    # when the frame should be rendered ideally.
+    corrupt_events = [
+        FakeEvent(actual_begin=1663780195583, actual_end=1663780212249,
+            ideal_instant=0, serial=self.remote_stream),
+        FakeEvent(actual_begin=1663780212249, actual_end=1663780228915,
+            ideal_instant=0, serial=self.remote_stream),
+        FakeEvent(actual_begin=1663780228915, actual_end=1663780245581,
+            ideal_instant=0, serial=self.remote_stream),
+        FakeEvent(actual_begin=1663780245581, actual_end=1663780262247,
+            ideal_instant=0, serial=self.remote_stream)]
+    return corrupt_events
+
+  def testGetCadence(self):
+    fake_events = self._GetFakeEvents()
+    stats_parser = stats_helper.WebMediaPlayerMsRenderingStats(fake_events)
+    # The events defined in _GetFakeEvents above show that the first source
+    # framee of ideal_instant=1663780179998 is rendered twice, then
+    # the second source frame of ideal_instant=1663780197998 is rendered once
+    # the third source frame of  ideal_instant=1663780215998 is rendered twice
+    # and so on. The expected cadence will therefore be [2 1 2 etc..]
+    expected_cadence = [2, 1, 2, 3, 3, 3, 1]
+    self.assertEqual(expected_cadence, stats_parser._GetCadence(fake_events))
+
+  def testGetSourceToOutputDistribution(self):
+    stats_parser = stats_helper.WebMediaPlayerMsRenderingStats([])
+    cadence = [2, 1, 2, 3, 3, 3, 1]
+    expected_frame_distribution = {1: 2, 2: 2, 3: 3}
+    self.assertEqual(expected_frame_distribution,
+        stats_parser._GetSourceToOutputDistribution(cadence))
+
+  def testGetFpsFromCadence(self):
+    frame_distribution = {1: 2, 2: 2, 3: 3}
+    stats_parser = stats_helper.WebMediaPlayerMsRenderingStats([])
+    expected_frame_rate = 28.0
+    self.assertEqual(expected_frame_rate,
+        stats_parser._GetFpsFromCadence(frame_distribution))
+
+  def testGetFrozenFramesReports(self):
+    frame_distribution = {1: 2, 2: 2, 3: 569, 6: 1}
+    expected_frozen_reports = [{'frozen_frames': 5, 'occurrences': 1}]
+    stats_parser = stats_helper.WebMediaPlayerMsRenderingStats([])
+    self.assertEqual(expected_frozen_reports,
+        stats_parser._GetFrozenFramesReports(frame_distribution))
+
+  def testIsRemoteStream(self):
+    stats_parser = stats_helper.WebMediaPlayerMsRenderingStats([])
+    self.assertTrue(stats_parser._IsRemoteStream(self.remote_stream))
+
+  def testGetDrifTimeStats(self):
+    fake_events = self._GetFakeEvents()
+    stats_parser = stats_helper.WebMediaPlayerMsRenderingStats([])
+    cadence = stats_parser._GetCadence(fake_events)
+    expected_drift_time = [15585, 30917, 29583, 23915, 17913, 16911, 15909]
+    expected_rendering_length_error = 29.613733905579398
+
+    self.assertEqual((expected_drift_time, expected_rendering_length_error),
+        stats_parser._GetDrifTimeStats(fake_events, cadence))
+
+  def testGetSmoothnessStats(self):
+    norm_drift_time = [5948.2857142857138, 9383.7142857142862,
+        8049.7142857142862, 2381.7142857142862, 3620.2857142857138,
+        4622.2857142857138, 5624.2857142857138]
+    stats_parser = stats_helper.WebMediaPlayerMsRenderingStats([])
+    expected_percent_badly_oos = 0.0
+    expected_percent_out_of_sync = 0.0
+    expected_smoothness_score = 100.0
+    expected_smoothness_stats = (expected_percent_badly_oos,
+        expected_percent_out_of_sync, expected_smoothness_score)
+
+    self.assertEqual(expected_smoothness_stats,
+        stats_parser._GetSmoothnessStats(norm_drift_time))
+
+  def testNegativeSmoothnessScoreChangedToZero(self):
+    norm_drift_time = [15948.285714285714, 9383.714285714286,
+        28049.714285714286, 72381.71428571429, 3620.2857142857138,
+        4622.285714285714, 35624.28571428572]
+    stats_parser = stats_helper.WebMediaPlayerMsRenderingStats([])
+    expected_percent_badly_oos = 28.571428571428573
+    expected_percent_out_of_sync = 42.857142857142854
+    expected_smoothness_score = 0.0
+    expected_smoothness_stats = (expected_percent_badly_oos,
+        expected_percent_out_of_sync, expected_smoothness_score)
+
+    self.assertEqual(expected_smoothness_stats,
+        stats_parser._GetSmoothnessStats(norm_drift_time))
+
+  def testGetFreezingScore(self):
+    frame_distribution = {1: 2, 2: 2, 3: 569, 6: 1}
+    stats_parser = stats_helper.WebMediaPlayerMsRenderingStats([])
+    expected_freezing_score = 99.94182664339732
+    self.assertEqual(expected_freezing_score,
+        stats_parser._GetFreezingScore(frame_distribution))
+
+  def testNegativeFrezingScoreChangedToZero(self):
+    frame_distribution = {1: 2, 2: 2, 3: 2, 8:100}
+    stats_parser = stats_helper.WebMediaPlayerMsRenderingStats([])
+    self.assertEqual(0.0, stats_parser._GetFreezingScore(frame_distribution))
+
+  def testGetTimeStats(self):
+    fake_events = self._GetFakeEvents()
+    expected_frame_dist = {1: 2, 2: 2, 3: 3}
+    expected_frame_rate = 28.0
+    expected_drift_time = [15585, 30917, 29583, 23915, 17913, 16911, 15909]
+    expected_rendering_length_error = 29.613733905579398
+    expected_percent_badly_oos = 0.0
+    expected_percent_out_of_sync = 0.0
+    expected_smoothness_score = 100.0
+    expected_freezing_score = 100.0
+
+    stats_cls = stats_helper.WebMediaPlayerMsRenderingStats
+
+    stats_parser = stats_cls(fake_events)
+
+    expected_stats = stats_helper.TimeStats(
+        drift_time=expected_drift_time,
+        percent_badly_out_of_sync=expected_percent_badly_oos,
+        percent_out_of_sync=expected_percent_out_of_sync,
+        smoothness_score=expected_smoothness_score,
+        freezing_score=expected_freezing_score,
+        rendering_length_error=expected_rendering_length_error,
+        fps=expected_frame_rate,
+        frame_distribution=expected_frame_dist)
+
+    stats = stats_parser.GetTimeStats()
+
+    self.assertEqual(expected_stats.drift_time, stats.drift_time)
+    self.assertEqual(expected_stats.percent_badly_out_of_sync,
+        stats.percent_badly_out_of_sync)
+    self.assertEqual(expected_stats.percent_out_of_sync,
+        stats.percent_out_of_sync)
+    self.assertEqual(expected_stats.smoothness_score, stats.smoothness_score)
+    self.assertEqual(expected_stats.freezing_score, stats.freezing_score)
+    self.assertEqual(expected_stats.rendering_length_error,
+        stats.rendering_length_error)
+    self.assertEqual(expected_stats.fps, stats.fps)
+    self.assertEqual(expected_stats.frame_distribution,
+        stats.frame_distribution)
+
+  def testCorruptData(self):
+    corrupt_events = self._GetCorruptEvents()
+    stats_parser = stats_helper.WebMediaPlayerMsRenderingStats(corrupt_events)
+    stats = stats_parser.GetTimeStats()
+    self.assertTrue(stats.invalid_data)
+    self.assertIsNone(stats.drift_time)
+    self.assertIsNone(stats.percent_badly_out_of_sync)
+    self.assertIsNone(stats.percent_out_of_sync)
+    self.assertIsNone(stats.smoothness_score)
+    self.assertIsNone(stats.freezing_score)
+    self.assertIsNone(stats.rendering_length_error)
+    self.assertIsNone(stats.fps)
+    self.assertIsNone(stats.frame_distribution)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/webrtc_rendering_timeline.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/webrtc_rendering_timeline.py
new file mode 100644
index 0000000..062ef09
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/metrics/webrtc_rendering_timeline.py
@@ -0,0 +1,133 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.value import list_of_scalar_values
+from telemetry.value import scalar
+from telemetry.value import improvement_direction
+from telemetry.web_perf.metrics import timeline_based_metric
+from telemetry.web_perf.metrics import webrtc_rendering_stats as stats_helper
+
+WEB_MEDIA_PLAYER_MS_EVENT = 'WebMediaPlayerMS::UpdateCurrentFrame'
+
+
+class WebRtcRenderingTimelineMetric(timeline_based_metric.TimelineBasedMetric):
+  """WebrtcRenderingTimelineMetric calculates metric for WebMediaPlayerMS.
+
+  The following metrics are added to the results:
+    WebRTCRendering_drift_time us
+    WebRTCRendering_percent_badly_out_of_sync %
+    WebRTCRendering_percent_out_of_sync %
+    WebRTCRendering_fps FPS
+    WebRTCRendering_smoothness_score %
+    WebRTCRendering_freezing_score %
+    WebRTCRendering_rendering_length_error %
+  """
+
+  def __init__(self):
+    super(WebRtcRenderingTimelineMetric, self).__init__()
+
+  @staticmethod
+  def IsMediaPlayerMSEvent(event):
+    """Verify that the event is a webmediaplayerMS event."""
+    return event.name == WEB_MEDIA_PLAYER_MS_EVENT
+
+  def AddResults(self, model, renderer_thread, interactions, results):
+    """Adding metrics to the results."""
+    assert interactions
+    found_events = []
+    for event in renderer_thread.parent.IterAllEvents(
+        event_predicate=self.IsMediaPlayerMSEvent):
+      if timeline_based_metric.IsEventInInteractions(event, interactions):
+        found_events.append(event)
+    stats_parser = stats_helper.WebMediaPlayerMsRenderingStats(found_events)
+    rendering_stats = stats_parser.GetTimeStats()
+    none_reason = None
+    if not rendering_stats:
+      # Create a TimeStats object whose members have None values.
+      rendering_stats = stats_helper.TimeStats()
+      none_reason = 'No WebMediaPlayerMS::UpdateCurrentFrame event found'
+    elif rendering_stats.invalid_data:
+      # Throw away the data.
+      rendering_stats = stats_helper.TimeStats()
+      none_reason = 'WebMediaPlayerMS data is corrupted.'
+    results.AddValue(list_of_scalar_values.ListOfScalarValues(
+        results.current_page,
+        'WebRTCRendering_drift_time',
+        'us',
+        rendering_stats.drift_time,
+        important=True,
+        description='Drift time for a rendered frame',
+        tir_label=interactions[0].label,
+        improvement_direction=improvement_direction.DOWN,
+        none_value_reason=none_reason))
+
+    results.AddValue(scalar.ScalarValue(
+        results.current_page,
+        'WebRTCRendering_percent_badly_out_of_sync',
+        '%',
+        rendering_stats.percent_badly_out_of_sync,
+        important=True,
+        description='Percentage of frame which drifted more than 2 VSYNC',
+        tir_label=interactions[0].label,
+        improvement_direction=improvement_direction.DOWN,
+        none_value_reason=none_reason))
+
+    results.AddValue(scalar.ScalarValue(
+        results.current_page,
+        'WebRTCRendering_percent_out_of_sync',
+        '%',
+        rendering_stats.percent_out_of_sync,
+        important=True,
+        description='Percentage of frame which drifted more than 1 VSYNC',
+        tir_label=interactions[0].label,
+        improvement_direction=improvement_direction.DOWN,
+        none_value_reason=none_reason))
+
+    # I removed the frame distribution list from stats as it is not a metric,
+    # rather it is the underlying data. Also there is no sense of improvement
+    # direction for frame distribution.
+
+    results.AddValue(scalar.ScalarValue(
+        results.current_page,
+        'WebRTCRendering_fps',
+        'fps',
+        rendering_stats.fps,
+        important=True,
+        description='Calculated Frame Rate of video rendering',
+        tir_label=interactions[0].label,
+        improvement_direction=improvement_direction.UP,
+        none_value_reason=none_reason))
+
+    results.AddValue(scalar.ScalarValue(
+        results.current_page,
+        'WebRTCRendering_smoothness_score',
+        '%',
+        rendering_stats.smoothness_score,
+        important=True,
+        description='Smoothness score of rendering',
+        tir_label=interactions[0].label,
+        improvement_direction=improvement_direction.UP,
+        none_value_reason=none_reason))
+
+    results.AddValue(scalar.ScalarValue(
+        results.current_page,
+        'WebRTCRendering_freezing_score',
+        '%',
+        rendering_stats.freezing_score,
+        important=True,
+        description='Freezing score of rendering',
+        tir_label=interactions[0].label,
+        improvement_direction=improvement_direction.UP,
+        none_value_reason=none_reason))
+
+    results.AddValue(scalar.ScalarValue(
+        results.current_page,
+        'WebRTCRendering_rendering_length_error',
+        '%',
+        rendering_stats.rendering_length_error,
+        important=True,
+        description='Rendering length error rate',
+        tir_label=interactions[0].label,
+        improvement_direction=improvement_direction.DOWN,
+        none_value_reason=none_reason))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/smooth_gesture_util.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/smooth_gesture_util.py
new file mode 100644
index 0000000..0d0d453
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/smooth_gesture_util.py
@@ -0,0 +1,37 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import copy
+
+from telemetry.web_perf import timeline_interaction_record as tir_module
+
+
+def GetAdjustedInteractionIfContainGesture(timeline, interaction_record):
+  """ Returns a new interaction record if interaction_record contains geture
+  whose time range that overlaps with interaction_record's range. If not,
+  returns a clone of original interaction_record.
+  The synthetic gesture controller inserts a trace marker to precisely
+  demarcate when the gesture was running. We check for overlap, not inclusion,
+  because gesture_actions can start/end slightly outside the telemetry markers
+  on Windows. This problem is probably caused by a race condition between
+  the browser and renderer process submitting the trace events for the
+  markers.
+  """
+  # Only adjust the range for gestures.
+  if not interaction_record.label.startswith('Gesture_'):
+    return copy.copy(interaction_record)
+  gesture_events = [
+    ev for ev
+    in timeline.IterAllAsyncSlicesOfName('SyntheticGestureController::running')
+    if ev.parent_slice is None and
+    ev.start <= interaction_record.end and
+    ev.end >= interaction_record.start]
+  if len(gesture_events) == 0:
+    return copy.copy(interaction_record)
+  if len(gesture_events) > 1:
+    raise Exception('More than one possible synthetic gesture marker found in '
+                    'interaction_record %s.' % interaction_record.label)
+  return tir_module.TimelineInteractionRecord(
+    interaction_record.label, gesture_events[0].start,
+    gesture_events[0].end, gesture_events[0],
+    interaction_record._flags)  # pylint: disable=protected-access
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/smooth_gesture_util_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/smooth_gesture_util_unittest.py
new file mode 100644
index 0000000..07bf4fd
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/smooth_gesture_util_unittest.py
@@ -0,0 +1,157 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import time
+import unittest
+
+from telemetry import decorators
+from telemetry.page import page as page_module
+from telemetry.page import legacy_page_test
+from telemetry.testing import page_test_test_case
+from telemetry.timeline import async_slice
+from telemetry.timeline import model as model_module
+from telemetry.timeline import tracing_config
+from telemetry.web_perf import smooth_gesture_util as sg_util
+from telemetry.web_perf import timeline_interaction_record as tir_module
+
+
+class SmoothGestureUtilTest(unittest.TestCase):
+  def testGetAdjustedInteractionIfContainGesture(self):
+    model = model_module.TimelineModel()
+    renderer_main = model.GetOrCreateProcess(1).GetOrCreateThread(2)
+    renderer_main.name = 'CrRendererMain'
+
+    #      [          X          ]                   [   Y  ]
+    #      [  sub_async_slice_X  ]
+    #          [   record_1]
+    #          [   record_6]
+    #  [  record_2 ]          [ record_3 ]
+    #  [           record_4              ]
+    #                                [ record_5 ]
+    #
+    # Note: X and Y are async slice with name
+    # SyntheticGestureController::running
+
+    async_slice_X = async_slice.AsyncSlice(
+      'X', 'SyntheticGestureController::running', 10, duration=20,
+      start_thread=renderer_main, end_thread=renderer_main)
+
+    sub_async_slice_X = async_slice.AsyncSlice(
+      'X', 'SyntheticGestureController::running', 10, duration=20,
+      start_thread=renderer_main, end_thread=renderer_main)
+    sub_async_slice_X.parent_slice = async_slice_X
+    async_slice_X.AddSubSlice(sub_async_slice_X)
+
+    async_slice_Y = async_slice.AsyncSlice(
+      'X', 'SyntheticGestureController::running', 60, duration=20,
+      start_thread=renderer_main, end_thread=renderer_main)
+
+    renderer_main.AddAsyncSlice(async_slice_X)
+    renderer_main.AddAsyncSlice(async_slice_Y)
+
+    model.FinalizeImport(shift_world_to_zero=False)
+
+    record_1 = tir_module.TimelineInteractionRecord('Gesture_included', 15, 25)
+    record_2 = tir_module.TimelineInteractionRecord(
+      'Gesture_overlapped_left', 5, 25)
+    record_3 = tir_module.TimelineInteractionRecord(
+      'Gesture_overlapped_right', 25, 35)
+    record_4 = tir_module.TimelineInteractionRecord(
+      'Gesture_containing', 5, 35)
+    record_5 = tir_module.TimelineInteractionRecord(
+      'Gesture_non_overlapped', 35, 45)
+    record_6 = tir_module.TimelineInteractionRecord('Action_included', 15, 25)
+
+    adjusted_record_1 = sg_util.GetAdjustedInteractionIfContainGesture(
+      model, record_1)
+    self.assertEquals(adjusted_record_1.start, 10)
+    self.assertEquals(adjusted_record_1.end, 30)
+    self.assertTrue(adjusted_record_1 is not record_1)
+
+    adjusted_record_2 = sg_util.GetAdjustedInteractionIfContainGesture(
+      model, record_2)
+    self.assertEquals(adjusted_record_2.start, 10)
+    self.assertEquals(adjusted_record_2.end, 30)
+
+    adjusted_record_3 = sg_util.GetAdjustedInteractionIfContainGesture(
+      model, record_3)
+    self.assertEquals(adjusted_record_3.start, 10)
+    self.assertEquals(adjusted_record_3.end, 30)
+
+    adjusted_record_4 = sg_util.GetAdjustedInteractionIfContainGesture(
+      model, record_4)
+    self.assertEquals(adjusted_record_4.start, 10)
+    self.assertEquals(adjusted_record_4.end, 30)
+
+    adjusted_record_5 = sg_util.GetAdjustedInteractionIfContainGesture(
+      model, record_5)
+    self.assertEquals(adjusted_record_5.start, 35)
+    self.assertEquals(adjusted_record_5.end, 45)
+    self.assertTrue(adjusted_record_5 is not record_5)
+
+    adjusted_record_6 = sg_util.GetAdjustedInteractionIfContainGesture(
+      model, record_6)
+    self.assertEquals(adjusted_record_6.start, 15)
+    self.assertEquals(adjusted_record_6.end, 25)
+    self.assertTrue(adjusted_record_6 is not record_6)
+
+
+class ScrollingPage(page_module.Page):
+  def __init__(self, url, page_set, base_dir):
+    super(ScrollingPage, self).__init__(url, page_set, base_dir)
+
+  def RunPageInteractions(self, action_runner):
+    with action_runner.CreateGestureInteraction('ScrollAction'):
+      # Add 0.5s gap between when Gesture records are issued to when we actually
+      # scroll the page.
+      time.sleep(0.5)
+      action_runner.ScrollPage()
+      time.sleep(0.5)
+
+
+class SmoothGestureTest(page_test_test_case.PageTestTestCase):
+
+  @decorators.Disabled('chromeos')  # crbug.com/483212
+  @decorators.Isolated  # Needed because of py_trace_event
+  def testSmoothGestureAdjusted(self):
+    ps = self.CreateEmptyPageSet()
+    ps.AddStory(ScrollingPage(
+      'file://scrollable_page.html', ps, base_dir=ps.base_dir))
+    models = []
+    tab_ids = []
+    class ScrollingGestureTestMeasurement(legacy_page_test.LegacyPageTest):
+      def __init__(self):
+        # pylint: disable=bad-super-call
+        super(ScrollingGestureTestMeasurement, self).__init__()
+
+      def WillNavigateToPage(self, page, tab):
+        del page  # unused
+        config = tracing_config.TracingConfig()
+        config.enable_chrome_trace = True
+        tab.browser.platform.tracing_controller.StartTracing(config)
+
+      def ValidateAndMeasurePage(self, page, tab, results):
+        del page, results  # unused
+        models.append(model_module.TimelineModel(
+          tab.browser.platform.tracing_controller.StopTracing()))
+        tab_ids.append(tab.id)
+
+    self.RunMeasurement(ScrollingGestureTestMeasurement(), ps)
+    timeline_model = models[0]
+    renderer_thread = timeline_model.GetRendererThreadFromTabId(
+        tab_ids[0])
+    smooth_record = None
+    for e in renderer_thread.async_slices:
+      if tir_module.IsTimelineInteractionRecord(e.name):
+        smooth_record = tir_module.TimelineInteractionRecord.FromAsyncEvent(e)
+    self.assertIsNotNone(smooth_record)
+    adjusted_smooth_gesture = (
+      sg_util.GetAdjustedInteractionIfContainGesture(
+        timeline_model, smooth_record))
+    # Test that the scroll gesture starts at at least 500ms after the start of
+    # the interaction record and ends at at least 500ms before the end of
+    # interaction record.
+    self.assertLessEqual(
+      500, adjusted_smooth_gesture.start - smooth_record.start)
+    self.assertLessEqual(
+      500, smooth_record.end - adjusted_smooth_gesture.end)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/story_test.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/story_test.py
new file mode 100644
index 0000000..2ed65a4
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/story_test.py
@@ -0,0 +1,49 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+class Failure(Exception):
+  """StoryTest Exception raised when an undesired but designed-for problem."""
+
+
+class StoryTest(object):
+  """A class for creating story tests.
+
+  The overall test run control flow follows this order:
+    test.WillRunStory
+    state.WillRunStory
+    state.RunStory
+    test.Measure
+    state.DidRunStory
+    test.DidRunStory
+  """
+
+  def WillRunStory(self, platform):
+    """Override to do any action before running the story.
+
+    This is run before state.WillRunStory.
+    Args:
+      platform: The platform that the story will run on.
+    """
+    raise NotImplementedError()
+
+  def Measure(self, platform, results):
+    """Override to take the measurement.
+
+    This is run only if state.RunStory is successful.
+    Args:
+      platform: The platform that the story will run on.
+      results: The results of running the story.
+    """
+    raise NotImplementedError()
+
+  def DidRunStory(self, platform):
+    """Override to do any action after running the story, e.g., clean up.
+
+    This is run after state.DidRunStory. And this is always called even if the
+    test run failed.
+    Args:
+      platform: The platform that the story will run on.
+    """
+    raise NotImplementedError()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/timeline_based_measurement.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/timeline_based_measurement.py
new file mode 100644
index 0000000..961ed91
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/timeline_based_measurement.py
@@ -0,0 +1,358 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import collections
+import logging
+from collections import defaultdict
+
+from tracing.metrics import metric_runner
+
+from telemetry.timeline import chrome_trace_category_filter
+from telemetry.timeline import model as model_module
+from telemetry.timeline import tracing_config
+from telemetry.value import trace
+from telemetry.value import common_value_helpers
+from telemetry.web_perf.metrics import timeline_based_metric
+from telemetry.web_perf.metrics import blob_timeline
+from telemetry.web_perf.metrics import jitter_timeline
+from telemetry.web_perf.metrics import webrtc_rendering_timeline
+from telemetry.web_perf.metrics import gpu_timeline
+from telemetry.web_perf.metrics import indexeddb_timeline
+from telemetry.web_perf.metrics import layout
+from telemetry.web_perf.metrics import smoothness
+from telemetry.web_perf.metrics import text_selection
+from telemetry.web_perf import smooth_gesture_util
+from telemetry.web_perf import story_test
+from telemetry.web_perf import timeline_interaction_record as tir_module
+
+# TimelineBasedMeasurement considers all instrumentation as producing a single
+# timeline. But, depending on the amount of instrumentation that is enabled,
+# overhead increases. The user of the measurement must therefore chose between
+# a few levels of instrumentation.
+LOW_OVERHEAD_LEVEL = 'low-overhead'
+DEFAULT_OVERHEAD_LEVEL = 'default-overhead'
+DEBUG_OVERHEAD_LEVEL = 'debug-overhead'
+
+ALL_OVERHEAD_LEVELS = [
+  LOW_OVERHEAD_LEVEL,
+  DEFAULT_OVERHEAD_LEVEL,
+  DEBUG_OVERHEAD_LEVEL,
+]
+
+
+def _GetAllLegacyTimelineBasedMetrics():
+  # TODO(nednguyen): use discovery pattern to return all the instances of
+  # all TimelineBasedMetrics class in web_perf/metrics/ folder.
+  # This cannot be done until crbug.com/460208 is fixed.
+  return (smoothness.SmoothnessMetric(),
+          layout.LayoutMetric(),
+          gpu_timeline.GPUTimelineMetric(),
+          blob_timeline.BlobTimelineMetric(),
+          jitter_timeline.JitterTimelineMetric(),
+          text_selection.TextSelectionMetric(),
+          indexeddb_timeline.IndexedDBTimelineMetric(),
+          webrtc_rendering_timeline.WebRtcRenderingTimelineMetric())
+
+
+class InvalidInteractions(Exception):
+  pass
+
+
+# TODO(nednguyen): Get rid of this results wrapper hack after we add interaction
+# record to telemetry value system (crbug.com/453109)
+class ResultsWrapperInterface(object):
+  def __init__(self):
+    self._tir_label = None
+    self._results = None
+
+  def SetResults(self, results):
+    self._results = results
+
+  def SetTirLabel(self, tir_label):
+    self._tir_label = tir_label
+
+  @property
+  def current_page(self):
+    return self._results.current_page
+
+  def AddValue(self, value):
+    raise NotImplementedError
+
+
+class _TBMResultWrapper(ResultsWrapperInterface):
+  def AddValue(self, value):
+    assert self._tir_label
+    if value.tir_label:
+      assert value.tir_label == self._tir_label
+    else:
+      value.tir_label = self._tir_label
+    self._results.AddValue(value)
+
+
+def _GetRendererThreadsToInteractionRecordsMap(model):
+  threads_to_records_map = defaultdict(list)
+  interaction_labels_of_previous_threads = set()
+  for curr_thread in model.GetAllThreads():
+    for event in curr_thread.async_slices:
+      # TODO(nduca): Add support for page-load interaction record.
+      if tir_module.IsTimelineInteractionRecord(event.name):
+        interaction = tir_module.TimelineInteractionRecord.FromAsyncEvent(event)
+        # Adjust the interaction record to match the synthetic gesture
+        # controller if needed.
+        interaction = (
+            smooth_gesture_util.GetAdjustedInteractionIfContainGesture(
+                model, interaction))
+        threads_to_records_map[curr_thread].append(interaction)
+        if interaction.label in interaction_labels_of_previous_threads:
+          raise InvalidInteractions(
+            'Interaction record label %s is duplicated on different '
+            'threads' % interaction.label)
+    if curr_thread in threads_to_records_map:
+      interaction_labels_of_previous_threads.update(
+        r.label for r in threads_to_records_map[curr_thread])
+
+  return threads_to_records_map
+
+
+class _TimelineBasedMetrics(object):
+  def __init__(self, model, renderer_thread, interaction_records,
+               results_wrapper, metrics):
+    self._model = model
+    self._renderer_thread = renderer_thread
+    self._interaction_records = interaction_records
+    self._results_wrapper = results_wrapper
+    self._all_metrics = metrics
+
+  def AddResults(self, results):
+    interactions_by_label = defaultdict(list)
+    for i in self._interaction_records:
+      interactions_by_label[i.label].append(i)
+
+    for label, interactions in interactions_by_label.iteritems():
+      are_repeatable = [i.repeatable for i in interactions]
+      if not all(are_repeatable) and len(interactions) > 1:
+        raise InvalidInteractions('Duplicate unrepeatable interaction records '
+                                  'on the page')
+      self._results_wrapper.SetResults(results)
+      self._results_wrapper.SetTirLabel(label)
+      self.UpdateResultsByMetric(interactions, self._results_wrapper)
+
+  def UpdateResultsByMetric(self, interactions, wrapped_results):
+    if not interactions:
+      return
+
+    for metric in self._all_metrics:
+      metric.AddResults(self._model, self._renderer_thread,
+                        interactions, wrapped_results)
+
+
+class Options(object):
+  """A class to be used to configure TimelineBasedMeasurement.
+
+  This is created and returned by
+  Benchmark.CreateTimelineBasedMeasurementOptions.
+
+  By default, all the timeline based metrics in telemetry/web_perf/metrics are
+  used (see _GetAllLegacyTimelineBasedMetrics above).
+  To customize your metric needs, use SetTimelineBasedMetrics().
+  """
+
+  def __init__(self, overhead_level=LOW_OVERHEAD_LEVEL):
+    """As the amount of instrumentation increases, so does the overhead.
+    The user of the measurement chooses the overhead level that is appropriate,
+    and the tracing is filtered accordingly.
+
+    overhead_level: Can either be a custom ChromeTraceCategoryFilter object or
+        one of LOW_OVERHEAD_LEVEL, DEFAULT_OVERHEAD_LEVEL or
+        DEBUG_OVERHEAD_LEVEL.
+    """
+    self._config = tracing_config.TracingConfig()
+    self._config.enable_chrome_trace = True
+    self._config.enable_platform_display_trace = False
+
+    if isinstance(overhead_level,
+                  chrome_trace_category_filter.ChromeTraceCategoryFilter):
+      self._config.chrome_trace_config.SetCategoryFilter(overhead_level)
+    elif overhead_level in ALL_OVERHEAD_LEVELS:
+      if overhead_level == LOW_OVERHEAD_LEVEL:
+        self._config.chrome_trace_config.SetLowOverheadFilter()
+      elif overhead_level == DEFAULT_OVERHEAD_LEVEL:
+        self._config.chrome_trace_config.SetDefaultOverheadFilter()
+      else:
+        self._config.chrome_trace_config.SetDebugOverheadFilter()
+    else:
+      raise Exception("Overhead level must be a ChromeTraceCategoryFilter "
+                      "object or valid overhead level string. Given overhead "
+                      "level: %s" % overhead_level)
+
+    self._timeline_based_metrics = None
+    self._legacy_timeline_based_metrics = []
+
+
+  def ExtendTraceCategoryFilter(self, filters):
+    category_filter = self._config.chrome_trace_config.category_filter
+    for new_category_filter in filters:
+      category_filter.AddIncludedCategory(new_category_filter)
+
+  @property
+  def category_filter(self):
+    return self._config.chrome_trace_config.category_filter
+
+  @property
+  def config(self):
+    return self._config
+
+  def AddTimelineBasedMetric(self, metric):
+    assert isinstance(metric, basestring)
+    if self._timeline_based_metrics is None:
+      self._timeline_based_metrics = []
+    self._timeline_based_metrics.append(metric)
+
+  def SetTimelineBasedMetrics(self, metrics):
+    """Sets the new-style (TBMv2) metrics to run.
+
+    Metrics are assumed to live in //tracing/tracing/metrics, so the path you
+    pass in should be relative to that. For example, to specify
+    sample_metric.html, you should pass in ['sample_metric.html'].
+
+    Args:
+      metrics: A list of strings giving metric paths under
+          //tracing/tracing/metrics.
+    """
+    assert isinstance(metrics, list)
+    for metric in metrics:
+      assert isinstance(metric, basestring)
+    self._timeline_based_metrics = metrics
+
+  def GetTimelineBasedMetrics(self):
+    return self._timeline_based_metrics
+
+  def SetLegacyTimelineBasedMetrics(self, metrics):
+    assert isinstance(metrics, collections.Iterable)
+    for m in metrics:
+      assert isinstance(m, timeline_based_metric.TimelineBasedMetric)
+    self._legacy_timeline_based_metrics = metrics
+
+  def GetLegacyTimelineBasedMetrics(self):
+    return self._legacy_timeline_based_metrics
+
+
+class TimelineBasedMeasurement(story_test.StoryTest):
+  """Collects multiple metrics based on their interaction records.
+
+  A timeline based measurement shifts the burden of what metrics to collect onto
+  the story under test. Instead of the measurement
+  having a fixed set of values it collects, the story being tested
+  issues (via javascript) an Interaction record into the user timing API that
+  describing what is happening at that time, as well as a standardized set
+  of flags describing the semantics of the work being done. The
+  TimelineBasedMeasurement object collects a trace that includes both these
+  interaction records, and a user-chosen amount of performance data using
+  Telemetry's various timeline-producing APIs, tracing especially.
+
+  It then passes the recorded timeline to different TimelineBasedMetrics based
+  on those flags. As an example, this allows a single story run to produce
+  load timing data, smoothness data, critical jank information and overall cpu
+  usage information.
+
+  For information on how to mark up a page to work with
+  TimelineBasedMeasurement, refer to the
+  perf.metrics.timeline_interaction_record module.
+
+  Args:
+      options: an instance of timeline_based_measurement.Options.
+      results_wrapper: A class that has the __init__ method takes in
+        the page_test_results object and the interaction record label. This
+        class follows the ResultsWrapperInterface. Note: this class is not
+        supported long term and to be removed when crbug.com/453109 is resolved.
+  """
+  def __init__(self, options, results_wrapper=None):
+    self._tbm_options = options
+    self._results_wrapper = results_wrapper or _TBMResultWrapper()
+
+  def WillRunStory(self, platform):
+    """Configure and start tracing."""
+    if not platform.tracing_controller.IsChromeTracingSupported():
+      raise Exception('Not supported')
+    if self._tbm_options.config.enable_chrome_trace:
+      # Always enable 'blink.console' category for:
+      # 1) Backward compat of chrome clock sync (crbug.com/646925)
+      # 2) Allows users to add trace event through javascript.
+      # Note that blink.console is extremely low-overhead, so this doesn't
+      # affect the tracing overhead budget much.
+      chrome_config = self._tbm_options.config.chrome_trace_config
+      chrome_config.category_filter.AddIncludedCategory('blink.console')
+    platform.tracing_controller.StartTracing(self._tbm_options.config)
+
+  def Measure(self, platform, results):
+    """Collect all possible metrics and added them to results."""
+    platform.tracing_controller.telemetry_info = results.telemetry_info
+    trace_result = platform.tracing_controller.StopTracing()
+    trace_value = trace.TraceValue(results.current_page, trace_result)
+    results.AddValue(trace_value)
+
+    try:
+      if self._tbm_options.GetTimelineBasedMetrics():
+        assert not self._tbm_options.GetLegacyTimelineBasedMetrics(), (
+            'Specifying both TBMv1 and TBMv2 metrics is not allowed.')
+        self._ComputeTimelineBasedMetrics(results, trace_value)
+      else:
+        # Run all TBMv1 metrics if no other metric is specified
+        # (legacy behavior)
+        if not self._tbm_options.GetLegacyTimelineBasedMetrics():
+          logging.warn('Please specify the TBMv1 metrics you are interested in '
+                       'explicitly. This implicit functionality will be removed'
+                       ' on July 17, 2016.')
+          self._tbm_options.SetLegacyTimelineBasedMetrics(
+              _GetAllLegacyTimelineBasedMetrics())
+        self._ComputeLegacyTimelineBasedMetrics(results, trace_result)
+    finally:
+      trace_result.CleanUpAllTraces()
+
+  def DidRunStory(self, platform):
+    """Clean up after running the story."""
+    if platform.tracing_controller.is_tracing_running:
+      platform.tracing_controller.StopTracing()
+
+  def _ComputeTimelineBasedMetrics(self, results, trace_value):
+    metrics = self._tbm_options.GetTimelineBasedMetrics()
+    extra_import_options = {
+      'trackDetailedModelStats': True
+    }
+
+    mre_result = metric_runner.RunMetric(
+        trace_value.filename, metrics, extra_import_options)
+    page = results.current_page
+
+    failure_dicts = mre_result.failures
+    for d in failure_dicts:
+      results.AddValue(
+          common_value_helpers.TranslateMreFailure(d, page))
+
+    results.value_set.extend(mre_result.pairs.get('histograms', []))
+
+    for d in mre_result.pairs.get('scalars', []):
+      results.AddValue(common_value_helpers.TranslateScalarValue(d, page))
+
+  def _ComputeLegacyTimelineBasedMetrics(self, results, trace_result):
+    model = model_module.TimelineModel(trace_result)
+    threads_to_records_map = _GetRendererThreadsToInteractionRecordsMap(model)
+    if (len(threads_to_records_map.values()) == 0 and
+        self._tbm_options.config.enable_chrome_trace):
+      logging.warning(
+          'No timeline interaction records were recorded in the trace. '
+          'This could be caused by console.time() & console.timeEnd() execution'
+          ' failure or the tracing category specified doesn\'t include '
+          'blink.console categories.')
+
+    all_metrics = self._tbm_options.GetLegacyTimelineBasedMetrics()
+
+    for renderer_thread, interaction_records in (
+        threads_to_records_map.iteritems()):
+      meta_metrics = _TimelineBasedMetrics(
+          model, renderer_thread, interaction_records, self._results_wrapper,
+          all_metrics)
+      meta_metrics.AddResults(results)
+
+    for metric in all_metrics:
+      metric.AddWholeTraceResults(model, results)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/timeline_based_measurement_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/timeline_based_measurement_unittest.py
new file mode 100644
index 0000000..1523bfb
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/timeline_based_measurement_unittest.py
@@ -0,0 +1,212 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import unittest
+
+from telemetry import story
+from telemetry.internal.results import page_test_results
+from telemetry.page import page as page_module
+from telemetry.timeline import async_slice
+from telemetry.timeline import model as model_module
+from telemetry.value import improvement_direction
+from telemetry.value import scalar
+from telemetry.web_perf.metrics import timeline_based_metric
+from telemetry.web_perf import timeline_based_measurement as tbm_module
+
+
+class FakeSmoothMetric(timeline_based_metric.TimelineBasedMetric):
+
+  def AddResults(self, model, renderer_thread, interaction_records, results):
+    results.AddValue(scalar.ScalarValue(
+        results.current_page, 'FakeSmoothMetric', 'ms', 1,
+        improvement_direction=improvement_direction.DOWN))
+    results.AddValue(scalar.ScalarValue(
+        results.current_page, 'SmoothMetricRecords', 'count',
+        len(interaction_records),
+        improvement_direction=improvement_direction.DOWN))
+
+
+class FakeLoadingMetric(timeline_based_metric.TimelineBasedMetric):
+
+  def AddResults(self, model, renderer_thread, interaction_records, results):
+    results.AddValue(scalar.ScalarValue(
+        results.current_page, 'FakeLoadingMetric', 'ms', 2,
+        improvement_direction=improvement_direction.DOWN))
+    results.AddValue(scalar.ScalarValue(
+        results.current_page, 'LoadingMetricRecords', 'count',
+        len(interaction_records),
+        improvement_direction=improvement_direction.DOWN))
+
+
+class FakeStartupMetric(timeline_based_metric.TimelineBasedMetric):
+
+  def AddResults(self, model, renderer_thread, interaction_records, results):
+    pass
+
+  def AddWholeTraceResults(self, model, results):
+    results.AddValue(scalar.ScalarValue(
+        results.current_page, 'FakeStartupMetric', 'ms', 3,
+        improvement_direction=improvement_direction.DOWN))
+
+
+class TimelineBasedMetricTestData(object):
+
+  def __init__(self, options):
+    self._model = model_module.TimelineModel()
+    renderer_process = self._model.GetOrCreateProcess(1)
+    self._renderer_thread = renderer_process.GetOrCreateThread(2)
+    self._renderer_thread.name = 'CrRendererMain'
+    self._foo_thread = renderer_process.GetOrCreateThread(3)
+    self._foo_thread.name = 'CrFoo'
+
+    self._results_wrapper = tbm_module._TBMResultWrapper()
+    self._results = page_test_results.PageTestResults()
+    self._story_set = None
+    self._threads_to_records_map = None
+    self._tbm_options = options
+
+  @property
+  def model(self):
+    return self._model
+
+  @property
+  def renderer_thread(self):
+    return self._renderer_thread
+
+  @property
+  def foo_thread(self):
+    return self._foo_thread
+
+  @property
+  def threads_to_records_map(self):
+    return self._threads_to_records_map
+
+  @property
+  def results(self):
+    return self._results
+
+  def AddInteraction(self, thread, marker='', ts=0, duration=5):
+    assert thread in (self._renderer_thread, self._foo_thread)
+    thread.async_slices.append(async_slice.AsyncSlice(
+        'category', marker, timestamp=ts, duration=duration,
+        start_thread=self._renderer_thread, end_thread=self._renderer_thread,
+        thread_start=ts, thread_duration=duration))
+
+  def FinalizeImport(self):
+    self._model.FinalizeImport()
+    self._threads_to_records_map = (
+      tbm_module._GetRendererThreadsToInteractionRecordsMap(self._model))
+    self._story_set = story.StorySet(base_dir=os.path.dirname(__file__))
+    self._story_set.AddStory(page_module.Page(
+        'http://www.bar.com/', self._story_set, self._story_set.base_dir))
+    self._results.WillRunPage(self._story_set.stories[0])
+
+  def AddResults(self):
+    all_metrics = self._tbm_options.GetLegacyTimelineBasedMetrics()
+
+    for thread, records in self._threads_to_records_map.iteritems():
+      # pylint: disable=protected-access
+      metric = tbm_module._TimelineBasedMetrics(
+          self._model, thread, records, self._results_wrapper, all_metrics)
+      metric.AddResults(self._results)
+
+    for metric in all_metrics:
+      metric.AddWholeTraceResults(self._model, self._results)
+
+    self._results.DidRunPage(self._story_set.stories[0])
+
+
+class TimelineBasedMetricsTests(unittest.TestCase):
+
+  def setUp(self):
+    self.actual_get_all_tbm_metrics = (
+        tbm_module._GetAllLegacyTimelineBasedMetrics)
+    self._options = tbm_module.Options()
+    self._options.SetLegacyTimelineBasedMetrics(
+        (FakeSmoothMetric(), FakeLoadingMetric(), FakeStartupMetric()))
+
+  def tearDown(self):
+    tbm_module._GetAllLegacyTimelineBasedMetrics = (
+        self.actual_get_all_tbm_metrics)
+
+  def testGetRendererThreadsToInteractionRecordsMap(self):
+    d = TimelineBasedMetricTestData(self._options)
+    # Insert 2 interaction records to renderer_thread and 1 to foo_thread
+    d.AddInteraction(d.renderer_thread, ts=0, duration=20,
+                     marker='Interaction.LogicalName1')
+    d.AddInteraction(d.renderer_thread, ts=25, duration=5,
+                     marker='Interaction.LogicalName2')
+    d.AddInteraction(d.foo_thread, ts=50, duration=15,
+                     marker='Interaction.LogicalName3')
+    d.FinalizeImport()
+
+    self.assertEquals(2, len(d.threads_to_records_map))
+
+    # Assert the 2 interaction records of renderer_thread are in the map.
+    self.assertIn(d.renderer_thread, d.threads_to_records_map)
+    interactions = d.threads_to_records_map[d.renderer_thread]
+    self.assertEquals(2, len(interactions))
+    self.assertEquals(0, interactions[0].start)
+    self.assertEquals(20, interactions[0].end)
+
+    self.assertEquals(25, interactions[1].start)
+    self.assertEquals(30, interactions[1].end)
+
+    # Assert the 1 interaction records of foo_thread is in the map.
+    self.assertIn(d.foo_thread, d.threads_to_records_map)
+    interactions = d.threads_to_records_map[d.foo_thread]
+    self.assertEquals(1, len(interactions))
+    self.assertEquals(50, interactions[0].start)
+    self.assertEquals(65, interactions[0].end)
+
+  def testAddResults(self):
+    d = TimelineBasedMetricTestData(self._options)
+    d.AddInteraction(d.renderer_thread, ts=0, duration=20,
+                     marker='Interaction.LogicalName1')
+    d.AddInteraction(d.foo_thread, ts=25, duration=5,
+                     marker='Interaction.LogicalName2')
+    d.FinalizeImport()
+    d.AddResults()
+    self.assertEquals(1, len(d.results.FindAllPageSpecificValuesFromIRNamed(
+        'LogicalName1', 'FakeSmoothMetric')))
+    self.assertEquals(1, len(d.results.FindAllPageSpecificValuesFromIRNamed(
+        'LogicalName2', 'FakeLoadingMetric')))
+    self.assertEquals(1, len(d.results.FindAllPageSpecificValuesNamed(
+        'FakeStartupMetric')))
+
+  def testDuplicateInteractionsInDifferentThreads(self):
+    d = TimelineBasedMetricTestData(self._options)
+    d.AddInteraction(d.renderer_thread, ts=10, duration=5,
+                     marker='Interaction.LogicalName/repeatable')
+    d.AddInteraction(d.foo_thread, ts=20, duration=5,
+                     marker='Interaction.LogicalName')
+    self.assertRaises(tbm_module.InvalidInteractions, d.FinalizeImport)
+
+  def testDuplicateRepeatableInteractionsInDifferentThreads(self):
+    d = TimelineBasedMetricTestData(self._options)
+    d.AddInteraction(d.renderer_thread, ts=10, duration=5,
+                     marker='Interaction.LogicalName/repeatable')
+    d.AddInteraction(d.foo_thread, ts=20, duration=5,
+                     marker='Interaction.LogicalName/repeatable')
+    self.assertRaises(tbm_module.InvalidInteractions, d.FinalizeImport)
+
+  def testDuplicateUnrepeatableInteractionsInSameThread(self):
+    d = TimelineBasedMetricTestData(self._options)
+    d.AddInteraction(d.renderer_thread, ts=10, duration=5,
+                     marker='Interaction.LogicalName')
+    d.AddInteraction(d.renderer_thread, ts=20, duration=5,
+                     marker='Interaction.LogicalName')
+    d.FinalizeImport()
+    self.assertRaises(tbm_module.InvalidInteractions, d.AddResults)
+
+  def testDuplicateRepeatableInteractions(self):
+    d = TimelineBasedMetricTestData(self._options)
+    d.AddInteraction(d.renderer_thread, ts=10, duration=5,
+                     marker='Interaction.LogicalName/repeatable')
+    d.AddInteraction(d.renderer_thread, ts=20, duration=5,
+                     marker='Interaction.LogicalName/repeatable')
+    d.FinalizeImport()
+    d.AddResults()
+    self.assertEquals(1, len(d.results.pages_that_succeeded))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/timeline_based_page_test.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/timeline_based_page_test.py
new file mode 100644
index 0000000..5413bb7
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/timeline_based_page_test.py
@@ -0,0 +1,28 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.page import legacy_page_test
+
+class TimelineBasedPageTest(legacy_page_test.LegacyPageTest):
+  """Page test that collects metrics with TimelineBasedMeasurement.
+
+  WillRunStory(), Measure() and DidRunStory() are all done in story_runner
+  explicitly. We still need this wrapper around PageTest because it executes
+  some browser related functions in the parent class, which is needed by
+  Timeline Based Measurement benchmarks. This class will be removed after
+  page_test's hooks are fully removed.
+  """
+  def __init__(self, tbm):
+    super(TimelineBasedPageTest, self).__init__()
+    self._measurement = tbm
+
+  @property
+  def measurement(self):
+    return self._measurement
+
+  def ValidateAndMeasurePage(self, page, tab, results):
+    """Collect all possible metrics and added them to results."""
+    # Measurement is done explicitly in story_runner for timeline based page
+    # test.
+    pass
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/timeline_based_page_test_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/timeline_based_page_test_unittest.py
new file mode 100644
index 0000000..0757250
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/timeline_based_page_test_unittest.py
@@ -0,0 +1,177 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry import decorators
+from telemetry.page import page as page_module
+from telemetry.testing import browser_test_case
+from telemetry.testing import options_for_unittests
+from telemetry.testing import page_test_test_case
+from telemetry.timeline import chrome_trace_category_filter
+from telemetry.util import wpr_modes
+from telemetry.web_perf import timeline_based_measurement as tbm_module
+from telemetry.web_perf.metrics import gpu_timeline
+from telemetry.web_perf.metrics import smoothness
+
+class TestTimelinebasedMeasurementPage(page_module.Page):
+
+  def __init__(self, ps, base_dir, trigger_animation=False,
+               trigger_jank=False, trigger_slow=False,
+               trigger_scroll_gesture=False):
+    super(TestTimelinebasedMeasurementPage, self).__init__(
+        'file://interaction_enabled_page.html', ps, base_dir)
+    self._trigger_animation = trigger_animation
+    self._trigger_jank = trigger_jank
+    self._trigger_slow = trigger_slow
+    self._trigger_scroll_gesture = trigger_scroll_gesture
+
+  def RunPageInteractions(self, action_runner):
+    if self._trigger_animation:
+      action_runner.TapElement('#animating-button')
+      action_runner.WaitForJavaScriptCondition('window.animationDone')
+    if self._trigger_jank:
+      action_runner.TapElement('#jank-button')
+      action_runner.WaitForJavaScriptCondition('window.jankScriptDone')
+    if self._trigger_slow:
+      action_runner.TapElement('#slow-button')
+      action_runner.WaitForJavaScriptCondition('window.slowScriptDone')
+    if self._trigger_scroll_gesture:
+      with action_runner.CreateGestureInteraction('Scroll'):
+        action_runner.ScrollPage()
+
+
+class TimelineBasedPageTestTest(page_test_test_case.PageTestTestCase):
+
+  def setUp(self):
+    browser_test_case.teardown_browser()
+    self._options = options_for_unittests.GetCopy()
+    self._options.browser_options.wpr_mode = wpr_modes.WPR_OFF
+
+  # This test is flaky when run in parallel on the mac: crbug.com/426676
+  # Also, fails on android: crbug.com/437057, and chromeos: crbug.com/483212
+  @decorators.Disabled('android', 'mac', 'chromeos')
+  @decorators.Disabled('win')  # catapult/issues/2282
+  @decorators.Isolated  # Needed because of py_trace_event
+  def testSmoothnessTimelineBasedMeasurementForSmoke(self):
+    ps = self.CreateEmptyPageSet()
+    ps.AddStory(TestTimelinebasedMeasurementPage(
+        ps, ps.base_dir, trigger_animation=True))
+
+    options = tbm_module.Options()
+    options.SetLegacyTimelineBasedMetrics([smoothness.SmoothnessMetric()])
+    tbm = tbm_module.TimelineBasedMeasurement(options)
+    results = self.RunMeasurement(tbm, ps, options=self._options)
+
+    self.assertEquals(0, len(results.failures))
+    v = results.FindAllPageSpecificValuesFromIRNamed(
+        'CenterAnimation', 'frame_time_discrepancy')
+    self.assertEquals(len(v), 1)
+    v = results.FindAllPageSpecificValuesFromIRNamed(
+        'DrawerAnimation', 'frame_time_discrepancy')
+    self.assertEquals(len(v), 1)
+
+  # This test should eventually work on all platforms, but currently this
+  # this metric is flaky on desktop: crbug.com/453131
+  # https://github.com/catapult-project/catapult/issues/3099 (Android)
+  @decorators.Disabled('all')
+  def testGPUTimesTimelineBasedMeasurementForSmoke(self):
+    ps = self.CreateEmptyPageSet()
+    ps.AddStory(TestTimelinebasedMeasurementPage(
+        ps, ps.base_dir, trigger_animation=True))
+
+    cat_filter = chrome_trace_category_filter.ChromeTraceCategoryFilter(
+        'disabled-by-default-gpu.service')
+    tbm_option = tbm_module.Options(overhead_level=cat_filter)
+    tbm_option.SetLegacyTimelineBasedMetrics([gpu_timeline.GPUTimelineMetric()])
+    tbm = tbm_module.TimelineBasedMeasurement(tbm_option)
+    results = self.RunMeasurement(tbm, ps, options=self._options)
+
+    self.assertEquals(0, len(results.failures))
+    v = results.FindAllPageSpecificValuesFromIRNamed(
+        'CenterAnimation', 'browser_compositor_max_cpu_time')
+    self.assertEquals(len(v), 1)
+    self.assertGreater(v[0].value, 0)
+    v = results.FindAllPageSpecificValuesFromIRNamed(
+        'DrawerAnimation', 'browser_compositor_max_cpu_time')
+    self.assertEquals(len(v), 1)
+    self.assertGreater(v[0].value, 0)
+
+  # win: crbug.com/520781, chromeos: crbug.com/483212.
+  @decorators.Disabled('win', 'chromeos')
+  @decorators.Isolated  # Needed because of py_trace_event
+  def testTimelineBasedMeasurementGestureAdjustmentSmoke(self):
+    ps = self.CreateEmptyPageSet()
+    ps.AddStory(TestTimelinebasedMeasurementPage(
+        ps, ps.base_dir, trigger_scroll_gesture=True))
+
+    options = tbm_module.Options()
+    options.SetLegacyTimelineBasedMetrics([smoothness.SmoothnessMetric()])
+    tbm = tbm_module.TimelineBasedMeasurement(options)
+    results = self.RunMeasurement(tbm, ps, options=self._options)
+
+    self.assertEquals(0, len(results.failures))
+    v = results.FindAllPageSpecificValuesFromIRNamed(
+        'Gesture_Scroll', 'frame_time_discrepancy')
+    self.assertEquals(len(v), 1)
+
+  # Fails on chromeos: crbug.com/483212
+  @decorators.Disabled('chromeos')
+  @decorators.Isolated
+  def testTBM2ForSmoke(self):
+    ps = self.CreateEmptyPageSet()
+    ps.AddStory(TestTimelinebasedMeasurementPage(ps, ps.base_dir))
+
+    options = tbm_module.Options()
+    options.config.enable_chrome_trace = True
+    options.SetTimelineBasedMetrics(['sampleMetric'])
+
+    tbm = tbm_module.TimelineBasedMeasurement(options)
+    results = self.RunMeasurement(tbm, ps, self._options)
+
+    self.assertEquals(0, len(results.failures))
+    self.assertEquals(2, len(results.value_set))
+    diagnostics = results.value_set[1]['diagnostics']
+    self.assertEquals(1, len(diagnostics))
+    telemetry_info = results.value_set[0]
+    self.assertEquals(telemetry_info['guid'], diagnostics['telemetry'])
+    self.assertEqual('TelemetryInfo', telemetry_info['type'])
+    self.assertEqual('', telemetry_info['benchmarkName'])
+    self.assertEqual('interaction_enabled_page.html',
+                     telemetry_info['storyDisplayName'])
+    self.assertNotIn('storyGroupingKeys', telemetry_info)
+    self.assertEqual(0, telemetry_info['storysetRepeatCounter'])
+    v_foo = results.FindAllPageSpecificValuesNamed('foo_avg')
+    self.assertEquals(len(v_foo), 1)
+    self.assertEquals(v_foo[0].value, 50)
+    self.assertIsNotNone(v_foo[0].page)
+
+  @decorators.Disabled('chromeos')
+  def testFirstPaintMetricSmoke(self):
+    ps = self.CreateEmptyPageSet()
+    ps.AddStory(TestTimelinebasedMeasurementPage(ps, ps.base_dir))
+
+    cat_filter = chrome_trace_category_filter.ChromeTraceCategoryFilter(
+        filter_string='*,blink.console,navigation,blink.user_timing,loading,' +
+        'devtools.timeline,disabled-by-default-blink.debug.layout')
+
+    options = tbm_module.Options(overhead_level=cat_filter)
+    options.SetTimelineBasedMetrics(['loadingMetric'])
+
+    tbm = tbm_module.TimelineBasedMeasurement(options)
+    results = self.RunMeasurement(tbm, ps, self._options)
+
+    self.assertEquals(0, len(results.failures), results.failures)
+    v_ttfcp_max = results.FindAllPageSpecificValuesNamed(
+        'timeToFirstContentfulPaint_max')
+    self.assertEquals(len(v_ttfcp_max), 1)
+    self.assertIsNotNone(v_ttfcp_max[0].page)
+    # TODO(kouhei): enable this once the reference build of telemetry is
+    # updated.
+    #  self.assertGreater(v_ttfcp_max[0].value, 0)
+
+    v_ttfmp_max = results.FindAllPageSpecificValuesNamed(
+       'timeToFirstMeaningfulPaint_max')
+    self.assertEquals(len(v_ttfmp_max), 1)
+    # TODO(ksakamoto): enable this once the reference build of telemetry is
+    # updated.
+    # self.assertIsNotNone(v_ttfmp_max[0].page)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/timeline_interaction_record.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/timeline_interaction_record.py
new file mode 100644
index 0000000..7d6ce26
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/timeline_interaction_record.py
@@ -0,0 +1,235 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import re
+
+from telemetry import decorators
+import telemetry.timeline.bounds as timeline_bounds
+
+# Allows multiple duplicate interactions of the same type
+REPEATABLE = 'repeatable'
+
+FLAGS = [REPEATABLE]
+
+
+class ThreadTimeRangeOverlappedException(Exception):
+  """Exception that can be thrown when computing overlapped thread time range
+  with other events.
+  """
+
+class NoThreadTimeDataException(ThreadTimeRangeOverlappedException):
+  """Exception that can be thrown if there is not sufficient thread time data
+  to compute the overlapped thread time range."""
+
+def IsTimelineInteractionRecord(event_name):
+  return event_name.startswith('Interaction.')
+
+def _AssertFlagsAreValid(flags):
+  assert isinstance(flags, list)
+  for f in flags:
+    if f not in FLAGS:
+      raise AssertionError(
+          'Unrecognized flag for a timeline interaction record: %s' % f)
+
+def GetJavaScriptMarker(label, flags):
+  """Computes the marker string of an interaction record.
+
+  This marker string can be used with JavaScript API console.time()
+  and console.timeEnd() to mark the beginning and end of the
+  interaction record..
+
+  Args:
+    label: The label used to identify the interaction record.
+    flags: the flags for the interaction record see FLAGS above.
+
+  Returns:
+    The interaction record marker string (e.g., Interaction.Label/flag1,flag2).
+
+  Raises:
+    AssertionError: If one or more of the flags is unrecognized.
+  """
+  _AssertFlagsAreValid(flags)
+  marker = 'Interaction.%s' % label
+  if flags:
+    marker += '/%s' % (','.join(flags))
+  return marker
+
+class TimelineInteractionRecord(object):
+  """Represents an interaction that took place during a timeline recording.
+
+  As a page runs, typically a number of different (simulated) user interactions
+  take place. For instance, a user might click a button in a mail app causing a
+  popup to animate in. Then they might press another button that sends data to a
+  server and simultaneously closes the popup without an animation. These are two
+  interactions.
+
+  From the point of view of the page, each interaction might have a different
+  label: ClickComposeButton and SendEmail, for instance. From the point
+  of view of the benchmarking harness, the labels aren't so interesting as what
+  the performance expectations are for that interaction: was it loading
+  resources from the network? was there an animation?
+
+  Determining these things is hard to do, simply by observing the state given to
+  a page from javascript. There are hints, for instance if network requests are
+  sent, or if a CSS animation is pending. But this is by no means a complete
+  story.
+
+  Instead, we expect pages to mark up the timeline what they are doing, with
+  label and flags indicating the semantics of that interaction. This
+  is currently done by pushing markers into the console.time/timeEnd API: this
+  for instance can be issued in JS:
+
+     var str = 'Interaction.SendEmail';
+     console.time(str);
+     setTimeout(function() {
+       console.timeEnd(str);
+     }, 1000);
+
+  When run with perf.measurements.timeline_based_measurement running, this will
+  then cause a TimelineInteractionRecord to be created for this range with
+  all metrics reported for the marked up 1000ms time-range.
+
+  The valid interaction flags are:
+     * repeatable: Allows other interactions to use the same label
+  """
+
+  def __init__(self, label, start, end, async_event=None, flags=None):
+    assert label
+    self._label = label
+    self._start = start
+    self._end = end
+    self._async_event = async_event
+    self._flags = flags if flags is not None else []
+    _AssertFlagsAreValid(self._flags)
+
+  @property
+  def label(self):
+    return self._label
+
+  @property
+  def start(self):
+    return self._start
+
+  @property
+  def end(self):
+    return self._end
+
+  @property
+  def repeatable(self):
+    return REPEATABLE in self._flags
+
+  # TODO(nednguyen): After crbug.com/367175 is marked fixed, we should be able
+  # to get rid of perf.measurements.smooth_gesture_util and make this the only
+  # constructor method for TimelineInteractionRecord.
+  @classmethod
+  def FromAsyncEvent(cls, async_event):
+    """Construct an timeline_interaction_record from an async event.
+    Args:
+      async_event: An instance of
+        telemetry.timeline.async_slices.AsyncSlice
+    """
+    assert async_event.start_thread == async_event.end_thread, (
+        'Start thread of this record\'s async event is not the same as its '
+        'end thread')
+    m = re.match(r'Interaction\.(?P<label>.+?)(/(?P<flags>[^/]+))?$',
+                 async_event.name)
+    assert m, "Async event is not an interaction record."
+    label = m.group('label')
+    flags = m.group('flags').split(',') if m.group('flags') is not None else []
+    return cls(label, async_event.start, async_event.end, async_event, flags)
+
+  @decorators.Cache
+  def GetBounds(self):
+    bounds = timeline_bounds.Bounds()
+    bounds.AddValue(self.start)
+    bounds.AddValue(self.end)
+    return bounds
+
+  def GetOverlappedThreadTimeForSlice(self, timeline_slice):
+    """Get the thread duration of timeline_slice that overlaps with this record.
+
+    There are two cases :
+
+    Case 1: timeline_slice runs in the same thread as the record.
+
+                  |    [       timeline_slice         ]
+      THREAD 1    |                  |                              |
+                  |            record starts                    record ends
+
+                      (relative order in thread time)
+
+      As the thread timestamps in timeline_slice and record are consistent, we
+      simply use them to compute the overlap.
+
+    Case 2: timeline_slice runs in a different thread from the record's.
+
+                  |
+      THREAD 2    |    [       timeline_slice         ]
+                  |
+
+                  |
+      THREAD 1    |               |                               |
+                  |          record starts                      record ends
+
+                      (relative order in wall-time)
+
+      Unlike case 1, thread timestamps of a thread are measured by its
+      thread-specific clock, which is inconsistent with that of the other
+      thread, and thus can't be used to compute the overlapped thread duration.
+      Hence, we use a heuristic to compute the overlap (see
+      _GetOverlappedThreadTimeForSliceInDifferentThread for more details)
+
+    Args:
+      timeline_slice: An instance of telemetry.timeline.slice.Slice
+    """
+    if not self._async_event:
+      raise ThreadTimeRangeOverlappedException(
+          'This record was not constructed from async event')
+    if not self._async_event.has_thread_timestamps:
+      raise NoThreadTimeDataException(
+          'This record\'s async_event does not contain thread time data. '
+          'Event data: %s' % repr(self._async_event))
+    if not timeline_slice.has_thread_timestamps:
+      raise NoThreadTimeDataException(
+          'slice does not contain thread time data')
+
+    if timeline_slice.parent_thread == self._async_event.start_thread:
+      return self._GetOverlappedThreadTimeForSliceInSameThread(
+          timeline_slice)
+    else:
+      return self._GetOverlappedThreadTimeForSliceInDifferentThread(
+          timeline_slice)
+
+  def _GetOverlappedThreadTimeForSliceInSameThread(self, timeline_slice):
+    return timeline_bounds.Bounds.GetOverlap(
+        timeline_slice.thread_start, timeline_slice.thread_end,
+        self._async_event.thread_start, self._async_event.thread_end)
+
+  def _GetOverlappedThreadTimeForSliceInDifferentThread(self, timeline_slice):
+    # In case timeline_slice's parent thread is not the parent thread of the
+    # async slice that issues this record, we assume that events are descheduled
+    # uniformly. The overlap duration in thread time is then computed by
+    # multiplying the overlap wall-time duration of timeline_slice and the
+    # record's async slice with their thread_duration/duration ratios.
+    overlapped_walltime_duration = timeline_bounds.Bounds.GetOverlap(
+        timeline_slice.start, timeline_slice.end,
+        self.start, self.end)
+    if timeline_slice.duration == 0 or self._async_event.duration == 0:
+      return 0
+    timeline_slice_scheduled_ratio = (
+        timeline_slice.thread_duration / float(timeline_slice.duration))
+    record_scheduled_ratio = (
+        self._async_event.thread_duration / float(self._async_event.duration))
+    return (overlapped_walltime_duration * timeline_slice_scheduled_ratio *
+            record_scheduled_ratio)
+
+  def __repr__(self):
+    flags_str = ','.join(self._flags)
+    return ('TimelineInteractionRecord(label=\'%s\', start=%f, end=%f,' +
+            ' flags=%s, async_event=%s)') % (
+                self.label,
+                self.start,
+                self.end,
+                flags_str,
+                repr(self._async_event))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/timeline_interaction_record_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/timeline_interaction_record_unittest.py
new file mode 100644
index 0000000..870b66b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/web_perf/timeline_interaction_record_unittest.py
@@ -0,0 +1,152 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from telemetry.timeline import async_slice
+from telemetry.timeline import model as model_module
+from telemetry.timeline import slice as slice_module
+from telemetry.web_perf import timeline_interaction_record as tir_module
+
+
+class ParseTests(unittest.TestCase):
+
+  def testParse(self):
+    self.assertTrue(tir_module.IsTimelineInteractionRecord(
+        'Interaction.Foo'))
+    self.assertTrue(tir_module.IsTimelineInteractionRecord(
+        'Interaction.Foo/Bar'))
+    self.assertFalse(tir_module.IsTimelineInteractionRecord(
+        'SomethingRandom'))
+
+
+class TimelineInteractionRecordTests(unittest.TestCase):
+
+  def CreateSimpleRecordWithName(self, event_name):
+    s = async_slice.AsyncSlice(
+        'cat', event_name,
+        timestamp=0, duration=200, thread_start=20, thread_duration=100)
+    return tir_module.TimelineInteractionRecord.FromAsyncEvent(s)
+
+  def CreateTestSliceFromTimeRanges(
+      self, parent_thread, time_start, time_end, thread_start, thread_end):
+    duration = time_end - time_start
+    thread_duration = thread_end - thread_start
+    return slice_module.Slice(parent_thread, 'Test', 'foo', time_start,
+                              duration, thread_start, thread_duration)
+
+  def testCreate(self):
+    r = self.CreateSimpleRecordWithName('Interaction.LogicalName')
+    self.assertEquals('LogicalName', r.label)
+    self.assertEquals(False, r.repeatable)
+
+    r = self.CreateSimpleRecordWithName('Interaction.LogicalName/repeatable')
+    self.assertEquals('LogicalName', r.label)
+    self.assertEquals(True, r.repeatable)
+
+    r = self.CreateSimpleRecordWithName(
+        'Interaction.LogicalNameWith/Slash/repeatable')
+    self.assertEquals('LogicalNameWith/Slash', r.label)
+    self.assertEquals(True, r.repeatable)
+
+  def testGetJavaScriptMarker(self):
+    repeatable_marker = tir_module.GetJavaScriptMarker(
+        'MyLabel', [tir_module.REPEATABLE])
+    self.assertEquals('Interaction.MyLabel/repeatable', repeatable_marker)
+
+  def testGetOverlappedThreadTimeForSliceInSameThread(self):
+    # Create a renderer thread.
+    model = model_module.TimelineModel()
+    renderer_main = model.GetOrCreateProcess(1).GetOrCreateThread(2)
+    model.FinalizeImport()
+
+   # Make a record that starts at 30ms and ends at 60ms in thread time.
+    s = async_slice.AsyncSlice(
+        'cat', 'Interaction.Test',
+        timestamp=0, duration=200, start_thread=renderer_main,
+        end_thread=renderer_main, thread_start=30, thread_duration=30)
+    record = tir_module.TimelineInteractionRecord.FromAsyncEvent(s)
+
+    # Non overlapped range on the left of event.
+    s1 = self.CreateTestSliceFromTimeRanges(renderer_main, 0, 100, 10, 20)
+    self.assertEquals(0, record.GetOverlappedThreadTimeForSlice(s1))
+
+    # Non overlapped range on the right of event.
+    s2 = self.CreateTestSliceFromTimeRanges(renderer_main, 0, 100, 70, 90)
+    self.assertEquals(0, record.GetOverlappedThreadTimeForSlice(s2))
+
+    # Overlapped range on the left of event.
+    s3 = self.CreateTestSliceFromTimeRanges(renderer_main, 0, 100, 20, 50)
+    self.assertEquals(20, record.GetOverlappedThreadTimeForSlice(s3))
+
+    # Overlapped range in the middle of event.
+    s4 = self.CreateTestSliceFromTimeRanges(renderer_main, 0, 100, 40, 50)
+    self.assertEquals(10, record.GetOverlappedThreadTimeForSlice(s4))
+
+    # Overlapped range on the left of event.
+    s5 = self.CreateTestSliceFromTimeRanges(renderer_main, 0, 100, 50, 90)
+    self.assertEquals(10, record.GetOverlappedThreadTimeForSlice(s5))
+
+  def testRepr(self):
+    # Create a renderer thread.
+    model = model_module.TimelineModel()
+    renderer_main = model.GetOrCreateProcess(1).GetOrCreateThread(2)
+    model.FinalizeImport()
+
+    s = async_slice.AsyncSlice(
+        'cat', 'Interaction.Test/repeatable',
+        timestamp=0, duration=200, start_thread=renderer_main,
+        end_thread=renderer_main, thread_start=30, thread_duration=30)
+    record = tir_module.TimelineInteractionRecord.FromAsyncEvent(s)
+    expected_repr = (
+        'TimelineInteractionRecord(label=\'Test\', '
+        'start=0.000000, end=200.000000, flags=repeatable, '
+        'async_event=TimelineEvent(name=\'Interaction.Test/repeatable\','
+        ' start=0.000000, duration=200, thread_start=30, thread_duration=30))')
+    self.assertEquals(expected_repr, repr(record))
+
+
+  def testGetOverlappedThreadTimeForSliceInDifferentThread(self):
+    # Create a renderer thread and another thread.
+    model = model_module.TimelineModel()
+    renderer_main = model.GetOrCreateProcess(1).GetOrCreateThread(2)
+    another_thread = model.GetOrCreateProcess(1).GetOrCreateThread(3)
+    model.FinalizeImport()
+
+   # Make a record that starts at 50ms and ends at 150ms in wall time, and is
+   # scheduled 75% of the time (hence thread_duration = 100ms*75% = 75ms).
+    s = async_slice.AsyncSlice(
+        'cat', 'Interaction.Test',
+        timestamp=50, duration=100, start_thread=renderer_main,
+        end_thread=renderer_main, thread_start=55, thread_duration=75)
+    record = tir_module.TimelineInteractionRecord.FromAsyncEvent(s)
+
+    # Non overlapped range on the left of event.
+    s1 = self.CreateTestSliceFromTimeRanges(another_thread, 25, 40, 28, 30)
+    self.assertEquals(0, record.GetOverlappedThreadTimeForSlice(s1))
+
+    # Non overlapped range on the right of event.
+    s2 = self.CreateTestSliceFromTimeRanges(another_thread, 200, 300, 270, 290)
+    self.assertEquals(0, record.GetOverlappedThreadTimeForSlice(s2))
+
+    # Overlapped range on the left of event, and slice is scheduled 50% of the
+    # time.
+    # The overlapped wall-time duration is 50ms.
+    # The overlapped thread-time duration is 50ms * 75% * 50% = 18.75
+    s3 = self.CreateTestSliceFromTimeRanges(another_thread, 0, 100, 20, 70)
+    self.assertEquals(18.75, record.GetOverlappedThreadTimeForSlice(s3))
+
+    # Overlapped range in the middle of event, and slice is scheduled 20% of the
+    # time.
+    # The overlapped wall-time duration is 40ms.
+    # The overlapped thread-time duration is 40ms * 75% * 20% = 6
+    s4 = self.CreateTestSliceFromTimeRanges(another_thread, 100, 140, 120, 128)
+    self.assertEquals(6, record.GetOverlappedThreadTimeForSlice(s4))
+
+    # Overlapped range on the left of event, and slice is scheduled 100% of the
+    # time.
+    # The overlapped wall-time duration is 32ms.
+    # The overlapped thread-time duration is 32ms * 75% * 100% = 24
+    s5 = self.CreateTestSliceFromTimeRanges(another_thread, 118, 170, 118, 170)
+    self.assertEquals(24, record.GetOverlappedThreadTimeForSlice(s5))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/wpr/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/wpr/__init__.py
new file mode 100644
index 0000000..4d6aabb
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/wpr/__init__.py
@@ -0,0 +1,3 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/wpr/archive_info.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/wpr/archive_info.py
new file mode 100644
index 0000000..9ec4cda
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/wpr/archive_info.py
@@ -0,0 +1,219 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import json
+import logging
+import os
+import re
+import shutil
+import tempfile
+import time
+
+from py_utils import cloud_storage  # pylint: disable=import-error
+
+
+_DEFAULT_PLATFORM = 'DEFAULT'
+_ALL_PLATFORMS = ['mac', 'linux', 'android', 'win', _DEFAULT_PLATFORM]
+
+
+def AssertValidCloudStorageBucket(bucket):
+  is_valid = bucket in (None,
+                        cloud_storage.PUBLIC_BUCKET,
+                        cloud_storage.PARTNER_BUCKET,
+                        cloud_storage.INTERNAL_BUCKET)
+  if not is_valid:
+    raise ValueError("Cloud storage privacy bucket %s is invalid" % bucket)
+
+
+class WprArchiveInfo(object):
+  def __init__(self, file_path, data, bucket):
+    AssertValidCloudStorageBucket(bucket)
+    self._file_path = file_path
+    self._base_dir = os.path.dirname(file_path)
+    self._data = data
+    self._bucket = bucket
+    self.temp_target_wpr_file_path = None
+    # Ensure directory exists.
+    if not os.path.exists(self._base_dir):
+      os.makedirs(self._base_dir)
+
+    assert data.get('platform_specific', False), (
+        'Detected old version of archive info json file. Please update to new '
+        'version.')
+
+    self._story_name_to_wpr_file = data['archives']
+
+  @classmethod
+  def FromFile(cls, file_path, bucket):
+    """ Generates an archive_info instance with the given json file. """
+    if os.path.exists(file_path):
+      with open(file_path, 'r') as f:
+        data = json.load(f)
+        return cls(file_path, data, bucket)
+    return cls(file_path, {'archives': {}, 'platform_specific': True}, bucket)
+
+  def DownloadArchivesIfNeeded(self, target_platforms=None):
+    """Downloads archives iff the Archive has a bucket parameter and the user
+    has permission to access the bucket.
+
+    Raises cloud storage Permissions or Credentials error when there is no
+    local copy of the archive and the user doesn't have permission to access
+    the archive's bucket.
+
+    Warns when a bucket is not specified or when the user doesn't have
+    permission to access the archive's bucket but a local copy of the archive
+    exists.
+    """
+    logging.info('Downloading WPR archives. This can take a long time.')
+    start_time = time.time()
+    # If no target platform is set, download all platforms.
+    if target_platforms is None:
+      target_platforms = _ALL_PLATFORMS
+    else:
+      assert isinstance(target_platforms, list), 'Must pass platforms as a list'
+      target_platforms = target_platforms + [_DEFAULT_PLATFORM]
+    # Download all .wpr files.
+    if not self._bucket:
+      logging.warning('Story set in %s has no bucket specified, and '
+                      'cannot be downloaded from cloud_storage.', )
+      return
+    assert 'archives' in self._data, ("Invalid data format in %s. 'archives' "
+                                      "field is needed" % self._file_path)
+
+    def download_if_needed(path):
+      try:
+        cloud_storage.GetIfChanged(path, self._bucket)
+      except (cloud_storage.CredentialsError, cloud_storage.PermissionError):
+        if os.path.exists(path):
+          # If the archive exists, assume the user recorded their own and warn
+          # them that they do not have the proper credentials to download.
+          logging.warning('Need credentials to update WPR archive: %s', path)
+        else:
+          logging.error("You either aren't authenticated or don't have "
+                        "permission to use the archives for this page set."
+                        "\nYou may need to run gsutil config."
+                        "\nYou can find instructions for gsutil config at: "
+                        "http://www.chromium.org/developers/telemetry/"
+                        "upload_to_cloud_storage")
+          raise
+    try:
+      story_archives = self._data['archives']
+      for story in story_archives:
+        for target_platform in target_platforms:
+          if story_archives[story].get(target_platform):
+            archive_path = self._WprFileNameToPath(
+                story_archives[story][target_platform])
+            download_if_needed(archive_path)
+    finally:
+      logging.info('All WPR archives are downloaded, took %s seconds.',
+                   time.time() - start_time)
+
+  def WprFilePathForStory(self, story, target_platform=_DEFAULT_PLATFORM):
+    if self.temp_target_wpr_file_path:
+      return self.temp_target_wpr_file_path
+
+    wpr_file = self._story_name_to_wpr_file.get(story.display_name, None)
+    if wpr_file is None and hasattr(story, 'url'):
+      # Some old pages always use the URL to identify a page rather than the
+      # display_name, so try to look for that.
+      wpr_file = self._story_name_to_wpr_file.get(story.url, None)
+    if wpr_file:
+      if target_platform in wpr_file:
+        return self._WprFileNameToPath(wpr_file[target_platform])
+      return self._WprFileNameToPath(wpr_file[_DEFAULT_PLATFORM])
+    return None
+
+  def AddNewTemporaryRecording(self, temp_wpr_file_path=None):
+    if temp_wpr_file_path is None:
+      temp_wpr_file_handle, temp_wpr_file_path = tempfile.mkstemp()
+      os.close(temp_wpr_file_handle)
+    self.temp_target_wpr_file_path = temp_wpr_file_path
+
+  def AddRecordedStories(self, stories, upload_to_cloud_storage=False,
+                         target_platform=_DEFAULT_PLATFORM):
+    if not stories:
+      os.remove(self.temp_target_wpr_file_path)
+      return
+
+    (target_wpr_file, target_wpr_file_path) = self._NextWprFileName()
+    for story in stories:
+      # Check to see if the platform has been manually overrided.
+      if not story.platform_specific:
+        current_target_platform = _DEFAULT_PLATFORM
+      else:
+        current_target_platform = target_platform
+      self._SetWprFileForStory(
+          story.display_name, target_wpr_file, current_target_platform)
+    shutil.move(self.temp_target_wpr_file_path, target_wpr_file_path)
+
+    # Update the hash file.
+    target_wpr_file_hash = cloud_storage.CalculateHash(target_wpr_file_path)
+    with open(target_wpr_file_path + '.sha1', 'wb') as f:
+      f.write(target_wpr_file_hash)
+      f.flush()
+
+    self._WriteToFile()
+
+    # Upload to cloud storage
+    if upload_to_cloud_storage:
+      if not self._bucket:
+        logging.warning('StorySet must have bucket specified to upload '
+                        'stories to cloud storage.')
+        return
+      try:
+        cloud_storage.Insert(self._bucket, target_wpr_file_hash,
+                             target_wpr_file_path)
+      except cloud_storage.CloudStorageError, e:
+        logging.warning('Failed to upload wpr file %s to cloud storage. '
+                        'Error:%s' % target_wpr_file_path, e)
+
+  def _WriteToFile(self):
+    """Writes the metadata into the file passed as constructor parameter."""
+    metadata = dict()
+    metadata['description'] = (
+        'Describes the Web Page Replay archives for a story set. '
+        'Don\'t edit by hand! Use record_wpr for updating.')
+    metadata['archives'] = self._story_name_to_wpr_file.copy()
+    metadata['platform_specific'] = True
+
+    with open(self._file_path, 'w') as f:
+      json.dump(metadata, f, indent=4, sort_keys=True, separators=(',', ': '))
+      f.flush()
+
+  def _WprFileNameToPath(self, wpr_file):
+    return os.path.abspath(os.path.join(self._base_dir, wpr_file))
+
+  def _NextWprFileName(self):
+    """Creates a new file name for a wpr archive file."""
+    # The names are of the format "some_thing_number.wpr". Read the numbers.
+    highest_number = -1
+    base = None
+    wpr_files = []
+    for story in self._data['archives']:
+      for p in self._data['archives'][story]:
+        wpr_files.append(self._data['archives'][story][p])
+
+    for wpr_file in wpr_files:
+      match = re.match(r'(?P<BASE>.*)_(?P<NUMBER>[0-9]+)\.wpr', wpr_file)
+      if not match:
+        raise Exception('Illegal wpr file name ' + wpr_file)
+      highest_number = max(int(match.groupdict()['NUMBER']), highest_number)
+      if base and match.groupdict()['BASE'] != base:
+        raise Exception('Illegal wpr file name ' + wpr_file +
+                        ', doesn\'t begin with ' + base)
+      base = match.groupdict()['BASE']
+    if not base:
+      # If we're creating a completely new info file, use the base name of the
+      # story set file.
+      base = os.path.splitext(os.path.basename(self._file_path))[0]
+    new_filename = '%s_%03d.wpr' % (base, highest_number + 1)
+    return new_filename, self._WprFileNameToPath(new_filename)
+
+  def _SetWprFileForStory(self, story_name, wpr_file, target_platform):
+    """For modifying the metadata when we're going to record a new archive."""
+    if story_name not in self._data['archives']:
+      # If there is no other recording we want the first to be the default
+      # until a new default is recorded.
+      self._data['archives'][story_name] = {_DEFAULT_PLATFORM: wpr_file}
+    self._data['archives'][story_name][target_platform] = wpr_file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/telemetry/wpr/archive_info_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/wpr/archive_info_unittest.py
new file mode 100644
index 0000000..1711eda
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/telemetry/wpr/archive_info_unittest.py
@@ -0,0 +1,407 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import json
+import os
+import shutil
+import tempfile
+import unittest
+
+from py_utils import cloud_storage  # pylint: disable=import-error
+
+from telemetry.page import page
+from telemetry.testing import system_stub
+from telemetry.wpr import archive_info
+
+
+class MockPage(page.Page):
+  def __init__(self, url, name=None, platform_specific=False):
+    super(MockPage, self).__init__(url, None, name=name)
+    self._platform_specific = platform_specific
+
+page1 = MockPage('http://www.foo.com/', 'Foo')
+page2 = MockPage('http://www.bar.com/', 'Bar', True)
+page3 = MockPage('http://www.baz.com/', platform_specific=True)
+pageNew1 = MockPage('http://www.new.com/', 'New')
+pageNew2 = MockPage('http://www.newer.com/', 'Newer', True)
+recording1 = 'data_001.wpr'
+recording2 = 'data_002.wpr'
+recording3 = 'data_003.wpr'
+recording4 = 'data_004.wpr'
+recording5 = 'data_005.wpr'
+_DEFAULT_PLATFORM = archive_info._DEFAULT_PLATFORM
+
+default_archives_info_contents_dict = {
+    "platform_specific": True,
+    "archives": {
+        "Foo": {
+            _DEFAULT_PLATFORM: recording1
+        },
+        "Bar": {
+            _DEFAULT_PLATFORM: recording2
+        },
+        "http://www.baz.com/": {
+            _DEFAULT_PLATFORM: recording1,
+            "win": recording2,
+            "mac": recording3,
+            "linux": recording4,
+            "android": recording5
+        }
+    }
+}
+
+default_archive_info_contents = json.dumps(default_archives_info_contents_dict)
+default_wpr_files = [
+    'data_001.wpr', 'data_002.wpr', 'data_003.wpr', 'data_004.wpr',
+    'data_005.wpr']
+_BASE_ARCHIVE = {
+        u'platform_specific': True,
+        u'description': (u'Describes the Web Page Replay archives for a'
+                         u' story set. Don\'t edit by hand! Use record_wpr for'
+                         u' updating.'),
+        u'archives': {},
+}
+
+
+class WprArchiveInfoTest(unittest.TestCase):
+  def setUp(self):
+    self.tmp_dir = tempfile.mkdtemp()
+    # Set file for the metadata.
+    self.story_set_archive_info_file = os.path.join(
+        self.tmp_dir, 'info.json')
+    self.overrides = system_stub.Override(archive_info, ['cloud_storage'])
+
+  def tearDown(self):
+    shutil.rmtree(self.tmp_dir)
+    self.overrides.Restore()
+
+  def createArchiveInfo(
+      self, archive_data=default_archive_info_contents,
+      cloud_storage_bucket=cloud_storage.PUBLIC_BUCKET, wpr_files=None):
+
+    # Cannot set lists as a default parameter, so doing it this way.
+    if wpr_files is None:
+      wpr_files = default_wpr_files
+
+    with open(self.story_set_archive_info_file, 'w') as f:
+      f.write(archive_data)
+
+    assert isinstance(wpr_files, list)
+    for wpr_file in wpr_files:
+      assert isinstance(wpr_file, basestring)
+      with open(os.path.join(self.tmp_dir, wpr_file), 'w') as f:
+        f.write(archive_data)
+    return archive_info.WprArchiveInfo.FromFile(
+        self.story_set_archive_info_file, cloud_storage_bucket)
+
+  def testInitNotPlatformSpecific(self):
+    with open(self.story_set_archive_info_file, 'w') as f:
+      f.write('{}')
+    with self.assertRaises(AssertionError):
+      self.createArchiveInfo(archive_data='{}')
+
+
+  def testDownloadArchivesIfNeededAllNeeded(self):
+    test_archive_info = self.createArchiveInfo()
+    cloud_storage_stub = self.overrides.cloud_storage
+    # Second hash doesn't match, need to fetch it.
+    cloud_storage_stub.SetRemotePathsForTesting(
+        {cloud_storage.PUBLIC_BUCKET: {recording1: "dummyhash1_old",
+                                       recording2: "dummyhash2_old",
+                                       recording3: "dummyhash3_old",
+                                       recording4: "dummyhash4_old"}})
+    cloud_storage_stub.SetCalculatedHashesForTesting(
+        {os.path.join(self.tmp_dir, recording1): "dummyhash1",
+         os.path.join(self.tmp_dir, recording2): "dummyhash2",
+         os.path.join(self.tmp_dir, recording3): "dummyhash3",
+         os.path.join(self.tmp_dir, recording4): "dummyhash4",})
+
+    test_archive_info.DownloadArchivesIfNeeded()
+    self.assertItemsEqual(cloud_storage_stub.downloaded_files,
+                          [recording1, recording2, recording3, recording4])
+
+
+  def testDownloadArchivesIfNeededOneNeeded(self):
+    test_archive_info = self.createArchiveInfo()
+    cloud_storage_stub = self.overrides.cloud_storage
+    # Second hash doesn't match, need to fetch it.
+    cloud_storage_stub.SetRemotePathsForTesting(
+        {cloud_storage.PUBLIC_BUCKET: {recording1: "dummyhash1_old",
+                                       recording2: "dummyhash2",
+                                       recording3: "dummyhash3",
+                                       recording4: "dummyhash4"}})
+    cloud_storage_stub.SetCalculatedHashesForTesting(
+        {os.path.join(self.tmp_dir, recording1): "dummyhash1",
+         os.path.join(self.tmp_dir, recording2): "dummyhash2",
+         os.path.join(self.tmp_dir, recording3): "dummyhash3",
+         os.path.join(self.tmp_dir, recording4): "dummyhash4",})
+    test_archive_info.DownloadArchivesIfNeeded()
+    self.assertItemsEqual(cloud_storage_stub.downloaded_files, [recording1])
+
+  def testDownloadArchivesIfNeededNonDefault(self):
+    data = {
+        'platform_specific': True,
+        'archives': {
+            'http://www.baz.com/': {
+                _DEFAULT_PLATFORM: 'data_001.wpr',
+                'win': 'data_002.wpr',
+                'linux': 'data_004.wpr',
+                'mac': 'data_003.wpr',
+                'android': 'data_005.wpr'},
+            'Foo': {_DEFAULT_PLATFORM: 'data_003.wpr'},
+            'Bar': {_DEFAULT_PLATFORM: 'data_002.wpr'}
+        }
+    }
+    test_archive_info = self.createArchiveInfo(
+        archive_data=json.dumps(data, separators=(',', ': ')))
+    cloud_storage_stub = self.overrides.cloud_storage
+    # Second hash doesn't match, need to fetch it.
+    cloud_storage_stub.SetRemotePathsForTesting(
+        {cloud_storage.PUBLIC_BUCKET: {recording1: "dummyhash1_old",
+                                       recording2: "dummyhash2",
+                                       recording3: "dummyhash3",
+                                       recording4: "dummyhash4_old"}})
+    cloud_storage_stub.SetCalculatedHashesForTesting(
+        {os.path.join(self.tmp_dir, recording1): "dummyhash1",
+         os.path.join(self.tmp_dir, recording2): "dummyhash2",
+         os.path.join(self.tmp_dir, recording3): "dummyhash3",
+         os.path.join(self.tmp_dir, recording4): "dummyhash4",})
+    test_archive_info.DownloadArchivesIfNeeded(target_platforms=['linux'])
+    self.assertItemsEqual(cloud_storage_stub.downloaded_files,
+                          [recording1, recording4])
+
+  def testDownloadArchivesIfNeededNoBucket(self):
+    test_archive_info = self.createArchiveInfo(cloud_storage_bucket=None)
+    cloud_storage_stub = self.overrides.cloud_storage
+    # Second hash doesn't match, need to fetch it.
+    cloud_storage_stub.SetRemotePathsForTesting(
+        {cloud_storage.PUBLIC_BUCKET: {recording1: "dummyhash1",
+                                       recording2: "dummyhash2",
+                                       recording3: "dummyhash3",
+                                       recording4: "dummyhash4_old"}})
+    cloud_storage_stub.SetCalculatedHashesForTesting(
+        {os.path.join(self.tmp_dir, recording1): "dummyhash1",
+         os.path.join(self.tmp_dir, recording2): "dummyhash2",
+         os.path.join(self.tmp_dir, recording3): "dummyhash3",
+         os.path.join(self.tmp_dir, recording4): "dummyhash4",})
+    test_archive_info.DownloadArchivesIfNeeded()
+    self.assertItemsEqual(cloud_storage_stub.downloaded_files, [])
+
+  def testWprFilePathForStoryDefault(self):
+    test_archive_info = self.createArchiveInfo()
+    self.assertEqual(
+        test_archive_info.WprFilePathForStory(page1),
+        os.path.join(self.tmp_dir, recording1))
+    self.assertEqual(
+        test_archive_info.WprFilePathForStory(page2),
+        os.path.join(self.tmp_dir, recording2))
+    self.assertEqual(
+        test_archive_info.WprFilePathForStory(page3),
+        os.path.join(self.tmp_dir, recording1))
+
+  def testWprFilePathForStoryMac(self):
+    test_archive_info = self.createArchiveInfo()
+    self.assertEqual(test_archive_info.WprFilePathForStory(page1, 'mac'),
+                     os.path.join(self.tmp_dir, recording1))
+    self.assertEqual(test_archive_info.WprFilePathForStory(page2, 'mac'),
+                     os.path.join(self.tmp_dir, recording2))
+    self.assertEqual(test_archive_info.WprFilePathForStory(page3, 'mac'),
+                     os.path.join(self.tmp_dir, recording3))
+
+  def testWprFilePathForStoryWin(self):
+    test_archive_info = self.createArchiveInfo()
+    self.assertEqual(test_archive_info.WprFilePathForStory(page1, 'win'),
+                     os.path.join(self.tmp_dir, recording1))
+    self.assertEqual(test_archive_info.WprFilePathForStory(page2, 'win'),
+                     os.path.join(self.tmp_dir, recording2))
+    self.assertEqual(test_archive_info.WprFilePathForStory(page3, 'win'),
+                     os.path.join(self.tmp_dir, recording2))
+
+  def testWprFilePathForStoryAndroid(self):
+    test_archive_info = self.createArchiveInfo()
+    self.assertEqual(test_archive_info.WprFilePathForStory(page1, 'android'),
+                     os.path.join(self.tmp_dir, recording1))
+    self.assertEqual(test_archive_info.WprFilePathForStory(page2, 'android'),
+                     os.path.join(self.tmp_dir, recording2))
+    self.assertEqual(test_archive_info.WprFilePathForStory(page3, 'android'),
+                     os.path.join(self.tmp_dir, recording5))
+
+  def testWprFilePathForStoryLinux(self):
+    test_archive_info = self.createArchiveInfo()
+    self.assertEqual(test_archive_info.WprFilePathForStory(page1, 'linux'),
+                     os.path.join(self.tmp_dir, recording1))
+    self.assertEqual(test_archive_info.WprFilePathForStory(page2, 'linux'),
+                     os.path.join(self.tmp_dir, recording2))
+    self.assertEqual(test_archive_info.WprFilePathForStory(page3, 'linux'),
+                     os.path.join(self.tmp_dir, recording4))
+
+  def testWprFilePathForStoryBadStory(self):
+    test_archive_info = self.createArchiveInfo()
+    self.assertIsNone(test_archive_info.WprFilePathForStory(pageNew1))
+
+
+  def testAddRecordedStoriesNoStories(self):
+    test_archive_info = self.createArchiveInfo()
+    old_data = test_archive_info._data.copy()
+    test_archive_info.AddNewTemporaryRecording()
+    test_archive_info.AddRecordedStories(None)
+    self.assertDictEqual(old_data, test_archive_info._data)
+
+  def assertWprFileDoesNotExist(self, file_name):
+    sha_file = file_name + '.sha1'
+    self.assertFalse(os.path.isfile(os.path.join(self.tmp_dir, sha_file)))
+    self.assertFalse(os.path.isfile(os.path.join(self.tmp_dir, file_name)))
+
+  def assertWprFileDoesExist(self, file_name):
+    sha_file = file_name + '.sha1'
+    self.assertTrue(os.path.isfile(os.path.join(self.tmp_dir, sha_file)))
+    self.assertTrue(os.path.isfile(os.path.join(self.tmp_dir, file_name)))
+
+  def testAddRecordedStoriesDefault(self):
+    test_archive_info = self.createArchiveInfo()
+    self.assertWprFileDoesNotExist('data_006.wpr')
+
+    new_temp_recording = os.path.join(self.tmp_dir, 'recording.wpr')
+    expected_archive_file_path = os.path.join(self.tmp_dir, 'data_006.wpr')
+    hash_dictionary = {expected_archive_file_path: 'filehash'}
+    cloud_storage_stub = self.overrides.cloud_storage
+    cloud_storage_stub.SetCalculatedHashesForTesting(hash_dictionary)
+
+    with open(new_temp_recording, 'w') as f:
+      f.write('wpr data')
+
+    test_archive_info.AddNewTemporaryRecording(new_temp_recording)
+    test_archive_info.AddRecordedStories([page2, page3])
+
+    with open(self.story_set_archive_info_file, 'r') as f:
+      archive_file_contents = json.load(f)
+
+    expected_archive_contents = _BASE_ARCHIVE.copy()
+    expected_archive_contents['archives'] = {
+        page1.display_name: {
+            _DEFAULT_PLATFORM: recording1
+        },
+        page2.display_name: {
+            _DEFAULT_PLATFORM: 'data_006.wpr'
+        },
+        page3.display_name: {
+           _DEFAULT_PLATFORM: u'data_006.wpr',
+           'linux': recording4,
+           'mac': recording3,
+           'win': recording2,
+           'android': recording5
+        }
+    }
+
+    self.assertDictEqual(expected_archive_contents, archive_file_contents)
+    # Ensure the saved JSON does not contain trailing spaces.
+    with open(self.story_set_archive_info_file, 'rU') as f:
+      for line in f:
+        self.assertFalse(line.rstrip('\n').endswith(' '))
+    self.assertWprFileDoesExist('data_006.wpr')
+
+  def testAddRecordedStoriesNotDefault(self):
+    test_archive_info = self.createArchiveInfo()
+    self.assertWprFileDoesNotExist('data_006.wpr')
+    new_temp_recording = os.path.join(self.tmp_dir, 'recording.wpr')
+    expected_archive_file_path = os.path.join(self.tmp_dir, 'data_006.wpr')
+    hash_dictionary = {expected_archive_file_path: 'filehash'}
+    cloud_storage_stub = self.overrides.cloud_storage
+    cloud_storage_stub.SetCalculatedHashesForTesting(hash_dictionary)
+
+    with open(new_temp_recording, 'w') as f:
+      f.write('wpr data')
+    test_archive_info.AddNewTemporaryRecording(new_temp_recording)
+    test_archive_info.AddRecordedStories([page2, page3],
+                                         target_platform='android')
+
+    with open(self.story_set_archive_info_file, 'r') as f:
+      archive_file_contents = json.load(f)
+
+    expected_archive_contents = _BASE_ARCHIVE.copy()
+    expected_archive_contents['archives'] = {
+        page1.display_name: {
+            _DEFAULT_PLATFORM: recording1
+        },
+        page2.display_name: {
+            _DEFAULT_PLATFORM: recording2,
+            'android': 'data_006.wpr'
+        },
+        page3.display_name: {
+           _DEFAULT_PLATFORM: recording1,
+           'linux': recording4,
+           'mac': recording3,
+           'win': recording2,
+           'android': 'data_006.wpr'
+        },
+    }
+
+    self.assertDictEqual(expected_archive_contents, archive_file_contents)
+    # Ensure the saved JSON does not contain trailing spaces.
+    with open(self.story_set_archive_info_file, 'rU') as f:
+      for line in f:
+        self.assertFalse(line.rstrip('\n').endswith(' '))
+    self.assertWprFileDoesExist('data_006.wpr')
+
+
+
+  def testAddRecordedStoriesNewPage(self):
+    test_archive_info = self.createArchiveInfo()
+    self.assertWprFileDoesNotExist('data_006.wpr')
+    self.assertWprFileDoesNotExist('data_007.wpr')
+    new_temp_recording = os.path.join(self.tmp_dir, 'recording.wpr')
+    expected_archive_file_path1 = os.path.join(self.tmp_dir, 'data_006.wpr')
+    expected_archive_file_path2 = os.path.join(self.tmp_dir, 'data_007.wpr')
+    hash_dictionary = {
+        expected_archive_file_path1: 'filehash',
+        expected_archive_file_path2: 'filehash2'
+    }
+    cloud_storage_stub = self.overrides.cloud_storage
+    cloud_storage_stub.SetCalculatedHashesForTesting(hash_dictionary)
+
+    with open(new_temp_recording, 'w') as f:
+      f.write('wpr data')
+    test_archive_info.AddNewTemporaryRecording(new_temp_recording)
+    test_archive_info.AddRecordedStories([pageNew1])
+
+    with open(new_temp_recording, 'w') as f:
+      f.write('wpr data2')
+
+    test_archive_info.AddNewTemporaryRecording(new_temp_recording)
+    test_archive_info.AddRecordedStories([pageNew2], target_platform='android')
+
+    with open(self.story_set_archive_info_file, 'r') as f:
+      archive_file_contents = json.load(f)
+
+
+    expected_archive_contents = _BASE_ARCHIVE.copy()
+    expected_archive_contents['archives'] = {
+        page1.display_name: {
+            _DEFAULT_PLATFORM: recording1
+        },
+        page2.display_name: {
+            _DEFAULT_PLATFORM: recording2,
+        },
+        page3.display_name: {
+           _DEFAULT_PLATFORM: recording1,
+           'linux': recording4,
+           'mac': recording3,
+           'win': recording2,
+           'android': recording5
+        },
+        pageNew1.display_name: {
+          _DEFAULT_PLATFORM: 'data_006.wpr'
+        },
+        pageNew2.display_name: {
+          _DEFAULT_PLATFORM: 'data_007.wpr',
+          'android': 'data_007.wpr'
+        }
+    }
+
+    self.assertDictEqual(expected_archive_contents, archive_file_contents)
+    # Ensure the saved JSON does not contain trailing spaces.
+    with open(self.story_set_archive_info_file, 'rU') as f:
+      for line in f:
+        self.assertFalse(line.rstrip('\n').endswith(' '))
+    self.assertWprFileDoesExist('data_006.wpr')
+    self.assertWprFileDoesExist('data_007.wpr')
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/WebKit/LICENSE b/sdk/platform-tools/systrace/catapult/telemetry/third_party/WebKit/LICENSE
new file mode 100644
index 0000000..70bcb8a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/WebKit/LICENSE
@@ -0,0 +1,30 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+//
+// The Chromium Authors can be found at
+// http://src.chromium.org/svn/trunk/src/AUTHORS
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/WebKit/PerformanceTests/resources/jquery.tablesorter.min.js b/sdk/platform-tools/systrace/catapult/telemetry/third_party/WebKit/PerformanceTests/resources/jquery.tablesorter.min.js
new file mode 100644
index 0000000..19d7359
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/WebKit/PerformanceTests/resources/jquery.tablesorter.min.js
@@ -0,0 +1,4 @@
+
+(function($){$.extend({tablesorter:new
+function(){var parsers=[],widgets=[];this.defaults={cssHeader:"header",cssAsc:"headerSortUp",cssDesc:"headerSortDown",cssChildRow:"expand-child",sortInitialOrder:"asc",sortMultiSortKey:"shiftKey",sortForce:null,sortAppend:null,sortLocaleCompare:true,textExtraction:"simple",parsers:{},widgets:[],widgetZebra:{css:["even","odd"]},headers:{},widthFixed:false,cancelSelection:true,sortList:[],headerList:[],dateFormat:"us",decimal:'/\.|\,/g',onRenderHeader:null,selectorHeaders:'thead th',debug:false};function benchmark(s,d){log(s+","+(new Date().getTime()-d.getTime())+"ms");}this.benchmark=benchmark;function log(s){if(typeof console!="undefined"&&typeof console.debug!="undefined"){console.log(s);}else{alert(s);}}function buildParserCache(table,$headers){if(table.config.debug){var parsersDebug="";}if(table.tBodies.length==0)return;var rows=table.tBodies[0].rows;if(rows[0]){var list=[],cells=rows[0].cells,l=cells.length;for(var i=0;i<l;i++){var p=false;if($.metadata&&($($headers[i]).metadata()&&$($headers[i]).metadata().sorter)){p=getParserById($($headers[i]).metadata().sorter);}else if((table.config.headers[i]&&table.config.headers[i].sorter)){p=getParserById(table.config.headers[i].sorter);}if(!p){p=detectParserForColumn(table,rows,-1,i);}if(table.config.debug){parsersDebug+="column:"+i+" parser:"+p.id+"\n";}list.push(p);}}if(table.config.debug){log(parsersDebug);}return list;};function detectParserForColumn(table,rows,rowIndex,cellIndex){var l=parsers.length,node=false,nodeValue=false,keepLooking=true;while(nodeValue==''&&keepLooking){rowIndex++;if(rows[rowIndex]){node=getNodeFromRowAndCellIndex(rows,rowIndex,cellIndex);nodeValue=trimAndGetNodeText(table.config,node);if(table.config.debug){log('Checking if value was empty on row:'+rowIndex);}}else{keepLooking=false;}}for(var i=1;i<l;i++){if(parsers[i].is(nodeValue,table,node)){return parsers[i];}}return parsers[0];}function getNodeFromRowAndCellIndex(rows,rowIndex,cellIndex){return rows[rowIndex].cells[cellIndex];}function trimAndGetNodeText(config,node){return $.trim(getElementText(config,node));}function getParserById(name){var l=parsers.length;for(var i=0;i<l;i++){if(parsers[i].id.toLowerCase()==name.toLowerCase()){return parsers[i];}}return false;}function buildCache(table){if(table.config.debug){var cacheTime=new Date();}var totalRows=(table.tBodies[0]&&table.tBodies[0].rows.length)||0,totalCells=(table.tBodies[0].rows[0]&&table.tBodies[0].rows[0].cells.length)||0,parsers=table.config.parsers,cache={row:[],normalized:[]};for(var i=0;i<totalRows;++i){var c=$(table.tBodies[0].rows[i]),cols=[];if(c.hasClass(table.config.cssChildRow)){cache.row[cache.row.length-1]=cache.row[cache.row.length-1].add(c);continue;}cache.row.push(c);for(var j=0;j<totalCells;++j){cols.push(parsers[j].format(getElementText(table.config,c[0].cells[j]),table,c[0].cells[j]));}cols.push(cache.normalized.length);cache.normalized.push(cols);cols=null;};if(table.config.debug){benchmark("Building cache for "+totalRows+" rows:",cacheTime);}return cache;};function getElementText(config,node){var text="";if(!node)return"";if(!config.supportsTextContent)config.supportsTextContent=node.textContent||false;if(config.textExtraction=="simple"){if(config.supportsTextContent){text=node.textContent;}else{if(node.childNodes[0]&&node.childNodes[0].hasChildNodes()){text=node.childNodes[0].innerHTML;}else{text=node.innerHTML;}}}else{if(typeof(config.textExtraction)=="function"){text=config.textExtraction(node);}else{text=$(node).text();}}return text;}function appendToTable(table,cache){if(table.config.debug){var appendTime=new Date()}var c=cache,r=c.row,n=c.normalized,totalRows=n.length,checkCell=(n[0].length-1),tableBody=$(table.tBodies[0]),rows=[];for(var i=0;i<totalRows;i++){var pos=n[i][checkCell];rows.push(r[pos]);if(!table.config.appender){var l=r[pos].length;for(var j=0;j<l;j++){tableBody[0].appendChild(r[pos][j]);}}}if(table.config.appender){table.config.appender(table,rows);}rows=null;if(table.config.debug){benchmark("Rebuilt table:",appendTime);}applyWidget(table);setTimeout(function(){$(table).trigger("sortEnd");},0);};function buildHeaders(table){if(table.config.debug){var time=new Date();}var meta=($.metadata)?true:false;var header_index=computeTableHeaderCellIndexes(table);$tableHeaders=$(table.config.selectorHeaders,table).each(function(index){this.column=header_index[this.parentNode.rowIndex+"-"+this.cellIndex];this.order=formatSortingOrder(table.config.sortInitialOrder);this.count=this.order;if(checkHeaderMetadata(this)||checkHeaderOptions(table,index))this.sortDisabled=true;if(checkHeaderOptionsSortingLocked(table,index))this.order=this.lockedOrder=checkHeaderOptionsSortingLocked(table,index);if(!this.sortDisabled){var $th=$(this).addClass(table.config.cssHeader);if(table.config.onRenderHeader)table.config.onRenderHeader.apply($th);}table.config.headerList[index]=this;});if(table.config.debug){benchmark("Built headers:",time);log($tableHeaders);}return $tableHeaders;};function computeTableHeaderCellIndexes(t){var matrix=[];var lookup={};var thead=t.getElementsByTagName('THEAD')[0];var trs=thead.getElementsByTagName('TR');for(var i=0;i<trs.length;i++){var cells=trs[i].cells;for(var j=0;j<cells.length;j++){var c=cells[j];var rowIndex=c.parentNode.rowIndex;var cellId=rowIndex+"-"+c.cellIndex;var rowSpan=c.rowSpan||1;var colSpan=c.colSpan||1
+var firstAvailCol;if(typeof(matrix[rowIndex])=="undefined"){matrix[rowIndex]=[];}for(var k=0;k<matrix[rowIndex].length+1;k++){if(typeof(matrix[rowIndex][k])=="undefined"){firstAvailCol=k;break;}}lookup[cellId]=firstAvailCol;for(var k=rowIndex;k<rowIndex+rowSpan;k++){if(typeof(matrix[k])=="undefined"){matrix[k]=[];}var matrixrow=matrix[k];for(var l=firstAvailCol;l<firstAvailCol+colSpan;l++){matrixrow[l]="x";}}}}return lookup;}function checkCellColSpan(table,rows,row){var arr=[],r=table.tHead.rows,c=r[row].cells;for(var i=0;i<c.length;i++){var cell=c[i];if(cell.colSpan>1){arr=arr.concat(checkCellColSpan(table,headerArr,row++));}else{if(table.tHead.length==1||(cell.rowSpan>1||!r[row+1])){arr.push(cell);}}}return arr;};function checkHeaderMetadata(cell){if(($.metadata)&&($(cell).metadata().sorter===false)){return true;};return false;}function checkHeaderOptions(table,i){if((table.config.headers[i])&&(table.config.headers[i].sorter===false)){return true;};return false;}function checkHeaderOptionsSortingLocked(table,i){if((table.config.headers[i])&&(table.config.headers[i].lockedOrder))return table.config.headers[i].lockedOrder;return false;}function applyWidget(table){var c=table.config.widgets;var l=c.length;for(var i=0;i<l;i++){getWidgetById(c[i]).format(table);}}function getWidgetById(name){var l=widgets.length;for(var i=0;i<l;i++){if(widgets[i].id.toLowerCase()==name.toLowerCase()){return widgets[i];}}};function formatSortingOrder(v){if(typeof(v)!="Number"){return(v.toLowerCase()=="desc")?1:0;}else{return(v==1)?1:0;}}function isValueInArray(v,a){var l=a.length;for(var i=0;i<l;i++){if(a[i][0]==v){return true;}}return false;}function setHeadersCss(table,$headers,list,css){$headers.removeClass(css[0]).removeClass(css[1]);var h=[];$headers.each(function(offset){if(!this.sortDisabled){h[this.column]=$(this);}});var l=list.length;for(var i=0;i<l;i++){h[list[i][0]].addClass(css[list[i][1]]);}}function fixColumnWidth(table,$headers){var c=table.config;if(c.widthFixed){var colgroup=$('<colgroup>');$("tr:first td",table.tBodies[0]).each(function(){colgroup.append($('<col>').css('width',$(this).width()));});$(table).prepend(colgroup);};}function updateHeaderSortCount(table,sortList){var c=table.config,l=sortList.length;for(var i=0;i<l;i++){var s=sortList[i],o=c.headerList[s[0]];o.count=s[1];o.count++;}}function multisort(table,sortList,cache){if(table.config.debug){var sortTime=new Date();}var dynamicExp="var sortWrapper = function(a,b) {",l=sortList.length;for(var i=0;i<l;i++){var c=sortList[i][0];var order=sortList[i][1];var s=(table.config.parsers[c].type=="text")?((order==0)?makeSortFunction("text","asc",c):makeSortFunction("text","desc",c)):((order==0)?makeSortFunction("numeric","asc",c):makeSortFunction("numeric","desc",c));var e="e"+i;dynamicExp+="var "+e+" = "+s;dynamicExp+="if("+e+") { return "+e+"; } ";dynamicExp+="else { ";}var orgOrderCol=cache.normalized[0].length-1;dynamicExp+="return a["+orgOrderCol+"]-b["+orgOrderCol+"];";for(var i=0;i<l;i++){dynamicExp+="}; ";}dynamicExp+="return 0; ";dynamicExp+="}; ";if(table.config.debug){benchmark("Evaling expression:"+dynamicExp,new Date());}eval(dynamicExp);cache.normalized.sort(sortWrapper);if(table.config.debug){benchmark("Sorting on "+sortList.toString()+" and dir "+order+" time:",sortTime);}return cache;};function makeSortFunction(type,direction,index){var a="a["+index+"]",b="b["+index+"]";if(type=='text'&&direction=='asc'){return"("+a+" == "+b+" ? 0 : ("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : ("+a+" < "+b+") ? -1 : 1 )));";}else if(type=='text'&&direction=='desc'){return"("+a+" == "+b+" ? 0 : ("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : ("+b+" < "+a+") ? -1 : 1 )));";}else if(type=='numeric'&&direction=='asc'){return"("+a+" === null && "+b+" === null) ? 0 :("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : "+a+" - "+b+"));";}else if(type=='numeric'&&direction=='desc'){return"("+a+" === null && "+b+" === null) ? 0 :("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : "+b+" - "+a+"));";}};function makeSortText(i){return"((a["+i+"] < b["+i+"]) ? -1 : ((a["+i+"] > b["+i+"]) ? 1 : 0));";};function makeSortTextDesc(i){return"((b["+i+"] < a["+i+"]) ? -1 : ((b["+i+"] > a["+i+"]) ? 1 : 0));";};function makeSortNumeric(i){return"a["+i+"]-b["+i+"];";};function makeSortNumericDesc(i){return"b["+i+"]-a["+i+"];";};function sortText(a,b){if(table.config.sortLocaleCompare)return a.localeCompare(b);return((a<b)?-1:((a>b)?1:0));};function sortTextDesc(a,b){if(table.config.sortLocaleCompare)return b.localeCompare(a);return((b<a)?-1:((b>a)?1:0));};function sortNumeric(a,b){return a-b;};function sortNumericDesc(a,b){return b-a;};function getCachedSortType(parsers,i){return parsers[i].type;};this.construct=function(settings){return this.each(function(){if(!this.tHead||!this.tBodies)return;var $this,$document,$headers,cache,config,shiftDown=0,sortOrder;this.config={};config=$.extend(this.config,$.tablesorter.defaults,settings);$this=$(this);$.data(this,"tablesorter",config);$headers=buildHeaders(this);this.config.parsers=buildParserCache(this,$headers);cache=buildCache(this);var sortCSS=[config.cssDesc,config.cssAsc];fixColumnWidth(this);$headers.click(function(e){var totalRows=($this[0].tBodies[0]&&$this[0].tBodies[0].rows.length)||0;if(!this.sortDisabled&&totalRows>0){$this.trigger("sortStart");var $cell=$(this);var i=this.column;this.order=this.count++%2;if(this.lockedOrder)this.order=this.lockedOrder;if(!e[config.sortMultiSortKey]){config.sortList=[];if(config.sortForce!=null){var a=config.sortForce;for(var j=0;j<a.length;j++){if(a[j][0]!=i){config.sortList.push(a[j]);}}}config.sortList.push([i,this.order]);}else{if(isValueInArray(i,config.sortList)){for(var j=0;j<config.sortList.length;j++){var s=config.sortList[j],o=config.headerList[s[0]];if(s[0]==i){o.count=s[1];o.count++;s[1]=o.count%2;}}}else{config.sortList.push([i,this.order]);}};setTimeout(function(){setHeadersCss($this[0],$headers,config.sortList,sortCSS);appendToTable($this[0],multisort($this[0],config.sortList,cache));},1);return false;}}).mousedown(function(){if(config.cancelSelection){this.onselectstart=function(){return false};return false;}});$this.bind("update",function(){var me=this;setTimeout(function(){me.config.parsers=buildParserCache(me,$headers);cache=buildCache(me);},1);}).bind("updateCell",function(e,cell){var config=this.config;var pos=[(cell.parentNode.rowIndex-1),cell.cellIndex];cache.normalized[pos[0]][pos[1]]=config.parsers[pos[1]].format(getElementText(config,cell),cell);}).bind("sorton",function(e,list){$(this).trigger("sortStart");config.sortList=list;var sortList=config.sortList;updateHeaderSortCount(this,sortList);setHeadersCss(this,$headers,sortList,sortCSS);appendToTable(this,multisort(this,sortList,cache));}).bind("appendCache",function(){appendToTable(this,cache);}).bind("applyWidgetId",function(e,id){getWidgetById(id).format(this);}).bind("applyWidgets",function(){applyWidget(this);});if($.metadata&&($(this).metadata()&&$(this).metadata().sortlist)){config.sortList=$(this).metadata().sortlist;}if(config.sortList.length>0){$this.trigger("sorton",[config.sortList]);}applyWidget(this);});};this.addParser=function(parser){var l=parsers.length,a=true;for(var i=0;i<l;i++){if(parsers[i].id.toLowerCase()==parser.id.toLowerCase()){a=false;}}if(a){parsers.push(parser);};};this.addWidget=function(widget){widgets.push(widget);};this.formatFloat=function(s){var i=parseFloat(s);return(isNaN(i))?0:i;};this.formatInt=function(s){var i=parseInt(s);return(isNaN(i))?0:i;};this.isDigit=function(s,config){return/^[-+]?\d*$/.test($.trim(s.replace(/[,.']/g,'')));};this.clearTableBody=function(table){if($.browser.msie){function empty(){while(this.firstChild)this.removeChild(this.firstChild);}empty.apply(table.tBodies[0]);}else{table.tBodies[0].innerHTML="";}};}});$.fn.extend({tablesorter:$.tablesorter.construct});var ts=$.tablesorter;ts.addParser({id:"text",is:function(s){return true;},format:function(s){return $.trim(s.toLocaleLowerCase());},type:"text"});ts.addParser({id:"digit",is:function(s,table){var c=table.config;return $.tablesorter.isDigit(s,c);},format:function(s){return $.tablesorter.formatFloat(s);},type:"numeric"});ts.addParser({id:"currency",is:function(s){return/^[£$€?.]/.test(s);},format:function(s){return $.tablesorter.formatFloat(s.replace(new RegExp(/[£$€]/g),""));},type:"numeric"});ts.addParser({id:"ipAddress",is:function(s){return/^\d{2,3}[\.]\d{2,3}[\.]\d{2,3}[\.]\d{2,3}$/.test(s);},format:function(s){var a=s.split("."),r="",l=a.length;for(var i=0;i<l;i++){var item=a[i];if(item.length==2){r+="0"+item;}else{r+=item;}}return $.tablesorter.formatFloat(r);},type:"numeric"});ts.addParser({id:"url",is:function(s){return/^(https?|ftp|file):\/\/$/.test(s);},format:function(s){return jQuery.trim(s.replace(new RegExp(/(https?|ftp|file):\/\//),''));},type:"text"});ts.addParser({id:"isoDate",is:function(s){return/^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(s);},format:function(s){return $.tablesorter.formatFloat((s!="")?new Date(s.replace(new RegExp(/-/g),"/")).getTime():"0");},type:"numeric"});ts.addParser({id:"percent",is:function(s){return/\%$/.test($.trim(s));},format:function(s){return $.tablesorter.formatFloat(s.replace(new RegExp(/%/g),""));},type:"numeric"});ts.addParser({id:"usLongDate",is:function(s){return s.match(new RegExp(/^[A-Za-z]{3,10}\.? [0-9]{1,2}, ([0-9]{4}|'?[0-9]{2}) (([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(AM|PM)))$/));},format:function(s){return $.tablesorter.formatFloat(new Date(s).getTime());},type:"numeric"});ts.addParser({id:"shortDate",is:function(s){return/\d{1,2}[\/\-]\d{1,2}[\/\-]\d{2,4}/.test(s);},format:function(s,table){var c=table.config;s=s.replace(/\-/g,"/");if(c.dateFormat=="us"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/,"$3/$1/$2");}else if(c.dateFormat=="uk"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/,"$3/$2/$1");}else if(c.dateFormat=="dd/mm/yy"||c.dateFormat=="dd-mm-yy"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{2})/,"$1/$2/$3");}return $.tablesorter.formatFloat(new Date(s).getTime());},type:"numeric"});ts.addParser({id:"time",is:function(s){return/^(([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(am|pm)))$/.test(s);},format:function(s){return $.tablesorter.formatFloat(new Date("2000/01/01 "+s).getTime());},type:"numeric"});ts.addParser({id:"metadata",is:function(s){return false;},format:function(s,table,cell){var c=table.config,p=(!c.parserMetadataName)?'sortValue':c.parserMetadataName;return $(cell).metadata()[p];},type:"numeric"});ts.addWidget({id:"zebra",format:function(table){if(table.config.debug){var time=new Date();}var $tr,row=-1,odd;$("tr:visible",table.tBodies[0]).each(function(i){$tr=$(this);if(!$tr.hasClass(table.config.cssChildRow))row++;odd=(row%2==0);$tr.removeClass(table.config.widgetZebra.css[odd?0:1]).addClass(table.config.widgetZebra.css[odd?1:0])});if(table.config.debug){$.tablesorter.benchmark("Applying Zebra widget",time);}}});})(jQuery);
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/WebKit/PerformanceTests/resources/statistics.js b/sdk/platform-tools/systrace/catapult/telemetry/third_party/WebKit/PerformanceTests/resources/statistics.js
new file mode 100644
index 0000000..5a14b9c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/WebKit/PerformanceTests/resources/statistics.js
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2012, 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+var Statistics = new (function () {
+
+    this.max = function (values) {
+        var maxVal = values[0];
+        for (var i = 1; i < values.length; i++) {
+            maxVal = Math.max(maxVal, values[i]);
+        }
+        return maxVal;
+    }
+
+    this.min = function (values) {
+        var minVal = values[0];
+        for (var i = 1; i < values.length; i++) {
+            minVal = Math.min(minVal, values[i]);
+        }
+        return minVal;
+    }
+
+    this.sum = function (values) {
+        return values.reduce(function (a, b) { return a + b; }, 0);
+    }
+
+    this.squareSum = function (values) {
+        return values.reduce(function (sum, value) { return sum + value * value;}, 0);
+    }
+
+    // With sum and sum of squares, we can compute the sample standard deviation in O(1).
+    // See https://rniwa.com/2012-11-10/sample-standard-deviation-in-terms-of-sum-and-square-sum-of-samples/
+    this.sampleStandardDeviation = function (numberOfSamples, sum, squareSum) {
+        if (numberOfSamples < 2)
+            return 0;
+        return Math.sqrt(squareSum / (numberOfSamples - 1)
+            - sum * sum / (numberOfSamples - 1) / numberOfSamples);
+    }
+
+    this.supportedConfidenceLevels = function () {
+        var supportedLevels = [];
+        for (var quantile in tDistributionInverseCDF)
+            supportedLevels.push((1 - (1 - quantile) * 2).toFixed(2));
+        return supportedLevels;
+    }
+
+    this.quantile = function (confidenceLevel, numberOfSamples, opt_degreesOfFreedom) {
+        var probability = (1 - (1 - confidenceLevel) / 2);
+        if (!(probability in tDistributionInverseCDF)) {
+            console.warn('We only support ' + this.supportedConfidenceLevels().map(
+                function (level) { return level * 100 + '%'; } ).join(', ') + ' confidence intervals.');
+            return NaN;
+        }
+        if (numberOfSamples < 2)
+            return Number.POSITIVE_INFINITY;
+
+        var cdfForProbability = tDistributionInverseCDF[probability];
+        var degreesOfFreedom = opt_degreesOfFreedom;
+        if (degreesOfFreedom === undefined)
+          degreesOfFreedom = numberOfSamples - 1;
+
+        // tDistributionQuantile(degreesOfFreedom, confidenceLevel) * sampleStandardDeviation / sqrt(numberOfSamples) * S/sqrt(numberOfSamples)
+        if (degreesOfFreedom <= 100)
+          return cdfForProbability[degreesOfFreedom - 1]; // The first entry is for the one degree of freedom.
+        else if (degreesOfFreedom <= 300)
+          return cdfForProbability[Math.round(degreesOfFreedom / 10) + 100 - 10 - 1];
+        else if (degreesOfFreedom <= 1300)
+          return cdfForProbability[Math.round(degreesOfFreedom / 100) + 120 - 3 - 1];
+        else
+          return cdfForProbability[cdfForProbability.length - 1];
+    }
+
+    // Computes the delta d s.t. (mean - d, mean + d) is the confidence interval with the specified confidence level in O(1).
+    this.confidenceIntervalDelta = function (confidenceLevel, numberOfSamples, sum, squareSum) {
+        var sampleStandardDeviation = this.sampleStandardDeviation(numberOfSamples, sum, squareSum);
+        return this.confidenceIntervalDeltaFromStd(confidenceLevel, numberOfSamples, sampleStandardDeviation);
+    }
+
+    this.confidenceIntervalDeltaFromStd = function (confidenceLevel, numberOfSamples, sampleStandardDeviation, opt_degreesOfFreedom) {
+        var quantile = this.quantile(confidenceLevel, numberOfSamples, opt_degreesOfFreedom);
+        return quantile * sampleStandardDeviation / Math.sqrt(numberOfSamples);
+    }
+
+
+    this.confidenceInterval = function (values, probability) {
+        var sum = this.sum(values);
+        var mean = sum / values.length;
+        var delta = this.confidenceIntervalDelta(probability || 0.95, values.length, sum, this.squareSum(values));
+        return [mean - delta, mean + delta];
+    }
+
+    // See http://en.wikipedia.org/wiki/Student's_t-distribution#Table_of_selected_values
+    // This table contains one sided (a.k.a. tail) values.
+    // Use TINV((1 - probability) * 2, df) in your favorite spreadsheet software to compute these.
+    // The spacing of the values with df greater than 100 maintains error less than 0.8%.
+    var tDistributionInverseCDF = {
+        0.9: [
+            // 1 - 100 step 1
+            3.077684, 1.885618, 1.637744, 1.533206, 1.475884, 1.439756, 1.414924, 1.396815, 1.383029, 1.372184,
+            1.363430, 1.356217, 1.350171, 1.345030, 1.340606, 1.336757, 1.333379, 1.330391, 1.327728, 1.325341,
+            1.323188, 1.321237, 1.319460, 1.317836, 1.316345, 1.314972, 1.313703, 1.312527, 1.311434, 1.310415,
+            1.309464, 1.308573, 1.307737, 1.306952, 1.306212, 1.305514, 1.304854, 1.304230, 1.303639, 1.303077,
+            1.302543, 1.302035, 1.301552, 1.301090, 1.300649, 1.300228, 1.299825, 1.299439, 1.299069, 1.298714,
+            1.298373, 1.298045, 1.297730, 1.297426, 1.297134, 1.296853, 1.296581, 1.296319, 1.296066, 1.295821,
+            1.295585, 1.295356, 1.295134, 1.294920, 1.294712, 1.294511, 1.294315, 1.294126, 1.293942, 1.293763,
+            1.293589, 1.293421, 1.293256, 1.293097, 1.292941, 1.292790, 1.292643, 1.292500, 1.292360, 1.292224,
+            1.292091, 1.291961, 1.291835, 1.291711, 1.291591, 1.291473, 1.291358, 1.291246, 1.291136, 1.291029,
+            1.290924, 1.290821, 1.290721, 1.290623, 1.290527, 1.290432, 1.290340, 1.290250, 1.290161, 1.290075,
+            // 110 - 300 step 10
+            1.289295, 1.288646, 1.288098, 1.287628, 1.287221, 1.286865, 1.286551, 1.286272, 1.286023, 1.285799,
+            1.285596, 1.285411, 1.285243, 1.285089, 1.284947, 1.284816, 1.284695, 1.284582, 1.284478, 1.284380,
+            // 400 - 1300 step 100
+            1.283672, 1.283247, 1.282964, 1.282762, 1.282611, 1.282493, 1.282399, 1.282322, 1.282257, 1.282203,
+            // Infinity
+            1.281548],
+        0.95: [
+            // 1 - 100 step 1
+            6.313752, 2.919986, 2.353363, 2.131847, 2.015048, 1.943180, 1.894579, 1.859548, 1.833113, 1.812461,
+            1.795885, 1.782288, 1.770933, 1.761310, 1.753050, 1.745884, 1.739607, 1.734064, 1.729133, 1.724718,
+            1.720743, 1.717144, 1.713872, 1.710882, 1.708141, 1.705618, 1.703288, 1.701131, 1.699127, 1.697261,
+            1.695519, 1.693889, 1.692360, 1.690924, 1.689572, 1.688298, 1.687094, 1.685954, 1.684875, 1.683851,
+            1.682878, 1.681952, 1.681071, 1.680230, 1.679427, 1.678660, 1.677927, 1.677224, 1.676551, 1.675905,
+            1.675285, 1.674689, 1.674116, 1.673565, 1.673034, 1.672522, 1.672029, 1.671553, 1.671093, 1.670649,
+            1.670219, 1.669804, 1.669402, 1.669013, 1.668636, 1.668271, 1.667916, 1.667572, 1.667239, 1.666914,
+            1.666600, 1.666294, 1.665996, 1.665707, 1.665425, 1.665151, 1.664885, 1.664625, 1.664371, 1.664125,
+            1.663884, 1.663649, 1.663420, 1.663197, 1.662978, 1.662765, 1.662557, 1.662354, 1.662155, 1.661961,
+            1.661771, 1.661585, 1.661404, 1.661226, 1.661052, 1.660881, 1.660715, 1.660551, 1.660391, 1.660234,
+            // 110 - 300 step 10
+            1.658824, 1.657651, 1.656659, 1.655811, 1.655076, 1.654433, 1.653866, 1.653363, 1.652913, 1.652508,
+            1.652142, 1.651809, 1.651506, 1.651227, 1.650971, 1.650735, 1.650517, 1.650314, 1.650125, 1.649949,
+            // 400 - 1300 step 100
+            1.648672, 1.647907, 1.647397, 1.647033, 1.646761, 1.646548, 1.646379, 1.646240, 1.646124, 1.646027,
+            // Infinity
+            1.644847],
+        0.975: [
+            // 1 - 100 step 1
+            12.706205, 4.302653, 3.182446, 2.776445, 2.570582, 2.446912, 2.364624, 2.306004, 2.262157, 2.228139,
+            2.200985, 2.178813, 2.160369, 2.144787, 2.131450, 2.119905, 2.109816, 2.100922, 2.093024, 2.085963,
+            2.079614, 2.073873, 2.068658, 2.063899, 2.059539, 2.055529, 2.051831, 2.048407, 2.045230, 2.042272,
+            2.039513, 2.036933, 2.034515, 2.032245, 2.030108, 2.028094, 2.026192, 2.024394, 2.022691, 2.021075,
+            2.019541, 2.018082, 2.016692, 2.015368, 2.014103, 2.012896, 2.011741, 2.010635, 2.009575, 2.008559,
+            2.007584, 2.006647, 2.005746, 2.004879, 2.004045, 2.003241, 2.002465, 2.001717, 2.000995, 2.000298,
+            1.999624, 1.998972, 1.998341, 1.997730, 1.997138, 1.996564, 1.996008, 1.995469, 1.994945, 1.994437,
+            1.993943, 1.993464, 1.992997, 1.992543, 1.992102, 1.991673, 1.991254, 1.990847, 1.990450, 1.990063,
+            1.989686, 1.989319, 1.988960, 1.988610, 1.988268, 1.987934, 1.987608, 1.987290, 1.986979, 1.986675,
+            1.986377, 1.986086, 1.985802, 1.985523, 1.985251, 1.984984, 1.984723, 1.984467, 1.984217, 1.983972,
+            // 110 - 300 step 10
+            1.981765, 1.979930, 1.978380, 1.977054, 1.975905, 1.974902, 1.974017, 1.973231, 1.972528, 1.971896,
+            1.971325, 1.970806, 1.970332, 1.969898, 1.969498, 1.969130, 1.968789, 1.968472, 1.968178, 1.967903,
+            // 400 - 1300 step 100
+            1.965912, 1.964720, 1.963926, 1.963359, 1.962934, 1.962603, 1.962339, 1.962123, 1.961943, 1.961790,
+            // Infinity
+            1.959964],
+        0.99: [
+            // 1 - 100 step 1
+            31.820516, 6.964557, 4.540703, 3.746947, 3.364930, 3.142668, 2.997952, 2.896459, 2.821438, 2.763769,
+            2.718079, 2.680998, 2.650309, 2.624494, 2.602480, 2.583487, 2.566934, 2.552380, 2.539483, 2.527977,
+            2.517648, 2.508325, 2.499867, 2.492159, 2.485107, 2.478630, 2.472660, 2.467140, 2.462021, 2.457262,
+            2.452824, 2.448678, 2.444794, 2.441150, 2.437723, 2.434494, 2.431447, 2.428568, 2.425841, 2.423257,
+            2.420803, 2.418470, 2.416250, 2.414134, 2.412116, 2.410188, 2.408345, 2.406581, 2.404892, 2.403272,
+            2.401718, 2.400225, 2.398790, 2.397410, 2.396081, 2.394801, 2.393568, 2.392377, 2.391229, 2.390119,
+            2.389047, 2.388011, 2.387008, 2.386037, 2.385097, 2.384186, 2.383302, 2.382446, 2.381615, 2.380807,
+            2.380024, 2.379262, 2.378522, 2.377802, 2.377102, 2.376420, 2.375757, 2.375111, 2.374482, 2.373868,
+            2.373270, 2.372687, 2.372119, 2.371564, 2.371022, 2.370493, 2.369977, 2.369472, 2.368979, 2.368497,
+            2.368026, 2.367566, 2.367115, 2.366674, 2.366243, 2.365821, 2.365407, 2.365002, 2.364606, 2.364217,
+            // 110 - 300 step 10
+            2.360726, 2.357825, 2.355375, 2.353278, 2.351465, 2.349880, 2.348483, 2.347243, 2.346134, 2.345137,
+            2.344236, 2.343417, 2.342670, 2.341985, 2.341356, 2.340775, 2.340238, 2.339739, 2.339275, 2.338842,
+            // 400 - 1300 step 100
+            2.335706, 2.333829, 2.332579, 2.331687, 2.331018, 2.330498, 2.330083, 2.329743, 2.329459, 2.329220,
+            // Infinity
+            2.326348],
+    };
+
+})();
+
+if (typeof module != 'undefined') {
+    for (var key in Statistics)
+        module.exports[key] = Statistics[key];
+}
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/WebKit/README.chromium b/sdk/platform-tools/systrace/catapult/telemetry/third_party/WebKit/README.chromium
new file mode 100644
index 0000000..798990b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/WebKit/README.chromium
@@ -0,0 +1,14 @@
+Name: Blink javascript libraries.
+Short Name: Blink
+URL: http://www.chromium.org/blink
+Version: N/A
+License: BSD license
+License File: NOT_SHIPPED
+Security Critical: no
+Description: Blink javascripts libraries are used for visualize performance
+metrics.
+Local Modifications: All the files not needed by telemetry are removed. Only
+keep jquery.tablesorter.min.js and statistics.js.
+statistics.js is modified to support computing confidenceIntervalDelta with
+custom standard deviation & degrees of freedom (see
+https://codereview.chromium.org/1309143006).
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/__init__.py
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/MANIFEST.in b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/MANIFEST.in
new file mode 100644
index 0000000..9a9b960
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/MANIFEST.in
@@ -0,0 +1,9 @@
+include ReadMe.txt
+include *.txt MANIFEST.in *.py
+graft doc
+graft doc/_static
+graft doc/_templates
+graft altgraph_tests
+global-exclude .DS_Store
+global-exclude *.pyc
+global-exclude *.so
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/PKG-INFO b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/PKG-INFO
new file mode 100644
index 0000000..87b602f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/PKG-INFO
@@ -0,0 +1,216 @@
+Metadata-Version: 1.1
+Name: altgraph
+Version: 0.12
+Summary: Python graph (network) package
+Home-page: http://packages.python.org/altgraph
+Author: Ronald Oussoren
+Author-email: ronaldoussoren@mac.com
+License: MIT
+Download-URL: http://pypi.python.org/pypi/altgraph
+Description: altgraph is a fork of graphlib: a graph (network) package for constructing
+        graphs, BFS and DFS traversals, topological sort, shortest paths, etc. with
+        graphviz output.
+        
+        altgraph includes some additional usage of Python 2.6+ features and
+        enhancements related to modulegraph and macholib.
+        
+        
+        Release history
+        ===============
+        
+        0.12
+        ----
+        
+        - Added ``ObjectGraph.edgeData`` to retrieve the edge data
+          from a specific edge.
+        
+        - Added ``AltGraph.update_edge_data`` and ``ObjectGraph.updateEdgeData``
+          to update the data associated with a graph edge.
+        
+        0.11
+        ----
+        
+        - Stabilize the order of elements in dot file exports,
+          patch from bitbucket user 'pombredanne'.
+        
+        - Tweak setup.py file to remove dependency on distribute (but
+          keep the dependency on setuptools)
+        
+        
+        0.10.2
+        ------
+        
+        - There where no classifiers in the package metadata due to a bug
+          in setup.py
+        
+        0.10.1
+        ------
+        
+        This is a bugfix release
+        
+        Bug fixes:
+        
+        - Issue #3: The source archive contains a README.txt
+          while the setup file refers to ReadMe.txt.
+        
+          This is caused by a misfeature in distutils, as a
+          workaround I've renamed ReadMe.txt to README.txt
+          in the source tree and setup file.
+        
+        
+        0.10
+        -----
+        
+        This is a minor feature release
+        
+        Features:
+        
+        - Do not use "2to3" to support Python 3.
+        
+          As a side effect of this altgraph now supports
+          Python 2.6 and later, and no longer supports
+          earlier releases of Python.
+        
+        - The order of attributes in the Dot output
+          is now always alphabetical.
+        
+          With this change the output will be consistent
+          between runs and Python versions.
+        
+        0.9
+        ---
+        
+        This is a minor bugfix release
+        
+        Features:
+        
+        - Added ``altgraph.ObjectGraph.ObjectGraph.nodes``, a method
+          yielding all nodes in an object graph.
+        
+        Bugfixes:
+        
+        - The 0.8 release didn't work with py2app when using
+          python 3.x.
+        
+        
+        0.8
+        -----
+        
+        This is a minor feature release. The major new feature
+        is a extensive set of unittests, which explains almost
+        all other changes in this release.
+        
+        Bugfixes:
+        
+        - Installing failed with Python 2.5 due to using a distutils
+          class that isn't available in that version of Python
+          (issue #1 on the issue tracker)
+        
+        - ``altgraph.GraphStat.degree_dist`` now actually works
+        
+        - ``altgraph.Graph.add_edge(a, b, create_nodes=False)`` will
+          no longer create the edge when one of the nodes doesn't
+          exist.
+        
+        - ``altgraph.Graph.forw_topo_sort`` failed for some sparse graphs.
+        
+        - ``altgraph.Graph.back_topo_sort`` was completely broken in
+          previous releases.
+        
+        - ``altgraph.Graph.forw_bfs_subgraph`` now actually works.
+        
+        - ``altgraph.Graph.back_bfs_subgraph`` now actually works.
+        
+        - ``altgraph.Graph.iterdfs`` now returns the correct result
+          when the ``forward`` argument is ``False``.
+        
+        - ``altgraph.Graph.iterdata`` now returns the correct result
+          when the ``forward`` argument is ``False``.
+        
+        
+        Features:
+        
+        - The ``altgraph.Graph`` constructor now accepts an argument
+          that contains 2- and 3-tuples instead of requireing that
+          all items have the same size. The (optional) argument can now
+          also be any iterator.
+        
+        - ``altgraph.Graph.Graph.add_node`` has no effect when you
+          add a hidden node.
+        
+        - The private method ``altgraph.Graph._bfs`` is no longer
+          present.
+        
+        - The private method ``altgraph.Graph._dfs`` is no longer
+          present.
+        
+        - ``altgraph.ObjectGraph`` now has a ``__contains__`` methods,
+          which means you can use the ``in`` operator to check if a
+          node is part of a graph.
+        
+        - ``altgraph.GraphUtil.generate_random_graph`` will raise
+          ``GraphError`` instead of looping forever when it is
+          impossible to create the requested graph.
+        
+        - ``altgraph.Dot.edge_style`` raises ``GraphError`` when
+          one of the nodes is not present in the graph. The method
+          silently added the tail in the past, but without ensuring
+          a consistent graph state.
+        
+        - ``altgraph.Dot.save_img`` now works when the mode is
+          ``"neato"``.
+        
+        0.7.2
+        -----
+        
+        This is a minor bugfix release
+        
+        Bugfixes:
+        
+        - distutils didn't include the documentation subtree
+        
+        0.7.1
+        -----
+        
+        This is a minor feature release
+        
+        Features:
+        
+        - Documentation is now generated using `sphinx <http://pypi.python.org/pypi/sphinx>`_
+          and can be viewed at <http://packages.python.org/altgraph>.
+        
+        - The repository has moved to bitbucket
+        
+        - ``altgraph.GraphStat.avg_hops`` is no longer present, the function had no
+          implementation and no specified behaviour.
+        
+        - the module ``altgraph.compat`` is gone, which means altgraph will no
+          longer work with Python 2.3.
+        
+        
+        0.7.0
+        -----
+        
+        This is a minor feature release.
+        
+        Features:
+        
+        - Support for Python 3
+        
+        - It is now possible to run tests using 'python setup.py test'
+        
+          (The actual testsuite is still very minimal though)
+        
+Keywords: graph
+Platform: any
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.3
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Topic :: Scientific/Engineering :: Mathematics
+Classifier: Topic :: Scientific/Engineering :: Visualization
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/README.chromium b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/README.chromium
new file mode 100644
index 0000000..f9f0cae
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/README.chromium
@@ -0,0 +1,12 @@
+Name: altgraph
+Short Name: altgraph
+URL: https://pypi.python.org/pypi/altgraph/
+Version: 0.12
+License: MIT
+License File: NOT_SHIPPED
+Security Critical: no
+Description: altgraph is a fork of graphlib: a graph (network) package for
+constructing graphs, BFS and DFS traversals, topological sort, shortest paths,
+etc. with graphviz output. altgraph is used by
+telemetry/third_party/modulegraph.
+Local modification: remove doc/_build directory.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/README.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/README.txt
new file mode 100644
index 0000000..904a14b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/README.txt
@@ -0,0 +1,6 @@
+altgraph is a fork of graphlib: a graph (network) package for constructing
+graphs, BFS and DFS traversals, topological sort, shortest paths, etc. with
+graphviz output.
+
+altgraph includes some additional usage of Python 2.6+ features and
+enhancements related to modulegraph and macholib.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph.egg-info/PKG-INFO b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph.egg-info/PKG-INFO
new file mode 100644
index 0000000..87b602f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph.egg-info/PKG-INFO
@@ -0,0 +1,216 @@
+Metadata-Version: 1.1
+Name: altgraph
+Version: 0.12
+Summary: Python graph (network) package
+Home-page: http://packages.python.org/altgraph
+Author: Ronald Oussoren
+Author-email: ronaldoussoren@mac.com
+License: MIT
+Download-URL: http://pypi.python.org/pypi/altgraph
+Description: altgraph is a fork of graphlib: a graph (network) package for constructing
+        graphs, BFS and DFS traversals, topological sort, shortest paths, etc. with
+        graphviz output.
+        
+        altgraph includes some additional usage of Python 2.6+ features and
+        enhancements related to modulegraph and macholib.
+        
+        
+        Release history
+        ===============
+        
+        0.12
+        ----
+        
+        - Added ``ObjectGraph.edgeData`` to retrieve the edge data
+          from a specific edge.
+        
+        - Added ``AltGraph.update_edge_data`` and ``ObjectGraph.updateEdgeData``
+          to update the data associated with a graph edge.
+        
+        0.11
+        ----
+        
+        - Stabilize the order of elements in dot file exports,
+          patch from bitbucket user 'pombredanne'.
+        
+        - Tweak setup.py file to remove dependency on distribute (but
+          keep the dependency on setuptools)
+        
+        
+        0.10.2
+        ------
+        
+        - There where no classifiers in the package metadata due to a bug
+          in setup.py
+        
+        0.10.1
+        ------
+        
+        This is a bugfix release
+        
+        Bug fixes:
+        
+        - Issue #3: The source archive contains a README.txt
+          while the setup file refers to ReadMe.txt.
+        
+          This is caused by a misfeature in distutils, as a
+          workaround I've renamed ReadMe.txt to README.txt
+          in the source tree and setup file.
+        
+        
+        0.10
+        -----
+        
+        This is a minor feature release
+        
+        Features:
+        
+        - Do not use "2to3" to support Python 3.
+        
+          As a side effect of this altgraph now supports
+          Python 2.6 and later, and no longer supports
+          earlier releases of Python.
+        
+        - The order of attributes in the Dot output
+          is now always alphabetical.
+        
+          With this change the output will be consistent
+          between runs and Python versions.
+        
+        0.9
+        ---
+        
+        This is a minor bugfix release
+        
+        Features:
+        
+        - Added ``altgraph.ObjectGraph.ObjectGraph.nodes``, a method
+          yielding all nodes in an object graph.
+        
+        Bugfixes:
+        
+        - The 0.8 release didn't work with py2app when using
+          python 3.x.
+        
+        
+        0.8
+        -----
+        
+        This is a minor feature release. The major new feature
+        is a extensive set of unittests, which explains almost
+        all other changes in this release.
+        
+        Bugfixes:
+        
+        - Installing failed with Python 2.5 due to using a distutils
+          class that isn't available in that version of Python
+          (issue #1 on the issue tracker)
+        
+        - ``altgraph.GraphStat.degree_dist`` now actually works
+        
+        - ``altgraph.Graph.add_edge(a, b, create_nodes=False)`` will
+          no longer create the edge when one of the nodes doesn't
+          exist.
+        
+        - ``altgraph.Graph.forw_topo_sort`` failed for some sparse graphs.
+        
+        - ``altgraph.Graph.back_topo_sort`` was completely broken in
+          previous releases.
+        
+        - ``altgraph.Graph.forw_bfs_subgraph`` now actually works.
+        
+        - ``altgraph.Graph.back_bfs_subgraph`` now actually works.
+        
+        - ``altgraph.Graph.iterdfs`` now returns the correct result
+          when the ``forward`` argument is ``False``.
+        
+        - ``altgraph.Graph.iterdata`` now returns the correct result
+          when the ``forward`` argument is ``False``.
+        
+        
+        Features:
+        
+        - The ``altgraph.Graph`` constructor now accepts an argument
+          that contains 2- and 3-tuples instead of requireing that
+          all items have the same size. The (optional) argument can now
+          also be any iterator.
+        
+        - ``altgraph.Graph.Graph.add_node`` has no effect when you
+          add a hidden node.
+        
+        - The private method ``altgraph.Graph._bfs`` is no longer
+          present.
+        
+        - The private method ``altgraph.Graph._dfs`` is no longer
+          present.
+        
+        - ``altgraph.ObjectGraph`` now has a ``__contains__`` methods,
+          which means you can use the ``in`` operator to check if a
+          node is part of a graph.
+        
+        - ``altgraph.GraphUtil.generate_random_graph`` will raise
+          ``GraphError`` instead of looping forever when it is
+          impossible to create the requested graph.
+        
+        - ``altgraph.Dot.edge_style`` raises ``GraphError`` when
+          one of the nodes is not present in the graph. The method
+          silently added the tail in the past, but without ensuring
+          a consistent graph state.
+        
+        - ``altgraph.Dot.save_img`` now works when the mode is
+          ``"neato"``.
+        
+        0.7.2
+        -----
+        
+        This is a minor bugfix release
+        
+        Bugfixes:
+        
+        - distutils didn't include the documentation subtree
+        
+        0.7.1
+        -----
+        
+        This is a minor feature release
+        
+        Features:
+        
+        - Documentation is now generated using `sphinx <http://pypi.python.org/pypi/sphinx>`_
+          and can be viewed at <http://packages.python.org/altgraph>.
+        
+        - The repository has moved to bitbucket
+        
+        - ``altgraph.GraphStat.avg_hops`` is no longer present, the function had no
+          implementation and no specified behaviour.
+        
+        - the module ``altgraph.compat`` is gone, which means altgraph will no
+          longer work with Python 2.3.
+        
+        
+        0.7.0
+        -----
+        
+        This is a minor feature release.
+        
+        Features:
+        
+        - Support for Python 3
+        
+        - It is now possible to run tests using 'python setup.py test'
+        
+          (The actual testsuite is still very minimal though)
+        
+Keywords: graph
+Platform: any
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.3
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Topic :: Scientific/Engineering :: Mathematics
+Classifier: Topic :: Scientific/Engineering :: Visualization
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph.egg-info/SOURCES.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph.egg-info/SOURCES.txt
new file mode 100644
index 0000000..c345b03
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph.egg-info/SOURCES.txt
@@ -0,0 +1,92 @@
+MANIFEST.in
+README.txt
+ReadMe.txt
+setup.cfg
+setup.py
+altgraph/Dot.py
+altgraph/Graph.py
+altgraph/GraphAlgo.py
+altgraph/GraphStat.py
+altgraph/GraphUtil.py
+altgraph/ObjectGraph.py
+altgraph/__init__.py
+altgraph.egg-info/PKG-INFO
+altgraph.egg-info/SOURCES.txt
+altgraph.egg-info/dependency_links.txt
+altgraph.egg-info/top_level.txt
+altgraph.egg-info/zip-safe
+altgraph_tests/__init__.py
+altgraph_tests/test_altgraph.py
+altgraph_tests/test_dot.py
+altgraph_tests/test_graph.py
+altgraph_tests/test_graphstat.py
+altgraph_tests/test_graphutil.py
+altgraph_tests/test_object_graph.py
+doc/Makefile
+doc/changelog.rst
+doc/conf.py
+doc/core.rst
+doc/dot.rst
+doc/graph.rst
+doc/graphalgo.rst
+doc/graphstat.rst
+doc/graphutil.rst
+doc/index.rst
+doc/license.rst
+doc/objectgraph.rst
+doc/_build/doctrees/changelog.doctree
+doc/_build/doctrees/core.doctree
+doc/_build/doctrees/dot.doctree
+doc/_build/doctrees/environment.pickle
+doc/_build/doctrees/graph.doctree
+doc/_build/doctrees/graphalgo.doctree
+doc/_build/doctrees/graphstat.doctree
+doc/_build/doctrees/graphutil.doctree
+doc/_build/doctrees/index.doctree
+doc/_build/doctrees/license.doctree
+doc/_build/doctrees/objectgraph.doctree
+doc/_build/html/.buildinfo
+doc/_build/html/changelog.html
+doc/_build/html/core.html
+doc/_build/html/dot.html
+doc/_build/html/genindex.html
+doc/_build/html/graph.html
+doc/_build/html/graphalgo.html
+doc/_build/html/graphstat.html
+doc/_build/html/graphutil.html
+doc/_build/html/index.html
+doc/_build/html/license.html
+doc/_build/html/objectgraph.html
+doc/_build/html/objects.inv
+doc/_build/html/py-modindex.html
+doc/_build/html/search.html
+doc/_build/html/searchindex.js
+doc/_build/html/_sources/changelog.txt
+doc/_build/html/_sources/core.txt
+doc/_build/html/_sources/dot.txt
+doc/_build/html/_sources/graph.txt
+doc/_build/html/_sources/graphalgo.txt
+doc/_build/html/_sources/graphstat.txt
+doc/_build/html/_sources/graphutil.txt
+doc/_build/html/_sources/index.txt
+doc/_build/html/_sources/license.txt
+doc/_build/html/_sources/objectgraph.txt
+doc/_build/html/_static/ajax-loader.gif
+doc/_build/html/_static/basic.css
+doc/_build/html/_static/comment-bright.png
+doc/_build/html/_static/comment-close.png
+doc/_build/html/_static/comment.png
+doc/_build/html/_static/doctools.js
+doc/_build/html/_static/down-pressed.png
+doc/_build/html/_static/down.png
+doc/_build/html/_static/file.png
+doc/_build/html/_static/jquery.js
+doc/_build/html/_static/minus.png
+doc/_build/html/_static/nature.css
+doc/_build/html/_static/plus.png
+doc/_build/html/_static/pygments.css
+doc/_build/html/_static/searchtools.js
+doc/_build/html/_static/underscore.js
+doc/_build/html/_static/up-pressed.png
+doc/_build/html/_static/up.png
+doc/_build/html/_static/websupport.js
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph.egg-info/dependency_links.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph.egg-info/dependency_links.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph.egg-info/top_level.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph.egg-info/top_level.txt
new file mode 100644
index 0000000..5ad6b8a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph.egg-info/top_level.txt
@@ -0,0 +1 @@
+altgraph
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph.egg-info/zip-safe b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph.egg-info/zip-safe
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph.egg-info/zip-safe
@@ -0,0 +1 @@
+
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph/Dot.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph/Dot.py
new file mode 100644
index 0000000..49a471e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph/Dot.py
@@ -0,0 +1,299 @@
+'''
+altgraph.Dot - Interface to the dot language
+============================================
+
+The :py:mod:`~altgraph.Dot` module provides a simple interface to the
+file format used in the `graphviz <http://www.research.att.com/sw/tools/graphviz/>`_
+program. The module is intended to offload the most tedious part of the process
+(the **dot** file generation) while transparently exposing most of its features.
+
+To display the graphs or to generate image files the `graphviz <http://www.research.att.com/sw/tools/graphviz/>`_
+package needs to be installed on the system, moreover the :command:`dot` and :command:`dotty` programs must
+be accesible in the program path so that they can be ran from processes spawned
+within the module.
+
+Example usage
+-------------
+
+Here is a typical usage::
+
+    from altgraph import Graph, Dot
+
+    # create a graph
+    edges = [ (1,2), (1,3), (3,4), (3,5), (4,5), (5,4) ]
+    graph = Graph.Graph(edges)
+
+    # create a dot representation of the graph
+    dot = Dot.Dot(graph)
+
+    # display the graph
+    dot.display()
+
+    # save the dot representation into the mydot.dot file
+    dot.save_dot(file_name='mydot.dot')
+
+    # save dot file as gif image into the graph.gif file
+    dot.save_img(file_name='graph', file_type='gif')
+
+Directed graph and non-directed graph
+-------------------------------------
+
+Dot class can use for both directed graph and non-directed graph
+by passing ``graphtype`` parameter.
+
+Example::
+
+    # create directed graph(default)
+    dot = Dot.Dot(graph, graphtype="digraph")
+
+    # create non-directed graph
+    dot = Dot.Dot(graph, graphtype="graph")
+
+Customizing the output
+----------------------
+
+The graph drawing process may be customized by passing
+valid :command:`dot` parameters for the nodes and edges. For a list of all
+parameters see the `graphviz <http://www.research.att.com/sw/tools/graphviz/>`_
+documentation.
+
+Example::
+
+    # customizing the way the overall graph is drawn
+    dot.style(size='10,10', rankdir='RL', page='5, 5' , ranksep=0.75)
+
+    # customizing node drawing
+    dot.node_style(1, label='BASE_NODE',shape='box', color='blue' )
+    dot.node_style(2, style='filled', fillcolor='red')
+
+    # customizing edge drawing
+    dot.edge_style(1, 2, style='dotted')
+    dot.edge_style(3, 5, arrowhead='dot', label='binds', labelangle='90')
+    dot.edge_style(4, 5, arrowsize=2, style='bold')
+
+
+.. note::
+
+   dotty (invoked via :py:func:`~altgraph.Dot.display`) may not be able to
+   display all graphics styles. To verify the output save it to an image file
+   and look at it that way.
+
+Valid attributes
+----------------
+
+    - dot styles, passed via the :py:meth:`Dot.style` method::
+
+        rankdir = 'LR'   (draws the graph horizontally, left to right)
+        ranksep = number (rank separation in inches)
+
+    - node attributes, passed via the :py:meth:`Dot.node_style` method::
+
+        style = 'filled' | 'invisible' | 'diagonals' | 'rounded'
+        shape = 'box' | 'ellipse' | 'circle' | 'point' | 'triangle'
+
+    - edge attributes, passed via the :py:meth:`Dot.edge_style` method::
+
+        style     = 'dashed' | 'dotted' | 'solid' | 'invis' | 'bold'
+        arrowhead = 'box' | 'crow' | 'diamond' | 'dot' | 'inv' | 'none' | 'tee' | 'vee'
+        weight    = number (the larger the number the closer the nodes will be)
+
+    - valid `graphviz colors <http://www.research.att.com/~erg/graphviz/info/colors.html>`_
+
+    - for more details on how to control the graph drawing process see the
+      `graphviz reference <http://www.research.att.com/sw/tools/graphviz/refs.html>`_.
+'''
+import os
+import warnings
+
+from altgraph import GraphError
+
+
+class Dot(object):
+    '''
+    A  class providing a **graphviz** (dot language) representation
+    allowing a fine grained control over how the graph is being
+    displayed.
+
+    If the :command:`dot` and :command:`dotty` programs are not in the current system path
+    their location needs to be specified in the contructor.
+    '''
+
+    def __init__(self, graph=None, nodes=None, edgefn=None, nodevisitor=None, edgevisitor=None, name="G", dot='dot', dotty='dotty', neato='neato', graphtype="digraph"):
+        '''
+        Initialization.
+        '''
+        self.name, self.attr = name, {}
+
+        assert graphtype in ['graph', 'digraph']
+        self.type = graphtype
+
+        self.temp_dot = "tmp_dot.dot"
+        self.temp_neo = "tmp_neo.dot"
+
+        self.dot, self.dotty, self.neato = dot, dotty, neato
+
+        # self.nodes: node styles
+        # self.edges: edge styles
+        self.nodes, self.edges = {}, {}
+
+        if graph is not None and nodes is None:
+            nodes = graph
+        if graph is not None and edgefn is None:
+            def edgefn(node, graph=graph):
+                return graph.out_nbrs(node)
+        if nodes is None:
+            nodes = ()
+
+        seen = set()
+        for node in nodes:
+            if nodevisitor is None:
+                style = {}
+            else:
+                style = nodevisitor(node)
+            if style is not None:
+                self.nodes[node] = {}
+                self.node_style(node, **style)
+                seen.add(node)
+        if edgefn is not None:
+            for head in seen:
+                for tail in (n for n in edgefn(head) if n in seen):
+                    if edgevisitor is None:
+                        edgestyle = {}
+                    else:
+                        edgestyle = edgevisitor(head, tail)
+                    if edgestyle is not None:
+                        if head not in self.edges:
+                            self.edges[head] = {}
+                        self.edges[head][tail] = {}
+                        self.edge_style(head, tail, **edgestyle)
+
+    def style(self, **attr):
+        '''
+        Changes the overall style
+        '''
+        self.attr = attr
+
+    def display(self, mode='dot'):
+        '''
+        Displays the current graph via dotty
+        '''
+
+        if  mode == 'neato':
+            self.save_dot(self.temp_neo)
+            neato_cmd = "%s -o %s %s" % (self.neato, self.temp_dot, self.temp_neo)
+            os.system(neato_cmd)
+        else:
+            self.save_dot(self.temp_dot)
+
+        plot_cmd = "%s %s" % (self.dotty, self.temp_dot)
+        os.system(plot_cmd)
+
+    def node_style(self, node, **kwargs):
+        '''
+        Modifies a node style to the dot representation.
+        '''
+        if node not in self.edges:
+            self.edges[node] = {}
+        self.nodes[node] = kwargs
+
+    def all_node_style(self, **kwargs):
+        '''
+        Modifies all node styles
+        '''
+        for node in self.nodes:
+            self.node_style(node, **kwargs)
+
+    def edge_style(self, head, tail, **kwargs):
+        '''
+        Modifies an edge style to the dot representation.
+        '''
+        if tail not in self.nodes:
+            raise GraphError("invalid node %s" % (tail,))
+
+        try:
+            if tail not in self.edges[head]:
+                self.edges[head][tail]= {}
+            self.edges[head][tail] = kwargs
+        except KeyError:
+            raise GraphError("invalid edge  %s -> %s " % (head, tail) )
+
+    def iterdot(self):
+        # write graph title
+        if self.type == 'digraph':
+            yield 'digraph %s {\n' % (self.name,)
+        elif self.type == 'graph':
+            yield 'graph %s {\n' % (self.name,)
+
+        else:
+            raise GraphError("unsupported graphtype %s" % (self.type,))
+
+        # write overall graph attributes
+        for attr_name, attr_value in sorted(self.attr.items()):
+            yield '%s="%s";' % (attr_name, attr_value)
+        yield '\n'
+
+        # some reusable patterns
+        cpatt  = '%s="%s",'      # to separate attributes
+        epatt  = '];\n'          # to end attributes
+
+        # write node attributes
+        for node_name, node_attr in sorted(self.nodes.items()):
+            yield '\t"%s" [' % (node_name,)
+            for attr_name, attr_value in sorted(node_attr.items()):
+                yield cpatt % (attr_name, attr_value)
+            yield epatt
+
+        # write edge attributes
+        for head in sorted(self.edges):
+            for tail in sorted(self.edges[head]):
+                if self.type == 'digraph':
+                    yield '\t"%s" -> "%s" [' % (head, tail)
+                else:
+                    yield '\t"%s" -- "%s" [' % (head, tail)
+                for attr_name, attr_value in sorted(self.edges[head][tail].items()):
+                    yield cpatt % (attr_name, attr_value)
+                yield epatt
+
+        # finish file
+        yield '}\n'
+
+    def __iter__(self):
+        return self.iterdot()
+
+    def save_dot(self, file_name=None):
+        '''
+        Saves the current graph representation into a file
+        '''
+
+        if not file_name:
+            warnings.warn(DeprecationWarning, "always pass a file_name")
+            file_name = self.temp_dot
+
+        fp   = open(file_name, "w")
+        try:
+            for chunk in self.iterdot():
+                fp.write(chunk)
+        finally:
+            fp.close()
+
+    def save_img(self, file_name=None, file_type="gif", mode='dot'):
+        '''
+        Saves the dot file as an image file
+        '''
+
+        if not file_name:
+            warnings.warn(DeprecationWarning, "always pass a file_name")
+            file_name = "out"
+
+        if  mode == 'neato':
+            self.save_dot(self.temp_neo)
+            neato_cmd = "%s -o %s %s" % (self.neato, self.temp_dot, self.temp_neo)
+            os.system(neato_cmd)
+            plot_cmd = self.dot
+        else:
+            self.save_dot(self.temp_dot)
+            plot_cmd = self.dot
+
+        file_name  = "%s.%s" % (file_name, file_type)
+        create_cmd = "%s -T%s %s -o %s" % (plot_cmd, file_type, self.temp_dot, file_name)
+        os.system(create_cmd)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph/Graph.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph/Graph.py
new file mode 100644
index 0000000..491e5c2
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph/Graph.py
@@ -0,0 +1,677 @@
+"""
+altgraph.Graph - Base Graph class
+=================================
+
+..
+  #--Version 2.1
+  #--Bob Ippolito October, 2004
+
+  #--Version 2.0
+  #--Istvan Albert June, 2004
+
+  #--Version 1.0
+  #--Nathan Denny, May 27, 1999
+"""
+
+from altgraph import GraphError
+from collections import deque
+
+class Graph(object):
+    """
+    The Graph class represents a directed graph with *N* nodes and *E* edges.
+
+    Naming conventions:
+
+    - the prefixes such as *out*, *inc* and *all* will refer to methods
+      that operate on the outgoing, incoming or all edges of that node.
+
+      For example: :py:meth:`inc_degree` will refer to the degree of the node
+      computed over the incoming edges (the number of neighbours linking to
+      the node).
+
+    - the prefixes such as *forw* and *back* will refer to the
+      orientation of the edges used in the method with respect to the node.
+
+      For example: :py:meth:`forw_bfs` will start at the node then use the outgoing
+      edges to traverse the graph (goes forward).
+    """
+
+    def __init__(self, edges=None):
+        """
+        Initialization
+        """
+
+        self.next_edge = 0
+        self.nodes, self.edges = {}, {}
+        self.hidden_edges, self.hidden_nodes = {}, {}
+
+        if edges is not None:
+            for item in edges:
+                if len(item) == 2:
+                    head, tail = item
+                    self.add_edge(head, tail)
+                elif len(item) == 3:
+                    head, tail, data = item
+                    self.add_edge(head, tail, data)
+                else:
+                    raise GraphError("Cannot create edge from %s"%(item,))
+
+
+    def __repr__(self):
+        return '<Graph: %d nodes, %d edges>' % (
+            self.number_of_nodes(), self.number_of_edges())
+
+    def add_node(self, node, node_data=None):
+        """
+        Adds a new node to the graph.  Arbitrary data can be attached to the
+        node via the node_data parameter.  Adding the same node twice will be
+        silently ignored.
+
+        The node must be a hashable value.
+        """
+        #
+        # the nodes will contain tuples that will store incoming edges,
+        # outgoing edges and data
+        #
+        # index 0 -> incoming edges
+        # index 1 -> outgoing edges
+
+        if node in self.hidden_nodes:
+            # Node is present, but hidden
+            return
+
+        if node not in self.nodes:
+            self.nodes[node] = ([], [], node_data)
+
+    def add_edge(self, head_id, tail_id, edge_data=1, create_nodes=True):
+        """
+        Adds a directed edge going from head_id to tail_id.
+        Arbitrary data can be attached to the edge via edge_data.
+        It may create the nodes if adding edges between nonexisting ones.
+
+        :param head_id: head node
+        :param tail_id: tail node
+        :param edge_data: (optional) data attached to the edge
+        :param create_nodes: (optional) creates the head_id or tail_id node in case they did not exist
+        """
+        # shorcut
+        edge = self.next_edge
+
+        # add nodes if on automatic node creation
+        if create_nodes:
+            self.add_node(head_id)
+            self.add_node(tail_id)
+
+        # update the corresponding incoming and outgoing lists in the nodes
+        # index 0 -> incoming edges
+        # index 1 -> outgoing edges
+
+        try:
+            self.nodes[tail_id][0].append(edge)
+            self.nodes[head_id][1].append(edge)
+        except KeyError:
+            raise GraphError('Invalid nodes %s -> %s' % (head_id, tail_id))
+
+        # store edge information
+        self.edges[edge] = (head_id, tail_id, edge_data)
+
+
+        self.next_edge += 1
+
+    def hide_edge(self, edge):
+        """
+        Hides an edge from the graph. The edge may be unhidden at some later
+        time.
+        """
+        try:
+            head_id, tail_id, edge_data = self.hidden_edges[edge] = self.edges[edge]
+            self.nodes[tail_id][0].remove(edge)
+            self.nodes[head_id][1].remove(edge)
+            del self.edges[edge]
+        except KeyError:
+            raise GraphError('Invalid edge %s' % edge)
+
+    def hide_node(self, node):
+        """
+        Hides a node from the graph.  The incoming and outgoing edges of the
+        node will also be hidden.  The node may be unhidden at some later time.
+        """
+        try:
+            all_edges = self.all_edges(node)
+            self.hidden_nodes[node] = (self.nodes[node], all_edges)
+            for edge in all_edges:
+                self.hide_edge(edge)
+            del self.nodes[node]
+        except KeyError:
+            raise GraphError('Invalid node %s' % node)
+
+    def restore_node(self, node):
+        """
+        Restores a previously hidden node back into the graph and restores
+        all of its incoming and outgoing edges.
+        """
+        try:
+            self.nodes[node], all_edges = self.hidden_nodes[node]
+            for edge in all_edges:
+                self.restore_edge(edge)
+            del self.hidden_nodes[node]
+        except KeyError:
+            raise GraphError('Invalid node %s' % node)
+
+    def restore_edge(self, edge):
+        """
+        Restores a previously hidden edge back into the graph.
+        """
+        try:
+            head_id, tail_id, data = self.hidden_edges[edge]
+            self.nodes[tail_id][0].append(edge)
+            self.nodes[head_id][1].append(edge)
+            self.edges[edge] = head_id, tail_id, data
+            del self.hidden_edges[edge]
+        except KeyError:
+            raise GraphError('Invalid edge %s' % edge)
+
+    def restore_all_edges(self):
+        """
+        Restores all hidden edges.
+        """
+        for edge in list(self.hidden_edges.keys()):
+            try:
+                self.restore_edge(edge)
+            except GraphError:
+                pass
+
+    def restore_all_nodes(self):
+        """
+        Restores all hidden nodes.
+        """
+        for node in list(self.hidden_nodes.keys()):
+            self.restore_node(node)
+
+    def __contains__(self, node):
+        """
+        Test whether a node is in the graph
+        """
+        return node in self.nodes
+
+    def edge_by_id(self, edge):
+        """
+        Returns the edge that connects the head_id and tail_id nodes
+        """
+        try:
+            head, tail, data =  self.edges[edge]
+        except KeyError:
+            head, tail = None, None
+            raise GraphError('Invalid edge %s' % edge)
+
+        return (head, tail)
+
+    def edge_by_node(self, head, tail):
+        """
+        Returns the edge that connects the head_id and tail_id nodes
+        """
+        for edge in self.out_edges(head):
+            if self.tail(edge) == tail:
+                return edge
+        return None
+
+    def number_of_nodes(self):
+        """
+        Returns the number of nodes
+        """
+        return len(self.nodes)
+
+    def number_of_edges(self):
+        """
+        Returns the number of edges
+        """
+        return len(self.edges)
+
+    def __iter__(self):
+        """
+        Iterates over all nodes in the graph
+        """
+        return iter(self.nodes)
+
+    def node_list(self):
+        """
+        Return a list of the node ids for all visible nodes in the graph.
+        """
+        return list(self.nodes.keys())
+
+    def edge_list(self):
+        """
+        Returns an iterator for all visible nodes in the graph.
+        """
+        return list(self.edges.keys())
+
+    def number_of_hidden_edges(self):
+        """
+        Returns the number of hidden edges
+        """
+        return len(self.hidden_edges)
+
+    def number_of_hidden_nodes(self):
+        """
+        Returns the number of hidden nodes
+        """
+        return len(self.hidden_nodes)
+
+    def hidden_node_list(self):
+        """
+        Returns the list with the hidden nodes
+        """
+        return list(self.hidden_nodes.keys())
+
+    def hidden_edge_list(self):
+        """
+        Returns a list with the hidden edges
+        """
+        return list(self.hidden_edges.keys())
+
+    def describe_node(self, node):
+        """
+        return node, node data, outgoing edges, incoming edges for node
+        """
+        incoming, outgoing, data = self.nodes[node]
+        return node, data, outgoing, incoming
+
+    def describe_edge(self, edge):
+        """
+        return edge, edge data, head, tail for edge
+        """
+        head, tail, data = self.edges[edge]
+        return edge, data, head, tail
+
+    def node_data(self, node):
+        """
+        Returns the data associated with a node
+        """
+        return self.nodes[node][2]
+
+    def edge_data(self, edge):
+        """
+        Returns the data associated with an edge
+        """
+        return self.edges[edge][2]
+
+    def update_edge_data(self, edge, edge_data):
+        """
+        Replace the edge data for a specific edge
+        """
+        self.edges[edge] = self.edges[edge][0:2] + (edge_data,)
+
+    def head(self, edge):
+        """
+        Returns the node of the head of the edge.
+        """
+        return self.edges[edge][0]
+
+    def tail(self, edge):
+        """
+        Returns node of the tail of the edge.
+        """
+        return self.edges[edge][1]
+
+    def out_nbrs(self, node):
+        """
+        List of nodes connected by outgoing edges
+        """
+        l = [self.tail(n) for n in self.out_edges(node)]
+        return l
+
+    def inc_nbrs(self, node):
+        """
+        List of nodes connected by incoming edges
+        """
+        l = [self.head(n) for n in self.inc_edges(node)]
+        return l
+
+    def all_nbrs(self, node):
+        """
+        List of nodes connected by incoming and outgoing edges
+        """
+        l = dict.fromkeys( self.inc_nbrs(node) + self.out_nbrs(node) )
+        return list(l)
+
+    def out_edges(self, node):
+        """
+        Returns a list of the outgoing edges
+        """
+        try:
+            return list(self.nodes[node][1])
+        except KeyError:
+            raise GraphError('Invalid node %s' % node)
+
+        return None
+
+    def inc_edges(self, node):
+        """
+        Returns a list of the incoming edges
+        """
+        try:
+            return list(self.nodes[node][0])
+        except KeyError:
+            raise GraphError('Invalid node %s' % node)
+
+        return None
+
+    def all_edges(self, node):
+        """
+        Returns a list of incoming and outging edges.
+        """
+        return set(self.inc_edges(node) + self.out_edges(node))
+
+    def out_degree(self, node):
+        """
+        Returns the number of outgoing edges
+        """
+        return len(self.out_edges(node))
+
+    def inc_degree(self, node):
+        """
+        Returns the number of incoming edges
+        """
+        return len(self.inc_edges(node))
+
+    def all_degree(self, node):
+        """
+        The total degree of a node
+        """
+        return self.inc_degree(node) + self.out_degree(node)
+
+    def _topo_sort(self, forward=True):
+        """
+        Topological sort.
+
+        Returns a list of nodes where the successors (based on outgoing and
+        incoming edges selected by the forward parameter) of any given node
+        appear in the sequence after that node.
+        """
+        topo_list = []
+        queue = deque()
+        indeg = {}
+
+        # select the operation that will be performed
+        if forward:
+            get_edges = self.out_edges
+            get_degree = self.inc_degree
+            get_next = self.tail
+        else:
+            get_edges = self.inc_edges
+            get_degree = self.out_degree
+            get_next = self.head
+
+        for node in self.node_list():
+            degree = get_degree(node)
+            if degree:
+                indeg[node] = degree
+            else:
+                queue.append(node)
+
+        while queue:
+            curr_node = queue.popleft()
+            topo_list.append(curr_node)
+            for edge in get_edges(curr_node):
+                tail_id = get_next(edge)
+                if tail_id in indeg:
+                    indeg[tail_id] -= 1
+                    if indeg[tail_id] == 0:
+                        queue.append(tail_id)
+
+        if len(topo_list) == len(self.node_list()):
+            valid = True
+        else:
+            # the graph has cycles, invalid topological sort
+            valid = False
+
+        return (valid, topo_list)
+
+    def forw_topo_sort(self):
+        """
+        Topological sort.
+
+        Returns a list of nodes where the successors (based on outgoing edges)
+        of any given node appear in the sequence after that node.
+        """
+        return self._topo_sort(forward=True)
+
+    def back_topo_sort(self):
+        """
+        Reverse topological sort.
+
+        Returns a list of nodes where the successors (based on incoming edges)
+        of any given node appear in the sequence after that node.
+        """
+        return self._topo_sort(forward=False)
+
+    def _bfs_subgraph(self, start_id, forward=True):
+        """
+        Private method creates a subgraph in a bfs order.
+
+        The forward parameter specifies whether it is a forward or backward
+        traversal.
+        """
+        if forward:
+            get_bfs  = self.forw_bfs
+            get_nbrs = self.out_nbrs
+        else:
+            get_bfs  = self.back_bfs
+            get_nbrs = self.inc_nbrs
+
+        g = Graph()
+        bfs_list = get_bfs(start_id)
+        for node in bfs_list:
+            g.add_node(node)
+
+        for node in bfs_list:
+            for nbr_id in get_nbrs(node):
+                g.add_edge(node, nbr_id)
+
+        return g
+
+    def forw_bfs_subgraph(self, start_id):
+        """
+        Creates and returns a subgraph consisting of the breadth first
+        reachable nodes based on their outgoing edges.
+        """
+        return self._bfs_subgraph(start_id, forward=True)
+
+    def back_bfs_subgraph(self, start_id):
+        """
+        Creates and returns a subgraph consisting of the breadth first
+        reachable nodes based on the incoming edges.
+        """
+        return self._bfs_subgraph(start_id, forward=False)
+
+    def iterdfs(self, start, end=None, forward=True):
+        """
+        Collecting nodes in some depth first traversal.
+
+        The forward parameter specifies whether it is a forward or backward
+        traversal.
+        """
+        visited, stack = set([start]), deque([start])
+
+        if forward:
+            get_edges = self.out_edges
+            get_next = self.tail
+        else:
+            get_edges = self.inc_edges
+            get_next = self.head
+
+        while stack:
+            curr_node = stack.pop()
+            yield curr_node
+            if curr_node == end:
+                break
+            for edge in sorted(get_edges(curr_node)):
+                tail = get_next(edge)
+                if tail not in visited:
+                    visited.add(tail)
+                    stack.append(tail)
+
+    def iterdata(self, start, end=None, forward=True, condition=None):
+        """
+        Perform a depth-first walk of the graph (as ``iterdfs``)
+        and yield the item data of every node where condition matches. The
+        condition callback is only called when node_data is not None.
+        """
+
+        visited, stack = set([start]), deque([start])
+
+        if forward:
+            get_edges = self.out_edges
+            get_next = self.tail
+        else:
+            get_edges = self.inc_edges
+            get_next = self.head
+
+        get_data = self.node_data
+
+        while stack:
+            curr_node = stack.pop()
+            curr_data = get_data(curr_node)
+            if curr_data is not None:
+                if condition is not None and not condition(curr_data):
+                    continue
+                yield curr_data
+            if curr_node == end:
+                break
+            for edge in get_edges(curr_node):
+                tail = get_next(edge)
+                if tail not in visited:
+                    visited.add(tail)
+                    stack.append(tail)
+
+    def _iterbfs(self, start, end=None, forward=True):
+        """
+        The forward parameter specifies whether it is a forward or backward
+        traversal.  Returns a list of tuples where the first value is the hop
+        value the second value is the node id.
+        """
+        queue, visited = deque([(start, 0)]), set([start])
+
+        # the direction of the bfs depends on the edges that are sampled
+        if forward:
+            get_edges = self.out_edges
+            get_next = self.tail
+        else:
+            get_edges = self.inc_edges
+            get_next = self.head
+
+        while queue:
+            curr_node, curr_step = queue.popleft()
+            yield (curr_node, curr_step)
+            if curr_node == end:
+                break
+            for edge in get_edges(curr_node):
+                tail = get_next(edge)
+                if tail not in visited:
+                    visited.add(tail)
+                    queue.append((tail, curr_step + 1))
+
+
+    def forw_bfs(self, start, end=None):
+        """
+        Returns a list of nodes in some forward BFS order.
+
+        Starting from the start node the breadth first search proceeds along
+        outgoing edges.
+        """
+        return [node for node, step in self._iterbfs(start, end, forward=True)]
+
+    def back_bfs(self, start, end=None):
+        """
+        Returns a list of nodes in some backward BFS order.
+
+        Starting from the start node the breadth first search proceeds along
+        incoming edges.
+        """
+        return [node for node, step in self._iterbfs(start, end, forward=False)]
+
+    def forw_dfs(self, start, end=None):
+        """
+        Returns a list of nodes in some forward DFS order.
+
+        Starting with the start node the depth first search proceeds along
+        outgoing edges.
+        """
+        return list(self.iterdfs(start, end, forward=True))
+
+    def back_dfs(self, start, end=None):
+        """
+        Returns a list of nodes in some backward DFS order.
+
+        Starting from the start node the depth first search proceeds along
+        incoming edges.
+        """
+        return list(self.iterdfs(start, end, forward=False))
+
+    def connected(self):
+        """
+        Returns :py:data:`True` if the graph's every node can be reached from every
+        other node.
+        """
+        node_list = self.node_list()
+        for node in node_list:
+            bfs_list = self.forw_bfs(node)
+            if len(bfs_list) != len(node_list):
+                return False
+        return True
+
+    def clust_coef(self, node):
+        """
+        Computes and returns the local clustering coefficient of node.  The
+        local cluster coefficient is proportion of the actual number of edges between
+        neighbours of node and the maximum number of edges between those neighbours.
+
+        See <http://en.wikipedia.org/wiki/Clustering_coefficient#Local_clustering_coefficient>
+        for a formal definition.
+        """
+        num = 0
+        nbr_set = set(self.out_nbrs(node))
+
+        if node in nbr_set:
+            nbr_set.remove(node) # loop defense
+
+        for nbr in nbr_set:
+            sec_set = set(self.out_nbrs(nbr))
+            if nbr in sec_set:
+                sec_set.remove(nbr) # loop defense
+            num += len(nbr_set & sec_set)
+
+        nbr_num = len(nbr_set)
+        if nbr_num:
+            clust_coef = float(num) / (nbr_num * (nbr_num - 1))
+        else:
+            clust_coef = 0.0
+        return clust_coef
+
+    def get_hops(self, start, end=None, forward=True):
+        """
+        Computes the hop distance to all nodes centered around a specified node.
+
+        First order neighbours are at hop 1, their neigbours are at hop 2 etc.
+        Uses :py:meth:`forw_bfs` or :py:meth:`back_bfs` depending on the value of the forward
+        parameter.  If the distance between all neighbouring nodes is 1 the hop
+        number corresponds to the shortest distance between the nodes.
+
+        :param start: the starting node
+        :param end: ending node (optional). When not specified will search the whole graph.
+        :param forward: directionality parameter (optional). If C{True} (default) it uses L{forw_bfs} otherwise L{back_bfs}.
+        :return: returns a list of tuples where each tuple contains the node and the hop.
+
+        Typical usage::
+
+            >>> print (graph.get_hops(1, 8))
+            >>> [(1, 0), (2, 1), (3, 1), (4, 2), (5, 3), (7, 4), (8, 5)]
+            # node 1 is at 0 hops
+            # node 2 is at 1 hop
+            # ...
+            # node 8 is at 5 hops
+        """
+        if forward:
+            return list(self._iterbfs(start=start, end=end, forward=True))
+        else:
+            return list(self._iterbfs(start=start, end=end, forward=False))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph/GraphAlgo.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph/GraphAlgo.py
new file mode 100644
index 0000000..9e6fff2
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph/GraphAlgo.py
@@ -0,0 +1,147 @@
+'''
+altgraph.GraphAlgo - Graph algorithms
+=====================================
+'''
+from altgraph import GraphError
+
+def dijkstra(graph, start, end=None):
+    """
+    Dijkstra's algorithm for shortest paths
+
+    `David Eppstein, UC Irvine, 4 April 2002 <http://www.ics.uci.edu/~eppstein/161/python/>`_
+
+    `Python Cookbook Recipe <http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/119466>`_
+
+    Find shortest paths from the  start node to all nodes nearer than or equal to the end node.
+
+    Dijkstra's algorithm is only guaranteed to work correctly when all edge lengths are positive.
+    This code does not verify this property for all edges (only the edges examined until the end
+    vertex is reached), but will correctly compute shortest paths even for some graphs with negative
+    edges, and will raise an exception if it discovers that a negative edge has caused it to make a mistake.
+
+    *Adapted to altgraph by Istvan Albert, Pennsylvania State University - June, 9 2004*
+
+    """
+    D = {}    # dictionary of final distances
+    P = {}    # dictionary of predecessors
+    Q = _priorityDictionary()    # estimated distances of non-final vertices
+    Q[start] = 0
+
+    for v in Q:
+        D[v] = Q[v]
+        if v == end: break
+
+        for w in graph.out_nbrs(v):
+            edge_id  = graph.edge_by_node(v,w)
+            vwLength = D[v] + graph.edge_data(edge_id)
+            if w in D:
+                if vwLength < D[w]:
+                    raise GraphError("Dijkstra: found better path to already-final vertex")
+            elif w not in Q or vwLength < Q[w]:
+                Q[w] = vwLength
+                P[w] = v
+
+    return (D,P)
+
+def shortest_path(graph, start, end):
+    """
+    Find a single shortest path from the given start node to the given end node.
+    The input has the same conventions as dijkstra(). The output is a list of the nodes
+    in order along the shortest path.
+
+    **Note that the distances must be stored in the edge data as numeric data**
+    """
+
+    D,P = dijkstra(graph, start, end)
+    Path = []
+    while 1:
+        Path.append(end)
+        if end == start: break
+        end = P[end]
+    Path.reverse()
+    return Path
+
+#
+# Utility classes and functions
+#
+class _priorityDictionary(dict):
+    '''
+    Priority dictionary using binary heaps (internal use only)
+
+    David Eppstein, UC Irvine, 8 Mar 2002
+
+    Implements a data structure that acts almost like a dictionary, with two modifications:
+        1. D.smallest() returns the value x minimizing D[x].  For this to work correctly,
+            all values D[x] stored in the dictionary must be comparable.
+        2. iterating "for x in D" finds and removes the items from D in sorted order.
+            Each item is not removed until the next item is requested, so D[x] will still
+            return a useful value until the next iteration of the for-loop.
+            Each operation takes logarithmic amortized time.
+    '''
+    def __init__(self):
+        '''
+        Initialize priorityDictionary by creating binary heap of pairs (value,key).
+        Note that changing or removing a dict entry will not remove the old pair from the heap
+        until it is found by smallest() or until the heap is rebuilt.
+        '''
+        self.__heap = []
+        dict.__init__(self)
+
+    def smallest(self):
+        '''
+        Find smallest item after removing deleted items from front of heap.
+        '''
+        if len(self) == 0:
+            raise IndexError("smallest of empty priorityDictionary")
+        heap = self.__heap
+        while heap[0][1] not in self or self[heap[0][1]] != heap[0][0]:
+            lastItem = heap.pop()
+            insertionPoint = 0
+            while 1:
+                smallChild = 2*insertionPoint+1
+                if smallChild+1 < len(heap) and heap[smallChild] > heap[smallChild+1] :
+                    smallChild += 1
+                if smallChild >= len(heap) or lastItem <= heap[smallChild]:
+                    heap[insertionPoint] = lastItem
+                    break
+                heap[insertionPoint] = heap[smallChild]
+                insertionPoint = smallChild
+        return heap[0][1]
+
+    def __iter__(self):
+        '''
+        Create destructive sorted iterator of priorityDictionary.
+        '''
+        def iterfn():
+            while len(self) > 0:
+                x = self.smallest()
+                yield x
+                del self[x]
+        return iterfn()
+
+    def __setitem__(self,key,val):
+        '''
+        Change value stored in dictionary and add corresponding pair to heap.
+        Rebuilds the heap if the number of deleted items gets large, to avoid memory leakage.
+        '''
+        dict.__setitem__(self,key,val)
+        heap = self.__heap
+        if len(heap) > 2 * len(self):
+            self.__heap = [(v,k) for k,v in self.iteritems()]
+            self.__heap.sort()  # builtin sort probably faster than O(n)-time heapify
+        else:
+            newPair = (val,key)
+            insertionPoint = len(heap)
+            heap.append(None)
+            while insertionPoint > 0 and newPair < heap[(insertionPoint-1)//2]:
+                heap[insertionPoint] = heap[(insertionPoint-1)//2]
+                insertionPoint = (insertionPoint-1)//2
+            heap[insertionPoint] = newPair
+
+    def setdefault(self,key,val):
+        '''
+        Reimplement setdefault to pass through our customized __setitem__.
+        '''
+        if key not in self:
+            self[key] = val
+        return self[key]
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph/GraphStat.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph/GraphStat.py
new file mode 100644
index 0000000..25fc46c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph/GraphStat.py
@@ -0,0 +1,73 @@
+'''
+altgraph.GraphStat - Functions providing various graph statistics
+=================================================================
+'''
+import sys
+
+def degree_dist(graph, limits=(0,0), bin_num=10, mode='out'):
+    '''
+    Computes the degree distribution for a graph.
+
+    Returns a list of tuples where the first element of the tuple is the center of the bin
+    representing a range of degrees and the second element of the tuple are the number of nodes
+    with the degree falling in the range.
+
+    Example::
+
+        ....
+    '''
+
+    deg = []
+    if mode == 'inc':
+        get_deg = graph.inc_degree
+    else:
+        get_deg = graph.out_degree
+
+    for node in graph:
+        deg.append( get_deg(node) )
+
+    if not deg:
+        return []
+
+    results = _binning(values=deg, limits=limits, bin_num=bin_num)
+
+    return results
+
+_EPS = 1.0/(2.0**32)
+def _binning(values, limits=(0,0), bin_num=10):
+    '''
+    Bins data that falls between certain limits, if the limits are (0, 0) the
+    minimum and maximum values are used.
+
+    Returns a list of tuples where the first element of the tuple is the center of the bin
+    and the second element of the tuple are the counts.
+    '''
+    if limits == (0, 0):
+        min_val, max_val = min(values) - _EPS, max(values) + _EPS
+    else:
+        min_val, max_val = limits
+
+    # get bin size
+    bin_size = (max_val - min_val)/float(bin_num)
+    bins = [0] * (bin_num)
+
+    # will ignore these outliers for now
+    out_points = 0
+    for value in values:
+        try:
+            if (value - min_val) < 0:
+                out_points += 1
+            else:
+                index = int((value - min_val)/float(bin_size))
+                bins[index] += 1
+        except IndexError:
+            out_points += 1
+
+    # make it ready for an x,y plot
+    result = []
+    center = (bin_size/2) + min_val
+    for i, y in enumerate(bins):
+        x = center + bin_size * i
+        result.append( (x,y) )
+
+    return result
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph/GraphUtil.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph/GraphUtil.py
new file mode 100644
index 0000000..d3b6acd
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph/GraphUtil.py
@@ -0,0 +1,137 @@
+'''
+altgraph.GraphUtil - Utility classes and functions
+==================================================
+'''
+
+import random
+from collections import deque
+from altgraph import Graph
+from altgraph import GraphError
+
+def generate_random_graph(node_num, edge_num, self_loops=False, multi_edges=False):
+    '''
+    Generates and returns a :py:class:`~altgraph.Graph.Graph` instance with *node_num* nodes
+    randomly connected by *edge_num* edges.
+    '''
+    g = Graph.Graph()
+
+    if not multi_edges:
+        if self_loops:
+            max_edges = node_num * node_num
+        else:
+            max_edges = node_num * (node_num-1)
+
+        if edge_num > max_edges:
+            raise GraphError("inconsistent arguments to 'generate_random_graph'")
+
+    nodes = range(node_num)
+
+    for node in nodes:
+        g.add_node(node)
+
+    while 1:
+        head = random.choice(nodes)
+        tail = random.choice(nodes)
+
+        # loop defense
+        if head == tail and not self_loops:
+            continue
+
+        # multiple edge defense
+        if g.edge_by_node(head,tail) is not None and not multi_edges:
+            continue
+
+        # add the edge
+        g.add_edge(head, tail)
+        if g.number_of_edges() >= edge_num:
+            break
+
+    return g
+
+def generate_scale_free_graph(steps, growth_num, self_loops=False, multi_edges=False):
+    '''
+    Generates and returns a :py:class:`~altgraph.Graph.Graph` instance that will have *steps* \* *growth_num* nodes
+    and a scale free (powerlaw) connectivity. Starting with a fully connected graph with *growth_num* nodes
+    at every step *growth_num* nodes are added to the graph and are connected to existing nodes with
+    a probability proportional to the degree of these existing nodes.
+    '''
+    # FIXME: The code doesn't seem to do what the documentation claims.
+    graph = Graph.Graph()
+
+    # initialize the graph
+    store = []
+    for i in range(growth_num):
+        #store   += [ i ] * (growth_num - 1)
+        for j in range(i + 1, growth_num):
+            store.append(i)
+            store.append(j)
+            graph.add_edge(i,j)
+
+    # generate
+    for node in range(growth_num, steps * growth_num):
+        graph.add_node(node)
+        while ( graph.out_degree(node) < growth_num ):
+            nbr = random.choice(store)
+
+            # loop defense
+            if node == nbr and not self_loops:
+                continue
+
+            # multi edge defense
+            if graph.edge_by_node(node, nbr) and not multi_edges:
+                continue
+
+            graph.add_edge(node, nbr)
+
+
+        for nbr in graph.out_nbrs(node):
+            store.append(node)
+            store.append(nbr)
+
+    return graph
+
+def filter_stack(graph, head, filters):
+    """
+    Perform a walk in a depth-first order starting
+    at *head*.
+
+    Returns (visited, removes, orphans).
+
+    * visited: the set of visited nodes
+    * removes: the list of nodes where the node
+      data does not all *filters*
+    * orphans: tuples of (last_good, node),
+      where node is not in removes, is directly
+      reachable from a node in *removes* and
+      *last_good* is the closest upstream node that is not
+      in *removes*.
+    """
+
+    visited, removes, orphans = set([head]), set(), set()
+    stack = deque([(head, head)])
+    get_data = graph.node_data
+    get_edges = graph.out_edges
+    get_tail = graph.tail
+
+    while stack:
+        last_good, node = stack.pop()
+        data = get_data(node)
+        if data is not None:
+            for filtfunc in filters:
+                if not filtfunc(data):
+                    removes.add(node)
+                    break
+            else:
+                last_good = node
+        for edge in get_edges(node):
+            tail = get_tail(edge)
+            if last_good is not node:
+                orphans.add((last_good, tail))
+            if tail not in visited:
+                visited.add(tail)
+                stack.append((last_good, tail))
+
+    orphans = [(last_good, tail) for (last_good, tail) in orphans if tail not in removes]
+    #orphans.sort()
+
+    return visited, removes, orphans
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph/ObjectGraph.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph/ObjectGraph.py
new file mode 100644
index 0000000..d07f51b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph/ObjectGraph.py
@@ -0,0 +1,202 @@
+"""
+altgraph.ObjectGraph - Graph of objects with an identifier
+==========================================================
+
+A graph of objects that have a "graphident" attribute.
+graphident is the key for the object in the graph
+"""
+
+from altgraph import GraphError
+from altgraph.Graph import Graph
+from altgraph.GraphUtil import filter_stack
+
+class ObjectGraph(object):
+    """
+    A graph of objects that have a "graphident" attribute.
+    graphident is the key for the object in the graph
+    """
+    def __init__(self, graph=None, debug=0):
+        if graph is None:
+            graph = Graph()
+        self.graphident = self
+        self.graph = graph
+        self.debug = debug
+        self.indent = 0
+        graph.add_node(self, None)
+
+    def __repr__(self):
+        return '<%s>' % (type(self).__name__,)
+
+    def flatten(self, condition=None, start=None):
+        """
+        Iterate over the subgraph that is entirely reachable by condition
+        starting from the given start node or the ObjectGraph root
+        """
+        if start is None:
+            start = self
+        start = self.getRawIdent(start)
+        return self.graph.iterdata(start=start, condition=condition)
+
+    def nodes(self):
+        for ident in self.graph:
+            node = self.graph.node_data(ident)
+            if node is not None:
+                yield self.graph.node_data(ident)
+
+
+    def get_edges(self, node):
+        start = self.getRawIdent(node)
+        _, _, outraw, incraw = self.graph.describe_node(start)
+        def iter_edges(lst, n):
+            seen = set()
+            for tpl in (self.graph.describe_edge(e) for e in lst):
+                ident = tpl[n]
+                if ident not in seen:
+                    yield self.findNode(ident)
+                    seen.add(ident)
+        return iter_edges(outraw, 3), iter_edges(incraw, 2)
+
+    def edgeData(self, fromNode, toNode):
+        start = self.getRawIdent(fromNode)
+        stop = self.getRawIdent(toNode)
+        edge = self.graph.edge_by_node(start, stop)
+        return self.graph.edge_data(edge)
+
+    def updateEdgeData(self, fromNode, toNode, edgeData):
+        start = self.getRawIdent(fromNode)
+        stop = self.getRawIdent(toNode)
+        edge = self.graph.edge_by_node(start, stop)
+        self.graph.update_edge_data(edge, edgeData)
+
+    def filterStack(self, filters):
+        """
+        Filter the ObjectGraph in-place by removing all edges to nodes that
+        do not match every filter in the given filter list
+
+        Returns a tuple containing the number of: (nodes_visited, nodes_removed, nodes_orphaned)
+        """
+        visited, removes, orphans = filter_stack(self.graph, self, filters)
+
+        for last_good, tail in orphans:
+            self.graph.add_edge(last_good, tail, edge_data='orphan')
+
+        for node in removes:
+            self.graph.hide_node(node)
+
+        return len(visited)-1, len(removes), len(orphans)
+
+    def removeNode(self, node):
+        """
+        Remove the given node from the graph if it exists
+        """
+        ident = self.getIdent(node)
+        if ident is not None:
+            self.graph.hide_node(ident)
+
+    def removeReference(self, fromnode, tonode):
+        """
+        Remove all edges from fromnode to tonode
+        """
+        if fromnode is None:
+            fromnode = self
+        fromident = self.getIdent(fromnode)
+        toident = self.getIdent(tonode)
+        if fromident is not None and toident is not None:
+            while True:
+                edge = self.graph.edge_by_node(fromident, toident)
+                if edge is None:
+                    break
+                self.graph.hide_edge(edge)
+
+    def getIdent(self, node):
+        """
+        Get the graph identifier for a node
+        """
+        ident = self.getRawIdent(node)
+        if ident is not None:
+            return ident
+        node = self.findNode(node)
+        if node is None:
+            return None
+        return node.graphident
+
+    def getRawIdent(self, node):
+        """
+        Get the identifier for a node object
+        """
+        if node is self:
+            return node
+        ident = getattr(node, 'graphident', None)
+        return ident
+
+    def __contains__(self, node):
+        return self.findNode(node) is not None
+
+    def findNode(self, node):
+        """
+        Find the node on the graph
+        """
+        ident = self.getRawIdent(node)
+        if ident is None:
+            ident = node
+        try:
+            return self.graph.node_data(ident)
+        except KeyError:
+            return None
+
+    def addNode(self, node):
+        """
+        Add a node to the graph referenced by the root
+        """
+        self.msg(4, "addNode", node)
+
+        try:
+            self.graph.restore_node(node.graphident)
+        except GraphError:
+            self.graph.add_node(node.graphident, node)
+
+    def createReference(self, fromnode, tonode, edge_data=None):
+        """
+        Create a reference from fromnode to tonode
+        """
+        if fromnode is None:
+            fromnode = self
+        fromident, toident = self.getIdent(fromnode), self.getIdent(tonode)
+        if fromident is None or toident is None:
+            return
+        self.msg(4, "createReference", fromnode, tonode, edge_data)
+        self.graph.add_edge(fromident, toident, edge_data=edge_data)
+
+    def createNode(self, cls, name, *args, **kw):
+        """
+        Add a node of type cls to the graph if it does not already exist
+        by the given name
+        """
+        m = self.findNode(name)
+        if m is None:
+            m = cls(name, *args, **kw)
+            self.addNode(m)
+        return m
+
+    def msg(self, level, s, *args):
+        """
+        Print a debug message with the given level
+        """
+        if s and level <= self.debug:
+            print ("%s%s %s" % ("  " * self.indent, s, ' '.join(map(repr, args))))
+
+    def msgin(self, level, s, *args):
+        """
+        Print a debug message and indent
+        """
+        if level <= self.debug:
+            self.msg(level, s, *args)
+            self.indent = self.indent + 1
+
+    def msgout(self, level, s, *args):
+        """
+        Dedent and print a debug message
+        """
+        if level <= self.debug:
+            self.indent = self.indent - 1
+            self.msg(level, s, *args)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph/__init__.py
new file mode 100644
index 0000000..9f72c18
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph/__init__.py
@@ -0,0 +1,135 @@
+'''
+altgraph - a python graph library
+=================================
+
+altgraph is a fork of `graphlib <http://pygraphlib.sourceforge.net>`_ tailored
+to use newer Python 2.3+ features, including additional support used by the
+py2app suite (modulegraph and macholib, specifically).
+
+altgraph is a python based graph (network) representation and manipulation package.
+It has started out as an extension to the `graph_lib module <http://www.ece.arizona.edu/~denny/python_nest/graph_lib_1.0.1.html>`_
+written by Nathan Denny it has been significantly optimized and expanded.
+
+The :class:`altgraph.Graph.Graph` class is loosely modeled after the `LEDA <http://www.algorithmic-solutions.com/enleda.htm>`_
+(Library of Efficient Datatypes)  representation. The library
+includes methods for constructing graphs, BFS and DFS traversals,
+topological sort, finding connected components, shortest paths as well as a number
+graph statistics functions. The library can also visualize graphs
+via `graphviz <http://www.research.att.com/sw/tools/graphviz/>`_.
+
+The package contains the following modules:
+
+    -  the :py:mod:`altgraph.Graph` module contains the :class:`~altgraph.Graph.Graph` class that stores the graph data
+
+    -  the :py:mod:`altgraph.GraphAlgo` module implements graph algorithms operating on graphs (:py:class:`~altgraph.Graph.Graph`} instances)
+
+    -  the :py:mod:`altgraph.GraphStat` module contains functions for computing statistical measures on graphs
+
+    -  the :py:mod:`altgraph.GraphUtil` module contains functions for generating, reading and saving graphs
+
+    -  the :py:mod:`altgraph.Dot` module  contains functions for displaying graphs via `graphviz <http://www.research.att.com/sw/tools/graphviz/>`_
+
+    -  the :py:mod:`altgraph.ObjectGraph` module implements a graph of objects with a unique identifier
+
+Installation
+------------
+
+Download and unpack the archive then type::
+
+    python setup.py install
+
+This will install the library in the default location. For instructions on
+how to customize the install procedure read the output of::
+
+    python setup.py --help install
+
+To verify that the code works run the test suite::
+
+    python setup.py test
+
+Example usage
+-------------
+
+Lets assume that we want to analyze the graph below (links to the full picture) GRAPH_IMG.
+Our script then might look the following way::
+
+    from altgraph import Graph, GraphAlgo, Dot
+
+    # these are the edges
+    edges = [ (1,2), (2,4), (1,3), (2,4), (3,4), (4,5), (6,5),
+        (6,14), (14,15), (6, 15),  (5,7), (7, 8), (7,13), (12,8),
+        (8,13), (11,12), (11,9), (13,11), (9,13), (13,10) ]
+
+    # creates the graph
+    graph = Graph.Graph()
+    for head, tail in edges:
+        graph.add_edge(head, tail)
+
+    # do a forward bfs from 1 at most to 20
+    print(graph.forw_bfs(1))
+
+This will print the nodes in some breadth first order::
+
+    [1, 2, 3, 4, 5, 7, 8, 13, 11, 10, 12, 9]
+
+If we wanted to get the hop-distance from node 1 to node 8
+we coud write::
+
+    print(graph.get_hops(1, 8))
+
+This will print the following::
+
+    [(1, 0), (2, 1), (3, 1), (4, 2), (5, 3), (7, 4), (8, 5)]
+
+Node 1 is at 0 hops since it is the starting node, nodes 2,3 are 1 hop away ...
+node 8 is 5 hops away. To find the shortest distance between two nodes you
+can use::
+
+    print(GraphAlgo.shortest_path(graph, 1, 12))
+
+It will print the nodes on one (if there are more) the shortest paths::
+
+    [1, 2, 4, 5, 7, 13, 11, 12]
+
+To display the graph we can use the GraphViz backend::
+
+    dot = Dot.Dot(graph)
+
+    # display the graph on the monitor
+    dot.display()
+
+    # save it in an image file
+    dot.save_img(file_name='graph', file_type='gif')
+
+
+
+..
+  @author: U{Istvan Albert<http://www.personal.psu.edu/staff/i/u/iua1/>}
+
+  @license:  MIT License
+
+  Copyright (c) 2004 Istvan Albert unless otherwise noted.
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+  and associated documentation files (the "Software"), to deal in the Software without restriction,
+  including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+  and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do
+  so.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+  INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+  PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+  FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+  ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+  @requires: Python 2.3 or higher
+
+  @newfield contributor: Contributors:
+  @contributor: U{Reka Albert <http://www.phys.psu.edu/~ralbert/>}
+
+'''
+import pkg_resources
+__version__ = pkg_resources.require('altgraph')[0].version
+
+class GraphError(ValueError):
+    pass
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph_tests/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph_tests/__init__.py
new file mode 100644
index 0000000..6890389
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph_tests/__init__.py
@@ -0,0 +1 @@
+""" altgraph tests """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph_tests/test_altgraph.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph_tests/test_altgraph.py
new file mode 100644
index 0000000..2ca6b25
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph_tests/test_altgraph.py
@@ -0,0 +1,45 @@
+#!/usr/bin/env py.test
+import os
+import sys
+
+from altgraph import Graph, GraphAlgo
+import unittest
+
+class BasicTests (unittest.TestCase):
+    def setUp(self):
+        self.edges = [
+            (1,2), (2,4), (1,3), (2,4), (3,4), (4,5), (6,5), (6,14), (14,15),
+            (6, 15), (5,7), (7, 8), (7,13), (12,8), (8,13), (11,12), (11,9),
+            (13,11), (9,13), (13,10)
+        ]
+
+        # these are the edges
+        self.store = {}
+        self.g = Graph.Graph()
+        for head, tail in self.edges:
+            self.store[head] = self.store[tail] = None
+            self.g.add_edge(head, tail)
+
+    def test_num_edges(self):
+        # check the parameters
+        self.assertEqual(self.g.number_of_nodes(), len(self.store))
+        self.assertEqual(self.g.number_of_edges(), len(self.edges))
+
+    def test_forw_bfs(self):
+        # do a forward bfs
+        self.assertEqual( self.g.forw_bfs(1),
+                [1, 2, 3, 4, 5, 7, 8, 13, 11, 10, 12, 9])
+
+
+    def test_get_hops(self):
+        # diplay the hops and hop numbers between nodes
+        self.assertEqual(self.g.get_hops(1, 8),
+                [(1, 0), (2, 1), (3, 1), (4, 2), (5, 3), (7, 4), (8, 5)])
+
+    def test_shortest_path(self):
+        self.assertEqual(GraphAlgo.shortest_path(self.g, 1, 12),
+                [1, 2, 4, 5, 7, 13, 11, 12])
+
+
+if __name__ == "__main__":  # pragma: no cover
+    unittest.main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph_tests/test_dot.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph_tests/test_dot.py
new file mode 100644
index 0000000..83993da
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph_tests/test_dot.py
@@ -0,0 +1,370 @@
+import unittest
+import os
+
+from altgraph import Dot
+from altgraph import Graph
+from altgraph import GraphError
+
+
+class TestDot (unittest.TestCase):
+
+    def test_constructor(self):
+        g = Graph.Graph([
+                (1,2),
+                (1,3),
+                (1,4),
+                (2,4),
+                (2,6),
+                (2,7),
+                (7,4),
+                (6,1),
+            ]
+        )
+
+        dot = Dot.Dot(g)
+
+        self.assertEqual(dot.name, 'G')
+        self.assertEqual(dot.attr, {})
+        self.assertEqual(dot.temp_dot, 'tmp_dot.dot')
+        self.assertEqual(dot.temp_neo, 'tmp_neo.dot')
+        self.assertEqual(dot.dot, 'dot')
+        self.assertEqual(dot.dotty, 'dotty')
+        self.assertEqual(dot.neato, 'neato')
+        self.assertEqual(dot.type, 'digraph')
+
+        self.assertEqual(dot.nodes, dict([(x, {}) for x in g]))
+
+        edges = {}
+        for head in g:
+            edges[head] = {}
+            for tail in g.out_nbrs(head):
+                edges[head][tail] = {}
+
+        self.assertEqual(dot.edges[1], edges[1])
+        self.assertEqual(dot.edges, edges)
+
+
+        dot = Dot.Dot(g, nodes=[1,2],
+                edgefn=lambda node: list(sorted(g.out_nbrs(node)))[:-1],
+                nodevisitor=lambda node: {'label': node},
+                edgevisitor=lambda head, tail: {'label': (head, tail) },
+                name="testgraph",
+                dot='/usr/local/bin/dot',
+                dotty='/usr/local/bin/dotty',
+                neato='/usr/local/bin/neato',
+                graphtype="graph")
+
+        self.assertEqual(dot.name, 'testgraph')
+        self.assertEqual(dot.attr, {})
+        self.assertEqual(dot.temp_dot, 'tmp_dot.dot')
+        self.assertEqual(dot.temp_neo, 'tmp_neo.dot')
+        self.assertEqual(dot.dot, '/usr/local/bin/dot')
+        self.assertEqual(dot.dotty, '/usr/local/bin/dotty')
+        self.assertEqual(dot.neato, '/usr/local/bin/neato')
+        self.assertEqual(dot.type, 'graph')
+
+        self.assertEqual(dot.nodes, dict([(x, {'label': x}) for x in [1,2]]))
+
+        edges = {}
+        for head in [1,2]:
+            edges[head] = {}
+            for tail in list(sorted(g.out_nbrs(head)))[:-1]:
+                if tail not in [1,2]: continue
+                edges[head][tail] = {'label': (head, tail) }
+
+        self.assertEqual(dot.edges[1], edges[1])
+        self.assertEqual(dot.edges, edges)
+
+        self.assertRaises(GraphError, Dot.Dot, g, nodes=[1,2, 9])
+
+    def test_style(self):
+        g = Graph.Graph([])
+
+        dot = Dot.Dot(g)
+
+        self.assertEqual(dot.attr, {})
+
+        dot.style(key='value')
+        self.assertEqual(dot.attr, {'key': 'value'})
+
+        dot.style(key2='value2')
+        self.assertEqual(dot.attr, {'key2': 'value2'})
+
+    def test_node_style(self):
+        g = Graph.Graph([
+                (1,2),
+                (1,3),
+                (1,4),
+                (2,4),
+                (2,6),
+                (2,7),
+                (7,4),
+                (6,1),
+            ]
+        )
+
+        dot = Dot.Dot(g)
+
+        self.assertEqual(dot.nodes[1], {})
+
+        dot.node_style(1, key='value')
+        self.assertEqual(dot.nodes[1], {'key': 'value'})
+
+        dot.node_style(1, key2='value2')
+        self.assertEqual(dot.nodes[1], {'key2': 'value2'})
+        self.assertEqual(dot.nodes[2], {})
+
+        dot.all_node_style(key3='value3')
+        for n in g:
+            self.assertEqual(dot.nodes[n], {'key3': 'value3'})
+
+        self.assertTrue(9 not in dot.nodes)
+        dot.node_style(9, key='value')
+        self.assertEqual(dot.nodes[9], {'key': 'value'})
+
+    def test_edge_style(self):
+        g = Graph.Graph([
+                (1,2),
+                (1,3),
+                (1,4),
+                (2,4),
+                (2,6),
+                (2,7),
+                (7,4),
+                (6,1),
+            ]
+        )
+
+        dot = Dot.Dot(g)
+
+        self.assertEqual(dot.edges[1][2], {})
+        dot.edge_style(1,2, foo='bar')
+        self.assertEqual(dot.edges[1][2], {'foo': 'bar'})
+
+        dot.edge_style(1,2, foo2='2bar')
+        self.assertEqual(dot.edges[1][2], {'foo2': '2bar'})
+
+        self.assertEqual(dot.edges[1][3], {})
+
+        self.assertFalse(6 in dot.edges[1])
+        dot.edge_style(1,6, foo2='2bar')
+        self.assertEqual(dot.edges[1][6], {'foo2': '2bar'})
+
+        self.assertRaises(GraphError, dot.edge_style, 1, 9, a=1)
+        self.assertRaises(GraphError, dot.edge_style, 9, 1, a=1)
+
+
+    def test_iter(self):
+        g = Graph.Graph([
+                (1,2),
+                (1,3),
+                (1,4),
+                (2,4),
+                (2,6),
+                (2,7),
+                (7,4),
+                (6,1),
+            ]
+        )
+
+        dot = Dot.Dot(g)
+        dot.style(graph="foobar")
+        dot.node_style(1, key='value')
+        dot.node_style(2, key='another', key2='world')
+        dot.edge_style(1,4, key1='value1', key2='value2')
+        dot.edge_style(2,4, key1='valueA')
+
+        self.assertEqual(list(iter(dot)), list(dot.iterdot()))
+
+        for item in dot.iterdot():
+            self.assertTrue(isinstance(item, str))
+
+        first = list(dot.iterdot())[0]
+        self.assertEqual(first, "digraph %s {\n"%(dot.name,))
+
+        dot.type = 'graph'
+        first = list(dot.iterdot())[0]
+        self.assertEqual(first, "graph %s {\n"%(dot.name,))
+
+        dot.type = 'foo'
+        self.assertRaises(GraphError, list, dot.iterdot())
+        dot.type = 'digraph'
+
+        self.assertEqual(list(dot), [
+            'digraph G {\n',
+              'graph="foobar";',
+              '\n',
+
+            '\t"1" [',
+              'key="value",',
+            '];\n',
+
+            '\t"2" [',
+              'key="another",',
+              'key2="world",',
+            '];\n',
+
+            '\t"3" [',
+            '];\n',
+
+            '\t"4" [',
+            '];\n',
+
+            '\t"6" [',
+            '];\n',
+
+            '\t"7" [',
+            '];\n',
+
+            '\t"1" -> "2" [',
+            '];\n',
+
+            '\t"1" -> "3" [',
+            '];\n',
+
+            '\t"1" -> "4" [',
+              'key1="value1",',
+              'key2="value2",',
+            '];\n',
+
+             '\t"2" -> "4" [',
+               'key1="valueA",',
+             '];\n',
+
+             '\t"2" -> "6" [',
+             '];\n',
+
+             '\t"2" -> "7" [',
+             '];\n',
+
+             '\t"6" -> "1" [',
+             '];\n',
+
+             '\t"7" -> "4" [',
+             '];\n',
+           '}\n'])
+
+
+    def test_save(self):
+        g = Graph.Graph([
+                (1,2),
+                (1,3),
+                (1,4),
+                (2,4),
+                (2,6),
+                (2,7),
+                (7,4),
+                (6,1),
+            ]
+        )
+
+        dot = Dot.Dot(g)
+        dot.style(graph="foobar")
+        dot.node_style(1, key='value')
+        dot.node_style(2, key='another', key2='world')
+        dot.edge_style(1,4, key1='value1', key2='value2')
+        dot.edge_style(2,4, key1='valueA')
+
+        fn = 'test_dot.dot'
+        self.assertTrue(not os.path.exists(fn))
+
+        try:
+            dot.save_dot(fn)
+
+            fp = open(fn, 'r')
+            data = fp.read()
+            fp.close()
+            self.assertEqual(data, ''.join(dot))
+
+        finally:
+            if os.path.exists(fn):
+                os.unlink(fn)
+
+
+    def test_img(self):
+        g = Graph.Graph([
+                (1,2),
+                (1,3),
+                (1,4),
+                (2,4),
+                (2,6),
+                (2,7),
+                (7,4),
+                (6,1),
+            ]
+        )
+
+        dot = Dot.Dot(g, dot='/usr/local/bin/!!dot', dotty='/usr/local/bin/!!dotty', neato='/usr/local/bin/!!neato')
+        dot.style(size='10,10', rankdir='RL', page='5, 5' , ranksep=0.75)
+        dot.node_style(1, label='BASE_NODE',shape='box', color='blue')
+        dot.node_style(2, style='filled', fillcolor='red')
+        dot.edge_style(1,4, style='dotted')
+        dot.edge_style(2,4, arrowhead='dot', label='binds', labelangle='90')
+
+        system_cmds = []
+        def fake_system(cmd):
+            system_cmds.append(cmd)
+            return None
+
+        try:
+            real_system = os.system
+            os.system = fake_system
+
+            system_cmds = []
+            dot.save_img('foo')
+            self.assertEqual(system_cmds, ['/usr/local/bin/!!dot -Tgif tmp_dot.dot -o foo.gif'])
+
+            system_cmds = []
+            dot.save_img('foo', file_type='jpg')
+            self.assertEqual(system_cmds, ['/usr/local/bin/!!dot -Tjpg tmp_dot.dot -o foo.jpg'])
+
+            system_cmds = []
+            dot.save_img('bar', file_type='jpg', mode='neato')
+            self.assertEqual(system_cmds, [
+                '/usr/local/bin/!!neato -o tmp_dot.dot tmp_neo.dot',
+                '/usr/local/bin/!!dot -Tjpg tmp_dot.dot -o bar.jpg',
+            ])
+
+            system_cmds = []
+            dot.display()
+            self.assertEqual(system_cmds, [
+                '/usr/local/bin/!!dotty tmp_dot.dot'
+            ])
+
+            system_cmds = []
+            dot.display(mode='neato')
+            self.assertEqual(system_cmds, [
+                '/usr/local/bin/!!neato -o tmp_dot.dot tmp_neo.dot',
+                '/usr/local/bin/!!dotty tmp_dot.dot'
+            ])
+
+        finally:
+            if os.path.exists(dot.temp_dot):
+                os.unlink(dot.temp_dot)
+            if os.path.exists(dot.temp_neo):
+                os.unlink(dot.temp_neo)
+            os.system = real_system
+
+        if os.path.exists('/usr/local/bin/dot') and os.path.exists('/usr/local/bin/neato'):
+            try:
+                dot.dot='/usr/local/bin/dot'
+                dot.neato='/usr/local/bin/neato'
+                self.assertFalse(os.path.exists('foo.gif'))
+                dot.save_img('foo')
+                self.assertTrue(os.path.exists('foo.gif'))
+                os.unlink('foo.gif')
+
+                self.assertFalse(os.path.exists('foo.gif'))
+                dot.save_img('foo', mode='neato')
+                self.assertTrue(os.path.exists('foo.gif'))
+                os.unlink('foo.gif')
+
+            finally:
+                if os.path.exists(dot.temp_dot):
+                    os.unlink(dot.temp_dot)
+                if os.path.exists(dot.temp_neo):
+                    os.unlink(dot.temp_neo)
+
+
+if __name__ == "__main__": # pragma: no cover
+    unittest.main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph_tests/test_graph.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph_tests/test_graph.py
new file mode 100644
index 0000000..553549f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph_tests/test_graph.py
@@ -0,0 +1,644 @@
+import unittest
+
+from altgraph import GraphError
+from altgraph.Graph import Graph
+
+class TestGraph (unittest.TestCase):
+
+    def test_nodes(self):
+        graph = Graph()
+
+        self.assertEqual(graph.node_list(), [])
+
+        o1 = object()
+        o1b = object()
+        o2 = object()
+        graph.add_node(1, o1)
+        graph.add_node(1, o1b)
+        graph.add_node(2, o2)
+        graph.add_node(3)
+
+        self.assertRaises(TypeError, graph.add_node, [])
+
+        self.assertTrue(graph.node_data(1) is o1)
+        self.assertTrue(graph.node_data(2) is o2)
+        self.assertTrue(graph.node_data(3) is None)
+
+        self.assertTrue(1 in graph)
+        self.assertTrue(2 in graph)
+        self.assertTrue(3 in graph)
+
+        self.assertEqual(graph.number_of_nodes(), 3)
+        self.assertEqual(graph.number_of_hidden_nodes(), 0)
+        self.assertEqual(graph.hidden_node_list(), [])
+        self.assertEqual(list(sorted(graph)), [1, 2, 3])
+
+        graph.hide_node(1)
+        graph.hide_node(2)
+        graph.hide_node(3)
+
+
+        self.assertEqual(graph.number_of_nodes(), 0)
+        self.assertEqual(graph.number_of_hidden_nodes(), 3)
+        self.assertEqual(list(sorted(graph.hidden_node_list())), [1, 2, 3])
+
+        self.assertFalse(1 in graph)
+        self.assertFalse(2 in graph)
+        self.assertFalse(3 in graph)
+
+        graph.add_node(1)
+        self.assertFalse(1 in graph)
+
+        graph.restore_node(1)
+        self.assertTrue(1 in graph)
+        self.assertFalse(2 in graph)
+        self.assertFalse(3 in graph)
+
+        graph.restore_all_nodes()
+        self.assertTrue(1 in graph)
+        self.assertTrue(2 in graph)
+        self.assertTrue(3 in graph)
+
+        self.assertEqual(list(sorted(graph.node_list())), [1, 2, 3])
+
+        v = graph.describe_node(1)
+        self.assertEqual(v, (1, o1, [], []))
+
+    def test_edges(self):
+        graph = Graph()
+        graph.add_node(1)
+        graph.add_node(2)
+        graph.add_node(3)
+        graph.add_node(4)
+        graph.add_node(5)
+
+        self.assertTrue(isinstance(graph.edge_list(), list))
+
+        graph.add_edge(1, 2)
+        graph.add_edge(4, 5, 'a')
+
+        self.assertRaises(GraphError, graph.add_edge, 'a', 'b', create_nodes=False)
+
+        self.assertEqual(graph.number_of_hidden_edges(), 0)
+        self.assertEqual(graph.number_of_edges(), 2)
+        e = graph.edge_by_node(1, 2)
+        self.assertTrue(isinstance(e, int))
+        graph.hide_edge(e)
+        self.assertEqual(graph.number_of_hidden_edges(), 1)
+        self.assertEqual(graph.number_of_edges(), 1)
+        e2 = graph.edge_by_node(1, 2)
+        self.assertTrue(e2 is None)
+
+        graph.restore_edge(e)
+        e2 = graph.edge_by_node(1, 2)
+        self.assertEqual(e, e2)
+        self.assertEqual(graph.number_of_hidden_edges(), 0)
+
+        self.assertEqual(graph.number_of_edges(), 2)
+
+        e1 = graph.edge_by_node(1, 2)
+        e2 = graph.edge_by_node(4, 5)
+        graph.hide_edge(e1)
+        graph.hide_edge(e2)
+
+        self.assertEqual(graph.number_of_edges(), 0)
+        graph.restore_all_edges()
+        self.assertEqual(graph.number_of_edges(), 2)
+
+        self.assertEqual(graph.edge_by_id(e1), (1,2))
+        self.assertRaises(GraphError, graph.edge_by_id, (e1+1)*(e2+1)+1)
+
+        self.assertEqual(list(sorted(graph.edge_list())), [e1, e2])
+
+        self.assertEqual(graph.describe_edge(e1), (e1, 1, 1, 2))
+        self.assertEqual(graph.describe_edge(e2), (e2, 'a', 4, 5))
+
+        self.assertEqual(graph.edge_data(e1), 1)
+        self.assertEqual(graph.edge_data(e2), 'a')
+
+        self.assertEqual(graph.head(e2), 4)
+        self.assertEqual(graph.tail(e2), 5)
+
+        graph.add_edge(1, 3)
+        graph.add_edge(1, 5)
+        graph.add_edge(4, 1)
+
+        self.assertEqual(list(sorted(graph.out_nbrs(1))), [2, 3, 5])
+        self.assertEqual(list(sorted(graph.inc_nbrs(1))), [4])
+        self.assertEqual(list(sorted(graph.inc_nbrs(5))), [1, 4])
+        self.assertEqual(list(sorted(graph.all_nbrs(1))), [2, 3, 4, 5])
+
+        graph.add_edge(5, 1)
+        self.assertEqual(list(sorted(graph.all_nbrs(5))), [1, 4])
+
+        self.assertEqual(graph.out_degree(1), 3)
+        self.assertEqual(graph.inc_degree(2), 1)
+        self.assertEqual(graph.inc_degree(5), 2)
+        self.assertEqual(graph.all_degree(5), 3)
+
+        v = graph.out_edges(4)
+        self.assertTrue(isinstance(v, list))
+        self.assertEqual(graph.edge_by_id(v[0]), (4, 5))
+
+        v = graph.out_edges(1)
+        for e in v:
+            self.assertEqual(graph.edge_by_id(e)[0], 1)
+
+        v = graph.inc_edges(1)
+        self.assertTrue(isinstance(v, list))
+        self.assertEqual(graph.edge_by_id(v[0]), (4, 1))
+
+        v = graph.inc_edges(5)
+        for e in v:
+            self.assertEqual(graph.edge_by_id(e)[1], 5)
+
+        v = graph.all_edges(5)
+        for e in v:
+            self.assertTrue(graph.edge_by_id(e)[1] == 5 or graph.edge_by_id(e)[0] == 5)
+
+        e1 = graph.edge_by_node(1, 2)
+        self.assertTrue(isinstance(e1, int))
+        graph.hide_node(1)
+        self.assertRaises(GraphError, graph.edge_by_node, 1, 2)
+        graph.restore_node(1)
+        e2 = graph.edge_by_node(1, 2)
+        self.assertEqual(e1, e2)
+
+
+
+    def test_toposort(self):
+        graph = Graph()
+        graph.add_node(1)
+        graph.add_node(2)
+        graph.add_node(3)
+        graph.add_node(4)
+        graph.add_node(5)
+
+        graph.add_edge(1, 2)
+        graph.add_edge(1, 3)
+        graph.add_edge(2, 4)
+        graph.add_edge(3, 5)
+
+        ok, result = graph.forw_topo_sort()
+        self.assertTrue(ok)
+        for idx in range(1, 6):
+            self.assertTrue(idx in result)
+
+        self.assertTrue(result.index(1) < result.index(2))
+        self.assertTrue(result.index(1) < result.index(3))
+        self.assertTrue(result.index(2) < result.index(4))
+        self.assertTrue(result.index(3) < result.index(5))
+
+        ok, result = graph.back_topo_sort()
+        self.assertTrue(ok)
+        for idx in range(1, 6):
+            self.assertTrue(idx in result)
+        self.assertTrue(result.index(2) < result.index(1))
+        self.assertTrue(result.index(3) < result.index(1))
+        self.assertTrue(result.index(4) < result.index(2))
+        self.assertTrue(result.index(5) < result.index(3))
+
+
+        # Same graph as before, but with edges
+        # reversed, which means we should get
+        # the same results as before if using
+        # back_topo_sort rather than forw_topo_sort
+        # (and v.v.)
+
+        graph = Graph()
+        graph.add_node(1)
+        graph.add_node(2)
+        graph.add_node(3)
+        graph.add_node(4)
+        graph.add_node(5)
+
+        graph.add_edge(2, 1)
+        graph.add_edge(3, 1)
+        graph.add_edge(4, 2)
+        graph.add_edge(5, 3)
+
+        ok, result = graph.back_topo_sort()
+        self.assertTrue(ok)
+        for idx in range(1, 6):
+            self.assertTrue(idx in result)
+
+        self.assertTrue(result.index(1) < result.index(2))
+        self.assertTrue(result.index(1) < result.index(3))
+        self.assertTrue(result.index(2) < result.index(4))
+        self.assertTrue(result.index(3) < result.index(5))
+
+        ok, result = graph.forw_topo_sort()
+        self.assertTrue(ok)
+        for idx in range(1, 6):
+            self.assertTrue(idx in result)
+        self.assertTrue(result.index(2) < result.index(1))
+        self.assertTrue(result.index(3) < result.index(1))
+        self.assertTrue(result.index(4) < result.index(2))
+        self.assertTrue(result.index(5) < result.index(3))
+
+
+        # Create a cycle
+        graph.add_edge(1, 5)
+        ok, result = graph.forw_topo_sort()
+        self.assertFalse(ok)
+        ok, result = graph.back_topo_sort()
+        self.assertFalse(ok)
+
+    def test_bfs_subgraph(self):
+        graph = Graph()
+        graph.add_edge(1, 2)
+        graph.add_edge(1, 4)
+        graph.add_edge(2, 4)
+        graph.add_edge(4, 8)
+        graph.add_edge(4, 9)
+        graph.add_edge(4, 10)
+        graph.add_edge(8, 10)
+
+        subgraph = graph.forw_bfs_subgraph(10)
+        self.assertTrue(isinstance(subgraph, Graph))
+        self.assertEqual(subgraph.number_of_nodes(), 1)
+        self.assertTrue(10 in subgraph)
+        self.assertEqual(subgraph.number_of_edges(), 0)
+
+        subgraph = graph.forw_bfs_subgraph(4)
+        self.assertTrue(isinstance(subgraph, Graph))
+        self.assertEqual(subgraph.number_of_nodes(), 4)
+        self.assertTrue(4 in subgraph)
+        self.assertTrue(8 in subgraph)
+        self.assertTrue(9 in subgraph)
+        self.assertTrue(10 in subgraph)
+        self.assertEqual(subgraph.number_of_edges(), 4)
+        e = subgraph.edge_by_node(4, 8)
+        e = subgraph.edge_by_node(4, 9)
+        e = subgraph.edge_by_node(4, 10)
+        e = subgraph.edge_by_node(8, 10)
+
+        # same graph as before, but switch around
+        # edges. This results in the same test results
+        # but now for back_bfs_subgraph rather than
+        # forw_bfs_subgraph
+
+        graph = Graph()
+        graph.add_edge(2, 1)
+        graph.add_edge(4, 1)
+        graph.add_edge(4, 2)
+        graph.add_edge(8, 4)
+        graph.add_edge(9, 4)
+        graph.add_edge(10, 4)
+        graph.add_edge(10, 8)
+
+        subgraph = graph.back_bfs_subgraph(10)
+        self.assertTrue(isinstance(subgraph, Graph))
+        self.assertEqual(subgraph.number_of_nodes(), 1)
+        self.assertTrue(10 in subgraph)
+        self.assertEqual(subgraph.number_of_edges(), 0)
+
+        subgraph = graph.back_bfs_subgraph(4)
+        self.assertTrue(isinstance(subgraph, Graph))
+        self.assertEqual(subgraph.number_of_nodes(), 4)
+        self.assertTrue(4 in subgraph)
+        self.assertTrue(8 in subgraph)
+        self.assertTrue(9 in subgraph)
+        self.assertTrue(10 in subgraph)
+        self.assertEqual(subgraph.number_of_edges(), 4)
+        e = subgraph.edge_by_node(4, 8)
+        e = subgraph.edge_by_node(4, 9)
+        e = subgraph.edge_by_node(4, 10)
+        e = subgraph.edge_by_node(8, 10)
+
+    def test_iterdfs(self):
+        graph = Graph()
+        graph.add_edge("1", "1.1")
+        graph.add_edge("1", "1.2")
+        graph.add_edge("1", "1.3")
+        graph.add_edge("1.1", "1.1.1")
+        graph.add_edge("1.1", "1.1.2")
+        graph.add_edge("1.2", "1.2.1")
+        graph.add_edge("1.2", "1.2.2")
+        graph.add_edge("1.2.2", "1.2.2.1")
+        graph.add_edge("1.2.2", "1.2.2.2")
+        graph.add_edge("1.2.2", "1.2.2.3")
+
+        result = list(graph.iterdfs("1"))
+        self.assertEqual(result, [
+            '1', '1.3', '1.2', '1.2.2', '1.2.2.3', '1.2.2.2',
+            '1.2.2.1', '1.2.1', '1.1', '1.1.2', '1.1.1'
+        ])
+        result = list(graph.iterdfs("1", "1.2.1"))
+        self.assertEqual(result, [
+            '1', '1.3', '1.2', '1.2.2', '1.2.2.3', '1.2.2.2',
+            '1.2.2.1', '1.2.1'
+        ])
+
+        result = graph.forw_dfs("1")
+        self.assertEqual(result, [
+            '1', '1.3', '1.2', '1.2.2', '1.2.2.3', '1.2.2.2',
+            '1.2.2.1', '1.2.1', '1.1', '1.1.2', '1.1.1'
+        ])
+        result = graph.forw_dfs("1", "1.2.1")
+        self.assertEqual(result, [
+            '1', '1.3', '1.2', '1.2.2', '1.2.2.3', '1.2.2.2',
+            '1.2.2.1', '1.2.1'
+        ])
+
+        graph = Graph()
+        graph.add_edge("1.1", "1")
+        graph.add_edge("1.2", "1")
+        graph.add_edge("1.3", "1")
+        graph.add_edge("1.1.1", "1.1")
+        graph.add_edge("1.1.2", "1.1")
+        graph.add_edge("1.2.1", "1.2")
+        graph.add_edge("1.2.2", "1.2")
+        graph.add_edge("1.2.2.1", "1.2.2")
+        graph.add_edge("1.2.2.2", "1.2.2")
+        graph.add_edge("1.2.2.3", "1.2.2")
+
+        result = list(graph.iterdfs("1", forward=False))
+        self.assertEqual(result, [
+            '1', '1.3', '1.2', '1.2.2', '1.2.2.3', '1.2.2.2',
+            '1.2.2.1', '1.2.1', '1.1', '1.1.2', '1.1.1'
+        ])
+        result = list(graph.iterdfs("1", "1.2.1", forward=False))
+        self.assertEqual(result, [
+            '1', '1.3', '1.2', '1.2.2', '1.2.2.3', '1.2.2.2',
+            '1.2.2.1', '1.2.1'
+        ])
+        result = graph.back_dfs("1")
+        self.assertEqual(result, [
+            '1', '1.3', '1.2', '1.2.2', '1.2.2.3', '1.2.2.2',
+            '1.2.2.1', '1.2.1', '1.1', '1.1.2', '1.1.1'
+        ])
+        result = graph.back_dfs("1", "1.2.1")
+        self.assertEqual(result, [
+            '1', '1.3', '1.2', '1.2.2', '1.2.2.3', '1.2.2.2',
+            '1.2.2.1', '1.2.1'
+        ])
+
+
+        # Introduce cyle:
+        graph.add_edge("1", "1.2")
+        result = list(graph.iterdfs("1", forward=False))
+        self.assertEqual(result, [
+            '1', '1.3', '1.2', '1.2.2', '1.2.2.3', '1.2.2.2',
+            '1.2.2.1', '1.2.1', '1.1', '1.1.2', '1.1.1'
+        ])
+
+        result = graph.back_dfs("1")
+        self.assertEqual(result, [
+            '1', '1.3', '1.2', '1.2.2', '1.2.2.3', '1.2.2.2',
+            '1.2.2.1', '1.2.1', '1.1', '1.1.2', '1.1.1'
+        ])
+
+
+    def test_iterdata(self):
+        graph = Graph()
+        graph.add_node("1", "I")
+        graph.add_node("1.1", "I.I")
+        graph.add_node("1.2", "I.II")
+        graph.add_node("1.3", "I.III")
+        graph.add_node("1.1.1", "I.I.I")
+        graph.add_node("1.1.2", "I.I.II")
+        graph.add_node("1.2.1", "I.II.I")
+        graph.add_node("1.2.2", "I.II.II")
+        graph.add_node("1.2.2.1", "I.II.II.I")
+        graph.add_node("1.2.2.2", "I.II.II.II")
+        graph.add_node("1.2.2.3", "I.II.II.III")
+
+        graph.add_edge("1", "1.1")
+        graph.add_edge("1", "1.2")
+        graph.add_edge("1", "1.3")
+        graph.add_edge("1.1", "1.1.1")
+        graph.add_edge("1.1", "1.1.2")
+        graph.add_edge("1.2", "1.2.1")
+        graph.add_edge("1.2", "1.2.2")
+        graph.add_edge("1.2.2", "1.2.2.1")
+        graph.add_edge("1.2.2", "1.2.2.2")
+        graph.add_edge("1.2.2", "1.2.2.3")
+
+        result = list(graph.iterdata("1", forward=True))
+        self.assertEqual(result, [
+            'I', 'I.III', 'I.II', 'I.II.II', 'I.II.II.III', 'I.II.II.II',
+            'I.II.II.I', 'I.II.I', 'I.I', 'I.I.II', 'I.I.I'
+        ])
+
+        result = list(graph.iterdata("1", end="1.2.1", forward=True))
+        self.assertEqual(result, [
+            'I', 'I.III', 'I.II', 'I.II.II', 'I.II.II.III', 'I.II.II.II',
+            'I.II.II.I', 'I.II.I'
+        ])
+
+        result = list(graph.iterdata("1", condition=lambda n: len(n) < 6, forward=True))
+        self.assertEqual(result, [
+            'I', 'I.III', 'I.II',
+            'I.I', 'I.I.I'
+        ])
+
+
+        # And the revese option:
+        graph = Graph()
+        graph.add_node("1", "I")
+        graph.add_node("1.1", "I.I")
+        graph.add_node("1.2", "I.II")
+        graph.add_node("1.3", "I.III")
+        graph.add_node("1.1.1", "I.I.I")
+        graph.add_node("1.1.2", "I.I.II")
+        graph.add_node("1.2.1", "I.II.I")
+        graph.add_node("1.2.2", "I.II.II")
+        graph.add_node("1.2.2.1", "I.II.II.I")
+        graph.add_node("1.2.2.2", "I.II.II.II")
+        graph.add_node("1.2.2.3", "I.II.II.III")
+
+        graph.add_edge("1.1", "1")
+        graph.add_edge("1.2", "1")
+        graph.add_edge("1.3", "1")
+        graph.add_edge("1.1.1", "1.1")
+        graph.add_edge("1.1.2", "1.1")
+        graph.add_edge("1.2.1", "1.2")
+        graph.add_edge("1.2.2", "1.2")
+        graph.add_edge("1.2.2.1", "1.2.2")
+        graph.add_edge("1.2.2.2", "1.2.2")
+        graph.add_edge("1.2.2.3", "1.2.2")
+
+        result = list(graph.iterdata("1", forward=False))
+        self.assertEqual(result, [
+            'I', 'I.III', 'I.II', 'I.II.II', 'I.II.II.III', 'I.II.II.II',
+            'I.II.II.I', 'I.II.I', 'I.I', 'I.I.II', 'I.I.I'
+        ])
+
+        result = list(graph.iterdata("1", end="1.2.1", forward=False))
+        self.assertEqual(result, [
+            'I', 'I.III', 'I.II', 'I.II.II', 'I.II.II.III', 'I.II.II.II',
+            'I.II.II.I', 'I.II.I'
+        ])
+
+        result = list(graph.iterdata("1", condition=lambda n: len(n) < 6, forward=False))
+        self.assertEqual(result, [
+            'I', 'I.III', 'I.II',
+            'I.I', 'I.I.I'
+        ])
+
+    def test_bfs(self):
+        graph = Graph()
+        graph.add_edge("1", "1.1")
+        graph.add_edge("1.1", "1.1.1")
+        graph.add_edge("1.1", "1.1.2")
+        graph.add_edge("1.1.2", "1.1.2.1")
+        graph.add_edge("1.1.2", "1.1.2.2")
+        graph.add_edge("1", "1.2")
+        graph.add_edge("1", "1.3")
+        graph.add_edge("1.2", "1.2.1")
+
+        self.assertEqual(graph.forw_bfs("1"),
+                ['1', '1.1', '1.2', '1.3', '1.1.1', '1.1.2', '1.2.1', '1.1.2.1', '1.1.2.2'])
+        self.assertEqual(graph.forw_bfs("1", "1.1.1"),
+                ['1', '1.1', '1.2', '1.3', '1.1.1'])
+
+
+        # And the "reverse" graph
+        graph = Graph()
+        graph.add_edge("1.1", "1")
+        graph.add_edge("1.1.1", "1.1")
+        graph.add_edge("1.1.2", "1.1")
+        graph.add_edge("1.1.2.1", "1.1.2")
+        graph.add_edge("1.1.2.2", "1.1.2")
+        graph.add_edge("1.2", "1")
+        graph.add_edge("1.3", "1")
+        graph.add_edge("1.2.1", "1.2")
+
+        self.assertEqual(graph.back_bfs("1"),
+                ['1', '1.1', '1.2', '1.3', '1.1.1', '1.1.2', '1.2.1', '1.1.2.1', '1.1.2.2'])
+        self.assertEqual(graph.back_bfs("1", "1.1.1"),
+                ['1', '1.1', '1.2', '1.3', '1.1.1'])
+
+
+
+        # check cycle handling
+        graph.add_edge("1", "1.2.1")
+        self.assertEqual(graph.back_bfs("1"),
+                ['1', '1.1', '1.2', '1.3', '1.1.1', '1.1.2', '1.2.1', '1.1.2.1', '1.1.2.2'])
+
+
+    def test_connected(self):
+        graph = Graph()
+        graph.add_node(1)
+        graph.add_node(2)
+        graph.add_node(3)
+        graph.add_node(4)
+
+        self.assertFalse(graph.connected())
+
+        graph.add_edge(1, 2)
+        graph.add_edge(3, 4)
+        self.assertFalse(graph.connected())
+
+        graph.add_edge(2, 3)
+        graph.add_edge(4, 1)
+        self.assertTrue(graph.connected())
+
+    def test_edges_complex(self):
+        g = Graph()
+        g.add_edge(1, 2)
+        e = g.edge_by_node(1,2)
+        g.hide_edge(e)
+        g.hide_node(2)
+        self.assertRaises(GraphError, g.restore_edge, e)
+
+        g.restore_all_edges()
+        self.assertRaises(GraphError, g.edge_by_id, e)
+
+    def test_clust_coef(self):
+        g = Graph()
+        g.add_edge(1, 2)
+        g.add_edge(1, 3)
+        g.add_edge(1, 4)
+        self.assertEqual(g.clust_coef(1), 0)
+
+        g.add_edge(2, 5)
+        g.add_edge(3, 5)
+        g.add_edge(4, 5)
+        self.assertEqual(g.clust_coef(1), 0)
+
+        g.add_edge(2, 3)
+        self.assertEqual(g.clust_coef(1), 1./6)
+        g.add_edge(2, 4)
+        self.assertEqual(g.clust_coef(1), 2./6)
+        g.add_edge(4, 2)
+        self.assertEqual(g.clust_coef(1), 3./6)
+
+        g.add_edge(2, 3)
+        g.add_edge(2, 4)
+        g.add_edge(3, 4)
+        g.add_edge(3, 2)
+        g.add_edge(4, 2)
+        g.add_edge(4, 3)
+        self.assertEqual(g.clust_coef(1), 1)
+
+
+    def test_get_hops(self):
+        graph = Graph()
+        graph.add_edge(1, 2)
+        graph.add_edge(1, 3)
+        graph.add_edge(2, 4)
+        graph.add_edge(4, 5)
+        graph.add_edge(5, 7)
+        graph.add_edge(7, 8)
+
+        self.assertEqual(graph.get_hops(1),
+            [(1, 0), (2, 1), (3, 1), (4, 2), (5, 3), (7, 4), (8, 5)])
+
+        self.assertEqual(graph.get_hops(1, 5),
+            [(1, 0), (2, 1), (3, 1), (4, 2), (5, 3)])
+
+        graph.add_edge(5, 1)
+        graph.add_edge(7, 1)
+        graph.add_edge(7, 4)
+
+        self.assertEqual(graph.get_hops(1),
+            [(1, 0), (2, 1), (3, 1), (4, 2), (5, 3), (7, 4), (8, 5)])
+
+        # And the reverse graph
+        graph = Graph()
+        graph.add_edge(2, 1)
+        graph.add_edge(3, 1)
+        graph.add_edge(4, 2)
+        graph.add_edge(5, 4)
+        graph.add_edge(7, 5)
+        graph.add_edge(8, 7)
+
+        self.assertEqual(graph.get_hops(1, forward=False),
+            [(1, 0), (2, 1), (3, 1), (4, 2), (5, 3), (7, 4), (8, 5)])
+
+        self.assertEqual(graph.get_hops(1, 5, forward=False),
+            [(1, 0), (2, 1), (3, 1), (4, 2), (5, 3)])
+
+        graph.add_edge(1, 5)
+        graph.add_edge(1, 7)
+        graph.add_edge(4, 7)
+
+        self.assertEqual(graph.get_hops(1, forward=False),
+            [(1, 0), (2, 1), (3, 1), (4, 2), (5, 3), (7, 4), (8, 5)])
+
+
+    def test_constructor(self):
+        graph = Graph(iter([
+                (1, 2),
+                (2, 3, 'a'),
+                (1, 3),
+                (3, 4),
+            ]))
+        self.assertEqual(graph.number_of_nodes(), 4)
+        self.assertEqual(graph.number_of_edges(), 4)
+        try:
+            graph.edge_by_node(1,2)
+            graph.edge_by_node(2,3)
+            graph.edge_by_node(1,3)
+            graph.edge_by_node(3,4)
+        except GraphError:
+            self.fail("Incorrect graph")
+
+        self.assertEqual(graph.edge_data(graph.edge_by_node(2, 3)), 'a')
+
+        self.assertRaises(GraphError, Graph, [(1,2,3,4)])
+
+if __name__ == "__main__": # pragma: no cover
+    unittest.main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph_tests/test_graphstat.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph_tests/test_graphstat.py
new file mode 100644
index 0000000..b628b6f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph_tests/test_graphstat.py
@@ -0,0 +1,70 @@
+import unittest
+
+from altgraph import GraphStat
+from altgraph import Graph
+import sys
+
+class TestDegreesDist (unittest.TestCase):
+
+    def test_simple(self):
+        a = Graph.Graph()
+        self.assertEqual(GraphStat.degree_dist(a), [])
+
+        a.add_node(1)
+        a.add_node(2)
+        a.add_node(3)
+
+        self.assertEqual(GraphStat.degree_dist(a), GraphStat._binning([0, 0, 0]))
+
+        for x in range(100):
+            a.add_node(x)
+
+        for x in range(1, 100):
+            for y in range(1, 50):
+                if x % y == 0:
+                    a.add_edge(x, y)
+
+        counts_inc = []
+        counts_out = []
+        for n in a:
+            counts_inc.append(a.inc_degree(n))
+            counts_out.append(a.out_degree(n))
+
+        self.assertEqual(GraphStat.degree_dist(a), GraphStat._binning(counts_out))
+        self.assertEqual(GraphStat.degree_dist(a, mode='inc'), GraphStat._binning(counts_inc))
+
+class TestBinning (unittest.TestCase):
+    def test_simple(self):
+
+        # Binning [0, 100) into 10 bins
+        a = list(range(100))
+        out = GraphStat._binning(a, limits=(0, 100), bin_num=10)
+
+        self.assertEqual(out,
+                [ (x*1.0, 10) for x in range(5, 100, 10) ])
+
+
+        # Check that outliers are ignored.
+        a = list(range(100))
+        out = GraphStat._binning(a, limits=(0, 90), bin_num=9)
+
+        self.assertEqual(out,
+                [ (x*1.0, 10) for x in range(5, 90, 10) ])
+
+
+        out = GraphStat._binning(a, limits=(0, 100), bin_num=15)
+        binSize = 100 / 15.0
+        result = [0]*15
+        for i in range(100):
+            bin = int(i/binSize)
+            try:
+                result[bin] += 1
+            except IndexError:
+                pass
+
+        result = [ (i * binSize + binSize/2, result[i]) for i in range(len(result))]
+
+        self.assertEqual(result, out)
+
+if __name__ == "__main__": # pragma: no cover
+    unittest.main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph_tests/test_graphutil.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph_tests/test_graphutil.py
new file mode 100644
index 0000000..c116623
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph_tests/test_graphutil.py
@@ -0,0 +1,140 @@
+import unittest
+from altgraph import GraphUtil
+from altgraph import Graph, GraphError
+
+class TestGraphUtil (unittest.TestCase):
+
+    def test_generate_random(self):
+        g =  GraphUtil.generate_random_graph(10, 50)
+        self.assertEqual(g.number_of_nodes(), 10)
+        self.assertEqual(g.number_of_edges(), 50)
+
+        seen = set()
+
+        for e in g.edge_list():
+            h, t = g.edge_by_id(e)
+            self.assertFalse(h == t)
+            self.assertTrue((h, t) not in seen)
+            seen.add((h, t))
+
+        g =  GraphUtil.generate_random_graph(5, 30, multi_edges=True)
+        self.assertEqual(g.number_of_nodes(), 5)
+        self.assertEqual(g.number_of_edges(), 30)
+
+        seen = set()
+
+        for e in g.edge_list():
+            h, t = g.edge_by_id(e)
+            self.assertFalse(h == t)
+            if (h, t) in seen:
+                break
+            seen.add((h, t))
+
+        else:
+            self.fail("no duplicates?")
+
+        g =  GraphUtil.generate_random_graph(5, 21, self_loops=True)
+        self.assertEqual(g.number_of_nodes(), 5)
+        self.assertEqual(g.number_of_edges(), 21)
+
+        seen = set()
+
+        for e in g.edge_list():
+            h, t = g.edge_by_id(e)
+            self.assertFalse((h, t) in seen)
+            if h == t:
+                break
+            seen.add((h, t))
+
+        else:
+            self.fail("no self loops?")
+
+        self.assertRaises(GraphError, GraphUtil.generate_random_graph, 5, 21)
+        g = GraphUtil.generate_random_graph(5, 21, True)
+        self.assertRaises(GraphError, GraphUtil.generate_random_graph, 5, 26, True)
+
+    def test_generate_scale_free(self):
+        graph = GraphUtil.generate_scale_free_graph(50, 10)
+        self.assertEqual(graph.number_of_nodes(), 500)
+
+        counts = {}
+        for node in graph:
+            degree = graph.inc_degree(node)
+            try:
+                counts[degree] += 1
+            except KeyError:
+                counts[degree] = 1
+
+        total_counts = sum(counts.values())
+        P = {}
+        for degree, count in counts.items():
+            P[degree] = count * 1.0 / total_counts
+
+        # XXX: use algoritm <http://stackoverflow.com/questions/3433486/how-to-do-exponential-and-logarithmic-curve-fitting-in-python-i-found-only-polyn>
+        # to check if P[degree] ~ degree ** G (for some G)
+
+        #print sorted(P.items())
+
+        #print sorted([(count, degree) for degree, count in counts.items()])
+
+        #self.fail("missing tests for GraphUtil.generate_scale_free_graph")
+
+    def test_filter_stack(self):
+        g = Graph.Graph()
+        g.add_node("1", "N.1")
+        g.add_node("1.1", "N.1.1")
+        g.add_node("1.1.1", "N.1.1.1")
+        g.add_node("1.1.2", "N.1.1.2")
+        g.add_node("1.1.3", "N.1.1.3")
+        g.add_node("1.1.1.1", "N.1.1.1.1")
+        g.add_node("1.1.1.2", "N.1.1.1.2")
+        g.add_node("1.1.2.1", "N.1.1.2.1")
+        g.add_node("1.1.2.2", "N.1.1.2.2")
+        g.add_node("1.1.2.3", "N.1.1.2.3")
+        g.add_node("2", "N.2")
+
+        g.add_edge("1", "1.1")
+        g.add_edge("1.1", "1.1.1")
+        g.add_edge("1.1", "1.1.2")
+        g.add_edge("1.1", "1.1.3")
+        g.add_edge("1.1.1", "1.1.1.1")
+        g.add_edge("1.1.1", "1.1.1.2")
+        g.add_edge("1.1.2", "1.1.2.1")
+        g.add_edge("1.1.2", "1.1.2.2")
+        g.add_edge("1.1.2", "1.1.2.3")
+
+        v, r, o =  GraphUtil.filter_stack(g, "1", [
+            lambda n: n != "N.1.1.1", lambda n: n != "N.1.1.2.3" ])
+
+        self.assertEqual(v,
+            set(["1", "1.1", "1.1.1", "1.1.2", "1.1.3",
+                "1.1.1.1", "1.1.1.2", "1.1.2.1", "1.1.2.2",
+                "1.1.2.3"]))
+        self.assertEqual(r, set([
+                "1.1.1", "1.1.2.3"]))
+
+        o.sort()
+        self.assertEqual(o,
+            [
+                ("1.1", "1.1.1.1"),
+                ("1.1", "1.1.1.2")
+            ])
+
+        v, r, o =  GraphUtil.filter_stack(g, "1", [
+            lambda n: n != "N.1.1.1", lambda n: n != "N.1.1.1.2" ])
+
+        self.assertEqual(v,
+            set(["1", "1.1", "1.1.1", "1.1.2", "1.1.3",
+                "1.1.1.1", "1.1.1.2", "1.1.2.1", "1.1.2.2",
+                "1.1.2.3"]))
+        self.assertEqual(r, set([
+                "1.1.1", "1.1.1.2"]))
+
+        self.assertEqual(o,
+            [
+                ("1.1", "1.1.1.1"),
+            ])
+
+
+if __name__ == "__main__": # pragma: no cover
+    unittest.main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph_tests/test_object_graph.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph_tests/test_object_graph.py
new file mode 100644
index 0000000..9035607
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/altgraph_tests/test_object_graph.py
@@ -0,0 +1,349 @@
+import unittest
+import sys
+from altgraph.ObjectGraph import ObjectGraph
+from altgraph.Graph import Graph
+
+try:
+    from StringIO import StringIO
+except ImportError:
+    from io import StringIO
+
+
+class Node (object):
+    def __init__(self, graphident):
+        self.graphident = graphident
+
+class SubNode (Node):
+    pass
+
+class ArgNode (object):
+    def __init__(self, graphident, *args, **kwds):
+        self.graphident = graphident
+        self.args = args
+        self.kwds = kwds
+
+    def __repr__(self):
+        return '<ArgNode %s>'%(self.graphident,)
+
+class TestObjectGraph (unittest.TestCase):
+
+    def test_constructor(self):
+        graph = ObjectGraph()
+        self.assertTrue(isinstance(graph, ObjectGraph))
+
+        g = Graph()
+        graph = ObjectGraph(g)
+        self.assertTrue(graph.graph is g)
+        self.assertEqual(graph.debug, 0)
+        self.assertEqual(graph.indent, 0)
+
+        graph = ObjectGraph(debug=5)
+        self.assertEqual(graph.debug, 5)
+
+    def test_repr(self):
+        graph = ObjectGraph()
+        self.assertEqual(repr(graph), '<ObjectGraph>')
+
+
+    def testNodes(self):
+        graph = ObjectGraph()
+        n1 = Node("n1")
+        n2 = Node("n2")
+        n3 = Node("n3")
+        n4 = Node("n4")
+
+        n1b = Node("n1")
+
+        self.assertTrue(graph.getIdent(graph)  is graph)
+        self.assertTrue(graph.getRawIdent(graph)  is graph)
+
+        graph.addNode(n1)
+        graph.addNode(n2)
+        graph.addNode(n3)
+
+        self.assertTrue(n1 in graph)
+        self.assertFalse(n4 in graph)
+        self.assertTrue("n1" in graph)
+        self.assertFalse("n4" in graph)
+
+        self.assertTrue(graph.findNode(n1) is n1)
+        self.assertTrue(graph.findNode(n1b) is n1)
+        self.assertTrue(graph.findNode(n2) is n2)
+        self.assertTrue(graph.findNode(n4) is None)
+        self.assertTrue(graph.findNode("n1") is n1)
+        self.assertTrue(graph.findNode("n2") is n2)
+        self.assertTrue(graph.findNode("n4") is None)
+
+        self.assertEqual(graph.getRawIdent(n1), "n1")
+        self.assertEqual(graph.getRawIdent(n1b), "n1")
+        self.assertEqual(graph.getRawIdent(n4), "n4")
+        self.assertEqual(graph.getRawIdent("n1"), None)
+
+        self.assertEqual(graph.getIdent(n1), "n1")
+        self.assertEqual(graph.getIdent(n1b), "n1")
+        self.assertEqual(graph.getIdent(n4), "n4")
+        self.assertEqual(graph.getIdent("n1"), "n1")
+
+        self.assertTrue(n3 in graph)
+        graph.removeNode(n3)
+        self.assertTrue(n3 not in graph)
+        graph.addNode(n3)
+        self.assertTrue(n3 in graph)
+
+        n = graph.createNode(SubNode, "n1")
+        self.assertTrue(n is n1)
+
+        n = graph.createNode(SubNode, "n8")
+        self.assertTrue(isinstance(n, SubNode))
+        self.assertTrue(n in graph)
+        self.assertTrue(graph.findNode("n8") is n)
+
+        n = graph.createNode(ArgNode, "args", 1, 2, 3, a='a', b='b')
+        self.assertTrue(isinstance(n, ArgNode))
+        self.assertTrue(n in graph)
+        self.assertTrue(graph.findNode("args") is n)
+        self.assertEqual(n.args, (1, 2, 3))
+        self.assertEqual(n.kwds, {'a':'a', 'b':'b'})
+
+    def testEdges(self):
+        graph = ObjectGraph()
+        n1 = graph.createNode(ArgNode, "n1", 1)
+        n2 = graph.createNode(ArgNode, "n2", 1)
+        n3 = graph.createNode(ArgNode, "n3", 1)
+        n4 = graph.createNode(ArgNode, "n4", 1)
+
+        graph.createReference(n1, n2, "n1-n2")
+        graph.createReference("n1", "n3", "n1-n3")
+        graph.createReference("n2", n3)
+
+        g = graph.graph
+        e = g.edge_by_node("n1", "n2")
+        self.assertTrue(e is not None)
+        self.assertEqual(g.edge_data(e), "n1-n2")
+
+        e = g.edge_by_node("n1", "n3")
+        self.assertTrue(e is not None)
+        self.assertEqual(g.edge_data(e), "n1-n3")
+
+        e = g.edge_by_node("n2", "n3")
+        self.assertTrue(e is not None)
+        self.assertEqual(g.edge_data(e), None)
+
+        e = g.edge_by_node("n1", "n4")
+        self.assertTrue(e is None)
+
+        graph.removeReference(n1, n2)
+        e = g.edge_by_node("n1", "n2")
+        self.assertTrue(e is None)
+
+        graph.removeReference("n1", "n3")
+        e = g.edge_by_node("n1", "n3")
+        self.assertTrue(e is None)
+
+        graph.createReference(n1, n2, "foo")
+        e = g.edge_by_node("n1", "n2")
+        self.assertTrue(e is not None)
+        self.assertEqual(g.edge_data(e), "foo")
+
+
+    def test_flatten(self):
+        graph = ObjectGraph()
+        n1 = graph.createNode(ArgNode, "n1", 1)
+        n2 = graph.createNode(ArgNode, "n2", 2)
+        n3 = graph.createNode(ArgNode, "n3", 3)
+        n4 = graph.createNode(ArgNode, "n4", 4)
+        n5 = graph.createNode(ArgNode, "n5", 5)
+        n6 = graph.createNode(ArgNode, "n6", 6)
+        n7 = graph.createNode(ArgNode, "n7", 7)
+        n8 = graph.createNode(ArgNode, "n8", 8)
+
+        graph.createReference(graph, n1)
+        graph.createReference(graph, n7)
+        graph.createReference(n1, n2)
+        graph.createReference(n1, n4)
+        graph.createReference(n2, n3)
+        graph.createReference(n2, n5)
+        graph.createReference(n5, n6)
+        graph.createReference(n4, n6)
+        graph.createReference(n4, n2)
+
+        self.assertFalse(isinstance(graph.flatten(), list))
+
+        fl = list(graph.flatten())
+        self.assertTrue(n1 in fl)
+        self.assertTrue(n2 in fl)
+        self.assertTrue(n3 in fl)
+        self.assertTrue(n4 in fl)
+        self.assertTrue(n5 in fl)
+        self.assertTrue(n6 in fl)
+        self.assertTrue(n7 in fl)
+        self.assertFalse(n8 in fl)
+
+        fl = list(graph.flatten(start=n2))
+        self.assertFalse(n1 in fl)
+        self.assertTrue(n2 in fl)
+        self.assertTrue(n3 in fl)
+        self.assertFalse(n4 in fl)
+        self.assertTrue(n5 in fl)
+        self.assertTrue(n6 in fl)
+        self.assertFalse(n7 in fl)
+        self.assertFalse(n8 in fl)
+
+        graph.createReference(n1, n5)
+        fl = list(graph.flatten(lambda n: n.args[0] % 2 != 0))
+        self.assertTrue(n1 in fl)
+        self.assertFalse(n2 in fl)
+        self.assertFalse(n3 in fl)
+        self.assertFalse(n4 in fl)
+        self.assertTrue(n5 in fl)
+        self.assertFalse(n6 in fl)
+        self.assertTrue(n7 in fl)
+        self.assertFalse(n8 in fl)
+
+    def test_iter_nodes(self):
+        graph = ObjectGraph()
+        n1 = graph.createNode(ArgNode, "n1", 1)
+        n2 = graph.createNode(ArgNode, "n2", 2)
+        n3 = graph.createNode(ArgNode, "n3", 3)
+        n4 = graph.createNode(ArgNode, "n4", 4)
+        n5 = graph.createNode(ArgNode, "n5", 5)
+        n6 = graph.createNode(ArgNode, "n6", 5)
+
+        nodes = graph.nodes()
+        if sys.version[0] == '2':
+            self.assertTrue(hasattr(nodes, 'next'))
+        else:
+            self.assertTrue(hasattr(nodes, '__next__'))
+        self.assertTrue(hasattr(nodes, '__iter__'))
+
+        nodes = list(nodes)
+        self.assertEqual(len(nodes), 6)
+        self.assertTrue(n1 in nodes)
+        self.assertTrue(n2 in nodes)
+        self.assertTrue(n3 in nodes)
+        self.assertTrue(n4 in nodes)
+        self.assertTrue(n5 in nodes)
+        self.assertTrue(n6 in nodes)
+
+    def test_get_edges(self):
+        graph = ObjectGraph()
+        n1 = graph.createNode(ArgNode, "n1", 1)
+        n2 = graph.createNode(ArgNode, "n2", 2)
+        n3 = graph.createNode(ArgNode, "n3", 3)
+        n4 = graph.createNode(ArgNode, "n4", 4)
+        n5 = graph.createNode(ArgNode, "n5", 5)
+        n6 = graph.createNode(ArgNode, "n6", 5)
+
+        graph.createReference(n1, n2)
+        graph.createReference(n1, n3)
+        graph.createReference(n3, n1)
+        graph.createReference(n5, n1)
+        graph.createReference(n2, n4)
+        graph.createReference(n2, n5)
+        graph.createReference(n6, n2)
+
+        outs, ins = graph.get_edges(n1)
+
+        self.assertFalse(isinstance(outs, list))
+        self.assertFalse(isinstance(ins, list))
+
+        ins = list(ins)
+        outs = list(outs)
+
+
+        self.assertTrue(n1 not in outs)
+        self.assertTrue(n2 in outs)
+        self.assertTrue(n3 in outs)
+        self.assertTrue(n4 not in outs)
+        self.assertTrue(n5 not in outs)
+        self.assertTrue(n6 not in outs)
+
+        self.assertTrue(n1 not in ins)
+        self.assertTrue(n2 not in ins)
+        self.assertTrue(n3 in ins)
+        self.assertTrue(n4 not in ins)
+        self.assertTrue(n5 in ins)
+        self.assertTrue(n6 not in ins)
+
+    def test_filterStack(self):
+        graph = ObjectGraph()
+        n1 = graph.createNode(ArgNode, "n1", 0)
+        n11 = graph.createNode(ArgNode, "n1.1", 1)
+        n12 = graph.createNode(ArgNode, "n1.2", 0)
+        n111 = graph.createNode(ArgNode, "n1.1.1", 0)
+        n112 = graph.createNode(ArgNode, "n1.1.2",2)
+        n2 = graph.createNode(ArgNode, "n2", 0)
+        n3 = graph.createNode(ArgNode, "n2", 0)
+
+        graph.createReference(None, n1)
+        graph.createReference(None, n2)
+        graph.createReference(n1, n11)
+        graph.createReference(n1, n12)
+        graph.createReference(n11, n111)
+        graph.createReference(n11, n112)
+
+        self.assertTrue(n1 in graph)
+        self.assertTrue(n2 in graph)
+        self.assertTrue(n11 in graph)
+        self.assertTrue(n12 in graph)
+        self.assertTrue(n111 in graph)
+        self.assertTrue(n112 in graph)
+        self.assertTrue(n2 in graph)
+        self.assertTrue(n3 in graph)
+
+        visited, removes, orphans = graph.filterStack(
+                [lambda n: n.args[0] != 1, lambda n: n.args[0] != 2])
+
+        self.assertEqual(visited, 6)
+        self.assertEqual(removes, 2)
+        self.assertEqual(orphans, 1)
+
+        e = graph.graph.edge_by_node(n1.graphident, n111.graphident)
+        self.assertEqual(graph.graph.edge_data(e), "orphan")
+
+        self.assertTrue(n1 in graph)
+        self.assertTrue(n2 in graph)
+        self.assertTrue(n11 not in graph)
+        self.assertTrue(n12 in graph)
+        self.assertTrue(n111 in graph)
+        self.assertTrue(n112 not in graph)
+        self.assertTrue(n2 in graph)
+        self.assertTrue(n3 in graph)
+
+
+class TestObjectGraphIO (unittest.TestCase):
+    def setUp(self):
+        self._stdout = sys.stdout
+
+    def tearDown(self):
+        sys.stdout = self._stdout
+
+    def test_msg(self):
+        graph = ObjectGraph()
+
+        sys.stdout = fp = StringIO()
+        graph.msg(0, "foo")
+        self.assertEqual(fp.getvalue(), "foo \n")
+
+        sys.stdout = fp = StringIO()
+        graph.msg(5, "foo")
+        self.assertEqual(fp.getvalue(), "")
+
+        sys.stdout = fp = StringIO()
+        graph.debug = 10
+        graph.msg(5, "foo")
+        self.assertEqual(fp.getvalue(), "foo \n")
+
+        sys.stdout = fp = StringIO()
+        graph.msg(0, "foo", 1, "a")
+        self.assertEqual(fp.getvalue(), "foo 1 'a'\n")
+
+        sys.stdout = fp = StringIO()
+        graph.msgin(0, "hello", "world")
+        graph.msg(0, "test me")
+        graph.msgout(0, "bye bye")
+        self.assertEqual(fp.getvalue(), "hello 'world'\n  test me \nbye bye \n")
+
+
+if __name__ == "__main__": # pragma: no cover
+    unittest.main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/doc/changelog.rst b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/doc/changelog.rst
new file mode 100644
index 0000000..02fd412
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/doc/changelog.rst
@@ -0,0 +1,185 @@
+Release history
+===============
+
+0.12
+----
+
+- Added ``ObjectGraph.edgeData`` to retrieve the edge data
+  from a specific edge.
+
+- Added ``AltGraph.update_edge_data`` and ``ObjectGraph.updateEdgeData``
+  to update the data associated with a graph edge.
+
+0.11
+----
+
+- Stabilize the order of elements in dot file exports,
+  patch from bitbucket user 'pombredanne'.
+
+- Tweak setup.py file to remove dependency on distribute (but
+  keep the dependency on setuptools)
+
+
+0.10.2
+------
+
+- There where no classifiers in the package metadata due to a bug
+  in setup.py
+
+0.10.1
+------
+
+This is a bugfix release
+
+Bug fixes:
+
+- Issue #3: The source archive contains a README.txt
+  while the setup file refers to ReadMe.txt.
+
+  This is caused by a misfeature in distutils, as a
+  workaround I've renamed ReadMe.txt to README.txt
+  in the source tree and setup file.
+
+
+0.10
+-----
+
+This is a minor feature release
+
+Features:
+
+- Do not use "2to3" to support Python 3.
+
+  As a side effect of this altgraph now supports
+  Python 2.6 and later, and no longer supports
+  earlier releases of Python.
+
+- The order of attributes in the Dot output
+  is now always alphabetical.
+
+  With this change the output will be consistent
+  between runs and Python versions.
+
+0.9
+---
+
+This is a minor bugfix release
+
+Features:
+
+- Added ``altgraph.ObjectGraph.ObjectGraph.nodes``, a method
+  yielding all nodes in an object graph.
+
+Bugfixes:
+
+- The 0.8 release didn't work with py2app when using
+  python 3.x.
+
+
+0.8
+-----
+
+This is a minor feature release. The major new feature
+is a extensive set of unittests, which explains almost
+all other changes in this release.
+
+Bugfixes:
+
+- Installing failed with Python 2.5 due to using a distutils
+  class that isn't available in that version of Python
+  (issue #1 on the issue tracker)
+
+- ``altgraph.GraphStat.degree_dist`` now actually works
+
+- ``altgraph.Graph.add_edge(a, b, create_nodes=False)`` will
+  no longer create the edge when one of the nodes doesn't
+  exist.
+
+- ``altgraph.Graph.forw_topo_sort`` failed for some sparse graphs.
+
+- ``altgraph.Graph.back_topo_sort`` was completely broken in
+  previous releases.
+
+- ``altgraph.Graph.forw_bfs_subgraph`` now actually works.
+
+- ``altgraph.Graph.back_bfs_subgraph`` now actually works.
+
+- ``altgraph.Graph.iterdfs`` now returns the correct result
+  when the ``forward`` argument is ``False``.
+
+- ``altgraph.Graph.iterdata`` now returns the correct result
+  when the ``forward`` argument is ``False``.
+
+
+Features:
+
+- The ``altgraph.Graph`` constructor now accepts an argument
+  that contains 2- and 3-tuples instead of requireing that
+  all items have the same size. The (optional) argument can now
+  also be any iterator.
+
+- ``altgraph.Graph.Graph.add_node`` has no effect when you
+  add a hidden node.
+
+- The private method ``altgraph.Graph._bfs`` is no longer
+  present.
+
+- The private method ``altgraph.Graph._dfs`` is no longer
+  present.
+
+- ``altgraph.ObjectGraph`` now has a ``__contains__`` methods,
+  which means you can use the ``in`` operator to check if a
+  node is part of a graph.
+
+- ``altgraph.GraphUtil.generate_random_graph`` will raise
+  ``GraphError`` instead of looping forever when it is
+  impossible to create the requested graph.
+
+- ``altgraph.Dot.edge_style`` raises ``GraphError`` when
+  one of the nodes is not present in the graph. The method
+  silently added the tail in the past, but without ensuring
+  a consistent graph state.
+
+- ``altgraph.Dot.save_img`` now works when the mode is
+  ``"neato"``.
+
+0.7.2
+-----
+
+This is a minor bugfix release
+
+Bugfixes:
+
+- distutils didn't include the documentation subtree
+
+0.7.1
+-----
+
+This is a minor feature release
+
+Features:
+
+- Documentation is now generated using `sphinx <http://pypi.python.org/pypi/sphinx>`_
+  and can be viewed at <http://packages.python.org/altgraph>.
+
+- The repository has moved to bitbucket
+
+- ``altgraph.GraphStat.avg_hops`` is no longer present, the function had no
+  implementation and no specified behaviour.
+
+- the module ``altgraph.compat`` is gone, which means altgraph will no
+  longer work with Python 2.3.
+
+
+0.7.0
+-----
+
+This is a minor feature release.
+
+Features:
+
+- Support for Python 3
+
+- It is now possible to run tests using 'python setup.py test'
+
+  (The actual testsuite is still very minimal though)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/doc/conf.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/doc/conf.py
new file mode 100644
index 0000000..cd3fd99
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/doc/conf.py
@@ -0,0 +1,209 @@
+# -*- coding: utf-8 -*-
+#
+# altgraph documentation build configuration file, created by
+# sphinx-quickstart on Tue Aug 31 11:04:49 2010.
+#
+# This file is execfile()d with the current directory set to its containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys, os
+
+def get_version():
+    fn = os.path.join(
+            os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
+            'setup.cfg')
+    for ln in open(fn):
+        if ln.startswith('version'):
+            version = ln.split('=')[-1].strip()
+            return version
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#sys.path.append(os.path.abspath('.'))
+sys.path.insert(0,
+        os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+
+# -- General configuration -----------------------------------------------------
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = ['sphinx.ext.intersphinx', 'sphinx.ext.todo', 'sphinx.ext.autodoc' ]
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'altgraph'
+copyright = u'2010-2011, Ronald Oussoren, Bob Ippolito, 2004 Istvan Albert'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = get_version()
+# The full version, including alpha/beta/rc tags.
+release = version
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of documents that shouldn't be included in the build.
+#unused_docs = []
+
+# List of directories, relative to source directory, that shouldn't be searched
+# for source files.
+exclude_trees = ['_build']
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+
+# -- Options for HTML output ---------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  Major themes that come with
+# Sphinx are currently 'default' and 'sphinxdoc'.
+html_theme = 'nature'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further.  For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents.  If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar.  Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_use_modindex = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+html_show_sourcelink = False
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it.  The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = ''
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'altgraphdoc'
+
+
+# -- Options for LaTeX output --------------------------------------------------
+
+# The paper size ('letter' or 'a4').
+#latex_paper_size = 'letter'
+
+# The font size ('10pt', '11pt' or '12pt').
+#latex_font_size = '10pt'
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, documentclass [howto/manual]).
+latex_documents = [
+  ('index', 'altgraph.tex', u'altgraph Documentation',
+   u'Ronald Oussoren', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# Additional stuff for the LaTeX preamble.
+#latex_preamble = ''
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_use_modindex = True
+
+
+# Example configuration for intersphinx: refer to the Python standard library.
+intersphinx_mapping = {'python': ('http://docs.python.org/', None) }
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/doc/core.rst b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/doc/core.rst
new file mode 100644
index 0000000..8288f6a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/doc/core.rst
@@ -0,0 +1,26 @@
+:mod:`altgraph` --- A Python Graph Library
+==================================================
+
+.. module:: altgraph
+   :synopsis: A directional graph for python
+
+altgraph is a fork of `graphlib <http://pygraphlib.sourceforge.net>`_ tailored
+to use newer Python 2.3+ features, including additional support used by the
+py2app suite (modulegraph and macholib, specifically).
+
+altgraph is a python based graph (network) representation and manipulation package.
+It has started out as an extension to the `graph_lib module <http://www.ece.arizona.edu/~denny/python_nest/graph_lib_1.0.1.html>`_
+written by Nathan Denny it has been significantly optimized and expanded.
+
+The :class:`altgraph.Graph.Graph` class is loosely modeled after the `LEDA <http://www.algorithmic-solutions.com/enleda.htm>`_ 
+(Library of Efficient Datatypes)  representation. The library
+includes methods for constructing graphs, BFS and DFS traversals,
+topological sort, finding connected components, shortest paths as well as a number
+graph statistics functions. The library can also visualize graphs
+via `graphviz <http://www.research.att.com/sw/tools/graphviz/>`_.
+
+
+.. exception:: GraphError
+
+   Exception raised when methods are called with bad values of
+   an inconsistent state.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/doc/dot.rst b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/doc/dot.rst
new file mode 100644
index 0000000..3848c48
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/doc/dot.rst
@@ -0,0 +1,224 @@
+:mod:`altgraph.Dot` --- Interface to the dot language
+=====================================================
+
+.. module:: altgraph.Dot
+   :synopsis: Interface to the dot language as used by Graphviz..
+
+The :py:mod:`~altgraph.Dot` module provides a simple interface to the
+file format used in the `graphviz`_ program. The module is intended to 
+offload the most tedious part of the process (the **dot** file generation) 
+while transparently exposing most of its features.
+
+.. _`graphviz`: <http://www.research.att.com/sw/tools/graphviz/>`_
+
+To display the graphs or to generate image files the `graphviz`_
+package needs to be installed on the system, moreover the :command:`dot` and :command:`dotty` programs must
+be accesible in the program path so that they can be ran from processes spawned
+within the module. 
+
+Example usage
+-------------
+
+Here is a typical usage::
+
+    from altgraph import Graph, Dot
+
+    # create a graph
+    edges = [ (1,2), (1,3), (3,4), (3,5), (4,5), (5,4) ]
+    graph = Graph.Graph(edges)
+    
+    # create a dot representation of the graph
+    dot = Dot.Dot(graph)
+
+    # display the graph
+    dot.display()
+
+    # save the dot representation into the mydot.dot file
+    dot.save_dot(file_name='mydot.dot')
+
+    # save dot file as gif image into the graph.gif file
+    dot.save_img(file_name='graph', file_type='gif')
+
+
+Directed graph and non-directed graph
+-------------------------------------
+
+Dot class can use for both directed graph and non-directed graph
+by passing *graphtype* parameter.
+
+Example::
+
+    # create directed graph(default)
+    dot = Dot.Dot(graph, graphtype="digraph")
+
+    # create non-directed graph
+    dot = Dot.Dot(graph, graphtype="graph")
+
+
+Customizing the output
+----------------------
+
+The graph drawing process may be customized by passing
+valid :command:`dot` parameters for the nodes and edges. For a list of all
+parameters see the `graphviz`_ documentation.
+
+Example::
+
+    # customizing the way the overall graph is drawn
+    dot.style(size='10,10', rankdir='RL', page='5, 5' , ranksep=0.75)
+
+    # customizing node drawing
+    dot.node_style(1, label='BASE_NODE',shape='box', color='blue' )
+    dot.node_style(2, style='filled', fillcolor='red')
+
+    # customizing edge drawing
+    dot.edge_style(1, 2, style='dotted')
+    dot.edge_style(3, 5, arrowhead='dot', label='binds', labelangle='90')
+    dot.edge_style(4, 5, arrowsize=2, style='bold')
+
+
+    .. note:: 
+       
+       dotty (invoked via :py:func:`~altgraph.Dot.display`) may not be able to
+       display all graphics styles. To verify the output save it to an image 
+       file and look at it that way.
+
+Valid attributes
+----------------
+
+- dot styles, passed via the :py:meth:`Dot.style` method::
+
+    rankdir = 'LR'   (draws the graph horizontally, left to right)
+    ranksep = number (rank separation in inches)
+
+- node attributes, passed via the :py:meth:`Dot.node_style` method::
+
+     style = 'filled' | 'invisible' | 'diagonals' | 'rounded'
+     shape = 'box' | 'ellipse' | 'circle' | 'point' | 'triangle'
+
+- edge attributes, passed via the :py:meth:`Dot.edge_style` method::
+
+     style     = 'dashed' | 'dotted' | 'solid' | 'invis' | 'bold'
+     arrowhead = 'box' | 'crow' | 'diamond' | 'dot' | 'inv' | 'none' | 'tee' | 'vee'
+     weight    = number (the larger the number the closer the nodes will be)
+
+- valid `graphviz colors <http://www.research.att.com/~erg/graphviz/info/colors.html>`_
+
+- for more details on how to control the graph drawing process see the 
+  `graphviz reference <http://www.research.att.com/sw/tools/graphviz/refs.html>`_.
+
+
+Class interface
+---------------
+
+.. class:: Dot(graph[, nodes[, edgefn[, nodevisitor[, edgevisitor[, name[, dot[, dotty[, neato[, graphtype]]]]]]]]])
+
+  Creates a new Dot generator based on the specified 
+  :class:`Graph <altgraph.Graph.Graph>`.  The Dot generator won't reference
+  the *graph* once it is constructed.
+
+  If the *nodes* argument is present it is the list of nodes to include
+  in the graph, otherwise all nodes in *graph* are included.
+  
+  If the *edgefn* argument is present it is a function that yields the
+  nodes connected to another node, this defaults to 
+  :meth:`graph.out_nbr <altgraph.Graph.Graph.out_nbr>`. The constructor won't
+  add edges to the dot file unless both the head and tail of the edge
+  are in *nodes*.
+
+  If the *name* is present it specifies the name of the graph in the resulting
+  dot file. The default is ``"G"``.
+
+  The functions *nodevisitor* and *edgevisitor* return the default style
+  for a given edge or node (both default to functions that return an empty
+  style).
+
+  The arguments *dot*, *dotty* and *neato* are used to pass the path to 
+  the corresponding `graphviz`_ command.
+
+
+Updating graph attributes
+.........................
+
+.. method:: Dot.style(\**attr)
+
+   Sets the overall style (graph attributes) to the given attributes.
+
+   See `Valid Attributes`_ for more information about the attributes.
+
+.. method:: Dot.node_style(node, \**attr)
+
+   Sets the style for *node* to the given attributes.
+
+   This method will add *node* to the graph when it isn't already 
+   present.
+
+   See `Valid Attributes`_ for more information about the attributes.
+
+.. method:: Dot.all_node_style(\**attr)
+
+   Replaces the current style for all nodes
+
+
+.. method:: edge_style(head, tail, \**attr)
+
+   Sets the style of an edge to the given attributes. The edge will
+   be added to the graph when it isn't already present, but *head*
+   and *tail* must both be valid nodes.
+
+   See `Valid Attributes`_ for more information about the attributes.
+
+
+
+Emitting output
+...............
+
+.. method:: Dot.display([mode])
+
+   Displays the current graph via dotty.
+
+   If the *mode* is ``"neato"`` the dot file is processed with
+   the neato command before displaying.
+
+   This method won't return until the dotty command exits.
+
+.. method:: save_dot(filename)
+
+   Saves the current graph representation into the given file.
+
+   .. note::
+
+       For backward compatibility reasons this method can also
+       be called without an argument, it will then write the graph
+       into a fixed filename (present in the attribute :data:`Graph.temp_dot`).
+
+       This feature is deprecated and should not be used.
+
+
+.. method:: save_image(file_name[, file_type[, mode]])
+
+   Saves the current graph representation as an image file. The output
+   is written into a file whose basename is *file_name* and whose suffix
+   is *file_type*.
+
+   The *file_type* specifies the type of file to write, the default
+   is ``"gif"``.
+
+   If the *mode* is ``"neato"`` the dot file is processed with
+   the neato command before displaying.
+
+   .. note::
+
+       For backward compatibility reasons this method can also
+       be called without an argument, it will then write the graph
+       with a fixed basename (``"out"``).
+
+       This feature is deprecated and should not be used.
+
+.. method:: iterdot()
+
+   Yields all lines of a `graphviz`_ input file (including line endings).
+
+.. method:: __iter__()
+
+   Alias for the :meth:`iterdot` method.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/doc/graph.rst b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/doc/graph.rst
new file mode 100644
index 0000000..502a218
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/doc/graph.rst
@@ -0,0 +1,305 @@
+:mod:`altgraph.Graph` --- Basic directional graphs
+==================================================
+
+.. module:: altgraph.Graph
+   :synopsis: Basic directional graphs.
+
+The module :mod:`altgraph.Graph` provides a class :class:`Graph` that
+represents a directed graph with *N* nodes and *E* edges.
+
+.. class:: Graph([edges])
+
+  Constructs a new empty :class:`Graph` object. If the optional
+  *edges* parameter is supplied, updates the graph by adding the
+  specified edges.
+
+  All of the elements in *edges* should be tuples with two or three
+  elements. The first two elements of the tuple are the source and
+  destination node of the edge, the optional third element is the
+  edge data.  The source and destination nodes are added to the graph
+  when the aren't already present.
+
+
+Node related methods
+--------------------
+
+.. method:: Graph.add_node(node[, node_data])
+
+   Adds a new node to the graph if it is not already present. The new
+   node must be a hashable object.
+
+   Arbitrary data can be attached to the node via the optional *node_data*
+   argument.
+
+   .. note:: the node also won't be added to the graph when it is
+      present but currently hidden.
+
+
+.. method:: Graph.hide_node(node)
+
+   Hides a *node* from the graph. The incoming and outgoing edges of
+   the node will also be hidden.
+
+   Raises :class:`altgraph.GraphError` when the node is not (visible)
+   node of the graph.
+
+
+.. method:: Graph.restore_node(node)
+
+   Restores a previously hidden *node*. The incoming and outgoing
+   edges of the node are also restored.
+
+   Raises :class:`altgraph.GraphError` when the node is not a hidden
+   node of the graph.
+
+.. method:: Graph.restore_all_nodes()
+
+   Restores all hidden nodes.
+
+.. method:: Graph.number_of_nodes()
+
+   Return the number of visible nodes in the graph.
+
+.. method:: Graph.number_of_hidden_nodes()
+
+   Return the number of hidden nodes in the graph.
+
+.. method:: Graph.node_list()
+
+   Return a list with all visible nodes in the graph.
+
+.. method:: Graph.hidden_node_list()
+
+   Return a list with all hidden nodes in the graph.
+
+.. method:: node_data(node)
+
+   Return the data associated with the *node* when it was
+   added.
+
+.. method:: Graph.describe_node(node)
+
+   Returns *node*, the node's data and the lists of outgoing
+   and incoming edges for the node.
+
+   .. note::
+
+      the edge lists should not be modified, doing so
+      can result in unpredicatable behavior.
+
+.. method:: Graph.__contains__(node)
+
+   Returns True iff *node* is a node in the graph. This
+   method is accessed through the *in* operator.
+
+.. method:: Graph.__iter__()
+
+   Yield all nodes in the graph.
+
+.. method:: Graph.out_edges(node)
+
+   Return the list of outgoing edges for *node*
+
+.. method:: Graph.inc_edges(node)
+
+   Return the list of incoming edges for *node*
+
+.. method:: Graph.all_edges(node)
+
+   Return the list of incoming and outgoing edges for *node*
+
+.. method:: Graph.out_degree(node)
+
+   Return the number of outgoing edges for *node*.
+
+.. method:: Graph.inc_degree(node)
+
+   Return the number of incoming edges for *node*.
+
+.. method:: Graph.all_degree(node)
+
+   Return the number of edges (incoming or outgoing) for *node*.
+
+Edge related methods
+--------------------
+
+.. method:: Graph.add_edge(head_id, tail_id [, edge data [, create_nodes]])
+
+   Adds a directed edge from *head_id* to *tail_id*. Arbitrary data can
+   be added via *edge_data*.  When *create_nodes* is *True* (the default),
+   *head_id* and *tail_id* will be added to the graph when the aren't
+   already present.
+
+.. method:: Graph.hide_edge(edge)
+
+   Hides an edge from the graph. The edge may be unhidden at some later
+   time.
+
+.. method:: Graph.restore_edge(edge)
+
+   Restores a previously hidden *edge*.
+
+.. method:: Graph.restore_all_edges()
+
+   Restore all edges that were hidden before, except for edges
+   referring to hidden nodes.
+
+.. method:: Graph.edge_by_node(head, tail)
+
+   Return the edge ID for an edge from *head* to *tail*,
+   or :data:`None` when no such edge exists.
+
+.. method:: Graph.edge_by_id(edge)
+
+   Return the head and tail of the *edge*
+
+.. method:: Graph.edge_data(edge)
+
+   Return the data associated with the *edge*.
+
+.. method:: Graph.update_edge_data(edge, data)
+
+   Replace the edge data for *edge* by *data*. Raises
+   :exc:`KeyError` when the edge does not exist.
+
+   .. versionadded:: 0.12
+
+.. method:: Graph.head(edge)
+
+   Return the head of an *edge*
+
+.. method:: Graph.tail(edge)
+
+   Return the tail of an *edge*
+
+.. method:: Graph.describe_edge(edge)
+
+   Return the *edge*, the associated data, its head and tail.
+
+.. method:: Graph.number_of_edges()
+
+   Return the number of visible edges.
+
+.. method:: Graph.number_of_hidden_edges()
+
+   Return the number of hidden edges.
+
+.. method:: Graph.edge_list()
+
+   Returns a list with all visible edges in the graph.
+
+.. method:: Graph.hidden_edge_list()
+
+   Returns a list with all hidden edges in the graph.
+
+Graph traversal
+---------------
+
+.. method:: Graph.out_nbrs(node)
+
+   Return a list of all nodes connected by outgoing edges.
+
+.. method:: Graph.inc_nbrs(node)
+
+   Return a list of all nodes connected by incoming edges.
+
+.. method:: Graph.all_nbrs(node)
+
+   Returns a list of nodes connected by an incoming or outgoing edge.
+
+.. method:: Graph.forw_topo_sort()
+
+   Return a list of nodes where the successors (based on outgoing
+   edges) of any given node apear in the sequence after that node.
+
+.. method:: Graph.back_topo_sort()
+
+   Return a list of nodes where the successors (based on incoming
+   edges) of any given node apear in the sequence after that node.
+
+.. method:: Graph.forw_bfs_subgraph(start_id)
+
+   Return a subgraph consisting of the breadth first
+   reachable nodes from *start_id* based on their outgoing edges.
+
+
+.. method:: Graph.back_bfs_subgraph(start_id)
+
+   Return a subgraph consisting of the breadth first
+   reachable nodes from *start_id* based on their incoming edges.
+
+.. method:: Graph.iterdfs(start[, end[, forward]])
+
+   Yield nodes in a depth first traversal starting at the *start*
+   node.
+
+   If *end* is specified traversal stops when reaching that node.
+
+   If forward is True (the default) edges are traversed in forward
+   direction, otherwise they are traversed in reverse direction.
+
+.. method:: Graph.iterdata(start[, end[, forward[, condition]]])
+
+   Yield the associated data for nodes in a depth first traversal
+   starting at the *start* node. This method will not yield values for nodes
+   without associated data.
+
+   If *end* is specified traversal stops when reaching that node.
+
+   If *condition* is specified and the condition callable returns
+   False for the associated data this method will not yield the
+   associated data and will not follow the edges for the node.
+
+   If forward is True (the default) edges are traversed in forward
+   direction, otherwise they are traversed in reverse direction.
+
+.. method:: Graph.forw_bfs(start[, end])
+
+   Returns a list of nodes starting at *start* in some bread first
+   search order (following outgoing edges).
+
+   When *end* is specified iteration stops at that node.
+
+.. method:: Graph.back_bfs(start[, end])
+
+   Returns a list of nodes starting at *start* in some bread first
+   search order (following incoming edges).
+
+   When *end* is specified iteration stops at that node.
+
+.. method:: Graph.get_hops(start[, end[, forward]])
+
+   Computes the hop distance to all nodes centered around a specified node.
+
+   First order neighbours are at hop 1, their neigbours are at hop 2 etc.
+   Uses :py:meth:`forw_bfs` or :py:meth:`back_bfs` depending on the value of
+   the forward parameter.
+
+   If the distance between all neighbouring nodes is 1 the hop number
+   corresponds to the shortest distance between the nodes.
+
+   Typical usage::
+
+        >>> print graph.get_hops(1, 8)
+        >>> [(1, 0), (2, 1), (3, 1), (4, 2), (5, 3), (7, 4), (8, 5)]
+        # node 1 is at 0 hops
+        # node 2 is at 1 hop
+        # ...
+        # node 8 is at 5 hops
+
+
+Graph statistics
+----------------
+
+.. method:: Graph.connected()
+
+   Returns True iff every node in the graph can be reached from
+   every other node.
+
+.. method:: Graph.clust_coef(node)
+
+   Returns the local clustering coefficient of node.
+
+   The local cluster coefficient is the proportion of the actual number
+   of edges between neighbours of node and the maximum number of
+   edges between those nodes.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/doc/graphalgo.rst b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/doc/graphalgo.rst
new file mode 100644
index 0000000..84d492f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/doc/graphalgo.rst
@@ -0,0 +1,26 @@
+:mod:`altgraph.GraphAlgo` --- Graph algorithms
+==================================================
+
+.. module:: altgraph.GraphAlgo
+   :synopsis: Basic graphs algoritms
+
+.. function:: dijkstra(graph, start[, end])
+
+   Dijkstra's algorithm for shortest paths.
+
+   Find shortest paths from the  start node to all nodes nearer 
+   than or equal to the *end* node. The edge data is assumed to be the edge length.
+
+   .. note::
+
+       Dijkstra's algorithm is only guaranteed to work correctly when all edge lengths are positive.
+       This code does not verify this property for all edges (only the edges examined until the end
+       vertex is reached), but will correctly compute shortest paths even for some graphs with negative
+       edges, and will raise an exception if it discovers that a negative edge has caused it to make a mistake.
+
+
+.. function:: shortest_path(graph, start, end)
+
+   Find a single shortest path from the given start node to the given end node.
+   The input has the same conventions as :func:`dijkstra`. The output is a list 
+   of the nodes in order along the shortest path.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/doc/graphstat.rst b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/doc/graphstat.rst
new file mode 100644
index 0000000..0931a12
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/doc/graphstat.rst
@@ -0,0 +1,25 @@
+:mod:`altgraph.GraphStat` --- Functions providing various graph statistics
+==========================================================================
+
+.. module:: altgraph.GraphStat
+   :synopsis: Functions providing various graph statistics
+
+The module :mod:`altgraph.GraphStat` provides function that calculate
+graph statistics. Currently there is only one such function, more may
+be added later.
+
+.. function:: degree_dist(graph[, limits[, bin_num[, mode]]])
+
+   Groups the number of edges per node into *bin_num* bins
+   and returns the list of those bins. Every item in the result
+   is a tuple with the center of the bin and the number of items
+   in that bin.
+
+   When the *limits* argument is present it must be a tuple with
+   the mininum and maximum number of edges that get binned (that
+   is, when *limits* is ``(4, 10)`` only nodes with between 4
+   and 10 edges get counted.
+
+   The *mode* argument is used to count incoming (``'inc'``) or
+   outgoing (``'out'``) edges. The default is to count the outgoing
+   edges.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/doc/graphutil.rst b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/doc/graphutil.rst
new file mode 100644
index 0000000..c07836d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/doc/graphutil.rst
@@ -0,0 +1,55 @@
+:mod:`altgraph.GraphUtil` --- Utility functions
+================================================
+
+.. module:: altgraph.GraphUtil
+   :synopsis: Utility functions
+
+The module :mod:`altgraph.GraphUtil` performs a number of more
+or less useful utility functions.
+
+.. function:: generate_random_graph(node_num, edge_num[, self_loops[, multi_edges])
+
+   Generates and returns a :class:`Graph <altgraph.Graph.Graph>` instance
+   with *node_num* nodes randomly connected by *edge_num* edges.
+
+   When *self_loops* is present and True there can be edges that point from
+   a node to itself.
+
+   When *multi_edge* is present and True there can be duplicate edges.
+
+   This method raises :class:`GraphError <altgraph.GraphError` when
+   a graph with the requested configuration cannot be created.
+
+.. function:: generate_scale_free_graph(steps, growth_num[, self_loops[, multi_edges]])
+
+    Generates and returns a :py:class:`~altgraph.Graph.Graph` instance that 
+    will have *steps*growth_n um* nodes and a scale free (powerlaw) 
+    connectivity. 
+    
+    Starting with a fully connected graph with *growth_num* nodes
+    at every step *growth_num* nodes are added to the graph and are connected 
+    to existing nodes with a probability proportional to the degree of these 
+    existing nodes.
+
+    .. warning:: The current implementation is basically untested, although
+       code inspection seems to indicate an implementation that is consistent
+       with the description at 
+       `Wolfram MathWorld <http://mathworld.wolfram.com/Scale-FreeNetwork.html>`_
+
+.. function:: filter_stack(graph, head, filters)
+
+   Perform a depth-first oder walk of the graph starting at *head* and
+   apply all filter functions in *filters* on the node data of the nodes
+   found.
+
+   Returns (*visited*, *removes*, *orphans*), where
+
+   * *visited*: the set of visited nodes
+
+   * *removes*: the list of nodes where the node data doesn't match
+     all *filters*.
+
+   * *orphans*: list of tuples (*last_good*, *node*), where 
+     node is not in *removes* and one of the nodes that is connected
+     by an incoming edge is in *removes*. *Last_good* is the
+     closest upstream node that is not in *removes*.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/doc/index.rst b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/doc/index.rst
new file mode 100644
index 0000000..1e8d504
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/doc/index.rst
@@ -0,0 +1,41 @@
+.. altgraph documentation master file, created by
+   sphinx-quickstart on Tue Aug 31 11:04:49 2010.
+   You can adapt this file completely to your liking, but it should at least
+   contain the root `toctree` directive.
+
+Altgraph - A basic graph library
+================================
+
+altgraph is a fork of graphlib: a graph (network) package for constructing
+graphs, BFS and DFS traversals, topological sort, shortest paths, etc. with
+graphviz output.
+
+The primary users of this package are `macholib <http://pypi.python.org/pypi/macholib>`_ and `modulegraph <http://pypi.python.org/pypi/modulegraph>`_.
+
+.. toctree::
+   :maxdepth: 1
+
+   changelog
+   license
+   core
+   graph
+   objectgraph
+   graphalgo
+   graphstat
+   graphutil
+   dot
+
+Online Resources
+----------------
+
+* `Sourcecode repository on bitbucket <http://bitbucket.org/ronaldoussoren/altgraph/>`_
+
+* `The issue tracker <http://bitbucket.org/ronaldoussoren/altgraph/issues>`_
+
+Indices and tables
+------------------
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
+
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/doc/license.rst b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/doc/license.rst
new file mode 100644
index 0000000..498e60b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/doc/license.rst
@@ -0,0 +1,25 @@
+License
+=======
+
+Copyright (c) 2004 Istvan Albert unless otherwise noted.
+
+Parts are copyright (c) Bob Ippolito
+
+Parts are copyright (c) 2010-2014 Ronald Oussoren
+
+MIT License
+...........
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+and associated documentation files (the "Software"), to deal in the Software without restriction,
+including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do
+so.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/doc/objectgraph.rst b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/doc/objectgraph.rst
new file mode 100644
index 0000000..e3df396
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/doc/objectgraph.rst
@@ -0,0 +1,146 @@
+:mod:`altgraph.ObjectGraph` --- Graphs of objecs with an identifier
+===================================================================
+
+.. module:: altgraph.ObjectGraph
+   :synopsis: A graph of objects that have a "graphident" attribute.
+
+.. class:: ObjectGraph([graph[, debug]])
+
+   A graph of objects that have a "graphident" attribute. The
+   value of this attribute is the key for the object in the
+   graph.
+
+   The optional *graph* is a previously constructed
+   :class:`Graph <altgraph.Graph.Graph>`.
+
+   The optional *debug* level controls the amount of debug output
+   (see :meth:`msg`, :meth:`msgin` and :meth:`msgout`).
+
+   .. note:: the altgraph library does not generate output, the
+      debug attribute and message methods are present for use
+      by subclasses.
+
+.. data:: ObjectGraph.graph
+
+   An :class:`Graph <altgraph.Graph.Graph>` object that contains
+   the graph data.
+
+
+.. method:: ObjectGraph.addNode(node)
+
+   Adds a *node* to the graph.
+
+   .. note:: re-adding a node that was previously removed
+      using :meth:`removeNode` will reinstate the previously
+      removed node.
+
+.. method:: ObjectGraph.createNode(self, cls, name, \*args, \**kwds)
+
+   Creates a new node using ``cls(*args, **kwds)`` and adds that
+   node using :meth:`addNode`.
+
+   Returns the newly created node.
+
+.. method:: ObjectGraph.removeNode(node)
+
+   Removes a *node* from the graph when it exists. The *node* argument
+   is either a node object, or the graphident of a node.
+
+.. method:: ObjectGraph.createReferences(fromnode, tonode[, edge_data])
+
+   Creates a reference from *fromnode* to *tonode*. The optional
+   *edge_data* is associated with the edge.
+
+   *Fromnode* and *tonode* can either be node objects or the graphident
+   values for nodes.
+
+.. method:: removeReference(fromnode, tonode)
+
+   Removes the reference from *fromnode* to *tonode* if it exists.
+
+.. method:: ObjectGraph.getRawIdent(node)
+
+   Returns the *graphident* attribute of *node*, or the graph itself
+   when *node* is :data:`None`.
+
+.. method:: getIdent(node)
+
+   Same as :meth:`getRawIdent`, but only if the node is part
+   of the graph.
+
+   *Node* can either be an actual node object or the graphident of
+   a node.
+
+.. method:: ObjectGraph.findNode(node)
+
+   Returns a given node in the graph, or :data:`Node` when it cannot
+   be found.
+
+   *Node* is either an object with a *graphident* attribute or
+   the *graphident* attribute itself.
+
+.. method:: ObjectGraph.__contains__(node)
+
+   Returns True if *node* is a member of the graph. *Node* is either an
+   object with a *graphident* attribute or the *graphident* attribute itself.
+
+.. method:: ObjectGraph.flatten([condition[, start]])
+
+   Yield all nodes that are entirely reachable by *condition*
+   starting fromt he given *start* node or the graph root.
+
+   .. note:: objects are only reachable from the graph root
+      when there is a reference from the root to the node
+      (either directly or through another node)
+
+.. method:: ObjectGraph.nodes()
+
+   Yield all nodes in the graph.
+
+.. method:: ObjectGraph.get_edges(node)
+
+   Returns two iterators that yield the nodes reaching by
+   outgoing and incoming edges.
+
+.. method:: ObjectGraph.filterStack(filters)
+
+   Filter the ObjectGraph in-place by removing all edges to nodes that
+   do not match every filter in the given filter list
+
+   Returns a tuple containing the number of:
+   (*nodes_visited*, *nodes_removed*, *nodes_orphaned*)
+
+.. method:: ObjectGraph.edgeData(fromNode, toNode):
+   Return the edge data associated with the edge from *fromNode*
+   to *toNode*.  Raises :exc:`KeyError` when no such edge exists.
+
+   .. versionadded: 0.12
+
+.. method:: ObjectGraph.updateEdgeData(fromNode, toNode, edgeData)
+
+   Replace the data associated with the edge from *fromNode* to
+   *toNode* by *edgeData*.
+
+   Raises :exc:`KeyError` when the edge does not exist.
+
+Debug output
+------------
+
+.. data:: ObjectGraph.debug
+
+   The current debug level.
+
+.. method:: ObjectGraph.msg(level, text, \*args)
+
+   Print a debug message at the current indentation level when the current
+   debug level is *level* or less.
+
+.. method:: ObjectGraph.msgin(level, text, \*args)
+
+   Print a debug message when the current debug level is *level* or less,
+   and increase the indentation level.
+
+.. method:: ObjectGraph.msgout(level, text, \*args)
+
+   Decrease the indentation level and print a debug message when the
+   current debug level is *level* or less.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/setup.cfg b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/setup.cfg
new file mode 100644
index 0000000..9c6880e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/setup.cfg
@@ -0,0 +1,36 @@
+[metadata]
+name = altgraph
+version = 0.12
+description = Python graph (network) package
+long_description_file = 
+	README.txt
+	doc/changelog.rst
+author = Ronald Oussoren
+author_email = ronaldoussoren@mac.com
+maintainer = Ronald Oussoren
+maintainer_email = ronaldoussoren@mac.com
+url = http://packages.python.org/altgraph
+download_url = http://pypi.python.org/pypi/altgraph
+license = MIT
+classifiers = 
+	Intended Audience :: Developers
+	License :: OSI Approved :: MIT License
+	Programming Language :: Python
+	Programming Language :: Python :: 2
+	Programming Language :: Python :: 2.7
+	Programming Language :: Python :: 3
+	Programming Language :: Python :: 3.3
+	Programming Language :: Python :: 3.4
+	Topic :: Software Development :: Libraries :: Python Modules
+	Topic :: Scientific/Engineering :: Mathematics
+	Topic :: Scientific/Engineering :: Visualization
+keywords = graph
+platforms = any
+packages = altgraph
+zip-safe = 1
+
+[egg_info]
+tag_build = 
+tag_date = 0
+tag_svn_revision = 0
+
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/setup.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/setup.py
new file mode 100644
index 0000000..a1a4cb6
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/altgraph/setup.py
@@ -0,0 +1,867 @@
+"""
+Shared setup file for simple python packages. Uses a setup.cfg that
+is the same as the distutils2 project, unless noted otherwise.
+
+It exists for two reasons:
+1) This makes it easier to reuse setup.py code between my own
+   projects
+
+2) Easier migration to distutils2 when that catches on.
+
+Additional functionality:
+
+* Section metadata:
+    requires-test:  Same as 'tests_require' option for setuptools.
+
+"""
+
+import sys
+import os
+import re
+import platform
+from fnmatch import fnmatch
+import os
+import sys
+import time
+import tempfile
+import tarfile
+try:
+    import urllib.request as urllib
+except ImportError:
+    import urllib
+from distutils import log
+try:
+    from hashlib import md5
+
+except ImportError:
+    from md5 import md5
+
+if sys.version_info[0] == 2:
+    from ConfigParser import RawConfigParser, NoOptionError, NoSectionError
+else:
+    from configparser import RawConfigParser, NoOptionError, NoSectionError
+
+ROOTDIR = os.path.dirname(os.path.abspath(__file__))
+
+
+#
+#
+#
+# Parsing the setup.cfg and converting it to something that can be
+# used by setuptools.setup()
+#
+#
+#
+
+def eval_marker(value):
+    """
+    Evaluate an distutils2 environment marker.
+
+    This code is unsafe when used with hostile setup.cfg files,
+    but that's not a problem for our own files.
+    """
+    value = value.strip()
+
+    class M:
+        def __init__(self, **kwds):
+            for k, v in kwds.items():
+                setattr(self, k, v)
+
+    variables = {
+        'python_version': '%d.%d'%(sys.version_info[0], sys.version_info[1]),
+        'python_full_version': sys.version.split()[0],
+        'os': M(
+            name=os.name,
+        ),
+        'sys': M(
+            platform=sys.platform,
+        ),
+        'platform': M(
+            version=platform.version(),
+            machine=platform.machine(),
+        ),
+    }
+
+    return bool(eval(value, variables, variables))
+
+
+    return True
+
+def _opt_value(cfg, into, section, key, transform = None):
+    try:
+        v = cfg.get(section, key)
+        if transform != _as_lines and ';' in v:
+            v, marker = v.rsplit(';', 1)
+            if not eval_marker(marker):
+                return
+
+            v = v.strip()
+
+        if v:
+            if transform:
+                into[key] = transform(v.strip())
+            else:
+                into[key] = v.strip()
+
+    except (NoOptionError, NoSectionError):
+        pass
+
+def _as_bool(value):
+    if value.lower() in ('y', 'yes', 'on'):
+        return True
+    elif value.lower() in ('n', 'no', 'off'):
+        return False
+    elif value.isdigit():
+        return bool(int(value))
+    else:
+        raise ValueError(value)
+
+def _as_list(value):
+    return value.split()
+
+def _as_lines(value):
+    result = []
+    for v in value.splitlines():
+        if ';' in v:
+            v, marker = v.rsplit(';', 1)
+            if not eval_marker(marker):
+                continue
+
+            v = v.strip()
+            if v:
+                result.append(v)
+        else:
+            result.append(v)
+    return result
+
+def _map_requirement(value):
+    m = re.search(r'(\S+)\s*(?:\((.*)\))?', value)
+    name = m.group(1)
+    version = m.group(2)
+
+    if version is None:
+        return name
+
+    else:
+        mapped = []
+        for v in version.split(','):
+            v = v.strip()
+            if v[0].isdigit():
+                # Checks for a specific version prefix
+                m = v.rsplit('.', 1)
+                mapped.append('>=%s,<%s.%s'%(
+                    v, m[0], int(m[1])+1))
+
+            else:
+                mapped.append(v)
+        return '%s %s'%(name, ','.join(mapped),)
+
+def _as_requires(value):
+    requires = []
+    for req in value.splitlines():
+        if ';' in req:
+            req, marker = v.rsplit(';', 1)
+            if not eval_marker(marker):
+                continue
+            req = req.strip()
+
+        if not req:
+            continue
+        requires.append(_map_requirement(req))
+    return requires
+
+def parse_setup_cfg():
+    cfg = RawConfigParser()
+    r = cfg.read([os.path.join(ROOTDIR, 'setup.cfg')])
+    if len(r) != 1:
+        print("Cannot read 'setup.cfg'")
+        sys.exit(1)
+
+    metadata = dict(
+            name        = cfg.get('metadata', 'name'),
+            version     = cfg.get('metadata', 'version'),
+            description = cfg.get('metadata', 'description'),
+    )
+
+    _opt_value(cfg, metadata, 'metadata', 'license')
+    _opt_value(cfg, metadata, 'metadata', 'maintainer')
+    _opt_value(cfg, metadata, 'metadata', 'maintainer_email')
+    _opt_value(cfg, metadata, 'metadata', 'author')
+    _opt_value(cfg, metadata, 'metadata', 'author_email')
+    _opt_value(cfg, metadata, 'metadata', 'url')
+    _opt_value(cfg, metadata, 'metadata', 'download_url')
+    _opt_value(cfg, metadata, 'metadata', 'classifiers', _as_lines)
+    _opt_value(cfg, metadata, 'metadata', 'platforms', _as_list)
+    _opt_value(cfg, metadata, 'metadata', 'packages', _as_list)
+    _opt_value(cfg, metadata, 'metadata', 'keywords', _as_list)
+
+    try:
+        v = cfg.get('metadata', 'requires-dist')
+
+    except (NoOptionError, NoSectionError):
+        pass
+
+    else:
+        requires = _as_requires(v)
+        if requires:
+            metadata['install_requires'] = requires
+
+    try:
+        v = cfg.get('metadata', 'requires-test')
+
+    except (NoOptionError, NoSectionError):
+        pass
+
+    else:
+        requires = _as_requires(v)
+        if requires:
+            metadata['tests_require'] = requires
+
+
+    try:
+        v = cfg.get('metadata', 'long_description_file')
+    except (NoOptionError, NoSectionError):
+        pass
+
+    else:
+        parts = []
+        for nm in v.split():
+            fp = open(nm, 'rU')
+            parts.append(fp.read())
+            fp.close()
+
+        metadata['long_description'] = '\n\n'.join(parts)
+
+
+    try:
+        v = cfg.get('metadata', 'zip-safe')
+    except (NoOptionError, NoSectionError):
+        pass
+
+    else:
+        metadata['zip_safe'] = _as_bool(v)
+
+    try:
+        v = cfg.get('metadata', 'console_scripts')
+    except (NoOptionError, NoSectionError):
+        pass
+
+    else:
+        if 'entry_points' not in metadata:
+            metadata['entry_points'] = {}
+
+        metadata['entry_points']['console_scripts'] = v.splitlines()
+
+    if sys.version_info[:2] <= (2,6):
+        try:
+            metadata['tests_require'] += ", unittest2"
+        except KeyError:
+            metadata['tests_require'] = "unittest2"
+
+    return metadata
+
+
+#
+#
+#
+# Bootstrapping setuptools/distribute, based on
+# a heavily modified version of distribute_setup.py
+#
+#
+#
+
+
+SETUPTOOLS_PACKAGE='setuptools'
+
+
+try:
+    import subprocess
+
+    def _python_cmd(*args):
+        args = (sys.executable,) + args
+        return subprocess.call(args) == 0
+
+except ImportError:
+    def _python_cmd(*args):
+        args = (sys.executable,) + args
+        new_args = []
+        for a in args:
+            new_args.append(a.replace("'", "'\"'\"'"))
+        os.system(' '.join(new_args)) == 0
+
+
+try:
+    import json
+
+    def get_pypi_src_download(package):
+        url = 'https://pypi.python.org/pypi/%s/json'%(package,)
+        fp = urllib.urlopen(url)
+        try:
+            try:
+                data = fp.read()
+
+            finally:
+                fp.close()
+        except urllib.error:
+            raise RuntimeError("Cannot determine download link for %s"%(package,))
+
+        pkgdata = json.loads(data.decode('utf-8'))
+        if 'urls' not in pkgdata:
+            raise RuntimeError("Cannot determine download link for %s"%(package,))
+
+        for info in pkgdata['urls']:
+            if info['packagetype'] == 'sdist' and info['url'].endswith('tar.gz'):
+                return (info.get('md5_digest'), info['url'])
+
+        raise RuntimeError("Cannot determine downlink link for %s"%(package,))
+
+except ImportError:
+    # Python 2.5 compatibility, no JSON in stdlib but luckily JSON syntax is
+    # simular enough to Python's syntax to be able to abuse the Python compiler
+
+    import _ast as ast
+
+    def get_pypi_src_download(package):
+        url = 'https://pypi.python.org/pypi/%s/json'%(package,)
+        fp = urllib.urlopen(url)
+        try:
+            try:
+                data = fp.read()
+
+            finally:
+                fp.close()
+        except urllib.error:
+            raise RuntimeError("Cannot determine download link for %s"%(package,))
+
+
+        a = compile(data, '-', 'eval', ast.PyCF_ONLY_AST)
+        if not isinstance(a, ast.Expression):
+            raise RuntimeError("Cannot determine download link for %s"%(package,))
+
+        a = a.body
+        if not isinstance(a, ast.Dict):
+            raise RuntimeError("Cannot determine download link for %s"%(package,))
+
+        for k, v in zip(a.keys, a.values):
+            if not isinstance(k, ast.Str):
+                raise RuntimeError("Cannot determine download link for %s"%(package,))
+
+            k = k.s
+            if k == 'urls':
+                a = v
+                break
+        else:
+            raise RuntimeError("PyPI JSON for %s doesn't contain URLs section"%(package,))
+
+        if not isinstance(a, ast.List):
+            raise RuntimeError("Cannot determine download link for %s"%(package,))
+
+        for info in v.elts:
+            if not isinstance(info, ast.Dict):
+                raise RuntimeError("Cannot determine download link for %s"%(package,))
+            url = None
+            packagetype = None
+            chksum = None
+
+            for k, v in zip(info.keys, info.values):
+                if not isinstance(k, ast.Str):
+                    raise RuntimeError("Cannot determine download link for %s"%(package,))
+
+                if k.s == 'url':
+                    if not isinstance(v, ast.Str):
+                        raise RuntimeError("Cannot determine download link for %s"%(package,))
+                    url = v.s
+
+                elif k.s == 'packagetype':
+                    if not isinstance(v, ast.Str):
+                        raise RuntimeError("Cannot determine download link for %s"%(package,))
+                    packagetype = v.s
+
+                elif k.s == 'md5_digest':
+                    if not isinstance(v, ast.Str):
+                        raise RuntimeError("Cannot determine download link for %s"%(package,))
+                    chksum = v.s
+
+            if url is not None and packagetype == 'sdist' and url.endswith('.tar.gz'):
+                return (chksum, url)
+
+        raise RuntimeError("Cannot determine download link for %s"%(package,))
+
+def _build_egg(egg, tarball, to_dir):
+    # extracting the tarball
+    tmpdir = tempfile.mkdtemp()
+    log.warn('Extracting in %s', tmpdir)
+    old_wd = os.getcwd()
+    try:
+        os.chdir(tmpdir)
+        tar = tarfile.open(tarball)
+        _extractall(tar)
+        tar.close()
+
+        # going in the directory
+        subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
+        os.chdir(subdir)
+        log.warn('Now working in %s', subdir)
+
+        # building an egg
+        log.warn('Building a %s egg in %s', egg, to_dir)
+        _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir)
+
+    finally:
+        os.chdir(old_wd)
+    # returning the result
+    log.warn(egg)
+    if not os.path.exists(egg):
+        raise IOError('Could not build the egg.')
+
+
+def _do_download(to_dir, packagename=SETUPTOOLS_PACKAGE):
+    tarball = download_setuptools(packagename, to_dir)
+    version = tarball.split('-')[-1][:-7]
+    egg = os.path.join(to_dir, '%s-%s-py%d.%d.egg'
+                       % (packagename, version, sys.version_info[0], sys.version_info[1]))
+    if not os.path.exists(egg):
+        _build_egg(egg, tarball, to_dir)
+    sys.path.insert(0, egg)
+    import setuptools
+    setuptools.bootstrap_install_from = egg
+
+
+def use_setuptools():
+    # making sure we use the absolute path
+    return _do_download(os.path.abspath(os.curdir))
+
+def download_setuptools(packagename, to_dir):
+    # making sure we use the absolute path
+    to_dir = os.path.abspath(to_dir)
+    try:
+        from urllib.request import urlopen
+    except ImportError:
+        from urllib2 import urlopen
+
+    chksum, url = get_pypi_src_download(packagename)
+    tgz_name = os.path.basename(url)
+    saveto = os.path.join(to_dir, tgz_name)
+
+    src = dst = None
+    if not os.path.exists(saveto):  # Avoid repeated downloads
+        try:
+            log.warn("Downloading %s", url)
+            src = urlopen(url)
+            # Read/write all in one block, so we don't create a corrupt file
+            # if the download is interrupted.
+            data = src.read()
+
+            if chksum is not None:
+                data_sum = md5(data).hexdigest()
+                if data_sum != chksum:
+                    raise RuntimeError("Downloading %s failed: corrupt checksum"%(url,))
+
+
+            dst = open(saveto, "wb")
+            dst.write(data)
+        finally:
+            if src:
+                src.close()
+            if dst:
+                dst.close()
+    return os.path.realpath(saveto)
+
+
+
+def _extractall(self, path=".", members=None):
+    """Extract all members from the archive to the current working
+       directory and set owner, modification time and permissions on
+       directories afterwards. `path' specifies a different directory
+       to extract to. `members' is optional and must be a subset of the
+       list returned by getmembers().
+    """
+    import copy
+    import operator
+    from tarfile import ExtractError
+    directories = []
+
+    if members is None:
+        members = self
+
+    for tarinfo in members:
+        if tarinfo.isdir():
+            # Extract directories with a safe mode.
+            directories.append(tarinfo)
+            tarinfo = copy.copy(tarinfo)
+            tarinfo.mode = 448 # decimal for oct 0700
+        self.extract(tarinfo, path)
+
+    # Reverse sort directories.
+    if sys.version_info < (2, 4):
+        def sorter(dir1, dir2):
+            return cmp(dir1.name, dir2.name)
+        directories.sort(sorter)
+        directories.reverse()
+    else:
+        directories.sort(key=operator.attrgetter('name'), reverse=True)
+
+    # Set correct owner, mtime and filemode on directories.
+    for tarinfo in directories:
+        dirpath = os.path.join(path, tarinfo.name)
+        try:
+            self.chown(tarinfo, dirpath)
+            self.utime(tarinfo, dirpath)
+            self.chmod(tarinfo, dirpath)
+        except ExtractError:
+            e = sys.exc_info()[1]
+            if self.errorlevel > 1:
+                raise
+            else:
+                self._dbg(1, "tarfile: %s" % e)
+
+
+#
+#
+#
+# Definitions of custom commands
+#
+#
+#
+
+try:
+    import setuptools
+
+except ImportError:
+    use_setuptools()
+
+from setuptools import setup
+
+try:
+    from distutils.core import PyPIRCCommand
+except ImportError:
+    PyPIRCCommand = None # Ancient python version
+
+from distutils.core import Command
+from distutils.errors  import DistutilsError
+from distutils import log
+
+if PyPIRCCommand is None:
+    class upload_docs (Command):
+        description = "upload sphinx documentation"
+        user_options = []
+
+        def initialize_options(self):
+            pass
+
+        def finalize_options(self):
+            pass
+
+        def run(self):
+            raise DistutilsError("not supported on this version of python")
+
+else:
+    class upload_docs (PyPIRCCommand):
+        description = "upload sphinx documentation"
+        user_options = PyPIRCCommand.user_options
+
+        def initialize_options(self):
+            PyPIRCCommand.initialize_options(self)
+            self.username = ''
+            self.password = ''
+
+
+        def finalize_options(self):
+            PyPIRCCommand.finalize_options(self)
+            config = self._read_pypirc()
+            if config != {}:
+                self.username = config['username']
+                self.password = config['password']
+
+
+        def run(self):
+            import subprocess
+            import shutil
+            import zipfile
+            import os
+            import urllib
+            import StringIO
+            from base64 import standard_b64encode
+            import httplib
+            import urlparse
+
+            # Extract the package name from distutils metadata
+            meta = self.distribution.metadata
+            name = meta.get_name()
+
+            # Run sphinx
+            if os.path.exists('doc/_build'):
+                shutil.rmtree('doc/_build')
+            os.mkdir('doc/_build')
+
+            p = subprocess.Popen(['make', 'html'],
+                cwd='doc')
+            exit = p.wait()
+            if exit != 0:
+                raise DistutilsError("sphinx-build failed")
+
+            # Collect sphinx output
+            if not os.path.exists('dist'):
+                os.mkdir('dist')
+            zf = zipfile.ZipFile('dist/%s-docs.zip'%(name,), 'w',
+                    compression=zipfile.ZIP_DEFLATED)
+
+            for toplevel, dirs, files in os.walk('doc/_build/html'):
+                for fn in files:
+                    fullname = os.path.join(toplevel, fn)
+                    relname = os.path.relpath(fullname, 'doc/_build/html')
+
+                    print ("%s -> %s"%(fullname, relname))
+
+                    zf.write(fullname, relname)
+
+            zf.close()
+
+            # Upload the results, this code is based on the distutils
+            # 'upload' command.
+            content = open('dist/%s-docs.zip'%(name,), 'rb').read()
+
+            data = {
+                ':action': 'doc_upload',
+                'name': name,
+                'content': ('%s-docs.zip'%(name,), content),
+            }
+            auth = "Basic " + standard_b64encode(self.username + ":" +
+                 self.password)
+
+
+            boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
+            sep_boundary = '\n--' + boundary
+            end_boundary = sep_boundary + '--'
+            body = StringIO.StringIO()
+            for key, value in data.items():
+                if not isinstance(value, list):
+                    value = [value]
+
+                for value in value:
+                    if isinstance(value, tuple):
+                        fn = ';filename="%s"'%(value[0])
+                        value = value[1]
+                    else:
+                        fn = ''
+
+                    body.write(sep_boundary)
+                    body.write('\nContent-Disposition: form-data; name="%s"'%key)
+                    body.write(fn)
+                    body.write("\n\n")
+                    body.write(value)
+
+            body.write(end_boundary)
+            body.write('\n')
+            body = body.getvalue()
+
+            self.announce("Uploading documentation to %s"%(self.repository,), log.INFO)
+
+            schema, netloc, url, params, query, fragments = \
+                    urlparse.urlparse(self.repository)
+
+
+            if schema == 'http':
+                http = httplib.HTTPConnection(netloc)
+            elif schema == 'https':
+                http = httplib.HTTPSConnection(netloc)
+            else:
+                raise AssertionError("unsupported schema "+schema)
+
+            data = ''
+            loglevel = log.INFO
+            try:
+                http.connect()
+                http.putrequest("POST", url)
+                http.putheader('Content-type',
+                    'multipart/form-data; boundary=%s'%boundary)
+                http.putheader('Content-length', str(len(body)))
+                http.putheader('Authorization', auth)
+                http.endheaders()
+                http.send(body)
+            except socket.error:
+                e = socket.exc_info()[1]
+                self.announce(str(e), log.ERROR)
+                return
+
+            r = http.getresponse()
+            if r.status in (200, 301):
+                self.announce('Upload succeeded (%s): %s' % (r.status, r.reason),
+                    log.INFO)
+            else:
+                self.announce('Upload failed (%s): %s' % (r.status, r.reason),
+                    log.ERROR)
+
+                print ('-'*75)
+                print (r.read())
+                print ('-'*75)
+
+
+def recursiveGlob(root, pathPattern):
+    """
+    Recursively look for files matching 'pathPattern'. Return a list
+    of matching files/directories.
+    """
+    result = []
+
+    for rootpath, dirnames, filenames in os.walk(root):
+        for fn in filenames:
+            if fnmatch(fn, pathPattern):
+                result.append(os.path.join(rootpath, fn))
+    return result
+
+
+def importExternalTestCases(unittest,
+        pathPattern="test_*.py", root=".", package=None):
+    """
+    Import all unittests in the PyObjC tree starting at 'root'
+    """
+
+    testFiles = recursiveGlob(root, pathPattern)
+    testModules = map(lambda x:x[len(root)+1:-3].replace('/', '.'), testFiles)
+    if package is not None:
+        testModules = [(package + '.' + m) for m in testModules]
+
+    suites = []
+
+    for modName in testModules:
+        try:
+            module = __import__(modName)
+        except ImportError:
+            print("SKIP %s: %s"%(modName, sys.exc_info()[1]))
+            continue
+
+        if '.' in modName:
+            for elem in modName.split('.')[1:]:
+                module = getattr(module, elem)
+
+        s = unittest.defaultTestLoader.loadTestsFromModule(module)
+        suites.append(s)
+
+    return unittest.TestSuite(suites)
+
+
+
+class test (Command):
+    description = "run test suite"
+    user_options = [
+        ('verbosity=', None, "print what tests are run"),
+    ]
+
+    def initialize_options(self):
+        self.verbosity='1'
+
+    def finalize_options(self):
+        if isinstance(self.verbosity, str):
+            self.verbosity = int(self.verbosity)
+
+
+    def cleanup_environment(self):
+        ei_cmd = self.get_finalized_command('egg_info')
+        egg_name = ei_cmd.egg_name.replace('-', '_')
+
+        to_remove =  []
+        for dirname in sys.path:
+            bn = os.path.basename(dirname)
+            if bn.startswith(egg_name + "-"):
+                to_remove.append(dirname)
+
+        for dirname in to_remove:
+            log.info("removing installed %r from sys.path before testing"%(
+                dirname,))
+            sys.path.remove(dirname)
+
+    def add_project_to_sys_path(self):
+        from pkg_resources import normalize_path, add_activation_listener
+        from pkg_resources import working_set, require
+
+        self.reinitialize_command('egg_info')
+        self.run_command('egg_info')
+        self.reinitialize_command('build_ext', inplace=1)
+        self.run_command('build_ext')
+
+
+        # Check if this distribution is already on sys.path
+        # and remove that version, this ensures that the right
+        # copy of the package gets tested.
+
+        self.__old_path = sys.path[:]
+        self.__old_modules = sys.modules.copy()
+
+
+        ei_cmd = self.get_finalized_command('egg_info')
+        sys.path.insert(0, normalize_path(ei_cmd.egg_base))
+        sys.path.insert(1, os.path.dirname(__file__))
+
+        # Strip the namespace packages defined in this distribution
+        # from sys.modules, needed to reset the search path for
+        # those modules.
+
+        nspkgs = getattr(self.distribution, 'namespace_packages')
+        if nspkgs is not None:
+            for nm in nspkgs:
+                del sys.modules[nm]
+
+        # Reset pkg_resources state:
+        add_activation_listener(lambda dist: dist.activate())
+        working_set.__init__()
+        require('%s==%s'%(ei_cmd.egg_name, ei_cmd.egg_version))
+
+    def remove_from_sys_path(self):
+        from pkg_resources import working_set
+        sys.path[:] = self.__old_path
+        sys.modules.clear()
+        sys.modules.update(self.__old_modules)
+        working_set.__init__()
+
+
+    def run(self):
+        import unittest
+
+        # Ensure that build directory is on sys.path (py3k)
+
+        self.cleanup_environment()
+        self.add_project_to_sys_path()
+
+        try:
+            meta = self.distribution.metadata
+            name = meta.get_name()
+            test_pkg = name + "_tests"
+            suite = importExternalTestCases(unittest,
+                    "test_*.py", test_pkg, test_pkg)
+
+            runner = unittest.TextTestRunner(verbosity=self.verbosity)
+            result = runner.run(suite)
+
+            # Print out summary. This is a structured format that
+            # should make it easy to use this information in scripts.
+            summary = dict(
+                count=result.testsRun,
+                fails=len(result.failures),
+                errors=len(result.errors),
+                xfails=len(getattr(result, 'expectedFailures', [])),
+                xpass=len(getattr(result, 'expectedSuccesses', [])),
+                skip=len(getattr(result, 'skipped', [])),
+            )
+            print("SUMMARY: %s"%(summary,))
+
+        finally:
+            self.remove_from_sys_path()
+
+#
+#
+#
+#  And finally run the setuptools main entry point.
+#
+#
+#
+
+metadata = parse_setup_cfg()
+
+setup(
+    cmdclass=dict(
+        upload_docs=upload_docs,
+        test=test,
+    ),
+    **metadata
+)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/chromite/LICENSE b/sdk/platform-tools/systrace/catapult/telemetry/third_party/chromite/LICENSE
new file mode 100644
index 0000000..0aa7fc9
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/chromite/LICENSE
@@ -0,0 +1,27 @@
+// Copyright (c) 2006-2009 The Chromium OS Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/chromite/README.chromium b/sdk/platform-tools/systrace/catapult/telemetry/third_party/chromite/README.chromium
new file mode 100644
index 0000000..b51feb8
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/chromite/README.chromium
@@ -0,0 +1,15 @@
+Name: chromite
+Short Name: chromite
+URL: https://chromium.googlesource.com/chromiumos/chromite
+Version: 0.0.2
+License: BSD
+License File: NOT_SHIPPED
+Security Critical: no
+
+Local modification: remove all the files except chromite/ssh_keys/ which contain
+keys used by telemetry.
+
+Description:
+This contains scripts used to build Chromium for Chromium OS
+('cros chrome-sdk'), as well as interact with the Chromium OS
+build system.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/chromite/ssh_keys/testing_rsa b/sdk/platform-tools/systrace/catapult/telemetry/third_party/chromite/ssh_keys/testing_rsa
new file mode 100644
index 0000000..d50a630
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/chromite/ssh_keys/testing_rsa
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEoAIBAAKCAQEAvsNpFdK5lb0GfKx+FgsrsM/2+aZVFYXHMPdvGtTz63ciRhq0
+Jnw7nln1SOcHraSz3/imECBg8NHIKV6rA+B9zbf7pZXEv20x5Ul0vrcPqYWC44PT
+tgsgvi8s0KZUZN93YlcjZ+Q7BjQ/tuwGSaLWLqJ7hnHALMJ3dbEM9fKBHQBCrG5H
+OaWD2gtXj7jp04M/WUnDDdemq/KMg6E9jcrJOiQ39IuTpas4hLQzVkKAKSrpl6MY
+2etHyoNarlWhcOwitArEDwf3WgnctwKstI/MTKB5BTpO2WXUNUv4kXzA+g8/l1al
+jIG13vtd9A/IV3KFVx/sLkkjuZ7z2rQXyNKuJwIBIwKCAQA79EWZJPh/hI0CnJyn
+16AEXp4T8nKDG2p9GpCiCGnq6u2Dvz/u1pZk97N9T+x4Zva0GvJc1vnlST7objW/
+Y8/ET8QeGSCT7x5PYDqiVspoemr3DCyYTKPkADKn+cLAngDzBXGHDTcfNP4U6xfr
+Qc5JK8BsFR8kApqSs/zCU4eqBtp2FVvPbgUOv3uUrFnjEuGs9rb1QZ0K6o08L4Cq
+N+e2nTysjp78blakZfqlurqTY6iJb0ImU2W3T8sV6w5GP1NT7eicXLO3WdIRB15a
+evogPeqtMo8GcO62wU/D4UCvq4GNEjvYOvFmPzXHvhTxsiWv5KEACtleBIEYmWHA
+POwrAoGBAOKgNRgxHL7r4bOmpLQcYK7xgA49OpikmrebXCQnZ/kZ3QsLVv1QdNMH
+Rx/ex7721g8R0oWslM14otZSMITCDCMWTYVBNM1bqYnUeEu5HagFwxjQ2tLuSs8E
+SBzEr96JLfhwuBhDH10sQqn+OQG1yj5acs4Pt3L4wlYwMx0vs1BxAoGBANd9Owro
+5ONiJXfKNaNY/cJYuLR+bzGeyp8oxToxgmM4UuA4hhDU7peg4sdoKJ4XjB9cKMCz
+ZGU5KHKKxNf95/Z7aywiIJEUE/xPRGNP6tngRunevp2QyvZf4pgvACvk1tl9B3HH
+7J5tY/GRkT4sQuZYpx3YnbdP5Y6Kx33BF7QXAoGAVCzghVQR/cVT1QNhvz29gs66
+iPIrtQnwUtNOHA6i9h+MnbPBOYRIpidGTaqEtKTTKisw79JjJ78X6TR4a9ML0oSg
+c1K71z9NmZgPbJU25qMN80ZCph3+h2f9hwc6AjLz0U5wQ4alP909VRVIX7iM8paf
+q59wBiHhyD3J16QAxhsCgYBu0rCmhmcV2rQu+kd4lCq7uJmBZZhFZ5tny9MlPgiK
+zIJkr1rkFbyIfqCDzyrU9irOTKc+iCUA25Ek9ujkHC4m/aTU3lnkNjYp/OFXpXF3
+XWZMY+0Ak5uUpldG85mwLIvATu3ivpbyZCTFYM5afSm4StmaUiU5tA+oZKEcGily
+jwKBgBdFLg+kTm877lcybQ04G1kIRMf5vAXcConzBt8ry9J+2iX1ddlu2K2vMroD
+1cP/U/EmvoCXSOGuetaI4UNQwE/rGCtkpvNj5y4twVLh5QufSOl49V0Ut0mwjPXw
+HfN/2MoO07vQrjgsFylvrw9A79xItABaqKndlmqlwMZWc9Ne
+-----END RSA PRIVATE KEY-----
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/chromite/ssh_keys/testing_rsa.pub b/sdk/platform-tools/systrace/catapult/telemetry/third_party/chromite/ssh_keys/testing_rsa.pub
new file mode 100644
index 0000000..7a4d033
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/chromite/ssh_keys/testing_rsa.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAvsNpFdK5lb0GfKx+FgsrsM/2+aZVFYXHMPdvGtTz63ciRhq0Jnw7nln1SOcHraSz3/imECBg8NHIKV6rA+B9zbf7pZXEv20x5Ul0vrcPqYWC44PTtgsgvi8s0KZUZN93YlcjZ+Q7BjQ/tuwGSaLWLqJ7hnHALMJ3dbEM9fKBHQBCrG5HOaWD2gtXj7jp04M/WUnDDdemq/KMg6E9jcrJOiQ39IuTpas4hLQzVkKAKSrpl6MY2etHyoNarlWhcOwitArEDwf3WgnctwKstI/MTKB5BTpO2WXUNUv4kXzA+g8/l1aljIG13vtd9A/IV3KFVx/sLkkjuZ7z2rQXyNKuJw== ChromeOS test key
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/flot/LICENSE.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/flot/LICENSE.txt
new file mode 100644
index 0000000..67f4625
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/flot/LICENSE.txt
@@ -0,0 +1,22 @@
+Copyright (c) 2007-2013 IOLA and Ole Laursen
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/flot/README.chromium b/sdk/platform-tools/systrace/catapult/telemetry/third_party/flot/README.chromium
new file mode 100644
index 0000000..6bbf71a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/flot/README.chromium
@@ -0,0 +1,10 @@
+Name: Flot Javascript/JQuery library for creating graphs
+Short Name: Flot
+URL: http://www.flotcharts.org
+Version: 0.8.1
+License: MIT
+License File: NOT_SHIPPED
+Security Critical: yes
+Description: Flot is used in the webui for performance monitor and the visualizer for Deep Memory Profiler to draw charts of performance metrics.
+Local Modifications: All the files not needed by telemetry are removed. Only keep
+jquery.flot.min.js.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/flot/jquery.flot.min.js b/sdk/platform-tools/systrace/catapult/telemetry/third_party/flot/jquery.flot.min.js
new file mode 100644
index 0000000..3706512
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/flot/jquery.flot.min.js
@@ -0,0 +1,29 @@
+/* Javascript plotting library for jQuery, version 0.8.1.
+
+Copyright (c) 2007-2013 IOLA and Ole Laursen.
+Licensed under the MIT license.
+
+*/// first an inline dependency, jquery.colorhelpers.js, we inline it here
+// for convenience
+/* Plugin for jQuery for working with colors.
+ *
+ * Version 1.1.
+ *
+ * Inspiration from jQuery color animation plugin by John Resig.
+ *
+ * Released under the MIT license by Ole Laursen, October 2009.
+ *
+ * Examples:
+ *
+ *   $.color.parse("#fff").scale('rgb', 0.25).add('a', -0.5).toString()
+ *   var c = $.color.extract($("#mydiv"), 'background-color');
+ *   console.log(c.r, c.g, c.b, c.a);
+ *   $.color.make(100, 50, 25, 0.4).toString() // returns "rgba(100,50,25,0.4)"
+ *
+ * Note that .scale() and .add() return the same modified object
+ * instead of making a new one.
+ *
+ * V. 1.1: Fix error handling so e.g. parsing an empty string does
+ * produce a color rather than just crashing.
+ */(function(e){e.color={},e.color.make=function(t,n,r,i){var s={};return s.r=t||0,s.g=n||0,s.b=r||0,s.a=i!=null?i:1,s.add=function(e,t){for(var n=0;n<e.length;++n)s[e.charAt(n)]+=t;return s.normalize()},s.scale=function(e,t){for(var n=0;n<e.length;++n)s[e.charAt(n)]*=t;return s.normalize()},s.toString=function(){return s.a>=1?"rgb("+[s.r,s.g,s.b].join(",")+")":"rgba("+[s.r,s.g,s.b,s.a].join(",")+")"},s.normalize=function(){function e(e,t,n){return t<e?e:t>n?n:t}return s.r=e(0,parseInt(s.r),255),s.g=e(0,parseInt(s.g),255),s.b=e(0,parseInt(s.b),255),s.a=e(0,s.a,1),s},s.clone=function(){return e.color.make(s.r,s.b,s.g,s.a)},s.normalize()},e.color.extract=function(t,n){var r;do{r=t.css(n).toLowerCase();if(r!=""&&r!="transparent")break;t=t.parent()}while(!e.nodeName(t.get(0),"body"));return r=="rgba(0, 0, 0, 0)"&&(r="transparent"),e.color.parse(r)},e.color.parse=function(n){var r,i=e.color.make;if(r=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(n))return i(parseInt(r[1],10),parseInt(r[2],10),parseInt(r[3],10));if(r=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(n))return i(parseInt(r[1],10),parseInt(r[2],10),parseInt(r[3],10),parseFloat(r[4]));if(r=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(n))return i(parseFloat(r[1])*2.55,parseFloat(r[2])*2.55,parseFloat(r[3])*2.55);if(r=/rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(n))return i(parseFloat(r[1])*2.55,parseFloat(r[2])*2.55,parseFloat(r[3])*2.55,parseFloat(r[4]));if(r=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(n))return i(parseInt(r[1],16),parseInt(r[2],16),parseInt(r[3],16));if(r=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(n))return i(parseInt(r[1]+r[1],16),parseInt(r[2]+r[2],16),parseInt(r[3]+r[3],16));var s=e.trim(n).toLowerCase();return s=="transparent"?i(255,255,255,0):(r=t[s]||[0,0,0],i(r[0],r[1],r[2]))};var t={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0]}})(jQuery),function(e){function n(t,n){var r=n.children("."+t)[0];if(r==null){r=document.createElement("canvas"),r.className=t,e(r).css({direction:"ltr",position:"absolute",left:0,top:0}).appendTo(n);if(!r.getContext){if(!window.G_vmlCanvasManager)throw new Error("Canvas is not available. If you're using IE with a fall-back such as Excanvas, then there's either a mistake in your conditional include, or the page has no DOCTYPE and is rendering in Quirks Mode.");r=window.G_vmlCanvasManager.initElement(r)}}this.element=r;var i=this.context=r.getContext("2d"),s=window.devicePixelRatio||1,o=i.webkitBackingStorePixelRatio||i.mozBackingStorePixelRatio||i.msBackingStorePixelRatio||i.oBackingStorePixelRatio||i.backingStorePixelRatio||1;this.pixelRatio=s/o,this.resize(n.width(),n.height()),this.textContainer=null,this.text={},this._textCache={}}function r(t,r,s,o){function E(e,t){t=[w].concat(t);for(var n=0;n<e.length;++n)e[n].apply(this,t)}function S(){var t={Canvas:n};for(var r=0;r<o.length;++r){var i=o[r];i.init(w,t),i.options&&e.extend(!0,a,i.options)}}function x(n){e.extend(!0,a,n),n&&n.colors&&(a.colors=n.colors),a.xaxis.color==null&&(a.xaxis.color=e.color.parse(a.grid.color).scale("a",.22).toString()),a.yaxis.color==null&&(a.yaxis.color=e.color.parse(a.grid.color).scale("a",.22).toString()),a.xaxis.tickColor==null&&(a.xaxis.tickColor=a.grid.tickColor||a.xaxis.color),a.yaxis.tickColor==null&&(a.yaxis.tickColor=a.grid.tickColor||a.yaxis.color),a.grid.borderColor==null&&(a.grid.borderColor=a.grid.color),a.grid.tickColor==null&&(a.grid.tickColor=e.color.parse(a.grid.color).scale("a",.22).toString());var r,i,s,o={style:t.css("font-style"),size:Math.round(.8*(+t.css("font-size").replace("px","")||13)),variant:t.css("font-variant"),weight:t.css("font-weight"),family:t.css("font-family")};o.lineHeight=o.size*1.15,s=a.xaxes.length||1;for(r=0;r<s;++r)i=a.xaxes[r],i&&!i.tickColor&&(i.tickColor=i.color),i=e.extend(!0,{},a.xaxis,i),a.xaxes[r]=i,i.font&&(i.font=e.extend({},o,i.font),i.font.color||(i.font.color=i.color));s=a.yaxes.length||1;for(r=0;r<s;++r)i=a.yaxes[r],i&&!i.tickColor&&(i.tickColor=i.color),i=e.extend(!0,{},a.yaxis,i),a.yaxes[r]=i,i.font&&(i.font=e.extend({},o,i.font),i.font.color||(i.font.color=i.color));a.xaxis.noTicks&&a.xaxis.ticks==null&&(a.xaxis.ticks=a.xaxis.noTicks),a.yaxis.noTicks&&a.yaxis.ticks==null&&(a.yaxis.ticks=a.yaxis.noTicks),a.x2axis&&(a.xaxes[1]=e.extend(!0,{},a.xaxis,a.x2axis),a.xaxes[1].position="top"),a.y2axis&&(a.yaxes[1]=e.extend(!0,{},a.yaxis,a.y2axis),a.yaxes[1].position="right"),a.grid.coloredAreas&&(a.grid.markings=a.grid.coloredAreas),a.grid.coloredAreasColor&&(a.grid.markingsColor=a.grid.coloredAreasColor),a.lines&&e.extend(!0,a.series.lines,a.lines),a.points&&e.extend(!0,a.series.points,a.points),a.bars&&e.extend(!0,a.series.bars,a.bars),a.shadowSize!=null&&(a.series.shadowSize=a.shadowSize),a.highlightColor!=null&&(a.series.highlightColor=a.highlightColor);for(r=0;r<a.xaxes.length;++r)O(d,r+1).options=a.xaxes[r];for(r=0;r<a.yaxes.length;++r)O(v,r+1).options=a.yaxes[r];for(var u in b)a.hooks[u]&&a.hooks[u].length&&(b[u]=b[u].concat(a.hooks[u]));E(b.processOptions,[a])}function T(e){u=N(e),M(),_()}function N(t){var n=[];for(var r=0;r<t.length;++r){var i=e.extend(!0,{},a.series);t[r].data!=null?(i.data=t[r].data,delete t[r].data,e.extend(!0,i,t[r]),t[r].data=i.data):i.data=t[r],n.push(i)}return n}function C(e,t){var n=e[t+"axis"];return typeof n=="object"&&(n=n.n),typeof n!="number"&&(n=1),n}function k(){return e.grep(d.concat(v),function(e){return e})}function L(e){var t={},n,r;for(n=0;n<d.length;++n)r=d[n],r&&r.used&&(t["x"+r.n]=r.c2p(e.left));for(n=0;n<v.length;++n)r=v[n],r&&r.used&&(t["y"+r.n]=r.c2p(e.top));return t.x1!==undefined&&(t.x=t.x1),t.y1!==undefined&&(t.y=t.y1),t}function A(e){var t={},n,r,i;for(n=0;n<d.length;++n){r=d[n];if(r&&r.used){i="x"+r.n,e[i]==null&&r.n==1&&(i="x");if(e[i]!=null){t.left=r.p2c(e[i]);break}}}for(n=0;n<v.length;++n){r=v[n];if(r&&r.used){i="y"+r.n,e[i]==null&&r.n==1&&(i="y");if(e[i]!=null){t.top=r.p2c(e[i]);break}}}return t}function O(t,n){return t[n-1]||(t[n-1]={n:n,direction:t==d?"x":"y",options:e.extend(!0,{},t==d?a.xaxis:a.yaxis)}),t[n-1]}function M(){var t=u.length,n=-1,r;for(r=0;r<u.length;++r){var i=u[r].color;i!=null&&(t--,typeof i=="number"&&i>n&&(n=i))}t<=n&&(t=n+1);var s,o=[],f=a.colors,l=f.length,c=0;for(r=0;r<t;r++)s=e.color.parse(f[r%l]||"#666"),r%l==0&&r&&(c>=0?c<.5?c=-c-.2:c=0:c=-c),o[r]=s.scale("rgb",1+c);var h=0,p;for(r=0;r<u.length;++r){p=u[r],p.color==null?(p.color=o[h].toString(),++h):typeof p.color=="number"&&(p.color=o[p.color].toString());if(p.lines.show==null){var m,g=!0;for(m in p)if(p[m]&&p[m].show){g=!1;break}g&&(p.lines.show=!0)}p.lines.zero==null&&(p.lines.zero=!!p.lines.fill),p.xaxis=O(d,C(p,"x")),p.yaxis=O(v,C(p,"y"))}}function _(){function x(e,t,n){t<e.datamin&&t!=-r&&(e.datamin=t),n>e.datamax&&n!=r&&(e.datamax=n)}var t=Number.POSITIVE_INFINITY,n=Number.NEGATIVE_INFINITY,r=Number.MAX_VALUE,i,s,o,a,f,l,c,h,p,d,v,m,g,y,w,S;e.each(k(),function(e,r){r.datamin=t,r.datamax=n,r.used=!1});for(i=0;i<u.length;++i)l=u[i],l.datapoints={points:[]},E(b.processRawData,[l,l.data,l.datapoints]);for(i=0;i<u.length;++i){l=u[i],w=l.data,S=l.datapoints.format;if(!S){S=[],S.push({x:!0,number:!0,required:!0}),S.push({y:!0,number:!0,required:!0});if(l.bars.show||l.lines.show&&l.lines.fill){var T=!!(l.bars.show&&l.bars.zero||l.lines.show&&l.lines.zero);S.push({y:!0,number:!0,required:!1,defaultValue:0,autoscale:T}),l.bars.horizontal&&(delete S[S.length-1].y,S[S.length-1].x=!0)}l.datapoints.format=S}if(l.datapoints.pointsize!=null)continue;l.datapoints.pointsize=S.length,h=l.datapoints.pointsize,c=l.datapoints.points;var N=l.lines.show&&l.lines.steps;l.xaxis.used=l.yaxis.used=!0;for(s=o=0;s<w.length;++s,o+=h){y=w[s];var C=y==null;if(!C)for(a=0;a<h;++a)m=y[a],g=S[a],g&&(g.number&&m!=null&&(m=+m,isNaN(m)?m=null:m==Infinity?m=r:m==-Infinity&&(m=-r)),m==null&&(g.required&&(C=!0),g.defaultValue!=null&&(m=g.defaultValue))),c[o+a]=m;if(C)for(a=0;a<h;++a)m=c[o+a],m!=null&&(g=S[a],g.autoscale&&(g.x&&x(l.xaxis,m,m),g.y&&x(l.yaxis,m,m))),c[o+a]=null;else if(N&&o>0&&c[o-h]!=null&&c[o-h]!=c[o]&&c[o-h+1]!=c[o+1]){for(a=0;a<h;++a)c[o+h+a]=c[o+a];c[o+1]=c[o-h+1],o+=h}}}for(i=0;i<u.length;++i)l=u[i],E(b.processDatapoints,[l,l.datapoints]);for(i=0;i<u.length;++i){l=u[i],c=l.datapoints.points,h=l.datapoints.pointsize,S=l.datapoints.format;var L=t,A=t,O=n,M=n;for(s=0;s<c.length;s+=h){if(c[s]==null)continue;for(a=0;a<h;++a){m=c[s+a],g=S[a];if(!g||g.autoscale===!1||m==r||m==-r)continue;g.x&&(m<L&&(L=m),m>O&&(O=m)),g.y&&(m<A&&(A=m),m>M&&(M=m))}}if(l.bars.show){var _;switch(l.bars.align){case"left":_=0;break;case"right":_=-l.bars.barWidth;break;case"center":_=-l.bars.barWidth/2;break;default:throw new Error("Invalid bar alignment: "+l.bars.align)}l.bars.horizontal?(A+=_,M+=_+l.bars.barWidth):(L+=_,O+=_+l.bars.barWidth)}x(l.xaxis,L,O),x(l.yaxis,A,M)}e.each(k(),function(e,r){r.datamin==t&&(r.datamin=null),r.datamax==n&&(r.datamax=null)})}function D(){t.css("padding",0).children(":not(.flot-base,.flot-overlay)").remove(),t.css("position")=="static"&&t.css("position","relative"),f=new n("flot-base",t),l=new n("flot-overlay",t),h=f.context,p=l.context,c=e(l.element).unbind();var r=t.data("plot");r&&(r.shutdown(),l.clear()),t.data("plot",w)}function P(){a.grid.hoverable&&(c.mousemove(at),c.bind("mouseleave",ft)),a.grid.clickable&&c.click(lt),E(b.bindEvents,[c])}function H(){ot&&clearTimeout(ot),c.unbind("mousemove",at),c.unbind("mouseleave",ft),c.unbind("click",lt),E(b.shutdown,[c])}function B(e){function t(e){return e}var n,r,i=e.options.transform||t,s=e.options.inverseTransform;e.direction=="x"?(n=e.scale=g/Math.abs(i(e.max)-i(e.min)),r=Math.min(i(e.max),i(e.min))):(n=e.scale=y/Math.abs(i(e.max)-i(e.min)),n=-n,r=Math.max(i(e.max),i(e.min))),i==t?e.p2c=function(e){return(e-r)*n}:e.p2c=function(e){return(i(e)-r)*n},s?e.c2p=function(e){return s(r+e/n)}:e.c2p=function(e){return r+e/n}}function j(e){var t=e.options,n=e.ticks||[],r=t.labelWidth||0,i=t.labelHeight||0,s=r||e.direction=="x"?Math.floor(f.width/(n.length||1)):null;legacyStyles=e.direction+"Axis "+e.direction+e.n+"Axis",layer="flot-"+e.direction+"-axis flot-"+e.direction+e.n+"-axis "+legacyStyles,font=t.font||"flot-tick-label tickLabel";for(var o=0;o<n.length;++o){var u=n[o];if(!u.label)continue;var a=f.getTextInfo(layer,u.label,font,null,s);r=Math.max(r,a.width),i=Math.max(i,a.height)}e.labelWidth=t.labelWidth||r,e.labelHeight=t.labelHeight||i}function F(t){var n=t.labelWidth,r=t.labelHeight,i=t.options.position,s=t.options.tickLength,o=a.grid.axisMargin,u=a.grid.labelMargin,l=t.direction=="x"?d:v,c,h,p=e.grep(l,function(e){return e&&e.options.position==i&&e.reserveSpace});e.inArray(t,p)==p.length-1&&(o=0);if(s==null){var g=e.grep(l,function(e){return e&&e.reserveSpace});h=e.inArray(t,g)==0,h?s="full":s=5}isNaN(+s)||(u+=+s),t.direction=="x"?(r+=u,i=="bottom"?(m.bottom+=r+o,t.box={top:f.height-m.bottom,height:r}):(t.box={top:m.top+o,height:r},m.top+=r+o)):(n+=u,i=="left"?(t.box={left:m.left+o,width:n},m.left+=n+o):(m.right+=n+o,t.box={left:f.width-m.right,width:n})),t.position=i,t.tickLength=s,t.box.padding=u,t.innermost=h}function I(e){e.direction=="x"?(e.box.left=m.left-e.labelWidth/2,e.box.width=f.width-m.left-m.right+e.labelWidth):(e.box.top=m.top-e.labelHeight/2,e.box.height=f.height-m.bottom-m.top+e.labelHeight)}function q(){var t=a.grid.minBorderMargin,n={x:0,y:0},r,i;if(t==null){t=0;for(r=0;r<u.length;++r)t=Math.max(t,2*(u[r].points.radius+u[r].points.lineWidth/2))}n.x=n.y=Math.ceil(t),e.each(k(),function(e,t){var r=t.direction;t.reserveSpace&&(n[r]=Math.ceil(Math.max(n[r],(r=="x"?t.labelWidth:t.labelHeight)/2)))}),m.left=Math.max(n.x,m.left),m.right=Math.max(n.x,m.right),m.top=Math.max(n.y,m.top),m.bottom=Math.max(n.y,m.bottom)}function R(){var t,n=k(),r=a.grid.show;for(var i in m){var s=a.grid.margin||0;m[i]=typeof s=="number"?s:s[i]||0}E(b.processOffset,[m]);for(var i in m)typeof a.grid.borderWidth=="object"?m[i]+=r?a.grid.borderWidth[i]:0:m[i]+=r?a.grid.borderWidth:0;e.each(n,function(e,t){t.show=t.options.show,t.show==null&&(t.show=t.used),t.reserveSpace=t.show||t.options.reserveSpace,U(t)});if(r){var o=e.grep(n,function(e){return e.reserveSpace});e.each(o,function(e,t){z(t),W(t),X(t,t.ticks),j(t)});for(t=o.length-1;t>=0;--t)F(o[t]);q(),e.each(o,function(e,t){I(t)})}g=f.width-m.left-m.right,y=f.height-m.bottom-m.top,e.each(n,function(e,t){B(t)}),r&&G(),it()}function U(e){var t=e.options,n=+(t.min!=null?t.min:e.datamin),r=+(t.max!=null?t.max:e.datamax),i=r-n;if(i==0){var s=r==0?1:.01;t.min==null&&(n-=s);if(t.max==null||t.min!=null)r+=s}else{var o=t.autoscaleMargin;o!=null&&(t.min==null&&(n-=i*o,n<0&&e.datamin!=null&&e.datamin>=0&&(n=0)),t.max==null&&(r+=i*o,r>0&&e.datamax!=null&&e.datamax<=0&&(r=0)))}e.min=n,e.max=r}function z(t){var n=t.options,r;typeof n.ticks=="number"&&n.ticks>0?r=n.ticks:r=.3*Math.sqrt(t.direction=="x"?f.width:f.height);var s=(t.max-t.min)/r,o=-Math.floor(Math.log(s)/Math.LN10),u=n.tickDecimals;u!=null&&o>u&&(o=u);var a=Math.pow(10,-o),l=s/a,c;l<1.5?c=1:l<3?(c=2,l>2.25&&(u==null||o+1<=u)&&(c=2.5,++o)):l<7.5?c=5:c=10,c*=a,n.minTickSize!=null&&c<n.minTickSize&&(c=n.minTickSize),t.delta=s,t.tickDecimals=Math.max(0,u!=null?u:o),t.tickSize=n.tickSize||c;if(n.mode=="time"&&!t.tickGenerator)throw new Error("Time mode requires the flot.time plugin.");t.tickGenerator||(t.tickGenerator=function(e){var t=[],n=i(e.min,e.tickSize),r=0,s=Number.NaN,o;do o=s,s=n+r*e.tickSize,t.push(s),++r;while(s<e.max&&s!=o);return t},t.tickFormatter=function(e,t){var n=t.tickDecimals?Math.pow(10,t.tickDecimals):1,r=""+Math.round(e*n)/n;if(t.tickDecimals!=null){var i=r.indexOf("."),s=i==-1?0:r.length-i-1;if(s<t.tickDecimals)return(s?r:r+".")+(""+n).substr(1,t.tickDecimals-s)}return r}),e.isFunction(n.tickFormatter)&&(t.tickFormatter=function(e,t){return""+n.tickFormatter(e,t)});if(n.alignTicksWithAxis!=null){var h=(t.direction=="x"?d:v)[n.alignTicksWithAxis-1];if(h&&h.used&&h!=t){var p=t.tickGenerator(t);p.length>0&&(n.min==null&&(t.min=Math.min(t.min,p[0])),n.max==null&&p.length>1&&(t.max=Math.max(t.max,p[p.length-1]))),t.tickGenerator=function(e){var t=[],n,r;for(r=0;r<h.ticks.length;++r)n=(h.ticks[r].v-h.min)/(h.max-h.min),n=e.min+n*(e.max-e.min),t.push(n);return t};if(!t.mode&&n.tickDecimals==null){var m=Math.max(0,-Math.floor(Math.log(t.delta)/Math.LN10)+1),g=t.tickGenerator(t);g.length>1&&/\..*0$/.test((g[1]-g[0]).toFixed(m))||(t.tickDecimals=m)}}}}function W(t){var n=t.options.ticks,r=[];n==null||typeof n=="number"&&n>0?r=t.tickGenerator(t):n&&(e.isFunction(n)?r=n(t):r=n);var i,s;t.ticks=[];for(i=0;i<r.length;++i){var o=null,u=r[i];typeof u=="object"?(s=+u[0],u.length>1&&(o=u[1])):s=+u,o==null&&(o=t.tickFormatter(s,t)),isNaN(s)||t.ticks.push({v:s,label:o})}}function X(e,t){e.options.autoscaleMargin&&t.length>0&&(e.options.min==null&&(e.min=Math.min(e.min,t[0].v)),e.options.max==null&&t.length>1&&(e.max=Math.max(e.max,t[t.length-1].v)))}function V(){f.clear(),E(b.drawBackground,[h]);var e=a.grid;e.show&&e.backgroundColor&&K(),e.show&&!e.aboveData&&Q();for(var t=0;t<u.length;++t)E(b.drawSeries,[h,u[t]]),Y(u[t]);E(b.draw,[h]),e.show&&e.aboveData&&Q(),f.render(),ht()}function J(e,t){var n,r,i,s,o=k();for(var u=0;u<o.length;++u){n=o[u];if(n.direction==t){s=t+n.n+"axis",!e[s]&&n.n==1&&(s=t+"axis");if(e[s]){r=e[s].from,i=e[s].to;break}}}e[s]||(n=t=="x"?d[0]:v[0],r=e[t+"1"],i=e[t+"2"]);if(r!=null&&i!=null&&r>i){var a=r;r=i,i=a}return{from:r,to:i,axis:n}}function K(){h.save(),h.translate(m.left,m.top),h.fillStyle=bt(a.grid.backgroundColor,y,0,"rgba(255, 255, 255, 0)"),h.fillRect(0,0,g,y),h.restore()}function Q(){var t,n,r,i;h.save(),h.translate(m.left,m.top);var s=a.grid.markings;if(s){e.isFunction(s)&&(n=w.getAxes(),n.xmin=n.xaxis.min,n.xmax=n.xaxis.max,n.ymin=n.yaxis.min,n.ymax=n.yaxis.max,s=s(n));for(t=0;t<s.length;++t){var o=s[t],u=J(o,"x"),f=J(o,"y");u.from==null&&(u.from=u.axis.min),u.to==null&&(u.to=u.axis.max),f.from==null&&(f.from=f.axis.min),f.to==null&&(f.to=f.axis.max);if(u.to<u.axis.min||u.from>u.axis.max||f.to<f.axis.min||f.from>f.axis.max)continue;u.from=Math.max(u.from,u.axis.min),u.to=Math.min(u.to,u.axis.max),f.from=Math.max(f.from,f.axis.min),f.to=Math.min(f.to,f.axis.max);if(u.from==u.to&&f.from==f.to)continue;u.from=u.axis.p2c(u.from),u.to=u.axis.p2c(u.to),f.from=f.axis.p2c(f.from),f.to=f.axis.p2c(f.to),u.from==u.to||f.from==f.to?(h.beginPath(),h.strokeStyle=o.color||a.grid.markingsColor,h.lineWidth=o.lineWidth||a.grid.markingsLineWidth,h.moveTo(u.from,f.from),h.lineTo(u.to,f.to),h.stroke()):(h.fillStyle=o.color||a.grid.markingsColor,h.fillRect(u.from,f.to,u.to-u.from,f.from-f.to))}}n=k(),r=a.grid.borderWidth;for(var l=0;l<n.length;++l){var c=n[l],p=c.box,d=c.tickLength,v,b,E,S;if(!c.show||c.ticks.length==0)continue;h.lineWidth=1,c.direction=="x"?(v=0,d=="full"?b=c.position=="top"?0:y:b=p.top-m.top+(c.position=="top"?p.height:0)):(b=0,d=="full"?v=c.position=="left"?0:g:v=p.left-m.left+(c.position=="left"?p.width:0)),c.innermost||(h.strokeStyle=c.options.color,h.beginPath(),E=S=0,c.direction=="x"?E=g+1:S=y+1,h.lineWidth==1&&(c.direction=="x"?b=Math.floor(b)+.5:v=Math.floor(v)+.5),h.moveTo(v,b),h.lineTo(v+E,b+S),h.stroke()),h.strokeStyle=c.options.tickColor,h.beginPath();for(t=0;t<c.ticks.length;++t){var x=c.ticks[t].v;E=S=0;if(isNaN(x)||x<c.min||x>c.max||d=="full"&&(typeof r=="object"&&r[c.position]>0||r>0)&&(x==c.min||x==c.max))continue;c.direction=="x"?(v=c.p2c(x),S=d=="full"?-y:d,c.position=="top"&&(S=-S)):(b=c.p2c(x),E=d=="full"?-g:d,c.position=="left"&&(E=-E)),h.lineWidth==1&&(c.direction=="x"?v=Math.floor(v)+.5:b=Math.floor(b)+.5),h.moveTo(v,b),h.lineTo(v+E,b+S)}h.stroke()}r&&(i=a.grid.borderColor,typeof r=="object"||typeof i=="object"?(typeof r!="object"&&(r={top:r,right:r,bottom:r,left:r}),typeof i!="object"&&(i={top:i,right:i,bottom:i,left:i}),r.top>0&&(h.strokeStyle=i.top,h.lineWidth=r.top,h.beginPath(),h.moveTo(0-r.left,0-r.top/2),h.lineTo(g,0-r.top/2),h.stroke()),r.right>0&&(h.strokeStyle=i.right,h.lineWidth=r.right,h.beginPath(),h.moveTo(g+r.right/2,0-r.top),h.lineTo(g+r.right/2,y),h.stroke()),r.bottom>0&&(h.strokeStyle=i.bottom,h.lineWidth=r.bottom,h.beginPath(),h.moveTo(g+r.right,y+r.bottom/2),h.lineTo(0,y+r.bottom/2),h.stroke()),r.left>0&&(h.strokeStyle=i.left,h.lineWidth=r.left,h.beginPath(),h.moveTo(0-r.left/2,y+r.bottom),h.lineTo(0-r.left/2,0),h.stroke())):(h.lineWidth=r,h.strokeStyle=a.grid.borderColor,h.strokeRect(-r/2,-r/2,g+r,y+r))),h.restore()}function G(){e.each(k(),function(e,t){if(!t.show||t.ticks.length==0)return;var n=t.box,r=t.direction+"Axis "+t.direction+t.n+"Axis",i="flot-"+t.direction+"-axis flot-"+t.direction+t.n+"-axis "+r,s=t.options.font||"flot-tick-label tickLabel",o,u,a,l,c;f.removeText(i);for(var h=0;h<t.ticks.length;++h){o=t.ticks[h];if(!o.label||o.v<t.min||o.v>t.max)continue;t.direction=="x"?(l="center",u=m.left+t.p2c(o.v),t.position=="bottom"?a=n.top+n.padding:(a=n.top+n.height-n.padding,c="bottom")):(c="middle",a=m.top+t.p2c(o.v),t.position=="left"?(u=n.left+n.width-n.padding,l="right"):u=n.left+n.padding),f.addText(i,u,a,o.label,s,null,null,l,c)}})}function Y(e){e.lines.show&&Z(e),e.bars.show&&nt(e),e.points.show&&et(e)}function Z(e){function t(e,t,n,r,i){var s=e.points,o=e.pointsize,u=null,a=null;h.beginPath();for(var f=o;f<s.length;f+=o){var l=s[f-o],c=s[f-o+1],p=s[f],d=s[f+1];if(l==null||p==null)continue;if(c<=d&&c<i.min){if(d<i.min)continue;l=(i.min-c)/(d-c)*(p-l)+l,c=i.min}else if(d<=c&&d<i.min){if(c<i.min)continue;p=(i.min-c)/(d-c)*(p-l)+l,d=i.min}if(c>=d&&c>i.max){if(d>i.max)continue;l=(i.max-c)/(d-c)*(p-l)+l,c=i.max}else if(d>=c&&d>i.max){if(c>i.max)continue;p=(i.max-c)/(d-c)*(p-l)+l,d=i.max}if(l<=p&&l<r.min){if(p<r.min)continue;c=(r.min-l)/(p-l)*(d-c)+c,l=r.min}else if(p<=l&&p<r.min){if(l<r.min)continue;d=(r.min-l)/(p-l)*(d-c)+c,p=r.min}if(l>=p&&l>r.max){if(p>r.max)continue;c=(r.max-l)/(p-l)*(d-c)+c,l=r.max}else if(p>=l&&p>r.max){if(l>r.max)continue;d=(r.max-l)/(p-l)*(d-c)+c,p=r.max}(l!=u||c!=a)&&h.moveTo(r.p2c(l)+t,i.p2c(c)+n),u=p,a=d,h.lineTo(r.p2c(p)+t,i.p2c(d)+n)}h.stroke()}function n(e,t,n){var r=e.points,i=e.pointsize,s=Math.min(Math.max(0,n.min),n.max),o=0,u,a=!1,f=1,l=0,c=0;for(;;){if(i>0&&o>r.length+i)break;o+=i;var p=r[o-i],d=r[o-i+f],v=r[o],m=r[o+f];if(a){if(i>0&&p!=null&&v==null){c=o,i=-i,f=2;continue}if(i<0&&o==l+i){h.fill(),a=!1,i=-i,f=1,o=l=c+i;continue}}if(p==null||v==null)continue;if(p<=v&&p<t.min){if(v<t.min)continue;d=(t.min-p)/(v-p)*(m-d)+d,p=t.min}else if(v<=p&&v<t.min){if(p<t.min)continue;m=(t.min-p)/(v-p)*(m-d)+d,v=t.min}if(p>=v&&p>t.max){if(v>t.max)continue;d=(t.max-p)/(v-p)*(m-d)+d,p=t.max}else if(v>=p&&v>t.max){if(p>t.max)continue;m=(t.max-p)/(v-p)*(m-d)+d,v=t.max}a||(h.beginPath(),h.moveTo(t.p2c(p),n.p2c(s)),a=!0);if(d>=n.max&&m>=n.max){h.lineTo(t.p2c(p),n.p2c(n.max)),h.lineTo(t.p2c(v),n.p2c(n.max));continue}if(d<=n.min&&m<=n.min){h.lineTo(t.p2c(p),n.p2c(n.min)),h.lineTo(t.p2c(v),n.p2c(n.min));continue}var g=p,y=v;d<=m&&d<n.min&&m>=n.min?(p=(n.min-d)/(m-d)*(v-p)+p,d=n.min):m<=d&&m<n.min&&d>=n.min&&(v=(n.min-d)/(m-d)*(v-p)+p,m=n.min),d>=m&&d>n.max&&m<=n.max?(p=(n.max-d)/(m-d)*(v-p)+p,d=n.max):m>=d&&m>n.max&&d<=n.max&&(v=(n.max-d)/(m-d)*(v-p)+p,m=n.max),p!=g&&h.lineTo(t.p2c(g),n.p2c(d)),h.lineTo(t.p2c(p),n.p2c(d)),h.lineTo(t.p2c(v),n.p2c(m)),v!=y&&(h.lineTo(t.p2c(v),n.p2c(m)),h.lineTo(t.p2c(y),n.p2c(m)))}}h.save(),h.translate(m.left,m.top),h.lineJoin="round";var r=e.lines.lineWidth,i=e.shadowSize;if(r>0&&i>0){h.lineWidth=i,h.strokeStyle="rgba(0,0,0,0.1)";var s=Math.PI/18;t(e.datapoints,Math.sin(s)*(r/2+i/2),Math.cos(s)*(r/2+i/2),e.xaxis,e.yaxis),h.lineWidth=i/2,t(e.datapoints,Math.sin(s)*(r/2+i/4),Math.cos(s)*(r/2+i/4),e.xaxis,e.yaxis)}h.lineWidth=r,h.strokeStyle=e.color;var o=rt(e.lines,e.color,0,y);o&&(h.fillStyle=o,n(e.datapoints,e.xaxis,e.yaxis)),r>0&&t(e.datapoints,0,0,e.xaxis,e.yaxis),h.restore()}function et(e){function t(e,t,n,r,i,s,o,u){var a=e.points,f=e.pointsize;for(var l=0;l<a.length;l+=f){var c=a[l],p=a[l+1];if(c==null||c<s.min||c>s.max||p<o.min||p>o.max)continue;h.beginPath(),c=s.p2c(c),p=o.p2c(p)+r,u=="circle"?h.arc(c,p,t,0,i?Math.PI:Math.PI*2,!1):u(h,c,p,t,i),h.closePath(),n&&(h.fillStyle=n,h.fill()),h.stroke()}}h.save(),h.translate(m.left,m.top);var n=e.points.lineWidth,r=e.shadowSize,i=e.points.radius,s=e.points.symbol;n==0&&(n=1e-4);if(n>0&&r>0){var o=r/2;h.lineWidth=o,h.strokeStyle="rgba(0,0,0,0.1)",t(e.datapoints,i,null,o+o/2,!0,e.xaxis,e.yaxis,s),h.strokeStyle="rgba(0,0,0,0.2)",t(e.datapoints,i,null,o/2,!0,e.xaxis,e.yaxis,s)}h.lineWidth=n,h.strokeStyle=e.color,t(e.datapoints,i,rt(e.points,e.color),0,!1,e.xaxis,e.yaxis,s),h.restore()}function tt(e,t,n,r,i,s,o,u,a,f,l,c){var h,p,d,v,m,g,y,b,w;l?(b=g=y=!0,m=!1,h=n,p=e,v=t+r,d=t+i,p<h&&(w=p,p=h,h=w,m=!0,g=!1)):(m=g=y=!0,b=!1,h=e+r,p=e+i,d=n,v=t,v<d&&(w=v,v=d,d=w,b=!0,y=!1));if(p<u.min||h>u.max||v<a.min||d>a.max)return;h<u.min&&(h=u.min,m=!1),p>u.max&&(p=u.max,g=!1),d<a.min&&(d=a.min,b=!1),v>a.max&&(v=a.max,y=!1),h=u.p2c(h),d=a.p2c(d),p=u.p2c(p),v=a.p2c(v),o&&(f.beginPath(),f.moveTo(h,d),f.lineTo(h,v),f.lineTo(p,v),f.lineTo(p,d),f.fillStyle=o(d,v),f.fill()),c>0&&(m||g||y||b)&&(f.beginPath(),f.moveTo(h,d+s),m?f.lineTo(h,v+s):f.moveTo(h,v+s),y?f.lineTo(p,v+s):f.moveTo(p,v+s),g?f.lineTo(p,d+s):f.moveTo(p,d+s),b?f.lineTo(h,d+s):f.moveTo(h,d+s),f.stroke())}function nt(e){function t(t,n,r,i,s,o,u){var a=t.points,f=t.pointsize;for(var l=0;l<a.length;l+=f){if(a[l]==null)continue;tt(a[l],a[l+1],a[l+2],n,r,i,s,o,u,h,e.bars.horizontal,e.bars.lineWidth)}}h.save(),h.translate(m.left,m.top),h.lineWidth=e.bars.lineWidth,h.strokeStyle=e.color;var n;switch(e.bars.align){case"left":n=0;break;case"right":n=-e.bars.barWidth;break;case"center":n=-e.bars.barWidth/2;break;default:throw new Error("Invalid bar alignment: "+e.bars.align)}var r=e.bars.fill?function(t,n){return rt(e.bars,e.color,t,n)}:null;t(e.datapoints,n,n+e.bars.barWidth,0,r,e.xaxis,e.yaxis),h.restore()}function rt(t,n,r,i){var s=t.fill;if(!s)return null;if(t.fillColor)return bt(t.fillColor,r,i,n);var o=e.color.parse(n);return o.a=typeof s=="number"?s:.4,o.normalize(),o.toString()}function it(){t.find(".legend").remove();if(!a.legend.show)return;var n=[],r=[],i=!1,s=a.legend.labelFormatter,o,f;for(var l=0;l<u.length;++l)o=u[l],o.label&&(f=s?s(o.label,o):o.label,f&&r.push({label:f,color:o.color}));if(a.legend.sorted)if(e.isFunction(a.legend.sorted))r.sort(a.legend.sorted);else if(a.legend.sorted=="reverse")r.reverse();else{var c=a.legend.sorted!="descending";r.sort(function(e,t){return e.label==t.label?0:e.label<t.label!=c?1:-1})}for(var l=0;l<r.length;++l){var h=r[l];l%a.legend.noColumns==0&&(i&&n.push("</tr>"),n.push("<tr>"),i=!0),n.push('<td class="legendColorBox"><div style="border:1px solid '+a.legend.labelBoxBorderColor+';padding:1px"><div style="width:4px;height:0;border:5px solid '+h.color+';overflow:hidden"></div></div></td>'+'<td class="legendLabel">'+h.label+"</td>")}i&&n.push("</tr>");if(n.length==0)return;var p='<table style="font-size:smaller;color:'+a.grid.color+'">'+n.join("")+"</table>";if(a.legend.container!=null)e(a.legend.container).html(p);else{var d="",v=a.legend.position,g=a.legend.margin;g[0]==null&&(g=[g,g]),v.charAt(0)=="n"?d+="top:"+(g[1]+m.top)+"px;":v.charAt(0)=="s"&&(d+="bottom:"+(g[1]+m.bottom)+"px;"),v.charAt(1)=="e"?d+="right:"+(g[0]+m.right)+"px;":v.charAt(1)=="w"&&(d+="left:"+(g[0]+m.left)+"px;");var y=e('<div class="legend">'+p.replace('style="','style="position:absolute;'+d+";")+"</div>").appendTo(t);if(a.legend.backgroundOpacity!=0){var b=a.legend.backgroundColor;b==null&&(b=a.grid.backgroundColor,b&&typeof b=="string"?b=e.color.parse(b):b=e.color.extract(y,"background-color"),b.a=1,b=b.toString());var w=y.children();e('<div style="position:absolute;width:'+w.width()+"px;height:"+w.height()+"px;"+d+"background-color:"+b+';"> </div>').prependTo(y).css("opacity",a.legend.backgroundOpacity)}}}function ut(e,t,n){var r=a.grid.mouseActiveRadius,i=r*r+1,s=null,o=!1,f,l,c;for(f=u.length-1;f>=0;--f){if(!n(u[f]))continue;var h=u[f],p=h.xaxis,d=h.yaxis,v=h.datapoints.points,m=p.c2p(e),g=d.c2p(t),y=r/p.scale,b=r/d.scale;c=h.datapoints.pointsize,p.options.inverseTransform&&(y=Number.MAX_VALUE),d.options.inverseTransform&&(b=Number.MAX_VALUE);if(h.lines.show||h.points.show)for(l=0;l<v.length;l+=c){var w=v[l],E=v[l+1];if(w==null)continue;if(w-m>y||w-m<-y||E-g>b||E-g<-b)continue;var S=Math.abs(p.p2c(w)-e),x=Math.abs(d.p2c(E)-t),T=S*S+x*x;T<i&&(i=T,s=[f,l/c])}if(h.bars.show&&!s){var N=h.bars.align=="left"?0:-h.bars.barWidth/2,C=N+h.bars.barWidth;for(l=0;l<v.length;l+=c){var w=v[l],E=v[l+1],k=v[l+2];if(w==null)continue;if(u[f].bars.horizontal?m<=Math.max(k,w)&&m>=Math.min(k,w)&&g>=E+N&&g<=E+C:m>=w+N&&m<=w+C&&g>=Math.min(k,E)&&g<=Math.max(k,E))s=[f,l/c]}}}return s?(f=s[0],l=s[1],c=u[f].datapoints.pointsize,{datapoint:u[f].datapoints.points.slice(l*c,(l+1)*c),dataIndex:l,series:u[f],seriesIndex:f}):null}function at(e){a.grid.hoverable&&ct("plothover",e,function(e){return e["hoverable"]!=0})}function ft(e){a.grid.hoverable&&ct("plothover",e,function(e){return!1})}function lt(e){ct("plotclick",e,function(e){return e["clickable"]!=0})}function ct(e,n,r){var i=c.offset(),s=n.pageX-i.left-m.left,o=n.pageY-i.top-m.top,u=L({left:s,top:o});u.pageX=n.pageX,u.pageY=n.pageY;var f=ut(s,o,r);f&&(f.pageX=parseInt(f.series.xaxis.p2c(f.datapoint[0])+i.left+m.left,10),f.pageY=parseInt(f.series.yaxis.p2c(f.datapoint[1])+i.top+m.top,10));if(a.grid.autoHighlight){for(var l=0;l<st.length;++l){var h=st[l];h.auto==e&&(!f||h.series!=f.series||h.point[0]!=f.datapoint[0]||h.point[1]!=f.datapoint[1])&&vt(h.series,h.point)}f&&dt(f.series,f.datapoint,e)}t.trigger(e,[u,f])}function ht(){var e=a.interaction.redrawOverlayInterval;if(e==-1){pt();return}ot||(ot=setTimeout(pt,e))}function pt(){ot=null,p.save(),l.clear(),p.translate(m.left,m.top);var e,t;for(e=0;e<st.length;++e)t=st[e],t.series.bars.show?yt(t.series,t.point):gt(t.series,t.point);p.restore(),E(b.drawOverlay,[p])}function dt(e,t,n){typeof e=="number"&&(e=u[e]);if(typeof t=="number"){var r=e.datapoints.pointsize;t=e.datapoints.points.slice(r*t,r*(t+1))}var i=mt(e,t);i==-1?(st.push({series:e,point:t,auto:n}),ht()):n||(st[i].auto=!1)}function vt(e,t){if(e==null&&t==null){st=[],ht();return}typeof e=="number"&&(e=u[e]);if(typeof t=="number"){var n=e.datapoints.pointsize;t=e.datapoints.points.slice(n*t,n*(t+1))}var r=mt(e,t);r!=-1&&(st.splice(r,1),ht())}function mt(e,t){for(var n=0;n<st.length;++n){var r=st[n];if(r.series==e&&r.point[0]==t[0]&&r.point[1]==t[1])return n}return-1}function gt(t,n){var r=n[0],i=n[1],s=t.xaxis,o=t.yaxis,u=typeof t.highlightColor=="string"?t.highlightColor:e.color.parse(t.color).scale("a",.5).toString();if(r<s.min||r>s.max||i<o.min||i>o.max)return;var a=t.points.radius+t.points.lineWidth/2;p.lineWidth=a,p.strokeStyle=u;var f=1.5*a;r=s.p2c(r),i=o.p2c(i),p.beginPath(),t.points.symbol=="circle"?p.arc(r,i,f,0,2*Math.PI,!1):t.points.symbol(p,r,i,f,!1),p.closePath(),p.stroke()}function yt(t,n){var r=typeof t.highlightColor=="string"?t.highlightColor:e.color.parse(t.color).scale("a",.5).toString(),i=r,s=t.bars.align=="left"?0:-t.bars.barWidth/2;p.lineWidth=t.bars.lineWidth,p.strokeStyle=r,tt(n[0],n[1],n[2]||0,s,s+t.bars.barWidth,0,function(){return i},t.xaxis,t.yaxis,p,t.bars.horizontal,t.bars.lineWidth)}function bt(t,n,r,i){if(typeof t=="string")return t;var s=h.createLinearGradient(0,r,0,n);for(var o=0,u=t.colors.length;o<u;++o){var a=t.colors[o];if(typeof a!="string"){var f=e.color.parse(i);a.brightness!=null&&(f=f.scale("rgb",a.brightness)),a.opacity!=null&&(f.a*=a.opacity),a=f.toString()}s.addColorStop(o/(u-1),a)}return s}var u=[],a={colors:["#edc240","#afd8f8","#cb4b4b","#4da74d","#9440ed"],legend:{show:!0,noColumns:1,labelFormatter:null,labelBoxBorderColor:"#ccc",container:null,position:"ne",margin:5,backgroundColor:null,backgroundOpacity:.85,sorted:null},xaxis:{show:null,position:"bottom",mode:null,font:null,color:null,tickColor:null,transform:null,inverseTransform:null,min:null,max:null,autoscaleMargin:null,ticks:null,tickFormatter:null,labelWidth:null,labelHeight:null,reserveSpace:null,tickLength:null,alignTicksWithAxis:null,tickDecimals:null,tickSize:null,minTickSize:null},yaxis:{autoscaleMargin:.02,position:"left"},xaxes:[],yaxes:[],series:{points:{show:!1,radius:3,lineWidth:2,fill:!0,fillColor:"#ffffff",symbol:"circle"},lines:{lineWidth:2,fill:!1,fillColor:null,steps:!1},bars:{show:!1,lineWidth:2,barWidth:1,fill:!0,fillColor:null,align:"left",horizontal:!1,zero:!0},shadowSize:3,highlightColor:null},grid:{show:!0,aboveData:!1,color:"#545454",backgroundColor:null,borderColor:null,tickColor:null,margin:0,labelMargin:5,axisMargin:8,borderWidth:2,minBorderMargin:null,markings:null,markingsColor:"#f4f4f4",markingsLineWidth:2,clickable:!1,hoverable:!1,autoHighlight:!0,mouseActiveRadius:10},interaction:{redrawOverlayInterval:1e3/60},hooks:{}},f=null,l=null,c=null,h=null,p=null,d=[],v=[],m={left:0,right:0,top:0,bottom
+:0},g=0,y=0,b={processOptions:[],processRawData:[],processDatapoints:[],processOffset:[],drawBackground:[],drawSeries:[],draw:[],bindEvents:[],drawOverlay:[],shutdown:[]},w=this;w.setData=T,w.setupGrid=R,w.draw=V,w.getPlaceholder=function(){return t},w.getCanvas=function(){return f.element},w.getPlotOffset=function(){return m},w.width=function(){return g},w.height=function(){return y},w.offset=function(){var e=c.offset();return e.left+=m.left,e.top+=m.top,e},w.getData=function(){return u},w.getAxes=function(){var t={},n;return e.each(d.concat(v),function(e,n){n&&(t[n.direction+(n.n!=1?n.n:"")+"axis"]=n)}),t},w.getXAxes=function(){return d},w.getYAxes=function(){return v},w.c2p=L,w.p2c=A,w.getOptions=function(){return a},w.highlight=dt,w.unhighlight=vt,w.triggerRedrawOverlay=ht,w.pointOffset=function(e){return{left:parseInt(d[C(e,"x")-1].p2c(+e.x)+m.left,10),top:parseInt(v[C(e,"y")-1].p2c(+e.y)+m.top,10)}},w.shutdown=H,w.resize=function(){var e=t.width(),n=t.height();f.resize(e,n),l.resize(e,n)},w.hooks=b,S(w),x(s),D(),T(r),R(),V(),P();var st=[],ot=null}function i(e,t){return t*Math.floor(e/t)}var t=Object.prototype.hasOwnProperty;n.prototype.resize=function(e,t){if(e<=0||t<=0)throw new Error("Invalid dimensions for plot, width = "+e+", height = "+t);var n=this.element,r=this.context,i=this.pixelRatio;this.width!=e&&(n.width=e*i,n.style.width=e+"px",this.width=e),this.height!=t&&(n.height=t*i,n.style.height=t+"px",this.height=t),r.restore(),r.save(),r.scale(i,i)},n.prototype.clear=function(){this.context.clearRect(0,0,this.width,this.height)},n.prototype.render=function(){var e=this._textCache;for(var n in e)if(t.call(e,n)){var r=this.getTextLayer(n),i=e[n];r.hide();for(var s in i)if(t.call(i,s)){var o=i[s];for(var u in o)if(t.call(o,u)){var a=o[u].positions;for(var f=0,l;l=a[f];f++)l.active?l.rendered||(r.append(l.element),l.rendered=!0):(a.splice(f--,1),l.rendered&&l.element.detach());a.length==0&&delete o[u]}}r.show()}},n.prototype.getTextLayer=function(t){var n=this.text[t];return n==null&&(this.textContainer==null&&(this.textContainer=e("<div class='flot-text'></div>").css({position:"absolute",top:0,left:0,bottom:0,right:0,"font-size":"smaller",color:"#545454"}).insertAfter(this.element)),n=this.text[t]=e("<div></div>").addClass(t).css({position:"absolute",top:0,left:0,bottom:0,right:0}).appendTo(this.textContainer)),n},n.prototype.getTextInfo=function(t,n,r,i,s){var o,u,a,f;n=""+n,typeof r=="object"?o=r.style+" "+r.variant+" "+r.weight+" "+r.size+"px/"+r.lineHeight+"px "+r.family:o=r,u=this._textCache[t],u==null&&(u=this._textCache[t]={}),a=u[o],a==null&&(a=u[o]={}),f=a[n];if(f==null){var l=e("<div></div>").html(n).css({position:"absolute","max-width":s,top:-9999}).appendTo(this.getTextLayer(t));typeof r=="object"?l.css({font:o,color:r.color}):typeof r=="string"&&l.addClass(r),f=a[n]={width:l.outerWidth(!0),height:l.outerHeight(!0),element:l,positions:[]},l.detach()}return f},n.prototype.addText=function(e,t,n,r,i,s,o,u,a){var f=this.getTextInfo(e,r,i,s,o),l=f.positions;u=="center"?t-=f.width/2:u=="right"&&(t-=f.width),a=="middle"?n-=f.height/2:a=="bottom"&&(n-=f.height);for(var c=0,h;h=l[c];c++)if(h.x==t&&h.y==n){h.active=!0;return}h={active:!0,rendered:!1,element:l.length?f.element.clone():f.element,x:t,y:n},l.push(h),h.element.css({top:Math.round(n),left:Math.round(t),"text-align":u})},n.prototype.removeText=function(e,n,r,i,s,o){if(i==null){var u=this._textCache[e];if(u!=null)for(var a in u)if(t.call(u,a)){var f=u[a];for(var l in f)if(t.call(f,l)){var c=f[l].positions;for(var h=0,p;p=c[h];h++)p.active=!1}}}else{var c=this.getTextInfo(e,i,s,o).positions;for(var h=0,p;p=c[h];h++)p.x==n&&p.y==r&&(p.active=!1)}},e.plot=function(t,n,i){var s=new r(e(t),n,i,e.plot.plugins);return s},e.plot.version="0.8.1",e.plot.plugins=[],e.fn.plot=function(t,n){return this.each(function(){e.plot(this,t,n)})}}(jQuery);
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/mock/LICENSE.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mock/LICENSE.txt
new file mode 100644
index 0000000..7891703
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mock/LICENSE.txt
@@ -0,0 +1,26 @@
+Copyright (c) 2003-2012, Michael Foord
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/mock/README.chromium b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mock/README.chromium
new file mode 100644
index 0000000..2dc689b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mock/README.chromium
@@ -0,0 +1,16 @@
+Name: mock
+Short Name: mock
+URL: http://www.voidspace.org.uk/python/mock/
+Version: 1.0.1
+License: BSD
+License File: NOT_SHIPPED
+Security Critical: no
+
+Description:
+Library for mocks in Python tests.
+
+Local Modifications:
+Includes only mock.py and LICENSE.txt.
+Packaging and setup files have not been copied downstream.
+All other files and folders (docs/, html/, tests/)
+have not been copied downstream.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/mock/mock.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mock/mock.py
new file mode 100644
index 0000000..c8fc5d1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mock/mock.py
@@ -0,0 +1,2367 @@
+# mock.py
+# Test tools for mocking and patching.
+# Copyright (C) 2007-2012 Michael Foord & the mock team
+# E-mail: fuzzyman AT voidspace DOT org DOT uk
+
+# mock 1.0
+# http://www.voidspace.org.uk/python/mock/
+
+# Released subject to the BSD License
+# Please see http://www.voidspace.org.uk/python/license.shtml
+
+# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml
+# Comments, suggestions and bug reports welcome.
+
+
+__all__ = (
+    'Mock',
+    'MagicMock',
+    'patch',
+    'sentinel',
+    'DEFAULT',
+    'ANY',
+    'call',
+    'create_autospec',
+    'FILTER_DIR',
+    'NonCallableMock',
+    'NonCallableMagicMock',
+    'mock_open',
+    'PropertyMock',
+)
+
+
+__version__ = '1.0.1'
+
+
+import pprint
+import sys
+
+try:
+    import inspect
+except ImportError:
+    # for alternative platforms that
+    # may not have inspect
+    inspect = None
+
+try:
+    from functools import wraps as original_wraps
+except ImportError:
+    # Python 2.4 compatibility
+    def wraps(original):
+        def inner(f):
+            f.__name__ = original.__name__
+            f.__doc__ = original.__doc__
+            f.__module__ = original.__module__
+            f.__wrapped__ = original
+            return f
+        return inner
+else:
+    if sys.version_info[:2] >= (3, 3):
+        wraps = original_wraps
+    else:
+        def wraps(func):
+            def inner(f):
+                f = original_wraps(func)(f)
+                f.__wrapped__ = func
+                return f
+            return inner
+
+try:
+    unicode
+except NameError:
+    # Python 3
+    basestring = unicode = str
+
+try:
+    long
+except NameError:
+    # Python 3
+    long = int
+
+try:
+    BaseException
+except NameError:
+    # Python 2.4 compatibility
+    BaseException = Exception
+
+try:
+    next
+except NameError:
+    def next(obj):
+        return obj.next()
+
+
+BaseExceptions = (BaseException,)
+if 'java' in sys.platform:
+    # jython
+    import java
+    BaseExceptions = (BaseException, java.lang.Throwable)
+
+try:
+    _isidentifier = str.isidentifier
+except AttributeError:
+    # Python 2.X
+    import keyword
+    import re
+    regex = re.compile(r'^[a-z_][a-z0-9_]*$', re.I)
+    def _isidentifier(string):
+        if string in keyword.kwlist:
+            return False
+        return regex.match(string)
+
+
+inPy3k = sys.version_info[0] == 3
+
+# Needed to work around Python 3 bug where use of "super" interferes with
+# defining __class__ as a descriptor
+_super = super
+
+self = 'im_self'
+builtin = '__builtin__'
+if inPy3k:
+    self = '__self__'
+    builtin = 'builtins'
+
+FILTER_DIR = True
+
+
+def _is_instance_mock(obj):
+    # can't use isinstance on Mock objects because they override __class__
+    # The base class for all mocks is NonCallableMock
+    return issubclass(type(obj), NonCallableMock)
+
+
+def _is_exception(obj):
+    return (
+        isinstance(obj, BaseExceptions) or
+        isinstance(obj, ClassTypes) and issubclass(obj, BaseExceptions)
+    )
+
+
+class _slotted(object):
+    __slots__ = ['a']
+
+
+DescriptorTypes = (
+    type(_slotted.a),
+    property,
+)
+
+
+def _getsignature(func, skipfirst, instance=False):
+    if inspect is None:
+        raise ImportError('inspect module not available')
+
+    if isinstance(func, ClassTypes) and not instance:
+        try:
+            func = func.__init__
+        except AttributeError:
+            return
+        skipfirst = True
+    elif not isinstance(func, FunctionTypes):
+        # for classes where instance is True we end up here too
+        try:
+            func = func.__call__
+        except AttributeError:
+            return
+
+    if inPy3k:
+        try:
+            argspec = inspect.getfullargspec(func)
+        except TypeError:
+            # C function / method, possibly inherited object().__init__
+            return
+        regargs, varargs, varkw, defaults, kwonly, kwonlydef, ann = argspec
+    else:
+        try:
+            regargs, varargs, varkwargs, defaults = inspect.getargspec(func)
+        except TypeError:
+            # C function / method, possibly inherited object().__init__
+            return
+
+    # instance methods and classmethods need to lose the self argument
+    if getattr(func, self, None) is not None:
+        regargs = regargs[1:]
+    if skipfirst:
+        # this condition and the above one are never both True - why?
+        regargs = regargs[1:]
+
+    if inPy3k:
+        signature = inspect.formatargspec(
+            regargs, varargs, varkw, defaults,
+            kwonly, kwonlydef, ann, formatvalue=lambda value: "")
+    else:
+        signature = inspect.formatargspec(
+            regargs, varargs, varkwargs, defaults,
+            formatvalue=lambda value: "")
+    return signature[1:-1], func
+
+
+def _check_signature(func, mock, skipfirst, instance=False):
+    if not _callable(func):
+        return
+
+    result = _getsignature(func, skipfirst, instance)
+    if result is None:
+        return
+    signature, func = result
+
+    # can't use self because "self" is common as an argument name
+    # unfortunately even not in the first place
+    src = "lambda _mock_self, %s: None" % signature
+    checksig = eval(src, {})
+    _copy_func_details(func, checksig)
+    type(mock)._mock_check_sig = checksig
+
+
+def _copy_func_details(func, funcopy):
+    funcopy.__name__ = func.__name__
+    funcopy.__doc__ = func.__doc__
+    #funcopy.__dict__.update(func.__dict__)
+    funcopy.__module__ = func.__module__
+    if not inPy3k:
+        funcopy.func_defaults = func.func_defaults
+        return
+    funcopy.__defaults__ = func.__defaults__
+    funcopy.__kwdefaults__ = func.__kwdefaults__
+
+
+def _callable(obj):
+    if isinstance(obj, ClassTypes):
+        return True
+    if getattr(obj, '__call__', None) is not None:
+        return True
+    return False
+
+
+def _is_list(obj):
+    # checks for list or tuples
+    # XXXX badly named!
+    return type(obj) in (list, tuple)
+
+
+def _instance_callable(obj):
+    """Given an object, return True if the object is callable.
+    For classes, return True if instances would be callable."""
+    if not isinstance(obj, ClassTypes):
+        # already an instance
+        return getattr(obj, '__call__', None) is not None
+
+    klass = obj
+    # uses __bases__ instead of __mro__ so that we work with old style classes
+    if klass.__dict__.get('__call__') is not None:
+        return True
+
+    for base in klass.__bases__:
+        if _instance_callable(base):
+            return True
+    return False
+
+
+def _set_signature(mock, original, instance=False):
+    # creates a function with signature (*args, **kwargs) that delegates to a
+    # mock. It still does signature checking by calling a lambda with the same
+    # signature as the original.
+    if not _callable(original):
+        return
+
+    skipfirst = isinstance(original, ClassTypes)
+    result = _getsignature(original, skipfirst, instance)
+    if result is None:
+        # was a C function (e.g. object().__init__ ) that can't be mocked
+        return
+
+    signature, func = result
+
+    src = "lambda %s: None" % signature
+    checksig = eval(src, {})
+    _copy_func_details(func, checksig)
+
+    name = original.__name__
+    if not _isidentifier(name):
+        name = 'funcopy'
+    context = {'_checksig_': checksig, 'mock': mock}
+    src = """def %s(*args, **kwargs):
+    _checksig_(*args, **kwargs)
+    return mock(*args, **kwargs)""" % name
+    exec (src, context)
+    funcopy = context[name]
+    _setup_func(funcopy, mock)
+    return funcopy
+
+
+def _setup_func(funcopy, mock):
+    funcopy.mock = mock
+
+    # can't use isinstance with mocks
+    if not _is_instance_mock(mock):
+        return
+
+    def assert_called_with(*args, **kwargs):
+        return mock.assert_called_with(*args, **kwargs)
+    def assert_called_once_with(*args, **kwargs):
+        return mock.assert_called_once_with(*args, **kwargs)
+    def assert_has_calls(*args, **kwargs):
+        return mock.assert_has_calls(*args, **kwargs)
+    def assert_any_call(*args, **kwargs):
+        return mock.assert_any_call(*args, **kwargs)
+    def reset_mock():
+        funcopy.method_calls = _CallList()
+        funcopy.mock_calls = _CallList()
+        mock.reset_mock()
+        ret = funcopy.return_value
+        if _is_instance_mock(ret) and not ret is mock:
+            ret.reset_mock()
+
+    funcopy.called = False
+    funcopy.call_count = 0
+    funcopy.call_args = None
+    funcopy.call_args_list = _CallList()
+    funcopy.method_calls = _CallList()
+    funcopy.mock_calls = _CallList()
+
+    funcopy.return_value = mock.return_value
+    funcopy.side_effect = mock.side_effect
+    funcopy._mock_children = mock._mock_children
+
+    funcopy.assert_called_with = assert_called_with
+    funcopy.assert_called_once_with = assert_called_once_with
+    funcopy.assert_has_calls = assert_has_calls
+    funcopy.assert_any_call = assert_any_call
+    funcopy.reset_mock = reset_mock
+
+    mock._mock_delegate = funcopy
+
+
+def _is_magic(name):
+    return '__%s__' % name[2:-2] == name
+
+
+class _SentinelObject(object):
+    "A unique, named, sentinel object."
+    def __init__(self, name):
+        self.name = name
+
+    def __repr__(self):
+        return 'sentinel.%s' % self.name
+
+
+class _Sentinel(object):
+    """Access attributes to return a named object, usable as a sentinel."""
+    def __init__(self):
+        self._sentinels = {}
+
+    def __getattr__(self, name):
+        if name == '__bases__':
+            # Without this help(mock) raises an exception
+            raise AttributeError
+        return self._sentinels.setdefault(name, _SentinelObject(name))
+
+
+sentinel = _Sentinel()
+
+DEFAULT = sentinel.DEFAULT
+_missing = sentinel.MISSING
+_deleted = sentinel.DELETED
+
+
+class OldStyleClass:
+    pass
+ClassType = type(OldStyleClass)
+
+
+def _copy(value):
+    if type(value) in (dict, list, tuple, set):
+        return type(value)(value)
+    return value
+
+
+ClassTypes = (type,)
+if not inPy3k:
+    ClassTypes = (type, ClassType)
+
+_allowed_names = set(
+    [
+        'return_value', '_mock_return_value', 'side_effect',
+        '_mock_side_effect', '_mock_parent', '_mock_new_parent',
+        '_mock_name', '_mock_new_name'
+    ]
+)
+
+
+def _delegating_property(name):
+    _allowed_names.add(name)
+    _the_name = '_mock_' + name
+    def _get(self, name=name, _the_name=_the_name):
+        sig = self._mock_delegate
+        if sig is None:
+            return getattr(self, _the_name)
+        return getattr(sig, name)
+    def _set(self, value, name=name, _the_name=_the_name):
+        sig = self._mock_delegate
+        if sig is None:
+            self.__dict__[_the_name] = value
+        else:
+            setattr(sig, name, value)
+
+    return property(_get, _set)
+
+
+
+class _CallList(list):
+
+    def __contains__(self, value):
+        if not isinstance(value, list):
+            return list.__contains__(self, value)
+        len_value = len(value)
+        len_self = len(self)
+        if len_value > len_self:
+            return False
+
+        for i in range(0, len_self - len_value + 1):
+            sub_list = self[i:i+len_value]
+            if sub_list == value:
+                return True
+        return False
+
+    def __repr__(self):
+        return pprint.pformat(list(self))
+
+
+def _check_and_set_parent(parent, value, name, new_name):
+    if not _is_instance_mock(value):
+        return False
+    if ((value._mock_name or value._mock_new_name) or
+        (value._mock_parent is not None) or
+        (value._mock_new_parent is not None)):
+        return False
+
+    _parent = parent
+    while _parent is not None:
+        # setting a mock (value) as a child or return value of itself
+        # should not modify the mock
+        if _parent is value:
+            return False
+        _parent = _parent._mock_new_parent
+
+    if new_name:
+        value._mock_new_parent = parent
+        value._mock_new_name = new_name
+    if name:
+        value._mock_parent = parent
+        value._mock_name = name
+    return True
+
+
+
+class Base(object):
+    _mock_return_value = DEFAULT
+    _mock_side_effect = None
+    def __init__(self, *args, **kwargs):
+        pass
+
+
+
+class NonCallableMock(Base):
+    """A non-callable version of `Mock`"""
+
+    def __new__(cls, *args, **kw):
+        # every instance has its own class
+        # so we can create magic methods on the
+        # class without stomping on other mocks
+        new = type(cls.__name__, (cls,), {'__doc__': cls.__doc__})
+        instance = object.__new__(new)
+        return instance
+
+
+    def __init__(
+            self, spec=None, wraps=None, name=None, spec_set=None,
+            parent=None, _spec_state=None, _new_name='', _new_parent=None,
+            **kwargs
+        ):
+        if _new_parent is None:
+            _new_parent = parent
+
+        __dict__ = self.__dict__
+        __dict__['_mock_parent'] = parent
+        __dict__['_mock_name'] = name
+        __dict__['_mock_new_name'] = _new_name
+        __dict__['_mock_new_parent'] = _new_parent
+
+        if spec_set is not None:
+            spec = spec_set
+            spec_set = True
+
+        self._mock_add_spec(spec, spec_set)
+
+        __dict__['_mock_children'] = {}
+        __dict__['_mock_wraps'] = wraps
+        __dict__['_mock_delegate'] = None
+
+        __dict__['_mock_called'] = False
+        __dict__['_mock_call_args'] = None
+        __dict__['_mock_call_count'] = 0
+        __dict__['_mock_call_args_list'] = _CallList()
+        __dict__['_mock_mock_calls'] = _CallList()
+
+        __dict__['method_calls'] = _CallList()
+
+        if kwargs:
+            self.configure_mock(**kwargs)
+
+        _super(NonCallableMock, self).__init__(
+            spec, wraps, name, spec_set, parent,
+            _spec_state
+        )
+
+
+    def attach_mock(self, mock, attribute):
+        """
+        Attach a mock as an attribute of this one, replacing its name and
+        parent. Calls to the attached mock will be recorded in the
+        `method_calls` and `mock_calls` attributes of this one."""
+        mock._mock_parent = None
+        mock._mock_new_parent = None
+        mock._mock_name = ''
+        mock._mock_new_name = None
+
+        setattr(self, attribute, mock)
+
+
+    def mock_add_spec(self, spec, spec_set=False):
+        """Add a spec to a mock. `spec` can either be an object or a
+        list of strings. Only attributes on the `spec` can be fetched as
+        attributes from the mock.
+
+        If `spec_set` is True then only attributes on the spec can be set."""
+        self._mock_add_spec(spec, spec_set)
+
+
+    def _mock_add_spec(self, spec, spec_set):
+        _spec_class = None
+
+        if spec is not None and not _is_list(spec):
+            if isinstance(spec, ClassTypes):
+                _spec_class = spec
+            else:
+                _spec_class = _get_class(spec)
+
+            spec = dir(spec)
+
+        __dict__ = self.__dict__
+        __dict__['_spec_class'] = _spec_class
+        __dict__['_spec_set'] = spec_set
+        __dict__['_mock_methods'] = spec
+
+
+    def __get_return_value(self):
+        ret = self._mock_return_value
+        if self._mock_delegate is not None:
+            ret = self._mock_delegate.return_value
+
+        if ret is DEFAULT:
+            ret = self._get_child_mock(
+                _new_parent=self, _new_name='()'
+            )
+            self.return_value = ret
+        return ret
+
+
+    def __set_return_value(self, value):
+        if self._mock_delegate is not None:
+            self._mock_delegate.return_value = value
+        else:
+            self._mock_return_value = value
+            _check_and_set_parent(self, value, None, '()')
+
+    __return_value_doc = "The value to be returned when the mock is called."
+    return_value = property(__get_return_value, __set_return_value,
+                            __return_value_doc)
+
+
+    @property
+    def __class__(self):
+        if self._spec_class is None:
+            return type(self)
+        return self._spec_class
+
+    called = _delegating_property('called')
+    call_count = _delegating_property('call_count')
+    call_args = _delegating_property('call_args')
+    call_args_list = _delegating_property('call_args_list')
+    mock_calls = _delegating_property('mock_calls')
+
+
+    def __get_side_effect(self):
+        sig = self._mock_delegate
+        if sig is None:
+            return self._mock_side_effect
+        return sig.side_effect
+
+    def __set_side_effect(self, value):
+        value = _try_iter(value)
+        sig = self._mock_delegate
+        if sig is None:
+            self._mock_side_effect = value
+        else:
+            sig.side_effect = value
+
+    side_effect = property(__get_side_effect, __set_side_effect)
+
+
+    def reset_mock(self):
+        "Restore the mock object to its initial state."
+        self.called = False
+        self.call_args = None
+        self.call_count = 0
+        self.mock_calls = _CallList()
+        self.call_args_list = _CallList()
+        self.method_calls = _CallList()
+
+        for child in self._mock_children.values():
+            if isinstance(child, _SpecState):
+                continue
+            child.reset_mock()
+
+        ret = self._mock_return_value
+        if _is_instance_mock(ret) and ret is not self:
+            ret.reset_mock()
+
+
+    def configure_mock(self, **kwargs):
+        """Set attributes on the mock through keyword arguments.
+
+        Attributes plus return values and side effects can be set on child
+        mocks using standard dot notation and unpacking a dictionary in the
+        method call:
+
+        >>> attrs = {'method.return_value': 3, 'other.side_effect': KeyError}
+        >>> mock.configure_mock(**attrs)"""
+        for arg, val in sorted(kwargs.items(),
+                               # we sort on the number of dots so that
+                               # attributes are set before we set attributes on
+                               # attributes
+                               key=lambda entry: entry[0].count('.')):
+            args = arg.split('.')
+            final = args.pop()
+            obj = self
+            for entry in args:
+                obj = getattr(obj, entry)
+            setattr(obj, final, val)
+
+
+    def __getattr__(self, name):
+        if name == '_mock_methods':
+            raise AttributeError(name)
+        elif self._mock_methods is not None:
+            if name not in self._mock_methods or name in _all_magics:
+                raise AttributeError("Mock object has no attribute %r" % name)
+        elif _is_magic(name):
+            raise AttributeError(name)
+
+        result = self._mock_children.get(name)
+        if result is _deleted:
+            raise AttributeError(name)
+        elif result is None:
+            wraps = None
+            if self._mock_wraps is not None:
+                # XXXX should we get the attribute without triggering code
+                # execution?
+                wraps = getattr(self._mock_wraps, name)
+
+            result = self._get_child_mock(
+                parent=self, name=name, wraps=wraps, _new_name=name,
+                _new_parent=self
+            )
+            self._mock_children[name]  = result
+
+        elif isinstance(result, _SpecState):
+            result = create_autospec(
+                result.spec, result.spec_set, result.instance,
+                result.parent, result.name
+            )
+            self._mock_children[name]  = result
+
+        return result
+
+
+    def __repr__(self):
+        _name_list = [self._mock_new_name]
+        _parent = self._mock_new_parent
+        last = self
+
+        dot = '.'
+        if _name_list == ['()']:
+            dot = ''
+        seen = set()
+        while _parent is not None:
+            last = _parent
+
+            _name_list.append(_parent._mock_new_name + dot)
+            dot = '.'
+            if _parent._mock_new_name == '()':
+                dot = ''
+
+            _parent = _parent._mock_new_parent
+
+            # use ids here so as not to call __hash__ on the mocks
+            if id(_parent) in seen:
+                break
+            seen.add(id(_parent))
+
+        _name_list = list(reversed(_name_list))
+        _first = last._mock_name or 'mock'
+        if len(_name_list) > 1:
+            if _name_list[1] not in ('()', '().'):
+                _first += '.'
+        _name_list[0] = _first
+        name = ''.join(_name_list)
+
+        name_string = ''
+        if name not in ('mock', 'mock.'):
+            name_string = ' name=%r' % name
+
+        spec_string = ''
+        if self._spec_class is not None:
+            spec_string = ' spec=%r'
+            if self._spec_set:
+                spec_string = ' spec_set=%r'
+            spec_string = spec_string % self._spec_class.__name__
+        return "<%s%s%s id='%s'>" % (
+            type(self).__name__,
+            name_string,
+            spec_string,
+            id(self)
+        )
+
+
+    def __dir__(self):
+        """Filter the output of `dir(mock)` to only useful members.
+        XXXX
+        """
+        extras = self._mock_methods or []
+        from_type = dir(type(self))
+        from_dict = list(self.__dict__)
+
+        if FILTER_DIR:
+            from_type = [e for e in from_type if not e.startswith('_')]
+            from_dict = [e for e in from_dict if not e.startswith('_') or
+                         _is_magic(e)]
+        return sorted(set(extras + from_type + from_dict +
+                          list(self._mock_children)))
+
+
+    def __setattr__(self, name, value):
+        if name in _allowed_names:
+            # property setters go through here
+            return object.__setattr__(self, name, value)
+        elif (self._spec_set and self._mock_methods is not None and
+            name not in self._mock_methods and
+            name not in self.__dict__):
+            raise AttributeError("Mock object has no attribute '%s'" % name)
+        elif name in _unsupported_magics:
+            msg = 'Attempting to set unsupported magic method %r.' % name
+            raise AttributeError(msg)
+        elif name in _all_magics:
+            if self._mock_methods is not None and name not in self._mock_methods:
+                raise AttributeError("Mock object has no attribute '%s'" % name)
+
+            if not _is_instance_mock(value):
+                setattr(type(self), name, _get_method(name, value))
+                original = value
+                value = lambda *args, **kw: original(self, *args, **kw)
+            else:
+                # only set _new_name and not name so that mock_calls is tracked
+                # but not method calls
+                _check_and_set_parent(self, value, None, name)
+                setattr(type(self), name, value)
+                self._mock_children[name] = value
+        elif name == '__class__':
+            self._spec_class = value
+            return
+        else:
+            if _check_and_set_parent(self, value, name, name):
+                self._mock_children[name] = value
+        return object.__setattr__(self, name, value)
+
+
+    def __delattr__(self, name):
+        if name in _all_magics and name in type(self).__dict__:
+            delattr(type(self), name)
+            if name not in self.__dict__:
+                # for magic methods that are still MagicProxy objects and
+                # not set on the instance itself
+                return
+
+        if name in self.__dict__:
+            object.__delattr__(self, name)
+
+        obj = self._mock_children.get(name, _missing)
+        if obj is _deleted:
+            raise AttributeError(name)
+        if obj is not _missing:
+            del self._mock_children[name]
+        self._mock_children[name] = _deleted
+
+
+
+    def _format_mock_call_signature(self, args, kwargs):
+        name = self._mock_name or 'mock'
+        return _format_call_signature(name, args, kwargs)
+
+
+    def _format_mock_failure_message(self, args, kwargs):
+        message = 'Expected call: %s\nActual call: %s'
+        expected_string = self._format_mock_call_signature(args, kwargs)
+        call_args = self.call_args
+        if len(call_args) == 3:
+            call_args = call_args[1:]
+        actual_string = self._format_mock_call_signature(*call_args)
+        return message % (expected_string, actual_string)
+
+
+    def assert_called_with(_mock_self, *args, **kwargs):
+        """assert that the mock was called with the specified arguments.
+
+        Raises an AssertionError if the args and keyword args passed in are
+        different to the last call to the mock."""
+        self = _mock_self
+        if self.call_args is None:
+            expected = self._format_mock_call_signature(args, kwargs)
+            raise AssertionError('Expected call: %s\nNot called' % (expected,))
+
+        if self.call_args != (args, kwargs):
+            msg = self._format_mock_failure_message(args, kwargs)
+            raise AssertionError(msg)
+
+
+    def assert_called_once_with(_mock_self, *args, **kwargs):
+        """assert that the mock was called exactly once and with the specified
+        arguments."""
+        self = _mock_self
+        if not self.call_count == 1:
+            msg = ("Expected to be called once. Called %s times." %
+                   self.call_count)
+            raise AssertionError(msg)
+        return self.assert_called_with(*args, **kwargs)
+
+
+    def assert_has_calls(self, calls, any_order=False):
+        """assert the mock has been called with the specified calls.
+        The `mock_calls` list is checked for the calls.
+
+        If `any_order` is False (the default) then the calls must be
+        sequential. There can be extra calls before or after the
+        specified calls.
+
+        If `any_order` is True then the calls can be in any order, but
+        they must all appear in `mock_calls`."""
+        if not any_order:
+            if calls not in self.mock_calls:
+                raise AssertionError(
+                    'Calls not found.\nExpected: %r\n'
+                    'Actual: %r' % (calls, self.mock_calls)
+                )
+            return
+
+        all_calls = list(self.mock_calls)
+
+        not_found = []
+        for kall in calls:
+            try:
+                all_calls.remove(kall)
+            except ValueError:
+                not_found.append(kall)
+        if not_found:
+            raise AssertionError(
+                '%r not all found in call list' % (tuple(not_found),)
+            )
+
+
+    def assert_any_call(self, *args, **kwargs):
+        """assert the mock has been called with the specified arguments.
+
+        The assert passes if the mock has *ever* been called, unlike
+        `assert_called_with` and `assert_called_once_with` that only pass if
+        the call is the most recent one."""
+        kall = call(*args, **kwargs)
+        if kall not in self.call_args_list:
+            expected_string = self._format_mock_call_signature(args, kwargs)
+            raise AssertionError(
+                '%s call not found' % expected_string
+            )
+
+
+    def _get_child_mock(self, **kw):
+        """Create the child mocks for attributes and return value.
+        By default child mocks will be the same type as the parent.
+        Subclasses of Mock may want to override this to customize the way
+        child mocks are made.
+
+        For non-callable mocks the callable variant will be used (rather than
+        any custom subclass)."""
+        _type = type(self)
+        if not issubclass(_type, CallableMixin):
+            if issubclass(_type, NonCallableMagicMock):
+                klass = MagicMock
+            elif issubclass(_type, NonCallableMock) :
+                klass = Mock
+        else:
+            klass = _type.__mro__[1]
+        return klass(**kw)
+
+
+
+def _try_iter(obj):
+    if obj is None:
+        return obj
+    if _is_exception(obj):
+        return obj
+    if _callable(obj):
+        return obj
+    try:
+        return iter(obj)
+    except TypeError:
+        # XXXX backwards compatibility
+        # but this will blow up on first call - so maybe we should fail early?
+        return obj
+
+
+
+class CallableMixin(Base):
+
+    def __init__(self, spec=None, side_effect=None, return_value=DEFAULT,
+                 wraps=None, name=None, spec_set=None, parent=None,
+                 _spec_state=None, _new_name='', _new_parent=None, **kwargs):
+        self.__dict__['_mock_return_value'] = return_value
+
+        _super(CallableMixin, self).__init__(
+            spec, wraps, name, spec_set, parent,
+            _spec_state, _new_name, _new_parent, **kwargs
+        )
+
+        self.side_effect = side_effect
+
+
+    def _mock_check_sig(self, *args, **kwargs):
+        # stub method that can be replaced with one with a specific signature
+        pass
+
+
+    def __call__(_mock_self, *args, **kwargs):
+        # can't use self in-case a function / method we are mocking uses self
+        # in the signature
+        _mock_self._mock_check_sig(*args, **kwargs)
+        return _mock_self._mock_call(*args, **kwargs)
+
+
+    def _mock_call(_mock_self, *args, **kwargs):
+        self = _mock_self
+        self.called = True
+        self.call_count += 1
+        self.call_args = _Call((args, kwargs), two=True)
+        self.call_args_list.append(_Call((args, kwargs), two=True))
+
+        _new_name = self._mock_new_name
+        _new_parent = self._mock_new_parent
+        self.mock_calls.append(_Call(('', args, kwargs)))
+
+        seen = set()
+        skip_next_dot = _new_name == '()'
+        do_method_calls = self._mock_parent is not None
+        name = self._mock_name
+        while _new_parent is not None:
+            this_mock_call = _Call((_new_name, args, kwargs))
+            if _new_parent._mock_new_name:
+                dot = '.'
+                if skip_next_dot:
+                    dot = ''
+
+                skip_next_dot = False
+                if _new_parent._mock_new_name == '()':
+                    skip_next_dot = True
+
+                _new_name = _new_parent._mock_new_name + dot + _new_name
+
+            if do_method_calls:
+                if _new_name == name:
+                    this_method_call = this_mock_call
+                else:
+                    this_method_call = _Call((name, args, kwargs))
+                _new_parent.method_calls.append(this_method_call)
+
+                do_method_calls = _new_parent._mock_parent is not None
+                if do_method_calls:
+                    name = _new_parent._mock_name + '.' + name
+
+            _new_parent.mock_calls.append(this_mock_call)
+            _new_parent = _new_parent._mock_new_parent
+
+            # use ids here so as not to call __hash__ on the mocks
+            _new_parent_id = id(_new_parent)
+            if _new_parent_id in seen:
+                break
+            seen.add(_new_parent_id)
+
+        ret_val = DEFAULT
+        effect = self.side_effect
+        if effect is not None:
+            if _is_exception(effect):
+                raise effect
+
+            if not _callable(effect):
+                result = next(effect)
+                if _is_exception(result):
+                    raise result
+                return result
+
+            ret_val = effect(*args, **kwargs)
+            if ret_val is DEFAULT:
+                ret_val = self.return_value
+
+        if (self._mock_wraps is not None and
+             self._mock_return_value is DEFAULT):
+            return self._mock_wraps(*args, **kwargs)
+        if ret_val is DEFAULT:
+            ret_val = self.return_value
+        return ret_val
+
+
+
+class Mock(CallableMixin, NonCallableMock):
+    """
+    Create a new `Mock` object. `Mock` takes several optional arguments
+    that specify the behaviour of the Mock object:
+
+    * `spec`: This can be either a list of strings or an existing object (a
+      class or instance) that acts as the specification for the mock object. If
+      you pass in an object then a list of strings is formed by calling dir on
+      the object (excluding unsupported magic attributes and methods). Accessing
+      any attribute not in this list will raise an `AttributeError`.
+
+      If `spec` is an object (rather than a list of strings) then
+      `mock.__class__` returns the class of the spec object. This allows mocks
+      to pass `isinstance` tests.
+
+    * `spec_set`: A stricter variant of `spec`. If used, attempting to *set*
+      or get an attribute on the mock that isn't on the object passed as
+      `spec_set` will raise an `AttributeError`.
+
+    * `side_effect`: A function to be called whenever the Mock is called. See
+      the `side_effect` attribute. Useful for raising exceptions or
+      dynamically changing return values. The function is called with the same
+      arguments as the mock, and unless it returns `DEFAULT`, the return
+      value of this function is used as the return value.
+
+      Alternatively `side_effect` can be an exception class or instance. In
+      this case the exception will be raised when the mock is called.
+
+      If `side_effect` is an iterable then each call to the mock will return
+      the next value from the iterable. If any of the members of the iterable
+      are exceptions they will be raised instead of returned.
+
+    * `return_value`: The value returned when the mock is called. By default
+      this is a new Mock (created on first access). See the
+      `return_value` attribute.
+
+    * `wraps`: Item for the mock object to wrap. If `wraps` is not None then
+      calling the Mock will pass the call through to the wrapped object
+      (returning the real result). Attribute access on the mock will return a
+      Mock object that wraps the corresponding attribute of the wrapped object
+      (so attempting to access an attribute that doesn't exist will raise an
+      `AttributeError`).
+
+      If the mock has an explicit `return_value` set then calls are not passed
+      to the wrapped object and the `return_value` is returned instead.
+
+    * `name`: If the mock has a name then it will be used in the repr of the
+      mock. This can be useful for debugging. The name is propagated to child
+      mocks.
+
+    Mocks can also be called with arbitrary keyword arguments. These will be
+    used to set attributes on the mock after it is created.
+    """
+
+
+
+def _dot_lookup(thing, comp, import_path):
+    try:
+        return getattr(thing, comp)
+    except AttributeError:
+        __import__(import_path)
+        return getattr(thing, comp)
+
+
+def _importer(target):
+    components = target.split('.')
+    import_path = components.pop(0)
+    thing = __import__(import_path)
+
+    for comp in components:
+        import_path += ".%s" % comp
+        thing = _dot_lookup(thing, comp, import_path)
+    return thing
+
+
+def _is_started(patcher):
+    # XXXX horrible
+    return hasattr(patcher, 'is_local')
+
+
+class _patch(object):
+
+    attribute_name = None
+    _active_patches = set()
+
+    def __init__(
+            self, getter, attribute, new, spec, create,
+            spec_set, autospec, new_callable, kwargs
+        ):
+        if new_callable is not None:
+            if new is not DEFAULT:
+                raise ValueError(
+                    "Cannot use 'new' and 'new_callable' together"
+                )
+            if autospec is not None:
+                raise ValueError(
+                    "Cannot use 'autospec' and 'new_callable' together"
+                )
+
+        self.getter = getter
+        self.attribute = attribute
+        self.new = new
+        self.new_callable = new_callable
+        self.spec = spec
+        self.create = create
+        self.has_local = False
+        self.spec_set = spec_set
+        self.autospec = autospec
+        self.kwargs = kwargs
+        self.additional_patchers = []
+
+
+    def copy(self):
+        patcher = _patch(
+            self.getter, self.attribute, self.new, self.spec,
+            self.create, self.spec_set,
+            self.autospec, self.new_callable, self.kwargs
+        )
+        patcher.attribute_name = self.attribute_name
+        patcher.additional_patchers = [
+            p.copy() for p in self.additional_patchers
+        ]
+        return patcher
+
+
+    def __call__(self, func):
+        if isinstance(func, ClassTypes):
+            return self.decorate_class(func)
+        return self.decorate_callable(func)
+
+
+    def decorate_class(self, klass):
+        for attr in dir(klass):
+            if not attr.startswith(patch.TEST_PREFIX):
+                continue
+
+            attr_value = getattr(klass, attr)
+            if not hasattr(attr_value, "__call__"):
+                continue
+
+            patcher = self.copy()
+            setattr(klass, attr, patcher(attr_value))
+        return klass
+
+
+    def decorate_callable(self, func):
+        if hasattr(func, 'patchings'):
+            func.patchings.append(self)
+            return func
+
+        @wraps(func)
+        def patched(*args, **keywargs):
+            # don't use a with here (backwards compatability with Python 2.4)
+            extra_args = []
+            entered_patchers = []
+
+            # can't use try...except...finally because of Python 2.4
+            # compatibility
+            exc_info = tuple()
+            try:
+                try:
+                    for patching in patched.patchings:
+                        arg = patching.__enter__()
+                        entered_patchers.append(patching)
+                        if patching.attribute_name is not None:
+                            keywargs.update(arg)
+                        elif patching.new is DEFAULT:
+                            extra_args.append(arg)
+
+                    args += tuple(extra_args)
+                    return func(*args, **keywargs)
+                except:
+                    if (patching not in entered_patchers and
+                        _is_started(patching)):
+                        # the patcher may have been started, but an exception
+                        # raised whilst entering one of its additional_patchers
+                        entered_patchers.append(patching)
+                    # Pass the exception to __exit__
+                    exc_info = sys.exc_info()
+                    # re-raise the exception
+                    raise
+            finally:
+                for patching in reversed(entered_patchers):
+                    patching.__exit__(*exc_info)
+
+        patched.patchings = [self]
+        if hasattr(func, 'func_code'):
+            # not in Python 3
+            patched.compat_co_firstlineno = getattr(
+                func, "compat_co_firstlineno",
+                func.func_code.co_firstlineno
+            )
+        return patched
+
+
+    def get_original(self):
+        target = self.getter()
+        name = self.attribute
+
+        original = DEFAULT
+        local = False
+
+        try:
+            original = target.__dict__[name]
+        except (AttributeError, KeyError):
+            original = getattr(target, name, DEFAULT)
+        else:
+            local = True
+
+        if not self.create and original is DEFAULT:
+            raise AttributeError(
+                "%s does not have the attribute %r" % (target, name)
+            )
+        return original, local
+
+
+    def __enter__(self):
+        """Perform the patch."""
+        new, spec, spec_set = self.new, self.spec, self.spec_set
+        autospec, kwargs = self.autospec, self.kwargs
+        new_callable = self.new_callable
+        self.target = self.getter()
+
+        # normalise False to None
+        if spec is False:
+            spec = None
+        if spec_set is False:
+            spec_set = None
+        if autospec is False:
+            autospec = None
+
+        if spec is not None and autospec is not None:
+            raise TypeError("Can't specify spec and autospec")
+        if ((spec is not None or autospec is not None) and
+            spec_set not in (True, None)):
+            raise TypeError("Can't provide explicit spec_set *and* spec or autospec")
+
+        original, local = self.get_original()
+
+        if new is DEFAULT and autospec is None:
+            inherit = False
+            if spec is True:
+                # set spec to the object we are replacing
+                spec = original
+                if spec_set is True:
+                    spec_set = original
+                    spec = None
+            elif spec is not None:
+                if spec_set is True:
+                    spec_set = spec
+                    spec = None
+            elif spec_set is True:
+                spec_set = original
+
+            if spec is not None or spec_set is not None:
+                if original is DEFAULT:
+                    raise TypeError("Can't use 'spec' with create=True")
+                if isinstance(original, ClassTypes):
+                    # If we're patching out a class and there is a spec
+                    inherit = True
+
+            Klass = MagicMock
+            _kwargs = {}
+            if new_callable is not None:
+                Klass = new_callable
+            elif spec is not None or spec_set is not None:
+                this_spec = spec
+                if spec_set is not None:
+                    this_spec = spec_set
+                if _is_list(this_spec):
+                    not_callable = '__call__' not in this_spec
+                else:
+                    not_callable = not _callable(this_spec)
+                if not_callable:
+                    Klass = NonCallableMagicMock
+
+            if spec is not None:
+                _kwargs['spec'] = spec
+            if spec_set is not None:
+                _kwargs['spec_set'] = spec_set
+
+            # add a name to mocks
+            if (isinstance(Klass, type) and
+                issubclass(Klass, NonCallableMock) and self.attribute):
+                _kwargs['name'] = self.attribute
+
+            _kwargs.update(kwargs)
+            new = Klass(**_kwargs)
+
+            if inherit and _is_instance_mock(new):
+                # we can only tell if the instance should be callable if the
+                # spec is not a list
+                this_spec = spec
+                if spec_set is not None:
+                    this_spec = spec_set
+                if (not _is_list(this_spec) and not
+                    _instance_callable(this_spec)):
+                    Klass = NonCallableMagicMock
+
+                _kwargs.pop('name')
+                new.return_value = Klass(_new_parent=new, _new_name='()',
+                                         **_kwargs)
+        elif autospec is not None:
+            # spec is ignored, new *must* be default, spec_set is treated
+            # as a boolean. Should we check spec is not None and that spec_set
+            # is a bool?
+            if new is not DEFAULT:
+                raise TypeError(
+                    "autospec creates the mock for you. Can't specify "
+                    "autospec and new."
+                )
+            if original is DEFAULT:
+                raise TypeError("Can't use 'autospec' with create=True")
+            spec_set = bool(spec_set)
+            if autospec is True:
+                autospec = original
+
+            new = create_autospec(autospec, spec_set=spec_set,
+                                  _name=self.attribute, **kwargs)
+        elif kwargs:
+            # can't set keyword args when we aren't creating the mock
+            # XXXX If new is a Mock we could call new.configure_mock(**kwargs)
+            raise TypeError("Can't pass kwargs to a mock we aren't creating")
+
+        new_attr = new
+
+        self.temp_original = original
+        self.is_local = local
+        setattr(self.target, self.attribute, new_attr)
+        if self.attribute_name is not None:
+            extra_args = {}
+            if self.new is DEFAULT:
+                extra_args[self.attribute_name] =  new
+            for patching in self.additional_patchers:
+                arg = patching.__enter__()
+                if patching.new is DEFAULT:
+                    extra_args.update(arg)
+            return extra_args
+
+        return new
+
+
+    def __exit__(self, *exc_info):
+        """Undo the patch."""
+        if not _is_started(self):
+            raise RuntimeError('stop called on unstarted patcher')
+
+        if self.is_local and self.temp_original is not DEFAULT:
+            setattr(self.target, self.attribute, self.temp_original)
+        else:
+            delattr(self.target, self.attribute)
+            if not self.create and not hasattr(self.target, self.attribute):
+                # needed for proxy objects like django settings
+                setattr(self.target, self.attribute, self.temp_original)
+
+        del self.temp_original
+        del self.is_local
+        del self.target
+        for patcher in reversed(self.additional_patchers):
+            if _is_started(patcher):
+                patcher.__exit__(*exc_info)
+
+
+    def start(self):
+        """Activate a patch, returning any created mock."""
+        result = self.__enter__()
+        self._active_patches.add(self)
+        return result
+
+
+    def stop(self):
+        """Stop an active patch."""
+        self._active_patches.discard(self)
+        return self.__exit__()
+
+
+
+def _get_target(target):
+    try:
+        target, attribute = target.rsplit('.', 1)
+    except (TypeError, ValueError):
+        raise TypeError("Need a valid target to patch. You supplied: %r" %
+                        (target,))
+    getter = lambda: _importer(target)
+    return getter, attribute
+
+
+def _patch_object(
+        target, attribute, new=DEFAULT, spec=None,
+        create=False, spec_set=None, autospec=None,
+        new_callable=None, **kwargs
+    ):
+    """
+    patch.object(target, attribute, new=DEFAULT, spec=None, create=False,
+                 spec_set=None, autospec=None, new_callable=None, **kwargs)
+
+    patch the named member (`attribute`) on an object (`target`) with a mock
+    object.
+
+    `patch.object` can be used as a decorator, class decorator or a context
+    manager. Arguments `new`, `spec`, `create`, `spec_set`,
+    `autospec` and `new_callable` have the same meaning as for `patch`. Like
+    `patch`, `patch.object` takes arbitrary keyword arguments for configuring
+    the mock object it creates.
+
+    When used as a class decorator `patch.object` honours `patch.TEST_PREFIX`
+    for choosing which methods to wrap.
+    """
+    getter = lambda: target
+    return _patch(
+        getter, attribute, new, spec, create,
+        spec_set, autospec, new_callable, kwargs
+    )
+
+
+def _patch_multiple(target, spec=None, create=False, spec_set=None,
+                    autospec=None, new_callable=None, **kwargs):
+    """Perform multiple patches in a single call. It takes the object to be
+    patched (either as an object or a string to fetch the object by importing)
+    and keyword arguments for the patches::
+
+        with patch.multiple(settings, FIRST_PATCH='one', SECOND_PATCH='two'):
+            ...
+
+    Use `DEFAULT` as the value if you want `patch.multiple` to create
+    mocks for you. In this case the created mocks are passed into a decorated
+    function by keyword, and a dictionary is returned when `patch.multiple` is
+    used as a context manager.
+
+    `patch.multiple` can be used as a decorator, class decorator or a context
+    manager. The arguments `spec`, `spec_set`, `create`,
+    `autospec` and `new_callable` have the same meaning as for `patch`. These
+    arguments will be applied to *all* patches done by `patch.multiple`.
+
+    When used as a class decorator `patch.multiple` honours `patch.TEST_PREFIX`
+    for choosing which methods to wrap.
+    """
+    if type(target) in (unicode, str):
+        getter = lambda: _importer(target)
+    else:
+        getter = lambda: target
+
+    if not kwargs:
+        raise ValueError(
+            'Must supply at least one keyword argument with patch.multiple'
+        )
+    # need to wrap in a list for python 3, where items is a view
+    items = list(kwargs.items())
+    attribute, new = items[0]
+    patcher = _patch(
+        getter, attribute, new, spec, create, spec_set,
+        autospec, new_callable, {}
+    )
+    patcher.attribute_name = attribute
+    for attribute, new in items[1:]:
+        this_patcher = _patch(
+            getter, attribute, new, spec, create, spec_set,
+            autospec, new_callable, {}
+        )
+        this_patcher.attribute_name = attribute
+        patcher.additional_patchers.append(this_patcher)
+    return patcher
+
+
+def patch(
+        target, new=DEFAULT, spec=None, create=False,
+        spec_set=None, autospec=None, new_callable=None, **kwargs
+    ):
+    """
+    `patch` acts as a function decorator, class decorator or a context
+    manager. Inside the body of the function or with statement, the `target`
+    is patched with a `new` object. When the function/with statement exits
+    the patch is undone.
+
+    If `new` is omitted, then the target is replaced with a
+    `MagicMock`. If `patch` is used as a decorator and `new` is
+    omitted, the created mock is passed in as an extra argument to the
+    decorated function. If `patch` is used as a context manager the created
+    mock is returned by the context manager.
+
+    `target` should be a string in the form `'package.module.ClassName'`. The
+    `target` is imported and the specified object replaced with the `new`
+    object, so the `target` must be importable from the environment you are
+    calling `patch` from. The target is imported when the decorated function
+    is executed, not at decoration time.
+
+    The `spec` and `spec_set` keyword arguments are passed to the `MagicMock`
+    if patch is creating one for you.
+
+    In addition you can pass `spec=True` or `spec_set=True`, which causes
+    patch to pass in the object being mocked as the spec/spec_set object.
+
+    `new_callable` allows you to specify a different class, or callable object,
+    that will be called to create the `new` object. By default `MagicMock` is
+    used.
+
+    A more powerful form of `spec` is `autospec`. If you set `autospec=True`
+    then the mock with be created with a spec from the object being replaced.
+    All attributes of the mock will also have the spec of the corresponding
+    attribute of the object being replaced. Methods and functions being
+    mocked will have their arguments checked and will raise a `TypeError` if
+    they are called with the wrong signature. For mocks replacing a class,
+    their return value (the 'instance') will have the same spec as the class.
+
+    Instead of `autospec=True` you can pass `autospec=some_object` to use an
+    arbitrary object as the spec instead of the one being replaced.
+
+    By default `patch` will fail to replace attributes that don't exist. If
+    you pass in `create=True`, and the attribute doesn't exist, patch will
+    create the attribute for you when the patched function is called, and
+    delete it again afterwards. This is useful for writing tests against
+    attributes that your production code creates at runtime. It is off by by
+    default because it can be dangerous. With it switched on you can write
+    passing tests against APIs that don't actually exist!
+
+    Patch can be used as a `TestCase` class decorator. It works by
+    decorating each test method in the class. This reduces the boilerplate
+    code when your test methods share a common patchings set. `patch` finds
+    tests by looking for method names that start with `patch.TEST_PREFIX`.
+    By default this is `test`, which matches the way `unittest` finds tests.
+    You can specify an alternative prefix by setting `patch.TEST_PREFIX`.
+
+    Patch can be used as a context manager, with the with statement. Here the
+    patching applies to the indented block after the with statement. If you
+    use "as" then the patched object will be bound to the name after the
+    "as"; very useful if `patch` is creating a mock object for you.
+
+    `patch` takes arbitrary keyword arguments. These will be passed to
+    the `Mock` (or `new_callable`) on construction.
+
+    `patch.dict(...)`, `patch.multiple(...)` and `patch.object(...)` are
+    available for alternate use-cases.
+    """
+    getter, attribute = _get_target(target)
+    return _patch(
+        getter, attribute, new, spec, create,
+        spec_set, autospec, new_callable, kwargs
+    )
+
+
+class _patch_dict(object):
+    """
+    Patch a dictionary, or dictionary like object, and restore the dictionary
+    to its original state after the test.
+
+    `in_dict` can be a dictionary or a mapping like container. If it is a
+    mapping then it must at least support getting, setting and deleting items
+    plus iterating over keys.
+
+    `in_dict` can also be a string specifying the name of the dictionary, which
+    will then be fetched by importing it.
+
+    `values` can be a dictionary of values to set in the dictionary. `values`
+    can also be an iterable of `(key, value)` pairs.
+
+    If `clear` is True then the dictionary will be cleared before the new
+    values are set.
+
+    `patch.dict` can also be called with arbitrary keyword arguments to set
+    values in the dictionary::
+
+        with patch.dict('sys.modules', mymodule=Mock(), other_module=Mock()):
+            ...
+
+    `patch.dict` can be used as a context manager, decorator or class
+    decorator. When used as a class decorator `patch.dict` honours
+    `patch.TEST_PREFIX` for choosing which methods to wrap.
+    """
+
+    def __init__(self, in_dict, values=(), clear=False, **kwargs):
+        if isinstance(in_dict, basestring):
+            in_dict = _importer(in_dict)
+        self.in_dict = in_dict
+        # support any argument supported by dict(...) constructor
+        self.values = dict(values)
+        self.values.update(kwargs)
+        self.clear = clear
+        self._original = None
+
+
+    def __call__(self, f):
+        if isinstance(f, ClassTypes):
+            return self.decorate_class(f)
+        @wraps(f)
+        def _inner(*args, **kw):
+            self._patch_dict()
+            try:
+                return f(*args, **kw)
+            finally:
+                self._unpatch_dict()
+
+        return _inner
+
+
+    def decorate_class(self, klass):
+        for attr in dir(klass):
+            attr_value = getattr(klass, attr)
+            if (attr.startswith(patch.TEST_PREFIX) and
+                 hasattr(attr_value, "__call__")):
+                decorator = _patch_dict(self.in_dict, self.values, self.clear)
+                decorated = decorator(attr_value)
+                setattr(klass, attr, decorated)
+        return klass
+
+
+    def __enter__(self):
+        """Patch the dict."""
+        self._patch_dict()
+
+
+    def _patch_dict(self):
+        values = self.values
+        in_dict = self.in_dict
+        clear = self.clear
+
+        try:
+            original = in_dict.copy()
+        except AttributeError:
+            # dict like object with no copy method
+            # must support iteration over keys
+            original = {}
+            for key in in_dict:
+                original[key] = in_dict[key]
+        self._original = original
+
+        if clear:
+            _clear_dict(in_dict)
+
+        try:
+            in_dict.update(values)
+        except AttributeError:
+            # dict like object with no update method
+            for key in values:
+                in_dict[key] = values[key]
+
+
+    def _unpatch_dict(self):
+        in_dict = self.in_dict
+        original = self._original
+
+        _clear_dict(in_dict)
+
+        try:
+            in_dict.update(original)
+        except AttributeError:
+            for key in original:
+                in_dict[key] = original[key]
+
+
+    def __exit__(self, *args):
+        """Unpatch the dict."""
+        self._unpatch_dict()
+        return False
+
+    start = __enter__
+    stop = __exit__
+
+
+def _clear_dict(in_dict):
+    try:
+        in_dict.clear()
+    except AttributeError:
+        keys = list(in_dict)
+        for key in keys:
+            del in_dict[key]
+
+
+def _patch_stopall():
+    """Stop all active patches."""
+    for patch in list(_patch._active_patches):
+        patch.stop()
+
+
+patch.object = _patch_object
+patch.dict = _patch_dict
+patch.multiple = _patch_multiple
+patch.stopall = _patch_stopall
+patch.TEST_PREFIX = 'test'
+
+magic_methods = (
+    "lt le gt ge eq ne "
+    "getitem setitem delitem "
+    "len contains iter "
+    "hash str sizeof "
+    "enter exit "
+    "divmod neg pos abs invert "
+    "complex int float index "
+    "trunc floor ceil "
+)
+
+numerics = "add sub mul div floordiv mod lshift rshift and xor or pow "
+inplace = ' '.join('i%s' % n for n in numerics.split())
+right = ' '.join('r%s' % n for n in numerics.split())
+extra = ''
+if inPy3k:
+    extra = 'bool next '
+else:
+    extra = 'unicode long nonzero oct hex truediv rtruediv '
+
+# not including __prepare__, __instancecheck__, __subclasscheck__
+# (as they are metaclass methods)
+# __del__ is not supported at all as it causes problems if it exists
+
+_non_defaults = set('__%s__' % method for method in [
+    'cmp', 'getslice', 'setslice', 'coerce', 'subclasses',
+    'format', 'get', 'set', 'delete', 'reversed',
+    'missing', 'reduce', 'reduce_ex', 'getinitargs',
+    'getnewargs', 'getstate', 'setstate', 'getformat',
+    'setformat', 'repr', 'dir'
+])
+
+
+def _get_method(name, func):
+    "Turns a callable object (like a mock) into a real function"
+    def method(self, *args, **kw):
+        return func(self, *args, **kw)
+    method.__name__ = name
+    return method
+
+
+_magics = set(
+    '__%s__' % method for method in
+    ' '.join([magic_methods, numerics, inplace, right, extra]).split()
+)
+
+_all_magics = _magics | _non_defaults
+
+_unsupported_magics = set([
+    '__getattr__', '__setattr__',
+    '__init__', '__new__', '__prepare__'
+    '__instancecheck__', '__subclasscheck__',
+    '__del__'
+])
+
+_calculate_return_value = {
+    '__hash__': lambda self: object.__hash__(self),
+    '__str__': lambda self: object.__str__(self),
+    '__sizeof__': lambda self: object.__sizeof__(self),
+    '__unicode__': lambda self: unicode(object.__str__(self)),
+}
+
+_return_values = {
+    '__lt__': NotImplemented,
+    '__gt__': NotImplemented,
+    '__le__': NotImplemented,
+    '__ge__': NotImplemented,
+    '__int__': 1,
+    '__contains__': False,
+    '__len__': 0,
+    '__exit__': False,
+    '__complex__': 1j,
+    '__float__': 1.0,
+    '__bool__': True,
+    '__nonzero__': True,
+    '__oct__': '1',
+    '__hex__': '0x1',
+    '__long__': long(1),
+    '__index__': 1,
+}
+
+
+def _get_eq(self):
+    def __eq__(other):
+        ret_val = self.__eq__._mock_return_value
+        if ret_val is not DEFAULT:
+            return ret_val
+        return self is other
+    return __eq__
+
+def _get_ne(self):
+    def __ne__(other):
+        if self.__ne__._mock_return_value is not DEFAULT:
+            return DEFAULT
+        return self is not other
+    return __ne__
+
+def _get_iter(self):
+    def __iter__():
+        ret_val = self.__iter__._mock_return_value
+        if ret_val is DEFAULT:
+            return iter([])
+        # if ret_val was already an iterator, then calling iter on it should
+        # return the iterator unchanged
+        return iter(ret_val)
+    return __iter__
+
+_side_effect_methods = {
+    '__eq__': _get_eq,
+    '__ne__': _get_ne,
+    '__iter__': _get_iter,
+}
+
+
+
+def _set_return_value(mock, method, name):
+    fixed = _return_values.get(name, DEFAULT)
+    if fixed is not DEFAULT:
+        method.return_value = fixed
+        return
+
+    return_calulator = _calculate_return_value.get(name)
+    if return_calulator is not None:
+        try:
+            return_value = return_calulator(mock)
+        except AttributeError:
+            # XXXX why do we return AttributeError here?
+            #      set it as a side_effect instead?
+            return_value = AttributeError(name)
+        method.return_value = return_value
+        return
+
+    side_effector = _side_effect_methods.get(name)
+    if side_effector is not None:
+        method.side_effect = side_effector(mock)
+
+
+
+class MagicMixin(object):
+    def __init__(self, *args, **kw):
+        _super(MagicMixin, self).__init__(*args, **kw)
+        self._mock_set_magics()
+
+
+    def _mock_set_magics(self):
+        these_magics = _magics
+
+        if self._mock_methods is not None:
+            these_magics = _magics.intersection(self._mock_methods)
+
+            remove_magics = set()
+            remove_magics = _magics - these_magics
+
+            for entry in remove_magics:
+                if entry in type(self).__dict__:
+                    # remove unneeded magic methods
+                    delattr(self, entry)
+
+        # don't overwrite existing attributes if called a second time
+        these_magics = these_magics - set(type(self).__dict__)
+
+        _type = type(self)
+        for entry in these_magics:
+            setattr(_type, entry, MagicProxy(entry, self))
+
+
+
+class NonCallableMagicMock(MagicMixin, NonCallableMock):
+    """A version of `MagicMock` that isn't callable."""
+    def mock_add_spec(self, spec, spec_set=False):
+        """Add a spec to a mock. `spec` can either be an object or a
+        list of strings. Only attributes on the `spec` can be fetched as
+        attributes from the mock.
+
+        If `spec_set` is True then only attributes on the spec can be set."""
+        self._mock_add_spec(spec, spec_set)
+        self._mock_set_magics()
+
+
+
+class MagicMock(MagicMixin, Mock):
+    """
+    MagicMock is a subclass of Mock with default implementations
+    of most of the magic methods. You can use MagicMock without having to
+    configure the magic methods yourself.
+
+    If you use the `spec` or `spec_set` arguments then *only* magic
+    methods that exist in the spec will be created.
+
+    Attributes and the return value of a `MagicMock` will also be `MagicMocks`.
+    """
+    def mock_add_spec(self, spec, spec_set=False):
+        """Add a spec to a mock. `spec` can either be an object or a
+        list of strings. Only attributes on the `spec` can be fetched as
+        attributes from the mock.
+
+        If `spec_set` is True then only attributes on the spec can be set."""
+        self._mock_add_spec(spec, spec_set)
+        self._mock_set_magics()
+
+
+
+class MagicProxy(object):
+    def __init__(self, name, parent):
+        self.name = name
+        self.parent = parent
+
+    def __call__(self, *args, **kwargs):
+        m = self.create_mock()
+        return m(*args, **kwargs)
+
+    def create_mock(self):
+        entry = self.name
+        parent = self.parent
+        m = parent._get_child_mock(name=entry, _new_name=entry,
+                                   _new_parent=parent)
+        setattr(parent, entry, m)
+        _set_return_value(parent, m, entry)
+        return m
+
+    def __get__(self, obj, _type=None):
+        return self.create_mock()
+
+
+
+class _ANY(object):
+    "A helper object that compares equal to everything."
+
+    def __eq__(self, other):
+        return True
+
+    def __ne__(self, other):
+        return False
+
+    def __repr__(self):
+        return '<ANY>'
+
+ANY = _ANY()
+
+
+
+def _format_call_signature(name, args, kwargs):
+    message = '%s(%%s)' % name
+    formatted_args = ''
+    args_string = ', '.join([repr(arg) for arg in args])
+    kwargs_string = ', '.join([
+        '%s=%r' % (key, value) for key, value in kwargs.items()
+    ])
+    if args_string:
+        formatted_args = args_string
+    if kwargs_string:
+        if formatted_args:
+            formatted_args += ', '
+        formatted_args += kwargs_string
+
+    return message % formatted_args
+
+
+
+class _Call(tuple):
+    """
+    A tuple for holding the results of a call to a mock, either in the form
+    `(args, kwargs)` or `(name, args, kwargs)`.
+
+    If args or kwargs are empty then a call tuple will compare equal to
+    a tuple without those values. This makes comparisons less verbose::
+
+        _Call(('name', (), {})) == ('name',)
+        _Call(('name', (1,), {})) == ('name', (1,))
+        _Call(((), {'a': 'b'})) == ({'a': 'b'},)
+
+    The `_Call` object provides a useful shortcut for comparing with call::
+
+        _Call(((1, 2), {'a': 3})) == call(1, 2, a=3)
+        _Call(('foo', (1, 2), {'a': 3})) == call.foo(1, 2, a=3)
+
+    If the _Call has no name then it will match any name.
+    """
+    def __new__(cls, value=(), name=None, parent=None, two=False,
+                from_kall=True):
+        name = ''
+        args = ()
+        kwargs = {}
+        _len = len(value)
+        if _len == 3:
+            name, args, kwargs = value
+        elif _len == 2:
+            first, second = value
+            if isinstance(first, basestring):
+                name = first
+                if isinstance(second, tuple):
+                    args = second
+                else:
+                    kwargs = second
+            else:
+                args, kwargs = first, second
+        elif _len == 1:
+            value, = value
+            if isinstance(value, basestring):
+                name = value
+            elif isinstance(value, tuple):
+                args = value
+            else:
+                kwargs = value
+
+        if two:
+            return tuple.__new__(cls, (args, kwargs))
+
+        return tuple.__new__(cls, (name, args, kwargs))
+
+
+    def __init__(self, value=(), name=None, parent=None, two=False,
+                 from_kall=True):
+        self.name = name
+        self.parent = parent
+        self.from_kall = from_kall
+
+
+    def __eq__(self, other):
+        if other is ANY:
+            return True
+        try:
+            len_other = len(other)
+        except TypeError:
+            return False
+
+        self_name = ''
+        if len(self) == 2:
+            self_args, self_kwargs = self
+        else:
+            self_name, self_args, self_kwargs = self
+
+        other_name = ''
+        if len_other == 0:
+            other_args, other_kwargs = (), {}
+        elif len_other == 3:
+            other_name, other_args, other_kwargs = other
+        elif len_other == 1:
+            value, = other
+            if isinstance(value, tuple):
+                other_args = value
+                other_kwargs = {}
+            elif isinstance(value, basestring):
+                other_name = value
+                other_args, other_kwargs = (), {}
+            else:
+                other_args = ()
+                other_kwargs = value
+        else:
+            # len 2
+            # could be (name, args) or (name, kwargs) or (args, kwargs)
+            first, second = other
+            if isinstance(first, basestring):
+                other_name = first
+                if isinstance(second, tuple):
+                    other_args, other_kwargs = second, {}
+                else:
+                    other_args, other_kwargs = (), second
+            else:
+                other_args, other_kwargs = first, second
+
+        if self_name and other_name != self_name:
+            return False
+
+        # this order is important for ANY to work!
+        return (other_args, other_kwargs) == (self_args, self_kwargs)
+
+
+    def __ne__(self, other):
+        return not self.__eq__(other)
+
+
+    def __call__(self, *args, **kwargs):
+        if self.name is None:
+            return _Call(('', args, kwargs), name='()')
+
+        name = self.name + '()'
+        return _Call((self.name, args, kwargs), name=name, parent=self)
+
+
+    def __getattr__(self, attr):
+        if self.name is None:
+            return _Call(name=attr, from_kall=False)
+        name = '%s.%s' % (self.name, attr)
+        return _Call(name=name, parent=self, from_kall=False)
+
+
+    def __repr__(self):
+        if not self.from_kall:
+            name = self.name or 'call'
+            if name.startswith('()'):
+                name = 'call%s' % name
+            return name
+
+        if len(self) == 2:
+            name = 'call'
+            args, kwargs = self
+        else:
+            name, args, kwargs = self
+            if not name:
+                name = 'call'
+            elif not name.startswith('()'):
+                name = 'call.%s' % name
+            else:
+                name = 'call%s' % name
+        return _format_call_signature(name, args, kwargs)
+
+
+    def call_list(self):
+        """For a call object that represents multiple calls, `call_list`
+        returns a list of all the intermediate calls as well as the
+        final call."""
+        vals = []
+        thing = self
+        while thing is not None:
+            if thing.from_kall:
+                vals.append(thing)
+            thing = thing.parent
+        return _CallList(reversed(vals))
+
+
+call = _Call(from_kall=False)
+
+
+
+def create_autospec(spec, spec_set=False, instance=False, _parent=None,
+                    _name=None, **kwargs):
+    """Create a mock object using another object as a spec. Attributes on the
+    mock will use the corresponding attribute on the `spec` object as their
+    spec.
+
+    Functions or methods being mocked will have their arguments checked
+    to check that they are called with the correct signature.
+
+    If `spec_set` is True then attempting to set attributes that don't exist
+    on the spec object will raise an `AttributeError`.
+
+    If a class is used as a spec then the return value of the mock (the
+    instance of the class) will have the same spec. You can use a class as the
+    spec for an instance object by passing `instance=True`. The returned mock
+    will only be callable if instances of the mock are callable.
+
+    `create_autospec` also takes arbitrary keyword arguments that are passed to
+    the constructor of the created mock."""
+    if _is_list(spec):
+        # can't pass a list instance to the mock constructor as it will be
+        # interpreted as a list of strings
+        spec = type(spec)
+
+    is_type = isinstance(spec, ClassTypes)
+
+    _kwargs = {'spec': spec}
+    if spec_set:
+        _kwargs = {'spec_set': spec}
+    elif spec is None:
+        # None we mock with a normal mock without a spec
+        _kwargs = {}
+
+    _kwargs.update(kwargs)
+
+    Klass = MagicMock
+    if type(spec) in DescriptorTypes:
+        # descriptors don't have a spec
+        # because we don't know what type they return
+        _kwargs = {}
+    elif not _callable(spec):
+        Klass = NonCallableMagicMock
+    elif is_type and instance and not _instance_callable(spec):
+        Klass = NonCallableMagicMock
+
+    _new_name = _name
+    if _parent is None:
+        # for a top level object no _new_name should be set
+        _new_name = ''
+
+    mock = Klass(parent=_parent, _new_parent=_parent, _new_name=_new_name,
+                 name=_name, **_kwargs)
+
+    if isinstance(spec, FunctionTypes):
+        # should only happen at the top level because we don't
+        # recurse for functions
+        mock = _set_signature(mock, spec)
+    else:
+        _check_signature(spec, mock, is_type, instance)
+
+    if _parent is not None and not instance:
+        _parent._mock_children[_name] = mock
+
+    if is_type and not instance and 'return_value' not in kwargs:
+        mock.return_value = create_autospec(spec, spec_set, instance=True,
+                                            _name='()', _parent=mock)
+
+    for entry in dir(spec):
+        if _is_magic(entry):
+            # MagicMock already does the useful magic methods for us
+            continue
+
+        if isinstance(spec, FunctionTypes) and entry in FunctionAttributes:
+            # allow a mock to actually be a function
+            continue
+
+        # XXXX do we need a better way of getting attributes without
+        # triggering code execution (?) Probably not - we need the actual
+        # object to mock it so we would rather trigger a property than mock
+        # the property descriptor. Likewise we want to mock out dynamically
+        # provided attributes.
+        # XXXX what about attributes that raise exceptions other than
+        # AttributeError on being fetched?
+        # we could be resilient against it, or catch and propagate the
+        # exception when the attribute is fetched from the mock
+        try:
+            original = getattr(spec, entry)
+        except AttributeError:
+            continue
+
+        kwargs = {'spec': original}
+        if spec_set:
+            kwargs = {'spec_set': original}
+
+        if not isinstance(original, FunctionTypes):
+            new = _SpecState(original, spec_set, mock, entry, instance)
+            mock._mock_children[entry] = new
+        else:
+            parent = mock
+            if isinstance(spec, FunctionTypes):
+                parent = mock.mock
+
+            new = MagicMock(parent=parent, name=entry, _new_name=entry,
+                            _new_parent=parent, **kwargs)
+            mock._mock_children[entry] = new
+            skipfirst = _must_skip(spec, entry, is_type)
+            _check_signature(original, new, skipfirst=skipfirst)
+
+        # so functions created with _set_signature become instance attributes,
+        # *plus* their underlying mock exists in _mock_children of the parent
+        # mock. Adding to _mock_children may be unnecessary where we are also
+        # setting as an instance attribute?
+        if isinstance(new, FunctionTypes):
+            setattr(mock, entry, new)
+
+    return mock
+
+
+def _must_skip(spec, entry, is_type):
+    if not isinstance(spec, ClassTypes):
+        if entry in getattr(spec, '__dict__', {}):
+            # instance attribute - shouldn't skip
+            return False
+        spec = spec.__class__
+    if not hasattr(spec, '__mro__'):
+        # old style class: can't have descriptors anyway
+        return is_type
+
+    for klass in spec.__mro__:
+        result = klass.__dict__.get(entry, DEFAULT)
+        if result is DEFAULT:
+            continue
+        if isinstance(result, (staticmethod, classmethod)):
+            return False
+        return is_type
+
+    # shouldn't get here unless function is a dynamically provided attribute
+    # XXXX untested behaviour
+    return is_type
+
+
+def _get_class(obj):
+    try:
+        return obj.__class__
+    except AttributeError:
+        # in Python 2, _sre.SRE_Pattern objects have no __class__
+        return type(obj)
+
+
+class _SpecState(object):
+
+    def __init__(self, spec, spec_set=False, parent=None,
+                 name=None, ids=None, instance=False):
+        self.spec = spec
+        self.ids = ids
+        self.spec_set = spec_set
+        self.parent = parent
+        self.instance = instance
+        self.name = name
+
+
+FunctionTypes = (
+    # python function
+    type(create_autospec),
+    # instance method
+    type(ANY.__eq__),
+    # unbound method
+    type(_ANY.__eq__),
+)
+
+FunctionAttributes = set([
+    'func_closure',
+    'func_code',
+    'func_defaults',
+    'func_dict',
+    'func_doc',
+    'func_globals',
+    'func_name',
+])
+
+
+file_spec = None
+
+
+def mock_open(mock=None, read_data=''):
+    """
+    A helper function to create a mock to replace the use of `open`. It works
+    for `open` called directly or used as a context manager.
+
+    The `mock` argument is the mock object to configure. If `None` (the
+    default) then a `MagicMock` will be created for you, with the API limited
+    to methods or attributes available on standard file handles.
+
+    `read_data` is a string for the `read` method of the file handle to return.
+    This is an empty string by default.
+    """
+    global file_spec
+    if file_spec is None:
+        # set on first use
+        if inPy3k:
+            import _io
+            file_spec = list(set(dir(_io.TextIOWrapper)).union(set(dir(_io.BytesIO))))
+        else:
+            file_spec = file
+
+    if mock is None:
+        mock = MagicMock(name='open', spec=open)
+
+    handle = MagicMock(spec=file_spec)
+    handle.write.return_value = None
+    handle.__enter__.return_value = handle
+    handle.read.return_value = read_data
+
+    mock.return_value = handle
+    return mock
+
+
+class PropertyMock(Mock):
+    """
+    A mock intended to be used as a property, or other descriptor, on a class.
+    `PropertyMock` provides `__get__` and `__set__` methods so you can specify
+    a return value when it is fetched.
+
+    Fetching a `PropertyMock` instance from an object calls the mock, with
+    no args. Setting it calls the mock with the value being set.
+    """
+    def _get_child_mock(self, **kwargs):
+        return MagicMock(**kwargs)
+
+    def __get__(self, obj, obj_type):
+        return self()
+    def __set__(self, obj, val):
+        self(val)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/MANIFEST.in b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/MANIFEST.in
new file mode 100644
index 0000000..7d2580d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/MANIFEST.in
@@ -0,0 +1,9 @@
+include *.txt MANIFEST.in *.py
+recursive-include scripts *.py
+graft doc
+graft doc/_static
+graft doc/_templates
+graft modulegraph_tests
+global-exclude .DS_Store
+global-exclude *.pyc
+global-exclude *.so
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/PKG-INFO b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/PKG-INFO
new file mode 100644
index 0000000..bfd4006
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/PKG-INFO
@@ -0,0 +1,337 @@
+Metadata-Version: 1.1
+Name: modulegraph
+Version: 0.12.1
+Summary: Python module dependency analysis tool
+Home-page: http://bitbucket.org/ronaldoussoren/modulegraph
+Author: Ronald Oussoren
+Author-email: ronaldoussoren@mac.com
+License: MIT
+Download-URL: http://pypi.python.org/pypi/modulegraph
+Description: modulegraph determines a dependency graph between Python modules primarily
+        by bytecode analysis for import statements.
+        
+        modulegraph uses similar methods to modulefinder from the standard library,
+        but uses a more flexible internal representation, has more extensive 
+        knowledge of special cases, and is extensible.
+        
+        
+        Release history
+        ===============
+        
+        0.12.1
+        ------
+        
+        * Issue #25: Complex python files could cause an "maximum recursion depth exceeded"
+          exception due to using stack-based recursion to walk the module AST.
+        
+        
+        0.12
+        ----
+        
+        * Added 'modulegraph.modulegraph.InvalidSourceModule'. This graph node is
+          used for Python source modules that cannot be compiled (for example because
+          they contain syntax errors).
+        
+          This is primarily useful for being able to create a graph for packages
+          that have python 2.x or python 3.x compatibility in separate modules that
+          contain code that isn't valid in the "other" python version.
+        
+        * Added 'modulegraph.modulegraph.InvalidCompiledModule'. This graph node
+          is used for Python bytecode modules that cannot be loaded.
+        
+        * Added 'modulegraph.modulegraph.NamespacePackage'.
+        
+          Patch by bitbucket user htgoebel.
+        
+        * No longer add a MissingModule node to the graph for 'collections.defaultdict'
+          when using 'from collections import defaultdict' ('collections.defaultdict'
+          is an attribute of 'collections', not a submodule).
+        
+        * Fixed typo in ModuleGraph.getReferences()
+        
+        * Added ModuleGraph.getReferers(tonode). This methods yields the
+          nodes that are referencing *tonode* (the reverse of getReferences)
+        
+        * The graph will no longer contain MissingModule nodes when using 'from ... import name' to
+          import a global variable in a python module.
+        
+          There will still be MissingModule nodes for global variables in C extentions, and
+          for 'from missing import name' when 'missing' is itself a MissingModule.
+        
+        * Issue #18: Don't assume that a PEP 302 loader object has a ``path`` attribute. That
+          attribute is not documented and is not always present.
+        
+        0.11.2
+        ------
+        
+        *
+        
+        0.11.1
+        ------
+        
+        * Issue #145: Don't exclude the platform specific 'path' modules (like ntpath)
+        
+        0.11
+        ----
+        
+        This is a feature release
+        
+        Features
+        ........
+        
+        * Hardcode knowlegde about the compatibility aliases in the email
+          module (for python 2.5 upto 3.0).
+        
+          This makes it possible to remove a heavy-handed recipe from py2app.
+        
+        * Added ``modegraph.zipio.getmode`` to fetch the Unix file mode
+          for a file.
+        
+        * Added some handy methods to ``modulegraph.modulegraph.ModuleGraph``.
+        
+        0.10.5
+        ------
+        
+        This is a bugfix release
+        
+        * Don't look at the file extension to determine the file type
+          in modulegraph.find_modules.parse_mf_results, but use the
+          class of the item.
+        
+        * Issue #13: Improved handing of bad relative imports
+          ("from .foo import bar"), these tended to raise confusing errors and
+          are now handled like any other failed import.
+        
+        0.10.4
+        ------
+        
+        This is a bugfix release
+        
+        * There were no 'classifiers' in the package metadata due to a bug
+          in setup.py.
+        
+        0.10.3
+        ------
+        
+        This is a bugfix release
+        
+        Bugfixes
+        ........
+        
+        * ``modulegraph.find.modules.parse_mf_results`` failed when the main script of
+          a py2app module didn't have a file name ending in '.py'.
+        
+        0.10.2
+        ------
+        
+        This is a bugfix release
+        
+        Bugfixes
+        ........
+        
+        * Issue #12: modulegraph would sometimes find the wrong package *__init__*
+          module due to using the wrong search method. One easy way to reproduce the
+          problem was to have a toplevel module named *__init__*.
+        
+          Reported by Kentzo.
+        
+        0.10.1
+        ------
+        
+        This is a bugfix release
+        
+        Bugfixes
+        ........
+        
+        * Issue #11: creating xrefs and dotty graphs from modulegraphs (the --xref
+          and --graph options of py2app) didn't work with python 3 due to use of
+          APIs that aren't available in that version of python.
+        
+          Reported by Andrew Barnert.
+        
+        
+        0.10
+        ----
+        
+        This is a minor feature release
+        
+        Features
+        ........
+        
+        * ``modulegraph.find_modules.find_needed_modules`` claimed to automaticly
+          include subpackages for the "packages" argument as well, but that code
+          didn't work at all.
+        
+        * Issue #9: The modulegraph script is deprecated, use
+          "python -mmodulegraph" instead.
+        
+        * Issue #10: Ensure that the result of "zipio.open" can be used
+          in a with statement (that is, ``with zipio.open(...) as fp``.
+        
+        * No longer use "2to3" to support Python 3.
+        
+          Because of this modulegraph now supports Python 2.6
+          and later.
+        
+        * Slightly improved HTML output, which makes it easier
+          to manipulate the generated HTML using JavaScript.
+        
+          Patch by anatoly techtonik.
+        
+        * Ensure modulegraph works with changes introduced after
+          Python 3.3b1.
+        
+        * Implement support for PEP 420 ("Implicit namespace packages")
+          in Python 3.3.
+        
+        * ``modulegraph.util.imp_walk`` is deprecated and will be
+          removed in the next release of this package.
+        
+        Bugfixes
+        ........
+        
+        * The module graph was incomplete, and generated incorrect warnings
+          along the way, when a subpackage contained import statements for
+          submodules.
+        
+          An example of this is ``sqlalchemy.util``, the ``__init__.py`` file
+          for this package contains imports of modules in that modules using
+          the classic relative import syntax (that is ``import compat`` to
+          import ``sqlalchemy.util.compat``). Until this release modulegraph
+          searched the wrong path to locate these modules (and hence failed
+          to find them).
+        
+        
+        0.9.2
+        -----
+        
+        This is a bugfix release
+        
+        Bugfixes
+        ........
+        
+        * The 'packages' option to modulegraph.find_modules.find_modules ignored
+          the search path argument but always used the default search path.
+        
+        * The 'imp_find_modules' function in modulegraph.util has an argument 'path',
+          this was a string in previous release and can now also be a sequence.
+        
+        * Don't crash when a module on the 'includes' list doesn't exist, but warn
+          just like for missing 'packages' (modulegraph.find_modules.find_modules)
+        
+        0.9.1
+        -----
+        
+        This is a bugfix release
+        
+        Bug fixes
+        .........
+        
+        - Fixed the name of nodes imports in packages where the first element of
+          a dotted name can be found but the rest cannot. This used to create
+          a MissingModule node for the dotted name in the global namespace instead
+          of relative to the package.
+        
+          That is, given a package "pkg" with submodule "sub" if the "__init__.py"
+          of "pkg" contains "import sub.nomod" we now create a MissingModule node
+          for "pkg.sub.nomod" instead of "sub.nomod".
+        
+          This fixes an issue with including the crcmod package in application
+          bundles, first reported on the pythonmac-sig mailinglist by
+          Brendan Simon.
+        
+        0.9
+        ---
+        
+        This is a minor feature release
+        
+        
+        Features:
+        
+        - Documentation is now generated using `sphinx <http://pypi.python.org/pypi/sphinx>`_
+          and can be viewed at <http://packages.python.org/modulegraph>.
+        
+          The documention is very rough at this moment and in need of reorganisation and
+          language cleanup. I've basiclly writting the current version by reading the code
+          and documenting what it does, the order in which classes and methods are document
+          is therefore not necessarily the most useful.
+        
+        - The repository has moved to bitbucket
+        
+        - Renamed ``modulegraph.modulegraph.AddPackagePath`` to ``addPackagePath``,
+          likewise ``ReplacePackage`` is now ``replacePackage``. The old name is still
+          available, but is deprecated and will be removed before the 1.0 release.
+        
+        - ``modulegraph.modulegraph`` contains two node types that are unused and
+          have unclear semantics: ``FlatPackage`` and ``ArchiveModule``. These node
+          types are deprecated and will be removed before 1.0 is released.
+        
+        - Added a simple commandline tool (``modulegraph``) that will print information
+          about the dependency graph of a script.
+        
+        - Added a module (``zipio``) for dealing with paths that may refer to entries
+          inside zipfiles (such as source paths referring to modules in zipped eggfiles).
+        
+          With this addition ``modulegraph.modulegraph.os_listdir`` is deprecated and
+          it will be removed before the 1.0 release.
+        
+        Bug fixes:
+        
+        - The ``__cmp__`` method of a Node no longer causes an exception
+          when the compared-to object is not a Node. Patch by Ivan Kozik.
+        
+        - Issue #1: The initialiser for ``modulegraph.ModuleGraph`` caused an exception
+          when an entry on the path (``sys.path``) doesn't actually exist.
+        
+          Fix by "skurylo", testcase by Ronald.
+        
+        - The code no longer worked with python 2.5, this release fixes that.
+        
+        - Due to the switch to mercurial setuptools will no longer include
+          all required files. Fixed by adding a MANIFEST.in file
+        
+        - The method for printing a ``.dot`` representation of a ``ModuleGraph``
+          works again.
+        
+        
+        0.8.1
+        -----
+        
+        This is a minor feature release
+        
+        Features:
+        
+        - ``from __future__ import absolute_import`` is now supported
+        
+        - Relative imports (``from . import module``) are now supported
+        
+        - Add support for namespace packages when those are installed
+          using option ``--single-version-externally-managed`` (part
+          of setuptools/distribute)
+        
+        0.8
+        ---
+        
+        This is a minor feature release
+        
+        Features:
+        
+        - Initial support for Python 3.x
+        
+        - It is now possible to run the test suite
+          using ``python setup.py test``.
+        
+          (The actual test suite is still fairly minimal though)
+        
+Keywords: import,,dependencies
+Platform: any
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.3
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Topic :: Software Development :: Build Tools
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/README.chromium b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/README.chromium
new file mode 100644
index 0000000..937e677
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/README.chromium
@@ -0,0 +1,11 @@
+Name: modulegraph
+Short Name: modulegraph
+URL: https://pypi.python.org/pypi/modulegraph/
+Version: 0.12.1
+License: MIT
+License File: NOT_SHIPPED
+Security Critical: no
+Description: modulegraph determines a dependency graph between Python modules
+primarily by bytecode analysis for import statements. It's used by telemetry's
+find_dependencies script.
+Local modification: remove doc/_build directory.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/README.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/README.txt
new file mode 100644
index 0000000..55ebf46
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/README.txt
@@ -0,0 +1,6 @@
+modulegraph determines a dependency graph between Python modules primarily
+by bytecode analysis for import statements.
+
+modulegraph uses similar methods to modulefinder from the standard library,
+but uses a more flexible internal representation, has more extensive 
+knowledge of special cases, and is extensible.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/doc/changelog.rst b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/doc/changelog.rst
new file mode 100644
index 0000000..f6725ac
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/doc/changelog.rst
@@ -0,0 +1,307 @@
+Release history
+===============
+
+0.12.1
+------
+
+* Issue #25: Complex python files could cause an "maximum recursion depth exceeded"
+  exception due to using stack-based recursion to walk the module AST.
+
+
+0.12
+----
+
+* Added 'modulegraph.modulegraph.InvalidSourceModule'. This graph node is
+  used for Python source modules that cannot be compiled (for example because
+  they contain syntax errors).
+
+  This is primarily useful for being able to create a graph for packages
+  that have python 2.x or python 3.x compatibility in separate modules that
+  contain code that isn't valid in the "other" python version.
+
+* Added 'modulegraph.modulegraph.InvalidCompiledModule'. This graph node
+  is used for Python bytecode modules that cannot be loaded.
+
+* Added 'modulegraph.modulegraph.NamespacePackage'.
+
+  Patch by bitbucket user htgoebel.
+
+* No longer add a MissingModule node to the graph for 'collections.defaultdict'
+  when using 'from collections import defaultdict' ('collections.defaultdict'
+  is an attribute of 'collections', not a submodule).
+
+* Fixed typo in ModuleGraph.getReferences()
+
+* Added ModuleGraph.getReferers(tonode). This methods yields the
+  nodes that are referencing *tonode* (the reverse of getReferences)
+
+* The graph will no longer contain MissingModule nodes when using 'from ... import name' to
+  import a global variable in a python module.
+
+  There will still be MissingModule nodes for global variables in C extentions, and
+  for 'from missing import name' when 'missing' is itself a MissingModule.
+
+* Issue #18: Don't assume that a PEP 302 loader object has a ``path`` attribute. That
+  attribute is not documented and is not always present.
+
+0.11.2
+------
+
+*
+
+0.11.1
+------
+
+* Issue #145: Don't exclude the platform specific 'path' modules (like ntpath)
+
+0.11
+----
+
+This is a feature release
+
+Features
+........
+
+* Hardcode knowlegde about the compatibility aliases in the email
+  module (for python 2.5 upto 3.0).
+
+  This makes it possible to remove a heavy-handed recipe from py2app.
+
+* Added ``modegraph.zipio.getmode`` to fetch the Unix file mode
+  for a file.
+
+* Added some handy methods to ``modulegraph.modulegraph.ModuleGraph``.
+
+0.10.5
+------
+
+This is a bugfix release
+
+* Don't look at the file extension to determine the file type
+  in modulegraph.find_modules.parse_mf_results, but use the
+  class of the item.
+
+* Issue #13: Improved handing of bad relative imports
+  ("from .foo import bar"), these tended to raise confusing errors and
+  are now handled like any other failed import.
+
+0.10.4
+------
+
+This is a bugfix release
+
+* There were no 'classifiers' in the package metadata due to a bug
+  in setup.py.
+
+0.10.3
+------
+
+This is a bugfix release
+
+Bugfixes
+........
+
+* ``modulegraph.find.modules.parse_mf_results`` failed when the main script of
+  a py2app module didn't have a file name ending in '.py'.
+
+0.10.2
+------
+
+This is a bugfix release
+
+Bugfixes
+........
+
+* Issue #12: modulegraph would sometimes find the wrong package *__init__*
+  module due to using the wrong search method. One easy way to reproduce the
+  problem was to have a toplevel module named *__init__*.
+
+  Reported by Kentzo.
+
+0.10.1
+------
+
+This is a bugfix release
+
+Bugfixes
+........
+
+* Issue #11: creating xrefs and dotty graphs from modulegraphs (the --xref
+  and --graph options of py2app) didn't work with python 3 due to use of
+  APIs that aren't available in that version of python.
+
+  Reported by Andrew Barnert.
+
+
+0.10
+----
+
+This is a minor feature release
+
+Features
+........
+
+* ``modulegraph.find_modules.find_needed_modules`` claimed to automaticly
+  include subpackages for the "packages" argument as well, but that code
+  didn't work at all.
+
+* Issue #9: The modulegraph script is deprecated, use
+  "python -mmodulegraph" instead.
+
+* Issue #10: Ensure that the result of "zipio.open" can be used
+  in a with statement (that is, ``with zipio.open(...) as fp``.
+
+* No longer use "2to3" to support Python 3.
+
+  Because of this modulegraph now supports Python 2.6
+  and later.
+
+* Slightly improved HTML output, which makes it easier
+  to manipulate the generated HTML using JavaScript.
+
+  Patch by anatoly techtonik.
+
+* Ensure modulegraph works with changes introduced after
+  Python 3.3b1.
+
+* Implement support for PEP 420 ("Implicit namespace packages")
+  in Python 3.3.
+
+* ``modulegraph.util.imp_walk`` is deprecated and will be
+  removed in the next release of this package.
+
+Bugfixes
+........
+
+* The module graph was incomplete, and generated incorrect warnings
+  along the way, when a subpackage contained import statements for
+  submodules.
+
+  An example of this is ``sqlalchemy.util``, the ``__init__.py`` file
+  for this package contains imports of modules in that modules using
+  the classic relative import syntax (that is ``import compat`` to
+  import ``sqlalchemy.util.compat``). Until this release modulegraph
+  searched the wrong path to locate these modules (and hence failed
+  to find them).
+
+
+0.9.2
+-----
+
+This is a bugfix release
+
+Bugfixes
+........
+
+* The 'packages' option to modulegraph.find_modules.find_modules ignored
+  the search path argument but always used the default search path.
+
+* The 'imp_find_modules' function in modulegraph.util has an argument 'path',
+  this was a string in previous release and can now also be a sequence.
+
+* Don't crash when a module on the 'includes' list doesn't exist, but warn
+  just like for missing 'packages' (modulegraph.find_modules.find_modules)
+
+0.9.1
+-----
+
+This is a bugfix release
+
+Bug fixes
+.........
+
+- Fixed the name of nodes imports in packages where the first element of
+  a dotted name can be found but the rest cannot. This used to create
+  a MissingModule node for the dotted name in the global namespace instead
+  of relative to the package.
+
+  That is, given a package "pkg" with submodule "sub" if the "__init__.py"
+  of "pkg" contains "import sub.nomod" we now create a MissingModule node
+  for "pkg.sub.nomod" instead of "sub.nomod".
+
+  This fixes an issue with including the crcmod package in application
+  bundles, first reported on the pythonmac-sig mailinglist by
+  Brendan Simon.
+
+0.9
+---
+
+This is a minor feature release
+
+
+Features:
+
+- Documentation is now generated using `sphinx <http://pypi.python.org/pypi/sphinx>`_
+  and can be viewed at <http://packages.python.org/modulegraph>.
+
+  The documention is very rough at this moment and in need of reorganisation and
+  language cleanup. I've basiclly writting the current version by reading the code
+  and documenting what it does, the order in which classes and methods are document
+  is therefore not necessarily the most useful.
+
+- The repository has moved to bitbucket
+
+- Renamed ``modulegraph.modulegraph.AddPackagePath`` to ``addPackagePath``,
+  likewise ``ReplacePackage`` is now ``replacePackage``. The old name is still
+  available, but is deprecated and will be removed before the 1.0 release.
+
+- ``modulegraph.modulegraph`` contains two node types that are unused and
+  have unclear semantics: ``FlatPackage`` and ``ArchiveModule``. These node
+  types are deprecated and will be removed before 1.0 is released.
+
+- Added a simple commandline tool (``modulegraph``) that will print information
+  about the dependency graph of a script.
+
+- Added a module (``zipio``) for dealing with paths that may refer to entries
+  inside zipfiles (such as source paths referring to modules in zipped eggfiles).
+
+  With this addition ``modulegraph.modulegraph.os_listdir`` is deprecated and
+  it will be removed before the 1.0 release.
+
+Bug fixes:
+
+- The ``__cmp__`` method of a Node no longer causes an exception
+  when the compared-to object is not a Node. Patch by Ivan Kozik.
+
+- Issue #1: The initialiser for ``modulegraph.ModuleGraph`` caused an exception
+  when an entry on the path (``sys.path``) doesn't actually exist.
+
+  Fix by "skurylo", testcase by Ronald.
+
+- The code no longer worked with python 2.5, this release fixes that.
+
+- Due to the switch to mercurial setuptools will no longer include
+  all required files. Fixed by adding a MANIFEST.in file
+
+- The method for printing a ``.dot`` representation of a ``ModuleGraph``
+  works again.
+
+
+0.8.1
+-----
+
+This is a minor feature release
+
+Features:
+
+- ``from __future__ import absolute_import`` is now supported
+
+- Relative imports (``from . import module``) are now supported
+
+- Add support for namespace packages when those are installed
+  using option ``--single-version-externally-managed`` (part
+  of setuptools/distribute)
+
+0.8
+---
+
+This is a minor feature release
+
+Features:
+
+- Initial support for Python 3.x
+
+- It is now possible to run the test suite
+  using ``python setup.py test``.
+
+  (The actual test suite is still fairly minimal though)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/doc/commandline.rst b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/doc/commandline.rst
new file mode 100644
index 0000000..b5a8df5
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/doc/commandline.rst
@@ -0,0 +1,31 @@
+Commandline tools
+=================
+
+The package can be used as a script using "python -mmodulegraph".
+
+This script calculates the module graph for the scripts passed
+on the commandline and by default prints a list of modules
+in the objectgraph, and their type and location.
+
+The script has a number of options to change the output:
+
+* ``-d``: Increase the debug level
+
+* ``-q``: Clear the debug level (emit minimal output)
+
+* ``-m``: The arguments are module names instead of script files
+
+* ``-x name``: Add ``name`` to the list of excludes
+
+* ``-p path``: Add ``path`` to the module search path
+
+* ``-g``: Emit a ``.dot`` file instead of a list of modules
+
+* ``-h``: Emit a ``.html`` file instead of a list of modules
+
+Deprecation warning
+-------------------
+
+The package also installs a command-line tool named "modulegraph",
+this command-line tool is deprecated and will be removed in a
+future version.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/doc/conf.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/doc/conf.py
new file mode 100644
index 0000000..76d7b80
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/doc/conf.py
@@ -0,0 +1,219 @@
+# -*- coding: utf-8 -*-
+#
+# modulegraph documentation build configuration file, created by
+# sphinx-quickstart on Tue Sep 28 21:04:40 2010.
+#
+# This file is execfile()d with the current directory set to its containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys, os
+
+def get_version():
+    fn = os.path.join(
+        os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
+        'setup.cfg')
+    for ln in open(fn):
+        if ln.startswith('version'):
+            version = ln.split('=')[-1].strip()
+            return version
+
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#sys.path.append(os.path.abspath('.'))
+sys.path.insert(0,
+    os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+
+
+# -- General configuration -----------------------------------------------------
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx', 'sphinx.ext.todo', 'sphinx.ext.ifconfig']
+
+
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'modulegraph'
+copyright = u'2010, Ronald Oussoren'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = get_version()
+# The full version, including alpha/beta/rc tags.
+release = version
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of documents that shouldn't be included in the build.
+#unused_docs = []
+
+# List of directories, relative to source directory, that shouldn't be searched
+# for source files.
+exclude_trees = ['_build']
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+
+# -- Options for HTML output ---------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  Major themes that come with
+# Sphinx are currently 'default' and 'sphinxdoc'.
+#html_theme = 'default'
+html_theme = 'nature'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further.  For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents.  If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar.  Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_use_modindex = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it.  The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = ''
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'modulegraphdoc'
+
+
+# -- Options for LaTeX output --------------------------------------------------
+
+# The paper size ('letter' or 'a4').
+#latex_paper_size = 'letter'
+
+# The font size ('10pt', '11pt' or '12pt').
+#latex_font_size = '10pt'
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, documentclass [howto/manual]).
+latex_documents = [
+  ('index', 'modulegraph.tex', u'modulegraph Documentation',
+   u'Ronald Oussoren', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# Additional stuff for the LaTeX preamble.
+#latex_preamble = ''
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_use_modindex = True
+
+
+# Example configuration for intersphinx: refer to the Python standard library.
+intersphinx_mapping = {
+        'python': ('http://docs.python.org/', None),
+        'altgraph': ('http://packages.python.org/altgraph', None),
+}
+
+todo_include_todos = True
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/doc/find_modules.rst b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/doc/find_modules.rst
new file mode 100644
index 0000000..48f0f97
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/doc/find_modules.rst
@@ -0,0 +1,58 @@
+:mod:`modulegraph.find_modules` --- High-level module dependency finding interface
+==================================================================================
+
+.. module:: modulegraph.find_modules
+   :synopsis: High-level module dependency finding interface
+
+This module provides a high-level interface to the functionality of 
+the modulegraph package.
+
+
+.. function:: find_modules([scripts[, includes[, packages[, excludes[, path[, debug]]]]]])
+
+   High-level interface, takes iterables for: scripts, includes, packages, excludes
+
+   And returns a :class:`modulegraph.modulegraph.ModuleGraph` instance, 
+   python_files, and extensions
+
+   python_files is a list of pure python dependencies as modulegraph.Module objects,
+
+   extensions is a list of platform-specific C extension dependencies as modulegraph.Module objects
+
+
+.. function:: parse_mf_results(mf)
+
+   Return two lists: the first one contains the python files in the graph,
+   the second the C extensions.
+        
+   :param mf: a :class:`modulegraph.modulegraph.ModuleGraph` instance
+
+
+Lower-level functionality
+-------------------------
+
+The functionality in this section is much lower level and should probably
+not be used. It's mostly documented as a convenience for maintainers.
+
+
+.. function:: get_implies()
+
+   Return a mapping of implied dependencies. The key is a, possibly dotted,
+   module name and the value a list of dependencies.
+
+   This contains hardcoded list of hard dependencies, for example for C
+   extensions in the standard libary that perform imports in C code, which
+   the generic dependency finder cannot locate.
+
+.. function:: plat_prepare(includes, packages, excludes)
+
+   Updates the lists of includes, packages and excludes for the current
+   platform. This will add items to these lists based on hardcoded platform
+   information.
+
+.. function:: find_needed_modules([mf[, scripts[, includes[, packages[, warn]]]]])
+
+   Feeds the given :class:`ModuleGraph <modulegraph.ModuleGraph>`  with
+   the *scripts*, *includes* and *packages* and returns the resulting
+   graph. This function will create a new graph when *mf* is not specified
+   or ``None``.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/doc/index.rst b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/doc/index.rst
new file mode 100644
index 0000000..534b4d3
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/doc/index.rst
@@ -0,0 +1,42 @@
+.. modulegraph documentation master file, created by
+   sphinx-quickstart on Tue Sep 28 21:04:40 2010.
+   You can adapt this file completely to your liking, but it should at least
+   contain the root `toctree` directive.
+
+Modulegraph - Python module dependency graph
+============================================
+
+modulegraph determines a dependency graph between Python modules primarily
+by bytecode analysis for import statements.
+
+modulegraph uses similar methods to :mod:`modulefinder` from the standard library,
+but uses a more flexible internal representation, has more extensive 
+knowledge of special cases, and is extensible.
+
+Contents:
+
+.. toctree::
+   :maxdepth: 1
+
+   changelog
+   license
+   commandline
+   modulegraph
+   find_modules
+   util
+   zipio
+
+Online Resources
+----------------
+
+* `Sourcecode repository on bitbucket <http://bitbucket.org/ronaldoussoren/modulegraph/>`_
+
+* `The issue tracker <http://bitbucket.org/ronaldoussoren/modulegraph/issues>`_
+
+Indices and tables
+------------------
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
+
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/doc/license.rst b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/doc/license.rst
new file mode 100644
index 0000000..f9c8cc5
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/doc/license.rst
@@ -0,0 +1,23 @@
+License
+=======
+
+Copyright (c) Bob Ippolito
+
+Parts are copyright (c) 2010-2014 Ronald Oussoren
+
+MIT License
+...........
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+and associated documentation files (the "Software"), to deal in the Software without restriction,
+including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do
+so.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/doc/modulegraph.rst b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/doc/modulegraph.rst
new file mode 100644
index 0000000..60566e7
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/doc/modulegraph.rst
@@ -0,0 +1,531 @@
+:mod:`modulegraph.modulegraph` --- Find modules used by a script
+================================================================
+
+.. module:: modulegraph.modulegraph
+   :synopsis: Find modules used by a script
+
+This module defines :class:`ModuleGraph`, which is used to find
+the dependencies of scripts using bytecode analysis.
+
+A number of APIs in this module refer to filesystem path. Those paths can refer to
+files inside zipfiles (for example when there are zipped egg files on :data:`sys.path`).
+Filenames referring to entries in a zipfile are not marked any way, if ``"somepath.zip"``
+refers to a zipfile, that is ``"somepath.zip/embedded/file"`` will be used to refer to
+``embedded/file`` inside the zipfile.
+
+The actual graph
+----------------
+
+.. class:: ModuleGraph([path[, excludes[, replace_paths[, implies[, graph[, debug]]]]]])
+
+   Create a new ModuleGraph object. Use the :meth:`run_script` method to add scripts,
+   and their dependencies to the graph.
+
+   :param path: Python search path to use, defaults to :data:`sys.path`
+   :param excludes: Iterable with module names that should not be included as a dependency
+   :param replace_paths: List of pathname rewrites ``(old, new)``. When this argument is
+     supplied the ``co_filename`` attributes of code objects get rewritten before scanning
+     them for dependencies.
+   :param implies: Implied module dependencies, a mapping from a module name to the list
+     of modules it depends on. Use this to tell modulegraph about dependencies that cannot
+     be found by code inspection (such as imports from C code or using the :func:`__import__`
+     function).
+   :param graph: A precreated :class:`Graph <altgraph.Graph.Graph>` object to use, the
+     default is to create a new one.
+   :param debug: The :class:`ObjectGraph <altgraph.ObjectGraph.ObjectGraph>` debug level.
+
+
+.. method:: run_script(pathname[, caller])
+
+   Create, and return,  a node by path (not module name). The *pathname* should
+   refer to a Python source file and will be scanned for dependencies.
+
+   The optional argument *caller* is the the node that calls this script,
+   and is used to add a reference in the graph.
+
+.. method:: import_hook(name[[, caller[, fromlist[, level, [, attr]]]])
+
+   Import a module and analyse its dependencies
+
+   :arg name:     The module name
+   :arg caller:   The node that caused the import to happen
+   :arg fromlist: The list of names to import, this is an empty list for
+      ``import name`` and a list of names for ``from name import a, b, c``.
+   :arg level:    The import level. The value should be ``-1`` for classical Python 2
+     imports, ``0`` for absolute imports and a positive number for relative imports (
+     where the value is the number of leading dots in the imported name).
+   :arg attr:     Attributes for the graph edge.
+
+
+.. method:: implyNodeReference(node, other, edgeData=None)
+
+   Explictly mark that *node* depends on *other*. Other is either
+   a :class:`node <Node>` or the name of a module that will be
+   searched for as if it were an absolute import.
+
+
+.. method:: createReference(fromnode, tonode[, edge_data])
+
+   Create a reference from *fromnode* to *tonode*, with optional edge data.
+
+   The default for *edge_data* is ``"direct"``.
+
+.. method:: getReferences(fromnode)
+
+   Yield all nodes that *fromnode* refers to. That is, all modules imported
+   by *fromnode*.
+
+   Node :data:`None` is the root of the graph, and refers to all notes that were
+   explicitly imported by :meth:`run_script` or :meth:`import_hook`, unless you use
+   an explicit parent with those methods.
+
+   .. versionadded:: 0.11
+
+.. method:: getReferers(tonode, collapse_missing_modules=True)
+
+   Yield all nodes that refer to *tonode*. That is, all modules that import
+   *tonode*.
+
+   If *collapse_missing_modules* is false this includes refererences from
+   :class:`MissingModule` nodes, otherwise :class:`MissingModule` nodes
+   are replaced by the "real" nodes that reference this missing node.
+
+   .. versionadded:: 0.12
+
+.. method:: foldReferences(pkgnode)
+
+   Hide all submodule nodes for package *pkgnode* and add ingoing and outgoing
+   edges to *pkgnode* based on the edges from the submodule nodes.
+
+   This can be used to simplify a module graph: after folding 'email' all
+   references to modules in the 'email' package are references to the package.
+
+   .. versionadded: 0.11
+
+.. method:: findNode(name)
+
+   Find a node by identifier.  If a node by that identifier exists, it will be returned.
+
+   If a lazy node exists by that identifier with no dependencies (excluded), it will be
+   instantiated and returned.
+
+   If a lazy node exists by that identifier with dependencies, it and its
+   dependencies will be instantiated and scanned for additional depende
+
+
+
+.. method:: create_xref([out])
+
+   Write an HTML file to the *out* stream (defaulting to :data:`sys.stdout`).
+
+   The HTML file contains a textual description of the dependency graph.
+
+
+
+.. method:: graphreport([fileobj[, flatpackages]])
+
+   .. todo:: To be documented
+
+
+
+.. method:: report()
+
+   Print a report to stdout, listing the found modules with their
+   paths, as well as modules that are missing, or seem to be missing.
+
+
+Mostly internal methods
+.......................
+
+The methods in this section should be considered as methods for subclassing at best,
+please let us know if you need these methods in your code as they are on track to be
+made private methods before the 1.0 release.
+
+.. warning:: The methods in this section will be refactored in a future release,
+   the current architecture makes it unnecessarily hard to write proper tests.
+
+.. method:: determine_parent(caller)
+
+   Returns the node of the package root voor *caller*. If *caller* is a package
+   this is the node itself, if the node is a module in a package this is the
+   node of for the package and otherwise the *caller* is not a package and
+   the result is :data:`None`.
+
+.. method:: find_head_package(parent, name[, level])
+
+   .. todo:: To be documented
+
+
+.. method:: load_tail(mod, tail)
+
+   This method is called to load the rest of a dotted name after loading the root
+   of a package. This will import all intermediate modules as well (using
+   :meth:`import_module`), and returns the module :class:`node <Node>` for the
+   requested node.
+
+   .. note:: When *tail* is empty this will just return *mod*.
+
+   :arg mod:   A start module (instance of :class:`Node`)
+   :arg tail:  The rest of a dotted name, can be empty
+   :raise ImportError: When the requested (or one of its parents) module cannot be found
+   :returns: the requested module
+
+
+
+.. method:: ensure_fromlist(m, fromlist)
+
+   Yield all submodules that would be imported when importing *fromlist*
+   from *m* (using ``from m import fromlist...``).
+
+   *m* must be a package and not a regular module.
+
+.. method:: find_all_submodules(m)
+
+   Yield the filenames for submodules of in the same package as *m*.
+
+
+
+.. method:: import_module(partname, fqname, parent)
+
+   Perform import of the module with basename *partname* (``path``) and
+   full name *fqname* (``os.path``). Import is performed by *parent*.
+
+   This will create a reference from the parent node to the
+   module node and will load the module node when it is not already
+   loaded.
+
+
+
+.. method:: load_module(fqname, fp, pathname, (suffix, mode, type))
+
+   Load the module named *fqname* from the given *pathame*. The
+   argument *fp* is either :data:`None`, or a stream where the
+   code for the Python module can be loaded (either byte-code or
+   the source code). The *(suffix, mode, type)* tuple are the
+   suffix of the source file, the open mode for the file and the
+   type of module.
+
+   Creates a node of the right class and processes the dependencies
+   of the :class:`node <Node>` by scanning the byte-code for the node.
+
+   Returns the resulting :class:`node <Node>`.
+
+
+
+.. method:: scan_code(code, m)
+
+   Scan the *code* object for module *m* and update the dependencies of
+   *m* using the import statemets found in the code.
+
+   This will automaticly scan the code for nested functions, generator
+   expressions and list comprehensions as well.
+
+
+
+.. method:: load_package(fqname, pathname)
+
+   Load a package directory.
+
+
+
+.. method:: find_module(name, path[, parent])
+
+   Locates a module named *name* that is not yet part of the
+   graph. This method will raise :exc:`ImportError` when
+   the module cannot be found or when it is already part
+   of the graph. The *name* can not be a dotted name.
+
+   The *path* is the search path used, or :data:`None` to
+   use the default path.
+
+   When the *parent* is specified *name* refers to a
+   subpackage of *parent*, and *path* should be the
+   search path of the parent.
+
+   Returns the result of the global function
+   :func:`find_module <modulegraph.modulegraph.find_module>`.
+
+
+.. method:: itergraphreport([name[, flatpackages]])
+
+   .. todo:: To be documented
+
+
+
+.. method:: replace_paths_in_code(co)
+
+   Replace the filenames in code object *co* using the *replace_paths* value that
+   was passed to the contructor. Returns the rewritten code object.
+
+
+
+.. method:: calc_setuptools_nspackages()
+
+   Returns a mapping from package name to a list of paths where that package
+   can be found in ``--single-version-externally-managed`` form.
+
+   This method is used to be able to find those packages: these use
+   a magic ``.pth`` file to ensure that the package is added to :data:`sys.path`,
+   as they do not contain an ``___init__.py`` file.
+
+   Packages in this form are used by system packages and the "pip"
+   installer.
+
+
+Graph nodes
+-----------
+
+The :class:`ModuleGraph` contains nodes that represent the various types of modules.
+
+.. class:: Alias(value)
+
+   This is a subclass of string that is used to mark module aliases.
+
+
+
+.. class:: Node(identifier)
+
+   Base class for nodes, which provides the common functionality.
+
+   Nodes can by used as mappings for storing arbitrary data in the node.
+
+   Nodes are compared by comparing their *identifier*.
+
+.. data:: debug
+
+   Debug level (integer)
+
+.. data:: graphident
+
+   The node identifier, this is the value of the *identifier* argument
+   to the constructor.
+
+.. data:: identifier
+
+   The node identifier, this is the value of the *identifier* argument
+   to the constructor.
+
+.. data:: filename
+
+   The filename associated with this node.
+
+.. data:: packagepath
+
+   The value of ``__path__`` for this node.
+
+.. data:: code
+
+   The :class:`code object <types.CodeObject>` associated with this node
+
+.. data:: globalnames
+
+   The set of global names that are assigned to in this module. This
+   includes those names imported through startimports of Python modules.
+
+.. data:: startimports
+
+   The set of startimports this module did that could not be resolved,
+   ie. a startimport from a non-Python module.
+
+
+.. method:: __contains__(name)
+
+   Return if there is a value associated with *name*.
+
+   This method is usually accessed as ``name in aNode``.
+
+.. method:: __setitem__(name, value)
+
+   Set the value of *name* to *value*.
+
+   This method is usually accessed as ``aNode[name] = value``.
+
+.. method:: __getitem__(name)
+
+   Returns the value of *name*, raises :exc:`KeyError` when
+   it cannot be found.
+
+   This method is usually accessed as ``value = aNode[name]``.
+
+.. method:: get(name[, default])
+
+   Returns the value of *name*, or the default value when it
+   cannot be found. The *default* is :data:`None` when not specified.
+
+.. method:: infoTuple()
+
+   Returns a tuple with information used in the :func:`repr`
+   output for the node. Subclasses can add additional informations
+   to the result.
+
+
+.. class:: AliasNode (name, node)
+
+   A node that represents an alias from a name to another node.
+
+   The value of attribute *graphident* for this node will be the
+   value of *name*, the other :class:`Node` attributed are
+   references to those attributed in *node*.
+
+.. class:: BadModule(identifier)
+
+   Base class for nodes that should be ignored for some reason
+
+.. class:: ExcludedModule(identifier)
+
+   A module that is explicitly excluded.
+
+.. class:: MissingModule(identifier)
+
+   A module that is imported but cannot be located.
+
+
+
+.. class:: Script(filename)
+
+   A python script.
+
+   .. data:: filename
+
+      The filename for the script
+
+.. class:: BaseModule(name[, filename[, path]])
+
+    The base class for actual modules. The *name* is
+    the possibly dotted module name, *filename* is the
+    filesystem path to the module and *path* is the
+    value of ``__path__`` for the module.
+
+.. data:: graphident
+
+   The name of the module
+
+.. data:: filename
+
+   The filesystem path to the module.
+
+.. data:: path
+
+   The value of ``__path__`` for this module.
+
+.. class:: BuiltinModule(name)
+
+   A built-in module (on in :data:`sys.builtin_module_names`).
+
+.. class:: SourceModule(name)
+
+   A module for which the python source code is available.
+
+.. class:: InvalidSourceModule(name)
+
+   A module for which the python source code is available, but where
+   that source code cannot be compiled (due to syntax errors).
+
+   This is a subclass of :class:`SourceModule`.
+
+   .. versionadded:: 0.12
+
+.. class:: CompiledModule(name)
+
+   A module for which only byte-code is available.
+
+.. class:: Package(name)
+
+   Represents a python package
+
+.. class:: NamespacePackage(name)
+
+   Represents a python namespace package.
+
+   This is a subclass of :class:`Package`.
+
+.. class:: Extension(name)
+
+   A native extension
+
+
+.. warning:: A number of other node types are defined in the module. Those modules aren't
+   used by modulegraph and will be removed in a future version.
+
+
+Edge data
+---------
+
+The edges in a module graph by default contain information about the edge, represented
+by an instance of :class:`DependencyInfo`.
+
+.. class:: DependencyInfo(conditional, function, tryexcept, fromlist)
+
+   This class is a :func:`namedtuple <collections.namedtuple>` for representing
+   the information on a dependency between two modules.
+
+   All attributes can be used to deduce if a dependency is essential or not, and
+   are particularly useful when reporting on missing modules (dependencies on
+   :class:`MissingModule`).
+
+   .. data:: fromlist
+
+      A boolean that is true iff the target of the edge is named in the "import"
+      list of a "from" import ("from package import module").
+
+      When the target module is imported multiple times this attribute is false
+      unless all imports are in "import" list of a "from" import.
+
+   .. data:: function
+
+      A boolean that is true iff the import is done inside a function definition,
+      and is false for imports in module scope (or class scope for classes that
+      aren't definined in a function).
+
+   .. data:: tryexcept
+
+      A boolean that is true iff the import that is done in the "try" or "except"
+      block of a try statement (but not in the "else" block).
+
+   .. data:: conditional
+
+      A boolean that is true iff the import is done in either block of an "if"
+      statement.
+
+   When the target of the edge is imported multiple times the :data:`function`,
+   :data:`tryexcept` and :data:`conditional` attributes of all imports are
+   merged: when there is an import where all these attributes are false the
+   attributes are false, otherwise each attribute is set to true if it is
+   true for at least one of the imports.
+
+   For example, when a module is imported both in a try-except statement and
+   furthermore is imported in a function (in two separate statements),
+   both :data:`tryexcept` and :data:`function` will be true.  But if there
+   is a third unconditional toplevel import for that module as well all
+   three attributes are false.
+
+   .. warning::
+
+      All attributes but :data:`fromlist` will be false when the source of
+      a dependency is scanned from a byte-compiled module instead of a python
+      source file. The :data:`fromlist` attribute will stil be set correctly.
+
+Utility functions
+-----------------
+
+.. function:: find_module(name[, path])
+
+   A version of :func:`imp.find_module` that works with zipped packages (and other
+   :pep:`302` importers).
+
+.. function:: moduleInfoForPath(path)
+
+   Return the module name, readmode and type for the file at *path*, or
+   None if it doesn't seem to be a valid module (based on its name).
+
+.. function:: addPackagePath(packagename, path)
+
+   Add *path* to the value of ``__path__`` for the package named *packagename*.
+
+.. function:: replacePackage(oldname, newname)
+
+   Rename *oldname* to *newname* when it is found by the module finder. This
+   is used as a workaround for the hack that the ``_xmlplus`` package uses
+   to inject itself in the ``xml`` namespace.
+
+
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/doc/util.rst b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/doc/util.rst
new file mode 100644
index 0000000..86427aa
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/doc/util.rst
@@ -0,0 +1,31 @@
+:mod:`modulegraph.util` --- Utilies functions
+=============================================
+
+.. module:: modulegraph.util
+   :synopsis: Utilitie functions
+
+
+.. function:: imp_find_module(name, path=None)
+
+   This function has the same interface as
+   :func:`imp.find_module`, but also works with
+   dotted names.
+
+.. function:: imp_walk(name)
+
+   yields the namepart and importer information
+   for every part of a dotted module name, and
+   raises :exc:`ImportError` when the *name*
+   cannot be found.
+
+   The result elements are tuples with two
+   elements, the first is a module name,
+   the second is the result for :func:`imp.find_module`
+   for that module (taking into account :pep:`302`
+   importers)
+
+   .. deprecated:: 0.10
+
+.. function:: guess_encoding(fp)
+
+   Returns the encoding of a python source file.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/doc/zipio.rst b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/doc/zipio.rst
new file mode 100644
index 0000000..dd227b8
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/doc/zipio.rst
@@ -0,0 +1,68 @@
+:mod:`modulegraph.zipio` --- Read-only filesystem access
+========================================================
+
+.. module:: modulegraph.zipio
+   :synopsis: Read-only filesystem access with ZIP support
+
+This module contains a number of functions that mirror functions found
+in :mod:`os` and :mod:`os.path`, but have support for data inside
+zipfiles as well as regular filesystem objects.
+
+The *path* argument of all functions below can refer to an object
+on the filesystem, but can also refer to an entry inside a zipfile. In
+the latter case, a prefix of *path* will be the name of zipfile while
+the rest refers to an object in that zipfile. As an example, when
+``somepath/mydata.zip`` is a zipfile the path ``somepath/mydata.zip/somefile.txt``
+will refer to ``somefile.txt`` inside the zipfile.
+
+.. function:: open(path[, mode])
+
+   Open a file, like :func:`the built-in open function <__builtin__.open>`.
+
+   The *mode* defaults to ``"r"`` and must be either ``"r"`` or ``"rb"``.
+
+.. function:: listdir(path)
+
+   List the contents of a directory, like :func:`os.listdir`.
+
+
+.. function:: isfile(path)
+
+   Returns true if *path* exists and refers to a file.
+
+   Raises IOError when *path* doesn't exist at all.
+
+   Based on :func:`os.path.isfile`
+
+
+.. function:: isdir(path)
+
+   Returns true if *path* exists and refers to a directory.
+
+   Raises IOError when *path* doesn't exist at all.
+
+   Based on :func:`os.path.isdir`
+
+
+.. function:: islink(path)
+
+   Returns true if *path* exists and refers to a symbolic link.
+
+   Raises IOError when *path* doesn't exist at all.
+
+   Based on :func:`os.path.islink`
+
+
+.. function:: readlink(path)
+
+   Returns the contents of a symbolic link, like :func:`os.readlink`.
+
+.. function:: getmtime(path)
+
+   Returns the last modifiction time of a file or directory, like
+   :func:`os.path.getmtime`.
+
+.. function:: getmode(path)
+
+   Returns the UNIX file mode for a file or directory, like the
+   *st_mode* attribute in the result of :func:`os.stat`.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph.egg-info/PKG-INFO b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph.egg-info/PKG-INFO
new file mode 100644
index 0000000..bfd4006
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph.egg-info/PKG-INFO
@@ -0,0 +1,337 @@
+Metadata-Version: 1.1
+Name: modulegraph
+Version: 0.12.1
+Summary: Python module dependency analysis tool
+Home-page: http://bitbucket.org/ronaldoussoren/modulegraph
+Author: Ronald Oussoren
+Author-email: ronaldoussoren@mac.com
+License: MIT
+Download-URL: http://pypi.python.org/pypi/modulegraph
+Description: modulegraph determines a dependency graph between Python modules primarily
+        by bytecode analysis for import statements.
+        
+        modulegraph uses similar methods to modulefinder from the standard library,
+        but uses a more flexible internal representation, has more extensive 
+        knowledge of special cases, and is extensible.
+        
+        
+        Release history
+        ===============
+        
+        0.12.1
+        ------
+        
+        * Issue #25: Complex python files could cause an "maximum recursion depth exceeded"
+          exception due to using stack-based recursion to walk the module AST.
+        
+        
+        0.12
+        ----
+        
+        * Added 'modulegraph.modulegraph.InvalidSourceModule'. This graph node is
+          used for Python source modules that cannot be compiled (for example because
+          they contain syntax errors).
+        
+          This is primarily useful for being able to create a graph for packages
+          that have python 2.x or python 3.x compatibility in separate modules that
+          contain code that isn't valid in the "other" python version.
+        
+        * Added 'modulegraph.modulegraph.InvalidCompiledModule'. This graph node
+          is used for Python bytecode modules that cannot be loaded.
+        
+        * Added 'modulegraph.modulegraph.NamespacePackage'.
+        
+          Patch by bitbucket user htgoebel.
+        
+        * No longer add a MissingModule node to the graph for 'collections.defaultdict'
+          when using 'from collections import defaultdict' ('collections.defaultdict'
+          is an attribute of 'collections', not a submodule).
+        
+        * Fixed typo in ModuleGraph.getReferences()
+        
+        * Added ModuleGraph.getReferers(tonode). This methods yields the
+          nodes that are referencing *tonode* (the reverse of getReferences)
+        
+        * The graph will no longer contain MissingModule nodes when using 'from ... import name' to
+          import a global variable in a python module.
+        
+          There will still be MissingModule nodes for global variables in C extentions, and
+          for 'from missing import name' when 'missing' is itself a MissingModule.
+        
+        * Issue #18: Don't assume that a PEP 302 loader object has a ``path`` attribute. That
+          attribute is not documented and is not always present.
+        
+        0.11.2
+        ------
+        
+        *
+        
+        0.11.1
+        ------
+        
+        * Issue #145: Don't exclude the platform specific 'path' modules (like ntpath)
+        
+        0.11
+        ----
+        
+        This is a feature release
+        
+        Features
+        ........
+        
+        * Hardcode knowlegde about the compatibility aliases in the email
+          module (for python 2.5 upto 3.0).
+        
+          This makes it possible to remove a heavy-handed recipe from py2app.
+        
+        * Added ``modegraph.zipio.getmode`` to fetch the Unix file mode
+          for a file.
+        
+        * Added some handy methods to ``modulegraph.modulegraph.ModuleGraph``.
+        
+        0.10.5
+        ------
+        
+        This is a bugfix release
+        
+        * Don't look at the file extension to determine the file type
+          in modulegraph.find_modules.parse_mf_results, but use the
+          class of the item.
+        
+        * Issue #13: Improved handing of bad relative imports
+          ("from .foo import bar"), these tended to raise confusing errors and
+          are now handled like any other failed import.
+        
+        0.10.4
+        ------
+        
+        This is a bugfix release
+        
+        * There were no 'classifiers' in the package metadata due to a bug
+          in setup.py.
+        
+        0.10.3
+        ------
+        
+        This is a bugfix release
+        
+        Bugfixes
+        ........
+        
+        * ``modulegraph.find.modules.parse_mf_results`` failed when the main script of
+          a py2app module didn't have a file name ending in '.py'.
+        
+        0.10.2
+        ------
+        
+        This is a bugfix release
+        
+        Bugfixes
+        ........
+        
+        * Issue #12: modulegraph would sometimes find the wrong package *__init__*
+          module due to using the wrong search method. One easy way to reproduce the
+          problem was to have a toplevel module named *__init__*.
+        
+          Reported by Kentzo.
+        
+        0.10.1
+        ------
+        
+        This is a bugfix release
+        
+        Bugfixes
+        ........
+        
+        * Issue #11: creating xrefs and dotty graphs from modulegraphs (the --xref
+          and --graph options of py2app) didn't work with python 3 due to use of
+          APIs that aren't available in that version of python.
+        
+          Reported by Andrew Barnert.
+        
+        
+        0.10
+        ----
+        
+        This is a minor feature release
+        
+        Features
+        ........
+        
+        * ``modulegraph.find_modules.find_needed_modules`` claimed to automaticly
+          include subpackages for the "packages" argument as well, but that code
+          didn't work at all.
+        
+        * Issue #9: The modulegraph script is deprecated, use
+          "python -mmodulegraph" instead.
+        
+        * Issue #10: Ensure that the result of "zipio.open" can be used
+          in a with statement (that is, ``with zipio.open(...) as fp``.
+        
+        * No longer use "2to3" to support Python 3.
+        
+          Because of this modulegraph now supports Python 2.6
+          and later.
+        
+        * Slightly improved HTML output, which makes it easier
+          to manipulate the generated HTML using JavaScript.
+        
+          Patch by anatoly techtonik.
+        
+        * Ensure modulegraph works with changes introduced after
+          Python 3.3b1.
+        
+        * Implement support for PEP 420 ("Implicit namespace packages")
+          in Python 3.3.
+        
+        * ``modulegraph.util.imp_walk`` is deprecated and will be
+          removed in the next release of this package.
+        
+        Bugfixes
+        ........
+        
+        * The module graph was incomplete, and generated incorrect warnings
+          along the way, when a subpackage contained import statements for
+          submodules.
+        
+          An example of this is ``sqlalchemy.util``, the ``__init__.py`` file
+          for this package contains imports of modules in that modules using
+          the classic relative import syntax (that is ``import compat`` to
+          import ``sqlalchemy.util.compat``). Until this release modulegraph
+          searched the wrong path to locate these modules (and hence failed
+          to find them).
+        
+        
+        0.9.2
+        -----
+        
+        This is a bugfix release
+        
+        Bugfixes
+        ........
+        
+        * The 'packages' option to modulegraph.find_modules.find_modules ignored
+          the search path argument but always used the default search path.
+        
+        * The 'imp_find_modules' function in modulegraph.util has an argument 'path',
+          this was a string in previous release and can now also be a sequence.
+        
+        * Don't crash when a module on the 'includes' list doesn't exist, but warn
+          just like for missing 'packages' (modulegraph.find_modules.find_modules)
+        
+        0.9.1
+        -----
+        
+        This is a bugfix release
+        
+        Bug fixes
+        .........
+        
+        - Fixed the name of nodes imports in packages where the first element of
+          a dotted name can be found but the rest cannot. This used to create
+          a MissingModule node for the dotted name in the global namespace instead
+          of relative to the package.
+        
+          That is, given a package "pkg" with submodule "sub" if the "__init__.py"
+          of "pkg" contains "import sub.nomod" we now create a MissingModule node
+          for "pkg.sub.nomod" instead of "sub.nomod".
+        
+          This fixes an issue with including the crcmod package in application
+          bundles, first reported on the pythonmac-sig mailinglist by
+          Brendan Simon.
+        
+        0.9
+        ---
+        
+        This is a minor feature release
+        
+        
+        Features:
+        
+        - Documentation is now generated using `sphinx <http://pypi.python.org/pypi/sphinx>`_
+          and can be viewed at <http://packages.python.org/modulegraph>.
+        
+          The documention is very rough at this moment and in need of reorganisation and
+          language cleanup. I've basiclly writting the current version by reading the code
+          and documenting what it does, the order in which classes and methods are document
+          is therefore not necessarily the most useful.
+        
+        - The repository has moved to bitbucket
+        
+        - Renamed ``modulegraph.modulegraph.AddPackagePath`` to ``addPackagePath``,
+          likewise ``ReplacePackage`` is now ``replacePackage``. The old name is still
+          available, but is deprecated and will be removed before the 1.0 release.
+        
+        - ``modulegraph.modulegraph`` contains two node types that are unused and
+          have unclear semantics: ``FlatPackage`` and ``ArchiveModule``. These node
+          types are deprecated and will be removed before 1.0 is released.
+        
+        - Added a simple commandline tool (``modulegraph``) that will print information
+          about the dependency graph of a script.
+        
+        - Added a module (``zipio``) for dealing with paths that may refer to entries
+          inside zipfiles (such as source paths referring to modules in zipped eggfiles).
+        
+          With this addition ``modulegraph.modulegraph.os_listdir`` is deprecated and
+          it will be removed before the 1.0 release.
+        
+        Bug fixes:
+        
+        - The ``__cmp__`` method of a Node no longer causes an exception
+          when the compared-to object is not a Node. Patch by Ivan Kozik.
+        
+        - Issue #1: The initialiser for ``modulegraph.ModuleGraph`` caused an exception
+          when an entry on the path (``sys.path``) doesn't actually exist.
+        
+          Fix by "skurylo", testcase by Ronald.
+        
+        - The code no longer worked with python 2.5, this release fixes that.
+        
+        - Due to the switch to mercurial setuptools will no longer include
+          all required files. Fixed by adding a MANIFEST.in file
+        
+        - The method for printing a ``.dot`` representation of a ``ModuleGraph``
+          works again.
+        
+        
+        0.8.1
+        -----
+        
+        This is a minor feature release
+        
+        Features:
+        
+        - ``from __future__ import absolute_import`` is now supported
+        
+        - Relative imports (``from . import module``) are now supported
+        
+        - Add support for namespace packages when those are installed
+          using option ``--single-version-externally-managed`` (part
+          of setuptools/distribute)
+        
+        0.8
+        ---
+        
+        This is a minor feature release
+        
+        Features:
+        
+        - Initial support for Python 3.x
+        
+        - It is now possible to run the test suite
+          using ``python setup.py test``.
+        
+          (The actual test suite is still fairly minimal though)
+        
+Keywords: import,,dependencies
+Platform: any
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.3
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Topic :: Software Development :: Build Tools
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph.egg-info/SOURCES.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph.egg-info/SOURCES.txt
new file mode 100644
index 0000000..3409094
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph.egg-info/SOURCES.txt
@@ -0,0 +1,280 @@
+MANIFEST.in
+README.txt
+setup.cfg
+setup.py
+doc/Makefile
+doc/changelog.rst
+doc/commandline.rst
+doc/conf.py
+doc/find_modules.rst
+doc/index.rst
+doc/license.rst
+doc/modulegraph.rst
+doc/util.rst
+doc/zipio.rst
+doc/_build/doctrees/changelog.doctree
+doc/_build/doctrees/commandline.doctree
+doc/_build/doctrees/environment.pickle
+doc/_build/doctrees/find_modules.doctree
+doc/_build/doctrees/index.doctree
+doc/_build/doctrees/license.doctree
+doc/_build/doctrees/modulegraph.doctree
+doc/_build/doctrees/util.doctree
+doc/_build/doctrees/zipio.doctree
+doc/_build/html/.buildinfo
+doc/_build/html/changelog.html
+doc/_build/html/commandline.html
+doc/_build/html/find_modules.html
+doc/_build/html/genindex.html
+doc/_build/html/index.html
+doc/_build/html/license.html
+doc/_build/html/modulegraph.html
+doc/_build/html/objects.inv
+doc/_build/html/py-modindex.html
+doc/_build/html/search.html
+doc/_build/html/searchindex.js
+doc/_build/html/util.html
+doc/_build/html/zipio.html
+doc/_build/html/_sources/changelog.txt
+doc/_build/html/_sources/commandline.txt
+doc/_build/html/_sources/find_modules.txt
+doc/_build/html/_sources/index.txt
+doc/_build/html/_sources/license.txt
+doc/_build/html/_sources/modulegraph.txt
+doc/_build/html/_sources/util.txt
+doc/_build/html/_sources/zipio.txt
+doc/_build/html/_static/ajax-loader.gif
+doc/_build/html/_static/basic.css
+doc/_build/html/_static/comment-bright.png
+doc/_build/html/_static/comment-close.png
+doc/_build/html/_static/comment.png
+doc/_build/html/_static/doctools.js
+doc/_build/html/_static/down-pressed.png
+doc/_build/html/_static/down.png
+doc/_build/html/_static/file.png
+doc/_build/html/_static/jquery-1.11.1.js
+doc/_build/html/_static/jquery.js
+doc/_build/html/_static/minus.png
+doc/_build/html/_static/nature.css
+doc/_build/html/_static/plus.png
+doc/_build/html/_static/pygments.css
+doc/_build/html/_static/searchtools.js
+doc/_build/html/_static/underscore-1.3.1.js
+doc/_build/html/_static/underscore.js
+doc/_build/html/_static/up-pressed.png
+doc/_build/html/_static/up.png
+doc/_build/html/_static/websupport.js
+modulegraph/__init__.py
+modulegraph/__main__.py
+modulegraph/_compat.py
+modulegraph/find_modules.py
+modulegraph/modulegraph.py
+modulegraph/util.py
+modulegraph/zipio.py
+modulegraph.egg-info/PKG-INFO
+modulegraph.egg-info/SOURCES.txt
+modulegraph.egg-info/dependency_links.txt
+modulegraph.egg-info/entry_points.txt
+modulegraph.egg-info/requires.txt
+modulegraph.egg-info/top_level.txt
+modulegraph.egg-info/zip-safe
+modulegraph_tests/__init__.py
+modulegraph_tests/test_basic.py
+modulegraph_tests/test_edge_data.py
+modulegraph_tests/test_explicit_packages.py
+modulegraph_tests/test_implies.py
+modulegraph_tests/test_import_from_init.py
+modulegraph_tests/test_imports.py
+modulegraph_tests/test_modulegraph.py
+modulegraph_tests/test_pep420_nspkg.py
+modulegraph_tests/test_pycompat_pkg.py
+modulegraph_tests/test_relimport2.py
+modulegraph_tests/test_setuptools_nspkg.py
+modulegraph_tests/test_util.py
+modulegraph_tests/test_zipio.py
+modulegraph_tests/testdata/script
+modulegraph_tests/testdata/syspath.egg
+modulegraph_tests/testdata/syspath.zip
+modulegraph_tests/testdata/test.egg
+modulegraph_tests/testdata/test.txt
+modulegraph_tests/testdata/zipped.egg
+modulegraph_tests/testdata/nspkg/distribute-0.6.10/child/nameduser-1.5-py2.6-nspkg.pth
+modulegraph_tests/testdata/nspkg/distribute-0.6.10/child/namedpkg/slave.py
+modulegraph_tests/testdata/nspkg/distribute-0.6.10/child/nameduser-1.5-py2.6.egg-info/PKG-INFO
+modulegraph_tests/testdata/nspkg/distribute-0.6.10/child/nameduser-1.5-py2.6.egg-info/SOURCES.txt
+modulegraph_tests/testdata/nspkg/distribute-0.6.10/child/nameduser-1.5-py2.6.egg-info/dependency_links.txt
+modulegraph_tests/testdata/nspkg/distribute-0.6.10/child/nameduser-1.5-py2.6.egg-info/namespace_packages.txt
+modulegraph_tests/testdata/nspkg/distribute-0.6.10/child/nameduser-1.5-py2.6.egg-info/top_level.txt
+modulegraph_tests/testdata/nspkg/distribute-0.6.10/parent/namedpkg-1.0-py2.6-nspkg.pth
+modulegraph_tests/testdata/nspkg/distribute-0.6.10/parent/namedpkg/parent.py
+modulegraph_tests/testdata/nspkg/distribute-0.6.10/parent/namedpkg-1.0-py2.6.egg-info/PKG-INFO
+modulegraph_tests/testdata/nspkg/distribute-0.6.10/parent/namedpkg-1.0-py2.6.egg-info/SOURCES.txt
+modulegraph_tests/testdata/nspkg/distribute-0.6.10/parent/namedpkg-1.0-py2.6.egg-info/dependency_links.txt
+modulegraph_tests/testdata/nspkg/distribute-0.6.10/parent/namedpkg-1.0-py2.6.egg-info/namespace_packages.txt
+modulegraph_tests/testdata/nspkg/distribute-0.6.10/parent/namedpkg-1.0-py2.6.egg-info/top_level.txt
+modulegraph_tests/testdata/nspkg/distribute-0.6.12/child/nameduser-1.5-py2.5-nspkg.pth
+modulegraph_tests/testdata/nspkg/distribute-0.6.12/child/namedpkg/slave.py
+modulegraph_tests/testdata/nspkg/distribute-0.6.12/child/nameduser-1.5-py2.5.egg-info/PKG-INFO
+modulegraph_tests/testdata/nspkg/distribute-0.6.12/child/nameduser-1.5-py2.5.egg-info/SOURCES.txt
+modulegraph_tests/testdata/nspkg/distribute-0.6.12/child/nameduser-1.5-py2.5.egg-info/dependency_links.txt
+modulegraph_tests/testdata/nspkg/distribute-0.6.12/child/nameduser-1.5-py2.5.egg-info/namespace_packages.txt
+modulegraph_tests/testdata/nspkg/distribute-0.6.12/child/nameduser-1.5-py2.5.egg-info/top_level.txt
+modulegraph_tests/testdata/nspkg/distribute-0.6.12/parent/namedpkg-1.0-py2.5-nspkg.pth
+modulegraph_tests/testdata/nspkg/distribute-0.6.12/parent/namedpkg/parent.py
+modulegraph_tests/testdata/nspkg/distribute-0.6.12/parent/namedpkg-1.0-py2.5.egg-info/PKG-INFO
+modulegraph_tests/testdata/nspkg/distribute-0.6.12/parent/namedpkg-1.0-py2.5.egg-info/SOURCES.txt
+modulegraph_tests/testdata/nspkg/distribute-0.6.12/parent/namedpkg-1.0-py2.5.egg-info/dependency_links.txt
+modulegraph_tests/testdata/nspkg/distribute-0.6.12/parent/namedpkg-1.0-py2.5.egg-info/namespace_packages.txt
+modulegraph_tests/testdata/nspkg/distribute-0.6.12/parent/namedpkg-1.0-py2.5.egg-info/top_level.txt
+modulegraph_tests/testdata/nspkg/setuptools-0.6c9/child/nameduser-1.5-py2.5-nspkg.pth
+modulegraph_tests/testdata/nspkg/setuptools-0.6c9/child/namedpkg/slave.py
+modulegraph_tests/testdata/nspkg/setuptools-0.6c9/child/nameduser-1.5-py2.5.egg-info/PKG-INFO
+modulegraph_tests/testdata/nspkg/setuptools-0.6c9/child/nameduser-1.5-py2.5.egg-info/SOURCES.txt
+modulegraph_tests/testdata/nspkg/setuptools-0.6c9/child/nameduser-1.5-py2.5.egg-info/dependency_links.txt
+modulegraph_tests/testdata/nspkg/setuptools-0.6c9/child/nameduser-1.5-py2.5.egg-info/namespace_packages.txt
+modulegraph_tests/testdata/nspkg/setuptools-0.6c9/child/nameduser-1.5-py2.5.egg-info/top_level.txt
+modulegraph_tests/testdata/nspkg/setuptools-0.6c9/parent/namedpkg-1.0-py2.5-nspkg.pth
+modulegraph_tests/testdata/nspkg/setuptools-0.6c9/parent/namedpkg/parent.py
+modulegraph_tests/testdata/nspkg/setuptools-0.6c9/parent/namedpkg-1.0-py2.5.egg-info/PKG-INFO
+modulegraph_tests/testdata/nspkg/setuptools-0.6c9/parent/namedpkg-1.0-py2.5.egg-info/SOURCES.txt
+modulegraph_tests/testdata/nspkg/setuptools-0.6c9/parent/namedpkg-1.0-py2.5.egg-info/dependency_links.txt
+modulegraph_tests/testdata/nspkg/setuptools-0.6c9/parent/namedpkg-1.0-py2.5.egg-info/namespace_packages.txt
+modulegraph_tests/testdata/nspkg/setuptools-0.6c9/parent/namedpkg-1.0-py2.5.egg-info/top_level.txt
+modulegraph_tests/testdata/nspkg/src/install.py
+modulegraph_tests/testdata/nspkg/src/child/setup.py
+modulegraph_tests/testdata/nspkg/src/child/namedpkg/__init__.py
+modulegraph_tests/testdata/nspkg/src/child/namedpkg/slave.py
+modulegraph_tests/testdata/nspkg/src/child/nameduser.egg-info/PKG-INFO
+modulegraph_tests/testdata/nspkg/src/child/nameduser.egg-info/SOURCES.txt
+modulegraph_tests/testdata/nspkg/src/child/nameduser.egg-info/dependency_links.txt
+modulegraph_tests/testdata/nspkg/src/child/nameduser.egg-info/namespace_packages.txt
+modulegraph_tests/testdata/nspkg/src/child/nameduser.egg-info/top_level.txt
+modulegraph_tests/testdata/nspkg/src/parent/setup.py
+modulegraph_tests/testdata/nspkg/src/parent/namedpkg/__init__.py
+modulegraph_tests/testdata/nspkg/src/parent/namedpkg/parent.py
+modulegraph_tests/testdata/nspkg/src/parent/namedpkg.egg-info/PKG-INFO
+modulegraph_tests/testdata/nspkg/src/parent/namedpkg.egg-info/SOURCES.txt
+modulegraph_tests/testdata/nspkg/src/parent/namedpkg.egg-info/dependency_links.txt
+modulegraph_tests/testdata/nspkg/src/parent/namedpkg.egg-info/namespace_packages.txt
+modulegraph_tests/testdata/nspkg/src/parent/namedpkg.egg-info/top_level.txt
+modulegraph_tests/testdata/subdir/file1.txt
+modulegraph_tests/testdata/subdir/file2.txt
+modulegraph_tests/testdata/syspath/myext.pyd
+modulegraph_tests/testdata/syspath/mymodule.py
+modulegraph_tests/testdata/syspath/mymodule3.py
+modulegraph_tests/testdata/syspath/mypkg/__init__.py
+modulegraph_tests/testpkg-compatmodule/pkg/__init__.py
+modulegraph_tests/testpkg-compatmodule/pkg/api.py
+modulegraph_tests/testpkg-compatmodule/pkg/api2.py
+modulegraph_tests/testpkg-compatmodule/pkg/api3.py
+modulegraph_tests/testpkg-edgedata/function_class_existing.py
+modulegraph_tests/testpkg-edgedata/function_conditional_existing.py
+modulegraph_tests/testpkg-edgedata/function_conditional_import2_existing.py
+modulegraph_tests/testpkg-edgedata/function_conditional_import_existing.py
+modulegraph_tests/testpkg-edgedata/function_existing.py
+modulegraph_tests/testpkg-edgedata/function_import2_existing.py
+modulegraph_tests/testpkg-edgedata/function_import_existing.py
+modulegraph_tests/testpkg-edgedata/script.py
+modulegraph_tests/testpkg-edgedata/script_from_import.py
+modulegraph_tests/testpkg-edgedata/script_multi_import.py
+modulegraph_tests/testpkg-edgedata/toplevel_class_existing.py
+modulegraph_tests/testpkg-edgedata/toplevel_conditional_existing.py
+modulegraph_tests/testpkg-edgedata/toplevel_conditional_import2_existing.py
+modulegraph_tests/testpkg-edgedata/toplevel_conditional_import_existing.py
+modulegraph_tests/testpkg-edgedata/toplevel_existing.py
+modulegraph_tests/testpkg-edgedata/toplevel_import2_existing.py
+modulegraph_tests/testpkg-edgedata/toplevel_import_existing.py
+modulegraph_tests/testpkg-edgedata/pkg/__init__.py
+modulegraph_tests/testpkg-edgedata/pkg/function_class_existing.py
+modulegraph_tests/testpkg-edgedata/pkg/function_conditional_existing.py
+modulegraph_tests/testpkg-edgedata/pkg/function_conditional_import2_existing.py
+modulegraph_tests/testpkg-edgedata/pkg/function_conditional_import_existing.py
+modulegraph_tests/testpkg-edgedata/pkg/function_existing.py
+modulegraph_tests/testpkg-edgedata/pkg/function_import2_existing.py
+modulegraph_tests/testpkg-edgedata/pkg/function_import_existing.py
+modulegraph_tests/testpkg-edgedata/pkg/toplevel_class_existing.py
+modulegraph_tests/testpkg-edgedata/pkg/toplevel_conditional_existing.py
+modulegraph_tests/testpkg-edgedata/pkg/toplevel_conditional_import2_existing.py
+modulegraph_tests/testpkg-edgedata/pkg/toplevel_conditional_import_existing.py
+modulegraph_tests/testpkg-edgedata/pkg/toplevel_existing.py
+modulegraph_tests/testpkg-edgedata/pkg/toplevel_import2_existing.py
+modulegraph_tests/testpkg-edgedata/pkg/toplevel_import_existing.py
+modulegraph_tests/testpkg-import-from-init/script.py
+modulegraph_tests/testpkg-import-from-init/pkg/__init__.py
+modulegraph_tests/testpkg-import-from-init/pkg/subpkg/__init__.py
+modulegraph_tests/testpkg-import-from-init/pkg/subpkg/_collections.py
+modulegraph_tests/testpkg-import-from-init/pkg/subpkg/compat.py
+modulegraph_tests/testpkg-import-from-init/pkg2/__init__.py
+modulegraph_tests/testpkg-import-from-init/pkg2/subpkg/__init__.py
+modulegraph_tests/testpkg-import-from-init/pkg2/subpkg/_collections.py
+modulegraph_tests/testpkg-import-from-init/pkg2/subpkg/compat.py
+modulegraph_tests/testpkg-packages/main_script.py
+modulegraph_tests/testpkg-packages/pkg/__init__.py
+modulegraph_tests/testpkg-packages/pkg/sub3.py
+modulegraph_tests/testpkg-packages/pkg/sub1/__init__.py
+modulegraph_tests/testpkg-packages/pkg/sub1/modA.py
+modulegraph_tests/testpkg-packages/pkg/sub2/__init__.py
+modulegraph_tests/testpkg-packages/pkg/sub2/mod.py
+modulegraph_tests/testpkg-pep420-namespace/path1/package/sub2.py
+modulegraph_tests/testpkg-pep420-namespace/path2/package/sub1.py
+modulegraph_tests/testpkg-pep420-namespace/path2/package/nspkg/mod.py
+modulegraph_tests/testpkg-pep420-namespace/path2/package/subpackage/__init__.py
+modulegraph_tests/testpkg-pep420-namespace/path2/package/subpackage/sub.py
+modulegraph_tests/testpkg-regr1/main_script.py
+modulegraph_tests/testpkg-regr1/pkg/__init__.py
+modulegraph_tests/testpkg-regr1/pkg/a.py
+modulegraph_tests/testpkg-regr1/pkg/b.py
+modulegraph_tests/testpkg-regr2/main_script.py
+modulegraph_tests/testpkg-regr2/pkg/__init__.py
+modulegraph_tests/testpkg-regr2/pkg/base.py
+modulegraph_tests/testpkg-regr2/pkg/pkg.py
+modulegraph_tests/testpkg-regr3/script.py
+modulegraph_tests/testpkg-regr3/mypkg/__init__.py
+modulegraph_tests/testpkg-regr3/mypkg/distutils/__init__.py
+modulegraph_tests/testpkg-regr3/mypkg/distutils/ccompiler.py
+modulegraph_tests/testpkg-regr4/script.py
+modulegraph_tests/testpkg-regr4/pkg/__init__.py
+modulegraph_tests/testpkg-regr4/pkg/core/__init__.py
+modulegraph_tests/testpkg-regr4/pkg/core/callables.py
+modulegraph_tests/testpkg-regr4/pkg/core/listener.py
+modulegraph_tests/testpkg-regr4/pkg/core/listenerimpl.py
+modulegraph_tests/testpkg-regr5/__init__.py
+modulegraph_tests/testpkg-regr5/script.py
+modulegraph_tests/testpkg-regr6/module.py
+modulegraph_tests/testpkg-regr6/script.py
+modulegraph_tests/testpkg-relimport/mod.py
+modulegraph_tests/testpkg-relimport/script.py
+modulegraph_tests/testpkg-relimport/pkg/__init__.py
+modulegraph_tests/testpkg-relimport/pkg/mod.py
+modulegraph_tests/testpkg-relimport/pkg/oldstyle.py
+modulegraph_tests/testpkg-relimport/pkg/relative.py
+modulegraph_tests/testpkg-relimport/pkg/relimport.py
+modulegraph_tests/testpkg-relimport/pkg/toplevel.py
+modulegraph_tests/testpkg-relimport/pkg/sub2/__init__.py
+modulegraph_tests/testpkg-relimport/pkg/sub2/mod.py
+modulegraph_tests/testpkg-relimport/pkg/subpkg/__init__.py
+modulegraph_tests/testpkg-relimport/pkg/subpkg/mod2.py
+modulegraph_tests/testpkg-relimport/pkg/subpkg/relative.py
+modulegraph_tests/testpkg-relimport/pkg/subpkg/relative2.py
+modulegraph_tests/testpkg-relimport2/toplevel.py
+modulegraph_tests/testpkg-relimport2/pkg/__init__.py
+modulegraph_tests/testpkg-relimport2/pkg/mod1.py
+modulegraph_tests/testpkg-relimport2/pkg/mod2.py
+modulegraph_tests/testpkg-relimport2/pkg/mod3.py
+modulegraph_tests/testpkg-relimport2/pkg/sub/__init__.py
+modulegraph_tests/testpkg-setuptools-namespace/setup.py
+modulegraph_tests/testpkg-setuptools-namespace/build/lib/nspkg/__init__.py
+modulegraph_tests/testpkg-setuptools-namespace/build/lib/nspkg/module.py
+modulegraph_tests/testpkg-setuptools-namespace/build/lib/nspkg/nssubpkg/__init__.py
+modulegraph_tests/testpkg-setuptools-namespace/build/lib/nspkg/nssubpkg/sub.py
+modulegraph_tests/testpkg-setuptools-namespace/src/nspkg/__init__.py
+modulegraph_tests/testpkg-setuptools-namespace/src/nspkg/module.py
+modulegraph_tests/testpkg-setuptools-namespace/src/nspkg.egg-info/PKG-INFO
+modulegraph_tests/testpkg-setuptools-namespace/src/nspkg.egg-info/SOURCES.txt
+modulegraph_tests/testpkg-setuptools-namespace/src/nspkg.egg-info/dependency_links.txt
+modulegraph_tests/testpkg-setuptools-namespace/src/nspkg.egg-info/namespace_packages.txt
+modulegraph_tests/testpkg-setuptools-namespace/src/nspkg.egg-info/not-zip-safe
+modulegraph_tests/testpkg-setuptools-namespace/src/nspkg.egg-info/top_level.txt
+modulegraph_tests/testpkg-setuptools-namespace/src/nspkg/nssubpkg/__init__.py
+modulegraph_tests/testpkg-setuptools-namespace/src/nspkg/nssubpkg/sub.py
+scripts/extract_implies.py
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph.egg-info/dependency_links.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph.egg-info/dependency_links.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph.egg-info/entry_points.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph.egg-info/entry_points.txt
new file mode 100644
index 0000000..9fc3791
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph.egg-info/entry_points.txt
@@ -0,0 +1,3 @@
+[console_scripts]
+modulegraph = modulegraph.__main__:main
+
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph.egg-info/requires.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph.egg-info/requires.txt
new file mode 100644
index 0000000..dde6882
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph.egg-info/requires.txt
@@ -0,0 +1 @@
+altgraph >= 0.12
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph.egg-info/top_level.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph.egg-info/top_level.txt
new file mode 100644
index 0000000..e0e1b8f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph.egg-info/top_level.txt
@@ -0,0 +1 @@
+modulegraph
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph.egg-info/zip-safe b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph.egg-info/zip-safe
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph.egg-info/zip-safe
@@ -0,0 +1 @@
+
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph/__init__.py
new file mode 100644
index 0000000..7c85619
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph/__init__.py
@@ -0,0 +1,2 @@
+import pkg_resources
+__version__ = pkg_resources.require('modulegraph')[0].version
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph/__main__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph/__main__.py
new file mode 100644
index 0000000..2a84cda
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph/__main__.py
@@ -0,0 +1,76 @@
+from __future__ import print_function
+import sys
+import os
+import optparse
+import textwrap
+from .modulegraph import ModuleGraph
+
+def main():
+    # Parse command line
+    usage = textwrap.dedent('''\
+        Usage:
+            modulegraph [options] scriptfile ...
+
+        Valid options:
+        * -d: Increase debug level
+        * -q: Clear debug level
+
+        * -m: arguments are module names, not script files
+        * -x name: Add 'name' to the excludes list
+        * -p name: Add 'name' to the module search path
+
+        * -g: Output a .dot graph
+        * -h: Output a html file
+    ''')
+    parser = optparse.OptionParser(usage=usage, add_help_option=False)
+    parser.add_option('-d', action='count', dest='debug', default=1)
+    parser.add_option('-q', action='store_const', dest='debug', const=0)
+
+    parser.add_option('-m', action='store_true', dest='domods', default=False)
+    parser.add_option('-x', action='append', dest='excludes', default=[])
+    parser.add_option('-p', action='append', dest='addpath', default=[])
+
+    parser.add_option('-g', action='store_const', dest='output', const='dot')
+    parser.add_option('-h', action='store_const', dest='output', const='html')
+    opts, args = parser.parse_args()
+
+    if not args:
+        print("No script specified", file=sys.stderr)
+        print(usage, file=sys.stderr)
+        sys.exit(1)
+
+    script = args[0]
+
+    # Set the path based on sys.path and the script directory
+    path = sys.path[:]
+    path[0] = os.path.dirname(script)
+    path = opts.addpath + path
+    if opts.debug > 1:
+        print("path:", file=sys.stderr)
+        for item in path:
+            print("   ", repr(item), file=sys.stderr)
+
+    # Create the module finder and turn its crank
+    mf = ModuleGraph(path, excludes=opts.excludes, debug=opts.debug)
+    for arg in args:
+        if opts.domods:
+            if arg[-2:] == '.*':
+                mf.import_hook(arg[:-2], None, ["*"])
+            else:
+                mf.import_hook(arg)
+        else:
+            mf.run_script(arg)
+    if opts.output == 'dot':
+        mf.graphreport()
+    elif opts.output == 'html':
+        mf.create_xref()
+    else:
+        mf.report()
+    sys.exit(0)
+
+
+if __name__ == '__main__':
+    try:
+        main()
+    except KeyboardInterrupt:
+        print("\n[interrupt]")
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph/find_modules.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph/find_modules.py
new file mode 100644
index 0000000..fee8c17
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph/find_modules.py
@@ -0,0 +1,366 @@
+"""
+modulegraph.find_modules - High-level module dependency finding interface
+=========================================================================
+
+History
+........
+
+Originally (loosely) based on code in py2exe's build_exe.py by Thomas Heller.
+"""
+from __future__ import absolute_import
+
+import sys
+import os
+import imp
+import warnings
+
+import modulegraph.modulegraph as modulegraph
+from modulegraph.modulegraph import Alias, Script, Extension
+from modulegraph.util import imp_find_module
+
+__all__ = [
+    'find_modules', 'parse_mf_results'
+]
+
+def get_implies():
+    result = {
+        # imports done from builtin modules in C code (untrackable by modulegraph)
+        "_curses":      ["curses"],
+        "posix":        ["resource"],
+        "gc":           ["time"],
+        "time":         ["_strptime"],
+        "datetime":     ["time"],
+        "MacOS":        ["macresource"],
+        "cPickle":      ["copy_reg", "cStringIO"],
+        "parser":       ["copy_reg"],
+        "codecs":       ["encodings"],
+        "cStringIO":    ["copy_reg"],
+        "_sre":         ["copy", "string", "sre"],
+        "zipimport":    ["zlib"],
+
+        # Python 3.2:
+        "_datetime":    ["time", "_strptime"],
+        "_json":        ["json.decoder"],
+        "_pickle":      ["codecs", "copyreg", "_compat_pickle"],
+        "_posixsubprocess": ["gc"],
+        "_ssl":         ["socket"],
+
+        # Python 3.3:
+        "_elementtree": ["copy", "xml.etree.ElementPath" ],
+
+        # mactoolboxglue can do a bunch more of these
+        # that are far harder to predict, these should be tracked
+        # manually for now.
+
+        # this isn't C, but it uses __import__
+        "anydbm":       ["dbhash", "gdbm", "dbm", "dumbdbm", "whichdb"],
+        # package aliases
+        "wxPython.wx":  Alias('wx'),
+
+    }
+
+    if sys.version_info[0] == 3:
+        result["_sre"] = ["copy", "re"]
+        result["parser"] = ["copyreg"]
+
+        # _frozen_importlib is part of the interpreter itself
+        result["_frozen_importlib"] = None
+
+    if sys.version_info[0] == 2 and sys.version_info[1] >= 5:
+        result.update({
+            "email.base64MIME":         Alias("email.base64mime"),
+            "email.Charset":            Alias("email.charset"),
+            "email.Encoders":           Alias("email.encoders"),
+            "email.Errors":             Alias("email.errors"),
+            "email.Feedparser":         Alias("email.feedParser"),
+            "email.Generator":          Alias("email.generator"),
+            "email.Header":             Alias("email.header"),
+            "email.Iterators":          Alias("email.iterators"),
+            "email.Message":            Alias("email.message"),
+            "email.Parser":             Alias("email.parser"),
+            "email.quopriMIME":         Alias("email.quoprimime"),
+            "email.Utils":              Alias("email.utils"),
+            "email.MIMEAudio":          Alias("email.mime.audio"),
+            "email.MIMEBase":           Alias("email.mime.base"),
+            "email.MIMEImage":          Alias("email.mime.image"),
+            "email.MIMEMessage":        Alias("email.mime.message"),
+            "email.MIMEMultipart":      Alias("email.mime.multipart"),
+            "email.MIMENonMultipart":   Alias("email.mime.nonmultipart"),
+            "email.MIMEText":           Alias("email.mime.text"),
+        })
+
+    if sys.version_info[:2] >= (2, 5):
+        result["_elementtree"] = ["pyexpat"]
+
+        import xml.etree
+        files = os.listdir(xml.etree.__path__[0])
+        for fn in files:
+            if fn.endswith('.py') and fn != "__init__.py":
+                result["_elementtree"].append("xml.etree.%s"%(fn[:-3],))
+
+    if sys.version_info[:2] >= (2, 6):
+        result['future_builtins'] = ['itertools']
+
+    # os.path is an alias for a platform specific submodule,
+    # ensure that the graph shows this.
+    result['os.path'] = Alias(os.path.__name__)
+
+
+    return result
+
+def parse_mf_results(mf):
+    """
+    Return two lists: the first one contains the python files in the graph,
+    the second the C extensions.
+
+    :param mf: a :class:`modulegraph.modulegraph.ModuleGraph` instance
+    """
+    #for name, imports in get_hidden_imports().items():
+    #    if name in mf.modules.keys():
+    #        for mod in imports:
+    #            mf.import_hook(mod)
+
+    # Retrieve modules from modulegraph
+    py_files = []
+    extensions = []
+
+    for item in mf.flatten():
+        # There may be __main__ modules (from mf.run_script), but
+        # we don't need it in the zipfile we build.
+        if item.identifier == "__main__":
+            continue
+        src = item.filename
+        if src and src != '-':
+            if isinstance(item, Script):
+                # Scripts are python files
+                py_files.append(item)
+
+            elif isinstance(item, Extension):
+                extensions.append(item)
+
+            else:
+                py_files.append(item)
+
+    # sort on the file names, the output is nicer to read
+    py_files.sort(key=lambda v: v.filename)
+    extensions.sort(key=lambda v: v.filename)
+    return py_files, extensions
+
+
+def plat_prepare(includes, packages, excludes):
+    # used by Python itself
+    includes.update(["warnings", "unicodedata", "weakref"])
+
+    #if os.uname()[0] != 'java':
+        # Jython specific imports in the stdlib:
+        #excludes.update([
+        #    'java.lang',
+        #    'org.python.core',
+        #])
+
+    if not sys.platform.startswith('irix'):
+        excludes.update([
+            'AL',
+            'sgi',
+            'vms_lib',
+        ])
+
+    if not sys.platform in ('mac', 'darwin'):
+        # XXX - this doesn't look nearly complete
+        excludes.update([
+            'Audio_mac',
+            'Carbon.File',
+            'Carbon.Folder',
+            'Carbon.Folders',
+            'EasyDialogs',
+            'MacOS',
+            'macfs',
+            'macostools',
+            #'macpath',
+            '_scproxy',
+        ])
+
+    if not sys.platform == 'win32':
+        # only win32
+        excludes.update([
+            #'ntpath',
+            'nturl2path',
+            'win32api',
+            'win32con',
+            'win32event',
+            'win32evtlogutil',
+            'win32evtlog',
+            'win32file',
+            'win32gui',
+            'win32pipe',
+            'win32process',
+            'win32security',
+            'pywintypes',
+            'winsound',
+            'win32',
+            '_winreg',
+            '_winapi',
+            'msvcrt',
+            'winreg',
+            '_subprocess',
+         ])
+
+    if not sys.platform == 'riscos':
+        excludes.update([
+             'riscosenviron',
+             #'riscospath',
+             'rourl2path',
+          ])
+
+    if not sys.platform == 'dos' or sys.platform.startswith('ms-dos'):
+        excludes.update([
+            'dos',
+        ])
+
+    if not sys.platform == 'os2emx':
+        excludes.update([
+            #'os2emxpath',
+            '_emx_link',
+        ])
+
+    excludes.update(set(['posix', 'nt', 'os2', 'mac', 'ce', 'riscos']) - set(sys.builtin_module_names))
+
+    # Carbon.Res depends on this, but the module hasn't been present
+    # for a while...
+    excludes.add('OverrideFrom23')
+    excludes.add('OverrideFrom23._Res')
+
+    # import trickery in the dummy_threading module (stdlib)
+    excludes.add('_dummy_threading')
+
+    try:
+        imp_find_module('poll')
+    except ImportError:
+        excludes.update([
+            'poll',
+        ])
+
+def find_needed_modules(mf=None, scripts=(), includes=(), packages=(), warn=warnings.warn):
+    if mf is None:
+        mf = modulegraph.ModuleGraph()
+    # feed Modulefinder with everything, and return it.
+
+    for path in scripts:
+        mf.run_script(path)
+
+    for mod in includes:
+        try:
+            if mod[-2:] == '.*':
+                mf.import_hook(mod[:-2], None, ['*'])
+            else:
+                mf.import_hook(mod)
+        except ImportError:
+            warn("No module named %s"%(mod,))
+
+    for f in packages:
+        # If modulegraph has seen a reference to the package, then
+        # we prefer to believe that (imp_find_module doesn't seem to locate
+        # sub-packages)
+        m = mf.findNode(f)
+        if m is not None:
+            path = m.packagepath[0]
+        else:
+            # Find path of package
+            # TODO: use imp_find_module_or_importer
+            try:
+                path = imp_find_module(f, mf.path)[1]
+            except ImportError:
+                warn("No package named %s" % f)
+                continue
+
+        # walk the path to find subdirs containing __init__.py files
+        # scan the results (directory of __init__.py files)
+        # first trim the path (of the head package),
+        # then convert directory name in package name,
+        # finally push into modulegraph.
+        # FIXME:
+        # 1) Needs to be adjusted for namespace packages in python 3.3
+        # 2) Code is fairly dodgy and needs better tests
+        for (dirpath, dirnames, filenames) in os.walk(path):
+            if '__init__.py' in filenames and dirpath.startswith(path):
+                package = f + '.' + dirpath[len(path)+1:].replace(os.sep, '.')
+                if package.endswith('.'):
+                    package = package[:-1]
+                m = mf.import_hook(package, None, ["*"])
+            else:
+                # Exclude subtrees that aren't packages
+                dirnames[:] = []
+
+
+    return mf
+
+#
+# resource constants
+#
+PY_SUFFIXES = ['.py', '.pyw', '.pyo', '.pyc']
+C_SUFFIXES = [
+    _triple[0] for _triple in imp.get_suffixes()
+    if _triple[2] == imp.C_EXTENSION
+]
+
+#
+# side-effects
+#
+
+def _replacePackages():
+    REPLACEPACKAGES = {
+        '_xmlplus':     'xml',
+    }
+    for k,v in REPLACEPACKAGES.items():
+        modulegraph.replacePackage(k, v)
+
+_replacePackages()
+
+def find_modules(scripts=(), includes=(), packages=(), excludes=(), path=None, debug=0):
+    """
+    High-level interface, takes iterables for:
+        scripts, includes, packages, excludes
+
+    And returns a :class:`modulegraph.modulegraph.ModuleGraph` instance,
+    python_files, and extensions
+
+    python_files is a list of pure python dependencies as modulegraph.Module objects,
+    extensions is a list of platform-specific C extension dependencies as modulegraph.Module objects
+    """
+    scripts = set(scripts)
+    includes = set(includes)
+    packages = set(packages)
+    excludes = set(excludes)
+    plat_prepare(includes, packages, excludes)
+    mf = modulegraph.ModuleGraph(
+        path=path,
+        excludes=(excludes - includes),
+        implies=get_implies(),
+        debug=debug,
+    )
+    find_needed_modules(mf, scripts, includes, packages)
+    return mf
+
+def test():
+    if '-g' in sys.argv[1:]:
+        sys.argv.remove('-g')
+        dograph = True
+    else:
+        dograph = False
+    if '-x' in sys.argv[1:]:
+        sys.argv.remove('-x')
+        doxref = True
+    else:
+        doxref= False
+
+    scripts = sys.argv[1:] or [__file__]
+    mf = find_modules(scripts=scripts)
+    if doxref:
+        mf.create_xref()
+    elif dograph:
+        mf.graphreport()
+    else:
+        mf.report()
+
+if __name__ == '__main__':
+    test()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph/modulegraph.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph/modulegraph.py
new file mode 100644
index 0000000..2795cc4
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph/modulegraph.py
@@ -0,0 +1,1686 @@
+"""
+Find modules used by a script, using bytecode analysis.
+
+Based on the stdlib modulefinder by Thomas Heller and Just van Rossum,
+but uses a graph data structure and 2.3 features
+
+XXX: Verify all calls to import_hook (and variants) to ensure that
+imports are done in the right way.
+"""
+from __future__ import absolute_import, print_function
+
+import pkg_resources
+
+import dis
+import imp
+import marshal
+import os
+import sys
+import struct
+import zipimport
+import re
+from collections import deque, namedtuple
+import ast
+
+from altgraph.ObjectGraph import ObjectGraph
+from altgraph import GraphError
+
+from itertools import count
+
+from modulegraph import util
+from modulegraph import zipio
+
+if sys.version_info[0] == 2:
+    from StringIO import StringIO as BytesIO
+    from StringIO import StringIO
+    from  urllib import pathname2url
+    def _Bchr(value):
+        return chr(value)
+
+else:
+    from urllib.request  import pathname2url
+    from io import BytesIO, StringIO
+
+    def _Bchr(value):
+        return value
+
+
+# File open mode for reading (univeral newlines)
+if sys.version_info[0] == 2:
+    _READ_MODE = "rU"
+else:
+    _READ_MODE = "r"
+
+
+
+
+# Modulegraph does a good job at simulating Python's, but it can not
+# handle packagepath modifications packages make at runtime.  Therefore there
+# is a mechanism whereby you can register extra paths in this map for a
+# package, and it will be honored.
+#
+# Note this is a mapping is lists of paths.
+_packagePathMap = {}
+
+# Prefix used in magic .pth files used by setuptools to create namespace
+# packages without an __init__.py file.
+#
+# The value is a list of such prefixes as the prefix varies with versions of
+# setuptools.
+_SETUPTOOLS_NAMESPACEPKG_PTHs=(
+    "import sys,types,os; p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('",
+    "import sys,new,os; p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('",
+    "import sys, types, os;p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('",
+)
+
+
+def _namespace_package_path(fqname, pathnames, path=None):
+    """
+    Return the __path__ for the python package in *fqname*.
+
+    This function uses setuptools metadata to extract information
+    about namespace packages from installed eggs.
+    """
+    working_set = pkg_resources.WorkingSet(path)
+
+    path = list(pathnames)
+
+    for dist in working_set:
+        if dist.has_metadata('namespace_packages.txt'):
+            namespaces = dist.get_metadata(
+                    'namespace_packages.txt').splitlines()
+            if fqname in namespaces:
+                nspath = os.path.join(dist.location, *fqname.split('.'))
+                if nspath not in path:
+                    path.append(nspath)
+
+    return path
+
+_strs = re.compile(r'''^\s*["']([A-Za-z0-9_]+)["'],?\s*''') # "<- emacs happy
+
+def _eval_str_tuple(value):
+    """
+    Input is the repr of a tuple of strings, output
+    is that tuple.
+
+    This only works with a tuple where the members are
+    python identifiers.
+    """
+    if not (value.startswith('(') and value.endswith(')')):
+        raise ValueError(value)
+
+    orig_value = value
+    value = value[1:-1]
+
+    result = []
+    while value:
+        m = _strs.match(value)
+        if m is None:
+            raise ValueError(orig_value)
+
+        result.append(m.group(1))
+        value = value[len(m.group(0)):]
+
+    return tuple(result)
+
+def _path_from_importerror(exc, default):
+    # This is a hack, but sadly enough the necessary information
+    # isn't available otherwise.
+    m = re.match('^No module named (\S+)$', str(exc))
+    if m is not None:
+        return m.group(1)
+
+    return default
+
+def os_listdir(path):
+    """
+    Deprecated name
+    """
+    warnings.warn("Use zipio.listdir instead of os_listdir",
+            DeprecationWarning)
+    return zipio.listdir(path)
+
+
+def _code_to_file(co):
+    """ Convert code object to a .pyc pseudo-file """
+    return BytesIO(
+            imp.get_magic() + b'\0\0\0\0' + marshal.dumps(co))
+
+
+def find_module(name, path=None):
+    """
+    A version of imp.find_module that works with zipped packages.
+    """
+    if path is None:
+        path = sys.path
+
+    # Support for the PEP302 importer for normal imports:
+    # - Python 2.5 has pkgutil.ImpImporter
+    # - In setuptools 0.7 and later there's _pkgutil.ImpImporter
+    # - In earlier setuptools versions you pkg_resources.ImpWrapper
+    #
+    # XXX: This is a bit of a hack, should check if we can just rely on
+    # PEP302's get_code() method with all recent versions of pkgutil and/or
+    # setuptools (setuptools 0.6.latest, setuptools trunk and python2.[45])
+    #
+    # For python 3.3 this code should be replaced by code using importlib,
+    # for python 3.2 and 2.7 this should be cleaned up a lot.
+    try:
+        from pkgutil import ImpImporter
+    except ImportError:
+        try:
+            from _pkgutil import ImpImporter
+        except ImportError:
+            ImpImporter = pkg_resources.ImpWrapper
+
+    namespace_path =[]
+    fp = None
+    for entry in path:
+        importer = pkg_resources.get_importer(entry)
+        if importer is None:
+            continue
+
+        if sys.version_info[:2] >= (3,3) and hasattr(importer, 'find_loader'):
+            loader, portions = importer.find_loader(name)
+
+        else:
+            loader = importer.find_module(name)
+            portions = []
+
+        namespace_path.extend(portions)
+
+        if loader is None: continue
+
+        if isinstance(importer, ImpImporter):
+            filename = loader.filename
+            if filename.endswith('.pyc') or filename.endswith('.pyo'):
+                fp = open(filename, 'rb')
+                description = ('.pyc', 'rb', imp.PY_COMPILED)
+                return (fp, filename, description)
+
+            elif filename.endswith('.py'):
+                if sys.version_info[0] == 2:
+                    fp = open(filename, _READ_MODE)
+                else:
+                    with open(filename, 'rb') as fp:
+                        encoding = util.guess_encoding(fp)
+
+                    fp = open(filename, _READ_MODE, encoding=encoding)
+                description = ('.py', _READ_MODE, imp.PY_SOURCE)
+                return (fp, filename, description)
+
+            else:
+                for _sfx, _mode, _type in imp.get_suffixes():
+                    if _type == imp.C_EXTENSION and filename.endswith(_sfx):
+                        description = (_sfx, 'rb', imp.C_EXTENSION)
+                        break
+                else:
+                    description = ('', '', imp.PKG_DIRECTORY)
+
+                return (None, filename, description)
+
+        if hasattr(loader, 'path'):
+            if loader.path.endswith('.pyc') or loader.path.endswith('.pyo'):
+                fp = open(loader.path, 'rb')
+                description = ('.pyc', 'rb', imp.PY_COMPILED)
+                return (fp, loader.path, description)
+
+
+        if hasattr(loader, 'get_source'):
+            source = loader.get_source(name)
+            fp = StringIO(source)
+            co = None
+
+        else:
+            source = None
+
+        if source is None:
+            if hasattr(loader, 'get_code'):
+                co = loader.get_code(name)
+                fp = _code_to_file(co)
+
+            else:
+                fp = None
+                co = None
+
+        pathname = os.path.join(entry, *name.split('.'))
+
+        if isinstance(loader, zipimport.zipimporter):
+            # Check if this happens to be a wrapper module introduced by
+            # setuptools, if it is we return the actual extension.
+            zn = '/'.join(name.split('.'))
+            for _sfx, _mode, _type in imp.get_suffixes():
+                if _type == imp.C_EXTENSION:
+                    p = loader.prefix + zn + _sfx
+                    if loader._files is None:
+                        loader_files = zipimport._zip_directory_cache[loader.archive]
+                    else:
+                        loader_files = loader._files
+
+                    if p in loader_files:
+                        description = (_sfx, 'rb', imp.C_EXTENSION)
+                        return (None, pathname + _sfx, description)
+
+        if hasattr(loader, 'is_package') and loader.is_package(name):
+            return (None, pathname, ('', '', imp.PKG_DIRECTORY))
+
+        if co is None:
+            if hasattr(loader, 'path'):
+                filename = loader.path
+            elif hasattr(loader, 'get_filename'):
+                filename = loader.get_filename(name)
+                if source is not None:
+                    if filename.endswith(".pyc") or filename.endswith(".pyo"):
+                        filename = filename[:-1]
+            else:
+                filename = None
+
+            if filename is not None and (filename.endswith('.py') or filename.endswith('.pyw')):
+                return (fp, filename, ('.py', 'rU', imp.PY_SOURCE))
+            else:
+                if fp is not None:
+                    fp.close()
+                return (None, filename, (os.path.splitext(filename)[-1], 'rb', imp.C_EXTENSION))
+
+        else:
+            if hasattr(loader, 'path'):
+                return (fp, loader.path, ('.pyc', 'rb', imp.PY_COMPILED))
+            else:
+                return (fp, pathname + '.pyc', ('.pyc', 'rb', imp.PY_COMPILED))
+
+    if namespace_path:
+        if fp is not None:
+            fp.close()
+        return (None, namespace_path[0], ('', namespace_path, imp.PKG_DIRECTORY))
+
+    raise ImportError(name)
+
+def moduleInfoForPath(path):
+    for (ext, readmode, typ) in imp.get_suffixes():
+        if path.endswith(ext):
+            return os.path.basename(path)[:-len(ext)], readmode, typ
+    return None
+
+# A Public interface
+import warnings
+def AddPackagePath(packagename, path):
+    warnings.warn("Use addPackagePath instead of AddPackagePath",
+            DeprecationWarning)
+
+    addPackagePath(packagename, path)
+
+def addPackagePath(packagename, path):
+    paths = _packagePathMap.get(packagename, [])
+    paths.append(path)
+    _packagePathMap[packagename] = paths
+
+_replacePackageMap = {}
+
+# This ReplacePackage mechanism allows modulefinder to work around the
+# way the _xmlplus package injects itself under the name "xml" into
+# sys.modules at runtime by calling ReplacePackage("_xmlplus", "xml")
+# before running ModuleGraph.
+def ReplacePackage(oldname, newname):
+    warnings.warn("use replacePackage instead of ReplacePackage",
+            DeprecationWarning)
+    replacePackage(oldname, newname)
+
+def replacePackage(oldname, newname):
+    _replacePackageMap[oldname] = newname
+
+
+class DependencyInfo (namedtuple("DependencyInfo", ["conditional", "function", "tryexcept", "fromlist"])):
+    __slots__ = ()
+
+    def _merged(self, other):
+        if (not self.conditional and not self.function and not self.tryexcept) \
+            or (not other.conditional and not other.function and not other.tryexcept):
+                return DependencyInfo(conditional=False, function=False, tryexcept=False, fromlist=self.fromlist and other.fromlist)
+
+        else:
+            return DependencyInfo(
+                    conditional=self.conditional or other.conditional,
+                    function=self.function or other.function,
+                    tryexcept=self.tryexcept or other.tryexcept,
+                    fromlist=self.fromlist and other.fromlist)
+
+
+class Node(object):
+    def __init__(self, identifier):
+        self.debug = 0
+        self.graphident = identifier
+        self.identifier = identifier
+        self._namespace = {}
+        self.filename = None
+        self.packagepath = None
+        self.code = None
+        # The set of global names that are assigned to in the module.
+        # This includes those names imported through starimports of
+        # Python modules.
+        self.globalnames = set()
+        # The set of starimports this module did that could not be
+        # resolved, ie. a starimport from a non-Python module.
+        self.starimports = set()
+
+    def __contains__(self, name):
+        return name in self._namespace
+
+    def __getitem__(self, name):
+        return self._namespace[name]
+
+    def __setitem__(self, name, value):
+        self._namespace[name] = value
+
+    def get(self, *args):
+        return self._namespace.get(*args)
+
+    def __cmp__(self, other):
+        try:
+            otherIdent = getattr(other, 'graphident')
+        except AttributeError:
+            return NotImplemented
+
+        return cmp(self.graphident, otherIdent)
+
+    def __eq__(self, other):
+        try:
+            otherIdent = getattr(other, 'graphident')
+        except AttributeError:
+            return False
+
+        return self.graphident == otherIdent
+
+    def __ne__(self, other):
+        try:
+            otherIdent = getattr(other, 'graphident')
+        except AttributeError:
+            return True
+
+        return self.graphident != otherIdent
+
+    def __lt__(self, other):
+        try:
+            otherIdent = getattr(other, 'graphident')
+        except AttributeError:
+            return NotImplemented
+
+        return self.graphident < otherIdent
+
+    def __le__(self, other):
+        try:
+            otherIdent = getattr(other, 'graphident')
+        except AttributeError:
+            return NotImplemented
+
+        return self.graphident <= otherIdent
+
+    def __gt__(self, other):
+        try:
+            otherIdent = getattr(other, 'graphident')
+        except AttributeError:
+            return NotImplemented
+
+        return self.graphident > otherIdent
+
+    def __ge__(self, other):
+        try:
+            otherIdent = getattr(other, 'graphident')
+        except AttributeError:
+            return NotImplemented
+
+        return self.graphident >= otherIdent
+
+
+    def __hash__(self):
+        return hash(self.graphident)
+
+    def infoTuple(self):
+        return (self.identifier,)
+
+    def __repr__(self):
+        return '%s%r' % (type(self).__name__, self.infoTuple())
+
+class Alias(str):
+    pass
+
+class AliasNode(Node):
+    def __init__(self, name, node):
+        super(AliasNode, self).__init__(name)
+        for k in 'identifier', 'packagepath', '_namespace', 'globalnames', 'starimports':
+            setattr(self, k, getattr(node, k, None))
+
+    def infoTuple(self):
+        return (self.graphident, self.identifier)
+
+class BadModule(Node):
+    pass
+
+class ExcludedModule(BadModule):
+    pass
+
+class MissingModule(BadModule):
+    pass
+
+class Script(Node):
+    def __init__(self, filename):
+        super(Script, self).__init__(filename)
+        self.filename = filename
+
+    def infoTuple(self):
+        return (self.filename,)
+
+class BaseModule(Node):
+    def __init__(self, name, filename=None, path=None):
+        super(BaseModule, self).__init__(name)
+        self.filename = filename
+        self.packagepath = path
+
+    def infoTuple(self):
+        return tuple(filter(None, (self.identifier, self.filename, self.packagepath)))
+
+class BuiltinModule(BaseModule):
+    pass
+
+class SourceModule(BaseModule):
+    pass
+
+class InvalidSourceModule(SourceModule):
+    pass
+
+class CompiledModule(BaseModule):
+    pass
+
+class InvalidCompiledModule(BaseModule):
+    pass
+
+class Package(BaseModule):
+    pass
+
+class NamespacePackage(Package):
+    pass
+
+class Extension(BaseModule):
+    pass
+
+class FlatPackage(BaseModule): # nocoverage
+    def __init__(self, *args, **kwds):
+        warnings.warn("This class will be removed in a future version of modulegraph",
+            DeprecationWarning)
+        super(FlatPackage, *args, **kwds)
+
+class ArchiveModule(BaseModule): # nocoverage
+    def __init__(self, *args, **kwds):
+        warnings.warn("This class will be removed in a future version of modulegraph",
+            DeprecationWarning)
+        super(FlatPackage, *args, **kwds)
+
+# HTML templates for ModuleGraph generator
+header = """\
+<html>
+  <head>
+    <title>%(TITLE)s</title>
+    <style>
+      .node { margin:1em 0; }
+    </style>
+  </head>
+  <body>
+    <h1>%(TITLE)s</h1>"""
+entry = """
+<div class="node">
+  <a name="%(NAME)s" />
+  %(CONTENT)s
+</div>"""
+contpl = """<tt>%(NAME)s</tt> %(TYPE)s"""
+contpl_linked = """\
+<a target="code" href="%(URL)s" type="text/plain"><tt>%(NAME)s</tt></a>"""
+imports = """\
+  <div class="import">
+%(HEAD)s:
+  %(LINKS)s
+  </div>
+"""
+footer = """
+  </body>
+</html>"""
+
+def _ast_names(names):
+    result = []
+    for nm in names:
+        if isinstance(nm, ast.alias):
+            result.append(nm.name)
+        else:
+            result.append(nm)
+    return result
+
+
+if sys.version_info[0] == 2:
+    DEFAULT_IMPORT_LEVEL= -1
+else:
+    DEFAULT_IMPORT_LEVEL= 0
+
+class _Visitor (ast.NodeVisitor):
+    def __init__(self, graph, module):
+        self._graph = graph
+        self._module = module
+        self._level = DEFAULT_IMPORT_LEVEL
+        self._in_if = [False]
+        self._in_def = [False]
+        self._in_tryexcept = [False]
+
+    @property
+    def in_if(self):
+        return self._in_if[-1]
+
+    @property
+    def in_def(self):
+        return self._in_def[-1]
+
+    @property
+    def in_tryexcept(self):
+        return self._in_tryexcept[-1]
+
+    def _process_import(self, name, fromlist, level):
+
+        if sys.version_info[0] == 2:
+            if name == '__future__' and 'absolute_import' in (fromlist or ()):
+                self._level = 0
+
+        have_star = False
+        if fromlist is not None:
+            fromlist = set(fromlist)
+            if '*' in fromlist:
+                fromlist.remove('*')
+                have_star = True
+
+        imported_module = self._graph._safe_import_hook(name,
+            self._module, fromlist, level, attr=DependencyInfo(
+                conditional=self.in_if,
+                tryexcept=self.in_tryexcept,
+                function=self.in_def,
+                fromlist=False,
+            ))[0]
+        if have_star:
+            self._module.globalnames.update(imported_module.globalnames)
+            self._module.starimports.update(imported_module.starimports)
+            if imported_module.code is None:
+                self._module.starimports.add(name)
+
+
+    def visit_Import(self, node):
+        for nm in _ast_names(node.names):
+            self._process_import(nm, None, self._level)
+
+    def visit_ImportFrom(self, node):
+        level = node.level if node.level != 0 else self._level
+        self._process_import(node.module or '', _ast_names(node.names), level)
+
+    def visit_If(self, node):
+        self._in_if.append(True)
+        self.generic_visit(node)
+        self._in_if.pop()
+
+    def visit_FunctionDef(self, node):
+        self._in_def.append(True)
+        self.generic_visit(node)
+        self._in_def.pop()
+
+    def visit_Try(self, node):
+        self._in_tryexcept.append(True)
+        self.generic_visit(node)
+        self._in_tryexcept.pop()
+
+    def visit_ExceptHandler(self, node):
+        self._in_tryexcept.append(True)
+        self.generic_visit(node)
+        self._in_tryexcept.pop()
+
+    def visit_TryExcept(self, node):
+        self._in_tryexcept.append(True)
+        self.generic_visit(node)
+        self._in_tryexcept.pop()
+
+    def visit_ExceptHandler(self, node):
+        self._in_tryexcept.append(True)
+        self.generic_visit(node)
+        self._in_tryexcept.pop()
+
+    def visit_Expression(self, node):
+        # Expression node's cannot contain import statements or
+        # other nodes that are relevant for us.
+        pass
+
+    # Expression isn't actually used as such in AST trees,
+    # therefore define visitors for all kinds of expression nodes.
+    visit_BoolOp = visit_Expression
+    visit_BinOp = visit_Expression
+    visit_UnaryOp = visit_Expression
+    visit_Lambda = visit_Expression
+    visit_IfExp = visit_Expression
+    visit_Dict = visit_Expression
+    visit_Set = visit_Expression
+    visit_ListComp = visit_Expression
+    visit_SetComp = visit_Expression
+    visit_ListComp = visit_Expression
+    visit_GeneratorExp = visit_Expression
+    visit_Compare = visit_Expression
+    visit_Yield = visit_Expression
+    visit_YieldFrom = visit_Expression
+    visit_Await = visit_Expression
+    visit_Call = visit_Expression
+
+
+
+class ModuleGraph(ObjectGraph):
+    def __init__(self, path=None, excludes=(), replace_paths=(), implies=(), graph=None, debug=0):
+        super(ModuleGraph, self).__init__(graph=graph, debug=debug)
+        if path is None:
+            path = sys.path
+        self.path = path
+        self.lazynodes = {}
+        # excludes is stronger than implies
+        self.lazynodes.update(dict(implies))
+        for m in excludes:
+            self.lazynodes[m] = None
+        self.replace_paths = replace_paths
+
+        self.nspackages = self._calc_setuptools_nspackages()
+
+    def _calc_setuptools_nspackages(self):
+        # Setuptools has some magic handling for namespace
+        # packages when using 'install --single-version-externally-managed'
+        # (used by system packagers and also by pip)
+        #
+        # When this option is used namespace packages are writting to
+        # disk *without* an __init__.py file, which means the regular
+        # import machinery will not find them.
+        #
+        # We therefore explicitly look for the hack used by
+        # setuptools to get this kind of namespace packages to work.
+
+        pkgmap = {}
+
+        try:
+            from pkgutil import ImpImporter
+        except ImportError:
+            try:
+                from _pkgutil import ImpImporter
+            except ImportError:
+                ImpImporter = pkg_resources.ImpWrapper
+
+        if sys.version_info[:2] >= (3,3):
+            import importlib.machinery
+            ImpImporter = importlib.machinery.FileFinder
+
+        for entry in self.path:
+            importer = pkg_resources.get_importer(entry)
+
+            if isinstance(importer, ImpImporter):
+                try:
+                    ldir = os.listdir(entry)
+                except os.error:
+                    continue
+
+                for fn in ldir:
+                    if fn.endswith('-nspkg.pth'):
+                        fp = open(os.path.join(entry, fn), 'rU')
+                        try:
+                            for ln in fp:
+                                for pfx in _SETUPTOOLS_NAMESPACEPKG_PTHs:
+                                    if ln.startswith(pfx):
+                                        try:
+                                            start = len(pfx)-2
+                                            stop = ln.index(')', start)+1
+                                        except ValueError:
+                                            continue
+
+                                        pkg = _eval_str_tuple(ln[start:stop])
+                                        identifier = ".".join(pkg)
+                                        subdir = os.path.join(entry, *pkg)
+                                        if os.path.exists(os.path.join(subdir, '__init__.py')):
+                                            # There is a real __init__.py, ignore the setuptools hack
+                                            continue
+
+                                        if identifier in pkgmap:
+                                            pkgmap[identifier].append(subdir)
+                                        else:
+                                            pkgmap[identifier] = [subdir]
+                                        break
+                        finally:
+                            fp.close()
+
+        return pkgmap
+
+    def implyNodeReference(self, node, other, edge_data=None):
+        """
+        Imply that one node depends on another.
+        other may be a module name or another node.
+
+        For use by extension modules and tricky import code
+        """
+        if isinstance(other, Node):
+            self._updateReference(node, other, edge_data)
+
+        else:
+            if isinstance(other, tuple):
+                raise ValueError(other)
+
+            others = self._safe_import_hook(other, node, None)
+            for other in others:
+                self._updateReference(node, other, edge_data)
+
+
+    def getReferences(self, fromnode):
+        """
+        Yield all nodes that 'fromnode' dependes on (that is,
+        all modules that 'fromnode' imports.
+        """
+        node = self.findNode(fromnode)
+        out_edges, _ = self.get_edges(node)
+        return out_edges
+
+    def getReferers(self, tonode, collapse_missing_modules=True):
+        node = self.findNode(tonode)
+        _, in_edges = self.get_edges(node)
+
+        if collapse_missing_modules:
+            for n in in_edges:
+                if isinstance(n, MissingModule):
+                    for n in self.getReferers(n, False):
+                        yield n
+
+                else:
+                    yield n
+
+        else:
+            for n in in_edges:
+                yield n
+
+    def hasEdge(self, fromnode, tonode):
+        """ Return True iff there is an edge from 'fromnode' to 'tonode' """
+        fromnode = self.findNode(fromnode)
+        tonode = self.findNode(tonode)
+
+        return self.graph.edge_by_node(fromnode, tonode) is not None
+
+
+    def foldReferences(self, packagenode):
+        """
+        Create edges to/from 'packagenode' based on the
+        edges to/from modules in package. The module nodes
+        are then hidden.
+        """
+        pkg = self.findNode(packagenode)
+
+        for n in self.nodes():
+            if not n.identifier.startswith(pkg.identifier + '.'):
+                continue
+
+            iter_out, iter_inc = n.get_edges()
+            for other in iter_out:
+                if other.identifier.startswith(pkg.identifier + '.'):
+                    continue
+
+                if not self.hasEdge(pkg, other):
+                    # Ignore circular dependencies
+                    self._updateReference(pkg, other, 'pkg-internal-import')
+
+            for other in iter_in:
+                if other.identifier.startswith(pkg.identifier + '.'):
+                    # Ignore circular dependencies
+                    continue
+
+                if not self.hasEdge(other, pkg):
+                    self._updateReference(other, pkg, 'pkg-import')
+
+            self.graph.hide_node(n)
+
+    # TODO: unfoldReferences(pkg) that restore the submodule nodes and
+    #       removes 'pkg-import' and 'pkg-internal-import' edges. Care should
+    #       be taken to ensure that references are correct if multiple packages
+    #       are folded and then one of them in unfolded
+
+
+    def _updateReference(self, fromnode, tonode, edge_data):
+        try:
+            ed = self.edgeData(fromnode, tonode)
+        except (KeyError, GraphError): # XXX: Why 'GraphError'
+            return self.createReference(fromnode, tonode, edge_data)
+
+        if not (isinstance(ed, DependencyInfo) and isinstance(edge_data, DependencyInfo)):
+            self.updateEdgeData(fromnode, tonode, edge_data)
+        else:
+            self.updateEdgeData(fromnode, tonode, ed._merged(edge_data))
+
+
+    def createReference(self, fromnode, tonode, edge_data='direct'):
+        """
+        Create a reference from fromnode to tonode
+        """
+        return super(ModuleGraph, self).createReference(fromnode, tonode, edge_data=edge_data)
+
+    def findNode(self, name):
+        """
+        Find a node by identifier.  If a node by that identifier exists,
+        it will be returned.
+
+        If a lazy node exists by that identifier with no dependencies (excluded),
+        it will be instantiated and returned.
+
+        If a lazy node exists by that identifier with dependencies, it and its
+        dependencies will be instantiated and scanned for additional dependencies.
+        """
+        data = super(ModuleGraph, self).findNode(name)
+        if data is not None:
+            return data
+        if name in self.lazynodes:
+            deps = self.lazynodes.pop(name)
+            if deps is None:
+                # excluded module
+                m = self.createNode(ExcludedModule, name)
+            elif isinstance(deps, Alias):
+                other = self._safe_import_hook(deps, None, None).pop()
+                m = self.createNode(AliasNode, name, other)
+                self.implyNodeReference(m, other)
+
+            else:
+                m = self._safe_import_hook(name, None, None).pop()
+                for dep in deps:
+                    self.implyNodeReference(m, dep)
+            return m
+
+        if name in self.nspackages:
+            # name is a --single-version-externally-managed
+            # namespace package (setuptools/distribute)
+            pathnames = self.nspackages.pop(name)
+            m = self.createNode(NamespacePackage, name)
+
+            # FIXME: The filename must be set to a string to ensure that py2app
+            # works, it is not clear yet why that is. Setting to None would be
+            # cleaner.
+            m.filename = '-'
+            m.packagepath = _namespace_package_path(name, pathnames, self.path)
+
+            # As per comment at top of file, simulate runtime packagepath additions.
+            m.packagepath = m.packagepath + _packagePathMap.get(name, [])
+            return m
+
+        return None
+
+    def run_script(self, pathname, caller=None):
+        """
+        Create a node by path (not module name).  It is expected to be a Python
+        source file, and will be scanned for dependencies.
+        """
+        self.msg(2, "run_script", pathname)
+        pathname = os.path.realpath(pathname)
+        m = self.findNode(pathname)
+        if m is not None:
+            return m
+
+        if sys.version_info[0] != 2:
+            with open(pathname, 'rb') as fp:
+                encoding = util.guess_encoding(fp)
+
+            with open(pathname, _READ_MODE, encoding=encoding) as fp:
+                contents = fp.read() + '\n'
+
+        else:
+            with open(pathname, _READ_MODE) as fp:
+                contents = fp.read() + '\n'
+
+        co = compile(contents, pathname, 'exec', ast.PyCF_ONLY_AST, True)
+        m = self.createNode(Script, pathname)
+        self._updateReference(caller, m, None)
+        self._scan_code(co, m)
+        m.code = compile(co, pathname, 'exec', 0, True)
+        if self.replace_paths:
+            m.code = self._replace_paths_in_code(m.code)
+        return m
+
+    def import_hook(self, name, caller=None, fromlist=None, level=DEFAULT_IMPORT_LEVEL, attr=None):
+        """
+        Import a module
+
+        Return the set of modules that are imported
+        """
+        self.msg(3, "import_hook", name, caller, fromlist, level)
+        parent = self._determine_parent(caller)
+        q, tail = self._find_head_package(parent, name, level)
+        m = self._load_tail(q, tail)
+        modules = [m]
+        if fromlist and m.packagepath:
+            for s in self._ensure_fromlist(m, fromlist):
+                if s not in modules:
+                    modules.append(s)
+        for m in modules:
+            self._updateReference(caller, m, edge_data=attr)
+        return modules
+
+    def _determine_parent(self, caller):
+        """
+        Determine the package containing a node
+        """
+        self.msgin(4, "determine_parent", caller)
+        parent = None
+        if caller:
+            pname = caller.identifier
+
+            if isinstance(caller, Package):
+                parent = caller
+
+            elif '.' in pname:
+                pname = pname[:pname.rfind('.')]
+                parent = self.findNode(pname)
+
+            elif caller.packagepath:
+                # XXX: I have no idea why this line
+                # is necessary.
+                parent = self.findNode(pname)
+
+
+        self.msgout(4, "determine_parent ->", parent)
+        return parent
+
+    def _find_head_package(self, parent, name, level=DEFAULT_IMPORT_LEVEL):
+        """
+        Given a calling parent package and an import name determine the containing
+        package for the name
+        """
+        self.msgin(4, "find_head_package", parent, name, level)
+        if '.' in name:
+            head, tail = name.split('.', 1)
+        else:
+            head, tail = name, ''
+
+        if level == -1:
+            if parent:
+                qname = parent.identifier + '.' + head
+            else:
+                qname = head
+
+        elif level == 0:
+            qname = head
+
+            # Absolute import, ignore the parent
+            parent = None
+
+        else:
+            if parent is None:
+                self.msg(2, "Relative import outside of package")
+                raise ImportError("Relative import outside of package (name=%r, parent=%r, level=%r)"%(name, parent, level))
+
+            for i in range(level-1):
+                if '.' not in parent.identifier:
+                    self.msg(2, "Relative import outside of package")
+                    raise ImportError("Relative import outside of package (name=%r, parent=%r, level=%r)"%(name, parent, level))
+
+                p_fqdn = parent.identifier.rsplit('.', 1)[0]
+                new_parent = self.findNode(p_fqdn)
+                if new_parent is None:
+                    self.msg(2, "Relative import outside of package")
+                    raise ImportError("Relative import outside of package (name=%r, parent=%r, level=%r)"%(name, parent, level))
+
+                assert new_parent is not parent, (new_parent, parent)
+                parent = new_parent
+
+            if head:
+                qname = parent.identifier + '.' + head
+            else:
+                qname = parent.identifier
+
+
+        q = self._import_module(head, qname, parent)
+        if q:
+            self.msgout(4, "find_head_package ->", (q, tail))
+            return q, tail
+        if parent:
+            qname = head
+            parent = None
+            q = self._import_module(head, qname, parent)
+            if q:
+                self.msgout(4, "find_head_package ->", (q, tail))
+                return q, tail
+        self.msgout(4, "raise ImportError: No module named", qname)
+        raise ImportError("No module named " + qname)
+
+    def _load_tail(self, mod, tail):
+        self.msgin(4, "load_tail", mod, tail)
+        result = mod
+        while tail:
+            i = tail.find('.')
+            if i < 0: i = len(tail)
+            head, tail = tail[:i], tail[i+1:]
+            mname = "%s.%s" % (result.identifier, head)
+            result = self._import_module(head, mname, result)
+            if result is None:
+                result = self.createNode(MissingModule, mname)
+                #self.msgout(4, "raise ImportError: No module named", mname)
+                #raise ImportError("No module named " + mname)
+        self.msgout(4, "load_tail ->", result)
+        return result
+
+    def _ensure_fromlist(self, m, fromlist):
+        fromlist = set(fromlist)
+        self.msg(4, "ensure_fromlist", m, fromlist)
+        if '*' in fromlist:
+            fromlist.update(self._find_all_submodules(m))
+            fromlist.remove('*')
+        for sub in fromlist:
+            submod = m.get(sub)
+            if submod is None:
+                if sub in m.globalnames:
+                    # Name is a global in the module
+                    continue
+                # XXX: ^^^ need something simular for names imported
+                #      by 'm'.
+
+                fullname = m.identifier + '.' + sub
+                submod = self._import_module(sub, fullname, m)
+                if submod is None:
+                    raise ImportError("No module named " + fullname)
+            yield submod
+
+    def _find_all_submodules(self, m):
+        if not m.packagepath:
+            return
+        # 'suffixes' used to be a list hardcoded to [".py", ".pyc", ".pyo"].
+        # But we must also collect Python extension modules - although
+        # we cannot separate normal dlls from Python extensions.
+        suffixes = [triple[0] for triple in imp.get_suffixes()]
+        for path in m.packagepath:
+            try:
+                names = zipio.listdir(path)
+            except (os.error, IOError):
+                self.msg(2, "can't list directory", path)
+                continue
+            for info in (moduleInfoForPath(p) for p in names):
+                if info is None: continue
+                if info[0] != '__init__':
+                    yield info[0]
+
+    def _import_module(self, partname, fqname, parent):
+        # XXX: Review me for use with absolute imports.
+        self.msgin(3, "import_module", partname, fqname, parent)
+        m = self.findNode(fqname)
+        if m is not None:
+            self.msgout(3, "import_module ->", m)
+            if parent:
+                self._updateReference(m, parent, edge_data=DependencyInfo(
+                    conditional=False, fromlist=False, function=False, tryexcept=False
+                ))
+            return m
+
+        if parent and parent.packagepath is None:
+            self.msgout(3, "import_module -> None")
+            return None
+
+        try:
+            searchpath = None
+            if parent is not None and parent.packagepath:
+                searchpath = parent.packagepath
+
+            fp, pathname, stuff = self._find_module(partname,
+                searchpath, parent)
+
+        except ImportError:
+            self.msgout(3, "import_module ->", None)
+            return None
+
+        try:
+            m = self._load_module(fqname, fp, pathname, stuff)
+
+        finally:
+            if fp is not None:
+                fp.close()
+
+        if parent:
+            self.msgout(4, "create reference", m, "->", parent)
+            self._updateReference(m, parent, edge_data=DependencyInfo(
+                conditional=False, fromlist=False, function=False, tryexcept=False
+            ))
+            parent[partname] = m
+
+        self.msgout(3, "import_module ->", m)
+        return m
+
+    def _load_module(self, fqname, fp, pathname, info):
+        suffix, mode, typ = info
+        self.msgin(2, "load_module", fqname, fp and "fp", pathname)
+
+        if typ == imp.PKG_DIRECTORY:
+            if isinstance(mode, (list, tuple)):
+                packagepath = mode
+            else:
+                packagepath = []
+
+            m = self._load_package(fqname, pathname, packagepath)
+            self.msgout(2, "load_module ->", m)
+            return m
+
+        if typ == imp.PY_SOURCE:
+            contents = fp.read()
+            if isinstance(contents, bytes):
+                contents += b'\n'
+            else:
+                contents += '\n'
+
+            try:
+                co = compile(contents, pathname, 'exec', ast.PyCF_ONLY_AST, True)
+                #co = compile(contents, pathname, 'exec', 0, True)
+            except SyntaxError:
+                co = None
+                cls = InvalidSourceModule
+
+            else:
+                cls = SourceModule
+
+        elif typ == imp.PY_COMPILED:
+            if fp.read(4) != imp.get_magic():
+                self.msgout(2, "raise ImportError: Bad magic number", pathname)
+                co = None
+                cls = InvalidCompiledModule
+
+            else:
+                fp.read(4)
+                try:
+                    co = marshal.loads(fp.read())
+                    cls = CompiledModule
+                except Exception:
+                    co = None
+                    cls = InvalidCompiledModule
+
+        elif typ == imp.C_BUILTIN:
+            cls = BuiltinModule
+            co = None
+
+        else:
+            cls = Extension
+            co = None
+
+        m = self.createNode(cls, fqname)
+        m.filename = pathname
+        if co is not None:
+            self._scan_code(co, m)
+
+            if isinstance(co, ast.AST):
+                co = compile(co, pathname, 'exec', 0, True)
+            if self.replace_paths:
+                co = self._replace_paths_in_code(co)
+            m.code = co
+
+
+        self.msgout(2, "load_module ->", m)
+        return m
+
+    def _safe_import_hook(self, name, caller, fromlist, level=DEFAULT_IMPORT_LEVEL, attr=None):
+        # wrapper for self.import_hook() that won't raise ImportError
+        try:
+            mods = self.import_hook(name, caller, level=level, attr=attr)
+        except ImportError as msg:
+            self.msg(2, "ImportError:", str(msg))
+            m = self.createNode(MissingModule, _path_from_importerror(msg, name))
+            self._updateReference(caller, m, edge_data=attr)
+
+        else:
+            assert len(mods) == 1
+            m = list(mods)[0]
+
+        subs = [m]
+        if isinstance(attr, DependencyInfo):
+            attr = attr._replace(fromlist=True)
+        for sub in (fromlist or ()):
+            # If this name is in the module namespace already,
+            # then add the entry to the list of substitutions
+            if sub in m:
+                sm = m[sub]
+                if sm is not None:
+                    if sm not in subs:
+                        self._updateReference(caller, sm, edge_data=attr)
+                        subs.append(sm)
+                    continue
+
+            elif sub in m.globalnames:
+                # Global variable in the module, ignore
+                continue
+
+
+            # See if we can load it
+            #    fullname = name + '.' + sub
+            fullname = m.identifier + '.' + sub
+            #else:
+            #    print("XXX", repr(name), repr(sub), repr(caller), repr(m))
+            sm = self.findNode(fullname)
+            if sm is None:
+                try:
+                    sm = self.import_hook(name, caller, fromlist=[sub], level=level, attr=attr)
+                except ImportError as msg:
+                    self.msg(2, "ImportError:", str(msg))
+                    #sm = self.createNode(MissingModule, _path_from_importerror(msg, fullname))
+                    sm = self.createNode(MissingModule, fullname)
+                else:
+                    sm = self.findNode(fullname)
+                    if sm is None:
+                        sm = self.createNode(MissingModule, fullname)
+
+            m[sub] = sm
+            if sm is not None:
+                self._updateReference(m, sm, edge_data=attr)
+                self._updateReference(caller, sm, edge_data=attr)
+                if sm not in subs:
+                    subs.append(sm)
+        return subs
+
+    def _scan_code(self, co, m):
+        if isinstance(co, ast.AST):
+            #return self._scan_bytecode(compile(co, '-', 'exec', 0, True), m)
+            self._scan_ast(co, m)
+            self._scan_bytecode_stores(
+                    compile(co, '-', 'exec', 0, True), m)
+
+        else:
+            self._scan_bytecode(co, m)
+
+    def _scan_ast(self, co, m):
+        visitor = _Visitor(self, m)
+        visitor.visit(co)
+
+    def _scan_bytecode_stores(self, co, m,
+            STORE_NAME=_Bchr(dis.opname.index('STORE_NAME')),
+            STORE_GLOBAL=_Bchr(dis.opname.index('STORE_GLOBAL')),
+            HAVE_ARGUMENT=_Bchr(dis.HAVE_ARGUMENT),
+            unpack=struct.unpack):
+
+        extended_import = bool(sys.version_info[:2] >= (2,5))
+
+        code = co.co_code
+        constants = co.co_consts
+        n = len(code)
+        i = 0
+
+        while i < n:
+            c = code[i]
+            i += 1
+            if c >= HAVE_ARGUMENT:
+                i = i+2
+
+            if c == STORE_NAME or c == STORE_GLOBAL:
+                # keep track of all global names that are assigned to
+                oparg = unpack('<H', code[i - 2:i])[0]
+                name = co.co_names[oparg]
+                m.globalnames.add(name)
+
+        cotype = type(co)
+        for c in constants:
+            if isinstance(c, cotype):
+                self._scan_bytecode_stores(c, m)
+
+    def _scan_bytecode(self, co, m,
+            HAVE_ARGUMENT=_Bchr(dis.HAVE_ARGUMENT),
+            LOAD_CONST=_Bchr(dis.opname.index('LOAD_CONST')),
+            IMPORT_NAME=_Bchr(dis.opname.index('IMPORT_NAME')),
+            IMPORT_FROM=_Bchr(dis.opname.index('IMPORT_FROM')),
+            STORE_NAME=_Bchr(dis.opname.index('STORE_NAME')),
+            STORE_GLOBAL=_Bchr(dis.opname.index('STORE_GLOBAL')),
+            unpack=struct.unpack):
+
+        # Python >=2.5: LOAD_CONST flags, LOAD_CONST names, IMPORT_NAME name
+        # Python < 2.5: LOAD_CONST names, IMPORT_NAME name
+        extended_import = bool(sys.version_info[:2] >= (2,5))
+
+        code = co.co_code
+        constants = co.co_consts
+        n = len(code)
+        i = 0
+
+        level = None
+        fromlist = None
+
+        while i < n:
+            c = code[i]
+            i += 1
+            if c >= HAVE_ARGUMENT:
+                i = i+2
+
+            if c == IMPORT_NAME:
+                if extended_import:
+                    assert code[i-9] == LOAD_CONST
+                    assert code[i-6] == LOAD_CONST
+                    arg1, arg2 = unpack('<xHxH', code[i-9:i-3])
+                    level = co.co_consts[arg1]
+                    fromlist = co.co_consts[arg2]
+                else:
+                    assert code[-6] == LOAD_CONST
+                    arg1, = unpack('<xH', code[i-6:i-3])
+                    level = -1
+                    fromlist = co.co_consts[arg1]
+
+                assert fromlist is None or type(fromlist) is tuple
+                oparg, = unpack('<H', code[i - 2:i])
+                name = co.co_names[oparg]
+                have_star = False
+                if fromlist is not None:
+                    fromlist = set(fromlist)
+                    if '*' in fromlist:
+                        fromlist.remove('*')
+                        have_star = True
+
+                #self.msgin(2, "Before import hook", repr(name), repr(m), repr(fromlist), repr(level))
+
+                imported_module = self._safe_import_hook(name, m, fromlist, level)[0]
+
+                if have_star:
+                    m.globalnames.update(imported_module.globalnames)
+                    m.starimports.update(imported_module.starimports)
+                    if imported_module.code is None:
+                        m.starimports.add(name)
+
+            elif c == STORE_NAME or c == STORE_GLOBAL:
+                # keep track of all global names that are assigned to
+                oparg = unpack('<H', code[i - 2:i])[0]
+                name = co.co_names[oparg]
+                m.globalnames.add(name)
+
+        cotype = type(co)
+        for c in constants:
+            if isinstance(c, cotype):
+                self._scan_bytecode(c, m)
+
+    def _load_package(self, fqname, pathname, pkgpath):
+        """
+        Called only when an imp.PACKAGE_DIRECTORY is found
+        """
+        self.msgin(2, "load_package", fqname, pathname, pkgpath)
+        newname = _replacePackageMap.get(fqname)
+        if newname:
+            fqname = newname
+
+        ns_pkgpath = _namespace_package_path(fqname, pkgpath or [], self.path)
+        if ns_pkgpath or pkgpath:
+            # this is a namespace package
+            m = self.createNode(NamespacePackage, fqname)
+            m.filename = '-'
+            m.packagepath = ns_pkgpath
+        else:
+            m = self.createNode(Package, fqname)
+            m.filename = pathname
+            m.packagepath = [pathname] + ns_pkgpath
+
+        # As per comment at top of file, simulate runtime packagepath additions.
+        m.packagepath = m.packagepath + _packagePathMap.get(fqname, [])
+
+
+
+        try:
+            self.msg(2, "find __init__ for %s"%(m.packagepath,))
+            fp, buf, stuff = self._find_module("__init__", m.packagepath, parent=m)
+        except ImportError:
+            pass
+
+        else:
+            try:
+                self.msg(2, "load __init__ for %s"%(m.packagepath,))
+                self._load_module(fqname, fp, buf, stuff)
+            finally:
+                if fp is not None:
+                    fp.close()
+        self.msgout(2, "load_package ->", m)
+        return m
+
+    def _find_module(self, name, path, parent=None):
+        if parent is not None:
+            # assert path is not None
+            fullname = parent.identifier + '.' + name
+        else:
+            fullname = name
+
+        node = self.findNode(fullname)
+        if node is not None:
+            self.msgout(3, "find_module -> already included?", node)
+            raise ImportError(name)
+
+        if path is None:
+            if name in sys.builtin_module_names:
+                return (None, None, ("", "", imp.C_BUILTIN))
+
+            path = self.path
+
+        fp, buf, stuff = find_module(name, path)
+        try:
+            if buf:
+                buf = os.path.realpath(buf)
+
+            return (fp, buf, stuff)
+        except:
+            fp.close()
+            raise
+
+    def create_xref(self, out=None):
+        global header, footer, entry, contpl, contpl_linked, imports
+        if out is None:
+            out = sys.stdout
+        scripts = []
+        mods = []
+        for mod in self.flatten():
+            name = os.path.basename(mod.identifier)
+            if isinstance(mod, Script):
+                scripts.append((name, mod))
+            else:
+                mods.append((name, mod))
+        scripts.sort()
+        mods.sort()
+        scriptnames = [name for name, m in scripts]
+        scripts.extend(mods)
+        mods = scripts
+
+        title = "modulegraph cross reference for "  + ', '.join(scriptnames)
+        print(header % {"TITLE": title}, file=out)
+
+        def sorted_namelist(mods):
+            lst = [os.path.basename(mod.identifier) for mod in mods if mod]
+            lst.sort()
+            return lst
+        for name, m in mods:
+            content = ""
+            if isinstance(m, BuiltinModule):
+                content = contpl % {"NAME": name,
+                                    "TYPE": "<i>(builtin module)</i>"}
+            elif isinstance(m, Extension):
+                content = contpl % {"NAME": name,\
+                                    "TYPE": "<tt>%s</tt>" % m.filename}
+            else:
+                url = pathname2url(m.filename or "")
+                content = contpl_linked % {"NAME": name, "URL": url}
+            oute, ince = map(sorted_namelist, self.get_edges(m))
+            if oute:
+                links = ""
+                for n in oute:
+                    links += """  <a href="#%s">%s</a>\n""" % (n, n)
+                content += imports % {"HEAD": "imports", "LINKS": links}
+            if ince:
+                links = ""
+                for n in ince:
+                    links += """  <a href="#%s">%s</a>\n""" % (n, n)
+                content += imports % {"HEAD": "imported by", "LINKS": links}
+            print(entry % {"NAME": name,"CONTENT": content}, file=out)
+        print(footer, file=out)
+
+
+    def itergraphreport(self, name='G', flatpackages=()):
+        # XXX: Can this be implemented using Dot()?
+        nodes = map(self.graph.describe_node, self.graph.iterdfs(self))
+        describe_edge = self.graph.describe_edge
+        edges = deque()
+        packagenodes = set()
+        packageidents = {}
+        nodetoident = {}
+        inpackages = {}
+        mainedges = set()
+
+        # XXX - implement
+        flatpackages = dict(flatpackages)
+
+        def nodevisitor(node, data, outgoing, incoming):
+            if not isinstance(data, Node):
+                return {'label': str(node)}
+            #if isinstance(d, (ExcludedModule, MissingModule, BadModule)):
+            #    return None
+            s = '<f0> ' + type(data).__name__
+            for i,v in enumerate(data.infoTuple()[:1], 1):
+                s += '| <f%d> %s' % (i,v)
+            return {'label':s, 'shape':'record'}
+
+
+        def edgevisitor(edge, data, head, tail):
+            # XXX: This method nonsense, the edge
+            # data is never initialized.
+            if data == 'orphan':
+                return {'style':'dashed'}
+            elif data == 'pkgref':
+                return {'style':'dotted'}
+            return {}
+
+        yield 'digraph %s {\n' % (name,)
+        attr = dict(rankdir='LR', concentrate='true')
+        cpatt  = '%s="%s"'
+        for item in attr.items():
+            yield '\t%s;\n' % (cpatt % item,)
+
+        # find all packages (subgraphs)
+        for (node, data, outgoing, incoming) in nodes:
+            nodetoident[node] = getattr(data, 'identifier', None)
+            if isinstance(data, Package):
+                packageidents[data.identifier] = node
+                inpackages[node] = set([node])
+                packagenodes.add(node)
+
+
+        # create sets for subgraph, write out descriptions
+        for (node, data, outgoing, incoming) in nodes:
+            # update edges
+            for edge in (describe_edge(e) for e in outgoing):
+                edges.append(edge)
+
+            # describe node
+            yield '\t"%s" [%s];\n' % (
+                node,
+                ','.join([
+                    (cpatt % item) for item in
+                    nodevisitor(node, data, outgoing, incoming).items()
+                ]),
+            )
+
+            inside = inpackages.get(node)
+            if inside is None:
+                inside = inpackages[node] = set()
+            ident = nodetoident[node]
+            if ident is None:
+                continue
+            pkgnode = packageidents.get(ident[:ident.rfind('.')])
+            if pkgnode is not None:
+                inside.add(pkgnode)
+
+
+        graph = []
+        subgraphs = {}
+        for key in packagenodes:
+            subgraphs[key] = []
+
+        while edges:
+            edge, data, head, tail = edges.popleft()
+            if ((head, tail)) in mainedges:
+                continue
+            mainedges.add((head, tail))
+            tailpkgs = inpackages[tail]
+            common = inpackages[head] & tailpkgs
+            if not common and tailpkgs:
+                usepkgs = sorted(tailpkgs)
+                if len(usepkgs) != 1 or usepkgs[0] != tail:
+                    edges.append((edge, data, head, usepkgs[0]))
+                    edges.append((edge, 'pkgref', usepkgs[-1], tail))
+                    continue
+            if common:
+                common = common.pop()
+                if tail == common:
+                    edges.append((edge, data, tail, head))
+                elif head == common:
+                    subgraphs[common].append((edge, 'pkgref', head, tail))
+                else:
+                    edges.append((edge, data, common, head))
+                    edges.append((edge, data, common, tail))
+
+            else:
+                graph.append((edge, data, head, tail))
+
+        def do_graph(edges, tabs):
+            edgestr = tabs + '"%s" -> "%s" [%s];\n'
+            # describe edge
+            for (edge, data, head, tail) in edges:
+                attribs = edgevisitor(edge, data, head, tail)
+                yield edgestr % (
+                    head,
+                    tail,
+                    ','.join([(cpatt % item) for item in attribs.items()]),
+                )
+
+        for g, edges in subgraphs.items():
+            yield '\tsubgraph "cluster_%s" {\n' % (g,)
+            yield '\t\tlabel="%s";\n' % (nodetoident[g],)
+            for s in do_graph(edges, '\t\t'):
+                yield s
+            yield '\t}\n'
+
+        for s in do_graph(graph, '\t'):
+            yield s
+
+        yield '}\n'
+
+
+    def graphreport(self, fileobj=None, flatpackages=()):
+        if fileobj is None:
+            fileobj = sys.stdout
+        fileobj.writelines(self.itergraphreport(flatpackages=flatpackages))
+
+    def report(self):
+        """Print a report to stdout, listing the found modules with their
+        paths, as well as modules that are missing, or seem to be missing.
+        """
+        print()
+        print("%-15s %-25s %s" % ("Class", "Name", "File"))
+        print("%-15s %-25s %s" % ("-----", "----", "----"))
+        # Print modules found
+        sorted = [(os.path.basename(mod.identifier), mod) for mod in self.flatten()]
+        sorted.sort()
+        for (name, m) in sorted:
+            print("%-15s %-25s %s" % (type(m).__name__, name, m.filename or ""))
+
+    def _replace_paths_in_code(self, co):
+        new_filename = original_filename = os.path.normpath(co.co_filename)
+        for f, r in self.replace_paths:
+            f = os.path.join(f, '')
+            r = os.path.join(r, '')
+            if original_filename.startswith(f):
+                new_filename = r + original_filename[len(f):]
+                break
+
+        else:
+            return co
+
+        consts = list(co.co_consts)
+        for i in range(len(consts)):
+            if isinstance(consts[i], type(co)):
+                consts[i] = self._replace_paths_in_code(consts[i])
+
+        code_func = type(co)
+
+        if hasattr(co, 'co_kwonlyargcount'):
+            return code_func(co.co_argcount, co.co_kwonlyargcount, co.co_nlocals, co.co_stacksize,
+                         co.co_flags, co.co_code, tuple(consts), co.co_names,
+                         co.co_varnames, new_filename, co.co_name,
+                         co.co_firstlineno, co.co_lnotab,
+                         co.co_freevars, co.co_cellvars)
+        else:
+            return code_func(co.co_argcount, co.co_nlocals, co.co_stacksize,
+                         co.co_flags, co.co_code, tuple(consts), co.co_names,
+                         co.co_varnames, new_filename, co.co_name,
+                         co.co_firstlineno, co.co_lnotab,
+                         co.co_freevars, co.co_cellvars)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph/util.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph/util.py
new file mode 100644
index 0000000..acf6bc1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph/util.py
@@ -0,0 +1,119 @@
+from __future__ import absolute_import
+
+import os
+import imp
+import sys
+import re
+import marshal
+import warnings
+
+try:
+    unicode
+except NameError:
+    unicode = str
+
+
+if sys.version_info[0] == 2:
+    from StringIO import StringIO as BytesIO
+    from StringIO import StringIO
+
+else:
+    from io import BytesIO, StringIO
+
+
+
+def imp_find_module(name, path=None):
+    """
+    same as imp.find_module, but handles dotted names
+    """
+    names = name.split('.')
+    if path is not None:
+        if isinstance(path, (str, unicode)):
+            path = [os.path.realpath(path)]
+    for name in names:
+        result = imp.find_module(name, path)
+        if result[0] is not None:
+            result[0].close()
+        path = [result[1]]
+    return result
+
+def _check_importer_for_path(name, path_item):
+    try:
+        importer = sys.path_importer_cache[path_item]
+    except KeyError:
+        for path_hook in sys.path_hooks:
+            try:
+                importer = path_hook(path_item)
+                break
+            except ImportError:
+                pass
+        else:
+            importer = None
+        sys.path_importer_cache.setdefault(path_item, importer)
+
+
+    if importer is None:
+        try:
+            return imp.find_module(name, [path_item])
+        except ImportError:
+            return None
+    return importer.find_module(name)
+
+def imp_walk(name):
+    """
+    yields namepart, tuple_or_importer for each path item
+
+    raise ImportError if a name can not be found.
+    """
+    warnings.warn("imp_walk will be removed in a future version", DeprecationWarning)
+
+    if name in sys.builtin_module_names:
+        yield name, (None, None, ("", "", imp.C_BUILTIN))
+        return
+    paths = sys.path
+    res = None
+    for namepart in name.split('.'):
+        for path_item in paths:
+            res = _check_importer_for_path(namepart, path_item)
+            if hasattr(res, 'load_module'):
+                if res.path.endswith('.py') or res.path.endswith('.pyw'):
+                    fp = StringIO(res.get_source(namepart))
+                    res = (fp, res.path, ('.py', 'rU', imp.PY_SOURCE))
+                elif res.path.endswith('.pyc') or res.path.endswith('.pyo'):
+                    co  = res.get_code(namepart)
+                    fp = BytesIO(imp.get_magic() + b'\0\0\0\0' + marshal.dumps(co))
+                    res = (fp, res.path, ('.pyc', 'rb', imp.PY_COMPILED))
+
+                else:
+                    res = (None, loader.path, (os.path.splitext(loader.path)[-1], 'rb', imp.C_EXTENSION))
+
+                break
+            elif isinstance(res, tuple):
+                break
+        else:
+            break
+
+        yield namepart, res
+        paths = [os.path.join(path_item, namepart)]
+    else:
+        return
+
+    raise ImportError('No module named %s' % (name,))
+
+
+cookie_re = re.compile(b"coding[:=]\s*([-\w.]+)")
+if sys.version_info[0] == 2:
+    default_encoding = 'ascii'
+else:
+    default_encoding = 'utf-8'
+
+def guess_encoding(fp):
+
+    for i in range(2):
+        ln = fp.readline()
+
+        m = cookie_re.search(ln)
+        if m is not None:
+            return m.group(1).decode('ascii')
+
+    return default_encoding
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph/zipio.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph/zipio.py
new file mode 100644
index 0000000..34d580e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph/zipio.py
@@ -0,0 +1,426 @@
+"""
+A helper module that can work with paths
+that can refer to data inside a zipfile
+
+XXX: Need to determine if isdir("zipfile.zip")
+should return True or False. Currently returns
+True, but that might do the wrong thing with
+data-files that are zipfiles.
+"""
+import os as _os
+import zipfile as _zipfile
+import errno as _errno
+import time as _time
+import sys as _sys
+import stat as _stat
+
+_DFLT_DIR_MODE = (
+      _stat.S_IFDIR
+    | _stat.S_IXOTH
+    | _stat.S_IXGRP
+    | _stat.S_IXUSR
+    | _stat.S_IROTH
+    | _stat.S_IRGRP
+    | _stat.S_IRUSR)
+
+_DFLT_FILE_MODE = (
+      _stat.S_IFREG
+    | _stat.S_IROTH
+    | _stat.S_IRGRP
+    | _stat.S_IRUSR)
+
+
+if _sys.version_info[0] == 2:
+    from  StringIO import StringIO as _BaseStringIO
+    from  StringIO import StringIO as _BaseBytesIO
+
+    class _StringIO (_BaseStringIO):
+        def __enter__(self):
+            return self
+
+        def __exit__(self, exc_type, exc_value, traceback):
+            self.close()
+            return False
+
+    class _BytesIO (_BaseBytesIO):
+        def __enter__(self):
+            return self
+
+        def __exit__(self, exc_type, exc_value, traceback):
+            self.close()
+            return False
+
+else:
+    from io import StringIO as _StringIO
+    from io import BytesIO as _BytesIO
+
+
+
+
+def _locate(path):
+    full_path = path
+    if _os.path.exists(path):
+        return path, None
+
+    else:
+        rest = []
+        root = _os.path.splitdrive(path)
+        while path and path != root:
+            path, bn = _os.path.split(path)
+            rest.append(bn)
+            if _os.path.exists(path):
+                break
+
+        if path == root:
+            raise IOError(
+                _errno.ENOENT, full_path,
+                "No such file or directory")
+
+        if not _os.path.isfile(path):
+            raise IOError(
+                _errno.ENOENT, full_path,
+                "No such file or directory")
+
+        rest.reverse()
+        return path, '/'.join(rest).strip('/')
+
+_open = open
+def open(path, mode='r'):
+    if 'w' in mode or 'a' in mode:
+        raise IOError(
+            _errno.EINVAL, path, "Write access not supported")
+    elif 'r+' in mode:
+        raise IOError(
+            _errno.EINVAL, path, "Write access not supported")
+
+    full_path = path
+    path, rest = _locate(path)
+    if not rest:
+        return _open(path, mode)
+
+    else:
+        try:
+            zf = _zipfile.ZipFile(path, 'r')
+
+        except _zipfile.error:
+            raise IOError(
+                _errno.ENOENT, full_path,
+                "No such file or directory")
+
+        try:
+            data = zf.read(rest)
+        except (_zipfile.error, KeyError):
+            zf.close()
+            raise IOError(
+                _errno.ENOENT, full_path,
+                "No such file or directory")
+        zf.close()
+
+        if mode == 'rb':
+            return _BytesIO(data)
+
+        else:
+            if _sys.version_info[0] == 3:
+                data = data.decode('ascii')
+
+            return _StringIO(data)
+
+def listdir(path):
+    full_path = path
+    path, rest = _locate(path)
+    if not rest and not _os.path.isfile(path):
+        return _os.listdir(path)
+
+    else:
+        try:
+            zf = _zipfile.ZipFile(path, 'r')
+
+        except _zipfile.error:
+            raise IOError(
+                _errno.ENOENT, full_path,
+                "No such file or directory")
+
+        result = set()
+        seen = False
+        try:
+            for nm in zf.namelist():
+                if rest is None:
+                    seen = True
+                    value = nm.split('/')[0]
+                    if value:
+                        result.add(value)
+
+                elif nm.startswith(rest):
+                    if nm == rest:
+                        seen = True
+                        value = ''
+                        pass
+                    elif nm[len(rest)] == '/':
+                        seen = True
+                        value = nm[len(rest)+1:].split('/')[0]
+                    else:
+                        value = None
+
+                    if value:
+                        result.add(value)
+        except _zipfile.error:
+            zf.close()
+            raise IOError(
+                _errno.ENOENT, full_path,
+                "No such file or directory")
+
+        zf.close()
+
+        if not seen:
+            raise IOError(
+                _errno.ENOENT, full_path,
+                "No such file or directory")
+
+        return list(result)
+
+def isfile(path):
+    full_path = path
+    path, rest = _locate(path)
+    if not rest:
+        ok =  _os.path.isfile(path)
+        if ok:
+            try:
+                zf = _zipfile.ZipFile(path, 'r')
+                return False
+            except (_zipfile.error, IOError):
+                return True
+        return False
+
+    zf = None
+    try:
+        zf = _zipfile.ZipFile(path, 'r')
+        info = zf.getinfo(rest)
+        zf.close()
+        return True
+    except (KeyError, _zipfile.error):
+        if zf is not None:
+            zf.close()
+
+        # Check if this is a directory
+        try:
+            info = zf.getinfo(rest + '/')
+        except KeyError:
+            pass
+        else:
+            return False
+
+        rest = rest + '/'
+        for nm in zf.namelist():
+            if nm.startswith(rest):
+                # Directory
+                return False
+
+        # No trace in zipfile
+        raise IOError(
+            _errno.ENOENT, full_path,
+            "No such file or directory")
+
+
+
+
+def isdir(path):
+    full_path = path
+    path, rest = _locate(path)
+    if not rest:
+        ok =  _os.path.isdir(path)
+        if not ok:
+            try:
+                zf = _zipfile.ZipFile(path, 'r')
+            except (_zipfile.error, IOError):
+                return False
+            return True
+        return True
+
+    zf = None
+    try:
+        try:
+            zf = _zipfile.ZipFile(path)
+        except _zipfile.error:
+            raise IOError(
+                _errno.ENOENT, full_path,
+                "No such file or directory")
+
+        try:
+            info = zf.getinfo(rest)
+        except KeyError:
+            pass
+        else:
+            # File found
+            return False
+
+        rest = rest + '/'
+        try:
+            info = zf.getinfo(rest)
+        except KeyError:
+            pass
+        else:
+            # Directory entry found
+            return True
+
+        for nm in zf.namelist():
+            if nm.startswith(rest):
+                return True
+
+        raise IOError(
+            _errno.ENOENT, full_path,
+            "No such file or directory")
+    finally:
+        if zf is not None:
+            zf.close()
+
+
+def islink(path):
+    full_path = path
+    path, rest = _locate(path)
+    if not rest:
+        return _os.path.islink(path)
+
+    try:
+        zf = _zipfile.ZipFile(path)
+    except _zipfile.error:
+        raise IOError(
+            _errno.ENOENT, full_path,
+            "No such file or directory")
+    try:
+
+
+        try:
+            info = zf.getinfo(rest)
+        except KeyError:
+            pass
+        else:
+            # File
+            return False
+
+        rest += '/'
+        try:
+            info = zf.getinfo(rest)
+        except KeyError:
+            pass
+        else:
+            # Directory
+            return False
+
+        for nm in zf.namelist():
+            if nm.startswith(rest):
+                # Directory without listing
+                return False
+
+        raise IOError(
+            _errno.ENOENT, full_path,
+            "No such file or directory")
+
+    finally:
+        zf.close()
+
+
+def readlink(path):
+    full_path = path
+    path, rest = _locate(path)
+    if rest:
+        # No symlinks inside zipfiles
+        raise OSError(
+            _errno.ENOENT, full_path,
+            "No such file or directory")
+
+    return _os.readlink(path)
+
+def getmode(path):
+    full_path = path
+    path, rest = _locate(path)
+    if not rest:
+        return _os.stat(path).st_mode
+
+    zf = None
+    try:
+        zf = _zipfile.ZipFile(path)
+        info = None
+
+        try:
+            info = zf.getinfo(rest)
+        except KeyError:
+            pass
+
+        if info is None:
+            try:
+                info = zf.getinfo(rest + '/')
+            except KeyError:
+                pass
+
+        if info is None:
+            rest = rest + '/'
+            for nm in zf.namelist():
+                if nm.startswith(rest):
+                    break
+            else:
+                raise IOError(
+                    _errno.ENOENT, full_path,
+                    "No such file or directory")
+
+            # Directory exists, but has no entry of its own.
+            return _DFLT_DIR_MODE
+
+        # The mode is stored without file-type in external_attr.
+        if (info.external_attr >> 16) != 0:
+            return _stat.S_IFREG | (info.external_attr >> 16)
+        else:
+            return _DFLT_FILE_MODE
+
+
+    except KeyError:
+        if zf is not None:
+            zf.close()
+        raise IOError(
+            _errno.ENOENT, full_path,
+            "No such file or directory")
+
+def getmtime(path):
+    full_path = path
+    path, rest = _locate(path)
+    if not rest:
+        return _os.path.getmtime(path)
+
+    zf = None
+    try:
+        zf = _zipfile.ZipFile(path)
+        info = None
+
+        try:
+            info = zf.getinfo(rest)
+        except KeyError:
+            pass
+
+        if info is None:
+            try:
+                info = zf.getinfo(rest + '/')
+            except KeyError:
+                pass
+
+        if info is None:
+            rest = rest + '/'
+            for nm in zf.namelist():
+                if nm.startswith(rest):
+                    break
+            else:
+                raise IOError(
+                    _errno.ENOENT, full_path,
+                    "No such file or directory")
+
+            # Directory exists, but has no entry of its
+            # own, fake mtime by using the timestamp of
+            # the zipfile itself.
+            return _os.path.getmtime(path)
+
+        return _time.mktime(info.date_time + (0, 0, -1))
+
+    except KeyError:
+        if zf is not None:
+            zf.close()
+        raise IOError(
+            _errno.ENOENT, full_path,
+            "No such file or directory")
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/__init__.py
new file mode 100644
index 0000000..3e9f9ed
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/__init__.py
@@ -0,0 +1 @@
+""" modulegraph tests """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/test_basic.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/test_basic.py
new file mode 100644
index 0000000..387fde9
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/test_basic.py
@@ -0,0 +1,43 @@
+import unittest
+
+import os, shutil
+
+from modulegraph import modulegraph
+
+class DummyModule(object):
+    packagepath = None
+    def __init__(self, ppath):
+        self.packagepath = ppath
+
+class FindAllSubmodulesTestCase(unittest.TestCase):
+    def testNone(self):
+        mg = modulegraph.ModuleGraph()
+        # empty packagepath
+        m = DummyModule(None)
+        sub_ms = []
+        for sm in mg._find_all_submodules(m):
+            sub_ms.append(sm)
+        self.assertEqual(sub_ms, [])
+
+    def testSimple(self):
+        mg = modulegraph.ModuleGraph()
+        # a string does not break anything although it is split into its characters
+        # BUG: "/hi/there" will read "/"
+        m = DummyModule("xyz")
+        sub_ms = []
+        for sm in mg._find_all_submodules(m):
+            sub_ms.append(sm)
+        self.assertEqual(sub_ms, [])
+
+    def testSlashes(self):
+        # a string does not break anything although it is split into its characters
+        # BUG: "/xyz" will read "/" so this one already triggers missing itertools
+        mg = modulegraph.ModuleGraph()
+        m = DummyModule("/xyz")
+        sub_ms = []
+        for sm in mg._find_all_submodules(m):
+            sub_ms.append(sm)
+        self.assertEqual(sub_ms, [])
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/test_edge_data.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/test_edge_data.py
new file mode 100644
index 0000000..0760894
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/test_edge_data.py
@@ -0,0 +1,417 @@
+import os
+import sys
+if sys.version_info[:2] <= (2,6):
+    import unittest2 as unittest
+else:
+    import unittest
+
+from modulegraph import modulegraph
+
+
+# XXX: Todo: simular tests with bytecompiled modules
+
+
+class TestEdgeData (unittest.TestCase):
+    if not hasattr(unittest.TestCase, 'assertIsInstance'):
+        def assertIsInstance(self, value, types):
+            if not isinstance(value, types):
+                self.fail("%r is not an instance of %r"%(value, types))
+
+    def test_regular_import(self):
+        root = os.path.join(
+                os.path.dirname(os.path.abspath(__file__)),
+                'testpkg-edgedata')
+        mf = modulegraph.ModuleGraph(path=[ root ] + sys.path)
+        script_name = os.path.join(root, 'script.py')
+        mf.run_script(script_name)
+
+        script_node = mf.findNode(script_name)
+        self.assertIsInstance(script_node, modulegraph.Script)
+
+
+        node = mf.findNode('toplevel_existing')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=False, tryexcept=False, fromlist=False))
+
+        node = mf.findNode('toplevel_nonexisting')
+        self.assertIsInstance(node, modulegraph.MissingModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=False, tryexcept=False, fromlist=False))
+
+        node = mf.findNode('toplevel_class_existing')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=False, tryexcept=False, fromlist=False))
+
+        node = mf.findNode('toplevel_class_nonexisting')
+        self.assertIsInstance(node, modulegraph.MissingModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=False, tryexcept=False, fromlist=False))
+
+        node = mf.findNode('toplevel_conditional_existing')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=False, tryexcept=False, fromlist=False))
+
+        node = mf.findNode('toplevel_conditional_nonexisting')
+        self.assertIsInstance(node, modulegraph.MissingModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=False, tryexcept=False, fromlist=False))
+
+        node = mf.findNode('toplevel_conditional_import_existing')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=False, tryexcept=True, fromlist=False))
+
+        node = mf.findNode('toplevel_conditional_import_nonexisting')
+        self.assertIsInstance(node, modulegraph.MissingModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=False, tryexcept=True, fromlist=False))
+
+        node = mf.findNode('toplevel_conditional_import2_existing')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=False, tryexcept=True, fromlist=False))
+
+        node = mf.findNode('toplevel_conditional_import2_nonexisting')
+        self.assertIsInstance(node, modulegraph.MissingModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=False, tryexcept=True, fromlist=False))
+
+        node = mf.findNode('toplevel_import_existing')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=False, tryexcept=True, fromlist=False))
+
+        node = mf.findNode('toplevel_import_nonexisting')
+        self.assertIsInstance(node, modulegraph.MissingModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=False, tryexcept=True, fromlist=False))
+
+        node = mf.findNode('toplevel_import2_existing')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=False, tryexcept=True, fromlist=False))
+
+        node = mf.findNode('toplevel_import2_nonexisting')
+        self.assertIsInstance(node, modulegraph.MissingModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=False, tryexcept=True, fromlist=False))
+
+        node = mf.findNode('function_existing')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=True, tryexcept=False, fromlist=False))
+
+        node = mf.findNode('function_nonexisting')
+        self.assertIsInstance(node, modulegraph.MissingModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=True, tryexcept=False, fromlist=False))
+
+        node = mf.findNode('function_class_existing')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=True, tryexcept=False, fromlist=False))
+
+        node = mf.findNode('function_class_nonexisting')
+        self.assertIsInstance(node, modulegraph.MissingModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=True, tryexcept=False, fromlist=False))
+
+        node = mf.findNode('function_conditional_existing')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=True, tryexcept=False, fromlist=False))
+
+        node = mf.findNode('function_conditional_nonexisting')
+        self.assertIsInstance(node, modulegraph.MissingModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=True, tryexcept=False, fromlist=False))
+
+        node = mf.findNode('function_conditional_import_existing')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=True, tryexcept=True, fromlist=False))
+
+        node = mf.findNode('function_conditional_import_nonexisting')
+        self.assertIsInstance(node, modulegraph.MissingModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=True, tryexcept=True, fromlist=False))
+
+        node = mf.findNode('function_conditional_import2_existing')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=True, tryexcept=True, fromlist=False))
+
+        node = mf.findNode('function_conditional_import2_nonexisting')
+        self.assertIsInstance(node, modulegraph.MissingModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=True, tryexcept=True, fromlist=False))
+
+        node = mf.findNode('function_import_existing')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=True, tryexcept=True, fromlist=False))
+
+        node = mf.findNode('function_import_nonexisting')
+        self.assertIsInstance(node, modulegraph.MissingModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=True, tryexcept=True, fromlist=False))
+
+        node = mf.findNode('function_import2_existing')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=True, tryexcept=True, fromlist=False))
+
+        node = mf.findNode('function_import2_nonexisting')
+        self.assertIsInstance(node, modulegraph.MissingModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=True, tryexcept=True, fromlist=False))
+
+
+    def test_multi_import(self):
+        root = os.path.join(
+                os.path.dirname(os.path.abspath(__file__)),
+                'testpkg-edgedata')
+        mf = modulegraph.ModuleGraph(path=[ root ] + sys.path)
+        script_name = os.path.join(root, 'script_multi_import.py')
+        mf.run_script(script_name)
+
+        script_node = mf.findNode(script_name)
+        self.assertIsInstance(script_node, modulegraph.Script)
+
+
+        node = mf.findNode('os.path')
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=False, tryexcept=True, fromlist=False))
+
+        node = mf.findNode('os')
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=False, tryexcept=False, fromlist=False))
+
+        node = mf.findNode('sys')
+        ed = mf.edgeData(script_node, node)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=True, tryexcept=False, fromlist=False))
+
+        node = mf.findNode('platform')
+        ed = mf.edgeData(script_node, node)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=True, tryexcept=False, fromlist=False))
+
+        node = mf.findNode('email')
+        ed = mf.edgeData(script_node, node)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=False, tryexcept=False, fromlist=False))
+
+    def test_from_imports(self):
+        root = os.path.join(
+                os.path.dirname(os.path.abspath(__file__)),
+                'testpkg-edgedata')
+        mf = modulegraph.ModuleGraph(path=[ root ] + sys.path)
+        script_name = os.path.join(root, 'script_from_import.py')
+        mf.run_script(script_name)
+
+        script_node = mf.findNode(script_name)
+        self.assertIsInstance(script_node, modulegraph.Script)
+
+
+        node = mf.findNode('pkg.toplevel_existing')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=False, tryexcept=False, fromlist=True))
+
+        node = mf.findNode('pkg.toplevel_nonexisting')
+        self.assertIsInstance(node, modulegraph.MissingModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=False, tryexcept=False, fromlist=True))
+
+        node = mf.findNode('pkg.toplevel_class_existing')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=False, tryexcept=False, fromlist=True))
+
+        node = mf.findNode('pkg.toplevel_class_nonexisting')
+        self.assertIsInstance(node, modulegraph.MissingModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=False, tryexcept=False, fromlist=True))
+
+        node = mf.findNode('pkg.toplevel_conditional_existing')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=False, tryexcept=False, fromlist=True))
+
+        node = mf.findNode('pkg.toplevel_conditional_nonexisting')
+        self.assertIsInstance(node, modulegraph.MissingModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=False, tryexcept=False, fromlist=True))
+
+        node = mf.findNode('pkg.toplevel_conditional_import_existing')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=False, tryexcept=True, fromlist=True))
+
+        node = mf.findNode('pkg.toplevel_conditional_import_nonexisting')
+        self.assertIsInstance(node, modulegraph.MissingModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=False, tryexcept=True, fromlist=True))
+
+        node = mf.findNode('pkg.toplevel_conditional_import2_existing')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=False, tryexcept=True, fromlist=True))
+
+        node = mf.findNode('pkg.toplevel_conditional_import2_nonexisting')
+        self.assertIsInstance(node, modulegraph.MissingModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=False, tryexcept=True, fromlist=True))
+
+        node = mf.findNode('pkg.toplevel_import_existing')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=False, tryexcept=True, fromlist=True))
+
+        node = mf.findNode('pkg.toplevel_import_nonexisting')
+        self.assertIsInstance(node, modulegraph.MissingModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=False, tryexcept=True, fromlist=True))
+
+        node = mf.findNode('pkg.toplevel_import2_existing')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=False, tryexcept=True, fromlist=True))
+
+        node = mf.findNode('pkg.toplevel_import2_nonexisting')
+        self.assertIsInstance(node, modulegraph.MissingModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=False, tryexcept=True, fromlist=True))
+
+        node = mf.findNode('pkg.function_existing')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=True, tryexcept=False, fromlist=True))
+
+        node = mf.findNode('pkg.function_nonexisting')
+        self.assertIsInstance(node, modulegraph.MissingModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=True, tryexcept=False, fromlist=True))
+
+        node = mf.findNode('pkg.function_class_existing')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=True, tryexcept=False, fromlist=True))
+
+        node = mf.findNode('pkg.function_class_nonexisting')
+        self.assertIsInstance(node, modulegraph.MissingModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=True, tryexcept=False, fromlist=True))
+
+        node = mf.findNode('pkg.function_conditional_existing')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=True, tryexcept=False, fromlist=True))
+
+        node = mf.findNode('pkg.function_conditional_nonexisting')
+        self.assertIsInstance(node, modulegraph.MissingModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=True, tryexcept=False, fromlist=True))
+
+        node = mf.findNode('pkg.function_conditional_import_existing')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=True, tryexcept=True, fromlist=True))
+
+        node = mf.findNode('pkg.function_conditional_import_nonexisting')
+        self.assertIsInstance(node, modulegraph.MissingModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=True, tryexcept=True, fromlist=True))
+
+        node = mf.findNode('pkg.function_conditional_import2_existing')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=True, tryexcept=True, fromlist=True))
+
+        node = mf.findNode('pkg.function_conditional_import2_nonexisting')
+        self.assertIsInstance(node, modulegraph.MissingModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=True, tryexcept=True, fromlist=True))
+
+        node = mf.findNode('pkg.function_import_existing')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=True, tryexcept=True, fromlist=True))
+
+        node = mf.findNode('pkg.function_import_nonexisting')
+        self.assertIsInstance(node, modulegraph.MissingModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=True, tryexcept=True, fromlist=True))
+
+        node = mf.findNode('pkg.function_import2_existing')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=True, tryexcept=True, fromlist=True))
+
+        node = mf.findNode('pkg.function_import2_nonexisting')
+        self.assertIsInstance(node, modulegraph.MissingModule)
+        ed = mf.edgeData(script_node, node)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=True, tryexcept=True, fromlist=True))
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/test_explicit_packages.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/test_explicit_packages.py
new file mode 100644
index 0000000..a964e4f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/test_explicit_packages.py
@@ -0,0 +1,51 @@
+from __future__ import absolute_import
+import unittest
+
+import os, shutil, sys
+
+from modulegraph import find_modules
+from modulegraph import modulegraph
+
+
+class PackagesTestCase (unittest.TestCase):
+    if not hasattr(unittest.TestCase, 'assertIsInstance'):
+        def assertIsInstance(self, object, types, message=None):
+            self.assertTrue(isinstance(object, types),
+                    message or '%r is not an instance of %r'%(object, types))
+
+    def testIncludePackage(self):
+        root = os.path.join(
+                os.path.dirname(os.path.abspath(__file__)),
+                'testpkg-packages')
+
+        mf = find_modules.find_modules(
+                path=[root]+sys.path,
+                scripts=[os.path.join(root, "main_script.py")],
+                packages=['pkg'],
+                debug=1)
+
+        node = mf.findNode('pkg')
+        self.assertIsInstance(node, modulegraph.Package)
+
+        node = mf.findNode('pkg.sub3')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+
+    def testIncludePackageWithExclude(self):
+        root = os.path.join(
+                os.path.dirname(os.path.abspath(__file__)),
+                'testpkg-packages')
+
+        mf = find_modules.find_modules(
+                path=[root]+sys.path,
+                scripts=[os.path.join(root, "main_script.py")],
+                packages=['pkg'],
+                excludes=['pkg.sub3'])
+
+        node = mf.findNode('pkg')
+        self.assertIsInstance(node, modulegraph.Package)
+
+        node = mf.findNode('pkg.sub3')
+        self.assertIsInstance(node, modulegraph.ExcludedModule)
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/test_implies.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/test_implies.py
new file mode 100644
index 0000000..71be6a9
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/test_implies.py
@@ -0,0 +1,78 @@
+import unittest
+
+import os, shutil, sys
+
+from modulegraph import modulegraph
+
+class ImpliesTestCase(unittest.TestCase):
+    if not hasattr(unittest.TestCase, 'assertIsInstance'):
+        def assertIsInstance(self, object, types, message=None):
+            self.assertTrue(isinstance(object, types),
+                    message or '%r is not an instance of %r'%(object, types))
+
+    def testBasicImplies(self):
+        root = os.path.join(
+                os.path.dirname(os.path.abspath(__file__)),
+                'testpkg-relimport')
+
+        # First check that 'syslog' isn't accidently in the graph:
+        mg = modulegraph.ModuleGraph(path=[root]+sys.path)
+        mg.run_script(os.path.join(root, 'script.py'))
+        node = mg.findNode('mod')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+
+        node = mg.findNode('syslog')
+        self.assertEqual(node, None)
+
+        # Now check that adding an implied dependency actually adds
+        # 'syslog' to the graph:
+        mg = modulegraph.ModuleGraph(path=[root]+sys.path, implies={
+            'mod': ['syslog']})
+        self.assertEqual(node, None)
+        mg.run_script(os.path.join(root, 'script.py'))
+        node = mg.findNode('mod')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+
+        node = mg.findNode('syslog')
+        self.assertIsInstance(node, modulegraph.Extension)
+
+        # Check that the edges are correct:
+        self.assertTrue(mg.findNode('mod') in mg.get_edges(node)[1])
+        self.assertTrue(node in mg.get_edges(mg.findNode('mod'))[0])
+
+    def testPackagedImplies(self):
+        root = os.path.join(
+                os.path.dirname(os.path.abspath(__file__)),
+                'testpkg-relimport')
+
+        # First check that 'syslog' isn't accidently in the graph:
+        mg = modulegraph.ModuleGraph(path=[root]+sys.path)
+        mg.run_script(os.path.join(root, 'script.py'))
+        node = mg.findNode('mod')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+
+        node = mg.findNode('syslog')
+        self.assertEqual(node, None)
+
+
+        # Now check that adding an implied dependency actually adds
+        # 'syslog' to the graph:
+        mg = modulegraph.ModuleGraph(path=[root]+sys.path, implies={
+            'pkg.relative': ['syslog']})
+        node = mg.findNode('syslog')
+        self.assertEqual(node, None)
+
+        mg.run_script(os.path.join(root, 'script.py'))
+        node = mg.findNode('pkg.relative')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+
+        node = mg.findNode('syslog')
+        self.assertIsInstance(node, modulegraph.Extension)
+
+        # Check that the edges are correct:
+        self.assertTrue(mg.findNode('pkg.relative') in mg.get_edges(node)[1])
+        self.assertTrue(node in mg.get_edges(mg.findNode('pkg.relative'))[0])
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/test_import_from_init.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/test_import_from_init.py
new file mode 100644
index 0000000..f1333a1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/test_import_from_init.py
@@ -0,0 +1,128 @@
+import sys
+if sys.version_info[:2] <= (2,6):
+    import unittest2 as unittest
+else:
+    import unittest
+import textwrap
+import subprocess
+import os
+from modulegraph import modulegraph
+
+class TestNativeImport (unittest.TestCase):
+    # The tests check that Python's import statement
+    # works as these tests expect.
+
+    def importModule(self, name):
+        if '.' in name:
+            script = textwrap.dedent("""\
+                try:
+                    import %s
+                except ImportError:
+                    import %s
+                print (%s.__name__)
+            """) %(name, name.rsplit('.', 1)[0], name)
+        else:
+            script = textwrap.dedent("""\
+                import %s
+                print (%s.__name__)
+            """) %(name, name)
+
+        p = subprocess.Popen([sys.executable, '-c', script],
+                stdout=subprocess.PIPE,
+                stderr=subprocess.STDOUT,
+                cwd=os.path.join(
+                    os.path.dirname(os.path.abspath(__file__)),
+                    'testpkg-import-from-init'),
+        )
+        data = p.communicate()[0]
+        if sys.version_info[0] != 2:
+            data = data.decode('UTF-8')
+        data = data.strip()
+
+        if data.endswith(' refs]'):
+            # with --with-pydebug builds
+            data = data.rsplit('\n', 1)[0].strip()
+
+        sts = p.wait()
+
+        if sts != 0:
+            print (data)
+        self.assertEqual(sts, 0)
+        return data
+
+
+    @unittest.skipUnless(sys.version_info[0] == 2, "Python 2.x test")
+    def testRootPkg(self):
+        m = self.importModule('pkg')
+        self.assertEqual(m, 'pkg')
+
+    @unittest.skipUnless(sys.version_info[0] == 2, "Python 2.x test")
+    def testSubPackage(self):
+        m = self.importModule('pkg.subpkg')
+        self.assertEqual(m, 'pkg.subpkg')
+
+    def testRootPkgRelImport(self):
+        m = self.importModule('pkg2')
+        self.assertEqual(m, 'pkg2')
+
+    def testSubPackageRelImport(self):
+        m = self.importModule('pkg2.subpkg')
+        self.assertEqual(m, 'pkg2.subpkg')
+
+
+class TestModuleGraphImport (unittest.TestCase):
+    if not hasattr(unittest.TestCase, 'assertIsInstance'):
+        def assertIsInstance(self, value, types):
+            if not isinstance(value, types):
+                self.fail("%r is not an instance of %r"%(value, types))
+
+    def setUp(self):
+        root = os.path.join(
+                os.path.dirname(os.path.abspath(__file__)),
+                'testpkg-import-from-init')
+        self.mf = modulegraph.ModuleGraph(path=[ root ] + sys.path)
+        #self.mf.debug = 999
+        self.mf.run_script(os.path.join(root, 'script.py'))
+
+
+    @unittest.skipUnless(sys.version_info[0] == 2, "Python 2.x test")
+    def testRootPkg(self):
+        node = self.mf.findNode('pkg')
+        self.assertIsInstance(node, modulegraph.Package)
+        self.assertEqual(node.identifier, 'pkg')
+
+    @unittest.skipUnless(sys.version_info[0] == 2, "Python 2.x test")
+    def testSubPackage(self):
+        node = self.mf.findNode('pkg.subpkg')
+        self.assertIsInstance(node, modulegraph.Package)
+        self.assertEqual(node.identifier, 'pkg.subpkg')
+
+        node = self.mf.findNode('pkg.subpkg.compat')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        self.assertEqual(node.identifier, 'pkg.subpkg.compat')
+
+        node = self.mf.findNode('pkg.subpkg._collections')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        self.assertEqual(node.identifier, 'pkg.subpkg._collections')
+
+    def testRootPkgRelImport(self):
+        node = self.mf.findNode('pkg2')
+        self.assertIsInstance(node, modulegraph.Package)
+        self.assertEqual(node.identifier, 'pkg2')
+
+    def testSubPackageRelImport(self):
+        node = self.mf.findNode('pkg2.subpkg')
+        self.assertIsInstance(node, modulegraph.Package)
+        self.assertEqual(node.identifier, 'pkg2.subpkg')
+
+        node = self.mf.findNode('pkg2.subpkg.compat')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        self.assertEqual(node.identifier, 'pkg2.subpkg.compat')
+
+        node = self.mf.findNode('pkg2.subpkg._collections')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        self.assertEqual(node.identifier, 'pkg2.subpkg._collections')
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/test_imports.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/test_imports.py
new file mode 100644
index 0000000..8cdcfa7
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/test_imports.py
@@ -0,0 +1,473 @@
+"""
+Test for import machinery
+"""
+import unittest
+import sys
+import textwrap
+import subprocess
+import os
+from modulegraph import modulegraph
+
+class TestNativeImport (unittest.TestCase):
+    # The tests check that Python's import statement
+    # works as these tests expect.
+
+    def importModule(self, name):
+        if '.' in name:
+            script = textwrap.dedent("""\
+                try:
+                    import %s
+                except ImportError:
+                    import %s
+                print (%s.__name__)
+            """) %(name, name.rsplit('.', 1)[0], name)
+        else:
+            script = textwrap.dedent("""\
+                import %s
+                print (%s.__name__)
+            """) %(name, name)
+
+        p = subprocess.Popen([sys.executable, '-c', script],
+                stdout=subprocess.PIPE,
+                stderr=subprocess.STDOUT,
+                cwd=os.path.join(
+                    os.path.dirname(os.path.abspath(__file__)),
+                    'testpkg-relimport'),
+        )
+        data = p.communicate()[0]
+        if sys.version_info[0] != 2:
+            data = data.decode('UTF-8')
+        data = data.strip()
+
+        if data.endswith(' refs]'):
+            # with --with-pydebug builds
+            data = data.rsplit('\n', 1)[0].strip()
+
+        sts = p.wait()
+
+        if sts != 0:
+            print (data)
+        self.assertEqual(sts, 0)
+        return data
+
+
+    def testRootModule(self):
+        m = self.importModule('mod')
+        self.assertEqual(m, 'mod')
+
+    def testRootPkg(self):
+        m = self.importModule('pkg')
+        self.assertEqual(m, 'pkg')
+
+    def testSubModule(self):
+        m = self.importModule('pkg.mod')
+        self.assertEqual(m, 'pkg.mod')
+
+    if sys.version_info[0] == 2:
+        def testOldStyle(self):
+            m = self.importModule('pkg.oldstyle.mod')
+            self.assertEqual(m, 'pkg.mod')
+    else:
+        # python3 always has __future__.absolute_import
+        def testOldStyle(self):
+            m = self.importModule('pkg.oldstyle.mod')
+            self.assertEqual(m, 'mod')
+
+    def testNewStyle(self):
+        m = self.importModule('pkg.toplevel.mod')
+        self.assertEqual(m, 'mod')
+
+    def testRelativeImport(self):
+        m = self.importModule('pkg.relative.mod')
+        self.assertEqual(m, 'pkg.mod')
+
+        m = self.importModule('pkg.subpkg.relative.mod')
+        self.assertEqual(m, 'pkg.mod')
+
+        m = self.importModule('pkg.subpkg.mod2.mod')
+        self.assertEqual(m, 'pkg.sub2.mod')
+
+        m = self.importModule('pkg.subpkg.relative2')
+        self.assertEqual(m, 'pkg.subpkg.relative2')
+
+class TestModuleGraphImport (unittest.TestCase):
+    if not hasattr(unittest.TestCase, 'assertIsInstance'):
+        def assertIsInstance(self, value, types):
+            if not isinstance(value, types):
+                self.fail("%r is not an instance of %r"%(value, types))
+
+    def setUp(self):
+        root = os.path.join(
+                os.path.dirname(os.path.abspath(__file__)),
+                'testpkg-relimport')
+        self.mf = modulegraph.ModuleGraph(path=[ root ] + sys.path)
+        #self.mf.debug = 999
+        self.script_name = os.path.join(root, 'script.py')
+        self.mf.run_script(self.script_name)
+
+    def testGraphStructure(self):
+
+        # 1. Script to imported modules
+        n = self.mf.findNode(self.script_name)
+        self.assertIsInstance(n, modulegraph.Script)
+
+        imported = ('mod', 'pkg', 'pkg.mod', 'pkg.oldstyle',
+            'pkg.relative', 'pkg.toplevel', 'pkg.subpkg.relative',
+            'pkg.subpkg.relative2', 'pkg.subpkg.mod2')
+
+        for nm in imported:
+            n2 = self.mf.findNode(nm)
+            ed = self.mf.edgeData(n, n2)
+            self.assertIsInstance(ed, modulegraph.DependencyInfo)
+            self.assertEqual(ed, modulegraph.DependencyInfo(
+                fromlist=False, conditional=False, function=False, tryexcept=False))
+
+        refs = self.mf.getReferences(n)
+        self.assertEqual(set(refs), set(self.mf.findNode(nm) for nm in imported))
+
+        refs = list(self.mf.getReferers(n))
+        # The script is a toplevel item and is therefore referred to from the graph root (aka 'None')
+        self.assertEqual(refs, [None])
+
+
+        # 2. 'mod'
+        n = self.mf.findNode('mod')
+        self.assertIsInstance(n, modulegraph.SourceModule)
+        refs = list(self.mf.getReferences(n))
+        self.assertEqual(refs, [])
+
+        #refs = list(self.mf.getReferers(n))
+        #self.assertEquals(refs, [])
+
+        # 3. 'pkg'
+        n = self.mf.findNode('pkg')
+        self.assertIsInstance(n, modulegraph.Package)
+        refs = list(self.mf.getReferences(n))
+        self.maxDiff = None
+        self.assertEqual(refs, [n])
+
+        #refs = list(self.mf.getReferers(n))
+        #self.assertEquals(refs, [])
+
+        # 4. pkg.mod
+        n = self.mf.findNode('pkg.mod')
+        self.assertIsInstance(n, modulegraph.SourceModule)
+        refs = set(self.mf.getReferences(n))
+        self.assertEqual(refs, set([self.mf.findNode('pkg')]))
+        ed = self.mf.edgeData(n, self.mf.findNode('pkg'))
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(
+            fromlist=False, conditional=False, function=False, tryexcept=False))
+
+
+        # 5. pkg.oldstyle
+        n = self.mf.findNode('pkg.oldstyle')
+        self.assertIsInstance(n, modulegraph.SourceModule)
+        refs = set(self.mf.getReferences(n))
+        if sys.version_info[0] == 2:
+            n2 = self.mf.findNode('pkg.mod')
+        else:
+            n2 = self.mf.findNode('mod')
+        self.assertEqual(refs, set([self.mf.findNode('pkg'), n2]))
+        ed = self.mf.edgeData(n, n2)
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(
+            fromlist=False, conditional=False, function=False, tryexcept=False))
+
+
+        # 6. pkg.relative
+        n = self.mf.findNode('pkg.relative')
+        self.assertIsInstance(n, modulegraph.SourceModule)
+        refs = set(self.mf.getReferences(n))
+        self.assertEqual(refs, set([self.mf.findNode('__future__'), self.mf.findNode('pkg'), self.mf.findNode('pkg.mod')]))
+
+        ed = self.mf.edgeData(n, self.mf.findNode('pkg.mod'))
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(
+            fromlist=True, conditional=False, function=False, tryexcept=False))
+
+        ed = self.mf.edgeData(n, self.mf.findNode('__future__'))
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(
+            fromlist=False, conditional=False, function=False, tryexcept=False))
+
+        #ed = self.mf.edgeData(n, self.mf.findNode('__future__.absolute_import'))
+        #self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        #self.assertEqual(ed, modulegraph.DependencyInfo(
+            #fromlist=True, conditional=False, function=False, tryexcept=False))
+
+        # 7. pkg.toplevel
+        n = self.mf.findNode('pkg.toplevel')
+        self.assertIsInstance(n, modulegraph.SourceModule)
+        refs = set(self.mf.getReferences(n))
+        self.assertEqual(refs, set([self.mf.findNode('__future__'), self.mf.findNode('pkg'), self.mf.findNode('mod')]))
+
+        ed = self.mf.edgeData(n, self.mf.findNode('mod'))
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(
+            fromlist=False, conditional=False, function=False, tryexcept=False))
+
+        ed = self.mf.edgeData(n, self.mf.findNode('__future__'))
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(
+            fromlist=False, conditional=False, function=False, tryexcept=False))
+
+        #ed = self.mf.edgeData(n, self.mf.findNode('__future__.absolute_import'))
+        #self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        #self.assertEqual(ed, modulegraph.DependencyInfo(
+            #fromlist=True, conditional=False, function=False, tryexcept=False))
+
+        # 8. pkg.subpkg
+        n = self.mf.findNode('pkg.subpkg')
+        self.assertIsInstance(n, modulegraph.Package)
+        refs = set(self.mf.getReferences(n))
+        self.assertEqual(refs, set([self.mf.findNode('pkg')]))
+
+        ed = self.mf.edgeData(n, self.mf.findNode('pkg'))
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(
+            fromlist=False, conditional=False, function=False, tryexcept=False))
+
+        # 9. pkg.subpkg.relative
+        n = self.mf.findNode('pkg.subpkg.relative')
+        self.assertIsInstance(n, modulegraph.SourceModule)
+        refs = set(self.mf.getReferences(n))
+        self.assertEqual(refs, set([self.mf.findNode('__future__'), self.mf.findNode('pkg'), self.mf.findNode('pkg.subpkg'), self.mf.findNode('pkg.mod')]))
+
+        ed = self.mf.edgeData(n, self.mf.findNode('pkg.subpkg'))
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(
+            fromlist=False, conditional=False, function=False, tryexcept=False))
+
+        ed = self.mf.edgeData(n, self.mf.findNode('pkg.mod'))
+        self.assertIsInstance(ed, modulegraph.DependencyInfo)
+        self.assertEqual(ed, modulegraph.DependencyInfo(
+            fromlist=True, conditional=False, function=False, tryexcept=False))
+
+        # 10. pkg.subpkg.relative2
+        n = self.mf.findNode('pkg.subpkg.relative2')
+        self.assertIsInstance(n, modulegraph.SourceModule)
+        refs = set(self.mf.getReferences(n))
+        self.assertEqual(refs, set([self.mf.findNode('pkg.subpkg'), self.mf.findNode('pkg.relimport'), self.mf.findNode('__future__')]))
+
+        # 10. pkg.subpkg.mod2
+        n = self.mf.findNode('pkg.subpkg.mod2')
+        self.assertIsInstance(n, modulegraph.SourceModule)
+        refs = set(self.mf.getReferences(n))
+        self.assertEqual(refs, set([
+            self.mf.findNode('__future__'),
+            self.mf.findNode('pkg.subpkg'),
+            self.mf.findNode('pkg.sub2.mod'),
+            self.mf.findNode('pkg.sub2'),
+        ]))
+
+
+    def testRootModule(self):
+        node = self.mf.findNode('mod')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        self.assertEqual(node.identifier, 'mod')
+
+    def testRootPkg(self):
+        node = self.mf.findNode('pkg')
+        self.assertIsInstance(node, modulegraph.Package)
+        self.assertEqual(node.identifier, 'pkg')
+
+    def testSubModule(self):
+        node = self.mf.findNode('pkg.mod')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        self.assertEqual(node.identifier, 'pkg.mod')
+
+    if sys.version_info[0] == 2:
+        def testOldStyle(self):
+            node = self.mf.findNode('pkg.oldstyle')
+            self.assertIsInstance(node, modulegraph.SourceModule)
+            self.assertEqual(node.identifier, 'pkg.oldstyle')
+            sub = [ n for n in self.mf.get_edges(node)[0] if n.identifier != '__future__' ][0]
+            self.assertEqual(sub.identifier, 'pkg.mod')
+    else:
+        # python3 always has __future__.absolute_import
+        def testOldStyle(self):
+            node = self.mf.findNode('pkg.oldstyle')
+            self.assertIsInstance(node, modulegraph.SourceModule)
+            self.assertEqual(node.identifier, 'pkg.oldstyle')
+            sub = [ n for n in self.mf.get_edges(node)[0] if n.identifier != '__future__' ][0]
+            self.assertEqual(sub.identifier, 'mod')
+
+    def testNewStyle(self):
+        node = self.mf.findNode('pkg.toplevel')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        self.assertEqual(node.identifier, 'pkg.toplevel')
+        sub = [ n for n in self.mf.get_edges(node)[0] if not n.identifier.startswith('__future__')][0]
+        self.assertEqual(sub.identifier, 'mod')
+
+    def testRelativeImport(self):
+        node = self.mf.findNode('pkg.relative')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        self.assertEqual(node.identifier, 'pkg.relative')
+        sub = [ n for n in self.mf.get_edges(node)[0] if not n.identifier.startswith('__future__') ][0]
+        self.assertIsInstance(sub, modulegraph.Package)
+        self.assertEqual(sub.identifier, 'pkg')
+
+        node = self.mf.findNode('pkg.subpkg.relative')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        self.assertEqual(node.identifier, 'pkg.subpkg.relative')
+        sub = [ n for n in self.mf.get_edges(node)[0] if not n.identifier.startswith('__future__') ][0]
+        self.assertIsInstance(sub, modulegraph.Package)
+        self.assertEqual(sub.identifier, 'pkg')
+
+        node = self.mf.findNode('pkg.subpkg.mod2')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        self.assertEqual(node.identifier, 'pkg.subpkg.mod2')
+        sub = [ n for n in self.mf.get_edges(node)[0] if not n.identifier.startswith('__future__') ][0]
+        self.assertIsInstance(sub, modulegraph.SourceModule)
+        self.assertEqual(sub.identifier, 'pkg.sub2.mod')
+
+        node = self.mf.findNode('pkg.subpkg.relative2')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        self.assertEqual(node.identifier, 'pkg.subpkg.relative2')
+
+        node = self.mf.findNode('pkg.relimport')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+
+class TestRegressions1 (unittest.TestCase):
+    if not hasattr(unittest.TestCase, 'assertIsInstance'):
+        def assertIsInstance(self, value, types):
+            if not isinstance(value, types):
+                self.fail("%r is not an instance of %r", value, types)
+
+    def setUp(self):
+        root = os.path.join(
+                os.path.dirname(os.path.abspath(__file__)),
+                'testpkg-regr1')
+        self.mf = modulegraph.ModuleGraph(path=[ root ] + sys.path)
+        self.mf.run_script(os.path.join(root, 'main_script.py'))
+
+    def testRegr1(self):
+        node = self.mf.findNode('pkg.a')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        node = self.mf.findNode('pkg.b')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+
+
+    def testMissingPathEntry(self):
+        root = os.path.join(
+                os.path.dirname(os.path.abspath(__file__)),
+                'nosuchdirectory')
+        try:
+            mf = modulegraph.ModuleGraph(path=[ root ] + sys.path)
+        except os.error:
+            self.fail('modulegraph initialiser raises os.error')
+
+class TestRegressions2 (unittest.TestCase):
+    if not hasattr(unittest.TestCase, 'assertIsInstance'):
+        def assertIsInstance(self, value, types):
+            if not isinstance(value, types):
+                self.fail("%r is not an instance of %r"%(value, types))
+
+    def setUp(self):
+        root = os.path.join(
+                os.path.dirname(os.path.abspath(__file__)),
+                'testpkg-regr2')
+        self.mf = modulegraph.ModuleGraph(path=[ root ] + sys.path)
+        self.mf.run_script(os.path.join(root, 'main_script.py'))
+
+    def testRegr1(self):
+        node = self.mf.findNode('pkg.base')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        node = self.mf.findNode('pkg.pkg')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+
+class TestRegressions3 (unittest.TestCase):
+    if not hasattr(unittest.TestCase, 'assertIsInstance'):
+        def assertIsInstance(self, value, types):
+            if not isinstance(value, types):
+                self.fail("%r is not an instance of %r"%(value, types))
+
+    def assertStartswith(self, value, test):
+        if not value.startswith(test):
+            self.fail("%r does not start with %r"%(value, test))
+
+    def setUp(self):
+        root = os.path.join(
+                os.path.dirname(os.path.abspath(__file__)),
+                'testpkg-regr3')
+        self.mf = modulegraph.ModuleGraph(path=[ root ] + sys.path)
+        self.mf.run_script(os.path.join(root, 'script.py'))
+
+    def testRegr1(self):
+        node = self.mf.findNode('mypkg.distutils')
+        self.assertIsInstance(node, modulegraph.Package)
+        node = self.mf.findNode('mypkg.distutils.ccompiler')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        self.assertStartswith(node.filename, os.path.dirname(__file__))
+
+        import distutils.sysconfig, distutils.ccompiler
+        node = self.mf.findNode('distutils.ccompiler')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        self.assertEqual(os.path.dirname(node.filename),
+                os.path.dirname(distutils.ccompiler.__file__))
+
+        node = self.mf.findNode('distutils.sysconfig')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        self.assertEqual(os.path.dirname(node.filename),
+                os.path.dirname(distutils.sysconfig.__file__))
+
+class TestRegression4 (unittest.TestCase):
+    if not hasattr(unittest.TestCase, 'assertIsInstance'):
+        def assertIsInstance(self, value, types):
+            if not isinstance(value, types):
+                self.fail("%r is not an instance of %r"%(value, types))
+
+    def setUp(self):
+        root = os.path.join(
+                os.path.dirname(os.path.abspath(__file__)),
+                'testpkg-regr4')
+        self.mf = modulegraph.ModuleGraph(path=[ root ] + sys.path)
+        self.mf.run_script(os.path.join(root, 'script.py'))
+
+    def testRegr1(self):
+        node = self.mf.findNode('pkg.core')
+        self.assertIsInstance(node, modulegraph.Package)
+
+        node = self.mf.findNode('pkg.core.callables')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+
+        node = self.mf.findNode('pkg.core.listener')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+
+        node = self.mf.findNode('pkg.core.listenerimpl')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+
+class TestRegression5 (unittest.TestCase):
+    if not hasattr(unittest.TestCase, 'assertIsInstance'):
+        def assertIsInstance(self, value, types):
+            if not isinstance(value, types):
+                self.fail("%r is not an instance of %r"%(value, types))
+
+    def setUp(self):
+        root = os.path.join(
+                os.path.dirname(os.path.abspath(__file__)),
+                'testpkg-regr5')
+        self.mf = modulegraph.ModuleGraph(path=[ root ] + sys.path)
+        self.mf.run_script(os.path.join(root, 'script.py'))
+
+    def testRegr1(self):
+        node = self.mf.findNode('distutils')
+        self.assertIsInstance(node, modulegraph.Package)
+        self.assertIn('distutils/__init__', node.filename)
+
+class TestDeeplyNested (unittest.TestCase):
+    def setUp(self):
+        root = os.path.join(
+                os.path.dirname(os.path.abspath(__file__)),
+                'testpkg-regr6')
+        self.mf = modulegraph.ModuleGraph(path=[ root ] + sys.path)
+        self.mf.run_script(os.path.join(root, 'script.py'))
+
+    def testRegr(self):
+        node = self.mf.findNode('os')
+        self.assertTrue(node is not None)
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/test_modulegraph.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/test_modulegraph.py
new file mode 100644
index 0000000..0ee724b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/test_modulegraph.py
@@ -0,0 +1,1060 @@
+import unittest
+from modulegraph import modulegraph
+import pkg_resources
+import os
+import imp
+import sys
+import shutil
+import warnings
+from altgraph import Graph
+import textwrap
+import xml.etree.ElementTree as ET
+import pickle
+
+try:
+    bytes
+except NameError:
+    bytes = str
+
+try:
+    from StringIO import StringIO
+except ImportError:
+    from io import StringIO
+
+TESTDATA = os.path.join(
+        os.path.dirname(os.path.abspath(__file__)),
+        "testdata", "nspkg")
+
+try:
+    expectedFailure = unittest.expectedFailure
+except AttributeError:
+    import functools
+    def expectedFailure(function):
+        @functools.wraps(function)
+        def wrapper(*args, **kwds):
+            try:
+                function(*args, **kwds)
+            except AssertionError:
+                pass
+
+            else:
+                self.fail("unexpected pass")
+
+class TestDependencyInfo (unittest.TestCase):
+    def test_pickling(self):
+        info = modulegraph.DependencyInfo(function=True, conditional=False, tryexcept=True, fromlist=False)
+        for proto in range(pickle.HIGHEST_PROTOCOL + 1):
+            b = pickle.dumps(info, proto)
+            self.assertTrue(isinstance(b, bytes))
+
+            o = pickle.loads(b)
+            self.assertEqual(o, info)
+
+    def test_merging(self):
+        info1 = modulegraph.DependencyInfo(function=True, conditional=False, tryexcept=True, fromlist=False)
+        info2 = modulegraph.DependencyInfo(function=False, conditional=True, tryexcept=True, fromlist=False)
+        self.assertEqual(
+            info1._merged(info2), modulegraph.DependencyInfo(function=True, conditional=True, tryexcept=True, fromlist=False))
+
+        info2 = modulegraph.DependencyInfo(function=False, conditional=True, tryexcept=False, fromlist=False)
+        self.assertEqual(
+            info1._merged(info2), modulegraph.DependencyInfo(function=True, conditional=True, tryexcept=True, fromlist=False))
+
+        info2 = modulegraph.DependencyInfo(function=False, conditional=False, tryexcept=False, fromlist=False)
+        self.assertEqual(
+            info1._merged(info2), modulegraph.DependencyInfo(function=False, conditional=False, tryexcept=False, fromlist=False))
+
+        info1 = modulegraph.DependencyInfo(function=True, conditional=False, tryexcept=True, fromlist=True)
+        self.assertEqual(
+            info1._merged(info2), modulegraph.DependencyInfo(function=False, conditional=False, tryexcept=False, fromlist=False))
+
+        info2 = modulegraph.DependencyInfo(function=False, conditional=False, tryexcept=False, fromlist=True)
+        self.assertEqual(
+            info1._merged(info2), modulegraph.DependencyInfo(function=False, conditional=False, tryexcept=False, fromlist=True))
+
+
+class TestFunctions (unittest.TestCase):
+    if not hasattr(unittest.TestCase, 'assertIsInstance'):
+        def assertIsInstance(self, obj, types):
+            self.assertTrue(isinstance(obj, types), '%r is not instance of %r'%(obj, types))
+
+    def test_eval_str_tuple(self):
+        for v in [
+            '()',
+            '("hello",)',
+            '("hello", "world")',
+            "('hello',)",
+            "('hello', 'world')",
+            "('hello', \"world\")",
+            ]:
+
+            self.assertEqual(modulegraph._eval_str_tuple(v), eval(v))
+
+        self.assertRaises(ValueError, modulegraph._eval_str_tuple, "")
+        self.assertRaises(ValueError, modulegraph._eval_str_tuple, "'a'")
+        self.assertRaises(ValueError, modulegraph._eval_str_tuple, "'a', 'b'")
+        self.assertRaises(ValueError, modulegraph._eval_str_tuple, "('a', ('b', 'c'))")
+        self.assertRaises(ValueError, modulegraph._eval_str_tuple, "('a', ('b\", 'c'))")
+
+    def test_namespace_package_path(self):
+        class DS (object):
+            def __init__(self, path, namespace_packages=None):
+                self.location = path
+                self._namespace_packages = namespace_packages
+
+            def has_metadata(self, key):
+                if key == 'namespace_packages.txt':
+                    return self._namespace_packages is not None
+
+                raise ValueError("invalid lookup key")
+
+            def get_metadata(self, key):
+                if key == 'namespace_packages.txt':
+                    if self._namespace_packages is None:
+                        raise ValueError("no file")
+
+                    return self._namespace_packages
+
+                raise ValueError("invalid lookup key")
+
+        class WS (object):
+            def __init__(self, path=None):
+                pass
+
+            def __iter__(self):
+                yield DS("/pkg/pkg1")
+                yield DS("/pkg/pkg2", "foo\n")
+                yield DS("/pkg/pkg3", "bar.baz\n")
+                yield DS("/pkg/pkg4", "foobar\nfoo\n")
+
+        saved_ws = pkg_resources.WorkingSet
+        try:
+            pkg_resources.WorkingSet = WS
+
+            self.assertEqual(modulegraph._namespace_package_path("sys", ["appdir/pkg"]), ["appdir/pkg"])
+            self.assertEqual(modulegraph._namespace_package_path("foo", ["appdir/pkg"]), ["appdir/pkg", "/pkg/pkg2/foo", "/pkg/pkg4/foo"])
+            self.assertEqual(modulegraph._namespace_package_path("bar.baz", ["appdir/pkg"]), ["appdir/pkg", "/pkg/pkg3/bar/baz"])
+
+        finally:
+            pkg_resources.WorkingSet = saved_ws
+
+    def test_os_listdir(self):
+        root = os.path.join(
+                os.path.dirname(os.path.abspath(__file__)), 'testdata')
+
+        self.assertEqual(modulegraph.os_listdir('/etc/'), os.listdir('/etc'))
+        self.assertRaises(IOError, modulegraph.os_listdir, '/etc/hosts/foobar')
+        self.assertRaises(IOError, modulegraph.os_listdir, os.path.join(root, 'test.egg', 'bar'))
+
+        self.assertEqual(list(sorted(modulegraph.os_listdir(os.path.join(root, 'test.egg', 'foo')))),
+            [ 'bar', 'bar.txt', 'baz.txt' ])
+
+    def test_code_to_file(self):
+        try:
+            code = modulegraph._code_to_file.__code__
+        except AttributeError:
+            code = modulegraph._code_to_file.func_code
+
+        data = modulegraph._code_to_file(code)
+        self.assertTrue(hasattr(data, 'read'))
+
+        content = data.read()
+        self.assertIsInstance(content, bytes)
+        data.close()
+
+    def test_find_module(self):
+        for path in ('syspath', 'syspath.zip', 'syspath.egg'):
+            path = os.path.join(os.path.dirname(TESTDATA), path)
+            if os.path.exists(os.path.join(path, 'mymodule.pyc')):
+                os.unlink(os.path.join(path, 'mymodule.pyc'))
+
+            # Plain module
+            info = modulegraph.find_module('mymodule', path=[path] + sys.path)
+
+            fp = info[0]
+            filename = info[1]
+            description = info[2]
+
+            self.assertTrue(hasattr(fp, 'read'))
+
+            if path.endswith('.zip') or path.endswith('.egg'):
+                # Zip importers may precompile
+                if filename.endswith('.py'):
+                    self.assertEqual(filename, os.path.join(path, 'mymodule.py'))
+                    self.assertEqual(description, ('.py', 'rU', imp.PY_SOURCE))
+
+                else:
+                    self.assertEqual(filename, os.path.join(path, 'mymodule.pyc'))
+                    self.assertEqual(description, ('.pyc', 'rb', imp.PY_COMPILED))
+
+            else:
+                self.assertEqual(filename, os.path.join(path, 'mymodule.py'))
+                self.assertEqual(description, ('.py', 'rU', imp.PY_SOURCE))
+
+            # Compiled plain module, no source
+            if path.endswith('.zip') or path.endswith('.egg'):
+                self.assertRaises(ImportError, modulegraph.find_module, 'mymodule2', path=[path] + sys.path)
+
+            else:
+                info = modulegraph.find_module('mymodule2', path=[path] + sys.path)
+
+                fp = info[0]
+                filename = info[1]
+                description = info[2]
+
+                self.assertTrue(hasattr(fp, 'read'))
+                self.assertEqual(filename, os.path.join(path, 'mymodule2.pyc'))
+                self.assertEqual(description, ('.pyc', 'rb', imp.PY_COMPILED))
+
+                fp.close()
+
+            # Compiled plain module, with source
+#            info = modulegraph.find_module('mymodule3', path=[path] + sys.path)
+#
+#            fp = info[0]
+#            filename = info[1]
+#            description = info[2]
+#
+#            self.assertTrue(hasattr(fp, 'read'))
+#
+#            if sys.version_info[:2] >= (3,2):
+#                self.assertEqual(filename, os.path.join(path, '__pycache__', 'mymodule3.cpython-32.pyc'))
+#            else:
+#                self.assertEqual(filename, os.path.join(path, 'mymodule3.pyc'))
+#            self.assertEqual(description, ('.pyc', 'rb', imp.PY_COMPILED))
+
+
+            # Package
+            info = modulegraph.find_module('mypkg', path=[path] + sys.path)
+            fp = info[0]
+            filename = info[1]
+            description = info[2]
+
+            self.assertEqual(fp, None)
+            self.assertEqual(filename, os.path.join(path, 'mypkg'))
+            self.assertEqual(description, ('', '', imp.PKG_DIRECTORY))
+
+            # Extension
+            if path.endswith('.zip'):
+                self.assertRaises(ImportError, modulegraph.find_module, 'myext', path=[path] + sys.path)
+
+            else:
+                info = modulegraph.find_module('myext', path=[path] + sys.path)
+                fp = info[0]
+                filename = info[1]
+                description = info[2]
+
+                if sys.platform == 'win32':
+                    ext = '.pyd'
+                else:
+                    # This is a ly, but is good enough for now
+                    ext = '.so'
+
+                self.assertEqual(filename, os.path.join(path, 'myext' + ext))
+                self.assertEqual(description, (ext, 'rb', imp.C_EXTENSION))
+                self.assertEqual(fp, None)
+
+    def test_moduleInfoForPath(self):
+        self.assertEqual(modulegraph.moduleInfoForPath("/somewhere/else/file.txt"), None)
+
+        info = modulegraph.moduleInfoForPath("/somewhere/else/file.py")
+        self.assertEqual(info[0], "file")
+        if sys.version_info[:2] >= (3,4):
+            self.assertEqual(info[1], "r")
+        else:
+            self.assertEqual(info[1], "U")
+        self.assertEqual(info[2], imp.PY_SOURCE)
+
+        info = modulegraph.moduleInfoForPath("/somewhere/else/file.pyc")
+        self.assertEqual(info[0], "file")
+        self.assertEqual(info[1], "rb")
+        self.assertEqual(info[2], imp.PY_COMPILED)
+
+        if sys.platform in ('darwin', 'linux2'):
+            info = modulegraph.moduleInfoForPath("/somewhere/else/file.so")
+            self.assertEqual(info[0], "file")
+            self.assertEqual(info[1], "rb")
+            self.assertEqual(info[2], imp.C_EXTENSION)
+
+        elif sys.platform in ('win32',):
+            info = modulegraph.moduleInfoForPath("/somewhere/else/file.pyd")
+            self.assertEqual(info[0], "file")
+            self.assertEqual(info[1], "rb")
+            self.assertEqual(info[2], imp.C_EXTENSION)
+
+    if sys.version_info[:2] > (2,5):
+        exec(textwrap.dedent('''\
+            def test_deprecated(self):
+                saved_add = modulegraph.addPackagePath
+                saved_replace = modulegraph.replacePackage
+                try:
+                    called = []
+
+                    def log_add(*args, **kwds):
+                        called.append(('add', args, kwds))
+                    def log_replace(*args, **kwds):
+                        called.append(('replace', args, kwds))
+
+                    modulegraph.addPackagePath = log_add
+                    modulegraph.replacePackage = log_replace
+
+                    with warnings.catch_warnings(record=True) as w:
+                        warnings.simplefilter("always")
+                        modulegraph.ReplacePackage('a', 'b')
+                        modulegraph.AddPackagePath('c', 'd')
+
+                    self.assertEqual(len(w), 2)
+                    self.assertTrue(w[-1].category is DeprecationWarning)
+                    self.assertTrue(w[-2].category is DeprecationWarning)
+
+                    self.assertEqual(called, [
+                        ('replace', ('a', 'b'), {}),
+                        ('add', ('c', 'd'), {}),
+                    ])
+
+                finally:
+                    modulegraph.addPackagePath = saved_add
+                    modulegraph.replacePackage = saved_replace
+            '''), locals(), globals())
+
+    def test_addPackage(self):
+        saved = modulegraph._packagePathMap
+        self.assertIsInstance(saved, dict)
+        try:
+            modulegraph._packagePathMap = {}
+
+            modulegraph.addPackagePath('foo', 'a')
+            self.assertEqual(modulegraph._packagePathMap, { 'foo': ['a'] })
+
+            modulegraph.addPackagePath('foo', 'b')
+            self.assertEqual(modulegraph._packagePathMap, { 'foo': ['a', 'b'] })
+
+            modulegraph.addPackagePath('bar', 'b')
+            self.assertEqual(modulegraph._packagePathMap, { 'foo': ['a', 'b'], 'bar': ['b'] })
+
+        finally:
+            modulegraph._packagePathMap = saved
+
+
+    def test_replacePackage(self):
+        saved = modulegraph._replacePackageMap
+        self.assertIsInstance(saved, dict)
+        try:
+            modulegraph._replacePackageMap = {}
+
+            modulegraph.replacePackage("a", "b")
+            self.assertEqual(modulegraph._replacePackageMap, {"a": "b"})
+            modulegraph.replacePackage("a", "c")
+            self.assertEqual(modulegraph._replacePackageMap, {"a": "c"})
+            modulegraph.replacePackage("b", "c")
+            self.assertEqual(modulegraph._replacePackageMap, {"a": "c", 'b': 'c'})
+
+        finally:
+            modulegraph._replacePackageMap = saved
+
+class TestNode (unittest.TestCase):
+    if not hasattr(unittest.TestCase, 'assertIsInstance'):
+        def assertIsInstance(self, obj, types):
+            self.assertTrue(isinstance(obj, types), '%r is not instance of %r'%(obj, types))
+    def testBasicAttributes(self):
+        n = modulegraph.Node("foobar.xyz")
+        self.assertIsInstance(n.debug, int)
+        self.assertEqual(n.identifier, n.graphident)
+        self.assertEqual(n.identifier, 'foobar.xyz')
+        self.assertEqual(n.filename, None)
+        self.assertEqual(n.packagepath, None)
+        self.assertEqual(n.code, None)
+        self.assertEqual(n.globalnames, set())
+        self.assertEqual(n.starimports, set())
+
+    def testMapping(self):
+        n = modulegraph.Node("foobar.xyz")
+        self.assertEqual(n._namespace, {})
+
+        self.assertFalse('foo' in n)
+        self.assertRaises(KeyError, n.__getitem__, 'foo')
+        self.assertEqual(n.get('foo'), None)
+        self.assertEqual(n.get('foo', 'a'), 'a')
+        n['foo'] = 42
+        self.assertEqual(n['foo'], 42)
+        self.assertTrue('foo' in n)
+        self.assertEqual(n._namespace, {'foo':42})
+
+    def testOrder(self):
+        n1 = modulegraph.Node("n1")
+        n2 = modulegraph.Node("n2")
+
+        self.assertTrue(n1 < n2)
+        self.assertFalse(n2 < n1)
+        self.assertTrue(n1 <= n1)
+        self.assertFalse(n1 == n2)
+        self.assertTrue(n1 == n1)
+        self.assertTrue(n1 != n2)
+        self.assertFalse(n1 != n1)
+        self.assertTrue(n2 > n1)
+        self.assertFalse(n1 > n2)
+        self.assertTrue(n1 >= n1)
+        self.assertTrue(n2 >= n1)
+
+    def testHashing(self):
+        n1a = modulegraph.Node('n1')
+        n1b = modulegraph.Node('n1')
+        n2 = modulegraph.Node('n2')
+
+        d = {}
+        d[n1a] = 'n1'
+        d[n2] = 'n2'
+        self.assertEqual(d[n1b], 'n1')
+        self.assertEqual(d[n2], 'n2')
+
+    def test_infoTuple(self):
+        n = modulegraph.Node('n1')
+        self.assertEqual(n.infoTuple(), ('n1',))
+
+    def assertNoMethods(self, klass):
+        d = dict(klass.__dict__)
+        del d['__doc__']
+        del d['__module__']
+        if '__qualname__' in d:
+            # New in Python 3.3
+            del d['__qualname__']
+        if '__dict__' in d:
+            # New in Python 3.4
+            del d['__dict__']
+        self.assertEqual(d, {})
+
+    def assertHasExactMethods(self, klass, *methods):
+        d = dict(klass.__dict__)
+        del d['__doc__']
+        del d['__module__']
+        if '__qualname__' in d:
+            # New in Python 3.3
+            del d['__qualname__']
+        if '__dict__' in d:
+            # New in Python 3.4
+            del d['__dict__']
+
+        for nm in methods:
+            self.assertTrue(nm in d, "%s doesn't have attribute %r"%(klass, nm))
+            del d[nm]
+
+        self.assertEqual(d, {})
+
+
+    if not hasattr(unittest.TestCase, 'assertIsSubclass'):
+        def assertIsSubclass(self, cls1, cls2, message=None):
+            self.assertTrue(issubclass(cls1, cls2),
+                    message or "%r is not a subclass of %r"%(cls1, cls2))
+
+    def test_subclasses(self):
+        self.assertIsSubclass(modulegraph.AliasNode, modulegraph.Node)
+        self.assertIsSubclass(modulegraph.Script, modulegraph.Node)
+        self.assertIsSubclass(modulegraph.BadModule, modulegraph.Node)
+        self.assertIsSubclass(modulegraph.ExcludedModule, modulegraph.BadModule)
+        self.assertIsSubclass(modulegraph.MissingModule, modulegraph.BadModule)
+        self.assertIsSubclass(modulegraph.BaseModule, modulegraph.Node)
+        self.assertIsSubclass(modulegraph.BuiltinModule, modulegraph.BaseModule)
+        self.assertIsSubclass(modulegraph.SourceModule, modulegraph.BaseModule)
+        self.assertIsSubclass(modulegraph.CompiledModule, modulegraph.BaseModule)
+        self.assertIsSubclass(modulegraph.Package, modulegraph.BaseModule)
+        self.assertIsSubclass(modulegraph.Extension, modulegraph.BaseModule)
+
+        # These classes have no new functionality, check that no code
+        # got added:
+        self.assertNoMethods(modulegraph.BadModule)
+        self.assertNoMethods(modulegraph.ExcludedModule)
+        self.assertNoMethods(modulegraph.MissingModule)
+        self.assertNoMethods(modulegraph.BuiltinModule)
+        self.assertNoMethods(modulegraph.SourceModule)
+        self.assertNoMethods(modulegraph.CompiledModule)
+        self.assertNoMethods(modulegraph.Package)
+        self.assertNoMethods(modulegraph.Extension)
+
+        # AliasNode is basicly a clone of an existing node
+        self.assertHasExactMethods(modulegraph.Script, '__init__', 'infoTuple')
+        n1 = modulegraph.Node('n1')
+        n1.packagepath = ['a', 'b']
+
+        a1 = modulegraph.AliasNode('a1', n1)
+        self.assertEqual(a1.graphident, 'a1')
+        self.assertEqual(a1.identifier, 'n1')
+        self.assertTrue(a1.packagepath is n1.packagepath)
+        self.assertTrue(a1._namespace is n1._namespace)
+        self.assertTrue(a1.globalnames is n1.globalnames)
+        self.assertTrue(a1.starimports is n1.starimports)
+
+        v = a1.infoTuple()
+        self.assertEqual(v, ('a1', 'n1'))
+
+        # Scripts have a filename
+        self.assertHasExactMethods(modulegraph.Script, '__init__', 'infoTuple')
+        s1 = modulegraph.Script('do_import')
+        self.assertEqual(s1.graphident, 'do_import')
+        self.assertEqual(s1.identifier, 'do_import')
+        self.assertEqual(s1.filename, 'do_import')
+
+        v = s1.infoTuple()
+        self.assertEqual(v, ('do_import',))
+
+        # BaseModule adds some attributes and a custom infotuple
+        self.assertHasExactMethods(modulegraph.BaseModule, '__init__', 'infoTuple')
+        m1 = modulegraph.BaseModule('foo')
+        self.assertEqual(m1.graphident, 'foo')
+        self.assertEqual(m1.identifier, 'foo')
+        self.assertEqual(m1.filename, None)
+        self.assertEqual(m1.packagepath, None)
+
+        m1 = modulegraph.BaseModule('foo', 'bar',  ['a'])
+        self.assertEqual(m1.graphident, 'foo')
+        self.assertEqual(m1.identifier, 'foo')
+        self.assertEqual(m1.filename, 'bar')
+        self.assertEqual(m1.packagepath, ['a'])
+
+class TestModuleGraph (unittest.TestCase):
+    # Test for class modulegraph.modulegraph.ModuleGraph
+    if not hasattr(unittest.TestCase, 'assertIsInstance'):
+        def assertIsInstance(self, obj, types):
+            self.assertTrue(isinstance(obj, types), '%r is not instance of %r'%(obj, types))
+
+    def test_constructor(self):
+        o = modulegraph.ModuleGraph()
+        self.assertTrue(o.path is sys.path)
+        self.assertEqual(o.lazynodes, {})
+        self.assertEqual(o.replace_paths, ())
+        self.assertEqual(o.debug, 0)
+
+        # Stricter tests would be nice, but that requires
+        # better control over what's on sys.path
+        self.assertIsInstance(o.nspackages, dict)
+
+        g = Graph.Graph()
+        o = modulegraph.ModuleGraph(['a', 'b', 'c'], ['modA'], [
+                ('fromA', 'toB'), ('fromC', 'toD')],
+                {
+                    'modA': ['modB', 'modC'],
+                    'modC': ['modE', 'modF'],
+                }, g, 1)
+        self.assertEqual(o.path, ['a', 'b', 'c'])
+        self.assertEqual(o.lazynodes, {
+            'modA': None,
+            'modC': ['modE', 'modF'],
+        })
+        self.assertEqual(o.replace_paths, [('fromA', 'toB'), ('fromC', 'toD')])
+        self.assertEqual(o.nspackages, {})
+        self.assertTrue(o.graph is g)
+        self.assertEqual(o.debug, 1)
+
+    def test_calc_setuptools_nspackages(self):
+        stdlib = [ fn for fn in sys.path if fn.startswith(sys.prefix) and 'site-packages' not in fn ]
+        for subdir in [ nm for nm in os.listdir(TESTDATA) if nm != 'src' ]:
+            graph = modulegraph.ModuleGraph(path=[
+                    os.path.join(TESTDATA, subdir, "parent"),
+                    os.path.join(TESTDATA, subdir, "child"),
+                ] + stdlib)
+
+            pkgs = graph.nspackages
+            self.assertTrue('namedpkg' in pkgs)
+            self.assertEqual(set(pkgs['namedpkg']),
+                    set([
+                        os.path.join(TESTDATA, subdir, "parent", "namedpkg"),
+                        os.path.join(TESTDATA, subdir, "child", "namedpkg"),
+                    ]))
+            self.assertFalse(os.path.exists(os.path.join(TESTDATA, subdir, "parent", "namedpkg", "__init__.py")))
+            self.assertFalse(os.path.exists(os.path.join(TESTDATA, subdir, "child", "namedpkg", "__init__.py")))
+
+    def testImpliedReference(self):
+        graph = modulegraph.ModuleGraph()
+
+        record = []
+        def import_hook(*args):
+            record.append(('import_hook',) + args)
+            return [graph.createNode(modulegraph.Node, args[0])]
+
+        def _safe_import_hook(*args):
+            record.append(('_safe_import_hook',) + args)
+            return [graph.createNode(modulegraph.Node, args[0])]
+
+        graph.import_hook = import_hook
+        graph._safe_import_hook = _safe_import_hook
+
+        n1 = graph.createNode(modulegraph.Node, 'n1')
+        n2 = graph.createNode(modulegraph.Node, 'n2')
+
+        graph.implyNodeReference(n1, n2)
+        outs, ins = map(list, graph.get_edges(n1))
+        self.assertEqual(outs, [n2])
+        self.assertEqual(ins, [])
+
+        self.assertEqual(record, [])
+
+        graph.implyNodeReference(n2, "n3")
+        n3 = graph.findNode('n3')
+        outs, ins = map(list, graph.get_edges(n2))
+        self.assertEqual(outs, [n3])
+        self.assertEqual(ins, [n1])
+        self.assertEqual(record, [
+            ('_safe_import_hook', 'n3', n2, None)
+        ])
+
+
+
+    @expectedFailure
+    def test_findNode(self):
+        self.fail("findNode")
+
+    def test_run_script(self):
+        script = os.path.join(os.path.dirname(TESTDATA), 'script')
+
+        graph = modulegraph.ModuleGraph()
+        master = graph.createNode(modulegraph.Node, 'root')
+        m = graph.run_script(script, master)
+        self.assertEqual(list(graph.get_edges(master)[0])[0], m)
+        self.assertEqual(set(graph.get_edges(m)[0]), set([
+            graph.findNode('sys'),
+            graph.findNode('os'),
+        ]))
+
+    @expectedFailure
+    def test_import_hook(self):
+        self.fail("import_hook")
+
+    def test_determine_parent(self):
+        graph = modulegraph.ModuleGraph()
+        graph.import_hook('os.path', None)
+        graph.import_hook('idlelib', None)
+        graph.import_hook('xml.dom', None)
+
+        for node in graph.nodes():
+            if isinstance(node, modulegraph.Package):
+                break
+        else:
+            self.fail("No package located, should have at least 'os'")
+
+        self.assertIsInstance(node, modulegraph.Package)
+        parent = graph._determine_parent(node)
+        self.assertEqual(parent.identifier, node.identifier)
+        self.assertEqual(parent, graph.findNode(node.identifier))
+        self.assertTrue(isinstance(parent, modulegraph.Package))
+
+        # XXX: Might be a usecase for some odd code in determine_parent...
+        #node = modulegraph.Package('encodings')
+        #node.packagepath = parent.packagepath
+        #m = graph._determine_parent(node)
+        #self.assertTrue(m is parent)
+
+        m = graph.findNode('xml')
+        self.assertEqual(graph._determine_parent(m), m)
+
+        m = graph.findNode('xml.dom')
+        self.assertEqual(graph._determine_parent(m), graph.findNode('xml.dom'))
+
+
+    @expectedFailure
+    def test_find_head_package(self):
+        self.fail("find_head_package")
+
+    def test_load_tail(self):
+        # XXX: This test is dodgy!
+        graph = modulegraph.ModuleGraph()
+
+        record = []
+        def _import_module(partname, fqname, parent):
+            record.append((partname, fqname, parent))
+            if partname == 'raises' or '.raises.' in fqname:
+                return None
+            return modulegraph.Node(fqname)
+
+        graph._import_module = _import_module
+
+        record = []
+        root = modulegraph.Node('root')
+        m = graph._load_tail(root, '')
+        self.assertTrue(m is root)
+        self.assertEqual(record, [
+            ])
+
+        record = []
+        root = modulegraph.Node('root')
+        m = graph._load_tail(root, 'sub')
+        self.assertFalse(m is root)
+        self.assertEqual(record, [
+                ('sub', 'root.sub', root),
+            ])
+
+        record = []
+        root = modulegraph.Node('root')
+        m = graph._load_tail(root, 'sub.sub1')
+        self.assertFalse(m is root)
+        node = modulegraph.Node('root.sub')
+        self.assertEqual(record, [
+                ('sub', 'root.sub', root),
+                ('sub1', 'root.sub.sub1', node),
+            ])
+
+        record = []
+        root = modulegraph.Node('root')
+        m = graph._load_tail(root, 'sub.sub1.sub2')
+        self.assertFalse(m is root)
+        node = modulegraph.Node('root.sub')
+        node2 = modulegraph.Node('root.sub.sub1')
+        self.assertEqual(record, [
+                ('sub', 'root.sub', root),
+                ('sub1', 'root.sub.sub1', node),
+                ('sub2', 'root.sub.sub1.sub2', node2),
+            ])
+
+        n = graph._load_tail(root, 'raises')
+        self.assertIsInstance(n, modulegraph.MissingModule)
+        self.assertEqual(n.identifier, 'root.raises')
+
+        n = graph._load_tail(root, 'sub.raises')
+        self.assertIsInstance(n, modulegraph.MissingModule)
+        self.assertEqual(n.identifier, 'root.sub.raises')
+
+        n = graph._load_tail(root, 'sub.raises.sub')
+        self.assertIsInstance(n, modulegraph.MissingModule)
+        self.assertEqual(n.identifier, 'root.sub.raises.sub')
+
+
+
+    @expectedFailure
+    def test_ensure_fromlist(self):
+        # 1. basic 'from module import name, name'
+        # 2. 'from module import *'
+        # 3. from module import os
+        #    (where 'os' is not a name in 'module',
+        #     should create MissingModule node, and
+        #     should *not* refer to the global os)
+        self.fail("ensure_fromlist")
+
+    @expectedFailure
+    def test_find_all_submodules(self):
+        # 1. basic
+        # 2. no packagepath (basic module)
+        # 3. extensions, python modules
+        # 4. with/without zipfile
+        # 5. files that aren't python modules/extensions
+        self.fail("find_all_submodules")
+
+    @expectedFailure
+    def test_import_module(self):
+        self.fail("import_module")
+
+    @expectedFailure
+    def test_load_module(self):
+        self.fail("load_module")
+
+    @expectedFailure
+    def test_safe_import_hook(self):
+        self.fail("safe_import_hook")
+
+    @expectedFailure
+    def test_scan_code(self):
+        mod = modulegraph.Node('root')
+
+        graph = modulegraph.ModuleGraph()
+        code = compile('', '<test>', 'exec', 0, False)
+        graph.scan_code(code, mod)
+        self.assertEqual(list(graph.nodes()), [])
+
+        node_map = {}
+        def _safe_import(name, mod, fromlist, level):
+            if name in node_map:
+                node = node_map[name]
+            else:
+                node = modulegraph.Node(name)
+            node_map[name] = node
+            return [node]
+
+        graph = modulegraph.ModuleGraph()
+        graph._safe_import_hook = _safe_import
+
+        code = compile(textwrap.dedent('''\
+            import sys
+            import os.path
+
+            def testfunc():
+                import shutil
+            '''), '<test>', 'exec', 0, False)
+        graph.scan_code(code, mod)
+        modules = [node.identifier for node in graph.nodes()]
+        self.assertEqual(set(node_map), set(['sys', 'os.path', 'shutil']))
+
+
+        # from module import a, b, c
+        # from module import *
+        #  both:
+        #   -> with/without globals
+        #   -> with/without modules in globals (e.g,
+        #       from os import * adds dependency to os.path)
+        # from .module import a
+        # from ..module import a
+        #   -> check levels
+        # import name
+        # import a.b
+        #   -> should add dependency to a
+        # try to build case where commented out
+        # code would behave different than current code
+        # (Carbon.SomeMod contains 'import Sibling' seems
+        # to cause difference in real code)
+
+        self.fail("actual test needed")
+
+
+
+    @expectedFailure
+    def test_load_package(self):
+        self.fail("load_package")
+
+    def test_find_module(self):
+        record = []
+        def mock_finder(name, path):
+            record.append((name, path))
+            return saved_finder(name, path)
+
+        saved_finder = modulegraph.find_module
+        try:
+            modulegraph.find_module = mock_finder
+
+            graph = modulegraph.ModuleGraph()
+            m = graph._find_module('sys', None)
+            self.assertEqual(record, [])
+            self.assertEqual(m, (None, None, ("", "", imp.C_BUILTIN)))
+
+            modulegraph.find_module = saved_finder
+            xml = graph.import_hook("xml")[0]
+            self.assertEqual(xml.identifier, 'xml')
+            modulegraph.find_module = mock_finder
+
+            self.assertRaises(ImportError, graph._find_module, 'xml', None)
+
+            self.assertEqual(record, [])
+            m = graph._find_module('shutil', None)
+            self.assertEqual(record, [
+                ('shutil', graph.path),
+            ])
+            self.assertTrue(isinstance(m, tuple))
+            self.assertEqual(len(m), 3)
+            self.assertTrue(hasattr(m[0], 'read'))
+            self.assertIsInstance(m[0].read(), str)
+            srcfn = shutil.__file__
+            if srcfn.endswith('.pyc'):
+                srcfn = srcfn[:-1]
+            self.assertEqual(m[1], srcfn)
+            self.assertEqual(m[2], ('.py', 'rU', imp.PY_SOURCE))
+            m[0].close()
+
+            m2 = graph._find_module('shutil', None)
+            self.assertEqual(m[1:], m2[1:])
+            m2[0].close()
+
+
+            record[:] = []
+            m = graph._find_module('sax', xml.packagepath, xml)
+            self.assertEqual(m,
+                    (None, os.path.join(os.path.dirname(xml.filename), 'sax'),
+                    ('', '', imp.PKG_DIRECTORY)))
+            self.assertEqual(record, [
+                ('sax', xml.packagepath),
+            ])
+            if m[0] is not None: m[0].close()
+
+        finally:
+            modulegraph.find_module = saved_finder
+
+    @expectedFailure
+    def test_create_xref(self):
+        self.fail("create_xref")
+
+    @expectedFailure
+    def test_itergraphreport(self):
+        self.fail("itergraphreport")
+
+    def test_report(self):
+        graph = modulegraph.ModuleGraph()
+
+        saved_stdout = sys.stdout
+        try:
+            fp = sys.stdout = StringIO()
+            graph.report()
+            lines = fp.getvalue().splitlines()
+            fp.close()
+
+            self.assertEqual(len(lines), 3)
+            self.assertEqual(lines[0], '')
+            self.assertEqual(lines[1], 'Class           Name                      File')
+            self.assertEqual(lines[2], '-----           ----                      ----')
+
+            fp = sys.stdout = StringIO()
+            graph._safe_import_hook('os', None, ())
+            graph._safe_import_hook('sys', None, ())
+            graph._safe_import_hook('nomod', None, ())
+            graph.report()
+            lines = fp.getvalue().splitlines()
+            fp.close()
+
+            self.assertEqual(lines[0], '')
+            self.assertEqual(lines[1], 'Class           Name                      File')
+            self.assertEqual(lines[2], '-----           ----                      ----')
+            expected = []
+            for n in graph.flatten():
+                if n.filename:
+                    expected.append([type(n).__name__, n.identifier, n.filename])
+                else:
+                    expected.append([type(n).__name__, n.identifier])
+
+            expected.sort()
+            actual = [item.split() for item in lines[3:]]
+            actual.sort()
+            self.assertEqual(expected, actual)
+
+
+        finally:
+            sys.stdout = saved_stdout
+
+    def test_graphreport(self):
+
+        def my_iter(flatpackages="packages"):
+            yield "line1\n"
+            yield str(flatpackages) + "\n"
+            yield "line2\n"
+
+        graph = modulegraph.ModuleGraph()
+        graph.itergraphreport = my_iter
+
+        fp = StringIO()
+        graph.graphreport(fp)
+        self.assertEqual(fp.getvalue(), "line1\n()\nline2\n")
+
+        fp = StringIO()
+        graph.graphreport(fp, "deps")
+        self.assertEqual(fp.getvalue(), "line1\ndeps\nline2\n")
+
+        saved_stdout = sys.stdout
+        try:
+            sys.stdout = fp = StringIO()
+            graph.graphreport()
+            self.assertEqual(fp.getvalue(), "line1\n()\nline2\n")
+
+        finally:
+            sys.stdout = saved_stdout
+
+
+    def test_replace_paths_in_code(self):
+        graph = modulegraph.ModuleGraph(replace_paths=[
+                ('path1', 'path2'),
+                ('path3/path5', 'path4'),
+            ])
+
+        co = compile(textwrap.dedent("""
+        [x for x in range(4)]
+        """), "path4/index.py", 'exec', 0, 1)
+        co = graph._replace_paths_in_code(co)
+        self.assertEqual(co.co_filename, 'path4/index.py')
+
+        co = compile(textwrap.dedent("""
+        [x for x in range(4)]
+        (x for x in range(4))
+        """), "path1/index.py", 'exec', 0, 1)
+        self.assertEqual(co.co_filename, 'path1/index.py')
+        co = graph._replace_paths_in_code(co)
+        self.assertEqual(co.co_filename, 'path2/index.py')
+        for c in co.co_consts:
+            if isinstance(c, type(co)):
+                self.assertEqual(c.co_filename, 'path2/index.py')
+
+        co = compile(textwrap.dedent("""
+        [x for x in range(4)]
+        """), "path3/path4/index.py", 'exec', 0, 1)
+        co = graph._replace_paths_in_code(co)
+        self.assertEqual(co.co_filename, 'path3/path4/index.py')
+
+        co = compile(textwrap.dedent("""
+        [x for x in range(4)]
+        """), "path3/path5.py", 'exec', 0, 1)
+        co = graph._replace_paths_in_code(co)
+        self.assertEqual(co.co_filename, 'path3/path5.py')
+
+        co = compile(textwrap.dedent("""
+        [x for x in range(4)]
+        """), "path3/path5/index.py", 'exec', 0, 1)
+        co = graph._replace_paths_in_code(co)
+        self.assertEqual(co.co_filename, 'path4/index.py')
+
+    def test_createReference(self):
+        graph = modulegraph.ModuleGraph()
+        n1 = modulegraph.Node('n1')
+        n2 = modulegraph.Node('n2')
+        graph.addNode(n1)
+        graph.addNode(n2)
+
+        graph.createReference(n1, n2)
+        outs, ins = map(list, graph.get_edges(n1))
+        self.assertEqual(outs, [n2])
+        self.assertEqual(ins, [])
+        outs, ins = map(list, graph.get_edges(n2))
+        self.assertEqual(outs, [])
+        self.assertEqual(ins, [n1])
+
+        e = graph.graph.edge_by_node('n1', 'n2')
+        self.assertIsInstance(e, int)
+        self.assertEqual(graph.graph.edge_data(e), 'direct')
+
+    def test_create_xref(self):
+        # XXX: This test is far from optimal, it just ensures
+        # that all code is exercised to catch small bugs and
+        # py3k issues without verifying that the code actually
+        # works....
+        graph = modulegraph.ModuleGraph()
+        if __file__.endswith('.py'):
+            graph.run_script(__file__)
+        else:
+            graph.run_script(__file__[:-1])
+
+        graph.import_hook('os')
+        graph.import_hook('xml.etree')
+        graph.import_hook('unittest')
+
+        fp = StringIO()
+        graph.create_xref(out=fp)
+
+        data = fp.getvalue()
+        r = ET.fromstring(data)
+
+    def test_itergraphreport(self):
+        # XXX: This test is far from optimal, it just ensures
+        # that all code is exercised to catch small bugs and
+        # py3k issues without verifying that the code actually
+        # works....
+        graph = modulegraph.ModuleGraph()
+        if __file__.endswith('.py'):
+            graph.run_script(__file__)
+        else:
+            graph.run_script(__file__[:-1])
+        graph.import_hook('os')
+        graph.import_hook('xml.etree')
+        graph.import_hook('unittest')
+        graph.import_hook('distutils.command.build')
+
+        fp = StringIO()
+        list(graph.itergraphreport())
+
+        # XXX: platpackages isn't implemented, and is undocumented hence
+        # it is unclear what this is inteded to be...
+        #list(graph.itergraphreport(flatpackages=...))
+
+
+
+
+class CompatTests (unittest.TestCase):
+    def test_Bchr(self):
+        v = modulegraph._Bchr(ord('A'))
+        if sys.version_info[0] == 2:
+            self.assertTrue(isinstance(v, bytes))
+            self.assertEqual(v, b'A')
+        else:
+            self.assertTrue(isinstance(v, int))
+            self.assertEqual(v, ord('A'))
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/test_pep420_nspkg.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/test_pep420_nspkg.py
new file mode 100644
index 0000000..a20c981
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/test_pep420_nspkg.py
@@ -0,0 +1,220 @@
+"""
+Tests that deal with pep420 namespace packages.
+
+PEP 420 is new in Python 3.3
+"""
+import os
+import shutil
+import sys
+import subprocess
+import textwrap
+
+if sys.version_info[:2] <= (2,6):
+    import unittest2 as unittest
+else:
+    import unittest
+
+from modulegraph import modulegraph
+
+gRootDir = os.path.dirname(os.path.abspath(__file__))
+gSrcDir = os.path.join(gRootDir, 'testpkg-pep420-namespace')
+
+if sys.version_info[:2] >= (3,3):
+
+    class TestPythonBehaviour (unittest.TestCase):
+        def importModule(self, name):
+            test_dir1 = os.path.join(gSrcDir, 'path1')
+            test_dir2 = os.path.join(gSrcDir, 'path2')
+            if '.' in name:
+                script = textwrap.dedent("""\
+                    import site
+                    site.addsitedir(%r)
+                    site.addsitedir(%r)
+                    try:
+                        import %s
+                    except ImportError:
+                        import %s
+                    print (%s.__name__)
+                """) %(test_dir1, test_dir2, name, name.rsplit('.', 1)[0], name)
+            else:
+                script = textwrap.dedent("""\
+                    import site
+                    site.addsitedir(%r)
+                    site.addsitedir(%r)
+                    import %s
+                    print (%s.__name__)
+                """) %(test_dir1, test_dir2, name, name)
+
+            p = subprocess.Popen([sys.executable, '-c', script],
+                    stdout=subprocess.PIPE,
+                    stderr=subprocess.STDOUT,
+                    cwd=os.path.join(
+                        os.path.dirname(os.path.abspath(__file__)),
+                        'testpkg-relimport'),
+            )
+            data = p.communicate()[0]
+            if sys.version_info[0] != 2:
+                data = data.decode('UTF-8')
+            data = data.strip()
+            if data.endswith(' refs]'):
+                data = data.rsplit('\n', 1)[0].strip()
+
+            sts = p.wait()
+
+            if sts != 0:
+                print (data)
+                self.fail("import of %r failed"%(name,))
+
+            return data
+
+        def testToplevel(self):
+            m = self.importModule('package.sub1')
+            self.assertEqual(m, 'package.sub1')
+
+            m = self.importModule('package.sub2')
+            self.assertEqual(m, 'package.sub2')
+
+        def testSub(self):
+            m = self.importModule('package.subpackage.sub')
+            self.assertEqual(m, 'package.subpackage.sub')
+
+            m = self.importModule('package.nspkg.mod')
+            self.assertEqual(m, 'package.nspkg.mod')
+
+    class TestModuleGraphImport (unittest.TestCase):
+        if not hasattr(unittest.TestCase, 'assertIsInstance'):
+            def assertIsInstance(self, value, types):
+                if not isinstance(value, types):
+                    self.fail("%r is not an instance of %r", value, types)
+
+        def setUp(self):
+            self.mf = modulegraph.ModuleGraph(path=[
+                    os.path.join(gSrcDir, 'path1'),
+                    os.path.join(gSrcDir, 'path2'),
+                ] + sys.path)
+
+
+        def testRootPkg(self):
+            self.mf.import_hook('package')
+
+            node = self.mf.findNode('package')
+            self.assertIsInstance(node, modulegraph.NamespacePackage)
+            self.assertEqual(node.identifier, 'package')
+            self.assertEqual(node.filename, '-')
+
+        def testRootPkgModule(self):
+            self.mf.import_hook('package.sub1')
+
+            node = self.mf.findNode('package.sub1')
+            self.assertIsInstance(node, modulegraph.SourceModule)
+            self.assertEqual(node.identifier, 'package.sub1')
+
+            self.mf.import_hook('package.sub2')
+            node = self.mf.findNode('package.sub2')
+            self.assertIsInstance(node, modulegraph.SourceModule)
+            self.assertEqual(node.identifier, 'package.sub2')
+
+        def testSubRootPkgModule(self):
+            self.mf.import_hook('package.subpackage.sub')
+
+            node = self.mf.findNode('package.subpackage.sub')
+            self.assertIsInstance(node, modulegraph.SourceModule)
+            self.assertEqual(node.identifier, 'package.subpackage.sub')
+
+            node = self.mf.findNode('package')
+            self.assertIsInstance(node, modulegraph.NamespacePackage)
+
+            self.mf.import_hook('package.nspkg.mod')
+            node = self.mf.findNode('package.nspkg.mod')
+            self.assertIsInstance(node, modulegraph.SourceModule)
+            self.assertEqual(node.identifier, 'package.nspkg.mod')
+
+else:
+    # Check that PEP 420 is not implemented in python 3.2 and earlier
+    # (and that modulegraph also doesn't do this)
+
+    class TestPythonBehaviour (unittest.TestCase):
+        def importModule(self, name):
+            test_dir1 = os.path.join(gSrcDir, 'path1')
+            test_dir2 = os.path.join(gSrcDir, 'path2')
+            if '.' in name:
+                script = textwrap.dedent("""\
+                    import site
+                    site.addsitedir(%r)
+                    site.addsitedir(%r)
+                    try:
+                        import %s
+                    except ImportError:
+                        import %s
+                    print (%s.__name__)
+                """) %(test_dir1, test_dir2, name, name.rsplit('.', 1)[0], name)
+            else:
+                script = textwrap.dedent("""\
+                    import site
+                    site.addsitedir(%r)
+                    site.addsitedir(%r)
+                    import %s
+                    print (%s.__name__)
+                """) %(test_dir1, test_dir2, name, name)
+
+            p = subprocess.Popen([sys.executable, '-c', script],
+                    stdout=subprocess.PIPE,
+                    stderr=subprocess.STDOUT,
+                    cwd=os.path.join(
+                        os.path.dirname(os.path.abspath(__file__)),
+                        'testpkg-relimport'),
+            )
+            data = p.communicate()[0]
+            if sys.version_info[0] != 2:
+                data = data.decode('UTF-8')
+            data = data.strip()
+            if data.endswith(' refs]'):
+                data = data.rsplit('\n', 1)[0].strip()
+
+            sts = p.wait()
+
+            if sts != 0:
+                raise ImportError(name)
+
+            return data
+
+        def testToplevel(self):
+            m = self.importModule('sys')
+            self.assertEqual(m, 'sys')
+
+            self.assertRaises(ImportError, self.importModule, 'package.sub1')
+            self.assertRaises(ImportError, self.importModule, 'package.sub2')
+
+        def testSub(self):
+            self.assertRaises(ImportError, self.importModule, 'package.subpackage.sub')
+
+    class TestModuleGraphImport (unittest.TestCase):
+        if not hasattr(unittest.TestCase, 'assertIsInstance'):
+            def assertIsInstance(self, value, types):
+                if not isinstance(value, types):
+                    self.fail("%r is not an instance of %r", value, types)
+
+        def setUp(self):
+            self.mf = modulegraph.ModuleGraph(path=[
+                    os.path.join(gSrcDir, 'path1'),
+                    os.path.join(gSrcDir, 'path2'),
+                ] + sys.path)
+
+
+        def testRootPkg(self):
+            self.assertRaises(ImportError, self.mf.import_hook, 'package')
+
+            node = self.mf.findNode('package')
+            self.assertIs(node, None)
+
+        def testRootPkgModule(self):
+            self.assertRaises(ImportError, self.mf.import_hook, 'package.sub1')
+
+            node = self.mf.findNode('package.sub1')
+            self.assertIs(node, None)
+
+            node = self.mf.findNode('package.sub2')
+            self.assertIs(node, None)
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/test_pycompat_pkg.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/test_pycompat_pkg.py
new file mode 100644
index 0000000..b5dcdb7
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/test_pycompat_pkg.py
@@ -0,0 +1,60 @@
+"""
+Test for import machinery
+"""
+import unittest
+import sys
+import textwrap
+import subprocess
+import os
+from modulegraph import modulegraph
+
+class TestModuleGraphImport (unittest.TestCase):
+    if not hasattr(unittest.TestCase, 'assertIsInstance'):
+        def assertIsInstance(self, value, types):
+            if not isinstance(value, types):
+                self.fail("%r is not an instance of %r"%(value, types))
+
+    def test_compat(self):
+        root = os.path.join(
+                os.path.dirname(os.path.abspath(__file__)),
+                'testpkg-compatmodule')
+        mf = modulegraph.ModuleGraph(path=[ root ] + sys.path)
+        mf.import_hook('pkg.api')
+
+        node = mf.findNode('pkg')
+        self.assertIsInstance(node, modulegraph.Package)
+
+        node = mf.findNode('pkg.api')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+
+        if sys.version_info[0] == 2:
+            node = mf.findNode('pkg.api2')
+            self.assertIsInstance(node, modulegraph.SourceModule)
+
+            node = mf.findNode('pkg.api3')
+            self.assertIsInstance(node, modulegraph.InvalidSourceModule)
+
+            node = mf.findNode('http.client')
+            self.assertIs(node, None)
+
+            node = mf.findNode('urllib2')
+            self.assertIsInstance(node, modulegraph.SourceModule)
+
+        else:
+            node = mf.findNode('pkg.api2')
+            self.assertIsInstance(node, modulegraph.InvalidSourceModule)
+
+            node = mf.findNode('pkg.api3')
+            self.assertIsInstance(node, modulegraph.SourceModule)
+
+            node = mf.findNode('http.client')
+            self.assertIsInstance(node, modulegraph.SourceModule)
+
+            node = mf.findNode('urllib2')
+            self.assertIs(node, None)
+
+
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/test_relimport2.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/test_relimport2.py
new file mode 100644
index 0000000..0a465a2
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/test_relimport2.py
@@ -0,0 +1,45 @@
+"""
+Test for import machinery
+"""
+import unittest
+import sys
+import textwrap
+import subprocess
+import os
+from modulegraph import modulegraph
+
+class TestModuleGraphImport (unittest.TestCase):
+    if not hasattr(unittest.TestCase, 'assertIsInstance'):
+        def assertIsInstance(self, value, types):
+            if not isinstance(value, types):
+                self.fail("%r is not an instance of %r"%(value, types))
+
+    def setUp(self):
+        self.root = os.path.join(
+                os.path.dirname(os.path.abspath(__file__)),
+                'testpkg-relimport2')
+        self.mf = modulegraph.ModuleGraph(path=[ self.root ] + sys.path)
+
+
+    def test_init_as_script(self):
+        self.mf.run_script(os.path.join(self.root, 'pkg/__init__.py'))
+        n = self.mf.findNode('mod1')
+        self.assertIs(n, None)
+
+        n = self.mf.findNode('mod2')
+        self.assertIsInstance(n, modulegraph.MissingModule)
+
+    def test_subpkg_bad_import(self):
+        self.mf.import_hook('pkg.sub')
+
+        n = self.mf.findNode('toplevel')
+        self.assertIs(n, None)
+
+        n = self.mf.findNode('pkg.mod1')
+        self.assertIsInstance(n, modulegraph.SourceModule)
+
+        n = self.mf.findNode('pkg.mod3')
+        self.assertIsInstance(n, modulegraph.SourceModule)
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/test_setuptools_nspkg.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/test_setuptools_nspkg.py
new file mode 100644
index 0000000..87f845c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/test_setuptools_nspkg.py
@@ -0,0 +1,147 @@
+"""
+Tests that deal with setuptools namespace
+packages, and in particular the installation
+flavour used by pip
+"""
+import os
+import shutil
+import sys
+import subprocess
+import unittest
+import textwrap
+
+from modulegraph import modulegraph
+
+gRootDir = os.path.dirname(os.path.abspath(__file__))
+gSrcDir = os.path.join(gRootDir, 'testpkg-setuptools-namespace')
+
+def install_testpkg(test_dir):
+    p = subprocess.Popen([
+        sys.executable, 'setup.py', 'install',
+            '--install-lib', test_dir,
+            '--single-version-externally-managed',
+            '--record', os.path.join(test_dir, 'record.lst'),
+        ], cwd=gSrcDir, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+
+    data = p.communicate()[0]
+
+    exit = p.wait()
+    return exit
+
+
+class TestPythonBehaviour (unittest.TestCase):
+    def setUp(self):
+        test_dir = os.path.join(gRootDir, 'test.dir')
+        if os.path.exists(test_dir):
+            shutil.rmtree(test_dir)
+
+        os.mkdir(test_dir)
+        exit = install_testpkg(test_dir)
+        self.assertEqual(exit, 0)
+
+    def tearDown(self):
+        test_dir = os.path.join(gRootDir, 'test.dir')
+        if os.path.exists(test_dir):
+            shutil.rmtree(test_dir)
+
+    def importModule(self, name):
+        test_dir = os.path.join(gRootDir, 'test.dir')
+        if '.' in name:
+            script = textwrap.dedent("""\
+                import site
+                site.addsitedir(%r)
+                try:
+                    import %s
+                except ImportError:
+                    import %s
+                print (%s.__name__)
+            """) %(test_dir, name, name.rsplit('.', 1)[0], name)
+        else:
+            script = textwrap.dedent("""\
+                import site
+                site.addsitedir(%r)
+                import %s
+                print (%s.__name__)
+            """) %(test_dir, name, name)
+
+        p = subprocess.Popen([sys.executable, '-c', script],
+                stdout=subprocess.PIPE,
+                stderr=subprocess.STDOUT,
+                cwd=os.path.join(
+                    os.path.dirname(os.path.abspath(__file__)),
+                    'testpkg-relimport'),
+        )
+        data = p.communicate()[0]
+        if sys.version_info[0] != 2:
+            data = data.decode('UTF-8')
+        data = data.strip()
+        if data.endswith(' refs]'):
+            data = data.rsplit('\n', 1)[0].strip()
+
+        sts = p.wait()
+
+        if sts != 0:
+            print (data)
+            self.fail("import of %r failed"%(name,))
+
+        return data
+
+    def testToplevel(self):
+        m = self.importModule('nspkg.module')
+        self.assertEqual(m, 'nspkg.module')
+
+    def testSub(self):
+        m = self.importModule('nspkg.nssubpkg.sub')
+        self.assertEqual(m, 'nspkg.nssubpkg.sub')
+
+class TestModuleGraphImport (unittest.TestCase):
+    if not hasattr(unittest.TestCase, 'assertIsInstance'):
+        def assertIsInstance(self, value, types):
+            if not isinstance(value, types):
+                self.fail("%r is not an instance of %r", value, types)
+
+    def setUp(self):
+        test_dir = os.path.join(gRootDir, 'test.dir')
+        if os.path.exists(test_dir):
+            shutil.rmtree(test_dir)
+
+        os.mkdir(test_dir)
+        exit = install_testpkg(test_dir)
+        self.assertEqual(exit, 0)
+
+        self.mf = modulegraph.ModuleGraph(path=[ test_dir ] + sys.path)
+
+    def tearDown(self):
+        test_dir = os.path.join(gRootDir, 'test.dir')
+        if os.path.exists(test_dir):
+            shutil.rmtree(test_dir)
+
+    def testRootPkg(self):
+        self.mf.import_hook('nspkg')
+
+        node = self.mf.findNode('nspkg')
+        self.assertIsInstance(node, modulegraph.NamespacePackage)
+        self.assertEqual(node.identifier, 'nspkg')
+        self.assertEqual(node.filename, '-')
+
+    def testRootPkgModule(self):
+        self.mf.import_hook('nspkg.module')
+
+        node = self.mf.findNode('nspkg.module')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        self.assertEqual(node.identifier, 'nspkg.module')
+
+    def testSubRootPkgModule(self):
+        self.mf.import_hook('nspkg.nssubpkg.sub')
+
+        node = self.mf.findNode('nspkg.nssubpkg.sub')
+        self.assertIsInstance(node, modulegraph.SourceModule)
+        self.assertEqual(node.identifier, 'nspkg.nssubpkg.sub')
+
+
+        node = self.mf.findNode('nspkg')
+        self.assertIsInstance(node, modulegraph.NamespacePackage)
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/test_util.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/test_util.py
new file mode 100644
index 0000000..eafe43e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/test_util.py
@@ -0,0 +1,59 @@
+import unittest
+import encodings
+import encodings.aliases
+from modulegraph import util
+import sys
+
+try:
+    from io import BytesIO
+except ImportError:
+    from cStringIO import StringIO as BytesIO
+
+class TestUtil (unittest.TestCase):
+    def test_imp_find_module(self):
+        fn = util.imp_find_module('encodings.aliases')[1]
+        self.assertTrue(encodings.aliases.__file__.startswith(fn))
+
+    def test_imp_walk(self):
+        imps = list(util.imp_walk('encodings.aliases'))
+        self.assertEqual(len(imps), 2)
+
+        self.assertEqual(imps[0][0], 'encodings')
+        self.assertTrue(encodings.__file__.startswith(imps[0][1][1]))
+
+        self.assertEqual(imps[1][0], 'aliases')
+        self.assertTrue(encodings.aliases.__file__.startswith(imps[1][1][1]))
+
+        # Close all files, avoid warning by unittest
+        for i in imps:
+            if i[1][0] is not None:
+                i[1][0].close()
+
+
+    def test_guess_encoding(self):
+        fp = BytesIO(b"# coding: utf-8")
+        self.assertEqual(util.guess_encoding(fp), "utf-8")
+
+        fp = BytesIO(b"\n# coding: utf-8")
+        self.assertEqual(util.guess_encoding(fp), "utf-8")
+
+        fp = BytesIO(b"# coding: latin-1")
+        self.assertEqual(util.guess_encoding(fp), "latin-1")
+
+        fp = BytesIO(b"\n# coding: latin-1")
+        self.assertEqual(util.guess_encoding(fp), "latin-1")
+
+        fp = BytesIO(b"#!/usr/bin/env/python\n# vim: set fileencoding=latin-1 :")
+        self.assertEqual(util.guess_encoding(fp), "latin-1")
+
+        fp = BytesIO(b"\n\n\n# coding: latin-1")
+        if sys.version_info[0] == 2:
+            self.assertEqual(util.guess_encoding(fp), "ascii")
+        else:
+            self.assertEqual(util.guess_encoding(fp), "utf-8")
+
+        del fp
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/test_zipio.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/test_zipio.py
new file mode 100644
index 0000000..81000bd
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/test_zipio.py
@@ -0,0 +1,218 @@
+from modulegraph import zipio
+import os
+import time
+import sys
+
+if sys.version_info[:2] <= (2,6):
+    import unittest2 as unittest
+
+else:
+    import unittest
+
+TESTDATA=os.path.join(
+        os.path.dirname(os.path.abspath(__file__)),
+        'testdata')
+
+class TestModuleGraph (unittest.TestCase):
+    def test_locating(self):
+        # Private function
+        self.assertEqual(zipio._locate('/usr/bin/ditto'), ('/usr/bin/ditto', None))
+        self.assertEqual(zipio._locate('/usr/bin/ditto/bar'), ('/usr/bin/ditto', 'bar'))
+        self.assertEqual(zipio._locate('/usr/bin/ditto/foo/bar///bar/'), ('/usr/bin/ditto', 'foo/bar/bar'))
+        self.assertEqual(zipio._locate('/usr/bin/ditto///foo/bar///bar/'), ('/usr/bin/ditto', 'foo/bar/bar'))
+        self.assertRaises(IOError, zipio._locate, '/usr/bin/ditto.bar')
+        self.assertRaises(IOError, zipio._locate, '/foo/bar/baz.txt')
+
+    def test_open(self):
+        # 1. Regular file
+        fp = zipio.open(os.path.join(TESTDATA, 'test.txt'), 'r')
+        data = fp.read()
+        fp.close()
+        self.assertEqual(data, 'This is test.txt\n')
+
+        if sys.version_info[0] == 3:
+            fp = zipio.open(os.path.join(TESTDATA, 'test.txt'), 'rb')
+            data = fp.read()
+            fp.close()
+            self.assertEqual(data, b'This is test.txt\n')
+
+        # 2. File inside zipfile
+        fp = zipio.open(os.path.join(TESTDATA, 'zipped.egg', 'test.txt'), 'r')
+        data = fp.read()
+        fp.close()
+        self.assertEqual(data, 'Zipped up test.txt\n')
+
+        if sys.version_info[0] == 3:
+            fp = zipio.open(os.path.join(TESTDATA, 'zipped.egg', 'test.txt'), 'rb')
+            data = fp.read()
+            fp.close()
+            self.assertEqual(data, b'Zipped up test.txt\n')
+
+        # 3. EXC: Directory inside zipfile
+        self.assertRaises(IOError, zipio.open, os.path.join(TESTDATA, 'zipped.egg', 'subdir'))
+        self.assertRaises(IOError, zipio.open, os.path.join(TESTDATA, 'zipped.egg', 'subdir2'))
+        self.assertRaises(IOError, zipio.open, os.path.join(TESTDATA, 'zipped.egg', 'subdir2/subdir'))
+        self.assertRaises(IOError, zipio.open, os.path.join(TESTDATA, 'zipped.egg', 'subdir3'))
+        # TODO: Add subdir4/file.txt, without directory entry
+        self.assertRaises(IOError, zipio.open, os.path.join(TESTDATA, 'zipped.egg', 'subdir4'))
+
+        # 4. EXC: No such file in zipfile
+        self.assertRaises(IOError, zipio.open, os.path.join(TESTDATA, 'zipped.egg', 'no-such-file'))
+        self.assertRaises(IOError, zipio.open, os.path.join(TESTDATA, 'zipped.egg', 'subdir/no-such-file'))
+
+        # 5. EXC: No such regular file
+        self.assertRaises(IOError, zipio.open, os.path.join(TESTDATA, 'no-such-file.txt'))
+
+        # 6. EXC: Open r/w
+        self.assertRaises(IOError, zipio.open, os.path.join(TESTDATA, 'test.txt'), 'w')
+        self.assertRaises(IOError, zipio.open, os.path.join(TESTDATA, 'test.txt'), 'a')
+        self.assertRaises(IOError, zipio.open, os.path.join(TESTDATA, 'test.txt'), 'r+')
+        self.assertRaises(IOError, zipio.open, os.path.join(TESTDATA, 'test.txt'), 'w+')
+        self.assertRaises(IOError, zipio.open, os.path.join(TESTDATA, 'test.txt'), 'a+')
+
+    def test_listdir(self):
+        # 1. Regular directory
+        self.assertEqual(set(os.listdir(os.path.join(TESTDATA, 'subdir'))), set(['file1.txt', 'file2.txt']))
+
+        # 2. Zipfile with files in directory
+        self.assertEqual(set(zipio.listdir(os.path.join(TESTDATA, 'zipped.egg'))), set([
+            'test.txt', 'subdir', 'subdir2', 'subdir3', 'subdir4']))
+
+        # 3. Zipfile with files in subdirectory
+        self.assertEqual(set(zipio.listdir(os.path.join(TESTDATA, 'zipped.egg', 'subdir'))), set(['file1.txt', 'file2.txt']))
+        self.assertEqual(set(zipio.listdir(os.path.join(TESTDATA, 'zipped.egg', 'subdir2'))), set(['subdir']))
+        self.assertEqual(set(zipio.listdir(os.path.join(TESTDATA, 'zipped.egg', 'subdir4', 'subdir6'))), set(['mydir']))
+
+        # 4. Zipfile with entry for directory, no files
+        self.assertEqual(set(zipio.listdir(os.path.join(TESTDATA, 'zipped.egg', 'subdir3'))), set([]))
+
+        # 5. EXC: Zipfile without directory
+        self.assertRaises(IOError, zipio.listdir, os.path.join(TESTDATA, 'zipped.egg', 'subdir10'))
+
+        # 6. EXC: Regular directory doesn't exist
+        self.assertRaises(IOError, zipio.listdir, os.path.join(TESTDATA, 'subdir10'))
+
+    def test_isfile(self):
+        self.assertTrue(zipio.isfile(os.path.join(TESTDATA, 'test.txt')))
+        self.assertFalse(zipio.isfile(os.path.join(TESTDATA, 'subdir')))
+        self.assertRaises(IOError, zipio.isfile, os.path.join(TESTDATA, 'no-such-file'))
+        self.assertFalse(zipio.isfile(os.path.join(TESTDATA, 'zipped.egg')))
+        self.assertFalse(zipio.isfile(os.path.join(TESTDATA, 'zipped.egg', 'subdir4')))
+        self.assertTrue(zipio.isfile(os.path.join(TESTDATA, 'zipped.egg', 'test.txt')))
+        self.assertFalse(zipio.isfile(os.path.join(TESTDATA, 'zipped.egg', 'subdir')))
+        self.assertRaises(IOError, zipio.isfile, os.path.join(TESTDATA, 'zipped.egg', 'no-such-file'))
+        self.assertTrue(zipio.isfile(os.path.join(TESTDATA, 'zipped.egg', 'subdir2', 'subdir', 'file1.txt')))
+
+    def test_isdir(self):
+        self.assertTrue(zipio.isdir(TESTDATA))
+        self.assertFalse(zipio.isdir(os.path.join(TESTDATA, 'test.txt')))
+        self.assertTrue(zipio.isdir(os.path.join(TESTDATA, 'zipped.egg')))
+        self.assertTrue(zipio.isdir(os.path.join(TESTDATA, 'zipped.egg', 'subdir')))
+        self.assertTrue(zipio.isdir(os.path.join(TESTDATA, 'zipped.egg', 'subdir2/subdir')))
+        self.assertTrue(zipio.isdir(os.path.join(TESTDATA, 'zipped.egg', 'subdir4')))
+        self.assertFalse(zipio.isdir(os.path.join(TESTDATA, 'zipped.egg', 'subdir4', 'file.txt')))
+        self.assertRaises(IOError, zipio.isdir, os.path.join(TESTDATA, 'no-such-file'))
+        self.assertRaises(IOError, zipio.isdir, os.path.join(TESTDATA, 'zipped.egg', 'no-such-file'))
+        self.assertRaises(IOError, zipio.isdir, os.path.join(TESTDATA, 'zipped.egg', 'subdir', 'no-such-file'))
+
+    def test_islink(self):
+        fn = os.path.join(TESTDATA, 'symlink')
+        os.symlink('test.txt', fn)
+        try:
+            self.assertTrue(zipio.islink(fn))
+
+        finally:
+            os.unlink(fn)
+
+        self.assertFalse(zipio.islink(os.path.join(TESTDATA, 'test.txt')))
+        self.assertFalse(zipio.islink(os.path.join(TESTDATA, 'subdir')))
+        self.assertFalse(zipio.islink(os.path.join(TESTDATA, 'zipped.egg')))
+        self.assertFalse(zipio.islink(os.path.join(TESTDATA, 'zipped.egg/subdir')))
+        self.assertFalse(zipio.islink(os.path.join(TESTDATA, 'zipped.egg/subdir4')))
+        self.assertFalse(zipio.islink(os.path.join(TESTDATA, 'zipped.egg/test.txt')))
+        self.assertFalse(zipio.islink(os.path.join(TESTDATA, 'zipped.egg/subdir/file1.txt')))
+
+        self.assertRaises(IOError, zipio.islink, os.path.join(TESTDATA, 'no-such-file'))
+        self.assertRaises(IOError, zipio.islink, os.path.join(TESTDATA, 'zipped.egg', 'no-such-file'))
+
+
+    def test_readlink(self):
+        fn = os.path.join(TESTDATA, 'symlink')
+        os.symlink('test.txt', fn)
+        try:
+            self.assertEqual(zipio.readlink(fn), 'test.txt')
+
+        finally:
+            os.unlink(fn)
+
+        self.assertRaises(OSError, zipio.readlink, os.path.join(TESTDATA, 'test.txt'))
+        self.assertRaises(OSError, zipio.readlink, os.path.join(TESTDATA, 'subdir'))
+        self.assertRaises(OSError, zipio.readlink, os.path.join(TESTDATA, 'zipped.egg'))
+        self.assertRaises(OSError, zipio.readlink, os.path.join(TESTDATA, 'zipped.egg', 'subdir4'))
+        self.assertRaises(OSError, zipio.readlink, os.path.join(TESTDATA, 'zipped.egg', 'no-such-file'))
+        self.assertRaises(OSError, zipio.readlink, os.path.join(TESTDATA, 'zipped.egg', 'subdir/no-such-file'))
+
+    def test_getmtime(self):
+        fn = os.path.join(TESTDATA, 'test.txt')
+        self.assertEqual(os.path.getmtime(fn), zipio.getmtime(fn))
+
+        fn = os.path.join(TESTDATA, 'zipped.egg')
+        self.assertEqual(os.path.getmtime(fn), zipio.getmtime(fn))
+
+        fn = os.path.join(TESTDATA, 'zipped.egg/test.txt')
+        self.assertIn(zipio.getmtime(fn), (1300193680.0, 1300222480.0))
+
+        fn = os.path.join(TESTDATA, 'zipped.egg/subdir')
+        self.assertIn(zipio.getmtime(fn), (1300193890.0, 1300222690.0))
+
+        fn = os.path.join(TESTDATA, 'zipped.egg/subdir4')
+        self.assertEqual(zipio.getmtime(fn), os.path.getmtime(os.path.join(TESTDATA, 'zipped.egg')))
+
+        self.assertRaises(IOError, zipio.getmtime, os.path.join(TESTDATA, 'no-file'))
+        self.assertRaises(IOError, zipio.getmtime, os.path.join(TESTDATA, 'zipped.egg/no-file'))
+
+    def test_contextlib(self):
+        # 1. Regular file
+        with zipio.open(os.path.join(TESTDATA, 'test.txt'), 'r') as fp:
+            data = fp.read()
+        try:
+            fp.read()
+            self.fail("file not closed")
+        except (ValueError, IOError):
+            pass
+
+        self.assertEqual(data, 'This is test.txt\n')
+
+        if sys.version_info[0] == 3:
+            with zipio.open(os.path.join(TESTDATA, 'test.txt'), 'rb') as fp:
+                data = fp.read()
+            try:
+                fp.read()
+                self.fail("file not closed")
+            except (ValueError, IOError):
+                pass
+
+            self.assertEqual(data, b'This is test.txt\n')
+
+        # 2. File inside zipfile
+        with zipio.open(os.path.join(TESTDATA, 'zipped.egg', 'test.txt'), 'r') as fp:
+            data = fp.read()
+        try:
+            fp.read()
+            self.fail("file not closed")
+        except (ValueError, IOError):
+            pass
+        self.assertEqual(data, 'Zipped up test.txt\n')
+
+        if sys.version_info[0] == 3:
+            with zipio.open(os.path.join(TESTDATA, 'zipped.egg', 'test.txt'), 'rb') as fp:
+                data = fp.read()
+            try:
+                fp.read()
+                self.fail("file not closed")
+            except (IOError, ValueError):
+                pass
+            self.assertEqual(data, b'Zipped up test.txt\n')
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.10/child/namedpkg/slave.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.10/child/namedpkg/slave.py
new file mode 100644
index 0000000..16a3d3c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.10/child/namedpkg/slave.py
@@ -0,0 +1,2 @@
+""" slave packages """
+import os
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.10/child/nameduser-1.5-py2.6-nspkg.pth b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.10/child/nameduser-1.5-py2.6-nspkg.pth
new file mode 100644
index 0000000..b020a3c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.10/child/nameduser-1.5-py2.6-nspkg.pth
@@ -0,0 +1 @@
+import sys,types,os; p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('namedpkg',)); ie = os.path.exists(os.path.join(p,'__init__.py')); m = not ie and sys.modules.setdefault('namedpkg',types.ModuleType('namedpkg')); mp = (m or []) and m.__dict__.setdefault('__path__',[]); (p not in mp) and mp.append(p)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.10/child/nameduser-1.5-py2.6.egg-info/PKG-INFO b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.10/child/nameduser-1.5-py2.6.egg-info/PKG-INFO
new file mode 100644
index 0000000..cff065a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.10/child/nameduser-1.5-py2.6.egg-info/PKG-INFO
@@ -0,0 +1,10 @@
+Metadata-Version: 1.0
+Name: nameduser
+Version: 1.5
+Summary: UNKNOWN
+Home-page: UNKNOWN
+Author: UNKNOWN
+Author-email: UNKNOWN
+License: UNKNOWN
+Description: UNKNOWN
+Platform: UNKNOWN
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.10/child/nameduser-1.5-py2.6.egg-info/SOURCES.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.10/child/nameduser-1.5-py2.6.egg-info/SOURCES.txt
new file mode 100644
index 0000000..15c6126
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.10/child/nameduser-1.5-py2.6.egg-info/SOURCES.txt
@@ -0,0 +1,8 @@
+setup.py
+namedpkg/__init__.py
+namedpkg/slave.py
+nameduser.egg-info/PKG-INFO
+nameduser.egg-info/SOURCES.txt
+nameduser.egg-info/dependency_links.txt
+nameduser.egg-info/namespace_packages.txt
+nameduser.egg-info/top_level.txt
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.10/child/nameduser-1.5-py2.6.egg-info/dependency_links.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.10/child/nameduser-1.5-py2.6.egg-info/dependency_links.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.10/child/nameduser-1.5-py2.6.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.10/child/nameduser-1.5-py2.6.egg-info/namespace_packages.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.10/child/nameduser-1.5-py2.6.egg-info/namespace_packages.txt
new file mode 100644
index 0000000..d332ce1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.10/child/nameduser-1.5-py2.6.egg-info/namespace_packages.txt
@@ -0,0 +1 @@
+namedpkg
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.10/child/nameduser-1.5-py2.6.egg-info/top_level.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.10/child/nameduser-1.5-py2.6.egg-info/top_level.txt
new file mode 100644
index 0000000..d332ce1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.10/child/nameduser-1.5-py2.6.egg-info/top_level.txt
@@ -0,0 +1 @@
+namedpkg
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.10/parent/namedpkg-1.0-py2.6-nspkg.pth b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.10/parent/namedpkg-1.0-py2.6-nspkg.pth
new file mode 100644
index 0000000..b020a3c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.10/parent/namedpkg-1.0-py2.6-nspkg.pth
@@ -0,0 +1 @@
+import sys,types,os; p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('namedpkg',)); ie = os.path.exists(os.path.join(p,'__init__.py')); m = not ie and sys.modules.setdefault('namedpkg',types.ModuleType('namedpkg')); mp = (m or []) and m.__dict__.setdefault('__path__',[]); (p not in mp) and mp.append(p)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.10/parent/namedpkg-1.0-py2.6.egg-info/PKG-INFO b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.10/parent/namedpkg-1.0-py2.6.egg-info/PKG-INFO
new file mode 100644
index 0000000..138c5fd
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.10/parent/namedpkg-1.0-py2.6.egg-info/PKG-INFO
@@ -0,0 +1,10 @@
+Metadata-Version: 1.0
+Name: namedpkg
+Version: 1.0
+Summary: UNKNOWN
+Home-page: UNKNOWN
+Author: UNKNOWN
+Author-email: UNKNOWN
+License: UNKNOWN
+Description: UNKNOWN
+Platform: UNKNOWN
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.10/parent/namedpkg-1.0-py2.6.egg-info/SOURCES.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.10/parent/namedpkg-1.0-py2.6.egg-info/SOURCES.txt
new file mode 100644
index 0000000..29dfc47
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.10/parent/namedpkg-1.0-py2.6.egg-info/SOURCES.txt
@@ -0,0 +1,8 @@
+setup.py
+namedpkg/__init__.py
+namedpkg/parent.py
+namedpkg.egg-info/PKG-INFO
+namedpkg.egg-info/SOURCES.txt
+namedpkg.egg-info/dependency_links.txt
+namedpkg.egg-info/namespace_packages.txt
+namedpkg.egg-info/top_level.txt
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.10/parent/namedpkg-1.0-py2.6.egg-info/dependency_links.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.10/parent/namedpkg-1.0-py2.6.egg-info/dependency_links.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.10/parent/namedpkg-1.0-py2.6.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.10/parent/namedpkg-1.0-py2.6.egg-info/namespace_packages.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.10/parent/namedpkg-1.0-py2.6.egg-info/namespace_packages.txt
new file mode 100644
index 0000000..d332ce1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.10/parent/namedpkg-1.0-py2.6.egg-info/namespace_packages.txt
@@ -0,0 +1 @@
+namedpkg
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.10/parent/namedpkg-1.0-py2.6.egg-info/top_level.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.10/parent/namedpkg-1.0-py2.6.egg-info/top_level.txt
new file mode 100644
index 0000000..d332ce1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.10/parent/namedpkg-1.0-py2.6.egg-info/top_level.txt
@@ -0,0 +1 @@
+namedpkg
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.10/parent/namedpkg/parent.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.10/parent/namedpkg/parent.py
new file mode 100644
index 0000000..db7354b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.10/parent/namedpkg/parent.py
@@ -0,0 +1,2 @@
+""" parent packages """
+import sys
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.12/child/namedpkg/slave.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.12/child/namedpkg/slave.py
new file mode 100644
index 0000000..16a3d3c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.12/child/namedpkg/slave.py
@@ -0,0 +1,2 @@
+""" slave packages """
+import os
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.12/child/nameduser-1.5-py2.5-nspkg.pth b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.12/child/nameduser-1.5-py2.5-nspkg.pth
new file mode 100644
index 0000000..9423238
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.12/child/nameduser-1.5-py2.5-nspkg.pth
@@ -0,0 +1 @@
+import sys,new,os; p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('namedpkg',)); ie = os.path.exists(os.path.join(p,'__init__.py')); m = not ie and sys.modules.setdefault('namedpkg',new.module('namedpkg')); mp = (m or []) and m.__dict__.setdefault('__path__',[]); (p not in mp) and mp.append(p)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.12/child/nameduser-1.5-py2.5.egg-info/PKG-INFO b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.12/child/nameduser-1.5-py2.5.egg-info/PKG-INFO
new file mode 100644
index 0000000..cff065a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.12/child/nameduser-1.5-py2.5.egg-info/PKG-INFO
@@ -0,0 +1,10 @@
+Metadata-Version: 1.0
+Name: nameduser
+Version: 1.5
+Summary: UNKNOWN
+Home-page: UNKNOWN
+Author: UNKNOWN
+Author-email: UNKNOWN
+License: UNKNOWN
+Description: UNKNOWN
+Platform: UNKNOWN
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.12/child/nameduser-1.5-py2.5.egg-info/SOURCES.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.12/child/nameduser-1.5-py2.5.egg-info/SOURCES.txt
new file mode 100644
index 0000000..15c6126
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.12/child/nameduser-1.5-py2.5.egg-info/SOURCES.txt
@@ -0,0 +1,8 @@
+setup.py
+namedpkg/__init__.py
+namedpkg/slave.py
+nameduser.egg-info/PKG-INFO
+nameduser.egg-info/SOURCES.txt
+nameduser.egg-info/dependency_links.txt
+nameduser.egg-info/namespace_packages.txt
+nameduser.egg-info/top_level.txt
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.12/child/nameduser-1.5-py2.5.egg-info/dependency_links.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.12/child/nameduser-1.5-py2.5.egg-info/dependency_links.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.12/child/nameduser-1.5-py2.5.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.12/child/nameduser-1.5-py2.5.egg-info/namespace_packages.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.12/child/nameduser-1.5-py2.5.egg-info/namespace_packages.txt
new file mode 100644
index 0000000..d332ce1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.12/child/nameduser-1.5-py2.5.egg-info/namespace_packages.txt
@@ -0,0 +1 @@
+namedpkg
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.12/child/nameduser-1.5-py2.5.egg-info/top_level.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.12/child/nameduser-1.5-py2.5.egg-info/top_level.txt
new file mode 100644
index 0000000..d332ce1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.12/child/nameduser-1.5-py2.5.egg-info/top_level.txt
@@ -0,0 +1 @@
+namedpkg
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.12/parent/namedpkg-1.0-py2.5-nspkg.pth b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.12/parent/namedpkg-1.0-py2.5-nspkg.pth
new file mode 100644
index 0000000..9423238
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.12/parent/namedpkg-1.0-py2.5-nspkg.pth
@@ -0,0 +1 @@
+import sys,new,os; p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('namedpkg',)); ie = os.path.exists(os.path.join(p,'__init__.py')); m = not ie and sys.modules.setdefault('namedpkg',new.module('namedpkg')); mp = (m or []) and m.__dict__.setdefault('__path__',[]); (p not in mp) and mp.append(p)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.12/parent/namedpkg-1.0-py2.5.egg-info/PKG-INFO b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.12/parent/namedpkg-1.0-py2.5.egg-info/PKG-INFO
new file mode 100644
index 0000000..138c5fd
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.12/parent/namedpkg-1.0-py2.5.egg-info/PKG-INFO
@@ -0,0 +1,10 @@
+Metadata-Version: 1.0
+Name: namedpkg
+Version: 1.0
+Summary: UNKNOWN
+Home-page: UNKNOWN
+Author: UNKNOWN
+Author-email: UNKNOWN
+License: UNKNOWN
+Description: UNKNOWN
+Platform: UNKNOWN
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.12/parent/namedpkg-1.0-py2.5.egg-info/SOURCES.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.12/parent/namedpkg-1.0-py2.5.egg-info/SOURCES.txt
new file mode 100644
index 0000000..29dfc47
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.12/parent/namedpkg-1.0-py2.5.egg-info/SOURCES.txt
@@ -0,0 +1,8 @@
+setup.py
+namedpkg/__init__.py
+namedpkg/parent.py
+namedpkg.egg-info/PKG-INFO
+namedpkg.egg-info/SOURCES.txt
+namedpkg.egg-info/dependency_links.txt
+namedpkg.egg-info/namespace_packages.txt
+namedpkg.egg-info/top_level.txt
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.12/parent/namedpkg-1.0-py2.5.egg-info/dependency_links.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.12/parent/namedpkg-1.0-py2.5.egg-info/dependency_links.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.12/parent/namedpkg-1.0-py2.5.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.12/parent/namedpkg-1.0-py2.5.egg-info/namespace_packages.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.12/parent/namedpkg-1.0-py2.5.egg-info/namespace_packages.txt
new file mode 100644
index 0000000..d332ce1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.12/parent/namedpkg-1.0-py2.5.egg-info/namespace_packages.txt
@@ -0,0 +1 @@
+namedpkg
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.12/parent/namedpkg-1.0-py2.5.egg-info/top_level.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.12/parent/namedpkg-1.0-py2.5.egg-info/top_level.txt
new file mode 100644
index 0000000..d332ce1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.12/parent/namedpkg-1.0-py2.5.egg-info/top_level.txt
@@ -0,0 +1 @@
+namedpkg
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.12/parent/namedpkg/parent.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.12/parent/namedpkg/parent.py
new file mode 100644
index 0000000..db7354b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/distribute-0.6.12/parent/namedpkg/parent.py
@@ -0,0 +1,2 @@
+""" parent packages """
+import sys
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/setuptools-0.6c9/child/namedpkg/slave.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/setuptools-0.6c9/child/namedpkg/slave.py
new file mode 100644
index 0000000..16a3d3c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/setuptools-0.6c9/child/namedpkg/slave.py
@@ -0,0 +1,2 @@
+""" slave packages """
+import os
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/setuptools-0.6c9/child/nameduser-1.5-py2.5-nspkg.pth b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/setuptools-0.6c9/child/nameduser-1.5-py2.5-nspkg.pth
new file mode 100644
index 0000000..9423238
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/setuptools-0.6c9/child/nameduser-1.5-py2.5-nspkg.pth
@@ -0,0 +1 @@
+import sys,new,os; p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('namedpkg',)); ie = os.path.exists(os.path.join(p,'__init__.py')); m = not ie and sys.modules.setdefault('namedpkg',new.module('namedpkg')); mp = (m or []) and m.__dict__.setdefault('__path__',[]); (p not in mp) and mp.append(p)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/setuptools-0.6c9/child/nameduser-1.5-py2.5.egg-info/PKG-INFO b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/setuptools-0.6c9/child/nameduser-1.5-py2.5.egg-info/PKG-INFO
new file mode 100644
index 0000000..cff065a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/setuptools-0.6c9/child/nameduser-1.5-py2.5.egg-info/PKG-INFO
@@ -0,0 +1,10 @@
+Metadata-Version: 1.0
+Name: nameduser
+Version: 1.5
+Summary: UNKNOWN
+Home-page: UNKNOWN
+Author: UNKNOWN
+Author-email: UNKNOWN
+License: UNKNOWN
+Description: UNKNOWN
+Platform: UNKNOWN
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/setuptools-0.6c9/child/nameduser-1.5-py2.5.egg-info/SOURCES.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/setuptools-0.6c9/child/nameduser-1.5-py2.5.egg-info/SOURCES.txt
new file mode 100644
index 0000000..15c6126
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/setuptools-0.6c9/child/nameduser-1.5-py2.5.egg-info/SOURCES.txt
@@ -0,0 +1,8 @@
+setup.py
+namedpkg/__init__.py
+namedpkg/slave.py
+nameduser.egg-info/PKG-INFO
+nameduser.egg-info/SOURCES.txt
+nameduser.egg-info/dependency_links.txt
+nameduser.egg-info/namespace_packages.txt
+nameduser.egg-info/top_level.txt
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/setuptools-0.6c9/child/nameduser-1.5-py2.5.egg-info/dependency_links.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/setuptools-0.6c9/child/nameduser-1.5-py2.5.egg-info/dependency_links.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/setuptools-0.6c9/child/nameduser-1.5-py2.5.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/setuptools-0.6c9/child/nameduser-1.5-py2.5.egg-info/namespace_packages.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/setuptools-0.6c9/child/nameduser-1.5-py2.5.egg-info/namespace_packages.txt
new file mode 100644
index 0000000..d332ce1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/setuptools-0.6c9/child/nameduser-1.5-py2.5.egg-info/namespace_packages.txt
@@ -0,0 +1 @@
+namedpkg
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/setuptools-0.6c9/child/nameduser-1.5-py2.5.egg-info/top_level.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/setuptools-0.6c9/child/nameduser-1.5-py2.5.egg-info/top_level.txt
new file mode 100644
index 0000000..d332ce1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/setuptools-0.6c9/child/nameduser-1.5-py2.5.egg-info/top_level.txt
@@ -0,0 +1 @@
+namedpkg
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/setuptools-0.6c9/parent/namedpkg-1.0-py2.5-nspkg.pth b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/setuptools-0.6c9/parent/namedpkg-1.0-py2.5-nspkg.pth
new file mode 100644
index 0000000..9423238
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/setuptools-0.6c9/parent/namedpkg-1.0-py2.5-nspkg.pth
@@ -0,0 +1 @@
+import sys,new,os; p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('namedpkg',)); ie = os.path.exists(os.path.join(p,'__init__.py')); m = not ie and sys.modules.setdefault('namedpkg',new.module('namedpkg')); mp = (m or []) and m.__dict__.setdefault('__path__',[]); (p not in mp) and mp.append(p)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/setuptools-0.6c9/parent/namedpkg-1.0-py2.5.egg-info/PKG-INFO b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/setuptools-0.6c9/parent/namedpkg-1.0-py2.5.egg-info/PKG-INFO
new file mode 100644
index 0000000..138c5fd
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/setuptools-0.6c9/parent/namedpkg-1.0-py2.5.egg-info/PKG-INFO
@@ -0,0 +1,10 @@
+Metadata-Version: 1.0
+Name: namedpkg
+Version: 1.0
+Summary: UNKNOWN
+Home-page: UNKNOWN
+Author: UNKNOWN
+Author-email: UNKNOWN
+License: UNKNOWN
+Description: UNKNOWN
+Platform: UNKNOWN
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/setuptools-0.6c9/parent/namedpkg-1.0-py2.5.egg-info/SOURCES.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/setuptools-0.6c9/parent/namedpkg-1.0-py2.5.egg-info/SOURCES.txt
new file mode 100644
index 0000000..29dfc47
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/setuptools-0.6c9/parent/namedpkg-1.0-py2.5.egg-info/SOURCES.txt
@@ -0,0 +1,8 @@
+setup.py
+namedpkg/__init__.py
+namedpkg/parent.py
+namedpkg.egg-info/PKG-INFO
+namedpkg.egg-info/SOURCES.txt
+namedpkg.egg-info/dependency_links.txt
+namedpkg.egg-info/namespace_packages.txt
+namedpkg.egg-info/top_level.txt
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/setuptools-0.6c9/parent/namedpkg-1.0-py2.5.egg-info/dependency_links.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/setuptools-0.6c9/parent/namedpkg-1.0-py2.5.egg-info/dependency_links.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/setuptools-0.6c9/parent/namedpkg-1.0-py2.5.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/setuptools-0.6c9/parent/namedpkg-1.0-py2.5.egg-info/namespace_packages.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/setuptools-0.6c9/parent/namedpkg-1.0-py2.5.egg-info/namespace_packages.txt
new file mode 100644
index 0000000..d332ce1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/setuptools-0.6c9/parent/namedpkg-1.0-py2.5.egg-info/namespace_packages.txt
@@ -0,0 +1 @@
+namedpkg
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/setuptools-0.6c9/parent/namedpkg-1.0-py2.5.egg-info/top_level.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/setuptools-0.6c9/parent/namedpkg-1.0-py2.5.egg-info/top_level.txt
new file mode 100644
index 0000000..d332ce1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/setuptools-0.6c9/parent/namedpkg-1.0-py2.5.egg-info/top_level.txt
@@ -0,0 +1 @@
+namedpkg
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/setuptools-0.6c9/parent/namedpkg/parent.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/setuptools-0.6c9/parent/namedpkg/parent.py
new file mode 100644
index 0000000..db7354b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/setuptools-0.6c9/parent/namedpkg/parent.py
@@ -0,0 +1,2 @@
+""" parent packages """
+import sys
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/child/namedpkg/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/child/namedpkg/__init__.py
new file mode 100644
index 0000000..de40ea7
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/child/namedpkg/__init__.py
@@ -0,0 +1 @@
+__import__('pkg_resources').declare_namespace(__name__)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/child/namedpkg/slave.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/child/namedpkg/slave.py
new file mode 100644
index 0000000..16a3d3c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/child/namedpkg/slave.py
@@ -0,0 +1,2 @@
+""" slave packages """
+import os
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/child/nameduser.egg-info/PKG-INFO b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/child/nameduser.egg-info/PKG-INFO
new file mode 100644
index 0000000..cff065a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/child/nameduser.egg-info/PKG-INFO
@@ -0,0 +1,10 @@
+Metadata-Version: 1.0
+Name: nameduser
+Version: 1.5
+Summary: UNKNOWN
+Home-page: UNKNOWN
+Author: UNKNOWN
+Author-email: UNKNOWN
+License: UNKNOWN
+Description: UNKNOWN
+Platform: UNKNOWN
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/child/nameduser.egg-info/SOURCES.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/child/nameduser.egg-info/SOURCES.txt
new file mode 100644
index 0000000..15c6126
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/child/nameduser.egg-info/SOURCES.txt
@@ -0,0 +1,8 @@
+setup.py
+namedpkg/__init__.py
+namedpkg/slave.py
+nameduser.egg-info/PKG-INFO
+nameduser.egg-info/SOURCES.txt
+nameduser.egg-info/dependency_links.txt
+nameduser.egg-info/namespace_packages.txt
+nameduser.egg-info/top_level.txt
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/child/nameduser.egg-info/dependency_links.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/child/nameduser.egg-info/dependency_links.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/child/nameduser.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/child/nameduser.egg-info/namespace_packages.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/child/nameduser.egg-info/namespace_packages.txt
new file mode 100644
index 0000000..d332ce1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/child/nameduser.egg-info/namespace_packages.txt
@@ -0,0 +1 @@
+namedpkg
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/child/nameduser.egg-info/top_level.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/child/nameduser.egg-info/top_level.txt
new file mode 100644
index 0000000..d332ce1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/child/nameduser.egg-info/top_level.txt
@@ -0,0 +1 @@
+namedpkg
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/child/setup.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/child/setup.py
new file mode 100644
index 0000000..e1000c5
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/child/setup.py
@@ -0,0 +1,8 @@
+from setuptools import setup
+
+setup(
+        name="nameduser",
+        version="1.5",
+        packages=["namedpkg"],
+        namespace_packages=["namedpkg"],
+)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/install.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/install.py
new file mode 100644
index 0000000..3f14e73
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/install.py
@@ -0,0 +1,54 @@
+#!/usr/bin/env python
+"""
+Script that will create a subdirectory one level up with two subdirs
+with --single-version-externally-managed namespace packages.
+
+Use this script with new versions of distribute and setuptools to ensure
+that changes in the handling of this option don't break us.
+"""
+import pkg_resources
+import subprocess
+import os
+import sys
+import shutil
+
+def main():
+    r = pkg_resources.require('setuptools')[0]
+    install_dir = os.path.join(
+            os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
+            "%s-%s"%(r.project_name, r.version))
+    if os.path.exists(install_dir):
+        print("Skip %s %s: already installed"%(r.project_name, r.version))
+
+    else:
+        os.mkdir(install_dir)
+        os.mkdir(os.path.join(install_dir, "parent"))
+        os.mkdir(os.path.join(install_dir, "child"))
+
+        if os.path.exists('parent/build'):
+            shutil.rmtree('parent/build')
+        if os.path.exists('child/build'):
+            shutil.rmtree('child/build')
+
+        for subdir in ('parent', 'child'):
+            p = subprocess.Popen([
+                sys.executable,
+                "setup.py",
+                "install",
+                 "--install-lib=%s/%s"%(install_dir, subdir),
+                 "--single-version-externally-managed",
+                 "--record", "files.txt"
+            ],
+            cwd=subdir)
+            xit = p.wait()
+            if xit != 0:
+                print("ERROR: install failed")
+                sys.exit(1)
+
+
+            if os.path.exists('%s/files.txt'%(subdir,)):
+                os.unlink('%s/files.txt'%(subdir,))
+
+
+if __name__ == "__main__":
+    main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/parent/namedpkg.egg-info/PKG-INFO b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/parent/namedpkg.egg-info/PKG-INFO
new file mode 100644
index 0000000..138c5fd
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/parent/namedpkg.egg-info/PKG-INFO
@@ -0,0 +1,10 @@
+Metadata-Version: 1.0
+Name: namedpkg
+Version: 1.0
+Summary: UNKNOWN
+Home-page: UNKNOWN
+Author: UNKNOWN
+Author-email: UNKNOWN
+License: UNKNOWN
+Description: UNKNOWN
+Platform: UNKNOWN
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/parent/namedpkg.egg-info/SOURCES.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/parent/namedpkg.egg-info/SOURCES.txt
new file mode 100644
index 0000000..29dfc47
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/parent/namedpkg.egg-info/SOURCES.txt
@@ -0,0 +1,8 @@
+setup.py
+namedpkg/__init__.py
+namedpkg/parent.py
+namedpkg.egg-info/PKG-INFO
+namedpkg.egg-info/SOURCES.txt
+namedpkg.egg-info/dependency_links.txt
+namedpkg.egg-info/namespace_packages.txt
+namedpkg.egg-info/top_level.txt
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/parent/namedpkg.egg-info/dependency_links.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/parent/namedpkg.egg-info/dependency_links.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/parent/namedpkg.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/parent/namedpkg.egg-info/namespace_packages.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/parent/namedpkg.egg-info/namespace_packages.txt
new file mode 100644
index 0000000..d332ce1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/parent/namedpkg.egg-info/namespace_packages.txt
@@ -0,0 +1 @@
+namedpkg
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/parent/namedpkg.egg-info/top_level.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/parent/namedpkg.egg-info/top_level.txt
new file mode 100644
index 0000000..d332ce1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/parent/namedpkg.egg-info/top_level.txt
@@ -0,0 +1 @@
+namedpkg
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/parent/namedpkg/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/parent/namedpkg/__init__.py
new file mode 100644
index 0000000..de40ea7
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/parent/namedpkg/__init__.py
@@ -0,0 +1 @@
+__import__('pkg_resources').declare_namespace(__name__)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/parent/namedpkg/parent.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/parent/namedpkg/parent.py
new file mode 100644
index 0000000..db7354b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/parent/namedpkg/parent.py
@@ -0,0 +1,2 @@
+""" parent packages """
+import sys
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/parent/setup.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/parent/setup.py
new file mode 100644
index 0000000..1a5be60
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/nspkg/src/parent/setup.py
@@ -0,0 +1,8 @@
+from setuptools import setup
+
+setup(
+        name="namedpkg",
+        version="1.0",
+        packages=["namedpkg"],
+        namespace_packages=["namedpkg"],
+)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/script b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/script
new file mode 100755
index 0000000..2716038
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/script
@@ -0,0 +1,4 @@
+#!/usr/bin/python
+import sys, os
+
+print (sys.version)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/subdir/file1.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/subdir/file1.txt
new file mode 100644
index 0000000..7898192
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/subdir/file1.txt
@@ -0,0 +1 @@
+a
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/subdir/file2.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/subdir/file2.txt
new file mode 100644
index 0000000..6178079
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/subdir/file2.txt
@@ -0,0 +1 @@
+b
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/syspath.egg b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/syspath.egg
new file mode 100644
index 0000000..64db323
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/syspath.egg
Binary files differ
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/syspath.zip b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/syspath.zip
new file mode 100644
index 0000000..d3c1f42
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/syspath.zip
Binary files differ
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/syspath/myext.pyd b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/syspath/myext.pyd
new file mode 100644
index 0000000..01e7507
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/syspath/myext.pyd
@@ -0,0 +1 @@
+""" fake extension """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/syspath/mymodule.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/syspath/mymodule.py
new file mode 100644
index 0000000..de6c648
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/syspath/mymodule.py
@@ -0,0 +1,3 @@
+"""
+some module
+"""
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/syspath/mymodule3.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/syspath/mymodule3.py
new file mode 100644
index 0000000..422b686
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/syspath/mymodule3.py
@@ -0,0 +1 @@
+"""  fake module """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/syspath/mypkg/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/syspath/mypkg/__init__.py
new file mode 100644
index 0000000..25597fb
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/syspath/mypkg/__init__.py
@@ -0,0 +1 @@
+""" fake package """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/test.egg b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/test.egg
new file mode 100644
index 0000000..219c116
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/test.egg
Binary files differ
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/test.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/test.txt
new file mode 100644
index 0000000..3b86232
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/test.txt
@@ -0,0 +1 @@
+This is test.txt
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/zipped.egg b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/zipped.egg
new file mode 100644
index 0000000..bf8bd09
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testdata/zipped.egg
Binary files differ
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-compatmodule/pkg/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-compatmodule/pkg/__init__.py
new file mode 100644
index 0000000..78b491e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-compatmodule/pkg/__init__.py
@@ -0,0 +1 @@
+""" pkg """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-compatmodule/pkg/api.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-compatmodule/pkg/api.py
new file mode 100644
index 0000000..53fe9ba
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-compatmodule/pkg/api.py
@@ -0,0 +1,9 @@
+""" pkg.api """
+
+import sys
+
+if sys.version_info[0] == 2:
+    from .api2 import *
+
+else:
+    from .api3 import *
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-compatmodule/pkg/api2.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-compatmodule/pkg/api2.py
new file mode 100644
index 0000000..4f5be0b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-compatmodule/pkg/api2.py
@@ -0,0 +1,11 @@
+import urllib2
+
+def div(a, b):
+    try:
+        return a/b
+
+    except ZeroDivisionError, exc:
+        return None
+
+class MyClass (object):
+    pass
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-compatmodule/pkg/api3.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-compatmodule/pkg/api3.py
new file mode 100644
index 0000000..dc4aefa
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-compatmodule/pkg/api3.py
@@ -0,0 +1,11 @@
+import http.client
+
+def div(a, b):
+    try:
+        return a/b
+
+    except ZeroDivisionError as exc:
+        return None
+
+class MyClass (object, metaclass=type):
+    pass
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/function_class_existing.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/function_class_existing.py
new file mode 100644
index 0000000..ef467ea
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/function_class_existing.py
@@ -0,0 +1 @@
+""" $fname """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/function_conditional_existing.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/function_conditional_existing.py
new file mode 100644
index 0000000..ef467ea
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/function_conditional_existing.py
@@ -0,0 +1 @@
+""" $fname """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/function_conditional_import2_existing.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/function_conditional_import2_existing.py
new file mode 100644
index 0000000..ef467ea
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/function_conditional_import2_existing.py
@@ -0,0 +1 @@
+""" $fname """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/function_conditional_import_existing.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/function_conditional_import_existing.py
new file mode 100644
index 0000000..ef467ea
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/function_conditional_import_existing.py
@@ -0,0 +1 @@
+""" $fname """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/function_existing.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/function_existing.py
new file mode 100644
index 0000000..ef467ea
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/function_existing.py
@@ -0,0 +1 @@
+""" $fname """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/function_import2_existing.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/function_import2_existing.py
new file mode 100644
index 0000000..ef467ea
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/function_import2_existing.py
@@ -0,0 +1 @@
+""" $fname """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/function_import_existing.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/function_import_existing.py
new file mode 100644
index 0000000..ef467ea
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/function_import_existing.py
@@ -0,0 +1 @@
+""" $fname """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/pkg/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/pkg/__init__.py
new file mode 100644
index 0000000..82e4875
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/pkg/__init__.py
@@ -0,0 +1 @@
+""" pkg.__init__ """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/pkg/function_class_existing.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/pkg/function_class_existing.py
new file mode 100644
index 0000000..ef467ea
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/pkg/function_class_existing.py
@@ -0,0 +1 @@
+""" $fname """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/pkg/function_conditional_existing.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/pkg/function_conditional_existing.py
new file mode 100644
index 0000000..ef467ea
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/pkg/function_conditional_existing.py
@@ -0,0 +1 @@
+""" $fname """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/pkg/function_conditional_import2_existing.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/pkg/function_conditional_import2_existing.py
new file mode 100644
index 0000000..ef467ea
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/pkg/function_conditional_import2_existing.py
@@ -0,0 +1 @@
+""" $fname """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/pkg/function_conditional_import_existing.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/pkg/function_conditional_import_existing.py
new file mode 100644
index 0000000..ef467ea
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/pkg/function_conditional_import_existing.py
@@ -0,0 +1 @@
+""" $fname """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/pkg/function_existing.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/pkg/function_existing.py
new file mode 100644
index 0000000..ef467ea
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/pkg/function_existing.py
@@ -0,0 +1 @@
+""" $fname """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/pkg/function_import2_existing.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/pkg/function_import2_existing.py
new file mode 100644
index 0000000..ef467ea
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/pkg/function_import2_existing.py
@@ -0,0 +1 @@
+""" $fname """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/pkg/function_import_existing.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/pkg/function_import_existing.py
new file mode 100644
index 0000000..ef467ea
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/pkg/function_import_existing.py
@@ -0,0 +1 @@
+""" $fname """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/pkg/toplevel_class_existing.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/pkg/toplevel_class_existing.py
new file mode 100644
index 0000000..ef467ea
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/pkg/toplevel_class_existing.py
@@ -0,0 +1 @@
+""" $fname """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/pkg/toplevel_conditional_existing.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/pkg/toplevel_conditional_existing.py
new file mode 100644
index 0000000..ef467ea
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/pkg/toplevel_conditional_existing.py
@@ -0,0 +1 @@
+""" $fname """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/pkg/toplevel_conditional_import2_existing.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/pkg/toplevel_conditional_import2_existing.py
new file mode 100644
index 0000000..ef467ea
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/pkg/toplevel_conditional_import2_existing.py
@@ -0,0 +1 @@
+""" $fname """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/pkg/toplevel_conditional_import_existing.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/pkg/toplevel_conditional_import_existing.py
new file mode 100644
index 0000000..ef467ea
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/pkg/toplevel_conditional_import_existing.py
@@ -0,0 +1 @@
+""" $fname """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/pkg/toplevel_existing.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/pkg/toplevel_existing.py
new file mode 100644
index 0000000..ef467ea
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/pkg/toplevel_existing.py
@@ -0,0 +1 @@
+""" $fname """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/pkg/toplevel_import2_existing.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/pkg/toplevel_import2_existing.py
new file mode 100644
index 0000000..ef467ea
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/pkg/toplevel_import2_existing.py
@@ -0,0 +1 @@
+""" $fname """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/pkg/toplevel_import_existing.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/pkg/toplevel_import_existing.py
new file mode 100644
index 0000000..ef467ea
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/pkg/toplevel_import_existing.py
@@ -0,0 +1 @@
+""" $fname """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/script.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/script.py
new file mode 100644
index 0000000..901c332
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/script.py
@@ -0,0 +1,51 @@
+
+import toplevel_existing
+import toplevel_nonexisting
+
+class MyClass:
+    import toplevel_class_existing
+    import toplevel_class_nonexisting
+
+if a == b:
+    import toplevel_conditional_existing
+    import toplevel_conditional_nonexisting
+
+    try:
+        import toplevel_conditional_import_existing
+        import toplevel_conditional_import_nonexisting
+    except:
+        import toplevel_conditional_import2_existing
+        import toplevel_conditional_import2_nonexisting
+
+try:
+    import toplevel_import_existing
+    import toplevel_import_nonexisting
+except:
+    import toplevel_import2_existing
+    import toplevel_import2_nonexisting
+
+def function():
+    import function_existing
+    import function_nonexisting
+
+    class MyClass:
+        import function_class_existing
+        import function_class_nonexisting
+
+    if a == b:
+        import function_conditional_existing
+        import function_conditional_nonexisting
+
+        try:
+            import function_conditional_import_existing
+            import function_conditional_import_nonexisting
+        except:
+            import function_conditional_import2_existing
+            import function_conditional_import2_nonexisting
+
+    try:
+        import function_import_existing
+        import function_import_nonexisting
+    except:
+        import function_import2_existing
+        import function_import2_nonexisting
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/script_from_import.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/script_from_import.py
new file mode 100644
index 0000000..1dd6783
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/script_from_import.py
@@ -0,0 +1,46 @@
+from pkg import toplevel_existing
+from pkg import toplevel_nonexisting
+
+class MyClass:
+    from pkg import toplevel_class_existing
+    from pkg import toplevel_class_nonexisting
+
+if a == b:
+    from pkg import toplevel_conditional_existing
+    from pkg import toplevel_conditional_nonexisting
+
+    try:
+        from pkg import toplevel_conditional_import_existing, toplevel_conditional_import_nonexisting
+    except:
+        from pkg import toplevel_conditional_import2_existing
+        from pkg import toplevel_conditional_import2_nonexisting
+
+try:
+    from pkg import toplevel_import_existing, toplevel_import_nonexisting
+except:
+    from pkg import toplevel_import2_existing
+    from pkg import toplevel_import2_nonexisting
+
+def function():
+    from pkg import function_existing, function_nonexisting
+
+    class MyClass:
+        from pkg import function_class_existing, function_class_nonexisting
+
+    if a == b:
+        from pkg import function_conditional_existing
+        from pkg import function_conditional_nonexisting
+
+        try:
+            from pkg import function_conditional_import_existing
+            from pkg import function_conditional_import_nonexisting
+        except:
+            from pkg import function_conditional_import2_existing
+            from pkg import function_conditional_import2_nonexisting
+
+    try:
+        from pkg import function_import_existing
+        from pkg import function_import_nonexisting
+    except:
+        from pkg import function_import2_existing
+        from pkg import function_import2_nonexisting
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/script_multi_import.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/script_multi_import.py
new file mode 100644
index 0000000..cb6ce54
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/script_multi_import.py
@@ -0,0 +1,26 @@
+
+
+try:
+    import os.path
+except ImportError:
+    pass
+
+import os
+
+def function(self):
+    import sys
+
+
+if a == b:
+    import sys
+
+def function2(self):
+    if a == b:
+        import platform
+
+def function3(self):
+    import platform
+    import email
+
+
+import email
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/toplevel_class_existing.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/toplevel_class_existing.py
new file mode 100644
index 0000000..ef467ea
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/toplevel_class_existing.py
@@ -0,0 +1 @@
+""" $fname """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/toplevel_conditional_existing.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/toplevel_conditional_existing.py
new file mode 100644
index 0000000..ef467ea
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/toplevel_conditional_existing.py
@@ -0,0 +1 @@
+""" $fname """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/toplevel_conditional_import2_existing.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/toplevel_conditional_import2_existing.py
new file mode 100644
index 0000000..ef467ea
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/toplevel_conditional_import2_existing.py
@@ -0,0 +1 @@
+""" $fname """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/toplevel_conditional_import_existing.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/toplevel_conditional_import_existing.py
new file mode 100644
index 0000000..ef467ea
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/toplevel_conditional_import_existing.py
@@ -0,0 +1 @@
+""" $fname """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/toplevel_existing.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/toplevel_existing.py
new file mode 100644
index 0000000..ef467ea
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/toplevel_existing.py
@@ -0,0 +1 @@
+""" $fname """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/toplevel_import2_existing.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/toplevel_import2_existing.py
new file mode 100644
index 0000000..ef467ea
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/toplevel_import2_existing.py
@@ -0,0 +1 @@
+""" $fname """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/toplevel_import_existing.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/toplevel_import_existing.py
new file mode 100644
index 0000000..ef467ea
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-edgedata/toplevel_import_existing.py
@@ -0,0 +1 @@
+""" $fname """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-import-from-init/pkg/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-import-from-init/pkg/__init__.py
new file mode 100644
index 0000000..78b491e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-import-from-init/pkg/__init__.py
@@ -0,0 +1 @@
+""" pkg """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-import-from-init/pkg/subpkg/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-import-from-init/pkg/subpkg/__init__.py
new file mode 100644
index 0000000..ecd411e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-import-from-init/pkg/subpkg/__init__.py
@@ -0,0 +1,5 @@
+""" pkg.subpkg """
+
+from compat import X, Y
+
+from _collections import A, B
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-import-from-init/pkg/subpkg/compat.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-import-from-init/pkg/subpkg/compat.py
new file mode 100644
index 0000000..92850f2
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-import-from-init/pkg/subpkg/compat.py
@@ -0,0 +1,3 @@
+""" pkg.subpkg.compat """
+
+X, Y = 1, 2
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-import-from-init/pkg2/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-import-from-init/pkg2/__init__.py
new file mode 100644
index 0000000..f6e15f3
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-import-from-init/pkg2/__init__.py
@@ -0,0 +1 @@
+""" pkg2.__init__ """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-import-from-init/pkg2/subpkg/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-import-from-init/pkg2/subpkg/__init__.py
new file mode 100644
index 0000000..97fddf1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-import-from-init/pkg2/subpkg/__init__.py
@@ -0,0 +1,5 @@
+""" pkg2.subpkg """
+
+from .compat import X, Y
+
+from ._collections import A, B
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-import-from-init/pkg2/subpkg/compat.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-import-from-init/pkg2/subpkg/compat.py
new file mode 100644
index 0000000..d544848
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-import-from-init/pkg2/subpkg/compat.py
@@ -0,0 +1,3 @@
+""" pkg2.subpkg.compat """
+
+X, Y = 1, 2
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-import-from-init/script.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-import-from-init/script.py
new file mode 100644
index 0000000..5867662
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-import-from-init/script.py
@@ -0,0 +1,2 @@
+import pkg.subpkg
+import pkg2.subpkg
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-packages/main_script.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-packages/main_script.py
new file mode 100644
index 0000000..de10111
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-packages/main_script.py
@@ -0,0 +1 @@
+import sys
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-packages/pkg/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-packages/pkg/__init__.py
new file mode 100644
index 0000000..84c8df8
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-packages/pkg/__init__.py
@@ -0,0 +1 @@
+""" pkg.init """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-packages/pkg/sub1/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-packages/pkg/sub1/__init__.py
new file mode 100644
index 0000000..7d52f7f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-packages/pkg/sub1/__init__.py
@@ -0,0 +1 @@
+""" pkg.sub1.init """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-packages/pkg/sub1/modA.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-packages/pkg/sub1/modA.py
new file mode 100644
index 0000000..b827020
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-packages/pkg/sub1/modA.py
@@ -0,0 +1 @@
+""" pkg.sub1.modA """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-packages/pkg/sub2/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-packages/pkg/sub2/__init__.py
new file mode 100644
index 0000000..ca5ca11
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-packages/pkg/sub2/__init__.py
@@ -0,0 +1 @@
+""" pkg.sub2.init """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-packages/pkg/sub2/mod.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-packages/pkg/sub2/mod.py
new file mode 100644
index 0000000..1b172c8
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-packages/pkg/sub2/mod.py
@@ -0,0 +1 @@
+""" pkg.sub2.mod """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-packages/pkg/sub3.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-packages/pkg/sub3.py
new file mode 100644
index 0000000..211217d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-packages/pkg/sub3.py
@@ -0,0 +1 @@
+""" pkg.sub3 """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-pep420-namespace/path1/package/sub2.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-pep420-namespace/path1/package/sub2.py
new file mode 100644
index 0000000..894a1ec
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-pep420-namespace/path1/package/sub2.py
@@ -0,0 +1 @@
+""" package.sub2 """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-pep420-namespace/path2/package/nspkg/mod.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-pep420-namespace/path2/package/nspkg/mod.py
new file mode 100644
index 0000000..9e846e4
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-pep420-namespace/path2/package/nspkg/mod.py
@@ -0,0 +1 @@
+""" package.nspkg.mod """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-pep420-namespace/path2/package/sub1.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-pep420-namespace/path2/package/sub1.py
new file mode 100644
index 0000000..bb1f933
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-pep420-namespace/path2/package/sub1.py
@@ -0,0 +1 @@
+""" package.sub1 """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-pep420-namespace/path2/package/subpackage/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-pep420-namespace/path2/package/subpackage/__init__.py
new file mode 100644
index 0000000..d1c6849
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-pep420-namespace/path2/package/subpackage/__init__.py
@@ -0,0 +1 @@
+""" package.subpackage """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-pep420-namespace/path2/package/subpackage/sub.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-pep420-namespace/path2/package/subpackage/sub.py
new file mode 100644
index 0000000..f0ed11d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-pep420-namespace/path2/package/subpackage/sub.py
@@ -0,0 +1 @@
+""" package.subpackage.sub """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr1/main_script.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr1/main_script.py
new file mode 100644
index 0000000..35e2a36
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr1/main_script.py
@@ -0,0 +1 @@
+from pkg import a
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr1/pkg/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr1/pkg/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr1/pkg/__init__.py
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr1/pkg/a.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr1/pkg/a.py
new file mode 100644
index 0000000..b02981c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr1/pkg/a.py
@@ -0,0 +1 @@
+from . import b
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr1/pkg/b.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr1/pkg/b.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr1/pkg/b.py
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr2/main_script.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr2/main_script.py
new file mode 100644
index 0000000..288701b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr2/main_script.py
@@ -0,0 +1 @@
+import pkg
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr2/pkg/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr2/pkg/__init__.py
new file mode 100644
index 0000000..1374eb3
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr2/pkg/__init__.py
@@ -0,0 +1,9 @@
+"""
+Package structure simular to crcmod
+"""
+try:
+    from pkg.pkg import *
+    import pkg.base
+except ImportError:
+    from pkg import *
+    import base
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr2/pkg/base.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr2/pkg/base.py
new file mode 100644
index 0000000..93e66ee
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr2/pkg/base.py
@@ -0,0 +1 @@
+""" package base """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr2/pkg/pkg.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr2/pkg/pkg.py
new file mode 100644
index 0000000..d5fcda2
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr2/pkg/pkg.py
@@ -0,0 +1 @@
+""" nested """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr3/mypkg/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr3/mypkg/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr3/mypkg/__init__.py
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr3/mypkg/distutils/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr3/mypkg/distutils/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr3/mypkg/distutils/__init__.py
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr3/mypkg/distutils/ccompiler.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr3/mypkg/distutils/ccompiler.py
new file mode 100644
index 0000000..c1b0f9b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr3/mypkg/distutils/ccompiler.py
@@ -0,0 +1,2 @@
+from distutils.ccompiler import *
+from distutils.sysconfig import customize_compiler
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr3/script.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr3/script.py
new file mode 100644
index 0000000..9e38ced
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr3/script.py
@@ -0,0 +1 @@
+from mypkg.distutils import ccompiler
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr4/pkg/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr4/pkg/__init__.py
new file mode 100644
index 0000000..82e4875
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr4/pkg/__init__.py
@@ -0,0 +1 @@
+""" pkg.__init__ """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr4/pkg/core/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr4/pkg/core/__init__.py
new file mode 100644
index 0000000..b393ff5
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr4/pkg/core/__init__.py
@@ -0,0 +1 @@
+""" pkg.core.__init__ """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr4/pkg/core/callables.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr4/pkg/core/callables.py
new file mode 100644
index 0000000..9ce619b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr4/pkg/core/callables.py
@@ -0,0 +1,3 @@
+""" pkg.callables """
+
+getID, getArgs, getRawFunction, ListenerInadequate, CallArgsInfo = [None]*5
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr4/pkg/core/listener.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr4/pkg/core/listener.py
new file mode 100644
index 0000000..28ae017
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr4/pkg/core/listener.py
@@ -0,0 +1,6 @@
+from .callables import \
+    getID, getArgs, getRawFunction,\
+    ListenerInadequate, \
+    CallArgsInfo
+
+from .listenerimpl import Listener, ListenerValidator
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr4/pkg/core/listenerimpl.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr4/pkg/core/listenerimpl.py
new file mode 100644
index 0000000..775bd34
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr4/pkg/core/listenerimpl.py
@@ -0,0 +1,6 @@
+""" pkg.listenerimp """
+class Listener:
+    pass
+
+class ListenerValidator:
+    pass
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr4/script.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr4/script.py
new file mode 100644
index 0000000..a3b2bfd
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr4/script.py
@@ -0,0 +1 @@
+from pkg.core.listener import Listener as listen
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr5/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr5/__init__.py
new file mode 100644
index 0000000..606f71e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr5/__init__.py
@@ -0,0 +1 @@
+""" A dummy __init__ file """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr5/script.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr5/script.py
new file mode 100644
index 0000000..2a5de40
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr5/script.py
@@ -0,0 +1,4 @@
+import __init__
+
+from modulegraph.find_modules import find_needed_modules
+import distutils
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr6/module.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr6/module.py
new file mode 100644
index 0000000..f6b6f89
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr6/module.py
@@ -0,0 +1,1009 @@
+
+ds = {
+    'name': [
+        list(1)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+        + list(2)
+    ]
+}
+
+import os
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr6/script.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr6/script.py
new file mode 100644
index 0000000..92211e1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-regr6/script.py
@@ -0,0 +1 @@
+import module
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport/mod.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport/mod.py
new file mode 100644
index 0000000..7828fc9
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport/mod.py
@@ -0,0 +1 @@
+""" Toplevel module """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport/pkg/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport/pkg/__init__.py
new file mode 100644
index 0000000..7fa65f6
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport/pkg/__init__.py
@@ -0,0 +1 @@
+""" A Package """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport/pkg/mod.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport/pkg/mod.py
new file mode 100644
index 0000000..de7fba1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport/pkg/mod.py
@@ -0,0 +1 @@
+""" A package module """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport/pkg/oldstyle.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport/pkg/oldstyle.py
new file mode 100644
index 0000000..4985e70
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport/pkg/oldstyle.py
@@ -0,0 +1 @@
+import mod
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport/pkg/relative.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport/pkg/relative.py
new file mode 100644
index 0000000..8ffd65a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport/pkg/relative.py
@@ -0,0 +1,2 @@
+from __future__ import absolute_import
+from . import mod
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport/pkg/relimport.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport/pkg/relimport.py
new file mode 100644
index 0000000..e23cb2e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport/pkg/relimport.py
@@ -0,0 +1 @@
+""" pkg.relimport """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport/pkg/sub2/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport/pkg/sub2/__init__.py
new file mode 100644
index 0000000..75c8c11
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport/pkg/sub2/__init__.py
@@ -0,0 +1 @@
+""" pkg.sub2 """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport/pkg/sub2/mod.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport/pkg/sub2/mod.py
new file mode 100644
index 0000000..1b172c8
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport/pkg/sub2/mod.py
@@ -0,0 +1 @@
+""" pkg.sub2.mod """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport/pkg/subpkg/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport/pkg/subpkg/__init__.py
new file mode 100644
index 0000000..ced6ba0
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport/pkg/subpkg/__init__.py
@@ -0,0 +1 @@
+""" pkg.subpkg """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport/pkg/subpkg/mod2.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport/pkg/subpkg/mod2.py
new file mode 100644
index 0000000..791e4d4
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport/pkg/subpkg/mod2.py
@@ -0,0 +1,4 @@
+""" pkg.subpkg.mod2 """
+from __future__ import absolute_import
+from ..sub2.mod import __doc__
+from ..sub2 import mod
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport/pkg/subpkg/relative.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport/pkg/subpkg/relative.py
new file mode 100644
index 0000000..775f435
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport/pkg/subpkg/relative.py
@@ -0,0 +1,3 @@
+""" pkg.subpkg.relative """
+from __future__ import absolute_import
+from .. import mod
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport/pkg/subpkg/relative2.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport/pkg/subpkg/relative2.py
new file mode 100644
index 0000000..9e11e20
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport/pkg/subpkg/relative2.py
@@ -0,0 +1,3 @@
+""" pkg.subpkg.relative """
+from __future__ import absolute_import
+from ..relimport import __doc__ as doc
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport/pkg/toplevel.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport/pkg/toplevel.py
new file mode 100644
index 0000000..67f0bb7
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport/pkg/toplevel.py
@@ -0,0 +1,2 @@
+from __future__ import absolute_import
+import mod
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport/script.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport/script.py
new file mode 100644
index 0000000..d2199dc
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport/script.py
@@ -0,0 +1,9 @@
+import mod
+import pkg
+import pkg.mod
+import pkg.oldstyle
+import pkg.relative
+import pkg.toplevel
+import pkg.subpkg.relative
+import pkg.subpkg.relative2
+import pkg.subpkg.mod2
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport2/pkg/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport2/pkg/__init__.py
new file mode 100644
index 0000000..085139d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport2/pkg/__init__.py
@@ -0,0 +1,2 @@
+from . import mod1
+from .mod2 import *
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport2/pkg/mod1.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport2/pkg/mod1.py
new file mode 100644
index 0000000..b7ef456
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport2/pkg/mod1.py
@@ -0,0 +1 @@
+""" mod1 """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport2/pkg/mod2.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport2/pkg/mod2.py
new file mode 100644
index 0000000..7161f08
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport2/pkg/mod2.py
@@ -0,0 +1 @@
+""" mod2 """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport2/pkg/mod3.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport2/pkg/mod3.py
new file mode 100644
index 0000000..7999d2d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport2/pkg/mod3.py
@@ -0,0 +1 @@
+""" mod3 """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport2/pkg/sub/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport2/pkg/sub/__init__.py
new file mode 100644
index 0000000..30440c3
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport2/pkg/sub/__init__.py
@@ -0,0 +1,3 @@
+from .. import mod1
+from .. import mod3
+from ... import toplevel
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport2/toplevel.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport2/toplevel.py
new file mode 100644
index 0000000..29059b9
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-relimport2/toplevel.py
@@ -0,0 +1 @@
+""" toplevel """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-setuptools-namespace/build/lib/nspkg/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-setuptools-namespace/build/lib/nspkg/__init__.py
new file mode 100644
index 0000000..2e2033b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-setuptools-namespace/build/lib/nspkg/__init__.py
@@ -0,0 +1,7 @@
+# this is a namespace package
+try:
+    import pkg_resources
+    pkg_resources.declare_namespace(__name__)
+except ImportError:
+    import pkgutil
+    __path__ = pkgutil.extend_path(__path__, __name__)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-setuptools-namespace/build/lib/nspkg/module.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-setuptools-namespace/build/lib/nspkg/module.py
new file mode 100644
index 0000000..0c1d857
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-setuptools-namespace/build/lib/nspkg/module.py
@@ -0,0 +1 @@
+""" nspkg.module """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-setuptools-namespace/build/lib/nspkg/nssubpkg/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-setuptools-namespace/build/lib/nspkg/nssubpkg/__init__.py
new file mode 100644
index 0000000..2e2033b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-setuptools-namespace/build/lib/nspkg/nssubpkg/__init__.py
@@ -0,0 +1,7 @@
+# this is a namespace package
+try:
+    import pkg_resources
+    pkg_resources.declare_namespace(__name__)
+except ImportError:
+    import pkgutil
+    __path__ = pkgutil.extend_path(__path__, __name__)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-setuptools-namespace/build/lib/nspkg/nssubpkg/sub.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-setuptools-namespace/build/lib/nspkg/nssubpkg/sub.py
new file mode 100644
index 0000000..bce954b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-setuptools-namespace/build/lib/nspkg/nssubpkg/sub.py
@@ -0,0 +1 @@
+""" nspkg.nsubpkg.sub """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-setuptools-namespace/setup.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-setuptools-namespace/setup.py
new file mode 100644
index 0000000..c3d21a1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-setuptools-namespace/setup.py
@@ -0,0 +1,10 @@
+from setuptools import setup
+
+setup(
+    name="nspkg",
+    version="1.0",
+    namespace_packages=['nspkg', 'nspkg.nssubpkg'],
+    packages=['nspkg', 'nspkg.nssubpkg'],
+    package_dir = {'': 'src'},
+    zip_safe=False,
+)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-setuptools-namespace/src/nspkg.egg-info/PKG-INFO b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-setuptools-namespace/src/nspkg.egg-info/PKG-INFO
new file mode 100644
index 0000000..a2d9629
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-setuptools-namespace/src/nspkg.egg-info/PKG-INFO
@@ -0,0 +1,10 @@
+Metadata-Version: 1.0
+Name: nspkg
+Version: 1.0
+Summary: UNKNOWN
+Home-page: UNKNOWN
+Author: UNKNOWN
+Author-email: UNKNOWN
+License: UNKNOWN
+Description: UNKNOWN
+Platform: UNKNOWN
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-setuptools-namespace/src/nspkg.egg-info/SOURCES.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-setuptools-namespace/src/nspkg.egg-info/SOURCES.txt
new file mode 100644
index 0000000..6288716
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-setuptools-namespace/src/nspkg.egg-info/SOURCES.txt
@@ -0,0 +1,11 @@
+setup.py
+src/nspkg/__init__.py
+src/nspkg/module.py
+src/nspkg.egg-info/PKG-INFO
+src/nspkg.egg-info/SOURCES.txt
+src/nspkg.egg-info/dependency_links.txt
+src/nspkg.egg-info/namespace_packages.txt
+src/nspkg.egg-info/not-zip-safe
+src/nspkg.egg-info/top_level.txt
+src/nspkg/nssubpkg/__init__.py
+src/nspkg/nssubpkg/sub.py
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-setuptools-namespace/src/nspkg.egg-info/dependency_links.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-setuptools-namespace/src/nspkg.egg-info/dependency_links.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-setuptools-namespace/src/nspkg.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-setuptools-namespace/src/nspkg.egg-info/namespace_packages.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-setuptools-namespace/src/nspkg.egg-info/namespace_packages.txt
new file mode 100644
index 0000000..2321d6b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-setuptools-namespace/src/nspkg.egg-info/namespace_packages.txt
@@ -0,0 +1,2 @@
+nspkg
+nspkg.nssubpkg
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-setuptools-namespace/src/nspkg.egg-info/not-zip-safe b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-setuptools-namespace/src/nspkg.egg-info/not-zip-safe
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-setuptools-namespace/src/nspkg.egg-info/not-zip-safe
@@ -0,0 +1 @@
+
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-setuptools-namespace/src/nspkg.egg-info/top_level.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-setuptools-namespace/src/nspkg.egg-info/top_level.txt
new file mode 100644
index 0000000..61d82f4
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-setuptools-namespace/src/nspkg.egg-info/top_level.txt
@@ -0,0 +1 @@
+nspkg
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-setuptools-namespace/src/nspkg/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-setuptools-namespace/src/nspkg/__init__.py
new file mode 100644
index 0000000..2e2033b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-setuptools-namespace/src/nspkg/__init__.py
@@ -0,0 +1,7 @@
+# this is a namespace package
+try:
+    import pkg_resources
+    pkg_resources.declare_namespace(__name__)
+except ImportError:
+    import pkgutil
+    __path__ = pkgutil.extend_path(__path__, __name__)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-setuptools-namespace/src/nspkg/module.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-setuptools-namespace/src/nspkg/module.py
new file mode 100644
index 0000000..0c1d857
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-setuptools-namespace/src/nspkg/module.py
@@ -0,0 +1 @@
+""" nspkg.module """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-setuptools-namespace/src/nspkg/nssubpkg/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-setuptools-namespace/src/nspkg/nssubpkg/__init__.py
new file mode 100644
index 0000000..2e2033b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-setuptools-namespace/src/nspkg/nssubpkg/__init__.py
@@ -0,0 +1,7 @@
+# this is a namespace package
+try:
+    import pkg_resources
+    pkg_resources.declare_namespace(__name__)
+except ImportError:
+    import pkgutil
+    __path__ = pkgutil.extend_path(__path__, __name__)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-setuptools-namespace/src/nspkg/nssubpkg/sub.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-setuptools-namespace/src/nspkg/nssubpkg/sub.py
new file mode 100644
index 0000000..bce954b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/modulegraph_tests/testpkg-setuptools-namespace/src/nspkg/nssubpkg/sub.py
@@ -0,0 +1 @@
+""" nspkg.nsubpkg.sub """
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/scripts/extract_implies.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/scripts/extract_implies.py
new file mode 100644
index 0000000..d6ab353
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/scripts/extract_implies.py
@@ -0,0 +1,44 @@
+#!/usr/bin/env python
+"""
+This script looks for ImportModules calls in C extensions
+of the stdlib.
+
+The current version has harcoded the location of the source
+tries on Ronald's machine, a future version will be able
+to rebuild the modulegraph source file that contains
+this information.
+"""
+
+import re
+import sys
+import os
+import pprint
+
+import_re = re.compile('PyImport_ImportModule\w+\("(\w+)"\);')
+
+def extract_implies(root):
+    modules_dir = os.path.join(root, "Modules")
+    for fn in os.listdir(modules_dir):
+        if not fn.endswith('.c'):
+            continue
+
+        module_name = fn[:-2]
+        if module_name.endswith('module'):
+            module_name = module_name[:-6]
+
+        with open(os.path.join(modules_dir, fn)) as fp:
+            data = fp.read()
+
+        imports = list(sorted(set(import_re.findall(data))))
+        if imports:
+            yield module_name, imports
+
+
+
+def main():
+    for version in ('2.6', '2.7', '3.1'):
+        print "====", version
+        pprint.pprint(list(extract_implies('/Users/ronald/Projects/python/release%s-maint'%(version.replace('.', '')))))
+
+if __name__ == "__main__":
+    main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/setup.cfg b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/setup.cfg
new file mode 100644
index 0000000..f1d0167
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/setup.cfg
@@ -0,0 +1,54 @@
+[metadata]
+name = modulegraph
+version = 0.12.1
+description = Python module dependency analysis tool
+long_description_file = 
+	README.txt
+	doc/changelog.rst
+classifiers = 
+	Intended Audience :: Developers
+	License :: OSI Approved :: MIT License
+	Programming Language :: Python
+	Programming Language :: Python :: 2
+	Programming Language :: Python :: 2.7
+	Programming Language :: Python :: 3
+	Programming Language :: Python :: 3.3
+	Programming Language :: Python :: 3.4
+	Topic :: Software Development :: Libraries :: Python Modules
+	Topic :: Software Development :: Build Tools
+author = Ronald Oussoren
+author_email = ronaldoussoren@mac.com
+maintainer = Ronald Oussoren
+maintainer_email = ronaldoussoren@mac.com
+url = http://bitbucket.org/ronaldoussoren/modulegraph
+download_url = http://pypi.python.org/pypi/modulegraph
+license = MIT
+packages = modulegraph
+platforms = any
+requires-dist = 
+	altgraph (>= 0.12)
+console_scripts = 
+	modulegraph = modulegraph.__main__:main
+zip-safe = 1
+keywords = import, dependencies
+
+[check-manifest]
+ignore = 
+	modulegraph_tests/testdata/nspkg/distribute-0.6.10/child/nameduser-1.5-py2.6.egg-info
+	modulegraph_tests/testdata/nspkg/distribute-0.6.10/parent/namedpkg-1.0-py2.6.egg-info
+	modulegraph_tests/testdata/nspkg/distribute-0.6.12/child/nameduser-1.5-py2.5.egg-info
+	modulegraph_tests/testdata/nspkg/distribute-0.6.12/parent/namedpkg-1.0-py2.5.egg-info
+	modulegraph_tests/testdata/nspkg/setuptools-0.6c9/child/nameduser-1.5-py2.5.egg-info
+	modulegraph_tests/testdata/nspkg/setuptools-0.6c9/parent/namedpkg-1.0-py2.5.egg-info
+	modulegraph_tests/testdata/nspkg/src/child/nameduser.egg-info
+	modulegraph_tests/testdata/nspkg/src/parent/namedpkg.egg-info
+	modulegraph_tests/testdata/syspath/myext.pyd
+	modulegraph_tests/testdata/syspath/myext.so
+	modulegraph_tests/testdata/syspath/mymodule2.pyc
+	modulegraph_tests/testpkg-setuptools-namespace/src/nspkg.egg-info
+
+[egg_info]
+tag_build = 
+tag_date = 0
+tag_svn_revision = 0
+
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/setup.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/setup.py
new file mode 100644
index 0000000..a1a4cb6
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/modulegraph/setup.py
@@ -0,0 +1,867 @@
+"""
+Shared setup file for simple python packages. Uses a setup.cfg that
+is the same as the distutils2 project, unless noted otherwise.
+
+It exists for two reasons:
+1) This makes it easier to reuse setup.py code between my own
+   projects
+
+2) Easier migration to distutils2 when that catches on.
+
+Additional functionality:
+
+* Section metadata:
+    requires-test:  Same as 'tests_require' option for setuptools.
+
+"""
+
+import sys
+import os
+import re
+import platform
+from fnmatch import fnmatch
+import os
+import sys
+import time
+import tempfile
+import tarfile
+try:
+    import urllib.request as urllib
+except ImportError:
+    import urllib
+from distutils import log
+try:
+    from hashlib import md5
+
+except ImportError:
+    from md5 import md5
+
+if sys.version_info[0] == 2:
+    from ConfigParser import RawConfigParser, NoOptionError, NoSectionError
+else:
+    from configparser import RawConfigParser, NoOptionError, NoSectionError
+
+ROOTDIR = os.path.dirname(os.path.abspath(__file__))
+
+
+#
+#
+#
+# Parsing the setup.cfg and converting it to something that can be
+# used by setuptools.setup()
+#
+#
+#
+
+def eval_marker(value):
+    """
+    Evaluate an distutils2 environment marker.
+
+    This code is unsafe when used with hostile setup.cfg files,
+    but that's not a problem for our own files.
+    """
+    value = value.strip()
+
+    class M:
+        def __init__(self, **kwds):
+            for k, v in kwds.items():
+                setattr(self, k, v)
+
+    variables = {
+        'python_version': '%d.%d'%(sys.version_info[0], sys.version_info[1]),
+        'python_full_version': sys.version.split()[0],
+        'os': M(
+            name=os.name,
+        ),
+        'sys': M(
+            platform=sys.platform,
+        ),
+        'platform': M(
+            version=platform.version(),
+            machine=platform.machine(),
+        ),
+    }
+
+    return bool(eval(value, variables, variables))
+
+
+    return True
+
+def _opt_value(cfg, into, section, key, transform = None):
+    try:
+        v = cfg.get(section, key)
+        if transform != _as_lines and ';' in v:
+            v, marker = v.rsplit(';', 1)
+            if not eval_marker(marker):
+                return
+
+            v = v.strip()
+
+        if v:
+            if transform:
+                into[key] = transform(v.strip())
+            else:
+                into[key] = v.strip()
+
+    except (NoOptionError, NoSectionError):
+        pass
+
+def _as_bool(value):
+    if value.lower() in ('y', 'yes', 'on'):
+        return True
+    elif value.lower() in ('n', 'no', 'off'):
+        return False
+    elif value.isdigit():
+        return bool(int(value))
+    else:
+        raise ValueError(value)
+
+def _as_list(value):
+    return value.split()
+
+def _as_lines(value):
+    result = []
+    for v in value.splitlines():
+        if ';' in v:
+            v, marker = v.rsplit(';', 1)
+            if not eval_marker(marker):
+                continue
+
+            v = v.strip()
+            if v:
+                result.append(v)
+        else:
+            result.append(v)
+    return result
+
+def _map_requirement(value):
+    m = re.search(r'(\S+)\s*(?:\((.*)\))?', value)
+    name = m.group(1)
+    version = m.group(2)
+
+    if version is None:
+        return name
+
+    else:
+        mapped = []
+        for v in version.split(','):
+            v = v.strip()
+            if v[0].isdigit():
+                # Checks for a specific version prefix
+                m = v.rsplit('.', 1)
+                mapped.append('>=%s,<%s.%s'%(
+                    v, m[0], int(m[1])+1))
+
+            else:
+                mapped.append(v)
+        return '%s %s'%(name, ','.join(mapped),)
+
+def _as_requires(value):
+    requires = []
+    for req in value.splitlines():
+        if ';' in req:
+            req, marker = v.rsplit(';', 1)
+            if not eval_marker(marker):
+                continue
+            req = req.strip()
+
+        if not req:
+            continue
+        requires.append(_map_requirement(req))
+    return requires
+
+def parse_setup_cfg():
+    cfg = RawConfigParser()
+    r = cfg.read([os.path.join(ROOTDIR, 'setup.cfg')])
+    if len(r) != 1:
+        print("Cannot read 'setup.cfg'")
+        sys.exit(1)
+
+    metadata = dict(
+            name        = cfg.get('metadata', 'name'),
+            version     = cfg.get('metadata', 'version'),
+            description = cfg.get('metadata', 'description'),
+    )
+
+    _opt_value(cfg, metadata, 'metadata', 'license')
+    _opt_value(cfg, metadata, 'metadata', 'maintainer')
+    _opt_value(cfg, metadata, 'metadata', 'maintainer_email')
+    _opt_value(cfg, metadata, 'metadata', 'author')
+    _opt_value(cfg, metadata, 'metadata', 'author_email')
+    _opt_value(cfg, metadata, 'metadata', 'url')
+    _opt_value(cfg, metadata, 'metadata', 'download_url')
+    _opt_value(cfg, metadata, 'metadata', 'classifiers', _as_lines)
+    _opt_value(cfg, metadata, 'metadata', 'platforms', _as_list)
+    _opt_value(cfg, metadata, 'metadata', 'packages', _as_list)
+    _opt_value(cfg, metadata, 'metadata', 'keywords', _as_list)
+
+    try:
+        v = cfg.get('metadata', 'requires-dist')
+
+    except (NoOptionError, NoSectionError):
+        pass
+
+    else:
+        requires = _as_requires(v)
+        if requires:
+            metadata['install_requires'] = requires
+
+    try:
+        v = cfg.get('metadata', 'requires-test')
+
+    except (NoOptionError, NoSectionError):
+        pass
+
+    else:
+        requires = _as_requires(v)
+        if requires:
+            metadata['tests_require'] = requires
+
+
+    try:
+        v = cfg.get('metadata', 'long_description_file')
+    except (NoOptionError, NoSectionError):
+        pass
+
+    else:
+        parts = []
+        for nm in v.split():
+            fp = open(nm, 'rU')
+            parts.append(fp.read())
+            fp.close()
+
+        metadata['long_description'] = '\n\n'.join(parts)
+
+
+    try:
+        v = cfg.get('metadata', 'zip-safe')
+    except (NoOptionError, NoSectionError):
+        pass
+
+    else:
+        metadata['zip_safe'] = _as_bool(v)
+
+    try:
+        v = cfg.get('metadata', 'console_scripts')
+    except (NoOptionError, NoSectionError):
+        pass
+
+    else:
+        if 'entry_points' not in metadata:
+            metadata['entry_points'] = {}
+
+        metadata['entry_points']['console_scripts'] = v.splitlines()
+
+    if sys.version_info[:2] <= (2,6):
+        try:
+            metadata['tests_require'] += ", unittest2"
+        except KeyError:
+            metadata['tests_require'] = "unittest2"
+
+    return metadata
+
+
+#
+#
+#
+# Bootstrapping setuptools/distribute, based on
+# a heavily modified version of distribute_setup.py
+#
+#
+#
+
+
+SETUPTOOLS_PACKAGE='setuptools'
+
+
+try:
+    import subprocess
+
+    def _python_cmd(*args):
+        args = (sys.executable,) + args
+        return subprocess.call(args) == 0
+
+except ImportError:
+    def _python_cmd(*args):
+        args = (sys.executable,) + args
+        new_args = []
+        for a in args:
+            new_args.append(a.replace("'", "'\"'\"'"))
+        os.system(' '.join(new_args)) == 0
+
+
+try:
+    import json
+
+    def get_pypi_src_download(package):
+        url = 'https://pypi.python.org/pypi/%s/json'%(package,)
+        fp = urllib.urlopen(url)
+        try:
+            try:
+                data = fp.read()
+
+            finally:
+                fp.close()
+        except urllib.error:
+            raise RuntimeError("Cannot determine download link for %s"%(package,))
+
+        pkgdata = json.loads(data.decode('utf-8'))
+        if 'urls' not in pkgdata:
+            raise RuntimeError("Cannot determine download link for %s"%(package,))
+
+        for info in pkgdata['urls']:
+            if info['packagetype'] == 'sdist' and info['url'].endswith('tar.gz'):
+                return (info.get('md5_digest'), info['url'])
+
+        raise RuntimeError("Cannot determine downlink link for %s"%(package,))
+
+except ImportError:
+    # Python 2.5 compatibility, no JSON in stdlib but luckily JSON syntax is
+    # simular enough to Python's syntax to be able to abuse the Python compiler
+
+    import _ast as ast
+
+    def get_pypi_src_download(package):
+        url = 'https://pypi.python.org/pypi/%s/json'%(package,)
+        fp = urllib.urlopen(url)
+        try:
+            try:
+                data = fp.read()
+
+            finally:
+                fp.close()
+        except urllib.error:
+            raise RuntimeError("Cannot determine download link for %s"%(package,))
+
+
+        a = compile(data, '-', 'eval', ast.PyCF_ONLY_AST)
+        if not isinstance(a, ast.Expression):
+            raise RuntimeError("Cannot determine download link for %s"%(package,))
+
+        a = a.body
+        if not isinstance(a, ast.Dict):
+            raise RuntimeError("Cannot determine download link for %s"%(package,))
+
+        for k, v in zip(a.keys, a.values):
+            if not isinstance(k, ast.Str):
+                raise RuntimeError("Cannot determine download link for %s"%(package,))
+
+            k = k.s
+            if k == 'urls':
+                a = v
+                break
+        else:
+            raise RuntimeError("PyPI JSON for %s doesn't contain URLs section"%(package,))
+
+        if not isinstance(a, ast.List):
+            raise RuntimeError("Cannot determine download link for %s"%(package,))
+
+        for info in v.elts:
+            if not isinstance(info, ast.Dict):
+                raise RuntimeError("Cannot determine download link for %s"%(package,))
+            url = None
+            packagetype = None
+            chksum = None
+
+            for k, v in zip(info.keys, info.values):
+                if not isinstance(k, ast.Str):
+                    raise RuntimeError("Cannot determine download link for %s"%(package,))
+
+                if k.s == 'url':
+                    if not isinstance(v, ast.Str):
+                        raise RuntimeError("Cannot determine download link for %s"%(package,))
+                    url = v.s
+
+                elif k.s == 'packagetype':
+                    if not isinstance(v, ast.Str):
+                        raise RuntimeError("Cannot determine download link for %s"%(package,))
+                    packagetype = v.s
+
+                elif k.s == 'md5_digest':
+                    if not isinstance(v, ast.Str):
+                        raise RuntimeError("Cannot determine download link for %s"%(package,))
+                    chksum = v.s
+
+            if url is not None and packagetype == 'sdist' and url.endswith('.tar.gz'):
+                return (chksum, url)
+
+        raise RuntimeError("Cannot determine download link for %s"%(package,))
+
+def _build_egg(egg, tarball, to_dir):
+    # extracting the tarball
+    tmpdir = tempfile.mkdtemp()
+    log.warn('Extracting in %s', tmpdir)
+    old_wd = os.getcwd()
+    try:
+        os.chdir(tmpdir)
+        tar = tarfile.open(tarball)
+        _extractall(tar)
+        tar.close()
+
+        # going in the directory
+        subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
+        os.chdir(subdir)
+        log.warn('Now working in %s', subdir)
+
+        # building an egg
+        log.warn('Building a %s egg in %s', egg, to_dir)
+        _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir)
+
+    finally:
+        os.chdir(old_wd)
+    # returning the result
+    log.warn(egg)
+    if not os.path.exists(egg):
+        raise IOError('Could not build the egg.')
+
+
+def _do_download(to_dir, packagename=SETUPTOOLS_PACKAGE):
+    tarball = download_setuptools(packagename, to_dir)
+    version = tarball.split('-')[-1][:-7]
+    egg = os.path.join(to_dir, '%s-%s-py%d.%d.egg'
+                       % (packagename, version, sys.version_info[0], sys.version_info[1]))
+    if not os.path.exists(egg):
+        _build_egg(egg, tarball, to_dir)
+    sys.path.insert(0, egg)
+    import setuptools
+    setuptools.bootstrap_install_from = egg
+
+
+def use_setuptools():
+    # making sure we use the absolute path
+    return _do_download(os.path.abspath(os.curdir))
+
+def download_setuptools(packagename, to_dir):
+    # making sure we use the absolute path
+    to_dir = os.path.abspath(to_dir)
+    try:
+        from urllib.request import urlopen
+    except ImportError:
+        from urllib2 import urlopen
+
+    chksum, url = get_pypi_src_download(packagename)
+    tgz_name = os.path.basename(url)
+    saveto = os.path.join(to_dir, tgz_name)
+
+    src = dst = None
+    if not os.path.exists(saveto):  # Avoid repeated downloads
+        try:
+            log.warn("Downloading %s", url)
+            src = urlopen(url)
+            # Read/write all in one block, so we don't create a corrupt file
+            # if the download is interrupted.
+            data = src.read()
+
+            if chksum is not None:
+                data_sum = md5(data).hexdigest()
+                if data_sum != chksum:
+                    raise RuntimeError("Downloading %s failed: corrupt checksum"%(url,))
+
+
+            dst = open(saveto, "wb")
+            dst.write(data)
+        finally:
+            if src:
+                src.close()
+            if dst:
+                dst.close()
+    return os.path.realpath(saveto)
+
+
+
+def _extractall(self, path=".", members=None):
+    """Extract all members from the archive to the current working
+       directory and set owner, modification time and permissions on
+       directories afterwards. `path' specifies a different directory
+       to extract to. `members' is optional and must be a subset of the
+       list returned by getmembers().
+    """
+    import copy
+    import operator
+    from tarfile import ExtractError
+    directories = []
+
+    if members is None:
+        members = self
+
+    for tarinfo in members:
+        if tarinfo.isdir():
+            # Extract directories with a safe mode.
+            directories.append(tarinfo)
+            tarinfo = copy.copy(tarinfo)
+            tarinfo.mode = 448 # decimal for oct 0700
+        self.extract(tarinfo, path)
+
+    # Reverse sort directories.
+    if sys.version_info < (2, 4):
+        def sorter(dir1, dir2):
+            return cmp(dir1.name, dir2.name)
+        directories.sort(sorter)
+        directories.reverse()
+    else:
+        directories.sort(key=operator.attrgetter('name'), reverse=True)
+
+    # Set correct owner, mtime and filemode on directories.
+    for tarinfo in directories:
+        dirpath = os.path.join(path, tarinfo.name)
+        try:
+            self.chown(tarinfo, dirpath)
+            self.utime(tarinfo, dirpath)
+            self.chmod(tarinfo, dirpath)
+        except ExtractError:
+            e = sys.exc_info()[1]
+            if self.errorlevel > 1:
+                raise
+            else:
+                self._dbg(1, "tarfile: %s" % e)
+
+
+#
+#
+#
+# Definitions of custom commands
+#
+#
+#
+
+try:
+    import setuptools
+
+except ImportError:
+    use_setuptools()
+
+from setuptools import setup
+
+try:
+    from distutils.core import PyPIRCCommand
+except ImportError:
+    PyPIRCCommand = None # Ancient python version
+
+from distutils.core import Command
+from distutils.errors  import DistutilsError
+from distutils import log
+
+if PyPIRCCommand is None:
+    class upload_docs (Command):
+        description = "upload sphinx documentation"
+        user_options = []
+
+        def initialize_options(self):
+            pass
+
+        def finalize_options(self):
+            pass
+
+        def run(self):
+            raise DistutilsError("not supported on this version of python")
+
+else:
+    class upload_docs (PyPIRCCommand):
+        description = "upload sphinx documentation"
+        user_options = PyPIRCCommand.user_options
+
+        def initialize_options(self):
+            PyPIRCCommand.initialize_options(self)
+            self.username = ''
+            self.password = ''
+
+
+        def finalize_options(self):
+            PyPIRCCommand.finalize_options(self)
+            config = self._read_pypirc()
+            if config != {}:
+                self.username = config['username']
+                self.password = config['password']
+
+
+        def run(self):
+            import subprocess
+            import shutil
+            import zipfile
+            import os
+            import urllib
+            import StringIO
+            from base64 import standard_b64encode
+            import httplib
+            import urlparse
+
+            # Extract the package name from distutils metadata
+            meta = self.distribution.metadata
+            name = meta.get_name()
+
+            # Run sphinx
+            if os.path.exists('doc/_build'):
+                shutil.rmtree('doc/_build')
+            os.mkdir('doc/_build')
+
+            p = subprocess.Popen(['make', 'html'],
+                cwd='doc')
+            exit = p.wait()
+            if exit != 0:
+                raise DistutilsError("sphinx-build failed")
+
+            # Collect sphinx output
+            if not os.path.exists('dist'):
+                os.mkdir('dist')
+            zf = zipfile.ZipFile('dist/%s-docs.zip'%(name,), 'w',
+                    compression=zipfile.ZIP_DEFLATED)
+
+            for toplevel, dirs, files in os.walk('doc/_build/html'):
+                for fn in files:
+                    fullname = os.path.join(toplevel, fn)
+                    relname = os.path.relpath(fullname, 'doc/_build/html')
+
+                    print ("%s -> %s"%(fullname, relname))
+
+                    zf.write(fullname, relname)
+
+            zf.close()
+
+            # Upload the results, this code is based on the distutils
+            # 'upload' command.
+            content = open('dist/%s-docs.zip'%(name,), 'rb').read()
+
+            data = {
+                ':action': 'doc_upload',
+                'name': name,
+                'content': ('%s-docs.zip'%(name,), content),
+            }
+            auth = "Basic " + standard_b64encode(self.username + ":" +
+                 self.password)
+
+
+            boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
+            sep_boundary = '\n--' + boundary
+            end_boundary = sep_boundary + '--'
+            body = StringIO.StringIO()
+            for key, value in data.items():
+                if not isinstance(value, list):
+                    value = [value]
+
+                for value in value:
+                    if isinstance(value, tuple):
+                        fn = ';filename="%s"'%(value[0])
+                        value = value[1]
+                    else:
+                        fn = ''
+
+                    body.write(sep_boundary)
+                    body.write('\nContent-Disposition: form-data; name="%s"'%key)
+                    body.write(fn)
+                    body.write("\n\n")
+                    body.write(value)
+
+            body.write(end_boundary)
+            body.write('\n')
+            body = body.getvalue()
+
+            self.announce("Uploading documentation to %s"%(self.repository,), log.INFO)
+
+            schema, netloc, url, params, query, fragments = \
+                    urlparse.urlparse(self.repository)
+
+
+            if schema == 'http':
+                http = httplib.HTTPConnection(netloc)
+            elif schema == 'https':
+                http = httplib.HTTPSConnection(netloc)
+            else:
+                raise AssertionError("unsupported schema "+schema)
+
+            data = ''
+            loglevel = log.INFO
+            try:
+                http.connect()
+                http.putrequest("POST", url)
+                http.putheader('Content-type',
+                    'multipart/form-data; boundary=%s'%boundary)
+                http.putheader('Content-length', str(len(body)))
+                http.putheader('Authorization', auth)
+                http.endheaders()
+                http.send(body)
+            except socket.error:
+                e = socket.exc_info()[1]
+                self.announce(str(e), log.ERROR)
+                return
+
+            r = http.getresponse()
+            if r.status in (200, 301):
+                self.announce('Upload succeeded (%s): %s' % (r.status, r.reason),
+                    log.INFO)
+            else:
+                self.announce('Upload failed (%s): %s' % (r.status, r.reason),
+                    log.ERROR)
+
+                print ('-'*75)
+                print (r.read())
+                print ('-'*75)
+
+
+def recursiveGlob(root, pathPattern):
+    """
+    Recursively look for files matching 'pathPattern'. Return a list
+    of matching files/directories.
+    """
+    result = []
+
+    for rootpath, dirnames, filenames in os.walk(root):
+        for fn in filenames:
+            if fnmatch(fn, pathPattern):
+                result.append(os.path.join(rootpath, fn))
+    return result
+
+
+def importExternalTestCases(unittest,
+        pathPattern="test_*.py", root=".", package=None):
+    """
+    Import all unittests in the PyObjC tree starting at 'root'
+    """
+
+    testFiles = recursiveGlob(root, pathPattern)
+    testModules = map(lambda x:x[len(root)+1:-3].replace('/', '.'), testFiles)
+    if package is not None:
+        testModules = [(package + '.' + m) for m in testModules]
+
+    suites = []
+
+    for modName in testModules:
+        try:
+            module = __import__(modName)
+        except ImportError:
+            print("SKIP %s: %s"%(modName, sys.exc_info()[1]))
+            continue
+
+        if '.' in modName:
+            for elem in modName.split('.')[1:]:
+                module = getattr(module, elem)
+
+        s = unittest.defaultTestLoader.loadTestsFromModule(module)
+        suites.append(s)
+
+    return unittest.TestSuite(suites)
+
+
+
+class test (Command):
+    description = "run test suite"
+    user_options = [
+        ('verbosity=', None, "print what tests are run"),
+    ]
+
+    def initialize_options(self):
+        self.verbosity='1'
+
+    def finalize_options(self):
+        if isinstance(self.verbosity, str):
+            self.verbosity = int(self.verbosity)
+
+
+    def cleanup_environment(self):
+        ei_cmd = self.get_finalized_command('egg_info')
+        egg_name = ei_cmd.egg_name.replace('-', '_')
+
+        to_remove =  []
+        for dirname in sys.path:
+            bn = os.path.basename(dirname)
+            if bn.startswith(egg_name + "-"):
+                to_remove.append(dirname)
+
+        for dirname in to_remove:
+            log.info("removing installed %r from sys.path before testing"%(
+                dirname,))
+            sys.path.remove(dirname)
+
+    def add_project_to_sys_path(self):
+        from pkg_resources import normalize_path, add_activation_listener
+        from pkg_resources import working_set, require
+
+        self.reinitialize_command('egg_info')
+        self.run_command('egg_info')
+        self.reinitialize_command('build_ext', inplace=1)
+        self.run_command('build_ext')
+
+
+        # Check if this distribution is already on sys.path
+        # and remove that version, this ensures that the right
+        # copy of the package gets tested.
+
+        self.__old_path = sys.path[:]
+        self.__old_modules = sys.modules.copy()
+
+
+        ei_cmd = self.get_finalized_command('egg_info')
+        sys.path.insert(0, normalize_path(ei_cmd.egg_base))
+        sys.path.insert(1, os.path.dirname(__file__))
+
+        # Strip the namespace packages defined in this distribution
+        # from sys.modules, needed to reset the search path for
+        # those modules.
+
+        nspkgs = getattr(self.distribution, 'namespace_packages')
+        if nspkgs is not None:
+            for nm in nspkgs:
+                del sys.modules[nm]
+
+        # Reset pkg_resources state:
+        add_activation_listener(lambda dist: dist.activate())
+        working_set.__init__()
+        require('%s==%s'%(ei_cmd.egg_name, ei_cmd.egg_version))
+
+    def remove_from_sys_path(self):
+        from pkg_resources import working_set
+        sys.path[:] = self.__old_path
+        sys.modules.clear()
+        sys.modules.update(self.__old_modules)
+        working_set.__init__()
+
+
+    def run(self):
+        import unittest
+
+        # Ensure that build directory is on sys.path (py3k)
+
+        self.cleanup_environment()
+        self.add_project_to_sys_path()
+
+        try:
+            meta = self.distribution.metadata
+            name = meta.get_name()
+            test_pkg = name + "_tests"
+            suite = importExternalTestCases(unittest,
+                    "test_*.py", test_pkg, test_pkg)
+
+            runner = unittest.TextTestRunner(verbosity=self.verbosity)
+            result = runner.run(suite)
+
+            # Print out summary. This is a structured format that
+            # should make it easy to use this information in scripts.
+            summary = dict(
+                count=result.testsRun,
+                fails=len(result.failures),
+                errors=len(result.errors),
+                xfails=len(getattr(result, 'expectedFailures', [])),
+                xpass=len(getattr(result, 'expectedSuccesses', [])),
+                skip=len(getattr(result, 'skipped', [])),
+            )
+            print("SUMMARY: %s"%(summary,))
+
+        finally:
+            self.remove_from_sys_path()
+
+#
+#
+#
+#  And finally run the setuptools main entry point.
+#
+#
+#
+
+metadata = parse_setup_cfg()
+
+setup(
+    cmdclass=dict(
+        upload_docs=upload_docs,
+        test=test,
+    ),
+    **metadata
+)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/CONTRIBUTING.rst b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/CONTRIBUTING.rst
new file mode 100644
index 0000000..8121da2
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/CONTRIBUTING.rst
@@ -0,0 +1,17 @@
+If you would like to contribute to the development of OpenStack,
+you must follow the steps in the "If you're a developer, start here"
+section of this page:
+
+   http://wiki.openstack.org/HowToContribute
+
+Once those steps have been completed, changes to OpenStack
+should be submitted for review via the Gerrit tool, following
+the workflow documented at:
+
+   http://wiki.openstack.org/GerritWorkflow
+
+Pull requests submitted through GitHub will be ignored.
+
+Bugs should be filed on Launchpad, not GitHub:
+
+   https://bugs.launchpad.net/mox3
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/COPYING.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/COPYING.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/COPYING.txt
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/MANIFEST.in b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/MANIFEST.in
new file mode 100644
index 0000000..c978a52
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/MANIFEST.in
@@ -0,0 +1,6 @@
+include AUTHORS
+include ChangeLog
+exclude .gitignore
+exclude .gitreview
+
+global-exclude *.pyc
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/README.chromium b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/README.chromium
new file mode 100644
index 0000000..cecbc0a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/README.chromium
@@ -0,0 +1,19 @@
+Name: mox3
+Short Name: mox3
+URL: https://github.com/openstack/mox3
+Version: 60dd893a8095f9d7957bf6635dc1620a7908d86b (commit hash)
+License: Apache License 2.0
+License File: NOT_SHIPPED
+Security Critical: no
+
+Local modification:
+Remove doc/source/conf.py because it's not needed and cause the checklicense.py
+to fail.
+
+Description:
+Mox3 is an unofficial port of the Google mox framework
+(http://code.google.com/p/pymox/) to Python 3. It was meant to be as compatible
+with mox as possible, but small enhancements have been made. The library was
+tested on Python version 3.2, 2.7 and 2.6.
+
+This library is added since pyfakefs depends on it.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/README.rst b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/README.rst
new file mode 100644
index 0000000..7f9e9db
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/README.rst
@@ -0,0 +1,60 @@
+Mox3 - Mock object framework for Python 3
+=========================================
+
+Mox3 is an unofficial port of the Google mox framework
+(http://code.google.com/p/pymox/) to Python 3. It was meant to be as compatible
+with mox as possible, but small enhancements have been made. The library was
+tested on Python version 3.2, 2.7 and 2.6.
+
+Use at your own risk ;) 
+
+To install:
+
+  $ python setup.py install
+
+Running Tests
+-------------
+The testing system is based on a combination of tox and testr. The canonical
+approach to running tests is to simply run the command `tox`. This will
+create virtual environments, populate them with depenedencies and run all of
+the tests that OpenStack CI systems run. Behind the scenes, tox is running
+`testr run --parallel`, but is set up such that you can supply any additional
+testr arguments that are needed to tox. For example, you can run:
+`tox -- --analyze-isolation` to cause tox to tell testr to add
+--analyze-isolation to its argument list.
+
+It is also possible to run the tests inside of a virtual environment
+you have created, or it is possible that you have all of the dependencies
+installed locally already. In this case, you can interact with the testr
+command directly. Running `testr run` will run the entire test suite. `testr
+run --parallel` will run it in parallel (this is the default incantation tox
+uses.) More information about testr can be found at:
+http://wiki.openstack.org/testr
+
+Basic Usage
+-----------
+  
+The basic usage of mox3 is the same as with mox, but the initial import should
+be made from the mox3 module:
+
+  from mox3 import mox
+
+To learn how to use mox3 you may check the documentation of the original mox
+framework:
+
+  http://code.google.com/p/pymox/wiki/MoxDocumentation
+
+Original Copyright
+------------------
+
+Mox is Copyright 2008 Google Inc, and licensed under the Apache
+License, Version 2.0; see the file COPYING.txt for details.  If you would
+like to help us improve Mox, join the group.
+
+OpenStack Fork
+--------------
+
+* Free software: Apache license
+* Documentation: http://docs.openstack.org/developer/mox3
+* Source: http://git.openstack.org/cgit/openstack/mox3
+* Bugs: http://bugs.launchpad.net/python-mox3
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/doc/source/contributing.rst b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/doc/source/contributing.rst
new file mode 100644
index 0000000..2ca75d1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/doc/source/contributing.rst
@@ -0,0 +1,5 @@
+==============
+ Contributing
+==============
+
+.. include:: ../../CONTRIBUTING.rst
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/doc/source/index.rst b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/doc/source/index.rst
new file mode 100644
index 0000000..2df4863
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/doc/source/index.rst
@@ -0,0 +1,21 @@
+mox3
+====
+
+A fork of mox with Python 3 support.
+
+Contents
+========
+
+.. toctree::
+   :maxdepth: 2
+
+   readme
+   contributing
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
+
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/doc/source/readme.rst b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/doc/source/readme.rst
new file mode 100644
index 0000000..a6210d3
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/doc/source/readme.rst
@@ -0,0 +1 @@
+.. include:: ../../README.rst
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/mox3/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/mox3/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/mox3/__init__.py
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/mox3/fixture.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/mox3/fixture.py
new file mode 100644
index 0000000..f6e39d8
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/mox3/fixture.py
@@ -0,0 +1,33 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 Hewlett-Packard Development Company, L.P.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import fixtures
+from mox3 import mox
+from mox3 import stubout
+
+
+class MoxStubout(fixtures.Fixture):
+    """Deal with code around mox and stubout as a fixture."""
+
+    def setUp(self):
+        super(MoxStubout, self).setUp()
+        self.mox = mox.Mox()
+        self.stubs = stubout.StubOutForTesting()
+        self.addCleanup(self.mox.UnsetStubs)
+        self.addCleanup(self.stubs.UnsetAll)
+        self.addCleanup(self.stubs.SmartUnsetAll)
+        self.addCleanup(self.mox.VerifyAll)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/mox3/mox.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/mox3/mox.py
new file mode 100644
index 0000000..3c10cc8
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/mox3/mox.py
@@ -0,0 +1,2168 @@
+# Copyright 2008 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# This is a fork of the pymox library intended to work with Python 3.
+# The file was modified by quermit@gmail.com and dawid.fatyga@gmail.com
+
+"""Mox, an object-mocking framework for Python.
+
+Mox works in the record-replay-verify paradigm.  When you first create
+a mock object, it is in record mode.  You then programmatically set
+the expected behavior of the mock object (what methods are to be
+called on it, with what parameters, what they should return, and in
+what order).
+
+Once you have set up the expected mock behavior, you put it in replay
+mode.  Now the mock responds to method calls just as you told it to.
+If an unexpected method (or an expected method with unexpected
+parameters) is called, then an exception will be raised.
+
+Once you are done interacting with the mock, you need to verify that
+all the expected interactions occured.  (Maybe your code exited
+prematurely without calling some cleanup method!)  The verify phase
+ensures that every expected method was called; otherwise, an exception
+will be raised.
+
+WARNING! Mock objects created by Mox are not thread-safe.  If you are
+call a mock in multiple threads, it should be guarded by a mutex.
+
+TODO(stevepm): Add the option to make mocks thread-safe!
+
+Suggested usage / workflow:
+
+    # Create Mox factory
+    my_mox = Mox()
+
+    # Create a mock data access object
+    mock_dao = my_mox.CreateMock(DAOClass)
+
+    # Set up expected behavior
+    mock_dao.RetrievePersonWithIdentifier('1').AndReturn(person)
+    mock_dao.DeletePerson(person)
+
+    # Put mocks in replay mode
+    my_mox.ReplayAll()
+
+    # Inject mock object and run test
+    controller.SetDao(mock_dao)
+    controller.DeletePersonById('1')
+
+    # Verify all methods were called as expected
+    my_mox.VerifyAll()
+"""
+
+import collections
+import difflib
+import inspect
+import re
+import types
+import unittest
+
+from mox3 import stubout
+
+
+class Error(AssertionError):
+    """Base exception for this module."""
+
+    pass
+
+
+class ExpectedMethodCallsError(Error):
+    """Raised when an expected method wasn't called.
+
+    This can occur if Verify() is called before all expected methods have been
+    called.
+    """
+
+    def __init__(self, expected_methods):
+        """Init exception.
+
+        Args:
+            # expected_methods: A sequence of MockMethod objects that should
+            #                   have been called.
+            expected_methods: [MockMethod]
+
+        Raises:
+            ValueError: if expected_methods contains no methods.
+        """
+
+        if not expected_methods:
+            raise ValueError("There must be at least one expected method")
+        Error.__init__(self)
+        self._expected_methods = expected_methods
+
+    def __str__(self):
+        calls = "\n".join(["%3d.  %s" % (i, m)
+                          for i, m in enumerate(self._expected_methods)])
+        return "Verify: Expected methods never called:\n%s" % (calls,)
+
+
+class UnexpectedMethodCallError(Error):
+    """Raised when an unexpected method is called.
+
+    This can occur if a method is called with incorrect parameters, or out of
+    the specified order.
+    """
+
+    def __init__(self, unexpected_method, expected):
+        """Init exception.
+
+        Args:
+            # unexpected_method: MockMethod that was called but was not at the
+            #     head of the expected_method queue.
+            # expected: MockMethod or UnorderedGroup the method should have
+            #     been in.
+            unexpected_method: MockMethod
+            expected: MockMethod or UnorderedGroup
+        """
+
+        Error.__init__(self)
+        if expected is None:
+            self._str = "Unexpected method call %s" % (unexpected_method,)
+        else:
+            differ = difflib.Differ()
+            diff = differ.compare(str(unexpected_method).splitlines(True),
+                                  str(expected).splitlines(True))
+            self._str = ("Unexpected method call."
+                         "  unexpected:-  expected:+\n%s"
+                         % ("\n".join(line.rstrip() for line in diff),))
+
+    def __str__(self):
+        return self._str
+
+
+class UnknownMethodCallError(Error):
+    """Raised if an unknown method is requested of the mock object."""
+
+    def __init__(self, unknown_method_name):
+        """Init exception.
+
+        Args:
+            # unknown_method_name: Method call that is not part of the mocked
+            #     class's public interface.
+            unknown_method_name: str
+        """
+
+        Error.__init__(self)
+        self._unknown_method_name = unknown_method_name
+
+    def __str__(self):
+        return ("Method called is not a member of the object: %s" %
+                self._unknown_method_name)
+
+
+class PrivateAttributeError(Error):
+    """Raised if a MockObject is passed a private additional attribute name."""
+
+    def __init__(self, attr):
+        Error.__init__(self)
+        self._attr = attr
+
+    def __str__(self):
+        return ("Attribute '%s' is private and should not be available"
+                "in a mock object." % self._attr)
+
+
+class ExpectedMockCreationError(Error):
+    """Raised if mocks should have been created by StubOutClassWithMocks."""
+
+    def __init__(self, expected_mocks):
+        """Init exception.
+
+        Args:
+            # expected_mocks: A sequence of MockObjects that should have been
+            #     created
+
+        Raises:
+            ValueError: if expected_mocks contains no methods.
+        """
+
+        if not expected_mocks:
+            raise ValueError("There must be at least one expected method")
+        Error.__init__(self)
+        self._expected_mocks = expected_mocks
+
+    def __str__(self):
+        mocks = "\n".join(["%3d.  %s" % (i, m)
+                          for i, m in enumerate(self._expected_mocks)])
+        return "Verify: Expected mocks never created:\n%s" % (mocks,)
+
+
+class UnexpectedMockCreationError(Error):
+    """Raised if too many mocks were created by StubOutClassWithMocks."""
+
+    def __init__(self, instance, *params, **named_params):
+        """Init exception.
+
+        Args:
+            # instance: the type of obejct that was created
+            # params: parameters given during instantiation
+            # named_params: named parameters given during instantiation
+        """
+
+        Error.__init__(self)
+        self._instance = instance
+        self._params = params
+        self._named_params = named_params
+
+    def __str__(self):
+        args = ", ".join(["%s" % v for i, v in enumerate(self._params)])
+        error = "Unexpected mock creation: %s(%s" % (self._instance, args)
+
+        if self._named_params:
+            error += ", " + ", ".join(["%s=%s" % (k, v) for k, v in
+                                      self._named_params.items()])
+
+        error += ")"
+        return error
+
+
+class Mox(object):
+    """Mox: a factory for creating mock objects."""
+
+    # A list of types that should be stubbed out with MockObjects (as
+    # opposed to MockAnythings).
+    _USE_MOCK_OBJECT = [types.FunctionType, types.ModuleType, types.MethodType]
+
+    def __init__(self):
+        """Initialize a new Mox."""
+
+        self._mock_objects = []
+        self.stubs = stubout.StubOutForTesting()
+
+    def CreateMock(self, class_to_mock, attrs=None, bounded_to=None):
+        """Create a new mock object.
+
+        Args:
+            # class_to_mock: the class to be mocked
+            class_to_mock: class
+            attrs: dict of attribute names to values that will be
+                   set on the mock object. Only public attributes may be set.
+            bounded_to: optionally, when class_to_mock is not a class,
+                        it points to a real class object, to which
+                        attribute is bound
+
+        Returns:
+            MockObject that can be used as the class_to_mock would be.
+        """
+        if attrs is None:
+            attrs = {}
+        new_mock = MockObject(class_to_mock, attrs=attrs,
+                              class_to_bind=bounded_to)
+        self._mock_objects.append(new_mock)
+        return new_mock
+
+    def CreateMockAnything(self, description=None):
+        """Create a mock that will accept any method calls.
+
+        This does not enforce an interface.
+
+        Args:
+        description: str. Optionally, a descriptive name for the mock object
+        being created, for debugging output purposes.
+        """
+        new_mock = MockAnything(description=description)
+        self._mock_objects.append(new_mock)
+        return new_mock
+
+    def ReplayAll(self):
+        """Set all mock objects to replay mode."""
+
+        for mock_obj in self._mock_objects:
+            mock_obj._Replay()
+
+    def VerifyAll(self):
+        """Call verify on all mock objects created."""
+
+        for mock_obj in self._mock_objects:
+            mock_obj._Verify()
+
+    def ResetAll(self):
+        """Call reset on all mock objects.    This does not unset stubs."""
+
+        for mock_obj in self._mock_objects:
+            mock_obj._Reset()
+
+    def StubOutWithMock(self, obj, attr_name, use_mock_anything=False):
+        """Replace a method, attribute, etc. with a Mock.
+
+        This will replace a class or module with a MockObject, and everything
+        else (method, function, etc) with a MockAnything. This can be
+        overridden to always use a MockAnything by setting use_mock_anything
+        to True.
+
+        Args:
+            obj: A Python object (class, module, instance, callable).
+            attr_name: str. The name of the attribute to replace with a mock.
+            use_mock_anything: bool. True if a MockAnything should be used
+                               regardless of the type of attribute.
+        """
+
+        if inspect.isclass(obj):
+            class_to_bind = obj
+        else:
+            class_to_bind = None
+
+        attr_to_replace = getattr(obj, attr_name)
+        attr_type = type(attr_to_replace)
+
+        if attr_type == MockAnything or attr_type == MockObject:
+            raise TypeError('Cannot mock a MockAnything! Did you remember to '
+                            'call UnsetStubs in your previous test?')
+
+        type_check = (
+            attr_type in self._USE_MOCK_OBJECT or
+            inspect.isclass(attr_to_replace) or
+            isinstance(attr_to_replace, object))
+        if type_check and not use_mock_anything:
+            stub = self.CreateMock(attr_to_replace, bounded_to=class_to_bind)
+        else:
+            stub = self.CreateMockAnything(
+                description='Stub for %s' % attr_to_replace)
+            stub.__name__ = attr_name
+
+        self.stubs.Set(obj, attr_name, stub)
+
+    def StubOutClassWithMocks(self, obj, attr_name):
+        """Replace a class with a "mock factory" that will create mock objects.
+
+        This is useful if the code-under-test directly instantiates
+        dependencies.    Previously some boilder plate was necessary to
+        create a mock that would act as a factory.    Using
+        StubOutClassWithMocks, once you've stubbed out the class you may
+        use the stubbed class as you would any other mock created by mox:
+        during the record phase, new mock instances will be created, and
+        during replay, the recorded mocks will be returned.
+
+        In replay mode
+
+        # Example using StubOutWithMock (the old, clunky way):
+
+        mock1 = mox.CreateMock(my_import.FooClass)
+        mock2 = mox.CreateMock(my_import.FooClass)
+        foo_factory = mox.StubOutWithMock(my_import, 'FooClass',
+                                          use_mock_anything=True)
+        foo_factory(1, 2).AndReturn(mock1)
+        foo_factory(9, 10).AndReturn(mock2)
+        mox.ReplayAll()
+
+        my_import.FooClass(1, 2)     # Returns mock1 again.
+        my_import.FooClass(9, 10)    # Returns mock2 again.
+        mox.VerifyAll()
+
+        # Example using StubOutClassWithMocks:
+
+        mox.StubOutClassWithMocks(my_import, 'FooClass')
+        mock1 = my_import.FooClass(1, 2)     # Returns a new mock of FooClass
+        mock2 = my_import.FooClass(9, 10)    # Returns another mock instance
+        mox.ReplayAll()
+
+        my_import.FooClass(1, 2)     # Returns mock1 again.
+        my_import.FooClass(9, 10)    # Returns mock2 again.
+        mox.VerifyAll()
+        """
+        attr_to_replace = getattr(obj, attr_name)
+        attr_type = type(attr_to_replace)
+
+        if attr_type == MockAnything or attr_type == MockObject:
+            raise TypeError('Cannot mock a MockAnything! Did you remember to '
+                            'call UnsetStubs in your previous test?')
+
+        if not inspect.isclass(attr_to_replace):
+            raise TypeError('Given attr is not a Class. Use StubOutWithMock.')
+
+        factory = _MockObjectFactory(attr_to_replace, self)
+        self._mock_objects.append(factory)
+        self.stubs.Set(obj, attr_name, factory)
+
+    def UnsetStubs(self):
+        """Restore stubs to their original state."""
+
+        self.stubs.UnsetAll()
+
+
+def Replay(*args):
+    """Put mocks into Replay mode.
+
+    Args:
+        # args is any number of mocks to put into replay mode.
+    """
+
+    for mock in args:
+        mock._Replay()
+
+
+def Verify(*args):
+    """Verify mocks.
+
+    Args:
+        # args is any number of mocks to be verified.
+    """
+
+    for mock in args:
+        mock._Verify()
+
+
+def Reset(*args):
+    """Reset mocks.
+
+    Args:
+        # args is any number of mocks to be reset.
+    """
+
+    for mock in args:
+        mock._Reset()
+
+
+class MockAnything(object):
+    """A mock that can be used to mock anything.
+
+    This is helpful for mocking classes that do not provide a public interface.
+    """
+
+    def __init__(self, description=None):
+        """Initialize a new MockAnything.
+
+        Args:
+            description: str. Optionally, a descriptive name for the mock
+                         object being created, for debugging output purposes.
+        """
+        self._description = description
+        self._Reset()
+
+    def __repr__(self):
+        if self._description:
+            return '<MockAnything instance of %s>' % self._description
+        else:
+            return '<MockAnything instance>'
+
+    def __getattr__(self, method_name):
+        """Intercept method calls on this object.
+
+         A new MockMethod is returned that is aware of the MockAnything's
+         state (record or replay).    The call will be recorded or replayed
+         by the MockMethod's __call__.
+
+        Args:
+            # method name: the name of the method being called.
+            method_name: str
+
+        Returns:
+            A new MockMethod aware of MockAnything's state (record or replay).
+        """
+        if method_name == '__dir__':
+                return self.__class__.__dir__.__get__(self, self.__class__)
+
+        return self._CreateMockMethod(method_name)
+
+    def __str__(self):
+        return self._CreateMockMethod('__str__')()
+
+    def __call__(self, *args, **kwargs):
+        return self._CreateMockMethod('__call__')(*args, **kwargs)
+
+    def __getitem__(self, i):
+        return self._CreateMockMethod('__getitem__')(i)
+
+    def _CreateMockMethod(self, method_name, method_to_mock=None,
+                          class_to_bind=object):
+        """Create a new mock method call and return it.
+
+        Args:
+            # method_name: the name of the method being called.
+            # method_to_mock: The actual method being mocked, used for
+            #                 introspection.
+            # class_to_bind: Class to which method is bounded
+            #                (object by default)
+            method_name: str
+            method_to_mock: a method object
+
+        Returns:
+            A new MockMethod aware of MockAnything's state (record or replay).
+        """
+
+        return MockMethod(method_name, self._expected_calls_queue,
+                          self._replay_mode, method_to_mock=method_to_mock,
+                          description=self._description,
+                          class_to_bind=class_to_bind)
+
+    def __nonzero__(self):
+        """Return 1 for nonzero so the mock can be used as a conditional."""
+
+        return 1
+
+    def __bool__(self):
+        """Return True for nonzero so the mock can be used as a conditional."""
+        return True
+
+    def __eq__(self, rhs):
+        """Provide custom logic to compare objects."""
+
+        return (isinstance(rhs, MockAnything) and
+                self._replay_mode == rhs._replay_mode and
+                self._expected_calls_queue == rhs._expected_calls_queue)
+
+    def __ne__(self, rhs):
+        """Provide custom logic to compare objects."""
+
+        return not self == rhs
+
+    def _Replay(self):
+        """Start replaying expected method calls."""
+
+        self._replay_mode = True
+
+    def _Verify(self):
+        """Verify that all of the expected calls have been made.
+
+        Raises:
+            ExpectedMethodCallsError: if there are still more method calls in
+                                      the expected queue.
+        """
+
+        # If the list of expected calls is not empty, raise an exception
+        if self._expected_calls_queue:
+            # The last MultipleTimesGroup is not popped from the queue.
+            if (len(self._expected_calls_queue) == 1 and
+                    isinstance(self._expected_calls_queue[0],
+                               MultipleTimesGroup) and
+                    self._expected_calls_queue[0].IsSatisfied()):
+                pass
+            else:
+                raise ExpectedMethodCallsError(self._expected_calls_queue)
+
+    def _Reset(self):
+        """Reset the state of this mock to record mode with an empty queue."""
+
+        # Maintain a list of method calls we are expecting
+        self._expected_calls_queue = collections.deque()
+
+        # Make sure we are in setup mode, not replay mode
+        self._replay_mode = False
+
+
+class MockObject(MockAnything):
+    """Mock object that simulates the public/protected interface of a class."""
+
+    def __init__(self, class_to_mock, attrs=None, class_to_bind=None):
+        """Initialize a mock object.
+
+        Determines the methods and properties of the class and stores them.
+
+        Args:
+            # class_to_mock: class to be mocked
+            class_to_mock: class
+            attrs: dict of attribute names to values that will be set on the
+                   mock object. Only public attributes may be set.
+            class_to_bind: optionally, when class_to_mock is not a class at
+                           all, it points to a real class
+
+        Raises:
+            PrivateAttributeError: if a supplied attribute is not public.
+            ValueError: if an attribute would mask an existing method.
+        """
+        if attrs is None:
+            attrs = {}
+
+        # Used to hack around the mixin/inheritance of MockAnything, which
+        # is not a proper object (it can be anything. :-)
+        MockAnything.__dict__['__init__'](self)
+
+        # Get a list of all the public and special methods we should mock.
+        self._known_methods = set()
+        self._known_vars = set()
+        self._class_to_mock = class_to_mock
+
+        if inspect.isclass(class_to_mock):
+            self._class_to_bind = self._class_to_mock
+        else:
+            self._class_to_bind = class_to_bind
+
+        try:
+            if inspect.isclass(self._class_to_mock):
+                self._description = class_to_mock.__name__
+            else:
+                self._description = type(class_to_mock).__name__
+        except Exception:
+            pass
+
+        for method in dir(class_to_mock):
+            attr = getattr(class_to_mock, method)
+            if callable(attr):
+                self._known_methods.add(method)
+            elif not (type(attr) is property):
+                # treating properties as class vars makes little sense.
+                self._known_vars.add(method)
+
+        # Set additional attributes at instantiation time; this is quicker
+        # than manually setting attributes that are normally created in
+        # __init__.
+        for attr, value in attrs.items():
+            if attr.startswith("_"):
+                raise PrivateAttributeError(attr)
+            elif attr in self._known_methods:
+                raise ValueError("'%s' is a method of '%s' objects." % (attr,
+                                 class_to_mock))
+            else:
+                setattr(self, attr, value)
+
+    def _CreateMockMethod(self, *args, **kwargs):
+        """Overridden to provide self._class_to_mock to class_to_bind."""
+        kwargs.setdefault("class_to_bind", self._class_to_bind)
+        return super(MockObject, self)._CreateMockMethod(*args, **kwargs)
+
+    def __getattr__(self, name):
+        """Intercept attribute request on this object.
+
+        If the attribute is a public class variable, it will be returned and
+        not recorded as a call.
+
+        If the attribute is not a variable, it is handled like a method
+        call. The method name is checked against the set of mockable
+        methods, and a new MockMethod is returned that is aware of the
+        MockObject's state (record or replay).    The call will be recorded
+        or replayed by the MockMethod's __call__.
+
+        Args:
+            # name: the name of the attribute being requested.
+            name: str
+
+        Returns:
+            Either a class variable or a new MockMethod that is aware of the
+            state of the mock (record or replay).
+
+        Raises:
+            UnknownMethodCallError if the MockObject does not mock the
+            requested method.
+        """
+
+        if name in self._known_vars:
+            return getattr(self._class_to_mock, name)
+
+        if name in self._known_methods:
+            return self._CreateMockMethod(
+                name,
+                method_to_mock=getattr(self._class_to_mock, name))
+
+        raise UnknownMethodCallError(name)
+
+    def __eq__(self, rhs):
+        """Provide custom logic to compare objects."""
+
+        return (isinstance(rhs, MockObject) and
+                self._class_to_mock == rhs._class_to_mock and
+                self._replay_mode == rhs._replay_mode and
+                self._expected_calls_queue == rhs._expected_calls_queue)
+
+    def __setitem__(self, key, value):
+        """Custom logic for mocking classes that support item assignment.
+
+        Args:
+            key: Key to set the value for.
+            value: Value to set.
+
+        Returns:
+            Expected return value in replay mode. A MockMethod object for the
+            __setitem__ method that has already been called if not in replay
+            mode.
+
+        Raises:
+            TypeError if the underlying class does not support item assignment.
+            UnexpectedMethodCallError if the object does not expect the call to
+                __setitem__.
+
+        """
+        # Verify the class supports item assignment.
+        if '__setitem__' not in dir(self._class_to_mock):
+            raise TypeError('object does not support item assignment')
+
+        # If we are in replay mode then simply call the mock __setitem__ method
+        if self._replay_mode:
+            return MockMethod('__setitem__', self._expected_calls_queue,
+                              self._replay_mode)(key, value)
+
+        # Otherwise, create a mock method __setitem__.
+        return self._CreateMockMethod('__setitem__')(key, value)
+
+    def __getitem__(self, key):
+        """Provide custom logic for mocking classes that are subscriptable.
+
+        Args:
+            key: Key to return the value for.
+
+        Returns:
+            Expected return value in replay mode. A MockMethod object for the
+            __getitem__ method that has already been called if not in replay
+            mode.
+
+        Raises:
+            TypeError if the underlying class is not subscriptable.
+            UnexpectedMethodCallError if the object does not expect the call to
+                __getitem__.
+
+        """
+        # Verify the class supports item assignment.
+        if '__getitem__' not in dir(self._class_to_mock):
+            raise TypeError('unsubscriptable object')
+
+        # If we are in replay mode then simply call the mock __getitem__ method
+        if self._replay_mode:
+            return MockMethod('__getitem__', self._expected_calls_queue,
+                              self._replay_mode)(key)
+
+        # Otherwise, create a mock method __getitem__.
+        return self._CreateMockMethod('__getitem__')(key)
+
+    def __iter__(self):
+        """Provide custom logic for mocking classes that are iterable.
+
+        Returns:
+            Expected return value in replay mode. A MockMethod object for the
+            __iter__ method that has already been called if not in replay mode.
+
+        Raises:
+            TypeError if the underlying class is not iterable.
+            UnexpectedMethodCallError if the object does not expect the call to
+                __iter__.
+
+        """
+        methods = dir(self._class_to_mock)
+
+        # Verify the class supports iteration.
+        if '__iter__' not in methods:
+            # If it doesn't have iter method and we are in replay method,
+            # then try to iterate using subscripts.
+            if '__getitem__' not in methods or not self._replay_mode:
+                raise TypeError('not iterable object')
+            else:
+                results = []
+                index = 0
+                try:
+                    while True:
+                        results.append(self[index])
+                        index += 1
+                except IndexError:
+                    return iter(results)
+
+        # If we are in replay mode then simply call the mock __iter__ method.
+        if self._replay_mode:
+            return MockMethod('__iter__', self._expected_calls_queue,
+                              self._replay_mode)()
+
+        # Otherwise, create a mock method __iter__.
+        return self._CreateMockMethod('__iter__')()
+
+    def __contains__(self, key):
+        """Provide custom logic for mocking classes that contain items.
+
+        Args:
+            key: Key to look in container for.
+
+        Returns:
+            Expected return value in replay mode. A MockMethod object for the
+            __contains__ method that has already been called if not in replay
+            mode.
+
+        Raises:
+            TypeError if the underlying class does not implement __contains__
+            UnexpectedMethodCaller if the object does not expect the call to
+            __contains__.
+
+        """
+        contains = self._class_to_mock.__dict__.get('__contains__', None)
+
+        if contains is None:
+            raise TypeError('unsubscriptable object')
+
+        if self._replay_mode:
+            return MockMethod('__contains__', self._expected_calls_queue,
+                              self._replay_mode)(key)
+
+        return self._CreateMockMethod('__contains__')(key)
+
+    def __call__(self, *params, **named_params):
+        """Provide custom logic for mocking classes that are callable."""
+
+        # Verify the class we are mocking is callable.
+        is_callable = hasattr(self._class_to_mock, '__call__')
+        if not is_callable:
+            raise TypeError('Not callable')
+
+        # Because the call is happening directly on this object instead of
+        # a method, the call on the mock method is made right here
+
+        # If we are mocking a Function, then use the function, and not the
+        # __call__ method
+        method = None
+        if type(self._class_to_mock) in (types.FunctionType, types.MethodType):
+            method = self._class_to_mock
+        else:
+            method = getattr(self._class_to_mock, '__call__')
+        mock_method = self._CreateMockMethod('__call__', method_to_mock=method)
+
+        return mock_method(*params, **named_params)
+
+    @property
+    def __name__(self):
+        """Return the name that is being mocked."""
+        return self._description
+
+    # TODO(dejw): this property stopped to work after I introduced changes with
+    #     binding classes. Fortunately I found a solution in the form of
+    #     __getattribute__ method below, but this issue should be investigated
+    @property
+    def __class__(self):
+        return self._class_to_mock
+
+    def __dir__(self):
+        """Return only attributes of a class to mock."""
+        return dir(self._class_to_mock)
+
+    def __getattribute__(self, name):
+        """Return _class_to_mock on __class__ attribute."""
+        if name == "__class__":
+            return super(MockObject, self).__getattribute__("_class_to_mock")
+
+        return super(MockObject, self).__getattribute__(name)
+
+
+class _MockObjectFactory(MockObject):
+    """A MockObjectFactory creates mocks and verifies __init__ params.
+
+    A MockObjectFactory removes the boiler plate code that was previously
+    necessary to stub out direction instantiation of a class.
+
+    The MockObjectFactory creates new MockObjects when called and verifies the
+    __init__ params are correct when in record mode.    When replaying,
+    existing mocks are returned, and the __init__ params are verified.
+
+    See StubOutWithMock vs StubOutClassWithMocks for more detail.
+    """
+
+    def __init__(self, class_to_mock, mox_instance):
+        MockObject.__init__(self, class_to_mock)
+        self._mox = mox_instance
+        self._instance_queue = collections.deque()
+
+    def __call__(self, *params, **named_params):
+        """Instantiate and record that a new mock has been created."""
+
+        method = getattr(self._class_to_mock, '__init__')
+        mock_method = self._CreateMockMethod('__init__', method_to_mock=method)
+        # Note: calling mock_method() is deferred in order to catch the
+        # empty instance_queue first.
+
+        if self._replay_mode:
+            if not self._instance_queue:
+                raise UnexpectedMockCreationError(self._class_to_mock, *params,
+                                                  **named_params)
+
+            mock_method(*params, **named_params)
+
+            return self._instance_queue.pop()
+        else:
+            mock_method(*params, **named_params)
+
+            instance = self._mox.CreateMock(self._class_to_mock)
+            self._instance_queue.appendleft(instance)
+            return instance
+
+    def _Verify(self):
+        """Verify that all mocks have been created."""
+        if self._instance_queue:
+            raise ExpectedMockCreationError(self._instance_queue)
+        super(_MockObjectFactory, self)._Verify()
+
+
+class MethodSignatureChecker(object):
+    """Ensures that methods are called correctly."""
+
+    _NEEDED, _DEFAULT, _GIVEN = range(3)
+
+    def __init__(self, method, class_to_bind=None):
+        """Creates a checker.
+
+        Args:
+            # method: A method to check.
+            # class_to_bind: optionally, a class used to type check first
+            #                method parameter, only used with unbound methods
+            method: function
+            class_to_bind: type or None
+
+        Raises:
+            ValueError: method could not be inspected, so checks aren't
+                        possible. Some methods and functions like built-ins
+                        can't be inspected.
+        """
+        try:
+            self._args, varargs, varkw, defaults = inspect.getargspec(method)
+        except TypeError:
+            raise ValueError('Could not get argument specification for %r'
+                             % (method,))
+        if inspect.ismethod(method) or class_to_bind:
+            self._args = self._args[1:]    # Skip 'self'.
+        self._method = method
+        self._instance = None    # May contain the instance this is bound to.
+        self._instance = getattr(method, "__self__", None)
+
+        # _bounded_to determines whether the method is bound or not
+        if self._instance:
+            self._bounded_to = self._instance.__class__
+        else:
+            self._bounded_to = class_to_bind or getattr(method, "im_class",
+                                                        None)
+
+        self._has_varargs = varargs is not None
+        self._has_varkw = varkw is not None
+        if defaults is None:
+            self._required_args = self._args
+            self._default_args = []
+        else:
+            self._required_args = self._args[:-len(defaults)]
+            self._default_args = self._args[-len(defaults):]
+
+    def _RecordArgumentGiven(self, arg_name, arg_status):
+        """Mark an argument as being given.
+
+        Args:
+            # arg_name: The name of the argument to mark in arg_status.
+            # arg_status: Maps argument names to one of
+            #             _NEEDED, _DEFAULT, _GIVEN.
+            arg_name: string
+            arg_status: dict
+
+        Raises:
+            AttributeError: arg_name is already marked as _GIVEN.
+        """
+        if arg_status.get(arg_name, None) == MethodSignatureChecker._GIVEN:
+            raise AttributeError('%s provided more than once' % (arg_name,))
+        arg_status[arg_name] = MethodSignatureChecker._GIVEN
+
+    def Check(self, params, named_params):
+        """Ensures that the parameters used while recording a call are valid.
+
+        Args:
+            # params: A list of positional parameters.
+            # named_params: A dict of named parameters.
+            params: list
+            named_params: dict
+
+        Raises:
+            AttributeError: the given parameters don't work with the given
+                            method.
+        """
+        arg_status = dict((a, MethodSignatureChecker._NEEDED)
+                          for a in self._required_args)
+        for arg in self._default_args:
+            arg_status[arg] = MethodSignatureChecker._DEFAULT
+
+        # WARNING: Suspect hack ahead.
+        #
+        # Check to see if this is an unbound method, where the instance
+        # should be bound as the first argument.    We try to determine if
+        # the first argument (param[0]) is an instance of the class, or it
+        # is equivalent to the class (used to account for Comparators).
+        #
+        # NOTE: If a Func() comparator is used, and the signature is not
+        # correct, this will cause extra executions of the function.
+        if inspect.ismethod(self._method) or self._bounded_to:
+            # The extra param accounts for the bound instance.
+            if len(params) > len(self._required_args):
+                expected = self._bounded_to
+
+                # Check if the param is an instance of the expected class,
+                # or check equality (useful for checking Comparators).
+
+                # This is a hack to work around the fact that the first
+                # parameter can be a Comparator, and the comparison may raise
+                # an exception during this comparison, which is OK.
+                try:
+                    param_equality = (params[0] == expected)
+                except Exception:
+                    param_equality = False
+
+                if isinstance(params[0], expected) or param_equality:
+                    params = params[1:]
+                # If the IsA() comparator is being used, we need to check the
+                # inverse of the usual case - that the given instance is a
+                # subclass of the expected class. For example, the code under
+                # test does late binding to a subclass.
+                elif (isinstance(params[0], IsA) and
+                      params[0]._IsSubClass(expected)):
+                    params = params[1:]
+
+        # Check that each positional param is valid.
+        for i in range(len(params)):
+            try:
+                arg_name = self._args[i]
+            except IndexError:
+                if not self._has_varargs:
+                    raise AttributeError(
+                        '%s does not take %d or more positional '
+                        'arguments' % (self._method.__name__, i))
+            else:
+                self._RecordArgumentGiven(arg_name, arg_status)
+
+        # Check each keyword argument.
+        for arg_name in named_params:
+            if arg_name not in arg_status and not self._has_varkw:
+                raise AttributeError('%s is not expecting keyword argument %s'
+                                     % (self._method.__name__, arg_name))
+            self._RecordArgumentGiven(arg_name, arg_status)
+
+        # Ensure all the required arguments have been given.
+        still_needed = [k for k, v in arg_status.items()
+                        if v == MethodSignatureChecker._NEEDED]
+        if still_needed:
+            raise AttributeError('No values given for arguments: %s'
+                                 % (' '.join(sorted(still_needed))))
+
+
+class MockMethod(object):
+    """Callable mock method.
+
+    A MockMethod should act exactly like the method it mocks, accepting
+    parameters and returning a value, or throwing an exception (as specified).
+    When this method is called, it can optionally verify whether the called
+    method (name and signature) matches the expected method.
+    """
+
+    def __init__(self, method_name, call_queue, replay_mode,
+                 method_to_mock=None, description=None, class_to_bind=None):
+        """Construct a new mock method.
+
+        Args:
+            # method_name: the name of the method
+            # call_queue: deque of calls, verify this call against the head,
+            #             or add this call to the queue.
+            # replay_mode: False if we are recording, True if we are verifying
+            #              calls against the call queue.
+            # method_to_mock: The actual method being mocked, used for
+            #                 introspection.
+            # description: optionally, a descriptive name for this method.
+            #              Typically this is equal to the descriptive name of
+            #              the method's class.
+            # class_to_bind: optionally, a class that is used for unbound
+            #                methods (or functions in Python3) to which method
+            #                is bound, in order not to loose binding
+            #                information. If given, it will be used for
+            #                checking the type of first method parameter
+            method_name: str
+            call_queue: list or deque
+            replay_mode: bool
+            method_to_mock: a method object
+            description: str or None
+            class_to_bind: type or None
+        """
+
+        self._name = method_name
+        self.__name__ = method_name
+        self._call_queue = call_queue
+        if not isinstance(call_queue, collections.deque):
+            self._call_queue = collections.deque(self._call_queue)
+        self._replay_mode = replay_mode
+        self._description = description
+
+        self._params = None
+        self._named_params = None
+        self._return_value = None
+        self._exception = None
+        self._side_effects = None
+
+        try:
+            self._checker = MethodSignatureChecker(method_to_mock,
+                                                   class_to_bind=class_to_bind)
+        except ValueError:
+            self._checker = None
+
+    def __call__(self, *params, **named_params):
+        """Log parameters and return the specified return value.
+
+        If the Mock(Anything/Object) associated with this call is in record
+        mode, this MockMethod will be pushed onto the expected call queue.
+        If the mock is in replay mode, this will pop a MockMethod off the
+        top of the queue and verify this call is equal to the expected call.
+
+        Raises:
+            UnexpectedMethodCall if this call is supposed to match an expected
+                method call and it does not.
+        """
+
+        self._params = params
+        self._named_params = named_params
+
+        if not self._replay_mode:
+            if self._checker is not None:
+                self._checker.Check(params, named_params)
+            self._call_queue.append(self)
+            return self
+
+        expected_method = self._VerifyMethodCall()
+
+        if expected_method._side_effects:
+            result = expected_method._side_effects(*params, **named_params)
+            if expected_method._return_value is None:
+                expected_method._return_value = result
+
+        if expected_method._exception:
+            raise expected_method._exception
+
+        return expected_method._return_value
+
+    def __getattr__(self, name):
+        """Raise an AttributeError with a helpful message."""
+
+        raise AttributeError(
+            'MockMethod has no attribute "%s". '
+            'Did you remember to put your mocks in replay mode?' % name)
+
+    def __iter__(self):
+        """Raise a TypeError with a helpful message."""
+        raise TypeError(
+            'MockMethod cannot be iterated. '
+            'Did you remember to put your mocks in replay mode?')
+
+    def next(self):
+        """Raise a TypeError with a helpful message."""
+        raise TypeError(
+            'MockMethod cannot be iterated. '
+            'Did you remember to put your mocks in replay mode?')
+
+    def __next__(self):
+        """Raise a TypeError with a helpful message."""
+        raise TypeError(
+            'MockMethod cannot be iterated. '
+            'Did you remember to put your mocks in replay mode?')
+
+    def _PopNextMethod(self):
+        """Pop the next method from our call queue."""
+        try:
+            return self._call_queue.popleft()
+        except IndexError:
+            raise UnexpectedMethodCallError(self, None)
+
+    def _VerifyMethodCall(self):
+        """Verify the called method is expected.
+
+        This can be an ordered method, or part of an unordered set.
+
+        Returns:
+            The expected mock method.
+
+        Raises:
+            UnexpectedMethodCall if the method called was not expected.
+        """
+
+        expected = self._PopNextMethod()
+
+        # Loop here, because we might have a MethodGroup followed by another
+        # group.
+        while isinstance(expected, MethodGroup):
+            expected, method = expected.MethodCalled(self)
+            if method is not None:
+                return method
+
+        # This is a mock method, so just check equality.
+        if expected != self:
+            raise UnexpectedMethodCallError(self, expected)
+
+        return expected
+
+    def __str__(self):
+        params = ', '.join(
+            [repr(p) for p in self._params or []] +
+            ['%s=%r' % x for x in sorted((self._named_params or {}).items())])
+        full_desc = "%s(%s) -> %r" % (self._name, params, self._return_value)
+        if self._description:
+            full_desc = "%s.%s" % (self._description, full_desc)
+        return full_desc
+
+    def __hash__(self):
+        return id(self)
+
+    def __eq__(self, rhs):
+        """Test whether this MockMethod is equivalent to another MockMethod.
+
+        Args:
+            # rhs: the right hand side of the test
+            rhs: MockMethod
+        """
+
+        return (isinstance(rhs, MockMethod) and
+                self._name == rhs._name and
+                self._params == rhs._params and
+                self._named_params == rhs._named_params)
+
+    def __ne__(self, rhs):
+        """Test if this MockMethod is not equivalent to another MockMethod.
+
+        Args:
+            # rhs: the right hand side of the test
+            rhs: MockMethod
+        """
+
+        return not self == rhs
+
+    def GetPossibleGroup(self):
+        """Returns a possible group from the end of the call queue.
+
+        Return None if no other methods are on the stack.
+        """
+
+        # Remove this method from the tail of the queue so we can add it
+        # to a group.
+        this_method = self._call_queue.pop()
+        assert this_method == self
+
+        # Determine if the tail of the queue is a group, or just a regular
+        # ordered mock method.
+        group = None
+        try:
+            group = self._call_queue[-1]
+        except IndexError:
+            pass
+
+        return group
+
+    def _CheckAndCreateNewGroup(self, group_name, group_class):
+        """Checks if the last method (a possible group) is an instance of our
+        group_class. Adds the current method to this group or creates a
+        new one.
+
+        Args:
+
+            group_name: the name of the group.
+            group_class: the class used to create instance of this new group
+        """
+        group = self.GetPossibleGroup()
+
+        # If this is a group, and it is the correct group, add the method.
+        if isinstance(group, group_class) and group.group_name() == group_name:
+            group.AddMethod(self)
+            return self
+
+        # Create a new group and add the method.
+        new_group = group_class(group_name)
+        new_group.AddMethod(self)
+        self._call_queue.append(new_group)
+        return self
+
+    def InAnyOrder(self, group_name="default"):
+        """Move this method into a group of unordered calls.
+
+        A group of unordered calls must be defined together, and must be
+        executed in full before the next expected method can be called.
+        There can be multiple groups that are expected serially, if they are
+        given different group names. The same group name can be reused if there
+        is a standard method call, or a group with a different name, spliced
+        between usages.
+
+        Args:
+            group_name: the name of the unordered group.
+
+        Returns:
+            self
+        """
+        return self._CheckAndCreateNewGroup(group_name, UnorderedGroup)
+
+    def MultipleTimes(self, group_name="default"):
+        """Move method into group of calls which may be called multiple times.
+
+        A group of repeating calls must be defined together, and must be
+        executed in full before the next expected method can be called.
+
+        Args:
+            group_name: the name of the unordered group.
+
+        Returns:
+            self
+        """
+        return self._CheckAndCreateNewGroup(group_name, MultipleTimesGroup)
+
+    def AndReturn(self, return_value):
+        """Set the value to return when this method is called.
+
+        Args:
+            # return_value can be anything.
+        """
+
+        self._return_value = return_value
+        return return_value
+
+    def AndRaise(self, exception):
+        """Set the exception to raise when this method is called.
+
+        Args:
+            # exception: the exception to raise when this method is called.
+            exception: Exception
+        """
+
+        self._exception = exception
+
+    def WithSideEffects(self, side_effects):
+        """Set the side effects that are simulated when this method is called.
+
+        Args:
+            side_effects: A callable which modifies the parameters or other
+                          relevant state which a given test case depends on.
+
+        Returns:
+            Self for chaining with AndReturn and AndRaise.
+        """
+        self._side_effects = side_effects
+        return self
+
+
+class Comparator:
+    """Base class for all Mox comparators.
+
+    A Comparator can be used as a parameter to a mocked method when the exact
+    value is not known.    For example, the code you are testing might build up
+    a long SQL string that is passed to your mock DAO. You're only interested
+    that the IN clause contains the proper primary keys, so you can set your
+    mock up as follows:
+
+    mock_dao.RunQuery(StrContains('IN (1, 2, 4, 5)')).AndReturn(mock_result)
+
+    Now whatever query is passed in must contain the string 'IN (1, 2, 4, 5)'.
+
+    A Comparator may replace one or more parameters, for example:
+    # return at most 10 rows
+    mock_dao.RunQuery(StrContains('SELECT'), 10)
+
+    or
+
+    # Return some non-deterministic number of rows
+    mock_dao.RunQuery(StrContains('SELECT'), IsA(int))
+    """
+
+    def equals(self, rhs):
+        """Special equals method that all comparators must implement.
+
+        Args:
+            rhs: any python object
+        """
+
+        raise NotImplementedError('method must be implemented by a subclass.')
+
+    def __eq__(self, rhs):
+        return self.equals(rhs)
+
+    def __ne__(self, rhs):
+        return not self.equals(rhs)
+
+
+class Is(Comparator):
+    """Comparison class used to check identity, instead of equality."""
+
+    def __init__(self, obj):
+        self._obj = obj
+
+    def equals(self, rhs):
+        return rhs is self._obj
+
+    def __repr__(self):
+        return "<is %r (%s)>" % (self._obj, id(self._obj))
+
+
+class IsA(Comparator):
+    """This class wraps a basic Python type or class.    It is used to verify
+    that a parameter is of the given type or class.
+
+    Example:
+    mock_dao.Connect(IsA(DbConnectInfo))
+    """
+
+    def __init__(self, class_name):
+        """Initialize IsA
+
+        Args:
+            class_name: basic python type or a class
+        """
+
+        self._class_name = class_name
+
+    def equals(self, rhs):
+        """Check to see if the RHS is an instance of class_name.
+
+        Args:
+            # rhs: the right hand side of the test
+            rhs: object
+
+        Returns:
+            bool
+        """
+
+        try:
+            return isinstance(rhs, self._class_name)
+        except TypeError:
+            # Check raw types if there was a type error.    This is helpful for
+            # things like cStringIO.StringIO.
+            return type(rhs) == type(self._class_name)
+
+    def _IsSubClass(self, clazz):
+        """Check to see if the IsA comparators class is a subclass of clazz.
+
+        Args:
+            # clazz: a class object
+
+        Returns:
+            bool
+        """
+
+        try:
+            return issubclass(self._class_name, clazz)
+        except TypeError:
+            # Check raw types if there was a type error.    This is helpful for
+            # things like cStringIO.StringIO.
+            return type(clazz) == type(self._class_name)
+
+    def __repr__(self):
+        return 'mox.IsA(%s) ' % str(self._class_name)
+
+
+class IsAlmost(Comparator):
+    """Comparison class used to check whether a parameter is nearly equal
+    to a given value.    Generally useful for floating point numbers.
+
+    Example mock_dao.SetTimeout((IsAlmost(3.9)))
+    """
+
+    def __init__(self, float_value, places=7):
+        """Initialize IsAlmost.
+
+        Args:
+            float_value: The value for making the comparison.
+            places: The number of decimal places to round to.
+        """
+
+        self._float_value = float_value
+        self._places = places
+
+    def equals(self, rhs):
+        """Check to see if RHS is almost equal to float_value
+
+        Args:
+            rhs: the value to compare to float_value
+
+        Returns:
+            bool
+        """
+
+        try:
+            return round(rhs - self._float_value, self._places) == 0
+        except Exception:
+            # Probably because either float_value or rhs is not a number.
+            return False
+
+    def __repr__(self):
+        return str(self._float_value)
+
+
+class StrContains(Comparator):
+    """Comparison class used to check whether a substring exists in a
+    string parameter.    This can be useful in mocking a database with SQL
+    passed in as a string parameter, for example.
+
+    Example:
+    mock_dao.RunQuery(StrContains('IN (1, 2, 4, 5)')).AndReturn(mock_result)
+    """
+
+    def __init__(self, search_string):
+        """Initialize.
+
+        Args:
+            # search_string: the string you are searching for
+            search_string: str
+        """
+
+        self._search_string = search_string
+
+    def equals(self, rhs):
+        """Check to see if the search_string is contained in the rhs string.
+
+        Args:
+            # rhs: the right hand side of the test
+            rhs: object
+
+        Returns:
+            bool
+        """
+
+        try:
+            return rhs.find(self._search_string) > -1
+        except Exception:
+            return False
+
+    def __repr__(self):
+        return '<str containing \'%s\'>' % self._search_string
+
+
+class Regex(Comparator):
+    """Checks if a string matches a regular expression.
+
+    This uses a given regular expression to determine equality.
+    """
+
+    def __init__(self, pattern, flags=0):
+        """Initialize.
+
+        Args:
+            # pattern is the regular expression to search for
+            pattern: str
+            # flags passed to re.compile function as the second argument
+            flags: int
+        """
+        self.flags = flags
+        self.regex = re.compile(pattern, flags=flags)
+
+    def equals(self, rhs):
+        """Check to see if rhs matches regular expression pattern.
+
+        Returns:
+            bool
+        """
+
+        try:
+            return self.regex.search(rhs) is not None
+        except Exception:
+            return False
+
+    def __repr__(self):
+        s = '<regular expression \'%s\'' % self.regex.pattern
+        if self.flags:
+            s += ', flags=%d' % self.flags
+        s += '>'
+        return s
+
+
+class In(Comparator):
+    """Checks whether an item (or key) is in a list (or dict) parameter.
+
+    Example:
+    mock_dao.GetUsersInfo(In('expectedUserName')).AndReturn(mock_result)
+    """
+
+    def __init__(self, key):
+        """Initialize.
+
+        Args:
+            # key is any thing that could be in a list or a key in a dict
+        """
+
+        self._key = key
+
+    def equals(self, rhs):
+        """Check to see whether key is in rhs.
+
+        Args:
+            rhs: dict
+
+        Returns:
+            bool
+        """
+
+        try:
+            return self._key in rhs
+        except Exception:
+            return False
+
+    def __repr__(self):
+        return '<sequence or map containing \'%s\'>' % str(self._key)
+
+
+class Not(Comparator):
+    """Checks whether a predicates is False.
+
+    Example:
+        mock_dao.UpdateUsers(Not(ContainsKeyValue('stevepm',
+                                                  stevepm_user_info)))
+    """
+
+    def __init__(self, predicate):
+        """Initialize.
+
+        Args:
+            # predicate: a Comparator instance.
+        """
+
+        assert isinstance(predicate, Comparator), ("predicate %r must be a"
+                                                   " Comparator." % predicate)
+        self._predicate = predicate
+
+    def equals(self, rhs):
+        """Check to see whether the predicate is False.
+
+        Args:
+            rhs: A value that will be given in argument of the predicate.
+
+        Returns:
+            bool
+        """
+
+        try:
+            return not self._predicate.equals(rhs)
+        except Exception:
+            return False
+
+    def __repr__(self):
+        return '<not \'%s\'>' % self._predicate
+
+
+class ContainsKeyValue(Comparator):
+    """Checks whether a key/value pair is in a dict parameter.
+
+    Example:
+    mock_dao.UpdateUsers(ContainsKeyValue('stevepm', stevepm_user_info))
+    """
+
+    def __init__(self, key, value):
+        """Initialize.
+
+        Args:
+            # key: a key in a dict
+            # value: the corresponding value
+        """
+
+        self._key = key
+        self._value = value
+
+    def equals(self, rhs):
+        """Check whether the given key/value pair is in the rhs dict.
+
+        Returns:
+            bool
+        """
+
+        try:
+            return rhs[self._key] == self._value
+        except Exception:
+            return False
+
+    def __repr__(self):
+        return '<map containing the entry \'%s: %s\'>' % (str(self._key),
+                                                          str(self._value))
+
+
+class ContainsAttributeValue(Comparator):
+    """Checks whether passed parameter contains attributes with a given value.
+
+    Example:
+    mock_dao.UpdateSomething(ContainsAttribute('stevepm', stevepm_user_info))
+    """
+
+    def __init__(self, key, value):
+        """Initialize.
+
+        Args:
+            # key: an attribute name of an object
+            # value: the corresponding value
+        """
+
+        self._key = key
+        self._value = value
+
+    def equals(self, rhs):
+        """Check if the given attribute has a matching value in the rhs object.
+
+        Returns:
+            bool
+        """
+
+        try:
+            return getattr(rhs, self._key) == self._value
+        except Exception:
+            return False
+
+
+class SameElementsAs(Comparator):
+    """Checks whether sequences contain the same elements (ignoring order).
+
+    Example:
+    mock_dao.ProcessUsers(SameElementsAs('stevepm', 'salomaki'))
+    """
+
+    def __init__(self, expected_seq):
+        """Initialize.
+
+        Args:
+            expected_seq: a sequence
+        """
+        # Store in case expected_seq is an iterator.
+        self._expected_list = list(expected_seq)
+
+    def equals(self, actual_seq):
+        """Check to see whether actual_seq has same elements as expected_seq.
+
+        Args:
+            actual_seq: sequence
+
+        Returns:
+            bool
+        """
+        try:
+            # Store in case actual_seq is an iterator. We potentially iterate
+            # twice: once to make the dict, once in the list fallback.
+            actual_list = list(actual_seq)
+        except TypeError:
+            # actual_seq cannot be read as a sequence.
+            #
+            # This happens because Mox uses __eq__ both to check object
+            # equality (in MethodSignatureChecker) and to invoke Comparators.
+            return False
+
+        try:
+            return set(self._expected_list) == set(actual_list)
+        except TypeError:
+            # Fall back to slower list-compare if any of the objects
+            # are unhashable.
+            if len(self._expected_list) != len(actual_list):
+                return False
+            for el in actual_list:
+                if el not in self._expected_list:
+                    return False
+        return True
+
+    def __repr__(self):
+        return '<sequence with same elements as \'%s\'>' % self._expected_list
+
+
+class And(Comparator):
+    """Evaluates one or more Comparators on RHS, returns an AND of the results.
+    """
+
+    def __init__(self, *args):
+        """Initialize.
+
+        Args:
+            *args: One or more Comparator
+        """
+
+        self._comparators = args
+
+    def equals(self, rhs):
+        """Checks whether all Comparators are equal to rhs.
+
+        Args:
+            # rhs: can be anything
+
+        Returns:
+            bool
+        """
+
+        for comparator in self._comparators:
+            if not comparator.equals(rhs):
+                return False
+
+        return True
+
+    def __repr__(self):
+        return '<AND %s>' % str(self._comparators)
+
+
+class Or(Comparator):
+    """Evaluates one or more Comparators on RHS; returns OR of the results."""
+
+    def __init__(self, *args):
+        """Initialize.
+
+        Args:
+            *args: One or more Mox comparators
+        """
+
+        self._comparators = args
+
+    def equals(self, rhs):
+        """Checks whether any Comparator is equal to rhs.
+
+        Args:
+            # rhs: can be anything
+
+        Returns:
+            bool
+        """
+
+        for comparator in self._comparators:
+            if comparator.equals(rhs):
+                return True
+
+        return False
+
+    def __repr__(self):
+        return '<OR %s>' % str(self._comparators)
+
+
+class Func(Comparator):
+    """Call a function that should verify the parameter passed in is correct.
+
+    You may need the ability to perform more advanced operations on the
+    parameter in order to validate it. You can use this to have a callable
+    validate any parameter. The callable should return either True or False.
+
+
+    Example:
+
+    def myParamValidator(param):
+        # Advanced logic here
+        return True
+
+    mock_dao.DoSomething(Func(myParamValidator), true)
+    """
+
+    def __init__(self, func):
+        """Initialize.
+
+        Args:
+            func: callable that takes one parameter and returns a bool
+        """
+
+        self._func = func
+
+    def equals(self, rhs):
+        """Test whether rhs passes the function test.
+
+        rhs is passed into func.
+
+        Args:
+            rhs: any python object
+
+        Returns:
+            the result of func(rhs)
+        """
+
+        return self._func(rhs)
+
+    def __repr__(self):
+        return str(self._func)
+
+
+class IgnoreArg(Comparator):
+    """Ignore an argument.
+
+    This can be used when we don't care about an argument of a method call.
+
+    Example:
+    # Check if CastMagic is called with 3 as first arg and
+    # 'disappear' as third.
+    mymock.CastMagic(3, IgnoreArg(), 'disappear')
+    """
+
+    def equals(self, unused_rhs):
+        """Ignores arguments and returns True.
+
+        Args:
+            unused_rhs: any python object
+
+        Returns:
+            always returns True
+        """
+
+        return True
+
+    def __repr__(self):
+        return '<IgnoreArg>'
+
+
+class Value(Comparator):
+    """Compares argument against a remembered value.
+
+    To be used in conjunction with Remember comparator.    See Remember()
+    for example.
+    """
+
+    def __init__(self):
+        self._value = None
+        self._has_value = False
+
+    def store_value(self, rhs):
+        self._value = rhs
+        self._has_value = True
+
+    def equals(self, rhs):
+        if not self._has_value:
+            return False
+        else:
+            return rhs == self._value
+
+    def __repr__(self):
+        if self._has_value:
+            return "<Value %r>" % self._value
+        else:
+            return "<Value>"
+
+
+class Remember(Comparator):
+    """Remembers the argument to a value store.
+
+    To be used in conjunction with Value comparator.
+
+    Example:
+    # Remember the argument for one method call.
+    users_list = Value()
+    mock_dao.ProcessUsers(Remember(users_list))
+
+    # Check argument against remembered value.
+    mock_dao.ReportUsers(users_list)
+    """
+
+    def __init__(self, value_store):
+        if not isinstance(value_store, Value):
+            raise TypeError(
+                "value_store is not an instance of the Value class")
+        self._value_store = value_store
+
+    def equals(self, rhs):
+        self._value_store.store_value(rhs)
+        return True
+
+    def __repr__(self):
+        return "<Remember %d>" % id(self._value_store)
+
+
+class MethodGroup(object):
+    """Base class containing common behaviour for MethodGroups."""
+
+    def __init__(self, group_name):
+        self._group_name = group_name
+
+    def group_name(self):
+        return self._group_name
+
+    def __str__(self):
+        return '<%s "%s">' % (self.__class__.__name__, self._group_name)
+
+    def AddMethod(self, mock_method):
+        raise NotImplementedError
+
+    def MethodCalled(self, mock_method):
+        raise NotImplementedError
+
+    def IsSatisfied(self):
+        raise NotImplementedError
+
+
+class UnorderedGroup(MethodGroup):
+    """UnorderedGroup holds a set of method calls that may occur in any order.
+
+    This construct is helpful for non-deterministic events, such as iterating
+    over the keys of a dict.
+    """
+
+    def __init__(self, group_name):
+        super(UnorderedGroup, self).__init__(group_name)
+        self._methods = []
+
+    def __str__(self):
+        return '%s "%s" pending calls:\n%s' % (
+            self.__class__.__name__,
+            self._group_name,
+            "\n".join(str(method) for method in self._methods))
+
+    def AddMethod(self, mock_method):
+        """Add a method to this group.
+
+        Args:
+            mock_method: A mock method to be added to this group.
+        """
+
+        self._methods.append(mock_method)
+
+    def MethodCalled(self, mock_method):
+        """Remove a method call from the group.
+
+        If the method is not in the set, an UnexpectedMethodCallError will be
+        raised.
+
+        Args:
+            mock_method: a mock method that should be equal to a method in the
+                         group.
+
+        Returns:
+            The mock method from the group
+
+        Raises:
+            UnexpectedMethodCallError if the mock_method was not in the group.
+        """
+
+        # Check to see if this method exists, and if so, remove it from the set
+        # and return it.
+        for method in self._methods:
+            if method == mock_method:
+                # Remove the called mock_method instead of the method in the
+                # group. The called method will match any comparators when
+                # equality is checked during removal. The method in the group
+                # could pass a comparator to another comparator during the
+                # equality check.
+                self._methods.remove(mock_method)
+
+                # If group is not empty, put it back at the head of the queue.
+                if not self.IsSatisfied():
+                    mock_method._call_queue.appendleft(self)
+
+                return self, method
+
+        raise UnexpectedMethodCallError(mock_method, self)
+
+    def IsSatisfied(self):
+        """Return True if there are not any methods in this group."""
+
+        return len(self._methods) == 0
+
+
+class MultipleTimesGroup(MethodGroup):
+    """MultipleTimesGroup holds methods that may be called any number of times.
+
+    Note: Each method must be called at least once.
+
+    This is helpful, if you don't know or care how many times a method is
+    called.
+    """
+
+    def __init__(self, group_name):
+        super(MultipleTimesGroup, self).__init__(group_name)
+        self._methods = set()
+        self._methods_left = set()
+
+    def AddMethod(self, mock_method):
+        """Add a method to this group.
+
+        Args:
+            mock_method: A mock method to be added to this group.
+        """
+
+        self._methods.add(mock_method)
+        self._methods_left.add(mock_method)
+
+    def MethodCalled(self, mock_method):
+        """Remove a method call from the group.
+
+        If the method is not in the set, an UnexpectedMethodCallError will be
+        raised.
+
+        Args:
+            mock_method: a mock method that should be equal to a method in the
+                         group.
+
+        Returns:
+            The mock method from the group
+
+        Raises:
+            UnexpectedMethodCallError if the mock_method was not in the group.
+        """
+
+        # Check to see if this method exists, and if so add it to the set of
+        # called methods.
+        for method in self._methods:
+            if method == mock_method:
+                self._methods_left.discard(method)
+                # Always put this group back on top of the queue,
+                # because we don't know when we are done.
+                mock_method._call_queue.appendleft(self)
+                return self, method
+
+        if self.IsSatisfied():
+            next_method = mock_method._PopNextMethod()
+            return next_method, None
+        else:
+            raise UnexpectedMethodCallError(mock_method, self)
+
+    def IsSatisfied(self):
+        """Return True if all methods in group are called at least once."""
+        return len(self._methods_left) == 0
+
+
+class MoxMetaTestBase(type):
+    """Metaclass to add mox cleanup and verification to every test.
+
+    As the mox unit testing class is being constructed (MoxTestBase or a
+    subclass), this metaclass will modify all test functions to call the
+    CleanUpMox method of the test class after they finish. This means that
+    unstubbing and verifying will happen for every test with no additional
+    code, and any failures will result in test failures as opposed to errors.
+    """
+
+    def __init__(cls, name, bases, d):
+        type.__init__(cls, name, bases, d)
+
+        # also get all the attributes from the base classes to account
+        # for a case when test class is not the immediate child of MoxTestBase
+        for base in bases:
+            for attr_name in dir(base):
+                if attr_name not in d:
+                    d[attr_name] = getattr(base, attr_name)
+
+        for func_name, func in d.items():
+            if func_name.startswith('test') and callable(func):
+
+                setattr(cls, func_name, MoxMetaTestBase.CleanUpTest(cls, func))
+
+    @staticmethod
+    def CleanUpTest(cls, func):
+        """Adds Mox cleanup code to any MoxTestBase method.
+
+        Always unsets stubs after a test. Will verify all mocks for tests that
+        otherwise pass.
+
+        Args:
+            cls: MoxTestBase or subclass; the class whose method we are
+                                          altering.
+            func: method; the method of the MoxTestBase test class we wish to
+                          alter.
+
+        Returns:
+            The modified method.
+        """
+        def new_method(self, *args, **kwargs):
+            mox_obj = getattr(self, 'mox', None)
+            stubout_obj = getattr(self, 'stubs', None)
+            cleanup_mox = False
+            cleanup_stubout = False
+            if mox_obj and isinstance(mox_obj, Mox):
+                cleanup_mox = True
+            if stubout_obj and isinstance(stubout_obj,
+                                          stubout.StubOutForTesting):
+                cleanup_stubout = True
+            try:
+                func(self, *args, **kwargs)
+            finally:
+                if cleanup_mox:
+                    mox_obj.UnsetStubs()
+                if cleanup_stubout:
+                    stubout_obj.UnsetAll()
+                    stubout_obj.SmartUnsetAll()
+            if cleanup_mox:
+                mox_obj.VerifyAll()
+        new_method.__name__ = func.__name__
+        new_method.__doc__ = func.__doc__
+        new_method.__module__ = func.__module__
+        return new_method
+
+
+_MoxTestBase = MoxMetaTestBase('_MoxTestBase', (unittest.TestCase, ), {})
+
+
+class MoxTestBase(_MoxTestBase):
+    """Convenience test class to make stubbing easier.
+
+    Sets up a "mox" attribute which is an instance of Mox (any mox tests will
+    want this), and a "stubs" attribute that is an instance of
+    StubOutForTesting (needed at times). Also automatically unsets any stubs
+    and verifies that all mock methods have been called at the end of each
+    test, eliminating boilerplate code.
+    """
+
+    def setUp(self):
+        super(MoxTestBase, self).setUp()
+        self.mox = Mox()
+        self.stubs = stubout.StubOutForTesting()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/mox3/stubout.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/mox3/stubout.py
new file mode 100644
index 0000000..a02ed40
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/mox3/stubout.py
@@ -0,0 +1,152 @@
+# Copyright 2008 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# This is a fork of the pymox library intended to work with Python 3.
+# The file was modified by quermit@gmail.com and dawid.fatyga@gmail.com
+
+import inspect
+
+
+class StubOutForTesting(object):
+    """Sample Usage:
+
+       You want os.path.exists() to always return true during testing.
+
+       stubs = StubOutForTesting()
+       stubs.Set(os.path, 'exists', lambda x: 1)
+           ...
+       stubs.UnsetAll()
+
+       The above changes os.path.exists into a lambda that returns 1.    Once
+       the ... part of the code finishes, the UnsetAll() looks up the old value
+       of os.path.exists and restores it.
+
+    """
+    def __init__(self):
+        self.cache = []
+        self.stubs = []
+
+    def __del__(self):
+        self.SmartUnsetAll()
+        self.UnsetAll()
+
+    def SmartSet(self, obj, attr_name, new_attr):
+        """Replace obj.attr_name with new_attr.
+
+        This method is smart and works at the module, class, and instance level
+        while preserving proper inheritance. It will not stub out C types
+        however unless that has been explicitly allowed by the type.
+
+        This method supports the case where attr_name is a staticmethod or a
+        classmethod of obj.
+
+        Notes:
+          - If obj is an instance, then it is its class that will actually be
+            stubbed. Note that the method Set() does not do that: if obj is
+            an instance, it (and not its class) will be stubbed.
+          - The stubbing is using the builtin getattr and setattr. So, the
+            __get__ and __set__ will be called when stubbing (TODO: A better
+            idea would probably be to manipulate obj.__dict__ instead of
+            getattr() and setattr()).
+
+        Raises AttributeError if the attribute cannot be found.
+        """
+        if (inspect.ismodule(obj) or
+                (not inspect.isclass(obj) and attr_name in obj.__dict__)):
+            orig_obj = obj
+            orig_attr = getattr(obj, attr_name)
+
+        else:
+            if not inspect.isclass(obj):
+                mro = list(inspect.getmro(obj.__class__))
+            else:
+                mro = list(inspect.getmro(obj))
+
+            mro.reverse()
+
+            orig_attr = None
+
+            for cls in mro:
+                try:
+                    orig_obj = cls
+                    orig_attr = getattr(obj, attr_name)
+                except AttributeError:
+                    continue
+
+        if orig_attr is None:
+            raise AttributeError("Attribute not found.")
+
+        # Calling getattr() on a staticmethod transforms it to a 'normal'
+        # function. We need to ensure that we put it back as a staticmethod.
+        old_attribute = obj.__dict__.get(attr_name)
+        if (old_attribute is not None
+                and isinstance(old_attribute, staticmethod)):
+            orig_attr = staticmethod(orig_attr)
+
+        self.stubs.append((orig_obj, attr_name, orig_attr))
+        setattr(orig_obj, attr_name, new_attr)
+
+    def SmartUnsetAll(self):
+        """Reverses all the SmartSet() calls.
+
+        Restores things to their original definition. Its okay to call
+        SmartUnsetAll() repeatedly, as later calls have no effect if no
+        SmartSet() calls have been made.
+        """
+        self.stubs.reverse()
+
+        for args in self.stubs:
+            setattr(*args)
+
+        self.stubs = []
+
+    def Set(self, parent, child_name, new_child):
+        """Replace child_name's old definition with new_child.
+
+        Replace definiion in the context of the given parent. The parent could
+        be a module when the child is a function at module scope. Or the parent
+        could be a class when a class' method is being replaced. The named
+        child is set to new_child, while the prior definition is saved away
+        for later, when UnsetAll() is called.
+
+        This method supports the case where child_name is a staticmethod or a
+        classmethod of parent.
+        """
+        old_child = getattr(parent, child_name)
+
+        old_attribute = parent.__dict__.get(child_name)
+        if old_attribute is not None:
+            if isinstance(old_attribute, staticmethod):
+                old_child = staticmethod(old_child)
+            elif isinstance(old_attribute, classmethod):
+                old_child = classmethod(old_child.__func__)
+
+        self.cache.append((parent, old_child, child_name))
+        setattr(parent, child_name, new_child)
+
+    def UnsetAll(self):
+        """Reverses all the Set() calls.
+
+        Restores things to their original definition. Its okay to call
+        UnsetAll() repeatedly, as later calls have no effect if no Set()
+        calls have been made.
+        """
+        # Undo calls to Set() in reverse order, in case Set() was called on the
+        # same arguments repeatedly (want the original call to be last one
+        # undone)
+        self.cache.reverse()
+
+        for (parent, old_child, child_name) in self.cache:
+            setattr(parent, child_name, old_child)
+        self.cache = []
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/mox3/tests/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/mox3/tests/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/mox3/tests/__init__.py
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/mox3/tests/mox_helper.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/mox3/tests/mox_helper.py
new file mode 100644
index 0000000..67843a9
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/mox3/tests/mox_helper.py
@@ -0,0 +1,145 @@
+# Copyright 2008 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#
+# This is a fork of the pymox library intended to work with Python 3.
+# The file was modified by quermit@gmail.com and dawid.fatyga@gmail.com
+
+"""A very basic test class derived from mox.MoxTestBase, used by test_mox.py.
+
+The class defined in this module is used to test the features of
+MoxTestBase and is not intended to be a standalone test.  It needs to
+be in a separate module, because otherwise the tests in this class
+(which should not all pass) would be executed as part of the
+test_mox.py test suite.
+
+See test_mox.MoxTestBaseTest for how this class is actually used.
+"""
+
+import os
+
+from mox3 import mox
+
+
+class ExampleMoxTestMixin(object):
+    """Mix-in class for mox test case class.
+
+    It stubs out the same function as one of the test methods in
+    the example test case.    Both tests must pass as meta class wraps
+    test methods in all base classes.
+    """
+
+    def testStat(self):
+        self.mox.StubOutWithMock(os, 'stat')
+        os.stat(self.DIR_PATH)
+        self.mox.ReplayAll()
+        os.stat(self.DIR_PATH)
+
+
+class ExampleMoxTest(mox.MoxTestBase, ExampleMoxTestMixin):
+
+    DIR_PATH = '/path/to/some/directory'
+
+    def testSuccess(self):
+        self.mox.StubOutWithMock(os, 'listdir')
+        os.listdir(self.DIR_PATH)
+        self.mox.ReplayAll()
+        os.listdir(self.DIR_PATH)
+
+    def testExpectedNotCalled(self):
+        self.mox.StubOutWithMock(os, 'listdir')
+        os.listdir(self.DIR_PATH)
+        self.mox.ReplayAll()
+
+    def testUnexpectedCall(self):
+        self.mox.StubOutWithMock(os, 'listdir')
+        os.listdir(self.DIR_PATH)
+        self.mox.ReplayAll()
+        os.listdir('/path/to/some/other/directory')
+        os.listdir(self.DIR_PATH)
+
+    def testFailure(self):
+        self.assertTrue(False)
+
+    def testStatOther(self):
+        self.mox.StubOutWithMock(os, 'stat')
+        os.stat(self.DIR_PATH)
+        self.mox.ReplayAll()
+        os.stat(self.DIR_PATH)
+
+    def testHasStubs(self):
+        listdir_list = []
+
+        def MockListdir(directory):
+            listdir_list.append(directory)
+
+        self.stubs.Set(os, 'listdir', MockListdir)
+        os.listdir(self.DIR_PATH)
+        self.assertEqual([self.DIR_PATH], listdir_list)
+
+
+class TestClassFromAnotherModule(object):
+
+    def __init__(self):
+        return None
+
+    def Value(self):
+        return 'Not mock'
+
+
+class ChildClassFromAnotherModule(TestClassFromAnotherModule):
+    """A child class of TestClassFromAnotherModule.
+
+    Used to test stubbing out unbound methods, where child classes
+    are eventually bound.
+    """
+
+    def __init__(self):
+        TestClassFromAnotherModule.__init__(self)
+
+
+class CallableClass(object):
+
+    def __init__(self, one, two, nine=None):
+        pass
+
+    def __call__(self, one):
+        return 'Not mock'
+
+    def Value(self):
+        return 'Not mock'
+
+
+def MyTestFunction(one, two, nine=None):
+    pass
+
+
+class ExampleClass(object):
+    def __init__(self, foo='bar'):
+        pass
+
+    def TestMethod(self, one, two, nine=None):
+        pass
+
+    def NamedParams(self, ignore, foo='bar', baz='qux'):
+        pass
+
+    def SpecialArgs(self, *args, **kwargs):
+        pass
+
+
+# This class is used to test stubbing out __init__ of a parent class.
+class ChildExampleClass(ExampleClass):
+    def __init__(self):
+        ExampleClass.__init__(self)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/mox3/tests/stubout_helper.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/mox3/tests/stubout_helper.py
new file mode 100644
index 0000000..7a6b266
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/mox3/tests/stubout_helper.py
@@ -0,0 +1,20 @@
+# Copyright 2008 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# This is a fork of the pymox library intended to work with Python 3.
+# The file was modified by quermit@gmail.com and dawid.fatyga@gmail.com
+
+
+def SampleFunction():
+    raise Exception('I should never be called!')
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/mox3/tests/test_mox.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/mox3/tests/test_mox.py
new file mode 100644
index 0000000..48d1ecf
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/mox3/tests/test_mox.py
@@ -0,0 +1,2408 @@
+# Unit tests for Mox.
+#
+# Copyright 2008 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# This is a fork of the pymox library intended to work with Python 3.
+# The file was modified by quermit@gmail.com and dawid.fatyga@gmail.com
+
+import io
+import re
+import sys
+
+from mox3 import mox
+from mox3.tests import mox_helper
+
+import six
+import testtools
+
+
+OS_LISTDIR = mox_helper.os.listdir
+
+
+class ExpectedMethodCallsErrorTest(testtools.TestCase):
+    """Test creation and string conversion of ExpectedMethodCallsError."""
+
+    def testAtLeastOneMethod(self):
+        self.assertRaises(ValueError, mox.ExpectedMethodCallsError, [])
+
+    def testOneError(self):
+        method = mox.MockMethod("testMethod", [], False)
+        method(1, 2).AndReturn('output')
+        e = mox.ExpectedMethodCallsError([method])
+        self.assertEqual(
+            "Verify: Expected methods never called:\n"
+            "  0.  testMethod(1, 2) -> 'output'",
+            str(e))
+
+    def testManyErrors(self):
+        method1 = mox.MockMethod("testMethod", [], False)
+        method1(1, 2).AndReturn('output')
+        method2 = mox.MockMethod("testMethod", [], False)
+        method2(a=1, b=2, c="only named")
+        method3 = mox.MockMethod("testMethod2", [], False)
+        method3().AndReturn(44)
+        method4 = mox.MockMethod("testMethod", [], False)
+        method4(1, 2).AndReturn('output')
+        e = mox.ExpectedMethodCallsError([method1, method2, method3, method4])
+        self.assertEqual(
+            "Verify: Expected methods never called:\n"
+            "  0.  testMethod(1, 2) -> 'output'\n"
+            "  1.  testMethod(a=1, b=2, c='only named') -> None\n"
+            "  2.  testMethod2() -> 44\n"
+            "  3.  testMethod(1, 2) -> 'output'",
+            str(e))
+
+
+class OrTest(testtools.TestCase):
+    """Test Or correctly chains Comparators."""
+
+    def testValidOr(self):
+        """Or should be True if either Comparator returns True."""
+        self.assertTrue(mox.Or(mox.IsA(dict), mox.IsA(str)) == {})
+        self.assertTrue(mox.Or(mox.IsA(dict), mox.IsA(str)) == 'test')
+        self.assertTrue(mox.Or(mox.IsA(str), mox.IsA(str)) == 'test')
+
+    def testInvalidOr(self):
+        """Or should be False if both Comparators return False."""
+        self.assertFalse(mox.Or(mox.IsA(dict), mox.IsA(str)) == 0)
+
+
+class AndTest(testtools.TestCase):
+    """Test And correctly chains Comparators."""
+
+    def testValidAnd(self):
+        """And should be True if both Comparators return True."""
+        self.assertTrue(mox.And(mox.IsA(str), mox.IsA(str)) == '1')
+
+    def testClauseOneFails(self):
+        """And should be False if the first Comparator returns False."""
+
+        self.assertFalse(mox.And(mox.IsA(dict), mox.IsA(str)) == '1')
+
+    def testAdvancedUsage(self):
+        """And should work with other Comparators.
+
+        Note: this test is reliant on In and ContainsKeyValue.
+        """
+        test_dict = {"mock": "obj", "testing": "isCOOL"}
+        self.assertTrue(mox.And(mox.In("testing"),
+                        mox.ContainsKeyValue("mock", "obj")) == test_dict)
+
+    def testAdvancedUsageFails(self):
+        """Note: this test is reliant on In and ContainsKeyValue."""
+        test_dict = {"mock": "obj", "testing": "isCOOL"}
+        self.assertFalse(mox.And(mox.In("NOTFOUND"),
+                         mox.ContainsKeyValue("mock", "obj")) == test_dict)
+
+
+class FuncTest(testtools.TestCase):
+    """Test Func correctly evaluates based upon true-false return."""
+
+    def testFuncTrueFalseEvaluation(self):
+        """Should return True if the validating function returns True."""
+        equals_one = lambda x: x == 1
+        always_none = lambda x: None
+
+        self.assertTrue(mox.Func(equals_one) == 1)
+        self.assertFalse(mox.Func(equals_one) == 0)
+
+        self.assertFalse(mox.Func(always_none) == 1)
+        self.assertFalse(mox.Func(always_none) == 0)
+        self.assertFalse(mox.Func(always_none) is None)
+
+    def testFuncExceptionPropagation(self):
+        """Exceptions within the validating function should propagate."""
+        class TestException(Exception):
+            pass
+
+        def raiseExceptionOnNotOne(value):
+            if value != 1:
+                raise TestException
+            else:
+                return True
+
+        self.assertTrue(mox.Func(raiseExceptionOnNotOne) == 1)
+        self.assertRaises(
+            TestException, mox.Func(raiseExceptionOnNotOne).__eq__, 2)
+
+
+class SameElementsAsTest(testtools.TestCase):
+    """SameElementsAs correctly identifies sequences with same elements."""
+
+    def testSortedLists(self):
+        """Should return True if two lists are exactly equal."""
+        self.assertTrue(mox.SameElementsAs([1, 2.0, 'c']) == [1, 2.0, 'c'])
+
+    def testUnsortedLists(self):
+        """Should return True if lists are unequal but have same elements."""
+        self.assertTrue(mox.SameElementsAs([1, 2.0, 'c']) == [2.0, 'c', 1])
+
+    def testUnhashableLists(self):
+        """Should return True if lists have the same unhashable elements."""
+        self.assertTrue(mox.SameElementsAs([{'a': 1}, {2: 'b'}]) ==
+                        [{2: 'b'}, {'a': 1}])
+
+    def testEmptyLists(self):
+        """Should return True for two empty lists."""
+        self.assertTrue(mox.SameElementsAs([]) == [])
+
+    def testUnequalLists(self):
+        """Should return False if the lists are not equal."""
+        self.assertFalse(mox.SameElementsAs([1, 2.0, 'c']) == [2.0, 'c'])
+
+    def testUnequalUnhashableLists(self):
+        """Should return False if lists with unhashable items are unequal."""
+        self.assertFalse(mox.SameElementsAs(
+            [{'a': 1}, {2: 'b'}]) == [{2: 'b'}])
+
+    def testActualIsNotASequence(self):
+        """Should return False if the actual object is not a sequence."""
+        self.assertFalse(mox.SameElementsAs([1]) == object())
+
+    def testOneUnhashableObjectInActual(self):
+        """Store the entire iterator for a correct comparison.
+
+        In a previous version of SameElementsAs, iteration stopped when an
+        unhashable object was encountered and then was restarted, so the actual
+        list appeared smaller than it was.
+        """
+        self.assertFalse(mox.SameElementsAs([1, 2]) == iter([{}, 1, 2]))
+
+
+class ContainsKeyValueTest(testtools.TestCase):
+    """Test ContainsKeyValue correctly identifies key/value pairs in a dict.
+    """
+
+    def testValidPair(self):
+        """Should return True if the key value is in the dict."""
+        self.assertTrue(mox.ContainsKeyValue("key", 1) == {"key": 1})
+
+    def testInvalidValue(self):
+        """Should return False if the value is not correct."""
+        self.assertFalse(mox.ContainsKeyValue("key", 1) == {"key": 2})
+
+    def testInvalidKey(self):
+        """Should return False if they key is not in the dict."""
+        self.assertFalse(mox.ContainsKeyValue("qux", 1) == {"key": 2})
+
+
+class ContainsAttributeValueTest(testtools.TestCase):
+    """Test ContainsAttributeValue identifies properties in an object."""
+
+    def setUp(self):
+        """Create an object to test with."""
+
+        class TestObject(object):
+            key = 1
+
+        super(ContainsAttributeValueTest, self).setUp()
+        self.test_object = TestObject()
+
+    def testValidPair(self):
+        """Return True if the object has the key attribute that matches."""
+        self.assertTrue(mox.ContainsAttributeValue("key", 1)
+                        == self.test_object)
+
+    def testInvalidValue(self):
+        """Should return False if the value is not correct."""
+        self.assertFalse(mox.ContainsKeyValue("key", 2) == self.test_object)
+
+    def testInvalidKey(self):
+        """Should return False if they the object doesn't have the property."""
+        self.assertFalse(mox.ContainsKeyValue("qux", 1) == self.test_object)
+
+
+class InTest(testtools.TestCase):
+    """Test In correctly identifies a key in a list/dict."""
+
+    def testItemInList(self):
+        """Should return True if the item is in the list."""
+        self.assertTrue(mox.In(1) == [1, 2, 3])
+
+    def testKeyInDict(self):
+        """Should return True if the item is a key in a dict."""
+        self.assertTrue(mox.In("test") == {"test": "module"})
+
+    def testItemInTuple(self):
+        """Should return True if the item is in the list."""
+        self.assertTrue(mox.In(1) == (1, 2, 3))
+
+    def testTupleInTupleOfTuples(self):
+        self.assertTrue(mox.In((1, 2, 3)) == ((1, 2, 3), (1, 2)))
+
+    def testItemNotInList(self):
+        self.assertFalse(mox.In(1) == [2, 3])
+
+    def testTupleNotInTupleOfTuples(self):
+        self.assertFalse(mox.In((1, 2)) == ((1, 2, 3), (4, 5)))
+
+
+class NotTest(testtools.TestCase):
+    """Test Not correctly identifies False predicates."""
+
+    def testItemInList(self):
+        """Should return True if the item is NOT in the list."""
+        self.assertTrue(mox.Not(mox.In(42)) == [1, 2, 3])
+
+    def testKeyInDict(self):
+        """Should return True if the item is NOT a key in a dict."""
+        self.assertTrue(mox.Not(mox.In("foo")) == {"key": 42})
+
+    def testInvalidKeyWithNot(self):
+        """Should return False if they key is NOT in the dict."""
+        self.assertTrue(mox.Not(mox.ContainsKeyValue("qux", 1)) == {"key": 2})
+
+
+class StrContainsTest(testtools.TestCase):
+    """Test StrContains checks for substring occurrence of a parameter."""
+
+    def testValidSubstringAtStart(self):
+        """Should return True if substring is at the start of the string."""
+        self.assertTrue(mox.StrContains("hello") == "hello world")
+
+    def testValidSubstringInMiddle(self):
+        """Should return True if substring is in the middle of the string."""
+        self.assertTrue(mox.StrContains("lo wo") == "hello world")
+
+    def testValidSubstringAtEnd(self):
+        """Should return True if the substring is at the end of the string."""
+        self.assertTrue(mox.StrContains("ld") == "hello world")
+
+    def testInvaildSubstring(self):
+        """Should return False if the substring is not in the string."""
+        self.assertFalse(mox.StrContains("AAA") == "hello world")
+
+    def testMultipleMatches(self):
+        """Should return True if there are multiple occurances of substring."""
+        self.assertTrue(mox.StrContains("abc") == "ababcabcabcababc")
+
+
+class RegexTest(testtools.TestCase):
+    """Test Regex correctly matches regular expressions."""
+
+    def testIdentifyBadSyntaxDuringInit(self):
+        """The user should know immediately if a regex has bad syntax."""
+        self.assertRaises(re.error, mox.Regex, '(a|b')
+
+    def testPatternInMiddle(self):
+        """Return True if the pattern matches at the middle of the string.
+
+        This ensures that re.search is used (instead of re.find).
+        """
+        self.assertTrue(mox.Regex(r"a\s+b") == "x y z a b c")
+
+    def testNonMatchPattern(self):
+        """Should return False if the pattern does not match the string."""
+        self.assertFalse(mox.Regex(r"a\s+b") == "x y z")
+
+    def testFlagsPassedCorrectly(self):
+        """Should return True as we pass IGNORECASE flag."""
+        self.assertTrue(mox.Regex(r"A", re.IGNORECASE) == "a")
+
+    def testReprWithoutFlags(self):
+        """repr should return the regular expression pattern."""
+        self.assertTrue(
+            repr(mox.Regex(r"a\s+b")) == "<regular expression 'a\s+b'>")
+
+    def testReprWithFlags(self):
+        """repr should return the regular expression pattern and flags."""
+        self.assertTrue(repr(mox.Regex(r"a\s+b", flags=4)) ==
+                        "<regular expression 'a\s+b', flags=4>")
+
+
+class IsTest(testtools.TestCase):
+    """Verify Is correctly checks equality based upon identity, not value."""
+
+    class AlwaysComparesTrue(object):
+        def __eq__(self, other):
+            return True
+
+        def __cmp__(self, other):
+            return 0
+
+        def __ne__(self, other):
+            return False
+
+    def testEqualityValid(self):
+        o1 = self.AlwaysComparesTrue()
+        self.assertTrue(mox.Is(o1), o1)
+
+    def testEqualityInvalid(self):
+        o1 = self.AlwaysComparesTrue()
+        o2 = self.AlwaysComparesTrue()
+        self.assertTrue(o1 == o2)
+        # but...
+        self.assertFalse(mox.Is(o1) == o2)
+
+    def testInequalityValid(self):
+        o1 = self.AlwaysComparesTrue()
+        o2 = self.AlwaysComparesTrue()
+        self.assertTrue(mox.Is(o1) != o2)
+
+    def testInequalityInvalid(self):
+        o1 = self.AlwaysComparesTrue()
+        self.assertFalse(mox.Is(o1) != o1)
+
+    def testEqualityInListValid(self):
+        o1 = self.AlwaysComparesTrue()
+        o2 = self.AlwaysComparesTrue()
+        isa_list = [mox.Is(o1), mox.Is(o2)]
+        str_list = [o1, o2]
+        self.assertTrue(isa_list == str_list)
+
+    def testEquailtyInListInvalid(self):
+        o1 = self.AlwaysComparesTrue()
+        o2 = self.AlwaysComparesTrue()
+        isa_list = [mox.Is(o1), mox.Is(o2)]
+        mixed_list = [o2, o1]
+        self.assertFalse(isa_list == mixed_list)
+
+
+class IsATest(testtools.TestCase):
+    """Verify IsA correctly checks equality based upon class type not value."""
+
+    def testEqualityValid(self):
+        """Verify that == correctly identifies objects of the same type."""
+        self.assertTrue(mox.IsA(str) == 'test')
+
+    def testEqualityInvalid(self):
+        """Verify that == correctly identifies objects of different types."""
+        self.assertFalse(mox.IsA(str) == 10)
+
+    def testInequalityValid(self):
+        """Verify that != identifies objects of different type."""
+        self.assertTrue(mox.IsA(str) != 10)
+
+    def testInequalityInvalid(self):
+        """Verify that != correctly identifies objects of the same type."""
+        self.assertFalse(mox.IsA(str) != "test")
+
+    def testEqualityInListValid(self):
+        """Verify list contents are properly compared."""
+        isa_list = [mox.IsA(str), mox.IsA(str)]
+        str_list = ["abc", "def"]
+        self.assertTrue(isa_list == str_list)
+
+    def testEquailtyInListInvalid(self):
+        """Verify list contents are properly compared."""
+        isa_list = [mox.IsA(str), mox.IsA(str)]
+        mixed_list = ["abc", 123]
+        self.assertFalse(isa_list == mixed_list)
+
+    def testSpecialTypes(self):
+        """Verify that IsA can handle objects like io.StringIO."""
+        isA = mox.IsA(io.StringIO())
+        stringIO = io.StringIO()
+        self.assertTrue(isA == stringIO)
+
+
+class IsAlmostTest(testtools.TestCase):
+    """Verify IsAlmost correctly checks equality of floating point numbers."""
+
+    def testEqualityValid(self):
+        """Verify that == correctly identifies nearly equivalent floats."""
+        self.assertEqual(mox.IsAlmost(1.8999999999), 1.9)
+
+    def testEqualityInvalid(self):
+        """Verify that == correctly identifies non-equivalent floats."""
+        self.assertNotEqual(mox.IsAlmost(1.899), 1.9)
+
+    def testEqualityWithPlaces(self):
+        """Verify that specifying places has the desired effect."""
+        self.assertNotEqual(mox.IsAlmost(1.899), 1.9)
+        self.assertEqual(mox.IsAlmost(1.899, places=2), 1.9)
+
+    def testNonNumericTypes(self):
+        """Verify that IsAlmost handles non-numeric types properly."""
+
+        self.assertNotEqual(mox.IsAlmost(1.8999999999), '1.9')
+        self.assertNotEqual(mox.IsAlmost('1.8999999999'), 1.9)
+        self.assertNotEqual(mox.IsAlmost('1.8999999999'), '1.9')
+
+
+class ValueRememberTest(testtools.TestCase):
+    """Verify comparing argument against remembered value."""
+
+    def testValueEquals(self):
+        """Verify that value will compare to stored value."""
+        value = mox.Value()
+        value.store_value('hello world')
+        self.assertEqual(value, 'hello world')
+
+    def testNoValue(self):
+        """Verify that uninitialized value does not compare to empty values."""
+        value = mox.Value()
+        self.assertNotEqual(value, None)
+        self.assertNotEqual(value, False)
+        self.assertNotEqual(value, 0)
+        self.assertNotEqual(value, '')
+        self.assertNotEqual(value, ())
+        self.assertNotEqual(value, [])
+        self.assertNotEqual(value, {})
+        self.assertNotEqual(value, object())
+        self.assertNotEqual(value, set())
+
+    def testRememberValue(self):
+        """Verify that comparing against remember will store argument."""
+        value = mox.Value()
+        remember = mox.Remember(value)
+        self.assertNotEqual(value, 'hello world')  # value not yet stored.
+        self.assertEqual(remember, 'hello world')  # store value here.
+        self.assertEqual(value, 'hello world')  # compare against stored value.
+
+
+class MockMethodTest(testtools.TestCase):
+    """Test class to verify that the MockMethod class is working correctly."""
+
+    def setUp(self):
+        super(MockMethodTest, self).setUp()
+        self.expected_method = mox.MockMethod(
+            "testMethod", [], False)(['original'])
+        self.mock_method = mox.MockMethod(
+            "testMethod", [self.expected_method], True)
+
+    def testNameAttribute(self):
+        """Should provide a __name__ attribute."""
+        self.assertEqual('testMethod', self.mock_method.__name__)
+
+    def testAndReturnNoneByDefault(self):
+        """Should return None by default."""
+        return_value = self.mock_method(['original'])
+        self.assertTrue(return_value is None)
+
+    def testAndReturnValue(self):
+        """Should return a specificed return value."""
+        expected_return_value = "test"
+        self.expected_method.AndReturn(expected_return_value)
+        return_value = self.mock_method(['original'])
+        self.assertTrue(return_value == expected_return_value)
+
+    def testAndRaiseException(self):
+        """Should raise a specified exception."""
+        class TestException(Exception):
+            pass
+
+        expected_exception = TestException('test exception')
+        self.expected_method.AndRaise(expected_exception)
+        self.assertRaises(TestException, self.mock_method, ['original'])
+
+    def testWithSideEffects(self):
+        """Should call state modifier."""
+        local_list = ['original']
+
+        def modifier(mutable_list):
+            self.assertTrue(local_list is mutable_list)
+            mutable_list[0] = 'mutation'
+
+        self.expected_method.WithSideEffects(modifier).AndReturn(1)
+        self.mock_method(local_list)
+        self.assertEqual('mutation', local_list[0])
+
+    def testWithReturningSideEffects(self):
+        """Should call state modifier and propagate its return value."""
+        local_list = ['original']
+        expected_return = 'expected_return'
+
+        def modifier_with_return(mutable_list):
+            self.assertTrue(local_list is mutable_list)
+            mutable_list[0] = 'mutation'
+            return expected_return
+
+        self.expected_method.WithSideEffects(modifier_with_return)
+        actual_return = self.mock_method(local_list)
+        self.assertEqual('mutation', local_list[0])
+        self.assertEqual(expected_return, actual_return)
+
+    def testWithReturningSideEffectsWithAndReturn(self):
+        """Should call state modifier and ignore its return value."""
+        local_list = ['original']
+        expected_return = 'expected_return'
+        unexpected_return = 'unexpected_return'
+
+        def modifier_with_return(mutable_list):
+            self.assertTrue(local_list is mutable_list)
+            mutable_list[0] = 'mutation'
+            return unexpected_return
+
+        self.expected_method.WithSideEffects(modifier_with_return).AndReturn(
+            expected_return)
+        actual_return = self.mock_method(local_list)
+        self.assertEqual('mutation', local_list[0])
+        self.assertEqual(expected_return, actual_return)
+
+    def testEqualityNoParamsEqual(self):
+        """Methods with the same name and without params should be equal."""
+        expected_method = mox.MockMethod("testMethod", [], False)
+        self.assertEqual(self.mock_method, expected_method)
+
+    def testEqualityNoParamsNotEqual(self):
+        """Methods with different names without params should not be equal."""
+        expected_method = mox.MockMethod("otherMethod", [], False)
+        self.assertNotEqual(self.mock_method, expected_method)
+
+    def testEqualityParamsEqual(self):
+        """Methods with the same name and parameters should be equal."""
+        params = [1, 2, 3]
+        expected_method = mox.MockMethod("testMethod", [], False)
+        expected_method._params = params
+
+        self.mock_method._params = params
+        self.assertEqual(self.mock_method, expected_method)
+
+    def testEqualityParamsNotEqual(self):
+        """Methods with same name and different params should not be equal."""
+        expected_method = mox.MockMethod("testMethod", [], False)
+        expected_method._params = [1, 2, 3]
+
+        self.mock_method._params = ['a', 'b', 'c']
+        self.assertNotEqual(self.mock_method, expected_method)
+
+    def testEqualityNamedParamsEqual(self):
+        """Methods with the same name and same named params should be equal."""
+        named_params = {"input1": "test", "input2": "params"}
+        expected_method = mox.MockMethod("testMethod", [], False)
+        expected_method._named_params = named_params
+
+        self.mock_method._named_params = named_params
+        self.assertEqual(self.mock_method, expected_method)
+
+    def testEqualityNamedParamsNotEqual(self):
+        """Methods with same name and diffnamed params should not be equal."""
+        expected_method = mox.MockMethod("testMethod", [], False)
+        expected_method._named_params = {"input1": "test", "input2": "params"}
+
+        self.mock_method._named_params = {
+            "input1": "test2", "input2": "params2"}
+        self.assertNotEqual(self.mock_method, expected_method)
+
+    def testEqualityWrongType(self):
+        """Method should not be equal to an object of a different type."""
+        self.assertNotEqual(self.mock_method, "string?")
+
+    def testObjectEquality(self):
+        """Equality of objects should work without a Comparator"""
+        instA = TestClass()
+        instB = TestClass()
+
+        params = [instA, ]
+        expected_method = mox.MockMethod("testMethod", [], False)
+        expected_method._params = params
+
+        self.mock_method._params = [instB, ]
+        self.assertEqual(self.mock_method, expected_method)
+
+    def testStrConversion(self):
+        method = mox.MockMethod("f", [], False)
+        method(1, 2, "st", n1=8, n2="st2")
+        self.assertEqual(str(method),
+                         ("f(1, 2, 'st', n1=8, n2='st2') -> None"))
+
+        method = mox.MockMethod("testMethod", [], False)
+        method(1, 2, "only positional")
+        self.assertEqual(str(method),
+                         "testMethod(1, 2, 'only positional') -> None")
+
+        method = mox.MockMethod("testMethod", [], False)
+        method(a=1, b=2, c="only named")
+        self.assertEqual(str(method),
+                         "testMethod(a=1, b=2, c='only named') -> None")
+
+        method = mox.MockMethod("testMethod", [], False)
+        method()
+        self.assertEqual(str(method), "testMethod() -> None")
+
+        method = mox.MockMethod("testMethod", [], False)
+        method(x="only 1 parameter")
+        self.assertEqual(str(method),
+                         "testMethod(x='only 1 parameter') -> None")
+
+        method = mox.MockMethod("testMethod", [], False)
+        method().AndReturn('return_value')
+        self.assertEqual(str(method), "testMethod() -> 'return_value'")
+
+        method = mox.MockMethod("testMethod", [], False)
+        method().AndReturn(('a', {1: 2}))
+        self.assertEqual(str(method), "testMethod() -> ('a', {1: 2})")
+
+
+class MockAnythingTest(testtools.TestCase):
+    """Verify that the MockAnything class works as expected."""
+
+    def setUp(self):
+        super(MockAnythingTest, self).setUp()
+        self.mock_object = mox.MockAnything()
+
+    def testRepr(self):
+        """Calling repr on a MockAnything instance must work."""
+        self.assertEqual('<MockAnything instance>', repr(self.mock_object))
+
+    def testCanMockStr(self):
+        self.mock_object.__str__().AndReturn("foo")
+        self.mock_object._Replay()
+        actual = str(self.mock_object)
+        self.mock_object._Verify()
+        self.assertEqual("foo", actual)
+
+    def testSetupMode(self):
+        """Verify the mock will accept any call."""
+        self.mock_object.NonsenseCall()
+        self.assertTrue(len(self.mock_object._expected_calls_queue) == 1)
+
+    def testReplayWithExpectedCall(self):
+        """Verify the mock replays method calls as expected."""
+        self.mock_object.ValidCall()                    # setup method call
+        self.mock_object._Replay()                        # start replay mode
+        self.mock_object.ValidCall()                    # make method call
+
+    def testReplayWithUnexpectedCall(self):
+        """Unexpected method calls should raise UnexpectedMethodCallError."""
+        self.mock_object.ValidCall()                    # setup method call
+        self.mock_object._Replay()                         # start replay mode
+        self.assertRaises(mox.UnexpectedMethodCallError,
+                          self.mock_object.OtherValidCall)
+
+    def testVerifyWithCompleteReplay(self):
+        """Verify should not raise an exception for a valid replay."""
+        self.mock_object.ValidCall()                    # setup method call
+        self.mock_object._Replay()                         # start replay mode
+        self.mock_object.ValidCall()                    # make method call
+        self.mock_object._Verify()
+
+    def testVerifyWithIncompleteReplay(self):
+        """Verify should raise an exception if the replay was not complete."""
+        self.mock_object.ValidCall()                    # setup method call
+        self.mock_object._Replay()                         # start replay mode
+        # ValidCall() is never made
+        self.assertRaises(
+            mox.ExpectedMethodCallsError, self.mock_object._Verify)
+
+    def testSpecialClassMethod(self):
+        """Verify should not raise exception when special methods are used."""
+        self.mock_object[1].AndReturn(True)
+        self.mock_object._Replay()
+        returned_val = self.mock_object[1]
+        self.assertTrue(returned_val)
+        self.mock_object._Verify()
+
+    def testNonzero(self):
+        """You should be able to use the mock object in an if."""
+        self.mock_object._Replay()
+        if self.mock_object:
+            pass
+
+    def testNotNone(self):
+        """Mock should be comparable to None."""
+        self.mock_object._Replay()
+        if self.mock_object is not None:
+            pass
+
+        if self.mock_object is None:
+            pass
+
+    def testEquals(self):
+        """A mock should be able to compare itself to another object."""
+        self.mock_object._Replay()
+        self.assertEqual(self.mock_object, self.mock_object)
+
+    def testEqualsMockFailure(self):
+        """Verify equals identifies unequal objects."""
+        self.mock_object.SillyCall()
+        self.mock_object._Replay()
+        self.assertNotEqual(self.mock_object, mox.MockAnything())
+
+    def testEqualsInstanceFailure(self):
+        """Verify equals identifies that objects are different instances."""
+        self.mock_object._Replay()
+        self.assertNotEqual(self.mock_object, TestClass())
+
+    def testNotEquals(self):
+        """Verify not equals works."""
+        self.mock_object._Replay()
+        self.assertFalse(self.mock_object != self.mock_object)
+
+    def testNestedMockCallsRecordedSerially(self):
+        """Test that nested calls work when recorded serially."""
+        self.mock_object.CallInner().AndReturn(1)
+        self.mock_object.CallOuter(1)
+        self.mock_object._Replay()
+
+        self.mock_object.CallOuter(self.mock_object.CallInner())
+
+        self.mock_object._Verify()
+
+    def testNestedMockCallsRecordedNested(self):
+        """Test that nested cals work when recorded in a nested fashion."""
+        self.mock_object.CallOuter(self.mock_object.CallInner().AndReturn(1))
+        self.mock_object._Replay()
+
+        self.mock_object.CallOuter(self.mock_object.CallInner())
+
+        self.mock_object._Verify()
+
+    def testIsCallable(self):
+        """Test that MockAnything can even mock a simple callable.
+
+        This is handy for "stubbing out" a method in a module with a mock, and
+        verifying that it was called.
+        """
+        self.mock_object().AndReturn('mox0rd')
+        self.mock_object._Replay()
+
+        self.assertEqual('mox0rd', self.mock_object())
+
+        self.mock_object._Verify()
+
+    def testIsReprable(self):
+        """Test that MockAnythings can be repr'd without causing a failure."""
+        self.assertTrue('MockAnything' in repr(self.mock_object))
+
+
+class MethodCheckerTest(testtools.TestCase):
+    """Tests MockMethod's use of MethodChecker method."""
+
+    def testUnboundMethodsRequiresInstance(self):
+        # SKIP TEST IN PYTHON 2.x (Ugly hack for python 2.6)
+        # REASON: semantics for unbound methods has changed only in Python 3
+        #     so this test in earlier versions is invald
+        if sys.version_info < (3, 0):
+            return
+
+        instance = CheckCallTestClass()
+        method = mox.MockMethod('NoParameters', [], False,
+                                CheckCallTestClass.NoParameters)
+
+        self.assertRaises(AttributeError, method)
+        method(instance)
+        self.assertRaises(AttributeError, method, instance, 1)
+
+    def testNoParameters(self):
+        method = mox.MockMethod('NoParameters', [], False,
+                                CheckCallTestClass.NoParameters,
+                                class_to_bind=CheckCallTestClass)
+        method()
+        self.assertRaises(AttributeError, method, 1)
+        self.assertRaises(AttributeError, method, 1, 2)
+        self.assertRaises(AttributeError, method, a=1)
+        self.assertRaises(AttributeError, method, 1, b=2)
+
+    def testOneParameter(self):
+        method = mox.MockMethod('OneParameter', [], False,
+                                CheckCallTestClass.OneParameter,
+                                class_to_bind=CheckCallTestClass)
+        self.assertRaises(AttributeError, method)
+        method(1)
+        method(a=1)
+        self.assertRaises(AttributeError, method, b=1)
+        self.assertRaises(AttributeError, method, 1, 2)
+        self.assertRaises(AttributeError, method, 1, a=2)
+        self.assertRaises(AttributeError, method, 1, b=2)
+
+    def testTwoParameters(self):
+        method = mox.MockMethod('TwoParameters', [], False,
+                                CheckCallTestClass.TwoParameters,
+                                class_to_bind=CheckCallTestClass)
+        self.assertRaises(AttributeError, method)
+        self.assertRaises(AttributeError, method, 1)
+        self.assertRaises(AttributeError, method, a=1)
+        self.assertRaises(AttributeError, method, b=1)
+        method(1, 2)
+        method(1, b=2)
+        method(a=1, b=2)
+        method(b=2, a=1)
+        self.assertRaises(AttributeError, method, b=2, c=3)
+        self.assertRaises(AttributeError, method, a=1, b=2, c=3)
+        self.assertRaises(AttributeError, method, 1, 2, 3)
+        self.assertRaises(AttributeError, method, 1, 2, 3, 4)
+        self.assertRaises(AttributeError, method, 3, a=1, b=2)
+
+    def testOneDefaultValue(self):
+        method = mox.MockMethod('OneDefaultValue', [], False,
+                                CheckCallTestClass.OneDefaultValue,
+                                class_to_bind=CheckCallTestClass)
+        method()
+        method(1)
+        method(a=1)
+        self.assertRaises(AttributeError, method, b=1)
+        self.assertRaises(AttributeError, method, 1, 2)
+        self.assertRaises(AttributeError, method, 1, a=2)
+        self.assertRaises(AttributeError, method, 1, b=2)
+
+    def testTwoDefaultValues(self):
+        method = mox.MockMethod('TwoDefaultValues', [], False,
+                                CheckCallTestClass.TwoDefaultValues,
+                                class_to_bind=CheckCallTestClass)
+        self.assertRaises(AttributeError, method)
+        self.assertRaises(AttributeError, method, c=3)
+        self.assertRaises(AttributeError, method, 1)
+        self.assertRaises(AttributeError, method, 1, d=4)
+        self.assertRaises(AttributeError, method, 1, d=4, c=3)
+        method(1, 2)
+        method(a=1, b=2)
+        method(1, 2, 3)
+        method(1, 2, 3, 4)
+        method(1, 2, c=3)
+        method(1, 2, c=3, d=4)
+        method(1, 2, d=4, c=3)
+        method(d=4, c=3, a=1, b=2)
+        self.assertRaises(AttributeError, method, 1, 2, 3, 4, 5)
+        self.assertRaises(AttributeError, method, 1, 2, e=9)
+        self.assertRaises(AttributeError, method, a=1, b=2, e=9)
+
+    def testArgs(self):
+        method = mox.MockMethod('Args', [], False, CheckCallTestClass.Args,
+                                class_to_bind=CheckCallTestClass)
+        self.assertRaises(AttributeError, method)
+        self.assertRaises(AttributeError, method, 1)
+        method(1, 2)
+        method(a=1, b=2)
+        method(1, 2, 3)
+        method(1, 2, 3, 4)
+        self.assertRaises(AttributeError, method, 1, 2, a=3)
+        self.assertRaises(AttributeError, method, 1, 2, c=3)
+
+    def testKwargs(self):
+        method = mox.MockMethod('Kwargs', [], False, CheckCallTestClass.Kwargs,
+                                class_to_bind=CheckCallTestClass)
+        self.assertRaises(AttributeError, method)
+        method(1)
+        method(1, 2)
+        method(a=1, b=2)
+        method(b=2, a=1)
+        self.assertRaises(AttributeError, method, 1, 2, 3)
+        self.assertRaises(AttributeError, method, 1, 2, a=3)
+        method(1, 2, c=3)
+        method(a=1, b=2, c=3)
+        method(c=3, a=1, b=2)
+        method(a=1, b=2, c=3, d=4)
+        self.assertRaises(AttributeError, method, 1, 2, 3, 4)
+
+    def testArgsAndKwargs(self):
+        method = mox.MockMethod('ArgsAndKwargs', [], False,
+                                CheckCallTestClass.ArgsAndKwargs,
+                                class_to_bind=CheckCallTestClass)
+        self.assertRaises(AttributeError, method)
+        method(1)
+        method(1, 2)
+        method(1, 2, 3)
+        method(a=1)
+        method(1, b=2)
+        self.assertRaises(AttributeError, method, 1, a=2)
+        method(b=2, a=1)
+        method(c=3, b=2, a=1)
+        method(1, 2, c=3)
+
+
+class CheckCallTestClass(object):
+    def NoParameters(self):
+        pass
+
+    def OneParameter(self, a):
+        pass
+
+    def TwoParameters(self, a, b):
+        pass
+
+    def OneDefaultValue(self, a=1):
+        pass
+
+    def TwoDefaultValues(self, a, b, c=1, d=2):
+        pass
+
+    def Args(self, a, b, *args):
+        pass
+
+    def Kwargs(self, a, b=2, **kwargs):
+        pass
+
+    def ArgsAndKwargs(self, a, *args, **kwargs):
+        pass
+
+
+class MockObjectTest(testtools.TestCase):
+    """Verify that the MockObject class works as exepcted."""
+
+    def setUp(self):
+        super(MockObjectTest, self).setUp()
+        self.mock_object = mox.MockObject(TestClass)
+
+    def testSetupModeWithValidCall(self):
+        """Verify the mock object properly mocks a basic method call."""
+        self.mock_object.ValidCall()
+        self.assertTrue(len(self.mock_object._expected_calls_queue) == 1)
+
+    def testSetupModeWithInvalidCall(self):
+        """Rase UnknownMethodCallError for a non-member method call.
+        """
+        # Note: assertRaises does not catch exceptions thrown by MockObject's
+        # __getattr__
+        try:
+            self.mock_object.InvalidCall()
+            self.fail("No exception thrown, expected UnknownMethodCallError")
+        except mox.UnknownMethodCallError:
+            pass
+        except Exception:
+            self.fail("Wrong exception type thrown,"
+                      " expected UnknownMethodCallError")
+
+    def testReplayWithInvalidCall(self):
+        """Rase UnknownMethodCallError for a non-member method call.
+        """
+        self.mock_object.ValidCall()  # setup method call
+        self.mock_object._Replay()  # start replay mode
+        # Note: assertRaises does not catch exceptions thrown by MockObject's
+        # __getattr__
+        try:
+            self.mock_object.InvalidCall()
+            self.fail("No exception thrown, expected UnknownMethodCallError")
+        except mox.UnknownMethodCallError:
+            pass
+        except Exception:
+            self.fail("Wrong exception type thrown,"
+                      " expected UnknownMethodCallError")
+
+    def testIsInstance(self):
+        """Mock should be able to pass as an instance of the mocked class."""
+        self.assertTrue(isinstance(self.mock_object, TestClass))
+
+    def testFindValidMethods(self):
+        """Mock should be able to mock all public methods."""
+        self.assertTrue('ValidCall' in self.mock_object._known_methods)
+        self.assertTrue('OtherValidCall' in self.mock_object._known_methods)
+        self.assertTrue('MyClassMethod' in self.mock_object._known_methods)
+        self.assertTrue('MyStaticMethod' in self.mock_object._known_methods)
+        self.assertTrue('_ProtectedCall' in self.mock_object._known_methods)
+        self.assertTrue('__PrivateCall' not in self.mock_object._known_methods)
+        self.assertTrue(
+            '_TestClass__PrivateCall' in self.mock_object._known_methods)
+
+    def testFindsSuperclassMethods(self):
+        """Mock should be able to mock superclasses methods."""
+        self.mock_object = mox.MockObject(ChildClass)
+        self.assertTrue('ValidCall' in self.mock_object._known_methods)
+        self.assertTrue('OtherValidCall' in self.mock_object._known_methods)
+        self.assertTrue('MyClassMethod' in self.mock_object._known_methods)
+        self.assertTrue('ChildValidCall' in self.mock_object._known_methods)
+
+    def testAccessClassVariables(self):
+        """Class variables should be accessible through the mock."""
+        self.assertTrue('SOME_CLASS_VAR' in self.mock_object._known_vars)
+        self.assertTrue('_PROTECTED_CLASS_VAR' in self.mock_object._known_vars)
+        self.assertEqual('test_value', self.mock_object.SOME_CLASS_VAR)
+
+    def testEquals(self):
+        """A mock should be able to compare itself to another object."""
+        self.mock_object._Replay()
+        self.assertEqual(self.mock_object, self.mock_object)
+
+    def testEqualsMockFailure(self):
+        """Verify equals identifies unequal objects."""
+        self.mock_object.ValidCall()
+        self.mock_object._Replay()
+        self.assertNotEqual(self.mock_object, mox.MockObject(TestClass))
+
+    def testEqualsInstanceFailure(self):
+        """Verify equals identifies that objects are different instances."""
+        self.mock_object._Replay()
+        self.assertNotEqual(self.mock_object, TestClass())
+
+    def testNotEquals(self):
+        """Verify not equals works."""
+        self.mock_object._Replay()
+        self.assertFalse(self.mock_object != self.mock_object)
+
+    def testMockSetItem_ExpectedSetItem_Success(self):
+        """Test that __setitem__() gets mocked in Dummy.
+
+        In this test, _Verify() succeeds.
+        """
+        dummy = mox.MockObject(TestClass)
+        dummy['X'] = 'Y'
+
+        dummy._Replay()
+
+        dummy['X'] = 'Y'
+
+        dummy._Verify()
+
+    def testMockSetItem_ExpectedSetItem_NoSuccess(self):
+        """Test that __setitem__() gets mocked in Dummy.
+
+        In this test, _Verify() fails.
+        """
+        dummy = mox.MockObject(TestClass)
+        dummy['X'] = 'Y'
+
+        dummy._Replay()
+
+        # NOT doing dummy['X'] = 'Y'
+
+        self.assertRaises(mox.ExpectedMethodCallsError, dummy._Verify)
+
+    def testMockSetItem_ExpectedNoSetItem_Success(self):
+        """Test that __setitem__() gets mocked in Dummy."""
+        dummy = mox.MockObject(TestClass)
+        # NOT doing dummy['X'] = 'Y'
+
+        dummy._Replay()
+
+        def call():
+            dummy['X'] = 'Y'
+
+        self.assertRaises(mox.UnexpectedMethodCallError, call)
+
+    def testMockSetItem_ExpectedNoSetItem_NoSuccess(self):
+        """Test that __setitem__() gets mocked in Dummy.
+
+        In this test, _Verify() fails.
+        """
+        dummy = mox.MockObject(TestClass)
+        # NOT doing dummy['X'] = 'Y'
+
+        dummy._Replay()
+
+        # NOT doing dummy['X'] = 'Y'
+
+        dummy._Verify()
+
+    def testMockSetItem_ExpectedSetItem_NonmatchingParameters(self):
+        """Test that __setitem__() fails if other parameters are expected."""
+        dummy = mox.MockObject(TestClass)
+        dummy['X'] = 'Y'
+
+        dummy._Replay()
+
+        def call():
+            dummy['wrong'] = 'Y'
+
+        self.assertRaises(mox.UnexpectedMethodCallError, call)
+
+        dummy._Verify()
+
+    def testMockSetItem_WithSubClassOfNewStyleClass(self):
+        class NewStyleTestClass(object):
+            def __init__(self):
+                self.my_dict = {}
+
+            def __setitem__(self, key, value):
+                self.my_dict[key], value
+
+        class TestSubClass(NewStyleTestClass):
+            pass
+
+        dummy = mox.MockObject(TestSubClass)
+        dummy[1] = 2
+        dummy._Replay()
+        dummy[1] = 2
+        dummy._Verify()
+
+    def testMockGetItem_ExpectedGetItem_Success(self):
+        """Test that __getitem__() gets mocked in Dummy.
+
+        In this test, _Verify() succeeds.
+        """
+        dummy = mox.MockObject(TestClass)
+        dummy['X'].AndReturn('value')
+
+        dummy._Replay()
+
+        self.assertEqual(dummy['X'], 'value')
+
+        dummy._Verify()
+
+    def testMockGetItem_ExpectedGetItem_NoSuccess(self):
+        """Test that __getitem__() gets mocked in Dummy.
+
+        In this test, _Verify() fails.
+        """
+        dummy = mox.MockObject(TestClass)
+        dummy['X'].AndReturn('value')
+
+        dummy._Replay()
+
+        # NOT doing dummy['X']
+
+        self.assertRaises(mox.ExpectedMethodCallsError, dummy._Verify)
+
+    def testMockGetItem_ExpectedNoGetItem_NoSuccess(self):
+        """Test that __getitem__() gets mocked in Dummy."""
+        dummy = mox.MockObject(TestClass)
+        # NOT doing dummy['X']
+
+        dummy._Replay()
+
+        def call():
+            return dummy['X']
+
+        self.assertRaises(mox.UnexpectedMethodCallError, call)
+
+    def testMockGetItem_ExpectedGetItem_NonmatchingParameters(self):
+        """Test that __getitem__() fails if other parameters are expected."""
+        dummy = mox.MockObject(TestClass)
+        dummy['X'].AndReturn('value')
+
+        dummy._Replay()
+
+        def call():
+            return dummy['wrong']
+
+        self.assertRaises(mox.UnexpectedMethodCallError, call)
+
+        dummy._Verify()
+
+    def testMockGetItem_WithSubClassOfNewStyleClass(self):
+        class NewStyleTestClass(object):
+            def __getitem__(self, key):
+                return {1: '1', 2: '2'}[key]
+
+        class TestSubClass(NewStyleTestClass):
+            pass
+
+        dummy = mox.MockObject(TestSubClass)
+        dummy[1].AndReturn('3')
+
+        dummy._Replay()
+        self.assertEqual('3', dummy.__getitem__(1))
+        dummy._Verify()
+
+    def testMockIter_ExpectedIter_Success(self):
+        """Test that __iter__() gets mocked in Dummy.
+
+        In this test, _Verify() succeeds.
+        """
+        dummy = mox.MockObject(TestClass)
+        iter(dummy).AndReturn(iter(['X', 'Y']))
+
+        dummy._Replay()
+
+        self.assertEqual([x for x in dummy], ['X', 'Y'])
+
+        dummy._Verify()
+
+    def testMockContains_ExpectedContains_Success(self):
+        """Test that __contains__ gets mocked in Dummy.
+
+        In this test, _Verify() succeeds.
+        """
+        dummy = mox.MockObject(TestClass)
+        dummy.__contains__('X').AndReturn(True)
+
+        dummy._Replay()
+
+        self.assertTrue('X' in dummy)
+
+        dummy._Verify()
+
+    def testMockContains_ExpectedContains_NoSuccess(self):
+        """Test that __contains__() gets mocked in Dummy.
+
+        In this test, _Verify() fails.
+        """
+        dummy = mox.MockObject(TestClass)
+        dummy.__contains__('X').AndReturn('True')
+
+        dummy._Replay()
+
+        # NOT doing 'X' in dummy
+
+        self.assertRaises(mox.ExpectedMethodCallsError, dummy._Verify)
+
+    def testMockContains_ExpectedContains_NonmatchingParameter(self):
+        """Test that __contains__ fails if other parameters are expected."""
+        dummy = mox.MockObject(TestClass)
+        dummy.__contains__('X').AndReturn(True)
+
+        dummy._Replay()
+
+        def call():
+            return 'Y' in dummy
+
+        self.assertRaises(mox.UnexpectedMethodCallError, call)
+
+        dummy._Verify()
+
+    def testMockIter_ExpectedIter_NoSuccess(self):
+        """Test that __iter__() gets mocked in Dummy.
+
+        In this test, _Verify() fails.
+        """
+        dummy = mox.MockObject(TestClass)
+        iter(dummy).AndReturn(iter(['X', 'Y']))
+
+        dummy._Replay()
+
+        # NOT doing self.assertEqual([x for x in dummy], ['X', 'Y'])
+
+        self.assertRaises(mox.ExpectedMethodCallsError, dummy._Verify)
+
+    def testMockIter_ExpectedNoIter_NoSuccess(self):
+        """Test that __iter__() gets mocked in Dummy."""
+        dummy = mox.MockObject(TestClass)
+        # NOT doing iter(dummy)
+
+        dummy._Replay()
+
+        def call():
+            return [x for x in dummy]
+        self.assertRaises(mox.UnexpectedMethodCallError, call)
+
+    def testMockIter_ExpectedGetItem_Success(self):
+        """Test that __iter__() gets mocked in Dummy using getitem."""
+        dummy = mox.MockObject(SubscribtableNonIterableClass)
+        dummy[0].AndReturn('a')
+        dummy[1].AndReturn('b')
+        dummy[2].AndRaise(IndexError)
+
+        dummy._Replay()
+        self.assertEqual(['a', 'b'], [x for x in dummy])
+        dummy._Verify()
+
+    def testMockIter_ExpectedNoGetItem_NoSuccess(self):
+        """Test that __iter__() gets mocked in Dummy using getitem."""
+        dummy = mox.MockObject(SubscribtableNonIterableClass)
+        # NOT doing dummy[index]
+
+        dummy._Replay()
+        function = lambda: [x for x in dummy]
+        self.assertRaises(mox.UnexpectedMethodCallError, function)
+
+    def testMockGetIter_WithSubClassOfNewStyleClass(self):
+        class NewStyleTestClass(object):
+            def __iter__(self):
+                return iter([1, 2, 3])
+
+        class TestSubClass(NewStyleTestClass):
+            pass
+
+        dummy = mox.MockObject(TestSubClass)
+        iter(dummy).AndReturn(iter(['a', 'b']))
+        dummy._Replay()
+        self.assertEqual(['a', 'b'], [x for x in dummy])
+        dummy._Verify()
+
+    def testInstantiationWithAdditionalAttributes(self):
+        mock_object = mox.MockObject(TestClass, attrs={"attr1": "value"})
+        self.assertEqual(mock_object.attr1, "value")
+
+    def testCantOverrideMethodsWithAttributes(self):
+        self.assertRaises(ValueError, mox.MockObject, TestClass,
+                          attrs={"ValidCall": "value"})
+
+    def testCantMockNonPublicAttributes(self):
+        self.assertRaises(mox.PrivateAttributeError, mox.MockObject, TestClass,
+                          attrs={"_protected": "value"})
+        self.assertRaises(mox.PrivateAttributeError, mox.MockObject, TestClass,
+                          attrs={"__private": "value"})
+
+
+class MoxTest(testtools.TestCase):
+    """Verify Mox works correctly."""
+
+    def setUp(self):
+        super(MoxTest, self).setUp()
+        self.mox = mox.Mox()
+
+    def testCreateObject(self):
+        """Mox should create a mock object."""
+        self.mox.CreateMock(TestClass)
+
+    def testVerifyObjectWithCompleteReplay(self):
+        """Mox should replay and verify all objects it created."""
+        mock_obj = self.mox.CreateMock(TestClass)
+        mock_obj.ValidCall()
+        mock_obj.ValidCallWithArgs(mox.IsA(TestClass))
+        self.mox.ReplayAll()
+        mock_obj.ValidCall()
+        mock_obj.ValidCallWithArgs(TestClass("some_value"))
+        self.mox.VerifyAll()
+
+    def testVerifyObjectWithIncompleteReplay(self):
+        """Mox should raise an exception if a mock didn't replay completely."""
+        mock_obj = self.mox.CreateMock(TestClass)
+        mock_obj.ValidCall()
+        self.mox.ReplayAll()
+        # ValidCall() is never made
+        self.assertRaises(mox.ExpectedMethodCallsError, self.mox.VerifyAll)
+
+    def testEntireWorkflow(self):
+        """Test the whole work flow."""
+        mock_obj = self.mox.CreateMock(TestClass)
+        mock_obj.ValidCall().AndReturn("yes")
+        self.mox.ReplayAll()
+
+        ret_val = mock_obj.ValidCall()
+        self.assertEqual("yes", ret_val)
+        self.mox.VerifyAll()
+
+    def testSignatureMatchingWithComparatorAsFirstArg(self):
+        """Test that the first argument can be a comparator."""
+
+        def VerifyLen(val):
+            """This will raise an exception when not given a list.
+
+            This exception will be raised when trying to infer/validate the
+            method signature.
+            """
+            return len(val) != 1
+
+        mock_obj = self.mox.CreateMock(TestClass)
+        # This intentionally does not name the 'nine' param so it triggers
+        # deeper inspection.
+        mock_obj.MethodWithArgs(mox.Func(VerifyLen), mox.IgnoreArg(), None)
+        self.mox.ReplayAll()
+
+        mock_obj.MethodWithArgs([1, 2], "foo", None)
+
+        self.mox.VerifyAll()
+
+    def testCallableObject(self):
+        """Test recording calls to a callable object works."""
+        mock_obj = self.mox.CreateMock(CallableClass)
+        mock_obj("foo").AndReturn("qux")
+        self.mox.ReplayAll()
+
+        ret_val = mock_obj("foo")
+        self.assertEqual("qux", ret_val)
+        self.mox.VerifyAll()
+
+    def testInheritedCallableObject(self):
+        """Recording calls to an object inheriting from a callable object."""
+        mock_obj = self.mox.CreateMock(InheritsFromCallable)
+        mock_obj("foo").AndReturn("qux")
+        self.mox.ReplayAll()
+
+        ret_val = mock_obj("foo")
+        self.assertEqual("qux", ret_val)
+        self.mox.VerifyAll()
+
+    def testCallOnNonCallableObject(self):
+        """Test that you cannot call a non-callable object."""
+        mock_obj = self.mox.CreateMock("string is not callable")
+        self.assertRaises(TypeError, mock_obj)
+
+    def testCallableObjectWithBadCall(self):
+        """Test verifying calls to a callable object works."""
+        mock_obj = self.mox.CreateMock(CallableClass)
+        mock_obj("foo").AndReturn("qux")
+        self.mox.ReplayAll()
+
+        self.assertRaises(mox.UnexpectedMethodCallError, mock_obj, "ZOOBAZ")
+
+    def testCallableObjectVerifiesSignature(self):
+        mock_obj = self.mox.CreateMock(CallableClass)
+        # Too many arguments
+        self.assertRaises(AttributeError, mock_obj, "foo", "bar")
+
+    def testUnorderedGroup(self):
+        """Test that using one unordered group works."""
+        mock_obj = self.mox.CreateMockAnything()
+        mock_obj.Method(1).InAnyOrder()
+        mock_obj.Method(2).InAnyOrder()
+        self.mox.ReplayAll()
+
+        mock_obj.Method(2)
+        mock_obj.Method(1)
+
+        self.mox.VerifyAll()
+
+    def testUnorderedGroupsInline(self):
+        """Unordered groups should work in the context of ordered calls."""
+        mock_obj = self.mox.CreateMockAnything()
+        mock_obj.Open()
+        mock_obj.Method(1).InAnyOrder()
+        mock_obj.Method(2).InAnyOrder()
+        mock_obj.Close()
+        self.mox.ReplayAll()
+
+        mock_obj.Open()
+        mock_obj.Method(2)
+        mock_obj.Method(1)
+        mock_obj.Close()
+
+        self.mox.VerifyAll()
+
+    def testMultipleUnorderdGroups(self):
+        """Multiple unoreded groups should work."""
+        mock_obj = self.mox.CreateMockAnything()
+        mock_obj.Method(1).InAnyOrder()
+        mock_obj.Method(2).InAnyOrder()
+        mock_obj.Foo().InAnyOrder('group2')
+        mock_obj.Bar().InAnyOrder('group2')
+        self.mox.ReplayAll()
+
+        mock_obj.Method(2)
+        mock_obj.Method(1)
+        mock_obj.Bar()
+        mock_obj.Foo()
+
+        self.mox.VerifyAll()
+
+    def testMultipleUnorderdGroupsOutOfOrder(self):
+        """Multiple unordered groups should maintain external order"""
+        mock_obj = self.mox.CreateMockAnything()
+        mock_obj.Method(1).InAnyOrder()
+        mock_obj.Method(2).InAnyOrder()
+        mock_obj.Foo().InAnyOrder('group2')
+        mock_obj.Bar().InAnyOrder('group2')
+        self.mox.ReplayAll()
+
+        mock_obj.Method(2)
+        self.assertRaises(mox.UnexpectedMethodCallError, mock_obj.Bar)
+
+    def testUnorderedGroupWithReturnValue(self):
+        """Unordered groups should work with return values."""
+        mock_obj = self.mox.CreateMockAnything()
+        mock_obj.Open()
+        mock_obj.Method(1).InAnyOrder().AndReturn(9)
+        mock_obj.Method(2).InAnyOrder().AndReturn(10)
+        mock_obj.Close()
+        self.mox.ReplayAll()
+
+        mock_obj.Open()
+        actual_two = mock_obj.Method(2)
+        actual_one = mock_obj.Method(1)
+        mock_obj.Close()
+
+        self.assertEqual(9, actual_one)
+        self.assertEqual(10, actual_two)
+
+        self.mox.VerifyAll()
+
+    def testUnorderedGroupWithComparator(self):
+        """Unordered groups should work with comparators."""
+
+        def VerifyOne(cmd):
+            if not isinstance(cmd, str):
+                self.fail('Unexpected type passed to comparator: ' + str(cmd))
+            return cmd == 'test'
+
+        def VerifyTwo(cmd):
+            return True
+
+        mock_obj = self.mox.CreateMockAnything()
+        mock_obj.Foo(['test'], mox.Func(VerifyOne), bar=1).InAnyOrder().\
+            AndReturn('yes test')
+        mock_obj.Foo(['test'], mox.Func(VerifyTwo), bar=1).InAnyOrder().\
+            AndReturn('anything')
+
+        self.mox.ReplayAll()
+
+        mock_obj.Foo(['test'], 'anything', bar=1)
+        mock_obj.Foo(['test'], 'test', bar=1)
+
+        self.mox.VerifyAll()
+
+    def testMultipleTimes(self):
+        """Test if MultipleTimesGroup works."""
+        mock_obj = self.mox.CreateMockAnything()
+        mock_obj.Method(1).MultipleTimes().AndReturn(9)
+        mock_obj.Method(2).AndReturn(10)
+        mock_obj.Method(3).MultipleTimes().AndReturn(42)
+        self.mox.ReplayAll()
+
+        actual_one = mock_obj.Method(1)
+        second_one = mock_obj.Method(1)    # This tests MultipleTimes.
+        actual_two = mock_obj.Method(2)
+        actual_three = mock_obj.Method(3)
+        mock_obj.Method(3)
+        mock_obj.Method(3)
+
+        self.mox.VerifyAll()
+
+        self.assertEqual(9, actual_one)
+        # Repeated calls should return same number.
+        self.assertEqual(9, second_one)
+        self.assertEqual(10, actual_two)
+        self.assertEqual(42, actual_three)
+
+    def testMultipleTimesUsingIsAParameter(self):
+        """Test if MultipleTimesGroup works with a IsA parameter."""
+        mock_obj = self.mox.CreateMockAnything()
+        mock_obj.Open()
+        mock_obj.Method(mox.IsA(str)).MultipleTimes("IsA").AndReturn(9)
+        mock_obj.Close()
+        self.mox.ReplayAll()
+
+        mock_obj.Open()
+        actual_one = mock_obj.Method("1")
+        second_one = mock_obj.Method("2")    # This tests MultipleTimes.
+        mock_obj.Close()
+
+        self.mox.VerifyAll()
+
+        self.assertEqual(9, actual_one)
+        # Repeated calls should return same number.
+        self.assertEqual(9, second_one)
+
+    def testMutlipleTimesUsingFunc(self):
+        """Test that the Func is not evaluated more times than necessary.
+
+        If a Func() has side effects, it can cause a passing test to fail.
+        """
+
+        self.counter = 0
+
+        def MyFunc(actual_str):
+            """Increment the counter if actual_str == 'foo'."""
+            if actual_str == 'foo':
+                self.counter += 1
+            return True
+
+        mock_obj = self.mox.CreateMockAnything()
+        mock_obj.Open()
+        mock_obj.Method(mox.Func(MyFunc)).MultipleTimes()
+        mock_obj.Close()
+        self.mox.ReplayAll()
+
+        mock_obj.Open()
+        mock_obj.Method('foo')
+        mock_obj.Method('foo')
+        mock_obj.Method('not-foo')
+        mock_obj.Close()
+
+        self.mox.VerifyAll()
+
+        self.assertEqual(2, self.counter)
+
+    def testMultipleTimesThreeMethods(self):
+        """Test if MultipleTimesGroup works with three or more methods."""
+        mock_obj = self.mox.CreateMockAnything()
+        mock_obj.Open()
+        mock_obj.Method(1).MultipleTimes().AndReturn(9)
+        mock_obj.Method(2).MultipleTimes().AndReturn(8)
+        mock_obj.Method(3).MultipleTimes().AndReturn(7)
+        mock_obj.Method(4).AndReturn(10)
+        mock_obj.Close()
+        self.mox.ReplayAll()
+
+        mock_obj.Open()
+        actual_three = mock_obj.Method(3)
+        mock_obj.Method(1)
+        actual_two = mock_obj.Method(2)
+        mock_obj.Method(3)
+        actual_one = mock_obj.Method(1)
+        actual_four = mock_obj.Method(4)
+        mock_obj.Close()
+
+        self.assertEqual(9, actual_one)
+        self.assertEqual(8, actual_two)
+        self.assertEqual(7, actual_three)
+        self.assertEqual(10, actual_four)
+
+        self.mox.VerifyAll()
+
+    def testMultipleTimesMissingOne(self):
+        """Test if MultipleTimesGroup fails if one method is missing."""
+        mock_obj = self.mox.CreateMockAnything()
+        mock_obj.Open()
+        mock_obj.Method(1).MultipleTimes().AndReturn(9)
+        mock_obj.Method(2).MultipleTimes().AndReturn(8)
+        mock_obj.Method(3).MultipleTimes().AndReturn(7)
+        mock_obj.Method(4).AndReturn(10)
+        mock_obj.Close()
+        self.mox.ReplayAll()
+
+        mock_obj.Open()
+        mock_obj.Method(3)
+        mock_obj.Method(2)
+        mock_obj.Method(3)
+        mock_obj.Method(3)
+        mock_obj.Method(2)
+
+        self.assertRaises(mox.UnexpectedMethodCallError, mock_obj.Method, 4)
+
+    def testMultipleTimesTwoGroups(self):
+        """Test if MultipleTimesGroup works with a group after a
+        MultipleTimesGroup.
+        """
+        mock_obj = self.mox.CreateMockAnything()
+        mock_obj.Open()
+        mock_obj.Method(1).MultipleTimes().AndReturn(9)
+        mock_obj.Method(3).MultipleTimes("nr2").AndReturn(42)
+        mock_obj.Close()
+        self.mox.ReplayAll()
+
+        mock_obj.Open()
+        actual_one = mock_obj.Method(1)
+        mock_obj.Method(1)
+        actual_three = mock_obj.Method(3)
+        mock_obj.Method(3)
+        mock_obj.Close()
+
+        self.assertEqual(9, actual_one)
+        self.assertEqual(42, actual_three)
+
+        self.mox.VerifyAll()
+
+    def testMultipleTimesTwoGroupsFailure(self):
+        """Test if MultipleTimesGroup fails with a group after a
+        MultipleTimesGroup.
+        """
+        mock_obj = self.mox.CreateMockAnything()
+        mock_obj.Open()
+        mock_obj.Method(1).MultipleTimes().AndReturn(9)
+        mock_obj.Method(3).MultipleTimes("nr2").AndReturn(42)
+        mock_obj.Close()
+        self.mox.ReplayAll()
+
+        mock_obj.Open()
+        mock_obj.Method(1)
+        mock_obj.Method(1)
+        mock_obj.Method(3)
+
+        self.assertRaises(mox.UnexpectedMethodCallError, mock_obj.Method, 1)
+
+    def testWithSideEffects(self):
+        """Test side effect operations actually modify their target objects."""
+        def modifier(mutable_list):
+            mutable_list[0] = 'mutated'
+        mock_obj = self.mox.CreateMockAnything()
+        mock_obj.ConfigureInOutParameter(
+            ['original']).WithSideEffects(modifier)
+        mock_obj.WorkWithParameter(['mutated'])
+        self.mox.ReplayAll()
+
+        local_list = ['original']
+        mock_obj.ConfigureInOutParameter(local_list)
+        mock_obj.WorkWithParameter(local_list)
+
+        self.mox.VerifyAll()
+
+    def testWithSideEffectsException(self):
+        """Test side effect operations actually modify their target objects."""
+        class TestException(Exception):
+            pass
+
+        def modifier(mutable_list):
+            mutable_list[0] = 'mutated'
+        mock_obj = self.mox.CreateMockAnything()
+        method = mock_obj.ConfigureInOutParameter(['original'])
+        method.WithSideEffects(modifier).AndRaise(TestException('exception'))
+        mock_obj.WorkWithParameter(['mutated'])
+        self.mox.ReplayAll()
+
+        local_list = ['original']
+        self.assertRaises(TestException,
+                          mock_obj.ConfigureInOutParameter,
+                          local_list)
+        mock_obj.WorkWithParameter(local_list)
+
+        self.mox.VerifyAll()
+
+    def testStubOutMethod(self):
+        """Test that a method is replaced with a MockObject."""
+        test_obj = TestClass()
+        method_type = type(test_obj.OtherValidCall)
+        # Replace OtherValidCall with a mock.
+        self.mox.StubOutWithMock(test_obj, 'OtherValidCall')
+        self.assertTrue(isinstance(test_obj.OtherValidCall, mox.MockObject))
+        self.assertFalse(type(test_obj.OtherValidCall) is method_type)
+
+        test_obj.OtherValidCall().AndReturn('foo')
+        self.mox.ReplayAll()
+
+        actual = test_obj.OtherValidCall()
+
+        self.mox.VerifyAll()
+        self.mox.UnsetStubs()
+        self.assertEqual('foo', actual)
+        self.assertTrue(type(test_obj.OtherValidCall) is method_type)
+
+    def testStubOutMethod_Unbound_Comparator(self):
+        instance = TestClass()
+        self.mox.StubOutWithMock(TestClass, 'OtherValidCall')
+
+        TestClass.OtherValidCall(mox.IgnoreArg()).AndReturn('foo')
+        self.mox.ReplayAll()
+
+        actual = TestClass.OtherValidCall(instance)
+
+        self.mox.VerifyAll()
+        self.mox.UnsetStubs()
+        self.assertEqual('foo', actual)
+
+    def testStubOutMethod_Unbound_Subclass_Comparator(self):
+        self.mox.StubOutWithMock(
+            mox_helper.TestClassFromAnotherModule, 'Value')
+        mox_helper.TestClassFromAnotherModule.Value(
+            mox.IsA(mox_helper.ChildClassFromAnotherModule)).AndReturn('foo')
+        self.mox.ReplayAll()
+
+        instance = mox_helper.ChildClassFromAnotherModule()
+        actual = mox_helper.TestClassFromAnotherModule.Value(instance)
+
+        self.mox.VerifyAll()
+        self.mox.UnsetStubs()
+        self.assertEqual('foo', actual)
+
+    def testStubOuMethod_Unbound_WithOptionalParams(self):
+        self.mox = mox.Mox()
+        self.mox.StubOutWithMock(TestClass, 'OptionalArgs')
+        TestClass.OptionalArgs(mox.IgnoreArg(), foo=2)
+        self.mox.ReplayAll()
+
+        t = TestClass()
+        TestClass.OptionalArgs(t, foo=2)
+
+        self.mox.VerifyAll()
+        self.mox.UnsetStubs()
+
+    def testStubOutMethod_Unbound_ActualInstance(self):
+        instance = TestClass()
+        self.mox.StubOutWithMock(TestClass, 'OtherValidCall')
+
+        TestClass.OtherValidCall(instance).AndReturn('foo')
+        self.mox.ReplayAll()
+
+        actual = TestClass.OtherValidCall(instance)
+
+        self.mox.VerifyAll()
+        self.mox.UnsetStubs()
+        self.assertEqual('foo', actual)
+
+    def testStubOutMethod_Unbound_DifferentInstance(self):
+        instance = TestClass()
+        self.mox.StubOutWithMock(TestClass, 'OtherValidCall')
+
+        TestClass.OtherValidCall(instance).AndReturn('foo')
+        self.mox.ReplayAll()
+
+        # This should fail, since the instances are different
+        self.assertRaises(mox.UnexpectedMethodCallError,
+                          TestClass.OtherValidCall, "wrong self")
+
+        self.mox.VerifyAll()
+        self.mox.UnsetStubs()
+
+    def testStubOutMethod_Unbound_NamedUsingPositional(self):
+        """Check positional parameters can be matched to keyword arguments."""
+        self.mox.StubOutWithMock(mox_helper.ExampleClass, 'NamedParams')
+        instance = mox_helper.ExampleClass()
+        mox_helper.ExampleClass.NamedParams(instance, 'foo', baz=None)
+        self.mox.ReplayAll()
+
+        mox_helper.ExampleClass.NamedParams(instance, 'foo', baz=None)
+
+        self.mox.VerifyAll()
+        self.mox.UnsetStubs()
+
+    def testStubOutMethod_Unbound_NamedUsingPositional_SomePositional(self):
+        """Check positional parameters can be matched to keyword arguments."""
+        self.mox.StubOutWithMock(mox_helper.ExampleClass, 'TestMethod')
+        instance = mox_helper.ExampleClass()
+        mox_helper.ExampleClass.TestMethod(instance, 'one', 'two', 'nine')
+        self.mox.ReplayAll()
+
+        mox_helper.ExampleClass.TestMethod(instance, 'one', 'two', 'nine')
+
+        self.mox.VerifyAll()
+        self.mox.UnsetStubs()
+
+    def testStubOutMethod_Unbound_SpecialArgs(self):
+        self.mox.StubOutWithMock(mox_helper.ExampleClass, 'SpecialArgs')
+        instance = mox_helper.ExampleClass()
+        mox_helper.ExampleClass.SpecialArgs(instance, 'foo', None, bar='bar')
+        self.mox.ReplayAll()
+
+        mox_helper.ExampleClass.SpecialArgs(instance, 'foo', None, bar='bar')
+
+        self.mox.VerifyAll()
+        self.mox.UnsetStubs()
+
+    def testStubOutMethod_Bound_SimpleTest(self):
+        t = self.mox.CreateMock(TestClass)
+
+        t.MethodWithArgs(mox.IgnoreArg(), mox.IgnoreArg()).AndReturn('foo')
+        self.mox.ReplayAll()
+
+        actual = t.MethodWithArgs(None, None)
+
+        self.mox.VerifyAll()
+        self.mox.UnsetStubs()
+        self.assertEqual('foo', actual)
+
+    def testStubOutMethod_Bound_NamedUsingPositional(self):
+        """Check positional parameters can be matched to keyword arguments."""
+        self.mox.StubOutWithMock(mox_helper.ExampleClass, 'NamedParams')
+        instance = mox_helper.ExampleClass()
+        instance.NamedParams('foo', baz=None)
+        self.mox.ReplayAll()
+
+        instance.NamedParams('foo', baz=None)
+
+        self.mox.VerifyAll()
+        self.mox.UnsetStubs()
+
+    def testStubOutMethod_Bound_NamedUsingPositional_SomePositional(self):
+        """Check positional parameters can be matched to keyword arguments."""
+        self.mox.StubOutWithMock(mox_helper.ExampleClass, 'TestMethod')
+        instance = mox_helper.ExampleClass()
+        instance.TestMethod(instance, 'one', 'two', 'nine')
+        self.mox.ReplayAll()
+
+        instance.TestMethod(instance, 'one', 'two', 'nine')
+
+        self.mox.VerifyAll()
+        self.mox.UnsetStubs()
+
+    def testStubOutMethod_Bound_SpecialArgs(self):
+        self.mox.StubOutWithMock(mox_helper.ExampleClass, 'SpecialArgs')
+        instance = mox_helper.ExampleClass()
+        instance.SpecialArgs(instance, 'foo', None, bar='bar')
+        self.mox.ReplayAll()
+
+        instance.SpecialArgs(instance, 'foo', None, bar='bar')
+
+        self.mox.VerifyAll()
+        self.mox.UnsetStubs()
+
+    def testStubOutMethod_Func_PropgatesExceptions(self):
+        """Errors in Func comparator should propagate to the calling method."""
+        class TestException(Exception):
+            pass
+
+        def raiseExceptionOnNotOne(value):
+            if value == 1:
+                return True
+            else:
+                raise TestException
+
+        test_obj = TestClass()
+        self.mox.StubOutWithMock(test_obj, 'MethodWithArgs')
+        test_obj.MethodWithArgs(
+            mox.IgnoreArg(), mox.Func(raiseExceptionOnNotOne)).AndReturn(1)
+        test_obj.MethodWithArgs(
+            mox.IgnoreArg(), mox.Func(raiseExceptionOnNotOne)).AndReturn(1)
+        self.mox.ReplayAll()
+
+        self.assertEqual(test_obj.MethodWithArgs('ignored', 1), 1)
+        self.assertRaises(TestException,
+                          test_obj.MethodWithArgs, 'ignored', 2)
+
+        self.mox.VerifyAll()
+        self.mox.UnsetStubs()
+
+    def testStubOut_SignatureMatching_init_(self):
+        self.mox.StubOutWithMock(mox_helper.ExampleClass, '__init__')
+        mox_helper.ExampleClass.__init__(mox.IgnoreArg())
+        self.mox.ReplayAll()
+
+        # Create an instance of a child class, which calls the parent
+        # __init__
+        mox_helper.ChildExampleClass()
+
+        self.mox.VerifyAll()
+        self.mox.UnsetStubs()
+
+    # FIXME(dhellmann): Skip this test until someone can debug why it
+    # fails on python 3.4.
+
+    @testtools.skipIf(six.PY3, "This test needs to be fixed for python 3")
+    def testStubOutClass_OldStyle(self):
+        """Test a mocked class whose __init__ returns a Mock."""
+        self.mox.StubOutWithMock(mox_helper, 'TestClassFromAnotherModule')
+        self.assertTrue(isinstance(mox_helper.TestClassFromAnotherModule,
+                                   mox.MockObject))
+
+        mock_instance = self.mox.CreateMock(
+            mox_helper.TestClassFromAnotherModule)
+        mox_helper.TestClassFromAnotherModule().AndReturn(mock_instance)
+        mock_instance.Value().AndReturn('mock instance')
+
+        self.mox.ReplayAll()
+
+        a_mock = mox_helper.TestClassFromAnotherModule()
+        actual = a_mock.Value()
+
+        self.mox.VerifyAll()
+        self.mox.UnsetStubs()
+        self.assertEqual('mock instance', actual)
+
+    def testStubOutClass(self):
+        self.mox.StubOutClassWithMocks(mox_helper, 'CallableClass')
+
+        # Instance one
+        mock_one = mox_helper.CallableClass(1, 2)
+        mock_one.Value().AndReturn('mock')
+
+        # Instance two
+        mock_two = mox_helper.CallableClass(8, 9)
+        mock_two('one').AndReturn('called mock')
+
+        self.mox.ReplayAll()
+
+        one = mox_helper.CallableClass(1, 2)
+        actual_one = one.Value()
+
+        two = mox_helper.CallableClass(8, 9)
+        actual_two = two('one')
+
+        self.mox.VerifyAll()
+        self.mox.UnsetStubs()
+
+        # Verify the correct mocks were returned
+        self.assertEqual(mock_one, one)
+        self.assertEqual(mock_two, two)
+
+        # Verify
+        self.assertEqual('mock', actual_one)
+        self.assertEqual('called mock', actual_two)
+
+    def testStubOutClass_NotAClass(self):
+        self.assertRaises(TypeError, self.mox.StubOutClassWithMocks,
+                          mox_helper, 'MyTestFunction')
+
+    def testStubOutClassNotEnoughCreated(self):
+        self.mox.StubOutClassWithMocks(mox_helper, 'CallableClass')
+
+        mox_helper.CallableClass(1, 2)
+        mox_helper.CallableClass(8, 9)
+
+        self.mox.ReplayAll()
+        mox_helper.CallableClass(1, 2)
+
+        self.assertRaises(mox.ExpectedMockCreationError, self.mox.VerifyAll)
+        self.mox.UnsetStubs()
+
+    def testStubOutClassWrongSignature(self):
+        self.mox.StubOutClassWithMocks(mox_helper, 'CallableClass')
+
+        self.assertRaises(AttributeError, mox_helper.CallableClass)
+
+        self.mox.UnsetStubs()
+
+    def testStubOutClassWrongParameters(self):
+        self.mox.StubOutClassWithMocks(mox_helper, 'CallableClass')
+
+        mox_helper.CallableClass(1, 2)
+
+        self.mox.ReplayAll()
+
+        self.assertRaises(mox.UnexpectedMethodCallError,
+                          mox_helper.CallableClass, 8, 9)
+        self.mox.UnsetStubs()
+
+    def testStubOutClassTooManyCreated(self):
+        self.mox.StubOutClassWithMocks(mox_helper, 'CallableClass')
+
+        mox_helper.CallableClass(1, 2)
+
+        self.mox.ReplayAll()
+        mox_helper.CallableClass(1, 2)
+        self.assertRaises(mox.UnexpectedMockCreationError,
+                          mox_helper.CallableClass, 8, 9)
+
+        self.mox.UnsetStubs()
+
+    def testWarnsUserIfMockingMock(self):
+        """Test that user is warned if they try to stub out a MockAnything."""
+        self.mox.StubOutWithMock(TestClass, 'MyStaticMethod')
+        self.assertRaises(TypeError, self.mox.StubOutWithMock, TestClass,
+                          'MyStaticMethod')
+
+    def testStubOutFirstClassMethodVerifiesSignature(self):
+        self.mox.StubOutWithMock(mox_helper, 'MyTestFunction')
+
+        # Wrong number of arguments
+        self.assertRaises(AttributeError, mox_helper.MyTestFunction, 1)
+        self.mox.UnsetStubs()
+
+    def _testMethodSignatureVerification(self, stubClass):
+        # If stubClass is true, the test is run against an a stubbed out class,
+        # else the test is run against a stubbed out instance.
+        if stubClass:
+            self.mox.StubOutWithMock(mox_helper.ExampleClass, "TestMethod")
+            obj = mox_helper.ExampleClass()
+        else:
+            obj = mox_helper.ExampleClass()
+            self.mox.StubOutWithMock(mox_helper.ExampleClass, "TestMethod")
+        self.assertRaises(AttributeError, obj.TestMethod)
+        self.assertRaises(AttributeError, obj.TestMethod, 1)
+        self.assertRaises(AttributeError, obj.TestMethod, nine=2)
+        obj.TestMethod(1, 2)
+        obj.TestMethod(1, 2, 3)
+        obj.TestMethod(1, 2, nine=3)
+        self.assertRaises(AttributeError, obj.TestMethod, 1, 2, 3, 4)
+        self.mox.UnsetStubs()
+
+    def testStubOutClassMethodVerifiesSignature(self):
+        self._testMethodSignatureVerification(stubClass=True)
+
+    def testStubOutObjectMethodVerifiesSignature(self):
+        self._testMethodSignatureVerification(stubClass=False)
+
+    def testStubOutObject(self):
+        """Test than object is replaced with a Mock."""
+
+        class Foo(object):
+            def __init__(self):
+                self.obj = TestClass()
+
+        foo = Foo()
+        self.mox.StubOutWithMock(foo, "obj")
+        self.assertTrue(isinstance(foo.obj, mox.MockObject))
+        foo.obj.ValidCall()
+        self.mox.ReplayAll()
+
+        foo.obj.ValidCall()
+
+        self.mox.VerifyAll()
+        self.mox.UnsetStubs()
+        self.assertFalse(isinstance(foo.obj, mox.MockObject))
+
+    def testForgotReplayHelpfulMessage(self):
+        """If there is an AttributeError on a MockMethod, give helpful msg."""
+        foo = self.mox.CreateMockAnything()
+        bar = self.mox.CreateMockAnything()
+        foo.GetBar().AndReturn(bar)
+        bar.ShowMeTheMoney()
+        # Forgot to replay!
+        try:
+            foo.GetBar().ShowMeTheMoney()
+        except AttributeError as e:
+            self.assertEqual(
+                'MockMethod has no attribute "ShowMeTheMoney". '
+                'Did you remember to put your mocks in replay mode?', str(e))
+
+
+class ReplayTest(testtools.TestCase):
+    """Verify Replay works properly."""
+
+    def testReplay(self):
+        """Replay should put objects into replay mode."""
+        mock_obj = mox.MockObject(TestClass)
+        self.assertFalse(mock_obj._replay_mode)
+        mox.Replay(mock_obj)
+        self.assertTrue(mock_obj._replay_mode)
+
+
+class MoxTestBaseTest(testtools.TestCase):
+    """Verify that all tests in class derived from MoxTestBase are wrapped."""
+
+    def setUp(self):
+        super(MoxTestBaseTest, self).setUp()
+        self.mox = mox.Mox()
+        self.addCleanup(self.mox.UnsetStubs)
+        self.test_mox = mox.Mox()
+        self.addCleanup(self.test_mox.UnsetStubs)
+        self.test_stubs = mox.stubout.StubOutForTesting()
+        self.addCleanup(self.test_stubs.UnsetAll)
+        self.addCleanup(self.test_stubs.SmartUnsetAll)
+        self.result = testtools.TestResult()
+
+    def _setUpTestClass(self):
+        """Replacement for setUp in the test class instance.
+
+        Assigns a mox.Mox instance as the mox attribute of the test instance.
+        Replacement Mox instance is under our control before setUp is called
+        in the test class instance.
+        """
+        self.test.mox = self.test_mox
+        self.test.stubs = self.test_stubs
+
+    def _CreateTest(self, test_name):
+        """Create a test from our example mox class.
+
+        The created test instance is assigned to this instances test attribute.
+        """
+        self.test = mox_helper.ExampleMoxTest(test_name)
+        self.mox.stubs.Set(self.test, 'setUp', self._setUpTestClass)
+
+    def _VerifySuccess(self):
+        """Run the checks to confirm test method completed successfully."""
+        self.mox.StubOutWithMock(self.test_mox, 'UnsetStubs')
+        self.mox.StubOutWithMock(self.test_mox, 'VerifyAll')
+        self.mox.StubOutWithMock(self.test_stubs, 'UnsetAll')
+        self.mox.StubOutWithMock(self.test_stubs, 'SmartUnsetAll')
+        self.test_mox.UnsetStubs()
+        self.test_mox.VerifyAll()
+        self.test_stubs.UnsetAll()
+        self.test_stubs.SmartUnsetAll()
+        self.mox.ReplayAll()
+        self.test.run(result=self.result)
+        self.assertTrue(self.result.wasSuccessful())
+        self.mox.VerifyAll()
+        self.mox.UnsetStubs()    # Needed to call the real VerifyAll() below.
+        self.test_mox.VerifyAll()
+
+    def testSuccess(self):
+        """Successful test method execution test."""
+        self._CreateTest('testSuccess')
+        self._VerifySuccess()
+
+    def testSuccessNoMocks(self):
+        """testSuccess() unsets all the mocks. Vverify they've been unset."""
+        self._CreateTest('testSuccess')
+        self.test.run(result=self.result)
+        self.assertTrue(self.result.wasSuccessful())
+        self.assertEqual(OS_LISTDIR, mox_helper.os.listdir)
+
+    def testStubs(self):
+        """Test that "self.stubs" is provided as is useful."""
+        self._CreateTest('testHasStubs')
+        self._VerifySuccess()
+
+    def testStubsNoMocks(self):
+        """Let testHasStubs() unset the stubs by itself."""
+        self._CreateTest('testHasStubs')
+        self.test.run(result=self.result)
+        self.assertTrue(self.result.wasSuccessful())
+        self.assertEqual(OS_LISTDIR, mox_helper.os.listdir)
+
+    def testExpectedNotCalled(self):
+        """Stubbed out method is not called."""
+        self._CreateTest('testExpectedNotCalled')
+        self.mox.StubOutWithMock(self.test_mox, 'UnsetStubs')
+        self.mox.StubOutWithMock(self.test_stubs, 'UnsetAll')
+        self.mox.StubOutWithMock(self.test_stubs, 'SmartUnsetAll')
+        # Don't stub out VerifyAll - that's what causes the test to fail
+        self.test_mox.UnsetStubs()
+        self.test_stubs.UnsetAll()
+        self.test_stubs.SmartUnsetAll()
+        self.mox.ReplayAll()
+        self.test.run(result=self.result)
+        self.assertFalse(self.result.wasSuccessful())
+        self.mox.VerifyAll()
+
+    def testExpectedNotCalledNoMocks(self):
+        """Let testExpectedNotCalled() unset all the mocks by itself."""
+        self._CreateTest('testExpectedNotCalled')
+        self.test.run(result=self.result)
+        self.assertFalse(self.result.wasSuccessful())
+        self.assertEqual(OS_LISTDIR, mox_helper.os.listdir)
+
+    def testUnexpectedCall(self):
+        """Stubbed out method is called with unexpected arguments."""
+        self._CreateTest('testUnexpectedCall')
+        self.mox.StubOutWithMock(self.test_mox, 'UnsetStubs')
+        self.mox.StubOutWithMock(self.test_stubs, 'UnsetAll')
+        self.mox.StubOutWithMock(self.test_stubs, 'SmartUnsetAll')
+        # Ensure no calls are made to VerifyAll()
+        self.mox.StubOutWithMock(self.test_mox, 'VerifyAll')
+        self.test_mox.UnsetStubs()
+        self.test_stubs.UnsetAll()
+        self.test_stubs.SmartUnsetAll()
+        self.mox.ReplayAll()
+        self.test.run(result=self.result)
+        self.assertFalse(self.result.wasSuccessful())
+        self.mox.VerifyAll()
+
+    def testFailure(self):
+        """Failing assertion in test method."""
+        self._CreateTest('testFailure')
+        self.mox.StubOutWithMock(self.test_mox, 'UnsetStubs')
+        self.mox.StubOutWithMock(self.test_stubs, 'UnsetAll')
+        self.mox.StubOutWithMock(self.test_stubs, 'SmartUnsetAll')
+        # Ensure no calls are made to VerifyAll()
+        self.mox.StubOutWithMock(self.test_mox, 'VerifyAll')
+        self.test_mox.UnsetStubs()
+        self.test_stubs.UnsetAll()
+        self.test_stubs.SmartUnsetAll()
+        self.mox.ReplayAll()
+        self.test.run(result=self.result)
+        self.assertFalse(self.result.wasSuccessful())
+        self.mox.VerifyAll()
+
+    def testMixin(self):
+        """Run test from mix-in test class, ensure it passes."""
+        self._CreateTest('testStat')
+        self._VerifySuccess()
+
+    def testMixinAgain(self):
+        """Run same test as above but from the current test class.
+
+        Ensures metaclass properly wrapped test methods from all base classes.
+        If unsetting of stubs doesn't happen, this will fail.
+        """
+        self._CreateTest('testStatOther')
+        self._VerifySuccess()
+
+
+class VerifyTest(testtools.TestCase):
+    """Verify Verify works properly."""
+
+    def testVerify(self):
+        """Verify should be called for all objects.
+
+        Should throw an exception because the expected behavior did not occur.
+        """
+        mock_obj = mox.MockObject(TestClass)
+        mock_obj.ValidCall()
+        mock_obj._Replay()
+        self.assertRaises(mox.ExpectedMethodCallsError, mox.Verify, mock_obj)
+
+
+class ResetTest(testtools.TestCase):
+    """Verify Reset works properly."""
+
+    def testReset(self):
+        """Should empty all queues and put mocks in record mode."""
+        mock_obj = mox.MockObject(TestClass)
+        mock_obj.ValidCall()
+        self.assertFalse(mock_obj._replay_mode)
+        mock_obj._Replay()
+        self.assertTrue(mock_obj._replay_mode)
+        self.assertEqual(1, len(mock_obj._expected_calls_queue))
+
+        mox.Reset(mock_obj)
+        self.assertFalse(mock_obj._replay_mode)
+        self.assertEqual(0, len(mock_obj._expected_calls_queue))
+
+
+class MyTestCase(testtools.TestCase):
+    """Simulate the use of a fake wrapper around Python's unittest library."""
+
+    def setUp(self):
+        super(MyTestCase, self).setUp()
+        self.critical_variable = 42
+        self.another_critical_variable = 42
+
+    def testMethodOverride(self):
+        """Should be properly overriden in a derived class."""
+        self.assertEqual(42, self.another_critical_variable)
+        self.another_critical_variable += 1
+
+
+class MoxTestBaseMultipleInheritanceTest(mox.MoxTestBase, MyTestCase):
+    """Test that multiple inheritance can be used with MoxTestBase."""
+
+    def setUp(self):
+        super(MoxTestBaseMultipleInheritanceTest, self).setUp()
+        self.another_critical_variable = 99
+
+    def testMultipleInheritance(self):
+        """Should be able to access members created by all parent setUp()."""
+        self.assertTrue(isinstance(self.mox, mox.Mox))
+        self.assertEqual(42, self.critical_variable)
+
+    def testMethodOverride(self):
+        """Should run before MyTestCase.testMethodOverride."""
+        self.assertEqual(99, self.another_critical_variable)
+        self.another_critical_variable = 42
+        super(MoxTestBaseMultipleInheritanceTest, self).testMethodOverride()
+        self.assertEqual(43, self.another_critical_variable)
+
+
+class MoxTestDontMockProperties(MoxTestBaseTest):
+        def testPropertiesArentMocked(self):
+                mock_class = self.mox.CreateMock(ClassWithProperties)
+                self.assertRaises(mox.UnknownMethodCallError,
+                                  lambda: mock_class.prop_attr)
+
+
+class TestClass(object):
+    """This class is used only for testing the mock framework."""
+
+    SOME_CLASS_VAR = "test_value"
+    _PROTECTED_CLASS_VAR = "protected value"
+
+    def __init__(self, ivar=None):
+        self.__ivar = ivar
+
+    def __eq__(self, rhs):
+        return self.__ivar == rhs
+
+    def __ne__(self, rhs):
+        return not self.__eq__(rhs)
+
+    def ValidCall(self):
+        pass
+
+    def MethodWithArgs(self, one, two, nine=None):
+        pass
+
+    def OtherValidCall(self):
+        pass
+
+    def OptionalArgs(self, foo='boom'):
+        pass
+
+    def ValidCallWithArgs(self, *args, **kwargs):
+        pass
+
+    @classmethod
+    def MyClassMethod(cls):
+        pass
+
+    @staticmethod
+    def MyStaticMethod():
+        pass
+
+    def _ProtectedCall(self):
+        pass
+
+    def __PrivateCall(self):
+        pass
+
+    def __DoNotMock(self):
+        pass
+
+    def __getitem__(self, key):
+        """Return the value for key."""
+        return self.d[key]
+
+    def __setitem__(self, key, value):
+        """Set the value for key to value."""
+        self.d[key] = value
+
+    def __contains__(self, key):
+        """Returns True if d contains the key."""
+        return key in self.d
+
+    def __iter__(self):
+        pass
+
+
+class ChildClass(TestClass):
+    """This inherits from TestClass."""
+    def __init__(self):
+        TestClass.__init__(self)
+
+    def ChildValidCall(self):
+        pass
+
+
+class CallableClass(object):
+    """This class is callable, and that should be mockable!"""
+
+    def __init__(self):
+        pass
+
+    def __call__(self, param):
+        return param
+
+
+class ClassWithProperties(object):
+        def setter_attr(self, value):
+                pass
+
+        def getter_attr(self):
+                pass
+
+        prop_attr = property(getter_attr, setter_attr)
+
+
+class SubscribtableNonIterableClass(object):
+    def __getitem__(self, index):
+        raise IndexError
+
+
+class InheritsFromCallable(CallableClass):
+    """This class should be mockable; it inherits from a callable class."""
+
+    pass
+
+
+if __name__ == '__main__':
+    testtools.main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/mox3/tests/test_stubout.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/mox3/tests/test_stubout.py
new file mode 100644
index 0000000..4a04170
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/mox3/tests/test_stubout.py
@@ -0,0 +1,49 @@
+# Unit tests for stubout.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# This is a fork of the pymox library intended to work with Python 3.
+# The file was modified by quermit@gmail.com and dawid.fatyga@gmail.com
+
+import fixtures
+import testtools
+
+from mox3 import mox
+from mox3 import stubout
+from mox3.tests import stubout_helper
+
+
+class StubOutForTestingTest(testtools.TestCase):
+    def setUp(self):
+        super(StubOutForTestingTest, self).setUp()
+        self.mox = mox.Mox()
+        self.useFixture(fixtures.MonkeyPatch(
+            'mox3.tests.stubout_helper.SampleFunction',
+            stubout_helper.SampleFunction))
+
+    def testSmartSetOnModule(self):
+        mock_function = self.mox.CreateMockAnything()
+        mock_function()
+
+        stubber = stubout.StubOutForTesting()
+        stubber.SmartSet(stubout_helper, 'SampleFunction', mock_function)
+
+        self.mox.ReplayAll()
+
+        stubout_helper.SampleFunction()
+
+        self.mox.VerifyAll()
+
+
+if __name__ == '__main__':
+    testtools.main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/requirements.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/requirements.txt
new file mode 100644
index 0000000..d52427f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/requirements.txt
@@ -0,0 +1,6 @@
+# The order of packages is significant, because pip processes them in the order
+# of appearance. Changing the order has an impact on the overall integration
+# process, which may cause wedges in the gate later.
+pbr<2.0,>=1.6
+
+fixtures>=1.3.1
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/setup.cfg b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/setup.cfg
new file mode 100644
index 0000000..4a3de06
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/setup.cfg
@@ -0,0 +1,27 @@
+[metadata]
+name = mox3
+summary = Mock object framework for Python
+description-file =
+    README.rst
+author = OpenStack
+author-email = openstack-dev@lists.openstack.org
+home-page = http://www.openstack.org/
+classifiers =
+    Environment :: OpenStack
+    Programming Language :: Python
+    License :: OSI Approved :: Apache Software License
+    Programming Language :: Python :: 2.6
+    Programming Language :: Python :: 2.7
+    Programming Language :: Python :: 3
+    Operating System :: OS Independent
+    Development Status :: 4 - Beta
+    Intended Audience :: Developers
+    Topic :: Software Development :: Testing
+
+[files]
+packages =
+    mox3
+
+[global]
+setup-hooks =
+    pbr.hooks.setup_hook
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/setup.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/setup.py
new file mode 100644
index 0000000..d8080d0
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/setup.py
@@ -0,0 +1,29 @@
+# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT
+import setuptools
+
+# In python < 2.7.4, a lazy loading of package `pbr` will break
+# setuptools if some other modules registered functions in `atexit`.
+# solution from: http://bugs.python.org/issue15881#msg170215
+try:
+    import multiprocessing  # noqa
+except ImportError:
+    pass
+
+setuptools.setup(
+    setup_requires=['pbr>=1.3'],
+    pbr=True)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/test-requirements.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/test-requirements.txt
new file mode 100644
index 0000000..22f6480
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/test-requirements.txt
@@ -0,0 +1,22 @@
+# The order of packages is significant, because pip processes them in the order
+# of appearance. Changing the order has an impact on the overall integration
+# process, which may cause wedges in the gate later.
+# this file lists dependencies required for the testing of heat
+
+# Install bounded pep8/pyflakes first, then let flake8 install
+pep8==1.5.7
+pyflakes==0.8.1
+flake8<=2.4.1,>=2.2.4
+
+coverage>=3.6
+discover
+python-subunit>=0.0.18
+testrepository>=0.0.18
+testtools>=1.4.0
+
+six>=1.9.0
+
+# this is required for the docs build jobs
+sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2
+oslosphinx>=2.5.0 # Apache-2.0
+
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/tox.ini b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/tox.ini
new file mode 100644
index 0000000..eea97fc
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/mox3/tox.ini
@@ -0,0 +1,28 @@
+[tox]
+envlist = py34,py27,pep8
+
+[testenv]
+setenv = VIRTUAL_ENV={envdir}
+deps = -r{toxinidir}/requirements.txt
+       -r{toxinidir}/test-requirements.txt
+commands =
+  python setup.py testr --slowest --testr-args='{posargs}'
+
+[testenv:docs]
+commands = python setup.py build_sphinx
+
+[testenv:pep8]
+commands = flake8
+
+[testenv:venv]
+commands = {posargs}
+
+[testenv:cover]
+setenv = VIRTUAL_ENV={envdir}
+commands =
+  python setup.py testr --coverage
+
+[flake8]
+show-source = true
+builtins = _
+exclude=.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/png/README.chromium b/sdk/platform-tools/systrace/catapult/telemetry/third_party/png/README.chromium
new file mode 100644
index 0000000..d838ece
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/png/README.chromium
@@ -0,0 +1,17 @@
+Name: Pure Python PNG Reader/Writer
+Short Name: pypng
+URL: https://github.com/drj11/pypng/
+Version: 0
+Date: 2009-03-11
+Revision: dd1797c361eafa443878b0915f767b75bd518d3b
+License: MIT
+License File: NOT_SHIPPED
+Security Critical: no
+
+Description:
+A png encoder and decoder for python. Used by telemetry to decode screenshots
+captured via the gpuBenchmark.windowSnapshot API, which are returned as
+Base64-encoded PNG files.
+
+Local Modifications:
+None.
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/png/png.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/png/png.py
new file mode 100755
index 0000000..b55dd3a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/png/png.py
@@ -0,0 +1,3857 @@
+#!/usr/bin/env python
+
+# $URL$
+# $Rev$
+
+# png.py - PNG encoder/decoder in pure Python
+#
+# Copyright (C) 2006 Johann C. Rocholl <johann@browsershots.org>
+# Portions Copyright (C) 2009 David Jones <drj@pobox.com>
+# And probably portions Copyright (C) 2006 Nicko van Someren <nicko@nicko.org>
+#
+# Original concept by Johann C. Rocholl.
+#
+# LICENSE (The MIT License)
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation files
+# (the "Software"), to deal in the Software without restriction,
+# including without limitation the rights to use, copy, modify, merge,
+# publish, distribute, sublicense, and/or sell copies of the Software,
+# and to permit persons to whom the Software is furnished to do so,
+# subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+#
+# Changelog (recent first):
+# 2009-03-11 David: interlaced bit depth < 8 (writing).
+# 2009-03-10 David: interlaced bit depth < 8 (reading).
+# 2009-03-04 David: Flat and Boxed pixel formats.
+# 2009-02-26 David: Palette support (writing).
+# 2009-02-23 David: Bit-depths < 8; better PNM support.
+# 2006-06-17 Nicko: Reworked into a class, faster interlacing.
+# 2006-06-17 Johann: Very simple prototype PNG decoder.
+# 2006-06-17 Nicko: Test suite with various image generators.
+# 2006-06-17 Nicko: Alpha-channel, grey-scale, 16-bit/plane support.
+# 2006-06-15 Johann: Scanline iterator interface for large input files.
+# 2006-06-09 Johann: Very simple prototype PNG encoder.
+
+# Incorporated into Bangai-O Development Tools by drj on 2009-02-11 from
+# http://trac.browsershots.org/browser/trunk/pypng/lib/png.py?rev=2885
+
+# Incorporated into pypng by drj on 2009-03-12 from
+# //depot/prj/bangaio/master/code/png.py#67
+
+
+"""
+Pure Python PNG Reader/Writer
+
+This Python module implements support for PNG images (see PNG
+specification at http://www.w3.org/TR/2003/REC-PNG-20031110/ ). It reads
+and writes PNG files with all allowable bit depths (1/2/4/8/16/24/32/48/64
+bits per pixel) and colour combinations: greyscale (1/2/4/8/16 bit); RGB,
+RGBA, LA (greyscale with alpha) with 8/16 bits per channel; colour mapped
+images (1/2/4/8 bit).  Adam7 interlacing is supported for reading and
+writing.  A number of optional chunks can be specified (when writing)
+and understood (when reading): ``tRNS``, ``bKGD``, ``gAMA``.
+
+For help, type ``import png; help(png)`` in your python interpreter.
+
+A good place to start is the :class:`Reader` and :class:`Writer` classes.
+
+Requires Python 2.3.  Limited support is available for Python 2.2, but
+not everything works.  Best with Python 2.4 and higher.  Installation is
+trivial, but see the ``README.txt`` file (with the source distribution)
+for details.
+
+This file can also be used as a command-line utility to convert
+`Netpbm <http://netpbm.sourceforge.net/>`_ PNM files to PNG, and the reverse conversion from PNG to
+PNM. The interface is similar to that of the ``pnmtopng`` program from
+Netpbm.  Type ``python png.py --help`` at the shell prompt
+for usage and a list of options.
+
+A note on spelling and terminology
+----------------------------------
+
+Generally British English spelling is used in the documentation.  So
+that's "greyscale" and "colour".  This not only matches the author's
+native language, it's also used by the PNG specification.
+
+The major colour models supported by PNG (and hence by PyPNG) are:
+greyscale, RGB, greyscale--alpha, RGB--alpha.  These are sometimes
+referred to using the abbreviations: L, RGB, LA, RGBA.  In this case
+each letter abbreviates a single channel: *L* is for Luminance or Luma or
+Lightness which is the channel used in greyscale images; *R*, *G*, *B* stand
+for Red, Green, Blue, the components of a colour image; *A* stands for
+Alpha, the opacity channel (used for transparency effects, but higher
+values are more opaque, so it makes sense to call it opacity).
+
+A note on formats
+-----------------
+
+When getting pixel data out of this module (reading) and presenting
+data to this module (writing) there are a number of ways the data could
+be represented as a Python value.  Generally this module uses one of
+three formats called "flat row flat pixel", "boxed row flat pixel", and
+"boxed row boxed pixel".  Basically the concern is whether each pixel
+and each row comes in its own little tuple (box), or not.
+
+Consider an image that is 3 pixels wide by 2 pixels high, and each pixel
+has RGB components:
+
+Boxed row flat pixel::
+
+  list([R,G,B, R,G,B, R,G,B],
+       [R,G,B, R,G,B, R,G,B])
+
+Each row appears as its own list, but the pixels are flattened so that
+three values for one pixel simply follow the three values for the previous
+pixel.  This is the most common format used, because it provides a good
+compromise between space and convenience.  PyPNG regards itself as
+at liberty to replace any sequence type with any sufficiently compatible
+other sequence type; in practice each row is an array (from the array
+module), and the outer list is sometimes an iterator rather than an
+explicit list (so that streaming is possible).
+
+Flat row flat pixel::
+
+  [R,G,B, R,G,B, R,G,B,
+   R,G,B, R,G,B, R,G,B]
+
+The entire image is one single giant sequence of colour values.
+Generally an array will be used (to save space), not a list.
+
+Boxed row boxed pixel::
+
+  list([ (R,G,B), (R,G,B), (R,G,B) ],
+       [ (R,G,B), (R,G,B), (R,G,B) ])
+
+Each row appears in its own list, but each pixel also appears in its own
+tuple.  A serious memory burn in Python.
+
+In all cases the top row comes first, and for each row the pixels are
+ordered from left-to-right.  Within a pixel the values appear in the
+order, R-G-B-A (or L-A for greyscale--alpha).
+
+There is a fourth format, mentioned because it is used internally,
+is close to what lies inside a PNG file itself, and has some support
+from the public API.  This format is called packed.  When packed,
+each row is a sequence of bytes (integers from 0 to 255), just as
+it is before PNG scanline filtering is applied.  When the bit depth
+is 8 this is essentially the same as boxed row flat pixel; when the
+bit depth is less than 8, several pixels are packed into each byte;
+when the bit depth is 16 (the only value more than 8 that is supported
+by the PNG image format) each pixel value is decomposed into 2 bytes
+(and `packed` is a misnomer).  This format is used by the
+:meth:`Writer.write_packed` method.  It isn't usually a convenient
+format, but may be just right if the source data for the PNG image
+comes from something that uses a similar format (for example, 1-bit
+BMPs, or another PNG file).
+
+And now, my famous members
+--------------------------
+"""
+
+# http://www.python.org/doc/2.2.3/whatsnew/node5.html
+from __future__ import generators
+
+__version__ = "$URL$ $Rev$"
+
+from array import array
+try: # See :pyver:old
+    import itertools
+except:
+    pass
+import math
+# http://www.python.org/doc/2.4.4/lib/module-operator.html
+import operator
+import struct
+import sys
+import zlib
+# http://www.python.org/doc/2.4.4/lib/module-warnings.html
+import warnings
+try:
+    import pyximport
+    pyximport.install()
+    import cpngfilters as pngfilters
+except ImportError:
+    pass
+
+
+__all__ = ['Image', 'Reader', 'Writer', 'write_chunks', 'from_array']
+
+
+# The PNG signature.
+# http://www.w3.org/TR/PNG/#5PNG-file-signature
+_signature = struct.pack('8B', 137, 80, 78, 71, 13, 10, 26, 10)
+
+_adam7 = ((0, 0, 8, 8),
+          (4, 0, 8, 8),
+          (0, 4, 4, 8),
+          (2, 0, 4, 4),
+          (0, 2, 2, 4),
+          (1, 0, 2, 2),
+          (0, 1, 1, 2))
+
+def group(s, n):
+    # See
+    # http://www.python.org/doc/2.6/library/functions.html#zip
+    return zip(*[iter(s)]*n)
+
+def isarray(x):
+    """Same as ``isinstance(x, array)`` except on Python 2.2, where it
+    always returns ``False``.  This helps PyPNG work on Python 2.2.
+    """
+
+    try:
+        return isinstance(x, array)
+    except:
+        return False
+
+try:  # see :pyver:old
+    array.tostring
+except:
+    def tostring(row):
+        l = len(row)
+        return struct.pack('%dB' % l, *row)
+else:
+    def tostring(row):
+        """Convert row of bytes to string.  Expects `row` to be an
+        ``array``.
+        """
+        return row.tostring()
+
+# Conditionally convert to bytes.  Works on Python 2 and Python 3.
+try:
+    bytes('', 'ascii')
+    def strtobytes(x): return bytes(x, 'iso8859-1')
+    def bytestostr(x): return str(x, 'iso8859-1')
+except:
+    strtobytes = str
+    bytestostr = str
+
+def interleave_planes(ipixels, apixels, ipsize, apsize):
+    """
+    Interleave (colour) planes, e.g. RGB + A = RGBA.
+
+    Return an array of pixels consisting of the `ipsize` elements of data
+    from each pixel in `ipixels` followed by the `apsize` elements of data
+    from each pixel in `apixels`.  Conventionally `ipixels` and
+    `apixels` are byte arrays so the sizes are bytes, but it actually
+    works with any arrays of the same type.  The returned array is the
+    same type as the input arrays which should be the same type as each other.
+    """
+
+    itotal = len(ipixels)
+    atotal = len(apixels)
+    newtotal = itotal + atotal
+    newpsize = ipsize + apsize
+    # Set up the output buffer
+    # See http://www.python.org/doc/2.4.4/lib/module-array.html#l2h-1356
+    out = array(ipixels.typecode)
+    # It's annoying that there is no cheap way to set the array size :-(
+    out.extend(ipixels)
+    out.extend(apixels)
+    # Interleave in the pixel data
+    for i in range(ipsize):
+        out[i:newtotal:newpsize] = ipixels[i:itotal:ipsize]
+    for i in range(apsize):
+        out[i+ipsize:newtotal:newpsize] = apixels[i:atotal:apsize]
+    return out
+
+def check_palette(palette):
+    """Check a palette argument (to the :class:`Writer` class) for validity.
+    Returns the palette as a list if okay; raises an exception otherwise.
+    """
+
+    # None is the default and is allowed.
+    if palette is None:
+        return None
+
+    p = list(palette)
+    if not (0 < len(p) <= 256):
+        raise ValueError("a palette must have between 1 and 256 entries")
+    seen_triple = False
+    for i,t in enumerate(p):
+        if len(t) not in (3,4):
+            raise ValueError(
+              "palette entry %d: entries must be 3- or 4-tuples." % i)
+        if len(t) == 3:
+            seen_triple = True
+        if seen_triple and len(t) == 4:
+            raise ValueError(
+              "palette entry %d: all 4-tuples must precede all 3-tuples" % i)
+        for x in t:
+            if int(x) != x or not(0 <= x <= 255):
+                raise ValueError(
+                  "palette entry %d: values must be integer: 0 <= x <= 255" % i)
+    return p
+
+class Error(Exception):
+    prefix = 'Error'
+    def __str__(self):
+        return self.prefix + ': ' + ' '.join(self.args)
+
+class FormatError(Error):
+    """Problem with input file format.  In other words, PNG file does
+    not conform to the specification in some way and is invalid.
+    """
+
+    prefix = 'FormatError'
+
+class ChunkError(FormatError):
+    prefix = 'ChunkError'
+
+
+class Writer:
+    """
+    PNG encoder in pure Python.
+    """
+
+    def __init__(self, width=None, height=None,
+                 size=None,
+                 greyscale=False,
+                 alpha=False,
+                 bitdepth=8,
+                 palette=None,
+                 transparent=None,
+                 background=None,
+                 gamma=None,
+                 compression=None,
+                 interlace=False,
+                 bytes_per_sample=None, # deprecated
+                 planes=None,
+                 colormap=None,
+                 maxval=None,
+                 chunk_limit=2**20):
+        """
+        Create a PNG encoder object.
+
+        Arguments:
+
+        width, height
+          Image size in pixels, as two separate arguments.
+        size
+          Image size (w,h) in pixels, as single argument.
+        greyscale
+          Input data is greyscale, not RGB.
+        alpha
+          Input data has alpha channel (RGBA or LA).
+        bitdepth
+          Bit depth: from 1 to 16.
+        palette
+          Create a palette for a colour mapped image (colour type 3).
+        transparent
+          Specify a transparent colour (create a ``tRNS`` chunk).
+        background
+          Specify a default background colour (create a ``bKGD`` chunk).
+        gamma
+          Specify a gamma value (create a ``gAMA`` chunk).
+        compression
+          zlib compression level: 0 (none) to 9 (more compressed); default: -1 or None.
+        interlace
+          Create an interlaced image.
+        chunk_limit
+          Write multiple ``IDAT`` chunks to save memory.
+
+        The image size (in pixels) can be specified either by using the
+        `width` and `height` arguments, or with the single `size`
+        argument.  If `size` is used it should be a pair (*width*,
+        *height*).
+
+        `greyscale` and `alpha` are booleans that specify whether
+        an image is greyscale (or colour), and whether it has an
+        alpha channel (or not).
+
+        `bitdepth` specifies the bit depth of the source pixel values.
+        Each source pixel value must be an integer between 0 and
+        ``2**bitdepth-1``.  For example, 8-bit images have values
+        between 0 and 255.  PNG only stores images with bit depths of
+        1,2,4,8, or 16.  When `bitdepth` is not one of these values,
+        the next highest valid bit depth is selected, and an ``sBIT``
+        (significant bits) chunk is generated that specifies the original
+        precision of the source image.  In this case the supplied pixel
+        values will be rescaled to fit the range of the selected bit depth.
+
+        The details of which bit depth / colour model combinations the
+        PNG file format supports directly, are somewhat arcane
+        (refer to the PNG specification for full details).  Briefly:
+        "small" bit depths (1,2,4) are only allowed with greyscale and
+        colour mapped images; colour mapped images cannot have bit depth
+        16.
+
+        For colour mapped images (in other words, when the `palette`
+        argument is specified) the `bitdepth` argument must match one of
+        the valid PNG bit depths: 1, 2, 4, or 8.  (It is valid to have a
+        PNG image with a palette and an ``sBIT`` chunk, but the meaning
+        is slightly different; it would be awkward to press the
+        `bitdepth` argument into service for this.)
+
+        The `palette` option, when specified, causes a colour mapped image
+        to be created: the PNG colour type is set to 3; greyscale
+        must not be set; alpha must not be set; transparent must
+        not be set; the bit depth must be 1,2,4, or 8.  When a colour
+        mapped image is created, the pixel values are palette indexes
+        and the `bitdepth` argument specifies the size of these indexes
+        (not the size of the colour values in the palette).
+
+        The palette argument value should be a sequence of 3- or
+        4-tuples.  3-tuples specify RGB palette entries; 4-tuples
+        specify RGBA palette entries.  If both 4-tuples and 3-tuples
+        appear in the sequence then all the 4-tuples must come
+        before all the 3-tuples.  A ``PLTE`` chunk is created; if there
+        are 4-tuples then a ``tRNS`` chunk is created as well.  The
+        ``PLTE`` chunk will contain all the RGB triples in the same
+        sequence; the ``tRNS`` chunk will contain the alpha channel for
+        all the 4-tuples, in the same sequence.  Palette entries
+        are always 8-bit.
+
+        If specified, the `transparent` and `background` parameters must
+        be a tuple with three integer values for red, green, blue, or
+        a simple integer (or singleton tuple) for a greyscale image.
+
+        If specified, the `gamma` parameter must be a positive number
+        (generally, a float).  A ``gAMA`` chunk will be created.  Note that
+        this will not change the values of the pixels as they appear in
+        the PNG file, they are assumed to have already been converted
+        appropriately for the gamma specified.
+
+        The `compression` argument specifies the compression level to
+        be used by the ``zlib`` module.  Values from 1 to 9 specify
+        compression, with 9 being "more compressed" (usually smaller
+        and slower, but it doesn't always work out that way).  0 means
+        no compression.  -1 and ``None`` both mean that the default
+        level of compession will be picked by the ``zlib`` module
+        (which is generally acceptable).
+
+        If `interlace` is true then an interlaced image is created
+        (using PNG's so far only interace method, *Adam7*).  This does not
+        affect how the pixels should be presented to the encoder, rather
+        it changes how they are arranged into the PNG file.  On slow
+        connexions interlaced images can be partially decoded by the
+        browser to give a rough view of the image that is successively
+        refined as more image data appears.
+        
+        .. note ::
+        
+          Enabling the `interlace` option requires the entire image
+          to be processed in working memory.
+
+        `chunk_limit` is used to limit the amount of memory used whilst
+        compressing the image.  In order to avoid using large amounts of
+        memory, multiple ``IDAT`` chunks may be created.
+        """
+
+        # At the moment the `planes` argument is ignored;
+        # its purpose is to act as a dummy so that
+        # ``Writer(x, y, **info)`` works, where `info` is a dictionary
+        # returned by Reader.read and friends.
+        # Ditto for `colormap`.
+
+        # A couple of helper functions come first.  Best skipped if you
+        # are reading through.
+
+        def isinteger(x):
+            try:
+                return int(x) == x
+            except:
+                return False
+
+        def check_color(c, which):
+            """Checks that a colour argument for transparent or
+            background options is the right form.  Also "corrects" bare
+            integers to 1-tuples.
+            """
+
+            if c is None:
+                return c
+            if greyscale:
+                try:
+                    l = len(c)
+                except TypeError:
+                    c = (c,)
+                if len(c) != 1:
+                    raise ValueError("%s for greyscale must be 1-tuple" %
+                        which)
+                if not isinteger(c[0]):
+                    raise ValueError(
+                        "%s colour for greyscale must be integer" %
+                        which)
+            else:
+                if not (len(c) == 3 and
+                        isinteger(c[0]) and
+                        isinteger(c[1]) and
+                        isinteger(c[2])):
+                    raise ValueError(
+                        "%s colour must be a triple of integers" %
+                        which)
+            return c
+
+        if size:
+            if len(size) != 2:
+                raise ValueError(
+                  "size argument should be a pair (width, height)")
+            if width is not None and width != size[0]:
+                raise ValueError(
+                  "size[0] (%r) and width (%r) should match when both are used."
+                    % (size[0], width))
+            if height is not None and height != size[1]:
+                raise ValueError(
+                  "size[1] (%r) and height (%r) should match when both are used."
+                    % (size[1], height))
+            width,height = size
+        del size
+
+        if width <= 0 or height <= 0:
+            raise ValueError("width and height must be greater than zero")
+        if not isinteger(width) or not isinteger(height):
+            raise ValueError("width and height must be integers")
+        # http://www.w3.org/TR/PNG/#7Integers-and-byte-order
+        if width > 2**32-1 or height > 2**32-1:
+            raise ValueError("width and height cannot exceed 2**32-1")
+
+        if alpha and transparent is not None:
+            raise ValueError(
+                "transparent colour not allowed with alpha channel")
+
+        if bytes_per_sample is not None:
+            warnings.warn('please use bitdepth instead of bytes_per_sample',
+                          DeprecationWarning)
+            if bytes_per_sample not in (0.125, 0.25, 0.5, 1, 2):
+                raise ValueError(
+                    "bytes per sample must be .125, .25, .5, 1, or 2")
+            bitdepth = int(8*bytes_per_sample)
+        del bytes_per_sample
+        if not isinteger(bitdepth) or bitdepth < 1 or 16 < bitdepth:
+            raise ValueError("bitdepth (%r) must be a postive integer <= 16" %
+              bitdepth)
+
+        self.rescale = None
+        if palette:
+            if bitdepth not in (1,2,4,8):
+                raise ValueError("with palette, bitdepth must be 1, 2, 4, or 8")
+            if transparent is not None:
+                raise ValueError("transparent and palette not compatible")
+            if alpha:
+                raise ValueError("alpha and palette not compatible")
+            if greyscale:
+                raise ValueError("greyscale and palette not compatible")
+        else:
+            # No palette, check for sBIT chunk generation.
+            if alpha or not greyscale:
+                if bitdepth not in (8,16):
+                    targetbitdepth = (8,16)[bitdepth > 8]
+                    self.rescale = (bitdepth, targetbitdepth)
+                    bitdepth = targetbitdepth
+                    del targetbitdepth
+            else:
+                assert greyscale
+                assert not alpha
+                if bitdepth not in (1,2,4,8,16):
+                    if bitdepth > 8:
+                        targetbitdepth = 16
+                    elif bitdepth == 3:
+                        targetbitdepth = 4
+                    else:
+                        assert bitdepth in (5,6,7)
+                        targetbitdepth = 8
+                    self.rescale = (bitdepth, targetbitdepth)
+                    bitdepth = targetbitdepth
+                    del targetbitdepth
+
+        if bitdepth < 8 and (alpha or not greyscale and not palette):
+            raise ValueError(
+              "bitdepth < 8 only permitted with greyscale or palette")
+        if bitdepth > 8 and palette:
+            raise ValueError(
+                "bit depth must be 8 or less for images with palette")
+
+        transparent = check_color(transparent, 'transparent')
+        background = check_color(background, 'background')
+
+        # It's important that the true boolean values (greyscale, alpha,
+        # colormap, interlace) are converted to bool because Iverson's
+        # convention is relied upon later on.
+        self.width = width
+        self.height = height
+        self.transparent = transparent
+        self.background = background
+        self.gamma = gamma
+        self.greyscale = bool(greyscale)
+        self.alpha = bool(alpha)
+        self.colormap = bool(palette)
+        self.bitdepth = int(bitdepth)
+        self.compression = compression
+        self.chunk_limit = chunk_limit
+        self.interlace = bool(interlace)
+        self.palette = check_palette(palette)
+
+        self.color_type = 4*self.alpha + 2*(not greyscale) + 1*self.colormap
+        assert self.color_type in (0,2,3,4,6)
+
+        self.color_planes = (3,1)[self.greyscale or self.colormap]
+        self.planes = self.color_planes + self.alpha
+        # :todo: fix for bitdepth < 8
+        self.psize = (self.bitdepth/8) * self.planes
+
+    def make_palette(self):
+        """Create the byte sequences for a ``PLTE`` and if necessary a
+        ``tRNS`` chunk.  Returned as a pair (*p*, *t*).  *t* will be
+        ``None`` if no ``tRNS`` chunk is necessary.
+        """
+
+        p = array('B')
+        t = array('B')
+
+        for x in self.palette:
+            p.extend(x[0:3])
+            if len(x) > 3:
+                t.append(x[3])
+        p = tostring(p)
+        t = tostring(t)
+        if t:
+            return p,t
+        return p,None
+
+    def write(self, outfile, rows):
+        """Write a PNG image to the output file.  `rows` should be
+        an iterable that yields each row in boxed row flat pixel format.
+        The rows should be the rows of the original image, so there
+        should be ``self.height`` rows of ``self.width * self.planes`` values.
+        If `interlace` is specified (when creating the instance), then
+        an interlaced PNG file will be written.  Supply the rows in the
+        normal image order; the interlacing is carried out internally.
+        
+        .. note ::
+
+          Interlacing will require the entire image to be in working memory.
+        """
+
+        if self.interlace:
+            fmt = 'BH'[self.bitdepth > 8]
+            a = array(fmt, itertools.chain(*rows))
+            return self.write_array(outfile, a)
+        else:
+            nrows = self.write_passes(outfile, rows)
+            if nrows != self.height:
+                raise ValueError(
+                  "rows supplied (%d) does not match height (%d)" %
+                  (nrows, self.height))
+
+    def write_passes(self, outfile, rows, packed=False):
+        """
+        Write a PNG image to the output file.
+
+        Most users are expected to find the :meth:`write` or
+        :meth:`write_array` method more convenient.
+        
+        The rows should be given to this method in the order that
+        they appear in the output file.  For straightlaced images,
+        this is the usual top to bottom ordering, but for interlaced
+        images the rows should have already been interlaced before
+        passing them to this function.
+
+        `rows` should be an iterable that yields each row.  When
+        `packed` is ``False`` the rows should be in boxed row flat pixel
+        format; when `packed` is ``True`` each row should be a packed
+        sequence of bytes.
+
+        """
+
+        # http://www.w3.org/TR/PNG/#5PNG-file-signature
+        outfile.write(_signature)
+
+        # http://www.w3.org/TR/PNG/#11IHDR
+        write_chunk(outfile, 'IHDR',
+                    struct.pack("!2I5B", self.width, self.height,
+                                self.bitdepth, self.color_type,
+                                0, 0, self.interlace))
+
+        # See :chunk:order
+        # http://www.w3.org/TR/PNG/#11gAMA
+        if self.gamma is not None:
+            write_chunk(outfile, 'gAMA',
+                        struct.pack("!L", int(round(self.gamma*1e5))))
+
+        # See :chunk:order
+        # http://www.w3.org/TR/PNG/#11sBIT
+        if self.rescale:
+            write_chunk(outfile, 'sBIT',
+                struct.pack('%dB' % self.planes,
+                            *[self.rescale[0]]*self.planes))
+        
+        # :chunk:order: Without a palette (PLTE chunk), ordering is
+        # relatively relaxed.  With one, gAMA chunk must precede PLTE
+        # chunk which must precede tRNS and bKGD.
+        # See http://www.w3.org/TR/PNG/#5ChunkOrdering
+        if self.palette:
+            p,t = self.make_palette()
+            write_chunk(outfile, 'PLTE', p)
+            if t:
+                # tRNS chunk is optional.  Only needed if palette entries
+                # have alpha.
+                write_chunk(outfile, 'tRNS', t)
+
+        # http://www.w3.org/TR/PNG/#11tRNS
+        if self.transparent is not None:
+            if self.greyscale:
+                write_chunk(outfile, 'tRNS',
+                            struct.pack("!1H", *self.transparent))
+            else:
+                write_chunk(outfile, 'tRNS',
+                            struct.pack("!3H", *self.transparent))
+
+        # http://www.w3.org/TR/PNG/#11bKGD
+        if self.background is not None:
+            if self.greyscale:
+                write_chunk(outfile, 'bKGD',
+                            struct.pack("!1H", *self.background))
+            else:
+                write_chunk(outfile, 'bKGD',
+                            struct.pack("!3H", *self.background))
+
+        # http://www.w3.org/TR/PNG/#11IDAT
+        if self.compression is not None:
+            compressor = zlib.compressobj(self.compression)
+        else:
+            compressor = zlib.compressobj()
+
+        # Choose an extend function based on the bitdepth.  The extend
+        # function packs/decomposes the pixel values into bytes and
+        # stuffs them onto the data array.
+        data = array('B')
+        if self.bitdepth == 8 or packed:
+            extend = data.extend
+        elif self.bitdepth == 16:
+            # Decompose into bytes
+            def extend(sl):
+                fmt = '!%dH' % len(sl)
+                data.extend(array('B', struct.pack(fmt, *sl)))
+        else:
+            # Pack into bytes
+            assert self.bitdepth < 8
+            # samples per byte
+            spb = int(8/self.bitdepth)
+            def extend(sl):
+                a = array('B', sl)
+                # Adding padding bytes so we can group into a whole
+                # number of spb-tuples.
+                l = float(len(a))
+                extra = math.ceil(l / float(spb))*spb - l
+                a.extend([0]*int(extra))
+                # Pack into bytes
+                l = group(a, spb)
+                l = map(lambda e: reduce(lambda x,y:
+                                           (x << self.bitdepth) + y, e), l)
+                data.extend(l)
+        if self.rescale:
+            oldextend = extend
+            factor = \
+              float(2**self.rescale[1]-1) / float(2**self.rescale[0]-1)
+            def extend(sl):
+                oldextend(map(lambda x: int(round(factor*x)), sl))
+
+        # Build the first row, testing mostly to see if we need to
+        # changed the extend function to cope with NumPy integer types
+        # (they cause our ordinary definition of extend to fail, so we
+        # wrap it).  See
+        # http://code.google.com/p/pypng/issues/detail?id=44
+        enumrows = enumerate(rows)
+        del rows
+
+        # First row's filter type.
+        data.append(0)
+        # :todo: Certain exceptions in the call to ``.next()`` or the
+        # following try would indicate no row data supplied.
+        # Should catch.
+        i,row = enumrows.next()
+        try:
+            # If this fails...
+            extend(row)
+        except:
+            # ... try a version that converts the values to int first.
+            # Not only does this work for the (slightly broken) NumPy
+            # types, there are probably lots of other, unknown, "nearly"
+            # int types it works for.
+            def wrapmapint(f):
+                return lambda sl: f(map(int, sl))
+            extend = wrapmapint(extend)
+            del wrapmapint
+            extend(row)
+
+        for i,row in enumrows:
+            # Add "None" filter type.  Currently, it's essential that
+            # this filter type be used for every scanline as we do not
+            # mark the first row of a reduced pass image; that means we
+            # could accidentally compute the wrong filtered scanline if
+            # we used "up", "average", or "paeth" on such a line.
+            data.append(0)
+            extend(row)
+            if len(data) > self.chunk_limit:
+                compressed = compressor.compress(tostring(data))
+                if len(compressed):
+                    # print >> sys.stderr, len(data), len(compressed)
+                    write_chunk(outfile, 'IDAT', compressed)
+                # Because of our very witty definition of ``extend``,
+                # above, we must re-use the same ``data`` object.  Hence
+                # we use ``del`` to empty this one, rather than create a
+                # fresh one (which would be my natural FP instinct).
+                del data[:]
+        if len(data):
+            compressed = compressor.compress(tostring(data))
+        else:
+            compressed = ''
+        flushed = compressor.flush()
+        if len(compressed) or len(flushed):
+            # print >> sys.stderr, len(data), len(compressed), len(flushed)
+            write_chunk(outfile, 'IDAT', compressed + flushed)
+        # http://www.w3.org/TR/PNG/#11IEND
+        write_chunk(outfile, 'IEND')
+        return i+1
+
+    def write_array(self, outfile, pixels):
+        """
+        Write an array in flat row flat pixel format as a PNG file on
+        the output file.  See also :meth:`write` method.
+        """
+
+        if self.interlace:
+            self.write_passes(outfile, self.array_scanlines_interlace(pixels))
+        else:
+            self.write_passes(outfile, self.array_scanlines(pixels))
+
+    def write_packed(self, outfile, rows):
+        """
+        Write PNG file to `outfile`.  The pixel data comes from `rows`
+        which should be in boxed row packed format.  Each row should be
+        a sequence of packed bytes.
+
+        Technically, this method does work for interlaced images but it
+        is best avoided.  For interlaced images, the rows should be
+        presented in the order that they appear in the file.
+
+        This method should not be used when the source image bit depth
+        is not one naturally supported by PNG; the bit depth should be
+        1, 2, 4, 8, or 16.
+        """
+
+        if self.rescale:
+            raise Error("write_packed method not suitable for bit depth %d" %
+              self.rescale[0])
+        return self.write_passes(outfile, rows, packed=True)
+
+    def convert_pnm(self, infile, outfile):
+        """
+        Convert a PNM file containing raw pixel data into a PNG file
+        with the parameters set in the writer object.  Works for
+        (binary) PGM, PPM, and PAM formats.
+        """
+
+        if self.interlace:
+            pixels = array('B')
+            pixels.fromfile(infile,
+                            (self.bitdepth/8) * self.color_planes *
+                            self.width * self.height)
+            self.write_passes(outfile, self.array_scanlines_interlace(pixels))
+        else:
+            self.write_passes(outfile, self.file_scanlines(infile))
+
+    def convert_ppm_and_pgm(self, ppmfile, pgmfile, outfile):
+        """
+        Convert a PPM and PGM file containing raw pixel data into a
+        PNG outfile with the parameters set in the writer object.
+        """
+        pixels = array('B')
+        pixels.fromfile(ppmfile,
+                        (self.bitdepth/8) * self.color_planes *
+                        self.width * self.height)
+        apixels = array('B')
+        apixels.fromfile(pgmfile,
+                         (self.bitdepth/8) *
+                         self.width * self.height)
+        pixels = interleave_planes(pixels, apixels,
+                                   (self.bitdepth/8) * self.color_planes,
+                                   (self.bitdepth/8))
+        if self.interlace:
+            self.write_passes(outfile, self.array_scanlines_interlace(pixels))
+        else:
+            self.write_passes(outfile, self.array_scanlines(pixels))
+
+    def file_scanlines(self, infile):
+        """
+        Generates boxed rows in flat pixel format, from the input file
+        `infile`.  It assumes that the input file is in a "Netpbm-like"
+        binary format, and is positioned at the beginning of the first
+        pixel.  The number of pixels to read is taken from the image
+        dimensions (`width`, `height`, `planes`) and the number of bytes
+        per value is implied by the image `bitdepth`.
+        """
+
+        # Values per row
+        vpr = self.width * self.planes
+        row_bytes = vpr
+        if self.bitdepth > 8:
+            assert self.bitdepth == 16
+            row_bytes *= 2
+            fmt = '>%dH' % vpr
+            def line():
+                return array('H', struct.unpack(fmt, infile.read(row_bytes)))
+        else:
+            def line():
+                scanline = array('B', infile.read(row_bytes))
+                return scanline
+        for y in range(self.height):
+            yield line()
+
+    def array_scanlines(self, pixels):
+        """
+        Generates boxed rows (flat pixels) from flat rows (flat pixels)
+        in an array.
+        """
+
+        # Values per row
+        vpr = self.width * self.planes
+        stop = 0
+        for y in range(self.height):
+            start = stop
+            stop = start + vpr
+            yield pixels[start:stop]
+
+    def array_scanlines_interlace(self, pixels):
+        """
+        Generator for interlaced scanlines from an array.  `pixels` is
+        the full source image in flat row flat pixel format.  The
+        generator yields each scanline of the reduced passes in turn, in
+        boxed row flat pixel format.
+        """
+
+        # http://www.w3.org/TR/PNG/#8InterlaceMethods
+        # Array type.
+        fmt = 'BH'[self.bitdepth > 8]
+        # Value per row
+        vpr = self.width * self.planes
+        for xstart, ystart, xstep, ystep in _adam7:
+            if xstart >= self.width:
+                continue
+            # Pixels per row (of reduced image)
+            ppr = int(math.ceil((self.width-xstart)/float(xstep)))
+            # number of values in reduced image row.
+            row_len = ppr*self.planes
+            for y in range(ystart, self.height, ystep):
+                if xstep == 1:
+                    offset = y * vpr
+                    yield pixels[offset:offset+vpr]
+                else:
+                    row = array(fmt)
+                    # There's no easier way to set the length of an array
+                    row.extend(pixels[0:row_len])
+                    offset = y * vpr + xstart * self.planes
+                    end_offset = (y+1) * vpr
+                    skip = self.planes * xstep
+                    for i in range(self.planes):
+                        row[i::self.planes] = \
+                            pixels[offset+i:end_offset:skip]
+                    yield row
+
+def write_chunk(outfile, tag, data=strtobytes('')):
+    """
+    Write a PNG chunk to the output file, including length and
+    checksum.
+    """
+
+    # http://www.w3.org/TR/PNG/#5Chunk-layout
+    outfile.write(struct.pack("!I", len(data)))
+    tag = strtobytes(tag)
+    outfile.write(tag)
+    outfile.write(data)
+    checksum = zlib.crc32(tag)
+    checksum = zlib.crc32(data, checksum)
+    checksum &= 2**32-1
+    outfile.write(struct.pack("!I", checksum))
+
+def write_chunks(out, chunks):
+    """Create a PNG file by writing out the chunks."""
+
+    out.write(_signature)
+    for chunk in chunks:
+        write_chunk(out, *chunk)
+
+def filter_scanline(type, line, fo, prev=None):
+    """Apply a scanline filter to a scanline.  `type` specifies the
+    filter type (0 to 4); `line` specifies the current (unfiltered)
+    scanline as a sequence of bytes; `prev` specifies the previous
+    (unfiltered) scanline as a sequence of bytes. `fo` specifies the
+    filter offset; normally this is size of a pixel in bytes (the number
+    of bytes per sample times the number of channels), but when this is
+    < 1 (for bit depths < 8) then the filter offset is 1.
+    """
+
+    assert 0 <= type < 5
+
+    # The output array.  Which, pathetically, we extend one-byte at a
+    # time (fortunately this is linear).
+    out = array('B', [type])
+
+    def sub():
+        ai = -fo
+        for x in line:
+            if ai >= 0:
+                x = (x - line[ai]) & 0xff
+            out.append(x)
+            ai += 1
+    def up():
+        for i,x in enumerate(line):
+            x = (x - prev[i]) & 0xff
+            out.append(x)
+    def average():
+        ai = -fo
+        for i,x in enumerate(line):
+            if ai >= 0:
+                x = (x - ((line[ai] + prev[i]) >> 1)) & 0xff
+            else:
+                x = (x - (prev[i] >> 1)) & 0xff
+            out.append(x)
+            ai += 1
+    def paeth():
+        # http://www.w3.org/TR/PNG/#9Filter-type-4-Paeth
+        ai = -fo # also used for ci
+        for i,x in enumerate(line):
+            a = 0
+            b = prev[i]
+            c = 0
+
+            if ai >= 0:
+                a = line[ai]
+                c = prev[ai]
+            p = a + b - c
+            pa = abs(p - a)
+            pb = abs(p - b)
+            pc = abs(p - c)
+            if pa <= pb and pa <= pc: Pr = a
+            elif pb <= pc: Pr = b
+            else: Pr = c
+
+            x = (x - Pr) & 0xff
+            out.append(x)
+            ai += 1
+
+    if not prev:
+        # We're on the first line.  Some of the filters can be reduced
+        # to simpler cases which makes handling the line "off the top"
+        # of the image simpler.  "up" becomes "none"; "paeth" becomes
+        # "left" (non-trivial, but true). "average" needs to be handled
+        # specially.
+        if type == 2: # "up"
+            return line # type = 0
+        elif type == 3:
+            prev = [0]*len(line)
+        elif type == 4: # "paeth"
+            type = 1
+    if type == 0:
+        out.extend(line)
+    elif type == 1:
+        sub()
+    elif type == 2:
+        up()
+    elif type == 3:
+        average()
+    else: # type == 4
+        paeth()
+    return out
+
+
+def from_array(a, mode=None, info={}):
+    """Create a PNG :class:`Image` object from a 2- or 3-dimensional array.
+    One application of this function is easy PIL-style saving:
+    ``png.from_array(pixels, 'L').save('foo.png')``.
+
+    .. note :
+
+      The use of the term *3-dimensional* is for marketing purposes
+      only.  It doesn't actually work.  Please bear with us.  Meanwhile
+      enjoy the complimentary snacks (on request) and please use a
+      2-dimensional array.
+    
+    Unless they are specified using the *info* parameter, the PNG's
+    height and width are taken from the array size.  For a 3 dimensional
+    array the first axis is the height; the second axis is the width;
+    and the third axis is the channel number.  Thus an RGB image that is
+    16 pixels high and 8 wide will use an array that is 16x8x3.  For 2
+    dimensional arrays the first axis is the height, but the second axis
+    is ``width*channels``, so an RGB image that is 16 pixels high and 8
+    wide will use a 2-dimensional array that is 16x24 (each row will be
+    8*3==24 sample values).
+
+    *mode* is a string that specifies the image colour format in a
+    PIL-style mode.  It can be:
+
+    ``'L'``
+      greyscale (1 channel)
+    ``'LA'``
+      greyscale with alpha (2 channel)
+    ``'RGB'``
+      colour image (3 channel)
+    ``'RGBA'``
+      colour image with alpha (4 channel)
+
+    The mode string can also specify the bit depth (overriding how this
+    function normally derives the bit depth, see below).  Appending
+    ``';16'`` to the mode will cause the PNG to be 16 bits per channel;
+    any decimal from 1 to 16 can be used to specify the bit depth.
+
+    When a 2-dimensional array is used *mode* determines how many
+    channels the image has, and so allows the width to be derived from
+    the second array dimension.
+
+    The array is expected to be a ``numpy`` array, but it can be any
+    suitable Python sequence.  For example, a list of lists can be used:
+    ``png.from_array([[0, 255, 0], [255, 0, 255]], 'L')``.  The exact
+    rules are: ``len(a)`` gives the first dimension, height;
+    ``len(a[0])`` gives the second dimension; ``len(a[0][0])`` gives the
+    third dimension, unless an exception is raised in which case a
+    2-dimensional array is assumed.  It's slightly more complicated than
+    that because an iterator of rows can be used, and it all still
+    works.  Using an iterator allows data to be streamed efficiently.
+
+    The bit depth of the PNG is normally taken from the array element's
+    datatype (but if *mode* specifies a bitdepth then that is used
+    instead).  The array element's datatype is determined in a way which
+    is supposed to work both for ``numpy`` arrays and for Python
+    ``array.array`` objects.  A 1 byte datatype will give a bit depth of
+    8, a 2 byte datatype will give a bit depth of 16.  If the datatype
+    does not have an implicit size, for example it is a plain Python
+    list of lists, as above, then a default of 8 is used.
+
+    The *info* parameter is a dictionary that can be used to specify
+    metadata (in the same style as the arguments to the
+    :class:``png.Writer`` class).  For this function the keys that are
+    useful are:
+    
+    height
+      overrides the height derived from the array dimensions and allows
+      *a* to be an iterable.
+    width
+      overrides the width derived from the array dimensions.
+    bitdepth
+      overrides the bit depth derived from the element datatype (but
+      must match *mode* if that also specifies a bit depth).
+
+    Generally anything specified in the
+    *info* dictionary will override any implicit choices that this
+    function would otherwise make, but must match any explicit ones.
+    For example, if the *info* dictionary has a ``greyscale`` key then
+    this must be true when mode is ``'L'`` or ``'LA'`` and false when
+    mode is ``'RGB'`` or ``'RGBA'``.
+    """
+
+    # We abuse the *info* parameter by modifying it.  Take a copy here.
+    # (Also typechecks *info* to some extent).
+    info = dict(info)
+
+    # Syntax check mode string.
+    bitdepth = None
+    try:
+        mode = mode.split(';')
+        if len(mode) not in (1,2):
+            raise Error()
+        if mode[0] not in ('L', 'LA', 'RGB', 'RGBA'):
+            raise Error()
+        if len(mode) == 2:
+            try:
+                bitdepth = int(mode[1])
+            except:
+                raise Error()
+    except Error:
+        raise Error("mode string should be 'RGB' or 'L;16' or similar.")
+    mode = mode[0]
+
+    # Get bitdepth from *mode* if possible.
+    if bitdepth:
+        if info.get('bitdepth') and bitdepth != info['bitdepth']:
+            raise Error("mode bitdepth (%d) should match info bitdepth (%d)." %
+              (bitdepth, info['bitdepth']))
+        info['bitdepth'] = bitdepth
+
+    # Fill in and/or check entries in *info*.
+    # Dimensions.
+    if 'size' in info:
+        # Check width, height, size all match where used.
+        for dimension,axis in [('width', 0), ('height', 1)]:
+            if dimension in info:
+                if info[dimension] != info['size'][axis]:
+                    raise Error(
+                      "info[%r] shhould match info['size'][%r]." %
+                      (dimension, axis))
+        info['width'],info['height'] = info['size']
+    if 'height' not in info:
+        try:
+            l = len(a)
+        except:
+            raise Error(
+              "len(a) does not work, supply info['height'] instead.")
+        info['height'] = l
+    # Colour format.
+    if 'greyscale' in info:
+        if bool(info['greyscale']) != ('L' in mode):
+            raise Error("info['greyscale'] should match mode.")
+    info['greyscale'] = 'L' in mode
+    if 'alpha' in info:
+        if bool(info['alpha']) != ('A' in mode):
+            raise Error("info['alpha'] should match mode.")
+    info['alpha'] = 'A' in mode
+
+    planes = len(mode)
+    if 'planes' in info:
+        if info['planes'] != planes:
+            raise Error("info['planes'] should match mode.")
+
+    # In order to work out whether we the array is 2D or 3D we need its
+    # first row, which requires that we take a copy of its iterator.
+    # We may also need the first row to derive width and bitdepth.
+    a,t = itertools.tee(a)
+    row = t.next()
+    del t
+    try:
+        row[0][0]
+        threed = True
+        testelement = row[0]
+    except:
+        threed = False
+        testelement = row
+    if 'width' not in info:
+        if threed:
+            width = len(row)
+        else:
+            width = len(row) // planes
+        info['width'] = width
+
+    # Not implemented yet
+    assert not threed
+
+    if 'bitdepth' not in info:
+        try:
+            dtype = testelement.dtype
+            # goto the "else:" clause.  Sorry.
+        except:
+            try:
+                # Try a Python array.array.
+                bitdepth = 8 * testelement.itemsize
+            except:
+                # We can't determine it from the array element's
+                # datatype, use a default of 8.
+                bitdepth = 8
+        else:
+            # If we got here without exception, we now assume that
+            # the array is a numpy array.
+            if dtype.kind == 'b':
+                bitdepth = 1
+            else:
+                bitdepth = 8 * dtype.itemsize
+        info['bitdepth'] = bitdepth
+
+    for thing in 'width height bitdepth greyscale alpha'.split():
+        assert thing in info
+    return Image(a, info)
+
+# So that refugee's from PIL feel more at home.  Not documented.
+fromarray = from_array
+
+class Image:
+    """A PNG image.
+    You can create an :class:`Image` object from an array of pixels by calling
+    :meth:`png.from_array`.  It can be saved to disk with the
+    :meth:`save` method."""
+    def __init__(self, rows, info):
+        """
+        .. note ::
+        
+          The constructor is not public.  Please do not call it.
+        """
+        
+        self.rows = rows
+        self.info = info
+
+    def save(self, file):
+        """Save the image to *file*.  If *file* looks like an open file
+        descriptor then it is used, otherwise it is treated as a
+        filename and a fresh file is opened.
+
+        In general, you can only call this method once; after it has
+        been called the first time and the PNG image has been saved, the
+        source data will have been streamed, and cannot be streamed
+        again.
+        """
+
+        w = Writer(**self.info)
+
+        try:
+            file.write
+            def close(): pass
+        except:
+            file = open(file, 'wb')
+            def close(): file.close()
+
+        try:
+            w.write(file, self.rows)
+        finally:
+            close()
+
+class _readable:
+    """
+    A simple file-like interface for strings and arrays.
+    """
+
+    def __init__(self, buf):
+        self.buf = buf
+        self.offset = 0
+
+    def read(self, n):
+        r = self.buf[self.offset:self.offset+n]
+        if isarray(r):
+            r = r.tostring()
+        self.offset += n
+        return r
+
+
+class Reader:
+    """
+    PNG decoder in pure Python.
+    """
+
+    def __init__(self, _guess=None, **kw):
+        """
+        Create a PNG decoder object.
+
+        The constructor expects exactly one keyword argument. If you
+        supply a positional argument instead, it will guess the input
+        type. You can choose among the following keyword arguments:
+
+        filename
+          Name of input file (a PNG file).
+        file
+          A file-like object (object with a read() method).
+        bytes
+          ``array`` or ``string`` with PNG data.
+
+        """
+        if ((_guess is not None and len(kw) != 0) or
+            (_guess is None and len(kw) != 1)):
+            raise TypeError("Reader() takes exactly 1 argument")
+
+        # Will be the first 8 bytes, later on.  See validate_signature.
+        self.signature = None
+        self.transparent = None
+        # A pair of (len,type) if a chunk has been read but its data and
+        # checksum have not (in other words the file position is just
+        # past the 4 bytes that specify the chunk type).  See preamble
+        # method for how this is used.
+        self.atchunk = None
+
+        if _guess is not None:
+            if isarray(_guess):
+                kw["bytes"] = _guess
+            elif isinstance(_guess, str):
+                kw["filename"] = _guess
+            elif isinstance(_guess, file):
+                kw["file"] = _guess
+
+        if "filename" in kw:
+            self.file = open(kw["filename"], "rb")
+        elif "file" in kw:
+            self.file = kw["file"]
+        elif "bytes" in kw:
+            self.file = _readable(kw["bytes"])
+        else:
+            raise TypeError("expecting filename, file or bytes array")
+
+
+    def chunk(self, seek=None, lenient=False):
+        """
+        Read the next PNG chunk from the input file; returns a
+        (*type*,*data*) tuple.  *type* is the chunk's type as a string
+        (all PNG chunk types are 4 characters long).  *data* is the
+        chunk's data content, as a string.
+
+        If the optional `seek` argument is
+        specified then it will keep reading chunks until it either runs
+        out of file or finds the type specified by the argument.  Note
+        that in general the order of chunks in PNGs is unspecified, so
+        using `seek` can cause you to miss chunks.
+
+        If the optional `lenient` argument evaluates to True,
+        checksum failures will raise warnings rather than exceptions.
+        """
+
+        self.validate_signature()
+
+        while True:
+            # http://www.w3.org/TR/PNG/#5Chunk-layout
+            if not self.atchunk:
+                self.atchunk = self.chunklentype()
+            length,type = self.atchunk
+            self.atchunk = None
+            data = self.file.read(length)
+            if len(data) != length:
+                raise ChunkError('Chunk %s too short for required %i octets.'
+                  % (type, length))
+            checksum = self.file.read(4)
+            if len(checksum) != 4:
+                raise ValueError('Chunk %s too short for checksum.', tag)
+            if seek and type != seek:
+                continue
+            verify = zlib.crc32(strtobytes(type))
+            verify = zlib.crc32(data, verify)
+            # Whether the output from zlib.crc32 is signed or not varies
+            # according to hideous implementation details, see
+            # http://bugs.python.org/issue1202 .
+            # We coerce it to be positive here (in a way which works on
+            # Python 2.3 and older).
+            verify &= 2**32 - 1
+            verify = struct.pack('!I', verify)
+            if checksum != verify:
+                # print repr(checksum)
+                (a, ) = struct.unpack('!I', checksum)
+                (b, ) = struct.unpack('!I', verify)
+                message = "Checksum error in %s chunk: 0x%08X != 0x%08X." % (type, a, b)
+                if lenient:
+                    warnings.warn(message, RuntimeWarning)
+                else:
+                    raise ChunkError(message)
+            return type, data
+
+    def chunks(self):
+        """Return an iterator that will yield each chunk as a
+        (*chunktype*, *content*) pair.
+        """
+
+        while True:
+            t,v = self.chunk()
+            yield t,v
+            if t == 'IEND':
+                break
+
+    def undo_filter(self, filter_type, scanline, previous):
+        """Undo the filter for a scanline.  `scanline` is a sequence of
+        bytes that does not include the initial filter type byte.
+        `previous` is decoded previous scanline (for straightlaced
+        images this is the previous pixel row, but for interlaced
+        images, it is the previous scanline in the reduced image, which
+        in general is not the previous pixel row in the final image).
+        When there is no previous scanline (the first row of a
+        straightlaced image, or the first row in one of the passes in an
+        interlaced image), then this argument should be ``None``.
+
+        The scanline will have the effects of filtering removed, and the
+        result will be returned as a fresh sequence of bytes.
+        """
+
+        # :todo: Would it be better to update scanline in place?
+        # Yes, with the Cython extension making the undo_filter fast,
+        # updating scanline inplace makes the code 3 times faster
+        # (reading 50 images of 800x800 went from 40s to 16s)
+        result = scanline
+
+        if filter_type == 0:
+            return result
+
+        if filter_type not in (1,2,3,4):
+            raise FormatError('Invalid PNG Filter Type.'
+              '  See http://www.w3.org/TR/2003/REC-PNG-20031110/#9Filters .')
+
+        # Filter unit.  The stride from one pixel to the corresponding
+        # byte from the previous previous.  Normally this is the pixel
+        # size in bytes, but when this is smaller than 1, the previous
+        # byte is used instead.
+        fu = max(1, self.psize)
+
+        # For the first line of a pass, synthesize a dummy previous
+        # line.  An alternative approach would be to observe that on the
+        # first line 'up' is the same as 'null', 'paeth' is the same
+        # as 'sub', with only 'average' requiring any special case.
+        if not previous:
+            previous = array('B', [0]*len(scanline))
+
+        def sub():
+            """Undo sub filter."""
+
+            ai = 0
+            # Loops starts at index fu.  Observe that the initial part
+            # of the result is already filled in correctly with
+            # scanline.
+            for i in range(fu, len(result)):
+                x = scanline[i]
+                a = result[ai]
+                result[i] = (x + a) & 0xff
+                ai += 1
+
+        def up():
+            """Undo up filter."""
+
+            for i in range(len(result)):
+                x = scanline[i]
+                b = previous[i]
+                result[i] = (x + b) & 0xff
+
+        def average():
+            """Undo average filter."""
+
+            ai = -fu
+            for i in range(len(result)):
+                x = scanline[i]
+                if ai < 0:
+                    a = 0
+                else:
+                    a = result[ai]
+                b = previous[i]
+                result[i] = (x + ((a + b) >> 1)) & 0xff
+                ai += 1
+
+        def paeth():
+            """Undo Paeth filter."""
+
+            # Also used for ci.
+            ai = -fu
+            for i in range(len(result)):
+                x = scanline[i]
+                if ai < 0:
+                    a = c = 0
+                else:
+                    a = result[ai]
+                    c = previous[ai]
+                b = previous[i]
+                p = a + b - c
+                pa = abs(p - a)
+                pb = abs(p - b)
+                pc = abs(p - c)
+                if pa <= pb and pa <= pc:
+                    pr = a
+                elif pb <= pc:
+                    pr = b
+                else:
+                    pr = c
+                result[i] = (x + pr) & 0xff
+                ai += 1
+
+        # Call appropriate filter algorithm.  Note that 0 has already
+        # been dealt with.
+        (None,
+         pngfilters.undo_filter_sub,
+         pngfilters.undo_filter_up,
+         pngfilters.undo_filter_average,
+         pngfilters.undo_filter_paeth)[filter_type](fu, scanline, previous, result)
+        return result
+
+    def deinterlace(self, raw):
+        """
+        Read raw pixel data, undo filters, deinterlace, and flatten.
+        Return in flat row flat pixel format.
+        """
+
+        # print >> sys.stderr, ("Reading interlaced, w=%s, r=%s, planes=%s," +
+        #     " bpp=%s") % (self.width, self.height, self.planes, self.bps)
+        # Values per row (of the target image)
+        vpr = self.width * self.planes
+
+        # Make a result array, and make it big enough.  Interleaving
+        # writes to the output array randomly (well, not quite), so the
+        # entire output array must be in memory.
+        fmt = 'BH'[self.bitdepth > 8]
+        a = array(fmt, [0]*vpr*self.height)
+        source_offset = 0
+
+        for xstart, ystart, xstep, ystep in _adam7:
+            # print >> sys.stderr, "Adam7: start=%s,%s step=%s,%s" % (
+            #     xstart, ystart, xstep, ystep)
+            if xstart >= self.width:
+                continue
+            # The previous (reconstructed) scanline.  None at the
+            # beginning of a pass to indicate that there is no previous
+            # line.
+            recon = None
+            # Pixels per row (reduced pass image)
+            ppr = int(math.ceil((self.width-xstart)/float(xstep)))
+            # Row size in bytes for this pass.
+            row_size = int(math.ceil(self.psize * ppr))
+            for y in range(ystart, self.height, ystep):
+                filter_type = raw[source_offset]
+                source_offset += 1
+                scanline = raw[source_offset:source_offset+row_size]
+                source_offset += row_size
+                recon = self.undo_filter(filter_type, scanline, recon)
+                # Convert so that there is one element per pixel value
+                flat = self.serialtoflat(recon, ppr)
+                if xstep == 1:
+                    assert xstart == 0
+                    offset = y * vpr
+                    a[offset:offset+vpr] = flat
+                else:
+                    offset = y * vpr + xstart * self.planes
+                    end_offset = (y+1) * vpr
+                    skip = self.planes * xstep
+                    for i in range(self.planes):
+                        a[offset+i:end_offset:skip] = \
+                            flat[i::self.planes]
+        return a
+
+    def iterboxed(self, rows):
+        """Iterator that yields each scanline in boxed row flat pixel
+        format.  `rows` should be an iterator that yields the bytes of
+        each row in turn.
+        """
+
+        def asvalues(raw):
+            """Convert a row of raw bytes into a flat row.  Result may
+            or may not share with argument"""
+
+            if self.bitdepth == 8:
+                return raw
+            if self.bitdepth == 16:
+                raw = tostring(raw)
+                return array('H', struct.unpack('!%dH' % (len(raw)//2), raw))
+            assert self.bitdepth < 8
+            width = self.width
+            # Samples per byte
+            spb = 8//self.bitdepth
+            out = array('B')
+            mask = 2**self.bitdepth - 1
+            shifts = map(self.bitdepth.__mul__, reversed(range(spb)))
+            for o in raw:
+                out.extend(map(lambda i: mask&(o>>i), shifts))
+            return out[:width]
+
+        return itertools.imap(asvalues, rows)
+
+    def serialtoflat(self, bytes, width=None):
+        """Convert serial format (byte stream) pixel data to flat row
+        flat pixel.
+        """
+
+        if self.bitdepth == 8:
+            return bytes
+        if self.bitdepth == 16:
+            bytes = tostring(bytes)
+            return array('H',
+              struct.unpack('!%dH' % (len(bytes)//2), bytes))
+        assert self.bitdepth < 8
+        if width is None:
+            width = self.width
+        # Samples per byte
+        spb = 8//self.bitdepth
+        out = array('B')
+        mask = 2**self.bitdepth - 1
+        shifts = map(self.bitdepth.__mul__, reversed(range(spb)))
+        l = width
+        for o in bytes:
+            out.extend([(mask&(o>>s)) for s in shifts][:l])
+            l -= spb
+            if l <= 0:
+                l = width
+        return out
+
+    def iterstraight(self, raw):
+        """Iterator that undoes the effect of filtering, and yields each
+        row in serialised format (as a sequence of bytes).  Assumes input
+        is straightlaced.  `raw` should be an iterable that yields the
+        raw bytes in chunks of arbitrary size."""
+
+        # length of row, in bytes
+        rb = self.row_bytes
+        a = array('B')
+        # The previous (reconstructed) scanline.  None indicates first
+        # line of image.
+        recon = None
+        for some in raw:
+            a.extend(some)
+            while len(a) >= rb + 1:
+                filter_type = a[0]
+                scanline = a[1:rb+1]
+                del a[:rb+1]
+                recon = self.undo_filter(filter_type, scanline, recon)
+                yield recon
+        if len(a) != 0:
+            # :file:format We get here with a file format error: when the
+            # available bytes (after decompressing) do not pack into exact
+            # rows.
+            raise FormatError(
+              'Wrong size for decompressed IDAT chunk.')
+        assert len(a) == 0
+
+    def validate_signature(self):
+        """If signature (header) has not been read then read and
+        validate it; otherwise do nothing.
+        """
+
+        if self.signature:
+            return
+        self.signature = self.file.read(8)
+        if self.signature != _signature:
+            raise FormatError("PNG file has invalid signature.")
+
+    def preamble(self, lenient=False):
+        """
+        Extract the image metadata by reading the initial part of the PNG
+        file up to the start of the ``IDAT`` chunk.  All the chunks that
+        precede the ``IDAT`` chunk are read and either processed for
+        metadata or discarded.
+
+        If the optional `lenient` argument evaluates to True,
+        checksum failures will raise warnings rather than exceptions.
+        """
+
+        self.validate_signature()
+
+        while True:
+            if not self.atchunk:
+                self.atchunk = self.chunklentype()
+                if self.atchunk is None:
+                    raise FormatError(
+                      'This PNG file has no IDAT chunks.')
+            if self.atchunk[1] == 'IDAT':
+                return
+            self.process_chunk(lenient=lenient)
+
+    def chunklentype(self):
+        """Reads just enough of the input to determine the next
+        chunk's length and type, returned as a (*length*, *type*) pair
+        where *type* is a string.  If there are no more chunks, ``None``
+        is returned.
+        """
+
+        x = self.file.read(8)
+        if not x:
+            return None
+        if len(x) != 8:
+            raise FormatError(
+              'End of file whilst reading chunk length and type.')
+        length,type = struct.unpack('!I4s', x)
+        type = bytestostr(type)
+        if length > 2**31-1:
+            raise FormatError('Chunk %s is too large: %d.' % (type,length))
+        return length,type
+
+    def process_chunk(self, lenient=False):
+        """Process the next chunk and its data.  This only processes the
+        following chunk types, all others are ignored: ``IHDR``,
+        ``PLTE``, ``bKGD``, ``tRNS``, ``gAMA``, ``sBIT``.
+
+        If the optional `lenient` argument evaluates to True,
+        checksum failures will raise warnings rather than exceptions.
+        """
+
+        type, data = self.chunk(lenient=lenient)
+        if type == 'IHDR':
+            # http://www.w3.org/TR/PNG/#11IHDR
+            if len(data) != 13:
+                raise FormatError('IHDR chunk has incorrect length.')
+            (self.width, self.height, self.bitdepth, self.color_type,
+             self.compression, self.filter,
+             self.interlace) = struct.unpack("!2I5B", data)
+
+            # Check that the header specifies only valid combinations.
+            if self.bitdepth not in (1,2,4,8,16):
+                raise Error("invalid bit depth %d" % self.bitdepth)
+            if self.color_type not in (0,2,3,4,6):
+                raise Error("invalid colour type %d" % self.color_type)
+            # Check indexed (palettized) images have 8 or fewer bits
+            # per pixel; check only indexed or greyscale images have
+            # fewer than 8 bits per pixel.
+            if ((self.color_type & 1 and self.bitdepth > 8) or
+                (self.bitdepth < 8 and self.color_type not in (0,3))):
+                raise FormatError("Illegal combination of bit depth (%d)"
+                  " and colour type (%d)."
+                  " See http://www.w3.org/TR/2003/REC-PNG-20031110/#table111 ."
+                  % (self.bitdepth, self.color_type))
+            if self.compression != 0:
+                raise Error("unknown compression method %d" % self.compression)
+            if self.filter != 0:
+                raise FormatError("Unknown filter method %d,"
+                  " see http://www.w3.org/TR/2003/REC-PNG-20031110/#9Filters ."
+                  % self.filter)
+            if self.interlace not in (0,1):
+                raise FormatError("Unknown interlace method %d,"
+                  " see http://www.w3.org/TR/2003/REC-PNG-20031110/#8InterlaceMethods ."
+                  % self.interlace)
+
+            # Derived values
+            # http://www.w3.org/TR/PNG/#6Colour-values
+            colormap =  bool(self.color_type & 1)
+            greyscale = not (self.color_type & 2)
+            alpha = bool(self.color_type & 4)
+            color_planes = (3,1)[greyscale or colormap]
+            planes = color_planes + alpha
+
+            self.colormap = colormap
+            self.greyscale = greyscale
+            self.alpha = alpha
+            self.color_planes = color_planes
+            self.planes = planes
+            self.psize = float(self.bitdepth)/float(8) * planes
+            if int(self.psize) == self.psize:
+                self.psize = int(self.psize)
+            self.row_bytes = int(math.ceil(self.width * self.psize))
+            # Stores PLTE chunk if present, and is used to check
+            # chunk ordering constraints.
+            self.plte = None
+            # Stores tRNS chunk if present, and is used to check chunk
+            # ordering constraints.
+            self.trns = None
+            # Stores sbit chunk if present.
+            self.sbit = None
+        elif type == 'PLTE':
+            # http://www.w3.org/TR/PNG/#11PLTE
+            if self.plte:
+                warnings.warn("Multiple PLTE chunks present.")
+            self.plte = data
+            if len(data) % 3 != 0:
+                raise FormatError(
+                  "PLTE chunk's length should be a multiple of 3.")
+            if len(data) > (2**self.bitdepth)*3:
+                raise FormatError("PLTE chunk is too long.")
+            if len(data) == 0:
+                raise FormatError("Empty PLTE is not allowed.")
+        elif type == 'bKGD':
+            try:
+                if self.colormap:
+                    if not self.plte:
+                        warnings.warn(
+                          "PLTE chunk is required before bKGD chunk.")
+                    self.background = struct.unpack('B', data)
+                else:
+                    self.background = struct.unpack("!%dH" % self.color_planes,
+                      data)
+            except struct.error:
+                raise FormatError("bKGD chunk has incorrect length.")
+        elif type == 'tRNS':
+            # http://www.w3.org/TR/PNG/#11tRNS
+            self.trns = data
+            if self.colormap:
+                if not self.plte:
+                    warnings.warn("PLTE chunk is required before tRNS chunk.")
+                else:
+                    if len(data) > len(self.plte)/3:
+                        # Was warning, but promoted to Error as it
+                        # would otherwise cause pain later on.
+                        raise FormatError("tRNS chunk is too long.")
+            else:
+                if self.alpha:
+                    raise FormatError(
+                      "tRNS chunk is not valid with colour type %d." %
+                      self.color_type)
+                try:
+                    self.transparent = \
+                        struct.unpack("!%dH" % self.color_planes, data)
+                except struct.error:
+                    raise FormatError("tRNS chunk has incorrect length.")
+        elif type == 'gAMA':
+            try:
+                self.gamma = struct.unpack("!L", data)[0] / 100000.0
+            except struct.error:
+                raise FormatError("gAMA chunk has incorrect length.")
+        elif type == 'sBIT':
+            self.sbit = data
+            if (self.colormap and len(data) != 3 or
+                not self.colormap and len(data) != self.planes):
+                raise FormatError("sBIT chunk has incorrect length.")
+
+    def read(self, lenient=False):
+        """
+        Read the PNG file and decode it.  Returns (`width`, `height`,
+        `pixels`, `metadata`).
+
+        May use excessive memory.
+
+        `pixels` are returned in boxed row flat pixel format.
+
+        If the optional `lenient` argument evaluates to True,
+        checksum failures will raise warnings rather than exceptions.
+        """
+
+        def iteridat():
+            """Iterator that yields all the ``IDAT`` chunks as strings."""
+            while True:
+                try:
+                    type, data = self.chunk(lenient=lenient)
+                except ValueError, e:
+                    raise ChunkError(e.args[0])
+                if type == 'IEND':
+                    # http://www.w3.org/TR/PNG/#11IEND
+                    break
+                if type != 'IDAT':
+                    continue
+                # type == 'IDAT'
+                # http://www.w3.org/TR/PNG/#11IDAT
+                if self.colormap and not self.plte:
+                    warnings.warn("PLTE chunk is required before IDAT chunk")
+                yield data
+
+        def iterdecomp(idat):
+            """Iterator that yields decompressed strings.  `idat` should
+            be an iterator that yields the ``IDAT`` chunk data.
+            """
+
+            # Currently, with no max_length paramter to decompress, this
+            # routine will do one yield per IDAT chunk.  So not very
+            # incremental.
+            d = zlib.decompressobj()
+            # Each IDAT chunk is passed to the decompressor, then any
+            # remaining state is decompressed out.
+            for data in idat:
+                # :todo: add a max_length argument here to limit output
+                # size.
+                yield array('B', d.decompress(data))
+            yield array('B', d.flush())
+
+        self.preamble(lenient=lenient)
+        raw = iterdecomp(iteridat())
+
+        if self.interlace:
+            raw = array('B', itertools.chain(*raw))
+            arraycode = 'BH'[self.bitdepth>8]
+            # Like :meth:`group` but producing an array.array object for
+            # each row.
+            pixels = itertools.imap(lambda *row: array(arraycode, row),
+                       *[iter(self.deinterlace(raw))]*self.width*self.planes)
+        else:
+            pixels = self.iterboxed(self.iterstraight(raw))
+        meta = dict()
+        for attr in 'greyscale alpha planes bitdepth interlace'.split():
+            meta[attr] = getattr(self, attr)
+        meta['size'] = (self.width, self.height)
+        for attr in 'gamma transparent background'.split():
+            a = getattr(self, attr, None)
+            if a is not None:
+                meta[attr] = a
+        if self.plte:
+            meta['palette'] = self.palette()
+        return self.width, self.height, pixels, meta
+
+
+    def read_flat(self):
+        """
+        Read a PNG file and decode it into flat row flat pixel format.
+        Returns (*width*, *height*, *pixels*, *metadata*).
+
+        May use excessive memory.
+
+        `pixels` are returned in flat row flat pixel format.
+
+        See also the :meth:`read` method which returns pixels in the
+        more stream-friendly boxed row flat pixel format.
+        """
+
+        x, y, pixel, meta = self.read()
+        arraycode = 'BH'[meta['bitdepth']>8]
+        pixel = array(arraycode, itertools.chain(*pixel))
+        return x, y, pixel, meta
+
+    def palette(self, alpha='natural'):
+        """Returns a palette that is a sequence of 3-tuples or 4-tuples,
+        synthesizing it from the ``PLTE`` and ``tRNS`` chunks.  These
+        chunks should have already been processed (for example, by
+        calling the :meth:`preamble` method).  All the tuples are the
+        same size: 3-tuples if there is no ``tRNS`` chunk, 4-tuples when
+        there is a ``tRNS`` chunk.  Assumes that the image is colour type
+        3 and therefore a ``PLTE`` chunk is required.
+
+        If the `alpha` argument is ``'force'`` then an alpha channel is
+        always added, forcing the result to be a sequence of 4-tuples.
+        """
+
+        if not self.plte:
+            raise FormatError(
+                "Required PLTE chunk is missing in colour type 3 image.")
+        plte = group(array('B', self.plte), 3)
+        if self.trns or alpha == 'force':
+            trns = array('B', self.trns or '')
+            trns.extend([255]*(len(plte)-len(trns)))
+            plte = map(operator.add, plte, group(trns, 1))
+        return plte
+
+    def asDirect(self):
+        """Returns the image data as a direct representation of an
+        ``x * y * planes`` array.  This method is intended to remove the
+        need for callers to deal with palettes and transparency
+        themselves.  Images with a palette (colour type 3)
+        are converted to RGB or RGBA; images with transparency (a
+        ``tRNS`` chunk) are converted to LA or RGBA as appropriate.
+        When returned in this format the pixel values represent the
+        colour value directly without needing to refer to palettes or
+        transparency information.
+
+        Like the :meth:`read` method this method returns a 4-tuple:
+
+        (*width*, *height*, *pixels*, *meta*)
+
+        This method normally returns pixel values with the bit depth
+        they have in the source image, but when the source PNG has an
+        ``sBIT`` chunk it is inspected and can reduce the bit depth of
+        the result pixels; pixel values will be reduced according to
+        the bit depth specified in the ``sBIT`` chunk (PNG nerds should
+        note a single result bit depth is used for all channels; the
+        maximum of the ones specified in the ``sBIT`` chunk.  An RGB565
+        image will be rescaled to 6-bit RGB666).
+
+        The *meta* dictionary that is returned reflects the `direct`
+        format and not the original source image.  For example, an RGB
+        source image with a ``tRNS`` chunk to represent a transparent
+        colour, will have ``planes=3`` and ``alpha=False`` for the
+        source image, but the *meta* dictionary returned by this method
+        will have ``planes=4`` and ``alpha=True`` because an alpha
+        channel is synthesized and added.
+
+        *pixels* is the pixel data in boxed row flat pixel format (just
+        like the :meth:`read` method).
+
+        All the other aspects of the image data are not changed.
+        """
+
+        self.preamble()
+
+        # Simple case, no conversion necessary.
+        if not self.colormap and not self.trns and not self.sbit:
+            return self.read()
+
+        x,y,pixels,meta = self.read()
+
+        if self.colormap:
+            meta['colormap'] = False
+            meta['alpha'] = bool(self.trns)
+            meta['bitdepth'] = 8
+            meta['planes'] = 3 + bool(self.trns)
+            plte = self.palette()
+            def iterpal(pixels):
+                for row in pixels:
+                    row = map(plte.__getitem__, row)
+                    yield array('B', itertools.chain(*row))
+            pixels = iterpal(pixels)
+        elif self.trns:
+            # It would be nice if there was some reasonable way of doing
+            # this without generating a whole load of intermediate tuples.
+            # But tuples does seem like the easiest way, with no other way
+            # clearly much simpler or much faster.  (Actually, the L to LA
+            # conversion could perhaps go faster (all those 1-tuples!), but
+            # I still wonder whether the code proliferation is worth it)
+            it = self.transparent
+            maxval = 2**meta['bitdepth']-1
+            planes = meta['planes']
+            meta['alpha'] = True
+            meta['planes'] += 1
+            typecode = 'BH'[meta['bitdepth']>8]
+            def itertrns(pixels):
+                for row in pixels:
+                    # For each row we group it into pixels, then form a
+                    # characterisation vector that says whether each pixel
+                    # is opaque or not.  Then we convert True/False to
+                    # 0/maxval (by multiplication), and add it as the extra
+                    # channel.
+                    row = group(row, planes)
+                    opa = map(it.__ne__, row)
+                    opa = map(maxval.__mul__, opa)
+                    opa = zip(opa) # convert to 1-tuples
+                    yield array(typecode,
+                      itertools.chain(*map(operator.add, row, opa)))
+            pixels = itertrns(pixels)
+        targetbitdepth = None
+        if self.sbit:
+            sbit = struct.unpack('%dB' % len(self.sbit), self.sbit)
+            targetbitdepth = max(sbit)
+            if targetbitdepth > meta['bitdepth']:
+                raise Error('sBIT chunk %r exceeds bitdepth %d' %
+                    (sbit,self.bitdepth))
+            if min(sbit) <= 0:
+                raise Error('sBIT chunk %r has a 0-entry' % sbit)
+            if targetbitdepth == meta['bitdepth']:
+                targetbitdepth = None
+        if targetbitdepth:
+            shift = meta['bitdepth'] - targetbitdepth
+            meta['bitdepth'] = targetbitdepth
+            def itershift(pixels):
+                for row in pixels:
+                    yield map(shift.__rrshift__, row)
+            pixels = itershift(pixels)
+        return x,y,pixels,meta
+
+    def asFloat(self, maxval=1.0):
+        """Return image pixels as per :meth:`asDirect` method, but scale
+        all pixel values to be floating point values between 0.0 and
+        *maxval*.
+        """
+
+        x,y,pixels,info = self.asDirect()
+        sourcemaxval = 2**info['bitdepth']-1
+        del info['bitdepth']
+        info['maxval'] = float(maxval)
+        factor = float(maxval)/float(sourcemaxval)
+        def iterfloat():
+            for row in pixels:
+                yield map(factor.__mul__, row)
+        return x,y,iterfloat(),info
+
+    def _as_rescale(self, get, targetbitdepth):
+        """Helper used by :meth:`asRGB8` and :meth:`asRGBA8`."""
+
+        width,height,pixels,meta = get()
+        maxval = 2**meta['bitdepth'] - 1
+        targetmaxval = 2**targetbitdepth - 1
+        factor = float(targetmaxval) / float(maxval)
+        meta['bitdepth'] = targetbitdepth
+        def iterscale():
+            for row in pixels:
+                yield map(lambda x: int(round(x*factor)), row)
+        if maxval == targetmaxval:
+            return width, height, pixels, meta
+        else:
+            return width, height, iterscale(), meta
+
+    def asRGB8(self):
+        """Return the image data as an RGB pixels with 8-bits per
+        sample.  This is like the :meth:`asRGB` method except that
+        this method additionally rescales the values so that they
+        are all between 0 and 255 (8-bit).  In the case where the
+        source image has a bit depth < 8 the transformation preserves
+        all the information; where the source image has bit depth
+        > 8, then rescaling to 8-bit values loses precision.  No
+        dithering is performed.  Like :meth:`asRGB`, an alpha channel
+        in the source image will raise an exception.
+
+        This function returns a 4-tuple:
+        (*width*, *height*, *pixels*, *metadata*).
+        *width*, *height*, *metadata* are as per the :meth:`read` method.
+        
+        *pixels* is the pixel data in boxed row flat pixel format.
+        """
+
+        return self._as_rescale(self.asRGB, 8)
+
+    def asRGBA8(self):
+        """Return the image data as RGBA pixels with 8-bits per
+        sample.  This method is similar to :meth:`asRGB8` and
+        :meth:`asRGBA`:  The result pixels have an alpha channel, *and*
+        values are rescaled to the range 0 to 255.  The alpha channel is
+        synthesized if necessary (with a small speed penalty).
+        """
+
+        return self._as_rescale(self.asRGBA, 8)
+
+    def asRGB(self):
+        """Return image as RGB pixels.  RGB colour images are passed
+        through unchanged; greyscales are expanded into RGB
+        triplets (there is a small speed overhead for doing this).
+
+        An alpha channel in the source image will raise an
+        exception.
+
+        The return values are as for the :meth:`read` method
+        except that the *metadata* reflect the returned pixels, not the
+        source image.  In particular, for this method
+        ``metadata['greyscale']`` will be ``False``.
+        """
+
+        width,height,pixels,meta = self.asDirect()
+        if meta['alpha']:
+            raise Error("will not convert image with alpha channel to RGB")
+        if not meta['greyscale']:
+            return width,height,pixels,meta
+        meta['greyscale'] = False
+        typecode = 'BH'[meta['bitdepth'] > 8]
+        def iterrgb():
+            for row in pixels:
+                a = array(typecode, [0]) * 3 * width
+                for i in range(3):
+                    a[i::3] = row
+                yield a
+        return width,height,iterrgb(),meta
+
+    def asRGBA(self):
+        """Return image as RGBA pixels.  Greyscales are expanded into
+        RGB triplets; an alpha channel is synthesized if necessary.
+        The return values are as for the :meth:`read` method
+        except that the *metadata* reflect the returned pixels, not the
+        source image.  In particular, for this method
+        ``metadata['greyscale']`` will be ``False``, and
+        ``metadata['alpha']`` will be ``True``.
+        """
+
+        width,height,pixels,meta = self.asDirect()
+        if meta['alpha'] and not meta['greyscale']:
+            return width,height,pixels,meta
+        typecode = 'BH'[meta['bitdepth'] > 8]
+        maxval = 2**meta['bitdepth'] - 1
+        maxbuffer = struct.pack('=' + typecode, maxval) * 4 * width
+        def newarray():
+            return array(typecode, maxbuffer)
+
+        if meta['alpha'] and meta['greyscale']:
+            # LA to RGBA
+            def convert():
+                for row in pixels:
+                    # Create a fresh target row, then copy L channel
+                    # into first three target channels, and A channel
+                    # into fourth channel.
+                    a = newarray()
+                    pngfilters.convert_la_to_rgba(row, a)
+                    yield a
+        elif meta['greyscale']:
+            # L to RGBA
+            def convert():
+                for row in pixels:
+                    a = newarray()
+                    pngfilters.convert_l_to_rgba(row, a)
+                    yield a
+        else:
+            assert not meta['alpha'] and not meta['greyscale']
+            # RGB to RGBA
+            def convert():
+                for row in pixels:
+                    a = newarray()
+                    pngfilters.convert_rgb_to_rgba(row, a)
+                    yield a
+        meta['alpha'] = True
+        meta['greyscale'] = False
+        return width,height,convert(),meta
+
+
+# === Legacy Version Support ===
+
+# :pyver:old:  PyPNG works on Python versions 2.3 and 2.2, but not
+# without some awkward problems.  Really PyPNG works on Python 2.4 (and
+# above); it works on Pythons 2.3 and 2.2 by virtue of fixing up
+# problems here.  It's a bit ugly (which is why it's hidden down here).
+#
+# Generally the strategy is one of pretending that we're running on
+# Python 2.4 (or above), and patching up the library support on earlier
+# versions so that it looks enough like Python 2.4.  When it comes to
+# Python 2.2 there is one thing we cannot patch: extended slices
+# http://www.python.org/doc/2.3/whatsnew/section-slices.html.
+# Instead we simply declare that features that are implemented using
+# extended slices will not work on Python 2.2.
+#
+# In order to work on Python 2.3 we fix up a recurring annoyance involving
+# the array type.  In Python 2.3 an array cannot be initialised with an
+# array, and it cannot be extended with a list (or other sequence).
+# Both of those are repeated issues in the code.  Whilst I would not
+# normally tolerate this sort of behaviour, here we "shim" a replacement
+# for array into place (and hope no-ones notices).  You never read this.
+#
+# In an amusing case of warty hacks on top of warty hacks... the array
+# shimming we try and do only works on Python 2.3 and above (you can't
+# subclass array.array in Python 2.2).  So to get it working on Python
+# 2.2 we go for something much simpler and (probably) way slower.
+try:
+    array('B').extend([])
+    array('B', array('B'))
+except:
+    # Expect to get here on Python 2.3
+    try:
+        class _array_shim(array):
+            true_array = array
+            def __new__(cls, typecode, init=None):
+                super_new = super(_array_shim, cls).__new__
+                it = super_new(cls, typecode)
+                if init is None:
+                    return it
+                it.extend(init)
+                return it
+            def extend(self, extension):
+                super_extend = super(_array_shim, self).extend
+                if isinstance(extension, self.true_array):
+                    return super_extend(extension)
+                if not isinstance(extension, (list, str)):
+                    # Convert to list.  Allows iterators to work.
+                    extension = list(extension)
+                return super_extend(self.true_array(self.typecode, extension))
+        array = _array_shim
+    except:
+        # Expect to get here on Python 2.2
+        def array(typecode, init=()):
+            if type(init) == str:
+                return map(ord, init)
+            return list(init)
+
+# Further hacks to get it limping along on Python 2.2
+try:
+    enumerate
+except:
+    def enumerate(seq):
+        i=0
+        for x in seq:
+            yield i,x
+            i += 1
+
+try:
+    reversed
+except:
+    def reversed(l):
+        l = list(l)
+        l.reverse()
+        for x in l:
+            yield x
+
+try:
+    itertools
+except:
+    class _dummy_itertools:
+        pass
+    itertools = _dummy_itertools()
+    def _itertools_imap(f, seq):
+        for x in seq:
+            yield f(x)
+    itertools.imap = _itertools_imap
+    def _itertools_chain(*iterables):
+        for it in iterables:
+            for element in it:
+                yield element
+    itertools.chain = _itertools_chain
+
+
+# === Support for users without Cython ===
+
+try:
+    pngfilters
+except:
+    class pngfilters(object):
+        def undo_filter_sub(filter_unit, scanline, previous, result):
+            """Undo sub filter."""
+
+            ai = 0
+            # Loops starts at index fu.  Observe that the initial part
+            # of the result is already filled in correctly with
+            # scanline.
+            for i in range(filter_unit, len(result)):
+                x = scanline[i]
+                a = result[ai]
+                result[i] = (x + a) & 0xff
+                ai += 1
+        undo_filter_sub = staticmethod(undo_filter_sub)
+
+        def undo_filter_up(filter_unit, scanline, previous, result):
+            """Undo up filter."""
+
+            for i in range(len(result)):
+                x = scanline[i]
+                b = previous[i]
+                result[i] = (x + b) & 0xff
+        undo_filter_up = staticmethod(undo_filter_up)
+
+        def undo_filter_average(filter_unit, scanline, previous, result):
+            """Undo up filter."""
+
+            ai = -filter_unit
+            for i in range(len(result)):
+                x = scanline[i]
+                if ai < 0:
+                    a = 0
+                else:
+                    a = result[ai]
+                b = previous[i]
+                result[i] = (x + ((a + b) >> 1)) & 0xff
+                ai += 1
+        undo_filter_average = staticmethod(undo_filter_average)
+
+        def undo_filter_paeth(filter_unit, scanline, previous, result):
+            """Undo Paeth filter."""
+
+            # Also used for ci.
+            ai = -filter_unit
+            for i in range(len(result)):
+                x = scanline[i]
+                if ai < 0:
+                    a = c = 0
+                else:
+                    a = result[ai]
+                    c = previous[ai]
+                b = previous[i]
+                p = a + b - c
+                pa = abs(p - a)
+                pb = abs(p - b)
+                pc = abs(p - c)
+                if pa <= pb and pa <= pc:
+                    pr = a
+                elif pb <= pc:
+                    pr = b
+                else:
+                    pr = c
+                result[i] = (x + pr) & 0xff
+                ai += 1
+        undo_filter_paeth = staticmethod(undo_filter_paeth)
+
+        def convert_la_to_rgba(row, result):
+            for i in range(3):
+                result[i::4] = row[0::2]
+            result[3::4] = row[1::2]
+        convert_la_to_rgba = staticmethod(convert_la_to_rgba)
+
+        def convert_l_to_rgba(row, result):
+            """Convert a grayscale image to RGBA. This method assumes the alpha
+            channel in result is already correctly initialized."""
+            for i in range(3):
+                result[i::4] = row
+        convert_l_to_rgba = staticmethod(convert_l_to_rgba)
+
+        def convert_rgb_to_rgba(row, result):
+            """Convert an RGB image to RGBA. This method assumes the alpha
+            channel in result is already correctly initialized."""
+            for i in range(3):
+                result[i::4] = row[i::3]
+        convert_rgb_to_rgba = staticmethod(convert_rgb_to_rgba)
+
+
+# === Internal Test Support ===
+
+# This section comprises the tests that are internally validated (as
+# opposed to tests which produce output files that are externally
+# validated).  Primarily they are unittests.
+
+# Note that it is difficult to internally validate the results of
+# writing a PNG file.  The only thing we can do is read it back in
+# again, which merely checks consistency, not that the PNG file we
+# produce is valid.
+
+# Run the tests from the command line:
+# python -c 'import png;png.test()'
+
+# (For an in-memory binary file IO object) We use BytesIO where
+# available, otherwise we use StringIO, but name it BytesIO.
+try:
+    from io import BytesIO
+except:
+    from StringIO import StringIO as BytesIO
+import tempfile
+# http://www.python.org/doc/2.4.4/lib/module-unittest.html
+import unittest
+
+
+def test():
+    unittest.main(__name__)
+
+def topngbytes(name, rows, x, y, **k):
+    """Convenience function for creating a PNG file "in memory" as a
+    string.  Creates a :class:`Writer` instance using the keyword arguments,
+    then passes `rows` to its :meth:`Writer.write` method.  The resulting
+    PNG file is returned as a string.  `name` is used to identify the file for
+    debugging.
+    """
+
+    import os
+
+    print name
+    f = BytesIO()
+    w = Writer(x, y, **k)
+    w.write(f, rows)
+    if os.environ.get('PYPNG_TEST_TMP'):
+        w = open(name, 'wb')
+        w.write(f.getvalue())
+        w.close()
+    return f.getvalue()
+
+def testWithIO(inp, out, f):
+    """Calls the function `f` with ``sys.stdin`` changed to `inp`
+    and ``sys.stdout`` changed to `out`.  They are restored when `f`
+    returns.  This function returns whatever `f` returns.
+    """
+
+    import os
+
+    try:
+        oldin,sys.stdin = sys.stdin,inp
+        oldout,sys.stdout = sys.stdout,out
+        x = f()
+    finally:
+        sys.stdin = oldin
+        sys.stdout = oldout
+    if os.environ.get('PYPNG_TEST_TMP') and hasattr(out,'getvalue'):
+        name = mycallersname()
+        if name:
+            w = open(name+'.png', 'wb')
+            w.write(out.getvalue())
+            w.close()
+    return x
+
+def mycallersname():
+    """Returns the name of the caller of the caller of this function
+    (hence the name of the caller of the function in which
+    "mycallersname()" textually appears).  Returns None if this cannot
+    be determined."""
+
+    # http://docs.python.org/library/inspect.html#the-interpreter-stack
+    import inspect
+
+    frame = inspect.currentframe()
+    if not frame:
+        return None
+    frame_,filename_,lineno_,funname,linelist_,listi_ = (
+      inspect.getouterframes(frame)[2])
+    return funname
+
+def seqtobytes(s):
+    """Convert a sequence of integers to a *bytes* instance.  Good for
+    plastering over Python 2 / Python 3 cracks.
+    """
+
+    return strtobytes(''.join(chr(x) for x in s))
+
+class Test(unittest.TestCase):
+    # This member is used by the superclass.  If we don't define a new
+    # class here then when we use self.assertRaises() and the PyPNG code
+    # raises an assertion then we get no proper traceback.  I can't work
+    # out why, but defining a new class here means we get a proper
+    # traceback.
+    class failureException(Exception):
+        pass
+
+    def helperLN(self, n):
+        mask = (1 << n) - 1
+        # Use small chunk_limit so that multiple chunk writing is
+        # tested.  Making it a test for Issue 20.
+        w = Writer(15, 17, greyscale=True, bitdepth=n, chunk_limit=99)
+        f = BytesIO()
+        w.write_array(f, array('B', map(mask.__and__, range(1, 256))))
+        r = Reader(bytes=f.getvalue())
+        x,y,pixels,meta = r.read()
+        self.assertEqual(x, 15)
+        self.assertEqual(y, 17)
+        self.assertEqual(list(itertools.chain(*pixels)),
+                         map(mask.__and__, range(1,256)))
+    def testL8(self):
+        return self.helperLN(8)
+    def testL4(self):
+        return self.helperLN(4)
+    def testL2(self):
+        "Also tests asRGB8."
+        w = Writer(1, 4, greyscale=True, bitdepth=2)
+        f = BytesIO()
+        w.write_array(f, array('B', range(4)))
+        r = Reader(bytes=f.getvalue())
+        x,y,pixels,meta = r.asRGB8()
+        self.assertEqual(x, 1)
+        self.assertEqual(y, 4)
+        for i,row in enumerate(pixels):
+            self.assertEqual(len(row), 3)
+            self.assertEqual(list(row), [0x55*i]*3)
+    def testP2(self):
+        "2-bit palette."
+        a = (255,255,255)
+        b = (200,120,120)
+        c = (50,99,50)
+        w = Writer(1, 4, bitdepth=2, palette=[a,b,c])
+        f = BytesIO()
+        w.write_array(f, array('B', (0,1,1,2)))
+        r = Reader(bytes=f.getvalue())
+        x,y,pixels,meta = r.asRGB8()
+        self.assertEqual(x, 1)
+        self.assertEqual(y, 4)
+        self.assertEqual(map(list, pixels), map(list, [a, b, b, c]))
+    def testPtrns(self):
+        "Test colour type 3 and tRNS chunk (and 4-bit palette)."
+        a = (50,99,50,50)
+        b = (200,120,120,80)
+        c = (255,255,255)
+        d = (200,120,120)
+        e = (50,99,50)
+        w = Writer(3, 3, bitdepth=4, palette=[a,b,c,d,e])
+        f = BytesIO()
+        w.write_array(f, array('B', (4, 3, 2, 3, 2, 0, 2, 0, 1)))
+        r = Reader(bytes=f.getvalue())
+        x,y,pixels,meta = r.asRGBA8()
+        self.assertEqual(x, 3)
+        self.assertEqual(y, 3)
+        c = c+(255,)
+        d = d+(255,)
+        e = e+(255,)
+        boxed = [(e,d,c),(d,c,a),(c,a,b)]
+        flat = map(lambda row: itertools.chain(*row), boxed)
+        self.assertEqual(map(list, pixels), map(list, flat))
+    def testRGBtoRGBA(self):
+        "asRGBA8() on colour type 2 source."""
+        # Test for Issue 26
+        r = Reader(bytes=_pngsuite['basn2c08'])
+        x,y,pixels,meta = r.asRGBA8()
+        # Test the pixels at row 9 columns 0 and 1.
+        row9 = list(pixels)[9]
+        self.assertEqual(list(row9[0:8]),
+                         [0xff, 0xdf, 0xff, 0xff, 0xff, 0xde, 0xff, 0xff])
+    def testLtoRGBA(self):
+        "asRGBA() on grey source."""
+        # Test for Issue 60
+        r = Reader(bytes=_pngsuite['basi0g08'])
+        x,y,pixels,meta = r.asRGBA()
+        row9 = list(list(pixels)[9])
+        self.assertEqual(row9[0:8],
+          [222, 222, 222, 255, 221, 221, 221, 255])
+    def testCtrns(self):
+        "Test colour type 2 and tRNS chunk."
+        # Test for Issue 25
+        r = Reader(bytes=_pngsuite['tbrn2c08'])
+        x,y,pixels,meta = r.asRGBA8()
+        # I just happen to know that the first pixel is transparent.
+        # In particular it should be #7f7f7f00
+        row0 = list(pixels)[0]
+        self.assertEqual(tuple(row0[0:4]), (0x7f, 0x7f, 0x7f, 0x00))
+    def testAdam7read(self):
+        """Adam7 interlace reading.
+        Specifically, test that for images in the PngSuite that
+        have both an interlaced and straightlaced pair that both
+        images from the pair produce the same array of pixels."""
+        for candidate in _pngsuite:
+            if not candidate.startswith('basn'):
+                continue
+            candi = candidate.replace('n', 'i')
+            if candi not in _pngsuite:
+                continue
+            print 'adam7 read', candidate
+            straight = Reader(bytes=_pngsuite[candidate])
+            adam7 = Reader(bytes=_pngsuite[candi])
+            # Just compare the pixels.  Ignore x,y (because they're
+            # likely to be correct?); metadata is ignored because the
+            # "interlace" member differs.  Lame.
+            straight = straight.read()[2]
+            adam7 = adam7.read()[2]
+            self.assertEqual(map(list, straight), map(list, adam7))
+    def testAdam7write(self):
+        """Adam7 interlace writing.
+        For each test image in the PngSuite, write an interlaced
+        and a straightlaced version.  Decode both, and compare results.
+        """
+        # Not such a great test, because the only way we can check what
+        # we have written is to read it back again.
+
+        for name,bytes in _pngsuite.items():
+            # Only certain colour types supported for this test.
+            if name[3:5] not in ['n0', 'n2', 'n4', 'n6']:
+                continue
+            it = Reader(bytes=bytes)
+            x,y,pixels,meta = it.read()
+            pngi = topngbytes('adam7wn'+name+'.png', pixels,
+              x=x, y=y, bitdepth=it.bitdepth,
+              greyscale=it.greyscale, alpha=it.alpha,
+              transparent=it.transparent,
+              interlace=False)
+            x,y,ps,meta = Reader(bytes=pngi).read()
+            it = Reader(bytes=bytes)
+            x,y,pixels,meta = it.read()
+            pngs = topngbytes('adam7wi'+name+'.png', pixels,
+              x=x, y=y, bitdepth=it.bitdepth,
+              greyscale=it.greyscale, alpha=it.alpha,
+              transparent=it.transparent,
+              interlace=True)
+            x,y,pi,meta = Reader(bytes=pngs).read()
+            self.assertEqual(map(list, ps), map(list, pi))
+    def testPGMin(self):
+        """Test that the command line tool can read PGM files."""
+        def do():
+            return _main(['testPGMin'])
+        s = BytesIO()
+        s.write(strtobytes('P5 2 2 3\n'))
+        s.write(strtobytes('\x00\x01\x02\x03'))
+        s.flush()
+        s.seek(0)
+        o = BytesIO()
+        testWithIO(s, o, do)
+        r = Reader(bytes=o.getvalue())
+        x,y,pixels,meta = r.read()
+        self.assertTrue(r.greyscale)
+        self.assertEqual(r.bitdepth, 2)
+    def testPAMin(self):
+        """Test that the command line tool can read PAM file."""
+        def do():
+            return _main(['testPAMin'])
+        s = BytesIO()
+        s.write(strtobytes('P7\nWIDTH 3\nHEIGHT 1\nDEPTH 4\nMAXVAL 255\n'
+                'TUPLTYPE RGB_ALPHA\nENDHDR\n'))
+        # The pixels in flat row flat pixel format
+        flat =  [255,0,0,255, 0,255,0,120, 0,0,255,30]
+        asbytes = seqtobytes(flat)
+        s.write(asbytes)
+        s.flush()
+        s.seek(0)
+        o = BytesIO()
+        testWithIO(s, o, do)
+        r = Reader(bytes=o.getvalue())
+        x,y,pixels,meta = r.read()
+        self.assertTrue(r.alpha)
+        self.assertTrue(not r.greyscale)
+        self.assertEqual(list(itertools.chain(*pixels)), flat)
+    def testLA4(self):
+        """Create an LA image with bitdepth 4."""
+        bytes = topngbytes('la4.png', [[5, 12]], 1, 1,
+          greyscale=True, alpha=True, bitdepth=4)
+        sbit = Reader(bytes=bytes).chunk('sBIT')[1]
+        self.assertEqual(sbit, strtobytes('\x04\x04'))
+    def testPal(self):
+        """Test that a palette PNG returns the palette in info."""
+        r = Reader(bytes=_pngsuite['basn3p04'])
+        x,y,pixels,info = r.read()
+        self.assertEqual(x, 32)
+        self.assertEqual(y, 32)
+        self.assertTrue('palette' in info)
+    def testPalWrite(self):
+        """Test metadata for paletted PNG can be passed from one PNG
+        to another."""
+        r = Reader(bytes=_pngsuite['basn3p04'])
+        x,y,pixels,info = r.read()
+        w = Writer(**info)
+        o = BytesIO()
+        w.write(o, pixels)
+        o.flush()
+        o.seek(0)
+        r = Reader(file=o)
+        _,_,_,again_info = r.read()
+        # Same palette
+        self.assertEqual(again_info['palette'], info['palette'])
+    def testPalExpand(self):
+        """Test that bitdepth can be used to fiddle with pallete image."""
+        r = Reader(bytes=_pngsuite['basn3p04'])
+        x,y,pixels,info = r.read()
+        pixels = [list(row) for row in pixels]
+        info['bitdepth'] = 8
+        w = Writer(**info)
+        o = BytesIO()
+        w.write(o, pixels)
+        o.flush()
+        o.seek(0)
+        r = Reader(file=o)
+        _,_,again_pixels,again_info = r.read()
+        # Same pixels
+        again_pixels = [list(row) for row in again_pixels]
+        self.assertEqual(again_pixels, pixels)
+
+    def testPNMsbit(self):
+        """Test that PNM files can generates sBIT chunk."""
+        def do():
+            return _main(['testPNMsbit'])
+        s = BytesIO()
+        s.write(strtobytes('P6 8 1 1\n'))
+        for pixel in range(8):
+            s.write(struct.pack('<I', (0x4081*pixel)&0x10101)[:3])
+        s.flush()
+        s.seek(0)
+        o = BytesIO()
+        testWithIO(s, o, do)
+        r = Reader(bytes=o.getvalue())
+        sbit = r.chunk('sBIT')[1]
+        self.assertEqual(sbit, strtobytes('\x01\x01\x01'))
+    def testLtrns0(self):
+        """Create greyscale image with tRNS chunk."""
+        return self.helperLtrns(0)
+    def testLtrns1(self):
+        """Using 1-tuple for transparent arg."""
+        return self.helperLtrns((0,))
+    def helperLtrns(self, transparent):
+        """Helper used by :meth:`testLtrns*`."""
+        pixels = zip([0x00, 0x38, 0x4c, 0x54, 0x5c, 0x40, 0x38, 0x00])
+        o = BytesIO()
+        w = Writer(8, 8, greyscale=True, bitdepth=1, transparent=transparent)
+        w.write_packed(o, pixels)
+        r = Reader(bytes=o.getvalue())
+        x,y,pixels,meta = r.asDirect()
+        self.assertTrue(meta['alpha'])
+        self.assertTrue(meta['greyscale'])
+        self.assertEqual(meta['bitdepth'], 1)
+    def testWinfo(self):
+        """Test the dictionary returned by a `read` method can be used
+        as args for :meth:`Writer`.
+        """
+        r = Reader(bytes=_pngsuite['basn2c16'])
+        info = r.read()[3]
+        w = Writer(**info)
+    def testPackedIter(self):
+        """Test iterator for row when using write_packed.
+
+        Indicative for Issue 47.
+        """
+        w = Writer(16, 2, greyscale=True, alpha=False, bitdepth=1)
+        o = BytesIO()
+        w.write_packed(o, [itertools.chain([0x0a], [0xaa]),
+                           itertools.chain([0x0f], [0xff])])
+        r = Reader(bytes=o.getvalue())
+        x,y,pixels,info = r.asDirect()
+        pixels = list(pixels)
+        self.assertEqual(len(pixels), 2)
+        self.assertEqual(len(pixels[0]), 16)
+    def testInterlacedArray(self):
+        """Test that reading an interlaced PNG yields each row as an
+        array."""
+        r = Reader(bytes=_pngsuite['basi0g08'])
+        list(r.read()[2])[0].tostring
+    def testTrnsArray(self):
+        """Test that reading a type 2 PNG with tRNS chunk yields each
+        row as an array (using asDirect)."""
+        r = Reader(bytes=_pngsuite['tbrn2c08'])
+        list(r.asDirect()[2])[0].tostring
+
+    # Invalid file format tests.  These construct various badly
+    # formatted PNG files, then feed them into a Reader.  When
+    # everything is working properly, we should get FormatError
+    # exceptions raised.
+    def testEmpty(self):
+        """Test empty file."""
+
+        r = Reader(bytes='')
+        self.assertRaises(FormatError, r.asDirect)
+    def testSigOnly(self):
+        """Test file containing just signature bytes."""
+
+        r = Reader(bytes=_signature)
+        self.assertRaises(FormatError, r.asDirect)
+    def testExtraPixels(self):
+        """Test file that contains too many pixels."""
+
+        def eachchunk(chunk):
+            if chunk[0] != 'IDAT':
+                return chunk
+            data = zlib.decompress(chunk[1])
+            data += strtobytes('\x00garbage')
+            data = zlib.compress(data)
+            chunk = (chunk[0], data)
+            return chunk
+        self.assertRaises(FormatError, self.helperFormat, eachchunk)
+    def testNotEnoughPixels(self):
+        def eachchunk(chunk):
+            if chunk[0] != 'IDAT':
+                return chunk
+            # Remove last byte.
+            data = zlib.decompress(chunk[1])
+            data = data[:-1]
+            data = zlib.compress(data)
+            return (chunk[0], data)
+        self.assertRaises(FormatError, self.helperFormat, eachchunk)
+    def helperFormat(self, f):
+        r = Reader(bytes=_pngsuite['basn0g01'])
+        o = BytesIO()
+        def newchunks():
+            for chunk in r.chunks():
+                yield f(chunk)
+        write_chunks(o, newchunks())
+        r = Reader(bytes=o.getvalue())
+        return list(r.asDirect()[2])
+    def testBadFilter(self):
+        def eachchunk(chunk):
+            if chunk[0] != 'IDAT':
+                return chunk
+            data = zlib.decompress(chunk[1])
+            # Corrupt the first filter byte
+            data = strtobytes('\x99') + data[1:]
+            data = zlib.compress(data)
+            return (chunk[0], data)
+        self.assertRaises(FormatError, self.helperFormat, eachchunk)
+
+    def testFlat(self):
+        """Test read_flat."""
+        import hashlib
+
+        r = Reader(bytes=_pngsuite['basn0g02'])
+        x,y,pixel,meta = r.read_flat()
+        d = hashlib.md5(seqtobytes(pixel)).digest()
+        self.assertEqual(_enhex(d), '255cd971ab8cd9e7275ff906e5041aa0')
+    def testfromarray(self):
+        img = from_array([[0, 0x33, 0x66], [0xff, 0xcc, 0x99]], 'L')
+        img.save('testfromarray.png')
+    def testfromarrayL16(self):
+        img = from_array(group(range(2**16), 256), 'L;16')
+        img.save('testL16.png')
+    def testfromarrayRGB(self):
+        img = from_array([[0,0,0, 0,0,1, 0,1,0, 0,1,1],
+                          [1,0,0, 1,0,1, 1,1,0, 1,1,1]], 'RGB;1')
+        o = BytesIO()
+        img.save(o)
+    def testfromarrayIter(self):
+        import itertools
+
+        i = itertools.islice(itertools.count(10), 20)
+        i = itertools.imap(lambda x: [x, x, x], i)
+        img = from_array(i, 'RGB;5', dict(height=20))
+        f = open('testiter.png', 'wb')
+        img.save(f)
+        f.close()
+
+    # numpy dependent tests.  These are skipped (with a message to
+    # sys.stderr) if numpy cannot be imported.
+    def testNumpyuint16(self):
+        """numpy uint16."""
+
+        try:
+            import numpy
+        except ImportError:
+            print >>sys.stderr, "skipping numpy test"
+            return
+
+        rows = [map(numpy.uint16, range(0,0x10000,0x5555))]
+        b = topngbytes('numpyuint16.png', rows, 4, 1,
+            greyscale=True, alpha=False, bitdepth=16)
+    def testNumpyuint8(self):
+        """numpy uint8."""
+
+        try:
+            import numpy
+        except ImportError:
+            print >>sys.stderr, "skipping numpy test"
+            return
+
+        rows = [map(numpy.uint8, range(0,0x100,0x55))]
+        b = topngbytes('numpyuint8.png', rows, 4, 1,
+            greyscale=True, alpha=False, bitdepth=8)
+    def testNumpybool(self):
+        """numpy bool."""
+
+        try:
+            import numpy
+        except ImportError:
+            print >>sys.stderr, "skipping numpy test"
+            return
+
+        rows = [map(numpy.bool, [0,1])]
+        b = topngbytes('numpybool.png', rows, 2, 1,
+            greyscale=True, alpha=False, bitdepth=1)
+    def testNumpyarray(self):
+        """numpy array."""
+        try:
+            import numpy
+        except ImportError:
+            print >>sys.stderr, "skipping numpy test"
+            return
+
+        pixels = numpy.array([[0,0x5555],[0x5555,0xaaaa]], numpy.uint16)
+        img = from_array(pixels, 'L')
+        img.save('testnumpyL16.png')
+
+    def paeth(self, x, a, b, c):
+        p = a + b - c
+        pa = abs(p - a)
+        pb = abs(p - b)
+        pc = abs(p - c)
+        if pa <= pb and pa <= pc:
+            pr = a
+        elif pb <= pc:
+            pr = b
+        else:
+            pr = c
+        return x - pr
+
+    # test filters and unfilters
+    def testFilterScanlineFirstLine(self):
+        fo = 3  # bytes per pixel
+        line = [30, 31, 32, 230, 231, 232]
+        out = filter_scanline(0, line, fo, None)  # none
+        self.assertEqual(list(out), [0, 30, 31, 32, 230, 231, 232])
+        out = filter_scanline(1, line, fo, None)  # sub
+        self.assertEqual(list(out), [1, 30, 31, 32, 200, 200, 200])
+        out = filter_scanline(2, line, fo, None)  # up
+        # TODO: All filtered scanlines start with a byte indicating the filter
+        # algorithm, except "up". Is this a bug? Should the expected output
+        # start with 2 here?
+        self.assertEqual(list(out), [30, 31, 32, 230, 231, 232])
+        out = filter_scanline(3, line, fo, None)  # average
+        self.assertEqual(list(out), [3, 30, 31, 32, 215, 216, 216])
+        out = filter_scanline(4, line, fo, None)  # paeth
+        self.assertEqual(list(out), [
+            4, self.paeth(30, 0, 0, 0), self.paeth(31, 0, 0, 0),
+            self.paeth(32, 0, 0, 0), self.paeth(230, 30, 0, 0),
+            self.paeth(231, 31, 0, 0), self.paeth(232, 32, 0, 0)
+            ])
+    def testFilterScanline(self):
+        prev = [20, 21, 22, 210, 211, 212]
+        line = [30, 32, 34, 230, 233, 236]
+        fo = 3
+        out = filter_scanline(0, line, fo, prev)  # none
+        self.assertEqual(list(out), [0, 30, 32, 34, 230, 233, 236])
+        out = filter_scanline(1, line, fo, prev)  # sub
+        self.assertEqual(list(out), [1, 30, 32, 34, 200, 201, 202])
+        out = filter_scanline(2, line, fo, prev)  # up
+        self.assertEqual(list(out), [2, 10, 11, 12, 20, 22, 24])
+        out = filter_scanline(3, line, fo, prev)  # average
+        self.assertEqual(list(out), [3, 20, 22, 23, 110, 112, 113])
+        out = filter_scanline(4, line, fo, prev)  # paeth
+        self.assertEqual(list(out), [
+            4, self.paeth(30, 0, 20, 0), self.paeth(32, 0, 21, 0),
+            self.paeth(34, 0, 22, 0), self.paeth(230, 30, 210, 20),
+            self.paeth(233, 32, 211, 21), self.paeth(236, 34, 212, 22)
+            ])
+    def testUnfilterScanline(self):
+        reader = Reader(bytes='')
+        reader.psize = 3
+        scanprev = array('B', [20, 21, 22, 210, 211, 212])
+        scanline = array('B', [30, 32, 34, 230, 233, 236])
+        def cp(a):
+            return array('B', a)
+
+        out = reader.undo_filter(0, cp(scanline), cp(scanprev))
+        self.assertEqual(list(out), list(scanline))  # none
+        out = reader.undo_filter(1, cp(scanline), cp(scanprev))
+        self.assertEqual(list(out), [30, 32, 34, 4, 9, 14])  # sub
+        out = reader.undo_filter(2, cp(scanline), cp(scanprev))
+        self.assertEqual(list(out), [50, 53, 56, 184, 188, 192])  # up
+        out = reader.undo_filter(3, cp(scanline), cp(scanprev))
+        self.assertEqual(list(out), [40, 42, 45, 99, 103, 108])  # average
+        out = reader.undo_filter(4, cp(scanline), cp(scanprev))
+        self.assertEqual(list(out), [50, 53, 56, 184, 188, 192])  # paeth
+    def testUnfilterScanlinePaeth(self):
+        # This tests more edge cases in the paeth unfilter
+        reader = Reader(bytes='')
+        reader.psize = 3
+        scanprev = array('B', [2, 0, 0, 0, 9, 11])
+        scanline = array('B', [6, 10, 9, 100, 101, 102])
+
+        out = reader.undo_filter(4, scanline, scanprev)
+        self.assertEqual(list(out), [8, 10, 9, 108, 111, 113])  # paeth
+    def testIterstraight(self):
+        def arraify(list_of_str):
+            return [array('B', s) for s in list_of_str]
+        reader = Reader(bytes='')
+        reader.row_bytes = 6
+        reader.psize = 3
+        rows = reader.iterstraight(arraify(['\x00abcdef', '\x00ghijkl']))
+        self.assertEqual(list(rows), arraify(['abcdef', 'ghijkl']))
+
+        rows = reader.iterstraight(arraify(['\x00abc', 'def\x00ghijkl']))
+        self.assertEqual(list(rows), arraify(['abcdef', 'ghijkl']))
+
+        rows = reader.iterstraight(arraify(['\x00abcdef\x00ghijkl']))
+        self.assertEqual(list(rows), arraify(['abcdef', 'ghijkl']))
+
+        rows = reader.iterstraight(arraify(['\x00abcdef\x00ghi', 'jkl']))
+        self.assertEqual(list(rows), arraify(['abcdef', 'ghijkl']))
+
+# === Command Line Support ===
+
+def _dehex(s):
+    """Liberally convert from hex string to binary string."""
+    import re
+    import binascii
+
+    # Remove all non-hexadecimal digits
+    s = re.sub(r'[^a-fA-F\d]', '', s)
+    # binscii.unhexlify works in Python 2 and Python 3 (unlike
+    # thing.decode('hex')).
+    return binascii.unhexlify(strtobytes(s))
+def _enhex(s):
+    """Convert from binary string (bytes) to hex string (str)."""
+
+    import binascii
+
+    return bytestostr(binascii.hexlify(s))
+
+# Copies of PngSuite test files taken
+# from http://www.schaik.com/pngsuite/pngsuite_bas_png.html
+# on 2009-02-19 by drj and converted to hex.
+# Some of these are not actually in PngSuite (but maybe they should
+# be?), they use the same naming scheme, but start with a capital
+# letter.
+_pngsuite = {
+  'basi0g01': _dehex("""
+89504e470d0a1a0a0000000d49484452000000200000002001000000012c0677
+cf0000000467414d41000186a031e8965f0000009049444154789c2d8d310ec2
+300c45dfc682c415187a00a42e197ab81e83b127e00c5639001363a580d8582c
+65c910357c4b78b0bfbfdf4f70168c19e7acb970a3f2d1ded9695ce5bf5963df
+d92aaf4c9fd927ea449e6487df5b9c36e799b91bdf082b4d4bd4014fe4014b01
+ab7a17aee694d28d328a2d63837a70451e1648702d9a9ff4a11d2f7a51aa21e5
+a18c7ffd0094e3511d661822f20000000049454e44ae426082
+"""),
+  'basi0g02': _dehex("""
+89504e470d0a1a0a0000000d49484452000000200000002002000000016ba60d
+1f0000000467414d41000186a031e8965f0000005149444154789c635062e860
+00e17286bb609c93c370ec189494960631366e4467b3ae675dcf10f521ea0303
+90c1ca006444e11643482064114a4852c710baea3f18c31918020c30410403a6
+0ac1a09239009c52804d85b6d97d0000000049454e44ae426082
+"""),
+  'basi0g04': _dehex("""
+89504e470d0a1a0a0000000d4948445200000020000000200400000001e4e6f8
+bf0000000467414d41000186a031e8965f000000ae49444154789c658e5111c2
+301044171c141c141c041c843a287510ea20d441c041c141c141c04191102454
+03994998cecd7edcecedbb9bdbc3b2c2b6457545fbc4bac1be437347f7c66a77
+3c23d60db15e88f5c5627338a5416c2e691a9b475a89cd27eda12895ae8dfdab
+43d61e590764f5c83a226b40d669bec307f93247701687723abf31ff83a2284b
+a5b4ae6b63ac6520ad730ca4ed7b06d20e030369bd6720ed383290360406d24e
+13811f2781eba9d34d07160000000049454e44ae426082
+"""),
+  'basi0g08': _dehex("""
+89504e470d0a1a0a0000000d4948445200000020000000200800000001211615
+be0000000467414d41000186a031e8965f000000b549444154789cb5905d0ac2
+3010849dbac81c42c47bf843cf253e8878b0aa17110f214bdca6be240f5d21a5
+94ced3e49bcd322c1624115515154998aa424822a82a5624a1aa8a8b24c58f99
+999908130989a04a00d76c2c09e76cf21adcb209393a6553577da17140a2c59e
+70ecbfa388dff1f03b82fb82bd07f05f7cb13f80bb07ad2fd60c011c3c588eef
+f1f4e03bbec7ce832dca927aea005e431b625796345307b019c845e6bfc3bb98
+769d84f9efb02ea6c00f9bb9ff45e81f9f280000000049454e44ae426082
+"""),
+  'basi0g16': _dehex("""
+89504e470d0a1a0a0000000d49484452000000200000002010000000017186c9
+fd0000000467414d41000186a031e8965f000000e249444154789cb5913b0ec2
+301044c7490aa8f85d81c3e4301c8f53a4ca0da8902c8144b3920b4043111282
+23bc4956681a6bf5fc3c5a3ba0448912d91a4de2c38dd8e380231eede4c4f7a1
+4677700bec7bd9b1d344689315a3418d1a6efbe5b8305ba01f8ff4808c063e26
+c60d5c81edcf6c58c535e252839e93801b15c0a70d810ae0d306b205dc32b187
+272b64057e4720ff0502154034831520154034c3df81400510cdf0015c86e5cc
+5c79c639fddba9dcb5456b51d7980eb52d8e7d7fa620a75120d6064641a05120
+b606771a05626b401a05f1f589827cf0fe44c1f0bae0055698ee8914fffffe00
+00000049454e44ae426082
+"""),
+  'basi2c08': _dehex("""
+89504e470d0a1a0a0000000d49484452000000200000002008020000018b1fdd
+350000000467414d41000186a031e8965f000000f249444154789cd59341aa04
+210c44abc07b78133d59d37333bd89d76868b566d10cf4675af8596431a11662
+7c5688919280e312257dd6a0a4cf1a01008ee312a5f3c69c37e6fcc3f47e6776
+a07f8bdaf5b40feed2d33e025e2ff4fe2d4a63e1a16d91180b736d8bc45854c5
+6d951863f4a7e0b66dcf09a900f3ffa2948d4091e53ca86c048a64390f662b50
+4a999660ced906182b9a01a8be00a56404a6ede182b1223b4025e32c4de34304
+63457680c93aada6c99b73865aab2fc094920d901a203f5ddfe1970d28456783
+26cffbafeffcd30654f46d119be4793f827387fc0d189d5bc4d69a3c23d45a7f
+db803146578337df4d0a3121fc3d330000000049454e44ae426082
+"""),
+  'basi2c16': _dehex("""
+89504e470d0a1a0a0000000d4948445200000020000000201002000001db8f01
+760000000467414d41000186a031e8965f0000020a49444154789cd5962173e3
+3010853fcf1838cc61a1818185a53e56787fa13fa130852e3b5878b4b0b03081
+b97f7030070b53e6b057a0a8912bbb9163b9f109ececbc59bd7dcf2b45492409
+d66f00eb1dd83cb5497d65456aeb8e1040913b3b2c04504c936dd5a9c7e2c6eb
+b1b8f17a58e8d043da56f06f0f9f62e5217b6ba3a1b76f6c9e99e8696a2a72e2
+c4fb1e4d452e92ec9652b807486d12b6669be00db38d9114b0c1961e375461a5
+5f76682a85c367ad6f682ff53a9c2a353191764b78bb07d8ddc3c97c1950f391
+6745c7b9852c73c2f212605a466a502705c8338069c8b9e84efab941eb393a97
+d4c9fd63148314209f1c1d3434e847ead6380de291d6f26a25c1ebb5047f5f24
+d85c49f0f22cc1d34282c72709cab90477bf25b89d49f0f351822297e0ea9704
+f34c82bc94002448ede51866e5656aef5d7c6a385cb4d80e6a538ceba04e6df2
+480e9aa84ddedb413bb5c97b3838456df2d4fec2c7a706983e7474d085fae820
+a841776a83073838973ac0413fea2f1dc4a06e71108fda73109bdae48954ad60
+bf867aac3ce44c7c1589a711cf8a81df9b219679d96d1cec3d8bbbeaa2012626
+df8c7802eda201b2d2e0239b409868171fc104ba8b76f10b4da09f6817ffc609
+c413ede267fd1fbab46880c90f80eccf0013185eb48b47ba03df2bdaadef3181
+cb8976f18e13188768170f98c0f844bb78cb04c62ddac59d09fc3fa25dfc1da4
+14deb3df1344f70000000049454e44ae426082
+"""),
+  'basi3p08': _dehex("""
+89504e470d0a1a0a0000000d494844520000002000000020080300000133a3ba
+500000000467414d41000186a031e8965f00000300504c5445224400f5ffed77
+ff77cbffff110a003a77002222ffff11ff110000222200ffac5566ff66ff6666
+ff01ff221200dcffffccff994444ff005555220000cbcbff44440055ff55cbcb
+00331a00ffecdcedffffe4ffcbffdcdc44ff446666ff330000442200ededff66
+6600ffa444ffffaaeded0000cbcbfefffffdfffeffff0133ff33552a000101ff
+8888ff00aaaa010100440000888800ffe4cbba5b0022ff22663200ffff99aaaa
+ff550000aaaa00cb630011ff11d4ffaa773a00ff4444dc6b0066000001ff0188
+4200ecffdc6bdc00ffdcba00333300ed00ed7300ffff88994a0011ffff770000
+ff8301ffbabafe7b00fffeff00cb00ff999922ffff880000ffff77008888ffdc
+ff1a33000000aa33ffff009900990000000001326600ffbaff44ffffffaaff00
+770000fefeaa00004a9900ffff66ff22220000998bff1155ffffff0101ff88ff
+005500001111fffffefffdfea4ff4466ffffff66ff003300ffff55ff77770000
+88ff44ff00110077ffff006666ffffed000100fff5ed1111ffffff44ff22ffff
+eded11110088ffff00007793ff2200dcdc3333fffe00febabaff99ffff333300
+63cb00baba00acff55ffffdcffff337bfe00ed00ed5555ffaaffffdcdcff5555
+00000066dcdc00dc00dc83ff017777fffefeffffffcbff5555777700fefe00cb
+00cb0000fe010200010000122200ffff220044449bff33ffd4aa0000559999ff
+999900ba00ba2a5500ffcbcbb4ff66ff9b33ffffbaaa00aa42880053aa00ffaa
+aa0000ed00babaffff1100fe00000044009999990099ffcc99ba000088008800
+dc00ff93220000dcfefffeaa5300770077020100cb0000000033ffedff00ba00
+ff3333edffedffc488bcff7700aa00660066002222dc0000ffcbffdcffdcff8b
+110000cb00010155005500880000002201ffffcbffcbed0000ff88884400445b
+ba00ffbc77ff99ff006600baffba00777773ed00fe00003300330000baff77ff
+004400aaffaafffefe000011220022c4ff8800eded99ff99ff55ff002200ffb4
+661100110a1100ff1111dcffbabaffff88ff88010001ff33ffb98ed362000002
+a249444154789c65d0695c0b001806f03711a9904a94d24dac63292949e5a810
+d244588a14ca5161d1a1323973252242d62157d12ae498c8124d25ca3a11398a
+16e55a3cdffab0ffe7f77d7fcff3528645349b584c3187824d9d19d4ec2e3523
+9eb0ae975cf8de02f2486d502191841b42967a1ad49e5ddc4265f69a899e26b5
+e9e468181baae3a71a41b95669da8df2ea3594c1b31046d7b17bfb86592e4cbe
+d89b23e8db0af6304d756e60a8f4ad378bdc2552ae5948df1d35b52143141533
+33bbbbababebeb3b3bc9c9c9c6c6c0c0d7b7b535323225a5aa8a02024a4bedec
+0a0a2a2bcdcd7d7cf2f3a9a9c9cdcdd8b8adcdd5b5ababa828298982824a4ab2
+b21212acadbdbc1414e2e24859b9a72730302f4f49292c4c57373c9c0a0b7372
+8c8c1c1c3a3a92936d6dfdfd293e3e26262a4a4eaea2424b4b5fbfbc9c323278
+3c0b0ba1303abaae8ecdeeed950d6669a9a7a7a141d4de9e9d5d5cdcd2229b94
+c572716132f97cb1d8db9bc3110864a39795d9db6b6a26267a7a9a98d4d6a6a7
+cb76090ef6f030354d4d75766e686030545464cb393a1a1ac6c68686eae8f8f9
+a9aa4644c8b66d6e1689dcdd2512a994cb35330b0991ad9f9b6b659596a6addd
+d8282fafae5e5323fb8f41d01f76c22fd8061be01bfc041a0323e1002c81cd30
+0b9ec027a0c930014ec035580fc3e112bc069a0b53e11c0c8095f00176c163a0
+e5301baec06a580677600ddc05ba0f13e120bc81a770133ec355a017300d4ec2
+0c7800bbe1219c02fa08f3e13c1c85dbb00a2ec05ea0dff00a6ec15a98027360
+070c047a06d7e1085c84f1b014f6c03fa0b33018b6c0211801ebe018fc00da0a
+6f61113c877eb01d4ec317a085700f26c130f80efbe132bc039a0733e106fc81
+f7f017f6c10aa0d1300a0ec374780943e1382c06fa0a9b60238c83473016cec0
+02f80f73fefe1072afc1e50000000049454e44ae426082
+"""),
+  'basi6a08': _dehex("""
+89504e470d0a1a0a0000000d4948445200000020000000200806000001047d4a
+620000000467414d41000186a031e8965f0000012049444154789cc595414ec3
+3010459fa541b8bbb26641b8069b861e8b4d12c1c112c1452a710a2a65d840d5
+949041fc481ec98ae27c7f3f8d27e3e4648047600fec0d1f390fbbe2633a31e2
+9389e4e4ea7bfdbf3d9a6b800ab89f1bd6b553cfcbb0679e960563d72e0a9293
+b7337b9f988cc67f5f0e186d20e808042f1c97054e1309da40d02d7e27f92e03
+6cbfc64df0fc3117a6210a1b6ad1a00df21c1abcf2a01944c7101b0cb568a001
+909c9cf9e399cf3d8d9d4660a875405d9a60d000b05e2de55e25780b7a5268e0
+622118e2399aab063a815808462f1ab86890fc2e03e48bb109ded7d26ce4bf59
+0db91bac0050747fec5015ce80da0e5700281be533f0ce6d5900b59bcb00ea6d
+200314cf801faab200ea752803a8d7a90c503a039f824a53f4694e7342000000
+0049454e44ae426082
+"""),
+  'basn0g01': _dehex("""
+89504e470d0a1a0a0000000d49484452000000200000002001000000005b0147
+590000000467414d41000186a031e8965f0000005b49444154789c2dccb10903
+300c05d1ebd204b24a200b7a346f90153c82c18d0a61450751f1e08a2faaead2
+a4846ccea9255306e753345712e211b221bf4b263d1b427325255e8bdab29e6f
+6aca30692e9d29616ee96f3065f0bf1f1087492fd02f14c90000000049454e44
+ae426082
+"""),
+  'basn0g02': _dehex("""
+89504e470d0a1a0a0000000d49484452000000200000002002000000001ca13d
+890000000467414d41000186a031e8965f0000001f49444154789c6360085df5
+1f8cf1308850c20053868f0133091f6390b90700bd497f818b0989a900000000
+49454e44ae426082
+"""),
+  # A version of basn0g04 dithered down to 3 bits.
+  'Basn0g03': _dehex("""
+89504e470d0a1a0a0000000d494844520000002000000020040000000093e1c8
+2900000001734249540371d88211000000fd49444154789c6d90d18906210c84
+c356f22356b2889588604301b112112b11d94a96bb495cf7fe87f32d996f2689
+44741cc658e39c0b118f883e1f63cc89dafbc04c0f619d7d898396c54b875517
+83f3a2e7ac09a2074430e7f497f00f1138a5444f82839c5206b1f51053cca968
+63258821e7f2b5438aac16fbecc052b646e709de45cf18996b29648508728612
+952ca606a73566d44612b876845e9a347084ea4868d2907ff06be4436c4b41a3
+a3e1774285614c5affb40dbd931a526619d9fa18e4c2be420858de1df0e69893
+a0e3e5523461be448561001042b7d4a15309ce2c57aef2ba89d1c13794a109d7
+b5880aa27744fc5c4aecb5e7bcef5fe528ec6293a930690000000049454e44ae
+426082
+"""),
+  'basn0g04': _dehex("""
+89504e470d0a1a0a0000000d494844520000002000000020040000000093e1c8
+290000000467414d41000186a031e8965f0000004849444154789c6360601014
+545232367671090d4d4b2b2f6720430095dbd1418e002a77e64c720450b9ab56
+912380caddbd9b1c0154ee9933e408a072efde25470095fbee1d1902001f14ee
+01eaff41fa0000000049454e44ae426082
+"""),
+  'basn0g08': _dehex("""
+89504e470d0a1a0a0000000d4948445200000020000000200800000000561125
+280000000467414d41000186a031e8965f0000004149444154789c6364602400
+1408c8b30c05058c0f0829f8f71f3f6079301c1430ca11906764a2795c0c0605
+8c8ff0cafeffcff887e67131181430cae0956564040050e5fe7135e2d8590000
+000049454e44ae426082
+"""),
+  'basn0g16': _dehex("""
+89504e470d0a1a0a0000000d49484452000000200000002010000000000681f9
+6b0000000467414d41000186a031e8965f0000005e49444154789cd5d2310ac0
+300c4351395bef7fc6dca093c0287b32d52a04a3d98f3f3880a7b857131363a0
+3a82601d089900dd82f640ca04e816dc06422640b7a03d903201ba05b7819009
+d02d680fa44c603f6f07ec4ff41938cf7f0016d84bd85fae2b9fd70000000049
+454e44ae426082
+"""),
+  'basn2c08': _dehex("""
+89504e470d0a1a0a0000000d4948445200000020000000200802000000fc18ed
+a30000000467414d41000186a031e8965f0000004849444154789cedd5c10900
+300c024085ec91fdb772133b442bf4a1f8cee12bb40d043b800a14f81ca0ede4
+7d4c784081020f4a871fc284071428f0a0743823a94081bb7077a3c00182b1f9
+5e0f40cf4b0000000049454e44ae426082
+"""),
+  'basn2c16': _dehex("""
+89504e470d0a1a0a0000000d4948445200000020000000201002000000ac8831
+e00000000467414d41000186a031e8965f000000e549444154789cd596c10a83
+301044a7e0417fcb7eb7fdadf6961e06039286266693cc7a188645e43dd6a08f
+1042003e2fe09aef6472737e183d27335fcee2f35a77b702ebce742870a23397
+f3edf2705dd10160f3b2815fe8ecf2027974a6b0c03f74a6e4192843e75c6c03
+35e8ec3202f5e84c0181bbe8cca967a00d9df3491bb040671f2e6087ce1c2860
+8d1e05f8c7ee0f1d00b667e70df44467ef26d01fbd9bc028f42860f71d188bce
+fb8d3630039dbd59601e7ab3c06cf428507f0634d039afdc80123a7bb1801e7a
+b1802a7a14c89f016d74ce331bf080ce9e08f8414f04bca133bfe642fe5e07bb
+c4ec0000000049454e44ae426082
+"""),
+  'basn3p04': _dehex("""
+89504e470d0a1a0a0000000d4948445200000020000000200403000000815467
+c70000000467414d41000186a031e8965f000000037342495404040477f8b5a3
+0000002d504c54452200ff00ffff8800ff22ff000099ffff6600dd00ff77ff00
+ff000000ff99ddff00ff00bbffbb000044ff00ff44d2b049bd00000047494441
+54789c63e8e8080d3d7366d5aaf27263e377ef66ce64204300952b28488e002a
+d7c5851c0154eeddbbe408a07119c81140e52a29912380ca4d4b23470095bb7b
+37190200e0c4ead10f82057d0000000049454e44ae426082
+"""),
+  'basn6a08': _dehex("""
+89504e470d0a1a0a0000000d4948445200000020000000200806000000737a7a
+f40000000467414d41000186a031e8965f0000006f49444154789cedd6310a80
+300c46e12764684fa1f73f55048f21c4ddc545781d52e85028fc1f4d28d98a01
+305e7b7e9cffba33831d75054703ca06a8f90d58a0074e351e227d805c8254e3
+1bb0420f5cdc2e0079208892ffe2a00136a07b4007943c1004d900195036407f
+011bf00052201a9c160fb84c0000000049454e44ae426082
+"""),
+  'cs3n3p08': _dehex("""
+89504e470d0a1a0a0000000d494844520000002000000020080300000044a48a
+c60000000467414d41000186a031e8965f0000000373424954030303a392a042
+00000054504c544592ff0000ff9200ffff00ff0000dbff00ff6dffb600006dff
+b6ff00ff9200dbff000049ffff2400ff000024ff0049ff0000ffdb00ff4900ff
+b6ffff0000ff2400b6ffffdb000092ffff6d000024ffff49006dff00df702b17
+0000004b49444154789c85cac70182000000b1b3625754b0edbfa72324ef7486
+184ed0177a437b680bcdd0031c0ed00ea21f74852ed00a1c9ed0086da0057487
+6ed0121cd6d004bda0013a421ff803224033e177f4ae260000000049454e44ae
+426082
+"""),
+  's09n3p02': _dehex("""
+89504e470d0a1a0a0000000d49484452000000090000000902030000009dffee
+830000000467414d41000186a031e8965f000000037342495404040477f8b5a3
+0000000c504c544500ff000077ffff00ffff7700ff5600640000001f49444154
+789c63600002fbff0c0c56ab19182ca381581a4283f82071200000696505c36a
+437f230000000049454e44ae426082
+"""),
+  'tbgn3p08': _dehex("""
+89504e470d0a1a0a0000000d494844520000002000000020080300000044a48a
+c60000000467414d41000186a031e8965f00000207504c54457f7f7fafafafab
+abab110000222200737300999999510d00444400959500959595e6e600919191
+8d8d8d620d00898989666600b7b700911600000000730d007373736f6f6faaaa
+006b6b6b676767c41a00cccc0000f30000ef00d51e0055555567670000dd0051
+515100d1004d4d4de61e0038380000b700160d0d00ab00560d00090900009500
+009100008d003333332f2f2f2f2b2f2b2b000077007c7c001a05002b27000073
+002b2b2b006f00bb1600272727780d002323230055004d4d00cc1e00004d00cc
+1a000d00003c09006f6f00002f003811271111110d0d0d55554d090909001100
+4d0900050505000d00e2e200000900000500626200a6a6a6a2a2a29e9e9e8484
+00fb00fbd5d500801100800d00ea00ea555500a6a600e600e6f7f700e200e233
+0500888888d900d9848484c01a007777003c3c05c8c8008080804409007c7c7c
+bb00bbaa00aaa600a61e09056262629e009e9a009af322005e5e5e05050000ee
+005a5a5adddd00a616008d008d00e20016050027270088110078780000c40078
+00787300736f006f44444400aa00c81e004040406600663c3c3c090000550055
+1a1a00343434d91e000084004d004d007c004500453c3c00ea1e00222222113c
+113300331e1e1efb22001a1a1a004400afaf00270027003c001616161e001e0d
+160d2f2f00808000001e00d1d1001100110d000db7b7b7090009050005b3b3b3
+6d34c4230000000174524e530040e6d86600000001624b474402660b7c640000
+01f249444154789c6360c0048c8c58049100575f215ee92e6161ef109cd2a15e
+4b9645ce5d2c8f433aa4c24f3cbd4c98833b2314ab74a186f094b9c2c27571d2
+6a2a58e4253c5cda8559057a392363854db4d9d0641973660b0b0bb76bb16656
+06970997256877a07a95c75a1804b2fbcd128c80b482a0b0300f8a824276a9a8
+ec6e61612b3e57ee06fbf0009619d5fac846ac5c60ed20e754921625a2daadc6
+1967e29e97d2239c8aec7e61fdeca9cecebef54eb36c848517164514af16169e
+866444b2b0b7b55534c815cc2ec22d89cd1353800a8473100a4485852d924a6a
+412adc74e7ad1016ceed043267238c901716f633a812022998a4072267c4af02
+92127005c0f811b62830054935ce017b38bf0948cc5c09955f030a24617d9d46
+63371fd940b0827931cbfdf4956076ac018b592f72d45594a9b1f307f3261b1a
+084bc2ad50018b1900719ba6ba4ca325d0427d3f6161449486f981144cf3100e
+2a5f2a1ce8683e4ddf1b64275240c8438d98af0c729bbe07982b8a1c94201dc2
+b3174c9820bcc06201585ad81b25b64a2146384e3798290c05ad280a18c0a62e
+e898260c07fca80a24c076cc864b777131a00190cdfa3069035eccbc038c30e1
+3e88b46d16b6acc5380d6ac202511c392f4b789aa7b0b08718765990111606c2
+9e854c38e5191878fbe471e749b0112bb18902008dc473b2b2e8e72700000000
+49454e44ae426082
+"""),
+  'Tp2n3p08': _dehex("""
+89504e470d0a1a0a0000000d494844520000002000000020080300000044a48a
+c60000000467414d41000186a031e8965f00000300504c544502ffff80ff05ff
+7f0703ff7f0180ff04ff00ffff06ff000880ff05ff7f07ffff06ff000804ff00
+0180ff02ffff03ff7f02ffff80ff0503ff7f0180ffff0008ff7f0704ff00ffff
+06ff000802ffffff7f0704ff0003ff7fffff0680ff050180ff04ff000180ffff
+0008ffff0603ff7f80ff05ff7f0702ffffff000880ff05ffff0603ff7f02ffff
+ff7f070180ff04ff00ffff06ff000880ff050180ffff7f0702ffff04ff0003ff
+7fff7f0704ff0003ff7f0180ffffff06ff000880ff0502ffffffff0603ff7fff
+7f0702ffff04ff000180ff80ff05ff0008ff7f07ffff0680ff0504ff00ff0008
+0180ff03ff7f02ffff02ffffffff0604ff0003ff7f0180ffff000880ff05ff7f
+0780ff05ff00080180ff02ffffff7f0703ff7fffff0604ff00ff7f07ff0008ff
+ff0680ff0504ff0002ffff0180ff03ff7fff0008ffff0680ff0504ff000180ff
+02ffff03ff7fff7f070180ff02ffff04ff00ffff06ff0008ff7f0780ff0503ff
+7fffff06ff0008ff7f0780ff0502ffff03ff7f0180ff04ff0002ffffff7f07ff
+ff0604ff0003ff7fff00080180ff80ff05ffff0603ff7f0180ffff000804ff00
+80ff0502ffffff7f0780ff05ffff0604ff000180ffff000802ffffff7f0703ff
+7fff0008ff7f070180ff03ff7f02ffff80ff05ffff0604ff00ff0008ffff0602
+ffff0180ff04ff0003ff7f80ff05ff7f070180ff04ff00ff7f0780ff0502ffff
+ff000803ff7fffff0602ffffff7f07ffff0680ff05ff000804ff0003ff7f0180
+ff02ffff0180ffff7f0703ff7fff000804ff0080ff05ffff0602ffff04ff00ff
+ff0603ff7fff7f070180ff80ff05ff000803ff7f0180ffff7f0702ffffff0008
+04ff00ffff0680ff0503ff7f0180ff04ff0080ff05ffff06ff000802ffffff7f
+0780ff05ff0008ff7f070180ff03ff7f04ff0002ffffffff0604ff00ff7f07ff
+000880ff05ffff060180ff02ffff03ff7f80ff05ffff0602ffff0180ff03ff7f
+04ff00ff7f07ff00080180ffff000880ff0502ffff04ff00ff7f0703ff7fffff
+06ff0008ffff0604ff00ff7f0780ff0502ffff03ff7f0180ffdeb83387000000
+f874524e53000000000000000008080808080808081010101010101010181818
+1818181818202020202020202029292929292929293131313131313131393939
+393939393941414141414141414a4a4a4a4a4a4a4a52525252525252525a5a5a
+5a5a5a5a5a62626262626262626a6a6a6a6a6a6a6a73737373737373737b7b7b
+7b7b7b7b7b83838383838383838b8b8b8b8b8b8b8b94949494949494949c9c9c
+9c9c9c9c9ca4a4a4a4a4a4a4a4acacacacacacacacb4b4b4b4b4b4b4b4bdbdbd
+bdbdbdbdbdc5c5c5c5c5c5c5c5cdcdcdcdcdcdcdcdd5d5d5d5d5d5d5d5dedede
+dededededee6e6e6e6e6e6e6e6eeeeeeeeeeeeeeeef6f6f6f6f6f6f6f6b98ac5
+ca0000012c49444154789c6360e7169150d230b475f7098d4ccc28a96ced9e32
+63c1da2d7b8e9fb97af3d1fb8f3f18e8a0808953544a4dd7c4c2c9233c2621bf
+b4aab17fdacce5ab36ee3a72eafaad87efbefea68702362e7159652d031b07cf
+c0b8a4cce28aa68e89f316aedfb4ffd0b92bf79fbcfcfe931e0a183904e55435
+8decdcbcc22292b3caaadb7b27cc5db67af3be63e72fdf78fce2d31f7a2860e5
+119356d037b374f10e8a4fc92eaa6fee99347fc9caad7b0f9ebd74f7c1db2fbf
+e8a180995f484645dbdccad12f38363dafbcb6a573faeca5ebb6ed3e7ce2c29d
+e76fbefda38702063e0149751d537b67ff80e8d4dcc29a86bea97316add9b0e3
+c0e96bf79ebdfafc971e0a587885e515f58cad5d7d43a2d2720aeadaba26cf5a
+bc62fbcea3272fde7efafac37f3a28000087c0fe101bc2f85f0000000049454e
+44ae426082
+"""),
+  'tbbn1g04': _dehex("""
+89504e470d0a1a0a0000000d494844520000002000000020040000000093e1c8
+290000000467414d41000186a031e8965f0000000274524e530007e8f7589b00
+000002624b47440000aa8d23320000013e49444154789c55d1cd4b024118c7f1
+efbe6419045b6a48a72d352808b435284f9187ae9b098627a1573a19945beba5
+e8129e8222af11d81e3a4545742de8ef6af6d5762e0fbf0fc33c33f36085cb76
+bc4204778771b867260683ee57e13f0c922df5c719c2b3b6c6c25b2382cea4b9
+9f7d4f244370746ac71f4ca88e0f173a6496749af47de8e44ba8f3bf9bdfa98a
+0faf857a7dd95c7dc8d7c67c782c99727997f41eb2e3c1e554152465bb00fe8e
+b692d190b718d159f4c0a45c4435915a243c58a7a4312a7a57913f05747594c6
+46169866c57101e4d4ce4d511423119c419183a3530cc63db88559ae28e7342a
+1e9c8122b71139b8872d6e913153224bc1f35b60e4445bd4004e20ed6682c759
+1d9873b3da0fbf50137dc5c9bde84fdb2ec8bde1189e0448b63584735993c209
+7a601bd2710caceba6158797285b7f2084a2f82c57c01a0000000049454e44ae
+426082
+"""),
+  'tbrn2c08': _dehex("""
+89504e470d0a1a0a0000000d4948445200000020000000200802000000fc18ed
+a30000000467414d41000186a031e8965f0000000674524e53007f007f007f8a
+33334f00000006624b474400ff0000000033277cf3000004d649444154789cad
+965f68537714c73fd912d640235e692f34d0406fa0c1663481045ab060065514
+56660a295831607df0a1488715167060840a1614e6431e9cb34fd2c00a762c85
+f6a10f816650c13b0cf40612e1822ddc4863bd628a8924d23d6464f9d3665dd9
+f7e977ce3dbff3cd3939bfdfef6bb87dfb364782dbed065ebe7cd93acc78b4ec
+a228debd7bb7bfbfbfbbbbfb7f261045311a8d261209405194274f9ea4d3e916
+f15f1c3eb5dd6e4fa5fecce526239184a2b0b8486f6f617171b1f5ae4311381c
+8e57af5e5dbd7a351088150a78bd389d44222c2f93cdfe66b7db8f4ee07038b6
+b6b6bebf766d7e7e7e60a06432313b4ba984c3c1c4049a46b95c5a58583822c1
+dbb76f27272733d1b9df853c3030c0f232562b9108cf9eb1b888d7cbf030abab
+31abd5fa1f08dc6ef7e7cf9f1f3f7e1c8944745d4f1400c62c001313acad21cb
+b8dd2c2c603271eb1640341aad4c6d331aa7e8c48913a150a861307ecc11e964
+74899919bc5e14e56fffc404f1388502f178dceff7ef4bf0a5cfe7abb533998c
+e5f9ea2f1dd88c180d64cb94412df3dd57e83a6b3b3c7a84c98420100c72fd3a
+636348bae726379fe69e8e8d8dbd79f3a6558b0607079796965256479b918085
+7b02db12712b6181950233023f3f647494ee6e2e5ea45864cce5b8a7fe3acffc
+3aebb22c2bd5d20e22d0757d7b7bbbbdbd3d94a313bed1b0aa3cd069838b163a
+8d4c59585f677292d0b84d9a995bd337def3fe6bbe5e6001989b9b6bfe27ea08
+36373781542ab56573248b4c5bc843ac4048c7ab21aa24ca00534c25482828a3
+8c9ee67475bbaaaab22cb722c8e57240a150301a8d219de94e44534d7d90e885
+87acb0e2c4f9800731629b6c5ee14a35a6b9887d2a0032994cb9cf15dbe59650
+ff7b46a04c9a749e7cc5112214266cc65c31354d5b5d5d3d90209bcd5616a552
+a95c2e87f2a659bd9ee01c2cd73964e438f129a6aa9e582c363838b80f81d7eb
+5555b56a2a8ad2d9d7affd0409f8015c208013fea00177b873831b0282c964f2
+783c1e8fa7582cee5f81a669b5e6eeeeaee58e8559b0c233d8843c7c0b963a82
+34e94b5cb2396d7d7d7db22c8ba258fb0afd43f0e2c58b919191ba9de9b4d425
+118329b0c3323c8709d02041b52b4ea7f39de75d2a934a2693c0a953a76a93d4
+5d157ebf7f6565a5542a553df97c5e10045dd731c130b86113cc300cbd489224
+08422a952a140a95788fc763b1d41558d7a2d7af5f5fb870a1d6a3aaaacd6603
+18802da84c59015bd2e6897b745d9765b99a1df0f97c0daf74e36deaf7fbcd66
+73ad2797cb89a2c839880188a2e8743a8bc5a22ccbba5e376466b3b9bdbdbd21
+6123413a9d0e0402b51e4dd3bababa788eb022b85caeb6b6364551b6b7b76942
+43f7f727007a7a7a04a1ee8065b3595fde2768423299ac1ec6669c3973e65004
+c0f8f878ad69341a33994ced2969c0d0d0502412f9f8f163f3a7fd654b474787
+288ad53e74757535df6215b85cae60302849d2410aecc037f9f2e5cbd5b5c160
+680eb0dbede170381c0e7ff8f0a185be3b906068684892a4ca7a6f6faff69328
+8ad3d3d3f7efdfdfdbdbfb57e96868a14d0d0643381c96242997cbe5f3794010
+84603078fcf8f1d6496bd14a3aba5c2ea7d369341a5555b5582c8140e0fcf9f3
+1b1b1b87cf4eeb0a8063c78e45a3d19e9e1ebfdfdf5a831e844655d18093274f
+9e3d7bf6d3a74f3b3b3b47c80efc05ff7af28fefb70d9b0000000049454e44ae
+426082
+"""),
+  'basn6a16': _dehex("""
+89504e470d0a1a0a0000000d494844520000002000000020100600000023eaa6
+b70000000467414d41000186a031e8965f00000d2249444154789cdd995f6c1c
+d775c67ff38fb34b724d2ee55a8e4b04a0ac87049100cab4dbd8c6528902cb4d
+10881620592e52d4325ac0905bc98a94025e71fd622cb5065ac98a0c283050c0
+728a00b6e542a1d126885cd3298928891d9a0444037e904434951d4b90b84b2f
+c9dde1fcebc33977a95555348f411e16dfce9d3b77ee77eebde77ce78c95a669
+0ad07c17009a13edd898b87dfb1fcb7d2b4d1bff217f33df80deb1e6267df0ff
+c1e6e6dfafdf1f5a7fd30f9aef66b6d546dd355bf02c40662e3307f9725a96c6
+744c3031f83782f171c148dbc3bf1774f5dad1e79d6f095a3f54d4fbec5234ef
+d9a2f8d73afe4f14f57ef4f42def7b44f19060f06b45bddf1c5534d77fd922be
+2973a15a82e648661c6e3240aa3612ead952b604bde57458894f29deaf133bac
+13d2766f5227a4a3b8cf08da7adfd6fbd6bd8a4fe9dbb43d35e3dfa3f844fbf8
+9119bf4f7144094fb56333abf8a86063ca106f94b3a3b512343765e60082097f
+1bb86ba72439a653519b09f5cee1ce61c897d37eedf5553580ae60f4af8af33a
+b14fd400b6a0f34535c0434afc0b3a9f07147527a5fa7ca218ff56c74d74dc3f
+155cfd3325fc278acf2ae1cb4a539f5f9937c457263b0bd51234c732a300cdd1
+cc1840f0aaff54db0e4874ed5a9b5d6d27d4bb36746d80de72baa877ff4b275a
+d7895ed1897ea4139b5143fcbb1a62560da1ed9662aaed895ec78a91c18795b8
+5e07ab4af8ba128e95e682e0728bf8f2e5ae815a091a53d902ac1920d8e05f06
+589de8d8d66680789f4e454fb9d9ec66cd857af796ee2d902fa73fd5bba775a2
+153580ae44705ed0d37647d15697cb8f14bfa3e3e8fdf8031d47af571503357c
+f30d25acedcbbf135c9a35c49766ba07ab255859e8ec03684e66860182dff8f7
+0304bff6ff1c20fc81b7afdd00a71475539a536e36bb5973a19e3b923b02bde5
+e4efd4003ac170eb2d13fe274157afedbd82d6fb3a9a1e85e4551d47cf7078f8
+9671fe4289ebf5f2bf08d63f37c4eb4773c55a0996efeefa0ca011671d8060ca
+2f0004c7fcc300e166ef0240f825efe3361f106d57d423d0723f7acacd66376b
+2ed47b7a7a7a205f4ef4ac4691e0aad9aa0d41cf13741c3580a506487574ddca
+61a8c403c1863ebfbcac3475168b2de28b8b3d77544bb05ce92a02aceced3c0d
+d0cc65ea371b201cf1c601c24dde1c4078cedbdeb60322f50126a019bf6edc9b
+39e566b39b3517eaf97c3e0fbde5e4491d45bd74537145d155b476aa0176e868
+c6abebf30dbd5e525c54ac8e18e2d56abeb756827a3d970358a97416019a6f64
+f60004fdfe1580d5c98e618070cc1b05887eee7e0d209a70db7d8063029889b4
+c620ead78d7b33a7dc6c76b3e6427ddddbebde867c393aa7845e5403e8ca794a
+d0d6fb897af5f03525fe5782f5e7046bdaef468bf88d1debc6ab25583cd17310
+6079b9ab0ba059c914018245bf076075b5a303200c3c1f209a733701444fbbaf
+00c4134ebb016c5d0b23614c243701cdf875e3decce9349bddacb9505fbf7dfd
+76e82d87736a00f5d2b5ffd4b7dce2719a4d25ae717ee153c1abef18e257cfad
+7fa45682da48ef38c052b53b0fd06864b300c151ff08c0ea431de701a287dd5f
+004497dc7b01a253ee3e80b8c7f91c20f967fb6fdb7c80ada7d8683723614c24
+3701cdf875e3decc29379bddacb950ef3fd47f08f2e5a61ea4aa2a3eb757cd55
+13345efcfa59c12b2f19e2578ef77fb75a82854ffbee01a83f977b11a031931d
+040802df07082b5e11207cc17b1e209a770700e2df0a83e409fb7580f827c230
+99b06fd901fb058d6835dacd481813c94d40337eddb83773cacd66376b2ed437
+bebcf165e82d2f4e4beb7f3fa6e652c2d7ee10bc78c010bfb87fe3c95a09ae9f
+bd732740bd2fb700d0f865f64180e059ff044018ca0ca28a5b04883f701e0088
+bfec7c0c909cb71f0448c6ec518074b375012079d9dedf66004bcfbc51eb2dd1
+aadacd481813c94d40337eddb83773cacd66376b2ed487868686205fbe7c49ef
+5605a73f34c4a7a787eeab96e0da81bb4e022c15ba27019a5b339300e16bf286
+a8eae601e25866907cdf3e0890acb36f00245fb57f05904e59c300e92561946e
+b2e600d209ab7d07f04d458dfb46ad1bd16ab49b913026929b8066fcba716fe6
+949bcd6ed65ca8ef7e7cf7e3d05b7e7c8f217ee6cdddbb6a25a856f37980e0c7
+fe4e80a82623c48193014846ec7180f4acf518409aca0cd28a5504e03b32c374
+de1a00608a0240faaa327a4b19fe946fb6f90054dbb5f2333d022db56eb4966a
+3723614c243701cdf8f556bea8a7dc6c76b3e66bd46584ddbbcebc0990cf4b0f
+ff4070520c282338a7e26700ec725202b01e4bcf0258963c6f1d4d8f0030cb20
+805549c520930c03584fa522b676f11600ffc03fde3e1b3489a9c9054c9aa23b
+c08856a3dd8c843191dc0434e3d78d7b33a75c36fb993761f7ae5a69f72ef97f
+e6ad336fed7e1c60e8bee96980bbdebbb60da07b7069062033d9dc0ae03d296f
+70ab511ec071640676252902d833c916007b3e1900b0a6d2028035968e025861
+ea01581369fb11488c34d18cbc95989afccca42baad65ba2d5683723614c24d7
+8066fcbab8b7e96918baaf5aaa56219f975fb50a43f7c9bde90fa73f1c1a02d8
+78f2e27e803b77ca08b90519315b6fe400fc1392097a9eccc0ad444500e70199
+a1331f0f00d8934901c07e5d526ceb87c2d07e2579badd005a2b31a5089391b7
+1253358049535a6add8856dd0146c298482e01ede27ed878b256ba7600ee3a09
+c18fc1df09fe01084ec25defc1b56db0f1a4f4bd78e0e2818d2f0334e7330300
+7df7c888b917e50dd9c1c60c80efcb0cbc63e1f700bce7c31700dccbd1060027
+8add9b0de06c8e2f00d84962b7d7030e2a61538331b98051f92631bd253f336a
+dd8856a3dd44c25c390efddfad96ae9f853b77c25201ba27c533b8bdf28b6ad0
+3d084b33d2e7fa59099e9901b8f2d29597fa0f01848f78e70082117f1ca07b76
+6910209b9519f895a008d031bbba05c09d8f06005c5b18b8fba25300cea6780e
+c03e911c6ccf06d507b48a4fa606634a114609de929f9934c5a87511ad57cfc1
+fa476aa5854fa1ef1e3910b905686e85cc24c40138198915f133d2d6dc2a7dea
+7df2ccc2a752faf2cec1d577aebeb37e3b4034eeee0008dff3be0e6b923773b4
+7904c0ef9119767cb4fa1500ef1361e08e452500f71561e84cc4ed3e20fab6a2
+c905f40cb76a3026bf3319b91ac2e46792a6dcd801ebc6aba5da08f48ecb81c8
+bd088d5f42f6417191de93908c803d0e76199292b485af41b60e8d9c3c537f0e
+8211f0c7211a077707dc18b931b2ee6d80a4d7ae024491ebc24d4a708ff70680
+7f25e807e8785f1878e322d6ddaf453f0770ff2dfa769b01423dbbad72a391b6
+5a7c3235985629423372494cab55c8f7d64a8b27a0e7202c55a13b0f8d19c80e
+4ae9ca3f015115dc3ca467c17a4c7ee95970ab10e5a54ff0ac3cd39881ee5958
+1a84f03df0be0e492fd855a8d6aa35d10b4962dbb0a604a3d3ee5e80a8eee600
+a24977f8660378bf0bbf00e01d0a8fb7f980f04b8aa6ce6aca8d5a7533c52753
+839152c4e222f4dc512dd5eb90cbc981e8ea12cf90cd8a8bf47d89159e2741d3
+7124f65b96fcd254dae258fa84a13c13043246a32129574787e49eae2b49b86d
+c3e2e78b9ff7f4002415bb08907c66df0d103b4e0c104db90500ff70700c203a
+ee1e82dba4c3e16e256c0acca6ceaae9afd1f612d7eb472157ac95962bd05594
+7dd1598466053245088e827f44628657942a825b84e4fb601f84b4025611aca3
+901e01bb024911dc0a4445f08e41f83df02b10142173149ab71baf027611ea95
+7a257704201d14cd9af4d90b00f194530088cb4e09c0df1c5c0088f7393f6833
+c0aa3ac156655de3bca9b34ab9716906ba07aba5e5bba1eb3358d90b9da7c533
+64f6888bf47b60f521e8380fe10be03d2feac17900927560df40f4e48f805960
+50328d648bf4893f9067c217a0631656b7c898c122847bc07b03a2d3e0ee85e4
+33b0ef867450c4fad2ecd26cf7168074c0ba0c904cdac300c9cfec4701924df6
+1cdca61e10685c6f7d52d0caba1498972f43d740adb4b2009d7d7220b20e3473
+90a943d00ffe959bb6eac3e0fe42ea49ee00c45f06e76329b1dabf127d690d80
+5581b408f63c2403e0cc433c00ee658836803b0fd100747c04ab5f917704fd10
+d5c1cd41ec801343d207f602a403605d86e5f9e5f9ae0d00e994556833806685
+c931fb709b0f08b4e869bea5c827859549e82c544b8d29c816a0390999613920
+7e610d5727a16318c2003c1fa24be0de2b32caf92224e7c17e5004b6350c4c01
+05601218066b0ad28224e149019c086257ca315102de2712903bde97b8144d82
+3b2c6ac52d403c054e019249b087f53d0558995a99ea946c70cc927458b3c1ff
+550f30050df988d4284376b4566a8e416654cc921985e037e0df0fc131f00f4b
+acf0c6211c036f14a239703741740adc7da227edd7e56b833d0ae92549b4d357
+25dfb49ed2ff63908e6adf27d6d0dda7638d4154d2778daca17f58e61297c129
+41f233b01f5dc3740cac51688c35c6b22580f48224fee9b83502569a66b629f1
+09f3713473413e2666e7fe6f6c6efefdfafda1f56f6e06f93496d9d67cb7366a
+9964b6f92e64b689196ec6c604646fd3fe4771ff1bf03f65d8ecc3addbb5f300
+00000049454e44ae426082
+"""),
+}
+
+def read_pam_header(infile):
+    """
+    Read (the rest of a) PAM header.  `infile` should be positioned
+    immediately after the initial 'P7' line (at the beginning of the
+    second line).  Returns are as for `read_pnm_header`.
+    """
+    
+    # Unlike PBM, PGM, and PPM, we can read the header a line at a time.
+    header = dict()
+    while True:
+        l = infile.readline().strip()
+        if l == strtobytes('ENDHDR'):
+            break
+        if not l:
+            raise EOFError('PAM ended prematurely')
+        if l[0] == strtobytes('#'):
+            continue
+        l = l.split(None, 1)
+        if l[0] not in header:
+            header[l[0]] = l[1]
+        else:
+            header[l[0]] += strtobytes(' ') + l[1]
+
+    required = ['WIDTH', 'HEIGHT', 'DEPTH', 'MAXVAL']
+    required = [strtobytes(x) for x in required]
+    WIDTH,HEIGHT,DEPTH,MAXVAL = required
+    present = [x for x in required if x in header]
+    if len(present) != len(required):
+        raise Error('PAM file must specify WIDTH, HEIGHT, DEPTH, and MAXVAL')
+    width = int(header[WIDTH])
+    height = int(header[HEIGHT])
+    depth = int(header[DEPTH])
+    maxval = int(header[MAXVAL])
+    if (width <= 0 or
+        height <= 0 or
+        depth <= 0 or
+        maxval <= 0):
+        raise Error(
+          'WIDTH, HEIGHT, DEPTH, MAXVAL must all be positive integers')
+    return 'P7', width, height, depth, maxval
+
+def read_pnm_header(infile, supported=('P5','P6')):
+    """
+    Read a PNM header, returning (format,width,height,depth,maxval).
+    `width` and `height` are in pixels.  `depth` is the number of
+    channels in the image; for PBM and PGM it is synthesized as 1, for
+    PPM as 3; for PAM images it is read from the header.  `maxval` is
+    synthesized (as 1) for PBM images.
+    """
+
+    # Generally, see http://netpbm.sourceforge.net/doc/ppm.html
+    # and http://netpbm.sourceforge.net/doc/pam.html
+
+    supported = [strtobytes(x) for x in supported]
+
+    # Technically 'P7' must be followed by a newline, so by using
+    # rstrip() we are being liberal in what we accept.  I think this
+    # is acceptable.
+    type = infile.read(3).rstrip()
+    if type not in supported:
+        raise NotImplementedError('file format %s not supported' % type)
+    if type == strtobytes('P7'):
+        # PAM header parsing is completely different.
+        return read_pam_header(infile)
+    # Expected number of tokens in header (3 for P4, 4 for P6)
+    expected = 4
+    pbm = ('P1', 'P4')
+    if type in pbm:
+        expected = 3
+    header = [type]
+
+    # We have to read the rest of the header byte by byte because the
+    # final whitespace character (immediately following the MAXVAL in
+    # the case of P6) may not be a newline.  Of course all PNM files in
+    # the wild use a newline at this point, so it's tempting to use
+    # readline; but it would be wrong.
+    def getc():
+        c = infile.read(1)
+        if not c:
+            raise Error('premature EOF reading PNM header')
+        return c
+
+    c = getc()
+    while True:
+        # Skip whitespace that precedes a token.
+        while c.isspace():
+            c = getc()
+        # Skip comments.
+        while c == '#':
+            while c not in '\n\r':
+                c = getc()
+        if not c.isdigit():
+            raise Error('unexpected character %s found in header' % c)
+        # According to the specification it is legal to have comments
+        # that appear in the middle of a token.
+        # This is bonkers; I've never seen it; and it's a bit awkward to
+        # code good lexers in Python (no goto).  So we break on such
+        # cases.
+        token = strtobytes('')
+        while c.isdigit():
+            token += c
+            c = getc()
+        # Slight hack.  All "tokens" are decimal integers, so convert
+        # them here.
+        header.append(int(token))
+        if len(header) == expected:
+            break
+    # Skip comments (again)
+    while c == '#':
+        while c not in '\n\r':
+            c = getc()
+    if not c.isspace():
+        raise Error('expected header to end with whitespace, not %s' % c)
+
+    if type in pbm:
+        # synthesize a MAXVAL
+        header.append(1)
+    depth = (1,3)[type == strtobytes('P6')]
+    return header[0], header[1], header[2], depth, header[3]
+
+def write_pnm(file, width, height, pixels, meta):
+    """Write a Netpbm PNM/PAM file."""
+
+    bitdepth = meta['bitdepth']
+    maxval = 2**bitdepth - 1
+    # Rudely, the number of image planes can be used to determine
+    # whether we are L (PGM), LA (PAM), RGB (PPM), or RGBA (PAM).
+    planes = meta['planes']
+    # Can be an assert as long as we assume that pixels and meta came
+    # from a PNG file.
+    assert planes in (1,2,3,4)
+    if planes in (1,3):
+        if 1 == planes:
+            # PGM
+            # Could generate PBM if maxval is 1, but we don't (for one
+            # thing, we'd have to convert the data, not just blat it
+            # out).
+            fmt = 'P5'
+        else:
+            # PPM
+            fmt = 'P6'
+        file.write('%s %d %d %d\n' % (fmt, width, height, maxval))
+    if planes in (2,4):
+        # PAM
+        # See http://netpbm.sourceforge.net/doc/pam.html
+        if 2 == planes:
+            tupltype = 'GRAYSCALE_ALPHA'
+        else:
+            tupltype = 'RGB_ALPHA'
+        file.write('P7\nWIDTH %d\nHEIGHT %d\nDEPTH %d\nMAXVAL %d\n'
+                   'TUPLTYPE %s\nENDHDR\n' %
+                   (width, height, planes, maxval, tupltype))
+    # Values per row
+    vpr = planes * width
+    # struct format
+    fmt = '>%d' % vpr
+    if maxval > 0xff:
+        fmt = fmt + 'H'
+    else:
+        fmt = fmt + 'B'
+    for row in pixels:
+        file.write(struct.pack(fmt, *row))
+    file.flush()
+
+def color_triple(color):
+    """
+    Convert a command line colour value to a RGB triple of integers.
+    FIXME: Somewhere we need support for greyscale backgrounds etc.
+    """
+    if color.startswith('#') and len(color) == 4:
+        return (int(color[1], 16),
+                int(color[2], 16),
+                int(color[3], 16))
+    if color.startswith('#') and len(color) == 7:
+        return (int(color[1:3], 16),
+                int(color[3:5], 16),
+                int(color[5:7], 16))
+    elif color.startswith('#') and len(color) == 13:
+        return (int(color[1:5], 16),
+                int(color[5:9], 16),
+                int(color[9:13], 16))
+
+def _add_common_options(parser):
+    """Call *parser.add_option* for each of the options that are
+    common between this PNG--PNM conversion tool and the gen
+    tool.
+    """
+    parser.add_option("-i", "--interlace",
+                      default=False, action="store_true",
+                      help="create an interlaced PNG file (Adam7)")
+    parser.add_option("-t", "--transparent",
+                      action="store", type="string", metavar="#RRGGBB",
+                      help="mark the specified colour as transparent")
+    parser.add_option("-b", "--background",
+                      action="store", type="string", metavar="#RRGGBB",
+                      help="save the specified background colour")
+    parser.add_option("-g", "--gamma",
+                      action="store", type="float", metavar="value",
+                      help="save the specified gamma value")
+    parser.add_option("-c", "--compression",
+                      action="store", type="int", metavar="level",
+                      help="zlib compression level (0-9)")
+    return parser
+
+def _main(argv):
+    """
+    Run the PNG encoder with options from the command line.
+    """
+
+    # Parse command line arguments
+    from optparse import OptionParser
+    import re
+    version = '%prog ' + re.sub(r'( ?\$|URL: |Rev:)', '', __version__)
+    parser = OptionParser(version=version)
+    parser.set_usage("%prog [options] [imagefile]")
+    parser.add_option('-r', '--read-png', default=False,
+                      action='store_true',
+                      help='Read PNG, write PNM')
+    parser.add_option("-a", "--alpha",
+                      action="store", type="string", metavar="pgmfile",
+                      help="alpha channel transparency (RGBA)")
+    _add_common_options(parser)
+
+    (options, args) = parser.parse_args(args=argv[1:])
+
+    # Convert options
+    if options.transparent is not None:
+        options.transparent = color_triple(options.transparent)
+    if options.background is not None:
+        options.background = color_triple(options.background)
+
+    # Prepare input and output files
+    if len(args) == 0:
+        infilename = '-'
+        infile = sys.stdin
+    elif len(args) == 1:
+        infilename = args[0]
+        infile = open(infilename, 'rb')
+    else:
+        parser.error("more than one input file")
+    outfile = sys.stdout
+    if sys.platform == "win32":
+        import msvcrt, os
+        msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
+
+    if options.read_png:
+        # Encode PNG to PPM
+        png = Reader(file=infile)
+        width,height,pixels,meta = png.asDirect()
+        write_pnm(outfile, width, height, pixels, meta) 
+    else:
+        # Encode PNM to PNG
+        format, width, height, depth, maxval = \
+          read_pnm_header(infile, ('P5','P6','P7'))
+        # When it comes to the variety of input formats, we do something
+        # rather rude.  Observe that L, LA, RGB, RGBA are the 4 colour
+        # types supported by PNG and that they correspond to 1, 2, 3, 4
+        # channels respectively.  So we use the number of channels in
+        # the source image to determine which one we have.  We do not
+        # care about TUPLTYPE.
+        greyscale = depth <= 2
+        pamalpha = depth in (2,4)
+        supported = map(lambda x: 2**x-1, range(1,17))
+        try:
+            mi = supported.index(maxval)
+        except ValueError:
+            raise NotImplementedError(
+              'your maxval (%s) not in supported list %s' %
+              (maxval, str(supported)))
+        bitdepth = mi+1
+        writer = Writer(width, height,
+                        greyscale=greyscale,
+                        bitdepth=bitdepth,
+                        interlace=options.interlace,
+                        transparent=options.transparent,
+                        background=options.background,
+                        alpha=bool(pamalpha or options.alpha),
+                        gamma=options.gamma,
+                        compression=options.compression)
+        if options.alpha:
+            pgmfile = open(options.alpha, 'rb')
+            format, awidth, aheight, adepth, amaxval = \
+              read_pnm_header(pgmfile, 'P5')
+            if amaxval != '255':
+                raise NotImplementedError(
+                  'maxval %s not supported for alpha channel' % amaxval)
+            if (awidth, aheight) != (width, height):
+                raise ValueError("alpha channel image size mismatch"
+                                 " (%s has %sx%s but %s has %sx%s)"
+                                 % (infilename, width, height,
+                                    options.alpha, awidth, aheight))
+            writer.convert_ppm_and_pgm(infile, pgmfile, outfile)
+        else:
+            writer.convert_pnm(infile, outfile)
+
+
+if __name__ == '__main__':
+    try:
+        _main(sys.argv)
+    except Error, e:
+        print >>sys.stderr, e
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/README.chromium b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/README.chromium
new file mode 100644
index 0000000..6b9dac7
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/README.chromium
@@ -0,0 +1,16 @@
+Name: pyfakefs
+Short Name: pyfakefs
+URL: https://github.com/jmcgeheeiv/pyfakefs
+Version: 7e8e097c0165ba9d51fa9d34a0888d8ec082d15b (commit hash)
+License: Apache License 2.0
+License File: NOT_SHIPPED
+Security Critical: no
+
+Local modification: create a pyfakefs as a project folder & move pyfakefs to
+pyfakefs/pyfakefs since we don't want the project folder to be a module.
+
+Description:
+pyfakefs implements a fake file system that mocks the Python file system
+modules. Using pyfakefs, your tests operate on a fake file system in memory
+without touching the real disk. The software under test requires no modification
+to work with pyfakefs.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/COPYING b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/COPYING
new file mode 100644
index 0000000..67db858
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/COPYING
@@ -0,0 +1,175 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/README.md b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/README.md
new file mode 100644
index 0000000..ba3f5e5
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/README.md
@@ -0,0 +1,51 @@
+# pyfakefs
+pyfakefs implements a fake file system that mocks the Python file system modules.
+Using pyfakefs, your tests operate on a fake file system in memory without
+touching the real disk.  The software under test requires no modification to
+work with pyfakefs.
+
+## Usage
+See the [usage tutorial](http://github.com/jmcgeheeiv/pyfakefs/wiki/Tutorial)
+for a concrete example of how to apply pyfakefs.
+
+## Continuous Integration
+pyfakefs is tested with Python 2.6 and above.  See [Travis-CI](http://travis-ci.org) for
+[pyfakefs continuous integration test results](https://travis-ci.org/jmcgeheeiv/pyfakefs)
+for each Python version.
+
+## Installation
+
+### Compatibility
+pyfakefs works with Python 2.6 and above.  pyfakefs has no dependencies beyond the Python
+standard library.
+
+### PyPi
+pyfakefs project is hosted on PyPi and can be installed:
+
+```bash
+pip install pyfakefs
+```
+
+## History
+pyfakefs.py was initially developed at Google by Mike Bland as a modest fake
+implementation of core Python modules.  It was introduced to all of Google
+in September 2006. Since then, it has been enhanced to extend its
+functionality and usefulness.  At Google alone, pyfakefs is used in over 2,000
+Python tests.
+
+pyfakefs was released to the public in 2011 as Google Code project
+[pyfakefs](http://code.google.com/p/pyfakefs/).
+
+Fork
+[jmcgeheeiv-pyfakefs](http://code.google.com/p/jmcgeheeiv-pyfakefs/)
+added a [usage tutorial](http://github.com/jmcgeheeiv/pyfakefs/wiki/Tutorial),
+direct support for [unittest](http://docs.python.org/2/library/unittest.html)
+and [doctest](http://docs.python.org/2/library/doctest.html).
+
+Fork
+[shiffdane-jmcgeheeiv-pyfakefs](http://code.google.com/p/shiffdane-jmcgeheeiv-pyfakefs/)
+added further corrections.
+
+After the [shutdown of Google Code was announced,](http://google-opensource.blogspot.com/2015/03/farewell-to-google-code.html)
+all three Google Code projects are merged together here on GitHub as pyfakefs.
+
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/__init__.py
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/__init__.py
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/all_tests.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/all_tests.py
new file mode 100755
index 0000000..1226362
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/all_tests.py
@@ -0,0 +1,47 @@
+#! /usr/bin/env python
+#
+# Copyright 2009 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""A test suite that runs all tests for pyfakefs at once."""
+
+import unittest
+
+import fake_filesystem_glob_test
+import fake_filesystem_shutil_test
+import fake_filesystem_test
+import fake_filesystem_vs_real_test
+import fake_tempfile_test
+import fake_filesystem_unittest_test
+import example_test
+
+
+class AllTests(unittest.TestSuite):
+  """A test suite that runs all tests for pyfakefs at once."""
+
+  def suite(self):  # pylint: disable-msg=C6409
+    loader = unittest.defaultTestLoader
+    self.addTests([
+        loader.loadTestsFromModule(fake_filesystem_test),
+        loader.loadTestsFromModule(fake_filesystem_glob_test),
+        loader.loadTestsFromModule(fake_filesystem_shutil_test),
+        loader.loadTestsFromModule(fake_tempfile_test),
+        loader.loadTestsFromModule(fake_filesystem_vs_real_test),
+        loader.loadTestsFromModule(fake_filesystem_unittest_test),
+        loader.loadTestsFromModule(example_test),
+    ])
+    return self
+
+if __name__ == '__main__':
+  unittest.TextTestRunner(verbosity=2).run(AllTests().suite())
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/example.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/example.py
new file mode 100644
index 0000000..436e4cf
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/example.py
@@ -0,0 +1,121 @@
+# Copyright 2014 Altera Corporation. All Rights Reserved.
+# Author: John McGehee
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+Example module that is tested in :py:class`pyfakefs.example_test.TestExample`.
+This demonstrates the usage of the
+:py:class`pyfakefs.fake_filesystem_unittest.TestCase` base class.
+
+The modules related to file handling are bound to the respective fake modules:
+
+>>> os     #doctest: +ELLIPSIS 
+<fake_filesystem.FakeOsModule object...>
+>>> os.path     #doctest: +ELLIPSIS
+<fake_filesystem.FakePathModule object...>
+>>> glob     #doctest: +ELLIPSIS
+<fake_filesystem_glob.FakeGlobModule object...>
+>>> shutil     #doctest: +ELLIPSIS
+<fake_filesystem_shutil.FakeShutilModule object...>
+
+The `open()` built-in is bound to the fake `open()`:
+
+>>> open     #doctest: +ELLIPSIS
+<fake_filesystem.FakeFileOpen object...>
+
+In Python 2 the `file()` built-in is also bound to the fake `open()`.  `file()`
+was eliminated in Python 3.
+"""
+
+import os
+import glob
+import shutil
+
+def create_file(path):
+    '''Create the specified file and add some content to it.  Use the `open()`
+    built in function.
+    
+    For example, the following file operations occur in the fake file system.
+    In the real file system, we would not even have permission to write `/test`:
+    
+    >>> os.path.isdir('/test')
+    False
+    >>> os.mkdir('/test')
+    >>> os.path.isdir('/test')
+    True
+    >>> os.path.exists('/test/file.txt')
+    False
+    >>> create_file('/test/file.txt')
+    >>> os.path.exists('/test/file.txt')
+    True
+    >>> with open('/test/file.txt') as f:
+    ...     f.readlines()
+    ["This is test file '/test/file.txt'.\\n", 'It was created using the open() function.\\n']
+    '''
+    with open(path, 'w') as f:
+        f.write("This is test file '{}'.\n".format(path))
+        f.write("It was created using the open() function.\n")
+
+def delete_file(path):
+    '''Delete the specified file.
+    
+    For example:
+        
+    >>> os.mkdir('/test')
+    >>> os.path.exists('/test/file.txt')
+    False
+    >>> create_file('/test/file.txt')
+    >>> os.path.exists('/test/file.txt')
+    True
+    >>> delete_file('/test/file.txt')
+    >>> os.path.exists('/test/file.txt')
+    False
+    '''
+    os.remove(path)
+    
+def path_exists(path):
+    '''Return True if the specified file exists.
+    
+    For example:
+        
+    >>> path_exists('/test')
+    False
+    >>> os.mkdir('/test')
+    >>> path_exists('/test')
+    True
+    >>>
+    >>> path_exists('/test/file.txt')
+    False
+    >>> create_file('/test/file.txt')
+    >>> path_exists('/test/file.txt')
+    True
+    '''
+    return os.path.exists(path)
+
+def get_glob(glob_path):
+    '''Return the list of paths matching the specified glob expression.
+    
+    For example:
+    
+    >>> os.mkdir('/test')
+    >>> create_file('/test/file1.txt')
+    >>> create_file('/test/file2.txt')
+    >>> get_glob('/test/file*.txt')
+    ['/test/file1.txt', '/test/file2.txt']
+    '''
+    return glob.glob(glob_path)
+
+def rm_tree(path):
+    '''Delete the specified file hierarchy.'''
+    shutil.rmtree(path)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/example_test.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/example_test.py
new file mode 100644
index 0000000..4a46dd9
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/example_test.py
@@ -0,0 +1,137 @@
+#! /usr/bin/env python
+#
+# Copyright 2014 Altera Corporation. All Rights Reserved.
+# Author: John McGehee
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+Test the :py:class`pyfakefs.example` module to demonstrate the usage of the
+:py:class`pyfakefs.fake_filesystem_unittest.TestCase` base class.
+"""
+
+import os
+import sys
+if sys.version_info < (2, 7):
+    import unittest2 as unittest
+else:
+    import unittest
+
+import fake_filesystem_unittest
+# The module under test is pyfakefs.example
+import example
+
+
+def load_tests(loader, tests, ignore):
+    '''Load the pyfakefs/example.py doctest tests into unittest.'''
+    return fake_filesystem_unittest.load_doctests(loader, tests, ignore, example)
+
+
+class TestExample(fake_filesystem_unittest.TestCase): # pylint: disable=R0904
+    '''Test the pyfakefs.example module.'''
+
+    def setUp(self):
+        '''Invoke the :py:class:`pyfakefs.fake_filesystem_unittest.TestCase`
+        `self.setUp()` method.  This defines:
+        
+        * Attribute `self.fs`, an instance of \
+          :py:class:`pyfakefs.fake_filesystem.FakeFilesystem`.  This is useful \
+          for creating test files.
+        * Attribute `self.stubs`, an instance of \
+          :py:class:`mox.stubout.StubOutForTesting`.  Use this if you need to
+          define additional stubs.
+        '''
+        self.setUpPyfakefs()
+
+    def tearDown(self):
+        # No longer need self.tearDownPyfakefs()
+        pass
+        
+    def test_create_file(self):
+        '''Test example.create_file()'''
+        # The os module has been replaced with the fake os module so all of the
+        # following occurs in the fake filesystem.
+        self.assertFalse(os.path.isdir('/test'))
+        os.mkdir('/test')
+        self.assertTrue(os.path.isdir('/test'))
+        
+        self.assertFalse(os.path.exists('/test/file.txt'))
+        example.create_file('/test/file.txt')
+        self.assertTrue(os.path.exists('/test/file.txt'))
+        
+    def test_delete_file(self):
+        '''Test example.delete_file()
+
+        `self.fs.CreateFile()` is convenient because it automatically creates
+        directories in the fake file system and allows you to specify the file
+        contents.
+        
+        You could also use `open()` or `file()`.
+        '''
+        self.fs.CreateFile('/test/full.txt',
+                           contents='First line\n'
+                                    'Second Line\n')
+        self.assertTrue(os.path.exists('/test/full.txt'))
+        example.delete_file('/test/full.txt')
+        self.assertFalse(os.path.exists('/test/full.txt'))
+
+    def test_file_exists(self):
+        '''Test example.path_exists()
+
+        `self.fs.CreateFile()` is convenient because it automatically creates
+        directories in the fake file system and allows you to specify the file
+        contents.
+        
+        You could also use `open()` or `file()` if you wanted.
+        '''
+        self.assertFalse(example.path_exists('/test/empty.txt'))          
+        self.fs.CreateFile('/test/empty.txt')
+        self.assertTrue(example.path_exists('/test/empty.txt'))              
+        
+    def test_get_globs(self):
+        '''Test example.get_glob()
+        
+        `self.fs.CreateDirectory()` creates directories.  However, you might
+        prefer the familiar `os.makedirs()`, which also works fine on the fake
+        file system.
+        '''
+        self.assertFalse(os.path.isdir('/test'))
+        self.fs.CreateDirectory('/test/dir1/dir2a')
+        self.assertTrue(os.path.isdir('/test/dir1/dir2a'))
+        # os.mkdirs() works, too.
+        os.makedirs('/test/dir1/dir2b')
+        self.assertTrue(os.path.isdir('/test/dir1/dir2b'))
+        
+        self.assertCountEqual(example.get_glob('/test/dir1/nonexistent*'),
+                              [])
+        self.assertCountEqual(example.get_glob('/test/dir1/dir*'),
+                              ['/test/dir1/dir2a', '/test/dir1/dir2b'])
+
+    def test_rm_tree(self):
+        '''Test example.rm_tree()
+        
+        `self.fs.CreateDirectory()` creates directories.  However, you might
+        prefer the familiar `os.makedirs()`, which also works fine on the fake
+        file system.
+        '''
+        self.fs.CreateDirectory('/test/dir1/dir2a')
+        # os.mkdirs() works, too.
+        os.makedirs('/test/dir1/dir2b')
+        self.assertTrue(os.path.isdir('/test/dir1/dir2b'))
+        self.assertTrue(os.path.isdir('/test/dir1/dir2a'))
+       
+        example.rm_tree('/test/dir1')
+        self.assertFalse(os.path.exists('/test/dir1'))
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/fake_filesystem.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/fake_filesystem.py
new file mode 100644
index 0000000..d390e66
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/fake_filesystem.py
@@ -0,0 +1,2202 @@
+# Copyright 2009 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# pylint: disable-msg=W0612,W0613,C6409
+
+"""A fake filesystem implementation for unit testing.
+
+Includes:
+  FakeFile:  Provides the appearance of a real file.
+  FakeDirectory: Provides the appearance of a real dir.
+  FakeFilesystem:  Provides the appearance of a real directory hierarchy.
+  FakeOsModule:  Uses FakeFilesystem to provide a fake os module replacement.
+  FakePathModule:  Faked os.path module replacement.
+  FakeFileOpen:  Faked file() and open() function replacements.
+
+Usage:
+>>> import fake_filesystem
+>>> filesystem = fake_filesystem.FakeFilesystem()
+>>> os_module = fake_filesystem.FakeOsModule(filesystem)
+>>> pathname = '/a/new/dir/new-file'
+
+Create a new file object, creating parent directory objects as needed:
+>>> os_module.path.exists(pathname)
+False
+>>> new_file = filesystem.CreateFile(pathname)
+
+File objects can't be overwritten:
+>>> os_module.path.exists(pathname)
+True
+>>> try:
+...   filesystem.CreateFile(pathname)
+... except IOError as e:
+...   assert e.errno == errno.EEXIST, 'unexpected errno: %d' % e.errno
+...   assert e.strerror == 'File already exists in fake filesystem'
+
+Remove a file object:
+>>> filesystem.RemoveObject(pathname)
+>>> os_module.path.exists(pathname)
+False
+
+Create a new file object at the previous path:
+>>> beatles_file = filesystem.CreateFile(pathname,
+...     contents='Dear Prudence\\nWon\\'t you come out to play?\\n')
+>>> os_module.path.exists(pathname)
+True
+
+Use the FakeFileOpen class to read fake file objects:
+>>> file_module = fake_filesystem.FakeFileOpen(filesystem)
+>>> for line in file_module(pathname):
+...     print line.rstrip()
+...
+Dear Prudence
+Won't you come out to play?
+
+File objects cannot be treated like directory objects:
+>>> os_module.listdir(pathname)  #doctest: +NORMALIZE_WHITESPACE
+Traceback (most recent call last):
+  File "fake_filesystem.py", line 291, in listdir
+    raise OSError(errno.ENOTDIR,
+OSError: [Errno 20] Fake os module: not a directory: '/a/new/dir/new-file'
+
+The FakeOsModule can list fake directory objects:
+>>> os_module.listdir(os_module.path.dirname(pathname))
+['new-file']
+
+The FakeOsModule also supports stat operations:
+>>> import stat
+>>> stat.S_ISREG(os_module.stat(pathname).st_mode)
+True
+>>> stat.S_ISDIR(os_module.stat(os_module.path.dirname(pathname)).st_mode)
+True
+"""
+
+import errno
+import heapq
+import os
+import stat
+import sys
+import time
+import warnings
+try:
+  import cStringIO as io  # pylint: disable-msg=C6204
+except ImportError:
+  import io  # pylint: disable-msg=C6204
+
+__pychecker__ = 'no-reimportself'
+
+__version__ = '2.5'
+
+PERM_READ = 0o400      # Read permission bit.
+PERM_WRITE = 0o200     # Write permission bit.
+PERM_EXE = 0o100       # Write permission bit.
+PERM_DEF = 0o777       # Default permission bits.
+PERM_DEF_FILE = 0o666  # Default permission bits (regular file)
+PERM_ALL = 0o7777      # All permission bits.
+
+_OPEN_MODE_MAP = {
+    # mode name:(file must exist, need read, need write,
+    #            truncate [implies need write], append)
+    'r': (True, True, False, False, False),
+    'w': (False, False, True, True, False),
+    'a': (False, False, True, False, True),
+    'r+': (True, True, True, False, False),
+    'w+': (False, True, True, True, False),
+    'a+': (False, True, True, False, True),
+    }
+
+_MAX_LINK_DEPTH = 20
+
+FAKE_PATH_MODULE_DEPRECATION = ('Do not instantiate a FakePathModule directly; '
+                                'let FakeOsModule instantiate it.  See the '
+                                'FakeOsModule docstring for details.')
+
+
+class Error(Exception):
+  pass
+
+_is_windows = sys.platform.startswith('win')
+_is_cygwin = sys.platform == 'cygwin'
+
+if _is_windows:
+  # On Windows, raise WindowsError instead of OSError if available
+  OSError = WindowsError  # pylint: disable-msg=E0602,W0622
+
+
+class FakeLargeFileIoException(Error):
+  def __init__(self, file_path):
+    Error.__init__(self,
+                   'Read and write operations not supported for '
+                   'fake large file: %s' % file_path)
+
+
+def CopyModule(old):
+  """Recompiles and creates new module object."""
+  saved = sys.modules.pop(old.__name__, None)
+  new = __import__(old.__name__)
+  sys.modules[old.__name__] = saved
+  return new
+
+
+class FakeFile(object):
+  """Provides the appearance of a real file.
+
+     Attributes currently faked out:
+       st_mode: user-specified, otherwise S_IFREG
+       st_ctime: the time.time() timestamp when the file is created.
+       st_size: the size of the file
+
+     Other attributes needed by os.stat are assigned default value of None
+      these include: st_ino, st_dev, st_nlink, st_uid, st_gid, st_atime,
+      st_mtime
+  """
+
+  def __init__(self, name, st_mode=stat.S_IFREG | PERM_DEF_FILE,
+               contents=None):
+    """init.
+
+    Args:
+      name:  name of the file/directory, without parent path information
+      st_mode:  the stat.S_IF* constant representing the file type (i.e.
+        stat.S_IFREG, stat.SIFDIR)
+      contents:  the contents of the filesystem object; should be a string for
+        regular files, and a list of other FakeFile or FakeDirectory objects
+        for FakeDirectory objects
+    """
+    self.name = name
+    self.st_mode = st_mode
+    self.contents = contents
+    self.epoch = 0
+    self.st_ctime = int(time.time())
+    self.st_atime = self.st_ctime
+    self.st_mtime = self.st_ctime
+    if contents:
+      self.st_size = len(contents)
+    else:
+      self.st_size = 0
+    # Non faked features, write setter methods for fakeing them
+    self.st_ino = None
+    self.st_dev = None
+    self.st_nlink = None
+    self.st_uid = None
+    self.st_gid = None
+
+  def SetLargeFileSize(self, st_size):
+    """Sets the self.st_size attribute and replaces self.content with None.
+
+    Provided specifically to simulate very large files without regards
+    to their content (which wouldn't fit in memory)
+
+    Args:
+      st_size: The desired file size
+
+    Raises:
+      IOError: if the st_size is not a non-negative integer
+    """
+    # the st_size should be an positive integer value
+    if not isinstance(st_size, int) or st_size < 0:
+      raise IOError(errno.ENOSPC,
+                    'Fake file object: can not create non negative integer '
+                    'size=%r fake file' % st_size,
+                    self.name)
+
+    self.st_size = st_size
+    self.contents = None
+
+  def IsLargeFile(self):
+    """Return True if this file was initialized with size but no contents."""
+    return self.contents is None
+
+  def SetContents(self, contents):
+    """Sets the file contents and size.
+
+    Args:
+      contents: string, new content of file.
+    """
+    # convert a byte array to a string
+    if sys.version_info >= (3, 0) and isinstance(contents, bytes):
+      contents = ''.join(chr(i) for i in contents)
+    self.contents = contents
+    self.st_size = len(contents)
+    self.epoch += 1
+
+  def SetSize(self, st_size):
+    """Resizes file content, padding with nulls if new size exceeds the old.
+
+    Args:
+      st_size: The desired size for the file.
+
+    Raises:
+      IOError: if the st_size arg is not a non-negative integer
+    """
+
+    if not isinstance(st_size, int) or st_size < 0:
+      raise IOError(errno.ENOSPC,
+                    'Fake file object: can not create non negative integer '
+                    'size=%r fake file' % st_size,
+                    self.name)
+
+    current_size = len(self.contents)
+    if st_size < current_size:
+      self.contents = self.contents[:st_size]
+    else:
+      self.contents = '%s%s' % (self.contents, '\0' * (st_size - current_size))
+    self.st_size = len(self.contents)
+    self.epoch += 1
+
+  def SetATime(self, st_atime):
+    """Set the self.st_atime attribute.
+
+    Args:
+      st_atime: The desired atime.
+    """
+    self.st_atime = st_atime
+
+  def SetMTime(self, st_mtime):
+    """Set the self.st_mtime attribute.
+
+    Args:
+      st_mtime: The desired mtime.
+    """
+    self.st_mtime = st_mtime
+
+  def __str__(self):
+    return '%s(%o)' % (self.name, self.st_mode)
+
+  def SetIno(self, st_ino):
+    """Set the self.st_ino attribute.
+
+    Args:
+      st_ino: The desired inode.
+    """
+    self.st_ino = st_ino
+
+
+class FakeDirectory(FakeFile):
+  """Provides the appearance of a real dir."""
+
+  def __init__(self, name, perm_bits=PERM_DEF):
+    """init.
+
+    Args:
+      name:  name of the file/directory, without parent path information
+      perm_bits: permission bits. defaults to 0o777.
+    """
+    FakeFile.__init__(self, name, stat.S_IFDIR | perm_bits, {})
+
+  def AddEntry(self, pathname):
+    """Adds a child FakeFile to this directory.
+
+    Args:
+      pathname:  FakeFile instance to add as a child of this directory
+    """
+    self.contents[pathname.name] = pathname
+
+  def GetEntry(self, pathname_name):
+    """Retrieves the specified child file or directory.
+
+    Args:
+      pathname_name: basename of the child object to retrieve
+    Returns:
+      string, file contents
+    Raises:
+      KeyError: if no child exists by the specified name
+    """
+    return self.contents[pathname_name]
+
+  def RemoveEntry(self, pathname_name):
+    """Removes the specified child file or directory.
+
+    Args:
+      pathname_name: basename of the child object to remove
+
+    Raises:
+      KeyError: if no child exists by the specified name
+    """
+    del self.contents[pathname_name]
+
+  def __str__(self):
+    rc = super(FakeDirectory, self).__str__() + ':\n'
+    for item in self.contents:
+      item_desc = self.contents[item].__str__()
+      for line in item_desc.split('\n'):
+        if line:
+          rc = rc + '  ' + line + '\n'
+    return rc
+
+
+class FakeFilesystem(object):
+  """Provides the appearance of a real directory tree for unit testing."""
+
+  def __init__(self, path_separator=os.path.sep):
+    """init.
+
+    Args:
+      path_separator:  optional substitute for os.path.sep
+    """
+    self.path_separator = path_separator
+    self.root = FakeDirectory(self.path_separator)
+    self.cwd = self.root.name
+    # We can't query the current value without changing it:
+    self.umask = os.umask(0o22)
+    os.umask(self.umask)
+    # A list of open file objects. Their position in the list is their
+    # file descriptor number
+    self.open_files = []
+    # A heap containing all free positions in self.open_files list
+    self.free_fd_heap = []
+
+  def SetIno(self, path, st_ino):
+    """Set the self.st_ino attribute of file at 'path'.
+
+    Args:
+      path: Path to file.
+      st_ino: The desired inode.
+    """
+    self.GetObject(path).SetIno(st_ino)
+
+  def AddOpenFile(self, file_obj):
+    """Adds file_obj to the list of open files on the filesystem.
+
+    The position in the self.open_files array is the file descriptor number
+
+    Args:
+      file_obj:  file object to be added to open files list.
+
+    Returns:
+      File descriptor number for the file object.
+    """
+    if self.free_fd_heap:
+      open_fd = heapq.heappop(self.free_fd_heap)
+      self.open_files[open_fd] = file_obj
+      return open_fd
+
+    self.open_files.append(file_obj)
+    return len(self.open_files) - 1
+
+  def CloseOpenFile(self, file_obj):
+    """Removes file_obj from the list of open files on the filesystem.
+
+    Sets the entry in open_files to None.
+
+    Args:
+      file_obj:  file object to be removed to open files list.
+    """
+    self.open_files[file_obj.filedes] = None
+    heapq.heappush(self.free_fd_heap, file_obj.filedes)
+
+  def GetOpenFile(self, file_des):
+    """Returns an open file.
+
+    Args:
+      file_des:  file descriptor of the open file.
+
+    Raises:
+      OSError: an invalid file descriptor.
+      TypeError: filedes is not an integer.
+
+    Returns:
+      Open file object.
+    """
+    if not isinstance(file_des, int):
+      raise TypeError('an integer is required')
+    if (file_des >= len(self.open_files) or
+        self.open_files[file_des] is None):
+      raise OSError(errno.EBADF, 'Bad file descriptor', file_des)
+    return self.open_files[file_des]
+
+  def CollapsePath(self, path):
+    """Mimics os.path.normpath using the specified path_separator.
+
+    Mimics os.path.normpath using the path_separator that was specified
+    for this FakeFilesystem.  Normalizes the path, but unlike the method
+    NormalizePath, does not make it absolute.  Eliminates dot components
+    (. and ..) and combines repeated path separators (//).  Initial ..
+    components are left in place for relative paths.  If the result is an empty
+    path, '.' is returned instead.  Unlike the real os.path.normpath, this does
+    not replace '/' with '\\' on Windows.
+
+    Args:
+      path:  (str) The path to normalize.
+
+    Returns:
+      (str) A copy of path with empty components and dot components removed.
+    """
+    is_absolute_path = path.startswith(self.path_separator)
+    path_components = path.split(self.path_separator)
+    collapsed_path_components = []
+    for component in path_components:
+      if (not component) or (component == '.'):
+        continue
+      if component == '..':
+        if collapsed_path_components and (
+            collapsed_path_components[-1] != '..'):
+          # Remove an up-reference: directory/..
+          collapsed_path_components.pop()
+          continue
+        elif is_absolute_path:
+          # Ignore leading .. components if starting from the root directory.
+          continue
+      collapsed_path_components.append(component)
+    collapsed_path = self.path_separator.join(collapsed_path_components)
+    if is_absolute_path:
+      collapsed_path = self.path_separator + collapsed_path
+    return collapsed_path or '.'
+
+  def NormalizePath(self, path):
+    """Absolutize and minimalize the given path.
+
+    Forces all relative paths to be absolute, and normalizes the path to
+    eliminate dot and empty components.
+
+    Args:
+      path:  path to normalize
+
+    Returns:
+      The normalized path relative to the current working directory, or the root
+        directory if path is empty.
+    """
+    if not path:
+      path = self.path_separator
+    elif not path.startswith(self.path_separator):
+      # Prefix relative paths with cwd, if cwd is not root.
+      path = self.path_separator.join(
+          (self.cwd != self.root.name and self.cwd or '',
+           path))
+    if path == '.':
+      path = self.cwd
+    return self.CollapsePath(path)
+
+  def SplitPath(self, path):
+    """Mimics os.path.split using the specified path_separator.
+
+    Mimics os.path.split using the path_separator that was specified
+    for this FakeFilesystem.
+
+    Args:
+      path:  (str) The path to split.
+
+    Returns:
+      (str) A duple (pathname, basename) for which pathname does not
+          end with a slash, and basename does not contain a slash.
+    """
+    path_components = path.split(self.path_separator)
+    if not path_components:
+      return ('', '')
+    basename = path_components.pop()
+    if not path_components:
+      return ('', basename)
+    for component in path_components:
+      if component:
+        # The path is not the root; it contains a non-separator component.
+        # Strip all trailing separators.
+        while not path_components[-1]:
+          path_components.pop()
+        return (self.path_separator.join(path_components), basename)
+    # Root path.  Collapse all leading separators.
+    return (self.path_separator, basename)
+
+  def JoinPaths(self, *paths):
+    """Mimics os.path.join using the specified path_separator.
+
+    Mimics os.path.join using the path_separator that was specified
+    for this FakeFilesystem.
+
+    Args:
+      *paths:  (str) Zero or more paths to join.
+
+    Returns:
+      (str) The paths joined by the path separator, starting with the last
+          absolute path in paths.
+    """
+    if len(paths) == 1:
+      return paths[0]
+    joined_path_segments = []
+    for path_segment in paths:
+      if path_segment.startswith(self.path_separator):
+        # An absolute path
+        joined_path_segments = [path_segment]
+      else:
+        if (joined_path_segments and
+            not joined_path_segments[-1].endswith(self.path_separator)):
+          joined_path_segments.append(self.path_separator)
+        if path_segment:
+          joined_path_segments.append(path_segment)
+    return ''.join(joined_path_segments)
+
+  def GetPathComponents(self, path):
+    """Breaks the path into a list of component names.
+
+    Does not include the root directory as a component, as all paths
+    are considered relative to the root directory for the FakeFilesystem.
+    Callers should basically follow this pattern:
+
+      file_path = self.NormalizePath(file_path)
+      path_components = self.GetPathComponents(file_path)
+      current_dir = self.root
+      for component in path_components:
+        if component not in current_dir.contents:
+          raise IOError
+        DoStuffWithComponent(curent_dir, component)
+        current_dir = current_dir.GetEntry(component)
+
+    Args:
+      path:  path to tokenize
+
+    Returns:
+      The list of names split from path
+    """
+    if not path or path == self.root.name:
+      return []
+    path_components = path.split(self.path_separator)
+    assert path_components
+    if not path_components[0]:
+      # This is an absolute path.
+      path_components = path_components[1:]
+    return path_components
+
+  def Exists(self, file_path):
+    """True if a path points to an existing file system object.
+
+    Args:
+      file_path:  path to examine
+
+    Returns:
+      bool(if object exists)
+
+    Raises:
+      TypeError: if file_path is None
+    """
+    if file_path is None:
+      raise TypeError
+    if not file_path:
+      return False
+    try:
+      file_path = self.ResolvePath(file_path)
+    except IOError:
+      return False
+    if file_path == self.root.name:
+      return True
+    path_components = self.GetPathComponents(file_path)
+    current_dir = self.root
+    for component in path_components:
+      if component not in current_dir.contents:
+        return False
+      current_dir = current_dir.contents[component]
+    return True
+
+  def ResolvePath(self, file_path):
+    """Follow a path, resolving symlinks.
+
+    ResolvePath traverses the filesystem along the specified file path,
+    resolving file names and symbolic links until all elements of the path are
+    exhausted, or we reach a file which does not exist.  If all the elements
+    are not consumed, they just get appended to the path resolved so far.
+    This gives us the path which is as resolved as it can be, even if the file
+    does not exist.
+
+    This behavior mimics Unix semantics, and is best shown by example.  Given a
+    file system that looks like this:
+
+          /a/b/
+          /a/b/c -> /a/b2          c is a symlink to /a/b2
+          /a/b2/x
+          /a/c   -> ../d
+          /a/x   -> y
+     Then:
+          /a/b/x      =>  /a/b/x
+          /a/c        =>  /a/d
+          /a/x        =>  /a/y
+          /a/b/c/d/e  =>  /a/b2/d/e
+
+    Args:
+      file_path:  path to examine
+
+    Returns:
+      resolved_path (string) or None
+
+    Raises:
+      TypeError: if file_path is None
+      IOError: if file_path is '' or a part of the path doesn't exist
+    """
+
+    def _ComponentsToPath(component_folders):
+      return '%s%s' % (self.path_separator,
+                       self.path_separator.join(component_folders))
+
+    def _ValidRelativePath(file_path):
+      while file_path and '/..' in file_path:
+        file_path = file_path[:file_path.rfind('/..')]
+        if not self.Exists(self.NormalizePath(file_path)):
+          return False
+      return True
+
+    def _FollowLink(link_path_components, link):
+      """Follow a link w.r.t. a path resolved so far.
+
+      The component is either a real file, which is a no-op, or a symlink.
+      In the case of a symlink, we have to modify the path as built up so far
+        /a/b => ../c   should yield /a/../c (which will normalize to /a/c)
+        /a/b => x      should yield /a/x
+        /a/b => /x/y/z should yield /x/y/z
+      The modified path may land us in a new spot which is itself a
+      link, so we may repeat the process.
+
+      Args:
+        link_path_components: The resolved path built up to the link so far.
+        link: The link object itself.
+
+      Returns:
+        (string) the updated path resolved after following the link.
+
+      Raises:
+        IOError: if there are too many levels of symbolic link
+      """
+      link_path = link.contents
+      # For links to absolute paths, we want to throw out everything in the
+      # path built so far and replace with the link.  For relative links, we
+      # have to append the link to what we have so far,
+      if not link_path.startswith(self.path_separator):
+        # Relative path.  Append remainder of path to what we have processed
+        # so far, excluding the name of the link itself.
+        # /a/b => ../c   should yield /a/../c (which will normalize to /c)
+        # /a/b => d should yield a/d
+        components = link_path_components[:-1]
+        components.append(link_path)
+        link_path = self.path_separator.join(components)
+      # Don't call self.NormalizePath(), as we don't want to prepend self.cwd.
+      return self.CollapsePath(link_path)
+
+    if file_path is None:
+      # file.open(None) raises TypeError, so mimic that.
+      raise TypeError('Expected file system path string, received None')
+    if not file_path or not _ValidRelativePath(file_path):
+      # file.open('') raises IOError, so mimic that, and validate that all
+      # parts of a relative path exist.
+      raise IOError(errno.ENOENT,
+                    'No such file or directory: \'%s\'' % file_path)
+    file_path = self.NormalizePath(file_path)
+    if file_path == self.root.name:
+      return file_path
+
+    current_dir = self.root
+    path_components = self.GetPathComponents(file_path)
+
+    resolved_components = []
+    link_depth = 0
+    while path_components:
+      component = path_components.pop(0)
+      resolved_components.append(component)
+      if component not in current_dir.contents:
+        # The component of the path at this point does not actually exist in
+        # the folder.   We can't resolve the path any more.  It is legal to link
+        # to a file that does not yet exist, so rather than raise an error, we
+        # just append the remaining components to what return path we have built
+        # so far and return that.
+        resolved_components.extend(path_components)
+        break
+      current_dir = current_dir.contents[component]
+
+      # Resolve any possible symlinks in the current path component.
+      if stat.S_ISLNK(current_dir.st_mode):
+        # This link_depth check is not really meant to be an accurate check.
+        # It is just a quick hack to prevent us from looping forever on
+        # cycles.
+        link_depth += 1
+        if link_depth > _MAX_LINK_DEPTH:
+          raise IOError(errno.EMLINK,
+                        'Too many levels of symbolic links: \'%s\'' %
+                        _ComponentsToPath(resolved_components))
+        link_path = _FollowLink(resolved_components, current_dir)
+
+        # Following the link might result in the complete replacement of the
+        # current_dir, so we evaluate the entire resulting path.
+        target_components = self.GetPathComponents(link_path)
+        path_components = target_components + path_components
+        resolved_components = []
+        current_dir = self.root
+    return _ComponentsToPath(resolved_components)
+
+  def GetObjectFromNormalizedPath(self, file_path):
+    """Searches for the specified filesystem object within the fake filesystem.
+
+    Args:
+      file_path: specifies target FakeFile object to retrieve, with a
+          path that has already been normalized/resolved
+
+    Returns:
+      the FakeFile object corresponding to file_path
+
+    Raises:
+      IOError: if the object is not found
+    """
+    if file_path == self.root.name:
+      return self.root
+    path_components = self.GetPathComponents(file_path)
+    target_object = self.root
+    try:
+      for component in path_components:
+        if not isinstance(target_object, FakeDirectory):
+          raise IOError(errno.ENOENT,
+                        'No such file or directory in fake filesystem',
+                        file_path)
+        target_object = target_object.GetEntry(component)
+    except KeyError:
+      raise IOError(errno.ENOENT,
+                    'No such file or directory in fake filesystem',
+                    file_path)
+    return target_object
+
+  def GetObject(self, file_path):
+    """Searches for the specified filesystem object within the fake filesystem.
+
+    Args:
+      file_path: specifies target FakeFile object to retrieve
+
+    Returns:
+      the FakeFile object corresponding to file_path
+
+    Raises:
+      IOError: if the object is not found
+    """
+    file_path = self.NormalizePath(file_path)
+    return self.GetObjectFromNormalizedPath(file_path)
+
+  def ResolveObject(self, file_path):
+    """Searches for the specified filesystem object, resolving all links.
+
+    Args:
+      file_path: specifies target FakeFile object to retrieve
+
+    Returns:
+      the FakeFile object corresponding to file_path
+
+    Raises:
+      IOError: if the object is not found
+    """
+    return self.GetObjectFromNormalizedPath(self.ResolvePath(file_path))
+
+  def LResolveObject(self, path):
+    """Searches for the specified object, resolving only parent links.
+
+    This is analogous to the stat/lstat difference.  This resolves links *to*
+    the object but not of the final object itself.
+
+    Args:
+      path: specifies target FakeFile object to retrieve
+
+    Returns:
+      the FakeFile object corresponding to path
+
+    Raises:
+      IOError: if the object is not found
+    """
+    if path == self.root.name:
+      # The root directory will never be a link
+      return self.root
+    parent_directory, child_name = self.SplitPath(path)
+    if not parent_directory:
+      parent_directory = self.cwd
+    try:
+      parent_obj = self.ResolveObject(parent_directory)
+      assert parent_obj
+      if not isinstance(parent_obj, FakeDirectory):
+        raise IOError(errno.ENOENT,
+                      'No such file or directory in fake filesystem',
+                      path)
+      return parent_obj.GetEntry(child_name)
+    except KeyError:
+      raise IOError(errno.ENOENT,
+                    'No such file or directory in the fake filesystem',
+                    path)
+
+  def AddObject(self, file_path, file_object):
+    """Add a fake file or directory into the filesystem at file_path.
+
+    Args:
+      file_path: the path to the file to be added relative to self
+      file_object: file or directory to add
+
+    Raises:
+      IOError: if file_path does not correspond to a directory
+    """
+    try:
+      target_directory = self.GetObject(file_path)
+      target_directory.AddEntry(file_object)
+    except AttributeError:
+      raise IOError(errno.ENOTDIR,
+                    'Not a directory in the fake filesystem',
+                    file_path)
+
+  def RemoveObject(self, file_path):
+    """Remove an existing file or directory.
+
+    Args:
+      file_path: the path to the file relative to self
+
+    Raises:
+      IOError: if file_path does not correspond to an existing file, or if part
+        of the path refers to something other than a directory
+      OSError: if the directory is in use (eg, if it is '/')
+    """
+    if file_path == self.root.name:
+      raise OSError(errno.EBUSY, 'Fake device or resource busy',
+                    file_path)
+    try:
+      dirname, basename = self.SplitPath(file_path)
+      target_directory = self.GetObject(dirname)
+      target_directory.RemoveEntry(basename)
+    except KeyError:
+      raise IOError(errno.ENOENT,
+                    'No such file or directory in the fake filesystem',
+                    file_path)
+    except AttributeError:
+      raise IOError(errno.ENOTDIR,
+                    'Not a directory in the fake filesystem',
+                    file_path)
+
+  def CreateDirectory(self, directory_path, perm_bits=PERM_DEF, inode=None):
+    """Creates directory_path, and all the parent directories.
+
+    Helper method to set up your test faster
+
+    Args:
+      directory_path:  directory to create
+      perm_bits: permission bits
+      inode: inode of directory
+
+    Returns:
+      the newly created FakeDirectory object
+
+    Raises:
+      OSError:  if the directory already exists
+    """
+    directory_path = self.NormalizePath(directory_path)
+    if self.Exists(directory_path):
+      raise OSError(errno.EEXIST,
+                    'Directory exists in fake filesystem',
+                    directory_path)
+    path_components = self.GetPathComponents(directory_path)
+    current_dir = self.root
+
+    for component in path_components:
+      if component not in current_dir.contents:
+        new_dir = FakeDirectory(component, perm_bits)
+        current_dir.AddEntry(new_dir)
+        current_dir = new_dir
+      else:
+        current_dir = current_dir.contents[component]
+
+    current_dir.SetIno(inode)
+    return current_dir
+
+  def CreateFile(self, file_path, st_mode=stat.S_IFREG | PERM_DEF_FILE,
+                 contents='', st_size=None, create_missing_dirs=True,
+                 apply_umask=False, inode=None):
+    """Creates file_path, including all the parent directories along the way.
+
+    Helper method to set up your test faster.
+
+    Args:
+      file_path: path to the file to create
+      st_mode: the stat.S_IF constant representing the file type
+      contents: the contents of the file
+      st_size: file size; only valid if contents=None
+      create_missing_dirs: if True, auto create missing directories
+      apply_umask: whether or not the current umask must be applied on st_mode
+      inode: inode of the file
+
+    Returns:
+      the newly created FakeFile object
+
+    Raises:
+      IOError: if the file already exists
+      IOError: if the containing directory is required and missing
+    """
+    file_path = self.NormalizePath(file_path)
+    if self.Exists(file_path):
+      raise IOError(errno.EEXIST,
+                    'File already exists in fake filesystem',
+                    file_path)
+    parent_directory, new_file = self.SplitPath(file_path)
+    if not parent_directory:
+      parent_directory = self.cwd
+    if not self.Exists(parent_directory):
+      if not create_missing_dirs:
+        raise IOError(errno.ENOENT, 'No such fake directory', parent_directory)
+      self.CreateDirectory(parent_directory)
+    if apply_umask:
+      st_mode &= ~self.umask
+    file_object = FakeFile(new_file, st_mode, contents)
+    file_object.SetIno(inode)
+    self.AddObject(parent_directory, file_object)
+
+    # set the size if st_size is given
+    if not contents and st_size is not None:
+      try:
+        file_object.SetLargeFileSize(st_size)
+      except IOError:
+        self.RemoveObject(file_path)
+        raise
+
+    return file_object
+
+  def CreateLink(self, file_path, link_target):
+    """Creates the specified symlink, pointed at the specified link target.
+
+    Args:
+      file_path:  path to the symlink to create
+      link_target:  the target of the symlink
+
+    Returns:
+      the newly created FakeFile object
+
+    Raises:
+      IOError:  if the file already exists
+    """
+    resolved_file_path = self.ResolvePath(file_path)
+    return self.CreateFile(resolved_file_path, st_mode=stat.S_IFLNK | PERM_DEF,
+                           contents=link_target)
+
+  def __str__(self):
+    return str(self.root)
+
+
+class FakePathModule(object):
+  """Faked os.path module replacement.
+
+  FakePathModule should *only* be instantiated by FakeOsModule.  See the
+  FakeOsModule docstring for details.
+  """
+  _OS_PATH_COPY = CopyModule(os.path)
+
+  def __init__(self, filesystem, os_module=None):
+    """Init.
+
+    Args:
+      filesystem:  FakeFilesystem used to provide file system information
+      os_module: (deprecated) FakeOsModule to assign to self.os
+    """
+    self.filesystem = filesystem
+    self._os_path = self._OS_PATH_COPY
+    if os_module is None:
+      warnings.warn(FAKE_PATH_MODULE_DEPRECATION, DeprecationWarning,
+                    stacklevel=2)
+    self._os_path.os = self.os = os_module
+    self.sep = self.filesystem.path_separator
+
+  def exists(self, path):
+    """Determines whether the file object exists within the fake filesystem.
+
+    Args:
+      path:  path to the file object
+
+    Returns:
+      bool (if file exists)
+    """
+    return self.filesystem.Exists(path)
+
+  def lexists(self, path):
+    """Test whether a path exists.  Returns True for broken symbolic links.
+
+    Args:
+      path:  path to the symlnk object
+
+    Returns:
+      bool (if file exists)
+    """
+    return self.exists(path) or self.islink(path)
+
+  def getsize(self, path):
+    """Return the file object size in bytes.
+
+    Args:
+      path:  path to the file object
+
+    Returns:
+      file size in bytes
+    """
+    file_obj = self.filesystem.GetObject(path)
+    return file_obj.st_size
+
+  def _istype(self, path, st_flag):
+    """Helper function to implement isdir(), islink(), etc.
+
+    See the stat(2) man page for valid stat.S_I* flag values
+
+    Args:
+      path:  path to file to stat and test
+      st_flag:  the stat.S_I* flag checked for the file's st_mode
+
+    Returns:
+      boolean (the st_flag is set in path's st_mode)
+
+    Raises:
+      TypeError: if path is None
+    """
+    if path is None:
+      raise TypeError
+    try:
+      obj = self.filesystem.ResolveObject(path)
+      if obj:
+        return stat.S_IFMT(obj.st_mode) == st_flag
+    except IOError:
+      return False
+    return False
+
+  def isabs(self, path):
+    if self.filesystem.path_separator == os.path.sep:
+      # Pass through to os.path.isabs, which on Windows has special
+      # handling for a leading drive letter.
+      return self._os_path.isabs(path)
+    else:
+      return path.startswith(self.filesystem.path_separator)
+
+  def isdir(self, path):
+    """Determines if path identifies a directory."""
+    return self._istype(path, stat.S_IFDIR)
+
+  def isfile(self, path):
+    """Determines if path identifies a regular file."""
+    return self._istype(path, stat.S_IFREG)
+
+  def islink(self, path):
+    """Determines if path identifies a symbolic link.
+
+    Args:
+      path: path to filesystem object.
+
+    Returns:
+      boolean (the st_flag is set in path's st_mode)
+
+    Raises:
+      TypeError: if path is None
+    """
+    if path is None:
+      raise TypeError
+    try:
+      link_obj = self.filesystem.LResolveObject(path)
+      return stat.S_IFMT(link_obj.st_mode) == stat.S_IFLNK
+    except IOError:
+      return False
+    except KeyError:
+      return False
+    return False
+
+  def getmtime(self, path):
+    """Returns the mtime of the file."""
+    try:
+      file_obj = self.filesystem.GetObject(path)
+    except IOError as e:
+      raise OSError(errno.ENOENT, str(e))
+    return file_obj.st_mtime
+
+  def abspath(self, path):
+    """Return the absolute version of a path."""
+    if not self.isabs(path):
+      if sys.version_info < (3, 0) and isinstance(path, unicode):
+        cwd = self.os.getcwdu()
+      else:
+        cwd = self.os.getcwd()
+      path = self.join(cwd, path)
+    return self.normpath(path)
+
+  def join(self, *p):
+    """Returns the completed path with a separator of the parts."""
+    return self.filesystem.JoinPaths(*p)
+
+  def normpath(self, path):
+    """Normalize path, eliminating double slashes, etc."""
+    return self.filesystem.CollapsePath(path)
+
+  if _is_windows:
+
+    def relpath(self, path, start=None):
+      """ntpath.relpath() needs the cwd passed in the start argument."""
+      if start is None:
+        start = self.filesystem.cwd
+      path = self._os_path.relpath(path, start)
+      return path.replace(self._os_path.sep, self.filesystem.path_separator)
+
+    realpath = abspath
+
+  def __getattr__(self, name):
+    """Forwards any non-faked calls to os.path."""
+    return self._os_path.__dict__[name]
+
+
+class FakeOsModule(object):
+  """Uses FakeFilesystem to provide a fake os module replacement.
+
+  Do not create os.path separately from os, as there is a necessary circular
+  dependency between os and os.path to replicate the behavior of the standard
+  Python modules.  What you want to do is to just let FakeOsModule take care of
+  os.path setup itself.
+
+  # You always want to do this.
+  filesystem = fake_filesystem.FakeFilesystem()
+  my_os_module = fake_filesystem.FakeOsModule(filesystem)
+  """
+
+  def __init__(self, filesystem, os_path_module=None):
+    """Also exposes self.path (to fake os.path).
+
+    Args:
+      filesystem:  FakeFilesystem used to provide file system information
+      os_path_module: (deprecated) optional FakePathModule instance
+    """
+    self.filesystem = filesystem
+    self.sep = filesystem.path_separator
+    self._os_module = os
+    if os_path_module is None:
+      self.path = FakePathModule(self.filesystem, self)
+    else:
+      warnings.warn(FAKE_PATH_MODULE_DEPRECATION, DeprecationWarning,
+                    stacklevel=2)
+      self.path = os_path_module
+    if sys.version_info < (3, 0):
+      self.fdopen = self._fdopen_ver2
+    else:
+      self.fdopen = self._fdopen
+
+  def _fdopen(self, *args, **kwargs):
+    """Redirector to open() builtin function.
+
+    Args:
+      *args: pass through args
+      **kwargs: pass through kwargs
+
+    Returns:
+      File object corresponding to file_des.
+
+    Raises:
+      TypeError: if file descriptor is not an integer.
+    """
+    if not isinstance(args[0], int):
+      raise TypeError('an integer is required')
+    return FakeFileOpen(self.filesystem)(*args, **kwargs)
+
+  def _fdopen_ver2(self, file_des, mode='r', bufsize=None):
+    """Returns an open file object connected to the file descriptor file_des.
+
+    Args:
+      file_des: An integer file descriptor for the file object requested.
+      mode: additional file flags. Currently checks to see if the mode matches
+        the mode of the requested file object.
+      bufsize: ignored. (Used for signature compliance with __builtin__.fdopen)
+
+    Returns:
+      File object corresponding to file_des.
+
+    Raises:
+      OSError: if bad file descriptor or incompatible mode is given.
+      TypeError: if file descriptor is not an integer.
+    """
+    if not isinstance(file_des, int):
+      raise TypeError('an integer is required')
+
+    try:
+      return FakeFileOpen(self.filesystem).Call(file_des, mode=mode)
+    except IOError as e:
+      raise OSError(e)
+
+  def open(self, file_path, flags, mode=None):
+    """Returns the file descriptor for a FakeFile.
+
+    WARNING: This implementation only implements creating a file. Please fill
+    out the remainder for your needs.
+
+    Args:
+      file_path: the path to the file
+      flags: low-level bits to indicate io operation
+      mode: bits to define default permissions
+
+    Returns:
+      A file descriptor.
+
+    Raises:
+      OSError: if the path cannot be found
+      ValueError: if invalid mode is given
+      NotImplementedError: if an unsupported flag is passed in
+    """
+    if flags & os.O_CREAT:
+      fake_file = FakeFileOpen(self.filesystem)(file_path, 'w')
+      if mode:
+        self.chmod(file_path, mode)
+      return fake_file.fileno()
+    else:
+      raise NotImplementedError('FakeOsModule.open')
+
+  def close(self, file_des):
+    """Closes a file descriptor.
+
+    Args:
+      file_des: An integer file descriptor for the file object requested.
+
+    Raises:
+      OSError: bad file descriptor.
+      TypeError: if file descriptor is not an integer.
+    """
+    fh = self.filesystem.GetOpenFile(file_des)
+    fh.close()
+
+  def read(self, file_des, num_bytes):
+    """Reads number of bytes from a file descriptor, returns bytes read.
+
+    Args:
+      file_des: An integer file descriptor for the file object requested.
+      num_bytes: Number of bytes to read from file.
+
+    Returns:
+      Bytes read from file.
+
+    Raises:
+      OSError: bad file descriptor.
+      TypeError: if file descriptor is not an integer.
+    """
+    fh = self.filesystem.GetOpenFile(file_des)
+    return fh.read(num_bytes)
+
+  def write(self, file_des, contents):
+    """Writes string to file descriptor, returns number of bytes written.
+
+    Args:
+      file_des: An integer file descriptor for the file object requested.
+      contents: String of bytes to write to file.
+
+    Returns:
+      Number of bytes written.
+
+    Raises:
+      OSError: bad file descriptor.
+      TypeError: if file descriptor is not an integer.
+    """
+    fh = self.filesystem.GetOpenFile(file_des)
+    fh.write(contents)
+    fh.flush()
+    return len(contents)
+
+  def fstat(self, file_des):
+    """Returns the os.stat-like tuple for the FakeFile object of file_des.
+
+    Args:
+      file_des:  file descriptor of filesystem object to retrieve
+
+    Returns:
+      the os.stat_result object corresponding to entry_path
+
+    Raises:
+      OSError: if the filesystem object doesn't exist.
+    """
+    # stat should return the tuple representing return value of os.stat
+    stats = self.filesystem.GetOpenFile(file_des).GetObject()
+    st_obj = os.stat_result((stats.st_mode, stats.st_ino, stats.st_dev,
+                             stats.st_nlink, stats.st_uid, stats.st_gid,
+                             stats.st_size, stats.st_atime,
+                             stats.st_mtime, stats.st_ctime))
+    return st_obj
+
+  def _ConfirmDir(self, target_directory):
+    """Tests that the target is actually a directory, raising OSError if not.
+
+    Args:
+      target_directory:  path to the target directory within the fake
+        filesystem
+
+    Returns:
+      the FakeFile object corresponding to target_directory
+
+    Raises:
+      OSError:  if the target is not a directory
+    """
+    try:
+      directory = self.filesystem.GetObject(target_directory)
+    except IOError as e:
+      raise OSError(e.errno, e.strerror, target_directory)
+    if not directory.st_mode & stat.S_IFDIR:
+      raise OSError(errno.ENOTDIR,
+                    'Fake os module: not a directory',
+                    target_directory)
+    return directory
+
+  def umask(self, new_mask):
+    """Change the current umask.
+
+    Args:
+      new_mask: An integer.
+
+    Returns:
+      The old mask.
+
+    Raises:
+      TypeError: new_mask is of an invalid type.
+    """
+    if not isinstance(new_mask, int):
+      raise TypeError('an integer is required')
+    old_umask = self.filesystem.umask
+    self.filesystem.umask = new_mask
+    return old_umask
+
+  def chdir(self, target_directory):
+    """Change current working directory to target directory.
+
+    Args:
+      target_directory:  path to new current working directory
+
+    Raises:
+      OSError: if user lacks permission to enter the argument directory or if
+               the target is not a directory
+    """
+    target_directory = self.filesystem.ResolvePath(target_directory)
+    self._ConfirmDir(target_directory)
+    directory = self.filesystem.GetObject(target_directory)
+    # A full implementation would check permissions all the way up the tree.
+    if not directory.st_mode | PERM_EXE:
+      raise OSError(errno.EACCES, 'Fake os module: permission denied',
+                    directory)
+    self.filesystem.cwd = target_directory
+
+  def getcwd(self):
+    """Return current working directory."""
+    return self.filesystem.cwd
+
+  def getcwdu(self):
+    """Return current working directory. Deprecated in Python 3."""
+    if sys.version_info >= (3, 0):
+      raise AttributeError('no attribute getcwdu')
+    return unicode(self.filesystem.cwd)
+
+  def listdir(self, target_directory):
+    """Returns a sorted list of filenames in target_directory.
+
+    Args:
+      target_directory:  path to the target directory within the fake
+        filesystem
+
+    Returns:
+      a sorted list of file names within the target directory
+
+    Raises:
+      OSError:  if the target is not a directory
+    """
+    target_directory = self.filesystem.ResolvePath(target_directory)
+    directory = self._ConfirmDir(target_directory)
+    return sorted(directory.contents)
+
+  def _ClassifyDirectoryContents(self, root):
+    """Classify contents of a directory as files/directories.
+
+    Args:
+      root: (str) Directory to examine.
+
+    Returns:
+      (tuple) A tuple consisting of three values: the directory examined, a
+      list containing all of the directory entries, and a list containing all
+      of the non-directory entries.  (This is the same format as returned by
+      the os.walk generator.)
+
+    Raises:
+      Nothing on its own, but be ready to catch exceptions generated by
+      underlying mechanisms like os.listdir.
+    """
+    dirs = []
+    files = []
+    for entry in self.listdir(root):
+      if self.path.isdir(self.path.join(root, entry)):
+        dirs.append(entry)
+      else:
+        files.append(entry)
+    return (root, dirs, files)
+
+  def walk(self, top, topdown=True, onerror=None):
+    """Performs an os.walk operation over the fake filesystem.
+
+    Args:
+      top:  root directory from which to begin walk
+      topdown:  determines whether to return the tuples with the root as the
+        first entry (True) or as the last, after all the child directory
+        tuples (False)
+      onerror:  if not None, function which will be called to handle the
+        os.error instance provided when os.listdir() fails
+
+    Yields:
+      (path, directories, nondirectories) for top and each of its
+      subdirectories.  See the documentation for the builtin os module for
+      further details.
+    """
+    top = self.path.normpath(top)
+    try:
+      top_contents = self._ClassifyDirectoryContents(top)
+    except OSError as e:
+      top_contents = None
+      if onerror is not None:
+        onerror(e)
+
+    if top_contents is not None:
+      if topdown:
+        yield top_contents
+
+      for directory in top_contents[1]:
+        for contents in self.walk(self.path.join(top, directory),
+                                  topdown=topdown, onerror=onerror):
+          yield contents
+
+      if not topdown:
+        yield top_contents
+
+  def readlink(self, path):
+    """Reads the target of a symlink.
+
+    Args:
+      path:  symlink to read the target of
+
+    Returns:
+      the string representing the path to which the symbolic link points.
+
+    Raises:
+      TypeError: if path is None
+      OSError: (with errno=ENOENT) if path is not a valid path, or
+               (with errno=EINVAL) if path is valid, but is not a symlink
+    """
+    if path is None:
+      raise TypeError
+    try:
+      link_obj = self.filesystem.LResolveObject(path)
+    except IOError:
+      raise OSError(errno.ENOENT, 'Fake os module: path does not exist', path)
+    if stat.S_IFMT(link_obj.st_mode) != stat.S_IFLNK:
+      raise OSError(errno.EINVAL, 'Fake os module: not a symlink', path)
+    return link_obj.contents
+
+  def stat(self, entry_path):
+    """Returns the os.stat-like tuple for the FakeFile object of entry_path.
+
+    Args:
+      entry_path:  path to filesystem object to retrieve
+
+    Returns:
+      the os.stat_result object corresponding to entry_path
+
+    Raises:
+      OSError: if the filesystem object doesn't exist.
+    """
+    # stat should return the tuple representing return value of os.stat
+    try:
+      stats = self.filesystem.ResolveObject(entry_path)
+      st_obj = os.stat_result((stats.st_mode, stats.st_ino, stats.st_dev,
+                               stats.st_nlink, stats.st_uid, stats.st_gid,
+                               stats.st_size, stats.st_atime,
+                               stats.st_mtime, stats.st_ctime))
+      return st_obj
+    except IOError as io_error:
+      raise OSError(io_error.errno, io_error.strerror, entry_path)
+
+  def lstat(self, entry_path):
+    """Returns the os.stat-like tuple for entry_path, not following symlinks.
+
+    Args:
+      entry_path:  path to filesystem object to retrieve
+
+    Returns:
+      the os.stat_result object corresponding to entry_path
+
+    Raises:
+      OSError: if the filesystem object doesn't exist.
+    """
+    # stat should return the tuple representing return value of os.stat
+    try:
+      stats = self.filesystem.LResolveObject(entry_path)
+      st_obj = os.stat_result((stats.st_mode, stats.st_ino, stats.st_dev,
+                               stats.st_nlink, stats.st_uid, stats.st_gid,
+                               stats.st_size, stats.st_atime,
+                               stats.st_mtime, stats.st_ctime))
+      return st_obj
+    except IOError as io_error:
+      raise OSError(io_error.errno, io_error.strerror, entry_path)
+
+  def remove(self, path):
+    """Removes the FakeFile object representing the specified file."""
+    path = self.filesystem.NormalizePath(path)
+    if self.path.isdir(path) and not self.path.islink(path):
+      raise OSError(errno.EISDIR, "Is a directory: '%s'" % path)
+    try:
+      self.filesystem.RemoveObject(path)
+    except IOError as e:
+      raise OSError(e.errno, e.strerror, e.filename)
+
+  # As per the documentation unlink = remove.
+  unlink = remove
+
+  def rename(self, old_file, new_file):
+    """Adds a FakeFile object at new_file containing contents of old_file.
+
+    Also removes the FakeFile object for old_file, and replaces existing
+    new_file object, if one existed.
+
+    Args:
+      old_file:  path to filesystem object to rename
+      new_file:  path to where the filesystem object will live after this call
+
+    Raises:
+      OSError:  if old_file does not exist.
+      IOError:  if dirname(new_file) does not exist
+    """
+    old_file = self.filesystem.NormalizePath(old_file)
+    new_file = self.filesystem.NormalizePath(new_file)
+    if not self.filesystem.Exists(old_file):
+      raise OSError(errno.ENOENT,
+                    'Fake os object: can not rename nonexistent file '
+                    'with name',
+                    old_file)
+    if self.filesystem.Exists(new_file):
+      if old_file == new_file:
+        return None  # Nothing to do here.
+      else:
+        self.remove(new_file)
+    old_dir, old_name = self.path.split(old_file)
+    new_dir, new_name = self.path.split(new_file)
+    if not self.filesystem.Exists(new_dir):
+      raise IOError(errno.ENOENT, 'No such fake directory', new_dir)
+    old_dir_object = self.filesystem.ResolveObject(old_dir)
+    old_object = old_dir_object.GetEntry(old_name)
+    old_object_mtime = old_object.st_mtime
+    new_dir_object = self.filesystem.ResolveObject(new_dir)
+    if old_object.st_mode & stat.S_IFDIR:
+      old_object.name = new_name
+      new_dir_object.AddEntry(old_object)
+      old_dir_object.RemoveEntry(old_name)
+    else:
+      self.filesystem.CreateFile(new_file,
+                                 st_mode=old_object.st_mode,
+                                 contents=old_object.contents,
+                                 create_missing_dirs=False)
+      self.remove(old_file)
+    new_object = self.filesystem.GetObject(new_file)
+    new_object.SetMTime(old_object_mtime)
+    self.chown(new_file, old_object.st_uid, old_object.st_gid)
+
+  def rmdir(self, target_directory):
+    """Remove a leaf Fake directory.
+
+    Args:
+      target_directory: (str) Name of directory to remove.
+
+    Raises:
+      OSError: if target_directory does not exist or is not a directory,
+      or as per FakeFilesystem.RemoveObject. Cannot remove '.'.
+    """
+    if target_directory == '.':
+      raise OSError(errno.EINVAL, 'Invalid argument: \'.\'')
+    target_directory = self.filesystem.NormalizePath(target_directory)
+    if self._ConfirmDir(target_directory):
+      if self.listdir(target_directory):
+        raise OSError(errno.ENOTEMPTY, 'Fake Directory not empty',
+                      target_directory)
+      try:
+        self.filesystem.RemoveObject(target_directory)
+      except IOError as e:
+        raise OSError(e.errno, e.strerror, e.filename)
+
+  def removedirs(self, target_directory):
+    """Remove a leaf Fake directory and all empty intermediate ones."""
+    target_directory = self.filesystem.NormalizePath(target_directory)
+    directory = self._ConfirmDir(target_directory)
+    if directory.contents:
+      raise OSError(errno.ENOTEMPTY, 'Fake Directory not empty',
+                    self.path.basename(target_directory))
+    else:
+      self.rmdir(target_directory)
+    head, tail = self.path.split(target_directory)
+    if not tail:
+      head, tail = self.path.split(head)
+    while head and tail:
+      head_dir = self._ConfirmDir(head)
+      if head_dir.contents:
+        break
+      self.rmdir(head)
+      head, tail = self.path.split(head)
+
+  def mkdir(self, dir_name, mode=PERM_DEF):
+    """Create a leaf Fake directory.
+
+    Args:
+      dir_name: (str) Name of directory to create.  Relative paths are assumed
+        to be relative to '/'.
+      mode: (int) Mode to create directory with.  This argument defaults to
+        0o777.  The umask is applied to this mode.
+
+    Raises:
+      OSError: if the directory name is invalid or parent directory is read only
+      or as per FakeFilesystem.AddObject.
+    """
+    if dir_name.endswith(self.sep):
+      dir_name = dir_name[:-1]
+
+    parent_dir, _ = self.path.split(dir_name)
+    if parent_dir:
+      base_dir = self.path.normpath(parent_dir)
+      if parent_dir.endswith(self.sep + '..'):
+        base_dir, unused_dotdot, _ = parent_dir.partition(self.sep + '..')
+      if not self.filesystem.Exists(base_dir):
+        raise OSError(errno.ENOENT, 'No such fake directory', base_dir)
+
+    dir_name = self.filesystem.NormalizePath(dir_name)
+    if self.filesystem.Exists(dir_name):
+      raise OSError(errno.EEXIST, 'Fake object already exists', dir_name)
+    head, tail = self.path.split(dir_name)
+    directory_object = self.filesystem.GetObject(head)
+    if not directory_object.st_mode & PERM_WRITE:
+      raise OSError(errno.EACCES, 'Permission Denied', dir_name)
+
+    self.filesystem.AddObject(
+        head, FakeDirectory(tail, mode & ~self.filesystem.umask))
+
+  def makedirs(self, dir_name, mode=PERM_DEF):
+    """Create a leaf Fake directory + create any non-existent parent dirs.
+
+    Args:
+      dir_name: (str) Name of directory to create.
+      mode: (int) Mode to create directory (and any necessary parent
+        directories) with. This argument defaults to 0o777.  The umask is
+        applied to this mode.
+
+    Raises:
+      OSError: if the directory already exists or as per
+      FakeFilesystem.CreateDirectory
+    """
+    dir_name = self.filesystem.NormalizePath(dir_name)
+    path_components = self.filesystem.GetPathComponents(dir_name)
+
+    # Raise a permission denied error if the first existing directory is not
+    # writeable.
+    current_dir = self.filesystem.root
+    for component in path_components:
+      if component not in current_dir.contents:
+        if not current_dir.st_mode & PERM_WRITE:
+          raise OSError(errno.EACCES, 'Permission Denied', dir_name)
+        else:
+          break
+      else:
+        current_dir = current_dir.contents[component]
+
+    self.filesystem.CreateDirectory(dir_name, mode & ~self.filesystem.umask)
+
+  def access(self, path, mode):
+    """Check if a file exists and has the specified permissions.
+
+    Args:
+      path: (str) Path to the file.
+      mode: (int) Permissions represented as a bitwise-OR combination of
+          os.F_OK, os.R_OK, os.W_OK, and os.X_OK.
+    Returns:
+      boolean, True if file is accessible, False otherwise
+    """
+    try:
+      st = self.stat(path)
+    except OSError as os_error:
+      if os_error.errno == errno.ENOENT:
+        return False
+      raise
+    return (mode & ((st.st_mode >> 6) & 7)) == mode
+
+  def chmod(self, path, mode):
+    """Change the permissions of a file as encoded in integer mode.
+
+    Args:
+      path: (str) Path to the file.
+      mode: (int) Permissions
+    """
+    try:
+      file_object = self.filesystem.GetObject(path)
+    except IOError as io_error:
+      if io_error.errno == errno.ENOENT:
+        raise OSError(errno.ENOENT,
+                      'No such file or directory in fake filesystem',
+                      path)
+      raise
+    file_object.st_mode = ((file_object.st_mode & ~PERM_ALL) |
+                           (mode & PERM_ALL))
+    file_object.st_ctime = int(time.time())
+
+  def utime(self, path, times):
+    """Change the access and modified times of a file.
+
+    Args:
+      path: (str) Path to the file.
+      times: 2-tuple of numbers, of the form (atime, mtime) which is used to set
+          the access and modified times, respectively. If None, file's access
+          and modified times are set to the current time.
+
+    Raises:
+      TypeError: If anything other than integers is specified in passed tuple or
+          number of elements in the tuple is not equal to 2.
+    """
+    try:
+      file_object = self.filesystem.GetObject(path)
+    except IOError as io_error:
+      if io_error.errno == errno.ENOENT:
+        raise OSError(errno.ENOENT,
+                      'No such file or directory in fake filesystem',
+                      path)
+      raise
+    if times is None:
+      file_object.st_atime = int(time.time())
+      file_object.st_mtime = int(time.time())
+    else:
+      if len(times) != 2:
+        raise TypeError('utime() arg 2 must be a tuple (atime, mtime)')
+      for t in times:
+        if not isinstance(t, (int, float)):
+          raise TypeError('an integer is required')
+
+      file_object.st_atime = times[0]
+      file_object.st_mtime = times[1]
+
+  def chown(self, path, uid, gid):
+    """Set ownership of a faked file.
+
+    Args:
+      path: (str) Path to the file or directory.
+      uid: (int) Numeric uid to set the file or directory to.
+      gid: (int) Numeric gid to set the file or directory to.
+    """
+    try:
+      file_object = self.filesystem.GetObject(path)
+    except IOError as io_error:
+      if io_error.errno == errno.ENOENT:
+        raise OSError(errno.ENOENT,
+                      'No such file or directory in fake filesystem',
+                      path)
+      raise
+    if uid != -1:
+      file_object.st_uid = uid
+    if gid != -1:
+      file_object.st_gid = gid
+
+  def mknod(self, filename, mode=None, device=None):
+    """Create a filesystem node named 'filename'.
+
+    Does not support device special files or named pipes as the real os
+    module does.
+
+    Args:
+      filename: (str) Name of the file to create
+      mode: (int) permissions to use and type of file to be created.
+        Default permissions are 0o666.  Only the stat.S_IFREG file type
+        is supported by the fake implementation.  The umask is applied
+        to this mode.
+      device: not supported in fake implementation
+
+    Raises:
+      OSError: if called with unsupported options or the file can not be
+      created.
+    """
+    if mode is None:
+      mode = stat.S_IFREG | PERM_DEF_FILE
+    if device or not mode & stat.S_IFREG:
+      raise OSError(errno.EINVAL,
+                    'Fake os mknod implementation only supports '
+                    'regular files.')
+
+    head, tail = self.path.split(filename)
+    if not tail:
+      if self.filesystem.Exists(head):
+        raise OSError(errno.EEXIST, 'Fake filesystem: %s: %s' % (
+            os.strerror(errno.EEXIST), filename))
+      raise OSError(errno.ENOENT, 'Fake filesystem: %s: %s' % (
+          os.strerror(errno.ENOENT), filename))
+    if tail == '.' or tail == '..' or self.filesystem.Exists(filename):
+      raise OSError(errno.EEXIST, 'Fake fileystem: %s: %s' % (
+          os.strerror(errno.EEXIST), filename))
+    try:
+      self.filesystem.AddObject(head, FakeFile(tail,
+                                               mode & ~self.filesystem.umask))
+    except IOError:
+      raise OSError(errno.ENOTDIR, 'Fake filesystem: %s: %s' % (
+          os.strerror(errno.ENOTDIR), filename))
+
+  def symlink(self, link_target, path):
+    """Creates the specified symlink, pointed at the specified link target.
+
+    Args:
+      link_target:  the target of the symlink
+      path:  path to the symlink to create
+
+    Returns:
+      None
+
+    Raises:
+      IOError:  if the file already exists
+    """
+    self.filesystem.CreateLink(path, link_target)
+
+  # pylint: disable-msg=C6002
+  # TODO: Link doesn't behave like os.link, this needs to be fixed properly.
+  link = symlink
+
+  def __getattr__(self, name):
+    """Forwards any unfaked calls to the standard os module."""
+    return getattr(self._os_module, name)
+
+
+class FakeFileOpen(object):
+  """Faked file() and open() function replacements.
+
+  Returns FakeFile objects in a FakeFilesystem in place of the file()
+  or open() function.
+  """
+
+  def __init__(self, filesystem, delete_on_close=False):
+    """init.
+
+    Args:
+      filesystem:  FakeFilesystem used to provide file system information
+      delete_on_close:  optional boolean, deletes file on close()
+    """
+    self.filesystem = filesystem
+    self._delete_on_close = delete_on_close
+
+  def __call__(self, *args, **kwargs):
+    """Redirects calls to file() or open() to appropriate method."""
+    if sys.version_info < (3, 0):
+      return self._call_ver2(*args, **kwargs)
+    else:
+      return self.Call(*args, **kwargs)
+
+  def _call_ver2(self, file_path, mode='r', buffering=-1, flags=None):
+    """Limits args of open() or file() for Python 2.x versions."""
+    # Backwards compatibility, mode arg used to be named flags
+    mode = flags or mode
+    return self.Call(file_path, mode, buffering)
+
+  def Call(self, file_, mode='r', buffering=-1, encoding=None,
+           errors=None, newline=None, closefd=True, opener=None):
+    """Returns a StringIO object with the contents of the target file object.
+
+    Args:
+      file_: path to target file or a file descriptor
+      mode: additional file modes. All r/w/a r+/w+/a+ modes are supported.
+        't', and 'U' are ignored, e.g., 'wU' is treated as 'w'. 'b' sets
+        binary mode, no end of line translations in StringIO.
+      buffering: ignored. (Used for signature compliance with __builtin__.open)
+      encoding: ignored, strings have no encoding
+      errors: ignored, this relates to encoding
+      newline: controls universal newlines, passed to StringIO object
+      closefd: if a file descriptor rather than file name is passed, and set
+        to false, then the file descriptor is kept open when file is closed
+      opener: not supported
+
+    Returns:
+      a StringIO object containing the contents of the target file
+
+    Raises:
+      IOError: if the target object is a directory, the path is invalid or
+        permission is denied.
+    """
+    orig_modes = mode  # Save original mdoes for error messages.
+    # Binary mode for non 3.x or set by mode
+    binary = sys.version_info < (3, 0) or 'b' in mode
+    # Normalize modes. Ignore 't' and 'U'.
+    mode = mode.replace('t', '').replace('b', '')
+    mode = mode.replace('rU', 'r').replace('U', 'r')
+
+    if mode not in _OPEN_MODE_MAP:
+      raise IOError('Invalid mode: %r' % orig_modes)
+
+    must_exist, need_read, need_write, truncate, append = _OPEN_MODE_MAP[mode]
+
+    file_object = None
+    filedes = None
+    # opening a file descriptor
+    if isinstance(file_, int):
+      filedes = file_
+      file_object = self.filesystem.GetOpenFile(filedes).GetObject()
+      file_path = file_object.name
+    else:
+      file_path = file_
+      real_path = self.filesystem.ResolvePath(file_path)
+      if self.filesystem.Exists(file_path):
+        file_object = self.filesystem.GetObjectFromNormalizedPath(real_path)
+      closefd = True
+
+    if file_object:
+      if ((need_read and not file_object.st_mode & PERM_READ) or
+          (need_write and not file_object.st_mode & PERM_WRITE)):
+        raise IOError(errno.EACCES, 'Permission denied', file_path)
+      if need_write:
+        file_object.st_ctime = int(time.time())
+        if truncate:
+          file_object.SetContents('')
+    else:
+      if must_exist:
+        raise IOError(errno.ENOENT, 'No such file or directory', file_path)
+      file_object = self.filesystem.CreateFile(
+          real_path, create_missing_dirs=False, apply_umask=True)
+
+    if file_object.st_mode & stat.S_IFDIR:
+      raise IOError(errno.EISDIR, 'Fake file object: is a directory', file_path)
+
+    class FakeFileWrapper(object):
+      """Wrapper for a StringIO object for use by a FakeFile object.
+
+      If the wrapper has any data written to it, it will propagate to
+      the FakeFile object on close() or flush().
+      """
+      if sys.version_info < (3, 0):
+        _OPERATION_ERROR = IOError
+      else:
+        _OPERATION_ERROR = io.UnsupportedOperation
+
+      def __init__(self, file_object, update=False, read=False, append=False,
+                   delete_on_close=False, filesystem=None, newline=None,
+                   binary=True, closefd=True):
+        self._file_object = file_object
+        self._append = append
+        self._read = read
+        self._update = update
+        self._closefd = closefd
+        self._file_epoch = file_object.epoch
+        contents = file_object.contents
+        newline_arg = {} if binary else {'newline': newline}
+        io_class = io.StringIO
+        # For Python 3, files opened as binary only read/write byte contents.
+        if sys.version_info >= (3, 0) and binary:
+          io_class = io.BytesIO
+          if contents and isinstance(contents, str):
+            contents = bytes(contents, 'ascii')
+        if contents:
+          if update:
+            self._io = io_class(**newline_arg)
+            self._io.write(contents)
+            if not append:
+              self._io.seek(0)
+            else:
+              self._read_whence = 0
+              if read:
+                self._read_seek = 0
+              else:
+                self._read_seek = self._io.tell()
+          else:
+            self._io = io_class(contents, **newline_arg)
+        else:
+          self._io = io_class(**newline_arg)
+          self._read_whence = 0
+          self._read_seek = 0
+        if delete_on_close:
+          assert filesystem, 'delete_on_close=True requires filesystem='
+        self._filesystem = filesystem
+        self._delete_on_close = delete_on_close
+        # override, don't modify FakeFile.name, as FakeFilesystem expects
+        # it to be the file name only, no directories.
+        self.name = file_object.opened_as
+
+      def __enter__(self):
+        """To support usage of this fake file with the 'with' statement."""
+        return self
+
+      def __exit__(self, type, value, traceback):  # pylint: disable-msg=W0622
+        """To support usage of this fake file with the 'with' statement."""
+        self.close()
+
+      def GetObject(self):
+        """Returns FakeFile object that is wrapped by current class."""
+        return self._file_object
+
+      def fileno(self):
+        """Returns file descriptor of file object."""
+        return self.filedes
+
+      def close(self):
+        """File close."""
+        if self._update:
+          self._file_object.SetContents(self._io.getvalue())
+        if self._closefd:
+          self._filesystem.CloseOpenFile(self)
+        if self._delete_on_close:
+          self._filesystem.RemoveObject(self.name)
+
+      def flush(self):
+        """Flush file contents to 'disk'."""
+        if self._update:
+          self._file_object.SetContents(self._io.getvalue())
+          self._file_epoch = self._file_object.epoch
+
+      def seek(self, offset, whence=0):
+        """Move read/write pointer in 'file'."""
+        if not self._append:
+          self._io.seek(offset, whence)
+        else:
+          self._read_seek = offset
+          self._read_whence = whence
+
+      def tell(self):
+        """Return the file's current position.
+
+        Returns:
+          int, file's current position in bytes.
+        """
+        if not self._append:
+          return self._io.tell()
+        if self._read_whence:
+          write_seek = self._io.tell()
+          self._io.seek(self._read_seek, self._read_whence)
+          self._read_seek = self._io.tell()
+          self._read_whence = 0
+          self._io.seek(write_seek)
+        return self._read_seek
+
+      def _UpdateStringIO(self):
+        """Updates the StringIO with changes to the file object contents."""
+        if self._file_epoch == self._file_object.epoch:
+          return
+        whence = self._io.tell()
+        self._io.seek(0)
+        self._io.truncate()
+        self._io.write(self._file_object.contents)
+        self._io.seek(whence)
+        self._file_epoch = self._file_object.epoch
+
+      def _ReadWrappers(self, name):
+        """Wrap a StringIO attribute in a read wrapper.
+
+        Returns a read_wrapper which tracks our own read pointer since the
+        StringIO object has no concept of a different read and write pointer.
+
+        Args:
+          name: the name StringIO attribute to wrap.  Should be a read call.
+
+        Returns:
+          either a read_error or read_wrapper function.
+        """
+        io_attr = getattr(self._io, name)
+
+        def read_wrapper(*args, **kwargs):
+          """Wrap all read calls to the StringIO Object.
+
+          We do this to track the read pointer separate from the write
+          pointer.  Anything that wants to read from the StringIO object
+          while we're in append mode goes through this.
+
+          Args:
+            *args: pass through args
+            **kwargs: pass through kwargs
+          Returns:
+            Wrapped StringIO object method
+          """
+          self._io.seek(self._read_seek, self._read_whence)
+          ret_value = io_attr(*args, **kwargs)
+          self._read_seek = self._io.tell()
+          self._read_whence = 0
+          self._io.seek(0, 2)
+          return ret_value
+        return read_wrapper
+
+      def _OtherWrapper(self, name):
+        """Wrap a StringIO attribute in an other_wrapper.
+
+        Args:
+          name: the name of the StringIO attribute to wrap.
+
+        Returns:
+          other_wrapper which is described below.
+        """
+        io_attr = getattr(self._io, name)
+
+        def other_wrapper(*args, **kwargs):
+          """Wrap all other calls to the StringIO Object.
+
+          We do this to track changes to the write pointer.  Anything that
+          moves the write pointer in a file open for appending should move
+          the read pointer as well.
+
+          Args:
+            *args: pass through args
+            **kwargs: pass through kwargs
+          Returns:
+            Wrapped StringIO object method
+          """
+          write_seek = self._io.tell()
+          ret_value = io_attr(*args, **kwargs)
+          if write_seek != self._io.tell():
+            self._read_seek = self._io.tell()
+            self._read_whence = 0
+            self._file_object.st_size += (self._read_seek - write_seek)
+          return ret_value
+        return other_wrapper
+
+      def Size(self):
+        return self._file_object.st_size
+
+      def __getattr__(self, name):
+        if self._file_object.IsLargeFile():
+          raise FakeLargeFileIoException(file_path)
+
+        # errors on called method vs. open mode
+        if not self._read and name.startswith('read'):
+          def read_error(*args, **kwargs):
+            """Throw an error unless the argument is zero."""
+            if args and args[0] == 0:
+              return ''
+            raise self._OPERATION_ERROR('File is not open for reading.')
+          return read_error
+        if not self._update and (name.startswith('write')
+                                 or name == 'truncate'):
+          def write_error(*args, **kwargs):
+            """Throw an error."""
+            raise self._OPERATION_ERROR('File is not open for writing.')
+          return write_error
+
+        if name.startswith('read'):
+          self._UpdateStringIO()
+        if self._append:
+          if name.startswith('read'):
+            return self._ReadWrappers(name)
+          else:
+            return self._OtherWrapper(name)
+        return getattr(self._io, name)
+
+      def __iter__(self):
+        if not self._read:
+          raise self._OPERATION_ERROR('File is not open for reading')
+        return self._io.__iter__()
+
+    # if you print obj.name, the argument to open() must be printed. Not the
+    # abspath, not the filename, but the actual argument.
+    file_object.opened_as = file_path
+
+    fakefile = FakeFileWrapper(file_object,
+                               update=need_write,
+                               read=need_read,
+                               append=append,
+                               delete_on_close=self._delete_on_close,
+                               filesystem=self.filesystem,
+                               newline=newline,
+                               binary=binary,
+                               closefd=closefd)
+    if filedes is not None:
+      fakefile.filedes = filedes
+    else:
+      fakefile.filedes = self.filesystem.AddOpenFile(fakefile)
+    return fakefile
+ 
+
+def _RunDoctest():
+  # pylint: disable-msg=C6204
+  import doctest
+  import fake_filesystem  # pylint: disable-msg=W0406
+  return doctest.testmod(fake_filesystem)
+
+
+if __name__ == '__main__':
+  _RunDoctest()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/fake_filesystem_glob.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/fake_filesystem_glob.py
new file mode 100755
index 0000000..db387df
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/fake_filesystem_glob.py
@@ -0,0 +1,120 @@
+# Copyright 2009 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""A fake glob module implementation that uses fake_filesystem for unit tests.
+
+Includes:
+  FakeGlob: Uses a FakeFilesystem to provide a fake replacement for the
+    glob module.
+
+Usage:
+>>> import fake_filesystem
+>>> import fake_filesystem_glob
+>>> filesystem = fake_filesystem.FakeFilesystem()
+>>> glob_module = fake_filesystem_glob.FakeGlobModule(filesystem)
+
+>>> file = filesystem.CreateFile('new-file')
+>>> glob_module.glob('*')
+['new-file']
+>>> glob_module.glob('???-file')
+['new-file']
+"""
+
+import fnmatch
+import glob
+import os
+
+import fake_filesystem
+
+
+class FakeGlobModule(object):
+  """Uses a FakeFilesystem to provide a fake replacement for glob module."""
+
+  def __init__(self, filesystem):
+    """Construct fake glob module using the fake filesystem.
+
+    Args:
+      filesystem:  FakeFilesystem used to provide file system information
+    """
+    self._glob_module = glob
+    self._os_module = fake_filesystem.FakeOsModule(filesystem)
+    self._path_module = self._os_module.path
+
+  def glob(self, pathname):  # pylint: disable-msg=C6409
+    """Return a list of paths matching a pathname pattern.
+
+    The pattern may contain shell-style wildcards a la fnmatch.
+
+    Args:
+      pathname: the pattern with which to find a list of paths
+
+    Returns:
+      List of strings matching the glob pattern.
+    """
+    if not self.has_magic(pathname):
+      if self._path_module.exists(pathname):
+        return [pathname]
+      else:
+        return []
+
+    dirname, basename = self._path_module.split(pathname)
+
+    if not dirname:
+      return self.glob1(self._path_module.curdir, basename)
+    elif self.has_magic(dirname):
+      path_list = self.glob(dirname)
+    else:
+      path_list = [dirname]
+
+    if not self.has_magic(basename):
+      result = []
+      for dirname in path_list:
+        if basename or self._path_module.isdir(dirname):
+          name = self._path_module.join(dirname, basename)
+          if self._path_module.exists(name):
+            result.append(name)
+    else:
+      result = []
+      for dirname in path_list:
+        sublist = self.glob1(dirname, basename)
+        for name in sublist:
+          result.append(self._path_module.join(dirname, name))
+
+    return result
+
+  def glob1(self, dirname, pattern):  # pylint: disable-msg=C6409
+    if not dirname:
+      dirname = self._path_module.curdir
+    try:
+      names = self._os_module.listdir(dirname)
+    except os.error:
+      return []
+    if pattern[0] != '.':
+      names = filter(lambda x: x[0] != '.', names)
+    return fnmatch.filter(names, pattern)
+
+  def __getattr__(self, name):
+    """Forwards any non-faked calls to the standard glob module."""
+    return getattr(self._glob_module, name)
+
+
+def _RunDoctest():
+  # pylint: disable-msg=C6111,C6204,W0406
+  import doctest
+  import fake_filesystem_glob
+  return doctest.testmod(fake_filesystem_glob)
+
+
+if __name__ == '__main__':
+  _RunDoctest()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/fake_filesystem_glob_test.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/fake_filesystem_glob_test.py
new file mode 100755
index 0000000..b08f982
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/fake_filesystem_glob_test.py
@@ -0,0 +1,82 @@
+#! /usr/bin/env python
+#
+# Copyright 2009 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Test for fake_filesystem_glob."""
+
+import doctest
+import sys
+if sys.version_info < (2, 7):
+    import unittest2 as unittest
+else:
+    import unittest
+
+import fake_filesystem
+import fake_filesystem_glob
+
+
+class FakeGlobUnitTest(unittest.TestCase):
+
+  def setUp(self):
+    self.filesystem = fake_filesystem.FakeFilesystem(path_separator='/')
+    self.glob = fake_filesystem_glob.FakeGlobModule(self.filesystem)
+    directory = './xyzzy'
+    self.filesystem.CreateDirectory(directory)
+    self.filesystem.CreateDirectory('%s/subdir' % directory)
+    self.filesystem.CreateDirectory('%s/subdir2' % directory)
+    self.filesystem.CreateFile('%s/subfile' % directory)
+    self.filesystem.CreateFile('[Temp]')
+
+  def testGlobEmpty(self):
+    self.assertEqual(self.glob.glob(''), [])
+
+  def testGlobStar(self):
+    self.assertEqual(['/xyzzy/subdir', '/xyzzy/subdir2', '/xyzzy/subfile'],
+                     self.glob.glob('/xyzzy/*'))
+
+  def testGlobExact(self):
+    self.assertEqual(['/xyzzy'], self.glob.glob('/xyzzy'))
+    self.assertEqual(['/xyzzy/subfile'], self.glob.glob('/xyzzy/subfile'))
+
+  def testGlobQuestion(self):
+    self.assertEqual(['/xyzzy/subdir', '/xyzzy/subdir2', '/xyzzy/subfile'],
+                     self.glob.glob('/x?zz?/*'))
+
+  def testGlobNoMagic(self):
+    self.assertEqual(['/xyzzy'], self.glob.glob('/xyzzy'))
+    self.assertEqual(['/xyzzy/subdir'], self.glob.glob('/xyzzy/subdir'))
+
+  def testNonExistentPath(self):
+    self.assertEqual([], self.glob.glob('nonexistent'))
+
+  def testDocTest(self):
+    self.assertFalse(doctest.testmod(fake_filesystem_glob)[0])
+
+  def testMagicDir(self):
+    self.assertEqual(['/[Temp]'], self.glob.glob('/*emp*'))
+
+  def testRootGlob(self):
+    self.assertEqual(['[Temp]', 'xyzzy'], self.glob.glob('*'))
+
+  def testGlob1(self):
+    self.assertEqual(['[Temp]'], self.glob.glob1('/', '*Tem*'))
+
+  def testHasMagic(self):
+    self.assertTrue(self.glob.has_magic('['))
+    self.assertFalse(self.glob.has_magic('a'))
+
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/fake_filesystem_shutil.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/fake_filesystem_shutil.py
new file mode 100755
index 0000000..87aff44
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/fake_filesystem_shutil.py
@@ -0,0 +1,220 @@
+#!/usr/bin/env python
+#
+# Copyright 2009 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# pylint: disable-msg=W0612,W0613,C6409
+
+"""A fake shutil module implementation that uses fake_filesystem for unit tests.
+
+Includes:
+  FakeShutil: Uses a FakeFilesystem to provide a fake replacement for the
+    shutil module.
+
+Usage:
+>>> import fake_filesystem
+>>> import fake_filesystem_shutil
+>>> filesystem = fake_filesystem.FakeFilesystem()
+>>> shutil_module = fake_filesystem_shutil.FakeShutilModule(filesystem)
+
+Copy a fake_filesystem directory tree:
+>>> new_file = filesystem.CreateFile('/src/new-file')
+>>> shutil_module.copytree('/src', '/dst')
+>>> filesystem.Exists('/dst/new-file')
+True
+
+Remove a fake_filesystem directory tree:
+>>> shutil_module.rmtree('/src')
+>>> filesystem.Exists('/src/new-file')
+False
+"""
+
+import errno
+import os
+import shutil
+import stat
+
+__pychecker__ = 'no-reimportself'
+
+_PERM_WRITE = 0o200  # Write permission bit.
+_PERM_READ = 0o400   # Read permission bit.
+_PERM_ALL = 0o7777   # All permission bits.
+
+
+class FakeShutilModule(object):
+  """Uses a FakeFilesystem to provide a fake replacement for shutil module."""
+
+  def __init__(self, filesystem):
+    """Construct fake shutil module using the fake filesystem.
+
+    Args:
+      filesystem:  FakeFilesystem used to provide file system information
+    """
+    self.filesystem = filesystem
+    self._shutil_module = shutil
+
+  def rmtree(self, path, ignore_errors=False, onerror=None):
+    """Remove a directory and all its contents.
+
+    Args:
+      path: (str) Directory tree to remove.
+      ignore_errors: (bool) unimplemented
+      onerror: (func) unimplemented
+    """
+    self.filesystem.RemoveObject(path)
+
+  def copy(self, src, dst):
+    """Copy data and mode bits ("cp src dst").
+
+    Args:
+      src: (str) source file
+      dst: (str) destination, may be a directory
+    """
+    if self.filesystem.Exists(dst):
+      if stat.S_ISDIR(self.filesystem.GetObject(dst).st_mode):
+        dst = self.filesystem.JoinPaths(dst, os.path.basename(src))
+    self.copyfile(src, dst)
+    src_object = self.filesystem.GetObject(src)
+    dst_object = self.filesystem.GetObject(dst)
+    dst_object.st_mode = ((dst_object.st_mode & ~_PERM_ALL) |
+                          (src_object.st_mode & _PERM_ALL))
+
+  def copyfile(self, src, dst):
+    """Copy data from src to dst.
+
+    Args:
+      src: (str) source file
+      dst: (dst) destination file
+
+    Raises:
+      IOError: if the file can't be copied
+      shutil.Error: if the src and dst files are the same
+    """
+    src_file_object = self.filesystem.GetObject(src)
+    if not src_file_object.st_mode & _PERM_READ:
+      raise IOError(errno.EACCES, 'Permission denied', src)
+    if stat.S_ISDIR(src_file_object.st_mode):
+      raise IOError(errno.EISDIR, 'Is a directory', src)
+
+    dst_dir = os.path.dirname(dst)
+    if dst_dir:
+      if not self.filesystem.Exists(dst_dir):
+        raise IOError(errno.ENOTDIR, 'Not a directory', dst)
+      dst_dir_object = self.filesystem.GetObject(dst_dir)
+      if not dst_dir_object.st_mode & _PERM_WRITE:
+        raise IOError(errno.EACCES, 'Permission denied', dst_dir)
+
+    abspath_src = self.filesystem.NormalizePath(
+        self.filesystem.ResolvePath(src))
+    abspath_dst = self.filesystem.NormalizePath(
+        self.filesystem.ResolvePath(dst))
+    if abspath_src == abspath_dst:
+      raise shutil.Error('`%s` and `%s` are the same file' % (src, dst))
+
+    if self.filesystem.Exists(dst):
+      dst_file_object = self.filesystem.GetObject(dst)
+      if stat.S_ISDIR(dst_file_object.st_mode):
+        raise IOError(errno.EISDIR, 'Is a directory', dst)
+      if not dst_file_object.st_mode & _PERM_WRITE:
+        raise IOError(errno.EACCES, 'Permission denied', dst)
+      dst_file_object.SetContents(src_file_object.contents)
+
+    else:
+      self.filesystem.CreateFile(dst, contents=src_file_object.contents)
+
+  def copystat(self, src, dst):
+    """Copy all stat info (mode bits, atime, and mtime) from src to dst.
+
+    Args:
+      src: (str) source file
+      dst: (str) destination file
+    """
+    src_object = self.filesystem.GetObject(src)
+    dst_object = self.filesystem.GetObject(dst)
+    dst_object.st_mode = ((dst_object.st_mode & ~_PERM_ALL) |
+                          (src_object.st_mode & _PERM_ALL))
+    dst_object.st_uid = src_object.st_uid
+    dst_object.st_gid = src_object.st_gid
+    dst_object.st_atime = src_object.st_atime
+    dst_object.st_mtime = src_object.st_mtime
+
+  def copy2(self, src, dst):
+    """Copy data and all stat info ("cp -p src dst").
+
+    Args:
+      src: (str) source file
+      dst: (str) destination, may be a directory
+    """
+    if self.filesystem.Exists(dst):
+      if stat.S_ISDIR(self.filesystem.GetObject(dst).st_mode):
+        dst = self.filesystem.JoinPaths(dst, os.path.basename(src))
+    self.copyfile(src, dst)
+    self.copystat(src, dst)
+
+  def copytree(self, src, dst, symlinks=False):
+    """Recursively copy a directory tree.
+
+    Args:
+      src: (str) source directory
+      dst: (str) destination directory, must not already exist
+      symlinks: (bool) copy symlinks as symlinks instead of copying the
+                contents of the linked files. Currently unused.
+
+    Raises:
+      OSError: if src is missing or isn't a directory
+    """
+    self.filesystem.CreateDirectory(dst)
+    try:
+      directory = self.filesystem.GetObject(src)
+    except IOError as e:
+      raise OSError(e.errno, e.message)
+    if not stat.S_ISDIR(directory.st_mode):
+      raise OSError(errno.ENOTDIR,
+                    'Fake os module: %r not a directory' % src)
+    for name in directory.contents:
+      srcname = self.filesystem.JoinPaths(src, name)
+      dstname = self.filesystem.JoinPaths(dst, name)
+      src_mode = self.filesystem.GetObject(srcname).st_mode
+      if stat.S_ISDIR(src_mode):
+        self.copytree(srcname, dstname, symlinks)
+      else:
+        self.copy2(srcname, dstname)
+
+  def move(self, src, dst):
+    """Rename a file or directory.
+
+    Args:
+      src: (str) source file or directory
+      dst: (str) if the src is a directory, the dst must not already exist
+    """
+    if stat.S_ISDIR(self.filesystem.GetObject(src).st_mode):
+      self.copytree(src, dst, symlinks=True)
+    else:
+      self.copy2(src, dst)
+    self.filesystem.RemoveObject(src)
+
+  def __getattr__(self, name):
+    """Forwards any non-faked calls to the standard shutil module."""
+    return getattr(self._shutil_module, name)
+
+
+def _RunDoctest():
+  # pylint: disable-msg=C6111,C6204,W0406
+  import doctest
+  import fake_filesystem_shutil
+  return doctest.testmod(fake_filesystem_shutil)
+
+
+if __name__ == '__main__':
+  _RunDoctest()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/fake_filesystem_shutil_test.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/fake_filesystem_shutil_test.py
new file mode 100755
index 0000000..4f26518
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/fake_filesystem_shutil_test.py
@@ -0,0 +1,305 @@
+#! /usr/bin/env python
+#
+# Copyright 2009 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Unittest for fake_filesystem_shutil."""
+
+import stat
+import time
+import sys
+if sys.version_info < (2, 7):
+    import unittest2 as unittest
+else:
+    import unittest
+
+import fake_filesystem
+import fake_filesystem_shutil
+
+
+class FakeShutilModuleTest(unittest.TestCase):
+
+  def setUp(self):
+    self.filesystem = fake_filesystem.FakeFilesystem(path_separator='/')
+    self.shutil = fake_filesystem_shutil.FakeShutilModule(self.filesystem)
+
+  def testRmtree(self):
+    directory = 'xyzzy'
+    self.filesystem.CreateDirectory(directory)
+    self.filesystem.CreateDirectory('%s/subdir' % directory)
+    self.filesystem.CreateFile('%s/subfile' % directory)
+    self.assertTrue(self.filesystem.Exists(directory))
+    self.shutil.rmtree(directory)
+    self.assertFalse(self.filesystem.Exists(directory))
+
+  def testCopy(self):
+    src_file = 'xyzzy'
+    dst_file = 'xyzzy_copy'
+    src_obj = self.filesystem.CreateFile(src_file)
+    src_obj.st_mode = ((src_obj.st_mode & ~0o7777) | 0o750)
+    self.assertTrue(self.filesystem.Exists(src_file))
+    self.assertFalse(self.filesystem.Exists(dst_file))
+    self.shutil.copy(src_file, dst_file)
+    self.assertTrue(self.filesystem.Exists(dst_file))
+    dst_obj = self.filesystem.GetObject(dst_file)
+    self.assertEqual(src_obj.st_mode, dst_obj.st_mode)
+
+  def testCopyDirectory(self):
+    src_file = 'xyzzy'
+    parent_directory = 'parent'
+    dst_file = '%s/%s' % (parent_directory, src_file)
+    src_obj = self.filesystem.CreateFile(src_file)
+    self.filesystem.CreateDirectory(parent_directory)
+    src_obj.st_mode = ((src_obj.st_mode & ~0o7777) | 0o750)
+    self.assertTrue(self.filesystem.Exists(src_file))
+    self.assertTrue(self.filesystem.Exists(parent_directory))
+    self.assertFalse(self.filesystem.Exists(dst_file))
+    self.shutil.copy(src_file, parent_directory)
+    self.assertTrue(self.filesystem.Exists(dst_file))
+    dst_obj = self.filesystem.GetObject(dst_file)
+    self.assertEqual(src_obj.st_mode, dst_obj.st_mode)
+
+  def testCopystat(self):
+    src_file = 'xyzzy'
+    dst_file = 'xyzzy_copy'
+    src_obj = self.filesystem.CreateFile(src_file)
+    dst_obj = self.filesystem.CreateFile(dst_file)
+    src_obj.st_mode = ((src_obj.st_mode & ~0o7777) | 0o750)
+    src_obj.st_uid = 123
+    src_obj.st_gid = 123
+    src_obj.st_atime = time.time()
+    src_obj.st_mtime = time.time()
+    self.assertTrue(self.filesystem.Exists(src_file))
+    self.assertTrue(self.filesystem.Exists(dst_file))
+    self.shutil.copystat(src_file, dst_file)
+    self.assertEqual(src_obj.st_mode, dst_obj.st_mode)
+    self.assertEqual(src_obj.st_uid, dst_obj.st_uid)
+    self.assertEqual(src_obj.st_gid, dst_obj.st_gid)
+    self.assertEqual(src_obj.st_atime, dst_obj.st_atime)
+    self.assertEqual(src_obj.st_mtime, dst_obj.st_mtime)
+
+  def testCopy2(self):
+    src_file = 'xyzzy'
+    dst_file = 'xyzzy_copy'
+    src_obj = self.filesystem.CreateFile(src_file)
+    src_obj.st_mode = ((src_obj.st_mode & ~0o7777) | 0o750)
+    src_obj.st_uid = 123
+    src_obj.st_gid = 123
+    src_obj.st_atime = time.time()
+    src_obj.st_mtime = time.time()
+    self.assertTrue(self.filesystem.Exists(src_file))
+    self.assertFalse(self.filesystem.Exists(dst_file))
+    self.shutil.copy2(src_file, dst_file)
+    self.assertTrue(self.filesystem.Exists(dst_file))
+    dst_obj = self.filesystem.GetObject(dst_file)
+    self.assertEqual(src_obj.st_mode, dst_obj.st_mode)
+    self.assertEqual(src_obj.st_uid, dst_obj.st_uid)
+    self.assertEqual(src_obj.st_gid, dst_obj.st_gid)
+    self.assertEqual(src_obj.st_atime, dst_obj.st_atime)
+    self.assertEqual(src_obj.st_mtime, dst_obj.st_mtime)
+
+  def testCopy2Directory(self):
+    src_file = 'xyzzy'
+    parent_directory = 'parent'
+    dst_file = '%s/%s' % (parent_directory, src_file)
+    src_obj = self.filesystem.CreateFile(src_file)
+    self.filesystem.CreateDirectory(parent_directory)
+    src_obj.st_mode = ((src_obj.st_mode & ~0o7777) | 0o750)
+    src_obj.st_uid = 123
+    src_obj.st_gid = 123
+    src_obj.st_atime = time.time()
+    src_obj.st_mtime = time.time()
+    self.assertTrue(self.filesystem.Exists(src_file))
+    self.assertTrue(self.filesystem.Exists(parent_directory))
+    self.assertFalse(self.filesystem.Exists(dst_file))
+    self.shutil.copy2(src_file, parent_directory)
+    self.assertTrue(self.filesystem.Exists(dst_file))
+    dst_obj = self.filesystem.GetObject(dst_file)
+    self.assertEqual(src_obj.st_mode, dst_obj.st_mode)
+    self.assertEqual(src_obj.st_uid, dst_obj.st_uid)
+    self.assertEqual(src_obj.st_gid, dst_obj.st_gid)
+    self.assertEqual(src_obj.st_atime, dst_obj.st_atime)
+    self.assertEqual(src_obj.st_mtime, dst_obj.st_mtime)
+
+  def testCopytree(self):
+    src_directory = 'xyzzy'
+    dst_directory = 'xyzzy_copy'
+    self.filesystem.CreateDirectory(src_directory)
+    self.filesystem.CreateDirectory('%s/subdir' % src_directory)
+    self.filesystem.CreateFile('%s/subfile' % src_directory)
+    self.assertTrue(self.filesystem.Exists(src_directory))
+    self.assertFalse(self.filesystem.Exists(dst_directory))
+    self.shutil.copytree(src_directory, dst_directory)
+    self.assertTrue(self.filesystem.Exists(dst_directory))
+    self.assertTrue(self.filesystem.Exists('%s/subdir' % dst_directory))
+    self.assertTrue(self.filesystem.Exists('%s/subfile' % dst_directory))
+
+  def testCopytreeSrcIsFile(self):
+    src_file = 'xyzzy'
+    dst_directory = 'xyzzy_copy'
+    self.filesystem.CreateFile(src_file)
+    self.assertTrue(self.filesystem.Exists(src_file))
+    self.assertFalse(self.filesystem.Exists(dst_directory))
+    self.assertRaises(OSError,
+                      self.shutil.copytree,
+                      src_file,
+                      dst_directory)
+
+  def testMoveFile(self):
+    src_file = 'original_xyzzy'
+    dst_file = 'moved_xyzzy'
+    self.filesystem.CreateFile(src_file)
+    self.assertTrue(self.filesystem.Exists(src_file))
+    self.assertFalse(self.filesystem.Exists(dst_file))
+    self.shutil.move(src_file, dst_file)
+    self.assertTrue(self.filesystem.Exists(dst_file))
+    self.assertFalse(self.filesystem.Exists(src_file))
+
+  def testMoveFileIntoDirectory(self):
+    src_file = 'xyzzy'
+    dst_directory = 'directory'
+    dst_file = '%s/%s' % (dst_directory, src_file)
+    self.filesystem.CreateFile(src_file)
+    self.filesystem.CreateDirectory(dst_directory)
+    self.assertTrue(self.filesystem.Exists(src_file))
+    self.assertFalse(self.filesystem.Exists(dst_file))
+    self.shutil.move(src_file, dst_directory)
+    self.assertTrue(self.filesystem.Exists(dst_file))
+    self.assertFalse(self.filesystem.Exists(src_file))
+
+  def testMoveDirectory(self):
+    src_directory = 'original_xyzzy'
+    dst_directory = 'moved_xyzzy'
+    self.filesystem.CreateDirectory(src_directory)
+    self.filesystem.CreateFile('%s/subfile' % src_directory)
+    self.filesystem.CreateDirectory('%s/subdir' % src_directory)
+    self.assertTrue(self.filesystem.Exists(src_directory))
+    self.assertFalse(self.filesystem.Exists(dst_directory))
+    self.shutil.move(src_directory, dst_directory)
+    self.assertTrue(self.filesystem.Exists(dst_directory))
+    self.assertTrue(self.filesystem.Exists('%s/subfile' % dst_directory))
+    self.assertTrue(self.filesystem.Exists('%s/subdir' % dst_directory))
+    self.assertFalse(self.filesystem.Exists(src_directory))
+
+
+class CopyFileTest(unittest.TestCase):
+
+  def setUp(self):
+    self.filesystem = fake_filesystem.FakeFilesystem(path_separator='/')
+    self.shutil = fake_filesystem_shutil.FakeShutilModule(self.filesystem)
+
+  def testCommonCase(self):
+    src_file = 'xyzzy'
+    dst_file = 'xyzzy_copy'
+    contents = 'contents of file'
+    self.filesystem.CreateFile(src_file, contents=contents)
+    self.assertTrue(self.filesystem.Exists(src_file))
+    self.assertFalse(self.filesystem.Exists(dst_file))
+    self.shutil.copyfile(src_file, dst_file)
+    self.assertTrue(self.filesystem.Exists(dst_file))
+    self.assertEqual(contents, self.filesystem.GetObject(dst_file).contents)
+
+  def testRaisesIfSourceAndDestAreTheSameFile(self):
+    src_file = 'xyzzy'
+    dst_file = src_file
+    contents = 'contents of file'
+    self.filesystem.CreateFile(src_file, contents=contents)
+    self.assertTrue(self.filesystem.Exists(src_file))
+    self.assertRaises(self.shutil.Error,
+                      self.shutil.copyfile, src_file, dst_file)
+
+  def testRaisesIfDestIsASymlinkToSrc(self):
+    src_file = '/tmp/foo'
+    dst_file = '/tmp/bar'
+    contents = 'contents of file'
+    self.filesystem.CreateFile(src_file, contents=contents)
+    self.filesystem.CreateLink(dst_file, src_file)
+    self.assertTrue(self.filesystem.Exists(src_file))
+    self.assertRaises(self.shutil.Error,
+                      self.shutil.copyfile, src_file, dst_file)
+
+  def testSucceedsIfDestExistsAndIsWritable(self):
+    src_file = 'xyzzy'
+    dst_file = 'xyzzy_copy'
+    src_contents = 'contents of source file'
+    dst_contents = 'contents of dest file'
+    self.filesystem.CreateFile(src_file, contents=src_contents)
+    self.filesystem.CreateFile(dst_file, contents=dst_contents)
+    self.assertTrue(self.filesystem.Exists(src_file))
+    self.assertTrue(self.filesystem.Exists(dst_file))
+    self.shutil.copyfile(src_file, dst_file)
+    self.assertTrue(self.filesystem.Exists(dst_file))
+    self.assertEqual(src_contents,
+                     self.filesystem.GetObject(dst_file).contents)
+
+  def testRaisesIfDestExistsAndIsNotWritable(self):
+    src_file = 'xyzzy'
+    dst_file = 'xyzzy_copy'
+    src_contents = 'contents of source file'
+    dst_contents = 'contents of dest file'
+    self.filesystem.CreateFile(src_file, contents=src_contents)
+    self.filesystem.CreateFile(dst_file,
+                               st_mode=stat.S_IFREG | 0o400,
+                               contents=dst_contents)
+    self.assertTrue(self.filesystem.Exists(src_file))
+    self.assertTrue(self.filesystem.Exists(dst_file))
+    self.assertRaises(IOError, self.shutil.copyfile, src_file, dst_file)
+
+  def testRaisesIfDestDirIsNotWritable(self):
+    src_file = 'xyzzy'
+    dst_dir = '/tmp/foo'
+    dst_file = '%s/%s' % (dst_dir, src_file)
+    src_contents = 'contents of source file'
+    self.filesystem.CreateFile(src_file, contents=src_contents)
+    self.filesystem.CreateDirectory(dst_dir, perm_bits=0o555)
+    self.assertTrue(self.filesystem.Exists(src_file))
+    self.assertTrue(self.filesystem.Exists(dst_dir))
+    self.assertRaises(IOError, self.shutil.copyfile, src_file, dst_file)
+
+  def testRaisesIfSrcDoesntExist(self):
+    src_file = 'xyzzy'
+    dst_file = 'xyzzy_copy'
+    self.assertFalse(self.filesystem.Exists(src_file))
+    self.assertRaises(IOError, self.shutil.copyfile, src_file, dst_file)
+
+  def testRaisesIfSrcNotReadable(self):
+    src_file = 'xyzzy'
+    dst_file = 'xyzzy_copy'
+    src_contents = 'contents of source file'
+    self.filesystem.CreateFile(src_file,
+                               st_mode=stat.S_IFREG | 0o000,
+                               contents=src_contents)
+    self.assertTrue(self.filesystem.Exists(src_file))
+    self.assertRaises(IOError, self.shutil.copyfile, src_file, dst_file)
+
+  def testRaisesIfSrcIsADirectory(self):
+    src_file = 'xyzzy'
+    dst_file = 'xyzzy_copy'
+    self.filesystem.CreateDirectory(src_file)
+    self.assertTrue(self.filesystem.Exists(src_file))
+    self.assertRaises(IOError, self.shutil.copyfile, src_file, dst_file)
+
+  def testRaisesIfDestIsADirectory(self):
+    src_file = 'xyzzy'
+    dst_dir = '/tmp/foo'
+    src_contents = 'contents of source file'
+    self.filesystem.CreateFile(src_file, contents=src_contents)
+    self.filesystem.CreateDirectory(dst_dir)
+    self.assertTrue(self.filesystem.Exists(src_file))
+    self.assertTrue(self.filesystem.Exists(dst_dir))
+    self.assertRaises(IOError, self.shutil.copyfile, src_file, dst_dir)
+
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/fake_filesystem_test.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/fake_filesystem_test.py
new file mode 100644
index 0000000..54aa256
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/fake_filesystem_test.py
@@ -0,0 +1,2925 @@
+#! /usr/bin/env python
+#
+# Copyright 2009 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Unittest for fake_filesystem module."""
+
+import errno
+import os
+import re
+import stat
+import sys
+import time
+if sys.version_info < (2, 7):
+    import unittest2 as unittest
+else:
+    import unittest
+
+import fake_filesystem
+
+
+def _GetDummyTime(start_time, increment):
+  def _DummyTime():
+    _DummyTime._curr_time += increment
+    return _DummyTime._curr_time
+  _DummyTime._curr_time = start_time - increment  # pylint: disable-msg=W0612
+  return _DummyTime
+
+
+class TestCase(unittest.TestCase):
+  is_windows = sys.platform.startswith('win')
+  is_cygwin = sys.platform == 'cygwin'
+
+  def assertModeEqual(self, expected, actual):
+    return self.assertEqual(stat.S_IMODE(expected), stat.S_IMODE(actual))
+
+
+class FakeDirectoryUnitTest(TestCase):
+  def setUp(self):
+    self.orig_time = time.time
+    time.time = _GetDummyTime(10, 1)
+    self.fake_file = fake_filesystem.FakeFile('foobar', contents='dummy_file')
+    self.fake_dir = fake_filesystem.FakeDirectory('somedir')
+
+  def tearDown(self):
+    time.time = self.orig_time
+
+  def testNewFileAndDirectory(self):
+    self.assertTrue(stat.S_IFREG & self.fake_file.st_mode)
+    self.assertTrue(stat.S_IFDIR & self.fake_dir.st_mode)
+    self.assertEqual({}, self.fake_dir.contents)
+    self.assertEqual(10, self.fake_file.st_ctime)
+
+  def testAddEntry(self):
+    self.fake_dir.AddEntry(self.fake_file)
+    self.assertEqual({'foobar': self.fake_file}, self.fake_dir.contents)
+
+  def testGetEntry(self):
+    self.fake_dir.AddEntry(self.fake_file)
+    self.assertEqual(self.fake_file, self.fake_dir.GetEntry('foobar'))
+
+  def testRemoveEntry(self):
+    self.fake_dir.AddEntry(self.fake_file)
+    self.assertEqual(self.fake_file, self.fake_dir.GetEntry('foobar'))
+    self.fake_dir.RemoveEntry('foobar')
+    self.assertRaises(KeyError, self.fake_dir.GetEntry, 'foobar')
+
+  def testShouldThrowIfSetSizeIsNotInteger(self):
+    self.assertRaises(IOError, self.fake_file.SetSize, 0.1)
+
+  def testShouldThrowIfSetSizeIsNegative(self):
+    self.assertRaises(IOError, self.fake_file.SetSize, -1)
+
+  def testProduceEmptyFileIfSetSizeIsZero(self):
+    self.fake_file.SetSize(0)
+    self.assertEqual('', self.fake_file.contents)
+
+  def testSetsContentEmptyIfSetSizeIsZero(self):
+    self.fake_file.SetSize(0)
+    self.assertEqual('', self.fake_file.contents)
+
+  def testTruncateFileIfSizeIsSmallerThanCurrentSize(self):
+    self.fake_file.SetSize(6)
+    self.assertEqual('dummy_', self.fake_file.contents)
+
+  def testLeaveFileUnchangedIfSizeIsEqualToCurrentSize(self):
+    self.fake_file.SetSize(10)
+    self.assertEqual('dummy_file', self.fake_file.contents)
+
+  def testPadsFileContentWithNullBytesIfSizeIsGreaterThanCurrentSize(self):
+    self.fake_file.SetSize(13)
+    self.assertEqual('dummy_file\0\0\0', self.fake_file.contents)
+
+  def testSetMTime(self):
+    self.assertEqual(10, self.fake_file.st_mtime)
+    self.fake_file.SetMTime(13)
+    self.assertEqual(13, self.fake_file.st_mtime)
+    self.fake_file.SetMTime(131)
+    self.assertEqual(131, self.fake_file.st_mtime)
+
+  def testFileInode(self):
+    filesystem = fake_filesystem.FakeFilesystem(path_separator='/')
+    fake_os = fake_filesystem.FakeOsModule(filesystem)
+    file_path = 'some_file1'
+    filesystem.CreateFile(file_path, contents='contents here1', inode=42)
+    self.assertEqual(42, fake_os.stat(file_path)[stat.ST_INO])
+
+    file_obj = filesystem.GetObject(file_path)
+    file_obj.SetIno(43)
+    self.assertEqual(43, fake_os.stat(file_path)[stat.ST_INO])
+
+  def testDirectoryInode(self):
+    filesystem = fake_filesystem.FakeFilesystem(path_separator='/')
+    fake_os = fake_filesystem.FakeOsModule(filesystem)
+    dirpath = 'testdir'
+    filesystem.CreateDirectory(dirpath, inode=42)
+    self.assertEqual(42, fake_os.stat(dirpath)[stat.ST_INO])
+
+    dir_obj = filesystem.GetObject(dirpath)
+    dir_obj.SetIno(43)
+    self.assertEqual(43, fake_os.stat(dirpath)[stat.ST_INO])
+
+
+class SetLargeFileSizeTest(FakeDirectoryUnitTest):
+
+  def testShouldThrowIfSizeIsNotInteger(self):
+    self.assertRaises(IOError, self.fake_file.SetLargeFileSize, 0.1)
+
+  def testShouldThrowIfSizeIsNegative(self):
+    self.assertRaises(IOError, self.fake_file.SetLargeFileSize, -1)
+
+  def testSetsContentNoneIfSizeIsNonNegativeInteger(self):
+    self.fake_file.SetLargeFileSize(1000000000)
+    self.assertEqual(None, self.fake_file.contents)
+    self.assertEqual(1000000000, self.fake_file.st_size)
+
+
+class NormalizePathTest(TestCase):
+  def setUp(self):
+    self.filesystem = fake_filesystem.FakeFilesystem(path_separator='/')
+    self.root_name = '/'
+
+  def testEmptyPathShouldGetNormalizedToRootPath(self):
+    self.assertEqual(self.root_name, self.filesystem.NormalizePath(''))
+
+  def testRootPathRemainsUnchanged(self):
+    self.assertEqual(self.root_name,
+                     self.filesystem.NormalizePath(self.root_name))
+
+  def testRelativePathForcedToCwd(self):
+    path = 'bar'
+    self.filesystem.cwd = '/foo'
+    self.assertEqual('/foo/bar', self.filesystem.NormalizePath(path))
+
+  def testAbsolutePathRemainsUnchanged(self):
+    path = '/foo/bar'
+    self.assertEqual(path, self.filesystem.NormalizePath(path))
+
+  def testDottedPathIsNormalized(self):
+    path = '/foo/..'
+    self.assertEqual('/', self.filesystem.NormalizePath(path))
+    path = 'foo/../bar'
+    self.assertEqual('/bar', self.filesystem.NormalizePath(path))
+
+  def testDotPathIsNormalized(self):
+    path = '.'
+    self.assertEqual('/', self.filesystem.NormalizePath(path))
+
+
+class GetPathComponentsTest(TestCase):
+  def setUp(self):
+    self.filesystem = fake_filesystem.FakeFilesystem(path_separator='/')
+    self.root_name = '/'
+
+  def testRootPathShouldReturnEmptyList(self):
+    self.assertEqual([], self.filesystem.GetPathComponents(self.root_name))
+
+  def testEmptyPathShouldReturnEmptyList(self):
+    self.assertEqual([], self.filesystem.GetPathComponents(''))
+
+  def testRelativePathWithOneComponentShouldReturnComponent(self):
+    self.assertEqual(['foo'], self.filesystem.GetPathComponents('foo'))
+
+  def testAbsolutePathWithOneComponentShouldReturnComponent(self):
+    self.assertEqual(['foo'], self.filesystem.GetPathComponents('/foo'))
+
+  def testTwoLevelRelativePathShouldReturnComponents(self):
+    self.assertEqual(['foo', 'bar'],
+                     self.filesystem.GetPathComponents('foo/bar'))
+
+  def testTwoLevelAbsolutePathShouldReturnComponents(self):
+    self.assertEqual(['foo', 'bar'],
+                     self.filesystem.GetPathComponents('/foo/bar'))
+
+
+class FakeFilesystemUnitTest(TestCase):
+  def setUp(self):
+    self.filesystem = fake_filesystem.FakeFilesystem(path_separator='/')
+    self.root_name = '/'
+    self.fake_file = fake_filesystem.FakeFile('foobar')
+    self.fake_child = fake_filesystem.FakeDirectory('foobaz')
+    self.fake_grandchild = fake_filesystem.FakeDirectory('quux')
+
+  def testNewFilesystem(self):
+    self.assertEqual('/', self.filesystem.path_separator)
+    self.assertTrue(stat.S_IFDIR & self.filesystem.root.st_mode)
+    self.assertEqual(self.root_name, self.filesystem.root.name)
+    self.assertEqual({}, self.filesystem.root.contents)
+
+  def testNoneRaisesTypeError(self):
+    self.assertRaises(TypeError, self.filesystem.Exists, None)
+
+  def testEmptyStringDoesNotExist(self):
+    self.assertFalse(self.filesystem.Exists(''))
+
+  def testExistsRoot(self):
+    self.assertTrue(self.filesystem.Exists(self.root_name))
+
+  def testExistsUnaddedFile(self):
+    self.assertFalse(self.filesystem.Exists(self.fake_file.name))
+
+  def testGetRootObject(self):
+    self.assertEqual(self.filesystem.root,
+                     self.filesystem.GetObject(self.root_name))
+
+  def testAddObjectToRoot(self):
+    self.filesystem.AddObject(self.root_name, self.fake_file)
+    self.assertEqual({'foobar': self.fake_file}, self.filesystem.root.contents)
+
+  def testExistsAddedFile(self):
+    self.filesystem.AddObject(self.root_name, self.fake_file)
+    self.assertTrue(self.filesystem.Exists(self.fake_file.name))
+
+  def testExistsRelativePath(self):
+    self.filesystem.CreateFile('/a/b/file_one')
+    self.filesystem.CreateFile('/a/c/file_two')
+    self.assertTrue(self.filesystem.Exists('a/b/../c/file_two'))
+    self.assertTrue(self.filesystem.Exists('/a/c/../b/file_one'))
+    self.assertTrue(self.filesystem.Exists('/a/c/../../a/b/file_one'))
+    self.assertFalse(self.filesystem.Exists('a/b/../z/d'))
+    self.assertFalse(self.filesystem.Exists('a/b/../z/../c/file_two'))
+    self.filesystem.cwd = '/a/c'
+    self.assertTrue(self.filesystem.Exists('../b/file_one'))
+    self.assertTrue(self.filesystem.Exists('../../a/b/file_one'))
+    self.assertTrue(self.filesystem.Exists('../../a/b/../../a/c/file_two'))
+    self.assertFalse(self.filesystem.Exists('../z/file_one'))
+    self.assertFalse(self.filesystem.Exists('../z/../c/file_two'))
+
+  def testGetObjectFromRoot(self):
+    self.filesystem.AddObject(self.root_name, self.fake_file)
+    self.assertEqual(self.fake_file, self.filesystem.GetObject('foobar'))
+
+  def testGetNonexistentObjectFromRootError(self):
+    self.filesystem.AddObject(self.root_name, self.fake_file)
+    self.assertEqual(self.fake_file, self.filesystem.GetObject('foobar'))
+    self.assertRaises(IOError, self.filesystem.GetObject,
+                      'some_bogus_filename')
+
+  def testRemoveObjectFromRoot(self):
+    self.filesystem.AddObject(self.root_name, self.fake_file)
+    self.filesystem.RemoveObject(self.fake_file.name)
+    self.assertRaises(IOError, self.filesystem.GetObject, self.fake_file.name)
+
+  def testRemoveNonexistenObjectFromRootError(self):
+    self.assertRaises(IOError, self.filesystem.RemoveObject,
+                      'some_bogus_filename')
+
+  def testExistsRemovedFile(self):
+    self.filesystem.AddObject(self.root_name, self.fake_file)
+    self.filesystem.RemoveObject(self.fake_file.name)
+    self.assertFalse(self.filesystem.Exists(self.fake_file.name))
+
+  def testAddObjectToChild(self):
+    self.filesystem.AddObject(self.root_name, self.fake_child)
+    self.filesystem.AddObject(self.fake_child.name, self.fake_file)
+    self.assertEqual(
+        {self.fake_file.name: self.fake_file},
+        self.filesystem.root.GetEntry(self.fake_child.name).contents)
+
+  def testAddObjectToRegularFileError(self):
+    self.filesystem.AddObject(self.root_name, self.fake_file)
+    self.assertRaises(IOError, self.filesystem.AddObject,
+                      self.fake_file.name, self.fake_file)
+
+  def testExistsFileAddedToChild(self):
+    self.filesystem.AddObject(self.root_name, self.fake_child)
+    self.filesystem.AddObject(self.fake_child.name, self.fake_file)
+    path = self.filesystem.JoinPaths(self.fake_child.name,
+                                     self.fake_file.name)
+    self.assertTrue(self.filesystem.Exists(path))
+
+  def testGetObjectFromChild(self):
+    self.filesystem.AddObject(self.root_name, self.fake_child)
+    self.filesystem.AddObject(self.fake_child.name, self.fake_file)
+    self.assertEqual(self.fake_file,
+                     self.filesystem.GetObject(
+                         self.filesystem.JoinPaths(self.fake_child.name,
+                                                   self.fake_file.name)))
+
+  def testGetNonexistentObjectFromChildError(self):
+    self.filesystem.AddObject(self.root_name, self.fake_child)
+    self.filesystem.AddObject(self.fake_child.name, self.fake_file)
+    self.assertRaises(IOError, self.filesystem.GetObject,
+                      self.filesystem.JoinPaths(self.fake_child.name,
+                                                'some_bogus_filename'))
+
+  def testRemoveObjectFromChild(self):
+    self.filesystem.AddObject(self.root_name, self.fake_child)
+    self.filesystem.AddObject(self.fake_child.name, self.fake_file)
+    target_path = self.filesystem.JoinPaths(self.fake_child.name,
+                                            self.fake_file.name)
+    self.filesystem.RemoveObject(target_path)
+    self.assertRaises(IOError, self.filesystem.GetObject, target_path)
+
+  def testRemoveObjectFromChildError(self):
+    self.filesystem.AddObject(self.root_name, self.fake_child)
+    self.assertRaises(IOError, self.filesystem.RemoveObject,
+                      self.filesystem.JoinPaths(self.fake_child.name,
+                                                'some_bogus_filename'))
+
+  def testRemoveObjectFromNonDirectoryError(self):
+    self.filesystem.AddObject(self.root_name, self.fake_file)
+    self.assertRaises(
+        IOError, self.filesystem.RemoveObject,
+        self.filesystem.JoinPaths(
+            '%s' % self.fake_file.name,
+            'file_does_not_matter_since_parent_not_a_directory'))
+
+  def testExistsFileRemovedFromChild(self):
+    self.filesystem.AddObject(self.root_name, self.fake_child)
+    self.filesystem.AddObject(self.fake_child.name, self.fake_file)
+    path = self.filesystem.JoinPaths(self.fake_child.name,
+                                     self.fake_file.name)
+    self.filesystem.RemoveObject(path)
+    self.assertFalse(self.filesystem.Exists(path))
+
+  def testOperateOnGrandchildDirectory(self):
+    self.filesystem.AddObject(self.root_name, self.fake_child)
+    self.filesystem.AddObject(self.fake_child.name, self.fake_grandchild)
+    grandchild_directory = self.filesystem.JoinPaths(self.fake_child.name,
+                                                     self.fake_grandchild.name)
+    grandchild_file = self.filesystem.JoinPaths(grandchild_directory,
+                                                self.fake_file.name)
+    self.assertRaises(IOError, self.filesystem.GetObject, grandchild_file)
+    self.filesystem.AddObject(grandchild_directory, self.fake_file)
+    self.assertEqual(self.fake_file,
+                     self.filesystem.GetObject(grandchild_file))
+    self.assertTrue(self.filesystem.Exists(grandchild_file))
+    self.filesystem.RemoveObject(grandchild_file)
+    self.assertRaises(IOError, self.filesystem.GetObject, grandchild_file)
+    self.assertFalse(self.filesystem.Exists(grandchild_file))
+
+  def testCreateDirectoryInRootDirectory(self):
+    path = 'foo'
+    self.filesystem.CreateDirectory(path)
+    new_dir = self.filesystem.GetObject(path)
+    self.assertEqual(os.path.basename(path), new_dir.name)
+    self.assertTrue(stat.S_IFDIR & new_dir.st_mode)
+
+  def testCreateDirectoryInRootDirectoryAlreadyExistsError(self):
+    path = 'foo'
+    self.filesystem.CreateDirectory(path)
+    self.assertRaises(OSError, self.filesystem.CreateDirectory, path)
+
+  def testCreateDirectory(self):
+    path = 'foo/bar/baz'
+    self.filesystem.CreateDirectory(path)
+    new_dir = self.filesystem.GetObject(path)
+    self.assertEqual(os.path.basename(path), new_dir.name)
+    self.assertTrue(stat.S_IFDIR & new_dir.st_mode)
+
+    # Create second directory to make sure first is OK.
+    path = '%s/quux' % path
+    self.filesystem.CreateDirectory(path)
+    new_dir = self.filesystem.GetObject(path)
+    self.assertEqual(os.path.basename(path), new_dir.name)
+    self.assertTrue(stat.S_IFDIR & new_dir.st_mode)
+
+  def testCreateDirectoryAlreadyExistsError(self):
+    path = 'foo/bar/baz'
+    self.filesystem.CreateDirectory(path)
+    self.assertRaises(OSError, self.filesystem.CreateDirectory, path)
+
+  def testCreateFileInCurrentDirectory(self):
+    path = 'foo'
+    contents = 'dummy data'
+    self.filesystem.CreateFile(path, contents=contents)
+    self.assertTrue(self.filesystem.Exists(path))
+    self.assertFalse(self.filesystem.Exists(os.path.dirname(path)))
+    path = './%s' % path
+    self.assertTrue(self.filesystem.Exists(os.path.dirname(path)))
+
+  def testCreateFileInRootDirectory(self):
+    path = '/foo'
+    contents = 'dummy data'
+    self.filesystem.CreateFile(path, contents=contents)
+    new_file = self.filesystem.GetObject(path)
+    self.assertTrue(self.filesystem.Exists(path))
+    self.assertTrue(self.filesystem.Exists(os.path.dirname(path)))
+    self.assertEqual(os.path.basename(path), new_file.name)
+    self.assertTrue(stat.S_IFREG & new_file.st_mode)
+    self.assertEqual(contents, new_file.contents)
+
+  def testCreateFileWithSizeButNoContentCreatesLargeFile(self):
+    path = 'large_foo_bar'
+    self.filesystem.CreateFile(path, st_size=100000000)
+    new_file = self.filesystem.GetObject(path)
+    self.assertEqual(None, new_file.contents)
+    self.assertEqual(100000000, new_file.st_size)
+
+  def testCreateFileInRootDirectoryAlreadyExistsError(self):
+    path = 'foo'
+    self.filesystem.CreateFile(path)
+    self.assertRaises(IOError, self.filesystem.CreateFile, path)
+
+  def testCreateFile(self):
+    path = 'foo/bar/baz'
+    retval = self.filesystem.CreateFile(path, contents='dummy_data')
+    self.assertTrue(self.filesystem.Exists(path))
+    self.assertTrue(self.filesystem.Exists(os.path.dirname(path)))
+    new_file = self.filesystem.GetObject(path)
+    self.assertEqual(os.path.basename(path), new_file.name)
+    self.assertTrue(stat.S_IFREG & new_file.st_mode)
+    self.assertEqual(new_file, retval)
+
+  def testCreateFileAlreadyExistsError(self):
+    path = 'foo/bar/baz'
+    self.filesystem.CreateFile(path, contents='dummy_data')
+    self.assertRaises(IOError, self.filesystem.CreateFile, path)
+
+  def testCreateLink(self):
+    path = 'foo/bar/baz'
+    target_path = 'foo/bar/quux'
+    new_file = self.filesystem.CreateLink(path, 'quux')
+    # Neither the path not the final target exists before we actually write to
+    # one of them, even though the link appears in the file system.
+    self.assertFalse(self.filesystem.Exists(path))
+    self.assertFalse(self.filesystem.Exists(target_path))
+    self.assertTrue(stat.S_IFLNK & new_file.st_mode)
+
+    # but once we write the linked to file, they both will exist.
+    self.filesystem.CreateFile(target_path)
+    self.assertTrue(self.filesystem.Exists(path))
+    self.assertTrue(self.filesystem.Exists(target_path))
+
+  def testResolveObject(self):
+    target_path = 'dir/target'
+    target_contents = '0123456789ABCDEF'
+    link_name = 'x'
+    self.filesystem.CreateDirectory('dir')
+    self.filesystem.CreateFile('dir/target', contents=target_contents)
+    self.filesystem.CreateLink(link_name, target_path)
+    obj = self.filesystem.ResolveObject(link_name)
+    self.assertEqual('target', obj.name)
+    self.assertEqual(target_contents, obj.contents)
+
+  def testLresolveObject(self):
+    target_path = 'dir/target'
+    target_contents = '0123456789ABCDEF'
+    link_name = 'x'
+    self.filesystem.CreateDirectory('dir')
+    self.filesystem.CreateFile('dir/target', contents=target_contents)
+    self.filesystem.CreateLink(link_name, target_path)
+    obj = self.filesystem.LResolveObject(link_name)
+    self.assertEqual(link_name, obj.name)
+    self.assertEqual(target_path, obj.contents)
+
+  def testDirectoryAccessOnFile(self):
+    self.filesystem.CreateFile('not_a_dir')
+    self.assertRaises(IOError, self.filesystem.ResolveObject, 'not_a_dir/foo')
+    self.assertRaises(IOError, self.filesystem.ResolveObject,
+                      'not_a_dir/foo/bar')
+    self.assertRaises(IOError, self.filesystem.LResolveObject, 'not_a_dir/foo')
+    self.assertRaises(IOError, self.filesystem.LResolveObject,
+                      'not_a_dir/foo/bar')
+
+
+class FakeOsModuleTest(TestCase):
+
+  def setUp(self):
+    self.filesystem = fake_filesystem.FakeFilesystem(path_separator='/')
+    self.os = fake_filesystem.FakeOsModule(self.filesystem)
+    self.rwx = self.os.R_OK | self.os.W_OK | self.os.X_OK
+    self.rw = self.os.R_OK | self.os.W_OK
+    self.orig_time = time.time
+    time.time = _GetDummyTime(200, 20)
+
+  def tearDown(self):
+    time.time = self.orig_time
+
+  def assertRaisesWithRegexpMatch(self, expected_exception, expected_regexp,
+                                  callable_obj, *args, **kwargs):
+    """Asserts that the message in a raised exception matches the given regexp.
+
+    Args:
+      expected_exception: Exception class expected to be raised.
+      expected_regexp: Regexp (re pattern object or string) expected to be
+        found in error message.
+      callable_obj: Function to be called.
+      *args: Extra args.
+      **kwargs: Extra kwargs.
+    """
+    try:
+      callable_obj(*args, **kwargs)
+    except expected_exception as err:
+      if isinstance(expected_regexp, str):
+        expected_regexp = re.compile(expected_regexp)
+      self.assertTrue(
+          expected_regexp.search(str(err)),
+          '"%s" does not match "%s"' % (expected_regexp.pattern, str(err)))
+    else:
+      self.fail(expected_exception.__name__ + ' not raised')
+
+  def testChdir(self):
+    """chdir should work on a directory."""
+    directory = '/foo'
+    self.filesystem.CreateDirectory(directory)
+    self.os.chdir(directory)
+
+  def testChdirFailsNonExist(self):
+    """chdir should raise OSError if the target does not exist."""
+    directory = '/no/such/directory'
+    self.assertRaises(OSError, self.os.chdir, directory)
+
+  def testChdirFailsNonDirectory(self):
+    """chdir should raies OSError if the target is not a directory."""
+    filename = '/foo/bar'
+    self.filesystem.CreateFile(filename)
+    self.assertRaises(OSError, self.os.chdir, filename)
+
+  def testConsecutiveChdir(self):
+    """Consecutive relative chdir calls should work."""
+    dir1 = 'foo'
+    dir2 = 'bar'
+    full_dirname = self.os.path.join(dir1, dir2)
+    self.filesystem.CreateDirectory(full_dirname)
+    self.os.chdir(dir1)
+    self.os.chdir(dir2)
+    self.assertEqual(self.os.getcwd(), self.os.path.sep + full_dirname)
+
+  def testBackwardsChdir(self):
+    """chdir into '..' should behave appropriately."""
+    rootdir = self.os.getcwd()
+    dirname = 'foo'
+    abs_dirname = self.os.path.abspath(dirname)
+    self.filesystem.CreateDirectory(dirname)
+    self.os.chdir(dirname)
+    self.assertEqual(abs_dirname, self.os.getcwd())
+    self.os.chdir('..')
+    self.assertEqual(rootdir, self.os.getcwd())
+    self.os.chdir(self.os.path.join(dirname, '..'))
+    self.assertEqual(rootdir, self.os.getcwd())
+
+  def testGetCwd(self):
+    dirname = '/foo/bar'
+    self.filesystem.CreateDirectory(dirname)
+    self.assertEqual(self.os.getcwd(), self.os.path.sep)
+    self.os.chdir(dirname)
+    self.assertEqual(self.os.getcwd(), dirname)
+
+  def testListdir(self):
+    directory = 'xyzzy/plugh'
+    files = ['foo', 'bar', 'baz']
+    for f in files:
+      self.filesystem.CreateFile('%s/%s' % (directory, f))
+    files.sort()
+    self.assertEqual(files, self.os.listdir(directory))
+
+  def testListdirOnSymlink(self):
+    directory = 'xyzzy'
+    files = ['foo', 'bar', 'baz']
+    for f in files:
+      self.filesystem.CreateFile('%s/%s' % (directory, f))
+    self.filesystem.CreateLink('symlink', 'xyzzy')
+    files.sort()
+    self.assertEqual(files, self.os.listdir('symlink'))
+
+  def testListdirError(self):
+    file_path = 'foo/bar/baz'
+    self.filesystem.CreateFile(file_path)
+    self.assertRaises(OSError, self.os.listdir, file_path)
+
+  def testExistsCurrentDir(self):
+    self.assertTrue(self.filesystem.Exists('.'))
+
+  def testListdirCurrent(self):
+    files = ['foo', 'bar', 'baz']
+    for f in files:
+      self.filesystem.CreateFile('%s' % f)
+    files.sort()
+    self.assertEqual(files, self.os.listdir('.'))
+
+  def testFdopen(self):
+    fake_open = fake_filesystem.FakeFileOpen(self.filesystem)
+    file_path1 = 'some_file1'
+    self.filesystem.CreateFile(file_path1, contents='contents here1')
+    fake_file1 = fake_open(file_path1, 'r')
+    self.assertEqual(0, fake_file1.fileno())
+
+    self.assertFalse(self.os.fdopen(0) is fake_file1)
+
+    self.assertRaises(TypeError, self.os.fdopen, None)
+    self.assertRaises(TypeError, self.os.fdopen, 'a string')
+
+  def testOutOfRangeFdopen(self):
+    # We haven't created any files, so even 0 is out of range.
+    self.assertRaises(OSError, self.os.fdopen, 0)
+
+  def testClosedFileDescriptor(self):
+    fake_open = fake_filesystem.FakeFileOpen(self.filesystem)
+    first_path = 'some_file1'
+    second_path = 'some_file2'
+    third_path = 'some_file3'
+    self.filesystem.CreateFile(first_path, contents='contents here1')
+    self.filesystem.CreateFile(second_path, contents='contents here2')
+    self.filesystem.CreateFile(third_path, contents='contents here3')
+
+    fake_file1 = fake_open(first_path, 'r')
+    fake_file2 = fake_open(second_path, 'r')
+    fake_file3 = fake_open(third_path, 'r')
+    self.assertEqual(0, fake_file1.fileno())
+    self.assertEqual(1, fake_file2.fileno())
+    self.assertEqual(2, fake_file3.fileno())
+
+    fileno2 = fake_file2.fileno()
+    self.os.close(fileno2)
+    self.assertRaises(OSError, self.os.close, fileno2)
+    self.assertEqual(0, fake_file1.fileno())
+    self.assertEqual(2, fake_file3.fileno())
+
+    self.assertFalse(self.os.fdopen(0) is fake_file1)
+    self.assertFalse(self.os.fdopen(2) is fake_file3)
+    self.assertRaises(OSError, self.os.fdopen, 1)
+
+  def testFdopenMode(self):
+    fake_open = fake_filesystem.FakeFileOpen(self.filesystem)
+    file_path1 = 'some_file1'
+    self.filesystem.CreateFile(file_path1, contents='contents here1',
+                               st_mode=((stat.S_IFREG | 0o666) ^ stat.S_IWRITE))
+
+    fake_file1 = fake_open(file_path1, 'r')
+    self.assertEqual(0, fake_file1.fileno())
+    self.os.fdopen(0)
+    self.os.fdopen(0, mode='r')
+    exception = OSError if sys.version_info < (3, 0) else IOError
+    self.assertRaises(exception, self.os.fdopen, 0, 'w')
+
+  def testLowLevelOpenCreate(self):
+    file_path = 'file1'
+    # this is the low-level open, not FakeFileOpen
+    fileno = self.os.open(file_path, self.os.O_CREAT)
+    self.assertEqual(0, fileno)
+    self.assertTrue(self.os.path.exists(file_path))
+
+  def testLowLevelOpenCreateMode(self):
+    file_path = 'file1'
+    fileno = self.os.open(file_path, self.os.O_CREAT, 0o700)
+    self.assertEqual(0, fileno)
+    self.assertTrue(self.os.path.exists(file_path))
+    self.assertModeEqual(0o700, self.os.stat(file_path).st_mode)
+
+  def testLowLevelOpenCreateModeUnsupported(self):
+    file_path = 'file1'
+    fake_flag = 0b100000000000000000000000
+    self.assertRaises(NotImplementedError, self.os.open, file_path, fake_flag)
+
+  def testLowLevelWriteRead(self):
+    file_path = 'file1'
+    self.filesystem.CreateFile(file_path, contents='orig contents')
+    new_contents = '1234567890abcdef'
+    fake_open = fake_filesystem.FakeFileOpen(self.filesystem)
+
+    fh = fake_open(file_path, 'w')
+    fileno = fh.fileno()
+
+    self.assertEqual(len(new_contents), self.os.write(fileno, new_contents))
+    self.assertEqual(new_contents,
+                     self.filesystem.GetObject(file_path).contents)
+    self.os.close(fileno)
+
+    fh = fake_open(file_path, 'r')
+    fileno = fh.fileno()
+    self.assertEqual('', self.os.read(fileno, 0))
+    self.assertEqual(new_contents[0:2], self.os.read(fileno, 2))
+    self.assertEqual(new_contents[2:10], self.os.read(fileno, 8))
+    self.assertEqual(new_contents[10:], self.os.read(fileno, 100))
+    self.assertEqual('', self.os.read(fileno, 10))
+    self.os.close(fileno)
+
+    self.assertRaises(OSError, self.os.write, fileno, new_contents)
+    self.assertRaises(OSError, self.os.read, fileno, 10)
+
+  def testFstat(self):
+    directory = 'xyzzy'
+    file_path = '%s/plugh' % directory
+    self.filesystem.CreateFile(file_path, contents='ABCDE')
+    fake_open = fake_filesystem.FakeFileOpen(self.filesystem)
+    file_obj = fake_open(file_path)
+    fileno = file_obj.fileno()
+    self.assertTrue(stat.S_IFREG & self.os.fstat(fileno)[stat.ST_MODE])
+    self.assertTrue(stat.S_IFREG & self.os.fstat(fileno).st_mode)
+    self.assertEqual(5, self.os.fstat(fileno)[stat.ST_SIZE])
+
+  def testStat(self):
+    directory = 'xyzzy'
+    file_path = '%s/plugh' % directory
+    self.filesystem.CreateFile(file_path, contents='ABCDE')
+    self.assertTrue(stat.S_IFDIR & self.os.stat(directory)[stat.ST_MODE])
+    self.assertTrue(stat.S_IFREG & self.os.stat(file_path)[stat.ST_MODE])
+    self.assertTrue(stat.S_IFREG & self.os.stat(file_path).st_mode)
+    self.assertEqual(5, self.os.stat(file_path)[stat.ST_SIZE])
+
+  def testLstat(self):
+    directory = 'xyzzy'
+    base_name = 'plugh'
+    file_contents = 'frobozz'
+    # Just make sure we didn't accidentally make our test data meaningless.
+    self.assertNotEqual(len(base_name), len(file_contents))
+    file_path = '%s/%s' % (directory, base_name)
+    link_path = '%s/link' % directory
+    self.filesystem.CreateFile(file_path, contents=file_contents)
+    self.filesystem.CreateLink(link_path, base_name)
+    self.assertEqual(len(file_contents), self.os.lstat(file_path)[stat.ST_SIZE])
+    self.assertEqual(len(base_name), self.os.lstat(link_path)[stat.ST_SIZE])
+
+  def testStatNonExistentFile(self):
+    # set up
+    file_path = '/non/existent/file'
+    self.assertFalse(self.filesystem.Exists(file_path))
+    # actual tests
+    try:
+      # Use try-catch to check exception attributes.
+      self.os.stat(file_path)
+      self.fail('Exception is expected.')  # COV_NF_LINE
+    except OSError as os_error:
+      self.assertEqual(errno.ENOENT, os_error.errno)
+      self.assertEqual(file_path, os_error.filename)
+
+  def testReadlink(self):
+    link_path = 'foo/bar/baz'
+    target = 'tarJAY'
+    self.filesystem.CreateLink(link_path, target)
+    self.assertEqual(self.os.readlink(link_path), target)
+
+  def testReadlinkRaisesIfPathIsNotALink(self):
+    file_path = 'foo/bar/eleventyone'
+    self.filesystem.CreateFile(file_path)
+    self.assertRaises(OSError, self.os.readlink, file_path)
+
+  def testReadlinkRaisesIfPathDoesNotExist(self):
+    self.assertRaises(OSError, self.os.readlink, '/this/path/does/not/exist')
+
+  def testReadlinkRaisesIfPathIsNone(self):
+    self.assertRaises(TypeError, self.os.readlink, None)
+
+  def testReadlinkWithLinksInPath(self):
+    self.filesystem.CreateLink('/meyer/lemon/pie', 'yum')
+    self.filesystem.CreateLink('/geo/metro', '/meyer')
+    self.assertEqual('yum', self.os.readlink('/geo/metro/lemon/pie'))
+
+  def testReadlinkWithChainedLinksInPath(self):
+    self.filesystem.CreateLink('/eastern/european/wolfhounds/chase', 'cats')
+    self.filesystem.CreateLink('/russian', '/eastern/european')
+    self.filesystem.CreateLink('/dogs', '/russian/wolfhounds')
+    self.assertEqual('cats', self.os.readlink('/dogs/chase'))
+
+  def testRemoveDir(self):
+    directory = 'xyzzy'
+    dir_path = '/%s/plugh' % directory
+    self.filesystem.CreateDirectory(dir_path)
+    self.assertTrue(self.filesystem.Exists(dir_path))
+    self.assertRaises(OSError, self.os.remove, dir_path)
+    self.assertTrue(self.filesystem.Exists(dir_path))
+    self.os.chdir(directory)
+    self.assertRaises(OSError, self.os.remove, 'plugh')
+    self.assertTrue(self.filesystem.Exists(dir_path))
+    self.assertRaises(OSError, self.os.remove, '/plugh')
+
+  def testRemoveFile(self):
+    directory = 'zzy'
+    file_path = '%s/plugh' % directory
+    self.filesystem.CreateFile(file_path)
+    self.assertTrue(self.filesystem.Exists(file_path))
+    self.os.remove(file_path)
+    self.assertFalse(self.filesystem.Exists(file_path))
+
+  def testRemoveFileNoDirectory(self):
+    directory = 'zzy'
+    file_name = 'plugh'
+    file_path = '%s/%s' % (directory, file_name)
+    self.filesystem.CreateFile(file_path)
+    self.assertTrue(self.filesystem.Exists(file_path))
+    self.os.chdir(directory)
+    self.os.remove(file_name)
+    self.assertFalse(self.filesystem.Exists(file_path))
+
+  def testRemoveFileRelativePath(self):
+    original_dir = self.os.getcwd()
+    directory = 'zzy'
+    subdirectory = self.os.path.join(directory, directory)
+    file_name = 'plugh'
+    file_path = '%s/%s' % (directory, file_name)
+    file_path_relative = self.os.path.join('..', file_name)
+    self.filesystem.CreateFile(file_path)
+    self.assertTrue(self.filesystem.Exists(file_path))
+    self.filesystem.CreateDirectory(subdirectory)
+    self.assertTrue(self.filesystem.Exists(subdirectory))
+    self.os.chdir(subdirectory)
+    self.os.remove(file_path_relative)
+    self.assertFalse(self.filesystem.Exists(file_path_relative))
+    self.os.chdir(original_dir)
+    self.assertFalse(self.filesystem.Exists(file_path))
+
+  def testRemoveDirRaisesError(self):
+    directory = 'zzy'
+    self.filesystem.CreateDirectory(directory)
+    self.assertRaises(OSError,
+                      self.os.remove,
+                      directory)
+
+  def testRemoveSymlinkToDir(self):
+    directory = 'zzy'
+    link = 'link_to_dir'
+    self.filesystem.CreateDirectory(directory)
+    self.os.symlink(directory, link)
+    self.assertTrue(self.filesystem.Exists(directory))
+    self.assertTrue(self.filesystem.Exists(link))
+    self.os.remove(link)
+    self.assertTrue(self.filesystem.Exists(directory))
+    self.assertFalse(self.filesystem.Exists(link))
+
+  def testUnlink(self):
+    self.assertTrue(self.os.unlink == self.os.remove)
+
+  def testUnlinkRaisesIfNotExist(self):
+    file_path = '/file/does/not/exist'
+    self.assertFalse(self.filesystem.Exists(file_path))
+    self.assertRaises(OSError, self.os.unlink, file_path)
+
+  def testRenameToNonexistentFile(self):
+    """Can rename a file to an unused name."""
+    directory = 'xyzzy'
+    old_file_path = '%s/plugh_old' % directory
+    new_file_path = '%s/plugh_new' % directory
+    self.filesystem.CreateFile(old_file_path, contents='test contents')
+    self.assertTrue(self.filesystem.Exists(old_file_path))
+    self.assertFalse(self.filesystem.Exists(new_file_path))
+    self.os.rename(old_file_path, new_file_path)
+    self.assertFalse(self.filesystem.Exists(old_file_path))
+    self.assertTrue(self.filesystem.Exists(new_file_path))
+    self.assertEqual('test contents',
+                     self.filesystem.GetObject(new_file_path).contents)
+
+  def testRenameDirectory(self):
+    """Can rename a directory to an unused name."""
+    for old_path, new_path in [('wxyyw', 'xyzzy'), ('/abccb', 'cdeed')]:
+      self.filesystem.CreateFile('%s/plugh' % old_path, contents='test')
+      self.assertTrue(self.filesystem.Exists(old_path))
+      self.assertFalse(self.filesystem.Exists(new_path))
+      self.os.rename(old_path, new_path)
+      self.assertFalse(self.filesystem.Exists(old_path))
+      self.assertTrue(self.filesystem.Exists(new_path))
+      self.assertEqual(
+          'test', self.filesystem.GetObject('%s/plugh' % new_path).contents)
+
+  def testRenameToExistentFile(self):
+    """Can rename a file to a used name."""
+    directory = 'xyzzy'
+    old_file_path = '%s/plugh_old' % directory
+    new_file_path = '%s/plugh_new' % directory
+    self.filesystem.CreateFile(old_file_path, contents='test contents 1')
+    self.filesystem.CreateFile(new_file_path, contents='test contents 2')
+    self.assertTrue(self.filesystem.Exists(old_file_path))
+    self.assertTrue(self.filesystem.Exists(new_file_path))
+    self.os.rename(old_file_path, new_file_path)
+    self.assertFalse(self.filesystem.Exists(old_file_path))
+    self.assertTrue(self.filesystem.Exists(new_file_path))
+    self.assertEqual('test contents 1',
+                     self.filesystem.GetObject(new_file_path).contents)
+
+  def testRenameToNonexistentDir(self):
+    """Can rename a file to a name in a nonexistent dir."""
+    directory = 'xyzzy'
+    old_file_path = '%s/plugh_old' % directory
+    new_file_path = '%s/no_such_path/plugh_new' % directory
+    self.filesystem.CreateFile(old_file_path, contents='test contents')
+    self.assertTrue(self.filesystem.Exists(old_file_path))
+    self.assertFalse(self.filesystem.Exists(new_file_path))
+    self.assertRaises(IOError, self.os.rename, old_file_path, new_file_path)
+    self.assertTrue(self.filesystem.Exists(old_file_path))
+    self.assertFalse(self.filesystem.Exists(new_file_path))
+    self.assertEqual('test contents',
+                     self.filesystem.GetObject(old_file_path).contents)
+
+  def testRenameNonexistentFileShouldRaiseError(self):
+    """Can't rename a file that doesn't exist."""
+    self.assertRaises(OSError,
+                      self.os.rename,
+                      'nonexistent-foo',
+                      'doesn\'t-matter-bar')
+
+  def testRenameEmptyDir(self):
+    """Test a rename of an empty directory."""
+    directory = 'xyzzy'
+    before_dir = '%s/empty' % directory
+    after_dir = '%s/unused' % directory
+    self.filesystem.CreateDirectory(before_dir)
+    self.assertTrue(self.filesystem.Exists('%s/.' % before_dir))
+    self.assertFalse(self.filesystem.Exists(after_dir))
+    self.os.rename(before_dir, after_dir)
+    self.assertFalse(self.filesystem.Exists(before_dir))
+    self.assertTrue(self.filesystem.Exists('%s/.' % after_dir))
+
+  def testRenameDir(self):
+    """Test a rename of a directory."""
+    directory = 'xyzzy'
+    before_dir = '%s/before' % directory
+    before_file = '%s/before/file' % directory
+    after_dir = '%s/after' % directory
+    after_file = '%s/after/file' % directory
+    self.filesystem.CreateDirectory(before_dir)
+    self.filesystem.CreateFile(before_file, contents='payload')
+    self.assertTrue(self.filesystem.Exists(before_dir))
+    self.assertTrue(self.filesystem.Exists(before_file))
+    self.assertFalse(self.filesystem.Exists(after_dir))
+    self.assertFalse(self.filesystem.Exists(after_file))
+    self.os.rename(before_dir, after_dir)
+    self.assertFalse(self.filesystem.Exists(before_dir))
+    self.assertFalse(self.filesystem.Exists(before_file))
+    self.assertTrue(self.filesystem.Exists(after_dir))
+    self.assertTrue(self.filesystem.Exists(after_file))
+    self.assertEqual('payload',
+                     self.filesystem.GetObject(after_file).contents)
+
+  def testRenamePreservesStat(self):
+    """Test if rename preserves mtime."""
+    directory = 'xyzzy'
+    old_file_path = '%s/plugh_old' % directory
+    new_file_path = '%s/plugh_new' % directory
+    old_file = self.filesystem.CreateFile(old_file_path)
+    old_file.SetMTime(old_file.st_mtime - 3600)
+    self.os.chown(old_file_path, 200, 200)
+    self.os.chmod(old_file_path, 0o222)
+    new_file = self.filesystem.CreateFile(new_file_path)
+    self.assertNotEqual(new_file.st_mtime, old_file.st_mtime)
+    self.os.rename(old_file_path, new_file_path)
+    new_file = self.filesystem.GetObject(new_file_path)
+    self.assertEqual(new_file.st_mtime, old_file.st_mtime)
+    self.assertEqual(new_file.st_mode, old_file.st_mode)
+    self.assertEqual(new_file.st_uid, old_file.st_uid)
+    self.assertEqual(new_file.st_gid, old_file.st_gid)
+
+  def testRenameSameFilenames(self):
+    """Test renaming when old and new names are the same."""
+    directory = 'xyzzy'
+    file_contents = 'Spam eggs'
+    file_path = '%s/eggs' % directory
+    self.filesystem.CreateFile(file_path, contents=file_contents)
+    self.os.rename(file_path, file_path)
+    self.assertEqual(file_contents,
+                     self.filesystem.GetObject(file_path).contents)
+
+  def testRmdir(self):
+    """Can remove a directory."""
+    directory = 'xyzzy'
+    sub_dir = '/xyzzy/abccd'
+    other_dir = '/xyzzy/cdeed'
+    self.filesystem.CreateDirectory(directory)
+    self.assertTrue(self.filesystem.Exists(directory))
+    self.os.rmdir(directory)
+    self.assertFalse(self.filesystem.Exists(directory))
+    self.filesystem.CreateDirectory(sub_dir)
+    self.filesystem.CreateDirectory(other_dir)
+    self.os.chdir(sub_dir)
+    self.os.rmdir('../cdeed')
+    self.assertFalse(self.filesystem.Exists(other_dir))
+    self.os.chdir('..')
+    self.os.rmdir('abccd')
+    self.assertFalse(self.filesystem.Exists(sub_dir))
+
+  def testRmdirRaisesIfNotEmpty(self):
+    """Raises an exception if the target directory is not empty."""
+    directory = 'xyzzy'
+    file_path = '%s/plugh' % directory
+    self.filesystem.CreateFile(file_path)
+    self.assertTrue(self.filesystem.Exists(file_path))
+    self.assertRaises(OSError, self.os.rmdir, directory)
+
+  def testRmdirRaisesIfNotDirectory(self):
+    """Raises an exception if the target is not a directory."""
+    directory = 'xyzzy'
+    file_path = '%s/plugh' % directory
+    self.filesystem.CreateFile(file_path)
+    self.assertTrue(self.filesystem.Exists(file_path))
+    self.assertRaises(OSError, self.os.rmdir, file_path)
+    self.assertRaises(OSError, self.os.rmdir, '.')
+
+  def testRmdirRaisesIfNotExist(self):
+    """Raises an exception if the target does not exist."""
+    directory = 'xyzzy'
+    self.assertFalse(self.filesystem.Exists(directory))
+    self.assertRaises(OSError, self.os.rmdir, directory)
+
+  def RemovedirsCheck(self, directory):
+    self.assertTrue(self.filesystem.Exists(directory))
+    self.os.removedirs(directory)
+    return not self.filesystem.Exists(directory)
+
+  def testRemovedirs(self):
+    data = ['test1', 'test1/test2', 'test1/extra', 'test1/test2/test3']
+    for directory in data:
+      self.filesystem.CreateDirectory(directory)
+      self.assertTrue(self.filesystem.Exists(directory))
+    self.assertRaises(OSError, self.RemovedirsCheck, data[0])
+    self.assertRaises(OSError, self.RemovedirsCheck, data[1])
+
+    self.assertTrue(self.RemovedirsCheck(data[3]))
+    self.assertTrue(self.filesystem.Exists(data[0]))
+    self.assertFalse(self.filesystem.Exists(data[1]))
+    self.assertTrue(self.filesystem.Exists(data[2]))
+
+    # Should raise because '/test1/extra' is all that is left, and
+    # removedirs('/test1/extra') will eventually try to rmdir('/').
+    self.assertRaises(OSError, self.RemovedirsCheck, data[2])
+
+    # However, it will still delete '/test1') in the process.
+    self.assertFalse(self.filesystem.Exists(data[0]))
+
+    self.filesystem.CreateDirectory('test1/test2')
+    # Add this to the root directory to avoid raising an exception.
+    self.filesystem.CreateDirectory('test3')
+    self.assertTrue(self.RemovedirsCheck('test1/test2'))
+    self.assertFalse(self.filesystem.Exists('test1/test2'))
+    self.assertFalse(self.filesystem.Exists('test1'))
+
+  def testRemovedirsRaisesIfRemovingRoot(self):
+    """Raises exception if asked to remove '/'."""
+    directory = '/'
+    self.assertTrue(self.filesystem.Exists(directory))
+    self.assertRaises(OSError, self.os.removedirs, directory)
+
+  def testRemovedirsRaisesIfCascadeRemovingRoot(self):
+    """Raises exception if asked to remove '/' as part of a larger operation.
+
+    All of other directories should still be removed, though.
+    """
+    directory = '/foo/bar/'
+    self.filesystem.CreateDirectory(directory)
+    self.assertTrue(self.filesystem.Exists(directory))
+    self.assertRaises(OSError, self.os.removedirs, directory)
+    head, unused_tail = self.os.path.split(directory)
+    while head != '/':
+      self.assertFalse(self.filesystem.Exists(directory))
+      head, unused_tail = self.os.path.split(head)
+
+  def testRemovedirsWithTrailingSlash(self):
+    """removedirs works on directory names with trailing slashes."""
+    # separate this case from the removing-root-directory case
+    self.filesystem.CreateDirectory('/baz')
+    directory = '/foo/bar/'
+    self.filesystem.CreateDirectory(directory)
+    self.assertTrue(self.filesystem.Exists(directory))
+    self.os.removedirs(directory)
+    self.assertFalse(self.filesystem.Exists(directory))
+
+  def testMkdir(self):
+    """mkdir can create a relative directory."""
+    directory = 'xyzzy'
+    self.assertFalse(self.filesystem.Exists(directory))
+    self.os.mkdir(directory)
+    self.assertTrue(self.filesystem.Exists('/%s' % directory))
+    self.os.chdir(directory)
+    self.os.mkdir(directory)
+    self.assertTrue(self.filesystem.Exists('/%s/%s' % (directory, directory)))
+    self.os.chdir(directory)
+    self.os.mkdir('../abccb')
+    self.assertTrue(self.filesystem.Exists('/%s/abccb' % directory))
+
+  def testMkdirWithTrailingSlash(self):
+    """mkdir can create a directory named with a trailing slash."""
+    directory = '/foo/'
+    self.assertFalse(self.filesystem.Exists(directory))
+    self.os.mkdir(directory)
+    self.assertTrue(self.filesystem.Exists(directory))
+    self.assertTrue(self.filesystem.Exists('/foo'))
+
+  def testMkdirRaisesIfEmptyDirectoryName(self):
+    """mkdir raises exeption if creating directory named ''."""
+    directory = ''
+    self.assertRaises(OSError, self.os.mkdir, directory)
+
+  def testMkdirRaisesIfNoParent(self):
+    """mkdir raises exception if parent directory does not exist."""
+    parent = 'xyzzy'
+    directory = '%s/foo' % (parent,)
+    self.assertFalse(self.filesystem.Exists(parent))
+    self.assertRaises(Exception, self.os.mkdir, directory)
+
+  def testMkdirRaisesIfDirectoryExists(self):
+    """mkdir raises exception if directory already exists."""
+    directory = 'xyzzy'
+    self.filesystem.CreateDirectory(directory)
+    self.assertTrue(self.filesystem.Exists(directory))
+    self.assertRaises(Exception, self.os.mkdir, directory)
+
+  def testMkdirRaisesIfFileExists(self):
+    """mkdir raises exception if name already exists as a file."""
+    directory = 'xyzzy'
+    file_path = '%s/plugh' % directory
+    self.filesystem.CreateFile(file_path)
+    self.assertTrue(self.filesystem.Exists(file_path))
+    self.assertRaises(Exception, self.os.mkdir, file_path)
+
+  def testMkdirRaisesWithSlashDot(self):
+    """mkdir raises exception if mkdir foo/. (trailing /.)."""
+    self.assertRaises(Exception, self.os.mkdir, '/.')
+    directory = '/xyzzy/.'
+    self.assertRaises(Exception, self.os.mkdir, directory)
+    self.filesystem.CreateDirectory('/xyzzy')
+    self.assertRaises(Exception, self.os.mkdir, directory)
+
+  def testMkdirRaisesWithDoubleDots(self):
+    """mkdir raises exception if mkdir foo/foo2/../foo3."""
+    self.assertRaises(Exception, self.os.mkdir, '/..')
+    directory = '/xyzzy/dir1/dir2/../../dir3'
+    self.assertRaises(Exception, self.os.mkdir, directory)
+    self.filesystem.CreateDirectory('/xyzzy')
+    self.assertRaises(Exception, self.os.mkdir, directory)
+    self.filesystem.CreateDirectory('/xyzzy/dir1')
+    self.assertRaises(Exception, self.os.mkdir, directory)
+    self.filesystem.CreateDirectory('/xyzzy/dir1/dir2')
+    self.os.mkdir(directory)
+    self.assertTrue(self.filesystem.Exists(directory))
+    directory = '/xyzzy/dir1/..'
+    self.assertRaises(Exception, self.os.mkdir, directory)
+
+  def testMkdirRaisesIfParentIsReadOnly(self):
+    """mkdir raises exception if parent is read only."""
+    directory = '/a'
+    self.os.mkdir(directory)
+
+    # Change directory permissions to be read only.
+    self.os.chmod(directory, 0o400)
+
+    directory = '/a/b'
+    self.assertRaises(Exception, self.os.mkdir, directory)
+
+  def testMakedirs(self):
+    """makedirs can create a directory even in parent does not exist."""
+    parent = 'xyzzy'
+    directory = '%s/foo' % (parent,)
+    self.assertFalse(self.filesystem.Exists(parent))
+    self.os.makedirs(directory)
+    self.assertTrue(self.filesystem.Exists(parent))
+
+  def testMakedirsRaisesIfParentIsFile(self):
+    """makedirs raises exception if a parent component exists as a file."""
+    file_path = 'xyzzy'
+    directory = '%s/plugh' % file_path
+    self.filesystem.CreateFile(file_path)
+    self.assertTrue(self.filesystem.Exists(file_path))
+    self.assertRaises(Exception, self.os.makedirs, directory)
+
+  def testMakedirsRaisesIfAccessDenied(self):
+    """makedirs raises exception if access denied."""
+    directory = '/a'
+    self.os.mkdir(directory)
+
+    # Change directory permissions to be read only.
+    self.os.chmod(directory, 0o400)
+
+    directory = '/a/b'
+    self.assertRaises(Exception, self.os.makedirs, directory)
+
+  def _CreateTestFile(self, path):
+    self.filesystem.CreateFile(path)
+    self.assertTrue(self.filesystem.Exists(path))
+    st = self.os.stat(path)
+    self.assertEqual(0o666, stat.S_IMODE(st.st_mode))
+    self.assertTrue(st.st_mode & stat.S_IFREG)
+    self.assertFalse(st.st_mode & stat.S_IFDIR)
+
+  def _CreateTestDirectory(self, path):
+    self.filesystem.CreateDirectory(path)
+    self.assertTrue(self.filesystem.Exists(path))
+    st = self.os.stat(path)
+    self.assertEqual(0o777, stat.S_IMODE(st.st_mode))
+    self.assertFalse(st.st_mode & stat.S_IFREG)
+    self.assertTrue(st.st_mode & stat.S_IFDIR)
+
+  def testAccess700(self):
+    # set up
+    path = '/some_file'
+    self._CreateTestFile(path)
+    self.os.chmod(path, 0o700)
+    self.assertModeEqual(0o700, self.os.stat(path).st_mode)
+    # actual tests
+    self.assertTrue(self.os.access(path, self.os.F_OK))
+    self.assertTrue(self.os.access(path, self.os.R_OK))
+    self.assertTrue(self.os.access(path, self.os.W_OK))
+    self.assertTrue(self.os.access(path, self.os.X_OK))
+    self.assertTrue(self.os.access(path, self.rwx))
+
+  def testAccess600(self):
+    # set up
+    path = '/some_file'
+    self._CreateTestFile(path)
+    self.os.chmod(path, 0o600)
+    self.assertModeEqual(0o600, self.os.stat(path).st_mode)
+    # actual tests
+    self.assertTrue(self.os.access(path, self.os.F_OK))
+    self.assertTrue(self.os.access(path, self.os.R_OK))
+    self.assertTrue(self.os.access(path, self.os.W_OK))
+    self.assertFalse(self.os.access(path, self.os.X_OK))
+    self.assertFalse(self.os.access(path, self.rwx))
+    self.assertTrue(self.os.access(path, self.rw))
+
+  def testAccess400(self):
+    # set up
+    path = '/some_file'
+    self._CreateTestFile(path)
+    self.os.chmod(path, 0o400)
+    self.assertModeEqual(0o400, self.os.stat(path).st_mode)
+    # actual tests
+    self.assertTrue(self.os.access(path, self.os.F_OK))
+    self.assertTrue(self.os.access(path, self.os.R_OK))
+    self.assertFalse(self.os.access(path, self.os.W_OK))
+    self.assertFalse(self.os.access(path, self.os.X_OK))
+    self.assertFalse(self.os.access(path, self.rwx))
+    self.assertFalse(self.os.access(path, self.rw))
+
+  def testAccessNonExistentFile(self):
+    # set up
+    path = '/non/existent/file'
+    self.assertFalse(self.filesystem.Exists(path))
+    # actual tests
+    self.assertFalse(self.os.access(path, self.os.F_OK))
+    self.assertFalse(self.os.access(path, self.os.R_OK))
+    self.assertFalse(self.os.access(path, self.os.W_OK))
+    self.assertFalse(self.os.access(path, self.os.X_OK))
+    self.assertFalse(self.os.access(path, self.rwx))
+    self.assertFalse(self.os.access(path, self.rw))
+
+  def testChmod(self):
+    # set up
+    path = '/some_file'
+    self._CreateTestFile(path)
+    # actual tests
+    self.os.chmod(path, 0o6543)
+    st = self.os.stat(path)
+    self.assertModeEqual(0o6543, st.st_mode)
+    self.assertTrue(st.st_mode & stat.S_IFREG)
+    self.assertFalse(st.st_mode & stat.S_IFDIR)
+
+  def testChmodDir(self):
+    # set up
+    path = '/some_dir'
+    self._CreateTestDirectory(path)
+    # actual tests
+    self.os.chmod(path, 0o1234)
+    st = self.os.stat(path)
+    self.assertModeEqual(0o1234, st.st_mode)
+    self.assertFalse(st.st_mode & stat.S_IFREG)
+    self.assertTrue(st.st_mode & stat.S_IFDIR)
+
+  def testChmodNonExistent(self):
+    # set up
+    path = '/non/existent/file'
+    self.assertFalse(self.filesystem.Exists(path))
+    # actual tests
+    try:
+      # Use try-catch to check exception attributes.
+      self.os.chmod(path, 0o777)
+      self.fail('Exception is expected.')  # COV_NF_LINE
+    except OSError as os_error:
+      self.assertEqual(errno.ENOENT, os_error.errno)
+      self.assertEqual(path, os_error.filename)
+
+  def testChmodStCtime(self):
+    # set up
+    file_path = 'some_file'
+    self.filesystem.CreateFile(file_path)
+    self.assertTrue(self.filesystem.Exists(file_path))
+    st = self.os.stat(file_path)
+    self.assertEqual(200, st.st_ctime)
+    # tests
+    self.os.chmod(file_path, 0o765)
+    st = self.os.stat(file_path)
+    self.assertEqual(220, st.st_ctime)
+
+  def testUtimeSetsCurrentTimeIfArgsIsNone(self):
+    # set up
+    path = '/some_file'
+    self._CreateTestFile(path)
+    st = self.os.stat(path)
+    # 200 is the current time established in setUp().
+    self.assertEqual(200, st.st_atime)
+    self.assertEqual(200, st.st_mtime)
+    # actual tests
+    self.os.utime(path, None)
+    st = self.os.stat(path)
+    self.assertEqual(220, st.st_atime)
+    self.assertEqual(240, st.st_mtime)
+
+  def testUtimeSetsCurrentTimeIfArgsIsNoneWithFloats(self):
+    # set up
+    # time.time can report back floats, but it should be converted to ints
+    # since atime/ctime/mtime are all defined as seconds since epoch.
+    time.time = _GetDummyTime(200.0123, 20)
+    path = '/some_file'
+    self._CreateTestFile(path)
+    st = self.os.stat(path)
+    # 200 is the current time established above (if converted to int).
+    self.assertEqual(200, st.st_atime)
+    self.assertEqual(200, st.st_mtime)
+    # actual tests
+    self.os.utime(path, None)
+    st = self.os.stat(path)
+    self.assertEqual(220, st.st_atime)
+    self.assertEqual(240, st.st_mtime)
+
+  def testUtimeSetsSpecifiedTime(self):
+    # set up
+    path = '/some_file'
+    self._CreateTestFile(path)
+    st = self.os.stat(path)
+    # actual tests
+    self.os.utime(path, (1, 2))
+    st = self.os.stat(path)
+    self.assertEqual(1, st.st_atime)
+    self.assertEqual(2, st.st_mtime)
+
+  def testUtimeDir(self):
+    # set up
+    path = '/some_dir'
+    self._CreateTestDirectory(path)
+    # actual tests
+    self.os.utime(path, (1.0, 2.0))
+    st = self.os.stat(path)
+    self.assertEqual(1.0, st.st_atime)
+    self.assertEqual(2.0, st.st_mtime)
+
+  def testUtimeNonExistent(self):
+    # set up
+    path = '/non/existent/file'
+    self.assertFalse(self.filesystem.Exists(path))
+    # actual tests
+    try:
+      # Use try-catch to check exception attributes.
+      self.os.utime(path, (1, 2))
+      self.fail('Exception is expected.')  # COV_NF_LINE
+    except OSError as os_error:
+      self.assertEqual(errno.ENOENT, os_error.errno)
+      self.assertEqual(path, os_error.filename)
+
+  def testUtimeTupleArgIsOfIncorrectLength(self):
+    # set up
+    path = '/some_dir'
+    self._CreateTestDirectory(path)
+    # actual tests
+    self.assertRaisesWithRegexpMatch(
+        TypeError, r'utime\(\) arg 2 must be a tuple \(atime, mtime\)',
+        self.os.utime, path, (1, 2, 3))
+
+  def testUtimeTupleArgContainsIncorrectType(self):
+    # set up
+    path = '/some_dir'
+    self._CreateTestDirectory(path)
+    # actual tests
+    self.assertRaisesWithRegexpMatch(
+        TypeError, 'an integer is required',
+        self.os.utime, path, (1, 'str'))
+
+  def testChownExistingFile(self):
+    # set up
+    file_path = 'some_file'
+    self.filesystem.CreateFile(file_path)
+    # first set it make sure it's set
+    self.os.chown(file_path, 100, 100)
+    st = self.os.stat(file_path)
+    self.assertEqual(st[stat.ST_UID], 100)
+    self.assertEqual(st[stat.ST_GID], 100)
+    # we can make sure it changed
+    self.os.chown(file_path, 200, 200)
+    st = self.os.stat(file_path)
+    self.assertEqual(st[stat.ST_UID], 200)
+    self.assertEqual(st[stat.ST_GID], 200)
+    # setting a value to -1 leaves it unchanged
+    self.os.chown(file_path, -1, -1)
+    st = self.os.stat(file_path)
+    self.assertEqual(st[stat.ST_UID], 200)
+    self.assertEqual(st[stat.ST_GID], 200)
+
+  def testChownNonexistingFileShouldRaiseOsError(self):
+    file_path = 'some_file'
+    self.assertFalse(self.filesystem.Exists(file_path))
+    self.assertRaises(OSError, self.os.chown, file_path, 100, 100)
+
+  def testClassifyDirectoryContents(self):
+    """Directory classification should work correctly."""
+    root_directory = '/foo'
+    test_directories = ['bar1', 'baz2']
+    test_files = ['baz1', 'bar2', 'baz3']
+    self.filesystem.CreateDirectory(root_directory)
+    for directory in test_directories:
+      directory = self.os.path.join(root_directory, directory)
+      self.filesystem.CreateDirectory(directory)
+    for test_file in test_files:
+      test_file = self.os.path.join(root_directory, test_file)
+      self.filesystem.CreateFile(test_file)
+
+    test_directories.sort()
+    test_files.sort()
+    generator = self.os.walk(root_directory)
+    root, dirs, files = next(generator)
+    dirs.sort()
+    files.sort()
+    self.assertEqual(root_directory, root)
+    self.assertEqual(test_directories, dirs)
+    self.assertEqual(test_files, files)
+
+  def testClassifyDoesNotHideExceptions(self):
+    """_ClassifyDirectoryContents should not hide exceptions."""
+    directory = '/foo'
+    self.assertEqual(False, self.filesystem.Exists(directory))
+    self.assertRaises(OSError, self.os._ClassifyDirectoryContents, directory)
+
+  def testWalkTopDown(self):
+    """Walk down ordering is correct."""
+    self.filesystem.CreateFile('foo/1.txt')
+    self.filesystem.CreateFile('foo/bar1/2.txt')
+    self.filesystem.CreateFile('foo/bar1/baz/3.txt')
+    self.filesystem.CreateFile('foo/bar2/4.txt')
+    expected = [
+        ('foo', ['bar1', 'bar2'], ['1.txt']),
+        ('foo/bar1', ['baz'], ['2.txt']),
+        ('foo/bar1/baz', [], ['3.txt']),
+        ('foo/bar2', [], ['4.txt']),
+        ]
+    self.assertEqual(expected, [step for step in self.os.walk('foo')])
+
+  def testWalkBottomUp(self):
+    """Walk up ordering is correct."""
+    self.filesystem.CreateFile('foo/bar1/baz/1.txt')
+    self.filesystem.CreateFile('foo/bar1/2.txt')
+    self.filesystem.CreateFile('foo/bar2/3.txt')
+    self.filesystem.CreateFile('foo/4.txt')
+
+    expected = [
+        ('foo/bar1/baz', [], ['1.txt']),
+        ('foo/bar1', ['baz'], ['2.txt']),
+        ('foo/bar2', [], ['3.txt']),
+        ('foo', ['bar1', 'bar2'], ['4.txt']),
+        ]
+    self.assertEqual(expected,
+                     [step for step in self.os.walk('foo', topdown=False)])
+
+  def testWalkRaisesIfNonExistent(self):
+    """Raises an exception when attempting to walk non-existent directory."""
+    directory = '/foo/bar'
+    self.assertEqual(False, self.filesystem.Exists(directory))
+    generator = self.os.walk(directory)
+    self.assertRaises(StopIteration, next, generator)
+
+  def testWalkRaisesIfNotDirectory(self):
+    """Raises an exception when attempting to walk a non-directory."""
+    filename = '/foo/bar'
+    self.filesystem.CreateFile(filename)
+    generator = self.os.walk(filename)
+    self.assertRaises(StopIteration, next, generator)
+
+  def testMkNodeCanCreateAFile(self):
+    filename = 'foo'
+    self.assertFalse(self.filesystem.Exists(filename))
+    self.os.mknod(filename)
+    self.assertTrue(self.filesystem.Exists(filename))
+
+  def testMkNodeRaisesIfEmptyFileName(self):
+    filename = ''
+    self.assertRaises(OSError, self.os.mknod, filename)
+
+  def testMkNodeRaisesIfParentDirDoesntExist(self):
+    parent = 'xyzzy'
+    filename = '%s/foo' % (parent,)
+    self.assertFalse(self.filesystem.Exists(parent))
+    self.assertRaises(OSError, self.os.mknod, filename)
+
+  def testMkNodeRaisesIfFileExists(self):
+    filename = '/tmp/foo'
+    self.filesystem.CreateFile(filename)
+    self.assertTrue(self.filesystem.Exists(filename))
+    self.assertRaises(OSError, self.os.mknod, filename)
+
+  def testMkNodeRaisesIfFilenameIsDot(self):
+    filename = '/tmp/.'
+    self.assertRaises(OSError, self.os.mknod, filename)
+
+  def testMkNodeRaisesIfFilenameIsDoubleDot(self):
+    filename = '/tmp/..'
+    self.assertRaises(OSError, self.os.mknod, filename)
+
+  def testMknodEmptyTailForExistingFileRaises(self):
+    filename = '/tmp/foo'
+    self.filesystem.CreateFile(filename)
+    self.assertTrue(self.filesystem.Exists(filename))
+    self.assertRaises(OSError, self.os.mknod, filename)
+
+  def testMknodEmptyTailForNonexistentFileRaises(self):
+    filename = '/tmp/foo'
+    self.assertRaises(OSError, self.os.mknod, filename)
+
+  def testMknodRaisesIfFilenameIsEmptyString(self):
+    filename = ''
+    self.assertRaises(OSError, self.os.mknod, filename)
+
+  def testMknodeRaisesIfUnsupportedOptions(self):
+    filename = 'abcde'
+    self.assertRaises(OSError, self.os.mknod, filename,
+                      mode=stat.S_IFCHR)
+
+  def testMknodeRaisesIfParentIsNotADirectory(self):
+    filename1 = '/tmp/foo'
+    self.filesystem.CreateFile(filename1)
+    self.assertTrue(self.filesystem.Exists(filename1))
+    filename2 = '/tmp/foo/bar'
+    self.assertRaises(OSError, self.os.mknod, filename2)
+
+  def ResetErrno(self):
+    """Reset the last seen errno."""
+    self.last_errno = False
+
+  def StoreErrno(self, os_error):
+    """Store the last errno we saw."""
+    self.last_errno = os_error.errno
+
+  def GetErrno(self):
+    """Return the last errno we saw."""
+    return self.last_errno
+
+  def testWalkCallsOnErrorIfNonExistent(self):
+    """Calls onerror with correct errno when walking non-existent directory."""
+    self.ResetErrno()
+    directory = '/foo/bar'
+    self.assertEqual(False, self.filesystem.Exists(directory))
+    # Calling os.walk on a non-existent directory should trigger a call to the
+    # onerror method.  We do not actually care what, if anything, is returned.
+    for unused_entry in self.os.walk(directory, onerror=self.StoreErrno):
+      pass
+    self.assertTrue(self.GetErrno() in (errno.ENOTDIR, errno.ENOENT))
+
+  def testWalkCallsOnErrorIfNotDirectory(self):
+    """Calls onerror with correct errno when walking non-directory."""
+    self.ResetErrno()
+    filename = '/foo/bar'
+    self.filesystem.CreateFile(filename)
+    self.assertEqual(True, self.filesystem.Exists(filename))
+    # Calling os.walk on a file should trigger a call to the onerror method.
+    # We do not actually care what, if anything, is returned.
+    for unused_entry in self.os.walk(filename, onerror=self.StoreErrno):
+      pass
+    self.assertTrue(self.GetErrno() in (errno.ENOTDIR, errno.EACCES))
+
+  def testWalkSkipsRemovedDirectories(self):
+    """Caller can modify list of directories to visit while walking."""
+    root = '/foo'
+    visit = 'visit'
+    no_visit = 'no_visit'
+    self.filesystem.CreateFile('%s/bar' % (root,))
+    self.filesystem.CreateFile('%s/%s/1.txt' % (root, visit))
+    self.filesystem.CreateFile('%s/%s/2.txt' % (root, visit))
+    self.filesystem.CreateFile('%s/%s/3.txt' % (root, no_visit))
+    self.filesystem.CreateFile('%s/%s/4.txt' % (root, no_visit))
+
+    generator = self.os.walk('/foo')
+    root_contents = next(generator)
+    root_contents[1].remove(no_visit)
+
+    visited_visit_directory = False
+
+    for root, unused_dirs, unused_files in iter(generator):
+      self.assertEqual(False, root.endswith('/%s' % (no_visit)))
+      if root.endswith('/%s' % (visit)):
+        visited_visit_directory = True
+
+    self.assertEqual(True, visited_visit_directory)
+
+  def testSymlink(self):
+    file_path = 'foo/bar/baz'
+    self.os.symlink('bogus', file_path)
+    self.assertTrue(self.os.path.lexists(file_path))
+    self.assertFalse(self.os.path.exists(file_path))
+    self.filesystem.CreateFile('foo/bar/bogus')
+    self.assertTrue(self.os.path.lexists(file_path))
+    self.assertTrue(self.os.path.exists(file_path))
+
+  def testUMask(self):
+    umask = os.umask(0o22)
+    os.umask(umask)
+    self.assertEqual(umask, self.os.umask(0o22))
+
+  def testMkdirUmaskApplied(self):
+    """mkdir creates a directory with umask applied."""
+    self.os.umask(0o22)
+    self.os.mkdir('dir1')
+    self.assertModeEqual(0o755, self.os.stat('dir1').st_mode)
+    self.os.umask(0o67)
+    self.os.mkdir('dir2')
+    self.assertModeEqual(0o710, self.os.stat('dir2').st_mode)
+
+  def testMakedirsUmaskApplied(self):
+    """makedirs creates a directories with umask applied."""
+    self.os.umask(0o22)
+    self.os.makedirs('/p1/dir1')
+    self.assertModeEqual(0o755, self.os.stat('/p1').st_mode)
+    self.assertModeEqual(0o755, self.os.stat('/p1/dir1').st_mode)
+    self.os.umask(0o67)
+    self.os.makedirs('/p2/dir2')
+    self.assertModeEqual(0o710, self.os.stat('/p2').st_mode)
+    self.assertModeEqual(0o710, self.os.stat('/p2/dir2').st_mode)
+
+  def testMknodeUmaskApplied(self):
+    """mkdir creates a device with umask applied."""
+    self.os.umask(0o22)
+    self.os.mknod('nod1')
+    self.assertModeEqual(0o644, self.os.stat('nod1').st_mode)
+    self.os.umask(0o27)
+    self.os.mknod('nod2')
+    self.assertModeEqual(0o640, self.os.stat('nod2').st_mode)
+
+  def testOpenUmaskApplied(self):
+    """open creates a file with umask applied."""
+    fake_open = fake_filesystem.FakeFileOpen(self.filesystem)
+    self.os.umask(0o22)
+    fake_open('file1', 'w').close()
+    self.assertModeEqual(0o644, self.os.stat('file1').st_mode)
+    self.os.umask(0o27)
+    fake_open('file2', 'w').close()
+    self.assertModeEqual(0o640, self.os.stat('file2').st_mode)
+
+
+class StatPropagationTest(TestCase):
+
+  def setUp(self):
+    self.filesystem = fake_filesystem.FakeFilesystem(path_separator='/')
+    self.os = fake_filesystem.FakeOsModule(self.filesystem)
+    self.open = fake_filesystem.FakeFileOpen(self.filesystem)
+
+  def testFileSizeUpdatedViaClose(self):
+    """test that file size gets updated via close()."""
+    file_dir = 'xyzzy'
+    file_path = 'xyzzy/close'
+    content = 'This is a test.'
+    self.os.mkdir(file_dir)
+    fh = self.open(file_path, 'w')
+    self.assertEqual(0, self.os.stat(file_path)[stat.ST_SIZE])
+    self.assertEqual('', self.filesystem.GetObject(file_path).contents)
+    fh.write(content)
+    self.assertEqual(0, self.os.stat(file_path)[stat.ST_SIZE])
+    self.assertEqual('', self.filesystem.GetObject(file_path).contents)
+    fh.close()
+    self.assertEqual(len(content), self.os.stat(file_path)[stat.ST_SIZE])
+    self.assertEqual(content, self.filesystem.GetObject(file_path).contents)
+
+  def testFileSizeNotResetAfterClose(self):
+    file_dir = 'xyzzy'
+    file_path = 'xyzzy/close'
+    self.os.mkdir(file_dir)
+    size = 1234
+    # The file has size, but no content. When the file is opened for reading,
+    # its size should be preserved.
+    self.filesystem.CreateFile(file_path, st_size=size)
+    fh = self.open(file_path, 'r')
+    fh.close()
+    self.assertEqual(size, self.open(file_path, 'r').Size())
+
+  def testFileSizeAfterWrite(self):
+    file_path = 'test_file'
+    original_content = 'abcdef'
+    original_size = len(original_content)
+    self.filesystem.CreateFile(file_path, contents=original_content)
+    added_content = 'foo bar'
+    expected_size = original_size + len(added_content)
+    fh = self.open(file_path, 'a')
+    fh.write(added_content)
+    self.assertEqual(expected_size, fh.Size())
+    fh.close()
+    self.assertEqual(expected_size, self.open(file_path, 'r').Size())
+
+  def testLargeFileSizeAfterWrite(self):
+    file_path = 'test_file'
+    original_content = 'abcdef'
+    original_size = len(original_content)
+    self.filesystem.CreateFile(file_path, st_size=original_size)
+    added_content = 'foo bar'
+    fh = self.open(file_path, 'a')
+    # We can't use assertRaises, because the exception is thrown
+    # in __getattr__, so just saying 'fh.write' causes the exception.
+    try:
+      fh.write(added_content)
+    except fake_filesystem.FakeLargeFileIoException:
+      return
+    self.fail('Writing to a large file should not be allowed')
+
+  def testFileSizeUpdatedViaFlush(self):
+    """test that file size gets updated via flush()."""
+    file_dir = 'xyzzy'
+    file_name = 'flush'
+    file_path = self.os.path.join(file_dir, file_name)
+    content = 'This might be a test.'
+    self.os.mkdir(file_dir)
+    fh = self.open(file_path, 'w')
+    self.assertEqual(0, self.os.stat(file_path)[stat.ST_SIZE])
+    self.assertEqual('', self.filesystem.GetObject(file_path).contents)
+    fh.write(content)
+    self.assertEqual(0, self.os.stat(file_path)[stat.ST_SIZE])
+    self.assertEqual('', self.filesystem.GetObject(file_path).contents)
+    fh.flush()
+    self.assertEqual(len(content), self.os.stat(file_path)[stat.ST_SIZE])
+    self.assertEqual(content, self.filesystem.GetObject(file_path).contents)
+    fh.close()
+    self.assertEqual(len(content), self.os.stat(file_path)[stat.ST_SIZE])
+    self.assertEqual(content, self.filesystem.GetObject(file_path).contents)
+
+  def testFileSizeTruncation(self):
+    """test that file size gets updated via open()."""
+    file_dir = 'xyzzy'
+    file_path = 'xyzzy/truncation'
+    content = 'AAA content.'
+
+    # pre-create file with content
+    self.os.mkdir(file_dir)
+    fh = self.open(file_path, 'w')
+    fh.write(content)
+    fh.close()
+    self.assertEqual(len(content), self.os.stat(file_path)[stat.ST_SIZE])
+    self.assertEqual(content, self.filesystem.GetObject(file_path).contents)
+
+    # test file truncation
+    fh = self.open(file_path, 'w')
+    self.assertEqual(0, self.os.stat(file_path)[stat.ST_SIZE])
+    self.assertEqual('', self.filesystem.GetObject(file_path).contents)
+    fh.close()
+
+
+class OsPathInjectionRegressionTest(TestCase):
+  """Test faking os.path before calling os.walk.
+
+  Found when investigating a problem with
+  gws/tools/labrat/rat_utils_unittest, which was faking out os.path
+  before calling os.walk.
+  """
+
+  def setUp(self):
+    self.filesystem = fake_filesystem.FakeFilesystem(path_separator='/')
+    self.os_path = os.path
+    # The bug was that when os.path gets faked, the FakePathModule doesn't get
+    # called in self.os.walk().  FakePathModule now insists that it is created
+    # as part of FakeOsModule.
+    self.os = fake_filesystem.FakeOsModule(self.filesystem)
+
+  def tearDown(self):
+    os.path = self.os_path
+
+  def testCreateTopLevelDirectory(self):
+    top_level_dir = '/x'
+    self.assertFalse(self.filesystem.Exists(top_level_dir))
+    self.filesystem.CreateDirectory(top_level_dir)
+    self.assertTrue(self.filesystem.Exists('/'))
+    self.assertTrue(self.filesystem.Exists(top_level_dir))
+    self.filesystem.CreateDirectory('%s/po' % top_level_dir)
+    self.filesystem.CreateFile('%s/po/control' % top_level_dir)
+    self.filesystem.CreateFile('%s/po/experiment' % top_level_dir)
+    self.filesystem.CreateDirectory('%s/gv' % top_level_dir)
+    self.filesystem.CreateFile('%s/gv/control' % top_level_dir)
+
+    expected = [
+        ('/', ['x'], []),
+        ('/x', ['gv', 'po'], []),
+        ('/x/gv', [], ['control']),
+        ('/x/po', [], ['control', 'experiment']),
+        ]
+    self.assertEqual(expected, [step for step in self.os.walk('/')])
+
+
+class FakePathModuleTest(TestCase):
+  def setUp(self):
+    self.orig_time = time.time
+    time.time = _GetDummyTime(10, 1)
+    self.filesystem = fake_filesystem.FakeFilesystem(path_separator='/')
+    self.os = fake_filesystem.FakeOsModule(self.filesystem)
+    self.path = self.os.path
+
+  def tearDown(self):
+    time.time = self.orig_time
+
+  def testAbspath(self):
+    """abspath should return a consistent representation of a file."""
+    filename = 'foo'
+    abspath = '/%s' % filename
+    self.filesystem.CreateFile(abspath)
+    self.assertEqual(abspath, self.path.abspath(abspath))
+    self.assertEqual(abspath, self.path.abspath(filename))
+    self.assertEqual(abspath, self.path.abspath('../%s' % filename))
+
+  def testAbspathDealsWithRelativeNonRootPath(self):
+    """abspath should correctly handle relative paths from a non-/ directory.
+
+    This test is distinct from the basic functionality test because
+    fake_filesystem has historically been based in /.
+    """
+    filename = '/foo/bar/baz'
+    file_components = filename.split(self.path.sep)
+    basedir = '/%s' % (file_components[0],)
+    self.filesystem.CreateFile(filename)
+    self.os.chdir(basedir)
+    self.assertEqual(basedir, self.path.abspath(self.path.curdir))
+    self.assertEqual('/', self.path.abspath('..'))
+    self.assertEqual(self.path.join(basedir, file_components[1]),
+                     self.path.abspath(file_components[1]))
+
+  def testRelpath(self):
+    path_foo = '/path/to/foo'
+    path_bar = '/path/to/bar'
+    path_other = '/some/where/else'
+    self.assertRaises(ValueError, self.path.relpath, None)
+    self.assertRaises(ValueError, self.path.relpath, '')
+    self.assertEqual(path_foo[1:],
+                     self.path.relpath(path_foo))
+    self.assertEqual('../foo',
+                     self.path.relpath(path_foo, path_bar))
+    self.assertEqual('../../..%s' % path_other,
+                     self.path.relpath(path_other, path_bar))
+    self.assertEqual('.',
+                     self.path.relpath(path_bar, path_bar))
+
+  @unittest.skipIf(TestCase.is_windows, 'realpath does not follow symlinks in win32')
+  def testRealpathVsAbspath(self):
+    self.filesystem.CreateFile('/george/washington/bridge')
+    self.filesystem.CreateLink('/first/president', '/george/washington')
+    self.assertEqual('/first/president/bridge',
+                     self.os.path.abspath('/first/president/bridge'))
+    self.assertEqual('/george/washington/bridge',
+                     self.os.path.realpath('/first/president/bridge'))
+    self.os.chdir('/first/president')
+    self.assertEqual('/george/washington/bridge',
+                     self.os.path.realpath('bridge'))
+
+  def testExists(self):
+    file_path = 'foo/bar/baz'
+    self.filesystem.CreateFile(file_path)
+    self.assertTrue(self.path.exists(file_path))
+    self.assertFalse(self.path.exists('/some/other/bogus/path'))
+
+  def testLexists(self):
+    file_path = 'foo/bar/baz'
+    self.filesystem.CreateDirectory('foo/bar')
+    self.filesystem.CreateLink(file_path, 'bogus')
+    self.assertTrue(self.path.lexists(file_path))
+    self.assertFalse(self.path.exists(file_path))
+    self.filesystem.CreateFile('foo/bar/bogus')
+    self.assertTrue(self.path.exists(file_path))
+
+  def testDirname(self):
+    dirname = 'foo/bar'
+    self.assertEqual(dirname, self.path.dirname('%s/baz' % dirname))
+
+  def testJoin(self):
+    components = ['foo', 'bar', 'baz']
+    self.assertEqual('foo/bar/baz', self.path.join(*components))
+
+  def testExpandUser(self):
+    if self.is_windows:
+      self.assertEqual(self.path.expanduser('~'),
+                       self.os.environ['USERPROFILE'].replace('\\', '/'))
+    else:
+      self.assertEqual(self.path.expanduser('~'),
+                       self.os.environ['HOME'])
+
+  @unittest.skipIf(TestCase.is_windows or TestCase.is_cygwin,
+                   'only tested on unix systems')
+  def testExpandRoot(self):
+      self.assertEqual('/root', self.path.expanduser('~root'))
+
+  def testGetsizePathNonexistent(self):
+    file_path = 'foo/bar/baz'
+    self.assertRaises(IOError, self.path.getsize, file_path)
+
+  def testGetsizeFileEmpty(self):
+    file_path = 'foo/bar/baz'
+    self.filesystem.CreateFile(file_path)
+    self.assertEqual(0, self.path.getsize(file_path))
+
+  def testGetsizeFileNonZeroSize(self):
+    file_path = 'foo/bar/baz'
+    self.filesystem.CreateFile(file_path, contents='1234567')
+    self.assertEqual(7, self.path.getsize(file_path))
+
+  def testGetsizeDirEmpty(self):
+    # For directories, only require that the size is non-negative.
+    dir_path = 'foo/bar'
+    self.filesystem.CreateDirectory(dir_path)
+    size = self.path.getsize(dir_path)
+    self.assertFalse(int(size) < 0,
+                     'expected non-negative size; actual: %s' % size)
+
+  def testGetsizeDirNonZeroSize(self):
+    # For directories, only require that the size is non-negative.
+    dir_path = 'foo/bar'
+    self.filesystem.CreateFile(self.filesystem.JoinPaths(dir_path, 'baz'))
+    size = self.path.getsize(dir_path)
+    self.assertFalse(int(size) < 0,
+                     'expected non-negative size; actual: %s' % size)
+
+  def testIsdir(self):
+    self.filesystem.CreateFile('foo/bar')
+    self.assertTrue(self.path.isdir('foo'))
+    self.assertFalse(self.path.isdir('foo/bar'))
+    self.assertFalse(self.path.isdir('it_dont_exist'))
+
+  def testIsdirWithCwdChange(self):
+    self.filesystem.CreateFile('/foo/bar/baz')
+    self.assertTrue(self.path.isdir('/foo'))
+    self.assertTrue(self.path.isdir('/foo/bar'))
+    self.assertTrue(self.path.isdir('foo'))
+    self.assertTrue(self.path.isdir('foo/bar'))
+    self.filesystem.cwd = '/foo'
+    self.assertTrue(self.path.isdir('/foo'))
+    self.assertTrue(self.path.isdir('/foo/bar'))
+    self.assertTrue(self.path.isdir('bar'))
+
+  def testIsfile(self):
+    self.filesystem.CreateFile('foo/bar')
+    self.assertFalse(self.path.isfile('foo'))
+    self.assertTrue(self.path.isfile('foo/bar'))
+    self.assertFalse(self.path.isfile('it_dont_exist'))
+
+  def testGetMtime(self):
+    test_file = self.filesystem.CreateFile('foo/bar1.txt')
+    # The root directory ('', effectively '/') is created at time 10,
+    # the parent directory ('foo') at time 11, and the file at time 12.
+    self.assertEqual(12, test_file.st_mtime)
+    test_file.SetMTime(24)
+    self.assertEqual(24, self.path.getmtime('foo/bar1.txt'))
+
+  def testGetMtimeRaisesOSError(self):
+    self.assertFalse(self.path.exists('it_dont_exist'))
+    self.assertRaises(OSError, self.path.getmtime, 'it_dont_exist')
+
+  def testIslink(self):
+    self.filesystem.CreateDirectory('foo')
+    self.filesystem.CreateFile('foo/regular_file')
+    self.filesystem.CreateLink('foo/link_to_file', 'regular_file')
+    self.assertFalse(self.path.islink('foo'))
+
+    # An object can be both a link and a file or file, according to the
+    # comments in Python/Lib/posixpath.py.
+    self.assertTrue(self.path.islink('foo/link_to_file'))
+    self.assertTrue(self.path.isfile('foo/link_to_file'))
+
+    self.assertTrue(self.path.isfile('foo/regular_file'))
+    self.assertFalse(self.path.islink('foo/regular_file'))
+
+    self.assertFalse(self.path.islink('it_dont_exist'))
+
+  @unittest.skipIf(sys.version_info >= (3, 0) or TestCase.is_windows,
+                   'os.path.walk deprecrated in Python 3, cannot be properly '
+                   'tested in win32')
+  def testWalk(self):
+    self.filesystem.CreateFile('/foo/bar/baz')
+    self.filesystem.CreateFile('/foo/bar/xyzzy/plugh')
+    visited_nodes = []
+
+    def RecordVisitedNodes(visited, dirname, fnames):
+      visited.extend(((dirname, fname) for fname in fnames))
+
+    self.path.walk('/foo', RecordVisitedNodes, visited_nodes)
+    expected = [('/foo', 'bar'),
+                ('/foo/bar', 'baz'),
+                ('/foo/bar', 'xyzzy'),
+                ('/foo/bar/xyzzy', 'plugh')]
+    self.assertEqual(expected, visited_nodes)
+
+  @unittest.skipIf(sys.version_info >= (3, 0) or TestCase.is_windows,
+                   'os.path.walk deprecrated in Python 3, cannot be properly '
+                   'tested in win32')
+  def testWalkFromNonexistentTopDoesNotThrow(self):
+    visited_nodes = []
+
+    def RecordVisitedNodes(visited, dirname, fnames):
+      visited.extend(((dirname, fname) for fname in fnames))
+
+    self.path.walk('/foo', RecordVisitedNodes, visited_nodes)
+    self.assertEqual([], visited_nodes)
+
+
+class FakeFileOpenTestBase(TestCase):
+  def setUp(self):
+    self.filesystem = fake_filesystem.FakeFilesystem(path_separator='/')
+    self.file = fake_filesystem.FakeFileOpen(self.filesystem)
+    self.open = self.file
+    self.os = fake_filesystem.FakeOsModule(self.filesystem)
+    self.orig_time = time.time
+    time.time = _GetDummyTime(100, 10)
+
+  def tearDown(self):
+    time.time = self.orig_time
+
+
+class FakeFileOpenTest(FakeFileOpenTestBase):
+  def testOpenNoParentDir(self):
+    """Expect raise when open'ing a file in a missing directory."""
+    file_path = 'foo/bar.txt'
+    self.assertRaises(IOError, self.file, file_path, 'w')
+
+  def testDeleteOnClose(self):
+    file_dir = 'boo'
+    file_path = 'boo/far'
+    self.os.mkdir(file_dir)
+    self.file = fake_filesystem.FakeFileOpen(self.filesystem,
+                                             delete_on_close=True)
+    fh = self.file(file_path, 'w')
+    self.assertTrue(self.filesystem.Exists(file_path))
+    fh.close()
+    self.assertFalse(self.filesystem.Exists(file_path))
+
+  def testNoDeleteOnCloseByDefault(self):
+    file_dir = 'boo'
+    file_path = 'boo/czar'
+    self.file = fake_filesystem.FakeFileOpen(self.filesystem)
+    self.os.mkdir(file_dir)
+    fh = self.file(file_path, 'w')
+    self.assertTrue(self.filesystem.Exists(file_path))
+    fh.close()
+    self.assertTrue(self.filesystem.Exists(file_path))
+
+  def testCompatibilityOfWithStatement(self):
+    self.file = fake_filesystem.FakeFileOpen(self.filesystem,
+                                             delete_on_close=True)
+    file_path = 'foo'
+    self.assertFalse(self.filesystem.Exists(file_path))
+    with self.file(file_path, 'w') as _:
+      self.assertTrue(self.filesystem.Exists(file_path))
+    # After the 'with' statement, the close() method should have been called.
+    self.assertFalse(self.filesystem.Exists(file_path))
+
+  def testOpenValidFile(self):
+    contents = [
+        'I am he as\n',
+        'you are he as\n',
+        'you are me and\n',
+        'we are all together\n'
+        ]
+    file_path = 'foo/bar.txt'
+    self.filesystem.CreateFile(file_path, contents=''.join(contents))
+    self.assertEqual(contents, self.file(file_path).readlines())
+
+  def testOpenValidArgs(self):
+    contents = [
+        "Bang bang Maxwell's silver hammer\n",
+        'Came down on her head',
+        ]
+    file_path = 'abbey_road/maxwell'
+    self.filesystem.CreateFile(file_path, contents=''.join(contents))
+    self.assertEqual(
+        contents, self.open(file_path, mode='r', buffering=1).readlines())
+    if sys.version_info >= (3, 0):
+      self.assertEqual(
+          contents, self.open(file_path, mode='r', buffering=1,
+                              encoding='utf-8', errors='strict', newline='\n',
+                              closefd=False, opener=False).readlines())
+
+  @unittest.skipIf(sys.version_info < (3, 0), 'only tested on 3.0 or greater')
+  def testOpenNewlineArg(self):
+    file_path = 'some_file'
+    file_contents = 'two\r\nlines'
+    self.filesystem.CreateFile(file_path, contents=file_contents)
+    fake_file = self.open(file_path, mode='r', newline=None)
+    self.assertEqual(['two\n', 'lines'], fake_file.readlines())
+    fake_file = self.open(file_path, mode='r', newline='')
+    self.assertEqual(['two\r\n', 'lines'], fake_file.readlines())
+    fake_file = self.open(file_path, mode='r', newline='\r')
+    self.assertEqual(['two\r', '\r', 'lines'], fake_file.readlines())
+    fake_file = self.open(file_path, mode='r', newline='\n')
+    self.assertEqual(['two\r\n', 'lines'], fake_file.readlines())
+    fake_file = self.open(file_path, mode='r', newline='\r\n')
+    self.assertEqual(['two\r\r\n', 'lines'], fake_file.readlines())
+
+  def testOpenValidFileWithCwd(self):
+    contents = [
+        'I am he as\n',
+        'you are he as\n',
+        'you are me and\n',
+        'we are all together\n'
+        ]
+    file_path = '/foo/bar.txt'
+    self.filesystem.CreateFile(file_path, contents=''.join(contents))
+    self.filesystem.cwd = '/foo'
+    self.assertEqual(contents, self.file(file_path).readlines())
+
+  def testIterateOverFile(self):
+    contents = [
+        "Bang bang Maxwell's silver hammer",
+        'Came down on her head',
+        ]
+    file_path = 'abbey_road/maxwell'
+    self.filesystem.CreateFile(file_path, contents='\n'.join(contents))
+    result = [line.rstrip() for line in self.file(file_path)]
+    self.assertEqual(contents, result)
+
+  def testOpenDirectoryError(self):
+    directory_path = 'foo/bar'
+    self.filesystem.CreateDirectory(directory_path)
+    self.assertRaises(IOError, self.file.__call__, directory_path)
+
+  def testCreateFileWithWrite(self):
+    contents = [
+        "Here comes the sun, little darlin'",
+        'Here comes the sun, and I say,',
+        "It's alright",
+        ]
+    file_dir = 'abbey_road'
+    file_path = 'abbey_road/here_comes_the_sun'
+    self.os.mkdir(file_dir)
+    fake_file = self.file(file_path, 'w')
+    for line in contents:
+      fake_file.write(line + '\n')
+    fake_file.close()
+    result = [line.rstrip() for line in self.file(file_path)]
+    self.assertEqual(contents, result)
+
+  def testCreateFileWithAppend(self):
+    contents = [
+        "Here comes the sun, little darlin'",
+        'Here comes the sun, and I say,',
+        "It's alright",
+        ]
+    file_dir = 'abbey_road'
+    file_path = 'abbey_road/here_comes_the_sun'
+    self.os.mkdir(file_dir)
+    fake_file = self.file(file_path, 'a')
+    for line in contents:
+      fake_file.write(line + '\n')
+    fake_file.close()
+    result = [line.rstrip() for line in self.file(file_path)]
+    self.assertEqual(contents, result)
+
+  def testOverwriteExistingFile(self):
+    file_path = 'overwrite/this/file'
+    self.filesystem.CreateFile(file_path, contents='To disappear')
+    new_contents = [
+        'Only these lines',
+        'should be in the file.',
+        ]
+    fake_file = self.file(file_path, 'w')
+    for line in new_contents:
+      fake_file.write(line + '\n')
+    fake_file.close()
+    result = [line.rstrip() for line in self.file(file_path)]
+    self.assertEqual(new_contents, result)
+
+  def testAppendExistingFile(self):
+    file_path = 'append/this/file'
+    contents = [
+        'Contents of original file'
+        'Appended contents',
+        ]
+    self.filesystem.CreateFile(file_path, contents=contents[0])
+    fake_file = self.file(file_path, 'a')
+    for line in contents[1:]:
+      fake_file.write(line + '\n')
+    fake_file.close()
+    result = [line.rstrip() for line in self.file(file_path)]
+    self.assertEqual(contents, result)
+
+  def testOpenWithWplus(self):
+    # set up
+    file_path = 'wplus_file'
+    self.filesystem.CreateFile(file_path, contents='old contents')
+    self.assertTrue(self.filesystem.Exists(file_path))
+    fake_file = self.file(file_path, 'r')
+    self.assertEqual('old contents', fake_file.read())
+    fake_file.close()
+    # actual tests
+    fake_file = self.file(file_path, 'w+')
+    fake_file.write('new contents')
+    fake_file.seek(0)
+    self.assertTrue('new contents', fake_file.read())
+    fake_file.close()
+
+  def testOpenWithWplusTruncation(self):
+    # set up
+    file_path = 'wplus_file'
+    self.filesystem.CreateFile(file_path, contents='old contents')
+    self.assertTrue(self.filesystem.Exists(file_path))
+    fake_file = self.file(file_path, 'r')
+    self.assertEqual('old contents', fake_file.read())
+    fake_file.close()
+    # actual tests
+    fake_file = self.file(file_path, 'w+')
+    fake_file.seek(0)
+    self.assertEqual('', fake_file.read())
+    fake_file.close()
+
+  def testOpenWithAppendFlag(self):
+    contents = [
+        'I am he as\n',
+        'you are he as\n',
+        'you are me and\n',
+        'we are all together\n'
+        ]
+    additional_contents = [
+        'These new lines\n',
+        'like you a lot.\n'
+        ]
+    file_path = 'append/this/file'
+    self.filesystem.CreateFile(file_path, contents=''.join(contents))
+    fake_file = self.file(file_path, 'a')
+    self.assertRaises(IOError, fake_file.read)
+    self.assertEqual('', fake_file.read(0))
+    self.assertEqual('', fake_file.readline(0))
+    self.assertEqual(len(''.join(contents)), fake_file.tell())
+    fake_file.seek(0)
+    self.assertEqual(0, fake_file.tell())
+    fake_file.writelines(additional_contents)
+    fake_file.close()
+    result = self.file(file_path).readlines()
+    self.assertEqual(contents + additional_contents, result)
+
+  def testAppendWithAplus(self):
+    # set up
+    file_path = 'aplus_file'
+    self.filesystem.CreateFile(file_path, contents='old contents')
+    self.assertTrue(self.filesystem.Exists(file_path))
+    fake_file = self.file(file_path, 'r')
+    self.assertEqual('old contents', fake_file.read())
+    fake_file.close()
+    # actual tests
+    fake_file = self.file(file_path, 'a+')
+    self.assertEqual(0, fake_file.tell())
+    fake_file.seek(6, 1)
+    fake_file.write('new contents')
+    self.assertEqual(24, fake_file.tell())
+    fake_file.seek(0)
+    self.assertEqual('old contentsnew contents', fake_file.read())
+    fake_file.close()
+
+  def testAppendWithAplusReadWithLoop(self):
+    # set up
+    file_path = 'aplus_file'
+    self.filesystem.CreateFile(file_path, contents='old contents')
+    self.assertTrue(self.filesystem.Exists(file_path))
+    fake_file = self.file(file_path, 'r')
+    self.assertEqual('old contents', fake_file.read())
+    fake_file.close()
+    # actual tests
+    fake_file = self.file(file_path, 'a+')
+    fake_file.seek(0)
+    fake_file.write('new contents')
+    fake_file.seek(0)
+    for line in fake_file:
+      self.assertEqual('old contentsnew contents', line)
+    fake_file.close()
+
+  def testReadEmptyFileWithAplus(self):
+    file_path = 'aplus_file'
+    fake_file = self.file(file_path, 'a+')
+    self.assertEqual('', fake_file.read())
+    fake_file.close()
+
+  def testReadWithRplus(self):
+    # set up
+    file_path = 'rplus_file'
+    self.filesystem.CreateFile(file_path, contents='old contents here')
+    self.assertTrue(self.filesystem.Exists(file_path))
+    fake_file = self.file(file_path, 'r')
+    self.assertEqual('old contents here', fake_file.read())
+    fake_file.close()
+    # actual tests
+    fake_file = self.file(file_path, 'r+')
+    self.assertEqual('old contents here', fake_file.read())
+    fake_file.seek(0)
+    fake_file.write('new contents')
+    fake_file.seek(0)
+    self.assertEqual('new contents here', fake_file.read())
+    fake_file.close()
+
+  def testOpenStCtime(self):
+    # set up
+    file_path = 'some_file'
+    self.assertFalse(self.filesystem.Exists(file_path))
+    # tests
+    fake_file = self.file(file_path, 'w')
+    fake_file.close()
+    st = self.os.stat(file_path)
+    self.assertEqual(100, st.st_ctime)
+
+    fake_file = self.file(file_path, 'w')
+    fake_file.close()
+    st = self.os.stat(file_path)
+    self.assertEqual(110, st.st_ctime)
+
+    fake_file = self.file(file_path, 'w+')
+    fake_file.close()
+    st = self.os.stat(file_path)
+    self.assertEqual(120, st.st_ctime)
+
+    fake_file = self.file(file_path, 'r')
+    fake_file.close()
+    st = self.os.stat(file_path)
+    self.assertEqual(120, st.st_ctime)
+
+  def _CreateWithPermission(self, file_path, perm_bits):
+    self.filesystem.CreateFile(file_path)
+    self.os.chmod(file_path, perm_bits)
+    st = self.os.stat(file_path)
+    self.assertModeEqual(perm_bits, st.st_mode)
+    self.assertTrue(st.st_mode & stat.S_IFREG)
+    self.assertFalse(st.st_mode & stat.S_IFDIR)
+
+  def testOpenFlags700(self):
+    # set up
+    file_path = 'target_file'
+    self._CreateWithPermission(file_path, 0o700)
+    # actual tests
+    self.file(file_path, 'r').close()
+    self.file(file_path, 'w').close()
+    self.file(file_path, 'w+').close()
+    self.assertRaises(IOError, self.file, file_path, 'INV')
+
+  def testOpenFlags400(self):
+    # set up
+    file_path = 'target_file'
+    self._CreateWithPermission(file_path, 0o400)
+    # actual tests
+    self.file(file_path, 'r').close()
+    self.assertRaises(IOError, self.file, file_path, 'w')
+    self.assertRaises(IOError, self.file, file_path, 'w+')
+
+  def testOpenFlags200(self):
+    # set up
+    file_path = 'target_file'
+    self._CreateWithPermission(file_path, 0o200)
+    # actual tests
+    self.assertRaises(IOError, self.file, file_path, 'r')
+    self.file(file_path, 'w').close()
+    self.assertRaises(IOError, self.file, file_path, 'w+')
+
+  def testOpenFlags100(self):
+    # set up
+    file_path = 'target_file'
+    self._CreateWithPermission(file_path, 0o100)
+    # actual tests 4
+    self.assertRaises(IOError, self.file, file_path, 'r')
+    self.assertRaises(IOError, self.file, file_path, 'w')
+    self.assertRaises(IOError, self.file, file_path, 'w+')
+
+  def testFollowLinkRead(self):
+    link_path = '/foo/bar/baz'
+    target = '/tarJAY'
+    target_contents = 'real baz contents'
+    self.filesystem.CreateFile(target, contents=target_contents)
+    self.filesystem.CreateLink(link_path, target)
+    self.assertEqual(target, self.os.readlink(link_path))
+    fh = self.open(link_path, 'r')
+    got_contents = fh.read()
+    fh.close()
+    self.assertEqual(target_contents, got_contents)
+
+  def testFollowLinkWrite(self):
+    link_path = '/foo/bar/TBD'
+    target = '/tarJAY'
+    target_contents = 'real baz contents'
+    self.filesystem.CreateLink(link_path, target)
+    self.assertFalse(self.filesystem.Exists(target))
+
+    fh = self.open(link_path, 'w')
+    fh.write(target_contents)
+    fh.close()
+    fh = self.open(target, 'r')
+    got_contents = fh.read()
+    fh.close()
+    self.assertEqual(target_contents, got_contents)
+
+  def testFollowIntraPathLinkWrite(self):
+    # Test a link in the middle of of a file path.
+    link_path = '/foo/build/local_machine/output/1'
+    target = '/tmp/output/1'
+    self.filesystem.CreateDirectory('/tmp/output')
+    self.filesystem.CreateLink('/foo/build/local_machine', '/tmp')
+    self.assertFalse(self.filesystem.Exists(link_path))
+    self.assertFalse(self.filesystem.Exists(target))
+
+    target_contents = 'real baz contents'
+    fh = self.open(link_path, 'w')
+    fh.write(target_contents)
+    fh.close()
+    fh = self.open(target, 'r')
+    got_contents = fh.read()
+    fh.close()
+    self.assertEqual(target_contents, got_contents)
+
+  def testFileDescriptorsForDifferentFiles(self):
+    first_path = 'some_file1'
+    second_path = 'some_file2'
+    third_path = 'some_file3'
+    self.filesystem.CreateFile(first_path, contents='contents here1')
+    self.filesystem.CreateFile(second_path, contents='contents here2')
+    self.filesystem.CreateFile(third_path, contents='contents here3')
+
+    fake_file1 = self.open(first_path, 'r')
+    fake_file2 = self.open(second_path, 'r')
+    fake_file3 = self.open(third_path, 'r')
+    self.assertEqual(0, fake_file1.fileno())
+    self.assertEqual(1, fake_file2.fileno())
+    self.assertEqual(2, fake_file3.fileno())
+
+  def testFileDescriptorsForTheSameFileAreDifferent(self):
+    first_path = 'some_file1'
+    second_path = 'some_file2'
+    self.filesystem.CreateFile(first_path, contents='contents here1')
+    self.filesystem.CreateFile(second_path, contents='contents here2')
+
+    fake_file1 = self.open(first_path, 'r')
+    fake_file2 = self.open(second_path, 'r')
+    fake_file1a = self.open(first_path, 'r')
+    self.assertEqual(0, fake_file1.fileno())
+    self.assertEqual(1, fake_file2.fileno())
+    self.assertEqual(2, fake_file1a.fileno())
+
+  def testReusedFileDescriptorsDoNotAffectOthers(self):
+    first_path = 'some_file1'
+    second_path = 'some_file2'
+    third_path = 'some_file3'
+    self.filesystem.CreateFile(first_path, contents='contents here1')
+    self.filesystem.CreateFile(second_path, contents='contents here2')
+    self.filesystem.CreateFile(third_path, contents='contents here3')
+
+    fake_file1 = self.open(first_path, 'r')
+    fake_file2 = self.open(second_path, 'r')
+    fake_file3 = self.open(third_path, 'r')
+    fake_file1a = self.open(first_path, 'r')
+    self.assertEqual(0, fake_file1.fileno())
+    self.assertEqual(1, fake_file2.fileno())
+    self.assertEqual(2, fake_file3.fileno())
+    self.assertEqual(3, fake_file1a.fileno())
+
+    fake_file1.close()
+    fake_file2.close()
+    fake_file2 = self.open(second_path, 'r')
+    fake_file1b = self.open(first_path, 'r')
+    self.assertEqual(0, fake_file2.fileno())
+    self.assertEqual(1, fake_file1b.fileno())
+    self.assertEqual(2, fake_file3.fileno())
+    self.assertEqual(3, fake_file1a.fileno())
+
+  def testIntertwinedReadWrite(self):
+    file_path = 'some_file'
+    self.filesystem.CreateFile(file_path)
+    with self.open(file_path, 'a') as writer:
+      with self.open(file_path, 'r') as reader:
+        writes = ['hello', 'world\n', 'somewhere\nover', 'the\n', 'rainbow']
+        reads = []
+        # when writes are flushes, they are piped to the reader
+        for write in writes:
+          writer.write(write)
+          writer.flush()
+          reads.append(reader.read())
+          reader.flush()
+        self.assertEqual(writes, reads)
+        writes = ['nothing', 'to\nsee', 'here']
+        reads = []
+        # when writes are not flushed, the reader doesn't read anything new
+        for write in writes:
+          writer.write(write)
+          reads.append(reader.read())
+        self.assertEqual(['' for _ in writes], reads)
+
+  def testOpenIoErrors(self):
+    file_path = 'some_file'
+    self.filesystem.CreateFile(file_path)
+
+    with self.open(file_path, 'a') as fh:
+      self.assertRaises(IOError, fh.read)
+      self.assertRaises(IOError, fh.readlines)
+    with self.open(file_path, 'w') as fh:
+      self.assertRaises(IOError, fh.read)
+      self.assertRaises(IOError, fh.readlines)
+    with self.open(file_path, 'r') as fh:
+      self.assertRaises(IOError, fh.truncate)
+      self.assertRaises(IOError, fh.write, 'contents')
+      self.assertRaises(IOError, fh.writelines, ['con', 'tents'])
+
+    def _IteratorOpen(file_path, mode):
+      for _ in self.open(file_path, mode):
+        pass
+    self.assertRaises(IOError, _IteratorOpen, file_path, 'w')
+    self.assertRaises(IOError, _IteratorOpen, file_path, 'a')
+
+
+class OpenWithFileDescriptorTest(FakeFileOpenTestBase):
+
+  @unittest.skipIf(sys.version_info < (3, 0), 'only tested on 3.0 or greater')
+  def testOpenWithFileDescriptor(self):
+    file_path = 'this/file'
+    self.filesystem.CreateFile(file_path)
+    fd = self.os.open(file_path, os.O_CREAT)
+    self.assertEqual(fd, self.open(fd, 'r').fileno())
+
+  @unittest.skipIf(sys.version_info < (3, 0), 'only tested on 3.0 or greater')
+  def testClosefdWithFileDescriptor(self):
+    file_path = 'this/file'
+    self.filesystem.CreateFile(file_path)
+    fd = self.os.open(file_path, os.O_CREAT)
+    fh = self.open(fd, 'r', closefd=False)
+    fh.close()
+    self.assertIsNotNone(self.filesystem.open_files[fd])
+    fh = self.open(fd, 'r', closefd=True)
+    fh.close()
+    self.assertIsNone(self.filesystem.open_files[fd])
+
+
+class OpenWithBinaryFlagsTest(TestCase):
+
+  def setUp(self):
+    self.filesystem = fake_filesystem.FakeFilesystem(path_separator='/')
+    self.file = fake_filesystem.FakeFileOpen(self.filesystem)
+    self.os = fake_filesystem.FakeOsModule(self.filesystem)
+    self.file_path = 'some_file'
+    self.file_contents = b'binary contents'
+    self.filesystem.CreateFile(self.file_path, contents=self.file_contents)
+
+  def OpenFakeFile(self, mode):
+    return self.file(self.file_path, mode=mode)
+
+  def OpenFileAndSeek(self, mode):
+    fake_file = self.file(self.file_path, mode=mode)
+    fake_file.seek(0, 2)
+    return fake_file
+
+  def WriteAndReopenFile(self, fake_file, mode='rb'):
+    fake_file.write(self.file_contents)
+    fake_file.close()
+    return self.file(self.file_path, mode=mode)
+
+  def testReadBinary(self):
+    fake_file = self.OpenFakeFile('rb')
+    self.assertEqual(self.file_contents, fake_file.read())
+
+  def testWriteBinary(self):
+    fake_file = self.OpenFileAndSeek('wb')
+    self.assertEqual(0, fake_file.tell())
+    fake_file = self.WriteAndReopenFile(fake_file, mode='rb')
+    self.assertEqual(self.file_contents, fake_file.read())
+    # reopen the file in text mode
+    fake_file = self.OpenFakeFile('wb')
+    fake_file = self.WriteAndReopenFile(fake_file, mode='r')
+    self.assertEqual(self.file_contents.decode('ascii'), fake_file.read())
+
+  def testWriteAndReadBinary(self):
+    fake_file = self.OpenFileAndSeek('w+b')
+    self.assertEqual(0, fake_file.tell())
+    fake_file = self.WriteAndReopenFile(fake_file, mode='rb')
+    self.assertEqual(self.file_contents, fake_file.read())
+
+
+class OpenWithIgnoredFlagsTest(TestCase):
+
+  def setUp(self):
+    self.filesystem = fake_filesystem.FakeFilesystem(path_separator='/')
+    self.file = fake_filesystem.FakeFileOpen(self.filesystem)
+    self.os = fake_filesystem.FakeOsModule(self.filesystem)
+    self.file_path = 'some_file'
+    self.read_contents = self.file_contents = 'two\r\nlines'
+    # For python 3.x, text file newlines are converted to \n
+    if sys.version_info >= (3, 0):
+      self.read_contents = 'two\nlines'
+    self.filesystem.CreateFile(self.file_path, contents=self.file_contents)
+    # It's resonable to assume the file exists at this point
+
+  def OpenFakeFile(self, mode):
+    return self.file(self.file_path, mode=mode)
+
+  def OpenFileAndSeek(self, mode):
+    fake_file = self.file(self.file_path, mode=mode)
+    fake_file.seek(0, 2)
+    return fake_file
+
+  def WriteAndReopenFile(self, fake_file, mode='r'):
+    fake_file.write(self.file_contents)
+    fake_file.close()
+    return self.file(self.file_path, mode=mode)
+
+  def testReadText(self):
+    fake_file = self.OpenFakeFile('rt')
+    self.assertEqual(self.read_contents, fake_file.read())
+
+  def testReadUniversalNewlines(self):
+    fake_file = self.OpenFakeFile('rU')
+    self.assertEqual(self.read_contents, fake_file.read())
+
+  def testUniversalNewlines(self):
+    fake_file = self.OpenFakeFile('U')
+    self.assertEqual(self.read_contents, fake_file.read())
+
+  def testWriteText(self):
+    fake_file = self.OpenFileAndSeek('wt')
+    self.assertEqual(0, fake_file.tell())
+    fake_file = self.WriteAndReopenFile(fake_file)
+    self.assertEqual(self.read_contents, fake_file.read())
+
+  def testWriteAndReadTextBinary(self):
+    fake_file = self.OpenFileAndSeek('w+bt')
+    self.assertEqual(0, fake_file.tell())
+    if sys.version_info >= (3, 0):
+      self.assertRaises(TypeError, fake_file.write, self.file_contents)
+    else:
+      fake_file = self.WriteAndReopenFile(fake_file, mode='rb')
+      self.assertEqual(self.file_contents, fake_file.read())
+
+
+class OpenWithInvalidFlagsTest(FakeFileOpenTestBase):
+
+  def testCapitalR(self):
+    self.assertRaises(IOError, self.file, 'some_file', 'R')
+
+  def testCapitalW(self):
+    self.assertRaises(IOError, self.file, 'some_file', 'W')
+
+  def testCapitalA(self):
+    self.assertRaises(IOError, self.file, 'some_file', 'A')
+
+  def testLowerU(self):
+    self.assertRaises(IOError, self.file, 'some_file', 'u')
+
+  def testLowerRw(self):
+    self.assertRaises(IOError, self.file, 'some_file', 'rw')
+
+
+class ResolvePathTest(FakeFileOpenTestBase):
+
+  def __WriteToFile(self, file_name):
+    fh = self.open(file_name, 'w')
+    fh.write('x')
+    fh.close()
+
+  def testNoneFilepathRaisesTypeError(self):
+    self.assertRaises(TypeError, self.open, None, 'w')
+
+  def testEmptyFilepathRaisesIOError(self):
+    self.assertRaises(IOError, self.open, '', 'w')
+
+  def testNormalPath(self):
+    self.__WriteToFile('foo')
+    self.assertTrue(self.filesystem.Exists('foo'))
+
+  def testLinkWithinSameDirectory(self):
+    final_target = '/foo/baz'
+    self.filesystem.CreateLink('/foo/bar', 'baz')
+    self.__WriteToFile('/foo/bar')
+    self.assertTrue(self.filesystem.Exists(final_target))
+    self.assertEqual(1, self.os.stat(final_target)[stat.ST_SIZE])
+
+  def testLinkToSubDirectory(self):
+    final_target = '/foo/baz/bip'
+    self.filesystem.CreateDirectory('/foo/baz')
+    self.filesystem.CreateLink('/foo/bar', 'baz/bip')
+    self.__WriteToFile('/foo/bar')
+    self.assertTrue(self.filesystem.Exists(final_target))
+    self.assertEqual(1, self.os.stat(final_target)[stat.ST_SIZE])
+    self.assertTrue(self.filesystem.Exists('/foo/baz'))
+    # Make sure that intermediate directory got created.
+    new_dir = self.filesystem.GetObject('/foo/baz')
+    self.assertTrue(stat.S_IFDIR & new_dir.st_mode)
+
+  def testLinkToParentDirectory(self):
+    final_target = '/baz/bip'
+    self.filesystem.CreateDirectory('/foo')
+    self.filesystem.CreateDirectory('/baz')
+    self.filesystem.CreateLink('/foo/bar', '../baz')
+    self.__WriteToFile('/foo/bar/bip')
+    self.assertTrue(self.filesystem.Exists(final_target))
+    self.assertEqual(1, self.os.stat(final_target)[stat.ST_SIZE])
+    self.assertTrue(self.filesystem.Exists('/foo/bar'))
+
+  def testLinkToAbsolutePath(self):
+    final_target = '/foo/baz/bip'
+    self.filesystem.CreateDirectory('/foo/baz')
+    self.filesystem.CreateLink('/foo/bar', final_target)
+    self.__WriteToFile('/foo/bar')
+    self.assertTrue(self.filesystem.Exists(final_target))
+
+  def testRelativeLinksWorkAfterChdir(self):
+    final_target = '/foo/baz/bip'
+    self.filesystem.CreateDirectory('/foo/baz')
+    self.filesystem.CreateLink('/foo/bar', './baz/bip')
+    self.assertEqual(final_target,
+                     self.filesystem.ResolvePath('/foo/bar'))
+
+    os_module = fake_filesystem.FakeOsModule(self.filesystem)
+    self.assertTrue(os_module.path.islink('/foo/bar'))
+    os_module.chdir('/foo')
+    self.assertEqual('/foo', os_module.getcwd())
+    self.assertTrue(os_module.path.islink('bar'))
+
+    self.assertEqual('/foo/baz/bip',
+                     self.filesystem.ResolvePath('bar'))
+
+    self.__WriteToFile('/foo/bar')
+    self.assertTrue(self.filesystem.Exists(final_target))
+
+  def testAbsoluteLinksWorkAfterChdir(self):
+    final_target = '/foo/baz/bip'
+    self.filesystem.CreateDirectory('/foo/baz')
+    self.filesystem.CreateLink('/foo/bar', final_target)
+    self.assertEqual(final_target,
+                     self.filesystem.ResolvePath('/foo/bar'))
+
+    os_module = fake_filesystem.FakeOsModule(self.filesystem)
+    self.assertTrue(os_module.path.islink('/foo/bar'))
+    os_module.chdir('/foo')
+    self.assertEqual('/foo', os_module.getcwd())
+    self.assertTrue(os_module.path.islink('bar'))
+
+    self.assertEqual('/foo/baz/bip',
+                     self.filesystem.ResolvePath('bar'))
+
+    self.__WriteToFile('/foo/bar')
+    self.assertTrue(self.filesystem.Exists(final_target))
+
+  def testChdirThroughRelativeLink(self):
+    self.filesystem.CreateDirectory('/x/foo')
+    self.filesystem.CreateDirectory('/x/bar')
+    self.filesystem.CreateLink('/x/foo/bar', '../bar')
+    self.assertEqual('/x/bar', self.filesystem.ResolvePath('/x/foo/bar'))
+
+    os_module = fake_filesystem.FakeOsModule(self.filesystem)
+    os_module.chdir('/x/foo')
+    self.assertEqual('/x/foo', os_module.getcwd())
+    self.assertEqual('/x/bar', self.filesystem.ResolvePath('bar'))
+
+    os_module.chdir('bar')
+    self.assertEqual('/x/bar', os_module.getcwd())
+
+  def testReadLinkToLink(self):
+    # Write into the final link target and read back from a file which will
+    # point to that.
+    self.filesystem.CreateLink('/foo/bar', 'link')
+    self.filesystem.CreateLink('/foo/link', 'baz')
+    self.__WriteToFile('/foo/baz')
+    fh = self.open('/foo/bar', 'r')
+    self.assertEqual('x', fh.read())
+
+  def testWriteLinkToLink(self):
+    final_target = '/foo/baz'
+    self.filesystem.CreateLink('/foo/bar', 'link')
+    self.filesystem.CreateLink('/foo/link', 'baz')
+    self.__WriteToFile('/foo/bar')
+    self.assertTrue(self.filesystem.Exists(final_target))
+
+  def testMultipleLinks(self):
+    final_target = '/a/link1/c/link2/e'
+    self.os.makedirs('/a/link1/c/link2')
+
+    self.filesystem.CreateLink('/a/b', 'link1')
+    self.assertEqual('/a/link1', self.filesystem.ResolvePath('/a/b'))
+    self.assertEqual('/a/link1/c', self.filesystem.ResolvePath('/a/b/c'))
+
+    self.filesystem.CreateLink('/a/link1/c/d', 'link2')
+    self.assertTrue(self.filesystem.Exists('/a/link1/c/d'))
+    self.assertTrue(self.filesystem.Exists('/a/b/c/d'))
+
+    final_target = '/a/link1/c/link2/e'
+    self.assertFalse(self.filesystem.Exists(final_target))
+    self.__WriteToFile('/a/b/c/d/e')
+    self.assertTrue(self.filesystem.Exists(final_target))
+
+  def testTooManyLinks(self):
+    self.filesystem.CreateLink('/a/loop', 'loop')
+    self.assertFalse(self.filesystem.Exists('/a/loop'))
+
+
+class PathManipulationTests(TestCase):
+  def setUp(self):
+    self.filesystem = fake_filesystem.FakeFilesystem(path_separator='|')
+
+
+class CollapsePathPipeSeparatorTest(PathManipulationTests):
+  """Tests CollapsePath (mimics os.path.normpath) using | as path separator."""
+
+  def testEmptyPathBecomesDotPath(self):
+    self.assertEqual('.', self.filesystem.CollapsePath(''))
+
+  def testDotPathUnchanged(self):
+    self.assertEqual('.', self.filesystem.CollapsePath('.'))
+
+  def testSlashesAreNotCollapsed(self):
+    """Tests that '/' is not treated specially if the path separator is '|'.
+
+    In particular, multiple slashes should not be collapsed.
+    """
+    self.assertEqual('/', self.filesystem.CollapsePath('/'))
+    self.assertEqual('/////', self.filesystem.CollapsePath('/////'))
+
+  def testRootPath(self):
+    self.assertEqual('|', self.filesystem.CollapsePath('|'))
+
+  def testMultipleSeparatorsCollapsedIntoRootPath(self):
+    self.assertEqual('|', self.filesystem.CollapsePath('|||||'))
+
+  def testAllDotPathsRemovedButOne(self):
+    self.assertEqual('.', self.filesystem.CollapsePath('.|.|.|.'))
+
+  def testAllDotPathsRemovedIfAnotherPathComponentExists(self):
+    self.assertEqual('|', self.filesystem.CollapsePath('|.|.|.|'))
+    self.assertEqual('foo|bar', self.filesystem.CollapsePath('foo|.|.|.|bar'))
+
+  def testIgnoresUpLevelReferencesStartingFromRoot(self):
+    self.assertEqual('|', self.filesystem.CollapsePath('|..|..|..|'))
+    self.assertEqual('|', self.filesystem.CollapsePath('||..|.|..||'))
+    self.assertEqual(
+        '|', self.filesystem.CollapsePath('|..|..|foo|bar|..|..|'))
+
+  def testConservesUpLevelReferencesStartingFromCurrentDirectory(self):
+    self.assertEqual(
+        '..|..', self.filesystem.CollapsePath('..|foo|bar|..|..|..'))
+
+  def testCombineDotAndUpLevelReferencesInAbsolutePath(self):
+    self.assertEqual(
+        '|yes', self.filesystem.CollapsePath('|||||.|..|||yes|no|..|.|||'))
+
+  def testDotsInPathCollapsesToLastPath(self):
+    self.assertEqual(
+        'bar', self.filesystem.CollapsePath('foo|..|bar'))
+    self.assertEqual(
+        'bar', self.filesystem.CollapsePath('foo|..|yes|..|no|..|bar'))
+
+
+class SplitPathTest(PathManipulationTests):
+  """Tests SplitPath (which mimics os.path.split) using | as path separator."""
+
+  def testEmptyPath(self):
+    self.assertEqual(('', ''), self.filesystem.SplitPath(''))
+
+  def testNoSeparators(self):
+    self.assertEqual(('', 'ab'), self.filesystem.SplitPath('ab'))
+
+  def testSlashesDoNotSplit(self):
+    """Tests that '/' is not treated specially if the path separator is '|'."""
+    self.assertEqual(('', 'a/b'), self.filesystem.SplitPath('a/b'))
+
+  def testEliminateTrailingSeparatorsFromHead(self):
+    self.assertEqual(('a', 'b'), self.filesystem.SplitPath('a|b'))
+    self.assertEqual(('a', 'b'), self.filesystem.SplitPath('a|||b'))
+    self.assertEqual(('|a', 'b'), self.filesystem.SplitPath('|a||b'))
+    self.assertEqual(('a|b', 'c'), self.filesystem.SplitPath('a|b|c'))
+    self.assertEqual(('|a|b', 'c'), self.filesystem.SplitPath('|a|b|c'))
+
+  def testRootSeparatorIsNotStripped(self):
+    self.assertEqual(('|', ''), self.filesystem.SplitPath('|||'))
+    self.assertEqual(('|', 'a'), self.filesystem.SplitPath('|a'))
+    self.assertEqual(('|', 'a'), self.filesystem.SplitPath('|||a'))
+
+  def testEmptyTailIfPathEndsInSeparator(self):
+    self.assertEqual(('a|b', ''), self.filesystem.SplitPath('a|b|'))
+
+  def testEmptyPathComponentsArePreservedInHead(self):
+    self.assertEqual(('|a||b', 'c'), self.filesystem.SplitPath('|a||b||c'))
+
+
+class JoinPathTest(PathManipulationTests):
+  """Tests JoinPath (which mimics os.path.join) using | as path separator."""
+
+  def testOneEmptyComponent(self):
+    self.assertEqual('', self.filesystem.JoinPaths(''))
+
+  def testMultipleEmptyComponents(self):
+    self.assertEqual('', self.filesystem.JoinPaths('', '', ''))
+
+  def testSeparatorsNotStrippedFromSingleComponent(self):
+    self.assertEqual('||a||', self.filesystem.JoinPaths('||a||'))
+
+  def testOneSeparatorAddedBetweenComponents(self):
+    self.assertEqual('a|b|c|d', self.filesystem.JoinPaths('a', 'b', 'c', 'd'))
+
+  def testNoSeparatorAddedForComponentsEndingInSeparator(self):
+    self.assertEqual('a|b|c', self.filesystem.JoinPaths('a|', 'b|', 'c'))
+    self.assertEqual('a|||b|||c',
+                     self.filesystem.JoinPaths('a|||', 'b|||', 'c'))
+
+  def testComponentsPrecedingAbsoluteComponentAreIgnored(self):
+    self.assertEqual('|c|d', self.filesystem.JoinPaths('a', '|b', '|c', 'd'))
+
+  def testOneSeparatorAddedForTrailingEmptyComponents(self):
+    self.assertEqual('a|', self.filesystem.JoinPaths('a', ''))
+    self.assertEqual('a|', self.filesystem.JoinPaths('a', '', ''))
+
+  def testNoSeparatorAddedForLeadingEmptyComponents(self):
+    self.assertEqual('a', self.filesystem.JoinPaths('', 'a'))
+
+  def testInternalEmptyComponentsIgnored(self):
+    self.assertEqual('a|b', self.filesystem.JoinPaths('a', '', 'b'))
+    self.assertEqual('a|b|', self.filesystem.JoinPaths('a|', '', 'b|'))
+
+
+class PathSeparatorTest(TestCase):
+  def testOsPathSepMatchesFakeFilesystemSeparator(self):
+    filesystem = fake_filesystem.FakeFilesystem(path_separator='!')
+    fake_os = fake_filesystem.FakeOsModule(filesystem)
+    self.assertEqual('!', fake_os.sep)
+    self.assertEqual('!', fake_os.path.sep)
+
+
+if __name__ == '__main__':
+  main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/fake_filesystem_unittest.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/fake_filesystem_unittest.py
new file mode 100644
index 0000000..692fb2f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/fake_filesystem_unittest.py
@@ -0,0 +1,226 @@
+# Copyright 2014 Altera Corporation. All Rights Reserved.
+# Copyright 2015 John McGehee
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""A base class for unit tests using the :py:class:`pyfakefs` module.
+
+This class searches `sys.modules` for modules that import the `os`, `glob`,
+`shutil`, and `tempfile` modules.
+
+The `setUp()` method binds these modules to the corresponding fake
+modules from `pyfakefs`.  Further, the built in functions `file()` and
+`open()` are bound to fake functions.
+
+The `tearDownPyfakefs()` method returns the module bindings to their original
+state.
+
+It is expected that `setUp()` be invoked at the beginning of the derived
+class' `setUp()` method, and `tearDownPyfakefs()` be invoked at the end of the
+derived class' `tearDown()` method.
+
+During the test, everything uses the fake file system and modules.  This means
+that even in your test, you can use familiar functions like `open()` and
+`os.makedirs()` to manipulate the fake file system.
+
+This also means existing unit tests that use the real file system can be
+retrofitted to use `pyfakefs` by simply changing their base class from
+`:py:class`unittest.TestCase` to
+`:py:class`pyfakefs.fake_filesystem_unittest.TestCase`.
+"""
+
+import sys
+import unittest
+import doctest
+import fake_filesystem
+import fake_filesystem_glob
+import fake_filesystem_shutil
+import fake_tempfile
+if sys.version_info < (3,):
+    import __builtin__ as builtins
+else:
+    import builtins
+
+import mox3.stubout
+
+def load_doctests(loader, tests, ignore, module):
+    '''Load the doctest tests for the specified module into unittest.'''
+    _patcher = Patcher()
+    globs = _patcher.replaceGlobs(vars(module))
+    tests.addTests(doctest.DocTestSuite(module,
+                                        globs=globs,
+                                        setUp=_patcher.setUp,
+                                        tearDown=_patcher.tearDown))
+    return tests
+
+
+class TestCase(unittest.TestCase):
+    def __init__(self, methodName='runTest'):
+        super(TestCase, self).__init__(methodName)
+        self._stubber = Patcher()
+        
+    @property
+    def fs(self):
+        return self._stubber.fs
+    
+    @property
+    def patches(self):
+        return self._stubber.patches
+        
+    def setUpPyfakefs(self):
+        '''Bind the file-related modules to the :py:class:`pyfakefs` fake file
+        system instead of the real file system.  Also bind the fake `file()` and
+        `open()` functions.
+        
+        Invoke this at the beginning of the `setUp()` method in your unit test
+        class.
+        '''
+        self._stubber.setUp()
+        self.addCleanup(self._stubber.tearDown)
+
+    
+    def tearDownPyfakefs(self):
+        ''':meth:`pyfakefs.fake_filesystem_unittest.setUpPyfakefs` registers the
+        tear down procedure using :meth:`unittest.TestCase.addCleanup`.  Thus this
+        method is deprecated, and remains just for backward compatibility.
+        '''
+        pass
+
+class Patcher(object):
+    '''
+    Instantiate a stub creator to bind and un-bind the file-related modules to
+    the :py:mod:`pyfakefs` fake modules.
+    '''
+    SKIPMODULES = set([None, fake_filesystem, fake_filesystem_glob,
+                      fake_filesystem_shutil, fake_tempfile, sys])
+    '''Stub nothing that is imported within these modules.
+    `sys` is included to prevent `sys.path` from being stubbed with the fake
+    `os.path`.
+    '''
+    assert None in SKIPMODULES, "sys.modules contains 'None' values; must skip them."
+    
+    SKIPNAMES = set(['os', 'glob', 'path', 'shutil', 'tempfile'])
+        
+    def __init__(self):
+        # Attributes set by _findModules()
+        self._osModules = None
+        self._globModules = None
+        self._pathModules = None
+        self._shutilModules = None
+        self._tempfileModules = None
+        self._findModules()
+        assert None not in vars(self).values(), \
+                "_findModules() missed the initialization of an instance variable"
+        
+        # Attributes set by _refresh()
+        self._stubs = None
+        self.fs = None
+        self.fake_os = None
+        self.fake_glob = None
+        self.fake_path = None
+        self.fake_shutil = None
+        self.fake_tempfile_ = None
+        self.fake_open = None
+        # _isStale is set by tearDown(), reset by _refresh()
+        self._isStale = True
+        self._refresh()
+        assert None not in vars(self).values(), \
+                "_refresh() missed the initialization of an instance variable"
+        assert self._isStale == False, "_refresh() did not reset _isStale"
+        
+    def _findModules(self):
+        '''Find and cache all modules that import file system modules.
+        Later, `setUp()` will stub these with the fake file system
+        modules.
+        '''
+        self._osModules = set()
+        self._globModules = set()
+        self._pathModules = set()
+        self._shutilModules = set()
+        self._tempfileModules = set()
+        for name, module in set(sys.modules.items()):
+            if module in self.SKIPMODULES or name in self.SKIPNAMES:
+                continue
+            if 'os' in module.__dict__:
+                self._osModules.add(module)
+            if 'glob' in module.__dict__:
+                self._globModules.add(module)
+            if 'path' in module.__dict__:
+                self._pathModules.add(module)
+            if 'shutil' in module.__dict__:
+                self._shutilModules.add(module)
+            if 'tempfile' in module.__dict__:
+                self._tempfileModules.add(module)
+
+    def _refresh(self):
+        '''Renew the fake file system and set the _isStale flag to `False`.'''
+        if self._stubs is not None:
+            self._stubs.SmartUnsetAll()
+        self._stubs = mox3.stubout.StubOutForTesting()
+
+        self.fs = fake_filesystem.FakeFilesystem()
+        self.fake_os = fake_filesystem.FakeOsModule(self.fs)
+        self.fake_glob = fake_filesystem_glob.FakeGlobModule(self.fs)
+        self.fake_path = self.fake_os.path
+        self.fake_shutil = fake_filesystem_shutil.FakeShutilModule(self.fs)
+        self.fake_tempfile_ = fake_tempfile.FakeTempfileModule(self.fs)
+        self.fake_open = fake_filesystem.FakeFileOpen(self.fs)
+
+        self._isStale = False
+
+    def setUp(self, doctester=None):
+        '''Bind the file-related modules to the :py:mod:`pyfakefs` fake
+        modules real ones.  Also bind the fake `file()` and `open()` functions.
+        '''
+        if self._isStale:
+            self._refresh()
+        
+        if doctester is not None:
+            doctester.globs = self.replaceGlobs(doctester.globs)
+            
+        if sys.version_info < (3,):
+            # No file() in Python3
+            self._stubs.SmartSet(builtins, 'file', self.fake_open)
+        self._stubs.SmartSet(builtins, 'open', self.fake_open)
+        
+        for module in self._osModules:
+            self._stubs.SmartSet(module,  'os', self.fake_os)
+        for module in self._globModules:
+            self._stubs.SmartSet(module,  'glob', self.fake_glob)
+        for module in self._pathModules:
+            self._stubs.SmartSet(module,  'path', self.fake_path)
+        for module in self._shutilModules:
+            self._stubs.SmartSet(module,  'shutil', self.fake_shutil)
+        for module in self._tempfileModules:
+            self._stubs.SmartSet(module,  'tempfile', self.fake_tempfile_)
+    
+    def replaceGlobs(self, globs_):
+        globs = globs_.copy()
+        if self._isStale:
+            self._refresh()
+        if 'os' in globs:
+            globs['os'] = fake_filesystem.FakeOsModule(self.fs)
+        if 'glob' in globs:
+            globs['glob'] = fake_filesystem_glob.FakeGlobModule(self.fs)
+        if 'path' in globs:
+            globs['path'] =  fake_filesystem.FakePathModule(self.fs)
+        if 'shutil' in globs:
+            globs['shutil'] = fake_filesystem_shutil.FakeShutilModule(self.fs)
+        if 'tempfile' in globs:
+            globs['tempfile'] = fake_tempfile.FakeTempfileModule(self.fs)
+        return globs
+    
+    def tearDown(self, doctester=None):
+        '''Clear the fake filesystem bindings created by `setUp()`.'''
+        self._isStale = True
+        self._stubs.SmartUnsetAll()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/fake_filesystem_unittest_test.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/fake_filesystem_unittest_test.py
new file mode 100644
index 0000000..7d0ac70
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/fake_filesystem_unittest_test.py
@@ -0,0 +1,107 @@
+#! /usr/bin/env python
+#
+# Copyright 2014 Altera Corporation. All Rights Reserved.
+# Author: John McGehee
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+Test the :py:class`pyfakefs.fake_filesystem_unittest.TestCase` base class.
+"""
+
+import os
+import glob
+import shutil
+import tempfile
+import sys
+if sys.version_info < (2, 7):
+    import unittest2 as unittest
+else:
+    import unittest
+import fake_filesystem_unittest
+import pytest
+
+class TestPyfakefsUnittest(fake_filesystem_unittest.TestCase): # pylint: disable=R0904
+    '''Test the pyfakefs.fake_filesystem_unittest.TestCase` base class.'''
+
+    def setUp(self):
+        '''Set up the fake file system'''
+        self.setUpPyfakefs()
+
+    def tearDown(self):
+        '''Tear down the fake file system'''
+        self.tearDownPyfakefs()
+
+    @unittest.skipIf(sys.version_info > (2,), "file() was removed in Python 3")
+    def test_file(self):
+        '''Fake `file()` function is bound'''
+        self.assertFalse(os.path.exists('/fake_file.txt'))
+        with file('/fake_file.txt', 'w') as f:
+            f.write("This test file was created using the file() function.\n")
+        self.assertTrue(self.fs.Exists('/fake_file.txt'))
+        with file('/fake_file.txt') as f:
+            content = f.read()
+        self.assertEqual(content,
+                         'This test file was created using the file() function.\n')
+            
+    def test_open(self):
+        '''Fake `open()` function is bound'''
+        self.assertFalse(os.path.exists('/fake_file.txt'))
+        with open('/fake_file.txt', 'w') as f:
+            f.write("This test file was created using the open() function.\n")
+        self.assertTrue(self.fs.Exists('/fake_file.txt'))
+        with open('/fake_file.txt') as f:
+            content = f.read()
+        self.assertEqual(content,
+                         'This test file was created using the open() function.\n')
+            
+    def test_os(self):
+        '''Fake os module is bound'''
+        self.assertFalse(self.fs.Exists('/test/dir1/dir2'))          
+        os.makedirs('/test/dir1/dir2')
+        self.assertTrue(self.fs.Exists('/test/dir1/dir2'))          
+        
+    def test_glob(self):
+        '''Fake glob module is bound'''
+        self.assertCountEqual(glob.glob('/test/dir1/dir*'),
+                              [])
+        self.fs.CreateDirectory('/test/dir1/dir2a')
+        self.assertCountEqual(glob.glob('/test/dir1/dir*'),
+                              ['/test/dir1/dir2a'])
+        self.fs.CreateDirectory('/test/dir1/dir2b')
+        self.assertCountEqual(glob.glob('/test/dir1/dir*'),
+                              ['/test/dir1/dir2a', '/test/dir1/dir2b'])
+
+    def test_shutil(self):
+        '''Fake shutil module is bound'''
+        self.fs.CreateDirectory('/test/dir1/dir2a')
+        self.fs.CreateDirectory('/test/dir1/dir2b')
+        self.assertTrue(self.fs.Exists('/test/dir1/dir2b'))
+        self.assertTrue(self.fs.Exists('/test/dir1/dir2a'))
+       
+        shutil.rmtree('/test/dir1')
+        self.assertFalse(self.fs.Exists('/test/dir1'))
+
+    def test_tempfile(self):
+        '''Fake tempfile module is bound'''
+        with tempfile.NamedTemporaryFile() as tf:
+            tf.write(b'Temporary file contents\n')
+            name = tf.name
+            self.assertTrue(self.fs.Exists(tf.name))
+    
+    def test_pytest(self):
+        '''Compatibility with the :py:module:`pytest` module.'''
+        pass
+                      
+if __name__ == "__main__":
+    unittest.main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/fake_filesystem_vs_real_test.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/fake_filesystem_vs_real_test.py
new file mode 100755
index 0000000..9da1bc8
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/fake_filesystem_vs_real_test.py
@@ -0,0 +1,612 @@
+#! /usr/bin/env python
+#
+# Copyright 2009 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#            http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Test that FakeFilesystem calls work identically to a real filesystem."""
+
+#pylint: disable-all
+
+import os #@UnusedImport
+import os.path
+import shutil
+import sys
+import tempfile
+import time
+import sys
+if sys.version_info < (2, 7):
+    import unittest2 as unittest
+else:
+    import unittest
+
+import fake_filesystem
+
+
+def Sep(path):
+    """Converts slashes in the path to the architecture's path seperator."""
+    if isinstance(path, str):
+        return path.replace('/', os.sep)
+    return path
+
+
+class TestCase(unittest.TestCase):
+    is_windows = sys.platform.startswith('win')
+    is_cygwin = sys.platform == 'cygwin'
+    _FAKE_FS_BASE = Sep('/fakefs')
+
+
+class FakeFilesystemVsRealTest(TestCase):
+
+    def _Paths(self, path):
+        """For a given path, return paths in the real and fake filesystems."""
+        if not path:
+            return (None, None)
+        return (os.path.join(self.real_base, path),
+                        os.path.join(self.fake_base, path))
+
+    def _CreateTestFile(self, file_type, path, contents=None):
+        """Create a dir, file, or link in both the real fs and the fake."""
+        path = Sep(path)
+        self._created_files.append([file_type, path, contents])
+        real_path, fake_path = self._Paths(path)
+        if file_type == 'd':
+            os.mkdir(real_path)
+            self.fake_os.mkdir(fake_path)
+        if file_type == 'f':
+            fh = open(real_path, 'w')
+            fh.write(contents or '')
+            fh.close()
+            fh = self.fake_open(fake_path, 'w')
+            fh.write(contents or '')
+            fh.close()
+        # b for binary file
+        if file_type == 'b':
+            fh = open(real_path, 'wb')
+            fh.write(contents or '')
+            fh.close()
+            fh = self.fake_open(fake_path, 'wb')
+            fh.write(contents or '')
+            fh.close()
+        # l for symlink, h for hard link
+        if file_type in ('l', 'h'):
+            real_target, fake_target = (contents, contents)
+            # If it begins with '/', make it relative to the base.    You can't go
+            # creating files in / for the real file system.
+            if contents.startswith(os.sep):
+                real_target, fake_target = self._Paths(contents[1:])
+            if file_type == 'l':
+                os.symlink(real_target, real_path)
+                self.fake_os.symlink(fake_target, fake_path)
+            elif file_type == 'h':
+                os.link(real_target, real_path)
+                self.fake_os.link(fake_target, fake_path)
+
+    def setUp(self):
+        # Base paths in the real and test file systems.     We keep them different
+        # so that missing features in the fake don't fall through to the base
+        # operations and magically succeed.
+        tsname = 'fakefs.%s' % time.time()
+        # Fully expand the base_path - required on OS X.
+        self.real_base = os.path.realpath(
+                os.path.join(tempfile.gettempdir(), tsname))
+        os.chdir(tempfile.gettempdir())
+        if os.path.isdir(self.real_base):
+            shutil.rmtree(self.real_base)
+        os.mkdir(self.real_base)
+        self.fake_base = self._FAKE_FS_BASE
+
+        # Make sure we can write to the physical testing temp directory.
+        self.assertTrue(os.access(self.real_base, os.W_OK))
+
+        self.fake_filesystem = fake_filesystem.FakeFilesystem()
+        self.fake_filesystem.CreateDirectory(self.fake_base)
+        self.fake_os = fake_filesystem.FakeOsModule(self.fake_filesystem)
+        self.fake_open = fake_filesystem.FakeFileOpen(self.fake_filesystem)
+        self._created_files = []
+
+        os.chdir(self.real_base)
+        self.fake_os.chdir(self.fake_base)
+
+    def tearDown(self):
+        # We have to remove all the files from the real FS. Doing the same for the
+        # fake FS is optional, but doing it is an extra sanity check.
+        os.chdir(tempfile.gettempdir())
+        try:
+            rev_files = self._created_files[:]
+            rev_files.reverse()
+            for info in rev_files:
+                real_path, fake_path = self._Paths(info[1])
+                if info[0] == 'd':
+                    try:
+                        os.rmdir(real_path)
+                    except OSError as e:
+                        if 'Directory not empty' in e:
+                            self.fail('Real path %s not empty: %s : %s' % (
+                                    real_path, e, os.listdir(real_path)))
+                        else:
+                            raise
+                    self.fake_os.rmdir(fake_path)
+                if info[0] == 'f' or info[0] == 'l':
+                    os.remove(real_path)
+                    self.fake_os.remove(fake_path)
+        finally:
+            shutil.rmtree(self.real_base)
+
+    def _GetErrno(self, raised_error):
+        try:
+            return (raised_error and raised_error.errno) or None
+        except AttributeError:
+            return None
+
+    def _CompareBehaviors(self, method_name, path, real, fake,
+                                                method_returns_path=False):
+        """Invoke an os method in both real and fake contexts and compare results.
+
+        Invoke a real filesystem method with a path to a real file and invoke a fake
+        filesystem method with a path to a fake file and compare the results.    We
+        expect some calls to throw Exceptions, so we catch those and compare them.
+
+        Args:
+            method_name: Name of method being tested, for use in error messages.
+            path: potential path to a file in the real and fake file systems, passing
+                an empty tuple indicates that no arguments to pass to method.
+            real: built-in system library or method from the built-in system library
+                which takes a path as an arg and returns some value.
+            fake: fake_filsystem object or method from a fake_filesystem class
+                which takes a path as an arg and returns some value.
+            method_returns_path: True if the method returns a path, and thus we must
+                compensate for expected difference between real and fake.
+
+        Returns:
+            A description of the difference in behavior, or None.
+        """
+        # pylint: disable=C6403
+
+        def _ErrorClass(e):
+            return (e and e.__class__.__name__) or 'None'
+
+        real_value = None
+        fake_value = None
+        real_err = None
+        fake_err = None
+        method_call = '%s' % method_name
+        method_call += '()' if path == () else '(%s)' % path
+        # Catching Exception below gives a lint warning, but it's what we need.
+        try:
+            args = [] if path == () else [path]
+            real_method = real
+            if not callable(real):
+                real_method = getattr(real, method_name)
+            real_value = str(real_method(*args))
+        except Exception as e:    # pylint: disable-msg=W0703
+            real_err = e
+        try:
+            fake_method = fake
+            if not callable(fake):
+                fake_method = getattr(fake, method_name)
+            args = [] if path == () else [path]
+            fake_value = str(fake_method(*args))
+        except Exception as e:    # pylint: disable-msg=W0703
+            fake_err = e
+        # We only compare on the error class because the acutal error contents
+        # is almost always different because of the file paths.
+        if _ErrorClass(real_err) != _ErrorClass(fake_err):
+            if real_err is None:
+                return '%s: real version returned %s, fake raised %s' % (
+                        method_call, real_value, _ErrorClass(fake_err))
+            if fake_err is None:
+                return '%s: real version raised %s, fake returned %s' % (
+                        method_call, _ErrorClass(real_err), fake_value)
+            return '%s: real version raised %s, fake raised %s' % (
+                    method_call, _ErrorClass(real_err), _ErrorClass(fake_err))
+        real_errno = self._GetErrno(real_err)
+        fake_errno = self._GetErrno(fake_err)
+        if real_errno != fake_errno:
+            return '%s(%s): both raised %s, real errno %s, fake errno %s' % (
+                    method_name, path, _ErrorClass(real_err), real_errno, fake_errno)
+        # If the method is supposed to return a full path AND both values
+        # begin with the expected full path, then trim it off.
+        if method_returns_path:
+            if (real_value and fake_value
+                    and real_value.startswith(self.real_base)
+                    and fake_value.startswith(self.fake_base)):
+                real_value = real_value[len(self.real_base):]
+                fake_value = fake_value[len(self.fake_base):]
+        if real_value != fake_value:
+            return '%s: real return %s, fake returned %s' % (
+                    method_call, real_value, fake_value)
+        return None
+
+    def assertOsMethodBehaviorMatches(self, method_name, path,
+                                                                        method_returns_path=False):
+        """Invoke an os method in both real and fake contexts and compare.
+
+        For a given method name (from the os module) and a path, compare the
+        behavior of the system provided module against the fake_filesytem module.
+        We expect results and/or Exceptions raised to be identical.
+
+        Args:
+            method_name: Name of method being tested.
+            path: potential path to a file in the real and fake file systems.
+            method_returns_path: True if the method returns a path, and thus we must
+                compensate for expected difference between real and fake.
+
+        Returns:
+            A description of the difference in behavior, or None.
+        """
+        path = Sep(path)
+        return self._CompareBehaviors(method_name, path, os, self.fake_os,
+                                                                    method_returns_path)
+
+    def DiffOpenMethodBehavior(self, method_name, path, mode, data,
+                                                         method_returns_data=True):
+        """Invoke an open method in both real and fkae contexts and compare.
+
+        Args:
+            method_name: Name of method being tested.
+            path: potential path to a file in the real and fake file systems.
+            mode: how to open the file.
+            data: any data to pass to the method.
+            method_returns_data: True if a method returns some sort of data.
+
+        For a given method name (from builtin open) and a path, compare the
+        behavior of the system provided module against the fake_filesytem module.
+        We expect results and/or Exceptions raised to be identical.
+
+        Returns:
+            A description of the difference in behavior, or None.
+        """
+        with open(path, mode) as real_fh:
+            with self.fake_open(path, mode) as fake_fh:
+                return self._CompareBehaviors(method_name, data, real_fh, fake_fh,
+                                                                            method_returns_data)
+
+    def DiffOsPathMethodBehavior(self, method_name, path,
+                                                             method_returns_path=False):
+        """Invoke an os.path method in both real and fake contexts and compare.
+
+        For a given method name (from the os.path module) and a path, compare the
+        behavior of the system provided module against the fake_filesytem module.
+        We expect results and/or Exceptions raised to be identical.
+
+        Args:
+            method_name: Name of method being tested.
+            path: potential path to a file in the real and fake file systems.
+            method_returns_path: True if the method returns a path, and thus we must
+                compensate for expected difference between real and fake.
+
+        Returns:
+            A description of the difference in behavior, or None.
+        """
+        return self._CompareBehaviors(method_name, path, os.path, self.fake_os.path,
+                                                                    method_returns_path)
+
+    def assertOsPathMethodBehaviorMatches(self, method_name, path,
+                                                                                method_returns_path=False):
+        """Assert that an os.path behaves the same in both real and fake contexts.
+
+        Wraps DiffOsPathMethodBehavior, raising AssertionError if any differences
+        are reported.
+
+        Args:
+            method_name: Name of method being tested.
+            path: potential path to a file in the real and fake file systems.
+            method_returns_path: True if the method returns a path, and thus we must
+                compensate for expected difference between real and fake.
+
+        Raises:
+            AssertionError if there is any difference in behavior.
+        """
+        path = Sep(path)
+        diff = self.DiffOsPathMethodBehavior(method_name, path, method_returns_path)
+        if diff:
+            self.fail(diff)
+
+    def assertAllOsBehaviorsMatch(self, path):
+        path = Sep(path)
+        os_method_names = [] if self.is_windows else ['readlink']
+        os_method_names_no_args = ['getcwd']
+        if sys.version_info < (3, 0):
+            os_method_names_no_args.append('getcwdu')
+        os_path_method_names = ['isabs',
+                                                        'isdir',
+                                                        'isfile',
+                                                        'exists'
+                                                     ]
+        if not self.is_windows:
+            os_path_method_names.append('islink')
+            os_path_method_names.append('lexists')
+        wrapped_methods = [['access', self._AccessReal, self._AccessFake],
+                                             ['stat.size', self._StatSizeReal, self._StatSizeFake],
+                                             ['lstat.size', self._LstatSizeReal, self._LstatSizeFake]
+                                            ]
+
+        differences = []
+        for method_name in os_method_names:
+            diff = self.assertOsMethodBehaviorMatches(method_name, path)
+            if diff:
+                differences.append(diff)
+        for method_name in os_method_names_no_args:
+            diff = self.assertOsMethodBehaviorMatches(method_name, (),
+                                                                                                method_returns_path=True)
+            if diff:
+                differences.append(diff)
+        for method_name in os_path_method_names:
+            diff = self.DiffOsPathMethodBehavior(method_name, path)
+            if diff:
+                differences.append(diff)
+        for m in wrapped_methods:
+            diff = self._CompareBehaviors(m[0], path, m[1], m[2])
+            if diff:
+                differences.append(diff)
+        if differences:
+            self.fail('Behaviors do not match for %s:\n    %s' %
+                                (path, '\n    '.join(differences)))
+
+    def assertFileHandleBehaviorsMatch(self, path, mode, data):
+        path = Sep(path)
+        write_method_names = ['write', 'writelines']
+        read_method_names = ['read', 'readlines']
+        other_method_names = ['truncate', 'flush', 'close']
+        differences = []
+        for method_name in write_method_names:
+            diff = self.DiffOpenMethodBehavior(method_name, path, mode, data)
+            if diff:
+                differences.append(diff)
+        for method_name in read_method_names + other_method_names:
+            diff = self.DiffOpenMethodBehavior(method_name, path, mode, ())
+            if diff:
+                differences.append(diff)
+        if differences:
+            self.fail('Behaviors do not match for %s:\n    %s' %
+                                (path, '\n    '.join(differences)))
+
+    # Helpers for checks which are not straight method calls.
+
+    def _AccessReal(self, path):
+        return os.access(path, os.R_OK)
+
+    def _AccessFake(self, path):
+        return self.fake_os.access(path, os.R_OK)
+
+    def _StatSizeReal(self, path):
+        real_path, unused_fake_path = self._Paths(path)
+        # fake_filesystem.py does not implement stat().st_size for directories
+        if os.path.isdir(real_path):
+            return None
+        return os.stat(real_path).st_size
+
+    def _StatSizeFake(self, path):
+        unused_real_path, fake_path = self._Paths(path)
+        # fake_filesystem.py does not implement stat().st_size for directories
+        if self.fake_os.path.isdir(fake_path):
+            return None
+        return self.fake_os.stat(fake_path).st_size
+
+    def _LstatSizeReal(self, path):
+        real_path, unused_fake_path = self._Paths(path)
+        if os.path.isdir(real_path):
+            return None
+        size = os.lstat(real_path).st_size
+        # Account for the difference in the lengths of the absolute paths.
+        if os.path.islink(real_path):
+            if os.readlink(real_path).startswith(os.sep):
+                size -= len(self.real_base)
+        return size
+
+    def _LstatSizeFake(self, path):
+        unused_real_path, fake_path = self._Paths(path)
+        #size = 0
+        if self.fake_os.path.isdir(fake_path):
+            return None
+        size = self.fake_os.lstat(fake_path).st_size
+        # Account for the difference in the lengths of the absolute paths.
+        if self.fake_os.path.islink(fake_path):
+            if self.fake_os.readlink(fake_path).startswith(os.sep):
+                size -= len(self.fake_base)
+        return size
+
+    def testIsabs(self):
+        # We do not have to create any files for isabs.
+        self.assertOsPathMethodBehaviorMatches('isabs', None)
+        self.assertOsPathMethodBehaviorMatches('isabs', '')
+        self.assertOsPathMethodBehaviorMatches('isabs', '/')
+        self.assertOsPathMethodBehaviorMatches('isabs', '/a')
+        self.assertOsPathMethodBehaviorMatches('isabs', 'a')
+
+    def testNonePath(self):
+        self.assertAllOsBehaviorsMatch(None)
+
+    def testEmptyPath(self):
+        self.assertAllOsBehaviorsMatch('')
+
+    def testRootPath(self):
+        self.assertAllOsBehaviorsMatch('/')
+
+    def testNonExistantFile(self):
+        self.assertAllOsBehaviorsMatch('foo')
+
+    def testEmptyFile(self):
+        self._CreateTestFile('f', 'aFile')
+        self.assertAllOsBehaviorsMatch('aFile')
+
+    def testFileWithContents(self):
+        self._CreateTestFile('f', 'aFile', 'some contents')
+        self.assertAllOsBehaviorsMatch('aFile')
+
+    def testFileWithBinaryContents(self):
+        self._CreateTestFile('b', 'aFile', b'some contents')
+        self.assertAllOsBehaviorsMatch('aFile')
+
+    @unittest.skipIf(TestCase.is_windows, 'no symlink in Windows')
+    def testSymLinkToEmptyFile(self):
+        self._CreateTestFile('f', 'aFile')
+        self._CreateTestFile('l', 'link_to_empty', 'aFile')
+        self.assertAllOsBehaviorsMatch('link_to_empty')
+
+    @unittest.skipIf(TestCase.is_windows, 'no symlink in Windows')
+    def TBD_testHardLinkToEmptyFile(self):
+        self._CreateTestFile('f', 'aFile')
+        self._CreateTestFile('h', 'link_to_empty', 'aFile')
+        self.assertAllOsBehaviorsMatch('link_to_empty')
+
+    @unittest.skipIf(TestCase.is_windows, 'no symlink in Windows')
+    def testSymLinkToRealFile(self):
+        self._CreateTestFile('f', 'aFile', 'some contents')
+        self._CreateTestFile('l', 'link_to_file', 'aFile')
+        self.assertAllOsBehaviorsMatch('link_to_file')
+
+    @unittest.skipIf(TestCase.is_windows, 'no symlink in Windows')
+    def TBD_testHardLinkToRealFile(self):
+        self._CreateTestFile('f', 'aFile', 'some contents')
+        self._CreateTestFile('h', 'link_to_file', 'aFile')
+        self.assertAllOsBehaviorsMatch('link_to_file')
+
+    @unittest.skipIf(TestCase.is_windows, 'no symlink in Windows')
+    def testBrokenSymLink(self):
+        self._CreateTestFile('l', 'broken_link', 'broken')
+        self._CreateTestFile('l', 'loop', '/a/loop')
+        self.assertAllOsBehaviorsMatch('broken_link')
+
+    def testFileInAFolder(self):
+        self._CreateTestFile('d', 'a')
+        self._CreateTestFile('d', 'a/b')
+        self._CreateTestFile('f', 'a/b/file', 'contents')
+        self.assertAllOsBehaviorsMatch('a/b/file')
+
+    @unittest.skipIf(TestCase.is_windows, 'no symlink in Windows')
+    def testAbsoluteSymLinkToFolder(self):
+        self._CreateTestFile('d', 'a')
+        self._CreateTestFile('d', 'a/b')
+        self._CreateTestFile('f', 'a/b/file', 'contents')
+        self._CreateTestFile('l', 'a/link', '/a/b')
+        self.assertAllOsBehaviorsMatch('a/link/file')
+
+    @unittest.skipIf(TestCase.is_windows, 'no symlink in Windows')
+    def testLinkToFolderAfterChdir(self):
+        self._CreateTestFile('d', 'a')
+        self._CreateTestFile('d', 'a/b')
+        self._CreateTestFile('f', 'a/b/file', 'contents')
+        self._CreateTestFile('l', 'a/link', '/a/b')
+
+        real_dir, fake_dir = self._Paths('a/b')
+        os.chdir(real_dir)
+        self.fake_os.chdir(fake_dir)
+        self.assertAllOsBehaviorsMatch('file')
+
+    @unittest.skipIf(TestCase.is_windows, 'no symlink in Windows')
+    def testRelativeSymLinkToFolder(self):
+        self._CreateTestFile('d', 'a')
+        self._CreateTestFile('d', 'a/b')
+        self._CreateTestFile('f', 'a/b/file', 'contents')
+        self._CreateTestFile('l', 'a/link', 'b')
+        self.assertAllOsBehaviorsMatch('a/link/file')
+
+    @unittest.skipIf(TestCase.is_windows, 'no symlink in Windows')
+    def testSymLinkToParent(self):
+        # Soft links on HFS+ / OS X behave differently.
+        if os.uname()[0] != 'Darwin':
+            self._CreateTestFile('d', 'a')
+            self._CreateTestFile('d', 'a/b')
+            self._CreateTestFile('l', 'a/b/c', '..')
+            self.assertAllOsBehaviorsMatch('a/b/c')
+
+    @unittest.skipIf(TestCase.is_windows, 'no symlink in Windows')
+    def testPathThroughSymLinkToParent(self):
+        self._CreateTestFile('d', 'a')
+        self._CreateTestFile('f', 'a/target', 'contents')
+        self._CreateTestFile('d', 'a/b')
+        self._CreateTestFile('l', 'a/b/c', '..')
+        self.assertAllOsBehaviorsMatch('a/b/c/target')
+
+    @unittest.skipIf(TestCase.is_windows, 'no symlink in Windows')
+    def testSymLinkToSiblingDirectory(self):
+        self._CreateTestFile('d', 'a')
+        self._CreateTestFile('d', 'a/b')
+        self._CreateTestFile('d', 'a/sibling_of_b')
+        self._CreateTestFile('f', 'a/sibling_of_b/target', 'contents')
+        self._CreateTestFile('l', 'a/b/c', '../sibling_of_b')
+        self.assertAllOsBehaviorsMatch('a/b/c/target')
+
+    @unittest.skipIf(TestCase.is_windows, 'no symlink in Windows')
+    def testSymLinkToSiblingDirectoryNonExistantFile(self):
+        self._CreateTestFile('d', 'a')
+        self._CreateTestFile('d', 'a/b')
+        self._CreateTestFile('d', 'a/sibling_of_b')
+        self._CreateTestFile('f', 'a/sibling_of_b/target', 'contents')
+        self._CreateTestFile('l', 'a/b/c', '../sibling_of_b')
+        self.assertAllOsBehaviorsMatch('a/b/c/file_does_not_exist')
+
+    @unittest.skipIf(TestCase.is_windows, 'no symlink in Windows')
+    def testBrokenSymLinkToSiblingDirectory(self):
+        self._CreateTestFile('d', 'a')
+        self._CreateTestFile('d', 'a/b')
+        self._CreateTestFile('d', 'a/sibling_of_b')
+        self._CreateTestFile('f', 'a/sibling_of_b/target', 'contents')
+        self._CreateTestFile('l', 'a/b/c', '../broken_sibling_of_b')
+        self.assertAllOsBehaviorsMatch('a/b/c/target')
+
+    def testRelativePath(self):
+        self._CreateTestFile('d', 'a')
+        self._CreateTestFile('d', 'a/b')
+        self._CreateTestFile('d', 'a/sibling_of_b')
+        self._CreateTestFile('f', 'a/sibling_of_b/target', 'contents')
+        self.assertAllOsBehaviorsMatch('a/b/../sibling_of_b/target')
+
+    def testBrokenRelativePath(self):
+        self._CreateTestFile('d', 'a')
+        self._CreateTestFile('d', 'a/b')
+        self._CreateTestFile('d', 'a/sibling_of_b')
+        self._CreateTestFile('f', 'a/sibling_of_b/target', 'contents')
+        self.assertAllOsBehaviorsMatch('a/b/../broken/target')
+
+    def testBadRelativePath(self):
+        self._CreateTestFile('d', 'a')
+        self._CreateTestFile('f', 'a/target', 'contents')
+        self._CreateTestFile('d', 'a/b')
+        self._CreateTestFile('d', 'a/sibling_of_b')
+        self._CreateTestFile('f', 'a/sibling_of_b/target', 'contents')
+        self.assertAllOsBehaviorsMatch('a/b/../broken/../target')
+
+    def testGetmtimeNonexistantPath(self):
+        self.assertOsPathMethodBehaviorMatches('getmtime', 'no/such/path')
+
+    def testBuiltinOpenModes(self):
+        self._CreateTestFile('f', 'read', 'some contents')
+        self._CreateTestFile('f', 'write', 'some contents')
+        self._CreateTestFile('f', 'append', 'some contents')
+        self.assertFileHandleBehaviorsMatch('read', 'r', 'other contents')
+        self.assertFileHandleBehaviorsMatch('write', 'w', 'other contents')
+        self.assertFileHandleBehaviorsMatch('append', 'a', 'other contents')
+        self._CreateTestFile('f', 'readplus', 'some contents')
+        self._CreateTestFile('f', 'writeplus', 'some contents')
+        self.assertFileHandleBehaviorsMatch('readplus', 'r+', 'other contents')
+        self.assertFileHandleBehaviorsMatch('writeplus', 'w+', 'other contents')
+        self._CreateTestFile('b', 'binaryread', b'some contents')
+        self._CreateTestFile('b', 'binarywrite', b'some contents')
+        self._CreateTestFile('b', 'binaryappend', b'some contents')
+        self.assertFileHandleBehaviorsMatch('binaryread', 'rb', b'other contents')
+        self.assertFileHandleBehaviorsMatch('binarywrite', 'wb', b'other contents')
+        self.assertFileHandleBehaviorsMatch('binaryappend', 'ab', b'other contents')
+        self.assertFileHandleBehaviorsMatch('read', 'rb', 'other contents')
+        self.assertFileHandleBehaviorsMatch('write', 'wb', 'other contents')
+        self.assertFileHandleBehaviorsMatch('append', 'ab', 'other contents')
+
+
+def main(unused_argv):
+    unittest.main()
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/fake_tempfile.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/fake_tempfile.py
new file mode 100644
index 0000000..090425d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/fake_tempfile.py
@@ -0,0 +1,312 @@
+# Copyright 2010 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Fake tempfile module.
+
+Fake implementation of the python2.4.1 tempfile built-in module that works with
+a FakeFilesystem object.
+"""
+#pylint: disable-all
+
+import errno
+import logging
+import os
+import stat
+import tempfile
+
+import fake_filesystem
+
+try:
+  import StringIO as io  # pylint: disable-msg=C6204
+except ImportError:
+  import io  # pylint: disable-msg=C6204
+
+
+class FakeTempfileModule(object):
+  """Uses a FakeFilesystem to provide a mock for the tempfile 2.4.1 module.
+
+  Common usage:
+  filesystem = fake_filesystem.FakeFilesystem()
+  my_tempfile_module = mock_tempfile.FakeTempfileModule(filesystem)
+
+  See also: default keyword arguments for Dependency Injection on
+  http://go/tott-episode-12
+  """
+
+  def __init__(self, filesystem):
+    self._filesystem = filesystem
+    self._tempfile = tempfile
+    self.tempdir = None  # initialized by mktemp(), others
+    self._temp_prefix = 'tmp'
+    self._mktemp_retvals = []
+
+  # pylint: disable-msg=W0622
+  def _TempFilename(self, suffix='', prefix=None, dir=None):
+    """Create a temporary filename that does not exist.
+
+    This is a re-implementation of how tempfile creates random filenames,
+    and is probably different.
+
+    Does not modify self._filesystem, that's your job.
+
+    Output: self.tempdir is initialized if unset
+    Args:
+      suffix: filename suffix
+      prefix: filename prefix
+      dir: dir to put filename in
+    Returns:
+      string, temp filename that does not exist
+    """
+    if dir is None:
+      dir = self._filesystem.JoinPaths(self._filesystem.root.name, 'tmp')
+    filename = None
+    if prefix is None:
+      prefix = self._temp_prefix
+    while not filename or self._filesystem.Exists(filename):
+      # pylint: disable-msg=W0212
+      filename = self._filesystem.JoinPaths(dir, '%s%s%s' % (
+          prefix,
+          next(self._tempfile._RandomNameSequence()),
+          suffix))
+    return filename
+
+  # pylint: disable-msg=W0622,W0613
+  def TemporaryFile(self, mode='w+b', bufsize=-1,
+                    suffix='', prefix=None, dir=None):
+    """Return a file-like object deleted on close().
+
+    Python 2.4.1 tempfile.TemporaryFile.__doc__ =
+    >Return a file (or file-like) object that can be used as a temporary
+    >storage area. The file is created using mkstemp. It will be destroyed as
+    >soon as it is closed (including an implicit close when the object is
+    >garbage collected). Under Unix, the directory entry for the file is
+    >removed immediately after the file is created. Other platforms do not
+    >support this; your code should not rely on a temporary file created using
+    >this function having or not having a visible name in the file system.
+    >
+    >The mode parameter defaults to 'w+b' so that the file created can be read
+    >and written without being closed. Binary mode is used so that it behaves
+    >consistently on all platforms without regard for the data that is stored.
+    >bufsize defaults to -1, meaning that the operating system default is used.
+    >
+    >The dir, prefix and suffix parameters are passed to mkstemp()
+
+    Args:
+      mode: optional string, see above
+      bufsize: optional int, see above
+      suffix: optional string, see above
+      prefix: optional string, see above
+      dir: optional string, see above
+    Returns:
+      a file-like object.
+    """
+    # pylint: disable-msg=C6002
+    # TODO: prefix, suffix, bufsize, dir, mode unused?
+    # cannot be cStringIO due to .name requirement below
+    retval = io.StringIO()
+    retval.name = '<fdopen>'  # as seen on 2.4.3
+    return retval
+
+  # pylint: disable-msg=W0622,W0613
+  def NamedTemporaryFile(self, mode='w+b', bufsize=-1,
+                         suffix='', prefix=None, dir=None, delete=True):
+    """Return a file-like object with name that is deleted on close().
+
+    Python 2.4.1 tempfile.NamedTemporaryFile.__doc__ =
+    >This function operates exactly as TemporaryFile() does, except that
+    >the file is guaranteed to have a visible name in the file system. That
+    >name can be retrieved from the name member of the file object.
+
+    Args:
+      mode: optional string, see above
+      bufsize: optional int, see above
+      suffix: optional string, see above
+      prefix: optional string, see above
+      dir: optional string, see above
+      delete: optional bool, see above
+    Returns:
+      a file-like object including obj.name
+    """
+    # pylint: disable-msg=C6002
+    # TODO: bufsiz unused?
+    temp = self.mkstemp(suffix=suffix, prefix=prefix, dir=dir)
+    filename = temp[1]
+    mock_open = fake_filesystem.FakeFileOpen(
+        self._filesystem, delete_on_close=delete)
+    obj = mock_open(filename, mode)
+    obj.name = filename
+    return obj
+
+  # pylint: disable-msg=C6409
+  def mkstemp(self, suffix='', prefix=None, dir=None, text=False):
+    """Create temp file, returning a 2-tuple: (9999, filename).
+
+    Important: Returns 9999 instead of a real file descriptor!
+
+    Python 2.4.1 tempfile.mkstemp.__doc__ =
+    >mkstemp([suffix, [prefix, [dir, [text]]]])
+    >
+    >User-callable function to create and return a unique temporary file.
+    >The return value is a pair (fd, name) where fd is the file descriptor
+    >returned by os.open, and name is the filename.
+    >
+    >...[snip args]...
+    >
+    >The file is readable and writable only by the creating user ID.
+    >If the operating system uses permission bits to indicate whether
+    >a file is executable, the file is executable by no one. The file
+    >descriptor is not inherited by children of this process.
+    >
+    >Caller is responsible for deleting the file when done with it.
+
+    NOTE: if dir is unspecified, this call creates a directory.
+
+    Output: self.tempdir is initialized if unset
+    Args:
+      suffix: optional string, filename suffix
+      prefix: optional string, filename prefix
+      dir: optional string, directory for temp file; must exist before call
+      text: optional boolean, True = open file in text mode.
+          default False = open file in binary mode.
+    Returns:
+      2-tuple containing
+      [0] = int, file descriptor number for the file object
+      [1] = string, absolute pathname of a file
+    Raises:
+      OSError: when dir= is specified but does not exist
+    """
+    # pylint: disable-msg=C6002
+    # TODO: optional boolean text is unused?
+    # default dir affected by "global"
+    filename = self._TempEntryname(suffix, prefix, dir)
+    fh = self._filesystem.CreateFile(filename, st_mode=stat.S_IFREG|0o600)
+    fd = self._filesystem.AddOpenFile(fh)
+
+    self._mktemp_retvals.append(filename)
+    return (fd, filename)
+
+  # pylint: disable-msg=C6409
+  def mkdtemp(self, suffix='', prefix=None, dir=None):
+    """Create temp directory, returns string, absolute pathname.
+
+    Python 2.4.1 tempfile.mkdtemp.__doc__ =
+    >mkdtemp([suffix[, prefix[, dir]]])
+    >Creates a temporary directory in the most secure manner
+    >possible. [...]
+    >
+    >The user of mkdtemp() is responsible for deleting the temporary
+    >directory and its contents when done with it.
+    > [...]
+    >mkdtemp() returns the absolute pathname of the new directory. [...]
+
+    Args:
+      suffix: optional string, filename suffix
+      prefix: optional string, filename prefix
+      dir: optional string, directory for temp dir. Must exist before call
+    Returns:
+      string, directory name
+    """
+    dirname = self._TempEntryname(suffix, prefix, dir)
+    self._filesystem.CreateDirectory(dirname, perm_bits=0o700)
+
+    self._mktemp_retvals.append(dirname)
+    return dirname
+
+  def _TempEntryname(self, suffix, prefix, dir):
+    """Helper function for mk[ds]temp.
+
+    Args:
+      suffix: string, filename suffix
+      prefix: string, filename prefix
+      dir: string, directory for temp dir. Must exist before call
+    Returns:
+      string, entry name
+    """
+    # default dir affected by "global"
+    if dir is None:
+      call_mkdir = True
+      dir = self.gettempdir()
+    else:
+      call_mkdir = False
+
+    entryname = None
+    while not entryname or self._filesystem.Exists(entryname):
+      entryname = self._TempFilename(suffix=suffix, prefix=prefix, dir=dir)
+    if not call_mkdir:
+      # This is simplistic. A bad input of suffix=/f will cause tempfile
+      # to blow up, but this mock won't.  But that's already a broken
+      # corner case
+      parent_dir = os.path.dirname(entryname)
+      try:
+        self._filesystem.GetObject(parent_dir)
+      except IOError as err:
+        assert 'No such file or directory' in str(err)
+        # python -c 'import tempfile; tempfile.mkstemp(dir="/no/such/dr")'
+        # OSError: [Errno 2] No such file or directory: '/no/such/dr/tmpFBuqjO'
+        raise OSError(
+            errno.ENOENT,
+            'No such directory in mock filesystem',
+            parent_dir)
+    return entryname
+
+  # pylint: disable-msg=C6409
+  def gettempdir(self):
+    """Get default temp dir.  Sets default if unset."""
+    if self.tempdir:
+      return self.tempdir
+    # pylint: disable-msg=C6002
+    # TODO: environment variables TMPDIR TEMP TMP, or other dirs?
+    self.tempdir = '/tmp'
+    return self.tempdir
+
+  # pylint: disable-msg=C6409
+  def gettempprefix(self):
+    """Get temp filename prefix.
+
+    NOTE: This has no effect on py2.4
+
+    Returns:
+      string, prefix to use in temporary filenames
+    """
+    return self._temp_prefix
+
+  # pylint: disable-msg=C6409
+  def mktemp(self, suffix=''):
+    """mktemp is deprecated in 2.4.1, and is thus unimplemented."""
+    raise NotImplementedError
+
+  def _SetTemplate(self, template):
+    """Setter for 'template' property."""
+    self._temp_prefix = template
+    logging.error('tempfile.template= is a NOP in python2.4')
+
+  def __SetTemplate(self, template):
+    """Indirect setter for 'template' property."""
+    self._SetTemplate(template)
+
+  def __DeprecatedTemplate(self):
+    """template property implementation."""
+    raise NotImplementedError
+
+  # reading from template is deprecated, setting is ok.
+  template = property(__DeprecatedTemplate, __SetTemplate,
+                      doc="""Set the prefix for temp filenames""")
+
+  def FakeReturnedMktempValues(self):
+    """For validation purposes, mktemp()'s return values are stored."""
+    return self._mktemp_retvals
+
+  def FakeMktempReset(self):
+    """Clear the stored mktemp() values."""
+    self._mktemp_retvals = []
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/fake_tempfile_test.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/fake_tempfile_test.py
new file mode 100755
index 0000000..e7dcb6c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/fake_tempfile_test.py
@@ -0,0 +1,197 @@
+#! /usr/bin/env python
+#
+# Copyright 2009 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Tests for the fake_tempfile module."""
+
+#pylint: disable-all
+
+import stat
+import sys
+if sys.version_info < (2, 7):
+    import unittest2 as unittest
+else:
+    import unittest
+
+try:
+  import StringIO as io  # pylint: disable-msg=C6204
+except ImportError:
+  import io  # pylint: disable-msg=C6204
+
+import fake_filesystem
+import fake_tempfile
+
+
+class FakeLogging(object):
+  """Fake logging object for testGettempprefix."""
+
+  def __init__(self, test_case):
+    self._message = None
+    self._test_case = test_case
+
+  # pylint: disable-msg=C6409
+  def error(self, message):
+    if self._message is not None:
+      self.FailOnMessage(message)
+    self._message = message
+
+  def FailOnMessage(self, message):
+    self._test_case.fail('Unexpected message received: %s' % message)
+
+  warn = FailOnMessage
+  info = FailOnMessage
+  debug = FailOnMessage
+  fatal = FailOnMessage
+
+  def message(self):
+    return self._message
+
+
+class FakeTempfileModuleTest(unittest.TestCase):
+  """Test the 'tempfile' module mock."""
+
+  def setUp(self):
+    self.filesystem = fake_filesystem.FakeFilesystem(path_separator='/')
+    self.tempfile = fake_tempfile.FakeTempfileModule(self.filesystem)
+    self.orig_logging = fake_tempfile.logging
+    self.fake_logging = FakeLogging(self)
+    fake_tempfile.logging = self.fake_logging
+
+  def tearDown(self):
+    fake_tempfile.logging = self.orig_logging
+
+  def testTempFilename(self):
+    # pylint: disable-msg=C6002
+    # TODO: test that tempdir is init'ed
+    filename_a = self.tempfile._TempFilename()
+    # expect /tmp/tmp######
+    self.assertTrue(filename_a.startswith('/tmp/tmp'))
+    self.assertLess(len('/tmp/tmpA'), len(filename_a))
+
+    # see that random part changes
+    filename_b = self.tempfile._TempFilename()
+    self.assertTrue(filename_b.startswith('/tmp/tmp'))
+    self.assertLess(len('/tmp/tmpB'), len(filename_a))
+    self.assertNotEqual(filename_a, filename_b)
+
+  def testTempFilenameSuffix(self):
+    """test tempfile._TempFilename(suffix=)."""
+    filename = self.tempfile._TempFilename(suffix='.suffix')
+    self.assertTrue(filename.startswith('/tmp/tmp'))
+    self.assertTrue(filename.endswith('.suffix'))
+    self.assertLess(len('/tmp/tmpX.suffix'), len(filename))
+
+  def testTempFilenamePrefix(self):
+    """test tempfile._TempFilename(prefix=)."""
+    filename = self.tempfile._TempFilename(prefix='prefix.')
+    self.assertTrue(filename.startswith('/tmp/prefix.'))
+    self.assertLess(len('/tmp/prefix.X'), len(filename))
+
+  def testTempFilenameDir(self):
+    """test tempfile._TempFilename(dir=)."""
+    filename = self.tempfile._TempFilename(dir='/dir')
+    self.assertTrue(filename.startswith('/dir/tmp'))
+    self.assertLess(len('/dir/tmpX'), len(filename))
+
+  def testTemporaryFile(self):
+    obj = self.tempfile.TemporaryFile()
+    self.assertEqual('<fdopen>', obj.name)
+    self.assertTrue(isinstance(obj, io.StringIO))
+
+  def testNamedTemporaryFile(self):
+    obj = self.tempfile.NamedTemporaryFile()
+    created_filenames = self.tempfile.FakeReturnedMktempValues()
+    self.assertEqual(created_filenames[0], obj.name)
+    self.assertTrue(self.filesystem.GetObject(obj.name))
+    obj.close()
+    self.assertRaises(IOError, self.filesystem.GetObject, obj.name)
+
+  def testNamedTemporaryFileNoDelete(self):
+    obj = self.tempfile.NamedTemporaryFile(delete=False)
+    obj.write(b'foo')
+    obj.close()
+    file_obj = self.filesystem.GetObject(obj.name)
+    self.assertEqual('foo', file_obj.contents)
+    obj = self.tempfile.NamedTemporaryFile(mode='w', delete=False)
+    obj.write('foo')
+    obj.close()
+    file_obj = self.filesystem.GetObject(obj.name)
+    self.assertEqual('foo', file_obj.contents)
+
+  def testMkstemp(self):
+    next_fd = len(self.filesystem.open_files)
+    temporary = self.tempfile.mkstemp()
+    self.assertEqual(2, len(temporary))
+    self.assertTrue(temporary[1].startswith('/tmp/tmp'))
+    created_filenames = self.tempfile.FakeReturnedMktempValues()
+    self.assertEqual(next_fd, temporary[0])
+    self.assertEqual(temporary[1], created_filenames[0])
+    self.assertTrue(self.filesystem.Exists(temporary[1]))
+    self.assertEqual(self.filesystem.GetObject(temporary[1]).st_mode,
+                     stat.S_IFREG|0o600)
+
+  def testMkstempDir(self):
+    """test tempfile.mkstemp(dir=)."""
+    # expect fail: /dir does not exist
+    self.assertRaises(OSError, self.tempfile.mkstemp, dir='/dir')
+    # expect pass: /dir exists
+    self.filesystem.CreateDirectory('/dir')
+    next_fd = len(self.filesystem.open_files)
+    temporary = self.tempfile.mkstemp(dir='/dir')
+    self.assertEqual(2, len(temporary))
+    self.assertEqual(next_fd, temporary[0])
+    self.assertTrue(temporary[1].startswith('/dir/tmp'))
+    created_filenames = self.tempfile.FakeReturnedMktempValues()
+    self.assertEqual(temporary[1], created_filenames[0])
+    self.assertTrue(self.filesystem.Exists(temporary[1]))
+    self.assertEqual(self.filesystem.GetObject(temporary[1]).st_mode,
+                     stat.S_IFREG|0o600)
+    # pylint: disable-msg=C6002
+    # TODO: add a test that /dir is actually writable.
+
+  def testMkdtemp(self):
+    dirname = self.tempfile.mkdtemp()
+    self.assertTrue(dirname)
+    created_filenames = self.tempfile.FakeReturnedMktempValues()
+    self.assertEqual(dirname, created_filenames[0])
+    self.assertTrue(self.filesystem.Exists(dirname))
+    self.assertEqual(self.filesystem.GetObject(dirname).st_mode,
+                     stat.S_IFDIR|0o700)
+
+  def testGettempdir(self):
+    self.assertEqual(None, self.tempfile.tempdir)
+    self.assertEqual('/tmp', self.tempfile.gettempdir())
+    self.assertEqual('/tmp', self.tempfile.tempdir)
+
+  def testGettempprefix(self):
+    """test tempfile.gettempprefix() and the tempfile.template setter."""
+    self.assertEqual('tmp', self.tempfile.gettempprefix())
+    # set and verify
+    self.tempfile.template = 'strung'
+    self.assertEqual('strung', self.tempfile.gettempprefix())
+    self.assertEqual('tempfile.template= is a NOP in python2.4',
+                     self.fake_logging.message())
+
+  def testMktemp(self):
+    self.assertRaises(NotImplementedError, self.tempfile.mktemp)
+
+  def testTemplateGet(self):
+    """verify tempfile.template still unimplemented."""
+    self.assertRaises(NotImplementedError, getattr,
+                      self.tempfile, 'template')
+
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/requirements.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/requirements.txt
new file mode 100644
index 0000000..ddd4287
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/requirements.txt
@@ -0,0 +1 @@
+wheel==0.23.0
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/setup.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/setup.py
new file mode 100644
index 0000000..b1a2f11
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/setup.py
@@ -0,0 +1,88 @@
+#! /usr/bin/env python
+
+# Copyright 2009 Google Inc. All Rights Reserved.
+# Copyright 2014 Altera Corporation. All Rights Reserved.
+# Copyright 2014-2015 John McGehee
+# 
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+# 
+#     http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+from fake_filesystem import __version__
+
+import os
+
+
+NAME = 'pyfakefs'
+MODULES = ['fake_filesystem',
+           'fake_filesystem_glob',
+           'fake_filesystem_shutil',
+           'fake_tempfile',
+           'fake_filesystem_unittest']
+REQUIRES = ['mox3']
+DESCRIPTION = 'Fake file system for testing file operations without touching the real file system.'
+
+URL = "https://github.com/jmcgeheeiv/pyfakefs"
+
+readme = os.path.join(os.path.dirname(__file__), 'README.md')
+LONG_DESCRIPTION = open(readme).read()
+
+CLASSIFIERS = [
+    'Development Status :: 5 - Production/Stable',
+    'Environment :: Console',
+    'Intended Audience :: Developers',
+    'License :: OSI Approved :: Apache Software License',
+    'Programming Language :: Python',
+    'Programming Language :: Python :: 2.6',
+    'Programming Language :: Python :: 2.7',
+    'Programming Language :: Python :: 3.2',
+    'Programming Language :: Python :: 3.3',
+    'Programming Language :: Python :: 3.4',
+    'Operating System :: POSIX',
+    'Operating System :: MacOS',
+    'Operating System :: Microsoft :: Windows',
+    'Topic :: Software Development :: Libraries',
+    'Topic :: Software Development :: Libraries :: Python Modules',
+    'Topic :: Software Development :: Testing',
+    'Topic :: System :: Filesystems',
+]
+
+AUTHOR = 'Google and John McGehee'
+AUTHOR_EMAIL = 'github@johnnado,com'
+KEYWORDS = ("testing test file os shutil glob mocking unittest "
+            "fakes filesystem unit").split(' ')
+
+params = dict(
+    name=NAME,
+    version=__version__,
+    py_modules=MODULES,
+    install_requires=REQUIRES,
+
+    # metadata for upload to PyPI
+    author=AUTHOR,
+    author_email=AUTHOR_EMAIL,
+    description=DESCRIPTION,
+    long_description=LONG_DESCRIPTION,
+    keywords=KEYWORDS,
+    url=URL,
+    classifiers=CLASSIFIERS,
+)
+
+try:
+    from setuptools import setup
+except ImportError:
+    from distutils.core import setup
+else:
+    params['tests_require'] = ['unittest2']
+    params['test_suite'] = 'unittest2.collector'
+
+setup(**params) # pylint: disable = W0142
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/tox.ini b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/tox.ini
new file mode 100644
index 0000000..0da79b7
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyfakefs/pyfakefs/tox.ini
@@ -0,0 +1,8 @@
+[tox]
+envlist=py26,py27,py32,py33,pypy
+
+[testenv]
+commands=python all_tests.py
+
+[testenv:py26]
+deps=unittest2
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/LICENSE.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/LICENSE.txt
new file mode 100644
index 0000000..f604ea3
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/LICENSE.txt
@@ -0,0 +1,61 @@
+Copyright (c) 2001-2013 Chris Liechti <cliechti@gmx.net>;
+All Rights Reserved.
+
+This is the Python license. In short, you can use this product in
+commercial and non-commercial applications, modify it, redistribute it.
+A notification to the author when you use and/or modify it is welcome.
+
+
+TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING THIS SOFTWARE
+===================================================================
+
+LICENSE AGREEMENT
+-----------------
+
+1. This LICENSE AGREEMENT is between the copyright holder of this
+product, and the Individual or Organization ("Licensee") accessing
+and otherwise using this product in source or binary form and its
+associated documentation.
+
+2. Subject to the terms and conditions of this License Agreement,
+the copyright holder hereby grants Licensee a nonexclusive,
+royalty-free, world-wide license to reproduce, analyze, test,
+perform and/or display publicly, prepare derivative works, distribute,
+and otherwise use this product alone or in any derivative version,
+provided, however, that copyright holders License Agreement and
+copyright holders notice of copyright are retained in this product
+alone or in any derivative version prepared by Licensee.
+
+3. In the event Licensee prepares a derivative work that is based on
+or incorporates this product or any part thereof, and wants to make
+the derivative work available to others as provided herein, then
+Licensee hereby agrees to include in any such work a brief summary of
+the changes made to this product.
+
+4. The copyright holder is making this product available to Licensee on
+an "AS IS" basis. THE COPYRIGHT HOLDER MAKES NO REPRESENTATIONS OR
+WARRANTIES, EXPRESS OR IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION,
+THE COPYRIGHT HOLDER MAKES NO AND DISCLAIMS ANY REPRESENTATION OR
+WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR
+THAT THE USE OF THIS PRODUCT WILL NOT INFRINGE ANY THIRD PARTY RIGHTS.
+
+5. THE COPYRIGHT HOLDER SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER
+USERS OF THIS PRODUCT FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL
+DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE
+USING THIS PRODUCT, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE
+POSSIBILITY THEREOF.
+
+6. This License Agreement will automatically terminate upon a material
+breach of its terms and conditions.
+
+7. Nothing in this License Agreement shall be deemed to create any
+relationship of agency, partnership, or joint venture between the
+copyright holder and Licensee. This License Agreement does not grant
+permission to use trademarks or trade names from the copyright holder
+in a trademark sense to endorse or promote products or services of
+Licensee, or any third party.
+
+8. By copying, installing or otherwise using this product, Licensee
+agrees to be bound by the terms and conditions of this License
+Agreement.
+
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/README.chromium b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/README.chromium
new file mode 100644
index 0000000..04593db
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/README.chromium
@@ -0,0 +1,19 @@
+Name: pySerial
+Short Name: pySerial
+URL: https://github.com/pyserial/pyserial
+Version: 2.7
+Date: 2013-10-17
+License: Python
+License File: NOT_SHIPPED
+Security Critical: no
+
+Description:
+Library for Python access for the serial port. Used for communication with
+a Monsoon device, which tunnels serial over USB.
+
+Local Modifications:
+Includes only the serial/ folder and LICENSE.txt.
+Packaging and setup files have not been copied downstream.
+All other files and folders (documentation/, examples/, test/)
+have not been copied downstream.
+linux-product_info.patch has been applied to include the product information as description.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/linux-product_info.patch b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/linux-product_info.patch
new file mode 100644
index 0000000..9f8001a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/linux-product_info.patch
@@ -0,0 +1,19 @@
+Index: serial/tools/list_ports_linux.py
+===================================================================
+--- serial/tools/list_ports_linux.py	(revision 494)
++++ serial/tools/list_ports_linux.py	(working copy)
+@@ -110,6 +110,14 @@
+     sys_dev_path = '/sys/class/tty/%s/device/interface' % (base,)
+     if os.path.exists(sys_dev_path):
+         return read_line(sys_dev_path)
++
++    # USB Product Information
++    sys_dev_path = '/sys/class/tty/%s/device' % (base,)
++    if os.path.exists(sys_dev_path):
++        product_name_file = os.path.dirname(os.path.realpath(sys_dev_path)) + "/product"
++        if os.path.exists(product_name_file):
++            return read_line(product_name_file)
++
+     return base
+ 
+ def hwinfo(device):
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/__init__.py
new file mode 100755
index 0000000..33ae52e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/__init__.py
@@ -0,0 +1,79 @@
+#!/usr/bin/env python 
+
+# portable serial port access with python
+# this is a wrapper module for different platform implementations
+#
+# (C) 2001-2010 Chris Liechti <cliechti@gmx.net>
+# this is distributed under a free software license, see license.txt
+
+VERSION = '2.7'
+
+import sys
+
+if sys.platform == 'cli':
+    from serial.serialcli import *
+else:
+    import os
+    # chose an implementation, depending on os
+    if os.name == 'nt': #sys.platform == 'win32':
+        from serial.serialwin32 import *
+    elif os.name == 'posix':
+        from serial.serialposix import *
+    elif os.name == 'java':
+        from serial.serialjava import *
+    else:
+        raise ImportError("Sorry: no implementation for your platform ('%s') available" % (os.name,))
+
+
+protocol_handler_packages = [
+        'serial.urlhandler',
+        ]
+
+def serial_for_url(url, *args, **kwargs):
+    """\
+    Get an instance of the Serial class, depending on port/url. The port is not
+    opened when the keyword parameter 'do_not_open' is true, by default it
+    is. All other parameters are directly passed to the __init__ method when
+    the port is instantiated.
+
+    The list of package names that is searched for protocol handlers is kept in
+    ``protocol_handler_packages``.
+
+    e.g. we want to support a URL ``foobar://``. A module
+    ``my_handlers.protocol_foobar`` is provided by the user. Then
+    ``protocol_handler_packages.append("my_handlers")`` would extend the search
+    path so that ``serial_for_url("foobar://"))`` would work.
+    """
+    # check remove extra parameter to not confuse the Serial class
+    do_open = 'do_not_open' not in kwargs or not kwargs['do_not_open']
+    if 'do_not_open' in kwargs: del kwargs['do_not_open']
+    # the default is to use the native version
+    klass = Serial   # 'native' implementation
+    # check port type and get class
+    try:
+        url_nocase = url.lower()
+    except AttributeError:
+        # it's not a string, use default
+        pass
+    else:
+        if '://' in url_nocase:
+            protocol = url_nocase.split('://', 1)[0]
+            for package_name in protocol_handler_packages:
+                module_name = '%s.protocol_%s' % (package_name, protocol,)
+                try:
+                    handler_module = __import__(module_name)
+                except ImportError:
+                    pass
+                else:
+                    klass = sys.modules[module_name].Serial
+                    break
+            else:
+                raise ValueError('invalid URL, protocol %r not known' % (protocol,))
+        else:
+            klass = Serial   # 'native' implementation
+    # instantiate and open when desired
+    instance = klass(None, *args, **kwargs)
+    instance.port = url
+    if do_open:
+        instance.open()
+    return instance
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/rfc2217.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/rfc2217.py
new file mode 100644
index 0000000..2012ea7
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/rfc2217.py
@@ -0,0 +1,1323 @@
+#! python
+#
+# Python Serial Port Extension for Win32, Linux, BSD, Jython
+# see __init__.py
+#
+# This module implements a RFC2217 compatible client. RF2217 descibes a
+# protocol to access serial ports over TCP/IP and allows setting the baud rate,
+# modem control lines etc.
+#
+# (C) 2001-2013 Chris Liechti <cliechti@gmx.net>
+# this is distributed under a free software license, see license.txt
+
+# TODO:
+# - setting control line -> answer is not checked (had problems with one of the
+#   severs). consider implementing a compatibility mode flag to make check
+#   conditional
+# - write timeout not implemented at all
+
+##############################################################################
+# observations and issues with servers
+#=============================================================================
+# sredird V2.2.1
+# - http://www.ibiblio.org/pub/Linux/system/serial/   sredird-2.2.2.tar.gz
+# - does not acknowledge SET_CONTROL (RTS/DTR) correctly, always responding
+#   [105 1] instead of the actual value.
+# - SET_BAUDRATE answer contains 4 extra null bytes -> probably for larger
+#   numbers than 2**32?
+# - To get the signature [COM_PORT_OPTION 0] has to be sent.
+# - run a server: while true; do nc -l -p 7000 -c "sredird debug /dev/ttyUSB0 /var/lock/sredir"; done
+#=============================================================================
+# telnetcpcd (untested)
+# - http://ftp.wayne.edu/kermit/sredird/telnetcpcd-1.09.tar.gz
+# - To get the signature [COM_PORT_OPTION] w/o data has to be sent.
+#=============================================================================
+# ser2net
+# - does not negotiate BINARY or COM_PORT_OPTION for his side but at least
+#   acknowledges that the client activates these options
+# - The configuration may be that the server prints a banner. As this client
+#   implementation does a flushInput on connect, this banner is hidden from
+#   the user application.
+# - NOTIFY_MODEMSTATE: the poll interval of the server seems to be one
+#   second.
+# - To get the signature [COM_PORT_OPTION 0] has to be sent.
+# - run a server: run ser2net daemon, in /etc/ser2net.conf:
+#     2000:telnet:0:/dev/ttyS0:9600 remctl banner
+##############################################################################
+
+# How to identify ports? pySerial might want to support other protocols in the
+# future, so lets use an URL scheme.
+# for RFC2217 compliant servers we will use this:
+#    rfc2217://<host>:<port>[/option[/option...]]
+#
+# options:
+# - "debug" print diagnostic messages
+# - "ign_set_control": do not look at the answers to SET_CONTROL
+# - "poll_modem": issue NOTIFY_MODEMSTATE requests when CTS/DTR/RI/CD is read.
+#   Without this option it expects that the server sends notifications
+#   automatically on change (which most servers do and is according to the
+#   RFC).
+# the order of the options is not relevant
+
+from serial.serialutil import *
+import time
+import struct
+import socket
+import threading
+import Queue
+import logging
+
+# port string is expected to be something like this:
+# rfc2217://host:port
+# host may be an IP or including domain, whatever.
+# port is 0...65535
+
+# map log level names to constants. used in fromURL()
+LOGGER_LEVELS = {
+    'debug': logging.DEBUG,
+    'info': logging.INFO,
+    'warning': logging.WARNING,
+    'error': logging.ERROR,
+    }
+
+
+# telnet protocol characters
+IAC  = to_bytes([255]) # Interpret As Command
+DONT = to_bytes([254])
+DO   = to_bytes([253])
+WONT = to_bytes([252])
+WILL = to_bytes([251])
+IAC_DOUBLED = to_bytes([IAC, IAC])
+
+SE  = to_bytes([240])  # Subnegotiation End
+NOP = to_bytes([241])  # No Operation
+DM  = to_bytes([242])  # Data Mark
+BRK = to_bytes([243])  # Break
+IP  = to_bytes([244])  # Interrupt process
+AO  = to_bytes([245])  # Abort output
+AYT = to_bytes([246])  # Are You There
+EC  = to_bytes([247])  # Erase Character
+EL  = to_bytes([248])  # Erase Line
+GA  = to_bytes([249])  # Go Ahead
+SB =  to_bytes([250])  # Subnegotiation Begin
+
+# selected telnet options
+BINARY = to_bytes([0]) # 8-bit data path
+ECHO = to_bytes([1])   # echo
+SGA = to_bytes([3])    # suppress go ahead
+
+# RFC2217
+COM_PORT_OPTION = to_bytes([44])
+
+# Client to Access Server
+SET_BAUDRATE = to_bytes([1])
+SET_DATASIZE = to_bytes([2])
+SET_PARITY = to_bytes([3])
+SET_STOPSIZE = to_bytes([4])
+SET_CONTROL = to_bytes([5])
+NOTIFY_LINESTATE = to_bytes([6])
+NOTIFY_MODEMSTATE = to_bytes([7])
+FLOWCONTROL_SUSPEND = to_bytes([8])
+FLOWCONTROL_RESUME = to_bytes([9])
+SET_LINESTATE_MASK = to_bytes([10])
+SET_MODEMSTATE_MASK = to_bytes([11])
+PURGE_DATA = to_bytes([12])
+
+SERVER_SET_BAUDRATE = to_bytes([101])
+SERVER_SET_DATASIZE = to_bytes([102])
+SERVER_SET_PARITY = to_bytes([103])
+SERVER_SET_STOPSIZE = to_bytes([104])
+SERVER_SET_CONTROL = to_bytes([105])
+SERVER_NOTIFY_LINESTATE = to_bytes([106])
+SERVER_NOTIFY_MODEMSTATE = to_bytes([107])
+SERVER_FLOWCONTROL_SUSPEND = to_bytes([108])
+SERVER_FLOWCONTROL_RESUME = to_bytes([109])
+SERVER_SET_LINESTATE_MASK = to_bytes([110])
+SERVER_SET_MODEMSTATE_MASK = to_bytes([111])
+SERVER_PURGE_DATA = to_bytes([112])
+
+RFC2217_ANSWER_MAP = {
+    SET_BAUDRATE: SERVER_SET_BAUDRATE,
+    SET_DATASIZE: SERVER_SET_DATASIZE,
+    SET_PARITY: SERVER_SET_PARITY,
+    SET_STOPSIZE: SERVER_SET_STOPSIZE,
+    SET_CONTROL: SERVER_SET_CONTROL,
+    NOTIFY_LINESTATE: SERVER_NOTIFY_LINESTATE,
+    NOTIFY_MODEMSTATE: SERVER_NOTIFY_MODEMSTATE,
+    FLOWCONTROL_SUSPEND: SERVER_FLOWCONTROL_SUSPEND,
+    FLOWCONTROL_RESUME: SERVER_FLOWCONTROL_RESUME,
+    SET_LINESTATE_MASK: SERVER_SET_LINESTATE_MASK,
+    SET_MODEMSTATE_MASK: SERVER_SET_MODEMSTATE_MASK,
+    PURGE_DATA: SERVER_PURGE_DATA,
+}
+
+SET_CONTROL_REQ_FLOW_SETTING = to_bytes([0])        # Request Com Port Flow Control Setting (outbound/both)
+SET_CONTROL_USE_NO_FLOW_CONTROL = to_bytes([1])     # Use No Flow Control (outbound/both)
+SET_CONTROL_USE_SW_FLOW_CONTROL = to_bytes([2])     # Use XON/XOFF Flow Control (outbound/both)
+SET_CONTROL_USE_HW_FLOW_CONTROL = to_bytes([3])     # Use HARDWARE Flow Control (outbound/both)
+SET_CONTROL_REQ_BREAK_STATE = to_bytes([4])         # Request BREAK State
+SET_CONTROL_BREAK_ON = to_bytes([5])                # Set BREAK State ON
+SET_CONTROL_BREAK_OFF = to_bytes([6])               # Set BREAK State OFF
+SET_CONTROL_REQ_DTR = to_bytes([7])                 # Request DTR Signal State
+SET_CONTROL_DTR_ON = to_bytes([8])                  # Set DTR Signal State ON
+SET_CONTROL_DTR_OFF = to_bytes([9])                 # Set DTR Signal State OFF
+SET_CONTROL_REQ_RTS = to_bytes([10])                # Request RTS Signal State
+SET_CONTROL_RTS_ON = to_bytes([11])                 # Set RTS Signal State ON
+SET_CONTROL_RTS_OFF = to_bytes([12])                # Set RTS Signal State OFF
+SET_CONTROL_REQ_FLOW_SETTING_IN = to_bytes([13])    # Request Com Port Flow Control Setting (inbound)
+SET_CONTROL_USE_NO_FLOW_CONTROL_IN = to_bytes([14]) # Use No Flow Control (inbound)
+SET_CONTROL_USE_SW_FLOW_CONTOL_IN = to_bytes([15])  # Use XON/XOFF Flow Control (inbound)
+SET_CONTROL_USE_HW_FLOW_CONTOL_IN = to_bytes([16])  # Use HARDWARE Flow Control (inbound)
+SET_CONTROL_USE_DCD_FLOW_CONTROL = to_bytes([17])   # Use DCD Flow Control (outbound/both)
+SET_CONTROL_USE_DTR_FLOW_CONTROL = to_bytes([18])   # Use DTR Flow Control (inbound)
+SET_CONTROL_USE_DSR_FLOW_CONTROL = to_bytes([19])   # Use DSR Flow Control (outbound/both)
+
+LINESTATE_MASK_TIMEOUT = 128                # Time-out Error
+LINESTATE_MASK_SHIFTREG_EMPTY = 64          # Transfer Shift Register Empty
+LINESTATE_MASK_TRANSREG_EMPTY = 32          # Transfer Holding Register Empty
+LINESTATE_MASK_BREAK_DETECT = 16            # Break-detect Error
+LINESTATE_MASK_FRAMING_ERROR = 8            # Framing Error
+LINESTATE_MASK_PARTIY_ERROR = 4             # Parity Error
+LINESTATE_MASK_OVERRUN_ERROR = 2            # Overrun Error
+LINESTATE_MASK_DATA_READY = 1               # Data Ready
+
+MODEMSTATE_MASK_CD = 128                    # Receive Line Signal Detect (also known as Carrier Detect)
+MODEMSTATE_MASK_RI = 64                     # Ring Indicator
+MODEMSTATE_MASK_DSR = 32                    # Data-Set-Ready Signal State
+MODEMSTATE_MASK_CTS = 16                    # Clear-To-Send Signal State
+MODEMSTATE_MASK_CD_CHANGE = 8               # Delta Receive Line Signal Detect
+MODEMSTATE_MASK_RI_CHANGE = 4               # Trailing-edge Ring Detector
+MODEMSTATE_MASK_DSR_CHANGE = 2              # Delta Data-Set-Ready
+MODEMSTATE_MASK_CTS_CHANGE = 1              # Delta Clear-To-Send
+
+PURGE_RECEIVE_BUFFER = to_bytes([1])        # Purge access server receive data buffer
+PURGE_TRANSMIT_BUFFER = to_bytes([2])       # Purge access server transmit data buffer
+PURGE_BOTH_BUFFERS = to_bytes([3])          # Purge both the access server receive data buffer and the access server transmit data buffer
+
+
+RFC2217_PARITY_MAP = {
+    PARITY_NONE: 1,
+    PARITY_ODD: 2,
+    PARITY_EVEN: 3,
+    PARITY_MARK: 4,
+    PARITY_SPACE: 5,
+}
+RFC2217_REVERSE_PARITY_MAP = dict((v,k) for k,v in RFC2217_PARITY_MAP.items())
+
+RFC2217_STOPBIT_MAP = {
+    STOPBITS_ONE: 1,
+    STOPBITS_ONE_POINT_FIVE: 3,
+    STOPBITS_TWO: 2,
+}
+RFC2217_REVERSE_STOPBIT_MAP = dict((v,k) for k,v in RFC2217_STOPBIT_MAP.items())
+
+# Telnet filter states
+M_NORMAL = 0
+M_IAC_SEEN = 1
+M_NEGOTIATE = 2
+
+# TelnetOption and TelnetSubnegotiation states
+REQUESTED = 'REQUESTED'
+ACTIVE = 'ACTIVE'
+INACTIVE = 'INACTIVE'
+REALLY_INACTIVE = 'REALLY_INACTIVE'
+
+class TelnetOption(object):
+    """Manage a single telnet option, keeps track of DO/DONT WILL/WONT."""
+
+    def __init__(self, connection, name, option, send_yes, send_no, ack_yes, ack_no, initial_state, activation_callback=None):
+        """\
+        Initialize option.
+        :param connection: connection used to transmit answers
+        :param name: a readable name for debug outputs
+        :param send_yes: what to send when option is to be enabled.
+        :param send_no: what to send when option is to be disabled.
+        :param ack_yes: what to expect when remote agrees on option.
+        :param ack_no: what to expect when remote disagrees on option.
+        :param initial_state: options initialized with REQUESTED are tried to
+            be enabled on startup. use INACTIVE for all others.
+        """
+        self.connection = connection
+        self.name = name
+        self.option = option
+        self.send_yes = send_yes
+        self.send_no = send_no
+        self.ack_yes = ack_yes
+        self.ack_no = ack_no
+        self.state = initial_state
+        self.active = False
+        self.activation_callback = activation_callback
+
+    def __repr__(self):
+        """String for debug outputs"""
+        return "%s:%s(%s)" % (self.name, self.active, self.state)
+
+    def process_incoming(self, command):
+        """A DO/DONT/WILL/WONT was received for this option, update state and
+        answer when needed."""
+        if command == self.ack_yes:
+            if self.state is REQUESTED:
+                self.state = ACTIVE
+                self.active = True
+                if self.activation_callback is not None:
+                    self.activation_callback()
+            elif self.state is ACTIVE:
+                pass
+            elif self.state is INACTIVE:
+                self.state = ACTIVE
+                self.connection.telnetSendOption(self.send_yes, self.option)
+                self.active = True
+                if self.activation_callback is not None:
+                    self.activation_callback()
+            elif self.state is REALLY_INACTIVE:
+                self.connection.telnetSendOption(self.send_no, self.option)
+            else:
+                raise ValueError('option in illegal state %r' % self)
+        elif command == self.ack_no:
+            if self.state is REQUESTED:
+                self.state = INACTIVE
+                self.active = False
+            elif self.state is ACTIVE:
+                self.state = INACTIVE
+                self.connection.telnetSendOption(self.send_no, self.option)
+                self.active = False
+            elif self.state is INACTIVE:
+                pass
+            elif self.state is REALLY_INACTIVE:
+                pass
+            else:
+                raise ValueError('option in illegal state %r' % self)
+
+
+class TelnetSubnegotiation(object):
+    """\
+    A object to handle subnegotiation of options. In this case actually
+    sub-sub options for RFC 2217. It is used to track com port options.
+    """
+
+    def __init__(self, connection, name, option, ack_option=None):
+        if ack_option is None: ack_option = option
+        self.connection = connection
+        self.name = name
+        self.option = option
+        self.value = None
+        self.ack_option = ack_option
+        self.state = INACTIVE
+
+    def __repr__(self):
+        """String for debug outputs."""
+        return "%s:%s" % (self.name, self.state)
+
+    def set(self, value):
+        """\
+        request a change of the value. a request is sent to the server. if
+        the client needs to know if the change is performed he has to check the
+        state of this object.
+        """
+        self.value = value
+        self.state = REQUESTED
+        self.connection.rfc2217SendSubnegotiation(self.option, self.value)
+        if self.connection.logger:
+            self.connection.logger.debug("SB Requesting %s -> %r" % (self.name, self.value))
+
+    def isReady(self):
+        """\
+        check if answer from server has been received. when server rejects
+        the change, raise a ValueError.
+        """
+        if self.state == REALLY_INACTIVE:
+            raise ValueError("remote rejected value for option %r" % (self.name))
+        return self.state == ACTIVE
+    # add property to have a similar interface as TelnetOption
+    active = property(isReady)
+
+    def wait(self, timeout=3):
+        """\
+        wait until the subnegotiation has been acknowledged or timeout. It
+        can also throw a value error when the answer from the server does not
+        match the value sent.
+        """
+        timeout_time = time.time() + timeout
+        while time.time() < timeout_time:
+            time.sleep(0.05)    # prevent 100% CPU load
+            if self.isReady():
+                break
+        else:
+            raise SerialException("timeout while waiting for option %r" % (self.name))
+
+    def checkAnswer(self, suboption):
+        """\
+        check an incoming subnegotiation block. the parameter already has
+        cut off the header like sub option number and com port option value.
+        """
+        if self.value == suboption[:len(self.value)]:
+            self.state = ACTIVE
+        else:
+            # error propagation done in isReady
+            self.state = REALLY_INACTIVE
+        if self.connection.logger:
+            self.connection.logger.debug("SB Answer %s -> %r -> %s" % (self.name, suboption, self.state))
+
+
+class RFC2217Serial(SerialBase):
+    """Serial port implementation for RFC 2217 remote serial ports."""
+
+    BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+                 9600, 19200, 38400, 57600, 115200)
+
+    def open(self):
+        """\
+        Open port with current settings. This may throw a SerialException
+        if the port cannot be opened.
+        """
+        self.logger = None
+        self._ignore_set_control_answer = False
+        self._poll_modem_state = False
+        self._network_timeout = 3
+        if self._port is None:
+            raise SerialException("Port must be configured before it can be used.")
+        if self._isOpen:
+            raise SerialException("Port is already open.")
+        try:
+            self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+            self._socket.connect(self.fromURL(self.portstr))
+            self._socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
+        except Exception, msg:
+            self._socket = None
+            raise SerialException("Could not open port %s: %s" % (self.portstr, msg))
+
+        self._socket.settimeout(5) # XXX good value?
+
+        # use a thread save queue as buffer. it also simplifies implementing
+        # the read timeout
+        self._read_buffer = Queue.Queue()
+        # to ensure that user writes does not interfere with internal
+        # telnet/rfc2217 options establish a lock
+        self._write_lock = threading.Lock()
+        # name the following separately so that, below, a check can be easily done
+        mandadory_options = [
+            TelnetOption(self, 'we-BINARY', BINARY, WILL, WONT, DO, DONT, INACTIVE),
+            TelnetOption(self, 'we-RFC2217', COM_PORT_OPTION, WILL, WONT, DO, DONT, REQUESTED),
+        ]
+        # all supported telnet options
+        self._telnet_options = [
+            TelnetOption(self, 'ECHO', ECHO, DO, DONT, WILL, WONT, REQUESTED),
+            TelnetOption(self, 'we-SGA', SGA, WILL, WONT, DO, DONT, REQUESTED),
+            TelnetOption(self, 'they-SGA', SGA, DO, DONT, WILL, WONT, REQUESTED),
+            TelnetOption(self, 'they-BINARY', BINARY, DO, DONT, WILL, WONT, INACTIVE),
+            TelnetOption(self, 'they-RFC2217', COM_PORT_OPTION, DO, DONT, WILL, WONT, REQUESTED),
+        ] + mandadory_options
+        # RFC 2217 specific states
+        # COM port settings
+        self._rfc2217_port_settings = {
+            'baudrate': TelnetSubnegotiation(self, 'baudrate', SET_BAUDRATE, SERVER_SET_BAUDRATE),
+            'datasize': TelnetSubnegotiation(self, 'datasize', SET_DATASIZE, SERVER_SET_DATASIZE),
+            'parity':   TelnetSubnegotiation(self, 'parity',   SET_PARITY,   SERVER_SET_PARITY),
+            'stopsize': TelnetSubnegotiation(self, 'stopsize', SET_STOPSIZE, SERVER_SET_STOPSIZE),
+            }
+        # There are more subnegotiation objects, combine all in one dictionary
+        # for easy access
+        self._rfc2217_options = {
+            'purge':    TelnetSubnegotiation(self, 'purge',    PURGE_DATA,   SERVER_PURGE_DATA),
+            'control':  TelnetSubnegotiation(self, 'control',  SET_CONTROL,  SERVER_SET_CONTROL),
+            }
+        self._rfc2217_options.update(self._rfc2217_port_settings)
+        # cache for line and modem states that the server sends to us
+        self._linestate = 0
+        self._modemstate = None
+        self._modemstate_expires = 0
+        # RFC 2217 flow control between server and client
+        self._remote_suspend_flow = False
+
+        self._thread = threading.Thread(target=self._telnetReadLoop)
+        self._thread.setDaemon(True)
+        self._thread.setName('pySerial RFC 2217 reader thread for %s' % (self._port,))
+        self._thread.start()
+
+        # negotiate Telnet/RFC 2217 -> send initial requests
+        for option in self._telnet_options:
+            if option.state is REQUESTED:
+                self.telnetSendOption(option.send_yes, option.option)
+        # now wait until important options are negotiated
+        timeout_time = time.time() + self._network_timeout
+        while time.time() < timeout_time:
+            time.sleep(0.05)    # prevent 100% CPU load
+            if sum(o.active for o in mandadory_options) == len(mandadory_options):
+                break
+        else:
+            raise SerialException("Remote does not seem to support RFC2217 or BINARY mode %r" % mandadory_options)
+        if self.logger:
+            self.logger.info("Negotiated options: %s" % self._telnet_options)
+
+        # fine, go on, set RFC 2271 specific things
+        self._reconfigurePort()
+        # all things set up get, now a clean start
+        self._isOpen = True
+        if not self._rtscts:
+            self.setRTS(True)
+            self.setDTR(True)
+        self.flushInput()
+        self.flushOutput()
+
+    def _reconfigurePort(self):
+        """Set communication parameters on opened port."""
+        if self._socket is None:
+            raise SerialException("Can only operate on open ports")
+
+        # if self._timeout != 0 and self._interCharTimeout is not None:
+            # XXX
+
+        if self._writeTimeout is not None:
+            raise NotImplementedError('writeTimeout is currently not supported')
+            # XXX
+
+        # Setup the connection
+        # to get good performance, all parameter changes are sent first...
+        if not isinstance(self._baudrate, (int, long)) or not 0 < self._baudrate < 2**32:
+            raise ValueError("invalid baudrate: %r" % (self._baudrate))
+        self._rfc2217_port_settings['baudrate'].set(struct.pack('!I', self._baudrate))
+        self._rfc2217_port_settings['datasize'].set(struct.pack('!B', self._bytesize))
+        self._rfc2217_port_settings['parity'].set(struct.pack('!B', RFC2217_PARITY_MAP[self._parity]))
+        self._rfc2217_port_settings['stopsize'].set(struct.pack('!B', RFC2217_STOPBIT_MAP[self._stopbits]))
+
+        # and now wait until parameters are active
+        items = self._rfc2217_port_settings.values()
+        if self.logger:
+            self.logger.debug("Negotiating settings: %s" % (items,))
+        timeout_time = time.time() + self._network_timeout
+        while time.time() < timeout_time:
+            time.sleep(0.05)    # prevent 100% CPU load
+            if sum(o.active for o in items) == len(items):
+                break
+        else:
+            raise SerialException("Remote does not accept parameter change (RFC2217): %r" % items)
+        if self.logger:
+            self.logger.info("Negotiated settings: %s" % (items,))
+
+        if self._rtscts and self._xonxoff:
+            raise ValueError('xonxoff and rtscts together are not supported')
+        elif self._rtscts:
+            self.rfc2217SetControl(SET_CONTROL_USE_HW_FLOW_CONTROL)
+        elif self._xonxoff:
+            self.rfc2217SetControl(SET_CONTROL_USE_SW_FLOW_CONTROL)
+        else:
+            self.rfc2217SetControl(SET_CONTROL_USE_NO_FLOW_CONTROL)
+
+    def close(self):
+        """Close port"""
+        if self._isOpen:
+            if self._socket:
+                try:
+                    self._socket.shutdown(socket.SHUT_RDWR)
+                    self._socket.close()
+                except:
+                    # ignore errors.
+                    pass
+                self._socket = None
+            if self._thread:
+                self._thread.join()
+            self._isOpen = False
+            # in case of quick reconnects, give the server some time
+            time.sleep(0.3)
+
+    def makeDeviceName(self, port):
+        raise SerialException("there is no sensible way to turn numbers into URLs")
+
+    def fromURL(self, url):
+        """extract host and port from an URL string"""
+        if url.lower().startswith("rfc2217://"): url = url[10:]
+        try:
+            # is there a "path" (our options)?
+            if '/' in url:
+                # cut away options
+                url, options = url.split('/', 1)
+                # process options now, directly altering self
+                for option in options.split('/'):
+                    if '=' in option:
+                        option, value = option.split('=', 1)
+                    else:
+                        value = None
+                    if option == 'logging':
+                        logging.basicConfig()   # XXX is that good to call it here?
+                        self.logger = logging.getLogger('pySerial.rfc2217')
+                        self.logger.setLevel(LOGGER_LEVELS[value])
+                        self.logger.debug('enabled logging')
+                    elif option == 'ign_set_control':
+                        self._ignore_set_control_answer = True
+                    elif option == 'poll_modem':
+                        self._poll_modem_state = True
+                    elif option == 'timeout':
+                        self._network_timeout = float(value)
+                    else:
+                        raise ValueError('unknown option: %r' % (option,))
+            # get host and port
+            host, port = url.split(':', 1) # may raise ValueError because of unpacking
+            port = int(port)               # and this if it's not a number
+            if not 0 <= port < 65536: raise ValueError("port not in range 0...65535")
+        except ValueError, e:
+            raise SerialException('expected a string in the form "[rfc2217://]<host>:<port>[/option[/option...]]": %s' % e)
+        return (host, port)
+
+    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
+
+    def inWaiting(self):
+        """Return the number of characters currently in the input buffer."""
+        if not self._isOpen: raise portNotOpenError
+        return self._read_buffer.qsize()
+
+    def read(self, size=1):
+        """\
+        Read size bytes from the serial port. If a timeout is set it may
+        return less characters as requested. With no timeout it will block
+        until the requested number of bytes is read.
+        """
+        if not self._isOpen: raise portNotOpenError
+        data = bytearray()
+        try:
+            while len(data) < size:
+                if self._thread is None:
+                    raise SerialException('connection failed (reader thread died)')
+                data.append(self._read_buffer.get(True, self._timeout))
+        except Queue.Empty: # -> timeout
+            pass
+        return bytes(data)
+
+    def write(self, data):
+        """\
+        Output the given string over the serial port. Can block if the
+        connection is blocked. May raise SerialException if the connection is
+        closed.
+        """
+        if not self._isOpen: raise portNotOpenError
+        self._write_lock.acquire()
+        try:
+            try:
+                self._socket.sendall(to_bytes(data).replace(IAC, IAC_DOUBLED))
+            except socket.error, e:
+                raise SerialException("connection failed (socket error): %s" % e) # XXX what exception if socket connection fails
+        finally:
+            self._write_lock.release()
+        return len(data)
+
+    def flushInput(self):
+        """Clear input buffer, discarding all that is in the buffer."""
+        if not self._isOpen: raise portNotOpenError
+        self.rfc2217SendPurge(PURGE_RECEIVE_BUFFER)
+        # empty read buffer
+        while self._read_buffer.qsize():
+            self._read_buffer.get(False)
+
+    def flushOutput(self):
+        """\
+        Clear output buffer, aborting the current output and
+        discarding all that is in the buffer.
+        """
+        if not self._isOpen: raise portNotOpenError
+        self.rfc2217SendPurge(PURGE_TRANSMIT_BUFFER)
+
+    def sendBreak(self, duration=0.25):
+        """Send break condition. Timed, returns to idle state after given
+        duration."""
+        if not self._isOpen: raise portNotOpenError
+        self.setBreak(True)
+        time.sleep(duration)
+        self.setBreak(False)
+
+    def setBreak(self, level=True):
+        """\
+        Set break: Controls TXD. When active, to transmitting is
+        possible.
+        """
+        if not self._isOpen: raise portNotOpenError
+        if self.logger:
+            self.logger.info('set BREAK to %s' % ('inactive', 'active')[bool(level)])
+        if level:
+            self.rfc2217SetControl(SET_CONTROL_BREAK_ON)
+        else:
+            self.rfc2217SetControl(SET_CONTROL_BREAK_OFF)
+
+    def setRTS(self, level=True):
+        """Set terminal status line: Request To Send."""
+        if not self._isOpen: raise portNotOpenError
+        if self.logger:
+            self.logger.info('set RTS to %s' % ('inactive', 'active')[bool(level)])
+        if level:
+            self.rfc2217SetControl(SET_CONTROL_RTS_ON)
+        else:
+            self.rfc2217SetControl(SET_CONTROL_RTS_OFF)
+
+    def setDTR(self, level=True):
+        """Set terminal status line: Data Terminal Ready."""
+        if not self._isOpen: raise portNotOpenError
+        if self.logger:
+            self.logger.info('set DTR to %s' % ('inactive', 'active')[bool(level)])
+        if level:
+            self.rfc2217SetControl(SET_CONTROL_DTR_ON)
+        else:
+            self.rfc2217SetControl(SET_CONTROL_DTR_OFF)
+
+    def getCTS(self):
+        """Read terminal status line: Clear To Send."""
+        if not self._isOpen: raise portNotOpenError
+        return bool(self.getModemState() & MODEMSTATE_MASK_CTS)
+
+    def getDSR(self):
+        """Read terminal status line: Data Set Ready."""
+        if not self._isOpen: raise portNotOpenError
+        return bool(self.getModemState() & MODEMSTATE_MASK_DSR)
+
+    def getRI(self):
+        """Read terminal status line: Ring Indicator."""
+        if not self._isOpen: raise portNotOpenError
+        return bool(self.getModemState() & MODEMSTATE_MASK_RI)
+
+    def getCD(self):
+        """Read terminal status line: Carrier Detect."""
+        if not self._isOpen: raise portNotOpenError
+        return bool(self.getModemState() & MODEMSTATE_MASK_CD)
+
+    # - - - platform specific - - -
+    # None so far
+
+    # - - - RFC2217 specific - - -
+
+    def _telnetReadLoop(self):
+        """read loop for the socket."""
+        mode = M_NORMAL
+        suboption = None
+        try:
+            while self._socket is not None:
+                try:
+                    data = self._socket.recv(1024)
+                except socket.timeout:
+                    # just need to get out of recv form time to time to check if
+                    # still alive
+                    continue
+                except socket.error, e:
+                    # connection fails -> terminate loop
+                    if self.logger:
+                        self.logger.debug("socket error in reader thread: %s" % (e,))
+                    break
+                if not data: break # lost connection
+                for byte in data:
+                    if mode == M_NORMAL:
+                        # interpret as command or as data
+                        if byte == IAC:
+                            mode = M_IAC_SEEN
+                        else:
+                            # store data in read buffer or sub option buffer
+                            # depending on state
+                            if suboption is not None:
+                                suboption.append(byte)
+                            else:
+                                self._read_buffer.put(byte)
+                    elif mode == M_IAC_SEEN:
+                        if byte == IAC:
+                            # interpret as command doubled -> insert character
+                            # itself
+                            if suboption is not None:
+                                suboption.append(IAC)
+                            else:
+                                self._read_buffer.put(IAC)
+                            mode = M_NORMAL
+                        elif byte == SB:
+                            # sub option start
+                            suboption = bytearray()
+                            mode = M_NORMAL
+                        elif byte == SE:
+                            # sub option end -> process it now
+                            self._telnetProcessSubnegotiation(bytes(suboption))
+                            suboption = None
+                            mode = M_NORMAL
+                        elif byte in (DO, DONT, WILL, WONT):
+                            # negotiation
+                            telnet_command = byte
+                            mode = M_NEGOTIATE
+                        else:
+                            # other telnet commands
+                            self._telnetProcessCommand(byte)
+                            mode = M_NORMAL
+                    elif mode == M_NEGOTIATE: # DO, DONT, WILL, WONT was received, option now following
+                        self._telnetNegotiateOption(telnet_command, byte)
+                        mode = M_NORMAL
+        finally:
+            self._thread = None
+            if self.logger:
+                self.logger.debug("read thread terminated")
+
+    # - incoming telnet commands and options
+
+    def _telnetProcessCommand(self, command):
+        """Process commands other than DO, DONT, WILL, WONT."""
+        # Currently none. RFC2217 only uses negotiation and subnegotiation.
+        if self.logger:
+            self.logger.warning("ignoring Telnet command: %r" % (command,))
+
+    def _telnetNegotiateOption(self, command, option):
+        """Process incoming DO, DONT, WILL, WONT."""
+        # check our registered telnet options and forward command to them
+        # they know themselves if they have to answer or not
+        known = False
+        for item in self._telnet_options:
+            # can have more than one match! as some options are duplicated for
+            # 'us' and 'them'
+            if item.option == option:
+                item.process_incoming(command)
+                known = True
+        if not known:
+            # handle unknown options
+            # only answer to positive requests and deny them
+            if command == WILL or command == DO:
+                self.telnetSendOption((command == WILL and DONT or WONT), option)
+                if self.logger:
+                    self.logger.warning("rejected Telnet option: %r" % (option,))
+
+
+    def _telnetProcessSubnegotiation(self, suboption):
+        """Process subnegotiation, the data between IAC SB and IAC SE."""
+        if suboption[0:1] == COM_PORT_OPTION:
+            if suboption[1:2] == SERVER_NOTIFY_LINESTATE and len(suboption) >= 3:
+                self._linestate = ord(suboption[2:3]) # ensure it is a number
+                if self.logger:
+                    self.logger.info("NOTIFY_LINESTATE: %s" % self._linestate)
+            elif suboption[1:2] == SERVER_NOTIFY_MODEMSTATE and len(suboption) >= 3:
+                self._modemstate = ord(suboption[2:3]) # ensure it is a number
+                if self.logger:
+                    self.logger.info("NOTIFY_MODEMSTATE: %s" % self._modemstate)
+                # update time when we think that a poll would make sense
+                self._modemstate_expires = time.time() + 0.3
+            elif suboption[1:2] == FLOWCONTROL_SUSPEND:
+                self._remote_suspend_flow = True
+            elif suboption[1:2] == FLOWCONTROL_RESUME:
+                self._remote_suspend_flow = False
+            else:
+                for item in self._rfc2217_options.values():
+                    if item.ack_option == suboption[1:2]:
+                        #~ print "processing COM_PORT_OPTION: %r" % list(suboption[1:])
+                        item.checkAnswer(bytes(suboption[2:]))
+                        break
+                else:
+                    if self.logger:
+                        self.logger.warning("ignoring COM_PORT_OPTION: %r" % (suboption,))
+        else:
+            if self.logger:
+                self.logger.warning("ignoring subnegotiation: %r" % (suboption,))
+
+    # - outgoing telnet commands and options
+
+    def _internal_raw_write(self, data):
+        """internal socket write with no data escaping. used to send telnet stuff."""
+        self._write_lock.acquire()
+        try:
+            self._socket.sendall(data)
+        finally:
+            self._write_lock.release()
+
+    def telnetSendOption(self, action, option):
+        """Send DO, DONT, WILL, WONT."""
+        self._internal_raw_write(to_bytes([IAC, action, option]))
+
+    def rfc2217SendSubnegotiation(self, option, value=''):
+        """Subnegotiation of RFC2217 parameters."""
+        value = value.replace(IAC, IAC_DOUBLED)
+        self._internal_raw_write(to_bytes([IAC, SB, COM_PORT_OPTION, option] + list(value) + [IAC, SE]))
+
+    def rfc2217SendPurge(self, value):
+        item = self._rfc2217_options['purge']
+        item.set(value) # transmit desired purge type
+        item.wait(self._network_timeout) # wait for acknowledge from the server
+
+    def rfc2217SetControl(self, value):
+        item = self._rfc2217_options['control']
+        item.set(value) # transmit desired control type
+        if self._ignore_set_control_answer:
+            # answers are ignored when option is set. compatibility mode for
+            # servers that answer, but not the expected one... (or no answer
+            # at all) i.e. sredird
+            time.sleep(0.1)  # this helps getting the unit tests passed
+        else:
+            item.wait(self._network_timeout)  # wait for acknowledge from the server
+
+    def rfc2217FlowServerReady(self):
+        """\
+        check if server is ready to receive data. block for some time when
+        not.
+        """
+        #~ if self._remote_suspend_flow:
+            #~ wait---
+
+    def getModemState(self):
+        """\
+        get last modem state (cached value. if value is "old", request a new
+        one. this cache helps that we don't issue to many requests when e.g. all
+        status lines, one after the other is queried by te user (getCTS, getDSR
+        etc.)
+        """
+        # active modem state polling enabled? is the value fresh enough?
+        if self._poll_modem_state and self._modemstate_expires < time.time():
+            if self.logger:
+                self.logger.debug('polling modem state')
+            # when it is older, request an update
+            self.rfc2217SendSubnegotiation(NOTIFY_MODEMSTATE)
+            timeout_time = time.time() + self._network_timeout
+            while time.time() < timeout_time:
+                time.sleep(0.05)    # prevent 100% CPU load
+                # when expiration time is updated, it means that there is a new
+                # value
+                if self._modemstate_expires > time.time():
+                    if self.logger:
+                        self.logger.warning('poll for modem state failed')
+                    break
+            # even when there is a timeout, do not generate an error just
+            # return the last known value. this way we can support buggy
+            # servers that do not respond to polls, but send automatic
+            # updates.
+        if self._modemstate is not None:
+            if self.logger:
+                self.logger.debug('using cached modem state')
+            return self._modemstate
+        else:
+            # never received a notification from the server
+            raise SerialException("remote sends no NOTIFY_MODEMSTATE")
+
+
+# assemble Serial class with the platform specific implementation and the base
+# for file-like behavior. for Python 2.6 and newer, that provide the new I/O
+# library, derive from io.RawIOBase
+try:
+    import io
+except ImportError:
+    # classic version with our own file-like emulation
+    class Serial(RFC2217Serial, FileLike):
+        pass
+else:
+    # io library present
+    class Serial(RFC2217Serial, io.RawIOBase):
+        pass
+
+
+#############################################################################
+# The following is code that helps implementing an RFC 2217 server.
+
+class PortManager(object):
+    """\
+    This class manages the state of Telnet and RFC 2217. It needs a serial
+    instance and a connection to work with. Connection is expected to implement
+    a (thread safe) write function, that writes the string to the network.
+    """
+
+    def __init__(self, serial_port, connection, logger=None):
+        self.serial = serial_port
+        self.connection = connection
+        self.logger = logger
+        self._client_is_rfc2217 = False
+
+        # filter state machine
+        self.mode = M_NORMAL
+        self.suboption = None
+        self.telnet_command = None
+
+        # states for modem/line control events
+        self.modemstate_mask = 255
+        self.last_modemstate = None
+        self.linstate_mask = 0
+
+        # all supported telnet options
+        self._telnet_options = [
+            TelnetOption(self, 'ECHO', ECHO, WILL, WONT, DO, DONT, REQUESTED),
+            TelnetOption(self, 'we-SGA', SGA, WILL, WONT, DO, DONT, REQUESTED),
+            TelnetOption(self, 'they-SGA', SGA, DO, DONT, WILL, WONT, INACTIVE),
+            TelnetOption(self, 'we-BINARY', BINARY, WILL, WONT, DO, DONT, INACTIVE),
+            TelnetOption(self, 'they-BINARY', BINARY, DO, DONT, WILL, WONT, REQUESTED),
+            TelnetOption(self, 'we-RFC2217', COM_PORT_OPTION, WILL, WONT, DO, DONT, REQUESTED, self._client_ok),
+            TelnetOption(self, 'they-RFC2217', COM_PORT_OPTION, DO, DONT, WILL, WONT, INACTIVE, self._client_ok),
+            ]
+
+        # negotiate Telnet/RFC2217 -> send initial requests
+        if self.logger:
+            self.logger.debug("requesting initial Telnet/RFC 2217 options")
+        for option in self._telnet_options:
+            if option.state is REQUESTED:
+                self.telnetSendOption(option.send_yes, option.option)
+        # issue 1st modem state notification
+
+    def _client_ok(self):
+        """\
+        callback of telnet option. it gets called when option is activated.
+        this one here is used to detect when the client agrees on RFC 2217. a
+        flag is set so that other functions like check_modem_lines know if the
+        client is ok.
+        """
+        # The callback is used for we and they so if one party agrees, we're
+        # already happy. it seems not all servers do the negotiation correctly
+        # and i guess there are incorrect clients too.. so be happy if client
+        # answers one or the other positively.
+        self._client_is_rfc2217 = True
+        if self.logger:
+            self.logger.info("client accepts RFC 2217")
+        # this is to ensure that the client gets a notification, even if there
+        # was no change
+        self.check_modem_lines(force_notification=True)
+
+    # - outgoing telnet commands and options
+
+    def telnetSendOption(self, action, option):
+        """Send DO, DONT, WILL, WONT."""
+        self.connection.write(to_bytes([IAC, action, option]))
+
+    def rfc2217SendSubnegotiation(self, option, value=''):
+        """Subnegotiation of RFC 2217 parameters."""
+        value = value.replace(IAC, IAC_DOUBLED)
+        self.connection.write(to_bytes([IAC, SB, COM_PORT_OPTION, option] + list(value) + [IAC, SE]))
+
+    # - check modem lines, needs to be called periodically from user to
+    # establish polling
+
+    def check_modem_lines(self, force_notification=False):
+        modemstate = (
+            (self.serial.getCTS() and MODEMSTATE_MASK_CTS) |
+            (self.serial.getDSR() and MODEMSTATE_MASK_DSR) |
+            (self.serial.getRI() and MODEMSTATE_MASK_RI) |
+            (self.serial.getCD() and MODEMSTATE_MASK_CD)
+        )
+        # check what has changed
+        deltas = modemstate ^ (self.last_modemstate or 0) # when last is None -> 0
+        if deltas & MODEMSTATE_MASK_CTS:
+            modemstate |= MODEMSTATE_MASK_CTS_CHANGE
+        if deltas & MODEMSTATE_MASK_DSR:
+            modemstate |= MODEMSTATE_MASK_DSR_CHANGE
+        if deltas & MODEMSTATE_MASK_RI:
+            modemstate |= MODEMSTATE_MASK_RI_CHANGE
+        if deltas & MODEMSTATE_MASK_CD:
+            modemstate |= MODEMSTATE_MASK_CD_CHANGE
+        # if new state is different and the mask allows this change, send
+        # notification. suppress notifications when client is not rfc2217
+        if modemstate != self.last_modemstate or force_notification:
+            if (self._client_is_rfc2217 and (modemstate & self.modemstate_mask)) or force_notification:
+                self.rfc2217SendSubnegotiation(
+                    SERVER_NOTIFY_MODEMSTATE,
+                    to_bytes([modemstate & self.modemstate_mask])
+                    )
+                if self.logger:
+                    self.logger.info("NOTIFY_MODEMSTATE: %s" % (modemstate,))
+            # save last state, but forget about deltas.
+            # otherwise it would also notify about changing deltas which is
+            # probably not very useful
+            self.last_modemstate = modemstate & 0xf0
+
+    # - outgoing data escaping
+
+    def escape(self, data):
+        """\
+        this generator function is for the user. all outgoing data has to be
+        properly escaped, so that no IAC character in the data stream messes up
+        the Telnet state machine in the server.
+
+        socket.sendall(escape(data))
+        """
+        for byte in data:
+            if byte == IAC:
+                yield IAC
+                yield IAC
+            else:
+                yield byte
+
+    # - incoming data filter
+
+    def filter(self, data):
+        """\
+        handle a bunch of incoming bytes. this is a generator. it will yield
+        all characters not of interest for Telnet/RFC 2217.
+
+        The idea is that the reader thread pushes data from the socket through
+        this filter:
+
+        for byte in filter(socket.recv(1024)):
+            # do things like CR/LF conversion/whatever
+            # and write data to the serial port
+            serial.write(byte)
+
+        (socket error handling code left as exercise for the reader)
+        """
+        for byte in data:
+            if self.mode == M_NORMAL:
+                # interpret as command or as data
+                if byte == IAC:
+                    self.mode = M_IAC_SEEN
+                else:
+                    # store data in sub option buffer or pass it to our
+                    # consumer depending on state
+                    if self.suboption is not None:
+                        self.suboption.append(byte)
+                    else:
+                        yield byte
+            elif self.mode == M_IAC_SEEN:
+                if byte == IAC:
+                    # interpret as command doubled -> insert character
+                    # itself
+                    if self.suboption is not None:
+                        self.suboption.append(byte)
+                    else:
+                        yield byte
+                    self.mode = M_NORMAL
+                elif byte == SB:
+                    # sub option start
+                    self.suboption = bytearray()
+                    self.mode = M_NORMAL
+                elif byte == SE:
+                    # sub option end -> process it now
+                    self._telnetProcessSubnegotiation(bytes(self.suboption))
+                    self.suboption = None
+                    self.mode = M_NORMAL
+                elif byte in (DO, DONT, WILL, WONT):
+                    # negotiation
+                    self.telnet_command = byte
+                    self.mode = M_NEGOTIATE
+                else:
+                    # other telnet commands
+                    self._telnetProcessCommand(byte)
+                    self.mode = M_NORMAL
+            elif self.mode == M_NEGOTIATE: # DO, DONT, WILL, WONT was received, option now following
+                self._telnetNegotiateOption(self.telnet_command, byte)
+                self.mode = M_NORMAL
+
+    # - incoming telnet commands and options
+
+    def _telnetProcessCommand(self, command):
+        """Process commands other than DO, DONT, WILL, WONT."""
+        # Currently none. RFC2217 only uses negotiation and subnegotiation.
+        if self.logger:
+            self.logger.warning("ignoring Telnet command: %r" % (command,))
+
+    def _telnetNegotiateOption(self, command, option):
+        """Process incoming DO, DONT, WILL, WONT."""
+        # check our registered telnet options and forward command to them
+        # they know themselves if they have to answer or not
+        known = False
+        for item in self._telnet_options:
+            # can have more than one match! as some options are duplicated for
+            # 'us' and 'them'
+            if item.option == option:
+                item.process_incoming(command)
+                known = True
+        if not known:
+            # handle unknown options
+            # only answer to positive requests and deny them
+            if command == WILL or command == DO:
+                self.telnetSendOption((command == WILL and DONT or WONT), option)
+                if self.logger:
+                    self.logger.warning("rejected Telnet option: %r" % (option,))
+
+
+    def _telnetProcessSubnegotiation(self, suboption):
+        """Process subnegotiation, the data between IAC SB and IAC SE."""
+        if suboption[0:1] == COM_PORT_OPTION:
+            if self.logger:
+                self.logger.debug('received COM_PORT_OPTION: %r' % (suboption,))
+            if suboption[1:2] == SET_BAUDRATE:
+                backup = self.serial.baudrate
+                try:
+                    (baudrate,) = struct.unpack("!I", suboption[2:6])
+                    if baudrate != 0:
+                        self.serial.baudrate = baudrate
+                except ValueError, e:
+                    if self.logger:
+                        self.logger.error("failed to set baud rate: %s" % (e,))
+                    self.serial.baudrate = backup
+                else:
+                    if self.logger:
+                        self.logger.info("%s baud rate: %s" % (baudrate and 'set' or 'get', self.serial.baudrate))
+                self.rfc2217SendSubnegotiation(SERVER_SET_BAUDRATE, struct.pack("!I", self.serial.baudrate))
+            elif suboption[1:2] == SET_DATASIZE:
+                backup = self.serial.bytesize
+                try:
+                    (datasize,) = struct.unpack("!B", suboption[2:3])
+                    if datasize != 0:
+                        self.serial.bytesize = datasize
+                except ValueError, e:
+                    if self.logger:
+                        self.logger.error("failed to set data size: %s" % (e,))
+                    self.serial.bytesize = backup
+                else:
+                    if self.logger:
+                        self.logger.info("%s data size: %s" % (datasize and 'set' or 'get', self.serial.bytesize))
+                self.rfc2217SendSubnegotiation(SERVER_SET_DATASIZE, struct.pack("!B", self.serial.bytesize))
+            elif suboption[1:2] == SET_PARITY:
+                backup = self.serial.parity
+                try:
+                    parity = struct.unpack("!B", suboption[2:3])[0]
+                    if parity != 0:
+                            self.serial.parity = RFC2217_REVERSE_PARITY_MAP[parity]
+                except ValueError, e:
+                    if self.logger:
+                        self.logger.error("failed to set parity: %s" % (e,))
+                    self.serial.parity = backup
+                else:
+                    if self.logger:
+                        self.logger.info("%s parity: %s" % (parity and 'set' or 'get', self.serial.parity))
+                self.rfc2217SendSubnegotiation(
+                    SERVER_SET_PARITY,
+                    struct.pack("!B", RFC2217_PARITY_MAP[self.serial.parity])
+                    )
+            elif suboption[1:2] == SET_STOPSIZE:
+                backup = self.serial.stopbits
+                try:
+                    stopbits = struct.unpack("!B", suboption[2:3])[0]
+                    if stopbits != 0:
+                        self.serial.stopbits = RFC2217_REVERSE_STOPBIT_MAP[stopbits]
+                except ValueError, e:
+                    if self.logger:
+                        self.logger.error("failed to set stop bits: %s" % (e,))
+                    self.serial.stopbits = backup
+                else:
+                    if self.logger:
+                        self.logger.info("%s stop bits: %s" % (stopbits and 'set' or 'get', self.serial.stopbits))
+                self.rfc2217SendSubnegotiation(
+                    SERVER_SET_STOPSIZE,
+                    struct.pack("!B", RFC2217_STOPBIT_MAP[self.serial.stopbits])
+                    )
+            elif suboption[1:2] == SET_CONTROL:
+                if suboption[2:3] == SET_CONTROL_REQ_FLOW_SETTING:
+                    if self.serial.xonxoff:
+                        self.rfc2217SendSubnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_SW_FLOW_CONTROL)
+                    elif self.serial.rtscts:
+                        self.rfc2217SendSubnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_HW_FLOW_CONTROL)
+                    else:
+                        self.rfc2217SendSubnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_NO_FLOW_CONTROL)
+                elif suboption[2:3] == SET_CONTROL_USE_NO_FLOW_CONTROL:
+                    self.serial.xonxoff = False
+                    self.serial.rtscts = False
+                    if self.logger:
+                        self.logger.info("changed flow control to None")
+                    self.rfc2217SendSubnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_NO_FLOW_CONTROL)
+                elif suboption[2:3] == SET_CONTROL_USE_SW_FLOW_CONTROL:
+                    self.serial.xonxoff = True
+                    if self.logger:
+                        self.logger.info("changed flow control to XON/XOFF")
+                    self.rfc2217SendSubnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_SW_FLOW_CONTROL)
+                elif suboption[2:3] == SET_CONTROL_USE_HW_FLOW_CONTROL:
+                    self.serial.rtscts = True
+                    if self.logger:
+                        self.logger.info("changed flow control to RTS/CTS")
+                    self.rfc2217SendSubnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_HW_FLOW_CONTROL)
+                elif suboption[2:3] == SET_CONTROL_REQ_BREAK_STATE:
+                    if self.logger:
+                        self.logger.warning("requested break state - not implemented")
+                    pass # XXX needs cached value
+                elif suboption[2:3] == SET_CONTROL_BREAK_ON:
+                    self.serial.setBreak(True)
+                    if self.logger:
+                        self.logger.info("changed BREAK to active")
+                    self.rfc2217SendSubnegotiation(SERVER_SET_CONTROL, SET_CONTROL_BREAK_ON)
+                elif suboption[2:3] == SET_CONTROL_BREAK_OFF:
+                    self.serial.setBreak(False)
+                    if self.logger:
+                        self.logger.info("changed BREAK to inactive")
+                    self.rfc2217SendSubnegotiation(SERVER_SET_CONTROL, SET_CONTROL_BREAK_OFF)
+                elif suboption[2:3] == SET_CONTROL_REQ_DTR:
+                    if self.logger:
+                        self.logger.warning("requested DTR state - not implemented")
+                    pass # XXX needs cached value
+                elif suboption[2:3] == SET_CONTROL_DTR_ON:
+                    self.serial.setDTR(True)
+                    if self.logger:
+                        self.logger.info("changed DTR to active")
+                    self.rfc2217SendSubnegotiation(SERVER_SET_CONTROL, SET_CONTROL_DTR_ON)
+                elif suboption[2:3] == SET_CONTROL_DTR_OFF:
+                    self.serial.setDTR(False)
+                    if self.logger:
+                        self.logger.info("changed DTR to inactive")
+                    self.rfc2217SendSubnegotiation(SERVER_SET_CONTROL, SET_CONTROL_DTR_OFF)
+                elif suboption[2:3] == SET_CONTROL_REQ_RTS:
+                    if self.logger:
+                        self.logger.warning("requested RTS state - not implemented")
+                    pass # XXX needs cached value
+                    #~ self.rfc2217SendSubnegotiation(SERVER_SET_CONTROL, SET_CONTROL_RTS_ON)
+                elif suboption[2:3] == SET_CONTROL_RTS_ON:
+                    self.serial.setRTS(True)
+                    if self.logger:
+                        self.logger.info("changed RTS to active")
+                    self.rfc2217SendSubnegotiation(SERVER_SET_CONTROL, SET_CONTROL_RTS_ON)
+                elif suboption[2:3] == SET_CONTROL_RTS_OFF:
+                    self.serial.setRTS(False)
+                    if self.logger:
+                        self.logger.info("changed RTS to inactive")
+                    self.rfc2217SendSubnegotiation(SERVER_SET_CONTROL, SET_CONTROL_RTS_OFF)
+                #~ elif suboption[2:3] == SET_CONTROL_REQ_FLOW_SETTING_IN:
+                #~ elif suboption[2:3] == SET_CONTROL_USE_NO_FLOW_CONTROL_IN:
+                #~ elif suboption[2:3] == SET_CONTROL_USE_SW_FLOW_CONTOL_IN:
+                #~ elif suboption[2:3] == SET_CONTROL_USE_HW_FLOW_CONTOL_IN:
+                #~ elif suboption[2:3] == SET_CONTROL_USE_DCD_FLOW_CONTROL:
+                #~ elif suboption[2:3] == SET_CONTROL_USE_DTR_FLOW_CONTROL:
+                #~ elif suboption[2:3] == SET_CONTROL_USE_DSR_FLOW_CONTROL:
+            elif suboption[1:2] == NOTIFY_LINESTATE:
+                # client polls for current state
+                self.rfc2217SendSubnegotiation(
+                    SERVER_NOTIFY_LINESTATE,
+                    to_bytes([0])   # sorry, nothing like that implemented
+                    )
+            elif suboption[1:2] == NOTIFY_MODEMSTATE:
+                if self.logger:
+                    self.logger.info("request for modem state")
+                # client polls for current state
+                self.check_modem_lines(force_notification=True)
+            elif suboption[1:2] == FLOWCONTROL_SUSPEND:
+                if self.logger:
+                    self.logger.info("suspend")
+                self._remote_suspend_flow = True
+            elif suboption[1:2] == FLOWCONTROL_RESUME:
+                if self.logger:
+                    self.logger.info("resume")
+                self._remote_suspend_flow = False
+            elif suboption[1:2] == SET_LINESTATE_MASK:
+                self.linstate_mask = ord(suboption[2:3]) # ensure it is a number
+                if self.logger:
+                    self.logger.info("line state mask: 0x%02x" % (self.linstate_mask,))
+            elif suboption[1:2] == SET_MODEMSTATE_MASK:
+                self.modemstate_mask = ord(suboption[2:3]) # ensure it is a number
+                if self.logger:
+                    self.logger.info("modem state mask: 0x%02x" % (self.modemstate_mask,))
+            elif suboption[1:2] == PURGE_DATA:
+                if suboption[2:3] == PURGE_RECEIVE_BUFFER:
+                    self.serial.flushInput()
+                    if self.logger:
+                        self.logger.info("purge in")
+                    self.rfc2217SendSubnegotiation(SERVER_PURGE_DATA, PURGE_RECEIVE_BUFFER)
+                elif suboption[2:3] == PURGE_TRANSMIT_BUFFER:
+                    self.serial.flushOutput()
+                    if self.logger:
+                        self.logger.info("purge out")
+                    self.rfc2217SendSubnegotiation(SERVER_PURGE_DATA, PURGE_TRANSMIT_BUFFER)
+                elif suboption[2:3] == PURGE_BOTH_BUFFERS:
+                    self.serial.flushInput()
+                    self.serial.flushOutput()
+                    if self.logger:
+                        self.logger.info("purge both")
+                    self.rfc2217SendSubnegotiation(SERVER_PURGE_DATA, PURGE_BOTH_BUFFERS)
+                else:
+                    if self.logger:
+                        self.logger.error("undefined PURGE_DATA: %r" % list(suboption[2:]))
+            else:
+                if self.logger:
+                    self.logger.error("undefined COM_PORT_OPTION: %r" % list(suboption[1:]))
+        else:
+            if self.logger:
+                self.logger.warning("unknown subnegotiation: %r" % (suboption,))
+
+
+# simple client test
+if __name__ == '__main__':
+    import sys
+    s = Serial('rfc2217://localhost:7000', 115200)
+    sys.stdout.write('%s\n' % s)
+
+    #~ s.baudrate = 1898
+
+    sys.stdout.write("write...\n")
+    s.write("hello\n")
+    s.flush()
+    sys.stdout.write("read: %s\n" % s.read(5))
+
+    #~ s.baudrate = 19200
+    #~ s.databits = 7
+    s.close()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/serialcli.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/serialcli.py
new file mode 100644
index 0000000..19169a3
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/serialcli.py
@@ -0,0 +1,273 @@
+#! python
+# Python Serial Port Extension for Win32, Linux, BSD, Jython and .NET/Mono
+# serial driver for .NET/Mono (IronPython), .NET >= 2
+# see __init__.py
+#
+# (C) 2008 Chris Liechti <cliechti@gmx.net>
+# this is distributed under a free software license, see license.txt
+
+import clr
+import System
+import System.IO.Ports
+from serial.serialutil import *
+
+
+def device(portnum):
+    """Turn a port number into a device name"""
+    return System.IO.Ports.SerialPort.GetPortNames()[portnum]
+
+
+# must invoke function with byte array, make a helper to convert strings
+# to byte arrays
+sab = System.Array[System.Byte]
+def as_byte_array(string):
+    return sab([ord(x) for x in string])  # XXX will require adaption when run with a 3.x compatible IronPython
+
+class IronSerial(SerialBase):
+    """Serial port implementation for .NET/Mono."""
+
+    BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+                9600, 19200, 38400, 57600, 115200)
+
+    def open(self):
+        """Open port with current settings. This may throw a SerialException
+           if the port cannot be opened."""
+        if self._port is None:
+            raise SerialException("Port must be configured before it can be used.")
+        if self._isOpen:
+            raise SerialException("Port is already open.")
+        try:
+            self._port_handle = System.IO.Ports.SerialPort(self.portstr)
+        except Exception, msg:
+            self._port_handle = None
+            raise SerialException("could not open port %s: %s" % (self.portstr, msg))
+
+        self._reconfigurePort()
+        self._port_handle.Open()
+        self._isOpen = True
+        if not self._rtscts:
+            self.setRTS(True)
+            self.setDTR(True)
+        self.flushInput()
+        self.flushOutput()
+
+    def _reconfigurePort(self):
+        """Set communication parameters on opened port."""
+        if not self._port_handle:
+            raise SerialException("Can only operate on a valid port handle")
+
+        #~ self._port_handle.ReceivedBytesThreshold = 1
+
+        if self._timeout is None:
+            self._port_handle.ReadTimeout = System.IO.Ports.SerialPort.InfiniteTimeout
+        else:
+            self._port_handle.ReadTimeout = int(self._timeout*1000)
+
+        # if self._timeout != 0 and self._interCharTimeout is not None:
+            # timeouts = (int(self._interCharTimeout * 1000),) + timeouts[1:]
+
+        if self._writeTimeout is None:
+            self._port_handle.WriteTimeout = System.IO.Ports.SerialPort.InfiniteTimeout
+        else:
+            self._port_handle.WriteTimeout = int(self._writeTimeout*1000)
+
+
+        # Setup the connection info.
+        try:
+            self._port_handle.BaudRate = self._baudrate
+        except IOError, e:
+            # catch errors from illegal baudrate settings
+            raise ValueError(str(e))
+
+        if self._bytesize == FIVEBITS:
+            self._port_handle.DataBits     = 5
+        elif self._bytesize == SIXBITS:
+            self._port_handle.DataBits     = 6
+        elif self._bytesize == SEVENBITS:
+            self._port_handle.DataBits     = 7
+        elif self._bytesize == EIGHTBITS:
+            self._port_handle.DataBits     = 8
+        else:
+            raise ValueError("Unsupported number of data bits: %r" % self._bytesize)
+
+        if self._parity == PARITY_NONE:
+            self._port_handle.Parity       = getattr(System.IO.Ports.Parity, 'None') # reserved keyword in Py3k
+        elif self._parity == PARITY_EVEN:
+            self._port_handle.Parity       = System.IO.Ports.Parity.Even
+        elif self._parity == PARITY_ODD:
+            self._port_handle.Parity       = System.IO.Ports.Parity.Odd
+        elif self._parity == PARITY_MARK:
+            self._port_handle.Parity       = System.IO.Ports.Parity.Mark
+        elif self._parity == PARITY_SPACE:
+            self._port_handle.Parity       = System.IO.Ports.Parity.Space
+        else:
+            raise ValueError("Unsupported parity mode: %r" % self._parity)
+
+        if self._stopbits == STOPBITS_ONE:
+            self._port_handle.StopBits     = System.IO.Ports.StopBits.One
+        elif self._stopbits == STOPBITS_ONE_POINT_FIVE:
+            self._port_handle.StopBits     = System.IO.Ports.StopBits.OnePointFive
+        elif self._stopbits == STOPBITS_TWO:
+            self._port_handle.StopBits     = System.IO.Ports.StopBits.Two
+        else:
+            raise ValueError("Unsupported number of stop bits: %r" % self._stopbits)
+
+        if self._rtscts and self._xonxoff:
+            self._port_handle.Handshake  = System.IO.Ports.Handshake.RequestToSendXOnXOff
+        elif self._rtscts:
+            self._port_handle.Handshake  = System.IO.Ports.Handshake.RequestToSend
+        elif self._xonxoff:
+            self._port_handle.Handshake  = System.IO.Ports.Handshake.XOnXOff
+        else:
+            self._port_handle.Handshake  = getattr(System.IO.Ports.Handshake, 'None')   # reserved keyword in Py3k
+
+    #~ def __del__(self):
+        #~ self.close()
+
+    def close(self):
+        """Close port"""
+        if self._isOpen:
+            if self._port_handle:
+                try:
+                    self._port_handle.Close()
+                except System.IO.Ports.InvalidOperationException:
+                    # ignore errors. can happen for unplugged USB serial devices
+                    pass
+                self._port_handle = None
+            self._isOpen = False
+
+    def makeDeviceName(self, port):
+        try:
+            return device(port)
+        except TypeError, e:
+            raise SerialException(str(e))
+
+    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
+
+    def inWaiting(self):
+        """Return the number of characters currently in the input buffer."""
+        if not self._port_handle: raise portNotOpenError
+        return self._port_handle.BytesToRead
+
+    def read(self, size=1):
+        """Read size bytes from the serial port. If a timeout is set it may
+           return less characters as requested. With no timeout it will block
+           until the requested number of bytes is read."""
+        if not self._port_handle: raise portNotOpenError
+        # must use single byte reads as this is the only way to read
+        # without applying encodings
+        data = bytearray()
+        while size:
+            try:
+                data.append(self._port_handle.ReadByte())
+            except System.TimeoutException, e:
+                break
+            else:
+                size -= 1
+        return bytes(data)
+
+    def write(self, data):
+        """Output the given string over the serial port."""
+        if not self._port_handle: raise portNotOpenError
+        if not isinstance(data, (bytes, bytearray)):
+            raise TypeError('expected %s or bytearray, got %s' % (bytes, type(data)))
+        try:
+            # must call overloaded method with byte array argument
+            # as this is the only one not applying encodings
+            self._port_handle.Write(as_byte_array(data), 0, len(data))
+        except System.TimeoutException, e:
+            raise writeTimeoutError
+        return len(data)
+
+    def flushInput(self):
+        """Clear input buffer, discarding all that is in the buffer."""
+        if not self._port_handle: raise portNotOpenError
+        self._port_handle.DiscardInBuffer()
+
+    def flushOutput(self):
+        """Clear output buffer, aborting the current output and
+        discarding all that is in the buffer."""
+        if not self._port_handle: raise portNotOpenError
+        self._port_handle.DiscardOutBuffer()
+
+    def sendBreak(self, duration=0.25):
+        """Send break condition. Timed, returns to idle state after given duration."""
+        if not self._port_handle: raise portNotOpenError
+        import time
+        self._port_handle.BreakState = True
+        time.sleep(duration)
+        self._port_handle.BreakState = False
+
+    def setBreak(self, level=True):
+        """Set break: Controls TXD. When active, to transmitting is possible."""
+        if not self._port_handle: raise portNotOpenError
+        self._port_handle.BreakState = bool(level)
+
+    def setRTS(self, level=True):
+        """Set terminal status line: Request To Send"""
+        if not self._port_handle: raise portNotOpenError
+        self._port_handle.RtsEnable = bool(level)
+
+    def setDTR(self, level=True):
+        """Set terminal status line: Data Terminal Ready"""
+        if not self._port_handle: raise portNotOpenError
+        self._port_handle.DtrEnable = bool(level)
+
+    def getCTS(self):
+        """Read terminal status line: Clear To Send"""
+        if not self._port_handle: raise portNotOpenError
+        return self._port_handle.CtsHolding
+
+    def getDSR(self):
+        """Read terminal status line: Data Set Ready"""
+        if not self._port_handle: raise portNotOpenError
+        return self._port_handle.DsrHolding
+
+    def getRI(self):
+        """Read terminal status line: Ring Indicator"""
+        if not self._port_handle: raise portNotOpenError
+        #~ return self._port_handle.XXX
+        return False #XXX an error would be better
+
+    def getCD(self):
+        """Read terminal status line: Carrier Detect"""
+        if not self._port_handle: raise portNotOpenError
+        return self._port_handle.CDHolding
+
+    # - - platform specific - - - -
+    # none
+
+
+# assemble Serial class with the platform specific implementation and the base
+# for file-like behavior. for Python 2.6 and newer, that provide the new I/O
+# library, derive from io.RawIOBase
+try:
+    import io
+except ImportError:
+    # classic version with our own file-like emulation
+    class Serial(IronSerial, FileLike):
+        pass
+else:
+    # io library present
+    class Serial(IronSerial, io.RawIOBase):
+        pass
+
+
+# Nur Testfunktion!!
+if __name__ == '__main__':
+    import sys
+
+    s = Serial(0)
+    sys.stdio.write('%s\n' % s)
+
+    s = Serial()
+    sys.stdio.write('%s\n' % s)
+
+
+    s.baudrate = 19200
+    s.databits = 7
+    s.close()
+    s.port = 0
+    s.open()
+    sys.stdio.write('%s\n' % s)
+
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/serialjava.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/serialjava.py
new file mode 100644
index 0000000..46a78f8
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/serialjava.py
@@ -0,0 +1,262 @@
+#!jython
+#
+# Python Serial Port Extension for Win32, Linux, BSD, Jython
+# module for serial IO for Jython and JavaComm
+# see __init__.py
+#
+# (C) 2002-2008 Chris Liechti <cliechti@gmx.net>
+# this is distributed under a free software license, see license.txt
+
+from serial.serialutil import *
+
+def my_import(name):
+    mod = __import__(name)
+    components = name.split('.')
+    for comp in components[1:]:
+        mod = getattr(mod, comp)
+    return mod
+
+
+def detect_java_comm(names):
+    """try given list of modules and return that imports"""
+    for name in names:
+        try:
+            mod = my_import(name)
+            mod.SerialPort
+            return mod
+        except (ImportError, AttributeError):
+            pass
+    raise ImportError("No Java Communications API implementation found")
+
+
+# Java Communications API implementations
+# http://mho.republika.pl/java/comm/
+
+comm = detect_java_comm([
+    'javax.comm', # Sun/IBM
+    'gnu.io',     # RXTX
+])
+
+
+def device(portnumber):
+    """Turn a port number into a device name"""
+    enum = comm.CommPortIdentifier.getPortIdentifiers()
+    ports = []
+    while enum.hasMoreElements():
+        el = enum.nextElement()
+        if el.getPortType() == comm.CommPortIdentifier.PORT_SERIAL:
+            ports.append(el)
+    return ports[portnumber].getName()
+
+
+class JavaSerial(SerialBase):
+    """Serial port class, implemented with Java Communications API and
+       thus usable with jython and the appropriate java extension."""
+
+    def open(self):
+        """Open port with current settings. This may throw a SerialException
+           if the port cannot be opened."""
+        if self._port is None:
+            raise SerialException("Port must be configured before it can be used.")
+        if self._isOpen:
+            raise SerialException("Port is already open.")
+        if type(self._port) == type(''):      # strings are taken directly
+            portId = comm.CommPortIdentifier.getPortIdentifier(self._port)
+        else:
+            portId = comm.CommPortIdentifier.getPortIdentifier(device(self._port))     # numbers are transformed to a comport id obj
+        try:
+            self.sPort = portId.open("python serial module", 10)
+        except Exception, msg:
+            self.sPort = None
+            raise SerialException("Could not open port: %s" % msg)
+        self._reconfigurePort()
+        self._instream = self.sPort.getInputStream()
+        self._outstream = self.sPort.getOutputStream()
+        self._isOpen = True
+
+    def _reconfigurePort(self):
+        """Set communication parameters on opened port."""
+        if not self.sPort:
+            raise SerialException("Can only operate on a valid port handle")
+
+        self.sPort.enableReceiveTimeout(30)
+        if self._bytesize == FIVEBITS:
+            jdatabits = comm.SerialPort.DATABITS_5
+        elif self._bytesize == SIXBITS:
+            jdatabits = comm.SerialPort.DATABITS_6
+        elif self._bytesize == SEVENBITS:
+            jdatabits = comm.SerialPort.DATABITS_7
+        elif self._bytesize == EIGHTBITS:
+            jdatabits = comm.SerialPort.DATABITS_8
+        else:
+            raise ValueError("unsupported bytesize: %r" % self._bytesize)
+
+        if self._stopbits == STOPBITS_ONE:
+            jstopbits = comm.SerialPort.STOPBITS_1
+        elif stopbits == STOPBITS_ONE_POINT_FIVE:
+            self._jstopbits = comm.SerialPort.STOPBITS_1_5
+        elif self._stopbits == STOPBITS_TWO:
+            jstopbits = comm.SerialPort.STOPBITS_2
+        else:
+            raise ValueError("unsupported number of stopbits: %r" % self._stopbits)
+
+        if self._parity == PARITY_NONE:
+            jparity = comm.SerialPort.PARITY_NONE
+        elif self._parity == PARITY_EVEN:
+            jparity = comm.SerialPort.PARITY_EVEN
+        elif self._parity == PARITY_ODD:
+            jparity = comm.SerialPort.PARITY_ODD
+        elif self._parity == PARITY_MARK:
+            jparity = comm.SerialPort.PARITY_MARK
+        elif self._parity == PARITY_SPACE:
+            jparity = comm.SerialPort.PARITY_SPACE
+        else:
+            raise ValueError("unsupported parity type: %r" % self._parity)
+
+        jflowin = jflowout = 0
+        if self._rtscts:
+            jflowin  |=  comm.SerialPort.FLOWCONTROL_RTSCTS_IN
+            jflowout |=  comm.SerialPort.FLOWCONTROL_RTSCTS_OUT
+        if self._xonxoff:
+            jflowin  |=  comm.SerialPort.FLOWCONTROL_XONXOFF_IN
+            jflowout |=  comm.SerialPort.FLOWCONTROL_XONXOFF_OUT
+
+        self.sPort.setSerialPortParams(self._baudrate, jdatabits, jstopbits, jparity)
+        self.sPort.setFlowControlMode(jflowin | jflowout)
+
+        if self._timeout >= 0:
+            self.sPort.enableReceiveTimeout(self._timeout*1000)
+        else:
+            self.sPort.disableReceiveTimeout()
+
+    def close(self):
+        """Close port"""
+        if self._isOpen:
+            if self.sPort:
+                self._instream.close()
+                self._outstream.close()
+                self.sPort.close()
+                self.sPort = None
+            self._isOpen = False
+
+    def makeDeviceName(self, port):
+        return device(port)
+
+    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
+
+    def inWaiting(self):
+        """Return the number of characters currently in the input buffer."""
+        if not self.sPort: raise portNotOpenError
+        return self._instream.available()
+
+    def read(self, size=1):
+        """Read size bytes from the serial port. If a timeout is set it may
+           return less characters as requested. With no timeout it will block
+           until the requested number of bytes is read."""
+        if not self.sPort: raise portNotOpenError
+        read = bytearray()
+        if size > 0:
+            while len(read) < size:
+                x = self._instream.read()
+                if x == -1:
+                    if self.timeout >= 0:
+                        break
+                else:
+                    read.append(x)
+        return bytes(read)
+
+    def write(self, data):
+        """Output the given string over the serial port."""
+        if not self.sPort: raise portNotOpenError
+        if not isinstance(data, (bytes, bytearray)):
+            raise TypeError('expected %s or bytearray, got %s' % (bytes, type(data)))
+        self._outstream.write(data)
+        return len(data)
+
+    def flushInput(self):
+        """Clear input buffer, discarding all that is in the buffer."""
+        if not self.sPort: raise portNotOpenError
+        self._instream.skip(self._instream.available())
+
+    def flushOutput(self):
+        """Clear output buffer, aborting the current output and
+        discarding all that is in the buffer."""
+        if not self.sPort: raise portNotOpenError
+        self._outstream.flush()
+
+    def sendBreak(self, duration=0.25):
+        """Send break condition. Timed, returns to idle state after given duration."""
+        if not self.sPort: raise portNotOpenError
+        self.sPort.sendBreak(duration*1000.0)
+
+    def setBreak(self, level=1):
+        """Set break: Controls TXD. When active, to transmitting is possible."""
+        if self.fd is None: raise portNotOpenError
+        raise SerialException("The setBreak function is not implemented in java.")
+
+    def setRTS(self, level=1):
+        """Set terminal status line: Request To Send"""
+        if not self.sPort: raise portNotOpenError
+        self.sPort.setRTS(level)
+
+    def setDTR(self, level=1):
+        """Set terminal status line: Data Terminal Ready"""
+        if not self.sPort: raise portNotOpenError
+        self.sPort.setDTR(level)
+
+    def getCTS(self):
+        """Read terminal status line: Clear To Send"""
+        if not self.sPort: raise portNotOpenError
+        self.sPort.isCTS()
+
+    def getDSR(self):
+        """Read terminal status line: Data Set Ready"""
+        if not self.sPort: raise portNotOpenError
+        self.sPort.isDSR()
+
+    def getRI(self):
+        """Read terminal status line: Ring Indicator"""
+        if not self.sPort: raise portNotOpenError
+        self.sPort.isRI()
+
+    def getCD(self):
+        """Read terminal status line: Carrier Detect"""
+        if not self.sPort: raise portNotOpenError
+        self.sPort.isCD()
+
+
+# assemble Serial class with the platform specific implementation and the base
+# for file-like behavior. for Python 2.6 and newer, that provide the new I/O
+# library, derive from io.RawIOBase
+try:
+    import io
+except ImportError:
+    # classic version with our own file-like emulation
+    class Serial(JavaSerial, FileLike):
+        pass
+else:
+    # io library present
+    class Serial(JavaSerial, io.RawIOBase):
+        pass
+
+
+if __name__ == '__main__':
+    s = Serial(0,
+         baudrate=19200,        # baudrate
+         bytesize=EIGHTBITS,    # number of databits
+         parity=PARITY_EVEN,    # enable parity checking
+         stopbits=STOPBITS_ONE, # number of stopbits
+         timeout=3,             # set a timeout value, None for waiting forever
+         xonxoff=0,             # enable software flow control
+         rtscts=0,              # enable RTS/CTS flow control
+    )
+    s.setRTS(1)
+    s.setDTR(1)
+    s.flushInput()
+    s.flushOutput()
+    s.write('hello')
+    sys.stdio.write('%r\n' % s.read(5))
+    sys.stdio.write('%s\n' % s.inWaiting())
+    del s
+
+
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/serialposix.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/serialposix.py
new file mode 100755
index 0000000..b9b4b28
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/serialposix.py
@@ -0,0 +1,703 @@
+#!/usr/bin/env python
+#
+# Python Serial Port Extension for Win32, Linux, BSD, Jython
+# module for serial IO for POSIX compatible systems, like Linux
+# see __init__.py
+#
+# (C) 2001-2010 Chris Liechti <cliechti@gmx.net>
+# this is distributed under a free software license, see license.txt
+#
+# parts based on code from Grant B. Edwards  <grante@visi.com>:
+#  ftp://ftp.visi.com/users/grante/python/PosixSerial.py
+#
+# references: http://www.easysw.com/~mike/serial/serial.html
+
+import sys, os, fcntl, termios, struct, select, errno, time
+from serial.serialutil import *
+
+# Do check the Python version as some constants have moved.
+if (sys.hexversion < 0x020100f0):
+    import TERMIOS
+else:
+    TERMIOS = termios
+
+if (sys.hexversion < 0x020200f0):
+    import FCNTL
+else:
+    FCNTL = fcntl
+
+# try to detect the OS so that a device can be selected...
+# this code block should supply a device() and set_special_baudrate() function
+# for the platform
+plat = sys.platform.lower()
+
+if   plat[:5] == 'linux':    # Linux (confirmed)
+
+    def device(port):
+        return '/dev/ttyS%d' % port
+
+    TCGETS2 = 0x802C542A
+    TCSETS2 = 0x402C542B
+    BOTHER = 0o010000
+
+    def set_special_baudrate(port, baudrate):
+        # right size is 44 on x86_64, allow for some growth
+        import array
+        buf = array.array('i', [0] * 64)
+
+        try:
+            # get serial_struct
+            FCNTL.ioctl(port.fd, TCGETS2, buf)
+            # set custom speed
+            buf[2] &= ~TERMIOS.CBAUD
+            buf[2] |= BOTHER
+            buf[9] = buf[10] = baudrate
+
+            # set serial_struct
+            res = FCNTL.ioctl(port.fd, TCSETS2, buf)
+        except IOError, e:
+            raise ValueError('Failed to set custom baud rate (%s): %s' % (baudrate, e))
+
+    baudrate_constants = {
+        0:       0000000,  # hang up
+        50:      0000001,
+        75:      0000002,
+        110:     0000003,
+        134:     0000004,
+        150:     0000005,
+        200:     0000006,
+        300:     0000007,
+        600:     0000010,
+        1200:    0000011,
+        1800:    0000012,
+        2400:    0000013,
+        4800:    0000014,
+        9600:    0000015,
+        19200:   0000016,
+        38400:   0000017,
+        57600:   0010001,
+        115200:  0010002,
+        230400:  0010003,
+        460800:  0010004,
+        500000:  0010005,
+        576000:  0010006,
+        921600:  0010007,
+        1000000: 0010010,
+        1152000: 0010011,
+        1500000: 0010012,
+        2000000: 0010013,
+        2500000: 0010014,
+        3000000: 0010015,
+        3500000: 0010016,
+        4000000: 0010017
+    }
+
+elif plat == 'cygwin':       # cygwin/win32 (confirmed)
+
+    def device(port):
+        return '/dev/com%d' % (port + 1)
+
+    def set_special_baudrate(port, baudrate):
+        raise ValueError("sorry don't know how to handle non standard baud rate on this platform")
+
+    baudrate_constants = {
+        128000: 0x01003,
+        256000: 0x01005,
+        500000: 0x01007,
+        576000: 0x01008,
+        921600: 0x01009,
+        1000000: 0x0100a,
+        1152000: 0x0100b,
+        1500000: 0x0100c,
+        2000000: 0x0100d,
+        2500000: 0x0100e,
+        3000000: 0x0100f
+    }
+
+elif plat[:7] == 'openbsd':    # OpenBSD
+
+    def device(port):
+        return '/dev/cua%02d' % port
+
+    def set_special_baudrate(port, baudrate):
+        raise ValueError("sorry don't know how to handle non standard baud rate on this platform")
+
+    baudrate_constants = {}
+
+elif plat[:3] == 'bsd' or  \
+    plat[:7] == 'freebsd':
+
+    def device(port):
+        return '/dev/cuad%d' % port
+
+    def set_special_baudrate(port, baudrate):
+        raise ValueError("sorry don't know how to handle non standard baud rate on this platform")
+
+    baudrate_constants = {}
+
+elif plat[:6] == 'darwin':   # OS X
+
+    version = os.uname()[2].split('.')
+    # Tiger or above can support arbitrary serial speeds
+    if int(version[0]) >= 8:
+        def set_special_baudrate(port, baudrate):
+            # use IOKit-specific call to set up high speeds
+            import array, fcntl
+            buf = array.array('i', [baudrate])
+            IOSSIOSPEED = 0x80045402 #_IOW('T', 2, speed_t)
+            fcntl.ioctl(port.fd, IOSSIOSPEED, buf, 1)
+    else: # version < 8
+        def set_special_baudrate(port, baudrate):
+            raise ValueError("baud rate not supported")
+
+    def device(port):
+        return '/dev/cuad%d' % port
+
+    baudrate_constants = {}
+
+
+elif plat[:6] == 'netbsd':   # NetBSD 1.6 testing by Erk
+
+    def device(port):
+        return '/dev/dty%02d' % port
+
+    def set_special_baudrate(port, baudrate):
+        raise ValueError("sorry don't know how to handle non standard baud rate on this platform")
+
+    baudrate_constants = {}
+
+elif plat[:4] == 'irix':     # IRIX (partially tested)
+
+    def device(port):
+        return '/dev/ttyf%d' % (port+1) #XXX different device names depending on flow control
+
+    def set_special_baudrate(port, baudrate):
+        raise ValueError("sorry don't know how to handle non standard baud rate on this platform")
+
+    baudrate_constants = {}
+
+elif plat[:2] == 'hp':       # HP-UX (not tested)
+
+    def device(port):
+        return '/dev/tty%dp0' % (port+1)
+
+    def set_special_baudrate(port, baudrate):
+        raise ValueError("sorry don't know how to handle non standard baud rate on this platform")
+
+    baudrate_constants = {}
+
+elif plat[:5] == 'sunos':    # Solaris/SunOS (confirmed)
+
+    def device(port):
+        return '/dev/tty%c' % (ord('a')+port)
+
+    def set_special_baudrate(port, baudrate):
+        raise ValueError("sorry don't know how to handle non standard baud rate on this platform")
+
+    baudrate_constants = {}
+
+elif plat[:3] == 'aix':      # AIX
+
+    def device(port):
+        return '/dev/tty%d' % (port)
+
+    def set_special_baudrate(port, baudrate):
+        raise ValueError("sorry don't know how to handle non standard baud rate on this platform")
+
+    baudrate_constants = {}
+
+else:
+    # platform detection has failed...
+    sys.stderr.write("""\
+don't know how to number ttys on this system.
+! Use an explicit path (eg /dev/ttyS1) or send this information to
+! the author of this module:
+
+sys.platform = %r
+os.name = %r
+serialposix.py version = %s
+
+also add the device name of the serial port and where the
+counting starts for the first serial port.
+e.g. 'first serial port: /dev/ttyS0'
+and with a bit luck you can get this module running...
+""" % (sys.platform, os.name, VERSION))
+    # no exception, just continue with a brave attempt to build a device name
+    # even if the device name is not correct for the platform it has chances
+    # to work using a string with the real device name as port parameter.
+    def device(portum):
+        return '/dev/ttyS%d' % portnum
+    def set_special_baudrate(port, baudrate):
+        raise SerialException("sorry don't know how to handle non standard baud rate on this platform")
+    baudrate_constants = {}
+    #~ raise Exception, "this module does not run on this platform, sorry."
+
+# whats up with "aix", "beos", ....
+# they should work, just need to know the device names.
+
+
+# load some constants for later use.
+# try to use values from TERMIOS, use defaults from linux otherwise
+TIOCMGET  = hasattr(TERMIOS, 'TIOCMGET') and TERMIOS.TIOCMGET or 0x5415
+TIOCMBIS  = hasattr(TERMIOS, 'TIOCMBIS') and TERMIOS.TIOCMBIS or 0x5416
+TIOCMBIC  = hasattr(TERMIOS, 'TIOCMBIC') and TERMIOS.TIOCMBIC or 0x5417
+TIOCMSET  = hasattr(TERMIOS, 'TIOCMSET') and TERMIOS.TIOCMSET or 0x5418
+
+#TIOCM_LE = hasattr(TERMIOS, 'TIOCM_LE') and TERMIOS.TIOCM_LE or 0x001
+TIOCM_DTR = hasattr(TERMIOS, 'TIOCM_DTR') and TERMIOS.TIOCM_DTR or 0x002
+TIOCM_RTS = hasattr(TERMIOS, 'TIOCM_RTS') and TERMIOS.TIOCM_RTS or 0x004
+#TIOCM_ST = hasattr(TERMIOS, 'TIOCM_ST') and TERMIOS.TIOCM_ST or 0x008
+#TIOCM_SR = hasattr(TERMIOS, 'TIOCM_SR') and TERMIOS.TIOCM_SR or 0x010
+
+TIOCM_CTS = hasattr(TERMIOS, 'TIOCM_CTS') and TERMIOS.TIOCM_CTS or 0x020
+TIOCM_CAR = hasattr(TERMIOS, 'TIOCM_CAR') and TERMIOS.TIOCM_CAR or 0x040
+TIOCM_RNG = hasattr(TERMIOS, 'TIOCM_RNG') and TERMIOS.TIOCM_RNG or 0x080
+TIOCM_DSR = hasattr(TERMIOS, 'TIOCM_DSR') and TERMIOS.TIOCM_DSR or 0x100
+TIOCM_CD  = hasattr(TERMIOS, 'TIOCM_CD') and TERMIOS.TIOCM_CD or TIOCM_CAR
+TIOCM_RI  = hasattr(TERMIOS, 'TIOCM_RI') and TERMIOS.TIOCM_RI or TIOCM_RNG
+#TIOCM_OUT1 = hasattr(TERMIOS, 'TIOCM_OUT1') and TERMIOS.TIOCM_OUT1 or 0x2000
+#TIOCM_OUT2 = hasattr(TERMIOS, 'TIOCM_OUT2') and TERMIOS.TIOCM_OUT2 or 0x4000
+if hasattr(TERMIOS, 'TIOCINQ'):
+    TIOCINQ = TERMIOS.TIOCINQ
+else:
+    TIOCINQ = hasattr(TERMIOS, 'FIONREAD') and TERMIOS.FIONREAD or 0x541B
+TIOCOUTQ   = hasattr(TERMIOS, 'TIOCOUTQ') and TERMIOS.TIOCOUTQ or 0x5411
+
+TIOCM_zero_str = struct.pack('I', 0)
+TIOCM_RTS_str = struct.pack('I', TIOCM_RTS)
+TIOCM_DTR_str = struct.pack('I', TIOCM_DTR)
+
+TIOCSBRK  = hasattr(TERMIOS, 'TIOCSBRK') and TERMIOS.TIOCSBRK or 0x5427
+TIOCCBRK  = hasattr(TERMIOS, 'TIOCCBRK') and TERMIOS.TIOCCBRK or 0x5428
+
+
+class PosixSerial(SerialBase):
+    """Serial port class POSIX implementation. Serial port configuration is 
+    done with termios and fcntl. Runs on Linux and many other Un*x like
+    systems."""
+
+    def open(self):
+        """Open port with current settings. This may throw a SerialException
+           if the port cannot be opened."""
+        if self._port is None:
+            raise SerialException("Port must be configured before it can be used.")
+        if self._isOpen:
+            raise SerialException("Port is already open.")
+        self.fd = None
+        # open
+        try:
+            self.fd = os.open(self.portstr, os.O_RDWR|os.O_NOCTTY|os.O_NONBLOCK)
+        except IOError, msg:
+            self.fd = None
+            raise SerialException(msg.errno, "could not open port %s: %s" % (self._port, msg))
+        #~ fcntl.fcntl(self.fd, FCNTL.F_SETFL, 0)  # set blocking
+
+        try:
+            self._reconfigurePort()
+        except:
+            try:
+                os.close(self.fd)
+            except:
+                # ignore any exception when closing the port
+                # also to keep original exception that happened when setting up
+                pass
+            self.fd = None
+            raise
+        else:
+            self._isOpen = True
+        self.flushInput()
+
+
+    def _reconfigurePort(self):
+        """Set communication parameters on opened port."""
+        if self.fd is None:
+            raise SerialException("Can only operate on a valid file descriptor")
+        custom_baud = None
+
+        vmin = vtime = 0                # timeout is done via select
+        if self._interCharTimeout is not None:
+            vmin = 1
+            vtime = int(self._interCharTimeout * 10)
+        try:
+            orig_attr = termios.tcgetattr(self.fd)
+            iflag, oflag, cflag, lflag, ispeed, ospeed, cc = orig_attr
+        except termios.error, msg:      # if a port is nonexistent but has a /dev file, it'll fail here
+            raise SerialException("Could not configure port: %s" % msg)
+        # set up raw mode / no echo / binary
+        cflag |=  (TERMIOS.CLOCAL|TERMIOS.CREAD)
+        lflag &= ~(TERMIOS.ICANON|TERMIOS.ECHO|TERMIOS.ECHOE|TERMIOS.ECHOK|TERMIOS.ECHONL|
+                     TERMIOS.ISIG|TERMIOS.IEXTEN) #|TERMIOS.ECHOPRT
+        for flag in ('ECHOCTL', 'ECHOKE'): # netbsd workaround for Erk
+            if hasattr(TERMIOS, flag):
+                lflag &= ~getattr(TERMIOS, flag)
+
+        oflag &= ~(TERMIOS.OPOST)
+        iflag &= ~(TERMIOS.INLCR|TERMIOS.IGNCR|TERMIOS.ICRNL|TERMIOS.IGNBRK)
+        if hasattr(TERMIOS, 'IUCLC'):
+            iflag &= ~TERMIOS.IUCLC
+        if hasattr(TERMIOS, 'PARMRK'):
+            iflag &= ~TERMIOS.PARMRK
+
+        # setup baud rate
+        try:
+            ispeed = ospeed = getattr(TERMIOS, 'B%s' % (self._baudrate))
+        except AttributeError:
+            try:
+                ispeed = ospeed = baudrate_constants[self._baudrate]
+            except KeyError:
+                #~ raise ValueError('Invalid baud rate: %r' % self._baudrate)
+                # may need custom baud rate, it isn't in our list.
+                ispeed = ospeed = getattr(TERMIOS, 'B38400')
+                try:
+                    custom_baud = int(self._baudrate) # store for later
+                except ValueError:
+                    raise ValueError('Invalid baud rate: %r' % self._baudrate)
+                else:
+                    if custom_baud < 0:
+                        raise ValueError('Invalid baud rate: %r' % self._baudrate)
+
+        # setup char len
+        cflag &= ~TERMIOS.CSIZE
+        if self._bytesize == 8:
+            cflag |= TERMIOS.CS8
+        elif self._bytesize == 7:
+            cflag |= TERMIOS.CS7
+        elif self._bytesize == 6:
+            cflag |= TERMIOS.CS6
+        elif self._bytesize == 5:
+            cflag |= TERMIOS.CS5
+        else:
+            raise ValueError('Invalid char len: %r' % self._bytesize)
+        # setup stopbits
+        if self._stopbits == STOPBITS_ONE:
+            cflag &= ~(TERMIOS.CSTOPB)
+        elif self._stopbits == STOPBITS_ONE_POINT_FIVE:
+            cflag |=  (TERMIOS.CSTOPB)  # XXX same as TWO.. there is no POSIX support for 1.5
+        elif self._stopbits == STOPBITS_TWO:
+            cflag |=  (TERMIOS.CSTOPB)
+        else:
+            raise ValueError('Invalid stop bit specification: %r' % self._stopbits)
+        # setup parity
+        iflag &= ~(TERMIOS.INPCK|TERMIOS.ISTRIP)
+        if self._parity == PARITY_NONE:
+            cflag &= ~(TERMIOS.PARENB|TERMIOS.PARODD)
+        elif self._parity == PARITY_EVEN:
+            cflag &= ~(TERMIOS.PARODD)
+            cflag |=  (TERMIOS.PARENB)
+        elif self._parity == PARITY_ODD:
+            cflag |=  (TERMIOS.PARENB|TERMIOS.PARODD)
+        else:
+            raise ValueError('Invalid parity: %r' % self._parity)
+        # setup flow control
+        # xonxoff
+        if hasattr(TERMIOS, 'IXANY'):
+            if self._xonxoff:
+                iflag |=  (TERMIOS.IXON|TERMIOS.IXOFF) #|TERMIOS.IXANY)
+            else:
+                iflag &= ~(TERMIOS.IXON|TERMIOS.IXOFF|TERMIOS.IXANY)
+        else:
+            if self._xonxoff:
+                iflag |=  (TERMIOS.IXON|TERMIOS.IXOFF)
+            else:
+                iflag &= ~(TERMIOS.IXON|TERMIOS.IXOFF)
+        # rtscts
+        if hasattr(TERMIOS, 'CRTSCTS'):
+            if self._rtscts:
+                cflag |=  (TERMIOS.CRTSCTS)
+            else:
+                cflag &= ~(TERMIOS.CRTSCTS)
+        elif hasattr(TERMIOS, 'CNEW_RTSCTS'):   # try it with alternate constant name
+            if self._rtscts:
+                cflag |=  (TERMIOS.CNEW_RTSCTS)
+            else:
+                cflag &= ~(TERMIOS.CNEW_RTSCTS)
+        # XXX should there be a warning if setting up rtscts (and xonxoff etc) fails??
+
+        # buffer
+        # vmin "minimal number of characters to be read. = for non blocking"
+        if vmin < 0 or vmin > 255:
+            raise ValueError('Invalid vmin: %r ' % vmin)
+        cc[TERMIOS.VMIN] = vmin
+        # vtime
+        if vtime < 0 or vtime > 255:
+            raise ValueError('Invalid vtime: %r' % vtime)
+        cc[TERMIOS.VTIME] = vtime
+        # activate settings
+        if [iflag, oflag, cflag, lflag, ispeed, ospeed, cc] != orig_attr:
+            termios.tcsetattr(self.fd, TERMIOS.TCSANOW, [iflag, oflag, cflag, lflag, ispeed, ospeed, cc])
+
+        # apply custom baud rate, if any
+        if custom_baud is not None:
+            set_special_baudrate(self, custom_baud)
+
+    def close(self):
+        """Close port"""
+        if self._isOpen:
+            if self.fd is not None:
+                os.close(self.fd)
+                self.fd = None
+            self._isOpen = False
+
+    def makeDeviceName(self, port):
+        return device(port)
+
+    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
+
+    def inWaiting(self):
+        """Return the number of characters currently in the input buffer."""
+        #~ s = fcntl.ioctl(self.fd, TERMIOS.FIONREAD, TIOCM_zero_str)
+        s = fcntl.ioctl(self.fd, TIOCINQ, TIOCM_zero_str)
+        return struct.unpack('I',s)[0]
+
+    # select based implementation, proved to work on many systems
+    def read(self, size=1):
+        """Read size bytes from the serial port. If a timeout is set it may
+           return less characters as requested. With no timeout it will block
+           until the requested number of bytes is read."""
+        if not self._isOpen: raise portNotOpenError
+        read = bytearray()
+        while len(read) < size:
+            try:
+                ready,_,_ = select.select([self.fd],[],[], self._timeout)
+                # If select was used with a timeout, and the timeout occurs, it
+                # returns with empty lists -> thus abort read operation.
+                # For timeout == 0 (non-blocking operation) also abort when there
+                # is nothing to read.
+                if not ready:
+                    break   # timeout
+                buf = os.read(self.fd, size-len(read))
+                # read should always return some data as select reported it was
+                # ready to read when we get to this point.
+                if not buf:
+                    # Disconnected devices, at least on Linux, show the
+                    # behavior that they are always ready to read immediately
+                    # but reading returns nothing.
+                    raise SerialException('device reports readiness to read but returned no data (device disconnected or multiple access on port?)')
+                read.extend(buf)
+            except select.error, e:
+                # ignore EAGAIN errors. all other errors are shown
+                # see also http://www.python.org/dev/peps/pep-3151/#select
+                if e[0] != errno.EAGAIN:
+                    raise SerialException('read failed: %s' % (e,))
+            except OSError, e:
+                # ignore EAGAIN errors. all other errors are shown
+                if e.errno != errno.EAGAIN:
+                    raise SerialException('read failed: %s' % (e,))
+        return bytes(read)
+
+    def write(self, data):
+        """Output the given string over the serial port."""
+        if not self._isOpen: raise portNotOpenError
+        d = to_bytes(data)
+        tx_len = len(d)
+        if self._writeTimeout is not None and self._writeTimeout > 0:
+            timeout = time.time() + self._writeTimeout
+        else:
+            timeout = None
+        while tx_len > 0:
+            try:
+                n = os.write(self.fd, d)
+                if timeout:
+                    # when timeout is set, use select to wait for being ready
+                    # with the time left as timeout
+                    timeleft = timeout - time.time()
+                    if timeleft < 0:
+                        raise writeTimeoutError
+                    _, ready, _ = select.select([], [self.fd], [], timeleft)
+                    if not ready:
+                        raise writeTimeoutError
+                else:
+                    # wait for write operation
+                    _, ready, _ = select.select([], [self.fd], [], None)
+                    if not ready:
+                        raise SerialException('write failed (select)')
+                d = d[n:]
+                tx_len -= n
+            except OSError, v:
+                if v.errno != errno.EAGAIN:
+                    raise SerialException('write failed: %s' % (v,))
+        return len(data)
+
+    def flush(self):
+        """Flush of file like objects. In this case, wait until all data
+           is written."""
+        self.drainOutput()
+
+    def flushInput(self):
+        """Clear input buffer, discarding all that is in the buffer."""
+        if not self._isOpen: raise portNotOpenError
+        termios.tcflush(self.fd, TERMIOS.TCIFLUSH)
+
+    def flushOutput(self):
+        """Clear output buffer, aborting the current output and
+        discarding all that is in the buffer."""
+        if not self._isOpen: raise portNotOpenError
+        termios.tcflush(self.fd, TERMIOS.TCOFLUSH)
+
+    def sendBreak(self, duration=0.25):
+        """Send break condition. Timed, returns to idle state after given duration."""
+        if not self._isOpen: raise portNotOpenError
+        termios.tcsendbreak(self.fd, int(duration/0.25))
+
+    def setBreak(self, level=1):
+        """Set break: Controls TXD. When active, no transmitting is possible."""
+        if self.fd is None: raise portNotOpenError
+        if level:
+            fcntl.ioctl(self.fd, TIOCSBRK)
+        else:
+            fcntl.ioctl(self.fd, TIOCCBRK)
+
+    def setRTS(self, level=1):
+        """Set terminal status line: Request To Send"""
+        if not self._isOpen: raise portNotOpenError
+        if level:
+            fcntl.ioctl(self.fd, TIOCMBIS, TIOCM_RTS_str)
+        else:
+            fcntl.ioctl(self.fd, TIOCMBIC, TIOCM_RTS_str)
+
+    def setDTR(self, level=1):
+        """Set terminal status line: Data Terminal Ready"""
+        if not self._isOpen: raise portNotOpenError
+        if level:
+            fcntl.ioctl(self.fd, TIOCMBIS, TIOCM_DTR_str)
+        else:
+            fcntl.ioctl(self.fd, TIOCMBIC, TIOCM_DTR_str)
+
+    def getCTS(self):
+        """Read terminal status line: Clear To Send"""
+        if not self._isOpen: raise portNotOpenError
+        s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str)
+        return struct.unpack('I',s)[0] & TIOCM_CTS != 0
+
+    def getDSR(self):
+        """Read terminal status line: Data Set Ready"""
+        if not self._isOpen: raise portNotOpenError
+        s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str)
+        return struct.unpack('I',s)[0] & TIOCM_DSR != 0
+
+    def getRI(self):
+        """Read terminal status line: Ring Indicator"""
+        if not self._isOpen: raise portNotOpenError
+        s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str)
+        return struct.unpack('I',s)[0] & TIOCM_RI != 0
+
+    def getCD(self):
+        """Read terminal status line: Carrier Detect"""
+        if not self._isOpen: raise portNotOpenError
+        s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str)
+        return struct.unpack('I',s)[0] & TIOCM_CD != 0
+
+    # - - platform specific - - - -
+
+    def outWaiting(self):
+        """Return the number of characters currently in the output buffer."""
+        #~ s = fcntl.ioctl(self.fd, TERMIOS.FIONREAD, TIOCM_zero_str)
+        s = fcntl.ioctl(self.fd, TIOCOUTQ, TIOCM_zero_str)
+        return struct.unpack('I',s)[0]
+
+    def drainOutput(self):
+        """internal - not portable!"""
+        if not self._isOpen: raise portNotOpenError
+        termios.tcdrain(self.fd)
+
+    def nonblocking(self):
+        """internal - not portable!"""
+        if not self._isOpen: raise portNotOpenError
+        fcntl.fcntl(self.fd, FCNTL.F_SETFL, os.O_NONBLOCK)
+
+    def fileno(self):
+        """\
+        For easier use of the serial port instance with select.
+        WARNING: this function is not portable to different platforms!
+        """
+        if not self._isOpen: raise portNotOpenError
+        return self.fd
+
+    def setXON(self, level=True):
+        """\
+        Manually control flow - when software flow control is enabled.
+        This will send XON (true) and XOFF (false) to the other device.
+        WARNING: this function is not portable to different platforms!
+        """
+        if not self.hComPort: raise portNotOpenError
+        if enable:
+            termios.tcflow(self.fd, TERMIOS.TCION)
+        else:
+            termios.tcflow(self.fd, TERMIOS.TCIOFF)
+
+    def flowControlOut(self, enable):
+        """\
+        Manually control flow of outgoing data - when hardware or software flow
+        control is enabled.
+        WARNING: this function is not portable to different platforms!
+        """
+        if not self._isOpen: raise portNotOpenError
+        if enable:
+            termios.tcflow(self.fd, TERMIOS.TCOON)
+        else:
+            termios.tcflow(self.fd, TERMIOS.TCOOFF)
+
+
+# assemble Serial class with the platform specifc implementation and the base
+# for file-like behavior. for Python 2.6 and newer, that provide the new I/O
+# library, derrive from io.RawIOBase
+try:
+    import io
+except ImportError:
+    # classic version with our own file-like emulation
+    class Serial(PosixSerial, FileLike):
+        pass
+else:
+    # io library present
+    class Serial(PosixSerial, io.RawIOBase):
+        pass
+
+class PosixPollSerial(Serial):
+    """poll based read implementation. not all systems support poll properly.
+    however this one has better handling of errors, such as a device
+    disconnecting while it's in use (e.g. USB-serial unplugged)"""
+
+    def read(self, size=1):
+        """Read size bytes from the serial port. If a timeout is set it may
+           return less characters as requested. With no timeout it will block
+           until the requested number of bytes is read."""
+        if self.fd is None: raise portNotOpenError
+        read = bytearray()
+        poll = select.poll()
+        poll.register(self.fd, select.POLLIN|select.POLLERR|select.POLLHUP|select.POLLNVAL)
+        if size > 0:
+            while len(read) < size:
+                # print "\tread(): size",size, "have", len(read)    #debug
+                # wait until device becomes ready to read (or something fails)
+                for fd, event in poll.poll(self._timeout*1000):
+                    if event & (select.POLLERR|select.POLLHUP|select.POLLNVAL):
+                        raise SerialException('device reports error (poll)')
+                    #  we don't care if it is select.POLLIN or timeout, that's
+                    #  handled below
+                buf = os.read(self.fd, size - len(read))
+                read.extend(buf)
+                if ((self._timeout is not None and self._timeout >= 0) or 
+                    (self._interCharTimeout is not None and self._interCharTimeout > 0)) and not buf:
+                    break   # early abort on timeout
+        return bytes(read)
+
+
+if __name__ == '__main__':
+    s = Serial(0,
+                 baudrate=19200,        # baud rate
+                 bytesize=EIGHTBITS,    # number of data bits
+                 parity=PARITY_EVEN,    # enable parity checking
+                 stopbits=STOPBITS_ONE, # number of stop bits
+                 timeout=3,             # set a timeout value, None for waiting forever
+                 xonxoff=0,             # enable software flow control
+                 rtscts=0,              # enable RTS/CTS flow control
+               )
+    s.setRTS(1)
+    s.setDTR(1)
+    s.flushInput()
+    s.flushOutput()
+    s.write('hello')
+    sys.stdout.write('%r\n' % s.read(5))
+    sys.stdout.write('%s\n' % s.inWaiting())
+    del s
+
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/serialutil.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/serialutil.py
new file mode 100644
index 0000000..f28ece4
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/serialutil.py
@@ -0,0 +1,551 @@
+#! python
+# Python Serial Port Extension for Win32, Linux, BSD, Jython
+# see __init__.py
+#
+# (C) 2001-2010 Chris Liechti <cliechti@gmx.net>
+# this is distributed under a free software license, see license.txt
+
+# compatibility for older Python < 2.6
+try:
+    bytes
+    bytearray
+except (NameError, AttributeError):
+    # Python older than 2.6 do not have these types. Like for Python 2.6 they
+    # should behave like str. For Python older than 3.0 we want to work with
+    # strings anyway, only later versions have a true bytes type.
+    bytes = str
+    # bytearray is a mutable type that is easily turned into an instance of
+    # bytes
+    class bytearray(list):
+        # for bytes(bytearray()) usage
+        def __str__(self): return ''.join(self)
+        def __repr__(self): return 'bytearray(%r)' % ''.join(self)
+        # append automatically converts integers to characters
+        def append(self, item):
+            if isinstance(item, str):
+                list.append(self, item)
+            else:
+                list.append(self, chr(item))
+        # +=
+        def __iadd__(self, other):
+            for byte in other:
+                self.append(byte)
+            return self
+
+        def __getslice__(self, i, j):
+            return bytearray(list.__getslice__(self, i, j))
+
+        def __getitem__(self, item):
+            if isinstance(item, slice):
+                return bytearray(list.__getitem__(self, item))
+            else:
+                return ord(list.__getitem__(self, item))
+
+        def __eq__(self, other):
+            if isinstance(other, basestring):
+                other = bytearray(other)
+            return list.__eq__(self, other)
+
+# ``memoryview`` was introduced in Python 2.7 and ``bytes(some_memoryview)``
+# isn't returning the contents (very unfortunate). Therefore we need special
+# cases and test for it. Ensure that there is a ``memoryview`` object for older
+# Python versions. This is easier than making every test dependent on its
+# existence.
+try:
+    memoryview
+except (NameError, AttributeError):
+    # implementation does not matter as we do not realy use it.
+    # it just must not inherit from something else we might care for.
+    class memoryview:
+        pass
+
+
+# all Python versions prior 3.x convert ``str([17])`` to '[17]' instead of '\x11'
+# so a simple ``bytes(sequence)`` doesn't work for all versions
+def to_bytes(seq):
+    """convert a sequence to a bytes type"""
+    if isinstance(seq, bytes):
+        return seq
+    elif isinstance(seq, bytearray):
+        return bytes(seq)
+    elif isinstance(seq, memoryview):
+        return seq.tobytes()
+    else:
+        b = bytearray()
+        for item in seq:
+            b.append(item)  # this one handles int and str for our emulation and ints for Python 3.x
+        return bytes(b)
+
+# create control bytes
+XON  = to_bytes([17])
+XOFF = to_bytes([19])
+
+CR = to_bytes([13])
+LF = to_bytes([10])
+
+
+PARITY_NONE, PARITY_EVEN, PARITY_ODD, PARITY_MARK, PARITY_SPACE = 'N', 'E', 'O', 'M', 'S'
+STOPBITS_ONE, STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO = (1, 1.5, 2)
+FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITS = (5, 6, 7, 8)
+
+PARITY_NAMES = {
+    PARITY_NONE:  'None',
+    PARITY_EVEN:  'Even',
+    PARITY_ODD:   'Odd',
+    PARITY_MARK:  'Mark',
+    PARITY_SPACE: 'Space',
+}
+
+
+class SerialException(IOError):
+    """Base class for serial port related exceptions."""
+
+
+class SerialTimeoutException(SerialException):
+    """Write timeouts give an exception"""
+
+
+writeTimeoutError = SerialTimeoutException('Write timeout')
+portNotOpenError = SerialException('Attempting to use a port that is not open')
+
+
+class FileLike(object):
+    """An abstract file like class.
+
+    This class implements readline and readlines based on read and
+    writelines based on write.
+    This class is used to provide the above functions for to Serial
+    port objects.
+
+    Note that when the serial port was opened with _NO_ timeout that
+    readline blocks until it sees a newline (or the specified size is
+    reached) and that readlines would never return and therefore
+    refuses to work (it raises an exception in this case)!
+    """
+
+    def __init__(self):
+        self.closed = True
+
+    def close(self):
+        self.closed = True
+
+    # so that ports are closed when objects are discarded
+    def __del__(self):
+        """Destructor.  Calls close()."""
+        # The try/except block is in case this is called at program
+        # exit time, when it's possible that globals have already been
+        # deleted, and then the close() call might fail.  Since
+        # there's nothing we can do about such failures and they annoy
+        # the end users, we suppress the traceback.
+        try:
+            self.close()
+        except:
+            pass
+
+    def writelines(self, sequence):
+        for line in sequence:
+            self.write(line)
+
+    def flush(self):
+        """flush of file like objects"""
+        pass
+
+    # iterator for e.g. "for line in Serial(0): ..." usage
+    def next(self):
+        line = self.readline()
+        if not line: raise StopIteration
+        return line
+
+    def __iter__(self):
+        return self
+
+    def readline(self, size=None, eol=LF):
+        """read a line which is terminated with end-of-line (eol) character
+        ('\n' by default) or until timeout."""
+        leneol = len(eol)
+        line = bytearray()
+        while True:
+            c = self.read(1)
+            if c:
+                line += c
+                if line[-leneol:] == eol:
+                    break
+                if size is not None and len(line) >= size:
+                    break
+            else:
+                break
+        return bytes(line)
+
+    def readlines(self, sizehint=None, eol=LF):
+        """read a list of lines, until timeout.
+        sizehint is ignored."""
+        if self.timeout is None:
+            raise ValueError("Serial port MUST have enabled timeout for this function!")
+        leneol = len(eol)
+        lines = []
+        while True:
+            line = self.readline(eol=eol)
+            if line:
+                lines.append(line)
+                if line[-leneol:] != eol:    # was the line received with a timeout?
+                    break
+            else:
+                break
+        return lines
+
+    def xreadlines(self, sizehint=None):
+        """Read lines, implemented as generator. It will raise StopIteration on
+        timeout (empty read). sizehint is ignored."""
+        while True:
+            line = self.readline()
+            if not line: break
+            yield line
+
+    # other functions of file-likes - not used by pySerial
+
+    #~ readinto(b)
+
+    def seek(self, pos, whence=0):
+        raise IOError("file is not seekable")
+
+    def tell(self):
+        raise IOError("file is not seekable")
+
+    def truncate(self, n=None):
+        raise IOError("file is not seekable")
+
+    def isatty(self):
+        return False
+
+
+class SerialBase(object):
+    """Serial port base class. Provides __init__ function and properties to
+       get/set port settings."""
+
+    # default values, may be overridden in subclasses that do not support all values
+    BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+                 9600, 19200, 38400, 57600, 115200, 230400, 460800, 500000,
+                 576000, 921600, 1000000, 1152000, 1500000, 2000000, 2500000,
+                 3000000, 3500000, 4000000)
+    BYTESIZES = (FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITS)
+    PARITIES  = (PARITY_NONE, PARITY_EVEN, PARITY_ODD, PARITY_MARK, PARITY_SPACE)
+    STOPBITS  = (STOPBITS_ONE, STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO)
+
+    def __init__(self,
+                 port = None,           # number of device, numbering starts at
+                                        # zero. if everything fails, the user
+                                        # can specify a device string, note
+                                        # that this isn't portable anymore
+                                        # port will be opened if one is specified
+                 baudrate=9600,         # baud rate
+                 bytesize=EIGHTBITS,    # number of data bits
+                 parity=PARITY_NONE,    # enable parity checking
+                 stopbits=STOPBITS_ONE, # number of stop bits
+                 timeout=None,          # set a timeout value, None to wait forever
+                 xonxoff=False,         # enable software flow control
+                 rtscts=False,          # enable RTS/CTS flow control
+                 writeTimeout=None,     # set a timeout for writes
+                 dsrdtr=False,          # None: use rtscts setting, dsrdtr override if True or False
+                 interCharTimeout=None  # Inter-character timeout, None to disable
+                 ):
+        """Initialize comm port object. If a port is given, then the port will be
+           opened immediately. Otherwise a Serial port object in closed state
+           is returned."""
+
+        self._isOpen   = False
+        self._port     = None           # correct value is assigned below through properties
+        self._baudrate = None           # correct value is assigned below through properties
+        self._bytesize = None           # correct value is assigned below through properties
+        self._parity   = None           # correct value is assigned below through properties
+        self._stopbits = None           # correct value is assigned below through properties
+        self._timeout  = None           # correct value is assigned below through properties
+        self._writeTimeout = None       # correct value is assigned below through properties
+        self._xonxoff  = None           # correct value is assigned below through properties
+        self._rtscts   = None           # correct value is assigned below through properties
+        self._dsrdtr   = None           # correct value is assigned below through properties
+        self._interCharTimeout = None   # correct value is assigned below through properties
+
+        # assign values using get/set methods using the properties feature
+        self.port     = port
+        self.baudrate = baudrate
+        self.bytesize = bytesize
+        self.parity   = parity
+        self.stopbits = stopbits
+        self.timeout  = timeout
+        self.writeTimeout = writeTimeout
+        self.xonxoff  = xonxoff
+        self.rtscts   = rtscts
+        self.dsrdtr   = dsrdtr
+        self.interCharTimeout = interCharTimeout
+
+        if port is not None:
+            self.open()
+
+    def isOpen(self):
+        """Check if the port is opened."""
+        return self._isOpen
+
+    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
+
+    # TODO: these are not really needed as the is the BAUDRATES etc. attribute...
+    # maybe i remove them before the final release...
+
+    def getSupportedBaudrates(self):
+        return [(str(b), b) for b in self.BAUDRATES]
+
+    def getSupportedByteSizes(self):
+        return [(str(b), b) for b in self.BYTESIZES]
+
+    def getSupportedStopbits(self):
+        return [(str(b), b) for b in self.STOPBITS]
+
+    def getSupportedParities(self):
+        return [(PARITY_NAMES[b], b) for b in self.PARITIES]
+
+    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
+
+    def setPort(self, port):
+        """Change the port. The attribute portstr is set to a string that
+           contains the name of the port."""
+
+        was_open = self._isOpen
+        if was_open: self.close()
+        if port is not None:
+            if isinstance(port, basestring):
+                self.portstr = port
+            else:
+                self.portstr = self.makeDeviceName(port)
+        else:
+            self.portstr = None
+        self._port = port
+        self.name = self.portstr
+        if was_open: self.open()
+
+    def getPort(self):
+        """Get the current port setting. The value that was passed on init or using
+           setPort() is passed back. See also the attribute portstr which contains
+           the name of the port as a string."""
+        return self._port
+
+    port = property(getPort, setPort, doc="Port setting")
+
+
+    def setBaudrate(self, baudrate):
+        """Change baud rate. It raises a ValueError if the port is open and the
+        baud rate is not possible. If the port is closed, then the value is
+        accepted and the exception is raised when the port is opened."""
+        try:
+            b = int(baudrate)
+        except TypeError:
+            raise ValueError("Not a valid baudrate: %r" % (baudrate,))
+        else:
+            if b <= 0:
+                raise ValueError("Not a valid baudrate: %r" % (baudrate,))
+            self._baudrate = b
+            if self._isOpen:  self._reconfigurePort()
+
+    def getBaudrate(self):
+        """Get the current baud rate setting."""
+        return self._baudrate
+
+    baudrate = property(getBaudrate, setBaudrate, doc="Baud rate setting")
+
+
+    def setByteSize(self, bytesize):
+        """Change byte size."""
+        if bytesize not in self.BYTESIZES: raise ValueError("Not a valid byte size: %r" % (bytesize,))
+        self._bytesize = bytesize
+        if self._isOpen: self._reconfigurePort()
+
+    def getByteSize(self):
+        """Get the current byte size setting."""
+        return self._bytesize
+
+    bytesize = property(getByteSize, setByteSize, doc="Byte size setting")
+
+
+    def setParity(self, parity):
+        """Change parity setting."""
+        if parity not in self.PARITIES: raise ValueError("Not a valid parity: %r" % (parity,))
+        self._parity = parity
+        if self._isOpen: self._reconfigurePort()
+
+    def getParity(self):
+        """Get the current parity setting."""
+        return self._parity
+
+    parity = property(getParity, setParity, doc="Parity setting")
+
+
+    def setStopbits(self, stopbits):
+        """Change stop bits size."""
+        if stopbits not in self.STOPBITS: raise ValueError("Not a valid stop bit size: %r" % (stopbits,))
+        self._stopbits = stopbits
+        if self._isOpen: self._reconfigurePort()
+
+    def getStopbits(self):
+        """Get the current stop bits setting."""
+        return self._stopbits
+
+    stopbits = property(getStopbits, setStopbits, doc="Stop bits setting")
+
+
+    def setTimeout(self, timeout):
+        """Change timeout setting."""
+        if timeout is not None:
+            try:
+                timeout + 1     # test if it's a number, will throw a TypeError if not...
+            except TypeError:
+                raise ValueError("Not a valid timeout: %r" % (timeout,))
+            if timeout < 0: raise ValueError("Not a valid timeout: %r" % (timeout,))
+        self._timeout = timeout
+        if self._isOpen: self._reconfigurePort()
+
+    def getTimeout(self):
+        """Get the current timeout setting."""
+        return self._timeout
+
+    timeout = property(getTimeout, setTimeout, doc="Timeout setting for read()")
+
+
+    def setWriteTimeout(self, timeout):
+        """Change timeout setting."""
+        if timeout is not None:
+            if timeout < 0: raise ValueError("Not a valid timeout: %r" % (timeout,))
+            try:
+                timeout + 1     #test if it's a number, will throw a TypeError if not...
+            except TypeError:
+                raise ValueError("Not a valid timeout: %r" % timeout)
+
+        self._writeTimeout = timeout
+        if self._isOpen: self._reconfigurePort()
+
+    def getWriteTimeout(self):
+        """Get the current timeout setting."""
+        return self._writeTimeout
+
+    writeTimeout = property(getWriteTimeout, setWriteTimeout, doc="Timeout setting for write()")
+
+
+    def setXonXoff(self, xonxoff):
+        """Change XON/XOFF setting."""
+        self._xonxoff = xonxoff
+        if self._isOpen: self._reconfigurePort()
+
+    def getXonXoff(self):
+        """Get the current XON/XOFF setting."""
+        return self._xonxoff
+
+    xonxoff = property(getXonXoff, setXonXoff, doc="XON/XOFF setting")
+
+    def setRtsCts(self, rtscts):
+        """Change RTS/CTS flow control setting."""
+        self._rtscts = rtscts
+        if self._isOpen: self._reconfigurePort()
+
+    def getRtsCts(self):
+        """Get the current RTS/CTS flow control setting."""
+        return self._rtscts
+
+    rtscts = property(getRtsCts, setRtsCts, doc="RTS/CTS flow control setting")
+
+    def setDsrDtr(self, dsrdtr=None):
+        """Change DsrDtr flow control setting."""
+        if dsrdtr is None:
+            # if not set, keep backwards compatibility and follow rtscts setting
+            self._dsrdtr = self._rtscts
+        else:
+            # if defined independently, follow its value
+            self._dsrdtr = dsrdtr
+        if self._isOpen: self._reconfigurePort()
+
+    def getDsrDtr(self):
+        """Get the current DSR/DTR flow control setting."""
+        return self._dsrdtr
+
+    dsrdtr = property(getDsrDtr, setDsrDtr, "DSR/DTR flow control setting")
+
+    def setInterCharTimeout(self, interCharTimeout):
+        """Change inter-character timeout setting."""
+        if interCharTimeout is not None:
+            if interCharTimeout < 0: raise ValueError("Not a valid timeout: %r" % interCharTimeout)
+            try:
+                interCharTimeout + 1     # test if it's a number, will throw a TypeError if not...
+            except TypeError:
+                raise ValueError("Not a valid timeout: %r" % interCharTimeout)
+
+        self._interCharTimeout = interCharTimeout
+        if self._isOpen: self._reconfigurePort()
+
+    def getInterCharTimeout(self):
+        """Get the current inter-character timeout setting."""
+        return self._interCharTimeout
+
+    interCharTimeout = property(getInterCharTimeout, setInterCharTimeout, doc="Inter-character timeout setting for read()")
+
+    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
+
+    _SETTINGS = ('baudrate', 'bytesize', 'parity', 'stopbits', 'xonxoff',
+            'dsrdtr', 'rtscts', 'timeout', 'writeTimeout', 'interCharTimeout')
+
+    def getSettingsDict(self):
+        """Get current port settings as a dictionary. For use with
+        applySettingsDict"""
+        return dict([(key, getattr(self, '_'+key)) for key in self._SETTINGS])
+
+    def applySettingsDict(self, d):
+        """apply stored settings from a dictionary returned from
+        getSettingsDict. it's allowed to delete keys from the dictionary. these
+        values will simply left unchanged."""
+        for key in self._SETTINGS:
+            if d[key] != getattr(self, '_'+key):   # check against internal "_" value
+                setattr(self, key, d[key])          # set non "_" value to use properties write function
+
+    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
+
+    def __repr__(self):
+        """String representation of the current port settings and its state."""
+        return "%s<id=0x%x, open=%s>(port=%r, baudrate=%r, bytesize=%r, parity=%r, stopbits=%r, timeout=%r, xonxoff=%r, rtscts=%r, dsrdtr=%r)" % (
+            self.__class__.__name__,
+            id(self),
+            self._isOpen,
+            self.portstr,
+            self.baudrate,
+            self.bytesize,
+            self.parity,
+            self.stopbits,
+            self.timeout,
+            self.xonxoff,
+            self.rtscts,
+            self.dsrdtr,
+        )
+
+
+    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
+    # compatibility with io library
+
+    def readable(self): return True
+    def writable(self): return True
+    def seekable(self): return False
+    def readinto(self, b):
+        data = self.read(len(b))
+        n = len(data)
+        try:
+            b[:n] = data
+        except TypeError, err:
+            import array
+            if not isinstance(b, array.array):
+                raise err
+            b[:n] = array.array('b', data)
+        return n
+
+
+if __name__ == '__main__':
+    import sys
+    s = SerialBase()
+    sys.stdout.write('port name:  %s\n' % s.portstr)
+    sys.stdout.write('baud rates: %s\n' % s.getSupportedBaudrates())
+    sys.stdout.write('byte sizes: %s\n' % s.getSupportedByteSizes())
+    sys.stdout.write('parities:   %s\n' % s.getSupportedParities())
+    sys.stdout.write('stop bits:  %s\n' % s.getSupportedStopbits())
+    sys.stdout.write('%s\n' % s)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/serialwin32.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/serialwin32.py
new file mode 100644
index 0000000..dfdd953
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/serialwin32.py
@@ -0,0 +1,461 @@
+#! python
+# Python Serial Port Extension for Win32, Linux, BSD, Jython
+# serial driver for win32
+# see __init__.py
+#
+# (C) 2001-2011 Chris Liechti <cliechti@gmx.net>
+# this is distributed under a free software license, see license.txt
+#
+# Initial patch to use ctypes by Giovanni Bajo <rasky@develer.com>
+
+import ctypes
+from serial import win32
+
+from serial.serialutil import *
+
+
+def device(portnum):
+    """Turn a port number into a device name"""
+    return 'COM%d' % (portnum+1) # numbers are transformed to a string
+
+
+class Win32Serial(SerialBase):
+    """Serial port implementation for Win32 based on ctypes."""
+
+    BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+                 9600, 19200, 38400, 57600, 115200)
+
+    def __init__(self, *args, **kwargs):
+        self.hComPort = None
+        self._overlappedRead = None
+        self._overlappedWrite = None
+        self._rtsToggle = False
+
+        self._rtsState = win32.RTS_CONTROL_ENABLE
+        self._dtrState = win32.DTR_CONTROL_ENABLE
+
+
+        SerialBase.__init__(self, *args, **kwargs)
+
+    def open(self):
+        """Open port with current settings. This may throw a SerialException
+           if the port cannot be opened."""
+        if self._port is None:
+            raise SerialException("Port must be configured before it can be used.")
+        if self._isOpen:
+            raise SerialException("Port is already open.")
+        # the "\\.\COMx" format is required for devices other than COM1-COM8
+        # not all versions of windows seem to support this properly
+        # so that the first few ports are used with the DOS device name
+        port = self.portstr
+        try:
+            if port.upper().startswith('COM') and int(port[3:]) > 8:
+                port = '\\\\.\\' + port
+        except ValueError:
+            # for like COMnotanumber
+            pass
+        self.hComPort = win32.CreateFile(port,
+               win32.GENERIC_READ | win32.GENERIC_WRITE,
+               0, # exclusive access
+               None, # no security
+               win32.OPEN_EXISTING,
+               win32.FILE_ATTRIBUTE_NORMAL | win32.FILE_FLAG_OVERLAPPED,
+               0)
+        if self.hComPort == win32.INVALID_HANDLE_VALUE:
+            self.hComPort = None    # 'cause __del__ is called anyway
+            raise SerialException("could not open port %r: %r" % (self.portstr, ctypes.WinError()))
+
+        try:
+            self._overlappedRead = win32.OVERLAPPED()
+            self._overlappedRead.hEvent = win32.CreateEvent(None, 1, 0, None)
+            self._overlappedWrite = win32.OVERLAPPED()
+            #~ self._overlappedWrite.hEvent = win32.CreateEvent(None, 1, 0, None)
+            self._overlappedWrite.hEvent = win32.CreateEvent(None, 0, 0, None)
+
+            # Setup a 4k buffer
+            win32.SetupComm(self.hComPort, 4096, 4096)
+
+            # Save original timeout values:
+            self._orgTimeouts = win32.COMMTIMEOUTS()
+            win32.GetCommTimeouts(self.hComPort, ctypes.byref(self._orgTimeouts))
+
+            self._reconfigurePort()
+
+            # Clear buffers:
+            # Remove anything that was there
+            win32.PurgeComm(self.hComPort,
+                    win32.PURGE_TXCLEAR | win32.PURGE_TXABORT |
+                    win32.PURGE_RXCLEAR | win32.PURGE_RXABORT)
+        except:
+            try:
+                self._close()
+            except:
+                # ignore any exception when closing the port
+                # also to keep original exception that happened when setting up
+                pass
+            self.hComPort = None
+            raise
+        else:
+            self._isOpen = True
+
+
+    def _reconfigurePort(self):
+        """Set communication parameters on opened port."""
+        if not self.hComPort:
+            raise SerialException("Can only operate on a valid port handle")
+
+        # Set Windows timeout values
+        # timeouts is a tuple with the following items:
+        # (ReadIntervalTimeout,ReadTotalTimeoutMultiplier,
+        #  ReadTotalTimeoutConstant,WriteTotalTimeoutMultiplier,
+        #  WriteTotalTimeoutConstant)
+        if self._timeout is None:
+            timeouts = (0, 0, 0, 0, 0)
+        elif self._timeout == 0:
+            timeouts = (win32.MAXDWORD, 0, 0, 0, 0)
+        else:
+            timeouts = (0, 0, int(self._timeout*1000), 0, 0)
+        if self._timeout != 0 and self._interCharTimeout is not None:
+            timeouts = (int(self._interCharTimeout * 1000),) + timeouts[1:]
+
+        if self._writeTimeout is None:
+            pass
+        elif self._writeTimeout == 0:
+            timeouts = timeouts[:-2] + (0, win32.MAXDWORD)
+        else:
+            timeouts = timeouts[:-2] + (0, int(self._writeTimeout*1000))
+        win32.SetCommTimeouts(self.hComPort, ctypes.byref(win32.COMMTIMEOUTS(*timeouts)))
+
+        win32.SetCommMask(self.hComPort, win32.EV_ERR)
+
+        # Setup the connection info.
+        # Get state and modify it:
+        comDCB = win32.DCB()
+        win32.GetCommState(self.hComPort, ctypes.byref(comDCB))
+        comDCB.BaudRate = self._baudrate
+
+        if self._bytesize == FIVEBITS:
+            comDCB.ByteSize     = 5
+        elif self._bytesize == SIXBITS:
+            comDCB.ByteSize     = 6
+        elif self._bytesize == SEVENBITS:
+            comDCB.ByteSize     = 7
+        elif self._bytesize == EIGHTBITS:
+            comDCB.ByteSize     = 8
+        else:
+            raise ValueError("Unsupported number of data bits: %r" % self._bytesize)
+
+        if self._parity == PARITY_NONE:
+            comDCB.Parity       = win32.NOPARITY
+            comDCB.fParity      = 0 # Disable Parity Check
+        elif self._parity == PARITY_EVEN:
+            comDCB.Parity       = win32.EVENPARITY
+            comDCB.fParity      = 1 # Enable Parity Check
+        elif self._parity == PARITY_ODD:
+            comDCB.Parity       = win32.ODDPARITY
+            comDCB.fParity      = 1 # Enable Parity Check
+        elif self._parity == PARITY_MARK:
+            comDCB.Parity       = win32.MARKPARITY
+            comDCB.fParity      = 1 # Enable Parity Check
+        elif self._parity == PARITY_SPACE:
+            comDCB.Parity       = win32.SPACEPARITY
+            comDCB.fParity      = 1 # Enable Parity Check
+        else:
+            raise ValueError("Unsupported parity mode: %r" % self._parity)
+
+        if self._stopbits == STOPBITS_ONE:
+            comDCB.StopBits     = win32.ONESTOPBIT
+        elif self._stopbits == STOPBITS_ONE_POINT_FIVE:
+            comDCB.StopBits     = win32.ONE5STOPBITS
+        elif self._stopbits == STOPBITS_TWO:
+            comDCB.StopBits     = win32.TWOSTOPBITS
+        else:
+            raise ValueError("Unsupported number of stop bits: %r" % self._stopbits)
+
+        comDCB.fBinary          = 1 # Enable Binary Transmission
+        # Char. w/ Parity-Err are replaced with 0xff (if fErrorChar is set to TRUE)
+        if self._rtscts:
+            comDCB.fRtsControl  = win32.RTS_CONTROL_HANDSHAKE
+        elif self._rtsToggle:
+            comDCB.fRtsControl  = win32.RTS_CONTROL_TOGGLE
+        else:
+            comDCB.fRtsControl  = self._rtsState
+        if self._dsrdtr:
+            comDCB.fDtrControl  = win32.DTR_CONTROL_HANDSHAKE
+        else:
+            comDCB.fDtrControl  = self._dtrState
+
+        if self._rtsToggle:
+            comDCB.fOutxCtsFlow     = 0
+        else:
+            comDCB.fOutxCtsFlow     = self._rtscts
+        comDCB.fOutxDsrFlow     = self._dsrdtr
+        comDCB.fOutX            = self._xonxoff
+        comDCB.fInX             = self._xonxoff
+        comDCB.fNull            = 0
+        comDCB.fErrorChar       = 0
+        comDCB.fAbortOnError    = 0
+        comDCB.XonChar          = XON
+        comDCB.XoffChar         = XOFF
+
+        if not win32.SetCommState(self.hComPort, ctypes.byref(comDCB)):
+            raise ValueError("Cannot configure port, some setting was wrong. Original message: %r" % ctypes.WinError())
+
+    #~ def __del__(self):
+        #~ self.close()
+
+
+    def _close(self):
+        """internal close port helper"""
+        if self.hComPort:
+            # Restore original timeout values:
+            win32.SetCommTimeouts(self.hComPort, self._orgTimeouts)
+            # Close COM-Port:
+            win32.CloseHandle(self.hComPort)
+            if self._overlappedRead is not None:
+                win32.CloseHandle(self._overlappedRead.hEvent)
+                self._overlappedRead = None
+            if self._overlappedWrite is not None:
+                win32.CloseHandle(self._overlappedWrite.hEvent)
+                self._overlappedWrite = None
+            self.hComPort = None
+
+    def close(self):
+        """Close port"""
+        if self._isOpen:
+            self._close()
+            self._isOpen = False
+
+    def makeDeviceName(self, port):
+        return device(port)
+
+    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
+
+    def inWaiting(self):
+        """Return the number of characters currently in the input buffer."""
+        flags = win32.DWORD()
+        comstat = win32.COMSTAT()
+        if not win32.ClearCommError(self.hComPort, ctypes.byref(flags), ctypes.byref(comstat)):
+            raise SerialException('call to ClearCommError failed')
+        return comstat.cbInQue
+
+    def read(self, size=1):
+        """Read size bytes from the serial port. If a timeout is set it may
+           return less characters as requested. With no timeout it will block
+           until the requested number of bytes is read."""
+        if not self.hComPort: raise portNotOpenError
+        if size > 0:
+            win32.ResetEvent(self._overlappedRead.hEvent)
+            flags = win32.DWORD()
+            comstat = win32.COMSTAT()
+            if not win32.ClearCommError(self.hComPort, ctypes.byref(flags), ctypes.byref(comstat)):
+                raise SerialException('call to ClearCommError failed')
+            if self.timeout == 0:
+                n = min(comstat.cbInQue, size)
+                if n > 0:
+                    buf = ctypes.create_string_buffer(n)
+                    rc = win32.DWORD()
+                    err = win32.ReadFile(self.hComPort, buf, n, ctypes.byref(rc), ctypes.byref(self._overlappedRead))
+                    if not err and win32.GetLastError() != win32.ERROR_IO_PENDING:
+                        raise SerialException("ReadFile failed (%r)" % ctypes.WinError())
+                    err = win32.WaitForSingleObject(self._overlappedRead.hEvent, win32.INFINITE)
+                    read = buf.raw[:rc.value]
+                else:
+                    read = bytes()
+            else:
+                buf = ctypes.create_string_buffer(size)
+                rc = win32.DWORD()
+                err = win32.ReadFile(self.hComPort, buf, size, ctypes.byref(rc), ctypes.byref(self._overlappedRead))
+                if not err and win32.GetLastError() != win32.ERROR_IO_PENDING:
+                    raise SerialException("ReadFile failed (%r)" % ctypes.WinError())
+                err = win32.GetOverlappedResult(self.hComPort, ctypes.byref(self._overlappedRead), ctypes.byref(rc), True)
+                read = buf.raw[:rc.value]
+        else:
+            read = bytes()
+        return bytes(read)
+
+    def write(self, data):
+        """Output the given string over the serial port."""
+        if not self.hComPort: raise portNotOpenError
+        #~ if not isinstance(data, (bytes, bytearray)):
+            #~ raise TypeError('expected %s or bytearray, got %s' % (bytes, type(data)))
+        # convert data (needed in case of memoryview instance: Py 3.1 io lib), ctypes doesn't like memoryview
+        data = to_bytes(data)
+        if data:
+            #~ win32event.ResetEvent(self._overlappedWrite.hEvent)
+            n = win32.DWORD()
+            err = win32.WriteFile(self.hComPort, data, len(data), ctypes.byref(n), self._overlappedWrite)
+            if not err and win32.GetLastError() != win32.ERROR_IO_PENDING:
+                raise SerialException("WriteFile failed (%r)" % ctypes.WinError())
+            if self._writeTimeout != 0: # if blocking (None) or w/ write timeout (>0)
+                # Wait for the write to complete.
+                #~ win32.WaitForSingleObject(self._overlappedWrite.hEvent, win32.INFINITE)
+                err = win32.GetOverlappedResult(self.hComPort, self._overlappedWrite, ctypes.byref(n), True)
+                if n.value != len(data):
+                    raise writeTimeoutError
+            return n.value
+        else:
+            return 0
+
+    def flush(self):
+        """Flush of file like objects. In this case, wait until all data
+           is written."""
+        while self.outWaiting():
+            time.sleep(0.05)
+        # XXX could also use WaitCommEvent with mask EV_TXEMPTY, but it would
+        # require overlapped IO and its also only possible to set a single mask
+        # on the port---
+
+    def flushInput(self):
+        """Clear input buffer, discarding all that is in the buffer."""
+        if not self.hComPort: raise portNotOpenError
+        win32.PurgeComm(self.hComPort, win32.PURGE_RXCLEAR | win32.PURGE_RXABORT)
+
+    def flushOutput(self):
+        """Clear output buffer, aborting the current output and
+        discarding all that is in the buffer."""
+        if not self.hComPort: raise portNotOpenError
+        win32.PurgeComm(self.hComPort, win32.PURGE_TXCLEAR | win32.PURGE_TXABORT)
+
+    def sendBreak(self, duration=0.25):
+        """Send break condition. Timed, returns to idle state after given duration."""
+        if not self.hComPort: raise portNotOpenError
+        import time
+        win32.SetCommBreak(self.hComPort)
+        time.sleep(duration)
+        win32.ClearCommBreak(self.hComPort)
+
+    def setBreak(self, level=1):
+        """Set break: Controls TXD. When active, to transmitting is possible."""
+        if not self.hComPort: raise portNotOpenError
+        if level:
+            win32.SetCommBreak(self.hComPort)
+        else:
+            win32.ClearCommBreak(self.hComPort)
+
+    def setRTS(self, level=1):
+        """Set terminal status line: Request To Send"""
+        # remember level for reconfigure
+        if level:
+            self._rtsState = win32.RTS_CONTROL_ENABLE
+        else:
+            self._rtsState = win32.RTS_CONTROL_DISABLE
+        # also apply now if port is open
+        if self.hComPort:
+            if level:
+                win32.EscapeCommFunction(self.hComPort, win32.SETRTS)
+            else:
+                win32.EscapeCommFunction(self.hComPort, win32.CLRRTS)
+
+    def setDTR(self, level=1):
+        """Set terminal status line: Data Terminal Ready"""
+        # remember level for reconfigure
+        if level:
+            self._dtrState = win32.DTR_CONTROL_ENABLE
+        else:
+            self._dtrState = win32.DTR_CONTROL_DISABLE
+        # also apply now if port is open
+        if self.hComPort:
+            if level:
+                win32.EscapeCommFunction(self.hComPort, win32.SETDTR)
+            else:
+                win32.EscapeCommFunction(self.hComPort, win32.CLRDTR)
+
+    def _GetCommModemStatus(self):
+        stat = win32.DWORD()
+        win32.GetCommModemStatus(self.hComPort, ctypes.byref(stat))
+        return stat.value
+
+    def getCTS(self):
+        """Read terminal status line: Clear To Send"""
+        if not self.hComPort: raise portNotOpenError
+        return win32.MS_CTS_ON & self._GetCommModemStatus() != 0
+
+    def getDSR(self):
+        """Read terminal status line: Data Set Ready"""
+        if not self.hComPort: raise portNotOpenError
+        return win32.MS_DSR_ON & self._GetCommModemStatus() != 0
+
+    def getRI(self):
+        """Read terminal status line: Ring Indicator"""
+        if not self.hComPort: raise portNotOpenError
+        return win32.MS_RING_ON & self._GetCommModemStatus() != 0
+
+    def getCD(self):
+        """Read terminal status line: Carrier Detect"""
+        if not self.hComPort: raise portNotOpenError
+        return win32.MS_RLSD_ON & self._GetCommModemStatus() != 0
+
+    # - - platform specific - - - -
+
+    def setBufferSize(self, rx_size=4096, tx_size=None):
+        """\
+        Recommend a buffer size to the driver (device driver can ignore this
+        vlaue). Must be called before the port is opended.
+        """
+        if tx_size is None: tx_size = rx_size
+        win32.SetupComm(self.hComPort, rx_size, tx_size)
+
+    def setXON(self, level=True):
+        """\
+        Manually control flow - when software flow control is enabled.
+        This will send XON (true) and XOFF (false) to the other device.
+        WARNING: this function is not portable to different platforms!
+        """
+        if not self.hComPort: raise portNotOpenError
+        if level:
+            win32.EscapeCommFunction(self.hComPort, win32.SETXON)
+        else:
+            win32.EscapeCommFunction(self.hComPort, win32.SETXOFF)
+
+    def outWaiting(self):
+        """return how many characters the in the outgoing buffer"""
+        flags = win32.DWORD()
+        comstat = win32.COMSTAT()
+        if not win32.ClearCommError(self.hComPort, ctypes.byref(flags), ctypes.byref(comstat)):
+            raise SerialException('call to ClearCommError failed')
+        return comstat.cbOutQue
+
+    # functions useful for RS-485 adapters
+    def setRtsToggle(self, rtsToggle):
+        """Change RTS toggle control setting."""
+        self._rtsToggle = rtsToggle
+        if self._isOpen: self._reconfigurePort()
+
+    def getRtsToggle(self):
+        """Get the current RTS toggle control setting."""
+        return self._rtsToggle
+
+    rtsToggle = property(getRtsToggle, setRtsToggle, doc="RTS toggle control setting")
+
+
+# assemble Serial class with the platform specific implementation and the base
+# for file-like behavior. for Python 2.6 and newer, that provide the new I/O
+# library, derive from io.RawIOBase
+try:
+    import io
+except ImportError:
+    # classic version with our own file-like emulation
+    class Serial(Win32Serial, FileLike):
+        pass
+else:
+    # io library present
+    class Serial(Win32Serial, io.RawIOBase):
+        pass
+
+
+# Nur Testfunktion!!
+if __name__ == '__main__':
+    s = Serial(0)
+    sys.stdout.write("%s\n" % s)
+
+    s = Serial()
+    sys.stdout.write("%s\n" % s)
+
+    s.baudrate = 19200
+    s.databits = 7
+    s.close()
+    s.port = 0
+    s.open()
+    sys.stdout.write("%s\n" % s)
+
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/sermsdos.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/sermsdos.py
new file mode 100644
index 0000000..09a0017
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/sermsdos.py
@@ -0,0 +1,200 @@
+# sermsdos.py
+#
+# History:
+#
+#   3rd September 2002                      Dave Haynes
+#   1. First defined
+#
+# Although this code should run under the latest versions of
+# Python, on DOS-based platforms such as Windows 95 and 98,
+# it has been specifically written to be compatible with
+# PyDOS, available at:
+# http://www.python.org/ftp/python/wpy/dos.html
+#
+# PyDOS is a stripped-down version of Python 1.5.2 for
+# DOS machines. Therefore, in making changes to this file,
+# please respect Python 1.5.2 syntax. In addition, please
+# limit the width of this file to 60 characters.
+#
+# Note also that the modules in PyDOS contain fewer members
+# than other versions, so we are restricted to using the
+# following:
+#
+# In module os:
+# -------------
+# environ, chdir, getcwd, getpid, umask, fdopen, close,
+# dup, dup2, fstat, lseek, open, read, write, O_RDONLY,
+# O_WRONLY, O_RDWR, O_APPEND, O_CREAT, O_EXCL, O_TRUNC,
+# access, F_OK, R_OK, W_OK, X_OK, chmod, listdir, mkdir,
+# remove, rename, renames, rmdir, stat, unlink, utime,
+# execl, execle, execlp, execlpe, execvp, execvpe, _exit,
+# system.
+#
+# In module os.path:
+# ------------------
+# curdir, pardir, sep, altsep, pathsep, defpath, linesep.
+#
+
+import os
+import sys
+import string
+import serial.serialutil
+
+BAUD_RATES = {
+                110: "11",
+                150: "15",
+                300: "30",
+                600: "60",
+                1200: "12",
+                2400: "24",
+                4800: "48",
+                9600: "96",
+                19200: "19"}
+
+(PARITY_NONE, PARITY_EVEN, PARITY_ODD, PARITY_MARK,
+PARITY_SPACE) = (0, 1, 2, 3, 4)
+(STOPBITS_ONE, STOPBITS_ONEANDAHALF,
+STOPBITS_TWO) = (1, 1.5, 2)
+FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITS = (5, 6, 7, 8)
+(RETURN_ERROR, RETURN_BUSY, RETURN_RETRY, RETURN_READY,
+RETURN_NONE) = ('E', 'B', 'P', 'R', 'N')
+portNotOpenError = ValueError('port not open')
+
+def device(portnum):
+    return 'COM%d' % (portnum+1)
+
+class Serial(serialutil.FileLike):
+    """
+       port: number of device; numbering starts at
+            zero. if everything fails, the user can
+            specify a device string, note that this
+            isn't portable any more
+       baudrate: baud rate
+       bytesize: number of databits
+       parity: enable parity checking
+       stopbits: number of stopbits
+       timeout: set a timeout (None for waiting forever)
+       xonxoff: enable software flow control
+       rtscts: enable RTS/CTS flow control
+       retry: DOS retry mode
+    """
+    def __init__(self,
+                 port,
+                 baudrate = 9600,
+                 bytesize = EIGHTBITS,
+                 parity = PARITY_NONE,
+                 stopbits = STOPBITS_ONE,
+                 timeout = None,
+                 xonxoff = 0,
+                 rtscts = 0,
+                 retry = RETURN_RETRY
+                 ):
+
+        if type(port) == type(''):
+        # strings are taken directly
+            self.portstr = port
+        else:
+        # numbers are transformed to a string
+            self.portstr = device(port+1)
+
+        self.baud = BAUD_RATES[baudrate]
+        self.bytesize = str(bytesize)
+
+        if parity == PARITY_NONE:
+            self.parity = 'N'
+        elif parity == PARITY_EVEN:
+            self.parity = 'E'
+        elif parity == PARITY_ODD:
+            self.parity = 'O'
+        elif parity == PARITY_MARK:
+            self.parity = 'M'
+        elif parity == PARITY_SPACE:
+            self.parity = 'S'
+
+        self.stop = str(stopbits)
+        self.retry = retry
+        self.filename = "sermsdos.tmp"
+
+        self._config(self.portstr, self.baud, self.parity,
+        self.bytesize, self.stop, self.retry, self.filename)
+
+    def __del__(self):
+        self.close()
+
+    def close(self):
+        pass
+
+    def _config(self, port, baud, parity, data, stop, retry,
+        filename):
+        comString = string.join(("MODE ", port, ":"
+        , " BAUD= ", baud, " PARITY= ", parity
+        , " DATA= ", data, " STOP= ", stop, " RETRY= ",
+        retry, " > ", filename ), '')
+        os.system(comString)
+
+    def setBaudrate(self, baudrate):
+        self._config(self.portstr, BAUD_RATES[baudrate],
+        self.parity, self.bytesize, self.stop, self.retry,
+        self.filename)
+
+    def inWaiting(self):
+        """returns the number of bytes waiting to be read"""
+        raise NotImplementedError
+
+    def read(self, num = 1):
+        """Read num bytes from serial port"""
+        handle = os.open(self.portstr,
+        os.O_RDONLY | os.O_BINARY)
+        rv = os.read(handle, num)
+        os.close(handle)
+        return rv
+
+    def write(self, s):
+        """Write string to serial port"""
+        handle = os.open(self.portstr,
+        os.O_WRONLY | os.O_BINARY)
+        rv = os.write(handle, s)
+        os.close(handle)
+        return rv
+
+    def flushInput(self):
+        raise NotImplementedError
+
+    def flushOutput(self):
+        raise NotImplementedError
+
+    def sendBreak(self):
+        raise NotImplementedError
+
+    def setRTS(self,level=1):
+        """Set terminal status line"""
+        raise NotImplementedError
+
+    def setDTR(self,level=1):
+        """Set terminal status line"""
+        raise NotImplementedError
+
+    def getCTS(self):
+        """Eead terminal status line"""
+        raise NotImplementedError
+
+    def getDSR(self):
+        """Eead terminal status line"""
+        raise NotImplementedError
+
+    def getRI(self):
+        """Eead terminal status line"""
+        raise NotImplementedError
+
+    def getCD(self):
+        """Eead terminal status line"""
+        raise NotImplementedError
+
+    def __repr__(self):
+        return string.join(( "<Serial>: ", self.portstr
+        , self.baud, self.parity, self.bytesize, self.stop,
+        self.retry , self.filename), ' ')
+
+if __name__ == '__main__':
+    s = Serial(0)
+    sys.stdio.write('%s %s\n' % (__name__, s))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/tools/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/tools/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/tools/__init__.py
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/tools/list_ports.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/tools/list_ports.py
new file mode 100755
index 0000000..d373a55
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/tools/list_ports.py
@@ -0,0 +1,103 @@
+#!/usr/bin/env python
+
+# portable serial port access with python
+# this is a wrapper module for different platform implementations of the
+# port enumeration feature
+#
+# (C) 2011-2013 Chris Liechti <cliechti@gmx.net>
+# this is distributed under a free software license, see license.txt
+
+"""\
+This module will provide a function called comports that returns an
+iterable (generator or list) that will enumerate available com ports. Note that
+on some systems non-existent ports may be listed.
+
+Additionally a grep function is supplied that can be used to search for ports
+based on their descriptions or hardware ID.
+"""
+
+import sys, os, re
+
+# chose an implementation, depending on os
+#~ if sys.platform == 'cli':
+#~ else:
+import os
+# chose an implementation, depending on os
+if os.name == 'nt': #sys.platform == 'win32':
+    from serial.tools.list_ports_windows import *
+elif os.name == 'posix':
+    from serial.tools.list_ports_posix import *
+#~ elif os.name == 'java':
+else:
+    raise ImportError("Sorry: no implementation for your platform ('%s') available" % (os.name,))
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+def grep(regexp):
+    """\
+    Search for ports using a regular expression. Port name, description and
+    hardware ID are searched. The function returns an iterable that returns the
+    same tuples as comport() would do.
+    """
+    r = re.compile(regexp, re.I)
+    for port, desc, hwid in comports():
+        if r.search(port) or r.search(desc) or r.search(hwid):
+            yield port, desc, hwid
+
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+def main():
+    import optparse
+
+    parser = optparse.OptionParser(
+        usage = "%prog [options] [<regexp>]",
+        description = "Miniterm - A simple terminal program for the serial port."
+    )
+
+    parser.add_option("--debug",
+            help="print debug messages and tracebacks (development mode)",
+            dest="debug",
+            default=False,
+            action='store_true')
+
+    parser.add_option("-v", "--verbose",
+            help="show more messages (can be given multiple times)",
+            dest="verbose",
+            default=1,
+            action='count')
+
+    parser.add_option("-q", "--quiet",
+            help="suppress all messages",
+            dest="verbose",
+            action='store_const',
+            const=0)
+
+    (options, args) = parser.parse_args()
+
+
+    hits = 0
+    # get iteraror w/ or w/o filter
+    if args:
+        if len(args) > 1:
+            parser.error('more than one regexp not supported')
+        print "Filtered list with regexp: %r" % (args[0],)
+        iterator = sorted(grep(args[0]))
+    else:
+        iterator = sorted(comports())
+    # list them
+    for port, desc, hwid in iterator:
+        print("%-20s" % (port,))
+        if options.verbose > 1:
+            print("    desc: %s" % (desc,))
+            print("    hwid: %s" % (hwid,))
+        hits += 1
+    if options.verbose:
+        if hits:
+            print("%d ports found" % (hits,))
+        else:
+            print("no ports found")
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# test
+if __name__ == '__main__':
+    main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/tools/list_ports_linux.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/tools/list_ports_linux.py
new file mode 100755
index 0000000..ecfd158
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/tools/list_ports_linux.py
@@ -0,0 +1,151 @@
+#!/usr/bin/env python
+
+# portable serial port access with python
+#
+# This is a module that gathers a list of serial ports including details on
+# GNU/Linux systems
+#
+# (C) 2011-2013 Chris Liechti <cliechti@gmx.net>
+# this is distributed under a free software license, see license.txt
+
+import glob
+import sys
+import os
+import re
+
+try:
+    import subprocess
+except ImportError:
+    def popen(argv):
+        try:
+            si, so =  os.popen4(' '.join(argv))
+            return so.read().strip()
+        except:
+            raise IOError('lsusb failed')
+else:
+    def popen(argv):
+        try:
+            return subprocess.check_output(argv, stderr=subprocess.STDOUT).strip()
+        except:
+            raise IOError('lsusb failed')
+
+
+# The comports function is expected to return an iterable that yields tuples of
+# 3 strings: port name, human readable description and a hardware ID.
+#
+# as currently no method is known to get the second two strings easily, they
+# are currently just identical to the port name.
+
+# try to detect the OS so that a device can be selected...
+plat = sys.platform.lower()
+
+def read_line(filename):
+    """help function to read a single line from a file. returns none"""
+    try:
+        f = open(filename)
+        line = f.readline().strip()
+        f.close()
+        return line
+    except IOError:
+        return None
+
+def re_group(regexp, text):
+    """search for regexp in text, return 1st group on match"""
+    if sys.version < '3':
+        m = re.search(regexp, text)
+    else:
+        # text is bytes-like
+        m = re.search(regexp, text.decode('ascii', 'replace'))
+    if m: return m.group(1)
+
+
+# try to extract descriptions from sysfs. this was done by experimenting,
+# no guarantee that it works for all devices or in the future...
+
+def usb_sysfs_hw_string(sysfs_path):
+    """given a path to a usb device in sysfs, return a string describing it"""
+    bus, dev = os.path.basename(os.path.realpath(sysfs_path)).split('-')
+    snr = read_line(sysfs_path+'/serial')
+    if snr:
+        snr_txt = ' SNR=%s' % (snr,)
+    else:
+        snr_txt = ''
+    return 'USB VID:PID=%s:%s%s' % (
+            read_line(sysfs_path+'/idVendor'),
+            read_line(sysfs_path+'/idProduct'),
+            snr_txt
+            )
+
+def usb_lsusb_string(sysfs_path):
+    base = os.path.basename(os.path.realpath(sysfs_path))
+    bus = base.split('-')[0]
+    try:
+        dev = int(read_line(os.path.join(sysfs_path, 'devnum')))
+        desc = popen(['lsusb', '-v', '-s', '%s:%s' % (bus, dev)])
+        # descriptions from device
+        iManufacturer = re_group('iManufacturer\s+\w+ (.+)', desc)
+        iProduct = re_group('iProduct\s+\w+ (.+)', desc)
+        iSerial = re_group('iSerial\s+\w+ (.+)', desc) or ''
+        # descriptions from kernel
+        idVendor = re_group('idVendor\s+0x\w+ (.+)', desc)
+        idProduct = re_group('idProduct\s+0x\w+ (.+)', desc)
+        # create descriptions. prefer text from device, fall back to the others
+        return '%s %s %s' % (iManufacturer or idVendor, iProduct or idProduct, iSerial)
+    except IOError:
+        return base
+
+def describe(device):
+    """\
+    Get a human readable description.
+    For USB-Serial devices try to run lsusb to get a human readable description.
+    For USB-CDC devices read the description from sysfs.
+    """
+    base = os.path.basename(device)
+    # USB-Serial devices
+    sys_dev_path = '/sys/class/tty/%s/device/driver/%s' % (base, base)
+    if os.path.exists(sys_dev_path):
+        sys_usb = os.path.dirname(os.path.dirname(os.path.realpath(sys_dev_path)))
+        return usb_lsusb_string(sys_usb)
+    # USB-CDC devices
+    sys_dev_path = '/sys/class/tty/%s/device/interface' % (base,)
+    if os.path.exists(sys_dev_path):
+        return read_line(sys_dev_path)
+
+    # USB Product Information
+    sys_dev_path = '/sys/class/tty/%s/device' % (base,)
+    if os.path.exists(sys_dev_path):
+        product_name_file = os.path.dirname(os.path.realpath(sys_dev_path)) + "/product"
+        if os.path.exists(product_name_file):
+            return read_line(product_name_file)
+
+    return base
+
+def hwinfo(device):
+    """Try to get a HW identification using sysfs"""
+    base = os.path.basename(device)
+    if os.path.exists('/sys/class/tty/%s/device' % (base,)):
+        # PCI based devices
+        sys_id_path = '/sys/class/tty/%s/device/id' % (base,)
+        if os.path.exists(sys_id_path):
+            return read_line(sys_id_path)
+        # USB-Serial devices
+        sys_dev_path = '/sys/class/tty/%s/device/driver/%s' % (base, base)
+        if os.path.exists(sys_dev_path):
+            sys_usb = os.path.dirname(os.path.dirname(os.path.realpath(sys_dev_path)))
+            return usb_sysfs_hw_string(sys_usb)
+        # USB-CDC devices
+        if base.startswith('ttyACM'):
+            sys_dev_path = '/sys/class/tty/%s/device' % (base,)
+            if os.path.exists(sys_dev_path):
+                return usb_sysfs_hw_string(sys_dev_path + '/..')
+    return 'n/a'    # XXX directly remove these from the list?
+
+def comports():
+    devices = glob.glob('/dev/ttyS*') + glob.glob('/dev/ttyUSB*') + glob.glob('/dev/ttyACM*')
+    return [(d, describe(d), hwinfo(d)) for d in devices]
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# test
+if __name__ == '__main__':
+    for port, desc, hwid in sorted(comports()):
+        print "%s: %s [%s]" % (port, desc, hwid)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/tools/list_ports_osx.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/tools/list_ports_osx.py
new file mode 100755
index 0000000..c9ed615
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/tools/list_ports_osx.py
@@ -0,0 +1,208 @@
+#!/usr/bin/env python
+
+# portable serial port access with python
+#
+# This is a module that gathers a list of serial ports including details on OSX
+#
+# code originally from https://github.com/makerbot/pyserial/tree/master/serial/tools
+# with contributions from cibomahto, dgs3, FarMcKon, tedbrandston
+# and modifications by cliechti
+#
+# this is distributed under a free software license, see license.txt
+
+
+
+# List all of the callout devices in OS/X by querying IOKit.
+
+# See the following for a reference of how to do this:
+# http://developer.apple.com/library/mac/#documentation/DeviceDrivers/Conceptual/WorkingWSerial/WWSerial_SerialDevs/SerialDevices.html#//apple_ref/doc/uid/TP30000384-CIHGEAFD
+
+# More help from darwin_hid.py
+
+# Also see the 'IORegistryExplorer' for an idea of what we are actually searching
+
+import ctypes
+from ctypes import util
+import re
+
+iokit = ctypes.cdll.LoadLibrary(ctypes.util.find_library('IOKit'))
+cf = ctypes.cdll.LoadLibrary(ctypes.util.find_library('CoreFoundation'))
+
+kIOMasterPortDefault = ctypes.c_void_p.in_dll(iokit, "kIOMasterPortDefault")
+kCFAllocatorDefault = ctypes.c_void_p.in_dll(cf, "kCFAllocatorDefault")
+
+kCFStringEncodingMacRoman = 0
+
+iokit.IOServiceMatching.restype = ctypes.c_void_p
+
+iokit.IOServiceGetMatchingServices.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]
+iokit.IOServiceGetMatchingServices.restype = ctypes.c_void_p
+
+iokit.IORegistryEntryGetParentEntry.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]
+
+iokit.IORegistryEntryCreateCFProperty.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_uint32]
+iokit.IORegistryEntryCreateCFProperty.restype = ctypes.c_void_p
+
+iokit.IORegistryEntryGetPath.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]
+iokit.IORegistryEntryGetPath.restype = ctypes.c_void_p
+
+iokit.IORegistryEntryGetName.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+iokit.IORegistryEntryGetName.restype = ctypes.c_void_p
+
+iokit.IOObjectGetClass.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+iokit.IOObjectGetClass.restype = ctypes.c_void_p
+
+iokit.IOObjectRelease.argtypes = [ctypes.c_void_p]
+
+
+cf.CFStringCreateWithCString.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_int32]
+cf.CFStringCreateWithCString.restype = ctypes.c_void_p
+
+cf.CFStringGetCStringPtr.argtypes = [ctypes.c_void_p, ctypes.c_uint32]
+cf.CFStringGetCStringPtr.restype = ctypes.c_char_p
+
+cf.CFNumberGetValue.argtypes = [ctypes.c_void_p, ctypes.c_uint32, ctypes.c_void_p]
+cf.CFNumberGetValue.restype = ctypes.c_void_p
+
+def get_string_property(device_t, property):
+    """ Search the given device for the specified string property
+
+    @param device_t Device to search
+    @param property String to search for.
+    @return Python string containing the value, or None if not found.
+    """
+    key = cf.CFStringCreateWithCString(
+        kCFAllocatorDefault,
+        property.encode("mac_roman"),
+        kCFStringEncodingMacRoman
+    )
+
+    CFContainer = iokit.IORegistryEntryCreateCFProperty(
+        device_t,
+        key,
+        kCFAllocatorDefault,
+        0
+    );
+
+    output = None
+
+    if CFContainer:
+        output = cf.CFStringGetCStringPtr(CFContainer, 0)
+
+    return output
+
+def get_int_property(device_t, property):
+    """ Search the given device for the specified string property
+
+    @param device_t Device to search
+    @param property String to search for.
+    @return Python string containing the value, or None if not found.
+    """
+    key = cf.CFStringCreateWithCString(
+        kCFAllocatorDefault,
+        property.encode("mac_roman"),
+        kCFStringEncodingMacRoman
+    )
+
+    CFContainer = iokit.IORegistryEntryCreateCFProperty(
+        device_t,
+        key,
+        kCFAllocatorDefault,
+        0
+    );
+
+    number = ctypes.c_uint16()
+
+    if CFContainer:
+        output = cf.CFNumberGetValue(CFContainer, 2, ctypes.byref(number))
+
+    return number.value
+
+def IORegistryEntryGetName(device):
+    pathname = ctypes.create_string_buffer(100) # TODO: Is this ok?
+    iokit.IOObjectGetClass(
+        device,
+        ctypes.byref(pathname)
+    )
+
+    return pathname.value
+
+def GetParentDeviceByType(device, parent_type):
+    """ Find the first parent of a device that implements the parent_type
+        @param IOService Service to inspect
+        @return Pointer to the parent type, or None if it was not found.
+    """
+    # First, try to walk up the IOService tree to find a parent of this device that is a IOUSBDevice.
+    while IORegistryEntryGetName(device) != parent_type:
+        parent = ctypes.c_void_p()
+        response = iokit.IORegistryEntryGetParentEntry(
+            device,
+            "IOService".encode("mac_roman"),
+            ctypes.byref(parent)
+        )
+
+        # If we weren't able to find a parent for the device, we're done.
+        if response != 0:
+            return None
+
+        device = parent
+
+    return device
+
+def GetIOServicesByType(service_type):
+    """
+    """
+    serial_port_iterator = ctypes.c_void_p()
+
+    response = iokit.IOServiceGetMatchingServices(
+        kIOMasterPortDefault,
+        iokit.IOServiceMatching(service_type),
+        ctypes.byref(serial_port_iterator)
+    )
+
+    services = []
+    while iokit.IOIteratorIsValid(serial_port_iterator):
+        service = iokit.IOIteratorNext(serial_port_iterator)
+        if not service:
+            break
+        services.append(service)
+
+    iokit.IOObjectRelease(serial_port_iterator)
+
+    return services
+
+def comports():
+    # Scan for all iokit serial ports
+    services = GetIOServicesByType('IOSerialBSDClient')
+
+    ports = []
+    for service in services:
+        info = []
+
+        # First, add the callout device file.
+        info.append(get_string_property(service, "IOCalloutDevice"))
+
+        # If the serial port is implemented by a
+        usb_device = GetParentDeviceByType(service, "IOUSBDevice")
+        if usb_device != None:
+            info.append(get_string_property(usb_device, "USB Product Name"))
+
+            info.append(
+                "USB VID:PID=%x:%x SNR=%s"%(
+                get_int_property(usb_device, "idVendor"),
+                get_int_property(usb_device, "idProduct"),
+                get_string_property(usb_device, "USB Serial Number"))
+            )
+        else:
+           info.append('n/a')
+           info.append('n/a')
+
+        ports.append(info)
+
+    return ports
+
+# test
+if __name__ == '__main__':
+    for port, desc, hwid in sorted(comports()):
+        print "%s: %s [%s]" % (port, desc, hwid)
+
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/tools/list_ports_posix.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/tools/list_ports_posix.py
new file mode 100755
index 0000000..09f115f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/tools/list_ports_posix.py
@@ -0,0 +1,101 @@
+#!/usr/bin/env python
+
+# portable serial port access with python
+
+# This is a module that gathers a list of serial ports on POSIXy systems.
+# For some specific implementations, see also list_ports_linux, list_ports_osx
+#
+# this is a wrapper module for different platform implementations of the
+# port enumeration feature
+#
+# (C) 2011-2013 Chris Liechti <cliechti@gmx.net>
+# this is distributed under a free software license, see license.txt
+
+"""\
+The ``comports`` function is expected to return an iterable that yields tuples
+of 3 strings: port name, human readable description and a hardware ID.
+
+As currently no method is known to get the second two strings easily, they are
+currently just identical to the port name.
+"""
+
+import glob
+import sys
+import os
+
+# try to detect the OS so that a device can be selected...
+plat = sys.platform.lower()
+
+if   plat[:5] == 'linux':    # Linux (confirmed)
+    from serial.tools.list_ports_linux import comports
+
+elif plat == 'cygwin':       # cygwin/win32
+    def comports():
+        devices = glob.glob('/dev/com*')
+        return [(d, d, d) for d in devices]
+
+elif plat[:7] == 'openbsd':    # OpenBSD
+    def comports():
+        devices = glob.glob('/dev/cua*')
+        return [(d, d, d) for d in devices]
+
+elif plat[:3] == 'bsd' or  \
+        plat[:7] == 'freebsd':
+
+    def comports():
+        devices = glob.glob('/dev/cuad*')
+        return [(d, d, d) for d in devices]
+
+elif plat[:6] == 'darwin':   # OS X (confirmed)
+    from serial.tools.list_ports_osx import comports
+
+elif plat[:6] == 'netbsd':   # NetBSD
+    def comports():
+        """scan for available ports. return a list of device names."""
+        devices = glob.glob('/dev/dty*')
+        return [(d, d, d) for d in devices]
+
+elif plat[:4] == 'irix':     # IRIX
+    def comports():
+        """scan for available ports. return a list of device names."""
+        devices = glob.glob('/dev/ttyf*')
+        return [(d, d, d) for d in devices]
+
+elif plat[:2] == 'hp':       # HP-UX (not tested)
+    def comports():
+        """scan for available ports. return a list of device names."""
+        devices = glob.glob('/dev/tty*p0')
+        return [(d, d, d) for d in devices]
+
+elif plat[:5] == 'sunos':    # Solaris/SunOS
+    def comports():
+        """scan for available ports. return a list of device names."""
+        devices = glob.glob('/dev/tty*c')
+        return [(d, d, d) for d in devices]
+
+elif plat[:3] == 'aix':      # AIX
+    def comports():
+        """scan for available ports. return a list of device names."""
+        devices = glob.glob('/dev/tty*')
+        return [(d, d, d) for d in devices]
+
+else:
+    # platform detection has failed...
+    sys.stderr.write("""\
+don't know how to enumerate ttys on this system.
+! I you know how the serial ports are named send this information to
+! the author of this module:
+
+sys.platform = %r
+os.name = %r
+pySerial version = %s
+
+also add the naming scheme of the serial ports and with a bit luck you can get
+this module running...
+""" % (sys.platform, os.name, serial.VERSION))
+    raise ImportError("Sorry: no implementation for your platform ('%s') available" % (os.name,))
+
+# test
+if __name__ == '__main__':
+    for port, desc, hwid in sorted(comports()):
+        print "%s: %s [%s]" % (port, desc, hwid)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/tools/list_ports_windows.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/tools/list_ports_windows.py
new file mode 100644
index 0000000..ca597ca
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/tools/list_ports_windows.py
@@ -0,0 +1,240 @@
+import ctypes
+import re
+
+def ValidHandle(value, func, arguments):
+    if value == 0:
+        raise ctypes.WinError()
+    return value
+
+import serial
+from serial.win32 import ULONG_PTR, is_64bit
+from ctypes.wintypes import HANDLE
+from ctypes.wintypes import BOOL
+from ctypes.wintypes import HWND
+from ctypes.wintypes import DWORD
+from ctypes.wintypes import WORD
+from ctypes.wintypes import LONG
+from ctypes.wintypes import ULONG
+from ctypes.wintypes import LPCSTR
+from ctypes.wintypes import HKEY
+from ctypes.wintypes import BYTE
+
+NULL = 0
+HDEVINFO = ctypes.c_void_p
+PCTSTR = ctypes.c_char_p
+PTSTR = ctypes.c_void_p
+CHAR = ctypes.c_char
+LPDWORD = PDWORD = ctypes.POINTER(DWORD)
+#~ LPBYTE = PBYTE = ctypes.POINTER(BYTE)
+LPBYTE = PBYTE = ctypes.c_void_p        # XXX avoids error about types
+
+ACCESS_MASK = DWORD
+REGSAM = ACCESS_MASK
+
+
+def byte_buffer(length):
+    """Get a buffer for a string"""
+    return (BYTE*length)()
+
+def string(buffer):
+    s = []
+    for c in buffer:
+        if c == 0: break
+        s.append(chr(c & 0xff)) # "& 0xff": hack to convert signed to unsigned
+    return ''.join(s)
+
+
+class GUID(ctypes.Structure):
+    _fields_ = [
+        ('Data1', DWORD),
+        ('Data2', WORD),
+        ('Data3', WORD),
+        ('Data4', BYTE*8),
+    ]
+    def __str__(self):
+        return "{%08x-%04x-%04x-%s-%s}" % (
+            self.Data1,
+            self.Data2,
+            self.Data3,
+            ''.join(["%02x" % d for d in self.Data4[:2]]),
+            ''.join(["%02x" % d for d in self.Data4[2:]]),
+        )
+
+class SP_DEVINFO_DATA(ctypes.Structure):
+    _fields_ = [
+        ('cbSize', DWORD),
+        ('ClassGuid', GUID),
+        ('DevInst', DWORD),
+        ('Reserved', ULONG_PTR),
+    ]
+    def __str__(self):
+        return "ClassGuid:%s DevInst:%s" % (self.ClassGuid, self.DevInst)
+PSP_DEVINFO_DATA = ctypes.POINTER(SP_DEVINFO_DATA)
+
+PSP_DEVICE_INTERFACE_DETAIL_DATA = ctypes.c_void_p
+
+setupapi = ctypes.windll.LoadLibrary("setupapi")
+SetupDiDestroyDeviceInfoList = setupapi.SetupDiDestroyDeviceInfoList
+SetupDiDestroyDeviceInfoList.argtypes = [HDEVINFO]
+SetupDiDestroyDeviceInfoList.restype = BOOL
+
+SetupDiClassGuidsFromName = setupapi.SetupDiClassGuidsFromNameA
+SetupDiClassGuidsFromName.argtypes = [PCTSTR, ctypes.POINTER(GUID), DWORD, PDWORD]
+SetupDiClassGuidsFromName.restype = BOOL
+
+SetupDiEnumDeviceInfo = setupapi.SetupDiEnumDeviceInfo
+SetupDiEnumDeviceInfo.argtypes = [HDEVINFO, DWORD, PSP_DEVINFO_DATA]
+SetupDiEnumDeviceInfo.restype = BOOL
+
+SetupDiGetClassDevs = setupapi.SetupDiGetClassDevsA
+SetupDiGetClassDevs.argtypes = [ctypes.POINTER(GUID), PCTSTR, HWND, DWORD]
+SetupDiGetClassDevs.restype = HDEVINFO
+SetupDiGetClassDevs.errcheck = ValidHandle
+
+SetupDiGetDeviceRegistryProperty = setupapi.SetupDiGetDeviceRegistryPropertyA
+SetupDiGetDeviceRegistryProperty.argtypes = [HDEVINFO, PSP_DEVINFO_DATA, DWORD, PDWORD, PBYTE, DWORD, PDWORD]
+SetupDiGetDeviceRegistryProperty.restype = BOOL
+
+SetupDiGetDeviceInstanceId = setupapi.SetupDiGetDeviceInstanceIdA
+SetupDiGetDeviceInstanceId.argtypes = [HDEVINFO, PSP_DEVINFO_DATA, PTSTR, DWORD, PDWORD]
+SetupDiGetDeviceInstanceId.restype = BOOL
+
+SetupDiOpenDevRegKey = setupapi.SetupDiOpenDevRegKey
+SetupDiOpenDevRegKey.argtypes = [HDEVINFO, PSP_DEVINFO_DATA, DWORD, DWORD, DWORD, REGSAM]
+SetupDiOpenDevRegKey.restype = HKEY
+
+advapi32 = ctypes.windll.LoadLibrary("Advapi32")
+RegCloseKey = advapi32.RegCloseKey
+RegCloseKey.argtypes = [HKEY]
+RegCloseKey.restype = LONG
+
+RegQueryValueEx = advapi32.RegQueryValueExA
+RegQueryValueEx.argtypes = [HKEY, LPCSTR, LPDWORD, LPDWORD, LPBYTE, LPDWORD]
+RegQueryValueEx.restype = LONG
+
+
+DIGCF_PRESENT = 2
+DIGCF_DEVICEINTERFACE = 16
+INVALID_HANDLE_VALUE = 0
+ERROR_INSUFFICIENT_BUFFER = 122
+SPDRP_HARDWAREID = 1
+SPDRP_FRIENDLYNAME = 12
+DICS_FLAG_GLOBAL = 1
+DIREG_DEV = 0x00000001
+KEY_READ = 0x20019
+
+# workaround for compatibility between Python 2.x and 3.x
+Ports = serial.to_bytes([80, 111, 114, 116, 115]) # "Ports"
+PortName = serial.to_bytes([80, 111, 114, 116, 78, 97, 109, 101]) # "PortName"
+
+def comports():
+    GUIDs = (GUID*8)() # so far only seen one used, so hope 8 are enough...
+    guids_size = DWORD()
+    if not SetupDiClassGuidsFromName(
+            Ports,
+            GUIDs,
+            ctypes.sizeof(GUIDs),
+            ctypes.byref(guids_size)):
+        raise ctypes.WinError()
+
+    # repeat for all possible GUIDs
+    for index in range(guids_size.value):
+        g_hdi = SetupDiGetClassDevs(
+                ctypes.byref(GUIDs[index]),
+                None,
+                NULL,
+                DIGCF_PRESENT) # was DIGCF_PRESENT|DIGCF_DEVICEINTERFACE which misses CDC ports
+
+        devinfo = SP_DEVINFO_DATA()
+        devinfo.cbSize = ctypes.sizeof(devinfo)
+        index = 0
+        while SetupDiEnumDeviceInfo(g_hdi, index, ctypes.byref(devinfo)):
+            index += 1
+
+            # get the real com port name
+            hkey = SetupDiOpenDevRegKey(
+                    g_hdi,
+                    ctypes.byref(devinfo),
+                    DICS_FLAG_GLOBAL,
+                    0,
+                    DIREG_DEV,  # DIREG_DRV for SW info
+                    KEY_READ)
+            port_name_buffer = byte_buffer(250)
+            port_name_length = ULONG(ctypes.sizeof(port_name_buffer))
+            RegQueryValueEx(
+                    hkey,
+                    PortName,
+                    None,
+                    None,
+                    ctypes.byref(port_name_buffer),
+                    ctypes.byref(port_name_length))
+            RegCloseKey(hkey)
+
+            # unfortunately does this method also include parallel ports.
+            # we could check for names starting with COM or just exclude LPT
+            # and hope that other "unknown" names are serial ports...
+            if string(port_name_buffer).startswith('LPT'):
+                continue
+
+            # hardware ID
+            szHardwareID = byte_buffer(250)
+            # try to get ID that includes serial number
+            if not SetupDiGetDeviceInstanceId(
+                    g_hdi,
+                    ctypes.byref(devinfo),
+                    ctypes.byref(szHardwareID),
+                    ctypes.sizeof(szHardwareID) - 1,
+                    None):
+                # fall back to more generic hardware ID if that would fail
+                if not SetupDiGetDeviceRegistryProperty(
+                        g_hdi,
+                        ctypes.byref(devinfo),
+                        SPDRP_HARDWAREID,
+                        None,
+                        ctypes.byref(szHardwareID),
+                        ctypes.sizeof(szHardwareID) - 1,
+                        None):
+                    # Ignore ERROR_INSUFFICIENT_BUFFER
+                    if ctypes.GetLastError() != ERROR_INSUFFICIENT_BUFFER:
+                        raise ctypes.WinError()
+            # stringify
+            szHardwareID_str = string(szHardwareID)
+
+            # in case of USB, make a more readable string, similar to that form
+            # that we also generate on other platforms
+            if szHardwareID_str.startswith('USB'):
+                m = re.search(r'VID_([0-9a-f]{4})&PID_([0-9a-f]{4})(\\(\w+))?', szHardwareID_str, re.I)
+                if m:
+                    if m.group(4):
+                        szHardwareID_str = 'USB VID:PID=%s:%s SNR=%s' % (m.group(1), m.group(2), m.group(4))
+                    else:
+                        szHardwareID_str = 'USB VID:PID=%s:%s' % (m.group(1), m.group(2))
+
+            # friendly name
+            szFriendlyName = byte_buffer(250)
+            if not SetupDiGetDeviceRegistryProperty(
+                    g_hdi,
+                    ctypes.byref(devinfo),
+                    SPDRP_FRIENDLYNAME,
+                    #~ SPDRP_DEVICEDESC,
+                    None,
+                    ctypes.byref(szFriendlyName),
+                    ctypes.sizeof(szFriendlyName) - 1,
+                    None):
+                # Ignore ERROR_INSUFFICIENT_BUFFER
+                #~ if ctypes.GetLastError() != ERROR_INSUFFICIENT_BUFFER:
+                    #~ raise IOError("failed to get details for %s (%s)" % (devinfo, szHardwareID.value))
+                # ignore errors and still include the port in the list, friendly name will be same as port name
+                yield string(port_name_buffer), 'n/a', szHardwareID_str
+            else:
+                yield string(port_name_buffer), string(szFriendlyName), szHardwareID_str
+
+        SetupDiDestroyDeviceInfoList(g_hdi)
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# test
+if __name__ == '__main__':
+    import serial
+
+    for port, desc, hwid in sorted(comports()):
+        print "%s: %s [%s]" % (port, desc, hwid)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/tools/miniterm.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/tools/miniterm.py
new file mode 100755
index 0000000..274c7fb
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/tools/miniterm.py
@@ -0,0 +1,694 @@
+#!/usr/bin/env python
+
+# Very simple serial terminal
+# (C)2002-2011 Chris Liechti <cliechti@gmx.net>
+
+# Input characters are sent directly (only LF -> CR/LF/CRLF translation is
+# done), received characters are displayed as is (or escaped trough pythons
+# repr, useful for debug purposes)
+
+
+import sys, os, serial, threading
+try:
+    from serial.tools.list_ports import comports
+except ImportError:
+    comports = None
+
+EXITCHARCTER = serial.to_bytes([0x1d])   # GS/CTRL+]
+MENUCHARACTER = serial.to_bytes([0x14])  # Menu: CTRL+T
+
+DEFAULT_PORT = None
+DEFAULT_BAUDRATE = 9600
+DEFAULT_RTS = None
+DEFAULT_DTR = None
+
+
+def key_description(character):
+    """generate a readable description for a key"""
+    ascii_code = ord(character)
+    if ascii_code < 32:
+        return 'Ctrl+%c' % (ord('@') + ascii_code)
+    else:
+        return repr(character)
+
+
+# help text, starts with blank line! it's a function so that the current values
+# for the shortcut keys is used and not the value at program start
+def get_help_text():
+    return """
+--- pySerial (%(version)s) - miniterm - help
+---
+--- %(exit)-8s Exit program
+--- %(menu)-8s Menu escape key, followed by:
+--- Menu keys:
+---    %(itself)-7s Send the menu character itself to remote
+---    %(exchar)-7s Send the exit character itself to remote
+---    %(info)-7s Show info
+---    %(upload)-7s Upload file (prompt will be shown)
+--- Toggles:
+---    %(rts)-7s RTS          %(echo)-7s local echo
+---    %(dtr)-7s DTR          %(break)-7s BREAK
+---    %(lfm)-7s line feed    %(repr)-7s Cycle repr mode
+---
+--- Port settings (%(menu)s followed by the following):
+---    p          change port
+---    7 8        set data bits
+---    n e o s m  change parity (None, Even, Odd, Space, Mark)
+---    1 2 3      set stop bits (1, 2, 1.5)
+---    b          change baud rate
+---    x X        disable/enable software flow control
+---    r R        disable/enable hardware flow control
+""" % {
+    'version': getattr(serial, 'VERSION', 'unknown version'),
+    'exit': key_description(EXITCHARCTER),
+    'menu': key_description(MENUCHARACTER),
+    'rts': key_description('\x12'),
+    'repr': key_description('\x01'),
+    'dtr': key_description('\x04'),
+    'lfm': key_description('\x0c'),
+    'break': key_description('\x02'),
+    'echo': key_description('\x05'),
+    'info': key_description('\x09'),
+    'upload': key_description('\x15'),
+    'itself': key_description(MENUCHARACTER),
+    'exchar': key_description(EXITCHARCTER),
+}
+
+if sys.version_info >= (3, 0):
+    def character(b):
+        return b.decode('latin1')
+else:
+    def character(b):
+        return b
+
+LF = serial.to_bytes([10])
+CR = serial.to_bytes([13])
+CRLF = serial.to_bytes([13, 10])
+
+X00 = serial.to_bytes([0])
+X0E = serial.to_bytes([0x0e])
+
+# first choose a platform dependant way to read single characters from the console
+global console
+
+if os.name == 'nt':
+    import msvcrt
+    class Console(object):
+        def __init__(self):
+            pass
+
+        def setup(self):
+            pass    # Do nothing for 'nt'
+
+        def cleanup(self):
+            pass    # Do nothing for 'nt'
+
+        def getkey(self):
+            while True:
+                z = msvcrt.getch()
+                if z == X00 or z == X0E:    # functions keys, ignore
+                    msvcrt.getch()
+                else:
+                    if z == CR:
+                        return LF
+                    return z
+
+    console = Console()
+
+elif os.name == 'posix':
+    import termios, sys, os
+    class Console(object):
+        def __init__(self):
+            self.fd = sys.stdin.fileno()
+            self.old = None
+
+        def setup(self):
+            self.old = termios.tcgetattr(self.fd)
+            new = termios.tcgetattr(self.fd)
+            new[3] = new[3] & ~termios.ICANON & ~termios.ECHO & ~termios.ISIG
+            new[6][termios.VMIN] = 1
+            new[6][termios.VTIME] = 0
+            termios.tcsetattr(self.fd, termios.TCSANOW, new)
+
+        def getkey(self):
+            c = os.read(self.fd, 1)
+            return c
+
+        def cleanup(self):
+            if self.old is not None:
+                termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.old)
+
+    console = Console()
+
+    def cleanup_console():
+        console.cleanup()
+
+    sys.exitfunc = cleanup_console      # terminal modes have to be restored on exit...
+
+else:
+    raise NotImplementedError("Sorry no implementation for your platform (%s) available." % sys.platform)
+
+
+def dump_port_list():
+    if comports:
+        sys.stderr.write('\n--- Available ports:\n')
+        for port, desc, hwid in sorted(comports()):
+            #~ sys.stderr.write('--- %-20s %s [%s]\n' % (port, desc, hwid))
+            sys.stderr.write('--- %-20s %s\n' % (port, desc))
+
+
+CONVERT_CRLF = 2
+CONVERT_CR   = 1
+CONVERT_LF   = 0
+NEWLINE_CONVERISON_MAP = (LF, CR, CRLF)
+LF_MODES = ('LF', 'CR', 'CR/LF')
+
+REPR_MODES = ('raw', 'some control', 'all control', 'hex')
+
+class Miniterm(object):
+    def __init__(self, port, baudrate, parity, rtscts, xonxoff, echo=False, convert_outgoing=CONVERT_CRLF, repr_mode=0):
+        try:
+            self.serial = serial.serial_for_url(port, baudrate, parity=parity, rtscts=rtscts, xonxoff=xonxoff, timeout=1)
+        except AttributeError:
+            # happens when the installed pyserial is older than 2.5. use the
+            # Serial class directly then.
+            self.serial = serial.Serial(port, baudrate, parity=parity, rtscts=rtscts, xonxoff=xonxoff, timeout=1)
+        self.echo = echo
+        self.repr_mode = repr_mode
+        self.convert_outgoing = convert_outgoing
+        self.newline = NEWLINE_CONVERISON_MAP[self.convert_outgoing]
+        self.dtr_state = True
+        self.rts_state = True
+        self.break_state = False
+
+    def _start_reader(self):
+        """Start reader thread"""
+        self._reader_alive = True
+        # start serial->console thread
+        self.receiver_thread = threading.Thread(target=self.reader)
+        self.receiver_thread.setDaemon(True)
+        self.receiver_thread.start()
+
+    def _stop_reader(self):
+        """Stop reader thread only, wait for clean exit of thread"""
+        self._reader_alive = False
+        self.receiver_thread.join()
+
+
+    def start(self):
+        self.alive = True
+        self._start_reader()
+        # enter console->serial loop
+        self.transmitter_thread = threading.Thread(target=self.writer)
+        self.transmitter_thread.setDaemon(True)
+        self.transmitter_thread.start()
+
+    def stop(self):
+        self.alive = False
+
+    def join(self, transmit_only=False):
+        self.transmitter_thread.join()
+        if not transmit_only:
+            self.receiver_thread.join()
+
+    def dump_port_settings(self):
+        sys.stderr.write("\n--- Settings: %s  %s,%s,%s,%s\n" % (
+                self.serial.portstr,
+                self.serial.baudrate,
+                self.serial.bytesize,
+                self.serial.parity,
+                self.serial.stopbits))
+        sys.stderr.write('--- RTS: %-8s  DTR: %-8s  BREAK: %-8s\n' % (
+                (self.rts_state and 'active' or 'inactive'),
+                (self.dtr_state and 'active' or 'inactive'),
+                (self.break_state and 'active' or 'inactive')))
+        try:
+            sys.stderr.write('--- CTS: %-8s  DSR: %-8s  RI: %-8s  CD: %-8s\n' % (
+                    (self.serial.getCTS() and 'active' or 'inactive'),
+                    (self.serial.getDSR() and 'active' or 'inactive'),
+                    (self.serial.getRI() and 'active' or 'inactive'),
+                    (self.serial.getCD() and 'active' or 'inactive')))
+        except serial.SerialException:
+            # on RFC 2217 ports it can happen to no modem state notification was
+            # yet received. ignore this error.
+            pass
+        sys.stderr.write('--- software flow control: %s\n' % (self.serial.xonxoff and 'active' or 'inactive'))
+        sys.stderr.write('--- hardware flow control: %s\n' % (self.serial.rtscts and 'active' or 'inactive'))
+        sys.stderr.write('--- data escaping: %s  linefeed: %s\n' % (
+                REPR_MODES[self.repr_mode],
+                LF_MODES[self.convert_outgoing]))
+
+    def reader(self):
+        """loop and copy serial->console"""
+        try:
+            while self.alive and self._reader_alive:
+                data = character(self.serial.read(1))
+
+                if self.repr_mode == 0:
+                    # direct output, just have to care about newline setting
+                    if data == '\r' and self.convert_outgoing == CONVERT_CR:
+                        sys.stdout.write('\n')
+                    else:
+                        sys.stdout.write(data)
+                elif self.repr_mode == 1:
+                    # escape non-printable, let pass newlines
+                    if self.convert_outgoing == CONVERT_CRLF and data in '\r\n':
+                        if data == '\n':
+                            sys.stdout.write('\n')
+                        elif data == '\r':
+                            pass
+                    elif data == '\n' and self.convert_outgoing == CONVERT_LF:
+                        sys.stdout.write('\n')
+                    elif data == '\r' and self.convert_outgoing == CONVERT_CR:
+                        sys.stdout.write('\n')
+                    else:
+                        sys.stdout.write(repr(data)[1:-1])
+                elif self.repr_mode == 2:
+                    # escape all non-printable, including newline
+                    sys.stdout.write(repr(data)[1:-1])
+                elif self.repr_mode == 3:
+                    # escape everything (hexdump)
+                    for c in data:
+                        sys.stdout.write("%s " % c.encode('hex'))
+                sys.stdout.flush()
+        except serial.SerialException, e:
+            self.alive = False
+            # would be nice if the console reader could be interruptted at this
+            # point...
+            raise
+
+
+    def writer(self):
+        """\
+        Loop and copy console->serial until EXITCHARCTER character is
+        found. When MENUCHARACTER is found, interpret the next key
+        locally.
+        """
+        menu_active = False
+        try:
+            while self.alive:
+                try:
+                    b = console.getkey()
+                except KeyboardInterrupt:
+                    b = serial.to_bytes([3])
+                c = character(b)
+                if menu_active:
+                    if c == MENUCHARACTER or c == EXITCHARCTER: # Menu character again/exit char -> send itself
+                        self.serial.write(b)                    # send character
+                        if self.echo:
+                            sys.stdout.write(c)
+                    elif c == '\x15':                       # CTRL+U -> upload file
+                        sys.stderr.write('\n--- File to upload: ')
+                        sys.stderr.flush()
+                        console.cleanup()
+                        filename = sys.stdin.readline().rstrip('\r\n')
+                        if filename:
+                            try:
+                                file = open(filename, 'r')
+                                sys.stderr.write('--- Sending file %s ---\n' % filename)
+                                while True:
+                                    line = file.readline().rstrip('\r\n')
+                                    if not line:
+                                        break
+                                    self.serial.write(line)
+                                    self.serial.write('\r\n')
+                                    # Wait for output buffer to drain.
+                                    self.serial.flush()
+                                    sys.stderr.write('.')   # Progress indicator.
+                                sys.stderr.write('\n--- File %s sent ---\n' % filename)
+                            except IOError, e:
+                                sys.stderr.write('--- ERROR opening file %s: %s ---\n' % (filename, e))
+                        console.setup()
+                    elif c in '\x08hH?':                    # CTRL+H, h, H, ? -> Show help
+                        sys.stderr.write(get_help_text())
+                    elif c == '\x12':                       # CTRL+R -> Toggle RTS
+                        self.rts_state = not self.rts_state
+                        self.serial.setRTS(self.rts_state)
+                        sys.stderr.write('--- RTS %s ---\n' % (self.rts_state and 'active' or 'inactive'))
+                    elif c == '\x04':                       # CTRL+D -> Toggle DTR
+                        self.dtr_state = not self.dtr_state
+                        self.serial.setDTR(self.dtr_state)
+                        sys.stderr.write('--- DTR %s ---\n' % (self.dtr_state and 'active' or 'inactive'))
+                    elif c == '\x02':                       # CTRL+B -> toggle BREAK condition
+                        self.break_state = not self.break_state
+                        self.serial.setBreak(self.break_state)
+                        sys.stderr.write('--- BREAK %s ---\n' % (self.break_state and 'active' or 'inactive'))
+                    elif c == '\x05':                       # CTRL+E -> toggle local echo
+                        self.echo = not self.echo
+                        sys.stderr.write('--- local echo %s ---\n' % (self.echo and 'active' or 'inactive'))
+                    elif c == '\x09':                       # CTRL+I -> info
+                        self.dump_port_settings()
+                    elif c == '\x01':                       # CTRL+A -> cycle escape mode
+                        self.repr_mode += 1
+                        if self.repr_mode > 3:
+                            self.repr_mode = 0
+                        sys.stderr.write('--- escape data: %s ---\n' % (
+                            REPR_MODES[self.repr_mode],
+                        ))
+                    elif c == '\x0c':                       # CTRL+L -> cycle linefeed mode
+                        self.convert_outgoing += 1
+                        if self.convert_outgoing > 2:
+                            self.convert_outgoing = 0
+                        self.newline = NEWLINE_CONVERISON_MAP[self.convert_outgoing]
+                        sys.stderr.write('--- line feed %s ---\n' % (
+                            LF_MODES[self.convert_outgoing],
+                        ))
+                    elif c in 'pP':                         # P -> change port
+                        dump_port_list()
+                        sys.stderr.write('--- Enter port name: ')
+                        sys.stderr.flush()
+                        console.cleanup()
+                        try:
+                            port = sys.stdin.readline().strip()
+                        except KeyboardInterrupt:
+                            port = None
+                        console.setup()
+                        if port and port != self.serial.port:
+                            # reader thread needs to be shut down
+                            self._stop_reader()
+                            # save settings
+                            settings = self.serial.getSettingsDict()
+                            try:
+                                try:
+                                    new_serial = serial.serial_for_url(port, do_not_open=True)
+                                except AttributeError:
+                                    # happens when the installed pyserial is older than 2.5. use the
+                                    # Serial class directly then.
+                                    new_serial = serial.Serial()
+                                    new_serial.port = port
+                                # restore settings and open
+                                new_serial.applySettingsDict(settings)
+                                new_serial.open()
+                                new_serial.setRTS(self.rts_state)
+                                new_serial.setDTR(self.dtr_state)
+                                new_serial.setBreak(self.break_state)
+                            except Exception, e:
+                                sys.stderr.write('--- ERROR opening new port: %s ---\n' % (e,))
+                                new_serial.close()
+                            else:
+                                self.serial.close()
+                                self.serial = new_serial
+                                sys.stderr.write('--- Port changed to: %s ---\n' % (self.serial.port,))
+                            # and restart the reader thread
+                            self._start_reader()
+                    elif c in 'bB':                         # B -> change baudrate
+                        sys.stderr.write('\n--- Baudrate: ')
+                        sys.stderr.flush()
+                        console.cleanup()
+                        backup = self.serial.baudrate
+                        try:
+                            self.serial.baudrate = int(sys.stdin.readline().strip())
+                        except ValueError, e:
+                            sys.stderr.write('--- ERROR setting baudrate: %s ---\n' % (e,))
+                            self.serial.baudrate = backup
+                        else:
+                            self.dump_port_settings()
+                        console.setup()
+                    elif c == '8':                          # 8 -> change to 8 bits
+                        self.serial.bytesize = serial.EIGHTBITS
+                        self.dump_port_settings()
+                    elif c == '7':                          # 7 -> change to 8 bits
+                        self.serial.bytesize = serial.SEVENBITS
+                        self.dump_port_settings()
+                    elif c in 'eE':                         # E -> change to even parity
+                        self.serial.parity = serial.PARITY_EVEN
+                        self.dump_port_settings()
+                    elif c in 'oO':                         # O -> change to odd parity
+                        self.serial.parity = serial.PARITY_ODD
+                        self.dump_port_settings()
+                    elif c in 'mM':                         # M -> change to mark parity
+                        self.serial.parity = serial.PARITY_MARK
+                        self.dump_port_settings()
+                    elif c in 'sS':                         # S -> change to space parity
+                        self.serial.parity = serial.PARITY_SPACE
+                        self.dump_port_settings()
+                    elif c in 'nN':                         # N -> change to no parity
+                        self.serial.parity = serial.PARITY_NONE
+                        self.dump_port_settings()
+                    elif c == '1':                          # 1 -> change to 1 stop bits
+                        self.serial.stopbits = serial.STOPBITS_ONE
+                        self.dump_port_settings()
+                    elif c == '2':                          # 2 -> change to 2 stop bits
+                        self.serial.stopbits = serial.STOPBITS_TWO
+                        self.dump_port_settings()
+                    elif c == '3':                          # 3 -> change to 1.5 stop bits
+                        self.serial.stopbits = serial.STOPBITS_ONE_POINT_FIVE
+                        self.dump_port_settings()
+                    elif c in 'xX':                         # X -> change software flow control
+                        self.serial.xonxoff = (c == 'X')
+                        self.dump_port_settings()
+                    elif c in 'rR':                         # R -> change hardware flow control
+                        self.serial.rtscts = (c == 'R')
+                        self.dump_port_settings()
+                    else:
+                        sys.stderr.write('--- unknown menu character %s --\n' % key_description(c))
+                    menu_active = False
+                elif c == MENUCHARACTER: # next char will be for menu
+                    menu_active = True
+                elif c == EXITCHARCTER: 
+                    self.stop()
+                    break                                   # exit app
+                elif c == '\n':
+                    self.serial.write(self.newline)         # send newline character(s)
+                    if self.echo:
+                        sys.stdout.write(c)                 # local echo is a real newline in any case
+                        sys.stdout.flush()
+                else:
+                    self.serial.write(b)                    # send byte
+                    if self.echo:
+                        sys.stdout.write(c)
+                        sys.stdout.flush()
+        except:
+            self.alive = False
+            raise
+
+def main():
+    import optparse
+
+    parser = optparse.OptionParser(
+        usage = "%prog [options] [port [baudrate]]",
+        description = "Miniterm - A simple terminal program for the serial port."
+    )
+
+    group = optparse.OptionGroup(parser, "Port settings")
+
+    group.add_option("-p", "--port",
+        dest = "port",
+        help = "port, a number or a device name. (deprecated option, use parameter instead)",
+        default = DEFAULT_PORT
+    )
+
+    group.add_option("-b", "--baud",
+        dest = "baudrate",
+        action = "store",
+        type = 'int',
+        help = "set baud rate, default %default",
+        default = DEFAULT_BAUDRATE
+    )
+
+    group.add_option("--parity",
+        dest = "parity",
+        action = "store",
+        help = "set parity, one of [N, E, O, S, M], default=N",
+        default = 'N'
+    )
+
+    group.add_option("--rtscts",
+        dest = "rtscts",
+        action = "store_true",
+        help = "enable RTS/CTS flow control (default off)",
+        default = False
+    )
+
+    group.add_option("--xonxoff",
+        dest = "xonxoff",
+        action = "store_true",
+        help = "enable software flow control (default off)",
+        default = False
+    )
+
+    group.add_option("--rts",
+        dest = "rts_state",
+        action = "store",
+        type = 'int',
+        help = "set initial RTS line state (possible values: 0, 1)",
+        default = DEFAULT_RTS
+    )
+
+    group.add_option("--dtr",
+        dest = "dtr_state",
+        action = "store",
+        type = 'int',
+        help = "set initial DTR line state (possible values: 0, 1)",
+        default = DEFAULT_DTR
+    )
+
+    parser.add_option_group(group)
+
+    group = optparse.OptionGroup(parser, "Data handling")
+
+    group.add_option("-e", "--echo",
+        dest = "echo",
+        action = "store_true",
+        help = "enable local echo (default off)",
+        default = False
+    )
+
+    group.add_option("--cr",
+        dest = "cr",
+        action = "store_true",
+        help = "do not send CR+LF, send CR only",
+        default = False
+    )
+
+    group.add_option("--lf",
+        dest = "lf",
+        action = "store_true",
+        help = "do not send CR+LF, send LF only",
+        default = False
+    )
+
+    group.add_option("-D", "--debug",
+        dest = "repr_mode",
+        action = "count",
+        help = """debug received data (escape non-printable chars)
+--debug can be given multiple times:
+0: just print what is received
+1: escape non-printable characters, do newlines as unusual
+2: escape non-printable characters, newlines too
+3: hex dump everything""",
+        default = 0
+    )
+
+    parser.add_option_group(group)
+
+
+    group = optparse.OptionGroup(parser, "Hotkeys")
+
+    group.add_option("--exit-char",
+        dest = "exit_char",
+        action = "store",
+        type = 'int',
+        help = "ASCII code of special character that is used to exit the application",
+        default = 0x1d
+    )
+
+    group.add_option("--menu-char",
+        dest = "menu_char",
+        action = "store",
+        type = 'int',
+        help = "ASCII code of special character that is used to control miniterm (menu)",
+        default = 0x14
+    )
+
+    parser.add_option_group(group)
+
+    group = optparse.OptionGroup(parser, "Diagnostics")
+
+    group.add_option("-q", "--quiet",
+        dest = "quiet",
+        action = "store_true",
+        help = "suppress non-error messages",
+        default = False
+    )
+
+    parser.add_option_group(group)
+
+
+    (options, args) = parser.parse_args()
+
+    options.parity = options.parity.upper()
+    if options.parity not in 'NEOSM':
+        parser.error("invalid parity")
+
+    if options.cr and options.lf:
+        parser.error("only one of --cr or --lf can be specified")
+
+    if options.menu_char == options.exit_char:
+        parser.error('--exit-char can not be the same as --menu-char')
+
+    global EXITCHARCTER, MENUCHARACTER
+    EXITCHARCTER = chr(options.exit_char)
+    MENUCHARACTER = chr(options.menu_char)
+
+    port = options.port
+    baudrate = options.baudrate
+    if args:
+        if options.port is not None:
+            parser.error("no arguments are allowed, options only when --port is given")
+        port = args.pop(0)
+        if args:
+            try:
+                baudrate = int(args[0])
+            except ValueError:
+                parser.error("baud rate must be a number, not %r" % args[0])
+            args.pop(0)
+        if args:
+            parser.error("too many arguments")
+    else:
+        # noport given on command line -> ask user now
+        if port is None:
+            dump_port_list()
+            port = raw_input('Enter port name:')
+
+    convert_outgoing = CONVERT_CRLF
+    if options.cr:
+        convert_outgoing = CONVERT_CR
+    elif options.lf:
+        convert_outgoing = CONVERT_LF
+
+    try:
+        miniterm = Miniterm(
+            port,
+            baudrate,
+            options.parity,
+            rtscts=options.rtscts,
+            xonxoff=options.xonxoff,
+            echo=options.echo,
+            convert_outgoing=convert_outgoing,
+            repr_mode=options.repr_mode,
+        )
+    except serial.SerialException, e:
+        sys.stderr.write("could not open port %r: %s\n" % (port, e))
+        sys.exit(1)
+
+    if not options.quiet:
+        sys.stderr.write('--- Miniterm on %s: %d,%s,%s,%s ---\n' % (
+            miniterm.serial.portstr,
+            miniterm.serial.baudrate,
+            miniterm.serial.bytesize,
+            miniterm.serial.parity,
+            miniterm.serial.stopbits,
+        ))
+        sys.stderr.write('--- Quit: %s  |  Menu: %s | Help: %s followed by %s ---\n' % (
+            key_description(EXITCHARCTER),
+            key_description(MENUCHARACTER),
+            key_description(MENUCHARACTER),
+            key_description('\x08'),
+        ))
+
+    if options.dtr_state is not None:
+        if not options.quiet:
+            sys.stderr.write('--- forcing DTR %s\n' % (options.dtr_state and 'active' or 'inactive'))
+        miniterm.serial.setDTR(options.dtr_state)
+        miniterm.dtr_state = options.dtr_state
+    if options.rts_state is not None:
+        if not options.quiet:
+            sys.stderr.write('--- forcing RTS %s\n' % (options.rts_state and 'active' or 'inactive'))
+        miniterm.serial.setRTS(options.rts_state)
+        miniterm.rts_state = options.rts_state
+
+    console.setup()
+    miniterm.start()
+    try:
+        miniterm.join(True)
+    except KeyboardInterrupt:
+        pass
+    if not options.quiet:
+        sys.stderr.write("\n--- exit ---\n")
+    miniterm.join()
+    #~ console.cleanup()
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+if __name__ == '__main__':
+    main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/urlhandler/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/urlhandler/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/urlhandler/__init__.py
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/urlhandler/protocol_hwgrep.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/urlhandler/protocol_hwgrep.py
new file mode 100644
index 0000000..62cda43
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/urlhandler/protocol_hwgrep.py
@@ -0,0 +1,45 @@
+#! python
+#
+# Python Serial Port Extension for Win32, Linux, BSD, Jython
+# see __init__.py
+#
+# This module implements a special URL handler that uses the port listing to
+# find ports by searching the string descriptions.
+#
+# (C) 2011 Chris Liechti <cliechti@gmx.net>
+# this is distributed under a free software license, see license.txt
+#
+# URL format:    hwgrep://regexp
+
+import serial
+import serial.tools.list_ports
+
+class Serial(serial.Serial):
+    """Just inherit the native Serial port implementation and patch the open function."""
+
+    def setPort(self, value):
+        """translate port name before storing it"""
+        if isinstance(value, basestring) and value.startswith('hwgrep://'):
+            serial.Serial.setPort(self, self.fromURL(value))
+        else:
+            serial.Serial.setPort(self, value)
+
+    def fromURL(self, url):
+        """extract host and port from an URL string"""
+        if url.lower().startswith("hwgrep://"): url = url[9:]
+        # use a for loop to get the 1st element from the generator
+        for port, desc, hwid in serial.tools.list_ports.grep(url):
+            return port
+        else:
+            raise serial.SerialException('no ports found matching regexp %r' % (url,))
+
+    # override property
+    port = property(serial.Serial.getPort, setPort, doc="Port setting")
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+if __name__ == '__main__':
+    #~ s = Serial('hwgrep://ttyS0')
+    s = Serial(None)
+    s.port = 'hwgrep://ttyS0'
+    print s
+
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/urlhandler/protocol_loop.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/urlhandler/protocol_loop.py
new file mode 100644
index 0000000..7da94ad
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/urlhandler/protocol_loop.py
@@ -0,0 +1,265 @@
+#! python
+#
+# Python Serial Port Extension for Win32, Linux, BSD, Jython
+# see __init__.py
+#
+# This module implements a loop back connection receiving itself what it sent.
+#
+# The purpose of this module is.. well... You can run the unit tests with it.
+# and it was so easy to implement ;-)
+#
+# (C) 2001-2011 Chris Liechti <cliechti@gmx.net>
+# this is distributed under a free software license, see license.txt
+#
+# URL format:    loop://[option[/option...]]
+# options:
+# - "debug" print diagnostic messages
+
+from serial.serialutil import *
+import threading
+import time
+import logging
+
+# map log level names to constants. used in fromURL()
+LOGGER_LEVELS = {
+    'debug': logging.DEBUG,
+    'info': logging.INFO,
+    'warning': logging.WARNING,
+    'error': logging.ERROR,
+    }
+
+
+class LoopbackSerial(SerialBase):
+    """Serial port implementation that simulates a loop back connection in plain software."""
+
+    BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+                 9600, 19200, 38400, 57600, 115200)
+
+    def open(self):
+        """Open port with current settings. This may throw a SerialException
+           if the port cannot be opened."""
+        if self._isOpen:
+            raise SerialException("Port is already open.")
+        self.logger = None
+        self.buffer_lock = threading.Lock()
+        self.loop_buffer = bytearray()
+        self.cts = False
+        self.dsr = False
+
+        if self._port is None:
+            raise SerialException("Port must be configured before it can be used.")
+        # not that there is anything to open, but the function applies the
+        # options found in the URL
+        self.fromURL(self.port)
+
+        # not that there anything to configure...
+        self._reconfigurePort()
+        # all things set up get, now a clean start
+        self._isOpen = True
+        if not self._rtscts:
+            self.setRTS(True)
+            self.setDTR(True)
+        self.flushInput()
+        self.flushOutput()
+
+    def _reconfigurePort(self):
+        """Set communication parameters on opened port. for the loop://
+        protocol all settings are ignored!"""
+        # not that's it of any real use, but it helps in the unit tests
+        if not isinstance(self._baudrate, (int, long)) or not 0 < self._baudrate < 2**32:
+            raise ValueError("invalid baudrate: %r" % (self._baudrate))
+        if self.logger:
+            self.logger.info('_reconfigurePort()')
+
+    def close(self):
+        """Close port"""
+        if self._isOpen:
+            self._isOpen = False
+            # in case of quick reconnects, give the server some time
+            time.sleep(0.3)
+
+    def makeDeviceName(self, port):
+        raise SerialException("there is no sensible way to turn numbers into URLs")
+
+    def fromURL(self, url):
+        """extract host and port from an URL string"""
+        if url.lower().startswith("loop://"): url = url[7:]
+        try:
+            # process options now, directly altering self
+            for option in url.split('/'):
+                if '=' in option:
+                    option, value = option.split('=', 1)
+                else:
+                    value = None
+                if not option:
+                    pass
+                elif option == 'logging':
+                    logging.basicConfig()   # XXX is that good to call it here?
+                    self.logger = logging.getLogger('pySerial.loop')
+                    self.logger.setLevel(LOGGER_LEVELS[value])
+                    self.logger.debug('enabled logging')
+                else:
+                    raise ValueError('unknown option: %r' % (option,))
+        except ValueError, e:
+            raise SerialException('expected a string in the form "[loop://][option[/option...]]": %s' % e)
+
+    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
+
+    def inWaiting(self):
+        """Return the number of characters currently in the input buffer."""
+        if not self._isOpen: raise portNotOpenError
+        if self.logger:
+            # attention the logged value can differ from return value in
+            # threaded environments...
+            self.logger.debug('inWaiting() -> %d' % (len(self.loop_buffer),))
+        return len(self.loop_buffer)
+
+    def read(self, size=1):
+        """Read size bytes from the serial port. If a timeout is set it may
+        return less characters as requested. With no timeout it will block
+        until the requested number of bytes is read."""
+        if not self._isOpen: raise portNotOpenError
+        if self._timeout is not None:
+            timeout = time.time() + self._timeout
+        else:
+            timeout = None
+        data = bytearray()
+        while size > 0:
+            self.buffer_lock.acquire()
+            try:
+                block = to_bytes(self.loop_buffer[:size])
+                del self.loop_buffer[:size]
+            finally:
+                self.buffer_lock.release()
+            data += block
+            size -= len(block)
+            # check for timeout now, after data has been read.
+            # useful for timeout = 0 (non blocking) read
+            if timeout and time.time() > timeout:
+                break
+        return bytes(data)
+
+    def write(self, data):
+        """Output the given string over the serial port. Can block if the
+        connection is blocked. May raise SerialException if the connection is
+        closed."""
+        if not self._isOpen: raise portNotOpenError
+        # ensure we're working with bytes
+        data = to_bytes(data)
+        # calculate aprox time that would be used to send the data
+        time_used_to_send = 10.0*len(data) / self._baudrate
+        # when a write timeout is configured check if we would be successful
+        # (not sending anything, not even the part that would have time)
+        if self._writeTimeout is not None and time_used_to_send > self._writeTimeout:
+            time.sleep(self._writeTimeout) # must wait so that unit test succeeds
+            raise writeTimeoutError
+        self.buffer_lock.acquire()
+        try:
+            self.loop_buffer += data
+        finally:
+            self.buffer_lock.release()
+        return len(data)
+
+    def flushInput(self):
+        """Clear input buffer, discarding all that is in the buffer."""
+        if not self._isOpen: raise portNotOpenError
+        if self.logger:
+            self.logger.info('flushInput()')
+        self.buffer_lock.acquire()
+        try:
+            del self.loop_buffer[:]
+        finally:
+            self.buffer_lock.release()
+
+    def flushOutput(self):
+        """Clear output buffer, aborting the current output and
+        discarding all that is in the buffer."""
+        if not self._isOpen: raise portNotOpenError
+        if self.logger:
+            self.logger.info('flushOutput()')
+
+    def sendBreak(self, duration=0.25):
+        """Send break condition. Timed, returns to idle state after given
+        duration."""
+        if not self._isOpen: raise portNotOpenError
+
+    def setBreak(self, level=True):
+        """Set break: Controls TXD. When active, to transmitting is
+        possible."""
+        if not self._isOpen: raise portNotOpenError
+        if self.logger:
+            self.logger.info('setBreak(%r)' % (level,))
+
+    def setRTS(self, level=True):
+        """Set terminal status line: Request To Send"""
+        if not self._isOpen: raise portNotOpenError
+        if self.logger:
+            self.logger.info('setRTS(%r) -> state of CTS' % (level,))
+        self.cts = level
+
+    def setDTR(self, level=True):
+        """Set terminal status line: Data Terminal Ready"""
+        if not self._isOpen: raise portNotOpenError
+        if self.logger:
+            self.logger.info('setDTR(%r) -> state of DSR' % (level,))
+        self.dsr = level
+
+    def getCTS(self):
+        """Read terminal status line: Clear To Send"""
+        if not self._isOpen: raise portNotOpenError
+        if self.logger:
+            self.logger.info('getCTS() -> state of RTS (%r)' % (self.cts,))
+        return self.cts
+
+    def getDSR(self):
+        """Read terminal status line: Data Set Ready"""
+        if not self._isOpen: raise portNotOpenError
+        if self.logger:
+            self.logger.info('getDSR() -> state of DTR (%r)' % (self.dsr,))
+        return self.dsr
+
+    def getRI(self):
+        """Read terminal status line: Ring Indicator"""
+        if not self._isOpen: raise portNotOpenError
+        if self.logger:
+            self.logger.info('returning dummy for getRI()')
+        return False
+
+    def getCD(self):
+        """Read terminal status line: Carrier Detect"""
+        if not self._isOpen: raise portNotOpenError
+        if self.logger:
+            self.logger.info('returning dummy for getCD()')
+        return True
+
+    # - - - platform specific - - -
+    # None so far
+
+
+# assemble Serial class with the platform specific implementation and the base
+# for file-like behavior. for Python 2.6 and newer, that provide the new I/O
+# library, derive from io.RawIOBase
+try:
+    import io
+except ImportError:
+    # classic version with our own file-like emulation
+    class Serial(LoopbackSerial, FileLike):
+        pass
+else:
+    # io library present
+    class Serial(LoopbackSerial, io.RawIOBase):
+        pass
+
+
+# simple client test
+if __name__ == '__main__':
+    import sys
+    s = Serial('loop://')
+    sys.stdout.write('%s\n' % s)
+
+    sys.stdout.write("write...\n")
+    s.write("hello\n")
+    s.flush()
+    sys.stdout.write("read: %s\n" % s.read(5))
+
+    s.close()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/urlhandler/protocol_rfc2217.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/urlhandler/protocol_rfc2217.py
new file mode 100644
index 0000000..981ba45
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/urlhandler/protocol_rfc2217.py
@@ -0,0 +1,11 @@
+#! python
+#
+# Python Serial Port Extension for Win32, Linux, BSD, Jython
+# see ../__init__.py
+#
+# This is a thin wrapper to load the rfc2271 implementation.
+#
+# (C) 2011 Chris Liechti <cliechti@gmx.net>
+# this is distributed under a free software license, see license.txt
+
+from serial.rfc2217 import Serial
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/urlhandler/protocol_socket.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/urlhandler/protocol_socket.py
new file mode 100644
index 0000000..c90a8e4
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/urlhandler/protocol_socket.py
@@ -0,0 +1,274 @@
+#! python
+#
+# Python Serial Port Extension for Win32, Linux, BSD, Jython
+# see __init__.py
+#
+# This module implements a simple socket based client.
+# It does not support changing any port parameters and will silently ignore any
+# requests to do so.
+#
+# The purpose of this module is that applications using pySerial can connect to
+# TCP/IP to serial port converters that do not support RFC 2217.
+#
+# (C) 2001-2011 Chris Liechti <cliechti@gmx.net>
+# this is distributed under a free software license, see license.txt
+#
+# URL format:    socket://<host>:<port>[/option[/option...]]
+# options:
+# - "debug" print diagnostic messages
+
+from serial.serialutil import *
+import time
+import socket
+import logging
+
+# map log level names to constants. used in fromURL()
+LOGGER_LEVELS = {
+    'debug': logging.DEBUG,
+    'info': logging.INFO,
+    'warning': logging.WARNING,
+    'error': logging.ERROR,
+    }
+
+POLL_TIMEOUT = 2
+
+class SocketSerial(SerialBase):
+    """Serial port implementation for plain sockets."""
+
+    BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+                 9600, 19200, 38400, 57600, 115200)
+
+    def open(self):
+        """Open port with current settings. This may throw a SerialException
+           if the port cannot be opened."""
+        self.logger = None
+        if self._port is None:
+            raise SerialException("Port must be configured before it can be used.")
+        if self._isOpen:
+            raise SerialException("Port is already open.")
+        try:
+            # XXX in future replace with create_connection (py >=2.6)
+            self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+            self._socket.connect(self.fromURL(self.portstr))
+        except Exception, msg:
+            self._socket = None
+            raise SerialException("Could not open port %s: %s" % (self.portstr, msg))
+
+        self._socket.settimeout(POLL_TIMEOUT) # used for write timeout support :/
+
+        # not that there anything to configure...
+        self._reconfigurePort()
+        # all things set up get, now a clean start
+        self._isOpen = True
+        if not self._rtscts:
+            self.setRTS(True)
+            self.setDTR(True)
+        self.flushInput()
+        self.flushOutput()
+
+    def _reconfigurePort(self):
+        """Set communication parameters on opened port. for the socket://
+        protocol all settings are ignored!"""
+        if self._socket is None:
+            raise SerialException("Can only operate on open ports")
+        if self.logger:
+            self.logger.info('ignored port configuration change')
+
+    def close(self):
+        """Close port"""
+        if self._isOpen:
+            if self._socket:
+                try:
+                    self._socket.shutdown(socket.SHUT_RDWR)
+                    self._socket.close()
+                except:
+                    # ignore errors.
+                    pass
+                self._socket = None
+            self._isOpen = False
+            # in case of quick reconnects, give the server some time
+            time.sleep(0.3)
+
+    def makeDeviceName(self, port):
+        raise SerialException("there is no sensible way to turn numbers into URLs")
+
+    def fromURL(self, url):
+        """extract host and port from an URL string"""
+        if url.lower().startswith("socket://"): url = url[9:]
+        try:
+            # is there a "path" (our options)?
+            if '/' in url:
+                # cut away options
+                url, options = url.split('/', 1)
+                # process options now, directly altering self
+                for option in options.split('/'):
+                    if '=' in option:
+                        option, value = option.split('=', 1)
+                    else:
+                        value = None
+                    if option == 'logging':
+                        logging.basicConfig()   # XXX is that good to call it here?
+                        self.logger = logging.getLogger('pySerial.socket')
+                        self.logger.setLevel(LOGGER_LEVELS[value])
+                        self.logger.debug('enabled logging')
+                    else:
+                        raise ValueError('unknown option: %r' % (option,))
+            # get host and port
+            host, port = url.split(':', 1) # may raise ValueError because of unpacking
+            port = int(port)               # and this if it's not a number
+            if not 0 <= port < 65536: raise ValueError("port not in range 0...65535")
+        except ValueError, e:
+            raise SerialException('expected a string in the form "[rfc2217://]<host>:<port>[/option[/option...]]": %s' % e)
+        return (host, port)
+
+    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
+
+    def inWaiting(self):
+        """Return the number of characters currently in the input buffer."""
+        if not self._isOpen: raise portNotOpenError
+        if self.logger:
+            # set this one to debug as the function could be called often...
+            self.logger.debug('WARNING: inWaiting returns dummy value')
+        return 0 # hmmm, see comment in read()
+
+    def read(self, size=1):
+        """Read size bytes from the serial port. If a timeout is set it may
+        return less characters as requested. With no timeout it will block
+        until the requested number of bytes is read."""
+        if not self._isOpen: raise portNotOpenError
+        data = bytearray()
+        if self._timeout is not None:
+            timeout = time.time() + self._timeout
+        else:
+            timeout = None
+        while len(data) < size and (timeout is None or time.time() < timeout):
+            try:
+                # an implementation with internal buffer would be better
+                # performing...
+                t = time.time()
+                block = self._socket.recv(size - len(data))
+                duration = time.time() - t
+                if block:
+                    data.extend(block)
+                else:
+                    # no data -> EOF (connection probably closed)
+                    break
+            except socket.timeout:
+                # just need to get out of recv from time to time to check if
+                # still alive
+                continue
+            except socket.error, e:
+                # connection fails -> terminate loop
+                raise SerialException('connection failed (%s)' % e)
+        return bytes(data)
+
+    def write(self, data):
+        """Output the given string over the serial port. Can block if the
+        connection is blocked. May raise SerialException if the connection is
+        closed."""
+        if not self._isOpen: raise portNotOpenError
+        try:
+            self._socket.sendall(to_bytes(data))
+        except socket.error, e:
+            # XXX what exception if socket connection fails
+            raise SerialException("socket connection failed: %s" % e)
+        return len(data)
+
+    def flushInput(self):
+        """Clear input buffer, discarding all that is in the buffer."""
+        if not self._isOpen: raise portNotOpenError
+        if self.logger:
+            self.logger.info('ignored flushInput')
+
+    def flushOutput(self):
+        """Clear output buffer, aborting the current output and
+        discarding all that is in the buffer."""
+        if not self._isOpen: raise portNotOpenError
+        if self.logger:
+            self.logger.info('ignored flushOutput')
+
+    def sendBreak(self, duration=0.25):
+        """Send break condition. Timed, returns to idle state after given
+        duration."""
+        if not self._isOpen: raise portNotOpenError
+        if self.logger:
+            self.logger.info('ignored sendBreak(%r)' % (duration,))
+
+    def setBreak(self, level=True):
+        """Set break: Controls TXD. When active, to transmitting is
+        possible."""
+        if not self._isOpen: raise portNotOpenError
+        if self.logger:
+            self.logger.info('ignored setBreak(%r)' % (level,))
+
+    def setRTS(self, level=True):
+        """Set terminal status line: Request To Send"""
+        if not self._isOpen: raise portNotOpenError
+        if self.logger:
+            self.logger.info('ignored setRTS(%r)' % (level,))
+
+    def setDTR(self, level=True):
+        """Set terminal status line: Data Terminal Ready"""
+        if not self._isOpen: raise portNotOpenError
+        if self.logger:
+            self.logger.info('ignored setDTR(%r)' % (level,))
+
+    def getCTS(self):
+        """Read terminal status line: Clear To Send"""
+        if not self._isOpen: raise portNotOpenError
+        if self.logger:
+            self.logger.info('returning dummy for getCTS()')
+        return True
+
+    def getDSR(self):
+        """Read terminal status line: Data Set Ready"""
+        if not self._isOpen: raise portNotOpenError
+        if self.logger:
+            self.logger.info('returning dummy for getDSR()')
+        return True
+
+    def getRI(self):
+        """Read terminal status line: Ring Indicator"""
+        if not self._isOpen: raise portNotOpenError
+        if self.logger:
+            self.logger.info('returning dummy for getRI()')
+        return False
+
+    def getCD(self):
+        """Read terminal status line: Carrier Detect"""
+        if not self._isOpen: raise portNotOpenError
+        if self.logger:
+            self.logger.info('returning dummy for getCD()')
+        return True
+
+    # - - - platform specific - - -
+    # None so far
+
+
+# assemble Serial class with the platform specific implementation and the base
+# for file-like behavior. for Python 2.6 and newer, that provide the new I/O
+# library, derive from io.RawIOBase
+try:
+    import io
+except ImportError:
+    # classic version with our own file-like emulation
+    class Serial(SocketSerial, FileLike):
+        pass
+else:
+    # io library present
+    class Serial(SocketSerial, io.RawIOBase):
+        pass
+
+
+# simple client test
+if __name__ == '__main__':
+    import sys
+    s = Serial('socket://localhost:7000')
+    sys.stdout.write('%s\n' % s)
+
+    sys.stdout.write("write...\n")
+    s.write("hello\n")
+    s.flush()
+    sys.stdout.write("read: %s\n" % s.read(5))
+
+    s.close()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/win32.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/win32.py
new file mode 100644
index 0000000..61b3d7a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/pyserial/serial/win32.py
@@ -0,0 +1,320 @@
+from ctypes import *
+from ctypes.wintypes import HANDLE
+from ctypes.wintypes import BOOL
+from ctypes.wintypes import LPCWSTR
+_stdcall_libraries = {}
+_stdcall_libraries['kernel32'] = WinDLL('kernel32')
+from ctypes.wintypes import DWORD
+from ctypes.wintypes import WORD
+from ctypes.wintypes import BYTE
+
+INVALID_HANDLE_VALUE = HANDLE(-1).value
+
+# some details of the windows API differ between 32 and 64 bit systems..
+def is_64bit():
+    """Returns true when running on a 64 bit system"""
+    return sizeof(c_ulong) != sizeof(c_void_p)
+
+# ULONG_PTR is a an ordinary number, not a pointer and contrary to the name it
+# is either 32 or 64 bits, depending on the type of windows...
+# so test if this a 32 bit windows...
+if is_64bit():
+    # assume 64 bits
+    ULONG_PTR = c_int64
+else:
+    # 32 bits
+    ULONG_PTR = c_ulong
+
+
+class _SECURITY_ATTRIBUTES(Structure):
+    pass
+LPSECURITY_ATTRIBUTES = POINTER(_SECURITY_ATTRIBUTES)
+
+
+try:
+    CreateEventW = _stdcall_libraries['kernel32'].CreateEventW
+except AttributeError:
+    # Fallback to non wide char version for old OS...
+    from ctypes.wintypes import LPCSTR
+    CreateEventA = _stdcall_libraries['kernel32'].CreateEventA
+    CreateEventA.restype = HANDLE
+    CreateEventA.argtypes = [LPSECURITY_ATTRIBUTES, BOOL, BOOL, LPCSTR]
+    CreateEvent=CreateEventA
+
+    CreateFileA = _stdcall_libraries['kernel32'].CreateFileA
+    CreateFileA.restype = HANDLE
+    CreateFileA.argtypes = [LPCSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE]
+    CreateFile = CreateFileA
+else:
+    CreateEventW.restype = HANDLE
+    CreateEventW.argtypes = [LPSECURITY_ATTRIBUTES, BOOL, BOOL, LPCWSTR]
+    CreateEvent = CreateEventW # alias
+
+    CreateFileW = _stdcall_libraries['kernel32'].CreateFileW
+    CreateFileW.restype = HANDLE
+    CreateFileW.argtypes = [LPCWSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE]
+    CreateFile = CreateFileW # alias
+
+class _OVERLAPPED(Structure):
+    pass
+OVERLAPPED = _OVERLAPPED
+
+class _COMSTAT(Structure):
+    pass
+COMSTAT = _COMSTAT
+
+class _DCB(Structure):
+    pass
+DCB = _DCB
+
+class _COMMTIMEOUTS(Structure):
+    pass
+COMMTIMEOUTS = _COMMTIMEOUTS
+
+GetLastError = _stdcall_libraries['kernel32'].GetLastError
+GetLastError.restype = DWORD
+GetLastError.argtypes = []
+
+LPOVERLAPPED = POINTER(_OVERLAPPED)
+LPDWORD = POINTER(DWORD)
+
+GetOverlappedResult = _stdcall_libraries['kernel32'].GetOverlappedResult
+GetOverlappedResult.restype = BOOL
+GetOverlappedResult.argtypes = [HANDLE, LPOVERLAPPED, LPDWORD, BOOL]
+
+ResetEvent = _stdcall_libraries['kernel32'].ResetEvent
+ResetEvent.restype = BOOL
+ResetEvent.argtypes = [HANDLE]
+
+LPCVOID = c_void_p
+
+WriteFile = _stdcall_libraries['kernel32'].WriteFile
+WriteFile.restype = BOOL
+WriteFile.argtypes = [HANDLE, LPCVOID, DWORD, LPDWORD, LPOVERLAPPED]
+
+LPVOID = c_void_p
+
+ReadFile = _stdcall_libraries['kernel32'].ReadFile
+ReadFile.restype = BOOL
+ReadFile.argtypes = [HANDLE, LPVOID, DWORD, LPDWORD, LPOVERLAPPED]
+
+CloseHandle = _stdcall_libraries['kernel32'].CloseHandle
+CloseHandle.restype = BOOL
+CloseHandle.argtypes = [HANDLE]
+
+ClearCommBreak = _stdcall_libraries['kernel32'].ClearCommBreak
+ClearCommBreak.restype = BOOL
+ClearCommBreak.argtypes = [HANDLE]
+
+LPCOMSTAT = POINTER(_COMSTAT)
+
+ClearCommError = _stdcall_libraries['kernel32'].ClearCommError
+ClearCommError.restype = BOOL
+ClearCommError.argtypes = [HANDLE, LPDWORD, LPCOMSTAT]
+
+SetupComm = _stdcall_libraries['kernel32'].SetupComm
+SetupComm.restype = BOOL
+SetupComm.argtypes = [HANDLE, DWORD, DWORD]
+
+EscapeCommFunction = _stdcall_libraries['kernel32'].EscapeCommFunction
+EscapeCommFunction.restype = BOOL
+EscapeCommFunction.argtypes = [HANDLE, DWORD]
+
+GetCommModemStatus = _stdcall_libraries['kernel32'].GetCommModemStatus
+GetCommModemStatus.restype = BOOL
+GetCommModemStatus.argtypes = [HANDLE, LPDWORD]
+
+LPDCB = POINTER(_DCB)
+
+GetCommState = _stdcall_libraries['kernel32'].GetCommState
+GetCommState.restype = BOOL
+GetCommState.argtypes = [HANDLE, LPDCB]
+
+LPCOMMTIMEOUTS = POINTER(_COMMTIMEOUTS)
+
+GetCommTimeouts = _stdcall_libraries['kernel32'].GetCommTimeouts
+GetCommTimeouts.restype = BOOL
+GetCommTimeouts.argtypes = [HANDLE, LPCOMMTIMEOUTS]
+
+PurgeComm = _stdcall_libraries['kernel32'].PurgeComm
+PurgeComm.restype = BOOL
+PurgeComm.argtypes = [HANDLE, DWORD]
+
+SetCommBreak = _stdcall_libraries['kernel32'].SetCommBreak
+SetCommBreak.restype = BOOL
+SetCommBreak.argtypes = [HANDLE]
+
+SetCommMask = _stdcall_libraries['kernel32'].SetCommMask
+SetCommMask.restype = BOOL
+SetCommMask.argtypes = [HANDLE, DWORD]
+
+SetCommState = _stdcall_libraries['kernel32'].SetCommState
+SetCommState.restype = BOOL
+SetCommState.argtypes = [HANDLE, LPDCB]
+
+SetCommTimeouts = _stdcall_libraries['kernel32'].SetCommTimeouts
+SetCommTimeouts.restype = BOOL
+SetCommTimeouts.argtypes = [HANDLE, LPCOMMTIMEOUTS]
+
+WaitForSingleObject = _stdcall_libraries['kernel32'].WaitForSingleObject
+WaitForSingleObject.restype = DWORD
+WaitForSingleObject.argtypes = [HANDLE, DWORD]
+
+ONESTOPBIT = 0 # Variable c_int
+TWOSTOPBITS = 2 # Variable c_int
+ONE5STOPBITS = 1
+
+NOPARITY = 0 # Variable c_int
+ODDPARITY = 1 # Variable c_int
+EVENPARITY = 2 # Variable c_int
+MARKPARITY = 3
+SPACEPARITY = 4
+
+RTS_CONTROL_HANDSHAKE = 2 # Variable c_int
+RTS_CONTROL_DISABLE = 0 # Variable c_int
+RTS_CONTROL_ENABLE = 1 # Variable c_int
+RTS_CONTROL_TOGGLE = 3 # Variable c_int
+SETRTS = 3
+CLRRTS = 4
+
+DTR_CONTROL_HANDSHAKE = 2 # Variable c_int
+DTR_CONTROL_DISABLE = 0 # Variable c_int
+DTR_CONTROL_ENABLE = 1 # Variable c_int
+SETDTR = 5
+CLRDTR = 6
+
+MS_DSR_ON = 32 # Variable c_ulong
+EV_RING = 256 # Variable c_int
+EV_PERR = 512 # Variable c_int
+EV_ERR = 128 # Variable c_int
+SETXOFF = 1 # Variable c_int
+EV_RXCHAR = 1 # Variable c_int
+GENERIC_WRITE = 1073741824 # Variable c_long
+PURGE_TXCLEAR = 4 # Variable c_int
+FILE_FLAG_OVERLAPPED = 1073741824 # Variable c_int
+EV_DSR = 16 # Variable c_int
+MAXDWORD = 4294967295L # Variable c_uint
+EV_RLSD = 32 # Variable c_int
+ERROR_IO_PENDING = 997 # Variable c_long
+MS_CTS_ON = 16 # Variable c_ulong
+EV_EVENT1 = 2048 # Variable c_int
+EV_RX80FULL = 1024 # Variable c_int
+PURGE_RXABORT = 2 # Variable c_int
+FILE_ATTRIBUTE_NORMAL = 128 # Variable c_int
+PURGE_TXABORT = 1 # Variable c_int
+SETXON = 2 # Variable c_int
+OPEN_EXISTING = 3 # Variable c_int
+MS_RING_ON = 64 # Variable c_ulong
+EV_TXEMPTY = 4 # Variable c_int
+EV_RXFLAG = 2 # Variable c_int
+MS_RLSD_ON = 128 # Variable c_ulong
+GENERIC_READ = 2147483648L # Variable c_ulong
+EV_EVENT2 = 4096 # Variable c_int
+EV_CTS = 8 # Variable c_int
+EV_BREAK = 64 # Variable c_int
+PURGE_RXCLEAR = 8 # Variable c_int
+INFINITE = 0xFFFFFFFFL
+
+
+class N11_OVERLAPPED4DOLLAR_48E(Union):
+    pass
+class N11_OVERLAPPED4DOLLAR_484DOLLAR_49E(Structure):
+    pass
+N11_OVERLAPPED4DOLLAR_484DOLLAR_49E._fields_ = [
+    ('Offset', DWORD),
+    ('OffsetHigh', DWORD),
+]
+
+PVOID = c_void_p
+
+N11_OVERLAPPED4DOLLAR_48E._anonymous_ = ['_0']
+N11_OVERLAPPED4DOLLAR_48E._fields_ = [
+    ('_0', N11_OVERLAPPED4DOLLAR_484DOLLAR_49E),
+    ('Pointer', PVOID),
+]
+_OVERLAPPED._anonymous_ = ['_0']
+_OVERLAPPED._fields_ = [
+    ('Internal', ULONG_PTR),
+    ('InternalHigh', ULONG_PTR),
+    ('_0', N11_OVERLAPPED4DOLLAR_48E),
+    ('hEvent', HANDLE),
+]
+_SECURITY_ATTRIBUTES._fields_ = [
+    ('nLength', DWORD),
+    ('lpSecurityDescriptor', LPVOID),
+    ('bInheritHandle', BOOL),
+]
+_COMSTAT._fields_ = [
+    ('fCtsHold', DWORD, 1),
+    ('fDsrHold', DWORD, 1),
+    ('fRlsdHold', DWORD, 1),
+    ('fXoffHold', DWORD, 1),
+    ('fXoffSent', DWORD, 1),
+    ('fEof', DWORD, 1),
+    ('fTxim', DWORD, 1),
+    ('fReserved', DWORD, 25),
+    ('cbInQue', DWORD),
+    ('cbOutQue', DWORD),
+]
+_DCB._fields_ = [
+    ('DCBlength', DWORD),
+    ('BaudRate', DWORD),
+    ('fBinary', DWORD, 1),
+    ('fParity', DWORD, 1),
+    ('fOutxCtsFlow', DWORD, 1),
+    ('fOutxDsrFlow', DWORD, 1),
+    ('fDtrControl', DWORD, 2),
+    ('fDsrSensitivity', DWORD, 1),
+    ('fTXContinueOnXoff', DWORD, 1),
+    ('fOutX', DWORD, 1),
+    ('fInX', DWORD, 1),
+    ('fErrorChar', DWORD, 1),
+    ('fNull', DWORD, 1),
+    ('fRtsControl', DWORD, 2),
+    ('fAbortOnError', DWORD, 1),
+    ('fDummy2', DWORD, 17),
+    ('wReserved', WORD),
+    ('XonLim', WORD),
+    ('XoffLim', WORD),
+    ('ByteSize', BYTE),
+    ('Parity', BYTE),
+    ('StopBits', BYTE),
+    ('XonChar', c_char),
+    ('XoffChar', c_char),
+    ('ErrorChar', c_char),
+    ('EofChar', c_char),
+    ('EvtChar', c_char),
+    ('wReserved1', WORD),
+]
+_COMMTIMEOUTS._fields_ = [
+    ('ReadIntervalTimeout', DWORD),
+    ('ReadTotalTimeoutMultiplier', DWORD),
+    ('ReadTotalTimeoutConstant', DWORD),
+    ('WriteTotalTimeoutMultiplier', DWORD),
+    ('WriteTotalTimeoutConstant', DWORD),
+]
+__all__ = ['GetLastError', 'MS_CTS_ON', 'FILE_ATTRIBUTE_NORMAL',
+           'DTR_CONTROL_ENABLE', '_COMSTAT', 'MS_RLSD_ON',
+           'GetOverlappedResult', 'SETXON', 'PURGE_TXABORT',
+           'PurgeComm', 'N11_OVERLAPPED4DOLLAR_48E', 'EV_RING',
+           'ONESTOPBIT', 'SETXOFF', 'PURGE_RXABORT', 'GetCommState',
+           'RTS_CONTROL_ENABLE', '_DCB', 'CreateEvent',
+           '_COMMTIMEOUTS', '_SECURITY_ATTRIBUTES', 'EV_DSR',
+           'EV_PERR', 'EV_RXFLAG', 'OPEN_EXISTING', 'DCB',
+           'FILE_FLAG_OVERLAPPED', 'EV_CTS', 'SetupComm',
+           'LPOVERLAPPED', 'EV_TXEMPTY', 'ClearCommBreak',
+           'LPSECURITY_ATTRIBUTES', 'SetCommBreak', 'SetCommTimeouts',
+           'COMMTIMEOUTS', 'ODDPARITY', 'EV_RLSD',
+           'GetCommModemStatus', 'EV_EVENT2', 'PURGE_TXCLEAR',
+           'EV_BREAK', 'EVENPARITY', 'LPCVOID', 'COMSTAT', 'ReadFile',
+           'PVOID', '_OVERLAPPED', 'WriteFile', 'GetCommTimeouts',
+           'ResetEvent', 'EV_RXCHAR', 'LPCOMSTAT', 'ClearCommError',
+           'ERROR_IO_PENDING', 'EscapeCommFunction', 'GENERIC_READ',
+           'RTS_CONTROL_HANDSHAKE', 'OVERLAPPED',
+           'DTR_CONTROL_HANDSHAKE', 'PURGE_RXCLEAR', 'GENERIC_WRITE',
+           'LPDCB', 'CreateEventW', 'SetCommMask', 'EV_EVENT1',
+           'SetCommState', 'LPVOID', 'CreateFileW', 'LPDWORD',
+           'EV_RX80FULL', 'TWOSTOPBITS', 'LPCOMMTIMEOUTS', 'MAXDWORD',
+           'MS_DSR_ON', 'MS_RING_ON',
+           'N11_OVERLAPPED4DOLLAR_484DOLLAR_49E', 'EV_ERR',
+           'ULONG_PTR', 'CreateFile', 'NOPARITY', 'CloseHandle']
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/tsproxy/LICENSE b/sdk/platform-tools/systrace/catapult/telemetry/third_party/tsproxy/LICENSE
new file mode 100644
index 0000000..8dada3e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/tsproxy/LICENSE
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright {yyyy} {name of copyright owner}
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/tsproxy/README.chromium b/sdk/platform-tools/systrace/catapult/telemetry/third_party/tsproxy/README.chromium
new file mode 100644
index 0000000..456cc23
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/tsproxy/README.chromium
@@ -0,0 +1,13 @@
+Name: tsproxy
+Short Name: tsproxy
+URL: https://github.com/WPO-Foundation/tsproxy
+Version: a2f578febcd79b751d948f615bbde8f6189fbeed (commit hash)
+License: Apache
+License File: NOT_SHIPPED
+Security Critical: no
+
+Local modification: no
+
+Description:
+tsproxy provides basic latency, download and upload traffic shaping while only
+requiring user-level access (no root permissions required)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/tsproxy/README.md b/sdk/platform-tools/systrace/catapult/telemetry/third_party/tsproxy/README.md
new file mode 100644
index 0000000..84cc3fa
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/tsproxy/README.md
@@ -0,0 +1,62 @@
+# tsproxy
+Traffic-shaping SOCKS5 proxy
+
+tsproxy provides basic latency, download and upload traffic shaping while only requiring user-level access (no root permissions required).  It should work for basic browser testing but for protocol-level work it does not provide a suitable replacement for something like dummynet or netem.
+
+tsproxy is monolithic and all of the functionality is in tsproxy.py.  It is written expecting Python 2.7.
+
+#Usage
+```bash
+$ python tsproxy.py --rtt=<latency> --inkbps=<download bandwidth> --outkbps=<upload bandwidth>
+```
+Hit `ctrl-C` (or send a `SIGINT`) to exit
+
+#Example
+```bash
+$ python tsproxy.py --rtt=200 --inkbps=1600 --outkbps=768
+```
+
+#Command-line Options
+
+
+| Option            | Alias    | Description                              |
+| ----------------- | -------- | ---------------------------------------- |
+| **`--rtt`**       | **`-r`** | Latency in milliseconds (full round trip, half of the latency gets applied to each direction). |
+| **`--inkbps`**    | **`-i`** | Download Bandwidth (in 1000 bits/s - Kbps). |
+| **`--outkbps`**   | **`-o`** | Upload Bandwidth (in 1000 bits/s - Kbps). |
+| **`--window`**    | **`-w`** | Emulated TCP initial congestion window (defaults to 10). |
+| **`--port`**      | **`-p`** | SOCKS 5 proxy port (defaults to port 1080). Specifying a port of 0 will use a randomly assigned port. |
+| **`--bind`**      | **`-b`** | Interface address to listen on (defaults to localhost). |
+| **`--desthost`**  | **`-d`** | Redirect all outbound connections to the specified host (name or IP). |
+| **`--mapports`**  | **`-m`** | Remap outbound ports. Comma-separated list of original:new with * as a wildcard. `--mapports '443:8443,*:8080'` |
+| **`--localhost`** | **`-l`** | Include connections already destined for localhost/127.0.0.1 in the host and port remapping. |
+| **`--verbose`**   | **`-v`** | Increase verbosity (specify multiple times for more). `-vvvv` for full debug output. |
+
+
+#Runtime Options
+The traffic shaping configuration can be changed dynamically at runtime by passing commands in through the console (or stdin).  Each command is on a line, terminated with an end-of-line (`\n`).
+
+
+* **`flush`** : Flush queued data out of the pipes.  Useful for clearing out any accumulated background data between tests.
+* **`set rtt <latency>`** : Change the connection latency. i.e. `set rtt 200\n` will change to a 200ms RTT.
+* **`set inkbps <bandwidth>`** : Change the download bandwidth. i.e. `set inkbps 5000\n` will change to a 5Mbps download connection.
+* **`set outkbps <bandwidth>`** : Change the upload bandwidth. i.e. `set outkbps 1000\n` will change to a 1Mbps upload connection.
+* **`set mapports <port mapping string>`** : Change the destination port mapping.
+* **`reset all`** : Disable all port mapping and traffic shaping
+* **`reset rtt`** : Set latency to 0
+* **`reset inkbps`** : Disable download traffic shaping
+* **`reset outkbps`** : Disable upload traffic shaping
+* **`reset mapports`** : Disable destination port mapping
+
+All bandwidth and latency changes also carry an implied flush and clear out any pending data.
+
+
+#Configuring Chrome to use tsproxy
+Add a --proxy-server command-line option.
+```bash
+--proxy-server="socks://localhost:1080"
+```
+
+#Known Shortcomings/Issues
+* DNS lookups on OSX (and FreeBSD) will block each other when it comes to actually resolving.  DNS in Python on most platforms is allowed to run concurrently in threads (which tsproxy does) but on OSX and FreeBSD it is not thread-safe and there is a lock around the actual lookups.  For most cases this isn't an issue because the latency isn't added on the actual DNS lookup (it is from the browser perspective but it is added outside of the actual lookup). This is also not an issue when desthost is used to override the destination address since dns lookups will be disabled.
+* QUIC support. Chrome [doesn't currently support QUIC proxies](https://bugs.chromium.org/p/chromium/issues/detail?id=335275), and further work would be neccessary to correctly handle UDP traffic in tsproxy.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/tsproxy/tsproxy.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/tsproxy/tsproxy.py
new file mode 100644
index 0000000..e4c0c27
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/tsproxy/tsproxy.py
@@ -0,0 +1,776 @@
+#!/usr/bin/python
+"""
+Copyright 2016 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+import asyncore
+import gc
+import logging
+import platform
+import Queue
+import re
+import signal
+import socket
+import sys
+import threading
+import time
+
+server = None
+in_pipe = None
+out_pipe = None
+must_exit = False
+options = None
+dest_addresses = None
+connections = {}
+dns_cache = {}
+port_mappings = None
+map_localhost = False
+needs_flush = False
+flush_pipes = False
+last_activity = None
+REMOVE_TCP_OVERHEAD = 1460.0 / 1500.0
+lock = threading.Lock()
+background_activity_count = 0
+
+
+def PrintMessage(msg):
+  # Print the message to stdout & flush to make sure that the message is not
+  # buffered when tsproxy is run as a subprocess.
+  print >> sys.stdout, msg
+  sys.stdout.flush()
+
+########################################################################################################################
+#   Traffic-shaping pipe (just passthrough for now)
+########################################################################################################################
+class TSPipe():
+  PIPE_IN = 0
+  PIPE_OUT = 1
+
+  def __init__(self, direction, latency, kbps):
+    self.direction = direction
+    self.latency = latency
+    self.kbps = kbps
+    self.queue = Queue.Queue()
+    self.last_tick = time.clock()
+    self.next_message = None
+    self.available_bytes = .0
+    self.peer = 'server'
+    if self.direction == self.PIPE_IN:
+      self.peer = 'client'
+
+  def SendMessage(self, message, main_thread = True):
+    global connections, in_pipe, out_pipe
+    message_sent = False
+    now = time.clock()
+    if message['message'] == 'closed':
+      message['time'] = now
+    else:
+      message['time'] = time.clock() + self.latency
+    message['size'] = .0
+    if 'data' in message:
+      message['size'] = float(len(message['data']))
+    try:
+      connection_id = message['connection']
+      # Send messages directly, bypassing the queues is throttling is disabled and we are on the main thread
+      if main_thread and connection_id in connections and self.peer in connections[connection_id]and self.latency == 0 and self.kbps == .0:
+        message_sent = self.SendPeerMessage(message)
+    except:
+      pass
+    if not message_sent:
+      try:
+        self.queue.put(message)
+      except:
+        pass
+
+  def SendPeerMessage(self, message):
+    global last_activity
+    last_activity = time.clock()
+    message_sent = False
+    connection_id = message['connection']
+    if connection_id in connections:
+      if self.peer in connections[connection_id]:
+        try:
+          connections[connection_id][self.peer].handle_message(message)
+          message_sent = True
+        except:
+          # Clean up any disconnected connections
+          try:
+            connections[connection_id]['server'].close()
+          except:
+            pass
+          try:
+            connections[connection_id]['client'].close()
+          except:
+            pass
+          del connections[connection_id]
+    return message_sent
+
+  def tick(self):
+    global connections
+    global flush_pipes
+    processed_messages = False
+    now = time.clock()
+    try:
+      if self.next_message is None:
+        self.next_message = self.queue.get_nowait()
+
+      # Accumulate bandwidth if an available packet/message was waiting since our last tick
+      if self.next_message is not None and self.kbps > .0 and self.next_message['time'] <= now:
+        elapsed = now - self.last_tick
+        accumulated_bytes = elapsed * self.kbps * 1000.0 / 8.0
+        self.available_bytes += accumulated_bytes
+
+      # process messages as long as the next message is sendable (latency or available bytes)
+      while (self.next_message is not None) and\
+          (flush_pipes or ((self.next_message['time'] <= now) and
+                          (self.kbps <= .0 or self.next_message['size'] <= self.available_bytes))):
+        self.queue.task_done()
+        processed_messages = True
+        if self.kbps > .0:
+          self.available_bytes -= self.next_message['size']
+        self.SendPeerMessage(self.next_message)
+        self.next_message = None
+        self.next_message = self.queue.get_nowait()
+    except:
+      pass
+
+    # Only accumulate bytes while we have messages that are ready to send
+    if self.next_message is None or self.next_message['time'] > now:
+      self.available_bytes = .0
+    self.last_tick = now
+
+    return processed_messages
+
+
+########################################################################################################################
+#   Threaded DNS resolver
+########################################################################################################################
+class AsyncDNS(threading.Thread):
+  def __init__(self, client_id, hostname, port, result_pipe):
+    threading.Thread.__init__(self)
+    self.hostname = hostname
+    self.port = port
+    self.client_id = client_id
+    self.result_pipe = result_pipe
+
+  def run(self):
+    global lock, background_activity_count
+    try:
+      logging.debug('[{0:d}] AsyncDNS - calling getaddrinfo for {1}:{2:d}'.format(self.client_id, self.hostname, self.port))
+      addresses = socket.getaddrinfo(self.hostname, self.port)
+      logging.info('[{0:d}] Resolving {1}:{2:d} Completed'.format(self.client_id, self.hostname, self.port))
+    except:
+      addresses = ()
+      logging.info('[{0:d}] Resolving {1}:{2:d} Failed'.format(self.client_id, self.hostname, self.port))
+    message = {'message': 'resolved', 'connection': self.client_id, 'addresses': addresses}
+    self.result_pipe.SendMessage(message, False)
+    lock.acquire()
+    if background_activity_count > 0:
+      background_activity_count -= 1
+    lock.release()
+    # open and close a local socket which will interrupt the long polling loop to process the message
+    s = socket.socket()
+    s.connect((server.ipaddr, server.port))
+    s.close()
+
+
+########################################################################################################################
+#   TCP Client
+########################################################################################################################
+class TCPConnection(asyncore.dispatcher):
+  STATE_ERROR = -1
+  STATE_IDLE = 0
+  STATE_RESOLVING = 1
+  STATE_CONNECTING = 2
+  STATE_CONNECTED = 3
+
+  def __init__(self, client_id):
+    global options
+    asyncore.dispatcher.__init__(self)
+    self.client_id = client_id
+    self.state = self.STATE_IDLE
+    self.buffer = ''
+    self.addr = None
+    self.dns_thread = None
+    self.hostname = None
+    self.port = None
+    self.needs_config = True
+    self.needs_close = False
+    self.is_localhost = False
+    self.did_resolve = False
+
+  def SendMessage(self, type, message):
+    message['message'] = type
+    message['connection'] = self.client_id
+    in_pipe.SendMessage(message)
+
+  def handle_message(self, message):
+    if message['message'] == 'data' and 'data' in message and len(message['data']):
+      self.buffer += message['data']
+      if self.state == self.STATE_CONNECTED:
+        self.handle_write()
+    elif message['message'] == 'resolve':
+      self.HandleResolve(message)
+    elif message['message'] == 'connect':
+      self.HandleConnect(message)
+    elif message['message'] == 'closed':
+      if len(self.buffer) == 0:
+        self.handle_close()
+      else:
+        self.needs_close = True
+
+  def handle_error(self):
+    logging.warning('[{0:d}] Error'.format(self.client_id))
+    if self.state == self.STATE_CONNECTING:
+      self.SendMessage('connected', {'success': False, 'address': self.addr})
+
+  def handle_close(self):
+    logging.info('[{0:d}] Server Connection Closed'.format(self.client_id))
+    self.state = self.STATE_ERROR
+    self.close()
+    try:
+      if self.client_id in connections:
+        if 'server' in connections[self.client_id]:
+          del connections[self.client_id]['server']
+        if 'client' in connections[self.client_id]:
+          self.SendMessage('closed', {})
+        else:
+          del connections[self.client_id]
+    except:
+      pass
+
+  def handle_connect(self):
+    if self.state == self.STATE_CONNECTING:
+      self.state = self.STATE_CONNECTED
+      self.SendMessage('connected', {'success': True, 'address': self.addr})
+      logging.info('[{0:d}] Connected'.format(self.client_id))
+    self.handle_write()
+
+  def writable(self):
+    if self.state == self.STATE_CONNECTING:
+      return True
+    return len(self.buffer) > 0
+
+  def handle_write(self):
+    if self.needs_config:
+      self.needs_config = False
+      self.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
+      self.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 128 * 1024)
+      self.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 128 * 1024)
+    if len(self.buffer) > 0:
+      sent = self.send(self.buffer)
+      logging.debug('[{0:d}] TCP => {1:d} byte(s)'.format(self.client_id, sent))
+      self.buffer = self.buffer[sent:]
+      if self.needs_close and len(self.buffer) == 0:
+        self.needs_close = False
+        self.handle_close()
+
+  def handle_read(self):
+    try:
+      while True:
+        data = self.recv(1460)
+        if data:
+          if self.state == self.STATE_CONNECTED:
+            logging.debug('[{0:d}] TCP <= {1:d} byte(s)'.format(self.client_id, len(data)))
+            self.SendMessage('data', {'data': data})
+        else:
+          return
+    except:
+      pass
+
+  def HandleResolve(self, message):
+    global in_pipe,  map_localhost, lock, background_activity_count
+    self.did_resolve = True
+    if 'hostname' in message:
+      self.hostname = message['hostname']
+    self.port = 0
+    if 'port' in message:
+      self.port = message['port']
+    logging.info('[{0:d}] Resolving {1}:{2:d}'.format(self.client_id, self.hostname, self.port))
+    if self.hostname == 'localhost':
+      self.hostname = '127.0.0.1'
+    if self.hostname == '127.0.0.1':
+      logging.info('[{0:d}] Connection to localhost detected'.format(self.client_id))
+      self.is_localhost = True
+    if (dest_addresses is not None) and (not self.is_localhost or map_localhost):
+      logging.info('[{0:d}] Resolving {1}:{2:d} to mapped address {3}'.format(self.client_id, self.hostname, self.port, dest_addresses))
+      self.SendMessage('resolved', {'addresses': dest_addresses})
+    else:
+      lock.acquire()
+      background_activity_count += 1
+      lock.release()
+      self.state = self.STATE_RESOLVING
+      self.dns_thread = AsyncDNS(self.client_id, self.hostname, self.port, in_pipe)
+      self.dns_thread.start()
+
+  def HandleConnect(self, message):
+    global map_localhost
+    if 'addresses' in message and len(message['addresses']):
+      self.state = self.STATE_CONNECTING
+      if not self.did_resolve and message['addresses'][0] == '127.0.0.1':
+        logging.info('[{0:d}] Connection to localhost detected'.format(self.client_id))
+        self.is_localhost = True
+      if (dest_addresses is not None) and (not self.is_localhost or map_localhost):
+        self.addr = dest_addresses[0]
+      else:
+        self.addr = message['addresses'][0]
+      self.create_socket(self.addr[0], socket.SOCK_STREAM)
+      addr = self.addr[4][0]
+      if not self.is_localhost or map_localhost:
+        port = GetDestPort(message['port'])
+      else:
+        port = message['port']
+      logging.info('[{0:d}] Connecting to {1}:{2:d}'.format(self.client_id, addr, port))
+      self.connect((addr, port))
+
+
+########################################################################################################################
+#   Socks5 Server
+########################################################################################################################
+class Socks5Server(asyncore.dispatcher):
+
+  def __init__(self, host, port):
+    asyncore.dispatcher.__init__(self)
+    self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
+    try:
+      #self.set_reuse_addr()
+      self.bind((host, port))
+      self.listen(socket.SOMAXCONN)
+      self.ipaddr, self.port = self.getsockname()
+      self.current_client_id = 0
+    except:
+      PrintMessage("Unable to listen on {0}:{1}. Is the port already in use?".format(host, port))
+      exit(1)
+
+  def handle_accept(self):
+    global connections
+    pair = self.accept()
+    if pair is not None:
+      sock, addr = pair
+      self.current_client_id += 1
+      logging.info('[{0:d}] Incoming connection from {1}'.format(self.current_client_id, repr(addr)))
+      connections[self.current_client_id] = {
+        'client' : Socks5Connection(sock, self.current_client_id),
+        'server' : None
+      }
+
+
+# Socks5 reference: https://en.wikipedia.org/wiki/SOCKS#SOCKS5
+class Socks5Connection(asyncore.dispatcher):
+  STATE_ERROR = -1
+  STATE_WAITING_FOR_HANDSHAKE = 0
+  STATE_WAITING_FOR_CONNECT_REQUEST = 1
+  STATE_RESOLVING = 2
+  STATE_CONNECTING = 3
+  STATE_CONNECTED = 4
+
+  def __init__(self, connected_socket, client_id):
+    global options
+    asyncore.dispatcher.__init__(self, connected_socket)
+    self.client_id = client_id
+    self.state = self.STATE_WAITING_FOR_HANDSHAKE
+    self.ip = None
+    self.addresses = None
+    self.hostname = None
+    self.port = None
+    self.requested_address = None
+    self.buffer = ''
+    self.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
+    self.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 128 * 1024)
+    self.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 128 * 1024)
+    self.needs_close = False
+
+  def SendMessage(self, type, message):
+    message['message'] = type
+    message['connection'] = self.client_id
+    out_pipe.SendMessage(message)
+
+  def handle_message(self, message):
+    if message['message'] == 'data' and 'data' in message and len(message['data']) > 0:
+      self.buffer += message['data']
+      if self.state == self.STATE_CONNECTED:
+        self.handle_write()
+    elif message['message'] == 'resolved':
+      self.HandleResolved(message)
+    elif message['message'] == 'connected':
+      self.HandleConnected(message)
+      self.handle_write()
+    elif message['message'] == 'closed':
+      if len(self.buffer) == 0:
+        logging.info('[{0:d}] Server connection close being processed, closing Browser connection'.format(self.client_id))
+        self.handle_close()
+      else:
+        logging.info('[{0:d}] Server connection close being processed, queuing browser connection close'.format(self.client_id))
+        self.needs_close = True
+
+  def writable(self):
+    return len(self.buffer) > 0
+
+  def handle_write(self):
+    if len(self.buffer) > 0:
+      sent = self.send(self.buffer)
+      logging.debug('[{0:d}] SOCKS <= {1:d} byte(s)'.format(self.client_id, sent))
+      self.buffer = self.buffer[sent:]
+      if self.needs_close and len(self.buffer) == 0:
+        logging.info('[{0:d}] queued browser connection close being processed, closing Browser connection'.format(self.client_id))
+        self.needs_close = False
+        self.handle_close()
+
+  def handle_read(self):
+    global connections
+    global dns_cache
+    try:
+      while True:
+        # Consume in up-to packet-sized chunks (TCP packet payload as 1460 bytes from 1500 byte ethernet frames)
+        data = self.recv(1460)
+        if data:
+          data_len = len(data)
+          if self.state == self.STATE_CONNECTED:
+            logging.debug('[{0:d}] SOCKS => {1:d} byte(s)'.format(self.client_id, data_len))
+            self.SendMessage('data', {'data': data})
+          elif self.state == self.STATE_WAITING_FOR_HANDSHAKE:
+            self.state = self.STATE_ERROR #default to an error state, set correctly if things work out
+            if data_len >= 2 and ord(data[0]) == 0x05:
+              supports_no_auth = False
+              auth_count = ord(data[1])
+              if data_len == auth_count + 2:
+                for i in range(auth_count):
+                  offset = i + 2
+                  if ord(data[offset]) == 0:
+                    supports_no_auth = True
+              if supports_no_auth:
+                # Respond with a message that "No Authentication" was agreed to
+                logging.info('[{0:d}] New Socks5 client'.format(self.client_id))
+                response = chr(0x05) + chr(0x00)
+                self.state = self.STATE_WAITING_FOR_CONNECT_REQUEST
+                self.buffer += response
+                self.handle_write()
+          elif self.state == self.STATE_WAITING_FOR_CONNECT_REQUEST:
+            self.state = self.STATE_ERROR #default to an error state, set correctly if things work out
+            if data_len >= 10 and ord(data[0]) == 0x05 and ord(data[2]) == 0x00:
+              if ord(data[1]) == 0x01: #TCP connection (only supported method for now)
+                connections[self.client_id]['server'] = TCPConnection(self.client_id)
+              self.requested_address = data[3:]
+              port_offset = 0
+              if ord(data[3]) == 0x01:
+                port_offset = 8
+                self.ip = '{0:d}.{1:d}.{2:d}.{3:d}'.format(ord(data[4]), ord(data[5]), ord(data[6]), ord(data[7]))
+              elif ord(data[3]) == 0x03:
+                name_len = ord(data[4])
+                if data_len >= 6 + name_len:
+                  port_offset = 5 + name_len
+                  self.hostname = data[5:5 + name_len]
+              elif ord(data[3]) == 0x04 and data_len >= 22:
+                port_offset = 20
+                self.ip = ''
+                for i in range(16):
+                  self.ip += '{0:02x}'.format(ord(data[4 + i]))
+                  if i % 2 and i < 15:
+                    self.ip += ':'
+              if port_offset and connections[self.client_id]['server'] is not None:
+                self.port = 256 * ord(data[port_offset]) + ord(data[port_offset + 1])
+                if self.port:
+                  if self.ip is None and self.hostname is not None:
+                    if self.hostname in dns_cache:
+                      self.state = self.STATE_CONNECTING
+                      self.addresses = dns_cache[self.hostname]
+                      self.SendMessage('connect', {'addresses': self.addresses, 'port': self.port})
+                    else:
+                      self.state = self.STATE_RESOLVING
+                      self.SendMessage('resolve', {'hostname': self.hostname, 'port': self.port})
+                  elif self.ip is not None:
+                    self.state = self.STATE_CONNECTING
+                    logging.debug('[{0:d}] Socks Connect - calling getaddrinfo for {1}:{2:d}'.format(self.client_id, self.ip, self.port))
+                    self.addresses = socket.getaddrinfo(self.ip, self.port)
+                    self.SendMessage('connect', {'addresses': self.addresses, 'port': self.port})
+        else:
+          return
+    except:
+      pass
+
+  def handle_close(self):
+    logging.info('[{0:d}] Browser Connection Closed by browser'.format(self.client_id))
+    self.state = self.STATE_ERROR
+    self.close()
+    try:
+      if self.client_id in connections:
+        if 'client' in connections[self.client_id]:
+          del connections[self.client_id]['client']
+        if 'server' in connections[self.client_id]:
+          self.SendMessage('closed', {})
+        else:
+          del connections[self.client_id]
+    except:
+      pass
+
+  def HandleResolved(self, message):
+    global dns_cache
+    if self.state == self.STATE_RESOLVING:
+      if 'addresses' in message and len(message['addresses']):
+        self.state = self.STATE_CONNECTING
+        self.addresses = message['addresses']
+        dns_cache[self.hostname] = self.addresses
+        logging.debug('[{0:d}] Resolved {1}, Connecting'.format(self.client_id, self.hostname))
+        self.SendMessage('connect', {'addresses': self.addresses, 'port': self.port})
+      else:
+        # Send host unreachable error
+        self.state = self.STATE_ERROR
+        self.buffer += chr(0x05) + chr(0x04) + self.requested_address
+        self.handle_write()
+
+  def HandleConnected(self, message):
+    if 'success' in message and self.state == self.STATE_CONNECTING:
+      response = chr(0x05)
+      if message['success']:
+        response += chr(0x00)
+        logging.debug('[{0:d}] Connected to {1}'.format(self.client_id, self.hostname))
+        self.state = self.STATE_CONNECTED
+      else:
+        response += chr(0x04)
+        self.state = self.STATE_ERROR
+      response += chr(0x00)
+      response += self.requested_address
+      self.buffer += response
+      self.handle_write()
+
+
+########################################################################################################################
+#   stdin command processor
+########################################################################################################################
+class CommandProcessor():
+  def __init__(self):
+    thread = threading.Thread(target = self.run, args=())
+    thread.daemon = True
+    thread.start()
+
+  def run(self):
+    global must_exit
+    while not must_exit:
+      for line in iter(sys.stdin.readline, ''):
+        self.ProcessCommand(line.strip())
+
+  def ProcessCommand(self, input):
+    global in_pipe
+    global out_pipe
+    global needs_flush
+    global REMOVE_TCP_OVERHEAD
+    global port_mappings
+    global server
+    if len(input):
+      ok = False
+      try:
+        command = input.split()
+        if len(command) and len(command[0]):
+          if command[0].lower() == 'flush':
+            ok = True
+          elif command[0].lower() == 'set' and len(command) >= 3:
+            if command[1].lower() == 'rtt' and len(command[2]):
+              rtt = float(command[2])
+              latency = rtt / 2000.0
+              in_pipe.latency = latency
+              out_pipe.latency = latency
+              ok = True
+            elif command[1].lower() == 'inkbps' and len(command[2]):
+              in_pipe.kbps = float(command[2]) * REMOVE_TCP_OVERHEAD
+              ok = True
+            elif command[1].lower() == 'outkbps' and len(command[2]):
+              out_pipe.kbps = float(command[2]) * REMOVE_TCP_OVERHEAD
+              ok = True
+            elif command[1].lower() == 'mapports' and len(command[2]):
+              SetPortMappings(command[2])
+              ok = True
+          elif command[0].lower() == 'reset' and len(command) >= 2:
+            if command[1].lower() == 'rtt' or command[1].lower() == 'all':
+              in_pipe.latency = 0
+              out_pipe.latency = 0
+              ok = True
+            if command[1].lower() == 'inkbps' or command[1].lower() == 'all':
+              in_pipe.kbps = 0
+              ok = True
+            if command[1].lower() == 'outkbps' or command[1].lower() == 'all':
+              out_pipe.kbps = 0
+              ok = True
+            if command[1].lower() == 'mapports' or command[1].lower() == 'all':
+              port_mappings = {}
+              ok = True
+
+          if ok:
+            needs_flush = True
+      except:
+        pass
+      if not ok:
+        PrintMessage('ERROR')
+      # open and close a local socket which will interrupt the long polling loop to process the flush
+      if needs_flush:
+        s = socket.socket()
+        s.connect((server.ipaddr, server.port))
+        s.close()
+
+
+########################################################################################################################
+#   Main Entry Point
+########################################################################################################################
+def main():
+  global server
+  global options
+  global in_pipe
+  global out_pipe
+  global dest_addresses
+  global port_mappings
+  global map_localhost
+  import argparse
+  global REMOVE_TCP_OVERHEAD
+  parser = argparse.ArgumentParser(description='Traffic-shaping socks5 proxy.',
+                                   prog='tsproxy')
+  parser.add_argument('-v', '--verbose', action='count', help="Increase verbosity (specify multiple times for more). -vvvv for full debug output.")
+  parser.add_argument('--logfile', help="Write log messages to given file instead of stdout.")
+  parser.add_argument('-b', '--bind', default='localhost', help="Server interface address (defaults to localhost).")
+  parser.add_argument('-p', '--port', type=int, default=1080, help="Server port (defaults to 1080, use 0 for randomly assigned).")
+  parser.add_argument('-r', '--rtt', type=float, default=.0, help="Round Trip Time Latency (in ms).")
+  parser.add_argument('-i', '--inkbps', type=float, default=.0, help="Download Bandwidth (in 1000 bits/s - Kbps).")
+  parser.add_argument('-o', '--outkbps', type=float, default=.0, help="Upload Bandwidth (in 1000 bits/s - Kbps).")
+  parser.add_argument('-w', '--window', type=int, default=10, help="Emulated TCP initial congestion window (defaults to 10).")
+  parser.add_argument('-d', '--desthost', help="Redirect all outbound connections to the specified host.")
+  parser.add_argument('-m', '--mapports', help="Remap outbound ports. Comma-separated list of original:new with * as a wildcard. --mapports '443:8443,*:8080'")
+  parser.add_argument('-l', '--localhost', action='store_true', default=False,
+                      help="Include connections already destined for localhost/127.0.0.1 in the host and port remapping.")
+  options = parser.parse_args()
+
+  # Set up logging
+  log_level = logging.CRITICAL
+  if options.verbose == 1:
+    log_level = logging.ERROR
+  elif options.verbose == 2:
+    log_level = logging.WARNING
+  elif options.verbose == 3:
+    log_level = logging.INFO
+  elif options.verbose >= 4:
+    log_level = logging.DEBUG
+  if options.logfile is not None:
+    logging.basicConfig(filename=options.logfile, level=log_level,
+                        format="%(asctime)s.%(msecs)03d - %(message)s", datefmt="%H:%M:%S")
+  else:
+    logging.basicConfig(level=log_level, format="%(asctime)s.%(msecs)03d - %(message)s", datefmt="%H:%M:%S")
+
+  # Parse any port mappings
+  if options.mapports:
+    SetPortMappings(options.mapports)
+
+  map_localhost = options.localhost
+
+  # Resolve the address for a rewrite destination host if one was specified
+  if options.desthost:
+    logging.debug('Startup - calling getaddrinfo for {0}:{1:d}'.format(options.desthost, GetDestPort(80)))
+    dest_addresses = socket.getaddrinfo(options.desthost, GetDestPort(80))
+
+  # Set up the pipes.  1/2 of the latency gets applied in each direction (and /1000 to convert to seconds)
+  in_pipe = TSPipe(TSPipe.PIPE_IN, options.rtt / 2000.0, options.inkbps * REMOVE_TCP_OVERHEAD)
+  out_pipe = TSPipe(TSPipe.PIPE_OUT, options.rtt / 2000.0, options.outkbps * REMOVE_TCP_OVERHEAD)
+
+  signal.signal(signal.SIGINT, signal_handler)
+  server = Socks5Server(options.bind, options.port)
+  command_processor = CommandProcessor()
+  PrintMessage('Started Socks5 proxy server on {0}:{1:d}\nHit Ctrl-C to exit.'.format(server.ipaddr, server.port))
+  run_loop()
+
+def signal_handler(signal, frame):
+  global server
+  global must_exit
+  logging.error('Exiting...')
+  must_exit = True
+  del server
+
+
+# Wrapper around the asyncore loop that lets us poll the in/out pipes every 1ms
+def run_loop():
+  global must_exit
+  global in_pipe
+  global out_pipe
+  global needs_flush
+  global flush_pipes
+  global last_activity
+  winmm = None
+
+  # increase the windows timer resolution to 1ms
+  if platform.system() == "Windows":
+    try:
+      import ctypes
+      winmm = ctypes.WinDLL('winmm')
+      winmm.timeBeginPeriod(1)
+    except:
+      pass
+
+  last_activity = time.clock()
+  last_check = time.clock()
+  # disable gc to avoid pauses during traffic shaping/proxying
+  gc.disable()
+  while not must_exit:
+    # Tick every 1ms if traffic-shaping is enabled and we have data or are doing background dns lookups, every 1 second otherwise
+    lock.acquire()
+    tick_interval = 0.001
+    if background_activity_count == 0:
+      if in_pipe.next_message is None and in_pipe.queue.empty() and out_pipe.next_message is None and out_pipe.queue.empty():
+        tick_interval = 1.0
+      elif in_pipe.kbps == .0 and in_pipe.latency == 0 and out_pipe.kbps == .0 and out_pipe.latency == 0:
+        tick_interval = 1.0
+    lock.release()
+    asyncore.poll(tick_interval, asyncore.socket_map)
+    if needs_flush:
+      flush_pipes = True
+      needs_flush = False
+    out_pipe.tick()
+    in_pipe.tick()
+    if flush_pipes:
+      PrintMessage('OK')
+      flush_pipes = False
+    # Every 500 ms check to see if it is a good time to do a gc
+    now = time.clock()
+    if now - last_check > 0.5:
+      last_check = now
+      # manually gc after 5 seconds of idle
+      if now - last_activity >= 5:
+        last_activity = now
+        logging.debug("Triggering manual GC")
+        gc.collect()
+
+  if winmm is not None:
+    winmm.timeEndPeriod(1)
+
+def GetDestPort(port):
+  global port_mappings
+  if port_mappings is not None:
+    src_port = str(port)
+    if src_port in port_mappings:
+      return port_mappings[src_port]
+    elif 'default' in port_mappings:
+      return port_mappings['default']
+  return port
+
+
+def SetPortMappings(map_string):
+  global port_mappings
+  port_mappings = {}
+  map_string = map_string.strip('\'" \t\r\n')
+  for pair in map_string.split(','):
+    (src, dest) = pair.split(':')
+    if src == '*':
+      port_mappings['default'] = int(dest)
+      logging.debug("Default port mapped to port {0}".format(dest))
+    else:
+      logging.debug("Port {0} mapped to port {1}".format(src, dest))
+      port_mappings[src] = int(dest)
+
+
+if '__main__' == __name__:
+  main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/COPYING b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/COPYING
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/COPYING
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/PRESUBMIT.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/PRESUBMIT.py
new file mode 100644
index 0000000..5948576
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/PRESUBMIT.py
@@ -0,0 +1,28 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Presubmit script for changes affecting tools/perf/.
+
+See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
+for more details about the presubmit API built into depot_tools.
+"""
+
+def _CommonChecks(input_api, output_api):
+  """Performs common checks, which includes running pylint."""
+  results = []
+  results.extend(input_api.canned_checks.RunPylint(
+        input_api, output_api, black_list=[], pylintrc='pylintrc'))
+  return results
+
+
+def CheckChangeOnUpload(input_api, output_api):
+  report = []
+  report.extend(_CommonChecks(input_api, output_api))
+  return report
+
+
+def CheckChangeOnCommit(input_api, output_api):
+  report = []
+  report.extend(_CommonChecks(input_api, output_api))
+  return report
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/README.chromium b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/README.chromium
new file mode 100644
index 0000000..0a1b657
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/README.chromium
@@ -0,0 +1,14 @@
+Name: chromite
+Short Name: webpagereplay
+URL: https://github.com/chromium/web-page-replay
+Version: 6cffdf1fb6c9a6d5dccbcc9cc18b8738a538eeba (commit hash)
+License: BSD
+License File: NOT_SHIPPED
+Security Critical: no
+
+Local modification: Remove webpagereplay/third_party/ipaddr/OWNERS to avoid
+PRESUBMIT complainings about non-standard OWNER format.
+
+Description:
+This contains webpagereplay used by telemetry for record & replay web requests &
+responses.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/README.md b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/README.md
new file mode 100644
index 0000000..28a3dc3
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/README.md
@@ -0,0 +1,28 @@
+[![Build
+Status](https://travis-ci.org/chromium/web-page-replay.png)](https://travis-ci.org/chromium/web-page-replay)
+[![Coverage
+Status](https://coveralls.io/repos/chromium/web-page-replay/badge.svg)](https://coveralls.io/r/chromium/web-page-replay)
+
+# Web Page Replay
+Record live Web pages and use them for local performance testing!
+
+## How?
+Use local DNS and HTTP(S) proxies to captures your live traffic. Then
+use these captures in order to replay the same exact content, making
+sure that your tests get consistent results, that are not affected by
+the origin servers, the network, etc.
+
+## Tell me more
+Check out the [getting
+started](documentation/GettingStarted.md) guide or take a
+look at the [architecture
+diagram](documentation/WebPageReplayDiagram.png).
+
+Also see [Note about web-page-replay
+code](https://docs.google.com/document/d/1cehHn3Lig7UYw_7pqQJjkbPTV3kS11EYwjKO-6jT0c8)
+
+## I want to help
+If you find issues with the project, you can file issues on this repo.
+If you want to do more and contribute code to help the project evolve,
+check out our [contribution
+guidelines](documentation/Contributing.md).
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/adb_install_cert.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/adb_install_cert.py
new file mode 100644
index 0000000..7d56df4
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/adb_install_cert.py
@@ -0,0 +1,275 @@
+# Copyright 2014 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Installs certificate on phone with KitKat."""
+
+import argparse
+import logging
+import os
+import subprocess
+import sys
+
+KEYCODE_ENTER = '66'
+KEYCODE_TAB = '61'
+
+
+class CertInstallError(Exception):
+  pass
+
+
+class CertRemovalError(Exception):
+  pass
+
+
+class AdbShellError(subprocess.CalledProcessError):
+  pass
+
+
+_ANDROID_M_BUILD_VERSION = 23
+
+
+class AndroidCertInstaller(object):
+  """Certificate installer for phones with KitKat."""
+
+  def __init__(self, device_id, cert_name, cert_path, adb_path=None):
+    if not os.path.exists(cert_path):
+      raise ValueError('Not a valid certificate path')
+    self.adb_path = adb_path or 'adb'
+    self.android_cacerts_path = None
+    self.cert_name = cert_name
+    self.cert_path = cert_path
+    self.device_id = device_id
+    self.file_name = os.path.basename(self.cert_path)
+    self.reformatted_cert_fname = None
+    self.reformatted_cert_path = None
+
+  @staticmethod
+  def _run_cmd(cmd, dirname=None):
+    return subprocess.check_output(cmd, cwd=dirname)
+
+  def _get_adb_cmd(self, *args):
+    cmd = [self.adb_path]
+    if self.device_id:
+      cmd.extend(['-s', self.device_id])
+    cmd.extend(args)
+    return cmd
+
+  def _adb(self, *args):
+    """Runs the adb command."""
+    return self._run_cmd(self._get_adb_cmd(*args))
+
+  def _adb_shell(self, *args):
+    """Runs the adb shell command."""
+    # We are not using self._adb() because adb shell return 0 even if the
+    # command has failed. This method is taking care of checking the actual
+    # return code of the command line ran on the device.
+    RETURN_CODE_PREFIX = '%%%s%% ' % __file__
+    adb_cmd = self._get_adb_cmd('shell', '(%s); echo %s$?' % (
+        subprocess.list2cmdline(args), RETURN_CODE_PREFIX))
+    process = subprocess.Popen(adb_cmd, stdout=subprocess.PIPE)
+    adb_stdout, _ = process.communicate()
+    if process.returncode != 0:
+      raise subprocess.CalledProcessError(
+          cmd=adb_cmd, returncode=process.returncode, output=adb_stdout)
+    assert adb_stdout[-1] == '\n'
+    prefix_pos = adb_stdout.rfind(RETURN_CODE_PREFIX)
+    assert prefix_pos != -1, \
+        'Couldn\'t find "%s" at the end of the output of %s' % (
+            RETURN_CODE_PREFIX, subprocess.list2cmdline(adb_cmd))
+    returncode = int(adb_stdout[prefix_pos + len(RETURN_CODE_PREFIX):])
+    stdout = adb_stdout[:prefix_pos]
+    if returncode != 0:
+      raise AdbShellError(cmd=args, returncode=returncode, output=stdout)
+    return stdout
+
+  def _adb_su_shell(self, *args):
+    """Runs command as root."""
+    build_version_sdk = int(self._get_property('ro.build.version.sdk'))
+    if build_version_sdk >= _ANDROID_M_BUILD_VERSION:
+      cmd = ['su', '0']
+    else:
+      cmd = ['su', '-c']
+    cmd.extend(args)
+    return self._adb_shell(*cmd)
+
+  def _get_property(self, prop):
+    return self._adb_shell('getprop', prop).strip()
+
+  def check_device(self):
+    install_warning = False
+    if self._get_property('ro.product.device') != 'hammerhead':
+      logging.warning('Device is not hammerhead')
+      install_warning = True
+    if self._get_property('ro.build.version.release') != '4.4.2':
+      logging.warning('Version is not 4.4.2')
+      install_warning = True
+    if install_warning:
+      logging.warning('Certificate may not install properly')
+
+  def _input_key(self, key):
+    """Inputs a keyevent."""
+    self._adb_shell('input', 'keyevent', key)
+
+  def _input_text(self, text):
+    """Inputs text."""
+    self._adb_shell('input', 'text', text)
+
+  @staticmethod
+  def _remove(file_name):
+    """Deletes file."""
+    if os.path.exists(file_name):
+      os.remove(file_name)
+
+  def _format_hashed_cert(self):
+    """Makes a certificate file that follows the format of files in cacerts."""
+    self._remove(self.reformatted_cert_path)
+    contents = self._run_cmd(['openssl', 'x509', '-inform', 'PEM', '-text',
+                              '-in', self.cert_path])
+    description, begin_cert, cert_body = contents.rpartition('-----BEGIN '
+                                                             'CERTIFICATE')
+    contents = ''.join([begin_cert, cert_body, description])
+    with open(self.reformatted_cert_path, 'w') as cert_file:
+      cert_file.write(contents)
+
+  def _remove_cert_from_cacerts(self):
+    self._adb_su_shell('mount', '-o', 'remount,rw', '/system')
+    self._adb_su_shell('rm', '-f', self.android_cacerts_path)
+
+  def _is_cert_installed(self):
+    try:
+      return (self._adb_su_shell('ls', self.android_cacerts_path).strip() ==
+              self.android_cacerts_path)
+    except AdbShellError:
+      return False
+
+  def _generate_reformatted_cert_path(self):
+    # Determine OpenSSL version, string is of the form
+    # 'OpenSSL 0.9.8za 5 Jun 2014' .
+    openssl_version = self._run_cmd(['openssl', 'version']).split()
+
+    if len(openssl_version) < 2:
+      raise ValueError('Unexpected OpenSSL version string: ', openssl_version)
+
+    # subject_hash flag name changed as of OpenSSL version 1.0.0 .
+    is_old_openssl_version = openssl_version[1].startswith('0')
+    subject_hash_flag = (
+        '-subject_hash' if is_old_openssl_version else '-subject_hash_old')
+
+    output = self._run_cmd(['openssl', 'x509', '-inform', 'PEM',
+                            subject_hash_flag, '-in', self.cert_path],
+                           os.path.dirname(self.cert_path))
+    self.reformatted_cert_fname = output.partition('\n')[0].strip() + '.0'
+    self.reformatted_cert_path = os.path.join(os.path.dirname(self.cert_path),
+                                              self.reformatted_cert_fname)
+    self.android_cacerts_path = ('/system/etc/security/cacerts/%s' %
+                                 self.reformatted_cert_fname)
+
+  def remove_cert(self):
+    self._generate_reformatted_cert_path()
+
+    if self._is_cert_installed():
+      self._remove_cert_from_cacerts()
+
+    if self._is_cert_installed():
+      raise CertRemovalError('Cert Removal Failed')
+
+  def install_cert(self, overwrite_cert=False):
+    """Installs a certificate putting it in /system/etc/security/cacerts."""
+    self._generate_reformatted_cert_path()
+
+    if self._is_cert_installed():
+      if overwrite_cert:
+        self._remove_cert_from_cacerts()
+      else:
+        logging.info('cert is already installed')
+        return
+
+    self._format_hashed_cert()
+    self._adb('push', self.reformatted_cert_path, '/sdcard/')
+    self._remove(self.reformatted_cert_path)
+    self._adb_su_shell('mount', '-o', 'remount,rw', '/system')
+    self._adb_su_shell(
+        'cp', '/sdcard/%s' % self.reformatted_cert_fname,
+        '/system/etc/security/cacerts/%s' % self.reformatted_cert_fname)
+    self._adb_su_shell('chmod', '644', self.android_cacerts_path)
+    if not self._is_cert_installed():
+      raise CertInstallError('Cert Install Failed')
+
+  def install_cert_using_gui(self):
+    """Installs certificate on the device using adb commands."""
+    self.check_device()
+    # TODO(mruthven): Add a check to see if the certificate is already installed
+    # Install the certificate.
+    logging.info('Installing %s on %s', self.cert_path, self.device_id)
+    self._adb('push', self.cert_path, '/sdcard/')
+
+    # Start credential install intent.
+    self._adb_shell('am', 'start', '-W', '-a', 'android.credentials.INSTALL')
+
+    # Move to and click search button.
+    self._input_key(KEYCODE_TAB)
+    self._input_key(KEYCODE_TAB)
+    self._input_key(KEYCODE_ENTER)
+
+    # Search for certificate and click it.
+    # Search only works with lower case letters
+    self._input_text(self.file_name.lower())
+    self._input_key(KEYCODE_ENTER)
+
+    # These coordinates work for hammerhead devices.
+    self._adb_shell('input', 'tap', '300', '300')
+
+    # Name the certificate and click enter.
+    self._input_text(self.cert_name)
+    self._input_key(KEYCODE_TAB)
+    self._input_key(KEYCODE_TAB)
+    self._input_key(KEYCODE_TAB)
+    self._input_key(KEYCODE_ENTER)
+
+    # Remove the file.
+    self._adb_shell('rm', '/sdcard/' + self.file_name)
+
+
+def parse_args():
+  """Parses command line arguments."""
+  parser = argparse.ArgumentParser(description='Install cert on device.')
+  parser.add_argument(
+      '-n', '--cert-name', default='dummycert', help='certificate name')
+  parser.add_argument(
+      '--overwrite', default=False, action='store_true',
+      help='Overwrite certificate file if it is already installed')
+  parser.add_argument(
+      '--remove', default=False, action='store_true',
+      help='Remove certificate file if it is installed')
+  parser.add_argument(
+      '--device-id', help='device serial number')
+  parser.add_argument(
+      '--adb-path', help='adb binary path')
+  parser.add_argument(
+      'cert_path', help='Certificate file path')
+  return parser.parse_args()
+
+
+def main():
+  args = parse_args()
+  cert_installer = AndroidCertInstaller(args.device_id, args.cert_name,
+                                        args.cert_path, adb_path=args.adb_path)
+  if args.remove:
+    cert_installer.remove_cert()
+  else:
+    cert_installer.install_cert(args.overwrite)
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/certutils.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/certutils.py
new file mode 100644
index 0000000..46c31a8
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/certutils.py
@@ -0,0 +1,289 @@
+# Copyright 2014 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Routines to generate root and server certificates.
+
+Certificate Naming Conventions:
+  ca_cert:  crypto.X509 for the certificate authority (w/ both the pub &
+                priv keys)
+  cert:  a crypto.X509 certificate (w/ just the pub key)
+  cert_str:  a certificate string (w/ just the pub cert)
+  key:  a private crypto.PKey  (from ca or pem)
+  ca_cert_str:  a certificae authority string (w/ both the pub & priv certs)
+"""
+
+import logging
+import os
+import platform
+import socket
+import subprocess
+import time
+
+openssl_import_error = None
+
+Error = None
+SSL_METHOD = None
+SysCallError = None
+VERIFY_PEER = None
+ZeroReturnError = None
+FILETYPE_PEM = None
+
+try:
+  from OpenSSL import crypto, SSL
+
+  Error = SSL.Error
+  SSL_METHOD = SSL.SSLv23_METHOD
+  SysCallError = SSL.SysCallError
+  VERIFY_PEER = SSL.VERIFY_PEER
+  ZeroReturnError = SSL.ZeroReturnError
+  FILETYPE_PEM = crypto.FILETYPE_PEM
+except ImportError, e:
+  openssl_import_error = e
+
+
+def get_ssl_context(method=SSL_METHOD):
+  # One of: One of SSLv2_METHOD, SSLv3_METHOD, SSLv23_METHOD, or TLSv1_METHOD
+  if openssl_import_error:
+    raise openssl_import_error  # pylint: disable=raising-bad-type
+  return SSL.Context(method)
+
+
+class WrappedConnection(object):
+
+  def __init__(self, obj):
+    self._wrapped_obj = obj
+
+  def __getattr__(self, attr):
+    if attr in self.__dict__:
+      return getattr(self, attr)
+    return getattr(self._wrapped_obj, attr)
+
+  def recv(self, buflen=1024, flags=0):
+    try:
+      return self._wrapped_obj.recv(buflen, flags)
+    except SSL.SysCallError, e:
+      if e.args[1] == 'Unexpected EOF':
+        return ''
+      raise
+    except SSL.ZeroReturnError:
+      return ''
+
+
+def get_ssl_connection(context, connection):
+  return WrappedConnection(SSL.Connection(context, connection))
+
+
+def load_privatekey(key, filetype=FILETYPE_PEM):
+  """Loads obj private key object from string."""
+  return crypto.load_privatekey(filetype, key)
+
+
+def load_cert(cert_str, filetype=FILETYPE_PEM):
+  """Loads obj cert object from string."""
+  return crypto.load_certificate(filetype, cert_str)
+
+
+def _dump_privatekey(key, filetype=FILETYPE_PEM):
+  """Dumps obj private key object to string."""
+  return crypto.dump_privatekey(filetype, key)
+
+
+def _dump_cert(cert, filetype=FILETYPE_PEM):
+  """Dumps obj cert object to string."""
+  return crypto.dump_certificate(filetype, cert)
+
+
+def generate_dummy_ca_cert(subject='_WebPageReplayCert'):
+  """Generates dummy certificate authority.
+
+  Args:
+    subject: a string representing the desired root cert issuer
+  Returns:
+    A tuple of the public key and the private key strings for the root
+    certificate
+  """
+  if openssl_import_error:
+    raise openssl_import_error  # pylint: disable=raising-bad-type
+
+  key = crypto.PKey()
+  key.generate_key(crypto.TYPE_RSA, 1024)
+
+  ca_cert = crypto.X509()
+  ca_cert.set_serial_number(int(time.time()*10000))
+  ca_cert.set_version(2)
+  ca_cert.get_subject().CN = subject
+  ca_cert.get_subject().O = subject
+  ca_cert.gmtime_adj_notBefore(-60 * 60 * 24 * 365 * 2)
+  ca_cert.gmtime_adj_notAfter(60 * 60 * 24 * 365 * 2)
+  ca_cert.set_issuer(ca_cert.get_subject())
+  ca_cert.set_pubkey(key)
+  ca_cert.add_extensions([
+      crypto.X509Extension('basicConstraints', True, 'CA:TRUE'),
+      crypto.X509Extension('subjectAltName', False, 'DNS:' + subject),
+      crypto.X509Extension('nsCertType', True, 'sslCA'),
+      crypto.X509Extension('extendedKeyUsage', True,
+                           ('serverAuth,clientAuth,emailProtection,'
+                            'timeStamping,msCodeInd,msCodeCom,msCTLSign,'
+                            'msSGC,msEFS,nsSGC')),
+      crypto.X509Extension('keyUsage', False, 'keyCertSign, cRLSign'),
+      crypto.X509Extension('subjectKeyIdentifier', False, 'hash',
+                           subject=ca_cert),
+      ])
+  ca_cert.sign(key, 'sha256')
+  key_str = _dump_privatekey(key)
+  ca_cert_str = _dump_cert(ca_cert)
+  return ca_cert_str, key_str
+
+
+def get_host_cert(host, port=443):
+  """Contacts the host and returns its certificate."""
+  host_certs = []
+  def verify_cb(conn, cert, errnum, depth, ok):
+    host_certs.append(cert)
+    # Return True to indicates that the certificate was ok.
+    return True
+
+  context = SSL.Context(SSL.SSLv23_METHOD)
+  context.set_verify(SSL.VERIFY_PEER, verify_cb)  # Demand a certificate
+  s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+  connection = SSL.Connection(context, s)
+  try:
+    connection.connect((host, port))
+    connection.send('')
+  except SSL.SysCallError:
+    pass
+  except socket.gaierror:
+    logging.debug('Host name is not valid')
+  finally:
+    connection.shutdown()
+    connection.close()
+  if not host_certs:
+    logging.warning('Unable to get host certificate from %s:%s', host, port)
+    return ''
+  return _dump_cert(host_certs[-1])
+
+
+def write_dummy_ca_cert(ca_cert_str, key_str, cert_path):
+  """Writes four certificate files.
+
+  For example, if cert_path is "mycert.pem":
+      mycert.pem - CA plus private key
+      mycert-cert.pem - CA in PEM format
+      mycert-cert.cer - CA for Android
+      mycert-cert.p12 - CA in PKCS12 format for Windows devices
+  Args:
+    cert_path: path string such as "mycert.pem"
+    ca_cert_str: certificate string
+    key_str: private key string
+  """
+  dirname = os.path.dirname(cert_path)
+  if dirname and not os.path.exists(dirname):
+    os.makedirs(dirname)
+
+  root_path = os.path.splitext(cert_path)[0]
+  ca_cert_path = root_path + '-cert.pem'
+  android_cer_path = root_path + '-cert.cer'
+  windows_p12_path = root_path + '-cert.p12'
+
+  # Dump the CA plus private key
+  with open(cert_path, 'w') as f:
+    f.write(key_str)
+    f.write(ca_cert_str)
+
+  # Dump the certificate in PEM format
+  with open(ca_cert_path, 'w') as f:
+    f.write(ca_cert_str)
+
+  # Create a .cer file with the same contents for Android
+  with open(android_cer_path, 'w') as f:
+    f.write(ca_cert_str)
+
+  ca_cert = load_cert(ca_cert_str)
+  key = load_privatekey(key_str)
+  # Dump the certificate in PKCS12 format for Windows devices
+  with open(windows_p12_path, 'w') as f:
+    p12 = crypto.PKCS12()
+    p12.set_certificate(ca_cert)
+    p12.set_privatekey(key)
+    f.write(p12.export())
+
+
+def generate_cert(root_ca_cert_str, server_cert_str, server_host):
+  """Generates a cert_str with the sni field in server_cert_str signed by the
+  root_ca_cert_str.
+
+  Args:
+    root_ca_cert_str: PEM formatted string representing the root cert
+    server_cert_str: PEM formatted string representing cert
+    server_host: host name to use if there is no server_cert_str
+  Returns:
+    a PEM formatted certificate string
+  """
+  EXTENSION_WHITELIST = set(['subjectAltName'])
+
+  if openssl_import_error:
+    raise openssl_import_error  # pylint: disable=raising-bad-type
+
+  common_name = server_host
+  reused_extensions = []
+  if server_cert_str:
+    original_cert = load_cert(server_cert_str)
+    common_name = original_cert.get_subject().commonName
+    for i in xrange(original_cert.get_extension_count()):
+      original_cert_extension = original_cert.get_extension(i)
+      if original_cert_extension.get_short_name() in EXTENSION_WHITELIST:
+        reused_extensions.append(original_cert_extension)
+
+  ca_cert = load_cert(root_ca_cert_str)
+  ca_key = load_privatekey(root_ca_cert_str)
+
+  cert = crypto.X509()
+  cert.get_subject().CN = common_name
+  cert.gmtime_adj_notBefore(-60 * 60)
+  cert.gmtime_adj_notAfter(60 * 60 * 24 * 30)
+  cert.set_issuer(ca_cert.get_subject())
+  cert.set_serial_number(int(time.time()*10000))
+  cert.set_pubkey(ca_key)
+  cert.add_extensions(reused_extensions)
+  cert.sign(ca_key, 'sha256')
+
+  return _dump_cert(cert)
+
+
+def install_cert_in_nssdb(home_directory_path, certificate_path):
+  """Installs a certificate into the ~/.pki/nssdb database.
+
+  Args:
+    home_directory_path: Path of the home directory where to install
+    certificate_path: Path of a CA in PEM format
+  """
+  assert os.path.isdir(home_directory_path)
+  assert platform.system() == 'Linux', \
+      'SSL certification authority has only been tested for linux.'
+  if (os.path.abspath(home_directory_path) ==
+          os.path.abspath(os.environ['HOME'])):
+    raise Exception('Modifying $HOME/.pki/nssdb compromises your machine.')
+
+  cert_database_path = os.path.join(home_directory_path, '.pki', 'nssdb')
+  def certutil(args):
+    cmd = ['certutil', '--empty-password', '-d', 'sql:' + cert_database_path]
+    cmd.extend(args)
+    logging.info(subprocess.list2cmdline(cmd))
+    subprocess.check_call(cmd)
+
+  if not os.path.isdir(cert_database_path):
+    os.makedirs(cert_database_path)
+    certutil(['-N'])
+
+  certutil(['-A', '-t', 'PC,,', '-n', certificate_path, '-i', certificate_path])
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/certutils_test.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/certutils_test.py
new file mode 100644
index 0000000..de1ac2d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/certutils_test.py
@@ -0,0 +1,135 @@
+# Copyright 2014 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Test routines to generate dummy certificates."""
+
+import BaseHTTPServer
+import os
+import shutil
+import ssl
+import tempfile
+import threading
+import unittest
+
+import certutils
+
+
+class Server(BaseHTTPServer.HTTPServer):
+
+  def __init__(self, https_root_ca_cert_path):
+    BaseHTTPServer.HTTPServer.__init__(
+        self, ('localhost', 0), BaseHTTPServer.BaseHTTPRequestHandler)
+    self.socket = ssl.wrap_socket(
+        self.socket, certfile=https_root_ca_cert_path, server_side=True,
+        do_handshake_on_connect=False)
+
+  def __enter__(self):
+    thread = threading.Thread(target=self.serve_forever)
+    thread.daemon = True
+    thread.start()
+    return self
+
+  def cleanup(self):
+    try:
+      self.shutdown()
+    except KeyboardInterrupt:
+      pass
+
+  def __exit__(self, type_, value_, traceback_):
+    self.cleanup()
+
+
+class CertutilsTest(unittest.TestCase):
+
+  def _check_cert_file(self, cert_file_path, cert_str, key_str=None):
+    cert_load = open(cert_file_path, 'r').read()
+    if key_str:
+      expected_cert = key_str + cert_str
+    else:
+      expected_cert = cert_str
+    self.assertEqual(expected_cert, cert_load)
+
+  def setUp(self):
+    self._temp_dir = tempfile.mkdtemp(prefix='certutils_', dir='/tmp')
+
+  def tearDown(self):
+    if self._temp_dir:
+      shutil.rmtree(self._temp_dir)
+
+  def test_generate_dummy_ca_cert(self):
+    subject = 'testSubject'
+    c, _ = certutils.generate_dummy_ca_cert(subject)
+    c = certutils.load_cert(c)
+    self.assertEqual(c.get_subject().commonName, subject)
+
+  def test_get_host_cert(self):
+    ca_cert_path = os.path.join(self._temp_dir, 'rootCA.pem')
+    issuer = 'testCA'
+    certutils.write_dummy_ca_cert(*certutils.generate_dummy_ca_cert(issuer),
+                                  cert_path=ca_cert_path)
+
+    with Server(ca_cert_path) as server:
+      cert_str = certutils.get_host_cert('localhost', server.server_port)
+      cert = certutils.load_cert(cert_str)
+      self.assertEqual(issuer, cert.get_subject().commonName)
+
+  def test_get_host_cert_gives_empty_for_bad_host(self):
+    cert_str = certutils.get_host_cert('not_a_valid_host_name_2472341234234234')
+    self.assertEqual('', cert_str)
+
+  def test_write_dummy_ca_cert(self):
+    base_path = os.path.join(self._temp_dir, 'testCA')
+    ca_cert_path = base_path + '.pem'
+    cert_path = base_path + '-cert.pem'
+    ca_cert_android = base_path + '-cert.cer'
+    ca_cert_windows = base_path + '-cert.p12'
+
+    self.assertFalse(os.path.exists(ca_cert_path))
+    self.assertFalse(os.path.exists(cert_path))
+    self.assertFalse(os.path.exists(ca_cert_android))
+    self.assertFalse(os.path.exists(ca_cert_windows))
+    c, k = certutils.generate_dummy_ca_cert()
+    certutils.write_dummy_ca_cert(c, k, ca_cert_path)
+
+    self._check_cert_file(ca_cert_path, c, k)
+    self._check_cert_file(cert_path, c)
+    self._check_cert_file(ca_cert_android, c)
+    self.assertTrue(os.path.exists(ca_cert_windows))
+
+  def test_generate_cert(self):
+    ca_cert_path = os.path.join(self._temp_dir, 'testCA.pem')
+    issuer = 'testIssuer'
+    certutils.write_dummy_ca_cert(
+        *certutils.generate_dummy_ca_cert(issuer), cert_path=ca_cert_path)
+
+    with open(ca_cert_path, 'r') as root_file:
+      root_string = root_file.read()
+    subject = 'testSubject'
+    cert_string = certutils.generate_cert(
+        root_string, '', subject)
+    cert = certutils.load_cert(cert_string)
+    self.assertEqual(issuer, cert.get_issuer().commonName)
+    self.assertEqual(subject, cert.get_subject().commonName)
+
+    with open(ca_cert_path, 'r') as ca_cert_file:
+      ca_cert_str = ca_cert_file.read()
+    cert_string = certutils.generate_cert(ca_cert_str, cert_string,
+                                          'host')
+    cert = certutils.load_cert(cert_string)
+    self.assertEqual(issuer, cert.get_issuer().commonName)
+    self.assertEqual(subject, cert.get_subject().commonName)
+
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/codereview.settings b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/codereview.settings
new file mode 100644
index 0000000..f0c2b12
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/codereview.settings
@@ -0,0 +1,3 @@
+# This file is used by gcl to get repository specific information.
+CODE_REVIEW_SERVER: codereview.chromium.org
+PROJECT: web-page-replay
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/customhandlers.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/customhandlers.py
new file mode 100644
index 0000000..14166af
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/customhandlers.py
@@ -0,0 +1,198 @@
+#!/usr/bin/env python
+# Copyright 2010 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Handle special HTTP requests.
+
+/web-page-replay-generate-[RESPONSE_CODE]
+  - Return the given RESPONSE_CODE.
+/web-page-replay-post-image-[FILENAME]
+  - Save the posted image to local disk.
+/web-page-replay-command-[record|replay|status]
+  - Optional. Enable by calling custom_handlers.add_server_manager_handler(...).
+  - Change the server mode to either record or replay.
+    + When switching to record, the http_archive is cleared.
+    + When switching to replay, the http_archive is maintained.
+"""
+
+import base64
+import httparchive
+import json
+import logging
+import os
+
+COMMON_URL_PREFIX = '/web-page-replay-'
+COMMAND_URL_PREFIX = COMMON_URL_PREFIX + 'command-'
+GENERATOR_URL_PREFIX = COMMON_URL_PREFIX + 'generate-'
+POST_IMAGE_URL_PREFIX = COMMON_URL_PREFIX + 'post-image-'
+IMAGE_DATA_PREFIX = 'data:image/png;base64,'
+
+
+def SimpleResponse(status):
+  """Return a ArchivedHttpResponse with |status| code and a simple text body."""
+  return httparchive.create_response(status)
+
+
+def JsonResponse(data):
+  """Return a ArchivedHttpResponse with |data| encoded as json in the body."""
+  status = 200
+  reason = 'OK'
+  headers = [('content-type', 'application/json')]
+  body = json.dumps(data)
+  return httparchive.create_response(status, reason, headers, body)
+
+
+class CustomHandlers(object):
+
+  def __init__(self, options, http_archive):
+    """Initialize CustomHandlers.
+
+    Args:
+      options: original options passed to the server.
+      http_archive: reference to the HttpArchive object.
+    """
+    self.server_manager = None
+    self.options = options
+    self.http_archive = http_archive
+    self.handlers = [
+        (GENERATOR_URL_PREFIX, self.get_generator_url_response_code)]
+    # screenshot_dir is a path to which screenshots are saved.
+    if options.screenshot_dir:
+      if not os.path.exists(options.screenshot_dir):
+        try:
+          os.makedirs(options.screenshot_dir)
+        except IOError:
+          logging.error('Unable to create screenshot dir: %s',
+                         options.screenshot_dir)
+          options.screenshot_dir = None
+      if options.screenshot_dir:
+        self.screenshot_dir = options.screenshot_dir
+        self.handlers.append(
+            (POST_IMAGE_URL_PREFIX, self.handle_possible_post_image))
+
+  def handle(self, request):
+    """Dispatches requests to matching handlers.
+
+    Args:
+      request: an http request
+    Returns:
+      ArchivedHttpResponse or None.
+    """
+    for prefix, handler in self.handlers:
+      if request.full_path.startswith(prefix):
+        return handler(request, request.full_path[len(prefix):])
+    return None
+
+  def get_generator_url_response_code(self, request, url_suffix):
+    """Parse special generator URLs for the embedded response code.
+
+    Args:
+      request: an ArchivedHttpRequest instance
+      url_suffix: string that is after the handler prefix (e.g. 304)
+    Returns:
+      On a match, an ArchivedHttpResponse.
+      Otherwise, None.
+    """
+    del request
+    try:
+      response_code = int(url_suffix)
+      return SimpleResponse(response_code)
+    except ValueError:
+      return None
+
+  def handle_possible_post_image(self, request, url_suffix):
+    """If sent, saves embedded image to local directory.
+
+    Expects a special url containing the filename. If sent, saves the base64
+    encoded request body as a PNG image locally. This feature is enabled by
+    passing in screenshot_dir to the initializer for this class.
+
+    Args:
+      request: an ArchivedHttpRequest instance
+      url_suffix: string that is after the handler prefix (e.g. 'foo.png')
+    Returns:
+      On a match, an ArchivedHttpResponse.
+      Otherwise, None.
+    """
+    basename = url_suffix
+    if not basename:
+      return None
+
+    data = request.request_body
+    if not data.startswith(IMAGE_DATA_PREFIX):
+      logging.error('Unexpected image format for: %s', basename)
+      return SimpleResponse(400)
+
+    data = data[len(IMAGE_DATA_PREFIX):]
+    png = base64.b64decode(data)
+    filename = os.path.join(self.screenshot_dir,
+                            '%s-%s.png' % (request.host, basename))
+    if not os.access(self.screenshot_dir, os.W_OK):
+      logging.error('Unable to write to: %s', filename)
+      return SimpleResponse(400)
+
+    with file(filename, 'w') as f:
+      f.write(png)
+    return SimpleResponse(200)
+
+  def add_server_manager_handler(self, server_manager):
+    """Add the ability to change the server mode (e.g. to record mode).
+    Args:
+      server_manager: a servermanager.ServerManager instance.
+    """
+    self.server_manager = server_manager
+    self.handlers.append(
+        (COMMAND_URL_PREFIX, self.handle_server_manager_command))
+
+  def handle_server_manager_command(self, request, url_suffix):
+    """Parse special URLs for the embedded server manager command.
+
+    Clients like webpagetest.org can use URLs of this form to change
+    the replay server from record mode to replay mode.
+
+    This handler is not in the default list of handlers. Call
+    add_server_manager_handler to add it.
+
+    In the future, this could be expanded to save or serve archive files.
+
+    Args:
+      request: an ArchivedHttpRequest instance
+      url_suffix: string that is after the handler prefix (e.g. 'record')
+    Returns:
+      On a match, an ArchivedHttpResponse.
+      Otherwise, None.
+    """
+    command = url_suffix
+    if command == 'record':
+      self.server_manager.SetRecordMode()
+      return SimpleResponse(200)
+    elif command == 'replay':
+      self.server_manager.SetReplayMode()
+      return SimpleResponse(200)
+    elif command == 'status':
+      status = {}
+      is_record_mode = self.server_manager.IsRecordMode()
+      status['is_record_mode'] = is_record_mode
+      status['options'] = json.loads(str(self.options))
+      archive_stats = self.http_archive.stats()
+      if archive_stats:
+        status['archive_stats'] = json.loads(archive_stats)
+      return JsonResponse(status)
+    elif command == 'exit':
+      self.server_manager.should_exit = True
+      return SimpleResponse(200)
+    elif command == 'log':
+      logging.info('log command: %s', str(request.request_body)[:1000000])
+      return SimpleResponse(200)
+    return None
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/daemonserver.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/daemonserver.py
new file mode 100644
index 0000000..371c654
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/daemonserver.py
@@ -0,0 +1,36 @@
+#!/usr/bin/env python
+# Copyright 2010 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import threading
+
+
+class DaemonServer(object):
+  """Base class which manages creation and cleanup of daemon style servers."""
+
+  def __enter__(self):
+    # TODO: Because of python's Global Interpreter Lock (GIL), the threads
+    # will run on the same CPU. Consider using processes instead because
+    # the components do not need to communicate with each other. On Linux,
+    # "taskset" could be used to assign each process to specific CPU/core.
+    # Of course, only bother with this if the processing speed is an issue.
+    # Some related discussion: http://stackoverflow.com/questions/990102/python-
+    # global-interpreter-lock-gil-workaround-on-multi-core-systems-using-tasks
+    thread = threading.Thread(target=self.serve_forever)
+    thread.daemon = True  # Python exits when no non-daemon threads are left.
+    thread.start()
+    return self
+
+  def __exit__(self, unused_exc_type, unused_exc_val, unused_exc_tb):
+    self.cleanup()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/deterministic.js b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/deterministic.js
new file mode 100644
index 0000000..050af31
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/deterministic.js
@@ -0,0 +1,66 @@
+'use strict';
+
+(function () {
+  var random_count = 0;
+  var random_count_threshold = 25;
+  var random_seed = 0.462;
+  Math.random = function() {
+    random_count++;
+    if (random_count > random_count_threshold){
+     random_seed += 0.1;
+     random_count = 1;
+    }
+    return (random_seed % 1);
+  };
+  if (typeof(crypto) == 'object' &&
+      typeof(crypto.getRandomValues) == 'function') {
+    crypto.getRandomValues = function(arr) {
+      var scale = Math.pow(256, arr.BYTES_PER_ELEMENT);
+      for (var i = 0; i < arr.length; i++) {
+        arr[i] = Math.floor(Math.random() * scale);
+      }
+      return arr;
+    };
+  }
+})();
+(function () {
+  var date_count = 0;
+  var date_count_threshold = 25;
+  var orig_date = Date;
+  // This should be replaced by web page replay by corresponding date
+  // (usually the date when the recording was done)
+  var time_seed = {{WPR_TIME_SEED_TIMESTAMP}};
+  Date = function() {
+    if (this instanceof Date) {
+      date_count++;
+      if (date_count > date_count_threshold){
+        time_seed += 50;
+        date_count = 1;
+      }
+      switch (arguments.length) {
+      case 0: return new orig_date(time_seed);
+      case 1: return new orig_date(arguments[0]);
+      default: return new orig_date(arguments[0], arguments[1],
+         arguments.length >= 3 ? arguments[2] : 1,
+         arguments.length >= 4 ? arguments[3] : 0,
+         arguments.length >= 5 ? arguments[4] : 0,
+         arguments.length >= 6 ? arguments[5] : 0,
+         arguments.length >= 7 ? arguments[6] : 0);
+      }
+    }
+    return new Date().toString();
+  };
+  Date.__proto__ = orig_date;
+  Date.prototype = orig_date.prototype;
+  Date.prototype.constructor = Date;
+  orig_date.now = function() {
+    return new Date().getTime();
+  };
+  orig_date.prototype.getTimezoneOffset = function() {
+    var dst2010Start = 1268560800000;
+    var dst2010End = 1289120400000;
+    if (this.getTime() >= dst2010Start && this.getTime() < dst2010End)
+      return 420;
+    return 480;
+  };
+})();
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/dnsproxy.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/dnsproxy.py
new file mode 100644
index 0000000..171b996
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/dnsproxy.py
@@ -0,0 +1,300 @@
+#!/usr/bin/env python
+# Copyright 2010 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import daemonserver
+import errno
+import logging
+import socket
+import SocketServer
+import threading
+import time
+
+from third_party.dns import flags
+from third_party.dns import message
+from third_party.dns import rcode
+from third_party.dns import resolver
+from third_party.dns import rdatatype
+from third_party import ipaddr
+
+
+
+class DnsProxyException(Exception):
+  pass
+
+
+DEFAULT_DNS_PORT = 53
+
+
+class RealDnsLookup(object):
+  def __init__(self, name_servers, dns_forwarding, proxy_host, proxy_port):
+    if (proxy_host in name_servers and proxy_port == DEFAULT_DNS_PORT and
+        dns_forwarding):
+      raise DnsProxyException(
+          'Invalid nameserver: %s (causes an infinte loop)'.format(
+              proxy_host))
+    self.resolver = resolver.get_default_resolver()
+    self.resolver.nameservers = name_servers
+    self.dns_cache_lock = threading.Lock()
+    self.dns_cache = {}
+
+  @staticmethod
+  def _IsIPAddress(hostname):
+    try:
+      socket.inet_aton(hostname)
+      return True
+    except socket.error:
+      return False
+
+  def __call__(self, hostname, rdtype=rdatatype.A):
+    """Return real IP for a host.
+
+    Args:
+      host: a hostname ending with a period (e.g. "www.google.com.")
+      rdtype: the query type (1 for 'A', 28 for 'AAAA')
+    Returns:
+      the IP address as a string (e.g. "192.168.25.2")
+    """
+    if self._IsIPAddress(hostname):
+      return hostname
+    self.dns_cache_lock.acquire()
+    ip = self.dns_cache.get(hostname)
+    self.dns_cache_lock.release()
+    if ip:
+      return ip
+    try:
+      answers = self.resolver.query(hostname, rdtype)
+    except resolver.NXDOMAIN:
+      return None
+    except resolver.NoNameservers:
+      logging.debug('_real_dns_lookup(%s) -> No nameserver.',
+                    hostname)
+      return None
+    except (resolver.NoAnswer, resolver.Timeout) as ex:
+      logging.debug('_real_dns_lookup(%s) -> None (%s)',
+                    hostname, ex.__class__.__name__)
+      return None
+    if answers:
+      ip = str(answers[0])
+    self.dns_cache_lock.acquire()
+    self.dns_cache[hostname] = ip
+    self.dns_cache_lock.release()
+    return ip
+
+  def ClearCache(self):
+    """Clear the dns cache."""
+    self.dns_cache_lock.acquire()
+    self.dns_cache.clear()
+    self.dns_cache_lock.release()
+
+
+class ReplayDnsLookup(object):
+  """Resolve DNS requests to replay host."""
+  def __init__(self, replay_ip, filters=None):
+    self.replay_ip = replay_ip
+    self.filters = filters or []
+
+  def __call__(self, hostname):
+    ip = self.replay_ip
+    for f in self.filters:
+      ip = f(hostname, default_ip=ip)
+    return ip
+
+
+class PrivateIpFilter(object):
+  """Resolve private hosts to their real IPs and others to the Web proxy IP.
+
+  Hosts in the given http_archive will resolve to the Web proxy IP without
+  checking the real IP.
+
+  This only supports IPv4 lookups.
+  """
+  def __init__(self, real_dns_lookup, http_archive):
+    """Initialize PrivateIpDnsLookup.
+
+    Args:
+      real_dns_lookup: a function that resolves a host to an IP.
+      http_archive: an instance of a HttpArchive
+        Hosts is in the archive will always resolve to the web_proxy_ip
+    """
+    self.real_dns_lookup = real_dns_lookup
+    self.http_archive = http_archive
+    self.InitializeArchiveHosts()
+
+  def __call__(self, host, default_ip):
+    """Return real IPv4 for private hosts and Web proxy IP otherwise.
+
+    Args:
+      host: a hostname ending with a period (e.g. "www.google.com.")
+    Returns:
+      IP address as a string or None (if lookup fails)
+    """
+    ip = default_ip
+    if host not in self.archive_hosts:
+      real_ip = self.real_dns_lookup(host)
+      if real_ip:
+        if ipaddr.IPAddress(real_ip).is_private:
+          ip = real_ip
+      else:
+        ip = None
+    return ip
+
+  def InitializeArchiveHosts(self):
+    """Recompute the archive_hosts from the http_archive."""
+    self.archive_hosts = set('%s.' % req.host.split(':')[0]
+                             for req in self.http_archive)
+
+
+class DelayFilter(object):
+  """Add a delay to replayed lookups."""
+
+  def __init__(self, is_record_mode, delay_ms):
+    self.is_record_mode = is_record_mode
+    self.delay_ms = int(delay_ms)
+
+  def __call__(self, host, default_ip):
+    if not self.is_record_mode:
+      time.sleep(self.delay_ms * 1000.0)
+    return default_ip
+
+  def SetRecordMode(self):
+    self.is_record_mode = True
+
+  def SetReplayMode(self):
+    self.is_record_mode = False
+
+
+class UdpDnsHandler(SocketServer.DatagramRequestHandler):
+  """Resolve DNS queries to localhost.
+
+  Possible alternative implementation:
+  http://howl.play-bow.org/pipermail/dnspython-users/2010-February/000119.html
+  """
+
+  STANDARD_QUERY_OPERATION_CODE = 0
+
+  def handle(self):
+    """Handle a DNS query.
+
+    IPv6 requests (with rdtype AAAA) receive mismatched IPv4 responses
+    (with rdtype A). To properly support IPv6, the http proxy would
+    need both types of addresses. By default, Windows XP does not
+    support IPv6.
+    """
+    self.data = self.rfile.read()
+    self.transaction_id = self.data[0]
+    self.flags = self.data[1]
+    self.qa_counts = self.data[4:6]
+    self.domain = ''
+    operation_code = (ord(self.data[2]) >> 3) & 15
+    if operation_code == self.STANDARD_QUERY_OPERATION_CODE:
+      self.wire_domain = self.data[12:]
+      self.domain = self._domain(self.wire_domain)
+    else:
+      logging.debug("DNS request with non-zero operation code: %s",
+                    operation_code)
+    ip = self.server.dns_lookup(self.domain)
+    if ip is None:
+      logging.debug('dnsproxy: %s -> NXDOMAIN', self.domain)
+      response = self.get_dns_no_such_name_response()
+    else:
+      if ip == self.server.server_address[0]:
+        logging.debug('dnsproxy: %s -> %s (replay web proxy)', self.domain, ip)
+      else:
+        logging.debug('dnsproxy: %s -> %s', self.domain, ip)
+      response = self.get_dns_response(ip)
+    self.wfile.write(response)
+
+  @classmethod
+  def _domain(cls, wire_domain):
+    domain = ''
+    index = 0
+    length = ord(wire_domain[index])
+    while length:
+      domain += wire_domain[index + 1:index + length + 1] + '.'
+      index += length + 1
+      length = ord(wire_domain[index])
+    return domain
+
+  def get_dns_response(self, ip):
+    packet = ''
+    if self.domain:
+      packet = (
+          self.transaction_id +
+          self.flags +
+          '\x81\x80' +        # standard query response, no error
+          self.qa_counts * 2 + '\x00\x00\x00\x00' +  # Q&A counts
+          self.wire_domain +
+          '\xc0\x0c'          # pointer to domain name
+          '\x00\x01'          # resource record type ("A" host address)
+          '\x00\x01'          # class of the data
+          '\x00\x00\x00\x3c'  # ttl (seconds)
+          '\x00\x04' +        # resource data length (4 bytes for ip)
+          socket.inet_aton(ip)
+          )
+    return packet
+
+  def get_dns_no_such_name_response(self):
+    query_message = message.from_wire(self.data)
+    response_message = message.make_response(query_message)
+    response_message.flags |= flags.AA | flags.RA
+    response_message.set_rcode(rcode.NXDOMAIN)
+    return response_message.to_wire()
+
+
+class DnsProxyServer(SocketServer.ThreadingUDPServer,
+                     daemonserver.DaemonServer):
+  # Increase the request queue size. The default value, 5, is set in
+  # SocketServer.TCPServer (the parent of BaseHTTPServer.HTTPServer).
+  # Since we're intercepting many domains through this single server,
+  # it is quite possible to get more than 5 concurrent requests.
+  request_queue_size = 256
+
+  # Allow sockets to be reused. See
+  # http://svn.python.org/projects/python/trunk/Lib/SocketServer.py for more
+  # details.
+  allow_reuse_address = True
+
+  # Don't prevent python from exiting when there is thread activity.
+  daemon_threads = True
+
+  def __init__(self, host='', port=53, dns_lookup=None):
+    """Initialize DnsProxyServer.
+
+    Args:
+      host: a host string (name or IP) to bind the dns proxy and to which
+        DNS requests will be resolved.
+      port: an integer port on which to bind the proxy.
+      dns_lookup: a list of filters to apply to lookup.
+    """
+    try:
+      SocketServer.ThreadingUDPServer.__init__(
+          self, (host, port), UdpDnsHandler)
+    except socket.error, (error_number, msg):
+      if error_number == errno.EACCES:
+        raise DnsProxyException(
+            'Unable to bind DNS server on (%s:%s)' % (host, port))
+      raise
+    self.dns_lookup = dns_lookup or (lambda host: self.server_address[0])
+    self.server_port = self.server_address[1]
+    logging.warning('DNS server started on %s:%d', self.server_address[0],
+                                                   self.server_address[1])
+
+  def cleanup(self):
+    try:
+      self.shutdown()
+      self.server_close()
+    except KeyboardInterrupt, e:
+      pass
+    logging.info('Stopped DNS server')
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/documentation/Contributing.md b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/documentation/Contributing.md
new file mode 100644
index 0000000..492649b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/documentation/Contributing.md
@@ -0,0 +1,41 @@
+# Contributing
+
+1. Thanks for considering contributing to Web Page Replay. You're awesome!
+2. Style Guide - The source code of Web Page Replay follows the [Google
+Python Style
+Guide](http://google-styleguide.googlecode.com/svn/trunk/pyguide.html) so you should familiarize yourself with those
+guidelines. You may also wish to email web-page-replay-dev at
+googlegroups.com for advice on your change before starting.
+3. Get the code - Fork this repo and clone it locally.
+4. Get a review - All submissions, including submissions by project members,
+require review.
+
+## Using rietveld
+
+1. Make sure that you have a fork of the original repo.
+2. Make your changes.
+3. Commit your changes.
+4. Run 'yes "" |git cl config' (first time only).
+5. Run 'git cl upload'.
+6. Once the review is approved, run 'git cl land' to land your changes. This also
+pushes your change to your forked branch.
+7. Login your github account and make a pull request to merge the change from
+your forked branch to the original repo.
+
+## The fine print
+
+Before we can use your code you have to sign the [Google Individual
+Contributor License
+Agreement](http://code.google.com/legal/individual-cla-v1.0.html), which you can do online. This is mainly
+because you own the copyright to your changes, even after your
+contribution becomes part of our codebase, so we need your permission to
+use and distribute your code. We also need to be sure of various other
+things, for instance that you'll tell us if you know that your code
+infringes on other people's patents. You don't have to do this until
+after you've submitted your code for review and a member has approved
+it, but you will have to do it before we can put your code into our
+codebase.
+
+Contributions made by corporations are covered by a different agreement
+than the one above, the [Software Grant and Corporate Contributor License
+Agreement](http://code.google.com/legal/corporate-cla-v1.0.html).
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/documentation/GettingStarted.md b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/documentation/GettingStarted.md
new file mode 100644
index 0000000..90790b4
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/documentation/GettingStarted.md
@@ -0,0 +1,169 @@
+# Prerequisites
+* A Mac running OS X 10.6 ("Snow Leopard") or Linux (tested with Ubuntu
+Lucid). Support for Windows is still experimental
+* [Python 2.6](http://www.python.org/download/releases/2.6.6/)
+
+# Install
+Only do this the first time.
+
+1. Open the Terminal application and download the source.
+```
+$ git clone https://github.com/chromium/web-page-replay.git
+```
+2. Move to the newly created directory.
+```
+$ cd web-page-replay
+```
+## Linux-specific install steps
+On Linux, Dummynet must be installed to simulate network conditions.
+
+1. For the Linux code, try downloading the [latest linux sources from Marta
+Carbone](http://info.iet.unipi.it/~marta/dummynet/). These are more up-to-date than what is found on the [Dummynet
+homepage](http://info.iet.unipi.it/~luigi/dummynet/).
+2. Build and install:
+```
+$ tar -C /tmp -xvzf ipfw3-20120119.tgz
+$ cd /tmp/ipfw3-20120119
+$ make
+[Ignore output like the following:]
+        echo "  ERROR: Kernel configuration is invalid.";\
+        echo "         include/generated/autoconf.h or
+include/config/auto.conf are missing.";\
+        echo "         Run 'make oldconfig && make prepare' on kernel
+src to fix it.";\
+[The lines will print without "echo" if there is an actual error.]
+$ sudo insmod dummynet2/ipfw_mod.ko
+$ sudo cp ipfw/ipfw /usr/local/sbin
+```
+3. To remove it later
+```
+$ sudo rmmod ipfw_mod.ko
+```
+## Windows-specific install steps
+*Windows support is experimental and not well tested.* On Windows XP, the
+Dummynet driver must be installed to simulate network conditions
+(Drivers for Windows Vista and Windows 7 are currently unavailable).
+
+1. Control Panel -> Network Connections -> Right-click adapter in use ->
+select Properties
+2. Click Install... -> Service -> Add... -> Have Disk...
+3. Browse... ->
+web-page-replay-read-only\third_party\ipfw_win32\netipfw.inf
+4. Click Open -> Ok -> Ok
+  - Accept any warnings for installing an unknown driver
+
+# Record
+First you must record the web page or pages that you wish to replay.
+
+1. Open the web browser you wish to use and clear its cache so that all
+resources will be requested from the network.
+2. Switch to the Terminal application and start the program in record mode.
+All HTTP requests performed on the machine while it is running will be
+saved into the archive.
+```
+$ sudo ./replay.py --record ~/archive.wpr
+```
+3. Load the web page or pages in the open web browser. Be sure to wait
+until each is fully loaded.
+4. Stop recording by killing the replay.py process with Ctrl+c. The archive
+will be saved to ~/archive.wpr.
+
+# Replay
+After you have created an archive, you may later replay it at any time.
+
+1. Start the program in replay mode with a previously recorded archive.
+```
+$ sudo ./replay.py ~/archive.wpr
+```
+2. Load recorded pages in a web browser. A 404 will be served for any pages
+or resources not in the recorded archive.
+3. Stop replaying by killing the replay.py process with Ctrl+c.
+
+## Network simulation examples
+During replay, you may simulate desired network conditions. This is
+useful for benchmarking.
+
+* 128KByte/s uplink bandwidth, 4Mbps/s downlink bandwidth with 100ms RTT
+time
+```
+$ sudo ./replay.py --up 128KByte/s --down 4Mbit/s --delay_ms=100 archive.wpr
+```
+* 1% packet loss rate
+```
+$ sudo ./replay.py --packet_loss_rate=0.01 ~/archive.wpr
+```
+
+## Using browser proxy settings
+You may choose to disable the forwarding of DNS requests to the local
+replay server. If DNS request forwarding is disabled, an external
+mechanism must be used to forward traffic to the replay server.
+
+* Disable DNS forwarding
+```
+$ ./replay.py --no-dns_forwarding --record ~/archive.wpr
+```
+* Forwarding traffic to replay server (via Google Chrome on linux)
+1. Go to Chrome Preferences -> Under the Hood -> Change Proxy Settings
+2. Under Manual Proxy configuration -> HTTP proxy, enter 127.0.0.1 for IP
+and the port that web page replay is configured to listen to (default
+80).
+
+Alternatively, traffic forwarding may also be configured via command
+line flags.
+```
+$ google-chrome --host-resolver-rules="MAP * 127.0.0.1:80,EXCLUDE localhost"
+```
+
+# HTTPS/SSL support
+By default, Web Page Replay, creates a self-signed certificate to serve
+SSL traffic. In order for it to work, browsers need to be configured to
+ignore certificate errors. Be aware that doing so opens a giant security
+hole.
+
+```
+$ google-chrome --ignore-certificate-errors
+```
+
+Firefox has [a configuration file for
+exceptions](https://developer.mozilla.org/En/Cert_override.txt). That requires listing
+each host that gets used. If you have a better solution, please add it
+to the comments below. IE and Safari options are also needed.
+
+To turn off SSL support, run replay.py with "--no-ssl".
+
+# Troubleshooting
+
+## Permission errors
+
+On Linux, either of the following two errors are permission problems:
+
+```
+python: can't open file './replay.py': [Errno 13] Permission denied
+```
+```
+Traceback (most recent call last):
+  File "./replay.py", line 50, in <module>
+    import dnsproxy
+  File "/home/slamm/p/wpr/dnsproxy.py", line 19, in <module>
+    import platformsettings
+ImportError: No module named platformsettings
+```
+This can happen if you checkout the files to an NFS directory. Either
+move the files to a local directory, or make them world
+readable/executable.
+
+## Unable to access auto mounted directories
+WPR can cause autofs to hang. On Ubuntu, the following command fixes it:
+
+```
+$ sudo restart autofs
+```
+
+# Help
+
+For full usage instructions and advanced options, see the program's
+help.
+
+```
+$ ./replay.py --help
+```
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/documentation/Rules.md b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/documentation/Rules.md
new file mode 100644
index 0000000..3517635
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/documentation/Rules.md
@@ -0,0 +1,95 @@
+WebPageReplay Rule Language
+===========================
+
+WebPageReplay rules allows developers to customize record/replay handling.
+
+Motiviation
+-----------
+
+Web sites often require custom replay logic, e.g.:
+
+  1. The recording uploads various metrics/logs to:
+
+        http://example.com/gen_204?emsg=foo
+
+     but, during replay, it uploads different parameters:
+
+        http://example.com/gen_204?emsg=bar
+
+     so (as-is) our replay fails.  We want "*/gen_204" to always respond
+     "HTTP 204 No Change".
+
+  2. The recording fetches data from one server:
+
+        http://mirrorA.example.com/stuff
+
+     but replay selects a different server:
+
+        http://mirrorB.example.com/stuff
+
+     which breaks replay.  We want "mirror*.example.com/stuff" to be equivalent.
+
+  3. The recorded URL + response contains a UID, e.g.:
+
+        http://example.com?q=foo  -->  "you sent foo."
+
+     but the replay asks for:
+
+        http://example.com?q=bar  -->  replay error!
+
+     We want it to reply "you sent bar."
+
+We could hack all the above rules into the code, but that can''t be (cleanly) extended or open sourced.
+
+Instead, we want a simple config file of "predicate --> action" rules.
+
+
+Format
+------
+
+The JSON-formatted rule file is specified on the command line:
+
+    replay.py ... --rules_path my_rules ...
+
+The rules file must contain an array of single-item objects, e.g.:
+
+    [{"comment": "ignore me"},
+     {"LogUrl": {"url": "example\\.com/logme.*"}},
+     {"LogUrl": {"url": "example\\.com/someotherpath"}}
+    ]
+
+All "comment" items are ignored and support arbitrary values, e.g., a string
+or commented-out rule(s).
+
+All other items must specify a string TYPE key and object ARGS value, e.g.:
+
+     {"LogUrl": {"url": "example\\.com/test", "stop": false}}
+
+The default TYPE package is "rules" and the default rule_parser
+"allowed_imports" is similarly restricted to only allow "rules" classes.
+
+The TYPE implementation class must match the Rule API defined in
+"rules/rule.py":
+
+  class Rule(object):
+    def IsType(self, rule_type_name): ...
+    def ApplyRule(self, return_value, request, response): ...
+
+The ARGS must match the rule-specific constructor, e.g.:
+
+    class LogUrl(rule.Rule):
+      def __init__(self, url, stop=False):
+        self._url_re = re.compile(url)
+        self._stop = stop
+      ...
+
+All rules of the same rule_type_name are chained together and applied in the
+same order as they appear in the input JSON file.
+
+
+Rules
+-------
+
+### rules.LogUrl:
+
+If the url pattern matches then log the request URL.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/documentation/WebPageReplayDiagram.png b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/documentation/WebPageReplayDiagram.png
new file mode 100644
index 0000000..fc98922
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/documentation/WebPageReplayDiagram.png
Binary files differ
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/exception_formatter.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/exception_formatter.py
new file mode 100644
index 0000000..8ceb0d4
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/exception_formatter.py
@@ -0,0 +1,96 @@
+# Copyright 2015 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import math
+import os
+import sys
+import traceback
+
+
+def PrintFormattedException(msg=None):
+  exception_class, exception, tb = sys.exc_info()
+
+  def _GetFinalFrame(tb_level):
+    while tb_level.tb_next:
+      tb_level = tb_level.tb_next
+    return tb_level.tb_frame
+
+  processed_tb = traceback.extract_tb(tb)
+  frame = _GetFinalFrame(tb)
+  exception_list = traceback.format_exception_only(exception_class, exception)
+  exception_string = '\n'.join(l.strip() for l in exception_list)
+
+  if msg:
+    print >> sys.stderr
+    print >> sys.stderr, msg
+
+  _PrintFormattedTrace(processed_tb, frame, exception_string)
+
+def PrintFormattedFrame(frame, exception_string=None):
+  _PrintFormattedTrace(traceback.extract_stack(frame), frame, exception_string)
+
+
+def _PrintFormattedTrace(processed_tb, frame, exception_string=None):
+  """Prints an Exception in a more useful format than the default.
+  """
+  print >> sys.stderr
+
+  # Format the traceback.
+  base_dir = os.path.dirname(__file__)
+  print >> sys.stderr, 'Traceback (most recent call last):'
+  for filename, line, function, text in processed_tb:
+    filename = os.path.abspath(filename)
+    if filename.startswith(base_dir):
+      filename = filename[len(base_dir)+1:]
+    print >> sys.stderr, '  %s at %s:%d' % (function, filename, line)
+    print >> sys.stderr, '    %s' % text
+
+  # Format the exception.
+  if exception_string:
+    print >> sys.stderr, exception_string
+
+  # Format the locals.
+  local_variables = [(variable, value) for variable, value in
+                     frame.f_locals.iteritems() if variable != 'self']
+  print >> sys.stderr
+  print >> sys.stderr, 'Locals:'
+  if local_variables:
+    longest_variable = max(len(v) for v, _ in local_variables)
+    for variable, value in sorted(local_variables):
+      value = repr(value)
+      possibly_truncated_value = _AbbreviateMiddleOfString(value, ' ... ', 1024)
+      truncation_indication = ''
+      if len(possibly_truncated_value) != len(value):
+        truncation_indication = ' (truncated)'
+      print >> sys.stderr, '  %s: %s%s' % (variable.ljust(longest_variable + 1),
+                                           possibly_truncated_value,
+                                           truncation_indication)
+  else:
+    print >> sys.stderr, '  No locals!'
+
+  print >> sys.stderr
+  sys.stderr.flush()
+
+
+def _AbbreviateMiddleOfString(target, middle, max_length):
+  if max_length < 0:
+    raise ValueError('Must provide positive max_length')
+  if len(middle) > max_length:
+    raise ValueError('middle must not be greater than max_length')
+
+  if len(target) <= max_length:
+    return target
+  half_length = (max_length - len(middle)) / 2.
+  return (target[:int(math.floor(half_length))] + middle +
+          target[-int(math.ceil(half_length)):])
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/httparchive.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/httparchive.py
new file mode 100755
index 0000000..bdfb66e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/httparchive.py
@@ -0,0 +1,1081 @@
+#!/usr/bin/env python
+# Copyright 2010 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""View and edit HTTP Archives.
+
+To list all URLs in an archive:
+  $ ./httparchive.py ls archive.wpr
+
+To view the content of all URLs from example.com:
+  $ ./httparchive.py cat --host example.com archive.wpr
+
+To view the content of a particular URL:
+  $ ./httparchive.py cat --host www.example.com --full_path /foo archive.wpr
+
+To view the content of all URLs:
+  $ ./httparchive.py cat archive.wpr
+
+To edit a particular URL:
+  $ ./httparchive.py edit --host www.example.com --full_path /foo archive.wpr
+
+To print statistics of an archive:
+  $ ./httparchive.py stats archive.wpr
+
+To print statistics of a set of URLs:
+  $ ./httparchive.py stats --host www.example.com archive.wpr
+
+To merge multiple archives
+  $ ./httparchive.py merge --merged_file new.wpr archive1.wpr archive2.wpr ...
+"""
+
+import calendar
+import certutils
+import datetime
+import cPickle
+import difflib
+import email.utils
+import httplib
+import httpzlib
+import json
+import logging
+import optparse
+import os
+import StringIO
+import subprocess
+import sys
+import tempfile
+import time
+import urlparse
+from collections import defaultdict
+
+
+
+def LogRunTime(fn):
+  """Annotation which logs the run time of the function."""
+  def wrapped(self, *args, **kwargs):
+    start_time = time.time()
+    try:
+      return fn(self, *args, **kwargs)
+    finally:
+      run_time = (time.time() - start_time) * 1000.0
+      logging.debug('%s: %dms', fn.__name__, run_time)
+  return wrapped
+
+
+class HttpArchiveException(Exception):
+  """Base class for all exceptions in httparchive."""
+  pass
+
+
+class HttpArchive(dict):
+  """Dict with ArchivedHttpRequest keys and ArchivedHttpResponse values.
+
+  Attributes:
+    responses_by_host: dict of {hostname, {request: response}}. This must remain
+        in sync with the underlying dict of self. It is used as an optimization
+        so that get_requests() doesn't have to linearly search all requests in
+        the archive to find potential matches.
+  """
+
+  def __init__(self):  # pylint: disable=super-init-not-called
+    self.responses_by_host = defaultdict(dict)
+
+  def __setstate__(self, state):
+    """Influence how to unpickle.
+
+    Args:
+      state: a dictionary for __dict__
+    """
+    self.__dict__.update(state)
+    self.responses_by_host = defaultdict(dict)
+    for request in self:
+      self.responses_by_host[request.host][request] = self[request]
+
+  def __getstate__(self):
+    """Influence how to pickle.
+
+    Returns:
+      a dict to use for pickling
+    """
+    state = self.__dict__.copy()
+    del state['responses_by_host']
+    return state
+
+  def __setitem__(self, key, value):
+    super(HttpArchive, self).__setitem__(key, value)
+    if hasattr(self, 'responses_by_host'):
+      self.responses_by_host[key.host][key] = value
+
+  def __delitem__(self, key):
+    super(HttpArchive, self).__delitem__(key)
+    del self.responses_by_host[key.host][key]
+
+  def get(self, request, default=None):
+    """Return the archived response for a given request.
+
+    Does extra checking for handling some HTTP request headers.
+
+    Args:
+      request: instance of ArchivedHttpRequest
+      default: default value to return if request is not found
+
+    Returns:
+      Instance of ArchivedHttpResponse or default if no matching
+      response is found
+    """
+    if request in self:
+      return self[request]
+    return self.get_conditional_response(request, default)
+
+  def get_conditional_response(self, request, default):
+    """Get the response based on the conditional HTTP request headers.
+
+    Args:
+      request: an ArchivedHttpRequest representing the original request.
+      default: default ArchivedHttpResponse
+          original request with matched headers removed.
+
+    Returns:
+      an ArchivedHttpResponse with a status of 200, 302 (not modified), or
+          412 (precondition failed)
+    """
+    response = default
+    if request.is_conditional():
+      stripped_request = request.create_request_without_conditions()
+      if stripped_request in self:
+        response = self[stripped_request]
+        if response.status == 200:
+          status = self.get_conditional_status(request, response)
+          if status != 200:
+            response = create_response(status)
+    return response
+
+  def get_conditional_status(self, request, response):
+    status = 200
+    last_modified = email.utils.parsedate(
+        response.update_date(response.get_header('last-modified')))
+    response_etag = response.get_header('etag')
+    is_get_or_head = request.command.upper() in ('GET', 'HEAD')
+
+    match_value = request.headers.get('if-match', None)
+    if match_value:
+      if self.is_etag_match(match_value, response_etag):
+        status = 200
+      else:
+        status = 412  # precondition failed
+    none_match_value = request.headers.get('if-none-match', None)
+    if none_match_value:
+      if self.is_etag_match(none_match_value, response_etag):
+        status = 304
+      elif is_get_or_head:
+        status = 200
+      else:
+        status = 412
+    if is_get_or_head and last_modified:
+      for header in ('if-modified-since', 'if-unmodified-since'):
+        date = email.utils.parsedate(request.headers.get(header, None))
+        if date:
+          if ((header == 'if-modified-since' and last_modified > date) or
+              (header == 'if-unmodified-since' and last_modified < date)):
+            if status != 412:
+              status = 200
+          else:
+            status = 304  # not modified
+    return status
+
+  @staticmethod
+  def is_etag_match(request_etag, response_etag):
+    """Determines whether the entity tags of the request/response matches.
+
+    Args:
+      request_etag: the value string of the "if-(none)-match:"
+                    portion of the request header
+      response_etag: the etag value of the response
+
+    Returns:
+      True on match, False otherwise
+    """
+    response_etag = response_etag.strip('" ')
+    for etag in request_etag.split(','):
+      etag = etag.strip('" ')
+      if etag in ('*', response_etag):
+        return True
+    return False
+
+  def get_requests(self, command=None, host=None, full_path=None, is_ssl=None,
+                   use_query=True):
+    """Return a list of requests that match the given args."""
+    if host:
+      return [r for r in self.responses_by_host[host]
+              if r.matches(command, None, full_path, is_ssl,
+                           use_query=use_query)]
+    else:
+      return [r for r in self
+              if r.matches(command, host, full_path, is_ssl,
+                           use_query=use_query)]
+
+  def ls(self, command=None, host=None, full_path=None):
+    """List all URLs that match given params."""
+    return ''.join(sorted(
+        '%s\n' % r for r in self.get_requests(command, host, full_path)))
+
+  def cat(self, command=None, host=None, full_path=None):
+    """Print the contents of all URLs that match given params."""
+    out = StringIO.StringIO()
+    for request in self.get_requests(command, host, full_path):
+      print >>out, str(request)
+      print >>out, 'Untrimmed request headers:'
+      for k in request.headers:
+        print >>out, '    %s: %s' % (k, request.headers[k])
+      if request.request_body:
+        print >>out, request.request_body
+      print >>out, '---- Response Info', '-' * 51
+      response = self[request]
+      chunk_lengths = [len(x) for x in response.response_data]
+      print >>out, ('Status: %s\n'
+                    'Reason: %s\n'
+                    'Headers delay: %s\n'
+                    'Untrimmed response headers:') % (
+          response.status, response.reason, response.delays['headers'])
+      for k, v in response.original_headers:
+        print >>out, '    %s: %s' % (k, v)
+      print >>out, ('Chunk count: %s\n'
+                    'Chunk lengths: %s\n'
+                    'Chunk delays: %s') % (
+          len(chunk_lengths), chunk_lengths, response.delays['data'])
+      body = response.get_data_as_text()
+      print >>out, '---- Response Data', '-' * 51
+      if body:
+        print >>out, body
+      else:
+        print >>out, '[binary data]'
+      print >>out, '=' * 70
+    return out.getvalue()
+
+  def stats(self, command=None, host=None, full_path=None):
+    """Print stats about the archive for all URLs that match given params."""
+    matching_requests = self.get_requests(command, host, full_path)
+    if not matching_requests:
+      print 'Failed to find any requests matching given command, host, path.'
+      return
+
+    out = StringIO.StringIO()
+    stats = {
+        'Total': len(matching_requests),
+        'Domains': defaultdict(int),
+        'HTTP_response_code': defaultdict(int),
+        'content_type': defaultdict(int),
+        'Documents': defaultdict(int),
+        }
+
+    for request in matching_requests:
+      stats['Domains'][request.host] += 1
+      stats['HTTP_response_code'][self[request].status] += 1
+
+      content_type = self[request].get_header('content-type')
+      # Remove content type options for readability and higher level groupings.
+      str_content_type = str(content_type.split(';')[0]
+                            if content_type else None)
+      stats['content_type'][str_content_type] += 1
+
+      #  Documents are the main URL requested and not a referenced resource.
+      if str_content_type == 'text/html' and not 'referer' in request.headers:
+        stats['Documents'][request.host] += 1
+
+    print >>out, json.dumps(stats, indent=4)
+    return out.getvalue()
+
+  def merge(self, merged_archive=None, other_archives=None):
+    """Merge multiple archives into merged_archive by 'chaining' resources,
+    only resources that are not part of the accumlated archive are added"""
+    if not other_archives:
+      print 'No archives passed to merge'
+      return
+
+    # Note we already loaded 'replay_file'.
+    print 'Loaded %d responses' % len(self)
+
+    for archive in other_archives:
+      if not os.path.exists(archive):
+        print 'Error: Replay file "%s" does not exist' % archive
+        return
+
+      http_archive_other = HttpArchive.Load(archive)
+      print 'Loaded %d responses from %s' % (len(http_archive_other), archive)
+      for r in http_archive_other:
+        # Only resources that are not already part of the current archive
+        # get added.
+        if r not in self:
+          print '\t %s ' % r
+          self[r] = http_archive_other[r]
+    self.Persist('%s' % merged_archive)
+
+  def edit(self, command=None, host=None, full_path=None):
+    """Edits the single request which matches given params."""
+    editor = os.getenv('EDITOR')
+    if not editor:
+      print 'You must set the EDITOR environmental variable.'
+      return
+
+    matching_requests = self.get_requests(command, host, full_path)
+    if not matching_requests:
+      print ('Failed to find any requests matching given command, host, '
+             'full_path.')
+      return
+
+    if len(matching_requests) > 1:
+      print 'Found multiple matching requests. Please refine.'
+      print self.ls(command, host, full_path)
+
+    response = self[matching_requests[0]]
+    tmp_file = tempfile.NamedTemporaryFile(delete=False)
+    tmp_file.write(response.get_response_as_text())
+    tmp_file.close()
+    subprocess.check_call([editor, tmp_file.name])
+    response.set_response_from_text(''.join(open(tmp_file.name).readlines()))
+    os.remove(tmp_file.name)
+
+  def find_closest_request(self, request, use_path=False):
+    """Find the closest matching request in the archive to the given request.
+
+    Args:
+      request: an ArchivedHttpRequest
+      use_path: If True, closest matching request's path component must match.
+        (Note: this refers to the 'path' component within the URL, not the
+         'full path' which includes the query string component.)
+
+        If use_path=True, candidate will NOT match in example below
+        e.g. request   = GET www.test.com/a?p=1
+             candidate = GET www.test.com/b?p=1
+
+        Even if use_path=False, urls with same paths are always favored.
+        For example, candidate1 is considered a better match than candidate2.
+          request    = GET www.test.com/a?p=1&q=2&r=3
+          candidate1 = GET www.test.com/a?s=4
+          candidate2 = GET www.test.com/b?p=1&q=2&r=3
+
+    Returns:
+      If a close match is found, return the instance of ArchivedHttpRequest.
+      Otherwise, return None.
+    """
+    # Start with strictest constraints. This trims search space considerably.
+    requests = self.get_requests(request.command, request.host,
+                                 request.full_path, is_ssl=request.is_ssl,
+                                 use_query=True)
+    # Relax constraint: use_query if there is no match.
+    if not requests:
+      requests = self.get_requests(request.command, request.host,
+                                   request.full_path, is_ssl=request.is_ssl,
+                                   use_query=False)
+    # Relax constraint: full_path if there is no match and use_path=False.
+    if not requests and not use_path:
+      requests = self.get_requests(request.command, request.host,
+                                   None, is_ssl=request.is_ssl,
+                                   use_query=False)
+
+    if not requests:
+      return None
+
+    if len(requests) == 1:
+      return requests[0]
+
+    matcher = difflib.SequenceMatcher(b=request.cmp_seq)
+
+    # quick_ratio() is cheap to compute, but ratio() is expensive. So we call
+    # quick_ratio() on all requests, sort them descending, and then loop through
+    # until we find a candidate whose ratio() is >= the next quick_ratio().
+    # This works because quick_ratio() is guaranteed to be an upper bound on
+    # ratio().
+    candidates = []
+    for candidate in requests:
+      matcher.set_seq1(candidate.cmp_seq)
+      candidates.append((matcher.quick_ratio(), candidate))
+
+    candidates.sort(reverse=True, key=lambda c: c[0])
+
+    best_match = (0, None)
+    for i in xrange(len(candidates)):
+      matcher.set_seq1(candidates[i][1].cmp_seq)
+      best_match = max(best_match, (matcher.ratio(), candidates[i][1]))
+      if i + 1 < len(candidates) and best_match[0] >= candidates[i+1][0]:
+        break
+    return best_match[1]
+
+  def diff(self, request):
+    """Diff the given request to the closest matching request in the archive.
+
+    Args:
+      request: an ArchivedHttpRequest
+    Returns:
+      If a close match is found, return a textual diff between the requests.
+      Otherwise, return None.
+    """
+    request_lines = request.formatted_request.split('\n')
+    closest_request = self.find_closest_request(request)
+    if closest_request:
+      closest_request_lines = closest_request.formatted_request.split('\n')
+      return '\n'.join(difflib.ndiff(closest_request_lines, request_lines))
+    return None
+
+  def get_server_cert(self, host):
+    """Gets certificate from the server and stores it in archive"""
+    request = ArchivedHttpRequest('SERVER_CERT', host, '', None, {})
+    if request not in self:
+      self[request] = create_response(200, body=certutils.get_host_cert(host))
+    return self[request].response_data[0]
+
+  def get_certificate(self, host):
+    request = ArchivedHttpRequest('DUMMY_CERT', host, '', None, {})
+    if request not in self:
+      self[request] = create_response(200, body=self._generate_cert(host))
+    return self[request].response_data[0]
+
+  @classmethod
+  def AssertWritable(cls, filename):
+    """Raises an IOError if filename is not writable."""
+    persist_dir = os.path.dirname(os.path.abspath(filename))
+    if not os.path.exists(persist_dir):
+      raise IOError('Directory does not exist: %s' % persist_dir)
+    if os.path.exists(filename):
+      if not os.access(filename, os.W_OK):
+        raise IOError('Need write permission on file: %s' % filename)
+    elif not os.access(persist_dir, os.W_OK):
+      raise IOError('Need write permission on directory: %s' % persist_dir)
+
+  @classmethod
+  def Load(cls, filename):
+    """Load an instance from filename."""
+    return cPickle.load(open(filename, 'rb'))
+
+  def Persist(self, filename):
+    """Persist all state to filename."""
+    try:
+      original_checkinterval = sys.getcheckinterval()
+      sys.setcheckinterval(2**31-1)  # Lock out other threads so nothing can
+                                     # modify |self| during pickling.
+      pickled_self = cPickle.dumps(self, cPickle.HIGHEST_PROTOCOL)
+    finally:
+      sys.setcheckinterval(original_checkinterval)
+    with open(filename, 'wb') as f:
+      f.write(pickled_self)
+
+
+class ArchivedHttpRequest(object):
+  """Record all the state that goes into a request.
+
+  ArchivedHttpRequest instances are considered immutable so they can
+  serve as keys for HttpArchive instances.
+  (The immutability is not enforced.)
+
+  Upon creation, the headers are "trimmed" (i.e. edited or dropped)
+  and saved to self.trimmed_headers to allow requests to match in a wider
+  variety of playback situations (e.g. using different user agents).
+
+  For unpickling, 'trimmed_headers' is recreated from 'headers'. That
+  allows for changes to the trim function and can help with debugging.
+  """
+  CONDITIONAL_HEADERS = [
+      'if-none-match', 'if-match',
+      'if-modified-since', 'if-unmodified-since']
+
+  def __init__(self, command, host, full_path, request_body, headers,
+               is_ssl=False):
+    """Initialize an ArchivedHttpRequest.
+
+    Args:
+      command: a string (e.g. 'GET' or 'POST').
+      host: a host name (e.g. 'www.google.com').
+      full_path: a request path.  Includes everything after the host & port in
+          the URL (e.g. '/search?q=dogs').
+      request_body: a request body string for a POST or None.
+      headers: {key: value, ...} where key and value are strings.
+      is_ssl: a boolean which is True iff request is make via SSL.
+    """
+    self.command = command
+    self.host = host
+    self.full_path = full_path
+    parsed_url = urlparse.urlparse(full_path) if full_path else None
+    self.path = parsed_url.path if parsed_url else None
+    self.request_body = request_body
+    self.headers = headers
+    self.is_ssl = is_ssl
+    self.trimmed_headers = self._TrimHeaders(headers)
+    self.formatted_request = self._GetFormattedRequest()
+    self.cmp_seq = self._GetCmpSeq(parsed_url.query if parsed_url else None)
+
+  def __str__(self):
+    scheme = 'https' if self.is_ssl else 'http'
+    return '%s %s://%s%s %s' % (
+        self.command, scheme, self.host, self.full_path, self.trimmed_headers)
+
+  def __repr__(self):
+    return repr((self.command, self.host, self.full_path, self.request_body,
+                 self.trimmed_headers, self.is_ssl))
+
+  def __hash__(self):
+    """Return a integer hash to use for hashed collections including dict."""
+    return hash(repr(self))
+
+  def __eq__(self, other):
+    """Define the __eq__ method to match the hash behavior."""
+    return repr(self) == repr(other)
+
+  def __setstate__(self, state):
+    """Influence how to unpickle.
+
+    "headers" are the original request headers.
+    "trimmed_headers" are the trimmed headers used for matching requests
+    during replay.
+
+    Args:
+      state: a dictionary for __dict__
+    """
+    if 'full_headers' in state:
+      # Fix older version of archive.
+      state['headers'] = state['full_headers']
+      del state['full_headers']
+    if 'headers' not in state:
+      raise HttpArchiveException(
+          'Archived HTTP request is missing "headers". The HTTP archive is'
+          ' likely from a previous version and must be re-recorded.')
+    if 'path' in state:
+      # before, 'path' and 'path_without_query' were used and 'path' was
+      # pickled.  Now, 'path' has been renamed to 'full_path' and
+      # 'path_without_query' has been renamed to 'path'.  'full_path' is
+      # pickled, but 'path' is not.  If we see 'path' here it means we are
+      # dealing with an older archive.
+      state['full_path'] = state['path']
+      del state['path']
+    state['trimmed_headers'] = self._TrimHeaders(dict(state['headers']))
+    if 'is_ssl' not in state:
+      state['is_ssl'] = False
+    self.__dict__.update(state)
+    parsed_url = urlparse.urlparse(self.full_path)
+    self.path = parsed_url.path
+    self.formatted_request = self._GetFormattedRequest()
+    self.cmp_seq = self._GetCmpSeq(parsed_url.query)
+
+  def __getstate__(self):
+    """Influence how to pickle.
+
+    Returns:
+      a dict to use for pickling
+    """
+    state = self.__dict__.copy()
+    del state['trimmed_headers']
+    del state['path']
+    del state['formatted_request']
+    del state['cmp_seq']
+    return state
+
+  def _GetFormattedRequest(self):
+    """Format request to make diffs easier to read.
+
+    Returns:
+      A string consisting of the request. Example:
+      'GET www.example.com/path\nHeader-Key: header value\n'
+    """
+    parts = ['%s %s%s\n' % (self.command, self.host, self.full_path)]
+    if self.request_body:
+      parts.append('%s\n' % self.request_body)
+    for k, v in self.trimmed_headers:
+      k = '-'.join(x.capitalize() for x in k.split('-'))
+      parts.append('%s: %s\n' % (k, v))
+    return ''.join(parts)
+
+  def _GetCmpSeq(self, query=None):
+    """Compute a sequence out of query and header for difflib to compare.
+    For example:
+      [('q1', 'a1'), ('q2', 'a2'), ('k1', 'v1'), ('k2', 'v2')]
+    will be returned for a request with URL:
+      http://example.com/index.html?q1=a2&q2=a2
+    and header:
+      k1: v1
+      k2: v2
+
+    Args:
+      query: the query string in the URL.
+
+    Returns:
+      A sequence for difflib to compare.
+    """
+    if not query:
+      return self.trimmed_headers
+    return sorted(urlparse.parse_qsl(query)) + self.trimmed_headers
+
+  def matches(self, command=None, host=None, full_path=None, is_ssl=None,
+              use_query=True):
+    """Returns true iff the request matches all parameters.
+
+    Args:
+      command: a string (e.g. 'GET' or 'POST').
+      host: a host name (e.g. 'www.google.com').
+      full_path: a request path with query string (e.g. '/search?q=dogs')
+      is_ssl: whether the request is secure.
+      use_query:
+        If use_query is True, request matching uses both the hierarchical path
+        and query string component.
+        If use_query is False, request matching only uses the hierarchical path
+
+        e.g. req1 = GET www.test.com/index?aaaa
+             req2 = GET www.test.com/index?bbbb
+
+        If use_query is True, req1.matches(req2) evaluates to False
+        If use_query is False, req1.matches(req2) evaluates to True
+
+    Returns:
+      True iff the request matches all parameters
+    """
+    if command is not None and command != self.command:
+      return False
+    if is_ssl is not None and is_ssl != self.is_ssl:
+      return False
+    if host is not None and host != self.host:
+      return False
+    if full_path is None:
+      return True
+    if use_query:
+      return full_path == self.full_path
+    else:
+      return self.path == urlparse.urlparse(full_path).path
+
+  @classmethod
+  def _TrimHeaders(cls, headers):
+    """Removes headers that are known to cause problems during replay.
+
+    These headers are removed for the following reasons:
+    - accept: Causes problems with www.bing.com. During record, CSS is fetched
+              with *. During replay, it's text/css.
+    - accept-charset, accept-language, referer: vary between clients.
+    - cache-control:  sometimes sent from Chrome with 'max-age=0' as value.
+    - connection, method, scheme, url, version: Cause problems with spdy.
+    - cookie: Extremely sensitive to request/response order.
+    - keep-alive: Doesn't affect the content of the request, only some
+      transient state of the transport layer.
+    - user-agent: Changes with every Chrome version.
+    - proxy-connection: Sent for proxy requests.
+    - x-chrome-variations, x-client-data: Unique to each Chrome binary. Used by
+      Google to collect statistics about Chrome's enabled features.
+
+    Another variant to consider is dropping only the value from the header.
+    However, this is particularly bad for the cookie header, because the
+    presence of the cookie depends on the responses we've seen when the request
+    is made.
+
+    Args:
+      headers: {header_key: header_value, ...}
+
+    Returns:
+      [(header_key, header_value), ...]  # (with undesirable headers removed)
+    """
+    # TODO(tonyg): Strip sdch from the request headers because we can't
+    # guarantee that the dictionary will be recorded, so replay may not work.
+    if 'accept-encoding' in headers:
+      accept_encoding = headers['accept-encoding']
+      accept_encoding = accept_encoding.replace('sdch', '')
+      # Strip lzma so Opera's requests matches archives recorded using Chrome.
+      accept_encoding = accept_encoding.replace('lzma', '')
+      stripped_encodings = [e.strip() for e in accept_encoding.split(',')]
+      accept_encoding = ','.join(filter(bool, stripped_encodings))
+      headers['accept-encoding'] = accept_encoding
+    undesirable_keys = [
+        'accept', 'accept-charset', 'accept-language', 'cache-control',
+        'connection', 'cookie', 'keep-alive', 'method',
+        'referer', 'scheme', 'url', 'version', 'user-agent', 'proxy-connection',
+        'x-chrome-variations', 'x-client-data']
+    return sorted([(k, v) for k, v in headers.items()
+                   if k.lower() not in undesirable_keys])
+
+  def is_conditional(self):
+    """Return list of headers that match conditional headers."""
+    for header in self.CONDITIONAL_HEADERS:
+      if header in self.headers:
+        return True
+    return False
+
+  def create_request_without_conditions(self):
+    stripped_headers = dict((k, v) for k, v in self.headers.iteritems()
+                            if k.lower() not in self.CONDITIONAL_HEADERS)
+    return ArchivedHttpRequest(
+        self.command, self.host, self.full_path, self.request_body,
+        stripped_headers, self.is_ssl)
+
+class ArchivedHttpResponse(object):
+  """All the data needed to recreate all HTTP response.
+
+  Upon creation, the headers are "trimmed" (i.e. edited or dropped).
+  The original headers are saved to self.original_headers, while the
+  trimmed ones are used to allow responses to match in a wider variety
+  of playback situations.
+
+  For pickling, 'original_headers' are stored in the archive.  For unpickling
+  the headers are trimmed again. That allows for changes to the trim
+  function and can help with debugging.
+  """
+
+  # CHUNK_EDIT_SEPARATOR is used to edit and view text content.
+  # It is not sent in responses. It is added by get_data_as_text()
+  # and removed by set_data().
+  CHUNK_EDIT_SEPARATOR = '[WEB_PAGE_REPLAY_CHUNK_BOUNDARY]'
+
+  # DELAY_EDIT_SEPARATOR is used to edit and view server delays.
+  DELAY_EDIT_SEPARATOR = ('\n[WEB_PAGE_REPLAY_EDIT_ARCHIVE --- '
+                          'Delays are above. Response content is below.]\n')
+
+  # This date was used in deterministic.js prior to switching to recorded
+  # request time.  See https://github.com/chromium/web-page-replay/issues/71
+  # for details.
+  DEFAULT_REQUEST_TIME = datetime.datetime(2008, 2, 29, 2, 26, 8, 254000)
+
+  def __init__(self, version, status, reason, headers, response_data,
+               delays=None, request_time=None):
+    """Initialize an ArchivedHttpResponse.
+
+    Args:
+      version: HTTP protocol version used by server.
+          10 for HTTP/1.0, 11 for HTTP/1.1 (same as httplib).
+      status: Status code returned by server (e.g. 200).
+      reason: Reason phrase returned by server (e.g. "OK").
+      headers: list of (header, value) tuples.
+      response_data: list of content chunks.
+          Concatenating the chunks gives the complete contents
+          (i.e. the chunks do not have any lengths or delimiters).
+          Do not include the final, zero-length chunk that marks the end.
+      delays: dict of (ms) delays for 'connect', 'headers' and 'data'.
+          e.g. {'connect': 50, 'headers': 150, 'data': [0, 10, 10]}
+          connect - The time to connect to the server.
+            Each resource has a value because Replay's record mode captures it.
+            This includes the time for the SYN and SYN/ACK (1 rtt).
+          headers - The time elapsed between the TCP connect and the headers.
+            This typically includes all the server-time to generate a response.
+          data - If the response is chunked, these are the times for each chunk.
+    """
+    self.version = version
+    self.status = status
+    self.reason = reason
+    self.original_headers = headers
+    self.headers = self._TrimHeaders(headers)
+    self.response_data = response_data
+    self.delays = delays
+    self.fix_delays()
+    self.request_time = (
+        request_time or ArchivedHttpResponse.DEFAULT_REQUEST_TIME
+    )
+
+  def fix_delays(self):
+    """Initialize delays, or check the number of data delays."""
+    expected_num_delays = len(self.response_data)
+    if not self.delays:
+      self.delays = {
+          'connect': 0,
+          'headers': 0,
+          'data': [0] * expected_num_delays
+          }
+    else:
+      num_delays = len(self.delays['data'])
+      if num_delays != expected_num_delays:
+        raise HttpArchiveException(
+            'Server delay length mismatch: %d (expected %d): %s',
+            num_delays, expected_num_delays, self.delays['data'])
+
+  @classmethod
+  def _TrimHeaders(cls, headers):
+    """Removes headers that are known to cause problems during replay.
+
+    These headers are removed for the following reasons:
+    - content-security-policy: Causes problems with script injection.
+    """
+    undesirable_keys = ['content-security-policy']
+    return [(k, v) for k, v in headers if k.lower() not in undesirable_keys]
+
+  def __repr__(self):
+    return repr((self.version, self.status, self.reason, sorted(self.headers),
+                 self.response_data, self.request_time))
+
+  def __hash__(self):
+    """Return a integer hash to use for hashed collections including dict."""
+    return hash(repr(self))
+
+  def __eq__(self, other):
+    """Define the __eq__ method to match the hash behavior."""
+    return repr(self) == repr(other)
+
+  def __setstate__(self, state):
+    """Influence how to unpickle.
+
+    "original_headers" are the original request headers.
+    "headers" are the trimmed headers used for replaying responses.
+
+    Args:
+      state: a dictionary for __dict__
+    """
+    if 'server_delays' in state:
+      state['delays'] = {
+          'connect': 0,
+          'headers': 0,
+          'data': state['server_delays']
+          }
+      del state['server_delays']
+    elif 'delays' not in state:
+      state['delays'] = None
+    # Set to date that was hardcoded in deterministic.js originally.
+    state.setdefault('request_time', ArchivedHttpResponse.DEFAULT_REQUEST_TIME)
+    state['original_headers'] = state['headers']
+    state['headers'] = self._TrimHeaders(state['original_headers'])
+    self.__dict__.update(state)
+    self.fix_delays()
+
+  def __getstate__(self):
+    """Influence how to pickle.
+
+    Returns:
+      a dict to use for pickling
+    """
+    state = self.__dict__.copy()
+    state['headers'] = state['original_headers']
+    del state['original_headers']
+    return state
+
+  def get_header(self, key, default=None):
+    for k, v in self.headers:
+      if key.lower() == k.lower():
+        return v
+    return default
+
+  def set_header(self, key, value):
+    for i, (k, v) in enumerate(self.headers):
+      if key == k:
+        self.headers[i] = (key, value)
+        return
+    self.headers.append((key, value))
+
+  def remove_header(self, key):
+    for i, (k, v) in enumerate(self.headers):
+      if key.lower() == k.lower():
+        self.headers.pop(i)
+        return
+
+  @staticmethod
+  def _get_epoch_seconds(date_str):
+    """Return the epoch seconds of a date header.
+
+    Args:
+      date_str: a date string (e.g. "Thu, 01 Dec 1994 16:00:00 GMT")
+    Returns:
+      epoch seconds as a float
+    """
+    date_tuple = email.utils.parsedate(date_str)
+    if date_tuple:
+      return calendar.timegm(date_tuple)
+    return None
+
+  def update_date(self, date_str, now=None):
+    """Return an updated date based on its delta from the "Date" header.
+
+    For example, if |date_str| is one week later than the "Date" header,
+    then the returned date string is one week later than the current date.
+
+    Args:
+      date_str: a date string (e.g. "Thu, 01 Dec 1994 16:00:00 GMT")
+    Returns:
+      a date string
+    """
+    date_seconds = self._get_epoch_seconds(self.get_header('date'))
+    header_seconds = self._get_epoch_seconds(date_str)
+    if date_seconds and header_seconds:
+      updated_seconds = header_seconds + (now or time.time()) - date_seconds
+      return email.utils.formatdate(updated_seconds, usegmt=True)
+    return date_str
+
+  def is_gzip(self):
+    return self.get_header('content-encoding') == 'gzip'
+
+  def is_compressed(self):
+    return self.get_header('content-encoding') in ('gzip', 'deflate')
+
+  def is_chunked(self):
+    return self.get_header('transfer-encoding') == 'chunked'
+
+  def get_data_as_chunks(self):
+    """Return content as a list of strings, each corresponding to a chunk.
+
+    Uncompresses the chunks, if needed.
+    """
+    content_type = self.get_header('content-type')
+    if (not content_type or
+        not (content_type.startswith('text/') or
+             content_type == 'application/x-javascript' or
+             content_type.startswith('application/json'))):
+      return None
+    if self.is_compressed():
+      return httpzlib.uncompress_chunks(self.response_data, self.is_gzip())
+    else:
+      return self.response_data
+
+  def get_data_as_text(self):
+    """Return content as a single string.
+
+    Uncompresses and concatenates chunks with CHUNK_EDIT_SEPARATOR.
+    """
+    return self.CHUNK_EDIT_SEPARATOR.join(self.get_data_as_chunks())
+
+  def get_delays_as_text(self):
+    """Return delays as editable text."""
+    return json.dumps(self.delays, indent=2)
+
+  def get_response_as_text(self):
+    """Returns response content as a single string.
+
+    Server delays are separated on a per-chunk basis. Delays are in seconds.
+    Response content begins after DELAY_EDIT_SEPARATOR
+    """
+    data = self.get_data_as_text()
+    if data is None:
+      logging.warning('Data can not be represented as text.')
+      data = ''
+    delays = self.get_delays_as_text()
+    return self.DELAY_EDIT_SEPARATOR.join((delays, data))
+
+  def set_data_from_chunks(self, text_chunks):
+    """Inverse of get_data_as_chunks().
+
+    Compress, if needed.
+    """
+    if self.is_compressed():
+      self.response_data = httpzlib.compress_chunks(text_chunks, self.is_gzip())
+    else:
+      self.response_data = text_chunks
+    if not self.is_chunked():
+      content_length = sum(len(c) for c in self.response_data)
+      self.set_header('content-length', str(content_length))
+
+  def set_data(self, text):
+    """Inverse of get_data_as_text().
+
+    Split on CHUNK_EDIT_SEPARATOR and compress if needed.
+    """
+    self.set_data_from_chunks(text.split(self.CHUNK_EDIT_SEPARATOR))
+
+  def set_delays(self, delays_text):
+    """Inverse of get_delays_as_text().
+
+    Args:
+      delays_text: JSON encoded text such as the following:
+          {
+            connect: 80,
+            headers: 80,
+            data: [6, 55, 0]
+          }
+        Times are in milliseconds.
+        Each data delay corresponds with one response_data value.
+    """
+    try:
+      self.delays = json.loads(delays_text)
+    except (ValueError, KeyError) as e:
+      logging.critical('Unable to parse delays %s: %s', delays_text, e)
+    self.fix_delays()
+
+  def set_response_from_text(self, text):
+    """Inverse of get_response_as_text().
+
+    Modifies the state of the archive according to the textual representation.
+    """
+    try:
+      delays, data = text.split(self.DELAY_EDIT_SEPARATOR)
+    except ValueError:
+      logging.critical(
+          'Error parsing text representation. Skipping edits.')
+      return
+    self.set_delays(delays)
+    self.set_data(data)
+
+
+def create_response(status, reason=None, headers=None, body=None):
+  """Convenience method for creating simple ArchivedHttpResponse objects."""
+  if reason is None:
+    reason = httplib.responses.get(status, 'Unknown')
+  if headers is None:
+    headers = [('content-type', 'text/plain')]
+  if body is None:
+    body = "%s %s" % (status, reason)
+  return ArchivedHttpResponse(11, status, reason, headers, [body])
+
+
+def main():
+  class PlainHelpFormatter(optparse.IndentedHelpFormatter):
+    def format_description(self, description):
+      if description:
+        return description + '\n'
+      else:
+        return ''
+
+  option_parser = optparse.OptionParser(
+      usage='%prog [ls|cat|edit|stats|merge] [options] replay_file(s)',
+      formatter=PlainHelpFormatter(),
+      description=__doc__,
+      epilog='http://code.google.com/p/web-page-replay/')
+
+  option_parser.add_option('-c', '--command', default=None,
+      action='store',
+      type='string',
+      help='Only show URLs matching this command.')
+  option_parser.add_option('-o', '--host', default=None,
+      action='store',
+      type='string',
+      help='Only show URLs matching this host.')
+  option_parser.add_option('-p', '--full_path', default=None,
+      action='store',
+      type='string',
+      help='Only show URLs matching this full path.')
+  option_parser.add_option('-f', '--merged_file', default=None,
+        action='store',
+        type='string',
+        help='The output file to use when using the merge command.')
+
+  options, args = option_parser.parse_args()
+
+  # Merge command expects an umlimited number of archives.
+  if len(args) < 2:
+    print 'args: %s' % args
+    option_parser.error('Must specify a command and replay_file')
+
+  command = args[0]
+  replay_file = args[1]
+
+  if not os.path.exists(replay_file):
+    option_parser.error('Replay file "%s" does not exist' % replay_file)
+
+  http_archive = HttpArchive.Load(replay_file)
+  if command == 'ls':
+    print http_archive.ls(options.command, options.host, options.full_path)
+  elif command == 'cat':
+    print http_archive.cat(options.command, options.host, options.full_path)
+  elif command == 'stats':
+    print http_archive.stats(options.command, options.host, options.full_path)
+  elif command == 'merge':
+    if not options.merged_file:
+      print 'Error: Must specify a merged file name (use --merged_file)'
+      return
+    http_archive.merge(options.merged_file, args[2:])
+  elif command == 'edit':
+    http_archive.edit(options.command, options.host, options.full_path)
+    http_archive.Persist(replay_file)
+  else:
+    option_parser.error('Unknown command "%s"' % command)
+  return 0
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/httparchive_test.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/httparchive_test.py
new file mode 100755
index 0000000..12923d4
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/httparchive_test.py
@@ -0,0 +1,484 @@
+#!/usr/bin/env python
+# Copyright 2011 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import calendar
+import email.utils
+import httparchive
+import unittest
+
+
+def create_request(headers):
+  return httparchive.ArchivedHttpRequest(
+      'GET', 'www.test.com', '/', None, headers)
+
+def create_response(headers):
+  return httparchive.ArchivedHttpResponse(
+      11, 200, 'OK', headers, '')
+
+
+class HttpArchiveTest(unittest.TestCase):
+
+  REQUEST_HEADERS = {}
+  REQUEST = create_request(REQUEST_HEADERS)
+
+  # Used for if-(un)modified-since checks
+  DATE_PAST = 'Wed, 13 Jul 2011 03:58:08 GMT'
+  DATE_PRESENT = 'Wed, 20 Jul 2011 04:58:08 GMT'
+  DATE_FUTURE = 'Wed, 27 Jul 2011 05:58:08 GMT'
+  DATE_INVALID = 'This is an invalid date!!'
+
+  # etag values
+  ETAG_VALID = 'etag'
+  ETAG_INVALID = 'This is an invalid etag value!!'
+
+  RESPONSE_HEADERS = [('last-modified', DATE_PRESENT), ('etag', ETAG_VALID)]
+  RESPONSE = create_response(RESPONSE_HEADERS)
+
+  def setUp(self):
+    self.archive = httparchive.HttpArchive()
+    self.archive[self.REQUEST] = self.RESPONSE
+
+    # Also add an identical POST request for testing
+    request = httparchive.ArchivedHttpRequest(
+        'POST', 'www.test.com', '/', None, self.REQUEST_HEADERS)
+    self.archive[request] = self.RESPONSE
+
+  def tearDown(self):
+    pass
+
+  def test_init(self):
+    archive = httparchive.HttpArchive()
+    self.assertEqual(len(archive), 0)
+
+  def test_request__TrimHeaders(self):
+    request = httparchive.ArchivedHttpRequest
+    header1 = {'accept-encoding': 'gzip,deflate'}
+    self.assertEqual(request._TrimHeaders(header1),
+                     [(k, v) for k, v in header1.items()])
+
+    header2 = {'referer': 'www.google.com'}
+    self.assertEqual(request._TrimHeaders(header2), [])
+
+    header3 = {'referer': 'www.google.com', 'cookie': 'cookie_monster!',
+               'hello': 'world'}
+    self.assertEqual(request._TrimHeaders(header3), [('hello', 'world')])
+
+    # Tests that spaces and trailing comma get stripped.
+    header4 = {'accept-encoding': 'gzip, deflate,, '}
+    self.assertEqual(request._TrimHeaders(header4),
+                     [('accept-encoding', 'gzip,deflate')])
+
+    # Tests that 'lzma' gets stripped.
+    header5 = {'accept-encoding': 'gzip, deflate, lzma'}
+    self.assertEqual(request._TrimHeaders(header5),
+                     [('accept-encoding', 'gzip,deflate')])
+
+    # Tests that x-client-data gets stripped.
+    header6 = {'x-client-data': 'testdata'}
+    self.assertEqual(request._TrimHeaders(header6), [])
+
+  def test_matches(self):
+    headers = {}
+    request1 = httparchive.ArchivedHttpRequest(
+        'GET', 'www.test.com', '/index.html?hello=world', None, headers)
+    request2 = httparchive.ArchivedHttpRequest(
+        'GET', 'www.test.com', '/index.html?foo=bar', None, headers)
+
+    self.assert_(not request1.matches(
+        request2.command, request2.host, request2.full_path, use_query=True))
+    self.assert_(request1.matches(
+        request2.command, request2.host, request2.full_path, use_query=False))
+
+    self.assert_(request1.matches(
+        request2.command, request2.host, None, use_query=True))
+    self.assert_(request1.matches(
+        request2.command, None, request2.full_path, use_query=False))
+
+    empty_request = httparchive.ArchivedHttpRequest(
+        None, None, None, None, headers)
+    self.assert_(not empty_request.matches(
+        request2.command, request2.host, None, use_query=True))
+    self.assert_(not empty_request.matches(
+        request2.command, None, request2.full_path, use_query=False))
+
+  def setup_find_closest_request(self):
+    headers = {}
+    request1 = httparchive.ArchivedHttpRequest(
+        'GET', 'www.test.com', '/a?hello=world', None, headers)
+    request2 = httparchive.ArchivedHttpRequest(
+        'GET', 'www.test.com', '/a?foo=bar', None, headers)
+    request3 = httparchive.ArchivedHttpRequest(
+        'GET', 'www.test.com', '/b?hello=world', None, headers)
+    request4 = httparchive.ArchivedHttpRequest(
+        'GET', 'www.test.com', '/c?hello=world', None, headers)
+
+    archive = httparchive.HttpArchive()
+    # Add requests 2 and 3 and find closest match with request1
+    archive[request2] = self.RESPONSE
+    archive[request3] = self.RESPONSE
+
+    return archive, request1, request2, request3, request4
+
+  def test_find_closest_request(self):
+    archive, request1, request2, request3, request4 = (
+      self.setup_find_closest_request())
+
+    # Always favor requests with same paths, even if use_path=False.
+    self.assertEqual(
+        request2, archive.find_closest_request(request1, use_path=False))
+    # If we match strictly on path, request2 is the only match
+    self.assertEqual(
+        request2, archive.find_closest_request(request1, use_path=True))
+    # request4 can be matched with request3, if use_path=False
+    self.assertEqual(
+        request3, archive.find_closest_request(request4, use_path=False))
+    # ...but None, if use_path=True
+    self.assertEqual(
+        None, archive.find_closest_request(request4, use_path=True))
+
+  def test_find_closest_request_delete_simple(self):
+    archive, request1, request2, request3, request4 = (
+      self.setup_find_closest_request())
+
+    del archive[request3]
+    self.assertEqual(
+        request2, archive.find_closest_request(request1, use_path=False))
+    self.assertEqual(
+        request2, archive.find_closest_request(request1, use_path=True))
+
+  def test_find_closest_request_delete_complex(self):
+    archive, request1, request2, request3, request4 = (
+      self.setup_find_closest_request())
+
+    del archive[request2]
+    self.assertEqual(
+        request3, archive.find_closest_request(request1, use_path=False))
+    self.assertEqual(
+        None, archive.find_closest_request(request1, use_path=True))
+
+  def test_find_closest_request_timestamp(self):
+    headers = {}
+    request1 = httparchive.ArchivedHttpRequest(
+        'GET', 'www.test.com', '/index.html?time=100000000&important=true',
+        None, headers)
+    request2 = httparchive.ArchivedHttpRequest(
+        'GET', 'www.test.com', '/index.html?time=99999999&important=true',
+        None, headers)
+    request3 = httparchive.ArchivedHttpRequest(
+        'GET', 'www.test.com', '/index.html?time=10000000&important=false',
+        None, headers)
+    archive = httparchive.HttpArchive()
+    # Add requests 2 and 3 and find closest match with request1
+    archive[request2] = self.RESPONSE
+    archive[request3] = self.RESPONSE
+
+    # Although request3 is lexicographically closer, request2 is semantically
+    # more similar.
+    self.assertEqual(
+        request2, archive.find_closest_request(request1, use_path=True))
+
+  def test_get_cmp_seq(self):
+    # The order of key-value pairs in query and header respectively should not
+    # matter.
+    headers = {'k2': 'v2', 'k1': 'v1'}
+    request = httparchive.ArchivedHttpRequest(
+        'GET', 'www.test.com', '/a?c=d&a=b;e=f', None, headers)
+    self.assertEqual([('a', 'b'), ('c', 'd'), ('e', 'f'),
+                      ('k1', 'v1'), ('k2', 'v2')],
+                     request._GetCmpSeq('c=d&a=b;e=f'))
+
+  def test_get_simple(self):
+    request = self.REQUEST
+    response = self.RESPONSE
+    archive = self.archive
+
+    self.assertEqual(archive.get(request), response)
+
+    false_request_headers = {'foo': 'bar'}
+    false_request = create_request(false_request_headers)
+    self.assertEqual(archive.get(false_request, default=None), None)
+
+  def test_get_modified_headers(self):
+    request = self.REQUEST
+    response = self.RESPONSE
+    archive = self.archive
+    not_modified_response = httparchive.create_response(304)
+
+    # Fail check and return response again
+    request_headers = {'if-modified-since': self.DATE_PAST}
+    request = create_request(request_headers)
+    self.assertEqual(archive.get(request), response)
+
+    # Succeed check and return 304 Not Modified
+    request_headers = {'if-modified-since': self.DATE_FUTURE}
+    request = create_request(request_headers)
+    self.assertEqual(archive.get(request), not_modified_response)
+
+    # Succeed check and return 304 Not Modified
+    request_headers = {'if-modified-since': self.DATE_PRESENT}
+    request = create_request(request_headers)
+    self.assertEqual(archive.get(request), not_modified_response)
+
+    # Invalid date, fail check and return response again
+    request_headers = {'if-modified-since': self.DATE_INVALID}
+    request = create_request(request_headers)
+    self.assertEqual(archive.get(request), response)
+
+    # fail check since the request is not a GET or HEAD request (as per RFC)
+    request_headers = {'if-modified-since': self.DATE_FUTURE}
+    request = httparchive.ArchivedHttpRequest(
+        'POST', 'www.test.com', '/', None, request_headers)
+    self.assertEqual(archive.get(request), response)
+
+  def test_get_unmodified_headers(self):
+    request = self.REQUEST
+    response = self.RESPONSE
+    archive = self.archive
+    not_modified_response = httparchive.create_response(304)
+
+    # Succeed check
+    request_headers = {'if-unmodified-since': self.DATE_PAST}
+    request = create_request(request_headers)
+    self.assertEqual(archive.get(request), not_modified_response)
+
+    # Fail check
+    request_headers = {'if-unmodified-since': self.DATE_FUTURE}
+    request = create_request(request_headers)
+    self.assertEqual(archive.get(request), response)
+
+    # Succeed check
+    request_headers = {'if-unmodified-since': self.DATE_PRESENT}
+    request = create_request(request_headers)
+    self.assertEqual(archive.get(request), not_modified_response)
+
+    # Fail check
+    request_headers = {'if-unmodified-since': self.DATE_INVALID}
+    request = create_request(request_headers)
+    self.assertEqual(archive.get(request), response)
+
+    # Fail check since the request is not a GET or HEAD request (as per RFC)
+    request_headers = {'if-modified-since': self.DATE_PAST}
+    request = httparchive.ArchivedHttpRequest(
+        'POST', 'www.test.com', '/', None, request_headers)
+    self.assertEqual(archive.get(request), response)
+
+  def test_get_etags(self):
+    request = self.REQUEST
+    response = self.RESPONSE
+    archive = self.archive
+    not_modified_response = httparchive.create_response(304)
+    precondition_failed_response = httparchive.create_response(412)
+
+    # if-match headers
+    request_headers = {'if-match': self.ETAG_VALID}
+    request = create_request(request_headers)
+    self.assertEqual(archive.get(request), response)
+
+    request_headers = {'if-match': self.ETAG_INVALID}
+    request = create_request(request_headers)
+    self.assertEqual(archive.get(request), precondition_failed_response)
+
+    # if-none-match headers
+    request_headers = {'if-none-match': self.ETAG_VALID}
+    request = create_request(request_headers)
+    self.assertEqual(archive.get(request), not_modified_response)
+
+    request_headers = {'if-none-match': self.ETAG_INVALID}
+    request = create_request(request_headers)
+    self.assertEqual(archive.get(request), response)
+
+  def test_get_multiple_match_headers(self):
+    request = self.REQUEST
+    response = self.RESPONSE
+    archive = self.archive
+    not_modified_response = httparchive.create_response(304)
+    precondition_failed_response = httparchive.create_response(412)
+
+    # if-match headers
+    # If the request would, without the If-Match header field,
+    # result in anything other than a 2xx or 412 status,
+    # then the If-Match header MUST be ignored.
+
+    request_headers = {
+        'if-match': self.ETAG_VALID,
+        'if-modified-since': self.DATE_PAST,
+    }
+    request = create_request(request_headers)
+    self.assertEqual(archive.get(request), response)
+
+    # Invalid etag, precondition failed
+    request_headers = {
+        'if-match': self.ETAG_INVALID,
+        'if-modified-since': self.DATE_PAST,
+    }
+    request = create_request(request_headers)
+    self.assertEqual(archive.get(request), precondition_failed_response)
+
+    # 304 response; ignore if-match header
+    request_headers = {
+        'if-match': self.ETAG_VALID,
+        'if-modified-since': self.DATE_FUTURE,
+    }
+    request = create_request(request_headers)
+    self.assertEqual(archive.get(request), not_modified_response)
+
+    # 304 response; ignore if-match header
+    request_headers = {
+        'if-match': self.ETAG_INVALID,
+        'if-modified-since': self.DATE_PRESENT,
+    }
+    request = create_request(request_headers)
+    self.assertEqual(archive.get(request), not_modified_response)
+
+    # Invalid etag, precondition failed
+    request_headers = {
+        'if-match': self.ETAG_INVALID,
+        'if-modified-since': self.DATE_INVALID,
+    }
+    request = create_request(request_headers)
+    self.assertEqual(archive.get(request), precondition_failed_response)
+
+  def test_get_multiple_none_match_headers(self):
+    request = self.REQUEST
+    response = self.RESPONSE
+    archive = self.archive
+    not_modified_response = httparchive.create_response(304)
+    precondition_failed_response = httparchive.create_response(412)
+
+    # if-none-match headers
+    # If the request would, without the If-None-Match header field,
+    # result in anything other than a 2xx or 304 status,
+    # then the If-None-Match header MUST be ignored.
+
+    request_headers = {
+        'if-none-match': self.ETAG_VALID,
+        'if-modified-since': self.DATE_PAST,
+    }
+    request = create_request(request_headers)
+    self.assertEqual(archive.get(request), response)
+
+    request_headers = {
+        'if-none-match': self.ETAG_INVALID,
+        'if-modified-since': self.DATE_PAST,
+    }
+    request = create_request(request_headers)
+    self.assertEqual(archive.get(request), response)
+
+    # etag match, precondition failed
+    request_headers = {
+        'if-none-match': self.ETAG_VALID,
+        'if-modified-since': self.DATE_FUTURE,
+    }
+    request = create_request(request_headers)
+    self.assertEqual(archive.get(request), not_modified_response)
+
+    request_headers = {
+        'if-none-match': self.ETAG_INVALID,
+        'if-modified-since': self.DATE_PRESENT,
+    }
+    request = create_request(request_headers)
+    self.assertEqual(archive.get(request), not_modified_response)
+
+    request_headers = {
+        'if-none-match': self.ETAG_INVALID,
+        'if-modified-since': self.DATE_INVALID,
+    }
+    request = create_request(request_headers)
+    self.assertEqual(archive.get(request), response)
+
+  def test_response__TrimHeaders(self):
+    response = httparchive.ArchivedHttpResponse
+    header1 = [('access-control-allow-origin', '*'),
+               ('content-type', 'image/jpeg'),
+               ('content-length', 2878)]
+    self.assertEqual(response._TrimHeaders(header1), header1)
+
+    header2 = [('content-type', 'text/javascript; charset=utf-8'),
+               ('connection', 'keep-alive'),
+               ('cache-control', 'private, must-revalidate, max-age=0'),
+               ('content-encoding', 'gzip')]
+    self.assertEqual(response._TrimHeaders(header2), header2)
+
+    header3 = [('content-security-policy', """\
+default-src 'self' http://*.cnn.com:* https://*.cnn.com:* \
+*.cnn.net:* *.turner.com:* *.ugdturner.com:* *.vgtf.net:*; \
+script-src 'unsafe-inline' 'unsafe-eval' 'self' *; \
+style-src 'unsafe-inline' 'self' *; frame-src 'self' *; \
+object-src 'self' *; img-src 'self' * data:; media-src 'self' *; \
+font-src 'self' *; connect-src 'self' *"""),
+               ('access-control-allow-origin', '*'),
+               ('content-type', 'text/html; charset=utf-8'),
+               ('content-encoding', 'gzip')]
+    self.assertEqual(response._TrimHeaders(header3), [
+        ('access-control-allow-origin', '*'),
+        ('content-type', 'text/html; charset=utf-8'),
+        ('content-encoding', 'gzip')
+    ])
+
+    header4 = [('content-security-policy', """\
+default-src * data: blob:;script-src *.facebook.com *.fbcdn.net \
+*.facebook.net *.google-analytics.com *.virtualearth.net *.google.com \
+127.0.0.1:* *.spotilocal.com:* 'unsafe-inline' 'unsafe-eval' \
+fbstatic-a.akamaihd.net fbcdn-static-b-a.akamaihd.net *.atlassolutions.com \
+blob: chrome-extension://lifbcibllhkdhoafpjfnlhfpfgnpldfl \
+*.liverail.com;style-src * 'unsafe-inline' data:;connect-src *.facebook.com \
+*.fbcdn.net *.facebook.net *.spotilocal.com:* *.akamaihd.net \
+wss://*.facebook.com:* https://fb.scanandcleanlocal.com:* \
+*.atlassolutions.com attachment.fbsbx.com ws://localhost:* \
+blob: 127.0.0.1:* *.liverail.com""")]
+    self.assertEqual(response._TrimHeaders(header4), [])
+
+
+class ArchivedHttpResponse(unittest.TestCase):
+  PAST_DATE_A = 'Tue, 13 Jul 2010 03:47:07 GMT'
+  PAST_DATE_B = 'Tue, 13 Jul 2010 02:47:07 GMT'  # PAST_DATE_A -1 hour
+  PAST_DATE_C = 'Tue, 13 Jul 2010 04:47:07 GMT'  # PAST_DATE_A +1 hour
+  NOW_DATE_A = 'Wed, 20 Jul 2011 04:58:08 GMT'
+  NOW_DATE_B = 'Wed, 20 Jul 2011 03:58:08 GMT'  # NOW_DATE_A -1 hour
+  NOW_DATE_C = 'Wed, 20 Jul 2011 05:58:08 GMT'  # NOW_DATE_A +1 hour
+  NOW_SECONDS = calendar.timegm(email.utils.parsedate(NOW_DATE_A))
+
+  def setUp(self):
+    self.response = create_response([('date', self.PAST_DATE_A)])
+
+  def test_update_date_same_date(self):
+    self.assertEqual(
+        self.response.update_date(self.PAST_DATE_A, now=self.NOW_SECONDS),
+        self.NOW_DATE_A)
+
+  def test_update_date_before_date(self):
+    self.assertEqual(
+        self.response.update_date(self.PAST_DATE_B, now=self.NOW_SECONDS),
+        self.NOW_DATE_B)
+
+  def test_update_date_after_date(self):
+    self.assertEqual(
+        self.response.update_date(self.PAST_DATE_C, now=self.NOW_SECONDS),
+        self.NOW_DATE_C)
+
+  def test_update_date_bad_date_param(self):
+    self.assertEqual(
+        self.response.update_date('garbage date', now=self.NOW_SECONDS),
+        'garbage date')
+
+  def test_update_date_bad_date_header(self):
+    self.response.set_header('date', 'garbage date')
+    self.assertEqual(
+        self.response.update_date(self.PAST_DATE_B, now=self.NOW_SECONDS),
+        self.PAST_DATE_B)
+
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/httpclient.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/httpclient.py
new file mode 100644
index 0000000..5db5e50
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/httpclient.py
@@ -0,0 +1,512 @@
+#!/usr/bin/env python
+# Copyright 2012 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Retrieve web resources over http."""
+
+import copy
+import datetime
+import httplib
+import logging
+import random
+import ssl
+import StringIO
+
+import httparchive
+import platformsettings
+import script_injector
+
+
+# PIL isn't always available, but we still want to be able to run without
+# the image scrambling functionality in this case.
+try:
+  import Image
+except ImportError:
+  Image = None
+
+TIMER = platformsettings.timer
+
+
+class HttpClientException(Exception):
+  """Base class for all exceptions in httpclient."""
+  pass
+
+
+def _InjectScripts(response, injector):
+  """Injects script generated by |injector| immediately after <head> or <html>.
+
+  Copies |response| if it is modified.
+
+  Args:
+    response: an ArchivedHttpResponse
+    injector: function which generates JavaScript string
+      based on recording time (e.g. "Math.random = function(){...}")
+  Returns:
+    an ArchivedHttpResponse
+  """
+  if type(response) == tuple:
+    logging.warn('tuple response: %s', response)
+  content_type = response.get_header('content-type')
+  if content_type and content_type.startswith('text/html'):
+    text_chunks = response.get_data_as_chunks()
+    text_chunks, just_injected = script_injector.InjectScript(
+        text_chunks, 'text/html', injector(response.request_time))
+    if just_injected:
+      response = copy.deepcopy(response)
+      response.set_data_from_chunks(text_chunks)
+  return response
+
+
+def _ScrambleImages(response):
+  """If the |response| is an image, attempt to scramble it.
+
+  Copies |response| if it is modified.
+
+  Args:
+    response: an ArchivedHttpResponse
+  Returns:
+    an ArchivedHttpResponse
+  """
+
+  assert Image, '--scramble_images requires the PIL module to be installed.'
+
+  content_type = response.get_header('content-type')
+  if content_type and content_type.startswith('image/'):
+    try:
+      image_data = response.response_data[0]
+      image_data.decode(encoding='base64')
+      im = Image.open(StringIO.StringIO(image_data))
+
+      pixel_data = list(im.getdata())
+      random.shuffle(pixel_data)
+
+      scrambled_image = im.copy()
+      scrambled_image.putdata(pixel_data)
+
+      output_image_io = StringIO.StringIO()
+      scrambled_image.save(output_image_io, im.format)
+      output_image_data = output_image_io.getvalue()
+      output_image_data.encode(encoding='base64')
+
+      response = copy.deepcopy(response)
+      response.set_data(output_image_data)
+    except Exception:
+      pass
+
+  return response
+
+
+class DetailedHTTPResponse(httplib.HTTPResponse):
+  """Preserve details relevant to replaying responses.
+
+  WARNING: This code uses attributes and methods of HTTPResponse
+  that are not part of the public interface.
+  """
+
+  def read_chunks(self):
+    """Return the response body content and timing data.
+
+    The returned chunks have the chunk size and CRLFs stripped off.
+    If the response was compressed, the returned data is still compressed.
+
+    Returns:
+      (chunks, delays)
+        chunks:
+          [response_body]                  # non-chunked responses
+          [chunk_1, chunk_2, ...]          # chunked responses
+        delays:
+          [0]                              # non-chunked responses
+          [chunk_1_first_byte_delay, ...]  # chunked responses
+
+      The delay for the first body item should be recorded by the caller.
+    """
+    buf = []
+    chunks = []
+    delays = []
+    if not self.chunked:
+      chunks.append(self.read())
+      delays.append(0)
+    else:
+      start = TIMER()
+      try:
+        while True:
+          line = self.fp.readline()
+          chunk_size = self._read_chunk_size(line)
+          if chunk_size is None:
+            raise httplib.IncompleteRead(''.join(chunks))
+          if chunk_size == 0:
+            break
+          delays.append(TIMER() - start)
+          chunks.append(self._safe_read(chunk_size))
+          self._safe_read(2)  # skip the CRLF at the end of the chunk
+          start = TIMER()
+
+        # Ignore any trailers.
+        while True:
+          line = self.fp.readline()
+          if not line or line == '\r\n':
+            break
+      finally:
+        self.close()
+    return chunks, delays
+
+  @classmethod
+  def _read_chunk_size(cls, line):
+    chunk_extensions_pos = line.find(';')
+    if chunk_extensions_pos != -1:
+      line = line[:chunk_extensions_pos]  # strip chunk-extensions
+    try:
+      chunk_size = int(line, 16)
+    except ValueError:
+      return None
+    return chunk_size
+
+
+class DetailedHTTPConnection(httplib.HTTPConnection):
+  """Preserve details relevant to replaying connections."""
+  response_class = DetailedHTTPResponse
+
+
+class DetailedHTTPSResponse(DetailedHTTPResponse):
+  """Preserve details relevant to replaying SSL responses."""
+  pass
+
+
+class DetailedHTTPSConnection(httplib.HTTPSConnection):
+  """Preserve details relevant to replaying SSL connections."""
+  response_class = DetailedHTTPSResponse
+
+  def __init__(self, host, port):
+    # https://www.python.org/dev/peps/pep-0476/#opting-out
+    if hasattr(ssl, '_create_unverified_context'):
+      httplib.HTTPSConnection.__init__(
+          self, host=host, port=port, context=ssl._create_unverified_context())
+    else:
+      httplib.HTTPSConnection.__init__(self, host=host, port=port)
+
+
+class RealHttpFetch(object):
+
+  def __init__(self, real_dns_lookup):
+    """Initialize RealHttpFetch.
+
+    Args:
+      real_dns_lookup: a function that resolves a host to an IP. RealHttpFetch
+        will resolve host name to the IP before making fetching request if this
+        is not None.
+    """
+    self._real_dns_lookup = real_dns_lookup
+
+  @staticmethod
+  def _GetHeaderNameValue(header):
+    """Parse the header line and return a name/value tuple.
+
+    Args:
+      header: a string for a header such as "Content-Length: 314".
+    Returns:
+      A tuple (header_name, header_value) on success or None if the header
+      is not in expected format. header_name is in lowercase.
+    """
+    i = header.find(':')
+    if i > 0:
+      return (header[:i].lower(), header[i+1:].strip())
+    return None
+
+  @staticmethod
+  def _ToTuples(headers):
+    """Parse headers and save them to a list of tuples.
+
+    This method takes HttpResponse.msg.headers as input and convert it
+    to a list of (header_name, header_value) tuples.
+    HttpResponse.msg.headers is a list of strings where each string
+    represents either a header or a continuation line of a header.
+    1. a normal header consists of two parts which are separated by colon :
+       "header_name:header_value..."
+    2. a continuation line is a string starting with whitespace
+       "[whitespace]continued_header_value..."
+    If a header is not in good shape or an unexpected continuation line is
+    seen, it will be ignored.
+
+    Should avoid using response.getheaders() directly
+    because response.getheaders() can't handle multiple headers
+    with the same name properly. Instead, parse the
+    response.msg.headers using this method to get all headers.
+
+    Args:
+      headers: an instance of HttpResponse.msg.headers.
+    Returns:
+      A list of tuples which looks like:
+      [(header_name, header_value), (header_name2, header_value2)...]
+    """
+    all_headers = []
+    for line in headers:
+      if line[0] in '\t ':
+        if not all_headers:
+          logging.warning(
+              'Unexpected response header continuation line [%s]', line)
+          continue
+        name, value = all_headers.pop()
+        value += '\n ' + line.strip()
+      else:
+        name_value = RealHttpFetch._GetHeaderNameValue(line)
+        if not name_value:
+          logging.warning(
+              'Response header in wrong format [%s]', line)
+          continue
+        name, value = name_value  # pylint: disable=unpacking-non-sequence
+      all_headers.append((name, value))
+    return all_headers
+
+  @staticmethod
+  def _get_request_host_port(request):
+    host_parts = request.host.split(':')
+    host = host_parts[0]
+    port = int(host_parts[1]) if len(host_parts) == 2 else None
+    return host, port
+
+  @staticmethod
+  def _get_system_proxy(is_ssl):
+    return platformsettings.get_system_proxy(is_ssl)
+
+  def _get_connection(self, request_host, request_port, is_ssl):
+    """Return a detailed connection object for host/port pair.
+
+    If a system proxy is defined (see platformsettings.py), it will be used.
+
+    Args:
+      request_host: a host string (e.g. "www.example.com").
+      request_port: a port integer (e.g. 8080) or None (for the default port).
+      is_ssl: True if HTTPS connection is needed.
+    Returns:
+      A DetailedHTTPSConnection or DetailedHTTPConnection instance.
+    """
+    connection_host = request_host
+    connection_port = request_port
+    system_proxy = self._get_system_proxy(is_ssl)
+    if system_proxy:
+      connection_host = system_proxy.host
+      connection_port = system_proxy.port
+
+    # Use an IP address because WPR may override DNS settings.
+    if self._real_dns_lookup:
+      connection_ip = self._real_dns_lookup(connection_host)
+      if not connection_ip:
+        logging.critical(
+            'Unable to find IP for host name: %s', connection_host)
+        return None
+      connection_host = connection_ip
+
+    if is_ssl:
+      connection = DetailedHTTPSConnection(connection_host, connection_port)
+      if system_proxy:
+        connection.set_tunnel(request_host, request_port)
+    else:
+      connection = DetailedHTTPConnection(connection_host, connection_port)
+    return connection
+
+  def __call__(self, request):
+    """Fetch an HTTP request.
+
+    Args:
+      request: an ArchivedHttpRequest
+    Returns:
+      an ArchivedHttpResponse
+    """
+    logging.debug('RealHttpFetch: %s %s', request.host, request.full_path)
+    request_host, request_port = self._get_request_host_port(request)
+    retries = 3
+    while True:
+      try:
+        request_time = datetime.datetime.utcnow()
+        connection = self._get_connection(
+            request_host, request_port, request.is_ssl)
+        connect_start = TIMER()
+        connection.connect()
+        connect_delay = int((TIMER() - connect_start) * 1000)
+        start = TIMER()
+        connection.request(
+            request.command,
+            request.full_path,
+            request.request_body,
+            request.headers)
+        response = connection.getresponse()
+        headers_delay = int((TIMER() - start) * 1000)
+
+        chunks, chunk_delays = response.read_chunks()
+        delays = {
+            'connect': connect_delay,
+            'headers': headers_delay,
+            'data': chunk_delays
+            }
+        archived_http_response = httparchive.ArchivedHttpResponse(
+            response.version,
+            response.status,
+            response.reason,
+            RealHttpFetch._ToTuples(response.msg.headers),
+            chunks,
+            delays,
+            request_time)
+        return archived_http_response
+      except Exception, e:
+        if retries:
+          retries -= 1
+          logging.warning('Retrying fetch %s: %s', request, repr(e))
+          continue
+        logging.critical('Could not fetch %s: %s', request, repr(e))
+        return None
+
+
+class RecordHttpArchiveFetch(object):
+  """Make real HTTP fetches and save responses in the given HttpArchive."""
+
+  def __init__(self, http_archive, injector):
+    """Initialize RecordHttpArchiveFetch.
+
+    Args:
+      http_archive: an instance of a HttpArchive
+      injector: script injector to inject scripts in all pages
+    """
+    self.http_archive = http_archive
+    # Do not resolve host name to IP when recording to avoid SSL3 handshake
+    # failure.
+    # See https://github.com/chromium/web-page-replay/issues/73 for details.
+    self.real_http_fetch = RealHttpFetch(real_dns_lookup=None)
+    self.injector = injector
+
+  def __call__(self, request):
+    """Fetch the request and return the response.
+
+    Args:
+      request: an ArchivedHttpRequest.
+    Returns:
+      an ArchivedHttpResponse
+    """
+    # If request is already in the archive, return the archived response.
+    if request in self.http_archive:
+      logging.debug('Repeated request found: %s', request)
+      response = self.http_archive[request]
+    else:
+      response = self.real_http_fetch(request)
+      if response is None:
+        return None
+      self.http_archive[request] = response
+    if self.injector:
+      response = _InjectScripts(response, self.injector)
+    logging.debug('Recorded: %s', request)
+    return response
+
+
+class ReplayHttpArchiveFetch(object):
+  """Serve responses from the given HttpArchive."""
+
+  def __init__(self, http_archive, real_dns_lookup, injector,
+               use_diff_on_unknown_requests=False,
+               use_closest_match=False, scramble_images=False):
+    """Initialize ReplayHttpArchiveFetch.
+
+    Args:
+      http_archive: an instance of a HttpArchive
+      real_dns_lookup: a function that resolves a host to an IP.
+      injector: script injector to inject scripts in all pages
+      use_diff_on_unknown_requests: If True, log unknown requests
+        with a diff to requests that look similar.
+      use_closest_match: If True, on replay mode, serve the closest match
+        in the archive instead of giving a 404.
+    """
+    self.http_archive = http_archive
+    self.injector = injector
+    self.use_diff_on_unknown_requests = use_diff_on_unknown_requests
+    self.use_closest_match = use_closest_match
+    self.scramble_images = scramble_images
+    self.real_http_fetch = RealHttpFetch(real_dns_lookup)
+
+  def __call__(self, request):
+    """Fetch the request and return the response.
+
+    Args:
+      request: an instance of an ArchivedHttpRequest.
+    Returns:
+      Instance of ArchivedHttpResponse (if found) or None
+    """
+    if request.host.startswith('127.0.0.1:'):
+      return self.real_http_fetch(request)
+
+    response = self.http_archive.get(request)
+
+    if self.use_closest_match and not response:
+      closest_request = self.http_archive.find_closest_request(
+          request, use_path=True)
+      if closest_request:
+        response = self.http_archive.get(closest_request)
+        if response:
+          logging.info('Request not found: %s\nUsing closest match: %s',
+                       request, closest_request)
+
+    if not response:
+      reason = str(request)
+      if self.use_diff_on_unknown_requests:
+        diff = self.http_archive.diff(request)
+        if diff:
+          reason += (
+              "\nNearest request diff "
+              "('-' for archived request, '+' for current request):\n%s" % diff)
+      logging.warning('Could not replay: %s', reason)
+    else:
+      if self.injector:
+        response = _InjectScripts(response, self.injector)
+      if self.scramble_images:
+        response = _ScrambleImages(response)
+    return response
+
+
+class ControllableHttpArchiveFetch(object):
+  """Controllable fetch function that can swap between record and replay."""
+
+  def __init__(self, http_archive, real_dns_lookup,
+               injector, use_diff_on_unknown_requests,
+               use_record_mode, use_closest_match, scramble_images):
+    """Initialize HttpArchiveFetch.
+
+    Args:
+      http_archive: an instance of a HttpArchive
+      real_dns_lookup: a function that resolves a host to an IP.
+      injector: function to inject scripts in all pages.
+        takes recording time as datetime.datetime object.
+      use_diff_on_unknown_requests: If True, log unknown requests
+        with a diff to requests that look similar.
+      use_record_mode: If True, start in server in record mode.
+      use_closest_match: If True, on replay mode, serve the closest match
+        in the archive instead of giving a 404.
+    """
+    self.http_archive = http_archive
+    self.record_fetch = RecordHttpArchiveFetch(http_archive, injector)
+    self.replay_fetch = ReplayHttpArchiveFetch(
+        http_archive, real_dns_lookup, injector,
+        use_diff_on_unknown_requests, use_closest_match, scramble_images)
+    if use_record_mode:
+      self.SetRecordMode()
+    else:
+      self.SetReplayMode()
+
+  def SetRecordMode(self):
+    self.fetch = self.record_fetch
+    self.is_record_mode = True
+
+  def SetReplayMode(self):
+    self.fetch = self.replay_fetch
+    self.is_record_mode = False
+
+  def __call__(self, *args, **kwargs):
+    """Forward calls to Replay/Record fetch functions depending on mode."""
+    return self.fetch(*args, **kwargs)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/httpclient_test.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/httpclient_test.py
new file mode 100644
index 0000000..709ce47
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/httpclient_test.py
@@ -0,0 +1,284 @@
+#!/usr/bin/env python
+# Copyright 2012 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import mock
+import unittest
+
+import datetime
+import dnsproxy
+import httparchive
+import httpclient
+import platformsettings
+import script_injector
+import test_utils
+
+
+class RealHttpFetchTest(unittest.TestCase):
+
+  # Initialize test data
+  CONTENT_TYPE = 'content-type: image/x-icon'
+  COOKIE_1 = ('Set-Cookie: GMAIL_IMP=EXPIRED; '
+              'Expires=Thu, 12-Jul-2012 22:41:22 GMT; '
+              'Path=/mail; Secure')
+  COOKIE_2 = ('Set-Cookie: GMAIL_STAT_205a=EXPIRED; '
+              'Expires=Thu, 12-Jul-2012 22:42:24 GMT; '
+              'Path=/mail; Secure')
+  FIRST_LINE = 'fake-header: first line'
+  SECOND_LINE = ' second line'
+  THIRD_LINE = '\tthird line'
+  BAD_HEADER = 'this is a bad header'
+
+  def test__GetHeaderNameValueBasic(self):
+    """Test _GetHeaderNameValue with normal header."""
+
+    real_http_fetch = httpclient.RealHttpFetch
+    name_value = real_http_fetch._GetHeaderNameValue(self.CONTENT_TYPE)
+    self.assertEqual(name_value, ('content-type', 'image/x-icon'))
+
+  def test__GetHeaderNameValueLowercasesName(self):
+    """_GetHeaderNameValue lowercases header name."""
+
+    real_http_fetch = httpclient.RealHttpFetch
+    header = 'X-Google-Gfe-Backend-Request-Info: eid=1KMAUMeiK4eMiAL52YyMBg'
+    expected = ('x-google-gfe-backend-request-info',
+                'eid=1KMAUMeiK4eMiAL52YyMBg')
+    name_value = real_http_fetch._GetHeaderNameValue(header)
+    self.assertEqual(name_value, expected)
+
+  def test__GetHeaderNameValueBadLineGivesNone(self):
+    """_GetHeaderNameValue returns None for a header in wrong format."""
+
+    real_http_fetch = httpclient.RealHttpFetch
+    name_value = real_http_fetch._GetHeaderNameValue(self.BAD_HEADER)
+    self.assertIsNone(name_value)
+
+  def test__ToTuplesBasic(self):
+    """Test _ToTuples with normal input."""
+
+    real_http_fetch = httpclient.RealHttpFetch
+    headers = [self.CONTENT_TYPE, self.COOKIE_1, self.FIRST_LINE]
+    result = real_http_fetch._ToTuples(headers)
+    expected = [('content-type', 'image/x-icon'),
+                ('set-cookie', self.COOKIE_1[12:]),
+                ('fake-header', 'first line')]
+    self.assertEqual(result, expected)
+
+  def test__ToTuplesMultipleHeadersWithSameName(self):
+    """Test mulitple headers with the same name."""
+
+    real_http_fetch = httpclient.RealHttpFetch
+    headers = [self.CONTENT_TYPE, self.COOKIE_1, self.COOKIE_2, self.FIRST_LINE]
+    result = real_http_fetch._ToTuples(headers)
+    expected = [('content-type', 'image/x-icon'),
+                ('set-cookie', self.COOKIE_1[12:]),
+                ('set-cookie', self.COOKIE_2[12:]),
+                ('fake-header', 'first line')]
+    self.assertEqual(result, expected)
+
+  def test__ToTuplesAppendsContinuationLine(self):
+    """Test continuation line is handled."""
+
+    real_http_fetch = httpclient.RealHttpFetch
+    headers = [self.CONTENT_TYPE, self.COOKIE_1, self.FIRST_LINE,
+               self.SECOND_LINE, self.THIRD_LINE]
+    result = real_http_fetch._ToTuples(headers)
+    expected = [('content-type', 'image/x-icon'),
+                ('set-cookie', self.COOKIE_1[12:]),
+                ('fake-header', 'first line\n second line\n third line')]
+    self.assertEqual(result, expected)
+
+  def test__ToTuplesIgnoresBadHeader(self):
+    """Test bad header is ignored."""
+
+    real_http_fetch = httpclient.RealHttpFetch
+    bad_headers = [self.CONTENT_TYPE, self.BAD_HEADER, self.COOKIE_1]
+    expected = [('content-type', 'image/x-icon'),
+                ('set-cookie', self.COOKIE_1[12:])]
+    result = real_http_fetch._ToTuples(bad_headers)
+    self.assertEqual(result, expected)
+
+  def test__ToTuplesIgnoresMisplacedContinuationLine(self):
+    """Test misplaced continuation line is ignored."""
+
+    real_http_fetch = httpclient.RealHttpFetch
+    misplaced_headers = [self.THIRD_LINE, self.CONTENT_TYPE,
+                         self.COOKIE_1, self.FIRST_LINE, self.SECOND_LINE]
+    result = real_http_fetch._ToTuples(misplaced_headers)
+    expected = [('content-type', 'image/x-icon'),
+                ('set-cookie', self.COOKIE_1[12:]),
+                ('fake-header', 'first line\n second line')]
+    self.assertEqual(result, expected)
+
+
+class RealHttpFetchGetConnectionTest(unittest.TestCase):
+  """Test that a connection is made with request IP/port or proxy IP/port."""
+
+  def setUp(self):
+    def real_dns_lookup(host):
+      return {
+          'example.com': '127.127.127.127',
+          'proxy.com': '2.2.2.2',
+          }[host]
+    self.fetch = httpclient.RealHttpFetch(real_dns_lookup)
+    self.https_proxy = None
+    self.http_proxy = None
+    def get_proxy(is_ssl):
+      return self.https_proxy if is_ssl else self.http_proxy
+    self.fetch._get_system_proxy = get_proxy
+
+  def set_http_proxy(self, host, port):
+    self.http_proxy = platformsettings.SystemProxy(host, port)
+
+  def set_https_proxy(self, host, port):
+    self.https_proxy = platformsettings.SystemProxy(host, port)
+
+  def test_get_connection_without_proxy_connects_to_host_ip(self):
+    """HTTP connection with no proxy connects to host IP."""
+    self.set_http_proxy(host=None, port=None)
+    connection = self.fetch._get_connection('example.com', None, is_ssl=False)
+    self.assertEqual('127.127.127.127', connection.host)
+    self.assertEqual(80, connection.port)  # default HTTP port
+
+  def test_get_connection_without_proxy_uses_nondefault_request_port(self):
+    """HTTP connection with no proxy connects with request port."""
+    self.set_https_proxy(host=None, port=None)
+    connection = self.fetch._get_connection('example.com', 8888, is_ssl=False)
+    self.assertEqual('127.127.127.127', connection.host)
+    self.assertEqual(8888, connection.port)  # request HTTP port
+
+  def test_get_connection_with_proxy_uses_proxy_port(self):
+    """HTTP connection with proxy connects used proxy port."""
+    self.set_http_proxy(host='proxy.com', port=None)
+    connection = self.fetch._get_connection('example.com', 8888, is_ssl=False)
+    self.assertEqual('2.2.2.2', connection.host)  # proxy IP
+    self.assertEqual(80, connection.port)  # proxy port (default HTTP)
+
+  def test_ssl_get_connection_without_proxy_connects_to_host_ip(self):
+    """HTTPS (SSL) connection with no proxy connects to host IP."""
+    self.set_https_proxy(host=None, port=None)
+    connection = self.fetch._get_connection('example.com', None, is_ssl=True)
+    self.assertEqual('127.127.127.127', connection.host)
+    self.assertEqual(443, connection.port)  # default SSL port
+
+  def test_ssl_get_connection_with_proxy_connects_to_proxy_ip(self):
+    """HTTPS (SSL) connection with proxy connects to proxy IP."""
+    self.set_https_proxy(host='proxy.com', port=8443)
+    connection = self.fetch._get_connection('example.com', None, is_ssl=True)
+    self.assertEqual('2.2.2.2', connection.host)  # proxy IP
+    self.assertEqual(8443, connection.port)  # SSL proxy port
+
+  def test_ssl_get_connection_with_proxy_tunnels_to_host(self):
+    """HTTPS (SSL) connection with proxy tunnels to target host."""
+    self.set_https_proxy(host='proxy.com', port=8443)
+    connection = self.fetch._get_connection('example.com', 9443, is_ssl=True)
+    self.assertEqual('example.com', connection._tunnel_host)  # host name
+    self.assertEqual(9443, connection._tunnel_port)  # host port
+
+
+class ActualNetworkFetchTest(test_utils.RealNetworkFetchTest):
+
+  def testFetchNonSSLRequest(self):
+    real_dns_lookup = dnsproxy.RealDnsLookup(
+        name_servers=[platformsettings.get_original_primary_nameserver()],
+        dns_forwarding=False, proxy_host='127.0.0.1', proxy_port=5353)
+    fetch = httpclient.RealHttpFetch(real_dns_lookup)
+    request = httparchive.ArchivedHttpRequest(
+        command='GET', host='google.com', full_path='/search?q=dogs',
+        request_body=None, headers={}, is_ssl=False)
+    response = fetch(request)
+    self.assertIsNotNone(response)
+
+  def testFetchSSLRequest(self):
+    real_dns_lookup = dnsproxy.RealDnsLookup(
+        name_servers=[platformsettings.get_original_primary_nameserver()],
+        dns_forwarding=False, proxy_host='127.0.0.1', proxy_port=5353)
+    fetch = httpclient.RealHttpFetch(real_dns_lookup)
+    request = httparchive.ArchivedHttpRequest(
+        command='GET', host='google.com', full_path='/search?q=dogs',
+        request_body=None, headers={}, is_ssl=True)
+    response = fetch(request)
+    self.assertIsNotNone(response)
+
+
+class HttpArchiveFetchTest(unittest.TestCase):
+
+  TEST_REQUEST_TIME = datetime.datetime(2016, 11, 17, 1, 2, 3, 456)
+
+  def createTestResponse(self):
+    return httparchive.ArchivedHttpResponse(
+        11, 200, 'OK', [('content-type', 'text/html')],
+        ['<body>test</body>'],
+        request_time=HttpArchiveFetchTest.TEST_REQUEST_TIME)
+
+  def checkTestResponse(self, actual_response, archive, request):
+    self.assertEqual(actual_response, archive[request])
+    self.assertEqual(['<body>test</body>'], actual_response.response_data)
+    self.assertEqual(HttpArchiveFetchTest.TEST_REQUEST_TIME,
+                     actual_response.request_time)
+
+  @staticmethod
+  def dummy_injector(_):
+    return '<body>test</body>'
+
+
+class RecordHttpArchiveFetchTest(HttpArchiveFetchTest):
+
+  @mock.patch('httpclient.RealHttpFetch')
+  def testFetch(self, real_http_fetch):
+    http_fetch_instance = real_http_fetch.return_value
+    response = self.createTestResponse()
+    http_fetch_instance.return_value = response
+    archive = httparchive.HttpArchive()
+    fetch = httpclient.RecordHttpArchiveFetch(archive, self.dummy_injector)
+    request = httparchive.ArchivedHttpRequest(
+        'GET', 'www.test.com', '/', None, {})
+    self.checkTestResponse(fetch(request), archive, request)
+
+
+class ReplayHttpArchiveFetchTest(HttpArchiveFetchTest):
+
+  def testFetch(self):
+    request = httparchive.ArchivedHttpRequest(
+        'GET', 'www.test.com', '/', None, {})
+    response = self.createTestResponse()
+    archive = httparchive.HttpArchive()
+    archive[request] = response
+    fetch = httpclient.ReplayHttpArchiveFetch(
+        archive, None, self.dummy_injector)
+    self.checkTestResponse(fetch(request), archive, request)
+
+  @mock.patch('script_injector.util.resource_string')
+  @mock.patch('script_injector.util.resource_exists')
+  @mock.patch('script_injector.os.path.exists')
+  def testInjectedDate(self, os_path, util_exists, util_resource_string):
+    os_path.return_value = False
+    util_exists.return_value = True
+    util_resource_string.return_value = \
+        ["""var time_seed={}""".format(script_injector.TIME_SEED_MARKER)]
+    request = httparchive.ArchivedHttpRequest(
+        'GET', 'www.test.com', '/', None, {})
+    response = self.createTestResponse()
+    archive = httparchive.HttpArchive()
+    archive[request] = response
+
+    fetch = httpclient.ReplayHttpArchiveFetch(
+        archive, None, script_injector.GetScriptInjector("time_script.js"))
+    self.assertEqual(
+        ['<script>var time_seed=1479344523000</script><body>test</body>'],
+        fetch(request).response_data)
+
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/httpproxy.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/httpproxy.py
new file mode 100644
index 0000000..9a69aa9
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/httpproxy.py
@@ -0,0 +1,446 @@
+#!/usr/bin/env python
+# Copyright 2010 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import BaseHTTPServer
+import certutils
+import collections
+import errno
+import logging
+import socket
+import SocketServer
+import ssl
+import sys
+import time
+import urlparse
+
+import daemonserver
+import httparchive
+import platformsettings
+import proxyshaper
+import sslproxy
+
+def _HandleSSLCertificateError():
+  """
+  This method is intended to be called from
+  BaseHTTPServer.HTTPServer.handle_error().
+  """
+  exc_type, exc_value, exc_traceback = sys.exc_info()
+  if isinstance(exc_value, ssl.SSLError):
+    return
+
+  raise
+
+
+class HttpProxyError(Exception):
+  """Module catch-all error."""
+  pass
+
+
+class HttpProxyServerError(HttpProxyError):
+  """Raised for errors like 'Address already in use'."""
+  pass
+
+
+class HttpArchiveHandler(BaseHTTPServer.BaseHTTPRequestHandler):
+  protocol_version = 'HTTP/1.1'  # override BaseHTTPServer setting
+
+  # Since we do lots of small wfile.write() calls, turn on buffering.
+  wbufsize = -1  # override StreamRequestHandler (a base class) setting
+
+  def setup(self):
+    """Override StreamRequestHandler method."""
+    BaseHTTPServer.BaseHTTPRequestHandler.setup(self)
+    if self.server.traffic_shaping_up_bps:
+      self.rfile = proxyshaper.RateLimitedFile(
+          self.server.get_active_request_count, self.rfile,
+          self.server.traffic_shaping_up_bps)
+    if self.server.traffic_shaping_down_bps:
+      self.wfile = proxyshaper.RateLimitedFile(
+          self.server.get_active_request_count, self.wfile,
+          self.server.traffic_shaping_down_bps)
+
+  # Make request handler logging match our logging format.
+  def log_request(self, code='-', size='-'):
+    pass
+
+  def log_error(self, format, *args):  # pylint:disable=redefined-builtin
+    logging.error(format, *args)
+
+  def log_message(self, format, *args):  # pylint:disable=redefined-builtin
+    logging.info(format, *args)
+
+  def read_request_body(self):
+    request_body = None
+    length = int(self.headers.get('content-length', 0)) or None
+    if length:
+      request_body = self.rfile.read(length)
+    return request_body
+
+  def get_header_dict(self):
+    return dict(self.headers.items())
+
+  def get_archived_http_request(self):
+    host = self.headers.get('host')
+    if host is None:
+      logging.error('Request without host header')
+      return None
+
+    parsed = urlparse.urlparse(self.path)
+    params = ';%s' % parsed.params if parsed.params else ''
+    query = '?%s' % parsed.query if parsed.query else ''
+    fragment = '#%s' % parsed.fragment if parsed.fragment else ''
+    full_path = '%s%s%s%s' % (parsed.path, params, query, fragment)
+
+    StubRequest = collections.namedtuple('StubRequest', ('host', 'full_path'))
+    request, response = StubRequest(host, full_path), None
+
+    self.server.log_url(request, response)
+
+    return httparchive.ArchivedHttpRequest(
+        self.command,
+        host,
+        full_path,
+        self.read_request_body(),
+        self.get_header_dict(),
+        self.server.is_ssl)
+
+  def send_archived_http_response(self, response):
+    try:
+      # We need to set the server name before we start the response.
+      is_chunked = response.is_chunked()
+      has_content_length = response.get_header('content-length') is not None
+      self.server_version = response.get_header('server', 'WebPageReplay')
+      self.sys_version = ''
+
+      if response.version == 10:
+        self.protocol_version = 'HTTP/1.0'
+
+      # If we don't have chunked encoding and there is no content length,
+      # we need to manually compute the content-length.
+      if not is_chunked and not has_content_length:
+        content_length = sum(len(c) for c in response.response_data)
+        response.headers.append(('content-length', str(content_length)))
+
+      is_replay = not self.server.http_archive_fetch.is_record_mode
+      if is_replay and self.server.traffic_shaping_delay_ms:
+        logging.debug('Using round trip delay: %sms',
+                      self.server.traffic_shaping_delay_ms)
+        time.sleep(self.server.traffic_shaping_delay_ms / 1000.0)
+      if is_replay and self.server.use_delays:
+        logging.debug('Using delays (ms): %s', response.delays)
+        time.sleep(response.delays['headers'] / 1000.0)
+        delays = response.delays['data']
+      else:
+        delays = [0] * len(response.response_data)
+      self.send_response(response.status, response.reason)
+      # TODO(mbelshe): This is lame - each write is a packet!
+      for header, value in response.headers:
+        if header in ('last-modified', 'expires'):
+          self.send_header(header, response.update_date(value))
+        elif header not in ('date', 'server'):
+          self.send_header(header, value)
+      self.end_headers()
+
+      for chunk, delay in zip(response.response_data, delays):
+        if delay:
+          self.wfile.flush()
+          time.sleep(delay / 1000.0)
+        if is_chunked:
+          # Write chunk length (hex) and data (e.g. "A\r\nTESSELATED\r\n").
+          self.wfile.write('%x\r\n%s\r\n' % (len(chunk), chunk))
+        else:
+          self.wfile.write(chunk)
+      if is_chunked:
+        self.wfile.write('0\r\n\r\n')  # write final, zero-length chunk.
+      self.wfile.flush()
+
+      # TODO(mbelshe): This connection close doesn't seem to work.
+      if response.version == 10:
+        self.close_connection = 1
+
+    except Exception, e:
+      logging.error('Error sending response for %s%s: %s',
+                    self.headers['host'], self.path, e)
+
+  def handle_one_request(self):
+    """Handle a single HTTP request.
+
+    This method overrides a method from BaseHTTPRequestHandler. When this
+    method returns, it must leave self.close_connection in the correct state.
+    If this method raises an exception, the state of self.close_connection
+    doesn't matter.
+    """
+    try:
+      self.raw_requestline = self.rfile.readline(65537)
+      self.do_parse_and_handle_one_request()
+    except socket.timeout, e:
+      # A read or a write timed out.  Discard this connection
+      self.log_error('Request timed out: %r', e)
+      self.close_connection = 1
+      return
+    except ssl.SSLError:
+      # There is insufficient information passed up the stack from OpenSSL to
+      # determine the true cause of the SSL error. This almost always happens
+      # because the client refuses to accept the self-signed certs of
+      # WebPageReplay.
+      self.close_connection = 1
+      return
+    except socket.error, e:
+      # Connection reset errors happen all the time due to the browser closing
+      # without terminating the connection properly.  They can be safely
+      # ignored.
+      if e[0] == errno.ECONNRESET:
+        self.close_connection = 1
+        return
+      raise
+
+
+  def do_parse_and_handle_one_request(self):
+    start_time = time.time()
+    self.server.num_active_requests += 1
+    request = None
+    try:
+      if len(self.raw_requestline) > 65536:
+        self.requestline = ''
+        self.request_version = ''
+        self.command = ''
+        self.send_error(414)
+        self.close_connection = 0
+        return
+      if not self.raw_requestline:
+        # This indicates that the socket has been closed by the client.
+        self.close_connection = 1
+        return
+
+      # self.parse_request() sets self.close_connection. There is no need to
+      # set the property after the method is executed, unless custom behavior
+      # is desired.
+      if not self.parse_request():
+        # An error code has been sent, just exit.
+        return
+
+      try:
+        response = None
+        request = self.get_archived_http_request()
+
+        if request is None:
+          self.send_error(500)
+          return
+        response = self.server.custom_handlers.handle(request)
+        if not response:
+          response = self.server.http_archive_fetch(request)
+          if (response and response.status == 200 and
+              self.server.allow_generate_304 and
+              request.command in set(['GET', 'HEAD']) and
+              (request.headers.get('if-modified-since', None) or
+               request.headers.get('if-none-match', None))):
+            # The WPR archive never get modified since it is not being recorded.
+            response = httparchive.create_response(
+                status=304, headers=response.headers)
+        if response:
+          self.send_archived_http_response(response)
+        else:
+          self.send_error(404)
+      finally:
+        self.wfile.flush()  # Actually send the response if not already done.
+    finally:
+      request_time_ms = (time.time() - start_time) * 1000.0
+      self.server.total_request_time += request_time_ms
+      if request:
+        if response:
+          logging.debug('Served: %s (%dms)', request, request_time_ms)
+        else:
+          logging.warning('Failed to find response for: %s (%dms)',
+                          request, request_time_ms)
+      self.server.num_active_requests -= 1
+
+  def send_error(self, status, body=None):
+    """Override the default send error with a version that doesn't unnecessarily
+    close the connection.
+    """
+    response = httparchive.create_response(status, body=body)
+    self.send_archived_http_response(response)
+
+
+class HttpProxyServer(SocketServer.ThreadingMixIn,
+                      BaseHTTPServer.HTTPServer,
+                      daemonserver.DaemonServer):
+  HANDLER = HttpArchiveHandler
+
+  # Increase the request queue size. The default value, 5, is set in
+  # SocketServer.TCPServer (the parent of BaseHTTPServer.HTTPServer).
+  # Since we're intercepting many domains through this single server,
+  # it is quite possible to get more than 5 concurrent requests.
+  request_queue_size = 256
+
+  # The number of simultaneous connections that the HTTP server supports. This
+  # is primarily limited by system limits such as RLIMIT_NOFILE.
+  connection_limit = 500
+
+  # Allow sockets to be reused. See
+  # http://svn.python.org/projects/python/trunk/Lib/SocketServer.py for more
+  # details.
+  allow_reuse_address = True
+
+  # Don't prevent python from exiting when there is thread activity.
+  daemon_threads = True
+
+  def __init__(self, http_archive_fetch, custom_handlers, rules,
+               host='localhost', port=80, use_delays=False, is_ssl=False,
+               protocol='HTTP', allow_generate_304=False,
+               down_bandwidth='0', up_bandwidth='0', delay_ms='0'):
+    """Start HTTP server.
+
+    Args:
+      rules: a rule_parser Rules.
+      host: a host string (name or IP) for the web proxy.
+      port: a port string (e.g. '80') for the web proxy.
+      use_delays: if True, add response data delays during replay.
+      is_ssl: True iff proxy is using SSL.
+      up_bandwidth: Upload bandwidth
+      down_bandwidth: Download bandwidth
+           Bandwidths measured in [K|M]{bit/s|Byte/s}. '0' means unlimited.
+      delay_ms: Propagation delay in milliseconds. '0' means no delay.
+    """
+    if platformsettings.SupportsFdLimitControl():
+      # BaseHTTPServer opens a new thread and two fds for each connection.
+      # Check that the process can open at least 1000 fds.
+      soft_limit, hard_limit = platformsettings.GetFdLimit()
+      # Add some wiggle room since there are probably fds not associated with
+      # connections.
+      wiggle_room = 100
+      desired_limit = 2 * HttpProxyServer.connection_limit + wiggle_room
+      if soft_limit < desired_limit:
+        assert desired_limit <= hard_limit, (
+            'The hard limit for number of open files per process is %s which '
+            'is lower than the desired limit of %s.' %
+            (hard_limit, desired_limit))
+        platformsettings.AdjustFdLimit(desired_limit, hard_limit)
+
+    try:
+      BaseHTTPServer.HTTPServer.__init__(self, (host, port), self.HANDLER)
+    except Exception, e:
+      raise HttpProxyServerError('Could not start HTTPServer on port %d: %s' %
+                                 (port, e))
+    self.http_archive_fetch = http_archive_fetch
+    self.custom_handlers = custom_handlers
+    self.use_delays = use_delays
+    self.is_ssl = is_ssl
+    self.traffic_shaping_down_bps = proxyshaper.GetBitsPerSecond(down_bandwidth)
+    self.traffic_shaping_up_bps = proxyshaper.GetBitsPerSecond(up_bandwidth)
+    self.traffic_shaping_delay_ms = int(delay_ms)
+    self.num_active_requests = 0
+    self.num_active_connections = 0
+    self.total_request_time = 0
+    self.protocol = protocol
+    self.allow_generate_304 = allow_generate_304
+    self.log_url = rules.Find('log_url')
+
+    # Note: This message may be scraped. Do not change it.
+    logging.warning(
+        '%s server started on %s:%d' % (self.protocol, self.server_address[0],
+                                        self.server_address[1]))
+
+  def cleanup(self):
+    try:
+      self.shutdown()
+      self.server_close()
+    except KeyboardInterrupt:
+      pass
+    logging.info('Stopped %s server. Total time processing requests: %dms',
+                 self.protocol, self.total_request_time)
+
+  def get_active_request_count(self):
+    return self.num_active_requests
+
+  def get_request(self):
+    self.num_active_connections += 1
+    if self.num_active_connections >= HttpProxyServer.connection_limit:
+      logging.error(
+          'Number of active connections (%s) surpasses the '
+          'supported limit of %s.' %
+          (self.num_active_connections, HttpProxyServer.connection_limit))
+    return BaseHTTPServer.HTTPServer.get_request(self)
+
+  def close_request(self, request):
+    BaseHTTPServer.HTTPServer.close_request(self, request)
+    self.num_active_connections -= 1
+
+
+class HttpsProxyServer(HttpProxyServer):
+  """SSL server that generates certs for each host."""
+
+  def __init__(self, http_archive_fetch, custom_handlers, rules,
+               https_root_ca_cert_path, **kwargs):
+    self.ca_cert_path = https_root_ca_cert_path
+    self.HANDLER = sslproxy.wrap_handler(HttpArchiveHandler)
+    HttpProxyServer.__init__(self, http_archive_fetch, custom_handlers, rules,
+                             is_ssl=True, protocol='HTTPS', **kwargs)
+    with open(self.ca_cert_path, 'r') as cert_file:
+      self._ca_cert_str = cert_file.read()
+    self._host_to_cert_map = {}
+    self._server_cert_to_cert_map = {}
+
+  def cleanup(self):
+    try:
+      self.shutdown()
+      self.server_close()
+    except KeyboardInterrupt:
+      pass
+
+  def get_certificate(self, host):
+    if host in self._host_to_cert_map:
+      return self._host_to_cert_map[host]
+
+    server_cert = self.http_archive_fetch.http_archive.get_server_cert(host)
+    if server_cert in self._server_cert_to_cert_map:
+      cert = self._server_cert_to_cert_map[server_cert]
+      self._host_to_cert_map[host] = cert
+      return cert
+
+    cert = certutils.generate_cert(self._ca_cert_str, server_cert, host)
+    self._server_cert_to_cert_map[server_cert] = cert
+    self._host_to_cert_map[host] = cert
+    return cert
+
+  def handle_error(self, request, client_address):
+    _HandleSSLCertificateError()
+
+
+class SingleCertHttpsProxyServer(HttpProxyServer):
+  """SSL server."""
+
+  def __init__(self, http_archive_fetch, custom_handlers, rules,
+               https_root_ca_cert_path, **kwargs):
+    HttpProxyServer.__init__(self, http_archive_fetch, custom_handlers, rules,
+                             is_ssl=True, protocol='HTTPS', **kwargs)
+    self.socket = ssl.wrap_socket(
+        self.socket, certfile=https_root_ca_cert_path, server_side=True,
+        do_handshake_on_connect=False)
+    # Ancestor class, DaemonServer, calls serve_forever() during its __init__.
+
+  def handle_error(self, request, client_address):
+    _HandleSSLCertificateError()
+
+
+class HttpToHttpsProxyServer(HttpProxyServer):
+  """Listens for HTTP requests but sends them to the target as HTTPS requests"""
+
+  def __init__(self, http_archive_fetch, custom_handlers, rules, **kwargs):
+    HttpProxyServer.__init__(self, http_archive_fetch, custom_handlers, rules,
+                             is_ssl=True, protocol='HTTP-to-HTTPS', **kwargs)
+
+  def handle_error(self, request, client_address):
+    _HandleSSLCertificateError()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/httpproxy_test.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/httpproxy_test.py
new file mode 100644
index 0000000..ff34180
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/httpproxy_test.py
@@ -0,0 +1,243 @@
+#!/usr/bin/env python
+# Copyright 2015 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+import httparchive
+import httplib
+import httpproxy
+import threading
+import unittest
+import util
+
+
+class MockCustomResponseHandler(object):
+  def __init__(self, response):
+    """
+    Args:
+      response: An instance of ArchivedHttpResponse that is returned for each
+      request.
+    """
+    self._response = response
+
+  def handle(self, request):
+    del request
+    return self._response
+
+
+class MockHttpArchiveFetch(object):
+  def __init__(self, response):
+    """
+    Args:
+      response: An instance of ArchivedHttpResponse that is returned for each
+      request.
+    """
+    self.is_record_mode = False
+    self._response = response
+
+  def __call__(self, request):
+    del request # unused
+    return self._response
+
+
+class MockHttpArchiveHandler(httpproxy.HttpArchiveHandler):
+  def handle_one_request(self):
+    httpproxy.HttpArchiveHandler.handle_one_request(self)
+    HttpProxyTest.HANDLED_REQUEST_COUNT += 1
+
+
+class MockRules(object):
+  def Find(self, unused_rule_type_name):  # pylint: disable=unused-argument
+    return lambda unused_request, unused_response: None
+
+
+class HttpProxyTest(unittest.TestCase):
+  def setUp(self):
+    self.has_proxy_server_bound_port = False
+    self.has_proxy_server_started = False
+    self.allow_generate_304 = False
+    self.serve_response_by_http_archive = False
+
+  def set_up_proxy_server(self, response):
+    """
+    Args:
+      response: An instance of ArchivedHttpResponse that is returned for each
+      request.
+    """
+    HttpProxyTest.HANDLED_REQUEST_COUNT = 0
+    self.host = 'localhost'
+    self.port = 8889
+    custom_handlers = MockCustomResponseHandler(
+        response if not self.serve_response_by_http_archive else None)
+    rules = MockRules()
+    http_archive_fetch = MockHttpArchiveFetch(
+        response if self.serve_response_by_http_archive else None)
+    self.proxy_server = httpproxy.HttpProxyServer(
+        http_archive_fetch, custom_handlers, rules,
+        host=self.host, port=self.port,
+        allow_generate_304=self.allow_generate_304)
+    self.proxy_server.RequestHandlerClass = MockHttpArchiveHandler
+    self.has_proxy_server_bound_port = True
+
+  def tear_down_proxy_server(self):
+    if self.has_proxy_server_started:
+      self.proxy_server.shutdown()
+    if self.has_proxy_server_bound_port:
+      self.proxy_server.server_close()
+
+  def tearDown(self):
+    self.tear_down_proxy_server()
+
+  def serve_requests_forever(self):
+    self.has_proxy_server_started = True
+    self.proxy_server.serve_forever(poll_interval=0.01)
+
+  # Tests that handle_one_request does not leak threads, and does not try to
+  # re-handle connections that are finished.
+  def test_handle_one_request_closes_connection(self):
+    # By default, BaseHTTPServer.py treats all HTTP 1.1 requests as keep-alive.
+    # Intentionally use HTTP 1.0 to prevent this behavior.
+    response = httparchive.ArchivedHttpResponse(
+        version=10, status=200, reason="OK",
+        headers=[], response_data=["bat1"])
+    self.set_up_proxy_server(response)
+    t = threading.Thread(
+        target=HttpProxyTest.serve_requests_forever, args=(self,))
+    t.start()
+
+    initial_thread_count = threading.activeCount()
+
+    # Make a bunch of requests.
+    request_count = 10
+    for _ in range(request_count):
+      conn = httplib.HTTPConnection('localhost', 8889, timeout=10)
+      conn.request("GET", "/index.html")
+      res = conn.getresponse().read()
+      self.assertEqual(res, "bat1")
+      conn.close()
+
+    # Check to make sure that there is no leaked thread.
+    util.WaitFor(lambda: threading.activeCount() == initial_thread_count, 2)
+
+    self.assertEqual(request_count, HttpProxyTest.HANDLED_REQUEST_COUNT)
+
+
+  # Tests that keep-alive header works.
+  def test_keep_alive_header(self):
+    response = httparchive.ArchivedHttpResponse(
+        version=11, status=200, reason="OK",
+        headers=[("Connection", "keep-alive")], response_data=["bat1"])
+    self.set_up_proxy_server(response)
+    t = threading.Thread(
+        target=HttpProxyTest.serve_requests_forever, args=(self,))
+    t.start()
+
+    initial_thread_count = threading.activeCount()
+
+    # Make a bunch of requests.
+    request_count = 10
+    connections = []
+    for _ in range(request_count):
+      conn = httplib.HTTPConnection('localhost', 8889, timeout=10)
+      conn.request("GET", "/index.html", headers={"Connection": "keep-alive"})
+      res = conn.getresponse().read()
+      self.assertEqual(res, "bat1")
+      connections.append(conn)
+
+    # Repeat the same requests.
+    for conn in connections:
+      conn.request("GET", "/index.html", headers={"Connection": "keep-alive"})
+      res = conn.getresponse().read()
+      self.assertEqual(res, "bat1")
+
+    # Check that the right number of requests have been handled.
+    self.assertEqual(2 * request_count, HttpProxyTest.HANDLED_REQUEST_COUNT)
+
+    # Check to make sure that exactly "request_count" new threads are active.
+    self.assertEqual(
+        threading.activeCount(), initial_thread_count + request_count)
+
+    for conn in connections:
+      conn.close()
+
+    util.WaitFor(lambda: threading.activeCount() == initial_thread_count, 1)
+
+  # Test that opening 400 simultaneous connections does not cause httpproxy to
+  # hit a process fd limit. The default limit is 256 fds.
+  def test_max_fd(self):
+    response = httparchive.ArchivedHttpResponse(
+        version=11, status=200, reason="OK",
+        headers=[("Connection", "keep-alive")], response_data=["bat1"])
+    self.set_up_proxy_server(response)
+    t = threading.Thread(
+        target=HttpProxyTest.serve_requests_forever, args=(self,))
+    t.start()
+
+    # Make a bunch of requests.
+    request_count = 400
+    connections = []
+    for _ in range(request_count):
+      conn = httplib.HTTPConnection('localhost', 8889, timeout=10)
+      conn.request("GET", "/index.html", headers={"Connection": "keep-alive"})
+      res = conn.getresponse().read()
+      self.assertEqual(res, "bat1")
+      connections.append(conn)
+
+    # Check that the right number of requests have been handled.
+    self.assertEqual(request_count, HttpProxyTest.HANDLED_REQUEST_COUNT)
+
+    for conn in connections:
+      conn.close()
+
+  # Tests that conditional requests return 304.
+  def test_generate_304(self):
+    REQUEST_HEADERS = [
+        {},
+        {'If-Modified-Since': 'whatever'},
+        {'If-None-Match': 'whatever yet again'}]
+    RESPONSE_STATUSES = [200, 204, 304, 404]
+    for allow_generate_304 in [False, True]:
+      self.allow_generate_304 = allow_generate_304
+      for serve_response_by_http_archive in [False, True]:
+        self.serve_response_by_http_archive = serve_response_by_http_archive
+        for response_status in RESPONSE_STATUSES:
+          response = None
+          if response_status != 404:
+            response = httparchive.ArchivedHttpResponse(
+                version=11, status=response_status, reason="OK", headers=[],
+                response_data=["some content"])
+          self.set_up_proxy_server(response)
+          t = threading.Thread(
+              target=HttpProxyTest.serve_requests_forever, args=(self,))
+          t.start()
+          for method in ['GET', 'HEAD', 'POST']:
+            for headers in REQUEST_HEADERS:
+              connection = httplib.HTTPConnection('localhost', 8889, timeout=10)
+              connection.request(method, "/index.html", headers=headers)
+              response = connection.getresponse()
+              connection.close()
+              if (allow_generate_304 and
+                  serve_response_by_http_archive and
+                  method in ['GET', 'HEAD'] and
+                  headers and
+                  response_status == 200):
+                self.assertEqual(304, response.status)
+                self.assertEqual('', response.read())
+              else:
+                self.assertEqual(response_status, response.status)
+          self.tear_down_proxy_server()
+
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/httpzlib.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/httpzlib.py
new file mode 100644
index 0000000..b06cb29
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/httpzlib.py
@@ -0,0 +1,86 @@
+#!/usr/bin/env python
+# Copyright 2011 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Apply gzip/deflate to separate chunks of data."""
+
+import struct
+import zlib
+
+GZIP_HEADER = (
+    '\037\213'             # magic header
+    '\010'                 # compression method
+    '\000'                 # flags (none)
+    '\000\000\000\000'     # packed time (use zero)
+    '\002'
+    '\377')
+
+
+def compress_chunks(uncompressed_chunks, use_gzip):
+  """Compress a list of data with gzip or deflate.
+
+  The returned chunks may be used with HTTP chunked encoding.
+
+  Args:
+    uncompressed_chunks: a list of strings
+       (e.g. ["this is the first chunk", "and the second"])
+    use_gzip: if True, compress with gzip. Otherwise, use deflate.
+
+  Returns:
+    [compressed_chunk_1, compressed_chunk_2, ...]
+  """
+  if use_gzip:
+    size = 0
+    crc = zlib.crc32("") & 0xffffffffL
+    compressor = zlib.compressobj(
+        6, zlib.DEFLATED, -zlib.MAX_WBITS, zlib.DEF_MEM_LEVEL, 0)
+  else:
+    compressor = zlib.compressobj()
+  compressed_chunks = []
+  last_index = len(uncompressed_chunks) - 1
+  for index, data in enumerate(uncompressed_chunks):
+    chunk = ''
+    if use_gzip:
+      size += len(data)
+      crc = zlib.crc32(data, crc) & 0xffffffffL
+      if index == 0:
+        chunk += GZIP_HEADER
+    chunk += compressor.compress(data)
+    if index < last_index:
+      chunk += compressor.flush(zlib.Z_SYNC_FLUSH)
+    else:
+      chunk += (compressor.flush(zlib.Z_FULL_FLUSH) +
+                compressor.flush())
+      if use_gzip:
+        chunk += (struct.pack("<L", long(crc)) +
+                  struct.pack("<L", long(size)))
+    compressed_chunks.append(chunk)
+  return compressed_chunks
+
+
+def uncompress_chunks(compressed_chunks, use_gzip):
+  """Uncompress a list of data compressed with gzip or deflate.
+
+  Args:
+    compressed_chunks: a list of compressed data
+    use_gzip: if True, uncompress with gzip. Otherwise, use deflate.
+
+  Returns:
+    [uncompressed_chunk_1, uncompressed_chunk_2, ...]
+  """
+  if use_gzip:
+    decompress = zlib.decompressobj(16 + zlib.MAX_WBITS).decompress
+  else:
+    decompress = zlib.decompressobj(-zlib.MAX_WBITS).decompress
+  return [decompress(c) for c in compressed_chunks]
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/mock-archive.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/mock-archive.txt
new file mode 100644
index 0000000..a90bb03
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/mock-archive.txt
@@ -0,0 +1,10 @@
+GET%www.zappos.com%/%%[('accept-encoding', 'gzip,deflate'), ('host', 'www.zappos.com')]
+GET%www.zappos.com%/css/print.20110525145237.css%%[('accept-encoding', 'gzip,deflate'), ('host', 'www.zappos.com')]
+GET%www.zappos.com%/favicon.ico%%[('accept-encoding', 'gzip,deflate'), ('host', 'www.zappos.com')]
+GET%www.zappos.com%/hydra/hydra.p.20110607.js%%[('accept-encoding', 'gzip,deflate'), ('host', 'www.zappos.com')]
+GET%www.zappos.com%/imgs/shadebg.20110525145241.png%%[('accept-encoding', 'gzip,deflate'), ('host', 'www.zappos.com')]
+GET%www.msn.com%/%%[('accept-encoding', 'gzip,deflate'), ('host', 'www.msn.com')]
+GET%www.msn.com%/?euid=&userGroup=W:default&PM=z:1%%[('accept-encoding', 'gzip,deflate'), ('host', 'www.msn.com'), ('x-requested-with', 'XMLHttpRequest')]
+GET%www.msn.com%/?euid=342%%[('accept-encoding', 'gzip,deflate'), ('host', 'www.msn.com'), ('x-requested-with', 'XMLHttpRequest')]
+GET%www.amazon.com%/%%[('accept-encoding', 'gzip,deflate'), ('host', 'www.amazon.com')]
+GET%www.google.com%/%%[('accept-encoding', 'gzip,deflate'), ('host', 'www.google.com')]
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/mockhttprequest.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/mockhttprequest.py
new file mode 100644
index 0000000..ac5df99
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/mockhttprequest.py
@@ -0,0 +1,59 @@
+#!/usr/bin/env python
+# Copyright 2010 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Mock instance of ArchivedHttpRequest used for testing."""
+
+
+class ArchivedHttpRequest(object):
+  """Mock instance of ArchivedHttpRequest in HttpArchive."""
+
+  def __init__(self, command, host, path, request_body, headers):
+    """Initialize an ArchivedHttpRequest.
+
+    Args:
+      command: a string (e.g. 'GET' or 'POST').
+      host: a host name (e.g. 'www.google.com').
+      path: a request path (e.g. '/search?q=dogs').
+      request_body: a request body string for a POST or None.
+      headers: [(header1, value1), ...] list of tuples
+    """
+    self.command = command
+    self.host = host
+    self.path = path
+    self.request_body = request_body
+    self.headers = headers
+    self.trimmed_headers = headers
+
+  def __str__(self):
+    return '%s %s%s %s' % (self.command, self.host, self.path,
+                           self.trimmed_headers)
+
+  def __repr__(self):
+    return repr((self.command, self.host, self.path, self.request_body,
+                 self.trimmed_headers))
+
+  def __hash__(self):
+    """Return a integer hash to use for hashed collections including dict."""
+    return hash(repr(self))
+
+  def __eq__(self, other):
+    """Define the __eq__ method to match the hash behavior."""
+    return repr(self) == repr(other)
+
+  def matches(self, command=None, host=None, path=None):
+    """Returns true iff the request matches all parameters."""
+    return ((command is None or command == self.command) and
+            (host is None or host == self.host) and
+            (path is None or path == self.path))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/net_configs.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/net_configs.py
new file mode 100644
index 0000000..644358e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/net_configs.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python
+# Copyright 2013 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Defines a list of common network speeds.
+
+These values come from http://www.webpagetest.org/
+
+See:
+https://sites.google.com/a/webpagetest.org/docs/other-resources/2011-fcc-broadband-data
+https://github.com/WPO-Foundation/webpagetest/blob/HEAD/www/settings/connectivity.ini.sample
+"""
+
+import collections
+
+
+NetConfig = collections.namedtuple('NetConfig', ['down', 'up', 'delay_ms'])
+
+
+# pylint: disable=bad-whitespace
+_NET_CONFIGS = {
+    'dialup': NetConfig(down=   '49Kbit/s', up=  '30Kbit/s', delay_ms= '120'),
+    '3g':     NetConfig(down= '1638Kbit/s', up= '768Kbit/s', delay_ms= '150'),
+    'dsl':    NetConfig(down= '1536Kbit/s', up= '384Kbit/s', delay_ms=  '50'),
+    'cable':  NetConfig(down=    '5Mbit/s', up=   '1Mbit/s', delay_ms=  '28'),
+    'fios':   NetConfig(down=   '20Mbit/s', up=   '5Mbit/s', delay_ms=   '4'),
+    }
+
+
+NET_CONFIG_NAMES = _NET_CONFIGS.keys()
+
+
+def GetNetConfig(key):
+  """Returns the NetConfig object corresponding to the given |key|."""
+  if key not in _NET_CONFIGS:
+    raise KeyError('No net config with key: %s' % key)
+  return _NET_CONFIGS[key]
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/platformsettings.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/platformsettings.py
new file mode 100644
index 0000000..81cb56c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/platformsettings.py
@@ -0,0 +1,794 @@
+#!/usr/bin/env python
+# Copyright 2010 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Provides cross-platform utility functions.
+
+Example:
+  import platformsettings
+  ip = platformsettings.get_server_ip_address()
+
+Functions with "_temporary_" in their name automatically clean-up upon
+termination (via the atexit module).
+
+For the full list of functions, see the bottom of the file.
+"""
+
+import atexit
+import distutils.spawn
+import distutils.version
+import fileinput
+import logging
+import os
+import platform
+import re
+import socket
+import stat
+import subprocess
+import sys
+import time
+import urlparse
+
+
+class PlatformSettingsError(Exception):
+  """Module catch-all error."""
+  pass
+
+
+class DnsReadError(PlatformSettingsError):
+  """Raised when unable to read DNS settings."""
+  pass
+
+
+class DnsUpdateError(PlatformSettingsError):
+  """Raised when unable to update DNS settings."""
+  pass
+
+
+class NotAdministratorError(PlatformSettingsError):
+  """Raised when not running as administrator."""
+  pass
+
+
+class CalledProcessError(PlatformSettingsError):
+  """Raised when a _check_output() process returns a non-zero exit status."""
+  def __init__(self, returncode, cmd):
+    super(CalledProcessError, self).__init__()
+    self.returncode = returncode
+    self.cmd = cmd
+
+  def __str__(self):
+    return 'Command "%s" returned non-zero exit status %d' % (
+        ' '.join(self.cmd), self.returncode)
+
+
+def FindExecutable(executable):
+  """Finds the given executable in PATH.
+
+  Since WPR may be invoked as sudo, meaning PATH is empty, we also hardcode a
+  few common paths.
+
+  Returns:
+    The fully qualified path with .exe appended if appropriate or None if it
+    doesn't exist.
+  """
+  return distutils.spawn.find_executable(executable,
+                                         os.pathsep.join([os.environ['PATH'],
+                                                          '/sbin',
+                                                          '/usr/bin',
+                                                          '/usr/sbin/',
+                                                          '/usr/local/sbin',
+                                                          ]))
+
+def HasSniSupport():
+  try:
+    import OpenSSL
+    return (distutils.version.StrictVersion(OpenSSL.__version__) >=
+            distutils.version.StrictVersion('0.13'))
+  except ImportError:
+    return False
+
+
+def SupportsFdLimitControl():
+  """Whether the platform supports changing the process fd limit."""
+  return os.name is 'posix'
+
+
+def GetFdLimit():
+  """Returns a tuple of (soft_limit, hard_limit)."""
+  import resource
+  return resource.getrlimit(resource.RLIMIT_NOFILE)
+
+
+def AdjustFdLimit(new_soft_limit, new_hard_limit):
+  """Sets a new soft and hard limit for max number of fds."""
+  import resource
+  resource.setrlimit(resource.RLIMIT_NOFILE, (new_soft_limit, new_hard_limit))
+
+
+class SystemProxy(object):
+  """A host/port pair for a HTTP or HTTPS proxy configuration."""
+
+  def __init__(self, host, port):
+    """Initialize a SystemProxy instance.
+
+    Args:
+      host: a host name or IP address string (e.g. "example.com" or "1.1.1.1").
+      port: a port string or integer (e.g. "8888" or 8888).
+    """
+    self.host = host
+    self.port = int(port) if port else None
+
+  def __nonzero__(self):
+    """True if the host is set."""
+    return bool(self.host)
+
+  @classmethod
+  def from_url(cls, proxy_url):
+    """Create a SystemProxy instance.
+
+    If proxy_url is None, an empty string, or an invalid URL, the
+    SystemProxy instance with have None and None for the host and port
+    (no exception is raised).
+
+    Args:
+      proxy_url: a proxy url string such as "http://proxy.com:8888/".
+    Returns:
+      a System proxy instance.
+    """
+    if proxy_url:
+      parse_result = urlparse.urlparse(proxy_url)
+      return cls(parse_result.hostname, parse_result.port)
+    return cls(None, None)
+
+
+class _BasePlatformSettings(object):
+
+  def get_system_logging_handler(self):
+    """Return a handler for the logging module (optional)."""
+    return None
+
+  def rerun_as_administrator(self):
+    """If needed, rerun the program with administrative privileges.
+
+    Raises NotAdministratorError if unable to rerun.
+    """
+    pass
+
+  def timer(self):
+    """Return the current time in seconds as a floating point number."""
+    return time.time()
+
+  def get_server_ip_address(self, is_server_mode=False):
+    """Returns the IP address to use for dnsproxy and ipfw."""
+    if is_server_mode:
+      return socket.gethostbyname(socket.gethostname())
+    return '127.0.0.1'
+
+  def get_httpproxy_ip_address(self, is_server_mode=False):
+    """Returns the IP address to use for httpproxy."""
+    if is_server_mode:
+      return '0.0.0.0'
+    return '127.0.0.1'
+
+  def get_system_proxy(self, use_ssl):
+    """Returns the system HTTP(S) proxy host, port."""
+    del use_ssl
+    return SystemProxy(None, None)
+
+  def _ipfw_cmd(self):
+    raise NotImplementedError
+
+  def ipfw(self, *args):
+    ipfw_cmd = (self._ipfw_cmd(), ) + args
+    return self._check_output(*ipfw_cmd, elevate_privilege=True)
+
+  def has_ipfw(self):
+    try:
+      self.ipfw('list')
+      return True
+    except AssertionError as e:
+      logging.warning('Failed to start ipfw command. '
+                      'Error: %s' % e.message)
+      return False
+
+  def _get_cwnd(self):
+    return None
+
+  def _set_cwnd(self, args):
+    pass
+
+  def _elevate_privilege_for_cmd(self, args):
+    return args
+
+  def _check_output(self, *args, **kwargs):
+    """Run Popen(*args) and return its output as a byte string.
+
+    Python 2.7 has subprocess.check_output. This is essentially the same
+    except that, as a convenience, all the positional args are used as
+    command arguments and the |elevate_privilege| kwarg is supported.
+
+    Args:
+      *args: sequence of program arguments
+      elevate_privilege: Run the command with elevated privileges.
+    Raises:
+      CalledProcessError if the program returns non-zero exit status.
+    Returns:
+      output as a byte string.
+    """
+    command_args = [str(a) for a in args]
+
+    if os.path.sep not in command_args[0]:
+      qualified_command = FindExecutable(command_args[0])
+      assert qualified_command, 'Failed to find %s in path' % command_args[0]
+      command_args[0] = qualified_command
+
+    if kwargs.get('elevate_privilege'):
+      command_args = self._elevate_privilege_for_cmd(command_args)
+
+    logging.debug(' '.join(command_args))
+    process = subprocess.Popen(
+        command_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+    output = process.communicate()[0]
+    retcode = process.poll()
+    if retcode:
+      raise CalledProcessError(retcode, command_args)
+    return output
+
+  def set_temporary_tcp_init_cwnd(self, cwnd):
+    cwnd = int(cwnd)
+    original_cwnd = self._get_cwnd()
+    if original_cwnd is None:
+      raise PlatformSettingsError('Unable to get current tcp init_cwnd.')
+    if cwnd == original_cwnd:
+      logging.info('TCP init_cwnd already set to target value: %s', cwnd)
+    else:
+      self._set_cwnd(cwnd)
+      if self._get_cwnd() == cwnd:
+        logging.info('Changed cwnd to %s', cwnd)
+        atexit.register(self._set_cwnd, original_cwnd)
+      else:
+        logging.error('Unable to update cwnd to %s', cwnd)
+
+  def setup_temporary_loopback_config(self):
+    """Setup the loopback interface similar to real interface.
+
+    We use loopback for much of our testing, and on some systems, loopback
+    behaves differently from real interfaces.
+    """
+    logging.error('Platform does not support loopback configuration.')
+
+  def _save_primary_interface_properties(self):
+    self._orig_nameserver = self.get_original_primary_nameserver()
+
+  def _restore_primary_interface_properties(self):
+    self._set_primary_nameserver(self._orig_nameserver)
+
+  def _get_primary_nameserver(self):
+    raise NotImplementedError
+
+  def _set_primary_nameserver(self, _):
+    raise NotImplementedError
+
+  def get_original_primary_nameserver(self):
+    if not hasattr(self, '_original_nameserver'):
+      self._original_nameserver = self._get_primary_nameserver()
+      logging.info('Saved original primary DNS nameserver: %s',
+                   self._original_nameserver)
+    return self._original_nameserver
+
+  def set_temporary_primary_nameserver(self, nameserver):
+    self._save_primary_interface_properties()
+    self._set_primary_nameserver(nameserver)
+    if self._get_primary_nameserver() == nameserver:
+      logging.info('Changed temporary primary nameserver to %s', nameserver)
+      atexit.register(self._restore_primary_interface_properties)
+    else:
+      raise self._get_dns_update_error()
+
+
+class _PosixPlatformSettings(_BasePlatformSettings):
+
+  # pylint: disable=abstract-method
+  # Suppress lint check for _get_primary_nameserver & _set_primary_nameserver
+
+  def rerun_as_administrator(self):
+    """If needed, rerun the program with administrative privileges.
+
+    Raises NotAdministratorError if unable to rerun.
+    """
+    if os.geteuid() != 0:
+      logging.warn('Rerunning with sudo: %s', sys.argv)
+      os.execv('/usr/bin/sudo', ['--'] + sys.argv)
+
+  def _elevate_privilege_for_cmd(self, args):
+    def IsSetUID(path):
+      return (os.stat(path).st_mode & stat.S_ISUID) == stat.S_ISUID
+
+    def IsElevated():
+      p = subprocess.Popen(
+          ['sudo', '-nv'], stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+          stderr=subprocess.STDOUT)
+      stdout = p.communicate()[0]
+      # Some versions of sudo set the returncode based on whether sudo requires
+      # a password currently. Other versions return output when password is
+      # required and no output when the user is already authenticated.
+      return not p.returncode and not stdout
+
+    if not IsSetUID(args[0]):
+      args = ['sudo'] + args
+
+      if not IsElevated():
+        print 'WPR needs to run %s under sudo. Please authenticate.' % args[1]
+        subprocess.check_call(['sudo', '-v'])  # Synchronously authenticate.
+
+        prompt = ('Would you like to always allow %s to run without sudo '
+                  '(via `sudo chmod +s %s`)? (y/N)' % (args[1], args[1]))
+        if raw_input(prompt).lower() == 'y':
+          subprocess.check_call(['sudo', 'chmod', '+s', args[1]])
+    return args
+
+  def get_system_proxy(self, use_ssl):
+    """Returns the system HTTP(S) proxy host, port."""
+    proxy_url = os.environ.get('https_proxy' if use_ssl else 'http_proxy')
+    return SystemProxy.from_url(proxy_url)
+
+  def _ipfw_cmd(self):
+    return 'ipfw'
+
+  def _get_dns_update_error(self):
+    return DnsUpdateError('Did you run under sudo?')
+
+  def _sysctl(self, *args, **kwargs):
+    sysctl_args = [FindExecutable('sysctl')]
+    if kwargs.get('use_sudo'):
+      sysctl_args = self._elevate_privilege_for_cmd(sysctl_args)
+    sysctl_args.extend(str(a) for a in args)
+    sysctl = subprocess.Popen(
+        sysctl_args, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
+    stdout = sysctl.communicate()[0]
+    return sysctl.returncode, stdout
+
+  def has_sysctl(self, name):
+    if not hasattr(self, 'has_sysctl_cache'):
+      self.has_sysctl_cache = {}
+    if name not in self.has_sysctl_cache:
+      self.has_sysctl_cache[name] = self._sysctl(name)[0] == 0
+    return self.has_sysctl_cache[name]
+
+  def set_sysctl(self, name, value):
+    rv = self._sysctl('%s=%s' % (name, value), use_sudo=True)[0]
+    if rv != 0:
+      logging.error('Unable to set sysctl %s: %s', name, rv)
+
+  def get_sysctl(self, name):
+    rv, value = self._sysctl('-n', name)
+    if rv == 0:
+      return value
+    else:
+      logging.error('Unable to get sysctl %s: %s', name, rv)
+      return None
+
+
+class _OsxPlatformSettings(_PosixPlatformSettings):
+  LOCAL_SLOWSTART_MIB_NAME = 'net.inet.tcp.local_slowstart_flightsize'
+
+  def _scutil(self, cmd):
+    scutil = subprocess.Popen([FindExecutable('scutil')],
+                               stdin=subprocess.PIPE, stdout=subprocess.PIPE)
+    return scutil.communicate(cmd)[0]
+
+  def _ifconfig(self, *args):
+    return self._check_output('ifconfig', *args, elevate_privilege=True)
+
+  def set_sysctl(self, name, value):
+    rv = self._sysctl('-w', '%s=%s' % (name, value), use_sudo=True)[0]
+    if rv != 0:
+      logging.error('Unable to set sysctl %s: %s', name, rv)
+
+  def _get_cwnd(self):
+    return int(self.get_sysctl(self.LOCAL_SLOWSTART_MIB_NAME))
+
+  def _set_cwnd(self, size):
+    self.set_sysctl(self.LOCAL_SLOWSTART_MIB_NAME, size)
+
+  def _get_loopback_mtu(self):
+    config = self._ifconfig('lo0')
+    match = re.search(r'\smtu\s+(\d+)', config)
+    return int(match.group(1)) if match else None
+
+  def setup_temporary_loopback_config(self):
+    """Configure loopback to temporarily use reasonably sized frames.
+
+    OS X uses jumbo frames by default (16KB).
+    """
+    TARGET_LOOPBACK_MTU = 1500
+    original_mtu = self._get_loopback_mtu()
+    if original_mtu is None:
+      logging.error('Unable to read loopback mtu. Setting left unchanged.')
+      return
+    if original_mtu == TARGET_LOOPBACK_MTU:
+      logging.debug('Loopback MTU already has target value: %d', original_mtu)
+    else:
+      self._ifconfig('lo0', 'mtu', TARGET_LOOPBACK_MTU)
+      if self._get_loopback_mtu() == TARGET_LOOPBACK_MTU:
+        logging.debug('Set loopback MTU to %d (was %d)',
+                      TARGET_LOOPBACK_MTU, original_mtu)
+        atexit.register(self._ifconfig, 'lo0', 'mtu', original_mtu)
+      else:
+        logging.error('Unable to change loopback MTU from %d to %d',
+                      original_mtu, TARGET_LOOPBACK_MTU)
+
+  def _get_dns_service_key(self):
+    output = self._scutil('show State:/Network/Global/IPv4')
+    lines = output.split('\n')
+    for line in lines:
+      key_value = line.split(' : ')
+      if key_value[0] == '  PrimaryService':
+        return 'State:/Network/Service/%s/DNS' % key_value[1]
+    raise DnsReadError('Unable to find DNS service key: %s', output)
+
+  def _get_primary_nameserver(self):
+    output = self._scutil('show %s' % self._get_dns_service_key())
+    match = re.search(
+        br'ServerAddresses\s+:\s+<array>\s+{\s+0\s+:\s+((\d{1,3}\.){3}\d{1,3})',
+        output)
+    if match:
+      return match.group(1)
+    else:
+      raise DnsReadError('Unable to find primary DNS server: %s', output)
+
+  def _set_primary_nameserver(self, dns):
+    command = '\n'.join([
+      'd.init',
+      'd.add ServerAddresses * %s' % dns,
+      'set %s' % self._get_dns_service_key()
+    ])
+    self._scutil(command)
+
+
+class _FreeBSDPlatformSettings(_PosixPlatformSettings):
+  """Partial implementation for FreeBSD.  Does not allow a DNS server to be
+  launched nor ipfw to be used.
+  """
+  RESOLV_CONF = '/etc/resolv.conf'
+
+  def _get_default_route_line(self):
+    raise NotImplementedError
+
+  def _set_cwnd(self, cwnd):
+    raise NotImplementedError
+
+  def _get_cwnd(self):
+    raise NotImplementedError
+
+  def setup_temporary_loopback_config(self):
+    raise NotImplementedError
+
+  def _write_resolve_conf(self, dns):
+    raise NotImplementedError
+
+  def _get_primary_nameserver(self):
+    try:
+      resolv_file = open(self.RESOLV_CONF)
+    except IOError:
+      raise DnsReadError()
+    for line in resolv_file:
+      if line.startswith('nameserver '):
+        return line.split()[1]
+    raise DnsReadError()
+
+  def _set_primary_nameserver(self, dns):
+    raise NotImplementedError
+
+
+class _LinuxPlatformSettings(_PosixPlatformSettings):
+  """The following thread recommends a way to update DNS on Linux:
+
+  http://ubuntuforums.org/showthread.php?t=337553
+
+         sudo cp /etc/dhcp3/dhclient.conf /etc/dhcp3/dhclient.conf.bak
+         sudo gedit /etc/dhcp3/dhclient.conf
+         #prepend domain-name-servers 127.0.0.1;
+         prepend domain-name-servers 208.67.222.222, 208.67.220.220;
+
+         prepend domain-name-servers 208.67.222.222, 208.67.220.220;
+         request subnet-mask, broadcast-address, time-offset, routers,
+             domain-name, domain-name-servers, host-name,
+             netbios-name-servers, netbios-scope;
+         #require subnet-mask, domain-name-servers;
+
+         sudo /etc/init.d/networking restart
+
+  The code below does not try to change dchp and does not restart networking.
+  Update this as needed to make it more robust on more systems.
+  """
+  RESOLV_CONF = '/etc/resolv.conf'
+  ROUTE_RE = re.compile('initcwnd (\d+)')
+  TCP_BASE_MSS = 'net.ipv4.tcp_base_mss'
+  TCP_MTU_PROBING = 'net.ipv4.tcp_mtu_probing'
+
+  def _get_default_route_line(self):
+    stdout = self._check_output('ip', 'route')
+    for line in stdout.split('\n'):
+      if line.startswith('default'):
+        return line
+    return None
+
+  def _set_cwnd(self, cwnd):
+    default_line = self._get_default_route_line()
+    self._check_output(
+        'ip', 'route', 'change', default_line, 'initcwnd', str(cwnd))
+
+  def _get_cwnd(self):
+    default_line = self._get_default_route_line()
+    m = self.ROUTE_RE.search(default_line)
+    if m:
+      return int(m.group(1))
+    # If 'initcwnd' wasn't found, then 0 means it's the system default.
+    return 0
+
+  def setup_temporary_loopback_config(self):
+    """Setup Linux to temporarily use reasonably sized frames.
+
+    Linux uses jumbo frames by default (16KB), using the combination
+    of MTU probing and a base MSS makes it use normal sized packets.
+
+    The reason this works is because tcp_base_mss is only used when MTU
+    probing is enabled.  And since we're using the max value, it will
+    always use the reasonable size.  This is relevant for server-side realism.
+    The client-side will vary depending on the client TCP stack config.
+    """
+    ENABLE_MTU_PROBING = 2
+    original_probing = self.get_sysctl(self.TCP_MTU_PROBING)
+    self.set_sysctl(self.TCP_MTU_PROBING, ENABLE_MTU_PROBING)
+    atexit.register(self.set_sysctl, self.TCP_MTU_PROBING, original_probing)
+
+    TCP_FULL_MSS = 1460
+    original_mss = self.get_sysctl(self.TCP_BASE_MSS)
+    self.set_sysctl(self.TCP_BASE_MSS, TCP_FULL_MSS)
+    atexit.register(self.set_sysctl, self.TCP_BASE_MSS, original_mss)
+
+  def _write_resolve_conf(self, dns):
+    is_first_nameserver_replaced = False
+    # The fileinput module uses sys.stdout as the edited file output.
+    for line in fileinput.input(self.RESOLV_CONF, inplace=1, backup='.bak'):
+      if line.startswith('nameserver ') and not is_first_nameserver_replaced:
+        print 'nameserver %s' % dns
+        is_first_nameserver_replaced = True
+      else:
+        print line,
+    if not is_first_nameserver_replaced:
+      raise DnsUpdateError('Could not find a suitable nameserver entry in %s' %
+                           self.RESOLV_CONF)
+
+  def _get_primary_nameserver(self):
+    try:
+      resolv_file = open(self.RESOLV_CONF)
+    except IOError:
+      raise DnsReadError()
+    for line in resolv_file:
+      if line.startswith('nameserver '):
+        return line.split()[1]
+    raise DnsReadError()
+
+  def _set_primary_nameserver(self, dns):
+    """Replace the first nameserver entry with the one given."""
+    try:
+      self._write_resolve_conf(dns)
+    except OSError, e:
+      if 'Permission denied' in e:
+        raise self._get_dns_update_error()
+      raise
+
+
+class _WindowsPlatformSettings(_BasePlatformSettings):
+
+  # pylint: disable=abstract-method
+  # Suppress lint check for _ipfw_cmd
+
+  def get_system_logging_handler(self):
+    """Return a handler for the logging module (optional).
+
+    For Windows, output can be viewed with DebugView.
+    http://technet.microsoft.com/en-us/sysinternals/bb896647.aspx
+    """
+    import ctypes
+    output_debug_string = ctypes.windll.kernel32.OutputDebugStringA
+    output_debug_string.argtypes = [ctypes.c_char_p]
+    class DebugViewHandler(logging.Handler):
+      def emit(self, record):
+        output_debug_string('[wpr] ' + self.format(record))
+    return DebugViewHandler()
+
+  def rerun_as_administrator(self):
+    """If needed, rerun the program with administrative privileges.
+
+    Raises NotAdministratorError if unable to rerun.
+    """
+    import ctypes
+    if not ctypes.windll.shell32.IsUserAnAdmin():
+      raise NotAdministratorError('Rerun with administrator privileges.')
+      #os.execv('runas', sys.argv)  # TODO: replace needed Windows magic
+
+  def timer(self):
+    """Return the current time in seconds as a floating point number.
+
+    From time module documentation:
+       On Windows, this function [time.clock()] returns wall-clock
+       seconds elapsed since the first call to this function, as a
+       floating point number, based on the Win32 function
+       QueryPerformanceCounter(). The resolution is typically better
+       than one microsecond.
+    """
+    return time.clock()
+
+  def _arp(self, *args):
+    return self._check_output('arp', *args)
+
+  def _route(self, *args):
+    return self._check_output('route', *args)
+
+  def _ipconfig(self, *args):
+    return self._check_output('ipconfig', *args)
+
+  def _get_mac_address(self, ip):
+    """Return the MAC address for the given ip."""
+    ip_re = re.compile(r'^\s*IP(?:v4)? Address[ .]+:\s+([0-9.]+)')
+    for line in self._ipconfig('/all').splitlines():
+      if line[:1].isalnum():
+        current_ip = None
+        current_mac = None
+      elif ':' in line:
+        line = line.strip()
+        ip_match = ip_re.match(line)
+        if ip_match:
+          current_ip = ip_match.group(1)
+        elif line.startswith('Physical Address'):
+          current_mac = line.split(':', 1)[1].lstrip()
+        if current_ip == ip and current_mac:
+          return current_mac
+    return None
+
+  def setup_temporary_loopback_config(self):
+    """On Windows, temporarily route the server ip to itself."""
+    ip = self.get_server_ip_address()
+    mac_address = self._get_mac_address(ip)
+    if self.mac_address:
+      self._arp('-s', ip, self.mac_address)
+      self._route('add', ip, ip, 'mask', '255.255.255.255')
+      atexit.register(self._arp, '-d', ip)
+      atexit.register(self._route, 'delete', ip, ip, 'mask', '255.255.255.255')
+    else:
+      logging.warn('Unable to configure loopback: MAC address not found.')
+    # TODO(slamm): Configure cwnd, MTU size
+
+  def _get_dns_update_error(self):
+    return DnsUpdateError('Did you run as administrator?')
+
+  def _netsh_show_dns(self):
+    """Return DNS information:
+
+    Example output:
+        Configuration for interface "Local Area Connection 3"
+        DNS servers configured through DHCP:  None
+        Register with which suffix:           Primary only
+
+        Configuration for interface "Wireless Network Connection 2"
+        DNS servers configured through DHCP:  192.168.1.1
+        Register with which suffix:           Primary only
+    """
+    return self._check_output('netsh', 'interface', 'ip', 'show', 'dns')
+
+  def _netsh_set_dns(self, iface_name, addr):
+    """Modify DNS information on the primary interface."""
+    output = self._check_output('netsh', 'interface', 'ip', 'set', 'dns',
+                                iface_name, 'static', addr)
+
+  def _netsh_set_dns_dhcp(self, iface_name):
+    """Modify DNS information on the primary interface."""
+    output = self._check_output('netsh', 'interface', 'ip', 'set', 'dns',
+                                iface_name, 'dhcp')
+
+  def _get_interfaces_with_dns(self):
+    output = self._netsh_show_dns()
+    lines = output.split('\n')
+    iface_re = re.compile(r'^Configuration for interface \"(?P<name>.*)\"')
+    dns_re = re.compile(r'(?P<kind>.*):\s+(?P<dns>\d+\.\d+\.\d+\.\d+)')
+    iface_name = None
+    iface_dns = None
+    iface_kind = None
+    ifaces = []
+    for line in lines:
+      iface_match = iface_re.match(line)
+      if iface_match:
+        iface_name = iface_match.group('name')
+      dns_match = dns_re.match(line)
+      if dns_match:
+        iface_dns = dns_match.group('dns')
+        iface_dns_config = dns_match.group('kind').strip()
+        if iface_dns_config == "Statically Configured DNS Servers":
+          iface_kind = "static"
+        elif iface_dns_config == "DNS servers configured through DHCP":
+          iface_kind = "dhcp"
+      if iface_name and iface_dns and iface_kind:
+        ifaces.append((iface_dns, iface_name, iface_kind))
+        iface_name = None
+        iface_dns = None
+    return ifaces
+
+  def _save_primary_interface_properties(self):
+    # TODO(etienneb): On windows, an interface can have multiple DNS server
+    # configured. We should save/restore all of them.
+    ifaces = self._get_interfaces_with_dns()
+    self._primary_interfaces = ifaces
+
+  def _restore_primary_interface_properties(self):
+    for iface in self._primary_interfaces:
+      (iface_dns, iface_name, iface_kind) = iface
+      self._netsh_set_dns(iface_name, iface_dns)
+      if iface_kind == "dhcp":
+        self._netsh_set_dns_dhcp(iface_name)
+
+  def _get_primary_nameserver(self):
+    ifaces = self._get_interfaces_with_dns()
+    if not len(ifaces):
+      raise DnsUpdateError("Interface with valid DNS configured not found.")
+    (iface_dns, iface_name, iface_kind) = ifaces[0]
+    return iface_dns
+
+  def _set_primary_nameserver(self, dns):
+    for iface in self._primary_interfaces:
+      (iface_dns, iface_name, iface_kind) = iface
+      self._netsh_set_dns(iface_name, dns)
+
+
+class _WindowsXpPlatformSettings(_WindowsPlatformSettings):
+  def _ipfw_cmd(self):
+    return (r'third_party\ipfw_win32\ipfw.exe',)
+
+
+def _new_platform_settings(system, release):
+  """Make a new instance of PlatformSettings for the current system."""
+  if system == 'Darwin':
+    return _OsxPlatformSettings()
+  if system == 'Linux':
+    return _LinuxPlatformSettings()
+  if system == 'Windows' and release == 'XP':
+    return _WindowsXpPlatformSettings()
+  if system == 'Windows':
+    return _WindowsPlatformSettings()
+  if system == 'FreeBSD':
+    return _FreeBSDPlatformSettings()
+  raise NotImplementedError('Sorry %s %s is not supported.' % (system, release))
+
+
+# Create one instance of the platform-specific settings and
+# make the functions available at the module-level.
+_inst = _new_platform_settings(platform.system(), platform.release())
+
+get_system_logging_handler = _inst.get_system_logging_handler
+rerun_as_administrator = _inst.rerun_as_administrator
+timer = _inst.timer
+
+get_server_ip_address = _inst.get_server_ip_address
+get_httpproxy_ip_address = _inst.get_httpproxy_ip_address
+get_system_proxy = _inst.get_system_proxy
+ipfw = _inst.ipfw
+has_ipfw = _inst.has_ipfw
+set_temporary_tcp_init_cwnd = _inst.set_temporary_tcp_init_cwnd
+setup_temporary_loopback_config = _inst.setup_temporary_loopback_config
+
+get_original_primary_nameserver = _inst.get_original_primary_nameserver
+set_temporary_primary_nameserver = _inst.set_temporary_primary_nameserver
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/platformsettings_test.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/platformsettings_test.py
new file mode 100755
index 0000000..3172f9b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/platformsettings_test.py
@@ -0,0 +1,248 @@
+#!/usr/bin/env python
+# Copyright 2011 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Unit tests for platformsettings.
+
+Usage:
+$ ./platformsettings_test.py
+"""
+
+import unittest
+
+import platformsettings
+
+WINDOWS_7_IP = '172.11.25.170'
+WINDOWS_7_MAC = '00-1A-44-DA-88-C0'
+WINDOWS_7_IPCONFIG = """
+Windows IP Configuration
+
+   Host Name . . . . . . . . . . . . : THEHOST1-W
+   Primary Dns Suffix  . . . . . . . : something.example.com
+   Node Type . . . . . . . . . . . . : Hybrid
+   IP Routing Enabled. . . . . . . . : No
+   WINS Proxy Enabled. . . . . . . . : No
+   DNS Suffix Search List. . . . . . : example.com
+                                       another.example.com
+
+Ethernet adapter Local Area Connection:
+
+   Connection-specific DNS Suffix  . : somethingexample.com
+   Description . . . . . . . . . . . : Int PRO/1000 MT Network Connection
+   Physical Address. . . . . . . . . : %(mac_addr)s
+   DHCP Enabled. . . . . . . . . . . : Yes
+   Autoconfiguration Enabled . . . . : Yes
+   IPv6 Address. . . . . . . . . . . : 1234:0:1000:1200:839f:d256:3a6c:210(Preferred)
+   Temporary IPv6 Address. . . . . . : 2143:0:2100:1800:38f9:2d65:a3c6:120(Preferred)
+   Link-local IPv6 Address . . . . . : abcd::1234:1a33:b2cc:238%%18(Preferred)
+   IPv4 Address. . . . . . . . . . . : %(ip_addr)s(Preferred)
+   Subnet Mask . . . . . . . . . . . : 255.255.248.0
+   Lease Obtained. . . . . . . . . . : Thursday, April 28, 2011 9:40:22 PM
+   Lease Expires . . . . . . . . . . : Tuesday, May 10, 2011 12:15:48 PM
+   Default Gateway . . . . . . . . . : abcd::2:37ee:ef70:56%%18
+                                       172.11.25.254
+   DHCP Server . . . . . . . . . . . : 172.11.22.33
+   DNS Servers . . . . . . . . . . . : 8.8.4.4
+   NetBIOS over Tcpip. . . . . . . . : Enabled
+""" % {'ip_addr': WINDOWS_7_IP, 'mac_addr': WINDOWS_7_MAC}
+
+WINDOWS_XP_IP = '172.1.2.3'
+WINDOWS_XP_MAC = '00-34-B8-1F-FA-70'
+WINDOWS_XP_IPCONFIG = """
+Windows IP Configuration
+
+        Host Name . . . . . . . . . . . . : HOSTY-0
+        Primary Dns Suffix  . . . . . . . :
+        Node Type . . . . . . . . . . . . : Unknown
+        IP Routing Enabled. . . . . . . . : No
+        WINS Proxy Enabled. . . . . . . . : No
+        DNS Suffix Search List. . . . . . : example.com
+
+Ethernet adapter Local Area Connection 2:
+
+        Connection-specific DNS Suffix  . : example.com
+        Description . . . . . . . . . . . : Int Adapter (PILA8470B)
+        Physical Address. . . . . . . . . : %(mac_addr)s
+        Dhcp Enabled. . . . . . . . . . . : Yes
+        Autoconfiguration Enabled . . . . : Yes
+        IP Address. . . . . . . . . . . . : %(ip_addr)s
+        Subnet Mask . . . . . . . . . . . : 255.255.254.0
+        Default Gateway . . . . . . . . . : 172.1.2.254
+        DHCP Server . . . . . . . . . . . : 172.1.3.241
+        DNS Servers . . . . . . . . . . . : 172.1.3.241
+                                            8.8.8.8
+                                            8.8.4.4
+        Lease Obtained. . . . . . . . . . : Thursday, April 07, 2011 9:14:55 AM
+        Lease Expires . . . . . . . . . . : Thursday, April 07, 2011 1:14:55 PM
+""" % {'ip_addr': WINDOWS_XP_IP, 'mac_addr': WINDOWS_XP_MAC}
+
+
+# scutil show State:/Network/Global/IPv4
+OSX_IPV4_STATE = """
+<dictionary> {
+  PrimaryInterface : en1
+  PrimaryService : 8824452C-FED4-4C09-9256-40FB146739E0
+  Router : 192.168.1.1
+}
+"""
+
+# scutil show State:/Network/Service/[PRIMARY_SERVICE_KEY]/DNS
+OSX_DNS_STATE_LION = """
+<dictionary> {
+  DomainName : mtv.corp.google.com
+  SearchDomains : <array> {
+    0 : mtv.corp.google.com
+    1 : corp.google.com
+    2 : prod.google.com
+    3 : prodz.google.com
+    4 : google.com
+  }
+  ServerAddresses : <array> {
+    0 : 172.72.255.1
+    1 : 172.49.117.57
+    2 : 172.54.116.57
+  }
+}
+"""
+
+OSX_DNS_STATE_SNOW_LEOPARD = """
+<dictionary> {
+  ServerAddresses : <array> {
+    0 : 172.27.1.1
+    1 : 172.94.117.57
+    2 : 172.45.116.57
+  }
+  DomainName : mtv.corp.google.com
+  SearchDomains : <array> {
+    0 : mtv.corp.google.com
+    1 : corp.google.com
+    2 : prod.google.com
+    3 : prodz.google.com
+    4 : google.com
+  }
+}
+"""
+
+
+class SystemProxyTest(unittest.TestCase):
+
+  def test_basic(self):
+    system_proxy = platformsettings.SystemProxy(None, None)
+    self.assertEqual(None, system_proxy.host)
+    self.assertEqual(None, system_proxy.port)
+    self.assertFalse(system_proxy)
+
+  def test_from_url_empty(self):
+    system_proxy = platformsettings.SystemProxy.from_url('')
+    self.assertEqual(None, system_proxy.host)
+    self.assertEqual(None, system_proxy.port)
+    self.assertFalse(system_proxy)
+
+  def test_from_url_basic(self):
+    system_proxy = platformsettings.SystemProxy.from_url('http://pxy.com:8888/')
+    self.assertEqual('pxy.com', system_proxy.host)
+    self.assertEqual(8888, system_proxy.port)
+    self.assertTrue(system_proxy)
+
+  def test_from_url_no_port(self):
+    system_proxy = platformsettings.SystemProxy.from_url('http://pxy.com/')
+    self.assertEqual('pxy.com', system_proxy.host)
+    self.assertEqual(None, system_proxy.port)
+    self.assertTrue(system_proxy)
+
+  def test_from_url_empty_string(self):
+    system_proxy = platformsettings.SystemProxy.from_url('')
+    self.assertEqual(None, system_proxy.host)
+    self.assertEqual(None, system_proxy.port)
+    self.assertFalse(system_proxy)
+
+  def test_from_url_bad_string(self):
+    system_proxy = platformsettings.SystemProxy.from_url('foo:80')
+    self.assertEqual(None, system_proxy.host)
+    self.assertEqual(None, system_proxy.port)
+    self.assertFalse(system_proxy)
+
+
+class HasSniTest(unittest.TestCase):
+  def test_has_sni(self):
+    # Check that no exception is raised.
+    platformsettings.HasSniSupport()
+
+
+# pylint: disable=abstract-method
+class Win7Settings(platformsettings._WindowsPlatformSettings):
+  @classmethod
+  def _ipconfig(cls, *args):
+    if args == ('/all',):
+      return WINDOWS_7_IPCONFIG
+    raise RuntimeError
+
+class WinXpSettings(platformsettings._WindowsPlatformSettings):
+  @classmethod
+  def _ipconfig(cls, *args):
+    if args == ('/all',):
+      return WINDOWS_XP_IPCONFIG
+    raise RuntimeError
+
+
+class WindowsPlatformSettingsTest(unittest.TestCase):
+  def test_get_mac_address_xp(self):
+    self.assertEqual(WINDOWS_XP_MAC,
+                     WinXpSettings()._get_mac_address(WINDOWS_XP_IP))
+
+  def test_get_mac_address_7(self):
+    self.assertEqual(WINDOWS_7_MAC,
+                     Win7Settings()._get_mac_address(WINDOWS_7_IP))
+
+
+class OsxSettings(platformsettings._OsxPlatformSettings):
+  def __init__(self):
+    super(OsxSettings, self).__init__()
+    self.ipv4_state = OSX_IPV4_STATE
+    self.dns_state = None  # varies by test
+
+  def _scutil(self, cmd):
+    if cmd == 'show State:/Network/Global/IPv4':
+      return self.ipv4_state
+    elif cmd.startswith('show State:/Network/Service/'):
+      return self.dns_state
+    raise RuntimeError("Unrecognized cmd: %s", cmd)
+
+
+class OsxPlatformSettingsTest(unittest.TestCase):
+  def setUp(self):
+    self.settings = OsxSettings()
+
+  def test_get_primary_nameserver_lion(self):
+    self.settings.dns_state = OSX_DNS_STATE_LION
+    self.assertEqual('172.72.255.1', self.settings._get_primary_nameserver())
+
+  def test_get_primary_nameserver_snow_leopard(self):
+    self.settings.dns_state = OSX_DNS_STATE_SNOW_LEOPARD
+    self.assertEqual('172.27.1.1', self.settings._get_primary_nameserver())
+
+  def test_get_primary_nameserver_unexpected_ipv4_state_raises(self):
+    self.settings.ipv4_state = 'Some error'
+    self.settings.dns_state = OSX_DNS_STATE_SNOW_LEOPARD
+    self.assertRaises(platformsettings.DnsReadError,
+                      self.settings._get_primary_nameserver)
+
+  def test_get_primary_nameserver_unexpected_dns_state_raises(self):
+    self.settings.dns_state = 'Some other error'
+    self.assertRaises(platformsettings.DnsReadError,
+                      self.settings._get_primary_nameserver)
+
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/proxyshaper.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/proxyshaper.py
new file mode 100644
index 0000000..6c6976f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/proxyshaper.py
@@ -0,0 +1,125 @@
+#!/usr/bin/env python
+# Copyright 2012 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Simulate network characteristics directly in Python.
+
+Allows running replay without dummynet.
+"""
+
+import logging
+import platformsettings
+import re
+import time
+
+
+TIMER = platformsettings.timer
+
+
+class ProxyShaperError(Exception):
+  """Module catch-all error."""
+  pass
+
+class BandwidthValueError(ProxyShaperError):
+  """Raised for unexpected dummynet-style bandwidth value."""
+  pass
+
+
+class RateLimitedFile(object):
+  """Wrap a file like object with rate limiting.
+
+  TODO(slamm): Simulate slow-start.
+      Each RateLimitedFile corresponds to one-direction of a
+      bidirectional socket. Slow-start can be added here (algorithm needed).
+      Will consider changing this class to take read and write files and
+      corresponding bit rates for each.
+  """
+  BYTES_PER_WRITE = 1460
+
+  def __init__(self, request_counter, f, bps):
+    """Initialize a RateLimiter.
+
+    Args:
+      request_counter: callable to see how many requests share the limit.
+      f: file-like object to wrap.
+      bps: an integer of bits per second.
+    """
+    self.request_counter = request_counter
+    self.original_file = f
+    self.bps = bps
+
+  def transfer_seconds(self, num_bytes):
+    """Seconds to read/write |num_bytes| with |self.bps|."""
+    return 8.0 * num_bytes / self.bps
+
+  def write(self, data):
+    num_bytes = len(data)
+    num_sent_bytes = 0
+    while num_sent_bytes < num_bytes:
+      num_write_bytes = min(self.BYTES_PER_WRITE, num_bytes - num_sent_bytes)
+      num_requests = self.request_counter()
+      wait = self.transfer_seconds(num_write_bytes) * num_requests
+      logging.debug('write sleep: %0.4fs (%d requests)', wait, num_requests)
+      time.sleep(wait)
+
+      self.original_file.write(
+          data[num_sent_bytes:num_sent_bytes + num_write_bytes])
+      num_sent_bytes += num_write_bytes
+
+  def _read(self, read_func, size):
+    start = TIMER()
+    data = read_func(size)
+    read_seconds = TIMER() - start
+    num_bytes = len(data)
+    num_requests = self.request_counter()
+    wait = self.transfer_seconds(num_bytes) * num_requests - read_seconds
+    if wait > 0:
+      logging.debug('read sleep: %0.4fs %d requests)', wait, num_requests)
+      time.sleep(wait)
+    return data
+
+  def readline(self, size=-1):
+    return self._read(self.original_file.readline, size)
+
+  def read(self, size=-1):
+    return self._read(self.original_file.read, size)
+
+  def __getattr__(self, name):
+    """Forward any non-overriden calls."""
+    return getattr(self.original_file, name)
+
+
+def GetBitsPerSecond(bandwidth):
+  """Return bits per second represented by dummynet bandwidth option.
+
+  See ipfw/dummynet.c:read_bandwidth for how it is really done.
+
+  Args:
+    bandwidth: a dummynet-style bandwidth specification (e.g. "10Kbit/s")
+  """
+  if bandwidth == '0':
+    return 0
+  bw_re = r'^(\d+)(?:([KM])?(bit|Byte)/s)?$'
+  match = re.match(bw_re, str(bandwidth))
+  if not match:
+    raise BandwidthValueError('Value, "%s", does not match regex: %s' % (
+        bandwidth, bw_re))
+  bw = int(match.group(1))
+  if match.group(2) == 'K':
+    bw *= 1000
+  if match.group(2) == 'M':
+    bw *= 1000000
+  if match.group(3) == 'Byte':
+    bw *= 8
+  return bw
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/proxyshaper_test.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/proxyshaper_test.py
new file mode 100755
index 0000000..5c2e3ae
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/proxyshaper_test.py
@@ -0,0 +1,141 @@
+#!/usr/bin/env python
+# Copyright 2012 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Unit tests for proxyshaper.
+
+Usage:
+$ ./proxyshaper_test.py
+"""
+
+import proxyshaper
+import StringIO
+import unittest
+
+
+# pylint: disable=bad-whitespace
+VALID_RATES = (
+    # input,       expected_bps
+    ( '384Kbit/s',   384000),
+    ('1536Kbit/s',  1536000),
+    (   '1Mbit/s',  1000000),
+    (   '5Mbit/s',  5000000),
+    (  '2MByte/s', 16000000),
+    (         '0',        0),
+    (         '5',        5),
+    (      384000,   384000),
+    )
+
+ERROR_RATES = (
+    '1536KBit/s',  # Older versions of dummynet used capital 'B' for bytes.
+    '1Mbyte/s',    # Require capital 'B' for bytes.
+    '5bps',
+    )
+
+
+class TimedTestCase(unittest.TestCase):
+  def assertValuesAlmostEqual(self, expected, actual, tolerance=0.05):
+    """Like the following with nicer default message:
+           assertTrue(expected <= actual + tolerance &&
+                      expected >= actual - tolerance)
+    """
+    delta = tolerance * expected
+    if actual > expected + delta or actual < expected - delta:
+      self.fail('%s is not equal to expected %s +/- %s%%' % (
+              actual, expected, 100 * tolerance))
+
+
+class RateLimitedFileTest(TimedTestCase):
+  def testReadLimitedBasic(self):
+    num_bytes = 1024
+    bps = 384000
+    request_counter = lambda: 1
+    f = StringIO.StringIO(' ' * num_bytes)
+    limited_f = proxyshaper.RateLimitedFile(request_counter, f, bps)
+    start = proxyshaper.TIMER()
+    self.assertEqual(num_bytes, len(limited_f.read()))
+    expected_ms = 8.0 * num_bytes / bps * 1000.0
+    actual_ms = (proxyshaper.TIMER() - start) * 1000.0
+    self.assertValuesAlmostEqual(expected_ms, actual_ms)
+
+  def testReadlineLimitedBasic(self):
+    num_bytes = 1024 * 8 + 512
+    bps = 384000
+    request_counter = lambda: 1
+    f = StringIO.StringIO(' ' * num_bytes)
+    limited_f = proxyshaper.RateLimitedFile(request_counter, f, bps)
+    start = proxyshaper.TIMER()
+    self.assertEqual(num_bytes, len(limited_f.readline()))
+    expected_ms = 8.0 * num_bytes / bps * 1000.0
+    actual_ms = (proxyshaper.TIMER() - start) * 1000.0
+    self.assertValuesAlmostEqual(expected_ms, actual_ms)
+
+  def testReadLimitedSlowedByMultipleRequests(self):
+    num_bytes = 1024
+    bps = 384000
+    request_count = 2
+    request_counter = lambda: request_count
+    f = StringIO.StringIO(' ' * num_bytes)
+    limited_f = proxyshaper.RateLimitedFile(request_counter, f, bps)
+    start = proxyshaper.TIMER()
+    num_read_bytes = limited_f.read()
+    self.assertEqual(num_bytes, len(num_read_bytes))
+    expected_ms = 8.0 * num_bytes / (bps / float(request_count)) * 1000.0
+    actual_ms = (proxyshaper.TIMER() - start) * 1000.0
+    self.assertValuesAlmostEqual(expected_ms, actual_ms)
+
+  def testWriteLimitedBasic(self):
+    num_bytes = 1024 * 10 + 350
+    bps = 384000
+    request_counter = lambda: 1
+    f = StringIO.StringIO()
+    limited_f = proxyshaper.RateLimitedFile(request_counter, f, bps)
+    start = proxyshaper.TIMER()
+    limited_f.write(' ' * num_bytes)
+    self.assertEqual(num_bytes, len(limited_f.getvalue()))
+    expected_ms = 8.0 * num_bytes / bps * 1000.0
+    actual_ms = (proxyshaper.TIMER() - start) * 1000.0
+    self.assertValuesAlmostEqual(expected_ms, actual_ms)
+
+  def testWriteLimitedSlowedByMultipleRequests(self):
+    num_bytes = 1024 * 10
+    bps = 384000
+    request_count = 2
+    request_counter = lambda: request_count
+    f = StringIO.StringIO(' ' * num_bytes)
+    limited_f = proxyshaper.RateLimitedFile(request_counter, f, bps)
+    start = proxyshaper.TIMER()
+    limited_f.write(' ' * num_bytes)
+    self.assertEqual(num_bytes, len(limited_f.getvalue()))
+    expected_ms = 8.0 * num_bytes / (bps / float(request_count)) * 1000.0
+    actual_ms = (proxyshaper.TIMER() - start) * 1000.0
+    self.assertValuesAlmostEqual(expected_ms, actual_ms)
+
+
+class GetBitsPerSecondTest(unittest.TestCase):
+  def testConvertsValidValues(self):
+    for dummynet_option, expected_bps in VALID_RATES:
+      bps = proxyshaper.GetBitsPerSecond(dummynet_option)
+      self.assertEqual(
+          expected_bps, bps, 'Unexpected result for %s: %s != %s' % (
+              dummynet_option, expected_bps, bps))
+
+  def testRaisesOnUnexpectedValues(self):
+    for dummynet_option in ERROR_RATES:
+      self.assertRaises(proxyshaper.BandwidthValueError,
+                        proxyshaper.GetBitsPerSecond, dummynet_option)
+
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/pylintrc b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/pylintrc
new file mode 100644
index 0000000..27c3925
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/pylintrc
@@ -0,0 +1,17 @@
+[MESSAGES CONTROL]
+
+# Disable the message, report, category or checker with the given id(s).
+# TODO(wpr-owners): Reduce this list to as small as possible.
+disable=I0010,I0011,abstract-class-little-used,abstract-class-not-used,anomalous-backslash-in-string,bad-builtin,bad-context-manager,bad-continuation,bad-str-strip-call,broad-except,cell-var-from-loop,deprecated-lambda,deprecated-module,duplicate-code,eval-used,exec-used,fixme,function-redefined,global-statement,interface-not-implemented,invalid-name,locally-enabled,logging-not-lazy,missing-docstring,missing-final-newline,no-init,no-member,no-name-in-module,no-self-use,no-self-use,not-callable,star-args,too-few-public-methods,too-many-ancestors,too-many-arguments,too-many-branches,too-many-function-args,too-many-instance-attributes,too-many-lines,too-many-locals,too-many-public-methods,too-many-return-statements,too-many-statements,useless-else-on-loop,unused-variable,attribute-defined-outside-init,protected-access
+
+
+[REPORTS]
+
+# Don't write out full reports, just messages.
+reports=no
+
+
+[FORMAT]
+
+# We use two spaces for indents, instead of the usual four spaces or tab.
+indent-string='  '
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/replay.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/replay.py
new file mode 100755
index 0000000..f748ef7
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/replay.py
@@ -0,0 +1,563 @@
+#!/usr/bin/env python
+# Copyright 2010 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Replays web pages under simulated network conditions.
+
+Must be run as administrator (sudo).
+
+To record web pages:
+  1. Start the program in record mode.
+     $ sudo ./replay.py --record archive.wpr
+  2. Load the web pages you want to record in a web browser. It is important to
+     clear browser caches before this so that all subresources are requested
+     from the network.
+  3. Kill the process to stop recording.
+
+To replay web pages:
+  1. Start the program in replay mode with a previously recorded archive.
+     $ sudo ./replay.py archive.wpr
+  2. Load recorded pages in a web browser. A 404 will be served for any pages or
+     resources not in the recorded archive.
+
+Network simulation examples:
+  # 128KByte/s uplink bandwidth, 4Mbps/s downlink bandwidth with 100ms RTT time
+  $ sudo ./replay.py --up 128KByte/s --down 4Mbit/s --delay_ms=100 archive.wpr
+
+  # 1% packet loss rate
+  $ sudo ./replay.py --packet_loss_rate=0.01 archive.wpr
+"""
+
+import argparse
+import json
+import logging
+import os
+import socket
+import sys
+import traceback
+
+import customhandlers
+import dnsproxy
+import httparchive
+import httpclient
+import httpproxy
+import net_configs
+import platformsettings
+import rules_parser
+import script_injector
+import servermanager
+import trafficshaper
+
+if sys.version < '2.6':
+  print 'Need Python 2.6 or greater.'
+  sys.exit(1)
+
+
+def configure_logging(log_level_name, log_file_name=None):
+  """Configure logging level and format.
+
+  Args:
+    log_level_name: 'debug', 'info', 'warning', 'error', or 'critical'.
+    log_file_name: a file name
+  """
+  if logging.root.handlers:
+    logging.critical('A logging method (e.g. "logging.warn(...)")'
+                     ' was called before logging was configured.')
+  log_level = getattr(logging, log_level_name.upper())
+  log_format = (
+    '(%(levelname)s) %(asctime)s %(module)s.%(funcName)s:%(lineno)d  '
+    '%(message)s')
+
+
+  logging.basicConfig(level=log_level, format=log_format)
+  logger = logging.getLogger()
+  if log_file_name:
+    fh = logging.FileHandler(log_file_name)
+    fh.setLevel(log_level)
+    fh.setFormatter(logging.Formatter(log_format))
+    logger.addHandler(fh)
+  system_handler = platformsettings.get_system_logging_handler()
+  if system_handler:
+    logger.addHandler(system_handler)
+
+
+def AddDnsForward(server_manager, host):
+  """Forward DNS traffic."""
+  server_manager.Append(platformsettings.set_temporary_primary_nameserver, host)
+
+
+def AddDnsProxy(server_manager, options, host, port, real_dns_lookup,
+                http_archive):
+  dns_filters = []
+  if options.dns_private_passthrough:
+    private_filter = dnsproxy.PrivateIpFilter(real_dns_lookup, http_archive)
+    dns_filters.append(private_filter)
+    server_manager.AppendRecordCallback(private_filter.InitializeArchiveHosts)
+    server_manager.AppendReplayCallback(private_filter.InitializeArchiveHosts)
+  if options.shaping_dns:
+    delay_filter = dnsproxy.DelayFilter(options.record, **options.shaping_dns)
+    dns_filters.append(delay_filter)
+    server_manager.AppendRecordCallback(delay_filter.SetRecordMode)
+    server_manager.AppendReplayCallback(delay_filter.SetReplayMode)
+  server_manager.Append(dnsproxy.DnsProxyServer, host, port,
+                        dns_lookup=dnsproxy.ReplayDnsLookup(host, dns_filters))
+
+
+def AddWebProxy(server_manager, options, host, real_dns_lookup, http_archive):
+  if options.rules_path:
+    with open(options.rules_path) as file_obj:
+      allowed_imports = [
+          name.strip() for name in options.allowed_rule_imports.split(',')]
+      rules = rules_parser.Rules(file_obj, allowed_imports)
+    logging.info('Parsed %s rules:\n%s', options.rules_path, rules)
+  else:
+    rules = rules_parser.Rules()
+  injector = script_injector.GetScriptInjector(options.inject_scripts)
+  custom_handlers = customhandlers.CustomHandlers(options, http_archive)
+  custom_handlers.add_server_manager_handler(server_manager)
+  archive_fetch = httpclient.ControllableHttpArchiveFetch(
+      http_archive, real_dns_lookup,
+      injector,
+      options.diff_unknown_requests, options.record,
+      use_closest_match=options.use_closest_match,
+      scramble_images=options.scramble_images)
+  server_manager.AppendRecordCallback(archive_fetch.SetRecordMode)
+  server_manager.AppendReplayCallback(archive_fetch.SetReplayMode)
+  allow_generate_304 = not options.record
+  server_manager.Append(
+      httpproxy.HttpProxyServer,
+      archive_fetch, custom_handlers, rules,
+      host=host, port=options.port, use_delays=options.use_server_delay,
+      allow_generate_304=allow_generate_304,
+      **options.shaping_http)
+  if options.ssl:
+    if options.should_generate_certs:
+      server_manager.Append(
+          httpproxy.HttpsProxyServer, archive_fetch, custom_handlers, rules,
+          options.https_root_ca_cert_path, host=host, port=options.ssl_port,
+          allow_generate_304=allow_generate_304,
+          use_delays=options.use_server_delay, **options.shaping_http)
+    else:
+      server_manager.Append(
+          httpproxy.SingleCertHttpsProxyServer, archive_fetch,
+          custom_handlers, rules, options.https_root_ca_cert_path, host=host,
+          port=options.ssl_port, use_delays=options.use_server_delay,
+          allow_generate_304=allow_generate_304,
+          **options.shaping_http)
+  if options.http_to_https_port:
+    server_manager.Append(
+        httpproxy.HttpToHttpsProxyServer,
+        archive_fetch, custom_handlers, rules,
+        host=host, port=options.http_to_https_port,
+        use_delays=options.use_server_delay,
+        allow_generate_304=allow_generate_304,
+        **options.shaping_http)
+
+
+def AddTrafficShaper(server_manager, options, host):
+  if options.shaping_dummynet:
+    server_manager.AppendTrafficShaper(
+        trafficshaper.TrafficShaper, host=host,
+        use_loopback=not options.server_mode and host == '127.0.0.1',
+        **options.shaping_dummynet)
+
+
+class OptionsWrapper(object):
+  """Add checks, updates, and methods to option values.
+
+  Example:
+    options, args = arg_parser.parse_args()
+    options = OptionsWrapper(options, arg_parser)  # run checks and updates
+    if options.record and options.HasTrafficShaping():
+       [...]
+  """
+  _TRAFFICSHAPING_OPTIONS = {
+      'down', 'up', 'delay_ms', 'packet_loss_rate', 'init_cwnd', 'net'}
+  _CONFLICTING_OPTIONS = (
+      ('record', ('down', 'up', 'delay_ms', 'packet_loss_rate', 'net',
+                  'spdy', 'use_server_delay')),
+      ('append', ('down', 'up', 'delay_ms', 'packet_loss_rate', 'net',
+                  'use_server_delay')),  # same as --record
+      ('net', ('down', 'up', 'delay_ms')),
+      ('server', ('server_mode',)),
+  )
+
+  def __init__(self, options, parser):
+    self._options = options
+    self._parser = parser
+    self._nondefaults = set([
+        action.dest for action in parser._optionals._actions
+        if getattr(options, action.dest, action.default) is not action.default])
+    self._CheckConflicts()
+    self._CheckValidIp('host')
+    self._CheckFeatureSupport()
+    self._MassageValues()
+
+  def _CheckConflicts(self):
+    """Give an error if mutually exclusive options are used."""
+    for option, bad_options in self._CONFLICTING_OPTIONS:
+      if option in self._nondefaults:
+        for bad_option in bad_options:
+          if bad_option in self._nondefaults:
+            self._parser.error('Option --%s cannot be used with --%s.' %
+                                (bad_option, option))
+
+  def _CheckValidIp(self, name):
+    """Give an error if option |name| is not a valid IPv4 address."""
+    value = getattr(self._options, name)
+    if value:
+      try:
+        socket.inet_aton(value)
+      except Exception:
+        self._parser.error('Option --%s must be a valid IPv4 address.' % name)
+
+  def _CheckFeatureSupport(self):
+    if (self._options.should_generate_certs and
+        not platformsettings.HasSniSupport()):
+      self._parser.error('Option --should_generate_certs requires pyOpenSSL '
+                         '0.13 or greater for SNI support.')
+
+  def _ShapingKeywordArgs(self, shaping_key):
+    """Return the shaping keyword args for |shaping_key|.
+
+    Args:
+      shaping_key: one of 'dummynet', 'dns', 'http'.
+    Returns:
+      {}  # if shaping_key does not apply, or options have default values.
+      {k: v, ...}
+    """
+    kwargs = {}
+    def AddItemIfSet(d, kw_key, opt_key=None):
+      opt_key = opt_key or kw_key
+      if opt_key in self._nondefaults:
+        d[kw_key] = getattr(self, opt_key)
+    if ((self.shaping_type == 'proxy' and shaping_key in ('dns', 'http')) or
+        self.shaping_type == shaping_key):
+      AddItemIfSet(kwargs, 'delay_ms')
+      if shaping_key in ('dummynet', 'http'):
+        AddItemIfSet(kwargs, 'down_bandwidth', opt_key='down')
+        AddItemIfSet(kwargs, 'up_bandwidth', opt_key='up')
+        if shaping_key == 'dummynet':
+          AddItemIfSet(kwargs, 'packet_loss_rate')
+          AddItemIfSet(kwargs, 'init_cwnd')
+        elif self.shaping_type != 'none':
+          if 'packet_loss_rate' in self._nondefaults:
+            logging.warn('Shaping type, %s, ignores --packet_loss_rate=%s',
+                         self.shaping_type, self.packet_loss_rate)
+          if 'init_cwnd' in self._nondefaults:
+            logging.warn('Shaping type, %s, ignores --init_cwnd=%s',
+                         self.shaping_type, self.init_cwnd)
+    return kwargs
+
+  def _MassageValues(self):
+    """Set options that depend on the values of other options."""
+    if self.append and not self.record:
+      self._options.record = True
+    if self.net:
+      self._options.down, self._options.up, self._options.delay_ms = \
+          net_configs.GetNetConfig(self.net)
+      self._nondefaults.update(['down', 'up', 'delay_ms'])
+    if not self.ssl:
+      self._options.https_root_ca_cert_path = None
+    self.shaping_dns = self._ShapingKeywordArgs('dns')
+    self.shaping_http = self._ShapingKeywordArgs('http')
+    self.shaping_dummynet = self._ShapingKeywordArgs('dummynet')
+
+  def __getattr__(self, name):
+    """Make the original option values available."""
+    return getattr(self._options, name)
+
+  def __repr__(self):
+    """Return a json representation of the original options dictionary."""
+    return json.dumps(self._options.__dict__)
+
+  def IsRootRequired(self):
+    """Returns True iff the options require whole program root access."""
+    if self.server:
+      return True
+
+    def IsPrivilegedPort(port):
+      return port and port < 1024
+
+    if IsPrivilegedPort(self.port) or (self.ssl and
+                                       IsPrivilegedPort(self.ssl_port)):
+      return True
+
+    if self.dns_forwarding:
+      if IsPrivilegedPort(self.dns_port):
+        return True
+      if not self.server_mode and self.host == '127.0.0.1':
+        return True
+
+    return False
+
+
+def replay(options, replay_filename):
+  if options.record and sys.version_info < (2, 7, 9):
+    print ('Need Python 2.7.9 or greater for recording mode.\n'
+           'For instructions on how to upgrade Python on Ubuntu 14.04, see:\n'
+           'http://mbless.de/blog/2016/01/09/upgrade-to-python-2711-on-ubuntu-1404-lts.html\n')
+  if options.admin_check and options.IsRootRequired():
+    platformsettings.rerun_as_administrator()
+  configure_logging(options.log_level, options.log_file)
+  server_manager = servermanager.ServerManager(options.record)
+  if options.server:
+    AddDnsForward(server_manager, options.server)
+  else:
+    if options.record:
+      httparchive.HttpArchive.AssertWritable(replay_filename)
+      if options.append and os.path.exists(replay_filename):
+        http_archive = httparchive.HttpArchive.Load(replay_filename)
+        logging.info('Appending to %s (loaded %d existing responses)',
+                     replay_filename, len(http_archive))
+      else:
+        http_archive = httparchive.HttpArchive()
+    else:
+      http_archive = httparchive.HttpArchive.Load(replay_filename)
+      logging.info('Loaded %d responses from %s',
+                   len(http_archive), replay_filename)
+    server_manager.AppendRecordCallback(http_archive.clear)
+
+    ipfw_dns_host = None
+    if options.dns_forwarding or options.shaping_dummynet:
+      # compute the ip/host used for the DNS server and traffic shaping
+      ipfw_dns_host = options.host
+      if not ipfw_dns_host:
+        ipfw_dns_host = platformsettings.get_server_ip_address(
+            options.server_mode)
+
+    real_dns_lookup = dnsproxy.RealDnsLookup(
+        name_servers=[platformsettings.get_original_primary_nameserver()],
+        dns_forwarding=options.dns_forwarding,
+        proxy_host=ipfw_dns_host,
+        proxy_port=options.dns_port)
+    server_manager.AppendRecordCallback(real_dns_lookup.ClearCache)
+
+    if options.dns_forwarding:
+      if not options.server_mode and ipfw_dns_host == '127.0.0.1':
+        AddDnsForward(server_manager, ipfw_dns_host)
+      AddDnsProxy(server_manager, options, ipfw_dns_host, options.dns_port,
+                  real_dns_lookup, http_archive)
+    if options.ssl and options.https_root_ca_cert_path is None:
+      options.https_root_ca_cert_path = os.path.join(os.path.dirname(__file__),
+                                                     'wpr_cert.pem')
+    http_proxy_address = options.host
+    if not http_proxy_address:
+      http_proxy_address = platformsettings.get_httpproxy_ip_address(
+          options.server_mode)
+    AddWebProxy(server_manager, options, http_proxy_address, real_dns_lookup,
+                http_archive)
+    AddTrafficShaper(server_manager, options, ipfw_dns_host)
+
+  exit_status = 0
+  try:
+    server_manager.Run()
+  except KeyboardInterrupt:
+    logging.info('Shutting down.')
+  except (dnsproxy.DnsProxyException,
+          trafficshaper.TrafficShaperException,
+          platformsettings.NotAdministratorError,
+          platformsettings.DnsUpdateError) as e:
+    logging.critical('%s: %s', e.__class__.__name__, e)
+    exit_status = 1
+  except Exception:
+    logging.critical(traceback.format_exc())
+    exit_status = 2
+
+  if options.record:
+    http_archive.Persist(replay_filename)
+    logging.info('Saved %d responses to %s', len(http_archive), replay_filename)
+  return exit_status
+
+
+def GetParser():
+  arg_parser = argparse.ArgumentParser(
+      usage='%(prog)s [options] replay_file',
+      description=__doc__,
+      formatter_class=argparse.RawDescriptionHelpFormatter,
+      epilog='http://code.google.com/p/web-page-replay/')
+
+  arg_parser.add_argument('replay_filename', type=str, help='Replay file',
+                          nargs='?')
+
+  arg_parser.add_argument('-r', '--record', default=False,
+      action='store_true',
+      help='Download real responses and record them to replay_file')
+  arg_parser.add_argument('--append', default=False,
+      action='store_true',
+      help='Append responses to replay_file.')
+  arg_parser.add_argument('-l', '--log_level', default='debug',
+      action='store',
+      type=str,
+      choices=('debug', 'info', 'warning', 'error', 'critical'),
+      help='Minimum verbosity level to log')
+  arg_parser.add_argument('-f', '--log_file', default=None,
+      action='store',
+      type=str,
+      help='Log file to use in addition to writting logs to stderr.')
+
+  network_group = arg_parser.add_argument_group(
+      title='Network Simulation Options',
+      description=('These options configure the network simulation in '
+                   'replay mode'))
+  network_group.add_argument('-u', '--up', default='0',
+      action='store',
+      type=str,
+      help='Upload Bandwidth in [K|M]{bit/s|Byte/s}. Zero means unlimited.')
+  network_group.add_argument('-d', '--down', default='0',
+      action='store',
+      type=str,
+      help='Download Bandwidth in [K|M]{bit/s|Byte/s}. Zero means unlimited.')
+  network_group.add_argument('-m', '--delay_ms', default='0',
+      action='store',
+      type=str,
+      help='Propagation delay (latency) in milliseconds. Zero means no delay.')
+  network_group.add_argument('-p', '--packet_loss_rate', default='0',
+      action='store',
+      type=str,
+      help='Packet loss rate in range [0..1]. Zero means no loss.')
+  network_group.add_argument('-w', '--init_cwnd', default='0',
+      action='store',
+      type=str,
+      help='Set initial cwnd (linux only, requires kernel patch)')
+  network_group.add_argument('--net', default=None,
+      action='store',
+      type=str,
+      choices=net_configs.NET_CONFIG_NAMES,
+      help='Select a set of network options: %s.' % ', '.join(
+          net_configs.NET_CONFIG_NAMES))
+  network_group.add_argument('--shaping_type', default='dummynet',
+      action='store',
+      choices=('dummynet', 'proxy'),
+      help='When shaping is configured (i.e. --up, --down, etc.) decides '
+           'whether to use |dummynet| (default), or |proxy| servers.')
+
+  harness_group = arg_parser.add_argument_group(
+      title='Replay Harness Options',
+      description=('These advanced options configure various aspects '
+                   'of the replay harness'))
+  harness_group.add_argument('-S', '--server', default=None,
+      action='store',
+      type=str,
+      help='IP address of host running "replay.py --server_mode". '
+           'This only changes the primary DNS nameserver to use the given IP.')
+  harness_group.add_argument('-M', '--server_mode', default=False,
+      action='store_true',
+      help='Run replay DNS & http proxies, and trafficshaping on --port '
+           'without changing the primary DNS nameserver. '
+           'Other hosts may connect to this using "replay.py --server" '
+           'or by pointing their DNS to this server.')
+  harness_group.add_argument('-i', '--inject_scripts', default='deterministic.js',
+      action='store',
+      dest='inject_scripts',
+      help='A comma separated list of JavaScript sources to inject in all '
+           'pages. By default a script is injected that eliminates sources '
+           'of entropy such as Date() and Math.random() deterministic. '
+           'CAUTION: Without deterministic.js, many pages will not replay.')
+  harness_group.add_argument('-D', '--no-diff_unknown_requests', default=True,
+      action='store_false',
+      dest='diff_unknown_requests',
+      help='During replay, do not show a diff of unknown requests against '
+           'their nearest match in the archive.')
+  harness_group.add_argument('-C', '--use_closest_match', default=False,
+      action='store_true',
+      dest='use_closest_match',
+      help='During replay, if a request is not found, serve the closest match'
+           'in the archive instead of giving a 404.')
+  harness_group.add_argument('-U', '--use_server_delay', default=False,
+      action='store_true',
+      dest='use_server_delay',
+      help='During replay, simulate server delay by delaying response time to'
+           'requests.')
+  harness_group.add_argument('-I', '--screenshot_dir', default=None,
+      action='store',
+      type=str,
+      help='Save PNG images of the loaded page in the given directory.')
+  harness_group.add_argument('-P', '--no-dns_private_passthrough', default=True,
+      action='store_false',
+      dest='dns_private_passthrough',
+      help='Don\'t forward DNS requests that resolve to private network '
+           'addresses. CAUTION: With this option important services like '
+           'Kerberos will resolve to the HTTP proxy address.')
+  harness_group.add_argument('-x', '--no-dns_forwarding', default=True,
+      action='store_false',
+      dest='dns_forwarding',
+      help='Don\'t forward DNS requests to the local replay server. '
+           'CAUTION: With this option an external mechanism must be used to '
+           'forward traffic to the replay server.')
+  harness_group.add_argument('--host', default=None,
+      action='store',
+      type=str,
+      help='The IP address to bind all servers to. Defaults to 0.0.0.0 or '
+           '127.0.0.1, depending on --server_mode and platform.')
+  harness_group.add_argument('-o', '--port', default=80,
+      action='store',
+      type=int,
+      help='Port number to listen on.')
+  harness_group.add_argument('--ssl_port', default=443,
+      action='store',
+      type=int,
+      help='SSL port number to listen on.')
+  harness_group.add_argument('--http_to_https_port', default=None,
+      action='store',
+      type=int,
+      help='Port on which WPR will listen for HTTP requests that it will send '
+           'along as HTTPS requests.')
+  harness_group.add_argument('--dns_port', default=53,
+      action='store',
+      type=int,
+      help='DNS port number to listen on.')
+  harness_group.add_argument('-c', '--https_root_ca_cert_path', default=None,
+      action='store',
+      type=str,
+      help='Certificate file to use with SSL (gets auto-generated if needed).')
+  harness_group.add_argument('--no-ssl', default=True,
+      action='store_false',
+      dest='ssl',
+      help='Do not setup an SSL proxy.')
+  harness_group.add_argument('--should_generate_certs', default=False,
+      action='store_true',
+      help='Use OpenSSL to generate certificate files for requested hosts.')
+  harness_group.add_argument('--no-admin-check', default=True,
+      action='store_false',
+      dest='admin_check',
+      help='Do not check if administrator access is needed.')
+  harness_group.add_argument('--scramble_images', default=False,
+      action='store_true',
+      dest='scramble_images',
+      help='Scramble image responses.')
+  harness_group.add_argument('--rules_path', default=None,
+      action='store',
+      help='Path of file containing Python rules.')
+  harness_group.add_argument('--allowed_rule_imports', default='rules',
+      action='store',
+      help='A comma-separate list of allowed rule imports, or \'*\' to allow'
+           ' all packages.  Defaults to %(default)s.')
+  return arg_parser
+
+
+def main():
+  arg_parser = GetParser()
+  options = arg_parser.parse_args()
+  options = OptionsWrapper(options, arg_parser)
+
+  if options.server:
+    options.replay_filename = None
+  elif options.replay_filename is None:
+    arg_parser.error('Must specify a replay_file')
+  return replay(options, options.replay_filename)
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/replay_test.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/replay_test.py
new file mode 100755
index 0000000..fa7d8e1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/replay_test.py
@@ -0,0 +1,89 @@
+#!/usr/bin/env python
+# Copyright 2012 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Unit tests for replay.
+
+Usage:
+$ ./replay_test.py
+"""
+
+import replay
+import unittest
+
+
+class MockOptions(dict):
+  """A dict with items that can be accessed as attributes."""
+  def __getattr__(self, name):
+    return self[name]
+
+
+class OptionsWrapperTest(unittest.TestCase):
+
+  def testNoTrafficShapingByDefault(self):
+    parser = replay.GetParser()
+    options = parser.parse_args([])
+    options = replay.OptionsWrapper(options, parser)
+    self.assertEqual({}, options.shaping_dns)
+    self.assertEqual({}, options.shaping_http)
+    self.assertEqual({}, options.shaping_dummynet)
+
+  def testShapingProxyWithoutOptionsGivesEmptySettings(self):
+    parser = replay.GetParser()
+    options = parser.parse_args(['--shaping=proxy'])
+    options = replay.OptionsWrapper(options, parser)
+    self.assertEqual({}, options.shaping_dns)
+    self.assertEqual({}, options.shaping_http)
+    self.assertEqual({}, options.shaping_dummynet)
+
+  def testShapingProxyWithNetOption(self):
+    parser = replay.GetParser()
+    options = parser.parse_args(['--shaping=proxy', '--net=cable'])
+    options = replay.OptionsWrapper(options, parser)
+    expected_http = {
+        'down_bandwidth': '5Mbit/s', 'delay_ms': '28', 'up_bandwidth': '1Mbit/s'
+        }
+    self.assertEqual({'delay_ms': '28'}, options.shaping_dns)
+    self.assertEqual(expected_http, options.shaping_http)
+    self.assertEqual({}, options.shaping_dummynet)
+
+  def testNetOptionUsesDummynetByDefault(self):
+    parser = replay.GetParser()
+    options = parser.parse_args(['--net=cable'])
+    options = replay.OptionsWrapper(options, parser)
+    expected_dummynet = {
+        'down_bandwidth': '5Mbit/s', 'delay_ms': '28', 'up_bandwidth': '1Mbit/s'
+        }
+    self.assertEqual({}, options.shaping_dns)
+    self.assertEqual({}, options.shaping_http)
+    self.assertEqual(expected_dummynet, options.shaping_dummynet)
+
+  def testPacketLossForDummynet(self):
+    parser = replay.GetParser()
+    options = parser.parse_args(['--packet_loss_rate=12'])
+    options = replay.OptionsWrapper(options, parser)
+    self.assertEqual({'packet_loss_rate': '12'}, options.shaping_dummynet)
+
+  def testIgnoredProxyShapingOptions(self):
+    parser = replay.GetParser()
+    options = parser.parse_args(
+        ['--packet_loss_rate=12', '--init_cwnd=10', '--shaping=proxy'])
+    options = replay.OptionsWrapper(options, parser)
+    self.assertEqual({}, options.shaping_dns)
+    self.assertEqual({}, options.shaping_http)
+    self.assertEqual({}, options.shaping_dummynet)
+
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/requirements.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/requirements.txt
new file mode 100644
index 0000000..41b4fa1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/requirements.txt
@@ -0,0 +1 @@
+pyOpenSSL==0.13
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/rules/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/rules/__init__.py
new file mode 100644
index 0000000..3486216
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/rules/__init__.py
@@ -0,0 +1,18 @@
+#!/usr/bin/env python
+# Copyright 2015 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+# Export rules for rules_parser access.
+from rules.log_url import LogUrl
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/rules/log_url.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/rules/log_url.py
new file mode 100644
index 0000000..2f5ab21
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/rules/log_url.py
@@ -0,0 +1,71 @@
+#!/usr/bin/env python
+# Copyright 2015 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging
+import re
+
+from rules import rule
+
+
+class LogUrl(rule.Rule):
+  """Logs the request URL."""
+
+  def __init__(self, url, stop=False):
+    r"""Initializes with a url pattern.
+
+    Args:
+      url: a string regex, e.g. r'example\.com/id=(\d{6})'.
+      stop:  boolean ApplyRule should_stop value, defaults to True.
+    """
+    self._url_re = re.compile(url)
+    self._stop = stop
+
+  def IsType(self, rule_type_name):
+    """Returns True if the name matches this rule."""
+    return rule_type_name == 'log_url'
+
+  def ApplyRule(self, return_value, request, response):
+    """Returns True if logged.
+
+    Args:
+      return_value: the prior log_url rule's return_value (if any).
+      request: the httparchive ArchivedHttpRequest.
+      response: the httparchive ArchivedHttpResponse.
+    Returns:
+      A (should_stop, return_value) tuple, e.g. (False, True).
+    """
+    del response  # unused.
+    url = '%s%s' % (request.host, request.full_path)
+    if not self._url_re.match(url):
+      return False, return_value
+
+    logging.debug('url: %s', url)
+    return self._stop, True
+
+  def __str__(self):
+    return _ToString(self, ('url', self._url_re.pattern),
+                     None if self._stop else ('stop', False))
+
+  def __repr__(self):
+    return str(self)
+
+
+def _ToString(obj, *items):
+  pkg = (obj.__module__[:obj.__module__.rfind('.') + 1]
+         if '.' in obj.__module__ else '')
+  clname = obj.__class__.__name__
+  args = [('%s=r\'%s\'' % item if isinstance(item[1], basestring)
+           else '%s=%s' % item) for item in items if item]
+  return '%s%s(%s)' % (pkg, clname, ', '.join(args))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/rules/rule.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/rules/rule.py
new file mode 100644
index 0000000..816b61b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/rules/rule.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python
+# Copyright 2015 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+class Rule(object):
+  """An optional base class for rule implementations.
+
+  The rule_parser looks for the 'IsType' and 'ApplyRule' methods by name, so
+  rules are not strictly required to extend this class.
+  """
+
+  def IsType(self, rule_type_name):
+    """Returns True if the name matches this rule."""
+    raise NotImplementedError
+
+  def ApplyRule(self, return_value, request, response):
+    """Invokes this rule with the given args.
+
+    Args:
+      return_value: the prior rule's return_value (if any).
+      request: the httparchive ArchivedHttpRequest.
+      response: the httparchive ArchivedHttpResponse, which may be None.
+    Returns:
+      A (should_stop, return_value) tuple.  Typically the request and response
+        are treated as immutable, so it's the caller's job to apply the
+        return_value (e.g., set response fields).
+    """
+    raise NotImplementedError
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/rules_parser.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/rules_parser.py
new file mode 100644
index 0000000..109db6d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/rules_parser.py
@@ -0,0 +1,167 @@
+# Copyright 2015 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+r"""Rules parser.
+
+The input syntax is:
+  [{"comment": ignored_value},
+   {"rule_class_name1": {"arg1": value, "arg2": value, ...}},
+   {"rule_class_name2": {"arg1": value, "arg2": value, ...}},
+   ...]
+E.g.:
+  [{"comment": "this text is ignored"},
+   {"SendStatus": {"url": "example\\.com/ss.*", "status": 204}},
+   {"ModifyUrl": {"url": "(example\\.com)(/.*)", "new_url": "{1}"}}
+  ]
+"""
+
+import json
+import re
+
+
+class Error(Exception):
+  pass
+
+
+class Rules(object):
+
+  """A parsed sequence of Rule objects."""
+
+  def __init__(self, file_obj=None, allowed_imports=None):
+    """Initializes from the given file object.
+
+    Args:
+      file_obj: A file object.
+      allowed_imports: A set of strings, defaults to {'rules'}.
+        Use {'*'} to allow any import path.
+    """
+    if allowed_imports is None:
+      allowed_imports = {'rules'}
+    self._rules = [] if file_obj is None else _Load(file_obj, allowed_imports)
+
+  def Contains(self, rule_type_name):
+    """Returns true if any rule matches the given type name.
+
+    Args:
+      rule_type_name: a string.
+    Returns:
+      True if any rule matches, else False.
+    """
+    return any(rule for rule in self._rules if rule.IsType(rule_type_name))
+
+  def Find(self, rule_type_name):
+    """Returns a _Rule object containing all rules with the given type name.
+
+    Args:
+      rule_type_name: a string.
+    Returns:
+      A callable object that expects two arguments:
+        request: the httparchive ArchivedHttpRequest
+        response: the httparchive ArchivedHttpResponse
+      and returns the rule return_value of the first rule that returns
+      should_stop == True, or the last rule's return_value if all rules returns
+      should_stop == False.
+    """
+    matches = [rule for rule in self._rules if rule.IsType(rule_type_name)]
+    return _Rule(matches)
+
+  def __str__(self):
+    return _ToString(self._rules)
+
+  def __repr__(self):
+    return str(self)
+
+
+class _Rule(object):
+  """Calls a sequence of Rule objects until one returns should_stop."""
+
+  def __init__(self, rules):
+    self._rules = rules
+
+  def __call__(self, request, response):
+    """Calls the rules until one returns should_stop.
+
+    Args:
+      request: the httparchive ArchivedHttpRequest.
+      response: the httparchive ArchivedHttpResponse, which may be None.
+    Returns:
+      The rule return_value of the first rule that returns should_stop == True,
+      or the last rule's return_value if all rules return should_stop == False.
+    """
+    return_value = None
+    for rule in self._rules:
+      should_stop, return_value = rule.ApplyRule(
+          return_value, request, response)
+      if should_stop:
+        break
+    return return_value
+
+  def __str__(self):
+    return _ToString(self._rules)
+
+  def __repr__(self):
+    return str(self)
+
+
+def _ToString(rules):
+  """Formats a sequence of Rule objects into a string."""
+  return '[\n%s\n]' % '\n'.join('%s' % rule for rule in rules)
+
+
+def _Load(file_obj, allowed_imports):
+  """Parses and evaluates all rules in the given file.
+
+  Args:
+    file_obj: a file object.
+    allowed_imports: a sequence of strings, e.g.: {'rules'}.
+  Returns:
+    a list of rules.
+  """
+  rules = []
+  entries = json.load(file_obj)
+  if not isinstance(entries, list):
+    raise Error('Expecting a list, not %s', type(entries))
+  for i, entry in enumerate(entries):
+    if not isinstance(entry, dict):
+      raise Error('%s: Expecting a dict, not %s', i, type(entry))
+    if len(entry) != 1:
+      raise Error('%s: Expecting 1 item, not %d', i, len(entry))
+    name, args = next(entry.iteritems())
+    if not isinstance(name, basestring):
+      raise Error('%s: Expecting a string TYPE, not %s', i, type(name))
+    if not re.match(r'(\w+\.)*\w+$', name):
+      raise Error('%s: Expecting a classname TYPE, not %s', i, name)
+    if name == 'comment':
+      continue
+    if not isinstance(args, dict):
+      raise Error('%s: Expecting a dict ARGS, not %s', i, type(args))
+    fullname = str(name)
+    if '.' not in fullname:
+      fullname = 'rules.%s' % fullname
+
+    modulename, classname = fullname.rsplit('.', 1)
+    if '*' not in allowed_imports and modulename not in allowed_imports:
+      raise Error('%s: Package %r is not in allowed_imports', i, modulename)
+
+    module = __import__(modulename, fromlist=[classname])
+    clazz = getattr(module, classname)
+
+    missing = {s for s in ('IsType', 'ApplyRule') if not hasattr(clazz, s)}
+    if missing:
+      raise Error('%s: %s lacks %s', i, clazz.__name__, ' and '.join(missing))
+
+    rule = clazz(**args)
+
+    rules.append(rule)
+  return rules
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/rules_parser_test.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/rules_parser_test.py
new file mode 100755
index 0000000..bc20d80
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/rules_parser_test.py
@@ -0,0 +1,81 @@
+# Copyright 2015 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Unit tests for rules_parser.  Usage: ./rules_parser_test.py"""
+
+import collections
+import logging
+from StringIO import StringIO
+import unittest
+
+import rules_parser
+
+
+class RuleParserTest(unittest.TestCase):
+
+  @classmethod
+  def setUpClass(cls):
+    if not logging.root.handlers:
+      logging.basicConfig(level=logging.DEBUG,  # Enable log_url stdout.
+                          format='%(asctime)s %(levelname)s %(message)s')
+
+  def testCall(self):
+    my_rules = rules_parser.Rules(StringIO(r'''
+        [{"comment": "ignore me"},
+         {"LogUrl": {"url": "example\\.com/ss.*"}},
+         {"LogUrl": {"url": "example\\.com/blah$"}}]'''))
+    log_url = my_rules.Find('log_url')
+    self.assertEquals(True, log_url(FakeRequest(full_path='/ss'), None))
+    self.assertEquals(True, log_url(FakeRequest(full_path='/ssxxxx'), None))
+    self.assertEquals(True, log_url(FakeRequest(full_path='/blah'), None))
+    self.assertEquals(None, log_url(FakeRequest(full_path='/blahxxx'), None))
+    self.assertEquals(None, log_url(FakeRequest(full_path='/'), None))
+
+  def testImport(self):
+    my_rules = rules_parser.Rules(StringIO(r'''
+        [{"rules.LogUrl": {"url": "example\\.com/ss.*"}}]'''))
+    self.assertTrue(my_rules.Contains('log_url'))
+
+  def testRaises(self):
+    input_pairs = [
+        'bad_json',
+        '123',
+        '{}',
+        '[42]',
+        '[{12:34}]',
+        '[{"a":"b","c":"d"}]',
+        '[{"bad+rule@name":{}}]',
+        '["unallowed.Path":{}]',
+        '["NoSuchRule":{}]',
+        '["LogUrl":"bad"]',
+        '["LogUrl":{}]',
+        '["LogUrl":{"url":123}]',
+        '["LogUrl":{"url":"", "bad_arg":123}]',
+    ]
+    for input_text in input_pairs:
+      self.assertRaises(Exception, rules_parser.Rules, StringIO(input_text))
+
+
+class FakeRequest(collections.namedtuple(
+    'FakeRequest', ('command', 'host', 'full_path', 'request_body',
+                    'headers', 'is_ssl'))):
+
+  def __new__(cls, command='GET', host='example.com', full_path='/',
+              request_body=None, headers=None, is_ssl=False):
+    return super(FakeRequest, cls).__new__(
+        cls, command, host, full_path, request_body, headers or {}, is_ssl)
+
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/run_tests b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/run_tests
new file mode 100755
index 0000000..4f632d0
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/run_tests
@@ -0,0 +1,12 @@
+#!/usr/bin/env python
+import os
+import sys
+
+import test_runner
+
+_WPR_DIR = os.path.dirname(os.path.abspath(__file__))
+
+if __name__ == '__main__':
+  runner = test_runner.TestRunner()
+  runner.AddDirectory(_WPR_DIR)
+  sys.exit(runner.Main())
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/script_injector.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/script_injector.py
new file mode 100644
index 0000000..d845233
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/script_injector.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python
+# Copyright 2013 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Inject javascript into html page source code."""
+
+import datetime
+import logging
+import os
+import re
+import util
+import third_party.jsmin as jsmin
+
+DOCTYPE_RE = re.compile(r'^.{,256}?(<!--.*-->)?.{,256}?<!doctype html>',
+                        re.IGNORECASE | re.DOTALL)
+HTML_RE = re.compile(r'^.{,256}?(<!--.*-->)?.{,256}?<html.*?>',
+                     re.IGNORECASE | re.DOTALL)
+HEAD_RE = re.compile(r'^.{,256}?(<!--.*-->)?.{,256}?<head.*?>',
+                     re.IGNORECASE | re.DOTALL)
+
+# Occurences of this marker in injected scripts will be replaced with
+# recording time in javascripts' Date().toValue() format.  This allows
+# to properly set deterministic date in JS code.  See
+# https://github.com/chromium/web-page-replay/issues/71 for details.
+TIME_SEED_MARKER = '{{WPR_TIME_SEED_TIMESTAMP}}'
+
+
+def GetScriptInjector(scripts):
+  """Loads |scripts| from disk and returns an injector of their content."""
+  lines = []
+  if scripts:
+    if not isinstance(scripts, list):
+      scripts = scripts.split(',')
+    for script in scripts:
+      if os.path.exists(script):
+        with open(script) as f:
+          lines.extend(f.read())
+      elif util.resource_exists(script):
+        lines.extend(util.resource_string(script))
+      else:
+        raise Exception('Script does not exist: %s', script)
+
+  script_template = jsmin.jsmin(''.join(lines), quote_chars="'\"`")
+  def injector(record_time):
+    delta = record_time - datetime.datetime(1970, 1, 1)
+    js_timestamp = \
+        int(delta.total_seconds()) * 1000 + delta.microseconds / 1000
+    return script_template.replace(TIME_SEED_MARKER, str(js_timestamp))
+  return injector
+
+
+def _IsHtmlContent(content):
+  content = content.strip()
+  return content.startswith('<') and content.endswith('>')
+
+
+def InjectScript(text_chunks, content_type, script_to_inject):
+  """Inject |script_to_inject| into |content| if |content_type| is 'text/html'.
+
+  Inject |script_to_inject| into |text_chunks| immediately after <head>,
+  <html> or <!doctype html>, if one of them is found. Otherwise, inject at
+  the beginning.
+
+  Returns:
+    text_chunks, already_injected
+    |text_chunks| is the new content if script is injected, otherwise
+      the original.  If the script was injected, exactly one chunk in
+      |text_chunks| will have changed.
+    |just_injected| indicates if |script_to_inject| was just injected in
+      the content.
+  """
+  if not content_type or content_type != 'text/html':
+    return text_chunks, False
+  content = "".join(text_chunks)
+  if not content or not _IsHtmlContent(content) or script_to_inject in content:
+    return text_chunks, False
+  for regexp in (HEAD_RE, HTML_RE, DOCTYPE_RE):
+    matchobj = regexp.search(content)
+    if matchobj:
+      pos = matchobj.end(0)
+      for i, chunk in enumerate(text_chunks):
+        if pos <= len(chunk):
+          result = text_chunks[:]
+          result[i] = '%s<script>%s</script>%s' % (chunk[0:pos],
+                                                   script_to_inject,
+                                                   chunk[pos:])
+          return result, True
+        pos -= len(chunk)
+  result = text_chunks[:]
+  result[0] = '<script>%s</script>%s' % (script_to_inject,
+                                         text_chunks[0])
+  logging.warning('Inject at the very beginning, because no tag of '
+                  '<head>, <html> or <!doctype html> is found.')
+  return result, True
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/script_injector_test.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/script_injector_test.py
new file mode 100755
index 0000000..2a2ab50
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/script_injector_test.py
@@ -0,0 +1,148 @@
+#!/usr/bin/env python
+# Copyright 2013 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import datetime
+import httparchive
+import mock
+import script_injector
+import unittest
+
+
+LONG_COMMENT = '<!--' + 'comment,' * 200 + '-->'
+COMMENT_OR_NOT = ('', LONG_COMMENT)
+SCRIPT_TO_INJECT = 'var flag = 0;'
+EXPECTED_SCRIPT = '<script>' + SCRIPT_TO_INJECT + '</script>'
+TEXT_HTML = 'text/html'
+TEXT_CSS = 'text/css'
+APPLICATION = 'application/javascript'
+SEPARATOR = httparchive.ArchivedHttpResponse.CHUNK_EDIT_SEPARATOR
+SEPARATORS_OR_NOT = ('', SEPARATOR, SEPARATOR*3)
+
+TEMPLATE_HEAD = """\
+{boundary_at_start}\
+<!doc{boundary_in_doctype}type html>{boundary_after_doctype}\
+<ht{boundary_in_html}ml>{boundary_after_html}\
+<he{boundary_in_head}ad>{injection}{boundary_after_head}\
+</head></html>\
+"""
+TEMPLATE_HTML = """\
+{boundary_at_start}\
+<!doc{boundary_in_doctype}type html>{boundary_after_doctype}\
+<ht{boundary_in_html}ml>{injection}{boundary_after_html}\
+</html>\
+"""
+TEMPLATE_DOCTYPE = """\
+{boundary_at_start}\
+<!doc{boundary_in_doctype}type html>{injection}{boundary_after_doctype}\
+<body></body>\
+"""
+TEMPLATE_RAW = """\
+{boundary_at_start}\
+{injection}<body></body>\
+"""
+NORMAL_TEMPLATES = (TEMPLATE_HEAD, TEMPLATE_HTML,
+                    TEMPLATE_DOCTYPE, TEMPLATE_RAW)
+TEMPLATE_COMMENT = """\
+{comment_before_doctype}<!doctype html>{comment_after_doctype}\
+<html>{comment_after_html}<head>{injection}</head></html>\
+"""
+
+
+def _wrap_inject_script(source, application, script_to_inject):
+  text_chunks = source.split(SEPARATOR)
+  text_chunks, just_injected = script_injector.InjectScript(
+      text_chunks, application, script_to_inject)
+  result = SEPARATOR.join(text_chunks)
+  return result, just_injected
+
+
+class ScriptInjectorTest(unittest.TestCase):
+
+  def _assert_no_injection(self, source, application):
+    new_source, just_injected = _wrap_inject_script(
+        source, application, SCRIPT_TO_INJECT)
+    self.assertEqual(new_source, source)
+    self.assertFalse(just_injected)
+
+  def _assert_successful_injection(self, template):
+    source, just_injected = _wrap_inject_script(
+        template.format(injection=''), TEXT_HTML, SCRIPT_TO_INJECT)
+    self.assertEqual(source, template.format(injection=EXPECTED_SCRIPT))
+    self.assertTrue(just_injected)
+
+  def test_unsupported_content_type(self):
+    self._assert_no_injection('abc', TEXT_CSS)
+    self._assert_no_injection('abc', APPLICATION)
+
+  def test_empty_content_as_already_injected(self):
+    self._assert_no_injection('', TEXT_HTML)
+
+  def test_non_html_content_with_html_content_type(self):
+    self._assert_no_injection('{"test": 1"}', TEXT_HTML)
+
+  def test_already_injected(self):
+    parameters = {'injection': SCRIPT_TO_INJECT}
+    for template in NORMAL_TEMPLATES:
+      for parameters['boundary_at_start'] in SEPARATORS_OR_NOT:
+        for parameters['boundary_in_doctype'] in SEPARATORS_OR_NOT:
+          for parameters['boundary_after_doctype'] in SEPARATORS_OR_NOT:
+            for parameters['boundary_in_html'] in SEPARATORS_OR_NOT:
+              for parameters['boundary_after_html'] in SEPARATORS_OR_NOT:
+                for parameters['boundary_in_head'] in SEPARATORS_OR_NOT:
+                  for parameters['boundary_after_head'] in SEPARATORS_OR_NOT:
+                    source = template.format(**parameters)
+                    self._assert_no_injection(source, TEXT_HTML)
+
+  def test_normal(self):
+    parameters = {'injection': '{injection}'}
+    for template in NORMAL_TEMPLATES:
+      for parameters['boundary_at_start'] in SEPARATORS_OR_NOT:
+        for parameters['boundary_in_doctype'] in SEPARATORS_OR_NOT:
+          for parameters['boundary_after_doctype'] in SEPARATORS_OR_NOT:
+            for parameters['boundary_in_html'] in SEPARATORS_OR_NOT:
+              for parameters['boundary_after_html'] in SEPARATORS_OR_NOT:
+                for parameters['boundary_in_head'] in SEPARATORS_OR_NOT:
+                  for parameters['boundary_after_head'] in SEPARATORS_OR_NOT:
+                    template = template.format(**parameters)
+                    self._assert_successful_injection(template)
+
+  def test_comments(self):
+    parameters = {'injection': '{injection}'}
+    for parameters['comment_before_doctype'] in COMMENT_OR_NOT:
+      for parameters['comment_after_doctype'] in COMMENT_OR_NOT:
+        for parameters['comment_after_html'] in COMMENT_OR_NOT:
+          template = TEMPLATE_COMMENT.format(**parameters)
+          self._assert_successful_injection(template)
+
+  @mock.patch('script_injector.os.path.exists', return_value=True)
+  @mock.patch('script_injector.open',
+              mock.mock_open(read_data='var time_seed = 123;'))
+  def test_injection_function(self, _):
+    injector = script_injector.GetScriptInjector('to_inject.js')
+    self.assertEqual('var time_seed=123;',
+                     injector(datetime.datetime.utcnow()))
+
+  @mock.patch('script_injector.os.path.exists', return_value=True)
+  @mock.patch('script_injector.open',
+              mock.mock_open(
+                  read_data='var time_seed = {{WPR_TIME_SEED_TIMESTAMP}};'))
+  def test_time_seed_replacement(self, _):
+    date = datetime.datetime(2016, 11, 17)
+    injector = script_injector.GetScriptInjector('date.js')
+    self.assertEqual('var time_seed=1479340800000;', injector(date))
+
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/servermanager.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/servermanager.py
new file mode 100644
index 0000000..8bb9b3a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/servermanager.py
@@ -0,0 +1,137 @@
+#!/usr/bin/env python
+# Copyright 2011 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Control "replay.py --server_mode" (e.g. switch from record to replay)."""
+
+import sys
+import time
+
+class ServerManager(object):
+  """Run servers until is removed or an exception is raised.
+
+  Servers start in the order they are appended and stop in the
+  opposite order. Servers are started by calling the initializer
+  passed to ServerManager.Append() and by calling __enter__(). Once an
+  server's initializer is called successfully, the __exit__() function
+  is guaranteed to be called when ServerManager.Run() completes.
+  """
+
+  def __init__(self, is_record_mode):
+    """Initialize a server manager."""
+    self.initializers = []
+    self.record_callbacks = []
+    self.replay_callbacks = []
+    self.traffic_shapers = []
+    self.is_record_mode = is_record_mode
+    self.should_exit = False
+
+  def Append(self, initializer, *init_args, **init_kwargs):
+    """Append a server to the end of the list to run.
+
+    Servers start in the order they are appended and stop in the
+    opposite order.
+
+    Args:
+      initializer: a function that returns a server instance.
+          A server needs to implement the with-statement interface.
+      init_args: positional arguments for the initializer.
+      init_args: keyword arguments for the initializer.
+    """
+    self.initializers.append((initializer, init_args, init_kwargs))
+
+  def AppendTrafficShaper(self, initializer, *init_args, **init_kwargs):
+    """Append a traffic shaper to the end of the list to run.
+
+    Args:
+      initializer: a function that returns a server instance.
+          A server needs to implement the with-statement interface.
+      init_args: positional arguments for the initializer.
+      init_args: keyword arguments for the initializer.
+    """
+    self.traffic_shapers.append((initializer, init_args, init_kwargs))
+
+  def AppendRecordCallback(self, func):
+    """Append a function to the list to call when switching to record mode.
+
+    Args:
+      func: a function that takes no arguments and returns no value.
+    """
+    self.record_callbacks.append(func)
+
+  def AppendReplayCallback(self, func):
+    """Append a function to the list to call when switching to replay mode.
+
+    Args:
+      func: a function that takes no arguments and returns no value.
+    """
+    self.replay_callbacks.append(func)
+
+  def IsRecordMode(self):
+    """Call all the functions that have been registered to enter replay mode."""
+    return self.is_record_mode
+
+  def SetRecordMode(self):
+    """Call all the functions that have been registered to enter record mode."""
+    self.is_record_mode = True
+    for record_func in self.record_callbacks:
+      record_func()
+
+  def SetReplayMode(self):
+    """Call all the functions that have been registered to enter replay mode."""
+    self.is_record_mode = False
+    for replay_func in self.replay_callbacks:
+      replay_func()
+
+  def Run(self):
+    """Create the servers and loop.
+
+    The loop quits if a server raises an exception.
+
+    Raises:
+      any exception raised by the servers
+    """
+    server_exits = []
+    server_ports = []
+    exception_info = (None, None, None)
+    try:
+      for initializer, init_args, init_kwargs in self.initializers:
+        server = initializer(*init_args, **init_kwargs)
+        if server:
+          server_exits.insert(0, server.__exit__)
+          server.__enter__()
+          if hasattr(server, 'server_port'):
+            server_ports.append(server.server_port)
+      for initializer, init_args, init_kwargs in self.traffic_shapers:
+        init_kwargs['ports'] = server_ports
+        shaper = initializer(*init_args, **init_kwargs)
+        if server:
+          server_exits.insert(0, shaper.__exit__)
+          shaper.__enter__()
+      while True:
+        time.sleep(1)
+        if self.should_exit:
+          break
+    except Exception:
+      exception_info = sys.exc_info()
+    finally:
+      for server_exit in server_exits:
+        try:
+          if server_exit(*exception_info):
+            exception_info = (None, None, None)
+        except Exception:
+          exception_info = sys.exc_info()
+      if exception_info != (None, None, None):
+        # pylint: disable=raising-bad-type
+        raise exception_info[0], exception_info[1], exception_info[2]
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/setup.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/setup.py
new file mode 100644
index 0000000..4a177be
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/setup.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python
+# Copyright 2012 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Creates a distributable python package.
+
+Creating new packages:
+  1. Generate the package, dist/webpagereplay-X.X.tar.gz:
+       python setup.py sdist
+  2. Upload the package file to the following:
+       http://code.google.com/p/web-page-replay/downloads/entry
+
+Installing packages:
+  $ easy_install http://web-page-replay.googlecode.com/files/webpagereplay-X.X.tar.gz
+  - The replay and httparchive commands are now on your PATH.
+"""
+
+import setuptools
+
+setuptools.setup(
+    name='webpagereplay',
+    version='1.1.2',
+    description='Record and replay web content',
+    author='Web Page Replay Project Authors',
+    author_email='web-page-replay-dev@googlegroups.com',
+    url='http://code.google.com/p/web-page-replay/',
+    license='Apache License 2.0',
+    install_requires=['dnspython>=1.8'],
+    packages=[
+        '',
+        'third_party',
+        'third_party.ipaddr'
+        ],
+    package_dir={'': '.'},
+    package_data={
+        '': ['*.js', '*.txt', 'COPYING', 'LICENSE'],
+        },
+    entry_points={
+        'console_scripts': [
+            'httparchive = httparchive:main',
+            'replay = replay:main',
+            ]
+        },
+    )
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/sslproxy.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/sslproxy.py
new file mode 100755
index 0000000..06bb601
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/sslproxy.py
@@ -0,0 +1,89 @@
+# Copyright 2014 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Extends BaseHTTPRequestHandler with SSL certificate generation."""
+
+import logging
+import socket
+
+import certutils
+
+
+
+def _SetUpUsingDummyCert(handler):
+  """Sets up connection providing the certificate to the client.
+
+  This method handles Server Name Indication (SNI) using dummy certs.
+
+  Args:
+    handler: an instance of BaseHTTPServer.BaseHTTPRequestHandler that is used
+      by some instance of  BaseHTTPServer.HTTPServer.
+  """
+  # One of: One of SSLv2_METHOD, SSLv3_METHOD, SSLv23_METHOD, or TLSv1_METHOD
+  context = certutils.get_ssl_context()
+  def handle_servername(connection):
+    """A SNI callback that happens during do_handshake()."""
+    try:
+      host = connection.get_servername()
+      if host:
+        cert_str = (
+            handler.server.get_certificate(host))
+        new_context = certutils.get_ssl_context()
+        cert = certutils.load_cert(cert_str)
+        new_context.use_certificate(cert)
+        new_context.use_privatekey_file(handler.server.ca_cert_path)
+        connection.set_context(new_context)
+        return new_context
+      # else: fail with 'no shared cipher'
+    except Exception, e:
+      # Do not leak any exceptions or else openssl crashes.
+      logging.error('Exception in SNI handler: %s', e)
+
+  context.set_tlsext_servername_callback(handle_servername)
+  handler.connection = certutils.get_ssl_connection(context, handler.connection)
+  handler.connection.set_accept_state()
+  try:
+    handler.connection.do_handshake()
+  except certutils.Error, v:
+    host = handler.connection.get_servername()
+    if not host:
+      logging.error('Dropping request without SNI')
+      return ''
+    raise certutils.Error('SSL handshake error %s: %s' % (host, str(v)))
+
+  # Re-wrap the read/write streams with our new connection.
+  handler.rfile = socket._fileobject(handler.connection, 'rb', handler.rbufsize,
+                                  close=False)
+  handler.wfile = socket._fileobject(handler.connection, 'wb', handler.wbufsize,
+                                  close=False)
+
+
+def wrap_handler(handler_class):
+  """Wraps a BaseHTTPHandler with SSL MITM certificates."""
+  if certutils.openssl_import_error:
+    # pylint: disable=raising-bad-type
+    raise certutils.openssl_import_error
+
+  class WrappedHandler(handler_class):
+
+    def setup(self):
+      handler_class.setup(self)
+      _SetUpUsingDummyCert(self)
+
+    def finish(self):
+      handler_class.finish(self)
+      self.connection.shutdown()
+      self.connection.close()
+
+  return WrappedHandler
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/sslproxy_test.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/sslproxy_test.py
new file mode 100644
index 0000000..faea0bd
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/sslproxy_test.py
@@ -0,0 +1,194 @@
+# Copyright 2014 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Test routines to generate dummy certificates."""
+
+import BaseHTTPServer
+import shutil
+import signal
+import socket
+import tempfile
+import threading
+import time
+import unittest
+
+import certutils
+import sslproxy
+
+
+class Client(object):
+
+  def __init__(self, ca_cert_path, verify_cb, port, host_name='foo.com',
+               host='localhost'):
+    self.host_name = host_name
+    self.verify_cb = verify_cb
+    self.ca_cert_path = ca_cert_path
+    self.port = port
+    self.host_name = host_name
+    self.host = host
+    self.connection = None
+
+  def run_request(self):
+    context = certutils.get_ssl_context()
+    context.set_verify(certutils.VERIFY_PEER, self.verify_cb)  # Demand a cert
+    context.use_certificate_file(self.ca_cert_path)
+    context.load_verify_locations(self.ca_cert_path)
+
+    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    self.connection = certutils.get_ssl_connection(context, s)
+    self.connection.connect((self.host, self.port))
+    self.connection.set_tlsext_host_name(self.host_name)
+
+    try:
+      self.connection.send('\r\n\r\n')
+    finally:
+      self.connection.shutdown()
+      self.connection.close()
+
+
+class Handler(BaseHTTPServer.BaseHTTPRequestHandler):
+  protocol_version = 'HTTP/1.1'  # override BaseHTTPServer setting
+
+  def handle_one_request(self):
+    """Handle a single HTTP request."""
+    self.raw_requestline = self.rfile.readline(65537)
+
+
+class WrappedErrorHandler(Handler):
+  """Wraps handler to verify expected sslproxy errors are being raised."""
+
+  def setup(self):
+    Handler.setup(self)
+    try:
+      sslproxy._SetUpUsingDummyCert(self)
+    except certutils.Error:
+      self.server.error_function = certutils.Error
+
+  def finish(self):
+    Handler.finish(self)
+    self.connection.shutdown()
+    self.connection.close()
+
+
+class DummyArchive(object):
+
+  def __init__(self):
+    pass
+
+
+class DummyFetch(object):
+
+  def __init__(self):
+    self.http_archive = DummyArchive()
+
+
+class Server(BaseHTTPServer.HTTPServer):
+  """SSL server."""
+
+  def __init__(self, ca_cert_path, use_error_handler=False, port=0,
+               host='localhost'):
+    self.ca_cert_path = ca_cert_path
+    with open(ca_cert_path, 'r') as ca_file:
+      self.ca_cert_str = ca_file.read()
+    self.http_archive_fetch = DummyFetch()
+    if use_error_handler:
+      self.HANDLER = WrappedErrorHandler
+    else:
+      self.HANDLER = sslproxy.wrap_handler(Handler)
+    try:
+      BaseHTTPServer.HTTPServer.__init__(self, (host, port), self.HANDLER)
+    except Exception, e:
+      raise RuntimeError('Could not start HTTPSServer on port %d: %s'
+                         % (port, e))
+
+  def __enter__(self):
+    thread = threading.Thread(target=self.serve_forever)
+    thread.daemon = True
+    thread.start()
+    return self
+
+  def cleanup(self):
+    try:
+      self.shutdown()
+    except KeyboardInterrupt:
+      pass
+
+  def __exit__(self, type_, value_, traceback_):
+    self.cleanup()
+
+  def get_certificate(self, host):
+    return certutils.generate_cert(self.ca_cert_str, '', host)
+
+
+class TestClient(unittest.TestCase):
+  _temp_dir = None
+
+  def setUp(self):
+    self._temp_dir = tempfile.mkdtemp(prefix='sslproxy_', dir='/tmp')
+    self.ca_cert_path = self._temp_dir + 'testCA.pem'
+    self.cert_path = self._temp_dir + 'testCA-cert.cer'
+    self.wrong_ca_cert_path = self._temp_dir + 'wrong.pem'
+    self.wrong_cert_path = self._temp_dir + 'wrong-cert.cer'
+
+    # Write both pem and cer files for certificates
+    certutils.write_dummy_ca_cert(*certutils.generate_dummy_ca_cert(),
+                                  cert_path=self.ca_cert_path)
+    certutils.write_dummy_ca_cert(*certutils.generate_dummy_ca_cert(),
+                                  cert_path=self.ca_cert_path)
+
+  def tearDown(self):
+    if self._temp_dir:
+      shutil.rmtree(self._temp_dir)
+
+  def verify_cb(self, conn, cert, errnum, depth, ok):
+    """A callback that verifies the certificate authentication worked.
+
+    Args:
+      conn: Connection object
+      cert: x509 object
+      errnum: possible error number
+      depth: error depth
+      ok: 1 if the authentication worked 0 if it didnt.
+    Returns:
+      1 or 0 depending on if the verification worked
+    """
+    self.assertFalse(cert.has_expired())
+    self.assertGreater(time.strftime('%Y%m%d%H%M%SZ', time.gmtime()),
+                       cert.get_notBefore())
+    return ok
+
+  def test_no_host(self):
+    with Server(self.ca_cert_path) as server:
+      c = Client(self.cert_path, self.verify_cb, server.server_port, '')
+      self.assertRaises(certutils.Error, c.run_request)
+
+  def test_client_connection(self):
+    with Server(self.ca_cert_path) as server:
+      c = Client(self.cert_path, self.verify_cb, server.server_port, 'foo.com')
+      c.run_request()
+
+      c = Client(self.cert_path, self.verify_cb, server.server_port,
+                 'random.host')
+      c.run_request()
+
+  def test_wrong_cert(self):
+    with Server(self.ca_cert_path, True) as server:
+      c = Client(self.wrong_cert_path, self.verify_cb, server.server_port,
+                 'foo.com')
+      self.assertRaises(certutils.Error, c.run_request)
+
+
+if __name__ == '__main__':
+  signal.signal(signal.SIGINT, signal.SIG_DFL)  # Exit on Ctrl-C
+  unittest.main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/test_runner.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/test_runner.py
new file mode 100644
index 0000000..c8ca89f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/test_runner.py
@@ -0,0 +1,78 @@
+#!/usr/bin/env python
+# Copyright (c) 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import unittest
+import sys
+import os
+import optparse
+
+__all__ = []
+
+def FilterSuite(suite, predicate):
+  new_suite = suite.__class__()
+
+  for x in suite:
+    if isinstance(x, unittest.TestSuite):
+      subsuite = FilterSuite(x, predicate)
+      if subsuite.countTestCases() == 0:
+        continue
+
+      new_suite.addTest(subsuite)
+      continue
+
+    assert isinstance(x, unittest.TestCase)
+    if predicate(x):
+      new_suite.addTest(x)
+
+  return new_suite
+
+class _TestLoader(unittest.TestLoader):
+  def __init__(self, *args):
+    super(_TestLoader, self).__init__(*args)
+    self.discover_calls = []
+
+  def loadTestsFromModule(self, module, use_load_tests=True):
+    if module.__file__ != __file__:
+      return super(_TestLoader, self).loadTestsFromModule(
+          module, use_load_tests)
+
+    suite = unittest.TestSuite()
+    for discover_args in self.discover_calls:
+      subsuite = self.discover(*discover_args)
+      suite.addTest(subsuite)
+    return suite
+
+class _RunnerImpl(unittest.TextTestRunner):
+  def __init__(self, filters):
+    super(_RunnerImpl, self).__init__(verbosity=2)
+    self.filters = filters
+
+  def ShouldTestRun(self, test):
+    return not self.filters or any(name in test.id() for name in self.filters)
+
+  def run(self, suite):
+    filtered_test = FilterSuite(suite, self.ShouldTestRun)
+    return super(_RunnerImpl, self).run(filtered_test)
+
+
+class TestRunner(object):
+  def __init__(self):
+    self._loader = _TestLoader()
+
+  def AddDirectory(self, dir_path, test_file_pattern="*test.py"):
+    assert os.path.isdir(dir_path)
+
+    self._loader.discover_calls.append((dir_path, test_file_pattern, dir_path))
+
+  def Main(self, argv=None):
+    if argv is None:
+      argv = sys.argv
+
+    parser = optparse.OptionParser()
+    options, args = parser.parse_args(argv[1:])
+
+    runner = _RunnerImpl(filters=args)
+    return unittest.main(module=__name__, argv=[sys.argv[0]],
+                         testLoader=self._loader,
+                         testRunner=runner)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/test_utils.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/test_utils.py
new file mode 100644
index 0000000..b76750e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/test_utils.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import unittest
+import urllib2
+
+
+def _IsInternetOn():
+  try:
+    urllib2.urlopen('https://example.com', timeout=10)
+    return True
+  except urllib2.URLError as err:
+    return False
+
+
+class RealNetworkFetchTest(unittest.TestCase):
+  def setUp(self):
+    if not _IsInternetOn():
+      self.skipTest('No internet, skip test')
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/__init__.py
new file mode 100644
index 0000000..ea60fc8
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/__init__.py
@@ -0,0 +1,38 @@
+# Copyright 2010 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import sys
+
+try:
+    __file__
+except NameError:
+    __file__ = sys.argv[0]
+third_party_dir = os.path.dirname(os.path.abspath(__file__))
+ipaddr_dir = os.path.join(third_party_dir, "ipaddr")
+sys.path.append(ipaddr_dir)  # workaround for no __init__.py
+import ipaddr
+
+# Modules in dns/ import sibling modules by "import dns/xxx", but
+# some platform has dns/ in global site-packages directory so we need to raise
+# the precedence of local search path (crbug/493869).
+# The implementation here preloads all dns/ modules into this package so clients
+# don't need to worry about import path issue.
+# An easier solution might be modify dns/ modules to use relative path, but I
+# tried not to touch third_party lib for now.
+sys.path.insert(0, third_party_dir)
+from dns import __all__ as all_dns_modules
+all_dns_modules = ['dns.' + m for m in  all_dns_modules]
+map(__import__, all_dns_modules)
+sys.path.pop(0)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/LICENSE b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/LICENSE
new file mode 100644
index 0000000..633c18c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/LICENSE
@@ -0,0 +1,14 @@
+Copyright (C) 2001-2003 Nominum, Inc.
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose with or without fee is hereby granted,
+provided that the above copyright notice and this permission notice
+appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/README.web-page-replay b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/README.web-page-replay
new file mode 100644
index 0000000..6d445fe
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/README.web-page-replay
@@ -0,0 +1,12 @@
+Name: A DNS toolkit for Python
+Short Name: dnspython
+URL: http://www.dnspython.org/
+Version: 1.8.0 (found in ./version.py)
+License: ISC
+License File: LICENSE
+
+Description:
+Used by Web Page Replay's dnsproxy module to create and handle dns queries.
+
+Local Modifications:
+None.
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/__init__.py
new file mode 100644
index 0000000..5ad5737
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/__init__.py
@@ -0,0 +1,52 @@
+# Copyright (C) 2003-2007, 2009 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""dnspython DNS toolkit"""
+
+__all__ = [
+    'dnssec',
+    'e164',
+    'edns',
+    'entropy',
+    'exception',
+    'flags',
+    'inet',
+    'ipv4',
+    'ipv6',
+    'message',
+    'name',
+    'namedict',
+    'node',
+    'opcode',
+    'query',
+    'rcode',
+    'rdata',
+    'rdataclass',
+    'rdataset',
+    'rdatatype',
+    'renderer',
+    'resolver',
+    'reversename',
+    'rrset',
+    'set',
+    'tokenizer',
+    'tsig',
+    'tsigkeyring',
+    'ttl',
+    'rdtypes',
+    'update',
+    'version',
+    'zone',
+]
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/dnssec.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/dnssec.py
new file mode 100644
index 0000000..54fd78d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/dnssec.py
@@ -0,0 +1,72 @@
+# Copyright (C) 2003-2007, 2009 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""Common DNSSEC-related functions and constants."""
+
+RSAMD5 = 1
+DH = 2
+DSA = 3
+ECC = 4
+RSASHA1 = 5
+DSANSEC3SHA1 = 6
+RSASHA1NSEC3SHA1 = 7
+RSASHA256 = 8
+RSASHA512 = 10
+INDIRECT = 252
+PRIVATEDNS = 253
+PRIVATEOID = 254
+
+_algorithm_by_text = {
+    'RSAMD5' : RSAMD5,
+    'DH' : DH,
+    'DSA' : DSA,
+    'ECC' : ECC,
+    'RSASHA1' : RSASHA1,
+    'DSANSEC3SHA1' : DSANSEC3SHA1,
+    'RSASHA1NSEC3SHA1' : RSASHA1NSEC3SHA1,
+    'RSASHA256' : RSASHA256,
+    'RSASHA512' : RSASHA512,
+    'INDIRECT' : INDIRECT,
+    'PRIVATEDNS' : PRIVATEDNS,
+    'PRIVATEOID' : PRIVATEOID,
+    }
+
+# We construct the inverse mapping programmatically to ensure that we
+# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that
+# would cause the mapping not to be true inverse.
+
+_algorithm_by_value = dict([(y, x) for x, y in _algorithm_by_text.iteritems()])
+
+class UnknownAlgorithm(Exception):
+    """Raised if an algorithm is unknown."""
+    pass
+
+def algorithm_from_text(text):
+    """Convert text into a DNSSEC algorithm value
+    @rtype: int"""
+    
+    value = _algorithm_by_text.get(text.upper())
+    if value is None:
+        value = int(text)
+    return value
+
+def algorithm_to_text(value):
+    """Convert a DNSSEC algorithm value to text
+    @rtype: string"""
+    
+    text = _algorithm_by_value.get(value)
+    if text is None:
+        text = str(value)
+    return text
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/e164.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/e164.py
new file mode 100644
index 0000000..d8f71ec
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/e164.py
@@ -0,0 +1,79 @@
+# Copyright (C) 2006, 2007, 2009 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""DNS E.164 helpers
+
+@var public_enum_domain: The DNS public ENUM domain, e164.arpa.
+@type public_enum_domain: dns.name.Name object
+"""
+
+import dns.exception
+import dns.name
+import dns.resolver
+
+public_enum_domain = dns.name.from_text('e164.arpa.')
+
+def from_e164(text, origin=public_enum_domain):
+    """Convert an E.164 number in textual form into a Name object whose
+    value is the ENUM domain name for that number.
+    @param text: an E.164 number in textual form.
+    @type text: str
+    @param origin: The domain in which the number should be constructed.
+    The default is e164.arpa.
+    @type: dns.name.Name object or None
+    @rtype: dns.name.Name object
+    """
+    parts = [d for d in text if d.isdigit()]
+    parts.reverse()
+    return dns.name.from_text('.'.join(parts), origin=origin)
+
+def to_e164(name, origin=public_enum_domain, want_plus_prefix=True):
+    """Convert an ENUM domain name into an E.164 number.
+    @param name: the ENUM domain name.
+    @type name: dns.name.Name object.
+    @param origin: A domain containing the ENUM domain name.  The
+    name is relativized to this domain before being converted to text.
+    @type: dns.name.Name object or None
+    @param want_plus_prefix: if True, add a '+' to the beginning of the
+    returned number.
+    @rtype: str
+    """
+    if not origin is None:
+        name = name.relativize(origin)
+    dlabels = [d for d in name.labels if (d.isdigit() and len(d) == 1)]
+    if len(dlabels) != len(name.labels):
+        raise dns.exception.SyntaxError('non-digit labels in ENUM domain name')
+    dlabels.reverse()
+    text = ''.join(dlabels)
+    if want_plus_prefix:
+        text = '+' + text
+    return text
+
+def query(number, domains, resolver=None):
+    """Look for NAPTR RRs for the specified number in the specified domains.
+
+    e.g. lookup('16505551212', ['e164.dnspython.org.', 'e164.arpa.'])
+    """
+    if resolver is None:
+        resolver = dns.resolver.get_default_resolver()
+    for domain in domains:
+        if isinstance(domain, (str, unicode)):
+            domain = dns.name.from_text(domain)
+        qname = dns.e164.from_e164(number, domain)
+        try:
+            return resolver.query(qname, 'NAPTR')
+        except dns.resolver.NXDOMAIN:
+            pass
+    raise dns.resolver.NXDOMAIN
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/edns.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/edns.py
new file mode 100644
index 0000000..1731ced
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/edns.py
@@ -0,0 +1,142 @@
+# Copyright (C) 2009 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""EDNS Options"""
+
+NSID = 3
+
+class Option(object):
+    """Base class for all EDNS option types.
+    """
+
+    def __init__(self, otype):
+        """Initialize an option.
+        @param rdtype: The rdata type
+        @type rdtype: int
+        """
+        self.otype = otype
+
+    def to_wire(self, file):
+        """Convert an option to wire format.
+        """
+        raise NotImplementedError
+
+    def from_wire(cls, otype, wire, current, olen):
+        """Build an EDNS option object from wire format
+
+        @param otype: The option type
+        @type otype: int
+        @param wire: The wire-format message
+        @type wire: string
+        @param current: The offet in wire of the beginning of the rdata.
+        @type current: int
+        @param olen: The length of the wire-format option data
+        @type olen: int
+        @rtype: dns.ends.Option instance"""
+        raise NotImplementedError
+
+    from_wire = classmethod(from_wire)
+
+    def _cmp(self, other):
+        """Compare an ENDS option with another option of the same type.
+        Return < 0 if self < other, 0 if self == other, and > 0 if self > other.
+        """
+        raise NotImplementedError
+
+    def __eq__(self, other):
+        if not isinstance(other, Option):
+            return False
+        if self.otype != other.otype:
+            return False
+        return self._cmp(other) == 0
+
+    def __ne__(self, other):
+        if not isinstance(other, Option):
+            return False
+        if self.otype != other.otype:
+            return False
+        return self._cmp(other) != 0
+
+    def __lt__(self, other):
+        if not isinstance(other, Option) or \
+               self.otype != other.otype:
+            return NotImplemented
+        return self._cmp(other) < 0
+
+    def __le__(self, other):
+        if not isinstance(other, Option) or \
+               self.otype != other.otype:
+            return NotImplemented
+        return self._cmp(other) <= 0
+
+    def __ge__(self, other):
+        if not isinstance(other, Option) or \
+               self.otype != other.otype:
+            return NotImplemented
+        return self._cmp(other) >= 0
+
+    def __gt__(self, other):
+        if not isinstance(other, Option) or \
+               self.otype != other.otype:
+            return NotImplemented
+        return self._cmp(other) > 0
+
+
+class GenericOption(Option):
+    """Generate Rdata Class
+
+    This class is used for EDNS option types for which we have no better
+    implementation.
+    """
+
+    def __init__(self, otype, data):
+        super(GenericOption, self).__init__(otype)
+        self.data = data
+
+    def to_wire(self, file):
+        file.write(self.data)
+
+    def from_wire(cls, otype, wire, current, olen):
+        return cls(otype, wire[current : current + olen])
+
+    from_wire = classmethod(from_wire)
+
+    def _cmp(self, other):
+	return cmp(self.data, other.data)
+
+_type_to_class = {
+}
+
+def get_option_class(otype):
+    cls = _type_to_class.get(otype)
+    if cls is None:
+        cls = GenericOption
+    return cls
+
+def option_from_wire(otype, wire, current, olen):
+    """Build an EDNS option object from wire format
+
+    @param otype: The option type
+    @type otype: int
+    @param wire: The wire-format message
+    @type wire: string
+    @param current: The offet in wire of the beginning of the rdata.
+    @type current: int
+    @param olen: The length of the wire-format option data
+    @type olen: int
+    @rtype: dns.ends.Option instance"""
+
+    cls = get_option_class(otype)
+    return cls.from_wire(otype, wire, current, olen)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/entropy.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/entropy.py
new file mode 100644
index 0000000..fd9d4f8
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/entropy.py
@@ -0,0 +1,123 @@
+# Copyright (C) 2009 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import os
+import time
+try:
+    import threading as _threading
+except ImportError:
+    import dummy_threading as _threading
+
+class EntropyPool(object):
+    def __init__(self, seed=None):
+        self.pool_index = 0
+        self.digest = None
+        self.next_byte = 0
+        self.lock = _threading.Lock()
+        try:
+            import hashlib
+            self.hash = hashlib.sha1()
+            self.hash_len = 20
+        except:
+            try:
+                import sha
+                self.hash = sha.new()
+                self.hash_len = 20
+            except:
+                import md5
+                self.hash = md5.new()
+                self.hash_len = 16
+        self.pool = '\0' * self.hash_len
+        if not seed is None:
+            self.stir(seed)
+            self.seeded = True
+        else:
+            self.seeded = False
+
+    def stir(self, entropy, already_locked=False):
+        if not already_locked:
+            self.lock.acquire()
+        try:
+            bytes = [ord(c) for c in self.pool]
+            for c in entropy:
+                if self.pool_index == self.hash_len:
+                    self.pool_index = 0
+                b = ord(c) & 0xff
+                bytes[self.pool_index] ^= b
+                self.pool_index += 1
+            self.pool = ''.join([chr(c) for c in bytes])
+        finally:
+            if not already_locked:
+                self.lock.release()
+
+    def _maybe_seed(self):
+        if not self.seeded:
+            try:
+                seed = os.urandom(16)
+            except:
+                try:
+                    r = file('/dev/urandom', 'r', 0)
+                    try:
+                        seed = r.read(16)
+                    finally:
+                        r.close()
+                except:
+                    seed = str(time.time())
+            self.seeded = True
+            self.stir(seed, True)
+
+    def random_8(self):
+        self.lock.acquire()
+        self._maybe_seed()
+        try:
+            if self.digest is None or self.next_byte == self.hash_len:
+                self.hash.update(self.pool)
+                self.digest = self.hash.digest()
+                self.stir(self.digest, True)
+                self.next_byte = 0
+            value = ord(self.digest[self.next_byte])
+            self.next_byte += 1
+        finally:
+            self.lock.release()
+        return value
+
+    def random_16(self):
+        return self.random_8() * 256 + self.random_8()
+
+    def random_32(self):
+        return self.random_16() * 65536 + self.random_16()
+
+    def random_between(self, first, last):
+        size = last - first + 1
+        if size > 4294967296L:
+            raise ValueError('too big')
+        if size > 65536:
+            rand = self.random_32
+            max = 4294967295L
+        elif size > 256:
+            rand = self.random_16
+            max = 65535
+        else:
+            rand = self.random_8
+            max = 255
+	return (first + size * rand() // (max + 1))
+
+pool = EntropyPool()
+
+def random_16():
+    return pool.random_16()
+
+def between(first, last):
+    return pool.random_between(first, last)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/exception.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/exception.py
new file mode 100644
index 0000000..c6d6570
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/exception.py
@@ -0,0 +1,40 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""Common DNS Exceptions."""
+
+class DNSException(Exception):
+    """Abstract base class shared by all dnspython exceptions."""
+    pass
+
+class FormError(DNSException):
+    """DNS message is malformed."""
+    pass
+
+class SyntaxError(DNSException):
+    """Text input is malformed."""
+    pass
+
+class UnexpectedEnd(SyntaxError):
+    """Raised if text input ends unexpectedly."""
+    pass
+
+class TooBig(DNSException):
+    """The message is too big."""
+    pass
+
+class Timeout(DNSException):
+    """The operation timed out."""
+    pass
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/flags.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/flags.py
new file mode 100644
index 0000000..17afdbc
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/flags.py
@@ -0,0 +1,106 @@
+# Copyright (C) 2001-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""DNS Message Flags."""
+
+# Standard DNS flags
+
+QR = 0x8000
+AA = 0x0400
+TC = 0x0200
+RD = 0x0100
+RA = 0x0080
+AD = 0x0020
+CD = 0x0010
+
+# EDNS flags
+
+DO = 0x8000
+
+_by_text = {
+    'QR' : QR,
+    'AA' : AA,
+    'TC' : TC,
+    'RD' : RD,
+    'RA' : RA,
+    'AD' : AD,
+    'CD' : CD
+}
+
+_edns_by_text = {
+    'DO' : DO
+}
+
+
+# We construct the inverse mappings programmatically to ensure that we
+# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that
+# would cause the mappings not to be true inverses.
+
+_by_value = dict([(y, x) for x, y in _by_text.iteritems()])
+
+_edns_by_value = dict([(y, x) for x, y in _edns_by_text.iteritems()])
+
+def _order_flags(table):
+    order = list(table.iteritems())
+    order.sort()
+    order.reverse()
+    return order
+
+_flags_order = _order_flags(_by_value)
+
+_edns_flags_order = _order_flags(_edns_by_value)
+
+def _from_text(text, table):
+    flags = 0
+    tokens = text.split()
+    for t in tokens:
+        flags = flags | table[t.upper()]
+    return flags
+
+def _to_text(flags, table, order):
+    text_flags = []
+    for k, v in order:
+        if flags & k != 0:
+            text_flags.append(v)
+    return ' '.join(text_flags)
+
+def from_text(text):
+    """Convert a space-separated list of flag text values into a flags
+    value.
+    @rtype: int"""
+
+    return _from_text(text, _by_text)
+
+def to_text(flags):
+    """Convert a flags value into a space-separated list of flag text
+    values.
+    @rtype: string"""
+
+    return _to_text(flags, _by_value, _flags_order)
+    
+
+def edns_from_text(text):
+    """Convert a space-separated list of EDNS flag text values into a EDNS
+    flags value.
+    @rtype: int"""
+
+    return _from_text(text, _edns_by_text)
+
+def edns_to_text(flags):
+    """Convert an EDNS flags value into a space-separated list of EDNS flag
+    text values.
+    @rtype: string"""
+
+    return _to_text(flags, _edns_by_value, _edns_flags_order)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/inet.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/inet.py
new file mode 100644
index 0000000..8a8f3e1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/inet.py
@@ -0,0 +1,108 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""Generic Internet address helper functions."""
+
+import socket
+
+import dns.ipv4
+import dns.ipv6
+
+
+# We assume that AF_INET is always defined.
+
+AF_INET = socket.AF_INET
+
+# AF_INET6 might not be defined in the socket module, but we need it.
+# We'll try to use the socket module's value, and if it doesn't work,
+# we'll use our own value.
+
+try:
+    AF_INET6 = socket.AF_INET6
+except AttributeError:
+    AF_INET6 = 9999
+
+def inet_pton(family, text):
+    """Convert the textual form of a network address into its binary form.
+
+    @param family: the address family
+    @type family: int
+    @param text: the textual address
+    @type text: string
+    @raises NotImplementedError: the address family specified is not
+    implemented.
+    @rtype: string
+    """
+    
+    if family == AF_INET:
+        return dns.ipv4.inet_aton(text)
+    elif family == AF_INET6:
+        return dns.ipv6.inet_aton(text)
+    else:
+        raise NotImplementedError
+
+def inet_ntop(family, address):
+    """Convert the binary form of a network address into its textual form.
+
+    @param family: the address family
+    @type family: int
+    @param address: the binary address
+    @type address: string
+    @raises NotImplementedError: the address family specified is not
+    implemented.
+    @rtype: string
+    """
+    if family == AF_INET:
+        return dns.ipv4.inet_ntoa(address)
+    elif family == AF_INET6:
+        return dns.ipv6.inet_ntoa(address)
+    else:
+        raise NotImplementedError
+
+def af_for_address(text):
+    """Determine the address family of a textual-form network address.
+
+    @param text: the textual address
+    @type text: string
+    @raises ValueError: the address family cannot be determined from the input.
+    @rtype: int
+    """
+    try:
+        junk = dns.ipv4.inet_aton(text)
+        return AF_INET
+    except:
+        try:
+            junk = dns.ipv6.inet_aton(text)
+            return AF_INET6
+        except:
+            raise ValueError
+
+def is_multicast(text):
+    """Is the textual-form network address a multicast address?
+
+    @param text: the textual address
+    @raises ValueError: the address family cannot be determined from the input.
+    @rtype: bool
+    """
+    try:
+        first = ord(dns.ipv4.inet_aton(text)[0])
+        return (first >= 224 and first <= 239)
+    except:
+        try:
+            first = ord(dns.ipv6.inet_aton(text)[0])
+            return (first == 255)
+        except:
+            raise ValueError
+    
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/ipv4.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/ipv4.py
new file mode 100644
index 0000000..1569da5
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/ipv4.py
@@ -0,0 +1,36 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""IPv4 helper functions."""
+
+import socket
+import sys
+
+if sys.hexversion < 0x02030000 or sys.platform == 'win32':
+    #
+    # Some versions of Python 2.2 have an inet_aton which rejects
+    # the valid IP address '255.255.255.255'.  It appears this
+    # problem is still present on the Win32 platform even in 2.3.
+    # We'll work around the problem.
+    #
+    def inet_aton(text):
+        if text == '255.255.255.255':
+            return '\xff' * 4
+        else:
+            return socket.inet_aton(text)
+else:
+    inet_aton = socket.inet_aton
+
+inet_ntoa = socket.inet_ntoa
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/ipv6.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/ipv6.py
new file mode 100644
index 0000000..33c6713
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/ipv6.py
@@ -0,0 +1,163 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""IPv6 helper functions."""
+
+import re
+
+import dns.exception
+import dns.ipv4
+
+_leading_zero = re.compile(r'0+([0-9a-f]+)')
+
+def inet_ntoa(address):
+    """Convert a network format IPv6 address into text.
+
+    @param address: the binary address
+    @type address: string
+    @rtype: string
+    @raises ValueError: the address isn't 16 bytes long
+    """
+
+    if len(address) != 16:
+        raise ValueError("IPv6 addresses are 16 bytes long")
+    hex = address.encode('hex_codec')
+    chunks = []
+    i = 0
+    l = len(hex)
+    while i < l:
+        chunk = hex[i : i + 4]
+        # strip leading zeros.  we do this with an re instead of
+        # with lstrip() because lstrip() didn't support chars until
+        # python 2.2.2
+        m = _leading_zero.match(chunk)
+        if not m is None:
+            chunk = m.group(1)
+        chunks.append(chunk)
+        i += 4
+    #
+    # Compress the longest subsequence of 0-value chunks to ::
+    #
+    best_start = 0
+    best_len = 0
+    start = -1
+    last_was_zero = False
+    for i in xrange(8):
+        if chunks[i] != '0':
+            if last_was_zero:
+                end = i
+                current_len = end - start
+                if current_len > best_len:
+                    best_start = start
+                    best_len = current_len
+                last_was_zero = False
+        elif not last_was_zero:
+            start = i
+            last_was_zero = True
+    if last_was_zero:
+        end = 8
+        current_len = end - start
+        if current_len > best_len:
+            best_start = start
+            best_len = current_len
+    if best_len > 0:
+        if best_start == 0 and \
+           (best_len == 6 or
+            best_len == 5 and chunks[5] == 'ffff'):
+            # We have an embedded IPv4 address
+            if best_len == 6:
+                prefix = '::'
+            else:
+                prefix = '::ffff:'
+            hex = prefix + dns.ipv4.inet_ntoa(address[12:])
+        else:
+            hex = ':'.join(chunks[:best_start]) + '::' + \
+                  ':'.join(chunks[best_start + best_len:])
+    else:
+        hex = ':'.join(chunks)
+    return hex
+
+_v4_ending = re.compile(r'(.*):(\d+)\.(\d+)\.(\d+)\.(\d+)$')
+_colon_colon_start = re.compile(r'::.*')
+_colon_colon_end = re.compile(r'.*::$')
+
+def inet_aton(text):
+    """Convert a text format IPv6 address into network format.
+
+    @param text: the textual address
+    @type text: string
+    @rtype: string
+    @raises dns.exception.SyntaxError: the text was not properly formatted
+    """
+
+    #
+    # Our aim here is not something fast; we just want something that works.
+    #
+
+    if text == '::':
+        text = '0::'
+    #
+    # Get rid of the icky dot-quad syntax if we have it.
+    #
+    m = _v4_ending.match(text)
+    if not m is None:
+        text = "%s:%04x:%04x" % (m.group(1),
+                                 int(m.group(2)) * 256 + int(m.group(3)),
+                                 int(m.group(4)) * 256 + int(m.group(5)))
+    #
+    # Try to turn '::<whatever>' into ':<whatever>'; if no match try to
+    # turn '<whatever>::' into '<whatever>:'
+    #
+    m = _colon_colon_start.match(text)
+    if not m is None:
+        text = text[1:]
+    else:
+        m = _colon_colon_end.match(text)
+        if not m is None:
+            text = text[:-1]
+    #
+    # Now canonicalize into 8 chunks of 4 hex digits each
+    #
+    chunks = text.split(':')
+    l = len(chunks)
+    if l > 8:
+        raise dns.exception.SyntaxError
+    seen_empty = False
+    canonical = []
+    for c in chunks:
+        if c == '':
+            if seen_empty:
+                raise dns.exception.SyntaxError
+            seen_empty = True
+            for i in xrange(0, 8 - l + 1):
+                canonical.append('0000')
+        else:
+            lc = len(c)
+            if lc > 4:
+                raise dns.exception.SyntaxError
+            if lc != 4:
+                c = ('0' * (4 - lc)) + c
+            canonical.append(c)
+    if l < 8 and not seen_empty:
+        raise dns.exception.SyntaxError
+    text = ''.join(canonical)
+
+    #
+    # Finally we can go to binary.
+    #
+    try:
+        return text.decode('hex_codec')
+    except TypeError:
+        raise dns.exception.SyntaxError
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/message.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/message.py
new file mode 100644
index 0000000..ba0ebf6
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/message.py
@@ -0,0 +1,1083 @@
+# Copyright (C) 2001-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""DNS Messages"""
+
+import cStringIO
+import random
+import struct
+import sys
+import time
+
+import dns.exception
+import dns.flags
+import dns.name
+import dns.opcode
+import dns.entropy
+import dns.rcode
+import dns.rdata
+import dns.rdataclass
+import dns.rdatatype
+import dns.rrset
+import dns.renderer
+import dns.tsig
+
+class ShortHeader(dns.exception.FormError):
+    """Raised if the DNS packet passed to from_wire() is too short."""
+    pass
+
+class TrailingJunk(dns.exception.FormError):
+    """Raised if the DNS packet passed to from_wire() has extra junk
+    at the end of it."""
+    pass
+
+class UnknownHeaderField(dns.exception.DNSException):
+    """Raised if a header field name is not recognized when converting from
+    text into a message."""
+    pass
+
+class BadEDNS(dns.exception.FormError):
+    """Raised if an OPT record occurs somewhere other than the start of
+    the additional data section."""
+    pass
+
+class BadTSIG(dns.exception.FormError):
+    """Raised if a TSIG record occurs somewhere other than the end of
+    the additional data section."""
+    pass
+
+class UnknownTSIGKey(dns.exception.DNSException):
+    """Raised if we got a TSIG but don't know the key."""
+    pass
+
+class Message(object):
+    """A DNS message.
+
+    @ivar id: The query id; the default is a randomly chosen id.
+    @type id: int
+    @ivar flags: The DNS flags of the message.  @see: RFC 1035 for an
+    explanation of these flags.
+    @type flags: int
+    @ivar question: The question section.
+    @type question: list of dns.rrset.RRset objects
+    @ivar answer: The answer section.
+    @type answer: list of dns.rrset.RRset objects
+    @ivar authority: The authority section.
+    @type authority: list of dns.rrset.RRset objects
+    @ivar additional: The additional data section.
+    @type additional: list of dns.rrset.RRset objects
+    @ivar edns: The EDNS level to use.  The default is -1, no Edns.
+    @type edns: int
+    @ivar ednsflags: The EDNS flags
+    @type ednsflags: long
+    @ivar payload: The EDNS payload size.  The default is 0.
+    @type payload: int
+    @ivar options: The EDNS options
+    @type options: list of dns.edns.Option objects
+    @ivar request_payload: The associated request's EDNS payload size.
+    @type request_payload: int
+    @ivar keyring: The TSIG keyring to use.  The default is None.
+    @type keyring: dict
+    @ivar keyname: The TSIG keyname to use.  The default is None.
+    @type keyname: dns.name.Name object
+    @ivar keyalgorithm: The TSIG key algorithm to use.  The default is
+    dns.tsig.default_algorithm.
+    @type keyalgorithm: string
+    @ivar request_mac: The TSIG MAC of the request message associated with
+    this message; used when validating TSIG signatures.   @see: RFC 2845 for
+    more information on TSIG fields.
+    @type request_mac: string
+    @ivar fudge: TSIG time fudge; default is 300 seconds.
+    @type fudge: int
+    @ivar original_id: TSIG original id; defaults to the message's id
+    @type original_id: int
+    @ivar tsig_error: TSIG error code; default is 0.
+    @type tsig_error: int
+    @ivar other_data: TSIG other data.
+    @type other_data: string
+    @ivar mac: The TSIG MAC for this message.
+    @type mac: string
+    @ivar xfr: Is the message being used to contain the results of a DNS
+    zone transfer?  The default is False.
+    @type xfr: bool
+    @ivar origin: The origin of the zone in messages which are used for
+    zone transfers or for DNS dynamic updates.  The default is None.
+    @type origin: dns.name.Name object
+    @ivar tsig_ctx: The TSIG signature context associated with this
+    message.  The default is None.
+    @type tsig_ctx: hmac.HMAC object
+    @ivar had_tsig: Did the message decoded from wire format have a TSIG
+    signature?
+    @type had_tsig: bool
+    @ivar multi: Is this message part of a multi-message sequence?  The
+    default is false.  This variable is used when validating TSIG signatures
+    on messages which are part of a zone transfer.
+    @type multi: bool
+    @ivar first: Is this message standalone, or the first of a multi
+    message sequence?  This variable is used when validating TSIG signatures
+    on messages which are part of a zone transfer.
+    @type first: bool
+    @ivar index: An index of rrsets in the message.  The index key is
+    (section, name, rdclass, rdtype, covers, deleting).  Indexing can be
+    disabled by setting the index to None.
+    @type index: dict
+    """
+
+    def __init__(self, id=None):
+        if id is None:
+            self.id = dns.entropy.random_16()
+        else:
+            self.id = id
+        self.flags = 0
+        self.question = []
+        self.answer = []
+        self.authority = []
+        self.additional = []
+        self.edns = -1
+        self.ednsflags = 0
+        self.payload = 0
+        self.options = []
+        self.request_payload = 0
+        self.keyring = None
+        self.keyname = None
+        self.keyalgorithm = dns.tsig.default_algorithm
+        self.request_mac = ''
+        self.other_data = ''
+        self.tsig_error = 0
+        self.fudge = 300
+        self.original_id = self.id
+        self.mac = ''
+        self.xfr = False
+        self.origin = None
+        self.tsig_ctx = None
+        self.had_tsig = False
+        self.multi = False
+        self.first = True
+        self.index = {}
+
+    def __repr__(self):
+        return '<DNS message, ID ' + `self.id` + '>'
+
+    def __str__(self):
+        return self.to_text()
+
+    def to_text(self,  origin=None, relativize=True, **kw):
+        """Convert the message to text.
+
+        The I{origin}, I{relativize}, and any other keyword
+        arguments are passed to the rrset to_wire() method.
+
+        @rtype: string
+        """
+
+        s = cStringIO.StringIO()
+        print >> s, 'id %d' % self.id
+        print >> s, 'opcode %s' % \
+              dns.opcode.to_text(dns.opcode.from_flags(self.flags))
+        rc = dns.rcode.from_flags(self.flags, self.ednsflags)
+        print >> s, 'rcode %s' % dns.rcode.to_text(rc)
+        print >> s, 'flags %s' % dns.flags.to_text(self.flags)
+        if self.edns >= 0:
+            print >> s, 'edns %s' % self.edns
+            if self.ednsflags != 0:
+                print >> s, 'eflags %s' % \
+                      dns.flags.edns_to_text(self.ednsflags)
+            print >> s, 'payload', self.payload
+        is_update = dns.opcode.is_update(self.flags)
+        if is_update:
+            print >> s, ';ZONE'
+        else:
+            print >> s, ';QUESTION'
+        for rrset in self.question:
+            print >> s, rrset.to_text(origin, relativize, **kw)
+        if is_update:
+            print >> s, ';PREREQ'
+        else:
+            print >> s, ';ANSWER'
+        for rrset in self.answer:
+            print >> s, rrset.to_text(origin, relativize, **kw)
+        if is_update:
+            print >> s, ';UPDATE'
+        else:
+            print >> s, ';AUTHORITY'
+        for rrset in self.authority:
+            print >> s, rrset.to_text(origin, relativize, **kw)
+        print >> s, ';ADDITIONAL'
+        for rrset in self.additional:
+            print >> s, rrset.to_text(origin, relativize, **kw)
+        #
+        # We strip off the final \n so the caller can print the result without
+        # doing weird things to get around eccentricities in Python print
+        # formatting
+        #
+        return s.getvalue()[:-1]
+
+    def __eq__(self, other):
+        """Two messages are equal if they have the same content in the
+        header, question, answer, and authority sections.
+        @rtype: bool"""
+        if not isinstance(other, Message):
+            return False
+        if self.id != other.id:
+            return False
+        if self.flags != other.flags:
+            return False
+        for n in self.question:
+            if n not in other.question:
+                return False
+        for n in other.question:
+            if n not in self.question:
+                return False
+        for n in self.answer:
+            if n not in other.answer:
+                return False
+        for n in other.answer:
+            if n not in self.answer:
+                return False
+        for n in self.authority:
+            if n not in other.authority:
+                return False
+        for n in other.authority:
+            if n not in self.authority:
+                return False
+        return True
+
+    def __ne__(self, other):
+        """Are two messages not equal?
+        @rtype: bool"""
+        return not self.__eq__(other)
+
+    def is_response(self, other):
+        """Is other a response to self?
+        @rtype: bool"""
+        if other.flags & dns.flags.QR == 0 or \
+           self.id != other.id or \
+           dns.opcode.from_flags(self.flags) != \
+           dns.opcode.from_flags(other.flags):
+            return False
+        if dns.rcode.from_flags(other.flags, other.ednsflags) != \
+               dns.rcode.NOERROR:
+            return True
+        if dns.opcode.is_update(self.flags):
+            return True
+        for n in self.question:
+            if n not in other.question:
+                return False
+        for n in other.question:
+            if n not in self.question:
+                return False
+        return True
+
+    def section_number(self, section):
+        if section is self.question:
+            return 0
+        elif section is self.answer:
+            return 1
+        elif section is self.authority:
+            return 2
+        elif section is self.additional:
+            return 3
+        else:
+            raise ValueError('unknown section')
+
+    def find_rrset(self, section, name, rdclass, rdtype,
+                   covers=dns.rdatatype.NONE, deleting=None, create=False,
+                   force_unique=False):
+        """Find the RRset with the given attributes in the specified section.
+
+        @param section: the section of the message to look in, e.g.
+        self.answer.
+        @type section: list of dns.rrset.RRset objects
+        @param name: the name of the RRset
+        @type name: dns.name.Name object
+        @param rdclass: the class of the RRset
+        @type rdclass: int
+        @param rdtype: the type of the RRset
+        @type rdtype: int
+        @param covers: the covers value of the RRset
+        @type covers: int
+        @param deleting: the deleting value of the RRset
+        @type deleting: int
+        @param create: If True, create the RRset if it is not found.
+        The created RRset is appended to I{section}.
+        @type create: bool
+        @param force_unique: If True and create is also True, create a
+        new RRset regardless of whether a matching RRset exists already.
+        @type force_unique: bool
+        @raises KeyError: the RRset was not found and create was False
+        @rtype: dns.rrset.RRset object"""
+
+        key = (self.section_number(section),
+               name, rdclass, rdtype, covers, deleting)
+        if not force_unique:
+            if not self.index is None:
+                rrset = self.index.get(key)
+                if not rrset is None:
+                    return rrset
+            else:
+                for rrset in section:
+                    if rrset.match(name, rdclass, rdtype, covers, deleting):
+                        return rrset
+        if not create:
+            raise KeyError
+        rrset = dns.rrset.RRset(name, rdclass, rdtype, covers, deleting)
+        section.append(rrset)
+        if not self.index is None:
+            self.index[key] = rrset
+        return rrset
+
+    def get_rrset(self, section, name, rdclass, rdtype,
+                  covers=dns.rdatatype.NONE, deleting=None, create=False,
+                  force_unique=False):
+        """Get the RRset with the given attributes in the specified section.
+
+        If the RRset is not found, None is returned.
+
+        @param section: the section of the message to look in, e.g.
+        self.answer.
+        @type section: list of dns.rrset.RRset objects
+        @param name: the name of the RRset
+        @type name: dns.name.Name object
+        @param rdclass: the class of the RRset
+        @type rdclass: int
+        @param rdtype: the type of the RRset
+        @type rdtype: int
+        @param covers: the covers value of the RRset
+        @type covers: int
+        @param deleting: the deleting value of the RRset
+        @type deleting: int
+        @param create: If True, create the RRset if it is not found.
+        The created RRset is appended to I{section}.
+        @type create: bool
+        @param force_unique: If True and create is also True, create a
+        new RRset regardless of whether a matching RRset exists already.
+        @type force_unique: bool
+        @rtype: dns.rrset.RRset object or None"""
+
+        try:
+            rrset = self.find_rrset(section, name, rdclass, rdtype, covers,
+                                    deleting, create, force_unique)
+        except KeyError:
+            rrset = None
+        return rrset
+
+    def to_wire(self, origin=None, max_size=0, **kw):
+        """Return a string containing the message in DNS compressed wire
+        format.
+
+        Additional keyword arguments are passed to the rrset to_wire()
+        method.
+
+        @param origin: The origin to be appended to any relative names.
+        @type origin: dns.name.Name object
+        @param max_size: The maximum size of the wire format output; default
+        is 0, which means 'the message's request payload, if nonzero, or
+        65536'.
+        @type max_size: int
+        @raises dns.exception.TooBig: max_size was exceeded
+        @rtype: string
+        """
+
+        if max_size == 0:
+            if self.request_payload != 0:
+                max_size = self.request_payload
+            else:
+                max_size = 65535
+        if max_size < 512:
+            max_size = 512
+        elif max_size > 65535:
+            max_size = 65535
+        r = dns.renderer.Renderer(self.id, self.flags, max_size, origin)
+        for rrset in self.question:
+            r.add_question(rrset.name, rrset.rdtype, rrset.rdclass)
+        for rrset in self.answer:
+            r.add_rrset(dns.renderer.ANSWER, rrset, **kw)
+        for rrset in self.authority:
+            r.add_rrset(dns.renderer.AUTHORITY, rrset, **kw)
+        if self.edns >= 0:
+            r.add_edns(self.edns, self.ednsflags, self.payload, self.options)
+        for rrset in self.additional:
+            r.add_rrset(dns.renderer.ADDITIONAL, rrset, **kw)
+        r.write_header()
+        if not self.keyname is None:
+            r.add_tsig(self.keyname, self.keyring[self.keyname],
+                       self.fudge, self.original_id, self.tsig_error,
+                       self.other_data, self.request_mac,
+                       self.keyalgorithm)
+            self.mac = r.mac
+        return r.get_wire()
+
+    def use_tsig(self, keyring, keyname=None, fudge=300,
+                 original_id=None, tsig_error=0, other_data='',
+                 algorithm=dns.tsig.default_algorithm):
+        """When sending, a TSIG signature using the specified keyring
+        and keyname should be added.
+
+        @param keyring: The TSIG keyring to use; defaults to None.
+        @type keyring: dict
+        @param keyname: The name of the TSIG key to use; defaults to None.
+        The key must be defined in the keyring.  If a keyring is specified
+        but a keyname is not, then the key used will be the first key in the
+        keyring.  Note that the order of keys in a dictionary is not defined,
+        so applications should supply a keyname when a keyring is used, unless
+        they know the keyring contains only one key.
+        @type keyname: dns.name.Name or string
+        @param fudge: TSIG time fudge; default is 300 seconds.
+        @type fudge: int
+        @param original_id: TSIG original id; defaults to the message's id
+        @type original_id: int
+        @param tsig_error: TSIG error code; default is 0.
+        @type tsig_error: int
+        @param other_data: TSIG other data.
+        @type other_data: string
+        @param algorithm: The TSIG algorithm to use; defaults to
+        dns.tsig.default_algorithm
+        """
+
+        self.keyring = keyring
+        if keyname is None:
+            self.keyname = self.keyring.keys()[0]
+        else:
+            if isinstance(keyname, (str, unicode)):
+                keyname = dns.name.from_text(keyname)
+            self.keyname = keyname
+        self.keyalgorithm = algorithm
+        self.fudge = fudge
+        if original_id is None:
+            self.original_id = self.id
+        else:
+            self.original_id = original_id
+        self.tsig_error = tsig_error
+        self.other_data = other_data
+
+    def use_edns(self, edns=0, ednsflags=0, payload=1280, request_payload=None, options=None):
+        """Configure EDNS behavior.
+        @param edns: The EDNS level to use.  Specifying None, False, or -1
+        means 'do not use EDNS', and in this case the other parameters are
+        ignored.  Specifying True is equivalent to specifying 0, i.e. 'use
+        EDNS0'.
+        @type edns: int or bool or None
+        @param ednsflags: EDNS flag values.
+        @type ednsflags: int
+        @param payload: The EDNS sender's payload field, which is the maximum
+        size of UDP datagram the sender can handle.
+        @type payload: int
+        @param request_payload: The EDNS payload size to use when sending
+        this message.  If not specified, defaults to the value of payload.
+        @type request_payload: int or None
+        @param options: The EDNS options
+        @type options: None or list of dns.edns.Option objects
+        @see: RFC 2671
+        """
+        if edns is None or edns is False:
+            edns = -1
+        if edns is True:
+            edns = 0
+        if request_payload is None:
+            request_payload = payload
+        if edns < 0:
+            ednsflags = 0
+            payload = 0
+            request_payload = 0
+            options = []
+        else:
+            # make sure the EDNS version in ednsflags agrees with edns
+            ednsflags &= 0xFF00FFFFL
+            ednsflags |= (edns << 16)
+            if options is None:
+                options = []
+        self.edns = edns
+        self.ednsflags = ednsflags
+        self.payload = payload
+        self.options = options
+        self.request_payload = request_payload
+
+    def want_dnssec(self, wanted=True):
+        """Enable or disable 'DNSSEC desired' flag in requests.
+        @param wanted: Is DNSSEC desired?  If True, EDNS is enabled if
+        required, and then the DO bit is set.  If False, the DO bit is
+        cleared if EDNS is enabled.
+        @type wanted: bool
+        """
+        if wanted:
+            if self.edns < 0:
+                self.use_edns()
+            self.ednsflags |= dns.flags.DO
+        elif self.edns >= 0:
+            self.ednsflags &= ~dns.flags.DO
+
+    def rcode(self):
+        """Return the rcode.
+        @rtype: int
+        """
+        return dns.rcode.from_flags(self.flags, self.ednsflags)
+
+    def set_rcode(self, rcode):
+        """Set the rcode.
+        @param rcode: the rcode
+        @type rcode: int
+        """
+        (value, evalue) = dns.rcode.to_flags(rcode)
+        self.flags &= 0xFFF0
+        self.flags |= value
+        self.ednsflags &= 0x00FFFFFFL
+        self.ednsflags |= evalue
+        if self.ednsflags != 0 and self.edns < 0:
+            self.edns = 0
+
+    def opcode(self):
+        """Return the opcode.
+        @rtype: int
+        """
+        return dns.opcode.from_flags(self.flags)
+
+    def set_opcode(self, opcode):
+        """Set the opcode.
+        @param opcode: the opcode
+        @type opcode: int
+        """
+        self.flags &= 0x87FF
+        self.flags |= dns.opcode.to_flags(opcode)
+
+class _WireReader(object):
+    """Wire format reader.
+
+    @ivar wire: the wire-format message.
+    @type wire: string
+    @ivar message: The message object being built
+    @type message: dns.message.Message object
+    @ivar current: When building a message object from wire format, this
+    variable contains the offset from the beginning of wire of the next octet
+    to be read.
+    @type current: int
+    @ivar updating: Is the message a dynamic update?
+    @type updating: bool
+    @ivar one_rr_per_rrset: Put each RR into its own RRset?
+    @type one_rr_per_rrset: bool
+    @ivar zone_rdclass: The class of the zone in messages which are
+    DNS dynamic updates.
+    @type zone_rdclass: int
+    """
+
+    def __init__(self, wire, message, question_only=False,
+                 one_rr_per_rrset=False):
+        self.wire = wire
+        self.message = message
+        self.current = 0
+        self.updating = False
+        self.zone_rdclass = dns.rdataclass.IN
+        self.question_only = question_only
+        self.one_rr_per_rrset = one_rr_per_rrset
+
+    def _get_question(self, qcount):
+        """Read the next I{qcount} records from the wire data and add them to
+        the question section.
+        @param qcount: the number of questions in the message
+        @type qcount: int"""
+
+        if self.updating and qcount > 1:
+            raise dns.exception.FormError
+
+        for i in xrange(0, qcount):
+            (qname, used) = dns.name.from_wire(self.wire, self.current)
+            if not self.message.origin is None:
+                qname = qname.relativize(self.message.origin)
+            self.current = self.current + used
+            (rdtype, rdclass) = \
+                     struct.unpack('!HH',
+                                   self.wire[self.current:self.current + 4])
+            self.current = self.current + 4
+            self.message.find_rrset(self.message.question, qname,
+                                    rdclass, rdtype, create=True,
+                                    force_unique=True)
+            if self.updating:
+                self.zone_rdclass = rdclass
+
+    def _get_section(self, section, count):
+        """Read the next I{count} records from the wire data and add them to
+        the specified section.
+        @param section: the section of the message to which to add records
+        @type section: list of dns.rrset.RRset objects
+        @param count: the number of records to read
+        @type count: int"""
+
+        if self.updating or self.one_rr_per_rrset:
+            force_unique = True
+        else:
+            force_unique = False
+        seen_opt = False
+        for i in xrange(0, count):
+            rr_start = self.current
+            (name, used) = dns.name.from_wire(self.wire, self.current)
+            absolute_name = name
+            if not self.message.origin is None:
+                name = name.relativize(self.message.origin)
+            self.current = self.current + used
+            (rdtype, rdclass, ttl, rdlen) = \
+                     struct.unpack('!HHIH',
+                                   self.wire[self.current:self.current + 10])
+            self.current = self.current + 10
+            if rdtype == dns.rdatatype.OPT:
+                if not section is self.message.additional or seen_opt:
+                    raise BadEDNS
+                self.message.payload = rdclass
+                self.message.ednsflags = ttl
+                self.message.edns = (ttl & 0xff0000) >> 16
+                self.message.options = []
+                current = self.current
+                optslen = rdlen
+                while optslen > 0:
+                    (otype, olen) = \
+                            struct.unpack('!HH',
+                                          self.wire[current:current + 4])
+                    current = current + 4
+                    opt = dns.edns.option_from_wire(otype, self.wire, current, olen)
+                    self.message.options.append(opt)
+                    current = current + olen
+                    optslen = optslen - 4 - olen
+                seen_opt = True
+            elif rdtype == dns.rdatatype.TSIG:
+                if not (section is self.message.additional and
+                        i == (count - 1)):
+                    raise BadTSIG
+                if self.message.keyring is None:
+                    raise UnknownTSIGKey('got signed message without keyring')
+                secret = self.message.keyring.get(absolute_name)
+                if secret is None:
+                    raise UnknownTSIGKey("key '%s' unknown" % name)
+                self.message.tsig_ctx = \
+                                      dns.tsig.validate(self.wire,
+                                          absolute_name,
+                                          secret,
+                                          int(time.time()),
+                                          self.message.request_mac,
+                                          rr_start,
+                                          self.current,
+                                          rdlen,
+                                          self.message.tsig_ctx,
+                                          self.message.multi,
+                                          self.message.first)
+                self.message.had_tsig = True
+            else:
+                if ttl < 0:
+                    ttl = 0
+                if self.updating and \
+                   (rdclass == dns.rdataclass.ANY or
+                    rdclass == dns.rdataclass.NONE):
+                    deleting = rdclass
+                    rdclass = self.zone_rdclass
+                else:
+                    deleting = None
+                if deleting == dns.rdataclass.ANY or \
+                   (deleting == dns.rdataclass.NONE and \
+                    section == self.message.answer):
+                    covers = dns.rdatatype.NONE
+                    rd = None
+                else:
+                    rd = dns.rdata.from_wire(rdclass, rdtype, self.wire,
+                                             self.current, rdlen,
+                                             self.message.origin)
+                    covers = rd.covers()
+                if self.message.xfr and rdtype == dns.rdatatype.SOA:
+                    force_unique = True
+                rrset = self.message.find_rrset(section, name,
+                                                rdclass, rdtype, covers,
+                                                deleting, True, force_unique)
+                if not rd is None:
+                    rrset.add(rd, ttl)
+            self.current = self.current + rdlen
+
+    def read(self):
+        """Read a wire format DNS message and build a dns.message.Message
+        object."""
+
+        l = len(self.wire)
+        if l < 12:
+            raise ShortHeader
+        (self.message.id, self.message.flags, qcount, ancount,
+         aucount, adcount) = struct.unpack('!HHHHHH', self.wire[:12])
+        self.current = 12
+        if dns.opcode.is_update(self.message.flags):
+            self.updating = True
+        self._get_question(qcount)
+        if self.question_only:
+            return
+        self._get_section(self.message.answer, ancount)
+        self._get_section(self.message.authority, aucount)
+        self._get_section(self.message.additional, adcount)
+        if self.current != l:
+            raise TrailingJunk
+        if self.message.multi and self.message.tsig_ctx and \
+               not self.message.had_tsig:
+            self.message.tsig_ctx.update(self.wire)
+
+
+def from_wire(wire, keyring=None, request_mac='', xfr=False, origin=None,
+              tsig_ctx = None, multi = False, first = True,
+              question_only = False, one_rr_per_rrset = False):
+    """Convert a DNS wire format message into a message
+    object.
+
+    @param keyring: The keyring to use if the message is signed.
+    @type keyring: dict
+    @param request_mac: If the message is a response to a TSIG-signed request,
+    I{request_mac} should be set to the MAC of that request.
+    @type request_mac: string
+    @param xfr: Is this message part of a zone transfer?
+    @type xfr: bool
+    @param origin: If the message is part of a zone transfer, I{origin}
+    should be the origin name of the zone.
+    @type origin: dns.name.Name object
+    @param tsig_ctx: The ongoing TSIG context, used when validating zone
+    transfers.
+    @type tsig_ctx: hmac.HMAC object
+    @param multi: Is this message part of a multiple message sequence?
+    @type multi: bool
+    @param first: Is this message standalone, or the first of a multi
+    message sequence?
+    @type first: bool
+    @param question_only: Read only up to the end of the question section?
+    @type question_only: bool
+    @param one_rr_per_rrset: Put each RR into its own RRset
+    @type one_rr_per_rrset: bool
+    @raises ShortHeader: The message is less than 12 octets long.
+    @raises TrailingJunk: There were octets in the message past the end
+    of the proper DNS message.
+    @raises BadEDNS: An OPT record was in the wrong section, or occurred more
+    than once.
+    @raises BadTSIG: A TSIG record was not the last record of the additional
+    data section.
+    @rtype: dns.message.Message object"""
+
+    m = Message(id=0)
+    m.keyring = keyring
+    m.request_mac = request_mac
+    m.xfr = xfr
+    m.origin = origin
+    m.tsig_ctx = tsig_ctx
+    m.multi = multi
+    m.first = first
+
+    reader = _WireReader(wire, m, question_only, one_rr_per_rrset)
+    reader.read()
+
+    return m
+
+
+class _TextReader(object):
+    """Text format reader.
+
+    @ivar tok: the tokenizer
+    @type tok: dns.tokenizer.Tokenizer object
+    @ivar message: The message object being built
+    @type message: dns.message.Message object
+    @ivar updating: Is the message a dynamic update?
+    @type updating: bool
+    @ivar zone_rdclass: The class of the zone in messages which are
+    DNS dynamic updates.
+    @type zone_rdclass: int
+    @ivar last_name: The most recently read name when building a message object
+    from text format.
+    @type last_name: dns.name.Name object
+    """
+
+    def __init__(self, text, message):
+        self.message = message
+        self.tok = dns.tokenizer.Tokenizer(text)
+        self.last_name = None
+        self.zone_rdclass = dns.rdataclass.IN
+        self.updating = False
+
+    def _header_line(self, section):
+        """Process one line from the text format header section."""
+
+        token = self.tok.get()
+        what = token.value
+        if what == 'id':
+            self.message.id = self.tok.get_int()
+        elif what == 'flags':
+            while True:
+                token = self.tok.get()
+                if not token.is_identifier():
+                    self.tok.unget(token)
+                    break
+                self.message.flags = self.message.flags | \
+                                     dns.flags.from_text(token.value)
+            if dns.opcode.is_update(self.message.flags):
+                self.updating = True
+        elif what == 'edns':
+            self.message.edns = self.tok.get_int()
+            self.message.ednsflags = self.message.ednsflags | \
+                                     (self.message.edns << 16)
+        elif what == 'eflags':
+            if self.message.edns < 0:
+                self.message.edns = 0
+            while True:
+                token = self.tok.get()
+                if not token.is_identifier():
+                    self.tok.unget(token)
+                    break
+                self.message.ednsflags = self.message.ednsflags | \
+                              dns.flags.edns_from_text(token.value)
+        elif what == 'payload':
+            self.message.payload = self.tok.get_int()
+            if self.message.edns < 0:
+                self.message.edns = 0
+        elif what == 'opcode':
+            text = self.tok.get_string()
+            self.message.flags = self.message.flags | \
+                      dns.opcode.to_flags(dns.opcode.from_text(text))
+        elif what == 'rcode':
+            text = self.tok.get_string()
+            self.message.set_rcode(dns.rcode.from_text(text))
+        else:
+            raise UnknownHeaderField
+        self.tok.get_eol()
+
+    def _question_line(self, section):
+        """Process one line from the text format question section."""
+
+        token = self.tok.get(want_leading = True)
+        if not token.is_whitespace():
+            self.last_name = dns.name.from_text(token.value, None)
+        name = self.last_name
+        token = self.tok.get()
+        if not token.is_identifier():
+            raise dns.exception.SyntaxError
+        # Class
+        try:
+            rdclass = dns.rdataclass.from_text(token.value)
+            token = self.tok.get()
+            if not token.is_identifier():
+                raise dns.exception.SyntaxError
+        except dns.exception.SyntaxError:
+            raise dns.exception.SyntaxError
+        except:
+            rdclass = dns.rdataclass.IN
+        # Type
+        rdtype = dns.rdatatype.from_text(token.value)
+        self.message.find_rrset(self.message.question, name,
+                                rdclass, rdtype, create=True,
+                                force_unique=True)
+        if self.updating:
+            self.zone_rdclass = rdclass
+        self.tok.get_eol()
+
+    def _rr_line(self, section):
+        """Process one line from the text format answer, authority, or
+        additional data sections.
+        """
+
+        deleting = None
+        # Name
+        token = self.tok.get(want_leading = True)
+        if not token.is_whitespace():
+            self.last_name = dns.name.from_text(token.value, None)
+        name = self.last_name
+        token = self.tok.get()
+        if not token.is_identifier():
+            raise dns.exception.SyntaxError
+        # TTL
+        try:
+            ttl = int(token.value, 0)
+            token = self.tok.get()
+            if not token.is_identifier():
+                raise dns.exception.SyntaxError
+        except dns.exception.SyntaxError:
+            raise dns.exception.SyntaxError
+        except:
+            ttl = 0
+        # Class
+        try:
+            rdclass = dns.rdataclass.from_text(token.value)
+            token = self.tok.get()
+            if not token.is_identifier():
+                raise dns.exception.SyntaxError
+            if rdclass == dns.rdataclass.ANY or rdclass == dns.rdataclass.NONE:
+                deleting = rdclass
+                rdclass = self.zone_rdclass
+        except dns.exception.SyntaxError:
+            raise dns.exception.SyntaxError
+        except:
+            rdclass = dns.rdataclass.IN
+        # Type
+        rdtype = dns.rdatatype.from_text(token.value)
+        token = self.tok.get()
+        if not token.is_eol_or_eof():
+            self.tok.unget(token)
+            rd = dns.rdata.from_text(rdclass, rdtype, self.tok, None)
+            covers = rd.covers()
+        else:
+            rd = None
+            covers = dns.rdatatype.NONE
+        rrset = self.message.find_rrset(section, name,
+                                        rdclass, rdtype, covers,
+                                        deleting, True, self.updating)
+        if not rd is None:
+            rrset.add(rd, ttl)
+
+    def read(self):
+        """Read a text format DNS message and build a dns.message.Message
+        object."""
+
+        line_method = self._header_line
+        section = None
+        while 1:
+            token = self.tok.get(True, True)
+            if token.is_eol_or_eof():
+                break
+            if token.is_comment():
+                u = token.value.upper()
+                if u == 'HEADER':
+                    line_method = self._header_line
+                elif u == 'QUESTION' or u == 'ZONE':
+                    line_method = self._question_line
+                    section = self.message.question
+                elif u == 'ANSWER' or u == 'PREREQ':
+                    line_method = self._rr_line
+                    section = self.message.answer
+                elif u == 'AUTHORITY' or u == 'UPDATE':
+                    line_method = self._rr_line
+                    section = self.message.authority
+                elif u == 'ADDITIONAL':
+                    line_method = self._rr_line
+                    section = self.message.additional
+                self.tok.get_eol()
+                continue
+            self.tok.unget(token)
+            line_method(section)
+
+
+def from_text(text):
+    """Convert the text format message into a message object.
+
+    @param text: The text format message.
+    @type text: string
+    @raises UnknownHeaderField:
+    @raises dns.exception.SyntaxError:
+    @rtype: dns.message.Message object"""
+
+    # 'text' can also be a file, but we don't publish that fact
+    # since it's an implementation detail.  The official file
+    # interface is from_file().
+
+    m = Message()
+
+    reader = _TextReader(text, m)
+    reader.read()
+
+    return m
+
+def from_file(f):
+    """Read the next text format message from the specified file.
+
+    @param f: file or string.  If I{f} is a string, it is treated
+    as the name of a file to open.
+    @raises UnknownHeaderField:
+    @raises dns.exception.SyntaxError:
+    @rtype: dns.message.Message object"""
+
+    if sys.hexversion >= 0x02030000:
+        # allow Unicode filenames; turn on universal newline support
+        str_type = basestring
+        opts = 'rU'
+    else:
+        str_type = str
+        opts = 'r'
+    if isinstance(f, str_type):
+        f = file(f, opts)
+        want_close = True
+    else:
+        want_close = False
+
+    try:
+        m = from_text(f)
+    finally:
+        if want_close:
+            f.close()
+    return m
+
+def make_query(qname, rdtype, rdclass = dns.rdataclass.IN, use_edns=None,
+               want_dnssec=False):
+    """Make a query message.
+
+    The query name, type, and class may all be specified either
+    as objects of the appropriate type, or as strings.
+
+    The query will have a randomly choosen query id, and its DNS flags
+    will be set to dns.flags.RD.
+
+    @param qname: The query name.
+    @type qname: dns.name.Name object or string
+    @param rdtype: The desired rdata type.
+    @type rdtype: int
+    @param rdclass: The desired rdata class; the default is class IN.
+    @type rdclass: int
+    @param use_edns: The EDNS level to use; the default is None (no EDNS).
+    See the description of dns.message.Message.use_edns() for the possible
+    values for use_edns and their meanings.
+    @type use_edns: int or bool or None
+    @param want_dnssec: Should the query indicate that DNSSEC is desired?
+    @type want_dnssec: bool
+    @rtype: dns.message.Message object"""
+
+    if isinstance(qname, (str, unicode)):
+        qname = dns.name.from_text(qname)
+    if isinstance(rdtype, str):
+        rdtype = dns.rdatatype.from_text(rdtype)
+    if isinstance(rdclass, str):
+        rdclass = dns.rdataclass.from_text(rdclass)
+    m = Message()
+    m.flags |= dns.flags.RD
+    m.find_rrset(m.question, qname, rdclass, rdtype, create=True,
+                 force_unique=True)
+    m.use_edns(use_edns)
+    m.want_dnssec(want_dnssec)
+    return m
+
+def make_response(query, recursion_available=False, our_payload=8192):
+    """Make a message which is a response for the specified query.
+    The message returned is really a response skeleton; it has all
+    of the infrastructure required of a response, but none of the
+    content.
+
+    The response's question section is a shallow copy of the query's
+    question section, so the query's question RRsets should not be
+    changed.
+
+    @param query: the query to respond to
+    @type query: dns.message.Message object
+    @param recursion_available: should RA be set in the response?
+    @type recursion_available: bool
+    @param our_payload: payload size to advertise in EDNS responses; default
+    is 8192.
+    @type our_payload: int
+    @rtype: dns.message.Message object"""
+
+    if query.flags & dns.flags.QR:
+        raise dns.exception.FormError('specified query message is not a query')
+    response = dns.message.Message(query.id)
+    response.flags = dns.flags.QR | (query.flags & dns.flags.RD)
+    if recursion_available:
+        response.flags |= dns.flags.RA
+    response.set_opcode(query.opcode())
+    response.question = list(query.question)
+    if query.edns >= 0:
+        response.use_edns(0, 0, our_payload, query.payload)
+    if not query.keyname is None:
+        response.keyname = query.keyname
+        response.keyring = query.keyring
+        response.request_mac = query.mac
+    return response
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/name.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/name.py
new file mode 100644
index 0000000..b54aa19
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/name.py
@@ -0,0 +1,700 @@
+# Copyright (C) 2001-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""DNS Names.
+
+@var root: The DNS root name.
+@type root: dns.name.Name object
+@var empty: The empty DNS name.
+@type empty: dns.name.Name object
+"""
+
+import cStringIO
+import struct
+import sys
+
+if sys.hexversion >= 0x02030000:
+    import encodings.idna
+
+import dns.exception
+
+NAMERELN_NONE = 0
+NAMERELN_SUPERDOMAIN = 1
+NAMERELN_SUBDOMAIN = 2
+NAMERELN_EQUAL = 3
+NAMERELN_COMMONANCESTOR = 4
+
+class EmptyLabel(dns.exception.SyntaxError):
+    """Raised if a label is empty."""
+    pass
+
+class BadEscape(dns.exception.SyntaxError):
+    """Raised if an escaped code in a text format name is invalid."""
+    pass
+
+class BadPointer(dns.exception.FormError):
+    """Raised if a compression pointer points forward instead of backward."""
+    pass
+
+class BadLabelType(dns.exception.FormError):
+    """Raised if the label type of a wire format name is unknown."""
+    pass
+
+class NeedAbsoluteNameOrOrigin(dns.exception.DNSException):
+    """Raised if an attempt is made to convert a non-absolute name to
+    wire when there is also a non-absolute (or missing) origin."""
+    pass
+
+class NameTooLong(dns.exception.FormError):
+    """Raised if a name is > 255 octets long."""
+    pass
+
+class LabelTooLong(dns.exception.SyntaxError):
+    """Raised if a label is > 63 octets long."""
+    pass
+
+class AbsoluteConcatenation(dns.exception.DNSException):
+    """Raised if an attempt is made to append anything other than the
+    empty name to an absolute name."""
+    pass
+
+class NoParent(dns.exception.DNSException):
+    """Raised if an attempt is made to get the parent of the root name
+    or the empty name."""
+    pass
+
+_escaped = {
+    '"' : True,
+    '(' : True,
+    ')' : True,
+    '.' : True,
+    ';' : True,
+    '\\' : True,
+    '@' : True,
+    '$' : True
+    }
+
+def _escapify(label):
+    """Escape the characters in label which need it.
+    @returns: the escaped string
+    @rtype: string"""
+    text = ''
+    for c in label:
+        if c in _escaped:
+            text += '\\' + c
+        elif ord(c) > 0x20 and ord(c) < 0x7F:
+            text += c
+        else:
+            text += '\\%03d' % ord(c)
+    return text
+
+def _validate_labels(labels):
+    """Check for empty labels in the middle of a label sequence,
+    labels that are too long, and for too many labels.
+    @raises NameTooLong: the name as a whole is too long
+    @raises LabelTooLong: an individual label is too long
+    @raises EmptyLabel: a label is empty (i.e. the root label) and appears
+    in a position other than the end of the label sequence"""
+
+    l = len(labels)
+    total = 0
+    i = -1
+    j = 0
+    for label in labels:
+        ll = len(label)
+        total += ll + 1
+        if ll > 63:
+            raise LabelTooLong
+        if i < 0 and label == '':
+            i = j
+        j += 1
+    if total > 255:
+        raise NameTooLong
+    if i >= 0 and i != l - 1:
+        raise EmptyLabel
+
+class Name(object):
+    """A DNS name.
+
+    The dns.name.Name class represents a DNS name as a tuple of labels.
+    Instances of the class are immutable.
+
+    @ivar labels: The tuple of labels in the name. Each label is a string of
+    up to 63 octets."""
+
+    __slots__ = ['labels']
+
+    def __init__(self, labels):
+        """Initialize a domain name from a list of labels.
+        @param labels: the labels
+        @type labels: any iterable whose values are strings
+        """
+
+        super(Name, self).__setattr__('labels', tuple(labels))
+        _validate_labels(self.labels)
+
+    def __setattr__(self, name, value):
+        raise TypeError("object doesn't support attribute assignment")
+
+    def is_absolute(self):
+        """Is the most significant label of this name the root label?
+        @rtype: bool
+        """
+
+        return len(self.labels) > 0 and self.labels[-1] == ''
+
+    def is_wild(self):
+        """Is this name wild?  (I.e. Is the least significant label '*'?)
+        @rtype: bool
+        """
+
+        return len(self.labels) > 0 and self.labels[0] == '*'
+
+    def __hash__(self):
+        """Return a case-insensitive hash of the name.
+        @rtype: int
+        """
+
+        h = 0L
+        for label in self.labels:
+            for c in label:
+                h += ( h << 3 ) + ord(c.lower())
+        return int(h % sys.maxint)
+
+    def fullcompare(self, other):
+        """Compare two names, returning a 3-tuple (relation, order, nlabels).
+
+        I{relation} describes the relation ship between the names,
+        and is one of: dns.name.NAMERELN_NONE,
+        dns.name.NAMERELN_SUPERDOMAIN, dns.name.NAMERELN_SUBDOMAIN,
+        dns.name.NAMERELN_EQUAL, or dns.name.NAMERELN_COMMONANCESTOR
+
+        I{order} is < 0 if self < other, > 0 if self > other, and ==
+        0 if self == other.  A relative name is always less than an
+        absolute name.  If both names have the same relativity, then
+        the DNSSEC order relation is used to order them.
+
+        I{nlabels} is the number of significant labels that the two names
+        have in common.
+        """
+
+        sabs = self.is_absolute()
+        oabs = other.is_absolute()
+        if sabs != oabs:
+            if sabs:
+                return (NAMERELN_NONE, 1, 0)
+            else:
+                return (NAMERELN_NONE, -1, 0)
+        l1 = len(self.labels)
+        l2 = len(other.labels)
+        ldiff = l1 - l2
+        if ldiff < 0:
+            l = l1
+        else:
+            l = l2
+
+        order = 0
+        nlabels = 0
+        namereln = NAMERELN_NONE
+        while l > 0:
+            l -= 1
+            l1 -= 1
+            l2 -= 1
+            label1 = self.labels[l1].lower()
+            label2 = other.labels[l2].lower()
+            if label1 < label2:
+                order = -1
+                if nlabels > 0:
+                    namereln = NAMERELN_COMMONANCESTOR
+                return (namereln, order, nlabels)
+            elif label1 > label2:
+                order = 1
+                if nlabels > 0:
+                    namereln = NAMERELN_COMMONANCESTOR
+                return (namereln, order, nlabels)
+            nlabels += 1
+        order = ldiff
+        if ldiff < 0:
+            namereln = NAMERELN_SUPERDOMAIN
+        elif ldiff > 0:
+            namereln = NAMERELN_SUBDOMAIN
+        else:
+            namereln = NAMERELN_EQUAL
+        return (namereln, order, nlabels)
+
+    def is_subdomain(self, other):
+        """Is self a subdomain of other?
+
+        The notion of subdomain includes equality.
+        @rtype: bool
+        """
+
+        (nr, o, nl) = self.fullcompare(other)
+        if nr == NAMERELN_SUBDOMAIN or nr == NAMERELN_EQUAL:
+            return True
+        return False
+
+    def is_superdomain(self, other):
+        """Is self a superdomain of other?
+
+        The notion of subdomain includes equality.
+        @rtype: bool
+        """
+
+        (nr, o, nl) = self.fullcompare(other)
+        if nr == NAMERELN_SUPERDOMAIN or nr == NAMERELN_EQUAL:
+            return True
+        return False
+
+    def canonicalize(self):
+        """Return a name which is equal to the current name, but is in
+        DNSSEC canonical form.
+        @rtype: dns.name.Name object
+        """
+
+        return Name([x.lower() for x in self.labels])
+
+    def __eq__(self, other):
+        if isinstance(other, Name):
+            return self.fullcompare(other)[1] == 0
+        else:
+            return False
+
+    def __ne__(self, other):
+        if isinstance(other, Name):
+            return self.fullcompare(other)[1] != 0
+        else:
+            return True
+
+    def __lt__(self, other):
+        if isinstance(other, Name):
+            return self.fullcompare(other)[1] < 0
+        else:
+            return NotImplemented
+
+    def __le__(self, other):
+        if isinstance(other, Name):
+            return self.fullcompare(other)[1] <= 0
+        else:
+            return NotImplemented
+
+    def __ge__(self, other):
+        if isinstance(other, Name):
+            return self.fullcompare(other)[1] >= 0
+        else:
+            return NotImplemented
+
+    def __gt__(self, other):
+        if isinstance(other, Name):
+            return self.fullcompare(other)[1] > 0
+        else:
+            return NotImplemented
+
+    def __repr__(self):
+        return '<DNS name ' + self.__str__() + '>'
+
+    def __str__(self):
+        return self.to_text(False)
+
+    def to_text(self, omit_final_dot = False):
+        """Convert name to text format.
+        @param omit_final_dot: If True, don't emit the final dot (denoting the
+        root label) for absolute names.  The default is False.
+        @rtype: string
+        """
+
+        if len(self.labels) == 0:
+            return '@'
+        if len(self.labels) == 1 and self.labels[0] == '':
+            return '.'
+        if omit_final_dot and self.is_absolute():
+            l = self.labels[:-1]
+        else:
+            l = self.labels
+        s = '.'.join(map(_escapify, l))
+        return s
+
+    def to_unicode(self, omit_final_dot = False):
+        """Convert name to Unicode text format.
+
+        IDN ACE lables are converted to Unicode.
+
+        @param omit_final_dot: If True, don't emit the final dot (denoting the
+        root label) for absolute names.  The default is False.
+        @rtype: string
+        """
+
+        if len(self.labels) == 0:
+            return u'@'
+        if len(self.labels) == 1 and self.labels[0] == '':
+            return u'.'
+        if omit_final_dot and self.is_absolute():
+            l = self.labels[:-1]
+        else:
+            l = self.labels
+        s = u'.'.join([encodings.idna.ToUnicode(_escapify(x)) for x in l])
+        return s
+
+    def to_digestable(self, origin=None):
+        """Convert name to a format suitable for digesting in hashes.
+
+        The name is canonicalized and converted to uncompressed wire format.
+
+        @param origin: If the name is relative and origin is not None, then
+        origin will be appended to it.
+        @type origin: dns.name.Name object
+        @raises NeedAbsoluteNameOrOrigin: All names in wire format are
+        absolute.  If self is a relative name, then an origin must be supplied;
+        if it is missing, then this exception is raised
+        @rtype: string
+        """
+
+        if not self.is_absolute():
+            if origin is None or not origin.is_absolute():
+                raise NeedAbsoluteNameOrOrigin
+            labels = list(self.labels)
+            labels.extend(list(origin.labels))
+        else:
+            labels = self.labels
+        dlabels = ["%s%s" % (chr(len(x)), x.lower()) for x in labels]
+        return ''.join(dlabels)
+
+    def to_wire(self, file = None, compress = None, origin = None):
+        """Convert name to wire format, possibly compressing it.
+
+        @param file: the file where the name is emitted (typically
+        a cStringIO file).  If None, a string containing the wire name
+        will be returned.
+        @type file: file or None
+        @param compress: The compression table.  If None (the default) names
+        will not be compressed.
+        @type compress: dict
+        @param origin: If the name is relative and origin is not None, then
+        origin will be appended to it.
+        @type origin: dns.name.Name object
+        @raises NeedAbsoluteNameOrOrigin: All names in wire format are
+        absolute.  If self is a relative name, then an origin must be supplied;
+        if it is missing, then this exception is raised
+        """
+
+        if file is None:
+            file = cStringIO.StringIO()
+            want_return = True
+        else:
+            want_return = False
+
+        if not self.is_absolute():
+            if origin is None or not origin.is_absolute():
+                raise NeedAbsoluteNameOrOrigin
+            labels = list(self.labels)
+            labels.extend(list(origin.labels))
+        else:
+            labels = self.labels
+        i = 0
+        for label in labels:
+            n = Name(labels[i:])
+            i += 1
+            if not compress is None:
+                pos = compress.get(n)
+            else:
+                pos = None
+            if not pos is None:
+                value = 0xc000 + pos
+                s = struct.pack('!H', value)
+                file.write(s)
+                break
+            else:
+                if not compress is None and len(n) > 1:
+                    pos = file.tell()
+                    if pos < 0xc000:
+                        compress[n] = pos
+                l = len(label)
+                file.write(chr(l))
+                if l > 0:
+                    file.write(label)
+        if want_return:
+            return file.getvalue()
+
+    def __len__(self):
+        """The length of the name (in labels).
+        @rtype: int
+        """
+
+        return len(self.labels)
+
+    def __getitem__(self, index):
+        return self.labels[index]
+
+    def __getslice__(self, start, stop):
+        return self.labels[start:stop]
+
+    def __add__(self, other):
+        return self.concatenate(other)
+
+    def __sub__(self, other):
+        return self.relativize(other)
+
+    def split(self, depth):
+        """Split a name into a prefix and suffix at depth.
+
+        @param depth: the number of labels in the suffix
+        @type depth: int
+        @raises ValueError: the depth was not >= 0 and <= the length of the
+        name.
+        @returns: the tuple (prefix, suffix)
+        @rtype: tuple
+        """
+
+        l = len(self.labels)
+        if depth == 0:
+            return (self, dns.name.empty)
+        elif depth == l:
+            return (dns.name.empty, self)
+        elif depth < 0 or depth > l:
+            raise ValueError('depth must be >= 0 and <= the length of the name')
+        return (Name(self[: -depth]), Name(self[-depth :]))
+
+    def concatenate(self, other):
+        """Return a new name which is the concatenation of self and other.
+        @rtype: dns.name.Name object
+        @raises AbsoluteConcatenation: self is absolute and other is
+        not the empty name
+        """
+
+        if self.is_absolute() and len(other) > 0:
+            raise AbsoluteConcatenation
+        labels = list(self.labels)
+        labels.extend(list(other.labels))
+        return Name(labels)
+
+    def relativize(self, origin):
+        """If self is a subdomain of origin, return a new name which is self
+        relative to origin.  Otherwise return self.
+        @rtype: dns.name.Name object
+        """
+
+        if not origin is None and self.is_subdomain(origin):
+            return Name(self[: -len(origin)])
+        else:
+            return self
+
+    def derelativize(self, origin):
+        """If self is a relative name, return a new name which is the
+        concatenation of self and origin.  Otherwise return self.
+        @rtype: dns.name.Name object
+        """
+
+        if not self.is_absolute():
+            return self.concatenate(origin)
+        else:
+            return self
+
+    def choose_relativity(self, origin=None, relativize=True):
+        """Return a name with the relativity desired by the caller.  If
+        origin is None, then self is returned.  Otherwise, if
+        relativize is true the name is relativized, and if relativize is
+        false the name is derelativized.
+        @rtype: dns.name.Name object
+        """
+
+        if origin:
+            if relativize:
+                return self.relativize(origin)
+            else:
+                return self.derelativize(origin)
+        else:
+            return self
+
+    def parent(self):
+        """Return the parent of the name.
+        @rtype: dns.name.Name object
+        @raises NoParent: the name is either the root name or the empty name,
+        and thus has no parent.
+        """
+        if self == root or self == empty:
+            raise NoParent
+        return Name(self.labels[1:])
+
+root = Name([''])
+empty = Name([])
+
+def from_unicode(text, origin = root):
+    """Convert unicode text into a Name object.
+
+    Lables are encoded in IDN ACE form.
+
+    @rtype: dns.name.Name object
+    """
+
+    if not isinstance(text, unicode):
+        raise ValueError("input to from_unicode() must be a unicode string")
+    if not (origin is None or isinstance(origin, Name)):
+        raise ValueError("origin must be a Name or None")
+    labels = []
+    label = u''
+    escaping = False
+    edigits = 0
+    total = 0
+    if text == u'@':
+        text = u''
+    if text:
+        if text == u'.':
+            return Name([''])	# no Unicode "u" on this constant!
+        for c in text:
+            if escaping:
+                if edigits == 0:
+                    if c.isdigit():
+                        total = int(c)
+                        edigits += 1
+                    else:
+                        label += c
+                        escaping = False
+                else:
+                    if not c.isdigit():
+                        raise BadEscape
+                    total *= 10
+                    total += int(c)
+                    edigits += 1
+                    if edigits == 3:
+                        escaping = False
+                        label += chr(total)
+            elif c == u'.' or c == u'\u3002' or \
+                 c == u'\uff0e' or c == u'\uff61':
+                if len(label) == 0:
+                    raise EmptyLabel
+                labels.append(encodings.idna.ToASCII(label))
+                label = u''
+            elif c == u'\\':
+                escaping = True
+                edigits = 0
+                total = 0
+            else:
+                label += c
+        if escaping:
+            raise BadEscape
+        if len(label) > 0:
+            labels.append(encodings.idna.ToASCII(label))
+        else:
+            labels.append('')
+    if (len(labels) == 0 or labels[-1] != '') and not origin is None:
+        labels.extend(list(origin.labels))
+    return Name(labels)
+
+def from_text(text, origin = root):
+    """Convert text into a Name object.
+    @rtype: dns.name.Name object
+    """
+
+    if not isinstance(text, str):
+        if isinstance(text, unicode) and sys.hexversion >= 0x02030000:
+            return from_unicode(text, origin)
+        else:
+            raise ValueError("input to from_text() must be a string")
+    if not (origin is None or isinstance(origin, Name)):
+        raise ValueError("origin must be a Name or None")
+    labels = []
+    label = ''
+    escaping = False
+    edigits = 0
+    total = 0
+    if text == '@':
+        text = ''
+    if text:
+        if text == '.':
+            return Name([''])
+        for c in text:
+            if escaping:
+                if edigits == 0:
+                    if c.isdigit():
+                        total = int(c)
+                        edigits += 1
+                    else:
+                        label += c
+                        escaping = False
+                else:
+                    if not c.isdigit():
+                        raise BadEscape
+                    total *= 10
+                    total += int(c)
+                    edigits += 1
+                    if edigits == 3:
+                        escaping = False
+                        label += chr(total)
+            elif c == '.':
+                if len(label) == 0:
+                    raise EmptyLabel
+                labels.append(label)
+                label = ''
+            elif c == '\\':
+                escaping = True
+                edigits = 0
+                total = 0
+            else:
+                label += c
+        if escaping:
+            raise BadEscape
+        if len(label) > 0:
+            labels.append(label)
+        else:
+            labels.append('')
+    if (len(labels) == 0 or labels[-1] != '') and not origin is None:
+        labels.extend(list(origin.labels))
+    return Name(labels)
+
+def from_wire(message, current):
+    """Convert possibly compressed wire format into a Name.
+    @param message: the entire DNS message
+    @type message: string
+    @param current: the offset of the beginning of the name from the start
+    of the message
+    @type current: int
+    @raises dns.name.BadPointer: a compression pointer did not point backwards
+    in the message
+    @raises dns.name.BadLabelType: an invalid label type was encountered.
+    @returns: a tuple consisting of the name that was read and the number
+    of bytes of the wire format message which were consumed reading it
+    @rtype: (dns.name.Name object, int) tuple
+    """
+
+    if not isinstance(message, str):
+        raise ValueError("input to from_wire() must be a byte string")
+    labels = []
+    biggest_pointer = current
+    hops = 0
+    count = ord(message[current])
+    current += 1
+    cused = 1
+    while count != 0:
+        if count < 64:
+            labels.append(message[current : current + count])
+            current += count
+            if hops == 0:
+                cused += count
+        elif count >= 192:
+            current = (count & 0x3f) * 256 + ord(message[current])
+            if hops == 0:
+                cused += 1
+            if current >= biggest_pointer:
+                raise BadPointer
+            biggest_pointer = current
+            hops += 1
+        else:
+            raise BadLabelType
+        count = ord(message[current])
+        current += 1
+        if hops == 0:
+            cused += 1
+    labels.append('')
+    return (Name(labels), cused)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/namedict.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/namedict.py
new file mode 100644
index 0000000..54afb77
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/namedict.py
@@ -0,0 +1,59 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""DNS name dictionary"""
+
+import dns.name
+
+class NameDict(dict):
+
+    """A dictionary whose keys are dns.name.Name objects.
+    @ivar max_depth: the maximum depth of the keys that have ever been
+    added to the dictionary.
+    @type max_depth: int
+    """
+
+    def __init__(self, *args, **kwargs):
+        super(NameDict, self).__init__(*args, **kwargs)
+        self.max_depth = 0
+
+    def __setitem__(self, key, value):
+        if not isinstance(key, dns.name.Name):
+            raise ValueError('NameDict key must be a name')
+        depth = len(key)
+        if depth > self.max_depth:
+            self.max_depth = depth
+        super(NameDict, self).__setitem__(key, value)
+
+    def get_deepest_match(self, name):
+        """Find the deepest match to I{name} in the dictionary.
+
+        The deepest match is the longest name in the dictionary which is
+        a superdomain of I{name}.
+
+        @param name: the name
+        @type name: dns.name.Name object
+        @rtype: (key, value) tuple
+        """
+
+        depth = len(name)
+        if depth > self.max_depth:
+            depth = self.max_depth
+        for i in xrange(-depth, 0):
+            n = dns.name.Name(name[i:])
+            if self.has_key(n):
+                return (n, self[n])
+        v = self[dns.name.empty]
+        return (dns.name.empty, v)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/node.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/node.py
new file mode 100644
index 0000000..07fff92
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/node.py
@@ -0,0 +1,172 @@
+# Copyright (C) 2001-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""DNS nodes.  A node is a set of rdatasets."""
+
+import StringIO
+
+import dns.rdataset
+import dns.rdatatype
+import dns.renderer
+
+class Node(object):
+    """A DNS node.
+    
+    A node is a set of rdatasets
+
+    @ivar rdatasets: the node's rdatasets
+    @type rdatasets: list of dns.rdataset.Rdataset objects"""
+
+    __slots__ = ['rdatasets']
+    
+    def __init__(self):
+        """Initialize a DNS node.
+        """
+        
+        self.rdatasets = [];
+
+    def to_text(self, name, **kw):
+        """Convert a node to text format.
+
+        Each rdataset at the node is printed.  Any keyword arguments
+        to this method are passed on to the rdataset's to_text() method.
+        @param name: the owner name of the rdatasets
+        @type name: dns.name.Name object
+        @rtype: string
+        """
+        
+        s = StringIO.StringIO()
+        for rds in self.rdatasets:
+            print >> s, rds.to_text(name, **kw)
+        return s.getvalue()[:-1]
+
+    def __repr__(self):
+        return '<DNS node ' + str(id(self)) + '>'
+    
+    def __eq__(self, other):
+        """Two nodes are equal if they have the same rdatasets.
+
+        @rtype: bool
+        """
+        #
+        # This is inefficient.  Good thing we don't need to do it much.
+        #
+        for rd in self.rdatasets:
+            if rd not in other.rdatasets:
+                return False
+        for rd in other.rdatasets:
+            if rd not in self.rdatasets:
+                return False
+        return True
+
+    def __ne__(self, other):
+        return not self.__eq__(other)
+        
+    def __len__(self):
+        return len(self.rdatasets)
+
+    def __iter__(self):
+        return iter(self.rdatasets)
+
+    def find_rdataset(self, rdclass, rdtype, covers=dns.rdatatype.NONE,
+                      create=False):
+        """Find an rdataset matching the specified properties in the
+        current node.
+
+        @param rdclass: The class of the rdataset
+        @type rdclass: int
+        @param rdtype: The type of the rdataset
+        @type rdtype: int
+        @param covers: The covered type.  Usually this value is
+        dns.rdatatype.NONE, but if the rdtype is dns.rdatatype.SIG or
+        dns.rdatatype.RRSIG, then the covers value will be the rdata
+        type the SIG/RRSIG covers.  The library treats the SIG and RRSIG
+        types as if they were a family of
+        types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA).  This makes RRSIGs much
+        easier to work with than if RRSIGs covering different rdata
+        types were aggregated into a single RRSIG rdataset.
+        @type covers: int
+        @param create: If True, create the rdataset if it is not found.
+        @type create: bool
+        @raises KeyError: An rdataset of the desired type and class does
+        not exist and I{create} is not True.
+        @rtype: dns.rdataset.Rdataset object
+        """
+
+        for rds in self.rdatasets:
+            if rds.match(rdclass, rdtype, covers):
+                return rds
+        if not create:
+            raise KeyError
+        rds = dns.rdataset.Rdataset(rdclass, rdtype)
+        self.rdatasets.append(rds)
+        return rds
+
+    def get_rdataset(self, rdclass, rdtype, covers=dns.rdatatype.NONE,
+                     create=False):
+        """Get an rdataset matching the specified properties in the
+        current node.
+
+        None is returned if an rdataset of the specified type and
+        class does not exist and I{create} is not True.
+
+        @param rdclass: The class of the rdataset
+        @type rdclass: int
+        @param rdtype: The type of the rdataset
+        @type rdtype: int
+        @param covers: The covered type.
+        @type covers: int
+        @param create: If True, create the rdataset if it is not found.
+        @type create: bool
+        @rtype: dns.rdataset.Rdataset object or None
+        """
+
+        try:
+            rds = self.find_rdataset(rdclass, rdtype, covers, create)
+        except KeyError:
+            rds = None
+        return rds
+
+    def delete_rdataset(self, rdclass, rdtype, covers=dns.rdatatype.NONE):
+        """Delete the rdataset matching the specified properties in the
+        current node.
+
+        If a matching rdataset does not exist, it is not an error.
+
+        @param rdclass: The class of the rdataset
+        @type rdclass: int
+        @param rdtype: The type of the rdataset
+        @type rdtype: int
+        @param covers: The covered type.
+        @type covers: int
+        """
+
+        rds = self.get_rdataset(rdclass, rdtype, covers)
+        if not rds is None:
+            self.rdatasets.remove(rds)
+
+    def replace_rdataset(self, replacement):
+        """Replace an rdataset.
+        
+        It is not an error if there is no rdataset matching I{replacement}.
+
+        Ownership of the I{replacement} object is transferred to the node;
+        in other words, this method does not store a copy of I{replacement}
+        at the node, it stores I{replacement} itself.
+        """
+
+        self.delete_rdataset(replacement.rdclass, replacement.rdtype,
+                             replacement.covers)
+        self.rdatasets.append(replacement)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/opcode.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/opcode.py
new file mode 100644
index 0000000..705bd09
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/opcode.py
@@ -0,0 +1,104 @@
+# Copyright (C) 2001-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""DNS Opcodes."""
+
+import dns.exception
+
+QUERY = 0
+IQUERY = 1
+STATUS = 2
+NOTIFY = 4
+UPDATE = 5
+
+_by_text = {
+    'QUERY' : QUERY,
+    'IQUERY' : IQUERY,
+    'STATUS' : STATUS,
+    'NOTIFY' : NOTIFY,
+    'UPDATE' : UPDATE
+}
+
+# We construct the inverse mapping programmatically to ensure that we
+# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that
+# would cause the mapping not to be true inverse.
+
+_by_value = dict([(y, x) for x, y in _by_text.iteritems()])
+
+
+class UnknownOpcode(dns.exception.DNSException):
+    """Raised if an opcode is unknown."""
+    pass
+
+def from_text(text):
+    """Convert text into an opcode.
+
+    @param text: the textual opcode
+    @type text: string
+    @raises UnknownOpcode: the opcode is unknown
+    @rtype: int
+    """
+
+    if text.isdigit():
+        value = int(text)
+        if value >= 0 and value <= 15:
+            return value
+    value = _by_text.get(text.upper())
+    if value is None:
+        raise UnknownOpcode
+    return value
+
+def from_flags(flags):
+    """Extract an opcode from DNS message flags.
+
+    @param flags: int
+    @rtype: int
+    """
+    
+    return (flags & 0x7800) >> 11
+
+def to_flags(value):
+    """Convert an opcode to a value suitable for ORing into DNS message
+    flags.
+    @rtype: int
+    """
+    
+    return (value << 11) & 0x7800
+    
+def to_text(value):
+    """Convert an opcode to text.
+
+    @param value: the opcdoe
+    @type value: int
+    @raises UnknownOpcode: the opcode is unknown
+    @rtype: string
+    """
+    
+    text = _by_value.get(value)
+    if text is None:
+        text = str(value)
+    return text
+
+def is_update(flags):
+    """True if the opcode in flags is UPDATE.
+
+    @param flags: DNS flags
+    @type flags: int
+    @rtype: bool
+    """
+    
+    if (from_flags(flags) == UPDATE):
+        return True
+    return False
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/query.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/query.py
new file mode 100644
index 0000000..c023b14
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/query.py
@@ -0,0 +1,428 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""Talk to a DNS server."""
+
+from __future__ import generators
+
+import errno
+import select
+import socket
+import struct
+import sys
+import time
+
+import dns.exception
+import dns.inet
+import dns.name
+import dns.message
+import dns.rdataclass
+import dns.rdatatype
+
+class UnexpectedSource(dns.exception.DNSException):
+    """Raised if a query response comes from an unexpected address or port."""
+    pass
+
+class BadResponse(dns.exception.FormError):
+    """Raised if a query response does not respond to the question asked."""
+    pass
+
+def _compute_expiration(timeout):
+    if timeout is None:
+        return None
+    else:
+        return time.time() + timeout
+
+def _wait_for(ir, iw, ix, expiration):
+    done = False
+    while not done:
+        if expiration is None:
+            timeout = None
+        else:
+            timeout = expiration - time.time()
+            if timeout <= 0.0:
+                raise dns.exception.Timeout
+        try:
+            if timeout is None:
+                (r, w, x) = select.select(ir, iw, ix)
+            else:
+                (r, w, x) = select.select(ir, iw, ix, timeout)
+        except select.error, e:
+            if e.args[0] != errno.EINTR:
+                raise e
+        done = True
+        if len(r) == 0 and len(w) == 0 and len(x) == 0:
+            raise dns.exception.Timeout
+
+def _wait_for_readable(s, expiration):
+    _wait_for([s], [], [s], expiration)
+
+def _wait_for_writable(s, expiration):
+    _wait_for([], [s], [s], expiration)
+
+def _addresses_equal(af, a1, a2):
+    # Convert the first value of the tuple, which is a textual format
+    # address into binary form, so that we are not confused by different
+    # textual representations of the same address
+    n1 = dns.inet.inet_pton(af, a1[0])
+    n2 = dns.inet.inet_pton(af, a2[0])
+    return n1 == n2 and a1[1:] == a2[1:]
+
+def udp(q, where, timeout=None, port=53, af=None, source=None, source_port=0,
+        ignore_unexpected=False, one_rr_per_rrset=False):
+    """Return the response obtained after sending a query via UDP.
+
+    @param q: the query
+    @type q: dns.message.Message
+    @param where: where to send the message
+    @type where: string containing an IPv4 or IPv6 address
+    @param timeout: The number of seconds to wait before the query times out.
+    If None, the default, wait forever.
+    @type timeout: float
+    @param port: The port to which to send the message.  The default is 53.
+    @type port: int
+    @param af: the address family to use.  The default is None, which
+    causes the address family to use to be inferred from the form of of where.
+    If the inference attempt fails, AF_INET is used.
+    @type af: int
+    @rtype: dns.message.Message object
+    @param source: source address.  The default is the IPv4 wildcard address.
+    @type source: string
+    @param source_port: The port from which to send the message.
+    The default is 0.
+    @type source_port: int
+    @param ignore_unexpected: If True, ignore responses from unexpected
+    sources.  The default is False.
+    @type ignore_unexpected: bool
+    @param one_rr_per_rrset: Put each RR into its own RRset
+    @type one_rr_per_rrset: bool
+    """
+
+    wire = q.to_wire()
+    if af is None:
+        try:
+            af = dns.inet.af_for_address(where)
+        except:
+            af = dns.inet.AF_INET
+    if af == dns.inet.AF_INET:
+        destination = (where, port)
+        if source is not None:
+            source = (source, source_port)
+    elif af == dns.inet.AF_INET6:
+        destination = (where, port, 0, 0)
+        if source is not None:
+            source = (source, source_port, 0, 0)
+    s = socket.socket(af, socket.SOCK_DGRAM, 0)
+    try:
+        expiration = _compute_expiration(timeout)
+        s.setblocking(0)
+        if source is not None:
+            s.bind(source)
+        _wait_for_writable(s, expiration)
+        s.sendto(wire, destination)
+        while 1:
+            _wait_for_readable(s, expiration)
+            (wire, from_address) = s.recvfrom(65535)
+            if _addresses_equal(af, from_address, destination) or \
+                    (dns.inet.is_multicast(where) and \
+                         from_address[1:] == destination[1:]):
+                break
+            if not ignore_unexpected:
+                raise UnexpectedSource('got a response from '
+                                       '%s instead of %s' % (from_address,
+                                                             destination))
+    finally:
+        s.close()
+    r = dns.message.from_wire(wire, keyring=q.keyring, request_mac=q.mac,
+                              one_rr_per_rrset=one_rr_per_rrset)
+    if not q.is_response(r):
+        raise BadResponse
+    return r
+
+def _net_read(sock, count, expiration):
+    """Read the specified number of bytes from sock.  Keep trying until we
+    either get the desired amount, or we hit EOF.
+    A Timeout exception will be raised if the operation is not completed
+    by the expiration time.
+    """
+    s = ''
+    while count > 0:
+        _wait_for_readable(sock, expiration)
+        n = sock.recv(count)
+        if n == '':
+            raise EOFError
+        count = count - len(n)
+        s = s + n
+    return s
+
+def _net_write(sock, data, expiration):
+    """Write the specified data to the socket.
+    A Timeout exception will be raised if the operation is not completed
+    by the expiration time.
+    """
+    current = 0
+    l = len(data)
+    while current < l:
+        _wait_for_writable(sock, expiration)
+        current += sock.send(data[current:])
+
+def _connect(s, address):
+    try:
+        s.connect(address)
+    except socket.error:
+        (ty, v) = sys.exc_info()[:2]
+        if v[0] != errno.EINPROGRESS and \
+               v[0] != errno.EWOULDBLOCK and \
+               v[0] != errno.EALREADY:
+            raise v
+
+def tcp(q, where, timeout=None, port=53, af=None, source=None, source_port=0,
+        one_rr_per_rrset=False):
+    """Return the response obtained after sending a query via TCP.
+
+    @param q: the query
+    @type q: dns.message.Message object
+    @param where: where to send the message
+    @type where: string containing an IPv4 or IPv6 address
+    @param timeout: The number of seconds to wait before the query times out.
+    If None, the default, wait forever.
+    @type timeout: float
+    @param port: The port to which to send the message.  The default is 53.
+    @type port: int
+    @param af: the address family to use.  The default is None, which
+    causes the address family to use to be inferred from the form of of where.
+    If the inference attempt fails, AF_INET is used.
+    @type af: int
+    @rtype: dns.message.Message object
+    @param source: source address.  The default is the IPv4 wildcard address.
+    @type source: string
+    @param source_port: The port from which to send the message.
+    The default is 0.
+    @type source_port: int
+    @param one_rr_per_rrset: Put each RR into its own RRset
+    @type one_rr_per_rrset: bool
+    """
+
+    wire = q.to_wire()
+    if af is None:
+        try:
+            af = dns.inet.af_for_address(where)
+        except:
+            af = dns.inet.AF_INET
+    if af == dns.inet.AF_INET:
+        destination = (where, port)
+        if source is not None:
+            source = (source, source_port)
+    elif af == dns.inet.AF_INET6:
+        destination = (where, port, 0, 0)
+        if source is not None:
+            source = (source, source_port, 0, 0)
+    s = socket.socket(af, socket.SOCK_STREAM, 0)
+    try:
+        expiration = _compute_expiration(timeout)
+        s.setblocking(0)
+        if source is not None:
+            s.bind(source)
+        _connect(s, destination)
+
+        l = len(wire)
+
+        # copying the wire into tcpmsg is inefficient, but lets us
+        # avoid writev() or doing a short write that would get pushed
+        # onto the net
+        tcpmsg = struct.pack("!H", l) + wire
+        _net_write(s, tcpmsg, expiration)
+        ldata = _net_read(s, 2, expiration)
+        (l,) = struct.unpack("!H", ldata)
+        wire = _net_read(s, l, expiration)
+    finally:
+        s.close()
+    r = dns.message.from_wire(wire, keyring=q.keyring, request_mac=q.mac,
+                              one_rr_per_rrset=one_rr_per_rrset)
+    if not q.is_response(r):
+        raise BadResponse
+    return r
+
+def xfr(where, zone, rdtype=dns.rdatatype.AXFR, rdclass=dns.rdataclass.IN,
+        timeout=None, port=53, keyring=None, keyname=None, relativize=True,
+        af=None, lifetime=None, source=None, source_port=0, serial=0,
+        use_udp=False, keyalgorithm=dns.tsig.default_algorithm):
+    """Return a generator for the responses to a zone transfer.
+
+    @param where: where to send the message
+    @type where: string containing an IPv4 or IPv6 address
+    @param zone: The name of the zone to transfer
+    @type zone: dns.name.Name object or string
+    @param rdtype: The type of zone transfer.  The default is
+    dns.rdatatype.AXFR.
+    @type rdtype: int or string
+    @param rdclass: The class of the zone transfer.  The default is
+    dns.rdatatype.IN.
+    @type rdclass: int or string
+    @param timeout: The number of seconds to wait for each response message.
+    If None, the default, wait forever.
+    @type timeout: float
+    @param port: The port to which to send the message.  The default is 53.
+    @type port: int
+    @param keyring: The TSIG keyring to use
+    @type keyring: dict
+    @param keyname: The name of the TSIG key to use
+    @type keyname: dns.name.Name object or string
+    @param relativize: If True, all names in the zone will be relativized to
+    the zone origin.  It is essential that the relativize setting matches
+    the one specified to dns.zone.from_xfr().
+    @type relativize: bool
+    @param af: the address family to use.  The default is None, which
+    causes the address family to use to be inferred from the form of of where.
+    If the inference attempt fails, AF_INET is used.
+    @type af: int
+    @param lifetime: The total number of seconds to spend doing the transfer.
+    If None, the default, then there is no limit on the time the transfer may
+    take.
+    @type lifetime: float
+    @rtype: generator of dns.message.Message objects.
+    @param source: source address.  The default is the IPv4 wildcard address.
+    @type source: string
+    @param source_port: The port from which to send the message.
+    The default is 0.
+    @type source_port: int
+    @param serial: The SOA serial number to use as the base for an IXFR diff
+    sequence (only meaningful if rdtype == dns.rdatatype.IXFR).
+    @type serial: int
+    @param use_udp: Use UDP (only meaningful for IXFR)
+    @type use_udp: bool
+    @param keyalgorithm: The TSIG algorithm to use; defaults to
+    dns.tsig.default_algorithm
+    @type keyalgorithm: string
+    """
+
+    if isinstance(zone, (str, unicode)):
+        zone = dns.name.from_text(zone)
+    if isinstance(rdtype, str):
+        rdtype = dns.rdatatype.from_text(rdtype)
+    q = dns.message.make_query(zone, rdtype, rdclass)
+    if rdtype == dns.rdatatype.IXFR:
+        rrset = dns.rrset.from_text(zone, 0, 'IN', 'SOA',
+                                    '. . %u 0 0 0 0' % serial)
+        q.authority.append(rrset)
+    if not keyring is None:
+        q.use_tsig(keyring, keyname, algorithm=keyalgorithm)
+    wire = q.to_wire()
+    if af is None:
+        try:
+            af = dns.inet.af_for_address(where)
+        except:
+            af = dns.inet.AF_INET
+    if af == dns.inet.AF_INET:
+        destination = (where, port)
+        if source is not None:
+            source = (source, source_port)
+    elif af == dns.inet.AF_INET6:
+        destination = (where, port, 0, 0)
+        if source is not None:
+            source = (source, source_port, 0, 0)
+    if use_udp:
+        if rdtype != dns.rdatatype.IXFR:
+            raise ValueError('cannot do a UDP AXFR')
+        s = socket.socket(af, socket.SOCK_DGRAM, 0)
+    else:
+        s = socket.socket(af, socket.SOCK_STREAM, 0)
+    s.setblocking(0)
+    if source is not None:
+        s.bind(source)
+    expiration = _compute_expiration(lifetime)
+    _connect(s, destination)
+    l = len(wire)
+    if use_udp:
+        _wait_for_writable(s, expiration)
+        s.send(wire)
+    else:
+        tcpmsg = struct.pack("!H", l) + wire
+        _net_write(s, tcpmsg, expiration)
+    done = False
+    soa_rrset = None
+    soa_count = 0
+    if relativize:
+        origin = zone
+        oname = dns.name.empty
+    else:
+        origin = None
+        oname = zone
+    tsig_ctx = None
+    first = True
+    while not done:
+        mexpiration = _compute_expiration(timeout)
+        if mexpiration is None or mexpiration > expiration:
+            mexpiration = expiration
+        if use_udp:
+            _wait_for_readable(s, expiration)
+            (wire, from_address) = s.recvfrom(65535)
+        else:
+            ldata = _net_read(s, 2, mexpiration)
+            (l,) = struct.unpack("!H", ldata)
+            wire = _net_read(s, l, mexpiration)
+        r = dns.message.from_wire(wire, keyring=q.keyring, request_mac=q.mac,
+                                  xfr=True, origin=origin, tsig_ctx=tsig_ctx,
+                                  multi=True, first=first,
+                                  one_rr_per_rrset=(rdtype==dns.rdatatype.IXFR))
+        tsig_ctx = r.tsig_ctx
+        first = False
+        answer_index = 0
+        delete_mode = False
+        expecting_SOA = False
+        if soa_rrset is None:
+            if not r.answer or r.answer[0].name != oname:
+                raise dns.exception.FormError
+            rrset = r.answer[0]
+            if rrset.rdtype != dns.rdatatype.SOA:
+                raise dns.exception.FormError("first RRset is not an SOA")
+            answer_index = 1
+            soa_rrset = rrset.copy()
+            if rdtype == dns.rdatatype.IXFR:
+                if soa_rrset[0].serial == serial:
+                    #
+                    # We're already up-to-date.
+                    #
+                    done = True
+                else:
+                    expecting_SOA = True
+        #
+        # Process SOAs in the answer section (other than the initial
+        # SOA in the first message).
+        #
+        for rrset in r.answer[answer_index:]:
+            if done:
+                raise dns.exception.FormError("answers after final SOA")
+            if rrset.rdtype == dns.rdatatype.SOA and rrset.name == oname:
+                if expecting_SOA:
+                    if rrset[0].serial != serial:
+                        raise dns.exception.FormError("IXFR base serial mismatch")
+                    expecting_SOA = False
+                elif rdtype == dns.rdatatype.IXFR:
+                    delete_mode = not delete_mode
+                if rrset == soa_rrset and not delete_mode:
+                    done = True
+            elif expecting_SOA:
+                #
+                # We made an IXFR request and are expecting another
+                # SOA RR, but saw something else, so this must be an
+                # AXFR response.
+                #
+                rdtype = dns.rdatatype.AXFR
+                expecting_SOA = False
+        if done and q.keyring and not r.had_tsig:
+            raise dns.exception.FormError("missing TSIG")
+        yield r
+    s.close()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rcode.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rcode.py
new file mode 100644
index 0000000..c055f2e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rcode.py
@@ -0,0 +1,119 @@
+# Copyright (C) 2001-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""DNS Result Codes."""
+
+import dns.exception
+
+NOERROR = 0
+FORMERR = 1
+SERVFAIL = 2
+NXDOMAIN = 3
+NOTIMP = 4
+REFUSED = 5
+YXDOMAIN = 6
+YXRRSET = 7
+NXRRSET = 8
+NOTAUTH = 9
+NOTZONE = 10
+BADVERS = 16
+
+_by_text = {
+    'NOERROR' : NOERROR,
+    'FORMERR' : FORMERR,
+    'SERVFAIL' : SERVFAIL,
+    'NXDOMAIN' : NXDOMAIN,
+    'NOTIMP' : NOTIMP,
+    'REFUSED' : REFUSED,
+    'YXDOMAIN' : YXDOMAIN,
+    'YXRRSET' : YXRRSET,
+    'NXRRSET' : NXRRSET,
+    'NOTAUTH' : NOTAUTH,
+    'NOTZONE' : NOTZONE,
+    'BADVERS' : BADVERS
+}
+
+# We construct the inverse mapping programmatically to ensure that we
+# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that
+# would cause the mapping not to be a true inverse.
+
+_by_value = dict([(y, x) for x, y in _by_text.iteritems()])
+
+
+class UnknownRcode(dns.exception.DNSException):
+    """Raised if an rcode is unknown."""
+    pass
+
+def from_text(text):
+    """Convert text into an rcode.
+
+    @param text: the texual rcode
+    @type text: string
+    @raises UnknownRcode: the rcode is unknown
+    @rtype: int
+    """
+
+    if text.isdigit():
+        v = int(text)
+        if v >= 0 and v <= 4095:
+            return v
+    v = _by_text.get(text.upper())
+    if v is None:
+        raise UnknownRcode
+    return v
+
+def from_flags(flags, ednsflags):
+    """Return the rcode value encoded by flags and ednsflags.
+
+    @param flags: the DNS flags
+    @type flags: int
+    @param ednsflags: the EDNS flags
+    @type ednsflags: int
+    @raises ValueError: rcode is < 0 or > 4095
+    @rtype: int
+    """
+
+    value = (flags & 0x000f) | ((ednsflags >> 20) & 0xff0)
+    if value < 0 or value > 4095:
+        raise ValueError('rcode must be >= 0 and <= 4095')
+    return value
+
+def to_flags(value):
+    """Return a (flags, ednsflags) tuple which encodes the rcode.
+
+    @param value: the rcode
+    @type value: int
+    @raises ValueError: rcode is < 0 or > 4095
+    @rtype: (int, int) tuple
+    """
+
+    if value < 0 or value > 4095:
+        raise ValueError('rcode must be >= 0 and <= 4095')
+    v = value & 0xf
+    ev = long(value & 0xff0) << 20
+    return (v, ev)
+
+def to_text(value):
+    """Convert rcode into text.
+
+    @param value: the rcode
+    @type value: int
+    @rtype: string
+    """
+
+    text = _by_value.get(value)
+    if text is None:
+        text = str(value)
+    return text
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdata.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdata.py
new file mode 100644
index 0000000..ce02686
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdata.py
@@ -0,0 +1,456 @@
+# Copyright (C) 2001-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""DNS rdata.
+
+@var _rdata_modules: A dictionary mapping a (rdclass, rdtype) tuple to
+the module which implements that type.
+@type _rdata_modules: dict
+@var _module_prefix: The prefix to use when forming modules names.  The
+default is 'dns.rdtypes'.  Changing this value will break the library.
+@type _module_prefix: string
+@var _hex_chunk: At most this many octets that will be represented in each
+chunk of hexstring that _hexify() produces before whitespace occurs.
+@type _hex_chunk: int"""
+
+import cStringIO
+
+import dns.exception
+import dns.rdataclass
+import dns.rdatatype
+import dns.tokenizer
+
+_hex_chunksize = 32
+
+def _hexify(data, chunksize=None):
+    """Convert a binary string into its hex encoding, broken up into chunks
+    of I{chunksize} characters separated by a space.
+
+    @param data: the binary string
+    @type data: string
+    @param chunksize: the chunk size.  Default is L{dns.rdata._hex_chunksize}
+    @rtype: string
+    """
+
+    if chunksize is None:
+        chunksize = _hex_chunksize
+    hex = data.encode('hex_codec')
+    l = len(hex)
+    if l > chunksize:
+        chunks = []
+        i = 0
+        while i < l:
+            chunks.append(hex[i : i + chunksize])
+            i += chunksize
+        hex = ' '.join(chunks)
+    return hex
+
+_base64_chunksize = 32
+
+def _base64ify(data, chunksize=None):
+    """Convert a binary string into its base64 encoding, broken up into chunks
+    of I{chunksize} characters separated by a space.
+
+    @param data: the binary string
+    @type data: string
+    @param chunksize: the chunk size.  Default is
+    L{dns.rdata._base64_chunksize}
+    @rtype: string
+    """
+
+    if chunksize is None:
+        chunksize = _base64_chunksize
+    b64 = data.encode('base64_codec')
+    b64 = b64.replace('\n', '')
+    l = len(b64)
+    if l > chunksize:
+        chunks = []
+        i = 0
+        while i < l:
+            chunks.append(b64[i : i + chunksize])
+            i += chunksize
+        b64 = ' '.join(chunks)
+    return b64
+
+__escaped = {
+    '"' : True,
+    '\\' : True,
+    }
+
+def _escapify(qstring):
+    """Escape the characters in a quoted string which need it.
+
+    @param qstring: the string
+    @type qstring: string
+    @returns: the escaped string
+    @rtype: string
+    """
+
+    text = ''
+    for c in qstring:
+        if c in __escaped:
+            text += '\\' + c
+        elif ord(c) >= 0x20 and ord(c) < 0x7F:
+            text += c
+        else:
+            text += '\\%03d' % ord(c)
+    return text
+
+def _truncate_bitmap(what):
+    """Determine the index of greatest byte that isn't all zeros, and
+    return the bitmap that contains all the bytes less than that index.
+
+    @param what: a string of octets representing a bitmap.
+    @type what: string
+    @rtype: string
+    """
+
+    for i in xrange(len(what) - 1, -1, -1):
+        if what[i] != '\x00':
+            break
+    return ''.join(what[0 : i + 1])
+
+class Rdata(object):
+    """Base class for all DNS rdata types.
+    """
+
+    __slots__ = ['rdclass', 'rdtype']
+
+    def __init__(self, rdclass, rdtype):
+        """Initialize an rdata.
+        @param rdclass: The rdata class
+        @type rdclass: int
+        @param rdtype: The rdata type
+        @type rdtype: int
+        """
+
+        self.rdclass = rdclass
+        self.rdtype = rdtype
+
+    def covers(self):
+        """DNS SIG/RRSIG rdatas apply to a specific type; this type is
+        returned by the covers() function.  If the rdata type is not
+        SIG or RRSIG, dns.rdatatype.NONE is returned.  This is useful when
+        creating rdatasets, allowing the rdataset to contain only RRSIGs
+        of a particular type, e.g. RRSIG(NS).
+        @rtype: int
+        """
+
+        return dns.rdatatype.NONE
+
+    def extended_rdatatype(self):
+        """Return a 32-bit type value, the least significant 16 bits of
+        which are the ordinary DNS type, and the upper 16 bits of which are
+        the "covered" type, if any.
+        @rtype: int
+        """
+
+        return self.covers() << 16 | self.rdtype
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        """Convert an rdata to text format.
+        @rtype: string
+        """
+        raise NotImplementedError
+
+    def to_wire(self, file, compress = None, origin = None):
+        """Convert an rdata to wire format.
+        @rtype: string
+        """
+
+        raise NotImplementedError
+
+    def to_digestable(self, origin = None):
+        """Convert rdata to a format suitable for digesting in hashes.  This
+        is also the DNSSEC canonical form."""
+        f = cStringIO.StringIO()
+        self.to_wire(f, None, origin)
+        return f.getvalue()
+
+    def validate(self):
+        """Check that the current contents of the rdata's fields are
+        valid.  If you change an rdata by assigning to its fields,
+        it is a good idea to call validate() when you are done making
+        changes.
+        """
+        dns.rdata.from_text(self.rdclass, self.rdtype, self.to_text())
+
+    def __repr__(self):
+        covers = self.covers()
+        if covers == dns.rdatatype.NONE:
+            ctext = ''
+        else:
+            ctext = '(' + dns.rdatatype.to_text(covers) + ')'
+        return '<DNS ' + dns.rdataclass.to_text(self.rdclass) + ' ' + \
+               dns.rdatatype.to_text(self.rdtype) + ctext + ' rdata: ' + \
+               str(self) + '>'
+
+    def __str__(self):
+        return self.to_text()
+
+    def _cmp(self, other):
+        """Compare an rdata with another rdata of the same rdtype and
+        rdclass.  Return < 0 if self < other in the DNSSEC ordering,
+        0 if self == other, and > 0 if self > other.
+        """
+
+        raise NotImplementedError
+
+    def __eq__(self, other):
+        if not isinstance(other, Rdata):
+            return False
+        if self.rdclass != other.rdclass or \
+           self.rdtype != other.rdtype:
+            return False
+        return self._cmp(other) == 0
+
+    def __ne__(self, other):
+        if not isinstance(other, Rdata):
+            return True
+        if self.rdclass != other.rdclass or \
+           self.rdtype != other.rdtype:
+            return True
+        return self._cmp(other) != 0
+
+    def __lt__(self, other):
+        if not isinstance(other, Rdata) or \
+               self.rdclass != other.rdclass or \
+               self.rdtype != other.rdtype:
+            return NotImplemented
+        return self._cmp(other) < 0
+
+    def __le__(self, other):
+        if not isinstance(other, Rdata) or \
+               self.rdclass != other.rdclass or \
+               self.rdtype != other.rdtype:
+            return NotImplemented
+        return self._cmp(other) <= 0
+
+    def __ge__(self, other):
+        if not isinstance(other, Rdata) or \
+               self.rdclass != other.rdclass or \
+               self.rdtype != other.rdtype:
+            return NotImplemented
+        return self._cmp(other) >= 0
+
+    def __gt__(self, other):
+        if not isinstance(other, Rdata) or \
+               self.rdclass != other.rdclass or \
+               self.rdtype != other.rdtype:
+            return NotImplemented
+        return self._cmp(other) > 0
+
+    def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
+        """Build an rdata object from text format.
+
+        @param rdclass: The rdata class
+        @type rdclass: int
+        @param rdtype: The rdata type
+        @type rdtype: int
+        @param tok: The tokenizer
+        @type tok: dns.tokenizer.Tokenizer
+        @param origin: The origin to use for relative names
+        @type origin: dns.name.Name
+        @param relativize: should names be relativized?
+        @type relativize: bool
+        @rtype: dns.rdata.Rdata instance
+        """
+
+        raise NotImplementedError
+
+    from_text = classmethod(from_text)
+
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
+        """Build an rdata object from wire format
+
+        @param rdclass: The rdata class
+        @type rdclass: int
+        @param rdtype: The rdata type
+        @type rdtype: int
+        @param wire: The wire-format message
+        @type wire: string
+        @param current: The offet in wire of the beginning of the rdata.
+        @type current: int
+        @param rdlen: The length of the wire-format rdata
+        @type rdlen: int
+        @param origin: The origin to use for relative names
+        @type origin: dns.name.Name
+        @rtype: dns.rdata.Rdata instance
+        """
+
+        raise NotImplementedError
+
+    from_wire = classmethod(from_wire)
+
+    def choose_relativity(self, origin = None, relativize = True):
+        """Convert any domain names in the rdata to the specified
+        relativization.
+        """
+
+        pass
+
+
+class GenericRdata(Rdata):
+    """Generate Rdata Class
+
+    This class is used for rdata types for which we have no better
+    implementation.  It implements the DNS "unknown RRs" scheme.
+    """
+
+    __slots__ = ['data']
+
+    def __init__(self, rdclass, rdtype, data):
+        super(GenericRdata, self).__init__(rdclass, rdtype)
+        self.data = data
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return r'\# %d ' % len(self.data) + _hexify(self.data)
+
+    def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
+        token = tok.get()
+        if not token.is_identifier() or token.value != '\#':
+            raise dns.exception.SyntaxError(r'generic rdata does not start with \#')
+        length = tok.get_int()
+        chunks = []
+        while 1:
+            token = tok.get()
+            if token.is_eol_or_eof():
+                break
+            chunks.append(token.value)
+        hex = ''.join(chunks)
+        data = hex.decode('hex_codec')
+        if len(data) != length:
+            raise dns.exception.SyntaxError('generic rdata hex data has wrong length')
+        return cls(rdclass, rdtype, data)
+
+    from_text = classmethod(from_text)
+
+    def to_wire(self, file, compress = None, origin = None):
+        file.write(self.data)
+
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
+        return cls(rdclass, rdtype, wire[current : current + rdlen])
+
+    from_wire = classmethod(from_wire)
+
+    def _cmp(self, other):
+        return cmp(self.data, other.data)
+
+_rdata_modules = {}
+_module_prefix = 'dns.rdtypes'
+
+def get_rdata_class(rdclass, rdtype):
+
+    def import_module(name):
+        mod = __import__(name)
+        components = name.split('.')
+        for comp in components[1:]:
+            mod = getattr(mod, comp)
+        return mod
+
+    mod = _rdata_modules.get((rdclass, rdtype))
+    rdclass_text = dns.rdataclass.to_text(rdclass)
+    rdtype_text = dns.rdatatype.to_text(rdtype)
+    rdtype_text = rdtype_text.replace('-', '_')
+    if not mod:
+        mod = _rdata_modules.get((dns.rdatatype.ANY, rdtype))
+        if not mod:
+            try:
+                mod = import_module('.'.join([_module_prefix,
+                                              rdclass_text, rdtype_text]))
+                _rdata_modules[(rdclass, rdtype)] = mod
+            except ImportError:
+                try:
+                    mod = import_module('.'.join([_module_prefix,
+                                                  'ANY', rdtype_text]))
+                    _rdata_modules[(dns.rdataclass.ANY, rdtype)] = mod
+                except ImportError:
+                    mod = None
+    if mod:
+        cls = getattr(mod, rdtype_text)
+    else:
+        cls = GenericRdata
+    return cls
+
+def from_text(rdclass, rdtype, tok, origin = None, relativize = True):
+    """Build an rdata object from text format.
+
+    This function attempts to dynamically load a class which
+    implements the specified rdata class and type.  If there is no
+    class-and-type-specific implementation, the GenericRdata class
+    is used.
+
+    Once a class is chosen, its from_text() class method is called
+    with the parameters to this function.
+
+    @param rdclass: The rdata class
+    @type rdclass: int
+    @param rdtype: The rdata type
+    @type rdtype: int
+    @param tok: The tokenizer
+    @type tok: dns.tokenizer.Tokenizer
+    @param origin: The origin to use for relative names
+    @type origin: dns.name.Name
+    @param relativize: Should names be relativized?
+    @type relativize: bool
+    @rtype: dns.rdata.Rdata instance"""
+
+    if isinstance(tok, str):
+        tok = dns.tokenizer.Tokenizer(tok)
+    cls = get_rdata_class(rdclass, rdtype)
+    if cls != GenericRdata:
+        # peek at first token
+        token = tok.get()
+        tok.unget(token)
+        if token.is_identifier() and \
+           token.value == r'\#':
+            #
+            # Known type using the generic syntax.  Extract the
+            # wire form from the generic syntax, and then run
+            # from_wire on it.
+            #
+            rdata = GenericRdata.from_text(rdclass, rdtype, tok, origin,
+                                           relativize)
+            return from_wire(rdclass, rdtype, rdata.data, 0, len(rdata.data),
+                             origin)
+    return cls.from_text(rdclass, rdtype, tok, origin, relativize)
+
+def from_wire(rdclass, rdtype, wire, current, rdlen, origin = None):
+    """Build an rdata object from wire format
+
+    This function attempts to dynamically load a class which
+    implements the specified rdata class and type.  If there is no
+    class-and-type-specific implementation, the GenericRdata class
+    is used.
+
+    Once a class is chosen, its from_wire() class method is called
+    with the parameters to this function.
+
+    @param rdclass: The rdata class
+    @type rdclass: int
+    @param rdtype: The rdata type
+    @type rdtype: int
+    @param wire: The wire-format message
+    @type wire: string
+    @param current: The offet in wire of the beginning of the rdata.
+    @type current: int
+    @param rdlen: The length of the wire-format rdata
+    @type rdlen: int
+    @param origin: The origin to use for relative names
+    @type origin: dns.name.Name
+    @rtype: dns.rdata.Rdata instance"""
+
+    cls = get_rdata_class(rdclass, rdtype)
+    return cls.from_wire(rdclass, rdtype, wire, current, rdlen, origin)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdataclass.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdataclass.py
new file mode 100644
index 0000000..887fd1a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdataclass.py
@@ -0,0 +1,114 @@
+# Copyright (C) 2001-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""DNS Rdata Classes.
+
+@var _by_text: The rdata class textual name to value mapping
+@type _by_text: dict
+@var _by_value: The rdata class value to textual name mapping
+@type _by_value: dict
+@var _metaclasses: If an rdataclass is a metaclass, there will be a mapping
+whose key is the rdatatype value and whose value is True in this dictionary.
+@type _metaclasses: dict"""
+
+import re
+
+import dns.exception
+
+RESERVED0 = 0
+IN = 1
+CH = 3
+HS = 4
+NONE = 254
+ANY = 255
+
+_by_text = {
+    'RESERVED0' : RESERVED0,
+    'IN' : IN,
+    'CH' : CH,
+    'HS' : HS,
+    'NONE' : NONE,
+    'ANY' : ANY
+    }
+
+# We construct the inverse mapping programmatically to ensure that we
+# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that
+# would cause the mapping not to be true inverse.
+
+_by_value = dict([(y, x) for x, y in _by_text.iteritems()])
+
+# Now that we've built the inverse map, we can add class aliases to
+# the _by_text mapping.
+
+_by_text.update({
+    'INTERNET' : IN,
+    'CHAOS' : CH,
+    'HESIOD' : HS
+    })
+
+_metaclasses = {
+    NONE : True,
+    ANY : True
+    }
+
+_unknown_class_pattern = re.compile('CLASS([0-9]+)$', re.I);
+
+class UnknownRdataclass(dns.exception.DNSException):
+    """Raised when a class is unknown."""
+    pass
+
+def from_text(text):
+    """Convert text into a DNS rdata class value.
+    @param text: the text
+    @type text: string
+    @rtype: int
+    @raises dns.rdataclass.UnknownRdataClass: the class is unknown
+    @raises ValueError: the rdata class value is not >= 0 and <= 65535
+    """
+
+    value = _by_text.get(text.upper())
+    if value is None:
+        match = _unknown_class_pattern.match(text)
+        if match == None:
+            raise UnknownRdataclass
+        value = int(match.group(1))
+        if value < 0 or value > 65535:
+            raise ValueError("class must be between >= 0 and <= 65535")
+    return value
+
+def to_text(value):
+    """Convert a DNS rdata class to text.
+    @param value: the rdata class value
+    @type value: int
+    @rtype: string
+    @raises ValueError: the rdata class value is not >= 0 and <= 65535
+    """
+
+    if value < 0 or value > 65535:
+        raise ValueError("class must be between >= 0 and <= 65535")
+    text = _by_value.get(value)
+    if text is None:
+        text = 'CLASS' + `value`
+    return text
+
+def is_metaclass(rdclass):
+    """True if the class is a metaclass.
+    @param rdclass: the rdata class
+    @type rdclass: int
+    @rtype: bool"""
+
+    if _metaclasses.has_key(rdclass):
+        return True
+    return False
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdataset.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdataset.py
new file mode 100644
index 0000000..0af018b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdataset.py
@@ -0,0 +1,329 @@
+# Copyright (C) 2001-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""DNS rdatasets (an rdataset is a set of rdatas of a given type and class)"""
+
+import random
+import StringIO
+import struct
+
+import dns.exception
+import dns.rdatatype
+import dns.rdataclass
+import dns.rdata
+import dns.set
+
+# define SimpleSet here for backwards compatibility
+SimpleSet = dns.set.Set
+
+class DifferingCovers(dns.exception.DNSException):
+    """Raised if an attempt is made to add a SIG/RRSIG whose covered type
+    is not the same as that of the other rdatas in the rdataset."""
+    pass
+
+class IncompatibleTypes(dns.exception.DNSException):
+    """Raised if an attempt is made to add rdata of an incompatible type."""
+    pass
+
+class Rdataset(dns.set.Set):
+    """A DNS rdataset.
+
+    @ivar rdclass: The class of the rdataset
+    @type rdclass: int
+    @ivar rdtype: The type of the rdataset
+    @type rdtype: int
+    @ivar covers: The covered type.  Usually this value is
+    dns.rdatatype.NONE, but if the rdtype is dns.rdatatype.SIG or
+    dns.rdatatype.RRSIG, then the covers value will be the rdata
+    type the SIG/RRSIG covers.  The library treats the SIG and RRSIG
+    types as if they were a family of
+    types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA).  This makes RRSIGs much
+    easier to work with than if RRSIGs covering different rdata
+    types were aggregated into a single RRSIG rdataset.
+    @type covers: int
+    @ivar ttl: The DNS TTL (Time To Live) value
+    @type ttl: int
+    """
+
+    __slots__ = ['rdclass', 'rdtype', 'covers', 'ttl']
+
+    def __init__(self, rdclass, rdtype, covers=dns.rdatatype.NONE):
+        """Create a new rdataset of the specified class and type.
+
+        @see: the description of the class instance variables for the
+        meaning of I{rdclass} and I{rdtype}"""
+
+        super(Rdataset, self).__init__()
+        self.rdclass = rdclass
+        self.rdtype = rdtype
+        self.covers = covers
+        self.ttl = 0
+
+    def _clone(self):
+        obj = super(Rdataset, self)._clone()
+        obj.rdclass = self.rdclass
+        obj.rdtype = self.rdtype
+        obj.covers = self.covers
+        obj.ttl = self.ttl
+        return obj
+
+    def update_ttl(self, ttl):
+        """Set the TTL of the rdataset to be the lesser of the set's current
+        TTL or the specified TTL.  If the set contains no rdatas, set the TTL
+        to the specified TTL.
+        @param ttl: The TTL
+        @type ttl: int"""
+
+        if len(self) == 0:
+            self.ttl = ttl
+        elif ttl < self.ttl:
+            self.ttl = ttl
+
+    def add(self, rd, ttl=None):
+        """Add the specified rdata to the rdataset.
+
+        If the optional I{ttl} parameter is supplied, then
+        self.update_ttl(ttl) will be called prior to adding the rdata.
+
+        @param rd: The rdata
+        @type rd: dns.rdata.Rdata object
+        @param ttl: The TTL
+        @type ttl: int"""
+
+        #
+        # If we're adding a signature, do some special handling to
+        # check that the signature covers the same type as the
+        # other rdatas in this rdataset.  If this is the first rdata
+        # in the set, initialize the covers field.
+        #
+        if self.rdclass != rd.rdclass or self.rdtype != rd.rdtype:
+            raise IncompatibleTypes
+        if not ttl is None:
+            self.update_ttl(ttl)
+        if self.rdtype == dns.rdatatype.RRSIG or \
+           self.rdtype == dns.rdatatype.SIG:
+            covers = rd.covers()
+            if len(self) == 0 and self.covers == dns.rdatatype.NONE:
+                self.covers = covers
+            elif self.covers != covers:
+                raise DifferingCovers
+        if dns.rdatatype.is_singleton(rd.rdtype) and len(self) > 0:
+            self.clear()
+        super(Rdataset, self).add(rd)
+
+    def union_update(self, other):
+        self.update_ttl(other.ttl)
+        super(Rdataset, self).union_update(other)
+
+    def intersection_update(self, other):
+        self.update_ttl(other.ttl)
+        super(Rdataset, self).intersection_update(other)
+
+    def update(self, other):
+        """Add all rdatas in other to self.
+
+        @param other: The rdataset from which to update
+        @type other: dns.rdataset.Rdataset object"""
+
+        self.update_ttl(other.ttl)
+        super(Rdataset, self).update(other)
+
+    def __repr__(self):
+        if self.covers == 0:
+            ctext = ''
+        else:
+            ctext = '(' + dns.rdatatype.to_text(self.covers) + ')'
+        return '<DNS ' + dns.rdataclass.to_text(self.rdclass) + ' ' + \
+               dns.rdatatype.to_text(self.rdtype) + ctext + ' rdataset>'
+
+    def __str__(self):
+        return self.to_text()
+
+    def __eq__(self, other):
+        """Two rdatasets are equal if they have the same class, type, and
+        covers, and contain the same rdata.
+        @rtype: bool"""
+
+        if not isinstance(other, Rdataset):
+            return False
+        if self.rdclass != other.rdclass or \
+           self.rdtype != other.rdtype or \
+           self.covers != other.covers:
+            return False
+        return super(Rdataset, self).__eq__(other)
+
+    def __ne__(self, other):
+        return not self.__eq__(other)
+
+    def to_text(self, name=None, origin=None, relativize=True,
+                override_rdclass=None, **kw):
+        """Convert the rdataset into DNS master file format.
+
+        @see: L{dns.name.Name.choose_relativity} for more information
+        on how I{origin} and I{relativize} determine the way names
+        are emitted.
+
+        Any additional keyword arguments are passed on to the rdata
+        to_text() method.
+
+        @param name: If name is not None, emit a RRs with I{name} as
+        the owner name.
+        @type name: dns.name.Name object
+        @param origin: The origin for relative names, or None.
+        @type origin: dns.name.Name object
+        @param relativize: True if names should names be relativized
+        @type relativize: bool"""
+        if not name is None:
+            name = name.choose_relativity(origin, relativize)
+            ntext = str(name)
+            pad = ' '
+        else:
+            ntext = ''
+            pad = ''
+        s = StringIO.StringIO()
+        if not override_rdclass is None:
+            rdclass = override_rdclass
+        else:
+            rdclass = self.rdclass
+        if len(self) == 0:
+            #
+            # Empty rdatasets are used for the question section, and in
+            # some dynamic updates, so we don't need to print out the TTL
+            # (which is meaningless anyway).
+            #
+            print >> s, '%s%s%s %s' % (ntext, pad,
+                                       dns.rdataclass.to_text(rdclass),
+                                       dns.rdatatype.to_text(self.rdtype))
+        else:
+            for rd in self:
+                print >> s, '%s%s%d %s %s %s' % \
+                      (ntext, pad, self.ttl, dns.rdataclass.to_text(rdclass),
+                       dns.rdatatype.to_text(self.rdtype),
+                       rd.to_text(origin=origin, relativize=relativize, **kw))
+        #
+        # We strip off the final \n for the caller's convenience in printing
+        #
+        return s.getvalue()[:-1]
+
+    def to_wire(self, name, file, compress=None, origin=None,
+                override_rdclass=None, want_shuffle=True):
+        """Convert the rdataset to wire format.
+
+        @param name: The owner name of the RRset that will be emitted
+        @type name: dns.name.Name object
+        @param file: The file to which the wire format data will be appended
+        @type file: file
+        @param compress: The compression table to use; the default is None.
+        @type compress: dict
+        @param origin: The origin to be appended to any relative names when
+        they are emitted.  The default is None.
+        @returns: the number of records emitted
+        @rtype: int
+        """
+
+        if not override_rdclass is None:
+            rdclass =  override_rdclass
+            want_shuffle = False
+        else:
+            rdclass = self.rdclass
+        file.seek(0, 2)
+        if len(self) == 0:
+            name.to_wire(file, compress, origin)
+            stuff = struct.pack("!HHIH", self.rdtype, rdclass, 0, 0)
+            file.write(stuff)
+            return 1
+        else:
+            if want_shuffle:
+                l = list(self)
+                random.shuffle(l)
+            else:
+                l = self
+            for rd in l:
+                name.to_wire(file, compress, origin)
+                stuff = struct.pack("!HHIH", self.rdtype, rdclass,
+                                    self.ttl, 0)
+                file.write(stuff)
+                start = file.tell()
+                rd.to_wire(file, compress, origin)
+                end = file.tell()
+                assert end - start < 65536
+                file.seek(start - 2)
+                stuff = struct.pack("!H", end - start)
+                file.write(stuff)
+                file.seek(0, 2)
+            return len(self)
+
+    def match(self, rdclass, rdtype, covers):
+        """Returns True if this rdataset matches the specified class, type,
+        and covers"""
+        if self.rdclass == rdclass and \
+           self.rdtype == rdtype and \
+           self.covers == covers:
+            return True
+        return False
+
+def from_text_list(rdclass, rdtype, ttl, text_rdatas):
+    """Create an rdataset with the specified class, type, and TTL, and with
+    the specified list of rdatas in text format.
+
+    @rtype: dns.rdataset.Rdataset object
+    """
+
+    if isinstance(rdclass, str):
+        rdclass = dns.rdataclass.from_text(rdclass)
+    if isinstance(rdtype, str):
+        rdtype = dns.rdatatype.from_text(rdtype)
+    r = Rdataset(rdclass, rdtype)
+    r.update_ttl(ttl)
+    for t in text_rdatas:
+        rd = dns.rdata.from_text(r.rdclass, r.rdtype, t)
+        r.add(rd)
+    return r
+
+def from_text(rdclass, rdtype, ttl, *text_rdatas):
+    """Create an rdataset with the specified class, type, and TTL, and with
+    the specified rdatas in text format.
+
+    @rtype: dns.rdataset.Rdataset object
+    """
+
+    return from_text_list(rdclass, rdtype, ttl, text_rdatas)
+
+def from_rdata_list(ttl, rdatas):
+    """Create an rdataset with the specified TTL, and with
+    the specified list of rdata objects.
+
+    @rtype: dns.rdataset.Rdataset object
+    """
+
+    if len(rdatas) == 0:
+        raise ValueError("rdata list must not be empty")
+    r = None
+    for rd in rdatas:
+        if r is None:
+            r = Rdataset(rd.rdclass, rd.rdtype)
+            r.update_ttl(ttl)
+            first_time = False
+        r.add(rd)
+    return r
+
+def from_rdata(ttl, *rdatas):
+    """Create an rdataset with the specified TTL, and with
+    the specified rdata objects.
+
+    @rtype: dns.rdataset.Rdataset object
+    """
+
+    return from_rdata_list(ttl, rdatas)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdatatype.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdatatype.py
new file mode 100644
index 0000000..1a02b7d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdatatype.py
@@ -0,0 +1,232 @@
+# Copyright (C) 2001-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""DNS Rdata Types.
+
+@var _by_text: The rdata type textual name to value mapping
+@type _by_text: dict
+@var _by_value: The rdata type value to textual name mapping
+@type _by_value: dict
+@var _metatypes: If an rdatatype is a metatype, there will be a mapping
+whose key is the rdatatype value and whose value is True in this dictionary.
+@type _metatypes: dict
+@var _singletons: If an rdatatype is a singleton, there will be a mapping
+whose key is the rdatatype value and whose value is True in this dictionary.
+@type _singletons: dict"""
+
+import re
+
+import dns.exception
+
+NONE = 0
+A = 1
+NS = 2
+MD = 3
+MF = 4
+CNAME = 5
+SOA = 6
+MB = 7
+MG = 8
+MR = 9
+NULL = 10
+WKS = 11
+PTR = 12
+HINFO = 13
+MINFO = 14
+MX = 15
+TXT = 16
+RP = 17
+AFSDB = 18
+X25 = 19
+ISDN = 20
+RT = 21
+NSAP = 22
+NSAP_PTR = 23
+SIG = 24
+KEY = 25
+PX = 26
+GPOS = 27
+AAAA = 28
+LOC = 29
+NXT = 30
+SRV = 33
+NAPTR = 35
+KX = 36
+CERT = 37
+A6 = 38
+DNAME = 39
+OPT = 41
+APL = 42
+DS = 43
+SSHFP = 44
+IPSECKEY = 45
+RRSIG = 46
+NSEC = 47
+DNSKEY = 48
+DHCID = 49
+NSEC3 = 50
+NSEC3PARAM = 51
+HIP = 55
+SPF = 99
+UNSPEC = 103
+TKEY = 249
+TSIG = 250
+IXFR = 251
+AXFR = 252
+MAILB = 253
+MAILA = 254
+ANY = 255
+TA = 32768
+DLV = 32769
+
+_by_text = {
+    'NONE' : NONE,
+    'A' : A,
+    'NS' : NS,
+    'MD' : MD,
+    'MF' : MF,
+    'CNAME' : CNAME,
+    'SOA' : SOA,
+    'MB' : MB,
+    'MG' : MG,
+    'MR' : MR,
+    'NULL' : NULL,
+    'WKS' : WKS,
+    'PTR' : PTR,
+    'HINFO' : HINFO,
+    'MINFO' : MINFO,
+    'MX' : MX,
+    'TXT' : TXT,
+    'RP' : RP,
+    'AFSDB' : AFSDB,
+    'X25' : X25,
+    'ISDN' : ISDN,
+    'RT' : RT,
+    'NSAP' : NSAP,
+    'NSAP-PTR' : NSAP_PTR,
+    'SIG' : SIG,
+    'KEY' : KEY,
+    'PX' : PX,
+    'GPOS' : GPOS,
+    'AAAA' : AAAA,
+    'LOC' : LOC,
+    'NXT' : NXT,
+    'SRV' : SRV,
+    'NAPTR' : NAPTR,
+    'KX' : KX,
+    'CERT' : CERT,
+    'A6' : A6,
+    'DNAME' : DNAME,
+    'OPT' : OPT,
+    'APL' : APL,
+    'DS' : DS,
+    'SSHFP' : SSHFP,
+    'IPSECKEY' : IPSECKEY,
+    'RRSIG' : RRSIG,
+    'NSEC' : NSEC,
+    'DNSKEY' : DNSKEY,
+    'DHCID' : DHCID,
+    'NSEC3' : NSEC3,
+    'NSEC3PARAM' : NSEC3PARAM,
+    'HIP' : HIP,
+    'SPF' : SPF,
+    'UNSPEC' : UNSPEC,
+    'TKEY' : TKEY,
+    'TSIG' : TSIG,
+    'IXFR' : IXFR,
+    'AXFR' : AXFR,
+    'MAILB' : MAILB,
+    'MAILA' : MAILA,
+    'ANY' : ANY,
+    'TA' : TA,
+    'DLV' : DLV,
+    }
+
+# We construct the inverse mapping programmatically to ensure that we
+# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that
+# would cause the mapping not to be true inverse.
+
+_by_value = dict([(y, x) for x, y in _by_text.iteritems()])
+
+
+_metatypes = {
+    OPT : True
+    }
+
+_singletons = {
+    SOA : True,
+    NXT : True,
+    DNAME : True,
+    NSEC : True,
+    # CNAME is technically a singleton, but we allow multiple CNAMEs.
+    }
+
+_unknown_type_pattern = re.compile('TYPE([0-9]+)$', re.I);
+
+class UnknownRdatatype(dns.exception.DNSException):
+    """Raised if a type is unknown."""
+    pass
+
+def from_text(text):
+    """Convert text into a DNS rdata type value.
+    @param text: the text
+    @type text: string
+    @raises dns.rdatatype.UnknownRdatatype: the type is unknown
+    @raises ValueError: the rdata type value is not >= 0 and <= 65535
+    @rtype: int"""
+
+    value = _by_text.get(text.upper())
+    if value is None:
+        match = _unknown_type_pattern.match(text)
+        if match == None:
+            raise UnknownRdatatype
+        value = int(match.group(1))
+        if value < 0 or value > 65535:
+            raise ValueError("type must be between >= 0 and <= 65535")
+    return value
+
+def to_text(value):
+    """Convert a DNS rdata type to text.
+    @param value: the rdata type value
+    @type value: int
+    @raises ValueError: the rdata type value is not >= 0 and <= 65535
+    @rtype: string"""
+
+    if value < 0 or value > 65535:
+        raise ValueError("type must be between >= 0 and <= 65535")
+    text = _by_value.get(value)
+    if text is None:
+        text = 'TYPE' + `value`
+    return text
+
+def is_metatype(rdtype):
+    """True if the type is a metatype.
+    @param rdtype: the type
+    @type rdtype: int
+    @rtype: bool"""
+
+    if rdtype >= TKEY and rdtype <= ANY or _metatypes.has_key(rdtype):
+        return True
+    return False
+
+def is_singleton(rdtype):
+    """True if the type is a singleton.
+    @param rdtype: the type
+    @type rdtype: int
+    @rtype: bool"""
+
+    if _singletons.has_key(rdtype):
+        return True
+    return False
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/AFSDB.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/AFSDB.py
new file mode 100644
index 0000000..e8ca6f5
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/AFSDB.py
@@ -0,0 +1,51 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.rdtypes.mxbase
+
+class AFSDB(dns.rdtypes.mxbase.UncompressedDowncasingMX):
+    """AFSDB record
+
+    @ivar subtype: the subtype value
+    @type subtype: int
+    @ivar hostname: the hostname name
+    @type hostname: dns.name.Name object"""
+
+    # Use the property mechanism to make "subtype" an alias for the
+    # "preference" attribute, and "hostname" an alias for the "exchange"
+    # attribute.
+    #
+    # This lets us inherit the UncompressedMX implementation but lets
+    # the caller use appropriate attribute names for the rdata type.
+    #
+    # We probably lose some performance vs. a cut-and-paste
+    # implementation, but this way we don't copy code, and that's
+    # good.
+
+    def get_subtype(self):
+        return self.preference
+
+    def set_subtype(self, subtype):
+        self.preference = subtype
+
+    subtype = property(get_subtype, set_subtype)
+
+    def get_hostname(self):
+        return self.exchange
+
+    def set_hostname(self, hostname):
+        self.exchange = hostname
+
+    hostname = property(get_hostname, set_hostname)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/CERT.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/CERT.py
new file mode 100644
index 0000000..d270351
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/CERT.py
@@ -0,0 +1,131 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import cStringIO
+import struct
+
+import dns.exception
+import dns.dnssec
+import dns.rdata
+import dns.tokenizer
+
+_ctype_by_value = {
+    1 : 'PKIX',
+    2 : 'SPKI',
+    3 : 'PGP',
+    253 : 'URI',
+    254 : 'OID',
+    }
+
+_ctype_by_name = {
+    'PKIX' : 1,
+    'SPKI' : 2,
+    'PGP' : 3,
+    'URI' : 253,
+    'OID' : 254,
+    }
+
+def _ctype_from_text(what):
+    v = _ctype_by_name.get(what)
+    if not v is None:
+        return v
+    return int(what)
+
+def _ctype_to_text(what):
+    v = _ctype_by_value.get(what)
+    if not v is None:
+        return v
+    return str(what)
+
+class CERT(dns.rdata.Rdata):
+    """CERT record
+
+    @ivar certificate_type: certificate type
+    @type certificate_type: int
+    @ivar key_tag: key tag
+    @type key_tag: int
+    @ivar algorithm: algorithm
+    @type algorithm: int
+    @ivar certificate: the certificate or CRL
+    @type certificate: string
+    @see: RFC 2538"""
+
+    __slots__ = ['certificate_type', 'key_tag', 'algorithm', 'certificate']
+
+    def __init__(self, rdclass, rdtype, certificate_type, key_tag, algorithm,
+                 certificate):
+        super(CERT, self).__init__(rdclass, rdtype)
+        self.certificate_type = certificate_type
+        self.key_tag = key_tag
+        self.algorithm = algorithm
+        self.certificate = certificate
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        certificate_type = _ctype_to_text(self.certificate_type)
+        return "%s %d %s %s" % (certificate_type, self.key_tag,
+                                dns.dnssec.algorithm_to_text(self.algorithm),
+                                dns.rdata._base64ify(self.certificate))
+
+    def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
+        certificate_type = _ctype_from_text(tok.get_string())
+        key_tag = tok.get_uint16()
+        algorithm = dns.dnssec.algorithm_from_text(tok.get_string())
+        if algorithm < 0 or algorithm > 255:
+            raise dns.exception.SyntaxError("bad algorithm type")
+        chunks = []
+        while 1:
+            t = tok.get().unescape()
+            if t.is_eol_or_eof():
+                break
+            if not t.is_identifier():
+                raise dns.exception.SyntaxError
+            chunks.append(t.value)
+        b64 = ''.join(chunks)
+        certificate = b64.decode('base64_codec')
+        return cls(rdclass, rdtype, certificate_type, key_tag,
+                   algorithm, certificate)
+
+    from_text = classmethod(from_text)
+
+    def to_wire(self, file, compress = None, origin = None):
+        prefix = struct.pack("!HHB", self.certificate_type, self.key_tag,
+                             self.algorithm)
+        file.write(prefix)
+        file.write(self.certificate)
+
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
+        prefix = wire[current : current + 5]
+        current += 5
+        rdlen -= 5
+        if rdlen < 0:
+            raise dns.exception.FormError
+        (certificate_type, key_tag, algorithm) = struct.unpack("!HHB", prefix)
+        certificate = wire[current : current + rdlen]
+        return cls(rdclass, rdtype, certificate_type, key_tag, algorithm,
+                   certificate)
+
+    from_wire = classmethod(from_wire)
+
+    def _cmp(self, other):
+        f = cStringIO.StringIO()
+        self.to_wire(f)
+        wire1 = f.getvalue()
+        f.seek(0)
+        f.truncate()
+        other.to_wire(f)
+        wire2 = f.getvalue()
+        f.close()
+
+        return cmp(wire1, wire2)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/CNAME.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/CNAME.py
new file mode 100644
index 0000000..7f5c4b3
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/CNAME.py
@@ -0,0 +1,24 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.rdtypes.nsbase
+
+class CNAME(dns.rdtypes.nsbase.NSBase):
+    """CNAME record
+
+    Note: although CNAME is officially a singleton type, dnspython allows
+    non-singleton CNAME rdatasets because such sets have been commonly
+    used by BIND and other nameservers for load balancing."""
+    pass
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/DLV.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/DLV.py
new file mode 100644
index 0000000..07b9548
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/DLV.py
@@ -0,0 +1,20 @@
+# Copyright (C) 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.rdtypes.dsbase
+
+class DLV(dns.rdtypes.dsbase.DSBase):
+    """DLV record"""
+    pass
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/DNAME.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/DNAME.py
new file mode 100644
index 0000000..99b5013
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/DNAME.py
@@ -0,0 +1,21 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.rdtypes.nsbase
+
+class DNAME(dns.rdtypes.nsbase.UncompressedNS):
+    """DNAME record"""
+    def to_digestable(self, origin = None):
+        return self.target.to_digestable(origin)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/DNSKEY.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/DNSKEY.py
new file mode 100644
index 0000000..ad66ef0
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/DNSKEY.py
@@ -0,0 +1,25 @@
+# Copyright (C) 2004-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.rdtypes.keybase
+
+# flag constants
+SEP = 0x0001
+REVOKE = 0x0080
+ZONE = 0x0100
+
+class DNSKEY(dns.rdtypes.keybase.KEYBase):
+    """DNSKEY record"""
+    pass
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/DS.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/DS.py
new file mode 100644
index 0000000..3a06f44
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/DS.py
@@ -0,0 +1,20 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.rdtypes.dsbase
+
+class DS(dns.rdtypes.dsbase.DSBase):
+    """DS record"""
+    pass
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/GPOS.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/GPOS.py
new file mode 100644
index 0000000..6f63cc0
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/GPOS.py
@@ -0,0 +1,156 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.exception
+import dns.rdata
+import dns.tokenizer
+
+def _validate_float_string(what):
+    if what[0] == '-' or what[0] == '+':
+        what = what[1:]
+    if what.isdigit():
+        return
+    (left, right) = what.split('.')
+    if left == '' and right == '':
+        raise dns.exception.FormError
+    if not left == '' and not left.isdigit():
+        raise dns.exception.FormError
+    if not right == '' and not right.isdigit():
+        raise dns.exception.FormError
+    
+class GPOS(dns.rdata.Rdata):
+    """GPOS record
+
+    @ivar latitude: latitude
+    @type latitude: string
+    @ivar longitude: longitude
+    @type longitude: string
+    @ivar altitude: altitude
+    @type altitude: string
+    @see: RFC 1712"""
+
+    __slots__ = ['latitude', 'longitude', 'altitude']
+    
+    def __init__(self, rdclass, rdtype, latitude, longitude, altitude):
+        super(GPOS, self).__init__(rdclass, rdtype)
+        if isinstance(latitude, float) or \
+           isinstance(latitude, int) or \
+           isinstance(latitude, long):
+            latitude = str(latitude)
+        if isinstance(longitude, float) or \
+           isinstance(longitude, int) or \
+           isinstance(longitude, long):
+            longitude = str(longitude)
+        if isinstance(altitude, float) or \
+           isinstance(altitude, int) or \
+           isinstance(altitude, long):
+            altitude = str(altitude)
+        _validate_float_string(latitude)
+        _validate_float_string(longitude)
+        _validate_float_string(altitude)
+        self.latitude = latitude
+        self.longitude = longitude
+        self.altitude = altitude
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return '%s %s %s' % (self.latitude, self.longitude, self.altitude)
+        
+    def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
+        latitude = tok.get_string()
+        longitude = tok.get_string()
+        altitude = tok.get_string()
+        tok.get_eol()
+        return cls(rdclass, rdtype, latitude, longitude, altitude)
+    
+    from_text = classmethod(from_text)
+
+    def to_wire(self, file, compress = None, origin = None):
+        l = len(self.latitude)
+        assert l < 256
+        byte = chr(l)
+        file.write(byte)
+        file.write(self.latitude)
+        l = len(self.longitude)
+        assert l < 256
+        byte = chr(l)
+        file.write(byte)
+        file.write(self.longitude)
+        l = len(self.altitude)
+        assert l < 256
+        byte = chr(l)
+        file.write(byte)
+        file.write(self.altitude)
+        
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
+        l = ord(wire[current])
+        current += 1
+        rdlen -= 1
+        if l > rdlen:
+            raise dns.exception.FormError
+        latitude = wire[current : current + l]
+        current += l
+        rdlen -= l
+        l = ord(wire[current])
+        current += 1
+        rdlen -= 1
+        if l > rdlen:
+            raise dns.exception.FormError
+        longitude = wire[current : current + l]
+        current += l
+        rdlen -= l
+        l = ord(wire[current])
+        current += 1
+        rdlen -= 1
+        if l != rdlen:
+            raise dns.exception.FormError
+        altitude = wire[current : current + l]
+        return cls(rdclass, rdtype, latitude, longitude, altitude)
+
+    from_wire = classmethod(from_wire)
+
+    def _cmp(self, other):
+        v = cmp(self.latitude, other.latitude)
+        if v == 0:
+            v = cmp(self.longitude, other.longitude)
+            if v == 0:
+                v = cmp(self.altitude, other.altitude)
+        return v
+
+    def _get_float_latitude(self):
+        return float(self.latitude)
+
+    def _set_float_latitude(self, value):
+        self.latitude = str(value)
+
+    float_latitude = property(_get_float_latitude, _set_float_latitude,
+                              doc="latitude as a floating point value")
+
+    def _get_float_longitude(self):
+        return float(self.longitude)
+
+    def _set_float_longitude(self, value):
+        self.longitude = str(value)
+
+    float_longitude = property(_get_float_longitude, _set_float_longitude,
+                               doc="longitude as a floating point value")
+
+    def _get_float_altitude(self):
+        return float(self.altitude)
+
+    def _set_float_altitude(self, value):
+        self.altitude = str(value)
+
+    float_altitude = property(_get_float_altitude, _set_float_altitude,
+                              doc="altitude as a floating point value")
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/HINFO.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/HINFO.py
new file mode 100644
index 0000000..e592ad3
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/HINFO.py
@@ -0,0 +1,83 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.exception
+import dns.rdata
+import dns.tokenizer
+
+class HINFO(dns.rdata.Rdata):
+    """HINFO record
+
+    @ivar cpu: the CPU type
+    @type cpu: string
+    @ivar os: the OS type
+    @type os: string
+    @see: RFC 1035"""
+
+    __slots__ = ['cpu', 'os']
+    
+    def __init__(self, rdclass, rdtype, cpu, os):
+        super(HINFO, self).__init__(rdclass, rdtype)
+        self.cpu = cpu
+        self.os = os
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return '"%s" "%s"' % (dns.rdata._escapify(self.cpu),
+                              dns.rdata._escapify(self.os))
+        
+    def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
+        cpu = tok.get_string()
+        os = tok.get_string()
+        tok.get_eol()
+        return cls(rdclass, rdtype, cpu, os)
+    
+    from_text = classmethod(from_text)
+
+    def to_wire(self, file, compress = None, origin = None):
+        l = len(self.cpu)
+        assert l < 256
+        byte = chr(l)
+        file.write(byte)
+        file.write(self.cpu)
+        l = len(self.os)
+        assert l < 256
+        byte = chr(l)
+        file.write(byte)
+        file.write(self.os)
+        
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
+        l = ord(wire[current])
+        current += 1
+        rdlen -= 1
+        if l > rdlen:
+            raise dns.exception.FormError
+        cpu = wire[current : current + l]
+        current += l
+        rdlen -= l
+        l = ord(wire[current])
+        current += 1
+        rdlen -= 1
+        if l != rdlen:
+            raise dns.exception.FormError
+        os = wire[current : current + l]
+        return cls(rdclass, rdtype, cpu, os)
+
+    from_wire = classmethod(from_wire)
+
+    def _cmp(self, other):
+        v = cmp(self.cpu, other.cpu)
+        if v == 0:
+            v = cmp(self.os, other.os)
+        return v
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/HIP.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/HIP.py
new file mode 100644
index 0000000..8f96ae9
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/HIP.py
@@ -0,0 +1,140 @@
+# Copyright (C) 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import cStringIO
+import string
+import struct
+
+import dns.exception
+import dns.rdata
+import dns.rdatatype
+
+class HIP(dns.rdata.Rdata):
+    """HIP record
+
+    @ivar hit: the host identity tag
+    @type hit: string
+    @ivar algorithm: the public key cryptographic algorithm
+    @type algorithm: int
+    @ivar key: the public key
+    @type key: string
+    @ivar servers: the rendezvous servers
+    @type servers: list of dns.name.Name objects
+    @see: RFC 5205"""
+
+    __slots__ = ['hit', 'algorithm', 'key', 'servers']
+
+    def __init__(self, rdclass, rdtype, hit, algorithm, key, servers):
+        super(HIP, self).__init__(rdclass, rdtype)
+        self.hit = hit
+        self.algorithm = algorithm
+        self.key = key
+        self.servers = servers
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        hit = self.hit.encode('hex-codec')
+        key = self.key.encode('base64-codec').replace('\n', '')
+        text = ''
+        servers = []
+        for server in self.servers:
+            servers.append(str(server.choose_relativity(origin, relativize)))
+        if len(servers) > 0:
+            text += (' ' + ' '.join(servers))
+        return '%u %s %s%s' % (self.algorithm, hit, key, text)
+
+    def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
+        algorithm = tok.get_uint8()
+        hit = tok.get_string().decode('hex-codec')
+        if len(hit) > 255:
+            raise dns.exception.SyntaxError("HIT too long")
+        key = tok.get_string().decode('base64-codec')
+        servers = []
+        while 1:
+            token = tok.get()
+            if token.is_eol_or_eof():
+                break
+            server = dns.name.from_text(token.value, origin)
+            server.choose_relativity(origin, relativize)
+            servers.append(server)
+        return cls(rdclass, rdtype, hit, algorithm, key, servers)
+
+    from_text = classmethod(from_text)
+
+    def to_wire(self, file, compress = None, origin = None):
+        lh = len(self.hit)
+        lk = len(self.key)
+        file.write(struct.pack("!BBH", lh, self.algorithm, lk))
+        file.write(self.hit)
+        file.write(self.key)
+        for server in self.servers:
+            server.to_wire(file, None, origin)
+
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
+        (lh, algorithm, lk) = struct.unpack('!BBH',
+                                            wire[current : current + 4])
+        current += 4
+        rdlen -= 4
+        hit = wire[current : current + lh]
+        current += lh
+        rdlen -= lh
+        key = wire[current : current + lk]
+        current += lk
+        rdlen -= lk
+        servers = []
+        while rdlen > 0:
+            (server, cused) = dns.name.from_wire(wire[: current + rdlen],
+                                                 current)
+            current += cused
+            rdlen -= cused
+            if not origin is None:
+                server = server.relativize(origin)
+            servers.append(server)
+        return cls(rdclass, rdtype, hit, algorithm, key, servers)
+
+    from_wire = classmethod(from_wire)
+
+    def choose_relativity(self, origin = None, relativize = True):
+        servers = []
+        for server in self.servers:
+            server = server.choose_relativity(origin, relativize)
+            servers.append(server)
+        self.servers = servers
+
+    def _cmp(self, other):
+        b1 = cStringIO.StringIO()
+        lh = len(self.hit)
+        lk = len(self.key)
+        b1.write(struct.pack("!BBH", lh, self.algorithm, lk))
+        b1.write(self.hit)
+        b1.write(self.key)
+        b2 = cStringIO.StringIO()
+        lh = len(other.hit)
+        lk = len(other.key)
+        b2.write(struct.pack("!BBH", lh, other.algorithm, lk))
+        b2.write(other.hit)
+        b2.write(other.key)
+        v = cmp(b1.getvalue(), b2.getvalue())
+        if v != 0:
+            return v
+        ls = len(self.servers)
+        lo = len(other.servers)
+        count = min(ls, lo)
+        i = 0
+        while i < count:
+            v = cmp(self.servers[i], other.servers[i])
+            if v != 0:
+                return v
+            i += 1
+        return ls - lo
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/ISDN.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/ISDN.py
new file mode 100644
index 0000000..424d3a9
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/ISDN.py
@@ -0,0 +1,96 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.exception
+import dns.rdata
+import dns.tokenizer
+
+class ISDN(dns.rdata.Rdata):
+    """ISDN record
+
+    @ivar address: the ISDN address
+    @type address: string
+    @ivar subaddress: the ISDN subaddress (or '' if not present)
+    @type subaddress: string
+    @see: RFC 1183"""
+
+    __slots__ = ['address', 'subaddress']
+
+    def __init__(self, rdclass, rdtype, address, subaddress):
+        super(ISDN, self).__init__(rdclass, rdtype)
+        self.address = address
+        self.subaddress = subaddress
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        if self.subaddress:
+            return '"%s" "%s"' % (dns.rdata._escapify(self.address),
+                                  dns.rdata._escapify(self.subaddress))
+        else:
+            return '"%s"' % dns.rdata._escapify(self.address)
+
+    def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
+        address = tok.get_string()
+        t = tok.get()
+        if not t.is_eol_or_eof():
+            tok.unget(t)
+            subaddress = tok.get_string()
+        else:
+            tok.unget(t)
+            subaddress = ''
+        tok.get_eol()
+        return cls(rdclass, rdtype, address, subaddress)
+
+    from_text = classmethod(from_text)
+
+    def to_wire(self, file, compress = None, origin = None):
+        l = len(self.address)
+        assert l < 256
+        byte = chr(l)
+        file.write(byte)
+        file.write(self.address)
+        l = len(self.subaddress)
+        if l > 0:
+            assert l < 256
+            byte = chr(l)
+            file.write(byte)
+            file.write(self.subaddress)
+
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
+        l = ord(wire[current])
+        current += 1
+        rdlen -= 1
+        if l > rdlen:
+            raise dns.exception.FormError
+        address = wire[current : current + l]
+        current += l
+        rdlen -= l
+        if rdlen > 0:
+            l = ord(wire[current])
+            current += 1
+            rdlen -= 1
+            if l != rdlen:
+                raise dns.exception.FormError
+            subaddress = wire[current : current + l]
+        else:
+            subaddress = ''
+        return cls(rdclass, rdtype, address, subaddress)
+
+    from_wire = classmethod(from_wire)
+
+    def _cmp(self, other):
+        v = cmp(self.address, other.address)
+        if v == 0:
+            v = cmp(self.subaddress, other.subaddress)
+        return v
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/KEY.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/KEY.py
new file mode 100644
index 0000000..c8581ed
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/KEY.py
@@ -0,0 +1,20 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.rdtypes.keybase
+
+class KEY(dns.rdtypes.keybase.KEYBase):
+    """KEY record"""
+    pass
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/LOC.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/LOC.py
new file mode 100644
index 0000000..518dd60
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/LOC.py
@@ -0,0 +1,334 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import cStringIO
+import struct
+
+import dns.exception
+import dns.rdata
+
+_pows = (1L, 10L, 100L, 1000L, 10000L, 100000L, 1000000L, 10000000L,
+         100000000L, 1000000000L, 10000000000L)
+
+def _exponent_of(what, desc):
+    exp = None
+    for i in xrange(len(_pows)):
+        if what // _pows[i] == 0L:
+            exp = i - 1
+            break
+    if exp is None or exp < 0:
+        raise dns.exception.SyntaxError("%s value out of bounds" % desc)
+    return exp
+
+def _float_to_tuple(what):
+    if what < 0:
+        sign = -1
+        what *= -1
+    else:
+        sign = 1
+    what = long(round(what * 3600000))
+    degrees = int(what // 3600000)
+    what -= degrees * 3600000
+    minutes = int(what // 60000)
+    what -= minutes * 60000
+    seconds = int(what // 1000)
+    what -= int(seconds * 1000)
+    what = int(what)
+    return (degrees * sign, minutes, seconds, what)
+
+def _tuple_to_float(what):
+    if what[0] < 0:
+        sign = -1
+        value = float(what[0]) * -1
+    else:
+        sign = 1
+        value = float(what[0])
+    value += float(what[1]) / 60.0
+    value += float(what[2]) / 3600.0
+    value += float(what[3]) / 3600000.0
+    return sign * value
+
+def _encode_size(what, desc):
+    what = long(what);
+    exponent = _exponent_of(what, desc) & 0xF
+    base = what // pow(10, exponent) & 0xF
+    return base * 16 + exponent
+
+def _decode_size(what, desc):
+    exponent = what & 0x0F
+    if exponent > 9:
+        raise dns.exception.SyntaxError("bad %s exponent" % desc)
+    base = (what & 0xF0) >> 4
+    if base > 9:
+        raise dns.exception.SyntaxError("bad %s base" % desc)
+    return long(base) * pow(10, exponent)
+
+class LOC(dns.rdata.Rdata):
+    """LOC record
+
+    @ivar latitude: latitude
+    @type latitude: (int, int, int, int) tuple specifying the degrees, minutes,
+    seconds, and milliseconds of the coordinate.
+    @ivar longitude: longitude
+    @type longitude: (int, int, int, int) tuple specifying the degrees,
+    minutes, seconds, and milliseconds of the coordinate.
+    @ivar altitude: altitude
+    @type altitude: float
+    @ivar size: size of the sphere
+    @type size: float
+    @ivar horizontal_precision: horizontal precision
+    @type horizontal_precision: float
+    @ivar vertical_precision: vertical precision
+    @type vertical_precision: float
+    @see: RFC 1876"""
+
+    __slots__ = ['latitude', 'longitude', 'altitude', 'size',
+                 'horizontal_precision', 'vertical_precision']
+
+    def __init__(self, rdclass, rdtype, latitude, longitude, altitude,
+                 size=1.0, hprec=10000.0, vprec=10.0):
+        """Initialize a LOC record instance.
+
+        The parameters I{latitude} and I{longitude} may be either a 4-tuple
+        of integers specifying (degrees, minutes, seconds, milliseconds),
+        or they may be floating point values specifying the number of
+        degrees.  The other parameters are floats."""
+
+        super(LOC, self).__init__(rdclass, rdtype)
+        if isinstance(latitude, int) or isinstance(latitude, long):
+            latitude = float(latitude)
+        if isinstance(latitude, float):
+            latitude = _float_to_tuple(latitude)
+        self.latitude = latitude
+        if isinstance(longitude, int) or isinstance(longitude, long):
+            longitude = float(longitude)
+        if isinstance(longitude, float):
+            longitude = _float_to_tuple(longitude)
+        self.longitude = longitude
+        self.altitude = float(altitude)
+        self.size = float(size)
+        self.horizontal_precision = float(hprec)
+        self.vertical_precision = float(vprec)
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        if self.latitude[0] > 0:
+            lat_hemisphere = 'N'
+            lat_degrees = self.latitude[0]
+        else:
+            lat_hemisphere = 'S'
+            lat_degrees = -1 * self.latitude[0]
+        if self.longitude[0] > 0:
+            long_hemisphere = 'E'
+            long_degrees = self.longitude[0]
+        else:
+            long_hemisphere = 'W'
+            long_degrees = -1 * self.longitude[0]
+        text = "%d %d %d.%03d %s %d %d %d.%03d %s %0.2fm" % (
+            lat_degrees, self.latitude[1], self.latitude[2], self.latitude[3],
+            lat_hemisphere, long_degrees, self.longitude[1], self.longitude[2],
+            self.longitude[3], long_hemisphere, self.altitude / 100.0
+            )
+
+        if self.size != 1.0 or self.horizontal_precision != 10000.0 or \
+           self.vertical_precision != 10.0:
+            text += " %0.2fm %0.2fm %0.2fm" % (
+                self.size / 100.0, self.horizontal_precision / 100.0,
+                self.vertical_precision / 100.0
+            )
+        return text
+
+    def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
+        latitude = [0, 0, 0, 0]
+        longitude = [0, 0, 0, 0]
+        size = 1.0
+        hprec = 10000.0
+        vprec = 10.0
+
+        latitude[0] = tok.get_int()
+        t = tok.get_string()
+        if t.isdigit():
+            latitude[1] = int(t)
+            t = tok.get_string()
+            if '.' in t:
+                (seconds, milliseconds) = t.split('.')
+                if not seconds.isdigit():
+                    raise dns.exception.SyntaxError('bad latitude seconds value')
+                latitude[2] = int(seconds)
+                if latitude[2] >= 60:
+                    raise dns.exception.SyntaxError('latitude seconds >= 60')
+                l = len(milliseconds)
+                if l == 0 or l > 3 or not milliseconds.isdigit():
+                    raise dns.exception.SyntaxError('bad latitude milliseconds value')
+                if l == 1:
+                    m = 100
+                elif l == 2:
+                    m = 10
+                else:
+                    m = 1
+                latitude[3] = m * int(milliseconds)
+                t = tok.get_string()
+            elif t.isdigit():
+                latitude[2] = int(t)
+                t = tok.get_string()
+        if t == 'S':
+            latitude[0] *= -1
+        elif t != 'N':
+            raise dns.exception.SyntaxError('bad latitude hemisphere value')
+
+        longitude[0] = tok.get_int()
+        t = tok.get_string()
+        if t.isdigit():
+            longitude[1] = int(t)
+            t = tok.get_string()
+            if '.' in t:
+                (seconds, milliseconds) = t.split('.')
+                if not seconds.isdigit():
+                    raise dns.exception.SyntaxError('bad longitude seconds value')
+                longitude[2] = int(seconds)
+                if longitude[2] >= 60:
+                    raise dns.exception.SyntaxError('longitude seconds >= 60')
+                l = len(milliseconds)
+                if l == 0 or l > 3 or not milliseconds.isdigit():
+                    raise dns.exception.SyntaxError('bad longitude milliseconds value')
+                if l == 1:
+                    m = 100
+                elif l == 2:
+                    m = 10
+                else:
+                    m = 1
+                longitude[3] = m * int(milliseconds)
+                t = tok.get_string()
+            elif t.isdigit():
+                longitude[2] = int(t)
+                t = tok.get_string()
+        if t == 'W':
+            longitude[0] *= -1
+        elif t != 'E':
+            raise dns.exception.SyntaxError('bad longitude hemisphere value')
+
+        t = tok.get_string()
+        if t[-1] == 'm':
+            t = t[0 : -1]
+        altitude = float(t) * 100.0	# m -> cm
+
+        token = tok.get().unescape()
+        if not token.is_eol_or_eof():
+            value = token.value
+            if value[-1] == 'm':
+                value = value[0 : -1]
+            size = float(value) * 100.0	# m -> cm
+            token = tok.get().unescape()
+            if not token.is_eol_or_eof():
+                value = token.value
+                if value[-1] == 'm':
+                    value = value[0 : -1]
+                hprec = float(value) * 100.0	# m -> cm
+                token = tok.get().unescape()
+                if not token.is_eol_or_eof():
+                    value = token.value
+                    if value[-1] == 'm':
+                        value = value[0 : -1]
+                        vprec = float(value) * 100.0	# m -> cm
+                        tok.get_eol()
+
+        return cls(rdclass, rdtype, latitude, longitude, altitude,
+                   size, hprec, vprec)
+
+    from_text = classmethod(from_text)
+
+    def to_wire(self, file, compress = None, origin = None):
+        if self.latitude[0] < 0:
+            sign = -1
+            degrees = long(-1 * self.latitude[0])
+        else:
+            sign = 1
+            degrees = long(self.latitude[0])
+        milliseconds = (degrees * 3600000 +
+                        self.latitude[1] * 60000 +
+                        self.latitude[2] * 1000 +
+                        self.latitude[3]) * sign
+        latitude = 0x80000000L + milliseconds
+        if self.longitude[0] < 0:
+            sign = -1
+            degrees = long(-1 * self.longitude[0])
+        else:
+            sign = 1
+            degrees = long(self.longitude[0])
+        milliseconds = (degrees * 3600000 +
+                        self.longitude[1] * 60000 +
+                        self.longitude[2] * 1000 +
+                        self.longitude[3]) * sign
+        longitude = 0x80000000L + milliseconds
+        altitude = long(self.altitude) + 10000000L
+        size = _encode_size(self.size, "size")
+        hprec = _encode_size(self.horizontal_precision, "horizontal precision")
+        vprec = _encode_size(self.vertical_precision, "vertical precision")
+        wire = struct.pack("!BBBBIII", 0, size, hprec, vprec, latitude,
+                           longitude, altitude)
+        file.write(wire)
+
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
+        (version, size, hprec, vprec, latitude, longitude, altitude) = \
+                  struct.unpack("!BBBBIII", wire[current : current + rdlen])
+        if latitude > 0x80000000L:
+            latitude = float(latitude - 0x80000000L) / 3600000
+        else:
+            latitude = -1 * float(0x80000000L - latitude) / 3600000
+        if latitude < -90.0 or latitude > 90.0:
+            raise dns.exception.FormError("bad latitude")
+        if longitude > 0x80000000L:
+            longitude = float(longitude - 0x80000000L) / 3600000
+        else:
+            longitude = -1 * float(0x80000000L - longitude) / 3600000
+        if longitude < -180.0 or longitude > 180.0:
+            raise dns.exception.FormError("bad longitude")
+        altitude = float(altitude) - 10000000.0
+        size = _decode_size(size, "size")
+        hprec = _decode_size(hprec, "horizontal precision")
+        vprec = _decode_size(vprec, "vertical precision")
+        return cls(rdclass, rdtype, latitude, longitude, altitude,
+                   size, hprec, vprec)
+
+    from_wire = classmethod(from_wire)
+
+    def _cmp(self, other):
+        f = cStringIO.StringIO()
+        self.to_wire(f)
+        wire1 = f.getvalue()
+        f.seek(0)
+        f.truncate()
+        other.to_wire(f)
+        wire2 = f.getvalue()
+        f.close()
+
+        return cmp(wire1, wire2)
+
+    def _get_float_latitude(self):
+        return _tuple_to_float(self.latitude)
+
+    def _set_float_latitude(self, value):
+        self.latitude = _float_to_tuple(value)
+
+    float_latitude = property(_get_float_latitude, _set_float_latitude,
+                              doc="latitude as a floating point value")
+
+    def _get_float_longitude(self):
+        return _tuple_to_float(self.longitude)
+
+    def _set_float_longitude(self, value):
+        self.longitude = _float_to_tuple(value)
+
+    float_longitude = property(_get_float_longitude, _set_float_longitude,
+                               doc="longitude as a floating point value")
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/MX.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/MX.py
new file mode 100644
index 0000000..9cad260
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/MX.py
@@ -0,0 +1,20 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.rdtypes.mxbase
+
+class MX(dns.rdtypes.mxbase.MXBase):
+    """MX record"""
+    pass
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/NS.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/NS.py
new file mode 100644
index 0000000..4b03a3a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/NS.py
@@ -0,0 +1,20 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.rdtypes.nsbase
+
+class NS(dns.rdtypes.nsbase.NSBase):
+    """NS record"""
+    pass
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/NSEC.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/NSEC.py
new file mode 100644
index 0000000..72859ce
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/NSEC.py
@@ -0,0 +1,141 @@
+# Copyright (C) 2004-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import cStringIO
+
+import dns.exception
+import dns.rdata
+import dns.rdatatype
+import dns.name
+
+class NSEC(dns.rdata.Rdata):
+    """NSEC record
+
+    @ivar next: the next name
+    @type next: dns.name.Name object
+    @ivar windows: the windowed bitmap list
+    @type windows: list of (window number, string) tuples"""
+
+    __slots__ = ['next', 'windows']
+
+    def __init__(self, rdclass, rdtype, next, windows):
+        super(NSEC, self).__init__(rdclass, rdtype)
+        self.next = next
+        self.windows = windows
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        next = self.next.choose_relativity(origin, relativize)
+        text = ''
+        for (window, bitmap) in self.windows:
+            bits = []
+            for i in xrange(0, len(bitmap)):
+                byte = ord(bitmap[i])
+                for j in xrange(0, 8):
+                    if byte & (0x80 >> j):
+                        bits.append(dns.rdatatype.to_text(window * 256 + \
+                                                          i * 8 + j))
+            text += (' ' + ' '.join(bits))
+        return '%s%s' % (next, text)
+
+    def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
+        next = tok.get_name()
+        next = next.choose_relativity(origin, relativize)
+        rdtypes = []
+        while 1:
+            token = tok.get().unescape()
+            if token.is_eol_or_eof():
+                break
+            nrdtype = dns.rdatatype.from_text(token.value)
+            if nrdtype == 0:
+                raise dns.exception.SyntaxError("NSEC with bit 0")
+            if nrdtype > 65535:
+                raise dns.exception.SyntaxError("NSEC with bit > 65535")
+            rdtypes.append(nrdtype)
+        rdtypes.sort()
+        window = 0
+        octets = 0
+        prior_rdtype = 0
+        bitmap = ['\0'] * 32
+        windows = []
+        for nrdtype in rdtypes:
+            if nrdtype == prior_rdtype:
+                continue
+            prior_rdtype = nrdtype
+            new_window = nrdtype // 256
+            if new_window != window:
+                windows.append((window, ''.join(bitmap[0:octets])))
+                bitmap = ['\0'] * 32
+                window = new_window
+            offset = nrdtype % 256
+            byte = offset / 8
+            bit = offset % 8
+            octets = byte + 1
+            bitmap[byte] = chr(ord(bitmap[byte]) | (0x80 >> bit))
+        windows.append((window, ''.join(bitmap[0:octets])))
+        return cls(rdclass, rdtype, next, windows)
+
+    from_text = classmethod(from_text)
+
+    def to_wire(self, file, compress = None, origin = None):
+        self.next.to_wire(file, None, origin)
+        for (window, bitmap) in self.windows:
+            file.write(chr(window))
+            file.write(chr(len(bitmap)))
+            file.write(bitmap)
+
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
+        (next, cused) = dns.name.from_wire(wire[: current + rdlen], current)
+        current += cused
+        rdlen -= cused
+        windows = []
+        while rdlen > 0:
+            if rdlen < 3:
+                raise dns.exception.FormError("NSEC too short")
+            window = ord(wire[current])
+            octets = ord(wire[current + 1])
+            if octets == 0 or octets > 32:
+                raise dns.exception.FormError("bad NSEC octets")
+            current += 2
+            rdlen -= 2
+            if rdlen < octets:
+                raise dns.exception.FormError("bad NSEC bitmap length")
+            bitmap = wire[current : current + octets]
+            current += octets
+            rdlen -= octets
+            windows.append((window, bitmap))
+        if not origin is None:
+            next = next.relativize(origin)
+        return cls(rdclass, rdtype, next, windows)
+
+    from_wire = classmethod(from_wire)
+
+    def choose_relativity(self, origin = None, relativize = True):
+        self.next = self.next.choose_relativity(origin, relativize)
+
+    def _cmp(self, other):
+        v = cmp(self.next, other.next)
+        if v == 0:
+            b1 = cStringIO.StringIO()
+            for (window, bitmap) in self.windows:
+                b1.write(chr(window))
+                b1.write(chr(len(bitmap)))
+                b1.write(bitmap)
+            b2 = cStringIO.StringIO()
+            for (window, bitmap) in other.windows:
+                b2.write(chr(window))
+                b2.write(chr(len(bitmap)))
+                b2.write(bitmap)
+            v = cmp(b1.getvalue(), b2.getvalue())
+        return v
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/NSEC3.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/NSEC3.py
new file mode 100644
index 0000000..932d7b4
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/NSEC3.py
@@ -0,0 +1,182 @@
+# Copyright (C) 2004-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import base64
+import cStringIO
+import string
+import struct
+
+import dns.exception
+import dns.rdata
+import dns.rdatatype
+
+b32_hex_to_normal = string.maketrans('0123456789ABCDEFGHIJKLMNOPQRSTUV',
+                                     'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567')
+b32_normal_to_hex = string.maketrans('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567',
+                                     '0123456789ABCDEFGHIJKLMNOPQRSTUV')
+
+# hash algorithm constants
+SHA1 = 1
+
+# flag constants
+OPTOUT = 1
+
+class NSEC3(dns.rdata.Rdata):
+    """NSEC3 record
+
+    @ivar algorithm: the hash algorithm number
+    @type algorithm: int
+    @ivar flags: the flags
+    @type flags: int
+    @ivar iterations: the number of iterations
+    @type iterations: int
+    @ivar salt: the salt
+    @type salt: string
+    @ivar next: the next name hash
+    @type next: string
+    @ivar windows: the windowed bitmap list
+    @type windows: list of (window number, string) tuples"""
+
+    __slots__ = ['algorithm', 'flags', 'iterations', 'salt', 'next', 'windows']
+
+    def __init__(self, rdclass, rdtype, algorithm, flags, iterations, salt,
+                 next, windows):
+        super(NSEC3, self).__init__(rdclass, rdtype)
+        self.algorithm = algorithm
+        self.flags = flags
+        self.iterations = iterations
+        self.salt = salt
+        self.next = next
+        self.windows = windows
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        next = base64.b32encode(self.next).translate(b32_normal_to_hex).lower()
+        if self.salt == '':
+            salt = '-'
+        else:
+            salt = self.salt.encode('hex-codec')
+        text = ''
+        for (window, bitmap) in self.windows:
+            bits = []
+            for i in xrange(0, len(bitmap)):
+                byte = ord(bitmap[i])
+                for j in xrange(0, 8):
+                    if byte & (0x80 >> j):
+                        bits.append(dns.rdatatype.to_text(window * 256 + \
+                                                          i * 8 + j))
+            text += (' ' + ' '.join(bits))
+        return '%u %u %u %s %s%s' % (self.algorithm, self.flags, self.iterations,
+                                     salt, next, text)
+
+    def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
+        algorithm = tok.get_uint8()
+        flags = tok.get_uint8()
+        iterations = tok.get_uint16()
+        salt = tok.get_string()
+        if salt == '-':
+            salt = ''
+        else:
+            salt = salt.decode('hex-codec')
+        next = tok.get_string().upper().translate(b32_hex_to_normal)
+        next = base64.b32decode(next)
+        rdtypes = []
+        while 1:
+            token = tok.get().unescape()
+            if token.is_eol_or_eof():
+                break
+            nrdtype = dns.rdatatype.from_text(token.value)
+            if nrdtype == 0:
+                raise dns.exception.SyntaxError("NSEC3 with bit 0")
+            if nrdtype > 65535:
+                raise dns.exception.SyntaxError("NSEC3 with bit > 65535")
+            rdtypes.append(nrdtype)
+        rdtypes.sort()
+        window = 0
+        octets = 0
+        prior_rdtype = 0
+        bitmap = ['\0'] * 32
+        windows = []
+        for nrdtype in rdtypes:
+            if nrdtype == prior_rdtype:
+                continue
+            prior_rdtype = nrdtype
+            new_window = nrdtype // 256
+            if new_window != window:
+                windows.append((window, ''.join(bitmap[0:octets])))
+                bitmap = ['\0'] * 32
+                window = new_window
+            offset = nrdtype % 256
+            byte = offset / 8
+            bit = offset % 8
+            octets = byte + 1
+            bitmap[byte] = chr(ord(bitmap[byte]) | (0x80 >> bit))
+        windows.append((window, ''.join(bitmap[0:octets])))
+        return cls(rdclass, rdtype, algorithm, flags, iterations, salt, next, windows)
+
+    from_text = classmethod(from_text)
+
+    def to_wire(self, file, compress = None, origin = None):
+        l = len(self.salt)
+        file.write(struct.pack("!BBHB", self.algorithm, self.flags,
+                               self.iterations, l))
+        file.write(self.salt)
+        l = len(self.next)
+        file.write(struct.pack("!B", l))
+        file.write(self.next)
+        for (window, bitmap) in self.windows:
+            file.write(chr(window))
+            file.write(chr(len(bitmap)))
+            file.write(bitmap)
+
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
+        (algorithm, flags, iterations, slen) = struct.unpack('!BBHB',
+                                                             wire[current : current + 5])
+        current += 5
+        rdlen -= 5
+        salt = wire[current : current + slen]
+        current += slen
+        rdlen -= slen
+        (nlen, ) = struct.unpack('!B', wire[current])
+        current += 1
+        rdlen -= 1
+        next = wire[current : current + nlen]
+        current += nlen
+        rdlen -= nlen
+        windows = []
+        while rdlen > 0:
+            if rdlen < 3:
+                raise dns.exception.FormError("NSEC3 too short")
+            window = ord(wire[current])
+            octets = ord(wire[current + 1])
+            if octets == 0 or octets > 32:
+                raise dns.exception.FormError("bad NSEC3 octets")
+            current += 2
+            rdlen -= 2
+            if rdlen < octets:
+                raise dns.exception.FormError("bad NSEC3 bitmap length")
+            bitmap = wire[current : current + octets]
+            current += octets
+            rdlen -= octets
+            windows.append((window, bitmap))
+        return cls(rdclass, rdtype, algorithm, flags, iterations, salt, next, windows)
+
+    from_wire = classmethod(from_wire)
+
+    def _cmp(self, other):
+        b1 = cStringIO.StringIO()
+        self.to_wire(b1)
+        b2 = cStringIO.StringIO()
+        other.to_wire(b2)
+        return cmp(b1.getvalue(), b2.getvalue())
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/NSEC3PARAM.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/NSEC3PARAM.py
new file mode 100644
index 0000000..ec91e5e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/NSEC3PARAM.py
@@ -0,0 +1,88 @@
+# Copyright (C) 2004-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import cStringIO
+import struct
+
+import dns.exception
+import dns.rdata
+
+class NSEC3PARAM(dns.rdata.Rdata):
+    """NSEC3PARAM record
+
+    @ivar algorithm: the hash algorithm number
+    @type algorithm: int
+    @ivar flags: the flags
+    @type flags: int
+    @ivar iterations: the number of iterations
+    @type iterations: int
+    @ivar salt: the salt
+    @type salt: string"""
+
+    __slots__ = ['algorithm', 'flags', 'iterations', 'salt']
+
+    def __init__(self, rdclass, rdtype, algorithm, flags, iterations, salt):
+        super(NSEC3PARAM, self).__init__(rdclass, rdtype)
+        self.algorithm = algorithm
+        self.flags = flags
+        self.iterations = iterations
+        self.salt = salt
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        if self.salt == '':
+            salt = '-'
+        else:
+            salt = self.salt.encode('hex-codec')
+        return '%u %u %u %s' % (self.algorithm, self.flags, self.iterations, salt)
+
+    def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
+        algorithm = tok.get_uint8()
+        flags = tok.get_uint8()
+        iterations = tok.get_uint16()
+        salt = tok.get_string()
+        if salt == '-':
+            salt = ''
+        else:
+            salt = salt.decode('hex-codec')
+        return cls(rdclass, rdtype, algorithm, flags, iterations, salt)
+
+    from_text = classmethod(from_text)
+
+    def to_wire(self, file, compress = None, origin = None):
+        l = len(self.salt)
+        file.write(struct.pack("!BBHB", self.algorithm, self.flags,
+                               self.iterations, l))
+        file.write(self.salt)
+
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
+        (algorithm, flags, iterations, slen) = struct.unpack('!BBHB',
+                                                             wire[current : current + 5])
+        current += 5
+        rdlen -= 5
+        salt = wire[current : current + slen]
+        current += slen
+        rdlen -= slen
+        if rdlen != 0:
+            raise dns.exception.FormError
+        return cls(rdclass, rdtype, algorithm, flags, iterations, salt)
+
+    from_wire = classmethod(from_wire)
+
+    def _cmp(self, other):
+        b1 = cStringIO.StringIO()
+        self.to_wire(b1)
+        b2 = cStringIO.StringIO()
+        other.to_wire(b2)
+        return cmp(b1.getvalue(), b2.getvalue())
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/NXT.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/NXT.py
new file mode 100644
index 0000000..99ae9b9
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/NXT.py
@@ -0,0 +1,99 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.exception
+import dns.rdata
+import dns.rdatatype
+import dns.name
+
+class NXT(dns.rdata.Rdata):
+    """NXT record
+
+    @ivar next: the next name
+    @type next: dns.name.Name object
+    @ivar bitmap: the type bitmap
+    @type bitmap: string
+    @see: RFC 2535"""
+
+    __slots__ = ['next', 'bitmap']
+
+    def __init__(self, rdclass, rdtype, next, bitmap):
+        super(NXT, self).__init__(rdclass, rdtype)
+        self.next = next
+        self.bitmap = bitmap
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        next = self.next.choose_relativity(origin, relativize)
+        bits = []
+        for i in xrange(0, len(self.bitmap)):
+            byte = ord(self.bitmap[i])
+            for j in xrange(0, 8):
+                if byte & (0x80 >> j):
+                    bits.append(dns.rdatatype.to_text(i * 8 + j))
+        text = ' '.join(bits)
+        return '%s %s' % (next, text)
+
+    def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
+        next = tok.get_name()
+        next = next.choose_relativity(origin, relativize)
+        bitmap = ['\x00', '\x00', '\x00', '\x00',
+                  '\x00', '\x00', '\x00', '\x00',
+                  '\x00', '\x00', '\x00', '\x00',
+                  '\x00', '\x00', '\x00', '\x00' ]
+        while 1:
+            token = tok.get().unescape()
+            if token.is_eol_or_eof():
+                break
+            if token.value.isdigit():
+                nrdtype = int(token.value)
+            else:
+                nrdtype = dns.rdatatype.from_text(token.value)
+            if nrdtype == 0:
+                raise dns.exception.SyntaxError("NXT with bit 0")
+            if nrdtype > 127:
+                raise dns.exception.SyntaxError("NXT with bit > 127")
+            i = nrdtype // 8
+            bitmap[i] = chr(ord(bitmap[i]) | (0x80 >> (nrdtype % 8)))
+        bitmap = dns.rdata._truncate_bitmap(bitmap)
+        return cls(rdclass, rdtype, next, bitmap)
+
+    from_text = classmethod(from_text)
+
+    def to_wire(self, file, compress = None, origin = None):
+        self.next.to_wire(file, None, origin)
+        file.write(self.bitmap)
+
+    def to_digestable(self, origin = None):
+        return self.next.to_digestable(origin) + self.bitmap
+
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
+        (next, cused) = dns.name.from_wire(wire[: current + rdlen], current)
+        current += cused
+        rdlen -= cused
+        bitmap = wire[current : current + rdlen]
+        if not origin is None:
+            next = next.relativize(origin)
+        return cls(rdclass, rdtype, next, bitmap)
+
+    from_wire = classmethod(from_wire)
+
+    def choose_relativity(self, origin = None, relativize = True):
+        self.next = self.next.choose_relativity(origin, relativize)
+
+    def _cmp(self, other):
+        v = cmp(self.next, other.next)
+        if v == 0:
+            v = cmp(self.bitmap, other.bitmap)
+        return v
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/PTR.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/PTR.py
new file mode 100644
index 0000000..6c4b79e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/PTR.py
@@ -0,0 +1,20 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.rdtypes.nsbase
+
+class PTR(dns.rdtypes.nsbase.NSBase):
+    """PTR record"""
+    pass
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/RP.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/RP.py
new file mode 100644
index 0000000..421ce8e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/RP.py
@@ -0,0 +1,86 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.exception
+import dns.rdata
+import dns.name
+
+class RP(dns.rdata.Rdata):
+    """RP record
+
+    @ivar mbox: The responsible person's mailbox
+    @type mbox: dns.name.Name object
+    @ivar txt: The owner name of a node with TXT records, or the root name
+    if no TXT records are associated with this RP.
+    @type txt: dns.name.Name object
+    @see: RFC 1183"""
+
+    __slots__ = ['mbox', 'txt']
+
+    def __init__(self, rdclass, rdtype, mbox, txt):
+        super(RP, self).__init__(rdclass, rdtype)
+        self.mbox = mbox
+        self.txt = txt
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        mbox = self.mbox.choose_relativity(origin, relativize)
+        txt = self.txt.choose_relativity(origin, relativize)
+        return "%s %s" % (str(mbox), str(txt))
+
+    def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
+        mbox = tok.get_name()
+        txt = tok.get_name()
+        mbox = mbox.choose_relativity(origin, relativize)
+        txt = txt.choose_relativity(origin, relativize)
+        tok.get_eol()
+        return cls(rdclass, rdtype, mbox, txt)
+
+    from_text = classmethod(from_text)
+
+    def to_wire(self, file, compress = None, origin = None):
+        self.mbox.to_wire(file, None, origin)
+        self.txt.to_wire(file, None, origin)
+
+    def to_digestable(self, origin = None):
+        return self.mbox.to_digestable(origin) + \
+            self.txt.to_digestable(origin)
+
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
+        (mbox, cused) = dns.name.from_wire(wire[: current + rdlen],
+                                           current)
+        current += cused
+        rdlen -= cused
+        if rdlen <= 0:
+            raise dns.exception.FormError
+        (txt, cused) = dns.name.from_wire(wire[: current + rdlen],
+                                          current)
+        if cused != rdlen:
+            raise dns.exception.FormError
+        if not origin is None:
+            mbox = mbox.relativize(origin)
+            txt = txt.relativize(origin)
+        return cls(rdclass, rdtype, mbox, txt)
+
+    from_wire = classmethod(from_wire)
+
+    def choose_relativity(self, origin = None, relativize = True):
+        self.mbox = self.mbox.choose_relativity(origin, relativize)
+        self.txt = self.txt.choose_relativity(origin, relativize)
+
+    def _cmp(self, other):
+        v = cmp(self.mbox, other.mbox)
+        if v == 0:
+            v = cmp(self.txt, other.txt)
+        return v
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/RRSIG.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/RRSIG.py
new file mode 100644
index 0000000..0e4816f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/RRSIG.py
@@ -0,0 +1,20 @@
+# Copyright (C) 2004-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.rdtypes.sigbase
+
+class RRSIG(dns.rdtypes.sigbase.SIGBase):
+    """RRSIG record"""
+    pass
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/RT.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/RT.py
new file mode 100644
index 0000000..1efd372
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/RT.py
@@ -0,0 +1,20 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.rdtypes.mxbase
+
+class RT(dns.rdtypes.mxbase.UncompressedDowncasingMX):
+    """RT record"""
+    pass
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/SIG.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/SIG.py
new file mode 100644
index 0000000..501e29c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/SIG.py
@@ -0,0 +1,26 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.rdtypes.sigbase
+
+class SIG(dns.rdtypes.sigbase.SIGBase):
+    """SIG record"""
+    def to_digestable(self, origin = None):
+        return struct.pack('!HBBIIIH', self.type_covered,
+                           self.algorithm, self.labels,
+                           self.original_ttl, self.expiration,
+                           self.inception, self.key_tag) + \
+                           self.signer.to_digestable(origin) + \
+                           self.signature
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/SOA.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/SOA.py
new file mode 100644
index 0000000..a25a35e
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/SOA.py
@@ -0,0 +1,127 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import struct
+
+import dns.exception
+import dns.rdata
+import dns.name
+
+class SOA(dns.rdata.Rdata):
+    """SOA record
+
+    @ivar mname: the SOA MNAME (master name) field
+    @type mname: dns.name.Name object
+    @ivar rname: the SOA RNAME (responsible name) field
+    @type rname: dns.name.Name object
+    @ivar serial: The zone's serial number
+    @type serial: int
+    @ivar refresh: The zone's refresh value (in seconds)
+    @type refresh: int
+    @ivar retry: The zone's retry value (in seconds)
+    @type retry: int
+    @ivar expire: The zone's expiration value (in seconds)
+    @type expire: int
+    @ivar minimum: The zone's negative caching time (in seconds, called
+    "minimum" for historical reasons)
+    @type minimum: int
+    @see: RFC 1035"""
+
+    __slots__ = ['mname', 'rname', 'serial', 'refresh', 'retry', 'expire',
+                 'minimum']
+    
+    def __init__(self, rdclass, rdtype, mname, rname, serial, refresh, retry,
+                 expire, minimum):
+        super(SOA, self).__init__(rdclass, rdtype)
+        self.mname = mname
+        self.rname = rname
+        self.serial = serial
+        self.refresh = refresh
+        self.retry = retry
+        self.expire = expire
+        self.minimum = minimum
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        mname = self.mname.choose_relativity(origin, relativize)
+        rname = self.rname.choose_relativity(origin, relativize)
+        return '%s %s %d %d %d %d %d' % (
+            mname, rname, self.serial, self.refresh, self.retry,
+            self.expire, self.minimum )
+        
+    def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
+        mname = tok.get_name()
+        rname = tok.get_name()
+        mname = mname.choose_relativity(origin, relativize)
+        rname = rname.choose_relativity(origin, relativize)
+        serial = tok.get_uint32()
+        refresh = tok.get_ttl()
+        retry = tok.get_ttl()
+        expire = tok.get_ttl()
+        minimum = tok.get_ttl()
+        tok.get_eol()
+        return cls(rdclass, rdtype, mname, rname, serial, refresh, retry,
+                   expire, minimum )
+    
+    from_text = classmethod(from_text)
+
+    def to_wire(self, file, compress = None, origin = None):
+        self.mname.to_wire(file, compress, origin)
+        self.rname.to_wire(file, compress, origin)
+        five_ints = struct.pack('!IIIII', self.serial, self.refresh,
+                                self.retry, self.expire, self.minimum)
+        file.write(five_ints)
+
+    def to_digestable(self, origin = None):
+        return self.mname.to_digestable(origin) + \
+            self.rname.to_digestable(origin) + \
+            struct.pack('!IIIII', self.serial, self.refresh,
+                        self.retry, self.expire, self.minimum)
+
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
+        (mname, cused) = dns.name.from_wire(wire[: current + rdlen], current)
+        current += cused
+        rdlen -= cused
+        (rname, cused) = dns.name.from_wire(wire[: current + rdlen], current)
+        current += cused
+        rdlen -= cused
+        if rdlen != 20:
+            raise dns.exception.FormError
+        five_ints = struct.unpack('!IIIII',
+                                  wire[current : current + rdlen])
+        if not origin is None:
+            mname = mname.relativize(origin)
+            rname = rname.relativize(origin)
+        return cls(rdclass, rdtype, mname, rname,
+                   five_ints[0], five_ints[1], five_ints[2], five_ints[3],
+                   five_ints[4])
+
+    from_wire = classmethod(from_wire)
+
+    def choose_relativity(self, origin = None, relativize = True):
+        self.mname = self.mname.choose_relativity(origin, relativize)
+        self.rname = self.rname.choose_relativity(origin, relativize)
+
+    def _cmp(self, other):
+        v = cmp(self.mname, other.mname)
+        if v == 0:
+            v = cmp(self.rname, other.rname)
+            if v == 0:
+                self_ints = struct.pack('!IIIII', self.serial, self.refresh,
+                                        self.retry, self.expire, self.minimum)
+                other_ints = struct.pack('!IIIII', other.serial, other.refresh,
+                                         other.retry, other.expire,
+                                         other.minimum)
+                v = cmp(self_ints, other_ints)
+        return v
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/SPF.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/SPF.py
new file mode 100644
index 0000000..9b5a9a9
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/SPF.py
@@ -0,0 +1,22 @@
+# Copyright (C) 2006, 2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.rdtypes.txtbase
+
+class SPF(dns.rdtypes.txtbase.TXTBase):
+    """SPF record
+
+    @see: RFC 4408"""
+    pass
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/SSHFP.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/SSHFP.py
new file mode 100644
index 0000000..162dda5
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/SSHFP.py
@@ -0,0 +1,77 @@
+# Copyright (C) 2005-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import struct
+
+import dns.rdata
+import dns.rdatatype
+
+class SSHFP(dns.rdata.Rdata):
+    """SSHFP record
+
+    @ivar algorithm: the algorithm
+    @type algorithm: int
+    @ivar fp_type: the digest type
+    @type fp_type: int
+    @ivar fingerprint: the fingerprint
+    @type fingerprint: string
+    @see: draft-ietf-secsh-dns-05.txt"""
+
+    __slots__ = ['algorithm', 'fp_type', 'fingerprint']
+    
+    def __init__(self, rdclass, rdtype, algorithm, fp_type,
+                 fingerprint):
+        super(SSHFP, self).__init__(rdclass, rdtype)
+        self.algorithm = algorithm
+        self.fp_type = fp_type
+        self.fingerprint = fingerprint
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return '%d %d %s' % (self.algorithm,
+                             self.fp_type,
+                             dns.rdata._hexify(self.fingerprint,
+                                               chunksize=128))
+        
+    def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
+        algorithm = tok.get_uint8()
+        fp_type = tok.get_uint8()
+        fingerprint = tok.get_string()
+        fingerprint = fingerprint.decode('hex_codec')
+        tok.get_eol()
+        return cls(rdclass, rdtype, algorithm, fp_type, fingerprint)
+    
+    from_text = classmethod(from_text)
+
+    def to_wire(self, file, compress = None, origin = None):
+        header = struct.pack("!BB", self.algorithm, self.fp_type)
+        file.write(header)
+        file.write(self.fingerprint)
+        
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
+        header = struct.unpack("!BB", wire[current : current + 2])
+        current += 2
+        rdlen -= 2
+        fingerprint = wire[current : current + rdlen]
+        return cls(rdclass, rdtype, header[0], header[1], fingerprint)
+
+    from_wire = classmethod(from_wire)
+
+    def _cmp(self, other):
+        hs = struct.pack("!BB", self.algorithm, self.fp_type)
+        ho = struct.pack("!BB", other.algorithm, other.fp_type)
+        v = cmp(hs, ho)
+        if v == 0:
+            v = cmp(self.fingerprint, other.fingerprint)
+        return v
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/TXT.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/TXT.py
new file mode 100644
index 0000000..23f4f3b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/TXT.py
@@ -0,0 +1,20 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.rdtypes.txtbase
+
+class TXT(dns.rdtypes.txtbase.TXTBase):
+    """TXT record"""
+    pass
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/X25.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/X25.py
new file mode 100644
index 0000000..c3632f7
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/X25.py
@@ -0,0 +1,62 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.exception
+import dns.rdata
+import dns.tokenizer
+
+class X25(dns.rdata.Rdata):
+    """X25 record
+
+    @ivar address: the PSDN address
+    @type address: string
+    @see: RFC 1183"""
+
+    __slots__ = ['address']
+    
+    def __init__(self, rdclass, rdtype, address):
+        super(X25, self).__init__(rdclass, rdtype)
+        self.address = address
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return '"%s"' % dns.rdata._escapify(self.address)
+        
+    def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
+        address = tok.get_string()
+        tok.get_eol()
+        return cls(rdclass, rdtype, address)
+    
+    from_text = classmethod(from_text)
+
+    def to_wire(self, file, compress = None, origin = None):
+        l = len(self.address)
+        assert l < 256
+        byte = chr(l)
+        file.write(byte)
+        file.write(self.address)
+        
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
+        l = ord(wire[current])
+        current += 1
+        rdlen -= 1
+        if l != rdlen:
+            raise dns.exception.FormError
+        address = wire[current : current + l]
+        return cls(rdclass, rdtype, address)
+
+    from_wire = classmethod(from_wire)
+
+    def _cmp(self, other):
+        return cmp(self.address, other.address)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/__init__.py
new file mode 100644
index 0000000..0815dd5
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/ANY/__init__.py
@@ -0,0 +1,48 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""Class ANY (generic) rdata type classes."""
+
+__all__ = [
+    'AFSDB',
+    'CERT',
+    'CNAME',
+    'DLV',
+    'DNAME',
+    'DNSKEY',
+    'DS',
+    'GPOS',
+    'HINFO',
+    'HIP',
+    'ISDN',
+    'KEY',
+    'LOC',
+    'MX',
+    'NS',
+    'NSEC',
+    'NSEC3',
+    'NSEC3PARAM',
+    'NXT',
+    'PTR',
+    'RP',
+    'RRSIG',
+    'RT',
+    'SIG',
+    'SOA',
+    'SPF',
+    'SSHFP',
+    'TXT',
+    'X25',
+]
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/IN/A.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/IN/A.py
new file mode 100644
index 0000000..e05f204
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/IN/A.py
@@ -0,0 +1,57 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.exception
+import dns.ipv4
+import dns.rdata
+import dns.tokenizer
+
+class A(dns.rdata.Rdata):
+    """A record.
+
+    @ivar address: an IPv4 address
+    @type address: string (in the standard "dotted quad" format)"""
+
+    __slots__ = ['address']
+
+    def __init__(self, rdclass, rdtype, address):
+        super(A, self).__init__(rdclass, rdtype)
+        # check that it's OK
+        junk = dns.ipv4.inet_aton(address)
+        self.address = address
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return self.address
+
+    def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
+        address = tok.get_identifier()
+        tok.get_eol()
+        return cls(rdclass, rdtype, address)
+
+    from_text = classmethod(from_text)
+
+    def to_wire(self, file, compress = None, origin = None):
+        file.write(dns.ipv4.inet_aton(self.address))
+
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
+        address = dns.ipv4.inet_ntoa(wire[current : current + rdlen])
+        return cls(rdclass, rdtype, address)
+
+    from_wire = classmethod(from_wire)
+
+    def _cmp(self, other):
+        sa = dns.ipv4.inet_aton(self.address)
+        oa = dns.ipv4.inet_aton(other.address)
+        return cmp(sa, oa)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/IN/AAAA.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/IN/AAAA.py
new file mode 100644
index 0000000..2d812d3
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/IN/AAAA.py
@@ -0,0 +1,58 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.exception
+import dns.inet
+import dns.rdata
+import dns.tokenizer
+
+class AAAA(dns.rdata.Rdata):
+    """AAAA record.
+
+    @ivar address: an IPv6 address
+    @type address: string (in the standard IPv6 format)"""
+
+    __slots__ = ['address']
+
+    def __init__(self, rdclass, rdtype, address):
+        super(AAAA, self).__init__(rdclass, rdtype)
+        # check that it's OK
+        junk = dns.inet.inet_pton(dns.inet.AF_INET6, address)
+        self.address = address
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return self.address
+
+    def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
+        address = tok.get_identifier()
+        tok.get_eol()
+        return cls(rdclass, rdtype, address)
+
+    from_text = classmethod(from_text)
+
+    def to_wire(self, file, compress = None, origin = None):
+        file.write(dns.inet.inet_pton(dns.inet.AF_INET6, self.address))
+
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
+        address = dns.inet.inet_ntop(dns.inet.AF_INET6,
+                                     wire[current : current + rdlen])
+        return cls(rdclass, rdtype, address)
+
+    from_wire = classmethod(from_wire)
+
+    def _cmp(self, other):
+        sa = dns.inet.inet_pton(dns.inet.AF_INET6, self.address)
+        oa = dns.inet.inet_pton(dns.inet.AF_INET6, other.address)
+        return cmp(sa, oa)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/IN/APL.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/IN/APL.py
new file mode 100644
index 0000000..7412c02
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/IN/APL.py
@@ -0,0 +1,170 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import cStringIO
+import struct
+
+import dns.exception
+import dns.inet
+import dns.rdata
+import dns.tokenizer
+
+class APLItem(object):
+    """An APL list item.
+
+    @ivar family: the address family (IANA address family registry)
+    @type family: int
+    @ivar negation: is this item negated?
+    @type negation: bool
+    @ivar address: the address
+    @type address: string
+    @ivar prefix: the prefix length
+    @type prefix: int
+    """
+
+    __slots__ = ['family', 'negation', 'address', 'prefix']
+
+    def __init__(self, family, negation, address, prefix):
+        self.family = family
+        self.negation = negation
+        self.address = address
+        self.prefix = prefix
+
+    def __str__(self):
+        if self.negation:
+            return "!%d:%s/%s" % (self.family, self.address, self.prefix)
+        else:
+            return "%d:%s/%s" % (self.family, self.address, self.prefix)
+
+    def to_wire(self, file):
+        if self.family == 1:
+            address = dns.inet.inet_pton(dns.inet.AF_INET, self.address)
+        elif self.family == 2:
+            address = dns.inet.inet_pton(dns.inet.AF_INET6, self.address)
+        else:
+            address = self.address.decode('hex_codec')
+        #
+        # Truncate least significant zero bytes.
+        #
+        last = 0
+        for i in xrange(len(address) - 1, -1, -1):
+            if address[i] != chr(0):
+                last = i + 1
+                break
+        address = address[0 : last]
+        l = len(address)
+        assert l < 128
+        if self.negation:
+            l |= 0x80
+        header = struct.pack('!HBB', self.family, self.prefix, l)
+        file.write(header)
+        file.write(address)
+
+class APL(dns.rdata.Rdata):
+    """APL record.
+
+    @ivar items: a list of APL items
+    @type items: list of APL_Item
+    @see: RFC 3123"""
+
+    __slots__ = ['items']
+
+    def __init__(self, rdclass, rdtype, items):
+        super(APL, self).__init__(rdclass, rdtype)
+        self.items = items
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return ' '.join(map(lambda x: str(x), self.items))
+
+    def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
+        items = []
+        while 1:
+            token = tok.get().unescape()
+            if token.is_eol_or_eof():
+                break
+            item = token.value
+            if item[0] == '!':
+                negation = True
+                item = item[1:]
+            else:
+                negation = False
+            (family, rest) = item.split(':', 1)
+            family = int(family)
+            (address, prefix) = rest.split('/', 1)
+            prefix = int(prefix)
+            item = APLItem(family, negation, address, prefix)
+            items.append(item)
+
+        return cls(rdclass, rdtype, items)
+
+    from_text = classmethod(from_text)
+
+    def to_wire(self, file, compress = None, origin = None):
+        for item in self.items:
+            item.to_wire(file)
+
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
+        items = []
+        while 1:
+            if rdlen < 4:
+                raise dns.exception.FormError
+            header = struct.unpack('!HBB', wire[current : current + 4])
+            afdlen = header[2]
+            if afdlen > 127:
+                negation = True
+                afdlen -= 128
+            else:
+                negation = False
+            current += 4
+            rdlen -= 4
+            if rdlen < afdlen:
+                raise dns.exception.FormError
+            address = wire[current : current + afdlen]
+            l = len(address)
+            if header[0] == 1:
+                if l < 4:
+                    address += '\x00' * (4 - l)
+                address = dns.inet.inet_ntop(dns.inet.AF_INET, address)
+            elif header[0] == 2:
+                if l < 16:
+                    address += '\x00' * (16 - l)
+                address = dns.inet.inet_ntop(dns.inet.AF_INET6, address)
+            else:
+                #
+                # This isn't really right according to the RFC, but it
+                # seems better than throwing an exception
+                #
+                address = address.encode('hex_codec')
+            current += afdlen
+            rdlen -= afdlen
+            item = APLItem(header[0], negation, address, header[1])
+            items.append(item)
+            if rdlen == 0:
+                break
+        return cls(rdclass, rdtype, items)
+
+    from_wire = classmethod(from_wire)
+
+    def _cmp(self, other):
+        f = cStringIO.StringIO()
+        self.to_wire(f)
+        wire1 = f.getvalue()
+        f.seek(0)
+        f.truncate()
+        other.to_wire(f)
+        wire2 = f.getvalue()
+        f.close()
+
+        return cmp(wire1, wire2)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/IN/DHCID.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/IN/DHCID.py
new file mode 100644
index 0000000..2d35234
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/IN/DHCID.py
@@ -0,0 +1,60 @@
+# Copyright (C) 2006, 2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.exception
+
+class DHCID(dns.rdata.Rdata):
+    """DHCID record
+
+    @ivar data: the data (the content of the RR is opaque as far as the
+    DNS is concerned)
+    @type data: string
+    @see: RFC 4701"""
+
+    __slots__ = ['data']
+
+    def __init__(self, rdclass, rdtype, data):
+        super(DHCID, self).__init__(rdclass, rdtype)
+        self.data = data
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return dns.rdata._base64ify(self.data)
+
+    def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
+        chunks = []
+        while 1:
+            t = tok.get().unescape()
+            if t.is_eol_or_eof():
+                break
+            if not t.is_identifier():
+                raise dns.exception.SyntaxError
+            chunks.append(t.value)
+        b64 = ''.join(chunks)
+        data = b64.decode('base64_codec')
+        return cls(rdclass, rdtype, data)
+
+    from_text = classmethod(from_text)
+
+    def to_wire(self, file, compress = None, origin = None):
+        file.write(self.data)
+
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
+        data = wire[current : current + rdlen]
+        return cls(rdclass, rdtype, data)
+
+    from_wire = classmethod(from_wire)
+
+    def _cmp(self, other):
+        return cmp(self.data, other.data)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/IN/IPSECKEY.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/IN/IPSECKEY.py
new file mode 100644
index 0000000..9ab08d8
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/IN/IPSECKEY.py
@@ -0,0 +1,159 @@
+# Copyright (C) 2006, 2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import cStringIO
+import struct
+
+import dns.exception
+import dns.inet
+import dns.name
+
+class IPSECKEY(dns.rdata.Rdata):
+    """IPSECKEY record
+
+    @ivar precedence: the precedence for this key data
+    @type precedence: int
+    @ivar gateway_type: the gateway type
+    @type gateway_type: int
+    @ivar algorithm: the algorithm to use
+    @type algorithm: int
+    @ivar gateway: the public key
+    @type gateway: None, IPv4 address, IPV6 address, or domain name
+    @ivar key: the public key
+    @type key: string
+    @see: RFC 4025"""
+
+    __slots__ = ['precedence', 'gateway_type', 'algorithm', 'gateway', 'key']
+
+    def __init__(self, rdclass, rdtype, precedence, gateway_type, algorithm,
+                 gateway, key):
+        super(IPSECKEY, self).__init__(rdclass, rdtype)
+        if gateway_type == 0:
+            if gateway != '.' and not gateway is None:
+                raise SyntaxError('invalid gateway for gateway type 0')
+            gateway = None
+        elif gateway_type == 1:
+            # check that it's OK
+            junk = dns.inet.inet_pton(dns.inet.AF_INET, gateway)
+        elif gateway_type == 2:
+            # check that it's OK
+            junk = dns.inet.inet_pton(dns.inet.AF_INET6, gateway)
+        elif gateway_type == 3:
+            pass
+        else:
+            raise SyntaxError('invalid IPSECKEY gateway type: %d' % gateway_type)
+        self.precedence = precedence
+        self.gateway_type = gateway_type
+        self.algorithm = algorithm
+        self.gateway = gateway
+        self.key = key
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        if self.gateway_type == 0:
+            gateway = '.'
+        elif self.gateway_type == 1:
+            gateway = self.gateway
+        elif self.gateway_type == 2:
+            gateway = self.gateway
+        elif self.gateway_type == 3:
+            gateway = str(self.gateway.choose_relativity(origin, relativize))
+        else:
+            raise ValueError('invalid gateway type')
+        return '%d %d %d %s %s' % (self.precedence, self.gateway_type,
+                                   self.algorithm, gateway,
+                                   dns.rdata._base64ify(self.key))
+
+    def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
+        precedence = tok.get_uint8()
+        gateway_type = tok.get_uint8()
+        algorithm = tok.get_uint8()
+        if gateway_type == 3:
+            gateway = tok.get_name().choose_relativity(origin, relativize)
+        else:
+            gateway = tok.get_string()
+        chunks = []
+        while 1:
+            t = tok.get().unescape()
+            if t.is_eol_or_eof():
+                break
+            if not t.is_identifier():
+                raise dns.exception.SyntaxError
+            chunks.append(t.value)
+        b64 = ''.join(chunks)
+        key = b64.decode('base64_codec')
+        return cls(rdclass, rdtype, precedence, gateway_type, algorithm,
+                   gateway, key)
+
+    from_text = classmethod(from_text)
+
+    def to_wire(self, file, compress = None, origin = None):
+        header = struct.pack("!BBB", self.precedence, self.gateway_type,
+                             self.algorithm)
+        file.write(header)
+        if self.gateway_type == 0:
+            pass
+        elif self.gateway_type == 1:
+            file.write(dns.inet.inet_pton(dns.inet.AF_INET, self.gateway))
+        elif self.gateway_type == 2:
+            file.write(dns.inet.inet_pton(dns.inet.AF_INET6, self.gateway))
+        elif self.gateway_type == 3:
+            self.gateway.to_wire(file, None, origin)
+        else:
+            raise ValueError('invalid gateway type')
+        file.write(self.key)
+
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
+        if rdlen < 3:
+            raise dns.exception.FormError
+        header = struct.unpack('!BBB', wire[current : current + 3])
+        gateway_type = header[1]
+        current += 3
+        rdlen -= 3
+        if gateway_type == 0:
+            gateway = None
+        elif gateway_type == 1:
+            gateway = dns.inet.inet_ntop(dns.inet.AF_INET,
+                                         wire[current : current + 4])
+            current += 4
+            rdlen -= 4
+        elif gateway_type == 2:
+            gateway = dns.inet.inet_ntop(dns.inet.AF_INET6,
+                                         wire[current : current + 16])
+            current += 16
+            rdlen -= 16
+        elif gateway_type == 3:
+            (gateway, cused) = dns.name.from_wire(wire[: current + rdlen],
+                                                  current)
+            current += cused
+            rdlen -= cused
+        else:
+            raise dns.exception.FormError('invalid IPSECKEY gateway type')
+        key = wire[current : current + rdlen]
+        return cls(rdclass, rdtype, header[0], gateway_type, header[2],
+                   gateway, key)
+
+    from_wire = classmethod(from_wire)
+
+    def _cmp(self, other):
+        f = cStringIO.StringIO()
+        self.to_wire(f)
+        wire1 = f.getvalue()
+        f.seek(0)
+        f.truncate()
+        other.to_wire(f)
+        wire2 = f.getvalue()
+        f.close()
+
+        return cmp(wire1, wire2)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/IN/KX.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/IN/KX.py
new file mode 100644
index 0000000..4d8a3a7
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/IN/KX.py
@@ -0,0 +1,20 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.rdtypes.mxbase
+
+class KX(dns.rdtypes.mxbase.UncompressedMX):
+    """KX record"""
+    pass
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/IN/NAPTR.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/IN/NAPTR.py
new file mode 100644
index 0000000..a3cca55
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/IN/NAPTR.py
@@ -0,0 +1,132 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import struct
+
+import dns.exception
+import dns.name
+import dns.rdata
+
+def _write_string(file, s):
+    l = len(s)
+    assert l < 256
+    byte = chr(l)
+    file.write(byte)
+    file.write(s)
+
+class NAPTR(dns.rdata.Rdata):
+    """NAPTR record
+
+    @ivar order: order
+    @type order: int
+    @ivar preference: preference
+    @type preference: int
+    @ivar flags: flags
+    @type flags: string
+    @ivar service: service
+    @type service: string
+    @ivar regexp: regular expression
+    @type regexp: string
+    @ivar replacement: replacement name
+    @type replacement: dns.name.Name object
+    @see: RFC 3403"""
+
+    __slots__ = ['order', 'preference', 'flags', 'service', 'regexp',
+                 'replacement']
+    
+    def __init__(self, rdclass, rdtype, order, preference, flags, service,
+                 regexp, replacement):
+        super(NAPTR, self).__init__(rdclass, rdtype)
+        self.order = order
+        self.preference = preference
+        self.flags = flags
+        self.service = service
+        self.regexp = regexp
+        self.replacement = replacement
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        replacement = self.replacement.choose_relativity(origin, relativize)
+        return '%d %d "%s" "%s" "%s" %s' % \
+               (self.order, self.preference,
+                dns.rdata._escapify(self.flags),
+                dns.rdata._escapify(self.service),
+                dns.rdata._escapify(self.regexp),
+                self.replacement)
+
+    def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
+        order = tok.get_uint16()
+        preference = tok.get_uint16()
+        flags = tok.get_string()
+        service = tok.get_string()
+        regexp = tok.get_string()
+        replacement = tok.get_name()
+        replacement = replacement.choose_relativity(origin, relativize)
+        tok.get_eol()
+        return cls(rdclass, rdtype, order, preference, flags, service,
+                   regexp, replacement)
+    
+    from_text = classmethod(from_text)
+
+    def to_wire(self, file, compress = None, origin = None):
+        two_ints = struct.pack("!HH", self.order, self.preference)
+        file.write(two_ints)
+        _write_string(file, self.flags)
+        _write_string(file, self.service)
+        _write_string(file, self.regexp)
+        self.replacement.to_wire(file, compress, origin)
+        
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
+        (order, preference) = struct.unpack('!HH', wire[current : current + 4])
+        current += 4
+        rdlen -= 4
+        strings = []
+        for i in xrange(3):
+            l = ord(wire[current])
+            current += 1
+            rdlen -= 1
+            if l > rdlen or rdlen < 0:
+                raise dns.exception.FormError
+            s = wire[current : current + l]
+            current += l
+            rdlen -= l
+            strings.append(s)
+        (replacement, cused) = dns.name.from_wire(wire[: current + rdlen],
+                                                  current)
+        if cused != rdlen:
+            raise dns.exception.FormError
+        if not origin is None:
+            replacement = replacement.relativize(origin)
+        return cls(rdclass, rdtype, order, preference, strings[0], strings[1],
+                   strings[2], replacement)
+
+    from_wire = classmethod(from_wire)
+
+    def choose_relativity(self, origin = None, relativize = True):
+        self.replacement = self.replacement.choose_relativity(origin,
+                                                              relativize)
+        
+    def _cmp(self, other):
+        sp = struct.pack("!HH", self.order, self.preference)
+        op = struct.pack("!HH", other.order, other.preference)
+        v = cmp(sp, op)
+        if v == 0:
+            v = cmp(self.flags, other.flags)
+            if v == 0:
+                v = cmp(self.service, other.service)
+                if v == 0:
+                    v = cmp(self.regexp, other.regexp)
+                    if v == 0:
+                        v = cmp(self.replacement, other.replacement)
+        return v
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/IN/NSAP.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/IN/NSAP.py
new file mode 100644
index 0000000..22b9131
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/IN/NSAP.py
@@ -0,0 +1,59 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.exception
+import dns.rdata
+import dns.tokenizer
+
+class NSAP(dns.rdata.Rdata):
+    """NSAP record.
+
+    @ivar address: a NASP
+    @type address: string
+    @see: RFC 1706"""
+
+    __slots__ = ['address']
+
+    def __init__(self, rdclass, rdtype, address):
+        super(NSAP, self).__init__(rdclass, rdtype)
+        self.address = address
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return "0x%s" % self.address.encode('hex_codec')
+
+    def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
+        address = tok.get_string()
+        t = tok.get_eol()
+        if address[0:2] != '0x':
+            raise dns.exception.SyntaxError('string does not start with 0x')
+        address = address[2:].replace('.', '')
+        if len(address) % 2 != 0:
+            raise dns.exception.SyntaxError('hexstring has odd length')
+        address = address.decode('hex_codec')
+        return cls(rdclass, rdtype, address)
+
+    from_text = classmethod(from_text)
+
+    def to_wire(self, file, compress = None, origin = None):
+        file.write(self.address)
+
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
+        address = wire[current : current + rdlen]
+        return cls(rdclass, rdtype, address)
+
+    from_wire = classmethod(from_wire)
+
+    def _cmp(self, other):
+        return cmp(self.address, other.address)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/IN/NSAP_PTR.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/IN/NSAP_PTR.py
new file mode 100644
index 0000000..6f591f4
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/IN/NSAP_PTR.py
@@ -0,0 +1,20 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.rdtypes.nsbase
+
+class NSAP_PTR(dns.rdtypes.nsbase.UncompressedNS):
+    """NSAP-PTR record"""
+    pass
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/IN/PX.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/IN/PX.py
new file mode 100644
index 0000000..0f11290
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/IN/PX.py
@@ -0,0 +1,97 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import struct
+
+import dns.exception
+import dns.rdata
+import dns.name
+
+class PX(dns.rdata.Rdata):
+    """PX record.
+
+    @ivar preference: the preference value
+    @type preference: int
+    @ivar map822: the map822 name
+    @type map822: dns.name.Name object
+    @ivar mapx400: the mapx400 name
+    @type mapx400: dns.name.Name object
+    @see: RFC 2163"""
+
+    __slots__ = ['preference', 'map822', 'mapx400']
+        
+    def __init__(self, rdclass, rdtype, preference, map822, mapx400):
+        super(PX, self).__init__(rdclass, rdtype)
+        self.preference = preference
+        self.map822 = map822
+        self.mapx400 = mapx400
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        map822 = self.map822.choose_relativity(origin, relativize)
+        mapx400 = self.mapx400.choose_relativity(origin, relativize)
+        return '%d %s %s' % (self.preference, map822, mapx400)
+        
+    def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
+        preference = tok.get_uint16()
+        map822 = tok.get_name()
+        map822 = map822.choose_relativity(origin, relativize)
+        mapx400 = tok.get_name(None)
+        mapx400 = mapx400.choose_relativity(origin, relativize)
+        tok.get_eol()
+        return cls(rdclass, rdtype, preference, map822, mapx400)
+    
+    from_text = classmethod(from_text)
+
+    def to_wire(self, file, compress = None, origin = None):
+        pref = struct.pack("!H", self.preference)
+        file.write(pref)
+        self.map822.to_wire(file, None, origin)
+        self.mapx400.to_wire(file, None, origin)
+        
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
+        (preference, ) = struct.unpack('!H', wire[current : current + 2])
+        current += 2
+        rdlen -= 2
+        (map822, cused) = dns.name.from_wire(wire[: current + rdlen],
+                                               current)
+        if cused > rdlen:
+            raise dns.exception.FormError
+        current += cused
+        rdlen -= cused
+        if not origin is None:
+            map822 = map822.relativize(origin)
+        (mapx400, cused) = dns.name.from_wire(wire[: current + rdlen],
+                                              current)
+        if cused != rdlen:
+            raise dns.exception.FormError
+        if not origin is None:
+            mapx400 = mapx400.relativize(origin)
+        return cls(rdclass, rdtype, preference, map822, mapx400)
+
+    from_wire = classmethod(from_wire)
+
+    def choose_relativity(self, origin = None, relativize = True):
+        self.map822 = self.map822.choose_relativity(origin, relativize)
+        self.mapx400 = self.mapx400.choose_relativity(origin, relativize)
+
+    def _cmp(self, other):
+        sp = struct.pack("!H", self.preference)
+        op = struct.pack("!H", other.preference)
+        v = cmp(sp, op)
+        if v == 0:
+            v = cmp(self.map822, other.map822)
+            if v == 0:
+                v = cmp(self.mapx400, other.mapx400)
+        return v
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/IN/SRV.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/IN/SRV.py
new file mode 100644
index 0000000..c9c5823
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/IN/SRV.py
@@ -0,0 +1,89 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import struct
+
+import dns.exception
+import dns.rdata
+import dns.name
+
+class SRV(dns.rdata.Rdata):
+    """SRV record
+
+    @ivar priority: the priority
+    @type priority: int
+    @ivar weight: the weight
+    @type weight: int
+    @ivar port: the port of the service
+    @type port: int
+    @ivar target: the target host
+    @type target: dns.name.Name object
+    @see: RFC 2782"""
+
+    __slots__ = ['priority', 'weight', 'port', 'target']
+
+    def __init__(self, rdclass, rdtype, priority, weight, port, target):
+        super(SRV, self).__init__(rdclass, rdtype)
+        self.priority = priority
+        self.weight = weight
+        self.port = port
+        self.target = target
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        target = self.target.choose_relativity(origin, relativize)
+        return '%d %d %d %s' % (self.priority, self.weight, self.port,
+                                target)
+
+    def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
+        priority = tok.get_uint16()
+        weight = tok.get_uint16()
+        port = tok.get_uint16()
+        target = tok.get_name(None)
+        target = target.choose_relativity(origin, relativize)
+        tok.get_eol()
+        return cls(rdclass, rdtype, priority, weight, port, target)
+
+    from_text = classmethod(from_text)
+
+    def to_wire(self, file, compress = None, origin = None):
+        three_ints = struct.pack("!HHH", self.priority, self.weight, self.port)
+        file.write(three_ints)
+        self.target.to_wire(file, compress, origin)
+
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
+        (priority, weight, port) = struct.unpack('!HHH',
+                                                 wire[current : current + 6])
+        current += 6
+        rdlen -= 6
+        (target, cused) = dns.name.from_wire(wire[: current + rdlen],
+                                             current)
+        if cused != rdlen:
+            raise dns.exception.FormError
+        if not origin is None:
+            target = target.relativize(origin)
+        return cls(rdclass, rdtype, priority, weight, port, target)
+
+    from_wire = classmethod(from_wire)
+
+    def choose_relativity(self, origin = None, relativize = True):
+        self.target = self.target.choose_relativity(origin, relativize)
+
+    def _cmp(self, other):
+        sp = struct.pack("!HHH", self.priority, self.weight, self.port)
+        op = struct.pack("!HHH", other.priority, other.weight, other.port)
+        v = cmp(sp, op)
+        if v == 0:
+            v = cmp(self.target, other.target)
+        return v
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/IN/WKS.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/IN/WKS.py
new file mode 100644
index 0000000..85aafb3
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/IN/WKS.py
@@ -0,0 +1,113 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import socket
+import struct
+
+import dns.ipv4
+import dns.rdata
+
+_proto_tcp = socket.getprotobyname('tcp')
+_proto_udp = socket.getprotobyname('udp')
+
+class WKS(dns.rdata.Rdata):
+    """WKS record
+
+    @ivar address: the address
+    @type address: string
+    @ivar protocol: the protocol
+    @type protocol: int
+    @ivar bitmap: the bitmap
+    @type bitmap: string
+    @see: RFC 1035"""
+
+    __slots__ = ['address', 'protocol', 'bitmap']
+
+    def __init__(self, rdclass, rdtype, address, protocol, bitmap):
+        super(WKS, self).__init__(rdclass, rdtype)
+        self.address = address
+        self.protocol = protocol
+        self.bitmap = bitmap
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        bits = []
+        for i in xrange(0, len(self.bitmap)):
+            byte = ord(self.bitmap[i])
+            for j in xrange(0, 8):
+                if byte & (0x80 >> j):
+                    bits.append(str(i * 8 + j))
+        text = ' '.join(bits)
+        return '%s %d %s' % (self.address, self.protocol, text)
+
+    def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
+        address = tok.get_string()
+        protocol = tok.get_string()
+        if protocol.isdigit():
+            protocol = int(protocol)
+        else:
+            protocol = socket.getprotobyname(protocol)
+        bitmap = []
+        while 1:
+            token = tok.get().unescape()
+            if token.is_eol_or_eof():
+                break
+            if token.value.isdigit():
+                serv = int(token.value)
+            else:
+                if protocol != _proto_udp and protocol != _proto_tcp:
+                    raise NotImplementedError("protocol must be TCP or UDP")
+                if protocol == _proto_udp:
+                    protocol_text = "udp"
+                else:
+                    protocol_text = "tcp"
+                serv = socket.getservbyname(token.value, protocol_text)
+            i = serv // 8
+            l = len(bitmap)
+            if l < i + 1:
+                for j in xrange(l, i + 1):
+                    bitmap.append('\x00')
+            bitmap[i] = chr(ord(bitmap[i]) | (0x80 >> (serv % 8)))
+        bitmap = dns.rdata._truncate_bitmap(bitmap)
+        return cls(rdclass, rdtype, address, protocol, bitmap)
+
+    from_text = classmethod(from_text)
+
+    def to_wire(self, file, compress = None, origin = None):
+        file.write(dns.ipv4.inet_aton(self.address))
+        protocol = struct.pack('!B', self.protocol)
+        file.write(protocol)
+        file.write(self.bitmap)
+
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
+        address = dns.ipv4.inet_ntoa(wire[current : current + 4])
+        protocol, = struct.unpack('!B', wire[current + 4 : current + 5])
+        current += 5
+        rdlen -= 5
+        bitmap = wire[current : current + rdlen]
+        return cls(rdclass, rdtype, address, protocol, bitmap)
+
+    from_wire = classmethod(from_wire)
+
+    def _cmp(self, other):
+        sa = dns.ipv4.inet_aton(self.address)
+        oa = dns.ipv4.inet_aton(other.address)
+        v = cmp(sa, oa)
+        if v == 0:
+            sp = struct.pack('!B', self.protocol)
+            op = struct.pack('!B', other.protocol)
+            v = cmp(sp, op)
+            if v == 0:
+                v = cmp(self.bitmap, other.bitmap)
+        return v
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/IN/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/IN/__init__.py
new file mode 100644
index 0000000..ab93129
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/IN/__init__.py
@@ -0,0 +1,30 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""Class IN rdata type classes."""
+
+__all__ = [
+    'A',
+    'AAAA',
+    'APL',
+    'DHCID',
+    'KX',
+    'NAPTR',
+    'NSAP',
+    'NSAP_PTR',
+    'PX',
+    'SRV',
+    'WKS',
+]
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/__init__.py
new file mode 100644
index 0000000..13282be
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/__init__.py
@@ -0,0 +1,25 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""DNS rdata type classes"""
+
+__all__ = [
+    'ANY',
+    'IN',
+    'mxbase',
+    'nsbase',
+    'sigbase',
+    'keybase',
+]
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/dsbase.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/dsbase.py
new file mode 100644
index 0000000..aa46403
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/dsbase.py
@@ -0,0 +1,92 @@
+# Copyright (C) 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import struct
+
+import dns.rdata
+import dns.rdatatype
+
+class DSBase(dns.rdata.Rdata):
+    """Base class for rdata that is like a DS record
+
+    @ivar key_tag: the key tag
+    @type key_tag: int
+    @ivar algorithm: the algorithm
+    @type algorithm: int
+    @ivar digest_type: the digest type
+    @type digest_type: int
+    @ivar digest: the digest
+    @type digest: int
+    @see: draft-ietf-dnsext-delegation-signer-14.txt"""
+
+    __slots__ = ['key_tag', 'algorithm', 'digest_type', 'digest']
+
+    def __init__(self, rdclass, rdtype, key_tag, algorithm, digest_type,
+                 digest):
+        super(DSBase, self).__init__(rdclass, rdtype)
+        self.key_tag = key_tag
+        self.algorithm = algorithm
+        self.digest_type = digest_type
+        self.digest = digest
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return '%d %d %d %s' % (self.key_tag, self.algorithm,
+                                self.digest_type,
+                                dns.rdata._hexify(self.digest,
+                                                  chunksize=128))
+
+    def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
+        key_tag = tok.get_uint16()
+        algorithm = tok.get_uint8()
+        digest_type = tok.get_uint8()
+        chunks = []
+        while 1:
+            t = tok.get().unescape()
+            if t.is_eol_or_eof():
+                break
+            if not t.is_identifier():
+                raise dns.exception.SyntaxError
+            chunks.append(t.value)
+        digest = ''.join(chunks)
+        digest = digest.decode('hex_codec')
+        return cls(rdclass, rdtype, key_tag, algorithm, digest_type,
+                   digest)
+
+    from_text = classmethod(from_text)
+
+    def to_wire(self, file, compress = None, origin = None):
+        header = struct.pack("!HBB", self.key_tag, self.algorithm,
+                             self.digest_type)
+        file.write(header)
+        file.write(self.digest)
+
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
+        header = struct.unpack("!HBB", wire[current : current + 4])
+        current += 4
+        rdlen -= 4
+        digest = wire[current : current + rdlen]
+        return cls(rdclass, rdtype, header[0], header[1], header[2], digest)
+
+    from_wire = classmethod(from_wire)
+
+    def _cmp(self, other):
+        hs = struct.pack("!HBB", self.key_tag, self.algorithm,
+                         self.digest_type)
+        ho = struct.pack("!HBB", other.key_tag, other.algorithm,
+                         other.digest_type)
+        v = cmp(hs, ho)
+        if v == 0:
+            v = cmp(self.digest, other.digest)
+        return v
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/keybase.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/keybase.py
new file mode 100644
index 0000000..75c9272
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/keybase.py
@@ -0,0 +1,149 @@
+# Copyright (C) 2004-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import struct
+
+import dns.exception
+import dns.dnssec
+import dns.rdata
+
+_flags_from_text = {
+    'NOCONF': (0x4000, 0xC000),
+    'NOAUTH': (0x8000, 0xC000),
+    'NOKEY': (0xC000, 0xC000),
+    'FLAG2': (0x2000, 0x2000),
+    'EXTEND': (0x1000, 0x1000),
+    'FLAG4': (0x0800, 0x0800),
+    'FLAG5': (0x0400, 0x0400),
+    'USER': (0x0000, 0x0300),
+    'ZONE': (0x0100, 0x0300),
+    'HOST': (0x0200, 0x0300),
+    'NTYP3': (0x0300, 0x0300),
+    'FLAG8': (0x0080, 0x0080),
+    'FLAG9': (0x0040, 0x0040),
+    'FLAG10': (0x0020, 0x0020),
+    'FLAG11': (0x0010, 0x0010),
+    'SIG0': (0x0000, 0x000f),
+    'SIG1': (0x0001, 0x000f),
+    'SIG2': (0x0002, 0x000f),
+    'SIG3': (0x0003, 0x000f),
+    'SIG4': (0x0004, 0x000f),
+    'SIG5': (0x0005, 0x000f),
+    'SIG6': (0x0006, 0x000f),
+    'SIG7': (0x0007, 0x000f),
+    'SIG8': (0x0008, 0x000f),
+    'SIG9': (0x0009, 0x000f),
+    'SIG10': (0x000a, 0x000f),
+    'SIG11': (0x000b, 0x000f),
+    'SIG12': (0x000c, 0x000f),
+    'SIG13': (0x000d, 0x000f),
+    'SIG14': (0x000e, 0x000f),
+    'SIG15': (0x000f, 0x000f),
+    }
+
+_protocol_from_text = {
+    'NONE' : 0,
+    'TLS' : 1,
+    'EMAIL' : 2,
+    'DNSSEC' : 3,
+    'IPSEC' : 4,
+    'ALL' : 255,
+    }
+
+class KEYBase(dns.rdata.Rdata):
+    """KEY-like record base
+
+    @ivar flags: the key flags
+    @type flags: int
+    @ivar protocol: the protocol for which this key may be used
+    @type protocol: int
+    @ivar algorithm: the algorithm used for the key
+    @type algorithm: int
+    @ivar key: the public key
+    @type key: string"""
+
+    __slots__ = ['flags', 'protocol', 'algorithm', 'key']
+
+    def __init__(self, rdclass, rdtype, flags, protocol, algorithm, key):
+        super(KEYBase, self).__init__(rdclass, rdtype)
+        self.flags = flags
+        self.protocol = protocol
+        self.algorithm = algorithm
+        self.key = key
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return '%d %d %d %s' % (self.flags, self.protocol, self.algorithm,
+                                dns.rdata._base64ify(self.key))
+
+    def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
+        flags = tok.get_string()
+        if flags.isdigit():
+            flags = int(flags)
+        else:
+            flag_names = flags.split('|')
+            flags = 0
+            for flag in flag_names:
+                v = _flags_from_text.get(flag)
+                if v is None:
+                    raise dns.exception.SyntaxError('unknown flag %s' % flag)
+                flags &= ~v[1]
+                flags |= v[0]
+        protocol = tok.get_string()
+        if protocol.isdigit():
+            protocol = int(protocol)
+        else:
+            protocol = _protocol_from_text.get(protocol)
+            if protocol is None:
+                raise dns.exception.SyntaxError('unknown protocol %s' % protocol)
+
+        algorithm = dns.dnssec.algorithm_from_text(tok.get_string())
+        chunks = []
+        while 1:
+            t = tok.get().unescape()
+            if t.is_eol_or_eof():
+                break
+            if not t.is_identifier():
+                raise dns.exception.SyntaxError
+            chunks.append(t.value)
+        b64 = ''.join(chunks)
+        key = b64.decode('base64_codec')
+        return cls(rdclass, rdtype, flags, protocol, algorithm, key)
+
+    from_text = classmethod(from_text)
+
+    def to_wire(self, file, compress = None, origin = None):
+        header = struct.pack("!HBB", self.flags, self.protocol, self.algorithm)
+        file.write(header)
+        file.write(self.key)
+
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
+        if rdlen < 4:
+            raise dns.exception.FormError
+        header = struct.unpack('!HBB', wire[current : current + 4])
+        current += 4
+        rdlen -= 4
+        key = wire[current : current + rdlen]
+        return cls(rdclass, rdtype, header[0], header[1], header[2],
+                   key)
+
+    from_wire = classmethod(from_wire)
+
+    def _cmp(self, other):
+        hs = struct.pack("!HBB", self.flags, self.protocol, self.algorithm)
+        ho = struct.pack("!HBB", other.flags, other.protocol, other.algorithm)
+        v = cmp(hs, ho)
+        if v == 0:
+            v = cmp(self.key, other.key)
+        return v
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/mxbase.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/mxbase.py
new file mode 100644
index 0000000..5e3515b
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/mxbase.py
@@ -0,0 +1,105 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""MX-like base classes."""
+
+import cStringIO
+import struct
+
+import dns.exception
+import dns.rdata
+import dns.name
+
+class MXBase(dns.rdata.Rdata):
+    """Base class for rdata that is like an MX record.
+
+    @ivar preference: the preference value
+    @type preference: int
+    @ivar exchange: the exchange name
+    @type exchange: dns.name.Name object"""
+
+    __slots__ = ['preference', 'exchange']
+
+    def __init__(self, rdclass, rdtype, preference, exchange):
+        super(MXBase, self).__init__(rdclass, rdtype)
+        self.preference = preference
+        self.exchange = exchange
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        exchange = self.exchange.choose_relativity(origin, relativize)
+        return '%d %s' % (self.preference, exchange)
+
+    def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
+        preference = tok.get_uint16()
+        exchange = tok.get_name()
+        exchange = exchange.choose_relativity(origin, relativize)
+        tok.get_eol()
+        return cls(rdclass, rdtype, preference, exchange)
+
+    from_text = classmethod(from_text)
+
+    def to_wire(self, file, compress = None, origin = None):
+        pref = struct.pack("!H", self.preference)
+        file.write(pref)
+        self.exchange.to_wire(file, compress, origin)
+
+    def to_digestable(self, origin = None):
+        return struct.pack("!H", self.preference) + \
+            self.exchange.to_digestable(origin)
+
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
+        (preference, ) = struct.unpack('!H', wire[current : current + 2])
+        current += 2
+        rdlen -= 2
+        (exchange, cused) = dns.name.from_wire(wire[: current + rdlen],
+                                               current)
+        if cused != rdlen:
+            raise dns.exception.FormError
+        if not origin is None:
+            exchange = exchange.relativize(origin)
+        return cls(rdclass, rdtype, preference, exchange)
+
+    from_wire = classmethod(from_wire)
+
+    def choose_relativity(self, origin = None, relativize = True):
+        self.exchange = self.exchange.choose_relativity(origin, relativize)
+
+    def _cmp(self, other):
+        sp = struct.pack("!H", self.preference)
+        op = struct.pack("!H", other.preference)
+        v = cmp(sp, op)
+        if v == 0:
+            v = cmp(self.exchange, other.exchange)
+        return v
+
+class UncompressedMX(MXBase):
+    """Base class for rdata that is like an MX record, but whose name
+    is not compressed when converted to DNS wire format, and whose
+    digestable form is not downcased."""
+
+    def to_wire(self, file, compress = None, origin = None):
+        super(UncompressedMX, self).to_wire(file, None, origin)
+
+    def to_digestable(self, origin = None):
+        f = cStringIO.StringIO()
+        self.to_wire(f, None, origin)
+        return f.getvalue()
+
+class UncompressedDowncasingMX(MXBase):
+    """Base class for rdata that is like an MX record, but whose name
+    is not compressed when convert to DNS wire format."""
+
+    def to_wire(self, file, compress = None, origin = None):
+        super(UncompressedDowncasingMX, self).to_wire(file, None, origin)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/nsbase.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/nsbase.py
new file mode 100644
index 0000000..7cdb2a0
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/nsbase.py
@@ -0,0 +1,82 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""NS-like base classes."""
+
+import cStringIO
+
+import dns.exception
+import dns.rdata
+import dns.name
+
+class NSBase(dns.rdata.Rdata):
+    """Base class for rdata that is like an NS record.
+
+    @ivar target: the target name of the rdata
+    @type target: dns.name.Name object"""
+
+    __slots__ = ['target']
+
+    def __init__(self, rdclass, rdtype, target):
+        super(NSBase, self).__init__(rdclass, rdtype)
+        self.target = target
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        target = self.target.choose_relativity(origin, relativize)
+        return str(target)
+
+    def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
+        target = tok.get_name()
+        target = target.choose_relativity(origin, relativize)
+        tok.get_eol()
+        return cls(rdclass, rdtype, target)
+
+    from_text = classmethod(from_text)
+
+    def to_wire(self, file, compress = None, origin = None):
+        self.target.to_wire(file, compress, origin)
+
+    def to_digestable(self, origin = None):
+        return self.target.to_digestable(origin)
+
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
+        (target, cused) = dns.name.from_wire(wire[: current + rdlen],
+                                             current)
+        if cused != rdlen:
+            raise dns.exception.FormError
+        if not origin is None:
+            target = target.relativize(origin)
+        return cls(rdclass, rdtype, target)
+
+    from_wire = classmethod(from_wire)
+
+    def choose_relativity(self, origin = None, relativize = True):
+        self.target = self.target.choose_relativity(origin, relativize)
+
+    def _cmp(self, other):
+        return cmp(self.target, other.target)
+
+class UncompressedNS(NSBase):
+    """Base class for rdata that is like an NS record, but whose name
+    is not compressed when convert to DNS wire format, and whose
+    digestable form is not downcased."""
+
+    def to_wire(self, file, compress = None, origin = None):
+        super(UncompressedNS, self).to_wire(file, None, origin)
+
+    def to_digestable(self, origin = None):
+        f = cStringIO.StringIO()
+        self.to_wire(f, None, origin)
+        return f.getvalue()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/sigbase.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/sigbase.py
new file mode 100644
index 0000000..ccb6dd6
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/sigbase.py
@@ -0,0 +1,168 @@
+# Copyright (C) 2004-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import calendar
+import struct
+import time
+
+import dns.dnssec
+import dns.exception
+import dns.rdata
+import dns.rdatatype
+
+class BadSigTime(dns.exception.DNSException):
+    """Raised when a SIG or RRSIG RR's time cannot be parsed."""
+    pass
+
+def sigtime_to_posixtime(what):
+    if len(what) != 14:
+        raise BadSigTime
+    year = int(what[0:4])
+    month = int(what[4:6])
+    day = int(what[6:8])
+    hour = int(what[8:10])
+    minute = int(what[10:12])
+    second = int(what[12:14])
+    return calendar.timegm((year, month, day, hour, minute, second,
+                            0, 0, 0))
+
+def posixtime_to_sigtime(what):
+    return time.strftime('%Y%m%d%H%M%S', time.gmtime(what))
+
+class SIGBase(dns.rdata.Rdata):
+    """SIG-like record base
+
+    @ivar type_covered: the rdata type this signature covers
+    @type type_covered: int
+    @ivar algorithm: the algorithm used for the sig
+    @type algorithm: int
+    @ivar labels: number of labels
+    @type labels: int
+    @ivar original_ttl: the original TTL
+    @type original_ttl: long
+    @ivar expiration: signature expiration time
+    @type expiration: long
+    @ivar inception: signature inception time
+    @type inception: long
+    @ivar key_tag: the key tag
+    @type key_tag: int
+    @ivar signer: the signer
+    @type signer: dns.name.Name object
+    @ivar signature: the signature
+    @type signature: string"""
+
+    __slots__ = ['type_covered', 'algorithm', 'labels', 'original_ttl',
+                 'expiration', 'inception', 'key_tag', 'signer',
+                 'signature']
+
+    def __init__(self, rdclass, rdtype, type_covered, algorithm, labels,
+                 original_ttl, expiration, inception, key_tag, signer,
+                 signature):
+        super(SIGBase, self).__init__(rdclass, rdtype)
+        self.type_covered = type_covered
+        self.algorithm = algorithm
+        self.labels = labels
+        self.original_ttl = original_ttl
+        self.expiration = expiration
+        self.inception = inception
+        self.key_tag = key_tag
+        self.signer = signer
+        self.signature = signature
+
+    def covers(self):
+        return self.type_covered
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return '%s %d %d %d %s %s %d %s %s' % (
+            dns.rdatatype.to_text(self.type_covered),
+            self.algorithm,
+            self.labels,
+            self.original_ttl,
+            posixtime_to_sigtime(self.expiration),
+            posixtime_to_sigtime(self.inception),
+            self.key_tag,
+            self.signer,
+            dns.rdata._base64ify(self.signature)
+            )
+
+    def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
+        type_covered = dns.rdatatype.from_text(tok.get_string())
+        algorithm = dns.dnssec.algorithm_from_text(tok.get_string())
+        labels = tok.get_int()
+        original_ttl = tok.get_ttl()
+        expiration = sigtime_to_posixtime(tok.get_string())
+        inception = sigtime_to_posixtime(tok.get_string())
+        key_tag = tok.get_int()
+        signer = tok.get_name()
+        signer = signer.choose_relativity(origin, relativize)
+        chunks = []
+        while 1:
+            t = tok.get().unescape()
+            if t.is_eol_or_eof():
+                break
+            if not t.is_identifier():
+                raise dns.exception.SyntaxError
+            chunks.append(t.value)
+        b64 = ''.join(chunks)
+        signature = b64.decode('base64_codec')
+        return cls(rdclass, rdtype, type_covered, algorithm, labels,
+                   original_ttl, expiration, inception, key_tag, signer,
+                   signature)
+
+    from_text = classmethod(from_text)
+
+    def to_wire(self, file, compress = None, origin = None):
+        header = struct.pack('!HBBIIIH', self.type_covered,
+                             self.algorithm, self.labels,
+                             self.original_ttl, self.expiration,
+                             self.inception, self.key_tag)
+        file.write(header)
+        self.signer.to_wire(file, None, origin)
+        file.write(self.signature)
+
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
+        header = struct.unpack('!HBBIIIH', wire[current : current + 18])
+        current += 18
+        rdlen -= 18
+        (signer, cused) = dns.name.from_wire(wire[: current + rdlen], current)
+        current += cused
+        rdlen -= cused
+        if not origin is None:
+            signer = signer.relativize(origin)
+        signature = wire[current : current + rdlen]
+        return cls(rdclass, rdtype, header[0], header[1], header[2],
+                   header[3], header[4], header[5], header[6], signer,
+                   signature)
+
+    from_wire = classmethod(from_wire)
+
+    def choose_relativity(self, origin = None, relativize = True):
+        self.signer = self.signer.choose_relativity(origin, relativize)
+
+    def _cmp(self, other):
+        hs = struct.pack('!HBBIIIH', self.type_covered,
+                         self.algorithm, self.labels,
+                         self.original_ttl, self.expiration,
+                         self.inception, self.key_tag)
+        ho = struct.pack('!HBBIIIH', other.type_covered,
+                         other.algorithm, other.labels,
+                         other.original_ttl, other.expiration,
+                         other.inception, other.key_tag)
+        v = cmp(hs, ho)
+        if v == 0:
+            v = cmp(self.signer, other.signer)
+            if v == 0:
+                v = cmp(self.signature, other.signature)
+        return v
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/txtbase.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/txtbase.py
new file mode 100644
index 0000000..43db2a4
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rdtypes/txtbase.py
@@ -0,0 +1,87 @@
+# Copyright (C) 2006, 2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""TXT-like base class."""
+
+import dns.exception
+import dns.rdata
+import dns.tokenizer
+
+class TXTBase(dns.rdata.Rdata):
+    """Base class for rdata that is like a TXT record
+
+    @ivar strings: the text strings
+    @type strings: list of string
+    @see: RFC 1035"""
+
+    __slots__ = ['strings']
+
+    def __init__(self, rdclass, rdtype, strings):
+        super(TXTBase, self).__init__(rdclass, rdtype)
+        if isinstance(strings, str):
+            strings = [ strings ]
+        self.strings = strings[:]
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        txt = ''
+        prefix = ''
+        for s in self.strings:
+            txt += '%s"%s"' % (prefix, dns.rdata._escapify(s))
+            prefix = ' '
+        return txt
+
+    def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
+        strings = []
+        while 1:
+            token = tok.get().unescape()
+            if token.is_eol_or_eof():
+                break
+            if not (token.is_quoted_string() or token.is_identifier()):
+                raise dns.exception.SyntaxError("expected a string")
+            if len(token.value) > 255:
+                raise dns.exception.SyntaxError("string too long")
+            strings.append(token.value)
+        if len(strings) == 0:
+            raise dns.exception.UnexpectedEnd
+        return cls(rdclass, rdtype, strings)
+
+    from_text = classmethod(from_text)
+
+    def to_wire(self, file, compress = None, origin = None):
+        for s in self.strings:
+            l = len(s)
+            assert l < 256
+            byte = chr(l)
+            file.write(byte)
+            file.write(s)
+
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
+        strings = []
+        while rdlen > 0:
+            l = ord(wire[current])
+            current += 1
+            rdlen -= 1
+            if l > rdlen:
+                raise dns.exception.FormError
+            s = wire[current : current + l]
+            current += l
+            rdlen -= l
+            strings.append(s)
+        return cls(rdclass, rdtype, strings)
+
+    from_wire = classmethod(from_wire)
+
+    def _cmp(self, other):
+        return cmp(self.strings, other.strings)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/renderer.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/renderer.py
new file mode 100644
index 0000000..bb0218a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/renderer.py
@@ -0,0 +1,324 @@
+# Copyright (C) 2001-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""Help for building DNS wire format messages"""
+
+import cStringIO
+import struct
+import random
+import time
+
+import dns.exception
+import dns.tsig
+
+QUESTION = 0
+ANSWER = 1
+AUTHORITY = 2
+ADDITIONAL = 3
+
+class Renderer(object):
+    """Helper class for building DNS wire-format messages.
+
+    Most applications can use the higher-level L{dns.message.Message}
+    class and its to_wire() method to generate wire-format messages.
+    This class is for those applications which need finer control
+    over the generation of messages.
+
+    Typical use::
+
+        r = dns.renderer.Renderer(id=1, flags=0x80, max_size=512)
+        r.add_question(qname, qtype, qclass)
+        r.add_rrset(dns.renderer.ANSWER, rrset_1)
+        r.add_rrset(dns.renderer.ANSWER, rrset_2)
+        r.add_rrset(dns.renderer.AUTHORITY, ns_rrset)
+        r.add_edns(0, 0, 4096)
+        r.add_rrset(dns.renderer.ADDTIONAL, ad_rrset_1)
+        r.add_rrset(dns.renderer.ADDTIONAL, ad_rrset_2)
+        r.write_header()
+        r.add_tsig(keyname, secret, 300, 1, 0, '', request_mac)
+        wire = r.get_wire()
+
+    @ivar output: where rendering is written
+    @type output: cStringIO.StringIO object
+    @ivar id: the message id
+    @type id: int
+    @ivar flags: the message flags
+    @type flags: int
+    @ivar max_size: the maximum size of the message
+    @type max_size: int
+    @ivar origin: the origin to use when rendering relative names
+    @type origin: dns.name.Name object
+    @ivar compress: the compression table
+    @type compress: dict
+    @ivar section: the section currently being rendered
+    @type section: int (dns.renderer.QUESTION, dns.renderer.ANSWER,
+    dns.renderer.AUTHORITY, or dns.renderer.ADDITIONAL)
+    @ivar counts: list of the number of RRs in each section
+    @type counts: int list of length 4
+    @ivar mac: the MAC of the rendered message (if TSIG was used)
+    @type mac: string
+    """
+
+    def __init__(self, id=None, flags=0, max_size=65535, origin=None):
+        """Initialize a new renderer.
+
+        @param id: the message id
+        @type id: int
+        @param flags: the DNS message flags
+        @type flags: int
+        @param max_size: the maximum message size; the default is 65535.
+        If rendering results in a message greater than I{max_size},
+        then L{dns.exception.TooBig} will be raised.
+        @type max_size: int
+        @param origin: the origin to use when rendering relative names
+        @type origin: dns.name.Namem or None.
+        """
+
+        self.output = cStringIO.StringIO()
+        if id is None:
+            self.id = random.randint(0, 65535)
+        else:
+            self.id = id
+        self.flags = flags
+        self.max_size = max_size
+        self.origin = origin
+        self.compress = {}
+        self.section = QUESTION
+        self.counts = [0, 0, 0, 0]
+        self.output.write('\x00' * 12)
+        self.mac = ''
+
+    def _rollback(self, where):
+        """Truncate the output buffer at offset I{where}, and remove any
+        compression table entries that pointed beyond the truncation
+        point.
+
+        @param where: the offset
+        @type where: int
+        """
+
+        self.output.seek(where)
+        self.output.truncate()
+        keys_to_delete = []
+        for k, v in self.compress.iteritems():
+            if v >= where:
+                keys_to_delete.append(k)
+        for k in keys_to_delete:
+            del self.compress[k]
+
+    def _set_section(self, section):
+        """Set the renderer's current section.
+
+        Sections must be rendered order: QUESTION, ANSWER, AUTHORITY,
+        ADDITIONAL.  Sections may be empty.
+
+        @param section: the section
+        @type section: int
+        @raises dns.exception.FormError: an attempt was made to set
+        a section value less than the current section.
+        """
+
+        if self.section != section:
+            if self.section > section:
+                raise dns.exception.FormError
+            self.section = section
+
+    def add_question(self, qname, rdtype, rdclass=dns.rdataclass.IN):
+        """Add a question to the message.
+
+        @param qname: the question name
+        @type qname: dns.name.Name
+        @param rdtype: the question rdata type
+        @type rdtype: int
+        @param rdclass: the question rdata class
+        @type rdclass: int
+        """
+
+        self._set_section(QUESTION)
+        before = self.output.tell()
+        qname.to_wire(self.output, self.compress, self.origin)
+        self.output.write(struct.pack("!HH", rdtype, rdclass))
+        after = self.output.tell()
+        if after >= self.max_size:
+            self._rollback(before)
+            raise dns.exception.TooBig
+        self.counts[QUESTION] += 1
+
+    def add_rrset(self, section, rrset, **kw):
+        """Add the rrset to the specified section.
+
+        Any keyword arguments are passed on to the rdataset's to_wire()
+        routine.
+
+        @param section: the section
+        @type section: int
+        @param rrset: the rrset
+        @type rrset: dns.rrset.RRset object
+        """
+
+        self._set_section(section)
+        before = self.output.tell()
+        n = rrset.to_wire(self.output, self.compress, self.origin, **kw)
+        after = self.output.tell()
+        if after >= self.max_size:
+            self._rollback(before)
+            raise dns.exception.TooBig
+        self.counts[section] += n
+
+    def add_rdataset(self, section, name, rdataset, **kw):
+        """Add the rdataset to the specified section, using the specified
+        name as the owner name.
+
+        Any keyword arguments are passed on to the rdataset's to_wire()
+        routine.
+
+        @param section: the section
+        @type section: int
+        @param name: the owner name
+        @type name: dns.name.Name object
+        @param rdataset: the rdataset
+        @type rdataset: dns.rdataset.Rdataset object
+        """
+
+        self._set_section(section)
+        before = self.output.tell()
+        n = rdataset.to_wire(name, self.output, self.compress, self.origin,
+                             **kw)
+        after = self.output.tell()
+        if after >= self.max_size:
+            self._rollback(before)
+            raise dns.exception.TooBig
+        self.counts[section] += n
+
+    def add_edns(self, edns, ednsflags, payload, options=None):
+        """Add an EDNS OPT record to the message.
+
+        @param edns: The EDNS level to use.
+        @type edns: int
+        @param ednsflags: EDNS flag values.
+        @type ednsflags: int
+        @param payload: The EDNS sender's payload field, which is the maximum
+        size of UDP datagram the sender can handle.
+        @type payload: int
+        @param options: The EDNS options list
+        @type options: list of dns.edns.Option instances
+        @see: RFC 2671
+        """
+
+        # make sure the EDNS version in ednsflags agrees with edns
+        ednsflags &= 0xFF00FFFFL
+        ednsflags |= (edns << 16)
+        self._set_section(ADDITIONAL)
+        before = self.output.tell()
+        self.output.write(struct.pack('!BHHIH', 0, dns.rdatatype.OPT, payload,
+                                      ednsflags, 0))
+        if not options is None:
+            lstart = self.output.tell()
+            for opt in options:
+                stuff = struct.pack("!HH", opt.otype, 0)
+                self.output.write(stuff)
+                start = self.output.tell()
+                opt.to_wire(self.output)
+                end = self.output.tell()
+                assert end - start < 65536
+                self.output.seek(start - 2)
+                stuff = struct.pack("!H", end - start)
+                self.output.write(stuff)
+                self.output.seek(0, 2)
+            lend = self.output.tell()
+            assert lend - lstart < 65536
+            self.output.seek(lstart - 2)
+            stuff = struct.pack("!H", lend - lstart)
+            self.output.write(stuff)
+            self.output.seek(0, 2)
+        after = self.output.tell()
+        if after >= self.max_size:
+            self._rollback(before)
+            raise dns.exception.TooBig
+        self.counts[ADDITIONAL] += 1
+
+    def add_tsig(self, keyname, secret, fudge, id, tsig_error, other_data,
+                 request_mac, algorithm=dns.tsig.default_algorithm):
+        """Add a TSIG signature to the message.
+
+        @param keyname: the TSIG key name
+        @type keyname: dns.name.Name object
+        @param secret: the secret to use
+        @type secret: string
+        @param fudge: TSIG time fudge
+        @type fudge: int
+        @param id: the message id to encode in the tsig signature
+        @type id: int
+        @param tsig_error: TSIG error code; default is 0.
+        @type tsig_error: int
+        @param other_data: TSIG other data.
+        @type other_data: string
+        @param request_mac: This message is a response to the request which
+        had the specified MAC.
+        @param algorithm: the TSIG algorithm to use
+        @type request_mac: string
+        """
+
+        self._set_section(ADDITIONAL)
+        before = self.output.tell()
+        s = self.output.getvalue()
+        (tsig_rdata, self.mac, ctx) = dns.tsig.sign(s,
+                                                    keyname,
+                                                    secret,
+                                                    int(time.time()),
+                                                    fudge,
+                                                    id,
+                                                    tsig_error,
+                                                    other_data,
+                                                    request_mac,
+                                                    algorithm=algorithm)
+        keyname.to_wire(self.output, self.compress, self.origin)
+        self.output.write(struct.pack('!HHIH', dns.rdatatype.TSIG,
+                                      dns.rdataclass.ANY, 0, 0))
+        rdata_start = self.output.tell()
+        self.output.write(tsig_rdata)
+        after = self.output.tell()
+        assert after - rdata_start < 65536
+        if after >= self.max_size:
+            self._rollback(before)
+            raise dns.exception.TooBig
+        self.output.seek(rdata_start - 2)
+        self.output.write(struct.pack('!H', after - rdata_start))
+        self.counts[ADDITIONAL] += 1
+        self.output.seek(10)
+        self.output.write(struct.pack('!H', self.counts[ADDITIONAL]))
+        self.output.seek(0, 2)
+
+    def write_header(self):
+        """Write the DNS message header.
+
+        Writing the DNS message header is done asfter all sections
+        have been rendered, but before the optional TSIG signature
+        is added.
+        """
+
+        self.output.seek(0)
+        self.output.write(struct.pack('!HHHHHH', self.id, self.flags,
+                                      self.counts[0], self.counts[1],
+                                      self.counts[2], self.counts[3]))
+        self.output.seek(0, 2)
+
+    def get_wire(self):
+        """Return the wire format message.
+
+        @rtype: string
+        """
+
+        return self.output.getvalue()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/resolver.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/resolver.py
new file mode 100644
index 0000000..372d7d8
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/resolver.py
@@ -0,0 +1,761 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""DNS stub resolver.
+
+@var default_resolver: The default resolver object
+@type default_resolver: dns.resolver.Resolver object"""
+
+import socket
+import sys
+import time
+
+import dns.exception
+import dns.message
+import dns.name
+import dns.query
+import dns.rcode
+import dns.rdataclass
+import dns.rdatatype
+
+if sys.platform == 'win32':
+    import _winreg
+
+class NXDOMAIN(dns.exception.DNSException):
+    """The query name does not exist."""
+    pass
+
+# The definition of the Timeout exception has moved from here to the
+# dns.exception module.  We keep dns.resolver.Timeout defined for
+# backwards compatibility.
+
+Timeout = dns.exception.Timeout
+
+class NoAnswer(dns.exception.DNSException):
+    """The response did not contain an answer to the question."""
+    pass
+
+class NoNameservers(dns.exception.DNSException):
+    """No non-broken nameservers are available to answer the query."""
+    pass
+
+class NotAbsolute(dns.exception.DNSException):
+    """Raised if an absolute domain name is required but a relative name
+    was provided."""
+    pass
+
+class NoRootSOA(dns.exception.DNSException):
+    """Raised if for some reason there is no SOA at the root name.
+    This should never happen!"""
+    pass
+
+
+class Answer(object):
+    """DNS stub resolver answer
+
+    Instances of this class bundle up the result of a successful DNS
+    resolution.
+
+    For convenience, the answer object implements much of the sequence
+    protocol, forwarding to its rrset.  E.g. "for a in answer" is
+    equivalent to "for a in answer.rrset", "answer[i]" is equivalent
+    to "answer.rrset[i]", and "answer[i:j]" is equivalent to
+    "answer.rrset[i:j]".
+
+    Note that CNAMEs or DNAMEs in the response may mean that answer
+    node's name might not be the query name.
+
+    @ivar qname: The query name
+    @type qname: dns.name.Name object
+    @ivar rdtype: The query type
+    @type rdtype: int
+    @ivar rdclass: The query class
+    @type rdclass: int
+    @ivar response: The response message
+    @type response: dns.message.Message object
+    @ivar rrset: The answer
+    @type rrset: dns.rrset.RRset object
+    @ivar expiration: The time when the answer expires
+    @type expiration: float (seconds since the epoch)
+    """
+    def __init__(self, qname, rdtype, rdclass, response):
+        self.qname = qname
+        self.rdtype = rdtype
+        self.rdclass = rdclass
+        self.response = response
+        min_ttl = -1
+        rrset = None
+        for count in xrange(0, 15):
+            try:
+                rrset = response.find_rrset(response.answer, qname,
+                                            rdclass, rdtype)
+                if min_ttl == -1 or rrset.ttl < min_ttl:
+                    min_ttl = rrset.ttl
+                break
+            except KeyError:
+                if rdtype != dns.rdatatype.CNAME:
+                    try:
+                        crrset = response.find_rrset(response.answer,
+                                                     qname,
+                                                     rdclass,
+                                                     dns.rdatatype.CNAME)
+                        if min_ttl == -1 or crrset.ttl < min_ttl:
+                            min_ttl = crrset.ttl
+                        for rd in crrset:
+                            qname = rd.target
+                            break
+                        continue
+                    except KeyError:
+                        raise NoAnswer
+                raise NoAnswer
+        if rrset is None:
+            raise NoAnswer
+        self.rrset = rrset
+        self.expiration = time.time() + min_ttl
+
+    def __getattr__(self, attr):
+        if attr == 'name':
+            return self.rrset.name
+        elif attr == 'ttl':
+            return self.rrset.ttl
+        elif attr == 'covers':
+            return self.rrset.covers
+        elif attr == 'rdclass':
+            return self.rrset.rdclass
+        elif attr == 'rdtype':
+            return self.rrset.rdtype
+        else:
+            raise AttributeError(attr)
+
+    def __len__(self):
+        return len(self.rrset)
+
+    def __iter__(self):
+        return iter(self.rrset)
+
+    def __getitem__(self, i):
+        return self.rrset[i]
+
+    def __delitem__(self, i):
+        del self.rrset[i]
+
+    def __getslice__(self, i, j):
+        return self.rrset[i:j]
+
+    def __delslice__(self, i, j):
+        del self.rrset[i:j]
+
+class Cache(object):
+    """Simple DNS answer cache.
+
+    @ivar data: A dictionary of cached data
+    @type data: dict
+    @ivar cleaning_interval: The number of seconds between cleanings.  The
+    default is 300 (5 minutes).
+    @type cleaning_interval: float
+    @ivar next_cleaning: The time the cache should next be cleaned (in seconds
+    since the epoch.)
+    @type next_cleaning: float
+    """
+
+    def __init__(self, cleaning_interval=300.0):
+        """Initialize a DNS cache.
+
+        @param cleaning_interval: the number of seconds between periodic
+        cleanings.  The default is 300.0
+        @type cleaning_interval: float.
+        """
+
+        self.data = {}
+        self.cleaning_interval = cleaning_interval
+        self.next_cleaning = time.time() + self.cleaning_interval
+
+    def maybe_clean(self):
+        """Clean the cache if it's time to do so."""
+
+        now = time.time()
+        if self.next_cleaning <= now:
+            keys_to_delete = []
+            for (k, v) in self.data.iteritems():
+                if v.expiration <= now:
+                    keys_to_delete.append(k)
+            for k in keys_to_delete:
+                del self.data[k]
+            now = time.time()
+            self.next_cleaning = now + self.cleaning_interval
+
+    def get(self, key):
+        """Get the answer associated with I{key}.  Returns None if
+        no answer is cached for the key.
+        @param key: the key
+        @type key: (dns.name.Name, int, int) tuple whose values are the
+        query name, rdtype, and rdclass.
+        @rtype: dns.resolver.Answer object or None
+        """
+
+        self.maybe_clean()
+        v = self.data.get(key)
+        if v is None or v.expiration <= time.time():
+            return None
+        return v
+
+    def put(self, key, value):
+        """Associate key and value in the cache.
+        @param key: the key
+        @type key: (dns.name.Name, int, int) tuple whose values are the
+        query name, rdtype, and rdclass.
+        @param value: The answer being cached
+        @type value: dns.resolver.Answer object
+        """
+
+        self.maybe_clean()
+        self.data[key] = value
+
+    def flush(self, key=None):
+        """Flush the cache.
+
+        If I{key} is specified, only that item is flushed.  Otherwise
+        the entire cache is flushed.
+
+        @param key: the key to flush
+        @type key: (dns.name.Name, int, int) tuple or None
+        """
+
+        if not key is None:
+            if self.data.has_key(key):
+                del self.data[key]
+        else:
+            self.data = {}
+            self.next_cleaning = time.time() + self.cleaning_interval
+
+class Resolver(object):
+    """DNS stub resolver
+
+    @ivar domain: The domain of this host
+    @type domain: dns.name.Name object
+    @ivar nameservers: A list of nameservers to query.  Each nameserver is
+    a string which contains the IP address of a nameserver.
+    @type nameservers: list of strings
+    @ivar search: The search list.  If the query name is a relative name,
+    the resolver will construct an absolute query name by appending the search
+    names one by one to the query name.
+    @type search: list of dns.name.Name objects
+    @ivar port: The port to which to send queries.  The default is 53.
+    @type port: int
+    @ivar timeout: The number of seconds to wait for a response from a
+    server, before timing out.
+    @type timeout: float
+    @ivar lifetime: The total number of seconds to spend trying to get an
+    answer to the question.  If the lifetime expires, a Timeout exception
+    will occur.
+    @type lifetime: float
+    @ivar keyring: The TSIG keyring to use.  The default is None.
+    @type keyring: dict
+    @ivar keyname: The TSIG keyname to use.  The default is None.
+    @type keyname: dns.name.Name object
+    @ivar keyalgorithm: The TSIG key algorithm to use.  The default is
+    dns.tsig.default_algorithm.
+    @type keyalgorithm: string
+    @ivar edns: The EDNS level to use.  The default is -1, no Edns.
+    @type edns: int
+    @ivar ednsflags: The EDNS flags
+    @type ednsflags: int
+    @ivar payload: The EDNS payload size.  The default is 0.
+    @type payload: int
+    @ivar cache: The cache to use.  The default is None.
+    @type cache: dns.resolver.Cache object
+    """
+    def __init__(self, filename='/etc/resolv.conf', configure=True):
+        """Initialize a resolver instance.
+
+        @param filename: The filename of a configuration file in
+        standard /etc/resolv.conf format.  This parameter is meaningful
+        only when I{configure} is true and the platform is POSIX.
+        @type filename: string or file object
+        @param configure: If True (the default), the resolver instance
+        is configured in the normal fashion for the operating system
+        the resolver is running on.  (I.e. a /etc/resolv.conf file on
+        POSIX systems and from the registry on Windows systems.)
+        @type configure: bool"""
+
+        self.reset()
+        if configure:
+            if sys.platform == 'win32':
+                self.read_registry()
+            elif filename:
+                self.read_resolv_conf(filename)
+
+    def reset(self):
+        """Reset all resolver configuration to the defaults."""
+        self.domain = \
+            dns.name.Name(dns.name.from_text(socket.gethostname())[1:])
+        if len(self.domain) == 0:
+            self.domain = dns.name.root
+        self.nameservers = []
+        self.search = []
+        self.port = 53
+        self.timeout = 2.0
+        self.lifetime = 30.0
+        self.keyring = None
+        self.keyname = None
+        self.keyalgorithm = dns.tsig.default_algorithm
+        self.edns = -1
+        self.ednsflags = 0
+        self.payload = 0
+        self.cache = None
+
+    def read_resolv_conf(self, f):
+        """Process f as a file in the /etc/resolv.conf format.  If f is
+        a string, it is used as the name of the file to open; otherwise it
+        is treated as the file itself."""
+        if isinstance(f, str) or isinstance(f, unicode):
+            try:
+                f = open(f, 'r')
+            except IOError:
+                # /etc/resolv.conf doesn't exist, can't be read, etc.
+                # We'll just use the default resolver configuration.
+                self.nameservers = ['127.0.0.1']
+                return
+            want_close = True
+        else:
+            want_close = False
+        try:
+            for l in f:
+                if len(l) == 0 or l[0] == '#' or l[0] == ';':
+                    continue
+                tokens = l.split()
+                if len(tokens) == 0:
+                    continue
+                if tokens[0] == 'nameserver':
+                    self.nameservers.append(tokens[1])
+                elif tokens[0] == 'domain':
+                    self.domain = dns.name.from_text(tokens[1])
+                elif tokens[0] == 'search':
+                    for suffix in tokens[1:]:
+                        self.search.append(dns.name.from_text(suffix))
+        finally:
+            if want_close:
+                f.close()
+        if len(self.nameservers) == 0:
+            self.nameservers.append('127.0.0.1')
+
+    def _determine_split_char(self, entry):
+        #
+        # The windows registry irritatingly changes the list element
+        # delimiter in between ' ' and ',' (and vice-versa) in various
+        # versions of windows.
+        #
+        if entry.find(' ') >= 0:
+            split_char = ' '
+        elif entry.find(',') >= 0:
+            split_char = ','
+        else:
+            # probably a singleton; treat as a space-separated list.
+            split_char = ' '
+        return split_char
+
+    def _config_win32_nameservers(self, nameservers):
+        """Configure a NameServer registry entry."""
+        # we call str() on nameservers to convert it from unicode to ascii
+        nameservers = str(nameservers)
+        split_char = self._determine_split_char(nameservers)
+        ns_list = nameservers.split(split_char)
+        for ns in ns_list:
+            if not ns in self.nameservers:
+                self.nameservers.append(ns)
+
+    def _config_win32_domain(self, domain):
+        """Configure a Domain registry entry."""
+        # we call str() on domain to convert it from unicode to ascii
+        self.domain = dns.name.from_text(str(domain))
+
+    def _config_win32_search(self, search):
+        """Configure a Search registry entry."""
+        # we call str() on search to convert it from unicode to ascii
+        search = str(search)
+        split_char = self._determine_split_char(search)
+        search_list = search.split(split_char)
+        for s in search_list:
+            if not s in self.search:
+                self.search.append(dns.name.from_text(s))
+
+    def _config_win32_fromkey(self, key):
+        """Extract DNS info from a registry key."""
+        try:
+            servers, rtype = _winreg.QueryValueEx(key, 'NameServer')
+        except WindowsError:
+            servers = None
+        if servers:
+            self._config_win32_nameservers(servers)
+            try:
+                dom, rtype = _winreg.QueryValueEx(key, 'Domain')
+                if dom:
+                    self._config_win32_domain(dom)
+            except WindowsError:
+                pass
+        else:
+            try:
+                servers, rtype = _winreg.QueryValueEx(key, 'DhcpNameServer')
+            except WindowsError:
+                servers = None
+            if servers:
+                self._config_win32_nameservers(servers)
+                try:
+                    dom, rtype = _winreg.QueryValueEx(key, 'DhcpDomain')
+                    if dom:
+                        self._config_win32_domain(dom)
+                except WindowsError:
+                    pass
+        try:
+            search, rtype = _winreg.QueryValueEx(key, 'SearchList')
+        except WindowsError:
+            search = None
+        if search:
+            self._config_win32_search(search)
+
+    def read_registry(self):
+        """Extract resolver configuration from the Windows registry."""
+        lm = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE)
+        want_scan = False
+        try:
+            try:
+                # XP, 2000
+                tcp_params = _winreg.OpenKey(lm,
+                                             r'SYSTEM\CurrentControlSet'
+                                             r'\Services\Tcpip\Parameters')
+                want_scan = True
+            except EnvironmentError:
+                # ME
+                tcp_params = _winreg.OpenKey(lm,
+                                             r'SYSTEM\CurrentControlSet'
+                                             r'\Services\VxD\MSTCP')
+            try:
+                self._config_win32_fromkey(tcp_params)
+            finally:
+                tcp_params.Close()
+            if want_scan:
+                interfaces = _winreg.OpenKey(lm,
+                                             r'SYSTEM\CurrentControlSet'
+                                             r'\Services\Tcpip\Parameters'
+                                             r'\Interfaces')
+                try:
+                    i = 0
+                    while True:
+                        try:
+                            guid = _winreg.EnumKey(interfaces, i)
+                            i += 1
+                            key = _winreg.OpenKey(interfaces, guid)
+                            if not self._win32_is_nic_enabled(lm, guid, key):
+                                continue
+                            try:
+                                self._config_win32_fromkey(key)
+                            finally:
+                                key.Close()
+                        except EnvironmentError:
+                            break
+                finally:
+                    interfaces.Close()
+        finally:
+            lm.Close()
+
+    def _win32_is_nic_enabled(self, lm, guid, interface_key):
+         # Look in the Windows Registry to determine whether the network
+         # interface corresponding to the given guid is enabled.
+         #
+         # (Code contributed by Paul Marks, thanks!)
+         #
+         try:
+             # This hard-coded location seems to be consistent, at least
+             # from Windows 2000 through Vista.
+             connection_key = _winreg.OpenKey(
+                 lm,
+                 r'SYSTEM\CurrentControlSet\Control\Network'
+                 r'\{4D36E972-E325-11CE-BFC1-08002BE10318}'
+                 r'\%s\Connection' % guid)
+
+             try:
+                 # The PnpInstanceID points to a key inside Enum
+                 (pnp_id, ttype) = _winreg.QueryValueEx(
+                     connection_key, 'PnpInstanceID')
+
+                 if ttype != _winreg.REG_SZ:
+                     raise ValueError
+
+                 device_key = _winreg.OpenKey(
+                     lm, r'SYSTEM\CurrentControlSet\Enum\%s' % pnp_id)
+
+                 try:
+                     # Get ConfigFlags for this device
+                     (flags, ttype) = _winreg.QueryValueEx(
+                         device_key, 'ConfigFlags')
+
+                     if ttype != _winreg.REG_DWORD:
+                         raise ValueError
+
+                     # Based on experimentation, bit 0x1 indicates that the
+                     # device is disabled.
+                     return not (flags & 0x1)
+
+                 finally:
+                     device_key.Close()
+             finally:
+                 connection_key.Close()
+         except (EnvironmentError, ValueError):
+             # Pre-vista, enabled interfaces seem to have a non-empty
+             # NTEContextList; this was how dnspython detected enabled
+             # nics before the code above was contributed.  We've retained
+             # the old method since we don't know if the code above works
+             # on Windows 95/98/ME.
+             try:
+                 (nte, ttype) = _winreg.QueryValueEx(interface_key,
+                                                     'NTEContextList')
+                 return nte is not None
+             except WindowsError:
+                 return False
+
+    def _compute_timeout(self, start):
+        now = time.time()
+        if now < start:
+            if start - now > 1:
+                # Time going backwards is bad.  Just give up.
+                raise Timeout
+            else:
+                # Time went backwards, but only a little.  This can
+                # happen, e.g. under vmware with older linux kernels.
+                # Pretend it didn't happen.
+                now = start
+        duration = now - start
+        if duration >= self.lifetime:
+            raise Timeout
+        return min(self.lifetime - duration, self.timeout)
+
+    def query(self, qname, rdtype=dns.rdatatype.A, rdclass=dns.rdataclass.IN,
+              tcp=False, source=None):
+        """Query nameservers to find the answer to the question.
+
+        The I{qname}, I{rdtype}, and I{rdclass} parameters may be objects
+        of the appropriate type, or strings that can be converted into objects
+        of the appropriate type.  E.g. For I{rdtype} the integer 2 and the
+        the string 'NS' both mean to query for records with DNS rdata type NS.
+
+        @param qname: the query name
+        @type qname: dns.name.Name object or string
+        @param rdtype: the query type
+        @type rdtype: int or string
+        @param rdclass: the query class
+        @type rdclass: int or string
+        @param tcp: use TCP to make the query (default is False).
+        @type tcp: bool
+        @param source: bind to this IP address (defaults to machine default IP).
+        @type source: IP address in dotted quad notation
+        @rtype: dns.resolver.Answer instance
+        @raises Timeout: no answers could be found in the specified lifetime
+        @raises NXDOMAIN: the query name does not exist
+        @raises NoAnswer: the response did not contain an answer
+        @raises NoNameservers: no non-broken nameservers are available to
+        answer the question."""
+
+        if isinstance(qname, (str, unicode)):
+            qname = dns.name.from_text(qname, None)
+        if isinstance(rdtype, str):
+            rdtype = dns.rdatatype.from_text(rdtype)
+        if isinstance(rdclass, str):
+            rdclass = dns.rdataclass.from_text(rdclass)
+        qnames_to_try = []
+        if qname.is_absolute():
+            qnames_to_try.append(qname)
+        else:
+            if len(qname) > 1:
+                qnames_to_try.append(qname.concatenate(dns.name.root))
+            if self.search:
+                for suffix in self.search:
+                    qnames_to_try.append(qname.concatenate(suffix))
+            else:
+                qnames_to_try.append(qname.concatenate(self.domain))
+        all_nxdomain = True
+        start = time.time()
+        for qname in qnames_to_try:
+            if self.cache:
+                answer = self.cache.get((qname, rdtype, rdclass))
+                if answer:
+                    return answer
+            request = dns.message.make_query(qname, rdtype, rdclass)
+            if not self.keyname is None:
+                request.use_tsig(self.keyring, self.keyname, self.keyalgorithm)
+            request.use_edns(self.edns, self.ednsflags, self.payload)
+            response = None
+            #
+            # make a copy of the servers list so we can alter it later.
+            #
+            nameservers = self.nameservers[:]
+            backoff = 0.10
+            while response is None:
+                if len(nameservers) == 0:
+                    raise NoNameservers
+                for nameserver in nameservers[:]:
+                    timeout = self._compute_timeout(start)
+                    try:
+                        if tcp:
+                            response = dns.query.tcp(request, nameserver,
+                                                     timeout, self.port,
+                                                     source=source)
+                        else:
+                            response = dns.query.udp(request, nameserver,
+                                                     timeout, self.port,
+                                                     source=source)
+                    except (socket.error, dns.exception.Timeout):
+                        #
+                        # Communication failure or timeout.  Go to the
+                        # next server
+                        #
+                        response = None
+                        continue
+                    except dns.query.UnexpectedSource:
+                        #
+                        # Who knows?  Keep going.
+                        #
+                        response = None
+                        continue
+                    except dns.exception.FormError:
+                        #
+                        # We don't understand what this server is
+                        # saying.  Take it out of the mix and
+                        # continue.
+                        #
+                        nameservers.remove(nameserver)
+                        response = None
+                        continue
+                    rcode = response.rcode()
+                    if rcode == dns.rcode.NOERROR or \
+                           rcode == dns.rcode.NXDOMAIN:
+                        break
+                    #
+                    # We got a response, but we're not happy with the
+                    # rcode in it.  Remove the server from the mix if
+                    # the rcode isn't SERVFAIL.
+                    #
+                    if rcode != dns.rcode.SERVFAIL:
+                        nameservers.remove(nameserver)
+                    response = None
+                if not response is None:
+                    break
+                #
+                # All nameservers failed!
+                #
+                if len(nameservers) > 0:
+                    #
+                    # But we still have servers to try.  Sleep a bit
+                    # so we don't pound them!
+                    #
+                    timeout = self._compute_timeout(start)
+                    sleep_time = min(timeout, backoff)
+                    backoff *= 2
+                    time.sleep(sleep_time)
+            if response.rcode() == dns.rcode.NXDOMAIN:
+                continue
+            all_nxdomain = False
+            break
+        if all_nxdomain:
+            raise NXDOMAIN
+        answer = Answer(qname, rdtype, rdclass, response)
+        if self.cache:
+            self.cache.put((qname, rdtype, rdclass), answer)
+        return answer
+
+    def use_tsig(self, keyring, keyname=None,
+                 algorithm=dns.tsig.default_algorithm):
+        """Add a TSIG signature to the query.
+
+        @param keyring: The TSIG keyring to use; defaults to None.
+        @type keyring: dict
+        @param keyname: The name of the TSIG key to use; defaults to None.
+        The key must be defined in the keyring.  If a keyring is specified
+        but a keyname is not, then the key used will be the first key in the
+        keyring.  Note that the order of keys in a dictionary is not defined,
+        so applications should supply a keyname when a keyring is used, unless
+        they know the keyring contains only one key.
+        @param algorithm: The TSIG key algorithm to use.  The default
+        is dns.tsig.default_algorithm.
+        @type algorithm: string"""
+        self.keyring = keyring
+        if keyname is None:
+            self.keyname = self.keyring.keys()[0]
+        else:
+            self.keyname = keyname
+        self.keyalgorithm = algorithm
+
+    def use_edns(self, edns, ednsflags, payload):
+        """Configure Edns.
+
+        @param edns: The EDNS level to use.  The default is -1, no Edns.
+        @type edns: int
+        @param ednsflags: The EDNS flags
+        @type ednsflags: int
+        @param payload: The EDNS payload size.  The default is 0.
+        @type payload: int"""
+
+        if edns is None:
+            edns = -1
+        self.edns = edns
+        self.ednsflags = ednsflags
+        self.payload = payload
+
+default_resolver = None
+
+def get_default_resolver():
+    """Get the default resolver, initializing it if necessary."""
+    global default_resolver
+    if default_resolver is None:
+        default_resolver = Resolver()
+    return default_resolver
+
+def query(qname, rdtype=dns.rdatatype.A, rdclass=dns.rdataclass.IN,
+          tcp=False, source=None):
+    """Query nameservers to find the answer to the question.
+
+    This is a convenience function that uses the default resolver
+    object to make the query.
+    @see: L{dns.resolver.Resolver.query} for more information on the
+    parameters."""
+    return get_default_resolver().query(qname, rdtype, rdclass, tcp, source)
+
+def zone_for_name(name, rdclass=dns.rdataclass.IN, tcp=False, resolver=None):
+    """Find the name of the zone which contains the specified name.
+
+    @param name: the query name
+    @type name: absolute dns.name.Name object or string
+    @param rdclass: The query class
+    @type rdclass: int
+    @param tcp: use TCP to make the query (default is False).
+    @type tcp: bool
+    @param resolver: the resolver to use
+    @type resolver: dns.resolver.Resolver object or None
+    @rtype: dns.name.Name"""
+
+    if isinstance(name, (str, unicode)):
+        name = dns.name.from_text(name, dns.name.root)
+    if resolver is None:
+        resolver = get_default_resolver()
+    if not name.is_absolute():
+        raise NotAbsolute(name)
+    while 1:
+        try:
+            answer = resolver.query(name, dns.rdatatype.SOA, rdclass, tcp)
+            return name
+        except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
+            try:
+                name = name.parent()
+            except dns.name.NoParent:
+                raise NoRootSOA
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/reversename.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/reversename.py
new file mode 100644
index 0000000..0a61b82
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/reversename.py
@@ -0,0 +1,75 @@
+# Copyright (C) 2006, 2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""DNS Reverse Map Names.
+
+@var ipv4_reverse_domain: The DNS IPv4 reverse-map domain, in-addr.arpa.
+@type ipv4_reverse_domain: dns.name.Name object
+@var ipv6_reverse_domain: The DNS IPv6 reverse-map domain, ip6.arpa.
+@type ipv6_reverse_domain: dns.name.Name object
+"""
+
+import dns.name
+import dns.ipv6
+import dns.ipv4
+
+ipv4_reverse_domain = dns.name.from_text('in-addr.arpa.')
+ipv6_reverse_domain = dns.name.from_text('ip6.arpa.')
+
+def from_address(text):
+    """Convert an IPv4 or IPv6 address in textual form into a Name object whose
+    value is the reverse-map domain name of the address.
+    @param text: an IPv4 or IPv6 address in textual form (e.g. '127.0.0.1',
+    '::1')
+    @type text: str
+    @rtype: dns.name.Name object
+    """
+    try:
+        parts = list(dns.ipv6.inet_aton(text).encode('hex_codec'))
+        origin = ipv6_reverse_domain
+    except:
+        parts = ['%d' % ord(byte) for byte in dns.ipv4.inet_aton(text)]
+        origin = ipv4_reverse_domain
+    parts.reverse()
+    return dns.name.from_text('.'.join(parts), origin=origin)
+
+def to_address(name):
+    """Convert a reverse map domain name into textual address form.
+    @param name: an IPv4 or IPv6 address in reverse-map form.
+    @type name: dns.name.Name object
+    @rtype: str
+    """
+    if name.is_subdomain(ipv4_reverse_domain):
+        name = name.relativize(ipv4_reverse_domain)
+        labels = list(name.labels)
+        labels.reverse()
+        text = '.'.join(labels)
+        # run through inet_aton() to check syntax and make pretty.
+        return dns.ipv4.inet_ntoa(dns.ipv4.inet_aton(text))
+    elif name.is_subdomain(ipv6_reverse_domain):
+        name = name.relativize(ipv6_reverse_domain)
+        labels = list(name.labels)
+        labels.reverse()
+        parts = []
+        i = 0
+        l = len(labels)
+        while i < l:
+            parts.append(''.join(labels[i:i+4]))
+            i += 4
+        text = ':'.join(parts)
+        # run through inet_aton() to check syntax and make pretty.
+        return dns.ipv6.inet_ntoa(dns.ipv6.inet_aton(text))
+    else:
+        raise dns.exception.SyntaxError('unknown reverse-map address family')
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rrset.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rrset.py
new file mode 100644
index 0000000..7f6c4af
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/rrset.py
@@ -0,0 +1,175 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""DNS RRsets (an RRset is a named rdataset)"""
+
+import dns.name
+import dns.rdataset
+import dns.rdataclass
+import dns.renderer
+
+class RRset(dns.rdataset.Rdataset):
+    """A DNS RRset (named rdataset).
+
+    RRset inherits from Rdataset, and RRsets can be treated as
+    Rdatasets in most cases.  There are, however, a few notable
+    exceptions.  RRsets have different to_wire() and to_text() method
+    arguments, reflecting the fact that RRsets always have an owner
+    name.
+    """
+
+    __slots__ = ['name', 'deleting']
+
+    def __init__(self, name, rdclass, rdtype, covers=dns.rdatatype.NONE,
+                 deleting=None):
+        """Create a new RRset."""
+
+        super(RRset, self).__init__(rdclass, rdtype)
+        self.name = name
+        self.deleting = deleting
+
+    def _clone(self):
+        obj = super(RRset, self)._clone()
+        obj.name = self.name
+        obj.deleting = self.deleting
+        return obj
+
+    def __repr__(self):
+        if self.covers == 0:
+            ctext = ''
+        else:
+            ctext = '(' + dns.rdatatype.to_text(self.covers) + ')'
+        if not self.deleting is None:
+            dtext = ' delete=' + dns.rdataclass.to_text(self.deleting)
+        else:
+            dtext = ''
+        return '<DNS ' + str(self.name) + ' ' + \
+               dns.rdataclass.to_text(self.rdclass) + ' ' + \
+               dns.rdatatype.to_text(self.rdtype) + ctext + dtext + ' RRset>'
+
+    def __str__(self):
+        return self.to_text()
+
+    def __eq__(self, other):
+        """Two RRsets are equal if they have the same name and the same
+        rdataset
+
+        @rtype: bool"""
+        if not isinstance(other, RRset):
+            return False
+        if self.name != other.name:
+            return False
+        return super(RRset, self).__eq__(other)
+
+    def match(self, name, rdclass, rdtype, covers, deleting=None):
+        """Returns True if this rrset matches the specified class, type,
+        covers, and deletion state."""
+
+        if not super(RRset, self).match(rdclass, rdtype, covers):
+            return False
+        if self.name != name or self.deleting != deleting:
+            return False
+        return True
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        """Convert the RRset into DNS master file format.
+
+        @see: L{dns.name.Name.choose_relativity} for more information
+        on how I{origin} and I{relativize} determine the way names
+        are emitted.
+
+        Any additional keyword arguments are passed on to the rdata
+        to_text() method.
+
+        @param origin: The origin for relative names, or None.
+        @type origin: dns.name.Name object
+        @param relativize: True if names should names be relativized
+        @type relativize: bool"""
+
+        return super(RRset, self).to_text(self.name, origin, relativize,
+                                          self.deleting, **kw)
+
+    def to_wire(self, file, compress=None, origin=None, **kw):
+        """Convert the RRset to wire format."""
+
+        return super(RRset, self).to_wire(self.name, file, compress, origin,
+                                          self.deleting, **kw)
+
+    def to_rdataset(self):
+        """Convert an RRset into an Rdataset.
+
+        @rtype: dns.rdataset.Rdataset object
+        """
+        return dns.rdataset.from_rdata_list(self.ttl, list(self))
+
+
+def from_text_list(name, ttl, rdclass, rdtype, text_rdatas):
+    """Create an RRset with the specified name, TTL, class, and type, and with
+    the specified list of rdatas in text format.
+
+    @rtype: dns.rrset.RRset object
+    """
+
+    if isinstance(name, (str, unicode)):
+        name = dns.name.from_text(name, None)
+    if isinstance(rdclass, str):
+        rdclass = dns.rdataclass.from_text(rdclass)
+    if isinstance(rdtype, str):
+        rdtype = dns.rdatatype.from_text(rdtype)
+    r = RRset(name, rdclass, rdtype)
+    r.update_ttl(ttl)
+    for t in text_rdatas:
+        rd = dns.rdata.from_text(r.rdclass, r.rdtype, t)
+        r.add(rd)
+    return r
+
+def from_text(name, ttl, rdclass, rdtype, *text_rdatas):
+    """Create an RRset with the specified name, TTL, class, and type and with
+    the specified rdatas in text format.
+
+    @rtype: dns.rrset.RRset object
+    """
+
+    return from_text_list(name, ttl, rdclass, rdtype, text_rdatas)
+
+def from_rdata_list(name, ttl, rdatas):
+    """Create an RRset with the specified name and TTL, and with
+    the specified list of rdata objects.
+
+    @rtype: dns.rrset.RRset object
+    """
+
+    if isinstance(name, (str, unicode)):
+        name = dns.name.from_text(name, None)
+
+    if len(rdatas) == 0:
+        raise ValueError("rdata list must not be empty")
+    r = None
+    for rd in rdatas:
+        if r is None:
+            r = RRset(name, rd.rdclass, rd.rdtype)
+            r.update_ttl(ttl)
+            first_time = False
+        r.add(rd)
+    return r
+
+def from_rdata(name, ttl, *rdatas):
+    """Create an RRset with the specified name and TTL, and with
+    the specified rdata objects.
+
+    @rtype: dns.rrset.RRset object
+    """
+
+    return from_rdata_list(name, ttl, rdatas)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/set.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/set.py
new file mode 100644
index 0000000..91f9fb8
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/set.py
@@ -0,0 +1,263 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""A simple Set class."""
+
+class Set(object):
+    """A simple set class.
+
+    Sets are not in Python until 2.3, and rdata are not immutable so
+    we cannot use sets.Set anyway.  This class implements subset of
+    the 2.3 Set interface using a list as the container.
+
+    @ivar items: A list of the items which are in the set
+    @type items: list"""
+
+    __slots__ = ['items']
+
+    def __init__(self, items=None):
+        """Initialize the set.
+
+        @param items: the initial set of items
+        @type items: any iterable or None
+        """
+
+        self.items = []
+        if not items is None:
+            for item in items:
+                self.add(item)
+
+    def __repr__(self):
+        return "dns.simpleset.Set(%s)" % repr(self.items)
+
+    def add(self, item):
+        """Add an item to the set."""
+        if not item in self.items:
+            self.items.append(item)
+
+    def remove(self, item):
+        """Remove an item from the set."""
+        self.items.remove(item)
+
+    def discard(self, item):
+        """Remove an item from the set if present."""
+        try:
+            self.items.remove(item)
+        except ValueError:
+            pass
+
+    def _clone(self):
+        """Make a (shallow) copy of the set.
+
+        There is a 'clone protocol' that subclasses of this class
+        should use.  To make a copy, first call your super's _clone()
+        method, and use the object returned as the new instance.  Then
+        make shallow copies of the attributes defined in the subclass.
+
+        This protocol allows us to write the set algorithms that
+        return new instances (e.g. union) once, and keep using them in
+        subclasses.
+        """
+
+        cls = self.__class__
+        obj = cls.__new__(cls)
+        obj.items = list(self.items)
+        return obj
+
+    def __copy__(self):
+        """Make a (shallow) copy of the set."""
+        return self._clone()
+
+    def copy(self):
+        """Make a (shallow) copy of the set."""
+        return self._clone()
+
+    def union_update(self, other):
+        """Update the set, adding any elements from other which are not
+        already in the set.
+        @param other: the collection of items with which to update the set
+        @type other: Set object
+        """
+        if not isinstance(other, Set):
+            raise ValueError('other must be a Set instance')
+        if self is other:
+            return
+        for item in other.items:
+            self.add(item)
+
+    def intersection_update(self, other):
+        """Update the set, removing any elements from other which are not
+        in both sets.
+        @param other: the collection of items with which to update the set
+        @type other: Set object
+        """
+        if not isinstance(other, Set):
+            raise ValueError('other must be a Set instance')
+        if self is other:
+            return
+        # we make a copy of the list so that we can remove items from
+        # the list without breaking the iterator.
+        for item in list(self.items):
+            if item not in other.items:
+                self.items.remove(item)
+
+    def difference_update(self, other):
+        """Update the set, removing any elements from other which are in
+        the set.
+        @param other: the collection of items with which to update the set
+        @type other: Set object
+        """
+        if not isinstance(other, Set):
+            raise ValueError('other must be a Set instance')
+        if self is other:
+            self.items = []
+        else:
+            for item in other.items:
+                self.discard(item)
+
+    def union(self, other):
+        """Return a new set which is the union of I{self} and I{other}.
+
+        @param other: the other set
+        @type other: Set object
+        @rtype: the same type as I{self}
+        """
+
+        obj = self._clone()
+        obj.union_update(other)
+        return obj
+
+    def intersection(self, other):
+        """Return a new set which is the intersection of I{self} and I{other}.
+
+        @param other: the other set
+        @type other: Set object
+        @rtype: the same type as I{self}
+        """
+
+        obj = self._clone()
+        obj.intersection_update(other)
+        return obj
+
+    def difference(self, other):
+        """Return a new set which I{self} - I{other}, i.e. the items
+        in I{self} which are not also in I{other}.
+
+        @param other: the other set
+        @type other: Set object
+        @rtype: the same type as I{self}
+        """
+
+        obj = self._clone()
+        obj.difference_update(other)
+        return obj
+
+    def __or__(self, other):
+        return self.union(other)
+
+    def __and__(self, other):
+        return self.intersection(other)
+
+    def __add__(self, other):
+        return self.union(other)
+
+    def __sub__(self, other):
+        return self.difference(other)
+
+    def __ior__(self, other):
+        self.union_update(other)
+        return self
+
+    def __iand__(self, other):
+        self.intersection_update(other)
+        return self
+
+    def __iadd__(self, other):
+        self.union_update(other)
+        return self
+
+    def __isub__(self, other):
+        self.difference_update(other)
+        return self
+
+    def update(self, other):
+        """Update the set, adding any elements from other which are not
+        already in the set.
+        @param other: the collection of items with which to update the set
+        @type other: any iterable type"""
+        for item in other:
+            self.add(item)
+
+    def clear(self):
+        """Make the set empty."""
+        self.items = []
+
+    def __eq__(self, other):
+        # Yes, this is inefficient but the sets we're dealing with are
+        # usually quite small, so it shouldn't hurt too much.
+        for item in self.items:
+            if not item in other.items:
+                return False
+        for item in other.items:
+            if not item in self.items:
+                return False
+        return True
+
+    def __ne__(self, other):
+        return not self.__eq__(other)
+
+    def __len__(self):
+        return len(self.items)
+
+    def __iter__(self):
+        return iter(self.items)
+
+    def __getitem__(self, i):
+        return self.items[i]
+
+    def __delitem__(self, i):
+        del self.items[i]
+
+    def __getslice__(self, i, j):
+        return self.items[i:j]
+
+    def __delslice__(self, i, j):
+        del self.items[i:j]
+
+    def issubset(self, other):
+        """Is I{self} a subset of I{other}?
+
+        @rtype: bool
+        """
+
+        if not isinstance(other, Set):
+            raise ValueError('other must be a Set instance')
+        for item in self.items:
+            if not item in other.items:
+                return False
+        return True
+
+    def issuperset(self, other):
+        """Is I{self} a superset of I{other}?
+
+        @rtype: bool
+        """
+
+        if not isinstance(other, Set):
+            raise ValueError('other must be a Set instance')
+        for item in other.items:
+            if not item in self.items:
+                return False
+        return True
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/tokenizer.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/tokenizer.py
new file mode 100644
index 0000000..4f68a2a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/tokenizer.py
@@ -0,0 +1,547 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""Tokenize DNS master file format"""
+
+import cStringIO
+import sys
+
+import dns.exception
+import dns.name
+import dns.ttl
+
+_DELIMITERS = {
+    ' ' : True,
+    '\t' : True,
+    '\n' : True,
+    ';' : True,
+    '(' : True,
+    ')' : True,
+    '"' : True }
+
+_QUOTING_DELIMITERS = { '"' : True }
+
+EOF = 0
+EOL = 1
+WHITESPACE = 2
+IDENTIFIER = 3
+QUOTED_STRING = 4
+COMMENT = 5
+DELIMITER = 6
+
+class UngetBufferFull(dns.exception.DNSException):
+    """Raised when an attempt is made to unget a token when the unget
+    buffer is full."""
+    pass
+
+class Token(object):
+    """A DNS master file format token.
+
+    @ivar ttype: The token type
+    @type ttype: int
+    @ivar value: The token value
+    @type value: string
+    @ivar has_escape: Does the token value contain escapes?
+    @type has_escape: bool
+    """
+
+    def __init__(self, ttype, value='', has_escape=False):
+        """Initialize a token instance.
+
+        @param ttype: The token type
+        @type ttype: int
+        @ivar value: The token value
+        @type value: string
+        @ivar has_escape: Does the token value contain escapes?
+        @type has_escape: bool
+        """
+        self.ttype = ttype
+        self.value = value
+        self.has_escape = has_escape
+
+    def is_eof(self):
+        return self.ttype == EOF
+
+    def is_eol(self):
+        return self.ttype == EOL
+
+    def is_whitespace(self):
+        return self.ttype == WHITESPACE
+
+    def is_identifier(self):
+        return self.ttype == IDENTIFIER
+
+    def is_quoted_string(self):
+        return self.ttype == QUOTED_STRING
+
+    def is_comment(self):
+        return self.ttype == COMMENT
+
+    def is_delimiter(self):
+        return self.ttype == DELIMITER
+
+    def is_eol_or_eof(self):
+        return (self.ttype == EOL or self.ttype == EOF)
+
+    def __eq__(self, other):
+        if not isinstance(other, Token):
+            return False
+        return (self.ttype == other.ttype and
+                self.value == other.value)
+
+    def __ne__(self, other):
+        if not isinstance(other, Token):
+            return True
+        return (self.ttype != other.ttype or
+                self.value != other.value)
+
+    def __str__(self):
+        return '%d "%s"' % (self.ttype, self.value)
+
+    def unescape(self):
+        if not self.has_escape:
+            return self
+        unescaped = ''
+        l = len(self.value)
+        i = 0
+        while i < l:
+            c = self.value[i]
+            i += 1
+            if c == '\\':
+                if i >= l:
+                    raise dns.exception.UnexpectedEnd
+                c = self.value[i]
+                i += 1
+                if c.isdigit():
+                    if i >= l:
+                        raise dns.exception.UnexpectedEnd
+                    c2 = self.value[i]
+                    i += 1
+                    if i >= l:
+                        raise dns.exception.UnexpectedEnd
+                    c3 = self.value[i]
+                    i += 1
+                    if not (c2.isdigit() and c3.isdigit()):
+                        raise dns.exception.SyntaxError
+                    c = chr(int(c) * 100 + int(c2) * 10 + int(c3))
+            unescaped += c
+        return Token(self.ttype, unescaped)
+
+    # compatibility for old-style tuple tokens
+
+    def __len__(self):
+        return 2
+
+    def __iter__(self):
+        return iter((self.ttype, self.value))
+
+    def __getitem__(self, i):
+        if i == 0:
+            return self.ttype
+        elif i == 1:
+            return self.value
+        else:
+            raise IndexError
+
+class Tokenizer(object):
+    """A DNS master file format tokenizer.
+
+    A token is a (type, value) tuple, where I{type} is an int, and
+    I{value} is a string.  The valid types are EOF, EOL, WHITESPACE,
+    IDENTIFIER, QUOTED_STRING, COMMENT, and DELIMITER.
+
+    @ivar file: The file to tokenize
+    @type file: file
+    @ivar ungotten_char: The most recently ungotten character, or None.
+    @type ungotten_char: string
+    @ivar ungotten_token: The most recently ungotten token, or None.
+    @type ungotten_token: (int, string) token tuple
+    @ivar multiline: The current multiline level.  This value is increased
+    by one every time a '(' delimiter is read, and decreased by one every time
+    a ')' delimiter is read.
+    @type multiline: int
+    @ivar quoting: This variable is true if the tokenizer is currently
+    reading a quoted string.
+    @type quoting: bool
+    @ivar eof: This variable is true if the tokenizer has encountered EOF.
+    @type eof: bool
+    @ivar delimiters: The current delimiter dictionary.
+    @type delimiters: dict
+    @ivar line_number: The current line number
+    @type line_number: int
+    @ivar filename: A filename that will be returned by the L{where} method.
+    @type filename: string
+    """
+
+    def __init__(self, f=sys.stdin, filename=None):
+        """Initialize a tokenizer instance.
+
+        @param f: The file to tokenize.  The default is sys.stdin.
+        This parameter may also be a string, in which case the tokenizer
+        will take its input from the contents of the string.
+        @type f: file or string
+        @param filename: the name of the filename that the L{where} method
+        will return.
+        @type filename: string
+        """
+
+        if isinstance(f, str):
+            f = cStringIO.StringIO(f)
+            if filename is None:
+                filename = '<string>'
+        else:
+            if filename is None:
+                if f is sys.stdin:
+                    filename = '<stdin>'
+                else:
+                    filename = '<file>'
+        self.file = f
+        self.ungotten_char = None
+        self.ungotten_token = None
+        self.multiline = 0
+        self.quoting = False
+        self.eof = False
+        self.delimiters = _DELIMITERS
+        self.line_number = 1
+        self.filename = filename
+
+    def _get_char(self):
+        """Read a character from input.
+        @rtype: string
+        """
+
+        if self.ungotten_char is None:
+            if self.eof:
+                c = ''
+            else:
+                c = self.file.read(1)
+                if c == '':
+                    self.eof = True
+                elif c == '\n':
+                    self.line_number += 1
+        else:
+            c = self.ungotten_char
+            self.ungotten_char = None
+        return c
+
+    def where(self):
+        """Return the current location in the input.
+
+        @rtype: (string, int) tuple.  The first item is the filename of
+        the input, the second is the current line number.
+        """
+
+        return (self.filename, self.line_number)
+
+    def _unget_char(self, c):
+        """Unget a character.
+
+        The unget buffer for characters is only one character large; it is
+        an error to try to unget a character when the unget buffer is not
+        empty.
+
+        @param c: the character to unget
+        @type c: string
+        @raises UngetBufferFull: there is already an ungotten char
+        """
+
+        if not self.ungotten_char is None:
+            raise UngetBufferFull
+        self.ungotten_char = c
+
+    def skip_whitespace(self):
+        """Consume input until a non-whitespace character is encountered.
+
+        The non-whitespace character is then ungotten, and the number of
+        whitespace characters consumed is returned.
+
+        If the tokenizer is in multiline mode, then newlines are whitespace.
+
+        @rtype: int
+        """
+
+        skipped = 0
+        while True:
+            c = self._get_char()
+            if c != ' ' and c != '\t':
+                if (c != '\n') or not self.multiline:
+                    self._unget_char(c)
+                    return skipped
+            skipped += 1
+
+    def get(self, want_leading = False, want_comment = False):
+        """Get the next token.
+
+        @param want_leading: If True, return a WHITESPACE token if the
+        first character read is whitespace.  The default is False.
+        @type want_leading: bool
+        @param want_comment: If True, return a COMMENT token if the
+        first token read is a comment.  The default is False.
+        @type want_comment: bool
+        @rtype: Token object
+        @raises dns.exception.UnexpectedEnd: input ended prematurely
+        @raises dns.exception.SyntaxError: input was badly formed
+        """
+
+        if not self.ungotten_token is None:
+            token = self.ungotten_token
+            self.ungotten_token = None
+            if token.is_whitespace():
+                if want_leading:
+                    return token
+            elif token.is_comment():
+                if want_comment:
+                    return token
+            else:
+                return token
+        skipped = self.skip_whitespace()
+        if want_leading and skipped > 0:
+            return Token(WHITESPACE, ' ')
+        token = ''
+        ttype = IDENTIFIER
+        has_escape = False
+        while True:
+            c = self._get_char()
+            if c == '' or c in self.delimiters:
+                if c == '' and self.quoting:
+                    raise dns.exception.UnexpectedEnd
+                if token == '' and ttype != QUOTED_STRING:
+                    if c == '(':
+                        self.multiline += 1
+                        self.skip_whitespace()
+                        continue
+                    elif c == ')':
+                        if not self.multiline > 0:
+                            raise dns.exception.SyntaxError
+                        self.multiline -= 1
+                        self.skip_whitespace()
+                        continue
+                    elif c == '"':
+                        if not self.quoting:
+                            self.quoting = True
+                            self.delimiters = _QUOTING_DELIMITERS
+                            ttype = QUOTED_STRING
+                            continue
+                        else:
+                            self.quoting = False
+                            self.delimiters = _DELIMITERS
+                            self.skip_whitespace()
+                            continue
+                    elif c == '\n':
+                        return Token(EOL, '\n')
+                    elif c == ';':
+                        while 1:
+                            c = self._get_char()
+                            if c == '\n' or c == '':
+                                break
+                            token += c
+                        if want_comment:
+                            self._unget_char(c)
+                            return Token(COMMENT, token)
+                        elif c == '':
+                            if self.multiline:
+                                raise dns.exception.SyntaxError('unbalanced parentheses')
+                            return Token(EOF)
+                        elif self.multiline:
+                            self.skip_whitespace()
+                            token = ''
+                            continue
+                        else:
+                            return Token(EOL, '\n')
+                    else:
+                        # This code exists in case we ever want a
+                        # delimiter to be returned.  It never produces
+                        # a token currently.
+                        token = c
+                        ttype = DELIMITER
+                else:
+                    self._unget_char(c)
+                break
+            elif self.quoting:
+                if c == '\\':
+                    c = self._get_char()
+                    if c == '':
+                        raise dns.exception.UnexpectedEnd
+                    if c.isdigit():
+                        c2 = self._get_char()
+                        if c2 == '':
+                            raise dns.exception.UnexpectedEnd
+                        c3 = self._get_char()
+                        if c == '':
+                            raise dns.exception.UnexpectedEnd
+                        if not (c2.isdigit() and c3.isdigit()):
+                            raise dns.exception.SyntaxError
+                        c = chr(int(c) * 100 + int(c2) * 10 + int(c3))
+                elif c == '\n':
+                    raise dns.exception.SyntaxError('newline in quoted string')
+            elif c == '\\':
+                #
+                # It's an escape.  Put it and the next character into
+                # the token; it will be checked later for goodness.
+                #
+                token += c
+                has_escape = True
+                c = self._get_char()
+                if c == '' or c == '\n':
+                    raise dns.exception.UnexpectedEnd
+            token += c
+        if token == '' and ttype != QUOTED_STRING:
+            if self.multiline:
+                raise dns.exception.SyntaxError('unbalanced parentheses')
+            ttype = EOF
+        return Token(ttype, token, has_escape)
+
+    def unget(self, token):
+        """Unget a token.
+
+        The unget buffer for tokens is only one token large; it is
+        an error to try to unget a token when the unget buffer is not
+        empty.
+
+        @param token: the token to unget
+        @type token: Token object
+        @raises UngetBufferFull: there is already an ungotten token
+        """
+
+        if not self.ungotten_token is None:
+            raise UngetBufferFull
+        self.ungotten_token = token
+
+    def next(self):
+        """Return the next item in an iteration.
+        @rtype: (int, string)
+        """
+
+        token = self.get()
+        if token.is_eof():
+            raise StopIteration
+        return token
+
+    def __iter__(self):
+        return self
+
+    # Helpers
+
+    def get_int(self):
+        """Read the next token and interpret it as an integer.
+
+        @raises dns.exception.SyntaxError:
+        @rtype: int
+        """
+
+        token = self.get().unescape()
+        if not token.is_identifier():
+            raise dns.exception.SyntaxError('expecting an identifier')
+        if not token.value.isdigit():
+            raise dns.exception.SyntaxError('expecting an integer')
+        return int(token.value)
+
+    def get_uint8(self):
+        """Read the next token and interpret it as an 8-bit unsigned
+        integer.
+
+        @raises dns.exception.SyntaxError:
+        @rtype: int
+        """
+
+        value = self.get_int()
+        if value < 0 or value > 255:
+            raise dns.exception.SyntaxError('%d is not an unsigned 8-bit integer' % value)
+        return value
+
+    def get_uint16(self):
+        """Read the next token and interpret it as a 16-bit unsigned
+        integer.
+
+        @raises dns.exception.SyntaxError:
+        @rtype: int
+        """
+
+        value = self.get_int()
+        if value < 0 or value > 65535:
+            raise dns.exception.SyntaxError('%d is not an unsigned 16-bit integer' % value)
+        return value
+
+    def get_uint32(self):
+        """Read the next token and interpret it as a 32-bit unsigned
+        integer.
+
+        @raises dns.exception.SyntaxError:
+        @rtype: int
+        """
+
+        token = self.get().unescape()
+        if not token.is_identifier():
+            raise dns.exception.SyntaxError('expecting an identifier')
+        if not token.value.isdigit():
+            raise dns.exception.SyntaxError('expecting an integer')
+        value = long(token.value)
+        if value < 0 or value > 4294967296L:
+            raise dns.exception.SyntaxError('%d is not an unsigned 32-bit integer' % value)
+        return value
+
+    def get_string(self, origin=None):
+        """Read the next token and interpret it as a string.
+
+        @raises dns.exception.SyntaxError:
+        @rtype: string
+        """
+
+        token = self.get().unescape()
+        if not (token.is_identifier() or token.is_quoted_string()):
+            raise dns.exception.SyntaxError('expecting a string')
+        return token.value
+
+    def get_identifier(self, origin=None):
+        """Read the next token and raise an exception if it is not an identifier.
+
+        @raises dns.exception.SyntaxError:
+        @rtype: string
+        """
+
+        token = self.get().unescape()
+        if not token.is_identifier():
+            raise dns.exception.SyntaxError('expecting an identifier')
+        return token.value
+
+    def get_name(self, origin=None):
+        """Read the next token and interpret it as a DNS name.
+
+        @raises dns.exception.SyntaxError:
+        @rtype: dns.name.Name object"""
+
+        token = self.get()
+        if not token.is_identifier():
+            raise dns.exception.SyntaxError('expecting an identifier')
+        return dns.name.from_text(token.value, origin)
+
+    def get_eol(self):
+        """Read the next token and raise an exception if it isn't EOL or
+        EOF.
+
+        @raises dns.exception.SyntaxError:
+        @rtype: string
+        """
+
+        token = self.get()
+        if not token.is_eol_or_eof():
+            raise dns.exception.SyntaxError('expected EOL or EOF, got %d "%s"' % (token.ttype, token.value))
+        return token.value
+
+    def get_ttl(self):
+        token = self.get().unescape()
+        if not token.is_identifier():
+            raise dns.exception.SyntaxError('expecting an identifier')
+        return dns.ttl.from_text(token.value)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/tsig.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/tsig.py
new file mode 100644
index 0000000..b4deeca
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/tsig.py
@@ -0,0 +1,216 @@
+# Copyright (C) 2001-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""DNS TSIG support."""
+
+import hmac
+import struct
+
+import dns.exception
+import dns.rdataclass
+import dns.name
+
+class BadTime(dns.exception.DNSException):
+    """Raised if the current time is not within the TSIG's validity time."""
+    pass
+
+class BadSignature(dns.exception.DNSException):
+    """Raised if the TSIG signature fails to verify."""
+    pass
+
+class PeerError(dns.exception.DNSException):
+    """Base class for all TSIG errors generated by the remote peer"""
+    pass
+
+class PeerBadKey(PeerError):
+    """Raised if the peer didn't know the key we used"""
+    pass
+
+class PeerBadSignature(PeerError):
+    """Raised if the peer didn't like the signature we sent"""
+    pass
+
+class PeerBadTime(PeerError):
+    """Raised if the peer didn't like the time we sent"""
+    pass
+
+class PeerBadTruncation(PeerError):
+    """Raised if the peer didn't like amount of truncation in the TSIG we sent"""
+    pass
+
+default_algorithm = "HMAC-MD5.SIG-ALG.REG.INT"
+
+BADSIG = 16
+BADKEY = 17
+BADTIME = 18
+BADTRUNC = 22
+
+def sign(wire, keyname, secret, time, fudge, original_id, error,
+         other_data, request_mac, ctx=None, multi=False, first=True,
+         algorithm=default_algorithm):
+    """Return a (tsig_rdata, mac, ctx) tuple containing the HMAC TSIG rdata
+    for the input parameters, the HMAC MAC calculated by applying the
+    TSIG signature algorithm, and the TSIG digest context.
+    @rtype: (string, string, hmac.HMAC object)
+    @raises ValueError: I{other_data} is too long
+    @raises NotImplementedError: I{algorithm} is not supported
+    """
+
+    (algorithm_name, digestmod) = get_algorithm(algorithm)
+    if first:
+        ctx = hmac.new(secret, digestmod=digestmod)
+        ml = len(request_mac)
+        if ml > 0:
+            ctx.update(struct.pack('!H', ml))
+            ctx.update(request_mac)
+    id = struct.pack('!H', original_id)
+    ctx.update(id)
+    ctx.update(wire[2:])
+    if first:
+        ctx.update(keyname.to_digestable())
+        ctx.update(struct.pack('!H', dns.rdataclass.ANY))
+        ctx.update(struct.pack('!I', 0))
+    long_time = time + 0L
+    upper_time = (long_time >> 32) & 0xffffL
+    lower_time = long_time & 0xffffffffL
+    time_mac = struct.pack('!HIH', upper_time, lower_time, fudge)
+    pre_mac = algorithm_name + time_mac
+    ol = len(other_data)
+    if ol > 65535:
+        raise ValueError('TSIG Other Data is > 65535 bytes')
+    post_mac = struct.pack('!HH', error, ol) + other_data
+    if first:
+        ctx.update(pre_mac)
+        ctx.update(post_mac)
+    else:
+        ctx.update(time_mac)
+    mac = ctx.digest()
+    mpack = struct.pack('!H', len(mac))
+    tsig_rdata = pre_mac + mpack + mac + id + post_mac
+    if multi:
+        ctx = hmac.new(secret)
+        ml = len(mac)
+        ctx.update(struct.pack('!H', ml))
+        ctx.update(mac)
+    else:
+        ctx = None
+    return (tsig_rdata, mac, ctx)
+
+def hmac_md5(wire, keyname, secret, time, fudge, original_id, error,
+             other_data, request_mac, ctx=None, multi=False, first=True,
+             algorithm=default_algorithm):
+    return sign(wire, keyname, secret, time, fudge, original_id, error,
+                other_data, request_mac, ctx, multi, first, algorithm)
+
+def validate(wire, keyname, secret, now, request_mac, tsig_start, tsig_rdata,
+             tsig_rdlen, ctx=None, multi=False, first=True):
+    """Validate the specified TSIG rdata against the other input parameters.
+
+    @raises FormError: The TSIG is badly formed.
+    @raises BadTime: There is too much time skew between the client and the
+    server.
+    @raises BadSignature: The TSIG signature did not validate
+    @rtype: hmac.HMAC object"""
+
+    (adcount,) = struct.unpack("!H", wire[10:12])
+    if adcount == 0:
+        raise dns.exception.FormError
+    adcount -= 1
+    new_wire = wire[0:10] + struct.pack("!H", adcount) + wire[12:tsig_start]
+    current = tsig_rdata
+    (aname, used) = dns.name.from_wire(wire, current)
+    current = current + used
+    (upper_time, lower_time, fudge, mac_size) = \
+                 struct.unpack("!HIHH", wire[current:current + 10])
+    time = ((upper_time + 0L) << 32) + (lower_time + 0L)
+    current += 10
+    mac = wire[current:current + mac_size]
+    current += mac_size
+    (original_id, error, other_size) = \
+                  struct.unpack("!HHH", wire[current:current + 6])
+    current += 6
+    other_data = wire[current:current + other_size]
+    current += other_size
+    if current != tsig_rdata + tsig_rdlen:
+        raise dns.exception.FormError
+    if error != 0:
+        if error == BADSIG:
+            raise PeerBadSignature
+        elif error == BADKEY:
+            raise PeerBadKey
+        elif error == BADTIME:
+            raise PeerBadTime
+        elif error == BADTRUNC:
+            raise PeerBadTruncation
+        else:
+            raise PeerError('unknown TSIG error code %d' % error)
+    time_low = time - fudge
+    time_high = time + fudge
+    if now < time_low or now > time_high:
+        raise BadTime
+    (junk, our_mac, ctx) = sign(new_wire, keyname, secret, time, fudge,
+                                original_id, error, other_data,
+                                request_mac, ctx, multi, first, aname)
+    if (our_mac != mac):
+        raise BadSignature
+    return ctx
+
+def get_algorithm(algorithm):
+    """Returns the wire format string and the hash module to use for the
+    specified TSIG algorithm
+
+    @rtype: (string, hash constructor)
+    @raises NotImplementedError: I{algorithm} is not supported
+    """
+
+    hashes = {}
+    try:
+        import hashlib
+        hashes[dns.name.from_text('hmac-sha224')] = hashlib.sha224
+        hashes[dns.name.from_text('hmac-sha256')] = hashlib.sha256
+        hashes[dns.name.from_text('hmac-sha384')] = hashlib.sha384
+        hashes[dns.name.from_text('hmac-sha512')] = hashlib.sha512
+        hashes[dns.name.from_text('hmac-sha1')] = hashlib.sha1
+        hashes[dns.name.from_text('HMAC-MD5.SIG-ALG.REG.INT')] = hashlib.md5
+
+        import sys
+        if sys.hexversion < 0x02050000:
+            # hashlib doesn't conform to PEP 247: API for
+            # Cryptographic Hash Functions, which hmac before python
+            # 2.5 requires, so add the necessary items.
+            class HashlibWrapper:
+                def __init__(self, basehash):
+                    self.basehash = basehash
+                    self.digest_size = self.basehash().digest_size
+
+                def new(self, *args, **kwargs):
+                    return self.basehash(*args, **kwargs)
+
+            for name in hashes:
+                hashes[name] = HashlibWrapper(hashes[name])
+
+    except ImportError:
+        import md5, sha
+        hashes[dns.name.from_text('HMAC-MD5.SIG-ALG.REG.INT')] =  md5.md5
+        hashes[dns.name.from_text('hmac-sha1')] = sha.sha
+
+    if isinstance(algorithm, (str, unicode)):
+        algorithm = dns.name.from_text(algorithm)
+
+    if algorithm in hashes:
+        return (algorithm.to_digestable(), hashes[algorithm])
+
+    raise NotImplementedError("TSIG algorithm " + str(algorithm) +
+                              " is not supported")
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/tsigkeyring.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/tsigkeyring.py
new file mode 100644
index 0000000..cbd1a27
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/tsigkeyring.py
@@ -0,0 +1,44 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""A place to store TSIG keys."""
+
+import base64
+
+import dns.name
+
+def from_text(textring):
+    """Convert a dictionary containing (textual DNS name, base64 secret) pairs
+    into a binary keyring which has (dns.name.Name, binary secret) pairs.
+    @rtype: dict"""
+    
+    keyring = {}
+    for keytext in textring:
+        keyname = dns.name.from_text(keytext)
+        secret = base64.decodestring(textring[keytext])
+        keyring[keyname] = secret
+    return keyring
+
+def to_text(keyring):
+    """Convert a dictionary containing (dns.name.Name, binary secret) pairs
+    into a text keyring which has (textual DNS name, base64 secret) pairs.
+    @rtype: dict"""
+    
+    textring = {}
+    for keyname in keyring:
+        keytext = dns.name.to_text(keyname)
+        secret = base64.encodestring(keyring[keyname])
+        textring[keytext] = secret
+    return textring
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/ttl.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/ttl.py
new file mode 100644
index 0000000..f295300
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/ttl.py
@@ -0,0 +1,64 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""DNS TTL conversion."""
+
+import dns.exception
+
+class BadTTL(dns.exception.SyntaxError):
+    pass
+
+def from_text(text):
+    """Convert the text form of a TTL to an integer.
+
+    The BIND 8 units syntax for TTLs (e.g. '1w6d4h3m10s') is supported.
+
+    @param text: the textual TTL
+    @type text: string
+    @raises dns.ttl.BadTTL: the TTL is not well-formed
+    @rtype: int
+    """
+
+    if text.isdigit():
+        total = long(text)
+    else:
+        if not text[0].isdigit():
+            raise BadTTL
+        total = 0L
+        current = 0L
+        for c in text:
+            if c.isdigit():
+                current *= 10
+                current += long(c)
+            else:
+                c = c.lower()
+                if c == 'w':
+                    total += current * 604800L
+                elif c == 'd':
+                    total += current * 86400L
+                elif c == 'h':
+                    total += current * 3600L
+                elif c == 'm':
+                    total += current * 60L
+                elif c == 's':
+                    total += current
+                else:
+                    raise BadTTL("unknown unit '%s'" % c)
+                current = 0
+        if not current == 0:
+            raise BadTTL("trailing integer")
+    if total < 0L or total > 2147483647L:
+        raise BadTTL("TTL should be between 0 and 2^31 - 1 (inclusive)")
+    return total
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/update.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/update.py
new file mode 100644
index 0000000..7d42636
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/update.py
@@ -0,0 +1,241 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""DNS Dynamic Update Support"""
+
+import dns.message
+import dns.name
+import dns.opcode
+import dns.rdata
+import dns.rdataclass
+import dns.rdataset
+
+class Update(dns.message.Message):
+    def __init__(self, zone, rdclass=dns.rdataclass.IN, keyring=None,
+                 keyname=None, keyalgorithm=dns.tsig.default_algorithm):
+        """Initialize a new DNS Update object.
+
+        @param zone: The zone which is being updated.
+        @type zone: A dns.name.Name or string
+        @param rdclass: The class of the zone; defaults to dns.rdataclass.IN.
+        @type rdclass: An int designating the class, or a string whose value
+        is the name of a class.
+        @param keyring: The TSIG keyring to use; defaults to None.
+        @type keyring: dict
+        @param keyname: The name of the TSIG key to use; defaults to None.
+        The key must be defined in the keyring.  If a keyring is specified
+        but a keyname is not, then the key used will be the first key in the
+        keyring.  Note that the order of keys in a dictionary is not defined,
+        so applications should supply a keyname when a keyring is used, unless
+        they know the keyring contains only one key.
+        @type keyname: dns.name.Name or string
+        @param keyalgorithm: The TSIG algorithm to use; defaults to
+        dns.tsig.default_algorithm
+        @type keyalgorithm: string
+        """
+        super(Update, self).__init__()
+        self.flags |= dns.opcode.to_flags(dns.opcode.UPDATE)
+        if isinstance(zone, (str, unicode)):
+            zone = dns.name.from_text(zone)
+        self.origin = zone
+        if isinstance(rdclass, str):
+            rdclass = dns.rdataclass.from_text(rdclass)
+        self.zone_rdclass = rdclass
+        self.find_rrset(self.question, self.origin, rdclass, dns.rdatatype.SOA,
+                        create=True, force_unique=True)
+        if not keyring is None:
+            self.use_tsig(keyring, keyname, keyalgorithm)
+
+    def _add_rr(self, name, ttl, rd, deleting=None, section=None):
+        """Add a single RR to the update section."""
+
+        if section is None:
+            section = self.authority
+        covers = rd.covers()
+        rrset = self.find_rrset(section, name, self.zone_rdclass, rd.rdtype,
+                                covers, deleting, True, True)
+        rrset.add(rd, ttl)
+
+    def _add(self, replace, section, name, *args):
+        """Add records.  The first argument is the replace mode.  If
+        false, RRs are added to an existing RRset; if true, the RRset
+        is replaced with the specified contents.  The second
+        argument is the section to add to.  The third argument
+        is always a name.  The other arguments can be:
+
+                - rdataset...
+
+                - ttl, rdata...
+
+                - ttl, rdtype, string..."""
+
+        if isinstance(name, (str, unicode)):
+            name = dns.name.from_text(name, None)
+        if isinstance(args[0], dns.rdataset.Rdataset):
+            for rds in args:
+                if replace:
+                    self.delete(name, rds.rdtype)
+                for rd in rds:
+                    self._add_rr(name, rds.ttl, rd, section=section)
+        else:
+            args = list(args)
+            ttl = int(args.pop(0))
+            if isinstance(args[0], dns.rdata.Rdata):
+                if replace:
+                    self.delete(name, args[0].rdtype)
+                for rd in args:
+                    self._add_rr(name, ttl, rd, section=section)
+            else:
+                rdtype = args.pop(0)
+                if isinstance(rdtype, str):
+                    rdtype = dns.rdatatype.from_text(rdtype)
+                if replace:
+                    self.delete(name, rdtype)
+                for s in args:
+                    rd = dns.rdata.from_text(self.zone_rdclass, rdtype, s,
+                                             self.origin)
+                    self._add_rr(name, ttl, rd, section=section)
+
+    def add(self, name, *args):
+        """Add records.  The first argument is always a name.  The other
+        arguments can be:
+
+                - rdataset...
+
+                - ttl, rdata...
+
+                - ttl, rdtype, string..."""
+        self._add(False, self.authority, name, *args)
+
+    def delete(self, name, *args):
+        """Delete records.  The first argument is always a name.  The other
+        arguments can be:
+
+                - I{nothing}
+
+                - rdataset...
+
+                - rdata...
+
+                - rdtype, [string...]"""
+
+        if isinstance(name, (str, unicode)):
+            name = dns.name.from_text(name, None)
+        if len(args) == 0:
+            rrset = self.find_rrset(self.authority, name, dns.rdataclass.ANY,
+                                    dns.rdatatype.ANY, dns.rdatatype.NONE,
+                                    dns.rdatatype.ANY, True, True)
+        elif isinstance(args[0], dns.rdataset.Rdataset):
+            for rds in args:
+                for rd in rds:
+                    self._add_rr(name, 0, rd, dns.rdataclass.NONE)
+        else:
+            args = list(args)
+            if isinstance(args[0], dns.rdata.Rdata):
+                for rd in args:
+                    self._add_rr(name, 0, rd, dns.rdataclass.NONE)
+            else:
+                rdtype = args.pop(0)
+                if isinstance(rdtype, str):
+                    rdtype = dns.rdatatype.from_text(rdtype)
+                if len(args) == 0:
+                    rrset = self.find_rrset(self.authority, name,
+                                            self.zone_rdclass, rdtype,
+                                            dns.rdatatype.NONE,
+                                            dns.rdataclass.ANY,
+                                            True, True)
+                else:
+                    for s in args:
+                        rd = dns.rdata.from_text(self.zone_rdclass, rdtype, s,
+                                                 self.origin)
+                        self._add_rr(name, 0, rd, dns.rdataclass.NONE)
+
+    def replace(self, name, *args):
+        """Replace records.  The first argument is always a name.  The other
+        arguments can be:
+
+                - rdataset...
+
+                - ttl, rdata...
+
+                - ttl, rdtype, string...
+
+        Note that if you want to replace the entire node, you should do
+        a delete of the name followed by one or more calls to add."""
+
+        self._add(True, self.authority, name, *args)
+
+    def present(self, name, *args):
+        """Require that an owner name (and optionally an rdata type,
+        or specific rdataset) exists as a prerequisite to the
+        execution of the update.  The first argument is always a name.
+        The other arguments can be:
+
+                - rdataset...
+
+                - rdata...
+
+                - rdtype, string..."""
+
+        if isinstance(name, (str, unicode)):
+            name = dns.name.from_text(name, None)
+        if len(args) == 0:
+            rrset = self.find_rrset(self.answer, name,
+                                    dns.rdataclass.ANY, dns.rdatatype.ANY,
+                                    dns.rdatatype.NONE, None,
+                                    True, True)
+        elif isinstance(args[0], dns.rdataset.Rdataset) or \
+             isinstance(args[0], dns.rdata.Rdata) or \
+             len(args) > 1:
+            if not isinstance(args[0], dns.rdataset.Rdataset):
+                # Add a 0 TTL
+                args = list(args)
+                args.insert(0, 0)
+            self._add(False, self.answer, name, *args)
+        else:
+            rdtype = args[0]
+            if isinstance(rdtype, str):
+                rdtype = dns.rdatatype.from_text(rdtype)
+            rrset = self.find_rrset(self.answer, name,
+                                    dns.rdataclass.ANY, rdtype,
+                                    dns.rdatatype.NONE, None,
+                                    True, True)
+
+    def absent(self, name, rdtype=None):
+        """Require that an owner name (and optionally an rdata type) does
+        not exist as a prerequisite to the execution of the update."""
+
+        if isinstance(name, (str, unicode)):
+            name = dns.name.from_text(name, None)
+        if rdtype is None:
+            rrset = self.find_rrset(self.answer, name,
+                                    dns.rdataclass.NONE, dns.rdatatype.ANY,
+                                    dns.rdatatype.NONE, None,
+                                    True, True)
+        else:
+            if isinstance(rdtype, str):
+                rdtype = dns.rdatatype.from_text(rdtype)
+            rrset = self.find_rrset(self.answer, name,
+                                    dns.rdataclass.NONE, rdtype,
+                                    dns.rdatatype.NONE, None,
+                                    True, True)
+
+    def to_wire(self, origin=None, max_size=65535):
+        """Return a string containing the update in DNS compressed wire
+        format.
+        @rtype: string"""
+        if origin is None:
+            origin = self.origin
+        return super(Update, self).to_wire(origin, max_size)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/version.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/version.py
new file mode 100644
index 0000000..7a36775
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/version.py
@@ -0,0 +1,34 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""dnspython release version information."""
+
+MAJOR = 1
+MINOR = 8
+MICRO = 0
+RELEASELEVEL = 0x0f
+SERIAL = 0
+
+if RELEASELEVEL == 0x0f:
+    version = '%d.%d.%d' % (MAJOR, MINOR, MICRO)
+elif RELEASELEVEL == 0x00:
+    version = '%d.%d.%dx%d' % \
+              (MAJOR, MINOR, MICRO, SERIAL)
+else:
+    version = '%d.%d.%d%x%d' % \
+              (MAJOR, MINOR, MICRO, RELEASELEVEL, SERIAL)
+
+hexversion = MAJOR << 24 | MINOR << 16 | MICRO << 8 | RELEASELEVEL << 4 | \
+             SERIAL
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/zone.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/zone.py
new file mode 100644
index 0000000..93c157d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/dns/zone.py
@@ -0,0 +1,855 @@
+# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""DNS Zones."""
+
+from __future__ import generators
+
+import sys
+
+import dns.exception
+import dns.name
+import dns.node
+import dns.rdataclass
+import dns.rdatatype
+import dns.rdata
+import dns.rrset
+import dns.tokenizer
+import dns.ttl
+
+class BadZone(dns.exception.DNSException):
+    """The zone is malformed."""
+    pass
+
+class NoSOA(BadZone):
+    """The zone has no SOA RR at its origin."""
+    pass
+
+class NoNS(BadZone):
+    """The zone has no NS RRset at its origin."""
+    pass
+
+class UnknownOrigin(BadZone):
+    """The zone's origin is unknown."""
+    pass
+
+class Zone(object):
+    """A DNS zone.
+
+    A Zone is a mapping from names to nodes.  The zone object may be
+    treated like a Python dictionary, e.g. zone[name] will retrieve
+    the node associated with that name.  The I{name} may be a
+    dns.name.Name object, or it may be a string.  In the either case,
+    if the name is relative it is treated as relative to the origin of
+    the zone.
+
+    @ivar rdclass: The zone's rdata class; the default is class IN.
+    @type rdclass: int
+    @ivar origin: The origin of the zone.
+    @type origin: dns.name.Name object
+    @ivar nodes: A dictionary mapping the names of nodes in the zone to the
+    nodes themselves.
+    @type nodes: dict
+    @ivar relativize: should names in the zone be relativized?
+    @type relativize: bool
+    @cvar node_factory: the factory used to create a new node
+    @type node_factory: class or callable
+    """
+
+    node_factory = dns.node.Node
+
+    __slots__ = ['rdclass', 'origin', 'nodes', 'relativize']
+
+    def __init__(self, origin, rdclass=dns.rdataclass.IN, relativize=True):
+        """Initialize a zone object.
+
+        @param origin: The origin of the zone.
+        @type origin: dns.name.Name object
+        @param rdclass: The zone's rdata class; the default is class IN.
+        @type rdclass: int"""
+
+        self.rdclass = rdclass
+        self.origin = origin
+        self.nodes = {}
+        self.relativize = relativize
+
+    def __eq__(self, other):
+        """Two zones are equal if they have the same origin, class, and
+        nodes.
+        @rtype: bool
+        """
+
+        if not isinstance(other, Zone):
+            return False
+        if self.rdclass != other.rdclass or \
+           self.origin != other.origin or \
+           self.nodes != other.nodes:
+            return False
+        return True
+
+    def __ne__(self, other):
+        """Are two zones not equal?
+        @rtype: bool
+        """
+
+        return not self.__eq__(other)
+
+    def _validate_name(self, name):
+        if isinstance(name, (str, unicode)):
+            name = dns.name.from_text(name, None)
+        elif not isinstance(name, dns.name.Name):
+            raise KeyError("name parameter must be convertable to a DNS name")
+        if name.is_absolute():
+            if not name.is_subdomain(self.origin):
+                raise KeyError("name parameter must be a subdomain of the zone origin")
+            if self.relativize:
+                name = name.relativize(self.origin)
+        return name
+
+    def __getitem__(self, key):
+        key = self._validate_name(key)
+        return self.nodes[key]
+
+    def __setitem__(self, key, value):
+        key = self._validate_name(key)
+        self.nodes[key] = value
+
+    def __delitem__(self, key):
+        key = self._validate_name(key)
+        del self.nodes[key]
+
+    def __iter__(self):
+        return self.nodes.iterkeys()
+
+    def iterkeys(self):
+        return self.nodes.iterkeys()
+
+    def keys(self):
+        return self.nodes.keys()
+
+    def itervalues(self):
+        return self.nodes.itervalues()
+
+    def values(self):
+        return self.nodes.values()
+
+    def iteritems(self):
+        return self.nodes.iteritems()
+
+    def items(self):
+        return self.nodes.items()
+
+    def get(self, key):
+        key = self._validate_name(key)
+        return self.nodes.get(key)
+
+    def __contains__(self, other):
+        return other in self.nodes
+
+    def find_node(self, name, create=False):
+        """Find a node in the zone, possibly creating it.
+
+        @param name: the name of the node to find
+        @type name: dns.name.Name object or string
+        @param create: should the node be created if it doesn't exist?
+        @type create: bool
+        @raises KeyError: the name is not known and create was not specified.
+        @rtype: dns.node.Node object
+        """
+
+        name = self._validate_name(name)
+        node = self.nodes.get(name)
+        if node is None:
+            if not create:
+                raise KeyError
+            node = self.node_factory()
+            self.nodes[name] = node
+        return node
+
+    def get_node(self, name, create=False):
+        """Get a node in the zone, possibly creating it.
+
+        This method is like L{find_node}, except it returns None instead
+        of raising an exception if the node does not exist and creation
+        has not been requested.
+
+        @param name: the name of the node to find
+        @type name: dns.name.Name object or string
+        @param create: should the node be created if it doesn't exist?
+        @type create: bool
+        @rtype: dns.node.Node object or None
+        """
+
+        try:
+            node = self.find_node(name, create)
+        except KeyError:
+            node = None
+        return node
+
+    def delete_node(self, name):
+        """Delete the specified node if it exists.
+
+        It is not an error if the node does not exist.
+        """
+
+        name = self._validate_name(name)
+        if self.nodes.has_key(name):
+            del self.nodes[name]
+
+    def find_rdataset(self, name, rdtype, covers=dns.rdatatype.NONE,
+                      create=False):
+        """Look for rdata with the specified name and type in the zone,
+        and return an rdataset encapsulating it.
+
+        The I{name}, I{rdtype}, and I{covers} parameters may be
+        strings, in which case they will be converted to their proper
+        type.
+
+        The rdataset returned is not a copy; changes to it will change
+        the zone.
+
+        KeyError is raised if the name or type are not found.
+        Use L{get_rdataset} if you want to have None returned instead.
+
+        @param name: the owner name to look for
+        @type name: DNS.name.Name object or string
+        @param rdtype: the rdata type desired
+        @type rdtype: int or string
+        @param covers: the covered type (defaults to None)
+        @type covers: int or string
+        @param create: should the node and rdataset be created if they do not
+        exist?
+        @type create: bool
+        @raises KeyError: the node or rdata could not be found
+        @rtype: dns.rrset.RRset object
+        """
+
+        name = self._validate_name(name)
+        if isinstance(rdtype, str):
+            rdtype = dns.rdatatype.from_text(rdtype)
+        if isinstance(covers, str):
+            covers = dns.rdatatype.from_text(covers)
+        node = self.find_node(name, create)
+        return node.find_rdataset(self.rdclass, rdtype, covers, create)
+
+    def get_rdataset(self, name, rdtype, covers=dns.rdatatype.NONE,
+                     create=False):
+        """Look for rdata with the specified name and type in the zone,
+        and return an rdataset encapsulating it.
+
+        The I{name}, I{rdtype}, and I{covers} parameters may be
+        strings, in which case they will be converted to their proper
+        type.
+
+        The rdataset returned is not a copy; changes to it will change
+        the zone.
+
+        None is returned if the name or type are not found.
+        Use L{find_rdataset} if you want to have KeyError raised instead.
+
+        @param name: the owner name to look for
+        @type name: DNS.name.Name object or string
+        @param rdtype: the rdata type desired
+        @type rdtype: int or string
+        @param covers: the covered type (defaults to None)
+        @type covers: int or string
+        @param create: should the node and rdataset be created if they do not
+        exist?
+        @type create: bool
+        @rtype: dns.rrset.RRset object
+        """
+
+        try:
+            rdataset = self.find_rdataset(name, rdtype, covers, create)
+        except KeyError:
+            rdataset = None
+        return rdataset
+
+    def delete_rdataset(self, name, rdtype, covers=dns.rdatatype.NONE):
+        """Delete the rdataset matching I{rdtype} and I{covers}, if it
+        exists at the node specified by I{name}.
+
+        The I{name}, I{rdtype}, and I{covers} parameters may be
+        strings, in which case they will be converted to their proper
+        type.
+
+        It is not an error if the node does not exist, or if there is no
+        matching rdataset at the node.
+
+        If the node has no rdatasets after the deletion, it will itself
+        be deleted.
+
+        @param name: the owner name to look for
+        @type name: DNS.name.Name object or string
+        @param rdtype: the rdata type desired
+        @type rdtype: int or string
+        @param covers: the covered type (defaults to None)
+        @type covers: int or string
+        """
+
+        name = self._validate_name(name)
+        if isinstance(rdtype, str):
+            rdtype = dns.rdatatype.from_text(rdtype)
+        if isinstance(covers, str):
+            covers = dns.rdatatype.from_text(covers)
+        node = self.get_node(name)
+        if not node is None:
+            node.delete_rdataset(self.rdclass, rdtype, covers)
+            if len(node) == 0:
+                self.delete_node(name)
+
+    def replace_rdataset(self, name, replacement):
+        """Replace an rdataset at name.
+
+        It is not an error if there is no rdataset matching I{replacement}.
+
+        Ownership of the I{replacement} object is transferred to the zone;
+        in other words, this method does not store a copy of I{replacement}
+        at the node, it stores I{replacement} itself.
+
+        If the I{name} node does not exist, it is created.
+
+        @param name: the owner name
+        @type name: DNS.name.Name object or string
+        @param replacement: the replacement rdataset
+        @type replacement: dns.rdataset.Rdataset
+        """
+
+        if replacement.rdclass != self.rdclass:
+            raise ValueError('replacement.rdclass != zone.rdclass')
+        node = self.find_node(name, True)
+        node.replace_rdataset(replacement)
+
+    def find_rrset(self, name, rdtype, covers=dns.rdatatype.NONE):
+        """Look for rdata with the specified name and type in the zone,
+        and return an RRset encapsulating it.
+
+        The I{name}, I{rdtype}, and I{covers} parameters may be
+        strings, in which case they will be converted to their proper
+        type.
+
+        This method is less efficient than the similar
+        L{find_rdataset} because it creates an RRset instead of
+        returning the matching rdataset.  It may be more convenient
+        for some uses since it returns an object which binds the owner
+        name to the rdata.
+
+        This method may not be used to create new nodes or rdatasets;
+        use L{find_rdataset} instead.
+
+        KeyError is raised if the name or type are not found.
+        Use L{get_rrset} if you want to have None returned instead.
+
+        @param name: the owner name to look for
+        @type name: DNS.name.Name object or string
+        @param rdtype: the rdata type desired
+        @type rdtype: int or string
+        @param covers: the covered type (defaults to None)
+        @type covers: int or string
+        @raises KeyError: the node or rdata could not be found
+        @rtype: dns.rrset.RRset object
+        """
+
+        name = self._validate_name(name)
+        if isinstance(rdtype, str):
+            rdtype = dns.rdatatype.from_text(rdtype)
+        if isinstance(covers, str):
+            covers = dns.rdatatype.from_text(covers)
+        rdataset = self.nodes[name].find_rdataset(self.rdclass, rdtype, covers)
+        rrset = dns.rrset.RRset(name, self.rdclass, rdtype, covers)
+        rrset.update(rdataset)
+        return rrset
+
+    def get_rrset(self, name, rdtype, covers=dns.rdatatype.NONE):
+        """Look for rdata with the specified name and type in the zone,
+        and return an RRset encapsulating it.
+
+        The I{name}, I{rdtype}, and I{covers} parameters may be
+        strings, in which case they will be converted to their proper
+        type.
+
+        This method is less efficient than the similar L{get_rdataset}
+        because it creates an RRset instead of returning the matching
+        rdataset.  It may be more convenient for some uses since it
+        returns an object which binds the owner name to the rdata.
+
+        This method may not be used to create new nodes or rdatasets;
+        use L{find_rdataset} instead.
+
+        None is returned if the name or type are not found.
+        Use L{find_rrset} if you want to have KeyError raised instead.
+
+        @param name: the owner name to look for
+        @type name: DNS.name.Name object or string
+        @param rdtype: the rdata type desired
+        @type rdtype: int or string
+        @param covers: the covered type (defaults to None)
+        @type covers: int or string
+        @rtype: dns.rrset.RRset object
+        """
+
+        try:
+            rrset = self.find_rrset(name, rdtype, covers)
+        except KeyError:
+            rrset = None
+        return rrset
+
+    def iterate_rdatasets(self, rdtype=dns.rdatatype.ANY,
+                          covers=dns.rdatatype.NONE):
+        """Return a generator which yields (name, rdataset) tuples for
+        all rdatasets in the zone which have the specified I{rdtype}
+        and I{covers}.  If I{rdtype} is dns.rdatatype.ANY, the default,
+        then all rdatasets will be matched.
+
+        @param rdtype: int or string
+        @type rdtype: int or string
+        @param covers: the covered type (defaults to None)
+        @type covers: int or string
+        """
+
+        if isinstance(rdtype, str):
+            rdtype = dns.rdatatype.from_text(rdtype)
+        if isinstance(covers, str):
+            covers = dns.rdatatype.from_text(covers)
+        for (name, node) in self.iteritems():
+            for rds in node:
+                if rdtype == dns.rdatatype.ANY or \
+                   (rds.rdtype == rdtype and rds.covers == covers):
+                    yield (name, rds)
+
+    def iterate_rdatas(self, rdtype=dns.rdatatype.ANY,
+                       covers=dns.rdatatype.NONE):
+        """Return a generator which yields (name, ttl, rdata) tuples for
+        all rdatas in the zone which have the specified I{rdtype}
+        and I{covers}.  If I{rdtype} is dns.rdatatype.ANY, the default,
+        then all rdatas will be matched.
+
+        @param rdtype: int or string
+        @type rdtype: int or string
+        @param covers: the covered type (defaults to None)
+        @type covers: int or string
+        """
+
+        if isinstance(rdtype, str):
+            rdtype = dns.rdatatype.from_text(rdtype)
+        if isinstance(covers, str):
+            covers = dns.rdatatype.from_text(covers)
+        for (name, node) in self.iteritems():
+            for rds in node:
+                if rdtype == dns.rdatatype.ANY or \
+                   (rds.rdtype == rdtype and rds.covers == covers):
+                    for rdata in rds:
+                        yield (name, rds.ttl, rdata)
+
+    def to_file(self, f, sorted=True, relativize=True, nl=None):
+        """Write a zone to a file.
+
+        @param f: file or string.  If I{f} is a string, it is treated
+        as the name of a file to open.
+        @param sorted: if True, the file will be written with the
+        names sorted in DNSSEC order from least to greatest.  Otherwise
+        the names will be written in whatever order they happen to have
+        in the zone's dictionary.
+        @param relativize: if True, domain names in the output will be
+        relativized to the zone's origin (if possible).
+        @type relativize: bool
+        @param nl: The end of line string.  If not specified, the
+        output will use the platform's native end-of-line marker (i.e.
+        LF on POSIX, CRLF on Windows, CR on Macintosh).
+        @type nl: string or None
+        """
+
+        if sys.hexversion >= 0x02030000:
+            # allow Unicode filenames
+            str_type = basestring
+        else:
+            str_type = str
+        if nl is None:
+            opts = 'w'
+        else:
+            opts = 'wb'
+        if isinstance(f, str_type):
+            f = file(f, opts)
+            want_close = True
+        else:
+            want_close = False
+        try:
+            if sorted:
+                names = self.keys()
+                names.sort()
+            else:
+                names = self.iterkeys()
+            for n in names:
+                l = self[n].to_text(n, origin=self.origin,
+                                    relativize=relativize)
+                if nl is None:
+                    print >> f, l
+                else:
+                    f.write(l)
+                    f.write(nl)
+        finally:
+            if want_close:
+                f.close()
+
+    def check_origin(self):
+        """Do some simple checking of the zone's origin.
+
+        @raises dns.zone.NoSOA: there is no SOA RR
+        @raises dns.zone.NoNS: there is no NS RRset
+        @raises KeyError: there is no origin node
+        """
+        if self.relativize:
+            name = dns.name.empty
+        else:
+            name = self.origin
+        if self.get_rdataset(name, dns.rdatatype.SOA) is None:
+            raise NoSOA
+        if self.get_rdataset(name, dns.rdatatype.NS) is None:
+            raise NoNS
+
+
+class _MasterReader(object):
+    """Read a DNS master file
+
+    @ivar tok: The tokenizer
+    @type tok: dns.tokenizer.Tokenizer object
+    @ivar ttl: The default TTL
+    @type ttl: int
+    @ivar last_name: The last name read
+    @type last_name: dns.name.Name object
+    @ivar current_origin: The current origin
+    @type current_origin: dns.name.Name object
+    @ivar relativize: should names in the zone be relativized?
+    @type relativize: bool
+    @ivar zone: the zone
+    @type zone: dns.zone.Zone object
+    @ivar saved_state: saved reader state (used when processing $INCLUDE)
+    @type saved_state: list of (tokenizer, current_origin, last_name, file)
+    tuples.
+    @ivar current_file: the file object of the $INCLUDed file being parsed
+    (None if no $INCLUDE is active).
+    @ivar allow_include: is $INCLUDE allowed?
+    @type allow_include: bool
+    @ivar check_origin: should sanity checks of the origin node be done?
+    The default is True.
+    @type check_origin: bool
+    """
+
+    def __init__(self, tok, origin, rdclass, relativize, zone_factory=Zone,
+                 allow_include=False, check_origin=True):
+        if isinstance(origin, (str, unicode)):
+            origin = dns.name.from_text(origin)
+        self.tok = tok
+        self.current_origin = origin
+        self.relativize = relativize
+        self.ttl = 0
+        self.last_name = None
+        self.zone = zone_factory(origin, rdclass, relativize=relativize)
+        self.saved_state = []
+        self.current_file = None
+        self.allow_include = allow_include
+        self.check_origin = check_origin
+
+    def _eat_line(self):
+        while 1:
+            token = self.tok.get()
+            if token.is_eol_or_eof():
+                break
+
+    def _rr_line(self):
+        """Process one line from a DNS master file."""
+        # Name
+        if self.current_origin is None:
+            raise UnknownOrigin
+        token = self.tok.get(want_leading = True)
+        if not token.is_whitespace():
+            self.last_name = dns.name.from_text(token.value, self.current_origin)
+        else:
+            token = self.tok.get()
+            if token.is_eol_or_eof():
+                # treat leading WS followed by EOL/EOF as if they were EOL/EOF.
+                return
+            self.tok.unget(token)
+        name = self.last_name
+        if not name.is_subdomain(self.zone.origin):
+            self._eat_line()
+            return
+        if self.relativize:
+            name = name.relativize(self.zone.origin)
+        token = self.tok.get()
+        if not token.is_identifier():
+            raise dns.exception.SyntaxError
+        # TTL
+        try:
+            ttl = dns.ttl.from_text(token.value)
+            token = self.tok.get()
+            if not token.is_identifier():
+                raise dns.exception.SyntaxError
+        except dns.ttl.BadTTL:
+            ttl = self.ttl
+        # Class
+        try:
+            rdclass = dns.rdataclass.from_text(token.value)
+            token = self.tok.get()
+            if not token.is_identifier():
+                raise dns.exception.SyntaxError
+        except dns.exception.SyntaxError:
+            raise dns.exception.SyntaxError
+        except:
+            rdclass = self.zone.rdclass
+        if rdclass != self.zone.rdclass:
+            raise dns.exception.SyntaxError("RR class is not zone's class")
+        # Type
+        try:
+            rdtype = dns.rdatatype.from_text(token.value)
+        except:
+            raise dns.exception.SyntaxError("unknown rdatatype '%s'" % token.value)
+        n = self.zone.nodes.get(name)
+        if n is None:
+            n = self.zone.node_factory()
+            self.zone.nodes[name] = n
+        try:
+            rd = dns.rdata.from_text(rdclass, rdtype, self.tok,
+                                     self.current_origin, False)
+        except dns.exception.SyntaxError:
+            # Catch and reraise.
+            (ty, va) = sys.exc_info()[:2]
+            raise va
+        except:
+            # All exceptions that occur in the processing of rdata
+            # are treated as syntax errors.  This is not strictly
+            # correct, but it is correct almost all of the time.
+            # We convert them to syntax errors so that we can emit
+            # helpful filename:line info.
+            (ty, va) = sys.exc_info()[:2]
+            raise dns.exception.SyntaxError("caught exception %s: %s" % (str(ty), str(va)))
+
+        rd.choose_relativity(self.zone.origin, self.relativize)
+        covers = rd.covers()
+        rds = n.find_rdataset(rdclass, rdtype, covers, True)
+        rds.add(rd, ttl)
+
+    def read(self):
+        """Read a DNS master file and build a zone object.
+
+        @raises dns.zone.NoSOA: No SOA RR was found at the zone origin
+        @raises dns.zone.NoNS: No NS RRset was found at the zone origin
+        """
+
+        try:
+            while 1:
+                token = self.tok.get(True, True).unescape()
+                if token.is_eof():
+                    if not self.current_file is None:
+                        self.current_file.close()
+                    if len(self.saved_state) > 0:
+                        (self.tok,
+                         self.current_origin,
+                         self.last_name,
+                         self.current_file,
+                         self.ttl) = self.saved_state.pop(-1)
+                        continue
+                    break
+                elif token.is_eol():
+                    continue
+                elif token.is_comment():
+                    self.tok.get_eol()
+                    continue
+                elif token.value[0] == '$':
+                    u = token.value.upper()
+                    if u == '$TTL':
+                        token = self.tok.get()
+                        if not token.is_identifier():
+                            raise dns.exception.SyntaxError("bad $TTL")
+                        self.ttl = dns.ttl.from_text(token.value)
+                        self.tok.get_eol()
+                    elif u == '$ORIGIN':
+                        self.current_origin = self.tok.get_name()
+                        self.tok.get_eol()
+                        if self.zone.origin is None:
+                            self.zone.origin = self.current_origin
+                    elif u == '$INCLUDE' and self.allow_include:
+                        token = self.tok.get()
+                        if not token.is_quoted_string():
+                            raise dns.exception.SyntaxError("bad filename in $INCLUDE")
+                        filename = token.value
+                        token = self.tok.get()
+                        if token.is_identifier():
+                            new_origin = dns.name.from_text(token.value, \
+                                                            self.current_origin)
+                            self.tok.get_eol()
+                        elif not token.is_eol_or_eof():
+                            raise dns.exception.SyntaxError("bad origin in $INCLUDE")
+                        else:
+                            new_origin = self.current_origin
+                        self.saved_state.append((self.tok,
+                                                 self.current_origin,
+                                                 self.last_name,
+                                                 self.current_file,
+                                                 self.ttl))
+                        self.current_file = file(filename, 'r')
+                        self.tok = dns.tokenizer.Tokenizer(self.current_file,
+                                                           filename)
+                        self.current_origin = new_origin
+                    else:
+                        raise dns.exception.SyntaxError("Unknown master file directive '" + u + "'")
+                    continue
+                self.tok.unget(token)
+                self._rr_line()
+        except dns.exception.SyntaxError, detail:
+            (filename, line_number) = self.tok.where()
+            if detail is None:
+                detail = "syntax error"
+            raise dns.exception.SyntaxError("%s:%d: %s" % (filename, line_number, detail))
+
+        # Now that we're done reading, do some basic checking of the zone.
+        if self.check_origin:
+            self.zone.check_origin()
+
+def from_text(text, origin = None, rdclass = dns.rdataclass.IN,
+              relativize = True, zone_factory=Zone, filename=None,
+              allow_include=False, check_origin=True):
+    """Build a zone object from a master file format string.
+
+    @param text: the master file format input
+    @type text: string.
+    @param origin: The origin of the zone; if not specified, the first
+    $ORIGIN statement in the master file will determine the origin of the
+    zone.
+    @type origin: dns.name.Name object or string
+    @param rdclass: The zone's rdata class; the default is class IN.
+    @type rdclass: int
+    @param relativize: should names be relativized?  The default is True
+    @type relativize: bool
+    @param zone_factory: The zone factory to use
+    @type zone_factory: function returning a Zone
+    @param filename: The filename to emit when describing where an error
+    occurred; the default is '<string>'.
+    @type filename: string
+    @param allow_include: is $INCLUDE allowed?
+    @type allow_include: bool
+    @param check_origin: should sanity checks of the origin node be done?
+    The default is True.
+    @type check_origin: bool
+    @raises dns.zone.NoSOA: No SOA RR was found at the zone origin
+    @raises dns.zone.NoNS: No NS RRset was found at the zone origin
+    @rtype: dns.zone.Zone object
+    """
+
+    # 'text' can also be a file, but we don't publish that fact
+    # since it's an implementation detail.  The official file
+    # interface is from_file().
+
+    if filename is None:
+        filename = '<string>'
+    tok = dns.tokenizer.Tokenizer(text, filename)
+    reader = _MasterReader(tok, origin, rdclass, relativize, zone_factory,
+                           allow_include=allow_include,
+                           check_origin=check_origin)
+    reader.read()
+    return reader.zone
+
+def from_file(f, origin = None, rdclass = dns.rdataclass.IN,
+              relativize = True, zone_factory=Zone, filename=None,
+              allow_include=True, check_origin=True):
+    """Read a master file and build a zone object.
+
+    @param f: file or string.  If I{f} is a string, it is treated
+    as the name of a file to open.
+    @param origin: The origin of the zone; if not specified, the first
+    $ORIGIN statement in the master file will determine the origin of the
+    zone.
+    @type origin: dns.name.Name object or string
+    @param rdclass: The zone's rdata class; the default is class IN.
+    @type rdclass: int
+    @param relativize: should names be relativized?  The default is True
+    @type relativize: bool
+    @param zone_factory: The zone factory to use
+    @type zone_factory: function returning a Zone
+    @param filename: The filename to emit when describing where an error
+    occurred; the default is '<file>', or the value of I{f} if I{f} is a
+    string.
+    @type filename: string
+    @param allow_include: is $INCLUDE allowed?
+    @type allow_include: bool
+    @param check_origin: should sanity checks of the origin node be done?
+    The default is True.
+    @type check_origin: bool
+    @raises dns.zone.NoSOA: No SOA RR was found at the zone origin
+    @raises dns.zone.NoNS: No NS RRset was found at the zone origin
+    @rtype: dns.zone.Zone object
+    """
+
+    if sys.hexversion >= 0x02030000:
+        # allow Unicode filenames; turn on universal newline support
+        str_type = basestring
+        opts = 'rU'
+    else:
+        str_type = str
+        opts = 'r'
+    if isinstance(f, str_type):
+        if filename is None:
+            filename = f
+        f = file(f, opts)
+        want_close = True
+    else:
+        if filename is None:
+            filename = '<file>'
+        want_close = False
+
+    try:
+        z = from_text(f, origin, rdclass, relativize, zone_factory,
+                      filename, allow_include, check_origin)
+    finally:
+        if want_close:
+            f.close()
+    return z
+
+def from_xfr(xfr, zone_factory=Zone, relativize=True):
+    """Convert the output of a zone transfer generator into a zone object.
+
+    @param xfr: The xfr generator
+    @type xfr: generator of dns.message.Message objects
+    @param relativize: should names be relativized?  The default is True.
+    It is essential that the relativize setting matches the one specified
+    to dns.query.xfr().
+    @type relativize: bool
+    @raises dns.zone.NoSOA: No SOA RR was found at the zone origin
+    @raises dns.zone.NoNS: No NS RRset was found at the zone origin
+    @rtype: dns.zone.Zone object
+    """
+
+    z = None
+    for r in xfr:
+        if z is None:
+            if relativize:
+                origin = r.origin
+            else:
+                origin = r.answer[0].name
+            rdclass = r.answer[0].rdclass
+            z = zone_factory(origin, rdclass, relativize=relativize)
+        for rrset in r.answer:
+            znode = z.nodes.get(rrset.name)
+            if not znode:
+                znode = z.node_factory()
+                z.nodes[rrset.name] = znode
+            zrds = znode.find_rdataset(rrset.rdclass, rrset.rdtype,
+                                       rrset.covers, True)
+            zrds.update_ttl(rrset.ttl)
+            for rd in rrset:
+                rd.choose_relativity(z.origin, relativize)
+                zrds.add(rd)
+    z.check_origin()
+    return z
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/ipaddr/COPYING b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/ipaddr/COPYING
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/ipaddr/COPYING
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/ipaddr/MANIFEST.in b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/ipaddr/MANIFEST.in
new file mode 100644
index 0000000..f572804
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/ipaddr/MANIFEST.in
@@ -0,0 +1,3 @@
+include COPYING
+include ipaddr_test.py
+include RELEASENOTES
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/ipaddr/README b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/ipaddr/README
new file mode 100644
index 0000000..1b54294
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/ipaddr/README
@@ -0,0 +1,8 @@
+ipaddr.py is a library for working with IP addresses, both IPv4 and IPv6.
+It was developed by Google for internal use, and is now open source.
+
+Project home page: http://code.google.com/p/ipaddr-py/
+
+Please send contributions to ipaddr-py-dev@googlegroups.com.  Code should
+include unit tests and follow the Google Python style guide:
+http://code.google.com/p/soc/wiki/PythonStyleGuide
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/ipaddr/README.web-page-replay b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/ipaddr/README.web-page-replay
new file mode 100644
index 0000000..4b42084
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/ipaddr/README.web-page-replay
@@ -0,0 +1,12 @@
+Name: An IPv4/IPv6 manipulation library in Python.
+Short Name: ipaddr-py
+URL: https://code.google.com/p/ipaddr-py/
+Version: 2.1.10 (ipaddr.__version__)
+License: Apache (v2.0)
+License File: COPYING
+
+Description:
+Used by Web Page Replay to check if an IP address is private.
+
+Local Modifications:
+Cherry picked revision 728996d6b1d4 to add license boilerplate to test-2to3.sh.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/ipaddr/ipaddr.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/ipaddr/ipaddr.py
new file mode 100644
index 0000000..ad27ae9
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/ipaddr/ipaddr.py
@@ -0,0 +1,1897 @@
+#!/usr/bin/python
+#
+# Copyright 2007 Google Inc.
+#  Licensed to PSF under a Contributor Agreement.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied. See the License for the specific language governing
+# permissions and limitations under the License.
+
+"""A fast, lightweight IPv4/IPv6 manipulation library in Python.
+
+This library is used to create/poke/manipulate IPv4 and IPv6 addresses
+and networks.
+
+"""
+
+__version__ = '2.1.10'
+
+import struct
+
+IPV4LENGTH = 32
+IPV6LENGTH = 128
+
+
+class AddressValueError(ValueError):
+    """A Value Error related to the address."""
+
+
+class NetmaskValueError(ValueError):
+    """A Value Error related to the netmask."""
+
+
+def IPAddress(address, version=None):
+    """Take an IP string/int and return an object of the correct type.
+
+    Args:
+        address: A string or integer, the IP address.  Either IPv4 or
+          IPv6 addresses may be supplied; integers less than 2**32 will
+          be considered to be IPv4 by default.
+        version: An Integer, 4 or 6. If set, don't try to automatically
+          determine what the IP address type is. important for things
+          like IPAddress(1), which could be IPv4, '0.0.0.1',  or IPv6,
+          '::1'.
+
+    Returns:
+        An IPv4Address or IPv6Address object.
+
+    Raises:
+        ValueError: if the string passed isn't either a v4 or a v6
+          address.
+
+    """
+    if version:
+        if version == 4:
+            return IPv4Address(address)
+        elif version == 6:
+            return IPv6Address(address)
+
+    try:
+        return IPv4Address(address)
+    except (AddressValueError, NetmaskValueError):
+        pass
+
+    try:
+        return IPv6Address(address)
+    except (AddressValueError, NetmaskValueError):
+        pass
+
+    raise ValueError('%r does not appear to be an IPv4 or IPv6 address' %
+                     address)
+
+
+def IPNetwork(address, version=None, strict=False):
+    """Take an IP string/int and return an object of the correct type.
+
+    Args:
+        address: A string or integer, the IP address.  Either IPv4 or
+          IPv6 addresses may be supplied; integers less than 2**32 will
+          be considered to be IPv4 by default.
+        version: An Integer, if set, don't try to automatically
+          determine what the IP address type is. important for things
+          like IPNetwork(1), which could be IPv4, '0.0.0.1/32', or IPv6,
+          '::1/128'.
+
+    Returns:
+        An IPv4Network or IPv6Network object.
+
+    Raises:
+        ValueError: if the string passed isn't either a v4 or a v6
+          address. Or if a strict network was requested and a strict
+          network wasn't given.
+
+    """
+    if version:
+        if version == 4:
+            return IPv4Network(address, strict)
+        elif version == 6:
+            return IPv6Network(address, strict)
+
+    try:
+        return IPv4Network(address, strict)
+    except (AddressValueError, NetmaskValueError):
+        pass
+
+    try:
+        return IPv6Network(address, strict)
+    except (AddressValueError, NetmaskValueError):
+        pass
+
+    raise ValueError('%r does not appear to be an IPv4 or IPv6 network' %
+                     address)
+
+
+def v4_int_to_packed(address):
+    """The binary representation of this address.
+
+    Args:
+        address: An integer representation of an IPv4 IP address.
+
+    Returns:
+        The binary representation of this address.
+
+    Raises:
+        ValueError: If the integer is too large to be an IPv4 IP
+          address.
+    """
+    if address > _BaseV4._ALL_ONES:
+        raise ValueError('Address too large for IPv4')
+    return Bytes(struct.pack('!I', address))
+
+
+def v6_int_to_packed(address):
+    """The binary representation of this address.
+
+    Args:
+        address: An integer representation of an IPv4 IP address.
+
+    Returns:
+        The binary representation of this address.
+    """
+    return Bytes(struct.pack('!QQ', address >> 64, address & (2**64 - 1)))
+
+
+def _find_address_range(addresses):
+    """Find a sequence of addresses.
+
+    Args:
+        addresses: a list of IPv4 or IPv6 addresses.
+
+    Returns:
+        A tuple containing the first and last IP addresses in the sequence.
+
+    """
+    first = last = addresses[0]
+    for ip in addresses[1:]:
+        if ip._ip == last._ip + 1:
+            last = ip
+        else:
+            break
+    return (first, last)
+
+def _get_prefix_length(number1, number2, bits):
+    """Get the number of leading bits that are same for two numbers.
+
+    Args:
+        number1: an integer.
+        number2: another integer.
+        bits: the maximum number of bits to compare.
+
+    Returns:
+        The number of leading bits that are the same for two numbers.
+
+    """
+    for i in range(bits):
+        if number1 >> i == number2 >> i:
+            return bits - i
+    return 0
+
+def _count_righthand_zero_bits(number, bits):
+    """Count the number of zero bits on the right hand side.
+
+    Args:
+        number: an integer.
+        bits: maximum number of bits to count.
+
+    Returns:
+        The number of zero bits on the right hand side of the number.
+
+    """
+    if number == 0:
+        return bits
+    for i in range(bits):
+        if (number >> i) % 2:
+            return i
+
+def summarize_address_range(first, last):
+    """Summarize a network range given the first and last IP addresses.
+
+    Example:
+        >>> summarize_address_range(IPv4Address('1.1.1.0'),
+            IPv4Address('1.1.1.130'))
+        [IPv4Network('1.1.1.0/25'), IPv4Network('1.1.1.128/31'),
+        IPv4Network('1.1.1.130/32')]
+
+    Args:
+        first: the first IPv4Address or IPv6Address in the range.
+        last: the last IPv4Address or IPv6Address in the range.
+
+    Returns:
+        The address range collapsed to a list of IPv4Network's or
+        IPv6Network's.
+
+    Raise:
+        TypeError:
+            If the first and last objects are not IP addresses.
+            If the first and last objects are not the same version.
+        ValueError:
+            If the last object is not greater than the first.
+            If the version is not 4 or 6.
+
+    """
+    if not (isinstance(first, _BaseIP) and isinstance(last, _BaseIP)):
+        raise TypeError('first and last must be IP addresses, not networks')
+    if first.version != last.version:
+        raise TypeError("%s and %s are not of the same version" % (
+                str(first), str(last)))
+    if first > last:
+        raise ValueError('last IP address must be greater than first')
+
+    networks = []
+
+    if first.version == 4:
+        ip = IPv4Network
+    elif first.version == 6:
+        ip = IPv6Network
+    else:
+        raise ValueError('unknown IP version')
+
+    ip_bits = first._max_prefixlen
+    first_int = first._ip
+    last_int = last._ip
+    while first_int <= last_int:
+        nbits = _count_righthand_zero_bits(first_int, ip_bits)
+        current = None
+        while nbits >= 0:
+            addend = 2**nbits - 1
+            current = first_int + addend
+            nbits -= 1
+            if current <= last_int:
+                break
+        prefix = _get_prefix_length(first_int, current, ip_bits)
+        net = ip('%s/%d' % (str(first), prefix))
+        networks.append(net)
+        if current == ip._ALL_ONES:
+            break
+        first_int = current + 1
+        first = IPAddress(first_int, version=first._version)
+    return networks
+
+def _collapse_address_list_recursive(addresses):
+    """Loops through the addresses, collapsing concurrent netblocks.
+
+    Example:
+
+        ip1 = IPv4Network('1.1.0.0/24')
+        ip2 = IPv4Network('1.1.1.0/24')
+        ip3 = IPv4Network('1.1.2.0/24')
+        ip4 = IPv4Network('1.1.3.0/24')
+        ip5 = IPv4Network('1.1.4.0/24')
+        ip6 = IPv4Network('1.1.0.1/22')
+
+        _collapse_address_list_recursive([ip1, ip2, ip3, ip4, ip5, ip6]) ->
+          [IPv4Network('1.1.0.0/22'), IPv4Network('1.1.4.0/24')]
+
+        This shouldn't be called directly; it is called via
+          collapse_address_list([]).
+
+    Args:
+        addresses: A list of IPv4Network's or IPv6Network's
+
+    Returns:
+        A list of IPv4Network's or IPv6Network's depending on what we were
+        passed.
+
+    """
+    ret_array = []
+    optimized = False
+
+    for cur_addr in addresses:
+        if not ret_array:
+            ret_array.append(cur_addr)
+            continue
+        if cur_addr in ret_array[-1]:
+            optimized = True
+        elif cur_addr == ret_array[-1].supernet().subnet()[1]:
+            ret_array.append(ret_array.pop().supernet())
+            optimized = True
+        else:
+            ret_array.append(cur_addr)
+
+    if optimized:
+        return _collapse_address_list_recursive(ret_array)
+
+    return ret_array
+
+
+def collapse_address_list(addresses):
+    """Collapse a list of IP objects.
+
+    Example:
+        collapse_address_list([IPv4('1.1.0.0/24'), IPv4('1.1.1.0/24')]) ->
+          [IPv4('1.1.0.0/23')]
+
+    Args:
+        addresses: A list of IPv4Network or IPv6Network objects.
+
+    Returns:
+        A list of IPv4Network or IPv6Network objects depending on what we
+        were passed.
+
+    Raises:
+        TypeError: If passed a list of mixed version objects.
+
+    """
+    i = 0
+    addrs = []
+    ips = []
+    nets = []
+
+    # split IP addresses and networks
+    for ip in addresses:
+        if isinstance(ip, _BaseIP):
+            if ips and ips[-1]._version != ip._version:
+                raise TypeError("%s and %s are not of the same version" % (
+                        str(ip), str(ips[-1])))
+            ips.append(ip)
+        elif ip._prefixlen == ip._max_prefixlen:
+            if ips and ips[-1]._version != ip._version:
+                raise TypeError("%s and %s are not of the same version" % (
+                        str(ip), str(ips[-1])))
+            ips.append(ip.ip)
+        else:
+            if nets and nets[-1]._version != ip._version:
+                raise TypeError("%s and %s are not of the same version" % (
+                        str(ip), str(ips[-1])))
+            nets.append(ip)
+
+    # sort and dedup
+    ips = sorted(set(ips))
+    nets = sorted(set(nets))
+
+    while i < len(ips):
+        (first, last) = _find_address_range(ips[i:])
+        i = ips.index(last) + 1
+        addrs.extend(summarize_address_range(first, last))
+
+    return _collapse_address_list_recursive(sorted(
+        addrs + nets, key=_BaseNet._get_networks_key))
+
+# backwards compatibility
+CollapseAddrList = collapse_address_list
+
+# We need to distinguish between the string and packed-bytes representations
+# of an IP address.  For example, b'0::1' is the IPv4 address 48.58.58.49,
+# while '0::1' is an IPv6 address.
+#
+# In Python 3, the native 'bytes' type already provides this functionality,
+# so we use it directly.  For earlier implementations where bytes is not a
+# distinct type, we create a subclass of str to serve as a tag.
+#
+# Usage example (Python 2):
+#   ip = ipaddr.IPAddress(ipaddr.Bytes('xxxx'))
+#
+# Usage example (Python 3):
+#   ip = ipaddr.IPAddress(b'xxxx')
+try:
+    if bytes is str:
+        raise TypeError("bytes is not a distinct type")
+    Bytes = bytes
+except (NameError, TypeError):
+    class Bytes(str):
+        def __repr__(self):
+            return 'Bytes(%s)' % str.__repr__(self)
+
+def get_mixed_type_key(obj):
+    """Return a key suitable for sorting between networks and addresses.
+
+    Address and Network objects are not sortable by default; they're
+    fundamentally different so the expression
+
+        IPv4Address('1.1.1.1') <= IPv4Network('1.1.1.1/24')
+
+    doesn't make any sense.  There are some times however, where you may wish
+    to have ipaddr sort these for you anyway. If you need to do this, you
+    can use this function as the key= argument to sorted().
+
+    Args:
+      obj: either a Network or Address object.
+    Returns:
+      appropriate key.
+
+    """
+    if isinstance(obj, _BaseNet):
+        return obj._get_networks_key()
+    elif isinstance(obj, _BaseIP):
+        return obj._get_address_key()
+    return NotImplemented
+
+class _IPAddrBase(object):
+
+    """The mother class."""
+
+    def __index__(self):
+        return self._ip
+
+    def __int__(self):
+        return self._ip
+
+    def __hex__(self):
+        return hex(self._ip)
+
+    @property
+    def exploded(self):
+        """Return the longhand version of the IP address as a string."""
+        return self._explode_shorthand_ip_string()
+
+    @property
+    def compressed(self):
+        """Return the shorthand version of the IP address as a string."""
+        return str(self)
+
+
+class _BaseIP(_IPAddrBase):
+
+    """A generic IP object.
+
+    This IP class contains the version independent methods which are
+    used by single IP addresses.
+
+    """
+
+    def __eq__(self, other):
+        try:
+            return (self._ip == other._ip
+                    and self._version == other._version)
+        except AttributeError:
+            return NotImplemented
+
+    def __ne__(self, other):
+        eq = self.__eq__(other)
+        if eq is NotImplemented:
+            return NotImplemented
+        return not eq
+
+    def __le__(self, other):
+        gt = self.__gt__(other)
+        if gt is NotImplemented:
+            return NotImplemented
+        return not gt
+
+    def __ge__(self, other):
+        lt = self.__lt__(other)
+        if lt is NotImplemented:
+            return NotImplemented
+        return not lt
+
+    def __lt__(self, other):
+        if self._version != other._version:
+            raise TypeError('%s and %s are not of the same version' % (
+                    str(self), str(other)))
+        if not isinstance(other, _BaseIP):
+            raise TypeError('%s and %s are not of the same type' % (
+                    str(self), str(other)))
+        if self._ip != other._ip:
+            return self._ip < other._ip
+        return False
+
+    def __gt__(self, other):
+        if self._version != other._version:
+            raise TypeError('%s and %s are not of the same version' % (
+                    str(self), str(other)))
+        if not isinstance(other, _BaseIP):
+            raise TypeError('%s and %s are not of the same type' % (
+                    str(self), str(other)))
+        if self._ip != other._ip:
+            return self._ip > other._ip
+        return False
+
+    # Shorthand for Integer addition and subtraction. This is not
+    # meant to ever support addition/subtraction of addresses.
+    def __add__(self, other):
+        if not isinstance(other, int):
+            return NotImplemented
+        return IPAddress(int(self) + other, version=self._version)
+
+    def __sub__(self, other):
+        if not isinstance(other, int):
+            return NotImplemented
+        return IPAddress(int(self) - other, version=self._version)
+
+    def __repr__(self):
+        return '%s(%r)' % (self.__class__.__name__, str(self))
+
+    def __str__(self):
+        return  '%s' % self._string_from_ip_int(self._ip)
+
+    def __hash__(self):
+        return hash(hex(long(self._ip)))
+
+    def _get_address_key(self):
+        return (self._version, self)
+
+    @property
+    def version(self):
+        raise NotImplementedError('BaseIP has no version')
+
+
+class _BaseNet(_IPAddrBase):
+
+    """A generic IP object.
+
+    This IP class contains the version independent methods which are
+    used by networks.
+
+    """
+
+    def __init__(self, address):
+        self._cache = {}
+
+    def __repr__(self):
+        return '%s(%r)' % (self.__class__.__name__, str(self))
+
+    def iterhosts(self):
+        """Generate Iterator over usable hosts in a network.
+
+           This is like __iter__ except it doesn't return the network
+           or broadcast addresses.
+
+        """
+        cur = int(self.network) + 1
+        bcast = int(self.broadcast) - 1
+        while cur <= bcast:
+            cur += 1
+            yield IPAddress(cur - 1, version=self._version)
+
+    def __iter__(self):
+        cur = int(self.network)
+        bcast = int(self.broadcast)
+        while cur <= bcast:
+            cur += 1
+            yield IPAddress(cur - 1, version=self._version)
+
+    def __getitem__(self, n):
+        network = int(self.network)
+        broadcast = int(self.broadcast)
+        if n >= 0:
+            if network + n > broadcast:
+                raise IndexError
+            return IPAddress(network + n, version=self._version)
+        else:
+            n += 1
+            if broadcast + n < network:
+                raise IndexError
+            return IPAddress(broadcast + n, version=self._version)
+
+    def __lt__(self, other):
+        if self._version != other._version:
+            raise TypeError('%s and %s are not of the same version' % (
+                    str(self), str(other)))
+        if not isinstance(other, _BaseNet):
+            raise TypeError('%s and %s are not of the same type' % (
+                    str(self), str(other)))
+        if self.network != other.network:
+            return self.network < other.network
+        if self.netmask != other.netmask:
+            return self.netmask < other.netmask
+        return False
+
+    def __gt__(self, other):
+        if self._version != other._version:
+            raise TypeError('%s and %s are not of the same version' % (
+                    str(self), str(other)))
+        if not isinstance(other, _BaseNet):
+            raise TypeError('%s and %s are not of the same type' % (
+                    str(self), str(other)))
+        if self.network != other.network:
+            return self.network > other.network
+        if self.netmask != other.netmask:
+            return self.netmask > other.netmask
+        return False
+
+    def __le__(self, other):
+        gt = self.__gt__(other)
+        if gt is NotImplemented:
+            return NotImplemented
+        return not gt
+
+    def __ge__(self, other):
+        lt = self.__lt__(other)
+        if lt is NotImplemented:
+            return NotImplemented
+        return not lt
+
+    def __eq__(self, other):
+        try:
+            return (self._version == other._version
+                    and self.network == other.network
+                    and int(self.netmask) == int(other.netmask))
+        except AttributeError:
+            if isinstance(other, _BaseIP):
+                return (self._version == other._version
+                        and self._ip == other._ip)
+
+    def __ne__(self, other):
+        eq = self.__eq__(other)
+        if eq is NotImplemented:
+            return NotImplemented
+        return not eq
+
+    def __str__(self):
+        return  '%s/%s' % (str(self.ip),
+                           str(self._prefixlen))
+
+    def __hash__(self):
+        return hash(int(self.network) ^ int(self.netmask))
+
+    def __contains__(self, other):
+        # always false if one is v4 and the other is v6.
+        if self._version != other._version:
+          return False
+        # dealing with another network.
+        if isinstance(other, _BaseNet):
+            return (self.network <= other.network and
+                    self.broadcast >= other.broadcast)
+        # dealing with another address
+        else:
+            return (int(self.network) <= int(other._ip) <=
+                    int(self.broadcast))
+
+    def overlaps(self, other):
+        """Tell if self is partly contained in other."""
+        return self.network in other or self.broadcast in other or (
+            other.network in self or other.broadcast in self)
+
+    @property
+    def network(self):
+        x = self._cache.get('network')
+        if x is None:
+            x = IPAddress(self._ip & int(self.netmask), version=self._version)
+            self._cache['network'] = x
+        return x
+
+    @property
+    def broadcast(self):
+        x = self._cache.get('broadcast')
+        if x is None:
+            x = IPAddress(self._ip | int(self.hostmask), version=self._version)
+            self._cache['broadcast'] = x
+        return x
+
+    @property
+    def hostmask(self):
+        x = self._cache.get('hostmask')
+        if x is None:
+            x = IPAddress(int(self.netmask) ^ self._ALL_ONES,
+                          version=self._version)
+            self._cache['hostmask'] = x
+        return x
+
+    @property
+    def with_prefixlen(self):
+        return '%s/%d' % (str(self.ip), self._prefixlen)
+
+    @property
+    def with_netmask(self):
+        return '%s/%s' % (str(self.ip), str(self.netmask))
+
+    @property
+    def with_hostmask(self):
+        return '%s/%s' % (str(self.ip), str(self.hostmask))
+
+    @property
+    def numhosts(self):
+        """Number of hosts in the current subnet."""
+        return int(self.broadcast) - int(self.network) + 1
+
+    @property
+    def version(self):
+        raise NotImplementedError('BaseNet has no version')
+
+    @property
+    def prefixlen(self):
+        return self._prefixlen
+
+    def address_exclude(self, other):
+        """Remove an address from a larger block.
+
+        For example:
+
+            addr1 = IPNetwork('10.1.1.0/24')
+            addr2 = IPNetwork('10.1.1.0/26')
+            addr1.address_exclude(addr2) =
+                [IPNetwork('10.1.1.64/26'), IPNetwork('10.1.1.128/25')]
+
+        or IPv6:
+
+            addr1 = IPNetwork('::1/32')
+            addr2 = IPNetwork('::1/128')
+            addr1.address_exclude(addr2) = [IPNetwork('::0/128'),
+                IPNetwork('::2/127'),
+                IPNetwork('::4/126'),
+                IPNetwork('::8/125'),
+                ...
+                IPNetwork('0:0:8000::/33')]
+
+        Args:
+            other: An IPvXNetwork object of the same type.
+
+        Returns:
+            A sorted list of IPvXNetwork objects addresses which is self
+            minus other.
+
+        Raises:
+            TypeError: If self and other are of difffering address
+              versions, or if other is not a network object.
+            ValueError: If other is not completely contained by self.
+
+        """
+        if not self._version == other._version:
+            raise TypeError("%s and %s are not of the same version" % (
+                str(self), str(other)))
+
+        if not isinstance(other, _BaseNet):
+            raise TypeError("%s is not a network object" % str(other))
+
+        if other not in self:
+            raise ValueError('%s not contained in %s' % (str(other),
+                                                         str(self)))
+        if other == self:
+            return []
+
+        ret_addrs = []
+
+        # Make sure we're comparing the network of other.
+        other = IPNetwork('%s/%s' % (str(other.network), str(other.prefixlen)),
+                   version=other._version)
+
+        s1, s2 = self.subnet()
+        while s1 != other and s2 != other:
+            if other in s1:
+                ret_addrs.append(s2)
+                s1, s2 = s1.subnet()
+            elif other in s2:
+                ret_addrs.append(s1)
+                s1, s2 = s2.subnet()
+            else:
+                # If we got here, there's a bug somewhere.
+                assert True == False, ('Error performing exclusion: '
+                                       's1: %s s2: %s other: %s' %
+                                       (str(s1), str(s2), str(other)))
+        if s1 == other:
+            ret_addrs.append(s2)
+        elif s2 == other:
+            ret_addrs.append(s1)
+        else:
+            # If we got here, there's a bug somewhere.
+            assert True == False, ('Error performing exclusion: '
+                                   's1: %s s2: %s other: %s' %
+                                   (str(s1), str(s2), str(other)))
+
+        return sorted(ret_addrs, key=_BaseNet._get_networks_key)
+
+    def compare_networks(self, other):
+        """Compare two IP objects.
+
+        This is only concerned about the comparison of the integer
+        representation of the network addresses.  This means that the
+        host bits aren't considered at all in this method.  If you want
+        to compare host bits, you can easily enough do a
+        'HostA._ip < HostB._ip'
+
+        Args:
+            other: An IP object.
+
+        Returns:
+            If the IP versions of self and other are the same, returns:
+
+            -1 if self < other:
+              eg: IPv4('1.1.1.0/24') < IPv4('1.1.2.0/24')
+              IPv6('1080::200C:417A') < IPv6('1080::200B:417B')
+            0 if self == other
+              eg: IPv4('1.1.1.1/24') == IPv4('1.1.1.2/24')
+              IPv6('1080::200C:417A/96') == IPv6('1080::200C:417B/96')
+            1 if self > other
+              eg: IPv4('1.1.1.0/24') > IPv4('1.1.0.0/24')
+              IPv6('1080::1:200C:417A/112') >
+              IPv6('1080::0:200C:417A/112')
+
+            If the IP versions of self and other are different, returns:
+
+            -1 if self._version < other._version
+              eg: IPv4('10.0.0.1/24') < IPv6('::1/128')
+            1 if self._version > other._version
+              eg: IPv6('::1/128') > IPv4('255.255.255.0/24')
+
+        """
+        if self._version < other._version:
+            return -1
+        if self._version > other._version:
+            return 1
+        # self._version == other._version below here:
+        if self.network < other.network:
+            return -1
+        if self.network > other.network:
+            return 1
+        # self.network == other.network below here:
+        if self.netmask < other.netmask:
+            return -1
+        if self.netmask > other.netmask:
+            return 1
+        # self.network == other.network and self.netmask == other.netmask
+        return 0
+
+    def _get_networks_key(self):
+        """Network-only key function.
+
+        Returns an object that identifies this address' network and
+        netmask. This function is a suitable "key" argument for sorted()
+        and list.sort().
+
+        """
+        return (self._version, self.network, self.netmask)
+
+    def _ip_int_from_prefix(self, prefixlen=None):
+        """Turn the prefix length netmask into a int for comparison.
+
+        Args:
+            prefixlen: An integer, the prefix length.
+
+        Returns:
+            An integer.
+
+        """
+        if not prefixlen and prefixlen != 0:
+            prefixlen = self._prefixlen
+        return self._ALL_ONES ^ (self._ALL_ONES >> prefixlen)
+
+    def _prefix_from_ip_int(self, ip_int, mask=32):
+        """Return prefix length from the decimal netmask.
+
+        Args:
+            ip_int: An integer, the IP address.
+            mask: The netmask.  Defaults to 32.
+
+        Returns:
+            An integer, the prefix length.
+
+        """
+        while mask:
+            if ip_int & 1 == 1:
+                break
+            ip_int >>= 1
+            mask -= 1
+
+        return mask
+
+    def _ip_string_from_prefix(self, prefixlen=None):
+        """Turn a prefix length into a dotted decimal string.
+
+        Args:
+            prefixlen: An integer, the netmask prefix length.
+
+        Returns:
+            A string, the dotted decimal netmask string.
+
+        """
+        if not prefixlen:
+            prefixlen = self._prefixlen
+        return self._string_from_ip_int(self._ip_int_from_prefix(prefixlen))
+
+    def iter_subnets(self, prefixlen_diff=1, new_prefix=None):
+        """The subnets which join to make the current subnet.
+
+        In the case that self contains only one IP
+        (self._prefixlen == 32 for IPv4 or self._prefixlen == 128
+        for IPv6), return a list with just ourself.
+
+        Args:
+            prefixlen_diff: An integer, the amount the prefix length
+              should be increased by. This should not be set if
+              new_prefix is also set.
+            new_prefix: The desired new prefix length. This must be a
+              larger number (smaller prefix) than the existing prefix.
+              This should not be set if prefixlen_diff is also set.
+
+        Returns:
+            An iterator of IPv(4|6) objects.
+
+        Raises:
+            ValueError: The prefixlen_diff is too small or too large.
+                OR
+            prefixlen_diff and new_prefix are both set or new_prefix
+              is a smaller number than the current prefix (smaller
+              number means a larger network)
+
+        """
+        if self._prefixlen == self._max_prefixlen:
+            yield self
+            return
+
+        if new_prefix is not None:
+            if new_prefix < self._prefixlen:
+                raise ValueError('new prefix must be longer')
+            if prefixlen_diff != 1:
+                raise ValueError('cannot set prefixlen_diff and new_prefix')
+            prefixlen_diff = new_prefix - self._prefixlen
+
+        if prefixlen_diff < 0:
+            raise ValueError('prefix length diff must be > 0')
+        new_prefixlen = self._prefixlen + prefixlen_diff
+
+        if not self._is_valid_netmask(str(new_prefixlen)):
+            raise ValueError(
+                'prefix length diff %d is invalid for netblock %s' % (
+                    new_prefixlen, str(self)))
+
+        first = IPNetwork('%s/%s' % (str(self.network),
+                                     str(self._prefixlen + prefixlen_diff)),
+                         version=self._version)
+
+        yield first
+        current = first
+        while True:
+            broadcast = current.broadcast
+            if broadcast == self.broadcast:
+                return
+            new_addr = IPAddress(int(broadcast) + 1, version=self._version)
+            current = IPNetwork('%s/%s' % (str(new_addr), str(new_prefixlen)),
+                                version=self._version)
+
+            yield current
+
+    def masked(self):
+        """Return the network object with the host bits masked out."""
+        return IPNetwork('%s/%d' % (self.network, self._prefixlen),
+                         version=self._version)
+
+    def subnet(self, prefixlen_diff=1, new_prefix=None):
+        """Return a list of subnets, rather than an iterator."""
+        return list(self.iter_subnets(prefixlen_diff, new_prefix))
+
+    def supernet(self, prefixlen_diff=1, new_prefix=None):
+        """The supernet containing the current network.
+
+        Args:
+            prefixlen_diff: An integer, the amount the prefix length of
+              the network should be decreased by.  For example, given a
+              /24 network and a prefixlen_diff of 3, a supernet with a
+              /21 netmask is returned.
+
+        Returns:
+            An IPv4 network object.
+
+        Raises:
+            ValueError: If self.prefixlen - prefixlen_diff < 0. I.e., you have a
+              negative prefix length.
+                OR
+            If prefixlen_diff and new_prefix are both set or new_prefix is a
+              larger number than the current prefix (larger number means a
+              smaller network)
+
+        """
+        if self._prefixlen == 0:
+            return self
+
+        if new_prefix is not None:
+            if new_prefix > self._prefixlen:
+                raise ValueError('new prefix must be shorter')
+            if prefixlen_diff != 1:
+                raise ValueError('cannot set prefixlen_diff and new_prefix')
+            prefixlen_diff = self._prefixlen - new_prefix
+
+
+        if self.prefixlen - prefixlen_diff < 0:
+            raise ValueError(
+                'current prefixlen is %d, cannot have a prefixlen_diff of %d' %
+                (self.prefixlen, prefixlen_diff))
+        return IPNetwork('%s/%s' % (str(self.network),
+                                    str(self.prefixlen - prefixlen_diff)),
+                         version=self._version)
+
+    # backwards compatibility
+    Subnet = subnet
+    Supernet = supernet
+    AddressExclude = address_exclude
+    CompareNetworks = compare_networks
+    Contains = __contains__
+
+
+class _BaseV4(object):
+
+    """Base IPv4 object.
+
+    The following methods are used by IPv4 objects in both single IP
+    addresses and networks.
+
+    """
+
+    # Equivalent to 255.255.255.255 or 32 bits of 1's.
+    _ALL_ONES = (2**IPV4LENGTH) - 1
+    _DECIMAL_DIGITS = frozenset('0123456789')
+
+    def __init__(self, address):
+        self._version = 4
+        self._max_prefixlen = IPV4LENGTH
+
+    def _explode_shorthand_ip_string(self):
+        return str(self)
+
+    def _ip_int_from_string(self, ip_str):
+        """Turn the given IP string into an integer for comparison.
+
+        Args:
+            ip_str: A string, the IP ip_str.
+
+        Returns:
+            The IP ip_str as an integer.
+
+        Raises:
+            AddressValueError: if ip_str isn't a valid IPv4 Address.
+
+        """
+        octets = ip_str.split('.')
+        if len(octets) != 4:
+            raise AddressValueError(ip_str)
+
+        packed_ip = 0
+        for oc in octets:
+            try:
+                packed_ip = (packed_ip << 8) | self._parse_octet(oc)
+            except ValueError:
+                raise AddressValueError(ip_str)
+        return packed_ip
+
+    def _parse_octet(self, octet_str):
+        """Convert a decimal octet into an integer.
+
+        Args:
+            octet_str: A string, the number to parse.
+
+        Returns:
+            The octet as an integer.
+
+        Raises:
+            ValueError: if the octet isn't strictly a decimal from [0..255].
+
+        """
+        # Whitelist the characters, since int() allows a lot of bizarre stuff.
+        if not self._DECIMAL_DIGITS.issuperset(octet_str):
+            raise ValueError
+        octet_int = int(octet_str, 10)
+        # Disallow leading zeroes, because no clear standard exists on
+        # whether these should be interpreted as decimal or octal.
+        if octet_int > 255 or (octet_str[0] == '0' and len(octet_str) > 1):
+            raise ValueError
+        return octet_int
+
+    def _string_from_ip_int(self, ip_int):
+        """Turns a 32-bit integer into dotted decimal notation.
+
+        Args:
+            ip_int: An integer, the IP address.
+
+        Returns:
+            The IP address as a string in dotted decimal notation.
+
+        """
+        octets = []
+        for _ in xrange(4):
+            octets.insert(0, str(ip_int & 0xFF))
+            ip_int >>= 8
+        return '.'.join(octets)
+
+    @property
+    def max_prefixlen(self):
+        return self._max_prefixlen
+
+    @property
+    def packed(self):
+        """The binary representation of this address."""
+        return v4_int_to_packed(self._ip)
+
+    @property
+    def version(self):
+        return self._version
+
+    @property
+    def is_reserved(self):
+       """Test if the address is otherwise IETF reserved.
+
+        Returns:
+            A boolean, True if the address is within the
+            reserved IPv4 Network range.
+
+       """
+       return self in IPv4Network('240.0.0.0/4')
+
+    @property
+    def is_private(self):
+        """Test if this address is allocated for private networks.
+
+        Returns:
+            A boolean, True if the address is reserved per RFC 1918.
+
+        """
+        return (self in IPv4Network('10.0.0.0/8') or
+                self in IPv4Network('172.16.0.0/12') or
+                self in IPv4Network('192.168.0.0/16'))
+
+    @property
+    def is_multicast(self):
+        """Test if the address is reserved for multicast use.
+
+        Returns:
+            A boolean, True if the address is multicast.
+            See RFC 3171 for details.
+
+        """
+        return self in IPv4Network('224.0.0.0/4')
+
+    @property
+    def is_unspecified(self):
+        """Test if the address is unspecified.
+
+        Returns:
+            A boolean, True if this is the unspecified address as defined in
+            RFC 5735 3.
+
+        """
+        return self in IPv4Network('0.0.0.0')
+
+    @property
+    def is_loopback(self):
+        """Test if the address is a loopback address.
+
+        Returns:
+            A boolean, True if the address is a loopback per RFC 3330.
+
+        """
+        return self in IPv4Network('127.0.0.0/8')
+
+    @property
+    def is_link_local(self):
+        """Test if the address is reserved for link-local.
+
+        Returns:
+            A boolean, True if the address is link-local per RFC 3927.
+
+        """
+        return self in IPv4Network('169.254.0.0/16')
+
+
+class IPv4Address(_BaseV4, _BaseIP):
+
+    """Represent and manipulate single IPv4 Addresses."""
+
+    def __init__(self, address):
+
+        """
+        Args:
+            address: A string or integer representing the IP
+              '192.168.1.1'
+
+              Additionally, an integer can be passed, so
+              IPv4Address('192.168.1.1') == IPv4Address(3232235777).
+              or, more generally
+              IPv4Address(int(IPv4Address('192.168.1.1'))) ==
+                IPv4Address('192.168.1.1')
+
+        Raises:
+            AddressValueError: If ipaddr isn't a valid IPv4 address.
+
+        """
+        _BaseV4.__init__(self, address)
+
+        # Efficient constructor from integer.
+        if isinstance(address, (int, long)):
+            self._ip = address
+            if address < 0 or address > self._ALL_ONES:
+                raise AddressValueError(address)
+            return
+
+        # Constructing from a packed address
+        if isinstance(address, Bytes):
+            try:
+                self._ip, = struct.unpack('!I', address)
+            except struct.error:
+                raise AddressValueError(address)  # Wrong length.
+            return
+
+        # Assume input argument to be string or any object representation
+        # which converts into a formatted IP string.
+        addr_str = str(address)
+        self._ip = self._ip_int_from_string(addr_str)
+
+
+class IPv4Network(_BaseV4, _BaseNet):
+
+    """This class represents and manipulates 32-bit IPv4 networks.
+
+    Attributes: [examples for IPv4Network('1.2.3.4/27')]
+        ._ip: 16909060
+        .ip: IPv4Address('1.2.3.4')
+        .network: IPv4Address('1.2.3.0')
+        .hostmask: IPv4Address('0.0.0.31')
+        .broadcast: IPv4Address('1.2.3.31')
+        .netmask: IPv4Address('255.255.255.224')
+        .prefixlen: 27
+
+    """
+
+    # the valid octets for host and netmasks. only useful for IPv4.
+    _valid_mask_octets = set((255, 254, 252, 248, 240, 224, 192, 128, 0))
+
+    def __init__(self, address, strict=False):
+        """Instantiate a new IPv4 network object.
+
+        Args:
+            address: A string or integer representing the IP [& network].
+              '192.168.1.1/24'
+              '192.168.1.1/255.255.255.0'
+              '192.168.1.1/0.0.0.255'
+              are all functionally the same in IPv4. Similarly,
+              '192.168.1.1'
+              '192.168.1.1/255.255.255.255'
+              '192.168.1.1/32'
+              are also functionaly equivalent. That is to say, failing to
+              provide a subnetmask will create an object with a mask of /32.
+
+              If the mask (portion after the / in the argument) is given in
+              dotted quad form, it is treated as a netmask if it starts with a
+              non-zero field (e.g. /255.0.0.0 == /8) and as a hostmask if it
+              starts with a zero field (e.g. 0.255.255.255 == /8), with the
+              single exception of an all-zero mask which is treated as a
+              netmask == /0. If no mask is given, a default of /32 is used.
+
+              Additionally, an integer can be passed, so
+              IPv4Network('192.168.1.1') == IPv4Network(3232235777).
+              or, more generally
+              IPv4Network(int(IPv4Network('192.168.1.1'))) ==
+                IPv4Network('192.168.1.1')
+
+            strict: A boolean. If true, ensure that we have been passed
+              A true network address, eg, 192.168.1.0/24 and not an
+              IP address on a network, eg, 192.168.1.1/24.
+
+        Raises:
+            AddressValueError: If ipaddr isn't a valid IPv4 address.
+            NetmaskValueError: If the netmask isn't valid for
+              an IPv4 address.
+            ValueError: If strict was True and a network address was not
+              supplied.
+
+        """
+        _BaseNet.__init__(self, address)
+        _BaseV4.__init__(self, address)
+
+        # Constructing from an integer or packed bytes.
+        if isinstance(address, (int, long, Bytes)):
+            self.ip = IPv4Address(address)
+            self._ip = self.ip._ip
+            self._prefixlen = self._max_prefixlen
+            self.netmask = IPv4Address(self._ALL_ONES)
+            return
+
+        # Assume input argument to be string or any object representation
+        # which converts into a formatted IP prefix string.
+        addr = str(address).split('/')
+
+        if len(addr) > 2:
+            raise AddressValueError(address)
+
+        self._ip = self._ip_int_from_string(addr[0])
+        self.ip = IPv4Address(self._ip)
+
+        if len(addr) == 2:
+            mask = addr[1].split('.')
+            if len(mask) == 4:
+                # We have dotted decimal netmask.
+                if self._is_valid_netmask(addr[1]):
+                    self.netmask = IPv4Address(self._ip_int_from_string(
+                            addr[1]))
+                elif self._is_hostmask(addr[1]):
+                    self.netmask = IPv4Address(
+                        self._ip_int_from_string(addr[1]) ^ self._ALL_ONES)
+                else:
+                    raise NetmaskValueError('%s is not a valid netmask'
+                                                     % addr[1])
+
+                self._prefixlen = self._prefix_from_ip_int(int(self.netmask))
+            else:
+                # We have a netmask in prefix length form.
+                if not self._is_valid_netmask(addr[1]):
+                    raise NetmaskValueError(addr[1])
+                self._prefixlen = int(addr[1])
+                self.netmask = IPv4Address(self._ip_int_from_prefix(
+                    self._prefixlen))
+        else:
+            self._prefixlen = self._max_prefixlen
+            self.netmask = IPv4Address(self._ip_int_from_prefix(
+                self._prefixlen))
+        if strict:
+            if self.ip != self.network:
+                raise ValueError('%s has host bits set' %
+                                 self.ip)
+        if self._prefixlen == (self._max_prefixlen - 1):
+            self.iterhosts = self.__iter__
+
+    def _is_hostmask(self, ip_str):
+        """Test if the IP string is a hostmask (rather than a netmask).
+
+        Args:
+            ip_str: A string, the potential hostmask.
+
+        Returns:
+            A boolean, True if the IP string is a hostmask.
+
+        """
+        bits = ip_str.split('.')
+        try:
+            parts = [int(x) for x in bits if int(x) in self._valid_mask_octets]
+        except ValueError:
+            return False
+        if len(parts) != len(bits):
+            return False
+        if parts[0] < parts[-1]:
+            return True
+        return False
+
+    def _is_valid_netmask(self, netmask):
+        """Verify that the netmask is valid.
+
+        Args:
+            netmask: A string, either a prefix or dotted decimal
+              netmask.
+
+        Returns:
+            A boolean, True if the prefix represents a valid IPv4
+            netmask.
+
+        """
+        mask = netmask.split('.')
+        if len(mask) == 4:
+            if [x for x in mask if int(x) not in self._valid_mask_octets]:
+                return False
+            if [y for idx, y in enumerate(mask) if idx > 0 and
+                y > mask[idx - 1]]:
+                return False
+            return True
+        try:
+            netmask = int(netmask)
+        except ValueError:
+            return False
+        return 0 <= netmask <= self._max_prefixlen
+
+    # backwards compatibility
+    IsRFC1918 = lambda self: self.is_private
+    IsMulticast = lambda self: self.is_multicast
+    IsLoopback = lambda self: self.is_loopback
+    IsLinkLocal = lambda self: self.is_link_local
+
+
+class _BaseV6(object):
+
+    """Base IPv6 object.
+
+    The following methods are used by IPv6 objects in both single IP
+    addresses and networks.
+
+    """
+
+    _ALL_ONES = (2**IPV6LENGTH) - 1
+    _HEXTET_COUNT = 8
+    _HEX_DIGITS = frozenset('0123456789ABCDEFabcdef')
+
+    def __init__(self, address):
+        self._version = 6
+        self._max_prefixlen = IPV6LENGTH
+
+    def _ip_int_from_string(self, ip_str):
+        """Turn an IPv6 ip_str into an integer.
+
+        Args:
+            ip_str: A string, the IPv6 ip_str.
+
+        Returns:
+            A long, the IPv6 ip_str.
+
+        Raises:
+            AddressValueError: if ip_str isn't a valid IPv6 Address.
+
+        """
+        parts = ip_str.split(':')
+
+        # An IPv6 address needs at least 2 colons (3 parts).
+        if len(parts) < 3:
+            raise AddressValueError(ip_str)
+
+        # If the address has an IPv4-style suffix, convert it to hexadecimal.
+        if '.' in parts[-1]:
+            ipv4_int = IPv4Address(parts.pop())._ip
+            parts.append('%x' % ((ipv4_int >> 16) & 0xFFFF))
+            parts.append('%x' % (ipv4_int & 0xFFFF))
+
+        # An IPv6 address can't have more than 8 colons (9 parts).
+        if len(parts) > self._HEXTET_COUNT + 1:
+            raise AddressValueError(ip_str)
+
+        # Disregarding the endpoints, find '::' with nothing in between.
+        # This indicates that a run of zeroes has been skipped.
+        try:
+            skip_index, = (
+                [i for i in xrange(1, len(parts) - 1) if not parts[i]] or
+                [None])
+        except ValueError:
+            # Can't have more than one '::'
+            raise AddressValueError(ip_str)
+
+        # parts_hi is the number of parts to copy from above/before the '::'
+        # parts_lo is the number of parts to copy from below/after the '::'
+        if skip_index is not None:
+            # If we found a '::', then check if it also covers the endpoints.
+            parts_hi = skip_index
+            parts_lo = len(parts) - skip_index - 1
+            if not parts[0]:
+                parts_hi -= 1
+                if parts_hi:
+                    raise AddressValueError(ip_str)  # ^: requires ^::
+            if not parts[-1]:
+                parts_lo -= 1
+                if parts_lo:
+                    raise AddressValueError(ip_str)  # :$ requires ::$
+            parts_skipped = self._HEXTET_COUNT - (parts_hi + parts_lo)
+            if parts_skipped < 1:
+                raise AddressValueError(ip_str)
+        else:
+            # Otherwise, allocate the entire address to parts_hi.  The endpoints
+            # could still be empty, but _parse_hextet() will check for that.
+            if len(parts) != self._HEXTET_COUNT:
+                raise AddressValueError(ip_str)
+            parts_hi = len(parts)
+            parts_lo = 0
+            parts_skipped = 0
+
+        try:
+            # Now, parse the hextets into a 128-bit integer.
+            ip_int = 0L
+            for i in xrange(parts_hi):
+                ip_int <<= 16
+                ip_int |= self._parse_hextet(parts[i])
+            ip_int <<= 16 * parts_skipped
+            for i in xrange(-parts_lo, 0):
+                ip_int <<= 16
+                ip_int |= self._parse_hextet(parts[i])
+            return ip_int
+        except ValueError:
+            raise AddressValueError(ip_str)
+
+    def _parse_hextet(self, hextet_str):
+        """Convert an IPv6 hextet string into an integer.
+
+        Args:
+            hextet_str: A string, the number to parse.
+
+        Returns:
+            The hextet as an integer.
+
+        Raises:
+            ValueError: if the input isn't strictly a hex number from [0..FFFF].
+
+        """
+        # Whitelist the characters, since int() allows a lot of bizarre stuff.
+        if not self._HEX_DIGITS.issuperset(hextet_str):
+            raise ValueError
+        hextet_int = int(hextet_str, 16)
+        if hextet_int > 0xFFFF:
+            raise ValueError
+        return hextet_int
+
+    def _compress_hextets(self, hextets):
+        """Compresses a list of hextets.
+
+        Compresses a list of strings, replacing the longest continuous
+        sequence of "0" in the list with "" and adding empty strings at
+        the beginning or at the end of the string such that subsequently
+        calling ":".join(hextets) will produce the compressed version of
+        the IPv6 address.
+
+        Args:
+            hextets: A list of strings, the hextets to compress.
+
+        Returns:
+            A list of strings.
+
+        """
+        best_doublecolon_start = -1
+        best_doublecolon_len = 0
+        doublecolon_start = -1
+        doublecolon_len = 0
+        for index in range(len(hextets)):
+            if hextets[index] == '0':
+                doublecolon_len += 1
+                if doublecolon_start == -1:
+                    # Start of a sequence of zeros.
+                    doublecolon_start = index
+                if doublecolon_len > best_doublecolon_len:
+                    # This is the longest sequence of zeros so far.
+                    best_doublecolon_len = doublecolon_len
+                    best_doublecolon_start = doublecolon_start
+            else:
+                doublecolon_len = 0
+                doublecolon_start = -1
+
+        if best_doublecolon_len > 1:
+            best_doublecolon_end = (best_doublecolon_start +
+                                    best_doublecolon_len)
+            # For zeros at the end of the address.
+            if best_doublecolon_end == len(hextets):
+                hextets += ['']
+            hextets[best_doublecolon_start:best_doublecolon_end] = ['']
+            # For zeros at the beginning of the address.
+            if best_doublecolon_start == 0:
+                hextets = [''] + hextets
+
+        return hextets
+
+    def _string_from_ip_int(self, ip_int=None):
+        """Turns a 128-bit integer into hexadecimal notation.
+
+        Args:
+            ip_int: An integer, the IP address.
+
+        Returns:
+            A string, the hexadecimal representation of the address.
+
+        Raises:
+            ValueError: The address is bigger than 128 bits of all ones.
+
+        """
+        if not ip_int and ip_int != 0:
+            ip_int = int(self._ip)
+
+        if ip_int > self._ALL_ONES:
+            raise ValueError('IPv6 address is too large')
+
+        hex_str = '%032x' % ip_int
+        hextets = []
+        for x in range(0, 32, 4):
+            hextets.append('%x' % int(hex_str[x:x+4], 16))
+
+        hextets = self._compress_hextets(hextets)
+        return ':'.join(hextets)
+
+    def _explode_shorthand_ip_string(self):
+        """Expand a shortened IPv6 address.
+
+        Args:
+            ip_str: A string, the IPv6 address.
+
+        Returns:
+            A string, the expanded IPv6 address.
+
+        """
+        if isinstance(self, _BaseNet):
+            ip_str = str(self.ip)
+        else:
+            ip_str = str(self)
+
+        ip_int = self._ip_int_from_string(ip_str)
+        parts = []
+        for i in xrange(self._HEXTET_COUNT):
+            parts.append('%04x' % (ip_int & 0xFFFF))
+            ip_int >>= 16
+        parts.reverse()
+        if isinstance(self, _BaseNet):
+            return '%s/%d' % (':'.join(parts), self.prefixlen)
+        return ':'.join(parts)
+
+    @property
+    def max_prefixlen(self):
+        return self._max_prefixlen
+
+    @property
+    def packed(self):
+        """The binary representation of this address."""
+        return v6_int_to_packed(self._ip)
+
+    @property
+    def version(self):
+        return self._version
+
+    @property
+    def is_multicast(self):
+        """Test if the address is reserved for multicast use.
+
+        Returns:
+            A boolean, True if the address is a multicast address.
+            See RFC 2373 2.7 for details.
+
+        """
+        return self in IPv6Network('ff00::/8')
+
+    @property
+    def is_reserved(self):
+        """Test if the address is otherwise IETF reserved.
+
+        Returns:
+            A boolean, True if the address is within one of the
+            reserved IPv6 Network ranges.
+
+        """
+        return (self in IPv6Network('::/8') or
+                self in IPv6Network('100::/8') or
+                self in IPv6Network('200::/7') or
+                self in IPv6Network('400::/6') or
+                self in IPv6Network('800::/5') or
+                self in IPv6Network('1000::/4') or
+                self in IPv6Network('4000::/3') or
+                self in IPv6Network('6000::/3') or
+                self in IPv6Network('8000::/3') or
+                self in IPv6Network('A000::/3') or
+                self in IPv6Network('C000::/3') or
+                self in IPv6Network('E000::/4') or
+                self in IPv6Network('F000::/5') or
+                self in IPv6Network('F800::/6') or
+                self in IPv6Network('FE00::/9'))
+
+    @property
+    def is_unspecified(self):
+        """Test if the address is unspecified.
+
+        Returns:
+            A boolean, True if this is the unspecified address as defined in
+            RFC 2373 2.5.2.
+
+        """
+        return self._ip == 0 and getattr(self, '_prefixlen', 128) == 128
+
+    @property
+    def is_loopback(self):
+        """Test if the address is a loopback address.
+
+        Returns:
+            A boolean, True if the address is a loopback address as defined in
+            RFC 2373 2.5.3.
+
+        """
+        return self._ip == 1 and getattr(self, '_prefixlen', 128) == 128
+
+    @property
+    def is_link_local(self):
+        """Test if the address is reserved for link-local.
+
+        Returns:
+            A boolean, True if the address is reserved per RFC 4291.
+
+        """
+        return self in IPv6Network('fe80::/10')
+
+    @property
+    def is_site_local(self):
+        """Test if the address is reserved for site-local.
+
+        Note that the site-local address space has been deprecated by RFC 3879.
+        Use is_private to test if this address is in the space of unique local
+        addresses as defined by RFC 4193.
+
+        Returns:
+            A boolean, True if the address is reserved per RFC 3513 2.5.6.
+
+        """
+        return self in IPv6Network('fec0::/10')
+
+    @property
+    def is_private(self):
+        """Test if this address is allocated for private networks.
+
+        Returns:
+            A boolean, True if the address is reserved per RFC 4193.
+
+        """
+        return self in IPv6Network('fc00::/7')
+
+    @property
+    def ipv4_mapped(self):
+        """Return the IPv4 mapped address.
+
+        Returns:
+            If the IPv6 address is a v4 mapped address, return the
+            IPv4 mapped address. Return None otherwise.
+
+        """
+        if (self._ip >> 32) != 0xFFFF:
+            return None
+        return IPv4Address(self._ip & 0xFFFFFFFF)
+
+    @property
+    def teredo(self):
+        """Tuple of embedded teredo IPs.
+
+        Returns:
+            Tuple of the (server, client) IPs or None if the address
+            doesn't appear to be a teredo address (doesn't start with
+            2001::/32)
+
+        """
+        if (self._ip >> 96) != 0x20010000:
+            return None
+        return (IPv4Address((self._ip >> 64) & 0xFFFFFFFF),
+                IPv4Address(~self._ip & 0xFFFFFFFF))
+
+    @property
+    def sixtofour(self):
+        """Return the IPv4 6to4 embedded address.
+
+        Returns:
+            The IPv4 6to4-embedded address if present or None if the
+            address doesn't appear to contain a 6to4 embedded address.
+
+        """
+        if (self._ip >> 112) != 0x2002:
+            return None
+        return IPv4Address((self._ip >> 80) & 0xFFFFFFFF)
+
+
+class IPv6Address(_BaseV6, _BaseIP):
+
+    """Represent and manipulate single IPv6 Addresses.
+    """
+
+    def __init__(self, address):
+        """Instantiate a new IPv6 address object.
+
+        Args:
+            address: A string or integer representing the IP
+
+              Additionally, an integer can be passed, so
+              IPv6Address('2001:4860::') ==
+                IPv6Address(42541956101370907050197289607612071936L).
+              or, more generally
+              IPv6Address(IPv6Address('2001:4860::')._ip) ==
+                IPv6Address('2001:4860::')
+
+        Raises:
+            AddressValueError: If address isn't a valid IPv6 address.
+
+        """
+        _BaseV6.__init__(self, address)
+
+        # Efficient constructor from integer.
+        if isinstance(address, (int, long)):
+            self._ip = address
+            if address < 0 or address > self._ALL_ONES:
+                raise AddressValueError(address)
+            return
+
+        # Constructing from a packed address
+        if isinstance(address, Bytes):
+            try:
+                hi, lo = struct.unpack('!QQ', address)
+            except struct.error:
+                raise AddressValueError(address)  # Wrong length.
+            self._ip = (hi << 64) | lo
+            return
+
+        # Assume input argument to be string or any object representation
+        # which converts into a formatted IP string.
+        addr_str = str(address)
+        if not addr_str:
+            raise AddressValueError('')
+
+        self._ip = self._ip_int_from_string(addr_str)
+
+
+class IPv6Network(_BaseV6, _BaseNet):
+
+    """This class represents and manipulates 128-bit IPv6 networks.
+
+    Attributes: [examples for IPv6('2001:658:22A:CAFE:200::1/64')]
+        .ip: IPv6Address('2001:658:22a:cafe:200::1')
+        .network: IPv6Address('2001:658:22a:cafe::')
+        .hostmask: IPv6Address('::ffff:ffff:ffff:ffff')
+        .broadcast: IPv6Address('2001:658:22a:cafe:ffff:ffff:ffff:ffff')
+        .netmask: IPv6Address('ffff:ffff:ffff:ffff::')
+        .prefixlen: 64
+
+    """
+
+
+    def __init__(self, address, strict=False):
+        """Instantiate a new IPv6 Network object.
+
+        Args:
+            address: A string or integer representing the IPv6 network or the IP
+              and prefix/netmask.
+              '2001:4860::/128'
+              '2001:4860:0000:0000:0000:0000:0000:0000/128'
+              '2001:4860::'
+              are all functionally the same in IPv6.  That is to say,
+              failing to provide a subnetmask will create an object with
+              a mask of /128.
+
+              Additionally, an integer can be passed, so
+              IPv6Network('2001:4860::') ==
+                IPv6Network(42541956101370907050197289607612071936L).
+              or, more generally
+              IPv6Network(IPv6Network('2001:4860::')._ip) ==
+                IPv6Network('2001:4860::')
+
+            strict: A boolean. If true, ensure that we have been passed
+              A true network address, eg, 192.168.1.0/24 and not an
+              IP address on a network, eg, 192.168.1.1/24.
+
+        Raises:
+            AddressValueError: If address isn't a valid IPv6 address.
+            NetmaskValueError: If the netmask isn't valid for
+              an IPv6 address.
+            ValueError: If strict was True and a network address was not
+              supplied.
+
+        """
+        _BaseNet.__init__(self, address)
+        _BaseV6.__init__(self, address)
+
+        # Constructing from an integer or packed bytes.
+        if isinstance(address, (int, long, Bytes)):
+            self.ip = IPv6Address(address)
+            self._ip = self.ip._ip
+            self._prefixlen = self._max_prefixlen
+            self.netmask = IPv6Address(self._ALL_ONES)
+            return
+
+        # Assume input argument to be string or any object representation
+        # which converts into a formatted IP prefix string.
+        addr = str(address).split('/')
+
+        if len(addr) > 2:
+            raise AddressValueError(address)
+
+        self._ip = self._ip_int_from_string(addr[0])
+        self.ip = IPv6Address(self._ip)
+
+        if len(addr) == 2:
+            if self._is_valid_netmask(addr[1]):
+                self._prefixlen = int(addr[1])
+            else:
+                raise NetmaskValueError(addr[1])
+        else:
+            self._prefixlen = self._max_prefixlen
+
+        self.netmask = IPv6Address(self._ip_int_from_prefix(self._prefixlen))
+
+        if strict:
+            if self.ip != self.network:
+                raise ValueError('%s has host bits set' %
+                                 self.ip)
+        if self._prefixlen == (self._max_prefixlen - 1):
+            self.iterhosts = self.__iter__
+
+    def _is_valid_netmask(self, prefixlen):
+        """Verify that the netmask/prefixlen is valid.
+
+        Args:
+            prefixlen: A string, the netmask in prefix length format.
+
+        Returns:
+            A boolean, True if the prefix represents a valid IPv6
+            netmask.
+
+        """
+        try:
+            prefixlen = int(prefixlen)
+        except ValueError:
+            return False
+        return 0 <= prefixlen <= self._max_prefixlen
+
+    @property
+    def with_netmask(self):
+        return self.with_prefixlen
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/ipaddr/ipaddr_test.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/ipaddr/ipaddr_test.py
new file mode 100755
index 0000000..9446889
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/ipaddr/ipaddr_test.py
@@ -0,0 +1,1105 @@
+#!/usr/bin/python
+#
+# Copyright 2007 Google Inc.
+#  Licensed to PSF under a Contributor Agreement.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Unittest for ipaddr module."""
+
+
+import unittest
+import time
+import ipaddr
+
+# Compatibility function to cast str to bytes objects
+if issubclass(ipaddr.Bytes, str):
+    _cb = ipaddr.Bytes
+else:
+    _cb = lambda bytestr: bytes(bytestr, 'charmap')
+
+class IpaddrUnitTest(unittest.TestCase):
+
+    def setUp(self):
+        self.ipv4 = ipaddr.IPv4Network('1.2.3.4/24')
+        self.ipv4_hostmask = ipaddr.IPv4Network('10.0.0.1/0.255.255.255')
+        self.ipv6 = ipaddr.IPv6Network('2001:658:22a:cafe:200:0:0:1/64')
+
+    def tearDown(self):
+        del(self.ipv4)
+        del(self.ipv4_hostmask)
+        del(self.ipv6)
+        del(self)
+
+    def testRepr(self):
+        self.assertEqual("IPv4Network('1.2.3.4/32')",
+                         repr(ipaddr.IPv4Network('1.2.3.4')))
+        self.assertEqual("IPv6Network('::1/128')",
+                         repr(ipaddr.IPv6Network('::1')))
+
+    def testAutoMasking(self):
+        addr1 = ipaddr.IPv4Network('1.1.1.255/24')
+        addr1_masked = ipaddr.IPv4Network('1.1.1.0/24')
+        self.assertEqual(addr1_masked, addr1.masked())
+
+        addr2 = ipaddr.IPv6Network('2000:cafe::efac:100/96')
+        addr2_masked = ipaddr.IPv6Network('2000:cafe::/96')
+        self.assertEqual(addr2_masked, addr2.masked())
+
+    # issue57
+    def testAddressIntMath(self):
+        self.assertEqual(ipaddr.IPv4Address('1.1.1.1') + 255,
+                         ipaddr.IPv4Address('1.1.2.0'))
+        self.assertEqual(ipaddr.IPv4Address('1.1.1.1') - 256,
+                         ipaddr.IPv4Address('1.1.0.1'))
+        self.assertEqual(ipaddr.IPv6Address('::1') + (2**16 - 2),
+                         ipaddr.IPv6Address('::ffff'))
+        self.assertEqual(ipaddr.IPv6Address('::ffff') - (2**16 - 2),
+                         ipaddr.IPv6Address('::1'))
+
+    def testInvalidStrings(self):
+        def AssertInvalidIP(ip_str):
+            self.assertRaises(ValueError, ipaddr.IPAddress, ip_str)
+        AssertInvalidIP("")
+        AssertInvalidIP("016.016.016.016")
+        AssertInvalidIP("016.016.016")
+        AssertInvalidIP("016.016")
+        AssertInvalidIP("016")
+        AssertInvalidIP("000.000.000.000")
+        AssertInvalidIP("000")
+        AssertInvalidIP("0x0a.0x0a.0x0a.0x0a")
+        AssertInvalidIP("0x0a.0x0a.0x0a")
+        AssertInvalidIP("0x0a.0x0a")
+        AssertInvalidIP("0x0a")
+        AssertInvalidIP("42.42.42.42.42")
+        AssertInvalidIP("42.42.42")
+        AssertInvalidIP("42.42")
+        AssertInvalidIP("42")
+        AssertInvalidIP("42..42.42")
+        AssertInvalidIP("42..42.42.42")
+        AssertInvalidIP("42.42.42.42.")
+        AssertInvalidIP("42.42.42.42...")
+        AssertInvalidIP(".42.42.42.42")
+        AssertInvalidIP("...42.42.42.42")
+        AssertInvalidIP("42.42.42.-0")
+        AssertInvalidIP("42.42.42.+0")
+        AssertInvalidIP(".")
+        AssertInvalidIP("...")
+        AssertInvalidIP("bogus")
+        AssertInvalidIP("bogus.com")
+        AssertInvalidIP("192.168.0.1.com")
+        AssertInvalidIP("12345.67899.-54321.-98765")
+        AssertInvalidIP("257.0.0.0")
+        AssertInvalidIP("42.42.42.-42")
+        AssertInvalidIP("3ffe::1.net")
+        AssertInvalidIP("3ffe::1::1")
+        AssertInvalidIP("1::2::3::4:5")
+        AssertInvalidIP("::7:6:5:4:3:2:")
+        AssertInvalidIP(":6:5:4:3:2:1::")
+        AssertInvalidIP("2001::db:::1")
+        AssertInvalidIP("FEDC:9878")
+        AssertInvalidIP("+1.+2.+3.4")
+        AssertInvalidIP("1.2.3.4e0")
+        AssertInvalidIP("::7:6:5:4:3:2:1:0")
+        AssertInvalidIP("7:6:5:4:3:2:1:0::")
+        AssertInvalidIP("9:8:7:6:5:4:3::2:1")
+        AssertInvalidIP("0:1:2:3::4:5:6:7")
+        AssertInvalidIP("3ffe:0:0:0:0:0:0:0:1")
+        AssertInvalidIP("3ffe::10000")
+        AssertInvalidIP("3ffe::goog")
+        AssertInvalidIP("3ffe::-0")
+        AssertInvalidIP("3ffe::+0")
+        AssertInvalidIP("3ffe::-1")
+        AssertInvalidIP(":")
+        AssertInvalidIP(":::")
+        AssertInvalidIP("::1.2.3")
+        AssertInvalidIP("::1.2.3.4.5")
+        AssertInvalidIP("::1.2.3.4:")
+        AssertInvalidIP("1.2.3.4::")
+        AssertInvalidIP("2001:db8::1:")
+        AssertInvalidIP(":2001:db8::1")
+        AssertInvalidIP(":1:2:3:4:5:6:7")
+        AssertInvalidIP("1:2:3:4:5:6:7:")
+        AssertInvalidIP(":1:2:3:4:5:6:")
+        AssertInvalidIP("192.0.2.1/32")
+        AssertInvalidIP("2001:db8::1/128")
+
+        self.assertRaises(ipaddr.AddressValueError, ipaddr.IPv4Network, '')
+        self.assertRaises(ipaddr.AddressValueError, ipaddr.IPv4Network,
+                          'google.com')
+        self.assertRaises(ipaddr.AddressValueError, ipaddr.IPv4Network,
+                          '::1.2.3.4')
+        self.assertRaises(ipaddr.AddressValueError, ipaddr.IPv6Network, '')
+        self.assertRaises(ipaddr.AddressValueError, ipaddr.IPv6Network,
+                          'google.com')
+        self.assertRaises(ipaddr.AddressValueError, ipaddr.IPv6Network,
+                          '1.2.3.4')
+        self.assertRaises(ipaddr.AddressValueError, ipaddr.IPv6Network,
+                          'cafe:cafe::/128/190')
+        self.assertRaises(ipaddr.AddressValueError, ipaddr.IPv6Network,
+                          '1234:axy::b')
+        self.assertRaises(ipaddr.AddressValueError, ipaddr.IPv6Address,
+                          '1234:axy::b')
+        self.assertRaises(ipaddr.AddressValueError, ipaddr.IPv6Address,
+                          '2001:db8:::1')
+        self.assertRaises(ipaddr.AddressValueError, ipaddr.IPv6Address,
+                          '2001:888888::1')
+        self.assertRaises(ipaddr.AddressValueError,
+                          ipaddr.IPv4Address(1)._ip_int_from_string,
+                          '1.a.2.3')
+        self.assertEqual(False, ipaddr.IPv4Network(1)._is_hostmask('1.a.2.3'))
+
+    def testGetNetwork(self):
+        self.assertEqual(int(self.ipv4.network), 16909056)
+        self.assertEqual(str(self.ipv4.network), '1.2.3.0')
+        self.assertEqual(str(self.ipv4_hostmask.network), '10.0.0.0')
+
+        self.assertEqual(int(self.ipv6.network),
+                         42540616829182469433403647294022090752)
+        self.assertEqual(str(self.ipv6.network),
+                         '2001:658:22a:cafe::')
+        self.assertEqual(str(self.ipv6.hostmask),
+                         '::ffff:ffff:ffff:ffff')
+
+    def testBadVersionComparison(self):
+        # These should always raise TypeError
+        v4addr = ipaddr.IPAddress('1.1.1.1')
+        v4net = ipaddr.IPNetwork('1.1.1.1')
+        v6addr = ipaddr.IPAddress('::1')
+        v6net = ipaddr.IPAddress('::1')
+
+        self.assertRaises(TypeError, v4addr.__lt__, v6addr)
+        self.assertRaises(TypeError, v4addr.__gt__, v6addr)
+        self.assertRaises(TypeError, v4net.__lt__, v6net)
+        self.assertRaises(TypeError, v4net.__gt__, v6net)
+
+        self.assertRaises(TypeError, v6addr.__lt__, v4addr)
+        self.assertRaises(TypeError, v6addr.__gt__, v4addr)
+        self.assertRaises(TypeError, v6net.__lt__, v4net)
+        self.assertRaises(TypeError, v6net.__gt__, v4net)
+
+    def testMixedTypeComparison(self):
+        v4addr = ipaddr.IPAddress('1.1.1.1')
+        v4net = ipaddr.IPNetwork('1.1.1.1/32')
+        v6addr = ipaddr.IPAddress('::1')
+        v6net = ipaddr.IPNetwork('::1/128')
+
+        self.assertFalse(v4net.__contains__(v6net))
+        self.assertFalse(v6net.__contains__(v4net))
+
+        self.assertRaises(TypeError, lambda: v4addr < v4net)
+        self.assertRaises(TypeError, lambda: v4addr > v4net)
+        self.assertRaises(TypeError, lambda: v4net < v4addr)
+        self.assertRaises(TypeError, lambda: v4net > v4addr)
+
+        self.assertRaises(TypeError, lambda: v6addr < v6net)
+        self.assertRaises(TypeError, lambda: v6addr > v6net)
+        self.assertRaises(TypeError, lambda: v6net < v6addr)
+        self.assertRaises(TypeError, lambda: v6net > v6addr)
+
+        # with get_mixed_type_key, you can sort addresses and network.
+        self.assertEqual([v4addr, v4net], sorted([v4net, v4addr],
+                                                 key=ipaddr.get_mixed_type_key))
+        self.assertEqual([v6addr, v6net], sorted([v6net, v6addr],
+                                                 key=ipaddr.get_mixed_type_key))
+
+    def testIpFromInt(self):
+        self.assertEqual(self.ipv4.ip, ipaddr.IPv4Network(16909060).ip)
+        self.assertRaises(ipaddr.AddressValueError,
+                          ipaddr.IPv4Network, 2**32)
+        self.assertRaises(ipaddr.AddressValueError,
+                          ipaddr.IPv4Network, -1)
+
+        ipv4 = ipaddr.IPNetwork('1.2.3.4')
+        ipv6 = ipaddr.IPNetwork('2001:658:22a:cafe:200:0:0:1')
+        self.assertEqual(ipv4, ipaddr.IPNetwork(int(ipv4)))
+        self.assertEqual(ipv6, ipaddr.IPNetwork(int(ipv6)))
+
+        v6_int = 42540616829182469433547762482097946625
+        self.assertEqual(self.ipv6.ip, ipaddr.IPv6Network(v6_int).ip)
+        self.assertRaises(ipaddr.AddressValueError,
+                          ipaddr.IPv6Network, 2**128)
+        self.assertRaises(ipaddr.AddressValueError,
+                          ipaddr.IPv6Network, -1)
+
+        self.assertEqual(ipaddr.IPNetwork(self.ipv4.ip).version, 4)
+        self.assertEqual(ipaddr.IPNetwork(self.ipv6.ip).version, 6)
+
+    def testIpFromPacked(self):
+        ip = ipaddr.IPNetwork
+
+        self.assertEqual(self.ipv4.ip,
+                         ip(_cb('\x01\x02\x03\x04')).ip)
+        self.assertEqual(ip('255.254.253.252'),
+                         ip(_cb('\xff\xfe\xfd\xfc')))
+        self.assertRaises(ValueError, ipaddr.IPNetwork, _cb('\x00' * 3))
+        self.assertRaises(ValueError, ipaddr.IPNetwork, _cb('\x00' * 5))
+        self.assertEqual(self.ipv6.ip,
+                         ip(_cb('\x20\x01\x06\x58\x02\x2a\xca\xfe'
+                           '\x02\x00\x00\x00\x00\x00\x00\x01')).ip)
+        self.assertEqual(ip('ffff:2:3:4:ffff::'),
+                         ip(_cb('\xff\xff\x00\x02\x00\x03\x00\x04' +
+                               '\xff\xff' + '\x00' * 6)))
+        self.assertEqual(ip('::'),
+                         ip(_cb('\x00' * 16)))
+        self.assertRaises(ValueError, ip, _cb('\x00' * 15))
+        self.assertRaises(ValueError, ip, _cb('\x00' * 17))
+
+    def testGetIp(self):
+        self.assertEqual(int(self.ipv4.ip), 16909060)
+        self.assertEqual(str(self.ipv4.ip), '1.2.3.4')
+        self.assertEqual(str(self.ipv4_hostmask.ip), '10.0.0.1')
+
+        self.assertEqual(int(self.ipv6.ip),
+                         42540616829182469433547762482097946625)
+        self.assertEqual(str(self.ipv6.ip),
+                         '2001:658:22a:cafe:200::1')
+
+    def testGetNetmask(self):
+        self.assertEqual(int(self.ipv4.netmask), 4294967040L)
+        self.assertEqual(str(self.ipv4.netmask), '255.255.255.0')
+        self.assertEqual(str(self.ipv4_hostmask.netmask), '255.0.0.0')
+        self.assertEqual(int(self.ipv6.netmask),
+                         340282366920938463444927863358058659840)
+        self.assertEqual(self.ipv6.prefixlen, 64)
+
+    def testZeroNetmask(self):
+        ipv4_zero_netmask = ipaddr.IPv4Network('1.2.3.4/0')
+        self.assertEqual(int(ipv4_zero_netmask.netmask), 0)
+        self.assertTrue(ipv4_zero_netmask._is_valid_netmask(str(0)))
+
+        ipv6_zero_netmask = ipaddr.IPv6Network('::1/0')
+        self.assertEqual(int(ipv6_zero_netmask.netmask), 0)
+        self.assertTrue(ipv6_zero_netmask._is_valid_netmask(str(0)))
+
+    def testGetBroadcast(self):
+        self.assertEqual(int(self.ipv4.broadcast), 16909311L)
+        self.assertEqual(str(self.ipv4.broadcast), '1.2.3.255')
+
+        self.assertEqual(int(self.ipv6.broadcast),
+                         42540616829182469451850391367731642367)
+        self.assertEqual(str(self.ipv6.broadcast),
+                         '2001:658:22a:cafe:ffff:ffff:ffff:ffff')
+
+    def testGetPrefixlen(self):
+        self.assertEqual(self.ipv4.prefixlen, 24)
+
+        self.assertEqual(self.ipv6.prefixlen, 64)
+
+    def testGetSupernet(self):
+        self.assertEqual(self.ipv4.supernet().prefixlen, 23)
+        self.assertEqual(str(self.ipv4.supernet().network), '1.2.2.0')
+        self.assertEqual(ipaddr.IPv4Network('0.0.0.0/0').supernet(),
+                         ipaddr.IPv4Network('0.0.0.0/0'))
+
+        self.assertEqual(self.ipv6.supernet().prefixlen, 63)
+        self.assertEqual(str(self.ipv6.supernet().network),
+                         '2001:658:22a:cafe::')
+        self.assertEqual(ipaddr.IPv6Network('::0/0').supernet(),
+                         ipaddr.IPv6Network('::0/0'))
+
+    def testGetSupernet3(self):
+        self.assertEqual(self.ipv4.supernet(3).prefixlen, 21)
+        self.assertEqual(str(self.ipv4.supernet(3).network), '1.2.0.0')
+
+        self.assertEqual(self.ipv6.supernet(3).prefixlen, 61)
+        self.assertEqual(str(self.ipv6.supernet(3).network),
+                         '2001:658:22a:caf8::')
+
+    def testGetSupernet4(self):
+        self.assertRaises(ValueError, self.ipv4.supernet, prefixlen_diff=2,
+                          new_prefix=1)
+        self.assertRaises(ValueError, self.ipv4.supernet, new_prefix=25)
+        self.assertEqual(self.ipv4.supernet(prefixlen_diff=2),
+                         self.ipv4.supernet(new_prefix=22))
+
+        self.assertRaises(ValueError, self.ipv6.supernet, prefixlen_diff=2,
+                          new_prefix=1)
+        self.assertRaises(ValueError, self.ipv6.supernet, new_prefix=65)
+        self.assertEqual(self.ipv6.supernet(prefixlen_diff=2),
+                         self.ipv6.supernet(new_prefix=62))
+
+    def testIterSubnets(self):
+        self.assertEqual(self.ipv4.subnet(), list(self.ipv4.iter_subnets()))
+        self.assertEqual(self.ipv6.subnet(), list(self.ipv6.iter_subnets()))
+
+    def testIterHosts(self):
+        self.assertEqual([ipaddr.IPv4Address('2.0.0.0'),
+                          ipaddr.IPv4Address('2.0.0.1')],
+                         list(ipaddr.IPNetwork('2.0.0.0/31').iterhosts()))
+
+    def testFancySubnetting(self):
+        self.assertEqual(sorted(self.ipv4.subnet(prefixlen_diff=3)),
+                         sorted(self.ipv4.subnet(new_prefix=27)))
+        self.assertRaises(ValueError, self.ipv4.subnet, new_prefix=23)
+        self.assertRaises(ValueError, self.ipv4.subnet,
+                          prefixlen_diff=3, new_prefix=27)
+        self.assertEqual(sorted(self.ipv6.subnet(prefixlen_diff=4)),
+                         sorted(self.ipv6.subnet(new_prefix=68)))
+        self.assertRaises(ValueError, self.ipv6.subnet, new_prefix=63)
+        self.assertRaises(ValueError, self.ipv6.subnet,
+                          prefixlen_diff=4, new_prefix=68)
+
+    def testGetSubnet(self):
+        self.assertEqual(self.ipv4.subnet()[0].prefixlen, 25)
+        self.assertEqual(str(self.ipv4.subnet()[0].network), '1.2.3.0')
+        self.assertEqual(str(self.ipv4.subnet()[1].network), '1.2.3.128')
+
+        self.assertEqual(self.ipv6.subnet()[0].prefixlen, 65)
+
+    def testGetSubnetForSingle32(self):
+        ip = ipaddr.IPv4Network('1.2.3.4/32')
+        subnets1 = [str(x) for x in ip.subnet()]
+        subnets2 = [str(x) for x in ip.subnet(2)]
+        self.assertEqual(subnets1, ['1.2.3.4/32'])
+        self.assertEqual(subnets1, subnets2)
+
+    def testGetSubnetForSingle128(self):
+        ip = ipaddr.IPv6Network('::1/128')
+        subnets1 = [str(x) for x in ip.subnet()]
+        subnets2 = [str(x) for x in ip.subnet(2)]
+        self.assertEqual(subnets1, ['::1/128'])
+        self.assertEqual(subnets1, subnets2)
+
+    def testSubnet2(self):
+        ips = [str(x) for x in self.ipv4.subnet(2)]
+        self.assertEqual(
+            ips,
+            ['1.2.3.0/26', '1.2.3.64/26', '1.2.3.128/26', '1.2.3.192/26'])
+
+        ipsv6 = [str(x) for x in self.ipv6.subnet(2)]
+        self.assertEqual(
+            ipsv6,
+            ['2001:658:22a:cafe::/66',
+             '2001:658:22a:cafe:4000::/66',
+             '2001:658:22a:cafe:8000::/66',
+             '2001:658:22a:cafe:c000::/66'])
+
+    def testSubnetFailsForLargeCidrDiff(self):
+        self.assertRaises(ValueError, self.ipv4.subnet, 9)
+        self.assertRaises(ValueError, self.ipv6.subnet, 65)
+
+    def testSupernetFailsForLargeCidrDiff(self):
+        self.assertRaises(ValueError, self.ipv4.supernet, 25)
+        self.assertRaises(ValueError, self.ipv6.supernet, 65)
+
+    def testSubnetFailsForNegativeCidrDiff(self):
+        self.assertRaises(ValueError, self.ipv4.subnet, -1)
+        self.assertRaises(ValueError, self.ipv6.subnet, -1)
+
+    def testGetNumHosts(self):
+        self.assertEqual(self.ipv4.numhosts, 256)
+        self.assertEqual(self.ipv4.subnet()[0].numhosts, 128)
+        self.assertEqual(self.ipv4.supernet().numhosts, 512)
+
+        self.assertEqual(self.ipv6.numhosts, 18446744073709551616)
+        self.assertEqual(self.ipv6.subnet()[0].numhosts, 9223372036854775808)
+        self.assertEqual(self.ipv6.supernet().numhosts, 36893488147419103232)
+
+    def testContains(self):
+        self.assertTrue(ipaddr.IPv4Network('1.2.3.128/25') in self.ipv4)
+        self.assertFalse(ipaddr.IPv4Network('1.2.4.1/24') in self.ipv4)
+        self.assertTrue(self.ipv4 in self.ipv4)
+        self.assertTrue(self.ipv6 in self.ipv6)
+        # We can test addresses and string as well.
+        addr1 = ipaddr.IPv4Address('1.2.3.37')
+        self.assertTrue(addr1 in self.ipv4)
+        # issue 61, bad network comparison on like-ip'd network objects
+        # with identical broadcast addresses.
+        self.assertFalse(ipaddr.IPv4Network('1.1.0.0/16').__contains__(
+                ipaddr.IPv4Network('1.0.0.0/15')))
+
+    def testBadAddress(self):
+        self.assertRaises(ipaddr.AddressValueError, ipaddr.IPv4Network,
+                          'poop')
+        self.assertRaises(ipaddr.AddressValueError,
+                          ipaddr.IPv4Network, '1.2.3.256')
+
+        self.assertRaises(ipaddr.AddressValueError, ipaddr.IPv6Network,
+                          'poopv6')
+        self.assertRaises(ipaddr.AddressValueError,
+                          ipaddr.IPv4Network, '1.2.3.4/32/24')
+        self.assertRaises(ipaddr.AddressValueError,
+                          ipaddr.IPv4Network, '10/8')
+        self.assertRaises(ipaddr.AddressValueError,
+                          ipaddr.IPv6Network, '10/8')
+
+
+    def testBadNetMask(self):
+        self.assertRaises(ipaddr.NetmaskValueError,
+                          ipaddr.IPv4Network, '1.2.3.4/')
+        self.assertRaises(ipaddr.NetmaskValueError,
+                          ipaddr.IPv4Network, '1.2.3.4/33')
+        self.assertRaises(ipaddr.NetmaskValueError,
+                          ipaddr.IPv4Network, '1.2.3.4/254.254.255.256')
+        self.assertRaises(ipaddr.NetmaskValueError,
+                          ipaddr.IPv4Network, '1.1.1.1/240.255.0.0')
+        self.assertRaises(ipaddr.NetmaskValueError,
+                          ipaddr.IPv6Network, '::1/')
+        self.assertRaises(ipaddr.NetmaskValueError,
+                          ipaddr.IPv6Network, '::1/129')
+
+    def testNth(self):
+        self.assertEqual(str(self.ipv4[5]), '1.2.3.5')
+        self.assertRaises(IndexError, self.ipv4.__getitem__, 256)
+
+        self.assertEqual(str(self.ipv6[5]),
+                         '2001:658:22a:cafe::5')
+
+    def testGetitem(self):
+        # http://code.google.com/p/ipaddr-py/issues/detail?id=15
+        addr = ipaddr.IPv4Network('172.31.255.128/255.255.255.240')
+        self.assertEqual(28, addr.prefixlen)
+        addr_list = list(addr)
+        self.assertEqual('172.31.255.128', str(addr_list[0]))
+        self.assertEqual('172.31.255.128', str(addr[0]))
+        self.assertEqual('172.31.255.143', str(addr_list[-1]))
+        self.assertEqual('172.31.255.143', str(addr[-1]))
+        self.assertEqual(addr_list[-1], addr[-1])
+
+    def testEqual(self):
+        self.assertTrue(self.ipv4 == ipaddr.IPv4Network('1.2.3.4/24'))
+        self.assertFalse(self.ipv4 == ipaddr.IPv4Network('1.2.3.4/23'))
+        self.assertFalse(self.ipv4 == ipaddr.IPv6Network('::1.2.3.4/24'))
+        self.assertFalse(self.ipv4 == '')
+        self.assertFalse(self.ipv4 == [])
+        self.assertFalse(self.ipv4 == 2)
+        self.assertTrue(ipaddr.IPNetwork('1.1.1.1/32') ==
+                        ipaddr.IPAddress('1.1.1.1'))
+        self.assertTrue(ipaddr.IPNetwork('1.1.1.1/24') ==
+                        ipaddr.IPAddress('1.1.1.1'))
+        self.assertFalse(ipaddr.IPNetwork('1.1.1.0/24') ==
+                         ipaddr.IPAddress('1.1.1.1'))
+
+        self.assertTrue(self.ipv6 ==
+            ipaddr.IPv6Network('2001:658:22a:cafe:200::1/64'))
+        self.assertTrue(ipaddr.IPNetwork('::1/128') ==
+                        ipaddr.IPAddress('::1'))
+        self.assertTrue(ipaddr.IPNetwork('::1/127') ==
+                        ipaddr.IPAddress('::1'))
+        self.assertFalse(ipaddr.IPNetwork('::0/127') ==
+                         ipaddr.IPAddress('::1'))
+        self.assertFalse(self.ipv6 ==
+            ipaddr.IPv6Network('2001:658:22a:cafe:200::1/63'))
+        self.assertFalse(self.ipv6 == ipaddr.IPv4Network('1.2.3.4/23'))
+        self.assertFalse(self.ipv6 == '')
+        self.assertFalse(self.ipv6 == [])
+        self.assertFalse(self.ipv6 == 2)
+
+    def testNotEqual(self):
+        self.assertFalse(self.ipv4 != ipaddr.IPv4Network('1.2.3.4/24'))
+        self.assertTrue(self.ipv4 != ipaddr.IPv4Network('1.2.3.4/23'))
+        self.assertTrue(self.ipv4 != ipaddr.IPv6Network('::1.2.3.4/24'))
+        self.assertTrue(self.ipv4 != '')
+        self.assertTrue(self.ipv4 != [])
+        self.assertTrue(self.ipv4 != 2)
+
+        addr2 = ipaddr.IPAddress('2001:658:22a:cafe:200::1')
+        self.assertFalse(self.ipv6 !=
+            ipaddr.IPv6Network('2001:658:22a:cafe:200::1/64'))
+        self.assertTrue(self.ipv6 !=
+            ipaddr.IPv6Network('2001:658:22a:cafe:200::1/63'))
+        self.assertTrue(self.ipv6 != ipaddr.IPv4Network('1.2.3.4/23'))
+        self.assertTrue(self.ipv6 != '')
+        self.assertTrue(self.ipv6 != [])
+        self.assertTrue(self.ipv6 != 2)
+
+    def testSlash32Constructor(self):
+        self.assertEqual(str(ipaddr.IPv4Network('1.2.3.4/255.255.255.255')),
+                          '1.2.3.4/32')
+
+    def testSlash128Constructor(self):
+        self.assertEqual(str(ipaddr.IPv6Network('::1/128')),
+                                  '::1/128')
+
+    def testSlash0Constructor(self):
+        self.assertEqual(str(ipaddr.IPv4Network('1.2.3.4/0.0.0.0')),
+                          '1.2.3.4/0')
+
+    def testCollapsing(self):
+        # test only IP addresses including some duplicates
+        ip1 = ipaddr.IPv4Address('1.1.1.0')
+        ip2 = ipaddr.IPv4Address('1.1.1.1')
+        ip3 = ipaddr.IPv4Address('1.1.1.2')
+        ip4 = ipaddr.IPv4Address('1.1.1.3')
+        ip5 = ipaddr.IPv4Address('1.1.1.4')
+        ip6 = ipaddr.IPv4Address('1.1.1.0')
+        # check that addreses are subsumed properly.
+        collapsed = ipaddr.collapse_address_list([ip1, ip2, ip3, ip4, ip5, ip6])
+        self.assertEqual(collapsed, [ipaddr.IPv4Network('1.1.1.0/30'),
+                                     ipaddr.IPv4Network('1.1.1.4/32')])
+
+        # test a mix of IP addresses and networks including some duplicates
+        ip1 = ipaddr.IPv4Address('1.1.1.0')
+        ip2 = ipaddr.IPv4Address('1.1.1.1')
+        ip3 = ipaddr.IPv4Address('1.1.1.2')
+        ip4 = ipaddr.IPv4Address('1.1.1.3')
+        ip5 = ipaddr.IPv4Network('1.1.1.4/30')
+        ip6 = ipaddr.IPv4Network('1.1.1.4/30')
+        # check that addreses are subsumed properly.
+        collapsed = ipaddr.collapse_address_list([ip5, ip1, ip2, ip3, ip4, ip6])
+        self.assertEqual(collapsed, [ipaddr.IPv4Network('1.1.1.0/29')])
+
+        # test only IP networks
+        ip1 = ipaddr.IPv4Network('1.1.0.0/24')
+        ip2 = ipaddr.IPv4Network('1.1.1.0/24')
+        ip3 = ipaddr.IPv4Network('1.1.2.0/24')
+        ip4 = ipaddr.IPv4Network('1.1.3.0/24')
+        ip5 = ipaddr.IPv4Network('1.1.4.0/24')
+        # stored in no particular order b/c we want CollapseAddr to call [].sort
+        ip6 = ipaddr.IPv4Network('1.1.0.0/22')
+        # check that addreses are subsumed properly.
+        collapsed = ipaddr.collapse_address_list([ip1, ip2, ip3, ip4, ip5, ip6])
+        self.assertEqual(collapsed, [ipaddr.IPv4Network('1.1.0.0/22'),
+                                     ipaddr.IPv4Network('1.1.4.0/24')])
+
+        # test that two addresses are supernet'ed properly
+        collapsed = ipaddr.collapse_address_list([ip1, ip2])
+        self.assertEqual(collapsed, [ipaddr.IPv4Network('1.1.0.0/23')])
+
+        # test same IP networks
+        ip_same1 = ip_same2 = ipaddr.IPv4Network('1.1.1.1/32')
+        self.assertEqual(ipaddr.collapse_address_list([ip_same1, ip_same2]),
+                         [ip_same1])
+
+        # test same IP addresses
+        ip_same1 = ip_same2 = ipaddr.IPv4Address('1.1.1.1')
+        self.assertEqual(ipaddr.collapse_address_list([ip_same1, ip_same2]),
+                         [ipaddr.IPNetwork('1.1.1.1/32')])
+        ip1 = ipaddr.IPv6Network('::2001:1/100')
+        ip2 = ipaddr.IPv6Network('::2002:1/120')
+        ip3 = ipaddr.IPv6Network('::2001:1/96')
+        # test that ipv6 addresses are subsumed properly.
+        collapsed = ipaddr.collapse_address_list([ip1, ip2, ip3])
+        self.assertEqual(collapsed, [ip3])
+
+        # the toejam test
+        ip1 = ipaddr.IPAddress('1.1.1.1')
+        ip2 = ipaddr.IPAddress('::1')
+        self.assertRaises(TypeError, ipaddr.collapse_address_list,
+                          [ip1, ip2])
+
+    def testSummarizing(self):
+        #ip = ipaddr.IPAddress
+        #ipnet = ipaddr.IPNetwork
+        summarize = ipaddr.summarize_address_range
+        ip1 = ipaddr.IPAddress('1.1.1.0')
+        ip2 = ipaddr.IPAddress('1.1.1.255')
+        # test a /24 is sumamrized properly
+        self.assertEqual(summarize(ip1, ip2)[0], ipaddr.IPNetwork('1.1.1.0/24'))
+        # test an  IPv4 range that isn't on a network byte boundary
+        ip2 = ipaddr.IPAddress('1.1.1.8')
+        self.assertEqual(summarize(ip1, ip2), [ipaddr.IPNetwork('1.1.1.0/29'),
+                                               ipaddr.IPNetwork('1.1.1.8')])
+
+        ip1 = ipaddr.IPAddress('1::')
+        ip2 = ipaddr.IPAddress('1:ffff:ffff:ffff:ffff:ffff:ffff:ffff')
+        # test a IPv6 is sumamrized properly
+        self.assertEqual(summarize(ip1, ip2)[0], ipaddr.IPNetwork('1::/16'))
+        # test an IPv6 range that isn't on a network byte boundary
+        ip2 = ipaddr.IPAddress('2::')
+        self.assertEqual(summarize(ip1, ip2), [ipaddr.IPNetwork('1::/16'),
+                                               ipaddr.IPNetwork('2::/128')])
+
+        # test exception raised when first is greater than last
+        self.assertRaises(ValueError, summarize, ipaddr.IPAddress('1.1.1.0'),
+            ipaddr.IPAddress('1.1.0.0'))
+        # test exception raised when first and last aren't IP addresses
+        self.assertRaises(TypeError, summarize,
+                          ipaddr.IPNetwork('1.1.1.0'),
+                          ipaddr.IPNetwork('1.1.0.0'))
+        self.assertRaises(TypeError, summarize,
+            ipaddr.IPNetwork('1.1.1.0'), ipaddr.IPNetwork('1.1.0.0'))
+        # test exception raised when first and last are not same version
+        self.assertRaises(TypeError, summarize, ipaddr.IPAddress('::'),
+            ipaddr.IPNetwork('1.1.0.0'))
+
+    def testAddressComparison(self):
+        self.assertTrue(ipaddr.IPAddress('1.1.1.1') <=
+                        ipaddr.IPAddress('1.1.1.1'))
+        self.assertTrue(ipaddr.IPAddress('1.1.1.1') <=
+                        ipaddr.IPAddress('1.1.1.2'))
+        self.assertTrue(ipaddr.IPAddress('::1') <= ipaddr.IPAddress('::1'))
+        self.assertTrue(ipaddr.IPAddress('::1') <= ipaddr.IPAddress('::2'))
+
+    def testNetworkComparison(self):
+        # ip1 and ip2 have the same network address
+        ip1 = ipaddr.IPv4Network('1.1.1.0/24')
+        ip2 = ipaddr.IPv4Network('1.1.1.1/24')
+        ip3 = ipaddr.IPv4Network('1.1.2.0/24')
+
+        self.assertTrue(ip1 < ip3)
+        self.assertTrue(ip3 > ip2)
+
+        self.assertEqual(ip1.compare_networks(ip2), 0)
+        self.assertTrue(ip1._get_networks_key() == ip2._get_networks_key())
+        self.assertEqual(ip1.compare_networks(ip3), -1)
+        self.assertTrue(ip1._get_networks_key() < ip3._get_networks_key())
+
+        ip1 = ipaddr.IPv6Network('2001::2000/96')
+        ip2 = ipaddr.IPv6Network('2001::2001/96')
+        ip3 = ipaddr.IPv6Network('2001:ffff::2000/96')
+
+        self.assertTrue(ip1 < ip3)
+        self.assertTrue(ip3 > ip2)
+        self.assertEqual(ip1.compare_networks(ip2), 0)
+        self.assertTrue(ip1._get_networks_key() == ip2._get_networks_key())
+        self.assertEqual(ip1.compare_networks(ip3), -1)
+        self.assertTrue(ip1._get_networks_key() < ip3._get_networks_key())
+
+        # Test comparing different protocols.
+        # Should always raise a TypeError.
+        ipv6 = ipaddr.IPv6Network('::/0')
+        ipv4 = ipaddr.IPv4Network('0.0.0.0/0')
+        self.assertRaises(TypeError, ipv4.__lt__, ipv6)
+        self.assertRaises(TypeError, ipv4.__gt__, ipv6)
+        self.assertRaises(TypeError, ipv6.__lt__, ipv4)
+        self.assertRaises(TypeError, ipv6.__gt__, ipv4)
+
+        # Regression test for issue 19.
+        ip1 = ipaddr.IPNetwork('10.1.2.128/25')
+        self.assertFalse(ip1 < ip1)
+        self.assertFalse(ip1 > ip1)
+        ip2 = ipaddr.IPNetwork('10.1.3.0/24')
+        self.assertTrue(ip1 < ip2)
+        self.assertFalse(ip2 < ip1)
+        self.assertFalse(ip1 > ip2)
+        self.assertTrue(ip2 > ip1)
+        ip3 = ipaddr.IPNetwork('10.1.3.0/25')
+        self.assertTrue(ip2 < ip3)
+        self.assertFalse(ip3 < ip2)
+        self.assertFalse(ip2 > ip3)
+        self.assertTrue(ip3 > ip2)
+
+        # Regression test for issue 28.
+        ip1 = ipaddr.IPNetwork('10.10.10.0/31')
+        ip2 = ipaddr.IPNetwork('10.10.10.0')
+        ip3 = ipaddr.IPNetwork('10.10.10.2/31')
+        ip4 = ipaddr.IPNetwork('10.10.10.2')
+        sorted = [ip1, ip2, ip3, ip4]
+        unsorted = [ip2, ip4, ip1, ip3]
+        unsorted.sort()
+        self.assertEqual(sorted, unsorted)
+        unsorted = [ip4, ip1, ip3, ip2]
+        unsorted.sort()
+        self.assertEqual(sorted, unsorted)
+        self.assertRaises(TypeError, ip1.__lt__, ipaddr.IPAddress('10.10.10.0'))
+        self.assertRaises(TypeError, ip2.__lt__, ipaddr.IPAddress('10.10.10.0'))
+
+        # <=, >=
+        self.assertTrue(ipaddr.IPNetwork('1.1.1.1') <=
+                        ipaddr.IPNetwork('1.1.1.1'))
+        self.assertTrue(ipaddr.IPNetwork('1.1.1.1') <=
+                        ipaddr.IPNetwork('1.1.1.2'))
+        self.assertFalse(ipaddr.IPNetwork('1.1.1.2') <=
+                        ipaddr.IPNetwork('1.1.1.1'))
+        self.assertTrue(ipaddr.IPNetwork('::1') <= ipaddr.IPNetwork('::1'))
+        self.assertTrue(ipaddr.IPNetwork('::1') <= ipaddr.IPNetwork('::2'))
+        self.assertFalse(ipaddr.IPNetwork('::2') <= ipaddr.IPNetwork('::1'))
+
+    def testStrictNetworks(self):
+        self.assertRaises(ValueError, ipaddr.IPNetwork, '192.168.1.1/24',
+                          strict=True)
+        self.assertRaises(ValueError, ipaddr.IPNetwork, '::1/120', strict=True)
+
+    def testOverlaps(self):
+        other = ipaddr.IPv4Network('1.2.3.0/30')
+        other2 = ipaddr.IPv4Network('1.2.2.0/24')
+        other3 = ipaddr.IPv4Network('1.2.2.64/26')
+        self.assertTrue(self.ipv4.overlaps(other))
+        self.assertFalse(self.ipv4.overlaps(other2))
+        self.assertTrue(other2.overlaps(other3))
+
+    def testEmbeddedIpv4(self):
+        ipv4_string = '192.168.0.1'
+        ipv4 = ipaddr.IPv4Network(ipv4_string)
+        v4compat_ipv6 = ipaddr.IPv6Network('::%s' % ipv4_string)
+        self.assertEqual(int(v4compat_ipv6.ip), int(ipv4.ip))
+        v4mapped_ipv6 = ipaddr.IPv6Network('::ffff:%s' % ipv4_string)
+        self.assertNotEqual(v4mapped_ipv6.ip, ipv4.ip)
+        self.assertRaises(ipaddr.AddressValueError, ipaddr.IPv6Network,
+                          '2001:1.1.1.1:1.1.1.1')
+
+    # Issue 67: IPv6 with embedded IPv4 address not recognized.
+    def testIPv6AddressTooLarge(self):
+        # RFC4291 2.5.5.2
+        self.assertEqual(ipaddr.IPAddress('::FFFF:192.0.2.1'),
+                          ipaddr.IPAddress('::FFFF:c000:201'))
+        # RFC4291 2.2 (part 3) x::d.d.d.d 
+        self.assertEqual(ipaddr.IPAddress('FFFF::192.0.2.1'),
+                          ipaddr.IPAddress('FFFF::c000:201'))
+
+    def testIPVersion(self):
+        self.assertEqual(self.ipv4.version, 4)
+        self.assertEqual(self.ipv6.version, 6)
+
+    def testMaxPrefixLength(self):
+        self.assertEqual(self.ipv4.max_prefixlen, 32)
+        self.assertEqual(self.ipv6.max_prefixlen, 128)
+
+    def testPacked(self):
+        self.assertEqual(self.ipv4.packed,
+                         _cb('\x01\x02\x03\x04'))
+        self.assertEqual(ipaddr.IPv4Network('255.254.253.252').packed,
+                         _cb('\xff\xfe\xfd\xfc'))
+        self.assertEqual(self.ipv6.packed,
+                         _cb('\x20\x01\x06\x58\x02\x2a\xca\xfe'
+                             '\x02\x00\x00\x00\x00\x00\x00\x01'))
+        self.assertEqual(ipaddr.IPv6Network('ffff:2:3:4:ffff::').packed,
+                         _cb('\xff\xff\x00\x02\x00\x03\x00\x04\xff\xff'
+                            + '\x00' * 6))
+        self.assertEqual(ipaddr.IPv6Network('::1:0:0:0:0').packed,
+                         _cb('\x00' * 6 + '\x00\x01' + '\x00' * 8))
+
+    def testIpStrFromPrefixlen(self):
+        ipv4 = ipaddr.IPv4Network('1.2.3.4/24')
+        self.assertEqual(ipv4._ip_string_from_prefix(), '255.255.255.0')
+        self.assertEqual(ipv4._ip_string_from_prefix(28), '255.255.255.240')
+
+    def testIpType(self):
+        ipv4net = ipaddr.IPNetwork('1.2.3.4')
+        ipv4addr = ipaddr.IPAddress('1.2.3.4')
+        ipv6net = ipaddr.IPNetwork('::1.2.3.4')
+        ipv6addr = ipaddr.IPAddress('::1.2.3.4')
+        self.assertEqual(ipaddr.IPv4Network, type(ipv4net))
+        self.assertEqual(ipaddr.IPv4Address, type(ipv4addr))
+        self.assertEqual(ipaddr.IPv6Network, type(ipv6net))
+        self.assertEqual(ipaddr.IPv6Address, type(ipv6addr))
+
+    def testReservedIpv4(self):
+        # test networks
+        self.assertEqual(True, ipaddr.IPNetwork('224.1.1.1/31').is_multicast)
+        self.assertEqual(False, ipaddr.IPNetwork('240.0.0.0').is_multicast)
+
+        self.assertEqual(True, ipaddr.IPNetwork('192.168.1.1/17').is_private)
+        self.assertEqual(False, ipaddr.IPNetwork('192.169.0.0').is_private)
+        self.assertEqual(True, ipaddr.IPNetwork('10.255.255.255').is_private)
+        self.assertEqual(False, ipaddr.IPNetwork('11.0.0.0').is_private)
+        self.assertEqual(True, ipaddr.IPNetwork('172.31.255.255').is_private)
+        self.assertEqual(False, ipaddr.IPNetwork('172.32.0.0').is_private)
+
+        self.assertEqual(True,
+                          ipaddr.IPNetwork('169.254.100.200/24').is_link_local)
+        self.assertEqual(False,
+                          ipaddr.IPNetwork('169.255.100.200/24').is_link_local)
+
+        self.assertEqual(True,
+                          ipaddr.IPNetwork('127.100.200.254/32').is_loopback)
+        self.assertEqual(True, ipaddr.IPNetwork('127.42.0.0/16').is_loopback)
+        self.assertEqual(False, ipaddr.IPNetwork('128.0.0.0').is_loopback)
+
+        # test addresses
+        self.assertEqual(True, ipaddr.IPAddress('224.1.1.1').is_multicast)
+        self.assertEqual(False, ipaddr.IPAddress('240.0.0.0').is_multicast)
+
+        self.assertEqual(True, ipaddr.IPAddress('192.168.1.1').is_private)
+        self.assertEqual(False, ipaddr.IPAddress('192.169.0.0').is_private)
+        self.assertEqual(True, ipaddr.IPAddress('10.255.255.255').is_private)
+        self.assertEqual(False, ipaddr.IPAddress('11.0.0.0').is_private)
+        self.assertEqual(True, ipaddr.IPAddress('172.31.255.255').is_private)
+        self.assertEqual(False, ipaddr.IPAddress('172.32.0.0').is_private)
+
+        self.assertEqual(True,
+                          ipaddr.IPAddress('169.254.100.200').is_link_local)
+        self.assertEqual(False,
+                          ipaddr.IPAddress('169.255.100.200').is_link_local)
+
+        self.assertEqual(True,
+                          ipaddr.IPAddress('127.100.200.254').is_loopback)
+        self.assertEqual(True, ipaddr.IPAddress('127.42.0.0').is_loopback)
+        self.assertEqual(False, ipaddr.IPAddress('128.0.0.0').is_loopback)
+        self.assertEqual(True, ipaddr.IPNetwork('0.0.0.0').is_unspecified)
+
+    def testReservedIpv6(self):
+
+        self.assertEqual(True, ipaddr.IPNetwork('ffff::').is_multicast)
+        self.assertEqual(True, ipaddr.IPNetwork(2**128-1).is_multicast)
+        self.assertEqual(True, ipaddr.IPNetwork('ff00::').is_multicast)
+        self.assertEqual(False, ipaddr.IPNetwork('fdff::').is_multicast)
+
+        self.assertEqual(True, ipaddr.IPNetwork('fecf::').is_site_local)
+        self.assertEqual(True, ipaddr.IPNetwork(
+                'feff:ffff:ffff:ffff::').is_site_local)
+        self.assertEqual(False, ipaddr.IPNetwork('fbf:ffff::').is_site_local)
+        self.assertEqual(False, ipaddr.IPNetwork('ff00::').is_site_local)
+
+        self.assertEqual(True, ipaddr.IPNetwork('fc00::').is_private)
+        self.assertEqual(True, ipaddr.IPNetwork(
+                'fc00:ffff:ffff:ffff::').is_private)
+        self.assertEqual(False, ipaddr.IPNetwork('fbff:ffff::').is_private)
+        self.assertEqual(False, ipaddr.IPNetwork('fe00::').is_private)
+
+        self.assertEqual(True, ipaddr.IPNetwork('fea0::').is_link_local)
+        self.assertEqual(True, ipaddr.IPNetwork('febf:ffff::').is_link_local)
+        self.assertEqual(False, ipaddr.IPNetwork('fe7f:ffff::').is_link_local)
+        self.assertEqual(False, ipaddr.IPNetwork('fec0::').is_link_local)
+
+        self.assertEqual(True, ipaddr.IPNetwork('0:0::0:01').is_loopback)
+        self.assertEqual(False, ipaddr.IPNetwork('::1/127').is_loopback)
+        self.assertEqual(False, ipaddr.IPNetwork('::').is_loopback)
+        self.assertEqual(False, ipaddr.IPNetwork('::2').is_loopback)
+
+        self.assertEqual(True, ipaddr.IPNetwork('0::0').is_unspecified)
+        self.assertEqual(False, ipaddr.IPNetwork('::1').is_unspecified)
+        self.assertEqual(False, ipaddr.IPNetwork('::/127').is_unspecified)
+
+        # test addresses
+        self.assertEqual(True, ipaddr.IPAddress('ffff::').is_multicast)
+        self.assertEqual(True, ipaddr.IPAddress(2**128-1).is_multicast)
+        self.assertEqual(True, ipaddr.IPAddress('ff00::').is_multicast)
+        self.assertEqual(False, ipaddr.IPAddress('fdff::').is_multicast)
+
+        self.assertEqual(True, ipaddr.IPAddress('fecf::').is_site_local)
+        self.assertEqual(True, ipaddr.IPAddress(
+                'feff:ffff:ffff:ffff::').is_site_local)
+        self.assertEqual(False, ipaddr.IPAddress('fbf:ffff::').is_site_local)
+        self.assertEqual(False, ipaddr.IPAddress('ff00::').is_site_local)
+
+        self.assertEqual(True, ipaddr.IPAddress('fc00::').is_private)
+        self.assertEqual(True, ipaddr.IPAddress(
+                'fc00:ffff:ffff:ffff::').is_private)
+        self.assertEqual(False, ipaddr.IPAddress('fbff:ffff::').is_private)
+        self.assertEqual(False, ipaddr.IPAddress('fe00::').is_private)
+
+        self.assertEqual(True, ipaddr.IPAddress('fea0::').is_link_local)
+        self.assertEqual(True, ipaddr.IPAddress('febf:ffff::').is_link_local)
+        self.assertEqual(False, ipaddr.IPAddress('fe7f:ffff::').is_link_local)
+        self.assertEqual(False, ipaddr.IPAddress('fec0::').is_link_local)
+
+        self.assertEqual(True, ipaddr.IPAddress('0:0::0:01').is_loopback)
+        self.assertEqual(True, ipaddr.IPAddress('::1').is_loopback)
+        self.assertEqual(False, ipaddr.IPAddress('::2').is_loopback)
+
+        self.assertEqual(True, ipaddr.IPAddress('0::0').is_unspecified)
+        self.assertEqual(False, ipaddr.IPAddress('::1').is_unspecified)
+
+        # some generic IETF reserved addresses
+        self.assertEqual(True, ipaddr.IPAddress('100::').is_reserved)
+        self.assertEqual(True, ipaddr.IPNetwork('4000::1/128').is_reserved)
+
+    def testIpv4Mapped(self):
+        self.assertEqual(ipaddr.IPAddress('::ffff:192.168.1.1').ipv4_mapped,
+                         ipaddr.IPAddress('192.168.1.1'))
+        self.assertEqual(ipaddr.IPAddress('::c0a8:101').ipv4_mapped, None)
+        self.assertEqual(ipaddr.IPAddress('::ffff:c0a8:101').ipv4_mapped,
+                         ipaddr.IPAddress('192.168.1.1'))
+
+    def testAddrExclude(self):
+        addr1 = ipaddr.IPNetwork('10.1.1.0/24')
+        addr2 = ipaddr.IPNetwork('10.1.1.0/26')
+        addr3 = ipaddr.IPNetwork('10.2.1.0/24')
+        addr4 = ipaddr.IPAddress('10.1.1.0')
+        self.assertEqual(addr1.address_exclude(addr2),
+                         [ipaddr.IPNetwork('10.1.1.64/26'),
+                          ipaddr.IPNetwork('10.1.1.128/25')])
+        self.assertRaises(ValueError, addr1.address_exclude, addr3)
+        self.assertRaises(TypeError, addr1.address_exclude, addr4)
+        self.assertEqual(addr1.address_exclude(addr1), [])
+
+    def testHash(self):
+        self.assertEqual(hash(ipaddr.IPNetwork('10.1.1.0/24')),
+                          hash(ipaddr.IPNetwork('10.1.1.0/24')))
+        self.assertEqual(hash(ipaddr.IPAddress('10.1.1.0')),
+                          hash(ipaddr.IPAddress('10.1.1.0')))
+        # i70
+        self.assertEqual(hash(ipaddr.IPAddress('1.2.3.4')),
+                          hash(ipaddr.IPAddress(
+                    long(ipaddr.IPAddress('1.2.3.4')._ip))))
+        ip1 = ipaddr.IPAddress('10.1.1.0')
+        ip2 = ipaddr.IPAddress('1::')
+        dummy = {}
+        dummy[self.ipv4] = None
+        dummy[self.ipv6] = None
+        dummy[ip1] = None
+        dummy[ip2] = None
+        self.assertTrue(self.ipv4 in dummy)
+        self.assertTrue(ip2 in dummy)
+
+    def testCopyConstructor(self):
+        addr1 = ipaddr.IPNetwork('10.1.1.0/24')
+        addr2 = ipaddr.IPNetwork(addr1)
+        addr3 = ipaddr.IPNetwork('2001:658:22a:cafe:200::1/64')
+        addr4 = ipaddr.IPNetwork(addr3)
+        addr5 = ipaddr.IPv4Address('1.1.1.1')
+        addr6 = ipaddr.IPv6Address('2001:658:22a:cafe:200::1')
+
+        self.assertEqual(addr1, addr2)
+        self.assertEqual(addr3, addr4)
+        self.assertEqual(addr5, ipaddr.IPv4Address(addr5))
+        self.assertEqual(addr6, ipaddr.IPv6Address(addr6))
+
+    def testCompressIPv6Address(self):
+        test_addresses = {
+            '1:2:3:4:5:6:7:8': '1:2:3:4:5:6:7:8/128',
+            '2001:0:0:4:0:0:0:8': '2001:0:0:4::8/128',
+            '2001:0:0:4:5:6:7:8': '2001::4:5:6:7:8/128',
+            '2001:0:3:4:5:6:7:8': '2001:0:3:4:5:6:7:8/128',
+            '2001:0:3:4:5:6:7:8': '2001:0:3:4:5:6:7:8/128',
+            '0:0:3:0:0:0:0:ffff': '0:0:3::ffff/128',
+            '0:0:0:4:0:0:0:ffff': '::4:0:0:0:ffff/128',
+            '0:0:0:0:5:0:0:ffff': '::5:0:0:ffff/128',
+            '1:0:0:4:0:0:7:8': '1::4:0:0:7:8/128',
+            '0:0:0:0:0:0:0:0': '::/128',
+            '0:0:0:0:0:0:0:0/0': '::/0',
+            '0:0:0:0:0:0:0:1': '::1/128',
+            '2001:0658:022a:cafe:0000:0000:0000:0000/66':
+            '2001:658:22a:cafe::/66',
+            '::1.2.3.4': '::102:304/128',
+            '1:2:3:4:5:ffff:1.2.3.4': '1:2:3:4:5:ffff:102:304/128',
+            '::7:6:5:4:3:2:1': '0:7:6:5:4:3:2:1/128',
+            '::7:6:5:4:3:2:0': '0:7:6:5:4:3:2:0/128',
+            '7:6:5:4:3:2:1::': '7:6:5:4:3:2:1:0/128',
+            '0:6:5:4:3:2:1::': '0:6:5:4:3:2:1:0/128',
+            }
+        for uncompressed, compressed in test_addresses.items():
+            self.assertEqual(compressed, str(ipaddr.IPv6Network(uncompressed)))
+
+    def testExplodeShortHandIpStr(self):
+        addr1 = ipaddr.IPv6Network('2001::1')
+        addr2 = ipaddr.IPv6Address('2001:0:5ef5:79fd:0:59d:a0e5:ba1')
+        self.assertEqual('2001:0000:0000:0000:0000:0000:0000:0001/128',
+                         addr1.exploded)
+        self.assertEqual('0000:0000:0000:0000:0000:0000:0000:0001/128',
+                         ipaddr.IPv6Network('::1/128').exploded)
+        # issue 77
+        self.assertEqual('2001:0000:5ef5:79fd:0000:059d:a0e5:0ba1',
+                         addr2.exploded)
+
+    def testIntRepresentation(self):
+        self.assertEqual(16909060, int(self.ipv4))
+        self.assertEqual(42540616829182469433547762482097946625, int(self.ipv6))
+
+    def testHexRepresentation(self):
+        self.assertEqual(hex(0x1020304),
+                         hex(self.ipv4))
+
+        self.assertEqual(hex(0x20010658022ACAFE0200000000000001),
+                         hex(self.ipv6))
+
+    # backwards compatibility
+    def testBackwardsCompability(self):
+        self.assertEqual(ipaddr.CollapseAddrList(
+            [ipaddr.IPNetwork('1.1.0.0/24'), ipaddr.IPNetwork('1.1.1.0/24')]),
+                         [ipaddr.IPNetwork('1.1.0.0/23')])
+
+        self.assertEqual(ipaddr.IPNetwork('::42:0/112').AddressExclude(
+            ipaddr.IPNetwork('::42:8000/113')),
+                         [ipaddr.IPNetwork('::42:0/113')])
+
+        self.assertTrue(ipaddr.IPNetwork('1::/8').CompareNetworks(
+            ipaddr.IPNetwork('2::/9')) < 0)
+
+        self.assertEqual(ipaddr.IPNetwork('1::/16').Contains(
+            ipaddr.IPNetwork('2::/16')), False)
+
+        self.assertEqual(ipaddr.IPNetwork('0.0.0.0/0').Subnet(),
+                         [ipaddr.IPNetwork('0.0.0.0/1'),
+                          ipaddr.IPNetwork('128.0.0.0/1')])
+        self.assertEqual(ipaddr.IPNetwork('::/127').Subnet(),
+                         [ipaddr.IPNetwork('::/128'),
+                          ipaddr.IPNetwork('::1/128')])
+
+        self.assertEqual(ipaddr.IPNetwork('1.0.0.0/32').Supernet(),
+                         ipaddr.IPNetwork('1.0.0.0/31'))
+        self.assertEqual(ipaddr.IPNetwork('::/121').Supernet(),
+                         ipaddr.IPNetwork('::/120'))
+
+        self.assertEqual(ipaddr.IPNetwork('10.0.0.2').IsRFC1918(), True)
+        self.assertEqual(ipaddr.IPNetwork('10.0.0.0').IsMulticast(), False)
+        self.assertEqual(ipaddr.IPNetwork('127.255.255.255').IsLoopback(), True)
+        self.assertEqual(ipaddr.IPNetwork('169.255.255.255').IsLinkLocal(),
+                         False)
+
+    def testForceVersion(self):
+        self.assertEqual(ipaddr.IPNetwork(1).version, 4)
+        self.assertEqual(ipaddr.IPNetwork(1, version=6).version, 6)
+
+    def testWithStar(self):
+        self.assertEqual(str(self.ipv4.with_prefixlen), "1.2.3.4/24")
+        self.assertEqual(str(self.ipv4.with_netmask), "1.2.3.4/255.255.255.0")
+        self.assertEqual(str(self.ipv4.with_hostmask), "1.2.3.4/0.0.0.255")
+
+        self.assertEqual(str(self.ipv6.with_prefixlen),
+                         '2001:658:22a:cafe:200::1/64')
+        # rfc3513 sec 2.3 says that ipv6 only uses cidr notation for
+        # subnets
+        self.assertEqual(str(self.ipv6.with_netmask),
+                         '2001:658:22a:cafe:200::1/64')
+        # this probably don't make much sense, but it's included for
+        # compatibility with ipv4
+        self.assertEqual(str(self.ipv6.with_hostmask),
+                         '2001:658:22a:cafe:200::1/::ffff:ffff:ffff:ffff')
+
+    def testNetworkElementCaching(self):
+        # V4 - make sure we're empty
+        self.assertFalse(self.ipv4._cache.has_key('network'))
+        self.assertFalse(self.ipv4._cache.has_key('broadcast'))
+        self.assertFalse(self.ipv4._cache.has_key('hostmask'))
+
+        # V4 - populate and test
+        self.assertEqual(self.ipv4.network, ipaddr.IPv4Address('1.2.3.0'))
+        self.assertEqual(self.ipv4.broadcast, ipaddr.IPv4Address('1.2.3.255'))
+        self.assertEqual(self.ipv4.hostmask, ipaddr.IPv4Address('0.0.0.255'))
+
+        # V4 - check we're cached
+        self.assertTrue(self.ipv4._cache.has_key('network'))
+        self.assertTrue(self.ipv4._cache.has_key('broadcast'))
+        self.assertTrue(self.ipv4._cache.has_key('hostmask'))
+
+        # V6 - make sure we're empty
+        self.assertFalse(self.ipv6._cache.has_key('network'))
+        self.assertFalse(self.ipv6._cache.has_key('broadcast'))
+        self.assertFalse(self.ipv6._cache.has_key('hostmask'))
+
+        # V6 - populate and test
+        self.assertEqual(self.ipv6.network,
+                         ipaddr.IPv6Address('2001:658:22a:cafe::'))
+        self.assertEqual(self.ipv6.broadcast, ipaddr.IPv6Address(
+            '2001:658:22a:cafe:ffff:ffff:ffff:ffff'))
+        self.assertEqual(self.ipv6.hostmask,
+                         ipaddr.IPv6Address('::ffff:ffff:ffff:ffff'))
+
+        # V6 - check we're cached
+        self.assertTrue(self.ipv6._cache.has_key('network'))
+        self.assertTrue(self.ipv6._cache.has_key('broadcast'))
+        self.assertTrue(self.ipv6._cache.has_key('hostmask'))
+
+    def testTeredo(self):
+        # stolen from wikipedia
+        server = ipaddr.IPv4Address('65.54.227.120')
+        client = ipaddr.IPv4Address('192.0.2.45')
+        teredo_addr = '2001:0000:4136:e378:8000:63bf:3fff:fdd2'
+        self.assertEqual((server, client),
+                         ipaddr.IPAddress(teredo_addr).teredo)
+        bad_addr = '2000::4136:e378:8000:63bf:3fff:fdd2'
+        self.assertFalse(ipaddr.IPAddress(bad_addr).teredo)
+        bad_addr = '2001:0001:4136:e378:8000:63bf:3fff:fdd2'
+        self.assertFalse(ipaddr.IPAddress(bad_addr).teredo)
+
+        # i77
+        teredo_addr = ipaddr.IPv6Address('2001:0:5ef5:79fd:0:59d:a0e5:ba1')
+        self.assertEqual((ipaddr.IPv4Address('94.245.121.253'),
+                          ipaddr.IPv4Address('95.26.244.94')),
+                         teredo_addr.teredo)
+
+
+    def testsixtofour(self):
+        sixtofouraddr = ipaddr.IPAddress('2002:ac1d:2d64::1')
+        bad_addr = ipaddr.IPAddress('2000:ac1d:2d64::1')
+        self.assertEqual(ipaddr.IPv4Address('172.29.45.100'),
+                         sixtofouraddr.sixtofour)
+        self.assertFalse(bad_addr.sixtofour)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/ipaddr/setup.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/ipaddr/setup.py
new file mode 100755
index 0000000..3356432
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/ipaddr/setup.py
@@ -0,0 +1,36 @@
+#!/usr/bin/python
+#
+# Copyright 2008 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from distutils.core import setup
+
+import ipaddr
+
+
+setup(name='ipaddr',
+      maintainer='Google',
+      maintainer_email='ipaddr-py-dev@googlegroups.com',
+      version=ipaddr.__version__,
+      url='http://code.google.com/p/ipaddr-py/',
+      license='Apache License, Version 2.0',
+      classifiers=[
+          'Development Status :: 5 - Production/Stable',
+          'Intended Audience :: Developers',
+          'License :: OSI Approved :: Apache Software License',
+          'Operating System :: OS Independent',
+          'Topic :: Internet',
+          'Topic :: Software Development :: Libraries',
+          'Topic :: System :: Networking'],
+      py_modules=['ipaddr'])
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/ipaddr/test-2to3.sh b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/ipaddr/test-2to3.sh
new file mode 100755
index 0000000..5196083
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/ipaddr/test-2to3.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+# Copyright 2007 Google Inc.
+#  Licensed to PSF under a Contributor Agreement.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied. See the License for the specific language governing
+# permissions and limitations under the License.
+#
+# Converts the python2 ipaddr files to python3 and runs the unit tests
+# with both python versions.
+
+mkdir -p 2to3output && \
+cp -f *.py 2to3output && \
+( cd 2to3output && 2to3 . | patch -p0 ) && \
+py3version=$(python3 --version 2>&1) && \
+echo -e "\nTesting with ${py3version}" && \
+python3 2to3output/ipaddr_test.py && \
+rm -r 2to3output && \
+pyversion=$(python --version 2>&1) && \
+echo -e "\nTesting with ${pyversion}" && \
+./ipaddr_test.py
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/ipfw_win32/LICENSE b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/ipfw_win32/LICENSE
new file mode 100644
index 0000000..c1df6fe
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/ipfw_win32/LICENSE
@@ -0,0 +1,25 @@
+/*-
+ * Copyright (c) 1998-2010 Luigi Rizzo, Universita` di Pisa
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/ipfw_win32/README.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/ipfw_win32/README.txt
new file mode 100644
index 0000000..3dae853
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/ipfw_win32/README.txt
@@ -0,0 +1,27 @@
+This directory contains the binaries to install and use IPFW and
+DUMMYNET on a Windows Machine. The kernel part is an NDIS module,
+whereas the user interface is a command line program.
+
+1. INSTALL THE NDIS DRIVER
+
+- open the configuration panel for the network card in use
+  (either right click on the icon on the SYSTRAY, or go to
+  Control Panel -> Network and select one card)
+
+- click on Properties->Install->Service->Add
+- click on 'Driver Disk' and select 'netipfw.inf' in this folder
+- select 'ipfw+dummynet' which is the only service you should see
+- click accept on the warnings for the installation of an unknown
+  driver (roughly twice per existing network card)
+
+Now you are ready to use the emulator. To configure it, open a 'cmd'
+window and you can use the ipfw command from the command line.
+Otherwise click on the 'TESTME.bat' which is a batch program that
+runs various tests.
+
+2. UNINSTALL THE DRIVER
+
+- select a network card as above.
+- click on Properties
+- select 'ipfw+dummynet'
+- click on 'Remove'
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/ipfw_win32/README.web-page-replay b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/ipfw_win32/README.web-page-replay
new file mode 100644
index 0000000..8bf15c6
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/ipfw_win32/README.web-page-replay
@@ -0,0 +1,12 @@
+Name: Windows XP NDIS module for Dummynet.
+Short Name: ipfw3
+URL: http://info.iet.unipi.it/~luigi/dummynet/
+Version: 20100322 v.3.0.0.2
+License: BSD
+License File: LICENSE
+
+Description:
+Used by Web Page Replay to simulate network delays and bandwidth throttling on Windows XP.
+
+Local Modifications:
+Dropped files: cyg-ipfw.exe, cygwin1.dll, testme.bat, wget.exe.
\ No newline at end of file
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/ipfw_win32/ipfw.exe b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/ipfw_win32/ipfw.exe
new file mode 100644
index 0000000..2ab5c3f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/ipfw_win32/ipfw.exe
Binary files differ
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/ipfw_win32/ipfw.sys b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/ipfw_win32/ipfw.sys
new file mode 100644
index 0000000..d5e1b9f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/ipfw_win32/ipfw.sys
Binary files differ
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/ipfw_win32/netipfw.inf b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/ipfw_win32/netipfw.inf
new file mode 100644
index 0000000..11ec684
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/ipfw_win32/netipfw.inf
@@ -0,0 +1,79 @@
+; version section
+[Version]
+Signature  = "$Windows NT$"
+Class      = NetService
+ClassGUID  = {4D36E974-E325-11CE-BFC1-08002BE10318}
+Provider   = %Unipi%
+DriverVer  = 26/02/2010,3.0.0.2
+
+; manufacturer section
+[Manufacturer]
+%Unipi% = UNIPI,NTx86
+
+; control flags section
+; optional, unused in netipfw.inf inf, used in netipfw_m.inf
+[ControlFlags]
+
+; models section
+[UNIPI] ; Win2k
+%Desc% = Ipfw.ndi, unipi_ipfw
+[UNIPI.NTx86] ;For WinXP and later
+%Desc% = Ipfw.ndi, unipi_ipfw
+
+; ddinstall section
+[Ipfw.ndi]
+AddReg          = Ipfw.ndi.AddReg, Ipfw.AddReg
+Characteristics = 0x4410 ;  NCF_FILTER | NCF_NDIS_PROTOCOL !--Filter Specific--!!
+CopyFiles       = Ipfw.Files.Sys
+CopyInf         = netipfw_m.inf
+
+; remove section
+[Ipfw.ndi.Remove]
+DelFiles = Ipfw.Files.Sys
+
+;ddinstall.services section
+[Ipfw.ndi.Services]
+AddService = Ipfw,,Ipfw.AddService
+
+[Ipfw.AddService]
+DisplayName    = %ServiceDesc%
+ServiceType    = 1 ;SERVICE_KERNEL_DRIVER
+StartType      = 3 ;SERVICE_DEMAND_START
+ErrorControl   = 1 ;SERVICE_ERROR_NORMAL
+ServiceBinary  = %12%\ipfw.sys
+AddReg         = Ipfw.AddService.AddReg
+
+[Ipfw.AddService.AddReg]
+
+;file copy related sections
+[SourceDisksNames]
+1=%DiskDescription%,"",,
+
+[SourceDisksFiles]
+ipfw.sys=1
+
+[DestinationDirs]
+DefaultDestDir = 12
+Ipfw.Files.Sys   = 12   ; %windir%\System32\drivers
+
+; ddinstall->copyfiles points here
+[Ipfw.Files.Sys]
+ipfw.sys,,,2
+
+; ddinstall->addreg points here
+[Ipfw.ndi.AddReg]
+HKR, Ndi,            HelpText,            , %HELP% ; this is displayed at the bottom of the General page of the Connection Properties dialog box
+HKR, Ndi,            FilterClass,         , failover
+HKR, Ndi,            FilterDeviceInfId,   , unipi_ipfwmp
+HKR, Ndi,            Service,             , Ipfw
+HKR, Ndi\Interfaces, UpperRange,          , noupper
+HKR, Ndi\Interfaces, LowerRange,          , nolower
+HKR, Ndi\Interfaces, FilterMediaTypes,    , "ethernet, tokenring, fddi, wan"
+
+;strings section
+[Strings]
+Unipi = "Unipi"
+DiskDescription = "Ipfw Driver Disk"
+Desc = "ipfw+dummynet"
+HELP = "This is ipfw and dummynet network emulator, developed by unipi.it"
+ServiceDesc = "ipfw service"
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/ipfw_win32/netipfw_m.inf b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/ipfw_win32/netipfw_m.inf
new file mode 100644
index 0000000..864559f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/ipfw_win32/netipfw_m.inf
@@ -0,0 +1,54 @@
+; version section
+[Version]
+Signature  = "$Windows NT$"
+Class      = Net
+ClassGUID  = {4D36E972-E325-11CE-BFC1-08002BE10318}
+Provider   = %Unipi%
+DriverVer  = 26/02/2010,3.0.0.2
+
+; control flags section
+; optional, unused in netipfw.inf inf, used in netipfw_m.inf
+[ControlFlags]
+ExcludeFromSelect = unipi_ipfwmp
+
+; destinationdirs section, optional
+[DestinationDirs]
+DefaultDestDir=12
+; No files to copy 
+
+; manufacturer section
+[Manufacturer]
+%Unipi% = UNIPI,NTx86
+
+; models section
+[UNIPI] ; Win2k
+%Desc% = IpfwMP.ndi, unipi_ipfwmp
+[UNIPI.NTx86] ;For WinXP and later
+%Desc% = IpfwMP.ndi, unipi_ipfwmp
+
+; ddinstall section
+[IpfwMP.ndi]
+AddReg  = IpfwMP.ndi.AddReg
+Characteristics = 0x29 ;NCF_NOT_USER_REMOVABLE | NCF_VIRTUAL | NCF_HIDDEN
+
+; ddinstall->addreg points here
+[IpfwMP.ndi.AddReg]
+HKR, Ndi, Service,  0,  IpfwMP
+
+;ddinstall.services section
+[IpfwMP.ndi.Services]
+AddService = IpfwMP,0x2, IpfwMP.AddService
+
+[IpfwMP.AddService]
+ServiceType    = 1 ;SERVICE_KERNEL_DRIVER
+StartType      = 3 ;SERVICE_DEMAND_START
+ErrorControl   = 1 ;SERVICE_ERROR_NORMAL
+ServiceBinary  = %12%\ipfw.sys
+AddReg         = IpfwMP.AddService.AddReg
+
+[IpfwMP.AddService.AddReg]
+; None
+
+[Strings]
+Unipi = "Unipi"
+Desc = "Ipfw Miniport"
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/jsmin/LICENSE.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/jsmin/LICENSE.txt
new file mode 100644
index 0000000..193a853
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/jsmin/LICENSE.txt
@@ -0,0 +1,23 @@
+The MIT License (MIT)
+
+Copyright (c) 2013 Dave St.Germain
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/jsmin/MANIFEST.in b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/jsmin/MANIFEST.in
new file mode 100644
index 0000000..ab30e9a
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/jsmin/MANIFEST.in
@@ -0,0 +1 @@
+include *.txt
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/jsmin/PKG-INFO b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/jsmin/PKG-INFO
new file mode 100644
index 0000000..66da942
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/jsmin/PKG-INFO
@@ -0,0 +1,177 @@
+Metadata-Version: 1.1
+Name: jsmin
+Version: 2.2.1
+Summary: JavaScript minifier.
+Home-page: https://bitbucket.org/dcs/jsmin/
+Author: Tikitu de Jager
+Author-email: tikitu+jsmin@logophile.org
+License: MIT License
+Description: =====
+        jsmin
+        =====
+        
+        JavaScript minifier.
+        
+        Usage
+        =====
+        
+        .. code:: python
+        
+         from jsmin import jsmin
+         with open('myfile.js') as js_file:
+             minified = jsmin(js_file.read())
+        
+        You can run it as a commandline tool also::
+        
+          python -m jsmin myfile.js
+        
+        NB: ``jsmin`` makes no attempt to be compatible with
+        `ECMAScript 6 / ES.next / Harmony <http://wiki.ecmascript.org/doku.php?id=harmony:specification_drafts>`_.
+        The current maintainer does not intend to add ES6-compatibility. If you would
+        like to take over maintenance and update ``jsmin`` for ES6, please contact
+        `Tikitu de Jager <mailto:tikitu+jsmin@logophile.org>`_. Pull requests are also
+        welcome, of course, but my time to review them is somewhat limited these days.
+        
+        If you're using ``jsmin`` on ES6 code, though, you might find the ``quote_chars``
+        parameter useful:
+        
+        .. code:: python
+        
+         from jsmin import jsmin
+         with open('myfile.js') as js_file:
+             minified = jsmin(js_file.read(), quote_chars="'\"`")
+        
+        
+        Where to get it
+        ===============
+        
+        * install the package `from pypi <https://pypi.python.org/pypi/jsmin/>`_
+        * get the latest release `from latest-release on github <https://github.com/tikitu/jsmin/tree/latest-release/jsmin>`_
+        * get the development version `from master on github <https://github.com/tikitu/jsmin/>`_
+        
+        Contributing
+        ============
+        
+        `Issues <https://github.com/tikitu/jsmin/issues>`_ and `Pull requests <https://github.com/tikitu/jsmin/pulls>`_
+        will be gratefully received on Github. The project used to be hosted
+        `on bitbucket <https://bitbucket.org/dcs/jsmin/>`_ and old issues can still be
+        found there.
+        
+        If possible, please make separate pull requests for tests and for code: tests will be added to the `latest-release` branch while code will go to `master`.
+        
+        Unless you request otherwise, your Github identity will be added to the contributor's list below; if you prefer a
+        different name feel free to add it in your pull request instead. (If you prefer not to be mentioned you'll have to let
+        the maintainer know somehow.)
+        
+        Build/test status
+        =================
+        
+        Both branches are tested with Travis: https://travis-ci.org/tikitu/jsmin
+        
+        The `latest-release` branch (the version on PyPI plus any new tests) is tested against CPython 2.6, 2.7, 3.2, and 3.3.
+        Currently:
+        
+        .. image:: https://travis-ci.org/tikitu/jsmin.png?branch=latest-release
+        
+        If that branch is failing that means there's a new test that fails on *the latest released version on pypi*, with no fix yet
+        released.
+        
+        The `master` branch (development version, might be ahead of latest released version) is tested against CPython 2.6, 2.7, 3.2, and
+        3.3. Currently:
+        
+        .. image:: https://travis-ci.org/tikitu/jsmin.png?branch=master
+        
+        If `master` is failing don't use it, but as long as `latest-release` is passing the pypi release should be ok.
+        
+        Contributors (chronological commit order)
+        =========================================
+        
+        * `Dave St.Germain <https://bitbucket.org/dcs>`_ (original author)
+        * `Hans weltar <https://bitbucket.org/hansweltar>`_
+        * `Tikitu de Jager <mailto:tikitu+jsmin@logophile.org>`_ (current maintainer)
+        * https://bitbucket.org/rennat
+        * `Nick Alexander <https://bitbucket.org/ncalexan>`_
+        * `Gennady Kovshenin <https://github.com/soulseekah>`_
+        * `Matt Molyneaux <https://github.com/moggers87>`_
+        
+        Changelog
+        =========
+        
+        v2.2.1 (2016-03-06) Tikitu de Jager
+        -----------------------------------
+        
+        - Fix #14: Infinite loop on `return x / 1;`
+        
+        v2.2.0 (2015-12-19) Tikitu de Jager
+        -----------------------------------
+        
+        - Merge #13: Preserve "loud comments" starting with `/*!`
+        
+          These are commonly used for copyright notices, and are preserved by various
+          other minifiers (e.g. YUI Compressor).
+        
+        v2.1.6 (2015-10-14) Tikitu de Jager
+        -----------------------------------
+        
+        - Fix #12: Newline following a regex literal should not be elided.
+        
+        v2.1.5 (2015-10-11) Tikitu de Jager
+        -----------------------------------
+        
+        - Fix #9: Premature end of statement caused by multi-line comment not
+          adding newline.
+        
+        - Fix #10: Removing multiline comment separating tokens must leave a space.
+        
+        - Refactor comment handling for maintainability.
+        
+        v2.1.4 (2015-08-23) Tikitu de Jager
+        -----------------------------------
+        
+        - Fix #6: regex literal matching comment was not correctly matched.
+        
+        - Refactor regex literal handling for robustness.
+        
+        v2.1.3 (2015-08-09) Tikitu de Jager
+        -----------------------------------
+        
+        - Reset issue numbering: issues live in github from now on.
+        
+        - Fix #1: regex literal was not recognised when occurring directly after `{`.
+        
+        v2.1.2 (2015-07-12) Tikitu de Jager
+        -----------------------------------
+        
+        - Issue numbers here and below refer to the bitbucket repository.
+        
+        - Fix #17: bug when JS starts with comment then literal regex.
+        
+        v2.1.1 (2015-02-14) Tikitu de Jager
+        -----------------------------------
+        
+        - Fix #16: bug returning a literal regex containing escaped forward-slashes.
+        
+        v2.1.0 (2014-12-24) Tikitu de Jager
+        -----------------------------------
+        
+        - First changelog entries; see README.rst for prior contributors.
+        
+        - Expose quote_chars parameter to provide just enough unofficial Harmony
+          support to be useful.
+        
+        
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Environment :: Web Environment
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.6
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.2
+Classifier: Programming Language :: Python :: 3.3
+Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
+Classifier: Topic :: Software Development :: Pre-processors
+Classifier: Topic :: Text Processing :: Filters
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/jsmin/README.rst b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/jsmin/README.rst
new file mode 100644
index 0000000..e7a5755
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/jsmin/README.rst
@@ -0,0 +1,87 @@
+=====
+jsmin
+=====
+
+JavaScript minifier.
+
+Usage
+=====
+
+.. code:: python
+
+ from jsmin import jsmin
+ with open('myfile.js') as js_file:
+     minified = jsmin(js_file.read())
+
+You can run it as a commandline tool also::
+
+  python -m jsmin myfile.js
+
+NB: ``jsmin`` makes no attempt to be compatible with
+`ECMAScript 6 / ES.next / Harmony <http://wiki.ecmascript.org/doku.php?id=harmony:specification_drafts>`_.
+The current maintainer does not intend to add ES6-compatibility. If you would
+like to take over maintenance and update ``jsmin`` for ES6, please contact
+`Tikitu de Jager <mailto:tikitu+jsmin@logophile.org>`_. Pull requests are also
+welcome, of course, but my time to review them is somewhat limited these days.
+
+If you're using ``jsmin`` on ES6 code, though, you might find the ``quote_chars``
+parameter useful:
+
+.. code:: python
+
+ from jsmin import jsmin
+ with open('myfile.js') as js_file:
+     minified = jsmin(js_file.read(), quote_chars="'\"`")
+
+
+Where to get it
+===============
+
+* install the package `from pypi <https://pypi.python.org/pypi/jsmin/>`_
+* get the latest release `from latest-release on github <https://github.com/tikitu/jsmin/tree/latest-release/jsmin>`_
+* get the development version `from master on github <https://github.com/tikitu/jsmin/>`_
+
+Contributing
+============
+
+`Issues <https://github.com/tikitu/jsmin/issues>`_ and `Pull requests <https://github.com/tikitu/jsmin/pulls>`_
+will be gratefully received on Github. The project used to be hosted
+`on bitbucket <https://bitbucket.org/dcs/jsmin/>`_ and old issues can still be
+found there.
+
+If possible, please make separate pull requests for tests and for code: tests will be added to the `latest-release` branch while code will go to `master`.
+
+Unless you request otherwise, your Github identity will be added to the contributor's list below; if you prefer a
+different name feel free to add it in your pull request instead. (If you prefer not to be mentioned you'll have to let
+the maintainer know somehow.)
+
+Build/test status
+=================
+
+Both branches are tested with Travis: https://travis-ci.org/tikitu/jsmin
+
+The `latest-release` branch (the version on PyPI plus any new tests) is tested against CPython 2.6, 2.7, 3.2, and 3.3.
+Currently:
+
+.. image:: https://travis-ci.org/tikitu/jsmin.png?branch=latest-release
+
+If that branch is failing that means there's a new test that fails on *the latest released version on pypi*, with no fix yet
+released.
+
+The `master` branch (development version, might be ahead of latest released version) is tested against CPython 2.6, 2.7, 3.2, and
+3.3. Currently:
+
+.. image:: https://travis-ci.org/tikitu/jsmin.png?branch=master
+
+If `master` is failing don't use it, but as long as `latest-release` is passing the pypi release should be ok.
+
+Contributors (chronological commit order)
+=========================================
+
+* `Dave St.Germain <https://bitbucket.org/dcs>`_ (original author)
+* `Hans weltar <https://bitbucket.org/hansweltar>`_
+* `Tikitu de Jager <mailto:tikitu+jsmin@logophile.org>`_ (current maintainer)
+* https://bitbucket.org/rennat
+* `Nick Alexander <https://bitbucket.org/ncalexan>`_
+* `Gennady Kovshenin <https://github.com/soulseekah>`_
+* `Matt Molyneaux <https://github.com/moggers87>`_
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/jsmin/README.web-page-replay b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/jsmin/README.web-page-replay
new file mode 100644
index 0000000..96366fd
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/jsmin/README.web-page-replay
@@ -0,0 +1,13 @@
+Name: JavaScript minifier
+Short Name: jsmin
+URL: https://pypi.python.org/pypi/jsmin
+Version: 2.2.1
+License: MIT
+License File: LICENSE.txt
+
+Description:
+Used by Web Page Replay's script_injector module to minify injected JS scripts.
+
+Local Modifications:
+Remove jsmin/__main__.py & jsmin/test.py since they are not needed and contain
+no license header (https://github.com/tikitu/jsmin/issues/17).
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/jsmin/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/jsmin/__init__.py
new file mode 100644
index 0000000..d2fdf8c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/third_party/jsmin/__init__.py
@@ -0,0 +1,268 @@
+# This code is original from jsmin by Douglas Crockford, it was translated to
+# Python by Baruch Even. It was rewritten by Dave St.Germain for speed.
+#
+# The MIT License (MIT)
+# 
+# Copyright (c) 2013 Dave St.Germain
+# 
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+# 
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+# 
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+
+import sys
+is_3 = sys.version_info >= (3, 0)
+if is_3:
+    import io
+else:
+    import StringIO
+    try:
+        import cStringIO
+    except ImportError:
+        cStringIO = None
+
+
+__all__ = ['jsmin', 'JavascriptMinify']
+__version__ = '2.2.1'
+
+
+def jsmin(js, **kwargs):
+    """
+    returns a minified version of the javascript string
+    """
+    if not is_3:        
+        if cStringIO and not isinstance(js, unicode):
+            # strings can use cStringIO for a 3x performance
+            # improvement, but unicode (in python2) cannot
+            klass = cStringIO.StringIO
+        else:
+            klass = StringIO.StringIO
+    else:
+        klass = io.StringIO
+    ins = klass(js)
+    outs = klass()
+    JavascriptMinify(ins, outs, **kwargs).minify()
+    return outs.getvalue()
+
+
+class JavascriptMinify(object):
+    """
+    Minify an input stream of javascript, writing
+    to an output stream
+    """
+
+    def __init__(self, instream=None, outstream=None, quote_chars="'\""):
+        self.ins = instream
+        self.outs = outstream
+        self.quote_chars = quote_chars
+
+    def minify(self, instream=None, outstream=None):
+        if instream and outstream:
+            self.ins, self.outs = instream, outstream
+        
+        self.is_return = False
+        self.return_buf = ''
+        
+        def write(char):
+            # all of this is to support literal regular expressions.
+            # sigh
+            if char in 'return':
+                self.return_buf += char
+                self.is_return = self.return_buf == 'return'
+            else:
+                self.return_buf = ''
+                self.is_return = self.is_return and char < '!'
+            self.outs.write(char)
+            if self.is_return:
+                self.return_buf = ''
+
+        read = self.ins.read
+
+        space_strings = "abcdefghijklmnopqrstuvwxyz"\
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$\\"
+        self.space_strings = space_strings
+        starters, enders = '{[(+-', '}])+-/' + self.quote_chars
+        newlinestart_strings = starters + space_strings + self.quote_chars
+        newlineend_strings = enders + space_strings + self.quote_chars
+        self.newlinestart_strings = newlinestart_strings
+        self.newlineend_strings = newlineend_strings
+
+        do_newline = False
+        do_space = False
+        escape_slash_count = 0
+        in_quote = ''
+        quote_buf = []
+
+        previous = ';'
+        previous_non_space = ';'
+        next1 = read(1)
+
+        while next1:
+            next2 = read(1)
+            if in_quote:
+                quote_buf.append(next1)
+
+                if next1 == in_quote:
+                    numslashes = 0
+                    for c in reversed(quote_buf[:-1]):
+                        if c != '\\':
+                            break
+                        else:
+                            numslashes += 1
+                    if numslashes % 2 == 0:
+                        in_quote = ''
+                        write(''.join(quote_buf))
+            elif next1 in '\r\n':
+                next2, do_newline = self.newline(
+                    previous_non_space, next2, do_newline)
+            elif next1 < '!':
+                if (previous_non_space in space_strings \
+                    or previous_non_space > '~') \
+                    and (next2 in space_strings or next2 > '~'):
+                    do_space = True
+                elif previous_non_space in '-+' and next2 == previous_non_space:
+                    # protect against + ++ or - -- sequences
+                    do_space = True
+                elif self.is_return and next2 == '/':
+                    # returning a regex...
+                    write(' ')
+            elif next1 == '/':
+                if do_space:
+                    write(' ')
+                if next2 == '/':
+                    # Line comment: treat it as a newline, but skip it
+                    next2 = self.line_comment(next1, next2)
+                    next1 = '\n'
+                    next2, do_newline = self.newline(
+                        previous_non_space, next2, do_newline)
+                elif next2 == '*':
+                    self.block_comment(next1, next2)
+                    next2 = read(1)
+                    if previous_non_space in space_strings:
+                        do_space = True
+                    next1 = previous
+                else:
+                    if previous_non_space in '{(,=:[?!&|;' or self.is_return:
+                        self.regex_literal(next1, next2)
+                        # hackish: after regex literal next1 is still /
+                        # (it was the initial /, now it's the last /)
+                        next2 = read(1)
+                    else:
+                        write('/')
+            else:
+                if do_newline:
+                    write('\n')
+                    do_newline = False
+                    do_space = False
+                if do_space:
+                    do_space = False
+                    write(' ')
+
+                write(next1)
+                if next1 in self.quote_chars:
+                    in_quote = next1
+                    quote_buf = []
+
+            if next1 >= '!':
+                previous_non_space = next1
+
+            if next1 == '\\':
+                escape_slash_count += 1
+            else:
+                escape_slash_count = 0
+
+            previous = next1
+            next1 = next2
+
+    def regex_literal(self, next1, next2):
+        assert next1 == '/'  # otherwise we should not be called!
+
+        self.return_buf = ''
+
+        read = self.ins.read
+        write = self.outs.write
+
+        in_char_class = False
+
+        write('/')
+
+        next = next2
+        while next and (next != '/' or in_char_class):
+            write(next)
+            if next == '\\':
+                write(read(1))  # whatever is next is escaped
+            elif next == '[':
+                write(read(1))  # character class cannot be empty
+                in_char_class = True
+            elif next == ']':
+                in_char_class = False
+            next = read(1)
+
+        write('/')
+
+    def line_comment(self, next1, next2):
+        assert next1 == next2 == '/'
+
+        read = self.ins.read
+
+        while next1 and next1 not in '\r\n':
+            next1 = read(1)
+        while next1 and next1 in '\r\n':
+            next1 = read(1)
+
+        return next1
+
+    def block_comment(self, next1, next2):
+        assert next1 == '/'
+        assert next2 == '*'
+
+        read = self.ins.read
+
+        # Skip past first /* and avoid catching on /*/...*/
+        next1 = read(1)
+        next2 = read(1)
+
+        comment_buffer = '/*'
+        while next1 != '*' or next2 != '/':
+            comment_buffer += next1
+            next1 = next2
+            next2 = read(1)
+
+        if comment_buffer.startswith("/*!"):
+            # comment needs preserving
+            self.outs.write(comment_buffer)
+            self.outs.write("*/\n")
+
+
+    def newline(self, previous_non_space, next2, do_newline):
+        read = self.ins.read
+
+        if previous_non_space and (
+                        previous_non_space in self.newlineend_strings
+                        or previous_non_space > '~'):
+            while 1:
+                if next2 < '!':
+                    next2 = read(1)
+                    if not next2:
+                        break
+                else:
+                    if next2 in self.newlinestart_strings \
+                            or next2 > '~' or next2 == '/':
+                        do_newline = True
+                    break
+
+        return next2, do_newline
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/trafficshaper.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/trafficshaper.py
new file mode 100644
index 0000000..0078218
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/trafficshaper.py
@@ -0,0 +1,186 @@
+#!/usr/bin/env python
+# Copyright 2010 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging
+import platformsettings
+import re
+
+
+# Mac has broken bandwitdh parsing, so double check the values.
+# On Mac OS X 10.6, "KBit/s" actually uses "KByte/s".
+BANDWIDTH_PATTERN = r'0|\d+[KM]?(bit|Byte)/s'
+
+
+class TrafficShaperException(Exception):
+  pass
+
+
+class BandwidthValueError(TrafficShaperException):
+  def __init__(self, value):  # pylint: disable=super-init-not-called
+    self.value = value
+
+  def __str__(self):
+    return 'Value, "%s", does not match regex: %s' % (
+        self.value, BANDWIDTH_PATTERN)
+
+
+class TrafficShaper(object):
+  """Manages network traffic shaping."""
+
+  # Pick webpagetest-compatible values (details: http://goo.gl/oghTg).
+  _UPLOAD_PIPE = '10'      # Enforces overall upload bandwidth.
+  _UPLOAD_QUEUE = '10'     # Shares upload bandwidth among source ports.
+  _UPLOAD_RULE = '5000'    # Specifies when the upload queue is used.
+  _DOWNLOAD_PIPE = '11'    # Enforces overall download bandwidth.
+  _DOWNLOAD_QUEUE = '11'   # Shares download bandwidth among destination ports.
+  _DOWNLOAD_RULE = '5100'  # Specifies when the download queue is used.
+  _QUEUE_SLOTS = 100       # Number of packets to queue.
+
+  _BANDWIDTH_RE = re.compile(BANDWIDTH_PATTERN)
+
+  def __init__(self,
+               dont_use=None,
+               host='127.0.0.1',
+               ports=None,
+               up_bandwidth='0',
+               down_bandwidth='0',
+               delay_ms='0',
+               packet_loss_rate='0',
+               init_cwnd='0',
+               use_loopback=True):
+    """Start shaping traffic.
+
+    Args:
+      host: a host string (name or IP) for the web proxy.
+      ports: a list of ports to shape traffic on.
+      up_bandwidth: Upload bandwidth
+      down_bandwidth: Download bandwidth
+           Bandwidths measured in [K|M]{bit/s|Byte/s}. '0' means unlimited.
+      delay_ms: Propagation delay in milliseconds. '0' means no delay.
+      packet_loss_rate: Packet loss rate in range [0..1]. '0' means no loss.
+      init_cwnd: the initial cwnd setting. '0' means no change.
+      use_loopback: True iff shaping is done on the loopback (or equiv) adapter.
+    """
+    assert dont_use is None  # Force args to be named.
+    self.host = host
+    self.ports = ports
+    self.up_bandwidth = up_bandwidth
+    self.down_bandwidth = down_bandwidth
+    self.delay_ms = delay_ms
+    self.packet_loss_rate = packet_loss_rate
+    self.init_cwnd = init_cwnd
+    self.use_loopback = use_loopback
+    if not self._BANDWIDTH_RE.match(self.up_bandwidth):
+      raise BandwidthValueError(self.up_bandwidth)
+    if not self._BANDWIDTH_RE.match(self.down_bandwidth):
+      raise BandwidthValueError(self.down_bandwidth)
+    self.is_shaping = False
+
+  def __enter__(self):
+    if self.use_loopback:
+      platformsettings.setup_temporary_loopback_config()
+    if self.init_cwnd != '0':
+      platformsettings.set_temporary_tcp_init_cwnd(self.init_cwnd)
+    try:
+      ipfw_list = platformsettings.ipfw('list')
+      if not ipfw_list.startswith('65535 '):
+        logging.warn('ipfw has existing rules:\n%s', ipfw_list)
+        self._delete_rules(ipfw_list)
+    except Exception:
+      pass
+    if (self.up_bandwidth == '0' and self.down_bandwidth == '0' and
+        self.delay_ms == '0' and self.packet_loss_rate == '0'):
+      logging.info('Skipped shaping traffic.')
+      return
+    if not self.ports:
+      raise TrafficShaperException('No ports on which to shape traffic.')
+
+    ports = ','.join(str(p) for p in self.ports)
+    half_delay_ms = int(self.delay_ms) / 2  # split over up/down links
+
+    try:
+      # Configure upload shaping.
+      platformsettings.ipfw(
+          'pipe', self._UPLOAD_PIPE,
+          'config',
+          'bw', self.up_bandwidth,
+          'delay', half_delay_ms,
+          )
+      platformsettings.ipfw(
+          'queue', self._UPLOAD_QUEUE,
+          'config',
+          'pipe', self._UPLOAD_PIPE,
+          'plr', self.packet_loss_rate,
+          'queue', self._QUEUE_SLOTS,
+          'mask', 'src-port', '0xffff',
+          )
+      platformsettings.ipfw(
+          'add', self._UPLOAD_RULE,
+          'queue', self._UPLOAD_QUEUE,
+          'ip',
+          'from', 'any',
+          'to', self.host,
+          self.use_loopback and 'out' or 'in',
+          'dst-port', ports,
+          )
+      self.is_shaping = True
+
+      # Configure download shaping.
+      platformsettings.ipfw(
+          'pipe', self._DOWNLOAD_PIPE,
+          'config',
+          'bw', self.down_bandwidth,
+          'delay', half_delay_ms,
+          )
+      platformsettings.ipfw(
+          'queue', self._DOWNLOAD_QUEUE,
+          'config',
+          'pipe', self._DOWNLOAD_PIPE,
+          'plr', self.packet_loss_rate,
+          'queue', self._QUEUE_SLOTS,
+          'mask', 'dst-port', '0xffff',
+          )
+      platformsettings.ipfw(
+          'add', self._DOWNLOAD_RULE,
+          'queue', self._DOWNLOAD_QUEUE,
+          'ip',
+          'from', self.host,
+          'to', 'any',
+          'out',
+          'src-port', ports,
+          )
+      logging.info('Started shaping traffic')
+    except Exception:
+      logging.error('Unable to shape traffic.')
+      raise
+
+  def __exit__(self, unused_exc_type, unused_exc_val, unused_exc_tb):
+    if self.is_shaping:
+      try:
+        self._delete_rules()
+        logging.info('Stopped shaping traffic')
+      except Exception:
+        logging.error('Unable to stop shaping traffic.')
+        raise
+
+  def _delete_rules(self, ipfw_list=None):
+    if ipfw_list is None:
+      ipfw_list = platformsettings.ipfw('list')
+    existing_rules = set(
+        r.split()[0].lstrip('0') for r in ipfw_list.splitlines())
+    delete_rules = [r for r in (self._DOWNLOAD_RULE, self._UPLOAD_RULE)
+                    if r in existing_rules]
+    if delete_rules:
+      platformsettings.ipfw('delete', *delete_rules)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/trafficshaper_test.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/trafficshaper_test.py
new file mode 100755
index 0000000..525446d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/trafficshaper_test.py
@@ -0,0 +1,277 @@
+#!/usr/bin/env python
+# Copyright 2011 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""System integration test for traffic shaping.
+
+Usage:
+$ sudo ./trafficshaper_test.py
+"""
+
+import daemonserver
+import logging
+import platformsettings
+import socket
+import SocketServer
+import trafficshaper
+import unittest
+
+RESPONSE_SIZE_KEY = 'response-size:'
+TEST_DNS_PORT = 5555
+TEST_HTTP_PORT = 8888
+TIMER = platformsettings.timer
+
+
+def GetElapsedMs(start_time, end_time):
+  """Return milliseconds elapsed between |start_time| and |end_time|.
+
+  Args:
+    start_time: seconds as a float (or string representation of float).
+    end_time: seconds as a float (or string representation of float).
+  Return:
+    milliseconds elapsed as integer.
+  """
+  return int((float(end_time) - float(start_time)) * 1000)
+
+
+class TrafficShaperTest(unittest.TestCase):
+
+  def testBadBandwidthRaises(self):
+    self.assertRaises(trafficshaper.BandwidthValueError,
+                      trafficshaper.TrafficShaper,
+                      down_bandwidth='1KBit/s')
+
+
+class TimedUdpHandler(SocketServer.DatagramRequestHandler):
+  """UDP handler that returns the time when the request was handled."""
+
+  def handle(self):
+    data = self.rfile.read()
+    read_time = self.server.timer()
+    self.wfile.write(str(read_time))
+
+
+class TimedTcpHandler(SocketServer.StreamRequestHandler):
+  """Tcp handler that returns the time when the request was read.
+
+  It can respond with the number of bytes specified in the request.
+  The request looks like:
+    request_data -> RESPONSE_SIZE_KEY num_response_bytes '\n' ANY_DATA
+  """
+
+  def handle(self):
+    data = self.rfile.read()
+    read_time = self.server.timer()
+    contents = str(read_time)
+    if data.startswith(RESPONSE_SIZE_KEY):
+      num_response_bytes = int(data[len(RESPONSE_SIZE_KEY):data.index('\n')])
+      contents = '%s\n%s' % (contents,
+                             '\x00' * (num_response_bytes - len(contents) - 1))
+    self.wfile.write(contents)
+
+
+class TimedUdpServer(SocketServer.ThreadingUDPServer,
+                     daemonserver.DaemonServer):
+  """A simple UDP server similar to dnsproxy."""
+
+  # Override SocketServer.TcpServer setting to avoid intermittent errors.
+  allow_reuse_address = True
+
+  def __init__(self, host, port, timer=TIMER):
+    SocketServer.ThreadingUDPServer.__init__(
+        self, (host, port), TimedUdpHandler)
+    self.timer = timer
+
+  def cleanup(self):
+    pass
+
+
+class TimedTcpServer(SocketServer.ThreadingTCPServer,
+                     daemonserver.DaemonServer):
+  """A simple TCP server similar to httpproxy."""
+
+  # Override SocketServer.TcpServer setting to avoid intermittent errors.
+  allow_reuse_address = True
+
+  def __init__(self, host, port, timer=TIMER):
+    SocketServer.ThreadingTCPServer.__init__(
+        self, (host, port), TimedTcpHandler)
+    self.timer = timer
+
+  def cleanup(self):
+    try:
+      self.shutdown()
+    except KeyboardInterrupt, e:
+      pass
+
+
+class TcpTestSocketCreator(object):
+  """A TCP socket creator suitable for with-statement."""
+
+  def __init__(self, host, port, timeout=1.0):
+    self.address = (host, port)
+    self.timeout = timeout
+
+  def __enter__(self):
+    self.socket = socket.create_connection(self.address, timeout=self.timeout)
+    return self.socket
+
+  def __exit__(self, *args):
+    self.socket.close()
+
+
+class TimedTestCase(unittest.TestCase):
+  def assertValuesAlmostEqual(self, expected, actual, tolerance=0.05):
+    """Like the following with nicer default message:
+           assertTrue(expected <= actual + tolerance &&
+                      expected >= actual - tolerance)
+    """
+    delta = tolerance * expected
+    if actual > expected + delta or actual < expected - delta:
+      self.fail('%s is not equal to expected %s +/- %s%%' % (
+              actual, expected, 100 * tolerance))
+
+
+class TcpTrafficShaperTest(TimedTestCase):
+
+  def setUp(self):
+    self.host = platformsettings.get_server_ip_address()
+    self.port = TEST_HTTP_PORT
+    self.tcp_socket_creator = TcpTestSocketCreator(self.host, self.port)
+    self.timer = TIMER
+
+  def TrafficShaper(self, **kwargs):
+    return trafficshaper.TrafficShaper(
+        host=self.host, ports=(self.port,), **kwargs)
+
+  def GetTcpSendTimeMs(self, num_bytes):
+    """Return time in milliseconds to send |num_bytes|."""
+
+    with self.tcp_socket_creator as s:
+      start_time = self.timer()
+      request_data = '\x00' * num_bytes
+
+      s.sendall(request_data)
+      # TODO(slamm): Figure out why partial is shutdown needed to make it work.
+      s.shutdown(socket.SHUT_WR)
+      read_time = s.recv(1024)
+    return GetElapsedMs(start_time, read_time)
+
+  def GetTcpReceiveTimeMs(self, num_bytes):
+    """Return time in milliseconds to receive |num_bytes|."""
+
+    with self.tcp_socket_creator as s:
+      s.sendall('%s%s\n' % (RESPONSE_SIZE_KEY, num_bytes))
+      # TODO(slamm): Figure out why partial is shutdown needed to make it work.
+      s.shutdown(socket.SHUT_WR)
+      num_remaining_bytes = num_bytes
+      read_time = None
+      while num_remaining_bytes > 0:
+        response_data = s.recv(4096)
+        num_remaining_bytes -= len(response_data)
+        if not read_time:
+          read_time, padding = response_data.split('\n')
+    return GetElapsedMs(read_time, self.timer())
+
+  def testTcpConnectToIp(self):
+    """Verify that it takes |delay_ms| to establish a TCP connection."""
+    if not platformsettings.has_ipfw():
+      logging.warning('ipfw is not available in path. Skip the test')
+      return
+    with TimedTcpServer(self.host, self.port):
+      for delay_ms in (100, 175):
+        with self.TrafficShaper(delay_ms=delay_ms):
+          start_time = self.timer()
+          with self.tcp_socket_creator:
+            connect_time = GetElapsedMs(start_time, self.timer())
+        self.assertValuesAlmostEqual(delay_ms, connect_time, tolerance=0.12)
+
+  def testTcpUploadShaping(self):
+    """Verify that 'up' bandwidth is shaped on TCP connections."""
+    if not platformsettings.has_ipfw():
+      logging.warning('ipfw is not available in path. Skip the test')
+      return
+    num_bytes = 1024 * 100
+    bandwidth_kbits = 2000
+    expected_ms = 8.0 * num_bytes / bandwidth_kbits
+    with TimedTcpServer(self.host, self.port):
+      with self.TrafficShaper(up_bandwidth='%sKbit/s' % bandwidth_kbits):
+        self.assertValuesAlmostEqual(expected_ms, self.GetTcpSendTimeMs(num_bytes))
+
+  def testTcpDownloadShaping(self):
+    """Verify that 'down' bandwidth is shaped on TCP connections."""
+    if not platformsettings.has_ipfw():
+      logging.warning('ipfw is not available in path. Skip the test')
+      return
+    num_bytes = 1024 * 100
+    bandwidth_kbits = 2000
+    expected_ms = 8.0 * num_bytes / bandwidth_kbits
+    with TimedTcpServer(self.host, self.port):
+      with self.TrafficShaper(down_bandwidth='%sKbit/s' % bandwidth_kbits):
+        self.assertValuesAlmostEqual(expected_ms, self.GetTcpReceiveTimeMs(num_bytes))
+
+  def testTcpInterleavedDownloads(self):
+    # TODO(slamm): write tcp interleaved downloads test
+    pass
+
+
+class UdpTrafficShaperTest(TimedTestCase):
+
+  def setUp(self):
+    self.host = platformsettings.get_server_ip_address()
+    self.dns_port = TEST_DNS_PORT
+    self.timer = TIMER
+
+  def TrafficShaper(self, **kwargs):
+    return trafficshaper.TrafficShaper(
+        host=self.host, ports=(self.dns_port,), **kwargs)
+
+  def GetUdpSendReceiveTimesMs(self):
+    """Return time in milliseconds to send |num_bytes|."""
+    start_time = self.timer()
+    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+    udp_socket.sendto('test data\n', (self.host, self.dns_port))
+    read_time = udp_socket.recv(1024)
+    return (GetElapsedMs(start_time, read_time),
+            GetElapsedMs(read_time, self.timer()))
+
+  def testUdpDelay(self):
+    if not platformsettings.has_ipfw():
+      logging.warning('ipfw is not available in path. Skip the test')
+      return
+    for delay_ms in (100, 170):
+      expected_ms = delay_ms / 2
+      with TimedUdpServer(self.host, self.dns_port):
+        with self.TrafficShaper(delay_ms=delay_ms):
+          send_ms, receive_ms = self.GetUdpSendReceiveTimesMs()
+          self.assertValuesAlmostEqual(expected_ms, send_ms, tolerance=0.10)
+          self.assertValuesAlmostEqual(expected_ms, receive_ms, tolerance=0.10)
+
+
+  def testUdpInterleavedDelay(self):
+    # TODO(slamm): write udp interleaved udp delay test
+    pass
+
+
+class TcpAndUdpTrafficShaperTest(TimedTestCase):
+  # TODO(slamm): Test concurrent TCP and UDP traffic
+  pass
+
+
+# TODO(slamm): Packet loss rate (try different ports)
+
+
+if __name__ == '__main__':
+  #logging.getLogger().setLevel(logging.DEBUG)
+  unittest.main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/util.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/util.py
new file mode 100644
index 0000000..419d232
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/util.py
@@ -0,0 +1,95 @@
+#!/usr/bin/env python
+# Copyright 2012 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+"""Miscellaneous utility functions."""
+
+import inspect
+import logging
+import time
+
+try:
+  # pkg_resources (part of setuptools) is needed when WPR is
+  # distributed as a package. (Resources may need to be extracted from
+  # the package.)
+
+  import pkg_resources
+
+  def resource_exists(resource_name):
+    return pkg_resources.resource_exists(__name__, resource_name)
+
+  def resource_string(resource_name):
+    return pkg_resources.resource_string(__name__, resource_name)
+
+except ImportError:
+  # Import of pkg_resources failed, so fall back to getting resources
+  # from the file system.
+
+  import os
+
+  def _resource_path(resource_name):
+    _replay_dir = os.path.dirname(os.path.abspath(__file__))
+    return os.path.join(_replay_dir, resource_name)
+
+  def resource_exists(resource_name):
+    return os.path.exists(_resource_path(resource_name))
+
+  def resource_string(resource_name):
+    return open(_resource_path(resource_name)).read()
+
+
+class TimeoutException(Exception):
+  pass
+
+
+def WaitFor(condition, timeout):
+  """Waits for up to |timeout| secs for the function |condition| to return True.
+
+  Polling frequency is (elapsed_time / 10), with a min of .1s and max of 5s.
+
+  Returns:
+    Result of |condition| function (if present).
+  """
+  min_poll_interval = 0.1
+  max_poll_interval = 5
+  output_interval = 300
+
+  def GetConditionString():
+    if condition.__name__ == '<lambda>':
+      try:
+        return inspect.getsource(condition).strip()
+      except IOError:
+        pass
+    return condition.__name__
+
+  start_time = time.time()
+  last_output_time = start_time
+  while True:
+    res = condition()
+    if res:
+      return res
+    now = time.time()
+    elapsed_time = now - start_time
+    last_output_elapsed_time = now - last_output_time
+    if elapsed_time > timeout:
+      raise TimeoutException('Timed out while waiting %ds for %s.' %
+                                        (timeout, GetConditionString()))
+    if last_output_elapsed_time > output_interval:
+      logging.info('Continuing to wait %ds for %s. Elapsed: %ds.',
+                   timeout, GetConditionString(), elapsed_time)
+      last_output_time = time.time()
+    poll_interval = min(max(elapsed_time / 10., min_poll_interval),
+                        max_poll_interval)
+    time.sleep(poll_interval)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/wpr_cert.pem b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/wpr_cert.pem
new file mode 100644
index 0000000..773bfea
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/web-page-replay/wpr_cert.pem
@@ -0,0 +1,31 @@
+-----BEGIN PRIVATE KEY-----
+MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALLOoY1ENN78dMvO
+33tSElddaaPFV4ooXJKbCqWsEurkKJjtoHxnri54rgNOLSU4O8aQytWE9zq6Ei/c
+qK7dl6UqZiZtwk4HVrBL786cVKlgpPkhcnG+EMdDxy/PyMD38jNtTNMJLiwbxiZq
+Mo3vjZ/4+1XLdNisAWTDLaWeXxmbAgMBAAECgYAadwLqScIZjvwqfkANnKQiUi0k
+lDzUsgyhllkJFGLoaUSo/eLXBvF851e6HYQJEj2msh+TYs7E3m16sAo3d4zOIdnz
+VwOF0SVuUveqJz6K1/k6nPxck+dPj8Mi+gBm3Fd0+0wcozjWaxhx3f462HCUb6b+
+ZpJRBsbyvzu6rn7iQQJBAOlWhtfL8r9+Kl0vxRD1XukaJwlxPv24JhfKOU4z8WlJ
+WX7Wr8ws+xKS+CtfFnjkf/iFJPpTb8jxpQyWMJzYZIkCQQDELE5hGnBFVQArMAOp
+VbwYordTrVY3AagO4tDJ6T3a7GEXE28ol16/i02+4FLd65vubL21IuX0exH/eRvZ
+Q4wDAkEAub/qyiEOFkjOWq5rd0uNiY0LJGYlWf7dPDT8l3ecJ09/0gv/mE76c9fR
+fV1N22EzSlhbjncbVuCenj11Z3aP2QJAILtfzJXzu63GHG6jfcKfYuDrg9u9Mepl
+1y4DNl1jg77DKG2Gs5gmKAGfVETrrrmcR/j+4lVTVyqdwym6+tJpbwJBAN3vixxc
+5N9pUMDfFnHrx/x9QPd0JgSAT21KSIB+PndlbD7QO6nwFhQNNcTYt2D4VWPVo1vg
+lOraHyFakb7NqEA=
+-----END PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+MIICWDCCAcGgAwIBAgIJAIt1sARz1phuMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
+BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
+aWRnaXRzIFB0eSBMdGQwHhcNMTIxMDIzMjA1NzA0WhcNMjIxMDIxMjA1NzA0WjBF
+MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
+ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
+gQCyzqGNRDTe/HTLzt97UhJXXWmjxVeKKFySmwqlrBLq5CiY7aB8Z64ueK4DTi0l
+ODvGkMrVhPc6uhIv3Kiu3ZelKmYmbcJOB1awS+/OnFSpYKT5IXJxvhDHQ8cvz8jA
+9/IzbUzTCS4sG8YmajKN742f+PtVy3TYrAFkwy2lnl8ZmwIDAQABo1AwTjAdBgNV
+HQ4EFgQUyihF4Nbabk9aBDOOxMVDLRLiqMEwHwYDVR0jBBgwFoAUyihF4Nbabk9a
+BDOOxMVDLRLiqMEwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQCIrYif
+q4YQ7A8zy8PB1wKOyMy50r7IokO8hplbjRwVCQwbLnRM74LPp84caEdwaCJvAaP9
+QWjVz6p3K2sK9iHaidQZ/9xqJPoZrYttQCcQUweaASp04Y9WwTA4EPeDZLgp4PgU
+SV/mvKRGimmce6LMxFlPiZio/IDiUNXVJ7j/sg==
+-----END CERTIFICATE-----
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/ChangeLog b/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/ChangeLog
new file mode 100644
index 0000000..b26e153
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/ChangeLog
@@ -0,0 +1,253 @@
+ChangeLog
+============
+
+- 0.41.0
+
+  - move to repository to https://github.com/websocket-client/websocket-client.git
+  - _send_ping warning fails due to missing reference in _logging.__all__ (#294)
+
+- 0.40.0
+  - Fix opcode -> op_code (#286)
+
+- 0.39.0
+  - Shuffled around example code (#256)
+  - _send_ping graceful error handling (#262)
+  - Allow closing WebSocketApp with status/reason/timeout (#265)
+  - Support universal wheels (#267)
+  - _url: Added subnet IP address matching in no_proxy host detection (#270)
+  - fixed Incorrect encoding in continued messages python3 (#261)
+  - Pass headers for websocket handshake (#271)
+  - setup.py: Import `logging` before calling it. (#272)
+  - Implemented close code 1014 (#273)
+  - Support CA bundle specified by environment variable (#279)
+  - Response header values should not be converted to lower case (#264)
+
+- 0.38.0
+  - Exclude port 443 from host http header (#248)
+  - Cleanup code (#249)
+  - Modify a code block directive in README (#250)
+  - fixed ping/pong timeouet (#253)
+
+- 0.37.0
+  - fixed failure that `websocket.create_connection` does not accept `origin` as a parameter (#246 )
+
+- 0.36.0
+  - added support for using custom connection class (#235)
+  - use Named logger (#238)
+  - implement ping/pong timeout (#241)
+  - Corrects the syntax highlight code (#243)
+  - fixed failure to join thread before it is started (#242)
+
+- 0.35.0
+  - Prints timings in console (#217)
+  - use inspect.getfullargspec with Python 3.x (#219)
+  - Check that exception message is actually a string before trying for substring check (#224)
+  - Use pre-initialized stream socket (#226)
+  - fixed TypeError: cafile, capath and cadata cannot be all omitted (#227)
+
+- 0.34.0
+
+  - Change import style (#203)
+  - fix attribute error on the older python. (#215)
+
+- 0.33.0
+
+  - fixed timeout+ssl error handling bug  on python 2.7.10 (#190)
+  - add proxy support to wsdump.py (#194)
+  - use wsaccel if available (#193)
+  - add support for ssl cert chains to support client certs (#195)
+  - fix string formatting in exception (#196)
+  - fix typo in README.rst (#197)
+  - introduce on_data callback to pass data type. (#198)
+  - WebSocketBadStatusException for Handshake error (#199)
+  - set close timeout (#192)
+  - Map dict to headers list (#204)
+  - support client certification (#207)
+  - security improvement during handshake (#211)
+  - improve logging of error from callback (#212)
+
+- 0.32.0
+
+  - fix http proxy bug (#189)
+
+- 0.31.0
+
+  - Avoid deprecated BaseException.message (#180)
+  - Add travis builds (#182)
+  - fixed wsdump to work with piped input (#183)
+  - fixed output of wsdump.py with python3 (#185)
+  - add raw mode to wsdump.py (#186)
+
+- 0.30.0
+
+  - fixed if client is behind proxy (#169)
+  - support SNI for python 2.7.9+ and 3.2+ (#172)
+  - update Host HTTP header by user. (#171)
+  - fix typo for isEnabledFor (#173)
+  - can set verify_mode to CERT_NONE when check_hostname is enabled.(#175)
+  - make websockets iterable (#178)
+
+- 0.29.0
+
+  - fixed ssl socket bug
+
+- 0.28.0
+
+  - Fix erroneous argument shadowing(#168)
+
+- 0.27.0
+
+  - remove unittest2 requirements for python 2.6 (#156)
+  - fixed subprotocol case during header validation (#158)
+  - get response status and headers (#160)
+  - fix out-of-memory due to fragmentation when receiving a very large frame(#163)
+  - fix error if the payload data is nothing.(#166)
+  - refactoring.
+
+- 0.26.0
+
+  - all WebSocketException provide message string (#152)
+  - fixed tests fail when not connected to the network (#155)
+  - Add command line options and handle closed socket to wsdump.py (#153)
+
+- 0.25.0
+
+  - fixed for Python 2.6(#151)
+
+- 0.24.0
+
+  - Supporting http-basic auth in WebSocketApp (#143)
+  - fix failure of test.testInternalRecvStrict(#141)
+  - skip utf8 validation by skip_utf8_validation argument (#137)
+  - WebsocketProxyException will be raised if we got error about proxy.(#138)
+
+- 0.23.0
+
+  - Remove spurious print statement. (#135)
+
+- 0.22.0
+
+  - Fix not thread-safe of Websocket.close() (#120)
+  - Try to get proxy info from environment if not explicitly provided (#124)
+  - support proxy basic authentication. (#125)
+  - Fix NoneType exception at WebsocketApp.send (#126)
+  - not use proxy for localhost (#132)
+
+- 0.21.0
+
+  - Check for socket before attempting to close (#115)
+  - Enable turning off SSL verification in wsdump.py(#116)
+  - Enable to set subprotocol(#118)
+  - Better support for Autobahn test suite (http://autobahn.ws/testsuite) (#117)
+
+- v0.20.0
+
+  - fix typo.
+
+- v0.19.0
+
+  - suppress close event message(#107)
+  - detect socket connection state(#109)
+  - support for code and reason in on_close callback(#111)
+  - continuation frame handling seems suspicious(#113)
+
+- v0.18.0
+
+  -  allow override of match_hostname usage on ssl (#105)
+
+- v0.17.0
+
+  - can't set timeout on a standing websocket connection (#102)
+  - fixed local variable 'error' referenced before assignment (#102, #98)
+
+- v0.16.0
+
+  - lock some method for multithread. (#92)
+  - disable cert verification. (#89)
+
+- v0.15.0
+
+  - fixed exception when send a large message (#84)
+
+- v0.14.1
+
+  - fixed to work on Python2.6 (#83)
+
+- v0.14.0
+
+  - Support python 3(#73)
+  - Support IPv6(#77)
+  - Support explicit web proxy(#57)
+  - specify cookie in connect method option(#82)
+
+- v0.13.0
+
+  - MemoryError when receiving large amount of data (~60 MB) at once(ISSUE#59)
+  - Controlling fragmentation(ISSUE#55)
+  - server certificate validation(ISSUE#56)
+  - PyPI tarball is missing test_websocket.py(ISSUE#65)
+  - Payload length encoding bug(ISSUE#58)
+  - disable Nagle algorithm by default(ISSUE#41)
+  - Better event loop in WebSocketApp(ISSUE#63)
+  - Skip tests that require Internet access by default(ISSUE#66)
+
+- v0.12.0
+
+  - support keep alive for WebSocketApp(ISSUE#34)
+  - fix some SSL bugs(ISSUE#35, #36)
+  - fix "Timing out leaves websocket library in bad state"(ISSUE#37)
+  - fix "WebSocketApp.run_with_no_err() silently eats all exceptions"(ISSUE#38)
+  - WebSocketTimeoutException will be raised for ws/wss timeout(ISSUE#40)
+  - improve wsdump message(ISSUE#42)
+  - support fragmentation message(ISSUE#43)
+  - fix some bugs
+
+- v0.11.0
+
+  - Only log non-normal close status(ISSUE#31)
+  - Fix default Origin isn't URI(ISSUE#32)
+  - fileno support(ISSUE#33)
+
+- v0.10.0
+
+  - allow to set HTTP Header to WebSocketApp(ISSUE#27)
+  - fix typo in pydoc(ISSUE#28)
+  - Passing a socketopt flag to the websocket constructor(ISSUE#29)
+  - websocket.send fails with long data(ISSUE#30)
+
+
+- v0.9.0
+
+  - allow to set opcode in WebSocketApp.send(ISSUE#25)
+  - allow to modify Origin(ISSUE#26)
+
+- v0.8.0
+
+  - many bug fix
+  - some performance improvement
+
+- v0.7.0
+
+  - fixed problem to read long data.(ISSUE#12)
+  - fix buffer size boundary violation
+
+- v0.6.0
+
+  - Patches: UUID4, self.keep_running, mask_key (ISSUE#11)
+  - add wsdump.py tool
+
+- v0.5.2
+
+  - fix Echo App Demo Throw Error: 'NoneType' object has no attribute 'opcode  (ISSUE#10)
+
+- v0.5.1
+
+  - delete invalid print statement.
+
+- v0.5.0
+
+  - support hybi-13 protocol.
+
+- v0.4.1
+
+  - fix incorrect custom header order(ISSUE#1)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/LICENSE b/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/LICENSE
new file mode 100644
index 0000000..31afd6d
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/LICENSE
@@ -0,0 +1,165 @@
+                  GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+  This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+  0. Additional Definitions.
+
+  As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+  "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+  An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+  A "Combined Work" is a work produced by combining or linking an
+Application with the Library.  The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+  The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+  The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+  1. Exception to Section 3 of the GNU GPL.
+
+  You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+  2. Conveying Modified Versions.
+
+  If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+   a) under this License, provided that you make a good faith effort to
+   ensure that, in the event an Application does not supply the
+   function or data, the facility still operates, and performs
+   whatever part of its purpose remains meaningful, or
+
+   b) under the GNU GPL, with none of the additional permissions of
+   this License applicable to that copy.
+
+  3. Object Code Incorporating Material from Library Header Files.
+
+  The object code form of an Application may incorporate material from
+a header file that is part of the Library.  You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+   a) Give prominent notice with each copy of the object code that the
+   Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the object code with a copy of the GNU GPL and this license
+   document.
+
+  4. Combined Works.
+
+  You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+   a) Give prominent notice with each copy of the Combined Work that
+   the Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the Combined Work with a copy of the GNU GPL and this license
+   document.
+
+   c) For a Combined Work that displays copyright notices during
+   execution, include the copyright notice for the Library among
+   these notices, as well as a reference directing the user to the
+   copies of the GNU GPL and this license document.
+
+   d) Do one of the following:
+
+       0) Convey the Minimal Corresponding Source under the terms of this
+       License, and the Corresponding Application Code in a form
+       suitable for, and under terms that permit, the user to
+       recombine or relink the Application with a modified version of
+       the Linked Version to produce a modified Combined Work, in the
+       manner specified by section 6 of the GNU GPL for conveying
+       Corresponding Source.
+
+       1) Use a suitable shared library mechanism for linking with the
+       Library.  A suitable mechanism is one that (a) uses at run time
+       a copy of the Library already present on the user's computer
+       system, and (b) will operate properly with a modified version
+       of the Library that is interface-compatible with the Linked
+       Version.
+
+   e) Provide Installation Information, but only if you would otherwise
+   be required to provide such information under section 6 of the
+   GNU GPL, and only to the extent that such information is
+   necessary to install and execute a modified version of the
+   Combined Work produced by recombining or relinking the
+   Application with a modified version of the Linked Version. (If
+   you use option 4d0, the Installation Information must accompany
+   the Minimal Corresponding Source and Corresponding Application
+   Code. If you use option 4d1, you must provide the Installation
+   Information in the manner specified by section 6 of the GNU GPL
+   for conveying Corresponding Source.)
+
+  5. Combined Libraries.
+
+  You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+   a) Accompany the combined library with a copy of the same work based
+   on the Library, uncombined with any other library facilities,
+   conveyed under the terms of this License.
+
+   b) Give prominent notice with the combined library that part of it
+   is a work based on the Library, and explaining where to find the
+   accompanying uncombined form of the same work.
+
+  6. Revised Versions of the GNU Lesser General Public License.
+
+  The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+  Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+  If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/MANIFEST.in b/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/MANIFEST.in
new file mode 100644
index 0000000..9df2a36
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/MANIFEST.in
@@ -0,0 +1,4 @@
+include LICENSE
+include README.rst
+include ChangeLog
+recursive-include examples *
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/README.chromium b/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/README.chromium
new file mode 100644
index 0000000..47e8102
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/README.chromium
@@ -0,0 +1,19 @@
+Name: Python websocket-client
+Short Name: websocket-client
+URL: https://github.com/liris/websocket-client
+Version: 0.41.0
+Revision: 83419000cb50a732bf3fc479cb0a9be097af212a
+Date: Fri Feb 3rd 03:14:01 2017 EST
+License: LGPL-3.0
+License File: NOT_SHIPPED
+Security Critical: no
+
+Description:
+
+websocket-client module is WebSocket client for python. This provide the low
+level APIs for WebSocket. All APIs are the synchronous functions.
+
+Used by the python code in devtools-auto to communicate with a running Chrome instance.
+
+Local Modifications:
+None.
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/README.rst b/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/README.rst
new file mode 100644
index 0000000..22447a8
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/README.rst
@@ -0,0 +1,260 @@
+=================
+websocket-client
+=================
+
+**Our repository has moved to https://github.com/websocket-client/websocket-client**
+
+websocket-client module  is WebSocket client for python. This provide the low level APIs for WebSocket. All APIs are the synchronous functions.
+
+websocket-client supports only hybi-13.
+
+
+License
+============
+
+ - LGPL
+
+Installation
+=============
+
+This module is tested on Python 2.7 and Python 3.x.
+
+Type "python setup.py install" or "pip install websocket-client" to install.
+
+.. CAUTION::
+
+  from v0.16.0, we can install by "pip install websocket-client" for python 3.
+
+This module depend on
+
+ - six
+ - backports.ssl_match_hostname for Python 2.x
+
+How about Python 3
+===========================
+
+Now, we support python 3 on  single source code from version 0.14.0. Thanks, @battlemidget and @ralphbean.
+
+HTTP Proxy
+=============
+
+Support websocket access via http proxy.
+The proxy server must allow "CONNECT" method to websocket port.
+Default squid setting is "ALLOWED TO CONNECT ONLY HTTPS PORT".
+
+Current implementation of websocket-client is using "CONNECT" method via proxy.
+
+
+example
+
+.. code:: python
+
+    import websocket
+    ws = websocket.WebSocket()
+    ws.connect("ws://example.com/websocket", http_proxy_host="proxy_host_name", http_proxy_port=3128)
+    :
+
+
+
+Examples
+========
+
+Long-lived connection
+---------------------
+This example is similar to how WebSocket code looks in browsers using JavaScript.
+
+.. code:: python
+
+    import websocket
+    import thread
+    import time
+
+    def on_message(ws, message):
+        print message
+
+    def on_error(ws, error):
+        print error
+
+    def on_close(ws):
+        print "### closed ###"
+
+    def on_open(ws):
+        def run(*args):
+            for i in range(3):
+                time.sleep(1)
+                ws.send("Hello %d" % i)
+            time.sleep(1)
+            ws.close()
+            print "thread terminating..."
+        thread.start_new_thread(run, ())
+
+
+    if __name__ == "__main__":
+        websocket.enableTrace(True)
+        ws = websocket.WebSocketApp("ws://echo.websocket.org/",
+                                  on_message = on_message,
+                                  on_error = on_error,
+                                  on_close = on_close)
+        ws.on_open = on_open
+        ws.run_forever()
+
+
+Short-lived one-off send-receive
+--------------------------------
+This is if you want to communicate a short message and disconnect immediately when done.
+
+.. code:: python
+
+    from websocket import create_connection
+    ws = create_connection("ws://echo.websocket.org/")
+    print "Sending 'Hello, World'..."
+    ws.send("Hello, World")
+    print "Sent"
+    print "Receiving..."
+    result =  ws.recv()
+    print "Received '%s'" % result
+    ws.close()
+
+If you want to customize socket options, set sockopt.
+
+sockopt example
+
+.. code:: python
+
+    from websocket import create_connection
+    ws = create_connection("ws://echo.websocket.org/",
+                            sockopt=((socket.IPPROTO_TCP, socket.TCP_NODELAY),))
+
+
+More advanced: Custom class
+---------------------------
+You can also write your own class for the connection, if you want to handle the nitty-gritty details yourself.
+
+.. code:: python
+
+    from websocket import create_connection, WebSocket
+    class MyWebSocket(WebSocket):
+        def recv_frame(self):
+            frame = super().recv_frame()
+            print('yay! I got this frame: ', frame)
+            return frame
+
+    ws = create_connection("ws://echo.websocket.org/",
+                            sockopt=((socket.IPPROTO_TCP, socket.TCP_NODELAY),), class_=MyWebSocket)
+
+
+FAQ
+============
+
+How to disable ssl cert verification?
+----------------------------------------
+
+Please set sslopt to {"cert_reqs": ssl.CERT_NONE}.
+
+WebSocketApp sample
+
+.. code:: python
+
+    ws = websocket.WebSocketApp("wss://echo.websocket.org")
+    ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE})
+
+create_connection sample
+
+.. code:: python
+
+    ws = websocket.create_connection("wss://echo.websocket.org",
+      sslopt={"cert_reqs": ssl.CERT_NONE})
+
+WebSocket sample
+
+.. code:: python
+
+    ws = websocket.WebSocket(sslopt={"cert_reqs": ssl.CERT_NONE})
+    ws.connect("wss://echo.websocket.org")
+
+
+How to disable hostname verification.
+----------------------------------------
+
+Please set sslopt to {"check_hostname": False}.
+(since v0.18.0)
+
+WebSocketApp sample
+
+.. code:: python
+
+    ws = websocket.WebSocketApp("wss://echo.websocket.org")
+    ws.run_forever(sslopt={"check_hostname": False})
+
+create_connection sample
+
+.. code:: python
+
+    ws = websocket.create_connection("wss://echo.websocket.org",
+      sslopt={"check_hostname": False})
+
+WebSocket sample
+
+.. code:: python
+
+    ws = websocket.WebSocket(sslopt={"check_hostname": False})
+    ws.connect("wss://echo.websocket.org")
+
+
+How to enable `SNI <http://en.wikipedia.org/wiki/Server_Name_Indication>`_?
+---------------------------------------------------------------------------
+
+SNI support is available for Python 2.7.9+ and 3.2+. It will be enabled automatically whenever possible.
+
+
+Sub Protocols.
+----------------------------------------
+
+The server needs to support sub protocols, please set the subprotocol like this.
+
+
+Subprotocol sample
+
+.. code:: python
+
+    ws = websocket.create_connection("ws://exapmle.com/websocket", subprotocols=["binary", "base64"])
+
+
+
+wsdump.py
+============
+
+wsdump.py is simple WebSocket test(debug) tool.
+
+sample for echo.websocket.org::
+
+  $ wsdump.py ws://echo.websocket.org/
+  Press Ctrl+C to quit
+  > Hello, WebSocket
+  < Hello, WebSocket
+  > How are you?
+  < How are you?
+
+Usage
+---------
+
+usage::
+
+  wsdump.py [-h] [-v [VERBOSE]] ws_url
+
+WebSocket Simple Dump Tool
+
+positional arguments:
+  ws_url                websocket url. ex. ws://echo.websocket.org/
+
+optional arguments:
+  -h, --help                           show this help message and exit
+WebSocketApp
+  -v VERBOSE, --verbose VERBOSE    set verbose mode. If set to 1, show opcode. If set to 2, enable to trace websocket module
+
+example::
+
+  $ wsdump.py ws://echo.websocket.org/
+  $ wsdump.py ws://echo.websocket.org/ -v
+  $ wsdump.py ws://echo.websocket.org/ -vv
+
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/bin/wsdump.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/bin/wsdump.py
new file mode 100755
index 0000000..e06271c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/bin/wsdump.py
@@ -0,0 +1,200 @@
+#!/usr/bin/env python
+
+import argparse
+import code
+import sys
+import threading
+import time
+
+import six
+from six.moves.urllib.parse import urlparse
+
+import websocket
+
+try:
+    import readline
+except ImportError:
+    pass
+
+
+def get_encoding():
+    encoding = getattr(sys.stdin, "encoding", "")
+    if not encoding:
+        return "utf-8"
+    else:
+        return encoding.lower()
+
+
+OPCODE_DATA = (websocket.ABNF.OPCODE_TEXT, websocket.ABNF.OPCODE_BINARY)
+ENCODING = get_encoding()
+
+
+class VAction(argparse.Action):
+
+    def __call__(self, parser, args, values, option_string=None):
+        if values is None:
+            values = "1"
+        try:
+            values = int(values)
+        except ValueError:
+            values = values.count("v") + 1
+        setattr(args, self.dest, values)
+
+
+def parse_args():
+    parser = argparse.ArgumentParser(description="WebSocket Simple Dump Tool")
+    parser.add_argument("url", metavar="ws_url",
+                        help="websocket url. ex. ws://echo.websocket.org/")
+    parser.add_argument("-p", "--proxy",
+                        help="proxy url. ex. http://127.0.0.1:8080")
+    parser.add_argument("-v", "--verbose", default=0, nargs='?', action=VAction,
+                        dest="verbose",
+                        help="set verbose mode. If set to 1, show opcode. "
+                        "If set to 2, enable to trace  websocket module")
+    parser.add_argument("-n", "--nocert", action='store_true',
+                        help="Ignore invalid SSL cert")
+    parser.add_argument("-r", "--raw", action="store_true",
+                        help="raw output")
+    parser.add_argument("-s", "--subprotocols", nargs='*',
+                        help="Set subprotocols")
+    parser.add_argument("-o", "--origin",
+                        help="Set origin")
+    parser.add_argument("--eof-wait", default=0, type=int,
+                        help="wait time(second) after 'EOF' received.")
+    parser.add_argument("-t", "--text",
+                        help="Send initial text")
+    parser.add_argument("--timings", action="store_true",
+                        help="Print timings in seconds")
+    parser.add_argument("--headers",
+                        help="Set custom headers. Use ',' as separator")
+
+    return parser.parse_args()
+
+
+class RawInput:
+
+    def raw_input(self, prompt):
+        if six.PY3:
+            line = input(prompt)
+        else:
+            line = raw_input(prompt)
+
+        if ENCODING and ENCODING != "utf-8" and not isinstance(line, six.text_type):
+            line = line.decode(ENCODING).encode("utf-8")
+        elif isinstance(line, six.text_type):
+            line = line.encode("utf-8")
+
+        return line
+
+
+class InteractiveConsole(RawInput, code.InteractiveConsole):
+
+    def write(self, data):
+        sys.stdout.write("\033[2K\033[E")
+        # sys.stdout.write("\n")
+        sys.stdout.write("\033[34m< " + data + "\033[39m")
+        sys.stdout.write("\n> ")
+        sys.stdout.flush()
+
+    def read(self):
+        return self.raw_input("> ")
+
+
+class NonInteractive(RawInput):
+
+    def write(self, data):
+        sys.stdout.write(data)
+        sys.stdout.write("\n")
+        sys.stdout.flush()
+
+    def read(self):
+        return self.raw_input("")
+
+
+def main():
+    start_time = time.time()
+    args = parse_args()
+    if args.verbose > 1:
+        websocket.enableTrace(True)
+    options = {}
+    if args.proxy:
+        p = urlparse(args.proxy)
+        options["http_proxy_host"] = p.hostname
+        options["http_proxy_port"] = p.port
+    if args.origin:
+        options["origin"] = args.origin
+    if args.subprotocols:
+        options["subprotocols"] = args.subprotocols
+    opts = {}
+    if args.nocert:
+        opts = {"cert_reqs": websocket.ssl.CERT_NONE, "check_hostname": False}
+    if args.headers:
+        options['header'] = map(str.strip, args.headers.split(','))
+    ws = websocket.create_connection(args.url, sslopt=opts, **options)
+    if args.raw:
+        console = NonInteractive()
+    else:
+        console = InteractiveConsole()
+        print("Press Ctrl+C to quit")
+
+    def recv():
+        try:
+            frame = ws.recv_frame()
+        except websocket.WebSocketException:
+            return websocket.ABNF.OPCODE_CLOSE, None
+        if not frame:
+            raise websocket.WebSocketException("Not a valid frame %s" % frame)
+        elif frame.opcode in OPCODE_DATA:
+            return frame.opcode, frame.data
+        elif frame.opcode == websocket.ABNF.OPCODE_CLOSE:
+            ws.send_close()
+            return frame.opcode, None
+        elif frame.opcode == websocket.ABNF.OPCODE_PING:
+            ws.pong(frame.data)
+            return frame.opcode, frame.data
+
+        return frame.opcode, frame.data
+
+    def recv_ws():
+        while True:
+            opcode, data = recv()
+            msg = None
+            if six.PY3 and opcode == websocket.ABNF.OPCODE_TEXT and isinstance(data, bytes):
+                data = str(data, "utf-8")
+            if not args.verbose and opcode in OPCODE_DATA:
+                msg = data
+            elif args.verbose:
+                msg = "%s: %s" % (websocket.ABNF.OPCODE_MAP.get(opcode), data)
+
+            if msg is not None:
+                if args.timings:
+                    console.write(str(time.time() - start_time) + ": " + msg)
+                else:
+                    console.write(msg)
+
+            if opcode == websocket.ABNF.OPCODE_CLOSE:
+                break
+
+    thread = threading.Thread(target=recv_ws)
+    thread.daemon = True
+    thread.start()
+
+    if args.text:
+        ws.send(args.text)
+
+    while True:
+        try:
+            message = console.read()
+            ws.send(message)
+        except KeyboardInterrupt:
+            return
+        except EOFError:
+            time.sleep(args.eof_wait)
+            return
+
+
+if __name__ == "__main__":
+    try:
+        main()
+    except Exception as e:
+        print(e)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/compliance/README.md b/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/compliance/README.md
new file mode 100644
index 0000000..5cef404
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/compliance/README.md
@@ -0,0 +1,11 @@
+# Autobahn Testsuite
+
+General information and installation instructions are available at http://autobahn.ws/testsuite .
+
+## Running the test suite
+
+
+  $ wstest -m fuzzingserver
+  $ python test_fuzzingclient.py
+
+
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/compliance/fuzzingserver.json b/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/compliance/fuzzingserver.json
new file mode 100644
index 0000000..6d4f4c0
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/compliance/fuzzingserver.json
@@ -0,0 +1,9 @@
+{
+"url": "ws://localhost:8642",
+"options": {"failByDrop": false},
+"outdir": "./reports/clients",
+"webport": 8080,
+"cases": ["*"],
+"exclude-cases": [],
+"exclude-agent-cases": {}
+}
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/compliance/test_fuzzingclient.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/compliance/test_fuzzingclient.py
new file mode 100644
index 0000000..f4b0ff1
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/compliance/test_fuzzingclient.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python
+
+import json
+import traceback
+
+import websocket
+
+SERVER = 'ws://127.0.0.1:8642'
+AGENT = 'py-websockets-client'
+
+
+ws = websocket.create_connection(SERVER + "/getCaseCount")
+count = json.loads(ws.recv())
+ws.close()
+
+
+for case in range(1, count+1):
+    url = SERVER + '/runCase?case={0}&agent={1}'.format(case, AGENT)
+    status = websocket.STATUS_NORMAL
+    try:
+        ws = websocket.create_connection(url)
+        while True:
+            opcode, msg = ws.recv_data()
+            if opcode == websocket.ABNF.OPCODE_TEXT:
+                msg.decode("utf-8")
+            if opcode  in (websocket.ABNF.OPCODE_TEXT, websocket.ABNF.OPCODE_BINARY):
+                ws.send(msg, opcode)
+    except UnicodeDecodeError:
+        # this case is ok.
+        status = websocket.STATUS_PROTOCOL_ERROR
+    except websocket.WebSocketProtocolException:
+        status = websocket.STATUS_PROTOCOL_ERROR
+    except websocket.WebSocketPayloadException:
+        status = websocket.STATUS_INVALID_PAYLOAD
+    except Exception as e:
+        # status = websocket.STATUS_PROTOCOL_ERROR
+        print(traceback.format_exc())
+    finally:
+        ws.close(status)
+
+print("Ran {} test cases.".format(case))
+url = SERVER + '/updateReports?agent={0}'.format(AGENT)
+ws = websocket.create_connection(url)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/examples/echo_client.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/examples/echo_client.py
new file mode 100644
index 0000000..7a74461
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/examples/echo_client.py
@@ -0,0 +1,13 @@
+from __future__ import print_function
+import websocket
+
+if __name__ == "__main__":
+    websocket.enableTrace(True)
+    ws = websocket.create_connection("ws://echo.websocket.org/")
+    print("Sending 'Hello, World'...")
+    ws.send("Hello, World")
+    print("Sent")
+    print("Receiving...")
+    result = ws.recv()
+    print("Received '%s'" % result)
+    ws.close()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/examples/echoapp_client.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/examples/echoapp_client.py
new file mode 100644
index 0000000..c15b35f
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/examples/echoapp_client.py
@@ -0,0 +1,48 @@
+import websocket
+try:
+    import thread
+except ImportError:  # TODO use Threading instead of _thread in python3
+    import _thread as thread
+import time
+import sys
+
+
+def on_message(ws, message):
+    print(message)
+
+
+def on_error(ws, error):
+    print(error)
+
+
+def on_close(ws):
+    print("### closed ###")
+
+
+def on_open(ws):
+    def run(*args):
+        for i in range(3):
+            # send the message, then wait
+            # so thread doesn't exit and socket
+            # isn't closed
+            ws.send("Hello %d" % i)
+            time.sleep(1)
+
+        time.sleep(1)
+        ws.close()
+        print("Thread terminating...")
+
+    thread.start_new_thread(run, ())
+
+if __name__ == "__main__":
+    websocket.enableTrace(True)
+    if len(sys.argv) < 2:
+        host = "ws://echo.websocket.org/"
+    else:
+        host = sys.argv[1]
+    ws = websocket.WebSocketApp(host,
+                                on_message=on_message,
+                                on_error=on_error,
+                                on_close=on_close)
+    ws.on_open = on_open
+    ws.run_forever()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/setup.cfg b/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/setup.cfg
new file mode 100644
index 0000000..50ee459
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/setup.cfg
@@ -0,0 +1,7 @@
+[egg_info]
+tag_build = 
+tag_date = 0
+tag_svn_revision = 0
+
+[bdist_wheel]
+universal = 1
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/setup.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/setup.py
new file mode 100644
index 0000000..38527a9
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/setup.py
@@ -0,0 +1,70 @@
+import sys
+
+from setuptools import setup
+import pkg_resources
+
+VERSION = "0.41.0"
+NAME = "websocket_client"
+
+install_requires = ["six"]
+tests_require = []
+
+if sys.version_info[0] == 2 and sys.version_info[1] < 7:
+        tests_require.append('unittest2==0.8.0')
+
+insecure_pythons = '2.4, 2.5, 2.6' + ', '.join("2.7.{pv}".format(pv=pv) for pv in range(10))
+
+extras_require = {
+    ':python_version in "{ips}"'.format(ips=insecure_pythons):
+        ['backports.ssl_match_hostname'],
+    ':python_version in "2.4, 2.5, 2.6"': ['argparse'],
+}
+
+try:
+    if 'bdist_wheel' not in sys.argv:
+        for key, value in extras_require.items():
+            if key.startswith(':') and pkg_resources.evaluate_marker(key[1:]):
+                install_requires.extend(value)
+except Exception:
+    import logging
+    logging.getLogger(__name__).exception(
+        'Something went wrong calculating platform specific dependencies, so '
+        "you're getting them all!"
+    )
+    for key, value in extras_require.items():
+        if key.startswith(':'):
+            install_requires.extend(value)
+
+setup(
+    name=NAME,
+    version=VERSION,
+    description="WebSocket client for python. hybi13 is supported.",
+    long_description=open("README.rst").read(),
+    author="liris",
+    author_email="liris.pp@gmail.com",
+    license="LGPL",
+    url="https://github.com/websocket-client/websocket-client.git",
+    classifiers=[
+        "Development Status :: 4 - Beta",
+        "License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)",
+        "Programming Language :: Python",
+        "Programming Language :: Python :: 2",
+        "Programming Language :: Python :: 3",
+        "Operating System :: MacOS :: MacOS X",
+        "Operating System :: POSIX",
+        "Operating System :: Microsoft :: Windows",
+        "Topic :: Internet",
+        "Topic :: Software Development :: Libraries :: Python Modules",
+        "Intended Audience :: Developers",
+    ],
+    keywords='websockets',
+    scripts=["bin/wsdump.py"],
+    install_requires=install_requires,
+    packages=["websocket", "websocket.tests"],
+    package_data={
+        'websocket.tests': ['data/*.txt'],
+        'websocket': ["cacert.pem"]
+    },
+    tests_require=tests_require,
+    test_suite="websocket.tests.test_websocket",
+)
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/websocket/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/websocket/__init__.py
new file mode 100644
index 0000000..a4f9374
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/websocket/__init__.py
@@ -0,0 +1,29 @@
+"""
+websocket - WebSocket client library for Python
+
+Copyright (C) 2010 Hiroki Ohtani(liris)
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA  02110-1335  USA
+
+"""
+from ._abnf import *
+from ._app import WebSocketApp
+from ._core import *
+from ._exceptions import *
+from ._logging import *
+from ._socket import *
+
+__version__ = "0.41.0"
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/websocket/cacert.pem b/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/websocket/cacert.pem
new file mode 100644
index 0000000..e908bb6
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/websocket/cacert.pem
@@ -0,0 +1,4966 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+# Issuer: CN=GTE CyberTrust Global Root O=GTE Corporation OU=GTE CyberTrust Solutions, Inc.
+# Subject: CN=GTE CyberTrust Global Root O=GTE Corporation OU=GTE CyberTrust Solutions, Inc.
+# Label: "GTE CyberTrust Global Root"
+# Serial: 421
+# MD5 Fingerprint: ca:3d:d3:68:f1:03:5c:d0:32:fa:b8:2b:59:e8:5a:db
+# SHA1 Fingerprint: 97:81:79:50:d8:1c:96:70:cc:34:d8:09:cf:79:44:31:36:7e:f4:74
+# SHA256 Fingerprint: a5:31:25:18:8d:21:10:aa:96:4b:02:c7:b7:c6:da:32:03:17:08:94:e5:fb:71:ff:fb:66:67:d5:e6:81:0a:36
+-----BEGIN CERTIFICATE-----
+MIICWjCCAcMCAgGlMA0GCSqGSIb3DQEBBAUAMHUxCzAJBgNVBAYTAlVTMRgwFgYD
+VQQKEw9HVEUgQ29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRydXN0IFNv
+bHV0aW9ucywgSW5jLjEjMCEGA1UEAxMaR1RFIEN5YmVyVHJ1c3QgR2xvYmFsIFJv
+b3QwHhcNOTgwODEzMDAyOTAwWhcNMTgwODEzMjM1OTAwWjB1MQswCQYDVQQGEwJV
+UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU
+cnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEds
+b2JhbCBSb290MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVD6C28FCc6HrH
+iM3dFw4usJTQGz0O9pTAipTHBsiQl8i4ZBp6fmw8U+E3KHNgf7KXUwefU/ltWJTS
+r41tiGeA5u2ylc9yMcqlHHK6XALnZELn+aks1joNrI1CqiQBOeacPwGFVw1Yh0X4
+04Wqk2kmhXBIgD8SFcd5tB8FLztimQIDAQABMA0GCSqGSIb3DQEBBAUAA4GBAG3r
+GwnpXtlR22ciYaQqPEh346B8pt5zohQDhT37qw4wxYMWM4ETCJ57NE7fQMh017l9
+3PR2VX2bY1QY6fDq81yx2YtCHrnAlU66+tXifPVoYb+O7AWXX1uw16OFNMQkpw0P
+lZPvy5TYnh+dXIVtx6quTx8itc2VrbqnzPmrC3p/
+-----END CERTIFICATE-----
+
+# Issuer: CN=Thawte Server CA O=Thawte Consulting cc OU=Certification Services Division
+# Subject: CN=Thawte Server CA O=Thawte Consulting cc OU=Certification Services Division
+# Label: "Thawte Server CA"
+# Serial: 1
+# MD5 Fingerprint: c5:70:c4:a2:ed:53:78:0c:c8:10:53:81:64:cb:d0:1d
+# SHA1 Fingerprint: 23:e5:94:94:51:95:f2:41:48:03:b4:d5:64:d2:a3:a3:f5:d8:8b:8c
+# SHA256 Fingerprint: b4:41:0b:73:e2:e6:ea:ca:47:fb:c4:2f:8f:a4:01:8a:f4:38:1d:c5:4c:fa:a8:44:50:46:1e:ed:09:45:4d:e9
+-----BEGIN CERTIFICATE-----
+MIIDEzCCAnygAwIBAgIBATANBgkqhkiG9w0BAQQFADCBxDELMAkGA1UEBhMCWkEx
+FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYD
+VQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlv
+biBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhhd3RlIFNlcnZlciBDQTEm
+MCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5jb20wHhcNOTYwODAx
+MDAwMDAwWhcNMjAxMjMxMjM1OTU5WjCBxDELMAkGA1UEBhMCWkExFTATBgNVBAgT
+DFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3
+dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNl
+cyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3
+DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQAD
+gY0AMIGJAoGBANOkUG7I/1Zr5s9dtuoMaHVHoqrC2oQl/Kj0R1HahbUgdJSGHg91
+yekIYfUGbTBuFRkC6VLAYttNmZ7iagxEOM3+vuNkCXDF/rFrKbYvScg71CcEJRCX
+L+eQbcAoQpnXTEPew/UhbVSfXcNY4cDk2VuwuNy0e982OsK1ZiIS1ocNAgMBAAGj
+EzARMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAB/pMaVz7lcxG
+7oWDTSEwjsrZqG9JGubaUeNgcGyEYRGhGshIPllDfU+VPaGLtwtimHp1it2ITk6e
+QNuozDJ0uW8NxuOzRAvZim+aKZuZGCg70eNAKJpaPNW15yAbi8qkq43pUdniTCxZ
+qdq5snUb9kLy78fyGPmJvKP/iiMucEc=
+-----END CERTIFICATE-----
+
+# Issuer: CN=Thawte Premium Server CA O=Thawte Consulting cc OU=Certification Services Division
+# Subject: CN=Thawte Premium Server CA O=Thawte Consulting cc OU=Certification Services Division
+# Label: "Thawte Premium Server CA"
+# Serial: 1
+# MD5 Fingerprint: 06:9f:69:79:16:66:90:02:1b:8c:8c:a2:c3:07:6f:3a
+# SHA1 Fingerprint: 62:7f:8d:78:27:65:63:99:d2:7d:7f:90:44:c9:fe:b3:f3:3e:fa:9a
+# SHA256 Fingerprint: ab:70:36:36:5c:71:54:aa:29:c2:c2:9f:5d:41:91:16:3b:16:2a:22:25:01:13:57:d5:6d:07:ff:a7:bc:1f:72
+-----BEGIN CERTIFICATE-----
+MIIDJzCCApCgAwIBAgIBATANBgkqhkiG9w0BAQQFADCBzjELMAkGA1UEBhMCWkEx
+FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYD
+VQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlv
+biBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhhd3RlIFByZW1pdW0gU2Vy
+dmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNlcnZlckB0aGF3dGUuY29t
+MB4XDTk2MDgwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVowgc4xCzAJBgNVBAYTAlpB
+MRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcTCUNhcGUgVG93bjEdMBsG
+A1UEChMUVGhhd3RlIENvbnN1bHRpbmcgY2MxKDAmBgNVBAsTH0NlcnRpZmljYXRp
+b24gU2VydmljZXMgRGl2aXNpb24xITAfBgNVBAMTGFRoYXd0ZSBQcmVtaXVtIFNl
+cnZlciBDQTEoMCYGCSqGSIb3DQEJARYZcHJlbWl1bS1zZXJ2ZXJAdGhhd3RlLmNv
+bTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0jY2aovXwlue2oFBYo847kkE
+VdbQ7xwblRZH7xhINTpS9CtqBo87L+pW46+GjZ4X9560ZXUCTe/LCaIhUdib0GfQ
+ug2SBhRz1JPLlyoAnFxODLz6FVL88kRu2hFKbgifLy3j+ao6hnO2RlNYyIkFvYMR
+uHM/qgeN9EJN50CdHDcCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG
+9w0BAQQFAAOBgQAmSCwWwlj66BZ0DKqqX1Q/8tfJeGBeXm43YyJ3Nn6yF8Q0ufUI
+hfzJATj/Tb7yFkJD57taRvvBxhEf8UqwKEbJw8RCfbz6q1lu1bdRiBHjpIUZa4JM
+pAwSremkrj/xw0llmozFyD4lt5SZu5IycQfwhl7tUCemDaYj+bvLpgcUQg==
+-----END CERTIFICATE-----
+
+# Issuer: O=Equifax OU=Equifax Secure Certificate Authority
+# Subject: O=Equifax OU=Equifax Secure Certificate Authority
+# Label: "Equifax Secure CA"
+# Serial: 903804111
+# MD5 Fingerprint: 67:cb:9d:c0:13:24:8a:82:9b:b2:17:1e:d1:1b:ec:d4
+# SHA1 Fingerprint: d2:32:09:ad:23:d3:14:23:21:74:e4:0d:7f:9d:62:13:97:86:63:3a
+# SHA256 Fingerprint: 08:29:7a:40:47:db:a2:36:80:c7:31:db:6e:31:76:53:ca:78:48:e1:be:bd:3a:0b:01:79:a7:07:f9:2c:f1:78
+-----BEGIN CERTIFICATE-----
+MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV
+UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy
+dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1
+MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx
+dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B
+AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f
+BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A
+cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC
+AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ
+MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm
+aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw
+ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj
+IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF
+MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA
+A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y
+7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh
+1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4
+-----END CERTIFICATE-----
+
+# Issuer: O=VeriSign, Inc. OU=Class 3 Public Primary Certification Authority
+# Subject: O=VeriSign, Inc. OU=Class 3 Public Primary Certification Authority
+# Label: "Verisign Class 3 Public Primary Certification Authority"
+# Serial: 149843929435818692848040365716851702463
+# MD5 Fingerprint: 10:fc:63:5d:f6:26:3e:0d:f3:25:be:5f:79:cd:67:67
+# SHA1 Fingerprint: 74:2c:31:92:e6:07:e4:24:eb:45:49:54:2b:e1:bb:c5:3e:61:74:e2
+# SHA256 Fingerprint: e7:68:56:34:ef:ac:f6:9a:ce:93:9a:6b:25:5b:7b:4f:ab:ef:42:93:5b:50:a2:65:ac:b5:cb:60:27:e4:4e:70
+-----BEGIN CERTIFICATE-----
+MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkG
+A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz
+cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2
+MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV
+BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt
+YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN
+ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE
+BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is
+I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G
+CSqGSIb3DQEBAgUAA4GBALtMEivPLCYATxQT3ab7/AoRhIzzKBxnki98tsX63/Do
+lbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59AhWM1pF+NEHJwZRDmJXNyc
+AA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2OmufTqj/ZA1k
+-----END CERTIFICATE-----
+
+# Issuer: O=VeriSign, Inc. OU=Class 3 Public Primary Certification Authority - G2/(c) 1998 VeriSign, Inc. - For authorized use only/VeriSign Trust Network
+# Subject: O=VeriSign, Inc. OU=Class 3 Public Primary Certification Authority - G2/(c) 1998 VeriSign, Inc. - For authorized use only/VeriSign Trust Network
+# Label: "Verisign Class 3 Public Primary Certification Authority - G2"
+# Serial: 167285380242319648451154478808036881606
+# MD5 Fingerprint: a2:33:9b:4c:74:78:73:d4:6c:e7:c1:f3:8d:cb:5c:e9
+# SHA1 Fingerprint: 85:37:1c:a6:e5:50:14:3d:ce:28:03:47:1b:de:3a:09:e8:f8:77:0f
+# SHA256 Fingerprint: 83:ce:3c:12:29:68:8a:59:3d:48:5f:81:97:3c:0f:91:95:43:1e:da:37:cc:5e:36:43:0e:79:c7:a8:88:63:8b
+-----BEGIN CERTIFICATE-----
+MIIDAjCCAmsCEH3Z/gfPqB63EHln+6eJNMYwDQYJKoZIhvcNAQEFBQAwgcExCzAJ
+BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xh
+c3MgMyBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcy
+MTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3Jp
+emVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMB4X
+DTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVTMRcw
+FQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMg
+UHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEo
+YykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5
+MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEB
+AQUAA4GNADCBiQKBgQDMXtERXVxp0KvTuWpMmR9ZmDCOFoUgRm1HP9SFIIThbbP4
+pO0M8RcPO/mn+SXXwc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71lSk8UOg0
+13gfqLptQ5GVj0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZwID
+AQABMA0GCSqGSIb3DQEBBQUAA4GBAFFNzb5cy5gZnBWyATl4Lk0PZ3BwmcYQWpSk
+U01UbSuvDV1Ai2TT1+7eVmGSX6bEHRBhNtMsJzzoKQm5EWR0zLVznxxIqbxhAe7i
+F6YM40AIOw7n60RzKprxaZLvcRTDOaxxp5EJb+RxBrO6WVcmeQD2+A2iMzAo1KpY
+oJ2daZH9
+-----END CERTIFICATE-----
+
+# Issuer: CN=GlobalSign Root CA O=GlobalSign nv-sa OU=Root CA
+# Subject: CN=GlobalSign Root CA O=GlobalSign nv-sa OU=Root CA
+# Label: "GlobalSign Root CA"
+# Serial: 4835703278459707669005204
+# MD5 Fingerprint: 3e:45:52:15:09:51:92:e1:b7:5d:37:9f:b1:87:29:8a
+# SHA1 Fingerprint: b1:bc:96:8b:d4:f4:9d:62:2a:a8:9a:81:f2:15:01:52:a4:1d:82:9c
+# SHA256 Fingerprint: eb:d4:10:40:e4:bb:3e:c7:42:c9:e3:81:d3:1e:f2:a4:1a:48:b6:68:5c:96:e7:ce:f3:c1:df:6c:d4:33:1c:99
+-----BEGIN CERTIFICATE-----
+MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG
+A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
+b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw
+MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i
+YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT
+aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ
+jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp
+xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp
+1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG
+snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ
+U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8
+9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E
+BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B
+AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz
+yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE
+38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP
+AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad
+DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME
+HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==
+-----END CERTIFICATE-----
+
+# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R2
+# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R2
+# Label: "GlobalSign Root CA - R2"
+# Serial: 4835703278459682885658125
+# MD5 Fingerprint: 94:14:77:7e:3e:5e:fd:8f:30:bd:41:b0:cf:e7:d0:30
+# SHA1 Fingerprint: 75:e0:ab:b6:13:85:12:27:1c:04:f8:5f:dd:de:38:e4:b7:24:2e:fe
+# SHA256 Fingerprint: ca:42:dd:41:74:5f:d0:b8:1e:b9:02:36:2c:f9:d8:bf:71:9d:a1:bd:1b:1e:fc:94:6f:5b:4c:99:f4:2c:1b:9e
+-----BEGIN CERTIFICATE-----
+MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4G
+A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNp
+Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1
+MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEG
+A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6ErPL
+v4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8
+eoLrvozps6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklq
+tTleiDTsvHgMCJiEbKjNS7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzd
+C9XZzPnqJworc5HGnRusyMvo4KD0L5CLTfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pa
+zq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6CygPCm48CAwEAAaOBnDCB
+mTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUm+IH
+V2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5n
+bG9iYWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG
+3lm0mi3f3BmGLjANBgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4Gs
+J0/WwbgcQ3izDJr86iw8bmEbTUsp9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO
+291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu01yiPqFbQfXf5WRDLenVOavS
+ot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG79G+dwfCMNYxd
+AfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7
+TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg==
+-----END CERTIFICATE-----
+
+# Issuer: CN=http://www.valicert.com/ O=ValiCert, Inc. OU=ValiCert Class 1 Policy Validation Authority
+# Subject: CN=http://www.valicert.com/ O=ValiCert, Inc. OU=ValiCert Class 1 Policy Validation Authority
+# Label: "ValiCert Class 1 VA"
+# Serial: 1
+# MD5 Fingerprint: 65:58:ab:15:ad:57:6c:1e:a8:a7:b5:69:ac:bf:ff:eb
+# SHA1 Fingerprint: e5:df:74:3c:b6:01:c4:9b:98:43:dc:ab:8c:e8:6a:81:10:9f:e4:8e
+# SHA256 Fingerprint: f4:c1:49:55:1a:30:13:a3:5b:c7:bf:fe:17:a7:f3:44:9b:c1:ab:5b:5a:0a:e7:4b:06:c2:3b:90:00:4c:01:04
+-----BEGIN CERTIFICATE-----
+MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0
+IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz
+BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDEgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y
+aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG
+9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNTIyMjM0OFoXDTE5MDYy
+NTIyMjM0OFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y
+azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs
+YXNzIDEgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw
+Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl
+cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDYWYJ6ibiWuqYvaG9Y
+LqdUHAZu9OqNSLwxlBfw8068srg1knaw0KWlAdcAAxIiGQj4/xEjm84H9b9pGib+
+TunRf50sQB1ZaG6m+FiwnRqP0z/x3BkGgagO4DrdyFNFCQbmD3DD+kCmDuJWBQ8Y
+TfwggtFzVXSNdnKgHZ0dwN0/cQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFBoPUn0
+LBwGlN+VYH+Wexf+T3GtZMjdd9LvWVXoP+iOBSoh8gfStadS/pyxtuJbdxdA6nLW
+I8sogTLDAHkY7FkXicnGah5xyf23dKUlRWnFSKsZ4UWKJWsZ7uW7EvV/96aNUcPw
+nXS3qT6gpf+2SQMT2iLM7XGCK5nPOrf1LXLI
+-----END CERTIFICATE-----
+
+# Issuer: CN=http://www.valicert.com/ O=ValiCert, Inc. OU=ValiCert Class 2 Policy Validation Authority
+# Subject: CN=http://www.valicert.com/ O=ValiCert, Inc. OU=ValiCert Class 2 Policy Validation Authority
+# Label: "ValiCert Class 2 VA"
+# Serial: 1
+# MD5 Fingerprint: a9:23:75:9b:ba:49:36:6e:31:c2:db:f2:e7:66:ba:87
+# SHA1 Fingerprint: 31:7a:2a:d0:7f:2b:33:5e:f5:a1:c3:4e:4b:57:e8:b7:d8:f1:fc:a6
+# SHA256 Fingerprint: 58:d0:17:27:9c:d4:dc:63:ab:dd:b1:96:a6:c9:90:6c:30:c4:e0:87:83:ea:e8:c1:60:99:54:d6:93:55:59:6b
+-----BEGIN CERTIFICATE-----
+MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0
+IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz
+BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y
+aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG
+9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAwMTk1NFoXDTE5MDYy
+NjAwMTk1NFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y
+azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs
+YXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw
+Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl
+cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOOnHK5avIWZJV16vY
+dA757tn2VUdZZUcOBVXc65g2PFxTXdMwzzjsvUGJ7SVCCSRrCl6zfN1SLUzm1NZ9
+WlmpZdRJEy0kTRxQb7XBhVQ7/nHk01xC+YDgkRoKWzk2Z/M/VXwbP7RfZHM047QS
+v4dk+NoS/zcnwbNDu+97bi5p9wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBADt/UG9v
+UJSZSWI4OB9L+KXIPqeCgfYrx+jFzug6EILLGACOTb2oWH+heQC1u+mNr0HZDzTu
+IYEZoDJJKPTEjlbVUjP9UNV+mWwD5MlM/Mtsq2azSiGM5bUMMj4QssxsodyamEwC
+W/POuZ6lcg5Ktz885hZo+L7tdEy8W9ViH0Pd
+-----END CERTIFICATE-----
+
+# Issuer: CN=http://www.valicert.com/ O=ValiCert, Inc. OU=ValiCert Class 3 Policy Validation Authority
+# Subject: CN=http://www.valicert.com/ O=ValiCert, Inc. OU=ValiCert Class 3 Policy Validation Authority
+# Label: "RSA Root Certificate 1"
+# Serial: 1
+# MD5 Fingerprint: a2:6f:53:b7:ee:40:db:4a:68:e7:fa:18:d9:10:4b:72
+# SHA1 Fingerprint: 69:bd:8c:f4:9c:d3:00:fb:59:2e:17:93:ca:55:6a:f3:ec:aa:35:fb
+# SHA256 Fingerprint: bc:23:f9:8a:31:3c:b9:2d:e3:bb:fc:3a:5a:9f:44:61:ac:39:49:4c:4a:e1:5a:9e:9d:f1:31:e9:9b:73:01:9a
+-----BEGIN CERTIFICATE-----
+MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0
+IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz
+BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDMgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y
+aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG
+9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAwMjIzM1oXDTE5MDYy
+NjAwMjIzM1owgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y
+azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs
+YXNzIDMgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw
+Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl
+cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDjmFGWHOjVsQaBalfD
+cnWTq8+epvzzFlLWLU2fNUSoLgRNB0mKOCn1dzfnt6td3zZxFJmP3MKS8edgkpfs
+2Ejcv8ECIMYkpChMMFp2bbFc893enhBxoYjHW5tBbcqwuI4V7q0zK89HBFx1cQqY
+JJgpp0lZpd34t0NiYfPT4tBVPwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFa7AliE
+Zwgs3x/be0kz9dNnnfS0ChCzycUs4pJqcXgn8nCDQtM+z6lU9PHYkhaM0QTLS6vJ
+n0WuPIqpsHEzXcjFV9+vqDWzf4mH6eglkrh/hXqu1rweN1gqZ8mRzyqBPu3GOd/A
+PhmcGcwTTYJBtYze4D1gCCAPRX5ron+jjBXu
+-----END CERTIFICATE-----
+
+# Issuer: CN=VeriSign Class 3 Public Primary Certification Authority - G3 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 1999 VeriSign, Inc. - For authorized use only
+# Subject: CN=VeriSign Class 3 Public Primary Certification Authority - G3 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 1999 VeriSign, Inc. - For authorized use only
+# Label: "Verisign Class 3 Public Primary Certification Authority - G3"
+# Serial: 206684696279472310254277870180966723415
+# MD5 Fingerprint: cd:68:b6:a7:c7:c4:ce:75:e0:1d:4f:57:44:61:92:09
+# SHA1 Fingerprint: 13:2d:0d:45:53:4b:69:97:cd:b2:d5:c3:39:e2:55:76:60:9b:5c:c6
+# SHA256 Fingerprint: eb:04:cf:5e:b1:f3:9a:fa:76:2f:2b:b1:20:f2:96:cb:a5:20:c1:b9:7d:b1:58:95:65:b8:1c:b9:a1:7b:72:44
+-----BEGIN CERTIFICATE-----
+MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQsw
+CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl
+cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu
+LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT
+aWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
+dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD
+VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT
+aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ
+bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu
+IENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg
+LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMu6nFL8eB8aHm8b
+N3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1EUGO+i2t
+KmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGu
+kxUccLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBm
+CC+Vk7+qRy+oRpfwEuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJ
+Xwzw3sJ2zq/3avL6QaaiMxTJ5Xpj055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWu
+imi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAERSWwauSCPc/L8my/uRan2Te
+2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5fj267Cz3qWhMe
+DGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC
+/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565p
+F4ErWjfJXir0xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGt
+TxzhT5yvDwyd93gN2PQ1VoDat20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ==
+-----END CERTIFICATE-----
+
+# Issuer: CN=VeriSign Class 4 Public Primary Certification Authority - G3 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 1999 VeriSign, Inc. - For authorized use only
+# Subject: CN=VeriSign Class 4 Public Primary Certification Authority - G3 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 1999 VeriSign, Inc. - For authorized use only
+# Label: "Verisign Class 4 Public Primary Certification Authority - G3"
+# Serial: 314531972711909413743075096039378935511
+# MD5 Fingerprint: db:c8:f2:27:2e:b1:ea:6a:29:23:5d:fe:56:3e:33:df
+# SHA1 Fingerprint: c8:ec:8c:87:92:69:cb:4b:ab:39:e9:8d:7e:57:67:f3:14:95:73:9d
+# SHA256 Fingerprint: e3:89:36:0d:0f:db:ae:b3:d2:50:58:4b:47:30:31:4e:22:2f:39:c1:56:a0:20:14:4e:8d:96:05:61:79:15:06
+-----BEGIN CERTIFICATE-----
+MIIEGjCCAwICEQDsoKeLbnVqAc/EfMwvlF7XMA0GCSqGSIb3DQEBBQUAMIHKMQsw
+CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl
+cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu
+LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT
+aWduIENsYXNzIDQgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
+dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD
+VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT
+aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ
+bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu
+IENsYXNzIDQgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg
+LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK3LpRFpxlmr8Y+1
+GQ9Wzsy1HyDkniYlS+BzZYlZ3tCD5PUPtbut8XzoIfzk6AzufEUiGXaStBO3IFsJ
++mGuqPKljYXCKtbeZjbSmwL0qJJgfJxptI8kHtCGUvYynEFYHiK9zUVilQhu0Gbd
+U6LM8BDcVHOLBKFGMzNcF0C5nk3T875Vg+ixiY5afJqWIpA7iCXy0lOIAgwLePLm
+NxdLMEYH5IBtptiWLugs+BGzOA1mppvqySNb247i8xOOGlktqgLw7KSHZtzBP/XY
+ufTsgsbSPZUd5cBPhMnZo0QoBmrXRazwa2rvTl/4EYIeOGM0ZlDUPpNz+jDDZq3/
+ky2X7wMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAj/ola09b5KROJ1WrIhVZPMq1
+CtRK26vdoV9TxaBXOcLORyu+OshWv8LZJxA6sQU8wHcxuzrTBXttmhwwjIDLk5Mq
+g6sFUYICABFna/OIYUdfA5PVWw3g8dShMjWFsjrbsIKr0csKvE+MW8VLADsfKoKm
+fjaF3H48ZwC15DtS4KjrXRX5xm3wrR0OhbepmnMUWluPQSjA1egtTaRezarZ7c7c
+2NU8Qh0XwRJdRTjDOPP8hS6DRkiy1yBfkjaP53kPmF6Z6PDQpLv1U70qzlmwr25/
+bLvSHgCwIe34QWKCudiyxLtGUPMxxY8BqHTr9Xgn2uf3ZkPznoM+IKrDNWCRzg==
+-----END CERTIFICATE-----
+
+# Issuer: CN=Entrust.net Secure Server Certification Authority O=Entrust.net OU=www.entrust.net/CPS incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited
+# Subject: CN=Entrust.net Secure Server Certification Authority O=Entrust.net OU=www.entrust.net/CPS incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited
+# Label: "Entrust.net Secure Server CA"
+# Serial: 927650371
+# MD5 Fingerprint: df:f2:80:73:cc:f1:e6:61:73:fc:f5:42:e9:c5:7c:ee
+# SHA1 Fingerprint: 99:a6:9b:e6:1a:fe:88:6b:4d:2b:82:00:7c:b8:54:fc:31:7e:15:39
+# SHA256 Fingerprint: 62:f2:40:27:8c:56:4c:4d:d8:bf:7d:9d:4f:6f:36:6e:a8:94:d2:2f:5f:34:d9:89:a9:83:ac:ec:2f:ff:ed:50
+-----BEGIN CERTIFICATE-----
+MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMC
+VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5u
+ZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc
+KGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5u
+ZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw05OTA1
+MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQswCQYDVQQGEwJVUzEUMBIGA1UE
+ChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5j
+b3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBF
+bnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUg
+U2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUA
+A4GLADCBhwKBgQDNKIM0VBuJ8w+vN5Ex/68xYMmo6LIQaO2f55M28Qpku0f1BBc/
+I0dNxScZgSYMVHINiC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5gXpa0zf3
+wkrYKZImZNHkmGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OC
+AdcwggHTMBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHb
+oIHYpIHVMIHSMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5
+BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1p
+dHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1pdGVk
+MTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRp
+b24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNodHRwOi8vd3d3LmVu
+dHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAigA8xOTk5MDUyNTE2MDk0
+MFqBDzIwMTkwNTI1MTYwOTQwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8Bdi
+E1U9s/8KAGv7UISX8+1i0BowHQYDVR0OBBYEFPAXYhNVPbP/CgBr+1CEl/PtYtAa
+MAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EABAwwChsEVjQuMAMCBJAwDQYJKoZI
+hvcNAQEFBQADgYEAkNwwAvpkdMKnCqV8IY00F6j7Rw7/JXyNEwr75Ji174z4xRAN
+95K+8cPV1ZVqBLssziY2ZcgxxufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9n9cd
+2cNgQ4xYDiKWL2KjLB+6rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI=
+-----END CERTIFICATE-----
+
+# Issuer: CN=Entrust.net Certification Authority (2048) O=Entrust.net OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited
+# Subject: CN=Entrust.net Certification Authority (2048) O=Entrust.net OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited
+# Label: "Entrust.net Premium 2048 Secure Server CA"
+# Serial: 946069240
+# MD5 Fingerprint: ee:29:31:bc:32:7e:9a:e6:e8:b5:f7:51:b4:34:71:90
+# SHA1 Fingerprint: 50:30:06:09:1d:97:d4:f5:ae:39:f7:cb:e7:92:7d:7d:65:2d:34:31
+# SHA256 Fingerprint: 6d:c4:71:72:e0:1c:bc:b0:bf:62:58:0d:89:5f:e2:b8:ac:9a:d4:f8:73:80:1e:0c:10:b9:c8:37:d2:1e:b1:77
+-----BEGIN CERTIFICATE-----
+MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChML
+RW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBp
+bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5
+IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENlcnRp
+ZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQxNzUwNTFaFw0yOTA3
+MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3
+LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxp
+YWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEG
+A1UEAxMqRW50cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArU1LqRKGsuqjIAcVFmQq
+K0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOLGp18EzoOH1u3Hs/lJBQe
+sYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSrhRSGlVuX
+MlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVT
+XTzWnLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/
+HoZdenoVve8AjhUiVBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH
+4QIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV
+HQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJKoZIhvcNAQEFBQADggEBADub
+j1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPyT/4xmf3IDExo
+U8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf
+zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5b
+u/8j72gZyxKTJ1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+
+bYQLCIt+jerXmCHG8+c8eS9enNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/Er
+fF6adulZkMV8gzURZVE=
+-----END CERTIFICATE-----
+
+# Issuer: CN=Baltimore CyberTrust Root O=Baltimore OU=CyberTrust
+# Subject: CN=Baltimore CyberTrust Root O=Baltimore OU=CyberTrust
+# Label: "Baltimore CyberTrust Root"
+# Serial: 33554617
+# MD5 Fingerprint: ac:b6:94:a5:9c:17:e0:d7:91:52:9b:b1:97:06:a6:e4
+# SHA1 Fingerprint: d4:de:20:d0:5e:66:fc:53:fe:1a:50:88:2c:78:db:28:52:ca:e4:74
+# SHA256 Fingerprint: 16:af:57:a9:f6:76:b0:ab:12:60:95:aa:5e:ba:de:f2:2a:b3:11:19:d6:44:ac:95:cd:4b:93:db:f3:f2:6a:eb
+-----BEGIN CERTIFICATE-----
+MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ
+RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD
+VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX
+DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y
+ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy
+VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr
+mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr
+IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK
+mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu
+XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy
+dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye
+jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1
+BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3
+DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92
+9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx
+jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0
+Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz
+ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS
+R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp
+-----END CERTIFICATE-----
+
+# Issuer: CN=Equifax Secure Global eBusiness CA-1 O=Equifax Secure Inc.
+# Subject: CN=Equifax Secure Global eBusiness CA-1 O=Equifax Secure Inc.
+# Label: "Equifax Secure Global eBusiness CA"
+# Serial: 1
+# MD5 Fingerprint: 8f:5d:77:06:27:c4:98:3c:5b:93:78:e7:d7:7d:9b:cc
+# SHA1 Fingerprint: 7e:78:4a:10:1c:82:65:cc:2d:e1:f1:6d:47:b4:40:ca:d9:0a:19:45
+# SHA256 Fingerprint: 5f:0b:62:ea:b5:e3:53:ea:65:21:65:16:58:fb:b6:53:59:f4:43:28:0a:4a:fb:d1:04:d7:7d:10:f9:f0:4c:07
+-----BEGIN CERTIFICATE-----
+MIICkDCCAfmgAwIBAgIBATANBgkqhkiG9w0BAQQFADBaMQswCQYDVQQGEwJVUzEc
+MBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5jLjEtMCsGA1UEAxMkRXF1aWZheCBT
+ZWN1cmUgR2xvYmFsIGVCdXNpbmVzcyBDQS0xMB4XDTk5MDYyMTA0MDAwMFoXDTIw
+MDYyMTA0MDAwMFowWjELMAkGA1UEBhMCVVMxHDAaBgNVBAoTE0VxdWlmYXggU2Vj
+dXJlIEluYy4xLTArBgNVBAMTJEVxdWlmYXggU2VjdXJlIEdsb2JhbCBlQnVzaW5l
+c3MgQ0EtMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuucXkAJlsTRVPEnC
+UdXfp9E3j9HngXNBUmCbnaEXJnitx7HoJpQytd4zjTov2/KaelpzmKNc6fuKcxtc
+58O/gGzNqfTWK8D3+ZmqY6KxRwIP1ORROhI8bIpaVIRw28HFkM9yRcuoWcDNM50/
+o5brhTMhHD4ePmBudpxnhcXIw2ECAwEAAaNmMGQwEQYJYIZIAYb4QgEBBAQDAgAH
+MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUvqigdHJQa0S3ySPY+6j/s1dr
+aGwwHQYDVR0OBBYEFL6ooHRyUGtEt8kj2Puo/7NXa2hsMA0GCSqGSIb3DQEBBAUA
+A4GBADDiAVGqx+pf2rnQZQ8w1j7aDRRJbpGTJxQx78T3LUX47Me/okENI7SS+RkA
+Z70Br83gcfxaz2TE4JaY0KNA4gGK7ycH8WUBikQtBmV1UsCGECAhX2xrD2yuCRyv
+8qIYNMR1pHMc8Y3c7635s3a0kr/clRAevsvIO1qEYBlWlKlV
+-----END CERTIFICATE-----
+
+# Issuer: CN=Equifax Secure eBusiness CA-1 O=Equifax Secure Inc.
+# Subject: CN=Equifax Secure eBusiness CA-1 O=Equifax Secure Inc.
+# Label: "Equifax Secure eBusiness CA 1"
+# Serial: 4
+# MD5 Fingerprint: 64:9c:ef:2e:44:fc:c6:8f:52:07:d0:51:73:8f:cb:3d
+# SHA1 Fingerprint: da:40:18:8b:91:89:a3:ed:ee:ae:da:97:fe:2f:9d:f5:b7:d1:8a:41
+# SHA256 Fingerprint: cf:56:ff:46:a4:a1:86:10:9d:d9:65:84:b5:ee:b5:8a:51:0c:42:75:b0:e5:f9:4f:40:bb:ae:86:5e:19:f6:73
+-----BEGIN CERTIFICATE-----
+MIICgjCCAeugAwIBAgIBBDANBgkqhkiG9w0BAQQFADBTMQswCQYDVQQGEwJVUzEc
+MBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBT
+ZWN1cmUgZUJ1c2luZXNzIENBLTEwHhcNOTkwNjIxMDQwMDAwWhcNMjAwNjIxMDQw
+MDAwWjBTMQswCQYDVQQGEwJVUzEcMBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5j
+LjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2luZXNzIENBLTEwgZ8wDQYJ
+KoZIhvcNAQEBBQADgY0AMIGJAoGBAM4vGbwXt3fek6lfWg0XTzQaDJj0ItlZ1MRo
+RvC0NcWFAyDGr0WlIVFFQesWWDYyb+JQYmT5/VGcqiTZ9J2DKocKIdMSODRsjQBu
+WqDZQu4aIZX5UkxVWsUPOE9G+m34LjXWHXzr4vCwdYDIqROsvojvOm6rXyo4YgKw
+Env+j6YDAgMBAAGjZjBkMBEGCWCGSAGG+EIBAQQEAwIABzAPBgNVHRMBAf8EBTAD
+AQH/MB8GA1UdIwQYMBaAFEp4MlIR21kWNl7fwRQ2QGpHfEyhMB0GA1UdDgQWBBRK
+eDJSEdtZFjZe38EUNkBqR3xMoTANBgkqhkiG9w0BAQQFAAOBgQB1W6ibAxHm6VZM
+zfmpTMANmvPMZWnmJXbMWbfWVMMdzZmsGd20hdXgPfxiIKeES1hl8eL5lSE/9dR+
+WB5Hh1Q+WKG1tfgq73HnvMP2sUlG4tega+VWeponmHxGYhTnyfxuAxJ5gDgdSIKN
+/Bf+KpYrtWKmpj29f5JZzVoqgrI3eQ==
+-----END CERTIFICATE-----
+
+# Issuer: CN=AddTrust Class 1 CA Root O=AddTrust AB OU=AddTrust TTP Network
+# Subject: CN=AddTrust Class 1 CA Root O=AddTrust AB OU=AddTrust TTP Network
+# Label: "AddTrust Low-Value Services Root"
+# Serial: 1
+# MD5 Fingerprint: 1e:42:95:02:33:92:6b:b9:5f:c0:7f:da:d6:b2:4b:fc
+# SHA1 Fingerprint: cc:ab:0e:a0:4c:23:01:d6:69:7b:dd:37:9f:cd:12:eb:24:e3:94:9d
+# SHA256 Fingerprint: 8c:72:09:27:9a:c0:4e:27:5e:16:d0:7f:d3:b7:75:e8:01:54:b5:96:80:46:e3:1f:52:dd:25:76:63:24:e9:a7
+-----BEGIN CERTIFICATE-----
+MIIEGDCCAwCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJTRTEU
+MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3
+b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwHhcNMDAwNTMw
+MTAzODMxWhcNMjAwNTMwMTAzODMxWjBlMQswCQYDVQQGEwJTRTEUMBIGA1UEChML
+QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYD
+VQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQCWltQhSWDia+hBBwzexODcEyPNwTXH+9ZOEQpnXvUGW2ul
+CDtbKRY654eyNAbFvAWlA3yCyykQruGIgb3WntP+LVbBFc7jJp0VLhD7Bo8wBN6n
+tGO0/7Gcrjyvd7ZWxbWroulpOj0OM3kyP3CCkplhbY0wCI9xP6ZIVxn4JdxLZlyl
+dI+Yrsj5wAYi56xz36Uu+1LcsRVlIPo1Zmne3yzxbrww2ywkEtvrNTVokMsAsJch
+PXQhI2U0K7t4WaPW4XY5mqRJjox0r26kmqPZm9I4XJuiGMx1I4S+6+JNM3GOGvDC
++Mcdoq0Dlyz4zyXG9rgkMbFjXZJ/Y/AlyVMuH79NAgMBAAGjgdIwgc8wHQYDVR0O
+BBYEFJWxtPCUtr3H2tERCSG+wa9J/RB7MAsGA1UdDwQEAwIBBjAPBgNVHRMBAf8E
+BTADAQH/MIGPBgNVHSMEgYcwgYSAFJWxtPCUtr3H2tERCSG+wa9J/RB7oWmkZzBl
+MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFk
+ZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENB
+IFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBACxtZBsfzQ3duQH6lmM0MkhHma6X
+7f1yFqZzR1r0693p9db7RcwpiURdv0Y5PejuvE1Uhh4dbOMXJ0PhiVYrqW9yTkkz
+43J8KiOavD7/KCrto/8cI7pDVwlnTUtiBi34/2ydYB7YHEt9tTEv2dB8Xfjea4MY
+eDdXL+gzB2ffHsdrKpV2ro9Xo/D0UrSpUwjP4E/TelOL/bscVjby/rK25Xa71SJl
+pz/+0WatC7xrmYbvP33zGDLKe8bjq2RGlfgmadlVg3sslgf/WSxEo8bl6ancoWOA
+WiFeIc9TVPC6b4nbqKqVz4vjccweGyBECMB6tkD9xOQ14R0WHNC8K47Wcdk=
+-----END CERTIFICATE-----
+
+# Issuer: CN=AddTrust External CA Root O=AddTrust AB OU=AddTrust External TTP Network
+# Subject: CN=AddTrust External CA Root O=AddTrust AB OU=AddTrust External TTP Network
+# Label: "AddTrust External Root"
+# Serial: 1
+# MD5 Fingerprint: 1d:35:54:04:85:78:b0:3f:42:42:4d:bf:20:73:0a:3f
+# SHA1 Fingerprint: 02:fa:f3:e2:91:43:54:68:60:78:57:69:4d:f5:e4:5b:68:85:18:68
+# SHA256 Fingerprint: 68:7f:a4:51:38:22:78:ff:f0:c8:b1:1f:8d:43:d5:76:67:1c:6e:b2:bc:ea:b4:13:fb:83:d9:65:d0:6d:2f:f2
+-----BEGIN CERTIFICATE-----
+MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU
+MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs
+IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290
+MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux
+FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h
+bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v
+dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt
+H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9
+uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX
+mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX
+a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN
+E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0
+WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD
+VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0
+Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU
+cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx
+IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN
+AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH
+YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5
+6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC
+Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX
+c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a
+mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ=
+-----END CERTIFICATE-----
+
+# Issuer: CN=AddTrust Public CA Root O=AddTrust AB OU=AddTrust TTP Network
+# Subject: CN=AddTrust Public CA Root O=AddTrust AB OU=AddTrust TTP Network
+# Label: "AddTrust Public Services Root"
+# Serial: 1
+# MD5 Fingerprint: c1:62:3e:23:c5:82:73:9c:03:59:4b:2b:e9:77:49:7f
+# SHA1 Fingerprint: 2a:b6:28:48:5e:78:fb:f3:ad:9e:79:10:dd:6b:df:99:72:2c:96:e5
+# SHA256 Fingerprint: 07:91:ca:07:49:b2:07:82:aa:d3:c7:d7:bd:0c:df:c9:48:58:35:84:3e:b2:d7:99:60:09:ce:43:ab:6c:69:27
+-----BEGIN CERTIFICATE-----
+MIIEFTCCAv2gAwIBAgIBATANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJTRTEU
+MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3
+b3JrMSAwHgYDVQQDExdBZGRUcnVzdCBQdWJsaWMgQ0EgUm9vdDAeFw0wMDA1MzAx
+MDQxNTBaFw0yMDA1MzAxMDQxNTBaMGQxCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtB
+ZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5ldHdvcmsxIDAeBgNV
+BAMTF0FkZFRydXN0IFB1YmxpYyBDQSBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEA6Rowj4OIFMEg2Dybjxt+A3S72mnTRqX4jsIMEZBRpS9mVEBV
+6tsfSlbunyNu9DnLoblv8n75XYcmYZ4c+OLspoH4IcUkzBEMP9smcnrHAZcHF/nX
+GCwwfQ56HmIexkvA/X1id9NEHif2P0tEs7c42TkfYNVRknMDtABp4/MUTu7R3AnP
+dzRGULD4EfL+OHn3Bzn+UZKXC1sIXzSGAa2Il+tmzV7R/9x98oTaunet3IAIx6eH
+1lWfl2royBFkuucZKT8Rs3iQhCBSWxHveNCD9tVIkNAwHM+A+WD+eeSI8t0A65RF
+62WUaUC6wNW0uLp9BBGo6zEFlpROWCGOn9Bg/QIDAQABo4HRMIHOMB0GA1UdDgQW
+BBSBPjfYkrAfd59ctKtzquf2NGAv+jALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/BAUw
+AwEB/zCBjgYDVR0jBIGGMIGDgBSBPjfYkrAfd59ctKtzquf2NGAv+qFopGYwZDEL
+MAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRU
+cnVzdCBUVFAgTmV0d29yazEgMB4GA1UEAxMXQWRkVHJ1c3QgUHVibGljIENBIFJv
+b3SCAQEwDQYJKoZIhvcNAQEFBQADggEBAAP3FUr4JNojVhaTdt02KLmuG7jD8WS6
+IBh4lSknVwW8fCr0uVFV2ocC3g8WFzH4qnkuCRO7r7IgGRLlk/lL+YPoRNWyQSW/
+iHVv/xD8SlTQX/D67zZzfRs2RcYhbbQVuE7PnFylPVoAjgbjPGsye/Kf8Lb93/Ao
+GEjwxrzQvzSAlsJKsW2Ox5BF3i9nrEUEo3rcVZLJR2bYGozH7ZxOmuASu7VqTITh
+4SINhwBk/ox9Yjllpu9CtoAlEmEBqCQTcAARJl/6NVDFSMwGR+gn2HCNX2TmoUQm
+XiLsks3/QppEIW1cxeMiHV9HEufOX1362KqxMy3ZdvJOOjMMK7MtkAY=
+-----END CERTIFICATE-----
+
+# Issuer: CN=AddTrust Qualified CA Root O=AddTrust AB OU=AddTrust TTP Network
+# Subject: CN=AddTrust Qualified CA Root O=AddTrust AB OU=AddTrust TTP Network
+# Label: "AddTrust Qualified Certificates Root"
+# Serial: 1
+# MD5 Fingerprint: 27:ec:39:47:cd:da:5a:af:e2:9a:01:65:21:a9:4c:bb
+# SHA1 Fingerprint: 4d:23:78:ec:91:95:39:b5:00:7f:75:8f:03:3b:21:1e:c5:4d:8b:cf
+# SHA256 Fingerprint: 80:95:21:08:05:db:4b:bc:35:5e:44:28:d8:fd:6e:c2:cd:e3:ab:5f:b9:7a:99:42:98:8e:b8:f4:dc:d0:60:16
+-----BEGIN CERTIFICATE-----
+MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJTRTEU
+MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3
+b3JrMSMwIQYDVQQDExpBZGRUcnVzdCBRdWFsaWZpZWQgQ0EgUm9vdDAeFw0wMDA1
+MzAxMDQ0NTBaFw0yMDA1MzAxMDQ0NTBaMGcxCzAJBgNVBAYTAlNFMRQwEgYDVQQK
+EwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5ldHdvcmsxIzAh
+BgNVBAMTGkFkZFRydXN0IFF1YWxpZmllZCBDQSBSb290MIIBIjANBgkqhkiG9w0B
+AQEFAAOCAQ8AMIIBCgKCAQEA5B6a/twJWoekn0e+EV+vhDTbYjx5eLfpMLXsDBwq
+xBb/4Oxx64r1EW7tTw2R0hIYLUkVAcKkIhPHEWT/IhKauY5cLwjPcWqzZwFZ8V1G
+87B4pfYOQnrjfxvM0PC3KP0q6p6zsLkEqv32x7SxuCqg+1jxGaBvcCV+PmlKfw8i
+2O+tCBGaKZnhqkRFmhJePp1tUvznoD1oL/BLcHwTOK28FSXx1s6rosAx1i+f4P8U
+WfyEk9mHfExUE+uf0S0R+Bg6Ot4l2ffTQO2kBhLEO+GRwVY18BTcZTYJbqukB8c1
+0cIDMzZbdSZtQvESa0NvS3GU+jQd7RNuyoB/mC9suWXY6QIDAQABo4HUMIHRMB0G
+A1UdDgQWBBQ5lYtii1zJ1IC6WA+XPxUIQ8yYpzALBgNVHQ8EBAMCAQYwDwYDVR0T
+AQH/BAUwAwEB/zCBkQYDVR0jBIGJMIGGgBQ5lYtii1zJ1IC6WA+XPxUIQ8yYp6Fr
+pGkwZzELMAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQL
+ExRBZGRUcnVzdCBUVFAgTmV0d29yazEjMCEGA1UEAxMaQWRkVHJ1c3QgUXVhbGlm
+aWVkIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBABmrder4i2VhlRO6aQTv
+hsoToMeqT2QbPxj2qC0sVY8FtzDqQmodwCVRLae/DLPt7wh/bDxGGuoYQ992zPlm
+hpwsaPXpF/gxsxjE1kh9I0xowX67ARRvxdlu3rsEQmr49lx95dr6h+sNNVJn0J6X
+dgWTP5XHAeZpVTh/EGGZyeNfpso+gmNIquIISD6q8rKFYqa0p9m9N5xotS1WfbC3
+P6CxB9bpT9zeRXEwMn8bLgn5v1Kh7sKAPgZcLlVAwRv1cEWw3F369nJad9Jjzc9Y
+iQBCYz95OdBEsIJuQRno3eDBiFrRHnGTHyQwdOUeqN48Jzd/g66ed8/wMLH/S5no
+xqE=
+-----END CERTIFICATE-----
+
+# Issuer: CN=Entrust Root Certification Authority O=Entrust, Inc. OU=www.entrust.net/CPS is incorporated by reference/(c) 2006 Entrust, Inc.
+# Subject: CN=Entrust Root Certification Authority O=Entrust, Inc. OU=www.entrust.net/CPS is incorporated by reference/(c) 2006 Entrust, Inc.
+# Label: "Entrust Root Certification Authority"
+# Serial: 1164660820
+# MD5 Fingerprint: d6:a5:c3:ed:5d:dd:3e:00:c1:3d:87:92:1f:1d:3f:e4
+# SHA1 Fingerprint: b3:1e:b1:b7:40:e3:6c:84:02:da:dc:37:d4:4d:f5:d4:67:49:52:f9
+# SHA256 Fingerprint: 73:c1:76:43:4f:1b:c6:d5:ad:f4:5b:0e:76:e7:27:28:7c:8d:e5:76:16:c1:e6:e6:14:1a:2b:2c:bc:7d:8e:4c
+-----BEGIN CERTIFICATE-----
+MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMC
+VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0
+Lm5ldC9DUFMgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW
+KGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsGA1UEAxMkRW50cnVzdCBSb290IENl
+cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0MloXDTI2MTEyNzIw
+NTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMTkw
+NwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSBy
+ZWZlcmVuY2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNV
+BAMTJEVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJ
+KoZIhvcNAQEBBQADggEPADCCAQoCggEBALaVtkNC+sZtKm9I35RMOVcF7sN5EUFo
+Nu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYszA9u3g3s+IIRe7bJWKKf4
+4LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOwwCj0Yzfv9
+KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGI
+rb68j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi
+94DkZfs0Nw4pgHBNrziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOB
+sDCBrTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAi
+gA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1MzQyWjAfBgNVHSMEGDAWgBRo
+kORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DHhmak8fdLQ/uE
+vW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA
+A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9t
+O1KzKtvn1ISMY/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6Zua
+AGAT/3B+XxFNSRuzFVJ7yVTav52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP
+9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTSW3iDVuycNsMm4hH2Z0kdkquM++v/
+eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0tHuu2guQOHXvgR1m
+0vdXcDazv/wor3ElhVsT/h5/WrQ8
+-----END CERTIFICATE-----
+
+# Issuer: O=RSA Security Inc OU=RSA Security 2048 V3
+# Subject: O=RSA Security Inc OU=RSA Security 2048 V3
+# Label: "RSA Security 2048 v3"
+# Serial: 13297492616345471454730593562152402946
+# MD5 Fingerprint: 77:0d:19:b1:21:fd:00:42:9c:3e:0c:a5:dd:0b:02:8e
+# SHA1 Fingerprint: 25:01:90:19:cf:fb:d9:99:1c:b7:68:25:74:8d:94:5f:30:93:95:42
+# SHA256 Fingerprint: af:8b:67:62:a1:e5:28:22:81:61:a9:5d:5c:55:9e:e2:66:27:8f:75:d7:9e:83:01:89:a5:03:50:6a:bd:6b:4c
+-----BEGIN CERTIFICATE-----
+MIIDYTCCAkmgAwIBAgIQCgEBAQAAAnwAAAAKAAAAAjANBgkqhkiG9w0BAQUFADA6
+MRkwFwYDVQQKExBSU0EgU2VjdXJpdHkgSW5jMR0wGwYDVQQLExRSU0EgU2VjdXJp
+dHkgMjA0OCBWMzAeFw0wMTAyMjIyMDM5MjNaFw0yNjAyMjIyMDM5MjNaMDoxGTAX
+BgNVBAoTEFJTQSBTZWN1cml0eSBJbmMxHTAbBgNVBAsTFFJTQSBTZWN1cml0eSAy
+MDQ4IFYzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt49VcdKA3Xtp
+eafwGFAyPGJn9gqVB93mG/Oe2dJBVGutn3y+Gc37RqtBaB4Y6lXIL5F4iSj7Jylg
+/9+PjDvJSZu1pJTOAeo+tWN7fyb9Gd3AIb2E0S1PRsNO3Ng3OTsor8udGuorryGl
+wSMiuLgbWhOHV4PR8CDn6E8jQrAApX2J6elhc5SYcSa8LWrg903w8bYqODGBDSnh
+AMFRD0xS+ARaqn1y07iHKrtjEAMqs6FPDVpeRrc9DvV07Jmf+T0kgYim3WBU6JU2
+PcYJk5qjEoAAVZkZR73QpXzDuvsf9/UP+Ky5tfQ3mBMY3oVbtwyCO4dvlTlYMNpu
+AWgXIszACwIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB
+BjAfBgNVHSMEGDAWgBQHw1EwpKrpRa41JPr/JCwz0LGdjDAdBgNVHQ4EFgQUB8NR
+MKSq6UWuNST6/yQsM9CxnYwwDQYJKoZIhvcNAQEFBQADggEBAF8+hnZuuDU8TjYc
+HnmYv/3VEhF5Ug7uMYm83X/50cYVIeiKAVQNOvtUudZj1LGqlk2iQk3UUx+LEN5/
+Zb5gEydxiKRz44Rj0aRV4VCT5hsOedBnvEbIvz8XDZXmxpBp3ue0L96VfdASPz0+
+f00/FGj1EVDVwfSQpQgdMWD/YIwjVAqv/qFuxdF6Kmh4zx6CCiC0H63lhbJqaHVO
+rSU3lIW+vaHU6rcMSzyd6BIA8F+sDeGscGNz9395nzIlQnQFgCi/vcEkllgVsRch
+6YlL2weIZ/QVrXA+L02FO8K32/6YaCOJ4XQP3vTFhGMpG8zLB8kApKnXwiJPZ9d3
+7CAFYd4=
+-----END CERTIFICATE-----
+
+# Issuer: CN=GeoTrust Global CA O=GeoTrust Inc.
+# Subject: CN=GeoTrust Global CA O=GeoTrust Inc.
+# Label: "GeoTrust Global CA"
+# Serial: 144470
+# MD5 Fingerprint: f7:75:ab:29:fb:51:4e:b7:77:5e:ff:05:3c:99:8e:f5
+# SHA1 Fingerprint: de:28:f4:a4:ff:e5:b9:2f:a3:c5:03:d1:a3:49:a7:f9:96:2a:82:12
+# SHA256 Fingerprint: ff:85:6a:2d:25:1d:cd:88:d3:66:56:f4:50:12:67:98:cf:ab:aa:de:40:79:9c:72:2d:e4:d2:b5:db:36:a7:3a
+-----BEGIN CERTIFICATE-----
+MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT
+MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
+YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG
+EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg
+R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9
+9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq
+fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv
+iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU
+1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+
+bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW
+MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA
+ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l
+uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn
+Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS
+tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF
+PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un
+hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV
+5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw==
+-----END CERTIFICATE-----
+
+# Issuer: CN=GeoTrust Global CA 2 O=GeoTrust Inc.
+# Subject: CN=GeoTrust Global CA 2 O=GeoTrust Inc.
+# Label: "GeoTrust Global CA 2"
+# Serial: 1
+# MD5 Fingerprint: 0e:40:a7:6c:de:03:5d:8f:d1:0f:e4:d1:8d:f9:6c:a9
+# SHA1 Fingerprint: a9:e9:78:08:14:37:58:88:f2:05:19:b0:6d:2b:0d:2b:60:16:90:7d
+# SHA256 Fingerprint: ca:2d:82:a0:86:77:07:2f:8a:b6:76:4f:f0:35:67:6c:fe:3e:5e:32:5e:01:21:72:df:3f:92:09:6d:b7:9b:85
+-----BEGIN CERTIFICATE-----
+MIIDZjCCAk6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBEMQswCQYDVQQGEwJVUzEW
+MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFs
+IENBIDIwHhcNMDQwMzA0MDUwMDAwWhcNMTkwMzA0MDUwMDAwWjBEMQswCQYDVQQG
+EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3Qg
+R2xvYmFsIENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDvPE1A
+PRDfO1MA4Wf+lGAVPoWI8YkNkMgoI5kF6CsgncbzYEbYwbLVjDHZ3CB5JIG/NTL8
+Y2nbsSpr7iFY8gjpeMtvy/wWUsiRxP89c96xPqfCfWbB9X5SJBri1WeR0IIQ13hL
+TytCOb1kLUCgsBDTOEhGiKEMuzozKmKY+wCdE1l/bztyqu6mD4b5BWHqZ38MN5aL
+5mkWRxHCJ1kDs6ZgwiFAVvqgx306E+PsV8ez1q6diYD3Aecs9pYrEw15LNnA5IZ7
+S4wMcoKK+xfNAGw6EzywhIdLFnopsk/bHdQL82Y3vdj2V7teJHq4PIu5+pIaGoSe
+2HSPqht/XvT+RSIhAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE
+FHE4NvICMVNHK266ZUapEBVYIAUJMB8GA1UdIwQYMBaAFHE4NvICMVNHK266ZUap
+EBVYIAUJMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQUFAAOCAQEAA/e1K6td
+EPx7srJerJsOflN4WT5CBP51o62sgU7XAotexC3IUnbHLB/8gTKY0UvGkpMzNTEv
+/NgdRN3ggX+d6YvhZJFiCzkIjKx0nVnZellSlxG5FntvRdOW2TF9AjYPnDtuzywN
+A0ZF66D0f0hExghAzN4bcLUprbqLOzRldRtxIR0sFAqwlpW41uryZfspuk/qkZN0
+abby/+Ea0AzRdoXLiiW9l14sbxWZJue2Kf8i7MkCx1YAzUm5s2x7UwQa4qjJqhIF
+I8LO57sEAszAR6LkxCkvW0VXiVHuPOtSCP8HNR6fNWpHSlaY0VqFH4z1Ir+rzoPz
+4iIprn2DQKi6bA==
+-----END CERTIFICATE-----
+
+# Issuer: CN=GeoTrust Universal CA O=GeoTrust Inc.
+# Subject: CN=GeoTrust Universal CA O=GeoTrust Inc.
+# Label: "GeoTrust Universal CA"
+# Serial: 1
+# MD5 Fingerprint: 92:65:58:8b:a2:1a:31:72:73:68:5c:b4:a5:7a:07:48
+# SHA1 Fingerprint: e6:21:f3:35:43:79:05:9a:4b:68:30:9d:8a:2f:74:22:15:87:ec:79
+# SHA256 Fingerprint: a0:45:9b:9f:63:b2:25:59:f5:fa:5d:4c:6d:b3:f9:f7:2f:f1:93:42:03:35:78:f0:73:bf:1d:1b:46:cb:b9:12
+-----BEGIN CERTIFICATE-----
+MIIFaDCCA1CgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEW
+MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEeMBwGA1UEAxMVR2VvVHJ1c3QgVW5pdmVy
+c2FsIENBMB4XDTA0MDMwNDA1MDAwMFoXDTI5MDMwNDA1MDAwMFowRTELMAkGA1UE
+BhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xHjAcBgNVBAMTFUdlb1RydXN0
+IFVuaXZlcnNhbCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKYV
+VaCjxuAfjJ0hUNfBvitbtaSeodlyWL0AG0y/YckUHUWCq8YdgNY96xCcOq9tJPi8
+cQGeBvV8Xx7BDlXKg5pZMK4ZyzBIle0iN430SppyZj6tlcDgFgDgEB8rMQ7XlFTT
+QjOgNB0eRXbdT8oYN+yFFXoZCPzVx5zw8qkuEKmS5j1YPakWaDwvdSEYfyh3peFh
+F7em6fgemdtzbvQKoiFs7tqqhZJmr/Z6a4LauiIINQ/PQvE1+mrufislzDoR5G2v
+c7J2Ha3QsnhnGqQ5HFELZ1aD/ThdDc7d8Lsrlh/eezJS/R27tQahsiFepdaVaH/w
+mZ7cRQg+59IJDTWU3YBOU5fXtQlEIGQWFwMCTFMNaN7VqnJNk22CDtucvc+081xd
+VHppCZbW2xHBjXWotM85yM48vCR85mLK4b19p71XZQvk/iXttmkQ3CgaRr0BHdCX
+teGYO8A3ZNY9lO4L4fUorgtWv3GLIylBjobFS1J72HGrH4oVpjuDWtdYAVHGTEHZ
+f9hBZ3KiKN9gg6meyHv8U3NyWfWTehd2Ds735VzZC1U0oqpbtWpU5xPKV+yXbfRe
+Bi9Fi1jUIxaS5BZuKGNZMN9QAZxjiRqf2xeUgnA3wySemkfWWspOqGmJch+RbNt+
+nhutxx9z3SxPGWX9f5NAEC7S8O08ni4oPmkmM8V7AgMBAAGjYzBhMA8GA1UdEwEB
+/wQFMAMBAf8wHQYDVR0OBBYEFNq7LqqwDLiIJlF0XG0D08DYj3rWMB8GA1UdIwQY
+MBaAFNq7LqqwDLiIJlF0XG0D08DYj3rWMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG
+9w0BAQUFAAOCAgEAMXjmx7XfuJRAyXHEqDXsRh3ChfMoWIawC/yOsjmPRFWrZIRc
+aanQmjg8+uUfNeVE44B5lGiku8SfPeE0zTBGi1QrlaXv9z+ZhP015s8xxtxqv6fX
+IwjhmF7DWgh2qaavdy+3YL1ERmrvl/9zlcGO6JP7/TG37FcREUWbMPEaiDnBTzyn
+ANXH/KttgCJwpQzgXQQpAvvLoJHRfNbDflDVnVi+QTjruXU8FdmbyUqDWcDaU/0z
+uzYYm4UPFd3uLax2k7nZAY1IEKj79TiG8dsKxr2EoyNB3tZ3b4XUhRxQ4K5RirqN
+Pnbiucon8l+f725ZDQbYKxek0nxru18UGkiPGkzns0ccjkxFKyDuSN/n3QmOGKja
+QI2SJhFTYXNd673nxE0pN2HrrDktZy4W1vUAg4WhzH92xH3kt0tm7wNFYGm2DFKW
+koRepqO1pD4r2czYG0eq8kTaT/kD6PAUyz/zg97QwVTjt+gKN02LIFkDMBmhLMi9
+ER/frslKxfMnZmaGrGiR/9nmUxwPi1xpZQomyB40w11Re9epnAahNt3ViZS82eQt
+DF4JbAiXfKM9fJP/P6EUp8+1Xevb2xzEdt+Iub1FBZUbrvxGakyvSOPOrg/Sfuvm
+bJxPgWp6ZKy7PtXny3YuxadIwVyQD8vIP/rmMuGNG2+k5o7Y+SlIis5z/iw=
+-----END CERTIFICATE-----
+
+# Issuer: CN=GeoTrust Universal CA 2 O=GeoTrust Inc.
+# Subject: CN=GeoTrust Universal CA 2 O=GeoTrust Inc.
+# Label: "GeoTrust Universal CA 2"
+# Serial: 1
+# MD5 Fingerprint: 34:fc:b8:d0:36:db:9e:14:b3:c2:f2:db:8f:e4:94:c7
+# SHA1 Fingerprint: 37:9a:19:7b:41:85:45:35:0c:a6:03:69:f3:3c:2e:af:47:4f:20:79
+# SHA256 Fingerprint: a0:23:4f:3b:c8:52:7c:a5:62:8e:ec:81:ad:5d:69:89:5d:a5:68:0d:c9:1d:1c:b8:47:7f:33:f8:78:b9:5b:0b
+-----BEGIN CERTIFICATE-----
+MIIFbDCCA1SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJVUzEW
+MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVy
+c2FsIENBIDIwHhcNMDQwMzA0MDUwMDAwWhcNMjkwMzA0MDUwMDAwWjBHMQswCQYD
+VQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1
+c3QgVW5pdmVyc2FsIENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
+AQCzVFLByT7y2dyxUxpZKeexw0Uo5dfR7cXFS6GqdHtXr0om/Nj1XqduGdt0DE81
+WzILAePb63p3NeqqWuDW6KFXlPCQo3RWlEQwAx5cTiuFJnSCegx2oG9NzkEtoBUG
+FF+3Qs17j1hhNNwqCPkuwwGmIkQcTAeC5lvO0Ep8BNMZcyfwqph/Lq9O64ceJHdq
+XbboW0W63MOhBW9Wjo8QJqVJwy7XQYci4E+GymC16qFjwAGXEHm9ADwSbSsVsaxL
+se4YuU6W3Nx2/zu+z18DwPw76L5GG//aQMJS9/7jOvdqdzXQ2o3rXhhqMcceujwb
+KNZrVMaqW9eiLBsZzKIC9ptZvTdrhrVtgrrY6slWvKk2WP0+GfPtDCapkzj4T8Fd
+IgbQl+rhrcZV4IErKIM6+vR7IVEAvlI4zs1meaj0gVbi0IMJR1FbUGrP20gaXT73
+y/Zl92zxlfgCOzJWgjl6W70viRu/obTo/3+NjN8D8WBOWBFM66M/ECuDmgFz2ZRt
+hAAnZqzwcEAJQpKtT5MNYQlRJNiS1QuUYbKHsu3/mjX/hVTK7URDrBs8FmtISgoc
+QIgfksILAAX/8sgCSqSqqcyZlpwvWOB94b67B9xfBHJcMTTD7F8t4D1kkCLm0ey4
+Lt1ZrtmhN79UNdxzMk+MBB4zsslG8dhcyFVQyWi9qLo2CQIDAQABo2MwYTAPBgNV
+HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAfBgNV
+HSMEGDAWgBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAOBgNVHQ8BAf8EBAMCAYYwDQYJ
+KoZIhvcNAQEFBQADggIBAGbBxiPz2eAubl/oz66wsCVNK/g7WJtAJDday6sWSf+z
+dXkzoS9tcBc0kf5nfo/sm+VegqlVHy/c1FEHEv6sFj4sNcZj/NwQ6w2jqtB8zNHQ
+L1EuxBRa3ugZ4T7GzKQp5y6EqgYweHZUcyiYWTjgAA1i00J9IZ+uPTqM1fp3DRgr
+Fg5fNuH8KrUwJM/gYwx7WBr+mbpCErGR9Hxo4sjoryzqyX6uuyo9DRXcNJW2GHSo
+ag/HtPQTxORb7QrSpJdMKu0vbBKJPfEncKpqA1Ihn0CoZ1Dy81of398j9tx4TuaY
+T1U6U+Pv8vSfx3zYWK8pIpe44L2RLrB27FcRz+8pRPPphXpgY+RdM4kX2TGq2tbz
+GDVyz4crL2MjhF2EjD9XoIj8mZEoJmmZ1I+XRL6O1UixpCgp8RW04eWe3fiPpm8m
+1wk8OhwRDqZsN/etRIcsKMfYdIKz0G9KV7s1KSegi+ghp4dkNl3M2Basx7InQJJV
+OCiNUW7dFGdTbHFcJoRNdVq2fmBWqU2t+5sel/MN2dKXVHfaPRK34B7vCAas+YWH
+6aLcr34YEoP9VhdBLtUpgn2Z9DH2canPLAEnpQW5qrJITirvn5NSUZU8UnOOVkwX
+QMAJKOSLakhT2+zNVVXxxvjpoixMptEmX36vWkzaH6byHCx+rgIW0lbQL1dTR+iS
+-----END CERTIFICATE-----
+
+# Issuer: CN=America Online Root Certification Authority 1 O=America Online Inc.
+# Subject: CN=America Online Root Certification Authority 1 O=America Online Inc.
+# Label: "America Online Root Certification Authority 1"
+# Serial: 1
+# MD5 Fingerprint: 14:f1:08:ad:9d:fa:64:e2:89:e7:1c:cf:a8:ad:7d:5e
+# SHA1 Fingerprint: 39:21:c1:15:c1:5d:0e:ca:5c:cb:5b:c4:f0:7d:21:d8:05:0b:56:6a
+# SHA256 Fingerprint: 77:40:73:12:c6:3a:15:3d:5b:c0:0b:4e:51:75:9c:df:da:c2:37:dc:2a:33:b6:79:46:e9:8e:9b:fa:68:0a:e3
+-----BEGIN CERTIFICATE-----
+MIIDpDCCAoygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEc
+MBoGA1UEChMTQW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBP
+bmxpbmUgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAxMB4XDTAyMDUyODA2
+MDAwMFoXDTM3MTExOTIwNDMwMFowYzELMAkGA1UEBhMCVVMxHDAaBgNVBAoTE0Ft
+ZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2EgT25saW5lIFJvb3Qg
+Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMTCCASIwDQYJKoZIhvcNAQEBBQADggEP
+ADCCAQoCggEBAKgv6KRpBgNHw+kqmP8ZonCaxlCyfqXfaE0bfA+2l2h9LaaLl+lk
+hsmj76CGv2BlnEtUiMJIxUo5vxTjWVXlGbR0yLQFOVwWpeKVBeASrlmLojNoWBym
+1BW32J/X3HGrfpq/m44zDyL9Hy7nBzbvYjnF3cu6JRQj3gzGPTzOggjmZj7aUTsW
+OqMFf6Dch9Wc/HKpoH145LcxVR5lu9RhsCFg7RAycsWSJR74kEoYeEfffjA3PlAb
+2xzTa5qGUwew76wGePiEmf4hjUyAtgyC9mZweRrTT6PP8c9GsEsPPt2IYriMqQko
+O3rHl+Ee5fSfwMCuJKDIodkP1nsmgmkyPacCAwEAAaNjMGEwDwYDVR0TAQH/BAUw
+AwEB/zAdBgNVHQ4EFgQUAK3Zo/Z59m50qX8zPYEX10zPM94wHwYDVR0jBBgwFoAU
+AK3Zo/Z59m50qX8zPYEX10zPM94wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB
+BQUAA4IBAQB8itEfGDeC4Liwo+1WlchiYZwFos3CYiZhzRAW18y0ZTTQEYqtqKkF
+Zu90821fnZmv9ov761KyBZiibyrFVL0lvV+uyIbqRizBs73B6UlwGBaXCBOMIOAb
+LjpHyx7kADCVW/RFo8AasAFOq73AI25jP4BKxQft3OJvx8Fi8eNy1gTIdGcL+oir
+oQHIb/AUr9KZzVGTfu0uOMe9zkZQPXLjeSWdm4grECDdpbgyn43gKd8hdIaC2y+C
+MMbHNYaz+ZZfRtsMRf3zUMNvxsNIrUam4SdHCh0Om7bCd39j8uB9Gr784N/Xx6ds
+sPmuujz9dLQR6FgNgLzTqIA6me11zEZ7
+-----END CERTIFICATE-----
+
+# Issuer: CN=America Online Root Certification Authority 2 O=America Online Inc.
+# Subject: CN=America Online Root Certification Authority 2 O=America Online Inc.
+# Label: "America Online Root Certification Authority 2"
+# Serial: 1
+# MD5 Fingerprint: d6:ed:3c:ca:e2:66:0f:af:10:43:0d:77:9b:04:09:bf
+# SHA1 Fingerprint: 85:b5:ff:67:9b:0c:79:96:1f:c8:6e:44:22:00:46:13:db:17:92:84
+# SHA256 Fingerprint: 7d:3b:46:5a:60:14:e5:26:c0:af:fc:ee:21:27:d2:31:17:27:ad:81:1c:26:84:2d:00:6a:f3:73:06:cc:80:bd
+-----BEGIN CERTIFICATE-----
+MIIFpDCCA4ygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEc
+MBoGA1UEChMTQW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBP
+bmxpbmUgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAyMB4XDTAyMDUyODA2
+MDAwMFoXDTM3MDkyOTE0MDgwMFowYzELMAkGA1UEBhMCVVMxHDAaBgNVBAoTE0Ft
+ZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2EgT25saW5lIFJvb3Qg
+Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIP
+ADCCAgoCggIBAMxBRR3pPU0Q9oyxQcngXssNt79Hc9PwVU3dxgz6sWYFas14tNwC
+206B89enfHG8dWOgXeMHDEjsJcQDIPT/DjsS/5uN4cbVG7RtIuOx238hZK+GvFci
+KtZHgVdEglZTvYYUAQv8f3SkWq7xuhG1m1hagLQ3eAkzfDJHA1zEpYNI9FdWboE2
+JxhP7JsowtS013wMPgwr38oE18aO6lhOqKSlGBxsRZijQdEt0sdtjRnxrXm3gT+9
+BoInLRBYBbV4Bbkv2wxrkJB+FFk4u5QkE+XRnRTf04JNRvCAOVIyD+OEsnpD8l7e
+Xz8d3eOyG6ChKiMDbi4BFYdcpnV1x5dhvt6G3NRI270qv0pV2uh9UPu0gBe4lL8B
+PeraunzgWGcXuVjgiIZGZ2ydEEdYMtA1fHkqkKJaEBEjNa0vzORKW6fIJ/KD3l67
+Xnfn6KVuY8INXWHQjNJsWiEOyiijzirplcdIz5ZvHZIlyMbGwcEMBawmxNJ10uEq
+Z8A9W6Wa6897GqidFEXlD6CaZd4vKL3Ob5Rmg0gp2OpljK+T2WSfVVcmv2/LNzGZ
+o2C7HK2JNDJiuEMhBnIMoVxtRsX6Kc8w3onccVvdtjc+31D1uAclJuW8tf48ArO3
++L5DwYcRlJ4jbBeKuIonDFRH8KmzwICMoCfrHRnjB453cMor9H124HhnAgMBAAGj
+YzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFE1FwWg4u3OpaaEg5+31IqEj
+FNeeMB8GA1UdIwQYMBaAFE1FwWg4u3OpaaEg5+31IqEjFNeeMA4GA1UdDwEB/wQE
+AwIBhjANBgkqhkiG9w0BAQUFAAOCAgEAZ2sGuV9FOypLM7PmG2tZTiLMubekJcmn
+xPBUlgtk87FYT15R/LKXeydlwuXK5w0MJXti4/qftIe3RUavg6WXSIylvfEWK5t2
+LHo1YGwRgJfMqZJS5ivmae2p+DYtLHe/YUjRYwu5W1LtGLBDQiKmsXeu3mnFzccc
+obGlHBD7GL4acN3Bkku+KVqdPzW+5X1R+FXgJXUjhx5c3LqdsKyzadsXg8n33gy8
+CNyRnqjQ1xU3c6U1uPx+xURABsPr+CKAXEfOAuMRn0T//ZoyzH1kUQ7rVyZ2OuMe
+IjzCpjbdGe+n/BLzJsBZMYVMnNjP36TMzCmT/5RtdlwTCJfy7aULTd3oyWgOZtMA
+DjMSW7yV5TKQqLPGbIOtd+6Lfn6xqavT4fG2wLHqiMDn05DpKJKUe2h7lyoKZy2F
+AjgQ5ANh1NolNscIWC2hp1GvMApJ9aZphwctREZ2jirlmjvXGKL8nDgQzMY70rUX
+Om/9riW99XJZZLF0KjhfGEzfz3EEWjbUvy+ZnOjZurGV5gJLIaFb1cFPj65pbVPb
+AZO1XB4Y3WRayhgoPmMEEf0cjQAPuDffZ4qdZqkCapH/E8ovXYO8h5Ns3CRRFgQl
+Zvqz2cK6Kb6aSDiCmfS/O0oxGfm/jiEzFMpPVF/7zvuPcX/9XhmgD0uRuMRUvAaw
+RY8mkaKO/qk=
+-----END CERTIFICATE-----
+
+# Issuer: CN=Visa eCommerce Root O=VISA OU=Visa International Service Association
+# Subject: CN=Visa eCommerce Root O=VISA OU=Visa International Service Association
+# Label: "Visa eCommerce Root"
+# Serial: 25952180776285836048024890241505565794
+# MD5 Fingerprint: fc:11:b8:d8:08:93:30:00:6d:23:f9:7e:eb:52:1e:02
+# SHA1 Fingerprint: 70:17:9b:86:8c:00:a4:fa:60:91:52:22:3f:9f:3e:32:bd:e0:05:62
+# SHA256 Fingerprint: 69:fa:c9:bd:55:fb:0a:c7:8d:53:bb:ee:5c:f1:d5:97:98:9f:d0:aa:ab:20:a2:51:51:bd:f1:73:3e:e7:d1:22
+-----BEGIN CERTIFICATE-----
+MIIDojCCAoqgAwIBAgIQE4Y1TR0/BvLB+WUF1ZAcYjANBgkqhkiG9w0BAQUFADBr
+MQswCQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRl
+cm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNv
+bW1lcmNlIFJvb3QwHhcNMDIwNjI2MDIxODM2WhcNMjIwNjI0MDAxNjEyWjBrMQsw
+CQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRlcm5h
+dGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNvbW1l
+cmNlIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvV95WHm6h
+2mCxlCfLF9sHP4CFT8icttD0b0/Pmdjh28JIXDqsOTPHH2qLJj0rNfVIsZHBAk4E
+lpF7sDPwsRROEW+1QK8bRaVK7362rPKgH1g/EkZgPI2h4H3PVz4zHvtH8aoVlwdV
+ZqW1LS7YgFmypw23RuwhY/81q6UCzyr0TP579ZRdhE2o8mCP2w4lPJ9zcc+U30rq
+299yOIzzlr3xF7zSujtFWsan9sYXiwGd/BmoKoMWuDpI/k4+oKsGGelT84ATB+0t
+vz8KPFUgOSwsAGl0lUq8ILKpeeUYiZGo3BxN77t+Nwtd/jmliFKMAGzsGHxBvfaL
+dXe6YJ2E5/4tAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD
+AgEGMB0GA1UdDgQWBBQVOIMPPyw/cDMezUb+B4wg4NfDtzANBgkqhkiG9w0BAQUF
+AAOCAQEAX/FBfXxcCLkr4NWSR/pnXKUTwwMhmytMiUbPWU3J/qVAtmPN3XEolWcR
+zCSs00Rsca4BIGsDoo8Ytyk6feUWYFN4PMCvFYP3j1IzJL1kk5fui/fbGKhtcbP3
+LBfQdCVp9/5rPJS+TUtBjE7ic9DjkCJzQ83z7+pzzkWKsKZJ/0x9nXGIxHYdkFsd
+7v3M9+79YKWxehZx0RbQfBI8bGmX265fOZpwLwU8GUYEmSA20GBuYQa7FkKMcPcw
+++DbZqMAAb3mLNqRX6BGi01qnD093QVG/na/oAo85ADmJ7f/hC3euiInlhBx6yLt
+398znM/jra6O1I7mT1GvFpLgXPYHDw==
+-----END CERTIFICATE-----
+
+# Issuer: CN=Certum CA O=Unizeto Sp. z o.o.
+# Subject: CN=Certum CA O=Unizeto Sp. z o.o.
+# Label: "Certum Root CA"
+# Serial: 65568
+# MD5 Fingerprint: 2c:8f:9f:66:1d:18:90:b1:47:26:9d:8e:86:82:8c:a9
+# SHA1 Fingerprint: 62:52:dc:40:f7:11:43:a2:2f:de:9e:f7:34:8e:06:42:51:b1:81:18
+# SHA256 Fingerprint: d8:e0:fe:bc:1d:b2:e3:8d:00:94:0f:37:d2:7d:41:34:4d:99:3e:73:4b:99:d5:65:6d:97:78:d4:d8:14:36:24
+-----BEGIN CERTIFICATE-----
+MIIDDDCCAfSgAwIBAgIDAQAgMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYTAlBM
+MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD
+QTAeFw0wMjA2MTExMDQ2MzlaFw0yNzA2MTExMDQ2MzlaMD4xCzAJBgNVBAYTAlBM
+MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD
+QTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6xwS7TT3zNJc4YPk/E
+jG+AanPIW1H4m9LcuwBcsaD8dQPugfCI7iNS6eYVM42sLQnFdvkrOYCJ5JdLkKWo
+ePhzQ3ukYbDYWMzhbGZ+nPMJXlVjhNWo7/OxLjBos8Q82KxujZlakE403Daaj4GI
+ULdtlkIJ89eVgw1BS7Bqa/j8D35in2fE7SZfECYPCE/wpFcozo+47UX2bu4lXapu
+Ob7kky/ZR6By6/qmW6/KUz/iDsaWVhFu9+lmqSbYf5VT7QqFiLpPKaVCjF62/IUg
+AKpoC6EahQGcxEZjgoi2IrHu/qpGWX7PNSzVttpd90gzFFS269lvzs2I1qsb2pY7
+HVkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEA
+uI3O7+cUus/usESSbLQ5PqKEbq24IXfS1HeCh+YgQYHu4vgRt2PRFze+GXYkHAQa
+TOs9qmdvLdTN/mUxcMUbpgIKumB7bVjCmkn+YzILa+M6wKyrO7Do0wlRjBCDxjTg
+xSvgGrZgFCdsMneMvLJymM/NzD+5yCRCFNZX/OYmQ6kd5YCQzgNUKD73P9P4Te1q
+CjqTE5s7FCMTY5w/0YcneeVMUeMBrYVdGjux1XMQpNPyvG5k9VpWkKjHDkx0Dy5x
+O/fIR/RpbxXyEV6DHpx8Uq79AtoSqFlnGNu8cN2bsWntgM6JQEhqDjXKKWYVIZQs
+6GAqm4VKQPNriiTsBhYscw==
+-----END CERTIFICATE-----
+
+# Issuer: CN=AAA Certificate Services O=Comodo CA Limited
+# Subject: CN=AAA Certificate Services O=Comodo CA Limited
+# Label: "Comodo AAA Services root"
+# Serial: 1
+# MD5 Fingerprint: 49:79:04:b0:eb:87:19:ac:47:b0:bc:11:51:9b:74:d0
+# SHA1 Fingerprint: d1:eb:23:a4:6d:17:d6:8f:d9:25:64:c2:f1:f1:60:17:64:d8:e3:49
+# SHA256 Fingerprint: d7:a7:a0:fb:5d:7e:27:31:d7:71:e9:48:4e:bc:de:f7:1d:5f:0c:3e:0a:29:48:78:2b:c8:3e:e0:ea:69:9e:f4
+-----BEGIN CERTIFICATE-----
+MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb
+MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow
+GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj
+YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL
+MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE
+BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM
+GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP
+ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua
+BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe
+3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4
+YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR
+rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm
+ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU
+oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF
+MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v
+QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t
+b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF
+AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q
+GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz
+Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2
+G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi
+l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3
+smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg==
+-----END CERTIFICATE-----
+
+# Issuer: CN=Secure Certificate Services O=Comodo CA Limited
+# Subject: CN=Secure Certificate Services O=Comodo CA Limited
+# Label: "Comodo Secure Services root"
+# Serial: 1
+# MD5 Fingerprint: d3:d9:bd:ae:9f:ac:67:24:b3:c8:1b:52:e1:b9:a9:bd
+# SHA1 Fingerprint: 4a:65:d5:f4:1d:ef:39:b8:b8:90:4a:4a:d3:64:81:33:cf:c7:a1:d1
+# SHA256 Fingerprint: bd:81:ce:3b:4f:65:91:d1:1a:67:b5:fc:7a:47:fd:ef:25:52:1b:f9:aa:4e:18:b9:e3:df:2e:34:a7:80:3b:e8
+-----BEGIN CERTIFICATE-----
+MIIEPzCCAyegAwIBAgIBATANBgkqhkiG9w0BAQUFADB+MQswCQYDVQQGEwJHQjEb
+MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow
+GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEkMCIGA1UEAwwbU2VjdXJlIENlcnRp
+ZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVow
+fjELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
+A1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxJDAiBgNV
+BAMMG1NlY3VyZSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEB
+BQADggEPADCCAQoCggEBAMBxM4KK0HDrc4eCQNUd5MvJDkKQ+d40uaG6EfQlhfPM
+cm3ye5drswfxdySRXyWP9nQ95IDC+DwN879A6vfIUtFyb+/Iq0G4bi4XKpVpDM3S
+HpR7LZQdqnXXs5jLrLxkU0C8j6ysNstcrbvd4JQX7NFc0L/vpZXJkMWwrPsbQ996
+CF23uPJAGysnnlDOXmWCiIxe004MeuoIkbY2qitC++rCoznl2yY4rYsK7hljxxwk
+3wN42ubqwUcaCwtGCd0C/N7Lh1/XMGNooa7cMqG6vv5Eq2i2pRcV/b3Vp6ea5EQz
+6YiO/O1R65NxTq0B50SOqy3LqP4BSUjwwN3HaNiS/j0CAwEAAaOBxzCBxDAdBgNV
+HQ4EFgQUPNiTiMLAggnMAZkGkyDpnnAJY08wDgYDVR0PAQH/BAQDAgEGMA8GA1Ud
+EwEB/wQFMAMBAf8wgYEGA1UdHwR6MHgwO6A5oDeGNWh0dHA6Ly9jcmwuY29tb2Rv
+Y2EuY29tL1NlY3VyZUNlcnRpZmljYXRlU2VydmljZXMuY3JsMDmgN6A1hjNodHRw
+Oi8vY3JsLmNvbW9kby5uZXQvU2VjdXJlQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmww
+DQYJKoZIhvcNAQEFBQADggEBAIcBbSMdflsXfcFhMs+P5/OKlFlm4J4oqF7Tt/Q0
+5qo5spcWxYJvMqTpjOev/e/C6LlLqqP05tqNZSH7uoDrJiiFGv45jN5bBAS0VPmj
+Z55B+glSzAVIqMk/IQQezkhr/IXownuvf7fM+F86/TXGDe+X3EyrEeFryzHRbPtI
+gKvcnDe4IRRLDXE97IMzbtFuMhbsmMcWi1mmNKsFVy2T96oTy9IT4rcuO81rUBcJ
+aD61JlfutuC23bkpgHl9j6PwpCikFcSF9CfUa7/lXORlAnZUtOM3ZiTTGWHIUhDl
+izeauan5Hb/qmZJhlv8BzaFfDbxxvA6sCx1HRR3B7Hzs/Sk=
+-----END CERTIFICATE-----
+
+# Issuer: CN=Trusted Certificate Services O=Comodo CA Limited
+# Subject: CN=Trusted Certificate Services O=Comodo CA Limited
+# Label: "Comodo Trusted Services root"
+# Serial: 1
+# MD5 Fingerprint: 91:1b:3f:6e:cd:9e:ab:ee:07:fe:1f:71:d2:b3:61:27
+# SHA1 Fingerprint: e1:9f:e3:0e:8b:84:60:9e:80:9b:17:0d:72:a8:c5:ba:6e:14:09:bd
+# SHA256 Fingerprint: 3f:06:e5:56:81:d4:96:f5:be:16:9e:b5:38:9f:9f:2b:8f:f6:1e:17:08:df:68:81:72:48:49:cd:5d:27:cb:69
+-----BEGIN CERTIFICATE-----
+MIIEQzCCAyugAwIBAgIBATANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJHQjEb
+MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow
+GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDElMCMGA1UEAwwcVHJ1c3RlZCBDZXJ0
+aWZpY2F0ZSBTZXJ2aWNlczAeFw0wNDAxMDEwMDAwMDBaFw0yODEyMzEyMzU5NTla
+MH8xCzAJBgNVBAYTAkdCMRswGQYDVQQIDBJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO
+BgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoMEUNvbW9kbyBDQSBMaW1pdGVkMSUwIwYD
+VQQDDBxUcnVzdGVkIENlcnRpZmljYXRlIFNlcnZpY2VzMIIBIjANBgkqhkiG9w0B
+AQEFAAOCAQ8AMIIBCgKCAQEA33FvNlhTWvI2VFeAxHQIIO0Yfyod5jWaHiWsnOWW
+fnJSoBVC21ndZHoa0Lh73TkVvFVIxO06AOoxEbrycXQaZ7jPM8yoMa+j49d/vzMt
+TGo87IvDktJTdyR0nAducPy9C1t2ul/y/9c3S0pgePfw+spwtOpZqqPOSC+pw7IL
+fhdyFgymBwwbOM/JYrc/oJOlh0Hyt3BAd9i+FHzjqMB6juljatEPmsbS9Is6FARW
+1O24zG71++IsWL1/T2sr92AkWCTOJu80kTrV44HQsvAEAtdbtz6SrGsSivnkBbA7
+kUlcsutT6vifR4buv5XAwAaf0lteERv0xwQ1KdJVXOTt6wIDAQABo4HJMIHGMB0G
+A1UdDgQWBBTFe1i97doladL3WRaoszLAeydb9DAOBgNVHQ8BAf8EBAMCAQYwDwYD
+VR0TAQH/BAUwAwEB/zCBgwYDVR0fBHwwejA8oDqgOIY2aHR0cDovL2NybC5jb21v
+ZG9jYS5jb20vVHJ1c3RlZENlcnRpZmljYXRlU2VydmljZXMuY3JsMDqgOKA2hjRo
+dHRwOi8vY3JsLmNvbW9kby5uZXQvVHJ1c3RlZENlcnRpZmljYXRlU2VydmljZXMu
+Y3JsMA0GCSqGSIb3DQEBBQUAA4IBAQDIk4E7ibSvuIQSTI3S8NtwuleGFTQQuS9/
+HrCoiWChisJ3DFBKmwCL2Iv0QeLQg4pKHBQGsKNoBXAxMKdTmw7pSqBYaWcOrp32
+pSxBvzwGa+RZzG0Q8ZZvH9/0BAKkn0U+yNj6NkZEUD+Cl5EfKNsYEYwq5GWDVxIS
+jBc/lDb+XbDABHcTuPQV1T84zJQ6VdCsmPW6AF/ghhmBeC8owH7TzEIK9a5QoNE+
+xqFx7D+gIIxmOom0jtTYsU0lR+4viMi14QVFwL4Ucd56/Y57fU0IlqUSc/Atyjcn
+dBInTMu2l+nZrghtWjlA3QVHdWpaIbOjGM9O9y5Xt5hwXsjEeLBi
+-----END CERTIFICATE-----
+
+# Issuer: CN=QuoVadis Root Certification Authority O=QuoVadis Limited OU=Root Certification Authority
+# Subject: CN=QuoVadis Root Certification Authority O=QuoVadis Limited OU=Root Certification Authority
+# Label: "QuoVadis Root CA"
+# Serial: 985026699
+# MD5 Fingerprint: 27:de:36:fe:72:b7:00:03:00:9d:f4:f0:1e:6c:04:24
+# SHA1 Fingerprint: de:3f:40:bd:50:93:d3:9b:6c:60:f6:da:bc:07:62:01:00:89:76:c9
+# SHA256 Fingerprint: a4:5e:de:3b:bb:f0:9c:8a:e1:5c:72:ef:c0:72:68:d6:93:a2:1c:99:6f:d5:1e:67:ca:07:94:60:fd:6d:88:73
+-----BEGIN CERTIFICATE-----
+MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJC
+TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0
+aWZpY2F0aW9uIEF1dGhvcml0eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0
+aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAzMTkxODMzMzNaFw0yMTAzMTcxODMz
+MzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUw
+IwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQDEyVR
+dW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG
+9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Yp
+li4kVEAkOPcahdxYTMukJ0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2D
+rOpm2RgbaIr1VxqYuvXtdj182d6UajtLF8HVj71lODqV0D1VNk7feVcxKh7YWWVJ
+WCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeLYzcS19Dsw3sgQUSj7cug
+F+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWenAScOospU
+xbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCC
+Ak4wPQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVv
+dmFkaXNvZmZzaG9yZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREw
+ggENMIIBCQYJKwYBBAG+WAABMIH7MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNl
+IG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBh
+c3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFy
+ZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh
+Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYI
+KwYBBQUHAgEWFmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3T
+KbkGGew5Oanwl4Rqy+/fMIGuBgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rq
+y+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1p
+dGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYD
+VQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6tlCL
+MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSk
+fnIYj9lofFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf8
+7C9TqnN7Az10buYWnuulLsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1R
+cHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2xgI4JVrmcGmD+XcHXetwReNDWXcG31a0y
+mQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi5upZIof4l/UO/erMkqQW
+xFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi5nrQNiOK
+SnQ2+Q==
+-----END CERTIFICATE-----
+
+# Issuer: CN=QuoVadis Root CA 2 O=QuoVadis Limited
+# Subject: CN=QuoVadis Root CA 2 O=QuoVadis Limited
+# Label: "QuoVadis Root CA 2"
+# Serial: 1289
+# MD5 Fingerprint: 5e:39:7b:dd:f8:ba:ec:82:e9:ac:62:ba:0c:54:00:2b
+# SHA1 Fingerprint: ca:3a:fb:cf:12:40:36:4b:44:b2:16:20:88:80:48:39:19:93:7c:f7
+# SHA256 Fingerprint: 85:a0:dd:7d:d7:20:ad:b7:ff:05:f8:3d:54:2b:20:9d:c7:ff:45:28:f7:d6:77:b1:83:89:fe:a5:e5:c4:9e:86
+-----BEGIN CERTIFICATE-----
+MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x
+GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv
+b3QgQ0EgMjAeFw0wNjExMjQxODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNV
+BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W
+YWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCa
+GMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6XJxg
+Fyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55J
+WpzmM+Yklvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bB
+rrcCaoF6qUWD4gXmuVbBlDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp
++ARz8un+XJiM9XOva7R+zdRcAitMOeGylZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1
+ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt66/3FsvbzSUr5R/7mp/i
+Ucw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1JdxnwQ5hYIiz
+PtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og
+/zOhD7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UH
+oycR7hYQe7xFSkyyBNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuI
+yV77zGHcizN300QyNQliBJIWENieJ0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1Ud
+EwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBQahGK8SEwzJQTU7tD2
+A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGUa6FJpEcwRTEL
+MAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT
+ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2f
+BluornFdLwUvZ+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzn
+g/iN/Ae42l9NLmeyhP3ZRPx3UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2Bl
+fF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodmVjB3pjd4M1IQWK4/YY7yarHvGH5K
+WWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK+JDSV6IZUaUtl0Ha
+B0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrWIozc
+hLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPR
+TUIZ3Ph1WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWD
+mbA4CD/pXvk1B+TJYm5Xf6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0Z
+ohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y
+4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8VCLAAVBpQ570su9t+Oza
+8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u
+-----END CERTIFICATE-----
+
+# Issuer: CN=QuoVadis Root CA 3 O=QuoVadis Limited
+# Subject: CN=QuoVadis Root CA 3 O=QuoVadis Limited
+# Label: "QuoVadis Root CA 3"
+# Serial: 1478
+# MD5 Fingerprint: 31:85:3c:62:94:97:63:b9:aa:fd:89:4e:af:6f:e0:cf
+# SHA1 Fingerprint: 1f:49:14:f7:d8:74:95:1d:dd:ae:02:c0:be:fd:3a:2d:82:75:51:85
+# SHA256 Fingerprint: 18:f1:fc:7f:20:5d:f8:ad:dd:eb:7f:e0:07:dd:57:e3:af:37:5a:9c:4d:8d:73:54:6b:f4:f1:fe:d1:e1:8d:35
+-----BEGIN CERTIFICATE-----
+MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x
+GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv
+b3QgQ0EgMzAeFw0wNjExMjQxOTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNV
+BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W
+YWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDM
+V0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNggDhoB
+4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUr
+H556VOijKTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd
+8lyyBTNvijbO0BNO/79KDDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9Cabwv
+vWhDFlaJKjdhkf2mrk7AyxRllDdLkgbvBNDInIjbC3uBr7E9KsRlOni27tyAsdLT
+mZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwpp5ijJUMv7/FfJuGITfhe
+btfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8nT8KKdjc
+T5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDt
+WAEXMJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZ
+c6tsgLjoC2SToJyMGf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A
+4iLItLRkT9a6fUg+qGkM17uGcclzuD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYD
+VR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHTBgkrBgEEAb5YAAMwgcUwgZMG
+CCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmljYXRlIGNvbnN0
+aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0
+aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVu
+dC4wLQYIKwYBBQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2Nw
+czALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4G
+A1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4ywLQoUmkRzBFMQswCQYDVQQGEwJC
+TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UEAxMSUXVvVmFkaXMg
+Um9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZVqyM0
+7ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSem
+d1o417+shvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd
++LJ2w/w4E6oM3kJpK27zPOuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B
+4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadN
+t54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp8kokUvd0/bpO5qgdAm6x
+DYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBCbjPsMZ57
+k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6s
+zHXug/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0j
+Wy10QJLZYxkNc91pvGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeT
+mJlglFwjz1onl14LBQaTNx47aTbrqZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK
+4SVhM7JZG+Ju1zdXtg2pEto=
+-----END CERTIFICATE-----
+
+# Issuer: O=SECOM Trust.net OU=Security Communication RootCA1
+# Subject: O=SECOM Trust.net OU=Security Communication RootCA1
+# Label: "Security Communication Root CA"
+# Serial: 0
+# MD5 Fingerprint: f1:bc:63:6a:54:e0:b5:27:f5:cd:e7:1a:e3:4d:6e:4a
+# SHA1 Fingerprint: 36:b1:2b:49:f9:81:9e:d7:4c:9e:bc:38:0f:c6:56:8f:5d:ac:b2:f7
+# SHA256 Fingerprint: e7:5e:72:ed:9f:56:0e:ec:6e:b4:80:00:73:a4:3f:c3:ad:19:19:5a:39:22:82:01:78:95:97:4a:99:02:6b:6c
+-----BEGIN CERTIFICATE-----
+MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEY
+MBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21t
+dW5pY2F0aW9uIFJvb3RDQTEwHhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5
+WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYD
+VQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw8yl8
+9f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJ
+DKaVv0uMDPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9
+Ms+k2Y7CI9eNqPPYJayX5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/N
+QV3Is00qVUarH9oe4kA92819uZKAnDfdDJZkndwi92SL32HeFZRSFaB9UslLqCHJ
+xrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2JChzAgMBAAGjPzA9MB0G
+A1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYwDwYDVR0T
+AQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vG
+kl3g0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfr
+Uj94nK9NrvjVT8+amCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5
+Bw+SUEmK3TGXX8npN6o7WWWXlDLJs58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJU
+JRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ6rBK+1YWc26sTfcioU+tHXot
+RSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAiFL39vmwLAw==
+-----END CERTIFICATE-----
+
+# Issuer: CN=Sonera Class2 CA O=Sonera
+# Subject: CN=Sonera Class2 CA O=Sonera
+# Label: "Sonera Class 2 Root CA"
+# Serial: 29
+# MD5 Fingerprint: a3:ec:75:0f:2e:88:df:fa:48:01:4e:0b:5c:48:6f:fb
+# SHA1 Fingerprint: 37:f7:6d:e6:07:7c:90:c5:b1:3e:93:1a:b7:41:10:b4:f2:e4:9a:27
+# SHA256 Fingerprint: 79:08:b4:03:14:c1:38:10:0b:51:8d:07:35:80:7f:fb:fc:f8:51:8a:00:95:33:71:05:ba:38:6b:15:3d:d9:27
+-----BEGIN CERTIFICATE-----
+MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEP
+MA0GA1UEChMGU29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAx
+MDQwNjA3Mjk0MFoXDTIxMDQwNjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNV
+BAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJhIENsYXNzMiBDQTCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE+hY3/Ei9vX+ALTU74W+o
+Z6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gXGM2RX/uJ4+q/Tl18GybTdXnt
+5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQTiMofRhj8VQ7Jp12W5dCsv+u8E7s
+3TmVToMGf+dJQMjFAbJUWmYdPfz56TwKnoG4cPABi+QjVHzIrviQHgCWctRUz2Ej
+vOr7nQKV0ba5cTppCD8PtOFCx4j1P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu
+8nYybieDwnPz3BjotJPqdURrBGAgcVeHnfO+oJAjPYok4doh28MCAwEAAaMzMDEw
+DwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQISqCqWITTXjwwCwYDVR0PBAQDAgEG
+MA0GCSqGSIb3DQEBBQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt0jSv9zil
+zqsWuasvfDXLrNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/
+3DEIcbCdjdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvD
+FNr450kkkdAdavphOe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6
+Tk6ezAyNlNzZRZxe7EJQY670XcSxEtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2
+ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLHllpwrN9M
+-----END CERTIFICATE-----
+
+# Issuer: CN=Staat der Nederlanden Root CA O=Staat der Nederlanden
+# Subject: CN=Staat der Nederlanden Root CA O=Staat der Nederlanden
+# Label: "Staat der Nederlanden Root CA"
+# Serial: 10000010
+# MD5 Fingerprint: 60:84:7c:5a:ce:db:0c:d4:cb:a7:e9:fe:02:c6:a9:c0
+# SHA1 Fingerprint: 10:1d:fa:3f:d5:0b:cb:bb:9b:b5:60:0c:19:55:a4:1a:f4:73:3a:04
+# SHA256 Fingerprint: d4:1d:82:9e:8c:16:59:82:2a:f9:3f:ce:62:bf:fc:de:26:4f:c8:4e:8b:95:0c:5f:f2:75:d0:52:35:46:95:a3
+-----BEGIN CERTIFICATE-----
+MIIDujCCAqKgAwIBAgIEAJiWijANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQGEwJO
+TDEeMBwGA1UEChMVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSYwJAYDVQQDEx1TdGFh
+dCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQTAeFw0wMjEyMTcwOTIzNDlaFw0xNTEy
+MTYwOTE1MzhaMFUxCzAJBgNVBAYTAk5MMR4wHAYDVQQKExVTdGFhdCBkZXIgTmVk
+ZXJsYW5kZW4xJjAkBgNVBAMTHVN0YWF0IGRlciBOZWRlcmxhbmRlbiBSb290IENB
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmNK1URF6gaYUmHFtvszn
+ExvWJw56s2oYHLZhWtVhCb/ekBPHZ+7d89rFDBKeNVU+LCeIQGv33N0iYfXCxw71
+9tV2U02PjLwYdjeFnejKScfST5gTCaI+Ioicf9byEGW07l8Y1Rfj+MX94p2i71MO
+hXeiD+EwR+4A5zN9RGcaC1Hoi6CeUJhoNFIfLm0B8mBF8jHrqTFoKbt6QZ7GGX+U
+tFE5A3+y3qcym7RHjm+0Sq7lr7HcsBthvJly3uSJt3omXdozSVtSnA71iq3DuD3o
+BmrC1SoLbHuEvVYFy4ZlkuxEK7COudxwC0barbxjiDn622r+I/q85Ej0ZytqERAh
+SQIDAQABo4GRMIGOMAwGA1UdEwQFMAMBAf8wTwYDVR0gBEgwRjBEBgRVHSAAMDww
+OgYIKwYBBQUHAgEWLmh0dHA6Ly93d3cucGtpb3ZlcmhlaWQubmwvcG9saWNpZXMv
+cm9vdC1wb2xpY3kwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSofeu8Y6R0E3QA
+7Jbg0zTBLL9s+DANBgkqhkiG9w0BAQUFAAOCAQEABYSHVXQ2YcG70dTGFagTtJ+k
+/rvuFbQvBgwp8qiSpGEN/KtcCFtREytNwiphyPgJWPwtArI5fZlmgb9uXJVFIGzm
+eafR2Bwp/MIgJ1HI8XxdNGdphREwxgDS1/PTfLbwMVcoEoJz6TMvplW0C5GUR5z6
+u3pCMuiufi3IvKwUv9kP2Vv8wfl6leF9fpb8cbDCTMjfRTTJzg3ynGQI0DvDKcWy
+7ZAEwbEpkcUwb8GpcjPM/l0WFywRaed+/sWDCN+83CI6LiBpIzlWYGeQiy52OfsR
+iJf2fL1LuCAWZwWN4jvBcj+UlTfHXbme2JOhF4//DGYVwSR8MnwDHTuhWEUykw==
+-----END CERTIFICATE-----
+
+# Issuer: O=TDC Internet OU=TDC Internet Root CA
+# Subject: O=TDC Internet OU=TDC Internet Root CA
+# Label: "TDC Internet Root CA"
+# Serial: 986490188
+# MD5 Fingerprint: 91:f4:03:55:20:a1:f8:63:2c:62:de:ac:fb:61:1c:8e
+# SHA1 Fingerprint: 21:fc:bd:8e:7f:6c:af:05:1b:d1:b3:43:ec:a8:e7:61:47:f2:0f:8a
+# SHA256 Fingerprint: 48:98:c6:88:8c:0c:ff:b0:d3:e3:1a:ca:8a:37:d4:e3:51:5f:f7:46:d0:26:35:d8:66:46:cf:a0:a3:18:5a:e7
+-----BEGIN CERTIFICATE-----
+MIIEKzCCAxOgAwIBAgIEOsylTDANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJE
+SzEVMBMGA1UEChMMVERDIEludGVybmV0MR0wGwYDVQQLExRUREMgSW50ZXJuZXQg
+Um9vdCBDQTAeFw0wMTA0MDUxNjMzMTdaFw0yMTA0MDUxNzAzMTdaMEMxCzAJBgNV
+BAYTAkRLMRUwEwYDVQQKEwxUREMgSW50ZXJuZXQxHTAbBgNVBAsTFFREQyBJbnRl
+cm5ldCBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxLhA
+vJHVYx/XmaCLDEAedLdInUaMArLgJF/wGROnN4NrXceO+YQwzho7+vvOi20jxsNu
+Zp+Jpd/gQlBn+h9sHvTQBda/ytZO5GhgbEaqHF1j4QeGDmUApy6mcca8uYGoOn0a
+0vnRrEvLznWv3Hv6gXPU/Lq9QYjUdLP5Xjg6PEOo0pVOd20TDJ2PeAG3WiAfAzc1
+4izbSysseLlJ28TQx5yc5IogCSEWVmb/Bexb4/DPqyQkXsN/cHoSxNK1EKC2IeGN
+eGlVRGn1ypYcNIUXJXfi9i8nmHj9eQY6otZaQ8H/7AQ77hPv01ha/5Lr7K7a8jcD
+R0G2l8ktCkEiu7vmpwIDAQABo4IBJTCCASEwEQYJYIZIAYb4QgEBBAQDAgAHMGUG
+A1UdHwReMFwwWqBYoFakVDBSMQswCQYDVQQGEwJESzEVMBMGA1UEChMMVERDIElu
+dGVybmV0MR0wGwYDVQQLExRUREMgSW50ZXJuZXQgUm9vdCBDQTENMAsGA1UEAxME
+Q1JMMTArBgNVHRAEJDAigA8yMDAxMDQwNTE2MzMxN1qBDzIwMjEwNDA1MTcwMzE3
+WjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUbGQBx/2FbazI2p5QCIUItTxWqFAw
+HQYDVR0OBBYEFGxkAcf9hW2syNqeUAiFCLU8VqhQMAwGA1UdEwQFMAMBAf8wHQYJ
+KoZIhvZ9B0EABBAwDhsIVjUuMDo0LjADAgSQMA0GCSqGSIb3DQEBBQUAA4IBAQBO
+Q8zR3R0QGwZ/t6T609lN+yOfI1Rb5osvBCiLtSdtiaHsmGnc540mgwV5dOy0uaOX
+wTUA/RXaOYE6lTGQ3pfphqiZdwzlWqCE/xIWrG64jcN7ksKsLtB9KOy282A4aW8+
+2ARVPp7MVdK6/rtHBNcK2RYKNCn1WBPVT8+PVkuzHu7TmHnaCB4Mb7j4Fifvwm89
+9qNLPg7kbWzbO0ESm70NRyN/PErQr8Cv9u8btRXE64PECV90i9kR+8JWsTz4cMo0
+jUNAE4z9mQNUecYu6oah9jrUCbz0vGbMPVjQV0kK7iXiQe4T+Zs4NNEA9X7nlB38
+aQNiuJkFBT1reBK9sG9l
+-----END CERTIFICATE-----
+
+# Issuer: CN=UTN - DATACorp SGC O=The USERTRUST Network OU=http://www.usertrust.com
+# Subject: CN=UTN - DATACorp SGC O=The USERTRUST Network OU=http://www.usertrust.com
+# Label: "UTN DATACorp SGC Root CA"
+# Serial: 91374294542884689855167577680241077609
+# MD5 Fingerprint: b3:a5:3e:77:21:6d:ac:4a:c0:c9:fb:d5:41:3d:ca:06
+# SHA1 Fingerprint: 58:11:9f:0e:12:82:87:ea:50:fd:d9:87:45:6f:4f:78:dc:fa:d6:d4
+# SHA256 Fingerprint: 85:fb:2f:91:dd:12:27:5a:01:45:b6:36:53:4f:84:02:4a:d6:8b:69:b8:ee:88:68:4f:f7:11:37:58:05:b3:48
+-----BEGIN CERTIFICATE-----
+MIIEXjCCA0agAwIBAgIQRL4Mi1AAIbQR0ypoBqmtaTANBgkqhkiG9w0BAQUFADCB
+kzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug
+Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho
+dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xGzAZBgNVBAMTElVUTiAtIERBVEFDb3Jw
+IFNHQzAeFw05OTA2MjQxODU3MjFaFw0xOTA2MjQxOTA2MzBaMIGTMQswCQYDVQQG
+EwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYD
+VQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cu
+dXNlcnRydXN0LmNvbTEbMBkGA1UEAxMSVVROIC0gREFUQUNvcnAgU0dDMIIBIjAN
+BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3+5YEKIrblXEjr8uRgnn4AgPLit6
+E5Qbvfa2gI5lBZMAHryv4g+OGQ0SR+ysraP6LnD43m77VkIVni5c7yPeIbkFdicZ
+D0/Ww5y0vpQZY/KmEQrrU0icvvIpOxboGqBMpsn0GFlowHDyUwDAXlCCpVZvNvlK
+4ESGoE1O1kduSUrLZ9emxAW5jh70/P/N5zbgnAVssjMiFdC04MwXwLLA9P4yPykq
+lXvY8qdOD1R8oQ2AswkDwf9c3V6aPryuvEeKaq5xyh+xKrhfQgUL7EYw0XILyulW
+bfXv33i+Ybqypa4ETLyorGkVl73v67SMvzX41MPRKA5cOp9wGDMgd8SirwIDAQAB
+o4GrMIGoMAsGA1UdDwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRT
+MtGzz3/64PGgXYVOktKeRR20TzA9BgNVHR8ENjA0MDKgMKAuhixodHRwOi8vY3Js
+LnVzZXJ0cnVzdC5jb20vVVROLURBVEFDb3JwU0dDLmNybDAqBgNVHSUEIzAhBggr
+BgEFBQcDAQYKKwYBBAGCNwoDAwYJYIZIAYb4QgQBMA0GCSqGSIb3DQEBBQUAA4IB
+AQAnNZcAiosovcYzMB4p/OL31ZjUQLtgyr+rFywJNn9Q+kHcrpY6CiM+iVnJowft
+Gzet/Hy+UUla3joKVAgWRcKZsYfNjGjgaQPpxE6YsjuMFrMOoAyYUJuTqXAJyCyj
+j98C5OBxOvG0I3KgqgHf35g+FFCgMSa9KOlaMCZ1+XtgHI3zzVAmbQQnmt/VDUVH
+KWss5nbZqSl9Mt3JNjy9rjXxEZ4du5A/EkdOjtd+D2JzHVImOBwYSf0wdJrE5SIv
+2MCN7ZF6TACPcn9d2t0bi0Vr591pl6jFVkwPDPafepE39peC4N1xaf92P2BNPM/3
+mfnGV/TJVTl4uix5yaaIK/QI
+-----END CERTIFICATE-----
+
+# Issuer: CN=UTN-USERFirst-Hardware O=The USERTRUST Network OU=http://www.usertrust.com
+# Subject: CN=UTN-USERFirst-Hardware O=The USERTRUST Network OU=http://www.usertrust.com
+# Label: "UTN USERFirst Hardware Root CA"
+# Serial: 91374294542884704022267039221184531197
+# MD5 Fingerprint: 4c:56:41:e5:0d:bb:2b:e8:ca:a3:ed:18:08:ad:43:39
+# SHA1 Fingerprint: 04:83:ed:33:99:ac:36:08:05:87:22:ed:bc:5e:46:00:e3:be:f9:d7
+# SHA256 Fingerprint: 6e:a5:47:41:d0:04:66:7e:ed:1b:48:16:63:4a:a3:a7:9e:6e:4b:96:95:0f:82:79:da:fc:8d:9b:d8:81:21:37
+-----BEGIN CERTIFICATE-----
+MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUFADCB
+lzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug
+Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho
+dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3Qt
+SGFyZHdhcmUwHhcNOTkwNzA5MTgxMDQyWhcNMTkwNzA5MTgxOTIyWjCBlzELMAkG
+A1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEe
+MBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8v
+d3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdh
+cmUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCx98M4P7Sof885glFn
+0G2f0v9Y8+efK+wNiVSZuTiZFvfgIXlIwrthdBKWHTxqctU8EGc6Oe0rE81m65UJ
+M6Rsl7HoxuzBdXmcRl6Nq9Bq/bkqVRcQVLMZ8Jr28bFdtqdt++BxF2uiiPsA3/4a
+MXcMmgF6sTLjKwEHOG7DpV4jvEWbe1DByTCP2+UretNb+zNAHqDVmBe8i4fDidNd
+oI6yqqr2jmmIBsX6iSHzCJ1pLgkzmykNRg+MzEk0sGlRvfkGzWitZky8PqxhvQqI
+DsjfPe58BEydCl5rkdbux+0ojatNh4lz0G6k0B4WixThdkQDf2Os5M1JnMWS9Ksy
+oUhbAgMBAAGjgbkwgbYwCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYD
+VR0OBBYEFKFyXyYbKJhDlV0HN9WFlp1L0sNFMEQGA1UdHwQ9MDswOaA3oDWGM2h0
+dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNFUkZpcnN0LUhhcmR3YXJlLmNy
+bDAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwUGCCsGAQUFBwMGBggrBgEF
+BQcDBzANBgkqhkiG9w0BAQUFAAOCAQEARxkP3nTGmZev/K0oXnWO6y1n7k57K9cM
+//bey1WiCuFMVGWTYGufEpytXoMs61quwOQt9ABjHbjAbPLPSbtNk28Gpgoiskli
+CE7/yMgUsogWXecB5BKV5UU0s4tpvc+0hY91UZ59Ojg6FEgSxvunOxqNDYJAB+gE
+CJChicsZUN/KHAG8HQQZexB2lzvukJDKxA4fFm517zP4029bHpbj4HR3dHuKom4t
+3XbWOTCC8KucUvIqx69JXn7HaOWCgchqJ/kniCrVWFCVH/A7HFe7fRQ5YiuayZSS
+KqMiDP+JJn1fIytH1xUdqWqeUQ0qUZ6B+dQ7XnASfxAynB67nfhmqA==
+-----END CERTIFICATE-----
+
+# Issuer: CN=Chambers of Commerce Root O=AC Camerfirma SA CIF A82743287 OU=http://www.chambersign.org
+# Subject: CN=Chambers of Commerce Root O=AC Camerfirma SA CIF A82743287 OU=http://www.chambersign.org
+# Label: "Camerfirma Chambers of Commerce Root"
+# Serial: 0
+# MD5 Fingerprint: b0:01:ee:14:d9:af:29:18:94:76:8e:f1:69:33:2a:84
+# SHA1 Fingerprint: 6e:3a:55:a4:19:0c:19:5c:93:84:3c:c0:db:72:2e:31:30:61:f0:b1
+# SHA256 Fingerprint: 0c:25:8a:12:a5:67:4a:ef:25:f2:8b:a7:dc:fa:ec:ee:a3:48:e5:41:e6:f5:cc:4e:e6:3b:71:b3:61:60:6a:c3
+-----BEGIN CERTIFICATE-----
+MIIEvTCCA6WgAwIBAgIBADANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJFVTEn
+MCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQL
+ExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEiMCAGA1UEAxMZQ2hhbWJlcnMg
+b2YgQ29tbWVyY2UgUm9vdDAeFw0wMzA5MzAxNjEzNDNaFw0zNzA5MzAxNjEzNDRa
+MH8xCzAJBgNVBAYTAkVVMScwJQYDVQQKEx5BQyBDYW1lcmZpcm1hIFNBIENJRiBB
+ODI3NDMyODcxIzAhBgNVBAsTGmh0dHA6Ly93d3cuY2hhbWJlcnNpZ24ub3JnMSIw
+IAYDVQQDExlDaGFtYmVycyBvZiBDb21tZXJjZSBSb290MIIBIDANBgkqhkiG9w0B
+AQEFAAOCAQ0AMIIBCAKCAQEAtzZV5aVdGDDg2olUkfzIx1L4L1DZ77F1c2VHfRtb
+unXF/KGIJPov7coISjlUxFF6tdpg6jg8gbLL8bvZkSM/SAFwdakFKq0fcfPJVD0d
+BmpAPrMMhe5cG3nCYsS4No41XQEMIwRHNaqbYE6gZj3LJgqcQKH0XZi/caulAGgq
+7YN6D6IUtdQis4CwPAxaUWktWBiP7Zme8a7ileb2R6jWDA+wWFjbw2Y3npuRVDM3
+0pQcakjJyfKl2qUMI/cjDpwyVV5xnIQFUZot/eZOKjRa3spAN2cMVCFVd9oKDMyX
+roDclDZK9D7ONhMeU+SsTjoF7Nuucpw4i9A5O4kKPnf+dQIBA6OCAUQwggFAMBIG
+A1UdEwEB/wQIMAYBAf8CAQwwPAYDVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybC5j
+aGFtYmVyc2lnbi5vcmcvY2hhbWJlcnNyb290LmNybDAdBgNVHQ4EFgQU45T1sU3p
+26EpW1eLTXYGduHRooowDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIA
+BzAnBgNVHREEIDAegRxjaGFtYmVyc3Jvb3RAY2hhbWJlcnNpZ24ub3JnMCcGA1Ud
+EgQgMB6BHGNoYW1iZXJzcm9vdEBjaGFtYmVyc2lnbi5vcmcwWAYDVR0gBFEwTzBN
+BgsrBgEEAYGHLgoDATA+MDwGCCsGAQUFBwIBFjBodHRwOi8vY3BzLmNoYW1iZXJz
+aWduLm9yZy9jcHMvY2hhbWJlcnNyb290Lmh0bWwwDQYJKoZIhvcNAQEFBQADggEB
+AAxBl8IahsAifJ/7kPMa0QOx7xP5IV8EnNrJpY0nbJaHkb5BkAFyk+cefV/2icZd
+p0AJPaxJRUXcLo0waLIJuvvDL8y6C98/d3tGfToSJI6WjzwFCm/SlCgdbQzALogi
+1djPHRPH8EjX1wWnz8dHnjs8NMiAT9QUu/wNUPf6s+xCX6ndbcj0dc97wXImsQEc
+XCz9ek60AcUFV7nnPKoF2YjpB0ZBzu9Bga5Y34OirsrXdx/nADydb47kMgkdTXg0
+eDQ8lJsm7U9xxhl6vSAiSFr+S30Dt+dYvsYyTnQeaN2oaFuzPu5ifdmA6Ap1erfu
+tGWaIZDgqtCYvDi1czyL+Nw=
+-----END CERTIFICATE-----
+
+# Issuer: CN=Global Chambersign Root O=AC Camerfirma SA CIF A82743287 OU=http://www.chambersign.org
+# Subject: CN=Global Chambersign Root O=AC Camerfirma SA CIF A82743287 OU=http://www.chambersign.org
+# Label: "Camerfirma Global Chambersign Root"
+# Serial: 0
+# MD5 Fingerprint: c5:e6:7b:bf:06:d0:4f:43:ed:c4:7a:65:8a:fb:6b:19
+# SHA1 Fingerprint: 33:9b:6b:14:50:24:9b:55:7a:01:87:72:84:d9:e0:2f:c3:d2:d8:e9
+# SHA256 Fingerprint: ef:3c:b4:17:fc:8e:bf:6f:97:87:6c:9e:4e:ce:39:de:1e:a5:fe:64:91:41:d1:02:8b:7d:11:c0:b2:29:8c:ed
+-----BEGIN CERTIFICATE-----
+MIIExTCCA62gAwIBAgIBADANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJFVTEn
+MCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQL
+ExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEgMB4GA1UEAxMXR2xvYmFsIENo
+YW1iZXJzaWduIFJvb3QwHhcNMDMwOTMwMTYxNDE4WhcNMzcwOTMwMTYxNDE4WjB9
+MQswCQYDVQQGEwJFVTEnMCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgy
+NzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEgMB4G
+A1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwggEgMA0GCSqGSIb3DQEBAQUA
+A4IBDQAwggEIAoIBAQCicKLQn0KuWxfH2H3PFIP8T8mhtxOviteePgQKkotgVvq0
+Mi+ITaFgCPS3CU6gSS9J1tPfnZdan5QEcOw/Wdm3zGaLmFIoCQLfxS+EjXqXd7/s
+QJ0lcqu1PzKY+7e3/HKE5TWH+VX6ox8Oby4o3Wmg2UIQxvi1RMLQQ3/bvOSiPGpV
+eAp3qdjqGTK3L/5cPxvusZjsyq16aUXjlg9V9ubtdepl6DJWk0aJqCWKZQbua795
+B9Dxt6/tLE2Su8CoX6dnfQTyFQhwrJLWfQTSM/tMtgsL+xrJxI0DqX5c8lCrEqWh
+z0hQpe/SyBoT+rB/sYIcd2oPX9wLlY/vQ37mRQklAgEDo4IBUDCCAUwwEgYDVR0T
+AQH/BAgwBgEB/wIBDDA/BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vY3JsLmNoYW1i
+ZXJzaWduLm9yZy9jaGFtYmVyc2lnbnJvb3QuY3JsMB0GA1UdDgQWBBRDnDafsJ4w
+TcbOX60Qq+UDpfqpFDAOBgNVHQ8BAf8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAgAH
+MCoGA1UdEQQjMCGBH2NoYW1iZXJzaWducm9vdEBjaGFtYmVyc2lnbi5vcmcwKgYD
+VR0SBCMwIYEfY2hhbWJlcnNpZ25yb290QGNoYW1iZXJzaWduLm9yZzBbBgNVHSAE
+VDBSMFAGCysGAQQBgYcuCgEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly9jcHMuY2hh
+bWJlcnNpZ24ub3JnL2Nwcy9jaGFtYmVyc2lnbnJvb3QuaHRtbDANBgkqhkiG9w0B
+AQUFAAOCAQEAPDtwkfkEVCeR4e3t/mh/YV3lQWVPMvEYBZRqHN4fcNs+ezICNLUM
+bKGKfKX0j//U2K0X1S0E0T9YgOKBWYi+wONGkyT+kL0mojAt6JcmVzWJdJYY9hXi
+ryQZVgICsroPFOrGimbBhkVVi76SvpykBMdJPJ7oKXqJ1/6v/2j1pReQvayZzKWG
+VwlnRtvWFsJG8eSpUPWP0ZIV018+xgBJOm5YstHRJw0lyDL4IBHNfTIzSJRUTN3c
+ecQwn+uOuFW114hcxWokPbLTBQNRxgfvzBRydD1ucs4YKIxKoHflCStFREest2d/
+AYoFWpO+ocH/+OcOZ6RHSXZddZAa9SaP8A==
+-----END CERTIFICATE-----
+
+# Issuer: CN=NetLock Kozjegyzoi (Class A) Tanusitvanykiado O=NetLock Halozatbiztonsagi Kft. OU=Tanusitvanykiadok
+# Subject: CN=NetLock Kozjegyzoi (Class A) Tanusitvanykiado O=NetLock Halozatbiztonsagi Kft. OU=Tanusitvanykiadok
+# Label: "NetLock Notary (Class A) Root"
+# Serial: 259
+# MD5 Fingerprint: 86:38:6d:5e:49:63:6c:85:5c:db:6d:dc:94:b7:d0:f7
+# SHA1 Fingerprint: ac:ed:5f:65:53:fd:25:ce:01:5f:1f:7a:48:3b:6a:74:9f:61:78:c6
+# SHA256 Fingerprint: 7f:12:cd:5f:7e:5e:29:0e:c7:d8:51:79:d5:b7:2c:20:a5:be:75:08:ff:db:5b:f8:1a:b9:68:4a:7f:c9:f6:67
+-----BEGIN CERTIFICATE-----
+MIIGfTCCBWWgAwIBAgICAQMwDQYJKoZIhvcNAQEEBQAwga8xCzAJBgNVBAYTAkhV
+MRAwDgYDVQQIEwdIdW5nYXJ5MREwDwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMe
+TmV0TG9jayBIYWxvemF0Yml6dG9uc2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0
+dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9jayBLb3pqZWd5em9pIChDbGFzcyBB
+KSBUYW51c2l0dmFueWtpYWRvMB4XDTk5MDIyNDIzMTQ0N1oXDTE5MDIxOTIzMTQ0
+N1owga8xCzAJBgNVBAYTAkhVMRAwDgYDVQQIEwdIdW5nYXJ5MREwDwYDVQQHEwhC
+dWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6dG9uc2FnaSBLZnQu
+MRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9jayBL
+b3pqZWd5em9pIChDbGFzcyBBKSBUYW51c2l0dmFueWtpYWRvMIIBIjANBgkqhkiG
+9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvHSMD7tM9DceqQWC2ObhbHDqeLVu0ThEDaiD
+zl3S1tWBxdRL51uUcCbbO51qTGL3cfNk1mE7PetzozfZz+qMkjvN9wfcZnSX9EUi
+3fRc4L9t875lM+QVOr/bmJBVOMTtplVjC7B4BPTjbsE/jvxReB+SnoPC/tmwqcm8
+WgD/qaiYdPv2LD4VOQ22BFWoDpggQrOxJa1+mm9dU7GrDPzr4PN6s6iz/0b2Y6LY
+Oph7tqyF/7AlT3Rj5xMHpQqPBffAZG9+pyeAlt7ULoZgx2srXnN7F+eRP2QM2Esi
+NCubMvJIH5+hCoR64sKtlz2O1cH5VqNQ6ca0+pii7pXmKgOM3wIDAQABo4ICnzCC
+ApswDgYDVR0PAQH/BAQDAgAGMBIGA1UdEwEB/wQIMAYBAf8CAQQwEQYJYIZIAYb4
+QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaCAk1GSUdZRUxFTSEgRXplbiB0
+YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFub3MgU3pvbGdhbHRhdGFz
+aSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBhbGFwamFuIGtlc3p1bHQu
+IEEgaGl0ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExvY2sgS2Z0LiB0ZXJtZWtm
+ZWxlbG9zc2VnLWJpenRvc2l0YXNhIHZlZGkuIEEgZGlnaXRhbGlzIGFsYWlyYXMg
+ZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUgYXogZWxvaXJ0IGVsbGVub3J6ZXNpIGVs
+amFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFzIGxlaXJhc2EgbWVndGFsYWxoYXRv
+IGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGphbiBhIGh0dHBzOi8vd3d3
+Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJoZXRvIGF6IGVsbGVub3J6
+ZXNAbmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBPUlRBTlQhIFRoZSBpc3N1
+YW5jZSBhbmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGlzIHN1YmplY3Qg
+dG8gdGhlIE5ldExvY2sgQ1BTIGF2YWlsYWJsZSBhdCBodHRwczovL3d3dy5uZXRs
+b2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFpbCBhdCBjcHNAbmV0bG9jay5uZXQuMA0G
+CSqGSIb3DQEBBAUAA4IBAQBIJEb3ulZv+sgoA0BO5TE5ayZrU3/b39/zcT0mwBQO
+xmd7I6gMc90Bu8bKbjc5VdXHjFYgDigKDtIqpLBJUsY4B/6+CgmM0ZjPytoUMaFP
+0jn8DxEsQ8Pdq5PHVT5HfBgaANzze9jyf1JsIPQLX2lS9O74silg6+NJMSEN1rUQ
+QeJBCWziGppWS3cC9qCbmieH6FUpccKQn0V4GuEVZD3QDtigdp+uxdAu6tYPVuxk
+f1qbFFgBJ34TUMdrKuZoPL9coAob4Q566eKAw+np9v1sEZ7Q5SgnK1QyQhSCdeZK
+8CtmdWOMovsEPoMOmzbwGOQmIMOM8CgHrTwXZoi1/baI
+-----END CERTIFICATE-----
+
+# Issuer: CN=NetLock Uzleti (Class B) Tanusitvanykiado O=NetLock Halozatbiztonsagi Kft. OU=Tanusitvanykiadok
+# Subject: CN=NetLock Uzleti (Class B) Tanusitvanykiado O=NetLock Halozatbiztonsagi Kft. OU=Tanusitvanykiadok
+# Label: "NetLock Business (Class B) Root"
+# Serial: 105
+# MD5 Fingerprint: 39:16:aa:b9:6a:41:e1:14:69:df:9e:6c:3b:72:dc:b6
+# SHA1 Fingerprint: 87:9f:4b:ee:05:df:98:58:3b:e3:60:d6:33:e7:0d:3f:fe:98:71:af
+# SHA256 Fingerprint: 39:df:7b:68:2b:7b:93:8f:84:71:54:81:cc:de:8d:60:d8:f2:2e:c5:98:87:7d:0a:aa:c1:2b:59:18:2b:03:12
+-----BEGIN CERTIFICATE-----
+MIIFSzCCBLSgAwIBAgIBaTANBgkqhkiG9w0BAQQFADCBmTELMAkGA1UEBhMCSFUx
+ETAPBgNVBAcTCEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0
+b25zYWdpIEtmdC4xGjAYBgNVBAsTEVRhbnVzaXR2YW55a2lhZG9rMTIwMAYDVQQD
+EylOZXRMb2NrIFV6bGV0aSAoQ2xhc3MgQikgVGFudXNpdHZhbnlraWFkbzAeFw05
+OTAyMjUxNDEwMjJaFw0xOTAyMjAxNDEwMjJaMIGZMQswCQYDVQQGEwJIVTERMA8G
+A1UEBxMIQnVkYXBlc3QxJzAlBgNVBAoTHk5ldExvY2sgSGFsb3phdGJpenRvbnNh
+Z2kgS2Z0LjEaMBgGA1UECxMRVGFudXNpdHZhbnlraWFkb2sxMjAwBgNVBAMTKU5l
+dExvY2sgVXpsZXRpIChDbGFzcyBCKSBUYW51c2l0dmFueWtpYWRvMIGfMA0GCSqG
+SIb3DQEBAQUAA4GNADCBiQKBgQCx6gTsIKAjwo84YM/HRrPVG/77uZmeBNwcf4xK
+gZjupNTKihe5In+DCnVMm8Bp2GQ5o+2So/1bXHQawEfKOml2mrriRBf8TKPV/riX
+iK+IA4kfpPIEPsgHC+b5sy96YhQJRhTKZPWLgLViqNhr1nGTLbO/CVRY7QbrqHvc
+Q7GhaQIDAQABo4ICnzCCApswEgYDVR0TAQH/BAgwBgEB/wIBBDAOBgNVHQ8BAf8E
+BAMCAAYwEQYJYIZIAYb4QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaCAk1G
+SUdZRUxFTSEgRXplbiB0YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFu
+b3MgU3pvbGdhbHRhdGFzaSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBh
+bGFwamFuIGtlc3p1bHQuIEEgaGl0ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExv
+Y2sgS2Z0LiB0ZXJtZWtmZWxlbG9zc2VnLWJpenRvc2l0YXNhIHZlZGkuIEEgZGln
+aXRhbGlzIGFsYWlyYXMgZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUgYXogZWxvaXJ0
+IGVsbGVub3J6ZXNpIGVsamFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFzIGxlaXJh
+c2EgbWVndGFsYWxoYXRvIGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGph
+biBhIGh0dHBzOi8vd3d3Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJo
+ZXRvIGF6IGVsbGVub3J6ZXNAbmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBP
+UlRBTlQhIFRoZSBpc3N1YW5jZSBhbmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmlj
+YXRlIGlzIHN1YmplY3QgdG8gdGhlIE5ldExvY2sgQ1BTIGF2YWlsYWJsZSBhdCBo
+dHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFpbCBhdCBjcHNA
+bmV0bG9jay5uZXQuMA0GCSqGSIb3DQEBBAUAA4GBAATbrowXr/gOkDFOzT4JwG06
+sPgzTEdM43WIEJessDgVkcYplswhwG08pXTP2IKlOcNl40JwuyKQ433bNXbhoLXa
+n3BukxowOR0w2y7jfLKRstE3Kfq51hdcR0/jHTjrn9V7lagonhVK0dHQKwCXoOKS
+NitjrFgBazMpUIaD8QFI
+-----END CERTIFICATE-----
+
+# Issuer: CN=NetLock Expressz (Class C) Tanusitvanykiado O=NetLock Halozatbiztonsagi Kft. OU=Tanusitvanykiadok
+# Subject: CN=NetLock Expressz (Class C) Tanusitvanykiado O=NetLock Halozatbiztonsagi Kft. OU=Tanusitvanykiadok
+# Label: "NetLock Express (Class C) Root"
+# Serial: 104
+# MD5 Fingerprint: 4f:eb:f1:f0:70:c2:80:63:5d:58:9f:da:12:3c:a9:c4
+# SHA1 Fingerprint: e3:92:51:2f:0a:cf:f5:05:df:f6:de:06:7f:75:37:e1:65:ea:57:4b
+# SHA256 Fingerprint: 0b:5e:ed:4e:84:64:03:cf:55:e0:65:84:84:40:ed:2a:82:75:8b:f5:b9:aa:1f:25:3d:46:13:cf:a0:80:ff:3f
+-----BEGIN CERTIFICATE-----
+MIIFTzCCBLigAwIBAgIBaDANBgkqhkiG9w0BAQQFADCBmzELMAkGA1UEBhMCSFUx
+ETAPBgNVBAcTCEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0
+b25zYWdpIEtmdC4xGjAYBgNVBAsTEVRhbnVzaXR2YW55a2lhZG9rMTQwMgYDVQQD
+EytOZXRMb2NrIEV4cHJlc3N6IChDbGFzcyBDKSBUYW51c2l0dmFueWtpYWRvMB4X
+DTk5MDIyNTE0MDgxMVoXDTE5MDIyMDE0MDgxMVowgZsxCzAJBgNVBAYTAkhVMREw
+DwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6dG9u
+c2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE0MDIGA1UEAxMr
+TmV0TG9jayBFeHByZXNzeiAoQ2xhc3MgQykgVGFudXNpdHZhbnlraWFkbzCBnzAN
+BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA6+ywbGGKIyWvYCDj2Z/8kwvbXY2wobNA
+OoLO/XXgeDIDhlqGlZHtU/qdQPzm6N3ZW3oDvV3zOwzDUXmbrVWg6dADEK8KuhRC
+2VImESLH0iDMgqSaqf64gXadarfSNnU+sYYJ9m5tfk63euyucYT2BDMIJTLrdKwW
+RMbkQJMdf60CAwEAAaOCAp8wggKbMBIGA1UdEwEB/wQIMAYBAf8CAQQwDgYDVR0P
+AQH/BAQDAgAGMBEGCWCGSAGG+EIBAQQEAwIABzCCAmAGCWCGSAGG+EIBDQSCAlEW
+ggJNRklHWUVMRU0hIEV6ZW4gdGFudXNpdHZhbnkgYSBOZXRMb2NrIEtmdC4gQWx0
+YWxhbm9zIFN6b2xnYWx0YXRhc2kgRmVsdGV0ZWxlaWJlbiBsZWlydCBlbGphcmFz
+b2sgYWxhcGphbiBrZXN6dWx0LiBBIGhpdGVsZXNpdGVzIGZvbHlhbWF0YXQgYSBO
+ZXRMb2NrIEtmdC4gdGVybWVrZmVsZWxvc3NlZy1iaXp0b3NpdGFzYSB2ZWRpLiBB
+IGRpZ2l0YWxpcyBhbGFpcmFzIGVsZm9nYWRhc2FuYWsgZmVsdGV0ZWxlIGF6IGVs
+b2lydCBlbGxlbm9yemVzaSBlbGphcmFzIG1lZ3RldGVsZS4gQXogZWxqYXJhcyBs
+ZWlyYXNhIG1lZ3RhbGFsaGF0byBhIE5ldExvY2sgS2Z0LiBJbnRlcm5ldCBob25s
+YXBqYW4gYSBodHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIGNpbWVuIHZhZ3kg
+a2VyaGV0byBheiBlbGxlbm9yemVzQG5ldGxvY2submV0IGUtbWFpbCBjaW1lbi4g
+SU1QT1JUQU5UISBUaGUgaXNzdWFuY2UgYW5kIHRoZSB1c2Ugb2YgdGhpcyBjZXJ0
+aWZpY2F0ZSBpcyBzdWJqZWN0IHRvIHRoZSBOZXRMb2NrIENQUyBhdmFpbGFibGUg
+YXQgaHR0cHM6Ly93d3cubmV0bG9jay5uZXQvZG9jcyBvciBieSBlLW1haWwgYXQg
+Y3BzQG5ldGxvY2submV0LjANBgkqhkiG9w0BAQQFAAOBgQAQrX/XDDKACtiG8XmY
+ta3UzbM2xJZIwVzNmtkFLp++UOv0JhQQLdRmF/iewSf98e3ke0ugbLWrmldwpu2g
+pO0u9f38vf5NNwgMvOOWgyL1SRt/Syu0VMGAfJlOHdCM7tCs5ZL6dVb+ZKATj7i4
+Fp1hBWeAyNDYpQcCNJgEjTME1A==
+-----END CERTIFICATE-----
+
+# Issuer: CN=XRamp Global Certification Authority O=XRamp Security Services Inc OU=www.xrampsecurity.com
+# Subject: CN=XRamp Global Certification Authority O=XRamp Security Services Inc OU=www.xrampsecurity.com
+# Label: "XRamp Global CA Root"
+# Serial: 107108908803651509692980124233745014957
+# MD5 Fingerprint: a1:0b:44:b3:ca:10:d8:00:6e:9d:0f:d8:0f:92:0a:d1
+# SHA1 Fingerprint: b8:01:86:d1:eb:9c:86:a5:41:04:cf:30:54:f3:4c:52:b7:e5:58:c6
+# SHA256 Fingerprint: ce:cd:dc:90:50:99:d8:da:df:c5:b1:d2:09:b7:37:cb:e2:c1:8c:fb:2c:10:c0:ff:0b:cf:0d:32:86:fc:1a:a2
+-----BEGIN CERTIFICATE-----
+MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCB
+gjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEk
+MCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRY
+UmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQxMTAxMTcx
+NDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3
+dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2Vy
+dmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB
+dXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS6
+38eMpSe2OAtp87ZOqCwuIR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCP
+KZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMxfoArtYzAQDsRhtDLooY2YKTVMIJt2W7Q
+DxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FEzG+gSqmUsE3a56k0enI4
+qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqsAxcZZPRa
+JSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNVi
+PvryxS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0P
+BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASs
+jVy16bYbMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0
+eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQEwDQYJKoZIhvcNAQEFBQAD
+ggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc/Kh4ZzXxHfAR
+vbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt
+qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLa
+IR9NmXmd4c8nnxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSy
+i6mx5O+aGtA9aZnuqCij4Tyz8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQ
+O+7ETPTsJ3xCwnR8gooJybQDJbw=
+-----END CERTIFICATE-----
+
+# Issuer: O=The Go Daddy Group, Inc. OU=Go Daddy Class 2 Certification Authority
+# Subject: O=The Go Daddy Group, Inc. OU=Go Daddy Class 2 Certification Authority
+# Label: "Go Daddy Class 2 CA"
+# Serial: 0
+# MD5 Fingerprint: 91:de:06:25:ab:da:fd:32:17:0c:bb:25:17:2a:84:67
+# SHA1 Fingerprint: 27:96:ba:e6:3f:18:01:e2:77:26:1b:a0:d7:77:70:02:8f:20:ee:e4
+# SHA256 Fingerprint: c3:84:6b:f2:4b:9e:93:ca:64:27:4c:0e:c6:7c:1e:cc:5e:02:4f:fc:ac:d2:d7:40:19:35:0e:81:fe:54:6a:e4
+-----BEGIN CERTIFICATE-----
+MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEh
+MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE
+YWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3
+MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRo
+ZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3Mg
+MiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggEN
+ADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCA
+PVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6w
+wdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXi
+EqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMY
+avx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+
+YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0OBBYEFNLE
+sNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h
+/t2oatTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5
+IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmlj
+YXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD
+ggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wimPQoZ+YeAEW5p5JYXMP80kWNy
+OO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKtI3lpjbi2Tc7P
+TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ
+HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mER
+dEr/VxqHD3VILs9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5Cuf
+ReYNnyicsbkqWletNw+vHX/bvZ8=
+-----END CERTIFICATE-----
+
+# Issuer: O=Starfield Technologies, Inc. OU=Starfield Class 2 Certification Authority
+# Subject: O=Starfield Technologies, Inc. OU=Starfield Class 2 Certification Authority
+# Label: "Starfield Class 2 CA"
+# Serial: 0
+# MD5 Fingerprint: 32:4a:4b:bb:c8:63:69:9b:be:74:9a:c6:dd:1d:46:24
+# SHA1 Fingerprint: ad:7e:1c:28:b0:64:ef:8f:60:03:40:20:14:c3:d0:e3:37:0e:b5:8a
+# SHA256 Fingerprint: 14:65:fa:20:53:97:b8:76:fa:a6:f0:a9:95:8e:55:90:e4:0f:cc:7f:aa:4f:b7:c2:c8:67:75:21:fb:5f:b6:58
+-----BEGIN CERTIFICATE-----
+MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl
+MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp
+U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw
+NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE
+ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp
+ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3
+DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf
+8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN
++lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0
+X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa
+K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA
+1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G
+A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR
+zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0
+YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD
+bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w
+DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3
+L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D
+eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl
+xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp
+VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY
+WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q=
+-----END CERTIFICATE-----
+
+# Issuer: CN=StartCom Certification Authority O=StartCom Ltd. OU=Secure Digital Certificate Signing
+# Subject: CN=StartCom Certification Authority O=StartCom Ltd. OU=Secure Digital Certificate Signing
+# Label: "StartCom Certification Authority"
+# Serial: 1
+# MD5 Fingerprint: 22:4d:8f:8a:fc:f7:35:c2:bb:57:34:90:7b:8b:22:16
+# SHA1 Fingerprint: 3e:2b:f7:f2:03:1b:96:f3:8c:e6:c4:d8:a8:5d:3e:2d:58:47:6a:0f
+# SHA256 Fingerprint: c7:66:a9:be:f2:d4:07:1c:86:3a:31:aa:49:20:e8:13:b2:d1:98:60:8c:b7:b7:cf:e2:11:43:b8:36:df:09:ea
+-----BEGIN CERTIFICATE-----
+MIIHyTCCBbGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEW
+MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg
+Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0NjM2WhcNMzYwOTE3MTk0NjM2WjB9
+MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi
+U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh
+cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA
+A4ICDwAwggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZk
+pMyONvg45iPwbm2xPN1yo4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rf
+OQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/C
+Ji/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/deMotHweXMAEtcnn6RtYT
+Kqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt2PZE4XNi
+HzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMM
+Av+Z6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w
++2OqqGwaVLRcJXrJosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+
+Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3
+Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVcUjyJthkqcwEKDwOzEmDyei+B
+26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT37uMdBNSSwID
+AQABo4ICUjCCAk4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAa4wHQYDVR0OBBYE
+FE4L7xqkQFulF2mHMMo0aEPQQa7yMGQGA1UdHwRdMFswLKAqoCiGJmh0dHA6Ly9j
+ZXJ0LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMCugKaAnhiVodHRwOi8vY3Js
+LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMIIBXQYDVR0gBIIBVDCCAVAwggFM
+BgsrBgEEAYG1NwEBATCCATswLwYIKwYBBQUHAgEWI2h0dHA6Ly9jZXJ0LnN0YXJ0
+Y29tLm9yZy9wb2xpY3kucGRmMDUGCCsGAQUFBwIBFilodHRwOi8vY2VydC5zdGFy
+dGNvbS5vcmcvaW50ZXJtZWRpYXRlLnBkZjCB0AYIKwYBBQUHAgIwgcMwJxYgU3Rh
+cnQgQ29tbWVyY2lhbCAoU3RhcnRDb20pIEx0ZC4wAwIBARqBl0xpbWl0ZWQgTGlh
+YmlsaXR5LCByZWFkIHRoZSBzZWN0aW9uICpMZWdhbCBMaW1pdGF0aW9ucyogb2Yg
+dGhlIFN0YXJ0Q29tIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFBvbGljeSBhdmFp
+bGFibGUgYXQgaHR0cDovL2NlcnQuc3RhcnRjb20ub3JnL3BvbGljeS5wZGYwEQYJ
+YIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilTdGFydENvbSBGcmVlIFNT
+TCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQUFAAOCAgEAFmyZ
+9GYMNPXQhV59CuzaEE44HF7fpiUFS5Eyweg78T3dRAlbB0mKKctmArexmvclmAk8
+jhvh3TaHK0u7aNM5Zj2gJsfyOZEdUauCe37Vzlrk4gNXcGmXCPleWKYK34wGmkUW
+FjgKXlf2Ysd6AgXmvB618p70qSmD+LIU424oh0TDkBreOKk8rENNZEXO3SipXPJz
+ewT4F+irsfMuXGRuczE6Eri8sxHkfY+BUZo7jYn0TZNmezwD7dOaHZrzZVD1oNB1
+ny+v8OqCQ5j4aZyJecRDjkZy42Q2Eq/3JR44iZB3fsNrarnDy0RLrHiQi+fHLB5L
+EUTINFInzQpdn4XBidUaePKVEFMy3YCEZnXZtWgo+2EuvoSoOMCZEoalHmdkrQYu
+L6lwhceWD3yJZfWOQ1QOq92lgDmUYMA0yZZwLKMS9R9Ie70cfmu3nZD0Ijuu+Pwq
+yvqCUqDvr0tVk+vBtfAii6w0TiYiBKGHLHVKt+V9E9e4DGTANtLJL4YSjCMJwRuC
+O3NJo2pXh5Tl1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6V
+um0ABj6y6koQOdjQK/W/7HW/lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkySh
+NOsF/5oirpt9P/FlUQqmMGqz9IgcgA38corog14=
+-----END CERTIFICATE-----
+
+# Issuer: O=Government Root Certification Authority
+# Subject: O=Government Root Certification Authority
+# Label: "Taiwan GRCA"
+# Serial: 42023070807708724159991140556527066870
+# MD5 Fingerprint: 37:85:44:53:32:45:1f:20:f0:f3:95:e1:25:c4:43:4e
+# SHA1 Fingerprint: f4:8b:11:bf:de:ab:be:94:54:20:71:e6:41:de:6b:be:88:2b:40:b9
+# SHA256 Fingerprint: 76:00:29:5e:ef:e8:5b:9e:1f:d6:24:db:76:06:2a:aa:ae:59:81:8a:54:d2:77:4c:d4:c0:b2:c0:11:31:e1:b3
+-----BEGIN CERTIFICATE-----
+MIIFcjCCA1qgAwIBAgIQH51ZWtcvwgZEpYAIaeNe9jANBgkqhkiG9w0BAQUFADA/
+MQswCQYDVQQGEwJUVzEwMC4GA1UECgwnR292ZXJubWVudCBSb290IENlcnRpZmlj
+YXRpb24gQXV0aG9yaXR5MB4XDTAyMTIwNTEzMjMzM1oXDTMyMTIwNTEzMjMzM1ow
+PzELMAkGA1UEBhMCVFcxMDAuBgNVBAoMJ0dvdmVybm1lbnQgUm9vdCBDZXJ0aWZp
+Y2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
+AJoluOzMonWoe/fOW1mKydGGEghU7Jzy50b2iPN86aXfTEc2pBsBHH8eV4qNw8XR
+IePaJD9IK/ufLqGU5ywck9G/GwGHU5nOp/UKIXZ3/6m3xnOUT0b3EEk3+qhZSV1q
+gQdW8or5BtD3cCJNtLdBuTK4sfCxw5w/cP1T3YGq2GN49thTbqGsaoQkclSGxtKy
+yhwOeYHWtXBiCAEuTk8O1RGvqa/lmr/czIdtJuTJV6L7lvnM4T9TjGxMfptTCAts
+F/tnyMKtsc2AtJfcdgEWFelq16TheEfOhtX7MfP6Mb40qij7cEwdScevLJ1tZqa2
+jWR+tSBqnTuBto9AAGdLiYa4zGX+FVPpBMHWXx1E1wovJ5pGfaENda1UhhXcSTvx
+ls4Pm6Dso3pdvtUqdULle96ltqqvKKyskKw4t9VoNSZ63Pc78/1Fm9G7Q3hub/FC
+VGqY8A2tl+lSXunVanLeavcbYBT0peS2cWeqH+riTcFCQP5nRhc4L0c/cZyu5SHK
+YS1tB6iEfC3uUSXxY5Ce/eFXiGvviiNtsea9P63RPZYLhY3Naye7twWb7LuRqQoH
+EgKXTiCQ8P8NHuJBO9NAOueNXdpm5AKwB1KYXA6OM5zCppX7VRluTI6uSw+9wThN
+Xo+EHWbNxWCWtFJaBYmOlXqYwZE8lSOyDvR5tMl8wUohAgMBAAGjajBoMB0GA1Ud
+DgQWBBTMzO/MKWCkO7GStjz6MmKPrCUVOzAMBgNVHRMEBTADAQH/MDkGBGcqBwAE
+MTAvMC0CAQAwCQYFKw4DAhoFADAHBgVnKgMAAAQUA5vwIhP/lSg209yewDL7MTqK
+UWUwDQYJKoZIhvcNAQEFBQADggIBAECASvomyc5eMN1PhnR2WPWus4MzeKR6dBcZ
+TulStbngCnRiqmjKeKBMmo4sIy7VahIkv9Ro04rQ2JyftB8M3jh+Vzj8jeJPXgyf
+qzvS/3WXy6TjZwj/5cAWtUgBfen5Cv8b5Wppv3ghqMKnI6mGq3ZW6A4M9hPdKmaK
+ZEk9GhiHkASfQlK3T8v+R0F2Ne//AHY2RTKbxkaFXeIksB7jSJaYV0eUVXoPQbFE
+JPPB/hprv4j9wabak2BegUqZIJxIZhm1AHlUD7gsL0u8qV1bYH+Mh6XgUmMqvtg7
+hUAV/h62ZT/FS9p+tXo1KaMuephgIqP0fSdOLeq0dDzpD6QzDxARvBMB1uUO07+1
+EqLhRSPAzAhuYbeJq4PjJB7mXQfnHyA+z2fI56wwbSdLaG5LKlwCCDTb+HbkZ6Mm
+nD+iMsJKxYEYMRBWqoTvLQr/uB930r+lWKBi5NdLkXWNiYCYfm3LU05er/ayl4WX
+udpVBrkk7tfGOB5jGxI7leFYrPLfhNVfmS8NVVvmONsuP3LpSIXLuykTjx44Vbnz
+ssQwmSNOXfJIoRIM3BKQCZBUkQM8R+XVyWXgt0t97EfTsws+rZ7QdAAO671RrcDe
+LMDDav7v3Aun+kbfYNucpllQdSNpc5Oy+fwC00fmcc4QAu4njIT/rEUNE1yDMuAl
+pYYsfPQS
+-----END CERTIFICATE-----
+
+# Issuer: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068
+# Subject: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068
+# Label: "Firmaprofesional Root CA"
+# Serial: 1
+# MD5 Fingerprint: 11:92:79:40:3c:b1:83:40:e5:ab:66:4a:67:92:80:df
+# SHA1 Fingerprint: a9:62:8f:4b:98:a9:1b:48:35:ba:d2:c1:46:32:86:bb:66:64:6a:8c
+# SHA256 Fingerprint: c1:cf:0b:52:09:64:35:e3:f1:b7:1d:aa:ec:45:5a:23:11:c8:40:4f:55:83:a9:e2:13:c6:9d:85:7d:94:33:05
+-----BEGIN CERTIFICATE-----
+MIIEVzCCAz+gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBnTELMAkGA1UEBhMCRVMx
+IjAgBgNVBAcTGUMvIE11bnRhbmVyIDI0NCBCYXJjZWxvbmExQjBABgNVBAMTOUF1
+dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2
+MjYzNDA2ODEmMCQGCSqGSIb3DQEJARYXY2FAZmlybWFwcm9mZXNpb25hbC5jb20w
+HhcNMDExMDI0MjIwMDAwWhcNMTMxMDI0MjIwMDAwWjCBnTELMAkGA1UEBhMCRVMx
+IjAgBgNVBAcTGUMvIE11bnRhbmVyIDI0NCBCYXJjZWxvbmExQjBABgNVBAMTOUF1
+dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2
+MjYzNDA2ODEmMCQGCSqGSIb3DQEJARYXY2FAZmlybWFwcm9mZXNpb25hbC5jb20w
+ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDnIwNvbyOlXnjOlSztlB5u
+Cp4Bx+ow0Syd3Tfom5h5VtP8c9/Qit5Vj1H5WuretXDE7aTt/6MNbg9kUDGvASdY
+rv5sp0ovFy3Tc9UTHI9ZpTQsHVQERc1ouKDAA6XPhUJHlShbz++AbOCQl4oBPB3z
+hxAwJkh91/zpnZFx/0GaqUC1N5wpIE8fUuOgfRNtVLcK3ulqTgesrBlf3H5idPay
+BQC6haD9HThuy1q7hryUZzM1gywfI834yJFxzJeL764P3CkDG8A563DtwW4O2GcL
+iam8NeTvtjS0pbbELaW+0MOUJEjb35bTALVmGotmBQ/dPz/LP6pemkr4tErvlTcb
+AgMBAAGjgZ8wgZwwKgYDVR0RBCMwIYYfaHR0cDovL3d3dy5maXJtYXByb2Zlc2lv
+bmFsLmNvbTASBgNVHRMBAf8ECDAGAQH/AgEBMCsGA1UdEAQkMCKADzIwMDExMDI0
+MjIwMDAwWoEPMjAxMzEwMjQyMjAwMDBaMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4E
+FgQUMwugZtHq2s7eYpMEKFK1FH84aLcwDQYJKoZIhvcNAQEFBQADggEBAEdz/o0n
+VPD11HecJ3lXV7cVVuzH2Fi3AQL0M+2TUIiefEaxvT8Ub/GzR0iLjJcG1+p+o1wq
+u00vR+L4OQbJnC4xGgN49Lw4xiKLMzHwFgQEffl25EvXwOaD7FnMP97/T2u3Z36m
+hoEyIwOdyPdfwUpgpZKpsaSgYMN4h7Mi8yrrW6ntBas3D7Hi05V2Y1Z0jFhyGzfl
+ZKG+TQyTmAyX9odtsz/ny4Cm7YjHX1BiAuiZdBbQ5rQ58SfLyEDW44YQqSMSkuBp
+QWOnryULwMWSyx6Yo1q6xTMPoJcB3X/ge9YGVM+h4k0460tQtcsm9MracEpqoeJ5
+quGnM/b9Sh/22WA=
+-----END CERTIFICATE-----
+
+# Issuer: CN=Wells Fargo Root Certificate Authority O=Wells Fargo OU=Wells Fargo Certification Authority
+# Subject: CN=Wells Fargo Root Certificate Authority O=Wells Fargo OU=Wells Fargo Certification Authority
+# Label: "Wells Fargo Root CA"
+# Serial: 971282334
+# MD5 Fingerprint: 20:0b:4a:7a:88:a7:a9:42:86:8a:5f:74:56:7b:88:05
+# SHA1 Fingerprint: 93:e6:ab:22:03:03:b5:23:28:dc:da:56:9e:ba:e4:d1:d1:cc:fb:65
+# SHA256 Fingerprint: 03:45:8b:6a:be:ec:c2:14:95:3d:97:14:9a:f4:53:91:69:1d:e9:f9:cd:cc:26:47:86:3a:3d:67:c9:5c:24:3b
+-----BEGIN CERTIFICATE-----
+MIID5TCCAs2gAwIBAgIEOeSXnjANBgkqhkiG9w0BAQUFADCBgjELMAkGA1UEBhMC
+VVMxFDASBgNVBAoTC1dlbGxzIEZhcmdvMSwwKgYDVQQLEyNXZWxscyBGYXJnbyBD
+ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEvMC0GA1UEAxMmV2VsbHMgRmFyZ28gUm9v
+dCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDAxMDExMTY0MTI4WhcNMjEwMTE0
+MTY0MTI4WjCBgjELMAkGA1UEBhMCVVMxFDASBgNVBAoTC1dlbGxzIEZhcmdvMSww
+KgYDVQQLEyNXZWxscyBGYXJnbyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEvMC0G
+A1UEAxMmV2VsbHMgRmFyZ28gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDVqDM7Jvk0/82bfuUER84A4n13
+5zHCLielTWi5MbqNQ1mXx3Oqfz1cQJ4F5aHiidlMuD+b+Qy0yGIZLEWukR5zcUHE
+SxP9cMIlrCL1dQu3U+SlK93OvRw6esP3E48mVJwWa2uv+9iWsWCaSOAlIiR5NM4O
+JgALTqv9i86C1y8IcGjBqAr5dE8Hq6T54oN+J3N0Prj5OEL8pahbSCOz6+MlsoCu
+ltQKnMJ4msZoGK43YjdeUXWoWGPAUe5AeH6orxqg4bB4nVCMe+ez/I4jsNtlAHCE
+AQgAFG5Uhpq6zPk3EPbg3oQtnaSFN9OH4xXQwReQfhkhahKpdv0SAulPIV4XAgMB
+AAGjYTBfMA8GA1UdEwEB/wQFMAMBAf8wTAYDVR0gBEUwQzBBBgtghkgBhvt7hwcB
+CzAyMDAGCCsGAQUFBwIBFiRodHRwOi8vd3d3LndlbGxzZmFyZ28uY29tL2NlcnRw
+b2xpY3kwDQYJKoZIhvcNAQEFBQADggEBANIn3ZwKdyu7IvICtUpKkfnRLb7kuxpo
+7w6kAOnu5+/u9vnldKTC2FJYxHT7zmu1Oyl5GFrvm+0fazbuSCUlFLZWohDo7qd/
+0D+j0MNdJu4HzMPBJCGHHt8qElNvQRbn7a6U+oxy+hNH8Dx+rn0ROhPs7fpvcmR7
+nX1/Jv16+yWt6j4pf0zjAFcysLPp7VMX2YuyFA4w6OXVE8Zkr8QA1dhYJPz1j+zx
+x32l2w8n0cbyQIjmH/ZhqPRCyLk306m+LFZ4wnKbWV01QIroTmMatukgalHizqSQ
+33ZwmVxwQ023tqcZZE6St8WRPH9IFmV7Fv3L/PvZ1dZPIWU7Sn9Ho/s=
+-----END CERTIFICATE-----
+
+# Issuer: CN=Swisscom Root CA 1 O=Swisscom OU=Digital Certificate Services
+# Subject: CN=Swisscom Root CA 1 O=Swisscom OU=Digital Certificate Services
+# Label: "Swisscom Root CA 1"
+# Serial: 122348795730808398873664200247279986742
+# MD5 Fingerprint: f8:38:7c:77:88:df:2c:16:68:2e:c2:e2:52:4b:b8:f9
+# SHA1 Fingerprint: 5f:3a:fc:0a:8b:64:f6:86:67:34:74:df:7e:a9:a2:fe:f9:fa:7a:51
+# SHA256 Fingerprint: 21:db:20:12:36:60:bb:2e:d4:18:20:5d:a1:1e:e7:a8:5a:65:e2:bc:6e:55:b5:af:7e:78:99:c8:a2:66:d9:2e
+-----BEGIN CERTIFICATE-----
+MIIF2TCCA8GgAwIBAgIQXAuFXAvnWUHfV8w/f52oNjANBgkqhkiG9w0BAQUFADBk
+MQswCQYDVQQGEwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0
+YWwgQ2VydGlmaWNhdGUgU2VydmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3Qg
+Q0EgMTAeFw0wNTA4MTgxMjA2MjBaFw0yNTA4MTgyMjA2MjBaMGQxCzAJBgNVBAYT
+AmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGlnaXRhbCBDZXJ0aWZp
+Y2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAxMIICIjAN
+BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0LmwqAzZuz8h+BvVM5OAFmUgdbI9
+m2BtRsiMMW8Xw/qabFbtPMWRV8PNq5ZJkCoZSx6jbVfd8StiKHVFXqrWW/oLJdih
+FvkcxC7mlSpnzNApbjyFNDhhSbEAn9Y6cV9Nbc5fuankiX9qUvrKm/LcqfmdmUc/
+TilftKaNXXsLmREDA/7n29uj/x2lzZAeAR81sH8A25Bvxn570e56eqeqDFdvpG3F
+EzuwpdntMhy0XmeLVNxzh+XTF3xmUHJd1BpYwdnP2IkCb6dJtDZd0KTeByy2dbco
+kdaXvij1mB7qWybJvbCXc9qukSbraMH5ORXWZ0sKbU/Lz7DkQnGMU3nn7uHbHaBu
+HYwadzVcFh4rUx80i9Fs/PJnB3r1re3WmquhsUvhzDdf/X/NTa64H5xD+SpYVUNF
+vJbNcA78yeNmuk6NO4HLFWR7uZToXTNShXEuT46iBhFRyePLoW4xCGQMwtI89Tbo
+19AOeCMgkckkKmUpWyL3Ic6DXqTz3kvTaI9GdVyDCW4pa8RwjPWd1yAv/0bSKzjC
+L3UcPX7ape8eYIVpQtPM+GP+HkM5haa2Y0EQs3MevNP6yn0WR+Kn1dCjigoIlmJW
+bjTb2QK5MHXjBNLnj8KwEUAKrNVxAmKLMb7dxiNYMUJDLXT5xp6mig/p/r+D5kNX
+JLrvRjSq1xIBOO0CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0hBBYw
+FDASBgdghXQBUwABBgdghXQBUwABMBIGA1UdEwEB/wQIMAYBAf8CAQcwHwYDVR0j
+BBgwFoAUAyUv3m+CATpcLNwroWm1Z9SM0/0wHQYDVR0OBBYEFAMlL95vggE6XCzc
+K6FptWfUjNP9MA0GCSqGSIb3DQEBBQUAA4ICAQA1EMvspgQNDQ/NwNurqPKIlwzf
+ky9NfEBWMXrrpA9gzXrzvsMnjgM+pN0S734edAY8PzHyHHuRMSG08NBsl9Tpl7Ik
+Vh5WwzW9iAUPWxAaZOHHgjD5Mq2eUCzneAXQMbFamIp1TpBcahQq4FJHgmDmHtqB
+sfsUC1rxn9KVuj7QG9YVHaO+htXbD8BJZLsuUBlL0iT43R4HVtA4oJVwIHaM190e
+3p9xxCPvgxNcoyQVTSlAPGrEqdi3pkSlDfTgnXceQHAm/NrZNuR55LU/vJtlvrsR
+ls/bxig5OgjOR1tTWsWZ/l2p3e9M1MalrQLmjAcSHm8D0W+go/MpvRLHUKKwf4ip
+mXeascClOS5cfGniLLDqN2qk4Vrh9VDlg++luyqI54zb/W1elxmofmZ1a3Hqv7HH
+b6D0jqTsNFFbjCYDcKF31QESVwA12yPeDooomf2xEG9L/zgtYE4snOtnta1J7ksf
+rK/7DZBaZmBwXarNeNQk7shBoJMBkpxqnvy5JMWzFYJ+vq6VK+uxwNrjAWALXmms
+hFZhvnEX/h0TD/7Gh0Xp/jKgGg0TpJRVcaUWi7rKibCyx/yP2FS1k2Kdzs9Z+z0Y
+zirLNRWCXf9UIltxUvu3yf5gmwBBZPCqKuy2QkPOiWaByIufOVQDJdMWNY6E0F/6
+MBr1mmz0DlP5OlvRHA==
+-----END CERTIFICATE-----
+
+# Issuer: CN=DigiCert Assured ID Root CA O=DigiCert Inc OU=www.digicert.com
+# Subject: CN=DigiCert Assured ID Root CA O=DigiCert Inc OU=www.digicert.com
+# Label: "DigiCert Assured ID Root CA"
+# Serial: 17154717934120587862167794914071425081
+# MD5 Fingerprint: 87:ce:0b:7b:2a:0e:49:00:e1:58:71:9b:37:a8:93:72
+# SHA1 Fingerprint: 05:63:b8:63:0d:62:d7:5a:bb:c8:ab:1e:4b:df:b5:a8:99:b2:4d:43
+# SHA256 Fingerprint: 3e:90:99:b5:01:5e:8f:48:6c:00:bc:ea:9d:11:1e:e7:21:fa:ba:35:5a:89:bc:f1:df:69:56:1e:3d:c6:32:5c
+-----BEGIN CERTIFICATE-----
+MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBl
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv
+b3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzExMTEwMDAwMDAwWjBlMQswCQYDVQQG
+EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl
+cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7c
+JpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYP
+mDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+
+wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4
+VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1roV9Iq4/
+AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whfGHdPAgMB
+AAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW
+BBRF66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYun
+pyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRC
+dWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTf
+fwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+fT8r87cm
+NW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5QZ7dsvfPx
+H2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe
++o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g==
+-----END CERTIFICATE-----
+
+# Issuer: CN=DigiCert Global Root CA O=DigiCert Inc OU=www.digicert.com
+# Subject: CN=DigiCert Global Root CA O=DigiCert Inc OU=www.digicert.com
+# Label: "DigiCert Global Root CA"
+# Serial: 10944719598952040374951832963794454346
+# MD5 Fingerprint: 79:e4:a9:84:0d:7d:3a:96:d7:c0:4f:e2:43:4c:89:2e
+# SHA1 Fingerprint: a8:98:5d:3a:65:e5:e5:c4:b2:d7:d6:6d:40:c6:dd:2f:b1:9c:54:36
+# SHA256 Fingerprint: 43:48:a0:e9:44:4c:78:cb:26:5e:05:8d:5e:89:44:b4:d8:4f:96:62:bd:26:db:25:7f:89:34:a4:43:c7:01:61
+-----BEGIN CERTIFICATE-----
+MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
+QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT
+MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
+b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG
+9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB
+CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97
+nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt
+43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P
+T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4
+gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO
+BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR
+TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw
+DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr
+hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg
+06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF
+PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls
+YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
+CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
+-----END CERTIFICATE-----
+
+# Issuer: CN=DigiCert High Assurance EV Root CA O=DigiCert Inc OU=www.digicert.com
+# Subject: CN=DigiCert High Assurance EV Root CA O=DigiCert Inc OU=www.digicert.com
+# Label: "DigiCert High Assurance EV Root CA"
+# Serial: 3553400076410547919724730734378100087
+# MD5 Fingerprint: d4:74:de:57:5c:39:b2:d3:9c:85:83:c5:c0:65:49:8a
+# SHA1 Fingerprint: 5f:b7:ee:06:33:e2:59:db:ad:0c:4c:9a:e6:d3:8f:1a:61:c7:dc:25
+# SHA256 Fingerprint: 74:31:e5:f4:c3:c1:ce:46:90:77:4f:0b:61:e0:54:40:88:3b:a9:a0:1e:d0:0b:a6:ab:d7:80:6e:d3:b1:18:cf
+-----BEGIN CERTIFICATE-----
+MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
+ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL
+MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
+LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
+RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm
++9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW
+PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM
+xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB
+Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3
+hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg
+EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF
+MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA
+FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec
+nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z
+eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF
+hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2
+Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
+vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep
++OkuE6N36B9K
+-----END CERTIFICATE-----
+
+# Issuer: CN=Class 2 Primary CA O=Certplus
+# Subject: CN=Class 2 Primary CA O=Certplus
+# Label: "Certplus Class 2 Primary CA"
+# Serial: 177770208045934040241468760488327595043
+# MD5 Fingerprint: 88:2c:8c:52:b8:a2:3c:f3:f7:bb:03:ea:ae:ac:42:0b
+# SHA1 Fingerprint: 74:20:74:41:72:9c:dd:92:ec:79:31:d8:23:10:8d:c2:81:92:e2:bb
+# SHA256 Fingerprint: 0f:99:3c:8a:ef:97:ba:af:56:87:14:0e:d5:9a:d1:82:1b:b4:af:ac:f0:aa:9a:58:b5:d5:7a:33:8a:3a:fb:cb
+-----BEGIN CERTIFICATE-----
+MIIDkjCCAnqgAwIBAgIRAIW9S/PY2uNp9pTXX8OlRCMwDQYJKoZIhvcNAQEFBQAw
+PTELMAkGA1UEBhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRswGQYDVQQDExJDbGFz
+cyAyIFByaW1hcnkgQ0EwHhcNOTkwNzA3MTcwNTAwWhcNMTkwNzA2MjM1OTU5WjA9
+MQswCQYDVQQGEwJGUjERMA8GA1UEChMIQ2VydHBsdXMxGzAZBgNVBAMTEkNsYXNz
+IDIgUHJpbWFyeSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANxQ
+ltAS+DXSCHh6tlJw/W/uz7kRy1134ezpfgSN1sxvc0NXYKwzCkTsA18cgCSR5aiR
+VhKC9+Ar9NuuYS6JEI1rbLqzAr3VNsVINyPi8Fo3UjMXEuLRYE2+L0ER4/YXJQyL
+kcAbmXuZVg2v7tK8R1fjeUl7NIknJITesezpWE7+Tt9avkGtrAjFGA7v0lPubNCd
+EgETjdyAYveVqUSISnFOYFWe2yMZeVYHDD9jC1yw4r5+FfyUM1hBOHTE4Y+L3yas
+H7WLO7dDWWuwJKZtkIvEcupdM5i3y95ee++U8Rs+yskhwcWYAqqi9lt3m/V+llU0
+HGdpwPFC40es/CgcZlUCAwEAAaOBjDCBiTAPBgNVHRMECDAGAQH/AgEKMAsGA1Ud
+DwQEAwIBBjAdBgNVHQ4EFgQU43Mt38sOKAze3bOkynm4jrvoMIkwEQYJYIZIAYb4
+QgEBBAQDAgEGMDcGA1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly93d3cuY2VydHBsdXMu
+Y29tL0NSTC9jbGFzczIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCnVM+IRBnL39R/
+AN9WM2K191EBkOvDP9GIROkkXe/nFL0gt5o8AP5tn9uQ3Nf0YtaLcF3n5QRIqWh8
+yfFC82x/xXp8HVGIutIKPidd3i1RTtMTZGnkLuPT55sJmabglZvOGtd/vjzOUrMR
+FcEPF80Du5wlFbqidon8BvEY0JNLDnyCt6X09l/+7UCmnYR0ObncHoUW2ikbhiMA
+ybuJfm6AiB4vFLQDJKgybwOaRywwvlbGp0ICcBvqQNi6BQNwB6SW//1IMwrh3KWB
+kJtN3X3n57LNXMhqlfil9o3EXXgIvnsG1knPGTZQIy4I5p4FTUcY1Rbpsda2ENW7
+l7+ijrRU
+-----END CERTIFICATE-----
+
+# Issuer: CN=DST Root CA X3 O=Digital Signature Trust Co.
+# Subject: CN=DST Root CA X3 O=Digital Signature Trust Co.
+# Label: "DST Root CA X3"
+# Serial: 91299735575339953335919266965803778155
+# MD5 Fingerprint: 41:03:52:dc:0f:f7:50:1b:16:f0:02:8e:ba:6f:45:c5
+# SHA1 Fingerprint: da:c9:02:4f:54:d8:f6:df:94:93:5f:b1:73:26:38:ca:6a:d7:7c:13
+# SHA256 Fingerprint: 06:87:26:03:31:a7:24:03:d9:09:f1:05:e6:9b:cf:0d:32:e1:bd:24:93:ff:c6:d9:20:6d:11:bc:d6:77:07:39
+-----BEGIN CERTIFICATE-----
+MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/
+MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
+DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow
+PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD
+Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O
+rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq
+OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b
+xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw
+7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD
+aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV
+HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG
+SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69
+ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr
+AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz
+R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5
+JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo
+Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ
+-----END CERTIFICATE-----
+
+# Issuer: CN=DST ACES CA X6 O=Digital Signature Trust OU=DST ACES
+# Subject: CN=DST ACES CA X6 O=Digital Signature Trust OU=DST ACES
+# Label: "DST ACES CA X6"
+# Serial: 17771143917277623872238992636097467865
+# MD5 Fingerprint: 21:d8:4c:82:2b:99:09:33:a2:eb:14:24:8d:8e:5f:e8
+# SHA1 Fingerprint: 40:54:da:6f:1c:3f:40:74:ac:ed:0f:ec:cd:db:79:d1:53:fb:90:1d
+# SHA256 Fingerprint: 76:7c:95:5a:76:41:2c:89:af:68:8e:90:a1:c7:0f:55:6c:fd:6b:60:25:db:ea:10:41:6d:7e:b6:83:1f:8c:40
+-----BEGIN CERTIFICATE-----
+MIIECTCCAvGgAwIBAgIQDV6ZCtadt3js2AdWO4YV2TANBgkqhkiG9w0BAQUFADBb
+MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3Qx
+ETAPBgNVBAsTCERTVCBBQ0VTMRcwFQYDVQQDEw5EU1QgQUNFUyBDQSBYNjAeFw0w
+MzExMjAyMTE5NThaFw0xNzExMjAyMTE5NThaMFsxCzAJBgNVBAYTAlVTMSAwHgYD
+VQQKExdEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdDERMA8GA1UECxMIRFNUIEFDRVMx
+FzAVBgNVBAMTDkRTVCBBQ0VTIENBIFg2MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAuT31LMmU3HWKlV1j6IR3dma5WZFcRt2SPp/5DgO0PWGSvSMmtWPu
+ktKe1jzIDZBfZIGxqAgNTNj50wUoUrQBJcWVHAx+PhCEdc/BGZFjz+iokYi5Q1K7
+gLFViYsx+tC3dr5BPTCapCIlF3PoHuLTrCq9Wzgh1SpL11V94zpVvddtawJXa+ZH
+fAjIgrrep4c9oW24MFbCswKBXy314powGCi4ZtPLAZZv6opFVdbgnf9nKxcCpk4a
+ahELfrd755jWjHZvwTvbUJN+5dCOHze4vbrGn2zpfDPyMjwmR/onJALJfh1biEIT
+ajV8fTXpLmaRcpPVMibEdPVTo7NdmvYJywIDAQABo4HIMIHFMA8GA1UdEwEB/wQF
+MAMBAf8wDgYDVR0PAQH/BAQDAgHGMB8GA1UdEQQYMBaBFHBraS1vcHNAdHJ1c3Rk
+c3QuY29tMGIGA1UdIARbMFkwVwYKYIZIAWUDAgEBATBJMEcGCCsGAQUFBwIBFjto
+dHRwOi8vd3d3LnRydXN0ZHN0LmNvbS9jZXJ0aWZpY2F0ZXMvcG9saWN5L0FDRVMt
+aW5kZXguaHRtbDAdBgNVHQ4EFgQUCXIGThhDD+XWzMNqizF7eI+og7gwDQYJKoZI
+hvcNAQEFBQADggEBAKPYjtay284F5zLNAdMEA+V25FYrnJmQ6AgwbN99Pe7lv7Uk
+QIRJ4dEorsTCOlMwiPH1d25Ryvr/ma8kXxug/fKshMrfqfBfBC6tFr8hlxCBPeP/
+h40y3JTlR4peahPJlJU90u7INJXQgNStMgiAVDzgvVJT11J8smk/f3rPanTK+gQq
+nExaBqXpIK1FZg9p8d2/6eMyi/rgwYZNcjwu2JN4Cir42NInPRmJX1p7ijvMDNpR
+rscL9yuwNwXsvFcj4jjSm2jzVhKIT0J8uDHEtdvkyCE06UgRNe76x5JXxZ805Mf2
+9w4LTJxoeHtxMcfrHuBnQfO3oKfN5XozNmr6mis=
+-----END CERTIFICATE-----
+
+# Issuer: CN=TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı O=(c) 2005 TÜRKTRUST Bilgi İletişim ve Bilişim Güvenliği Hizmetleri A.Ş.
+# Subject: CN=TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı O=(c) 2005 TÜRKTRUST Bilgi İletişim ve Bilişim Güvenliği Hizmetleri A.Ş.
+# Label: "TURKTRUST Certificate Services Provider Root 1"
+# Serial: 1
+# MD5 Fingerprint: f1:6a:22:18:c9:cd:df:ce:82:1d:1d:b7:78:5c:a9:a5
+# SHA1 Fingerprint: 79:98:a3:08:e1:4d:65:85:e6:c2:1e:15:3a:71:9f:ba:5a:d3:4a:d9
+# SHA256 Fingerprint: 44:04:e3:3b:5e:14:0d:cf:99:80:51:fd:fc:80:28:c7:c8:16:15:c5:ee:73:7b:11:1b:58:82:33:a9:b5:35:a0
+-----BEGIN CERTIFICATE-----
+MIID+zCCAuOgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBtzE/MD0GA1UEAww2VMOc
+UktUUlVTVCBFbGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sx
+c8SxMQswCQYDVQQGDAJUUjEPMA0GA1UEBwwGQU5LQVJBMVYwVAYDVQQKDE0oYykg
+MjAwNSBUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmlsacWfaW0gR8O8
+dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLjAeFw0wNTA1MTMxMDI3MTdaFw0xNTAz
+MjIxMDI3MTdaMIG3MT8wPQYDVQQDDDZUw5xSS1RSVVNUIEVsZWt0cm9uaWsgU2Vy
+dGlmaWthIEhpem1ldCBTYcSfbGF5xLFjxLFzxLExCzAJBgNVBAYMAlRSMQ8wDQYD
+VQQHDAZBTktBUkExVjBUBgNVBAoMTShjKSAyMDA1IFTDnFJLVFJVU1QgQmlsZ2kg
+xLBsZXRpxZ9pbSB2ZSBCaWxpxZ9pbSBHw7x2ZW5sacSfaSBIaXptZXRsZXJpIEEu
+xZ4uMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAylIF1mMD2Bxf3dJ7
+XfIMYGFbazt0K3gNfUW9InTojAPBxhEqPZW8qZSwu5GXyGl8hMW0kWxsE2qkVa2k
+heiVfrMArwDCBRj1cJ02i67L5BuBf5OI+2pVu32Fks66WJ/bMsW9Xe8iSi9BB35J
+YbOG7E6mQW6EvAPs9TscyB/C7qju6hJKjRTP8wrgUDn5CDX4EVmt5yLqS8oUBt5C
+urKZ8y1UiBAG6uEaPj1nH/vO+3yC6BFdSsG5FOpU2WabfIl9BJpiyelSPJ6c79L1
+JuTm5Rh8i27fbMx4W09ysstcP4wFjdFMjK2Sx+F4f2VsSQZQLJ4ywtdKxnWKWU51
+b0dewQIDAQABoxAwDjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAV
+9VX/N5aAWSGk/KEVTCD21F/aAyT8z5Aa9CEKmu46sWrv7/hg0Uw2ZkUd82YCdAR7
+kjCo3gp2D++Vbr3JN+YaDayJSFvMgzbC9UZcWYJWtNX+I7TYVBxEq8Sn5RTOPEFh
+fEPmzcSBCYsk+1Ql1haolgxnB2+zUEfjHCQo3SqYpGH+2+oSN7wBGjSFvW5P55Fy
+B0SFHljKVETd96y5y4khctuPwGkplyqjrhgjlxxBKot8KsF8kOipKMDTkcatKIdA
+aLX/7KfS0zgYnNN9aV3wxqUeJBujR/xpB2jn5Jq07Q+hh4cCzofSSE7hvP/L8XKS
+RGQDJereW26fyfJOrN3H
+-----END CERTIFICATE-----
+
+# Issuer: CN=TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı O=TÜRKTRUST Bilgi İletişim ve Bilişim Güvenliği Hizmetleri A.Ş. (c) Kasım 2005
+# Subject: CN=TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı O=TÜRKTRUST Bilgi İletişim ve Bilişim Güvenliği Hizmetleri A.Ş. (c) Kasım 2005
+# Label: "TURKTRUST Certificate Services Provider Root 2"
+# Serial: 1
+# MD5 Fingerprint: 37:a5:6e:d4:b1:25:84:97:b7:fd:56:15:7a:f9:a2:00
+# SHA1 Fingerprint: b4:35:d4:e1:11:9d:1c:66:90:a7:49:eb:b3:94:bd:63:7b:a7:82:b7
+# SHA256 Fingerprint: c4:70:cf:54:7e:23:02:b9:77:fb:29:dd:71:a8:9a:7b:6c:1f:60:77:7b:03:29:f5:60:17:f3:28:bf:4f:6b:e6
+-----BEGIN CERTIFICATE-----
+MIIEPDCCAySgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvjE/MD0GA1UEAww2VMOc
+UktUUlVTVCBFbGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sx
+c8SxMQswCQYDVQQGEwJUUjEPMA0GA1UEBwwGQW5rYXJhMV0wWwYDVQQKDFRUw5xS
+S1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnEn2kg
+SGl6bWV0bGVyaSBBLsWeLiAoYykgS2FzxLFtIDIwMDUwHhcNMDUxMTA3MTAwNzU3
+WhcNMTUwOTE2MTAwNzU3WjCBvjE/MD0GA1UEAww2VMOcUktUUlVTVCBFbGVrdHJv
+bmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJU
+UjEPMA0GA1UEBwwGQW5rYXJhMV0wWwYDVQQKDFRUw5xSS1RSVVNUIEJpbGdpIMSw
+bGV0acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWe
+LiAoYykgS2FzxLFtIDIwMDUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
+AQCpNn7DkUNMwxmYCMjHWHtPFoylzkkBH3MOrHUTpvqeLCDe2JAOCtFp0if7qnef
+J1Il4std2NiDUBd9irWCPwSOtNXwSadktx4uXyCcUHVPr+G1QRT0mJKIx+XlZEdh
+R3n9wFHxwZnn3M5q+6+1ATDcRhzviuyV79z/rxAc653YsKpqhRgNF8k+v/Gb0AmJ
+Qv2gQrSdiVFVKc8bcLyEVK3BEx+Y9C52YItdP5qtygy/p1Zbj3e41Z55SZI/4PGX
+JHpsmxcPbe9TmJEr5A++WXkHeLuXlfSfadRYhwqp48y2WBmfJiGxxFmNskF1wK1p
+zpwACPI2/z7woQ8arBT9pmAPAgMBAAGjQzBBMB0GA1UdDgQWBBTZN7NOBf3Zz58S
+Fq62iS/rJTqIHDAPBgNVHQ8BAf8EBQMDBwYAMA8GA1UdEwEB/wQFMAMBAf8wDQYJ
+KoZIhvcNAQEFBQADggEBAHJglrfJ3NgpXiOFX7KzLXb7iNcX/nttRbj2hWyfIvwq
+ECLsqrkw9qtY1jkQMZkpAL2JZkH7dN6RwRgLn7Vhy506vvWolKMiVW4XSf/SKfE4
+Jl3vpao6+XF75tpYHdN0wgH6PmlYX63LaL4ULptswLbcoCb6dxriJNoaN+BnrdFz
+gw2lGh1uEpJ+hGIAF728JRhX8tepb1mIvDS3LoV4nZbcFMMsilKbloxSZj2GFotH
+uFEJjOp9zYhys2AzsfAKRO8P9Qk3iCQOLGsgOqL6EfJANZxEaGM7rDNvY7wsu/LS
+y3Z9fYjYHcgFHW68lKlmjHdxx/qR+i9Rnuk5UrbnBEI=
+-----END CERTIFICATE-----
+
+# Issuer: CN=SwissSign Gold CA - G2 O=SwissSign AG
+# Subject: CN=SwissSign Gold CA - G2 O=SwissSign AG
+# Label: "SwissSign Gold CA - G2"
+# Serial: 13492815561806991280
+# MD5 Fingerprint: 24:77:d9:a8:91:d1:3b:fa:88:2d:c2:ff:f8:cd:33:93
+# SHA1 Fingerprint: d8:c5:38:8a:b7:30:1b:1b:6e:d4:7a:e6:45:25:3a:6f:9f:1a:27:61
+# SHA256 Fingerprint: 62:dd:0b:e9:b9:f5:0a:16:3e:a0:f8:e7:5c:05:3b:1e:ca:57:ea:55:c8:68:8f:64:7c:68:81:f2:c8:35:7b:95
+-----BEGIN CERTIFICATE-----
+MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
+BAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2ln
+biBHb2xkIENBIC0gRzIwHhcNMDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBF
+MQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dpc3NTaWduIEFHMR8wHQYDVQQDExZT
+d2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
+CgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUqt2/8
+76LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+
+bbqBHH5CjCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c
+6bM8K8vzARO/Ws/BtQpgvd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqE
+emA8atufK+ze3gE/bk3lUIbLtK/tREDFylqM2tIrfKjuvqblCqoOpd8FUrdVxyJd
+MmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvRAiTysybUa9oEVeXBCsdt
+MDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuendjIj3o02y
+MszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69y
+FGkOpeUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPi
+aG59je883WX0XaxR7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxM
+gI93e2CaHt+28kgeDrpOVG2Y4OGiGqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCB
+qTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUWyV7
+lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64OfPAeGZe6Drn
+8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov
+L3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe6
+45R88a7A3hfm5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczO
+UYrHUDFu4Up+GC9pWbY9ZIEr44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5
+O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOfMke6UiI0HTJ6CVanfCU2qT1L2sCC
+bwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6mGu6uLftIdxf+u+yv
+GPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxpmo/a
+77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCC
+hdiDyyJkvC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid3
+92qgQmwLOM7XdVAyksLfKzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEpp
+Ld6leNcG2mqeSz53OiATIgHQv2ieY2BrNU0LbbqhPcCT4H8js1WtciVORvnSFu+w
+ZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6LqjviOvrv1vA+ACOzB2+htt
+Qc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ
+-----END CERTIFICATE-----
+
+# Issuer: CN=SwissSign Silver CA - G2 O=SwissSign AG
+# Subject: CN=SwissSign Silver CA - G2 O=SwissSign AG
+# Label: "SwissSign Silver CA - G2"
+# Serial: 5700383053117599563
+# MD5 Fingerprint: e0:06:a1:c9:7d:cf:c9:fc:0d:c0:56:75:96:d8:62:13
+# SHA1 Fingerprint: 9b:aa:e5:9f:56:ee:21:cb:43:5a:be:25:93:df:a7:f0:40:d1:1d:cb
+# SHA256 Fingerprint: be:6c:4d:a2:bb:b9:ba:59:b6:f3:93:97:68:37:42:46:c3:c0:05:99:3f:a9:8f:02:0d:1d:ed:be:d4:8a:81:d5
+-----BEGIN CERTIFICATE-----
+MIIFvTCCA6WgAwIBAgIITxvUL1S7L0swDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UE
+BhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWdu
+IFNpbHZlciBDQSAtIEcyMB4XDTA2MTAyNTA4MzI0NloXDTM2MTAyNTA4MzI0Nlow
+RzELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMY
+U3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
+MIICCgKCAgEAxPGHf9N4Mfc4yfjDmUO8x/e8N+dOcbpLj6VzHVxumK4DV644N0Mv
+Fz0fyM5oEMF4rhkDKxD6LHmD9ui5aLlV8gREpzn5/ASLHvGiTSf5YXu6t+WiE7br
+YT7QbNHm+/pe7R20nqA1W6GSy/BJkv6FCgU+5tkL4k+73JU3/JHpMjUi0R86TieF
+nbAVlDLaYQ1HTWBCrpJH6INaUFjpiou5XaHc3ZlKHzZnu0jkg7Y360g6rw9njxcH
+6ATK72oxh9TAtvmUcXtnZLi2kUpCe2UuMGoM9ZDulebyzYLs2aFK7PayS+VFheZt
+eJMELpyCbTapxDFkH4aDCyr0NQp4yVXPQbBH6TCfmb5hqAaEuSh6XzjZG6k4sIN/
+c8HDO0gqgg8hm7jMqDXDhBuDsz6+pJVpATqJAHgE2cn0mRmrVn5bi4Y5FZGkECwJ
+MoBgs5PAKrYYC51+jUnyEEp/+dVGLxmSo5mnJqy7jDzmDrxHB9xzUfFwZC8I+bRH
+HTBsROopN4WSaGa8gzj+ezku01DwH/teYLappvonQfGbGHLy9YR0SslnxFSuSGTf
+jNFusB3hB48IHpmccelM2KX3RxIfdNFRnobzwqIjQAtz20um53MGjMGg6cFZrEb6
+5i/4z3GcRm25xBWNOHkDRUjvxF3XCO6HOSKGsg0PWEP3calILv3q1h8CAwEAAaOB
+rDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU
+F6DNweRBtjpbO8tFnb0cwpj6hlgwHwYDVR0jBBgwFoAUF6DNweRBtjpbO8tFnb0c
+wpj6hlgwRgYDVR0gBD8wPTA7BglghXQBWQEDAQEwLjAsBggrBgEFBQcCARYgaHR0
+cDovL3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIB
+AHPGgeAn0i0P4JUw4ppBf1AsX19iYamGamkYDHRJ1l2E6kFSGG9YrVBWIGrGvShp
+WJHckRE1qTodvBqlYJ7YH39FkWnZfrt4csEGDyrOj4VwYaygzQu4OSlWhDJOhrs9
+xCrZ1x9y7v5RoSJBsXECYxqCsGKrXlcSH9/L3XWgwF15kIwb4FDm3jH+mHtwX6WQ
+2K34ArZv02DdQEsixT2tOnqfGhpHkXkzuoLcMmkDlm4fS/Bx/uNncqCxv1yL5PqZ
+IseEuRuNI5c/7SXgz2W79WEE790eslpBIlqhn10s6FvJbakMDHiqYMZWjwFaDGi8
+aRl5xB9+lwW/xekkUV7U1UtT7dkjWjYDZaPBA61BMPNGG4WQr2W11bHkFlt4dR2X
+em1ZqSqPe97Dh4kQmUlzeMg9vVE1dCrV8X5pGyq7O70luJpaPXJhkGaH7gzWTdQR
+dAtq/gsD/KNVV4n+SsuuWxcFyPKNIzFTONItaj+CuY0IavdeQXRuwxF+B6wpYJE/
+OMpXEA29MC/HpeZBoNquBYeaoKRlbEwJDIm6uNO5wJOKMPqN5ZprFQFOZ6raYlY+
+hAhm0sQ2fac+EPyI4NSA5QC9qvNOBqN6avlicuMJT+ubDgEj8Z+7fNzcbBGXJbLy
+tGMU0gYqZ4yD9c7qB9iaah7s5Aq7KkzrCWA5zspi2C5u
+-----END CERTIFICATE-----
+
+# Issuer: CN=GeoTrust Primary Certification Authority O=GeoTrust Inc.
+# Subject: CN=GeoTrust Primary Certification Authority O=GeoTrust Inc.
+# Label: "GeoTrust Primary Certification Authority"
+# Serial: 32798226551256963324313806436981982369
+# MD5 Fingerprint: 02:26:c3:01:5e:08:30:37:43:a9:d0:7d:cf:37:e6:bf
+# SHA1 Fingerprint: 32:3c:11:8e:1b:f7:b8:b6:52:54:e2:e2:10:0d:d6:02:90:37:f0:96
+# SHA256 Fingerprint: 37:d5:10:06:c5:12:ea:ab:62:64:21:f1:ec:8c:92:01:3f:c5:f8:2a:e9:8e:e5:33:eb:46:19:b8:de:b4:d0:6c
+-----BEGIN CERTIFICATE-----
+MIIDfDCCAmSgAwIBAgIQGKy1av1pthU6Y2yv2vrEoTANBgkqhkiG9w0BAQUFADBY
+MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMo
+R2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEx
+MjcwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMFgxCzAJBgNVBAYTAlVTMRYwFAYDVQQK
+Ew1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQcmltYXJ5IENlcnRp
+ZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+AQEAvrgVe//UfH1nrYNke8hCUy3f9oQIIGHWAVlqnEQRr+92/ZV+zmEwu3qDXwK9
+AWbK7hWNb6EwnL2hhZ6UOvNWiAAxz9juapYC2e0DjPt1befquFUWBRaa9OBesYjA
+ZIVcFU2Ix7e64HXprQU9nceJSOC7KMgD4TCTZF5SwFlwIjVXiIrxlQqD17wxcwE0
+7e9GceBrAqg1cmuXm2bgyxx5X9gaBGgeRwLmnWDiNpcB3841kt++Z8dtd1k7j53W
+kBWUvEI0EME5+bEnPn7WinXFsq+W06Lem+SYvn3h6YGttm/81w7a4DSwDRp35+MI
+mO9Y+pyEtzavwt+s0vQQBnBxNQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4G
+A1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJ
+KoZIhvcNAQEFBQADggEBAFpwfyzdtzRP9YZRqSa+S7iq8XEN3GHHoOo0Hnp3DwQ1
+6CePbJC/kRYkRj5KTs4rFtULUh38H2eiAkUxT87z+gOneZ1TatnaYzr4gNfTmeGl
+4b7UVXGYNTq+k+qurUKykG/g/CFNNWMziUnWm07Kx+dOCQD32sfvmWKZd7aVIl6K
+oKv0uHiYyjgZmclynnjNS6yvGaBzEi38wkG6gZHaFloxt/m0cYASSJlyc1pZU8Fj
+UjPtp8nSOQJw+uCxQmYpqptR7TBUIhRf2asdweSU8Pj1K/fqynhG1riR/aYNKxoU
+AT6A8EKglQdebc3MS6RFjasS6LPeWuWgfOgPIh1a6Vk=
+-----END CERTIFICATE-----
+
+# Issuer: CN=thawte Primary Root CA O=thawte, Inc. OU=Certification Services Division/(c) 2006 thawte, Inc. - For authorized use only
+# Subject: CN=thawte Primary Root CA O=thawte, Inc. OU=Certification Services Division/(c) 2006 thawte, Inc. - For authorized use only
+# Label: "thawte Primary Root CA"
+# Serial: 69529181992039203566298953787712940909
+# MD5 Fingerprint: 8c:ca:dc:0b:22:ce:f5:be:72:ac:41:1a:11:a8:d8:12
+# SHA1 Fingerprint: 91:c6:d6:ee:3e:8a:c8:63:84:e5:48:c2:99:29:5c:75:6c:81:7b:81
+# SHA256 Fingerprint: 8d:72:2f:81:a9:c1:13:c0:79:1d:f1:36:a2:96:6d:b2:6c:95:0a:97:1d:b4:6b:41:99:f4:ea:54:b7:8b:fb:9f
+-----BEGIN CERTIFICATE-----
+MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCB
+qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf
+Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw
+MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV
+BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3MDAwMDAwWhcNMzYw
+NzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5j
+LjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYG
+A1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl
+IG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCsoPD7gFnUnMekz52hWXMJEEUMDSxuaPFs
+W0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ1CRfBsDMRJSUjQJib+ta
+3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGcq/gcfomk
+6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6
+Sk/KaAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94J
+NqR32HuHUETVPm4pafs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBA
+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XP
+r87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUFAAOCAQEAeRHAS7ORtvzw6WfU
+DW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeEuzLlQRHAd9mz
+YJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX
+xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2
+/qxAeeWsEG89jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/
+LHbTY5xZ3Y+m4Q6gLkH3LpVHz7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7
+jVaMaA==
+-----END CERTIFICATE-----
+
+# Issuer: CN=VeriSign Class 3 Public Primary Certification Authority - G5 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2006 VeriSign, Inc. - For authorized use only
+# Subject: CN=VeriSign Class 3 Public Primary Certification Authority - G5 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2006 VeriSign, Inc. - For authorized use only
+# Label: "VeriSign Class 3 Public Primary Certification Authority - G5"
+# Serial: 33037644167568058970164719475676101450
+# MD5 Fingerprint: cb:17:e4:31:67:3e:e2:09:fe:45:57:93:f3:0a:fa:1c
+# SHA1 Fingerprint: 4e:b6:d5:78:49:9b:1c:cf:5f:58:1e:ad:56:be:3d:9b:67:44:a5:e5
+# SHA256 Fingerprint: 9a:cf:ab:7e:43:c8:d8:80:d0:6b:26:2a:94:de:ee:e4:b4:65:99:89:c3:d0:ca:f1:9b:af:64:05:e4:1a:b7:df
+-----BEGIN CERTIFICATE-----
+MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCB
+yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
+ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp
+U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW
+ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0
+aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCByjEL
+MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW
+ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2ln
+biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp
+U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y
+aXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1
+nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbex
+t0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIz
+SdhDY2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQG
+BO+QueQA5N06tRn/Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+
+rCpSx4/VBEnkjWNHiDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/
+NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E
+BAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAH
+BgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy
+aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKv
+MzEzMA0GCSqGSIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzE
+p6B4Eq1iDkVwZMXnl2YtmAl+X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y
+5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKEKQsTb47bDN0lAtukixlE0kF6BWlK
+WE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiCKm0oHw0LxOXnGiYZ
+4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vEZV8N
+hnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq
+-----END CERTIFICATE-----
+
+# Issuer: CN=SecureTrust CA O=SecureTrust Corporation
+# Subject: CN=SecureTrust CA O=SecureTrust Corporation
+# Label: "SecureTrust CA"
+# Serial: 17199774589125277788362757014266862032
+# MD5 Fingerprint: dc:32:c3:a7:6d:25:57:c7:68:09:9d:ea:2d:a9:a2:d1
+# SHA1 Fingerprint: 87:82:c6:c3:04:35:3b:cf:d2:96:92:d2:59:3e:7d:44:d9:34:ff:11
+# SHA256 Fingerprint: f1:c1:b5:0a:e5:a2:0d:d8:03:0e:c9:f6:bc:24:82:3d:d3:67:b5:25:57:59:b4:e7:1b:61:fc:e9:f7:37:5d:73
+-----BEGIN CERTIFICATE-----
+MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBI
+MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x
+FzAVBgNVBAMTDlNlY3VyZVRydXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIz
+MTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAeBgNVBAoTF1NlY3VyZVRydXN0IENv
+cnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQXOZEz
+Zum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO
+0gMdA+9tDWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIao
+wW8xQmxSPmjL8xk037uHGFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj
+7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b01k/unK8RCSc43Oz969XL0Imnal0ugBS
+8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmHursCAwEAAaOBnTCBmjAT
+BgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB
+/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCeg
+JYYjaHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGC
+NxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt3
+6Z3q059c4EVlew3KW+JwULKUBRSuSceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/
+3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHfmbx8IVQr5Fiiu1cprp6poxkm
+D5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZnMUFdAvnZyPS
+CPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR
+3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE=
+-----END CERTIFICATE-----
+
+# Issuer: CN=Secure Global CA O=SecureTrust Corporation
+# Subject: CN=Secure Global CA O=SecureTrust Corporation
+# Label: "Secure Global CA"
+# Serial: 9751836167731051554232119481456978597
+# MD5 Fingerprint: cf:f4:27:0d:d4:ed:dc:65:16:49:6d:3d:da:bf:6e:de
+# SHA1 Fingerprint: 3a:44:73:5a:e5:81:90:1f:24:86:61:46:1e:3b:9c:c4:5f:f5:3a:1b
+# SHA256 Fingerprint: 42:00:f5:04:3a:c8:59:0e:bb:52:7d:20:9e:d1:50:30:29:fb:cb:d4:1c:a1:b5:06:ec:27:f1:5a:de:7d:ac:69
+-----BEGIN CERTIFICATE-----
+MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBK
+MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x
+GTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkx
+MjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3Qg
+Q29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jxYDiJ
+iQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa
+/FHtaMbQbqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJ
+jnIFHovdRIWCQtBJwB1g8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnI
+HmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYVHDGA76oYa8J719rO+TMg1fW9ajMtgQT7
+sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi0XPnj3pDAgMBAAGjgZ0w
+gZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQF
+MAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCsw
+KaAnoCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsG
+AQQBgjcVAQQDAgEAMA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0L
+URYD7xh8yOOvaliTFGCRsoTciE6+OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXO
+H0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cnCDpOGR86p1hcF895P4vkp9Mm
+I50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/53CYNv6ZHdAbY
+iNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc
+f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW
+-----END CERTIFICATE-----
+
+# Issuer: CN=COMODO Certification Authority O=COMODO CA Limited
+# Subject: CN=COMODO Certification Authority O=COMODO CA Limited
+# Label: "COMODO Certification Authority"
+# Serial: 104350513648249232941998508985834464573
+# MD5 Fingerprint: 5c:48:dc:f7:42:72:ec:56:94:6d:1c:cc:71:35:80:75
+# SHA1 Fingerprint: 66:31:bf:9e:f7:4f:9e:b6:c9:d5:a6:0c:ba:6a:be:d1:f7:bd:ef:7b
+# SHA256 Fingerprint: 0c:2c:d6:3d:f7:80:6f:a3:99:ed:e8:09:11:6b:57:5b:f8:79:89:f0:65:18:f9:80:8c:86:05:03:17:8b:af:66
+-----BEGIN CERTIFICATE-----
+MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCB
+gTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
+A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNV
+BAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEyMDEwMDAw
+MDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3Jl
+YXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01P
+RE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0
+aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3
+UcEbVASY06m/weaKXTuH+7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI
+2GqGd0S7WWaXUF601CxwRM/aN5VCaTwwxHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8
+Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV4EajcNxo2f8ESIl33rXp
++2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA1KGzqSX+
+DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5O
+nKVIrLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW
+/zAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6g
+PKA6hjhodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9u
+QXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOCAQEAPpiem/Yb6dc5t3iuHXIY
+SdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CPOGEIqB6BCsAv
+IC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/
+RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4
+zJVSk/BwJVmcIGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5dd
+BA6+C4OmF4O5MBKgxTMVBbkN+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IB
+ZQ==
+-----END CERTIFICATE-----
+
+# Issuer: CN=Network Solutions Certificate Authority O=Network Solutions L.L.C.
+# Subject: CN=Network Solutions Certificate Authority O=Network Solutions L.L.C.
+# Label: "Network Solutions Certificate Authority"
+# Serial: 116697915152937497490437556386812487904
+# MD5 Fingerprint: d3:f3:a6:16:c0:fa:6b:1d:59:b1:2d:96:4d:0e:11:2e
+# SHA1 Fingerprint: 74:f8:a3:c3:ef:e7:b3:90:06:4b:83:90:3c:21:64:60:20:e5:df:ce
+# SHA256 Fingerprint: 15:f0:ba:00:a3:ac:7a:f3:ac:88:4c:07:2b:10:11:a0:77:bd:77:c0:97:f4:01:64:b2:f8:59:8a:bd:83:86:0c
+-----BEGIN CERTIFICATE-----
+MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBi
+MQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu
+MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3Jp
+dHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMxMjM1OTU5WjBiMQswCQYDVQQGEwJV
+UzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydO
+ZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwz
+c7MEL7xxjOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPP
+OCwGJgl6cvf6UDL4wpPTaaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rl
+mGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXTcrA/vGp97Eh/jcOrqnErU2lBUzS1sLnF
+BgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc/Qzpf14Dl847ABSHJ3A4
+qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMBAAGjgZcw
+gZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIB
+BjAPBgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwu
+bmV0c29sc3NsLmNvbS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3Jp
+dHkuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc8
+6fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q4LqILPxFzBiwmZVRDuwduIj/
+h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/GGUsyfJj4akH
+/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv
+wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHN
+pGxlaKFJdlxDydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey
+-----END CERTIFICATE-----
+
+# Issuer: CN=WellsSecure Public Root Certificate Authority O=Wells Fargo WellsSecure OU=Wells Fargo Bank NA
+# Subject: CN=WellsSecure Public Root Certificate Authority O=Wells Fargo WellsSecure OU=Wells Fargo Bank NA
+# Label: "WellsSecure Public Root Certificate Authority"
+# Serial: 1
+# MD5 Fingerprint: 15:ac:a5:c2:92:2d:79:bc:e8:7f:cb:67:ed:02:cf:36
+# SHA1 Fingerprint: e7:b4:f6:9d:61:ec:90:69:db:7e:90:a7:40:1a:3c:f4:7d:4f:e8:ee
+# SHA256 Fingerprint: a7:12:72:ae:aa:a3:cf:e8:72:7f:7f:b3:9f:0f:b3:d1:e5:42:6e:90:60:b0:6e:e6:f1:3e:9a:3c:58:33:cd:43
+-----BEGIN CERTIFICATE-----
+MIIEvTCCA6WgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMCVVMx
+IDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxs
+cyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9v
+dCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDcxMjEzMTcwNzU0WhcNMjIxMjE0
+MDAwNzU0WjCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdl
+bGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQD
+DC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkw
+ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDub7S9eeKPCCGeOARBJe+r
+WxxTkqxtnt3CxC5FlAM1iGd0V+PfjLindo8796jE2yljDpFoNoqXjopxaAkH5OjU
+Dk/41itMpBb570OYj7OeUt9tkTmPOL13i0Nj67eT/DBMHAGTthP796EfvyXhdDcs
+HqRePGj4S78NuR4uNuip5Kf4D8uCdXw1LSLWwr8L87T8bJVhHlfXBIEyg1J55oNj
+z7fLY4sR4r1e6/aN7ZVyKLSsEmLpSjPmgzKuBXWVvYSV2ypcm44uDLiBK0HmOFaf
+SZtsdvqKXfcBeYF8wYNABf5x/Qw/zE5gCQ5lRxAvAcAFP4/4s0HvWkJ+We/Slwxl
+AgMBAAGjggE0MIIBMDAPBgNVHRMBAf8EBTADAQH/MDkGA1UdHwQyMDAwLqAsoCqG
+KGh0dHA6Ly9jcmwucGtpLndlbGxzZmFyZ28uY29tL3dzcHJjYS5jcmwwDgYDVR0P
+AQH/BAQDAgHGMB0GA1UdDgQWBBQmlRkQ2eihl5H/3BnZtQQ+0nMKajCBsgYDVR0j
+BIGqMIGngBQmlRkQ2eihl5H/3BnZtQQ+0nMKaqGBi6SBiDCBhTELMAkGA1UEBhMC
+VVMxIDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNX
+ZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMg
+Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHmCAQEwDQYJKoZIhvcNAQEFBQADggEB
+ALkVsUSRzCPIK0134/iaeycNzXK7mQDKfGYZUMbVmO2rvwNa5U3lHshPcZeG1eMd
+/ZDJPHV3V3p9+N701NX3leZ0bh08rnyd2wIDBSxxSyU+B+NemvVmFymIGjifz6pB
+A4SXa5M4esowRBskRDPQ5NHcKDj0E0M1NSljqHyita04pO2t/caaH/+Xc/77szWn
+k4bGdpEA5qxRFsQnMlzbc9qlk1eOPm01JghZ1edE13YgY+esE2fDbbFwRnzVlhE9
+iW9dqKHrjQrawx0zbKPqZxmamX9LPYNRKh3KL4YMon4QLSvUFpULB6ouFJJJtylv
+2G0xffX8oRAHh84vWdw+WNs=
+-----END CERTIFICATE-----
+
+# Issuer: CN=COMODO ECC Certification Authority O=COMODO CA Limited
+# Subject: CN=COMODO ECC Certification Authority O=COMODO CA Limited
+# Label: "COMODO ECC Certification Authority"
+# Serial: 41578283867086692638256921589707938090
+# MD5 Fingerprint: 7c:62:ff:74:9d:31:53:5e:68:4a:d5:78:aa:1e:bf:23
+# SHA1 Fingerprint: 9f:74:4e:9f:2b:4d:ba:ec:0f:31:2c:50:b6:56:3b:8e:2d:93:c3:11
+# SHA256 Fingerprint: 17:93:92:7a:06:14:54:97:89:ad:ce:2f:8f:34:f7:f0:b6:6d:0f:3a:e3:a3:b8:4d:21:ec:15:db:ba:4f:ad:c7
+-----BEGIN CERTIFICATE-----
+MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTEL
+MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE
+BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMT
+IkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwMzA2MDAw
+MDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdy
+ZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09N
+T0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlv
+biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSR
+FtSrYpn1PlILBs5BAH+X4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0J
+cfRK9ChQtP6IHG4/bC8vCVlbpVsLM5niwz2J+Wos77LTBumjQjBAMB0GA1UdDgQW
+BBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/
+BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VGFAkK+qDm
+fQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdv
+GDeAU/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY=
+-----END CERTIFICATE-----
+
+# Issuer: CN=IGC/A O=PM/SGDN OU=DCSSI
+# Subject: CN=IGC/A O=PM/SGDN OU=DCSSI
+# Label: "IGC/A"
+# Serial: 245102874772
+# MD5 Fingerprint: 0c:7f:dd:6a:f4:2a:b9:c8:9b:bd:20:7e:a9:db:5c:37
+# SHA1 Fingerprint: 60:d6:89:74:b5:c2:65:9e:8a:0f:c1:88:7c:88:d2:46:69:1b:18:2c
+# SHA256 Fingerprint: b9:be:a7:86:0a:96:2e:a3:61:1d:ab:97:ab:6d:a3:e2:1c:10:68:b9:7d:55:57:5e:d0:e1:12:79:c1:1c:89:32
+-----BEGIN CERTIFICATE-----
+MIIEAjCCAuqgAwIBAgIFORFFEJQwDQYJKoZIhvcNAQEFBQAwgYUxCzAJBgNVBAYT
+AkZSMQ8wDQYDVQQIEwZGcmFuY2UxDjAMBgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQ
+TS9TR0ROMQ4wDAYDVQQLEwVEQ1NTSTEOMAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG
+9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZyMB4XDTAyMTIxMzE0MjkyM1oXDTIw
+MTAxNzE0MjkyMlowgYUxCzAJBgNVBAYTAkZSMQ8wDQYDVQQIEwZGcmFuY2UxDjAM
+BgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVEQ1NTSTEO
+MAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2
+LmZyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsh/R0GLFMzvABIaI
+s9z4iPf930Pfeo2aSVz2TqrMHLmh6yeJ8kbpO0px1R2OLc/mratjUMdUC24SyZA2
+xtgv2pGqaMVy/hcKshd+ebUyiHDKcMCWSo7kVc0dJ5S/znIq7Fz5cyD+vfcuiWe4
+u0dzEvfRNWk68gq5rv9GQkaiv6GFGvm/5P9JhfejcIYyHF2fYPepraX/z9E0+X1b
+F8bc1g4oa8Ld8fUzaJ1O/Id8NhLWo4DoQw1VYZTqZDdH6nfK0LJYBcNdfrGoRpAx
+Vs5wKpayMLh35nnAvSk7/ZR3TL0gzUEl4C7HG7vupARB0l2tEmqKm0f7yd1GQOGd
+PDPQtQIDAQABo3cwdTAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBRjAVBgNV
+HSAEDjAMMAoGCCqBegF5AQEBMB0GA1UdDgQWBBSjBS8YYFDCiQrdKyFP/45OqDAx
+NjAfBgNVHSMEGDAWgBSjBS8YYFDCiQrdKyFP/45OqDAxNjANBgkqhkiG9w0BAQUF
+AAOCAQEABdwm2Pp3FURo/C9mOnTgXeQp/wYHE4RKq89toB9RlPhJy3Q2FLwV3duJ
+L92PoF189RLrn544pEfMs5bZvpwlqwN+Mw+VgQ39FuCIvjfwbF3QMZsyK10XZZOY
+YLxuj7GoPB7ZHPOpJkL5ZB3C55L29B5aqhlSXa/oovdgoPaN8In1buAKBQGVyYsg
+Crpa/JosPL3Dt8ldeCUFP1YUmwza+zpI/pdpXsoQhvdOlgQITeywvl3cO45Pwf2a
+NjSaTFR+FwNIlQgRHAdvhQh+XU3Endv7rs6y0bO4g2wdsrN58dhwmX7wEwLOXt1R
+0982gaEbeC9xs/FZTEYYKKuF0mBWWg==
+-----END CERTIFICATE-----
+
+# Issuer: O=SECOM Trust Systems CO.,LTD. OU=Security Communication EV RootCA1
+# Subject: O=SECOM Trust Systems CO.,LTD. OU=Security Communication EV RootCA1
+# Label: "Security Communication EV RootCA1"
+# Serial: 0
+# MD5 Fingerprint: 22:2d:a6:01:ea:7c:0a:f7:f0:6c:56:43:3f:77:76:d3
+# SHA1 Fingerprint: fe:b8:c4:32:dc:f9:76:9a:ce:ae:3d:d8:90:8f:fd:28:86:65:64:7d
+# SHA256 Fingerprint: a2:2d:ba:68:1e:97:37:6e:2d:39:7d:72:8a:ae:3a:9b:62:96:b9:fd:ba:60:bc:2e:11:f6:47:f2:c6:75:fb:37
+-----BEGIN CERTIFICATE-----
+MIIDfTCCAmWgAwIBAgIBADANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJKUDEl
+MCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEqMCgGA1UECxMh
+U2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBFViBSb290Q0ExMB4XDTA3MDYwNjAyMTIz
+MloXDTM3MDYwNjAyMTIzMlowYDELMAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09N
+IFRydXN0IFN5c3RlbXMgQ08uLExURC4xKjAoBgNVBAsTIVNlY3VyaXR5IENvbW11
+bmljYXRpb24gRVYgUm9vdENBMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBALx/7FebJOD+nLpCeamIivqA4PUHKUPqjgo0No0c+qe1OXj/l3X3L+SqawSE
+RMqm4miO/VVQYg+kcQ7OBzgtQoVQrTyWb4vVog7P3kmJPdZkLjjlHmy1V4qe70gO
+zXppFodEtZDkBp2uoQSXWHnvIEqCa4wiv+wfD+mEce3xDuS4GBPMVjZd0ZoeUWs5
+bmB2iDQL87PRsJ3KYeJkHcFGB7hj3R4zZbOOCVVSPbW9/wfrrWFVGCypaZhKqkDF
+MxRldAD5kd6vA0jFQFTcD4SQaCDFkpbcLuUCRarAX1T4bepJz11sS6/vmsJWXMY1
+VkJqMF/Cq/biPT+zyRGPMUzXn0kCAwEAAaNCMEAwHQYDVR0OBBYEFDVK9U2vP9eC
+OKyrcWUXdYydVZPmMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0G
+CSqGSIb3DQEBBQUAA4IBAQCoh+ns+EBnXcPBZsdAS5f8hxOQWsTvoMpfi7ent/HW
+tWS3irO4G8za+6xmiEHO6Pzk2x6Ipu0nUBsCMCRGef4Eh3CXQHPRwMFXGZpppSeZ
+q51ihPZRwSzJIxXYKLerJRO1RuGGAv8mjMSIkh1W/hln8lXkgKNrnKt34VFxDSDb
+EJrbvXZ5B3eZKK2aXtqxT0QsNY6llsf9g/BYxnnWmHyojf6GPgcWkuF75x3sM3Z+
+Qi5KhfmRiWiEA4Glm5q+4zfFVKtWOxgtQaQM+ELbmaDgcm+7XeEWT1MKZPlO9L9O
+VL14bIjqv5wTJMJwaaJ/D8g8rQjJsJhAoyrniIPtd490
+-----END CERTIFICATE-----
+
+# Issuer: CN=OISTE WISeKey Global Root GA CA O=WISeKey OU=Copyright (c) 2005/OISTE Foundation Endorsed
+# Subject: CN=OISTE WISeKey Global Root GA CA O=WISeKey OU=Copyright (c) 2005/OISTE Foundation Endorsed
+# Label: "OISTE WISeKey Global Root GA CA"
+# Serial: 86718877871133159090080555911823548314
+# MD5 Fingerprint: bc:6c:51:33:a7:e9:d3:66:63:54:15:72:1b:21:92:93
+# SHA1 Fingerprint: 59:22:a1:e1:5a:ea:16:35:21:f8:98:39:6a:46:46:b0:44:1b:0f:a9
+# SHA256 Fingerprint: 41:c9:23:86:6a:b4:ca:d6:b7:ad:57:80:81:58:2e:02:07:97:a6:cb:df:4f:ff:78:ce:83:96:b3:89:37:d7:f5
+-----BEGIN CERTIFICATE-----
+MIID8TCCAtmgAwIBAgIQQT1yx/RrH4FDffHSKFTfmjANBgkqhkiG9w0BAQUFADCB
+ijELMAkGA1UEBhMCQ0gxEDAOBgNVBAoTB1dJU2VLZXkxGzAZBgNVBAsTEkNvcHly
+aWdodCAoYykgMjAwNTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNl
+ZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQSBDQTAeFw0w
+NTEyMTExNjAzNDRaFw0zNzEyMTExNjA5NTFaMIGKMQswCQYDVQQGEwJDSDEQMA4G
+A1UEChMHV0lTZUtleTEbMBkGA1UECxMSQ29weXJpZ2h0IChjKSAyMDA1MSIwIAYD
+VQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBX
+SVNlS2V5IEdsb2JhbCBSb290IEdBIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAy0+zAJs9Nt350UlqaxBJH+zYK7LG+DKBKUOVTJoZIyEVRd7jyBxR
+VVuuk+g3/ytr6dTqvirdqFEr12bDYVxgAsj1znJ7O7jyTmUIms2kahnBAbtzptf2
+w93NvKSLtZlhuAGio9RN1AU9ka34tAhxZK9w8RxrfvbDd50kc3vkDIzh2TbhmYsF
+mQvtRTEJysIA2/dyoJaqlYfQjse2YXMNdmaM3Bu0Y6Kff5MTMPGhJ9vZ/yxViJGg
+4E8HsChWjBgbl0SOid3gF27nKu+POQoxhILYQBRJLnpB5Kf+42TMwVlxSywhp1t9
+4B3RLoGbw9ho972WG6xwsRYUC9tguSYBBQIDAQABo1EwTzALBgNVHQ8EBAMCAYYw
+DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUswN+rja8sHnR3JQmthG+IbJphpQw
+EAYJKwYBBAGCNxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBAEuh/wuHbrP5wUOx
+SPMowB0uyQlB+pQAHKSkq0lPjz0e701vvbyk9vImMMkQyh2I+3QZH4VFvbBsUfk2
+ftv1TDI6QU9bR8/oCy22xBmddMVHxjtqD6wU2zz0c5ypBd8A3HR4+vg1YFkCExh8
+vPtNsCBtQ7tgMHpnM1zFmdH4LTlSc/uMqpclXHLZCB6rTjzjgTGfA6b7wP4piFXa
+hNVQA7bihKOmNqoROgHhGEvWRGizPflTdISzRpFGlgC3gCy24eMQ4tui5yiPAZZi
+Fj4A4xylNoEYokxSdsARo27mHbrjWr42U8U+dY+GaSlYU7Wcu2+fXMUY7N0v4ZjJ
+/L7fCg0=
+-----END CERTIFICATE-----
+
+# Issuer: CN=Microsec e-Szigno Root CA O=Microsec Ltd. OU=e-Szigno CA
+# Subject: CN=Microsec e-Szigno Root CA O=Microsec Ltd. OU=e-Szigno CA
+# Label: "Microsec e-Szigno Root CA"
+# Serial: 272122594155480254301341951808045322001
+# MD5 Fingerprint: f0:96:b6:2f:c5:10:d5:67:8e:83:25:32:e8:5e:2e:e5
+# SHA1 Fingerprint: 23:88:c9:d3:71:cc:9e:96:3d:ff:7d:3c:a7:ce:fc:d6:25:ec:19:0d
+# SHA256 Fingerprint: 32:7a:3d:76:1a:ba:de:a0:34:eb:99:84:06:27:5c:b1:a4:77:6e:fd:ae:2f:df:6d:01:68:ea:1c:4f:55:67:d0
+-----BEGIN CERTIFICATE-----
+MIIHqDCCBpCgAwIBAgIRAMy4579OKRr9otxmpRwsDxEwDQYJKoZIhvcNAQEFBQAw
+cjELMAkGA1UEBhMCSFUxETAPBgNVBAcTCEJ1ZGFwZXN0MRYwFAYDVQQKEw1NaWNy
+b3NlYyBMdGQuMRQwEgYDVQQLEwtlLVN6aWdubyBDQTEiMCAGA1UEAxMZTWljcm9z
+ZWMgZS1Temlnbm8gUm9vdCBDQTAeFw0wNTA0MDYxMjI4NDRaFw0xNzA0MDYxMjI4
+NDRaMHIxCzAJBgNVBAYTAkhVMREwDwYDVQQHEwhCdWRhcGVzdDEWMBQGA1UEChMN
+TWljcm9zZWMgTHRkLjEUMBIGA1UECxMLZS1Temlnbm8gQ0ExIjAgBgNVBAMTGU1p
+Y3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
+ggEKAoIBAQDtyADVgXvNOABHzNuEwSFpLHSQDCHZU4ftPkNEU6+r+ICbPHiN1I2u
+uO/TEdyB5s87lozWbxXGd36hL+BfkrYn13aaHUM86tnsL+4582pnS4uCzyL4ZVX+
+LMsvfUh6PXX5qqAnu3jCBspRwn5mS6/NoqdNAoI/gqyFxuEPkEeZlApxcpMqyabA
+vjxWTHOSJ/FrtfX9/DAFYJLG65Z+AZHCabEeHXtTRbjcQR/Ji3HWVBTji1R4P770
+Yjtb9aPs1ZJ04nQw7wHb4dSrmZsqa/i9phyGI0Jf7Enemotb9HI6QMVJPqW+jqpx
+62z69Rrkav17fVVA71hu5tnVvCSrwe+3AgMBAAGjggQ3MIIEMzBnBggrBgEFBQcB
+AQRbMFkwKAYIKwYBBQUHMAGGHGh0dHBzOi8vcmNhLmUtc3ppZ25vLmh1L29jc3Aw
+LQYIKwYBBQUHMAKGIWh0dHA6Ly93d3cuZS1zemlnbm8uaHUvUm9vdENBLmNydDAP
+BgNVHRMBAf8EBTADAQH/MIIBcwYDVR0gBIIBajCCAWYwggFiBgwrBgEEAYGoGAIB
+AQEwggFQMCgGCCsGAQUFBwIBFhxodHRwOi8vd3d3LmUtc3ppZ25vLmh1L1NaU1ov
+MIIBIgYIKwYBBQUHAgIwggEUHoIBEABBACAAdABhAG4A+gBzAO0AdAB2AOEAbgB5
+ACAA6QByAHQAZQBsAG0AZQB6AOkAcwDpAGgAZQB6ACAA6QBzACAAZQBsAGYAbwBn
+AGEAZADhAHMA4QBoAG8AegAgAGEAIABTAHoAbwBsAGcA4QBsAHQAYQB0APMAIABT
+AHoAbwBsAGcA4QBsAHQAYQB0AOEAcwBpACAAUwB6AGEAYgDhAGwAeQB6AGEAdABh
+ACAAcwB6AGUAcgBpAG4AdAAgAGsAZQBsAGwAIABlAGwAagDhAHIAbgBpADoAIABo
+AHQAdABwADoALwAvAHcAdwB3AC4AZQAtAHMAegBpAGcAbgBvAC4AaAB1AC8AUwBa
+AFMAWgAvMIHIBgNVHR8EgcAwgb0wgbqggbeggbSGIWh0dHA6Ly93d3cuZS1zemln
+bm8uaHUvUm9vdENBLmNybIaBjmxkYXA6Ly9sZGFwLmUtc3ppZ25vLmh1L0NOPU1p
+Y3Jvc2VjJTIwZS1Temlnbm8lMjBSb290JTIwQ0EsT1U9ZS1Temlnbm8lMjBDQSxP
+PU1pY3Jvc2VjJTIwTHRkLixMPUJ1ZGFwZXN0LEM9SFU/Y2VydGlmaWNhdGVSZXZv
+Y2F0aW9uTGlzdDtiaW5hcnkwDgYDVR0PAQH/BAQDAgEGMIGWBgNVHREEgY4wgYuB
+EGluZm9AZS1zemlnbm8uaHWkdzB1MSMwIQYDVQQDDBpNaWNyb3NlYyBlLVN6aWdu
+w7MgUm9vdCBDQTEWMBQGA1UECwwNZS1TemlnbsOzIEhTWjEWMBQGA1UEChMNTWlj
+cm9zZWMgS2Z0LjERMA8GA1UEBxMIQnVkYXBlc3QxCzAJBgNVBAYTAkhVMIGsBgNV
+HSMEgaQwgaGAFMegSXUWYYTbMUuE0vE3QJDvTtz3oXakdDByMQswCQYDVQQGEwJI
+VTERMA8GA1UEBxMIQnVkYXBlc3QxFjAUBgNVBAoTDU1pY3Jvc2VjIEx0ZC4xFDAS
+BgNVBAsTC2UtU3ppZ25vIENBMSIwIAYDVQQDExlNaWNyb3NlYyBlLVN6aWdubyBS
+b290IENBghEAzLjnv04pGv2i3GalHCwPETAdBgNVHQ4EFgQUx6BJdRZhhNsxS4TS
+8TdAkO9O3PcwDQYJKoZIhvcNAQEFBQADggEBANMTnGZjWS7KXHAM/IO8VbH0jgds
+ZifOwTsgqRy7RlRw7lrMoHfqaEQn6/Ip3Xep1fvj1KcExJW4C+FEaGAHQzAxQmHl
+7tnlJNUb3+FKG6qfx1/4ehHqE5MAyopYse7tDk2016g2JnzgOsHVV4Lxdbb9iV/a
+86g4nzUGCM4ilb7N1fy+W955a9x6qWVmvrElWl/tftOsRm1M9DKHtCAE4Gx4sHfR
+hUZLphK3dehKyVZs15KrnfVJONJPU+NVkBHbmJbGSfI+9J8b4PeI3CVimUTYc78/
+MPMMNz7UwiiAc7EBt51alhQBS6kRnSlqLtBdgcDPsiBDxwPgN05dCtxZICU=
+-----END CERTIFICATE-----
+
+# Issuer: CN=Certigna O=Dhimyotis
+# Subject: CN=Certigna O=Dhimyotis
+# Label: "Certigna"
+# Serial: 18364802974209362175
+# MD5 Fingerprint: ab:57:a6:5b:7d:42:82:19:b5:d8:58:26:28:5e:fd:ff
+# SHA1 Fingerprint: b1:2e:13:63:45:86:a4:6f:1a:b2:60:68:37:58:2d:c4:ac:fd:94:97
+# SHA256 Fingerprint: e3:b6:a2:db:2e:d7:ce:48:84:2f:7a:c5:32:41:c7:b7:1d:54:14:4b:fb:40:c1:1f:3f:1d:0b:42:f5:ee:a1:2d
+-----BEGIN CERTIFICATE-----
+MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNV
+BAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4X
+DTA3MDYyOTE1MTMwNVoXDTI3MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQ
+BgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwIQ2VydGlnbmEwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7qXOEm7RFHYeGifBZ4
+QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyHGxny
+gQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbw
+zBfsV1/pogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q
+130yGLMLLGq/jj8UEYkgDncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2
+JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKfIrjxwo1p3Po6WAbfAgMBAAGjgbwwgbkw
+DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQtCRZvgHyUtVF9lo53BEw
+ZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJBgNVBAYT
+AkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzj
+AQ/JSP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG
+9w0BAQUFAAOCAQEAhQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8h
+bV6lUmPOEvjvKtpv6zf+EwLHyzs+ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFnc
+fca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1kluPBS1xp81HlDQwY9qcEQCYsuu
+HWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY1gkIl2PlwS6w
+t0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw
+WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg==
+-----END CERTIFICATE-----
+
+# Issuer: CN=AC Raíz Certicámara S.A. O=Sociedad Cameral de Certificación Digital - Certicámara S.A.
+# Subject: CN=AC Raíz Certicámara S.A. O=Sociedad Cameral de Certificación Digital - Certicámara S.A.
+# Label: "AC Ra\xC3\xADz Certic\xC3\xA1mara S.A."
+# Serial: 38908203973182606954752843738508300
+# MD5 Fingerprint: 93:2a:3e:f6:fd:23:69:0d:71:20:d4:2b:47:99:2b:a6
+# SHA1 Fingerprint: cb:a1:c5:f8:b0:e3:5e:b8:b9:45:12:d3:f9:34:a2:e9:06:10:d3:36
+# SHA256 Fingerprint: a6:c5:1e:0d:a5:ca:0a:93:09:d2:e4:c0:e4:0c:2a:f9:10:7a:ae:82:03:85:7f:e1:98:e3:e7:69:e3:43:08:5c
+-----BEGIN CERTIFICATE-----
+MIIGZjCCBE6gAwIBAgIPB35Sk3vgFeNX8GmMy+wMMA0GCSqGSIb3DQEBBQUAMHsx
+CzAJBgNVBAYTAkNPMUcwRQYDVQQKDD5Tb2NpZWRhZCBDYW1lcmFsIGRlIENlcnRp
+ZmljYWNpw7NuIERpZ2l0YWwgLSBDZXJ0aWPDoW1hcmEgUy5BLjEjMCEGA1UEAwwa
+QUMgUmHDrXogQ2VydGljw6FtYXJhIFMuQS4wHhcNMDYxMTI3MjA0NjI5WhcNMzAw
+NDAyMjE0MjAyWjB7MQswCQYDVQQGEwJDTzFHMEUGA1UECgw+U29jaWVkYWQgQ2Ft
+ZXJhbCBkZSBDZXJ0aWZpY2FjacOzbiBEaWdpdGFsIC0gQ2VydGljw6FtYXJhIFMu
+QS4xIzAhBgNVBAMMGkFDIFJhw616IENlcnRpY8OhbWFyYSBTLkEuMIICIjANBgkq
+hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq2uJo1PMSCMI+8PPUZYILrgIem08kBeG
+qentLhM0R7LQcNzJPNCNyu5LF6vQhbCnIwTLqKL85XXbQMpiiY9QngE9JlsYhBzL
+fDe3fezTf3MZsGqy2IiKLUV0qPezuMDU2s0iiXRNWhU5cxh0T7XrmafBHoi0wpOQ
+Y5fzp6cSsgkiBzPZkc0OnB8OIMfuuzONj8LSWKdf/WU34ojC2I+GdV75LaeHM/J4
+Ny+LvB2GNzmxlPLYvEqcgxhaBvzz1NS6jBUJJfD5to0EfhcSM2tXSExP2yYe68yQ
+54v5aHxwD6Mq0Do43zeX4lvegGHTgNiRg0JaTASJaBE8rF9ogEHMYELODVoqDA+b
+MMCm8Ibbq0nXl21Ii/kDwFJnmxL3wvIumGVC2daa49AZMQyth9VXAnow6IYm+48j
+ilSH5L887uvDdUhfHjlvgWJsxS3EF1QZtzeNnDeRyPYL1epjb4OsOMLzP96a++Ej
+YfDIJss2yKHzMI+ko6Kh3VOz3vCaMh+DkXkwwakfU5tTohVTP92dsxA7SH2JD/zt
+A/X7JWR1DhcZDY8AFmd5ekD8LVkH2ZD6mq093ICK5lw1omdMEWux+IBkAC1vImHF
+rEsm5VoQgpukg3s0956JkSCXjrdCx2bD0Omk1vUgjcTDlaxECp1bczwmPS9KvqfJ
+pxAe+59QafMCAwEAAaOB5jCB4zAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE
+AwIBBjAdBgNVHQ4EFgQU0QnQ6dfOeXRU+Tows/RtLAMDG2gwgaAGA1UdIASBmDCB
+lTCBkgYEVR0gADCBiTArBggrBgEFBQcCARYfaHR0cDovL3d3dy5jZXJ0aWNhbWFy
+YS5jb20vZHBjLzBaBggrBgEFBQcCAjBOGkxMaW1pdGFjaW9uZXMgZGUgZ2FyYW50
+7WFzIGRlIGVzdGUgY2VydGlmaWNhZG8gc2UgcHVlZGVuIGVuY29udHJhciBlbiBs
+YSBEUEMuMA0GCSqGSIb3DQEBBQUAA4ICAQBclLW4RZFNjmEfAygPU3zmpFmps4p6
+xbD/CHwso3EcIRNnoZUSQDWDg4902zNc8El2CoFS3UnUmjIz75uny3XlesuXEpBc
+unvFm9+7OSPI/5jOCk0iAUgHforA1SBClETvv3eiiWdIG0ADBaGJ7M9i4z0ldma/
+Jre7Ir5v/zlXdLp6yQGVwZVR6Kss+LGGIOk/yzVb0hfpKv6DExdA7ohiZVvVO2Dp
+ezy4ydV/NgIlqmjCMRW3MGXrfx1IebHPOeJCgBbT9ZMj/EyXyVo3bHwi2ErN0o42
+gzmRkBDI8ck1fj+404HGIGQatlDCIaR43NAvO2STdPCWkPHv+wlaNECW8DYSwaN0
+jJN+Qd53i+yG2dIPPy3RzECiiWZIHiCznCNZc6lEc7wkeZBWN7PGKX6jD/EpOe9+
+XCgycDWs2rjIdWb8m0w5R44bb5tNAlQiM+9hup4phO9OSzNHdpdqy35f/RWmnkJD
+W2ZaiogN9xa5P1FlK2Zqi9E4UqLWRhH6/JocdJ6PlwsCT2TG9WjTSy3/pDceiz+/
+RL5hRqGEPQgnTIEgd4kI6mdAXmwIUV80WoyWaM3X94nCHNMyAK9Sy9NgWyo6R35r
+MDOhYil/SrnhLecUIw4OGEfhefwVVdCx/CVxY3UzHCMrr1zZ7Ud3YA47Dx7SwNxk
+BYn8eNZcLCZDqQ==
+-----END CERTIFICATE-----
+
+# Issuer: CN=TC TrustCenter Class 2 CA II O=TC TrustCenter GmbH OU=TC TrustCenter Class 2 CA
+# Subject: CN=TC TrustCenter Class 2 CA II O=TC TrustCenter GmbH OU=TC TrustCenter Class 2 CA
+# Label: "TC TrustCenter Class 2 CA II"
+# Serial: 941389028203453866782103406992443
+# MD5 Fingerprint: ce:78:33:5c:59:78:01:6e:18:ea:b9:36:a0:b9:2e:23
+# SHA1 Fingerprint: ae:50:83:ed:7c:f4:5c:bc:8f:61:c6:21:fe:68:5d:79:42:21:15:6e
+# SHA256 Fingerprint: e6:b8:f8:76:64:85:f8:07:ae:7f:8d:ac:16:70:46:1f:07:c0:a1:3e:ef:3a:1f:f7:17:53:8d:7a:ba:d3:91:b4
+-----BEGIN CERTIFICATE-----
+MIIEqjCCA5KgAwIBAgIOLmoAAQACH9dSISwRXDswDQYJKoZIhvcNAQEFBQAwdjEL
+MAkGA1UEBhMCREUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNV
+BAsTGVRDIFRydXN0Q2VudGVyIENsYXNzIDIgQ0ExJTAjBgNVBAMTHFRDIFRydXN0
+Q2VudGVyIENsYXNzIDIgQ0EgSUkwHhcNMDYwMTEyMTQzODQzWhcNMjUxMjMxMjI1
+OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1c3RDZW50ZXIgR21i
+SDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQTElMCMGA1UEAxMc
+VEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBAKuAh5uO8MN8h9foJIIRszzdQ2Lu+MNF2ujhoF/RKrLqk2jf
+tMjWQ+nEdVl//OEd+DFwIxuInie5e/060smp6RQvkL4DUsFJzfb95AhmC1eKokKg
+uNV/aVyQMrKXDcpK3EY+AlWJU+MaWss2xgdW94zPEfRMuzBwBJWl9jmM/XOBCH2J
+XjIeIqkiRUuwZi4wzJ9l/fzLganx4Duvo4bRierERXlQXa7pIXSSTYtZgo+U4+lK
+8edJsBTj9WLL1XK9H7nSn6DNqPoByNkN39r8R52zyFTfSUrxIan+GE7uSNQZu+99
+5OKdy1u2bv/jzVrndIIFuoAlOMvkaZ6vQaoahPUCAwEAAaOCATQwggEwMA8GA1Ud
+EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTjq1RMgKHbVkO3
+kUrL84J6E1wIqzCB7QYDVR0fBIHlMIHiMIHfoIHcoIHZhjVodHRwOi8vd3d3LnRy
+dXN0Y2VudGVyLmRlL2NybC92Mi90Y19jbGFzc18yX2NhX0lJLmNybIaBn2xkYXA6
+Ly93d3cudHJ1c3RjZW50ZXIuZGUvQ049VEMlMjBUcnVzdENlbnRlciUyMENsYXNz
+JTIwMiUyMENBJTIwSUksTz1UQyUyMFRydXN0Q2VudGVyJTIwR21iSCxPVT1yb290
+Y2VydHMsREM9dHJ1c3RjZW50ZXIsREM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9u
+TGlzdD9iYXNlPzANBgkqhkiG9w0BAQUFAAOCAQEAjNfffu4bgBCzg/XbEeprS6iS
+GNn3Bzn1LL4GdXpoUxUc6krtXvwjshOg0wn/9vYua0Fxec3ibf2uWWuFHbhOIprt
+ZjluS5TmVfwLG4t3wVMTZonZKNaL80VKY7f9ewthXbhtvsPcW3nS7Yblok2+XnR8
+au0WOB9/WIFaGusyiC2y8zl3gK9etmF1KdsjTYjKUCjLhdLTEKJZbtOTVAB6okaV
+hgWcqRmY5TFyDADiZ9lA4CQze28suVyrZZ0srHbqNZn1l7kPJOzHdiEoZa5X6AeI
+dUpWoNIFOqTmjZKILPPy4cHGYdtBxceb9w4aUUXCYWvcZCcXjFq32nQozZfkvQ==
+-----END CERTIFICATE-----
+
+# Issuer: CN=TC TrustCenter Class 3 CA II O=TC TrustCenter GmbH OU=TC TrustCenter Class 3 CA
+# Subject: CN=TC TrustCenter Class 3 CA II O=TC TrustCenter GmbH OU=TC TrustCenter Class 3 CA
+# Label: "TC TrustCenter Class 3 CA II"
+# Serial: 1506523511417715638772220530020799
+# MD5 Fingerprint: 56:5f:aa:80:61:12:17:f6:67:21:e6:2b:6d:61:56:8e
+# SHA1 Fingerprint: 80:25:ef:f4:6e:70:c8:d4:72:24:65:84:fe:40:3b:8a:8d:6a:db:f5
+# SHA256 Fingerprint: 8d:a0:84:fc:f9:9c:e0:77:22:f8:9b:32:05:93:98:06:fa:5c:b8:11:e1:c8:13:f6:a1:08:c7:d3:36:b3:40:8e
+-----BEGIN CERTIFICATE-----
+MIIEqjCCA5KgAwIBAgIOSkcAAQAC5aBd1j8AUb8wDQYJKoZIhvcNAQEFBQAwdjEL
+MAkGA1UEBhMCREUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNV
+BAsTGVRDIFRydXN0Q2VudGVyIENsYXNzIDMgQ0ExJTAjBgNVBAMTHFRDIFRydXN0
+Q2VudGVyIENsYXNzIDMgQ0EgSUkwHhcNMDYwMTEyMTQ0MTU3WhcNMjUxMjMxMjI1
+OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1c3RDZW50ZXIgR21i
+SDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQTElMCMGA1UEAxMc
+VEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBALTgu1G7OVyLBMVMeRwjhjEQY0NVJz/GRcekPewJDRoeIMJW
+Ht4bNwcwIi9v8Qbxq63WyKthoy9DxLCyLfzDlml7forkzMA5EpBCYMnMNWju2l+Q
+Vl/NHE1bWEnrDgFPZPosPIlY2C8u4rBo6SI7dYnWRBpl8huXJh0obazovVkdKyT2
+1oQDZogkAHhg8fir/gKya/si+zXmFtGt9i4S5Po1auUZuV3bOx4a+9P/FRQI2Alq
+ukWdFHlgfa9Aigdzs5OW03Q0jTo3Kd5c7PXuLjHCINy+8U9/I1LZW+Jk2ZyqBwi1
+Rb3R0DHBq1SfqdLDYmAD8bs5SpJKPQq5ncWg/jcCAwEAAaOCATQwggEwMA8GA1Ud
+EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTUovyfs8PYA9NX
+XAek0CSnwPIA1DCB7QYDVR0fBIHlMIHiMIHfoIHcoIHZhjVodHRwOi8vd3d3LnRy
+dXN0Y2VudGVyLmRlL2NybC92Mi90Y19jbGFzc18zX2NhX0lJLmNybIaBn2xkYXA6
+Ly93d3cudHJ1c3RjZW50ZXIuZGUvQ049VEMlMjBUcnVzdENlbnRlciUyMENsYXNz
+JTIwMyUyMENBJTIwSUksTz1UQyUyMFRydXN0Q2VudGVyJTIwR21iSCxPVT1yb290
+Y2VydHMsREM9dHJ1c3RjZW50ZXIsREM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9u
+TGlzdD9iYXNlPzANBgkqhkiG9w0BAQUFAAOCAQEANmDkcPcGIEPZIxpC8vijsrlN
+irTzwppVMXzEO2eatN9NDoqTSheLG43KieHPOh6sHfGcMrSOWXaiQYUlN6AT0PV8
+TtXqluJucsG7Kv5sbviRmEb8yRtXW+rIGjs/sFGYPAfaLFkB2otE6OF0/ado3VS6
+g0bsyEa1+K+XwDsJHI/OcpY9M1ZwvJbL2NV9IJqDnxrcOfHFcqMRA/07QlIp2+gB
+95tejNaNhk4Z+rwcvsUhpYeeeC422wlxo3I0+GzjBgnyXlal092Y+tTmBvTwtiBj
+S+opvaqCZh77gaqnN60TGOaSw4HBM7uIHqHn4rS9MWwOUT1v+5ZWgOI2F9Hc5A==
+-----END CERTIFICATE-----
+
+# Issuer: CN=TC TrustCenter Universal CA I O=TC TrustCenter GmbH OU=TC TrustCenter Universal CA
+# Subject: CN=TC TrustCenter Universal CA I O=TC TrustCenter GmbH OU=TC TrustCenter Universal CA
+# Label: "TC TrustCenter Universal CA I"
+# Serial: 601024842042189035295619584734726
+# MD5 Fingerprint: 45:e1:a5:72:c5:a9:36:64:40:9e:f5:e4:58:84:67:8c
+# SHA1 Fingerprint: 6b:2f:34:ad:89:58:be:62:fd:b0:6b:5c:ce:bb:9d:d9:4f:4e:39:f3
+# SHA256 Fingerprint: eb:f3:c0:2a:87:89:b1:fb:7d:51:19:95:d6:63:b7:29:06:d9:13:ce:0d:5e:10:56:8a:8a:77:e2:58:61:67:e7
+-----BEGIN CERTIFICATE-----
+MIID3TCCAsWgAwIBAgIOHaIAAQAC7LdggHiNtgYwDQYJKoZIhvcNAQEFBQAweTEL
+MAkGA1UEBhMCREUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxJDAiBgNV
+BAsTG1RDIFRydXN0Q2VudGVyIFVuaXZlcnNhbCBDQTEmMCQGA1UEAxMdVEMgVHJ1
+c3RDZW50ZXIgVW5pdmVyc2FsIENBIEkwHhcNMDYwMzIyMTU1NDI4WhcNMjUxMjMx
+MjI1OTU5WjB5MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1c3RDZW50ZXIg
+R21iSDEkMCIGA1UECxMbVEMgVHJ1c3RDZW50ZXIgVW5pdmVyc2FsIENBMSYwJAYD
+VQQDEx1UQyBUcnVzdENlbnRlciBVbml2ZXJzYWwgQ0EgSTCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBAKR3I5ZEr5D0MacQ9CaHnPM42Q9e3s9B6DGtxnSR
+JJZ4Hgmgm5qVSkr1YnwCqMqs+1oEdjneX/H5s7/zA1hV0qq34wQi0fiU2iIIAI3T
+fCZdzHd55yx4Oagmcw6iXSVphU9VDprvxrlE4Vc93x9UIuVvZaozhDrzznq+VZeu
+jRIPFDPiUHDDSYcTvFHe15gSWu86gzOSBnWLknwSaHtwag+1m7Z3W0hZneTvWq3z
+wZ7U10VOylY0Ibw+F1tvdwxIAUMpsN0/lm7mlaoMwCC2/T42J5zjXM9OgdwZu5GQ
+fezmlwQek8wiSdeXhrYTCjxDI3d+8NzmzSQfO4ObNDqDNOMCAwEAAaNjMGEwHwYD
+VR0jBBgwFoAUkqR1LKSevoFE63n8isWVpesQdXMwDwYDVR0TAQH/BAUwAwEB/zAO
+BgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFJKkdSyknr6BROt5/IrFlaXrEHVzMA0G
+CSqGSIb3DQEBBQUAA4IBAQAo0uCG1eb4e/CX3CJrO5UUVg8RMKWaTzqwOuAGy2X1
+7caXJ/4l8lfmXpWMPmRgFVp/Lw0BxbFg/UU1z/CyvwbZ71q+s2IhtNerNXxTPqYn
+8aEt2hojnczd7Dwtnic0XQ/CNnm8yUpiLe1r2X1BQ3y2qsrtYbE3ghUJGooWMNjs
+ydZHcnhLEEYUjl8Or+zHL6sQ17bxbuyGssLoDZJz3KL0Dzq/YSMQiZxIQG5wALPT
+ujdEWBF6AmqI8Dc08BnprNRlc/ZpjGSUOnmFKbAWKwyCPwacx/0QK54PLLae4xW/
+2TYcuiUaUj0a7CIMHOCkoj3w6DnPgcB77V0fb8XQC9eY
+-----END CERTIFICATE-----
+
+# Issuer: CN=Deutsche Telekom Root CA 2 O=Deutsche Telekom AG OU=T-TeleSec Trust Center
+# Subject: CN=Deutsche Telekom Root CA 2 O=Deutsche Telekom AG OU=T-TeleSec Trust Center
+# Label: "Deutsche Telekom Root CA 2"
+# Serial: 38
+# MD5 Fingerprint: 74:01:4a:91:b1:08:c4:58:ce:47:cd:f0:dd:11:53:08
+# SHA1 Fingerprint: 85:a4:08:c0:9c:19:3e:5d:51:58:7d:cd:d6:13:30:fd:8c:de:37:bf
+# SHA256 Fingerprint: b6:19:1a:50:d0:c3:97:7f:7d:a9:9b:cd:aa:c8:6a:22:7d:ae:b9:67:9e:c7:0b:a3:b0:c9:d9:22:71:c1:70:d3
+-----BEGIN CERTIFICATE-----
+MIIDnzCCAoegAwIBAgIBJjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJERTEc
+MBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2Vj
+IFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENB
+IDIwHhcNOTkwNzA5MTIxMTAwWhcNMTkwNzA5MjM1OTAwWjBxMQswCQYDVQQGEwJE
+RTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxl
+U2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290
+IENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrC6M14IspFLEU
+ha88EOQ5bzVdSq7d6mGNlUn0b2SjGmBmpKlAIoTZ1KXleJMOaAGtuU1cOs7TuKhC
+QN/Po7qCWWqSG6wcmtoIKyUn+WkjR/Hg6yx6m/UTAtB+NHzCnjwAWav12gz1Mjwr
+rFDa1sPeg5TKqAyZMg4ISFZbavva4VhYAUlfckE8FQYBjl2tqriTtM2e66foai1S
+NNs671x1Udrb8zH57nGYMsRUFUQM+ZtV7a3fGAigo4aKSe5TBY8ZTNXeWHmb0moc
+QqvF1afPaA+W5OFhmHZhyJF81j4A4pFQh+GdCuatl9Idxjp9y7zaAzTVjlsB9WoH
+txa2bkp/AgMBAAGjQjBAMB0GA1UdDgQWBBQxw3kbuvVT1xfgiXotF2wKsyudMzAP
+BgNVHRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOC
+AQEAlGRZrTlk5ynrE/5aw4sTV8gEJPB0d8Bg42f76Ymmg7+Wgnxu1MM9756Abrsp
+tJh6sTtU6zkXR34ajgv8HzFZMQSyzhfzLMdiNlXiItiJVbSYSKpk+tYcNthEeFpa
+IzpXl/V6ME+un2pMSyuOoAPjPuCp1NJ70rOo4nI8rZ7/gFnkm0W09juwzTkZmDLl
+6iFhkOQxIY40sfcvNUqFENrnijchvllj4PKFiDFT1FQUhXB59C4Gdyd1Lx+4ivn+
+xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU
+Cm26OWMohpLzGITY+9HPBVZkVw==
+-----END CERTIFICATE-----
+
+# Issuer: CN=ComSign Secured CA O=ComSign
+# Subject: CN=ComSign Secured CA O=ComSign
+# Label: "ComSign Secured CA"
+# Serial: 264725503855295744117309814499492384489
+# MD5 Fingerprint: 40:01:25:06:8d:21:43:6a:0e:43:00:9c:e7:43:f3:d5
+# SHA1 Fingerprint: f9:cd:0e:2c:da:76:24:c1:8f:bd:f0:f0:ab:b6:45:b8:f7:fe:d5:7a
+# SHA256 Fingerprint: 50:79:41:c7:44:60:a0:b4:70:86:22:0d:4e:99:32:57:2a:b5:d1:b5:bb:cb:89:80:ab:1c:b1:76:51:a8:44:d2
+-----BEGIN CERTIFICATE-----
+MIIDqzCCApOgAwIBAgIRAMcoRwmzuGxFjB36JPU2TukwDQYJKoZIhvcNAQEFBQAw
+PDEbMBkGA1UEAxMSQ29tU2lnbiBTZWN1cmVkIENBMRAwDgYDVQQKEwdDb21TaWdu
+MQswCQYDVQQGEwJJTDAeFw0wNDAzMjQxMTM3MjBaFw0yOTAzMTYxNTA0NTZaMDwx
+GzAZBgNVBAMTEkNvbVNpZ24gU2VjdXJlZCBDQTEQMA4GA1UEChMHQ29tU2lnbjEL
+MAkGA1UEBhMCSUwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGtWhf
+HZQVw6QIVS3joFd67+l0Kru5fFdJGhFeTymHDEjWaueP1H5XJLkGieQcPOqs49oh
+gHMhCu95mGwfCP+hUH3ymBvJVG8+pSjsIQQPRbsHPaHA+iqYHU4Gk/v1iDurX8sW
+v+bznkqH7Rnqwp9D5PGBpX8QTz7RSmKtUxvLg/8HZaWSLWapW7ha9B20IZFKF3ue
+Mv5WJDmyVIRD9YTC2LxBkMyd1mja6YJQqTtoz7VdApRgFrFD2UNd3V2Hbuq7s8lr
+9gOUCXDeFhF6K+h2j0kQmHe5Y1yLM5d19guMsqtb3nQgJT/j8xH5h2iGNXHDHYwt
+6+UarA9z1YJZQIDTAgMBAAGjgacwgaQwDAYDVR0TBAUwAwEB/zBEBgNVHR8EPTA7
+MDmgN6A1hjNodHRwOi8vZmVkaXIuY29tc2lnbi5jby5pbC9jcmwvQ29tU2lnblNl
+Y3VyZWRDQS5jcmwwDgYDVR0PAQH/BAQDAgGGMB8GA1UdIwQYMBaAFMFL7XC29z58
+ADsAj8c+DkWfHl3sMB0GA1UdDgQWBBTBS+1wtvc+fAA7AI/HPg5Fnx5d7DANBgkq
+hkiG9w0BAQUFAAOCAQEAFs/ukhNQq3sUnjO2QiBq1BW9Cav8cujvR3qQrFHBZE7p
+iL1DRYHjZiM/EoZNGeQFsOY3wo3aBijJD4mkU6l1P7CW+6tMM1X5eCZGbxs2mPtC
+dsGCuY7e+0X5YxtiOzkGynd6qDwJz2w2PQ8KRUtpFhpFfTMDZflScZAmlaxMDPWL
+kz/MdXSFmLr/YnpNH4n+rr2UAJm/EaXc4HnFFgt9AmEd6oX5AhVP51qJThRv4zdL
+hfXBPGHg/QVBspJ/wx2g0K5SZGBrGMYmnNj1ZOQ2GmKfig8+/21OGVZOIJFsnzQz
+OjRXUDpvgV4GxvU+fE6OK85lBi5d0ipTdF7Tbieejw==
+-----END CERTIFICATE-----
+
+# Issuer: CN=Cybertrust Global Root O=Cybertrust, Inc
+# Subject: CN=Cybertrust Global Root O=Cybertrust, Inc
+# Label: "Cybertrust Global Root"
+# Serial: 4835703278459682877484360
+# MD5 Fingerprint: 72:e4:4a:87:e3:69:40:80:77:ea:bc:e3:f4:ff:f0:e1
+# SHA1 Fingerprint: 5f:43:e5:b1:bf:f8:78:8c:ac:1c:c7:ca:4a:9a:c6:22:2b:cc:34:c6
+# SHA256 Fingerprint: 96:0a:df:00:63:e9:63:56:75:0c:29:65:dd:0a:08:67:da:0b:9c:bd:6e:77:71:4a:ea:fb:23:49:ab:39:3d:a3
+-----BEGIN CERTIFICATE-----
+MIIDoTCCAomgAwIBAgILBAAAAAABD4WqLUgwDQYJKoZIhvcNAQEFBQAwOzEYMBYG
+A1UEChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2Jh
+bCBSb290MB4XDTA2MTIxNTA4MDAwMFoXDTIxMTIxNTA4MDAwMFowOzEYMBYGA1UE
+ChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2JhbCBS
+b290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+Mi8vRRQZhP/8NN5
+7CPytxrHjoXxEnOmGaoQ25yiZXRadz5RfVb23CO21O1fWLE3TdVJDm71aofW0ozS
+J8bi/zafmGWgE07GKmSb1ZASzxQG9Dvj1Ci+6A74q05IlG2OlTEQXO2iLb3VOm2y
+HLtgwEZLAfVJrn5GitB0jaEMAs7u/OePuGtm839EAL9mJRQr3RAwHQeWP032a7iP
+t3sMpTjr3kfb1V05/Iin89cqdPHoWqI7n1C6poxFNcJQZZXcY4Lv3b93TZxiyWNz
+FtApD0mpSPCzqrdsxacwOUBdrsTiXSZT8M4cIwhhqJQZugRiQOwfOHB3EgZxpzAY
+XSUnpQIDAQABo4GlMIGiMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/
+MB0GA1UdDgQWBBS2CHsNesysIEyGVjJez6tuhS1wVzA/BgNVHR8EODA2MDSgMqAw
+hi5odHRwOi8vd3d3Mi5wdWJsaWMtdHJ1c3QuY29tL2NybC9jdC9jdHJvb3QuY3Js
+MB8GA1UdIwQYMBaAFLYIew16zKwgTIZWMl7Pq26FLXBXMA0GCSqGSIb3DQEBBQUA
+A4IBAQBW7wojoFROlZfJ+InaRcHUowAl9B8Tq7ejhVhpwjCt2BWKLePJzYFa+HMj
+Wqd8BfP9IjsO0QbE2zZMcwSO5bAi5MXzLqXZI+O4Tkogp24CJJ8iYGd7ix1yCcUx
+XOl5n4BHPa2hCwcUPUf/A2kaDAtE52Mlp3+yybh2hO0j9n0Hq0V+09+zv+mKts2o
+omcrUtW3ZfA5TGOgkXmTUg9U3YO7n9GPp1Nzw8v/MOx8BLjYRB+TX3EJIrduPuoc
+A06dGiBh+4E37F78CkWr1+cXVdCg6mCbpvbjjFspwgZgFJ0tl0ypkxWdYcQBX0jW
+WL1WMRJOEcgh4LMRkWXbtKaIOM5V
+-----END CERTIFICATE-----
+
+# Issuer: O=Chunghwa Telecom Co., Ltd. OU=ePKI Root Certification Authority
+# Subject: O=Chunghwa Telecom Co., Ltd. OU=ePKI Root Certification Authority
+# Label: "ePKI Root Certification Authority"
+# Serial: 28956088682735189655030529057352760477
+# MD5 Fingerprint: 1b:2e:00:ca:26:06:90:3d:ad:fe:6f:15:68:d3:6b:b3
+# SHA1 Fingerprint: 67:65:0d:f1:7e:8e:7e:5b:82:40:a4:f4:56:4b:cf:e2:3d:69:c6:f0
+# SHA256 Fingerprint: c0:a6:f4:dc:63:a2:4b:fd:cf:54:ef:2a:6a:08:2a:0a:72:de:35:80:3e:2f:f5:ff:52:7a:e5:d8:72:06:df:d5
+-----BEGIN CERTIFICATE-----
+MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBe
+MQswCQYDVQQGEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0
+ZC4xKjAoBgNVBAsMIWVQS0kgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe
+Fw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMxMjdaMF4xCzAJBgNVBAYTAlRXMSMw
+IQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEqMCgGA1UECwwhZVBL
+SSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEF
+AAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAH
+SyZbCUNsIZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAh
+ijHyl3SJCRImHJ7K2RKilTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3X
+DZoTM1PRYfl61dd4s5oz9wCGzh1NlDivqOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1
+TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX12ruOzjjK9SXDrkb5wdJ
+fzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0OWQqraffA
+sgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uU
+WH1+ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLS
+nT0IFaUQAS2zMnaolQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pH
+dmX2Os+PYhcZewoozRrSgx4hxyy/vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJip
+NiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXiZo1jDiVN1Rmy5nk3pyKdVDEC
+AwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/QkqiMAwGA1UdEwQF
+MAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH
+ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGB
+uvl2ICO1J2B01GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6Yl
+PwZpVnPDimZI+ymBV3QGypzqKOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkP
+JXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdVxrsStZf0X4OFunHB2WyBEXYKCrC/
+gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEPNXubrjlpC2JgQCA2
+j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+rGNm6
+5ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUB
+o2M3IUxExJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS
+/jQ6fbjpKdx2qcgw+BRxgMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2z
+Gp1iro2C6pSe3VkQw63d4k3jMdXH7OjysP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTE
+W9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmODBCEIZ43ygknQW/2xzQ+D
+hNQ+IIX3Sj0rnP0qCglN6oH4EZw=
+-----END CERTIFICATE-----
+
+# Issuer: CN=TÜBİTAK UEKAE Kök Sertifika Hizmet Sağlayıcısı - Sürüm 3 O=Türkiye Bilimsel ve Teknolojik Araştırma Kurumu - TÜBİTAK OU=Ulusal Elektronik ve Kriptoloji Araştırma Enstitüsü - UEKAE/Kamu Sertifikasyon Merkezi
+# Subject: CN=TÜBİTAK UEKAE Kök Sertifika Hizmet Sağlayıcısı - Sürüm 3 O=Türkiye Bilimsel ve Teknolojik Araştırma Kurumu - TÜBİTAK OU=Ulusal Elektronik ve Kriptoloji Araştırma Enstitüsü - UEKAE/Kamu Sertifikasyon Merkezi
+# Label: "T\xc3\x9c\x42\xC4\xB0TAK UEKAE K\xC3\xB6k Sertifika Hizmet Sa\xC4\x9Flay\xc4\xb1\x63\xc4\xb1s\xc4\xb1 - S\xC3\xBCr\xC3\xBCm 3"
+# Serial: 17
+# MD5 Fingerprint: ed:41:f5:8c:50:c5:2b:9c:73:e6:ee:6c:eb:c2:a8:26
+# SHA1 Fingerprint: 1b:4b:39:61:26:27:6b:64:91:a2:68:6d:d7:02:43:21:2d:1f:1d:96
+# SHA256 Fingerprint: e4:c7:34:30:d7:a5:b5:09:25:df:43:37:0a:0d:21:6e:9a:79:b9:d6:db:83:73:a0:c6:9e:b1:cc:31:c7:c5:2a
+-----BEGIN CERTIFICATE-----
+MIIFFzCCA/+gAwIBAgIBETANBgkqhkiG9w0BAQUFADCCASsxCzAJBgNVBAYTAlRS
+MRgwFgYDVQQHDA9HZWJ6ZSAtIEtvY2FlbGkxRzBFBgNVBAoMPlTDvHJraXllIEJp
+bGltc2VsIHZlIFRla25vbG9qaWsgQXJhxZ90xLFybWEgS3VydW11IC0gVMOcQsSw
+VEFLMUgwRgYDVQQLDD9VbHVzYWwgRWxla3Ryb25payB2ZSBLcmlwdG9sb2ppIEFy
+YcWfdMSxcm1hIEVuc3RpdMO8c8O8IC0gVUVLQUUxIzAhBgNVBAsMGkthbXUgU2Vy
+dGlmaWthc3lvbiBNZXJrZXppMUowSAYDVQQDDEFUw5xCxLBUQUsgVUVLQUUgS8O2
+ayBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsSAtIFPDvHLDvG0gMzAe
+Fw0wNzA4MjQxMTM3MDdaFw0xNzA4MjExMTM3MDdaMIIBKzELMAkGA1UEBhMCVFIx
+GDAWBgNVBAcMD0dlYnplIC0gS29jYWVsaTFHMEUGA1UECgw+VMO8cmtpeWUgQmls
+aW1zZWwgdmUgVGVrbm9sb2ppayBBcmHFn3TEsXJtYSBLdXJ1bXUgLSBUw5xCxLBU
+QUsxSDBGBgNVBAsMP1VsdXNhbCBFbGVrdHJvbmlrIHZlIEtyaXB0b2xvamkgQXJh
+xZ90xLFybWEgRW5zdGl0w7xzw7wgLSBVRUtBRTEjMCEGA1UECwwaS2FtdSBTZXJ0
+aWZpa2FzeW9uIE1lcmtlemkxSjBIBgNVBAMMQVTDnELEsFRBSyBVRUtBRSBLw7Zr
+IFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIC0gU8O8csO8bSAzMIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAim1L/xCIOsP2fpTo6iBkcK4h
+gb46ezzb8R1Sf1n68yJMlaCQvEhOEav7t7WNeoMojCZG2E6VQIdhn8WebYGHV2yK
+O7Rm6sxA/OOqbLLLAdsyv9Lrhc+hDVXDWzhXcLh1xnnRFDDtG1hba+818qEhTsXO
+fJlfbLm4IpNQp81McGq+agV/E5wrHur+R84EpW+sky58K5+eeROR6Oqeyjh1jmKw
+lZMq5d/pXpduIF9fhHpEORlAHLpVK/swsoHvhOPc7Jg4OQOFCKlUAwUp8MmPi+oL
+hmUZEdPpCSPeaJMDyTYcIW7OjGbxmTDY17PDHfiBLqi9ggtm/oLL4eAagsNAgQID
+AQABo0IwQDAdBgNVHQ4EFgQUvYiHyY/2pAoLquvF/pEjnatKijIwDgYDVR0PAQH/
+BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAB18+kmP
+NOm3JpIWmgV050vQbTlswyb2zrgxvMTfvCr4N5EY3ATIZJkrGG2AA1nJrvhY0D7t
+wyOfaTyGOBye79oneNGEN3GKPEs5z35FBtYt2IpNeBLWrcLTy9LQQfMmNkqblWwM
+7uXRQydmwYj3erMgbOqwaSvHIOgMA8RBBZniP+Rr+KCGgceExh/VS4ESshYhLBOh
+gLJeDEoTniDYYkCrkOpkSi+sDQESeUWoL4cZaMjihccwsnX5OD+ywJO0a+IDRM5n
+oN+J1q2MdqMTw5RhK2vZbMEHCiIHhWyFJEapvj+LeISCfiQMnf2BN+MlqO02TpUs
+yZyQ2uypQjyttgI=
+-----END CERTIFICATE-----
+
+# Issuer: CN=Buypass Class 2 CA 1 O=Buypass AS-983163327
+# Subject: CN=Buypass Class 2 CA 1 O=Buypass AS-983163327
+# Label: "Buypass Class 2 CA 1"
+# Serial: 1
+# MD5 Fingerprint: b8:08:9a:f0:03:cc:1b:0d:c8:6c:0b:76:a1:75:64:23
+# SHA1 Fingerprint: a0:a1:ab:90:c9:fc:84:7b:3b:12:61:e8:97:7d:5f:d3:22:61:d3:cc
+# SHA256 Fingerprint: 0f:4e:9c:dd:26:4b:02:55:50:d1:70:80:63:40:21:4f:e9:44:34:c9:b0:2f:69:7e:c7:10:fc:5f:ea:fb:5e:38
+-----BEGIN CERTIFICATE-----
+MIIDUzCCAjugAwIBAgIBATANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEd
+MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3Mg
+Q2xhc3MgMiBDQSAxMB4XDTA2MTAxMzEwMjUwOVoXDTE2MTAxMzEwMjUwOVowSzEL
+MAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MR0wGwYD
+VQQDDBRCdXlwYXNzIENsYXNzIDIgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEP
+ADCCAQoCggEBAIs8B0XY9t/mx8q6jUPFR42wWsE425KEHK8T1A9vNkYgxC7McXA0
+ojTTNy7Y3Tp3L8DrKehc0rWpkTSHIln+zNvnma+WwajHQN2lFYxuyHyXA8vmIPLX
+l18xoS830r7uvqmtqEyeIWZDO6i88wmjONVZJMHCR3axiFyCO7srpgTXjAePzdVB
+HfCuuCkslFJgNJQ72uA40Z0zPhX0kzLFANq1KWYOOngPIVJfAuWSeyXTkh4vFZ2B
+5J2O6O+JzhRMVB0cgRJNcKi+EAUXfh/RuFdV7c27UsKwHnjCTTZoy1YmwVLBvXb3
+WNVyfh9EdrsAiR0WnVE1703CVu9r4Iw7DekCAwEAAaNCMEAwDwYDVR0TAQH/BAUw
+AwEB/zAdBgNVHQ4EFgQUP42aWYv8e3uco684sDntkHGA1sgwDgYDVR0PAQH/BAQD
+AgEGMA0GCSqGSIb3DQEBBQUAA4IBAQAVGn4TirnoB6NLJzKyQJHyIdFkhb5jatLP
+gcIV1Xp+DCmsNx4cfHZSldq1fyOhKXdlyTKdqC5Wq2B2zha0jX94wNWZUYN/Xtm+
+DKhQ7SLHrQVMdvvt7h5HZPb3J31cKA9FxVxiXqaakZG3Uxcu3K1gnZZkOb1naLKu
+BctN518fV4bVIJwo+28TOPX2EZL2fZleHwzoq0QkKXJAPTZSr4xYkHPB7GEseaHs
+h7U/2k3ZIQAw3pDaDtMaSKk+hQsUi4y8QZ5q9w5wwDX3OaJdZtB7WZ+oRxKaJyOk
+LY4ng5IgodcVf/EuGO70SH8vf/GhGLWhC5SgYiAynB321O+/TIho
+-----END CERTIFICATE-----
+
+# Issuer: CN=Buypass Class 3 CA 1 O=Buypass AS-983163327
+# Subject: CN=Buypass Class 3 CA 1 O=Buypass AS-983163327
+# Label: "Buypass Class 3 CA 1"
+# Serial: 2
+# MD5 Fingerprint: df:3c:73:59:81:e7:39:50:81:04:4c:34:a2:cb:b3:7b
+# SHA1 Fingerprint: 61:57:3a:11:df:0e:d8:7e:d5:92:65:22:ea:d0:56:d7:44:b3:23:71
+# SHA256 Fingerprint: b7:b1:2b:17:1f:82:1d:aa:99:0c:d0:fe:50:87:b1:28:44:8b:a8:e5:18:4f:84:c5:1e:02:b5:c8:fb:96:2b:24
+-----BEGIN CERTIFICATE-----
+MIIDUzCCAjugAwIBAgIBAjANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEd
+MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3Mg
+Q2xhc3MgMyBDQSAxMB4XDTA1MDUwOTE0MTMwM1oXDTE1MDUwOTE0MTMwM1owSzEL
+MAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MR0wGwYD
+VQQDDBRCdXlwYXNzIENsYXNzIDMgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEP
+ADCCAQoCggEBAKSO13TZKWTeXx+HgJHqTjnmGcZEC4DVC69TB4sSveZn8AKxifZg
+isRbsELRwCGoy+Gb72RRtqfPFfV0gGgEkKBYouZ0plNTVUhjP5JW3SROjvi6K//z
+NIqeKNc0n6wv1g/xpC+9UrJJhW05NfBEMJNGJPO251P7vGGvqaMU+8IXF4Rs4HyI
++MkcVyzwPX6UvCWThOiaAJpFBUJXgPROztmuOfbIUxAMZTpHe2DC1vqRycZxbL2R
+hzyRhkmr8w+gbCZ2Xhysm3HljbybIR6c1jh+JIAVMYKWsUnTYjdbiAwKYjT+p0h+
+mbEwi5A3lRyoH6UsjfRVyNvdWQrCrXig9IsCAwEAAaNCMEAwDwYDVR0TAQH/BAUw
+AwEB/zAdBgNVHQ4EFgQUOBTmyPCppAP0Tj4io1vy1uCtQHQwDgYDVR0PAQH/BAQD
+AgEGMA0GCSqGSIb3DQEBBQUAA4IBAQABZ6OMySU9E2NdFm/soT4JXJEVKirZgCFP
+Bdy7pYmrEzMqnji3jG8CcmPHc3ceCQa6Oyh7pEfJYWsICCD8igWKH7y6xsL+z27s
+EzNxZy5p+qksP2bAEllNC1QCkoS72xLvg3BweMhT+t/Gxv/ciC8HwEmdMldg0/L2
+mSlf56oBzKwzqBwKu5HEA6BvtjT5htOzdlSY9EqBs1OdTUDs5XcTRa9bqh/YL0yC
+e/4qxFi7T/ye/QNlGioOw6UgFpRreaaiErS7GqQjel/wroQk5PMr+4okoyeYZdow
+dXb8GZHo2+ubPzK/QJcHJrrM85SFSnonk8+QQtS4Wxam58tAA915
+-----END CERTIFICATE-----
+
+# Issuer: CN=EBG Elektronik Sertifika Hizmet Sağlayıcısı O=EBG Bilişim Teknolojileri ve Hizmetleri A.Ş.
+# Subject: CN=EBG Elektronik Sertifika Hizmet Sağlayıcısı O=EBG Bilişim Teknolojileri ve Hizmetleri A.Ş.
+# Label: "EBG Elektronik Sertifika Hizmet Sa\xC4\x9Flay\xc4\xb1\x63\xc4\xb1s\xc4\xb1"
+# Serial: 5525761995591021570
+# MD5 Fingerprint: 2c:20:26:9d:cb:1a:4a:00:85:b5:b7:5a:ae:c2:01:37
+# SHA1 Fingerprint: 8c:96:ba:eb:dd:2b:07:07:48:ee:30:32:66:a0:f3:98:6e:7c:ae:58
+# SHA256 Fingerprint: 35:ae:5b:dd:d8:f7:ae:63:5c:ff:ba:56:82:a8:f0:0b:95:f4:84:62:c7:10:8e:e9:a0:e5:29:2b:07:4a:af:b2
+-----BEGIN CERTIFICATE-----
+MIIF5zCCA8+gAwIBAgIITK9zQhyOdAIwDQYJKoZIhvcNAQEFBQAwgYAxODA2BgNV
+BAMML0VCRyBFbGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sx
+c8SxMTcwNQYDVQQKDC5FQkcgQmlsacWfaW0gVGVrbm9sb2ppbGVyaSB2ZSBIaXpt
+ZXRsZXJpIEEuxZ4uMQswCQYDVQQGEwJUUjAeFw0wNjA4MTcwMDIxMDlaFw0xNjA4
+MTQwMDMxMDlaMIGAMTgwNgYDVQQDDC9FQkcgRWxla3Ryb25payBTZXJ0aWZpa2Eg
+SGl6bWV0IFNhxJ9sYXnEsWPEsXPEsTE3MDUGA1UECgwuRUJHIEJpbGnFn2ltIFRl
+a25vbG9qaWxlcmkgdmUgSGl6bWV0bGVyaSBBLsWeLjELMAkGA1UEBhMCVFIwggIi
+MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDuoIRh0DpqZhAy2DE4f6en5f2h
+4fuXd7hxlugTlkaDT7byX3JWbhNgpQGR4lvFzVcfd2NR/y8927k/qqk153nQ9dAk
+tiHq6yOU/im/+4mRDGSaBUorzAzu8T2bgmmkTPiab+ci2hC6X5L8GCcKqKpE+i4s
+tPtGmggDg3KriORqcsnlZR9uKg+ds+g75AxuetpX/dfreYteIAbTdgtsApWjluTL
+dlHRKJ2hGvxEok3MenaoDT2/F08iiFD9rrbskFBKW5+VQarKD7JK/oCZTqNGFav4
+c0JqwmZ2sQomFd2TkuzbqV9UIlKRcF0T6kjsbgNs2d1s/OsNA/+mgxKb8amTD8Um
+TDGyY5lhcucqZJnSuOl14nypqZoaqsNW2xCaPINStnuWt6yHd6i58mcLlEOzrz5z
++kI2sSXFCjEmN1ZnuqMLfdb3ic1nobc6HmZP9qBVFCVMLDMNpkGMvQQxahByCp0O
+Lna9XvNRiYuoP1Vzv9s6xiQFlpJIqkuNKgPlV5EQ9GooFW5Hd4RcUXSfGenmHmMW
+OeMRFeNYGkS9y8RsZteEBt8w9DeiQyJ50hBs37vmExH8nYQKE3vwO9D8owrXieqW
+fo1IhR5kX9tUoqzVegJ5a9KK8GfaZXINFHDk6Y54jzJ0fFfy1tb0Nokb+Clsi7n2
+l9GkLqq+CxnCRelwXQIDAJ3Zo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB
+/wQEAwIBBjAdBgNVHQ4EFgQU587GT/wWZ5b6SqMHwQSny2re2kcwHwYDVR0jBBgw
+FoAU587GT/wWZ5b6SqMHwQSny2re2kcwDQYJKoZIhvcNAQEFBQADggIBAJuYml2+
+8ygjdsZs93/mQJ7ANtyVDR2tFcU22NU57/IeIl6zgrRdu0waypIN30ckHrMk2pGI
+6YNw3ZPX6bqz3xZaPt7gyPvT/Wwp+BVGoGgmzJNSroIBk5DKd8pNSe/iWtkqvTDO
+TLKBtjDOWU/aWR1qeqRFsIImgYZ29fUQALjuswnoT4cCB64kXPBfrAowzIpAoHME
+wfuJJPaaHFy3PApnNgUIMbOv2AFoKuB4j3TeuFGkjGwgPaL7s9QJ/XvCgKqTbCmY
+Iai7FvOpEl90tYeY8pUm3zTvilORiF0alKM/fCL414i6poyWqD1SNGKfAB5UVUJn
+xk1Gj7sURT0KlhaOEKGXmdXTMIXM3rRyt7yKPBgpaP3ccQfuJDlq+u2lrDgv+R4Q
+DgZxGhBM/nV+/x5XOULK1+EVoVZVWRvRo68R2E7DpSvvkL/A7IITW43WciyTTo9q
+Kd+FPNMN4KIYEsxVL0e3p5sC/kH2iExt2qkBR4NkJ2IQgtYSe14DHzSpyZH+r11t
+hie3I6p1GMog57AP14kOpmciY/SDQSsGS7tY1dHXt7kQY9iJSrSq3RZj9W6+YKH4
+7ejWkE8axsWgKdOnIaj1Wjz3x0miIZpKlVIglnKaZsv30oZDfCK+lvm9AahH3eU7
+QPl1K5srRmSGjR70j/sHd9DqSaIcjVIUpgqT
+-----END CERTIFICATE-----
+
+# Issuer: O=certSIGN OU=certSIGN ROOT CA
+# Subject: O=certSIGN OU=certSIGN ROOT CA
+# Label: "certSIGN ROOT CA"
+# Serial: 35210227249154
+# MD5 Fingerprint: 18:98:c0:d6:e9:3a:fc:f9:b0:f5:0c:f7:4b:01:44:17
+# SHA1 Fingerprint: fa:b7:ee:36:97:26:62:fb:2d:b0:2a:f6:bf:03:fd:e8:7c:4b:2f:9b
+# SHA256 Fingerprint: ea:a9:62:c4:fa:4a:6b:af:eb:e4:15:19:6d:35:1c:cd:88:8d:4f:53:f3:fa:8a:e6:d7:c4:66:a9:4e:60:42:bb
+-----BEGIN CERTIFICATE-----
+MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYT
+AlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBD
+QTAeFw0wNjA3MDQxNzIwMDRaFw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJP
+MREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTCC
+ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7IJUqOtdu0KBuqV5Do
+0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHHrfAQ
+UySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5d
+RdY4zTW2ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQ
+OA7+j0xbm0bqQfWwCHTD0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwv
+JoIQ4uNllAoEwF73XVv4EOLQunpL+943AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08C
+AwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAcYwHQYDVR0O
+BBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IBAQA+0hyJ
+LjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecY
+MnQ8SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ
+44gx+FkagQnIl6Z0x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6I
+Jd1hJyMctTEHBDa0GpC9oHRxUIltvBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNw
+i/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7NzTogVZ96edhBiIL5VaZVDADlN
+9u6wWk5JRFRYX0KD
+-----END CERTIFICATE-----
+
+# Issuer: CN=CNNIC ROOT O=CNNIC
+# Subject: CN=CNNIC ROOT O=CNNIC
+# Label: "CNNIC ROOT"
+# Serial: 1228079105
+# MD5 Fingerprint: 21:bc:82:ab:49:c4:13:3b:4b:b2:2b:5c:6b:90:9c:19
+# SHA1 Fingerprint: 8b:af:4c:9b:1d:f0:2a:92:f7:da:12:8e:b9:1b:ac:f4:98:60:4b:6f
+# SHA256 Fingerprint: e2:83:93:77:3d:a8:45:a6:79:f2:08:0c:c7:fb:44:a3:b7:a1:c3:79:2c:b7:eb:77:29:fd:cb:6a:8d:99:ae:a7
+-----BEGIN CERTIFICATE-----
+MIIDVTCCAj2gAwIBAgIESTMAATANBgkqhkiG9w0BAQUFADAyMQswCQYDVQQGEwJD
+TjEOMAwGA1UEChMFQ05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1QwHhcNMDcwNDE2
+MDcwOTE0WhcNMjcwNDE2MDcwOTE0WjAyMQswCQYDVQQGEwJDTjEOMAwGA1UEChMF
+Q05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1QwggEiMA0GCSqGSIb3DQEBAQUAA4IB
+DwAwggEKAoIBAQDTNfc/c3et6FtzF8LRb+1VvG7q6KR5smzDo+/hn7E7SIX1mlwh
+IhAsxYLO2uOabjfhhyzcuQxauohV3/2q2x8x6gHx3zkBwRP9SFIhxFXf2tizVHa6
+dLG3fdfA6PZZxU3Iva0fFNrfWEQlMhkqx35+jq44sDB7R3IJMfAw28Mbdim7aXZO
+V/kbZKKTVrdvmW7bCgScEeOAH8tjlBAKqeFkgjH5jCftppkA9nCTGPihNIaj3XrC
+GHn2emU1z5DrvTOTn1OrczvmmzQgLx3vqR1jGqCA2wMv+SYahtKNu6m+UjqHZ0gN
+v7Sg2Ca+I19zN38m5pIEo3/PIKe38zrKy5nLAgMBAAGjczBxMBEGCWCGSAGG+EIB
+AQQEAwIABzAfBgNVHSMEGDAWgBRl8jGtKvf33VKWCscCwQ7vptU7ETAPBgNVHRMB
+Af8EBTADAQH/MAsGA1UdDwQEAwIB/jAdBgNVHQ4EFgQUZfIxrSr3991SlgrHAsEO
+76bVOxEwDQYJKoZIhvcNAQEFBQADggEBAEs17szkrr/Dbq2flTtLP1se31cpolnK
+OOK5Gv+e5m4y3R6u6jW39ZORTtpC4cMXYFDy0VwmuYK36m3knITnA3kXr5g9lNvH
+ugDnuL8BV8F3RTIMO/G0HAiw/VGgod2aHRM2mm23xzy54cXZF/qD1T0VoDy7Hgvi
+yJA/qIYM/PmLXoXLT1tLYhFHxUV8BS9BsZ4QaRuZluBVeftOhpm4lNqGOGqTo+fL
+buXf6iFViZx9fX+Y9QCJ7uOEwFyWtcVG6kbghVW2G8kS1sHNzYDzAgE8yGnLRUhj
+2JTQ7IUOO04RZfSCjKY9ri4ilAnIXOo8gV0WKgOXFlUJ24pBgp5mmxE=
+-----END CERTIFICATE-----
+
+# Issuer: O=Japanese Government OU=ApplicationCA
+# Subject: O=Japanese Government OU=ApplicationCA
+# Label: "ApplicationCA - Japanese Government"
+# Serial: 49
+# MD5 Fingerprint: 7e:23:4e:5b:a7:a5:b4:25:e9:00:07:74:11:62:ae:d6
+# SHA1 Fingerprint: 7f:8a:b0:cf:d0:51:87:6a:66:f3:36:0f:47:c8:8d:8c:d3:35:fc:74
+# SHA256 Fingerprint: 2d:47:43:7d:e1:79:51:21:5a:12:f3:c5:8e:51:c7:29:a5:80:26:ef:1f:cc:0a:5f:b3:d9:dc:01:2f:60:0d:19
+-----BEGIN CERTIFICATE-----
+MIIDoDCCAoigAwIBAgIBMTANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJKUDEc
+MBoGA1UEChMTSmFwYW5lc2UgR292ZXJubWVudDEWMBQGA1UECxMNQXBwbGljYXRp
+b25DQTAeFw0wNzEyMTIxNTAwMDBaFw0xNzEyMTIxNTAwMDBaMEMxCzAJBgNVBAYT
+AkpQMRwwGgYDVQQKExNKYXBhbmVzZSBHb3Zlcm5tZW50MRYwFAYDVQQLEw1BcHBs
+aWNhdGlvbkNBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp23gdE6H
+j6UG3mii24aZS2QNcfAKBZuOquHMLtJqO8F6tJdhjYq+xpqcBrSGUeQ3DnR4fl+K
+f5Sk10cI/VBaVuRorChzoHvpfxiSQE8tnfWuREhzNgaeZCw7NCPbXCbkcXmP1G55
+IrmTwcrNwVbtiGrXoDkhBFcsovW8R0FPXjQilbUfKW1eSvNNcr5BViCH/OlQR9cw
+FO5cjFW6WY2H/CPek9AEjP3vbb3QesmlOmpyM8ZKDQUXKi17safY1vC+9D/qDiht
+QWEjdnjDuGWk81quzMKq2edY3rZ+nYVunyoKb58DKTCXKB28t89UKU5RMfkntigm
+/qJj5kEW8DOYRwIDAQABo4GeMIGbMB0GA1UdDgQWBBRUWssmP3HMlEYNllPqa0jQ
+k/5CdTAOBgNVHQ8BAf8EBAMCAQYwWQYDVR0RBFIwUKROMEwxCzAJBgNVBAYTAkpQ
+MRgwFgYDVQQKDA/ml6XmnKzlm73mlL/lupwxIzAhBgNVBAsMGuOCouODl+ODquOC
+seODvOOCt+ODp+ODs0NBMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD
+ggEBADlqRHZ3ODrso2dGD/mLBqj7apAxzn7s2tGJfHrrLgy9mTLnsCTWw//1sogJ
+hyzjVOGjprIIC8CFqMjSnHH2HZ9g/DgzE+Ge3Atf2hZQKXsvcJEPmbo0NI2VdMV+
+eKlmXb3KIXdCEKxmJj3ekav9FfBv7WxfEPjzFvYDio+nEhEMy/0/ecGc/WLuo89U
+DNErXxc+4z6/wCs+CZv+iKZ+tJIX/COUgb1up8WMwusRRdv4QcmWdupwX3kSa+Sj
+B1oF7ydJzyGfikwJcGapJsErEU4z0g781mzSDjJkaP+tBXhfAx2o45CsJOAPQKdL
+rosot4LKGAfmt1t06SAZf7IbiVQ=
+-----END CERTIFICATE-----
+
+# Issuer: CN=GeoTrust Primary Certification Authority - G3 O=GeoTrust Inc. OU=(c) 2008 GeoTrust Inc. - For authorized use only
+# Subject: CN=GeoTrust Primary Certification Authority - G3 O=GeoTrust Inc. OU=(c) 2008 GeoTrust Inc. - For authorized use only
+# Label: "GeoTrust Primary Certification Authority - G3"
+# Serial: 28809105769928564313984085209975885599
+# MD5 Fingerprint: b5:e8:34:36:c9:10:44:58:48:70:6d:2e:83:d4:b8:05
+# SHA1 Fingerprint: 03:9e:ed:b8:0b:e7:a0:3c:69:53:89:3b:20:d2:d9:32:3a:4c:2a:fd
+# SHA256 Fingerprint: b4:78:b8:12:25:0d:f8:78:63:5c:2a:a7:ec:7d:15:5e:aa:62:5e:e8:29:16:e2:cd:29:43:61:88:6c:d1:fb:d4
+-----BEGIN CERTIFICATE-----
+MIID/jCCAuagAwIBAgIQFaxulBmyeUtB9iepwxgPHzANBgkqhkiG9w0BAQsFADCB
+mDELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsT
+MChjKSAyMDA4IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25s
+eTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhv
+cml0eSAtIEczMB4XDTA4MDQwMjAwMDAwMFoXDTM3MTIwMTIzNTk1OVowgZgxCzAJ
+BgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykg
+MjAwOCBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0
+BgNVBAMTLUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg
+LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANziXmJYHTNXOTIz
++uvLh4yn1ErdBojqZI4xmKU4kB6Yzy5jK/BGvESyiaHAKAxJcCGVn2TAppMSAmUm
+hsalifD614SgcK9PGpc/BkTVyetyEH3kMSj7HGHmKAdEc5IiaacDiGydY8hS2pgn
+5whMcD60yRLBxWeDXTPzAxHsatBT4tG6NmCUgLthY2xbF37fQJQeqw3CIShwiP/W
+JmxsYAQlTlV+fe+/lEjetx3dcI0FX4ilm/LC7urRQEFtYjgdVgbFA0dRIBn8exAL
+DmKudlW/X3e+PkkBUz2YJQN2JFodtNuJ6nnltrM7P7pMKEF/BqxqjsHQ9gUdfeZC
+huOl1UcCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw
+HQYDVR0OBBYEFMR5yo6hTgMdHNxr2zFblD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IB
+AQAtxRPPVoB7eni9n64smefv2t+UXglpp+duaIy9cr5HqQ6XErhK8WTTOd8lNNTB
+zU6B8A8ExCSzNJbGpqow32hhc9f5joWJ7w5elShKKiePEI4ufIbEAp7aDHdlDkQN
+kv39sxY2+hENHYwOB4lqKVb3cvTdFZx3NWZXqxNT2I7BQMXXExZacse3aQHEerGD
+AWh9jUGhlBjBJVz88P6DAod8DQ3PLghcSkANPuyBYeYk28rgDi0Hsj5W3I31QYUH
+SJsMC8tJP33st/3LjWeJGqvtux6jAAgIFyqCXDFdRootD4abdNlF+9RAsXqqaC2G
+spki4cErx5z481+oghLrGREt
+-----END CERTIFICATE-----
+
+# Issuer: CN=thawte Primary Root CA - G2 O=thawte, Inc. OU=(c) 2007 thawte, Inc. - For authorized use only
+# Subject: CN=thawte Primary Root CA - G2 O=thawte, Inc. OU=(c) 2007 thawte, Inc. - For authorized use only
+# Label: "thawte Primary Root CA - G2"
+# Serial: 71758320672825410020661621085256472406
+# MD5 Fingerprint: 74:9d:ea:60:24:c4:fd:22:53:3e:cc:3a:72:d9:29:4f
+# SHA1 Fingerprint: aa:db:bc:22:23:8f:c4:01:a1:27:bb:38:dd:f4:1d:db:08:9e:f0:12
+# SHA256 Fingerprint: a4:31:0d:50:af:18:a6:44:71:90:37:2a:86:af:af:8b:95:1f:fb:43:1d:83:7f:1e:56:88:b4:59:71:ed:15:57
+-----BEGIN CERTIFICATE-----
+MIICiDCCAg2gAwIBAgIQNfwmXNmET8k9Jj1Xm67XVjAKBggqhkjOPQQDAzCBhDEL
+MAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjE4MDYGA1UECxMvKGMp
+IDIwMDcgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAi
+BgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMjAeFw0wNzExMDUwMDAw
+MDBaFw0zODAxMTgyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhh
+d3RlLCBJbmMuMTgwNgYDVQQLEy8oYykgMjAwNyB0aGF3dGUsIEluYy4gLSBGb3Ig
+YXV0aG9yaXplZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9v
+dCBDQSAtIEcyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEotWcgnuVnfFSeIf+iha/
+BebfowJPDQfGAFG6DAJSLSKkQjnE/o/qycG+1E3/n3qe4rF8mq2nhglzh9HnmuN6
+papu+7qzcMBniKI11KOasf2twu8x+qi58/sIxpHR+ymVo0IwQDAPBgNVHRMBAf8E
+BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUmtgAMADna3+FGO6Lts6K
+DPgR4bswCgYIKoZIzj0EAwMDaQAwZgIxAN344FdHW6fmCsO99YCKlzUNG4k8VIZ3
+KMqh9HneteY4sPBlcIx/AlTCv//YoT7ZzwIxAMSNlPzcU9LcnXgWHxUzI1NS41ox
+XZ3Krr0TKUQNJ1uo52icEvdYPy5yAlejj6EULg==
+-----END CERTIFICATE-----
+
+# Issuer: CN=thawte Primary Root CA - G3 O=thawte, Inc. OU=Certification Services Division/(c) 2008 thawte, Inc. - For authorized use only
+# Subject: CN=thawte Primary Root CA - G3 O=thawte, Inc. OU=Certification Services Division/(c) 2008 thawte, Inc. - For authorized use only
+# Label: "thawte Primary Root CA - G3"
+# Serial: 127614157056681299805556476275995414779
+# MD5 Fingerprint: fb:1b:5d:43:8a:94:cd:44:c6:76:f2:43:4b:47:e7:31
+# SHA1 Fingerprint: f1:8b:53:8d:1b:e9:03:b6:a6:f0:56:43:5b:17:15:89:ca:f3:6b:f2
+# SHA256 Fingerprint: 4b:03:f4:58:07:ad:70:f2:1b:fc:2c:ae:71:c9:fd:e4:60:4c:06:4c:f5:ff:b6:86:ba:e5:db:aa:d7:fd:d3:4c
+-----BEGIN CERTIFICATE-----
+MIIEKjCCAxKgAwIBAgIQYAGXt0an6rS0mtZLL/eQ+zANBgkqhkiG9w0BAQsFADCB
+rjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf
+Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw
+MDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNV
+BAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0wODA0MDIwMDAwMDBa
+Fw0zNzEyMDEyMzU5NTlaMIGuMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhhd3Rl
+LCBJbmMuMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9u
+MTgwNgYDVQQLEy8oYykgMjAwOCB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXpl
+ZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAtIEcz
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsr8nLPvb2FvdeHsbnndm
+gcs+vHyu86YnmjSjaDFxODNi5PNxZnmxqWWjpYvVj2AtP0LMqmsywCPLLEHd5N/8
+YZzic7IilRFDGF/Eth9XbAoFWCLINkw6fKXRz4aviKdEAhN0cXMKQlkC+BsUa0Lf
+b1+6a4KinVvnSr0eAXLbS3ToO39/fR8EtCab4LRarEc9VbjXsCZSKAExQGbY2SS9
+9irY7CFJXJv2eul/VTV+lmuNk5Mny5K76qxAwJ/C+IDPXfRa3M50hqY+bAtTyr2S
+zhkGcuYMXDhpxwTWvGzOW/b3aJzcJRVIiKHpqfiYnODz1TEoYRFsZ5aNOZnLwkUk
+OQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNV
+HQ4EFgQUrWyqlGCc7eT/+j4KdCtjA/e2Wb8wDQYJKoZIhvcNAQELBQADggEBABpA
+2JVlrAmSicY59BDlqQ5mU1143vokkbvnRFHfxhY0Cu9qRFHqKweKA3rD6z8KLFIW
+oCtDuSWQP3CpMyVtRRooOyfPqsMpQhvfO0zAMzRbQYi/aytlryjvsvXDqmbOe1bu
+t8jLZ8HJnBoYuMTDSQPxYA5QzUbF83d597YV4Djbxy8ooAw/dyZ02SUS2jHaGh7c
+KUGRIjxpp7sC8rZcJwOJ9Abqm+RyguOhCcHpABnTPtRwa7pxpqpYrvS76Wy274fM
+m7v/OeZWYdMKp8RcTGB7BXcmer/YB1IsYvdwY9k5vG8cwnncdimvzsUsZAReiDZu
+MdRAGmI0Nj81Aa6sY6A=
+-----END CERTIFICATE-----
+
+# Issuer: CN=GeoTrust Primary Certification Authority - G2 O=GeoTrust Inc. OU=(c) 2007 GeoTrust Inc. - For authorized use only
+# Subject: CN=GeoTrust Primary Certification Authority - G2 O=GeoTrust Inc. OU=(c) 2007 GeoTrust Inc. - For authorized use only
+# Label: "GeoTrust Primary Certification Authority - G2"
+# Serial: 80682863203381065782177908751794619243
+# MD5 Fingerprint: 01:5e:d8:6b:bd:6f:3d:8e:a1:31:f8:12:e0:98:73:6a
+# SHA1 Fingerprint: 8d:17:84:d5:37:f3:03:7d:ec:70:fe:57:8b:51:9a:99:e6:10:d7:b0
+# SHA256 Fingerprint: 5e:db:7a:c4:3b:82:a0:6a:87:61:e8:d7:be:49:79:eb:f2:61:1f:7d:d7:9b:f9:1c:1c:6b:56:6a:21:9e:d7:66
+-----BEGIN CERTIFICATE-----
+MIICrjCCAjWgAwIBAgIQPLL0SAoA4v7rJDteYD7DazAKBggqhkjOPQQDAzCBmDEL
+MAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChj
+KSAyMDA3IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2
+MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0
+eSAtIEcyMB4XDTA3MTEwNTAwMDAwMFoXDTM4MDExODIzNTk1OVowgZgxCzAJBgNV
+BAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykgMjAw
+NyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNV
+BAMTLUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBH
+MjB2MBAGByqGSM49AgEGBSuBBAAiA2IABBWx6P0DFUPlrOuHNxFi79KDNlJ9RVcL
+So17VDs6bl8VAsBQps8lL33KSLjHUGMcKiEIfJo22Av+0SbFWDEwKCXzXV2juLal
+tJLtbCyf691DiaI8S0iRHVDsJt/WYC69IaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO
+BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBVfNVdRVfslsq0DafwBo/q+EVXVMAoG
+CCqGSM49BAMDA2cAMGQCMGSWWaboCd6LuvpaiIjwH5HTRqjySkwCY/tsXzjbLkGT
+qQ7mndwxHLKgpxgceeHHNgIwOlavmnRs9vuD4DPTCF+hnMJbn0bWtsuRBmOiBucz
+rD6ogRLQy7rQkgu2npaqBA+K
+-----END CERTIFICATE-----
+
+# Issuer: CN=VeriSign Universal Root Certification Authority O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2008 VeriSign, Inc. - For authorized use only
+# Subject: CN=VeriSign Universal Root Certification Authority O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2008 VeriSign, Inc. - For authorized use only
+# Label: "VeriSign Universal Root Certification Authority"
+# Serial: 85209574734084581917763752644031726877
+# MD5 Fingerprint: 8e:ad:b5:01:aa:4d:81:e4:8c:1d:d1:e1:14:00:95:19
+# SHA1 Fingerprint: 36:79:ca:35:66:87:72:30:4d:30:a5:fb:87:3b:0f:a7:7b:b7:0d:54
+# SHA256 Fingerprint: 23:99:56:11:27:a5:71:25:de:8c:ef:ea:61:0d:df:2f:a0:78:b5:c8:06:7f:4e:82:82:90:bf:b8:60:e8:4b:3c
+-----BEGIN CERTIFICATE-----
+MIIEuTCCA6GgAwIBAgIQQBrEZCGzEyEDDrvkEhrFHTANBgkqhkiG9w0BAQsFADCB
+vTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
+ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJp
+U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MTgwNgYDVQQDEy9W
+ZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe
+Fw0wODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIG9MQswCQYDVQQGEwJVUzEX
+MBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0
+IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9y
+IGF1dGhvcml6ZWQgdXNlIG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNh
+bCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAx2E3XrEBNNti1xWb/1hajCMj1mCOkdeQmIN65lgZOIzF
+9uVkhbSicfvtvbnazU0AtMgtc6XHaXGVHzk8skQHnOgO+k1KxCHfKWGPMiJhgsWH
+H26MfF8WIFFE0XBPV+rjHOPMee5Y2A7Cs0WTwCznmhcrewA3ekEzeOEz4vMQGn+H
+LL729fdC4uW/h2KJXwBL38Xd5HVEMkE6HnFuacsLdUYI0crSK5XQz/u5QGtkjFdN
+/BMReYTtXlT2NJ8IAfMQJQYXStrxHXpma5hgZqTZ79IugvHw7wnqRMkVauIDbjPT
+rJ9VAMf2CGqUuV/c4DPxhGD5WycRtPwW8rtWaoAljQIDAQABo4GyMIGvMA8GA1Ud
+EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMG0GCCsGAQUFBwEMBGEwX6FdoFsw
+WTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2Oa8PPgGrUSBgs
+exkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMB0GA1Ud
+DgQWBBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEASvj4
+sAPmLGd75JR3Y8xuTPl9Dg3cyLk1uXBPY/ok+myDjEedO2Pzmvl2MpWRsXe8rJq+
+seQxIcaBlVZaDrHC1LGmWazxY8u4TB1ZkErvkBYoH1quEPuBUDgMbMzxPcP1Y+Oz
+4yHJJDnp/RVmRvQbEdBNc6N9Rvk97ahfYtTxP/jgdFcrGJ2BtMQo2pSXpXDrrB2+
+BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+PwGZsY6rp2aQW9IHR
+lRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4mJO3
+7M2CYfE45k+XmCpajQ==
+-----END CERTIFICATE-----
+
+# Issuer: CN=VeriSign Class 3 Public Primary Certification Authority - G4 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2007 VeriSign, Inc. - For authorized use only
+# Subject: CN=VeriSign Class 3 Public Primary Certification Authority - G4 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2007 VeriSign, Inc. - For authorized use only
+# Label: "VeriSign Class 3 Public Primary Certification Authority - G4"
+# Serial: 63143484348153506665311985501458640051
+# MD5 Fingerprint: 3a:52:e1:e7:fd:6f:3a:e3:6f:f3:6f:99:1b:f9:22:41
+# SHA1 Fingerprint: 22:d5:d8:df:8f:02:31:d1:8d:f7:9d:b7:cf:8a:2d:64:c9:3f:6c:3a
+# SHA256 Fingerprint: 69:dd:d7:ea:90:bb:57:c9:3e:13:5d:c8:5e:a6:fc:d5:48:0b:60:32:39:bd:c4:54:fc:75:8b:2a:26:cf:7f:79
+-----BEGIN CERTIFICATE-----
+MIIDhDCCAwqgAwIBAgIQL4D+I4wOIg9IZxIokYesszAKBggqhkjOPQQDAzCByjEL
+MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW
+ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2ln
+biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp
+U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y
+aXR5IC0gRzQwHhcNMDcxMTA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCByjELMAkG
+A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJp
+U2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwg
+SW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2ln
+biBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5
+IC0gRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASnVnp8Utpkmw4tXNherJI9/gHm
+GUo9FANL+mAnINmDiWn6VMaaGF5VKmTeBvaNSjutEDxlPZCIBIngMGGzrl0Bp3ve
+fLK+ymVhAIau2o970ImtTR1ZmkGxvEeA3J5iw/mjgbIwga8wDwYDVR0TAQH/BAUw
+AwEB/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJ
+aW1hZ2UvZ2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYj
+aHR0cDovL2xvZ28udmVyaXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFLMW
+kf3upm7ktS5Jj4d4gYDs5bG1MAoGCCqGSM49BAMDA2gAMGUCMGYhDBgmYFo4e1ZC
+4Kf8NoRRkSAsdk1DPcQdhCPQrNZ8NQbOzWm9kA3bbEhCHQ6qQgIxAJw9SDkjOVga
+FRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA==
+-----END CERTIFICATE-----
+
+# Issuer: CN=NetLock Arany (Class Gold) Főtanúsítvány O=NetLock Kft. OU=Tanúsítványkiadók (Certification Services)
+# Subject: CN=NetLock Arany (Class Gold) Főtanúsítvány O=NetLock Kft. OU=Tanúsítványkiadók (Certification Services)
+# Label: "NetLock Arany (Class Gold) Főtanúsítvány"
+# Serial: 80544274841616
+# MD5 Fingerprint: c5:a1:b7:ff:73:dd:d6:d7:34:32:18:df:fc:3c:ad:88
+# SHA1 Fingerprint: 06:08:3f:59:3f:15:a1:04:a0:69:a4:6b:a9:03:d0:06:b7:97:09:91
+# SHA256 Fingerprint: 6c:61:da:c3:a2:de:f0:31:50:6b:e0:36:d2:a6:fe:40:19:94:fb:d1:3d:f9:c8:d4:66:59:92:74:c4:46:ec:98
+-----BEGIN CERTIFICATE-----
+MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQG
+EwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3
+MDUGA1UECwwuVGFuw7pzw610dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNl
+cnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBBcmFueSAoQ2xhc3MgR29sZCkgRsWR
+dGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgxMjA2MTUwODIxWjCB
+pzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxOZXRM
+b2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlm
+aWNhdGlvbiBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNz
+IEdvbGQpIEbFkXRhbsO6c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAxCRec75LbRTDofTjl5Bu0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrT
+lF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw/HpYzY6b7cNGbIRwXdrz
+AZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAkH3B5r9s5
+VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRG
+ILdwfzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2
+BJtr+UBdADTHLpl1neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAG
+AQH/AgEEMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2M
+U9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwWqZw8UQCgwBEIBaeZ5m8BiFRh
+bvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTtaYtOUZcTh5m2C
++C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC
+bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2F
+uLjbvrW5KfnaNwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2
+XjG4Kvte9nHfRCaexOYNkbQudZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E=
+-----END CERTIFICATE-----
+
+# Issuer: CN=Staat der Nederlanden Root CA - G2 O=Staat der Nederlanden
+# Subject: CN=Staat der Nederlanden Root CA - G2 O=Staat der Nederlanden
+# Label: "Staat der Nederlanden Root CA - G2"
+# Serial: 10000012
+# MD5 Fingerprint: 7c:a5:0f:f8:5b:9a:7d:6d:30:ae:54:5a:e3:42:a2:8a
+# SHA1 Fingerprint: 59:af:82:79:91:86:c7:b4:75:07:cb:cf:03:57:46:eb:04:dd:b7:16
+# SHA256 Fingerprint: 66:8c:83:94:7d:a6:3b:72:4b:ec:e1:74:3c:31:a0:e6:ae:d0:db:8e:c5:b3:1b:e3:77:bb:78:4f:91:b6:71:6f
+-----BEGIN CERTIFICATE-----
+MIIFyjCCA7KgAwIBAgIEAJiWjDANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJO
+TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFh
+dCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQSAtIEcyMB4XDTA4MDMyNjExMTgxN1oX
+DTIwMDMyNTExMDMxMFowWjELMAkGA1UEBhMCTkwxHjAcBgNVBAoMFVN0YWF0IGRl
+ciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5lZGVybGFuZGVuIFJv
+b3QgQ0EgLSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMVZ5291
+qj5LnLW4rJ4L5PnZyqtdj7U5EILXr1HgO+EASGrP2uEGQxGZqhQlEq0i6ABtQ8Sp
+uOUfiUtnvWFI7/3S4GCI5bkYYCjDdyutsDeqN95kWSpGV+RLufg3fNU254DBtvPU
+Z5uW6M7XxgpT0GtJlvOjCwV3SPcl5XCsMBQgJeN/dVrlSPhOewMHBPqCYYdu8DvE
+pMfQ9XQ+pV0aCPKbJdL2rAQmPlU6Yiile7Iwr/g3wtG61jj99O9JMDeZJiFIhQGp
+5Rbn3JBV3w/oOM2ZNyFPXfUib2rFEhZgF1XyZWampzCROME4HYYEhLoaJXhena/M
+UGDWE4dS7WMfbWV9whUYdMrhfmQpjHLYFhN9C0lK8SgbIHRrxT3dsKpICT0ugpTN
+GmXZK4iambwYfp/ufWZ8Pr2UuIHOzZgweMFvZ9C+X+Bo7d7iscksWXiSqt8rYGPy
+5V6548r6f1CGPqI0GAwJaCgRHOThuVw+R7oyPxjMW4T182t0xHJ04eOLoEq9jWYv
+6q012iDTiIJh8BIitrzQ1aTsr1SIJSQ8p22xcik/Plemf1WvbibG/ufMQFxRRIEK
+eN5KzlW/HdXZt1bv8Hb/C3m1r737qWmRRpdogBQ2HbN/uymYNqUg+oJgYjOk7Na6
+B6duxc8UpufWkjTYgfX8HV2qXB72o007uPc5AgMBAAGjgZcwgZQwDwYDVR0TAQH/
+BAUwAwEB/zBSBgNVHSAESzBJMEcGBFUdIAAwPzA9BggrBgEFBQcCARYxaHR0cDov
+L3d3dy5wa2lvdmVyaGVpZC5ubC9wb2xpY2llcy9yb290LXBvbGljeS1HMjAOBgNV
+HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJFoMocVHYnitfGsNig0jQt8YojrMA0GCSqG
+SIb3DQEBCwUAA4ICAQCoQUpnKpKBglBu4dfYszk78wIVCVBR7y29JHuIhjv5tLyS
+CZa59sCrI2AGeYwRTlHSeYAz+51IvuxBQ4EffkdAHOV6CMqqi3WtFMTC6GY8ggen
+5ieCWxjmD27ZUD6KQhgpxrRW/FYQoAUXvQwjf/ST7ZwaUb7dRUG/kSS0H4zpX897
+IZmflZ85OkYcbPnNe5yQzSipx6lVu6xiNGI1E0sUOlWDuYaNkqbG9AclVMwWVxJK
+gnjIFNkXgiYtXSAfea7+1HAWFpWD2DU5/1JddRwWxRNVz0fMdWVSSt7wsKfkCpYL
++63C4iWEst3kvX5ZbJvw8NjnyvLplzh+ib7M+zkXYT9y2zqR2GUBGR2tUKRXCnxL
+vJxxcypFURmFzI79R6d0lR2o0a9OF7FpJsKqeFdbxU2n5Z4FF5TKsl+gSRiNNOkm
+bEgeqmiSBeGCc1qb3AdbCG19ndeNIdn8FCCqwkXfP+cAslHkwvgFuXkajDTznlvk
+N1trSt8sV4pAWja63XVECDdCcAz+3F4hoKOKwJCcaNpQ5kUQR3i2TtJlycM33+FC
+Y7BXN0Ute4qcvwXqZVUz9zkQxSgqIXobisQk+T8VyJoVIPVVYpbtbZNQvOSqeK3Z
+ywplh6ZmwcSBo3c6WB4L7oOLnR7SUqTMHW+wmG2UMbX4cQrcufx9MmDm66+KAQ==
+-----END CERTIFICATE-----
+
+# Issuer: CN=CA Disig O=Disig a.s.
+# Subject: CN=CA Disig O=Disig a.s.
+# Label: "CA Disig"
+# Serial: 1
+# MD5 Fingerprint: 3f:45:96:39:e2:50:87:f7:bb:fe:98:0c:3c:20:98:e6
+# SHA1 Fingerprint: 2a:c8:d5:8b:57:ce:bf:2f:49:af:f2:fc:76:8f:51:14:62:90:7a:41
+# SHA256 Fingerprint: 92:bf:51:19:ab:ec:ca:d0:b1:33:2d:c4:e1:d0:5f:ba:75:b5:67:90:44:ee:0c:a2:6e:93:1f:74:4f:2f:33:cf
+-----BEGIN CERTIFICATE-----
+MIIEDzCCAvegAwIBAgIBATANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQGEwJTSzET
+MBEGA1UEBxMKQnJhdGlzbGF2YTETMBEGA1UEChMKRGlzaWcgYS5zLjERMA8GA1UE
+AxMIQ0EgRGlzaWcwHhcNMDYwMzIyMDEzOTM0WhcNMTYwMzIyMDEzOTM0WjBKMQsw
+CQYDVQQGEwJTSzETMBEGA1UEBxMKQnJhdGlzbGF2YTETMBEGA1UEChMKRGlzaWcg
+YS5zLjERMA8GA1UEAxMIQ0EgRGlzaWcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
+ggEKAoIBAQCS9jHBfYj9mQGp2HvycXXxMcbzdWb6UShGhJd4NLxs/LxFWYgmGErE
+Nx+hSkS943EE9UQX4j/8SFhvXJ56CbpRNyIjZkMhsDxkovhqFQ4/61HhVKndBpnX
+mjxUizkDPw/Fzsbrg3ICqB9x8y34dQjbYkzo+s7552oftms1grrijxaSfQUMbEYD
+XcDtab86wYqg6I7ZuUUohwjstMoVvoLdtUSLLa2GDGhibYVW8qwUYzrG0ZmsNHhW
+S8+2rT+MitcE5eN4TPWGqvWP+j1scaMtymfraHtuM6kMgiioTGohQBUgDCZbg8Kp
+FhXAJIJdKxatymP2dACw30PEEGBWZ2NFAgMBAAGjgf8wgfwwDwYDVR0TAQH/BAUw
+AwEB/zAdBgNVHQ4EFgQUjbJJaJ1yCCW5wCf1UJNWSEZx+Y8wDgYDVR0PAQH/BAQD
+AgEGMDYGA1UdEQQvMC2BE2Nhb3BlcmF0b3JAZGlzaWcuc2uGFmh0dHA6Ly93d3cu
+ZGlzaWcuc2svY2EwZgYDVR0fBF8wXTAtoCugKYYnaHR0cDovL3d3dy5kaXNpZy5z
+ay9jYS9jcmwvY2FfZGlzaWcuY3JsMCygKqAohiZodHRwOi8vY2EuZGlzaWcuc2sv
+Y2EvY3JsL2NhX2Rpc2lnLmNybDAaBgNVHSAEEzARMA8GDSuBHpGT5goAAAABAQEw
+DQYJKoZIhvcNAQEFBQADggEBAF00dGFMrzvY/59tWDYcPQuBDRIrRhCA/ec8J9B6
+yKm2fnQwM6M6int0wHl5QpNt/7EpFIKrIYwvF/k/Ji/1WcbvgAa3mkkp7M5+cTxq
+EEHA9tOasnxakZzArFvITV734VP/Q3f8nktnbNfzg9Gg4H8l37iYC5oyOGwwoPP/
+CBUz91BKez6jPiCp3C9WgArtQVCwyfTssuMmRAAOb54GvCKWU3BlxFAKRmukLyeB
+EicTXxChds6KezfqwzlhA5WYOudsiCUI/HloDYd9Yvi0X/vF2Ey9WLw/Q1vUHgFN
+PGO+I++MzVpQuGhU+QqZMxEA4Z7CRneC9VkGjCFMhwnN5ag=
+-----END CERTIFICATE-----
+
+# Issuer: CN=Juur-SK O=AS Sertifitseerimiskeskus
+# Subject: CN=Juur-SK O=AS Sertifitseerimiskeskus
+# Label: "Juur-SK"
+# Serial: 999181308
+# MD5 Fingerprint: aa:8e:5d:d9:f8:db:0a:58:b7:8d:26:87:6c:82:35:55
+# SHA1 Fingerprint: 40:9d:4b:d9:17:b5:5c:27:b6:9b:64:cb:98:22:44:0d:cd:09:b8:89
+# SHA256 Fingerprint: ec:c3:e9:c3:40:75:03:be:e0:91:aa:95:2f:41:34:8f:f8:8b:aa:86:3b:22:64:be:fa:c8:07:90:15:74:e9:39
+-----BEGIN CERTIFICATE-----
+MIIE5jCCA86gAwIBAgIEO45L/DANBgkqhkiG9w0BAQUFADBdMRgwFgYJKoZIhvcN
+AQkBFglwa2lAc2suZWUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKExlBUyBTZXJ0aWZp
+dHNlZXJpbWlza2Vza3VzMRAwDgYDVQQDEwdKdXVyLVNLMB4XDTAxMDgzMDE0MjMw
+MVoXDTE2MDgyNjE0MjMwMVowXTEYMBYGCSqGSIb3DQEJARYJcGtpQHNrLmVlMQsw
+CQYDVQQGEwJFRTEiMCAGA1UEChMZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1czEQ
+MA4GA1UEAxMHSnV1ci1TSzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+AIFxNj4zB9bjMI0TfncyRsvPGbJgMUaXhvSYRqTCZUXP00B841oiqBB4M8yIsdOB
+SvZiF3tfTQou0M+LI+5PAk676w7KvRhj6IAcjeEcjT3g/1tf6mTll+g/mX8MCgkz
+ABpTpyHhOEvWgxutr2TC+Rx6jGZITWYfGAriPrsfB2WThbkasLnE+w0R9vXW+RvH
+LCu3GFH+4Hv2qEivbDtPL+/40UceJlfwUR0zlv/vWT3aTdEVNMfqPxZIe5EcgEMP
+PbgFPtGzlc3Yyg/CQ2fbt5PgIoIuvvVoKIO5wTtpeyDaTpxt4brNj3pssAki14sL
+2xzVWiZbDcDq5WDQn/413z8CAwEAAaOCAawwggGoMA8GA1UdEwEB/wQFMAMBAf8w
+ggEWBgNVHSAEggENMIIBCTCCAQUGCisGAQQBzh8BAQEwgfYwgdAGCCsGAQUFBwIC
+MIHDHoHAAFMAZQBlACAAcwBlAHIAdABpAGYAaQBrAGEAYQB0ACAAbwBuACAAdgDk
+AGwAagBhAHMAdABhAHQAdQBkACAAQQBTAC0AaQBzACAAUwBlAHIAdABpAGYAaQB0
+AHMAZQBlAHIAaQBtAGkAcwBrAGUAcwBrAHUAcwAgAGEAbABhAG0ALQBTAEsAIABz
+AGUAcgB0AGkAZgBpAGsAYQBhAHQAaQBkAGUAIABrAGkAbgBuAGkAdABhAG0AaQBz
+AGUAawBzMCEGCCsGAQUFBwIBFhVodHRwOi8vd3d3LnNrLmVlL2Nwcy8wKwYDVR0f
+BCQwIjAgoB6gHIYaaHR0cDovL3d3dy5zay5lZS9qdXVyL2NybC8wHQYDVR0OBBYE
+FASqekej5ImvGs8KQKcYP2/v6X2+MB8GA1UdIwQYMBaAFASqekej5ImvGs8KQKcY
+P2/v6X2+MA4GA1UdDwEB/wQEAwIB5jANBgkqhkiG9w0BAQUFAAOCAQEAe8EYlFOi
+CfP+JmeaUOTDBS8rNXiRTHyoERF5TElZrMj3hWVcRrs7EKACr81Ptcw2Kuxd/u+g
+kcm2k298gFTsxwhwDY77guwqYHhpNjbRxZyLabVAyJRld/JXIWY7zoVAtjNjGr95
+HvxcHdMdkxuLDF2FvZkwMhgJkVLpfKG6/2SSmuz+Ne6ML678IIbsSt4beDI3poHS
+na9aEhbKmVv8b20OxaAehsmR0FyYgl9jDIpaq9iVpszLita/ZEuOyoqysOkhMp6q
+qIWYNIE5ITuoOlIyPfZrN4YGWhWY3PARZv40ILcD9EEQfTmEeZZyY7aWAuVrua0Z
+TbvGRNs2yyqcjg==
+-----END CERTIFICATE-----
+
+# Issuer: CN=Hongkong Post Root CA 1 O=Hongkong Post
+# Subject: CN=Hongkong Post Root CA 1 O=Hongkong Post
+# Label: "Hongkong Post Root CA 1"
+# Serial: 1000
+# MD5 Fingerprint: a8:0d:6f:39:78:b9:43:6d:77:42:6d:98:5a:cc:23:ca
+# SHA1 Fingerprint: d6:da:a8:20:8d:09:d2:15:4d:24:b5:2f:cb:34:6e:b2:58:b2:8a:58
+# SHA256 Fingerprint: f9:e6:7d:33:6c:51:00:2a:c0:54:c6:32:02:2d:66:dd:a2:e7:e3:ff:f1:0a:d0:61:ed:31:d8:bb:b4:10:cf:b2
+-----BEGIN CERTIFICATE-----
+MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsx
+FjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3Qg
+Um9vdCBDQSAxMB4XDTAzMDUxNTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkG
+A1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdr
+b25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+AQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1ApzQ
+jVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEn
+PzlTCeqrauh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjh
+ZY4bXSNmO7ilMlHIhqqhqZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9
+nnV0ttgCXjqQesBCNnLsak3c78QA3xMYV18meMjWCnl3v/evt3a5pQuEF10Q6m/h
+q5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNVHRMBAf8ECDAGAQH/AgED
+MA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7ih9legYsC
+mEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI3
+7piol7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clB
+oiMBdDhViw+5LmeiIAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJs
+EhTkYY2sEJCehFC78JZvRZ+K88psT/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpO
+fMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilTc4afU9hDDl3WY4JxHYB0yvbi
+AmvZWg==
+-----END CERTIFICATE-----
+
+# Issuer: CN=SecureSign RootCA11 O=Japan Certification Services, Inc.
+# Subject: CN=SecureSign RootCA11 O=Japan Certification Services, Inc.
+# Label: "SecureSign RootCA11"
+# Serial: 1
+# MD5 Fingerprint: b7:52:74:e2:92:b4:80:93:f2:75:e4:cc:d7:f2:ea:26
+# SHA1 Fingerprint: 3b:c4:9f:48:f8:f3:73:a0:9c:1e:bd:f8:5b:b1:c3:65:c7:d8:11:b3
+# SHA256 Fingerprint: bf:0f:ee:fb:9e:3a:58:1a:d5:f9:e9:db:75:89:98:57:43:d2:61:08:5c:4d:31:4f:6f:5d:72:59:aa:42:16:12
+-----BEGIN CERTIFICATE-----
+MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDEr
+MCkGA1UEChMiSmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoG
+A1UEAxMTU2VjdXJlU2lnbiBSb290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0
+MDgwNDU2NDdaMFgxCzAJBgNVBAYTAkpQMSswKQYDVQQKEyJKYXBhbiBDZXJ0aWZp
+Y2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1cmVTaWduIFJvb3RD
+QTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/XeqpRyQBTvLTJsz
+i1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1yfIw/XwFndBWW4wI8
+h9uuywGOwvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyKyiyhFTOV
+MdrAG/LuYpmGYz+/3ZMqg6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9
+UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rPO7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni
+8McDWc/V1uinMrPmmECGxc0nEovMe863ETxiYAcjPitAbpSACW22s293bzUIUPsC
+h8U+iQIDAQABo0IwQDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZXt94wDgYD
+VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEB
+AKChOBZmLqdWHyGcBvod7bkixTgm2E5P7KN/ed5GIaGHd48HCJqypMWvDzKYC3xm
+KbabfSVSSUOrTC4rbnpwrxYO4wJs+0LmGJ1F2FXI6Dvd5+H0LgscNFxsWEr7jIhQ
+X5Ucv+2rIrVls4W6ng+4reV6G4pQOh29Dbx7VFALuUKvVaAYga1lme++5Jy/xIWr
+QbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01y8hSyn+B/tlr0/cR7SXf+Of5
+pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061lgeLKBObjBmN
+QSdJQO7e5iNEOdyhIta6A/I=
+-----END CERTIFICATE-----
+
+# Issuer: CN=ACEDICOM Root O=EDICOM OU=PKI
+# Subject: CN=ACEDICOM Root O=EDICOM OU=PKI
+# Label: "ACEDICOM Root"
+# Serial: 7029493972724711941
+# MD5 Fingerprint: 42:81:a0:e2:1c:e3:55:10:de:55:89:42:65:96:22:e6
+# SHA1 Fingerprint: e0:b4:32:2e:b2:f6:a5:68:b6:54:53:84:48:18:4a:50:36:87:43:84
+# SHA256 Fingerprint: 03:95:0f:b4:9a:53:1f:3e:19:91:94:23:98:df:a9:e0:ea:32:d7:ba:1c:dd:9b:c8:5d:b5:7e:d9:40:0b:43:4a
+-----BEGIN CERTIFICATE-----
+MIIFtTCCA52gAwIBAgIIYY3HhjsBggUwDQYJKoZIhvcNAQEFBQAwRDEWMBQGA1UE
+AwwNQUNFRElDT00gUm9vdDEMMAoGA1UECwwDUEtJMQ8wDQYDVQQKDAZFRElDT00x
+CzAJBgNVBAYTAkVTMB4XDTA4MDQxODE2MjQyMloXDTI4MDQxMzE2MjQyMlowRDEW
+MBQGA1UEAwwNQUNFRElDT00gUm9vdDEMMAoGA1UECwwDUEtJMQ8wDQYDVQQKDAZF
+RElDT00xCzAJBgNVBAYTAkVTMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC
+AgEA/5KV4WgGdrQsyFhIyv2AVClVYyT/kGWbEHV7w2rbYgIB8hiGtXxaOLHkWLn7
+09gtn70yN78sFW2+tfQh0hOR2QetAQXW8713zl9CgQr5auODAKgrLlUTY4HKRxx7
+XBZXehuDYAQ6PmXDzQHe3qTWDLqO3tkE7hdWIpuPY/1NFgu3e3eM+SW10W2ZEi5P
+Grjm6gSSrj0RuVFCPYewMYWveVqc/udOXpJPQ/yrOq2lEiZmueIM15jO1FillUAK
+t0SdE3QrwqXrIhWYENiLxQSfHY9g5QYbm8+5eaA9oiM/Qj9r+hwDezCNzmzAv+Yb
+X79nuIQZ1RXve8uQNjFiybwCq0Zfm/4aaJQ0PZCOrfbkHQl/Sog4P75n/TSW9R28
+MHTLOO7VbKvU/PQAtwBbhTIWdjPp2KOZnQUAqhbm84F9b32qhm2tFXTTxKJxqvQU
+fecyuB+81fFOvW8XAjnXDpVCOscAPukmYxHqC9FK/xidstd7LzrZlvvoHpKuE1XI
+2Sf23EgbsCTBheN3nZqk8wwRHQ3ItBTutYJXCb8gWH8vIiPYcMt5bMlL8qkqyPyH
+K9caUPgn6C9D4zq92Fdx/c6mUlv53U3t5fZvie27k5x2IXXwkkwp9y+cAS7+UEae
+ZAwUswdbxcJzbPEHXEUkFDWug/FqTYl6+rPYLWbwNof1K1MCAwEAAaOBqjCBpzAP
+BgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKaz4SsrSbbXc6GqlPUB53NlTKxQ
+MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUprPhKytJttdzoaqU9QHnc2VMrFAw
+RAYDVR0gBD0wOzA5BgRVHSAAMDEwLwYIKwYBBQUHAgEWI2h0dHA6Ly9hY2VkaWNv
+bS5lZGljb21ncm91cC5jb20vZG9jMA0GCSqGSIb3DQEBBQUAA4ICAQDOLAtSUWIm
+fQwng4/F9tqgaHtPkl7qpHMyEVNEskTLnewPeUKzEKbHDZ3Ltvo/Onzqv4hTGzz3
+gvoFNTPhNahXwOf9jU8/kzJPeGYDdwdY6ZXIfj7QeQCM8htRM5u8lOk6e25SLTKe
+I6RF+7YuE7CLGLHdztUdp0J/Vb77W7tH1PwkzQSulgUV1qzOMPPKC8W64iLgpq0i
+5ALudBF/TP94HTXa5gI06xgSYXcGCRZj6hitoocf8seACQl1ThCojz2GuHURwCRi
+ipZ7SkXp7FnFvmuD5uHorLUwHv4FB4D54SMNUI8FmP8sX+g7tq3PgbUhh8oIKiMn
+MCArz+2UW6yyetLHKKGKC5tNSixthT8Jcjxn4tncB7rrZXtaAWPWkFtPF2Y9fwsZ
+o5NjEFIqnxQWWOLcpfShFosOkYuByptZ+thrkQdlVV9SH686+5DdaaVbnG0OLLb6
+zqylfDJKZ0DcMDQj3dcEI2bw/FWAp/tmGYI1Z2JwOV5vx+qQQEQIHriy1tvuWacN
+GHk0vFQYXlPKNFHtRQrmjseCNj6nOGOpMCwXEGCSn1WHElkQwg9naRHMTh5+Spqt
+r0CodaxWkHS4oJyleW/c6RrIaQXpuvoDs3zk4E7Czp3otkYNbn5XOmeUwssfnHdK
+Z05phkOTOPu220+DkdRgfks+KzgHVZhepA==
+-----END CERTIFICATE-----
+
+# Issuer: O=VeriSign, Inc. OU=Class 3 Public Primary Certification Authority
+# Subject: O=VeriSign, Inc. OU=Class 3 Public Primary Certification Authority
+# Label: "Verisign Class 3 Public Primary Certification Authority"
+# Serial: 80507572722862485515306429940691309246
+# MD5 Fingerprint: ef:5a:f1:33:ef:f1:cd:bb:51:02:ee:12:14:4b:96:c4
+# SHA1 Fingerprint: a1:db:63:93:91:6f:17:e4:18:55:09:40:04:15:c7:02:40:b0:ae:6b
+# SHA256 Fingerprint: a4:b6:b3:99:6f:c2:f3:06:b3:fd:86:81:bd:63:41:3d:8c:50:09:cc:4f:a3:29:c2:cc:f0:e2:fa:1b:14:03:05
+-----BEGIN CERTIFICATE-----
+MIICPDCCAaUCEDyRMcsf9tAbDpq40ES/Er4wDQYJKoZIhvcNAQEFBQAwXzELMAkG
+A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz
+cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2
+MDEyOTAwMDAwMFoXDTI4MDgwMjIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV
+BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt
+YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN
+ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE
+BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is
+I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G
+CSqGSIb3DQEBBQUAA4GBABByUqkFFBkyCEHwxWsKzH4PIRnN5GfcX6kb5sroc50i
+2JhucwNhkcV8sEVAbkSdjbCxlnRhLQ2pRdKkkirWmnWXbj9T/UWZYB2oK0z5XqcJ
+2HUw19JlYD1n1khVdWk/kfVIC0dpImmClr7JyDiGSnoscxlIaU5rfGW/D/xwzoiQ
+-----END CERTIFICATE-----
+
+# Issuer: CN=Microsec e-Szigno Root CA 2009 O=Microsec Ltd.
+# Subject: CN=Microsec e-Szigno Root CA 2009 O=Microsec Ltd.
+# Label: "Microsec e-Szigno Root CA 2009"
+# Serial: 14014712776195784473
+# MD5 Fingerprint: f8:49:f4:03:bc:44:2d:83:be:48:69:7d:29:64:fc:b1
+# SHA1 Fingerprint: 89:df:74:fe:5c:f4:0f:4a:80:f9:e3:37:7d:54:da:91:e1:01:31:8e
+# SHA256 Fingerprint: 3c:5f:81:fe:a5:fa:b8:2c:64:bf:a2:ea:ec:af:cd:e8:e0:77:fc:86:20:a7:ca:e5:37:16:3d:f3:6e:db:f3:78
+-----BEGIN CERTIFICATE-----
+MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYD
+VQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0
+ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0G
+CSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTAeFw0wOTA2MTYxMTMwMThaFw0y
+OTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3Qx
+FjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3pp
+Z25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o
+dTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvP
+kd6mJviZpWNwrZuuyjNAfW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tc
+cbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG0IMZfcChEhyVbUr02MelTTMuhTlAdX4U
+fIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKApxn1ntxVUwOXewdI/5n7
+N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm1HxdrtbC
+xkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1
++rUCAwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G
+A1UdDgQWBBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPM
+Pcu1SCOhGnqmKrs0aDAbBgNVHREEFDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqG
+SIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0olZMEyL/azXm4Q5DwpL7v8u8h
+mLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfXI/OMn74dseGk
+ddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775
+tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c
+2Pm2G2JwCz02yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5t
+HMN1Rq41Bab2XD0h7lbwyYIiLXpUq3DDfSJlgnCW
+-----END CERTIFICATE-----
+
+# Issuer: CN=e-Guven Kok Elektronik Sertifika Hizmet Saglayicisi O=Elektronik Bilgi Guvenligi A.S.
+# Subject: CN=e-Guven Kok Elektronik Sertifika Hizmet Saglayicisi O=Elektronik Bilgi Guvenligi A.S.
+# Label: "E-Guven Kok Elektronik Sertifika Hizmet Saglayicisi"
+# Serial: 91184789765598910059173000485363494069
+# MD5 Fingerprint: 3d:41:29:cb:1e:aa:11:74:cd:5d:b0:62:af:b0:43:5b
+# SHA1 Fingerprint: dd:e1:d2:a9:01:80:2e:1d:87:5e:84:b3:80:7e:4b:b1:fd:99:41:34
+# SHA256 Fingerprint: e6:09:07:84:65:a4:19:78:0c:b6:ac:4c:1c:0b:fb:46:53:d9:d9:cc:6e:b3:94:6e:b7:f3:d6:99:97:ba:d5:98
+-----BEGIN CERTIFICATE-----
+MIIDtjCCAp6gAwIBAgIQRJmNPMADJ72cdpW56tustTANBgkqhkiG9w0BAQUFADB1
+MQswCQYDVQQGEwJUUjEoMCYGA1UEChMfRWxla3Ryb25payBCaWxnaSBHdXZlbmxp
+Z2kgQS5TLjE8MDoGA1UEAxMzZS1HdXZlbiBLb2sgRWxla3Ryb25payBTZXJ0aWZp
+a2EgSGl6bWV0IFNhZ2xheWljaXNpMB4XDTA3MDEwNDExMzI0OFoXDTE3MDEwNDEx
+MzI0OFowdTELMAkGA1UEBhMCVFIxKDAmBgNVBAoTH0VsZWt0cm9uaWsgQmlsZ2kg
+R3V2ZW5saWdpIEEuUy4xPDA6BgNVBAMTM2UtR3V2ZW4gS29rIEVsZWt0cm9uaWsg
+U2VydGlmaWthIEhpem1ldCBTYWdsYXlpY2lzaTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBAMMSIJ6wXgBljU5Gu4Bc6SwGl9XzcslwuedLZYDBS75+PNdU
+MZTe1RK6UxYC6lhj71vY8+0qGqpxSKPcEC1fX+tcS5yWCEIlKBHMilpiAVDV6wlT
+L/jDj/6z/P2douNffb7tC+Bg62nsM+3YjfsSSYMAyYuXjDtzKjKzEve5TfL0TW3H
+5tYmNwjy2f1rXKPlSFxYvEK+A1qBuhw1DADT9SN+cTAIJjjcJRFHLfO6IxClv7wC
+90Nex/6wN1CZew+TzuZDLMN+DfIcQ2Zgy2ExR4ejT669VmxMvLz4Bcpk9Ok0oSy1
+c+HCPujIyTQlCFzz7abHlJ+tiEMl1+E5YP6sOVkCAwEAAaNCMEAwDgYDVR0PAQH/
+BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFJ/uRLOU1fqRTy7ZVZoE
+VtstxNulMA0GCSqGSIb3DQEBBQUAA4IBAQB/X7lTW2M9dTLn+sR0GstG30ZpHFLP
+qk/CaOv/gKlR6D1id4k9CnU58W5dF4dvaAXBlGzZXd/aslnLpRCKysw5zZ/rTt5S
+/wzw9JKp8mxTq5vSR6AfdPebmvEvFZ96ZDAYBzwqD2fK/A+JYZ1lpTzlvBNbCNvj
+/+27BrtqBrF6T2XGgv0enIu1De5Iu7i9qgi0+6N8y5/NkHZchpZ4Vwpm+Vganf2X
+KWDeEaaQHBkc7gGWIjQ0LpH5t8Qn0Xvmv/uARFoW5evg1Ao4vOSR49XrXMGs3xtq
+fJ7lddK2l4fbzIcrQzqECK+rPNv3PGYxhrCdU3nt+CPeQuMtgvEP5fqX
+-----END CERTIFICATE-----
+
+# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R3
+# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R3
+# Label: "GlobalSign Root CA - R3"
+# Serial: 4835703278459759426209954
+# MD5 Fingerprint: c5:df:b8:49:ca:05:13:55:ee:2d:ba:1a:c3:3e:b0:28
+# SHA1 Fingerprint: d6:9b:56:11:48:f0:1c:77:c5:45:78:c1:09:26:df:5b:85:69:76:ad
+# SHA256 Fingerprint: cb:b5:22:d7:b7:f1:27:ad:6a:01:13:86:5b:df:1c:d4:10:2e:7d:07:59:af:63:5a:7c:f4:72:0d:c9:63:c5:3b
+-----BEGIN CERTIFICATE-----
+MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G
+A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp
+Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4
+MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG
+A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8
+RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT
+gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm
+KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd
+QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ
+XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw
+DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o
+LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU
+RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp
+jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK
+6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX
+mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs
+Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH
+WD9f
+-----END CERTIFICATE-----
+
+# Issuer: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068
+# Subject: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068
+# Label: "Autoridad de Certificacion Firmaprofesional CIF A62634068"
+# Serial: 6047274297262753887
+# MD5 Fingerprint: 73:3a:74:7a:ec:bb:a3:96:a6:c2:e4:e2:c8:9b:c0:c3
+# SHA1 Fingerprint: ae:c5:fb:3f:c8:e1:bf:c4:e5:4f:03:07:5a:9a:e8:00:b7:f7:b6:fa
+# SHA256 Fingerprint: 04:04:80:28:bf:1f:28:64:d4:8f:9a:d4:d8:32:94:36:6a:82:88:56:55:3f:3b:14:30:3f:90:14:7f:5d:40:ef
+-----BEGIN CERTIFICATE-----
+MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UE
+BhMCRVMxQjBABgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1h
+cHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEy
+MzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUg
+Q2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjgwggIi
+MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDDUtd9
+thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQM
+cas9UX4PB99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefG
+L9ItWY16Ck6WaVICqjaY7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15i
+NA9wBj4gGFrO93IbJWyTdBSTo3OxDqqHECNZXyAFGUftaI6SEspd/NYrspI8IM/h
+X68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyIplD9amML9ZMWGxmPsu2b
+m8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctXMbScyJCy
+Z/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirja
+EbsXLZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/T
+KI8xWVvTyQKmtFLKbpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF
+6NkBiDkal4ZkQdU7hwxu+g/GvUgUvzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVh
+OSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1UdEwEB/wQIMAYBAf8CAQEwDgYD
+VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNHDhpkLzCBpgYD
+VR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp
+cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBv
+ACAAZABlACAAbABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBl
+AGwAbwBuAGEAIAAwADgAMAAxADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF
+661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx51tkljYyGOylMnfX40S2wBEqgLk9
+am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qkR71kMrv2JYSiJ0L1
+ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaPT481
+PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS
+3a/DTg4fJl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5k
+SeTy36LssUzAKh3ntLFlosS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF
+3dvd6qJ2gHN99ZwExEWN57kci57q13XRcrHedUTnQn3iV2t93Jm8PYMo6oCTjcVM
+ZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoRsaS8I8nkvof/uZS2+F0g
+StRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTDKCOM/icz
+Q0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQB
+jLMi6Et8Vcad+qMUu2WFbm5PEn4KPJ2V
+-----END CERTIFICATE-----
+
+# Issuer: CN=Izenpe.com O=IZENPE S.A.
+# Subject: CN=Izenpe.com O=IZENPE S.A.
+# Label: "Izenpe.com"
+# Serial: 917563065490389241595536686991402621
+# MD5 Fingerprint: a6:b0:cd:85:80:da:5c:50:34:a3:39:90:2f:55:67:73
+# SHA1 Fingerprint: 2f:78:3d:25:52:18:a7:4a:65:39:71:b5:2c:a2:9c:45:15:6f:e9:19
+# SHA256 Fingerprint: 25:30:cc:8e:98:32:15:02:ba:d9:6f:9b:1f:ba:1b:09:9e:2d:29:9e:0f:45:48:bb:91:4f:36:3b:c0:d4:53:1f
+-----BEGIN CERTIFICATE-----
+MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4
+MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6
+ZW5wZS5jb20wHhcNMDcxMjEzMTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYD
+VQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5j
+b20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ03rKDx6sp4boFmVq
+scIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAKClaO
+xdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6H
+LmYRY2xU+zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFX
+uaOKmMPsOzTFlUFpfnXCPCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQD
+yCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxTOTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+
+JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbKF7jJeodWLBoBHmy+E60Q
+rLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK0GqfvEyN
+BjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8L
+hij+0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIB
+QFqNeb+Lz0vPqhbBleStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+
+HMh3/1uaD7euBUbl8agW7EekFwIDAQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2lu
+Zm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+SVpFTlBFIFMuQS4gLSBDSUYg
+QTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBGNjIgUzgxQzBB
+BgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx
+MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
+AQYwHQYDVR0OBBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUA
+A4ICAQB4pgwWSp9MiDrAyw6lFn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWb
+laQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbgakEyrkgPH7UIBzg/YsfqikuFgba56
+awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8qhT/AQKM6WfxZSzwo
+JNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Csg1lw
+LDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCT
+VyvehQP5aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGk
+LhObNA5me0mrZJfQRsN5nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJb
+UjWumDqtujWTI6cfSN01RpiyEGjkpTHCClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/
+QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZoQ0iy2+tzJOeRf1SktoA+
+naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1ZWrOZyGls
+QyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw==
+-----END CERTIFICATE-----
+
+# Issuer: CN=Chambers of Commerce Root - 2008 O=AC Camerfirma S.A.
+# Subject: CN=Chambers of Commerce Root - 2008 O=AC Camerfirma S.A.
+# Label: "Chambers of Commerce Root - 2008"
+# Serial: 11806822484801597146
+# MD5 Fingerprint: 5e:80:9e:84:5a:0e:65:0b:17:02:f3:55:18:2a:3e:d7
+# SHA1 Fingerprint: 78:6a:74:ac:76:ab:14:7f:9c:6a:30:50:ba:9e:a8:7e:fe:9a:ce:3c
+# SHA256 Fingerprint: 06:3e:4a:fa:c4:91:df:d3:32:f3:08:9b:85:42:e9:46:17:d8:93:d7:fe:94:4e:10:a7:93:7e:e2:9d:96:93:c0
+-----BEGIN CERTIFICATE-----
+MIIHTzCCBTegAwIBAgIJAKPaQn6ksa7aMA0GCSqGSIb3DQEBBQUAMIGuMQswCQYD
+VQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0
+IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3
+MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xKTAnBgNVBAMTIENoYW1iZXJz
+IG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4MB4XDTA4MDgwMTEyMjk1MFoXDTM4MDcz
+MTEyMjk1MFowga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNlZSBj
+dXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29tL2FkZHJlc3MpMRIw
+EAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVyZmlybWEgUy5BLjEp
+MCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDgwggIiMA0G
+CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCvAMtwNyuAWko6bHiUfaN/Gh/2NdW9
+28sNRHI+JrKQUrpjOyhYb6WzbZSm891kDFX29ufyIiKAXuFixrYp4YFs8r/lfTJq
+VKAyGVn+H4vXPWCGhSRv4xGzdz4gljUha7MI2XAuZPeEklPWDrCQiorjh40G072Q
+DuKZoRuGDtqaCrsLYVAGUvGef3bsyw/QHg3PmTA9HMRFEFis1tPo1+XqxQEHd9ZR
+5gN/ikilTWh1uem8nk4ZcfUyS5xtYBkL+8ydddy/Js2Pk3g5eXNeJQ7KXOt3EgfL
+ZEFHcpOrUMPrCXZkNNI5t3YRCQ12RcSprj1qr7V9ZS+UWBDsXHyvfuK2GNnQm05a
+Sd+pZgvMPMZ4fKecHePOjlO+Bd5gD2vlGts/4+EhySnB8esHnFIbAURRPHsl18Tl
+UlRdJQfKFiC4reRB7noI/plvg6aRArBsNlVq5331lubKgdaX8ZSD6e2wsWsSaR6s
++12pxZjptFtYer49okQ6Y1nUCyXeG0+95QGezdIp1Z8XGQpvvwyQ0wlf2eOKNcx5
+Wk0ZN5K3xMGtr/R5JJqyAQuxr1yW84Ay+1w9mPGgP0revq+ULtlVmhduYJ1jbLhj
+ya6BXBg14JC7vjxPNyK5fuvPnnchpj04gftI2jE9K+OJ9dC1vX7gUMQSibMjmhAx
+hduub+84Mxh2EQIDAQABo4IBbDCCAWgwEgYDVR0TAQH/BAgwBgEB/wIBDDAdBgNV
+HQ4EFgQU+SSsD7K1+HnA+mCIG8TZTQKeFxkwgeMGA1UdIwSB2zCB2IAU+SSsD7K1
++HnA+mCIG8TZTQKeFxmhgbSkgbEwga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpN
+YWRyaWQgKHNlZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29t
+L2FkZHJlc3MpMRIwEAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVy
+ZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAt
+IDIwMDiCCQCj2kJ+pLGu2jAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRV
+HSAAMCowKAYIKwYBBQUHAgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20w
+DQYJKoZIhvcNAQEFBQADggIBAJASryI1wqM58C7e6bXpeHxIvj99RZJe6dqxGfwW
+PJ+0W2aeaufDuV2I6A+tzyMP3iU6XsxPpcG1Lawk0lgH3qLPaYRgM+gQDROpI9CF
+5Y57pp49chNyM/WqfcZjHwj0/gF/JM8rLFQJ3uIrbZLGOU8W6jx+ekbURWpGqOt1
+glanq6B8aBMz9p0w8G8nOSQjKpD9kCk18pPfNKXG9/jvjA9iSnyu0/VU+I22mlaH
+FoI6M6taIgj3grrqLuBHmrS1RaMFO9ncLkVAO+rcf+g769HsJtg1pDDFOqxXnrN2
+pSB7+R5KBWIBpih1YJeSDW4+TTdDDZIVnBgizVGZoCkaPF+KMjNbMMeJL0eYD6MD
+xvbxrN8y8NmBGuScvfaAFPDRLLmF9dijscilIeUcE5fuDr3fKanvNFNb0+RqE4QG
+tjICxFKuItLcsiFCGtpA8CnJ7AoMXOLQusxI0zcKzBIKinmwPQN/aUv0NCB9szTq
+jktk9T79syNnFQ0EuPAtwQlRPLJsFfClI9eDdOTlLsn+mCdCxqvGnrDQWzilm1De
+fhiYtUU79nm06PcaewaD+9CL2rvHvRirCG88gGtAPxkZumWK5r7VXNM21+9AUiRg
+OGcEMeyP84LG3rlV8zsxkVrctQgVrXYlCg17LofiDKYGvCYQbTed7N14jHyAxfDZ
+d0jQ
+-----END CERTIFICATE-----
+
+# Issuer: CN=Global Chambersign Root - 2008 O=AC Camerfirma S.A.
+# Subject: CN=Global Chambersign Root - 2008 O=AC Camerfirma S.A.
+# Label: "Global Chambersign Root - 2008"
+# Serial: 14541511773111788494
+# MD5 Fingerprint: 9e:80:ff:78:01:0c:2e:c1:36:bd:fe:96:90:6e:08:f3
+# SHA1 Fingerprint: 4a:bd:ee:ec:95:0d:35:9c:89:ae:c7:52:a1:2c:5b:29:f6:d6:aa:0c
+# SHA256 Fingerprint: 13:63:35:43:93:34:a7:69:80:16:a0:d3:24:de:72:28:4e:07:9d:7b:52:20:bb:8f:bd:74:78:16:ee:be:ba:ca
+-----BEGIN CERTIFICATE-----
+MIIHSTCCBTGgAwIBAgIJAMnN0+nVfSPOMA0GCSqGSIb3DQEBBQUAMIGsMQswCQYD
+VQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0
+IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3
+MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMTHkdsb2JhbCBD
+aGFtYmVyc2lnbiBSb290IC0gMjAwODAeFw0wODA4MDExMjMxNDBaFw0zODA3MzEx
+MjMxNDBaMIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3Vy
+cmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAG
+A1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAl
+BgNVBAMTHkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwODCCAiIwDQYJKoZI
+hvcNAQEBBQADggIPADCCAgoCggIBAMDfVtPkOpt2RbQT2//BthmLN0EYlVJH6xed
+KYiONWwGMi5HYvNJBL99RDaxccy9Wglz1dmFRP+RVyXfXjaOcNFccUMd2drvXNL7
+G706tcuto8xEpw2uIRU/uXpbknXYpBI4iRmKt4DS4jJvVpyR1ogQC7N0ZJJ0YPP2
+zxhPYLIj0Mc7zmFLmY/CDNBAspjcDahOo7kKrmCgrUVSY7pmvWjg+b4aqIG7HkF4
+ddPB/gBVsIdU6CeQNR1MM62X/JcumIS/LMmjv9GYERTtY/jKmIhYF5ntRQOXfjyG
+HoiMvvKRhI9lNNgATH23MRdaKXoKGCQwoze1eqkBfSbW+Q6OWfH9GzO1KTsXO0G2
+Id3UwD2ln58fQ1DJu7xsepeY7s2MH/ucUa6LcL0nn3HAa6x9kGbo1106DbDVwo3V
+yJ2dwW3Q0L9R5OP4wzg2rtandeavhENdk5IMagfeOx2YItaswTXbo6Al/3K1dh3e
+beksZixShNBFks4c5eUzHdwHU1SjqoI7mjcv3N2gZOnm3b2u/GSFHTynyQbehP9r
+6GsaPMWis0L7iwk+XwhSx2LE1AVxv8Rk5Pihg+g+EpuoHtQ2TS9x9o0o9oOpE9Jh
+wZG7SMA0j0GMS0zbaRL/UJScIINZc+18ofLx/d33SdNDWKBWY8o9PeU1VlnpDsog
+zCtLkykPAgMBAAGjggFqMIIBZjASBgNVHRMBAf8ECDAGAQH/AgEMMB0GA1UdDgQW
+BBS5CcqcHtvTbDprru1U8VuTBjUuXjCB4QYDVR0jBIHZMIHWgBS5CcqcHtvTbDpr
+ru1U8VuTBjUuXqGBsqSBrzCBrDELMAkGA1UEBhMCRVUxQzBBBgNVBAcTOk1hZHJp
+ZCAoc2VlIGN1cnJlbnQgYWRkcmVzcyBhdCB3d3cuY2FtZXJmaXJtYS5jb20vYWRk
+cmVzcykxEjAQBgNVBAUTCUE4Mjc0MzI4NzEbMBkGA1UEChMSQUMgQ2FtZXJmaXJt
+YSBTLkEuMScwJQYDVQQDEx5HbG9iYWwgQ2hhbWJlcnNpZ24gUm9vdCAtIDIwMDiC
+CQDJzdPp1X0jzjAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCow
+KAYIKwYBBQUHAgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZI
+hvcNAQEFBQADggIBAICIf3DekijZBZRG/5BXqfEv3xoNa/p8DhxJJHkn2EaqbylZ
+UohwEurdPfWbU1Rv4WCiqAm57OtZfMY18dwY6fFn5a+6ReAJ3spED8IXDneRRXoz
+X1+WLGiLwUePmJs9wOzL9dWCkoQ10b42OFZyMVtHLaoXpGNR6woBrX/sdZ7LoR/x
+fxKxueRkf2fWIyr0uDldmOghp+G9PUIadJpwr2hsUF1Jz//7Dl3mLEfXgTpZALVz
+a2Mg9jFFCDkO9HB+QHBaP9BrQql0PSgvAm11cpUJjUhjxsYjV5KTXjXBjfkK9yyd
+Yhz2rXzdpjEetrHHfoUm+qRqtdpjMNHvkzeyZi99Bffnt0uYlDXA2TopwZ2yUDMd
+SqlapskD7+3056huirRXhOukP9DuqqqHW2Pok+JrqNS4cnhrG+055F3Lm6qH1U9O
+AP7Zap88MQ8oAgF9mOinsKJknnn4SPIVqczmyETrP3iZ8ntxPjzxmKfFGBI/5rso
+M0LpRQp8bfKGeS/Fghl9CYl8slR2iK7ewfPM4W7bMdaTrpmg7yVqc5iJWzouE4ge
+v8CSlDQb4ye3ix5vQv/n6TebUB0tovkC7stYWDpxvGjjqsGvHCgfotwjZT+B6q6Z
+09gwzxMNTxXJhLynSC34MCN32EZLeW32jO06f2ARePTpm67VVMB0gNELQp/B
+-----END CERTIFICATE-----
+
+# Issuer: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc.
+# Subject: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc.
+# Label: "Go Daddy Root Certificate Authority - G2"
+# Serial: 0
+# MD5 Fingerprint: 80:3a:bc:22:c1:e6:fb:8d:9b:3b:27:4a:32:1b:9a:01
+# SHA1 Fingerprint: 47:be:ab:c9:22:ea:e8:0e:78:78:34:62:a7:9f:45:c2:54:fd:e6:8b
+# SHA256 Fingerprint: 45:14:0b:32:47:eb:9c:c8:c5:b4:f0:d7:b5:30:91:f7:32:92:08:9e:6e:5a:63:e2:74:9d:d3:ac:a9:19:8e:da
+-----BEGIN CERTIFICATE-----
+MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx
+EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT
+EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp
+ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIz
+NTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH
+EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8GA1UE
+AxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIw
+DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKD
+E6bFIEMBO4Tx5oVJnyfq9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH
+/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD+qK+ihVqf94Lw7YZFAXK6sOoBJQ7Rnwy
+DfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutdfMh8+7ArU6SSYmlRJQVh
+GkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMlNAJWJwGR
+tDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEA
+AaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE
+FDqahQcQZyi27/a9BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmX
+WWcDYfF+OwYxdS2hII5PZYe096acvNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu
+9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r5N9ss4UXnT3ZJE95kTXWXwTr
+gIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYVN8Gb5DKj7Tjo
+2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO
+LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI
+4uJEvlz36hz1
+-----END CERTIFICATE-----
+
+# Issuer: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc.
+# Subject: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc.
+# Label: "Starfield Root Certificate Authority - G2"
+# Serial: 0
+# MD5 Fingerprint: d6:39:81:c6:52:7e:96:69:fc:fc:ca:66:ed:05:f2:96
+# SHA1 Fingerprint: b5:1c:06:7c:ee:2b:0c:3d:f8:55:ab:2d:92:f4:fe:39:d4:e7:0f:0e
+# SHA256 Fingerprint: 2c:e1:cb:0b:f9:d2:f9:e1:02:99:3f:be:21:51:52:c3:b2:dd:0c:ab:de:1c:68:e5:31:9b:83:91:54:db:b7:f5
+-----BEGIN CERTIFICATE-----
+MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx
+EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT
+HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs
+ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw
+MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6
+b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj
+aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp
+Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg
+nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1
+HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N
+Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN
+dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0
+HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO
+BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G
+CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU
+sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3
+4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg
+8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K
+pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1
+mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0
+-----END CERTIFICATE-----
+
+# Issuer: CN=Starfield Services Root Certificate Authority - G2 O=Starfield Technologies, Inc.
+# Subject: CN=Starfield Services Root Certificate Authority - G2 O=Starfield Technologies, Inc.
+# Label: "Starfield Services Root Certificate Authority - G2"
+# Serial: 0
+# MD5 Fingerprint: 17:35:74:af:7b:61:1c:eb:f4:f9:3c:e2:ee:40:f9:a2
+# SHA1 Fingerprint: 92:5a:8f:8d:2c:6d:04:e0:66:5f:59:6a:ff:22:d8:63:e8:25:6f:3f
+# SHA256 Fingerprint: 56:8d:69:05:a2:c8:87:08:a4:b3:02:51:90:ed:cf:ed:b1:97:4a:60:6a:13:c6:e5:29:0f:cb:2a:e6:3e:da:b5
+-----BEGIN CERTIFICATE-----
+MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMx
+EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT
+HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVs
+ZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5
+MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNVBAYTAlVTMRAwDgYD
+VQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFy
+ZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2Vy
+dmljZXMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20p
+OsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm2
+8xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4PahHQUw2eeBGg6345AWh1K
+Ts9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLPLJGmpufe
+hRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk
+6mFBrMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAw
+DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+q
+AdcwKziIorhtSpzyEZGDMA0GCSqGSIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMI
+bw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPPE95Dz+I0swSdHynVv/heyNXB
+ve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTyxQGjhdByPq1z
+qwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd
+iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn
+0q23KXB56jzaYyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCN
+sSi6
+-----END CERTIFICATE-----
+
+# Issuer: CN=AffirmTrust Commercial O=AffirmTrust
+# Subject: CN=AffirmTrust Commercial O=AffirmTrust
+# Label: "AffirmTrust Commercial"
+# Serial: 8608355977964138876
+# MD5 Fingerprint: 82:92:ba:5b:ef:cd:8a:6f:a6:3d:55:f9:84:f6:d6:b7
+# SHA1 Fingerprint: f9:b5:b6:32:45:5f:9c:be:ec:57:5f:80:dc:e9:6e:2c:c7:b2:78:b7
+# SHA256 Fingerprint: 03:76:ab:1d:54:c5:f9:80:3c:e4:b2:e2:01:a0:ee:7e:ef:7b:57:b6:36:e8:a9:3c:9b:8d:48:60:c9:6f:5f:a7
+-----BEGIN CERTIFICATE-----
+MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UE
+BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz
+dCBDb21tZXJjaWFsMB4XDTEwMDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDEL
+MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp
+cm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+AQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6EqdbDuKP
+Hx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yr
+ba0F8PrVC8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPAL
+MeIrJmqbTFeurCA+ukV6BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1
+yHp52UKqK39c/s4mT6NmgTWvRLpUHhwwMmWd5jyTXlBOeuM61G7MGvv50jeuJCqr
+VwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNVHQ4EFgQUnZPGU4teyq8/
+nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ
+KoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYG
+XUPGhi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNj
+vbz4YYCanrHOQnDiqX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivt
+Z8SOyUOyXGsViQK8YvxO8rUzqrJv0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9g
+N53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0khsUlHRUe072o0EclNmsxZt9YC
+nlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8=
+-----END CERTIFICATE-----
+
+# Issuer: CN=AffirmTrust Networking O=AffirmTrust
+# Subject: CN=AffirmTrust Networking O=AffirmTrust
+# Label: "AffirmTrust Networking"
+# Serial: 8957382827206547757
+# MD5 Fingerprint: 42:65:ca:be:01:9a:9a:4c:a9:8c:41:49:cd:c0:d5:7f
+# SHA1 Fingerprint: 29:36:21:02:8b:20:ed:02:f5:66:c5:32:d1:d6:ed:90:9f:45:00:2f
+# SHA256 Fingerprint: 0a:81:ec:5a:92:97:77:f1:45:90:4a:f3:8d:5d:50:9f:66:b5:e2:c5:8f:cd:b5:31:05:8b:0e:17:f3:f0:b4:1b
+-----BEGIN CERTIFICATE-----
+MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UE
+BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz
+dCBOZXR3b3JraW5nMB4XDTEwMDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDEL
+MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp
+cm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+AQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SEHi3y
+YJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbua
+kCNrmreIdIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRL
+QESxG9fhwoXA3hA/Pe24/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp
+6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gbh+0t+nvujArjqWaJGctB+d1ENmHP4ndG
+yH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNVHQ4EFgQUBx/S55zawm6i
+QLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ
+KoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfO
+tDIuUFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzu
+QY0x2+c06lkh1QF612S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZ
+Lgo/bNjR9eUJtGxUAArgFU2HdW23WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4u
+olu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9/ZFvgrG+CJPbFEfxojfHRZ48
+x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s=
+-----END CERTIFICATE-----
+
+# Issuer: CN=AffirmTrust Premium O=AffirmTrust
+# Subject: CN=AffirmTrust Premium O=AffirmTrust
+# Label: "AffirmTrust Premium"
+# Serial: 7893706540734352110
+# MD5 Fingerprint: c4:5d:0e:48:b6:ac:28:30:4e:0a:bc:f9:38:16:87:57
+# SHA1 Fingerprint: d8:a6:33:2c:e0:03:6f:b1:85:f6:63:4f:7d:6a:06:65:26:32:28:27
+# SHA256 Fingerprint: 70:a7:3f:7f:37:6b:60:07:42:48:90:45:34:b1:14:82:d5:bf:0e:69:8e:cc:49:8d:f5:25:77:eb:f2:e9:3b:9a
+-----BEGIN CERTIFICATE-----
+MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UE
+BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVz
+dCBQcmVtaXVtMB4XDTEwMDEyOTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkG
+A1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1U
+cnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxBLf
+qV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtnBKAQ
+JG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ
++jjeRFcV5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrS
+s8PhaJyJ+HoAVt70VZVs+7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5
+HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmdGPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d7
+70O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5Rp9EixAqnOEhss/n/fauG
+V+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NIS+LI+H+S
+qHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S
+5u046uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4Ia
+C1nEWTJ3s7xgaVY5/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TX
+OwF0lkLgAOIua+rF7nKsu7/+6qqo+Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYE
+FJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/
+BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByvMiPIs0laUZx2
+KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg
+Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B
+8OWycvpEgjNC6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQ
+MKSOyARiqcTtNd56l+0OOF6SL5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc
+0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK+4w1IX2COPKpVJEZNZOUbWo6xbLQ
+u4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmVBtWVyuEklut89pMF
+u+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFgIxpH
+YoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8
+GKa1qF60g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaO
+RtGdFNrHF+QFlozEJLUbzxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6e
+KeC2uAloGRwYQw==
+-----END CERTIFICATE-----
+
+# Issuer: CN=AffirmTrust Premium ECC O=AffirmTrust
+# Subject: CN=AffirmTrust Premium ECC O=AffirmTrust
+# Label: "AffirmTrust Premium ECC"
+# Serial: 8401224907861490260
+# MD5 Fingerprint: 64:b0:09:55:cf:b1:d5:99:e2:be:13:ab:a6:5d:ea:4d
+# SHA1 Fingerprint: b8:23:6b:00:2f:1d:16:86:53:01:55:6c:11:a4:37:ca:eb:ff:c3:bb
+# SHA256 Fingerprint: bd:71:fd:f6:da:97:e4:cf:62:d1:64:7a:dd:25:81:b0:7d:79:ad:f8:39:7e:b4:ec:ba:9c:5e:84:88:82:14:23
+-----BEGIN CERTIFICATE-----
+MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMC
+VVMxFDASBgNVBAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQ
+cmVtaXVtIEVDQzAeFw0xMDAxMjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJ
+BgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1UcnVzdDEgMB4GA1UEAwwXQWZmaXJt
+VHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQNMF4bFZ0D
+0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQN8O9
+ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0G
+A1UdDgQWBBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4G
+A1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/Vs
+aobgxCd05DhT1wV/GzTjxi+zygk8N53X57hG8f2h4nECMEJZh0PUUd+60wkyWs6I
+flc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKMeQ==
+-----END CERTIFICATE-----
+
+# Issuer: CN=Certum Trusted Network CA O=Unizeto Technologies S.A. OU=Certum Certification Authority
+# Subject: CN=Certum Trusted Network CA O=Unizeto Technologies S.A. OU=Certum Certification Authority
+# Label: "Certum Trusted Network CA"
+# Serial: 279744
+# MD5 Fingerprint: d5:e9:81:40:c5:18:69:fc:46:2c:89:75:62:0f:aa:78
+# SHA1 Fingerprint: 07:e0:32:e0:20:b7:2c:3f:19:2f:06:28:a2:59:3a:19:a7:0f:06:9e
+# SHA256 Fingerprint: 5c:58:46:8d:55:f5:8e:49:7e:74:39:82:d2:b5:00:10:b6:d1:65:37:4a:cf:83:a7:d4:a3:2d:b7:68:c4:40:8e
+-----BEGIN CERTIFICATE-----
+MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBM
+MSIwIAYDVQQKExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5D
+ZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBU
+cnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIyMTIwNzM3WhcNMjkxMjMxMTIwNzM3
+WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMg
+Uy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSIw
+IAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0B
+AQEFAAOCAQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rH
+UV+rpDKmYYe2bg+G0jACl/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LM
+TXPb865Px1bVWqeWifrzq2jUI4ZZJ88JJ7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVU
+BBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4fOQtf/WsX+sWn7Et0brM
+kUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0cvW0QM8x
+AcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNV
+HRMBAf8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNV
+HQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15y
+sHhE49wcrwn9I0j6vSrEuVUEtRCjjSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfL
+I9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1mS1FhIrlQgnXdAIv94nYmem8
+J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5ajZt3hrvJBW8qY
+VoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI
+03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw=
+-----END CERTIFICATE-----
+
+# Issuer: CN=Certinomis - Autorité Racine O=Certinomis OU=0002 433998903
+# Subject: CN=Certinomis - Autorité Racine O=Certinomis OU=0002 433998903
+# Label: "Certinomis - Autorité Racine"
+# Serial: 1
+# MD5 Fingerprint: 7f:30:78:8c:03:e3:ca:c9:0a:e2:c9:ea:1e:aa:55:1a
+# SHA1 Fingerprint: 2e:14:da:ec:28:f0:fa:1e:8e:38:9a:4e:ab:eb:26:c0:0a:d3:83:c3
+# SHA256 Fingerprint: fc:bf:e2:88:62:06:f7:2b:27:59:3c:8b:07:02:97:e1:2d:76:9e:d1:0e:d7:93:07:05:a8:09:8e:ff:c1:4d:17
+-----BEGIN CERTIFICATE-----
+MIIFnDCCA4SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJGUjET
+MBEGA1UEChMKQ2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxJjAk
+BgNVBAMMHUNlcnRpbm9taXMgLSBBdXRvcml0w6kgUmFjaW5lMB4XDTA4MDkxNzA4
+Mjg1OVoXDTI4MDkxNzA4Mjg1OVowYzELMAkGA1UEBhMCRlIxEzARBgNVBAoTCkNl
+cnRpbm9taXMxFzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMSYwJAYDVQQDDB1DZXJ0
+aW5vbWlzIC0gQXV0b3JpdMOpIFJhY2luZTCCAiIwDQYJKoZIhvcNAQEBBQADggIP
+ADCCAgoCggIBAJ2Fn4bT46/HsmtuM+Cet0I0VZ35gb5j2CN2DpdUzZlMGvE5x4jY
+F1AMnmHawE5V3udauHpOd4cN5bjr+p5eex7Ezyh0x5P1FMYiKAT5kcOrJ3NqDi5N
+8y4oH3DfVS9O7cdxbwlyLu3VMpfQ8Vh30WC8Tl7bmoT2R2FFK/ZQpn9qcSdIhDWe
+rP5pqZ56XjUl+rSnSTV3lqc2W+HN3yNw2F1MpQiD8aYkOBOo7C+ooWfHpi2GR+6K
+/OybDnT0K0kCe5B1jPyZOQE51kqJ5Z52qz6WKDgmi92NjMD2AR5vpTESOH2VwnHu
+7XSu5DaiQ3XV8QCb4uTXzEIDS3h65X27uK4uIJPT5GHfceF2Z5c/tt9qc1pkIuVC
+28+BA5PY9OMQ4HL2AHCs8MF6DwV/zzRpRbWT5BnbUhYjBYkOjUjkJW+zeL9i9Qf6
+lSTClrLooyPCXQP8w9PlfMl1I9f09bze5N/NgL+RiH2nE7Q5uiy6vdFrzPOlKO1E
+nn1So2+WLhl+HPNbxxaOu2B9d2ZHVIIAEWBsMsGoOBvrbpgT1u449fCfDu/+MYHB
+0iSVL1N6aaLwD4ZFjliCK0wi1F6g530mJ0jfJUaNSih8hp75mxpZuWW/Bd22Ql09
+5gBIgl4g9xGC3srYn+Y3RyYe63j3YcNBZFgCQfna4NH4+ej9Uji29YnfAgMBAAGj
+WzBZMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBQN
+jLZh2kS40RR9w759XkjwzspqsDAXBgNVHSAEEDAOMAwGCiqBegFWAgIAAQEwDQYJ
+KoZIhvcNAQEFBQADggIBACQ+YAZ+He86PtvqrxyaLAEL9MW12Ukx9F1BjYkMTv9s
+ov3/4gbIOZ/xWqndIlgVqIrTseYyCYIDbNc/CMf4uboAbbnW/FIyXaR/pDGUu7ZM
+OH8oMDX/nyNTt7buFHAAQCvaR6s0fl6nVjBhK4tDrP22iCj1a7Y+YEq6QpA0Z43q
+619FVDsXrIvkxmUP7tCMXWY5zjKn2BCXwH40nJ+U8/aGH88bc62UeYdocMMzpXDn
+2NU4lG9jeeu/Cg4I58UvD0KgKxRA/yHgBcUn4YQRE7rWhh1BCxMjidPJC+iKunqj
+o3M3NYB9Ergzd0A4wPpeMNLytqOx1qKVl4GbUu1pTP+A5FPbVFsDbVRfsbjvJL1v
+nxHDx2TCDyhihWZeGnuyt++uNckZM6i4J9szVb9o4XVIRFb7zdNIu0eJOqxp9YDG
+5ERQL1TEqkPFMTFYvZbF6nVsmnWxTfj3l/+WFvKXTej28xH5On2KOG4Ey+HTRRWq
+pdEdnV1j6CTmNhTih60bWfVEm/vXd3wfAXBioSAaosUaKPQhA+4u2cGA6rnZgtZb
+dsLLO7XSAPCjDuGtbkD326C00EauFddEwk01+dIL8hf2rGbVJLJP0RyZwG71fet0
+BLj5TXcJ17TPBzAJ8bgAVtkXFhYKK4bfjwEZGuW7gmP/vgt2Fl43N+bYdJeimUV5
+-----END CERTIFICATE-----
+
+# Issuer: CN=Root CA Generalitat Valenciana O=Generalitat Valenciana OU=PKIGVA
+# Subject: CN=Root CA Generalitat Valenciana O=Generalitat Valenciana OU=PKIGVA
+# Label: "Root CA Generalitat Valenciana"
+# Serial: 994436456
+# MD5 Fingerprint: 2c:8c:17:5e:b1:54:ab:93:17:b5:36:5a:db:d1:c6:f2
+# SHA1 Fingerprint: a0:73:e5:c5:bd:43:61:0d:86:4c:21:13:0a:85:58:57:cc:9c:ea:46
+# SHA256 Fingerprint: 8c:4e:df:d0:43:48:f3:22:96:9e:7e:29:a4:cd:4d:ca:00:46:55:06:1c:16:e1:b0:76:42:2e:f3:42:ad:63:0e
+-----BEGIN CERTIFICATE-----
+MIIGizCCBXOgAwIBAgIEO0XlaDANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJF
+UzEfMB0GA1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJ
+R1ZBMScwJQYDVQQDEx5Sb290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwHhcN
+MDEwNzA2MTYyMjQ3WhcNMjEwNzAxMTUyMjQ3WjBoMQswCQYDVQQGEwJFUzEfMB0G
+A1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJR1ZBMScw
+JQYDVQQDEx5Sb290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGKqtXETcvIorKA3Qdyu0togu8M1JAJke+
+WmmmO3I2F0zo37i7L3bhQEZ0ZQKQUgi0/6iMweDHiVYQOTPvaLRfX9ptI6GJXiKj
+SgbwJ/BXufjpTjJ3Cj9BZPPrZe52/lSqfR0grvPXdMIKX/UIKFIIzFVd0g/bmoGl
+u6GzwZTNVOAydTGRGmKy3nXiz0+J2ZGQD0EbtFpKd71ng+CT516nDOeB0/RSrFOy
+A8dEJvt55cs0YFAQexvba9dHq198aMpunUEDEO5rmXteJajCq+TA81yc477OMUxk
+Hl6AovWDfgzWyoxVjr7gvkkHD6MkQXpYHYTqWBLI4bft75PelAgxAgMBAAGjggM7
+MIIDNzAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9vY3NwLnBr
+aS5ndmEuZXMwEgYDVR0TAQH/BAgwBgEB/wIBAjCCAjQGA1UdIASCAiswggInMIIC
+IwYKKwYBBAG/VQIBADCCAhMwggHoBggrBgEFBQcCAjCCAdoeggHWAEEAdQB0AG8A
+cgBpAGQAYQBkACAAZABlACAAQwBlAHIAdABpAGYAaQBjAGEAYwBpAPMAbgAgAFIA
+YQDtAHoAIABkAGUAIABsAGEAIABHAGUAbgBlAHIAYQBsAGkAdABhAHQAIABWAGEA
+bABlAG4AYwBpAGEAbgBhAC4ADQAKAEwAYQAgAEQAZQBjAGwAYQByAGEAYwBpAPMA
+bgAgAGQAZQAgAFAAcgDhAGMAdABpAGMAYQBzACAAZABlACAAQwBlAHIAdABpAGYA
+aQBjAGEAYwBpAPMAbgAgAHEAdQBlACAAcgBpAGcAZQAgAGUAbAAgAGYAdQBuAGMA
+aQBvAG4AYQBtAGkAZQBuAHQAbwAgAGQAZQAgAGwAYQAgAHAAcgBlAHMAZQBuAHQA
+ZQAgAEEAdQB0AG8AcgBpAGQAYQBkACAAZABlACAAQwBlAHIAdABpAGYAaQBjAGEA
+YwBpAPMAbgAgAHMAZQAgAGUAbgBjAHUAZQBuAHQAcgBhACAAZQBuACAAbABhACAA
+ZABpAHIAZQBjAGMAaQDzAG4AIAB3AGUAYgAgAGgAdAB0AHAAOgAvAC8AdwB3AHcA
+LgBwAGsAaQAuAGcAdgBhAC4AZQBzAC8AYwBwAHMwJQYIKwYBBQUHAgEWGWh0dHA6
+Ly93d3cucGtpLmd2YS5lcy9jcHMwHQYDVR0OBBYEFHs100DSHHgZZu90ECjcPk+y
+eAT8MIGVBgNVHSMEgY0wgYqAFHs100DSHHgZZu90ECjcPk+yeAT8oWykajBoMQsw
+CQYDVQQGEwJFUzEfMB0GA1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0G
+A1UECxMGUEtJR1ZBMScwJQYDVQQDEx5Sb290IENBIEdlbmVyYWxpdGF0IFZhbGVu
+Y2lhbmGCBDtF5WgwDQYJKoZIhvcNAQEFBQADggEBACRhTvW1yEICKrNcda3Fbcrn
+lD+laJWIwVTAEGmiEi8YPyVQqHxK6sYJ2fR1xkDar1CdPaUWu20xxsdzCkj+IHLt
+b8zog2EWRpABlUt9jppSCS/2bxzkoXHPjCpaF3ODR00PNvsETUlR4hTJZGH71BTg
+9J63NI8KJr2XXPR5OkowGcytT6CYirQxlyric21+eLj4iIlPsSKRZEv1UN4D2+XF
+ducTZnV+ZfsBn5OHiJ35Rld8TWCvmHMTI6QgkYH60GFmuH3Rr9ZvHmw96RH9qfmC
+IoaZM3Fa6hlXPZHNqcCjbgcTpsnt+GijnsNacgmHKNHEc8RzGF9QdRYxn7fofMM=
+-----END CERTIFICATE-----
+
+# Issuer: CN=A-Trust-nQual-03 O=A-Trust Ges. f. Sicherheitssysteme im elektr. Datenverkehr GmbH OU=A-Trust-nQual-03
+# Subject: CN=A-Trust-nQual-03 O=A-Trust Ges. f. Sicherheitssysteme im elektr. Datenverkehr GmbH OU=A-Trust-nQual-03
+# Label: "A-Trust-nQual-03"
+# Serial: 93214
+# MD5 Fingerprint: 49:63:ae:27:f4:d5:95:3d:d8:db:24:86:b8:9c:07:53
+# SHA1 Fingerprint: d3:c0:63:f2:19:ed:07:3e:34:ad:5d:75:0b:32:76:29:ff:d5:9a:f2
+# SHA256 Fingerprint: 79:3c:bf:45:59:b9:fd:e3:8a:b2:2d:f1:68:69:f6:98:81:ae:14:c4:b0:13:9a:c7:88:a7:8a:1a:fc:ca:02:fb
+-----BEGIN CERTIFICATE-----
+MIIDzzCCAregAwIBAgIDAWweMA0GCSqGSIb3DQEBBQUAMIGNMQswCQYDVQQGEwJB
+VDFIMEYGA1UECgw/QS1UcnVzdCBHZXMuIGYuIFNpY2hlcmhlaXRzc3lzdGVtZSBp
+bSBlbGVrdHIuIERhdGVudmVya2VociBHbWJIMRkwFwYDVQQLDBBBLVRydXN0LW5R
+dWFsLTAzMRkwFwYDVQQDDBBBLVRydXN0LW5RdWFsLTAzMB4XDTA1MDgxNzIyMDAw
+MFoXDTE1MDgxNzIyMDAwMFowgY0xCzAJBgNVBAYTAkFUMUgwRgYDVQQKDD9BLVRy
+dXN0IEdlcy4gZi4gU2ljaGVyaGVpdHNzeXN0ZW1lIGltIGVsZWt0ci4gRGF0ZW52
+ZXJrZWhyIEdtYkgxGTAXBgNVBAsMEEEtVHJ1c3QtblF1YWwtMDMxGTAXBgNVBAMM
+EEEtVHJ1c3QtblF1YWwtMDMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
+AQCtPWFuA/OQO8BBC4SAzewqo51ru27CQoT3URThoKgtUaNR8t4j8DRE/5TrzAUj
+lUC5B3ilJfYKvUWG6Nm9wASOhURh73+nyfrBJcyFLGM/BWBzSQXgYHiVEEvc+RFZ
+znF/QJuKqiTfC0Li21a8StKlDJu3Qz7dg9MmEALP6iPESU7l0+m0iKsMrmKS1GWH
+2WrX9IWf5DMiJaXlyDO6w8dB3F/GaswADm0yqLaHNgBid5seHzTLkDx4iHQF63n1
+k3Flyp3HaxgtPVxO59X4PzF9j4fsCiIvI+n+u33J4PTs63zEsMMtYrWacdaxaujs
+2e3Vcuy+VwHOBVWf3tFgiBCzAgMBAAGjNjA0MA8GA1UdEwEB/wQFMAMBAf8wEQYD
+VR0OBAoECERqlWdVeRFPMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOC
+AQEAVdRU0VlIXLOThaq/Yy/kgM40ozRiPvbY7meIMQQDbwvUB/tOdQ/TLtPAF8fG
+KOwGDREkDg6lXb+MshOWcdzUzg4NCmgybLlBMRmrsQd7TZjTXLDR8KdCoLXEjq/+
+8T/0709GAHbrAvv5ndJAlseIOrifEXnzgGWovR/TeIGgUUw3tKZdJXDRZslo+S4R
+FGjxVJgIrCaSD96JntT6s3kr0qN51OyLrIdTaEJMUVF0HhsnLuP1Hyl0Te2v9+GS
+mYHovjrHF1D2t8b8m7CKa9aIA5GPBnc6hQLdmNVDeD/GMBWsm2vLV7eJUYs66MmE
+DNuxUCAKGkq6ahq97BvIxYSazQ==
+-----END CERTIFICATE-----
+
+# Issuer: CN=TWCA Root Certification Authority O=TAIWAN-CA OU=Root CA
+# Subject: CN=TWCA Root Certification Authority O=TAIWAN-CA OU=Root CA
+# Label: "TWCA Root Certification Authority"
+# Serial: 1
+# MD5 Fingerprint: aa:08:8f:f6:f9:7b:b7:f2:b1:a7:1e:9b:ea:ea:bd:79
+# SHA1 Fingerprint: cf:9e:87:6d:d3:eb:fc:42:26:97:a3:b5:a3:7a:a0:76:a9:06:23:48
+# SHA256 Fingerprint: bf:d8:8f:e1:10:1c:41:ae:3e:80:1b:f8:be:56:35:0e:e9:ba:d1:a6:b9:bd:51:5e:dc:5c:6d:5b:87:11:ac:44
+-----BEGIN CERTIFICATE-----
+MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzES
+MBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFU
+V0NBIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMz
+WhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJVEFJV0FO
+LUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlm
+aWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
+AQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFE
+AcK0HMMxQhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HH
+K3XLfJ+utdGdIzdjp9xCoi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeX
+RfwZVzsrb+RH9JlF/h3x+JejiB03HFyP4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/z
+rX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1ry+UPizgN7gr8/g+YnzAx
+3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
+HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkq
+hkiG9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeC
+MErJk/9q56YAf4lCmtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdls
+XebQ79NqZp4VKIV66IIArB6nCWlWQtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62D
+lhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVYT0bf+215WfKEIlKuD8z7fDvn
+aspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocnyYh0igzyXxfkZ
+YiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw==
+-----END CERTIFICATE-----
+
+# Issuer: O=SECOM Trust Systems CO.,LTD. OU=Security Communication RootCA2
+# Subject: O=SECOM Trust Systems CO.,LTD. OU=Security Communication RootCA2
+# Label: "Security Communication RootCA2"
+# Serial: 0
+# MD5 Fingerprint: 6c:39:7d:a4:0e:55:59:b2:3f:d6:41:b1:12:50:de:43
+# SHA1 Fingerprint: 5f:3b:8c:f2:f8:10:b3:7d:78:b4:ce:ec:19:19:c3:73:34:b9:c7:74
+# SHA256 Fingerprint: 51:3b:2c:ec:b8:10:d4:cd:e5:dd:85:39:1a:df:c6:c2:dd:60:d8:7b:b7:36:d2:b5:21:48:4a:a4:7a:0e:be:f6
+-----BEGIN CERTIFICATE-----
+MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDEl
+MCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMe
+U2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoX
+DTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRy
+dXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3VyaXR5IENvbW11bmlj
+YXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANAV
+OVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGr
+zbl+dp+++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVM
+VAX3NuRFg3sUZdbcDE3R3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQ
+hNBqyjoGADdH5H5XTz+L62e4iKrFvlNVspHEfbmwhRkGeC7bYRr6hfVKkaHnFtWO
+ojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1KEOtOghY6rCcMU/Gt1SSw
+awNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8QIH4D5cs
+OPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3
+DQEBCwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpF
+coJxDjrSzG+ntKEju/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXc
+okgfGT+Ok+vx+hfuzU7jBBJV1uXk3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8
+t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6qtnRGEmyR7jTV7JqR50S+kDFy
+1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29mvVXIwAHIRc/
+SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03
+-----END CERTIFICATE-----
+
+# Issuer: CN=Hellenic Academic and Research Institutions RootCA 2011 O=Hellenic Academic and Research Institutions Cert. Authority
+# Subject: CN=Hellenic Academic and Research Institutions RootCA 2011 O=Hellenic Academic and Research Institutions Cert. Authority
+# Label: "Hellenic Academic and Research Institutions RootCA 2011"
+# Serial: 0
+# MD5 Fingerprint: 73:9f:4c:4b:73:5b:79:e9:fa:ba:1c:ef:6e:cb:d5:c9
+# SHA1 Fingerprint: fe:45:65:9b:79:03:5b:98:a1:61:b5:51:2e:ac:da:58:09:48:22:4d
+# SHA256 Fingerprint: bc:10:4f:15:a4:8b:e7:09:dc:a5:42:a7:e1:d4:b9:df:6f:05:45:27:e8:02:ea:a9:2d:59:54:44:25:8a:fe:71
+-----BEGIN CERTIFICATE-----
+MIIEMTCCAxmgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCR1Ix
+RDBCBgNVBAoTO0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1
+dGlvbnMgQ2VydC4gQXV0aG9yaXR5MUAwPgYDVQQDEzdIZWxsZW5pYyBBY2FkZW1p
+YyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIFJvb3RDQSAyMDExMB4XDTExMTIw
+NjEzNDk1MloXDTMxMTIwMTEzNDk1MlowgZUxCzAJBgNVBAYTAkdSMUQwQgYDVQQK
+EztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIENl
+cnQuIEF1dGhvcml0eTFAMD4GA1UEAxM3SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl
+c2VhcmNoIEluc3RpdHV0aW9ucyBSb290Q0EgMjAxMTCCASIwDQYJKoZIhvcNAQEB
+BQADggEPADCCAQoCggEBAKlTAOMupvaO+mDYLZU++CwqVE7NuYRhlFhPjz2L5EPz
+dYmNUeTDN9KKiE15HrcS3UN4SoqS5tdI1Q+kOilENbgH9mgdVc04UfCMJDGFr4PJ
+fel3r+0ae50X+bOdOFAPplp5kYCvN66m0zH7tSYJnTxa71HFK9+WXesyHgLacEns
+bgzImjeN9/E2YEsmLIKe0HjzDQ9jpFEw4fkrJxIH2Oq9GGKYsFk3fb7u8yBRQlqD
+75O6aRXxYp2fmTmCobd0LovUxQt7L/DICto9eQqakxylKHJzkUOap9FNhYS5qXSP
+FEDH3N6sQWRstBmbAmNtJGSPRLIl6s5ddAxjMlyNh+UCAwEAAaOBiTCBhjAPBgNV
+HRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUppFC/RNhSiOeCKQp
+5dgTBCPuQSUwRwYDVR0eBEAwPqA8MAWCAy5ncjAFggMuZXUwBoIELmVkdTAGggQu
+b3JnMAWBAy5ncjAFgQMuZXUwBoEELmVkdTAGgQQub3JnMA0GCSqGSIb3DQEBBQUA
+A4IBAQAf73lB4XtuP7KMhjdCSk4cNx6NZrokgclPEg8hwAOXhiVtXdMiKahsog2p
+6z0GW5k6x8zDmjR/qw7IThzh+uTczQ2+vyT+bOdrwg3IBp5OjWEopmr95fZi6hg8
+TqBTnbI6nOulnJEWtk2C4AwFSKls9cz4y51JtPACpf1wA+2KIaWuE4ZJwzNzvoc7
+dIsXRSZMFpGD/md9zU1jZ/rzAxKWeAaNsWftjj++n08C9bMJL/NMh98qy5V8Acys
+Nnq/onN694/BtZqhFLKPM58N7yLcZnuEvUUXBj08yrl3NI/K6s8/MT7jiOOASSXI
+l7WdmplNsDz4SgCbZN2fOUvRJ9e4
+-----END CERTIFICATE-----
+
+# Issuer: CN=Actalis Authentication Root CA O=Actalis S.p.A./03358520967
+# Subject: CN=Actalis Authentication Root CA O=Actalis S.p.A./03358520967
+# Label: "Actalis Authentication Root CA"
+# Serial: 6271844772424770508
+# MD5 Fingerprint: 69:c1:0d:4f:07:a3:1b:c3:fe:56:3d:04:bc:11:f6:a6
+# SHA1 Fingerprint: f3:73:b3:87:06:5a:28:84:8a:f2:f3:4a:ce:19:2b:dd:c7:8e:9c:ac
+# SHA256 Fingerprint: 55:92:60:84:ec:96:3a:64:b9:6e:2a:be:01:ce:0b:a8:6a:64:fb:fe:bc:c7:aa:b5:af:c1:55:b3:7f:d7:60:66
+-----BEGIN CERTIFICATE-----
+MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UE
+BhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8w
+MzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290
+IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDkyMjExMjIwMlowazELMAkGA1UEBhMC
+SVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1
+ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENB
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNv
+UTufClrJwkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX
+4ay8IMKx4INRimlNAJZaby/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9
+KK3giq0itFZljoZUj5NDKd45RnijMCO6zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/
+gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1fYVEiVRvjRuPjPdA1Yprb
+rxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2oxgkg4YQ
+51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2F
+be8lEfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxe
+KF+w6D9Fz8+vm2/7hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4F
+v6MGn8i1zeQf1xcGDXqVdFUNaBr8EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbn
+fpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5jF66CyCU3nuDuP/jVo23Eek7
+jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLYiDrIn3hm7Ynz
+ezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt
+ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAL
+e3KHwGCmSUyIWOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70
+jsNjLiNmsGe+b7bAEzlgqqI0JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDz
+WochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKxK3JCaKygvU5a2hi/a5iB0P2avl4V
+SM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+Xlff1ANATIGk0k9j
+pwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC4yyX
+X04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+Ok
+fcvHlXHo2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7R
+K4X9p2jIugErsWx0Hbhzlefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btU
+ZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXemOR/qnuOf0GZvBeyqdn6/axag67XH/JJU
+LysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9vwGYT7JZVEc+NHt4bVaT
+LnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg==
+-----END CERTIFICATE-----
+
+# Issuer: O=Trustis Limited OU=Trustis FPS Root CA
+# Subject: O=Trustis Limited OU=Trustis FPS Root CA
+# Label: "Trustis FPS Root CA"
+# Serial: 36053640375399034304724988975563710553
+# MD5 Fingerprint: 30:c9:e7:1e:6b:e6:14:eb:65:b2:16:69:20:31:67:4d
+# SHA1 Fingerprint: 3b:c0:38:0b:33:c3:f6:a6:0c:86:15:22:93:d9:df:f5:4b:81:c0:04
+# SHA256 Fingerprint: c1:b4:82:99:ab:a5:20:8f:e9:63:0a:ce:55:ca:68:a0:3e:da:5a:51:9c:88:02:a0:d3:a6:73:be:8f:8e:55:7d
+-----BEGIN CERTIFICATE-----
+MIIDZzCCAk+gAwIBAgIQGx+ttiD5JNM2a/fH8YygWTANBgkqhkiG9w0BAQUFADBF
+MQswCQYDVQQGEwJHQjEYMBYGA1UEChMPVHJ1c3RpcyBMaW1pdGVkMRwwGgYDVQQL
+ExNUcnVzdGlzIEZQUyBSb290IENBMB4XDTAzMTIyMzEyMTQwNloXDTI0MDEyMTEx
+MzY1NFowRTELMAkGA1UEBhMCR0IxGDAWBgNVBAoTD1RydXN0aXMgTGltaXRlZDEc
+MBoGA1UECxMTVHJ1c3RpcyBGUFMgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBAMVQe547NdDfxIzNjpvto8A2mfRC6qc+gIMPpqdZh8mQRUN+
+AOqGeSoDvT03mYlmt+WKVoaTnGhLaASMk5MCPjDSNzoiYYkchU59j9WvezX2fihH
+iTHcDnlkH5nSW7r+f2C/revnPDgpai/lkQtV/+xvWNUtyd5MZnGPDNcE2gfmHhjj
+vSkCqPoc4Vu5g6hBSLwacY3nYuUtsuvffM/bq1rKMfFMIvMFE/eC+XN5DL7XSxzA
+0RU8k0Fk0ea+IxciAIleH2ulrG6nS4zto3Lmr2NNL4XSFDWaLk6M6jKYKIahkQlB
+OrTh4/L68MkKokHdqeMDx4gVOxzUGpTXn2RZEm0CAwEAAaNTMFEwDwYDVR0TAQH/
+BAUwAwEB/zAfBgNVHSMEGDAWgBS6+nEleYtXQSUhhgtx67JkDoshZzAdBgNVHQ4E
+FgQUuvpxJXmLV0ElIYYLceuyZA6LIWcwDQYJKoZIhvcNAQEFBQADggEBAH5Y//01
+GX2cGE+esCu8jowU/yyg2kdbw++BLa8F6nRIW/M+TgfHbcWzk88iNVy2P3UnXwmW
+zaD+vkAMXBJV+JOCyinpXj9WV4s4NvdFGkwozZ5BuO1WTISkQMi4sKUraXAEasP4
+1BIy+Q7DsdwyhEQsb8tGD+pmQQ9P8Vilpg0ND2HepZ5dfWWhPBfnqFVO76DH7cZE
+f1T1o+CP8HxVIo8ptoGj4W1OLBuAZ+ytIJ8MYmHVl/9D7S3B2l0pKoU/rGXuhg8F
+jZBf3+6f9L/uHfuY5H+QK4R4EA5sSVPvFVtlRkpdr7r7OnIdzfYliB6XzCGcKQEN
+ZetX2fNXlrtIzYE=
+-----END CERTIFICATE-----
+
+# Issuer: CN=StartCom Certification Authority O=StartCom Ltd. OU=Secure Digital Certificate Signing
+# Subject: CN=StartCom Certification Authority O=StartCom Ltd. OU=Secure Digital Certificate Signing
+# Label: "StartCom Certification Authority"
+# Serial: 45
+# MD5 Fingerprint: c9:3b:0d:84:41:fc:a4:76:79:23:08:57:de:10:19:16
+# SHA1 Fingerprint: a3:f1:33:3f:e2:42:bf:cf:c5:d1:4e:8f:39:42:98:40:68:10:d1:a0
+# SHA256 Fingerprint: e1:78:90:ee:09:a3:fb:f4:f4:8b:9c:41:4a:17:d6:37:b7:a5:06:47:e9:bc:75:23:22:72:7f:cc:17:42:a9:11
+-----BEGIN CERTIFICATE-----
+MIIHhzCCBW+gAwIBAgIBLTANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJJTDEW
+MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg
+Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0NjM3WhcNMzYwOTE3MTk0NjM2WjB9
+MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi
+U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh
+cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA
+A4ICDwAwggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZk
+pMyONvg45iPwbm2xPN1yo4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rf
+OQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/C
+Ji/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/deMotHweXMAEtcnn6RtYT
+Kqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt2PZE4XNi
+HzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMM
+Av+Z6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w
++2OqqGwaVLRcJXrJosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+
+Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3
+Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVcUjyJthkqcwEKDwOzEmDyei+B
+26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT37uMdBNSSwID
+AQABo4ICEDCCAgwwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD
+VR0OBBYEFE4L7xqkQFulF2mHMMo0aEPQQa7yMB8GA1UdIwQYMBaAFE4L7xqkQFul
+F2mHMMo0aEPQQa7yMIIBWgYDVR0gBIIBUTCCAU0wggFJBgsrBgEEAYG1NwEBATCC
+ATgwLgYIKwYBBQUHAgEWImh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL3BvbGljeS5w
+ZGYwNAYIKwYBBQUHAgEWKGh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL2ludGVybWVk
+aWF0ZS5wZGYwgc8GCCsGAQUFBwICMIHCMCcWIFN0YXJ0IENvbW1lcmNpYWwgKFN0
+YXJ0Q29tKSBMdGQuMAMCAQEagZZMaW1pdGVkIExpYWJpbGl0eSwgcmVhZCB0aGUg
+c2VjdGlvbiAqTGVnYWwgTGltaXRhdGlvbnMqIG9mIHRoZSBTdGFydENvbSBDZXJ0
+aWZpY2F0aW9uIEF1dGhvcml0eSBQb2xpY3kgYXZhaWxhYmxlIGF0IGh0dHA6Ly93
+d3cuc3RhcnRzc2wuY29tL3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgG
+CWCGSAGG+EIBDQQrFilTdGFydENvbSBGcmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1
+dGhvcml0eTANBgkqhkiG9w0BAQsFAAOCAgEAjo/n3JR5fPGFf59Jb2vKXfuM/gTF
+wWLRfUKKvFO3lANmMD+x5wqnUCBVJX92ehQN6wQOQOY+2IirByeDqXWmN3PH/UvS
+Ta0XQMhGvjt/UfzDtgUx3M2FIk5xt/JxXrAaxrqTi3iSSoX4eA+D/i+tLPfkpLst
+0OcNOrg+zvZ49q5HJMqjNTbOx8aHmNrs++myziebiMMEofYLWWivydsQD032ZGNc
+pRJvkrKTlMeIFw6Ttn5ii5B/q06f/ON1FE8qMt9bDeD1e5MNq6HPh+GlBEXoPBKl
+CcWw0bdT82AUuoVpaiF8H3VhFyAXe2w7QSlc4axa0c2Mm+tgHRns9+Ww2vl5GKVF
+P0lDV9LdJNUso/2RjSe15esUBppMeyG7Oq0wBhjA2MFrLH9ZXF2RsXAiV+uKa0hK
+1Q8p7MZAwC+ITGgBF3f0JBlPvfrhsiAhS90a2Cl9qrjeVOwhVYBsHvUwyKMQ5bLm
+KhQxw4UtjJixhlpPiVktucf3HMiKf8CdBUrmQk9io20ppB+Fq9vlgcitKj1MXVuE
+JnHEhV5xJMqlG2zYYdMa4FTbzrqpMrUi9nNBCV24F10OD5mQ1kfabwo6YigUZ4LZ
+8dCAWZvLMdibD4x3TrVoivJs9iQOLWxwxXPR3hTQcY+203sC9uO41Alua551hDnm
+fyWl8kgAwKQB2j8=
+-----END CERTIFICATE-----
+
+# Issuer: CN=StartCom Certification Authority G2 O=StartCom Ltd.
+# Subject: CN=StartCom Certification Authority G2 O=StartCom Ltd.
+# Label: "StartCom Certification Authority G2"
+# Serial: 59
+# MD5 Fingerprint: 78:4b:fb:9e:64:82:0a:d3:b8:4c:62:f3:64:f2:90:64
+# SHA1 Fingerprint: 31:f1:fd:68:22:63:20:ee:c6:3b:3f:9d:ea:4a:3e:53:7c:7c:39:17
+# SHA256 Fingerprint: c7:ba:65:67:de:93:a7:98:ae:1f:aa:79:1e:71:2d:37:8f:ae:1f:93:c4:39:7f:ea:44:1b:b7:cb:e6:fd:59:95
+-----BEGIN CERTIFICATE-----
+MIIFYzCCA0ugAwIBAgIBOzANBgkqhkiG9w0BAQsFADBTMQswCQYDVQQGEwJJTDEW
+MBQGA1UEChMNU3RhcnRDb20gTHRkLjEsMCoGA1UEAxMjU3RhcnRDb20gQ2VydGlm
+aWNhdGlvbiBBdXRob3JpdHkgRzIwHhcNMTAwMTAxMDEwMDAxWhcNMzkxMjMxMjM1
+OTAxWjBTMQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjEsMCoG
+A1UEAxMjU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgRzIwggIiMA0G
+CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2iTZbB7cgNr2Cu+EWIAOVeq8Oo1XJ
+JZlKxdBWQYeQTSFgpBSHO839sj60ZwNq7eEPS8CRhXBF4EKe3ikj1AENoBB5uNsD
+vfOpL9HG4A/LnooUCri99lZi8cVytjIl2bLzvWXFDSxu1ZJvGIsAQRSCb0AgJnoo
+D/Uefyf3lLE3PbfHkffiAez9lInhzG7TNtYKGXmu1zSCZf98Qru23QumNK9LYP5/
+Q0kGi4xDuFby2X8hQxfqp0iVAXV16iulQ5XqFYSdCI0mblWbq9zSOdIxHWDirMxW
+RST1HFSr7obdljKF+ExP6JV2tgXdNiNnvP8V4so75qbsO+wmETRIjfaAKxojAuuK
+HDp2KntWFhxyKrOq42ClAJ8Em+JvHhRYW6Vsi1g8w7pOOlz34ZYrPu8HvKTlXcxN
+nw3h3Kq74W4a7I/htkxNeXJdFzULHdfBR9qWJODQcqhaX2YtENwvKhOuJv4KHBnM
+0D4LnMgJLvlblnpHnOl68wVQdJVznjAJ85eCXuaPOQgeWeU1FEIT/wCc976qUM/i
+UUjXuG+v+E5+M5iSFGI6dWPPe/regjupuznixL0sAA7IF6wT700ljtizkC+p2il9
+Ha90OrInwMEePnWjFqmveiJdnxMaz6eg6+OGCtP95paV1yPIN93EfKo2rJgaErHg
+TuixO/XWb/Ew1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE
+AwIBBjAdBgNVHQ4EFgQUS8W0QGutHLOlHGVuRjaJhwUMDrYwDQYJKoZIhvcNAQEL
+BQADggIBAHNXPyzVlTJ+N9uWkusZXn5T50HsEbZH77Xe7XRcxfGOSeD8bpkTzZ+K
+2s06Ctg6Wgk/XzTQLwPSZh0avZyQN8gMjgdalEVGKua+etqhqaRpEpKwfTbURIfX
+UfEpY9Z1zRbkJ4kd+MIySP3bmdCPX1R0zKxnNBFi2QwKN4fRoxdIjtIXHfbX/dtl
+6/2o1PXWT6RbdejF0mCy2wl+JYt7ulKSnj7oxXehPOBKc2thz4bcQ///If4jXSRK
+9dNtD2IEBVeC2m6kMyV5Sy5UGYvMLD0w6dEG/+gyRr61M3Z3qAFdlsHB1b6uJcDJ
+HgoJIIihDsnzb02CVAAgp9KP5DlUFy6NHrgbuxu9mk47EDTcnIhT76IxW1hPkWLI
+wpqazRVdOKnWvvgTtZ8SafJQYqz7Fzf07rh1Z2AQ+4NQ+US1dZxAF7L+/XldblhY
+XzD8AK6vM8EOTmy6p6ahfzLbOOCxchcKK5HsamMm7YnUeMx0HgX4a/6ManY5Ka5l
+IxKVCCIcl85bBu4M4ru8H0ST9tg4RQUh7eStqxK2A6RCLi3ECToDZ2mEmuFZkIoo
+hdVddLHRDiBYmxOlsGOm7XtH/UVVMKTumtTm4ofvmMkyghEpIrwACjFeLQ/Ajulr
+so8uBtjRkcfGEvRM/TAXw8HaOFvjqermobp573PYtlNXLfbQ4ddI
+-----END CERTIFICATE-----
+
+# Issuer: CN=Buypass Class 2 Root CA O=Buypass AS-983163327
+# Subject: CN=Buypass Class 2 Root CA O=Buypass AS-983163327
+# Label: "Buypass Class 2 Root CA"
+# Serial: 2
+# MD5 Fingerprint: 46:a7:d2:fe:45:fb:64:5a:a8:59:90:9b:78:44:9b:29
+# SHA1 Fingerprint: 49:0a:75:74:de:87:0a:47:fe:58:ee:f6:c7:6b:eb:c6:0b:12:40:99
+# SHA256 Fingerprint: 9a:11:40:25:19:7c:5b:b9:5d:94:e6:3d:55:cd:43:79:08:47:b6:46:b2:3c:df:11:ad:a4:a0:0e:ff:15:fb:48
+-----BEGIN CERTIFICATE-----
+MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd
+MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg
+Q2xhc3MgMiBSb290IENBMB4XDTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1ow
+TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw
+HgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB
+BQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1g1Lr
+6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPV
+L4O2fuPn9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC91
+1K2GScuVr1QGbNgGE41b/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHx
+MlAQTn/0hpPshNOOvEu/XAFOBz3cFIqUCqTqc/sLUegTBxj6DvEr0VQVfTzh97QZ
+QmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeffawrbD02TTqigzXsu8lkB
+arcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgIzRFo1clr
+Us3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLi
+FRhnBkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRS
+P/TizPJhk9H9Z2vXUq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN
+9SG9dKpN6nIDSdvHXx1iY8f93ZHsM+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxP
+AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMmAd+BikoL1Rpzz
+uvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAU18h
+9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s
+A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3t
+OluwlN5E40EIosHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo
++fsicdl9sz1Gv7SEr5AcD48Saq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7
+KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYdDnkM/crqJIByw5c/8nerQyIKx+u2
+DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWDLfJ6v9r9jv6ly0Us
+H8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0oyLQ
+I+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK7
+5t98biGCwWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h
+3PFaTWwyI0PurKju7koSCTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPz
+Y11aWOIv4x3kqdbQCtCev9eBCfHJxyYNrJgWVqA=
+-----END CERTIFICATE-----
+
+# Issuer: CN=Buypass Class 3 Root CA O=Buypass AS-983163327
+# Subject: CN=Buypass Class 3 Root CA O=Buypass AS-983163327
+# Label: "Buypass Class 3 Root CA"
+# Serial: 2
+# MD5 Fingerprint: 3d:3b:18:9e:2c:64:5a:e8:d5:88:ce:0e:f9:37:c2:ec
+# SHA1 Fingerprint: da:fa:f7:fa:66:84:ec:06:8f:14:50:bd:c7:c2:81:a5:bc:a9:64:57
+# SHA256 Fingerprint: ed:f7:eb:bc:a2:7a:2a:38:4d:38:7b:7d:40:10:c6:66:e2:ed:b4:84:3e:4c:29:b4:ae:1d:5b:93:32:e6:b2:4d
+-----BEGIN CERTIFICATE-----
+MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd
+MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg
+Q2xhc3MgMyBSb290IENBMB4XDTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFow
+TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw
+HgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB
+BQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRHsJ8Y
+ZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3E
+N3coTRiR5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9
+tznDDgFHmV0ST9tD+leh7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX
+0DJq1l1sDPGzbjniazEuOQAnFN44wOwZZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c
+/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH2xc519woe2v1n/MuwU8X
+KhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV/afmiSTY
+zIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvS
+O1UQRwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D
+34xFMFbG02SrZvPAXpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgP
+K9Dx2hzLabjKSWJtyNBjYt1gD1iqj6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3
+AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFEe4zf/lb+74suwv
+Tg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAACAj
+QTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV
+cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXS
+IGrs/CIBKM+GuIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2
+HJLw5QY33KbmkJs4j1xrG0aGQ0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsa
+O5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8ZORK15FTAaggiG6cX0S5y2CBNOxv
+033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2KSb12tjE8nVhz36u
+dmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz6MkE
+kbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg41
+3OEMXbugUZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvD
+u79leNKGef9JOxqDDPDeeOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq
+4/g7u9xN12TyUb7mqqta6THuBrxzvxNiCp/HuZc=
+-----END CERTIFICATE-----
+
+# Issuer: CN=T-TeleSec GlobalRoot Class 3 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center
+# Subject: CN=T-TeleSec GlobalRoot Class 3 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center
+# Label: "T-TeleSec GlobalRoot Class 3"
+# Serial: 1
+# MD5 Fingerprint: ca:fb:40:a8:4e:39:92:8a:1d:fe:8e:2f:c4:27:ea:ef
+# SHA1 Fingerprint: 55:a6:72:3e:cb:f2:ec:cd:c3:23:74:70:19:9d:2a:be:11:e3:81:d1
+# SHA256 Fingerprint: fd:73:da:d3:1c:64:4f:f1:b4:3b:ef:0c:cd:da:96:71:0b:9c:d9:87:5e:ca:7e:31:70:7a:f3:e9:6d:52:2b:bd
+-----BEGIN CERTIFICATE-----
+MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx
+KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd
+BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl
+YyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgxMDAxMTAyOTU2WhcNMzMxMDAxMjM1
+OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy
+aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50
+ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0G
+CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN
+8ELg63iIVl6bmlQdTQyK9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/
+RLyTPWGrTs0NvvAgJ1gORH8EGoel15YUNpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4
+hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZFiP0Zf3WHHx+xGwpzJFu5
+ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W0eDrXltM
+EnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGj
+QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1
+A/d2O2GCahKqGFPrAyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOy
+WL6ukK2YJ5f+AbGwUgC4TeQbIXQbfsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ
+1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzTucpH9sry9uetuUg/vBa3wW30
+6gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7hP0HHRwA11fXT
+91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml
+e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4p
+TpPDpFQUWw==
+-----END CERTIFICATE-----
+
+# Issuer: CN=EE Certification Centre Root CA O=AS Sertifitseerimiskeskus
+# Subject: CN=EE Certification Centre Root CA O=AS Sertifitseerimiskeskus
+# Label: "EE Certification Centre Root CA"
+# Serial: 112324828676200291871926431888494945866
+# MD5 Fingerprint: 43:5e:88:d4:7d:1a:4a:7e:fd:84:2e:52:eb:01:d4:6f
+# SHA1 Fingerprint: c9:a8:b9:e7:55:80:5e:58:e3:53:77:a7:25:eb:af:c3:7b:27:cc:d7
+# SHA256 Fingerprint: 3e:84:ba:43:42:90:85:16:e7:75:73:c0:99:2f:09:79:ca:08:4e:46:85:68:1f:f1:95:cc:ba:8a:22:9b:8a:76
+-----BEGIN CERTIFICATE-----
+MIIEAzCCAuugAwIBAgIQVID5oHPtPwBMyonY43HmSjANBgkqhkiG9w0BAQUFADB1
+MQswCQYDVQQGEwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1
+czEoMCYGA1UEAwwfRUUgQ2VydGlmaWNhdGlvbiBDZW50cmUgUm9vdCBDQTEYMBYG
+CSqGSIb3DQEJARYJcGtpQHNrLmVlMCIYDzIwMTAxMDMwMTAxMDMwWhgPMjAzMDEy
+MTcyMzU5NTlaMHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlBUyBTZXJ0aWZpdHNl
+ZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRyZSBS
+b290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWUwggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQDIIMDs4MVLqwd4lfNE7vsLDP90jmG7sWLqI9iroWUy
+euuOF0+W2Ap7kaJjbMeMTC55v6kF/GlclY1i+blw7cNRfdCT5mzrMEvhvH2/UpvO
+bntl8jixwKIy72KyaOBhU8E2lf/slLo2rpwcpzIP5Xy0xm90/XsY6KxX7QYgSzIw
+WFv9zajmofxwvI6Sc9uXp3whrj3B9UiHbCe9nyV0gVWw93X2PaRka9ZP585ArQ/d
+MtO8ihJTmMmJ+xAdTX7Nfh9WDSFwhfYggx/2uh8Ej+p3iDXE/+pOoYtNP2MbRMNE
+1CV2yreN1x5KZmTNXMWcg+HCCIia7E6j8T4cLNlsHaFLAgMBAAGjgYowgYcwDwYD
+VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBLyWj7qVhy/
+zQas8fElyalL1BSZMEUGA1UdJQQ+MDwGCCsGAQUFBwMCBggrBgEFBQcDAQYIKwYB
+BQUHAwMGCCsGAQUFBwMEBggrBgEFBQcDCAYIKwYBBQUHAwkwDQYJKoZIhvcNAQEF
+BQADggEBAHv25MANqhlHt01Xo/6tu7Fq1Q+e2+RjxY6hUFaTlrg4wCQiZrxTFGGV
+v9DHKpY5P30osxBAIWrEr7BSdxjhlthWXePdNl4dp1BUoMUq5KqMlIpPnTX/dqQG
+E5Gion0ARD9V04I8GtVbvFZMIi5GQ4okQC3zErg7cBqklrkar4dBGmoYDQZPxz5u
+uSlNDUmJEYcyW+ZLBMjkXOZ0c5RdFpgTlf7727FE5TpwrDdr5rMzcijJs1eg9gIW
+iAYLtqZLICjU3j2LrTcFU3T+bsy8QxdxXvnFzBqpYe73dgzzcvRyrc9yAjYHR8/v
+GVCJYMzpJJUPwssd8m92kMfMdcGWxZ0=
+-----END CERTIFICATE-----
+
+# Issuer: CN=TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı O=TÜRKTRUST Bilgi İletişim ve Bilişim Güvenliği Hizmetleri A.Ş. (c) Aralık 2007
+# Subject: CN=TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı O=TÜRKTRUST Bilgi İletişim ve Bilişim Güvenliği Hizmetleri A.Ş. (c) Aralık 2007
+# Label: "TURKTRUST Certificate Services Provider Root 2007"
+# Serial: 1
+# MD5 Fingerprint: 2b:70:20:56:86:82:a0:18:c8:07:53:12:28:70:21:72
+# SHA1 Fingerprint: f1:7f:6f:b6:31:dc:99:e3:a3:c8:7f:fe:1c:f1:81:10:88:d9:60:33
+# SHA256 Fingerprint: 97:8c:d9:66:f2:fa:a0:7b:a7:aa:95:00:d9:c0:2e:9d:77:f2:cd:ad:a6:ad:6b:a7:4a:f4:b9:1c:66:59:3c:50
+-----BEGIN CERTIFICATE-----
+MIIEPTCCAyWgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvzE/MD0GA1UEAww2VMOc
+UktUUlVTVCBFbGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sx
+c8SxMQswCQYDVQQGEwJUUjEPMA0GA1UEBwwGQW5rYXJhMV4wXAYDVQQKDFVUw5xS
+S1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnEn2kg
+SGl6bWV0bGVyaSBBLsWeLiAoYykgQXJhbMSxayAyMDA3MB4XDTA3MTIyNTE4Mzcx
+OVoXDTE3MTIyMjE4MzcxOVowgb8xPzA9BgNVBAMMNlTDnFJLVFJVU1QgRWxla3Ry
+b25payBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsTELMAkGA1UEBhMC
+VFIxDzANBgNVBAcMBkFua2FyYTFeMFwGA1UECgxVVMOcUktUUlVTVCBCaWxnaSDE
+sGxldGnFn2ltIHZlIEJpbGnFn2ltIEfDvHZlbmxpxJ9pIEhpem1ldGxlcmkgQS7F
+ni4gKGMpIEFyYWzEsWsgMjAwNzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAKu3PgqMyKVYFeaK7yc9SrToJdPNM8Ig3BnuiD9NYvDdE3ePYakqtdTyuTFY
+KTsvP2qcb3N2Je40IIDu6rfwxArNK4aUyeNgsURSsloptJGXg9i3phQvKUmi8wUG
++7RP2qFsmmaf8EMJyupyj+sA1zU511YXRxcw9L6/P8JorzZAwan0qafoEGsIiveG
+HtyaKhUG9qPw9ODHFNRRf8+0222vR5YXm3dx2KdxnSQM9pQ/hTEST7ruToK4uT6P
+IzdezKKqdfcYbwnTrqdUKDT74eA7YH2gvnmJhsifLfkKS8RQouf9eRbHegsYz85M
+733WB2+Y8a+xwXrXgTW4qhe04MsCAwEAAaNCMEAwHQYDVR0OBBYEFCnFkKslrxHk
+Yb+j/4hhkeYO/pyBMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0G
+CSqGSIb3DQEBBQUAA4IBAQAQDdr4Ouwo0RSVgrESLFF6QSU2TJ/sPx+EnWVUXKgW
+AkD6bho3hO9ynYYKVZ1WKKxmLNA6VpM0ByWtCLCPyA8JWcqdmBzlVPi5RX9ql2+I
+aE1KBiY3iAIOtsbWcpnOa3faYjGkVh+uX4132l32iPwa2Z61gfAyuOOI0JzzaqC5
+mxRZNTZPz/OOXl0XrRWV2N2y1RVuAE6zS89mlOTgzbUF2mNXi+WzqtvALhyQRNsa
+XRik7r4EW5nVcV9VZWRi1aKbBFmGyGJ353yCRWo9F7/snXUMrqNvWtMvmDb08PUZ
+qxFdyKbjKlhqQgnDvZImZjINXQhVdP+MmNAKpoRq0Tl9
+-----END CERTIFICATE-----
+
+# Issuer: CN=D-TRUST Root Class 3 CA 2 2009 O=D-Trust GmbH
+# Subject: CN=D-TRUST Root Class 3 CA 2 2009 O=D-Trust GmbH
+# Label: "D-TRUST Root Class 3 CA 2 2009"
+# Serial: 623603
+# MD5 Fingerprint: cd:e0:25:69:8d:47:ac:9c:89:35:90:f7:fd:51:3d:2f
+# SHA1 Fingerprint: 58:e8:ab:b0:36:15:33:fb:80:f7:9b:1b:6d:29:d3:ff:8d:5f:00:f0
+# SHA256 Fingerprint: 49:e7:a4:42:ac:f0:ea:62:87:05:00:54:b5:25:64:b6:50:e4:f4:9e:42:e3:48:d6:aa:38:e0:39:e9:57:b1:c1
+-----BEGIN CERTIFICATE-----
+MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRF
+MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBD
+bGFzcyAzIENBIDIgMjAwOTAeFw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NTha
+ME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMM
+HkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIwDQYJKoZIhvcNAQEB
+BQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOADER03
+UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42
+tSHKXzlABF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9R
+ySPocq60vFYJfxLLHLGvKZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsM
+lFqVlNpQmvH/pStmMaTJOKDfHR+4CS7zp+hnUquVH+BGPtikw8paxTGA6Eian5Rp
+/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUCAwEAAaOCARowggEWMA8G
+A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ4PGEMA4G
+A1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVj
+dG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUy
+MENBJTIwMiUyMDIwMDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRl
+cmV2b2NhdGlvbmxpc3QwQ6BBoD+GPWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3Js
+L2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAwOS5jcmwwDQYJKoZIhvcNAQEL
+BQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm2H6NMLVwMeni
+acfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0
+o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4K
+zCUqNQT4YJEVdT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8
+PIWmawomDeCTmGCufsYkl4phX5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3Y
+Johw1+qRzT65ysCQblrGXnRl11z+o+I=
+-----END CERTIFICATE-----
+
+# Issuer: CN=D-TRUST Root Class 3 CA 2 EV 2009 O=D-Trust GmbH
+# Subject: CN=D-TRUST Root Class 3 CA 2 EV 2009 O=D-Trust GmbH
+# Label: "D-TRUST Root Class 3 CA 2 EV 2009"
+# Serial: 623604
+# MD5 Fingerprint: aa:c6:43:2c:5e:2d:cd:c4:34:c0:50:4f:11:02:4f:b6
+# SHA1 Fingerprint: 96:c9:1b:0b:95:b4:10:98:42:fa:d0:d8:22:79:fe:60:fa:b9:16:83
+# SHA256 Fingerprint: ee:c5:49:6b:98:8c:e9:86:25:b9:34:09:2e:ec:29:08:be:d0:b0:f3:16:c2:d4:73:0c:84:ea:f1:f3:d3:48:81
+-----BEGIN CERTIFICATE-----
+MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRF
+MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBD
+bGFzcyAzIENBIDIgRVYgMjAwOTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUw
+NDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNV
+BAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAwOTCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfSegpn
+ljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM0
+3TP1YtHhzRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6Z
+qQTMFexgaDbtCHu39b+T7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lR
+p75mpoo6Kr3HGrHhFPC+Oh25z1uxav60sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8
+HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure3511H3a6UCAwEAAaOCASQw
+ggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyvcop9Ntea
+HNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFw
+Oi8vZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xh
+c3MlMjAzJTIwQ0ElMjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1E
+RT9jZXJ0aWZpY2F0ZXJldm9jYXRpb25saXN0MEagRKBChkBodHRwOi8vd3d3LmQt
+dHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xhc3NfM19jYV8yX2V2XzIwMDku
+Y3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+PPoeUSbrh/Yp
+3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05
+nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNF
+CSuGdXzfX2lXANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7na
+xpeG0ILD5EJt/rDiZE4OJudANCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqX
+KVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVvw9y4AyHqnxbxLFS1
+-----END CERTIFICATE-----
+
+# Issuer: CN=Autoridad de Certificacion Raiz del Estado Venezolano O=Sistema Nacional de Certificacion Electronica OU=Superintendencia de Servicios de Certificacion Electronica
+# Subject: CN=PSCProcert O=Sistema Nacional de Certificacion Electronica OU=Proveedor de Certificados PROCERT
+# Label: "PSCProcert"
+# Serial: 11
+# MD5 Fingerprint: e6:24:e9:12:01:ae:0c:de:8e:85:c4:ce:a3:12:dd:ec
+# SHA1 Fingerprint: 70:c1:8d:74:b4:28:81:0a:e4:fd:a5:75:d7:01:9f:99:b0:3d:50:74
+# SHA256 Fingerprint: 3c:fc:3c:14:d1:f6:84:ff:17:e3:8c:43:ca:44:0c:00:b9:67:ec:93:3e:8b:fe:06:4c:a1:d7:2c:90:f2:ad:b0
+-----BEGIN CERTIFICATE-----
+MIIJhjCCB26gAwIBAgIBCzANBgkqhkiG9w0BAQsFADCCAR4xPjA8BgNVBAMTNUF1
+dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIFJhaXogZGVsIEVzdGFkbyBWZW5lem9s
+YW5vMQswCQYDVQQGEwJWRTEQMA4GA1UEBxMHQ2FyYWNhczEZMBcGA1UECBMQRGlz
+dHJpdG8gQ2FwaXRhbDE2MDQGA1UEChMtU2lzdGVtYSBOYWNpb25hbCBkZSBDZXJ0
+aWZpY2FjaW9uIEVsZWN0cm9uaWNhMUMwQQYDVQQLEzpTdXBlcmludGVuZGVuY2lh
+IGRlIFNlcnZpY2lvcyBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9uaWNhMSUwIwYJ
+KoZIhvcNAQkBFhZhY3JhaXpAc3VzY2VydGUuZ29iLnZlMB4XDTEwMTIyODE2NTEw
+MFoXDTIwMTIyNTIzNTk1OVowgdExJjAkBgkqhkiG9w0BCQEWF2NvbnRhY3RvQHBy
+b2NlcnQubmV0LnZlMQ8wDQYDVQQHEwZDaGFjYW8xEDAOBgNVBAgTB01pcmFuZGEx
+KjAoBgNVBAsTIVByb3ZlZWRvciBkZSBDZXJ0aWZpY2Fkb3MgUFJPQ0VSVDE2MDQG
+A1UEChMtU2lzdGVtYSBOYWNpb25hbCBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9u
+aWNhMQswCQYDVQQGEwJWRTETMBEGA1UEAxMKUFNDUHJvY2VydDCCAiIwDQYJKoZI
+hvcNAQEBBQADggIPADCCAgoCggIBANW39KOUM6FGqVVhSQ2oh3NekS1wwQYalNo9
+7BVCwfWMrmoX8Yqt/ICV6oNEolt6Vc5Pp6XVurgfoCfAUFM+jbnADrgV3NZs+J74
+BCXfgI8Qhd19L3uA3VcAZCP4bsm+lU/hdezgfl6VzbHvvnpC2Mks0+saGiKLt38G
+ieU89RLAu9MLmV+QfI4tL3czkkohRqipCKzx9hEC2ZUWno0vluYC3XXCFCpa1sl9
+JcLB/KpnheLsvtF8PPqv1W7/U0HU9TI4seJfxPmOEO8GqQKJ/+MMbpfg353bIdD0
+PghpbNjU5Db4g7ayNo+c7zo3Fn2/omnXO1ty0K+qP1xmk6wKImG20qCZyFSTXai2
+0b1dCl53lKItwIKOvMoDKjSuc/HUtQy9vmebVOvh+qBa7Dh+PsHMosdEMXXqP+UH
+0quhJZb25uSgXTcYOWEAM11G1ADEtMo88aKjPvM6/2kwLkDd9p+cJsmWN63nOaK/
+6mnbVSKVUyqUtd+tFjiBdWbjxywbk5yqjKPK2Ww8F22c3HxT4CAnQzb5EuE8XL1m
+v6JpIzi4mWCZDlZTOpx+FIywBm/xhnaQr/2v/pDGj59/i5IjnOcVdo/Vi5QTcmn7
+K2FjiO/mpF7moxdqWEfLcU8UC17IAggmosvpr2uKGcfLFFb14dq12fy/czja+eev
+bqQ34gcnAgMBAAGjggMXMIIDEzASBgNVHRMBAf8ECDAGAQH/AgEBMDcGA1UdEgQw
+MC6CD3N1c2NlcnRlLmdvYi52ZaAbBgVghl4CAqASDBBSSUYtRy0yMDAwNDAzNi0w
+MB0GA1UdDgQWBBRBDxk4qpl/Qguk1yeYVKIXTC1RVDCCAVAGA1UdIwSCAUcwggFD
+gBStuyIdxuDSAaj9dlBSk+2YwU2u06GCASakggEiMIIBHjE+MDwGA1UEAxM1QXV0
+b3JpZGFkIGRlIENlcnRpZmljYWNpb24gUmFpeiBkZWwgRXN0YWRvIFZlbmV6b2xh
+bm8xCzAJBgNVBAYTAlZFMRAwDgYDVQQHEwdDYXJhY2FzMRkwFwYDVQQIExBEaXN0
+cml0byBDYXBpdGFsMTYwNAYDVQQKEy1TaXN0ZW1hIE5hY2lvbmFsIGRlIENlcnRp
+ZmljYWNpb24gRWxlY3Ryb25pY2ExQzBBBgNVBAsTOlN1cGVyaW50ZW5kZW5jaWEg
+ZGUgU2VydmljaW9zIGRlIENlcnRpZmljYWNpb24gRWxlY3Ryb25pY2ExJTAjBgkq
+hkiG9w0BCQEWFmFjcmFpekBzdXNjZXJ0ZS5nb2IudmWCAQowDgYDVR0PAQH/BAQD
+AgEGME0GA1UdEQRGMESCDnByb2NlcnQubmV0LnZloBUGBWCGXgIBoAwMClBTQy0w
+MDAwMDKgGwYFYIZeAgKgEgwQUklGLUotMzE2MzUzNzMtNzB2BgNVHR8EbzBtMEag
+RKBChkBodHRwOi8vd3d3LnN1c2NlcnRlLmdvYi52ZS9sY3IvQ0VSVElGSUNBRE8t
+UkFJWi1TSEEzODRDUkxERVIuY3JsMCOgIaAfhh1sZGFwOi8vYWNyYWl6LnN1c2Nl
+cnRlLmdvYi52ZTA3BggrBgEFBQcBAQQrMCkwJwYIKwYBBQUHMAGGG2h0dHA6Ly9v
+Y3NwLnN1c2NlcnRlLmdvYi52ZTBBBgNVHSAEOjA4MDYGBmCGXgMBAjAsMCoGCCsG
+AQUFBwIBFh5odHRwOi8vd3d3LnN1c2NlcnRlLmdvYi52ZS9kcGMwDQYJKoZIhvcN
+AQELBQADggIBACtZ6yKZu4SqT96QxtGGcSOeSwORR3C7wJJg7ODU523G0+1ng3dS
+1fLld6c2suNUvtm7CpsR72H0xpkzmfWvADmNg7+mvTV+LFwxNG9s2/NkAZiqlCxB
+3RWGymspThbASfzXg0gTB1GEMVKIu4YXx2sviiCtxQuPcD4quxtxj7mkoP3Yldmv
+Wb8lK5jpY5MvYB7Eqvh39YtsL+1+LrVPQA3uvFd359m21D+VJzog1eWuq2w1n8Gh
+HVnchIHuTQfiSLaeS5UtQbHh6N5+LwUeaO6/u5BlOsju6rEYNxxik6SgMexxbJHm
+pHmJWhSnFFAFTKQAVzAswbVhltw+HoSvOULP5dAssSS830DD7X9jSr3hTxJkhpXz
+sOfIt+FTvZLm8wyWuevo5pLtp4EJFAv8lXrPj9Y0TzYS3F7RNHXGRoAvlQSMx4bE
+qCaJqD8Zm4G7UaRKhqsLEQ+xrmNTbSjq3TNWOByyrYDT13K9mmyZY+gAu0F2Bbdb
+mRiKw7gSXFbPVgx96OLP7bx0R/vu0xdOIk9W/1DzLuY5poLWccret9W6aAjtmcz9
+opLLabid+Qqkpj5PkygqYWwHJgD/ll9ohri4zspV4KuxPX+Y1zMOWj3YeMLEYC/H
+YvBhkdI4sPaeVdtAgAUSM84dkpvRabP/v/GSCmE1P93+hvS84Bpxs2Km
+-----END CERTIFICATE-----
+
+# Issuer: CN=China Internet Network Information Center EV Certificates Root O=China Internet Network Information Center
+# Subject: CN=China Internet Network Information Center EV Certificates Root O=China Internet Network Information Center
+# Label: "China Internet Network Information Center EV Certificates Root"
+# Serial: 1218379777
+# MD5 Fingerprint: 55:5d:63:00:97:bd:6a:97:f5:67:ab:4b:fb:6e:63:15
+# SHA1 Fingerprint: 4f:99:aa:93:fb:2b:d1:37:26:a1:99:4a:ce:7f:f0:05:f2:93:5d:1e
+# SHA256 Fingerprint: 1c:01:c6:f4:db:b2:fe:fc:22:55:8b:2b:ca:32:56:3f:49:84:4a:cf:c3:2b:7b:e4:b0:ff:59:9f:9e:8c:7a:f7
+-----BEGIN CERTIFICATE-----
+MIID9zCCAt+gAwIBAgIESJ8AATANBgkqhkiG9w0BAQUFADCBijELMAkGA1UEBhMC
+Q04xMjAwBgNVBAoMKUNoaW5hIEludGVybmV0IE5ldHdvcmsgSW5mb3JtYXRpb24g
+Q2VudGVyMUcwRQYDVQQDDD5DaGluYSBJbnRlcm5ldCBOZXR3b3JrIEluZm9ybWF0
+aW9uIENlbnRlciBFViBDZXJ0aWZpY2F0ZXMgUm9vdDAeFw0xMDA4MzEwNzExMjVa
+Fw0zMDA4MzEwNzExMjVaMIGKMQswCQYDVQQGEwJDTjEyMDAGA1UECgwpQ2hpbmEg
+SW50ZXJuZXQgTmV0d29yayBJbmZvcm1hdGlvbiBDZW50ZXIxRzBFBgNVBAMMPkNo
+aW5hIEludGVybmV0IE5ldHdvcmsgSW5mb3JtYXRpb24gQ2VudGVyIEVWIENlcnRp
+ZmljYXRlcyBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm35z
+7r07eKpkQ0H1UN+U8i6yjUqORlTSIRLIOTJCBumD1Z9S7eVnAztUwYyZmczpwA//
+DdmEEbK40ctb3B75aDFk4Zv6dOtouSCV98YPjUesWgbdYavi7NifFy2cyjw1l1Vx
+zUOFsUcW9SxTgHbP0wBkvUCZ3czY28Sf1hNfQYOL+Q2HklY0bBoQCxfVWhyXWIQ8
+hBouXJE0bhlffxdpxWXvayHG1VA6v2G5BY3vbzQ6sm8UY78WO5upKv23KzhmBsUs
+4qpnHkWnjQRmQvaPK++IIGmPMowUc9orhpFjIpryp9vOiYurXccUwVswah+xt54u
+gQEC7c+WXmPbqOY4twIDAQABo2MwYTAfBgNVHSMEGDAWgBR8cks5x8DbYqVPm6oY
+NJKiyoOCWTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4E
+FgQUfHJLOcfA22KlT5uqGDSSosqDglkwDQYJKoZIhvcNAQEFBQADggEBACrDx0M3
+j92tpLIM7twUbY8opJhJywyA6vPtI2Z1fcXTIWd50XPFtQO3WKwMVC/GVhMPMdoG
+52U7HW8228gd+f2ABsqjPWYWqJ1MFn3AlUa1UeTiH9fqBk1jjZaM7+czV0I664zB
+echNdn3e9rG3geCg+aF4RhcaVpjwTj2rHO3sOdwHSPdj/gauwqRcalsyiMXHM4Ws
+ZkJHwlgkmeHlPuV1LI5D1l08eB6olYIpUNHRFrrvwb562bTYzB5MRuF3sTGrvSrI
+zo9uoV1/A3U05K2JRVRevq4opbs/eHnrc7MKDf2+yfdWrPa37S+bISnHOLaVxATy
+wy39FCqQmbkHzJ8=
+-----END CERTIFICATE-----
+
+# Issuer: CN=Swisscom Root CA 2 O=Swisscom OU=Digital Certificate Services
+# Subject: CN=Swisscom Root CA 2 O=Swisscom OU=Digital Certificate Services
+# Label: "Swisscom Root CA 2"
+# Serial: 40698052477090394928831521023204026294
+# MD5 Fingerprint: 5b:04:69:ec:a5:83:94:63:18:a7:86:d0:e4:f2:6e:19
+# SHA1 Fingerprint: 77:47:4f:c6:30:e4:0f:4c:47:64:3f:84:ba:b8:c6:95:4a:8a:41:ec
+# SHA256 Fingerprint: f0:9b:12:2c:71:14:f4:a0:9b:d4:ea:4f:4a:99:d5:58:b4:6e:4c:25:cd:81:14:0d:29:c0:56:13:91:4c:38:41
+-----BEGIN CERTIFICATE-----
+MIIF2TCCA8GgAwIBAgIQHp4o6Ejy5e/DfEoeWhhntjANBgkqhkiG9w0BAQsFADBk
+MQswCQYDVQQGEwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0
+YWwgQ2VydGlmaWNhdGUgU2VydmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3Qg
+Q0EgMjAeFw0xMTA2MjQwODM4MTRaFw0zMTA2MjUwNzM4MTRaMGQxCzAJBgNVBAYT
+AmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGlnaXRhbCBDZXJ0aWZp
+Y2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAyMIICIjAN
+BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAlUJOhJ1R5tMJ6HJaI2nbeHCOFvEr
+jw0DzpPMLgAIe6szjPTpQOYXTKueuEcUMncy3SgM3hhLX3af+Dk7/E6J2HzFZ++r
+0rk0X2s682Q2zsKwzxNoysjL67XiPS4h3+os1OD5cJZM/2pYmLcX5BtS5X4HAB1f
+2uY+lQS3aYg5oUFgJWFLlTloYhyxCwWJwDaCFCE/rtuh/bxvHGCGtlOUSbkrRsVP
+ACu/obvLP+DHVxxX6NZp+MEkUp2IVd3Chy50I9AU/SpHWrumnf2U5NGKpV+GY3aF
+y6//SSj8gO1MedK75MDvAe5QQQg1I3ArqRa0jG6F6bYRzzHdUyYb3y1aSgJA/MTA
+tukxGggo5WDDH8SQjhBiYEQN7Aq+VRhxLKX0srwVYv8c474d2h5Xszx+zYIdkeNL
+6yxSNLCK/RJOlrDrcH+eOfdmQrGrrFLadkBXeyq96G4DsguAhYidDMfCd7Camlf0
+uPoTXGiTOmekl9AbmbeGMktg2M7v0Ax/lZ9vh0+Hio5fCHyqW/xavqGRn1V9TrAL
+acywlKinh/LTSlDcX3KwFnUey7QYYpqwpzmqm59m2I2mbJYV4+by+PGDYmy7Velh
+k6M99bFXi08jsJvllGov34zflVEpYKELKeRcVVi3qPyZ7iVNTA6z00yPhOgpD/0Q
+VAKFyPnlw4vP5w8CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0hBBYw
+FDASBgdghXQBUwIBBgdghXQBUwIBMBIGA1UdEwEB/wQIMAYBAf8CAQcwHQYDVR0O
+BBYEFE0mICKJS9PVpAqhb97iEoHF8TwuMB8GA1UdIwQYMBaAFE0mICKJS9PVpAqh
+b97iEoHF8TwuMA0GCSqGSIb3DQEBCwUAA4ICAQAyCrKkG8t9voJXiblqf/P0wS4R
+fbgZPnm3qKhyN2abGu2sEzsOv2LwnN+ee6FTSA5BesogpxcbtnjsQJHzQq0Qw1zv
+/2BZf82Fo4s9SBwlAjxnffUy6S8w5X2lejjQ82YqZh6NM4OKb3xuqFp1mrjX2lhI
+REeoTPpMSQpKwhI3qEAMw8jh0FcNlzKVxzqfl9NX+Ave5XLzo9v/tdhZsnPdTSpx
+srpJ9csc1fV5yJmz/MFMdOO0vSk3FQQoHt5FRnDsr7p4DooqzgB53MBfGWcsa0vv
+aGgLQ+OswWIJ76bdZWGgr4RVSJFSHMYlkSrQwSIjYVmvRRGFHQEkNI/Ps/8XciAT
+woCqISxxOQ7Qj1zB09GOInJGTB2Wrk9xseEFKZZZ9LuedT3PDTcNYtsmjGOpI99n
+Bjx8Oto0QuFmtEYE3saWmA9LSHokMnWRn6z3aOkquVVlzl1h0ydw2Df+n7mvoC5W
+t6NlUe07qxS/TFED6F+KBZvuim6c779o+sjaC+NCydAXFJy3SuCvkychVSa1ZC+N
+8f+mQAWFBVzKBxlcCxMoTFh/wqXvRdpg065lYZ1Tg3TCrvJcwhbtkj6EPnNgiLx2
+9CzP0H1907he0ZESEOnN3col49XtmS++dYFLJPlFRpTJKSFTnCZFqhMX5OfNeOI5
+wSsSnqaeG8XmDtkx2Q==
+-----END CERTIFICATE-----
+
+# Issuer: CN=Swisscom Root EV CA 2 O=Swisscom OU=Digital Certificate Services
+# Subject: CN=Swisscom Root EV CA 2 O=Swisscom OU=Digital Certificate Services
+# Label: "Swisscom Root EV CA 2"
+# Serial: 322973295377129385374608406479535262296
+# MD5 Fingerprint: 7b:30:34:9f:dd:0a:4b:6b:35:ca:31:51:28:5d:ae:ec
+# SHA1 Fingerprint: e7:a1:90:29:d3:d5:52:dc:0d:0f:c6:92:d3:ea:88:0d:15:2e:1a:6b
+# SHA256 Fingerprint: d9:5f:ea:3c:a4:ee:dc:e7:4c:d7:6e:75:fc:6d:1f:f6:2c:44:1f:0f:a8:bc:77:f0:34:b1:9e:5d:b2:58:01:5d
+-----BEGIN CERTIFICATE-----
+MIIF4DCCA8igAwIBAgIRAPL6ZOJ0Y9ON/RAdBB92ylgwDQYJKoZIhvcNAQELBQAw
+ZzELMAkGA1UEBhMCY2gxETAPBgNVBAoTCFN3aXNzY29tMSUwIwYDVQQLExxEaWdp
+dGFsIENlcnRpZmljYXRlIFNlcnZpY2VzMR4wHAYDVQQDExVTd2lzc2NvbSBSb290
+IEVWIENBIDIwHhcNMTEwNjI0MDk0NTA4WhcNMzEwNjI1MDg0NTA4WjBnMQswCQYD
+VQQGEwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0YWwgQ2Vy
+dGlmaWNhdGUgU2VydmljZXMxHjAcBgNVBAMTFVN3aXNzY29tIFJvb3QgRVYgQ0Eg
+MjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMT3HS9X6lds93BdY7Bx
+UglgRCgzo3pOCvrY6myLURYaVa5UJsTMRQdBTxB5f3HSek4/OE6zAMaVylvNwSqD
+1ycfMQ4jFrclyxy0uYAyXhqdk/HoPGAsp15XGVhRXrwsVgu42O+LgrQ8uMIkqBPH
+oCE2G3pXKSinLr9xJZDzRINpUKTk4RtiGZQJo/PDvO/0vezbE53PnUgJUmfANykR
+HvvSEaeFGHR55E+FFOtSN+KxRdjMDUN/rhPSays/p8LiqG12W0OfvrSdsyaGOx9/
+5fLoZigWJdBLlzin5M8J0TbDC77aO0RYjb7xnglrPvMyxyuHxuxenPaHZa0zKcQv
+idm5y8kDnftslFGXEBuGCxobP/YCfnvUxVFkKJ3106yDgYjTdLRZncHrYTNaRdHL
+OdAGalNgHa/2+2m8atwBz735j9m9W8E6X47aD0upm50qKGsaCnw8qyIL5XctcfaC
+NYGu+HuB5ur+rPQam3Rc6I8k9l2dRsQs0h4rIWqDJ2dVSqTjyDKXZpBy2uPUZC5f
+46Fq9mDU5zXNysRojddxyNMkM3OxbPlq4SjbX8Y96L5V5jcb7STZDxmPX2MYWFCB
+UWVv8p9+agTnNCRxunZLWB4ZvRVgRaoMEkABnRDixzgHcgplwLa7JSnaFp6LNYth
+7eVxV4O1PHGf40+/fh6Bn0GXAgMBAAGjgYYwgYMwDgYDVR0PAQH/BAQDAgGGMB0G
+A1UdIQQWMBQwEgYHYIV0AVMCAgYHYIV0AVMCAjASBgNVHRMBAf8ECDAGAQH/AgED
+MB0GA1UdDgQWBBRF2aWBbj2ITY1x0kbBbkUe88SAnTAfBgNVHSMEGDAWgBRF2aWB
+bj2ITY1x0kbBbkUe88SAnTANBgkqhkiG9w0BAQsFAAOCAgEAlDpzBp9SSzBc1P6x
+XCX5145v9Ydkn+0UjrgEjihLj6p7jjm02Vj2e6E1CqGdivdj5eu9OYLU43otb98T
+PLr+flaYC/NUn81ETm484T4VvwYmneTwkLbUwp4wLh/vx3rEUMfqe9pQy3omywC0
+Wqu1kx+AiYQElY2NfwmTv9SoqORjbdlk5LgpWgi/UOGED1V7XwgiG/W9mR4U9s70
+WBCCswo9GcG/W6uqmdjyMb3lOGbcWAXH7WMaLgqXfIeTK7KK4/HsGOV1timH59yL
+Gn602MnTihdsfSlEvoqq9X46Lmgxk7lq2prg2+kupYTNHAq4Sgj5nPFhJpiTt3tm
+7JFe3VE/23MPrQRYCd0EApUKPtN236YQHoA96M2kZNEzx5LH4k5E4wnJTsJdhw4S
+nr8PyQUQ3nqjsTzyP6WqJ3mtMX0f/fwZacXduT98zca0wjAefm6S139hdlqP65VN
+vBFuIXxZN5nQBrz5Bm0yFqXZaajh3DyAHmBR3NdUIR7KYndP+tiPsys6DXhyyWhB
+WkdKwqPrGtcKqzwyVcgKEZzfdNbwQBUdyLmPtTbFr/giuMod89a2GQ+fYWVq6nTI
+fI/DT11lgh/ZDYnadXL77/FHZxOzyNEZiCcmmpl5fx7kLD977vHeTYuWl8PVP3wb
+I+2ksx0WckNLIOFZfsLorSa/ovc=
+-----END CERTIFICATE-----
+
+# Issuer: CN=CA Disig Root R1 O=Disig a.s.
+# Subject: CN=CA Disig Root R1 O=Disig a.s.
+# Label: "CA Disig Root R1"
+# Serial: 14052245610670616104
+# MD5 Fingerprint: be:ec:11:93:9a:f5:69:21:bc:d7:c1:c0:67:89:cc:2a
+# SHA1 Fingerprint: 8e:1c:74:f8:a6:20:b9:e5:8a:f4:61:fa:ec:2b:47:56:51:1a:52:c6
+# SHA256 Fingerprint: f9:6f:23:f4:c3:e7:9c:07:7a:46:98:8d:5a:f5:90:06:76:a0:f0:39:cb:64:5d:d1:75:49:b2:16:c8:24:40:ce
+-----BEGIN CERTIFICATE-----
+MIIFaTCCA1GgAwIBAgIJAMMDmu5QkG4oMA0GCSqGSIb3DQEBBQUAMFIxCzAJBgNV
+BAYTAlNLMRMwEQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMu
+MRkwFwYDVQQDExBDQSBEaXNpZyBSb290IFIxMB4XDTEyMDcxOTA5MDY1NloXDTQy
+MDcxOTA5MDY1NlowUjELMAkGA1UEBhMCU0sxEzARBgNVBAcTCkJyYXRpc2xhdmEx
+EzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERpc2lnIFJvb3QgUjEw
+ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCqw3j33Jijp1pedxiy3QRk
+D2P9m5YJgNXoqqXinCaUOuiZc4yd39ffg/N4T0Dhf9Kn0uXKE5Pn7cZ3Xza1lK/o
+OI7bm+V8u8yN63Vz4STN5qctGS7Y1oprFOsIYgrY3LMATcMjfF9DCCMyEtztDK3A
+fQ+lekLZWnDZv6fXARz2m6uOt0qGeKAeVjGu74IKgEH3G8muqzIm1Cxr7X1r5OJe
+IgpFy4QxTaz+29FHuvlglzmxZcfe+5nkCiKxLU3lSCZpq+Kq8/v8kiky6bM+TR8n
+oc2OuRf7JT7JbvN32g0S9l3HuzYQ1VTW8+DiR0jm3hTaYVKvJrT1cU/J19IG32PK
+/yHoWQbgCNWEFVP3Q+V8xaCJmGtzxmjOZd69fwX3se72V6FglcXM6pM6vpmumwKj
+rckWtc7dXpl4fho5frLABaTAgqWjR56M6ly2vGfb5ipN0gTco65F97yLnByn1tUD
+3AjLLhbKXEAz6GfDLuemROoRRRw1ZS0eRWEkG4IupZ0zXWX4Qfkuy5Q/H6MMMSRE
+7cderVC6xkGbrPAXZcD4XW9boAo0PO7X6oifmPmvTiT6l7Jkdtqr9O3jw2Dv1fkC
+yC2fg69naQanMVXVz0tv/wQFx1isXxYb5dKj6zHbHzMVTdDypVP1y+E9Tmgt2BLd
+qvLmTZtJ5cUoobqwWsagtQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud
+DwEB/wQEAwIBBjAdBgNVHQ4EFgQUiQq0OJMa5qvum5EY+fU8PjXQ04IwDQYJKoZI
+hvcNAQEFBQADggIBADKL9p1Kyb4U5YysOMo6CdQbzoaz3evUuii+Eq5FLAR0rBNR
+xVgYZk2C2tXck8An4b58n1KeElb21Zyp9HWc+jcSjxyT7Ff+Bw+r1RL3D65hXlaA
+SfX8MPWbTx9BLxyE04nH4toCdu0Jz2zBuByDHBb6lM19oMgY0sidbvW9adRtPTXo
+HqJPYNcHKfyyo6SdbhWSVhlMCrDpfNIZTUJG7L399ldb3Zh+pE3McgODWF3vkzpB
+emOqfDqo9ayk0d2iLbYq/J8BjuIQscTK5GfbVSUZP/3oNn6z4eGBrxEWi1CXYBmC
+AMBrTXO40RMHPuq2MU/wQppt4hF05ZSsjYSVPCGvxdpHyN85YmLLW1AL14FABZyb
+7bq2ix4Eb5YgOe2kfSnbSM6C3NQCjR0EMVrHS/BsYVLXtFHCgWzN4funodKSds+x
+DzdYpPJScWc/DIh4gInByLUfkmO+p3qKViwaqKactV2zY9ATIKHrkWzQjX2v3wvk
+F7mGnjixlAxYjOBVqjtjbZqJYLhkKpLGN/R+Q0O3c+gB53+XD9fyexn9GtePyfqF
+a3qdnom2piiZk4hA9z7NUaPK6u95RyG1/jLix8NRb76AdPCkwzryT+lf3xkK8jsT
+Q6wxpLPn6/wY1gGp8yqPNg7rtLG8t0zJa7+h89n07eLw4+1knj0vllJPgFOL
+-----END CERTIFICATE-----
+
+# Issuer: CN=CA Disig Root R2 O=Disig a.s.
+# Subject: CN=CA Disig Root R2 O=Disig a.s.
+# Label: "CA Disig Root R2"
+# Serial: 10572350602393338211
+# MD5 Fingerprint: 26:01:fb:d8:27:a7:17:9a:45:54:38:1a:43:01:3b:03
+# SHA1 Fingerprint: b5:61:eb:ea:a4:de:e4:25:4b:69:1a:98:a5:57:47:c2:34:c7:d9:71
+# SHA256 Fingerprint: e2:3d:4a:03:6d:7b:70:e9:f5:95:b1:42:20:79:d2:b9:1e:df:bb:1f:b6:51:a0:63:3e:aa:8a:9d:c5:f8:07:03
+-----BEGIN CERTIFICATE-----
+MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNV
+BAYTAlNLMRMwEQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMu
+MRkwFwYDVQQDExBDQSBEaXNpZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQy
+MDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sxEzARBgNVBAcTCkJyYXRpc2xhdmEx
+EzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERpc2lnIFJvb3QgUjIw
+ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbCw3Oe
+NcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNH
+PWSb6WiaxswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3I
+x2ymrdMxp7zo5eFm1tL7A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbe
+QTg06ov80egEFGEtQX6sx3dOy1FU+16SGBsEWmjGycT6txOgmLcRK7fWV8x8nhfR
+yyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqVg8NTEQxzHQuyRpDRQjrO
+QG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa5Beny912
+H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJ
+QfYEkoopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUD
+i/ZnWejBBhG93c+AAk9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORs
+nLMOPReisjQS1n6yqEm70XooQL6iFh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1
+rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud
+DwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5uQu0wDQYJKoZI
+hvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM
+tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqf
+GopTpti72TVVsRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkb
+lvdhuDvEK7Z4bLQjb/D907JedR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka
++elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W81k/BfDxujRNt+3vrMNDcTa/F1bal
+TFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjxmHHEt38OFdAlab0i
+nSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01utI3
+gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18Dr
+G5gPcFw0sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3Os
+zMOl6W8KjptlwlCFtaOgUxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8x
+L4ysEr3vQCj8KWefshNPZiTEUxnpHikV7+ZtsH8tZ/3zbBt1RqPlShfppNcL
+-----END CERTIFICATE-----
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/websocket/tests/__init__.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/websocket/tests/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/websocket/tests/__init__.py
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/websocket/tests/data/header01.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/websocket/tests/data/header01.txt
new file mode 100644
index 0000000..d44d24c
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/websocket/tests/data/header01.txt
@@ -0,0 +1,6 @@
+HTTP/1.1 101 WebSocket Protocol Handshake
+Connection: Upgrade 
+Upgrade: WebSocket
+Sec-WebSocket-Accept: Kxep+hNu9n51529fGidYu7a3wO0=
+some_header: something
+
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/websocket/tests/data/header02.txt b/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/websocket/tests/data/header02.txt
new file mode 100644
index 0000000..f481de9
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/websocket/tests/data/header02.txt
@@ -0,0 +1,6 @@
+HTTP/1.1 101 WebSocket Protocol Handshake
+Connection: Upgrade
+Upgrade WebSocket
+Sec-WebSocket-Accept: Kxep+hNu9n51529fGidYu7a3wO0=
+some_header: something
+
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/websocket/tests/test_websocket.py b/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/websocket/tests/test_websocket.py
new file mode 100644
index 0000000..8a8de52
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/third_party/websocket-client/websocket/tests/test_websocket.py
@@ -0,0 +1,660 @@
+# -*- coding: utf-8 -*-
+#
+
+import sys
+sys.path[0:0] = [""]
+
+import os
+import os.path
+import socket
+
+import six
+
+# websocket-client
+import websocket as ws
+from websocket._handshake import _create_sec_websocket_key, \
+    _validate as _validate_header
+from websocket._http import read_headers
+from websocket._url import get_proxy_info, parse_url
+from websocket._utils import validate_utf8
+
+if six.PY3:
+    from base64 import decodebytes as base64decode
+else:
+    from base64 import decodestring as base64decode
+
+if sys.version_info[0] == 2 and sys.version_info[1] < 7:
+    import unittest2 as unittest
+else:
+    import unittest
+
+try:
+    from ssl import SSLError
+except ImportError:
+    # dummy class of SSLError for ssl none-support environment.
+    class SSLError(Exception):
+        pass
+
+# Skip test to access the internet.
+TEST_WITH_INTERNET = os.environ.get('TEST_WITH_INTERNET', '0') == '1'
+
+# Skip Secure WebSocket test.
+TEST_SECURE_WS = True
+TRACEABLE = False
+
+
+def create_mask_key(_):
+    return "abcd"
+
+
+class SockMock(object):
+    def __init__(self):
+        self.data = []
+        self.sent = []
+
+    def add_packet(self, data):
+        self.data.append(data)
+
+    def recv(self, bufsize):
+        if self.data:
+            e = self.data.pop(0)
+            if isinstance(e, Exception):
+                raise e
+            if len(e) > bufsize:
+                self.data.insert(0, e[bufsize:])
+            return e[:bufsize]
+
+    def send(self, data):
+        self.sent.append(data)
+        return len(data)
+
+    def close(self):
+        pass
+
+
+class HeaderSockMock(SockMock):
+
+    def __init__(self, fname):
+        SockMock.__init__(self)
+        path = os.path.join(os.path.dirname(__file__), fname)
+        with open(path, "rb") as f:
+            self.add_packet(f.read())
+
+
+class WebSocketTest(unittest.TestCase):
+    def setUp(self):
+        ws.enableTrace(TRACEABLE)
+
+    def tearDown(self):
+        pass
+
+    def testDefaultTimeout(self):
+        self.assertEqual(ws.getdefaulttimeout(), None)
+        ws.setdefaulttimeout(10)
+        self.assertEqual(ws.getdefaulttimeout(), 10)
+        ws.setdefaulttimeout(None)
+
+    def testParseUrl(self):
+        p = parse_url("ws://www.example.com/r")
+        self.assertEqual(p[0], "www.example.com")
+        self.assertEqual(p[1], 80)
+        self.assertEqual(p[2], "/r")
+        self.assertEqual(p[3], False)
+
+        p = parse_url("ws://www.example.com/r/")
+        self.assertEqual(p[0], "www.example.com")
+        self.assertEqual(p[1], 80)
+        self.assertEqual(p[2], "/r/")
+        self.assertEqual(p[3], False)
+
+        p = parse_url("ws://www.example.com/")
+        self.assertEqual(p[0], "www.example.com")
+        self.assertEqual(p[1], 80)
+        self.assertEqual(p[2], "/")
+        self.assertEqual(p[3], False)
+
+        p = parse_url("ws://www.example.com")
+        self.assertEqual(p[0], "www.example.com")
+        self.assertEqual(p[1], 80)
+        self.assertEqual(p[2], "/")
+        self.assertEqual(p[3], False)
+
+        p = parse_url("ws://www.example.com:8080/r")
+        self.assertEqual(p[0], "www.example.com")
+        self.assertEqual(p[1], 8080)
+        self.assertEqual(p[2], "/r")
+        self.assertEqual(p[3], False)
+
+        p = parse_url("ws://www.example.com:8080/")
+        self.assertEqual(p[0], "www.example.com")
+        self.assertEqual(p[1], 8080)
+        self.assertEqual(p[2], "/")
+        self.assertEqual(p[3], False)
+
+        p = parse_url("ws://www.example.com:8080")
+        self.assertEqual(p[0], "www.example.com")
+        self.assertEqual(p[1], 8080)
+        self.assertEqual(p[2], "/")
+        self.assertEqual(p[3], False)
+
+        p = parse_url("wss://www.example.com:8080/r")
+        self.assertEqual(p[0], "www.example.com")
+        self.assertEqual(p[1], 8080)
+        self.assertEqual(p[2], "/r")
+        self.assertEqual(p[3], True)
+
+        p = parse_url("wss://www.example.com:8080/r?key=value")
+        self.assertEqual(p[0], "www.example.com")
+        self.assertEqual(p[1], 8080)
+        self.assertEqual(p[2], "/r?key=value")
+        self.assertEqual(p[3], True)
+
+        self.assertRaises(ValueError, parse_url, "http://www.example.com/r")
+
+        if sys.version_info[0] == 2 and sys.version_info[1] < 7:
+            return
+
+        p = parse_url("ws://[2a03:4000:123:83::3]/r")
+        self.assertEqual(p[0], "2a03:4000:123:83::3")
+        self.assertEqual(p[1], 80)
+        self.assertEqual(p[2], "/r")
+        self.assertEqual(p[3], False)
+
+        p = parse_url("ws://[2a03:4000:123:83::3]:8080/r")
+        self.assertEqual(p[0], "2a03:4000:123:83::3")
+        self.assertEqual(p[1], 8080)
+        self.assertEqual(p[2], "/r")
+        self.assertEqual(p[3], False)
+
+        p = parse_url("wss://[2a03:4000:123:83::3]/r")
+        self.assertEqual(p[0], "2a03:4000:123:83::3")
+        self.assertEqual(p[1], 443)
+        self.assertEqual(p[2], "/r")
+        self.assertEqual(p[3], True)
+
+        p = parse_url("wss://[2a03:4000:123:83::3]:8080/r")
+        self.assertEqual(p[0], "2a03:4000:123:83::3")
+        self.assertEqual(p[1], 8080)
+        self.assertEqual(p[2], "/r")
+        self.assertEqual(p[3], True)
+
+    def testWSKey(self):
+        key = _create_sec_websocket_key()
+        self.assertTrue(key != 24)
+        self.assertTrue(six.u("¥n") not in key)
+
+    def testWsUtils(self):
+        key = "c6b8hTg4EeGb2gQMztV1/g=="
+        required_header = {
+            "upgrade": "websocket",
+            "connection": "upgrade",
+            "sec-websocket-accept": "Kxep+hNu9n51529fGidYu7a3wO0=",
+            }
+        self.assertEqual(_validate_header(required_header, key, None), (True, None))
+
+        header = required_header.copy()
+        header["upgrade"] = "http"
+        self.assertEqual(_validate_header(header, key, None), (False, None))
+        del header["upgrade"]
+        self.assertEqual(_validate_header(header, key, None), (False, None))
+
+        header = required_header.copy()
+        header["connection"] = "something"
+        self.assertEqual(_validate_header(header, key, None), (False, None))
+        del header["connection"]
+        self.assertEqual(_validate_header(header, key, None), (False, None))
+
+        header = required_header.copy()
+        header["sec-websocket-accept"] = "something"
+        self.assertEqual(_validate_header(header, key, None), (False, None))
+        del header["sec-websocket-accept"]
+        self.assertEqual(_validate_header(header, key, None), (False, None))
+
+        header = required_header.copy()
+        header["sec-websocket-protocol"] = "sub1"
+        self.assertEqual(_validate_header(header, key, ["sub1", "sub2"]), (True, "sub1"))
+        self.assertEqual(_validate_header(header, key, ["sub2", "sub3"]), (False, None))
+
+        header = required_header.copy()
+        header["sec-websocket-protocol"] = "sUb1"
+        self.assertEqual(_validate_header(header, key, ["Sub1", "suB2"]), (True, "sub1"))
+
+
+    def testReadHeader(self):
+        status, header = read_headers(HeaderSockMock("data/header01.txt"))
+        self.assertEqual(status, 101)
+        self.assertEqual(header["connection"], "Upgrade")
+
+        HeaderSockMock("data/header02.txt")
+        self.assertRaises(ws.WebSocketException, read_headers, HeaderSockMock("data/header02.txt"))
+
+    def testSend(self):
+        # TODO: add longer frame data
+        sock = ws.WebSocket()
+        sock.set_mask_key(create_mask_key)
+        s = sock.sock = HeaderSockMock("data/header01.txt")
+        sock.send("Hello")
+        self.assertEqual(s.sent[0], six.b("\x81\x85abcd)\x07\x0f\x08\x0e"))
+
+        sock.send("こんにちは")
+        self.assertEqual(s.sent[1], six.b("\x81\x8fabcd\x82\xe3\xf0\x87\xe3\xf1\x80\xe5\xca\x81\xe2\xc5\x82\xe3\xcc"))
+
+        sock.send(u"こんにちは")
+        self.assertEqual(s.sent[1], six.b("\x81\x8fabcd\x82\xe3\xf0\x87\xe3\xf1\x80\xe5\xca\x81\xe2\xc5\x82\xe3\xcc"))
+
+        sock.send("x" * 127)
+
+    def testRecv(self):
+        # TODO: add longer frame data
+        sock = ws.WebSocket()
+        s = sock.sock = SockMock()
+        something = six.b("\x81\x8fabcd\x82\xe3\xf0\x87\xe3\xf1\x80\xe5\xca\x81\xe2\xc5\x82\xe3\xcc")
+        s.add_packet(something)
+        data = sock.recv()
+        self.assertEqual(data, "こんにちは")
+
+        s.add_packet(six.b("\x81\x85abcd)\x07\x0f\x08\x0e"))
+        data = sock.recv()
+        self.assertEqual(data, "Hello")
+
+    @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
+    def testIter(self):
+        count = 2
+        for _ in ws.create_connection('ws://stream.meetup.com/2/rsvps'):
+            count -= 1
+            if count == 0:
+                break
+
+    @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
+    def testNext(self):
+        sock = ws.create_connection('ws://stream.meetup.com/2/rsvps')
+        self.assertEqual(str, type(next(sock)))
+
+    def testInternalRecvStrict(self):
+        sock = ws.WebSocket()
+        s = sock.sock = SockMock()
+        s.add_packet(six.b("foo"))
+        s.add_packet(socket.timeout())
+        s.add_packet(six.b("bar"))
+        # s.add_packet(SSLError("The read operation timed out"))
+        s.add_packet(six.b("baz"))
+        with self.assertRaises(ws.WebSocketTimeoutException):
+            sock.frame_buffer.recv_strict(9)
+        # if six.PY2:
+        #     with self.assertRaises(ws.WebSocketTimeoutException):
+        #         data = sock._recv_strict(9)
+        # else:
+        #     with self.assertRaises(SSLError):
+        #         data = sock._recv_strict(9)
+        data = sock.frame_buffer.recv_strict(9)
+        self.assertEqual(data, six.b("foobarbaz"))
+        with self.assertRaises(ws.WebSocketConnectionClosedException):
+            sock.frame_buffer.recv_strict(1)
+
+    def testRecvTimeout(self):
+        sock = ws.WebSocket()
+        s = sock.sock = SockMock()
+        s.add_packet(six.b("\x81"))
+        s.add_packet(socket.timeout())
+        s.add_packet(six.b("\x8dabcd\x29\x07\x0f\x08\x0e"))
+        s.add_packet(socket.timeout())
+        s.add_packet(six.b("\x4e\x43\x33\x0e\x10\x0f\x00\x40"))
+        with self.assertRaises(ws.WebSocketTimeoutException):
+            sock.recv()
+        with self.assertRaises(ws.WebSocketTimeoutException):
+            sock.recv()
+        data = sock.recv()
+        self.assertEqual(data, "Hello, World!")
+        with self.assertRaises(ws.WebSocketConnectionClosedException):
+            sock.recv()
+
+    def testRecvWithSimpleFragmentation(self):
+        sock = ws.WebSocket()
+        s = sock.sock = SockMock()
+        # OPCODE=TEXT, FIN=0, MSG="Brevity is "
+        s.add_packet(six.b("\x01\x8babcd#\x10\x06\x12\x08\x16\x1aD\x08\x11C"))
+        # OPCODE=CONT, FIN=1, MSG="the soul of wit"
+        s.add_packet(six.b("\x80\x8fabcd\x15\n\x06D\x12\r\x16\x08A\r\x05D\x16\x0b\x17"))
+        data = sock.recv()
+        self.assertEqual(data, "Brevity is the soul of wit")
+        with self.assertRaises(ws.WebSocketConnectionClosedException):
+            sock.recv()
+
+    def testRecvWithFireEventOfFragmentation(self):
+        sock = ws.WebSocket(fire_cont_frame=True)
+        s = sock.sock = SockMock()
+        # OPCODE=TEXT, FIN=0, MSG="Brevity is "
+        s.add_packet(six.b("\x01\x8babcd#\x10\x06\x12\x08\x16\x1aD\x08\x11C"))
+        # OPCODE=CONT, FIN=0, MSG="Brevity is "
+        s.add_packet(six.b("\x00\x8babcd#\x10\x06\x12\x08\x16\x1aD\x08\x11C"))
+        # OPCODE=CONT, FIN=1, MSG="the soul of wit"
+        s.add_packet(six.b("\x80\x8fabcd\x15\n\x06D\x12\r\x16\x08A\r\x05D\x16\x0b\x17"))
+
+        _, data = sock.recv_data()
+        self.assertEqual(data, six.b("Brevity is "))
+        _, data = sock.recv_data()
+        self.assertEqual(data, six.b("Brevity is "))
+        _, data = sock.recv_data()
+        self.assertEqual(data, six.b("the soul of wit"))
+
+        # OPCODE=CONT, FIN=0, MSG="Brevity is "
+        s.add_packet(six.b("\x80\x8babcd#\x10\x06\x12\x08\x16\x1aD\x08\x11C"))
+
+        with self.assertRaises(ws.WebSocketException):
+            sock.recv_data()
+
+        with self.assertRaises(ws.WebSocketConnectionClosedException):
+            sock.recv()
+
+    def testClose(self):
+        sock = ws.WebSocket()
+        sock.sock = SockMock()
+        sock.connected = True
+        sock.close()
+        self.assertEqual(sock.connected, False)
+
+        sock = ws.WebSocket()
+        s = sock.sock = SockMock()
+        sock.connected = True
+        s.add_packet(six.b('\x88\x80\x17\x98p\x84'))
+        sock.recv()
+        self.assertEqual(sock.connected, False)
+
+    def testRecvContFragmentation(self):
+        sock = ws.WebSocket()
+        s = sock.sock = SockMock()
+        # OPCODE=CONT, FIN=1, MSG="the soul of wit"
+        s.add_packet(six.b("\x80\x8fabcd\x15\n\x06D\x12\r\x16\x08A\r\x05D\x16\x0b\x17"))
+        self.assertRaises(ws.WebSocketException, sock.recv)
+
+    def testRecvWithProlongedFragmentation(self):
+        sock = ws.WebSocket()
+        s = sock.sock = SockMock()
+        # OPCODE=TEXT, FIN=0, MSG="Once more unto the breach, "
+        s.add_packet(six.b("\x01\x9babcd.\x0c\x00\x01A\x0f\x0c\x16\x04B\x16\n\x15"
+                           "\rC\x10\t\x07C\x06\x13\x07\x02\x07\tNC"))
+        # OPCODE=CONT, FIN=0, MSG="dear friends, "
+        s.add_packet(six.b("\x00\x8eabcd\x05\x07\x02\x16A\x04\x11\r\x04\x0c\x07"
+                           "\x17MB"))
+        # OPCODE=CONT, FIN=1, MSG="once more"
+        s.add_packet(six.b("\x80\x89abcd\x0e\x0c\x00\x01A\x0f\x0c\x16\x04"))
+        data = sock.recv()
+        self.assertEqual(
+            data,
+            "Once more unto the breach, dear friends, once more")
+        with self.assertRaises(ws.WebSocketConnectionClosedException):
+            sock.recv()
+
+    def testRecvWithFragmentationAndControlFrame(self):
+        sock = ws.WebSocket()
+        sock.set_mask_key(create_mask_key)
+        s = sock.sock = SockMock()
+        # OPCODE=TEXT, FIN=0, MSG="Too much "
+        s.add_packet(six.b("\x01\x89abcd5\r\x0cD\x0c\x17\x00\x0cA"))
+        # OPCODE=PING, FIN=1, MSG="Please PONG this"
+        s.add_packet(six.b("\x89\x90abcd1\x0e\x06\x05\x12\x07C4.,$D\x15\n\n\x17"))
+        # OPCODE=CONT, FIN=1, MSG="of a good thing"
+        s.add_packet(six.b("\x80\x8fabcd\x0e\x04C\x05A\x05\x0c\x0b\x05B\x17\x0c"
+                           "\x08\x0c\x04"))
+        data = sock.recv()
+        self.assertEqual(data, "Too much of a good thing")
+        with self.assertRaises(ws.WebSocketConnectionClosedException):
+            sock.recv()
+        self.assertEqual(
+            s.sent[0],
+            six.b("\x8a\x90abcd1\x0e\x06\x05\x12\x07C4.,$D\x15\n\n\x17"))
+
+    @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
+    def testWebSocket(self):
+        s = ws.create_connection("ws://echo.websocket.org/")
+        self.assertNotEqual(s, None)
+        s.send("Hello, World")
+        result = s.recv()
+        self.assertEqual(result, "Hello, World")
+
+        s.send(u"こにゃにゃちは、世界")
+        result = s.recv()
+        self.assertEqual(result, "こにゃにゃちは、世界")
+        s.close()
+
+    @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
+    def testPingPong(self):
+        s = ws.create_connection("ws://echo.websocket.org/")
+        self.assertNotEqual(s, None)
+        s.ping("Hello")
+        s.pong("Hi")
+        s.close()
+
+    @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
+    @unittest.skipUnless(TEST_SECURE_WS, "wss://echo.websocket.org doesn't work well.")
+    def testSecureWebSocket(self):
+        if 1:
+            import ssl
+            s = ws.create_connection("wss://echo.websocket.org/")
+            self.assertNotEqual(s, None)
+            self.assertTrue(isinstance(s.sock, ssl.SSLSocket))
+            s.send("Hello, World")
+            result = s.recv()
+            self.assertEqual(result, "Hello, World")
+            s.send(u"こにゃにゃちは、世界")
+            result = s.recv()
+            self.assertEqual(result, "こにゃにゃちは、世界")
+            s.close()
+        #except:
+        #    pass
+
+    @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
+    def testWebSocketWihtCustomHeader(self):
+        s = ws.create_connection("ws://echo.websocket.org/",
+                                 headers={"User-Agent": "PythonWebsocketClient"})
+        self.assertNotEqual(s, None)
+        s.send("Hello, World")
+        result = s.recv()
+        self.assertEqual(result, "Hello, World")
+        s.close()
+
+    @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
+    def testAfterClose(self):
+        s = ws.create_connection("ws://echo.websocket.org/")
+        self.assertNotEqual(s, None)
+        s.close()
+        self.assertRaises(ws.WebSocketConnectionClosedException, s.send, "Hello")
+        self.assertRaises(ws.WebSocketConnectionClosedException, s.recv)
+
+    def testNonce(self):
+        """ WebSocket key should be a random 16-byte nonce.
+        """
+        key = _create_sec_websocket_key()
+        nonce = base64decode(key.encode("utf-8"))
+        self.assertEqual(16, len(nonce))
+
+
+class WebSocketAppTest(unittest.TestCase):
+
+    class NotSetYet(object):
+        """ A marker class for signalling that a value hasn't been set yet.
+        """
+
+    def setUp(self):
+        ws.enableTrace(TRACEABLE)
+
+        WebSocketAppTest.keep_running_open = WebSocketAppTest.NotSetYet()
+        WebSocketAppTest.keep_running_close = WebSocketAppTest.NotSetYet()
+        WebSocketAppTest.get_mask_key_id = WebSocketAppTest.NotSetYet()
+
+    def tearDown(self):
+        WebSocketAppTest.keep_running_open = WebSocketAppTest.NotSetYet()
+        WebSocketAppTest.keep_running_close = WebSocketAppTest.NotSetYet()
+        WebSocketAppTest.get_mask_key_id = WebSocketAppTest.NotSetYet()
+
+    @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
+    def testKeepRunning(self):
+        """ A WebSocketApp should keep running as long as its self.keep_running
+        is not False (in the boolean context).
+        """
+
+        def on_open(self, *args, **kwargs):
+            """ Set the keep_running flag for later inspection and immediately
+            close the connection.
+            """
+            WebSocketAppTest.keep_running_open = self.keep_running
+
+            self.close()
+
+        def on_close(self, *args, **kwargs):
+            """ Set the keep_running flag for the test to use.
+            """
+            WebSocketAppTest.keep_running_close = self.keep_running
+
+        app = ws.WebSocketApp('ws://echo.websocket.org/', on_open=on_open, on_close=on_close)
+        app.run_forever()
+
+        self.assertFalse(isinstance(WebSocketAppTest.keep_running_open,
+                                    WebSocketAppTest.NotSetYet))
+
+        self.assertFalse(isinstance(WebSocketAppTest.keep_running_close,
+                                    WebSocketAppTest.NotSetYet))
+
+        self.assertEqual(True, WebSocketAppTest.keep_running_open)
+        self.assertEqual(False, WebSocketAppTest.keep_running_close)
+
+    @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
+    def testSockMaskKey(self):
+        """ A WebSocketApp should forward the received mask_key function down
+        to the actual socket.
+        """
+
+        def my_mask_key_func():
+            pass
+
+        def on_open(self, *args, **kwargs):
+            """ Set the value so the test can use it later on and immediately
+            close the connection.
+            """
+            WebSocketAppTest.get_mask_key_id = id(self.get_mask_key)
+            self.close()
+
+        app = ws.WebSocketApp('ws://echo.websocket.org/', on_open=on_open, get_mask_key=my_mask_key_func)
+        app.run_forever()
+
+        # Note: We can't use 'is' for comparing the functions directly, need to use 'id'.
+        self.assertEqual(WebSocketAppTest.get_mask_key_id, id(my_mask_key_func))
+
+
+class SockOptTest(unittest.TestCase):
+    @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
+    def testSockOpt(self):
+        sockopt = ((socket.IPPROTO_TCP, socket.TCP_NODELAY, 1),)
+        s = ws.create_connection("ws://echo.websocket.org", sockopt=sockopt)
+        self.assertNotEqual(s.sock.getsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY), 0)
+        s.close()
+
+class UtilsTest(unittest.TestCase):
+    def testUtf8Validator(self):
+        state = validate_utf8(six.b('\xf0\x90\x80\x80'))
+        self.assertEqual(state, True)
+        state = validate_utf8(six.b('\xce\xba\xe1\xbd\xb9\xcf\x83\xce\xbc\xce\xb5\xed\xa0\x80edited'))
+        self.assertEqual(state, False)
+        state = validate_utf8(six.b(''))
+        self.assertEqual(state, True)
+
+class ProxyInfoTest(unittest.TestCase):
+    def setUp(self):
+        self.http_proxy = os.environ.get("http_proxy", None)
+        self.https_proxy = os.environ.get("https_proxy", None)
+        if "http_proxy" in os.environ:
+            del os.environ["http_proxy"]
+        if "https_proxy" in os.environ:
+            del os.environ["https_proxy"]
+
+    def tearDown(self):
+        if self.http_proxy:
+            os.environ["http_proxy"] = self.http_proxy
+        elif "http_proxy" in os.environ:
+            del os.environ["http_proxy"]
+
+        if self.https_proxy:
+            os.environ["https_proxy"] = self.https_proxy
+        elif "https_proxy" in os.environ:
+            del os.environ["https_proxy"]
+
+
+    def testProxyFromArgs(self):
+        self.assertEqual(get_proxy_info("echo.websocket.org", False, proxy_host="localhost"), ("localhost", 0, None))
+        self.assertEqual(get_proxy_info("echo.websocket.org", False, proxy_host="localhost", proxy_port=3128), ("localhost", 3128, None))
+        self.assertEqual(get_proxy_info("echo.websocket.org", True, proxy_host="localhost"), ("localhost", 0, None))
+        self.assertEqual(get_proxy_info("echo.websocket.org", True, proxy_host="localhost", proxy_port=3128), ("localhost", 3128, None))
+
+        self.assertEqual(get_proxy_info("echo.websocket.org", False, proxy_host="localhost", proxy_auth=("a", "b")),
+            ("localhost", 0, ("a", "b")))
+        self.assertEqual(get_proxy_info("echo.websocket.org", False, proxy_host="localhost", proxy_port=3128, proxy_auth=("a", "b")),
+            ("localhost", 3128, ("a", "b")))
+        self.assertEqual(get_proxy_info("echo.websocket.org", True, proxy_host="localhost", proxy_auth=("a", "b")),
+            ("localhost", 0, ("a", "b")))
+        self.assertEqual(get_proxy_info("echo.websocket.org", True, proxy_host="localhost", proxy_port=3128, proxy_auth=("a", "b")),
+            ("localhost", 3128, ("a", "b")))
+
+        self.assertEqual(get_proxy_info("echo.websocket.org", True, proxy_host="localhost", proxy_port=3128, no_proxy=["example.com"], proxy_auth=("a", "b")),
+            ("localhost", 3128, ("a", "b")))
+        self.assertEqual(get_proxy_info("echo.websocket.org", True, proxy_host="localhost", proxy_port=3128, no_proxy=["echo.websocket.org"], proxy_auth=("a", "b")),
+            (None, 0, None))
+
+
+    def testProxyFromEnv(self):
+        os.environ["http_proxy"] = "http://localhost/"
+        self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", None, None))
+        os.environ["http_proxy"] = "http://localhost:3128/"
+        self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", 3128, None))
+
+        os.environ["http_proxy"] = "http://localhost/"
+        os.environ["https_proxy"] = "http://localhost2/"
+        self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", None, None))
+        os.environ["http_proxy"] = "http://localhost:3128/"
+        os.environ["https_proxy"] = "http://localhost2:3128/"
+        self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", 3128, None))
+
+        os.environ["http_proxy"] = "http://localhost/"
+        os.environ["https_proxy"] = "http://localhost2/"
+        self.assertEqual(get_proxy_info("echo.websocket.org", True), ("localhost2", None, None))
+        os.environ["http_proxy"] = "http://localhost:3128/"
+        os.environ["https_proxy"] = "http://localhost2:3128/"
+        self.assertEqual(get_proxy_info("echo.websocket.org", True), ("localhost2", 3128, None))
+
+
+        os.environ["http_proxy"] = "http://a:b@localhost/"
+        self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", None, ("a", "b")))
+        os.environ["http_proxy"] = "http://a:b@localhost:3128/"
+        self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", 3128, ("a", "b")))
+
+        os.environ["http_proxy"] = "http://a:b@localhost/"
+        os.environ["https_proxy"] = "http://a:b@localhost2/"
+        self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", None, ("a", "b")))
+        os.environ["http_proxy"] = "http://a:b@localhost:3128/"
+        os.environ["https_proxy"] = "http://a:b@localhost2:3128/"
+        self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", 3128, ("a", "b")))
+
+        os.environ["http_proxy"] = "http://a:b@localhost/"
+        os.environ["https_proxy"] = "http://a:b@localhost2/"
+        self.assertEqual(get_proxy_info("echo.websocket.org", True), ("localhost2", None, ("a", "b")))
+        os.environ["http_proxy"] = "http://a:b@localhost:3128/"
+        os.environ["https_proxy"] = "http://a:b@localhost2:3128/"
+        self.assertEqual(get_proxy_info("echo.websocket.org", True), ("localhost2", 3128, ("a", "b")))
+
+        os.environ["http_proxy"] = "http://a:b@localhost/"
+        os.environ["https_proxy"] = "http://a:b@localhost2/"
+        os.environ["no_proxy"] = "example1.com,example2.com"
+        self.assertEqual(get_proxy_info("example.1.com", True), ("localhost2", None, ("a", "b")))
+        os.environ["http_proxy"] = "http://a:b@localhost:3128/"
+        os.environ["https_proxy"] = "http://a:b@localhost2:3128/"
+        os.environ["no_proxy"] = "example1.com,example2.com, echo.websocket.org"
+        self.assertEqual(get_proxy_info("echo.websocket.org", True), (None, 0, None))
+
+        os.environ["http_proxy"] = "http://a:b@localhost:3128/"
+        os.environ["https_proxy"] = "http://a:b@localhost2:3128/"
+        os.environ["no_proxy"] = "127.0.0.0/8, 192.168.0.0/16"
+        self.assertEqual(get_proxy_info("127.0.0.1", False), (None, 0, None))
+        self.assertEqual(get_proxy_info("192.168.1.1", False), (None, 0, None))
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/update_docs b/sdk/platform-tools/systrace/catapult/telemetry/update_docs
new file mode 100755
index 0000000..f2f92ac
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/update_docs
@@ -0,0 +1,11 @@
+#!/usr/bin/env python
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import os
+import sys
+
+from build import update_docs
+
+if __name__ == '__main__':
+  sys.exit(update_docs.Main(sys.argv[1:]))
diff --git a/sdk/platform-tools/systrace/catapult/telemetry/validate_binary_dependencies b/sdk/platform-tools/systrace/catapult/telemetry/validate_binary_dependencies
new file mode 100755
index 0000000..dbcd7e4
--- /dev/null
+++ b/sdk/platform-tools/systrace/catapult/telemetry/validate_binary_dependencies
@@ -0,0 +1,49 @@
+#!/usr/bin/env python
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import argparse
+import json
+import os
+import sys
+
+from telemetry.core import util
+
+sys.path.insert(1, os.path.abspath(os.path.join(
+    util.GetCatapultDir(), 'common', 'py_utils')))
+sys.path.insert(1, os.path.abspath(os.path.join(
+    util.GetCatapultDir(), 'dependency_manager')))
+from py_utils import cloud_storage
+import dependency_manager
+
+
+def ValidateCloudStorageDependencies(file_path):
+  base_config = dependency_manager.BaseConfig(file_path)
+  cloud_storage_deps_not_exist = []
+  for dep_info in base_config.IterDependencyInfo():
+    if dep_info.has_cloud_storage_info:
+      if not dep_info.cloud_storage_info.DependencyExistsInCloudStorage():
+        print >> sys.stderr, (
+          '%s does not exist in cloud storage' % dep_info.cloud_storage_info)
+        cloud_storage_deps_not_exist = True
+      else:
+        print >> sys.stdout, (
+          '%s passes cloud storage validation' % dep_info.dependency)
+
+  if cloud_storage_deps_not_exist:
+    raise Exception(
+        "Some dependencies specify cloud storage locations that don't exist.")
+
+
+def Main(args):
+  parser = argparse.ArgumentParser(
+      description='Validate the dependencies in a binary dependency json file')
+  parser.add_argument('file_path', type=str,
+                      help='The path to binary dependency json file')
+  options = parser.parse_args(args)
+  ValidateCloudStorageDependencies(options.file_path)
+  return 0
+
+
+if __name__ == '__main__':
+  sys.exit(Main(sys.argv[1:]))